feat: Implement Tekton roaming NPC with movement, combat, carry, throw, and knock mechanics.
This commit is contained in:
+44
-18
@@ -22,12 +22,16 @@ var display_name: String:
|
||||
if name_label:
|
||||
name_label.text = _display_name
|
||||
|
||||
# Sync to other peers if we are authority
|
||||
if is_multiplayer_authority() and is_inside_tree():
|
||||
# Sync to other peers if we are authority and connected
|
||||
if is_multiplayer_authority() and is_inside_tree() and can_rpc():
|
||||
rpc("sync_display_name", _display_name)
|
||||
get:
|
||||
return _display_name
|
||||
|
||||
# Helper to check network status
|
||||
func can_rpc() -> bool:
|
||||
return multiplayer.has_multiplayer_peer() and multiplayer.multiplayer_peer.get_connection_status() == MultiplayerPeer.CONNECTION_CONNECTED
|
||||
|
||||
# Special effect states
|
||||
var is_frozen: bool = false
|
||||
var is_invisible: bool = false
|
||||
@@ -55,7 +59,7 @@ var is_attack_mode: bool = false:
|
||||
_apply_tint_recursive(self , Color.WHITE)
|
||||
|
||||
# Sync to others if we are the authority
|
||||
if is_multiplayer_authority():
|
||||
if is_multiplayer_authority() and can_rpc():
|
||||
rpc("sync_attack_mode", is_attack_mode)
|
||||
|
||||
@rpc("any_peer", "call_local", "reliable")
|
||||
@@ -199,7 +203,7 @@ func _ready():
|
||||
pointer.visible = is_multiplayer_authority()
|
||||
|
||||
# Sync name to other peers if this is our local player or a bot we own
|
||||
if is_multiplayer_authority():
|
||||
if is_multiplayer_authority() and can_rpc():
|
||||
rpc("sync_display_name", display_name)
|
||||
|
||||
# Wait briefly to ensure proper scene setup and server recognition
|
||||
@@ -239,25 +243,21 @@ func _ready():
|
||||
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)
|
||||
if can_rpc():
|
||||
rpc("set_spawn_position", current_position)
|
||||
rpc("notify_spawn_selected", current_position)
|
||||
|
||||
# Assign bot character (deterministic based on ID to match lobby preview)
|
||||
# Bot IDs start from 2 (host is 1)
|
||||
# Lobby slots are 0-indexed in UI loop, but bots fill empty slots.
|
||||
# Use name.to_int() because all bots have authority 1 (Server)
|
||||
var bot_id_val = name.to_int()
|
||||
var bot_characters = ["Bob", "Gatot", "Masbro", "Oldpop"]
|
||||
# Map bot ID to character index. Bot 2 -> Index 1. Bot 3 -> Index 2.
|
||||
# Formula: (bot_id - 1) % size
|
||||
var char_index = (bot_id_val - 1) % bot_characters.size()
|
||||
var bot_char_name = bot_characters[char_index]
|
||||
set_character(bot_char_name)
|
||||
if is_multiplayer_authority():
|
||||
if is_multiplayer_authority() and can_rpc():
|
||||
rpc("sync_character", bot_char_name)
|
||||
|
||||
# Sync bot status to network
|
||||
if is_multiplayer_authority():
|
||||
if is_multiplayer_authority() and can_rpc():
|
||||
rpc("sync_bot_status", true)
|
||||
|
||||
# Continue to manager initialization...
|
||||
@@ -282,7 +282,7 @@ func _ready():
|
||||
current_position.y * 1 + 1 * 0.5
|
||||
)
|
||||
target_visual_position = global_position
|
||||
if is_multiplayer_authority():
|
||||
if is_multiplayer_authority() and can_rpc():
|
||||
rpc("sync_position", current_position)
|
||||
else:
|
||||
target_visual_position = global_position
|
||||
@@ -407,7 +407,7 @@ func _setup_character() -> void:
|
||||
set_character(character_name)
|
||||
|
||||
# If this is our local player, also sync to other clients for late joiners
|
||||
if is_multiplayer_authority():
|
||||
if is_multiplayer_authority() and can_rpc():
|
||||
rpc("sync_character", character_name)
|
||||
|
||||
# =============================================================================
|
||||
@@ -987,7 +987,8 @@ func _process(delta):
|
||||
_verify_timer += delta
|
||||
if _verify_timer >= 3.0:
|
||||
_verify_timer = 0.0
|
||||
rpc("ping_existence")
|
||||
if can_rpc():
|
||||
rpc("ping_existence")
|
||||
else:
|
||||
# Client-side visual smoothing
|
||||
# Only interpolate if NOT running a movement tween, OR if the drift is large (teleport/snap)
|
||||
@@ -1024,7 +1025,8 @@ var last_sent_position: Vector3
|
||||
func _physics_process(delta):
|
||||
if is_multiplayer_authority():
|
||||
if global_position.distance_squared_to(last_sent_position) > 0.001:
|
||||
rpc("remote_set_position", global_position)
|
||||
if can_rpc():
|
||||
rpc("remote_set_position", global_position)
|
||||
last_sent_position = global_position
|
||||
|
||||
# NOTE: Finish line checking removed - game uses cycle-based goals system now
|
||||
@@ -1158,6 +1160,30 @@ func _find_random_spawn_position() -> Vector2i:
|
||||
print("Warning: No gridmap for random spawn")
|
||||
return Vector2i.ZERO
|
||||
|
||||
# Special handling for Stop n Go mode (22x10 Grid)
|
||||
if LobbyManager.game_mode == "Stop n Go":
|
||||
var available_positions = []
|
||||
# Scan the 22x10 grid
|
||||
for x in range(22):
|
||||
for z in range(10):
|
||||
var pos = Vector2i(x, z)
|
||||
# Check if position is walkable (not a wall/obstacle)
|
||||
# We check Floor 0 item. Assuming walls are identifiable.
|
||||
# In setup_arena, walls are TILE_OBSTACLE (4).
|
||||
# We should check if it is NOT TILE_OBSTACLE.
|
||||
var item = enhanced_gridmap.get_cell_item(Vector3i(x, 0, z))
|
||||
# Assuming 4 is obstacle, and -1 is void. 0 is walkable, 2 is safe zone.
|
||||
if item != -1 and item != 4:
|
||||
if not is_position_occupied(pos):
|
||||
available_positions.append(pos)
|
||||
|
||||
if available_positions.size() > 0:
|
||||
var rng = RandomNumberGenerator.new()
|
||||
rng.randomize()
|
||||
return available_positions[rng.randi() % available_positions.size()]
|
||||
return Vector2i(10, 5) # Fallback center
|
||||
|
||||
|
||||
var available_positions = []
|
||||
|
||||
# Scan the grid for valid walkable floor tiles that are not occupied
|
||||
@@ -1893,7 +1919,7 @@ func grab_tekton():
|
||||
# Find nearby Tekton
|
||||
var tekton = _find_nearby_tekton()
|
||||
if tekton:
|
||||
if is_multiplayer_authority():
|
||||
if is_multiplayer_authority() and can_rpc():
|
||||
rpc("sync_grab_tekton", tekton.get_path())
|
||||
|
||||
@rpc("any_peer", "call_local", "reliable")
|
||||
|
||||
Reference in New Issue
Block a user