territory claiming

This commit is contained in:
hz
2026-05-18 15:40:15 -05:00
parent 37a46a7b36
commit 3167558e04
5 changed files with 127 additions and 4 deletions
@@ -11,10 +11,12 @@ namespace LocationsApi.Controllers;
public class LocationsController : ControllerBase
{
private readonly LocationService _locations;
private readonly UnitTrainingService _units;
public LocationsController(LocationService locations)
public LocationsController(LocationService locations, UnitTrainingService units)
{
_locations = locations;
_units = units;
}
[Authorize(Roles = "USER,SUPER")]
@@ -86,6 +88,22 @@ public class LocationsController : ControllerBase
return NotFound();
}
var existingLocation = await _locations.GetAsync(request.Q, request.R);
if (existingLocation is not null && !string.IsNullOrWhiteSpace(existingLocation.OwnerUsername))
{
if (string.Equals(existingLocation.OwnerUsername, username, StringComparison.OrdinalIgnoreCase))
{
return Ok(LocationResponse.FromLocation(existingLocation));
}
return Conflict("Location is already controlled by another player.");
}
if (!User.IsInRole("SUPER") && !await _units.TryConsumeCompletedScoutAsync(username))
{
return BadRequest("A completed scout is required to claim unclaimed territory.");
}
var location = await _locations.ClaimAsync(request.Q, request.R, username);
if (location is null)
{
+3 -2
View File
@@ -7,7 +7,7 @@ Tracks hex locations for the territory map.
- `GET /api/locations` List visible locations for USER/SUPER, all known locations for SUPER.
- `GET /api/locations?minQ=-7&maxQ=7&minR=-7&maxR=7` List a coordinate range, constrained by visibility for USER.
- `GET /api/locations/{q}/{r}` Fetch one visible location for USER, any known location for SUPER.
- `POST /api/locations/claim` Claim a location for the authenticated user.
- `POST /api/locations/claim` Claim visible unclaimed territory for the authenticated user. Normal players spend one completed Scout.
- `GET /api/locations/home` Get the authenticated user's home location.
- `POST /api/locations/home` Assign or return the authenticated user's home location.
- `GET /api/locations/visible` Get locations visible to the authenticated user.
@@ -33,4 +33,5 @@ minute. Generation is applied lazily when the resource record is read.
Unit training is stored server-side in the `UnitTraining` collection. The first
unit type is `Scout`, which costs 50 gold and takes 60 seconds to train.
Finished training is completed lazily when unit state is read.
Finished training is completed lazily when unit state is read. Claiming an
unclaimed visible location consumes one completed Scout.
@@ -78,6 +78,33 @@ public class UnitTrainingService
return training;
}
public async Task<bool> TryConsumeCompletedScoutAsync(string username)
{
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(
x => x.Username == username && x.UnitType == ScoutUnitType && x.Status == "Completed",
update,
new FindOneAndUpdateOptions<UnitTraining>
{
Sort = Builders<UnitTraining>.Sort.Ascending(x => x.CompletedAtUtc).Ascending(x => x.CreatedAtUtc),
ReturnDocument = ReturnDocument.After
});
return consumed is not null;
}
public async Task<bool> HasCompletedScoutAsync(string username)
{
await CompleteFinishedTrainingAsync(username);
return await _training.Find(x => x.Username == username && x.UnitType == ScoutUnitType && x.Status == "Completed").AnyAsync();
}
private async Task CompleteFinishedTrainingAsync(string username)
{
var now = DateTime.UtcNow;