extends GutTest ## Tests for EffectResolver effect execution ## Covers core effects: DAMAGE, POWER_MOD, DULL, ACTIVATE, DRAW, BREAK, RETURN var resolver: EffectResolver func before_each() -> void: resolver = EffectResolver.new() # ============================================================================= # DAMAGE EFFECT TESTS # ============================================================================= func test_resolve_damage_basic() -> void: var card = _create_mock_forward(7000) var game_state = _create_mock_game_state_with_forward(card, 0) var effect = {"type": "DAMAGE", "amount": 3000} resolver.resolve(effect, null, [card], game_state) assert_eq(card.damage_received, 3000, "Card should have received 3000 damage") func test_resolve_damage_breaks_forward() -> void: var card = _create_mock_forward(5000) var game_state = _create_mock_game_state_with_forward(card, 0) var effect = {"type": "DAMAGE", "amount": 5000} resolver.resolve(effect, null, [card], game_state) assert_true(game_state.players[0].break_zone.has_card(card), "Forward should be in break zone") assert_false(game_state.players[0].field_forwards.has_card(card), "Forward should not be on field") func test_resolve_damage_exact_power_breaks() -> void: var card = _create_mock_forward(6000) var game_state = _create_mock_game_state_with_forward(card, 0) var effect = {"type": "DAMAGE", "amount": 6000} resolver.resolve(effect, null, [card], game_state) assert_true(game_state.players[0].break_zone.has_card(card), "Forward should break when damage equals power") func test_resolve_damage_multiple_targets() -> void: var card1 = _create_mock_forward(8000) var card2 = _create_mock_forward(5000) var game_state = _create_mock_game_state() game_state.players[0].field_forwards.add_card(card1) game_state.players[0].field_forwards.add_card(card2) card1.controller_index = 0 card2.controller_index = 0 var effect = {"type": "DAMAGE", "amount": 3000} resolver.resolve(effect, null, [card1, card2], game_state) assert_eq(card1.damage_received, 3000, "First card should have 3000 damage") assert_eq(card2.damage_received, 3000, "Second card should have 3000 damage") func test_resolve_damage_ignores_non_forwards() -> void: var backup = _create_mock_backup() var game_state = _create_mock_game_state() var effect = {"type": "DAMAGE", "amount": 5000} resolver.resolve(effect, null, [backup], game_state) assert_eq(backup.damage_received, 0, "Backup should not receive damage") # ============================================================================= # POWER_MOD EFFECT TESTS # ============================================================================= func test_resolve_power_mod_positive() -> void: var card = _create_mock_forward(5000) var game_state = _create_mock_game_state() var effect = {"type": "POWER_MOD", "amount": 2000} resolver.resolve(effect, null, [card], game_state) assert_eq(card.power_modifiers.size(), 1, "Should have one power modifier") assert_eq(card.power_modifiers[0], 2000, "Power modifier should be +2000") func test_resolve_power_mod_negative() -> void: var card = _create_mock_forward(7000) var game_state = _create_mock_game_state() var effect = {"type": "POWER_MOD", "amount": -3000} resolver.resolve(effect, null, [card], game_state) assert_eq(card.power_modifiers[0], -3000, "Power modifier should be -3000") func test_resolve_power_mod_multiple_targets() -> void: var card1 = _create_mock_forward(5000) var card2 = _create_mock_forward(6000) var game_state = _create_mock_game_state() var effect = {"type": "POWER_MOD", "amount": 1000} resolver.resolve(effect, null, [card1, card2], game_state) assert_eq(card1.power_modifiers[0], 1000, "First card should have +1000") assert_eq(card2.power_modifiers[0], 1000, "Second card should have +1000") func test_resolve_power_mod_applies_to_source_if_no_targets() -> void: var source = _create_mock_forward(5000) var game_state = _create_mock_game_state() var effect = {"type": "POWER_MOD", "amount": 3000} resolver.resolve(effect, source, [], game_state) assert_eq(source.power_modifiers[0], 3000, "Source should get modifier when no targets") # ============================================================================= # DULL EFFECT TESTS # ============================================================================= func test_resolve_dull_single_target() -> void: var card = _create_mock_forward(5000) card.activate() # Ensure card starts active var game_state = _create_mock_game_state() var effect = {"type": "DULL"} assert_true(card.is_active(), "Card should start active") resolver.resolve(effect, null, [card], game_state) assert_true(card.is_dull(), "Card should be dulled") func test_resolve_dull_multiple_targets() -> void: var card1 = _create_mock_forward(5000) var card2 = _create_mock_forward(6000) card1.activate() card2.activate() var game_state = _create_mock_game_state() var effect = {"type": "DULL"} resolver.resolve(effect, null, [card1, card2], game_state) assert_true(card1.is_dull(), "First card should be dulled") assert_true(card2.is_dull(), "Second card should be dulled") func test_resolve_dull_already_dull_card() -> void: var card = _create_mock_forward(5000) card.dull() # Already dull var game_state = _create_mock_game_state() var effect = {"type": "DULL"} resolver.resolve(effect, null, [card], game_state) assert_true(card.is_dull(), "Card should remain dulled") # ============================================================================= # ACTIVATE EFFECT TESTS # ============================================================================= func test_resolve_activate_single_target() -> void: var card = _create_mock_forward(5000) card.dull() # Start dull var game_state = _create_mock_game_state() var effect = {"type": "ACTIVATE"} assert_true(card.is_dull(), "Card should start dull") resolver.resolve(effect, null, [card], game_state) assert_true(card.is_active(), "Card should be activated") func test_resolve_activate_multiple_targets() -> void: var card1 = _create_mock_forward(5000) var card2 = _create_mock_forward(6000) card1.dull() card2.dull() var game_state = _create_mock_game_state() var effect = {"type": "ACTIVATE"} resolver.resolve(effect, null, [card1, card2], game_state) assert_true(card1.is_active(), "First card should be active") assert_true(card2.is_active(), "Second card should be active") func test_resolve_activate_already_active_card() -> void: var card = _create_mock_forward(5000) card.activate() # Already active var game_state = _create_mock_game_state() var effect = {"type": "ACTIVATE"} resolver.resolve(effect, null, [card], game_state) assert_true(card.is_active(), "Card should remain active") # ============================================================================= # DRAW EFFECT TESTS # ============================================================================= func test_resolve_draw_single_card() -> void: var source = _create_mock_forward(5000) source.controller_index = 0 var game_state = _create_mock_game_state_with_deck(5) var effect = {"type": "DRAW", "amount": 1} var initial_deck_size = game_state.players[0].deck.get_count() var initial_hand_size = game_state.players[0].hand.get_count() resolver.resolve(effect, source, [], game_state) assert_eq(game_state.players[0].deck.get_count(), initial_deck_size - 1, "Deck should have 1 less card") assert_eq(game_state.players[0].hand.get_count(), initial_hand_size + 1, "Hand should have 1 more card") func test_resolve_draw_multiple_cards() -> void: var source = _create_mock_forward(5000) source.controller_index = 0 var game_state = _create_mock_game_state_with_deck(10) var effect = {"type": "DRAW", "amount": 3} var initial_deck_size = game_state.players[0].deck.get_count() resolver.resolve(effect, source, [], game_state) assert_eq(game_state.players[0].deck.get_count(), initial_deck_size - 3, "Deck should have 3 fewer cards") func test_resolve_draw_opponent() -> void: var source = _create_mock_forward(5000) source.controller_index = 0 var game_state = _create_mock_game_state_with_deck(5) # Add cards to opponent's deck too for i in range(5): var deck_card = _create_mock_forward(1000) game_state.players[1].deck.add_card(deck_card) var effect = {"type": "DRAW", "amount": 1, "target": {"type": "OPPONENT"}} var initial_opp_deck = game_state.players[1].deck.get_count() var initial_opp_hand = game_state.players[1].hand.get_count() resolver.resolve(effect, source, [], game_state) assert_eq(game_state.players[1].deck.get_count(), initial_opp_deck - 1, "Opponent deck should have 1 less card") assert_eq(game_state.players[1].hand.get_count(), initial_opp_hand + 1, "Opponent hand should have 1 more card") # ============================================================================= # BREAK EFFECT TESTS # ============================================================================= func test_resolve_break_single_target() -> void: var card = _create_mock_forward(7000) var game_state = _create_mock_game_state_with_forward(card, 0) var effect = {"type": "BREAK"} assert_true(game_state.players[0].field_forwards.has_card(card), "Card should start on field") resolver.resolve(effect, null, [card], game_state) assert_false(game_state.players[0].field_forwards.has_card(card), "Card should not be on field") assert_true(game_state.players[0].break_zone.has_card(card), "Card should be in break zone") func test_resolve_break_multiple_targets() -> void: var card1 = _create_mock_forward(5000) var card2 = _create_mock_forward(6000) var game_state = _create_mock_game_state() game_state.players[0].field_forwards.add_card(card1) game_state.players[0].field_forwards.add_card(card2) card1.controller_index = 0 card2.controller_index = 0 var effect = {"type": "BREAK"} resolver.resolve(effect, null, [card1, card2], game_state) assert_true(game_state.players[0].break_zone.has_card(card1), "First card should be in break zone") assert_true(game_state.players[0].break_zone.has_card(card2), "Second card should be in break zone") func test_resolve_break_backup() -> void: var backup = _create_mock_backup() var game_state = _create_mock_game_state() game_state.players[0].field_backups.add_card(backup) backup.controller_index = 0 var effect = {"type": "BREAK"} resolver.resolve(effect, null, [backup], game_state) assert_true(game_state.players[0].break_zone.has_card(backup), "Backup should be in break zone") # ============================================================================= # RETURN EFFECT TESTS # ============================================================================= func test_resolve_return_forward_to_hand() -> void: var card = _create_mock_forward(5000) var game_state = _create_mock_game_state_with_forward(card, 0) var effect = {"type": "RETURN"} resolver.resolve(effect, null, [card], game_state) assert_false(game_state.players[0].field_forwards.has_card(card), "Card should not be on field") assert_true(game_state.players[0].hand.has_card(card), "Card should be in hand") func test_resolve_return_backup_to_hand() -> void: var backup = _create_mock_backup() var game_state = _create_mock_game_state() game_state.players[0].field_backups.add_card(backup) backup.controller_index = 0 backup.owner_index = 0 var effect = {"type": "RETURN"} resolver.resolve(effect, null, [backup], game_state) assert_false(game_state.players[0].field_backups.has_card(backup), "Backup should not be on field") assert_true(game_state.players[0].hand.has_card(backup), "Backup should be in hand") func test_resolve_return_multiple_targets() -> void: var card1 = _create_mock_forward(5000) var card2 = _create_mock_forward(6000) var game_state = _create_mock_game_state() game_state.players[0].field_forwards.add_card(card1) game_state.players[0].field_forwards.add_card(card2) card1.controller_index = 0 card1.owner_index = 0 card2.controller_index = 0 card2.owner_index = 0 var effect = {"type": "RETURN"} resolver.resolve(effect, null, [card1, card2], game_state) assert_true(game_state.players[0].hand.has_card(card1), "First card should be in hand") assert_true(game_state.players[0].hand.has_card(card2), "Second card should be in hand") # ============================================================================= # SCALING_EFFECT TESTS # ============================================================================= func test_resolve_scaling_effect_by_forwards() -> void: var source = _create_mock_forward(5000) source.controller_index = 0 var target = _create_mock_forward(10000) target.controller_index = 1 var game_state = _create_mock_game_state() # Add 3 forwards to controller's field for i in range(3): var forward = _create_mock_forward(4000) forward.controller_index = 0 game_state.players[0].field_forwards.add_card(forward) # Add target to opponent's field game_state.players[1].field_forwards.add_card(target) var effect = { "type": "SCALING_EFFECT", "base_effect": {"type": "DAMAGE"}, "scale_by": "FORWARDS_CONTROLLED", "multiplier": 1000, "scale_filter": {} } resolver.resolve(effect, source, [target], game_state) assert_eq(target.damage_received, 3000, "Should deal 3000 damage (3 forwards * 1000)") func test_resolve_scaling_effect_by_backups() -> void: var source = _create_mock_forward(5000) source.controller_index = 0 var target = _create_mock_forward(10000) target.controller_index = 1 var game_state = _create_mock_game_state() # Add 4 backups to controller's field for i in range(4): var backup = _create_mock_backup() backup.controller_index = 0 game_state.players[0].field_backups.add_card(backup) game_state.players[1].field_forwards.add_card(target) var effect = { "type": "SCALING_EFFECT", "base_effect": {"type": "DAMAGE"}, "scale_by": "BACKUPS_CONTROLLED", "multiplier": 500, "scale_filter": {} } resolver.resolve(effect, source, [target], game_state) assert_eq(target.damage_received, 2000, "Should deal 2000 damage (4 backups * 500)") func test_resolve_scaling_effect_with_filter() -> void: var source = _create_mock_forward(5000) source.controller_index = 0 var target = _create_mock_forward(10000) target.controller_index = 1 var game_state = _create_mock_game_state() # Add 2 Fire forwards and 2 Ice forwards for i in range(2): var fire_forward = _create_mock_forward_with_element(Enums.Element.FIRE) fire_forward.controller_index = 0 game_state.players[0].field_forwards.add_card(fire_forward) for i in range(2): var ice_forward = _create_mock_forward_with_element(Enums.Element.ICE) ice_forward.controller_index = 0 game_state.players[0].field_forwards.add_card(ice_forward) game_state.players[1].field_forwards.add_card(target) var effect = { "type": "SCALING_EFFECT", "base_effect": {"type": "DAMAGE"}, "scale_by": "FORWARDS_CONTROLLED", "multiplier": 2000, "scale_filter": {"element": "FIRE"} # Only count Fire forwards } resolver.resolve(effect, source, [target], game_state) assert_eq(target.damage_received, 4000, "Should deal 4000 damage (2 Fire forwards * 2000)") func test_resolve_scaling_effect_zero_count() -> void: var source = _create_mock_forward(5000) source.controller_index = 0 var target = _create_mock_forward(10000) target.controller_index = 1 var game_state = _create_mock_game_state() # No forwards on controller's field game_state.players[1].field_forwards.add_card(target) var effect = { "type": "SCALING_EFFECT", "base_effect": {"type": "DAMAGE"}, "scale_by": "FORWARDS_CONTROLLED", "multiplier": 1000, "scale_filter": {} } resolver.resolve(effect, source, [target], game_state) assert_eq(target.damage_received, 0, "Should deal 0 damage (0 forwards * 1000)") func test_resolve_scaling_effect_power_mod() -> void: var source = _create_mock_forward(5000) source.controller_index = 0 var game_state = _create_mock_game_state() # Add 2 backups for i in range(2): var backup = _create_mock_backup() backup.controller_index = 0 game_state.players[0].field_backups.add_card(backup) var effect = { "type": "SCALING_EFFECT", "base_effect": {"type": "POWER_MOD"}, "scale_by": "BACKUPS_CONTROLLED", "multiplier": 1000, "scale_filter": {} } resolver.resolve(effect, source, [source], game_state) assert_eq(source.power_modifiers[0], 2000, "Should get +2000 power (2 backups * 1000)") # ============================================================================= # CONDITIONAL EFFECT TESTS # ============================================================================= func test_resolve_conditional_then_branch() -> void: var source = _create_mock_forward(5000) source.controller_index = 0 var target = _create_mock_forward(7000) var game_state = _create_mock_game_state() game_state.players[0].damage = 5 # 5 damage points # Setup ConditionChecker mock resolver.condition_checker = MockConditionChecker.new(true) var effect = { "type": "CONDITIONAL", "condition": {"type": "DAMAGE_RECEIVED", "comparison": "GTE", "value": 3}, "then_effects": [{"type": "DAMAGE", "amount": 5000}], "else_effects": [] } resolver.resolve(effect, source, [target], game_state) assert_eq(target.damage_received, 5000, "Should execute then_effects branch") func test_resolve_conditional_else_branch() -> void: var source = _create_mock_forward(5000) source.controller_index = 0 var target = _create_mock_forward(7000) var game_state = _create_mock_game_state() game_state.players[0].damage = 1 # Only 1 damage # Setup ConditionChecker mock that returns false resolver.condition_checker = MockConditionChecker.new(false) var effect = { "type": "CONDITIONAL", "condition": {"type": "DAMAGE_RECEIVED", "comparison": "GTE", "value": 5}, "then_effects": [{"type": "DAMAGE", "amount": 5000}], "else_effects": [{"type": "DAMAGE", "amount": 1000}] } resolver.resolve(effect, source, [target], game_state) assert_eq(target.damage_received, 1000, "Should execute else_effects branch") func test_resolve_conditional_no_else_branch() -> void: var source = _create_mock_forward(5000) source.controller_index = 0 var target = _create_mock_forward(7000) var game_state = _create_mock_game_state() # Setup ConditionChecker mock that returns false resolver.condition_checker = MockConditionChecker.new(false) var effect = { "type": "CONDITIONAL", "condition": {"type": "SOME_CONDITION"}, "then_effects": [{"type": "DAMAGE", "amount": 5000}], "else_effects": [] # No else branch } resolver.resolve(effect, source, [target], game_state) assert_eq(target.damage_received, 0, "Should do nothing when condition false and no else") func test_resolve_conditional_multiple_then_effects() -> void: var source = _create_mock_forward(5000) source.controller_index = 0 var target = _create_mock_forward(7000) var game_state = _create_mock_game_state() resolver.condition_checker = MockConditionChecker.new(true) var effect = { "type": "CONDITIONAL", "condition": {"type": "ALWAYS_TRUE"}, "then_effects": [ {"type": "DAMAGE", "amount": 2000}, {"type": "POWER_MOD", "amount": -1000} ], "else_effects": [] } resolver.resolve(effect, source, [target], game_state) assert_eq(target.damage_received, 2000, "Should apply damage") assert_eq(target.power_modifiers[0], -1000, "Should apply power mod") # ============================================================================= # EFFECT_COMPLETED SIGNAL TEST # ============================================================================= func test_effect_completed_signal_emitted() -> void: var card = _create_mock_forward(5000) var game_state = _create_mock_game_state() var effect = {"type": "DULL"} var signal_received = false var received_effect = {} var received_targets = [] resolver.effect_completed.connect(func(e, t): signal_received = true received_effect = e received_targets = t ) resolver.resolve(effect, null, [card], game_state) assert_true(signal_received, "effect_completed signal should be emitted") assert_eq(received_effect.type, "DULL", "Signal should contain effect") assert_eq(received_targets.size(), 1, "Signal should contain targets") # ============================================================================= # COMBINED DAMAGE AND BREAK TEST # ============================================================================= func test_damage_accumulates_before_break() -> void: var card = _create_mock_forward(10000) var game_state = _create_mock_game_state_with_forward(card, 0) # Apply 4000 damage (shouldn't break) resolver.resolve({"type": "DAMAGE", "amount": 4000}, null, [card], game_state) assert_eq(card.damage_received, 4000, "Should have 4000 damage") assert_true(game_state.players[0].field_forwards.has_card(card), "Should still be on field") # Apply 6000 more (total 10000, should break) resolver.resolve({"type": "DAMAGE", "amount": 6000}, null, [card], game_state) assert_true(game_state.players[0].break_zone.has_card(card), "Should be broken with 10000 total damage") # ============================================================================= # HELPER FUNCTIONS # ============================================================================= func _create_mock_forward(power: int) -> CardInstance: var card = CardInstance.new() var data = CardDatabase.CardData.new() data.type = Enums.CardType.FORWARD data.power = power data.cost = 3 data.name = "Test Forward" data.elements = [Enums.Element.FIRE] card.card_data = data card.current_power = power return card func _create_mock_backup() -> CardInstance: var card = CardInstance.new() var data = CardDatabase.CardData.new() data.type = Enums.CardType.BACKUP data.cost = 2 data.name = "Test Backup" data.elements = [Enums.Element.WATER] card.card_data = data return card func _create_mock_forward_with_element(element: Enums.Element) -> CardInstance: var card = _create_mock_forward(5000) card.card_data.elements = [element] return card func _create_mock_game_state() -> MockGameState: return MockGameState.new() func _create_mock_game_state_with_forward(card: CardInstance, player_index: int) -> MockGameState: var gs = MockGameState.new() gs.players[player_index].field_forwards.add_card(card) card.controller_index = player_index card.owner_index = player_index return gs func _create_mock_game_state_with_deck(card_count: int) -> MockGameState: var gs = MockGameState.new() for i in range(card_count): var deck_card = _create_mock_forward(1000 + i * 1000) gs.players[0].deck.add_card(deck_card) return gs # ============================================================================= # MOCK CLASSES # ============================================================================= class MockGameState: var players: Array func _init(): players = [MockPlayer.new(0), MockPlayer.new(1)] func get_player(index: int): if index >= 0 and index < players.size(): return players[index] return null class MockPlayer: var player_index: int var damage: int = 0 var deck: Zone var hand: Zone var field_forwards: Zone var field_backups: Zone var break_zone: Zone func _init(index: int = 0): player_index = index deck = Zone.new(Enums.ZoneType.DECK, index) hand = Zone.new(Enums.ZoneType.HAND, index) field_forwards = Zone.new(Enums.ZoneType.FIELD, index) field_backups = Zone.new(Enums.ZoneType.FIELD, index) break_zone = Zone.new(Enums.ZoneType.BREAK_ZONE, index) func draw_cards(count: int) -> Array: var drawn: Array = [] for i in range(count): var card = deck.pop_top_card() if card: hand.add_card(card) drawn.append(card) return drawn func break_card(card: CardInstance) -> bool: var removed = false if field_forwards.has_card(card): field_forwards.remove_card(card) removed = true elif field_backups.has_card(card): field_backups.remove_card(card) removed = true if removed: break_zone.add_card(card) return true return false class MockConditionChecker: var _return_value: bool func _init(return_value: bool = true): _return_value = return_value func evaluate(_condition: Dictionary, _context: Dictionary) -> bool: return _return_value