Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions crates/engine/src/game/effects/counters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1977,6 +1977,23 @@ fn resolve_defined_or_targets(
.collect();
}

// CR 608.2c: A `ParentTarget` in a chained counter effect refers to the
// object chosen by the parent instruction. Prefer that propagated object
// target over the triggering event context; for a landfall trigger, the
// latter is the entering land rather than the creature chosen by the
// player. The object guard deliberately excludes the chooser-only target
// bookkeeping used by `ChooseOneOf` branches above.
if matches!(target_spec, Some(TargetFilter::ParentTarget)) && has_object_target {
return ability
.live_object_targets(state)
.into_iter()
.filter_map(|target| match target {
TargetRef::Object(id) => Some(id),
TargetRef::Player(_) => None,
})
.collect();
}

if let Some(filter) = target_spec {
let event_targets =
crate::game::targeting::resolve_event_context_targets(state, filter, ability.source_id);
Expand Down
1 change: 1 addition & 0 deletions crates/engine/tests/integration/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -896,6 +896,7 @@ mod scarab_god_regression;
mod scholarship_sponsor;
mod screaming_nemesis_combat_damage_multi_trigger;
mod screaming_nemesis_life_lock;
mod scythecat_cub_second_resolution;
mod search_delivery_observer_dedup;
mod season_points_budget_modal;
mod seasoned_dungeoneer_initiative_room_trigger;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
//! Scythecat Cub doubles the counters placed by its second landfall resolution.

use engine::game::scenario::{GameScenario, P0};
use engine::types::ability::TargetRef;
use engine::types::actions::GameAction;
use engine::types::counter::CounterType;
use engine::types::game_state::WaitingFor;
use engine::types::phase::Phase;

const SCYTHECAT_CUB_ORACLE: &str = "Trample\n\
Landfall — Whenever a land you control enters, put a +1/+1 counter on target creature you control. If this is the second time this ability has resolved this turn, double the number of +1/+1 counters on that creature instead.";

fn resolve_cub_landfall(
runner: &mut engine::game::scenario::GameRunner,
land_id: engine::types::identifiers::ObjectId,
target_id: engine::types::identifiers::ObjectId,
) {
let card_id = runner.state().objects[&land_id].card_id;
runner
.act(GameAction::PlayLand {
object_id: land_id,
card_id,
})
.expect("land should be playable");

match &runner.state().waiting_for {
WaitingFor::TriggerTargetSelection { target_slots, .. } => {
assert!(
target_slots[0]
.legal_targets
.iter()
.any(|target| matches!(target, TargetRef::Object(id) if *id == target_id)),
"Lotus Cobra must be a legal Scythecat Cub target"
);
}
other => panic!("expected Scythecat Cub target selection, got {other:?}"),
}

runner
.act(GameAction::ChooseTarget {
target: Some(TargetRef::Object(target_id)),
})
.expect("Lotus Cobra should be selectable");
runner.advance_until_stack_empty();
}

#[test]
fn scythecat_cub_doubles_counters_on_second_landfall_resolution() {
let mut scenario = GameScenario::new();
scenario.at_phase(Phase::PreCombatMain);

let cub_id = scenario
.add_creature_from_oracle(P0, "Scythecat Cub", 2, 2, SCYTHECAT_CUB_ORACLE)
.id();
let cobra_id = scenario.add_creature(P0, "Lotus Cobra", 2, 1).id();
let first_land_id = scenario.add_land_to_hand(P0, "First Land").id();
let second_land_id = scenario.add_land_to_hand(P0, "Second Land").id();

let mut runner = scenario.build();
runner.state_mut().max_lands_per_turn = 2;
runner
.state_mut()
.objects
.get_mut(&cobra_id)
.expect("Lotus Cobra should start on the battlefield")
.counters
.insert(CounterType::Plus1Plus1, 1);

resolve_cub_landfall(&mut runner, first_land_id, cobra_id);
assert_eq!(
runner.state().objects[&cobra_id]
.counters
.get(&CounterType::Plus1Plus1)
.copied(),
Some(2),
"the first landfall resolution should place one +1/+1 counter on the seeded target"
);

resolve_cub_landfall(&mut runner, second_land_id, cobra_id);
assert_eq!(
runner.state().objects[&cobra_id]
.counters
.get(&CounterType::Plus1Plus1)
.copied(),
Some(4),
"the second landfall resolution should double Lotus Cobra's counters"
);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
assert_eq!(
runner
.state()
.ability_resolutions_this_turn
.get(&(cub_id, 0)),
Some(&2),
"both landfall triggers should share Cub's printed ability index"
);
}
Loading