Adding upkeep

This commit is contained in:
hz
2026-05-20 04:25:24 -05:00
parent 194dae43da
commit dfbe1d63c0
10 changed files with 530 additions and 40 deletions
@@ -21,6 +21,7 @@ public class UnitTrainingService
private readonly int _defaultScoutTrainingSeconds;
private readonly int _defaultArcherTrainingSeconds;
private readonly int _defaultPeasantTrainingSeconds;
private readonly int _unitMovementPerTick;
private readonly int _visibilityDepth;
public UnitTrainingService(IConfiguration cfg, ResourceService resources, GameSettingsService settings)
@@ -47,6 +48,9 @@ public class UnitTrainingService
_defaultPeasantTrainingSeconds = int.TryParse(cfg["Units:PeasantTrainingSeconds"], out var peasantTrainingSeconds)
? Math.Max(1, peasantTrainingSeconds)
: 60;
_unitMovementPerTick = int.TryParse(cfg["Units:UnitMovementPerTick"], out var unitMovementPerTick)
? Math.Max(1, unitMovementPerTick)
: 1;
_visibilityDepth = int.TryParse(cfg["World:VisibilityDepth"], out var visibilityDepth) ? Math.Max(0, visibilityDepth) : 3;
_training.Indexes.CreateOne(new CreateIndexModel<UnitTraining>(
@@ -65,6 +69,8 @@ public class UnitTrainingService
public async Task<PlayerUnitsResponse> GetMineAsync(string username)
{
await CompleteFinishedTrainingAsync(username);
await CompletePendingMovementAsync(username);
await RefillMovementAsync(username);
await _training.DeleteManyAsync(x => x.Username == username && x.Status == "Consumed");
var allTraining = await _training.Find(x => x.Username == username).ToListAsync();
@@ -167,6 +173,9 @@ public class UnitTrainingService
public async Task<List<UnitInstance>> MoveSquadAsync(string username, MoveUnitSquadRequest request)
{
await CompletePendingMovementAsync(username);
await RefillMovementAsync(username);
var normalizedUnitType = NormalizeUnitType(request.UnitType);
if (normalizedUnitType != ArcherUnitType)
{
@@ -192,6 +201,7 @@ public class UnitTrainingService
var units = await _units.Find(x =>
x.Username == username &&
x.UnitType == ArcherUnitType &&
x.Status == "Idle" &&
x.Q == request.FromQ &&
x.R == request.FromR)
.ToListAsync();
@@ -201,24 +211,35 @@ public class UnitTrainingService
return [];
}
if (units.Any(x => x.MovementRemaining <= 0))
{
throw new InvalidOperationException("That squad has no movement remaining this tick.");
}
var movementCompletesAt = now.AddMinutes(1);
await _units.UpdateManyAsync(
x => x.Username == username &&
x.UnitType == ArcherUnitType &&
x.Status == "Idle" &&
x.Q == request.FromQ &&
x.R == request.FromR,
Builders<UnitInstance>.Update
.Set(x => x.Q, request.ToQ)
.Set(x => x.R, request.ToR)
.Set(x => x.Status, "Idle")
.Set(x => x.Status, "Moving")
.Inc(x => x.MovementRemaining, -1)
.Set(x => x.DestinationQ, request.ToQ)
.Set(x => x.DestinationR, request.ToR)
.Set(x => x.MovementStartedAtUtc, now)
.Set(x => x.MovementCompletesAtUtc, movementCompletesAt)
.Set(x => x.UpdatedAtUtc, now));
await ClaimTargetIfEmptyAsync(username, request.ToQ, request.ToR, now);
return await _units.Find(x =>
x.Username == username &&
x.UnitType == ArcherUnitType &&
x.Q == request.ToQ &&
x.R == request.ToR)
x.Status == "Moving" &&
x.Q == request.FromQ &&
x.R == request.FromR &&
x.DestinationQ == request.ToQ &&
x.DestinationR == request.ToR)
.ToListAsync();
}
@@ -234,6 +255,86 @@ public class UnitTrainingService
.FirstOrDefaultAsync();
}
private async Task CompletePendingMovementAsync(string username)
{
var now = DateTime.UtcNow;
var movementPerTick = await _settings.GetIntAsync(GameSettingsService.UnitMovementPerTick, _unitMovementPerTick);
var movingUnits = await _units.Find(x =>
x.Username == username &&
x.Status == "Moving" &&
x.MovementCompletesAtUtc != null &&
x.MovementCompletesAtUtc <= now)
.ToListAsync();
foreach (var unit in movingUnits)
{
if (unit.DestinationQ is null || unit.DestinationR is null)
{
await _units.UpdateOneAsync(
x => x.Id == unit.Id,
Builders<UnitInstance>.Update
.Set(x => x.Status, "Idle")
.Set(x => x.MovementPerTick, movementPerTick)
.Set(x => x.MovementRemaining, movementPerTick)
.Unset(x => x.MovementStartedAtUtc)
.Unset(x => x.MovementCompletesAtUtc)
.Set(x => x.UpdatedAtUtc, now));
continue;
}
await _units.UpdateOneAsync(
x => x.Id == unit.Id,
Builders<UnitInstance>.Update
.Set(x => x.Q, unit.DestinationQ.Value)
.Set(x => x.R, unit.DestinationR.Value)
.Set(x => x.Status, "Idle")
.Set(x => x.MovementPerTick, movementPerTick)
.Set(x => x.MovementRemaining, movementPerTick)
.Set(x => x.LastMovementRefillAtUtc, now)
.Unset(x => x.DestinationQ)
.Unset(x => x.DestinationR)
.Unset(x => x.MovementStartedAtUtc)
.Unset(x => x.MovementCompletesAtUtc)
.Set(x => x.UpdatedAtUtc, now));
if (unit.UnitType == ArcherUnitType)
{
await ClaimTargetIfEmptyAsync(username, unit.DestinationQ.Value, unit.DestinationR.Value, now);
}
}
}
private async Task RefillMovementAsync(string username)
{
var now = DateTime.UtcNow;
var movementPerTick = await _settings.GetIntAsync(GameSettingsService.UnitMovementPerTick, _unitMovementPerTick);
var units = await _units.Find(x => x.Username == username && x.Status != "Moving" && x.Status != "MovingToWork").ToListAsync();
foreach (var unit in units)
{
var lastRefill = unit.LastMovementRefillAtUtc.Year < 2000 ? now : unit.LastMovementRefillAtUtc;
var elapsedTicks = (int)Math.Floor((now - lastRefill).TotalMinutes);
var movementRemaining = elapsedTicks > 0
? movementPerTick
: Math.Min(unit.MovementRemaining, movementPerTick);
if (elapsedTicks <= 0 && unit.MovementPerTick == movementPerTick && unit.MovementRemaining == movementRemaining)
{
continue;
}
var update = Builders<UnitInstance>.Update
.Set(x => x.MovementPerTick, movementPerTick)
.Set(x => x.MovementRemaining, movementRemaining)
.Set(x => x.UpdatedAtUtc, now);
if (elapsedTicks > 0)
{
update = update.Set(x => x.LastMovementRefillAtUtc, lastRefill.AddMinutes(elapsedTicks));
}
await _units.UpdateOneAsync(x => x.Id == unit.Id, update);
}
}
private async Task CompleteFinishedBuildingsAsync(string username)
{
var now = DateTime.UtcNow;
@@ -376,6 +477,7 @@ public class UnitTrainingService
continue;
}
var movementPerTick = await _settings.GetIntAsync(GameSettingsService.UnitMovementPerTick, _unitMovementPerTick);
var unit = new UnitInstance
{
Username = completed.Username,
@@ -383,6 +485,9 @@ public class UnitTrainingService
Q = completed.OriginQ ?? 0,
R = completed.OriginR ?? 0,
Status = "Idle",
MovementPerTick = movementPerTick,
MovementRemaining = movementPerTick,
LastMovementRefillAtUtc = now,
SourceTrainingId = completed.Id,
CreatedAtUtc = now,
UpdatedAtUtc = now