feat: implement core player entity with state management, network synchronization, and bot logic.

This commit is contained in:
Yogi Wiguna
2026-03-23 17:37:08 +08:00
parent 9867ddc262
commit 7f1ae0f757
+60 -4
View File
@@ -751,7 +751,7 @@ func _apply_tint_recursive(node: Node, color: Color):
var immunity_timer: float = 0.0 var immunity_timer: float = 0.0
var tekton_carry_timer: float = 0.0 var tekton_carry_timer: float = 0.0
const MAX_TEKTON_CARRY_TIME: float = 3.0 const MAX_TEKTON_CARRY_TIME: float = 4.0
@rpc("any_peer", "call_local") @rpc("any_peer", "call_local")
@@ -2139,12 +2139,58 @@ func grab_tekton():
if not is_multiplayer_authority() or is_carrying_tekton or is_frozen or is_stop_frozen: if not is_multiplayer_authority() or is_carrying_tekton or is_frozen or is_stop_frozen:
return return
# Find nearby Tekton # 1. Check for nearby carrier to snatch from
var carrier = _find_nearby_carrier()
if carrier:
snatch_tekton(carrier)
return
# 2. Find nearby roaming Tekton
var tekton = _find_nearby_tekton() var tekton = _find_nearby_tekton()
if tekton: if tekton:
if is_multiplayer_authority() and can_rpc(): if is_multiplayer_authority() and can_rpc():
rpc("sync_grab_tekton", tekton.get_path()) rpc("sync_grab_tekton", tekton.get_path())
func snatch_tekton(target_carrier: Node3D):
if not is_multiplayer_authority() or not target_carrier.is_carrying_tekton:
return
var tekton = target_carrier.carried_tekton
if tekton:
if is_multiplayer_authority() and can_rpc():
rpc("sync_snatch_tekton", target_carrier.get_path(), tekton.get_path())
@rpc("any_peer", "call_local", "reliable")
func sync_snatch_tekton(carrier_path: NodePath, tekton_path: NodePath):
var carrier = get_node_or_null(carrier_path)
var tekton = get_node_or_null(tekton_path)
if carrier and tekton:
# Security: ensure multiple people don't think they are carrying it
# Transfer logic
carrier.is_carrying_tekton = false
carrier.carried_tekton = null
self.is_carrying_tekton = true
self.carried_tekton = tekton
tekton.set_carried(true, self)
# Reset my carry timer (3s rule starts fresh)
tekton_carry_timer = 0.0
# Visual/Logic side effects
if is_attack_mode:
is_attack_mode = false
SfxManager.play("pick_up_tekton_roaming")
play_pickup_animation()
# Visual feedback for the victim
if carrier.has_method("sync_bump"):
# Bump away from the snatcher
carrier.sync_bump(current_position, true)
print("[Player %s] SNATCHED Tekton from %s" % [name, carrier.name])
@rpc("any_peer", "call_local", "reliable") @rpc("any_peer", "call_local", "reliable")
func sync_grab_tekton(tekton_path: NodePath): func sync_grab_tekton(tekton_path: NodePath):
var tekton = get_node_or_null(tekton_path) var tekton = get_node_or_null(tekton_path)
@@ -2409,8 +2455,6 @@ func _find_nearby_tekton() -> Node3D: # Find closest Tekton
if t.get("is_recovering"): continue # Cannot grab recovering/shrunk Tekton if t.get("is_recovering"): continue # Cannot grab recovering/shrunk Tekton
if t.get("is_static_turret"): continue # Cannot grab/knock static turret if t.get("is_static_turret"): continue # Cannot grab/knock static turret
# Check adjacency (or same tile)
# Assuming is_adjacent_or_same is a helper function that checks if two Vector2i are adjacent or the same.
# For example: (pos1 - pos2).length_squared() <= 2 (for adjacent or same tile) # For example: (pos1 - pos2).length_squared() <= 2 (for adjacent or same tile)
var dist_grid = (t.current_position - current_position).length() var dist_grid = (t.current_position - current_position).length()
if dist_grid <= 1.5: # Adjacent (1.0 or 1.41) or same tile (0.0) if dist_grid <= 1.5: # Adjacent (1.0 or 1.41) or same tile (0.0)
@@ -2419,3 +2463,15 @@ func _find_nearby_tekton() -> Node3D: # Find closest Tekton
min_dist = dist min_dist = dist
closest_tekton = t closest_tekton = t
return closest_tekton return closest_tekton
func _find_nearby_carrier() -> Node3D:
"""Find a nearby player who is carrying a Tekton."""
var players = get_tree().get_nodes_in_group("Players")
for p in players:
if p == self: continue
if p.is_carrying_tekton:
# Check adjacency or same tile
var dist = Vector2(p.current_position.x, p.current_position.y).distance_to(Vector2(current_position.x, current_position.y))
if dist <= 1.5:
return p
return null