the old system wasn't random, but this makes things much more intuitive while not reducing the (interesting) emergent complexity. IMO
94 lines
2.8 KiB
GDScript
94 lines
2.8 KiB
GDScript
class_name Board
|
|
extends Node3D
|
|
|
|
var dims: Vector2i
|
|
|
|
func _ready():
|
|
for piece in pieces():
|
|
dims.x = max(dims.x, piece.lpos.x)
|
|
dims.y = max(dims.y, piece.lpos.y)
|
|
|
|
# for zooming out to see the whole puzzle
|
|
func top_left_aabb() -> AABB:
|
|
return AABB(position-Vector3(0,0,1), Vector3.ONE*0.1)
|
|
|
|
func bottom_right_aabb() -> AABB:
|
|
return AABB(position + Vector3(dims.x, 0, dims.y) + Vector3(1,0,2), Vector3.ONE*0.1)
|
|
|
|
func pieces() -> Array[Piece]:
|
|
var sorted: Array[Piece] = []
|
|
for child in get_children():
|
|
if child is Piece:
|
|
sorted.push_back(child)
|
|
# important for consistency.
|
|
# everything involving pieces is done top to bottom, then left to right
|
|
# expensive? yes. wasteful? definitely. worth it? i think so.
|
|
sorted.sort_custom(func(a, b): return a.lpos.y < b.lpos.y or a.lpos.x < b.lpos.x)
|
|
return sorted
|
|
|
|
func pieces_at(pos: Vector2i) -> Array[Piece]:
|
|
var out: Array[Piece] = []
|
|
for piece in pieces():
|
|
if piece.lpos == pos:
|
|
out.push_back(piece)
|
|
return out
|
|
|
|
func find_piece_at(pos: Vector2i, filter: Callable) -> Piece:
|
|
var at := pieces_at(pos)
|
|
var idx := at.find_custom(filter)
|
|
if idx < 0: return
|
|
return at[idx]
|
|
|
|
func any_at(pos: Vector2i, filter: Callable) -> bool:
|
|
return pieces_at(pos).any(filter)
|
|
|
|
func type_at(pos: Vector2i, type: Piece.Type) -> Piece:
|
|
return find_piece_at(pos, func(p): return p.type == type)
|
|
|
|
# TODO: ball collisions
|
|
func solid_at(pos: Vector2i) -> bool:
|
|
return any_at(pos, func(p): return p.type == Piece.Type.Wall or p.type == Piece.Type.Ball or p.type == Piece.Type.Player)
|
|
|
|
func passable_at(pos: Vector2i, for_player := false) -> bool:
|
|
return not solid_at(pos) and not (for_player and type_at(pos, Piece.Type.PlayerBarrier))
|
|
|
|
func remove_piece(piece: Piece):
|
|
remove_child(piece)
|
|
|
|
func add_piece(piece: Piece):
|
|
add_child(piece)
|
|
|
|
func add_pieces(piece: Array[Piece]):
|
|
for p in piece:
|
|
add_piece(p)
|
|
|
|
# here are the phases:
|
|
# cardinal velocity
|
|
# diagonal velocity
|
|
# uhh that's it lol?
|
|
# well, subphases: higher lvel magnitude moves first within a phase
|
|
# TODO: maybe show these phases with the sun
|
|
func do_step():
|
|
var pieces_moving := pieces().filter(func(piece): return piece.lvel != Vector2i.ZERO)
|
|
var pieces_cardinal := pieces_moving.filter(func(piece): return piece.lvel.x == 0 or piece.lvel.y == 0)
|
|
var pieces_diagonal := pieces_moving.filter(func(piece): return piece.lvel.x != 0 and piece.lvel.y != 0)
|
|
|
|
var magnitude_sort := func(a: Piece, b: Piece):
|
|
return a.lvel.length() < b.lvel.length()
|
|
|
|
pieces_cardinal.sort_custom(magnitude_sort)
|
|
pieces_diagonal.sort_custom(magnitude_sort)
|
|
|
|
for piece in pieces_cardinal:
|
|
piece.do_step(self)
|
|
for piece in pieces_diagonal:
|
|
piece.do_step(self)
|
|
|
|
func undo_step() -> Callable:
|
|
var undos: Array[Callable] = []
|
|
for piece in pieces():
|
|
undos.push_back(piece.undo_step())
|
|
return func():
|
|
for undo in undos:
|
|
undo.call()
|