adding farms

This commit is contained in:
hz
2026-05-19 15:40:31 -05:00
parent 0757344bdf
commit 864835a8f9
11 changed files with 241 additions and 39 deletions
@@ -6,19 +6,22 @@ namespace LocationsApi.Services;
public class ResourceService
{
private readonly IMongoCollection<PlayerResources> _resources;
private readonly GameSettingsService _settings;
private readonly int _startingGold;
private readonly int _headquartersGoldPerMinute;
private readonly int _townHallGoldCapacity;
private readonly int _townHallWoodCapacity;
private readonly int _townHallOreCapacity;
private readonly int _townHallFoodCapacity;
public ResourceService(IConfiguration cfg)
public ResourceService(IConfiguration cfg, GameSettingsService settings)
{
var connectionString = cfg["MongoDB:ConnectionString"] ?? "mongodb://127.0.0.1:27017";
var databaseName = cfg["MongoDB:DatabaseName"] ?? "territory-autobattler";
var client = new MongoClient(connectionString);
var database = client.GetDatabase(databaseName);
_resources = database.GetCollection<PlayerResources>("PlayerResources");
_settings = settings;
_startingGold = int.TryParse(cfg["Resources:StartingGold"], out var startingGold) ? Math.Max(0, startingGold) : 100;
_headquartersGoldPerMinute = int.TryParse(cfg["Resources:HeadquartersGoldPerMinute"], out var goldPerMinute)
? Math.Max(0, goldPerMinute)
@@ -32,6 +35,9 @@ public class ResourceService
_townHallOreCapacity = int.TryParse(cfg["Resources:TownHallOreCapacity"], out var oreCapacity)
? Math.Max(0, oreCapacity)
: 100;
_townHallFoodCapacity = int.TryParse(cfg["Resources:TownHallFoodCapacity"], out var foodCapacity)
? Math.Max(0, foodCapacity)
: 100;
_resources.Indexes.CreateOne(new CreateIndexModel<PlayerResources>(
Builders<PlayerResources>.IndexKeys.Ascending(x => x.Username),
@@ -41,19 +47,28 @@ public class ResourceService
public async Task<PlayerResources> EnsureAsync(string username)
{
var now = DateTime.UtcNow;
var startingGold = await _settings.GetNonNegativeIntAsync(GameSettingsService.StartingGold, _startingGold);
var goldPerMinute = await _settings.GetNonNegativeIntAsync(GameSettingsService.HeadquartersGoldPerMinute, _headquartersGoldPerMinute);
var goldCapacity = await _settings.GetNonNegativeIntAsync(GameSettingsService.TownHallGoldCapacity, _townHallGoldCapacity);
var woodCapacity = await _settings.GetNonNegativeIntAsync(GameSettingsService.TownHallWoodCapacity, _townHallWoodCapacity);
var oreCapacity = await _settings.GetNonNegativeIntAsync(GameSettingsService.TownHallOreCapacity, _townHallOreCapacity);
var foodCapacity = await _settings.GetNonNegativeIntAsync(GameSettingsService.TownHallFoodCapacity, _townHallFoodCapacity);
var update = Builders<PlayerResources>.Update
.SetOnInsert(x => x.Username, username)
.SetOnInsert(x => x.Gold, Math.Min(_startingGold, _townHallGoldCapacity))
.SetOnInsert(x => x.Gold, Math.Min(startingGold, goldCapacity))
.SetOnInsert(x => x.Wood, 0)
.SetOnInsert(x => x.Ore, 0)
.SetOnInsert(x => x.Food, 0)
.SetOnInsert(x => x.CreatedAtUtc, now)
.SetOnInsert(x => x.LastGoldGeneratedAtUtc, now)
.SetOnInsert(x => x.LastWoodGeneratedAtUtc, now)
.SetOnInsert(x => x.LastOreGeneratedAtUtc, now)
.Set(x => x.GoldPerMinute, _headquartersGoldPerMinute)
.Set(x => x.GoldCapacity, _townHallGoldCapacity)
.Set(x => x.WoodCapacity, _townHallWoodCapacity)
.Set(x => x.OreCapacity, _townHallOreCapacity)
.SetOnInsert(x => x.LastFoodGeneratedAtUtc, now)
.Set(x => x.GoldPerMinute, goldPerMinute)
.Set(x => x.GoldCapacity, goldCapacity)
.Set(x => x.WoodCapacity, woodCapacity)
.Set(x => x.OreCapacity, oreCapacity)
.Set(x => x.FoodCapacity, foodCapacity)
.Set(x => x.UpdatedAtUtc, now);
var resources = await _resources.FindOneAndUpdateAsync(
@@ -65,7 +80,7 @@ public class ResourceService
ReturnDocument = ReturnDocument.After
});
return await ApplyGoldGenerationAsync(resources);
return await ApplyResourceGenerationAsync(resources);
}
public async Task<PlayerResources> GetAsync(string username)
@@ -170,10 +185,37 @@ public class ResourceService
await _resources.UpdateOneAsync(x => x.Username == username, update);
}
private async Task<PlayerResources> ApplyGoldGenerationAsync(PlayerResources resources)
public async Task AddFoodGenerationAsync(string username, int foodPerMinute)
{
if (foodPerMinute <= 0)
{
return;
}
var resources = await EnsureAsync(username);
var now = DateTime.UtcNow;
var update = Builders<PlayerResources>.Update
.Inc(x => x.FoodPerMinute, foodPerMinute)
.Set(x => x.UpdatedAtUtc, now);
if (resources.FoodPerMinute <= 0 || resources.LastFoodGeneratedAtUtc.Year < 2000)
{
update = update.Set(x => x.LastFoodGeneratedAtUtc, now);
}
await _resources.UpdateOneAsync(x => x.Username == username, update);
}
private async Task<PlayerResources> ApplyResourceGenerationAsync(PlayerResources resources)
{
var now = DateTime.UtcNow;
var goldElapsedMinutes = resources.GoldPerMinute <= 0
var goldPerMinute = await _settings.GetNonNegativeIntAsync(GameSettingsService.HeadquartersGoldPerMinute, _headquartersGoldPerMinute);
var goldCapacity = await _settings.GetNonNegativeIntAsync(GameSettingsService.TownHallGoldCapacity, _townHallGoldCapacity);
var woodCapacity = await _settings.GetNonNegativeIntAsync(GameSettingsService.TownHallWoodCapacity, _townHallWoodCapacity);
var oreCapacity = await _settings.GetNonNegativeIntAsync(GameSettingsService.TownHallOreCapacity, _townHallOreCapacity);
var foodCapacity = await _settings.GetNonNegativeIntAsync(GameSettingsService.TownHallFoodCapacity, _townHallFoodCapacity);
var goldElapsedMinutes = goldPerMinute <= 0
? 0
: (int)Math.Floor((now - resources.LastGoldGeneratedAtUtc).TotalMinutes);
var woodElapsedMinutes = resources.WoodPerMinute <= 0 || resources.LastWoodGeneratedAtUtc.Year < 2000
@@ -182,21 +224,26 @@ public class ResourceService
var oreElapsedMinutes = resources.OrePerMinute <= 0 || resources.LastOreGeneratedAtUtc.Year < 2000
? 0
: (int)Math.Floor((now - resources.LastOreGeneratedAtUtc).TotalMinutes);
var cappedGold = Math.Min(resources.Gold, _townHallGoldCapacity);
var cappedWood = Math.Min(resources.Wood, _townHallWoodCapacity);
var cappedOre = Math.Min(resources.Ore, _townHallOreCapacity);
var isOverCapacity = resources.Gold != cappedGold || resources.Wood != cappedWood || resources.Ore != cappedOre;
var foodElapsedMinutes = resources.FoodPerMinute <= 0 || resources.LastFoodGeneratedAtUtc.Year < 2000
? 0
: (int)Math.Floor((now - resources.LastFoodGeneratedAtUtc).TotalMinutes);
var cappedGold = Math.Min(resources.Gold, goldCapacity);
var cappedWood = Math.Min(resources.Wood, woodCapacity);
var cappedOre = Math.Min(resources.Ore, oreCapacity);
var cappedFood = Math.Min(resources.Food, foodCapacity);
var isOverCapacity = resources.Gold != cappedGold || resources.Wood != cappedWood || resources.Ore != cappedOre || resources.Food != cappedFood;
if (goldElapsedMinutes <= 0 && woodElapsedMinutes <= 0 && oreElapsedMinutes <= 0 && !isOverCapacity)
if (goldElapsedMinutes <= 0 && woodElapsedMinutes <= 0 && oreElapsedMinutes <= 0 && foodElapsedMinutes <= 0 && !isOverCapacity)
{
return resources;
}
var update = Builders<PlayerResources>.Update
.Set(x => x.GoldPerMinute, _headquartersGoldPerMinute)
.Set(x => x.GoldCapacity, _townHallGoldCapacity)
.Set(x => x.WoodCapacity, _townHallWoodCapacity)
.Set(x => x.OreCapacity, _townHallOreCapacity)
.Set(x => x.GoldPerMinute, goldPerMinute)
.Set(x => x.GoldCapacity, goldCapacity)
.Set(x => x.WoodCapacity, woodCapacity)
.Set(x => x.OreCapacity, oreCapacity)
.Set(x => x.FoodCapacity, foodCapacity)
.Set(x => x.UpdatedAtUtc, now);
if (isOverCapacity)
@@ -204,12 +251,13 @@ public class ResourceService
update = update
.Set(x => x.Gold, cappedGold)
.Set(x => x.Wood, cappedWood)
.Set(x => x.Ore, cappedOre);
.Set(x => x.Ore, cappedOre)
.Set(x => x.Food, cappedFood);
}
if (goldElapsedMinutes > 0)
{
var generatedGold = Math.Min(_townHallGoldCapacity, cappedGold + (goldElapsedMinutes * resources.GoldPerMinute));
var generatedGold = Math.Min(goldCapacity, cappedGold + (goldElapsedMinutes * goldPerMinute));
update = update
.Set(x => x.Gold, generatedGold)
.Set(x => x.LastGoldGeneratedAtUtc, resources.LastGoldGeneratedAtUtc.AddMinutes(goldElapsedMinutes));
@@ -217,7 +265,7 @@ public class ResourceService
if (woodElapsedMinutes > 0)
{
var generatedWood = Math.Min(_townHallWoodCapacity, cappedWood + (woodElapsedMinutes * resources.WoodPerMinute));
var generatedWood = Math.Min(woodCapacity, cappedWood + (woodElapsedMinutes * resources.WoodPerMinute));
update = update
.Set(x => x.Wood, generatedWood)
.Set(x => x.LastWoodGeneratedAtUtc, resources.LastWoodGeneratedAtUtc.AddMinutes(woodElapsedMinutes));
@@ -225,12 +273,20 @@ public class ResourceService
if (oreElapsedMinutes > 0)
{
var generatedOre = Math.Min(_townHallOreCapacity, cappedOre + (oreElapsedMinutes * resources.OrePerMinute));
var generatedOre = Math.Min(oreCapacity, cappedOre + (oreElapsedMinutes * resources.OrePerMinute));
update = update
.Set(x => x.Ore, generatedOre)
.Set(x => x.LastOreGeneratedAtUtc, resources.LastOreGeneratedAtUtc.AddMinutes(oreElapsedMinutes));
}
if (foodElapsedMinutes > 0)
{
var generatedFood = Math.Min(foodCapacity, cappedFood + (foodElapsedMinutes * resources.FoodPerMinute));
update = update
.Set(x => x.Food, generatedFood)
.Set(x => x.LastFoodGeneratedAtUtc, resources.LastFoodGeneratedAtUtc.AddMinutes(foodElapsedMinutes));
}
return await _resources.FindOneAndUpdateAsync(
x => x.Username == resources.Username,
update,