adding mines

This commit is contained in:
hz
2026-05-18 21:37:48 -05:00
parent 973eda5b44
commit 34d406e993
12 changed files with 294 additions and 45 deletions
@@ -7,6 +7,7 @@ public class GameSettingsService
{
public const string ScoutTrainingSeconds = "ScoutTrainingSeconds";
public const string LogCampBuildSeconds = "LogCampBuildSeconds";
public const string MineBuildSeconds = "MineBuildSeconds";
private readonly IMongoCollection<GameSetting> _settings;
@@ -12,7 +12,9 @@ public class LocationService
private readonly int _homeSearchRadius;
private readonly int _visibilityDepth;
private readonly int _logCampWoodPerMinute;
private readonly int _mineOrePerMinute;
private readonly int _defaultLogCampBuildSeconds;
private readonly int _defaultMineBuildSeconds;
public LocationService(IConfiguration cfg, ResourceService resources, GameSettingsService settings)
{
@@ -29,9 +31,15 @@ public class LocationService
_logCampWoodPerMinute = int.TryParse(cfg["Buildings:LogCampWoodPerMinute"], out var logCampWoodPerMinute)
? Math.Max(1, logCampWoodPerMinute)
: 5;
_mineOrePerMinute = int.TryParse(cfg["Buildings:MineOrePerMinute"], out var mineOrePerMinute)
? Math.Max(1, mineOrePerMinute)
: 5;
_defaultLogCampBuildSeconds = int.TryParse(cfg["Buildings:LogCampBuildSeconds"], out var logCampBuildSeconds)
? Math.Max(1, logCampBuildSeconds)
: 60;
_defaultMineBuildSeconds = int.TryParse(cfg["Buildings:MineBuildSeconds"], out var mineBuildSeconds)
? Math.Max(1, mineBuildSeconds)
: 60;
var coordinateIndex = Builders<Location>.IndexKeys
.Ascending(x => x.Q)
@@ -123,7 +131,8 @@ public class LocationService
public async Task<Location?> BuildAsync(BuildLocationRequest request, string username)
{
if (!string.Equals(request.BuildingType, "LogCamp", StringComparison.OrdinalIgnoreCase))
var buildingType = NormalizeBuildingType(request.BuildingType);
if (buildingType is null)
{
throw new ArgumentException("Unsupported building type.", nameof(request.BuildingType));
}
@@ -140,17 +149,24 @@ public class LocationService
throw new InvalidOperationException("This location already has a building or construction in progress.");
}
if (!string.Equals(location.Terrain, "Forest", StringComparison.OrdinalIgnoreCase))
var requiredTerrain = buildingType == "LogCamp" ? "Forest" : "Ore";
if (!string.Equals(location.Terrain, requiredTerrain, StringComparison.OrdinalIgnoreCase))
{
throw new InvalidOperationException("Log Camps can only be built on Forest territory.");
throw new InvalidOperationException($"{buildingType} can only be built on {requiredTerrain} territory.");
}
await CompleteFinishedBuildingsAsync(username);
var now = DateTime.UtcNow;
var buildSeconds = await _settings.GetIntAsync(GameSettingsService.LogCampBuildSeconds, _defaultLogCampBuildSeconds);
var settingKey = buildingType == "LogCamp"
? GameSettingsService.LogCampBuildSeconds
: GameSettingsService.MineBuildSeconds;
var defaultBuildSeconds = buildingType == "LogCamp"
? _defaultLogCampBuildSeconds
: _defaultMineBuildSeconds;
var buildSeconds = await _settings.GetIntAsync(settingKey, defaultBuildSeconds);
var update = Builders<Location>.Update
.Set(x => x.BuildingType, "LogCamp")
.Set(x => x.BuildingType, buildingType)
.Set(x => x.BuildingStatus, "Building")
.Set(x => x.BuildingStartedAtUtc, now)
.Set(x => x.BuildingCompletesAtUtc, now.AddSeconds(buildSeconds))
@@ -177,7 +193,7 @@ public class LocationService
var now = DateTime.UtcNow;
var readyBuildings = await _locations.Find(x =>
x.OwnerUsername == username &&
x.BuildingType == "LogCamp" &&
(x.BuildingType == "LogCamp" || x.BuildingType == "Mine") &&
x.BuildingStatus == "Building" &&
x.BuildingCompletesAtUtc <= now)
.ToListAsync();
@@ -199,7 +215,14 @@ public class LocationService
if (updated is not null)
{
await _resources.AddWoodGenerationAsync(username, _logCampWoodPerMinute);
if (updated.BuildingType == "LogCamp")
{
await _resources.AddWoodGenerationAsync(username, _logCampWoodPerMinute);
}
else if (updated.BuildingType == "Mine")
{
await _resources.AddOreGenerationAsync(username, _mineOrePerMinute);
}
}
}
}
@@ -551,4 +574,15 @@ public class LocationService
_ => "Field"
};
}
private static string? NormalizeBuildingType(string buildingType)
{
var trimmed = buildingType.Trim();
return trimmed.ToLowerInvariant() switch
{
"logcamp" or "log camp" => "LogCamp",
"mine" => "Mine",
_ => null
};
}
}
@@ -33,9 +33,11 @@ public class ResourceService
.SetOnInsert(x => x.Username, username)
.SetOnInsert(x => x.Gold, _startingGold)
.SetOnInsert(x => x.Wood, 0)
.SetOnInsert(x => x.Ore, 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.UpdatedAtUtc, now);
@@ -102,6 +104,28 @@ public class ResourceService
await _resources.UpdateOneAsync(x => x.Username == username, update);
}
public async Task AddOreGenerationAsync(string username, int orePerMinute)
{
if (orePerMinute <= 0)
{
return;
}
var resources = await EnsureAsync(username);
var now = DateTime.UtcNow;
var update = Builders<PlayerResources>.Update
.Inc(x => x.OrePerMinute, orePerMinute)
.Set(x => x.UpdatedAtUtc, now);
if (resources.OrePerMinute <= 0 || resources.LastOreGeneratedAtUtc.Year < 2000)
{
update = update.Set(x => x.LastOreGeneratedAtUtc, now);
}
await _resources.UpdateOneAsync(x => x.Username == username, update);
}
private async Task<PlayerResources> ApplyGoldGenerationAsync(PlayerResources resources)
{
var now = DateTime.UtcNow;
@@ -111,8 +135,11 @@ public class ResourceService
var woodElapsedMinutes = resources.WoodPerMinute <= 0 || resources.LastWoodGeneratedAtUtc.Year < 2000
? 0
: (int)Math.Floor((now - resources.LastWoodGeneratedAtUtc).TotalMinutes);
var oreElapsedMinutes = resources.OrePerMinute <= 0 || resources.LastOreGeneratedAtUtc.Year < 2000
? 0
: (int)Math.Floor((now - resources.LastOreGeneratedAtUtc).TotalMinutes);
if (goldElapsedMinutes <= 0 && woodElapsedMinutes <= 0)
if (goldElapsedMinutes <= 0 && woodElapsedMinutes <= 0 && oreElapsedMinutes <= 0)
{
return resources;
}
@@ -135,6 +162,13 @@ public class ResourceService
.Set(x => x.LastWoodGeneratedAtUtc, resources.LastWoodGeneratedAtUtc.AddMinutes(woodElapsedMinutes));
}
if (oreElapsedMinutes > 0)
{
update = update
.Inc(x => x.Ore, oreElapsedMinutes * resources.OrePerMinute)
.Set(x => x.LastOreGeneratedAtUtc, resources.LastOreGeneratedAtUtc.AddMinutes(oreElapsedMinutes));
}
return await _resources.FindOneAndUpdateAsync(
x => x.Username == resources.Username,
update,
@@ -34,6 +34,7 @@ public class UnitTrainingService
public async Task<PlayerUnitsResponse> GetMineAsync(string username)
{
await CompleteFinishedTrainingAsync(username);
await _training.DeleteManyAsync(x => x.Username == username && x.Status == "Consumed");
var all = await _training.Find(x => x.Username == username).ToListAsync();
var completedScouts = all.Count(x => x.UnitType == ScoutUnitType && x.Status == "Completed");
@@ -85,18 +86,11 @@ public class UnitTrainingService
{
await CompleteFinishedTrainingAsync(username);
var now = DateTime.UtcNow;
var update = Builders<UnitTraining>.Update
.Set(x => x.Status, "Consumed")
.Set(x => x.UpdatedAtUtc, now);
var consumed = await _training.FindOneAndUpdateAsync(
var consumed = await _training.FindOneAndDeleteAsync(
x => x.Username == username && x.UnitType == ScoutUnitType && x.Status == "Completed",
update,
new FindOneAndUpdateOptions<UnitTraining>
new FindOneAndDeleteOptions<UnitTraining>
{
Sort = Builders<UnitTraining>.Sort.Ascending(x => x.CompletedAtUtc).Ascending(x => x.CreatedAtUtc),
ReturnDocument = ReturnDocument.After
Sort = Builders<UnitTraining>.Sort.Ascending(x => x.CompletedAtUtc).Ascending(x => x.CreatedAtUtc)
});
return consumed is not null;