File size: 3,299 Bytes
bb3fbf9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
94
95
import os
import sys

# Add project root to path
# CWD should be loveca-copy
sys.path.insert(0, os.getcwd())
print(f"DEBUG: sys.path[0] = {sys.path[0]}")
print(f"DEBUG: CWD = {os.getcwd()}")

import numpy as np

from engine.game.game_state import GameState
from engine.models.ability import Ability, AbilityCostType, Cost, Effect, EffectType, TriggerType
from engine.models.card import MemberCard


def test_pr_027_self_salvage():
    """

    Verify that a card with 'Cost: Send self to waiting room' and 'Effect: Add member from waiting room to hand'

    can target itself.

    """
    # 1. Setup Game
    game = GameState()
    p1 = game.players[0]

    # 2. Mock Card
    # Ability: [Startup] Cost: Self to WR -> Effect: Salvage 1 Member
    # We construct the ability manually to match the expected parser output for:
    # "{{kidou.png|起動}}このメンバーをステージから控え室に置く:自分の控え室からメンバーカードを1枚手札に加える。"

    ability = Ability(
        raw_text="Activate: Self to WR -> Salvage 1",
        trigger=TriggerType.ACTIVATED,
        costs=[Cost(type=AbilityCostType.SACRIFICE_SELF, value=1)],
        effects=[Effect(effect_type=EffectType.ADD_TO_HAND, value=1, params={"from": "discard", "filter": "member"})],
    )

    card = MemberCard(
        card_id=9999,
        card_no="PL!S-PR-027-PR",
        name="Matsuura Kanan",
        cost=1,
        hearts=np.zeros(7, dtype=np.int32),
        blade_hearts=np.zeros(7, dtype=np.int32),
        blades=1,
        abilities=[ability],
    )

    # Register card
    game.member_db[9999] = card

    # 3. Setup State
    # Place card on stage
    p1.stage[0] = 9999
    p1.hand = []  # Empty hand
    p1.discard = []  # Empty discard to ensure we only pick self
    p1.main_deck = [101, 102]  # Non-empty deck to prevent auto-refresh

    from engine.game.enums import Phase

    game.phase = Phase.MAIN

    # 4. Action: Activate Ability
    # Action 200 = Activate Area 0
    game._execute_action(200)

    # Cost should be paid: Card moves Stage -> Discard
    assert p1.stage[0] == -1, "Card should be removed from stage"
    assert len(p1.discard) == 1, "Card should be in discard"
    assert p1.discard[0] == 9999, "Card 9999 should be in discard"

    # Choice should be pending: Select card from discard
    assert len(game.pending_choices) > 0, "Should have pending choice for effect"
    choice_type, params = game.pending_choices[0]

    # Updated expectation: SELECT_FROM_DISCARD
    assert choice_type == "SELECT_FROM_DISCARD", f"Expected SELECT_FROM_DISCARD, got {choice_type}"
    assert params["filter"] == "member", "Should filter for members"

    # 5. Execute Choice: Select the card itself (which is in discard now)
    # SELECT_FROM_DISCARD choice handler uses actions 660-719.
    # params['cards'] should be [9999] in this case.
    # So we choose index 0 (Action 660).

    game._execute_action(660)

    # 6. Verify Result
    assert len(p1.hand) == 1, "Hand should have 1 card"
    assert p1.hand[0] == 9999, "Should have drawn self"
    assert len(p1.discard) == 0, "Discard should be empty"


if __name__ == "__main__":
    test_pr_027_self_salvage()