class_name Piece extends MeshInstance3D const WALL = preload("res://model/wall.tres") const GOAL = preload("res://model/goal.tres") const BALL = preload("res://model/ball.tres") const PLAYER = preload("res://model/player.tres") const FLOOR_ICE = preload("res://model/floor_ice.tres") enum Type { Wall, Goal, Ball, Player, FloorIce, } # idea: SLOW MO KEY!!!!!!!!!!!!!!! ## 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 @export var lpos: Vector2i: get: return lpos set(val): lpos = val position = target_pos start_pos = position target_pos = Vector3(lpos.x, position.y, lpos.y) anim_progress = 0 ## logical velocity @export var lvel: Vector2i: get: return lvel set(val): lvel = val display_vel = target_vel start_vel = target_vel target_vel = lvel anim_progress = 0 @export var type: Piece.Type @onready var start_pos := position @onready var target_pos := position @onready var start_vel := Vector2(lvel) @onready var target_vel := Vector2(lvel) @onready var display_vel := Vector2(lvel) @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 if board.solid_at(lpos + lvel.clampi(-1, 1)): lpos = lpos lvel -= lvel.clampi(-1, 1) return lpos += lvel.clampi(-1, 1) var on_ice := !!board.type_at(lpos, Piece.Type.FloorIce) if not on_ice: lvel -= lvel.clampi(-1, 1) 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 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(lpos: Vector2i) -> Piece: return make(lpos, Piece.Type.Wall, WALL) static func goal(lpos: Vector2i) -> Piece: return make(lpos, Piece.Type.Goal, GOAL) static func ball(lpos: Vector2i) -> Piece: return make(lpos, Piece.Type.Ball, BALL) static func player(lpos: Vector2i) -> Piece: return make(lpos, Piece.Type.Player, PLAYER) static func floor_ice(lpos: Vector2i) -> Piece: return make(lpos, Piece.Type.FloorIce, FLOOR_ICE) func _ready() -> void: lpos = lpos lvel = Vector2i.ZERO anim_progress = 1 speedometer = Label3D.new() speedometer.billboard = BaseMaterial3D.BILLBOARD_ENABLED speedometer.text = "0" speedometer.no_depth_test = true speedometer.font_size = 64 add_child(speedometer) func format_vel(vel: Vector2) -> String: if vel.is_zero_approx(): return "" else: return "%d,%d" % [vel.x,vel.y] #if is_zero_approx(vel.x) or is_zero_approx(vel.y): #if is_equal_approx(round(abs(vel.x)), abs(vel.x)) or is_equal_approx(round(abs(vel.y)), abs(vel.y)): #return "%.1d" % vel.length() #else: #return "%.1f" % vel.length() #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) display_vel = start_vel.lerp(target_vel, anim_progress) speedometer.text = format_vel(display_vel)