feat(ui): Phase 30 ship-class calculator with goal-seek and reach circles
Fuse the standalone ship-class designer (Phases 17/18) into a sidebar calculator: live mass/speed/attack/defence/bombing results, a planet build-rate readout, single-target goal-seek, a modernization-cost mode, and auto reach circles on the map for the selected planet. pkg/calc becomes the single source for the new math (no mirroring): extract BombingPower from the engine model and the per-turn ship-production loop from controller.ProduceShip into pkg/calc (engine now delegates), and add inverse goal-seek solvers in pkg/calc/solve.go. Thin-bridge the combat, planet-build, and solver functions through ui/core/calc + ui/wasm and rebuild core.wasm. Remove the standalone designer view/route; the ship-classes table and the view/bottom menus open the calculator via a shared request store. Docs: rewrite ui/PLAN.md Phase 30, adjust Phase 34 (realistic forecast + CAP/COL ownership), add ui/docs/calculator-ux.md, extend calc-bridge.md, fix navigation.md; remove ui/CALCULATOR.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -337,6 +337,65 @@ const en = {
|
||||
"game.designer.ship_class.preview.cargo_capacity": "cargo capacity per ship",
|
||||
"game.designer.ship_class.preview.unavailable": "—",
|
||||
|
||||
"game.calculator.title": "ship class calculator",
|
||||
"game.calculator.mode.ship": "calculator",
|
||||
"game.calculator.mode.modernization": "modernization",
|
||||
"game.calculator.name.placeholder": "new class name",
|
||||
"game.calculator.name.existing": "your ship classes",
|
||||
"game.calculator.action.create": "create",
|
||||
"game.calculator.action.delete": "delete",
|
||||
"game.calculator.col.ship": "ship",
|
||||
"game.calculator.col.tech": "tech",
|
||||
"game.calculator.field.drive": "drive",
|
||||
"game.calculator.field.armament": "armament",
|
||||
"game.calculator.field.weapons": "weapons",
|
||||
"game.calculator.field.shields": "shields",
|
||||
"game.calculator.field.cargo": "cargo",
|
||||
"game.calculator.load.label": "load",
|
||||
"game.calculator.load.empty": "empty",
|
||||
"game.calculator.load.full": "full",
|
||||
"game.calculator.load.custom": "custom",
|
||||
"game.calculator.col.empty": "empty",
|
||||
"game.calculator.col.loaded": "loaded",
|
||||
"game.calculator.out.mass": "mass",
|
||||
"game.calculator.out.speed": "speed",
|
||||
"game.calculator.out.attack": "attack",
|
||||
"game.calculator.out.defense": "defense",
|
||||
"game.calculator.out.bombing": "bombing",
|
||||
"game.calculator.out.cargo_capacity": "cargo capacity",
|
||||
"game.calculator.planet.title": "planet",
|
||||
"game.calculator.planet.none": "select one of your planets on the map",
|
||||
"game.calculator.planet.label": "planet {name} (#{number})",
|
||||
"game.calculator.planet.mat": "MAT",
|
||||
"game.calculator.planet.ships_per_turn": "ships / turn",
|
||||
"game.calculator.planet.turns_per_ship": "turns / ship",
|
||||
"game.calculator.lock.reset": "locked — click to release to the computed value",
|
||||
"game.calculator.lock.infeasible": "this target cannot be reached with the current design",
|
||||
"game.calculator.lock.max": "release the locked result first — one result at a time",
|
||||
"game.calculator.tech.reset": "overridden — click to reset to your current tech",
|
||||
"game.calculator.mat.reset": "overridden — click to reset to the planet value",
|
||||
"game.calculator.modern.current": "current",
|
||||
"game.calculator.modern.target": "target",
|
||||
"game.calculator.modern.cost": "upgrade cost",
|
||||
"game.calculator.modern.total": "total upgrade cost",
|
||||
"game.calculator.unavailable": "—",
|
||||
"game.calculator.invalid.empty": "name cannot be empty",
|
||||
"game.calculator.invalid.too_long": "name is too long (30 characters max)",
|
||||
"game.calculator.invalid.starts_with_special": "name cannot start with a special character",
|
||||
"game.calculator.invalid.ends_with_special": "name cannot end with a special character",
|
||||
"game.calculator.invalid.consecutive_specials": "too many special characters in a row",
|
||||
"game.calculator.invalid.whitespace": "name cannot contain spaces",
|
||||
"game.calculator.invalid.disallowed_character": "name contains disallowed characters",
|
||||
"game.calculator.invalid.duplicate_name": "a ship class with this name already exists",
|
||||
"game.calculator.invalid.drive_value": "drive must be 0 or ≥ 1",
|
||||
"game.calculator.invalid.armament_value": "armament must be 0 or a positive integer",
|
||||
"game.calculator.invalid.armament_not_integer": "armament must be an integer",
|
||||
"game.calculator.invalid.weapons_value": "weapons must be 0 or ≥ 1",
|
||||
"game.calculator.invalid.shields_value": "shields must be 0 or ≥ 1",
|
||||
"game.calculator.invalid.cargo_value": "cargo must be 0 or ≥ 1",
|
||||
"game.calculator.invalid.armament_weapons_pair": "armament and weapons must be both zero or both nonzero",
|
||||
"game.calculator.invalid.all_zero": "at least one value must be nonzero",
|
||||
|
||||
"game.table.sciences.title": "sciences",
|
||||
"game.table.sciences.column.name": "name",
|
||||
"game.table.sciences.column.drive": "drive %",
|
||||
|
||||
@@ -338,6 +338,65 @@ const ru: Record<keyof typeof en, string> = {
|
||||
"game.designer.ship_class.preview.cargo_capacity": "грузоподъёмность одного корабля",
|
||||
"game.designer.ship_class.preview.unavailable": "—",
|
||||
|
||||
"game.calculator.title": "калькулятор классов кораблей",
|
||||
"game.calculator.mode.ship": "калькулятор",
|
||||
"game.calculator.mode.modernization": "модернизация",
|
||||
"game.calculator.name.placeholder": "имя нового класса",
|
||||
"game.calculator.name.existing": "ваши классы кораблей",
|
||||
"game.calculator.action.create": "создать",
|
||||
"game.calculator.action.delete": "удалить",
|
||||
"game.calculator.col.ship": "корабль",
|
||||
"game.calculator.col.tech": "технологии",
|
||||
"game.calculator.field.drive": "двигатель",
|
||||
"game.calculator.field.armament": "вооружённость",
|
||||
"game.calculator.field.weapons": "оружие",
|
||||
"game.calculator.field.shields": "защита",
|
||||
"game.calculator.field.cargo": "трюм",
|
||||
"game.calculator.load.label": "загрузка",
|
||||
"game.calculator.load.empty": "пусто",
|
||||
"game.calculator.load.full": "полная",
|
||||
"game.calculator.load.custom": "своя",
|
||||
"game.calculator.col.empty": "пустой",
|
||||
"game.calculator.col.loaded": "гружёный",
|
||||
"game.calculator.out.mass": "масса",
|
||||
"game.calculator.out.speed": "скорость",
|
||||
"game.calculator.out.attack": "атака",
|
||||
"game.calculator.out.defense": "защита",
|
||||
"game.calculator.out.bombing": "бомбардировка",
|
||||
"game.calculator.out.cargo_capacity": "грузоподъёмность",
|
||||
"game.calculator.planet.title": "планета",
|
||||
"game.calculator.planet.none": "выберите свою планету на карте",
|
||||
"game.calculator.planet.label": "планета {name} (#{number})",
|
||||
"game.calculator.planet.mat": "MAT",
|
||||
"game.calculator.planet.ships_per_turn": "кораблей / ход",
|
||||
"game.calculator.planet.turns_per_ship": "ходов / корабль",
|
||||
"game.calculator.lock.reset": "зафиксировано — нажмите, чтобы вернуть вычисляемое значение",
|
||||
"game.calculator.lock.infeasible": "эта цель недостижима при текущих параметрах",
|
||||
"game.calculator.lock.max": "сначала снимите фиксацию с другого результата — по одному за раз",
|
||||
"game.calculator.tech.reset": "переопределено — нажмите, чтобы вернуть ваши текущие технологии",
|
||||
"game.calculator.mat.reset": "переопределено — нажмите, чтобы вернуть значение планеты",
|
||||
"game.calculator.modern.current": "текущий",
|
||||
"game.calculator.modern.target": "целевой",
|
||||
"game.calculator.modern.cost": "стоимость апгрейда",
|
||||
"game.calculator.modern.total": "суммарная стоимость апгрейда",
|
||||
"game.calculator.unavailable": "—",
|
||||
"game.calculator.invalid.empty": "имя не может быть пустым",
|
||||
"game.calculator.invalid.too_long": "имя слишком длинное (максимум 30 символов)",
|
||||
"game.calculator.invalid.starts_with_special": "имя не может начинаться со спецсимвола",
|
||||
"game.calculator.invalid.ends_with_special": "имя не может заканчиваться спецсимволом",
|
||||
"game.calculator.invalid.consecutive_specials": "слишком много спецсимволов подряд",
|
||||
"game.calculator.invalid.whitespace": "имя не может содержать пробелы",
|
||||
"game.calculator.invalid.disallowed_character": "имя содержит недопустимые символы",
|
||||
"game.calculator.invalid.duplicate_name": "класс корабля с таким именем уже существует",
|
||||
"game.calculator.invalid.drive_value": "двигатель должен быть 0 или ≥ 1",
|
||||
"game.calculator.invalid.armament_value": "вооружённость должна быть 0 или положительным целым",
|
||||
"game.calculator.invalid.armament_not_integer": "вооружённость должна быть целым числом",
|
||||
"game.calculator.invalid.weapons_value": "оружие должно быть 0 или ≥ 1",
|
||||
"game.calculator.invalid.shields_value": "защита должна быть 0 или ≥ 1",
|
||||
"game.calculator.invalid.cargo_value": "трюм должен быть 0 или ≥ 1",
|
||||
"game.calculator.invalid.armament_weapons_pair": "вооружённость и оружие должны быть оба нулевыми или оба ненулевыми",
|
||||
"game.calculator.invalid.all_zero": "хотя бы одно значение должно быть ненулевым",
|
||||
|
||||
"game.table.sciences.title": "науки",
|
||||
"game.table.sciences.column.name": "название",
|
||||
"game.table.sciences.column.drive": "двигатель %",
|
||||
|
||||
Reference in New Issue
Block a user