macros: an errand arrives inside the building, and a building already entered has none
Two rules for section 13's errands, both from row 54's `GO HEAL`: 204 starts at a mean net of 0.0 tiles and a mean reach of 0.0, cycling with `GO ROUTE` and `GO FRONTIER` over five tiles for seven and a half brain minutes. An errand's aim at a door carries no press -- the warp fires when it is stepped on -- so an aim on the tile the fly is already standing on settles for `SETTLE_FRAMES` and reports `done` with the world exactly as it was. A completed errand walk writes the reached ledger, which `goals_toward` does not filter, so the same button was dealt on the next hold and the same nothing happened again. `exit_goals` has excluded a settled goal underfoot since row 13; this is the one walk that did not have the rule. And the errand ledger is session state, so a restore re-armed every errand in the town and walked the fly back to a counter it had already used. `map_visited` is the adapter's lifetime answer to the same question and it does survive, so both are asked and either pays the errand.
This commit is contained in:
parent
895fb52cda
commit
611fe9e108
2 changed files with 76 additions and 2 deletions
|
|
@ -868,7 +868,17 @@ pub fn errand(state: &mut dyn MacroState, kind: Amenity) -> Option<u8> {
|
|||
if kind == Amenity::Mart && state.money() < CHEAPEST_PURCHASE {
|
||||
return None;
|
||||
}
|
||||
geography::amenity_of(area, kind)
|
||||
let map = geography::amenity_of(area, kind)?;
|
||||
// **A building this run has already been inside is an errand already discharged.** The session
|
||||
// ledger above is the errand's own record and it is the one that can be missing: it is written
|
||||
// from the frame the fly stands on the building's map, so a restore starts with it empty and
|
||||
// the run walks back to a counter it has already used. [`MacroState::map_visited`] is the
|
||||
// adapter's lifetime answer to the same question and it does survive, so the two together are
|
||||
// "has this run been in there", asked twice (`infra/docs/macros-traps.md` row 54).
|
||||
if state.map_visited(map) {
|
||||
return None;
|
||||
}
|
||||
Some(map)
|
||||
}
|
||||
|
||||
/// The errand `GO OBJECTIVE` puts *ahead* of the rung's place, when this area has one.
|
||||
|
|
@ -930,7 +940,21 @@ pub fn amenity_goals(state: &mut dyn MacroState, kind: Amenity) -> Vec<Aim> {
|
|||
return counter_aims(state, counter_sprite(kind));
|
||||
}
|
||||
match errand(state, kind) {
|
||||
Some(map) => goals_toward(state, map),
|
||||
Some(map) => {
|
||||
let here = state.player().map(|player| Tile::new(player.x, player.y));
|
||||
goals_toward(state, map)
|
||||
.into_iter()
|
||||
// **An errand arrives inside the building, never on the doormat outside it**
|
||||
// (section 12.2's rule, row 54). An aim with no press settles where it stands, so
|
||||
// an aim on the tile the fly is already on is `Done` in `SETTLE_FRAMES` with the
|
||||
// world exactly as it was -- and a completed errand walk writes the reached ledger,
|
||||
// so the same button is dealt on the next hold and the same nothing happens again:
|
||||
// `GO HEAL` 204 starts at a mean net of 0.0 tiles and a mean reach of 0.0. The same
|
||||
// exclusion [`super::executor::exit_goals`] has made since row 13, for the same
|
||||
// reason, on the one walk that did not have it.
|
||||
.filter(|aim| aim.press.is_some() || Some(aim.tile) != here)
|
||||
.collect()
|
||||
}
|
||||
None => Vec::new(),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3970,6 +3970,56 @@ fn go_shop_and_go_heal_are_on_the_pad_while_their_errand_stands() {
|
|||
assert!(!on_the_pad(&mut pallet, MacroKind::GoHeal));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_errand_does_not_settle_on_the_doormat_it_is_standing_on() {
|
||||
// Row 54 of `infra/docs/macros-traps.md`, and section 12.2's rule: "a macro that completes
|
||||
// without moving because its precondition is already satisfied where the fly stands is a
|
||||
// trap". An errand's aim at a door carries no press -- the warp fires when it is stepped on --
|
||||
// so an aim on the tile the fly is already on settles for `SETTLE_FRAMES` and reports `done`
|
||||
// with the world exactly as it was. A completed errand walk writes the reached ledger, which
|
||||
// `goals_toward` does not filter, so the same button was dealt on the next hold and the same
|
||||
// nothing happened again: `GO HEAL` 204 starts at a mean net of 0.0 tiles and a mean reach of
|
||||
// 0.0, in a cycle with `GO ROUTE` and `GO FRONTIER` over five tiles.
|
||||
let mut world = viridian();
|
||||
world.player = Tile::new(6, 1);
|
||||
assert!(
|
||||
amenity_goals(&mut world, Amenity::Center).is_empty(),
|
||||
"the centre's own doormat is not somewhere to walk to"
|
||||
);
|
||||
assert!(!on_the_pad(&mut world, MacroKind::GoHeal), "so the button is not on the pad");
|
||||
|
||||
// The other errand is a tile away and untouched: this excludes one aim, not the walk.
|
||||
assert_eq!(
|
||||
amenity_goals(&mut world, Amenity::Mart).iter().map(|aim| aim.tile).collect::<Vec<_>>(),
|
||||
vec![Tile::new(1, 1)]
|
||||
);
|
||||
assert!(on_the_pad(&mut world, MacroKind::GoShop));
|
||||
|
||||
// And one tile off the doormat the centre is a walk again.
|
||||
world.player = Tile::new(6, 2);
|
||||
assert_eq!(
|
||||
amenity_goals(&mut world, Amenity::Center).iter().map(|aim| aim.tile).collect::<Vec<_>>(),
|
||||
vec![Tile::new(6, 1)]
|
||||
);
|
||||
assert!(on_the_pad(&mut world, MacroKind::GoHeal));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn an_errand_is_paid_by_a_building_this_run_has_already_been_inside() {
|
||||
// The errand ledger is session state and the adapter's map ledger is not, so a restored run
|
||||
// re-armed every errand in the town and walked back to a counter it had already used
|
||||
// (`docs/design/macros.md` section 13's own residual). Asking both is asking "has this run
|
||||
// been in there" twice, and either answer pays the errand.
|
||||
let mut world = viridian();
|
||||
assert_eq!(errand(&mut world, Amenity::Center), Some(maps::VIRIDIAN_POKECENTER));
|
||||
world.seen_maps.insert(maps::VIRIDIAN_POKECENTER);
|
||||
assert_eq!(errand(&mut world, Amenity::Center), None, "already been inside it");
|
||||
assert!(!on_the_pad(&mut world, MacroKind::GoHeal));
|
||||
// The mart is a different building and a different errand.
|
||||
assert_eq!(errand(&mut world, Amenity::Mart), Some(maps::VIRIDIAN_MART));
|
||||
assert!(on_the_pad(&mut world, MacroKind::GoShop));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn go_shop_walks_to_the_marts_door_and_then_to_the_counter() {
|
||||
// Outside: the goal is the door, and it is the warp's own tile.
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue