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

This commit is contained in:
Zeeshaun 2026-03-21 13:09:41 -05:00
parent c5e692c051
commit f90de8a98b
5 changed files with 108 additions and 14 deletions

View File

@ -8,8 +8,9 @@ const SETTINGS_SCENE := "res://scenes/UI/Settings.tscn"
const CHARACTER_SLOT_COUNT := 6
@export var tile_size := 8.0
@export var block_height := 1.0
@export_range(1, 8, 1) var tile_radius := 3
@export var block_height := 1.0
@export var elevation_step_height := 0.5
@export_range(1, 8, 1) var tile_radius := 3
@export var tracked_node_path: NodePath
@export var player_spawn_height := 2.0
@export var border_color: Color = Color(0.05, 0.05, 0.05, 1.0)
@ -129,14 +130,14 @@ func _world_to_coord(world_pos: Vector3) -> Vector2i:
)
func _coord_to_world(coord: Vector2i) -> Vector3:
return Vector3(coord.x * tile_size, block_height * 0.5, coord.y * tile_size)
func _coord_to_world(coord: Vector2i) -> Vector3:
return Vector3(coord.x * tile_size, _get_tile_center_y(coord), coord.y * tile_size)
func _move_player_to_coord(coord: Vector2i) -> void:
if _player == null:
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.angular_velocity = Vector3.ZERO
@ -302,11 +303,12 @@ func _spawn_tile(coord: Vector2i, location_data: Dictionary) -> void:
_tile_nodes[coord] = tile_root
func _update_tile(coord: Vector2i, location_data: Dictionary) -> void:
var tile_root := _tile_nodes.get(coord) as Node3D
if tile_root == null:
return
func _update_tile(coord: Vector2i, location_data: Dictionary) -> void:
var tile_root := _tile_nodes.get(coord) as Node3D
if tile_root == null:
return
tile_root.position = _coord_to_world(coord)
if show_tile_labels:
var label := tile_root.get_node_or_null("LocationNameLabel") as Label3D
if label:
@ -480,6 +482,19 @@ func _build_floor_inventory_label(floor_items: Array) -> String:
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:
if _inventory_location_label == null:
return
@ -812,6 +827,7 @@ func _ensure_selected_location_exists(coord: Vector2i) -> void:
"id": "",
"name": _selected_location_name(coord),
"biomeKey": "plains",
"elevation": 0,
"locationObject": {},
"floorItems": []
}
@ -889,6 +905,7 @@ func _load_existing_locations() -> void:
"id": String(location.get("id", "")).strip_edges(),
"name": location_name,
"biomeKey": String(location.get("biomeKey", "plains")).strip_edges(),
"elevation": int(location.get("elevation", 0)),
"locationObject": _parse_location_object(location.get("locationObject", {})),
"floorItems": _parse_floor_inventory_items(location.get("floorItems", []))
}

View File

@ -19,6 +19,9 @@ public class VisibleLocation
[BsonElement("biomeKey")]
public string BiomeKey { get; set; } = "plains";
[BsonElement("elevation")]
public int Elevation { get; set; }
[BsonElement("locationObject")]
public VisibleLocationObject? LocationObject { get; set; }

View File

@ -18,6 +18,9 @@ public class Location
[BsonElement("biomeKey")]
public string BiomeKey { get; set; } = "plains";
[BsonElement("elevation")]
public int Elevation { get; set; }
[BsonElement("resources")]
public List<LocationResource> Resources { get; set; } = [];

View File

@ -10,6 +10,8 @@ public class VisibleLocationResponse
public string BiomeKey { get; set; } = "plains";
public int Elevation { get; set; }
public VisibleLocationObjectResponse? LocationObject { get; set; }
public List<FloorInventoryItemResponse> FloorItems { get; set; } = [];

View File

@ -38,7 +38,7 @@ public class LocationStore
"$jsonSchema", new BsonDocument
{
{ "bsonType", "object" },
{ "required", new BsonArray { "name", "coord", "biomeKey", "createdUtc" } },
{ "required", new BsonArray { "name", "coord", "biomeKey", "elevation", "createdUtc" } },
{
"properties", new BsonDocument
{
@ -58,6 +58,7 @@ public class LocationStore
}
},
{ "biomeKey", new BsonDocument { { "bsonType", "string" } } },
{ "elevation", new BsonDocument { { "bsonType", "int" } } },
{
"resources", new BsonDocument
{
@ -348,6 +349,7 @@ public class LocationStore
Name = "Origin",
Coord = new Coord { X = 0, Y = 0 },
BiomeKey = originBiomeKey,
Elevation = 0,
LocationObject = CreateLocationObjectForBiome(biomeDefinitions, originBiomeKey, 0, 0),
LocationObjectResolved = true,
CreatedUtc = DateTime.UtcNow
@ -423,6 +425,7 @@ public class LocationStore
}
var biomeKey = await DetermineBiomeKeyAsync(x, y, biomeDefinitions);
var elevation = await DetermineElevationAsync(x, y, biomeKey);
var locationObject = CreateLocationObjectForBiome(biomeDefinitions, biomeKey, x, y);
BsonValue locationObjectValue = locationObject is null ? BsonNull.Value : locationObject.ToBsonDocument();
var update = Builders<BsonDocument>.Update
@ -430,6 +433,7 @@ public class LocationStore
.SetOnInsert("name", DefaultLocationName(x, y))
.SetOnInsert("coord", new BsonDocument { { "x", x }, { "y", y } })
.SetOnInsert("biomeKey", biomeKey)
.SetOnInsert("elevation", elevation)
.SetOnInsert("locationObject", locationObjectValue)
.SetOnInsert("locationObjectResolved", true)
.SetOnInsert("createdUtc", DateTime.UtcNow);
@ -447,13 +451,16 @@ public class LocationStore
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;
var biomeDefinitions = await LoadBiomeDefinitionsAsync();
var biomeKey = location.BiomeKey;
if (string.IsNullOrWhiteSpace(biomeKey))
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 filter = Builders<Location>.Filter.And(
@ -461,10 +468,12 @@ public class LocationStore
);
var update = Builders<Location>.Update
.Set(l => l.BiomeKey, biomeKey)
.Set(l => l.Elevation, elevation)
.Set(l => l.LocationObjectResolved, true)
.Set(l => l.LocationObject, migratedObject);
await _col.UpdateOneAsync(filter, update);
location.BiomeKey = biomeKey;
location.Elevation = elevation;
location.LocationObject = migratedObject;
location.LocationObjectResolved = true;
return location;
@ -478,6 +487,7 @@ public class LocationStore
Name = location.Name,
Coord = new Coord { X = location.Coord.X, Y = location.Coord.Y },
BiomeKey = location.BiomeKey,
Elevation = location.Elevation,
LocationObject = MapVisibleLocationObject(location.LocationObject)
};
}
@ -571,6 +581,23 @@ public class LocationStore
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)
{
var biome = biomeDefinitions.FirstOrDefault(definition => definition.BiomeKey == biomeKey)
@ -655,6 +682,32 @@ public class LocationStore
.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()
{
return _biomeDefinitions.Find(Builders<BiomeDefinition>.Filter.Empty)
@ -689,6 +742,22 @@ public class LocationStore
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)
{
var value = Math.Sin((x * 12.9898) + (y * 78.233) + ((1729 + salt) * 0.1597)) * 43758.5453;