Eight windows per room and random zombie entry from different windows
This commit is contained in:
+32
-10
@@ -20,6 +20,7 @@ var wave_pending := false
|
||||
var smoke := false
|
||||
var awaiting_web_start := false
|
||||
var start_label: Label
|
||||
var last_window := -1
|
||||
|
||||
func _ready() -> void:
|
||||
smoke = "--smoke" in OS.get_cmdline_user_args()
|
||||
@@ -169,19 +170,40 @@ func _next_wave() -> void:
|
||||
for i in zombies_for_wave(wave):
|
||||
if player.hp <= 0:
|
||||
break
|
||||
_spawn(i % 4)
|
||||
_spawn()
|
||||
await get_tree().create_timer(0.8).timeout
|
||||
spawning = false
|
||||
_check_wave()
|
||||
|
||||
func _spawn(index: int) -> void:
|
||||
var centers := [Vector3(0, 0, -9), Vector3(0, 0, 9), Vector3(-9, 0, 0), Vector3(9, 0, 0)]
|
||||
var inward: Vector3 = -centers[index].normalized()
|
||||
## Sceglie una finestra a caso fra quelle disponibili, evitando di ripetere
|
||||
## l'ultima usata: gli zombie entrano da aperture diverse invece che sempre dalle
|
||||
## stesse quattro posizioni fisse.
|
||||
func _pick_window() -> int:
|
||||
var count: int = room.windows.size()
|
||||
if count == 0:
|
||||
return -1
|
||||
if count == 1:
|
||||
return 0
|
||||
var index: int = randi() % count
|
||||
if index == last_window:
|
||||
index = (last_window + 1 + randi() % (count - 1)) % count
|
||||
return index
|
||||
|
||||
func _spawn() -> void:
|
||||
var chosen := _pick_window()
|
||||
if chosen < 0:
|
||||
return
|
||||
last_window = chosen
|
||||
var window: Dictionary = room.windows[chosen]
|
||||
var slots: Array = window.slots
|
||||
var start: Vector3 = slots[randi() % slots.size()] # slot casuale nella finestra
|
||||
var face: Vector3 = window.center
|
||||
var inward: Vector3 = window.normal
|
||||
var zombie := CharacterBody3D.new()
|
||||
zombie.set_script(ZombieScript)
|
||||
zombie.player = player
|
||||
zombie.navigation = navigation
|
||||
zombie.position = centers[index] - inward * 1.2
|
||||
zombie.position = start
|
||||
add_child(zombie)
|
||||
alive += 1
|
||||
zombie.killed.connect(func(headshot: bool):
|
||||
@@ -190,12 +212,12 @@ func _spawn(index: int) -> void:
|
||||
_check_wave())
|
||||
var tween := zombie.create_tween()
|
||||
zombie.entry_tween = tween
|
||||
tween.tween_property(zombie, "position", centers[index] + Vector3(0, 1.55, 0), 1.2)
|
||||
tween.tween_property(zombie, "position", face + Vector3(0, 1.55, 0), 1.2)
|
||||
tween.tween_callback(func():
|
||||
for glass in room.glass_panes.duplicate():
|
||||
if is_instance_valid(glass) and Vector2(glass.position.x - centers[index].x, glass.position.z - centers[index].z).length() < 2:
|
||||
room.shatter_glass(glass))
|
||||
tween.tween_property(zombie, "position", centers[index] + inward * 1.4 + Vector3(0, 0.1, 0), 0.8)
|
||||
var glass = window.glass
|
||||
if is_instance_valid(glass):
|
||||
room.shatter_glass(glass)) # solo la finestra usata
|
||||
tween.tween_property(zombie, "position", face + inward * 1.4 + Vector3(0, 0.1, 0), 0.8)
|
||||
tween.tween_callback(func(): zombie.entering = false)
|
||||
|
||||
func _check_wave() -> void:
|
||||
|
||||
+81
-54
@@ -1,5 +1,7 @@
|
||||
## Stanza 18x18x5 con 4 finestre (una per muro) e nessuna porta.
|
||||
## Stanza 18x18x5 con 8 finestre (due per muro) e nessuna porta.
|
||||
## Geometria costruita in codice per determinismo; vetro = Area3D rompibile.
|
||||
## Le finestre sono DATI (windows): la logica di spawn degli zombie le legge da qui
|
||||
## invece di usare centri scritti a mano.
|
||||
class_name Room
|
||||
extends Node3D
|
||||
|
||||
@@ -14,9 +16,20 @@ const CEILING_REPEAT_M := 1.2 # CEIL5 contains 2x2 panels: each panel = 0.6m
|
||||
const WIN_W: float = 4.0
|
||||
const WIN_H: float = 2.0
|
||||
const WIN_Y0: float = 1.5
|
||||
# Due finestre per muro, distanziate simmetricamente. WIN_EDGE_GAP e' il muro che
|
||||
# resta fra il bordo di una finestra e l'angolo della stanza: con W=18 e WIN_W=4
|
||||
# restano 2.4 m agli angoli e 5.2 m di pilastro fra le due aperture.
|
||||
const WINDOWS_PER_WALL := 2
|
||||
const WIN_EDGE_GAP := 2.4
|
||||
# Slot di spawn fuori da ogni finestra, e quanto fuori dal muro.
|
||||
const SPAWN_SLOTS := 4
|
||||
const SPAWN_OUTSIDE := 1.2
|
||||
|
||||
var glass_panes: Array[Area3D] = []
|
||||
## Punti di spawn esterni, uno per finestra (4 finestre x 4 slot)
|
||||
## Una voce per finestra: {center (sul piano interno del muro, y=0), normal (verso
|
||||
## l'interno), axis, slots: Array[Vector3], glass: Area3D}.
|
||||
var windows: Array[Dictionary] = []
|
||||
## Punti di spawn esterni, appiattiti (finestre x slot) - usati dai test.
|
||||
var window_spawn_points: Array[Vector3] = []
|
||||
var window_normals: Array[Vector3] = [] # direzione "dentro" la stanza per ogni finestra
|
||||
|
||||
@@ -30,69 +43,82 @@ func _ready() -> void:
|
||||
# Soffitto
|
||||
_box(Vector3(W, 0.2, D), Vector3(0, H + 0.1, 0), ceil_mat, false)
|
||||
|
||||
# 4 muri con apertura finestra al centro
|
||||
# 4 muri, ognuno con WINDOWS_PER_WALL aperture (due per muro = otto totali)
|
||||
# nord (z = -D/2), sud (z = +D/2), ovest (x = -W/2), est (x = +W/2)
|
||||
var offsets := window_offsets(W)
|
||||
var walls := [
|
||||
{"center": Vector3(0, H / 2, -D / 2 - WALL_T / 2), "size": Vector3(W, H, WALL_T), "axis": "x"},
|
||||
{"center": Vector3(0, H / 2, D / 2 + WALL_T / 2), "size": Vector3(W, H, WALL_T), "axis": "x"},
|
||||
{"center": Vector3(-W / 2 - WALL_T / 2, H / 2, 0), "size": Vector3(WALL_T, H, D), "axis": "z"},
|
||||
{"center": Vector3(W / 2 + WALL_T / 2, H / 2, 0), "size": Vector3(WALL_T, H, D), "axis": "z"},
|
||||
{"axis": "x", "center": Vector3(0, H / 2, -D / 2 - WALL_T / 2), "size": Vector3(W, H, WALL_T), "normal": Vector3(0, 0, 1), "face": Vector3(0, 0, -D / 2)},
|
||||
{"axis": "x", "center": Vector3(0, H / 2, D / 2 + WALL_T / 2), "size": Vector3(W, H, WALL_T), "normal": Vector3(0, 0, -1), "face": Vector3(0, 0, D / 2)},
|
||||
{"axis": "z", "center": Vector3(-W / 2 - WALL_T / 2, H / 2, 0), "size": Vector3(WALL_T, H, D), "normal": Vector3(1, 0, 0), "face": Vector3(-W / 2, 0, 0)},
|
||||
{"axis": "z", "center": Vector3(W / 2 + WALL_T / 2, H / 2, 0), "size": Vector3(WALL_T, H, D), "normal": Vector3(-1, 0, 0), "face": Vector3(W / 2, 0, 0)},
|
||||
]
|
||||
for w in walls:
|
||||
_build_wall_with_window(w.center, w.size, w.axis, wall_mat)
|
||||
for wall in walls:
|
||||
_build_wall(wall.center, wall.size, wall.axis, wall_mat, offsets)
|
||||
for offset in offsets:
|
||||
var lateral := Vector3.RIGHT if wall.axis == "x" else Vector3.BACK
|
||||
var center: Vector3 = wall.face + lateral * offset
|
||||
var glass := _add_glass(center + Vector3(0, WIN_Y0 + WIN_H / 2, 0), _window_size(wall.axis, WIN_W, WIN_H))
|
||||
var slots: Array[Vector3] = []
|
||||
for i in SPAWN_SLOTS:
|
||||
var spread := (i - (SPAWN_SLOTS - 1) * 0.5) * (WIN_W / SPAWN_SLOTS)
|
||||
var point: Vector3 = center + lateral * spread + Vector3(wall.normal) * (-SPAWN_OUTSIDE)
|
||||
slots.append(point)
|
||||
window_spawn_points.append(point)
|
||||
window_normals.append(wall.normal)
|
||||
windows.append({"center": center, "normal": wall.normal, "axis": wall.axis,
|
||||
"slots": slots, "glass": glass})
|
||||
assert(windows.size() == WINDOWS_PER_WALL * 4)
|
||||
|
||||
# Vetrata per ogni finestra
|
||||
_add_glass(Vector3(0, WIN_Y0 + WIN_H / 2, -D / 2), Vector3(WIN_W, WIN_H, 0.05), Vector3(1, 0, 0))
|
||||
_add_glass(Vector3(0, WIN_Y0 + WIN_H / 2, D / 2), Vector3(WIN_W, WIN_H, 0.05), Vector3(1, 0, 0))
|
||||
_add_glass(Vector3(-W / 2, WIN_Y0 + WIN_H / 2, 0), Vector3(0.05, WIN_H, WIN_W), Vector3(0, 0, 1))
|
||||
_add_glass(Vector3(W / 2, WIN_Y0 + WIN_H / 2, 0), Vector3(0.05, WIN_H, WIN_W), Vector3(0, 0, 1))
|
||||
## Posizioni laterali delle aperture su un muro lungo `length`, simmetriche rispetto
|
||||
## al centro e distribuite uniformemente, lasciando WIN_EDGE_GAP a ogni angolo.
|
||||
static func window_offsets(length: float) -> Array[float]:
|
||||
var offsets: Array[float] = []
|
||||
if WINDOWS_PER_WALL <= 0:
|
||||
return offsets
|
||||
if WINDOWS_PER_WALL == 1:
|
||||
offsets.append(0.0)
|
||||
return offsets
|
||||
var limit := length * 0.5 - WIN_W * 0.5 - WIN_EDGE_GAP
|
||||
var step := 2.0 * limit / float(WINDOWS_PER_WALL - 1)
|
||||
for i in WINDOWS_PER_WALL:
|
||||
offsets.append((i - (WINDOWS_PER_WALL - 1) * 0.5) * step)
|
||||
return offsets
|
||||
|
||||
# Punti di spawn: 4 slot per finestra, fuori dal muro
|
||||
var win_centers := [
|
||||
{"c": Vector3(0, 0, -D / 2), "n": Vector3(0, 0, 1)},
|
||||
{"c": Vector3(0, 0, D / 2), "n": Vector3(0, 0, -1)},
|
||||
{"c": Vector3(-W / 2, 0, 0), "n": Vector3(1, 0, 0)},
|
||||
{"c": Vector3(W / 2, 0, 0), "n": Vector3(-1, 0, 0)},
|
||||
]
|
||||
for wc in win_centers:
|
||||
var side: Vector3 = wc.n.cross(Vector3(0, 1, 0)) # asse laterale della finestra
|
||||
for i in 4:
|
||||
var off := (i - 1.5) * (WIN_W / 3.0)
|
||||
var p: Vector3 = wc.c + side * off + wc.n * (-1.2) # 1.2 m fuori dalla finestra
|
||||
window_spawn_points.append(p)
|
||||
window_normals.append(wc.n)
|
||||
func _window_size(axis: String, width: float, height: float) -> Vector3:
|
||||
return Vector3(width, height, 0.05) if axis == "x" else Vector3(0.05, height, width)
|
||||
|
||||
func _build_wall_with_window(center: Vector3, size: Vector3, axis: String, mat: StandardMaterial3D) -> void:
|
||||
# Segments around the window opening (window is centered on the wall face)
|
||||
## Muro con un numero qualsiasi di aperture: pezzi pieni fra le aperture e agli
|
||||
## angoli, piu' davanzale e architrave per ciascuna apertura.
|
||||
func _build_wall(center: Vector3, size: Vector3, axis: String, mat: StandardMaterial3D, offsets: Array[float]) -> void:
|
||||
var wall := StaticBody3D.new()
|
||||
wall.name = "Wall"
|
||||
add_child(wall)
|
||||
var half_win_x := WIN_W / 2.0
|
||||
var y0 := WIN_Y0
|
||||
var y1 := WIN_Y0 + WIN_H
|
||||
var length := size.x if axis == "x" else size.z
|
||||
var sorted_offsets := offsets.duplicate()
|
||||
sorted_offsets.sort()
|
||||
var edges: Array[float] = [-length * 0.5]
|
||||
for offset in sorted_offsets:
|
||||
edges.append(offset - WIN_W * 0.5)
|
||||
edges.append(offset + WIN_W * 0.5)
|
||||
edges.append(length * 0.5)
|
||||
for i in range(0, edges.size(), 2):
|
||||
var span := edges[i + 1] - edges[i]
|
||||
if span > 0.001:
|
||||
_wall_segment(wall, center, size, axis, (edges[i] + edges[i + 1]) * 0.5, 0.0, span, size.y, mat)
|
||||
var header := size.y - (WIN_Y0 + WIN_H)
|
||||
for offset in sorted_offsets:
|
||||
_wall_segment(wall, center, size, axis, offset, -size.y * 0.5 + WIN_Y0 * 0.5,
|
||||
WIN_W, WIN_Y0, mat) # davanzale
|
||||
_wall_segment(wall, center, size, axis, offset, size.y * 0.5 - header * 0.5,
|
||||
WIN_W, header, mat) # architrave
|
||||
|
||||
if axis == "x": # muro lungo x
|
||||
var L := size.x
|
||||
var segs := [
|
||||
{"s": Vector3(L / 2 - half_win_x, size.y, size.z), "c": Vector3(-half_win_x - (L / 2 - half_win_x) / 2.0, 0, 0)},
|
||||
{"s": Vector3(L / 2 - half_win_x, size.y, size.z), "c": Vector3(half_win_x + (L / 2 - half_win_x) / 2.0, 0, 0)},
|
||||
{"s": Vector3(WIN_W, y0, size.z), "c": Vector3(0, -size.y / 2 + y0 / 2.0, 0)},
|
||||
{"s": Vector3(WIN_W, size.y - y1, size.z), "c": Vector3(0, size.y / 2 - (size.y - y1) / 2.0, 0)},
|
||||
]
|
||||
for seg in segs:
|
||||
_add_box_to(wall, seg.s, center + seg.c, mat)
|
||||
else: # muro lungo z
|
||||
var L := size.z
|
||||
var segs := [
|
||||
{"s": Vector3(size.x, size.y, L / 2 - half_win_x), "c": Vector3(0, 0, -half_win_x - (L / 2 - half_win_x) / 2.0)},
|
||||
{"s": Vector3(size.x, size.y, L / 2 - half_win_x), "c": Vector3(0, 0, half_win_x + (L / 2 - half_win_x) / 2.0)},
|
||||
{"s": Vector3(size.x, y0, WIN_W), "c": Vector3(0, -size.y / 2 + y0 / 2.0, 0)},
|
||||
{"s": Vector3(size.x, size.y - y1, WIN_W), "c": Vector3(0, size.y / 2 - (size.y - y1) / 2.0, 0)},
|
||||
]
|
||||
for seg in segs:
|
||||
_add_box_to(wall, seg.s, center + seg.c, mat)
|
||||
func _wall_segment(wall: StaticBody3D, center: Vector3, size: Vector3, axis: String,
|
||||
lateral: float, y_offset: float, span: float, height: float, mat: StandardMaterial3D) -> void:
|
||||
var box := Vector3(span, height, size.z) if axis == "x" else Vector3(size.x, height, span)
|
||||
var shift := Vector3(lateral, y_offset, 0) if axis == "x" else Vector3(0, y_offset, lateral)
|
||||
_add_box_to(wall, box, center + shift, mat)
|
||||
|
||||
func _add_glass(center: Vector3, size: Vector3, rot_axis: Vector3) -> void:
|
||||
func _add_glass(center: Vector3, size: Vector3) -> Area3D:
|
||||
var glass := Area3D.new()
|
||||
glass.name = "Glass"
|
||||
glass.collision_layer = 8
|
||||
@@ -118,6 +144,7 @@ func _add_glass(center: Vector3, size: Vector3, rot_axis: Vector3) -> void:
|
||||
mesh.mesh = bm
|
||||
glass.add_child(mesh)
|
||||
glass_panes.append(glass)
|
||||
return glass
|
||||
|
||||
func shatter_glass(glass: Area3D) -> void:
|
||||
var sound_origin := Node3D.new()
|
||||
|
||||
Reference in New Issue
Block a user