1use forge_foundation::ZoneType;
7use serde::{Deserialize, Serialize};
8
9use crate::ids::{CardId, PlayerId};
10use crate::spellability::SpellAbility;
11
12#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct StackEntry {
18 pub id: u32,
19 pub spell_ability: SpellAbility,
21 #[serde(default)]
25 pub is_pending_cast: bool,
26 pub is_creature_spell: bool,
28 pub is_permanent_spell: bool,
30 pub cast_from_zone: Option<ZoneType>,
32 #[serde(default)]
35 pub optional_trigger_decider: Option<PlayerId>,
36 #[serde(default)]
38 pub optional_trigger_description: Option<String>,
39 #[serde(default)]
41 pub optional_trigger_source_name: Option<String>,
42}
43
44impl StackEntry {
45 pub fn next_id() -> u64 {
48 use std::sync::atomic::{AtomicU64, Ordering};
49 static COUNTER: AtomicU64 = AtomicU64::new(1);
50 COUNTER.fetch_add(1, Ordering::Relaxed)
51 }
52
53 pub fn update_target(&mut self, old: CardId, new: CardId) {
56 self.spell_ability.update_target(old, new);
57 }
58
59 pub fn set_triggering_object<K: crate::spellability::TriggerKeyInput>(
62 &mut self,
63 key: K,
64 value: &str,
65 ) {
66 self.spell_ability.set_triggering_object(key, value);
67 }
68
69 pub fn update_triggering_object<K: crate::spellability::TriggerKeyInput>(
72 &mut self,
73 key: K,
74 value: &str,
75 ) {
76 self.spell_ability.update_triggering_object(key, value);
77 }
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize)]
83pub struct MagicStack {
84 entries: Vec<StackEntry>,
85 next_id: u32,
86
87 #[serde(default)]
90 frozen: bool,
91
92 #[serde(default)]
94 frozen_stack: Vec<StackEntry>,
95
96 #[serde(default)]
98 resolving: bool,
99
100 #[serde(default)]
102 cur_resolving_card: Option<CardId>,
103
104 #[serde(default, skip)]
110 recently_removed: Vec<StackEntry>,
111
112 #[serde(default)]
114 this_turn_cast: Vec<CardId>,
115
116 #[serde(default)]
118 last_turn_cast: Vec<CardId>,
119
120 #[serde(default)]
122 this_turn_activated: Vec<CardId>,
123
124 #[serde(default)]
126 max_distinct_sources: usize,
127
128 #[serde(default)]
130 undo_stack: Vec<UndoEntry>,
131
132 #[serde(default)]
134 undo_stack_owner: Option<PlayerId>,
135
136 #[serde(default)]
139 simultaneous_entries: Vec<StackEntry>,
140
141 #[serde(default)]
143 cast_commands: std::collections::HashMap<String, Vec<String>>,
144}
145
146#[derive(Debug, Clone, Serialize, Deserialize)]
148pub struct UndoEntry {
149 pub source_card: Option<CardId>,
150 pub activating_player: PlayerId,
151}
152
153impl MagicStack {
154 pub fn new() -> Self {
155 MagicStack {
156 entries: Vec::new(),
157 next_id: 0,
158 frozen: false,
159 frozen_stack: Vec::new(),
160 resolving: false,
161 cur_resolving_card: None,
162 this_turn_cast: Vec::new(),
163 last_turn_cast: Vec::new(),
164 this_turn_activated: Vec::new(),
165 max_distinct_sources: 0,
166 undo_stack: Vec::new(),
167 undo_stack_owner: None,
168 simultaneous_entries: Vec::new(),
169 cast_commands: std::collections::HashMap::new(),
170 recently_removed: Vec::new(),
171 }
172 }
173
174 pub fn push(&mut self, mut entry: StackEntry) -> u32 {
175 let id = self.next_id;
176 self.next_id += 1;
177 entry.is_pending_cast = false;
178 entry.id = id;
179 self.entries.push(entry);
180 self.update_max_distinct_sources();
181 id
182 }
183
184 pub fn begin_pending_cast(&mut self, mut entry: StackEntry) -> u32 {
185 let id = self.next_id;
186 self.next_id += 1;
187 entry.id = id;
188 entry.is_pending_cast = true;
189 self.entries.push(entry);
190 self.update_max_distinct_sources();
191 id
192 }
193
194 pub fn complete_pending_cast(&mut self, id: u32, mut entry: StackEntry) -> Option<&StackEntry> {
195 let pending = self
196 .entries
197 .iter_mut()
198 .find(|existing| existing.id == id && existing.is_pending_cast)?;
199 entry.id = id;
200 entry.is_pending_cast = false;
201 *pending = entry;
202 self.update_max_distinct_sources();
203 self.entries.iter().find(|existing| existing.id == id)
204 }
205
206 pub fn remove_pending_cast(&mut self, id: u32) -> Option<StackEntry> {
207 let index = self
208 .entries
209 .iter()
210 .position(|entry| entry.id == id && entry.is_pending_cast)?;
211 let entry = self.entries.remove(index);
212 self.update_max_distinct_sources();
213 Some(entry)
214 }
215
216 fn update_max_distinct_sources(&mut self) {
217 let distinct: std::collections::HashSet<_> = self
218 .entries
219 .iter()
220 .filter_map(|e| e.spell_ability.source)
221 .collect();
222 if distinct.len() > self.max_distinct_sources {
223 self.max_distinct_sources = distinct.len();
224 }
225 }
226
227 pub fn pop(&mut self) -> Option<StackEntry> {
228 self.entries.pop()
229 }
230
231 pub fn peek(&self) -> Option<&StackEntry> {
232 self.entries.last()
233 }
234
235 pub fn is_empty(&self) -> bool {
236 self.entries.is_empty()
237 }
238
239 pub fn len(&self) -> usize {
240 self.entries.len()
241 }
242
243 pub fn iter(&self) -> impl Iterator<Item = &StackEntry> {
244 self.entries.iter()
245 }
246
247 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut StackEntry> {
248 self.entries.iter_mut()
249 }
250
251 pub fn find_by_id(&self, id: u32) -> Option<&StackEntry> {
256 self.entries
257 .iter()
258 .find(|e| e.id == id)
259 .or_else(|| self.recently_removed.iter().find(|e| e.id == id))
260 }
261
262 pub fn remove_by_id(&mut self, id: u32) -> Option<StackEntry> {
266 if let Some(pos) = self.entries.iter().position(|e| e.id == id) {
267 let entry = self.entries.remove(pos);
268 self.recently_removed.push(entry.clone());
269 Some(entry)
270 } else {
271 None
272 }
273 }
274
275 pub fn clear_recently_removed(&mut self) {
278 self.recently_removed.clear();
279 }
280
281 pub fn find_by_source_card(&self, card_id: CardId) -> Option<&StackEntry> {
283 self.entries
284 .iter()
285 .find(|e| e.spell_ability.source == Some(card_id))
286 }
287
288 pub fn size(&self) -> usize {
290 self.entries.len()
291 }
292
293 pub fn add(&mut self, entry: StackEntry) -> u32 {
295 self.push(entry)
296 }
297
298 pub fn remove(&mut self, id: u32) -> Option<StackEntry> {
300 self.remove_by_id(id)
301 }
302
303 pub fn clear(&mut self) {
305 self.entries.clear();
306 }
307
308 pub fn reset(&mut self) {
310 self.entries.clear();
311 self.next_id = 0;
312 self.frozen = false;
313 self.frozen_stack.clear();
314 self.resolving = false;
315 self.cur_resolving_card = None;
316 self.last_turn_cast.clear();
317 self.this_turn_cast.clear();
318 self.simultaneous_entries.clear();
319 self.undo_stack.clear();
320 self.undo_stack_owner = None;
321 self.cast_commands.clear();
322 }
323
324 pub fn peek_ability(&self) -> Option<&SpellAbility> {
326 self.entries.last().map(|e| &e.spell_ability)
327 }
328
329 pub fn has_source_on_stack(&self, card_id: CardId) -> bool {
332 self.entries
333 .iter()
334 .any(|e| e.spell_ability.source == Some(card_id))
335 }
336
337 pub fn has_legal_targeting(&self) -> bool {
340 match self.entries.last() {
341 Some(entry) => {
342 let tc = &entry.spell_ability.target_chosen;
343 if entry.spell_ability.target_restrictions.is_none() {
345 return true;
346 }
347 tc.target_card.is_some()
349 || tc.target_player.is_some()
350 || tc.target_stack_entry.is_some()
351 }
352 None => false,
353 }
354 }
355
356 pub fn remove_instances_controlled_by(&mut self, player: PlayerId) {
359 self.entries
360 .retain(|e| e.spell_ability.activating_player != player);
361 }
362
363 pub fn iterator(&self) -> impl Iterator<Item = &StackEntry> {
365 self.entries.iter()
366 }
367
368 pub fn reverse_iterator(&self) -> impl Iterator<Item = &StackEntry> {
370 self.entries.iter().rev()
371 }
372
373 pub fn is_frozen(&self) -> bool {
377 self.frozen
378 }
379
380 pub fn freeze_stack(&mut self) {
383 self.frozen = true;
384 }
385
386 pub fn add_and_unfreeze(&mut self, entry: StackEntry) -> u32 {
389 let id = self.push(entry);
390 self.unfreeze_stack();
391 id
392 }
393
394 pub fn unfreeze_stack(&mut self) {
397 self.frozen = false;
398 let frozen = std::mem::take(&mut self.frozen_stack);
400 for entry in frozen.into_iter().rev() {
401 self.push(entry);
402 }
403 }
404
405 pub fn clear_frozen(&mut self) {
408 self.frozen = false;
409 self.frozen_stack.clear();
410 }
411
412 pub fn is_resolving(&self) -> bool {
416 self.resolving
417 }
418
419 pub fn set_resolving(&mut self, resolving: bool) {
420 self.resolving = resolving;
421 }
422
423 pub fn set_cur_resolving_card(&mut self, card: Option<CardId>) {
424 self.cur_resolving_card = card;
425 }
426
427 pub fn cur_resolving_card(&self) -> Option<CardId> {
428 self.cur_resolving_card
429 }
430
431 pub fn can_undo(&self, player: PlayerId) -> bool {
436 self.undo_stack_owner == Some(player) && !self.undo_stack.is_empty()
437 }
438
439 pub fn undo(&mut self) -> bool {
442 if self.undo_stack.is_empty() {
443 return false;
444 }
445 self.undo_stack.pop();
446 if self.undo_stack.is_empty() {
447 self.undo_stack_owner = None;
448 }
449 true
450 }
451
452 pub fn clear_undo_stack(&mut self) {
455 self.undo_stack.clear();
456 self.undo_stack_owner = None;
457 }
458
459 pub fn filter_undo_stack_by_host(&mut self, card_id: CardId) {
462 self.undo_stack.retain(|e| e.source_card != Some(card_id));
463 if self.undo_stack.is_empty() {
464 self.undo_stack_owner = None;
465 }
466 }
467
468 pub fn record_undoable(&mut self, source: Option<CardId>, player: PlayerId) {
470 self.undo_stack_owner = Some(player);
471 self.undo_stack.push(UndoEntry {
472 source_card: source,
473 activating_player: player,
474 });
475 }
476
477 pub fn has_simultaneous_stack_entries(&self) -> bool {
482 !self.simultaneous_entries.is_empty()
483 }
484
485 pub fn clear_simultaneous_stack(&mut self) {
488 self.simultaneous_entries.clear();
489 }
490
491 pub fn add_simultaneous_stack_entry(&mut self, entry: StackEntry) {
494 self.simultaneous_entries.push(entry);
495 }
496
497 pub fn add_all_triggered_abilities_to_stack(&mut self) -> bool {
501 if self.simultaneous_entries.is_empty() {
502 return false;
503 }
504 let entries = std::mem::take(&mut self.simultaneous_entries);
505 for entry in entries {
506 self.push(entry);
507 }
508 true
509 }
510
511 pub fn has_state_trigger(&self) -> bool {
514 self.simultaneous_entries
515 .iter()
516 .any(|e| e.spell_ability.is_trigger)
517 }
518
519 pub fn has_state_trigger_id(&self, trigger_id: u32) -> bool {
522 let matches = |e: &StackEntry| {
523 e.spell_ability.is_trigger && e.spell_ability.source_trigger_id == Some(trigger_id)
524 };
525 self.entries.iter().any(matches)
526 || self.frozen_stack.iter().any(matches)
527 || self.simultaneous_entries.iter().any(matches)
528 }
529
530 pub fn add_cast_command(&mut self, key: &str, command: String) {
535 self.cast_commands
536 .entry(key.to_string())
537 .or_default()
538 .push(command);
539 }
540
541 pub fn take_cast_commands(&mut self, key: &str) -> Vec<String> {
543 self.cast_commands.remove(key).unwrap_or_default()
544 }
545
546 pub fn resolve_stack(&mut self) -> Option<StackEntry> {
554 if self
555 .entries
556 .last()
557 .map(|entry| entry.is_pending_cast)
558 .unwrap_or(false)
559 {
560 return None;
561 }
562 let entry = self.entries.pop()?;
563 self.resolving = true;
564 self.cur_resolving_card = entry.spell_ability.source;
565 Some(entry)
566 }
567
568 pub fn finish_resolving(&mut self) {
570 self.resolving = false;
571 self.cur_resolving_card = None;
572 }
573
574 pub fn on_next_turn(&mut self) {
579 self.last_turn_cast = std::mem::take(&mut self.this_turn_cast);
580 self.this_turn_activated.clear();
581 }
582
583 pub fn record_spell_cast(&mut self, card_id: CardId) {
585 self.this_turn_cast.push(card_id);
586 }
587
588 pub fn spells_cast_this_turn(&self) -> usize {
590 self.this_turn_cast.len()
591 }
592
593 pub fn get_spells_cast_this_turn(&self) -> &[CardId] {
595 &self.this_turn_cast
596 }
597
598 pub fn get_spells_cast_last_turn(&self) -> &[CardId] {
600 &self.last_turn_cast
601 }
602
603 pub fn add_ability_activated_this_turn(&mut self, sa: &SpellAbility) {
606 if let Some(source) = sa.source {
607 self.this_turn_activated.push(source);
608 }
609 }
610
611 pub fn reset_max_distinct_sources(&mut self) {
614 self.max_distinct_sources = 0;
615 }
616
617 pub fn get_max_distinct_sources(&self) -> usize {
619 self.max_distinct_sources
620 }
621
622 pub fn get_instance_matching_spell_ability_id(&self, id: u32) -> Option<&StackEntry> {
625 self.entries.iter().find(|si| si.id == id)
626 }
627
628 pub fn get_spell_matching_host(&self, host: CardId) -> Option<&SpellAbility> {
632 for si in &self.entries {
633 if si.spell_ability.is_spell && si.spell_ability.source == Some(host) {
634 return Some(&si.spell_ability);
635 }
636 }
637 None
638 }
639}
640
641impl Default for MagicStack {
642 fn default() -> Self {
643 Self::new()
644 }
645}