Files
jak-project/goal_src/jak1/engine/game/effect-control.gc
T
2026-07-26 14:55:16 -04:00

595 lines
40 KiB
Common Lisp

;;-*-Lisp-*-
(in-package goal)
(bundles "ENGINE.CGO" "GAME.CGO")
(require "engine/common-obs/generic-obs-h.gc")
(require "engine/gfx/merc/merc-death.gc")
(require "engine/common-obs/process-drawable-h.gc")
;; DECOMP BEGINS
(define *footstep-surface* 448)
(define *debug-effect-control* #f)
(defun sound-name-with-material ((base-name symbol) (ground-surface pat-surface) (suffix string))
"Build a sound name from base, the collision material's sound-family name, and suffix. Materials
that share audio use the pcmetal, metal, or grass family, and waterbottom and deepsnow use the
shortened water and dpsnow names."
(let ((format-fn format)
(name-buffer (clear *temp-string*))
(name-template "~S-~S~S")
(material (-> ground-surface material)))
(format-fn name-buffer
name-template
base-name
(cond
((= material (pat-material sand)) "sand")
((= material (pat-material wood)) "wood")
((= material (pat-material crwood)) "crwood")
((or (= material (pat-material tube)) (= material (pat-material pcmetal))) "pcmetal")
((or (= material (pat-material metal)) (= material (pat-material rotate))) "metal")
((= material (pat-material snow)) "snow")
((= material (pat-material deepsnow)) "dpsnow")
((= material (pat-material gravel)) "gravel")
((= material (pat-material dirt)) "dirt")
((= material (pat-material stone)) "stone")
((= material (pat-material waterbottom)) "water")
((= material (pat-material tar)) "tar")
((= material (pat-material straw)) "straw")
((= material (pat-material ice)) "ice")
((= material (pat-material swamp)) "swamp")
((= material (pat-material neutral)) "neutral")
(else "grass"))
suffix))
(string->sound-name *temp-string*))
(defun effect-param->sound-spec ((specification sound-spec) (parameters (pointer float)) (value-count int))
"Apply opcode/value pairs to spec. Opcodes set or randomly add volume, pitch, and bend; set falloff
minimum, maximum, and curve; or set priority. value-count counts floats and is consumed two at a
time."
(while (> value-count 0)
(case (the int (-> parameters 0))
((3)
(logior! (-> specification mask) (sound-mask volume))
(set! (-> specification volume) (the int (* 10.24 (-> parameters 1)))))
((4)
(logior! (-> specification mask) (sound-mask volume))
(+! (-> specification volume) (the int (* 10.24 (* (-> parameters 1) (rand-vu))))))
((5)
(logior! (-> specification mask) (sound-mask pitch))
(set! (-> specification pitch-mod) (the int (* 1524.0 (-> parameters 1)))))
((6)
(logior! (-> specification mask) (sound-mask pitch))
(+! (-> specification pitch-mod) (the int (* 1524.0 (* (-> parameters 1) (rand-vu))))))
((9)
(logior! (-> specification mask) (sound-mask bend))
(set! (-> specification bend) (the int (* 327.66998 (-> parameters 1)))))
((10)
(logior! (-> specification mask) (sound-mask bend))
(+! (-> specification bend) (the int (* 327.66998 (* (-> parameters 1) (rand-vu))))))
((11)
(logior! (-> specification mask) (sound-mask fo-min))
(set! (-> specification fo-min) (the int (-> parameters 1))))
((12)
(logior! (-> specification mask) (sound-mask fo-max))
(set! (-> specification fo-max) (the int (-> parameters 1))))
((13)
(logior! (-> specification mask) (sound-mask fo-curve))
(set! (-> specification fo-curve) (the int (-> parameters 1))))
((19) (set! (-> specification priority) (the int (-> parameters 1)))))
(+! value-count -2)
(set! parameters (&-> parameters 2)))
specification)
;; Effect tags are authored in the animation's artist-frame domain rather than its internal frame
;; indices. The selected channel supplies the conversion, while root channel zero supplies the
;; frame-advance function that determines whether the interval moved, sought, wrapped, or held.
(defmethod update-effects ((this effect-control))
"Track the selected animation channel in artist-frame units and dispatch every
effect tag crossed since the previous update. Handle forward and backward seeking, loop wraps, and
held frames without firing a tag twice."
(let* ((skel-controller (-> this process skel))
(channel (if (< (-> this channel-offset) (-> skel-controller active-channels))
(-> skel-controller root-channel (-> this channel-offset))
(the-as joint-control-channel #f))))
(cond
((and channel (-> channel frame-group))
(let* ((frame-group (-> channel frame-group))
(artist-frame (+ (* (-> channel frame-num) (-> frame-group artist-step)) (-> frame-group artist-base))))
(let ((frame-function (-> skel-controller root-channel 0 num-func)))
(cond
((!= frame-group (-> this last-frame-group))
(set! (-> this res) (-> frame-group extra))
(let ((first-effect-tag-index (-> (lookup-tag-idx (-> frame-group extra) 'effect-name 'base -1000000000.0) lo)))
(set! (-> this name)
(if (>= (the-as int first-effect-tag-index) 0)
(&-> frame-group extra tag first-effect-tag-index)
(the-as (pointer res-tag) #f))))
(if (and (-> this name) (= (-> this name 0 key-frame) -1000000000.0)) (set! (-> this name) (&-> this name 1)))
(play-effects-from-res-lump this artist-frame artist-frame artist-frame))
((or (not (-> this name)) (= artist-frame (-> this last-frame-num))))
(else
(let ((previous-frame (-> this last-frame-num))
(current-frame artist-frame))
(cond
((= frame-function num-func-seek!)
(let ((seek-frame (+ (* (-> channel param 0) (-> frame-group artist-step)) (-> frame-group artist-base))))
(cond
((< current-frame previous-frame)
(if (>= previous-frame seek-frame) (play-effects-from-res-lump this current-frame previous-frame artist-frame)))
(else (if (>= seek-frame previous-frame) (play-effects-from-res-lump this previous-frame current-frame artist-frame))))))
((= frame-function num-func-loop!)
(cond
((>= (-> channel param 0) 0.0)
(cond
((< current-frame previous-frame)
(play-effects-from-res-lump this previous-frame 9999999.0 artist-frame)
(play-effects-from-res-lump this -100000000.0 current-frame 9999999.0))
(else (play-effects-from-res-lump this previous-frame current-frame artist-frame))))
((< previous-frame current-frame)
(play-effects-from-res-lump this current-frame 9999999.0 artist-frame)
(play-effects-from-res-lump this -100000000.0 previous-frame 9999999.0))
(else (play-effects-from-res-lump this current-frame previous-frame artist-frame))))
((= frame-function num-func-+!)
(if (>= (-> channel param 0) 0.0)
(play-effects-from-res-lump this previous-frame current-frame artist-frame)
(play-effects-from-res-lump this current-frame previous-frame artist-frame)))
((= frame-function num-func-identity) (play-effects-from-res-lump this artist-frame artist-frame artist-frame)))))))
(set! (-> this last-frame-group) frame-group)
(set! (-> this last-frame-num) artist-frame)))
(else (set! (-> this last-frame-group) #f))))
0
(none))
(defmethod play-effects-from-res-lump ((this effect-control) (lower-frame float) (upper-frame float) (exact-frame float))
"Dispatch each consecutive effect-name tag strictly between the
lower and upper frame bounds, plus a tag exactly equal to exact-frame."
;; note: this check was added. I believe in the original game name could be false, and then the
;; effect-name check below would fail. This did not cause a crash on original hardware because
;; misaligned 16-byte loads silently align. This causes a crash in opengoal, so we skip it manually.
(when (-> this name)
(let ((tag-cursor (-> this name)))
(while (= (-> tag-cursor 0 name) 'effect-name)
(let ((key-frame (-> tag-cursor 0 key-frame)))
(when (or (and (< key-frame upper-frame) (< lower-frame key-frame)) (= key-frame exact-frame))
(let* ((controller this)
(dispatch-effect (method-of-object controller do-effect))
(resource-lump (-> this res))
(tag-data (-> tag-cursor 0)))
(dispatch-effect controller
(the-as symbol (-> (the-as (pointer uint32) (&+ (-> resource-lump data-base) (-> tag-data data-offset)))))
key-frame
-1))))
(set! tag-cursor (&-> tag-cursor 1)))))
0
(none))
;; An effect-name symbol doubles as the dispatch key and, when bound, the effect object. The
;; effect- and group- prefixes provide two data-driven families; the remaining cases accept direct
;; particle launchers, particle groups, sound specifications, death effects, and ordinary sounds.
(defmethod do-effect ((this effect-control) (effect-name symbol) (frame float) (joint-index int))
"Dispatch one named animation effect. Give the owning process first chance to consume
it as an event, then resolve material effects, particle launchers or groups, sounds, camera shake,
and death effects. A negative joint selects the effect-joint property at frame."
(let ((effect-value (-> effect-name value))
(resolved-joint-index (cond
((< joint-index 0)
(let ((joint-property (get-property-value (-> this res)
'effect-joint
'exact
frame
(the-as uint128 0)
(the-as (pointer res-tag) #f)
*res-static-buf*)))
(if (zero? joint-property) 0 (the-as int (+ joint-property 1)))))
(else (empty) joint-index))))
(when (logtest? (-> this flags) (effect-control-flag event))
(if (send-event (-> this process) 'effect effect-name frame resolved-joint-index) (return (the-as object 0))))
(let ((effect-name-string (symbol->string effect-name)))
(cond
((and (= (-> effect-name-string data 0) 101)
(= (-> effect-name-string data 1) 102)
(= (-> effect-name-string data 2) 102)
(= (-> effect-name-string data 3) 101)
(= (-> effect-name-string data 4) 99)
(= (-> effect-name-string data 5) 116)
(= (-> effect-name-string data 6) 45))
(let* ((root-transform (-> this process root))
(moving-root (if (and (nonzero? root-transform) (type-type? (-> root-transform type) collide-shape-moving)) root-transform))
(ground-surface-value (if moving-root (the-as int (-> (the-as collide-shape-moving moving-root) ground-pat)) *footstep-surface*)))
(do-effect-for-surface this
effect-name
frame
resolved-joint-index
(-> this res)
(the-as pat-surface ground-surface-value))))
((let ((group-name-string (symbol->string effect-name)))
(and (= (-> group-name-string data 0) 103)
(= (-> group-name-string data 1) 114)
(= (-> group-name-string data 2) 111)
(= (-> group-name-string data 3) 117)
(= (-> group-name-string data 4) 112)
(= (-> group-name-string data 5) 45)))
(set! effect-value
(cond
((zero? effect-value)
(let ((launch-group-pointer (lookup-part-group-pointer-by-name (symbol->string effect-name))))
(when launch-group-pointer
(set! (-> effect-name value) launch-group-pointer)
(set! effect-value (-> launch-group-pointer 0))))
effect-value)
(else (-> (the-as (pointer sparticle-launch-group) effect-value) 0))))
(when (and (nonzero? effect-value) (= (-> (the-as sparticle-launch-group effect-value) type) sparticle-launch-group))
(if *debug-effect-control*
(format #t
"(~5D) effect group ~A ~A frame ~F joint ~D~%"
(current-time)
(-> this process name)
effect-name
frame
resolved-joint-index))
(process-spawn part-tracker
:init
part-tracker-init
(the-as sparticle-launch-group effect-value)
-1
(the-as symbol #f)
(the-as symbol #f)
(the-as symbol #f)
(vector<-cspace! (new 'stack-no-clear 'vector) (-> this process node-list data resolved-joint-index))
:to (-> this process))))
((= effect-name 'camera-shake) (activate! *camera-smush-control* 819.2 37 600 1.0 0.995))
((zero? effect-value)
(play-effect-sound this
effect-name
frame
resolved-joint-index
(-> this res)
(string->sound-name (symbol->string effect-name))))
((= (-> (the-as basic effect-value) type) sparticle-launcher)
(if *debug-effect-control*
(format #t
"(~5D) effect part ~A ~A frame ~F joint ~D~%"
(current-time)
(-> this process name)
effect-name
frame
resolved-joint-index))
(format #t
"-----> (~5D) effect part ~A ~A frame ~F joint ~D~%"
(current-time)
(-> this process name)
effect-name
frame
resolved-joint-index)
(launch-particles (the-as sparticle-launcher effect-value)
(vector<-cspace! (new 'stack-no-clear 'vector) (-> this process node-list data resolved-joint-index))))
((= (-> (the-as basic effect-value) type) sparticle-launch-group)
(if *debug-effect-control*
(format #t
"(~5D) effect group ~A ~A frame ~F joint ~D~%"
(current-time)
(-> this process name)
effect-name
frame
resolved-joint-index))
(process-spawn part-tracker
:init
part-tracker-init
effect-value
-1
(the-as symbol #f)
(the-as symbol #f)
(the-as symbol #f)
(vector<-cspace! (new 'stack-no-clear 'vector) (-> this process node-list data resolved-joint-index))
:to (-> this process)))
((= (-> (the-as basic effect-value) type) sound-spec)
(sound-play-by-spec (the-as sound-spec effect-value)
(new-sound-id)
(vector<-cspace! (new 'stack-no-clear 'vector) (-> this process node-list data resolved-joint-index))))
((= (-> (the-as basic effect-value) type) death-info)
(let ((draw-state (-> this process draw)))
(let ((vertex-skip (-> (the-as death-info effect-value) vertex-skip))
(death-timer (max 2 (the-as int (/ (-> (the-as death-info effect-value) timer) (the-as uint (the int (-> *display* time-factor))))))))
(when (= (-> *setting-control* current video-mode) 'pal)
(if (< (the-as uint 1) vertex-skip) (set! vertex-skip (/ (the-as uint (* (the-as uint 50) vertex-skip)) (the-as uint 60)))))
(let ((run-time (-> *display* frames (-> *display* last-screen) frame run-time)))
(cond
((< 9000 run-time) (set! vertex-skip (* vertex-skip 4)))
((< 7000 run-time) (set! vertex-skip (* vertex-skip 2)))))
(set! (-> draw-state death-vertex-skip) vertex-skip)
(set! (-> draw-state death-effect) (-> (the-as death-info effect-value) effect))
(set! (-> draw-state death-timer) (+ death-timer 1)))
(set! (-> draw-state death-timer-org) (-> draw-state death-timer))
(set! (-> draw-state death-draw-overlap) (-> (the-as death-info effect-value) overlap)))
(if (-> (the-as death-info effect-value) sound)
(play-effect-sound this
(-> (the-as death-info effect-value) sound)
frame
resolved-joint-index
(-> this res)
(string->sound-name (symbol->string (-> (the-as death-info effect-value) sound)))))
(send-event (-> this process) 'death-start (the-as death-info effect-value)))
(else
(play-effect-sound this
effect-name
frame
resolved-joint-index
(-> this res)
(string->sound-name (symbol->string effect-name)))))))
0)
;; Surface effects share authored event names but select their particle and sound assets from the
;; collision material. Materials without a specialized asset use the grass family.
(defmethod do-effect-for-surface ((this effect-control) (effect-name symbol) (frame float) (joint-index int) (resource-lump basic) (ground-surface pat-surface))
"Resolve a footstep, landing, slide, footprint, or droppings effect from
the supplied collision material, launch its material-specific particles when applicable, and play
its material-specific sound."
(let ((effect-sound (the-as sound-name #f)))
(let ((run-time (-> *display* frames (-> *display* last-screen) frame run-time)))
(case effect-name
(('effect-walk-step-left 'effect-run-step-left) (set! effect-sound (sound-name-with-material 'walk ground-surface "1")))
(('effect-walk-step-right 'effect-run-step-right) (set! effect-sound (sound-name-with-material 'walk ground-surface "2")))
(('effect-roll) (set! effect-sound (sound-name-with-material 'roll ground-surface "")))
(('effect-slide) (set! effect-sound (sound-name-with-material 'slide ground-surface "")))
(('effect-land) (set! effect-sound (sound-name-with-material 'land ground-surface "")))
(('effect-zoom-land) (set! effect-sound (sound-name-with-material 'zoom-land ground-surface "")))
(('effect-zoom-hit) (set! effect-sound (sound-name-with-material 'zoom-hit ground-surface "")))
(('effect-flut-land) (set! effect-sound (sound-name-with-material 'flut-land ground-surface "")))
(('effect-land-poof)
(when (< run-time 9000)
(let* ((land-poof-controller this)
(dispatch-land-poof (method-of-object land-poof-controller do-effect))
(land-poof-material (-> ground-surface material)))
(dispatch-land-poof land-poof-controller
(cond
((= land-poof-material (pat-material sand)) 'group-land-poof-sand)
((= land-poof-material (pat-material wood)) 'group-land-poof-wood)
((= land-poof-material (pat-material crwood)) 'group-land-poof-crwood)
((or (= land-poof-material (pat-material tube)) (= land-poof-material (pat-material pcmetal))) 'group-land-poof-pcmetal)
((or (= land-poof-material (pat-material metal)) (= land-poof-material (pat-material rotate))) 'group-land-poof-metal)
((= land-poof-material (pat-material ice)) 'group-land-poof-ice)
((= land-poof-material (pat-material snow)) 'group-land-poof-snow)
((= land-poof-material (pat-material deepsnow)) 'group-land-poof-dpsnow)
((= land-poof-material (pat-material gravel)) 'group-land-poof-gravel)
((= land-poof-material (pat-material dirt)) 'group-land-poof-dirt)
((= land-poof-material (pat-material stone)) 'group-land-poof-stone)
((= land-poof-material (pat-material waterbottom)) 'group-land-poof-water)
((= land-poof-material (pat-material tar)) 'group-land-poof-tar)
((= land-poof-material (pat-material straw)) 'group-land-poof-straw)
((= land-poof-material (pat-material swamp)) 'group-land-poof-swamp)
((= land-poof-material (pat-material neutral)) 'group-land-poof-neutral)
(else 'group-land-poof-grass))
frame
-1))))
(('effect-run-poof)
(when (< run-time 9000)
(let* ((run-poof-controller this)
(dispatch-run-poof (method-of-object run-poof-controller do-effect))
(run-poof-material (-> ground-surface material)))
(dispatch-run-poof run-poof-controller
(cond
((= run-poof-material (pat-material sand)) 'group-run-poof-sand)
((= run-poof-material (pat-material wood)) 'group-run-poof-wood)
((= run-poof-material (pat-material crwood)) 'group-run-poof-crwood)
((or (= run-poof-material (pat-material tube)) (= run-poof-material (pat-material pcmetal))) 'group-run-poof-pcmetal)
((or (= run-poof-material (pat-material metal)) (= run-poof-material (pat-material rotate))) 'group-run-poof-metal)
((= run-poof-material (pat-material ice)) 'group-run-poof-ice)
((= run-poof-material (pat-material snow)) 'group-run-poof-snow)
((= run-poof-material (pat-material deepsnow)) 'group-run-poof-dpsnow)
((= run-poof-material (pat-material gravel)) 'group-run-poof-gravel)
((= run-poof-material (pat-material dirt)) 'group-run-poof-dirt)
((= run-poof-material (pat-material stone)) 'group-run-poof-stone)
((= run-poof-material (pat-material waterbottom)) 'group-run-poof-water)
((= run-poof-material (pat-material tar)) 'group-run-poof-tar)
((= run-poof-material (pat-material straw)) 'group-run-poof-straw)
((= run-poof-material (pat-material swamp)) 'group-run-poof-swamp)
((= run-poof-material (pat-material neutral)) 'group-run-poof-neutral)
(else 'group-run-poof-grass))
frame
-1))))
(('effect-just-footprint)
(let* ((footprint-controller this)
(dispatch-footprint (method-of-object footprint-controller do-effect))
(footprint-material (-> ground-surface material)))
(dispatch-footprint footprint-controller
(cond
((= footprint-material (pat-material sand)) 'group-just-footprint-sand)
((= footprint-material (pat-material wood)) 'group-just-footprint-wood)
((= footprint-material (pat-material crwood)) 'group-just-footprint-crwood)
((or (= footprint-material (pat-material tube)) (= footprint-material (pat-material pcmetal)))
'group-just-footprint-pcmetal)
((or (= footprint-material (pat-material metal)) (= footprint-material (pat-material rotate)))
'group-just-footprint-metal)
((= footprint-material (pat-material ice)) 'group-just-footprint-ice)
((= footprint-material (pat-material snow)) 'group-just-footprint-snow)
((= footprint-material (pat-material deepsnow)) 'group-just-footprint-dpsnow)
((= footprint-material (pat-material gravel)) 'group-just-footprint-gravel)
((= footprint-material (pat-material dirt)) 'group-just-footprint-dirt)
((= footprint-material (pat-material stone)) 'group-just-footprint-stone)
((= footprint-material (pat-material waterbottom)) 'group-just-footprint-water)
((= footprint-material (pat-material tar)) 'group-just-footprint-tar)
((= footprint-material (pat-material straw)) 'group-just-footprint-straw)
((= footprint-material (pat-material swamp)) 'group-just-footprint-swamp)
((= footprint-material (pat-material neutral)) 'group-just-footprint-neutral)
(else 'group-just-footprint-grass))
frame
-1)))
(('effect-just-poof)
(when (< run-time 9000)
(let* ((just-poof-controller this)
(dispatch-just-poof (method-of-object just-poof-controller do-effect))
(just-poof-material (-> ground-surface material)))
(dispatch-just-poof just-poof-controller
(cond
((= just-poof-material (pat-material sand)) 'group-just-poof-sand)
((= just-poof-material (pat-material wood)) 'group-just-poof-wood)
((= just-poof-material (pat-material crwood)) 'group-just-poof-crwood)
((or (= just-poof-material (pat-material tube)) (= just-poof-material (pat-material pcmetal))) 'group-just-poof-pcmetal)
((or (= just-poof-material (pat-material metal)) (= just-poof-material (pat-material rotate))) 'group-just-poof-metal)
((= just-poof-material (pat-material ice)) 'group-just-poof-ice)
((= just-poof-material (pat-material snow)) 'group-just-poof-snow)
((= just-poof-material (pat-material deepsnow)) 'group-just-poof-dpsnow)
((= just-poof-material (pat-material gravel)) 'group-just-poof-gravel)
((= just-poof-material (pat-material dirt)) 'group-just-poof-dirt)
((= just-poof-material (pat-material stone)) 'group-just-poof-stone)
((= just-poof-material (pat-material waterbottom)) 'group-just-poof-water)
((= just-poof-material (pat-material tar)) 'group-just-poof-tar)
((= just-poof-material (pat-material straw)) 'group-just-poof-straw)
((= just-poof-material (pat-material swamp)) 'group-just-poof-swamp)
((= just-poof-material (pat-material neutral)) 'group-just-poof-neutral)
(else 'group-just-poof-grass))
frame
-1))))
(('effect-slide-poof)
(let* ((slide-poof-controller this)
(dispatch-slide-poof (method-of-object slide-poof-controller do-effect))
(slide-poof-material (-> ground-surface material)))
(dispatch-slide-poof slide-poof-controller
(cond
((= slide-poof-material (pat-material sand)) 'group-slide-poof-sand)
((= slide-poof-material (pat-material wood)) 'group-slide-poof-wood)
((= slide-poof-material (pat-material crwood)) 'group-slide-poof-crwood)
((or (= slide-poof-material (pat-material tube)) (= slide-poof-material (pat-material pcmetal)))
'group-slide-poof-pcmetal)
((or (= slide-poof-material (pat-material metal)) (= slide-poof-material (pat-material rotate))) 'group-slide-poof-metal)
((= slide-poof-material (pat-material ice)) 'group-slide-poof-ice)
((= slide-poof-material (pat-material snow)) 'group-slide-poof-snow)
((= slide-poof-material (pat-material deepsnow)) 'group-slide-poof-dpsnow)
((= slide-poof-material (pat-material gravel)) 'group-slide-poof-gravel)
((= slide-poof-material (pat-material dirt)) 'group-slide-poof-dirt)
((= slide-poof-material (pat-material stone)) 'group-slide-poof-stone)
((= slide-poof-material (pat-material waterbottom)) 'group-slide-poof-water)
((= slide-poof-material (pat-material tar)) 'group-slide-poof-tar)
((= slide-poof-material (pat-material straw)) 'group-slide-poof-straw)
((= slide-poof-material (pat-material swamp)) 'group-slide-poof-swamp)
((= slide-poof-material (pat-material neutral)) 'group-slide-poof-neutral)
(else 'group-slide-poof-grass))
frame
-1)))
(('effect-droppings)
(let* ((droppings-material (-> ground-surface material))
(droppings-particle (cond
((= droppings-material (pat-material sand)) (-> *part-id-table* 95))
((= droppings-material (pat-material wood)) (-> *part-id-table* 97))
((= droppings-material (pat-material crwood)) (-> *part-id-table* 99))
((or (= droppings-material (pat-material tube)) (= droppings-material (pat-material pcmetal)))
(-> *part-id-table* 2248))
((or (= droppings-material (pat-material metal)) (= droppings-material (pat-material rotate)))
(-> *part-id-table* 2334))
((= droppings-material (pat-material ice)) (-> *part-id-table* 2249))
((= droppings-material (pat-material snow)) (-> *part-id-table* 2250))
((= droppings-material (pat-material deepsnow)) (-> *part-id-table* 2251))
((= droppings-material (pat-material gravel)) (-> *part-id-table* 2252))
((= droppings-material (pat-material dirt)) (-> *part-id-table* 2253))
((= droppings-material (pat-material stone)) (-> *part-id-table* 98))
((= droppings-material (pat-material waterbottom)) (-> *part-id-table* 2254))
((= droppings-material (pat-material tar)) (-> *part-id-table* 2255))
((= droppings-material (pat-material straw)) (-> *part-id-table* 2256))
((= droppings-material (pat-material swamp)) (-> *part-id-table* 2257))
((= droppings-material (pat-material neutral)) (-> *part-id-table* 2773))
(else (-> *part-id-table* 96)))))
(if (nonzero? droppings-particle)
(launch-particles droppings-particle
(vector<-cspace! (new 'stack-no-clear 'vector) (-> this process node-list data joint-index))))))
(('effect-jump-droppings)
(let* ((jump-droppings-material (-> ground-surface material))
(jump-droppings-particle (cond
((= jump-droppings-material (pat-material sand)) (-> *part-id-table* 106))
((= jump-droppings-material (pat-material wood)) (-> *part-id-table* 2258))
((= jump-droppings-material (pat-material crwood)) (-> *part-id-table* 2259))
((or (= jump-droppings-material (pat-material tube)) (= jump-droppings-material (pat-material pcmetal)))
(-> *part-id-table* 2260))
((or (= jump-droppings-material (pat-material metal)) (= jump-droppings-material (pat-material rotate)))
(-> *part-id-table* 2335))
((= jump-droppings-material (pat-material ice)) (-> *part-id-table* 2261))
((= jump-droppings-material (pat-material snow)) (-> *part-id-table* 2262))
((= jump-droppings-material (pat-material deepsnow)) (-> *part-id-table* 2263))
((= jump-droppings-material (pat-material gravel)) (-> *part-id-table* 2264))
((= jump-droppings-material (pat-material dirt)) (-> *part-id-table* 2265))
((= jump-droppings-material (pat-material stone)) (-> *part-id-table* 2266))
((= jump-droppings-material (pat-material waterbottom)) (-> *part-id-table* 2267))
((= jump-droppings-material (pat-material tar)) (-> *part-id-table* 2268))
((= jump-droppings-material (pat-material straw)) (-> *part-id-table* 2269))
((= jump-droppings-material (pat-material swamp)) (-> *part-id-table* 2270))
((= jump-droppings-material (pat-material neutral)) (-> *part-id-table* 2774))
(else (-> *part-id-table* 107)))))
(if (nonzero? jump-droppings-particle)
(launch-particles jump-droppings-particle
(vector<-cspace! (new 'stack-no-clear 'vector) (-> this process node-list data joint-index))))))))
(if effect-sound (play-effect-sound this effect-name frame joint-index resource-lump effect-sound)))
0
(none))
;; Each sound starts at full volume with a small random bend. An exact-frame effect-param array may
;; replace or perturb that base specification before the falloff test and playback.
(defmethod play-effect-sound ((this effect-control) (effect-name symbol) (frame float) (joint-index int) (resource-lump basic) (effect-sound sound-name))
"Build a default sound specification, apply exact-frame effect-param pairs,
skip sounds beyond their falloff maximum, and play the result at the selected joint or without a
position when joint is negative."
(let ((lump resource-lump)
(sound-to-play effect-sound)
(specification (new 'stack 'sound-spec))
(joint-position (if (< joint-index 0)
(the-as vector #f)
(vector<-cspace! (new 'stack-no-clear 'vector) (-> this process node-list data joint-index)))))
(set! (-> specification sound-name) sound-to-play)
(logior! (-> specification mask) (sound-mask volume))
(set! (-> specification volume) 1024)
(logior! (-> specification mask) (sound-mask bend))
(set! (-> specification bend) (the int (* 327.66998 (rand-vu-float-range -100.0 100.0))))
(let* ((parameter-tag (new 'static 'res-tag))
(parameter-data ((method-of-type res-lump get-property-data)
(the-as res-lump lump)
'effect-param
'exact
frame
(the-as pointer #f)
(& parameter-tag)
*res-static-buf*)))
(if parameter-data
(effect-param->sound-spec specification
(the-as (pointer float) parameter-data)
(the-as int (-> parameter-tag elt-count)))))
(if (and (nonzero? (-> specification fo-max))
(< (* 4096.0 (the float (-> specification fo-max))) (vector-vector-distance (ear-trans) joint-position)))
(return 0))
(when *debug-effect-control*
(let ((sound-name-address sound-to-play))
(string<-charp (clear *temp-string*) (the-as (pointer uint8) (& sound-name-address))))
(format #t
"(~5D) effect sound ~A ~A (~S) frame ~F joint ~D "
(current-time)
(-> this process name)
effect-name
*temp-string*
frame
joint-index)
(format #t
"volume: ~f pitch-mod: ~f~%"
(* 0.09765625 (the float (-> specification volume)))
(* 0.000656168 (the float (-> specification pitch-mod)))))
(sound-play-by-spec specification (new-sound-id) joint-position))
0)
(defbehavior target-land-effect target ()
"Play landing effects for the target's current movement and surface. Flut launches its landing
poof and sound, racer plays an impact-scaled zoom sound, water uses the water landing effect, and
ordinary landings launch a poof and material sound."
(cond
((logtest? (-> self control root-prim prim-core action) (collide-action flut))
(do-effect (-> self skel effect) 'effect-land-poof -1.0 -1)
(do-effect (-> self skel effect) 'effect-flut-land -1.0 -1))
((logtest? (-> self control root-prim prim-core action) (collide-action racer))
(sound-play-by-name (sound-name-with-material 'zoom-land (-> self control ground-pat) "")
(new-sound-id)
(the int (* 10.24 (* 100.0 (the float (the int (* 10.24 (/ (-> self control ground-impact-vel) (meters 15))))))))
0
0
(sound-group sfx)
#t))
((logtest? (-> self water flag) (water-flag touch-water)) (do-effect (-> self skel effect) 'effect-land-water -1.0 -1))
(else (do-effect (-> self skel effect) 'effect-land-poof -1.0 -1) (do-effect (-> self skel effect) 'effect-land -1.0 -1)))
0
(none))