implemented unit movement

This commit is contained in:
hz
2026-05-19 05:29:51 -05:00
parent b0b270f4d8
commit 14e2c26e74
6 changed files with 247 additions and 6 deletions
@@ -18,6 +18,7 @@ public class UnitTrainingService
private readonly int _archerWoodCost;
private readonly int _defaultScoutTrainingSeconds;
private readonly int _defaultArcherTrainingSeconds;
private readonly int _visibilityDepth;
public UnitTrainingService(IConfiguration cfg, ResourceService resources, GameSettingsService settings)
{
@@ -39,6 +40,7 @@ public class UnitTrainingService
_defaultArcherTrainingSeconds = int.TryParse(cfg["Units:ArcherTrainingSeconds"], out var archerTrainingSeconds)
? Math.Max(1, archerTrainingSeconds)
: 60;
_visibilityDepth = int.TryParse(cfg["World:VisibilityDepth"], out var visibilityDepth) ? Math.Max(0, visibilityDepth) : 3;
_training.Indexes.CreateOne(new CreateIndexModel<UnitTraining>(
Builders<UnitTraining>.IndexKeys.Ascending(x => x.Username).Ascending(x => x.Status)));
@@ -138,6 +140,56 @@ public class UnitTrainingService
return training;
}
public async Task<List<UnitInstance>> MoveSquadAsync(string username, MoveUnitSquadRequest request)
{
var normalizedUnitType = NormalizeUnitType(request.UnitType);
if (normalizedUnitType != ArcherUnitType)
{
throw new ArgumentException("Only Archer squads can be moved right now.", nameof(request.UnitType));
}
if (request.FromQ == request.ToQ && request.FromR == request.ToR)
{
throw new InvalidOperationException("Select a different destination tile.");
}
if (!await CanMoveToAsync(username, request.ToQ, request.ToR))
{
throw new InvalidOperationException("Archers can only move to visible non-rival tiles.");
}
var now = DateTime.UtcNow;
var units = await _units.Find(x =>
x.Username == username &&
x.UnitType == ArcherUnitType &&
x.Q == request.FromQ &&
x.R == request.FromR)
.ToListAsync();
if (units.Count == 0)
{
return [];
}
await _units.UpdateManyAsync(
x => x.Username == username &&
x.UnitType == ArcherUnitType &&
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.UpdatedAtUtc, now));
return await _units.Find(x =>
x.Username == username &&
x.UnitType == ArcherUnitType &&
x.Q == request.ToQ &&
x.R == request.ToR)
.ToListAsync();
}
private async Task<Location?> GetBuiltBarracksAsync(string username, int q, int r)
{
await CompleteFinishedBuildingsAsync(username);
@@ -166,6 +218,27 @@ public class UnitTrainingService
update);
}
private async Task<bool> CanMoveToAsync(string username, int q, int r)
{
var ownedLocations = await _locations.Find(x => x.OwnerUsername == username).ToListAsync();
if (!ownedLocations.Any(owned => HexDistance(owned.Q, owned.R, q, r) <= _visibilityDepth))
{
return false;
}
var target = await _locations.Find(x => x.Q == q && x.R == r).FirstOrDefaultAsync();
return target is null ||
string.IsNullOrWhiteSpace(target.OwnerUsername) ||
string.Equals(target.OwnerUsername, username, StringComparison.OrdinalIgnoreCase);
}
private static int HexDistance(int q1, int r1, int q2, int r2)
{
var s1 = -q1 - r1;
var s2 = -q2 - r2;
return Math.Max(Math.Max(Math.Abs(q1 - q2), Math.Abs(r1 - r2)), Math.Abs(s1 - s2));
}
public async Task<bool> TryConsumeCompletedScoutAsync(string username)
{
await CompleteFinishedTrainingAsync(username);