class_name AIController extends Node ## AIController - Coordinates AI player turns ## Handles timing, action execution, and phase transitions signal ai_action_started signal ai_action_completed signal ai_thinking(player_index: int) var strategy: AIStrategy var game_state: GameState var player_index: int var is_processing: bool = false # Reference to GameManager for executing actions var _game_manager: Node func _init() -> void: pass func setup(p_player_index: int, difficulty: AIStrategy.Difficulty, p_game_manager: Node) -> void: player_index = p_player_index _game_manager = p_game_manager # Create appropriate strategy based on difficulty match difficulty: AIStrategy.Difficulty.EASY: strategy = EasyAI.new(player_index) AIStrategy.Difficulty.NORMAL: strategy = NormalAI.new(player_index) AIStrategy.Difficulty.HARD: strategy = HardAI.new(player_index) func set_game_state(state: GameState) -> void: game_state = state if strategy: strategy.set_game_state(state) ## Called when it's the AI's turn to act in the current phase func process_turn() -> void: if is_processing: return is_processing = true ai_thinking.emit(player_index) # Add thinking delay var delay := strategy.get_thinking_delay() await get_tree().create_timer(delay).timeout var phase := game_state.turn_manager.current_phase match phase: Enums.TurnPhase.ACTIVE: # Active phase is automatic - no AI decision needed _pass_priority() Enums.TurnPhase.DRAW: # Draw phase is automatic _pass_priority() Enums.TurnPhase.MAIN_1, Enums.TurnPhase.MAIN_2: await _process_main_phase() Enums.TurnPhase.ATTACK: await _process_attack_phase() Enums.TurnPhase.END: # End phase is automatic _pass_priority() is_processing = false ai_action_completed.emit() ## Process main phase - play cards or pass func _process_main_phase() -> void: var max_actions := 10 # Prevent infinite loops var actions_taken := 0 while actions_taken < max_actions: var decision := strategy.decide_main_phase_action() if decision.action == "pass": _pass_priority() break elif decision.action == "play": var card: CardInstance = decision.card var success := await _try_play_card(card) if not success: # Couldn't play - pass _pass_priority() break # Small delay between actions await get_tree().create_timer(0.3).timeout actions_taken += 1 ## Try to play a card, handling CP generation if needed func _try_play_card(card: CardInstance) -> bool: var player := game_state.get_player(player_index) var cost := card.card_data.cost # Check if we have enough CP var current_cp := player.cp_pool.get_total_cp() if current_cp < cost: # Need to generate CP var needed := cost - current_cp var success := await _generate_cp(needed, card.card_data.elements) if not success: return false # Try to play the card return _game_manager.try_play_card(card) ## Generate CP by dulling backups or discarding cards func _generate_cp(needed: int, elements: Array) -> bool: var generated := 0 var max_attempts := 20 while generated < needed and max_attempts > 0: var decision := strategy.decide_cp_generation({ "needed": needed - generated, "elements": elements }) if decision.is_empty(): return false if decision.action == "dull_backup": var backup: CardInstance = decision.card if _game_manager.dull_backup_for_cp(backup): generated += 1 await get_tree().create_timer(0.2).timeout elif decision.action == "discard": var discard_card: CardInstance = decision.card if _game_manager.discard_card_for_cp(discard_card): generated += 2 await get_tree().create_timer(0.2).timeout max_attempts -= 1 return generated >= needed ## Process attack phase - declare attacks func _process_attack_phase() -> void: var attack_step := game_state.turn_manager.attack_step match attack_step: Enums.AttackStep.PREPARATION, Enums.AttackStep.DECLARATION: await _process_attack_declaration() Enums.AttackStep.BLOCK_DECLARATION: # This shouldn't happen - AI blocks are handled in opponent's turn _pass_priority() Enums.AttackStep.DAMAGE_RESOLUTION: # Automatic _pass_priority() ## Declare attacks with forwards func _process_attack_declaration() -> void: var max_attacks := 5 var attacks_made := 0 while attacks_made < max_attacks: var decision := strategy.decide_attack_action() if decision.action == "end_attacks": # End attack phase _game_manager.pass_priority() break elif decision.action == "attack": var attacker: CardInstance = decision.card var success := _game_manager.declare_attack(attacker) if success: attacks_made += 1 # Wait for block decision or damage resolution await get_tree().create_timer(0.5).timeout else: # Couldn't attack - end attacks _game_manager.pass_priority() break ## Called when AI needs to decide on blocking func process_block_decision(attacker: CardInstance) -> void: if is_processing: return is_processing = true ai_thinking.emit(player_index) var delay := strategy.get_thinking_delay() await get_tree().create_timer(delay).timeout var decision := strategy.decide_block_action(attacker) if decision.action == "block": var blocker: CardInstance = decision.card _game_manager.declare_block(blocker) else: _game_manager.skip_block() is_processing = false ai_action_completed.emit() func _pass_priority() -> void: _game_manager.pass_priority()