Compare commits
29 Commits
4c4e027cf6
...
progressio
| Author | SHA1 | Date | |
|---|---|---|---|
| a93f7c3897 | |||
| f8028a6ca2 | |||
| b4fa3e6dff | |||
| dcc6af123f | |||
| f62b7bb78e | |||
| 27c3a7b65b | |||
| 9ddd622870 | |||
| df9f0d5ccd | |||
| db7a03dc1f | |||
| 67054f432f | |||
| 7a9a7a35ad | |||
| f822047b4e | |||
| d9e7dd0fe8 | |||
| 4d5b218fec | |||
| 97bafc95c3 | |||
| 5aa0544cf2 | |||
| 38fd2e4e89 | |||
| d889b79658 | |||
| b9e70ba83a | |||
| 3aefc05118 | |||
| fe7703d5c3 | |||
| 72b38df0dd | |||
| c7a168f4a9 | |||
| a6451f2bdf | |||
| 493208d230 | |||
| 4098e40c8c | |||
| 730cd3bdf3 | |||
| 437ac97355 | |||
| 41081620f0 |
@@ -56,9 +56,12 @@ production_time_seconds = 3
|
|||||||
fill_color = "#FF8040"
|
fill_color = "#FF8040"
|
||||||
glyph = "Rm"
|
glyph = "Rm"
|
||||||
|
|
||||||
|
# damage 14 keeps a 60 HP drone at 5 hits — 15+ crosses a breakpoint that
|
||||||
|
# silently adds ~25% effective DPS vs drones (docs/balancing/history.md,
|
||||||
|
# round 6).
|
||||||
[module.weapon]
|
[module.weapon]
|
||||||
damage = 14
|
damage = 14
|
||||||
attack_range_m = 70
|
attack_range_m = 80
|
||||||
attack_rate_hz = 1.5
|
attack_rate_hz = 1.5
|
||||||
|
|
||||||
|
|
||||||
@@ -75,9 +78,12 @@ production_time_seconds = 4
|
|||||||
fill_color = "#FF8040"
|
fill_color = "#FF8040"
|
||||||
glyph = "Rl"
|
glyph = "Rl"
|
||||||
|
|
||||||
|
# attack_range_m 130 deliberately exceeds the station range of 120
|
||||||
|
# (stations.toml) — the l gun is the only weapon that can besiege stations
|
||||||
|
# without tanking their fire (docs/balancing/history.md, playtest 1).
|
||||||
[module.weapon]
|
[module.weapon]
|
||||||
damage = 52
|
damage = 52
|
||||||
attack_range_m = 100
|
attack_range_m = 130
|
||||||
attack_rate_hz = 0.8
|
attack_rate_hz = 0.8
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
@@ -110,7 +116,7 @@ glyph = "Rp"
|
|||||||
|
|
||||||
[module.repair]
|
[module.repair]
|
||||||
repair_rate_hz = 1
|
repair_rate_hz = 1
|
||||||
repair_amount_hp = 9
|
repair_amount_hp = 4
|
||||||
repair_range_m = 80
|
repair_range_m = 80
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
# using the verified fitted threat values from tools/threat_report.py:
|
# using the verified fitted threat values from tools/threat_report.py:
|
||||||
# drone 10.5, frigate 47, destroyer 99, cruiser 233.5,
|
# drone 10.5, frigate 47, destroyer 99, cruiser 233.5,
|
||||||
# battlecruiser 354.5, battleship 722.5, dreadnought 1491.5,
|
# battlecruiser 354.5, battleship 722.5, dreadnought 1491.5,
|
||||||
# carrier 1436.5, glass destroyer (8 small guns) 92, repair drone 17.
|
# carrier 1436.5, glass destroyer (8 small guns) 92, repair drone 17,
|
||||||
|
# railgun_s-spam cruiser (12 small guns) 178.
|
||||||
# Module arrays mirror the ships' default_modules loadouts unless a
|
# Module arrays mirror the ships' default_modules loadouts unless a
|
||||||
# doctrine variant is the point of the arena.
|
# doctrine variant is the point of the arena.
|
||||||
#
|
#
|
||||||
@@ -348,6 +349,50 @@ enemy_buffer_width_tiles = 10
|
|||||||
{type = "railgun_s", x = 4, y = 1, rotation = "east"},
|
{type = "railgun_s", x = 4, y = 1, rotation = "east"},
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[arena]]
|
||||||
|
name = "Railgun_s-spam cruisers vs default cruisers (1424 vs 1401)"
|
||||||
|
# Tracks the playtest-1 meta: cruiser hulls filled with 12 small guns
|
||||||
|
# (max DPS/threat, no armor, range 50) against the default m-gun fit.
|
||||||
|
# The concentration tax means the spam side SHOULD win a brawl somewhat;
|
||||||
|
# this arena bounds its margin — a blowout here means the small-gun
|
||||||
|
# premium or the m-gun range edge needs retuning.
|
||||||
|
height_tiles = 10
|
||||||
|
player_buffer_width_tiles = 10
|
||||||
|
contest_zone_width_tiles = 50
|
||||||
|
enemy_buffer_width_tiles = 10
|
||||||
|
|
||||||
|
[[arena.team]]
|
||||||
|
name = "Spam"
|
||||||
|
[[arena.team.ship]]
|
||||||
|
schematic = "cruiser"
|
||||||
|
count = 8
|
||||||
|
modules = [
|
||||||
|
{type = "railgun_s", x = 1, y = 0, rotation = "east"},
|
||||||
|
{type = "railgun_s", x = 2, y = 0, rotation = "east"},
|
||||||
|
{type = "railgun_s", x = 0, y = 1, rotation = "east"},
|
||||||
|
{type = "railgun_s", x = 1, y = 1, rotation = "east"},
|
||||||
|
{type = "railgun_s", x = 2, y = 1, rotation = "east"},
|
||||||
|
{type = "railgun_s", x = 3, y = 1, rotation = "east"},
|
||||||
|
{type = "railgun_s", x = 0, y = 2, rotation = "east"},
|
||||||
|
{type = "railgun_s", x = 1, y = 2, rotation = "east"},
|
||||||
|
{type = "railgun_s", x = 2, y = 2, rotation = "east"},
|
||||||
|
{type = "railgun_s", x = 3, y = 2, rotation = "east"},
|
||||||
|
{type = "railgun_s", x = 1, y = 3, rotation = "east"},
|
||||||
|
{type = "railgun_s", x = 2, y = 3, rotation = "east"},
|
||||||
|
]
|
||||||
|
|
||||||
|
[[arena.team]]
|
||||||
|
name = "Default"
|
||||||
|
[[arena.team.ship]]
|
||||||
|
schematic = "cruiser"
|
||||||
|
count = 6
|
||||||
|
modules = [
|
||||||
|
{type = "railgun_m", x = 0, y = 1, rotation = "east"},
|
||||||
|
{type = "railgun_m", x = 2, y = 1, rotation = "east"},
|
||||||
|
{type = "armor_plates", x = 1, y = 0, rotation = "east"},
|
||||||
|
{type = "maneuvering_thrusters", x = 1, y = 3, rotation = "east"},
|
||||||
|
]
|
||||||
|
|
||||||
[[arena]]
|
[[arena]]
|
||||||
name = "Repair escort vs raw numbers (444 vs 444)"
|
name = "Repair escort vs raw numbers (444 vs 444)"
|
||||||
height_tiles = 10
|
height_tiles = 10
|
||||||
|
|||||||
@@ -21,8 +21,12 @@ REQ-* ids in [../requirements.md](../requirements.md).
|
|||||||
|
|
||||||
First full balancing round complete (2026-07-06): targets → tree →
|
First full balancing round complete (2026-07-06): targets → tree →
|
||||||
numbers → threat-calculator parity → combat stats (arena-converged) →
|
numbers → threat-calculator parity → combat stats (arena-converged) →
|
||||||
pacing. Next step: full-game playtests against the run-shape targets in
|
pacing. Playtesting in progress: playtest 1 (two full ~40-min wins,
|
||||||
`targets.md`.
|
cruisers only) found the railgun_s-spam-cruiser meta, confirmed repair
|
||||||
|
as overpowered, and exposed a 2.5–3× run-length gap; combat stats
|
||||||
|
adjusted and re-checked in arena round 6 (see `history.md`). Next:
|
||||||
|
playtest 2 with the new stats — pacing knobs (station scaling, threat
|
||||||
|
rate, win pacing) wait for its result.
|
||||||
|
|
||||||
## Open action items
|
## Open action items
|
||||||
|
|
||||||
|
|||||||
@@ -94,10 +94,16 @@ geometry-validated against the hull grids)
|
|||||||
|
|
||||||
## Combat stats
|
## Combat stats
|
||||||
|
|
||||||
(arena-converged, 2026-07; see `history.md` rounds 1–5)
|
(arena-converged 2026-07, rounds 1–5; playtest-1 adjustments 2026-07-06 —
|
||||||
|
see `history.md`)
|
||||||
|
|
||||||
**Weapons:** railgun_s 2 dmg × 2.0 Hz (4.0 DPS), range 50 m;
|
**Weapons:** railgun_s 2 dmg × 2.0 Hz (4.0 DPS), range 50 m;
|
||||||
railgun_m 14 × 1.5 (21), range 70; railgun_l 52 × 0.8 (41.6), range 100.
|
railgun_m 14 × 1.5 (21), range 80; railgun_l 52 × 0.8 (41.6), range 130.
|
||||||
|
DPS per threat: s 0.62, m 0.48, l 0.41 — the concentration tax stands;
|
||||||
|
reach is the bigger guns' compensation (ranges raised after playtest 1,
|
||||||
|
which alone priced out the small-gun-spam meta). railgun_l deliberately
|
||||||
|
outranges stations (120 m) to buy the siege role. railgun_m damage is
|
||||||
|
breakpoint-sensitive: 15+ drops a 60 HP drone from 5 hits to 4.
|
||||||
|
|
||||||
**Hull HP** (15/threat prior + empirical trims): drone 60, frigate 300,
|
**Hull HP** (15/threat prior + empirical trims): drone 60, frigate 300,
|
||||||
destroyer 550, cruiser 1500, battlecruiser 2400, battleship 6300,
|
destroyer 550, cruiser 1500, battlecruiser 2400, battleship 6300,
|
||||||
@@ -109,8 +115,9 @@ destroyer 30|35|18|6|3, cruiser 24|25|12|4|2, battlecruiser 20|20|10|3|1.5,
|
|||||||
battleship 15|14|7|2|1, dreadnought/carrier 10|8|4|1|0.5.
|
battleship 15|14|7|2|1, dreadnought/carrier 10|8|4|1|0.5.
|
||||||
Sensors: 150/200/220/250/260/280/300/350 m.
|
Sensors: 150/200/220/250/260/280/300/350 m.
|
||||||
|
|
||||||
**Other modules:** armor_plates +1200 HP; repair_tool 9 HP × 1 Hz,
|
**Other modules:** armor_plates +1200 HP; repair_tool 4 HP × 1 Hz,
|
||||||
range 80; salvager range 60, cargo 20, 0.5 collections/s; afterburner
|
range 80 (halved after playtest 1 — free between-wave top-offs were never
|
||||||
|
priced by the arena escort test); salvager range 60, cargo 20, 0.5 collections/s; afterburner
|
||||||
×1.6 speed +60 accel; maneuvering_thrusters ×1.2 speed +10 maneuvering;
|
×1.6 speed +60 accel; maneuvering_thrusters ×1.2 speed +10 maneuvering;
|
||||||
sensor_booster +50 m; weapon_upgrade ×1.2 damage; weapon_primer ×1.2
|
sensor_booster +50 m; weapon_upgrade ×1.2 damage; weapon_primer ×1.2
|
||||||
rate; weapon_stabilizer ×1.3 range ×0.8 rate.
|
rate; weapon_stabilizer ×1.3 range ×0.8 rate.
|
||||||
|
|||||||
@@ -94,3 +94,68 @@ range 200).
|
|||||||
— ~1 expansion per cycle mid-game, 2–3 cycles apart late.
|
— ~1 expansion per cycle mid-game, 2–3 cycles apart late.
|
||||||
- **First full balancing round complete.** Next: full-game playtests
|
- **First full balancing round complete.** Next: full-game playtests
|
||||||
against the run-shape targets.
|
against the run-shape targets.
|
||||||
|
|
||||||
|
## 2026-07-06 — playtest 1 (two full playthroughs)
|
||||||
|
|
||||||
|
Two complete runs, WON in ~40 min each with cruiser fleets only — never
|
||||||
|
needing capitals. (Initially misread as "two pushes in 40 min, pacing on
|
||||||
|
target"; corrected in round 6.) That is roughly cycle 8 against the
|
||||||
|
win-cycle target of 20–24: a 2.5–3× pacing gap. Pacing knobs deliberately
|
||||||
|
untouched this round — the runs rode 9 HP/s repair and stations nothing
|
||||||
|
outranged, both nerfed below; playtest 2 measures the remaining gap.
|
||||||
|
|
||||||
|
- **Meta finding: cruisers filled with 12× railgun_s dominate.** Predicted
|
||||||
|
by the numbers in hindsight: the concentration tax makes railgun_s the
|
||||||
|
best DPS/threat (0.62 vs m 0.48, l 0.41), range is the big guns' only
|
||||||
|
mechanical edge (armor is added HP, not damage reduction — no anti-swarm
|
||||||
|
mechanic), repair sustain covers the closing distance, and stations
|
||||||
|
outranged every ship gun (120 vs railgun_l's 100), so even capitals had
|
||||||
|
to tank-and-brawl. The cruiser compounds it: first hull with a large
|
||||||
|
1×1 canvas (12 cells) and a nearly quartz-free chain.
|
||||||
|
- **Repair tool confirmed overpowered** (second signal after the
|
||||||
|
persistent +24% arena escort margin): the arena only prices in-fight
|
||||||
|
sustain; real runs add free full top-offs in every 15–45 s wave gap
|
||||||
|
across the whole swarm. The 0.7 HP/s-per-threat prior is wrong for
|
||||||
|
wave defence.
|
||||||
|
- **Changes:** repair_tool 9→4 HP/s; railgun_l range 100→130 (now
|
||||||
|
outranges stations — buys the siege role the capital ladder promises);
|
||||||
|
railgun_m 14→16 dmg and range 70→80 (tax softened: m sits at 0.55
|
||||||
|
DPS/threat, between s and l). Module threats unchanged (costs
|
||||||
|
untouched), so no ladder recalculation needed.
|
||||||
|
- New tracked arena added: railgun_s-spam cruisers (8× 178) vs default
|
||||||
|
cruisers (6× 233.5) — the spam side should win a brawl somewhat, but a
|
||||||
|
blowout means the small-gun premium needs retuning.
|
||||||
|
- **Open:** re-run the arena suite to check the range/damage changes
|
||||||
|
against the round 1–5 results; next playtest should verify big guns now
|
||||||
|
feel worth climbing to and repair is merely good.
|
||||||
|
|
||||||
|
## 2026-07-06 — arena round 6 (checking the playtest-1 adjustments)
|
||||||
|
|
||||||
|
Mirrors healthy (5–8% margins, durations 24/66/91 s). Results:
|
||||||
|
|
||||||
|
- **Spam-cruiser arena: default cruisers +9% — the meta is priced out**,
|
||||||
|
and the range buff alone did the work.
|
||||||
|
- **Regression: drone swarm vs cruisers +47% for cruisers** (was +14%
|
||||||
|
swarm in round 3). Besides the wider range gap, the damage buff crossed
|
||||||
|
a breakpoint: 14 dmg kills a 60 HP drone in 5 hits, 16 in 4 — a hidden
|
||||||
|
~25% effective-DPS gain vs drones. Change: **railgun_m damage 16→14**
|
||||||
|
(range stays 80); the tax stands, reach is the compensation.
|
||||||
|
- Battleship +30% and dreadnought +29% vs pure railgun_s fleets:
|
||||||
|
**accepted as reach-doctrine texture** (BS was already accepted at
|
||||||
|
+23%) — the l gun's 130 m standoff is exactly what the range buff
|
||||||
|
bought; the counter is your own reach or 2:1 numbers, not equal-threat
|
||||||
|
small guns. Watch, don't tune.
|
||||||
|
- Repair escort flipped to raw +16%: **kept at 4 HP/s deliberately** —
|
||||||
|
the arena cannot price the free between-wave top-offs, so slightly
|
||||||
|
below par in-fight is the correct price for a module whose run-value
|
||||||
|
includes them. Playtest 2 decides; 6 is the fallback if repair feels
|
||||||
|
dead.
|
||||||
|
- Station assault: the 3× swarm cracked the fortified position keeping
|
||||||
|
45% EHP. No knob this round touched it; together with playtest 1's
|
||||||
|
trivially easy pushes it flags **station strength as the first pacing
|
||||||
|
lever** for the next pass.
|
||||||
|
|
||||||
|
**Pacing deferred:** playtest 1's 40-min wins predate the repair nerf
|
||||||
|
and the l-gun siege range. If playtest 2 still wins by ~cycle 10, the
|
||||||
|
levers are enemy station scaling (`3000 + 1500*x` likely too shallow),
|
||||||
|
the threat rate, and possibly `artifact_win_count`.
|
||||||
|
|||||||
@@ -109,8 +109,6 @@ Modules in `modules.toml` define a `surface_mask` — a list of strings that des
|
|||||||
- REQ-BLD-BELT-DRAG: For belts, the player can click and drag across multiple tiles to place a construction site on each tile in one gesture.
|
- REQ-BLD-BELT-DRAG: For belts, the player can click and drag across multiple tiles to place a construction site on each tile in one gesture.
|
||||||
- REQ-BLD-TUNNEL-AUTO-SWITCH: After the player successfully places a Tunnel Entry construction site, builder mode automatically switches to Tunnel Exit (and vice versa), preserving the current ghost rotation. This makes it easy to immediately place the paired end without manually selecting the complementary type.
|
- REQ-BLD-TUNNEL-AUTO-SWITCH: After the player successfully places a Tunnel Entry construction site, builder mode automatically switches to Tunnel Exit (and vice versa), preserving the current ghost rotation. This makes it easy to immediately place the paired end without manually selecting the complementary type.
|
||||||
- REQ-BLD-DEMOLISH: The player can demolish a placed factory building. Demolition returns `world.toml [world].refund_percentage` percent of the original building block cost (default 75%) to the global stock. Exception: if the building is still in the construction queue (not yet fully built, including the one currently being constructed), it is removed from the queue and the **full** building block cost is refunded. The HQ and player defence stations cannot be demolished.
|
- REQ-BLD-DEMOLISH: The player can demolish a placed factory building. Demolition returns `world.toml [world].refund_percentage` percent of the original building block cost (default 75%) to the global stock. Exception: if the building is still in the construction queue (not yet fully built, including the one currently being constructed), it is removed from the queue and the **full** building block cost is refunded. The HQ and player defence stations cannot be demolished.
|
||||||
- REQ-BLD-DEMOLISH-CLICK: While in demolish mode (REQ-UI-HOTKEYS, REQ-UI-DEMOLISH-BUTTON), left-clicking a placed factory building or construction site in the game world demolishes it, following the refund rules of REQ-BLD-DEMOLISH — the partial refund for built buildings and the full refund for still-queued construction sites. Clicking a building that cannot be demolished (the HQ or a player defence station, per REQ-BLD-DEMOLISH), or clicking empty world space, has no effect. Demolish mode stays active after a demolition so the player can demolish further buildings without re-entering the mode; it is exited via the Q toggle (REQ-UI-HOTKEYS) or the Demolish button (REQ-UI-DEMOLISH-BUTTON).
|
|
||||||
- REQ-BLD-DEMOLISH-BOX: While in demolish mode (REQ-UI-HOTKEYS, REQ-UI-DEMOLISH-BUTTON), the player can click and drag a selection box in the game world. A selection rectangle is drawn while dragging, using the same box-drag gesture and coverage semantics as the multi-select box (REQ-UI-MULTI-SELECT). On mouse up, every placed factory building and construction site covered by the box is demolished, each following the refund rules of REQ-BLD-DEMOLISH — the partial refund for built buildings and the full refund for still-queued construction sites. Buildings that cannot be demolished (the HQ and player defence stations, per REQ-BLD-DEMOLISH) are excluded from the box demolition; ships and defence stations are never affected.
|
|
||||||
- REQ-BLD-SITE-CONFIG: A construction site — a building that has been placed but is still queued or under construction (REQ-BLD-QUEUE) — can be selected and configured exactly like the equivalent operational building, before it finishes building. Whatever configuration the building type supports is available on the site: the recipe for a Miner or Assembler (REQ-UI-SELECT-BUTTON), the produced-ship schematic and its module layout for a Shipyard (REQ-UI-SELECT-BUTTON, REQ-MOD-UI-PREVIEW, REQ-MOD-UI-DIALOG), and the output filters for a Splitter (REQ-BLD-SPLITTER) — all set through the same Selected Building Panel controls (REQ-UI-CONFIG-INLINE). Only currently unlocked recipes and schematics are offered, exactly as for operational buildings (REQ-LOCK-UI-RECIPE, REQ-LOCK-UI-SCHEMATIC, REQ-LOCK-UI-SPLITTER). The configuration is stored on the construction site and carries over unchanged when construction completes, so the building becomes operational already configured. A construction site has no input/output buffers and runs no production cycle, so the buffer and production-progress portions of the panel (REQ-UI-SINGLE-SELECTION, REQ-UI-PRODUCTION-PROGRESS) are not shown for it; only its construction progress (REQ-UI-CONSTRUCTION-PROGRESS) and its configuration controls appear. (Blueprint placement already applies a stored recipe or schematic to a construction site on placement per REQ-UI-BLUEPRINT-PLACE; this requirement additionally lets the player set or change that configuration directly on an existing site.)
|
- REQ-BLD-SITE-CONFIG: A construction site — a building that has been placed but is still queued or under construction (REQ-BLD-QUEUE) — can be selected and configured exactly like the equivalent operational building, before it finishes building. Whatever configuration the building type supports is available on the site: the recipe for a Miner or Assembler (REQ-UI-SELECT-BUTTON), the produced-ship schematic and its module layout for a Shipyard (REQ-UI-SELECT-BUTTON, REQ-MOD-UI-PREVIEW, REQ-MOD-UI-DIALOG), and the output filters for a Splitter (REQ-BLD-SPLITTER) — all set through the same Selected Building Panel controls (REQ-UI-CONFIG-INLINE). Only currently unlocked recipes and schematics are offered, exactly as for operational buildings (REQ-LOCK-UI-RECIPE, REQ-LOCK-UI-SCHEMATIC, REQ-LOCK-UI-SPLITTER). The configuration is stored on the construction site and carries over unchanged when construction completes, so the building becomes operational already configured. A construction site has no input/output buffers and runs no production cycle, so the buffer and production-progress portions of the panel (REQ-UI-SINGLE-SELECTION, REQ-UI-PRODUCTION-PROGRESS) are not shown for it; only its construction progress (REQ-UI-CONSTRUCTION-PROGRESS) and its configuration controls appear. (Blueprint placement already applies a stored recipe or schematic to a construction site on placement per REQ-UI-BLUEPRINT-PLACE; this requirement additionally lets the player set or change that configuration directly on an existing site.)
|
||||||
|
|
||||||
## Building Types
|
## Building Types
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ SET(HDRS
|
|||||||
${CMAKE_CURRENT_SOURCE_DIR}/BeamFiredEvent.h
|
${CMAKE_CURRENT_SOURCE_DIR}/BeamFiredEvent.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/DebugDrawToggledEvent.h
|
${CMAKE_CURRENT_SOURCE_DIR}/DebugDrawToggledEvent.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/CommandRequestedEvent.h
|
${CMAKE_CURRENT_SOURCE_DIR}/CommandRequestedEvent.h
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/PlayerCommandsAppliedEvent.h
|
|
||||||
PARENT_SCOPE
|
PARENT_SCOPE
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "Event.h"
|
|
||||||
|
|
||||||
// Emitted by GameWorldView once per frame after queued player commands have been
|
|
||||||
// drained and applied to the simulation. It lets presentation widgets refresh
|
|
||||||
// even while the game is paused (no tick advances, so no TickAdvancedEvent), for
|
|
||||||
// example so a shipyard's layout preview appears immediately after its schematic
|
|
||||||
// is chosen. It is a UI notification only and never feeds back into the command
|
|
||||||
// queue, so it has no effect on replay recording or determinism.
|
|
||||||
class PlayerCommandsAppliedEvent : public Event
|
|
||||||
{
|
|
||||||
};
|
|
||||||
@@ -62,7 +62,6 @@
|
|||||||
#include "ExpansionCostChangedEvent.h"
|
#include "ExpansionCostChangedEvent.h"
|
||||||
#include "GameSpeedChangedEvent.h"
|
#include "GameSpeedChangedEvent.h"
|
||||||
#include "SchematicChoicesAvailableEvent.h"
|
#include "SchematicChoicesAvailableEvent.h"
|
||||||
#include "PlayerCommandsAppliedEvent.h"
|
|
||||||
#include "TickAdvancedEvent.h"
|
#include "TickAdvancedEvent.h"
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
@@ -213,7 +212,6 @@ void GameWorldView::onFrame()
|
|||||||
// Drain queued player commands once per frame, before the tick batch. This
|
// Drain queued player commands once per frame, before the tick batch. This
|
||||||
// runs even at 0x so a paused player sees placed construction sites
|
// runs even at 0x so a paused player sees placed construction sites
|
||||||
// immediately, while staying deterministic (see docs/replay_design.md).
|
// immediately, while staying deterministic (see docs/replay_design.md).
|
||||||
const bool commandsApplied = m_commandManager.hasPending();
|
|
||||||
m_commandManager.drain();
|
m_commandManager.drain();
|
||||||
|
|
||||||
// A drained Reset reinitialized the simulation; reset the view to match.
|
// A drained Reset reinitialized the simulation; reset the view to match.
|
||||||
@@ -223,17 +221,6 @@ void GameWorldView::onFrame()
|
|||||||
resetForNewGame();
|
resetForNewGame();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify presentation widgets that queued commands were applied, so a
|
|
||||||
// paused player still sees the effect (e.g. a shipyard's layout preview
|
|
||||||
// after picking a schematic) even though no tick advances. UI-only: this
|
|
||||||
// does not touch the command queue or simulation, so replay recording
|
|
||||||
// and determinism are unaffected.
|
|
||||||
if (commandsApplied)
|
|
||||||
{
|
|
||||||
EventManager::getInstance()->sendEventImmediately(
|
|
||||||
std::make_shared<PlayerCommandsAppliedEvent>());
|
|
||||||
}
|
|
||||||
|
|
||||||
const int ticks = m_tickDriver.advance(
|
const int ticks = m_tickDriver.advance(
|
||||||
static_cast<double>(elapsed), m_gameSpeedMultiplier);
|
static_cast<double>(elapsed), m_gameSpeedMultiplier);
|
||||||
for (int i = 0; i < ticks; ++i)
|
for (int i = 0; i < ticks; ++i)
|
||||||
@@ -570,41 +557,6 @@ BuildingId GameWorldView::siteAtTile(QPoint tile) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::vector<BuildingId> GameWorldView::buildingsInBox(QPoint cornerA, QPoint cornerB) const
|
|
||||||
{
|
|
||||||
const int x0 = std::min(cornerA.x(), cornerB.x());
|
|
||||||
const int y0 = std::min(cornerA.y(), cornerB.y());
|
|
||||||
const int x1 = std::max(cornerA.x(), cornerB.x());
|
|
||||||
const int y1 = std::max(cornerA.y(), cornerB.y());
|
|
||||||
|
|
||||||
std::vector<BuildingId> ids;
|
|
||||||
for (const Building& b : m_sim->buildings().allBuildings())
|
|
||||||
{
|
|
||||||
for (const QPoint& cell : b.bodyCells)
|
|
||||||
{
|
|
||||||
if (cell.x() >= x0 && cell.x() <= x1
|
|
||||||
&& cell.y() >= y0 && cell.y() <= y1)
|
|
||||||
{
|
|
||||||
ids.push_back(b.id);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const ConstructionSite& s : m_sim->buildings().allSites())
|
|
||||||
{
|
|
||||||
for (const QPoint& cell : s.bodyCells)
|
|
||||||
{
|
|
||||||
if (cell.x() >= x0 && cell.x() <= x1
|
|
||||||
&& cell.y() >= y0 && cell.y() <= y1)
|
|
||||||
{
|
|
||||||
ids.push_back(s.id);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ids;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::optional<QVector2D> GameWorldView::entityPosition(entt::entity entity) const
|
std::optional<QVector2D> GameWorldView::entityPosition(entt::entity entity) const
|
||||||
{
|
{
|
||||||
if (!m_sim->admin().isValid(entity) || !m_sim->admin().hasAll<PositionComponent>(entity))
|
if (!m_sim->admin().isValid(entity) || !m_sim->admin().hasAll<PositionComponent>(entity))
|
||||||
@@ -1271,28 +1223,8 @@ void GameWorldView::drawOverlays(QPainter& painter)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Demolish tint: while dragging a demolish box, tint every covered
|
// Demolish hover tint
|
||||||
// building/site (REQ-BLD-DEMOLISH-BOX); otherwise tint the hovered one.
|
if (m_demolishMode && m_demolishHoverBuildingId != kInvalidBuildingId)
|
||||||
if (m_demolishMode && m_boxSelecting)
|
|
||||||
{
|
|
||||||
for (BuildingId id : buildingsInBox(m_boxStartTile, m_boxCurrentTile))
|
|
||||||
{
|
|
||||||
const Building* b = m_sim->buildings().findBuilding(id);
|
|
||||||
if (b && b->type == BuildingType::Hq) { continue; }
|
|
||||||
const std::vector<QPoint>* cells = nullptr;
|
|
||||||
const ConstructionSite* s = nullptr;
|
|
||||||
if (b) { cells = &b->bodyCells; }
|
|
||||||
else if ((s = m_sim->buildings().findSite(id))) { cells = &s->bodyCells; }
|
|
||||||
if (cells)
|
|
||||||
{
|
|
||||||
for (const QPoint& cell : *cells)
|
|
||||||
{
|
|
||||||
painter.fillRect(tileRect(cell), m_visuals->overlays.demolishTint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (m_demolishMode && m_demolishHoverBuildingId != kInvalidBuildingId)
|
|
||||||
{
|
{
|
||||||
const Building* b = m_sim->buildings().findBuilding(m_demolishHoverBuildingId);
|
const Building* b = m_sim->buildings().findBuilding(m_demolishHoverBuildingId);
|
||||||
if (b)
|
if (b)
|
||||||
@@ -1586,11 +1518,24 @@ void GameWorldView::mousePressEvent(QMouseEvent* event)
|
|||||||
}
|
}
|
||||||
else if (m_demolishMode)
|
else if (m_demolishMode)
|
||||||
{
|
{
|
||||||
// Start a demolish box drag; a plain click resolves as a 1x1 box on
|
BuildingId hovered = buildingAtTile(tile);
|
||||||
// release (REQ-BLD-DEMOLISH-CLICK, REQ-BLD-DEMOLISH-BOX).
|
if (hovered == kInvalidBuildingId)
|
||||||
m_boxSelecting = true;
|
{
|
||||||
m_boxStartTile = tile;
|
hovered = siteAtTile(tile);
|
||||||
m_boxCurrentTile = tile;
|
}
|
||||||
|
if (hovered != kInvalidBuildingId)
|
||||||
|
{
|
||||||
|
const Building* b = m_sim->buildings().findBuilding(hovered);
|
||||||
|
const bool isProtected = b && b->type == BuildingType::Hq;
|
||||||
|
if (!isProtected)
|
||||||
|
{
|
||||||
|
std::shared_ptr<DemolishCommand> command =
|
||||||
|
std::make_shared<DemolishCommand>();
|
||||||
|
command->id = hovered;
|
||||||
|
enqueueCommand(command);
|
||||||
|
m_demolishHoverBuildingId = kInvalidBuildingId;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1678,7 +1623,6 @@ void GameWorldView::mouseMoveEvent(QMouseEvent* event)
|
|||||||
else if (m_demolishMode)
|
else if (m_demolishMode)
|
||||||
{
|
{
|
||||||
m_demolishHoverBuildingId = buildingAtTile(tile);
|
m_demolishHoverBuildingId = buildingAtTile(tile);
|
||||||
if (m_boxSelecting) { m_boxCurrentTile = tile; }
|
|
||||||
}
|
}
|
||||||
else if (m_boxSelecting)
|
else if (m_boxSelecting)
|
||||||
{
|
{
|
||||||
@@ -1700,33 +1644,44 @@ void GameWorldView::mouseReleaseEvent(QMouseEvent* event)
|
|||||||
{
|
{
|
||||||
m_boxSelecting = false;
|
m_boxSelecting = false;
|
||||||
|
|
||||||
const std::vector<BuildingId> boxIds =
|
const int x0 = std::min(m_boxStartTile.x(), m_boxCurrentTile.x());
|
||||||
buildingsInBox(m_boxStartTile, m_boxCurrentTile);
|
const int y0 = std::min(m_boxStartTile.y(), m_boxCurrentTile.y());
|
||||||
|
const int x1 = std::max(m_boxStartTile.x(), m_boxCurrentTile.x());
|
||||||
|
const int y1 = std::max(m_boxStartTile.y(), m_boxCurrentTile.y());
|
||||||
|
|
||||||
if (m_demolishMode)
|
std::vector<BuildingId> boxSel;
|
||||||
|
for (const Building& b : m_sim->buildings().allBuildings())
|
||||||
{
|
{
|
||||||
// Demolish every covered building/site; the HQ is protected
|
for (const QPoint& cell : b.bodyCells)
|
||||||
// (REQ-BLD-DEMOLISH, REQ-BLD-DEMOLISH-BOX).
|
|
||||||
for (BuildingId id : boxIds)
|
|
||||||
{
|
{
|
||||||
const Building* b = m_sim->buildings().findBuilding(id);
|
if (cell.x() >= x0 && cell.x() <= x1
|
||||||
if (b && b->type == BuildingType::Hq) { continue; }
|
&& cell.y() >= y0 && cell.y() <= y1)
|
||||||
std::shared_ptr<DemolishCommand> command =
|
{
|
||||||
std::make_shared<DemolishCommand>();
|
boxSel.push_back(b.id);
|
||||||
command->id = id;
|
break;
|
||||||
enqueueCommand(command);
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const ConstructionSite& s : m_sim->buildings().allSites())
|
||||||
|
{
|
||||||
|
for (const QPoint& cell : s.bodyCells)
|
||||||
|
{
|
||||||
|
if (cell.x() >= x0 && cell.x() <= x1
|
||||||
|
&& cell.y() >= y0 && cell.y() <= y1)
|
||||||
|
{
|
||||||
|
boxSel.push_back(s.id);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
m_demolishHoverBuildingId = kInvalidBuildingId;
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!(event->modifiers() & Qt::ControlModifier))
|
if (!(event->modifiers() & Qt::ControlModifier))
|
||||||
{
|
{
|
||||||
m_selectedBuildingIds = boxIds;
|
m_selectedBuildingIds = boxSel;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
for (BuildingId id : boxIds)
|
for (BuildingId id : boxSel)
|
||||||
{
|
{
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for (BuildingId sel : m_selectedBuildingIds)
|
for (BuildingId sel : m_selectedBuildingIds)
|
||||||
|
|||||||
@@ -143,9 +143,6 @@ private:
|
|||||||
const BuildingDef* findBuildingDef(BuildingType type) const;
|
const BuildingDef* findBuildingDef(BuildingType type) const;
|
||||||
BuildingId buildingAtTile(QPoint tile) const;
|
BuildingId buildingAtTile(QPoint tile) const;
|
||||||
BuildingId siteAtTile(QPoint tile) const;
|
BuildingId siteAtTile(QPoint tile) const;
|
||||||
// Ids of all buildings and construction sites whose footprint intersects
|
|
||||||
// the tile box spanned by the two (unordered) corner tiles.
|
|
||||||
std::vector<BuildingId> buildingsInBox(QPoint cornerA, QPoint cornerB) const;
|
|
||||||
QVector2D widgetToWorld(QPoint widgetPt) const;
|
QVector2D widgetToWorld(QPoint widgetPt) const;
|
||||||
|
|
||||||
void drawPortGlyph(QPainter& painter, QPoint bodyTile,
|
void drawPortGlyph(QPainter& painter, QPoint bodyTile,
|
||||||
|
|||||||
@@ -33,7 +33,6 @@
|
|||||||
#include "ItemType.h"
|
#include "ItemType.h"
|
||||||
#include "LayoutDialogRequestedEvent.h"
|
#include "LayoutDialogRequestedEvent.h"
|
||||||
#include "ModulesConfig.h"
|
#include "ModulesConfig.h"
|
||||||
#include "PlayerCommandsAppliedEvent.h"
|
|
||||||
#include "RecipeSelectionDialog.h"
|
#include "RecipeSelectionDialog.h"
|
||||||
#include "RecipeSelectionRequestedEvent.h"
|
#include "RecipeSelectionRequestedEvent.h"
|
||||||
#include "Rotation.h"
|
#include "Rotation.h"
|
||||||
@@ -295,12 +294,38 @@ void SelectedBuildingPanel::buildSingle(BuildingId id)
|
|||||||
}
|
}
|
||||||
m_recipeSelectButton->show();
|
m_recipeSelectButton->show();
|
||||||
|
|
||||||
updateShipyardLayoutWidgets(type, recipeId, shipLayout);
|
if (type == BuildingType::Shipyard && !recipeId.empty())
|
||||||
|
{
|
||||||
|
const ShipDef* sDef = findShipDef(recipeId);
|
||||||
|
if (sDef && !sDef->layout.empty())
|
||||||
|
{
|
||||||
|
ShipLayoutConfig layout;
|
||||||
|
if (shipLayout.has_value())
|
||||||
|
{
|
||||||
|
layout = *shipLayout;
|
||||||
|
}
|
||||||
|
m_layoutPreview->setShipAndLayout(
|
||||||
|
sDef->layout, layout, &m_config->modules.modules);
|
||||||
|
m_layoutPreview->show();
|
||||||
|
m_configureLayoutBtn->show();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_layoutPreview->hide();
|
||||||
|
m_configureLayoutBtn->hide();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
m_layoutPreview->hide();
|
||||||
|
m_configureLayoutBtn->hide();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
m_recipeSelectButton->hide();
|
m_recipeSelectButton->hide();
|
||||||
updateShipyardLayoutWidgets(type, recipeId, shipLayout);
|
m_layoutPreview->hide();
|
||||||
|
m_configureLayoutBtn->hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Belt "Clear" removes items from a live belt tile; a construction site has
|
// Belt "Clear" removes items from a live belt tile; a construction site has
|
||||||
@@ -338,16 +363,6 @@ void SelectedBuildingPanel::buildSingle(BuildingId id)
|
|||||||
|
|
||||||
if (m_singleIsSite)
|
if (m_singleIsSite)
|
||||||
{
|
{
|
||||||
refreshSiteProgress(s);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
refreshBuffers(b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SelectedBuildingPanel::refreshSiteProgress(const ConstructionSite* s)
|
|
||||||
{
|
|
||||||
QString progress;
|
QString progress;
|
||||||
if (s->completesAt == 0)
|
if (s->completesAt == 0)
|
||||||
{
|
{
|
||||||
@@ -374,6 +389,11 @@ void SelectedBuildingPanel::refreshSiteProgress(const ConstructionSite* s)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
m_buffersLabel->setText(progress);
|
m_buffersLabel->setText(progress);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
refreshBuffers(b);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SelectedBuildingPanel::refreshBuffers(const Building* b)
|
void SelectedBuildingPanel::refreshBuffers(const Building* b)
|
||||||
@@ -510,38 +530,15 @@ void SelectedBuildingPanel::refreshBuffers(const Building* b)
|
|||||||
|
|
||||||
m_buffersLabel->setText(bufText);
|
m_buffersLabel->setText(bufText);
|
||||||
|
|
||||||
// The recipe/schematic is applied via a queued command that only drains on a
|
if (b->type == BuildingType::Shipyard && shipDef && !shipDef->layout.empty())
|
||||||
// later frame, so the per-tick refresh must own the shipyard preview and the
|
|
||||||
// Configure Layout button's visibility; otherwise they stay hidden until the
|
|
||||||
// building is re-selected (which re-runs buildSingle).
|
|
||||||
updateShipyardLayoutWidgets(b->type, b->recipeId, b->shipLayout);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SelectedBuildingPanel::updateShipyardLayoutWidgets(
|
|
||||||
BuildingType type,
|
|
||||||
const std::string& recipeId,
|
|
||||||
const std::optional<ShipLayoutConfig>& shipLayout)
|
|
||||||
{
|
|
||||||
const ShipDef* shipDef = (type == BuildingType::Shipyard)
|
|
||||||
? findShipDef(recipeId)
|
|
||||||
: nullptr;
|
|
||||||
|
|
||||||
if (shipDef && !shipDef->layout.empty())
|
|
||||||
{
|
{
|
||||||
ShipLayoutConfig layout;
|
ShipLayoutConfig layout;
|
||||||
if (shipLayout.has_value())
|
if (b->shipLayout.has_value())
|
||||||
{
|
{
|
||||||
layout = *shipLayout;
|
layout = *b->shipLayout;
|
||||||
}
|
}
|
||||||
m_layoutPreview->setShipAndLayout(
|
m_layoutPreview->setShipAndLayout(
|
||||||
shipDef->layout, layout, &m_config->modules.modules);
|
shipDef->layout, layout, &m_config->modules.modules);
|
||||||
m_layoutPreview->show();
|
|
||||||
m_configureLayoutBtn->show();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
m_layoutPreview->hide();
|
|
||||||
m_configureLayoutBtn->hide();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -566,21 +563,6 @@ const ShipDef* SelectedBuildingPanel::findShipDef(const std::string& id) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SelectedBuildingPanel::handleEvent(std::shared_ptr<const TickAdvancedEvent> /*event*/)
|
void SelectedBuildingPanel::handleEvent(std::shared_ptr<const TickAdvancedEvent> /*event*/)
|
||||||
{
|
|
||||||
refreshSelectionDisplay(RefreshReason::PeriodicTick);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SelectedBuildingPanel::handleEvent(
|
|
||||||
std::shared_ptr<const PlayerCommandsAppliedEvent> /*event*/)
|
|
||||||
{
|
|
||||||
// Player commands (e.g. choosing a shipyard schematic) are applied by a
|
|
||||||
// queued drain, not synchronously. When the game is paused no tick advances,
|
|
||||||
// so TickAdvancedEvent never fires; refresh here too, otherwise the panel
|
|
||||||
// would not reflect the change until the next tick or a re-selection.
|
|
||||||
refreshSelectionDisplay(RefreshReason::CommandApplied);
|
|
||||||
}
|
|
||||||
|
|
||||||
void SelectedBuildingPanel::refreshSelectionDisplay(RefreshReason reason)
|
|
||||||
{
|
{
|
||||||
if (m_selectedEntity.has_value())
|
if (m_selectedEntity.has_value())
|
||||||
{
|
{
|
||||||
@@ -604,19 +586,8 @@ void SelectedBuildingPanel::refreshSelectionDisplay(RefreshReason reason)
|
|||||||
}
|
}
|
||||||
const ConstructionSite* s = m_sim->buildings().findSite(m_singleBuildingId);
|
const ConstructionSite* s = m_sim->buildings().findSite(m_singleBuildingId);
|
||||||
if (s)
|
if (s)
|
||||||
{
|
|
||||||
// A periodic tick only advances construction progress, so update just the
|
|
||||||
// progress label. Rebuilding every tick would hide/re-show all widgets and
|
|
||||||
// cancel any in-progress click on the recipe button. An applied command
|
|
||||||
// may have changed the site's recipe/layout, so rebuild in that case.
|
|
||||||
if (reason == RefreshReason::CommandApplied)
|
|
||||||
{
|
{
|
||||||
rebuild();
|
rebuild();
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
refreshSiteProgress(s);
|
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
buildEmpty();
|
buildEmpty();
|
||||||
@@ -676,11 +647,9 @@ void SelectedBuildingPanel::onSelectRecipeClicked()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// The emit is synchronous: MainWindow pauses the game, runs the modal
|
// The emit is synchronous: MainWindow pauses the game, runs the modal
|
||||||
// selection dialog, and restores the speed before this returns. The chosen
|
// selection dialog, applies the chosen recipe/schematic, and restores the
|
||||||
// recipe/schematic is only *enqueued* as a command, though, and drains on a
|
// speed before this returns. rebuild() then refreshes the button caption,
|
||||||
// later frame -- so this rebuild() still sees the old recipe. The per-tick
|
// tooltip, preview, and buffers for the new selection.
|
||||||
// refreshBuffers() path picks up the new schematic (and shows the layout
|
|
||||||
// preview + Configure Layout button) once the command has been applied.
|
|
||||||
EventManager::getInstance()->sendEventImmediately(
|
EventManager::getInstance()->sendEventImmediately(
|
||||||
std::make_shared<RecipeSelectionRequestedEvent>(m_singleBuildingId));
|
std::make_shared<RecipeSelectionRequestedEvent>(m_singleBuildingId));
|
||||||
rebuild();
|
rebuild();
|
||||||
|
|||||||
@@ -16,7 +16,6 @@
|
|||||||
#include "EntitySelectedEvent.h"
|
#include "EntitySelectedEvent.h"
|
||||||
#include "EventHandler.h"
|
#include "EventHandler.h"
|
||||||
#include "GameConfig.h"
|
#include "GameConfig.h"
|
||||||
#include "PlayerCommandsAppliedEvent.h"
|
|
||||||
#include "RecipesConfig.h"
|
#include "RecipesConfig.h"
|
||||||
#include "SelectionChangedEvent.h"
|
#include "SelectionChangedEvent.h"
|
||||||
#include "ShipLayout.h"
|
#include "ShipLayout.h"
|
||||||
@@ -34,7 +33,6 @@ class QVBoxLayout;
|
|||||||
|
|
||||||
class SelectedBuildingPanel : public QWidget,
|
class SelectedBuildingPanel : public QWidget,
|
||||||
public CombinedEventHandler<TickAdvancedEvent,
|
public CombinedEventHandler<TickAdvancedEvent,
|
||||||
PlayerCommandsAppliedEvent,
|
|
||||||
EntitySelectedEvent,
|
EntitySelectedEvent,
|
||||||
SelectionChangedEvent,
|
SelectionChangedEvent,
|
||||||
DebugDrawToggledEvent>
|
DebugDrawToggledEvent>
|
||||||
@@ -48,7 +46,6 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void handleEvent(std::shared_ptr<const TickAdvancedEvent> event) override;
|
void handleEvent(std::shared_ptr<const TickAdvancedEvent> event) override;
|
||||||
void handleEvent(std::shared_ptr<const PlayerCommandsAppliedEvent> event) override;
|
|
||||||
void handleEvent(std::shared_ptr<const EntitySelectedEvent> event) override;
|
void handleEvent(std::shared_ptr<const EntitySelectedEvent> event) override;
|
||||||
void handleEvent(std::shared_ptr<const SelectionChangedEvent> event) override;
|
void handleEvent(std::shared_ptr<const SelectionChangedEvent> event) override;
|
||||||
void handleEvent(std::shared_ptr<const DebugDrawToggledEvent> event) override;
|
void handleEvent(std::shared_ptr<const DebugDrawToggledEvent> event) override;
|
||||||
@@ -59,18 +56,7 @@ private slots:
|
|||||||
void onSplitterFilterChanged();
|
void onSplitterFilterChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Why the selection display is being refreshed. A periodic tick only needs a
|
|
||||||
// lightweight content update (e.g. a construction site's progress label),
|
|
||||||
// whereas an applied player command may have changed the configuration and
|
|
||||||
// needs a full structural rebuild.
|
|
||||||
enum class RefreshReason
|
|
||||||
{
|
|
||||||
PeriodicTick,
|
|
||||||
CommandApplied
|
|
||||||
};
|
|
||||||
|
|
||||||
void onSelectionChanged(const std::vector<BuildingId>& ids);
|
void onSelectionChanged(const std::vector<BuildingId>& ids);
|
||||||
void refreshSelectionDisplay(RefreshReason reason);
|
|
||||||
void rebuild();
|
void rebuild();
|
||||||
void hideAllWidgets();
|
void hideAllWidgets();
|
||||||
void clearContent();
|
void clearContent();
|
||||||
@@ -78,10 +64,6 @@ private:
|
|||||||
void buildSingle(BuildingId id);
|
void buildSingle(BuildingId id);
|
||||||
void buildMulti(const std::vector<BuildingId>& ids);
|
void buildMulti(const std::vector<BuildingId>& ids);
|
||||||
void refreshBuffers(const Building* b);
|
void refreshBuffers(const Building* b);
|
||||||
void refreshSiteProgress(const ConstructionSite* s);
|
|
||||||
void updateShipyardLayoutWidgets(BuildingType type,
|
|
||||||
const std::string& recipeId,
|
|
||||||
const std::optional<ShipLayoutConfig>& shipLayout);
|
|
||||||
void buildSplitterFilters(const std::optional<BeltSystem::SplitterInfo>& info);
|
void buildSplitterFilters(const std::optional<BeltSystem::SplitterInfo>& info);
|
||||||
const RecipeDef* findRecipe(const Building* b) const;
|
const RecipeDef* findRecipe(const Building* b) const;
|
||||||
const ShipDef* findShipDef(const std::string& id) const;
|
const ShipDef* findShipDef(const std::string& id) const;
|
||||||
|
|||||||
Reference in New Issue
Block a user