class_name GameState extends RefCounted ## GameState - Central game state container and rules engine signal game_started signal game_ended(winner_index: int) signal card_played(card: CardInstance, player_index: int) signal summon_cast(card: CardInstance, player_index: int) signal card_moved(card: CardInstance, from_zone: Enums.ZoneType, to_zone: Enums.ZoneType) signal damage_dealt(player_index: int, amount: int, cards: Array[CardInstance]) signal forward_broken(card: CardInstance) signal attack_declared(attacker: CardInstance) signal block_declared(blocker: CardInstance) signal combat_resolved(attacker: CardInstance, blocker: CardInstance) signal cp_generated(player_index: int, element: Enums.Element, amount: int) # Players var players: Array[Player] = [] # Turn management var turn_manager: TurnManager # Shared stack for abilities/summons var stack: Zone # Game state flags var game_active: bool = false var winner_index: int = -1 func _init() -> void: turn_manager = TurnManager.new() stack = Zone.new(Enums.ZoneType.STACK, -1) # Connect turn manager signals turn_manager.phase_changed.connect(_on_phase_changed) turn_manager.turn_started.connect(_on_turn_started) turn_manager.turn_ended.connect(_on_turn_ended) ## Initialize a new game with two players func setup_game(deck1: Array[String], deck2: Array[String]) -> void: # Create players players.clear() players.append(Player.new(0, "Player 1")) players.append(Player.new(1, "Player 2")) # Setup decks players[0].setup_deck(deck1) players[1].setup_deck(deck2) game_active = false winner_index = -1 ## Start the game func start_game(first_player: int = -1) -> void: if first_player < 0: first_player = randi() % 2 players[first_player].is_first_player = true # Draw initial hands (5 cards each) players[0].draw_cards(5) players[1].draw_cards(5) game_active = true # Connect ability system if available var ability_system = Engine.get_singleton("AbilitySystem") if ability_system == null: # Try getting from scene tree (autoload) var tree = Engine.get_main_loop() if tree and tree.root.has_node("AbilitySystem"): ability_system = tree.root.get_node("AbilitySystem") if ability_system: ability_system.connect_to_game(self) turn_manager.start_game(first_player) game_started.emit() ## Get current player func get_current_player() -> Player: return players[turn_manager.current_player_index] ## Get opponent of current player func get_opponent() -> Player: return players[1 - turn_manager.current_player_index] ## Get player by index func get_player(index: int) -> Player: if index >= 0 and index < players.size(): return players[index] return null ## Execute Active Phase func execute_active_phase() -> void: var player = get_current_player() player.activate_all() turn_manager.advance_phase() ## Execute Draw Phase func execute_draw_phase() -> void: var player = get_current_player() var draw_count = turn_manager.get_draw_count() var drawn = player.draw_cards(draw_count) # Check for loss condition (can't draw) if drawn.size() < draw_count and player.deck.is_empty(): _check_loss_conditions() turn_manager.advance_phase() ## End Main Phase func end_main_phase() -> void: # Clear any unused CP get_current_player().cp_pool.clear() turn_manager.advance_phase() ## Play a card from hand ## Returns dictionary of CP spent, or empty dict on failure func play_card(player_index: int, card: CardInstance) -> Dictionary: if not game_active: return {} if player_index != turn_manager.current_player_index: return {} if not turn_manager.is_main_phase(): return {} var player = players[player_index] # Validate and play (returns CP spent dict) var cp_spent = player.play_card(card) if not cp_spent.is_empty(): card_played.emit(card, player_index) card_moved.emit(card, Enums.ZoneType.HAND, Enums.ZoneType.FIELD_FORWARDS if card.is_forward() else Enums.ZoneType.FIELD_BACKUPS) return cp_spent return {} ## Cast a summon from hand func cast_summon(player_index: int, card: CardInstance) -> bool: if not game_active: return false if player_index != turn_manager.current_player_index: return false if not turn_manager.is_main_phase(): return false var player = players[player_index] # Cast the summon (pays cost, removes from hand) var cast_card = player.cast_summon(card) if cast_card: # Emit signal for effect resolution (TODO: implement effect system) summon_cast.emit(cast_card, player_index) # Move to break zone after casting player.break_zone.add_card(cast_card) card_moved.emit(cast_card, Enums.ZoneType.HAND, Enums.ZoneType.BREAK) return true return false ## Discard a card for CP func discard_for_cp(player_index: int, card: CardInstance) -> bool: if not game_active: return false var player = players[player_index] var element = card.get_element() if player.discard_for_cp(card): cp_generated.emit(player_index, element, 2) card_moved.emit(card, Enums.ZoneType.HAND, Enums.ZoneType.BREAK) return true return false ## Dull a backup for CP func dull_backup_for_cp(player_index: int, card: CardInstance) -> bool: if not game_active: return false var player = players[player_index] var element = card.get_element() if player.dull_backup_for_cp(card): cp_generated.emit(player_index, element, 1) return true return false ## Start Attack Phase func start_attack_phase() -> void: turn_manager.start_attack_declaration() ## Declare an attack func declare_attack(attacker: CardInstance) -> bool: if not game_active: return false if not turn_manager.is_attack_phase(): return false # Must be in DECLARATION step to declare attack if turn_manager.attack_step != Enums.AttackStep.DECLARATION: return false var player = get_current_player() if not player.field_forwards.has_card(attacker): return false if not attacker.can_attack(): return false # Dull the attacker (unless Brave) if not attacker.has_brave(): attacker.dull() attacker.attacked_this_turn = true # Update attack step (now returns bool for validation) if not turn_manager.set_attacker(attacker): # Rollback dull if state transition failed if not attacker.has_brave(): attacker.activate() attacker.attacked_this_turn = false return false attack_declared.emit(attacker) return true ## Declare a block func declare_block(blocker: CardInstance) -> bool: if not game_active: return false if turn_manager.attack_step != Enums.AttackStep.BLOCK_DECLARATION: return false var opponent = get_opponent() if blocker != null: if not opponent.field_forwards.has_card(blocker): return false if not blocker.can_block(): return false # Update attack step (now returns bool for validation) if not turn_manager.set_blocker(blocker): return false if blocker: block_declared.emit(blocker) return true ## Skip blocking func skip_block() -> bool: return declare_block(null) ## Resolve combat damage func resolve_combat() -> void: if turn_manager.attack_step != Enums.AttackStep.DAMAGE_RESOLUTION: return var attacker = turn_manager.current_attacker var blocker = turn_manager.current_blocker var player = get_current_player() # Validate attacker is still on field (could have been broken by ability) if not attacker or not player.field_forwards.has_card(attacker): turn_manager.complete_attack() return if blocker == null: # Unblocked - deal 1 damage to opponent _deal_damage_to_player(1 - turn_manager.current_player_index, 1) else: # Validate blocker is still on field var opponent = get_opponent() if not opponent.field_forwards.has_card(blocker): # Blocker was removed - attack goes through as unblocked _deal_damage_to_player(1 - turn_manager.current_player_index, 1) else: # Blocked - exchange damage var attacker_power = attacker.get_power() var blocker_power = blocker.get_power() # Apply damage var attacker_broken = attacker.apply_damage(blocker_power) var blocker_broken = blocker.apply_damage(attacker_power) # Break destroyed forwards if attacker_broken: _break_forward(attacker, turn_manager.current_player_index) if blocker_broken: _break_forward(blocker, 1 - turn_manager.current_player_index) combat_resolved.emit(attacker, blocker) turn_manager.complete_attack() ## End Attack Phase (no more attacks) func end_attack_phase() -> void: turn_manager.end_attack_phase() ## Execute End Phase func execute_end_phase() -> void: var player = get_current_player() # Discard to hand limit var discarded = player.discard_to_hand_limit() for card in discarded: card_moved.emit(card, Enums.ZoneType.HAND, Enums.ZoneType.BREAK) # Cleanup player.end_turn_cleanup() turn_manager.advance_phase() ## Deal damage to a player func _deal_damage_to_player(player_index: int, amount: int) -> void: var player = players[player_index] var damage_cards = player.take_damage(amount) damage_dealt.emit(player_index, amount, damage_cards) # Check for EX Bursts (simplified - would need full implementation) for card in damage_cards: if card.card_data and card.card_data.has_ex_burst: # TODO: Offer EX Burst choice to player pass _check_loss_conditions() ## Break a forward func _break_forward(card: CardInstance, player_index: int) -> void: var player = players[player_index] if player.break_card(card): forward_broken.emit(card) card_moved.emit(card, Enums.ZoneType.FIELD_FORWARDS, Enums.ZoneType.BREAK) ## Check for game-ending conditions func _check_loss_conditions() -> void: for i in range(players.size()): var player = players[i] # Check damage if player.has_lost(): _end_game(1 - i) # Other player wins return # Check deck out (can't draw when needed) # This is checked during draw phase ## End the game func _end_game(winner: int) -> void: game_active = false winner_index = winner game_ended.emit(winner) ## Phase change handler func _on_phase_changed(phase: Enums.TurnPhase) -> void: match phase: Enums.TurnPhase.ACTIVE: execute_active_phase() Enums.TurnPhase.DRAW: execute_draw_phase() Enums.TurnPhase.ATTACK: start_attack_phase() Enums.TurnPhase.END: execute_end_phase() ## Turn started handler func _on_turn_started(player_index: int, _turn_number: int) -> void: players[player_index].start_turn() ## Turn ended handler func _on_turn_ended(_player_index: int) -> void: pass func _to_string() -> String: if not game_active: return "[GameState: Inactive]" return "[GameState: Turn %d, Phase: %s, Player %d]" % [ turn_manager.turn_number, turn_manager.get_phase_string(), turn_manager.current_player_index + 1 ]