164 lines
5.4 KiB
GDScript
164 lines
5.4 KiB
GDScript
## 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)
|