manabrew_engine/replacement/
replace_damage.rs1use crate::card::Card;
6use crate::game::GameState;
7use crate::ids::CardId;
8use crate::parsing::compare::compare_expr;
9
10use super::replacement_effect::ReplacementEffect;
11use super::replacement_handler::ReplacementEvent;
12use super::replacement_handler::{execute_replace_with_numeric_update, resolve_replace_value};
13use super::replacement_result::ReplacementResult;
14use super::replacement_type::ReplacementType;
15use crate::card_trait_base::CardTrait;
16
17pub fn can_replace(
19 effect: &ReplacementEffect,
20 event: &ReplacementEvent,
21 game: &GameState,
22 source_card: &Card,
23) -> bool {
24 if effect.event != ReplacementType::DamageDone {
25 return false;
26 }
27 let (damage_source, target_player, target_card, amount, is_combat) = match event {
28 ReplacementEvent::DamageToCard {
29 target,
30 amount,
31 source,
32 is_combat,
33 } => (*source, None, Some(*target), *amount, *is_combat),
34 ReplacementEvent::DamageToPlayer {
35 target,
36 amount,
37 source,
38 is_combat,
39 } => (*source, Some(*target), None, *amount, *is_combat),
40 _ => return false,
41 };
42 if amount <= 0 {
43 return false;
44 }
45 if let Some(valid_source) = effect.ir.valid_source_selector.as_ref() {
46 let Some(source_id) = damage_source else {
47 return false;
48 };
49 if !effect.matches_compiled_valid_card(valid_source, game.card(source_id), source_card) {
50 return false;
51 }
52 }
53 if let Some(valid_target) = effect.ir.valid_target_selector.as_ref() {
54 let target_matches = if let Some(target) = target_player {
55 crate::card::valid_filter::matches_valid(
56 &valid_target.as_raw(),
57 None,
58 Some(target),
59 source_card,
60 source_card.controller,
61 )
62 } else if let Some(target) = target_card {
63 effect.matches_compiled_valid_card(valid_target, game.card(target), source_card)
64 } else {
65 false
66 };
67 if !target_matches {
68 return false;
69 }
70 }
71 if let Some(wants_max_speed) = effect.ir.max_speed {
72 if wants_max_speed != (game.player(source_card.controller).speed == 4) {
73 return false;
74 }
75 }
76 if let Some(wants_combat) = effect.ir.is_combat {
77 if wants_combat != is_combat {
78 return false;
79 }
80 }
81 if let Some(damage_amount) = effect.ir.damage_amount_text.as_deref() {
82 let threshold = damage_amount.get(2..).unwrap_or("");
83 let rhs = resolve_replace_value(threshold, game, source_card.id, event)
84 .or_else(|| threshold.parse::<i32>().ok())
85 .unwrap_or(0);
86 let cmp = format!("{}{}", damage_amount.get(..2).unwrap_or("GE"), rhs);
87 if !compare_expr(amount, &cmp) {
88 return false;
89 }
90 }
91 true
92}
93
94pub fn execute(
96 effect: &ReplacementEffect,
97 event: &mut ReplacementEvent,
98 game: &GameState,
99 source_card_id: CardId,
100) -> ReplacementResult {
101 match event {
102 ReplacementEvent::DamageToCard { .. } | ReplacementEvent::DamageToPlayer { .. } => {}
103 _ => return ReplacementResult::NotReplaced,
104 }
105 if effect.prevents() {
106 match event {
107 ReplacementEvent::DamageToCard { amount, .. } => *amount = 0,
108 ReplacementEvent::DamageToPlayer { amount, .. } => *amount = 0,
109 _ => {}
110 }
111 return ReplacementResult::Prevented;
112 }
113 if let Some(replace) = effect.replace_with() {
115 match replace {
116 "DmgTwice" | "DoubleDamage" => {
117 match event {
118 ReplacementEvent::DamageToCard { amount, .. } => *amount *= 2,
119 ReplacementEvent::DamageToPlayer { amount, .. } => *amount *= 2,
120 _ => {}
121 }
122 return ReplacementResult::Updated;
123 }
124 "DmgHalf" | "HalfDamage" => {
125 match event {
126 ReplacementEvent::DamageToCard { amount, .. } => *amount = (*amount + 1) / 2,
127 ReplacementEvent::DamageToPlayer { amount, .. } => *amount = (*amount + 1) / 2,
128 _ => {}
129 }
130 return ReplacementResult::Updated;
131 }
132 "DmgPlus1" => {
133 match event {
134 ReplacementEvent::DamageToCard { amount, .. } => *amount += 1,
135 ReplacementEvent::DamageToPlayer { amount, .. } => *amount += 1,
136 _ => {}
137 }
138 return ReplacementResult::Updated;
139 }
140 "DmgPlus2" => {
141 match event {
142 ReplacementEvent::DamageToCard { amount, .. } => *amount += 2,
143 ReplacementEvent::DamageToPlayer { amount, .. } => *amount += 2,
144 _ => {}
145 }
146 return ReplacementResult::Updated;
147 }
148 _ => {}
149 }
150 }
151 if let Some(result) =
152 execute_replace_with_numeric_update(effect, event, game, source_card_id, "DamageAmount")
153 {
154 return result;
155 }
156 ReplacementResult::Replaced
157}