mirror of
https://github.com/ElHombreAlex/FTL-Faster-Than-Light-VR.git
synced 2026-10-06 01:00:06 +02:00
191 lines
10 KiB
GDScript
191 lines
10 KiB
GDScript
extends Node3D
|
|
|
|
const Voxels = preload("res://scripts/voxel_mesh.gd")
|
|
const UNIT := 0.012
|
|
const SPECIES := ["human", "engi", "mantis", "rock", "slug", "energy", "crystal", "lanius"]
|
|
var species := "human"
|
|
var animation_state := "idle"
|
|
var animation_time := 0.0
|
|
var torso: Node3D
|
|
var head: Node3D
|
|
var arms: Array[Node3D] = []
|
|
var legs: Array[Node3D] = []
|
|
var tool: Node3D
|
|
var facing := 0.0
|
|
var viewer_facing_valid := false
|
|
|
|
|
|
func build(race: String) -> void:
|
|
for child in get_children():
|
|
remove_child(child)
|
|
child.queue_free()
|
|
arms.clear()
|
|
legs.clear()
|
|
species = canonical_species(race)
|
|
if not SPECIES.has(species):
|
|
species = "human"
|
|
torso = Node3D.new()
|
|
torso.name = "Torso"
|
|
add_child(torso)
|
|
head = Node3D.new()
|
|
head.name = "Head"
|
|
head.position = Vector3(0, 8.4, 0) * UNIT
|
|
torso.add_child(head)
|
|
var dark := Color("253744")
|
|
var light := Color("b9c8cc")
|
|
var eye := Color("a4f4ff")
|
|
var body_blocks: Array = []
|
|
var head_blocks: Array = []
|
|
var arm_blocks: Array = []
|
|
var leg_blocks: Array = []
|
|
match species:
|
|
"human":
|
|
body_blocks = [_b(0, 5.4, 0, 4.2, 4.8, 2.6, Color("467da6")), _b(0, 3.3, 0, 4.3, 0.7, 2.8, dark), _b(0, 6.3, -1.5, 1.4, 1.4, 0.5, Color("ffd777"))]
|
|
head_blocks = [_b(0, 0.8, 0, 3.2, 3.1, 3.0, Color("c89571")), _b(0, 2.2, 0.2, 3.4, 0.8, 3.2, Color("5a392c")), _b(-0.7, 0.9, -1.55, 0.55, 0.6, 0.2, dark), _b(0.7, 0.9, -1.55, 0.55, 0.6, 0.2, dark)]
|
|
arm_blocks = [_b(0, -1.5, 0, 1.4, 3.3, 1.7, Color("467da6")), _b(0, -3.25, -0.2, 1.3, 1.0, 1.4, Color("c89571"))]
|
|
leg_blocks = [_b(0, -1.5, 0, 1.6, 3.2, 1.8, dark), _b(0, -3.2, -0.4, 1.7, 0.9, 2.5, Color("16232e"))]
|
|
"engi":
|
|
body_blocks = [_b(0, 5.0, 0, 4.6, 3.8, 3.2, light), _b(0, 5.2, -1.8, 2.1, 2.2, 0.6, dark), _b(0, 5.2, -2.15, 1.2, 0.6, 0.2, eye), _b(0, 7.1, 0, 2.0, 1.0, 2.0, dark)]
|
|
head_blocks = [_b(0, 0.5, 0, 3.4, 2.6, 3.0, light), _b(0, 0.7, -1.6, 2.5, 1.1, 0.4, dark), _b(-0.65, 0.7, -1.85, 0.6, 0.6, 0.15, eye), _b(0.65, 0.7, -1.85, 0.6, 0.6, 0.15, eye)]
|
|
arm_blocks = [_b(0, -0.8, 0, 1.6, 1.6, 1.7, light), _b(0, -2, 0, 0.7, 1.1, 0.9, dark), _b(0, -3, -0.2, 1.8, 1.4, 1.7, light)]
|
|
leg_blocks = [_b(0, -0.7, 0, 1.4, 1.5, 1.8, light), _b(0, -1.9, 0, 0.7, 1.0, 1.0, dark), _b(0, -3, -0.5, 1.9, 1.4, 2.7, light)]
|
|
"mantis":
|
|
body_blocks = [_b(0, 5.2, 0, 2.7, 4.5, 2.4, Color("568e3b")), _b(0, 4.1, 1.2, 3.5, 2.3, 3.0, Color("759f45")), _b(0, 7, 0, 2.0, 1.4, 2.1, Color("375d29"))]
|
|
head_blocks = [_b(0, 0.5, -0.5, 3.7, 2.8, 2.7, Color("8fb654")), _b(-1.5, 0.8, -2.0, 0.9, 1.2, 0.7, Color("df5146")), _b(1.5, 0.8, -2.0, 0.9, 1.2, 0.7, Color("df5146")), _b(-1.2, 2.9, 0, 0.35, 2.2, 0.4, dark), _b(1.2, 2.9, 0, 0.35, 2.2, 0.4, dark), _b(-0.9, -1.0, -2.0, 0.5, 1.2, 1.2, Color("e7d09a")), _b(0.9, -1.0, -2.0, 0.5, 1.2, 1.2, Color("e7d09a"))]
|
|
arm_blocks = [_b(0, -1.3, 0, 0.8, 3.3, 1.0, Color("568e3b")), _b(0, -3.2, -0.7, 0.9, 2.3, 1.8, Color("bdd887")), _b(0, -4.5, -1.4, 0.7, 1.2, 0.7, Color("e7d09a"))]
|
|
leg_blocks = [_b(0, -1.4, 0.5, 0.9, 2.8, 1.1, Color("568e3b")), _b(0, -3.0, -0.7, 0.8, 1.4, 2.3, Color("759f45"))]
|
|
"rock":
|
|
body_blocks = [_b(0, 5.1, 0, 6.4, 4.8, 4.3, Color("8c7252")), _b(-2.4, 6.3, 0, 2.5, 3.1, 3.4, Color("ac9065")), _b(2.4, 5.8, 0, 2.3, 3.3, 3.8, Color("74624b")), _b(0.7, 3.4, -2.3, 2.3, 1.9, 0.7, Color("6a563f"))]
|
|
head_blocks = [_b(0, 0.4, 0, 4.6, 3.5, 3.7, Color("ac9065")), _b(-1.8, 1.4, 0.2, 1.2, 2.5, 2.7, Color("8c7252")), _b(0, 0.3, -2.0, 3.1, 0.7, 0.4, dark), _b(-0.8, 0.3, -2.25, 0.7, 0.35, 0.2, Color("ffe4a0")), _b(0.8, 0.3, -2.25, 0.7, 0.35, 0.2, Color("ffe4a0"))]
|
|
arm_blocks = [_b(0, -1.3, 0, 2.3, 3.7, 3.1, Color("8c7252")), _b(0, -3.2, -0.3, 2.6, 1.9, 3.0, Color("ac9065"))]
|
|
leg_blocks = [_b(0, -1.6, 0, 2.3, 3.3, 2.7, Color("74624b")), _b(0, -3.1, -0.5, 2.6, 1.1, 3.4, Color("8c7252"))]
|
|
"slug":
|
|
body_blocks = [_b(0, 3.2, 0.9, 5.4, 3.0, 6.5, Color("846aa8")), _b(0, 5.0, 0, 4.6, 3.8, 4.0, Color("a887c1")), _b(0, 2.0, 3.4, 3.3, 1.7, 3.6, Color("725793")), _b(0, 1.25, 1.0, 5.8, 0.7, 8.2, Color("58446e"))]
|
|
head_blocks = [_b(0, -0.5, 0, 4.5, 2.6, 3.8, Color("a887c1")), _b(-1.5, 1.6, -1.1, 0.7, 3.5, 0.7, Color("a887c1")), _b(1.5, 1.6, -1.1, 0.7, 3.5, 0.7, Color("a887c1")), _b(-1.5, 3.2, -1.2, 1.3, 1.0, 1.1, Color("e1e791")), _b(1.5, 3.2, -1.2, 1.3, 1.0, 1.1, Color("e1e791")), _b(-1.5, 3.2, -1.8, 0.5, 0.7, 0.2, dark), _b(1.5, 3.2, -1.8, 0.5, 0.7, 0.2, dark)]
|
|
arm_blocks = [_b(0, -1.5, -0.3, 1.0, 3.1, 1.4, Color("a887c1")), _b(0, -2.8, -1.1, 1.5, 1.0, 1.5, Color("846aa8"))]
|
|
"energy":
|
|
body_blocks = [_b(0, 5.2, 0, 3.5, 4.7, 2.4, Color("57c282")), _b(0, 5.5, -1.35, 1.0, 3.1, 0.4, Color("b4ffb0")), _b(-1.9, 6.4, 0, 0.6, 2.0, 2.6, Color("91e999")), _b(1.9, 6.4, 0, 0.6, 2.0, 2.6, Color("91e999"))]
|
|
head_blocks = [_b(0, 0.8, 0, 2.7, 3.5, 2.5, Color("8be891")), _b(0, 2.8, 0, 1.8, 1.0, 1.7, Color("b4ffb0")), _b(0, 0.9, -1.4, 1.7, 0.6, 0.3, Color("e6ffd1"))]
|
|
arm_blocks = [_b(0, -1.7, 0, 1.0, 3.7, 1.3, Color("8be891")), _b(0, -3.7, 0, 1.1, 0.6, 1.4, Color("b4ffb0"))]
|
|
leg_blocks = [_b(0, -1.7, 0, 1.1, 3.5, 1.6, Color("57c282")), _b(0, -3.4, -0.3, 1.3, 0.8, 2.1, Color("b4ffb0"))]
|
|
"crystal":
|
|
body_blocks = [_b(0, 5.1, 0, 4.7, 5.0, 3.5, Color("65a6bf")), _b(-1.9, 6.2, 0, 1.7, 4.4, 2.7, Color("b3e3ed")), _b(1.9, 5.4, -0.2, 1.7, 3.2, 3.2, Color("85c4d7")), _b(0, 5.4, -2.0, 1.6, 2.5, 0.7, Color("c9f5f8"))]
|
|
head_blocks = [_b(0, 0.6, 0, 3.5, 3.4, 3.1, Color("85c4d7")), _b(-0.9, 2.8, 0.2, 1.3, 2.0, 1.9, Color("c9f5f8")), _b(1.0, 2.1, 0.4, 1.4, 1.2, 2.2, Color("b3e3ed")), _b(0, 0.8, -1.7, 2.2, 0.6, 0.3, dark)]
|
|
arm_blocks = [_b(0, -1.6, 0, 1.8, 3.8, 2.3, Color("85c4d7")), _b(0, -3.6, -0.2, 1.9, 1.4, 2.4, Color("b3e3ed"))]
|
|
leg_blocks = [_b(0, -1.8, 0, 1.8, 3.7, 2.3, Color("65a6bf")), _b(0, -3.5, -0.5, 2.0, 1.0, 2.8, Color("85c4d7"))]
|
|
"lanius":
|
|
body_blocks = [_b(0, 5.2, 0, 3.4, 4.5, 2.3, Color("9eaaaf")), _b(-1.6, 6.6, 0, 1.3, 2.0, 2.4, Color("d0d8dc")), _b(1.6, 6.6, 0, 1.3, 2.0, 2.4, Color("d0d8dc")), _b(0, 5.0, -1.5, 0.7, 2.7, 0.8, Color("667f8c"))]
|
|
head_blocks = [_b(0, 0.9, 0, 2.7, 3.2, 2.7, Color("d0d8dc")), _b(-1.25, 2.8, 0.3, 0.6, 2.5, 0.7, Color("9eaaaf")), _b(1.25, 2.8, 0.3, 0.6, 2.5, 0.7, Color("9eaaaf")), _b(0, 0.5, -1.6, 1.9, 0.6, 0.4, Color("e7f788"))]
|
|
arm_blocks = [_b(0, -1.5, 0, 1.0, 3.5, 1.4, Color("9eaaaf")), _b(0, -3.4, -0.6, 0.6, 2.2, 1.5, Color("d0d8dc"))]
|
|
leg_blocks = [_b(0, -1.7, 0, 1.0, 3.7, 1.4, Color("9eaaaf")), _b(0, -3.5, -0.7, 0.9, 1.2, 2.9, Color("d0d8dc"))]
|
|
torso.add_child(Voxels.make(body_blocks))
|
|
head.add_child(Voxels.make(head_blocks))
|
|
for side in [-1.0, 1.0]:
|
|
var arm := Node3D.new()
|
|
arm.position = Vector3(side * (3.4 if species == "rock" else 2.5), 7.0, 0) * UNIT
|
|
arm.add_child(Voxels.make(arm_blocks))
|
|
torso.add_child(arm)
|
|
arms.append(arm)
|
|
if species != "slug":
|
|
var leg := Node3D.new()
|
|
leg.position = Vector3(side * (1.6 if species == "rock" else 1.0), 3.6, 0) * UNIT
|
|
leg.add_child(Voxels.make(leg_blocks))
|
|
torso.add_child(leg)
|
|
legs.append(leg)
|
|
tool = Voxels.make([_b(0, 0, 0, 0.8, 3.5, 0.8, light), _b(0, -1.5, 0, 2.2, 0.8, 1.1, Color("ffc967"))])
|
|
tool.position = Vector3(0, -3.6, -0.8) * UNIT
|
|
arms[1].add_child(tool)
|
|
tool.visible = false
|
|
animate(0.0)
|
|
|
|
|
|
func set_state(crew: Dictionary) -> void:
|
|
var previous_state := animation_state
|
|
if bool(crew.get("fighting", false)):
|
|
animation_state = "fight"
|
|
elif bool(crew.get("repairing", false)):
|
|
animation_state = "repair"
|
|
elif bool(crew.get("running", false)):
|
|
animation_state = "run"
|
|
elif bool(crew.get("working", false)):
|
|
animation_state = "work"
|
|
else:
|
|
animation_state = "idle"
|
|
tool.visible = animation_state == "repair"
|
|
if animation_state != previous_state:
|
|
# Native state changes must select the right pose even when an actor is
|
|
# first seen while paused. Preserve the frozen animation phase.
|
|
animate(0.0)
|
|
|
|
|
|
func face_viewer(deck_direction: Vector3, delta: float) -> void:
|
|
# Rotate around the deck normal only. Looking straight down at an actor
|
|
# must not tip its body or cause unstable yaw from a near-zero projection.
|
|
if animation_state == "run" or Vector2(deck_direction.x, deck_direction.z).length_squared() < 0.0004:
|
|
return
|
|
var target := atan2(-deck_direction.x, -deck_direction.z)
|
|
var difference := wrapf(target - facing, -PI, PI)
|
|
if not viewer_facing_valid:
|
|
facing = target
|
|
viewer_facing_valid = true
|
|
elif absf(difference) > 0.012:
|
|
facing = lerp_angle(facing, target, 1.0 - exp(-delta * 12.0))
|
|
rotation.y = facing
|
|
|
|
|
|
func face_movement(deck_direction: Vector3) -> void:
|
|
if animation_state != "run" or deck_direction.length_squared() < 0.00004:
|
|
return
|
|
facing = atan2(-deck_direction.x, -deck_direction.z)
|
|
rotation.y = facing
|
|
viewer_facing_valid = false
|
|
|
|
|
|
func animate(delta: float) -> void:
|
|
animation_time += delta
|
|
var t := animation_time
|
|
for part in arms + legs:
|
|
part.rotation = Vector3.ZERO
|
|
torso.position = Vector3.ZERO
|
|
torso.rotation = Vector3.ZERO
|
|
head.rotation = Vector3.ZERO
|
|
match animation_state:
|
|
"run":
|
|
for i in range(legs.size()):
|
|
legs[i].rotation.x = sin(t * 14.0 + i * PI) * 0.65
|
|
for i in range(arms.size()):
|
|
arms[i].rotation.x = sin(t * 14.0 + i * PI + PI) * 0.5
|
|
torso.position.y = absf(sin(t * 14.0)) * 0.004
|
|
torso.rotation.x = -0.12
|
|
if species == "slug":
|
|
torso.scale.z = 1.0 + sin(t * 9.0) * 0.08
|
|
"work":
|
|
arms[0].rotation.x = 0.9 + sin(t * 5.0) * 0.14
|
|
arms[1].rotation.x = 0.9 + sin(t * 5.0 + PI) * 0.14
|
|
head.rotation.x = -0.12
|
|
"repair":
|
|
arms[0].rotation.x = 0.8
|
|
arms[1].rotation.x = 1.0 + sin(t * 11.0) * 0.45
|
|
torso.rotation.x = -0.2
|
|
torso.position.y = -0.005
|
|
head.rotation.x = -0.18
|
|
"fight":
|
|
arms[0].rotation.x = 0.9 + sin(t * 9.0) * 0.65
|
|
arms[1].rotation.x = 0.9 + sin(t * 9.0 + PI) * 0.65
|
|
torso.rotation.y = sin(t * 9.0) * 0.12
|
|
torso.position.z = sin(t * 9.0) * 0.004
|
|
_:
|
|
head.rotation.y = sin(t * 1.4) * 0.06
|
|
torso.position.y = sin(t * 2.0) * 0.001
|
|
if animation_state != "run":
|
|
torso.scale.z = 1.0
|
|
rotation.y = facing
|
|
|
|
|
|
func _b(x: float, y: float, z: float, w: float, h: float, d: float, color: Color) -> Array:
|
|
return Voxels.block(Vector3(x, y, z) * UNIT, Vector3(w, h, d) * UNIT, color)
|
|
|
|
|
|
static func canonical_species(race: String) -> String:
|
|
return {"zoltan": "energy", "anaerobic": "lanius"}.get(race.to_lower(), race.to_lower())
|