File size: 3,567 Bytes
463f868
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93

use crate::core::logic::card_db::LOGIC_ID_MASK;
use crate::core::logic::*;
// use std::collections::HashMap;

fn create_mini_db_with_bytecode(bc: Vec<i32>) -> CardDatabase {
    let mut db = CardDatabase::default();
    let mut m = MemberCard {
        card_id: 101,
        card_no: "MINI-101".to_string(),
        name: "Mini Card".to_string(),
        cost: 2,
        ..Default::default()
    };
    m.abilities.push(Ability {
        trigger: TriggerType::Activated,
        bytecode: bc,
        ..Default::default()
    });
    db.members.insert(101, m.clone());
    db.members_vec[(101 as usize) & LOGIC_ID_MASK as usize] = Some(m);
    db
}

fn create_mini_state() -> GameState {
    let mut state = GameState::default();
    state.players[0].player_id = 0;
    state.players[1].player_id = 1;
    state.phase = Phase::Main;
    state.ui.silent = false;
    // Place the card on stage so activate_ability_with_choice can find it
    state.players[0].stage[0] = 101;
    state
}

#[test]
fn mini_test_o_pay_energy_resumption() {
    // Bytecode: PAY_ENERGY(1), DRAW(1), RETURN
    let bc = vec![64, 1, 0, 0, 10, 1, 0, 1, 1, 0, 0, 0];
    let db = create_mini_db_with_bytecode(bc);
    let mut state = create_mini_state();

    // Setup energy (2 untapped, need 1 -> should suspend for choice)
    state.players[0].tapped_energy_mask = 0;

    // Populate deck so DRAW works
    state.players[0].deck.extend(vec![1, 2, 3, 4, 5]);

    // Activate the ability at slot 0, ability 0
    state.activate_ability_with_choice(&db, 0, 0, -1, 0).unwrap();

    assert_eq!(state.phase, Phase::Response, "Should suspend for PAY_ENERGY selection");
    assert_eq!(state.interaction_stack.last().map(|i| i.effect_opcode).unwrap_or(0), 64, "Pending opcode should be O_PAY_ENERGY");

    // Resume with SelectResponseSlot action
    state.step(&db, Action::SelectResponseSlot { slot_idx: 0 }.id() as i32).unwrap();

    assert!(state.players[0].is_energy_tapped(0), "Energy should be tapped");
    assert_eq!(state.players[0].hand.len(), 1, "Should have resumed and drawn a card");
    assert_eq!(state.phase, Phase::Main, "Should return to Main phase");
}

#[test]
fn mini_test_o_select_mode_resumption() {
    // Bytecode: SELECT_MODE(2 choices), jump targets, Option 1: DRAW(1)+RETURN, Option 2: DRAW(2)+RETURN
    let bc = vec![
        30, 2, 12, 20, // SELECT_MODE, v=2, Jmp0=12, Jmp1=20
        3, 0, 0, 0,  // Option 1 -> instruction 3 (*4 = IP 12)
        5, 0, 0, 0,  // Option 2 -> instruction 5 (*4 = IP 20)
        10, 1, 0, 1, // IP 12: DRAW(1)
        1, 0, 0, 0,  // IP 16: RETURN
        10, 2, 0, 1, // IP 20: DRAW(2)
        1, 0, 0, 0   // IP 24: RETURN
    ];
    let db = create_mini_db_with_bytecode(bc);
    let mut state = create_mini_state();

    // Populate deck so DRAW works
    state.players[0].deck.extend(vec![1, 2, 3, 4, 5]);

    // Activate the ability at slot 0, ability 0
    state.activate_ability_with_choice(&db, 0, 0, -1, 0).unwrap();

    assert_eq!(state.phase, Phase::Response, "Should suspend for SELECT_MODE");
    assert_eq!(state.interaction_stack.last().map(|i| i.effect_opcode).unwrap_or(0), 30, "Pending opcode should be O_SELECT_MODE");

    // Pick Option 2 (choice_idx=1)
    state.step(&db, Action::SelectChoice { choice_idx: 1 }.id() as i32).unwrap();

    assert_eq!(state.players[0].hand.len(), 2, "Should have picked Option 2 and drawn 2 cards");
    assert_eq!(state.phase, Phase::Main, "Should return to Main phase");
}