class_name Piece extends MeshInstance3D const BALL = preload("res://piece/ball.tscn") const FLOOR_ICE = preload("res://piece/floor_ice.tscn") const GOAL = preload("res://piece/goal.tscn") const PLAYER = preload("res://piece/player.tscn") const WALL = preload("res://piece/wall.tscn") enum Type { Wall, Goal, Ball, Player, FloorIce, } ## squares per second @export_range(0.1, 50) var anim_speed := 10.0 @export_range(0.1, 50) var anim_speed_slow := 2.0 ## logical position var lpos: Vector2i: get: return Vector2i(int(target_pos.x), int(target_pos.z)) set(val): position = target_pos start_pos = position target_pos = Vector3(val.x, position.y, val.y) anim_progress = 0 ## logical velocity @export var lvel := Vector2i.ZERO @export var type: Piece.Type @onready var start_pos := position @onready var target_pos := position @onready var anim_progress := 1.0 var speedometer: Label3D # TODO: this is *complex*, transfer momentum and stuff func do_step(board: Board): if lvel == Vector2i.ZERO: return var move := lvel.clampi(-1, 1) var new_pos := lpos + move if board.solid_at(new_pos): var ball := board.type_at(new_pos, Piece.Type.Ball) if ball: ball.do_push(lvel-move).call() lvel = -move else: lpos = lpos lvel -= move return lpos = new_pos var on_ice := !!board.type_at(lpos, Piece.Type.FloorIce) if not on_ice: lvel -= move func undo_step() -> Callable: var old_pos := lpos var old_vel := lvel return func(): lpos = old_pos lvel = old_vel func do_move(move: Vector2i) -> Callable: return func(): lpos += move func undo_move() -> Callable: var old_pos := lpos return func(): lpos = old_pos func do_push(move: Vector2i) -> Callable: return func(): lvel += move anim_progress = 1 func undo_push() -> Callable: var old_vel := lvel return func(): lvel = old_vel anim_progress = 1 static func ball(pos: Vector2i) -> Piece: return BALL.instantiate().with_lpos(pos) static func floor_ice(pos: Vector2i) -> Piece: return FLOOR_ICE.instantiate().with_lpos(pos) static func goal(pos: Vector2i) -> Piece: return GOAL.instantiate().with_lpos(pos) static func player(pos: Vector2i) -> Piece: return PLAYER.instantiate().with_lpos(pos) static func wall(pos: Vector2i) -> Piece: return WALL.instantiate().with_lpos(pos) func _ready() -> void: anim_progress = 1 speedometer = Label3D.new() speedometer.billboard = BaseMaterial3D.BILLBOARD_ENABLED speedometer.no_depth_test = true speedometer.font_size = 64 var font := SystemFont.new() font.font_names = PackedStringArray(["monospace"]) speedometer.font = font add_child(speedometer) #TODO: arrow! func format_vel(vel: Vector2) -> String: if vel.is_zero_approx(): return "" else: return "%d,%d" % [vel.x,vel.y] func _process(delta: float) -> void: var speed := anim_speed_slow if Input.is_action_pressed("slowmo") else anim_speed anim_progress = min(1, anim_progress + speed*delta) position = start_pos.lerp(target_pos, anim_progress) speedometer.text = format_vel(lvel) # weird hack only necessary for the soon to be gone text parser func with_lpos(pos: Vector2i) -> Piece: lpos = pos lpos = pos anim_progress = 1 return self