From 200e198428cce391ba2ab908d50d44173701454c Mon Sep 17 00:00:00 2001 From: Yogi Wiguna Date: Fri, 13 Mar 2026 10:07:17 +0800 Subject: [PATCH] feat: Introduce Tekton roaming NPC with grid movement, player interaction, tile spawning, and visual effects. --- scenes/player.gd | 4 +- scripts/managers/special_tiles_manager.gd | 15 +++++--- scripts/managers/stop_n_go_manager.gd | 14 +++---- scripts/tekton.gd | 46 ++++++++++++++--------- 4 files changed, 48 insertions(+), 31 deletions(-) diff --git a/scenes/player.gd b/scenes/player.gd index 2671ca0..4af77a6 100644 --- a/scenes/player.gd +++ b/scenes/player.gd @@ -155,6 +155,8 @@ var _selected_character: String = "Masbro" var selected_character: String: get: return _selected_character set(value): + if _selected_character == value: + return _selected_character = value if is_inside_tree(): set_character(value) @@ -368,7 +370,7 @@ func set_character(character_name: String) -> void: push_warning("Invalid character name: %s" % character_name) return - selected_character = character_name + _selected_character = character_name # Hide all character models if character_bob: character_bob.visible = false diff --git a/scripts/managers/special_tiles_manager.gd b/scripts/managers/special_tiles_manager.gd index b04a1d4..9baed62 100644 --- a/scripts/managers/special_tiles_manager.gd +++ b/scripts/managers/special_tiles_manager.gd @@ -392,7 +392,7 @@ func _execute_block_floor(target_pos: Vector2i = Vector2i.ZERO): if "immutable_items" in enhanced_gridmap: if original_item in enhanced_gridmap.immutable_items: is_immutable = true - if original_item in [1, 2, 3, 4, 16] or is_immutable: continue + if original_item in [1, 2, 3, 4, 15, 16] or is_immutable: continue batch_data.append({"x": block_pos.x, "y": 0, "z": block_pos.z, "item": 4}) @@ -442,6 +442,11 @@ func spawn_powerups_around(center: Vector2i, force_powerups: bool = true, only_c # PROTECTED FLOOR CHECK: Don't spawn on existing walls or void var f0 = enhanced_gridmap.get_cell_item(Vector3i(pos.x, 0, pos.y)) var f1 = enhanced_gridmap.get_cell_item(Vector3i(pos.x, 1, pos.y)) + + # Stop n Go: Don't overwrite static powerup spawns (ID 15 floor) + if LobbyManager.is_game_mode(GameMode.Mode.STOP_N_GO) and f0 == 15: + continue + if f0 in [4, -1] or f1 in [4, 16]: continue @@ -449,16 +454,16 @@ func spawn_powerups_around(center: Vector2i, force_powerups: bool = true, only_c var mode = LobbyManager.get_game_mode() if only_common or LobbyManager.is_game_mode(GameMode.Mode.STOP_N_GO): - # Spawn ONLY common tiles (7-10) for Stop n Go or if forced + # Spawn ONLY common tiles (7-10) in Stop n Go mode (User Request) item_id = rng.randi_range(7, 10) else: - # Free mode: 80% Chance for Common Tile (7-10), 20% for PowerUp + # Other modes: 80% Chance for Common Tile (7-10), 20% for PowerUp if rng.randf() < 0.8: item_id = rng.randi_range(7, 10) else: # 20% Chance for PowerUp - var is_restricted = GameMode.is_restricted(mode) - if is_restricted: + if LobbyManager.is_game_mode(GameMode.Mode.TEKTON_DOORS): + # Restrict to Speed (11) and Ghost (14) for Tekton Doors item_id = [11, 14].pick_random() else: item_id = rng.randi_range(11, 14) diff --git a/scripts/managers/stop_n_go_manager.gd b/scripts/managers/stop_n_go_manager.gd index 2822de0..9c8c1b3 100644 --- a/scripts/managers/stop_n_go_manager.gd +++ b/scripts/managers/stop_n_go_manager.gd @@ -16,7 +16,7 @@ var safe_zone_centers: Array[Vector2i] = [] var safe_zone_spawned: bool = false # Power-Up Tile Spawning -const POWERUP_TILES = [11, 12, 13, 14] # Speed, Freeze, Wall, Ghost +const POWERUP_TILES = [11, 14] # Speed, Ghost (Freeze and Wall excluded in this mode) const POWERUP_SPAWN_COUNT: int = 5 # Number of power-up tiles to spawn var powerups_spawned: bool = false var stop_phase_occurred: bool = false @@ -360,8 +360,8 @@ func _spawn_mission_tiles(): var base_tile = gridmap.get_cell_item(Vector3i(x, 0, z)) var current_item = gridmap.get_cell_item(Vector3i(x, 1, z)) - # PROTECTED FLOOR CHECK: Don't spawn on walls or void - if base_tile in [TILE_OBSTACLE, -1] or current_item == TILE_OBSTACLE: + # PROTECTED FLOOR CHECK: Don't spawn on walls, void, or static powerup pods + if base_tile in [TILE_OBSTACLE, -1, TILE_LIGHTNING_STONE] or current_item == TILE_OBSTACLE: continue # Spawn tiles with 60% density @@ -593,9 +593,9 @@ func _scatter_player_tiles(player_node: Node): # Bounds check if pos.x < 0 or pos.x >= gridmap.columns or pos.y < 0 or pos.y >= gridmap.rows: continue - # Check floor is walkable (not void, not obstacle) + # Check floor is walkable (not void, not obstacle, not static powerup spawn) var floor_tile = gridmap.get_cell_item(Vector3i(pos.x, 0, pos.y)) - if floor_tile == -1 or floor_tile == TILE_OBSTACLE: + if floor_tile == -1 or floor_tile == TILE_OBSTACLE or floor_tile == TILE_LIGHTNING_STONE: continue # Check floor 1 is empty (no existing item) var existing_item = gridmap.get_cell_item(Vector3i(pos.x, 1, pos.y)) @@ -750,8 +750,8 @@ func _spawn_powerup_tiles(): # Set Floor 0 beneath power-up to ID 15 (Ancient Lightning Stone) gridmap.set_cell_item(Vector3i(pos.x, 0, pos.y), TILE_LIGHTNING_STONE) - # Cycle through the available power-up types - var tile_id = POWERUP_TILES[i % POWERUP_TILES.size()] + # Select a random power-up type (User Request: ensure all types can spawn) + var tile_id = POWERUP_TILES.pick_random() # Place on Floor 1 gridmap.set_cell_item(Vector3i(pos.x, 1, pos.y), tile_id) diff --git a/scripts/tekton.gd b/scripts/tekton.gd index 780a906..531e1b2 100644 --- a/scripts/tekton.gd +++ b/scripts/tekton.gd @@ -298,9 +298,8 @@ func temporarily_change_floor(center: Vector2i, radius: int, new_id: int, durati 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) - # PROTECTED FLOOR CHECK: avoid overwriting Start (1), Safe (2), Finish (3), or Wall (4) - if original != new_id and not original in [1, 2, 3, 4]: + # PROTECTED FLOOR CHECK: avoid overwriting Start (1), Safe (2), Finish (3), Wall (4), or PowerUp Stand (15) + if original != new_id and not original in [1, 2, 3, 4, 15]: # PRE-FIX: If we capture a "Hole" (Void or Pickup 7-14) here, we must record it as Floor (0) # so that we restore a valid floor later. if original == -1 or (original >= 7 and original <= 14): @@ -371,24 +370,35 @@ func spawn_tiles_around(count: int = 4): if _is_position_blocked_by_stand(pos): continue - # EXTRA CHECK: Do not spawn tiles on walls (ID 4) or empty void (ID -1) on Floor 0 - # Note: We allow spawning on Safe Zones, Start, and Finish as it's on Layer 1. - var floor_0_item = enhanced_gridmap.get_cell_item(Vector3i(pos.x, 0, pos.y)) - if floor_0_item in [4, -1]: - continue - - # 50% chance to spawn something - if rng.randf() > 0.5: continue + # EXTRA CHECK: Do not spawn tiles on walls (ID 4), empty void (ID -1), or static powerup spawns (ID 15) + var floor_0_item = enhanced_gridmap.get_cell_item(Vector3i(pos.x, 0, pos.y)) + + # Stop n Go: Don't overwrite static powerup spawns + if LobbyManager and LobbyManager.game_mode == "Stop n Go" and floor_0_item == 15: + continue - # Determine Type - var item_id: int - var roll = rng.randf() + if floor_0_item in [4, -1]: + continue - if roll < 0.6: - # 60% Normal Tile (7-10) - item_id = rng.randi_range(7, 10) + # 50% chance to spawn something + if rng.randf() > 0.5: continue + + # Determine Type + var item_id: int + var roll = rng.randf() + + if roll < 0.6 or (LobbyManager and LobbyManager.game_mode == "Stop n Go"): + # 60% Normal Tile (7-10) OR 100% if Stop n Go (User Request) + item_id = rng.randi_range(7, 10) + else: + # 40% PowerUp (11-14) + var mode = GameMode.Mode.FREEMODE + if LobbyManager: + mode = LobbyManager.get_game_mode() + + if mode == GameMode.Mode.TEKTON_DOORS: + item_id = [11, 14].pick_random() else: - # 40% PowerUp (11-14) item_id = rng.randi_range(11, 14) if item_id != -1: