Take from Drill8, and implement macros to match this sample usage. Also bring over the unit tests and upgrade them (e.x. aknowledging component/entity destruction)
"Some kind of movement animation that plays out over a certain desired interval"
@abstract_component AbstractManeuver {entitySingleton} {require: ContinuousPosition} begin
cosmetic_shake::CosmeticOffsetComponent
pos::ContinuousPosition
maneuver_duration::Float32 # seconds
progress::Float32 # 0 to 1
function CONSTRUCT(duration_seconds::Float32)
@bp_check(!has_component(entity, AbstractManeuver),
"Entity is already in the middle of a maneuver")
this.cosmetic_shake = add_component(entity, CosmeticOffset)
this.pos = get_component(entity, ContinuousPosition)
this.maneuver_duration = duration_seconds
this.progress = 0
end
function DESTRUCT(entity_is_dying::Bool) # Last arg not needed if not wanted
if !entity_is_dying
remove_component(entity, this.cosmetic_shake)
end
end
"Complete the animation (e.x. teleport to the final position)"
@promise finish()
"Report the current strength of the component's shaking"
@configurable function get_shake_strengths()::Vec{NShakeModes, Float32}
return zero(Vec{NShakeModes, Float32})
end
function TICK()
this.progress += world.delta_seconds / this.maneuver_duration
# Update shaking animation.
shake_data = zip(
this.get_shake_strengths(),
(
CAB_SHAKE_MODES[i](world.elapsed_seconds)
for i in 1:NShakeModes
)
)
this.cosmetic_shake.pos = sum(strength * state.pos for (strength, state) in shake_data)
this.cosmetic_shake.rot = let rot = fquat()
for (strength, state) in shake_data
rot >>= fquat(get_up_vector(), state.yaw * strength)
rot >>= fquat(get_horz_vector(2), state.pitch * strength)
rot >>= fquat(get_horz_vector(1), state.roll * strength)
end
rot
end
end
function POST_TICK()
# If the animation is finished, clean up.
if this.progress >= 1
this.finish()
remove_component(entity, this)
end
end
end
@component CabTurnComponent<:AbstractManeuver {require: Orientation} begin
target::fquat
entity_rot::Orientation
CONSTRUCT(target::fquat) = begin
this.target = target
this.entity_rot = get_component(entity, Orientation)
end
get_shake_strengths() = zero(Vec{NShakeModes, Float32})
finish() = (this.entity_rot.rot = this.target)
TICK() = begin
# Calculate the full turn needed to make it to the target.
forward::v3f = q_apply(this.entity_rot.rot, get_horz_vector(1))
desired_forward::v3f = q_apply(this.target, get_horz_vector(1))
full_turn = fquat(forward, desired_forward)
(turn_axis, turn_radians) = q_axisangle(full_turn)
# Constrain the turn based on the size of the time-step.
frame_max_rad = deg2rad(world.delta_seconds * TURN_SPEED_DEG_PER_SECOND)
turn_radians = copysign(min(frame_max_rad, abs(turn_radians)),
turn_radians)
this.entity_rot.rot >>= fquat(turn_axis, turn_radians)
end
end
function make_turn(entity::Entity, turn_move::TurnMove)
return add_component(entity, CabTurnComponent, turn_move.duration, turn_move.target)
end
Take from Drill8, and implement macros to match this sample usage. Also bring over the unit tests and upgrade them (e.x. aknowledging component/entity destruction)