Adding elevation
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:
2026-03-21 13:09:41 -05:00
parent c5e692c051
commit f90de8a98b
5 changed files with 108 additions and 14 deletions
@@ -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; }
@@ -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; } = [];
@@ -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; } = [];
@@ -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;