update feature & bugfix

This commit is contained in:
2026-01-23 22:26:44 +08:00
parent 89a3beb2b2
commit d262bb8dc0
12 changed files with 198 additions and 87 deletions
+16 -3
View File
@@ -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="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"]
layout_mode = 3
@@ -13,14 +15,25 @@ grow_vertical = 2
theme = ExtResource("2")
script = ExtResource("1")
[node name="Background" type="ColorRect" parent="."]
[node name="Background" type="TextureRect" parent="."]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 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="."]
layout_mode = 1
+12 -1
View File
@@ -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="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://dvp0as6yyudco" path="res://assets/graphics/main_menu/bg_illust.png" id="4_nqcc7"]
[node name="Lobby" type="Control"]
layout_mode = 3
@@ -24,6 +25,16 @@ grow_vertical = 2
texture = ExtResource("3_q60fs")
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="."]
layout_mode = 1
anchors_preset = 8
+23 -11
View File
@@ -367,15 +367,17 @@ func _setup_client_game():
add_player_character(p_id)
print("Client: Pre-spawned player ", p_id)
# Pre-spawn potential bots (IDs 2 to MaxPlayers) to prevent RPC "Node not found" errors
# Bots use small integer IDs (2, 3, 4...) while clients use large unique IDs
# Pre-spawn potential bots (IDs from count+1 to MaxPlayers) to prevent RPC "Node not found" errors
# Bots use small integer IDs (e.g. 2, 3, 4...) while clients use large unique IDs
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)
if not has_node(str(i)):
add_player_character(i)
get_node(str(i)).is_bot = true # Assume bot initially
get_node(str(i)).add_to_group("Bots", true)
# Spawning as BOT
add_player_character(i, true)
print("Client: Pre-spawned potential bot ", i)
# Ensure local player setup (UI, controls) is verified
@@ -488,18 +490,28 @@ func create_bot(bot_id: int):
var goal_index = bot_id - 1
if goal_index < GoalManager.preset_goals.size():
# Wait for bot managers to be ready
await get_tree().create_timer(0.2).timeout
# Wait for bot managers to be ready (race_manager is created at T=0.5)
await get_tree().create_timer(0.75).timeout
bot_character.goals = GoalManager.preset_goals[goal_index].duplicate()
# Use deferred goals sync to avoid timing issues
call_deferred("_deferred_set_player_goals", bot_id, bot_character.goals)
@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)):
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)
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"):
player_scores.append({
"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)
+107 -9
View File
@@ -173,9 +173,6 @@ func _ready():
# =========================================================================
# 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"):
# Disable input processing for bots
set_process_input(false)
@@ -183,10 +180,11 @@ func _ready():
# Set initial position for bots
if enhanced_gridmap:
current_position = _find_random_spawn_position()
update_player_position(current_position)
spawn_point_selected = true
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)
# 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():
_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):
if is_multiplayer_authority():
@@ -629,6 +717,13 @@ func _process(delta):
if movement_manager:
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")
func ping_existence():
# 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
if clear_visual:
enhanced_gridmap.clear_path_visualization()
if enhanced_gridmap:
enhanced_gridmap.clear_path_visualization()
# Check for buffered input
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
) + cell_offset
global_position = new_pos
target_visual_position = new_pos # Reset smoothing target to prevent fighting
# Only snap visual if not moving (moving players will tween to destination)
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")
func set_spawn_position(pos: Vector2i):