macros: an UNKNOWN frame with nothing drawn on it deals no buttons
BACK was 678 macro starts in 47 minutes on rung 10, 189 of them on Pewter City with no text box on screen at all. BACK is on no overworld and no dialog pad, so every one of them was dealt by Scene::Unknown -- which holds two states under one name. One is a screen this crate cannot name, the Pokedex or the trainer card or OPTION, where A and B are what leave it. The other is a frame of the overworld where the cartridge is driving -- a warp in flight, a scripted push-back, the museum guide walking the fly through the door -- which the detector calls Unknown because the buttons are not reaching the player. On the second, NEXT and BACK are an A and a B pressed into somebody else's script: they change nothing and they complete where the fly stands, which is section 12.2's trap with no box to advance. So Unknown's pad is dealt on whether a box is drawn, and a scripted overworld frame is an empty pad the fly waits out -- the cartridge gives the buttons back by itself.
This commit is contained in:
parent
a389734201
commit
84ed0410bf
4 changed files with 83 additions and 2 deletions
|
|
@ -465,6 +465,22 @@ pub trait MacroState: GameState {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether a text box is open at all: `wFontLoaded`'s bit, and nothing drawn.
|
||||||
|
///
|
||||||
|
/// The one thing that tells a screen with words on it from a frame of the overworld the
|
||||||
|
/// cartridge happens to be driving, and [`super::palette::scene_set`] deals
|
||||||
|
/// [`Scene::Unknown`]'s pad on it (**section 12.13**). `Unknown` is two different states
|
||||||
|
/// wearing one name: a screen this crate cannot name -- the Pokedex, the trainer card,
|
||||||
|
/// OPTION -- where `NEXT` and `BACK` are the A and B that leave it; and a *scripted* overworld
|
||||||
|
/// frame, where `scene::detect` falls through to `Unknown` because the buttons are not
|
||||||
|
/// reaching the player, and where an A or a B press is a press into somebody else's script.
|
||||||
|
///
|
||||||
|
/// The default is `false`, which narrows: with no reading, `Unknown` deals nothing and the
|
||||||
|
/// fly waits, which is what the doctrine says a scene with nothing to press does.
|
||||||
|
fn text_open(&mut self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
/// Whether the box on screen is the two-option YES/NO prompt rather than a plain text box.
|
/// Whether the box on screen is the two-option YES/NO prompt rather than a plain text box.
|
||||||
///
|
///
|
||||||
/// `pokemon_red::state::yes_no_prompt`: the border `DisplayTwoOptionMenu` draws plus the
|
/// `pokemon_red::state::yes_no_prompt`: the border `DisplayTwoOptionMenu` draws plus the
|
||||||
|
|
|
||||||
|
|
@ -476,7 +476,26 @@ pub fn scene_set(scene: Scene, state: &mut dyn MacroState) -> Vec<MacroKind> {
|
||||||
// Section 2 treats `Unknown` like a dialog -- advance only -- and `BACK` is the other half
|
// Section 2 treats `Unknown` like a dialog -- advance only -- and `BACK` is the other half
|
||||||
// of advancing, because `Unknown` is also where the Pokédex, the trainer card and OPTION
|
// of advancing, because `Unknown` is also where the Pokédex, the trainer card and OPTION
|
||||||
// land (`docs/design/macros-wram.md`) and B is what leaves all three.
|
// land (`docs/design/macros-wram.md`) and B is what leaves all three.
|
||||||
Scene::Unknown => vec![Next, Back],
|
//
|
||||||
|
// **Only while there is something on screen with words in it** (section 12.13, the
|
||||||
|
// rung-10 Pewter loop). `Unknown` is the detector's residue and it holds two states, not
|
||||||
|
// one: a screen this crate cannot name, where A and B are what leave it, and a frame of
|
||||||
|
// the *overworld* where the cartridge is driving -- a warp in flight, a scripted
|
||||||
|
// push-back, the museum guide walking the fly through the door -- which
|
||||||
|
// `scene::detect` calls `Unknown` because the buttons are not reaching the player. On the
|
||||||
|
// second, `NEXT` and `BACK` are an A and a B pressed into somebody else's script: they
|
||||||
|
// change nothing, they complete where the fly stands, and they are section 12.2's trap
|
||||||
|
// with no text box to advance. Measured live on rung 10: `BACK` **678** macro starts in
|
||||||
|
// 47 minutes, 189 of them on map `0x02` with no box on screen at all. So the pad is
|
||||||
|
// empty there and the fly waits, which is the doctrine's own answer for a scene with
|
||||||
|
// nothing sensible to press -- and the cartridge gives the buttons back by itself.
|
||||||
|
Scene::Unknown => {
|
||||||
|
if state.text_open() {
|
||||||
|
vec![Next, Back]
|
||||||
|
} else {
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
// `NEXT`, `YES`, `NO` for a plain box -- A and B both advance one, and what the three
|
// `NEXT`, `YES`, `NO` for a plain box -- A and B both advance one, and what the three
|
||||||
// buy is the fly being *able* to answer no. On the one box that **is** a choice, the pad
|
// buy is the fly being *able* to answer no. On the one box that **is** a choice, the pad
|
||||||
// is the choice's own answers: section 12.12.
|
// is the choice's own answers: section 12.12.
|
||||||
|
|
|
||||||
|
|
@ -164,6 +164,13 @@ struct World {
|
||||||
/// A frame at which the cartridge heals the party, which is what a Pokémon Center does while
|
/// A frame at which the cartridge heals the party, which is what a Pokémon Center does while
|
||||||
/// its text box is open (`docs/design/macros.md` section 13).
|
/// its text box is open (`docs/design/macros.md` section 13).
|
||||||
heal_at: Option<u32>,
|
heal_at: Option<u32>,
|
||||||
|
/// Whether a text box is drawn on a scene that is not [`Scene::Dialog`]
|
||||||
|
/// ([`MacroState::text_open`]).
|
||||||
|
///
|
||||||
|
/// `Dialog` *is* an open box, so the reading is true there by construction; the field is for
|
||||||
|
/// `Unknown`, which holds both a screen with words on it -- the Pokedex, the trainer card,
|
||||||
|
/// OPTION -- and a frame of the overworld the cartridge is driving (section 12.13).
|
||||||
|
box_open: bool,
|
||||||
/// Whether the two-option YES/NO box is the thing on screen ([`MacroState::yes_no_prompt`]).
|
/// Whether the two-option YES/NO box is the thing on screen ([`MacroState::yes_no_prompt`]).
|
||||||
///
|
///
|
||||||
/// A field rather than a shape of the `list`, because on the cartridge it is a *drawn box*
|
/// A field rather than a shape of the `list`, because on the cartridge it is a *drawn box*
|
||||||
|
|
@ -243,6 +250,7 @@ impl World {
|
||||||
pending: None,
|
pending: None,
|
||||||
switch: None,
|
switch: None,
|
||||||
heal_at: None,
|
heal_at: None,
|
||||||
|
box_open: false,
|
||||||
prompt: false,
|
prompt: false,
|
||||||
scripted: false,
|
scripted: false,
|
||||||
scripted_at: None,
|
scripted_at: None,
|
||||||
|
|
@ -658,6 +666,13 @@ impl MacroState for World {
|
||||||
self.scripted
|
self.scripted
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `wFontLoaded` is set for every dialogue box, which is what `Dialog` is; on `Unknown` the
|
||||||
|
/// fixture has to say, because that is the reading that tells a screen from a scripted
|
||||||
|
/// overworld frame (section 12.13).
|
||||||
|
fn text_open(&mut self) -> bool {
|
||||||
|
self.scene == Scene::Dialog || self.box_open
|
||||||
|
}
|
||||||
|
|
||||||
/// A drawn box is what the reading rests on, so a prompt cannot be open with no box open:
|
/// A drawn box is what the reading rests on, so a prompt cannot be open with no box open:
|
||||||
/// `pokemon_red::state::yes_no_prompt` gates on `wFontLoaded` before it looks at the tiles.
|
/// `pokemon_red::state::yes_no_prompt` gates on `wFontLoaded` before it looks at the tiles.
|
||||||
fn yes_no_prompt(&mut self) -> bool {
|
fn yes_no_prompt(&mut self) -> bool {
|
||||||
|
|
@ -990,12 +1005,31 @@ fn the_dialog_row_is_next_yes_and_no() {
|
||||||
#[test]
|
#[test]
|
||||||
fn an_unknown_scene_is_dialog_with_advance_only() {
|
fn an_unknown_scene_is_dialog_with_advance_only() {
|
||||||
let mut world = World::room();
|
let mut world = World::room();
|
||||||
|
world.box_open = true;
|
||||||
let palette = Palette::for_scene(Scene::Unknown, &mut world);
|
let palette = Palette::for_scene(Scene::Unknown, &mut world);
|
||||||
// Row 9 of `infra/docs/macros-traps.md`, closed by section 13.1: B is what leaves the Pokédex,
|
// Row 9 of `infra/docs/macros-traps.md`, closed by section 13.1: B is what leaves the Pokédex,
|
||||||
// the trainer card and OPTION, and all three read `Unknown`.
|
// the trainer card and OPTION, and all three read `Unknown` with a box drawn.
|
||||||
assert_eq!(names(&palette), ["NEXT", "BACK"]);
|
assert_eq!(names(&palette), ["NEXT", "BACK"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn an_unknown_frame_with_no_box_on_it_deals_nothing() {
|
||||||
|
// Section 12.13, the rung-10 Pewter loop. The other half of `Unknown` is the overworld with
|
||||||
|
// the cartridge driving -- a warp in flight, a push-back, the museum guide walking the fly in
|
||||||
|
// -- where `scene::detect` falls through because the buttons are not reaching the player.
|
||||||
|
// There is no box to advance and no screen to leave, so `NEXT` and `BACK` are an A and a B
|
||||||
|
// pressed into somebody else's script: they change nothing and they complete where the fly
|
||||||
|
// stands, which is section 12.2's trap. The pad is empty and the fly waits.
|
||||||
|
let mut world = World::room();
|
||||||
|
world.scripted = true;
|
||||||
|
assert!(!world.text_open());
|
||||||
|
let palette = Palette::for_scene(Scene::Unknown, &mut world);
|
||||||
|
assert_eq!(palette.bound(), 0, "an A and a B into a script are not buttons");
|
||||||
|
// And the moment the cartridge draws something, both are back.
|
||||||
|
world.box_open = true;
|
||||||
|
assert_eq!(names(&Palette::for_scene(Scene::Unknown, &mut world)), ["NEXT", "BACK"]);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn the_menu_row_is_close_confirm_back() {
|
fn the_menu_row_is_close_confirm_back() {
|
||||||
let mut world = World::room();
|
let mut world = World::room();
|
||||||
|
|
@ -2525,6 +2559,7 @@ fn a_forced_switch_plans_one_entry_and_the_other_scenes_plan_their_one_move() {
|
||||||
] {
|
] {
|
||||||
let mut world = World::room();
|
let mut world = World::room();
|
||||||
world.scene = scene;
|
world.scene = scene;
|
||||||
|
world.box_open = scene == Scene::Unknown;
|
||||||
assert_eq!(plan(&mut world), entries, "{}", scene.label());
|
assert_eq!(plan(&mut world), entries, "{}", scene.label());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3139,6 +3174,10 @@ fn no_playable_scene_deals_an_empty_pad() {
|
||||||
] {
|
] {
|
||||||
let mut world = World::room();
|
let mut world = World::room();
|
||||||
world.scene = scene;
|
world.scene = scene;
|
||||||
|
// `Unknown` is dealt on what is drawn (section 12.13): a screen with words on it has
|
||||||
|
// `NEXT` and `BACK`, and a scripted overworld frame is the one deliberate empty pad,
|
||||||
|
// which `an_unknown_frame_with_no_box_on_it_deals_nothing` is about.
|
||||||
|
world.box_open = scene == Scene::Unknown;
|
||||||
world.battle = matches!(scene, Scene::Battle { .. })
|
world.battle = matches!(scene, Scene::Battle { .. })
|
||||||
.then_some((BattleKind::Wild, false, false));
|
.then_some((BattleKind::Wild, false, false));
|
||||||
assert!(
|
assert!(
|
||||||
|
|
@ -4087,6 +4126,9 @@ fn no_playable_scene_and_no_sub_state_deals_an_empty_pad() {
|
||||||
] {
|
] {
|
||||||
let mut world = World::room();
|
let mut world = World::room();
|
||||||
world.scene = scene;
|
world.scene = scene;
|
||||||
|
// See the sweep in `no_playable_scene_deals_an_empty_pad`: `Unknown` with nothing drawn
|
||||||
|
// on it is the overworld being driven by the cartridge, and its pad is empty by design.
|
||||||
|
world.box_open = scene == Scene::Unknown;
|
||||||
world.mons.clear();
|
world.mons.clear();
|
||||||
worst(&mut world);
|
worst(&mut world);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1342,6 +1342,10 @@ impl MacroState for PokeState<'_> {
|
||||||
!controllable(self.memory)
|
!controllable(self.memory)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn text_open(&mut self) -> bool {
|
||||||
|
text_box(self.memory).open
|
||||||
|
}
|
||||||
|
|
||||||
fn yes_no_prompt(&mut self) -> bool {
|
fn yes_no_prompt(&mut self) -> bool {
|
||||||
yes_no_prompt(self.memory)
|
yes_no_prompt(self.memory)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue