mirror of
https://github.com/open-goal/jak-project
synced 2026-08-21 23:00:45 -04:00
404 lines
22 KiB
Common Lisp
404 lines
22 KiB
Common Lisp
;;-*-Lisp-*-
|
|
(in-package goal)
|
|
(bundles "ENGINE.CGO" "GAME.CGO")
|
|
(require "engine/entity/res.gc")
|
|
(require "engine/util/smush-control-h.gc")
|
|
(require "engine/util/sync-info-h.gc")
|
|
|
|
;; DECOMP BEGINS
|
|
|
|
(defmethod setup-params! ((this sync-info) (period uint) (phase float) (ease-out float) (ease-in float))
|
|
"Set the period in 300 Hz game-time ticks and the initial phase as a fraction of that period.
|
|
The base clock ignores ease-out and ease-in, which are retained for the shared subtype interface."
|
|
(set! (-> this period) period)
|
|
(let* ((period-float (the float period))
|
|
(phase-ticks (* phase period-float)))
|
|
;; This is fmod for the phase values used here.
|
|
(set! (-> this offset) (- phase-ticks (* (the float (the int (/ phase-ticks period-float))) period-float))))
|
|
0
|
|
(none))
|
|
|
|
(defmethod setup-params! ((this sync-info-eased) (period uint) (phase float) (ease-out float) (ease-in float))
|
|
"Set the period and initial phase, then build a normalized quadratic-linear-quadratic easing
|
|
curve for each leg of the mirrored phase. ease-out is the accelerating fraction at the start and
|
|
ease-in is the decelerating fraction at the end; both are clamped, and ease-out is shortened if
|
|
they overlap."
|
|
(set! (-> this period) period)
|
|
(let* ((period-float (the float period))
|
|
(phase-ticks (* phase period-float)))
|
|
(set! (-> this offset) (- phase-ticks (* (the float (the int (/ phase-ticks period-float))) period-float))))
|
|
(if (< ease-out 0.0) (set! ease-out 0.0))
|
|
(if (< 1.0 ease-out) (set! ease-out 1.0))
|
|
(if (< ease-in 0.001) (set! ease-in 0.001))
|
|
(if (< 1.0 ease-in) (set! ease-in 1.0))
|
|
(let ((total-easing-phase (+ ease-out ease-in)))
|
|
(when (< 1.0 total-easing-phase)
|
|
(set! total-easing-phase 1.0)
|
|
(set! ease-out (- 1.0 ease-in)))
|
|
(let* ((total-normal-phase (- 1.0 total-easing-phase))
|
|
(tlo ease-out)
|
|
(thi (+ ease-out total-normal-phase))
|
|
(ylo (square tlo))
|
|
(y-at-thi (+ (* 2.0 tlo (- thi tlo)) ylo))
|
|
(m2 (/ tlo (- 1.0 thi)))
|
|
(y-end (+ (* (- 1.0 thi) (- 1.0 thi) m2) y-at-thi)))
|
|
(set! (-> this tlo) tlo)
|
|
(set! (-> this thi) thi)
|
|
(set! (-> this ylo) ylo)
|
|
(set! (-> this m2) m2)
|
|
(set! (-> this yend) y-end)))
|
|
0
|
|
(none))
|
|
|
|
(defmethod setup-params! ((this sync-info-paused) (period uint) (phase float) (pause-after-out float) (pause-after-in float))
|
|
"Set the period and initial phase plus endpoint pauses measured as fractions of the complete
|
|
cycle. pause-after-out holds the value at one and pause-after-in holds it at zero; both are clamped
|
|
and pause-after-in is shortened if the pauses would overlap."
|
|
(set! (-> this period) period)
|
|
(let* ((period-float (the float period))
|
|
(phase-ticks (* phase period-float)))
|
|
(set! (-> this offset) (- phase-ticks (* (the float (the int (/ phase-ticks period-float))) period-float))))
|
|
(cond
|
|
((< pause-after-out 0.0) (set! pause-after-out 0.0))
|
|
((< 1.0 pause-after-out) (set! pause-after-out 1.0)))
|
|
(cond
|
|
((< pause-after-in 0.0) (set! pause-after-in 0.0))
|
|
;; The two endpoint pauses may not overlap.
|
|
((< (- 1.0 pause-after-out) pause-after-in) (set! pause-after-in (- 1.0 pause-after-out))))
|
|
(set! (-> this pause-after-in) pause-after-in)
|
|
(set! (-> this pause-after-out) pause-after-out)
|
|
0
|
|
(none))
|
|
|
|
(defmethod load-params! ((this sync-info) (proc process) (default-period uint) (default-phase float) (default-ease-out float) (default-ease-in float))
|
|
"Load the sync resource from proc. Its first two floats are the period in seconds and initial
|
|
phase; the period is converted to 300 Hz game-time ticks. Return true when the resource exists,
|
|
otherwise install the supplied defaults and return false. The base clock ignores default-ease-out
|
|
and default-ease-in."
|
|
(local-vars (sync-tag res-tag))
|
|
(set! sync-tag (new 'static 'res-tag))
|
|
(let ((params (res-lump-data (-> proc entity) 'sync pointer :tag-ptr (& sync-tag))))
|
|
(cond
|
|
(params
|
|
(setup-params! this
|
|
(the-as uint (the int (* 300.0 (-> (the-as (pointer float) params) 0))))
|
|
(-> (the-as (pointer float) params) 1)
|
|
0.15
|
|
0.15)
|
|
#t)
|
|
(else (setup-params! this default-period default-phase 0.15 0.15) #f))))
|
|
|
|
(defmethod load-params! ((this sync-info-eased) (proc process) (default-period uint) (default-phase float) (default-ease-out float) (default-ease-in float))
|
|
"Load the sync resource from proc. Four floats supply period seconds, initial phase,
|
|
ease-out, and ease-in; a two-value resource uses the supplied easing defaults. Return true when the
|
|
resource exists, otherwise install every supplied default and return false."
|
|
(local-vars (sync-tag res-tag))
|
|
(set! sync-tag (new 'static 'res-tag))
|
|
(let ((params (res-lump-data (-> proc entity) 'sync pointer :tag-ptr (& sync-tag))))
|
|
(cond
|
|
(params
|
|
(if (>= (-> sync-tag elt-count) (the-as uint 4))
|
|
(setup-params! this
|
|
(the-as uint (the int (* 300.0 (-> (the-as (pointer float) params) 0))))
|
|
(-> (the-as (pointer float) params) 1)
|
|
(-> (the-as (pointer float) params) 2)
|
|
(-> (the-as (pointer float) params) 3))
|
|
(setup-params! this
|
|
(the-as uint (the int (* 300.0 (-> (the-as (pointer float) params) 0))))
|
|
(-> (the-as (pointer float) params) 1)
|
|
default-ease-out
|
|
default-ease-in))
|
|
#t)
|
|
(else (setup-params! this default-period default-phase default-ease-out default-ease-in) #f))))
|
|
|
|
(defmethod load-params! ((this sync-info-paused) (proc process) (default-period uint) (default-phase float) (default-pause-after-out float) (default-pause-after-in float))
|
|
"Load the sync resource from proc. Four floats supply period seconds, initial phase,
|
|
pause-after-out, and pause-after-in; a two-value resource uses the supplied pause defaults. Return
|
|
true when the resource exists, otherwise install every supplied default and return false."
|
|
(local-vars (sync-tag res-tag))
|
|
(set! sync-tag (new 'static 'res-tag))
|
|
(let ((params (res-lump-data (-> proc entity) 'sync pointer :tag-ptr (& sync-tag))))
|
|
(cond
|
|
(params
|
|
(if (>= (-> sync-tag elt-count) (the-as uint 4))
|
|
(setup-params! this
|
|
(the-as uint (the int (* 300.0 (-> (the-as (pointer float) params) 0))))
|
|
(-> (the-as (pointer float) params) 1)
|
|
(-> (the-as (pointer float) params) 2)
|
|
(-> (the-as (pointer float) params) 3))
|
|
(setup-params! this
|
|
(the-as uint (the int (* 300.0 (-> (the-as (pointer float) params) 0))))
|
|
(-> (the-as (pointer float) params) 1)
|
|
default-pause-after-out
|
|
default-pause-after-in))
|
|
#t)
|
|
(else (setup-params! this default-period default-phase default-pause-after-out default-pause-after-in) #f))))
|
|
|
|
(defmethod get-current-phase-no-mod ((this sync-info))
|
|
"Return the base clock's wrapped phase from 0 through 1 without applying a subtype's easing
|
|
or endpoint pauses."
|
|
(let* ((period (-> this period))
|
|
(period-float (the float period))
|
|
(current-time (+ (the float (mod (the-as uint (current-time)) period)) (-> this offset))))
|
|
(/ (- current-time (* (the float (the int (/ current-time period-float))) period-float)) period-float)))
|
|
|
|
(defmethod get-phase-offset ((this sync-info))
|
|
"Return the stored tick offset as a fraction of the period."
|
|
(/ (-> this offset) (the float (-> this period))))
|
|
|
|
(defmethod sync-now! ((this sync-info) (phase-now float))
|
|
"Adjust the stored tick offset so the current normalized phase equals phase-now, then return
|
|
the wrapped offset."
|
|
(let* ((period (-> this period))
|
|
(period-float (the float period))
|
|
;; phase-now is normally in [0, 1], so this period-sized wrap leaves it unchanged.
|
|
(wrapped-phase-now (- phase-now (* (the float (the int (/ phase-now period-float))) period-float)))
|
|
(current-time (+ (the float (mod (the-as uint (current-time)) period)) (-> this offset)))
|
|
(current-phase (/ (- current-time (* (the float (the int (/ current-time period-float))) period-float)) period-float))
|
|
(new-offset (+ (* (- wrapped-phase-now current-phase) period-float) period-float (-> this offset))))
|
|
(set! (-> this offset) (- new-offset (* (the float (the int (/ new-offset period-float))) period-float)))))
|
|
|
|
(defmethod get-current-phase ((this sync-info))
|
|
"Return the current sawtooth phase from 0 through 1."
|
|
(let* ((period (-> this period))
|
|
(period-float (the float period))
|
|
(current-time (+ (the float (mod (the-as uint (current-time)) period)) (-> this offset))))
|
|
(/ (- current-time (* (the float (the int (/ current-time period-float))) period-float)) period-float)))
|
|
|
|
(defmethod get-current-phase ((this sync-info-paused))
|
|
"Return a zero-to-one sawtooth that reaches one early, then holds there for the configured
|
|
pause-after-out fraction of the cycle. This non-mirrored form does not use pause-after-in."
|
|
(let* ((period (-> this period))
|
|
(period-float (the float period))
|
|
(max-phase 1.0)
|
|
(current-time (+ (the float (mod (the-as uint (current-time)) period)) (-> this offset))))
|
|
(fmin max-phase
|
|
(/ (- current-time (* (the float (the int (/ current-time period-float))) period-float))
|
|
(* period-float (- 1.0 (-> this pause-after-out)))))))
|
|
|
|
(defmethod get-current-value ((this sync-info) (max-value float))
|
|
"Return the current sawtooth phase multiplied by max-value."
|
|
(let* ((period (-> this period))
|
|
(period-float (the float period))
|
|
(current-time (+ (the float (mod (the-as uint (current-time)) period)) (-> this offset))))
|
|
(* (/ (- current-time (* (the float (the int (/ current-time period-float))) period-float)) period-float) max-value)))
|
|
|
|
(defmethod get-current-value ((this sync-info-paused) (max-value float))
|
|
"Return the paused non-mirrored phase multiplied by max-value."
|
|
(* (get-current-phase this) max-value))
|
|
|
|
(defmethod get-current-phase-with-mirror ((this sync-info))
|
|
"Return a triangular phase that moves from zero to one and back to zero during each period."
|
|
(let* ((period (-> this period))
|
|
(period-float (the float period))
|
|
(mirror-scale 2.0)
|
|
(current-time (+ (the float (mod (the-as uint (current-time)) period)) (-> this offset)))
|
|
(mirrored-phase (* mirror-scale (/ (- current-time (* (the float (the int (/ current-time period-float))) period-float)) period-float))))
|
|
(if (>= mirrored-phase 1.0) (set! mirrored-phase (- 2.0 mirrored-phase)))
|
|
mirrored-phase))
|
|
|
|
(defmethod get-current-phase-with-mirror ((this sync-info-eased))
|
|
"Return the mirrored zero-to-one-to-zero phase after applying the stored
|
|
quadratic-linear-quadratic easing curve independently to each leg."
|
|
(let* ((period (-> this period))
|
|
(period-float (the float period))
|
|
(mirror-scale 2.0)
|
|
(current-time (+ (the float (mod (the-as uint (current-time)) period)) (-> this offset)))
|
|
(leg-phase (* mirror-scale (/ (- current-time (* (the float (the int (/ current-time period-float))) period-float)) period-float)))
|
|
(returning? #f))
|
|
(when (>= leg-phase 1.0)
|
|
(set! returning? #t)
|
|
(set! leg-phase (+ -1.0 leg-phase)))
|
|
(let* ((tlo (-> this tlo))
|
|
(phase (/ (cond
|
|
((< leg-phase tlo)
|
|
;; Accelerate quadratically from zero slope.
|
|
(square leg-phase))
|
|
((< leg-phase (-> this thi))
|
|
;; Continue at the tangent reached at tlo.
|
|
(+ (* 2.0 tlo (- leg-phase tlo)) (-> this ylo)))
|
|
(else
|
|
(let ((remaining-phase (- 1.0 leg-phase)))
|
|
;; Decelerate quadratically to zero slope at the endpoint.
|
|
(- (-> this yend) (* (square remaining-phase) (-> this m2))))))
|
|
(-> this yend))))
|
|
(if returning? (set! phase (- 1.0 phase)))
|
|
phase)))
|
|
|
|
(defmethod get-current-phase-with-mirror ((this sync-info-paused))
|
|
"Return a zero-to-one-to-zero phase whose moving legs are shortened to leave the configured
|
|
holds at one and zero."
|
|
(let* ((period (-> this period))
|
|
(period-float (the float period))
|
|
(mirror-scale 2.0)
|
|
(current-time (+ (the float (mod (the-as uint (current-time)) period)) (-> this offset)))
|
|
(mirrored-phase (* mirror-scale (/ (- current-time (* (the float (the int (/ current-time period-float))) period-float)) period-float)))
|
|
(return-duration (- 1.0 (* 2.0 (-> this pause-after-in))))
|
|
(outward-duration (- 1.0 (* 2.0 (-> this pause-after-out)))))
|
|
(cond
|
|
((>= mirrored-phase (+ 1.0 return-duration)) 0.0)
|
|
((< 1.0 mirrored-phase) (- 1.0 (/ (+ -1.0 mirrored-phase) return-duration)))
|
|
((>= mirrored-phase outward-duration) 1.0)
|
|
(else (/ mirrored-phase outward-duration)))))
|
|
|
|
(defmethod get-current-value-with-mirror ((this sync-info) (max-value float))
|
|
"Return a triangular value that moves from zero to max-value and back to zero during each
|
|
period."
|
|
(let* ((period (-> this period))
|
|
(period-float (the float period))
|
|
(mirror-scale 2.0)
|
|
(current-time (+ (the float (mod (the-as uint (current-time)) period)) (-> this offset)))
|
|
(mirrored-phase (* mirror-scale (/ (- current-time (* (the float (the int (/ current-time period-float))) period-float)) period-float))))
|
|
(if (>= mirrored-phase 1.0) (set! mirrored-phase (- 2.0 mirrored-phase)))
|
|
(* mirrored-phase max-value)))
|
|
|
|
(defmethod get-current-value-with-mirror ((this sync-info-eased) (max-value float))
|
|
"Return the eased mirrored phase multiplied by max-value."
|
|
(* (get-current-phase-with-mirror this) max-value))
|
|
|
|
(defmethod get-current-value-with-mirror ((this sync-info-paused) (max-value float))
|
|
"Return the paused mirrored phase multiplied by max-value."
|
|
(* (get-current-phase-with-mirror this) max-value))
|
|
|
|
(defmethod set-params! ((this delayed-rand-float) (min-delay int) (max-delay int) (value-range float))
|
|
"Initialize the value to zero. Each later change waits a random number of game-time ticks
|
|
between min-delay and max-delay and chooses a value from the symmetric interval whose total width
|
|
is value-range."
|
|
(set! (-> this min-time) min-delay)
|
|
(set! (-> this max-time) max-delay)
|
|
(set! (-> this max-val) (/ value-range 2))
|
|
(set! (-> this start-time) 0)
|
|
(set! (-> this timer) 0)
|
|
(set! (-> this value) 0.0)
|
|
(-> this value))
|
|
|
|
(defmethod update! ((this delayed-rand-float))
|
|
"Choose a new delay and uniformly distributed value when the current delay expires, then
|
|
return the held value."
|
|
(when (time-elapsed? (-> this start-time) (-> this timer))
|
|
(set-time! (-> this start-time))
|
|
(set! (-> this timer) (rand-vu-int-range (-> this min-time) (-> this max-time)))
|
|
(set! (-> this value) (rand-vu-float-range (- (-> this max-val)) (-> this max-val))))
|
|
(-> this value))
|
|
|
|
(defmethod set-params! ((this oscillating-float) (initial-value float) (accel float) (max-vel float) (damping float))
|
|
"Initialize value and target to initial-value, clear velocity, and set the spring gain,
|
|
velocity limit, and retained damping fraction. A damping value of zero prevents movement, while
|
|
one applies no damping and permits continued oscillation."
|
|
(set! (-> this value) initial-value)
|
|
(set! (-> this target) initial-value)
|
|
(set! (-> this vel) 0.0)
|
|
(set! (-> this max-vel) max-vel)
|
|
(set! (-> this damping) damping)
|
|
(set! (-> this accel) accel)
|
|
(-> this value))
|
|
|
|
(defmethod update! ((this oscillating-float) (target-offset float))
|
|
"Advance the damped spring toward target plus target-offset. Acceleration is scaled by frame
|
|
time, velocity is clamped before damping, and the final integration is also frame-time scaled."
|
|
(let ((acceleration (* (- (+ (-> this target) target-offset) (-> this value)) (* (-> this accel) (-> *display* time-adjust-ratio)))))
|
|
(+! (-> this vel) acceleration))
|
|
(set! (-> this vel) (fmin (-> this max-vel) (fmax (- (-> this max-vel)) (-> this vel))))
|
|
(set! (-> this vel) (* (-> this vel) (-> this damping)))
|
|
(+! (-> this value) (* (-> this vel) (-> *display* time-adjust-ratio)))
|
|
(-> this value))
|
|
|
|
(defmethod set-params! ((this bouncing-float) (initial-value float) (max-value float) (min-value float) (elasticity float) (accel float) (max-vel float) (damping float))
|
|
"Initialize a damped spring constrained between min-value and max-value. elasticity controls
|
|
the fraction of outward velocity retained, with its direction reversed, when an endpoint is hit;
|
|
max-vel clamps the spring velocity before that collision response."
|
|
(set-params! (-> this osc) initial-value accel max-vel damping)
|
|
(set! (-> this max-value) max-value)
|
|
(set! (-> this min-value) min-value)
|
|
(set! (-> this elasticity) elasticity)
|
|
(set! (-> this state) (bouncing-float-state free))
|
|
(-> this osc value))
|
|
|
|
(defmethod update! ((this bouncing-float) (target-offset float))
|
|
"Advance the spring toward target plus target-offset, clamp any endpoint crossing, reflect
|
|
outward velocity by elasticity, record which endpoint was hit, and return the resulting value."
|
|
(update! (-> this osc) target-offset)
|
|
(set! (-> this state) (bouncing-float-state free))
|
|
(when (>= (-> this osc value) (-> this max-value))
|
|
(set! (-> this osc value) (-> this max-value))
|
|
(if (< 0.0 (-> this osc vel)) (set! (-> this osc vel) (* (-> this osc vel) (- (-> this elasticity)))))
|
|
(set! (-> this state) (bouncing-float-state at-maximum)))
|
|
(when (>= (-> this min-value) (-> this osc value))
|
|
(set! (-> this osc value) (-> this min-value))
|
|
(if (< (-> this osc vel) 0.0) (set! (-> this osc vel) (* (-> this osc vel) (- (-> this elasticity)))))
|
|
(set! (-> this state) (bouncing-float-state at-minimum)))
|
|
(-> this osc value))
|
|
|
|
(defmethod at-min? ((this bouncing-float))
|
|
"Return true when the latest update hit the minimum endpoint."
|
|
(= (-> this state) (bouncing-float-state at-minimum)))
|
|
|
|
(defmethod at-max? ((this bouncing-float))
|
|
"Return true when the latest update hit the maximum endpoint."
|
|
(= (-> this state) (bouncing-float-state at-maximum)))
|
|
|
|
(defmethod set-params! ((this delayed-rand-vector) (min-delay int) (max-delay int) (xz-range float) (y-range float))
|
|
"Initialize the vector to zero. Each later change waits a random number of game-time ticks
|
|
between min-delay and max-delay; X and Z use the symmetric xz-range and Y uses y-range."
|
|
(set! (-> this min-time) min-delay)
|
|
(set! (-> this max-time) max-delay)
|
|
(set! (-> this xz-max) (/ xz-range 2))
|
|
(set! (-> this y-max) (/ y-range 2))
|
|
(set! (-> this start-time) 0)
|
|
(set! (-> this timer) 0)
|
|
(vector-reset! (-> this value))
|
|
(-> this value))
|
|
|
|
(defmethod update-now! ((this delayed-rand-vector))
|
|
"Choose a new delay and uniformly distributed X, Y, and Z components immediately, then return
|
|
the stored vector."
|
|
(set-time! (-> this start-time))
|
|
(set! (-> this timer) (rand-vu-int-range (-> this min-time) (-> this max-time)))
|
|
(set! (-> this value x) (rand-vu-float-range (- (-> this xz-max)) (-> this xz-max)))
|
|
(set! (-> this value y) (rand-vu-float-range (- (-> this y-max)) (-> this y-max)))
|
|
(set! (-> this value z) (rand-vu-float-range (- (-> this xz-max)) (-> this xz-max)))
|
|
(-> this value))
|
|
|
|
(defmethod update-with-delay! ((this delayed-rand-vector))
|
|
"Choose a new random vector if the current delay has expired; otherwise retain the previous
|
|
value."
|
|
(if (time-elapsed? (-> this start-time) (-> this timer)) (update-now! this))
|
|
(-> this value))
|
|
|
|
(defmethod update-with-delay-or-reset! ((this delayed-rand-vector))
|
|
"Choose a new random vector if the current delay has expired; otherwise reset the stored
|
|
value to zero."
|
|
(if (time-elapsed? (-> this start-time) (-> this timer)) (update-now! this) (vector-reset! (-> this value)))
|
|
(-> this value))
|
|
|
|
(defmethod set-params! ((this oscillating-vector) (initial-value vector) (accel float) (max-vel float) (damping float))
|
|
"Initialize value and target from initial-value, or reset both when it is false; clear
|
|
velocity and set the spring gain, speed limit, and retained damping fraction. A damping value of
|
|
zero prevents movement, while one applies no damping and permits continued oscillation."
|
|
(cond
|
|
(initial-value (vector-copy! (-> this value) initial-value) (vector-copy! (-> this target) initial-value))
|
|
(else (vector-reset! (-> this value)) (vector-reset! (-> this target))))
|
|
(vector-reset! (-> this vel))
|
|
(set! (-> this max-vel) max-vel)
|
|
(set! (-> this damping) damping)
|
|
(set! (-> this accel) accel)
|
|
(-> this value))
|
|
|
|
(defmethod update! ((this oscillating-vector) (target-offset vector))
|
|
"Advance the damped vector spring toward target plus an optional target-offset. Limit the
|
|
velocity by magnitude so its direction is preserved, then apply damping and frame-time integration."
|
|
(let ((acceleration (new 'stack-no-clear 'vector)))
|
|
(cond
|
|
(target-offset
|
|
(vector+! acceleration (-> this target) target-offset)
|
|
(vector-! acceleration acceleration (-> this value)))
|
|
(else (vector-! acceleration (-> this target) (-> this value))))
|
|
(vector-float*! acceleration acceleration (* (-> this accel) (-> *display* time-adjust-ratio)))
|
|
(vector+! (-> this vel) (-> this vel) acceleration)
|
|
(let ((speed (vector-length (-> this vel))))
|
|
(if (< (-> this max-vel) speed) (vector-float*! (-> this vel) (-> this vel) (/ (-> this max-vel) speed))))
|
|
(vector-float*! (-> this vel) (-> this vel) (-> this damping))
|
|
(vector-float*! acceleration (-> this vel) (-> *display* time-adjust-ratio))
|
|
(vector+! (-> this value) (-> this value) acceleration))
|
|
(-> this value))
|