Skip to main content

LoadedProject

Struct LoadedProject 

Source
pub struct LoadedProject { /* private fields */ }
Expand description

A fully loaded zero-Rust game project.

Implementations§

Source§

impl LoadedProject

Source

pub fn load(root: &Path) -> Result<Self>

Load the project rooted at root (the directory containing .dotzuki-editor.json) from disk. Convenience for load_with_files over a DiskFiles.

§Errors

Fails on a missing/unparseable manifest or any DSL diagnostic.

Examples found in repository?
examples/menu_accept.rs (line 83)
76fn main() {
77    let mut args = std::env::args().skip(1);
78    let dir = args
79        .next()
80        .expect("usage: menu_accept <project-dir> [shot-dir]");
81    let shot_dir = args.next().map(PathBuf::from);
82
83    let project = LoadedProject::load(Path::new(&dir)).expect("load project");
84    let mut game = RunnerGame::new(
85        project,
86        RunnerOptions {
87            headless: true,
88            fresh: true,
89            // Deterministic battles: always hit, 89% variance, never crit.
90            rng_script: Some(vec![50, 100, 1]),
91            ..RunnerOptions::default()
92        },
93    )
94    .expect("boot game");
95
96    dismiss(&mut game); // StartTown main
97    println!("== 1. the shop ==");
98    println!("money at boot: {} (manifest shop.startMoney)", game.money());
99
100    // Talk to the Shopkeeper at (13,10): the shop opens on the spot.
101    game.debug_place(12, 10, Direction::Right);
102    press_a(&mut game);
103    let rows = game.shop_lines().expect("shop open");
104    println!("shop shelf: {rows:?}");
105    snap(&mut game, &shot_dir, "shop");
106
107    // Buy one Potion: 100 → 80 G, inventory 3 → 4.
108    let before = game.money();
109    press_a(&mut game);
110    let rows = game.shop_lines().expect("shop still open");
111    println!(
112        "bought a Potion: money {before} → {} G; inventory {:?}",
113        game.money(),
114        game.inventory().expect("inventory materialized")
115    );
116    println!("shop note: {:?}", rows.last());
117    snap(&mut game, &shot_dir, "shop-bought");
118    assert_eq!(game.money(), 80, "20 G charged");
119    assert_eq!(game.inventory().unwrap().get("potion"), Some(&4));
120    press_a(&mut game); // dismiss the note
121
122    // B exits; the scene resumes with its follow-up line.
123    press(&mut game, GbButton::B);
124    let page = game.dialogue_text().expect("scene resumed after the shop");
125    println!("scene resumed with: {page:?}");
126    assert!(page.contains("Come again!"), "page: {page:?}");
127    dismiss(&mut game);
128
129    println!("\n== 2. the whiteout ==");
130    // Talk to the Hermit at (11,10) and lose on purpose (overwhelming Slime).
131    game.debug_place(12, 10, Direction::Left);
132    press_a(&mut game); // "Something stirs…"
133    press_a(&mut game); // → battle
134    let mut last_log: Vec<String> = Vec::new();
135    for _ in 0..120 {
136        let Some(battle) = game.battle() else { break };
137        last_log = battle.log().to_vec();
138        press_a(&mut game);
139    }
140    println!("lost battle log:");
141    for line in &last_log {
142        println!("  {line}");
143    }
144    assert_eq!(
145        last_log.last().map(String::as_str),
146        Some("You lost the battle…")
147    );
148
149    // The scene's post-lose text still plays…
150    let page = game.dialogue_text().expect("post-lose text");
151    println!("post-lose text: {page:?}");
152    assert!(page.contains("no match"), "page: {page:?}");
153    press_a(&mut game); // scene finishes → whiteout
154
155    // …then the whiteout: blackout first, then the collapsed line.
156    assert!(game.whiteout_active(), "whiteout armed");
157    idle(&mut game, 10);
158    snap(&mut game, &shot_dir, "whiteout-blackout");
159    idle(&mut game, 25);
160    let page = game.dialogue_text().expect("whiteout message");
161    println!("whiteout message: {page:?}");
162    assert!(page.contains("collapsed"), "page: {page:?}");
163    snap(&mut game, &shot_dir, "whiteout-message");
164
165    // Landing it: party healed to full, back at the entry spawn, flags kept.
166    press_a(&mut game);
167    assert!(!game.whiteout_active());
168    println!("after the whiteout:");
169    println!(
170        "  map: {:?} tile: {:?}",
171        game.current_map_id(),
172        game.player_tile()
173    );
174    for m in game.party_state().expect("party state") {
175        println!("  {} hp {} mp {} status {:?}", m.id, m.hp, m.mp, m.status);
176    }
177    println!(
178        "  __played_main_StartTown flag kept: {}",
179        game.flag("__played_main_StartTown")
180    );
181    assert_eq!(game.current_map_id(), Some("StartTown"));
182    assert!(game
183        .party_state()
184        .unwrap()
185        .iter()
186        .all(|m| m.hp > 0 && m.status.is_none()));
187    snap(&mut game, &shot_dir, "whiteout-after");
188
189    println!("\nacceptance OK");
190}
More examples
Hide additional examples
examples/levels_accept.rs (line 79)
71fn main() {
72    let mut args = std::env::args().skip(1);
73    let dir = args
74        .next()
75        .expect("usage: levels_accept <project-dir> [shot-dir]");
76    let shot_dir = args.next().map(PathBuf::from);
77
78    let save_file = Path::new(&dir).join(".dotzuki-save.json");
79    let project = LoadedProject::load(Path::new(&dir)).expect("load project");
80    let mut game = RunnerGame::new(
81        project,
82        RunnerOptions {
83            headless: true,
84            fresh: true,
85            write_saves: true,
86            external_saves: false,
87            save_file: Some(save_file.clone()),
88            // Deterministic battles: always hit, 89% variance, never crit.
89            rng_script: Some(vec![50, 100, 1]),
90            ..RunnerOptions::default()
91        },
92    )
93    .expect("boot game");
94
95    // Auto-A drives everything: dialogue pages, battle narration, and the
96    // Fight → first-skill picks. Back-to-back `startBattle` commands swap
97    // Battle→Battle INSIDE one update, so a new battle is detected by the
98    // log resetting, not just by a not-in-battle gap.
99    let mut battle_no = 0usize;
100    let mut completed = 0usize;
101    let mut last_log: Vec<String> = Vec::new();
102    let mut was_in_battle = false;
103    let mut snapped_battle = false;
104
105    for frame_no in 0..6000u32 {
106        if frame_no % 4 == 0 {
107            frame(&mut game, GbButton::A.bit_mask());
108        } else {
109            idle(&mut game, 1);
110        }
111
112        let in_battle = game.battle().is_some();
113        if in_battle {
114            let log_len = game.battle().unwrap().log().len();
115            let new_battle = !was_in_battle || log_len < last_log.len();
116            if new_battle {
117                if was_in_battle {
118                    // Seamless Battle→Battle swap: close the previous one
119                    // out (the party state is already harvested).
120                    completed += 1;
121                    println!("\nbattle {completed} log:");
122                    for line in &last_log {
123                        println!("  {line}");
124                    }
125                    print_party(&game, &format!("after battle {completed}"));
126                    println!();
127                    if completed == 2 {
128                        break;
129                    }
130                }
131                battle_no += 1;
132                let battle = game.battle().unwrap();
133                println!("battle {battle_no} starts:");
134                for c in battle.party() {
135                    println!(
136                        "  {} level {} exp {} hp {}/{} mp {}/{} atk {}",
137                        c.name, c.level, c.exp, c.hp, c.max_hp, c.mp, c.max_mp, c.attack
138                    );
139                }
140                if battle_no == 2 && !snapped_battle {
141                    snapped_battle = true;
142                    snap(&mut game, &shot_dir, "battle2-start");
143                }
144            }
145            last_log = game.battle().unwrap().log().to_vec();
146        } else if was_in_battle {
147            completed += 1;
148            println!("\nbattle {completed} log:");
149            for line in &last_log {
150                println!("  {line}");
151            }
152            print_party(&game, &format!("after battle {completed}"));
153            println!();
154            last_log.clear();
155            if completed == 2 {
156                break;
157            }
158        }
159        was_in_battle = in_battle;
160    }
161    if completed < 2 {
162        eprintln!("harness: only {completed} battle(s) completed within the frame cap");
163        std::process::exit(1);
164    }
165
166    // Dismiss the post-battle text so the scene finishes (the save is
167    // written at that stable point).
168    for _ in 0..40 {
169        if game.dialogue_text().is_none() {
170            break;
171        }
172        press_a(&mut game);
173    }
174    assert!(game.flag("SLIMES_BEATEN"), "the scene ran to its end");
175
176    // The Party view: Lv + EXP progress on the member rows.
177    frame(&mut game, GbButton::Start.bit_mask());
178    idle(&mut game, 1);
179    press_a(&mut game); // Party
180    println!("party view:");
181    for line in game.menu_lines().expect("party view open") {
182        println!("  {line}");
183    }
184    snap(&mut game, &shot_dir, "party-view");
185    frame(&mut game, GbButton::B.bit_mask());
186    idle(&mut game, 1);
187    frame(&mut game, GbButton::B.bit_mask());
188    idle(&mut game, 1);
189
190    // Save round trip: a fresh boot resumes level/exp from the save.
191    println!("\nsave round trip ({}):", save_file.display());
192    let project = LoadedProject::load(Path::new(&dir)).expect("reload project");
193    let game = RunnerGame::new(
194        project,
195        RunnerOptions {
196            headless: true,
197            save_file: Some(save_file),
198            rng_script: Some(vec![50, 100, 1]),
199            ..RunnerOptions::default()
200        },
201    )
202    .expect("boot from save");
203    print_party(&game, "resumed from the save");
204    assert!(
205        game.party_state().is_some_and(|p| p[0].level > 1),
206        "level must survive the save round trip"
207    );
208    println!("\nacceptance OK");
209}
examples/battle_accept.rs (line 53)
46fn main() {
47    let mut args = std::env::args().skip(1);
48    let dir = args
49        .next()
50        .expect("usage: battle_accept <project-dir> [shot-dir]");
51    let shot_dir = args.next().map(PathBuf::from);
52
53    let project = LoadedProject::load(Path::new(&dir)).expect("load project");
54    let mut game = RunnerGame::new(
55        project,
56        RunnerOptions {
57            headless: true,
58            fresh: true,
59            // Deterministic battles: always hit, 89% variance, never crit.
60            rng_script: Some(vec![50, 100, 1]),
61            ..RunnerOptions::default()
62        },
63    )
64    .expect("boot game");
65
66    // The agenda per battle (index = battles started so far − 1).
67    let agendas: &[&[Pick]] = &[&[Pick::Item, Pick::Party, Pick::Fight], &[Pick::Fight]];
68    let mut battles_done = 0usize;
69    let mut agenda: Vec<Pick> = Vec::new();
70    // Queued button presses; one key every other frame (a held key is not a
71    // fresh just-press, so keys alternate with idle frames).
72    let mut keys: Vec<GbButton> = Vec::new();
73    let mut last_log: Vec<String> = Vec::new();
74    let mut snapped: Vec<String> = Vec::new();
75    let mut was_in_battle_prev = false;
76
77    let mut input = InputState::new();
78    for frame in 0..6000u32 {
79        let was_in_battle = was_in_battle_prev;
80
81        // Decide this frame's input.
82        let mut mask = 0;
83        if frame % 2 == 0 {
84            if !keys.is_empty() {
85                mask = keys.remove(0).bit_mask();
86            } else if let Some(battle) = game.battle() {
87                if battle.in_menu() {
88                    let items = battle.menu_items();
89                    let label = if items.first().is_some_and(|i| i == "Fight") {
90                        "root"
91                    } else if items.iter().any(|i| i.contains('/')) {
92                        "party"
93                    } else {
94                        "sub"
95                    };
96                    if let Some(dir) = &shot_dir {
97                        let tag = format!("battle{}-{label}", battles_done + 1);
98                        if !snapped.contains(&tag) {
99                            snapped.push(tag.clone());
100                            snap(&mut game, &dir.join(format!("{tag}.png")));
101                        }
102                    }
103                    mask = match label {
104                        "root" => {
105                            let pick = agenda.first().copied().unwrap_or(Pick::Fight);
106                            if !agenda.is_empty() {
107                                agenda.remove(0);
108                            }
109                            match pick {
110                                Pick::Fight => GbButton::A.bit_mask(),
111                                Pick::Party => {
112                                    keys.push(GbButton::A);
113                                    GbButton::Down.bit_mask()
114                                }
115                                Pick::Item => {
116                                    keys.push(GbButton::Down);
117                                    keys.push(GbButton::A);
118                                    GbButton::Down.bit_mask()
119                                }
120                            }
121                        }
122                        // Submenus: confirm the entry under the cursor (the
123                        // party cursor starts on the first switchable member).
124                        _ => GbButton::A.bit_mask(),
125                    };
126                } else if frame % 4 == 0 {
127                    mask = GbButton::A.bit_mask(); // page narration
128                }
129            } else if frame % 4 == 0 {
130                mask = GbButton::A.bit_mask(); // page dialogue
131            }
132        }
133        input.set_from_bitmask(mask);
134        game.update(&input);
135        input.begin_frame();
136
137        let in_battle = game.battle().is_some();
138        // Battle-start edge: print the party it starts with (the carried-over
139        // state from battle 2 on) and load its agenda.
140        if in_battle && !was_in_battle {
141            let n = battles_done + 1;
142            let battle = game.battle().unwrap();
143            println!("battle {n} starts:");
144            for c in battle.party() {
145                let status = c.status.as_deref().unwrap_or("-");
146                println!(
147                    "  {} {}/{} MP {}/{} status {}",
148                    c.name, c.hp, c.max_hp, c.mp, c.max_mp, status
149                );
150            }
151            println!("  inventory: {:?}", battle.inventory());
152            agenda = agendas[battles_done].to_vec();
153        }
154        if in_battle {
155            last_log = game.battle().unwrap().log().to_vec();
156        }
157        // Battle-end edge: the runner has harvested the party state.
158        if was_in_battle && !in_battle {
159            battles_done += 1;
160            println!("\nbattle {battles_done} log:");
161            for line in &last_log {
162                println!("  {line}");
163            }
164            if let Some(party) = game.party_state() {
165                println!("party state after battle {battles_done}:");
166                for m in party {
167                    println!("  {} hp {} mp {} status {:?}", m.id, m.hp, m.mp, m.status);
168                }
169            }
170            if let Some(inv) = game.inventory() {
171                println!("inventory: {inv:?}");
172            }
173            println!();
174            last_log.clear();
175            if battles_done == 2 {
176                println!("both battles done — stopping");
177                break;
178            }
179        }
180        was_in_battle_prev = in_battle;
181    }
182
183    if battles_done < 2 {
184        eprintln!("harness: only {battles_done} battle(s) completed within the frame cap");
185        std::process::exit(1);
186    }
187}
examples/encounter_accept.rs (line 53)
46fn main() {
47    let mut args = std::env::args().skip(1);
48    let dir = args
49        .next()
50        .expect("usage: encounter_accept <project-dir> [shot-dir]");
51    let shot_dir = args.next().map(PathBuf::from);
52
53    let project = LoadedProject::load(Path::new(&dir)).expect("load project");
54    let mut game = RunnerGame::new(
55        project,
56        RunnerOptions {
57            headless: true,
58            fresh: true,
59            // Deterministic battles: always hit, 89% variance, never crit.
60            rng_script: Some(vec![50, 100, 1]),
61            ..RunnerOptions::default()
62        },
63    )
64    .expect("boot game");
65    println!("money at boot: {}", game.money());
66
67    // The agenda per battle (index = battles started so far − 1): the
68    // trainer battle tries Run first (blocked), the wild battle runs.
69    // Battle 1's agenda is pre-armed: the entry scene suspends on
70    // `startBattle` during boot, so its first menu frame precedes the
71    // battle-start edge below (later battles arm on their edge — text plays
72    // between them).
73    let agendas: &[&[Pick]] = &[&[Pick::Run, Pick::Fight], &[Pick::Run]];
74    let mut battles_done = 0usize;
75    let mut agenda: Vec<Pick> = agendas[0].to_vec();
76    // Queued button presses; one key every other frame (a held key is not a
77    // fresh just-press, so keys alternate with idle frames).
78    let mut keys: Vec<GbButton> = Vec::new();
79    let mut last_log: Vec<String> = Vec::new();
80    let mut was_in_battle_prev = false;
81    let mut snapped: Vec<String> = Vec::new();
82    // Shop agenda: 0 = root (→ Sell), 1 = sell view (→ sell one), 2 = note
83    // (→ dismiss), 3 = back to root (→ B), 4 = exit (→ B), 5 = done.
84    let mut shop_step = 0usize;
85    let mut shop_done = false;
86
87    let mut input = InputState::new();
88    for frame in 0..6000u32 {
89        let was_in_battle = was_in_battle_prev;
90
91        // Decide this frame's input.
92        let mut mask = 0;
93        if frame % 2 == 0 {
94            if !keys.is_empty() {
95                mask = keys.remove(0).bit_mask();
96            } else if let Some(battle) = game.battle() {
97                if battle.in_menu() {
98                    let is_root = battle.menu_items().first().is_some_and(|i| i == "Fight");
99                    mask = if is_root {
100                        let pick = agenda.first().copied().unwrap_or(Pick::Fight);
101                        if !agenda.is_empty() {
102                            agenda.remove(0);
103                        }
104                        match pick {
105                            Pick::Fight => GbButton::A.bit_mask(),
106                            Pick::Run => {
107                                // Fight/Party/Item/Run → Down ×3, confirm.
108                                keys.push(GbButton::Down);
109                                keys.push(GbButton::Down);
110                                keys.push(GbButton::A);
111                                GbButton::Down.bit_mask()
112                            }
113                        }
114                    } else {
115                        GbButton::A.bit_mask() // skill submenu
116                    };
117                } else if frame % 4 == 0 {
118                    mask = GbButton::A.bit_mask(); // page narration
119                }
120            } else if game.shop_lines().is_some() && battles_done == 2 && !shop_done {
121                match shop_step {
122                    0 => {
123                        println!("shop root: {:?}", game.shop_lines().unwrap());
124                        keys.push(GbButton::Down); // → Sell
125                        keys.push(GbButton::A);
126                    }
127                    1 => {
128                        println!("sell view: {:?}", game.shop_lines().unwrap());
129                        keys.push(GbButton::A); // sell one Potion
130                    }
131                    2 => {
132                        let lines = game.shop_lines().unwrap();
133                        println!("sell note: {lines:?}");
134                        if let Some(dir) = &shot_dir {
135                            snap(&mut game, &dir.join("shop-sold.png"));
136                        }
137                        keys.push(GbButton::A); // dismiss the note
138                    }
139                    3 => keys.push(GbButton::B), // back to the root
140                    _ => keys.push(GbButton::B), // exit the shop
141                }
142                shop_step += 1;
143            } else if frame % 4 == 0 {
144                mask = GbButton::A.bit_mask(); // page dialogue
145            }
146        }
147        input.set_from_bitmask(mask);
148        game.update(&input);
149        input.begin_frame();
150
151        let in_battle = game.battle().is_some();
152        // Battle-start edge: print the matchup and load its agenda.
153        if in_battle && !was_in_battle {
154            let n = battles_done + 1;
155            let battle = game.battle().unwrap();
156            println!(
157                "battle {n} starts: {} vs {} (queued {}, trainer {}, reward {})",
158                battle.player().name,
159                battle.enemy().name,
160                battle.enemies_remaining(),
161                battle.is_trainer(),
162                battle.trainer_money(),
163            );
164            if battles_done > 0 {
165                agenda = agendas[battles_done].to_vec();
166            }
167        }
168        if in_battle {
169            // Screenshot the two v2-d narration lines on their first show.
170            // Screenshot the two v2-d narration lines on their first show.
171            let line = game.battle().unwrap().current_line().map(str::to_string);
172            if let (Some(dir), Some(line)) = (&shot_dir, line) {
173                let tag = match line.as_str() {
174                    l if l.starts_with("Can't escape") => Some("run-blocked"),
175                    l if l.starts_with("Got away") => Some("run-safe"),
176                    l if l.starts_with("Got ") && l.ends_with("for winning!") => {
177                        Some("trainer-money")
178                    }
179                    _ => None,
180                };
181                if let Some(tag) = tag {
182                    if !snapped.contains(&tag.to_string()) {
183                        snapped.push(tag.to_string());
184                        snap(&mut game, &dir.join(format!("{tag}.png")));
185                    }
186                }
187            }
188            last_log = game.battle().unwrap().log().to_vec();
189        }
190        // Battle-end edge: the runner has harvested the state.
191        if was_in_battle && !in_battle {
192            battles_done += 1;
193            println!("\nbattle {battles_done} log:");
194            for line in &last_log {
195                println!("  {line}");
196            }
197            if battles_done == 1 {
198                assert!(
199                    last_log
200                        .iter()
201                        .any(|l| l == "Can't escape from a trainer battle!"),
202                    "the trainer Run attempt must be blocked (and the turn not consumed)"
203                );
204            }
205            println!("money after battle {battles_done}: {}", game.money());
206            if let Some(party) = game.party_state() {
207                for m in party {
208                    println!(
209                        "  {} hp {} mp {} level {} exp {}",
210                        m.id, m.hp, m.mp, m.level, m.exp
211                    );
212                }
213            }
214            println!();
215            last_log.clear();
216        }
217        // Shop-exit edge.
218        if shop_step >= 5 && game.shop_lines().is_none() && !shop_done {
219            shop_done = true;
220            println!(
221                "shop closed: money {}, inventory {:?}",
222                game.money(),
223                game.inventory()
224            );
225        }
226        // Everything done: both battles + the shop, scene finished.
227        if battles_done == 2 && shop_done && game.battle().is_none() {
228            println!("acceptance complete");
229            break;
230        }
231        was_in_battle_prev = in_battle;
232    }
233
234    // Verify the v2-d contract points and exit non-zero on any miss.
235    let money = game.money();
236    let potions = game.inventory().and_then(|inv| inv.get("potion").copied());
237    println!("final: money {money}, potions {potions:?}");
238    assert_eq!(battles_done, 2, "both battles must complete");
239    assert!(shop_done, "the shop sell must complete");
240    assert_eq!(money, 142, "100 start + 32 trainer + 10 sell (20/2)");
241    assert_eq!(potions, Some(2), "3 starting − 1 sold");
242}
Source

pub fn load_with_files(files: Arc<dyn ProjectFiles>) -> Result<Self>

Load the project from a ProjectFiles backend.

The DSL is compiled in memory; any compiler diagnostic (unreadable file, compile failure, route conflict) aborts the load with an error listing every diagnostic — the same bar dotzuki check enforces.

§Errors

Fails on a missing/unparseable manifest or any DSL diagnostic.

Source

pub fn recompile_scripts(&mut self) -> Result<()>

Recompile every DSL directory and swap the compiled scenes in place.

On success the script registry, routing table and stem indexes are replaced wholesale — a scene currently mid-activation keeps running the JS it was started with; the next activation picks up the new source. On any compiler diagnostic the old scenes are kept and an error listing every diagnostic is returned (same bar as load).

§Errors

Fails when the recompile produces any diagnostic.

Source

pub fn files(&self) -> &Arc<dyn ProjectFiles>

The project’s file backend.

Source

pub fn root(&self) -> &Path

Project root directory (empty for a project without a disk root).

Source

pub fn manifest(&self) -> &Manifest

The parsed .dotzuki-editor.json manifest.

Source

pub fn data_root(&self) -> &Path

Resolved data root (manifest dataRoot against the project root).

Source

pub fn data_root_rel(&self) -> &str

The data root as a project-relative POSIX path (VFS key prefix).

Source

pub fn gfx_root(&self) -> Option<&Path>

Resolved graphics root (manifest gfxRoot), when configured.

Source

pub fn gfx_root_rel(&self) -> String

The graphics root as a project-relative POSIX path (the manifest’s gfxRoot, default "gfx").

Source

pub fn scripts(&self) -> &ScriptLoader

Registry of compiled scene JS, keyed by scene name.

Source

pub fn report(&self) -> &CompileReport

The full DSL compile report.

Source

pub fn routes(&self) -> &[RouteEntry]

Storyline routing table (map, npc/onEnter) → storyline, collected from @trigger declarations across all scenes.

Source

pub fn scene_name_for_stem(&self, stem: &str) -> Option<&str>

Compiled scene name for a .scene file stem (e.g. "main""Main").

Source

pub fn stem_for_scene_name(&self, name: &str) -> Option<&str>

.scene file stem for a compiled scene name.

Source

pub fn maps_dir_rel(&self) -> String

The maps directory as a project-relative POSIX path: the map activity’s mapsDir (dataRoot-relative), default maps.

Source

pub fn maps_dir(&self) -> PathBuf

Directory holding the per-map subdirectories (disk form of maps_dir_rel).

Source

pub fn map_ids(&self) -> Vec<String>

Sorted ids of all map directories under maps_dir.

Source

pub fn entry_map(&self) -> Result<String>

Map to spawn on: game.entryMap, or the first map directory (sorted) under the maps dir.

§Errors

Fails when no entryMap is configured and no map directories exist.

Source

pub fn entry_scene_name(&self) -> Result<&str>

Scene to boot into: game.entryScene (a .scene file stem) resolved to its compiled scene name, or the first compiled scene sorted by source path.

§Errors

Fails when entryScene names a stem that compiled to nothing, or when the project has no scenes at all.

Source

pub fn load_map(&self, map_id: &str) -> Result<RuntimeMap>

Load a map by id from this project’s maps dir.

Source

pub fn table_dir(&self, table_id: &str) -> Option<PathBuf>

Directory holding the records of data table table_id (a table id from the data activity’s config.tables[]), resolved against the project root. None when the id names no declared table.

Source

pub fn table_dir_rel(&self, table_id: &str) -> Option<String>

The record directory of data table table_id as a project-relative POSIX path (the VFS form of table_dir). None when the id names no declared table.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> Conv for T

Source§

fn conv<T>(self) -> T
where Self: Into<T>,

Converts self into T using Into<T>. Read more
Source§

impl<T> Downcast for T
where T: Any,

Source§

fn into_any(self: Box<T>) -> Box<dyn Any>

Convert Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>

Convert Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be further downcast into Rc<ConcreteType> where ConcreteType implements Trait.
Source§

fn as_any(&self) -> &(dyn Any + 'static)

Convert &Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &Any’s vtable from &Trait’s.
Source§

fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)

Convert &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
Source§

impl<T> Downcast<T> for T

Source§

fn downcast(&self) -> &T

Source§

impl<S, T> Duplex<S> for T
where T: FromSample<S> + ToSample<S>,

Source§

impl<T> ErasedDestructor for T
where T: 'static,

Source§

impl<T> FmtForward for T

Source§

fn fmt_binary(self) -> FmtBinary<Self>
where Self: Binary,

Causes self to use its Binary implementation when Debug-formatted.
Source§

fn fmt_display(self) -> FmtDisplay<Self>
where Self: Display,

Causes self to use its Display implementation when Debug-formatted.
Source§

fn fmt_lower_exp(self) -> FmtLowerExp<Self>
where Self: LowerExp,

Causes self to use its LowerExp implementation when Debug-formatted.
Source§

fn fmt_lower_hex(self) -> FmtLowerHex<Self>
where Self: LowerHex,

Causes self to use its LowerHex implementation when Debug-formatted.
Source§

fn fmt_octal(self) -> FmtOctal<Self>
where Self: Octal,

Causes self to use its Octal implementation when Debug-formatted.
Source§

fn fmt_pointer(self) -> FmtPointer<Self>
where Self: Pointer,

Causes self to use its Pointer implementation when Debug-formatted.
Source§

fn fmt_upper_exp(self) -> FmtUpperExp<Self>
where Self: UpperExp,

Causes self to use its UpperExp implementation when Debug-formatted.
Source§

fn fmt_upper_hex(self) -> FmtUpperHex<Self>
where Self: UpperHex,

Causes self to use its UpperHex implementation when Debug-formatted.
Source§

fn fmt_list(self) -> FmtList<Self>
where &'a Self: for<'a> IntoIterator,

Formats each item in a sequence. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<S> FromSample<S> for S

Source§

fn from_sample_(s: S) -> S

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> MaybeSendSync for T

Source§

impl<D> OwoColorize for D

Source§

fn fg<C>(&self) -> FgColorDisplay<'_, C, Self>
where C: Color,

Set the foreground color generically Read more
Source§

fn bg<C>(&self) -> BgColorDisplay<'_, C, Self>
where C: Color,

Set the background color generically. Read more
Source§

fn black(&self) -> FgColorDisplay<'_, Black, Self>

Change the foreground color to black
Source§

fn on_black(&self) -> BgColorDisplay<'_, Black, Self>

Change the background color to black
Source§

fn red(&self) -> FgColorDisplay<'_, Red, Self>

Change the foreground color to red
Source§

fn on_red(&self) -> BgColorDisplay<'_, Red, Self>

Change the background color to red
Source§

fn green(&self) -> FgColorDisplay<'_, Green, Self>

Change the foreground color to green
Source§

fn on_green(&self) -> BgColorDisplay<'_, Green, Self>

Change the background color to green
Source§

fn yellow(&self) -> FgColorDisplay<'_, Yellow, Self>

Change the foreground color to yellow
Source§

fn on_yellow(&self) -> BgColorDisplay<'_, Yellow, Self>

Change the background color to yellow
Source§

fn blue(&self) -> FgColorDisplay<'_, Blue, Self>

Change the foreground color to blue
Source§

fn on_blue(&self) -> BgColorDisplay<'_, Blue, Self>

Change the background color to blue
Source§

fn magenta(&self) -> FgColorDisplay<'_, Magenta, Self>

Change the foreground color to magenta
Source§

fn on_magenta(&self) -> BgColorDisplay<'_, Magenta, Self>

Change the background color to magenta
Source§

fn purple(&self) -> FgColorDisplay<'_, Magenta, Self>

Change the foreground color to purple
Source§

fn on_purple(&self) -> BgColorDisplay<'_, Magenta, Self>

Change the background color to purple
Source§

fn cyan(&self) -> FgColorDisplay<'_, Cyan, Self>

Change the foreground color to cyan
Source§

fn on_cyan(&self) -> BgColorDisplay<'_, Cyan, Self>

Change the background color to cyan
Source§

fn white(&self) -> FgColorDisplay<'_, White, Self>

Change the foreground color to white
Source§

fn on_white(&self) -> BgColorDisplay<'_, White, Self>

Change the background color to white
Source§

fn default_color(&self) -> FgColorDisplay<'_, Default, Self>

Change the foreground color to the terminal default
Source§

fn on_default_color(&self) -> BgColorDisplay<'_, Default, Self>

Change the background color to the terminal default
Source§

fn bright_black(&self) -> FgColorDisplay<'_, BrightBlack, Self>

Change the foreground color to bright black
Source§

fn on_bright_black(&self) -> BgColorDisplay<'_, BrightBlack, Self>

Change the background color to bright black
Source§

fn bright_red(&self) -> FgColorDisplay<'_, BrightRed, Self>

Change the foreground color to bright red
Source§

fn on_bright_red(&self) -> BgColorDisplay<'_, BrightRed, Self>

Change the background color to bright red
Source§

fn bright_green(&self) -> FgColorDisplay<'_, BrightGreen, Self>

Change the foreground color to bright green
Source§

fn on_bright_green(&self) -> BgColorDisplay<'_, BrightGreen, Self>

Change the background color to bright green
Source§

fn bright_yellow(&self) -> FgColorDisplay<'_, BrightYellow, Self>

Change the foreground color to bright yellow
Source§

fn on_bright_yellow(&self) -> BgColorDisplay<'_, BrightYellow, Self>

Change the background color to bright yellow
Source§

fn bright_blue(&self) -> FgColorDisplay<'_, BrightBlue, Self>

Change the foreground color to bright blue
Source§

fn on_bright_blue(&self) -> BgColorDisplay<'_, BrightBlue, Self>

Change the background color to bright blue
Source§

fn bright_magenta(&self) -> FgColorDisplay<'_, BrightMagenta, Self>

Change the foreground color to bright magenta
Source§

fn on_bright_magenta(&self) -> BgColorDisplay<'_, BrightMagenta, Self>

Change the background color to bright magenta
Source§

fn bright_purple(&self) -> FgColorDisplay<'_, BrightMagenta, Self>

Change the foreground color to bright purple
Source§

fn on_bright_purple(&self) -> BgColorDisplay<'_, BrightMagenta, Self>

Change the background color to bright purple
Source§

fn bright_cyan(&self) -> FgColorDisplay<'_, BrightCyan, Self>

Change the foreground color to bright cyan
Source§

fn on_bright_cyan(&self) -> BgColorDisplay<'_, BrightCyan, Self>

Change the background color to bright cyan
Source§

fn bright_white(&self) -> FgColorDisplay<'_, BrightWhite, Self>

Change the foreground color to bright white
Source§

fn on_bright_white(&self) -> BgColorDisplay<'_, BrightWhite, Self>

Change the background color to bright white
Source§

fn bold(&self) -> BoldDisplay<'_, Self>

Make the text bold
Source§

fn dimmed(&self) -> DimDisplay<'_, Self>

Make the text dim
Source§

fn italic(&self) -> ItalicDisplay<'_, Self>

Make the text italicized
Source§

fn underline(&self) -> UnderlineDisplay<'_, Self>

Make the text underlined
Make the text blink
Make the text blink (but fast!)
Source§

fn reversed(&self) -> ReversedDisplay<'_, Self>

Swap the foreground and background colors
Source§

fn hidden(&self) -> HiddenDisplay<'_, Self>

Hide the text
Source§

fn strikethrough(&self) -> StrikeThroughDisplay<'_, Self>

Cross out the text
Source§

fn color<Color>(&self, color: Color) -> FgDynColorDisplay<'_, Color, Self>
where Color: DynColor,

Set the foreground color at runtime. Only use if you do not know which color will be used at compile-time. If the color is constant, use either OwoColorize::fg or a color-specific method, such as OwoColorize::green, Read more
Source§

fn on_color<Color>(&self, color: Color) -> BgDynColorDisplay<'_, Color, Self>
where Color: DynColor,

Set the background color at runtime. Only use if you do not know what color to use at compile-time. If the color is constant, use either OwoColorize::bg or a color-specific method, such as OwoColorize::on_yellow, Read more
Source§

fn fg_rgb<const R: u8, const G: u8, const B: u8>( &self, ) -> FgColorDisplay<'_, CustomColor<R, G, B>, Self>

Set the foreground color to a specific RGB value.
Source§

fn bg_rgb<const R: u8, const G: u8, const B: u8>( &self, ) -> BgColorDisplay<'_, CustomColor<R, G, B>, Self>

Set the background color to a specific RGB value.
Source§

fn truecolor(&self, r: u8, g: u8, b: u8) -> FgDynColorDisplay<'_, Rgb, Self>

Sets the foreground color to an RGB value.
Source§

fn on_truecolor(&self, r: u8, g: u8, b: u8) -> BgDynColorDisplay<'_, Rgb, Self>

Sets the background color to an RGB value.
Source§

fn style(&self, style: Style) -> Styled<&Self>

Apply a runtime-determined style
Source§

impl<T> Pipe for T
where T: ?Sized,

Source§

fn pipe<R>(self, func: impl FnOnce(Self) -> R) -> R
where Self: Sized,

Pipes by value. This is generally the method you want to use. Read more
Source§

fn pipe_ref<'a, R>(&'a self, func: impl FnOnce(&'a Self) -> R) -> R
where R: 'a,

Borrows self and passes that borrow into the pipe function. Read more
Source§

fn pipe_ref_mut<'a, R>(&'a mut self, func: impl FnOnce(&'a mut Self) -> R) -> R
where R: 'a,

Mutably borrows self and passes that borrow into the pipe function. Read more
Source§

fn pipe_borrow<'a, B, R>(&'a self, func: impl FnOnce(&'a B) -> R) -> R
where Self: Borrow<B>, B: 'a + ?Sized, R: 'a,

Borrows self, then passes self.borrow() into the pipe function. Read more
Source§

fn pipe_borrow_mut<'a, B, R>( &'a mut self, func: impl FnOnce(&'a mut B) -> R, ) -> R
where Self: BorrowMut<B>, B: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.borrow_mut() into the pipe function. Read more
Source§

fn pipe_as_ref<'a, U, R>(&'a self, func: impl FnOnce(&'a U) -> R) -> R
where Self: AsRef<U>, U: 'a + ?Sized, R: 'a,

Borrows self, then passes self.as_ref() into the pipe function.
Source§

fn pipe_as_mut<'a, U, R>(&'a mut self, func: impl FnOnce(&'a mut U) -> R) -> R
where Self: AsMut<U>, U: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.as_mut() into the pipe function.
Source§

fn pipe_deref<'a, T, R>(&'a self, func: impl FnOnce(&'a T) -> R) -> R
where Self: Deref<Target = T>, T: 'a + ?Sized, R: 'a,

Borrows self, then passes self.deref() into the pipe function.
Source§

fn pipe_deref_mut<'a, T, R>( &'a mut self, func: impl FnOnce(&'a mut T) -> R, ) -> R
where Self: DerefMut<Target = T> + Deref, T: 'a + ?Sized, R: 'a,

Mutably borrows self, then passes self.deref_mut() into the pipe function.
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Tap for T

Source§

fn tap(self, func: impl FnOnce(&Self)) -> Self

Immutable access to a value. Read more
Source§

fn tap_mut(self, func: impl FnOnce(&mut Self)) -> Self

Mutable access to a value. Read more
Source§

fn tap_borrow<B>(self, func: impl FnOnce(&B)) -> Self
where Self: Borrow<B>, B: ?Sized,

Immutable access to the Borrow<B> of a value. Read more
Source§

fn tap_borrow_mut<B>(self, func: impl FnOnce(&mut B)) -> Self
where Self: BorrowMut<B>, B: ?Sized,

Mutable access to the BorrowMut<B> of a value. Read more
Source§

fn tap_ref<R>(self, func: impl FnOnce(&R)) -> Self
where Self: AsRef<R>, R: ?Sized,

Immutable access to the AsRef<R> view of a value. Read more
Source§

fn tap_ref_mut<R>(self, func: impl FnOnce(&mut R)) -> Self
where Self: AsMut<R>, R: ?Sized,

Mutable access to the AsMut<R> view of a value. Read more
Source§

fn tap_deref<T>(self, func: impl FnOnce(&T)) -> Self
where Self: Deref<Target = T>, T: ?Sized,

Immutable access to the Deref::Target of a value. Read more
Source§

fn tap_deref_mut<T>(self, func: impl FnOnce(&mut T)) -> Self
where Self: DerefMut<Target = T> + Deref, T: ?Sized,

Mutable access to the Deref::Target of a value. Read more
Source§

fn tap_dbg(self, func: impl FnOnce(&Self)) -> Self

Calls .tap() only in debug builds, and is erased in release builds.
Source§

fn tap_mut_dbg(self, func: impl FnOnce(&mut Self)) -> Self

Calls .tap_mut() only in debug builds, and is erased in release builds.
Source§

fn tap_borrow_dbg<B>(self, func: impl FnOnce(&B)) -> Self
where Self: Borrow<B>, B: ?Sized,

Calls .tap_borrow() only in debug builds, and is erased in release builds.
Source§

fn tap_borrow_mut_dbg<B>(self, func: impl FnOnce(&mut B)) -> Self
where Self: BorrowMut<B>, B: ?Sized,

Calls .tap_borrow_mut() only in debug builds, and is erased in release builds.
Source§

fn tap_ref_dbg<R>(self, func: impl FnOnce(&R)) -> Self
where Self: AsRef<R>, R: ?Sized,

Calls .tap_ref() only in debug builds, and is erased in release builds.
Source§

fn tap_ref_mut_dbg<R>(self, func: impl FnOnce(&mut R)) -> Self
where Self: AsMut<R>, R: ?Sized,

Calls .tap_ref_mut() only in debug builds, and is erased in release builds.
Source§

fn tap_deref_dbg<T>(self, func: impl FnOnce(&T)) -> Self
where Self: Deref<Target = T>, T: ?Sized,

Calls .tap_deref() only in debug builds, and is erased in release builds.
Source§

fn tap_deref_mut_dbg<T>(self, func: impl FnOnce(&mut T)) -> Self
where Self: DerefMut<Target = T> + Deref, T: ?Sized,

Calls .tap_deref_mut() only in debug builds, and is erased in release builds.
Source§

impl<T, U> ToSample<U> for T
where U: FromSample<T>,

Source§

fn to_sample_(self) -> U

Source§

impl<T> TryConv for T

Source§

fn try_conv<T>(self) -> Result<T, Self::Error>
where Self: TryInto<T>,

Attempts to convert self into T using TryInto<T>. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> Upcast<T> for T

Source§

fn upcast(&self) -> Option<&T>

Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more