feature updates

This commit is contained in:
2026-02-02 16:28:53 -05:00
parent bf9aa3fa23
commit 44c06530ac
83 changed files with 282641 additions and 11251 deletions

View File

@@ -0,0 +1,223 @@
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()