diff --git a/assets/models/tiles/tile_diamond.tres b/assets/models/tiles/tile_diamond.tres index b130073..b593302 100644 --- a/assets/models/tiles/tile_diamond.tres +++ b/assets/models/tiles/tile_diamond.tres @@ -1,15 +1,15 @@ [gd_resource type="ArrayMesh" load_steps=4 format=4 uid="uid://dr80txgr61irt"] -[ext_resource type="Texture2D" uid="uid://dpkx1a780pvwv" path="res://assets/textures/tile_diamond.png" id="1_66odq"] +[ext_resource type="Texture2D" uid="uid://dpkx1a780pvwv" path="res://assets/textures/tile_diamond.png" id="1_fck33"] [sub_resource type="StandardMaterial3D" id="StandardMaterial3D_2vf4e"] resource_name = "boost" transparency = 1 cull_mode = 2 albedo_color = Color(0.91, 0.91, 0.91, 0.45098) -albedo_texture = ExtResource("1_66odq") +albedo_texture = ExtResource("1_fck33") -[sub_resource type="ArrayMesh" id="ArrayMesh_ff8mg"] +[sub_resource type="ArrayMesh" id="ArrayMesh_vxvvp"] _surfaces = [{ "aabb": AABB(-0.282176, -0.000324821, -0.282176, 0.564351, 0.050792, 0.564351), "format": 34896613377, @@ -39,4 +39,4 @@ _surfaces = [{ "vertex_data": PackedByteArray("AAD//////78AAP//////vwAA/////6oqAAD+/wAAAAAAAP7/AAD/vwAA/v8AAKoq/////////7//////////v//////////////+/wAAAAD///7/AAD/v////v8AAP////8AAP///7///wAA////P///AAD///////8AAAAAAAD//wAAAAD/P///AAAAAP//AAAAAP///78AAAAA////PwAAAAD//6oqAAAAAAAAAAAAAAAAAAD/PwAAAAAAAKoq/////////39U1VTV/7//v////39U1VTV/////////3//v/9//7//v////3//v/9//////wAA/3//v/9//7//vwAA/3//v/9//////wAA/39U1VTV/7//vwAA/39U1VTV") }] blend_shape_mode = 0 -shadow_mesh = SubResource("ArrayMesh_ff8mg") +shadow_mesh = SubResource("ArrayMesh_vxvvp") diff --git a/scenes/main.gd b/scenes/main.gd index 9f13dd6..a4110b7 100644 --- a/scenes/main.gd +++ b/scenes/main.gd @@ -21,9 +21,32 @@ const BOT_MOVE_INTERVAL: float = 2.0 # Time between bot movements in realtime m # Bot movement state tracking var moving_bots = {} # Dictionary to track which bots are currently moving +# Action State Management +enum ActionState { + NONE, + MOVING, + GRABBING, + PUTTING, + RANDOMIZING, + ARRANGING +} + +var current_action_state = ActionState.NONE + +# UI References +@onready var action_menu = $ActionMenu +@onready var move_button = $ActionMenu/ActionButtonContainer/MoveButton +@onready var grab_button = $ActionMenu/ActionButtonContainer/GrabButton +@onready var put_button = $ActionMenu/ActionButtonContainer/PutButton +@onready var randomize_button = $ActionMenu/ActionButtonContainer/RandomizeButton +@onready var arrange_button = $ActionMenu/ActionButtonContainer/ArrangeButton +@onready var playerboard_ui = $PlayerboardUI + func _ready(): multiplayer_peer.peer_connected.connect(_on_peer_connected) multiplayer_peer.peer_disconnected.connect(_on_peer_disconnected) + setup_action_buttons() + setup_playerboard_ui() func _process(delta): if multiplayer.is_server(): @@ -39,6 +62,96 @@ func _process(delta): if not moving_bots.get(bot_id, false): # Only move if bot isn't already moving move_bot(bot_id) +func setup_action_buttons(): + move_button.pressed.connect(func(): set_action_state(ActionState.MOVING)) + grab_button.pressed.connect(func(): set_action_state(ActionState.GRABBING)) + put_button.pressed.connect(func(): set_action_state(ActionState.PUTTING)) + randomize_button.pressed.connect(func(): set_action_state(ActionState.RANDOMIZING)) + arrange_button.pressed.connect(func(): set_action_state(ActionState.ARRANGING)) + +func setup_playerboard_ui(): + # Clear existing children + for child in playerboard_ui.get_children(): + child.queue_free() + + # Setup grid + playerboard_ui.columns = 5 + + # Create slots + for i in range(25): + var slot = TextureRect.new() + slot.custom_minimum_size = Vector2(40, 40) + slot.gui_input.connect(func(event): _on_playerboard_slot_clicked(event, i)) + # Add a colored rectangle as background + var style = StyleBoxFlat.new() + style.bg_color = Color(0.2, 0.2, 0.2, 1.0) + style.border_color = Color(0.4, 0.4, 0.4, 1.0) + slot.add_theme_stylebox_override("panel", style) + playerboard_ui.add_child(slot) + +func set_action_state(new_state): + if not local_player_character: + return + + current_action_state = new_state + + # Clear previous highlights + local_player_character.clear_highlights() + + match new_state: + ActionState.MOVING: + local_player_character.highlight_movement_range() + ActionState.GRABBING: + local_player_character.highlight_adjacent_cells() + ActionState.PUTTING: + local_player_character.highlight_empty_adjacent_cells() + ActionState.RANDOMIZING: + local_player_character.highlight_random_valid_cells() + ActionState.ARRANGING: + show_arrangement_ui() + +func update_button_states(): + if not local_player_character: + return + + var can_move = not local_player_character.is_player_moving and not local_player_character.has_performed_action + var can_grab = local_player_character.has_item_at_current_position() + var can_put = local_player_character.has_items_in_playerboard() and not local_player_character.has_item_at_current_position() + var can_randomize = not local_player_character.has_performed_action + var can_arrange = local_player_character.has_items_in_playerboard() + + move_button.disabled = not can_move + grab_button.disabled = not can_grab + put_button.disabled = not can_put + randomize_button.disabled = not can_randomize + arrange_button.disabled = not can_arrange + +func _on_playerboard_slot_clicked(event, slot_index): + if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT: + if current_action_state == ActionState.ARRANGING and local_player_character: + local_player_character.arrange_playerboard_item(slot_index) + +func update_playerboard_ui(): + if not local_player_character: + return + + for i in range(25): + var slot = playerboard_ui.get_child(i) + var item = local_player_character.playerboard[i] + if item != -1: + # Update slot appearance based on item + slot.modulate = Color(1, 1, 1, 1) + # You can set different colors or textures based on item type + slot.self_modulate = Color(0.5 + (item * 0.1), 0.7, 0.3, 1.0) + else: + slot.modulate = Color(0.5, 0.5, 0.5, 0.5) + slot.self_modulate = Color(1, 1, 1, 1) + +func show_arrangement_ui(): + if playerboard_ui: + playerboard_ui.visible = true + update_playerboard_ui() + func _on_host_pressed(): $NetworkInfo/NetworkSideDisplay.text = "Server" $Menu.visible = false @@ -85,6 +198,8 @@ func add_player_character(peer_id): player_character.add_to_group("Players", true) if peer_id == multiplayer.get_unique_id(): local_player_character = player_character + update_button_states() + update_playerboard_ui() func add_bot(bot_id): if multiplayer.is_server(): @@ -214,9 +329,55 @@ func _on_message_input_text_submitted(new_text): $MessageInput.text = "" $MessageInput.release_focus() -# --------- -# Bot Logic -# --------- +func move_bot(bot_id): + if multiplayer.is_server(): + if moving_bots.get(bot_id, false): + return + + var bot = get_node(str(bot_id)) + if bot and (turn_based_mode or not bot.is_player_moving): + moving_bots[bot_id] = true + + var best_move = evaluate_bot_move(bot) + + match best_move.action: + "grab": + if bot.grab_item(): + bot.has_performed_action = true + moving_bots[bot_id] = false + # Don't end turn, allow movement after grab + move_bot(bot_id) # Try to move after grabbing + "put": + if bot.put_item(): + bot.has_performed_action = true + moving_bots[bot_id] = false + # Don't end turn, allow movement after put + move_bot(bot_id) # Try to move after putting + "move": + if bot.has_performed_action: + var path = bot.enhanced_gridmap.find_path(Vector2(bot.current_position), Vector2(best_move.position)) + if path.size() > 1: + path.pop_front() + var trimmed_path = path.slice(0, bot.movement_range) + bot.rotate_towards_target(best_move.position) + bot.move_bot_along_path(trimmed_path, bot_id) + else: + moving_bots[bot_id] = false + if turn_based_mode: + end_current_turn() + else: + moving_bots[bot_id] = false + if turn_based_mode: + end_current_turn() + _: + moving_bots[bot_id] = false + if turn_based_mode: + end_current_turn() + +func bot_movement_completed(bot_id): + if multiplayer.is_server(): + moving_bots[bot_id] = false + func evaluate_bot_move(bot: Node) -> Dictionary: var best_move = { "action": "none", @@ -271,84 +432,3 @@ func evaluate_bot_move(bot: Node) -> Dictionary: } return best_move - -#func move_bot(bot_id): - #if multiplayer.is_server(): - #if moving_bots.get(bot_id, false): - #return # Skip if bot is already moving - # - #var bot = get_node(str(bot_id)) - #if bot and (turn_based_mode or not bot.is_player_moving): - #moving_bots[bot_id] = true # Mark bot as moving - #var target_position = bot.find_random_valid_position_in_range() # Use the new function - #if target_position != bot.current_position: - #var path = bot.enhanced_gridmap.find_path(Vector2(bot.current_position), Vector2(target_position)) - # - #if path.size() > 1: - #path.pop_front() - ## Trim path to respect movement range - #var trimmed_path = path.slice(0, bot.movement_range) - #bot.move_bot_along_path(trimmed_path, bot_id) - #else: - #print("No valid path found for bot") - #moving_bots[bot_id] = false - #if turn_based_mode: - #end_current_turn() - #else: - #print("No new valid position found for bot") - #moving_bots[bot_id] = false - #if turn_based_mode: - #end_current_turn() - #else: - #print("Bot not found or is moving") - #if turn_based_mode: - #end_current_turn() -func move_bot(bot_id): - if multiplayer.is_server(): - if moving_bots.get(bot_id, false): - return - - var bot = get_node(str(bot_id)) - if bot and (turn_based_mode or not bot.is_player_moving): - moving_bots[bot_id] = true - - var best_move = evaluate_bot_move(bot) - - match best_move.action: - "grab": - if bot.grab_item(): - bot.has_performed_action = true - moving_bots[bot_id] = false - # Don't end turn, allow movement after grab - move_bot(bot_id) # Try to move after grabbing - "put": - if bot.put_item(): - bot.has_performed_action = true - moving_bots[bot_id] = false - # Don't end turn, allow movement after put - move_bot(bot_id) # Try to move after putting - "move": - if bot.has_performed_action: - var path = bot.enhanced_gridmap.find_path(Vector2(bot.current_position), Vector2(best_move.position)) - if path.size() > 1: - path.pop_front() - var trimmed_path = path.slice(0, bot.movement_range) - bot.rotate_towards_target(best_move.position) - bot.move_bot_along_path(trimmed_path, bot_id) - else: - moving_bots[bot_id] = false - if turn_based_mode: - end_current_turn() - else: - moving_bots[bot_id] = false - if turn_based_mode: - end_current_turn() - _: - moving_bots[bot_id] = false - if turn_based_mode: - end_current_turn() - -# Called when a bot finishes moving -func bot_movement_completed(bot_id): - if multiplayer.is_server(): - moving_bots[bot_id] = false diff --git a/scenes/main.tscn b/scenes/main.tscn index e8ac5f0..74c42f5 100644 --- a/scenes/main.tscn +++ b/scenes/main.tscn @@ -10,7 +10,6 @@ [node name="Main" type="Node3D"] script = ExtResource("1_xcpe3") -turn_based_mode = false [node name="EnhancedGridMap" type="GridMap" parent="."] mesh_library = ExtResource("1_110wo") @@ -45,6 +44,55 @@ offset_bottom = 59.0 grow_horizontal = 2 theme_override_styles/panel = ExtResource("5_dvx6y") +[node name="PlayerboardUI" type="GridContainer" parent="."] +custom_minimum_size = Vector2(200, 200) +anchors_preset = 2 +anchor_top = 1.0 +anchor_bottom = 1.0 +offset_left = 30.0 +offset_top = -230.0 +offset_right = 230.0 +offset_bottom = -30.0 +grow_vertical = 0 +columns = 5 + +[node name="ActionMenu" type="Control" parent="."] +layout_mode = 3 +anchors_preset = 3 +anchor_left = 1.0 +anchor_top = 1.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +offset_left = -145.0 +offset_top = -218.0 +grow_horizontal = 0 +grow_vertical = 0 + +[node name="ActionButtonContainer" type="VBoxContainer" parent="ActionMenu"] +layout_mode = 0 +offset_right = 40.0 +offset_bottom = 40.0 + +[node name="MoveButton" type="Button" parent="ActionMenu/ActionButtonContainer"] +layout_mode = 2 +text = "Move" + +[node name="GrabButton" type="Button" parent="ActionMenu/ActionButtonContainer"] +layout_mode = 2 +text = "Grab" + +[node name="PutButton" type="Button" parent="ActionMenu/ActionButtonContainer"] +layout_mode = 2 +text = "Put" + +[node name="RandomizeButton" type="Button" parent="ActionMenu/ActionButtonContainer"] +layout_mode = 2 +text = "Randomize" + +[node name="ArrangeButton" type="Button" parent="ActionMenu/ActionButtonContainer"] +layout_mode = 2 +text = "Arrange" + [node name="NetworkInfo" type="VBoxContainer" parent="."] anchors_preset = 5 anchor_left = 0.5 @@ -130,6 +178,8 @@ alignment = 1 [node name="WorldEnvironment" type="WorldEnvironment" parent="."] environment = ExtResource("4_ky38j") +[node name="CanvasLayer" type="CanvasLayer" parent="."] + [connection signal="pressed" from="Menu/Host" to="." method="_on_host_pressed"] [connection signal="pressed" from="Menu/Join" to="." method="_on_join_pressed"] [connection signal="text_submitted" from="MessageInput" to="." method="_on_message_input_text_submitted"] diff --git a/scenes/player.gd b/scenes/player.gd index 913cb6a..5b4691c 100644 --- a/scenes/player.gd +++ b/scenes/player.gd @@ -18,7 +18,6 @@ var is_player_moving: bool = false var has_performed_action: bool = false # Track if grab/put action has been done # Player Rotation Config -# Add these variables for rotation var target_rotation: float = 0.0 var rotation_speed: float = 10.0 # Adjust this to control rotation speed @@ -37,6 +36,8 @@ var rotation_speed: float = 10.0 # Adjust this to control rotation speed @export var has_moved_this_turn = false @onready var main_scene = get_tree().current_scene +var highlighted_cells = [] + func _ready(): name = str(get_multiplayer_authority()) $Name.text = str(name) @@ -58,7 +59,9 @@ func _ready(): current_position = find_valid_starting_position() update_player_position(current_position) - set_process_unhandled_input(is_multiplayer_authority()) + # Only set up input processing if this is not a bot + if not is_in_group("Bots"): + set_process_unhandled_input(is_multiplayer_authority()) # Initialize random goals initialize_random_goals() @@ -67,6 +70,57 @@ func _ready(): playerboard.resize(25) playerboard.fill(-1) +func _physics_process(_delta): + if is_multiplayer_authority(): + rpc("remote_set_position", global_position) + +func _process(_delta): + # Skip processing for bots + if is_in_group("Bots"): + return + + if is_multiplayer_authority(): + if Input.is_action_just_pressed("grab_item"): + if grab_item(): + rpc("display_message", "Item grabbed!") + elif Input.is_action_just_pressed("put_item"): + if put_item(): + rpc("display_message", "Item placed!") + +func _unhandled_input(event): + # First check if this is a bot + if is_in_group("Bots"): + return + + var main = get_node("/root/Main") + if not is_multiplayer_authority() or (main.turn_based_mode and (not is_my_turn or is_player_moving)): + return + + if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT: + var camera = get_viewport().get_camera_3d() + var from = camera.project_ray_origin(event.position) + var to = from + camera.project_ray_normal(event.position) * 1000 + + var click_position = raycast_to_grid(from, to) + if click_position != Vector2i(-1, -1): + handle_grid_click(click_position) + +func handle_grid_click(grid_position: Vector2i): + var main = get_node("/root/Main") + match main.current_action_state: + main.ActionState.MOVING: + if grid_position in highlighted_cells: + move_player_to_clicked_position(grid_position) + main.ActionState.GRABBING: + if grid_position in highlighted_cells: + grab_item_at_position(grid_position) + main.ActionState.PUTTING: + if grid_position in highlighted_cells: + put_item_at_position(grid_position) + main.ActionState.RANDOMIZING: + if grid_position in highlighted_cells: + randomize_item_at_position(grid_position) + func find_valid_starting_position() -> Vector2i: var rng = RandomNumberGenerator.new() rng.randomize() @@ -96,7 +150,6 @@ func find_random_valid_position_in_range() -> Vector2i: var range_x = min(enhanced_gridmap.columns - 1, movement_range) var range_y = min(enhanced_gridmap.rows - 1, movement_range) - # Generate position within movement range of current position var offset_x = rng.randi_range(-range_x, range_x) var offset_y = rng.randi_range(-range_y, range_y) @@ -105,7 +158,6 @@ func find_random_valid_position_in_range() -> Vector2i: clamp(current_position.y + offset_y, 0, enhanced_gridmap.rows - 1) ) - # Check if the position is within movement range if not is_within_movement_range(random_position): attempts += 1 continue @@ -119,34 +171,6 @@ func find_random_valid_position_in_range() -> Vector2i: return current_position -func _physics_process(_delta): - if is_multiplayer_authority(): - rpc("remote_set_position", global_position) - -# Add this to your _process function if you don't have one -func _process(delta): - if is_multiplayer_authority(): - if Input.is_action_just_pressed("grab_item"): # Define this input in project settings - if grab_item(): - rpc("display_message", "Item grabbed!") - elif Input.is_action_just_pressed("put_item"): # Define this input in project settings - if put_item(): - rpc("display_message", "Item placed!") - -func _unhandled_input(event): - var main = get_node("/root/Main") - if not is_multiplayer_authority() or (main.turn_based_mode and (not is_my_turn or is_player_moving)): - return - - if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT: - var camera = get_viewport().get_camera_3d() - var from = camera.project_ray_origin(event.position) - var to = from + camera.project_ray_normal(event.position) * 1000 - - var click_position = raycast_to_grid(from, to) - if click_position != Vector2i(-1, -1): - move_player_to_clicked_position(click_position) - func raycast_to_grid(from: Vector3, to: Vector3) -> Vector2i: var plane = Plane(Vector3.UP, cell_offset.y) var intersection = plane.intersects_ray(from, to - from) @@ -176,44 +200,24 @@ func is_within_movement_range(target_position: Vector2i) -> bool: abs(target_position.y - current_position.y) return distance <= movement_range -#func move_player_to_clicked_position(grid_position: Vector2i): - #if not is_multiplayer_authority(): - #return - # - #if is_player_moving: - #return - # - ## Check if the movement is within range - #if not is_within_movement_range(grid_position): - #print("Movement out of range") - #return - # - #var cell_item = enhanced_gridmap.get_cell_item(Vector3i(grid_position.x, 0, grid_position.y)) - # - #if cell_item in enhanced_gridmap.non_walkable_items: - #print("Cannot move to non-walkable cell") - #return - # - #var path = enhanced_gridmap.find_path(Vector2(current_position), Vector2(grid_position)) - # - #if path.size() > 1: - #path.pop_front() - #rpc("start_movement_along_path", path) - #else: - #print("No valid path found") - func move_player_to_clicked_position(grid_position: Vector2i): if not is_multiplayer_authority(): return if is_player_moving: return + + var main = get_node("/root/Main") - # Check if player has performed grab/put action - if not has_performed_action and current_position != grid_position: - rpc("display_message", "Must grab or place item before moving!") + # Check if we're in the correct action state + if main.current_action_state != main.ActionState.MOVING: return + # Validate the clicked position is highlighted + if not grid_position in highlighted_cells: + return + + # Check if movement is valid if not is_within_movement_range(grid_position): print("Movement out of range") return @@ -231,6 +235,7 @@ func move_player_to_clicked_position(grid_position: Vector2i): if path.size() > 1: path.pop_front() rpc("start_movement_along_path", path) + clear_highlights() # Clear highlights after movement starts else: print("No valid path found") @@ -248,15 +253,18 @@ func start_movement_along_path(path: Array): tween.tween_callback(func(): current_position = Vector2i(path[-1].x, path[-1].y) is_player_moving = false - has_performed_action = false # Reset the action flag after movement enhanced_gridmap.clear_path_visualization() has_moved_this_turn = true var main = get_node("/root/Main") + + # Reset the action state + main.set_action_state(main.ActionState.NONE) + if main.turn_based_mode: end_turn() + _after_action_completed() ) - func move_bot_along_path(path: Array, bot_id: int): if not is_multiplayer_authority(): return @@ -315,6 +323,7 @@ func start_turn(): is_my_turn = true if is_multiplayer_authority(): rpc("display_message", "It's your turn!") + _after_action_completed() func end_turn(): is_my_turn = false @@ -367,8 +376,37 @@ func grab_item() -> bool: rpc("sync_playerboard", playerboard) has_performed_action = true # Set action flag + _after_action_completed() return true +func grab_item_at_position(grid_position: Vector2i) -> bool: + var main = get_node("/root/Main") + if main.current_action_state != main.ActionState.GRABBING: + return false + + var cell = Vector3i(grid_position.x, 1, grid_position.y) + var item = enhanced_gridmap.get_cell_item(cell) + + if item == -1: + return false + + var empty_slot = playerboard.find(-1) + if empty_slot == -1: + return false + + playerboard[empty_slot] = item + enhanced_gridmap.set_cell_item(cell, -1) + + if is_multiplayer_authority(): + rpc("sync_playerboard", playerboard) + + has_performed_action = true + clear_highlights() + main.set_action_state(main.ActionState.MOVING) # Automatically switch to movement state + _after_action_completed() + return true + + func put_item() -> bool: if not enhanced_gridmap: return false @@ -397,6 +435,63 @@ func put_item() -> bool: rpc("sync_playerboard", playerboard) has_performed_action = true # Set action flag + _after_action_completed() + return true + +func put_item_at_position(grid_position: Vector2i) -> bool: + var main = get_node("/root/Main") + if main.current_action_state != main.ActionState.PUTTING: + return false + + var cell = Vector3i(grid_position.x, 1, grid_position.y) + if enhanced_gridmap.get_cell_item(cell) != -1: + return false + + var item_to_put = -1 + var item_index = -1 + + for goal in goals: + var index = playerboard.find(goal) + if index != -1: + item_to_put = goal + item_index = index + break + + if item_to_put == -1: + return false + + enhanced_gridmap.set_cell_item(cell, item_to_put) + playerboard[item_index] = -1 + + if is_multiplayer_authority(): + rpc("sync_playerboard", playerboard) + + has_performed_action = true + clear_highlights() + main.set_action_state(main.ActionState.MOVING) # Automatically switch to movement state + _after_action_completed() + return true + +func randomize_item_at_position(grid_position: Vector2i) -> bool: + var main = get_node("/root/Main") + if main.current_action_state != main.ActionState.RANDOMIZING: + return false + + var cell = Vector3i(grid_position.x, 1, grid_position.y) + if enhanced_gridmap.get_cell_item(cell) != -1: + return false + + var rng = RandomNumberGenerator.new() + rng.randomize() + + # Assuming items are numbered 1-10 + var random_item = rng.randi_range(7, 10) + enhanced_gridmap.set_cell_item(cell, random_item) + + has_performed_action = true + clear_highlights() + main.set_action_state(main.ActionState.MOVING) # Automatically switch to movement state + _after_action_completed() return true @rpc("any_peer", "call_local") @@ -406,6 +501,7 @@ func sync_goals(new_goals: Array): @rpc("any_peer", "call_local") func sync_playerboard(new_playerboard: Array): playerboard = new_playerboard + _after_action_completed() func rotate_towards_target(target_pos: Vector2i): var direction = Vector2( @@ -418,3 +514,107 @@ func rotate_towards_target(target_pos: Vector2i): # Create a tween for smooth rotation var tween = create_tween() tween.tween_property(self, "rotation:y", target_rotation, 0.2) + +func _after_action_completed(): + if multiplayer.get_unique_id() == get_multiplayer_authority(): + var main = get_node("/root/Main") + main.update_button_states() + main.update_playerboard_ui() + +func has_item_at_current_position() -> bool: + var current_cell = Vector3i(current_position.x, 1, current_position.y) + return enhanced_gridmap.get_cell_item(current_cell) != -1 + +func has_items_in_playerboard() -> bool: + return playerboard.any(func(item): return item != -1) + +func highlight_movement_range(): + var cells_to_highlight = [] + for x in range(enhanced_gridmap.columns): + for z in range(enhanced_gridmap.rows): + var test_pos = Vector2i(x, z) + if is_within_movement_range(test_pos): + var cell_item = enhanced_gridmap.get_cell_item(Vector3i(x, 0, z)) + if cell_item != -1 and not (cell_item in enhanced_gridmap.non_walkable_items): + cells_to_highlight.append(test_pos) + + for cell in cells_to_highlight: + highlighted_cells.append(cell) + # Store original item for restoration + var original_item = enhanced_gridmap.get_cell_item(Vector3i(cell.x, 0, cell.y)) + enhanced_gridmap.set_cell_item(Vector3i(cell.x, 0, cell.y), enhanced_gridmap.hover_item) + +func highlight_adjacent_cells(): + var neighbors = enhanced_gridmap.get_neighbors(current_position, 0) + for neighbor in neighbors: + if neighbor.is_walkable: + var cell_pos = neighbor.position + if enhanced_gridmap.get_cell_item(Vector3i(cell_pos.x, 1, cell_pos.y)) != -1: + highlighted_cells.append(cell_pos) + enhanced_gridmap.set_cell_item(Vector3i(cell_pos.x, 0, cell_pos.y), enhanced_gridmap.hover_item) + +func highlight_empty_adjacent_cells(): + var neighbors = enhanced_gridmap.get_neighbors(current_position, 0) + for neighbor in neighbors: + if neighbor.is_walkable: + var cell_pos = neighbor.position + if enhanced_gridmap.get_cell_item(Vector3i(cell_pos.x, 1, cell_pos.y)) == -1: + highlighted_cells.append(cell_pos) + enhanced_gridmap.set_cell_item(Vector3i(cell_pos.x, 0, cell_pos.y), enhanced_gridmap.hover_item) + +func highlight_random_valid_cells(): + var valid_cells = [] + for x in range(enhanced_gridmap.columns): + for z in range(enhanced_gridmap.rows): + var cell_pos = Vector2i(x, z) + var cell_item = enhanced_gridmap.get_cell_item(Vector3i(x, 0, z)) + if cell_item != -1 and not (cell_item in enhanced_gridmap.non_walkable_items): + valid_cells.append(cell_pos) + + # Highlight random subset of valid cells + var rng = RandomNumberGenerator.new() + rng.randomize() + for _i in range(min(5, valid_cells.size())): + var index = rng.randi() % valid_cells.size() + var cell = valid_cells[index] + highlighted_cells.append(cell) + enhanced_gridmap.set_cell_item(Vector3i(cell.x, 0, cell.y), enhanced_gridmap.hover_item) + valid_cells.remove_at(index) + +func clear_highlights(): + for cell in highlighted_cells: + # Reset to normal item + enhanced_gridmap.set_cell_item(Vector3i(cell.x, 0, cell.y), enhanced_gridmap.normal_items[0]) + highlighted_cells.clear() + +func arrange_playerboard_item(slot_index: int): + if playerboard[slot_index] == -1: + return + + # Store the selected slot for arrangement + var temp_item = playerboard[slot_index] + playerboard[slot_index] = -1 + + # Wait for second click to complete arrangement + await get_tree().create_timer(0.1).timeout + + var target_slot = await wait_for_playerboard_click() + if target_slot != -1: + var swap_item = playerboard[target_slot] + playerboard[target_slot] = temp_item + playerboard[slot_index] = swap_item + + if multiplayer.is_server(): + rpc("sync_playerboard", playerboard) + +func wait_for_playerboard_click() -> int: + var main = get_node("/root/Main") + while true: + var event = await main.playerboard_ui.gui_input + if event is InputEventMouseButton and event.pressed and event.button_index == MOUSE_BUTTON_LEFT: + var local_pos = main.playerboard_ui.get_local_mouse_position() + for i in range(25): + var slot = main.playerboard_ui.get_child(i) + if slot.get_global_rect().has_point(event.global_position): + return i + return -1