From cd7881bc3fb6969b76098c8890d8fba38ba2f03f Mon Sep 17 00:00:00 2001 From: Yogi Wiguna Date: Wed, 4 Mar 2026 17:40:10 +0800 Subject: [PATCH] feat: Introduce an EnhancedGridMap with advanced generation, randomization, pathfinding, and data serialization, along with new player, powerup, and portal managers. --- addons/enhanced_gridmap/enhanced_gridmap.gd | 2 +- scenes/main.gd | 29 ++++++----- scenes/main.tscn | 13 +---- scenes/player.gd | 31 +++++++++++ scripts/managers/player_input_manager.gd | 19 ++----- scripts/managers/player_movement_manager.gd | 6 +++ scripts/managers/portal_mode_manager.gd | 4 +- scripts/managers/powerup_manager.gd | 16 ++++-- scripts/managers/special_tiles_manager.gd | 40 ++++++++++----- scripts/managers/stop_n_go_manager.gd | 11 +++- scripts/managers/touch_controls.gd | 57 ++++++--------------- scripts/tekton.gd | 7 +-- 12 files changed, 128 insertions(+), 107 deletions(-) diff --git a/addons/enhanced_gridmap/enhanced_gridmap.gd b/addons/enhanced_gridmap/enhanced_gridmap.gd index ca853cf..894130f 100644 --- a/addons/enhanced_gridmap/enhanced_gridmap.gd +++ b/addons/enhanced_gridmap/enhanced_gridmap.gd @@ -16,7 +16,7 @@ signal grid_updated @export var hover_item: int = 1 @export var start_item: int = 2 @export var end_item: int = 3 -@export var immutable_items: Array[int] = [4] # Items that cannot be randomized/reset +@export var immutable_items: Array[int] = [1, 2, 3, 4] # Items that cannot be randomized/reset (Start, Safe, Finish, Wall) var current_mesh_library: MeshLibrary var grid_data: Array = [] # 3D array [floor][row][column] diff --git a/scenes/main.gd b/scenes/main.gd index e88bd41..3eb119d 100644 --- a/scenes/main.gd +++ b/scenes/main.gd @@ -1581,12 +1581,15 @@ func request_randomize_item(grid_position: Vector2i): func sync_grid_item(x: int, y: int, z: int, item: int): var enhanced_gridmap = $EnhancedGridMap if enhanced_gridmap: - # WALL-SAFETY CHECK: Block tiles (7-20) from being placed on walls (4) or void (-1) + # PROTECTED FLOOR CHECK: Block tiles (7-20) from being placed on walls (4) or void (-1) + # Note: We allow spawning on Safe Zones, Start, and Finish as it's on Layer 1. if y == 1 and item >= 7 and item <= 20: var f0 = enhanced_gridmap.get_cell_item(Vector3i(x, 0, z)) - if f0 == 4 or f0 == -1: - # Log and block illegal placement - print("[Main] Blocked illegal tile (%d) placement on wall/void at (%d, %d)" % [item, x, z]) + var f1 = enhanced_gridmap.get_cell_item(Vector3i(x, 1, z)) + + # Block if Layer 0 is Wall (4) or Void (-1) + # OR Layer 1 is already a wall (4 or 13) + if f0 in [4, -1] or f1 == 4 or f1 == 13: return enhanced_gridmap.set_cell_item(Vector3i(x, y, z), item) @@ -1607,10 +1610,11 @@ func sync_grid_items_batch(data: Array): var z = entry.get("z", 0) var item = entry.get("item", -1) - # WALL-SAFETY CHECK + # PROTECTED FLOOR CHECK if y == 1 and item >= 7 and item <= 20: var f0 = enhanced_gridmap.get_cell_item(Vector3i(x, 0, z)) - if f0 == 4 or f0 == -1: + var f1 = enhanced_gridmap.get_cell_item(Vector3i(x, 1, z)) + if f0 in [4, -1] or f1 == 4 or f1 == 13: continue enhanced_gridmap.set_cell_item(Vector3i(x, y, z), item) @@ -1625,13 +1629,12 @@ func randomize_game_grid(): var enhanced_gridmap = $EnhancedGridMap if enhanced_gridmap: - # Randomize Floor 1 using ScarcityController - enhanced_gridmap.randomize_floor(1, ScarcityController.get_random_tile_id) - - # Sync to clients if needed (usually handled by initial state sync or explicit item syncs) - # Since Main.gd doesn't have a "Sync Floor" RPC, we rely on clients running the same seed or syncing individual cells. - # For now, let's assume server authority + sync on connect handles it, or add sync loop if critical. - pass + # Use density-aware callable: 60% chance for a real tile, 40% for none + var density_callable = func(): + if randf() > 0.6: return -1 + return ScarcityController.get_random_tile_id() + + enhanced_gridmap.randomize_floor(1, density_callable) @rpc("authority", "call_local", "reliable") func sync_full_grid_data_stop_n_go(floor0: PackedInt32Array, floor1: PackedInt32Array, cols: int, rows: int): diff --git a/scenes/main.tscn b/scenes/main.tscn index cb0c8b9..d20ae8c 100644 --- a/scenes/main.tscn +++ b/scenes/main.tscn @@ -32,7 +32,6 @@ [ext_resource type="Texture2D" uid="uid://3up2su2e0lfa" path="res://assets/graphics/touch_control/freeze_area.png" id="28_fv21b"] [ext_resource type="Texture2D" uid="uid://ckhdyxnho6sjp" path="res://assets/graphics/touch_control/spawn_tile.png" id="28_j8jky"] [ext_resource type="Texture2D" uid="uid://b2vhatfmufn3d" path="res://assets/graphics/touch_control/ghost.png" id="33_5q0nq"] -[ext_resource type="Texture2D" uid="uid://cdwk17moidkj2" path="res://assets/graphics/touch_control/knock_tekton.png" id="35_fuf3a"] [ext_resource type="Texture2D" uid="uid://biun2yvglxgij" path="res://assets/graphics/touch_control/grab_tekton.png" id="36_pibwh"] [ext_resource type="Script" uid="uid://86ikh0wuqk7v" path="res://scripts/ui/powerup_inventory_ui.gd" id="powerup_ui_script"] [ext_resource type="Script" uid="uid://b54tfa0n6kogi" path="res://scripts/managers/touch_controls.gd" id="touch_manager"] @@ -99,6 +98,7 @@ columns = 14 rows = 14 floors = 2 auto_randomize = true +immutable_items = Array[int]([4]) metadata/_editor_floor_ = Vector3(0, 1, 0) [node name="Camera3D" type="Camera3D" parent="." unique_id=1200003163] @@ -9808,16 +9808,7 @@ flat = true icon_alignment = 1 expand_icon = true -[node name="TektonKnockBtn" type="Button" parent="TouchControls/TouchControls/ActionsBtn" unique_id=2133168886] -layout_mode = 2 -size_flags_horizontal = 3 -size_flags_vertical = 3 -icon = ExtResource("35_fuf3a") -flat = true -icon_alignment = 1 -expand_icon = true - -[node name="TektonThrowBtn" type="Button" parent="TouchControls/TouchControls/ActionsBtn" unique_id=2097928368] +[node name="TektonGrabBtn" type="Button" parent="TouchControls/TouchControls/ActionsBtn" unique_id=2097928368] layout_mode = 2 size_flags_horizontal = 3 size_flags_vertical = 3 diff --git a/scenes/player.gd b/scenes/player.gd index 930cf4c..5fcd459 100644 --- a/scenes/player.gd +++ b/scenes/player.gd @@ -707,6 +707,9 @@ var immunity_timer: float = 0.0 @rpc("any_peer", "call_local") func apply_stagger(duration: float = 1.5): + if is_carrying_tekton: + return # Cannot be staggered while carrying a Tekton + if immunity_timer > 0: return # Immune! @@ -2191,6 +2194,34 @@ func sync_throw_tekton(target_pos: Vector2i): print("[Player %s] Threw Tekton to %s (Dist: %s)" % [name, target_pos, target_pos.distance_to(tekton.current_position)]) +func drop_tekton(): + """Drops the Tekton at the current player position immediately.""" + if not is_multiplayer_authority() or not is_carrying_tekton: + return + + if is_multiplayer_authority() and can_rpc(): + rpc("sync_drop_tekton") + +@rpc("any_peer", "call_local", "reliable") +func sync_drop_tekton(): + if carried_tekton: + var tekton = carried_tekton + carried_tekton = null + is_carrying_tekton = false + + # Set its position to player's current position (but on ground) + var drop_pos = grid_to_world(current_position) + tekton.global_position = drop_pos + + if tekton.has_method("set_carried"): + tekton.set_carried(false) + + # Trigger landing effects (minimal scale) + if tekton.has_method("on_thrown_landing"): + tekton.on_thrown_landing(self, 1.0) # Minimal scale impact + + print("[Player %s] Dropped Tekton at %s" % [name, current_position]) + # is_attack_mode is already declared at top of file (or inherited?) # Keeping is_knock_mode here for now or moving it up would be better, but let's just fix the error first. var is_knock_mode: bool = false # Yellow mode for knocking Tekton diff --git a/scripts/managers/player_input_manager.gd b/scripts/managers/player_input_manager.gd index 0ab76ff..1ef18cb 100644 --- a/scripts/managers/player_input_manager.gd +++ b/scripts/managers/player_input_manager.gd @@ -120,26 +120,13 @@ func handle_unhandled_input(event): player.enter_attack_mode() KEY_E: - if player.powerup_manager: - # Spawn Boost + if player.is_carrying_tekton and player.powerup_manager: + # Spawn Boost (Now uses Tekton) if player.powerup_manager.has_method("spawn_boost_reward"): player.powerup_manager.spawn_boost_reward() - else: - # Fallback if method missing - player.powerup_manager.use_special_effect() KEY_G: - if player.is_carrying_tekton: - if player.powerup_manager and player.powerup_manager.can_use_special(): - player.throw_tekton() - player.powerup_manager.reset_boost() - else: + if not player.is_carrying_tekton: player.grab_tekton() - KEY_B: - if player.powerup_manager and player.powerup_manager.can_use_special(): - if player.has_method("_find_nearby_tekton") and player._find_nearby_tekton(): - player.knock_tekton() - elif player.has_method("enter_knock_mode"): - player.enter_knock_mode() # Handle spawn point selection if not yet selected diff --git a/scripts/managers/player_movement_manager.gd b/scripts/managers/player_movement_manager.gd index 97c21dd..5c51c4c 100644 --- a/scripts/managers/player_movement_manager.gd +++ b/scripts/managers/player_movement_manager.gd @@ -160,6 +160,12 @@ func try_push(target_pos: Vector2i, direction: Vector2i) -> bool: if not other_player: return false + # === INVULNERABILITY CHECK === + if other_player.get("is_carrying_tekton"): + print("[Move] Push blocked: Target is carrying a Tekton and is invulnerable.") + NotificationManager.send_message(player, "Target is Immune!", NotificationManager.MessageType.WARNING) + return false + # === NEW LOGIC: Only allow push if in ATTACK MODE === if not player.get("is_attack_mode"): # Standard bumping effect or nothing? diff --git a/scripts/managers/portal_mode_manager.gd b/scripts/managers/portal_mode_manager.gd index e474102..8ccdcad 100644 --- a/scripts/managers/portal_mode_manager.gd +++ b/scripts/managers/portal_mode_manager.gd @@ -479,9 +479,9 @@ func _refresh_tiles(): for x in range(GRID_SIZE): for z in range(GRID_SIZE): - # 1. Check if Floor 0 is a wall or empty (non-walkable) + # 1. Check if Floor 0 is a wall or void var floor_0_item = gridmap.get_cell_item(Vector3i(x, 0, z)) - if floor_0_item == 4 or floor_0_item == -1: + if floor_0_item in [4, -1]: continue # 1.5. Prevent spawning directly under portal doors diff --git a/scripts/managers/powerup_manager.gd b/scripts/managers/powerup_manager.gd index 689541a..2d0b3b0 100644 --- a/scripts/managers/powerup_manager.gd +++ b/scripts/managers/powerup_manager.gd @@ -177,14 +177,20 @@ func consume_boost(amount: float): rpc("sync_boost", current_boost) func spawn_boost_reward() -> bool: - """Alternative Boost Usage: Spawn a PowerUp Tile properly.""" - if not can_use_special(): + """Alternative Boost Usage: Spawn a PowerUp Tile properly. + Now requires carrying a Tekton (E shortcut).""" + if not player.is_carrying_tekton: return false if player.special_tiles_manager and player.special_tiles_manager.has_method("spawn_powerups_around"): - player.special_tiles_manager.spawn_powerups_around(player.current_position) - reset_boost() # Consumes full bar - print("[PowerUp] %s used Boost to SPAWN ITEM." % player.name) + # Spawn only common tiles (7-10) with 100% density + player.special_tiles_manager.spawn_powerups_around(player.current_position, true, true, true) + + # Drop the Tekton after spawning + if player.has_method("drop_tekton"): + player.drop_tekton() + + print("[PowerUp] %s used Tekton to SPAWN TILES (100%% Density Common)." % player.name) return true return false diff --git a/scripts/managers/special_tiles_manager.gd b/scripts/managers/special_tiles_manager.gd index 588a512..ac43bb5 100644 --- a/scripts/managers/special_tiles_manager.gd +++ b/scripts/managers/special_tiles_manager.gd @@ -377,20 +377,21 @@ func _execute_block_floor(target_pos: Vector2i = Vector2i.ZERO): if _is_position_blocked_by_stand(pos): continue var block_pos = Vector3i(pos.x, 0, pos.y) - var current_item = enhanced_gridmap.get_cell_item(block_pos) + var original_item = enhanced_gridmap.get_cell_item(block_pos) + # PROTECTED FLOOR CHECK: avoid overwriting Start (1), Safe (2), Finish (3), or Wall (4) var is_immutable = false if "immutable_items" in enhanced_gridmap: - if current_item in enhanced_gridmap.immutable_items: + if original_item in enhanced_gridmap.immutable_items: is_immutable = true - if current_item == 4 or is_immutable: continue + if original_item in [1, 2, 3, 4] or is_immutable: continue batch_data.append({"x": block_pos.x, "y": 0, "z": block_pos.z, "item": 4}) # Record for restoration blocked_tiles.append({ "position": block_pos, - "original_item": current_item, + "original_item": original_item, "timer": BLOCK_DURATION }) @@ -415,7 +416,7 @@ func _execute_invisible_mode(target: Node3D): # Helper: Spawn Powerups (For Super Push) # ============================================================================= -func spawn_powerups_around(center: Vector2i, force_powerups: bool = true): +func spawn_powerups_around(center: Vector2i, force_powerups: bool = true, only_common: bool = false, full_density: bool = false): # "spawn / replace your nearby tiles into power up ( special tiles )" # New PowerUp Tiles are 11, 12, 13, 14 var radius = 2 @@ -428,20 +429,31 @@ func spawn_powerups_around(center: Vector2i, force_powerups: bool = true): if enhanced_gridmap.is_position_valid(pos): # Random chance to spawn ANYTHING at this spot (keep density reasonable) - if rng.randf() > 0.5: continue + if not full_density and rng.randf() > 0.5: continue + + # 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)) + if f0 in [4, -1] or f1 == 4 or f1 == 13: + continue var item_id: int - # 70% Chance for Normal Tile (7-10) - if rng.randf() < 0.7: + var mode = LobbyManager.get_game_mode() + + if only_common or LobbyManager.is_game_mode(GameMode.Mode.STOP_N_GO): + # Spawn ONLY goals (7-10) for Stop n Go or if forced item_id = rng.randi_range(7, 10) else: - # 30% Chance for PowerUp (Speed 11, Freeze 12, Ghost 14 - Exclude Wall 13 in restricted modes) - var mode = LobbyManager.get_game_mode() - var is_restricted = GameMode.is_restricted(mode) - if is_restricted: - item_id = [11, 14].pick_random() + # Free mode: 60% Chance for Normal Tile (7-10), 40% for PowerUp + if rng.randf() < 0.6: + item_id = rng.randi_range(7, 10) else: - item_id = rng.randi_range(11, 14) + # 30% Chance for PowerUp (Speed 11, Freeze 12, Ghost 14 - Exclude Wall 13 in restricted modes) + var is_restricted = GameMode.is_restricted(mode) + if is_restricted: + item_id = [11, 14].pick_random() + else: + item_id = rng.randi_range(11, 14) var cell = Vector3i(pos.x, 1, pos.y) diff --git a/scripts/managers/stop_n_go_manager.gd b/scripts/managers/stop_n_go_manager.gd index 7efda86..3a5e791 100644 --- a/scripts/managers/stop_n_go_manager.gd +++ b/scripts/managers/stop_n_go_manager.gd @@ -319,10 +319,17 @@ func _spawn_mission_tiles(): for z in range(gridmap.rows): # Ensure we don't spawn on obstacles var base_tile = gridmap.get_cell_item(Vector3i(x, 0, z)) - if base_tile == TILE_OBSTACLE: + 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 or current_item == 13: + continue + + # Spawn tiles with 60% density + if randf() > 0.6: + gridmap.set_cell_item(Vector3i(x, 1, z), -1) continue - # Spawn tiles on all floors (100% density) var tile_type = goal_items[randi() % goal_items.size()] gridmap.set_cell_item(Vector3i(x, 1, z), tile_type) diff --git a/scripts/managers/touch_controls.gd b/scripts/managers/touch_controls.gd index d5bf139..09db2d7 100644 --- a/scripts/managers/touch_controls.gd +++ b/scripts/managers/touch_controls.gd @@ -14,8 +14,7 @@ var put_button: Button var attack_mode_button: Button # Renamed from special_button var spawn_boost_button: Button var settings_button: Button -var tekton_knock_button: Button -var tekton_throw_button: Button +var tekton_grab_button: Button # Settings - persisted to config file const CONFIG_PATH = "user://touch_controls_settings.cfg" @@ -137,15 +136,13 @@ func _create_touch_ui(): grab_button = _find_or_create_action_button(actions_container, "Grab", "👋", button_positions.grab) put_button = _find_or_create_action_button(actions_container, "Put", "📦", button_positions.put) - tekton_knock_button = _find_or_create_action_button(actions_container, "TektonKnock", "👊", Vector2(-280, -160)) - tekton_throw_button = _find_or_create_action_button(actions_container, "TektonThrow", "🎯", Vector2(-280, -80)) + tekton_grab_button = _find_or_create_action_button(actions_container, "TektonGrab", "👋", Vector2(-280, -80)) # Order: AttackMode, SpawnBoost, Grab if attack_mode_button: actions_container.move_child(attack_mode_button, 0) if spawn_boost_button: actions_container.move_child(spawn_boost_button, 1) if grab_button: actions_container.move_child(grab_button, 2) - if tekton_knock_button: actions_container.move_child(tekton_knock_button, 3) - if tekton_throw_button: actions_container.move_child(tekton_throw_button, 4) + if tekton_grab_button: actions_container.move_child(tekton_grab_button, 3) # Hide Put Button if put_button: @@ -263,8 +260,7 @@ func _ensure_shortcut_label(btn: Button, button_name: String): "Put": existing_lbl.text = "" "AttackMode": existing_lbl.text = "Q" if is_sng else "" "SpawnBoost": existing_lbl.text = "E" if is_sng else "" - "TektonKnock": existing_lbl.text = "B" - "TektonThrow": existing_lbl.text = "G" + "TektonGrab": existing_lbl.text = "G" # Ensure correct placement (Top Right) existing_lbl.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT @@ -294,8 +290,7 @@ func _ensure_shortcut_label(btn: Button, button_name: String): "Put": shortcut_lbl.text = "" # Disabled shortcut "AttackMode": shortcut_lbl.text = "Q" "SpawnBoost": shortcut_lbl.text = "E" - "TektonKnock": shortcut_lbl.text = "B" - "TektonThrow": shortcut_lbl.text = "G" + "TektonGrab": shortcut_lbl.text = "G" btn.add_child(shortcut_lbl) @@ -315,8 +310,7 @@ func _on_button_pressed(button_name: String): "Put": btn = put_button "AttackMode": btn = attack_mode_button "SpawnBoost": btn = spawn_boost_button - "TektonKnock": btn = tekton_knock_button - "TektonThrow": btn = tekton_throw_button + "TektonGrab": btn = tekton_grab_button if btn: var tween = create_tween() @@ -347,27 +341,11 @@ func _on_button_pressed(button_name: String): else: print("[TouchControls] PowerUpManager missing on player") "SpawnBoost": - var powerup_mgr = local_player.get_node_or_null("PowerUpManager") - if powerup_mgr and powerup_mgr.can_use_special(): - if local_player.special_tiles_manager and local_player.special_tiles_manager.has_method("spawn_powerups_around"): - local_player.special_tiles_manager.spawn_powerups_around(local_player.current_position) - powerup_mgr.reset_boost() - "TektonKnock": - var powerup_mgr = local_player.get_node_or_null("PowerUpManager") - if powerup_mgr and powerup_mgr.can_use_special(): - # Proactive: If nearby, knock immediately. Else enter mode. - if local_player.has_method("_find_nearby_tekton") and local_player._find_nearby_tekton(): - local_player.knock_tekton() - elif local_player.has_method("enter_knock_mode"): - local_player.enter_knock_mode() - "TektonThrow": - if local_player.is_carrying_tekton: - var powerup_mgr = local_player.get_node_or_null("PowerUpManager") - if powerup_mgr and powerup_mgr.can_use_special(): - if local_player.has_method("throw_tekton"): - local_player.throw_tekton() - powerup_mgr.reset_boost() - else: + if local_player and local_player.is_carrying_tekton: + if local_player.powerup_manager and local_player.powerup_manager.has_method("spawn_boost_reward"): + local_player.powerup_manager.spawn_boost_reward() + "TektonGrab": + if not local_player.is_carrying_tekton: if local_player.has_method("grab_tekton"): local_player.grab_tekton() @@ -378,8 +356,7 @@ func _on_button_released(button_name: String): "Put": btn = put_button "AttackMode": btn = attack_mode_button "SpawnBoost": btn = spawn_boost_button - "TektonKnock": btn = tekton_knock_button - "TektonThrow": btn = tekton_throw_button + "TektonGrab": btn = tekton_grab_button if btn: var tween = create_tween() @@ -548,12 +525,12 @@ func _on_boost_points_changed(current_points: int, max_points: int): var is_full = current_points >= (max_points - 1) # Tolerance _update_boost_button_state(attack_mode_button, is_full) - _update_boost_button_state(spawn_boost_button, is_full) - _update_boost_button_state(tekton_knock_button, is_full) - # TektonThrow can be used for "Grab" even without full boost - var can_throw_or_grab = is_full or (local_player and not local_player.is_carrying_tekton) - _update_boost_button_state(tekton_throw_button, can_throw_or_grab) + # SpawnBoost depends on carrying a Tekton, not boost points + var can_spawn = local_player and local_player.is_carrying_tekton + _update_boost_button_state(spawn_boost_button, can_spawn) + + _update_boost_button_state(tekton_grab_button, true) # Always enabled (logic handles grab-ability) func _on_tekton_carried_changed(_is_carrying: bool): # Refresh button states when player grabs/throws a tekton diff --git a/scripts/tekton.gd b/scripts/tekton.gd index 0ed3b98..82a38b9 100644 --- a/scripts/tekton.gd +++ b/scripts/tekton.gd @@ -299,7 +299,8 @@ func temporarily_change_floor(center: Vector2i, radius: int, new_id: int, durati 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: + # 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]: # 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): @@ -370,10 +371,10 @@ func spawn_tiles_around(count: int = 4): if _is_position_blocked_by_stand(pos): continue - if enhanced_gridmap.is_position_valid(pos): # 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 == 4 or floor_0_item == -1: + if floor_0_item in [4, -1]: continue # 50% chance to spawn something