Tutorial: Crookfinger leads a new master through the first steps

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard ZinkandClaude Opus 5.5 committed 2026-11-06 00:37:25 +01:00
1 parent 1fb6a6ab58
commit 160935bf40
23 files changed
+501 -3

No files matched your search

+30 -1
View File
@@ -586,6 +586,7 @@ struct App {
coop_editor: bool, // the co-op match plays a mission of an editor campaign
coop_run: Option<CoopRun>, // ... and this is where the run through that campaign stands
offline: bool, // a co-op game played without the server: the other seat is an AI partner
tutorial: bool, // the tutorial mission is being played (its end leads back to the title)
fps: f32,
renderer: String,
frame_ms: f32,
@@ -881,7 +882,7 @@ async fn main() {
let seed = (miniquad::date::now() * 1000.0) as u32 | 1;
// behind the title menu: the endless bridge battle
let (game, cam) = Game::new_demo(seed);
let mut app = App { game, cam, settings, seed, status: None, recorded: false, client: MatchClient::new(), ws_id: 0, match_mode: false, coop: false, coop_editor: false, coop_run: None, offline: false, fps: 0.0, renderer: platform::renderer(), frame_ms: 0.0, frame_hist: [0.0; FRAME_HIST], frame_hist_pos: 0, custom_campaign: None, custom_progress: None, custom_mission_index: None, custom_next_index: None, editor: None, editor_testing: false, editor_test_only: false, editor_public_pending: false, editor_loading: None, pending_briefing: None };
let mut app = App { game, cam, settings, seed, status: None, recorded: false, client: MatchClient::new(), ws_id: 0, match_mode: false, coop: false, coop_editor: false, coop_run: None, offline: false, tutorial: false, fps: 0.0, renderer: platform::renderer(), frame_ms: 0.0, frame_hist: [0.0; FRAME_HIST], frame_hist_pos: 0, custom_campaign: None, custom_progress: None, custom_mission_index: None, custom_next_index: None, editor: None, editor_testing: false, editor_test_only: false, editor_public_pending: false, editor_loading: None, pending_briefing: None };
menu.progress = platform::storage_get(PROGRESS_KEY).and_then(|s| serde_json::from_str(&s).ok()).unwrap_or_default();
apply_game_settings(&mut app.game, &app.settings);
let mut inp = Input { ldown: None, rdown: None, rlast: (0.0, 0.0), rdrag: false, mlast: None, minimap_drag: false, tlast: None, touch_world: false, inspect: None };
@@ -1571,6 +1572,24 @@ app.pending_briefing = briefing_of(&menu.campaign_doc, i);
}
}
}
MenuAction::Tutorial => {
let doc = editor::templates::tutorial_campaign();
let progress = campaign_doc::CampaignProgress::for_doc(&doc);
(app.game, app.cam) = custom_mission_game(&doc, 0, progress.run_seed);
app.pending_briefing = briefing_of(&doc, 0);
apply_game_settings(&mut app.game, &app.settings);
app.game.coop = false;
app.coop = false;
app.recorded = false;
app.custom_campaign = Some(doc);
app.custom_progress = Some(progress);
app.custom_mission_index = Some(0);
app.tutorial = true;
menu.page = None;
ui.log.clear();
ui.show_help = false;
autosave_t = 0.0;
}
MenuAction::OpenRepo => platform::open_url(menu::REPO_URL),
MenuAction::OpenClaudeCode => platform::open_url(menu::CLAUDE_CODE_URL),
MenuAction::OpenOpenCode => platform::open_url(menu::OPENCODE_URL),
@@ -1608,6 +1627,7 @@ app.pending_briefing = briefing_of(&menu.campaign_doc, i);
MenuAction::BackToEditor => back_to_editor(&mut app, &mut menu, &mut r3d),
MenuAction::MainMenu if app.editor_testing => back_to_editor(&mut app, &mut menu, &mut r3d),
MenuAction::MainMenu => {
app.tutorial = false;
if app.match_mode || app.game.in_match() {
app.leave_match();
} else if app.game.state == 0 {
@@ -1689,6 +1709,15 @@ app.pending_briefing = briefing_of(&menu.campaign_doc, i);
menu.error = Some(i18n::tr("net.status_offline"));
}
}
UiAction::EndNext | UiAction::EndCampaign if app.tutorial && app.game.state == 1 => {
app.tutorial = false;
app.custom_campaign = None;
app.custom_progress = None;
app.custom_mission_index = None;
menu.page = Some(Page::Title);
app.seed = next_seed(app.seed);
(app.game, app.cam) = Game::new_demo(app.seed);
}
UiAction::EndNext => {
if app.custom_campaign.is_some() {
let campaign = app.custom_campaign.clone();