mirror of
https://github.com/nolen777/eagle0.git
synced 2026-07-28 21:35:42 +00:00
Document current tutorial flow (#7264)
This commit is contained in:
+130
-234
@@ -1,267 +1,163 @@
|
|||||||
# Tutorial Battle System
|
# Tutorial Battle System
|
||||||
|
|
||||||
This document describes the tutorial battle system for first-time players. The system provides a scripted introductory battle that teaches combat basics while telling a story.
|
This document describes the current opening tutorial battle. It is based on the live implementation in `TutorialGameCreation.scala`, `TutorialBattleController.cpp`, `ShardokGamesManager.cpp`, `ResolveBattleAction.scala`, and the Unity dialogue scripts.
|
||||||
|
|
||||||
|
For the Unity tutorial UI/dialogue architecture, see `TUTORIAL_SYSTEM.md`. For copy and content notes, see `TUTORIAL_CONTENT.md`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Overview
|
## Current Flow
|
||||||
|
|
||||||
When a new player starts their first game in tutorial mode, they experience:
|
When a player creates a tutorial game at the default `OpeningBattle` phase:
|
||||||
|
|
||||||
1. **Narrative Intro** - Story screens introducing the scenario
|
1. `TutorialGameCreation.createTutorialGame()` creates a `GameType.Tutorial(TutorialPhase.OpeningBattle)` game starting in `RoundPhase.BattleRequest`.
|
||||||
2. **Immediate Battle** - Skip strategic map, start directly in combat
|
2. Province 14, **Onmaa**, belongs to Sadar Rakon's faction and already has a defending army.
|
||||||
3. **Scripted Flee** - Enemy flees when player is "on the ropes"
|
3. Bregos Fyar's faction has a hostile attacking army, led by Ikhaan Tarn, already moving from province 31 to Onmaa.
|
||||||
4. **Reinforcements** - Allied heroes arrive mid-battle
|
4. `RequestBattlesAction` immediately creates the Shardok battle and tags tutorial reinforcement hero IDs `100` and `101`.
|
||||||
5. **Heroes Join** - Reinforcement heroes join player's faction after battle
|
5. `GamesManager` stores the generated `TutorialBattleConfig` for this game and passes it to Shardok when the battle starts.
|
||||||
|
6. Shardok creates the battle with the configured timed reinforcement events.
|
||||||
|
7. Unity plays the active dialogue scripts from `tutorial_strategic.json` and `tutorial_battle.json`.
|
||||||
|
|
||||||
|
There is also a tutorial phase-start path. Starting after `OpeningBattle` applies precomputed tutorial setup results from `TutorialPhaseResultsLoader`; this is why the Unity post-battle trigger logic handles a "battle was never observed" auto-resolve path.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Battle Configuration
|
## Battle Setup
|
||||||
|
|
||||||
### Defender (Player - John Ranil)
|
### Defender: Sadar Rakon's Faction
|
||||||
- **Heroes**: John Ranil + 2 random vassals (3 total)
|
|
||||||
- **Units**:
|
|
||||||
- 2x Light Infantry (300 troops each, 60 training/armament)
|
|
||||||
- 1x Longbowmen (200 troops, 60 training/armament)
|
|
||||||
- **Province**: 14 (Onmaa) with 40 support, 500 gold, 3000 food
|
|
||||||
- **Restriction**: Cannot flee (must defend)
|
|
||||||
|
|
||||||
### Attacker (Ikhaan Tarn)
|
Configured in `src/main/resources/net/eagle0/eagle/tutorial_parameters.json`:
|
||||||
- **Heroes**: Ikhaan Tarn + 2 sworn brothers (3 total)
|
|
||||||
- **Units**:
|
|
||||||
- 1x Heavy Cavalry (600 troops, 80 training/armament)
|
|
||||||
- 1x Heavy Infantry (500 troops, 80 training/armament)
|
|
||||||
- 1x Longbowmen (300 troops, 80 training/armament)
|
|
||||||
- **Origin Province**: 31
|
|
||||||
|
|
||||||
### Flee Trigger
|
- **Faction ID:** 3
|
||||||
Attacker flees when ANY of these conditions are met:
|
- **Province:** 14, Onmaa
|
||||||
- Player loses 1 unit
|
- **Starting support:** 23
|
||||||
- 5 rounds have passed
|
- **Starting resources:** 50 gold, 4000 food
|
||||||
|
- **Heroes:** Sadar Rakon, Old Marek the Learned, Agamemnon
|
||||||
|
- **Battalions:**
|
||||||
|
- Rakon's Loyalists: Light Infantry, 529 size, 80 training, 75 armament
|
||||||
|
- Onmaa Defenders: Light Infantry, 311 size, 80 training, 60 armament
|
||||||
|
- Hunters of the Steppe: Longbowmen, 478 size, 80 training, 60 armament
|
||||||
|
|
||||||
### Reinforcements
|
### Attacker: Bregos Fyar's Faction
|
||||||
When Tarn flees, these heroes arrive as player reinforcements:
|
|
||||||
- Elena Fyar
|
The opening attacking army is configured under Bregos Fyar's `attackingArmies`:
|
||||||
- Hedrick
|
|
||||||
- The Boulder
|
- **Faction ID:** 2
|
||||||
|
- **Origin province:** 31
|
||||||
|
- **Destination province:** 14, Onmaa
|
||||||
|
- **Heroes:** Ikhaan Tarn, Tall Edgtheow, Waylaid Julius, Luke the Prank-tricker
|
||||||
|
- **Battalions:**
|
||||||
|
- Doomriders: Heavy Cavalry, 592 size, 80 training, 80 armament
|
||||||
|
- The Shardok's Guard: Heavy Infantry, 507 size, 80 training, 80 armament
|
||||||
|
- Bowmen of Nikemi: Longbowmen, 291 size, 80 training, 80 armament
|
||||||
|
- Swift Sabres: Light Cavalry, 450 size, 80 training, 80 armament
|
||||||
|
|
||||||
|
Tarn's unit is made visible to the defender from the start through `TutorialBattleConfig.initial_visibilities`.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Implementation Architecture
|
## Reinforcements
|
||||||
|
|
||||||
### Phase 1: Narrative Intro System
|
The current tutorial config has two timed reinforcement events. It does not currently configure a scripted Tarn flee event.
|
||||||
|
|
||||||
**Proto Changes:**
|
| Event ID | Trigger | Hero ID | Hero | Battalion |
|
||||||
- `game_state_view.proto`: Added `TutorialNarrativeScreen` message and `pending_narrative_screens` field
|
|----------|---------|---------|------|-----------|
|
||||||
- `game_parameters.proto`: Added `TutorialNarrativeScreen` and `tutorial_narrative_screens` field
|
| `elena_reinforcement_round_5` | End of round 5 or later | 101 | Elena Fyar | Fyar's Vanguard, Heavy Infantry, 535 size, 75 training, 75 armament |
|
||||||
|
| `ranil_reinforcement_round_7` | End of round 7 or later | 100 | John Ranil | Ranil's Riders, Heavy Cavalry, 458 size, 80 training, 80 armament |
|
||||||
|
|
||||||
**Unity Client:**
|
Shardok creates reinforcement units at battle creation time with `PENDING_REINFORCEMENT` status. `TutorialBattleController` activates them when their event triggers and returns a `TUTORIAL_REINFORCEMENTS_ARRIVED` action result. Unity maps that action to profession-specific dialogue triggers:
|
||||||
- `NarrativeScreenController.cs`: Modal screen with title, body text, optional image
|
|
||||||
- `EagleGameController.cs`: Checks for pending narrative screens on game start
|
|
||||||
|
|
||||||
**Narrative Content** (defined in `tutorial_game_parameters.json`):
|
- `tutorial_reinforcement_paladin` for Elena Fyar
|
||||||
```
|
- `tutorial_reinforcement_engineer` for John Ranil
|
||||||
Screen 1: "The Engineer of Onmaa" - Player backstory
|
|
||||||
Screen 2: "The Traitor Arrives" - Tarn's attack
|
|
||||||
Screen 3: "Defend Your Home" - Call to action
|
|
||||||
```
|
|
||||||
|
|
||||||
### Phase 2: Auto-Start Battle
|
The reinforcement action includes an `attacker_starting_position_index`; Shardok resolves that to currently open map positions and starts a reinforcement placement phase.
|
||||||
|
|
||||||
**Configuration:**
|
|
||||||
- `tutorial_game_parameters.json`: Contains `tutorialBattle` config with attacker, target, flee conditions
|
|
||||||
- `game_parameters.proto`: `TutorialBattleConfig` message
|
|
||||||
|
|
||||||
**Game Creation:**
|
|
||||||
- `NewGameCreation.scala`: `setupTutorialBattle()` method creates:
|
|
||||||
- `HostileArmyGroup` with `Attacking` status (bypasses decision phase)
|
|
||||||
- `Army` for defender
|
|
||||||
- Both added to target province
|
|
||||||
|
|
||||||
**Battle Creation:**
|
|
||||||
- When round advances to `BattleRequest` phase, `RequestBattlesAction` automatically creates the `ShardokBattle`
|
|
||||||
|
|
||||||
### Phase 3: Scripted Flee Mechanism
|
|
||||||
|
|
||||||
**C++ Component:**
|
|
||||||
- `TutorialBattleController.hpp/cpp`: Controller for scripted battle logic
|
|
||||||
|
|
||||||
**Key Methods:**
|
|
||||||
```cpp
|
|
||||||
// Check all events and execute triggered actions (called at end of each round)
|
|
||||||
std::vector<ActionResult> CheckAndExecuteEvents(
|
|
||||||
const GameStateW& state,
|
|
||||||
const SettingsGetter& settings,
|
|
||||||
const std::shared_ptr<RandomGenerator>& randomGenerator);
|
|
||||||
```
|
|
||||||
|
|
||||||
**Integration:**
|
|
||||||
- `ShardokEngine.cpp`: Calls `CheckAndExecuteEvents()` at end of each round in `HandlePlayerTurnEnd()`
|
|
||||||
- Events fire in config order, each event fires at most once
|
|
||||||
|
|
||||||
### Phase 4: Mid-Battle Reinforcements
|
|
||||||
|
|
||||||
**Action Types** (in `action_type.proto`):
|
|
||||||
- `TUTORIAL_ENEMY_FLED = 78`
|
|
||||||
- `TUTORIAL_REINFORCEMENTS_ARRIVED = 79`
|
|
||||||
|
|
||||||
**Implementation (TODO):**
|
|
||||||
- When scripted flee triggers, create reinforcement units in defender's reserves
|
|
||||||
- Units have `UnitStatus_RESERVE_UNIT` status
|
|
||||||
- Reinforcements enter battle on subsequent turns
|
|
||||||
|
|
||||||
### Phase 5: Tutorial Popups
|
|
||||||
|
|
||||||
**Content Definitions** (in `TutorialContentDefinitions.cs`):
|
|
||||||
- `tutorial_battle_started`: "Defend your province!"
|
|
||||||
- `tutorial_enemy_fled`: "The enemy retreats!"
|
|
||||||
- `tutorial_reinforcements_arrived`: "Allies have arrived!"
|
|
||||||
- `tutorial_battle_victory`: Victory celebration
|
|
||||||
|
|
||||||
**Trigger Integration:**
|
|
||||||
- `TutorialTriggerRegistry.cs`: Handles tutorial action types from `ActionResultView`
|
|
||||||
- `ShardokGameController.cs`: Detects tutorial events and triggers popups
|
|
||||||
|
|
||||||
### Phase 6: Post-Battle Hero Joining
|
|
||||||
|
|
||||||
**Implementation (TODO):**
|
|
||||||
- `ResolveBattleAction.scala`: Check if battle was tutorial and defender won
|
|
||||||
- Create `TutorialHeroJoined` action results adding reinforcement heroes to player faction
|
|
||||||
- Set appropriate loyalty, vigor, location for joined heroes
|
|
||||||
|
|
||||||
### Phase 7: Post-Battle Strategic Dialogue
|
|
||||||
|
|
||||||
After the battle ends and the player returns to the strategic map, narrative dialogues guide them through the next steps.
|
|
||||||
|
|
||||||
**Dialogue 1: Battle Aftermath** (trigger: `tutorial_battle_ended`)
|
|
||||||
- Old Marek reflects on the close call
|
|
||||||
- Tarn has vanished — rumors of sorcery or escape, no one knows
|
|
||||||
- Captured lieutenants: they followed orders, not to blame for Tarn's madness
|
|
||||||
- Player should try to recruit them or hold them
|
|
||||||
- No instruction text needed — Handle Captured Heroes UI is self-explanatory
|
|
||||||
|
|
||||||
**Dialogue 2: Rebuild Support** (trigger: `tutorial_rebuild_support`)
|
|
||||||
- Fires when available commands no longer include HandleCapturedHeroCommand
|
|
||||||
- Marek explains the province needs support rebuilt after the battle
|
|
||||||
- Engineers (John Ranil) can **Improve** the province — build infrastructure, develop economy
|
|
||||||
- Paladins (Elena Fyar) can **Give Alms** — distribute food to win hearts
|
|
||||||
- Instructions highlight **Improve** and **Give Alms** buttons
|
|
||||||
- Goal: get Support to **40** before January for tax revenue
|
|
||||||
|
|
||||||
**Implementation:**
|
|
||||||
- Dialogues defined in `tutorial_strategic.json`
|
|
||||||
- `tutorial_battle_ended`: fire when tutorial battle is removed from `RunningShardokGameModels`
|
|
||||||
- `tutorial_rebuild_support`: fire when available commands no longer include `HandleCapturedHeroCommand`
|
|
||||||
- Register `ImproveButton` and `AlmsButton` as highlight targets in `TutorialTargetRegistry`
|
|
||||||
|
|
||||||
See `TUTORIAL_CONTENT.md` for full dialogue text.
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## File Summary
|
## Scripted Event Controller
|
||||||
|
|
||||||
|
`TutorialBattleController` is a generic event-driven controller, even though the current tutorial battle only uses timed reinforcements.
|
||||||
|
|
||||||
|
Supported trigger types:
|
||||||
|
|
||||||
|
- `after_round`
|
||||||
|
- `units_lost`
|
||||||
|
- `damage_taken`
|
||||||
|
- `unit_killed`
|
||||||
|
|
||||||
|
Supported action types:
|
||||||
|
|
||||||
|
- `flee`
|
||||||
|
- `reinforcements`
|
||||||
|
|
||||||
|
Events are checked at the end of each round, evaluated in config order, and each `event_id` fires at most once.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Battle Resolution
|
||||||
|
|
||||||
|
`RequestBattlesAction` marks tutorial opening battles with `reinforcementHeroIds = Set(100, 101)`. `ResolveBattleAction` uses that metadata so reinforcement heroes are accepted when battle results return, even though they were not part of the original defending army.
|
||||||
|
|
||||||
|
After the opening battle resolves, `ResolveBattleAction` applies `TutorialTarnDisappearsAction` for `GameType.Tutorial(TutorialPhase.OpeningBattle)`. Tarn is removed from captured/unaffiliated province state and from his faction's leaders list, matching the post-battle dialogue that he has vanished.
|
||||||
|
|
||||||
|
`TutorialBattleAutoResolve` provides a synthetic defender victory for tutorial setup paths that skip past the opening battle. In that synthetic result:
|
||||||
|
|
||||||
|
- The defender wins.
|
||||||
|
- Reinforcement heroes 100 and 101 survive.
|
||||||
|
- Tarn is marked outlawed/escaped.
|
||||||
|
- Other attacker heroes are captured.
|
||||||
|
- Defender battalions take about 30% casualties.
|
||||||
|
- Attacker battalions are destroyed.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Unity Dialogue Hooks
|
||||||
|
|
||||||
|
Strategic tutorial dialogue:
|
||||||
|
|
||||||
|
- `game_started`: opening Onmaa monologue, ending with `FightButton` highlighted.
|
||||||
|
- `tutorial_battle_ended`: post-battle aftermath.
|
||||||
|
- `tutorial_rebuild_support`: support rebuilding guidance after captured-hero handling is done.
|
||||||
|
|
||||||
|
Battle tutorial dialogue:
|
||||||
|
|
||||||
|
- `shardok_placement_started`: placement and unit overview.
|
||||||
|
- `shardok_battle_running`: first player turn guidance.
|
||||||
|
- Ability/terrain triggers such as `archery_available`, `melee_available`, `ability_charge_available`, `start_fire_available`, `thunderstorm`, `duel_available`, `hide_available`, and `engineer_near_enemy`.
|
||||||
|
- Reinforcement triggers `tutorial_reinforcement_paladin` and `tutorial_reinforcement_engineer`.
|
||||||
|
- Capture triggers `enemy_hero_captured` and `friendly_hero_captured`.
|
||||||
|
- `shardok_battle_reset`: replay dialogue after a battle reset.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Files To Check First
|
||||||
|
|
||||||
### New Files
|
|
||||||
| File | Purpose |
|
| File | Purpose |
|
||||||
|------|---------|
|
|------|---------|
|
||||||
| `TutorialBattleController.hpp/cpp` | C++ scripted flee and reinforcement logic |
|
| `src/main/resources/net/eagle0/eagle/tutorial_parameters.json` | Tutorial map, factions, starting provinces, armies |
|
||||||
| `NarrativeScreenController.cs` | Unity narrative screen display |
|
| `src/main/scala/net/eagle0/eagle/service/new_game_creation/TutorialGameCreation.scala` | Opening battle setup and `TutorialBattleConfig` creation |
|
||||||
|
| `src/main/protobuf/net/eagle0/common/tutorial_battle_config.proto` | Scripted event config schema |
|
||||||
### Modified Files
|
| `src/main/cpp/net/eagle0/shardok/server/ShardokGamesManager.cpp` | Adds pending reinforcement units and applies initial visibility |
|
||||||
| File | Changes |
|
| `src/main/cpp/net/eagle0/shardok/library/tutorial/TutorialBattleController.cpp` | Evaluates scripted events and creates Shardok action results |
|
||||||
|------|---------|
|
| `src/main/scala/net/eagle0/eagle/library/actions/impl/action/RequestBattlesAction.scala` | Creates the opening battle and tags reinforcement hero IDs |
|
||||||
| `tutorial_game_parameters.json` | Battle configuration and narrative content |
|
| `src/main/scala/net/eagle0/eagle/library/actions/impl/action/ResolveBattleAction.scala` | Resolves reinforcements and applies Tarn disappearance |
|
||||||
| `game_state_view.proto` | Narrative screens field |
|
| `src/main/scala/net/eagle0/eagle/service/tutorial/TutorialBattleAutoResolve.scala` | Synthetic result for phase-start paths after the opening battle |
|
||||||
| `game_parameters.proto` | Tutorial battle config |
|
| `src/main/csharp/net/eagle0/clients/unity/eagle0/Assets/Tutorial/Triggers/TutorialTriggerRegistry.cs` | Converts strategic and tactical events into dialogue triggers |
|
||||||
| `player_info.proto` | Tutorial battle flags for Shardok |
|
| `src/main/csharp/net/eagle0/clients/unity/eagle0/Assets/Resources/Dialogues/tutorial_battle.json` | Active battle dialogue content |
|
||||||
| `action_type.proto` | Tutorial action types |
|
|
||||||
| `NewGameCreation.scala` | Auto-start battle setup |
|
|
||||||
| `EagleGameController.cs` | Narrative display integration |
|
|
||||||
| `TutorialContentDefinitions.cs` | Battle tutorial content |
|
|
||||||
| `TutorialTriggerRegistry.cs` | Tutorial event triggers |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Remaining Implementation
|
|
||||||
|
|
||||||
### Disable Player Flee
|
|
||||||
In `FleeCommandFactory.cpp`:
|
|
||||||
- Check if tutorial mode is enabled
|
|
||||||
- If defender, don't offer flee commands
|
|
||||||
|
|
||||||
### Reinforcement Units
|
|
||||||
In `TutorialBattleController::ExecuteScriptedFlee()`:
|
|
||||||
- Create `Unit` objects for Elena Fyar, Hedrick, The Boulder
|
|
||||||
- Add to defender's reserve units
|
|
||||||
- Generate `TUTORIAL_REINFORCEMENTS_ARRIVED` action
|
|
||||||
|
|
||||||
### Post-Battle Hero Joining
|
|
||||||
In Eagle's battle resolution:
|
|
||||||
- Detect tutorial battle completion
|
|
||||||
- Create heroes in player's faction with proper stats
|
|
||||||
- Generate notification action results
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Testing Checklist
|
## Testing Checklist
|
||||||
|
|
||||||
- [ ] New user sees narrative screens before battle
|
- [ ] Tutorial game starts in BattleRequest and creates an Onmaa battle immediately.
|
||||||
- [ ] Battle starts immediately after narratives (no strategic map)
|
- [ ] Opening strategic dialogue highlights the Fight button.
|
||||||
- [ ] Player has correct units (3 heroes, 3 battalions)
|
- [ ] Tarn's unit is visible to the defender from battle start.
|
||||||
- [ ] Attacker has correct units (3 heroes, 3 battalions)
|
- [ ] Placement dialogue fires during setup.
|
||||||
- [ ] Player cannot flee
|
- [ ] First-turn battle-running dialogue fires on the player's first active turn.
|
||||||
- [ ] Tarn flees after player loses 1 unit
|
- [ ] Elena Fyar arrives from the configured round-5 event.
|
||||||
- [ ] Tarn flees after 5 rounds (if no units lost)
|
- [ ] John Ranil arrives from the configured round-7 event.
|
||||||
- [ ] Reinforcements appear when Tarn flees
|
- [ ] Reinforcement arrival dialogues fire once per hero.
|
||||||
- [ ] Tutorial popups appear at correct moments
|
- [ ] Battle reset clears battle dialogue completion for replay.
|
||||||
- [ ] Reinforcement heroes join faction after battle victory
|
- [ ] Post-battle aftermath fires after the battle leaves `RunningShardokGameModels`.
|
||||||
|
- [ ] Rebuild-support dialogue waits until captured-hero handling and visible notifications are done.
|
||||||
---
|
- [ ] Starting a tutorial at a later phase uses precomputed setup and still reaches coherent strategic dialogue state.
|
||||||
|
|
||||||
## Configuration Reference
|
|
||||||
|
|
||||||
### tutorial_game_parameters.json
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"tutorialBattle": {
|
|
||||||
"attackerFactionHead": "Ikhaan Tarn",
|
|
||||||
"targetProvinceId": 14,
|
|
||||||
"fleeAfterDefenderUnitsLost": 1,
|
|
||||||
"fleeAfterRounds": 5,
|
|
||||||
"reinforcements": ["Elena Fyar", "Hedrick", "The Boulder"]
|
|
||||||
},
|
|
||||||
"tutorialNarrativeScreens": [
|
|
||||||
{ "title": "...", "bodyText": "...", "imagePath": "" }
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### TutorialBattleConfig Proto (Shardok)
|
|
||||||
```protobuf
|
|
||||||
message TutorialBattleConfig {
|
|
||||||
bool enabled = 1;
|
|
||||||
repeated TutorialEvent events = 2;
|
|
||||||
repeated UnitInitialSize initial_sizes = 3; // For damage tracking
|
|
||||||
}
|
|
||||||
|
|
||||||
message TutorialEvent {
|
|
||||||
string event_id = 1;
|
|
||||||
TutorialTrigger trigger = 2;
|
|
||||||
TutorialAction action = 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
message TutorialTrigger {
|
|
||||||
oneof trigger_type {
|
|
||||||
RoundTrigger after_round = 1;
|
|
||||||
UnitsLostTrigger units_lost = 2;
|
|
||||||
DamageTakenTrigger damage_taken = 3;
|
|
||||||
UnitKilledTrigger unit_killed = 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
message TutorialAction {
|
|
||||||
oneof action_type {
|
|
||||||
FleeAction flee = 1;
|
|
||||||
ReinforcementsAction reinforcements = 2; // Uses CommonUnit
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|||||||
+13
-13
@@ -1,6 +1,8 @@
|
|||||||
# Tutorial Content Guide
|
# Tutorial Content Guide
|
||||||
|
|
||||||
This document defines all tutorial content. Edit this to refine the text, then update `TutorialContentDefinitions.cs` to match.
|
This document describes tutorial content. The active tutorial experience is driven by the narrative dialogue JSON files under `src/main/csharp/net/eagle0/clients/unity/eagle0/Assets/Resources/Dialogues/`.
|
||||||
|
|
||||||
|
The older step-based modal/overlay content in `TutorialContentDefinitions.cs` is currently dormant: `RegisterAll()` returns before registering any sequences. Keep that in mind when editing this file; changing `TutorialContentDefinitions.cs` will not affect the live tutorial until the early return is removed and the overlap with dialogue triggers is audited.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -181,17 +183,16 @@ These fire after the first tutorial battle ends and the player returns to the st
|
|||||||
>
|
>
|
||||||
> Any of our heroes can develop the land or give alms to the people — nothing wins hearts faster than a full belly. Engineers like John Ranil are especially effective at improving a province, and paladins like Elena Fyar are the best at winning hearts. Either way, we need the people's support before the tax collectors come round in January.
|
> Any of our heroes can develop the land or give alms to the people — nothing wins hearts faster than a full belly. Engineers like John Ranil are especially effective at improving a province, and paladins like Elena Fyar are the best at winning hearts. Either way, we need the people's support before the tax collectors come round in January.
|
||||||
|
|
||||||
**Instructions:** Use **Improve** and **Give Alms** to raise Support. You must have at least **40** support in the province by January in order to collect gold and food in taxes.
|
**Instructions:** John Ranil's step highlights `ImproveProvinceButton` and explains **Improve**. Elena Fyar's step highlights `GiveAlmsButton` and explains **Give Alms**. You must have at least **40** support in the province by January in order to collect gold and food in taxes.
|
||||||
|
|
||||||
**Highlight targets:** `SupportField`
|
**Highlight targets:** `ImproveProvinceButton`, `GiveAlmsButton`
|
||||||
|
|
||||||
### Implementation Notes
|
### Implementation Notes
|
||||||
|
|
||||||
- Both dialogues go in `tutorial_strategic.json`
|
- Both dialogues are defined in `tutorial_strategic.json`.
|
||||||
- Need new trigger events: `tutorial_battle_ended` and `tutorial_rebuild_support`
|
- `tutorial_battle_ended` fires from `TutorialTriggerRegistry.CheckTutorialBattleEnded()` when the opening battle is gone and strategic commands are available. It also handles the server-side auto-resolve path where the Unity client never observed a running Shardok model.
|
||||||
- `tutorial_battle_ended` should fire when the tutorial battle is removed from `RunningShardokGameModels` in `EagleGameModel.ApplyGameStateViewDiff`
|
- `tutorial_rebuild_support` fires from `TutorialTriggerRegistry.CheckTutorialRebuildSupport()` when available commands no longer include `HandleCapturedHeroCommand` and the notification panel has no visible info.
|
||||||
- `tutorial_rebuild_support` should fire when available commands no longer include `HandleCapturedHeroCommand` (i.e. the captured heroes phase is done and the player has regular commands available)
|
- `ImproveProvinceButton` and `GiveAlmsButton` are runtime command-button targets registered by `CommandButtonPanelController`.
|
||||||
- The "rebuild" dialogue should highlight the Improve and Give Alms buttons in the command panel (requires registering them as highlight targets)
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -210,11 +211,10 @@ These fire after the first tutorial battle ends and the player returns to the st
|
|||||||
## Adding New Tutorials
|
## Adding New Tutorials
|
||||||
|
|
||||||
1. Add entry to this document
|
1. Add entry to this document
|
||||||
2. Update `TutorialContentDefinitions.cs`:
|
2. For active tutorial content, add or edit a script in `Assets/Resources/Dialogues/tutorial_strategic.json` or `tutorial_battle.json`.
|
||||||
- For onboarding: add to `CreateOnboardingSequence()`
|
3. Ensure the trigger event exists in `TutorialTriggerRegistry.cs` or is fired directly by the relevant controller.
|
||||||
- For contextual: add to `RegisterStrategicTutorials()` or `RegisterTacticalTutorials()`
|
4. If reviving the dormant step-UI system, remove the early return in `TutorialContentDefinitions.RegisterAll()` only after auditing duplicate trigger coverage with the dialogue JSON.
|
||||||
3. Ensure trigger event exists in `TutorialTriggerRegistry.cs`
|
5. Test the flow.
|
||||||
4. Test the flow
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
+16
-11
@@ -3,7 +3,7 @@
|
|||||||
This document describes how the Unity client's tutorial system is wired together: the classes involved, the trigger catalog, the step lifecycle, and the expected first-session flow. It complements two existing docs:
|
This document describes how the Unity client's tutorial system is wired together: the classes involved, the trigger catalog, the step lifecycle, and the expected first-session flow. It complements two existing docs:
|
||||||
|
|
||||||
- **`TUTORIAL_CONTENT.md`** — human-facing copy for tutorial steps and dialogues
|
- **`TUTORIAL_CONTENT.md`** — human-facing copy for tutorial steps and dialogues
|
||||||
- **`TUTORIAL_BATTLE_SYSTEM.md`** — the scripted first-battle scenario (Tarn vs. John Ranil)
|
- **`TUTORIAL_BATTLE_SYSTEM.md`** — the scripted opening battle at Onmaa
|
||||||
|
|
||||||
It also overlaps slightly with the in-tree `Assets/Tutorial/TUTORIAL_PLAN.md`, which is an older implementation plan.
|
It also overlaps slightly with the in-tree `Assets/Tutorial/TUTORIAL_PLAN.md`, which is an older implementation plan.
|
||||||
|
|
||||||
@@ -20,12 +20,13 @@ There are **two parallel subsystems** that share the same trigger plumbing:
|
|||||||
| Step-based tutorial UI (modals, overlays, hints) | **Dormant** | `Tutorial/TutorialManager.cs`, `Tutorial/UI/*`, `Tutorial/Content/*` |
|
| Step-based tutorial UI (modals, overlays, hints) | **Dormant** | `Tutorial/TutorialManager.cs`, `Tutorial/UI/*`, `Tutorial/Content/*` |
|
||||||
| Narrative dialogue system | **Live** | `Tutorial/Dialogue/*`, `Resources/Dialogues/*.json` |
|
| Narrative dialogue system | **Live** | `Tutorial/Dialogue/*`, `Resources/Dialogues/*.json` |
|
||||||
|
|
||||||
`TutorialContentDefinitions.RegisterAll()` returns early at line 17 with the comment *"Old tutorial content suppressed — replaced by narrative dialogue system."* As a result:
|
`TutorialContentDefinitions.RegisterAll()` returns early with the comment *"Old tutorial content suppressed — replaced by narrative dialogue system."* As a result:
|
||||||
|
|
||||||
- `OnboardingSequence` is never assigned, so `StartOnboarding()` no-ops at the "No onboarding sequence assigned" branch.
|
- `OnboardingSequence` is never assigned, so `StartOnboarding()` no-ops at the "No onboarding sequence assigned" branch.
|
||||||
- No contextual tutorial sequences are registered with the trigger registry.
|
- No contextual tutorial sequences are registered with the trigger registry.
|
||||||
- The trigger registry still fires events normally, but nothing on the step-UI side is listening.
|
- The trigger registry still fires events normally, but nothing on the step-UI side is listening.
|
||||||
- `DialogueManager` consumes those same events and matches them against scripts in `Resources/Dialogues/` (`tutorial_strategic.json`, `tutorial_battle.json`).
|
- `DialogueManager` consumes those same events and matches them against scripts in `Resources/Dialogues/` (`tutorial_strategic.json`, `tutorial_battle.json`).
|
||||||
|
- Dialogue completion is persisted per game through `TutorialDialogueProgressStore`, so completed scripts do not replay after a reconnect or scene reload for the same game.
|
||||||
|
|
||||||
**In practice today, the entire active tutorial experience runs through the dialogue system.** The step-UI machinery is preserved for future use — content definitions still exist below the early return in `TutorialContentDefinitions.cs` for reference.
|
**In practice today, the entire active tutorial experience runs through the dialogue system.** The step-UI machinery is preserved for future use — content definitions still exist below the early return in `TutorialContentDefinitions.cs` for reference.
|
||||||
|
|
||||||
@@ -37,6 +38,7 @@ There are **two parallel subsystems** that share the same trigger plumbing:
|
|||||||
- **`Tutorial/TutorialManager.cs`** — Singleton. Owns `OnboardingSequence`, the active step queue, the trigger registry, and the dialogue manager handle. Initialized by `EagleGameController.SetUpGame()` and `ShardokGameController.SetUpGame()`.
|
- **`Tutorial/TutorialManager.cs`** — Singleton. Owns `OnboardingSequence`, the active step queue, the trigger registry, and the dialogue manager handle. Initialized by `EagleGameController.SetUpGame()` and `ShardokGameController.SetUpGame()`.
|
||||||
- **`Tutorial/TutorialState.cs`** — PlayerPrefs persistence. Tracks `OnboardingCompleted`, `OnboardingStepReached`, completed sequence IDs (HashSet for O(1) lookup), dismissed hints, and a global "tutorials disabled" preference.
|
- **`Tutorial/TutorialState.cs`** — PlayerPrefs persistence. Tracks `OnboardingCompleted`, `OnboardingStepReached`, completed sequence IDs (HashSet for O(1) lookup), dismissed hints, and a global "tutorials disabled" preference.
|
||||||
- **`Tutorial/TutorialTargetRegistry.cs`** — Maps string IDs to `RectTransform`s for highlighting. Static targets are Inspector-assigned (`ProvinceInfoPanel`, `SupportField`, `CommitButton`, etc.); dynamic targets register at runtime (e.g., hero rows from `HeroesAndBattalionsPanelController`).
|
- **`Tutorial/TutorialTargetRegistry.cs`** — Maps string IDs to `RectTransform`s for highlighting. Static targets are Inspector-assigned (`ProvinceInfoPanel`, `SupportField`, `CommitButton`, etc.); dynamic targets register at runtime (e.g., hero rows from `HeroesAndBattalionsPanelController`).
|
||||||
|
- **`Tutorial/TutorialDialogueCoordinator.cs`** — Recomputes durable dialogue triggers from the current strategic model and sends them directly to `DialogueManager`. This covers reconnects, phase-start tutorial games, and strategic beats that should be recoverable from state.
|
||||||
|
|
||||||
### Triggers
|
### Triggers
|
||||||
- **`Tutorial/Triggers/TutorialTriggerRegistry.cs`** (~1100 lines) — Routes game events to both the step UI (when sequences are registered) and `DialogueManager`. Maintains one-shot session flags so the same first-encounter trigger doesn't fire twice.
|
- **`Tutorial/Triggers/TutorialTriggerRegistry.cs`** (~1100 lines) — Routes game events to both the step UI (when sequences are registered) and `DialogueManager`. Maintains one-shot session flags so the same first-encounter trigger doesn't fire twice.
|
||||||
@@ -94,14 +96,14 @@ Panels can be anchored via `TutorialPanelAnchor` (`Center` / `Left` / `Right` /
|
|||||||
|
|
||||||
## Trigger Catalog
|
## Trigger Catalog
|
||||||
|
|
||||||
All triggers are raised via `TutorialTriggerRegistry`. Today these flow to `DialogueManager.TriggerDialogue()` and are matched against the dialogue JSON; if step-UI sequences are re-registered, they will also route there. File:line citations are for the registry unless noted; line numbers may drift.
|
Most event-style triggers are raised via `TutorialTriggerRegistry`. Durable strategic dialogue beats are also recomputed by `TutorialDialogueCoordinator` on each model update and sent directly to `DialogueManager.TriggerDialogue()`. Both paths match against the dialogue JSON; if step-UI sequences are re-registered, only the registry path will route to them. File:line citations are for the registry unless noted; line numbers may drift.
|
||||||
|
|
||||||
### Bootstrap / first-session
|
### Bootstrap / first-session
|
||||||
| Trigger | Fires from | When |
|
| Trigger | Fires from | When |
|
||||||
|---------|-----------|------|
|
|---------|-----------|------|
|
||||||
| `game_started` | `EagleGameController.SetUpGame()` | First time entering a tutorial game |
|
| `game_started` | `TutorialDialogueCoordinator.EligibleTriggers()` | Any tutorial strategic model update; dialogue progress keeps it from replaying |
|
||||||
| `first_battle_available` | `OnModelUpdated()` ~L204 | A `RunningShardokGameModel` first appears |
|
| `first_battle_available` | `OnModelUpdated()` ~L204 | A `RunningShardokGameModel` first appears |
|
||||||
| `battle_entered` | `OnBattleEntered()` ~L793 | User clicks **Battle!** |
|
| `battle_entered` | `OnBattleEntered()` ~L793 | User clicks **Fight!** |
|
||||||
| `tutorial_battle_ended` | `CheckTutorialBattleEnded()` ~L594 | Tutorial battle removed from running models |
|
| `tutorial_battle_ended` | `CheckTutorialBattleEnded()` ~L594 | Tutorial battle removed from running models |
|
||||||
| `tutorial_rebuild_support` | `CheckTutorialRebuildSupport()` ~L616 | Captured-heroes phase done |
|
| `tutorial_rebuild_support` | `CheckTutorialRebuildSupport()` ~L616 | Captured-heroes phase done |
|
||||||
| `tutorial_taxes_collected` | `CheckTaxesCollected()` ~L680 | New Year action with positive tax delta |
|
| `tutorial_taxes_collected` | `CheckTaxesCollected()` ~L680 | New Year action with positive tax delta |
|
||||||
@@ -135,7 +137,7 @@ All triggers are raised via `TutorialTriggerRegistry`. Today these flow to `Dial
|
|||||||
| `hero_stat_gained` | ~L281 | `HeroStatGained` action result |
|
| `hero_stat_gained` | ~L281 | `HeroStatGained` action result |
|
||||||
| `hero_profession_gained` | ~L284 | `ProfessionGained` action result |
|
| `hero_profession_gained` | ~L284 | `ProfessionGained` action result |
|
||||||
| `tutorial_faction_appears` | ~L287 | `TutorialFactionAppears` action — the Fracture Covenant landing |
|
| `tutorial_faction_appears` | ~L287 | `TutorialFactionAppears` action — the Fracture Covenant landing |
|
||||||
| `tutorial_hero_faction_appears` | ~L290 | `TutorialHeroFactionAppears` action (no dialogue script today) |
|
| `tutorial_hero_faction_appears` | ~L290 | `TutorialHeroFactionAppears` action; has a four-step strategic dialogue today |
|
||||||
| `tutorial_hero_departed` | ~L299 | First `HeroesDeparted` action against another player (King's hero abandons service) |
|
| `tutorial_hero_departed` | ~L299 | First `HeroesDeparted` action against another player (King's hero abandons service) |
|
||||||
| `tutorial_hero_departed_again` | ~L302 | Second `HeroesDeparted` in a *different* month than the first |
|
| `tutorial_hero_departed_again` | ~L302 | Second `HeroesDeparted` in a *different* month than the first |
|
||||||
|
|
||||||
@@ -220,8 +222,8 @@ Script shape (see `tutorial_strategic.json`):
|
|||||||
"speakerName": "Old Marek the Learned",
|
"speakerName": "Old Marek the Learned",
|
||||||
"speakerImagePath": "fixed/old_marek_the_learned.png",
|
"speakerImagePath": "fixed/old_marek_the_learned.png",
|
||||||
"dialogueText": "...",
|
"dialogueText": "...",
|
||||||
"instructionText": "Click Battle! to enter tactical combat.",
|
"instructionText": "Click <b>Fight!</b> to enter tactical combat.",
|
||||||
"highlightTarget": "GoToBattleButton",
|
"highlightTarget": "FightButton",
|
||||||
"persistHighlight": true,
|
"persistHighlight": true,
|
||||||
"highlightProvince": "Onmaa",
|
"highlightProvince": "Onmaa",
|
||||||
"completionEvent": null
|
"completionEvent": null
|
||||||
@@ -247,10 +249,12 @@ Dialogue panel position defaults sensibly per scene (`top` during combat) and ca
|
|||||||
|
|
||||||
Reset paths:
|
Reset paths:
|
||||||
- `TutorialState.Reset()` clears everything.
|
- `TutorialState.Reset()` clears everything.
|
||||||
- Entering a tutorial game (`GameType.Tutorial`) resets so it can be replayed.
|
- Entering a tutorial game (`GameType.Tutorial`) resets `TutorialState`; dialogue replay is controlled separately by the per-game dialogue progress key.
|
||||||
- Settings → "Reset Tutorials" calls `TutorialManager.ResetAllProgress()`, which also calls `DialogueManager.ResetCompletedScripts()`.
|
- Settings → "Reset Tutorials" calls `TutorialManager.ResetAllProgress()`, which also calls `DialogueManager.ResetCompletedScripts()`.
|
||||||
|
|
||||||
`DialogueManager` tracks completed scripts in memory only — they reset whenever `TutorialState` does.
|
`TutorialDialogueProgressStore` separately stores completed dialogue script IDs per game under PlayerPrefs keys named `Eagle0_TutorialDialogueProgress_<gameId>`.
|
||||||
|
|
||||||
|
`DialogueManager` keeps the active game's completed script IDs in memory and saves them through `TutorialDialogueProgressStore`. `ResetCompletedScripts()` clears the current game's dialogue progress; `ClearCurrentGameProgress()` deletes the current game's persisted dialogue key.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -276,7 +280,7 @@ You play **Sadar Rakon**, formerly Ikhaan Tarn's most trusted lieutenant. Tarn's
|
|||||||
|
|
||||||
| # | Trigger | What happens |
|
| # | Trigger | What happens |
|
||||||
|---|---------|--------------|
|
|---|---------|--------------|
|
||||||
| 1 | `game_started` | **Opening monologue** (3 steps): Sadar's backstory, the Reclamation, last stand at Onmaa. Ends highlighting the **Battle!** button (`persistHighlight`) |
|
| 1 | `game_started` | **Opening monologue** (3 steps): Sadar's backstory, the Reclamation, last stand at Onmaa. Ends highlighting the **Fight!** button (`FightButton`, `persistHighlight`) |
|
||||||
| 2 | `tutorial_battle_ended` | **Aftermath**: Tarn has *vanished*; you've captured his lieutenants. Recruit them or hold them |
|
| 2 | `tutorial_battle_ended` | **Aftermath**: Tarn has *vanished*; you've captured his lieutenants. Recruit them or hold them |
|
||||||
| 3 | `tutorial_rebuild_support` | **Rebuild Onmaa** (3 steps): Marek frames it; **John Ranil** introduces *Improve* (engineer bonus); **Elena Fyar** introduces *Give Alms* (paladin bonus). Goal: 40 support by January |
|
| 3 | `tutorial_rebuild_support` | **Rebuild Onmaa** (3 steps): Marek frames it; **John Ranil** introduces *Improve* (engineer bonus); **Elena Fyar** introduces *Give Alms* (paladin bonus). Goal: 40 support by January |
|
||||||
| 4 | `tutorial_loyalty_warning` | November-ish, low-loyalty hero may leave at year end → use *Give Gold* / *Feast* |
|
| 4 | `tutorial_loyalty_warning` | November-ish, low-loyalty hero may leave at year end → use *Give Gold* / *Feast* |
|
||||||
@@ -288,6 +292,7 @@ You play **Sadar Rakon**, formerly Ikhaan Tarn's most trusted lieutenant. Tarn's
|
|||||||
| 10 | `tutorial_faction_appears` | **The twist**: Fracture Covenant lands at Ingia and Soria. *The Eagle* commands them. Tarn may be with them |
|
| 10 | `tutorial_faction_appears` | **The twist**: Fracture Covenant lands at Ingia and Soria. *The Eagle* commands them. Tarn may be with them |
|
||||||
| 11 | `tutorial_hero_departed` | First King's hero abandons service — "something deeper is at work" |
|
| 11 | `tutorial_hero_departed` | First King's hero abandons service — "something deeper is at work" |
|
||||||
| 12 | `tutorial_hero_departed_again` | Second one in a different month — "something is wrong, I can feel it" (foreshadowing) |
|
| 12 | `tutorial_hero_departed_again` | Second one in a different month — "something is wrong, I can feel it" (foreshadowing) |
|
||||||
|
| 13 | `tutorial_hero_faction_appears` | John Ranil and Elena Fyar leave Sadar's service and reappear as independent tutorial factions |
|
||||||
|
|
||||||
### Tactical battle flow (`tutorial_battle.json`)
|
### Tactical battle flow (`tutorial_battle.json`)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user