feat: Implement core player character logic, including state management, network synchronization, character selection, and manager initialization.

This commit is contained in:
Yogi Wiguna
2026-02-12 11:02:30 +08:00
parent 786e73dbaf
commit da858c12aa
7 changed files with 206 additions and 20 deletions
+4 -1
View File
@@ -34,6 +34,9 @@ func grab_item(grid_position: Vector2i) -> bool:
if not enhanced_gridmap or not has_ap:
return false
if player.get("is_frozen"):
return false
var cell = Vector3i(grid_position.x, 1, grid_position.y)
var item = enhanced_gridmap.get_cell_item(cell)
@@ -319,7 +322,7 @@ func auto_put_item() -> bool:
# Check AP only if in turn-based mode
var has_ap = player.action_points > 0 if TurnManager.turn_based_mode else true
if not enhanced_gridmap or not has_ap or player.is_bot or player.is_in_group("Bots"):
if not enhanced_gridmap or not has_ap or player.is_bot or player.is_in_group("Bots") or player.get("is_frozen"):
return false
# Step 1: Find empty adjacent (or current) grid cells
+106 -7
View File
@@ -120,15 +120,114 @@ func _process(delta):
global_position = carrier.global_position + Vector3(0, 1.5, 0)
rotation = carrier.rotation
func _flash_damage():
var mesh_cache: Array[MeshInstance3D] = []
var original_scales: Array[Vector3] = []
func _ready():
# Cache meshes and their initial scales
# We wait a frame to ensure all children are ready and transforms applied
await get_tree().process_frame
var meshes = find_children("*", "MeshInstance3D", true)
for mesh in meshes:
var original_modulate = mesh.transparency
# Quick flash hack or shader param?
# If standard material, maybe just modulate visibility or scale
var t = create_tween()
t.tween_property(mesh, "scale", Vector3(1.2, 1.2, 1.2), 0.1)
t.tween_property(mesh, "scale", Vector3(1.0, 1.0, 1.0), 0.1)
mesh_cache.append(mesh)
original_scales.append(mesh.scale)
func _flash_damage():
# If cache empty (e.g. called before ready), try to populate or just skip custom scaling
if mesh_cache.is_empty():
return
for i in range(mesh_cache.size()):
var mesh = mesh_cache[i]
if is_instance_valid(mesh):
var base_scale = original_scales[i]
var t = create_tween()
t.tween_property(mesh, "scale", base_scale * 1.2, 0.1)
t.tween_property(mesh, "scale", base_scale, 0.1)
@rpc("any_peer", "call_local", "reliable")
func on_thrown_landing(attacker: Node = null):
"""Called when Tekton lands after being thrown."""
print("[Tekton] Landed! Shrinking and waiting...")
# Disable movement/interaction logic temporarily
var controller = get_node_or_null("TektonController")
if controller and controller.get("timer"):
controller.timer.stop()
# Visual Shrink
# Use cached meshes if available, else find them (but can't restore accurately if not cached)
if mesh_cache.is_empty():
# Fallback if _ready hasn't run or failed
# We'll just define the user's specific vector as fallback target for the sphere
# But better to rely on cache.
pass
for i in range(mesh_cache.size()):
var mesh = mesh_cache[i]
if is_instance_valid(mesh):
var base_scale = original_scales[i]
var t = create_tween()
t.tween_property(mesh, "scale", base_scale * 0.5, 0.2).set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_BACK)
# Wait 1 seconds
await get_tree().create_timer(1.0).timeout
# Grow back
for i in range(mesh_cache.size()):
var mesh = mesh_cache[i]
if is_instance_valid(mesh):
var base_scale = original_scales[i]
var t = create_tween()
t.tween_property(mesh, "scale", base_scale, 0.2).set_ease(Tween.EASE_OUT).set_trans(Tween.TRANS_BACK)
# Resume AI
if controller and controller.has_method("_start_timer"):
if is_multiplayer_authority() and not is_carried:
controller._start_timer()
# Spawn tiles (as requested "tekton will spawn a tiles around that floor also")
if is_multiplayer_authority():
spawn_tiles_around(8) # Standard amount
# Floor Freeze (Visual/Instant - Run on all clients locally)
_temporarily_change_floor(current_position, 1, 6, 3.0)
func _temporarily_change_floor(center: Vector2i, radius: int, new_id: int, duration: float):
if not enhanced_gridmap: return
# Run locally on all clients to ensure instant feedback without network delay
var changed_cells = {} # pos: original_id
for x in range(-radius, radius + 1):
for y in range(-radius, radius + 1):
var pos = center + Vector2i(x, y)
if enhanced_gridmap.is_position_valid(pos):
var cell_3d = Vector3i(pos.x, 0, pos.y)
var original = enhanced_gridmap.get_cell_item(cell_3d)
# Only change if not already the new ID (avoid redundant updates or overriding existing freeze)
if original != new_id:
changed_cells[pos] = original
# Set locally immediately
enhanced_gridmap.set_cell_item(cell_3d, new_id)
await get_tree().create_timer(duration).timeout
# Restore locally
for pos in changed_cells:
var original = changed_cells[pos]
var current_cell = Vector3i(pos.x, 0, pos.y)
var current = enhanced_gridmap.get_cell_item(current_cell)
# Only restore if it hasn't been changed to something else in meantime
if current == new_id:
enhanced_gridmap.set_cell_item(current_cell, original)
# Stun nearby players handled by Thrower (Player.gd) or here?
# Player.gd handles the stun call because it knows the impact zone context better?
# Actually, Player.gd calls this function. Player.gd *also* iterates players to stun them.
# That is fine.
func spawn_tiles_around(count: int = 4):
"""Spawns a mix of normal and special tiles in a radius."""