Adding elevation
All checks were successful
Deploy Promiscuity Auth API / deploy (push) Successful in 48s
Deploy Promiscuity Character API / deploy (push) Successful in 1m0s
Deploy Promiscuity Inventory API / deploy (push) Successful in 47s
Deploy Promiscuity Locations API / deploy (push) Successful in 1m0s
k8s smoke test / test (push) Successful in 9s
All checks were successful
Deploy Promiscuity Auth API / deploy (push) Successful in 48s
Deploy Promiscuity Character API / deploy (push) Successful in 1m0s
Deploy Promiscuity Inventory API / deploy (push) Successful in 47s
Deploy Promiscuity Locations API / deploy (push) Successful in 1m0s
k8s smoke test / test (push) Successful in 9s
This commit is contained in:
parent
c5e692c051
commit
f90de8a98b
@ -9,6 +9,7 @@ const CHARACTER_SLOT_COUNT := 6
|
|||||||
|
|
||||||
@export var tile_size := 8.0
|
@export var tile_size := 8.0
|
||||||
@export var block_height := 1.0
|
@export var block_height := 1.0
|
||||||
|
@export var elevation_step_height := 0.5
|
||||||
@export_range(1, 8, 1) var tile_radius := 3
|
@export_range(1, 8, 1) var tile_radius := 3
|
||||||
@export var tracked_node_path: NodePath
|
@export var tracked_node_path: NodePath
|
||||||
@export var player_spawn_height := 2.0
|
@export var player_spawn_height := 2.0
|
||||||
@ -130,13 +131,13 @@ func _world_to_coord(world_pos: Vector3) -> Vector2i:
|
|||||||
|
|
||||||
|
|
||||||
func _coord_to_world(coord: Vector2i) -> Vector3:
|
func _coord_to_world(coord: Vector2i) -> Vector3:
|
||||||
return Vector3(coord.x * tile_size, block_height * 0.5, coord.y * tile_size)
|
return Vector3(coord.x * tile_size, _get_tile_center_y(coord), coord.y * tile_size)
|
||||||
|
|
||||||
|
|
||||||
func _move_player_to_coord(coord: Vector2i) -> void:
|
func _move_player_to_coord(coord: Vector2i) -> void:
|
||||||
if _player == null:
|
if _player == null:
|
||||||
return
|
return
|
||||||
_player.global_position = Vector3(coord.x * tile_size, player_spawn_height, coord.y * tile_size)
|
_player.global_position = Vector3(coord.x * tile_size, _get_tile_surface_y(coord) + player_spawn_height, coord.y * tile_size)
|
||||||
_player.linear_velocity = Vector3.ZERO
|
_player.linear_velocity = Vector3.ZERO
|
||||||
_player.angular_velocity = Vector3.ZERO
|
_player.angular_velocity = Vector3.ZERO
|
||||||
|
|
||||||
@ -306,6 +307,7 @@ func _update_tile(coord: Vector2i, location_data: Dictionary) -> void:
|
|||||||
var tile_root := _tile_nodes.get(coord) as Node3D
|
var tile_root := _tile_nodes.get(coord) as Node3D
|
||||||
if tile_root == null:
|
if tile_root == null:
|
||||||
return
|
return
|
||||||
|
tile_root.position = _coord_to_world(coord)
|
||||||
|
|
||||||
if show_tile_labels:
|
if show_tile_labels:
|
||||||
var label := tile_root.get_node_or_null("LocationNameLabel") as Label3D
|
var label := tile_root.get_node_or_null("LocationNameLabel") as Label3D
|
||||||
@ -480,6 +482,19 @@ func _build_floor_inventory_label(floor_items: Array) -> String:
|
|||||||
return label
|
return label
|
||||||
|
|
||||||
|
|
||||||
|
func _get_tile_elevation(coord: Vector2i) -> int:
|
||||||
|
var location_data := _get_location_data(coord)
|
||||||
|
return int(location_data.get("elevation", 0))
|
||||||
|
|
||||||
|
|
||||||
|
func _get_tile_center_y(coord: Vector2i) -> float:
|
||||||
|
return (_get_tile_elevation(coord) * elevation_step_height) + (block_height * 0.5)
|
||||||
|
|
||||||
|
|
||||||
|
func _get_tile_surface_y(coord: Vector2i) -> float:
|
||||||
|
return (_get_tile_elevation(coord) * elevation_step_height) + block_height
|
||||||
|
|
||||||
|
|
||||||
func _update_inventory_location_label() -> void:
|
func _update_inventory_location_label() -> void:
|
||||||
if _inventory_location_label == null:
|
if _inventory_location_label == null:
|
||||||
return
|
return
|
||||||
@ -812,6 +827,7 @@ func _ensure_selected_location_exists(coord: Vector2i) -> void:
|
|||||||
"id": "",
|
"id": "",
|
||||||
"name": _selected_location_name(coord),
|
"name": _selected_location_name(coord),
|
||||||
"biomeKey": "plains",
|
"biomeKey": "plains",
|
||||||
|
"elevation": 0,
|
||||||
"locationObject": {},
|
"locationObject": {},
|
||||||
"floorItems": []
|
"floorItems": []
|
||||||
}
|
}
|
||||||
@ -889,6 +905,7 @@ func _load_existing_locations() -> void:
|
|||||||
"id": String(location.get("id", "")).strip_edges(),
|
"id": String(location.get("id", "")).strip_edges(),
|
||||||
"name": location_name,
|
"name": location_name,
|
||||||
"biomeKey": String(location.get("biomeKey", "plains")).strip_edges(),
|
"biomeKey": String(location.get("biomeKey", "plains")).strip_edges(),
|
||||||
|
"elevation": int(location.get("elevation", 0)),
|
||||||
"locationObject": _parse_location_object(location.get("locationObject", {})),
|
"locationObject": _parse_location_object(location.get("locationObject", {})),
|
||||||
"floorItems": _parse_floor_inventory_items(location.get("floorItems", []))
|
"floorItems": _parse_floor_inventory_items(location.get("floorItems", []))
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,6 +19,9 @@ public class VisibleLocation
|
|||||||
[BsonElement("biomeKey")]
|
[BsonElement("biomeKey")]
|
||||||
public string BiomeKey { get; set; } = "plains";
|
public string BiomeKey { get; set; } = "plains";
|
||||||
|
|
||||||
|
[BsonElement("elevation")]
|
||||||
|
public int Elevation { get; set; }
|
||||||
|
|
||||||
[BsonElement("locationObject")]
|
[BsonElement("locationObject")]
|
||||||
public VisibleLocationObject? LocationObject { get; set; }
|
public VisibleLocationObject? LocationObject { get; set; }
|
||||||
|
|
||||||
|
|||||||
@ -18,6 +18,9 @@ public class Location
|
|||||||
[BsonElement("biomeKey")]
|
[BsonElement("biomeKey")]
|
||||||
public string BiomeKey { get; set; } = "plains";
|
public string BiomeKey { get; set; } = "plains";
|
||||||
|
|
||||||
|
[BsonElement("elevation")]
|
||||||
|
public int Elevation { get; set; }
|
||||||
|
|
||||||
[BsonElement("resources")]
|
[BsonElement("resources")]
|
||||||
public List<LocationResource> Resources { get; set; } = [];
|
public List<LocationResource> Resources { get; set; } = [];
|
||||||
|
|
||||||
|
|||||||
@ -10,6 +10,8 @@ public class VisibleLocationResponse
|
|||||||
|
|
||||||
public string BiomeKey { get; set; } = "plains";
|
public string BiomeKey { get; set; } = "plains";
|
||||||
|
|
||||||
|
public int Elevation { get; set; }
|
||||||
|
|
||||||
public VisibleLocationObjectResponse? LocationObject { get; set; }
|
public VisibleLocationObjectResponse? LocationObject { get; set; }
|
||||||
|
|
||||||
public List<FloorInventoryItemResponse> FloorItems { get; set; } = [];
|
public List<FloorInventoryItemResponse> FloorItems { get; set; } = [];
|
||||||
|
|||||||
@ -38,7 +38,7 @@ public class LocationStore
|
|||||||
"$jsonSchema", new BsonDocument
|
"$jsonSchema", new BsonDocument
|
||||||
{
|
{
|
||||||
{ "bsonType", "object" },
|
{ "bsonType", "object" },
|
||||||
{ "required", new BsonArray { "name", "coord", "biomeKey", "createdUtc" } },
|
{ "required", new BsonArray { "name", "coord", "biomeKey", "elevation", "createdUtc" } },
|
||||||
{
|
{
|
||||||
"properties", new BsonDocument
|
"properties", new BsonDocument
|
||||||
{
|
{
|
||||||
@ -58,6 +58,7 @@ public class LocationStore
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ "biomeKey", new BsonDocument { { "bsonType", "string" } } },
|
{ "biomeKey", new BsonDocument { { "bsonType", "string" } } },
|
||||||
|
{ "elevation", new BsonDocument { { "bsonType", "int" } } },
|
||||||
{
|
{
|
||||||
"resources", new BsonDocument
|
"resources", new BsonDocument
|
||||||
{
|
{
|
||||||
@ -348,6 +349,7 @@ public class LocationStore
|
|||||||
Name = "Origin",
|
Name = "Origin",
|
||||||
Coord = new Coord { X = 0, Y = 0 },
|
Coord = new Coord { X = 0, Y = 0 },
|
||||||
BiomeKey = originBiomeKey,
|
BiomeKey = originBiomeKey,
|
||||||
|
Elevation = 0,
|
||||||
LocationObject = CreateLocationObjectForBiome(biomeDefinitions, originBiomeKey, 0, 0),
|
LocationObject = CreateLocationObjectForBiome(biomeDefinitions, originBiomeKey, 0, 0),
|
||||||
LocationObjectResolved = true,
|
LocationObjectResolved = true,
|
||||||
CreatedUtc = DateTime.UtcNow
|
CreatedUtc = DateTime.UtcNow
|
||||||
@ -423,6 +425,7 @@ public class LocationStore
|
|||||||
}
|
}
|
||||||
|
|
||||||
var biomeKey = await DetermineBiomeKeyAsync(x, y, biomeDefinitions);
|
var biomeKey = await DetermineBiomeKeyAsync(x, y, biomeDefinitions);
|
||||||
|
var elevation = await DetermineElevationAsync(x, y, biomeKey);
|
||||||
var locationObject = CreateLocationObjectForBiome(biomeDefinitions, biomeKey, x, y);
|
var locationObject = CreateLocationObjectForBiome(biomeDefinitions, biomeKey, x, y);
|
||||||
BsonValue locationObjectValue = locationObject is null ? BsonNull.Value : locationObject.ToBsonDocument();
|
BsonValue locationObjectValue = locationObject is null ? BsonNull.Value : locationObject.ToBsonDocument();
|
||||||
var update = Builders<BsonDocument>.Update
|
var update = Builders<BsonDocument>.Update
|
||||||
@ -430,6 +433,7 @@ public class LocationStore
|
|||||||
.SetOnInsert("name", DefaultLocationName(x, y))
|
.SetOnInsert("name", DefaultLocationName(x, y))
|
||||||
.SetOnInsert("coord", new BsonDocument { { "x", x }, { "y", y } })
|
.SetOnInsert("coord", new BsonDocument { { "x", x }, { "y", y } })
|
||||||
.SetOnInsert("biomeKey", biomeKey)
|
.SetOnInsert("biomeKey", biomeKey)
|
||||||
|
.SetOnInsert("elevation", elevation)
|
||||||
.SetOnInsert("locationObject", locationObjectValue)
|
.SetOnInsert("locationObject", locationObjectValue)
|
||||||
.SetOnInsert("locationObjectResolved", true)
|
.SetOnInsert("locationObjectResolved", true)
|
||||||
.SetOnInsert("createdUtc", DateTime.UtcNow);
|
.SetOnInsert("createdUtc", DateTime.UtcNow);
|
||||||
@ -447,13 +451,16 @@ public class LocationStore
|
|||||||
|
|
||||||
private async Task<Location> EnsureLocationMetadataAsync(Location location)
|
private async Task<Location> EnsureLocationMetadataAsync(Location location)
|
||||||
{
|
{
|
||||||
if (!string.IsNullOrWhiteSpace(location.BiomeKey) && location.LocationObjectResolved)
|
var locationId = location.Id ?? string.Empty;
|
||||||
|
var hasElevation = !string.IsNullOrWhiteSpace(locationId) && await HasStoredElevationAsync(locationId);
|
||||||
|
if (!string.IsNullOrWhiteSpace(location.BiomeKey) && location.LocationObjectResolved && hasElevation)
|
||||||
return location;
|
return location;
|
||||||
|
|
||||||
var biomeDefinitions = await LoadBiomeDefinitionsAsync();
|
var biomeDefinitions = await LoadBiomeDefinitionsAsync();
|
||||||
var biomeKey = location.BiomeKey;
|
var biomeKey = location.BiomeKey;
|
||||||
if (string.IsNullOrWhiteSpace(biomeKey))
|
if (string.IsNullOrWhiteSpace(biomeKey))
|
||||||
biomeKey = await DetermineBiomeKeyAsync(location.Coord.X, location.Coord.Y, biomeDefinitions);
|
biomeKey = await DetermineBiomeKeyAsync(location.Coord.X, location.Coord.Y, biomeDefinitions);
|
||||||
|
var elevation = hasElevation ? location.Elevation : await DetermineElevationAsync(location.Coord.X, location.Coord.Y, biomeKey);
|
||||||
|
|
||||||
var migratedObject = TryMigrateLegacyResources(location) ?? CreateLocationObjectForBiome(biomeDefinitions, biomeKey, location.Coord.X, location.Coord.Y);
|
var migratedObject = TryMigrateLegacyResources(location) ?? CreateLocationObjectForBiome(biomeDefinitions, biomeKey, location.Coord.X, location.Coord.Y);
|
||||||
var filter = Builders<Location>.Filter.And(
|
var filter = Builders<Location>.Filter.And(
|
||||||
@ -461,10 +468,12 @@ public class LocationStore
|
|||||||
);
|
);
|
||||||
var update = Builders<Location>.Update
|
var update = Builders<Location>.Update
|
||||||
.Set(l => l.BiomeKey, biomeKey)
|
.Set(l => l.BiomeKey, biomeKey)
|
||||||
|
.Set(l => l.Elevation, elevation)
|
||||||
.Set(l => l.LocationObjectResolved, true)
|
.Set(l => l.LocationObjectResolved, true)
|
||||||
.Set(l => l.LocationObject, migratedObject);
|
.Set(l => l.LocationObject, migratedObject);
|
||||||
await _col.UpdateOneAsync(filter, update);
|
await _col.UpdateOneAsync(filter, update);
|
||||||
location.BiomeKey = biomeKey;
|
location.BiomeKey = biomeKey;
|
||||||
|
location.Elevation = elevation;
|
||||||
location.LocationObject = migratedObject;
|
location.LocationObject = migratedObject;
|
||||||
location.LocationObjectResolved = true;
|
location.LocationObjectResolved = true;
|
||||||
return location;
|
return location;
|
||||||
@ -478,6 +487,7 @@ public class LocationStore
|
|||||||
Name = location.Name,
|
Name = location.Name,
|
||||||
Coord = new Coord { X = location.Coord.X, Y = location.Coord.Y },
|
Coord = new Coord { X = location.Coord.X, Y = location.Coord.Y },
|
||||||
BiomeKey = location.BiomeKey,
|
BiomeKey = location.BiomeKey,
|
||||||
|
Elevation = location.Elevation,
|
||||||
LocationObject = MapVisibleLocationObject(location.LocationObject)
|
LocationObject = MapVisibleLocationObject(location.LocationObject)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -571,6 +581,23 @@ public class LocationStore
|
|||||||
return bestBiome;
|
return bestBiome;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<int> DetermineElevationAsync(int x, int y, string biomeKey)
|
||||||
|
{
|
||||||
|
if (x == 0 && y == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
var baseElevation = DetermineBaseElevation(x, y, biomeKey);
|
||||||
|
var neighbors = await LoadNeighborElevationsAsync(x, y);
|
||||||
|
if (neighbors.Count == 0)
|
||||||
|
return baseElevation;
|
||||||
|
|
||||||
|
var averageNeighbor = (int)Math.Round(neighbors.Average());
|
||||||
|
var blended = (int)Math.Round((baseElevation + averageNeighbor) / 2.0);
|
||||||
|
var minNeighbor = neighbors.Min();
|
||||||
|
var maxNeighbor = neighbors.Max();
|
||||||
|
return Math.Clamp(blended, minNeighbor - 1, maxNeighbor + 1);
|
||||||
|
}
|
||||||
|
|
||||||
private static LocationObject? CreateLocationObjectForBiome(IReadOnlyList<BiomeDefinition> biomeDefinitions, string biomeKey, int x, int y)
|
private static LocationObject? CreateLocationObjectForBiome(IReadOnlyList<BiomeDefinition> biomeDefinitions, string biomeKey, int x, int y)
|
||||||
{
|
{
|
||||||
var biome = biomeDefinitions.FirstOrDefault(definition => definition.BiomeKey == biomeKey)
|
var biome = biomeDefinitions.FirstOrDefault(definition => definition.BiomeKey == biomeKey)
|
||||||
@ -655,6 +682,32 @@ public class LocationStore
|
|||||||
.ToList();
|
.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task<List<int>> LoadNeighborElevationsAsync(int x, int y)
|
||||||
|
{
|
||||||
|
var coords = new[] { (x - 1, y), (x + 1, y), (x, y - 1), (x, y + 1) };
|
||||||
|
var filters = coords.Select(coord =>
|
||||||
|
Builders<BsonDocument>.Filter.And(
|
||||||
|
Builders<BsonDocument>.Filter.Eq("coord.x", coord.Item1),
|
||||||
|
Builders<BsonDocument>.Filter.Eq("coord.y", coord.Item2)))
|
||||||
|
.ToList();
|
||||||
|
var filter = Builders<BsonDocument>.Filter.Or(filters);
|
||||||
|
var neighbors = await _rawCol.Find(filter).ToListAsync();
|
||||||
|
|
||||||
|
return neighbors
|
||||||
|
.Where(doc => doc.Contains("elevation") && doc["elevation"].IsInt32)
|
||||||
|
.Select(doc => doc["elevation"].AsInt32)
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<bool> HasStoredElevationAsync(string locationId)
|
||||||
|
{
|
||||||
|
var filter = Builders<BsonDocument>.Filter.And(
|
||||||
|
Builders<BsonDocument>.Filter.Eq("_id", ObjectId.Parse(locationId)),
|
||||||
|
Builders<BsonDocument>.Filter.Exists("elevation", true)
|
||||||
|
);
|
||||||
|
return await _rawCol.Find(filter).AnyAsync();
|
||||||
|
}
|
||||||
|
|
||||||
private List<BiomeDefinition> LoadBiomeDefinitions()
|
private List<BiomeDefinition> LoadBiomeDefinitions()
|
||||||
{
|
{
|
||||||
return _biomeDefinitions.Find(Builders<BiomeDefinition>.Filter.Empty)
|
return _biomeDefinitions.Find(Builders<BiomeDefinition>.Filter.Empty)
|
||||||
@ -689,6 +742,22 @@ public class LocationStore
|
|||||||
return "plains";
|
return "plains";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int DetermineBaseElevation(int x, int y, string biomeKey)
|
||||||
|
{
|
||||||
|
var macro = StableNoise(x, y, 404);
|
||||||
|
var micro = StableNoise(x, y, 505);
|
||||||
|
|
||||||
|
return biomeKey switch
|
||||||
|
{
|
||||||
|
"wetlands" => (int)Math.Round((macro * 2.0) - 1.0),
|
||||||
|
"plains" => (int)Math.Round((macro * 3.0) - 1.0),
|
||||||
|
"forest" => (int)Math.Round((macro * 4.0) - 1.5),
|
||||||
|
"desert" => (int)Math.Round((macro * 3.0) - 1.0 + ((micro - 0.5) * 0.75)),
|
||||||
|
"rocky" => (int)Math.Round((macro * 5.0) - 1.0 + ((micro - 0.5) * 1.25)),
|
||||||
|
_ => (int)Math.Round((macro * 3.0) - 1.0)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private static double StableNoise(int x, int y, int salt)
|
private static double StableNoise(int x, int y, int salt)
|
||||||
{
|
{
|
||||||
var value = Math.Sin((x * 12.9898) + (y * 78.233) + ((1729 + salt) * 0.1597)) * 43758.5453;
|
var value = Math.Sin((x * 12.9898) + (y * 78.233) + ((1729 + salt) * 0.1597)) * 43758.5453;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user