Add example scene and update gridmap assets

Added a new example scene and player script for EnhancedGridMap. Updated mesh library, materials, and tile assets for improved visuals and structure. Modified main scene and logic to support new gridmap configuration and auto item handling. Adjusted project settings for resolution and main scene path.
This commit is contained in:
2025-10-27 12:35:06 +08:00
parent 84413314ef
commit f9f7d34f80
4 changed files with 503 additions and 108 deletions
+308 -96
View File
@@ -524,9 +524,9 @@ func handle_grid_click(grid_position: Vector2i):
main.ActionState.GRABBING:
if grid_position in highlighted_cells or grid_position == current_position:
grab_item(grid_position)
main.ActionState.PUTTING:
if grid_position in highlighted_cells and selected_playerboard_slot != -1:
put_item(grid_position)
#main.ActionState.PUTTING:
#if grid_position in highlighted_cells and selected_playerboard_slot != -1:
#put_item(grid_position)
main.ActionState.RANDOMIZING:
if grid_position in highlighted_cells:
main.randomize_item_at_position(grid_position)
@@ -933,16 +933,63 @@ func bot_try_grab_item() -> bool:
return false
#func grab_item(grid_position: Vector2i = current_position) -> bool:
#if is_bot:
#return bot_try_grab_item()
#
#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)
#
#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
#
## Bot-specific grab logic moved to bot_grab_item RPC
#if is_in_group("Bots") or is_bot:
#var empty_slot = playerboard.find(-1)
#if empty_slot == -1:
#return false
#
#if is_multiplayer_authority():
#rpc("bot_grab_item", grid_position, empty_slot, cell.x, cell.y, cell.z)
#return true
#
#var main = get_tree().get_root().get_node_or_null("Main")
#if main:
#selected_gridmap_position = grid_position
#clear_highlights()
#clear_playerboard_highlights()
#
#for i in range(playerboard.size()):
#if playerboard[i] == -1:
#var slot = main.playerboard_ui.get_child(i)
#if slot.get_child_count() > 0:
#slot.get_child(0).show()
#highlighted_cells.append(i)
#return true
#
#return false
func grab_item(grid_position: Vector2i = current_position) -> bool:
if is_bot:
return bot_try_grab_item()
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
@@ -952,86 +999,137 @@ func grab_item(grid_position: Vector2i = current_position) -> bool:
break
if not is_adjacent:
return false
if item == -1:
return false
# Bot-specific grab logic moved to bot_grab_item RPC
if is_in_group("Bots") or is_bot:
var empty_slot = playerboard.find(-1)
if empty_slot == -1:
return false
if is_multiplayer_authority():
rpc("bot_grab_item", grid_position, empty_slot, cell.x, cell.y, cell.z)
return true
var main = get_tree().get_root().get_node_or_null("Main")
if main:
selected_gridmap_position = grid_position
clear_highlights()
clear_playerboard_highlights()
for i in range(playerboard.size()):
if playerboard[i] == -1:
var slot = main.playerboard_ui.get_child(i)
if slot.get_child_count() > 0:
slot.get_child(0).show()
highlighted_cells.append(i)
return true
return false
# === AUTO-ARRANGE LOGIC ===
var target_slot = find_best_goal_slot_for_item(item)
if target_slot == -1:
return false # no space
func put_item(grid_position: Vector2i = current_position) -> bool:
if not enhanced_gridmap or action_points <= 0 or selected_playerboard_slot == -1:
return false
var cell = Vector3i(grid_position.x, 1, grid_position.y)
if enhanced_gridmap.get_cell_item(cell) != -1:
return false
# Check if position is adjacent or current position
if grid_position != current_position:
var is_adjacent = false
var neighbors = enhanced_gridmap.get_neighbors(current_position, 0)
for neighbor in neighbors:
if neighbor.position == grid_position:
is_adjacent = true
break
if not is_adjacent:
return false
# Get the item to place first
var item = playerboard[selected_playerboard_slot]
# For clients, we need to RPC to the server first, then let the server RPC back
if is_multiplayer_authority() and not multiplayer.is_server():
# Client requests server to perform the put operation
rpc_id(1, "request_server_put", grid_position, selected_playerboard_slot, cell.x, cell.y, cell.z, item)
# We'll return true and let the server handle the actual operation
# The server will RPC back to update our state if successful
return true
elif is_multiplayer_authority() and multiplayer.is_server():
# Server directly implements the change
rpc("sync_grid_item", cell.x, cell.y, cell.z, item)
playerboard[selected_playerboard_slot] = -1
# 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)
if not is_bot == true:
clear_highlights()
clear_playerboard_highlights()
selected_playerboard_slot = -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
# Auto-put: no manual selection needed
# Automatically puts a goal-matching tile into an adjacent (or current) empty grid cell
func auto_put_item() -> bool:
if not enhanced_gridmap or action_points <= 0 or is_bot or is_in_group("Bots"):
return false
# Step 1: Find empty adjacent (or current) grid cells
var valid_put_positions = []
# Check current position
var current_cell_3d = Vector3i(current_position.x, 1, current_position.y)
if enhanced_gridmap.get_cell_item(current_cell_3d) == -1:
valid_put_positions.append(current_position)
# Check neighbors
var neighbors = enhanced_gridmap.get_neighbors(current_position, 0)
for neighbor in neighbors:
if neighbor.is_walkable:
var pos = neighbor.position
var cell_3d = Vector3i(pos.x, 1, pos.y)
if enhanced_gridmap.get_cell_item(cell_3d) == -1 and not is_position_occupied(pos):
valid_put_positions.append(pos)
if valid_put_positions.is_empty():
return false
# Step 2: Find a playerboard tile that matches any goal
var put_slot = -1
for i in range(playerboard.size()):
if playerboard[i] in goals:
put_slot = i
break
if put_slot == -1:
return false
# Step 3: Perform the put
var target_pos = valid_put_positions[0] # pick first valid
var item = playerboard[put_slot]
var cell = Vector3i(target_pos.x, 1, target_pos.y)
if is_multiplayer_authority():
rpc("sync_grid_item", cell.x, cell.y, cell.z, item)
playerboard[put_slot] = -1
rpc("sync_playerboard", playerboard)
has_performed_action = true
consume_action_points(1)
var main = get_tree().get_root().get_node_or_null("Main")
if main:
main.set_action_state(main.ActionState.NONE)
_after_action_completed()
return true
return false
return true
# Disabled logic, due the auto put
#func put_item(grid_position: Vector2i = current_position) -> bool:
#if not enhanced_gridmap or action_points <= 0 or selected_playerboard_slot == -1:
#return false
#
#var cell = Vector3i(grid_position.x, 1, grid_position.y)
#if enhanced_gridmap.get_cell_item(cell) != -1:
#return false
#
## Check if position is adjacent or current position
#if grid_position != current_position:
#var is_adjacent = false
#var neighbors = enhanced_gridmap.get_neighbors(current_position, 0)
#for neighbor in neighbors:
#if neighbor.position == grid_position:
#is_adjacent = true
#break
#if not is_adjacent:
#return false
#
## Get the item to place first
#var item = playerboard[selected_playerboard_slot]
#
## For clients, we need to RPC to the server first, then let the server RPC back
#if is_multiplayer_authority() and not multiplayer.is_server():
## Client requests server to perform the put operation
#rpc_id(1, "request_server_put", grid_position, selected_playerboard_slot, cell.x, cell.y, cell.z, item)
#
## We'll return true and let the server handle the actual operation
## The server will RPC back to update our state if successful
#return true
#elif is_multiplayer_authority() and multiplayer.is_server():
## Server directly implements the change
#rpc("sync_grid_item", cell.x, cell.y, cell.z, item)
#playerboard[selected_playerboard_slot] = -1
#rpc("sync_playerboard", playerboard)
#
#has_performed_action = true
#consume_action_points(1)
#if not is_bot == true:
#clear_highlights()
#clear_playerboard_highlights()
#selected_playerboard_slot = -1
#
#var main = get_tree().get_root().get_node_or_null("Main")
#if main:
#main.set_action_state(main.ActionState.NONE)
#_after_action_completed()
#return true
#
#return false
@rpc("any_peer", "reliable")
func request_server_put(grid_position: Vector2i, slot_index: int, x: int, y: int, z: int, item: int):
@@ -1092,24 +1190,23 @@ func notify_spawn_selected(spawn_pos: Vector2i):
enhanced_gridmap.normal_items[0]
)
func handle_put_action():
var main = get_tree().get_root().get_node_or_null("Main")
if not main or action_points < 1:
return
if not is_bot == true:
clear_highlights()
clear_playerboard_highlights()
# Highlight non-empty slots in playerboard
for i in range(playerboard.size()):
if playerboard[i] != -1: # Highlight occupied slots
var slot = main.playerboard_ui.get_child(i)
if slot.get_child_count() > 0:
slot.get_child(0).show() # Show highlight for occupied slots
highlighted_cells.append(i)
# Disabled, auto put activated
#func handle_put_action():
#var main = get_tree().get_root().get_node_or_null("Main")
#if not main or action_points < 1:
#return
#
#if not is_bot == true:
#clear_highlights()
#clear_playerboard_highlights()
#
## Highlight non-empty slots in playerboard
#for i in range(playerboard.size()):
#if playerboard[i] != -1: # Highlight occupied slots
#var slot = main.playerboard_ui.get_child(i)
#if slot.get_child_count() > 0:
#slot.get_child(0).show() # Show highlight for occupied slots
#highlighted_cells.append(i)
func handle_playerboard_slot_selected(slot_index: int):
var main = get_tree().get_root().get_node_or_null("Main")
@@ -1247,6 +1344,121 @@ func is_valid_arrangement_slot(from_slot: int, to_slot: int) -> bool:
return (row_diff == 1 and col_diff == 0) or (row_diff == 0 and col_diff == 1)
# Returns { slot_index: int, grid_position: Vector2i } or null if no valid put
func find_best_put_candidate() -> Dictionary:
# Convert goals to 2D (3x3)
var goals_2d = []
for i in range(3):
var row = []
for j in range(3):
row.append(goals[i * 3 + j])
goals_2d.append(row)
# Convert playerboard to 2D (5x5)
var board_2d = []
for i in range(5):
var row = []
for j in range(5):
row.append(playerboard[i * 5 + j])
board_2d.append(row)
# Step 1: Find misplaced or extra goal-matching items
var candidate_items = []
for board_i in range(5):
for board_j in range(5):
var item = board_2d[board_i][board_j]
if item == -1:
continue
var board_idx = board_i * 5 + board_j
# Is this item part of the goals?
if item not in goals:
continue
# Is it already in the correct central position?
var is_in_correct_central_spot = false
if board_i in [1,2,3] and board_j in [1,2,3]:
var goal_i = board_i - 1
var goal_j = board_j - 1
if goals_2d[goal_i][goal_j] == item:
is_in_correct_central_spot = true
if not is_in_correct_central_spot:
candidate_items.append({
"slot": board_idx,
"item": item
})
# Step 2: Find valid adjacent empty grid cells
var valid_cells = []
# Check current position
var current_cell_3d = Vector3i(current_position.x, 1, current_position.y)
if enhanced_gridmap.get_cell_item(current_cell_3d) == -1:
valid_cells.append(current_position)
# Check neighbors
var neighbors = enhanced_gridmap.get_neighbors(current_position, 0)
for neighbor in neighbors:
if neighbor.is_walkable:
var pos = neighbor.position
var cell_3d = Vector3i(pos.x, 1, pos.y)
if enhanced_gridmap.get_cell_item(cell_3d) == -1 and not is_position_occupied(pos):
valid_cells.append(pos)
if valid_cells.is_empty() or candidate_items.is_empty():
return {}
# Step 3: Prefer to put an item that *completes* a missing goal
for goal_i in range(3):
for goal_j in range(3):
var needed_item = goals_2d[goal_i][goal_j]
if needed_item == -1:
continue
# Check if central spot is empty
var board_i = goal_i + 1
var board_j = goal_j + 1
var central_slot = board_i * 5 + board_j
if playerboard[central_slot] == -1:
# Look for this item in candidate_items
for cand in candidate_items:
if cand.item == needed_item:
if not valid_cells.is_empty():
return {
"slot_index": cand.slot,
"grid_position": valid_cells[0] # pick first valid cell
}
# Fallback: just put any candidate item
return {
"slot_index": candidate_items[0].slot,
"grid_position": valid_cells[0]
}
# Finds the best slot in the playerboard for a given item based on goals
func find_best_goal_slot_for_item(item: int) -> int:
if item == -1:
return -1
# Convert goals to 2D (3x3)
var goals_2d = []
for i in range(3):
var row = []
for j in range(3):
row.append(goals[i * 3 + j])
goals_2d.append(row)
# Search for where this item should go in the central 3x3 (mapped to 5x5 board)
for i in range(3):
for j in range(3):
if goals_2d[i][j] == item:
var board_row = i + 1 # offset to center in 5x5
var board_col = j + 1
var slot_index = board_row * 5 + board_col
if playerboard[slot_index] == -1: # only if empty
return slot_index
# No ideal slot? Return any empty slot
return playerboard.find(-1)
func get_adjacent_playerboard_slots(slot_index) -> Array:
var adjacent = []
var row = slot_index / 5