class_name UndoSystem extends RefCounted ## UndoSystem - Tracks the last action for undo capability signal undo_available_changed(available: bool) signal action_undone(action_name: String) # Action types that can be undone enum ActionType { NONE, DISCARD_FOR_CP, DULL_BACKUP_FOR_CP, PLAY_CARD } # Stored action data for undo class UndoAction: var type: ActionType = ActionType.NONE var player_index: int = -1 var card: CardInstance = null var description: String = "" # Additional data depending on action type var cp_element: Enums.Element = Enums.Element.FIRE var cp_amount: int = 0 var cp_spent: Dictionary = {} # Tracks CP spent by element for proper refund var previous_card_state: Enums.CardState = Enums.CardState.ACTIVE var from_zone: Enums.ZoneType = Enums.ZoneType.HAND var to_zone: Enums.ZoneType = Enums.ZoneType.FIELD_FORWARDS var last_action: UndoAction = null var game_state: GameState = null func _init(state: GameState = null) -> void: game_state = state ## Set the game state reference func set_game_state(state: GameState) -> void: game_state = state ## Check if undo is available func can_undo() -> bool: return last_action != null and last_action.type != ActionType.NONE ## Record a discard for CP action func record_discard_for_cp(player_index: int, card: CardInstance, element: Enums.Element) -> void: var action = UndoAction.new() action.type = ActionType.DISCARD_FOR_CP action.player_index = player_index action.card = card action.cp_element = element action.cp_amount = 2 action.description = "Discard " + card.get_display_name() + " for CP" last_action = action undo_available_changed.emit(true) ## Record a dull backup for CP action func record_dull_backup_for_cp(player_index: int, card: CardInstance, element: Enums.Element) -> void: var action = UndoAction.new() action.type = ActionType.DULL_BACKUP_FOR_CP action.player_index = player_index action.card = card action.cp_element = element action.cp_amount = 1 action.previous_card_state = Enums.CardState.ACTIVE # Was active before dulling action.description = "Dull " + card.get_display_name() + " for CP" last_action = action undo_available_changed.emit(true) ## Record playing a card func record_play_card(player_index: int, card: CardInstance, to_zone: Enums.ZoneType, cp_spent: Dictionary) -> void: var action = UndoAction.new() action.type = ActionType.PLAY_CARD action.player_index = player_index action.card = card action.from_zone = Enums.ZoneType.HAND action.to_zone = to_zone action.cp_spent = cp_spent # Store actual CP spent for proper refund action.description = "Play " + card.get_display_name() last_action = action undo_available_changed.emit(true) ## Clear the undo history (called when phase changes, combat happens, etc.) func clear_history() -> void: last_action = null undo_available_changed.emit(false) ## Execute undo of the last action func undo() -> bool: if not can_undo() or not game_state: return false var action = last_action var success = false match action.type: ActionType.DISCARD_FOR_CP: success = _undo_discard_for_cp(action) ActionType.DULL_BACKUP_FOR_CP: success = _undo_dull_backup_for_cp(action) ActionType.PLAY_CARD: success = _undo_play_card(action) if success: var description = action.description last_action = null undo_available_changed.emit(false) action_undone.emit(description) return success ## Undo a discard for CP action func _undo_discard_for_cp(action: UndoAction) -> bool: var player = game_state.get_player(action.player_index) if not player: return false # Remove card from break zone if not player.break_zone.has_card(action.card): return false player.break_zone.remove_card(action.card) # Add card back to hand player.hand.add_card(action.card) # Remove the CP that was generated player.cp_pool.add_cp(action.cp_element, -action.cp_amount) return true ## Undo a dull backup for CP action func _undo_dull_backup_for_cp(action: UndoAction) -> bool: var player = game_state.get_player(action.player_index) if not player: return false # Check card is still on field if not player.field_backups.has_card(action.card): return false # Reactivate the backup action.card.activate() # Remove the CP that was generated player.cp_pool.add_cp(action.cp_element, -action.cp_amount) return true ## Undo playing a card func _undo_play_card(action: UndoAction) -> bool: var player = game_state.get_player(action.player_index) if not player: return false var card = action.card # Remove from field var removed = false if action.to_zone == Enums.ZoneType.FIELD_FORWARDS: if player.field_forwards.has_card(card): player.field_forwards.remove_card(card) removed = true elif action.to_zone == Enums.ZoneType.FIELD_BACKUPS: if player.field_backups.has_card(card): player.field_backups.remove_card(card) removed = true if not removed: return false # Return to hand player.hand.add_card(card) # Reset card state card.state = Enums.CardState.ACTIVE card.turns_on_field = 0 # Refund the actual CP spent (by element) if action.cp_spent.is_empty(): # Fallback: refund card cost as primary element (legacy behavior) var cost = card.card_data.cost var element = card.get_element() player.cp_pool.add_cp(element, cost) else: # Refund each element's CP properly for element in action.cp_spent: player.cp_pool.add_cp(element, action.cp_spent[element]) return true ## Get description of the last action (for display) func get_last_action_description() -> String: if not can_undo(): return "" return last_action.description