layout, card, and design camera fixes
This commit is contained in:
@@ -19,8 +19,8 @@ dock_filesystem_split=0
|
|||||||
dock_filesystem_display_mode=0
|
dock_filesystem_display_mode=0
|
||||||
dock_filesystem_file_sort=0
|
dock_filesystem_file_sort=0
|
||||||
dock_filesystem_file_list_display_mode=1
|
dock_filesystem_file_list_display_mode=1
|
||||||
dock_filesystem_selected_paths=PackedStringArray("res://scripts/visual/TableCamera.gd")
|
dock_filesystem_selected_paths=PackedStringArray("res://scripts/ui/GameUI.gd")
|
||||||
dock_filesystem_uncollapsed_paths=PackedStringArray("Favorites", "res://", "res://scripts/", "res://scripts/visual/")
|
dock_filesystem_uncollapsed_paths=PackedStringArray("Favorites", "res://", "res://scripts/", "res://scripts/visual/", "res://scripts/ui/")
|
||||||
dock_3="Scene,Import"
|
dock_3="Scene,Import"
|
||||||
dock_4="FileSystem"
|
dock_4="FileSystem"
|
||||||
dock_5="Inspector,Node,History"
|
dock_5="Inspector,Node,History"
|
||||||
@@ -36,8 +36,8 @@ selected_bottom_panel_item=0
|
|||||||
|
|
||||||
[ScriptEditor]
|
[ScriptEditor]
|
||||||
|
|
||||||
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/TableCamera.gd", "res://scripts/visual/TableSetup.gd", "res://scripts/game/UndoSystem.gd"]
|
open_scripts=["res://scripts/ui/ActionLog.gd", "res://scripts/autoload/CardDatabase.gd", "res://scripts/game/CardInstance.gd", "res://scripts/game/CPPool.gd", "res://scripts/game/GameState.gd", "res://scripts/ui/GameUI.gd", "res://scripts/ui/HandDisplay.gd", "res://scripts/Main.gd", "res://scripts/ui/PauseMenu.gd", "res://scripts/game/Player.gd", "res://scripts/visual/TableCamera.gd", "res://scripts/visual/TableSetup.gd", "res://scripts/game/UndoSystem.gd"]
|
||||||
selected_script="res://scripts/visual/TableCamera.gd"
|
selected_script="res://scripts/ui/GameUI.gd"
|
||||||
open_help=[]
|
open_help=[]
|
||||||
script_split_offset=140
|
script_split_offset=140
|
||||||
list_split_offset=0
|
list_split_offset=0
|
||||||
|
|||||||
@@ -1,17 +1,19 @@
|
|||||||
ea4bc82a6ad023ab7ee23ee620429895
|
ea4bc82a6ad023ab7ee23ee620429895
|
||||||
::res://::1769464020
|
::res://::1769470366
|
||||||
background_1.png::CompressedTexture2D::259206091835802070::1769464008::1769464212::1::::<><>::
|
background_1.png::CompressedTexture2D::259206091835802070::1769464008::1769464212::1::::<><>::
|
||||||
|
card_back.png::CompressedTexture2D::4833498016096001590::1769466370::1769466517::1::::<><>::
|
||||||
FF14_Playmat__12516.webp::CompressedTexture2D::1641665221299209414::1769277769::1769280957::1::::<><>::
|
FF14_Playmat__12516.webp::CompressedTexture2D::1641665221299209414::1769277769::1769280957::1::::<><>::
|
||||||
FF_mat_option_1.png::CompressedTexture2D::4359709237641823626::1769451897::1769453057::1::::<><>::
|
FF_mat_option_1.png::CompressedTexture2D::4359709237641823626::1769451897::1769453057::1::::<><>::
|
||||||
README.md::TextFile::-1::1769279531::0::1::::<><>::
|
README.md::TextFile::-1::1769279531::0::1::::<><>::
|
||||||
Screenshot 2026-01-24 at 12-53-03 Untitled-3 - fftcgrulesheet-en.pdf.png::CompressedTexture2D::5958662832102035034::1769277183::1769280957::1::::<><>::
|
Screenshot 2026-01-24 at 12-53-03 Untitled-3 - fftcgrulesheet-en.pdf.png::CompressedTexture2D::5958662832102035034::1769277183::1769280957::1::::<><>::
|
||||||
::res://assets/::1769279430
|
::res://assets/::1769279430
|
||||||
::res://assets/cards/::1769280956
|
::res://assets/cards/::1769466517
|
||||||
1-003C_eg.jpg::CompressedTexture2D::3078340571116611252::1769279471::1769280956::1::::<><>::
|
1-003C_eg.jpg::CompressedTexture2D::3078340571116611252::1769279471::1769280956::1::::<><>::
|
||||||
1-005R_eg.jpg::CompressedTexture2D::9030396388734102056::1769279471::1769280956::1::::<><>::
|
1-005R_eg.jpg::CompressedTexture2D::9030396388734102056::1769279471::1769280956::1::::<><>::
|
||||||
1-006H_eg.jpg::CompressedTexture2D::8795536954934893861::1769279471::1769280956::1::::<><>::
|
1-006H_eg.jpg::CompressedTexture2D::8795536954934893861::1769279471::1769280956::1::::<><>::
|
||||||
1-007R_eg.jpg::CompressedTexture2D::6933100492479484556::1769279471::1769280956::1::::<><>::
|
1-007R_eg.jpg::CompressedTexture2D::6933100492479484556::1769279471::1769280956::1::::<><>::
|
||||||
::res://assets/table/::1769464097
|
card_back.png::CompressedTexture2D::7787125851359297441::1769466418::1769466517::1::::<><>::
|
||||||
|
::res://assets/table/::1769464212
|
||||||
background_1.png::CompressedTexture2D::102728058489724503::1769464097::1769464212::1::::<><>::
|
background_1.png::CompressedTexture2D::102728058489724503::1769464097::1769464212::1::::<><>::
|
||||||
playmat.webp::CompressedTexture2D::3235866490631872101::1769279471::1769280957::1::::<><>::
|
playmat.webp::CompressedTexture2D::3235866490631872101::1769279471::1769280957::1::::<><>::
|
||||||
::res://assets/ui/::1769280956
|
::res://assets/ui/::1769280956
|
||||||
@@ -21,9 +23,9 @@ cards.json::JSON::-1::1769309289::0::1::::<><>::
|
|||||||
::res://docs/::1769279608
|
::res://docs/::1769279608
|
||||||
CARD_FORMAT.md::TextFile::-1::1769279608::0::1::::<><>::
|
CARD_FORMAT.md::TextFile::-1::1769279608::0::1::::<><>::
|
||||||
DESIGN.md::TextFile::-1::1769279572::0::1::::<><>::
|
DESIGN.md::TextFile::-1::1769279572::0::1::::<><>::
|
||||||
::res://scenes/::1769461610
|
::res://scenes/::1769466609
|
||||||
game_controller.tscn::PackedScene::3882700613993784342::1769285267::0::1::::<><>::res://scripts/GameController.gd
|
game_controller.tscn::PackedScene::3882700613993784342::1769285267::0::1::::<><>::res://scripts/GameController.gd
|
||||||
main.tscn::PackedScene::5942992277112036945::1769461610::0::1::::<><>::res://scripts/Main.gd
|
main.tscn::PackedScene::5942992277112036945::1769466609::0::1::::<><>::res://scripts/Main.gd
|
||||||
::res://scenes/card/::1769279430
|
::res://scenes/card/::1769279430
|
||||||
::res://scenes/main/::1769279430
|
::res://scenes/main/::1769279430
|
||||||
::res://scenes/table/::1769279430
|
::res://scenes/table/::1769279430
|
||||||
@@ -50,11 +52,11 @@ GameUI.gd::GDScript::-1::1769379370::0::1::::GameUI<>CanvasLayer<>::
|
|||||||
HandDisplay.gd::GDScript::-1::1769381830::0::1::::HandDisplay<>Control<>::
|
HandDisplay.gd::GDScript::-1::1769381830::0::1::::HandDisplay<>Control<>::
|
||||||
MainMenu.gd::GDScript::-1::1769285226::0::1::::MainMenu<>CanvasLayer<>::
|
MainMenu.gd::GDScript::-1::1769285226::0::1::::MainMenu<>CanvasLayer<>::
|
||||||
PauseMenu.gd::GDScript::-1::1769287615::0::1::::PauseMenu<>CanvasLayer<>::
|
PauseMenu.gd::GDScript::-1::1769287615::0::1::::PauseMenu<>CanvasLayer<>::
|
||||||
::res://scripts/visual/::1769464132
|
::res://scripts/visual/::1769466440
|
||||||
CardVisual.gd::GDScript::-1::1769460118::0::1::::CardVisual<>Node3D<>::
|
CardVisual.gd::GDScript::-1::1769460118::0::1::::CardVisual<>Node3D<>::
|
||||||
PlaymatRenderer.gd::GDScript::-1::1769452774::0::1::::PlaymatRenderer<>Node<>::
|
PlaymatRenderer.gd::GDScript::-1::1769452774::0::1::::PlaymatRenderer<>Node<>::
|
||||||
TableCamera.gd::GDScript::-1::1769461608::0::1::::TableCamera<>Camera3D<>::
|
TableCamera.gd::GDScript::-1::1769461608::0::1::::TableCamera<>Camera3D<>::
|
||||||
TableSetup.gd::GDScript::-1::1769464132::0::1::::TableSetup<>Node3D<>::
|
TableSetup.gd::GDScript::-1::1769466440::0::1::::TableSetup<>Node3D<>::
|
||||||
ZoneVisual.gd::GDScript::-1::1769454229::0::1::::ZoneVisual<>Node3D<>::
|
ZoneVisual.gd::GDScript::-1::1769454229::0::1::::ZoneVisual<>Node3D<>::
|
||||||
::res://source-cards/::1769308626
|
::res://source-cards/::1769308626
|
||||||
1-001H.jpg::CompressedTexture2D::2056726104879484109::1769306016::1769308625::1::::<><>::
|
1-001H.jpg::CompressedTexture2D::2056726104879484109::1769306016::1769308625::1::::<><>::
|
||||||
|
|||||||
@@ -1,2 +1 @@
|
|||||||
res://scenes/main.tscn
|
res://scenes/main.tscn
|
||||||
res://scripts/visual/TableCamera.gd
|
|
||||||
|
|||||||
@@ -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/visual/TableCamera.gd", "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"]
|
scripts=["res://scripts/ui/GameUI.gd", "res://scripts/game/Player.gd", "res://scripts/game/CardInstance.gd", "res://scripts/game/CPPool.gd", "res://scripts/game/GameState.gd", "res://scripts/visual/TableCamera.gd", "res://scripts/ui/ActionLog.gd", "res://scripts/game/UndoSystem.gd", "res://scripts/ui/HandDisplay.gd", "res://scripts/ui/PauseMenu.gd"]
|
||||||
|
|
||||||
[linked_properties]
|
[linked_properties]
|
||||||
|
|
||||||
|
|||||||
@@ -3,11 +3,11 @@
|
|||||||
state={
|
state={
|
||||||
"bookmarks": PackedInt32Array(),
|
"bookmarks": PackedInt32Array(),
|
||||||
"breakpoints": PackedInt32Array(),
|
"breakpoints": PackedInt32Array(),
|
||||||
"column": 29,
|
"column": 0,
|
||||||
"folded_lines": Array[int]([]),
|
"folded_lines": Array[int]([]),
|
||||||
"h_scroll_position": 0,
|
"h_scroll_position": 0,
|
||||||
"row": 49,
|
"row": 54,
|
||||||
"scroll_position": 45.0,
|
"scroll_position": 50.0,
|
||||||
"selection": false,
|
"selection": false,
|
||||||
"syntax_highlighter": "GDScript"
|
"syntax_highlighter": "GDScript"
|
||||||
}
|
}
|
||||||
@@ -105,7 +105,77 @@ state={
|
|||||||
"folded_lines": Array[int]([]),
|
"folded_lines": Array[int]([]),
|
||||||
"h_scroll_position": 0,
|
"h_scroll_position": 0,
|
||||||
"row": 8,
|
"row": 8,
|
||||||
"scroll_position": 3.0,
|
"scroll_position": 0.0,
|
||||||
|
"selection": false,
|
||||||
|
"syntax_highlighter": "GDScript"
|
||||||
|
}
|
||||||
|
|
||||||
|
[res://scripts/game/GameState.gd]
|
||||||
|
|
||||||
|
state={
|
||||||
|
"bookmarks": PackedInt32Array(),
|
||||||
|
"breakpoints": PackedInt32Array(),
|
||||||
|
"column": 0,
|
||||||
|
"folded_lines": Array[int]([]),
|
||||||
|
"h_scroll_position": 0,
|
||||||
|
"row": 0,
|
||||||
|
"scroll_position": 0.0,
|
||||||
|
"selection": false,
|
||||||
|
"syntax_highlighter": "GDScript"
|
||||||
|
}
|
||||||
|
|
||||||
|
[res://scripts/game/CPPool.gd]
|
||||||
|
|
||||||
|
state={
|
||||||
|
"bookmarks": PackedInt32Array(),
|
||||||
|
"breakpoints": PackedInt32Array(),
|
||||||
|
"column": 31,
|
||||||
|
"folded_lines": Array[int]([]),
|
||||||
|
"h_scroll_position": 0,
|
||||||
|
"row": 3,
|
||||||
|
"scroll_position": 0.0,
|
||||||
|
"selection": false,
|
||||||
|
"syntax_highlighter": "GDScript"
|
||||||
|
}
|
||||||
|
|
||||||
|
[res://scripts/game/CardInstance.gd]
|
||||||
|
|
||||||
|
state={
|
||||||
|
"bookmarks": PackedInt32Array(),
|
||||||
|
"breakpoints": PackedInt32Array(),
|
||||||
|
"column": 0,
|
||||||
|
"folded_lines": Array[int]([]),
|
||||||
|
"h_scroll_position": 0,
|
||||||
|
"row": 0,
|
||||||
|
"scroll_position": 0.0,
|
||||||
|
"selection": false,
|
||||||
|
"syntax_highlighter": "GDScript"
|
||||||
|
}
|
||||||
|
|
||||||
|
[res://scripts/game/Player.gd]
|
||||||
|
|
||||||
|
state={
|
||||||
|
"bookmarks": PackedInt32Array(),
|
||||||
|
"breakpoints": PackedInt32Array(),
|
||||||
|
"column": 0,
|
||||||
|
"folded_lines": Array[int]([]),
|
||||||
|
"h_scroll_position": 0,
|
||||||
|
"row": 0,
|
||||||
|
"scroll_position": 0.0,
|
||||||
|
"selection": false,
|
||||||
|
"syntax_highlighter": "GDScript"
|
||||||
|
}
|
||||||
|
|
||||||
|
[res://scripts/ui/GameUI.gd]
|
||||||
|
|
||||||
|
state={
|
||||||
|
"bookmarks": PackedInt32Array(),
|
||||||
|
"breakpoints": PackedInt32Array(),
|
||||||
|
"column": 0,
|
||||||
|
"folded_lines": Array[int]([]),
|
||||||
|
"h_scroll_position": 0,
|
||||||
|
"row": 0,
|
||||||
|
"scroll_position": 0.0,
|
||||||
"selection": false,
|
"selection": false,
|
||||||
"syntax_highlighter": "GDScript"
|
"syntax_highlighter": "GDScript"
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
BIN
assets/cards/card_back.png
Normal file
BIN
assets/cards/card_back.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 81 KiB |
34
assets/cards/card_back.png.import
Normal file
34
assets/cards/card_back.png.import
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://di7e3b1dpuo1f"
|
||||||
|
path="res://.godot/imported/card_back.png-2577f00fd56181f44075a936080e7577.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/cards/card_back.png"
|
||||||
|
dest_files=["res://.godot/imported/card_back.png-2577f00fd56181f44075a936080e7577.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
BIN
card_back.png
Normal file
BIN
card_back.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 81 KiB |
34
card_back.png.import
Normal file
34
card_back.png.import
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://ca4nahdobuqyw"
|
||||||
|
path="res://.godot/imported/card_back.png-0c38304b68d8509854fe3cd66366fdbb.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://card_back.png"
|
||||||
|
dest_files=["res://.godot/imported/card_back.png-0c38304b68d8509854fe3cd66366fdbb.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
@@ -123,6 +123,9 @@ func _connect_signals() -> void:
|
|||||||
GameManager.phase_changed.connect(_on_phase_changed)
|
GameManager.phase_changed.connect(_on_phase_changed)
|
||||||
GameManager.damage_dealt.connect(_on_damage_dealt)
|
GameManager.damage_dealt.connect(_on_damage_dealt)
|
||||||
|
|
||||||
|
# Field card action signal (deferred to ensure game_ui is ready)
|
||||||
|
call_deferred("_connect_field_card_signals")
|
||||||
|
|
||||||
func _start_game() -> void:
|
func _start_game() -> void:
|
||||||
GameManager.start_new_game()
|
GameManager.start_new_game()
|
||||||
# Force an update of visuals after a frame to ensure everything is ready
|
# Force an update of visuals after a frame to ensure everything is ready
|
||||||
@@ -137,6 +140,11 @@ func _on_game_started() -> void:
|
|||||||
_sync_visuals()
|
_sync_visuals()
|
||||||
_update_hand_display()
|
_update_hand_display()
|
||||||
|
|
||||||
|
# Set initial camera to first player's perspective
|
||||||
|
if GameManager.game_state and table_setup:
|
||||||
|
var player_index = GameManager.game_state.turn_manager.current_player_index
|
||||||
|
table_setup.switch_camera_to_player(player_index)
|
||||||
|
|
||||||
func _on_game_ended(winner_name: String) -> void:
|
func _on_game_ended(winner_name: String) -> void:
|
||||||
game_ui.show_message(winner_name + " wins the game!")
|
game_ui.show_message(winner_name + " wins the game!")
|
||||||
|
|
||||||
@@ -145,9 +153,16 @@ func _on_turn_changed(_player_name: String, _turn_number: int) -> void:
|
|||||||
_update_hand_display()
|
_update_hand_display()
|
||||||
_update_cp_display()
|
_update_cp_display()
|
||||||
|
|
||||||
|
# Rotate camera to face the current player's mat
|
||||||
|
if GameManager.game_state and table_setup:
|
||||||
|
var player_index = GameManager.game_state.turn_manager.current_player_index
|
||||||
|
table_setup.switch_camera_to_player(player_index)
|
||||||
|
|
||||||
func _on_phase_changed(_phase_name: String) -> void:
|
func _on_phase_changed(_phase_name: String) -> void:
|
||||||
_update_playable_highlights()
|
_update_playable_highlights()
|
||||||
_update_cp_display()
|
_update_cp_display()
|
||||||
|
# Refresh hand after draw phase completes (hand updates on entering main phase)
|
||||||
|
_update_hand_display()
|
||||||
|
|
||||||
func _on_damage_dealt(player_name: String, _amount: int) -> void:
|
func _on_damage_dealt(player_name: String, _amount: int) -> void:
|
||||||
# Find player index
|
# Find player index
|
||||||
@@ -229,7 +244,9 @@ func _on_hand_card_unhovered() -> void:
|
|||||||
game_ui.hide_card_detail()
|
game_ui.hide_card_detail()
|
||||||
|
|
||||||
func _on_hand_card_selected(_card: CardInstance) -> void:
|
func _on_hand_card_selected(_card: CardInstance) -> void:
|
||||||
# Selection panel is now visible - ensure any stale hover preview is hidden
|
# Close field card panel if open
|
||||||
|
game_ui.hide_field_card_selection()
|
||||||
|
# Ensure any stale hover preview is hidden
|
||||||
game_ui.hide_card_detail()
|
game_ui.hide_card_detail()
|
||||||
|
|
||||||
func _on_undo_requested() -> void:
|
func _on_undo_requested() -> void:
|
||||||
@@ -248,32 +265,49 @@ func _on_undo_available_changed(available: bool) -> void:
|
|||||||
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
|
||||||
|
|
||||||
|
# Handle special input modes first (these take priority)
|
||||||
match input_mode:
|
match input_mode:
|
||||||
GameManager.InputMode.SELECT_CP_SOURCE:
|
GameManager.InputMode.SELECT_CP_SOURCE:
|
||||||
# Check if it's a backup we can dull
|
|
||||||
if zone_type == Enums.ZoneType.FIELD_BACKUPS:
|
if zone_type == Enums.ZoneType.FIELD_BACKUPS:
|
||||||
if player_index == GameManager.game_state.turn_manager.current_player_index:
|
if player_index == GameManager.game_state.turn_manager.current_player_index:
|
||||||
GameManager.dull_backup_for_cp(card)
|
GameManager.dull_backup_for_cp(card)
|
||||||
_sync_visuals()
|
_sync_visuals()
|
||||||
_update_cp_display()
|
_update_cp_display()
|
||||||
|
return
|
||||||
|
|
||||||
GameManager.InputMode.SELECT_ATTACKER:
|
GameManager.InputMode.SELECT_ATTACKER:
|
||||||
# Select attacker
|
|
||||||
if zone_type == Enums.ZoneType.FIELD_FORWARDS:
|
if zone_type == Enums.ZoneType.FIELD_FORWARDS:
|
||||||
if player_index == GameManager.game_state.turn_manager.current_player_index:
|
if player_index == GameManager.game_state.turn_manager.current_player_index:
|
||||||
GameManager.declare_attack(card)
|
GameManager.declare_attack(card)
|
||||||
_sync_visuals()
|
_sync_visuals()
|
||||||
|
return
|
||||||
|
|
||||||
GameManager.InputMode.SELECT_BLOCKER:
|
GameManager.InputMode.SELECT_BLOCKER:
|
||||||
# Select blocker
|
|
||||||
if zone_type == Enums.ZoneType.FIELD_FORWARDS:
|
if zone_type == Enums.ZoneType.FIELD_FORWARDS:
|
||||||
var opponent_index = 1 - GameManager.game_state.turn_manager.current_player_index
|
var opponent_index = 1 - GameManager.game_state.turn_manager.current_player_index
|
||||||
if player_index == opponent_index:
|
if player_index == opponent_index:
|
||||||
GameManager.declare_block(card)
|
GameManager.declare_block(card)
|
||||||
_sync_visuals()
|
_sync_visuals()
|
||||||
|
return
|
||||||
|
|
||||||
# Show card detail on any click
|
# Close hand selection panel if open
|
||||||
game_ui.show_card_detail(card)
|
hand_display.deselect()
|
||||||
|
# Show field card selection panel with card image and actions
|
||||||
|
game_ui.show_field_card_selection(card, zone_type, player_index)
|
||||||
|
|
||||||
|
func _connect_field_card_signals() -> void:
|
||||||
|
if game_ui:
|
||||||
|
game_ui.field_card_action_requested.connect(_on_field_card_action)
|
||||||
|
|
||||||
|
func _on_field_card_action(card: CardInstance, zone_type: Enums.ZoneType, player_index: int, action: String) -> void:
|
||||||
|
match action:
|
||||||
|
"dull_cp":
|
||||||
|
GameManager.dull_backup_for_cp(card)
|
||||||
|
_sync_visuals()
|
||||||
|
_update_cp_display()
|
||||||
|
"attack":
|
||||||
|
GameManager.declare_attack(card)
|
||||||
|
_sync_visuals()
|
||||||
|
|
||||||
func _input(event: InputEvent) -> void:
|
func _input(event: InputEvent) -> void:
|
||||||
# Keyboard shortcuts
|
# Keyboard shortcuts
|
||||||
@@ -301,3 +335,4 @@ func _input(event: InputEvent) -> void:
|
|||||||
GameManager.clear_selection()
|
GameManager.clear_selection()
|
||||||
table_setup.clear_all_highlights()
|
table_setup.clear_all_highlights()
|
||||||
hand_display.clear_highlights()
|
hand_display.clear_highlights()
|
||||||
|
game_ui.hide_field_card_selection()
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ extends CanvasLayer
|
|||||||
|
|
||||||
signal end_phase_pressed
|
signal end_phase_pressed
|
||||||
signal pass_priority_pressed
|
signal pass_priority_pressed
|
||||||
|
signal field_card_action_requested(card: CardInstance, zone_type: Enums.ZoneType, player_index: int, action: String)
|
||||||
|
|
||||||
# UI Components
|
# UI Components
|
||||||
var turn_panel: PanelContainer
|
var turn_panel: PanelContainer
|
||||||
@@ -28,6 +29,19 @@ var detail_power_label: Label
|
|||||||
var detail_element_label: Label
|
var detail_element_label: Label
|
||||||
var detail_ability_label: Label
|
var detail_ability_label: Label
|
||||||
|
|
||||||
|
# Field card selection panel
|
||||||
|
var field_card_panel: Panel
|
||||||
|
var field_card_image_container: Control
|
||||||
|
var field_card_action_menu: VBoxContainer
|
||||||
|
var selected_field_card: CardInstance = null
|
||||||
|
var selected_field_zone: Enums.ZoneType = Enums.ZoneType.HAND
|
||||||
|
var selected_field_player: int = -1
|
||||||
|
|
||||||
|
# Field card panel sizes (match hand selection panel)
|
||||||
|
const FIELD_CARD_WIDTH: float = 405.0
|
||||||
|
const FIELD_CARD_HEIGHT: float = 567.0
|
||||||
|
const FIELD_MENU_WIDTH: float = 180.0
|
||||||
|
|
||||||
# Buttons
|
# Buttons
|
||||||
var end_phase_button: Button
|
var end_phase_button: Button
|
||||||
var pass_button: Button
|
var pass_button: Button
|
||||||
@@ -204,6 +218,9 @@ func _create_ui() -> void:
|
|||||||
detail_ability_label.custom_minimum_size.x = 180
|
detail_ability_label.custom_minimum_size.x = 180
|
||||||
detail_vbox.add_child(detail_ability_label)
|
detail_vbox.add_child(detail_ability_label)
|
||||||
|
|
||||||
|
# === FIELD CARD SELECTION PANEL (centered on screen) ===
|
||||||
|
_create_field_card_panel(root)
|
||||||
|
|
||||||
# Message timer
|
# Message timer
|
||||||
message_timer = Timer.new()
|
message_timer = Timer.new()
|
||||||
add_child(message_timer)
|
add_child(message_timer)
|
||||||
@@ -347,3 +364,233 @@ func hide_card_detail() -> void:
|
|||||||
func update_button_states(can_end_phase: bool, can_pass: bool) -> void:
|
func update_button_states(can_end_phase: bool, can_pass: bool) -> void:
|
||||||
end_phase_button.disabled = not can_end_phase
|
end_phase_button.disabled = not can_end_phase
|
||||||
pass_button.disabled = not can_pass
|
pass_button.disabled = not can_pass
|
||||||
|
|
||||||
|
## Create field card selection panel
|
||||||
|
func _create_field_card_panel(root: Control) -> void:
|
||||||
|
field_card_panel = Panel.new()
|
||||||
|
field_card_panel.visible = false
|
||||||
|
field_card_panel.mouse_filter = Control.MOUSE_FILTER_STOP
|
||||||
|
field_card_panel.z_index = 200
|
||||||
|
|
||||||
|
var style = StyleBoxFlat.new()
|
||||||
|
style.bg_color = Color(0.08, 0.08, 0.12, 0.95)
|
||||||
|
style.border_color = Color(0.5, 0.4, 0.2)
|
||||||
|
style.set_border_width_all(2)
|
||||||
|
style.set_corner_radius_all(6)
|
||||||
|
style.content_margin_left = 15
|
||||||
|
style.content_margin_right = 15
|
||||||
|
style.content_margin_top = 15
|
||||||
|
style.content_margin_bottom = 15
|
||||||
|
field_card_panel.add_theme_stylebox_override("panel", style)
|
||||||
|
|
||||||
|
root.add_child(field_card_panel)
|
||||||
|
|
||||||
|
var hbox = HBoxContainer.new()
|
||||||
|
hbox.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||||
|
hbox.add_theme_constant_override("separation", 20)
|
||||||
|
field_card_panel.add_child(hbox)
|
||||||
|
|
||||||
|
# Card image
|
||||||
|
field_card_image_container = Control.new()
|
||||||
|
field_card_image_container.custom_minimum_size = Vector2(FIELD_CARD_WIDTH, FIELD_CARD_HEIGHT)
|
||||||
|
field_card_image_container.clip_contents = false
|
||||||
|
hbox.add_child(field_card_image_container)
|
||||||
|
|
||||||
|
# Action menu
|
||||||
|
field_card_action_menu = VBoxContainer.new()
|
||||||
|
field_card_action_menu.custom_minimum_size = Vector2(FIELD_MENU_WIDTH, FIELD_CARD_HEIGHT)
|
||||||
|
field_card_action_menu.add_theme_constant_override("separation", 10)
|
||||||
|
hbox.add_child(field_card_action_menu)
|
||||||
|
|
||||||
|
var panel_width = 15 + FIELD_CARD_WIDTH + 20 + FIELD_MENU_WIDTH + 15
|
||||||
|
var panel_height = 15 + FIELD_CARD_HEIGHT + 15
|
||||||
|
field_card_panel.custom_minimum_size = Vector2(panel_width, panel_height)
|
||||||
|
field_card_panel.size = Vector2(panel_width, panel_height)
|
||||||
|
|
||||||
|
## Show field card selection panel with context-sensitive actions
|
||||||
|
func show_field_card_selection(card: CardInstance, zone_type: Enums.ZoneType, player_index: int) -> void:
|
||||||
|
if not card or not card.card_data:
|
||||||
|
return
|
||||||
|
|
||||||
|
selected_field_card = card
|
||||||
|
selected_field_zone = zone_type
|
||||||
|
selected_field_player = player_index
|
||||||
|
|
||||||
|
# Clear previous content
|
||||||
|
for child in field_card_image_container.get_children():
|
||||||
|
child.queue_free()
|
||||||
|
for child in field_card_action_menu.get_children():
|
||||||
|
child.queue_free()
|
||||||
|
|
||||||
|
# Card image
|
||||||
|
var texture = CardDatabase.get_card_texture(card.card_data)
|
||||||
|
if texture:
|
||||||
|
var tex_rect = TextureRect.new()
|
||||||
|
tex_rect.texture = texture
|
||||||
|
tex_rect.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||||
|
tex_rect.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||||||
|
tex_rect.stretch_mode = TextureRect.STRETCH_SCALE
|
||||||
|
tex_rect.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
field_card_image_container.add_child(tex_rect)
|
||||||
|
else:
|
||||||
|
var color_rect = ColorRect.new()
|
||||||
|
color_rect.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||||
|
color_rect.color = Enums.get_element_color(card.card_data.get_primary_element()).lightened(0.4)
|
||||||
|
field_card_image_container.add_child(color_rect)
|
||||||
|
|
||||||
|
# Context-sensitive action buttons
|
||||||
|
var is_current_player = GameManager.game_state and player_index == GameManager.game_state.turn_manager.current_player_index
|
||||||
|
var phase = GameManager.get_current_phase() if GameManager.game_state else Enums.TurnPhase.ACTIVE
|
||||||
|
|
||||||
|
if is_current_player:
|
||||||
|
match zone_type:
|
||||||
|
Enums.ZoneType.FIELD_BACKUPS:
|
||||||
|
if not card.is_dull() and (phase == Enums.TurnPhase.MAIN_1 or phase == Enums.TurnPhase.MAIN_2):
|
||||||
|
_add_field_action_button("Dull for CP", "dull_cp")
|
||||||
|
|
||||||
|
Enums.ZoneType.FIELD_FORWARDS:
|
||||||
|
if card.can_attack() and phase == Enums.TurnPhase.ATTACK:
|
||||||
|
_add_field_action_button("Attack", "attack")
|
||||||
|
|
||||||
|
# Always show card info
|
||||||
|
_add_field_card_info(card)
|
||||||
|
_add_field_action_button("Close", "cancel")
|
||||||
|
|
||||||
|
# Position same as hand selection panel: centered horizontally, above hand area
|
||||||
|
var viewport = get_viewport()
|
||||||
|
if viewport:
|
||||||
|
var vp_size = viewport.get_visible_rect().size
|
||||||
|
var panel_w = field_card_panel.size.x
|
||||||
|
var panel_h = field_card_panel.size.y
|
||||||
|
|
||||||
|
# Center horizontally on screen
|
||||||
|
var panel_x = (vp_size.x - panel_w) / 2.0
|
||||||
|
|
||||||
|
# Position above the hand area (hand cards are ~373px from bottom)
|
||||||
|
# Match hand panel: bottom of panel sits ~20px above hand cards
|
||||||
|
var hand_top_y = vp_size.y - 100.0 - 273.0 # Same calc as Main._position_hand_display
|
||||||
|
var panel_y = hand_top_y - panel_h - 20.0
|
||||||
|
|
||||||
|
# Clamp to stay on screen (don't go above top bar)
|
||||||
|
if panel_y < 90.0:
|
||||||
|
panel_y = 90.0
|
||||||
|
|
||||||
|
field_card_panel.position = Vector2(panel_x, panel_y)
|
||||||
|
|
||||||
|
# Hide the old detail panel
|
||||||
|
card_detail_panel.visible = false
|
||||||
|
field_card_panel.visible = true
|
||||||
|
|
||||||
|
## Add card info labels to the action menu
|
||||||
|
func _add_field_card_info(card: CardInstance) -> void:
|
||||||
|
var data = card.card_data
|
||||||
|
|
||||||
|
# Card name header
|
||||||
|
var name_label = Label.new()
|
||||||
|
name_label.text = data.name
|
||||||
|
name_label.add_theme_font_size_override("font_size", 16)
|
||||||
|
name_label.add_theme_color_override("font_color", Color(0.9, 0.85, 0.7))
|
||||||
|
field_card_action_menu.add_child(name_label)
|
||||||
|
|
||||||
|
# Type / Element / Cost
|
||||||
|
var info_label = Label.new()
|
||||||
|
var type_str = Enums.card_type_to_string(data.type)
|
||||||
|
var elem_strs = []
|
||||||
|
for elem in data.elements:
|
||||||
|
elem_strs.append(Enums.element_to_string(elem))
|
||||||
|
info_label.text = type_str + " | " + "/".join(elem_strs) + " | Cost: " + str(data.cost)
|
||||||
|
info_label.add_theme_font_size_override("font_size", 12)
|
||||||
|
info_label.autowrap_mode = TextServer.AUTOWRAP_WORD
|
||||||
|
field_card_action_menu.add_child(info_label)
|
||||||
|
|
||||||
|
# Power (if applicable)
|
||||||
|
if data.type == Enums.CardType.FORWARD or data.power > 0:
|
||||||
|
var power_label = Label.new()
|
||||||
|
power_label.text = "Power: " + str(card.get_power())
|
||||||
|
power_label.add_theme_font_size_override("font_size", 12)
|
||||||
|
field_card_action_menu.add_child(power_label)
|
||||||
|
|
||||||
|
# State
|
||||||
|
var state_label = Label.new()
|
||||||
|
state_label.text = "Status: " + ("Dull" if card.is_dull() else "Active")
|
||||||
|
state_label.add_theme_font_size_override("font_size", 12)
|
||||||
|
field_card_action_menu.add_child(state_label)
|
||||||
|
|
||||||
|
# Separator before abilities
|
||||||
|
var sep = HSeparator.new()
|
||||||
|
field_card_action_menu.add_child(sep)
|
||||||
|
|
||||||
|
# Abilities
|
||||||
|
if data.abilities.size() > 0:
|
||||||
|
for ability in data.abilities:
|
||||||
|
var ability_label = Label.new()
|
||||||
|
var type_prefix = ""
|
||||||
|
match ability.type:
|
||||||
|
Enums.AbilityType.FIELD: type_prefix = "[Field] "
|
||||||
|
Enums.AbilityType.AUTO: type_prefix = "[Auto] "
|
||||||
|
Enums.AbilityType.ACTION: type_prefix = "[Action] "
|
||||||
|
Enums.AbilityType.SPECIAL: type_prefix = "[Special] "
|
||||||
|
ability_label.text = type_prefix + ability.effect
|
||||||
|
ability_label.add_theme_font_size_override("font_size", 11)
|
||||||
|
ability_label.autowrap_mode = TextServer.AUTOWRAP_WORD
|
||||||
|
ability_label.custom_minimum_size.x = FIELD_MENU_WIDTH - 10
|
||||||
|
field_card_action_menu.add_child(ability_label)
|
||||||
|
else:
|
||||||
|
var no_ability = Label.new()
|
||||||
|
no_ability.text = "No abilities"
|
||||||
|
no_ability.add_theme_font_size_override("font_size", 11)
|
||||||
|
field_card_action_menu.add_child(no_ability)
|
||||||
|
|
||||||
|
var sep2 = HSeparator.new()
|
||||||
|
field_card_action_menu.add_child(sep2)
|
||||||
|
|
||||||
|
## Add a styled action button to the field card panel
|
||||||
|
func _add_field_action_button(text: String, action: String) -> void:
|
||||||
|
var button = Button.new()
|
||||||
|
button.text = text
|
||||||
|
button.custom_minimum_size = Vector2(FIELD_MENU_WIDTH - 10, 40)
|
||||||
|
|
||||||
|
var style_normal = StyleBoxFlat.new()
|
||||||
|
style_normal.bg_color = Color(0.25, 0.25, 0.3)
|
||||||
|
style_normal.border_color = Color(0.5, 0.5, 0.6)
|
||||||
|
style_normal.set_border_width_all(1)
|
||||||
|
style_normal.set_corner_radius_all(5)
|
||||||
|
style_normal.content_margin_top = 8
|
||||||
|
style_normal.content_margin_bottom = 8
|
||||||
|
button.add_theme_stylebox_override("normal", style_normal)
|
||||||
|
|
||||||
|
var style_hover = StyleBoxFlat.new()
|
||||||
|
style_hover.bg_color = Color(0.35, 0.35, 0.45)
|
||||||
|
style_hover.border_color = Color(0.7, 0.6, 0.3)
|
||||||
|
style_hover.set_border_width_all(2)
|
||||||
|
style_hover.set_corner_radius_all(5)
|
||||||
|
style_hover.content_margin_top = 8
|
||||||
|
style_hover.content_margin_bottom = 8
|
||||||
|
button.add_theme_stylebox_override("hover", style_hover)
|
||||||
|
|
||||||
|
var style_pressed = StyleBoxFlat.new()
|
||||||
|
style_pressed.bg_color = Color(0.2, 0.2, 0.25)
|
||||||
|
style_pressed.border_color = Color(0.7, 0.6, 0.3)
|
||||||
|
style_pressed.set_border_width_all(2)
|
||||||
|
style_pressed.set_corner_radius_all(5)
|
||||||
|
style_pressed.content_margin_top = 8
|
||||||
|
style_pressed.content_margin_bottom = 8
|
||||||
|
button.add_theme_stylebox_override("pressed", style_pressed)
|
||||||
|
|
||||||
|
button.add_theme_font_size_override("font_size", 14)
|
||||||
|
button.pressed.connect(_on_field_action_pressed.bind(action))
|
||||||
|
field_card_action_menu.add_child(button)
|
||||||
|
|
||||||
|
func _on_field_action_pressed(action: String) -> void:
|
||||||
|
if action == "cancel":
|
||||||
|
hide_field_card_selection()
|
||||||
|
return
|
||||||
|
|
||||||
|
if selected_field_card:
|
||||||
|
field_card_action_requested.emit(selected_field_card, selected_field_zone, selected_field_player, action)
|
||||||
|
hide_field_card_selection()
|
||||||
|
|
||||||
|
## Hide field card selection panel
|
||||||
|
func hide_field_card_selection() -> void:
|
||||||
|
field_card_panel.visible = false
|
||||||
|
selected_field_card = null
|
||||||
|
|||||||
@@ -9,45 +9,66 @@ extends Camera3D
|
|||||||
@export var camera_height_offset: float = -4.0 # Offset to look slightly above center
|
@export var camera_height_offset: float = -4.0 # Offset to look slightly above center
|
||||||
|
|
||||||
# Smooth movement
|
# Smooth movement
|
||||||
@export var smooth_speed: float = 5.0
|
@export var smooth_speed: float = 3.0
|
||||||
var target_position: Vector3 = Vector3.ZERO
|
var target_position: Vector3 = Vector3.ZERO
|
||||||
|
var target_look_at: Vector3 = Vector3.ZERO
|
||||||
|
var is_animating: bool = false
|
||||||
|
|
||||||
|
# Current player side (0 = positive Z, 1 = negative Z)
|
||||||
|
var current_player: int = 0
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
_setup_isometric_view()
|
_setup_for_player(0, false)
|
||||||
|
|
||||||
func _setup_isometric_view() -> void:
|
func _setup_for_player(player_index: int, animate: bool = true) -> void:
|
||||||
# Set perspective projection
|
# Set perspective projection
|
||||||
projection = PROJECTION_PERSPECTIVE
|
projection = PROJECTION_PERSPECTIVE
|
||||||
fov = 50.0
|
fov = 50.0
|
||||||
|
|
||||||
# Calculate position - camera is positioned behind Player 1's side
|
current_player = player_index
|
||||||
var angle_rad = deg_to_rad(camera_angle)
|
|
||||||
|
|
||||||
# Height based on angle
|
var angle_rad = deg_to_rad(camera_angle)
|
||||||
var height = camera_distance * sin(angle_rad)
|
var height = camera_distance * sin(angle_rad)
|
||||||
# Distance along Z axis (positive Z is toward Player 1)
|
|
||||||
var z_offset = camera_distance * cos(angle_rad)
|
var z_offset = camera_distance * cos(angle_rad)
|
||||||
|
|
||||||
# Position camera behind Player 1 (positive Z), looking toward center/opponent
|
# Flip Z direction based on player
|
||||||
position = Vector3(0, height, z_offset)
|
var side = 1 if player_index == 0 else -1
|
||||||
|
var new_position = Vector3(0, height, z_offset * side)
|
||||||
|
|
||||||
# Look at center of table
|
target_look_at = Vector3(0, camera_height_offset, 0)
|
||||||
look_at(Vector3(0, camera_height_offset, 0), Vector3.UP)
|
|
||||||
|
|
||||||
target_position = position
|
if animate:
|
||||||
|
target_position = new_position
|
||||||
|
is_animating = true
|
||||||
|
else:
|
||||||
|
position = new_position
|
||||||
|
target_position = new_position
|
||||||
|
look_at(target_look_at, Vector3.UP)
|
||||||
|
is_animating = false
|
||||||
|
|
||||||
func _process(delta: float) -> void:
|
func _process(delta: float) -> void:
|
||||||
# Smooth camera movement if needed
|
if is_animating:
|
||||||
if position.distance_to(target_position) > 0.01:
|
|
||||||
position = position.lerp(target_position, smooth_speed * delta)
|
position = position.lerp(target_position, smooth_speed * delta)
|
||||||
look_at(Vector3(0, camera_height_offset, 0), Vector3.UP)
|
look_at(target_look_at, Vector3.UP)
|
||||||
|
|
||||||
|
if position.distance_to(target_position) < 0.05:
|
||||||
|
position = target_position
|
||||||
|
look_at(target_look_at, Vector3.UP)
|
||||||
|
is_animating = false
|
||||||
|
|
||||||
|
## Switch camera to view from a player's perspective
|
||||||
|
func switch_to_player(player_index: int) -> void:
|
||||||
|
if player_index != current_player:
|
||||||
|
_setup_for_player(player_index, true)
|
||||||
|
|
||||||
## Set camera to look at a specific point
|
## Set camera to look at a specific point
|
||||||
func focus_on(point: Vector3) -> void:
|
func focus_on(point: Vector3) -> void:
|
||||||
var angle_rad = deg_to_rad(camera_angle)
|
var angle_rad = deg_to_rad(camera_angle)
|
||||||
|
var side = 1 if current_player == 0 else -1
|
||||||
target_position = point + Vector3(0, camera_distance * sin(angle_rad),
|
target_position = point + Vector3(0, camera_distance * sin(angle_rad),
|
||||||
camera_distance * cos(angle_rad))
|
camera_distance * cos(angle_rad) * side)
|
||||||
|
is_animating = true
|
||||||
|
|
||||||
## Reset to default position
|
## Reset to default position
|
||||||
func reset_position() -> void:
|
func reset_position() -> void:
|
||||||
_setup_isometric_view()
|
_setup_for_player(current_player, false)
|
||||||
|
|||||||
@@ -25,16 +25,17 @@ const MAT_MARGIN: float = 0.3 # Gap between mats and from table center
|
|||||||
# Player 1 sits at +Z looking toward -Z
|
# Player 1 sits at +Z looking toward -Z
|
||||||
# Player's left = +X, Player's right = -X
|
# Player's left = +X, Player's right = -X
|
||||||
const ZONE_POSITIONS = {
|
const ZONE_POSITIONS = {
|
||||||
"damage": Vector3(7.0, 0.1, 1.5), # Front-left (player's left)
|
"damage": Vector3(-7.3, 0.1, 2.1), # Left column, top (forwards row)
|
||||||
"deck": Vector3(-7.0, 0.1, 4.0), # Back-right (player's right, behind backups)
|
"deck": Vector3(7.3, 0.1, 2.0), # Right column, top (forwards row)
|
||||||
"break": Vector3(-7.0, 0.1, 1.5), # Front-right (player's right, front row)
|
"break": Vector3(7.3, 0.1, 5.4), # Right column, centered in break zone box
|
||||||
"forwards": Vector3(0.0, 0.1, 2.0), # Center, front row
|
"forwards": Vector3(0.0, 0.1, 1.9), # Center, front row (near table center)
|
||||||
"backups": Vector3(0.0, 0.1, 5.0), # Center, back row
|
"backups": Vector3(0.0, 0.1, 4.5), # Center, back row (pulled forward to stay visible above hand)
|
||||||
"hand": Vector3(0.0, 0.5, 7.5) # Not used on table (2D overlay)
|
"hand": Vector3(0.0, 0.5, 7.5) # Not used on table (2D overlay)
|
||||||
}
|
}
|
||||||
|
|
||||||
# Background texture path
|
# Background texture path
|
||||||
const BACKGROUND_TEXTURE_PATH: String = "res://assets/table/background_1.png"
|
const BACKGROUND_TEXTURE_PATH: String = "res://assets/table/background_1.png"
|
||||||
|
const CARD_BACK_TEXTURE_PATH: String = "res://assets/cards/card_back.png"
|
||||||
|
|
||||||
# Components
|
# Components
|
||||||
var table_mesh: MeshInstance3D
|
var table_mesh: MeshInstance3D
|
||||||
@@ -195,27 +196,47 @@ func _create_zones() -> void:
|
|||||||
player_zones[player_idx]["break"] = break_zone
|
player_zones[player_idx]["break"] = break_zone
|
||||||
|
|
||||||
func _create_deck_indicators() -> void:
|
func _create_deck_indicators() -> void:
|
||||||
# Create simple card-back boxes for deck representation
|
var card_back_texture = load(CARD_BACK_TEXTURE_PATH)
|
||||||
|
|
||||||
for player_idx in range(2):
|
for player_idx in range(2):
|
||||||
var flip = 1 if player_idx == 0 else -1
|
var flip = 1 if player_idx == 0 else -1
|
||||||
var pos = ZONE_POSITIONS["deck"] * Vector3(flip, 1, flip)
|
var pos = ZONE_POSITIONS["deck"] * Vector3(flip, 1, flip)
|
||||||
|
|
||||||
# Card back mesh (simple colored box)
|
# Deck stack (thin box for thickness)
|
||||||
var deck_mesh = MeshInstance3D.new()
|
var deck_mesh = MeshInstance3D.new()
|
||||||
add_child(deck_mesh)
|
add_child(deck_mesh)
|
||||||
|
|
||||||
var box = BoxMesh.new()
|
var box = BoxMesh.new()
|
||||||
box.size = Vector3(1.26, 0.3, 1.76) # Card size with thickness for deck
|
box.size = Vector3(1.6, 0.3, 2.2)
|
||||||
deck_mesh.mesh = box
|
deck_mesh.mesh = box
|
||||||
|
|
||||||
var mat = StandardMaterial3D.new()
|
var side_mat = StandardMaterial3D.new()
|
||||||
mat.albedo_color = Color(0.15, 0.1, 0.3) # Dark blue for card back
|
side_mat.albedo_color = Color(0.08, 0.06, 0.12) # Dark edges
|
||||||
deck_mesh.material_override = mat
|
deck_mesh.material_override = side_mat
|
||||||
|
|
||||||
deck_mesh.position = pos + Vector3(0, 0.15, 0) # Raise above table
|
deck_mesh.position = pos + Vector3(0, 0.15, 0)
|
||||||
|
|
||||||
deck_indicators[player_idx] = deck_mesh
|
deck_indicators[player_idx] = deck_mesh
|
||||||
|
|
||||||
|
# Card back face on top of the deck
|
||||||
|
var top_card = MeshInstance3D.new()
|
||||||
|
add_child(top_card)
|
||||||
|
|
||||||
|
var plane = PlaneMesh.new()
|
||||||
|
plane.size = Vector2(1.6, 2.2)
|
||||||
|
top_card.mesh = plane
|
||||||
|
|
||||||
|
var top_mat = StandardMaterial3D.new()
|
||||||
|
if card_back_texture:
|
||||||
|
top_mat.albedo_texture = card_back_texture
|
||||||
|
else:
|
||||||
|
top_mat.albedo_color = Color(0.15, 0.1, 0.3)
|
||||||
|
top_card.material_override = top_mat
|
||||||
|
|
||||||
|
top_card.position = pos + Vector3(0, 0.31, 0)
|
||||||
|
if player_idx == 1:
|
||||||
|
top_card.rotation.y = deg_to_rad(180)
|
||||||
|
|
||||||
# Card count label
|
# Card count label
|
||||||
var label = Label3D.new()
|
var label = Label3D.new()
|
||||||
add_child(label)
|
add_child(label)
|
||||||
@@ -224,13 +245,13 @@ func _create_deck_indicators() -> void:
|
|||||||
label.position = pos + Vector3(0, 0.35, 0)
|
label.position = pos + Vector3(0, 0.35, 0)
|
||||||
label.rotation.x = deg_to_rad(-90) # Face up
|
label.rotation.x = deg_to_rad(-90) # Face up
|
||||||
if player_idx == 1:
|
if player_idx == 1:
|
||||||
label.rotation.z = deg_to_rad(180) # Flip for opponent
|
label.rotation.z = deg_to_rad(180)
|
||||||
deck_count_labels[player_idx] = label
|
deck_count_labels[player_idx] = label
|
||||||
|
|
||||||
func _create_zone(zone_type: Enums.ZoneType, player_idx: int, pos: Vector3, rot: float) -> ZoneVisual:
|
func _create_zone(zone_type: Enums.ZoneType, player_idx: int, pos: Vector3, rot: float) -> ZoneVisual:
|
||||||
var zone = ZoneVisual.new()
|
var zone = ZoneVisual.new()
|
||||||
add_child(zone)
|
|
||||||
|
|
||||||
|
# Set properties BEFORE add_child so _ready() uses the correct values
|
||||||
zone.zone_type = zone_type
|
zone.zone_type = zone_type
|
||||||
zone.player_index = player_idx
|
zone.player_index = player_idx
|
||||||
zone.zone_position = pos
|
zone.zone_position = pos
|
||||||
@@ -243,6 +264,7 @@ func _create_zone(zone_type: Enums.ZoneType, player_idx: int, pos: Vector3, rot:
|
|||||||
Enums.ZoneType.DAMAGE, Enums.ZoneType.BREAK:
|
Enums.ZoneType.DAMAGE, Enums.ZoneType.BREAK:
|
||||||
zone.stack_offset = 0.02
|
zone.stack_offset = 0.02
|
||||||
|
|
||||||
|
add_child(zone)
|
||||||
zone.card_clicked.connect(_on_zone_card_clicked.bind(zone_type, player_idx))
|
zone.card_clicked.connect(_on_zone_card_clicked.bind(zone_type, player_idx))
|
||||||
|
|
||||||
return zone
|
return zone
|
||||||
@@ -333,3 +355,8 @@ func clear_all_highlights() -> void:
|
|||||||
var zone = player_zones[player_idx][zone_name]
|
var zone = player_zones[player_idx][zone_name]
|
||||||
if zone:
|
if zone:
|
||||||
zone.clear_highlights()
|
zone.clear_highlights()
|
||||||
|
|
||||||
|
## Switch camera to a player's perspective
|
||||||
|
func switch_camera_to_player(player_index: int) -> void:
|
||||||
|
if camera:
|
||||||
|
camera.switch_to_player(player_index)
|
||||||
|
|||||||
Reference in New Issue
Block a user