extends Node @export var throw_interval: float = 2.0 @export var throw_range: int = 3 var tekton: Node3D var enhanced_gridmap: Node var timer: Timer func _ready(): tekton = get_parent() if not tekton: set_physics_process(false) return # Wait for gridmap await get_tree().process_frame var main = tekton.get_tree().get_root().get_node_or_null("Main") if main: enhanced_gridmap = main.get_node_or_null("EnhancedGridMap") # Initial State # No idle anim for static tekton mesh, just ensure stopped var ap = tekton.get_node_or_null("Visuals/tekton/AnimationPlayer") if ap: ap.stop() # Setup Timer timer = Timer.new() timer.one_shot = true timer.timeout.connect(_on_timer_timeout) add_child(timer) _start_timer() func _start_timer(): timer.wait_time = throw_interval timer.start() func _on_timer_timeout(): if not multiplayer.has_multiplayer_peer(): return if not is_multiplayer_authority(): return if not tekton or not enhanced_gridmap: return if tekton.get("is_carried") or tekton.get("is_thrown"): _start_timer() return # print("[StaticTekton] Timer timeout. Attempting throw...") _attempt_throw() func _attempt_throw(): # Find target var target = _find_empty_tile() if target == Vector2i(-1, -1): # print("[StaticTekton] No valid target found.") _start_timer() return # print("[StaticTekton] Target found: %s" % target) # Execute Throw # 1. Face target var target_world_pos = Vector3(target.x + 0.5, 0, target.y + 0.5) if enhanced_gridmap and "cell_size" in enhanced_gridmap: target_world_pos = Vector3( target.x * enhanced_gridmap.cell_size.x + enhanced_gridmap.cell_size.x / 2, enhanced_gridmap.cell_size.y, target.y * enhanced_gridmap.cell_size.z + enhanced_gridmap.cell_size.z / 2 ) tekton.look_at(Vector3(target_world_pos.x, tekton.global_position.y, target_world_pos.z), Vector3.UP) # 2. Play Animation if tekton.has_method("play_animation_rpc") and tekton.has_method("can_rpc") and tekton.can_rpc(): tekton.rpc("play_animation_rpc", "ted_bones_001|Tekton Throwing Tiles|Anima_Layer") # 3. Create Projectile Visual (Synced) if tekton.has_method("spawn_projectile_rpc") and tekton.has_method("can_rpc") and tekton.can_rpc(): tekton.rpc("spawn_projectile_rpc", target_world_pos, 0.5) # 4. Impact / Spawn await get_tree().create_timer(0.5).timeout var main = tekton.get_tree().get_root().get_node_or_null("Main") if main and tekton.has_method("can_rpc") and tekton.can_rpc(): # Spawn Item (Random ID 7-10) var item_id = randi_range(7, 10) main.rpc("sync_grid_item", target.x, 1, target.y, item_id) # 5. Resume Idle await get_tree().create_timer(0.5).timeout # Small delay after throw # No idle anim for static tekton mesh, just stop if tekton.has_method("can_rpc") and tekton.can_rpc(): # Stop animation on all peers via a direct call (AnimationPlayer.stop is visual-only, safe) var ap = tekton.get_node_or_null("Visuals/tekton/AnimationPlayer") if ap: ap.stop() _start_timer() func _find_empty_tile() -> Vector2i: if not enhanced_gridmap or not "rows" in enhanced_gridmap: return Vector2i(-1, -1) var center = Vector2i(tekton.global_position.x, tekton.global_position.z) # Approx grid pos # Better: use tekton.current_position if available if "current_position" in tekton: center = tekton.current_position var candidates = [] for x in range(center.x - throw_range, center.x + throw_range + 1): for y in range(center.y - throw_range, center.y + throw_range + 1): if x < 0 or y < 0 or x >= enhanced_gridmap.columns or y >= enhanced_gridmap.rows: continue # Check range logic (Euclidean or Manhattan?) if Vector2(x, y).distance_to(Vector2(center.x, center.y)) > throw_range: continue # EXPLICITLY EXCLUDE 3x3 BASE AREA (Tekton Stand) # The stand occupies [center-1, center+1]. We must NOT spawn here. if abs(x - center.x) <= 1 and abs(y - center.y) <= 1: continue # Check if empty (Layer 1 has no item) if enhanced_gridmap.get_cell_item(Vector3i(x, 1, y)) == -1: # Also check floor exist? (Layer 0) if enhanced_gridmap.get_cell_item(Vector3i(x, 0, y)) != -1: candidates.append(Vector2i(x, y)) if candidates.is_empty(): return Vector2i(-1, -1) return candidates.pick_random()