updates for cleaning functionality
This commit is contained in:
@@ -7,7 +7,8 @@
|
|||||||
"Bash(flatpak list)",
|
"Bash(flatpak list)",
|
||||||
"Bash(snap list:*)",
|
"Bash(snap list:*)",
|
||||||
"Bash(timeout 10 godot4:*)",
|
"Bash(timeout 10 godot4:*)",
|
||||||
"Bash(ls:*)"
|
"Bash(ls:*)",
|
||||||
|
"Bash(timeout 5 godot4:*)"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,14 +30,14 @@ dock_5="Inspector,Node,History"
|
|||||||
open_scenes=PackedStringArray("res://scenes/main.tscn")
|
open_scenes=PackedStringArray("res://scenes/main.tscn")
|
||||||
current_scene="res://scenes/main.tscn"
|
current_scene="res://scenes/main.tscn"
|
||||||
center_split_offset=0
|
center_split_offset=0
|
||||||
selected_default_debugger_tab_idx=1
|
selected_default_debugger_tab_idx=0
|
||||||
selected_main_editor_idx=2
|
selected_main_editor_idx=2
|
||||||
selected_bottom_panel_item=1
|
selected_bottom_panel_item=0
|
||||||
|
|
||||||
[ScriptEditor]
|
[ScriptEditor]
|
||||||
|
|
||||||
open_scripts=["res://scripts/autoload/CardDatabase.gd", "res://scripts/ui/HandDisplay.gd", "res://scripts/Main.gd", "res://scripts/ui/PauseMenu.gd", "res://scripts/visual/TableSetup.gd"]
|
open_scripts=["res://scripts/ui/ActionLog.gd", "res://scripts/autoload/CardDatabase.gd", "res://scripts/ui/HandDisplay.gd", "res://scripts/Main.gd", "res://scripts/ui/PauseMenu.gd", "res://scripts/visual/TableSetup.gd", "res://scripts/game/UndoSystem.gd"]
|
||||||
selected_script="res://scripts/ui/HandDisplay.gd"
|
selected_script="res://scripts/ui/ActionLog.gd"
|
||||||
open_help=[]
|
open_help=[]
|
||||||
script_split_offset=140
|
script_split_offset=140
|
||||||
list_split_offset=0
|
list_split_offset=0
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ run_reload_scripts=true
|
|||||||
[recent_files]
|
[recent_files]
|
||||||
|
|
||||||
scenes=["res://scenes/main.tscn"]
|
scenes=["res://scenes/main.tscn"]
|
||||||
scripts=["res://scripts/ui/HandDisplay.gd", "res://scripts/ui/PauseMenu.gd", "res://scripts/visual/TableSetup.gd", "res://scripts/autoload/CardDatabase.gd", "res://scripts/Main.gd"]
|
scripts=["res://scripts/ui/ActionLog.gd", "res://scripts/game/UndoSystem.gd", "res://scripts/ui/HandDisplay.gd", "res://scripts/ui/PauseMenu.gd", "res://scripts/visual/TableSetup.gd", "res://scripts/autoload/CardDatabase.gd", "res://scripts/Main.gd"]
|
||||||
|
|
||||||
[linked_properties]
|
[linked_properties]
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ state={
|
|||||||
"column": 0,
|
"column": 0,
|
||||||
"folded_lines": Array[int]([]),
|
"folded_lines": Array[int]([]),
|
||||||
"h_scroll_position": 0,
|
"h_scroll_position": 0,
|
||||||
"row": 68,
|
"row": 75,
|
||||||
"scroll_position": 64.0,
|
"scroll_position": 64.0,
|
||||||
"selection": false,
|
"selection": false,
|
||||||
"syntax_highlighter": "GDScript"
|
"syntax_highlighter": "GDScript"
|
||||||
@@ -63,7 +63,35 @@ state={
|
|||||||
"folded_lines": Array[int]([]),
|
"folded_lines": Array[int]([]),
|
||||||
"h_scroll_position": 0,
|
"h_scroll_position": 0,
|
||||||
"row": 166,
|
"row": 166,
|
||||||
"scroll_position": 166.0,
|
"scroll_position": 161.0,
|
||||||
|
"selection": false,
|
||||||
|
"syntax_highlighter": "GDScript"
|
||||||
|
}
|
||||||
|
|
||||||
|
[res://scripts/game/UndoSystem.gd]
|
||||||
|
|
||||||
|
state={
|
||||||
|
"bookmarks": PackedInt32Array(),
|
||||||
|
"breakpoints": PackedInt32Array(),
|
||||||
|
"column": 0,
|
||||||
|
"folded_lines": Array[int]([]),
|
||||||
|
"h_scroll_position": 0,
|
||||||
|
"row": 71,
|
||||||
|
"scroll_position": 71.0,
|
||||||
|
"selection": false,
|
||||||
|
"syntax_highlighter": "GDScript"
|
||||||
|
}
|
||||||
|
|
||||||
|
[res://scripts/ui/ActionLog.gd]
|
||||||
|
|
||||||
|
state={
|
||||||
|
"bookmarks": PackedInt32Array(),
|
||||||
|
"breakpoints": PackedInt32Array(),
|
||||||
|
"column": 0,
|
||||||
|
"folded_lines": Array[int]([]),
|
||||||
|
"h_scroll_position": 0,
|
||||||
|
"row": 225,
|
||||||
|
"scroll_position": 220.0,
|
||||||
"selection": false,
|
"selection": false,
|
||||||
"syntax_highlighter": "GDScript"
|
"syntax_highlighter": "GDScript"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,10 @@
|
|||||||
list=Array[Dictionary]([{
|
list=Array[Dictionary]([{
|
||||||
|
"base": &"Control",
|
||||||
|
"class": &"ActionLog",
|
||||||
|
"icon": "",
|
||||||
|
"language": &"GDScript",
|
||||||
|
"path": "res://scripts/ui/ActionLog.gd"
|
||||||
|
}, {
|
||||||
"base": &"RefCounted",
|
"base": &"RefCounted",
|
||||||
"class": &"CPPool",
|
"class": &"CPPool",
|
||||||
"icon": "",
|
"icon": "",
|
||||||
@@ -84,6 +90,12 @@ list=Array[Dictionary]([{
|
|||||||
"path": "res://scripts/game/TurnManager.gd"
|
"path": "res://scripts/game/TurnManager.gd"
|
||||||
}, {
|
}, {
|
||||||
"base": &"RefCounted",
|
"base": &"RefCounted",
|
||||||
|
"class": &"UndoSystem",
|
||||||
|
"icon": "",
|
||||||
|
"language": &"GDScript",
|
||||||
|
"path": "res://scripts/game/UndoSystem.gd"
|
||||||
|
}, {
|
||||||
|
"base": &"RefCounted",
|
||||||
"class": &"Zone",
|
"class": &"Zone",
|
||||||
"icon": "",
|
"icon": "",
|
||||||
"language": &"GDScript",
|
"language": &"GDScript",
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ var table_setup: TableSetup
|
|||||||
var game_ui: GameUI
|
var game_ui: GameUI
|
||||||
var hand_display: HandDisplay
|
var hand_display: HandDisplay
|
||||||
var hand_layer: CanvasLayer
|
var hand_layer: CanvasLayer
|
||||||
|
var action_log: ActionLog
|
||||||
|
|
||||||
# Player damage displays
|
# Player damage displays
|
||||||
var damage_displays: Array[DamageDisplay] = []
|
var damage_displays: Array[DamageDisplay] = []
|
||||||
@@ -35,6 +36,29 @@ func _setup_ui() -> void:
|
|||||||
game_ui.layer = 10 # Base UI layer
|
game_ui.layer = 10 # Base UI layer
|
||||||
add_child(game_ui)
|
add_child(game_ui)
|
||||||
|
|
||||||
|
# Action log (collapsible panel on right side)
|
||||||
|
var log_layer = CanvasLayer.new()
|
||||||
|
log_layer.layer = 12 # Above other UI
|
||||||
|
add_child(log_layer)
|
||||||
|
|
||||||
|
action_log = ActionLog.new()
|
||||||
|
log_layer.add_child(action_log)
|
||||||
|
# Position on right side of screen
|
||||||
|
action_log.set_anchors_preset(Control.PRESET_CENTER_RIGHT)
|
||||||
|
action_log.offset_left = -290
|
||||||
|
action_log.offset_right = -10
|
||||||
|
action_log.offset_top = -200
|
||||||
|
action_log.offset_bottom = 200
|
||||||
|
|
||||||
|
# Connect undo signal
|
||||||
|
action_log.undo_requested.connect(_on_undo_requested)
|
||||||
|
|
||||||
|
# Connect to GameManager undo signals
|
||||||
|
GameManager.undo_available_changed.connect(_on_undo_available_changed)
|
||||||
|
|
||||||
|
# Ensure ActionLog connects to GameManager after it's in the tree
|
||||||
|
action_log.call_deferred("_connect_game_manager_signals")
|
||||||
|
|
||||||
# Hand display needs its own CanvasLayer to render on top of 3D
|
# Hand display needs its own CanvasLayer to render on top of 3D
|
||||||
hand_layer = CanvasLayer.new()
|
hand_layer = CanvasLayer.new()
|
||||||
hand_layer.layer = 11 # Above the game UI
|
hand_layer.layer = 11 # Above the game UI
|
||||||
@@ -71,6 +95,18 @@ func _position_hand_display() -> void:
|
|||||||
hand_display.position = Vector2(50, vp_size.y - 180)
|
hand_display.position = Vector2(50, vp_size.y - 180)
|
||||||
hand_display.size = Vector2(vp_size.x - 100, 170)
|
hand_display.size = Vector2(vp_size.x - 100, 170)
|
||||||
|
|
||||||
|
# Connect to viewport size changed signal if not already connected
|
||||||
|
if not viewport.size_changed.is_connected(_on_viewport_resized):
|
||||||
|
viewport.size_changed.connect(_on_viewport_resized)
|
||||||
|
|
||||||
|
func _on_viewport_resized() -> void:
|
||||||
|
# Reposition hand display when window resizes
|
||||||
|
var viewport = get_viewport()
|
||||||
|
if viewport and hand_display:
|
||||||
|
var vp_size = viewport.get_visible_rect().size
|
||||||
|
hand_display.position = Vector2(50, vp_size.y - 180)
|
||||||
|
hand_display.size = Vector2(vp_size.x - 100, 170)
|
||||||
|
|
||||||
func _connect_signals() -> void:
|
func _connect_signals() -> void:
|
||||||
# GameManager signals
|
# GameManager signals
|
||||||
GameManager.game_started.connect(_on_game_started)
|
GameManager.game_started.connect(_on_game_started)
|
||||||
@@ -157,12 +193,10 @@ func _update_playable_highlights() -> void:
|
|||||||
hand_display.clear_highlights()
|
hand_display.clear_highlights()
|
||||||
|
|
||||||
func _on_hand_card_selected(card: CardInstance) -> void:
|
func _on_hand_card_selected(card: CardInstance) -> void:
|
||||||
print("Main: Card selected: ", card.card_data.name, " input_mode=", GameManager.input_mode)
|
|
||||||
var input_mode = GameManager.input_mode
|
var input_mode = GameManager.input_mode
|
||||||
|
|
||||||
match input_mode:
|
match input_mode:
|
||||||
GameManager.InputMode.SELECT_CARD_TO_PLAY:
|
GameManager.InputMode.SELECT_CARD_TO_PLAY:
|
||||||
print("Main: Trying to play card")
|
|
||||||
GameManager.try_play_card(card)
|
GameManager.try_play_card(card)
|
||||||
_sync_visuals()
|
_sync_visuals()
|
||||||
_update_hand_display()
|
_update_hand_display()
|
||||||
@@ -170,12 +204,9 @@ func _on_hand_card_selected(card: CardInstance) -> void:
|
|||||||
|
|
||||||
GameManager.InputMode.SELECT_CP_SOURCE:
|
GameManager.InputMode.SELECT_CP_SOURCE:
|
||||||
# Discard for CP
|
# Discard for CP
|
||||||
print("Main: Discarding for CP")
|
|
||||||
GameManager.discard_card_for_cp(card)
|
GameManager.discard_card_for_cp(card)
|
||||||
_update_hand_display()
|
_update_hand_display()
|
||||||
_update_cp_display()
|
_update_cp_display()
|
||||||
_:
|
|
||||||
print("Main: Input mode not handled: ", input_mode)
|
|
||||||
|
|
||||||
func _on_hand_card_hovered(card: CardInstance) -> void:
|
func _on_hand_card_hovered(card: CardInstance) -> void:
|
||||||
game_ui.show_card_detail(card)
|
game_ui.show_card_detail(card)
|
||||||
@@ -183,6 +214,19 @@ func _on_hand_card_hovered(card: CardInstance) -> void:
|
|||||||
func _on_hand_card_unhovered() -> void:
|
func _on_hand_card_unhovered() -> void:
|
||||||
game_ui.hide_card_detail()
|
game_ui.hide_card_detail()
|
||||||
|
|
||||||
|
func _on_undo_requested() -> void:
|
||||||
|
if GameManager.undo_last_action():
|
||||||
|
_sync_visuals()
|
||||||
|
_update_hand_display()
|
||||||
|
_update_cp_display()
|
||||||
|
_update_playable_highlights()
|
||||||
|
# Restore input mode based on current phase
|
||||||
|
GameManager.restore_input_mode_for_phase()
|
||||||
|
|
||||||
|
func _on_undo_available_changed(available: bool) -> void:
|
||||||
|
if action_log:
|
||||||
|
action_log.set_undo_available(available)
|
||||||
|
|
||||||
func _on_table_card_clicked(card: CardInstance, zone_type: Enums.ZoneType, player_index: int) -> void:
|
func _on_table_card_clicked(card: CardInstance, zone_type: Enums.ZoneType, player_index: int) -> void:
|
||||||
var input_mode = GameManager.input_mode
|
var input_mode = GameManager.input_mode
|
||||||
|
|
||||||
@@ -216,6 +260,17 @@ func _on_table_card_clicked(card: CardInstance, zone_type: Enums.ZoneType, playe
|
|||||||
func _input(event: InputEvent) -> void:
|
func _input(event: InputEvent) -> void:
|
||||||
# Keyboard shortcuts
|
# Keyboard shortcuts
|
||||||
if event is InputEventKey and event.pressed:
|
if event is InputEventKey and event.pressed:
|
||||||
|
# Ctrl+Z for undo
|
||||||
|
if event.keycode == KEY_Z and event.ctrl_pressed:
|
||||||
|
_on_undo_requested()
|
||||||
|
return
|
||||||
|
|
||||||
|
# L key to toggle action log
|
||||||
|
if event.keycode == KEY_L and not event.ctrl_pressed:
|
||||||
|
if action_log:
|
||||||
|
action_log.toggle_panel()
|
||||||
|
return
|
||||||
|
|
||||||
match event.keycode:
|
match event.keycode:
|
||||||
KEY_SPACE:
|
KEY_SPACE:
|
||||||
# Pass priority / end phase
|
# Pass priority / end phase
|
||||||
|
|||||||
@@ -11,10 +11,15 @@ signal phase_changed(phase_name: String)
|
|||||||
signal card_played(card_data: Dictionary)
|
signal card_played(card_data: Dictionary)
|
||||||
signal damage_dealt(player_name: String, amount: int)
|
signal damage_dealt(player_name: String, amount: int)
|
||||||
signal message(text: String)
|
signal message(text: String)
|
||||||
|
signal action_undone(action_name: String)
|
||||||
|
signal undo_available_changed(available: bool)
|
||||||
|
|
||||||
# Game state
|
# Game state
|
||||||
var game_state: GameState = null
|
var game_state: GameState = null
|
||||||
|
|
||||||
|
# Undo system
|
||||||
|
var undo_system: UndoSystem = null
|
||||||
|
|
||||||
# State flags
|
# State flags
|
||||||
var is_initialized: bool = false
|
var is_initialized: bool = false
|
||||||
var is_game_active: bool = false
|
var is_game_active: bool = false
|
||||||
@@ -55,6 +60,11 @@ func start_new_game() -> void:
|
|||||||
# Create new game state
|
# Create new game state
|
||||||
game_state = GameState.new()
|
game_state = GameState.new()
|
||||||
|
|
||||||
|
# Create undo system
|
||||||
|
undo_system = UndoSystem.new(game_state)
|
||||||
|
undo_system.undo_available_changed.connect(_on_undo_available_changed)
|
||||||
|
undo_system.action_undone.connect(_on_action_undone)
|
||||||
|
|
||||||
# Connect signals
|
# Connect signals
|
||||||
_connect_game_signals()
|
_connect_game_signals()
|
||||||
|
|
||||||
@@ -131,21 +141,67 @@ func try_play_card(card: CardInstance) -> bool:
|
|||||||
selected_card = card
|
selected_card = card
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
# Check play restrictions before attempting to play
|
||||||
|
var play_error = _check_play_restrictions(player, card)
|
||||||
|
if play_error != "":
|
||||||
|
message.emit(play_error)
|
||||||
|
return false
|
||||||
|
|
||||||
|
# Determine target zone
|
||||||
|
var to_zone = Enums.ZoneType.FIELD_FORWARDS if card.is_forward() else Enums.ZoneType.FIELD_BACKUPS
|
||||||
|
|
||||||
# Try to play
|
# Try to play
|
||||||
if game_state.play_card(player_index, card):
|
if game_state.play_card(player_index, card):
|
||||||
|
# Record for undo
|
||||||
|
if undo_system:
|
||||||
|
undo_system.record_play_card(player_index, card, to_zone, {})
|
||||||
|
|
||||||
message.emit("Played " + card.get_display_name())
|
message.emit("Played " + card.get_display_name())
|
||||||
return true
|
return true
|
||||||
else:
|
else:
|
||||||
message.emit("Cannot play that card!")
|
# This shouldn't happen if _check_play_restrictions works correctly
|
||||||
|
# but provide context if it does
|
||||||
|
message.emit("Failed to play " + card.get_display_name() + " (internal error)")
|
||||||
return false
|
return false
|
||||||
|
|
||||||
|
## Check play restrictions and return error message, or empty string if playable
|
||||||
|
func _check_play_restrictions(player: Player, card: CardInstance) -> String:
|
||||||
|
# Check if card is in hand
|
||||||
|
if not player.hand.has_card(card):
|
||||||
|
return "Card is no longer in your hand!"
|
||||||
|
|
||||||
|
# Check backup limit
|
||||||
|
if card.is_backup():
|
||||||
|
if player.field_backups.get_count() >= Player.MAX_BACKUPS:
|
||||||
|
return "Cannot play: Maximum 5 Backups allowed!"
|
||||||
|
|
||||||
|
# Check unique name restriction (non-generic cards)
|
||||||
|
if not card.card_data.is_generic:
|
||||||
|
if card.is_forward() and player.field_forwards.has_card_with_name(card.card_data.name):
|
||||||
|
return "Cannot play: You already have " + card.card_data.name + " on the field!"
|
||||||
|
if card.is_backup() and player.field_backups.has_card_with_name(card.card_data.name):
|
||||||
|
return "Cannot play: You already have " + card.card_data.name + " on the field!"
|
||||||
|
|
||||||
|
# Check Light/Dark restriction
|
||||||
|
if card.is_light_or_dark():
|
||||||
|
if player.field_forwards.has_light_or_dark() or player.field_backups.has_light_or_dark():
|
||||||
|
return "Cannot play: You can only have one Light/Dark card on the field!"
|
||||||
|
|
||||||
|
return ""
|
||||||
|
|
||||||
## Discard a card to generate CP
|
## Discard a card to generate CP
|
||||||
func discard_card_for_cp(card: CardInstance) -> bool:
|
func discard_card_for_cp(card: CardInstance) -> bool:
|
||||||
if not game_state or not is_game_active:
|
if not game_state or not is_game_active:
|
||||||
return false
|
return false
|
||||||
|
|
||||||
var player_index = card.owner_index
|
var player_index = card.owner_index
|
||||||
|
var element = card.get_element()
|
||||||
|
|
||||||
if game_state.discard_for_cp(player_index, card):
|
if game_state.discard_for_cp(player_index, card):
|
||||||
|
# Record for undo
|
||||||
|
if undo_system:
|
||||||
|
undo_system.record_discard_for_cp(player_index, card, element)
|
||||||
|
|
||||||
message.emit("Discarded " + card.get_display_name() + " for 2 CP")
|
message.emit("Discarded " + card.get_display_name() + " for 2 CP")
|
||||||
_check_pending_action()
|
_check_pending_action()
|
||||||
return true
|
return true
|
||||||
@@ -158,7 +214,13 @@ func dull_backup_for_cp(card: CardInstance) -> bool:
|
|||||||
return false
|
return false
|
||||||
|
|
||||||
var player_index = card.controller_index
|
var player_index = card.controller_index
|
||||||
|
var element = card.get_element()
|
||||||
|
|
||||||
if game_state.dull_backup_for_cp(player_index, card):
|
if game_state.dull_backup_for_cp(player_index, card):
|
||||||
|
# Record for undo
|
||||||
|
if undo_system:
|
||||||
|
undo_system.record_dull_backup_for_cp(player_index, card, element)
|
||||||
|
|
||||||
message.emit("Dulled " + card.get_display_name() + " for 1 CP")
|
message.emit("Dulled " + card.get_display_name() + " for 1 CP")
|
||||||
_check_pending_action()
|
_check_pending_action()
|
||||||
return true
|
return true
|
||||||
@@ -169,6 +231,13 @@ func dull_backup_for_cp(card: CardInstance) -> bool:
|
|||||||
func _check_pending_action() -> void:
|
func _check_pending_action() -> void:
|
||||||
if input_mode == InputMode.SELECT_CP_SOURCE and selected_card:
|
if input_mode == InputMode.SELECT_CP_SOURCE and selected_card:
|
||||||
var player = game_state.get_player(selected_card.controller_index)
|
var player = game_state.get_player(selected_card.controller_index)
|
||||||
|
|
||||||
|
# Check if the card is still in hand (it may have been discarded for CP)
|
||||||
|
if not player.hand.has_card(selected_card):
|
||||||
|
message.emit("The card you wanted to play was discarded!")
|
||||||
|
clear_selection()
|
||||||
|
return
|
||||||
|
|
||||||
if player.cp_pool.can_afford_card(selected_card.card_data):
|
if player.cp_pool.can_afford_card(selected_card.card_data):
|
||||||
# Can now afford - try to play
|
# Can now afford - try to play
|
||||||
try_play_card(selected_card)
|
try_play_card(selected_card)
|
||||||
@@ -233,8 +302,48 @@ func clear_selection() -> void:
|
|||||||
selected_card = null
|
selected_card = null
|
||||||
pending_action = func(): pass
|
pending_action = func(): pass
|
||||||
|
|
||||||
|
## Undo the last action
|
||||||
|
func undo_last_action() -> bool:
|
||||||
|
if not undo_system:
|
||||||
|
return false
|
||||||
|
|
||||||
|
return undo_system.undo()
|
||||||
|
|
||||||
|
## Check if undo is available
|
||||||
|
func can_undo() -> bool:
|
||||||
|
if not undo_system:
|
||||||
|
return false
|
||||||
|
return undo_system.can_undo()
|
||||||
|
|
||||||
|
## Get description of last undoable action
|
||||||
|
func get_undo_description() -> String:
|
||||||
|
if not undo_system:
|
||||||
|
return ""
|
||||||
|
return undo_system.get_last_action_description()
|
||||||
|
|
||||||
|
## Restore the input mode based on the current phase
|
||||||
|
func restore_input_mode_for_phase() -> void:
|
||||||
|
if not game_state:
|
||||||
|
return
|
||||||
|
|
||||||
|
var phase = game_state.turn_manager.current_phase
|
||||||
|
match phase:
|
||||||
|
Enums.TurnPhase.MAIN_1, Enums.TurnPhase.MAIN_2:
|
||||||
|
input_mode = InputMode.SELECT_CARD_TO_PLAY
|
||||||
|
Enums.TurnPhase.ATTACK:
|
||||||
|
input_mode = InputMode.SELECT_ATTACKER
|
||||||
|
_:
|
||||||
|
input_mode = InputMode.NONE
|
||||||
|
|
||||||
## Signal handlers
|
## Signal handlers
|
||||||
|
|
||||||
|
func _on_undo_available_changed(available: bool) -> void:
|
||||||
|
undo_available_changed.emit(available)
|
||||||
|
|
||||||
|
func _on_action_undone(action_name: String) -> void:
|
||||||
|
message.emit("Undid: " + action_name)
|
||||||
|
action_undone.emit(action_name)
|
||||||
|
|
||||||
func _on_game_ended(winner_index: int) -> void:
|
func _on_game_ended(winner_index: int) -> void:
|
||||||
is_game_active = false
|
is_game_active = false
|
||||||
var winner_name = game_state.get_player(winner_index).player_name
|
var winner_name = game_state.get_player(winner_index).player_name
|
||||||
@@ -276,6 +385,10 @@ func _on_phase_changed(phase: Enums.TurnPhase) -> void:
|
|||||||
var phase_name = Enums.phase_to_string(phase)
|
var phase_name = Enums.phase_to_string(phase)
|
||||||
phase_changed.emit(phase_name)
|
phase_changed.emit(phase_name)
|
||||||
|
|
||||||
|
# Clear undo history on phase change
|
||||||
|
if undo_system:
|
||||||
|
undo_system.clear_history()
|
||||||
|
|
||||||
# Set appropriate input mode
|
# Set appropriate input mode
|
||||||
match phase:
|
match phase:
|
||||||
Enums.TurnPhase.MAIN_1, Enums.TurnPhase.MAIN_2:
|
Enums.TurnPhase.MAIN_1, Enums.TurnPhase.MAIN_2:
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ func start_game(first_player: int) -> void:
|
|||||||
is_first_turn = true
|
is_first_turn = true
|
||||||
current_phase = Enums.TurnPhase.ACTIVE
|
current_phase = Enums.TurnPhase.ACTIVE
|
||||||
turn_started.emit(current_player_index, turn_number)
|
turn_started.emit(current_player_index, turn_number)
|
||||||
|
# Emit phase_changed to trigger the initial Active phase execution
|
||||||
|
phase_changed.emit(current_phase)
|
||||||
|
|
||||||
## Advance to the next phase
|
## Advance to the next phase
|
||||||
func advance_phase() -> Enums.TurnPhase:
|
func advance_phase() -> Enums.TurnPhase:
|
||||||
|
|||||||
193
scripts/game/UndoSystem.gd
Normal file
193
scripts/game/UndoSystem.gd
Normal file
@@ -0,0 +1,193 @@
|
|||||||
|
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 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.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 CP (we don't track exact CP spent, so this is simplified)
|
||||||
|
# In a full implementation, we'd track what CP was spent
|
||||||
|
# For now, we'll add back CP equal to the card cost
|
||||||
|
var cost = card.card_data.cost
|
||||||
|
var element = card.get_element()
|
||||||
|
player.cp_pool.add_cp(element, cost)
|
||||||
|
|
||||||
|
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
|
||||||
264
scripts/ui/ActionLog.gd
Normal file
264
scripts/ui/ActionLog.gd
Normal file
@@ -0,0 +1,264 @@
|
|||||||
|
class_name ActionLog
|
||||||
|
extends Control
|
||||||
|
|
||||||
|
## ActionLog - Collapsible panel showing game actions
|
||||||
|
|
||||||
|
signal undo_requested
|
||||||
|
|
||||||
|
# UI Components
|
||||||
|
var panel: PanelContainer
|
||||||
|
var toggle_button: Button
|
||||||
|
var scroll_container: ScrollContainer
|
||||||
|
var log_container: VBoxContainer
|
||||||
|
var undo_button: Button
|
||||||
|
var clear_button: Button
|
||||||
|
|
||||||
|
# State
|
||||||
|
var is_expanded: bool = true
|
||||||
|
var action_entries: Array[Control] = []
|
||||||
|
|
||||||
|
# Layout constants
|
||||||
|
const COLLAPSED_WIDTH: float = 40.0
|
||||||
|
const EXPANDED_WIDTH: float = 280.0
|
||||||
|
const PANEL_HEIGHT: float = 400.0
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
_create_ui()
|
||||||
|
_connect_signals()
|
||||||
|
|
||||||
|
func _create_ui() -> void:
|
||||||
|
# Set up this control to anchor to right side
|
||||||
|
set_anchors_preset(Control.PRESET_CENTER_RIGHT)
|
||||||
|
custom_minimum_size = Vector2(EXPANDED_WIDTH, PANEL_HEIGHT)
|
||||||
|
size = Vector2(EXPANDED_WIDTH, PANEL_HEIGHT)
|
||||||
|
mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
|
||||||
|
# Main horizontal container
|
||||||
|
var hbox = HBoxContainer.new()
|
||||||
|
add_child(hbox)
|
||||||
|
hbox.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||||
|
hbox.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||||
|
hbox.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||||
|
hbox.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
|
||||||
|
# Toggle button on the left edge
|
||||||
|
toggle_button = Button.new()
|
||||||
|
toggle_button.text = ">"
|
||||||
|
toggle_button.custom_minimum_size = Vector2(30, 0)
|
||||||
|
toggle_button.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||||
|
toggle_button.tooltip_text = "Toggle Action Log (L)"
|
||||||
|
hbox.add_child(toggle_button)
|
||||||
|
|
||||||
|
# Panel container for the log
|
||||||
|
panel = PanelContainer.new()
|
||||||
|
hbox.add_child(panel)
|
||||||
|
panel.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||||
|
panel.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||||
|
|
||||||
|
var style = StyleBoxFlat.new()
|
||||||
|
style.bg_color = Color(0.08, 0.08, 0.12, 0.95)
|
||||||
|
style.border_color = Color(0.3, 0.3, 0.4)
|
||||||
|
style.set_border_width_all(2)
|
||||||
|
style.set_corner_radius_all(5)
|
||||||
|
style.set_content_margin_all(8)
|
||||||
|
panel.add_theme_stylebox_override("panel", style)
|
||||||
|
|
||||||
|
# Panel content
|
||||||
|
var panel_vbox = VBoxContainer.new()
|
||||||
|
panel.add_child(panel_vbox)
|
||||||
|
panel_vbox.add_theme_constant_override("separation", 5)
|
||||||
|
|
||||||
|
# Header
|
||||||
|
var header = HBoxContainer.new()
|
||||||
|
panel_vbox.add_child(header)
|
||||||
|
header.add_theme_constant_override("separation", 5)
|
||||||
|
|
||||||
|
var title = Label.new()
|
||||||
|
title.text = "Action Log"
|
||||||
|
title.add_theme_font_size_override("font_size", 14)
|
||||||
|
title.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||||
|
header.add_child(title)
|
||||||
|
|
||||||
|
# Undo button
|
||||||
|
undo_button = Button.new()
|
||||||
|
undo_button.text = "Undo"
|
||||||
|
undo_button.custom_minimum_size = Vector2(50, 25)
|
||||||
|
undo_button.add_theme_font_size_override("font_size", 11)
|
||||||
|
undo_button.tooltip_text = "Undo last action (Ctrl+Z)"
|
||||||
|
undo_button.disabled = true
|
||||||
|
header.add_child(undo_button)
|
||||||
|
|
||||||
|
# Clear button
|
||||||
|
clear_button = Button.new()
|
||||||
|
clear_button.text = "Clear"
|
||||||
|
clear_button.custom_minimum_size = Vector2(45, 25)
|
||||||
|
clear_button.add_theme_font_size_override("font_size", 11)
|
||||||
|
header.add_child(clear_button)
|
||||||
|
|
||||||
|
# Separator
|
||||||
|
var separator = HSeparator.new()
|
||||||
|
panel_vbox.add_child(separator)
|
||||||
|
|
||||||
|
# Scroll container for log entries
|
||||||
|
scroll_container = ScrollContainer.new()
|
||||||
|
panel_vbox.add_child(scroll_container)
|
||||||
|
scroll_container.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||||
|
scroll_container.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||||
|
scroll_container.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
|
||||||
|
scroll_container.custom_minimum_size = Vector2(200, 250)
|
||||||
|
|
||||||
|
# Log entries container
|
||||||
|
log_container = VBoxContainer.new()
|
||||||
|
scroll_container.add_child(log_container)
|
||||||
|
log_container.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||||
|
log_container.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||||
|
log_container.add_theme_constant_override("separation", 3)
|
||||||
|
|
||||||
|
func _connect_signals() -> void:
|
||||||
|
toggle_button.pressed.connect(_on_toggle_pressed)
|
||||||
|
undo_button.pressed.connect(_on_undo_pressed)
|
||||||
|
clear_button.pressed.connect(clear_log)
|
||||||
|
|
||||||
|
# Defer GameManager connections to ensure it's ready
|
||||||
|
call_deferred("_connect_game_manager_signals")
|
||||||
|
|
||||||
|
func _connect_game_manager_signals() -> void:
|
||||||
|
# Connect to GameManager signals
|
||||||
|
if GameManager:
|
||||||
|
if not GameManager.message.is_connected(_on_game_message):
|
||||||
|
GameManager.message.connect(_on_game_message)
|
||||||
|
if not GameManager.card_played.is_connected(_on_card_played):
|
||||||
|
GameManager.card_played.connect(_on_card_played)
|
||||||
|
if not GameManager.turn_changed.is_connected(_on_turn_changed):
|
||||||
|
GameManager.turn_changed.connect(_on_turn_changed)
|
||||||
|
if not GameManager.phase_changed.is_connected(_on_phase_changed):
|
||||||
|
GameManager.phase_changed.connect(_on_phase_changed)
|
||||||
|
if not GameManager.damage_dealt.is_connected(_on_damage_dealt):
|
||||||
|
GameManager.damage_dealt.connect(_on_damage_dealt)
|
||||||
|
if not GameManager.action_undone.is_connected(_on_action_undone):
|
||||||
|
GameManager.action_undone.connect(_on_action_undone)
|
||||||
|
|
||||||
|
func _on_toggle_pressed() -> void:
|
||||||
|
toggle_panel()
|
||||||
|
|
||||||
|
func toggle_panel() -> void:
|
||||||
|
is_expanded = not is_expanded
|
||||||
|
panel.visible = is_expanded
|
||||||
|
toggle_button.text = ">" if is_expanded else "<"
|
||||||
|
|
||||||
|
if is_expanded:
|
||||||
|
custom_minimum_size.x = EXPANDED_WIDTH
|
||||||
|
size.x = EXPANDED_WIDTH
|
||||||
|
else:
|
||||||
|
custom_minimum_size.x = COLLAPSED_WIDTH
|
||||||
|
size.x = COLLAPSED_WIDTH
|
||||||
|
|
||||||
|
func _on_undo_pressed() -> void:
|
||||||
|
undo_requested.emit()
|
||||||
|
|
||||||
|
## Add a log entry with a specific type for styling
|
||||||
|
func add_entry(text: String, entry_type: String = "info") -> void:
|
||||||
|
if not log_container:
|
||||||
|
return
|
||||||
|
|
||||||
|
var entry = _create_entry(text, entry_type)
|
||||||
|
log_container.add_child(entry)
|
||||||
|
action_entries.append(entry)
|
||||||
|
|
||||||
|
# Keep log at reasonable size
|
||||||
|
while action_entries.size() > 100:
|
||||||
|
var old_entry = action_entries.pop_front()
|
||||||
|
old_entry.queue_free()
|
||||||
|
|
||||||
|
# Scroll to bottom after adding
|
||||||
|
call_deferred("_scroll_to_bottom")
|
||||||
|
|
||||||
|
func _create_entry(text: String, entry_type: String) -> Control:
|
||||||
|
var entry_panel = PanelContainer.new()
|
||||||
|
|
||||||
|
var style = StyleBoxFlat.new()
|
||||||
|
style.set_content_margin_all(4)
|
||||||
|
style.set_corner_radius_all(3)
|
||||||
|
|
||||||
|
# Color based on type
|
||||||
|
match entry_type:
|
||||||
|
"turn":
|
||||||
|
style.bg_color = Color(0.2, 0.3, 0.4, 0.8)
|
||||||
|
"phase":
|
||||||
|
style.bg_color = Color(0.15, 0.25, 0.35, 0.6)
|
||||||
|
"action":
|
||||||
|
style.bg_color = Color(0.2, 0.2, 0.3, 0.7)
|
||||||
|
"damage":
|
||||||
|
style.bg_color = Color(0.4, 0.15, 0.15, 0.7)
|
||||||
|
"card":
|
||||||
|
style.bg_color = Color(0.15, 0.3, 0.2, 0.7)
|
||||||
|
_:
|
||||||
|
style.bg_color = Color(0.12, 0.12, 0.18, 0.5)
|
||||||
|
|
||||||
|
entry_panel.add_theme_stylebox_override("panel", style)
|
||||||
|
|
||||||
|
var label = Label.new()
|
||||||
|
label.text = text
|
||||||
|
label.add_theme_font_size_override("font_size", 11)
|
||||||
|
label.autowrap_mode = TextServer.AUTOWRAP_WORD
|
||||||
|
label.custom_minimum_size.x = 200
|
||||||
|
entry_panel.add_child(label)
|
||||||
|
|
||||||
|
return entry_panel
|
||||||
|
|
||||||
|
func _scroll_to_bottom() -> void:
|
||||||
|
await get_tree().process_frame
|
||||||
|
scroll_container.scroll_vertical = int(scroll_container.get_v_scroll_bar().max_value)
|
||||||
|
|
||||||
|
func clear_log() -> void:
|
||||||
|
for entry in action_entries:
|
||||||
|
entry.queue_free()
|
||||||
|
action_entries.clear()
|
||||||
|
|
||||||
|
## Enable or disable undo button
|
||||||
|
func set_undo_available(available: bool) -> void:
|
||||||
|
undo_button.disabled = not available
|
||||||
|
|
||||||
|
## Manually add a message (can be called from Main.gd if signal connection fails)
|
||||||
|
func log_message(text: String) -> void:
|
||||||
|
_on_game_message(text)
|
||||||
|
|
||||||
|
## Manually log a turn change
|
||||||
|
func log_turn_change(player_name: String, turn_number: int) -> void:
|
||||||
|
_on_turn_changed(player_name, turn_number)
|
||||||
|
|
||||||
|
## Manually log a phase change
|
||||||
|
func log_phase_change(phase_name: String) -> void:
|
||||||
|
_on_phase_changed(phase_name)
|
||||||
|
|
||||||
|
## Signal handlers
|
||||||
|
func _on_game_message(text: String) -> void:
|
||||||
|
# Determine entry type based on message content
|
||||||
|
var entry_type = "info"
|
||||||
|
if "attacks" in text or "blocks" in text:
|
||||||
|
entry_type = "action"
|
||||||
|
elif "damage" in text or "broken" in text:
|
||||||
|
entry_type = "damage"
|
||||||
|
elif "Played" in text or "Discarded" in text or "Dulled" in text:
|
||||||
|
entry_type = "card"
|
||||||
|
|
||||||
|
add_entry(text, entry_type)
|
||||||
|
|
||||||
|
func _on_card_played(_card_data: Dictionary) -> void:
|
||||||
|
# Already handled by message signal
|
||||||
|
pass
|
||||||
|
|
||||||
|
func _on_turn_changed(player_name: String, turn_number: int) -> void:
|
||||||
|
add_entry("=== " + player_name + " - Turn " + str(turn_number) + " ===", "turn")
|
||||||
|
|
||||||
|
func _on_phase_changed(phase_name: String) -> void:
|
||||||
|
add_entry(phase_name, "phase")
|
||||||
|
|
||||||
|
func _on_damage_dealt(_player_name: String, _amount: int) -> void:
|
||||||
|
# Already handled by message signal
|
||||||
|
pass
|
||||||
|
|
||||||
|
func _on_action_undone(action_name: String) -> void:
|
||||||
|
add_entry("UNDO: " + action_name, "action")
|
||||||
|
|
||||||
|
# Input is handled by Main.gd to avoid duplicate handling
|
||||||
@@ -126,17 +126,29 @@ func _create_ui() -> void:
|
|||||||
end_phase_button.custom_minimum_size = Vector2(100, 40)
|
end_phase_button.custom_minimum_size = Vector2(100, 40)
|
||||||
action_buttons.add_child(end_phase_button)
|
action_buttons.add_child(end_phase_button)
|
||||||
|
|
||||||
# === MESSAGE PANEL (center, above hand) ===
|
# === MESSAGE PANEL (upper center of screen, below top bar) ===
|
||||||
|
# Use a container to properly center the message
|
||||||
|
var message_container = Control.new()
|
||||||
|
root.add_child(message_container)
|
||||||
|
message_container.set_anchors_preset(Control.PRESET_TOP_WIDE)
|
||||||
|
message_container.offset_top = 90
|
||||||
|
message_container.offset_bottom = 160
|
||||||
|
message_container.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
|
||||||
|
var message_center = CenterContainer.new()
|
||||||
|
message_container.add_child(message_center)
|
||||||
|
message_center.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||||
|
message_center.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
|
||||||
message_panel = _create_panel()
|
message_panel = _create_panel()
|
||||||
root.add_child(message_panel)
|
message_center.add_child(message_panel)
|
||||||
message_panel.set_anchors_preset(Control.PRESET_CENTER)
|
message_panel.custom_minimum_size = Vector2(350, 50)
|
||||||
message_panel.position = Vector2(-100, 200)
|
|
||||||
message_panel.custom_minimum_size = Vector2(200, 40)
|
|
||||||
|
|
||||||
message_label = Label.new()
|
message_label = Label.new()
|
||||||
message_label.text = ""
|
message_label.text = ""
|
||||||
message_label.add_theme_font_size_override("font_size", 16)
|
message_label.add_theme_font_size_override("font_size", 20)
|
||||||
message_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
message_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
|
message_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||||
message_panel.add_child(message_label)
|
message_panel.add_child(message_label)
|
||||||
message_panel.visible = false
|
message_panel.visible = false
|
||||||
|
|
||||||
|
|||||||
@@ -191,9 +191,7 @@ func _layout_cards() -> void:
|
|||||||
func _on_card_gui_input(event: InputEvent, index: int) -> void:
|
func _on_card_gui_input(event: InputEvent, index: int) -> void:
|
||||||
if event is InputEventMouseButton:
|
if event is InputEventMouseButton:
|
||||||
if event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
|
if event.button_index == MOUSE_BUTTON_LEFT and event.pressed:
|
||||||
print("HandDisplay: Card clicked, index=", index)
|
|
||||||
if index >= 0 and index < card_instances.size():
|
if index >= 0 and index < card_instances.size():
|
||||||
print("HandDisplay: Emitting card_selected for ", card_instances[index].card_data.name)
|
|
||||||
card_selected.emit(card_instances[index])
|
card_selected.emit(card_instances[index])
|
||||||
|
|
||||||
func _on_card_mouse_entered(index: int) -> void:
|
func _on_card_mouse_entered(index: int) -> void:
|
||||||
|
|||||||
Reference in New Issue
Block a user