From 498554be2946a21a8d2d27bd06200629d818f2c1 Mon Sep 17 00:00:00 2001 From: mehbark Date: Thu, 1 May 2025 16:02:47 -0400 Subject: [PATCH] UndoRedo poc --- board.gd | 65 ++++++++++++++++++++++++++++++++++---------------------- main.gd | 24 +++++++++++++++++---- piece.gd | 36 +++++++++++++++++++++++-------- 3 files changed, 87 insertions(+), 38 deletions(-) diff --git a/board.gd b/board.gd index 38297d4..4ba2a88 100644 --- a/board.gd +++ b/board.gd @@ -3,32 +3,47 @@ extends Node3D @export var dims: Vector2i -var pieces: Dictionary[Vector2i, Array] = {} +# idea: board has very little logic, delegates to something else +# l8r tho + +# literally just linearly search to find pieces at a position +# children + +# todo: Array[Piece] +func pieces() -> Array: + return get_children() func pieces_at(pos: Vector2i) -> Array[Piece]: - return pieces.get(pos, []) + var out: Array[Piece] = [] + for piece in get_children(): + if piece.lpos == pos: + out.push_back(piece) + return out -func remove_piece(piece: Piece, from: Vector2i): - pieces[from] = pieces_at(from).filter(func (p): return p != piece) +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 add_piece(piece: Piece, to: Vector2i): - pieces.get_or_add(to, []).push_back(piece) - piece.position = Vector3(to.x, 0, to.y) +func any_at(pos: Vector2i, filter: Callable) -> bool: + return pieces_at(pos).any(filter) + +func solid_at(pos: Vector2i) -> bool: + return any_at(pos, func(p): return p.type == Piece.Type.Wall or p.type == Piece.Type.Box) + +func passable_at(pos: Vector2i) -> bool: + return not solid_at(pos) + +func remove_piece(piece: Piece): + remove_child(piece) + +func add_piece(piece: Piece): add_child(piece) -func add_pieces(piece: Array[Piece], to: Vector2i): +func add_pieces(piece: Array[Piece]): for p in piece: - add_piece(p, to) - -func do_move(piece: Piece, from: Vector2i, to: Vector2i) -> Callable: - return func(): - remove_piece(piece, from) - add_piece(piece, to) - -func undo_move(piece: Piece, from: Vector2i, to: Vector2i) -> Callable: - return func(): - remove_piece(piece, to) - add_piece(piece, from) + add_piece(p) static func from_string(src: String) -> Board: var lines := src.lstrip("\n").rstrip("\n").split("\n") @@ -48,10 +63,10 @@ static func from_string(src: String) -> Board: var c := line[x] var pos := Vector2i(x, y) match c: - ".": board.add_piece(Piece.goal(), pos) - "*": board.add_pieces([Piece.goal(), Piece.box()], pos) - "+": board.add_pieces([Piece.goal(), Piece.player()], pos) - "$": board.add_piece(Piece.box(), pos) - "@": board.add_piece(Piece.player(), pos) - "#": board.add_piece(Piece.wall(), pos) + ".": board.add_piece(Piece.goal(pos)) + "*": board.add_pieces([Piece.goal(pos), Piece.box(pos)]) + "+": board.add_pieces([Piece.goal(pos), Piece.player(pos)]) + "$": board.add_piece(Piece.box(pos)) + "@": board.add_piece(Piece.player(pos)) + "#": board.add_piece(Piece.wall(pos)) return board diff --git a/main.gd b/main.gd index f253b20..9eacae2 100644 --- a/main.gd +++ b/main.gd @@ -13,9 +13,14 @@ var microban_1 := " var hist := UndoRedo.new() @onready var board := Board.from_string(microban_1) +var player: Piece func _ready() -> void: add_child(board) + for piece in board.pieces(): + if piece.type == Piece.Type.Player: + player = piece + break func _input(event: InputEvent) -> void: if event.is_action_pressed("u"): @@ -29,7 +34,6 @@ func _input(event: InputEvent) -> void: elif event.is_action_pressed("undo"): hist.undo() elif event.is_action_pressed("redo"): - print('hi') hist.redo() elif event.is_action_pressed("restart"): restart() @@ -39,6 +43,18 @@ func restart(): pass func step(move: Vector2i): - hist.create_action("move") - - hist.commit_action() + var pos := player.lpos + var box := board.find_piece_at(pos + move, func(p): return p.type == Piece.Type.Box) + if board.passable_at(pos + move): + hist.create_action("move") + hist.add_do_method(player.do_move(move)) + hist.add_undo_method(player.undo_move()) + hist.commit_action() + elif box != null and board.passable_at(pos + move * 2): + hist.create_action("push") + hist.add_do_method(player.do_move(move)) + hist.add_do_method(box.do_move(move)) + hist.add_undo_method(player.undo_move()) + hist.add_undo_method(box.undo_move()) + hist.commit_action() + diff --git a/piece.gd b/piece.gd index 9a3d3c6..58d5538 100644 --- a/piece.gd +++ b/piece.gd @@ -13,22 +13,40 @@ enum Type { Player, } +# logical position +@export var lpos: Vector2i: + get: + return lpos + set(val): + lpos = val + position = Vector3(val.x, 0, val.y) + @export var type: Piece.Type -static func make(type: Piece.Type, mesh: Mesh) -> Piece: +func do_move(move: Vector2i) -> Callable: + return func(): + lpos += move + +func undo_move() -> Callable: + var old_pos := lpos + return func(): + lpos = old_pos + +static func make(lpos: Vector2i, type: Piece.Type, mesh: Mesh) -> Piece: var piece := Piece.new() + piece.lpos = lpos piece.mesh = mesh piece.type = type return piece -static func wall() -> Piece: - return make(Piece.Type.Wall, WALL) +static func wall(lpos: Vector2i) -> Piece: + return make(lpos, Piece.Type.Wall, WALL) -static func goal() -> Piece: - return make(Piece.Type.Goal, GOAL) +static func goal(lpos: Vector2i) -> Piece: + return make(lpos, Piece.Type.Goal, GOAL) -static func box() -> Piece: - return make(Piece.Type.Box, BOX) +static func box(lpos: Vector2i) -> Piece: + return make(lpos, Piece.Type.Box, BOX) -static func player() -> Piece: - return make(Piece.Type.Player, PLAYER) +static func player(lpos: Vector2i) -> Piece: + return make(lpos, Piece.Type.Player, PLAYER)