diff --git a/Mods/Core/Defs/plants.json b/Mods/Core/Defs/plants.json
index e520e4b..0791e14 100644
--- a/Mods/Core/Defs/plants.json
+++ b/Mods/Core/Defs/plants.json
@@ -21,8 +21,8 @@
{ "defName": "GrassA", "label": "plant.grass", "texture": "things/plant/grass/grassa", "sizeCells": 1.2,
"harvestProduct": "ProductGrass",
- "genome": { "optimalLight": 0.85, "lightTolerance": 0.35, "optimalTemperature": 22, "temperatureTolerance": 8,
- "coldHardiness": 8, "heatHardiness": 6,
+ "genome": { "optimalLight": 0.85, "lightTolerance": 0.35, "optimalTemperature": 19, "temperatureTolerance": 8,
+ "coldHardiness": 9, "heatHardiness": 6,
"optimalFertility": 1.0, "fertilityTolerance": 1.0, "vigor": 1.3,
"lifespan": 22, "dispersalRange": 3, "reproduceInterval": 3, "selfPollination": 0.8,
"mutationRate": 0.06, "variantChance": 0.2, "spread": 0.1,
diff --git a/Mods/Core/Defs/terrain.json b/Mods/Core/Defs/terrain.json
index a53f681..d86cb5b 100644
--- a/Mods/Core/Defs/terrain.json
+++ b/Mods/Core/Defs/terrain.json
@@ -8,13 +8,14 @@
{ "defName": "Grass", "label": "terrain.grass", "maxHeight": 0.68, "color": [90, 160, 70],
"isLand": true, "surface": "terrain/surfaces/mossy", "fertility": 1.0,
"scatter": [
- { "chance": 0.35, "options": ["GrassA"] },
+ { "chance": 0.6, "options": ["GrassA"] },
{ "chance": 0.07, "options": ["BushA", "BushB", "BushC"] }
] },
{ "defName": "Forest", "label": "terrain.forest", "maxHeight": 0.85, "color": [44, 110, 50],
"isLand": true, "surface": "terrain/surfaces/soil", "fertility": 1.6,
"scatter": [
- { "chance": 0.35, "options": ["TreeOakA", "TreeOakB", "TreeBirchA", "TreeGrayPineA"] }
+ { "chance": 0.35, "options": ["TreeOakA", "TreeOakB", "TreeBirchA", "TreeGrayPineA"] },
+ { "chance": 0.3, "options": ["GrassA"] }
] },
{ "defName": "Mountain", "label": "terrain.mountain", "maxHeight": 1.01, "color": [136, 132, 128],
"surface": "terrain/surfaces/roughhewnrock", "fertility": 0.2, "blocksLight": true }
diff --git a/Mods/Core/Languages/en/ui.json b/Mods/Core/Languages/en/ui.json
index e57fb66..bb0403b 100644
--- a/Mods/Core/Languages/en/ui.json
+++ b/Mods/Core/Languages/en/ui.json
@@ -6,7 +6,29 @@
"season.summer": "summer",
"season.autumn": "autumn",
"season.winter": "winter",
- "hud.controls": "WASD — camera, wheel — zoom, ` — console, F1 — inspector",
+ "hud.controls": "WASD — camera, wheel — zoom, LMB — select, ` — console, F1 — inspector",
+ "inspect.stage": "Stage: {0} ({1}/{2})",
+ "inspect.growing": "Growth: {0:0}% to next stage",
+ "inspect.mature": "Fully grown",
+ "inspect.age": "Age: {0:0.0} d · lives to {1:0}",
+ "inspect.state": "State: {0}",
+ "inspect.state.growing": "growing",
+ "inspect.state.mature": "mature",
+ "inspect.state.dormantcold": "dormant — too cold",
+ "inspect.state.dormanthot": "dormant — too hot",
+ "inspect.stress": "Stress: {0:0.0} d to death",
+ "inspect.temp": "Temperature: grows {0:0}…{1:0}°C, optimal {2:0}…{3:0}°C",
+ "inspect.genes": "— Genes —",
+ "inspect.gene.vigor": "Vigor {0:0.00} · lifespan {1:0} d",
+ "inspect.gene.env": "Opt. light {0:0.00} · opt. soil {1:0.00}",
+ "inspect.gene.repro": "Dispersal {0:0.0} · interval {1:0.0} d",
+ "inspect.gene.repro2": "Self-pollin. {0:0.00} · mutation {1:0.00}",
+ "inspect.gene.hardy": "Cold/heat {0:0}/{1:0} · leaf hue {2:0.00}",
+ "inspect.gene.variant": "Morph: recessive variant",
+ "inspect.harvest": "Harvest: {0} ×{1:0.#}",
+ "inspect.fruit": "Fruit: {0} ×{1:0.#} ({2}) · ripe {3:0.#}",
+ "inspect.barren": "Bears no fruit",
+ "inspect.hint": "LMB — select · RMB/Esc — clear",
"hud.paused": "PAUSED",
"menu.title": "LittleSim",
"menu.subtitle": "a god-game: minimal graphics, deep simulation",
@@ -84,5 +106,6 @@
"net.connecting": "connecting…",
"net.connected": "connected",
"net.failed": "connection failed",
- "net.lost": "connection lost"
+ "net.lost": "connection lost",
+ "net.reconnecting": "reconnecting…"
}
diff --git a/Mods/Core/Languages/ru/ui.json b/Mods/Core/Languages/ru/ui.json
index b7982f4..05e1408 100644
--- a/Mods/Core/Languages/ru/ui.json
+++ b/Mods/Core/Languages/ru/ui.json
@@ -6,7 +6,29 @@
"season.summer": "лето",
"season.autumn": "осень",
"season.winter": "зима",
- "hud.controls": "WASD — камера, колесо — зум, ` — консоль, F1 — инспектор",
+ "hud.controls": "WASD — камера, колесо — зум, ЛКМ — выбрать, ` — консоль, F1 — инспектор",
+ "inspect.stage": "Стадия: {0} ({1}/{2})",
+ "inspect.growing": "Рост: {0:0}% до следующей стадии",
+ "inspect.mature": "Полностью выросло",
+ "inspect.age": "Возраст: {0:0.0} дн · живёт до {1:0}",
+ "inspect.state": "Состояние: {0}",
+ "inspect.state.growing": "растёт",
+ "inspect.state.mature": "созрело",
+ "inspect.state.dormantcold": "покой — слишком холодно",
+ "inspect.state.dormanthot": "покой — слишком жарко",
+ "inspect.stress": "Стресс: {0:0.0} дн до гибели",
+ "inspect.temp": "Температура: рост {0:0}…{1:0}°C, оптимум {2:0}…{3:0}°C",
+ "inspect.genes": "— Гены —",
+ "inspect.gene.vigor": "Бодрость {0:0.00} · жизнь {1:0} дн",
+ "inspect.gene.env": "Опт. свет {0:0.00} · опт. почва {1:0.00}",
+ "inspect.gene.repro": "Расселение {0:0.0} кл · период {1:0.0} дн",
+ "inspect.gene.repro2": "Самоопыление {0:0.00} · мутации {1:0.00}",
+ "inspect.gene.hardy": "Морозо/жаро {0:0}/{1:0} · оттенок {2:0.00}",
+ "inspect.gene.variant": "Морфа: рецессивный вариант",
+ "inspect.harvest": "Сбор: {0} ×{1:0.#}",
+ "inspect.fruit": "Плоды: {0} ×{1:0.#} ({2}) · зрелых {3:0.#}",
+ "inspect.barren": "Не плодоносит",
+ "inspect.hint": "ЛКМ — выбрать · ПКМ/Esc — снять",
"hud.paused": "ПАУЗА",
"menu.title": "LittleSim",
"menu.subtitle": "бог-игра: минимум графики, максимум симуляции",
@@ -84,5 +106,6 @@
"net.connecting": "подключение…",
"net.connected": "подключено",
"net.failed": "не удалось подключиться",
- "net.lost": "соединение потеряно"
+ "net.lost": "соединение потеряно",
+ "net.reconnecting": "переподключение…"
}
diff --git a/scripts/run.ps1 b/scripts/run.ps1
index 19984da..404d248 100644
--- a/scripts/run.ps1
+++ b/scripts/run.ps1
@@ -9,7 +9,9 @@ $Project = Join-Path $Root "src\LittleSim\LittleSim.csproj"
Push-Location $Root
try {
- dotnet build LittleSim.sln -c $Configuration
+ # Только игровой проект (тянет за собой нужные движковые библиотеки) — не вся солюшн с
+ # тестами, сервером и WASM-клиентом (его emscripten-сборка занимает минуты).
+ dotnet build $Project -c $Configuration
if ($LASTEXITCODE -ne 0) {
exit $LASTEXITCODE
}
diff --git a/scripts/run.sh b/scripts/run.sh
index 51b8afd..930af78 100644
--- a/scripts/run.sh
+++ b/scripts/run.sh
@@ -18,5 +18,7 @@ done
cd "$ROOT"
-dotnet build LittleSim.sln -c "$CONFIGURATION"
+# Только игровой проект (тянет за собой нужные движковые библиотеки) — не вся солюшн с
+# тестами, сервером и WASM-клиентом (его emscripten-сборка занимает минуты).
+dotnet build src/LittleSim/LittleSim.csproj -c "$CONFIGURATION"
dotnet run --project src/LittleSim/LittleSim.csproj -c "$CONFIGURATION" --no-build "$@"
diff --git a/src/LittleSim.Web/LittleSimWebGame.cs b/src/LittleSim.Web/LittleSimWebGame.cs
index 6892636..9f420ff 100644
--- a/src/LittleSim.Web/LittleSimWebGame.cs
+++ b/src/LittleSim.Web/LittleSimWebGame.cs
@@ -16,24 +16,33 @@ public class LittleSimWebGame : Game
/// Контекст ядра движка, общий со сценами и системами.
public EngineContext Context { get; } = new EngineContext();
- private readonly Uri _server;
private GraphicsDeviceManager _graphics;
private SpriteBatch _spriteBatch = null!;
private Texture2D _pixel = null!;
+ private WorldViewScene _scene = null!;
- /// Игра, подключающаяся к серверу .
- public LittleSimWebGame(Uri server)
+ /// Игра-наблюдатель: мира не создаёт, ждёт из UI.
+ public LittleSimWebGame()
{
- _server = server;
_graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
}
+ /// Состояние соединения со сценой-наблюдателем — читает оверлей подключения.
+ public ConnectionStatus Status => _scene?.Status ?? ConnectionStatus.Idle;
+
+ /// Число реплицированных жителей — для статуса в оверлее.
+ public int EntityCount => _scene?.EntityCount ?? 0;
+
+ /// Подключиться к серверу (вызывается из Blazor-оверлея).
+ public void Connect(Uri server) => _scene?.Connect(server);
+
///
protected override void Initialize()
{
base.Initialize();
- Context.Scenes.Switch(new WorldViewScene(_server, () => _spriteBatch, () => _pixel));
+ _scene = new WorldViewScene(() => _spriteBatch, () => _pixel);
+ Context.Scenes.Switch(_scene);
}
///
diff --git a/src/LittleSim.Web/Pages/Index.razor b/src/LittleSim.Web/Pages/Index.razor
index 262c9da..adaaa04 100644
--- a/src/LittleSim.Web/Pages/Index.razor
+++ b/src/LittleSim.Web/Pages/Index.razor
@@ -1,20 +1,29 @@
-@page "/"
-@page "/index.html"
-@inject IJSRuntime JsRuntime
-@using nkast.Wasm.Canvas
-
-LittleSim
-
-
-
-
+@page "/"
+@page "/index.html"
+@inject IJSRuntime JsRuntime
+@using nkast.Wasm.Canvas
+
+LittleSim
+
+
+
+
+
+
+ LittleSim
+
+
+ @StatusText
+
diff --git a/src/LittleSim.Web/Pages/Index.razor.cs b/src/LittleSim.Web/Pages/Index.razor.cs
index 072d728..a78a404 100644
--- a/src/LittleSim.Web/Pages/Index.razor.cs
+++ b/src/LittleSim.Web/Pages/Index.razor.cs
@@ -1,7 +1,7 @@
using System;
using Microsoft.AspNetCore.Components;
+using Microsoft.AspNetCore.Components.Web;
using Microsoft.JSInterop;
-using Microsoft.Xna.Framework;
namespace LittleSim.Web.Pages
{
@@ -10,7 +10,12 @@ namespace LittleSim.Web.Pages
[Inject]
private NavigationManager Navigation { get; set; } = null!;
- private Game? _game;
+ private LittleSimWebGame? _game;
+ private string _serverAddress = "";
+ private Uri? _pendingServer; // запрошен Connect до создания игры — подключим в первом тике
+ private ConnectionStatus _lastStatus = ConnectionStatus.Idle;
+ private int _lastCount = -1;
+ private int _frame;
protected override void OnAfterRender(bool firstRender)
{
@@ -18,25 +23,92 @@ namespace LittleSim.Web.Pages
if (firstRender)
{
+ _serverAddress = DefaultServerAddress();
+ StateHasChanged();
JsRuntime.InvokeAsync