adding timer cfgs
This commit is contained in:
@@ -33,6 +33,9 @@ public class LocationResponse
|
||||
public string? Label { get; set; }
|
||||
public int Level { get; set; }
|
||||
public string? BuildingType { get; set; }
|
||||
public string? BuildingStatus { get; set; }
|
||||
public DateTime? BuildingStartedAtUtc { get; set; }
|
||||
public DateTime? BuildingCompletesAtUtc { get; set; }
|
||||
public DateTime? BuildingBuiltAtUtc { get; set; }
|
||||
public DateTime? HomeAssignedAtUtc { get; set; }
|
||||
public DateTime UpdatedAtUtc { get; set; }
|
||||
@@ -49,6 +52,11 @@ public class LocationResponse
|
||||
Label = location.Label,
|
||||
Level = location.Level,
|
||||
BuildingType = location.BuildingType,
|
||||
BuildingStatus = location.BuildingType == "LogCamp" && string.IsNullOrWhiteSpace(location.BuildingStatus)
|
||||
? "Built"
|
||||
: location.BuildingStatus,
|
||||
BuildingStartedAtUtc = location.BuildingStartedAtUtc,
|
||||
BuildingCompletesAtUtc = location.BuildingCompletesAtUtc,
|
||||
BuildingBuiltAtUtc = location.BuildingBuiltAtUtc,
|
||||
HomeAssignedAtUtc = location.HomeAssignedAtUtc,
|
||||
UpdatedAtUtc = location.UpdatedAtUtc,
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
using MongoDB.Bson;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
|
||||
namespace LocationsApi.Models;
|
||||
|
||||
public class GameSetting
|
||||
{
|
||||
[BsonId]
|
||||
[BsonRepresentation(BsonType.ObjectId)]
|
||||
public string Id { get; set; } = "";
|
||||
|
||||
public string Key { get; set; } = "";
|
||||
public int IntValue { get; set; }
|
||||
public DateTime CreatedAtUtc { get; set; } = DateTime.UtcNow;
|
||||
public DateTime UpdatedAtUtc { get; set; } = DateTime.UtcNow;
|
||||
}
|
||||
@@ -17,6 +17,9 @@ public class Location
|
||||
public string? Label { get; set; }
|
||||
public int Level { get; set; } = 1;
|
||||
public string? BuildingType { get; set; }
|
||||
public string? BuildingStatus { get; set; }
|
||||
public DateTime? BuildingStartedAtUtc { get; set; }
|
||||
public DateTime? BuildingCompletesAtUtc { get; set; }
|
||||
public DateTime? BuildingBuiltAtUtc { get; set; }
|
||||
public DateTime? HomeAssignedAtUtc { get; set; }
|
||||
public DateTime CreatedAtUtc { get; set; } = DateTime.UtcNow;
|
||||
|
||||
@@ -10,6 +10,7 @@ builder.Services.AddControllers();
|
||||
builder.Services.AddSingleton<LocationService>();
|
||||
builder.Services.AddSingleton<ResourceService>();
|
||||
builder.Services.AddSingleton<UnitTrainingService>();
|
||||
builder.Services.AddSingleton<GameSettingsService>();
|
||||
builder.Services.AddEndpointsApiExplorer();
|
||||
builder.Services.AddSwaggerGen(c =>
|
||||
{
|
||||
|
||||
@@ -41,5 +41,12 @@ unclaimed visible location consumes one completed Scout.
|
||||
## Buildings
|
||||
|
||||
Buildings are stored on location records. The first building type is `LogCamp`,
|
||||
which can be built on controlled Forest territory and adds 5 wood per minute to
|
||||
the owning player's resource generation.
|
||||
which can be built on controlled Forest territory, takes 60 seconds to build,
|
||||
and adds 5 wood per minute to the owning player's resource generation after it
|
||||
finishes.
|
||||
|
||||
Timer settings are stored in the MongoDB `GameSettings` collection and are
|
||||
seeded on first read:
|
||||
|
||||
- `ScoutTrainingSeconds`
|
||||
- `LogCampBuildSeconds`
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
using LocationsApi.Models;
|
||||
using MongoDB.Driver;
|
||||
|
||||
namespace LocationsApi.Services;
|
||||
|
||||
public class GameSettingsService
|
||||
{
|
||||
public const string ScoutTrainingSeconds = "ScoutTrainingSeconds";
|
||||
public const string LogCampBuildSeconds = "LogCampBuildSeconds";
|
||||
|
||||
private readonly IMongoCollection<GameSetting> _settings;
|
||||
|
||||
public GameSettingsService(IConfiguration cfg)
|
||||
{
|
||||
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);
|
||||
_settings = database.GetCollection<GameSetting>("GameSettings");
|
||||
|
||||
_settings.Indexes.CreateOne(new CreateIndexModel<GameSetting>(
|
||||
Builders<GameSetting>.IndexKeys.Ascending(x => x.Key),
|
||||
new CreateIndexOptions { Unique = true }));
|
||||
}
|
||||
|
||||
public async Task<int> GetIntAsync(string key, int defaultValue)
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
var setting = await _settings.FindOneAndUpdateAsync(
|
||||
x => x.Key == key,
|
||||
Builders<GameSetting>.Update
|
||||
.SetOnInsert(x => x.Key, key)
|
||||
.SetOnInsert(x => x.IntValue, defaultValue)
|
||||
.SetOnInsert(x => x.CreatedAtUtc, now)
|
||||
.Set(x => x.UpdatedAtUtc, now),
|
||||
new FindOneAndUpdateOptions<GameSetting>
|
||||
{
|
||||
IsUpsert = true,
|
||||
ReturnDocument = ReturnDocument.After
|
||||
});
|
||||
|
||||
return Math.Max(1, setting.IntValue);
|
||||
}
|
||||
}
|
||||
@@ -7,12 +7,14 @@ public class LocationService
|
||||
{
|
||||
private readonly IMongoCollection<Location> _locations;
|
||||
private readonly ResourceService _resources;
|
||||
private readonly GameSettingsService _settings;
|
||||
private readonly int _homeMinDistance;
|
||||
private readonly int _homeSearchRadius;
|
||||
private readonly int _visibilityDepth;
|
||||
private readonly int _logCampWoodPerMinute;
|
||||
private readonly int _defaultLogCampBuildSeconds;
|
||||
|
||||
public LocationService(IConfiguration cfg, ResourceService resources)
|
||||
public LocationService(IConfiguration cfg, ResourceService resources, GameSettingsService settings)
|
||||
{
|
||||
var connectionString = cfg["MongoDB:ConnectionString"] ?? "mongodb://127.0.0.1:27017";
|
||||
var databaseName = cfg["MongoDB:DatabaseName"] ?? "territory-autobattler";
|
||||
@@ -20,12 +22,16 @@ public class LocationService
|
||||
var database = client.GetDatabase(databaseName);
|
||||
_locations = database.GetCollection<Location>("Locations");
|
||||
_resources = resources;
|
||||
_settings = settings;
|
||||
_homeMinDistance = int.TryParse(cfg["World:HomeMinDistance"], out var minDistance) ? Math.Max(1, minDistance) : 8;
|
||||
_homeSearchRadius = int.TryParse(cfg["World:HomeSearchRadius"], out var searchRadius) ? Math.Max(_homeMinDistance, searchRadius) : 80;
|
||||
_visibilityDepth = int.TryParse(cfg["World:VisibilityDepth"], out var visibilityDepth) ? Math.Max(0, visibilityDepth) : 3;
|
||||
_logCampWoodPerMinute = int.TryParse(cfg["Buildings:LogCampWoodPerMinute"], out var logCampWoodPerMinute)
|
||||
? Math.Max(1, logCampWoodPerMinute)
|
||||
: 5;
|
||||
_defaultLogCampBuildSeconds = int.TryParse(cfg["Buildings:LogCampBuildSeconds"], out var logCampBuildSeconds)
|
||||
? Math.Max(1, logCampBuildSeconds)
|
||||
: 60;
|
||||
|
||||
var coordinateIndex = Builders<Location>.IndexKeys
|
||||
.Ascending(x => x.Q)
|
||||
@@ -139,10 +145,15 @@ public class LocationService
|
||||
throw new InvalidOperationException("Log Camps can only be built on Forest territory.");
|
||||
}
|
||||
|
||||
await CompleteFinishedBuildingsAsync(username);
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
var buildSeconds = await _settings.GetIntAsync(GameSettingsService.LogCampBuildSeconds, _defaultLogCampBuildSeconds);
|
||||
var update = Builders<Location>.Update
|
||||
.Set(x => x.BuildingType, "LogCamp")
|
||||
.Set(x => x.BuildingBuiltAtUtc, now)
|
||||
.Set(x => x.BuildingStatus, "Building")
|
||||
.Set(x => x.BuildingStartedAtUtc, now)
|
||||
.Set(x => x.BuildingCompletesAtUtc, now.AddSeconds(buildSeconds))
|
||||
.Set(x => x.UpdatedAtUtc, now);
|
||||
|
||||
var updated = await _locations.FindOneAndUpdateAsync(
|
||||
@@ -158,10 +169,41 @@ public class LocationService
|
||||
throw new InvalidOperationException("This location already has a building.");
|
||||
}
|
||||
|
||||
await _resources.AddWoodGenerationAsync(username, _logCampWoodPerMinute);
|
||||
return updated;
|
||||
}
|
||||
|
||||
public async Task CompleteFinishedBuildingsAsync(string username)
|
||||
{
|
||||
var now = DateTime.UtcNow;
|
||||
var readyBuildings = await _locations.Find(x =>
|
||||
x.OwnerUsername == username &&
|
||||
x.BuildingType == "LogCamp" &&
|
||||
x.BuildingStatus == "Building" &&
|
||||
x.BuildingCompletesAtUtc <= now)
|
||||
.ToListAsync();
|
||||
|
||||
foreach (var building in readyBuildings)
|
||||
{
|
||||
var update = Builders<Location>.Update
|
||||
.Set(x => x.BuildingStatus, "Built")
|
||||
.Set(x => x.BuildingBuiltAtUtc, now)
|
||||
.Set(x => x.UpdatedAtUtc, now);
|
||||
|
||||
var updated = await _locations.FindOneAndUpdateAsync(
|
||||
x => x.Id == building.Id && x.BuildingStatus == "Building",
|
||||
update,
|
||||
new FindOneAndUpdateOptions<Location>
|
||||
{
|
||||
ReturnDocument = ReturnDocument.After
|
||||
});
|
||||
|
||||
if (updated is not null)
|
||||
{
|
||||
await _resources.AddWoodGenerationAsync(username, _logCampWoodPerMinute);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<Location?> GetHomeAsync(string username) =>
|
||||
await _locations.Find(x => x.OwnerUsername == username && x.Kind == "Home").FirstOrDefaultAsync();
|
||||
|
||||
@@ -204,6 +246,8 @@ public class LocationService
|
||||
|
||||
public async Task<List<LocationResponse>> GetVisibleAsync(string username, int? visibilityDepth = null)
|
||||
{
|
||||
await CompleteFinishedBuildingsAsync(username);
|
||||
|
||||
var ownedLocations = await _locations.Find(x => x.OwnerUsername == username).ToListAsync();
|
||||
if (ownedLocations.Count == 0)
|
||||
{
|
||||
|
||||
@@ -9,10 +9,11 @@ public class UnitTrainingService
|
||||
|
||||
private readonly IMongoCollection<UnitTraining> _training;
|
||||
private readonly ResourceService _resources;
|
||||
private readonly GameSettingsService _settings;
|
||||
private readonly int _scoutGoldCost;
|
||||
private readonly int _scoutTrainingSeconds;
|
||||
private readonly int _defaultScoutTrainingSeconds;
|
||||
|
||||
public UnitTrainingService(IConfiguration cfg, ResourceService resources)
|
||||
public UnitTrainingService(IConfiguration cfg, ResourceService resources, GameSettingsService settings)
|
||||
{
|
||||
var connectionString = cfg["MongoDB:ConnectionString"] ?? "mongodb://127.0.0.1:27017";
|
||||
var databaseName = cfg["MongoDB:DatabaseName"] ?? "territory-autobattler";
|
||||
@@ -20,8 +21,9 @@ public class UnitTrainingService
|
||||
var database = client.GetDatabase(databaseName);
|
||||
_training = database.GetCollection<UnitTraining>("UnitTraining");
|
||||
_resources = resources;
|
||||
_settings = settings;
|
||||
_scoutGoldCost = int.TryParse(cfg["Units:ScoutGoldCost"], out var scoutGoldCost) ? Math.Max(0, scoutGoldCost) : 50;
|
||||
_scoutTrainingSeconds = int.TryParse(cfg["Units:ScoutTrainingSeconds"], out var scoutTrainingSeconds)
|
||||
_defaultScoutTrainingSeconds = int.TryParse(cfg["Units:ScoutTrainingSeconds"], out var scoutTrainingSeconds)
|
||||
? Math.Max(1, scoutTrainingSeconds)
|
||||
: 60;
|
||||
|
||||
@@ -62,6 +64,7 @@ public class UnitTrainingService
|
||||
}
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
var scoutTrainingSeconds = await _settings.GetIntAsync(GameSettingsService.ScoutTrainingSeconds, _defaultScoutTrainingSeconds);
|
||||
var training = new UnitTraining
|
||||
{
|
||||
Username = username,
|
||||
@@ -69,7 +72,7 @@ public class UnitTrainingService
|
||||
GoldCost = _scoutGoldCost,
|
||||
Status = "Training",
|
||||
StartedAtUtc = now,
|
||||
CompletesAtUtc = now.AddSeconds(_scoutTrainingSeconds),
|
||||
CompletesAtUtc = now.AddSeconds(scoutTrainingSeconds),
|
||||
CreatedAtUtc = now,
|
||||
UpdatedAtUtc = now
|
||||
};
|
||||
|
||||
@@ -28,7 +28,8 @@
|
||||
"ScoutTrainingSeconds": 60
|
||||
},
|
||||
"Buildings": {
|
||||
"LogCampWoodPerMinute": 5
|
||||
"LogCampWoodPerMinute": 5,
|
||||
"LogCampBuildSeconds": 60
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
|
||||
@@ -16,3 +16,4 @@ data:
|
||||
Units__ScoutGoldCost: "50"
|
||||
Units__ScoutTrainingSeconds: "60"
|
||||
Buildings__LogCampWoodPerMinute: "5"
|
||||
Buildings__LogCampBuildSeconds: "60"
|
||||
|
||||
Reference in New Issue
Block a user