From cdf3f0bb1d663f188dcc0c82f383fded2ce20097 Mon Sep 17 00:00:00 2001 From: Yogi Wiguna Date: Thu, 12 Feb 2026 16:49:37 +0800 Subject: [PATCH] feat: Initialize the main game scene with core managers, UI, networking, and a new message bar system. --- scenes/main.gd | 47 +++++++++- scripts/static_tekton_controller.gd | 117 ++++++++++++++++++++++++ scripts/static_tekton_controller.gd.uid | 1 + scripts/tekton.gd | 13 +++ 4 files changed, 175 insertions(+), 3 deletions(-) create mode 100644 scripts/static_tekton_controller.gd create mode 100644 scripts/static_tekton_controller.gd.uid diff --git a/scenes/main.gd b/scenes/main.gd index bfb5730..2b605e0 100644 --- a/scenes/main.gd +++ b/scenes/main.gd @@ -611,7 +611,10 @@ func spawn_tekton_npc(): var enhanced_gridmap = $EnhancedGridMap if not enhanced_gridmap: return - # Spawn 3 Tektons + # Spawn Static Tektons (Bottom Right, Mid Right) + spawn_static_tektons() + + # Spawn 3 Roaming Tektons var spawned_count = 0 var attempts = 0 @@ -640,7 +643,7 @@ func spawn_tekton_npc(): func sync_spawn_tekton(pos: Vector2i, tekton_id: int): _create_tekton(pos, tekton_id) -func _create_tekton(pos: Vector2i, tekton_id: int): +func _create_tekton(pos: Vector2i, tekton_id: int, is_static: bool = false): var node_name = "Tekton_%d" % tekton_id if has_node(node_name): return @@ -654,7 +657,45 @@ func _create_tekton(pos: Vector2i, tekton_id: int): if has_node("EnhancedGridMap"): tekton.initialize(pos, $EnhancedGridMap) - print("[Main] Spawned Tekton at %s (ID: %d)" % [pos, tekton_id]) + # If Static, swap controller + if is_static: + var old_controller = tekton.get_node_or_null("TektonController") + if old_controller: + old_controller.queue_free() + + var static_controller = load("res://scripts/static_tekton_controller.gd").new() + static_controller.name = "StaticTektonController" + tekton.add_child(static_controller) + print("[Main] Spawned STATIC Tekton at %s (ID: %d)" % [pos, tekton_id]) + else: + print("[Main] Spawned Tekton at %s (ID: %d)" % [pos, tekton_id]) + +func spawn_static_tektons(): + """Spawn fixed static tektons (Bottom Right, Mid Right).""" + if not multiplayer.is_server(): return + var enhanced_gridmap = $EnhancedGridMap + if not enhanced_gridmap: return + + # Bottom Right (Max X, Max Y) + if "columns" in enhanced_gridmap and "rows" in enhanced_gridmap: + var cols = enhanced_gridmap.columns + var rows = enhanced_gridmap.rows + + # Bottom Right (Corner - 1) + var pos_br = Vector2i(cols - 2, rows - 2) + var id_br = 99001 + _create_tekton(pos_br, id_br, true) + rpc("sync_spawn_static_tekton", pos_br, id_br) + + # Mid Right + var pos_mr = Vector2i(cols - 2, rows / 2) + var id_mr = 99002 + _create_tekton(pos_mr, id_mr, true) + rpc("sync_spawn_static_tekton", pos_mr, id_mr) + +@rpc("call_remote", "reliable") +func sync_spawn_static_tekton(pos: Vector2i, tekton_id: int): + _create_tekton(pos, tekton_id, true) # ============================================================================= diff --git a/scripts/static_tekton_controller.gd b/scripts/static_tekton_controller.gd new file mode 100644 index 0000000..f75dbe1 --- /dev/null +++ b/scripts/static_tekton_controller.gd @@ -0,0 +1,117 @@ +extends Node + +@export var throw_interval_min: float = 4.0 +@export var throw_interval_max: float = 7.0 +@export var throw_range: int = 6 + +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 + if tekton.has_method("play_animation"): + tekton.play_animation("tekton_idle") + + # 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 = randf_range(throw_interval_min, throw_interval_max) + timer.start() + +func _on_timer_timeout(): + 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, + 0, + 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"): + tekton.rpc("play_animation_rpc", "tekton_throw_tile") + + # 3. Sync projectile/effect (Delay for animation sync?) + # Assuming animation takes ~1.0s, throw happens at ~0.5s? + await get_tree().create_timer(0.5).timeout + + var main = tekton.get_tree().get_root().get_node_or_null("Main") + if main: + # 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) + + # Optional: Spawn Projectile Visual? + # For now, instant spawn is safest, or we can add a projectile RPC later. + + # 4. Resume Idle + await get_tree().create_timer(1.0).timeout + if tekton.has_method("play_animation_rpc"): + tekton.rpc("play_animation_rpc", "tekton_idle") + + _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 + + # 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() diff --git a/scripts/static_tekton_controller.gd.uid b/scripts/static_tekton_controller.gd.uid new file mode 100644 index 0000000..3c79db5 --- /dev/null +++ b/scripts/static_tekton_controller.gd.uid @@ -0,0 +1 @@ +uid://dj1rb4wa8wxeu diff --git a/scripts/tekton.gd b/scripts/tekton.gd index 951c779..8c268f6 100644 --- a/scripts/tekton.gd +++ b/scripts/tekton.gd @@ -295,6 +295,15 @@ func spawn_tiles_around(count: int = 4): """Spawns a mix of normal and special tiles in a radius.""" if not enhanced_gridmap: return + # Play throw animation + if not is_carried and not is_thrown: + play_animation("tekton_throw_tile") + # Queue idle after animation finishes (approx 1.0s) + get_tree().create_timer(1.0).timeout.connect(func(): + if not is_moving and not is_carried and not is_thrown: + play_animation("tekton_idle") + ) + var radius = 2 var rng = RandomNumberGenerator.new() rng.randomize() @@ -338,6 +347,10 @@ func spawn_tiles_around(count: int = 4): main.rpc("sync_grid_item", pos.x, 1, pos.y, item_id) spawned += 1 +@rpc("call_local", "reliable") +func play_animation_rpc(anim_name: String): + play_animation(anim_name) + func play_animation(anim_name: String): # Try specific user path first var anim_player = get_node_or_null("Visuals/tekton/Armature/AnimationPlayer")