Bring plants back as data-driven growth stages over the calendar
Bump the engine submodule to f791ee6 (Calendar) and use it to drive plant growth. PlantDef gains a Stages list (texture/size/growDays per stage, inheritable via abstract parents in plants.json); a resolved PlantSet table turns those into atlas regions and day thresholds at scene load. New PlantGrowth component plus PlantGrowthSystem advance each plant by in-game days from the Calendar and swap its sprite/scale at stage borders. WorldScene scatters plants again with a seed-deterministic, varied initial maturity, registers the calendar and growth system, and the HUD now shows the current day. Stage durations and visuals are pure data in plants.json. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
923644dd19
commit
1030f27c50
@@ -1,15 +1,28 @@
|
||||
{
|
||||
"type": "Plant",
|
||||
"defs": [
|
||||
{ "defName": "BaseTree", "abstract": true, "sizeCells": 2.0, "trunkRadiusCells": 0.28 },
|
||||
{ "defName": "BaseTree", "abstract": true, "sizeCells": 2.0, "trunkRadiusCells": 0.28,
|
||||
"stages": [
|
||||
{ "texture": "things/plant/seed_default", "sizeCells": 0.6, "growDays": 4, "label": "plant.stage.seedling" },
|
||||
{ "sizeCells": 1.2, "growDays": 9, "label": "plant.stage.sapling" },
|
||||
{ "sizeCells": 2.0, "label": "plant.stage.mature" }
|
||||
] },
|
||||
{ "defName": "TreeOakA", "parent": "BaseTree", "label": "plant.oak", "texture": "things/plant/treeoak/TreeOakA" },
|
||||
{ "defName": "TreeOakB", "parent": "BaseTree", "label": "plant.oak", "texture": "things/plant/treeoak/TreeOakB" },
|
||||
{ "defName": "TreeBirchA", "parent": "BaseTree", "label": "plant.birch", "texture": "things/plant/treebirch/TreeBirchA" },
|
||||
{ "defName": "TreeGrayPineA", "parent": "BaseTree", "label": "plant.pine", "texture": "things/plant/treegraypine/TreeGrayPineA" },
|
||||
|
||||
{ "defName": "GrassA", "label": "plant.grass", "texture": "things/plant/grass/grassa", "sizeCells": 1.2 },
|
||||
{ "defName": "GrassA", "label": "plant.grass", "texture": "things/plant/grass/grassa", "sizeCells": 1.2,
|
||||
"stages": [
|
||||
{ "texture": "things/plant/seed_default", "sizeCells": 0.5, "growDays": 2, "label": "plant.stage.sprout" },
|
||||
{ "sizeCells": 1.2, "label": "plant.stage.mature" }
|
||||
] },
|
||||
|
||||
{ "defName": "BaseBush", "abstract": true, "label": "plant.bush", "sizeCells": 1.4 },
|
||||
{ "defName": "BaseBush", "abstract": true, "label": "plant.bush", "sizeCells": 1.4,
|
||||
"stages": [
|
||||
{ "texture": "things/plant/seed_default", "sizeCells": 0.6, "growDays": 3, "label": "plant.stage.sprout" },
|
||||
{ "sizeCells": 1.4, "label": "plant.stage.mature" }
|
||||
] },
|
||||
{ "defName": "BushA", "parent": "BaseBush", "texture": "things/plant/bush/BushA" },
|
||||
{ "defName": "BushB", "parent": "BaseBush", "texture": "things/plant/bush/BushB" },
|
||||
{ "defName": "BushC", "parent": "BaseBush", "texture": "things/plant/bush/BushC" }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"hud.world": "LittleSim | seed {0} | {2} FPS",
|
||||
"hud.world": "LittleSim | seed {0} | day {1} | {2} FPS",
|
||||
"hud.controls": "WASD — camera, wheel — zoom, ` — console ('help' lists commands)",
|
||||
"hud.paused": "PAUSED",
|
||||
|
||||
@@ -67,6 +67,10 @@
|
||||
"plant.pine": "pine",
|
||||
"plant.grass": "grass tuft",
|
||||
"plant.bush": "bush",
|
||||
"plant.stage.seedling": "seedling",
|
||||
"plant.stage.sprout": "sprout",
|
||||
"plant.stage.sapling": "sapling",
|
||||
"plant.stage.mature": "mature",
|
||||
|
||||
"pawn.being": "being",
|
||||
"pawn.bear": "bear",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"hud.world": "LittleSim | сид {0} | {2} FPS",
|
||||
"hud.world": "LittleSim | сид {0} | день {1} | {2} FPS",
|
||||
"hud.controls": "WASD — камера, колесо — зум, ` — консоль ('help' — список команд)",
|
||||
"hud.paused": "ПАУЗА",
|
||||
|
||||
@@ -67,6 +67,10 @@
|
||||
"plant.pine": "сосна",
|
||||
"plant.grass": "пучок травы",
|
||||
"plant.bush": "куст",
|
||||
"plant.stage.seedling": "росток",
|
||||
"plant.stage.sprout": "всходы",
|
||||
"plant.stage.sapling": "саженец",
|
||||
"plant.stage.mature": "взрослое",
|
||||
|
||||
"pawn.being": "житель",
|
||||
"pawn.bear": "медведь",
|
||||
|
||||
+1
-1
Submodule engine updated: 53659450a6...f791ee6c95
@@ -37,17 +37,40 @@ public sealed class TerrainDef : Def
|
||||
public Color Tint => Rgb is [var r, var g, var b, ..] ? new(r, g, b) : Color.Magenta;
|
||||
}
|
||||
|
||||
/// <summary>Растение (Defs/plants.json): текстура, размер, опциональный ствол-препятствие.</summary>
|
||||
/// <summary>
|
||||
/// Одна стадия роста растения (вложенный объект <see cref="PlantDef.Stages"/>): свой спрайт и
|
||||
/// размер, и сколько игровых дней растение проводит на этой стадии до перехода к следующей.
|
||||
/// Растёт на месте — последняя стадия терминальная (её <see cref="GrowDays"/> игнорируется).
|
||||
/// </summary>
|
||||
public sealed class PlantGrowthStage
|
||||
{
|
||||
/// <summary>Ключ текстуры стадии; null — берётся базовая <see cref="PlantDef.Texture"/>.</summary>
|
||||
public string? Texture { get; init; }
|
||||
|
||||
/// <summary>Размер спрайта в клетках на этой стадии; 0 — берётся базовый <see cref="PlantDef.SizeCells"/>.</summary>
|
||||
public float SizeCells { get; init; }
|
||||
|
||||
/// <summary>Сколько игровых дней расти на этой стадии до перехода к следующей.</summary>
|
||||
public float GrowDays { get; init; }
|
||||
|
||||
/// <summary>Ключ локализации названия стадии (опционально, для будущих тултипов).</summary>
|
||||
public string? Label { get; init; }
|
||||
}
|
||||
|
||||
/// <summary>Растение (Defs/plants.json): текстура, размер, опциональный ствол-препятствие, стадии роста.</summary>
|
||||
public sealed class PlantDef : Def
|
||||
{
|
||||
/// <summary>Ключ текстуры (путь региона в атласах).</summary>
|
||||
/// <summary>Ключ текстуры (путь региона в атласах); база для стадий без своей текстуры.</summary>
|
||||
public string Texture { get; init; } = "";
|
||||
|
||||
/// <summary>Размер спрайта в клетках.</summary>
|
||||
/// <summary>Базовый размер спрайта в клетках; база для стадий без своего размера.</summary>
|
||||
public float SizeCells { get; init; } = 1f;
|
||||
|
||||
/// <summary>Радиус коллайдера ствола в клетках; 0 — без коллайдера (трава, кусты).</summary>
|
||||
public float TrunkRadiusCells { get; init; }
|
||||
|
||||
/// <summary>Стадии роста по порядку; пусто — одна терминальная стадия из базовой текстуры.</summary>
|
||||
public List<PlantGrowthStage> Stages { get; init; } = [];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using MrGameEng.Graphics;
|
||||
|
||||
namespace LittleSim.Content;
|
||||
|
||||
/// <summary>
|
||||
/// Готовая (resolved) таблица растений для сцены: по каждому <see cref="PlantDef"/> — массив
|
||||
/// стадий с уже загруженными регионами атласа, размерами и порогами входа в днях. Строится один
|
||||
/// раз при загрузке сцены (как <see cref="MrGameEng.Tilemaps.TileSet"/>, потому что нужны атлас и
|
||||
/// устройство). <see cref="Sim.PlantGrowthSystem"/> индексируется в неё без словарей и аллокаций.
|
||||
/// </summary>
|
||||
public sealed class PlantSet
|
||||
{
|
||||
/// <summary>Разрешённая стадия: спрайт, размер в клетках и день, с которого стадия активна.</summary>
|
||||
public readonly record struct Stage(Texture2DRegion Region, float SizeCells, float EnterDay);
|
||||
|
||||
/// <summary>Вид растения: его деф и стадии роста по возрастанию <see cref="Stage.EnterDay"/>.</summary>
|
||||
public sealed class Species
|
||||
{
|
||||
/// <summary>Исходный деф растения.</summary>
|
||||
public required PlantDef Def { get; init; }
|
||||
|
||||
/// <summary>Стадии роста по порядку; последняя — терминальная.</summary>
|
||||
public required Stage[] Stages { get; init; }
|
||||
|
||||
/// <summary>День, с которого растение достигает последней (зрелой) стадии.</summary>
|
||||
public float MaturityDays => Stages[^1].EnterDay;
|
||||
|
||||
/// <summary>Индекс стадии для данного возраста в днях.</summary>
|
||||
public int StageAt(float ageDays)
|
||||
{
|
||||
var stage = 0;
|
||||
while (stage + 1 < Stages.Length && ageDays >= Stages[stage + 1].EnterDay)
|
||||
{
|
||||
stage++;
|
||||
}
|
||||
|
||||
return stage;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly Species[] _species;
|
||||
private readonly Dictionary<PlantDef, int> _index = new();
|
||||
|
||||
/// <summary>Строит таблицу из всех (не-abstract) <see cref="PlantDef"/> мода.</summary>
|
||||
public PlantSet(GameContent content, ModAtlases atlases, GraphicsDevice device)
|
||||
{
|
||||
var defs = content.Defs.All<PlantDef>().ToArray();
|
||||
_species = new Species[defs.Length];
|
||||
for (var i = 0; i < defs.Length; i++)
|
||||
{
|
||||
var def = defs[i];
|
||||
_species[i] = new Species { Def = def, Stages = BuildStages(def, atlases, device) };
|
||||
_index[def] = i;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Число видов в таблице.</summary>
|
||||
public int Count => _species.Length;
|
||||
|
||||
/// <summary>Вид по индексу.</summary>
|
||||
public Species this[int index] => _species[index];
|
||||
|
||||
/// <summary>Индекс вида по дефу.</summary>
|
||||
public int IndexOf(PlantDef def) => _index[def];
|
||||
|
||||
private static Stage[] BuildStages(PlantDef def, ModAtlases atlases, GraphicsDevice device)
|
||||
{
|
||||
// Без явных стадий растение существует как одна терминальная стадия из базовой текстуры.
|
||||
if (def.Stages.Count == 0)
|
||||
{
|
||||
var region = atlases.GetRegion(device, def.Texture);
|
||||
return [new Stage(region, def.SizeCells, 0f)];
|
||||
}
|
||||
|
||||
var stages = new Stage[def.Stages.Count];
|
||||
var enterDay = 0f;
|
||||
for (var i = 0; i < def.Stages.Count; i++)
|
||||
{
|
||||
var stage = def.Stages[i];
|
||||
var region = atlases.GetRegion(device, stage.Texture ?? def.Texture);
|
||||
var size = stage.SizeCells > 0f ? stage.SizeCells : def.SizeCells;
|
||||
stages[i] = new Stage(region, size, enterDay);
|
||||
enterDay += stage.GrowDays; // следующая стадия входит после GrowDays этой
|
||||
}
|
||||
|
||||
return stages;
|
||||
}
|
||||
}
|
||||
@@ -1,32 +1,30 @@
|
||||
using Friflo.Engine.ECS;
|
||||
using LittleSim.Content;
|
||||
using LittleSim.Sim;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using MrGameEng.Core;
|
||||
using MrGameEng.DevConsole;
|
||||
using MrGameEng.Graphics;
|
||||
|
||||
namespace LittleSim.Scenes;
|
||||
|
||||
/// <summary>Спавн растительности по скаттеру дефов террейна (общий для обеих сцен).</summary>
|
||||
/// <summary>Спавн растительности по скаттеру дефов террейна.</summary>
|
||||
internal static class ScatterSpawner
|
||||
{
|
||||
/// <summary>
|
||||
/// Бросает каждую запись скаттера клетки независимо; при успехе ставит одно растение
|
||||
/// со случайным смещением. Деревья (ствол > 0) получают коллайдер-препятствие,
|
||||
/// когда заданы слои коллизий.
|
||||
/// Бросает каждую запись скаттера клетки независимо; при успехе ставит одно растение со
|
||||
/// случайным смещением и случайной (детерминированной сидом) зрелостью — мир после генерации
|
||||
/// разновозрастный. Стадия и спрайт берутся из <paramref name="plants"/>, дальше растением
|
||||
/// управляет <see cref="PlantGrowthSystem"/>.
|
||||
/// </summary>
|
||||
public static void Spawn(
|
||||
Scene scene,
|
||||
GameContent content,
|
||||
ModAtlases atlases,
|
||||
GraphicsDevice device,
|
||||
PlantSet plants,
|
||||
TerrainDef terrain,
|
||||
Point cell,
|
||||
int cellSize,
|
||||
Random random,
|
||||
uint obstacleLayer = 0,
|
||||
uint collidesWith = 0
|
||||
Random random
|
||||
)
|
||||
{
|
||||
foreach (var entry in terrain.Scatter)
|
||||
@@ -36,28 +34,30 @@ internal static class ScatterSpawner
|
||||
continue;
|
||||
}
|
||||
|
||||
var plant = content.Defs.Get<PlantDef>(entry.Options[random.Next(entry.Options.Count)]);
|
||||
var region = atlases.GetRegion(device, plant.Texture);
|
||||
var sprite = new Sprite(region, GameLayers.Beings);
|
||||
sprite.CenterOrigin();
|
||||
var entity = scene.Store.CreateEntity(
|
||||
new Transform2D(
|
||||
new Vector2(
|
||||
(cell.X + 0.3f + random.NextSingle() * 0.4f) * cellSize,
|
||||
(cell.Y + 0.3f + random.NextSingle() * 0.4f) * cellSize
|
||||
),
|
||||
scale: new Vector2(cellSize * plant.SizeCells / region.Width)
|
||||
),
|
||||
sprite
|
||||
);
|
||||
var def = content.Defs.Get<PlantDef>(entry.Options[random.Next(entry.Options.Count)]);
|
||||
var index = plants.IndexOf(def);
|
||||
var species = plants[index];
|
||||
var px = (cell.X + 0.3f + random.NextSingle() * 0.4f) * cellSize;
|
||||
var py = (cell.Y + 0.3f + random.NextSingle() * 0.4f) * cellSize;
|
||||
var age = random.NextSingle() * species.MaturityDays;
|
||||
var stage = species.StageAt(age);
|
||||
var resolved = species.Stages[stage];
|
||||
|
||||
if (obstacleLayer != 0 && plant.TrunkRadiusCells > 0f)
|
||||
{
|
||||
var trunk = MrGameEng.Collisions.Collider.Circle(cellSize * plant.TrunkRadiusCells);
|
||||
trunk.Layer = obstacleLayer;
|
||||
trunk.CollidesWith = collidesWith;
|
||||
entity.AddComponent(trunk);
|
||||
}
|
||||
var sprite = new Sprite(resolved.Region, GameLayers.Beings);
|
||||
sprite.CenterOrigin();
|
||||
scene.Store.CreateEntity(
|
||||
new Transform2D(
|
||||
new Vector2(px, py),
|
||||
scale: new Vector2(cellSize * resolved.SizeCells / resolved.Region.Width)
|
||||
),
|
||||
sprite,
|
||||
new PlantGrowth
|
||||
{
|
||||
Species = index,
|
||||
Stage = stage,
|
||||
AgeDays = age,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,9 @@ public sealed class WorldScene : Scene
|
||||
{
|
||||
public const int CellSize = 16;
|
||||
|
||||
/// <summary>Сколько секунд масштабированного времени длится один игровой день.</summary>
|
||||
public const float SecondsPerDay = 30f;
|
||||
|
||||
private readonly WorldConfig _config;
|
||||
private readonly WorldSave? _save;
|
||||
private readonly RectF _bounds;
|
||||
@@ -66,7 +69,12 @@ public sealed class WorldScene : Scene
|
||||
GameLayers.EnsureRegistered(renderer);
|
||||
this.UseTilemaps();
|
||||
|
||||
BuildTerrain(content, atlases, device, assets);
|
||||
var calendar = Context.UseCalendar(SecondsPerDay);
|
||||
|
||||
// Рельеф и расстановка растений детерминированы сидом мира (независимые потоки seed).
|
||||
var plants = new PlantSet(content, atlases, device);
|
||||
var random = new Random(_config.Seed);
|
||||
BuildTerrain(content, atlases, device, assets, plants, random);
|
||||
|
||||
var camera = Store.CreateEntity(new Camera(_bounds.Center, zoom: 1f, bounds: _bounds));
|
||||
|
||||
@@ -87,9 +95,10 @@ public sealed class WorldScene : Scene
|
||||
var console = this.UseDevConsole();
|
||||
RegisterCommands(console, content, atlases);
|
||||
|
||||
UpdateSystems.Add(new PlantGrowthSystem(plants, calendar, CellSize));
|
||||
UpdateSystems.Add(new GodCameraSystem(camera, input, console, () => _pause.IsOpen));
|
||||
UpdateSystems.Add(
|
||||
new HudSystem(Context, content.Languages, hudLabel, "hud.world", _config.Seed, 0)
|
||||
new HudSystem(Context, content.Languages, hudLabel, "hud.world", _config.Seed, calendar)
|
||||
);
|
||||
UpdateSystems.Add(new CallbackSystem(() => Hotkeys(input)));
|
||||
|
||||
@@ -101,13 +110,16 @@ public sealed class WorldScene : Scene
|
||||
|
||||
/// <summary>
|
||||
/// Строит одну сущность-<see cref="Tilemap"/>: тайлсет из дефов рельефа (поверхность из
|
||||
/// атласа либо тонированный тайл) и сетка тайлов по карте высот процедурной генерации.
|
||||
/// атласа либо тонированный тайл) и сетка тайлов по карте высот процедурной генерации, а
|
||||
/// поверх — растительность по скаттеру дефов (с компонентом роста, разной зрелости).
|
||||
/// </summary>
|
||||
private void BuildTerrain(
|
||||
GameContent content,
|
||||
ModAtlases atlases,
|
||||
Microsoft.Xna.Framework.Graphics.GraphicsDevice device,
|
||||
AssetManager assets
|
||||
AssetManager assets,
|
||||
PlantSet plants,
|
||||
Random random
|
||||
)
|
||||
{
|
||||
var white = new Texture2DRegion(assets.Load(GameAssets.Textures.White));
|
||||
@@ -134,7 +146,17 @@ public sealed class WorldScene : Scene
|
||||
{
|
||||
for (var y = 0; y < _config.Height; y++)
|
||||
{
|
||||
grid[x, y] = tileByDef[content.Terrains.Classify(heights[x, y])];
|
||||
var terrain = content.Terrains.Classify(heights[x, y]);
|
||||
grid[x, y] = tileByDef[terrain];
|
||||
ScatterSpawner.Spawn(
|
||||
this,
|
||||
content,
|
||||
plants,
|
||||
terrain,
|
||||
new Point(x, y),
|
||||
CellSize,
|
||||
random
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
using Friflo.Engine.ECS;
|
||||
|
||||
namespace LittleSim.Sim;
|
||||
|
||||
/// <summary>
|
||||
/// Состояние роста растения: индекс вида в <see cref="Content.PlantSet"/>, текущая стадия и
|
||||
/// накопленный возраст в игровых днях. Плоские данные — переход между стадиями и смену спрайта
|
||||
/// выполняет <see cref="PlantGrowthSystem"/>, читая пороги стадий из таблицы вида.
|
||||
/// </summary>
|
||||
public struct PlantGrowth : IComponent
|
||||
{
|
||||
/// <summary>Индекс вида растения в <see cref="Content.PlantSet"/>.</summary>
|
||||
public int Species;
|
||||
|
||||
/// <summary>Текущая стадия роста (индекс в стадиях вида).</summary>
|
||||
public int Stage;
|
||||
|
||||
/// <summary>Накопленный возраст в игровых днях.</summary>
|
||||
public float AgeDays;
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
using Friflo.Engine.ECS.Systems;
|
||||
using LittleSim.Content;
|
||||
using Microsoft.Xna.Framework;
|
||||
using MrGameEng.Core;
|
||||
using MrGameEng.Graphics;
|
||||
|
||||
namespace LittleSim.Sim;
|
||||
|
||||
/// <summary>
|
||||
/// Двигает рост растений: копит возраст по игровому календарю и при пересечении порога стадии
|
||||
/// переключает спрайт на текстуру новой стадии, переотцентровывает и масштабирует его. Время
|
||||
/// идёт по <see cref="Calendar"/> (пауза/скорость учитываются через <c>Tick.deltaTime</c>),
|
||||
/// данные стадий — из <see cref="PlantSet"/>. Терминальную стадию пропускает (рост окончен).
|
||||
/// </summary>
|
||||
public sealed class PlantGrowthSystem(PlantSet plants, Calendar calendar, int cellSize)
|
||||
: QuerySystem<PlantGrowth, Sprite, Transform2D>
|
||||
{
|
||||
protected override void OnUpdate()
|
||||
{
|
||||
var deltaDays = Tick.deltaTime / calendar.SecondsPerDay;
|
||||
if (deltaDays <= 0f)
|
||||
{
|
||||
return; // пауза — рост стоит
|
||||
}
|
||||
|
||||
foreach (var (growths, sprites, transforms, _) in Query.Chunks)
|
||||
{
|
||||
var g = growths.Span;
|
||||
var s = sprites.Span;
|
||||
var t = transforms.Span;
|
||||
for (var i = 0; i < g.Length; i++)
|
||||
{
|
||||
ref var grow = ref g[i];
|
||||
var stages = plants[grow.Species].Stages;
|
||||
if (grow.Stage >= stages.Length - 1)
|
||||
{
|
||||
continue; // уже зрелое — рост окончен
|
||||
}
|
||||
|
||||
grow.AgeDays += deltaDays;
|
||||
var changed = false;
|
||||
while (
|
||||
grow.Stage < stages.Length - 1
|
||||
&& grow.AgeDays >= stages[grow.Stage + 1].EnterDay
|
||||
)
|
||||
{
|
||||
grow.Stage++;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed)
|
||||
{
|
||||
Apply(ref s[i], ref t[i], stages[grow.Stage]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Apply(ref Sprite sprite, ref Transform2D transform, in PlantSet.Stage stage)
|
||||
{
|
||||
sprite.Region = stage.Region;
|
||||
sprite.Origin = new Vector2(stage.Region.Width / 2f, stage.Region.Height / 2f);
|
||||
transform.Scale = new Vector2(cellSize * stage.SizeCells / stage.Region.Width);
|
||||
}
|
||||
}
|
||||
@@ -161,7 +161,7 @@ public sealed class GodCameraSystem(
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Строка статуса в HUD: заголовок сцены (ключ локализации с сидом, населением и FPS)
|
||||
/// Строка статуса в HUD: заголовок сцены (ключ локализации с сидом, днём календаря и FPS)
|
||||
/// и подсказка управления. Обновляется 4 раза в секунду; смена языка подхватывается сама.
|
||||
/// </summary>
|
||||
public sealed class HudSystem(
|
||||
@@ -170,7 +170,7 @@ public sealed class HudSystem(
|
||||
Myra.Graphics2D.UI.Label label,
|
||||
string titleKey,
|
||||
int seed,
|
||||
int population
|
||||
MrGameEng.Core.Calendar? calendar = null
|
||||
) : BaseSystem
|
||||
{
|
||||
private float _accumulated;
|
||||
@@ -189,7 +189,7 @@ public sealed class HudSystem(
|
||||
_accumulated = 0f;
|
||||
_frames = 0;
|
||||
label.Text =
|
||||
languages.Format(titleKey, seed, population, fps)
|
||||
languages.Format(titleKey, seed, calendar?.Day ?? 0, fps)
|
||||
+ "\n"
|
||||
+ languages.Get("hud.controls");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user