Spaces:
Running
Running
| 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() | |