164 lines
5.3 KiB
GDScript
164 lines
5.3 KiB
GDScript
extends Node
|
|
|
|
# StaticTektonManager
|
|
# Handles spawning and placement of Static Tektons in restricted zones.
|
|
|
|
const STAND_SCENE_PATH = "res://scenes/static_tekton_stand.tscn"
|
|
const TEKTON_SCENE_PATH = "res://scenes/tekton.tscn"
|
|
const STATIC_CONTROLLER_SCRIPT = "res://scripts/static_tekton_controller.gd"
|
|
|
|
# Zone Definitions based on CameraContextManager logic
|
|
# 9 Zones in a 3x3 grid (approximate for 14x14 map)
|
|
# Top-Left, Top-Mid, Top-Right
|
|
# Mid-Left, Mid-Mid, Mid-Right
|
|
# Bot-Left, Bot-Mid, Bot-Right
|
|
|
|
func calculate_spawn_points(_count: int, gridmap: Node) -> Array[Vector2i]:
|
|
"""
|
|
Calculates the 5 fixed potential spawn positions for static tektons.
|
|
Corners: (1,1), (W-2,1), (1,H-2), (W-2,H-2)
|
|
Center: (W/2, H/2)
|
|
Returns exactly 5 spots if possible.
|
|
"""
|
|
if not gridmap: return []
|
|
|
|
var width = gridmap.columns
|
|
var height = gridmap.rows
|
|
|
|
print("[StaticTektonManager] Calculating static tekton positions (Fixed 5-Spots)...")
|
|
|
|
# Fixed Spots
|
|
var spots: Array[Vector2i] = [
|
|
Vector2i(1, 1), # Top-Left
|
|
Vector2i(width - 2, 1), # Top-Right
|
|
Vector2i(1, height - 2), # Bottom-Left
|
|
Vector2i(width - 2, height - 2), # Bottom-Right
|
|
Vector2i(width / 2, height / 2) # Center
|
|
]
|
|
|
|
# Validate spots (ensure they are walkable and have room)
|
|
var valid_spots: Array[Vector2i] = []
|
|
for spot in spots:
|
|
if _is_valid_3x3(spot, gridmap):
|
|
valid_spots.append(spot)
|
|
else:
|
|
print("[StaticTektonManager] Warning: Spot %s is not a valid 3x3 walkable area!" % str(spot))
|
|
|
|
return valid_spots
|
|
|
|
func _pick_spot_in_zone_biased(zone: Rect2i, gridmap: Node, type: int) -> Vector2i:
|
|
# type: 0=TL, 1=TR, 2=BL, 3=BR, 4=Center
|
|
|
|
# ideal target relative to map
|
|
var target = Vector2i.ZERO
|
|
match type:
|
|
0: target = Vector2i(0, 0)
|
|
1: target = Vector2i(gridmap.columns, 0)
|
|
2: target = Vector2i(0, gridmap.rows)
|
|
3: target = Vector2i(gridmap.columns, gridmap.rows)
|
|
4: target = Vector2i(gridmap.columns / 2, gridmap.rows / 2)
|
|
|
|
# Clamp target to be inside valid area (taking 3x3 margin into account)
|
|
# Center of 3x3 must be at least 1 tile from edge
|
|
var min_x = max(1, zone.position.x + 1)
|
|
var max_x = min(gridmap.columns - 2, zone.position.x + zone.size.x - 2)
|
|
var min_y = max(1, zone.position.y + 1)
|
|
var max_y = min(gridmap.rows - 2, zone.position.y + zone.size.y - 2)
|
|
|
|
if min_x > max_x or min_y > max_y:
|
|
return Vector2i(-1, -1)
|
|
|
|
var clamped_target = Vector2i(
|
|
clamp(target.x, min_x, max_x),
|
|
clamp(target.y, min_y, max_y)
|
|
)
|
|
|
|
# BFS to find nearest valid 3x3 spot to clamped_target
|
|
var queue = [clamped_target]
|
|
var visited = {clamped_target: true}
|
|
|
|
# Limit search to avoid hanging
|
|
var checks = 0
|
|
while not queue.is_empty() and checks < 200:
|
|
var current = queue.pop_front()
|
|
checks += 1
|
|
|
|
if _is_valid_3x3(current, gridmap):
|
|
return current
|
|
|
|
var neighbors = [
|
|
Vector2i(0, 1), Vector2i(0, -1), Vector2i(1, 0), Vector2i(-1, 0)
|
|
]
|
|
|
|
for n in neighbors:
|
|
var next = current + n
|
|
if next.x >= min_x and next.x <= max_x and next.y >= min_y and next.y <= max_y:
|
|
if not visited.has(next):
|
|
visited[next] = true
|
|
queue.append(next)
|
|
|
|
return Vector2i(-1, -1)
|
|
|
|
func _is_valid_3x3(center: Vector2i, gridmap: Node) -> bool:
|
|
for dx in range(-1, 2):
|
|
for dy in range(-1, 2):
|
|
var check_pos = Vector3i(center.x + dx, 0, center.y + dy)
|
|
if gridmap.get_cell_item(check_pos) == -1:
|
|
return false
|
|
return true
|
|
|
|
func _pick_spot_in_zone(zone: Rect2i, gridmap: Node, zone_idx: int = -1) -> Vector2i:
|
|
"""
|
|
Find a valid 3x3 spot in the zone.
|
|
The returned position is the CENTER of the 3x3 area.
|
|
If zone_idx is a corner (0, 2, 6, 8), we snap to the absolute map corner.
|
|
"""
|
|
# CORNER SNAPPING: If this is a corner zone, force it to the extreme corner
|
|
# to ensure the 3x3 Stand fills the corner completely (no 1-tile gaps).
|
|
if zone_idx == 0: # Top-Left
|
|
var center = Vector2i(1, 1)
|
|
if _is_valid_3x3(center, gridmap): return center
|
|
elif zone_idx == 2: # Top-Right
|
|
var center = Vector2i(gridmap.columns - 2, 1)
|
|
if _is_valid_3x3(center, gridmap): return center
|
|
elif zone_idx == 6: # Bottom-Left
|
|
var center = Vector2i(1, gridmap.rows - 2)
|
|
if _is_valid_3x3(center, gridmap): return center
|
|
elif zone_idx == 8: # Bottom-Right
|
|
var center = Vector2i(gridmap.columns - 2, gridmap.rows - 2)
|
|
if _is_valid_3x3(center, gridmap): return center
|
|
|
|
# Fallback/Random logic for non-corner zones or if preferred corner was invalid
|
|
var attempts = 0
|
|
while attempts < 30:
|
|
attempts += 1
|
|
# Ensure center is at least 1 tile away from edges of the map to fit 3x3
|
|
# zone.position might be 0,0.
|
|
var min_x = max(1, zone.position.x + 1)
|
|
var max_x = min(gridmap.columns - 2, zone.position.x + zone.size.x - 2)
|
|
var min_y = max(1, zone.position.y + 1)
|
|
var max_y = min(gridmap.rows - 2, zone.position.y + zone.size.y - 2)
|
|
|
|
if min_x > max_x or min_y > max_y:
|
|
break # Zone too small
|
|
|
|
var x = randi_range(min_x, max_x)
|
|
var y = randi_range(min_y, max_y)
|
|
var center = Vector2i(x, y)
|
|
|
|
# Check 3x3 area validity
|
|
var valid_area = true
|
|
for dx in range(-1, 2):
|
|
for dy in range(-1, 2):
|
|
var check_pos = Vector3i(center.x + dx, 0, center.y + dy)
|
|
if gridmap.get_cell_item(check_pos) == -1:
|
|
valid_area = false # Void/Hole
|
|
break
|
|
# Optionally check for other obstacles?
|
|
|
|
if valid_area:
|
|
return center
|
|
|
|
print("[StaticTektonManager] Failed to find 3x3 spot in zone %s" % zone)
|
|
return Vector2i(-1, -1)
|