Refactor item grabbing logic and remove TileGrabber

Moved item grabbing and auto-arrange logic from the separate TileGrabber node into the player.gd script, consolidating server/client authority checks and RPC handling for multiplayer. Removed obsolete tile_grabber.gd and its UID file. Updated main.tscn to clean up unused UI nodes and adjust visibility settings for player boards.
This commit is contained in:
2025-11-04 10:43:34 +08:00
parent 3c3ba0cd63
commit cd22925449
4 changed files with 159 additions and 231 deletions
+2 -39
View File
@@ -74,6 +74,7 @@ grow_vertical = 2
theme_override_styles/panel = ExtResource("5_dvx6y") theme_override_styles/panel = ExtResource("5_dvx6y")
[node name="NetworkInfo" type="VBoxContainer" parent="NetworkPanel"] [node name="NetworkInfo" type="VBoxContainer" parent="NetworkPanel"]
layout_mode = 0
offset_left = 8.0 offset_left = 8.0
offset_right = 124.0 offset_right = 124.0
offset_bottom = 50.0 offset_bottom = 50.0
@@ -90,7 +91,6 @@ horizontal_alignment = 1
vertical_alignment = 1 vertical_alignment = 1
[node name="PlayerboardUI" type="GridContainer" parent="."] [node name="PlayerboardUI" type="GridContainer" parent="."]
visible = false
clip_contents = true clip_contents = true
anchors_preset = 2 anchors_preset = 2
anchor_top = 1.0 anchor_top = 1.0
@@ -1056,44 +1056,6 @@ environment = ExtResource("4_ky38j")
[node name="CanvasLayer" type="CanvasLayer" parent="."] [node name="CanvasLayer" type="CanvasLayer" parent="."]
[node name="MarginContainer" type="Control" parent="."]
visible = false
clip_contents = true
layout_direction = 2
layout_mode = 3
anchors_preset = 5
anchor_left = 0.5
anchor_right = 0.5
offset_left = -248.0
offset_top = 24.0
offset_right = 275.0
offset_bottom = 129.0
grow_horizontal = 2
[node name="Panel4" type="Panel" parent="MarginContainer"]
custom_minimum_size = Vector2(105, 105)
layout_mode = 2
offset_left = 137.0
offset_right = 242.0
offset_bottom = 105.0
theme_override_styles/panel = SubResource("StyleBoxTexture_5oeq4")
[node name="Panel5" type="Panel" parent="MarginContainer"]
custom_minimum_size = Vector2(105, 105)
layout_mode = 2
offset_left = 275.0
offset_right = 380.0
offset_bottom = 105.0
theme_override_styles/panel = SubResource("StyleBoxTexture_5oeq4")
[node name="Panel6" type="Panel" parent="MarginContainer"]
custom_minimum_size = Vector2(105, 105)
layout_mode = 2
offset_left = 413.0
offset_right = 518.0
offset_bottom = 105.0
theme_override_styles/panel = SubResource("StyleBoxTexture_5oeq4")
[node name="AllPlayerGoals" type="HBoxContainer" parent="."] [node name="AllPlayerGoals" type="HBoxContainer" parent="."]
y_sort_enabled = true y_sort_enabled = true
clip_contents = true clip_contents = true
@@ -6052,6 +6014,7 @@ grow_vertical = 2
texture = ExtResource("10_my1qp") texture = ExtResource("10_my1qp")
[node name="AllPlayerBoards" type="TabContainer" parent="."] [node name="AllPlayerBoards" type="TabContainer" parent="."]
visible = false
clip_contents = true clip_contents = true
anchors_preset = 4 anchors_preset = 4
anchor_top = 0.5 anchor_top = 0.5
+155 -17
View File
@@ -947,6 +947,9 @@ func bot_try_grab_item() -> bool:
return false return false
# -----------------------------------------------------------------
# OLD GRAB Func
# -----------------------------------------------------------------
#func grab_item(grid_position: Vector2i = current_position) -> bool: #func grab_item(grid_position: Vector2i = current_position) -> bool:
#if is_bot: #if is_bot:
#return bot_try_grab_item() #return bot_try_grab_item()
@@ -995,7 +998,52 @@ func bot_try_grab_item() -> bool:
#return true #return true
# #
#return false #return false
# -----------------------------------------------------------------
#func grab_item(grid_position: Vector2i = current_position) -> bool:
#if not enhanced_gridmap or action_points <= 0:
#return false
#
#var cell = Vector3i(grid_position.x, 1, grid_position.y)
#var item = enhanced_gridmap.get_cell_item(cell)
#
## Validate adjacency (unless it's current position)
#if grid_position != current_position:
#var neighbors = enhanced_gridmap.get_neighbors(current_position, 0)
#var is_adjacent = false
#for neighbor in neighbors:
#if neighbor.position == grid_position:
#is_adjacent = true
#break
#if not is_adjacent:
#return false
#
#if item == -1:
#return false
#
## === AUTO-ARRANGE LOGIC ===
#var target_slot = find_best_goal_slot_for_item(item)
#if target_slot == -1:
#return false # no space
#
## Perform the grab and auto-place
#if is_multiplayer_authority():
## Update gridmap: remove item
#rpc("sync_grid_item", cell.x, cell.y, cell.z, -1)
## Update playerboard
#playerboard[target_slot] = item
#rpc("sync_playerboard", playerboard)
## Consume action
#has_performed_action = true
#consume_action_points(1)
#
## Optional: visual feedback
#var main = get_tree().get_root().get_node_or_null("Main")
#if main:
#main.update_playerboard_ui()
#main.set_action_state(main.ActionState.NONE)
#
#return true
# -----------------------------------------------------------------
func grab_item(grid_position: Vector2i = current_position) -> bool: func grab_item(grid_position: Vector2i = current_position) -> bool:
if not enhanced_gridmap or action_points <= 0: if not enhanced_gridmap or action_points <= 0:
return false return false
@@ -1017,32 +1065,108 @@ func grab_item(grid_position: Vector2i = current_position) -> bool:
if item == -1: if item == -1:
return false return false
# === AUTO-ARRANGE LOGIC === # === AUTO-ARRANGE LOGIC (Client-side pre-check) ===
var target_slot = find_best_goal_slot_for_item(item) var target_slot = find_best_goal_slot_for_item(item)
if target_slot == -1: if target_slot == -1:
print("Player: No valid slot found for item.")
return false # no space return false # no space
# Perform the grab and auto-place if not is_multiplayer_authority():
if is_multiplayer_authority(): return false
# Update gridmap: remove item
rpc("sync_grid_item", cell.x, cell.y, cell.z, -1)
# Update playerboard
playerboard[target_slot] = item
rpc("sync_playerboard", playerboard)
# Consume action
has_performed_action = true
consume_action_points(1)
# Optional: visual feedback # === Branching Logic: Host vs Client ===
var main = get_tree().get_root().get_node_or_null("Main") if multiplayer.is_server():
if main: # HOST/SERVER: Call the logic directly
main.update_playerboard_ui() _execute_grab(grid_position, cell, item)
main.set_action_state(main.ActionState.NONE) else:
# CLIENT: Send RPC request to server (peer 1)
rpc_id(1, "request_server_grab", grid_position, cell.x, cell.y, cell.z, item)
return true # Request was sent or processed
# -----------------------------------------------------------------
# Execute Grab
# -----------------------------------------------------------------
func _execute_grab(grid_pos: Vector2i, cell: Vector3i, item_id: int):
var main = get_tree().get_root().get_node_or_null("Main")
if not main:
push_error("Server: Main node not found.")
return false
var server_gridmap = main.get_node("EnhancedGridMap")
if not server_gridmap:
push_error("Server: EnhancedGridMap not found.")
return false
# 1. Server-side Validation
var server_item = server_gridmap.get_cell_item(cell)
# Check if item is still there
if server_item != item_id:
print("Server: Item mismatch or already taken. Server has ", server_item)
return false
# Check action points
if action_points <= 0:
print("Server: Player has no action points.")
return false
# Check adjacency
if grid_pos != current_position:
var neighbors = server_gridmap.get_neighbors(current_position, 0)
if not neighbors.any(func(n): return n.position == grid_pos):
print("Server: Player is not adjacent to item.")
return false
# 2. Server-side Auto-Arrange
var target_slot = find_best_goal_slot_for_item(item_id)
if target_slot == -1:
print("Server: Player has no valid slot for item.")
return false
# 3. Server Executes the Action
# 3a. Update gridmap (using Main's RPC, which has authority)
main.rpc("sync_grid_item", cell.x, cell.y, cell.z, -1)
# 3b. Update playerboard state (on this server-side instance)
playerboard[target_slot] = item_id
# 3c. Broadcast the new playerboard state to all clients
rpc("sync_playerboard", playerboard)
# 3d. Consume action points
has_performed_action = true
consume_action_points(1)
# 3e. Reset the UI for the player who acted
# This will RPC to the client, or run locally for the host
rpc("force_action_state_none")
return true return true
# -----------------------------------------------------------------
# This function runs on the server when requested by a client
# -----------------------------------------------------------------
@rpc("any_peer", "reliable")
func request_server_grab(grid_pos: Vector2i, x: int, y: int, z: int, item_id: int):
# 1. Only the server (peer 1) should process this
if not multiplayer.is_server():
return
# 2. Security check: Did this request come from the actual owner of this node?
if multiplayer.get_remote_sender_id() != get_multiplayer_authority():
push_error("Security: Non-authority tried to grab item!")
return
# 3. Call the execution logic
_execute_grab(grid_pos, Vector3i(x, y, z), item_id)
# -----------------------------------------------------------------
# Auto-put: no manual selection needed # Auto-put: no manual selection needed
# Automatically puts a goal-matching tile into an adjacent (or current) empty grid cell # Automatically puts a goal-matching tile into an adjacent (or current) empty grid cell
# -----------------------------------------------------------------
func auto_put_item() -> bool: func auto_put_item() -> bool:
if not enhanced_gridmap or action_points <= 0 or is_bot or is_in_group("Bots"): if not enhanced_gridmap or action_points <= 0 or is_bot or is_in_group("Bots"):
return false return false
@@ -1134,6 +1258,20 @@ func auto_put_item() -> bool:
return true return true
# -----------------------------------------------------------------
# Force ActionState : None
# -----------------------------------------------------------------
@rpc("authority", "reliable")
func force_action_state_none():
# This is called by the server on the client to reset the UI
var main = get_tree().get_root().get_node_or_null("Main")
if main:
main.set_action_state(main.ActionState.NONE)
clear_highlights()
clear_playerboard_highlights()
# -----------------------------------------------------------------
@rpc("any_peer", "reliable") @rpc("any_peer", "reliable")
func request_server_put(grid_position: Vector2i, slot_index: int, x: int, y: int, z: int, item: int): func request_server_put(grid_position: Vector2i, slot_index: int, x: int, y: int, z: int, item: int):
-172
View File
@@ -1,172 +0,0 @@
# tekton-enet/scripts/tile_grabber.gd
extends Node
class_name TileGrabber
signal tile_grabbed(tile_id: int, position: Vector3i)
signal tiles_arranged(arranged_tiles: Array)
@export var enhanced_gridmap_path: NodePath
@export var player_board_path: NodePath
var enhanced_gridmap: EnhancedGridMap
var player_board: Node
var grabbed_tile: int = -1
var grabbed_position: Vector3i
# Goal combinations - define what patterns score points
var goal_combinations = [
{
"name": "Line of 3",
"pattern": [
[1, 1, 1],
[0, 0, 0],
[0, 0, 0]
],
"score": 10
},
{
"name": "L-Shape",
"pattern": [
[1, 0, 0],
[1, 0, 0],
[1, 1, 0]
],
"score": 15
},
{
"name": "Square",
"pattern": [
[1, 1, 0],
[1, 1, 0],
[0, 0, 0]
],
"score": 20
}
]
func _ready():
if enhanced_gridmap_path:
enhanced_gridmap = get_node(enhanced_gridmap_path)
if player_board_path:
player_board = get_node(player_board_path)
func grab_tile(grid_position: Vector3i) -> bool:
if not enhanced_gridmap:
print("Error: EnhancedGridMap not found")
return false
# Get the tile at the specified position
var tile_id = enhanced_gridmap.get_cell_item(grid_position)
if tile_id == -1:
print("No tile at position: ", grid_position)
return false
# Store the grabbed tile and its position
grabbed_tile = tile_id
grabbed_position = grid_position
# Remove the tile from the grid
enhanced_gridmap.set_cell_item(grid_position, -1)
# Emit signal that a tile was grabbed
tile_grabbed.emit(tile_id, grid_position)
print("Grabbed tile ", tile_id, " from position ", grid_position)
return true
func auto_arrange_tile() -> bool:
if grabbed_tile == -1:
print("No tile to arrange")
return false
# Find the best position for the grabbed tile based on goal combinations
var best_position = find_best_position_for_tile(grabbed_tile)
if best_position == Vector3i(-1, -1, -1):
print("No valid position found for tile")
return false
# Place the tile at the best position
enhanced_gridmap.set_cell_item(best_position, grabbed_tile)
# Reset grabbed tile
grabbed_tile = -1
grabbed_position = Vector3i(-1, -1, -1)
# Emit signal that tiles were arranged
tiles_arranged.emit([best_position])
print("Auto-arranged tile at position: ", best_position)
return true
func find_best_position_for_tile(tile_id: int) -> Vector3i:
var best_position = Vector3i(-1, -1, -1)
var best_score = -1
# Check all possible positions on the grid
for x in range(enhanced_gridmap.columns):
for z in range(enhanced_gridmap.rows):
var position = Vector3i(x, 0, z)
# Skip if position is already occupied
if enhanced_gridmap.get_cell_item(position) != -1:
continue
# Temporarily place the tile to check for goal combinations
enhanced_gridmap.set_cell_item(position, tile_id)
# Calculate score for this position
var score = calculate_position_score(position)
# Remove the temporary tile
enhanced_gridmap.set_cell_item(position, -1)
# Update best position if this one scores higher
if score > best_score:
best_score = score
best_position = position
return best_position
func calculate_position_score(position: Vector3i) -> int:
var total_score = 0
# Check each goal combination
for goal in goal_combinations:
var matches = check_goal_combination(position, goal.pattern)
if matches:
total_score += goal.score
return total_score
func check_goal_combination(center_position: Vector3i, pattern: Array) -> bool:
# Get the pattern dimensions
var pattern_width = pattern[0].size()
var pattern_height = pattern.size()
# Calculate the top-left position of the pattern
var start_x = center_position.x - (pattern_width / 2)
var start_z = center_position.z - (pattern_height / 2)
# Check if the pattern fits within the grid
if start_x < 0 or start_z < 0 or start_x + pattern_width > enhanced_gridmap.columns or start_z + pattern_height > enhanced_gridmap.rows:
return false
# Check each cell in the pattern
for z in range(pattern_height):
for x in range(pattern_width):
var pattern_value = pattern[z][x]
var grid_position = Vector3i(start_x + x, 0, start_z + z)
var grid_value = enhanced_gridmap.get_cell_item(grid_position)
# If pattern expects a tile (1) but grid is empty (-1), no match
if pattern_value == 1 and grid_value == -1:
return false
# If pattern expects empty (0) but grid has a tile, no match
if pattern_value == 0 and grid_value != -1:
return false
# All cells match the pattern
return true
-1
View File
@@ -1 +0,0 @@
uid://dc1cw21830bfp