feat: Add diverse game tiles, 3D wall scenes, and player movement and stop-n-go managers.

This commit is contained in:
Yogi Wiguna
2026-03-12 17:47:23 +08:00
parent 739489cd6f
commit e5e99f5853
5 changed files with 100 additions and 69 deletions
@@ -180,7 +180,7 @@ item/15/navigation_mesh_transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0,
item/15/navigation_layers = 1
item/16/name = "wall"
item/16/mesh = ExtResource("9_uwjsj")
item/16/mesh_transform = Transform3D(2, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0.5)
item/16/mesh_transform = Transform3D(1.68, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0)
item/16/mesh_cast_shadow = 1
item/16/shapes = []
item/16/navigation_mesh_transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0)
+24
View File
@@ -0,0 +1,24 @@
[gd_scene format=3 uid="uid://b8yqx5v3n8u1p"]
[sub_resource type="BoxShape3D" id="BoxShape3D_wall"]
size = Vector3(1, 2, 0.1)
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_wall"]
albedo_color = Color(0.8, 0.2, 0.2, 0.8)
emission_enabled = true
emission = Color(1, 0, 0, 1)
emission_energy_multiplier = 0.5
[sub_resource type="BoxMesh" id="BoxMesh_wall"]
material = SubResource("StandardMaterial3D_wall")
size = Vector3(1, 2, 0.1)
[node name="SafeZoneWall" type="StaticBody3D"]
collision_layer = 1
collision_mask = 0
[node name="MeshInstance3D" type="MeshInstance3D" parent="."]
mesh = SubResource("BoxMesh_wall")
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
shape = SubResource("BoxShape3D_wall")
+18
View File
@@ -0,0 +1,18 @@
[gd_scene format=3 uid="uid://cggmcgvdj6wxt"]
[ext_resource type="ArrayMesh" uid="uid://dtr46jmckif0p" path="res://assets/models/meshes/block.res" id="1_block"]
[sub_resource type="BoxShape3D" id="BoxShape3D_wall"]
size = Vector3(1, 1.5, 1)
[node name="Wall3D" type="StaticBody3D" unique_id=992511920]
collision_mask = 0
[node name="MeshInstance3D" type="MeshInstance3D" parent="." unique_id=1405008923]
transform = Transform3D(1.68, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0)
mesh = ExtResource("1_block")
[node name="CollisionShape3D" type="CollisionShape3D" parent="." unique_id=1446599023]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.35764623, 0)
shape = SubResource("BoxShape3D_wall")
debug_color = Color(0.94447005, 0.20628157, 0.3074098, 0.41960785)
+31 -19
View File
@@ -100,9 +100,9 @@ func simple_move_to(grid_position: Vector2i) -> bool:
print("[Move] Failed: Blocked by Item %d on Floor 1" % cell_item)
return false
# PHYSICS CHECK: Ensure no static obstacles (like Stands) are blocking the path
if _is_position_blocked_by_physics(grid_position):
print("[Move] Failed: Blocked by physics raycast at %s" % grid_position)
# PHYSICS CHECK: Ensure no static obstacles (like Wall blocks or Stands) are blocking the path
if _is_path_blocked_by_physics(player.current_position, grid_position):
print("[Move] Failed: Path blocked by physics at %s" % grid_position)
return false
if player.is_position_occupied(grid_position):
@@ -512,30 +512,42 @@ func _can_push_to(pos: Vector2i) -> bool:
if _is_position_in_static_stand_area(pos):
return false
if _is_position_blocked_by_physics(pos):
if _is_path_blocked_by_physics(player.current_position, pos):
return false
return true
func _is_position_blocked_by_physics(target_pos: Vector2i) -> bool:
func _is_path_blocked_by_physics(from_grid: Vector2i, to_grid: Vector2i) -> bool:
if not player.is_inside_tree(): return false
var space_state = player.get_world_3d().direct_space_state
var center_x = target_pos.x + 0.5
var center_z = target_pos.y + 0.5
var from = Vector3(center_x, 1.0, center_z)
var to = Vector3(center_x, 0.1, center_z)
var query = PhysicsRayQueryParameters3D.create(from, to)
query.collide_with_areas = false
query.collide_with_bodies = true
# 1. Path check: Block movement if a wall exists between the current and target tile
var from_v3 = Vector3(from_grid.x + 0.5, 0.5, from_grid.y + 0.5)
var to_v3 = Vector3(to_grid.x + 0.5, 0.5, to_grid.y + 0.5)
var result = space_state.intersect_ray(query)
if result:
if result.collider != player:
# ONLY block if it's a Static Tekton Stand
# Ignore GridMap floors/walls, which are handled by get_cell_item rules
if result.collider.name.find("StaticTektonStand") != -1 or result.collider.is_in_group("StaticTektonStands") or result.collider.has_method("is_stand"):
return true
var path_query = PhysicsRayQueryParameters3D.create(from_v3, to_v3)
path_query.collide_with_areas = false
path_query.collide_with_bodies = true
var path_result = space_state.intersect_ray(path_query)
if path_result:
if path_result.collider != player:
# This correctly hits thin walls placed on tile boundaries
return true
# 2. Target tile occupancy check: Block if a static object is in the middle of the tile
var target_from = Vector3(to_grid.x + 0.5, 1.0, to_grid.y + 0.5)
var target_to = Vector3(to_grid.x + 0.5, 0.1, to_grid.y + 0.5)
var target_query = PhysicsRayQueryParameters3D.create(target_from, target_to)
target_query.collide_with_areas = false
target_query.collide_with_bodies = true
var target_result = space_state.intersect_ray(target_query)
if target_result:
if target_result.collider != player:
# This hits objects like Stands that sit in the center of the tile
return true
return false
+26 -49
View File
@@ -21,6 +21,8 @@ const POWERUP_SPAWN_COUNT: int = 5 # Number of power-up tiles to spawn
var powerups_spawned: bool = false
var stop_phase_occurred: bool = false
var safe_zone_wall_scene = preload("res://scenes/wall_3d.tscn")
const PERMANENT_POWERUP_LOCATIONS: Array[Vector2i] = [
Vector2i(4, 3), # Area 1
Vector2i(8, 7), # Area 2
@@ -657,45 +659,18 @@ func sync_safe_zone(centers: Array, radius: int):
if gridmap.get_cell_item(Vector3i(x, 0, z)) == TILE_WALKABLE:
gridmap.set_cell_item(Vector3i(x, 0, z), TILE_SAFE)
# 2. Paint the walls (item ID 16) ON LAYER 1 to block movement
# We scan from -3 to +3 to handle symmetric wall indices around the 5x5 zone
for dx in range(-radius - 1, radius + 2):
for dz in range(-radius - 1, radius + 2):
var x = center.x + dx
var z = center.y + dz
if x >= 0 and x < gridmap.columns and z >= 0 and z < gridmap.rows:
# big room logic: 5x5 span (Indices -2 to 2)
# North/West use -radius/-radius-1, South/East use radius/radius+1
var is_north = dz == -radius - 1 and dx >= -radius and dx <= radius
var is_south = dz == radius and dx >= -radius and dx <= radius
var is_west = dx == -radius and dz >= -radius and dz <= radius
var is_east = dx == radius + 1 and dz >= -radius and dz <= radius
var orientation = 0
var is_wall = false
if is_north:
orientation = 0
is_wall = true
elif is_south:
orientation = 0
is_wall = true
elif is_west:
orientation = 22
is_wall = true
elif is_east:
orientation = 22
is_wall = true
if is_wall:
# Door logic: Skip center (0) on the respective border line
var is_door = (is_north and dx == 0) or (is_south and dx == 0) or \
(is_west and dz == 0) or (is_east and dz == 0)
if is_door:
continue
gridmap.set_cell_item(Vector3i(x, 1, z), 16, orientation)
# 2. Instantiate North and South walls (Horizontal)
for dx in range(-radius, radius + 1):
if dx == 0: continue # Opening
_instantiate_safe_zone_wall(Vector3(center.x + dx + 0.5, 0.0, center.y - radius), 0)
_instantiate_safe_zone_wall(Vector3(center.x + dx + 0.5, 0.0, center.y + radius + 1), 0)
# 3. Instantiate East and West walls (Vertical - 4 walls each)
# From -2 to 2 (radius), with center opening = 4 walls per side
for dz in range(-radius, radius + 1):
if dz == 0: continue # Opening
_instantiate_safe_zone_wall(Vector3(center.x - radius, 0.0, center.y + dz + 0.5), 90)
_instantiate_safe_zone_wall(Vector3(center.x + radius + 1, 0.0, center.y + dz + 0.5), 90)
# Update pathfinding for bots and movement checks
gridmap.initialize_astar()
@@ -728,16 +703,9 @@ func sync_clear_safe_zone(centers_to_clear: Array):
if current == TILE_SAFE:
gridmap.set_cell_item(Vector3i(x, 0, z), TILE_WALKABLE)
# Also clean up walls ON LAYER 1
# Scan -3 to +3 range to ensure all shifted walls are cleared
for dx in range(-SAFE_ZONE_RADIUS - 1, SAFE_ZONE_RADIUS + 2):
for dz in range(-SAFE_ZONE_RADIUS - 1, SAFE_ZONE_RADIUS + 2):
var x = center.x + dx
var z = center.y + dz
if x >= 0 and x < gridmap.columns and z >= 0 and z < gridmap.rows:
# Clear any wall items on Floor 1
if gridmap.get_cell_item(Vector3i(x, 1, z)) != -1:
gridmap.set_cell_item(Vector3i(x, 1, z), -1)
# Also clean up walls
for wall in get_tree().get_nodes_in_group("SafeZoneWalls"):
wall.queue_free()
# Clear local state
safe_zone_centers = []
@@ -751,6 +719,15 @@ func sync_clear_safe_zone(centers_to_clear: Array):
safe_zone_centers = []
safe_zone_spawned = false
func _instantiate_safe_zone_wall(pos: Vector3, rotation_deg: float):
if not safe_zone_wall_scene: return
var wall = safe_zone_wall_scene.instantiate()
add_child(wall)
wall.add_to_group("SafeZoneWalls")
wall.position = pos
wall.rotation_degrees.y = rotation_deg
# =============================================================================
# Power-Up Tile Spawning (Speed & Ghost)
# =============================================================================