64 lines
10 KiB
Markdown
64 lines
10 KiB
Markdown
# Architecture
|
|
|
|
## Principles
|
|
|
|
- Keep rules data-driven.
|
|
- Keep battle state independent from rendering.
|
|
- Build one complete vertical slice before expanding systems.
|
|
- Prefer simple Godot nodes and GDScript until a system proves it needs more structure.
|
|
|
|
## Current Layout
|
|
|
|
- `project.godot`: Godot 4 project configuration.
|
|
- `scenes/battle_scene.tscn`: Main playable battle scene.
|
|
- `scripts/core/data_catalog.gd`: JSON definition loader and deployment hydrator.
|
|
- `scripts/core/campaign_state.gd`: Thin campaign save state for gold, inventory, roster, completed scenarios, and campaign flags.
|
|
- `scripts/core/battle_state.gd`: Battle rules, unit state, turns, AI, and victory checks.
|
|
- `scripts/scenes/battle_scene.gd`: Rendering, input handling, and HUD wiring.
|
|
- `audio/bgm/*.wav` and `audio/sfx/*.wav`: Placeholder music loops and interface/action stingers.
|
|
- `data/campaign/campaign.json`: Campaign chapter ranges, scenario order, and scenario paths.
|
|
- `data/defs/*.json`: Officers, classes, terrain, items, and skills.
|
|
- `data/scenarios/*.json`: Scenario definitions.
|
|
|
|
## Data Files
|
|
|
|
- `data/defs/classes.json`: Class stats, movement types, skill access.
|
|
- `data/defs/officers.json`: Named officer base stats, growth, and default portrait paths.
|
|
- `data/defs/items.json`: Weapons, armor, accessories, and consumables.
|
|
- `data/defs/skills.json`: MP tactic definitions for damage, healing, target rules, and range.
|
|
- `data/defs/terrain.json`: Terrain colors, defense, avoid, and movement costs.
|
|
- `data/campaign/campaign.json`: Chapter ranges, scenario sequence, titles, and resource paths.
|
|
- `data/scenarios/*.json`: Dialogue, deployments, rewards, and branching flags.
|
|
|
|
## Battle Loop
|
|
|
|
1. Load battle data.
|
|
2. Hydrate deployments through `DataCatalog`.
|
|
3. Dispatch battle-start scenario events.
|
|
4. Show scenario briefing with the campaign chapter header, battle header, objective summary, chapter overview, manual checkpoint menu, and optional pre-battle shop, Armory, Roster, and Formation setup.
|
|
5. Player selects a unit.
|
|
6. Briefing completion dispatches battle-begin events, including opening dialogue.
|
|
7. Unit moves, attacks, chooses a tactic or consumable item from the side menu, changes equipment, counters, gains EXP, levels up, promotes at class thresholds, or waits. Tactic buttons show MP cost, kind, range, and power, support, debuff, or damage-over-time effect. Physical attacks roll hit chance from unit agility and target terrain avoid; misses give reduced EXP. The scene plays placeholder SFX, a short movement slide, low-HP warning rings, hover target preview badges, and floating combat text for core action and UI feedback.
|
|
8. Player ends turn.
|
|
9. Enemy AI moves, attacks, and can cast damage, healing, support, debuff, or damage-over-time tactics through the same combat and skill resolvers.
|
|
10. Battle checks scenario-defined defeat conditions first, including commander loss and turn limits, then victory conditions such as enemy defeat or reaching a marked destination tile.
|
|
11. Turn-start and movement-triggered scenario events run as phases change or units enter marked cells, and may update objectives or deploy reinforcements.
|
|
12. On first-time victory, `CampaignState` snapshots player progression and the battle inventory, applies rewards once, advances `current_scenario_id`, and writes `user://campaign_save.json`.
|
|
13. If the scenario defines post-battle dialogue, it plays once before the victory result panel opens.
|
|
14. The victory result panel summarizes rewards plus saved player level-ups and promotions.
|
|
15. If the scenario defines post-battle choices, the result panel waits for the player to select one, then saves the selected flags and optional choice rewards before enabling the next battle.
|
|
16. The result panel can load the next scenario, passing saved roster and inventory overlays back into `BattleState`.
|
|
17. Once all campaign scenarios are complete, the battle scene shows a campaign completion panel instead of loading another map.
|
|
|
|
## Save Boundary
|
|
|
|
`BattleState` owns tactical runtime fields. `CampaignState` saves only campaign-facing data: completed scenario ids, gold, inventory counts, joined officers, campaign flags, and a player roster snapshot. It does not save map positions, pre-battle Roster/Formation choices, action flags, temporary status effects, or transient battle occupancy.
|
|
|
|
Joined officers gate whether `requires_joined` player deployments are loaded at all. Rewards and post-battle choices can add or remove joined officers without deleting their saved roster snapshot, so a later rejoin can preserve progression. When a save loads, `CampaignState` scans completed scenarios in campaign order and reapplies only officer join/leave transitions from rewards and the saved `applied_post_battle_choices` ledger, allowing added recruitment rewards to reach older saves without replaying gold or items. Old saves without the ledger can backfill a choice id only when exactly one completed scenario choice matches saved flags. Roster entries are keyed by `officer_id` when available, so future scenarios can deploy the same joined officer under different scenario-specific `unit_id` values. Saved progression overlays final battle stats for now; a later equipment refactor should split base stats from equipment bonuses before adding item leveling or stat recalculation. New battles currently start player units healed to their saved maximum HP/MP. Skill access is carried in roster snapshots, with class defaults still applied for old saves that do not include skills. Promoted class ids are saved in the roster, and the next battle rehydrates class-derived movement, growth, skills, and range from the saved class id before equipment range is recalculated.
|
|
|
|
Inventory and campaign flags are copied from `CampaignState` into `BattleState` when a scenario starts. The pre-battle Chapters overview reads `CampaignState` chapter progress and can load completed or current battles without mutating the save. The pre-battle Save menu can write the current campaign state to `user://campaign_manual_save.json`, and loading that checkpoint restores it into `user://campaign_save.json` before re-entering the same pending-choice, completion, or current-briefing branch as startup. Pre-battle shop stock comes from the loaded scenario's `shop.items` list plus matching `shop.conditional_items` blocks, with optional finite limits from item entries or `stock` maps. Shop purchases and 50% sell-back are campaign transactions: they update saved gold, unequipped inventory, and finite-stock purchase counts immediately, write the save file, and refresh the already-loaded battle inventory before the player begins the battle. Pre-battle Armory equipment changes use the same equipment rules as battle HUD equipment changes, then immediately save merged roster equipment/stat snapshots and inventory stock. Pre-battle Roster uses scenario `roster.max_units`, `roster.required_officers`, and `roster.required_units` to mark optional player deployments as sortie or reserve; reserve units remain in the candidate list but are excluded from board occupancy, drawing, selection, AI targeting, and living-unit victory checks. Non-controllable player units can act as protected escort targets: enemies can attack them and conditions can reference them, but they are skipped by player selection, Armory, Formation, counterattacks, and campaign roster progression snapshots when `persist_progression` is false. Scenario `ai_target_priority` nudges enemy movement and damage-skill scoring toward important targets such as envoys without overriding defeat bonuses or range checks. Pre-battle Formation uses the loaded scenario's `formation.cells` and only mutates current battle unit positions before battle-begin events fire. Destination victory cells from `unit_reaches_tile` conditions are exposed to the scene for objective overlays and tile info. Movement-triggered `unit_reaches_tile` events receive the unit that just moved, so ambushes fire on entry rather than from units already standing on a marker. Briefing data can append matching `briefing.conditional_lines`, and scenario events can use `when.campaign_flags` to branch dialogue, objective changes, and reinforcements from saved campaign choices. Consumable use can restore HP or MP or cure matching damage-over-time statuses, and in-battle item use plus equipment swaps mutate only the battle copy until victory; defeats and restarts do not spend saved items or preserve in-battle gear changes. Post-battle choices write campaign `flags` only after the result-panel button is pressed; if the player reloads after rewards are saved but before selecting, the pending scenario id restores the victory choice panel without replaying rewards. The next battle button stays locked until the campaign choice save succeeds. Equipment rewards share the same `item_id -> count` inventory stock, cover weapon, armor, and accessory slots, can be equipped from the side HUD before the selected unit moves or acts, and do not appear in the consumable action menu. Already completed scenarios can be replayed without granting duplicate rewards, choices, replay inventory consumption, save writes, current-scenario advancement, or finite-stock purchases; pre-battle Shop, Armory, Roster, Formation, and Save are disabled on completed-scenario replays to avoid side effects.
|
|
|
|
The battle scene owns presentation feedback: briefing and result states use menu BGM, briefing headers combine `CampaignState` chapter ranges, campaign order, and title data with `BattleState` briefing and objective text, active battles use battle BGM, dialogue lines can show optional portrait textures or speaker-initial fallback panels and can place the portrait on the left or right side of the dialogue panel, board units render low-HP warning rings and HP bar color states from battle state fields, hover previews are rendered as target badges from existing `BattleState` forecast APIs, log/result hooks trigger placeholder SFX, support, debuff, and poison effects use distinct floating feedback colors, `BattleState.unit_motion_requested` asks the scene to interpolate a unit's draw position during movement, and `BattleState.combat_feedback_requested` asks the scene to draw transient floating combat text without changing battle rules.
|
|
|
|
The `New Campaign` button clears `user://campaign_save.json`, resets campaign state to the first scenario, and reloads the opening briefing.
|