update feature & bugfix
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
.vscode/
|
.vscode/
|
||||||
.agent/
|
.agent/
|
||||||
_daily_basis/
|
_daily_basis/
|
||||||
|
_daily_changes/
|
||||||
|
|
||||||
/android/
|
/android/
|
||||||
.tmp
|
.tmp
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 29 KiB |
@@ -1,41 +0,0 @@
|
|||||||
[remap]
|
|
||||||
|
|
||||||
importer="texture"
|
|
||||||
type="CompressedTexture2D"
|
|
||||||
uid="uid://dq75sm8vwoei0"
|
|
||||||
path.s3tc="res://.godot/imported/modal.png-43ddf0c61a3419805cffadd2ed5d63ce.s3tc.ctex"
|
|
||||||
metadata={
|
|
||||||
"imported_formats": ["s3tc_bptc"],
|
|
||||||
"vram_texture": true
|
|
||||||
}
|
|
||||||
|
|
||||||
[deps]
|
|
||||||
|
|
||||||
source_file="res://assets/graphics/pop_up_window/modal.png"
|
|
||||||
dest_files=["res://.godot/imported/modal.png-43ddf0c61a3419805cffadd2ed5d63ce.s3tc.ctex"]
|
|
||||||
|
|
||||||
[params]
|
|
||||||
|
|
||||||
compress/mode=2
|
|
||||||
compress/high_quality=false
|
|
||||||
compress/lossy_quality=0.7
|
|
||||||
compress/uastc_level=0
|
|
||||||
compress/rdo_quality_loss=0.0
|
|
||||||
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/channel_remap/red=0
|
|
||||||
process/channel_remap/green=1
|
|
||||||
process/channel_remap/blue=2
|
|
||||||
process/channel_remap/alpha=3
|
|
||||||
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
|
|
||||||
@@ -23,7 +23,6 @@ ssao_intensity = 0.5
|
|||||||
ssao_power = 100.0
|
ssao_power = 100.0
|
||||||
ssao_horizon = 0.1
|
ssao_horizon = 0.1
|
||||||
sdfgi_cascades = 1
|
sdfgi_cascades = 1
|
||||||
sdfgi_max_distance = 25.6
|
|
||||||
sdfgi_energy = 0.5
|
sdfgi_energy = 0.5
|
||||||
glow_levels/2 = 0.6
|
glow_levels/2 = 0.6
|
||||||
glow_levels/3 = 0.6
|
glow_levels/3 = 0.6
|
||||||
|
|||||||
+16
-3
@@ -1,7 +1,9 @@
|
|||||||
[gd_scene load_steps=3 format=3 uid="uid://cyfjwldknv8m6"]
|
[gd_scene load_steps=5 format=3 uid="uid://cyfjwldknv8m6"]
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://vgyrq5y5p7jw" path="res://scripts/ui/boot_screen.gd" id="1"]
|
[ext_resource type="Script" uid="uid://vgyrq5y5p7jw" path="res://scripts/ui/boot_screen.gd" id="1"]
|
||||||
[ext_resource type="Theme" uid="uid://da337sh5qxi0s" path="res://assets/themes/ui_theme.tres" id="2"]
|
[ext_resource type="Theme" uid="uid://da337sh5qxi0s" path="res://assets/themes/ui_theme.tres" id="2"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://40tlo0mda3wr" path="res://assets/graphics/main_menu/result_bg.png" id="3_v46t4"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://dvp0as6yyudco" path="res://assets/graphics/main_menu/bg_illust.png" id="4_okh44"]
|
||||||
|
|
||||||
[node name="BootScreen" type="Control"]
|
[node name="BootScreen" type="Control"]
|
||||||
layout_mode = 3
|
layout_mode = 3
|
||||||
@@ -13,14 +15,25 @@ grow_vertical = 2
|
|||||||
theme = ExtResource("2")
|
theme = ExtResource("2")
|
||||||
script = ExtResource("1")
|
script = ExtResource("1")
|
||||||
|
|
||||||
[node name="Background" type="ColorRect" parent="."]
|
[node name="Background" type="TextureRect" parent="."]
|
||||||
layout_mode = 1
|
layout_mode = 1
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
grow_horizontal = 2
|
grow_horizontal = 2
|
||||||
grow_vertical = 2
|
grow_vertical = 2
|
||||||
color = Color(0.12, 0.1, 0.08, 1)
|
texture = ExtResource("3_v46t4")
|
||||||
|
expand_mode = 2
|
||||||
|
|
||||||
|
[node name="Background2" type="TextureRect" parent="."]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
texture = ExtResource("4_okh44")
|
||||||
|
expand_mode = 3
|
||||||
|
|
||||||
[node name="CenterContainer" type="CenterContainer" parent="."]
|
[node name="CenterContainer" type="CenterContainer" parent="."]
|
||||||
layout_mode = 1
|
layout_mode = 1
|
||||||
|
|||||||
+12
-1
@@ -1,8 +1,9 @@
|
|||||||
[gd_scene load_steps=4 format=3 uid="uid://b7nxt2hc4kqp8"]
|
[gd_scene load_steps=5 format=3 uid="uid://b7nxt2hc4kqp8"]
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://b5q6yekyk0tld" path="res://scenes/lobby.gd" id="1_lp6xi"]
|
[ext_resource type="Script" uid="uid://b5q6yekyk0tld" path="res://scenes/lobby.gd" id="1_lp6xi"]
|
||||||
[ext_resource type="Theme" uid="uid://da337sh5qxi0s" path="res://assets/themes/ui_theme.tres" id="2_theme"]
|
[ext_resource type="Theme" uid="uid://da337sh5qxi0s" path="res://assets/themes/ui_theme.tres" id="2_theme"]
|
||||||
[ext_resource type="Texture2D" uid="uid://2d1ks5pmblc7" path="res://assets/graphics/main_menu/bg_back.png" id="3_q60fs"]
|
[ext_resource type="Texture2D" uid="uid://2d1ks5pmblc7" path="res://assets/graphics/main_menu/bg_back.png" id="3_q60fs"]
|
||||||
|
[ext_resource type="Texture2D" uid="uid://dvp0as6yyudco" path="res://assets/graphics/main_menu/bg_illust.png" id="4_nqcc7"]
|
||||||
|
|
||||||
[node name="Lobby" type="Control"]
|
[node name="Lobby" type="Control"]
|
||||||
layout_mode = 3
|
layout_mode = 3
|
||||||
@@ -24,6 +25,16 @@ grow_vertical = 2
|
|||||||
texture = ExtResource("3_q60fs")
|
texture = ExtResource("3_q60fs")
|
||||||
expand_mode = 2
|
expand_mode = 2
|
||||||
|
|
||||||
|
[node name="Background2" type="TextureRect" parent="."]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
texture = ExtResource("4_nqcc7")
|
||||||
|
expand_mode = 2
|
||||||
|
|
||||||
[node name="MainMenuPanel" type="PanelContainer" parent="."]
|
[node name="MainMenuPanel" type="PanelContainer" parent="."]
|
||||||
layout_mode = 1
|
layout_mode = 1
|
||||||
anchors_preset = 8
|
anchors_preset = 8
|
||||||
|
|||||||
+23
-11
@@ -367,15 +367,17 @@ func _setup_client_game():
|
|||||||
add_player_character(p_id)
|
add_player_character(p_id)
|
||||||
print("Client: Pre-spawned player ", p_id)
|
print("Client: Pre-spawned player ", p_id)
|
||||||
|
|
||||||
# Pre-spawn potential bots (IDs 2 to MaxPlayers) to prevent RPC "Node not found" errors
|
# Pre-spawn potential bots (IDs from count+1 to MaxPlayers) to prevent RPC "Node not found" errors
|
||||||
# Bots use small integer IDs (2, 3, 4...) while clients use large unique IDs
|
# Bots use small integer IDs (e.g. 2, 3, 4...) while clients use large unique IDs
|
||||||
if GameStateManager.enable_bots:
|
if GameStateManager.enable_bots:
|
||||||
for i in range(2, GameStateManager.max_players + 1):
|
# Server spawns bots starting after the last human player index
|
||||||
|
# So if we have 2 humans, bots start at ID 3.
|
||||||
|
var start_bot_id = lobby_players.size() + 1
|
||||||
|
for i in range(start_bot_id, GameStateManager.max_players + 1):
|
||||||
# Only spawn if not already existing (e.g. if a human somehow got this ID, though unlikely)
|
# Only spawn if not already existing (e.g. if a human somehow got this ID, though unlikely)
|
||||||
if not has_node(str(i)):
|
if not has_node(str(i)):
|
||||||
add_player_character(i)
|
# Spawning as BOT
|
||||||
get_node(str(i)).is_bot = true # Assume bot initially
|
add_player_character(i, true)
|
||||||
get_node(str(i)).add_to_group("Bots", true)
|
|
||||||
print("Client: Pre-spawned potential bot ", i)
|
print("Client: Pre-spawned potential bot ", i)
|
||||||
|
|
||||||
# Ensure local player setup (UI, controls) is verified
|
# Ensure local player setup (UI, controls) is verified
|
||||||
@@ -488,18 +490,28 @@ func create_bot(bot_id: int):
|
|||||||
|
|
||||||
var goal_index = bot_id - 1
|
var goal_index = bot_id - 1
|
||||||
if goal_index < GoalManager.preset_goals.size():
|
if goal_index < GoalManager.preset_goals.size():
|
||||||
# Wait for bot managers to be ready
|
# Wait for bot managers to be ready (race_manager is created at T=0.5)
|
||||||
await get_tree().create_timer(0.2).timeout
|
await get_tree().create_timer(0.75).timeout
|
||||||
bot_character.goals = GoalManager.preset_goals[goal_index].duplicate()
|
bot_character.goals = GoalManager.preset_goals[goal_index].duplicate()
|
||||||
# Use deferred goals sync to avoid timing issues
|
# Use deferred goals sync to avoid timing issues
|
||||||
call_deferred("_deferred_set_player_goals", bot_id, bot_character.goals)
|
call_deferred("_deferred_set_player_goals", bot_id, bot_character.goals)
|
||||||
|
|
||||||
@rpc("any_peer", "call_local")
|
@rpc("any_peer", "call_local")
|
||||||
func add_player_character(peer_id: int):
|
func add_player_character(peer_id: int, is_bot: bool = false):
|
||||||
if has_node(str(peer_id)):
|
if has_node(str(peer_id)):
|
||||||
return
|
return
|
||||||
|
|
||||||
var player_character = PlayerManager.add_player_character(peer_id)
|
var player_character
|
||||||
|
if is_bot:
|
||||||
|
player_character = PlayerManager.create_bot(peer_id)
|
||||||
|
player_character.add_to_group("Bots", true)
|
||||||
|
else:
|
||||||
|
player_character = PlayerManager.add_player_character(peer_id)
|
||||||
|
|
||||||
|
# Set properties BEFORE adding to tree (ensure _ready sees correct state)
|
||||||
|
# create_bot already sets is_bot=true, but we ensure consistency
|
||||||
|
player_character.is_bot = is_bot
|
||||||
|
|
||||||
add_child(player_character)
|
add_child(player_character)
|
||||||
player_character.add_to_group("Players", true)
|
player_character.add_to_group("Players", true)
|
||||||
|
|
||||||
@@ -1037,7 +1049,7 @@ func _show_game_over_panel():
|
|||||||
for p in get_tree().get_nodes_in_group("Players"):
|
for p in get_tree().get_nodes_in_group("Players"):
|
||||||
player_scores.append({
|
player_scores.append({
|
||||||
"name": p.display_name if not p.display_name.is_empty() else str(p.name),
|
"name": p.display_name if not p.display_name.is_empty() else str(p.name),
|
||||||
"score": goals_cycle_manager.get_player_score(p.get_multiplayer_authority()) if goals_cycle_manager else 0
|
"score": goals_cycle_manager.get_player_score(p.name.to_int()) if goals_cycle_manager else 0
|
||||||
})
|
})
|
||||||
player_scores.sort_custom(func(a, b): return a.score > b.score)
|
player_scores.sort_custom(func(a, b): return a.score > b.score)
|
||||||
|
|
||||||
|
|||||||
+107
-9
@@ -173,9 +173,6 @@ func _ready():
|
|||||||
# =========================================================================
|
# =========================================================================
|
||||||
# BOT-SPECIFIC SETUP - BotController handles bot AI, we just disable input
|
# BOT-SPECIFIC SETUP - BotController handles bot AI, we just disable input
|
||||||
# =========================================================================
|
# =========================================================================
|
||||||
# =========================================================================
|
|
||||||
# BOT-SPECIFIC SETUP - BotController handles bot AI
|
|
||||||
# =========================================================================
|
|
||||||
if is_bot == true or is_in_group("Bots"):
|
if is_bot == true or is_in_group("Bots"):
|
||||||
# Disable input processing for bots
|
# Disable input processing for bots
|
||||||
set_process_input(false)
|
set_process_input(false)
|
||||||
@@ -183,10 +180,11 @@ func _ready():
|
|||||||
|
|
||||||
# Set initial position for bots
|
# Set initial position for bots
|
||||||
if enhanced_gridmap:
|
if enhanced_gridmap:
|
||||||
current_position = _find_random_spawn_position()
|
|
||||||
update_player_position(current_position)
|
|
||||||
spawn_point_selected = true
|
|
||||||
if is_multiplayer_authority():
|
if is_multiplayer_authority():
|
||||||
|
current_position = _find_random_spawn_position()
|
||||||
|
update_player_position(current_position)
|
||||||
|
spawn_point_selected = true
|
||||||
|
rpc("set_spawn_position", current_position)
|
||||||
rpc("notify_spawn_selected", current_position)
|
rpc("notify_spawn_selected", current_position)
|
||||||
|
|
||||||
# Assign bot character (deterministic based on ID to match lobby preview)
|
# Assign bot character (deterministic based on ID to match lobby preview)
|
||||||
@@ -603,6 +601,96 @@ func _apply_tint_recursive(node: Node, color: Color):
|
|||||||
for child in node.get_children():
|
for child in node.get_children():
|
||||||
_apply_tint_recursive(child, color)
|
_apply_tint_recursive(child, color)
|
||||||
|
|
||||||
|
var immunity_timer: float = 0.0
|
||||||
|
|
||||||
|
@rpc("any_peer", "call_local")
|
||||||
|
func apply_stagger(duration: float = 1.5):
|
||||||
|
if immunity_timer > 0:
|
||||||
|
return # Immune!
|
||||||
|
|
||||||
|
if is_frozen:
|
||||||
|
return # Already staggered
|
||||||
|
|
||||||
|
is_frozen = true
|
||||||
|
_apply_tint_recursive(self, Color.BLUE) # Visual feedback
|
||||||
|
|
||||||
|
# Set immunity (3 seconds as requested)
|
||||||
|
immunity_timer = 3.0
|
||||||
|
|
||||||
|
print("Player %s staggered for %.1f seconds" % [name, duration])
|
||||||
|
|
||||||
|
if is_multiplayer_authority():
|
||||||
|
rpc("display_message", "C R U S H E D !", 4) # MessageType.WARNING
|
||||||
|
drop_random_item()
|
||||||
|
|
||||||
|
# Grant "Smashed" Bonus (1 bar, max 2)
|
||||||
|
if powerup_manager:
|
||||||
|
powerup_manager.acquire_smash_bonus()
|
||||||
|
|
||||||
|
await get_tree().create_timer(duration).timeout
|
||||||
|
|
||||||
|
is_frozen = false
|
||||||
|
# If still immune, show immunity tint (Green?), otherwise White
|
||||||
|
if immunity_timer > 0:
|
||||||
|
_apply_tint_recursive(self, Color(0.5, 1.0, 0.5)) # Light Green for immunity
|
||||||
|
else:
|
||||||
|
_apply_tint_recursive(self, Color.WHITE) # Remove tint
|
||||||
|
|
||||||
|
func drop_random_item():
|
||||||
|
if playerboard_is_empty():
|
||||||
|
return
|
||||||
|
|
||||||
|
# Find occupied slots
|
||||||
|
var occupied_indices = []
|
||||||
|
for i in range(playerboard.size()):
|
||||||
|
if playerboard[i] != -1:
|
||||||
|
occupied_indices.append(i)
|
||||||
|
|
||||||
|
if occupied_indices.size() == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
# Pick random slot
|
||||||
|
var slot_index = occupied_indices.pick_random()
|
||||||
|
var item_id = playerboard[slot_index]
|
||||||
|
|
||||||
|
# Try to find empty spot on grid near player
|
||||||
|
var drop_pos = _find_valid_drop_position()
|
||||||
|
|
||||||
|
if drop_pos != Vector2i(-1, -1):
|
||||||
|
# Drop it
|
||||||
|
playerboard[slot_index] = -1
|
||||||
|
rpc("sync_playerboard", playerboard)
|
||||||
|
|
||||||
|
# Sync grid item
|
||||||
|
var cell = Vector3i(drop_pos.x, 0, drop_pos.y)
|
||||||
|
rpc("sync_grid_item", cell.x, cell.y, cell.z, item_id)
|
||||||
|
|
||||||
|
rpc("display_message", "Dropped item!", 4)
|
||||||
|
print("Player %s dropped item %d at %s" % [name, item_id, drop_pos])
|
||||||
|
|
||||||
|
func playerboard_is_empty() -> bool:
|
||||||
|
for item in playerboard:
|
||||||
|
if item != -1:
|
||||||
|
return false
|
||||||
|
return true
|
||||||
|
|
||||||
|
func _find_valid_drop_position() -> Vector2i:
|
||||||
|
# Try random adjacent cells
|
||||||
|
var neighbors = enhanced_gridmap.get_neighbors(current_position, 0)
|
||||||
|
neighbors.shuffle()
|
||||||
|
|
||||||
|
for neighbor in neighbors:
|
||||||
|
var pos = neighbor.position
|
||||||
|
if enhanced_gridmap.get_cell_item(Vector3i(pos.x, 0, pos.y)) == -1: # Empty floor? No, 0 is floor. -1 is void?
|
||||||
|
# Wait, items are on layer 1 usually?
|
||||||
|
# Check logic: grab_item uses y=1?
|
||||||
|
var item_cell = Vector3i(pos.x, 1, pos.y)
|
||||||
|
if enhanced_gridmap.get_cell_item(item_cell) == -1:
|
||||||
|
if not is_position_occupied(pos):
|
||||||
|
return pos
|
||||||
|
|
||||||
|
return Vector2i(-1, -1)
|
||||||
|
|
||||||
|
|
||||||
func _process(delta):
|
func _process(delta):
|
||||||
if is_multiplayer_authority():
|
if is_multiplayer_authority():
|
||||||
@@ -629,6 +717,13 @@ func _process(delta):
|
|||||||
if movement_manager:
|
if movement_manager:
|
||||||
movement_manager._process(delta)
|
movement_manager._process(delta)
|
||||||
|
|
||||||
|
# Immunity Timer Logic
|
||||||
|
if immunity_timer > 0:
|
||||||
|
immunity_timer -= delta
|
||||||
|
if immunity_timer <= 0:
|
||||||
|
immunity_timer = 0
|
||||||
|
_apply_tint_recursive(self, Color.WHITE) # Remove immunity tint
|
||||||
|
|
||||||
@rpc("any_peer", "call_local")
|
@rpc("any_peer", "call_local")
|
||||||
func ping_existence():
|
func ping_existence():
|
||||||
# This just lets other clients know this player exists
|
# This just lets other clients know this player exists
|
||||||
@@ -876,7 +971,8 @@ func start_movement_along_path(path: Array, clear_visual: bool = true):
|
|||||||
|
|
||||||
# Clear visuals for everyone including bots
|
# Clear visuals for everyone including bots
|
||||||
if clear_visual:
|
if clear_visual:
|
||||||
enhanced_gridmap.clear_path_visualization()
|
if enhanced_gridmap:
|
||||||
|
enhanced_gridmap.clear_path_visualization()
|
||||||
|
|
||||||
# Check for buffered input
|
# Check for buffered input
|
||||||
if movement_manager and movement_manager.has_method("_on_movement_finished"):
|
if movement_manager and movement_manager.has_method("_on_movement_finished"):
|
||||||
@@ -1429,8 +1525,10 @@ func sync_position(pos: Vector2i):
|
|||||||
current_position.y * cell_size.z + cell_size.z * 0.5
|
current_position.y * cell_size.z + cell_size.z * 0.5
|
||||||
) + cell_offset
|
) + cell_offset
|
||||||
|
|
||||||
global_position = new_pos
|
# Only snap visual if not moving (moving players will tween to destination)
|
||||||
target_visual_position = new_pos # Reset smoothing target to prevent fighting
|
if not is_player_moving:
|
||||||
|
global_position = new_pos
|
||||||
|
target_visual_position = new_pos # Reset smoothing target to prevent fighting
|
||||||
|
|
||||||
@rpc("any_peer", "call_local", "reliable")
|
@rpc("any_peer", "call_local", "reliable")
|
||||||
func set_spawn_position(pos: Vector2i):
|
func set_spawn_position(pos: Vector2i):
|
||||||
|
|||||||
@@ -51,8 +51,8 @@ func _ready():
|
|||||||
queue_free()
|
queue_free()
|
||||||
return
|
return
|
||||||
|
|
||||||
# Wait for actor to be fully ready
|
# Wait for actor to be fully ready (player._ready awaits 0.5s then creates managers)
|
||||||
await get_tree().create_timer(1.0).timeout
|
await get_tree().create_timer(1.5).timeout
|
||||||
|
|
||||||
enhanced_gridmap = actor.enhanced_gridmap
|
enhanced_gridmap = actor.enhanced_gridmap
|
||||||
if not enhanced_gridmap:
|
if not enhanced_gridmap:
|
||||||
@@ -335,9 +335,22 @@ func _try_move() -> bool:
|
|||||||
_is_processing_action = true
|
_is_processing_action = true
|
||||||
_current_action = "moving"
|
_current_action = "moving"
|
||||||
|
|
||||||
# Wait for movement to finish (signal from movement manager)
|
# Wait for movement to finish or timeout (safety)
|
||||||
await actor.movement_manager.movement_finished
|
# Race: Signal vs Timeout
|
||||||
|
# Since Godot 4 doesn't support 'await' racing easily without helper,
|
||||||
|
# we'll just wait for the signal but ensure movement manager emits it.
|
||||||
|
# safer approach: check if is_moving goes false
|
||||||
|
|
||||||
|
# Safety timeout to prevent infinite loop
|
||||||
|
var max_wait_time = 2.0
|
||||||
|
var elapsed = 0.0
|
||||||
|
|
||||||
|
while actor.is_player_moving and is_instance_valid(self):
|
||||||
|
await get_tree().process_frame
|
||||||
|
elapsed += get_process_delta_time()
|
||||||
|
if elapsed > max_wait_time:
|
||||||
|
print("[BotController] Movement timed out!")
|
||||||
|
break
|
||||||
|
|
||||||
if not is_instance_valid(self): return true
|
if not is_instance_valid(self): return true
|
||||||
_is_processing_action = false
|
_is_processing_action = false
|
||||||
|
|||||||
@@ -42,10 +42,7 @@ func rotate_towards_target(target_pos: Vector2i):
|
|||||||
|
|
||||||
func simple_move_to(grid_position: Vector2i) -> bool:
|
func simple_move_to(grid_position: Vector2i) -> bool:
|
||||||
if is_moving:
|
if is_moving:
|
||||||
# Calculate direction for buffering
|
|
||||||
var direction = grid_position - player.current_position
|
var direction = grid_position - player.current_position
|
||||||
|
|
||||||
# FIX: Only buffer if direction is DIFFERENT from current move (prevents overshoot)
|
|
||||||
if direction != current_move_direction:
|
if direction != current_move_direction:
|
||||||
buffer_move_input(direction)
|
buffer_move_input(direction)
|
||||||
return false
|
return false
|
||||||
@@ -53,11 +50,9 @@ func simple_move_to(grid_position: Vector2i) -> bool:
|
|||||||
if not player.is_multiplayer_authority():
|
if not player.is_multiplayer_authority():
|
||||||
return false
|
return false
|
||||||
|
|
||||||
# Check if player is frozen
|
|
||||||
if player.get("is_frozen"):
|
if player.get("is_frozen"):
|
||||||
return false
|
return false
|
||||||
|
|
||||||
# Check if target is within 1-tile range
|
|
||||||
var distance: int
|
var distance: int
|
||||||
if use_diagonal_movement:
|
if use_diagonal_movement:
|
||||||
distance = max(abs(grid_position.x - player.current_position.x), abs(grid_position.y - player.current_position.y))
|
distance = max(abs(grid_position.x - player.current_position.x), abs(grid_position.y - player.current_position.y))
|
||||||
@@ -67,32 +62,23 @@ func simple_move_to(grid_position: Vector2i) -> bool:
|
|||||||
if distance != 1:
|
if distance != 1:
|
||||||
return false # Only single-step moves allowed
|
return false # Only single-step moves allowed
|
||||||
|
|
||||||
# Check if target position is within grid bounds
|
|
||||||
if not enhanced_gridmap.is_position_valid(grid_position):
|
if not enhanced_gridmap.is_position_valid(grid_position):
|
||||||
return false
|
return false
|
||||||
|
|
||||||
# Check for finish line logic (delegated back to player or race manager)
|
|
||||||
if player.has_method("can_move_to_finish") and not player.can_move_to_finish(grid_position):
|
if player.has_method("can_move_to_finish") and not player.can_move_to_finish(grid_position):
|
||||||
return false
|
return false
|
||||||
|
|
||||||
# Check walkability and obstacles
|
|
||||||
var cell_item = enhanced_gridmap.get_cell_item(Vector3i(grid_position.x, 0, grid_position.y))
|
var cell_item = enhanced_gridmap.get_cell_item(Vector3i(grid_position.x, 0, grid_position.y))
|
||||||
if cell_item == -1 or cell_item in enhanced_gridmap.non_walkable_items:
|
if cell_item == -1 or cell_item in enhanced_gridmap.non_walkable_items:
|
||||||
return false
|
return false
|
||||||
|
|
||||||
# Check for player collision and try to push
|
|
||||||
if player.is_position_occupied(grid_position):
|
if player.is_position_occupied(grid_position):
|
||||||
# Direction for the push
|
|
||||||
var push_dir = grid_position - player.current_position
|
var push_dir = grid_position - player.current_position
|
||||||
if not try_push(grid_position, push_dir):
|
if not try_push(grid_position, push_dir):
|
||||||
return false
|
return false
|
||||||
# If push succeeded, the tile is now technically free (or will be processed as free)
|
|
||||||
# proceed to move into it
|
|
||||||
|
|
||||||
# All checks passed, perform move
|
|
||||||
rotate_towards_target(grid_position)
|
rotate_towards_target(grid_position)
|
||||||
|
|
||||||
# Play walk animation (synced across network)
|
|
||||||
if player.is_multiplayer_authority() and player.has_method("sync_walk_animation"):
|
if player.is_multiplayer_authority() and player.has_method("sync_walk_animation"):
|
||||||
player.rpc("sync_walk_animation")
|
player.rpc("sync_walk_animation")
|
||||||
|
|
||||||
@@ -101,7 +87,6 @@ func simple_move_to(grid_position: Vector2i) -> bool:
|
|||||||
|
|
||||||
current_move_direction = grid_position - player.current_position
|
current_move_direction = grid_position - player.current_position
|
||||||
|
|
||||||
# Use the existing RPC to move
|
|
||||||
player.rpc("start_movement_along_path", path, not (player.is_bot or player.is_in_group("Bots")))
|
player.rpc("start_movement_along_path", path, not (player.is_bot or player.is_in_group("Bots")))
|
||||||
|
|
||||||
return true
|
return true
|
||||||
@@ -119,15 +104,21 @@ func try_push(target_pos: Vector2i, direction: Vector2i) -> bool:
|
|||||||
|
|
||||||
# Check if pushed destination is valid
|
# Check if pushed destination is valid
|
||||||
if not enhanced_gridmap.is_position_valid(pushed_to_pos):
|
if not enhanced_gridmap.is_position_valid(pushed_to_pos):
|
||||||
|
# Blocked by world bounds -> Double Push!
|
||||||
|
other_player.rpc("apply_stagger", 1.5)
|
||||||
return false
|
return false
|
||||||
|
|
||||||
# Check walkability of pushed destination
|
# Check walkability of pushed destination
|
||||||
var cell_item = enhanced_gridmap.get_cell_item(Vector3i(pushed_to_pos.x, 0, pushed_to_pos.y))
|
var cell_item = enhanced_gridmap.get_cell_item(Vector3i(pushed_to_pos.x, 0, pushed_to_pos.y))
|
||||||
if cell_item == -1 or cell_item in enhanced_gridmap.non_walkable_items:
|
if cell_item == -1 or cell_item in enhanced_gridmap.non_walkable_items:
|
||||||
|
# Blocked by obstacle -> Double Push!
|
||||||
|
other_player.rpc("apply_stagger", 1.5)
|
||||||
return false
|
return false
|
||||||
|
|
||||||
# Check if pushed destination is ALREADY occupied (no daisy chaining)
|
# Check if pushed destination is ALREADY occupied (Double Push / Crush)
|
||||||
if player.is_position_occupied(pushed_to_pos):
|
if player.is_position_occupied(pushed_to_pos):
|
||||||
|
# Blocked by another player -> Double Push!
|
||||||
|
other_player.rpc("apply_stagger", 1.5)
|
||||||
return false
|
return false
|
||||||
|
|
||||||
# Check if other player is currently moving (don't push moving players to avoid sync issues)
|
# Check if other player is currently moving (don't push moving players to avoid sync issues)
|
||||||
|
|||||||
@@ -59,10 +59,21 @@ func _add_bar():
|
|||||||
player.rpc("display_message", "Power-up bar filled!", 1)
|
player.rpc("display_message", "Power-up bar filled!", 1)
|
||||||
print("[PowerUp] Player %s gained 1 bar! Total: %d/%d points" % [player.name, current_points, MAX_POINTS])
|
print("[PowerUp] Player %s gained 1 bar! Total: %d/%d points" % [player.name, current_points, MAX_POINTS])
|
||||||
|
|
||||||
|
if player.is_multiplayer_authority():
|
||||||
|
player.get_node("PowerUpManager").rpc("sync_points", current_points)
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Goal Completion Reward
|
# Goal Completion Reward
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|
||||||
|
func acquire_smash_bonus():
|
||||||
|
"""Called when player is smashed. Grants 1 bar up to a max of 2 bars."""
|
||||||
|
if get_bars() < 2:
|
||||||
|
_add_bar()
|
||||||
|
print("[PowerUp] Player %s gained smash bonus bar! Total: %d/%d" % [player.name, current_points, MAX_POINTS])
|
||||||
|
else:
|
||||||
|
print("[PowerUp] Player %s smash bonus capped (already has >= 2 bars)" % player.name)
|
||||||
|
|
||||||
func add_goal_completion_reward():
|
func add_goal_completion_reward():
|
||||||
"""Called when player completes a goal pattern. Awards 1 bar."""
|
"""Called when player completes a goal pattern. Awards 1 bar."""
|
||||||
_add_bar()
|
_add_bar()
|
||||||
|
|||||||
@@ -205,7 +205,8 @@ func set_local_player(player):
|
|||||||
|
|
||||||
func _connect_powerup_manager_deferred(player):
|
func _connect_powerup_manager_deferred(player):
|
||||||
"""Wait for PowerUpManager to be initialized before connecting."""
|
"""Wait for PowerUpManager to be initialized before connecting."""
|
||||||
await player.get_tree().create_timer(0.3).timeout
|
# player._ready waits 0.5s before creating managers, so wait longer
|
||||||
|
await player.get_tree().create_timer(0.8).timeout
|
||||||
|
|
||||||
var powerup_manager = player.get_node_or_null("PowerUpManager")
|
var powerup_manager = player.get_node_or_null("PowerUpManager")
|
||||||
if powerup_manager:
|
if powerup_manager:
|
||||||
@@ -213,6 +214,8 @@ func _connect_powerup_manager_deferred(player):
|
|||||||
powerup_manager.points_changed.connect(_on_powerup_points_changed)
|
powerup_manager.points_changed.connect(_on_powerup_points_changed)
|
||||||
# Initialize bar with current values
|
# Initialize bar with current values
|
||||||
update_powerup_bar(powerup_manager.get_points(), powerup_manager.get_max_points())
|
update_powerup_bar(powerup_manager.get_points(), powerup_manager.get_max_points())
|
||||||
|
else:
|
||||||
|
push_warning("[UIManager] PowerUpManager not found on player after 0.8s wait")
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Power-Up Bar UI (Battery Style)
|
# Power-Up Bar UI (Battery Style)
|
||||||
|
|||||||
Reference in New Issue
Block a user