a NO inside a conversation declines nothing, so it does not un-arm the talk
The other half of row 56. The Pewter Gym guide's conversation is fifty-two boxes long and a third of the presses that walk it are `NO`, whose B advances a plain box exactly as `NEXT`'s A does. Every one of them cleared the pending `TALK`, so the talked ledger never learned the conversation had happened, `TALK` stayed on the overworld pad, and an A press at him reopened the whole ring: thirty brain minutes of scene `dialog` with no walk macro dealt. 12.4's rule -- "the fly said no, so the thing is still on offer" -- is about a declined *offer*, and which of the two a `NO` was is decided where it can be seen: by whether the box closes on it. So the decision moves to the frame the text goes away, which is where the talked entry is written anyway, and the reading is `pending_answer`: armed only by an answer to a prompt this crate can read, alive for one hold. A declining `NO` still standing there is a `NO` the box closed on; anything else is a conversation walked through to its end.
This commit is contained in:
parent
5f9237efcd
commit
886509b3b5
2 changed files with 79 additions and 13 deletions
|
|
@ -959,10 +959,26 @@ impl MacroMachine {
|
||||||
// the text is gone.
|
// the text is gone.
|
||||||
if class(state.scene()) != Class::Talking {
|
if class(state.scene()) != Class::Talking {
|
||||||
self.pending_talk = None;
|
self.pending_talk = None;
|
||||||
self.talked = Some((pending.map, pending.target));
|
// A conversation the fly *declined its way out of* is not a conversation it has had:
|
||||||
|
// whatever it said no to is still on offer, which is section 12.4's rule and the one
|
||||||
|
// 12.12 inverts for the nurse alone. The box closing is what tells that apart from a
|
||||||
|
// `NO` pressed dozens of boxes deep, and [`MacroMachine::pending_answer`] is the
|
||||||
|
// reading: it is armed only by an answer to a prompt this crate can read and it lives
|
||||||
|
// for one hold, so a declining `NO` still standing here is a `NO` this box closed on.
|
||||||
|
if !self.declined_out_of(pending.map) {
|
||||||
|
self.talked = Some((pending.map, pending.target));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether the answer still standing on `map` is a `NO` to a readable prompt: a declined offer
|
||||||
|
/// rather than a conversation walked through (section 12.20).
|
||||||
|
fn declined_out_of(&self, map: u8) -> bool {
|
||||||
|
self.pending_answer.is_some_and(|pending| {
|
||||||
|
pending.answered.map == map && pending.answered.prompt && !pending.answered.yes
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// One frame after a `YES` or `NO`: decide whether the box it answered has come straight back.
|
/// One frame after a `YES` or `NO`: decide whether the box it answered has come straight back.
|
||||||
///
|
///
|
||||||
/// `docs/design/macros.md` section 12.12. The evidence is all in one frame: the fly is on the
|
/// `docs/design/macros.md` section 12.12. The evidence is all in one frame: the fly is on the
|
||||||
|
|
@ -1099,11 +1115,12 @@ impl MacroMachine {
|
||||||
{
|
{
|
||||||
self.pending_talk = Some(PendingTalk { map, target, at });
|
self.pending_talk = Some(PendingTalk { map, target, at });
|
||||||
}
|
}
|
||||||
// The fly said no. Whatever it said no to is still on offer, so the thing it
|
// The fly said no, and whether that retires what it was facing is decided
|
||||||
// was facing is not retired.
|
// when the box closes rather than here (section 12.20). A `NO` inside a
|
||||||
if active.kind == MacroKind::No {
|
// conversation is the B that advances a plain box -- it declines nothing --
|
||||||
self.pending_talk = None;
|
// and clearing the pending talk on it kept the Pewter Gym guide `untalked`
|
||||||
}
|
// for thirty brain minutes: his conversation is fifty-two boxes long and
|
||||||
|
// about a third of the presses that walk it are `NO`.
|
||||||
// An answer, and the box it answered: armed so that the same prompt coming
|
// An answer, and the box it answered: armed so that the same prompt coming
|
||||||
// straight back is recorded (section 12.12), and the nurse written into the
|
// straight back is recorded (section 12.12), and the nurse written into the
|
||||||
// talked ledger when what was declined was *her* offer. That is 12.4's rule
|
// talked ledger when what was declined was *her* offer. That is 12.4's rule
|
||||||
|
|
|
||||||
|
|
@ -3410,9 +3410,12 @@ fn a_conversation_that_walks_the_fly_off_its_tile_is_not_talked_to() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn a_fly_that_answers_no_has_not_talked_to_anything() {
|
fn a_fly_that_declines_an_offer_has_not_talked_to_anything() {
|
||||||
// The catching tutorial's own shape: a yes/no box, and `NO` is a real answer the pad has to be
|
// The catching tutorial's own shape: a yes/no box, and `NO` is a real answer the pad has to be
|
||||||
// able to give. What it must not do is retire the thing that asked -- the offer stands.
|
// able to give. What it must not do is retire the thing that asked -- the offer stands.
|
||||||
|
//
|
||||||
|
// Since row 56 the box has to be a **readable prompt** for that to be the reading: a `NO` on a
|
||||||
|
// plain text box declines nothing, and the test below is the other half.
|
||||||
let mut world = World::room().at(3, 3);
|
let mut world = World::room().at(3, 3);
|
||||||
world.facing = Facing::Down;
|
world.facing = Facing::Down;
|
||||||
world.npcs = vec![Npc { slot: 4, picture: 1, x: 3, y: 4, facing: Facing::Up }];
|
world.npcs = vec![Npc { slot: 4, picture: 1, x: 3, y: 4, facing: Facing::Up }];
|
||||||
|
|
@ -3422,23 +3425,69 @@ fn a_fly_that_answers_no_has_not_talked_to_anything() {
|
||||||
while machine.step(&mut world).is_some() {
|
while machine.step(&mut world).is_some() {
|
||||||
world.frame(buttons::NONE);
|
world.frame(buttons::NONE);
|
||||||
}
|
}
|
||||||
// The box is open, so the scene is a dialog and the pad is the dialog's.
|
// The box is open and it is the choice, so the scene is a dialog and the pad is its answers.
|
||||||
world.scene = Scene::Dialog;
|
world.scene = Scene::Dialog;
|
||||||
|
world.prompt = true;
|
||||||
let (dialog, no) = pick(&mut world, MacroKind::No);
|
let (dialog, no) = pick(&mut world, MacroKind::No);
|
||||||
// Both answers are on the pad, whatever the box is: there is no WRAM observable for "a choice
|
assert_eq!(names(&dialog), ["YES", "NO"], "a readable choice deals its own answers (12.12)");
|
||||||
// is open" (section 12.2, row 15).
|
|
||||||
assert!(names(&dialog).contains(&"YES"), "{:?}", names(&dialog));
|
|
||||||
assert!(names(&dialog).contains(&"NO"), "{:?}", names(&dialog));
|
|
||||||
machine.start(&dialog, no, &mut world).expect("NO is bound in a dialog");
|
machine.start(&dialog, no, &mut world).expect("NO is bound in a dialog");
|
||||||
while machine.step(&mut world).is_some() {
|
while machine.step(&mut world).is_some() {
|
||||||
world.frame(buttons::NONE);
|
world.frame(buttons::NONE);
|
||||||
}
|
}
|
||||||
world.scene = Scene::Overworld;
|
world.scene = Scene::Overworld;
|
||||||
machine.observe_frame(&mut world);
|
machine.observe_frame(&mut world);
|
||||||
assert_eq!(machine.take_talked(), None, "a no is not a conversation had");
|
assert_eq!(machine.take_talked(), None, "a declined offer is not a conversation had");
|
||||||
assert!(on_the_pad(&mut world, MacroKind::Talk), "the offer stands");
|
assert!(on_the_pad(&mut world, MacroKind::Talk), "the offer stands");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn a_no_pressed_inside_a_conversation_is_not_a_declined_offer() {
|
||||||
|
// Row 56, the Pewter Gym guide. His conversation is fifty-two boxes long and `NO`'s B advances
|
||||||
|
// a plain one exactly as `NEXT`'s A does -- it declines nothing. Clearing the pending `TALK` on
|
||||||
|
// it meant the talked ledger never learned the conversation had happened, so `TALK` was on the
|
||||||
|
// overworld pad every hold, and an A press at him reopened the whole ring: thirty brain
|
||||||
|
// minutes of scene `dialog` with no walk macro dealt at all.
|
||||||
|
//
|
||||||
|
// Which of the two a `NO` was is decided where it can be seen: by whether the box closes on it.
|
||||||
|
let mut world = World::room().at(3, 3);
|
||||||
|
world.facing = Facing::Down;
|
||||||
|
world.npcs = vec![Npc { slot: 4, picture: 1, x: 3, y: 4, facing: Facing::Up }];
|
||||||
|
let mut machine = MacroMachine::new(1);
|
||||||
|
let (palette, slot) = pick(&mut world, MacroKind::Talk);
|
||||||
|
machine.start(&palette, slot, &mut world).expect("TALK is bound at a person");
|
||||||
|
while machine.step(&mut world).is_some() {
|
||||||
|
world.frame(buttons::NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deep inside the conversation: a plain text box, not a choice.
|
||||||
|
world.scene = Scene::Dialog;
|
||||||
|
world.prompt = false;
|
||||||
|
let (dialog, no) = pick(&mut world, MacroKind::No);
|
||||||
|
assert!(names(&dialog).contains(&"NEXT"), "a plain box deals all three: {:?}", names(&dialog));
|
||||||
|
machine.start(&dialog, no, &mut world).expect("NO is bound in a dialog");
|
||||||
|
while machine.step(&mut world).is_some() {
|
||||||
|
world.frame(buttons::NONE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The box is still open, so nothing is decided yet -- the conversation is still running.
|
||||||
|
machine.observe_frame(&mut world);
|
||||||
|
assert_eq!(machine.take_talked(), None, "the box is still open");
|
||||||
|
|
||||||
|
// And when the text is gone the conversation counts, which is what shuts the ring's door.
|
||||||
|
world.scene = Scene::Overworld;
|
||||||
|
machine.observe_frame(&mut world);
|
||||||
|
assert_eq!(
|
||||||
|
machine.take_talked(),
|
||||||
|
Some((world.map, TalkTarget::Sprite(4))),
|
||||||
|
"a conversation walked through to its end is a conversation had"
|
||||||
|
);
|
||||||
|
world.talked.insert(TalkTarget::Sprite(4));
|
||||||
|
assert!(
|
||||||
|
!on_the_pad(&mut world, MacroKind::Talk),
|
||||||
|
"`TALK` is the ring's door and this run has been through it"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn a_walk_the_cartridge_pushes_back_excludes_what_it_was_walking_to() {
|
fn a_walk_the_cartridge_pushes_back_excludes_what_it_was_walking_to() {
|
||||||
// Row 28's other half. Every macro that walked into the gate ended `Done` -- a scene change,
|
// Row 28's other half. Every macro that walked into the gate ended `Done` -- a scene change,
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue