godot-puzzle/piece.gd
2025-05-03 19:37:06 -04:00

147 lines
3.5 KiB
GDScript

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)