xanarch-td/scripts/gameplay/Lane.gd
2026-06-03 21:53:16 -04:00

164 lines
5.4 KiB
GDScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

## Lane.gd
## Manages a player's lane: a TileMap background, a grid of TowerSlots,
## and the creep Path2D that winds through the play area.
##
## Lane layout (64px tiles, 4 cols wide × 8 rows tall build area):
##
## col: 0 1 2 3 [path col 4]
## row 0 [ ] [ ] [ ] [ ] ↓
## row 1 [ ] [ ] [ ] [ ] ↓
## row 2 [ ] [ ] [ ] [ ] ←←
## row 3 [ ] [ ] [ ] [ ] ↑
## row 4 [ ] [ ] [ ] [ ] ↑
## row 5 [ ] [ ] [ ] [ ] →→
## row 6 [ ] [ ] [ ] [ ] ↓
## row 7 [ ] [ ] [ ] [ ] ↓ → EXIT
##
## The path enters from the top-right, snakes left/right, exits bottom-right.
## Creeps follow the Path2D curve; towers build on the coloured cells.
class_name Lane
extends Node2D
@export var player_id: int = 0
@export var slot_columns: int = 4
@export var slot_rows: int = 8
@export var tile_size: float = 64.0
# Colour used to tint each slot cell (overridden in editor per player)
@export var slot_color: Color = Color(0.18, 0.22, 0.28, 0.85)
@export var path_color: Color = Color(0.35, 0.30, 0.20, 1.0)
var _slots: Array[Node] = []
# Path points in local space. The snake runs down the right side of the
# build grid, then doubles back. Adjust these to reshape the lane.
const PATH_POINTS: Array = [
Vector2(288, -32), # entry: above top of lane (col 4.5, off-screen)
Vector2(288, 32), # row 0 right edge
Vector2(288, 96), # row 1
Vector2(288, 160), # row 2 — turn left
Vector2( 32, 160), # row 2 left edge — turn up
Vector2( 32, 96), # row 1 left
Vector2( 32, 32), # row 0 left — turn right (second pass)
Vector2(288, 32), # NOTE: this pass is a separate sweep for zigzag lanes
# Simplified single-snake for MVP — straight entry, one hairpin, exit:
]
# Cleaner single-hairpin path used at runtime
func _build_path_points() -> PackedVector2Array:
var pts := PackedVector2Array()
var w := slot_columns * tile_size # 256
var h := slot_rows * tile_size # 512
var mid := tile_size * 0.5 # 32 — centre of path corridor
# Entry: top-right corridor (one tile to the right of the build grid)
pts.append(Vector2(w + mid, -tile_size)) # off-screen top
pts.append(Vector2(w + mid, h * 0.25)) # quarter down
# Hairpin: sweep left across the top of the grid
pts.append(Vector2(w + mid, h * 0.375))
pts.append(Vector2(-mid, h * 0.375)) # left gutter
# Come back up then sweep across mid
pts.append(Vector2(-mid, h * 0.5))
pts.append(Vector2(w + mid, h * 0.5))
# Second hairpin lower
pts.append(Vector2(w + mid, h * 0.625))
pts.append(Vector2(-mid, h * 0.625))
# Exit: bottom-right
pts.append(Vector2(-mid, h * 0.875))
pts.append(Vector2(w + mid, h * 0.875))
pts.append(Vector2(w + mid, h + tile_size)) # off-screen bottom
return pts
func _ready() -> void:
_draw_background()
_build_slots()
_build_path()
## Draws the lane background using plain ColorRect nodes (no TileSet required).
## Replace with a real TileMap once you have art assets.
func _draw_background() -> void:
var w := slot_columns * tile_size
var h := slot_rows * tile_size
var corridor := tile_size # width of the path corridor on each side
# Main play field
var field := ColorRect.new()
field.size = Vector2(w, h)
field.color = Color(0.12, 0.14, 0.16)
add_child(field)
# Left path gutter
var left_gutter := ColorRect.new()
left_gutter.size = Vector2(corridor, h)
left_gutter.position = Vector2(-corridor, 0)
left_gutter.color = path_color
add_child(left_gutter)
# Right path corridor
var right_corridor := ColorRect.new()
right_corridor.size = Vector2(corridor, h)
right_corridor.position = Vector2(w, 0)
right_corridor.color = path_color
add_child(right_corridor)
# Hairpin cross-corridors
var hairpin_rows := [0.375, 0.5, 0.625]
for frac in hairpin_rows:
var bar := ColorRect.new()
bar.size = Vector2(w + corridor * 2, corridor)
bar.position = Vector2(-corridor, h * frac - corridor * 0.5)
bar.color = path_color
add_child(bar)
# Grid lines over the build area
for col in range(slot_columns + 1):
var line := ColorRect.new()
line.size = Vector2(1, h)
line.position = Vector2(col * tile_size, 0)
line.color = Color(1, 1, 1, 0.06)
add_child(line)
for row in range(slot_rows + 1):
var line := ColorRect.new()
line.size = Vector2(w, 1)
line.position = Vector2(0, row * tile_size)
line.color = Color(1, 1, 1, 0.06)
add_child(line)
func _build_slots() -> void:
var slot_scene := load("res://scenes/gameplay/TowerSlot.tscn") as PackedScene
if not slot_scene:
push_error("Lane: TowerSlot.tscn not found")
return
for row in range(slot_rows):
for col in range(slot_columns):
var slot = slot_scene.instantiate()
slot.player_id = player_id
slot.position = Vector2(col * tile_size + tile_size * 0.5,
row * tile_size + tile_size * 0.5)
slot.slot_clicked.connect(_on_slot_clicked)
add_child(slot)
_slots.append(slot)
func _build_path() -> void:
var path := Path2D.new()
path.name = "CreepPath"
var curve := Curve2D.new()
for pt in _build_path_points():
curve.add_point(pt)
path.curve = curve
add_child(path)
func get_path_points() -> PackedVector2Array:
var path := get_node_or_null("CreepPath") as Path2D
if path:
return path.curve.get_baked_points()
return PackedVector2Array()
func _on_slot_clicked(slot: Node) -> void:
EventBus.slot_clicked_in_lane.emit(player_id, slot)