1use forge_foundation::ZoneType;
7use serde::{Deserialize, Serialize};
8
9use crate::game::GameState;
10use crate::ids::{CardId, PlayerId};
11use crate::spellability::SpellAbility;
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
18pub struct StackEntry {
19 pub id: u32,
20 pub spell_ability: SpellAbility,
22 #[serde(default)]
26 pub is_pending_cast: bool,
27 pub is_creature_spell: bool,
29 pub is_permanent_spell: bool,
31 pub cast_from_zone: Option<ZoneType>,
33 #[serde(default)]
36 pub optional_trigger_decider: Option<PlayerId>,
37 #[serde(default)]
39 pub optional_trigger_description: Option<String>,
40 #[serde(default)]
42 pub optional_trigger_source_name: Option<String>,
43}
44
45impl StackEntry {
46 pub fn next_id() -> u64 {
49 use std::sync::atomic::{AtomicU64, Ordering};
50 static COUNTER: AtomicU64 = AtomicU64::new(1);
51 COUNTER.fetch_add(1, Ordering::Relaxed)
52 }
53
54 pub fn update_target(&mut self, old: CardId, new: CardId) {
57 self.spell_ability.update_target(old, new);
58 }
59
60 pub fn set_triggering_object<K: crate::spellability::TriggerKeyInput>(
63 &mut self,
64 key: K,
65 value: &str,
66 ) {
67 self.spell_ability.set_triggering_object(key, value);
68 }
69
70 pub fn update_triggering_object<K: crate::spellability::TriggerKeyInput>(
73 &mut self,
74 key: K,
75 value: &str,
76 ) {
77 self.spell_ability.update_triggering_object(key, value);
78 }
79}
80
81#[derive(Debug, Clone, Serialize, Deserialize)]
84pub struct MagicStack {
85 entries: Vec<StackEntry>,
86 next_id: u32,
87
88 #[serde(default)]
91 frozen: bool,
92
93 #[serde(default)]
95 frozen_stack: Vec<StackEntry>,
96
97 #[serde(default)]
99 resolving: bool,
100
101 #[serde(default)]
103 cur_resolving_card: Option<CardId>,
104
105 #[serde(default, skip)]
106 resolving_entry: Option<StackEntry>,
107
108 #[serde(default, skip)]
114 recently_removed: Vec<StackEntry>,
115
116 #[serde(default)]
118 this_turn_cast: Vec<CardId>,
119
120 #[serde(default)]
122 last_turn_cast: Vec<CardId>,
123
124 #[serde(default)]
126 this_turn_activated: Vec<CardId>,
127
128 #[serde(default)]
130 max_distinct_sources: usize,
131
132 #[serde(default)]
134 undo_stack: Vec<UndoEntry>,
135
136 #[serde(default)]
138 undo_stack_owner: Option<PlayerId>,
139
140 #[serde(default)]
143 simultaneous_entries: Vec<StackEntry>,
144
145 #[serde(default)]
147 cast_commands: std::collections::HashMap<String, Vec<String>>,
148}
149
150#[derive(Debug, Clone, Serialize, Deserialize)]
152pub struct UndoEntry {
153 pub source_card: Option<CardId>,
154 pub activating_player: PlayerId,
155}
156
157impl MagicStack {
158 pub fn new() -> Self {
159 MagicStack {
160 entries: Vec::new(),
161 next_id: 0,
162 frozen: false,
163 frozen_stack: Vec::new(),
164 resolving: false,
165 cur_resolving_card: None,
166 resolving_entry: None,
167 this_turn_cast: Vec::new(),
168 last_turn_cast: Vec::new(),
169 this_turn_activated: Vec::new(),
170 max_distinct_sources: 0,
171 undo_stack: Vec::new(),
172 undo_stack_owner: None,
173 simultaneous_entries: Vec::new(),
174 cast_commands: std::collections::HashMap::new(),
175 recently_removed: Vec::new(),
176 }
177 }
178
179 pub fn push(&mut self, mut entry: StackEntry) -> u32 {
180 let id = self.next_id;
181 self.next_id += 1;
182 entry.is_pending_cast = false;
183 entry.id = id;
184 self.entries.push(entry);
185 self.update_max_distinct_sources();
186 id
187 }
188
189 pub fn begin_pending_cast(&mut self, mut entry: StackEntry) -> u32 {
190 let id = self.next_id;
191 self.next_id += 1;
192 entry.id = id;
193 entry.is_pending_cast = true;
194 self.entries.push(entry);
195 self.update_max_distinct_sources();
196 id
197 }
198
199 pub fn complete_pending_cast(&mut self, id: u32, mut entry: StackEntry) -> Option<&StackEntry> {
200 let pending = self
201 .entries
202 .iter_mut()
203 .find(|existing| existing.id == id && existing.is_pending_cast)?;
204 entry.id = id;
205 entry.is_pending_cast = false;
206 *pending = entry;
207 self.update_max_distinct_sources();
208 self.entries.iter().find(|existing| existing.id == id)
209 }
210
211 pub fn remove_pending_cast(&mut self, id: u32) -> Option<StackEntry> {
212 let index = self
213 .entries
214 .iter()
215 .position(|entry| entry.id == id && entry.is_pending_cast)?;
216 let entry = self.entries.remove(index);
217 self.update_max_distinct_sources();
218 Some(entry)
219 }
220
221 fn update_max_distinct_sources(&mut self) {
222 let distinct: std::collections::HashSet<_> = self
223 .entries
224 .iter()
225 .filter_map(|e| e.spell_ability.source)
226 .collect();
227 if distinct.len() > self.max_distinct_sources {
228 self.max_distinct_sources = distinct.len();
229 }
230 }
231
232 pub fn pop(&mut self) -> Option<StackEntry> {
233 self.entries.pop()
234 }
235
236 pub fn peek(&self) -> Option<&StackEntry> {
237 self.entries.last()
238 }
239
240 pub fn is_empty(&self) -> bool {
241 self.entries.is_empty()
242 }
243
244 pub fn len(&self) -> usize {
245 self.entries.len()
246 }
247
248 pub fn iter(&self) -> impl Iterator<Item = &StackEntry> {
249 self.entries.iter()
250 }
251
252 pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut StackEntry> {
253 self.entries.iter_mut()
254 }
255
256 pub fn find_by_id(&self, id: u32) -> Option<&StackEntry> {
261 self.entries
262 .iter()
263 .find(|e| e.id == id)
264 .or_else(|| self.recently_removed.iter().find(|e| e.id == id))
265 }
266
267 pub fn remove_by_id(&mut self, id: u32) -> Option<StackEntry> {
271 if let Some(pos) = self.entries.iter().position(|e| e.id == id) {
272 let entry = self.entries.remove(pos);
273 self.recently_removed.push(entry.clone());
274 Some(entry)
275 } else {
276 None
277 }
278 }
279
280 pub fn clear_recently_removed(&mut self) {
283 self.recently_removed.clear();
284 }
285
286 pub fn find_by_source_card(&self, card_id: CardId) -> Option<&StackEntry> {
288 self.entries
289 .iter()
290 .find(|e| e.spell_ability.source == Some(card_id))
291 }
292
293 pub fn size(&self) -> usize {
295 self.entries.len()
296 }
297
298 pub fn add(&mut self, entry: StackEntry) -> u32 {
300 self.push(entry)
301 }
302
303 pub fn remove(&mut self, id: u32) -> Option<StackEntry> {
305 self.remove_by_id(id)
306 }
307
308 pub fn clear(&mut self) {
310 self.entries.clear();
311 }
312
313 pub fn reset(&mut self) {
315 self.entries.clear();
316 self.next_id = 0;
317 self.frozen = false;
318 self.frozen_stack.clear();
319 self.resolving = false;
320 self.cur_resolving_card = None;
321 self.resolving_entry = None;
322 self.last_turn_cast.clear();
323 self.this_turn_cast.clear();
324 self.simultaneous_entries.clear();
325 self.undo_stack.clear();
326 self.undo_stack_owner = None;
327 self.cast_commands.clear();
328 }
329
330 pub fn peek_ability(&self) -> Option<&SpellAbility> {
332 self.entries.last().map(|e| &e.spell_ability)
333 }
334
335 pub fn has_source_on_stack(&self, card_id: CardId) -> bool {
338 let matches = |entry: &StackEntry| entry.spell_ability.source == Some(card_id);
339 self.entries.iter().any(&matches)
340 || self.frozen_stack.iter().any(&matches)
341 || self.simultaneous_entries.iter().any(&matches)
342 || self.resolving_entry.as_ref().is_some_and(matches)
343 }
344
345 pub fn has_source_chapter_on_stack(&self, game: &GameState, card_id: CardId) -> bool {
346 let matches = |entry: &StackEntry| {
347 entry.spell_ability.is_trigger
348 && entry.spell_ability.source == Some(card_id)
349 && entry
350 .spell_ability
351 .source_trigger_id
352 .is_some_and(|trigger_id| {
353 game.card(card_id)
354 .triggers
355 .iter()
356 .any(|trigger| trigger.id == trigger_id && trigger.is_chapter())
357 })
358 };
359
360 self.entries.iter().any(&matches)
361 || self.frozen_stack.iter().any(&matches)
362 || self.simultaneous_entries.iter().any(&matches)
363 || self.resolving_entry.as_ref().is_some_and(matches)
364 }
365
366 pub fn has_legal_targeting(&self) -> bool {
369 match self.entries.last() {
370 Some(entry) => {
371 let tc = &entry.spell_ability.target_chosen;
372 if entry.spell_ability.target_restrictions.is_none() {
374 return true;
375 }
376 tc.target_card.is_some()
378 || tc.target_player.is_some()
379 || tc.target_stack_entry.is_some()
380 }
381 None => false,
382 }
383 }
384
385 pub fn remove_instances_controlled_by(&mut self, player: PlayerId) {
388 self.entries
389 .retain(|e| e.spell_ability.activating_player != player);
390 }
391
392 pub fn iterator(&self) -> impl Iterator<Item = &StackEntry> {
394 self.entries.iter()
395 }
396
397 pub fn reverse_iterator(&self) -> impl Iterator<Item = &StackEntry> {
399 self.entries.iter().rev()
400 }
401
402 pub fn is_frozen(&self) -> bool {
406 self.frozen
407 }
408
409 pub fn freeze_stack(&mut self) {
412 self.frozen = true;
413 }
414
415 pub fn add_and_unfreeze(&mut self, entry: StackEntry) -> u32 {
418 let id = self.push(entry);
419 self.unfreeze_stack();
420 id
421 }
422
423 pub fn unfreeze_stack(&mut self) {
426 self.frozen = false;
427 let frozen = std::mem::take(&mut self.frozen_stack);
429 for entry in frozen.into_iter().rev() {
430 self.push(entry);
431 }
432 }
433
434 pub fn clear_frozen(&mut self) {
437 self.frozen = false;
438 self.frozen_stack.clear();
439 }
440
441 pub fn is_resolving(&self) -> bool {
445 self.resolving
446 }
447
448 pub fn set_resolving(&mut self, resolving: bool) {
449 self.resolving = resolving;
450 }
451
452 pub fn set_cur_resolving_card(&mut self, card: Option<CardId>) {
453 self.cur_resolving_card = card;
454 }
455
456 pub fn cur_resolving_card(&self) -> Option<CardId> {
457 self.cur_resolving_card
458 }
459
460 pub fn set_resolving_entry(&mut self, entry: Option<StackEntry>) {
461 self.resolving_entry = entry;
462 }
463
464 pub fn can_undo(&self, player: PlayerId) -> bool {
469 self.undo_stack_owner == Some(player) && !self.undo_stack.is_empty()
470 }
471
472 pub fn undo(&mut self) -> bool {
475 if self.undo_stack.is_empty() {
476 return false;
477 }
478 self.undo_stack.pop();
479 if self.undo_stack.is_empty() {
480 self.undo_stack_owner = None;
481 }
482 true
483 }
484
485 pub fn clear_undo_stack(&mut self) {
488 self.undo_stack.clear();
489 self.undo_stack_owner = None;
490 }
491
492 pub fn filter_undo_stack_by_host(&mut self, card_id: CardId) {
495 self.undo_stack.retain(|e| e.source_card != Some(card_id));
496 if self.undo_stack.is_empty() {
497 self.undo_stack_owner = None;
498 }
499 }
500
501 pub fn record_undoable(&mut self, source: Option<CardId>, player: PlayerId) {
503 self.undo_stack_owner = Some(player);
504 self.undo_stack.push(UndoEntry {
505 source_card: source,
506 activating_player: player,
507 });
508 }
509
510 pub fn has_simultaneous_stack_entries(&self) -> bool {
515 !self.simultaneous_entries.is_empty()
516 }
517
518 pub fn clear_simultaneous_stack(&mut self) {
521 self.simultaneous_entries.clear();
522 }
523
524 pub fn add_simultaneous_stack_entry(&mut self, entry: StackEntry) {
527 self.simultaneous_entries.push(entry);
528 }
529
530 pub fn add_all_triggered_abilities_to_stack(&mut self) -> bool {
534 if self.simultaneous_entries.is_empty() {
535 return false;
536 }
537 let entries = std::mem::take(&mut self.simultaneous_entries);
538 for entry in entries {
539 self.push(entry);
540 }
541 true
542 }
543
544 pub fn has_state_trigger(&self) -> bool {
547 self.simultaneous_entries
548 .iter()
549 .any(|e| e.spell_ability.is_trigger)
550 }
551
552 pub fn has_state_trigger_id(&self, trigger_id: u32) -> bool {
555 let matches = |e: &StackEntry| {
556 e.spell_ability.is_trigger && e.spell_ability.source_trigger_id == Some(trigger_id)
557 };
558 self.entries.iter().any(matches)
559 || self.frozen_stack.iter().any(matches)
560 || self.simultaneous_entries.iter().any(matches)
561 }
562
563 pub fn add_cast_command(&mut self, key: &str, command: String) {
568 self.cast_commands
569 .entry(key.to_string())
570 .or_default()
571 .push(command);
572 }
573
574 pub fn take_cast_commands(&mut self, key: &str) -> Vec<String> {
576 self.cast_commands.remove(key).unwrap_or_default()
577 }
578
579 pub fn resolve_stack(&mut self) -> Option<StackEntry> {
587 if self
588 .entries
589 .last()
590 .map(|entry| entry.is_pending_cast)
591 .unwrap_or(false)
592 {
593 return None;
594 }
595 let entry = self.entries.pop()?;
596 self.resolving = true;
597 self.cur_resolving_card = entry.spell_ability.source;
598 self.resolving_entry = Some(entry.clone());
599 Some(entry)
600 }
601
602 pub fn finish_resolving(&mut self) {
604 self.resolving = false;
605 self.cur_resolving_card = None;
606 self.resolving_entry = None;
607 }
608
609 pub fn on_next_turn(&mut self) {
614 self.last_turn_cast = std::mem::take(&mut self.this_turn_cast);
615 self.this_turn_activated.clear();
616 }
617
618 pub fn record_spell_cast(&mut self, card_id: CardId) {
620 self.this_turn_cast.push(card_id);
621 }
622
623 pub fn spells_cast_this_turn(&self) -> usize {
625 self.this_turn_cast.len()
626 }
627
628 pub fn get_spells_cast_this_turn(&self) -> &[CardId] {
630 &self.this_turn_cast
631 }
632
633 pub fn get_spells_cast_last_turn(&self) -> &[CardId] {
635 &self.last_turn_cast
636 }
637
638 pub fn add_ability_activated_this_turn(&mut self, sa: &SpellAbility) {
641 if let Some(source) = sa.source {
642 self.this_turn_activated.push(source);
643 }
644 }
645
646 pub fn reset_max_distinct_sources(&mut self) {
649 self.max_distinct_sources = 0;
650 }
651
652 pub fn get_max_distinct_sources(&self) -> usize {
654 self.max_distinct_sources
655 }
656
657 pub fn get_instance_matching_spell_ability_id(&self, id: u32) -> Option<&StackEntry> {
660 self.entries.iter().find(|si| si.id == id)
661 }
662
663 pub fn get_spell_matching_host(&self, host: CardId) -> Option<&SpellAbility> {
667 for si in &self.entries {
668 if si.spell_ability.is_spell && si.spell_ability.source == Some(host) {
669 return Some(&si.spell_ability);
670 }
671 }
672 None
673 }
674}
675
676impl Default for MagicStack {
677 fn default() -> Self {
678 Self::new()
679 }
680}