jak-project/goal_src/jak2/levels/forest/pegasus.gc
ManDude 7a8aa71204
[jak2] implement statistics tracker (#3288)
Currently only tracks enemy kills, and how they were killed. There is
currently no menu for this, but I've already added most of the text for
it. Also did a bunch of misc decompilation fixes and renamed some
methods.

Fixes #3277 
Fixes #3278
2024-01-11 22:49:41 +00:00

1417 lines
51 KiB
Common Lisp

;;-*-Lisp-*-
(in-package goal)
;; name: pegasus.gc
;; name in dgo: pegasus
;; dgos: FOR
;; DECOMP BEGINS
(deftype pegasus-path-info (structure)
((num-data int32)
(path-data curve-control)
(control-data (pointer float))
)
:allow-misaligned
)
(deftype pegasus (enemy)
((curve-position float)
(speed float)
(facing vector :inline)
(tangent vector :inline)
(run-blend-interp float)
(near-timer int32)
(far-time time-frame)
(y-offset float)
(y-offset-desired float)
(y-vel float)
(water-height float)
(timeout uint64)
(ambient-possible uint64)
(ambient-expire uint64)
(can-run symbol)
(on-ground symbol)
(over-ground symbol)
(allow-idle symbol)
(path-info pegasus-path-info 20 :inline)
(previous-path int32 :offset 968)
(current-path int32 :offset 972)
(num-paths int32 :offset 976)
(display-path int32 :offset 980)
(targetted-timer time-frame :offset 984)
)
(:methods
(run-logic? (_type_) symbol :replace)
(damage-amount-from-attack (_type_ process event-message-block) int :replace)
(update-target-awareness! (_type_ process-focusable enemy-best-focus) enemy-aware :replace)
(track-how-long-aimed-at! (_type_) none)
)
(:states
pegasus-debug
pegasus-tired
)
)
(defskelgroup skel-pegasus pegasus pegasus-lod0-jg pegasus-idle-ja
((pegasus-lod0-mg (meters 20)) (pegasus-lod1-mg (meters 40)) (pegasus-lod2-mg (meters 999999)))
:bounds (static-spherem 0 0 0 4)
:shadow pegasus-shadow-mg
)
(define *pegasus-enemy-info*
(new 'static 'enemy-info
:use-die-falling #f
:use-victory #f
:use-jump-blocked #f
:debug-draw-neck #f
:jump-debug-draw #f
:move-to-ground #f
:hover-if-no-ground #f
:idle-anim-script (new 'static 'array idle-control-frame 4
(new 'static 'idle-control-frame :command (ic-cmd play) :anim #x5 :param0 #x1 :param1 #x1)
(new 'static 'idle-control-frame)
(new 'static 'idle-control-frame)
(new 'static 'idle-control-frame)
)
:idle-anim 5
:notice-anim 6
:hostile-anim 8
:hit-anim 5
:knocked-anim -1
:knocked-land-anim -1
:die-anim 15
:die-falling-anim -1
:victory-anim -1
:jump-wind-up-anim -1
:jump-in-air-anim -1
:jump-land-anim -1
:neck-joint -1
:sound-die (static-sound-name "pegasus-die")
:notice-distance (meters 45)
:notice-distance-delta (meters 30)
:proximity-notice-distance (meters 55)
:default-hit-points 6
:gnd-collide-with (collide-spec backgnd)
:overlaps-others-collide-with-filter (collide-spec jak bot player-list)
:movement-gravity (meters -100)
:friction 0.8
:attack-shove-back (meters 3)
:attack-shove-up (meters 2)
:attack-mode 'generic
:attack-damage 2
:jump-height-min (meters 3)
:jump-height-factor 0.5
:knocked-seek-ry-clamp 2730.6667
:knocked-soft-vxz-lo 72089.6
:knocked-soft-vxz-hi 108134.4
:knocked-soft-vy-lo 81920.0
:knocked-soft-vy-hi 122880.0
:knocked-medium-vxz-lo 147456.0
:knocked-medium-vxz-hi 196608.0
:knocked-medium-vy-lo 135168.0
:knocked-medium-vy-hi 151552.0
:knocked-hard-vxz-lo 78643.2
:knocked-hard-vxz-hi 117964.8
:knocked-hard-vy-lo 183500.8
:knocked-hard-vy-hi 209715.2
:knocked-huge-vxz-lo 164659.2
:knocked-huge-vxz-hi 249036.8
:knocked-huge-vy-lo 183500.8
:knocked-huge-vy-hi 217907.2
:knocked-yellow-vxz-lo 40960.0
:knocked-yellow-vxz-hi 49152.0
:knocked-yellow-vy-lo 57344.0
:knocked-yellow-vy-hi 81920.0
:knocked-red-vxz-lo 24576.0
:knocked-red-vxz-hi 196608.0
:knocked-red-vy-lo 94208.0
:knocked-red-vy-hi 151552.0
:knocked-blue-vxz-lo 40960.0
:knocked-blue-vxz-hi 49152.0
:knocked-blue-vy-lo 24576.0
:knocked-blue-vy-hi 81920.0
:shadow-size (meters 1)
:shadow-max-y (meters 1)
:shadow-min-y (meters -1)
:shadow-locus-dist (meters 150)
:gem-joint 9
:gem-seg #x2
:gem-no-seg #x4
:gem-offset (new 'static 'sphere :y 942.08 :z 40.96 :r 163840.0)
)
)
(set! (-> *pegasus-enemy-info* fact-defaults) *fact-info-enemy-defaults*)
(defmethod run-logic? ((this pegasus))
"Calls [[process-tree::12]] if we are considered in-range of the [[pegasus]], otherwise returns [[#t]]"
(let ((min-distance 491520.0))
(if (< (* min-distance min-distance) (vector-vector-distance-squared (-> this root trans) (camera-pos)))
((method-of-type enemy run-logic?) this)
#t
)
)
)
(defmethod track-how-long-aimed-at! ((this pegasus))
"Updates `targetted-timer` with the `frame-counter` while Jak is aiming at the [[pegasus]]"
(let ((target *target*))
(when (and target (-> target gun active?))
(let ((inaccuracy-dir (new 'stack-no-clear 'vector))
(inaccuracy-vec (new 'stack-no-clear 'vector))
)
(vector-! inaccuracy-dir (-> this root trans) (-> target gun fire-point))
(vector+float*!
inaccuracy-vec
(-> target gun fire-point)
(-> target gun fire-dir-out)
(vector-length inaccuracy-dir)
)
(let ((inaccuracy-mag (vector-vector-distance-squared inaccuracy-vec (-> this root trans)))
(threshold 20480.0)
)
(if (< inaccuracy-mag (* threshold threshold))
(set-time! (-> this targetted-timer))
)
)
)
)
)
0
(none)
)
;; WARN: Return type mismatch int vs enemy-aware.
(defmethod update-target-awareness! ((this pegasus) (proc process-focusable) (focus enemy-best-focus))
"Checks a variety of criteria to determine the level of awareness the enemy is of the target. Sets `aware` and related fields as well!
For pegasus, it will call [[enemy::57]] only if the [[pegasus]] has been targetted for more than 5 [[seconds]]
@returns the value from [[enemy::57]], otherwise [[enemy-aware::4]]"
(the-as enemy-aware (if (time-elapsed? (-> this targetted-timer) (seconds 5))
(the-as int ((method-of-type enemy update-target-awareness!) this proc focus))
4
)
)
)
(defmethod general-event-handler ((this pegasus) (proc process) (arg2 int) (event-type symbol) (event event-message-block))
"Handles various events for the enemy
@TODO - unsure if there is a pattern for the events and this should have a more specific name"
(case event-type
(('track)
#f
)
(('hit 'hit-knocked)
(cond
((zero? (-> this hit-points))
(logclear! (-> this mask) (process-mask actor-pause))
(logclear! (-> this focus-status) (focus-status dangerous))
(logclear! (-> this enemy-flags) (enemy-flag use-notice-distance))
(logior! (-> this enemy-flags) (enemy-flag alert))
(logior! (-> this focus-status) (focus-status hit))
(if (zero? (-> this hit-points))
(logior! (-> this focus-status) (focus-status dead))
)
(logclear! (-> this enemy-flags) (enemy-flag lock-focus))
(enemy-method-62 this)
(logior! (-> this enemy-flags) (enemy-flag lock-focus))
(process-contact-action proc)
(send-event proc 'get-attack-count 1)
(kill-prefer-falling this)
)
(else
(let ((v0-0 (the-as object (current-time))))
(set! (-> this targetted-timer) (the-as time-frame v0-0))
v0-0
)
)
)
)
(else
((method-of-type enemy general-event-handler) this proc arg2 event-type event)
)
)
)
(defmethod damage-amount-from-attack ((this pegasus) (arg0 process) (arg1 event-message-block))
"Only attacks from the jetboard will deal max damage (6), but by default it will return `1`.
This is why the scouts are technically killable by other means
@returns the amount of damage taken from an attack. Also updates the `targetted-timer`.
@see [[*pegasus-enemy-info*]]"
(let ((hitpoints 1))
(let ((attack-info (the-as attack-info (-> arg1 param 1))))
(case (-> arg1 message)
(('attack)
(if (and (logtest? (-> attack-info mask) (attack-mask mode)) (= (-> attack-info mode) 'board))
(set! hitpoints (-> this hit-points))
)
)
)
)
(set-time! (-> this targetted-timer))
hitpoints
)
)
;; WARN: Return type mismatch symbol vs none.
(defbehavior pegasus-draw-section pegasus ((path-percent-a float) (path-percent-b float) (color rgba))
"Draws the [[pegasus]] curve section between the two provided points.
@param path-percent-a Percentage along the path for the first point
@param path-percent-b Percentage along the path for the second point
param color The color to draw the curve with
@see [[curve-control]]"
(let ((point-a (new 'stack-no-clear 'vector))
(point-b (new 'stack-no-clear 'vector))
)
(get-point-at-percent-along-path!
(-> self path-info (-> self current-path) path-data)
point-a
path-percent-a
'interp
)
(get-point-at-percent-along-path!
(-> self path-info (-> self current-path) path-data)
point-b
path-percent-b
'interp
)
(add-debug-line #t (bucket-id debug-no-zbuf1) point-a point-b color #f (the-as rgba -1))
)
(none)
)
(defbehavior pegasus-show-runs pegasus ()
"When [[*display-path-marks*]] is enabled, allow the user to cycle through the displayed path
- Pressing [[up]] on the D-Pad will increment the path index
- Pressing [[down]] on the D-Pad will decrement the path index
Additional debug text will be displayed as well. This is useful as many paths overlap other paths"
(when *display-path-marks*
(when (cpad-pressed? 0 up)
(+! (-> self display-path) 1)
(if (>= (-> self display-path) (-> self num-paths))
(set! (-> self display-path) -1)
)
)
(when (cpad-pressed? 0 down)
(+! (-> self display-path) -1)
(if (< (-> self display-path) -1)
(set! (-> self display-path) (+ (-> self num-paths) -1))
)
)
(cond
((< (-> self display-path) 0)
(countdown (path-idx 20)
(let ((path (-> self path-info path-idx path-data)))
(if path
(debug-draw path)
)
)
)
)
(else
(format
*stdcon*
"~A old-path ~D can run ~A at ~D ~F~%"
(-> self name)
(-> self previous-path)
(-> self can-run)
(-> self current-path)
(-> self curve-position)
)
(format *stdcon* "~A showing path ~D~%" (-> self name) (-> self display-path))
(let ((_path (-> self path-info (-> self display-path) path-data)))
(if _path
(debug-draw _path)
)
)
)
)
)
(none)
)
;; WARN: Return type mismatch vector vs none.
(defbehavior pegasus-rotate pegasus ((influenced-by-target? symbol) (angle float))
"Rotates the [[pegasus]] along the path, factoring in the current speed and the target's position
@param influenced-by-target? Whether or not to care about [[target]]'s position
@param angle The hopeful angle to use when constructing the rotation matrix, ultimately limited to `0.25` (radians?) though
@see [[target-pos]] and [[matrix-from-two-vectors-max-angle-partial!]]
@TODO - `float` should be `degrees` -- The usual amount passed in is `(degrees 100.0835)`"
(let ((rotation-matrix (new 'stack-no-clear 'matrix))
(flee-direction-vec (new 'stack-no-clear 'vector))
)
(displacement-between-points-at-percent-normalized!
(-> self path-info (-> self current-path) path-data)
(-> self tangent)
(-> self curve-position)
)
(cond
((and influenced-by-target? *target*)
(vector-! flee-direction-vec (-> self root trans) (target-pos 0))
(vector-normalize! flee-direction-vec 1.0)
)
((= (-> self speed) 0.0)
(set! (-> flee-direction-vec quad) (-> self facing quad))
)
((< (-> self speed) 0.0)
(vector-negate! flee-direction-vec (-> self tangent))
)
(else
(set! (-> flee-direction-vec quad) (-> self tangent quad))
)
)
(matrix-from-two-vectors-max-angle-partial! rotation-matrix (-> self facing) flee-direction-vec angle 0.25)
(vector-matrix*! flee-direction-vec (-> self facing) rotation-matrix)
(vector-normalize! flee-direction-vec 1.0)
(forward-down->inv-matrix rotation-matrix flee-direction-vec (new 'static 'vector :y -1.0))
(matrix->quaternion (-> self root quat) rotation-matrix)
(set! (-> self run-blend-interp) (acos (vector-dot flee-direction-vec (-> self facing))))
(set! (-> self run-blend-interp) (* 0.0002746582 (-> self run-blend-interp)))
(if (< (vector-dot (-> self facing) (the-as vector (-> rotation-matrix vector))) 0.0)
(set! (-> self run-blend-interp) (- (-> self run-blend-interp)))
)
(set! (-> self facing quad) (-> flee-direction-vec quad))
)
(none)
)
;; WARN: Return type mismatch float vs none.
(defbehavior pegasus-loop-on-same-path pegasus ()
"Creates an endless loop on the same path as the name suggests
- If we are beyond the end of the path, start again near the beginning (1%)
- If we are far before the beginning, start at the beginning (0%)
- If we are atleast 1% into the path, step back 1%
- If we are less than 0% somehow, slowly add 1%
Ultimately this appears to be some sort of safe-guard to get things back into a somewhat working state
if there are unexpected path conditions -- but the results of this are not great!
Another potential explaination is to make it easy during development to identify and debug a bad path
In practice, this is never called"
(cond
((< 100.0 (-> self curve-position))
(set! (-> self curve-position) 1.0)
)
((< (-> self curve-position) -100.0)
(set! (-> self curve-position) 0.0)
)
((< 1.0 (-> self curve-position))
(+! (-> self curve-position) -1.0)
)
((< (-> self curve-position) 0.0)
(+! (-> self curve-position) 1.0)
)
)
(none)
)
(defbehavior pegasus-choose-path pegasus ()
"Determines the next path the pegasus should take if we have completed the current path it's on
There are many fail-safes here if something goes wrong to try to keep the pegasus behaving
@see [[pegasus-look-on-same-path]]
@TODO - understand the path selection better"
(local-vars (f0-15 float) (f30-0 float))
(while (begin (label cfg-61) (or (< 1.0 (-> self curve-position)) (< (-> self curve-position) 0.0)))
(let ((curr-pegasus-path (-> self path-info (-> self current-path)))
(control-data-idx 3)
)
0.0
(set! f30-0 (cond
((or (>= (-> self current-path) (-> self num-paths))
(< (-> self current-path) 0)
(<= (-> curr-pegasus-path num-data) 0)
)
(format
0
"<GMJ>: ~A has path out of range cur ~D num ~D num-data ~D~%"
(-> self name)
(-> self current-path)
(-> self num-paths)
(-> curr-pegasus-path num-data)
)
(pegasus-loop-on-same-path)
(goto cfg-61)
f30-0
)
((< (-> self curve-position) 0.0)
(while (and (< control-data-idx (-> curr-pegasus-path num-data))
(>= (-> curr-pegasus-path control-data control-data-idx) 0.0)
)
(+! control-data-idx 3)
)
(+! control-data-idx 1)
(* (total-distance (-> curr-pegasus-path path-data)) (- (-> self curve-position)))
)
(else
(* (total-distance (-> curr-pegasus-path path-data)) (+ -1.0 (-> self curve-position)))
)
)
)
(when (or (< (- (-> curr-pegasus-path num-data) control-data-idx) 3)
(< (-> curr-pegasus-path control-data control-data-idx) 0.0)
)
(format
0
"<GMJ>: ~A has bad path connect data format num-data ~D ctrl-data ~F~%"
(-> self name)
(- (-> curr-pegasus-path num-data) control-data-idx)
(-> curr-pegasus-path control-data control-data-idx)
)
(pegasus-loop-on-same-path)
(goto cfg-61)
)
(let* ((rand-int (/ (the-as int (rand-uint31-gen *random-generator*)) 256))
(rand-float (the-as float (logior #x3f800000 rand-int)))
(_rand (+ -1.0 rand-float))
)
(while (and (>= (- (-> curr-pegasus-path num-data) control-data-idx) 6)
(>= (-> curr-pegasus-path control-data control-data-idx) 0.0)
(< (-> curr-pegasus-path control-data (+ control-data-idx 2)) _rand)
(!= (-> curr-pegasus-path control-data (+ control-data-idx 3)) -1.0)
(!= (-> curr-pegasus-path control-data (+ control-data-idx 5)) 0.0)
)
(set! _rand (- _rand (-> curr-pegasus-path control-data (+ control-data-idx 2))))
(+! control-data-idx 3)
)
)
(let ((control-data (the int (-> curr-pegasus-path control-data control-data-idx)))
(f28-0 (fmin 1.0 (fmax 0.0 (-> curr-pegasus-path control-data (+ control-data-idx 1)))))
)
(when (or (>= control-data (-> self num-paths)) (< control-data 0))
(format
0
"<GMJ>: ~A has bad path connect data prev ~D current ~D desired ~D num ~D~%"
(-> self name)
(-> self previous-path)
(-> self current-path)
control-data
(-> self num-paths)
)
(pegasus-loop-on-same-path)
(goto cfg-61)
)
(set! (-> self previous-path) (-> self current-path))
(set! (-> self current-path) control-data)
(let ((_curr-pegasus-path (-> self path-info (-> self current-path))))
(let ((f0-14 (/ f30-0 (total-distance (-> _curr-pegasus-path path-data)))))
(cond
((< 0.5 f28-0)
(set! f0-15 (- f28-0 f0-14))
(if (< 0.0 (-> self speed))
(set! (-> self speed) (- (-> self speed)))
)
)
(else
(set! f0-15 (+ f28-0 f0-14))
(if (< (-> self speed) 0.0)
(set! (-> self speed) (- (-> self speed)))
)
)
)
)
(set! (-> self curve-position) f0-15)
(set! (-> self can-run) #f)
(if (> (-> _curr-pegasus-path num-data) 0)
(set! (-> self can-run) (!= (-> _curr-pegasus-path control-data 0) 0.0))
)
)
)
)
)
#f
)
(defbehavior pegasus-move pegasus ((explicit-y-vel float) (adjust-y-offset? symbol))
"Moves the pegasus along the path by smoothly interpolating along it
@param explicit-y-vel Normally `0.0` but this value is used when the pegasus needs to switch direction
@param adjust-y-offset? Normally [[#f]] which forces the movement to reflect the result of the collison query - the [[pegasus]] will stay close to the ground. [[#t]] ignores this"
(+! (-> self curve-position)
(/ (the float (* (- (current-time) (-> self clock old-frame-counter)) (the int (-> self speed))))
(total-distance (-> self path-info (-> self current-path) path-data))
)
)
(pegasus-choose-path)
(get-point-at-percent-along-path!
(-> self path-info (-> self current-path) path-data)
(-> self root trans)
(-> self curve-position)
'interp
)
(let ((y-pos (+ -81920.0 (-> self root trans y)))
(draw-shadow? #f)
)
(let ((cquery (new 'stack 'collide-query))
(start-pos (new 'stack-no-clear 'vector))
)
(set! (-> start-pos quad) (-> self root trans quad))
(set! (-> self over-ground) #f)
(when (enemy-above-ground? self cquery start-pos (collide-spec backgnd) 12288.0 81920.0 1024.0)
(set! y-pos (-> cquery best-other-tri intersect y))
(set! draw-shadow? #t)
(set! (-> self over-ground) (< 204.8 (fabs (- y-pos (-> self water-height)))))
(set! adjust-y-offset? (and (-> self over-ground) adjust-y-offset?))
(if adjust-y-offset?
(set! (-> self y-offset-desired) (- y-pos (-> start-pos y)))
)
)
)
(cond
((< (-> self y-offset-desired) (-> self y-offset))
(set! (-> self y-vel) (* 0.25 (- (-> self y-offset-desired) (-> self y-offset))))
(set! (-> self y-vel) (fmax -68.26667 (-> self y-vel)))
(if (!= explicit-y-vel 0.0)
(set! (-> self y-vel) (- explicit-y-vel))
)
(+! (-> self y-offset) (-> self y-vel))
(when (>= (-> self y-offset-desired) (-> self y-offset))
(set! (-> self y-offset) (-> self y-offset-desired))
(set! (-> self y-vel) 0.0)
)
)
((< (-> self y-offset) (-> self y-offset-desired))
(set! (-> self y-vel) (* 0.5 (- (-> self y-offset-desired) (-> self y-offset))))
(set! (-> self y-vel) (fmin 13.653334 (-> self y-vel)))
(if (!= explicit-y-vel 0.0)
(set! (-> self y-vel) explicit-y-vel)
)
(+! (-> self y-offset) (-> self y-vel))
(when (>= (-> self y-offset) (-> self y-offset-desired))
(set! (-> self y-offset) (-> self y-offset-desired))
(set! (-> self y-vel) 0.0)
)
)
)
(+! (-> self root trans y) (-> self y-offset))
(when (< (-> self root trans y) y-pos)
(set! (-> self y-offset) (- y-pos (- (-> self root trans y) (-> self y-offset))))
(set! (-> self root trans y) y-pos)
(set! (-> self y-vel) (fmax 0.0 (-> self y-vel)))
)
(set! (-> self on-ground) (< (fabs (- (-> self root trans y) y-pos)) 409.6))
(cond
(draw-shadow?
(let ((shadow-ctrl (-> self draw shadow-ctrl)))
(logclear! (-> shadow-ctrl settings flags) (shadow-flags disable-draw))
)
0
(let ((_shadow-ctrl (-> self draw shadow-ctrl)))
(set! (-> _shadow-ctrl settings bot-plane w) (- (+ (- -6144.0 (-> self root trans y)) y-pos)))
)
0
)
(else
(let ((__shadow-ctrl (-> self draw shadow-ctrl)))
(logior! (-> __shadow-ctrl settings flags) (shadow-flags disable-draw))
)
0
)
)
)
(none)
)
(defbehavior pegasus-calc-speed pegasus ((min-dist float) (max-dist float) (max-speed float) (min-speed float))
"Calculates the pegasus' speed based on a number of factors:
- Speed up as the target gets closer (up to a max) and slow down as the target is further away (up to a min)
This function also is what causes the pegasus to flip around
@param min-dist TODO
@param max-dist TODO
@param max-speed The maximum speed the pegasus can go
@param min-speed The minimum speed the pegasus can go
@returns If there should be a reaction to the target via [[enemy::72]]"
(let ((dir-to-target (vector-! (new 'stack-no-clear 'vector) (-> self root trans) (target-pos 0)))
(react-to-target? #f)
)
(set! (-> dir-to-target y) 0.0)
(let* ((dist-from-target (/ (- (vector-length dir-to-target) min-dist) (- max-dist min-dist)))
(interpolant (- 1.0 (fmax 0.0 (fmin 1.0 dist-from-target))))
(interpolated-speed (lerp min-speed max-speed interpolant))
)
(if (not (time-elapsed? (-> self targetted-timer) (seconds 5)))
(set! interpolated-speed (fmax 163840.0 interpolated-speed))
)
(let ((movement-speed (* 0.0033333334 interpolated-speed)))
(cond
((< (vector-dot dir-to-target (-> self tangent)) 0.0)
(if (< 0.0 (-> self speed))
(set! react-to-target? #t)
)
)
((< (-> self speed) 0.0)
(set! react-to-target? #t)
)
)
(let ((anim-group (ja-group)))
(cond
((and anim-group (= anim-group pegasus-flip-around-a-ja))
(if (< 1.0 (fabs (-> self speed)))
(set! (-> self speed) (* 0.92 (-> self speed)))
)
)
(else
(let ((_anim-group (ja-group)))
(cond
((and _anim-group (= _anim-group pegasus-flip-around-b-ja))
(if (< (-> self speed) 0.0)
(seek! (-> self speed) (- movement-speed) 13.653334)
(seek! (-> self speed) movement-speed 13.653334)
)
)
((< (-> self speed) 0.0)
(set! (-> self speed) (- movement-speed))
)
(else
(set! (-> self speed) movement-speed)
)
)
)
)
)
)
)
)
react-to-target?
)
)
(defbehavior pegasus-calc-anim-speed pegasus ()
"Based on `speed`, adjust how fast the pegasus should animate.
The faster it's moving the fast it flaps it's wings, etc
@TODO understand the magic values here better
@returns The anim speed, it can be no lower than `1.5`"
(let* ((speed-abs (fabs (-> self speed)))
(f0-2 (* 0.07324219 speed-abs))
(f0-3 (+ -15.0 f0-2))
(f0-4 (* 0.13333334 f0-3))
)
(fmin 1.5 (fmax 0.9 f0-4))
)
)
(defmethod common-post ((this pegasus))
"Does a lot of various things relating to interacting with the target
- tracks when the enemy was last drawn
- looks at the target and handles attacking
@TODO Not extremely well understood yet"
(track-how-long-aimed-at! this)
(pegasus-show-runs)
((method-of-type enemy common-post) this)
(none)
)
(defmethod enemy-method-99 ((this pegasus) (arg0 process-focusable))
#t
)
(defstate pegasus-debug (pegasus)
:code (behavior ()
(until #f
(ja-no-eval :group! (ja-group) :num! (seek!) :frame-num 0.0)
(until (ja-done? 0)
(let ((cam-dbg-vec (new 'stack-no-clear 'vector)))
(let ((vec (new 'stack-no-clear 'vector)))
(clmf-input cam-dbg-vec vec 1)
)
(vector-float*! cam-dbg-vec cam-dbg-vec 4096.0)
(+! (-> self curve-position)
(/ (-> cam-dbg-vec y) (total-distance (-> self path-info (-> self current-path) path-data)))
)
)
(cond
((< 1.0 (-> self curve-position))
(+! (-> self curve-position) -1.0)
)
((< (-> self curve-position) 0.0)
(+! (-> self curve-position) 1.0)
)
)
(get-point-at-percent-along-path!
(-> self path-info (-> self current-path) path-data)
(-> self root trans)
(-> self curve-position)
'interp
)
(pegasus-rotate #f 1820.4445)
(suspend)
(ja :num! (seek!))
)
)
#f
)
:post ja-post
)
(defstate die (pegasus)
:virtual #t
:code (behavior ()
(process-entity-status! self (entity-perm-status subtask-complete) #t)
(let ((evt (new 'stack-no-clear 'event-message-block)))
(set! (-> evt from) (process->ppointer self))
(set! (-> evt num-params) 1)
(set! (-> evt message) 'change-mode)
(set! (-> evt param 0) (the-as uint 'pegasus))
(let ((event-result (send-event-function (handle->process (-> self incoming attacker-handle)) evt)))
(-> self speed)
(ja-channel-push! 1 (seconds 0.1))
(ja-no-eval :group! pegasus-board-attack-pegasus-ja :num! (seek! max 1.5) :frame-num 0.0)
(until (ja-done? 0)
(let ((frame-num (ja-aframe-num 0))
(speed (-> self speed))
)
(when (>= frame-num 60.0)
(set! (-> self speed) (lerp-scale speed 0.0 frame-num 60.0 100.0))
(set! (-> self y-offset-desired) 0.0)
)
)
(pegasus-move 0.0 #t)
(pegasus-rotate #f 1820.4445)
(suspend)
(ja :num! (seek! max 1.5))
)
(if event-result
(send-event (handle->process (-> self incoming attacker-handle)) 'end-mode)
)
)
)
(send-event self 'death-end)
(while (-> self child)
(suspend)
)
(cleanup-for-death self)
)
)
(defstate stare (pegasus)
:virtual #t
:trans (behavior ()
(when (time-elapsed? (-> self state-time) (seconds 0.1))
(let ((awareness (-> self focus aware)))
(cond
((and (>= 1 (the-as int awareness))
(-> self over-ground)
(< (fabs (- (-> self y-offset) (-> self y-offset-desired))) 409.6)
)
(go-virtual active)
)
((and (= awareness (enemy-aware enemy-aware-3)) (get-enemy-target self))
(go-hostile self)
)
((= awareness (enemy-aware unaware))
(go-flee self)
)
)
)
)
)
:code (behavior ()
(let ((anim-group (ja-group)))
(when (not (and anim-group (= anim-group pegasus-fly-ja)))
(ja-channel-push! 1 (seconds 0.2))
(ja :group! pegasus-fly-ja)
)
)
(until #f
(ja :num! (loop! (pegasus-calc-anim-speed)))
(if (< 27.306667 (fabs (-> self speed)))
(set! (-> self speed) (* 0.95 (-> self speed)))
)
(set! (-> self y-offset-desired) 0.0)
(pegasus-move 409.6 #t)
(pegasus-rotate #f 1820.4445)
(suspend)
)
#f
)
:post enemy-simple-post
)
(defbehavior pegasus-fly-code pegasus ((arg0 int))
"Handles the flying animations and sounds
@TODO - cleanup a bit more"
(let ((anim-speed (pegasus-calc-anim-speed)))
(let ((gp-0 (and (-> self can-run) (< (the-as time-frame (-> self ambient-possible)) (current-time)))))
(let* ((min-dist (lerp-scale 61440.0 32768.0 (the float arg0) 0.0 3000.0))
(min-speed (lerp-scale 122880.0 81920.0 (the float arg0) 0.0 3000.0))
(s4-0 (pegasus-calc-speed min-dist 122880.0 min-speed 49152.0))
)
(case (ja-group)
(('pegasus-fly-doublespeed-ja)
(ja :num! (seek! max (fmax 1.0 (* 0.8 anim-speed))))
)
(('pegasus-fly-ja)
(ja :num! (seek! max (* 1.5 anim-speed)))
)
(else
(ja :num! (seek!))
)
)
(let ((v1-34 #f)
(s5-1 12)
)
(let ((a0-13 (ja-group)))
(when (not (and a0-13
(or (= a0-13 pegasus-fly-glide-ja)
(= a0-13 pegasus-fly-doublespeed-ja)
(= a0-13 pegasus-fly-ja)
(= a0-13 pegasus-flip-around-a-ja)
(= a0-13 pegasus-flip-around-b-ja)
(= a0-13 pegasus-fly-to-run-ja)
(= a0-13 pegasus-run-ja)
(= a0-13 pegasus-run-to-fly-ja)
)
)
)
(set! s5-1 37)
(set! v1-34 #t)
)
)
(cond
((and s4-0 (let ((a0-21 (ja-group)))
(not (and a0-21 (or (= a0-21 pegasus-flip-around-a-ja) (= a0-21 pegasus-flip-around-b-ja))))
)
)
(ja-channel-push! 1 (seconds 0.125))
(ja :group! pegasus-flip-around-a-ja)
(ja :num-func num-func-identity :frame-num 0.0)
)
((and (not v1-34) (not (ja-done? 0)))
)
(else
(let ((v1-46 (ja-group)))
(cond
((and v1-46 (= v1-46 pegasus-fly-to-run-ja))
(ja :group! pegasus-run-ja)
(ja :num-func num-func-identity :frame-num 0.0)
)
((let ((v1-58 (ja-group)))
(and v1-58 (= v1-58 pegasus-flip-around-a-ja))
)
(ja :group! pegasus-flip-around-b-ja :num! min)
(set! (-> self speed) (- (-> self speed)))
(let ((s5-3 (new 'stack-no-clear 'vector))
(s4-1 (new 'stack-no-clear 'matrix))
)
(new 'stack-no-clear 'vector)
(vector-cross! s5-3 (-> self tangent) (new 'static 'vector :y 1.0))
(vector-cross! s5-3 s5-3 (-> self tangent))
(vector-normalize! s5-3 1.0)
(matrix-axis-angle! s4-1 s5-3 32768.0)
(vector-matrix*! (-> self facing) (-> self facing) s4-1)
)
)
((let ((v1-72 (ja-group)))
(and v1-72 (= v1-72 pegasus-run-ja))
)
(if (or (not (-> self can-run)) (< (the-as time-frame (-> self ambient-expire)) (current-time)))
(ja :group! pegasus-run-to-fly-ja)
)
(ja :num-func num-func-identity :frame-num 0.0)
)
((and gp-0 (-> self on-ground))
(ja-channel-push! 1 (seconds 0.125))
(ja :group! pegasus-fly-to-run-ja)
(ja :num-func num-func-identity :frame-num 0.0)
(let* ((s5-4 (current-time))
(f30-1 300.0)
(f28-1 2.0)
(f26-0 3.0)
(v1-99 (/ (the-as int (rand-uint31-gen *random-generator*)) 256))
(v1-100 (the-as number (logior #x3f800000 v1-99)))
)
(set! (-> self ambient-expire)
(the-as uint (+ s5-4 (the int (* f30-1 (+ f28-1 (* f26-0 (+ -1.0 (the-as float v1-100))))))))
)
)
(let* ((s5-5 (-> self ambient-expire))
(f30-2 300.0)
(f28-2 2.0)
(f26-1 3.0)
(v1-108 (/ (the-as int (rand-uint31-gen *random-generator*)) 256))
(v1-109 (the-as number (logior #x3f800000 v1-108)))
)
(set! (-> self ambient-possible)
(+ s5-5 (the int (* f30-2 (+ f28-2 (* f26-1 (+ -1.0 (the-as float v1-109)))))))
)
)
)
((or (and (< (-> self speed) 0.0) (< 0.125 (-> self tangent y)))
(and (< 0.0 (-> self speed)) (< (-> self tangent y) -0.125))
)
(let ((v1-121 (ja-group)))
(when (not (and v1-121 (= v1-121 pegasus-fly-glide-ja)))
(ja-channel-push! 1 (the-as time-frame s5-1))
(ja :group! pegasus-fly-glide-ja)
)
)
(ja :num-func num-func-identity :frame-num 0.0)
)
((< 1.25 anim-speed)
(let ((v1-135 (ja-group)))
(when (not (and v1-135 (= v1-135 pegasus-fly-doublespeed-ja)))
(ja-channel-push! 1 (the-as time-frame s5-1))
(ja :group! pegasus-fly-doublespeed-ja)
)
)
(ja :num-func num-func-identity :frame-num 0.0)
)
(else
(let ((v1-148 (ja-group)))
(when (not (and v1-148 (= v1-148 pegasus-fly-ja)))
(ja-channel-push! 1 (the-as time-frame s5-1))
(ja :group! pegasus-fly-ja)
)
)
(ja :num-func num-func-identity :frame-num 0.0)
)
)
)
)
)
)
)
(pegasus-move
0.0
(the-as symbol (or gp-0 (let ((v1-161 (ja-group)))
(and v1-161 (or (= v1-161 pegasus-fly-to-run-ja) (= v1-161 pegasus-run-ja)))
)
)
)
)
)
)
(pegasus-rotate #f 1820.4445)
(none)
)
(defstate pegasus-tired (pegasus)
:event enemy-event-handler
:enter (behavior ()
(let ((func (-> (method-of-type enemy flee) enter)))
(if func
(func)
)
)
(set-time! (-> self state-time))
(set-time! (-> self far-time))
(set-setting! 'sound-mode #f 0.0 1)
)
:exit (behavior ()
(let ((func (-> (method-of-type enemy flee) exit)))
(if func
(func)
)
)
(remove-setting! 'sound-mode)
)
:trans (behavior ()
(let ((func (-> (method-of-type enemy flee) trans)))
(if func
(func)
)
)
(cond
((and *target* (and (>= 102400.0 (vector-vector-xz-distance (-> self root trans) (-> *target* control trans)))
(not (logtest? (focus-status teleporting) (-> *target* focus-status)))
)
)
(set! (-> self state-time) (the-as time-frame (max (+ (current-time) (seconds -10)) (-> self state-time))))
)
(else
(+! (-> self state-time) (* 10 (- (current-time) (-> self far-time))))
(if (>= (-> self state-time) (current-time))
(go-virtual flee)
)
)
)
(set-time! (-> self far-time))
)
:code (behavior ()
(until #f
(set! (-> self y-offset-desired) 0.0)
(pegasus-fly-code (the-as int (- (current-time) (-> self state-time))))
(suspend)
)
#f
)
:post (-> (method-of-type enemy flee) post)
)
(defstate flee (pegasus)
:virtual #t
:enter (behavior ()
(let ((func (-> (method-of-type enemy flee) enter)))
(if func
(func)
)
)
(set! (-> self near-timer) 3000)
(set-time! (-> self far-time))
(set-setting! 'sound-mode #f 0.0 1)
)
:exit (behavior ()
(let ((func (-> (method-of-type enemy flee) exit)))
(if func
(func)
)
)
(set! (-> self y-offset-desired) 0.0)
(remove-setting! 'sound-mode)
)
:trans (behavior ()
(when (-> self can-run)
(let ((func (-> (method-of-type enemy flee) trans)))
(if func
(func)
)
)
)
(when (and *target*
(and (>= 102400.0 (vector-vector-xz-distance (-> self root trans) (-> *target* control trans)))
(not (logtest? (focus-status teleporting) (-> *target* focus-status)))
)
)
(set! (-> self near-timer)
(- (the-as time-frame (-> self near-timer)) (- (current-time) (-> self clock old-frame-counter)))
)
(if (<= (-> self near-timer) 0)
(go pegasus-tired)
)
(set-time! (-> self far-time))
)
(if (time-elapsed? (-> self far-time) (seconds 3))
(set! (-> self near-timer) (the-as int (-> self timeout)))
)
)
:code (behavior ()
(until #f
(set! (-> self y-offset-desired) 2.0)
(pegasus-fly-code 0)
(suspend)
)
#f
)
)
(defstate active (pegasus)
:virtual #t
:code (behavior ()
(set! (-> self allow-idle) #f)
(let ((anim-group (ja-group)))
(when (not (and anim-group (= anim-group pegasus-idle-ja)))
(ja-channel-push! 1 (seconds 0.1))
(ja-no-eval :group! pegasus-fly-to-idle-ja :num! (seek!) :frame-num 0.0)
(until (ja-done? 0)
(suspend)
(ja :num! (seek!))
)
)
)
(set! (-> self allow-idle) #t)
(ja-channel-push! 1 (seconds 0.1))
(sleep-code)
)
:post (behavior ()
(if (-> self allow-idle)
(idle-control-method-10 (-> self idle-anim-player) self)
)
(enemy-simple-post)
)
)
(defstate notice (pegasus)
:virtual #t
:enter (behavior ()
(let ((t9-0 (-> (method-of-type enemy notice) enter)))
(if t9-0
(t9-0)
)
)
(set-setting! 'sound-mode #f 0.0 1)
)
:exit (behavior ()
(let ((func (-> (method-of-type enemy notice) exit)))
(if func
(func)
)
)
(remove-setting! 'sound-mode)
)
:code (behavior ()
(displacement-between-points-at-percent-normalized!
(-> self path-info (-> self current-path) path-data)
(-> self tangent)
(-> self curve-position)
)
(if (pegasus-calc-speed 61440.0 122880.0 204800.0 49152.0)
(react-to-focus self)
)
(let ((anim-length (rnd-float-range self 0.8 1.2)))
(ja-channel-push! 1 (seconds 0.2))
(ja-no-eval :group! (-> self draw art-group data (-> self enemy-info notice-anim))
:num! (seek! max anim-length)
:frame-num 0.0
)
(until (ja-done? 0)
(if (pegasus-calc-speed 61440.0 122880.0 204800.0 49152.0)
(react-to-focus self)
)
(pegasus-rotate #f 1820.4445)
(suspend)
(ja :num! (seek! max anim-length))
)
(ja-no-eval :group! pegasus-idle-takeoff-in-air-ja :num! (seek! max anim-length) :frame-num 0.0)
(until (ja-done? 0)
(if (pegasus-calc-speed 61440.0 122880.0 204800.0 49152.0)
(react-to-focus self)
)
(pegasus-rotate #f 1820.4445)
(set! (-> self y-offset-desired) 0.0)
(pegasus-move 0.0 #f)
(suspend)
(ja :num! (seek! max anim-length))
)
)
(react-to-focus self)
)
)
(defstate idle (pegasus)
:virtual #t
:post (behavior ()
(pegasus-show-runs)
(track-how-long-aimed-at! self)
(let ((func (-> (method-of-type enemy idle) post)))
(if func
((the-as (function none) func))
)
)
)
)
(defmethod init-enemy-collision! ((this pegasus))
"Initializes the [[collide-shape-moving]] and any ancillary tasks to make the enemy collide properly"
;; og:preserve-this added
(stack-size-set! (-> this main-thread) 512)
(let ((cshape (new 'process 'collide-shape-moving this (collide-list-enum usually-hit-by-player))))
(set! (-> cshape dynam) (copy *standard-dynamics* 'process))
(set! (-> cshape reaction) cshape-reaction-default)
(set! (-> cshape no-reaction)
(the-as (function collide-shape-moving collide-query vector vector object) nothing)
)
(set! (-> cshape penetrated-by) (penetrate
generic-attack
lunge
flop
punch
spin
roll
uppercut
bonk
tube
vehicle
flut-attack
board
mech-punch
dark-punch
dark-giant
)
)
(let ((prim-group (new 'process 'collide-shape-prim-group cshape (the-as uint 1) 0)))
(set! (-> cshape total-prims) (the-as uint 2))
(set! (-> prim-group prim-core collide-as) (collide-spec enemy))
(set! (-> prim-group prim-core collide-with) (collide-spec jak player-list))
(set! (-> prim-group prim-core action) (collide-action solid no-standon))
(set! (-> prim-group transform-index) 3)
(set-vector! (-> prim-group local-sphere) 0.0 9216.0 4096.0 18432.0)
(set! (-> cshape root-prim) prim-group)
)
(let ((prim-sphere (new 'process 'collide-shape-prim-sphere cshape (the-as uint 0))))
(set! (-> prim-sphere prim-core collide-as) (collide-spec enemy))
(set! (-> prim-sphere prim-core collide-with) (collide-spec jak player-list))
(set! (-> prim-sphere prim-core action) (collide-action solid no-standon))
(set! (-> prim-sphere transform-index) 3)
(set-vector! (-> prim-sphere local-sphere) 0.0 6144.0 0.0 11264.0)
)
(set! (-> cshape nav-radius) (* 0.75 (-> cshape root-prim local-sphere w)))
(let ((root-prim (-> cshape root-prim)))
(set! (-> cshape backup-collide-as) (-> root-prim prim-core collide-as))
(set! (-> cshape backup-collide-with) (-> root-prim prim-core collide-with))
)
(set! (-> this root) cshape)
)
0
(none)
)
(defmethod coin-flip? ((this pegasus))
"@returns The result of a 50/50 RNG roll"
#f
)
(defmethod enemy-method-108 ((this pegasus) (arg0 process-drawable) (arg1 event-message-block))
0
)
;; WARN: Return type mismatch enemy vs pegasus.
(defmethod relocate ((this pegasus) (arg0 int))
(countdown (v1-0 20)
(if (-> this path-info v1-0 path-data)
(&+! (-> this path-info v1-0 path-data) arg0)
)
)
(the-as pegasus ((method-of-type enemy relocate) this arg0))
)
(defmethod init-enemy! ((this pegasus))
"Common method called to initialize the enemy, typically sets up default field values and calls ancillary helper methods"
(local-vars (tag res-tag))
(initialize-skeleton
this
(the-as skeleton-group (art-group-get-by-name *level* "skel-pegasus" (the-as (pointer uint32) #f)))
(the-as pair 0)
)
(init-enemy-behaviour-and-stats! this *pegasus-enemy-info*)
(logclear! (-> this draw shadow-ctrl settings flags) (shadow-flags shdf00))
(set! (-> this link) (new 'process 'actor-link-info this #f))
(dotimes (v1-8 20)
(set! (-> this path-info v1-8 path-data) #f)
)
(set! (-> this num-paths) 0)
(set! (-> this path-info 0 path-data) (new 'process 'curve-control this 'path -1000000000.0))
(when (-> this path-info 0 path-data)
(logior! (-> this path-info 0 path-data flags) (path-control-flag display draw-line draw-point draw-text))
(set! (-> this num-paths) 1)
)
(dotimes (path-info-idx 20)
(let ((curve (new 'process 'curve-control this 'path (the float path-info-idx))))
(if (logtest? (-> curve flags) (path-control-flag not-found))
(goto cfg-15)
)
(set! (-> this num-paths) (+ path-info-idx 1))
(set! (-> this path-info path-info-idx path-data) curve)
(logior! (-> curve flags) (path-control-flag display draw-line draw-point))
)
(set! tag (new 'static 'res-tag))
(let ((data
(res-lump-data (-> this entity) 'path-connection pointer :tag-ptr (& tag) :time (the float path-info-idx))
)
)
(cond
(data
(set! (-> this path-info path-info-idx num-data) (the-as int (-> tag elt-count)))
(set! (-> this path-info path-info-idx control-data) (the-as (pointer float) data))
)
(else
(set! (-> this path-info path-info-idx num-data) 0)
0
)
)
)
)
(label cfg-15)
(set! (-> this current-path) 0)
(set! (-> this previous-path) 0)
(set! (-> this display-path) -1)
(set! (-> this curve-position) (res-lump-float (-> this entity) 'initial-spline-pos))
(get-point-at-percent-along-path!
(-> this path-info (-> this current-path) path-data)
(-> this root trans)
(-> this curve-position)
'interp
)
(displacement-between-points-at-percent-normalized!
(-> this path-info (-> this current-path) path-data)
(-> this tangent)
(-> this curve-position)
)
(set! (-> this facing quad) (-> this tangent quad))
(let ((matrix (new 'stack-no-clear 'matrix)))
(forward-down->inv-matrix matrix (-> this facing) (new 'static 'vector :y -1.0))
(matrix->quaternion (-> this root quat) matrix)
)
(set! (-> this y-vel) 0.0)
(set! (-> this water-height) (res-lump-float (-> this entity) 'water-height))
(set! (-> this timeout) (the-as uint 3000))
(when (-> this entity)
(let ((timeout (res-lump-float (-> this entity) 'timeout :default 10.0)))
(set! (-> this timeout) (the-as uint (the int (* 300.0 timeout))))
)
)
(set! (-> this ambient-possible) (the-as uint 0))
(set! (-> this speed) 0.0)
(set! (-> this y-offset-desired) 0.0)
(pegasus-move 409600.0 #t)
(pegasus-calc-speed 61440.0 122880.0 2048.0 2048.0)
(set! (-> this can-run) #f)
0
(none)
)
(set-subtask-hook!
*game-info*
(game-task-node forest-scouts-pegasus)
TASK_MANAGER_INIT_HOOK
(lambda :behavior task-manager
()
(set! (-> *game-info* controller 0) (process->handle self))
(set! (-> self entity) (entity-by-type pegasus))
(set! (-> self actor-group 0) (res-lump-struct (-> self entity) 'actor-groups (pointer entity-actor)))
(when (-> self actor-group 0)
(let ((actor-group (-> self actor-group 0 0)))
(set! (-> self data-int32 0) (the-as int actor-group))
(set! (-> self data-int32 1) (the-as int actor-group))
)
(dotimes (actor-idx (the-as int (-> self actor-group 0 0)))
(let ((actor (-> (&+ (-> self actor-group 0) (* actor-idx 8)) 3)))
(if actor
(add-icon! *minimap* self (the-as uint 16) (the-as int #f) (the-as vector actor) 0)
)
)
)
#f
)
)
)
(set-subtask-hook!
*game-info*
(game-task-node forest-scouts-pegasus)
TASK_MANAGER_CODE_HOOK
(lambda :behavior task-manager
()
(local-vars (data int))
(set-time! (-> self start-time))
(set! (-> self hud-timer) (ppointer->handle (process-spawn hud-pegasus :init hud-init-by-other :to self)))
(while (> (-> self data-int32 0) 0)
(set! data (-> self data-int32 1))
(when (-> self actor-group 0)
(dotimes (actor-group (the-as int (-> self actor-group 0 0)))
(let ((actor (-> (&+ (-> self actor-group 0) (* actor-group 8)) 3)))
(if (and actor (logtest? (-> actor extra perm status) (entity-perm-status subtask-complete)))
(set! data (+ data -1))
)
)
)
)
(let ((_data data))
(set! (-> self data-int32 0) _data)
(set! (-> *game-info* counter) (the float _data))
)
(suspend)
)
(go-virtual complete)
(none)
)
)
(set-subtask-hook!
*game-info*
(game-task-node forest-scouts-pegasus)
TASK_MANAGER_COMPLETE_HOOK
(lambda ()
(task-node-close! (game-task-node forest-scouts-resolution))
(talker-spawn-func (-> *talker-speech* 90) *entity-pool* (target-pos 0) (the-as region #f))
(none)
)
)