diff --git a/.gitignore b/.gitignore index e9459fd3c6..0dd1030289 100644 --- a/.gitignore +++ b/.gitignore @@ -107,3 +107,4 @@ out/jak2/fr3.rar goalc-report.html .vscode/PythonImportHelper-v2-Completion.json _new-all-types.gc +goal_src/jak1/engine/mods/mod-common-functions.txt diff --git a/.vs/launch.vs.json b/.vs/launch.vs.json index 720cd1af8c..63b52f5c50 100644 --- a/.vs/launch.vs.json +++ b/.vs/launch.vs.json @@ -177,6 +177,20 @@ "-fakeiso", "-debug" ] + }, + { + "type": "default", + "project": "CMakeLists.txt", + "projectTarget": "gk.exe (bin\\gk.exe)", + "name": "Game - Jak 1 - Runtime (release)", + "args": [ + "-v", + "--game", + "jak1", + "--", + "-boot", + "-fakeiso" + ] }, { "type": "default", diff --git a/TODO.txt b/TODO.txt new file mode 100644 index 0000000000..63ec55af37 --- /dev/null +++ b/TODO.txt @@ -0,0 +1,15 @@ +**why the hell do enemies randomly despawn** + +(mod-rules-reset) on game exit +improve game over state +finish drought (disable water draw) +alert sound on last 10 seconds, find a replacement for new rule +look at orb refresh on hidden treasure again + +flut can still grab fly cells during wings tied + +maybe rename: crate break, keep it low, one frame +maybe add checks for green eco lurkers +maybe add settings for timer placement +consider replacement for jak rabbit +maybe improve all other sounds diff --git a/goal_src/jak1/engine/common-obs/collectables.gc b/goal_src/jak1/engine/common-obs/collectables.gc index b81ffc53e8..be290dbf89 100644 --- a/goal_src/jak1/engine/common-obs/collectables.gc +++ b/goal_src/jak1/engine/common-obs/collectables.gc @@ -22,6 +22,10 @@ (define-extern orb-placer-list-maintenace (function symbol none)) +(define-extern target-on-flut? (function symbol)) + +(define-extern mod-rule-active? (function symbol symbol)) + (declare-type vent process-drawable) (declare-type eco eco-collectable) @@ -699,6 +703,10 @@ :code (behavior () (loop + (if (mod-rule-active? 'complication-hidden-treasure) + (set! (-> self draw status) (draw-status hidden)) + (set! (-> self draw status) (draw-status))) + (quaternion-rotate-y! (-> self root quat) (-> self root quat) (* 40049.777 (seconds-per-frame))) (let ((f30-0 (-> self bob-amount))) (when (< 0.0 f30-0) @@ -707,10 +715,12 @@ (-> self suck-y-offset) (* f30-0 (sin (* 109.22667 (the float (mod (+ (- (current-time) (-> self birth-time)) (-> self bob-offset)) 600))))))) (update-transforms! (-> self root)))) - (ja-post) - ;; og:preserve-this added particle! - (with-pc - (if (-> *pc-settings* money-starburst?) (spawn (-> self part) (-> self root root-prim world-sphere)))) + + (when (!= (-> self draw status) (draw-status hidden)) + (ja-post) + (with-pc + (if (-> *pc-settings* money-starburst?) (spawn (-> self part) (-> self root root-prim world-sphere))))) + (suspend)))) (defstate notice-blue (money) @@ -894,18 +904,19 @@ :event (behavior ((proc process) (argc int) (message symbol) (block event-message-block)) (local-vars (v0-3 object)) - (when (and (or (= message 'touch) (= message 'attack)) - (and (logtest? (-> self flags) (collectable-flags can-collect)) - (time-elapsed? (-> self birth-time) (-> self collect-timeout)) - (and (not (handle->process (-> *game-info* other-camera-handle))) - (not *progress-process*) - (!= (-> self next-state name) 'pickup) - *target* - (not (logtest? (-> *target* state-flags) (state-flags grabbed dying)))))) +(when (and (not (mod-rule-active? 'complication-fuel-shortage)) + (or (= message 'touch) (= message 'attack)) + (and (logtest? (-> self flags) (collectable-flags can-collect)) + (time-elapsed? (-> self birth-time) (-> self collect-timeout)) + (and (not (handle->process (-> *game-info* other-camera-handle))) + (not *progress-process*) + (!= (-> self next-state name) 'pickup) + *target* + (not (logtest? (-> *target* state-flags) (state-flags grabbed dying)))))) (add-setting! 'allow-progress #f 0.0 0) (apply-settings *setting-control*) (logclear! (-> self mask) (process-mask actor-pause)) - (go-virtual pickup #f (process->handle proc))) + (if (not (and (target-on-flut?) (mod-rule-active? 'complication-wings-tied))) (go-virtual pickup #f (process->handle proc)))) (cond ((= message 'trans) (set! (-> self root trans quad) (-> (the-as vector (-> block param 0)) quad)) @@ -934,6 +945,13 @@ (let ((f28-0 0.0)) (ja :group! fuel-cell-idle-ja) (loop + (if (not (mod-rule-active? 'complication-fuel-shortage)) + (begin + (logclear! (-> self draw status) (draw-status hidden)) + (restore-collide-with-as (-> self root))) + (begin + (logior! (-> self draw status) (draw-status hidden)) + (clear-collide-with-as (-> self root)))) (let ((f30-0 (vector-vector-distance (-> self base) (target-pos 0)))) (set! f28-0 (if (and (< f30-0 (-> *FACT-bank* suck-suck-dist)) (not (logtest? (-> self flags) (collectable-flags anim)))) @@ -1013,6 +1031,7 @@ (move-to-ground (-> self root) (the-as float 40960.0) (the-as float 40960.0) #f (collide-kind background))))) (spool-push *art-control* (-> self victory-anim name) 0 self (the-as float -99.0)) (send-event *target* 'get-pickup (-> self fact pickup-type) (-> self fact pickup-amount)) + (runs-on-cell-pickup 'pickup) (set! (-> self draw bounds w) 32768.0) (logior! (-> self skel status) (janim-status inited)) (send-event *target* 'blend-shape #t) diff --git a/goal_src/jak1/engine/common-obs/crates.gc b/goal_src/jak1/engine/common-obs/crates.gc index 3b2f6ba924..3a09219e91 100644 --- a/goal_src/jak1/engine/common-obs/crates.gc +++ b/goal_src/jak1/engine/common-obs/crates.gc @@ -2,12 +2,19 @@ (in-package goal) (bundles "ENGINE.CGO" "GAME.CGO") (require "engine/common-obs/collectables.gc") + +(define-extern runs-on-crate-break (function process-drawable symbol int none)) + (declare-type crate process-drawable) (declare-type crate-buzzer crate) ;; DECOMP BEGINS +(define-extern *buzzer-dist-tracker* (float)) +(define-extern *closest-buzzer-dist* (float)) +(define-extern mod-rule-trigger-blue-crate (function none)) + (defskelgroup *crate-barrel-sg* crate crate-barrel-lod0-jg @@ -562,6 +569,14 @@ :event crate-standard-event-handler :trans (behavior () + (when (and *target* (type-type? (-> self type) crate-buzzer)) + (let ((current-frame (-> *display* base-frame-counter))) + (when (!= *closest-buzzer-frame* current-frame) + (set! *closest-buzzer-frame* current-frame) + (set! *closest-buzzer-dist* 1000000000.0)) + (let ((current-dist (vector-vector-distance (-> self root trans) (-> *target* control trans)))) + (when (< current-dist *closest-buzzer-dist*) + (set! *closest-buzzer-dist* current-dist))))) (cond ((not (send-event *target* 'query 'powerup (pickup-type eco-blue))) (logior! (-> self mask) (process-mask sleep-code)) @@ -582,6 +597,7 @@ (f30-0 (vector-vector-distance (-> gp-2 world-sphere) (-> a1-3 world-sphere)))) (when (and (< f30-0 (-> *FACT-bank* suck-suck-dist)) (!= (-> self defense) 'steel)) (logior! (-> self fact options) (fact-options can-collect)) + (mod-rule-trigger-blue-crate) (go-virtual die #f 0)) (when (rand-vu-percent? 0.5) (let ((s5-0 (new 'stack-no-clear 'vector))) @@ -638,6 +654,7 @@ (logior! (-> self fact options) (fact-options instant-collect))) (when (not arg0) (suspend-for (seconds 0.04)) + (runs-on-crate-break self 'break 1) (logior! (-> self draw status) (draw-status hidden)) (case (-> self look) (('iron) (sound-play "icrate-break")) @@ -929,12 +946,20 @@ :virtual #t :trans (behavior () - (when (and (and *target* (>= 327680.0 (vector-vector-distance (-> self root trans) (-> *target* control trans)))) - (time-elapsed? (-> self state-time) (seconds 1.5)) - (rand-vu-percent? 0.03)) - (spawn (-> self part) (-> self root trans)) - (activate! (-> self smush) 0.2 90 150 1.0 1.0) - (logclear! (-> self mask) (process-mask sleep-code))) + (when *target* + (let ((current-frame (-> *display* base-frame-counter))) + (when (!= *closest-buzzer-frame* current-frame) + (set! *closest-buzzer-frame* current-frame) + (set! *closest-buzzer-dist* 1000000000.0)) + (let ((current-dist (vector-vector-distance (-> self root trans) (-> *target* control trans)))) + (when (< current-dist *closest-buzzer-dist*) + (set! *closest-buzzer-dist* current-dist)) + (when (and (>= 327680.0 current-dist) + (time-elapsed? (-> self state-time) (seconds 1.5)) + (rand-vu-percent? 0.03)) + (spawn (-> self part) (-> self root trans)) + (activate! (-> self smush) 0.2 90 150 1.0 1.0) + (logclear! (-> self mask) (process-mask sleep-code)))))) (if (nonzero? (-> self sound)) (update! (-> self sound))) (if (and *target* (>= (-> *target* fact buzzer) 6.0)) (spool-push *art-control* (-> self victory-anim name) 0 self -99.0))) :code diff --git a/goal_src/jak1/engine/common-obs/nav-enemy.gc b/goal_src/jak1/engine/common-obs/nav-enemy.gc index dfa1c75b05..cf2c53bf6c 100644 --- a/goal_src/jak1/engine/common-obs/nav-enemy.gc +++ b/goal_src/jak1/engine/common-obs/nav-enemy.gc @@ -10,6 +10,9 @@ ;; DECOMP BEGINS +(define-extern mod-rule-trigger-mean-look (function none)) +(define-extern notify-enemy-death (function process-drawable none)) + (defun nav-enemy-rnd-float () (rand-vu)) @@ -540,7 +543,7 @@ nav-enemy-default-event-handler (vector-vector-distance (-> self collide-info trans) (-> *target* control trans)))) (let ((gp-0 'racer)) (= gp-0 (send-event *target* 'query 'mode))))) (go-virtual nav-enemy-flee)) - (if (nav-enemy-notice-player?) (go-virtual nav-enemy-notice)))) + (when (nav-enemy-notice-player?) (mod-rule-trigger-mean-look) (go-virtual nav-enemy-notice)))) :code (behavior () (let ((f30-0 (nav-enemy-rnd-float-range 0.9 1.1))) @@ -809,6 +812,7 @@ nav-enemy-default-event-handler :event process-drawable-death-event-handler :enter (behavior () + (notify-enemy-death self) (send-event (ppointer->process (-> self parent)) 'child-die)) :code (behavior () diff --git a/goal_src/jak1/engine/common-obs/process-drawable.gc b/goal_src/jak1/engine/common-obs/process-drawable.gc index 46b1f0a126..cba6db9a5f 100644 --- a/goal_src/jak1/engine/common-obs/process-drawable.gc +++ b/goal_src/jak1/engine/common-obs/process-drawable.gc @@ -13,6 +13,12 @@ ;; DECOMP BEGINS +(define-extern runs-on-enemy-death (function process-drawable none)) + +(defun notify-enemy-death ((enemy process-drawable)) + (runs-on-enemy-death enemy) + (none)) + (defun cspace-by-name ((arg0 process-drawable) (arg1 string)) (let* ((s4-0 (-> arg0 node-list length)) (s3-0 0) diff --git a/goal_src/jak1/engine/common-obs/water.gc b/goal_src/jak1/engine/common-obs/water.gc index 9b161455ff..478b17a54d 100644 --- a/goal_src/jak1/engine/common-obs/water.gc +++ b/goal_src/jak1/engine/common-obs/water.gc @@ -9,6 +9,8 @@ ;; DECOMP BEGINS +(define-extern mod-rule-active? (function symbol symbol)) + (defpart 108 :init-specs ((:texture (water-ring effects)) @@ -988,13 +990,18 @@ :event (behavior ((proc process) (argc int) (message symbol) (block event-message-block)) (case message - (('update) (update! self)))) + (('update) + (if (not (mod-rule-active? 'complication-drought)) + (update! self) + (on-exit-water self))))) :exit (behavior () (on-exit-water self)) :trans (behavior () - (update! self)) + (if (not (mod-rule-active? 'complication-drought)) + (update! self) + (on-exit-water self))) :code anim-loop) (defmethod set-stack-size! ((this water-vol)) diff --git a/goal_src/jak1/engine/entity/entity.gc b/goal_src/jak1/engine/entity/entity.gc index 4a872866fd..fc7a7b7fc0 100644 --- a/goal_src/jak1/engine/entity/entity.gc +++ b/goal_src/jak1/engine/entity/entity.gc @@ -1040,9 +1040,9 @@ (set! (-> arg0 root trans quad) (-> arg1 extra trans quad)) (quaternion-copy! (-> arg0 root quat) (-> arg1 quat)) (vector-identity! (-> arg0 root scale)) - (let ((scale (res-lump-struct arg1 'scale vector))) - (when scale - (set-vector! (-> arg0 root scale) (-> scale x) (-> scale y) (-> scale z) 1.0))) + ;; (let ((scale (res-lump-struct arg1 'scale vector))) + ;; (when scale + ;; (set-vector! (-> arg0 root scale) (-> scale x) (-> scale y) (-> scale z) 1.0))) (none)) (defmethod update-perm! ((this entity-perm) (arg0 symbol) (arg1 entity-perm-status)) diff --git a/goal_src/jak1/engine/game/game-save.gc b/goal_src/jak1/engine/game/game-save.gc index 9292b31b9c..1ad5e0c4d6 100644 --- a/goal_src/jak1/engine/game/game-save.gc +++ b/goal_src/jak1/engine/game/game-save.gc @@ -18,6 +18,11 @@ (define-extern mark-speedrun-started (function none)) +;; mod: hooks for persisting the global countdown timer in the save file. +;; implemented in mod-common-functions.gc +(define-extern global-countdown-save-value (function int)) +(define-extern global-countdown-resume-from-save (function int none)) + ;; version identifier (defconstant SAVE_VERSION 1) @@ -92,7 +97,8 @@ (vibration 506) (play-hints 507) (video-mode 508) - (aspect-ratio 509)) + (aspect-ratio 509) + (mod-global-timer 510)) ;; DECOMP BEGINS @@ -578,7 +584,14 @@ ((= a1-125 'aspect4x3) 1) ((= a1-125 'aspect16x9) 2) (else 0))))) - (set! (-> arg0 length) (&- (&+ v1-112 16) (the-as uint (the-as pointer (-> arg0 tag)))))))))))))))))))))))))))))))))))))))))))))))))))))))) + ;; mod: persist the global countdown timer as one more tag + (let ((v1-113 (&+ v1-112 16))) + (let ((a0-89 (the-as game-save-tag (&+ v1-113 0)))) + (set! (-> a0-89 elt-type) (game-save-elt mod-global-timer)) + (set! (-> a0-89 elt-count) 0) + (set! (-> a0-89 user-uint64) + (the-as uint (global-countdown-save-value)))) + (set! (-> arg0 length) (&- (&+ v1-113 16) (the-as uint (the-as pointer (-> arg0 tag))))))))))))))))))))))))))))))))))))))))))))))))))))))))) (if (< (-> arg0 allocated-length) (-> arg0 length)) (format 0 "ERROR: SAVEGAME: fatal error, save is using ~D of ~D bytes." (-> arg0 length) (-> arg0 allocated-length))) (none)) @@ -709,7 +722,9 @@ (let ((v1-92 (min 32 (-> data elt-count)))) (dotimes (a0-133 v1-92) (set! (-> this fuel-cell-time a0-133) - (the-as time-frame (-> (the-as (pointer uint64) (&+ (&+ (the-as pointer data) 16) (* a0-133 8)))))))))) + (the-as time-frame (-> (the-as (pointer uint64) (&+ (&+ (the-as pointer data) 16) (* a0-133 8))))))))) + (((game-save-elt mod-global-timer)) + (global-countdown-resume-from-save (the-as int (-> data user-uint64))))) (set! data (the-as game-save-tag (&+ (the-as pointer data) (logand -16 (+ (* (the-as int (-> data elt-size)) (-> data elt-count)) 31))))))) diff --git a/goal_src/jak1/engine/mods/mod-common-functions.gc b/goal_src/jak1/engine/mods/mod-common-functions.gc index fb250f9667..caf37f7320 100644 --- a/goal_src/jak1/engine/mods/mod-common-functions.gc +++ b/goal_src/jak1/engine/mods/mod-common-functions.gc @@ -41,6 +41,12 @@ if the result of rand-vu-int-range is 1, then DANCE! if it is not 1, then Don't ;; add your own unique custom functions here! +;; COMMAND/FUNCTION REFERENCE +;; Start game: (global-countdown-start) +;; Stop game: (global-countdown-reset) +;; Force specific rule: (mod-rule-activate-slot 0 'rule-grounded) +;; Check if a specific slot is active: (mod-rule-slot-active? 0) + ;; Macros can be used more-or-less just like functions (defmacro current-cell-count () `(-> *game-info* fuel)) @@ -48,18 +54,6 @@ if the result of rand-vu-int-range is 1, then DANCE! if it is not 1, then Don't (defmacro set-current-cell-count (count) `(set! (-> *game-info* fuel) ,count)) -(defun increase-power-cell-by-one () - (set-current-cell-count (+ (current-cell-count) 1)) - ;; with the two macros defined above, this is equivalent to - ;; (set! (-> *game-info* fuel) (+ (-> *game-info* fuel) 1)) - (none)) - -;;;;;;;;;;;;;;;;;;;;;;;;;; -;; Define Approved Custom Functions/Macros to call in all mods -;;;;;;;;;;;;;;;;;;;;;;;;;; - -;; These are included with the mod base and you are welcome to use them in your mods! - (defmacro current-checkpoint-name () `(-> *game-info* current-continue name)) @@ -81,6 +75,2110 @@ if the result of rand-vu-int-range is 1, then DANCE! if it is not 1, then Don't (defmacro set-cpad-hold! (pad-idx &rest buttons) `(logior! (cpad-hold ,pad-idx) (pad-buttons ,@buttons))) +(defun increase-power-cell-by-one () + (set-current-cell-count (+ (current-cell-count) 1)) + ;; with the two macros defined above, this is equivalent to + ;; (set! (-> *game-info* fuel) (+ (-> *game-info* fuel) 1)) + (none)) +(define *global-countdown-active?* #f) +(define *global-countdown-lost?* #f) +(define *global-countdown-remaining-time* 0) +(define *global-countdown-last-update-time* 0) +(define *mod-rule-active-slots* (new 'global 'boxed-array symbol 3)) +(define *mod-rule-pending-slots* (new 'global 'boxed-array symbol 3)) +(define *mod-rule-slot-end-times* (new 'global 'boxed-array time-frame 3)) +(define *mod-rule-slot-roll-ready-times* (new 'global 'boxed-array time-frame 3)) +(define *mod-rule-slot-punishments* (new 'global 'boxed-array symbol 3)) +(define *mod-rule-slot-goals* (new 'global 'boxed-array int32 3)) +(define *mod-rule-slot-progress* (new 'global 'boxed-array int32 3)) +(define *mod-rule-recent-rolls* (new 'global 'boxed-array symbol 8)) +(define *mod-rule-unique-enemy-killed-types* (new 'global 'boxed-array symbol 3)) +(define *mod-rule-roll-buffer-time* (seconds 5)) +(define *mod-rule-last-update-time* 0) +(define *mod-rule-current-slot* 0) +(define *mod-rule-last-reverted-cell-task* 0) +(define *mod-rule-last-reverted-cell-until* 0) +(define *mod-rule-last-outcome* 'none) +(define *mod-rule-player-hit-handles* (new 'global 'boxed-array handle 64)) +(define *mod-rule-player-hit-modes* (new 'global 'boxed-array symbol 64)) +(define *mod-rule-player-hit-write-idx* 0) +(define *mod-rule-timed-punishments* (new 'global 'boxed-array symbol 4)) +(define *mod-rule-timed-punishment-end-times* (new 'global 'boxed-array time-frame 4)) +(define *mod-rule-timed-punishment-next-ticks* (new 'global 'boxed-array time-frame 4)) +(define *mod-rule-id-count* 32) ;; ***DON'T FORGET TO UPDATE THIS WITH NEW RULES*** +(define *mean-look-clear-time* 0) +(define *mod-rule-slot-grace-periods* (new 'global 'boxed-array time-frame 3)) +(define *mod-rule-all-cell-tasks* + (new 'static + 'boxed-array + :type + game-task + (game-task training-gimmie) + (game-task training-door) + (game-task training-climb) + (game-task training-buzzer) + (game-task village1-mayor-money) + (game-task village1-uncle-money) + (game-task village1-yakow) + (game-task village1-oracle-money1) + (game-task village1-oracle-money2) + (game-task village1-buzzer) + (game-task beach-ecorocks) + (game-task beach-flutflut) + (game-task beach-pelican) + (game-task beach-seagull) + (game-task beach-cannon) + (game-task beach-gimmie) + (game-task beach-sentinel) + (game-task beach-buzzer) + (game-task jungle-lurkerm) + (game-task jungle-tower) + (game-task jungle-eggtop) + (game-task jungle-plant) + (game-task jungle-fishgame) + (game-task jungle-canyon-end) + (game-task jungle-temple-door) + (game-task jungle-buzzer) + (game-task misty-muse) + (game-task misty-boat) + (game-task misty-cannon) + (game-task misty-warehouse) + (game-task misty-bike) + (game-task misty-bike-jump) + (game-task misty-eco-challenge) + (game-task misty-buzzer) + (game-task firecanyon-end) + (game-task firecanyon-buzzer) + (game-task village2-gambler-money) + (game-task village2-geologist-money) + (game-task village2-warrior-money) + (game-task village2-oracle-money1) + (game-task village2-oracle-money2) + (game-task village2-buzzer) + (game-task sunken-room) + (game-task sunken-pipe) + (game-task sunken-slide) + (game-task sunken-sharks) + (game-task sunken-platforms) + (game-task sunken-top-of-helix) + (game-task sunken-spinning-room) + (game-task sunken-buzzer) + (game-task swamp-flutflut) + (game-task swamp-billy) + (game-task swamp-battle) + (game-task swamp-tether-4) + (game-task swamp-tether-1) + (game-task swamp-tether-2) + (game-task swamp-tether-3) + (game-task swamp-buzzer) + (game-task rolling-moles) + (game-task rolling-robbers) + (game-task rolling-race) + (game-task rolling-lake) + (game-task rolling-plants) + (game-task rolling-ring-chase-1) + (game-task rolling-ring-chase-2) + (game-task rolling-buzzer) + (game-task ogre-boss) + (game-task ogre-end) + (game-task ogre-secret) + (game-task ogre-buzzer) + (game-task village3-miner-money1) + (game-task village3-miner-money2) + (game-task village3-miner-money3) + (game-task village3-miner-money4) + (game-task village3-oracle-money1) + (game-task village3-oracle-money2) + (game-task village3-extra1) + (game-task village3-buzzer) + (game-task snow-eggtop) + (game-task snow-ram) + (game-task snow-bumpers) + (game-task snow-cage) + (game-task snow-fort) + (game-task snow-ball) + (game-task snow-bunnies) + (game-task snow-buzzer) + (game-task cave-gnawers) + (game-task cave-dark-crystals) + (game-task cave-dark-climb) + (game-task cave-robot-climb) + (game-task cave-swing-poles) + (game-task cave-spider-tunnel) + (game-task cave-platforms) + (game-task cave-buzzer) + (game-task lavatube-end) + (game-task lavatube-buzzer) + (game-task citadel-sage-blue) + (game-task citadel-sage-red) + (game-task citadel-sage-yellow) + (game-task citadel-sage-green) + (game-task citadel-buzzer))) + +(define *mod-rule-revert-cell-task-blacklist* + (new 'static + 'boxed-array + :type + game-task + (game-task training-buzzer) + (game-task village1-buzzer) + (game-task village2-buzzer) + (game-task village3-buzzer) + (game-task beach-buzzer) + (game-task misty-buzzer) + (game-task jungle-buzzer) + (game-task firecanyon-buzzer) + (game-task sunken-buzzer) + (game-task rolling-buzzer) + (game-task swamp-buzzer) + (game-task ogre-buzzer) + (game-task snow-buzzer) + (game-task cave-buzzer) + (game-task lavatube-buzzer) + (game-task citadel-buzzer) + (game-task jungle-fishgame) + (game-task jungle-plant) + (game-task beach-cannon) + (game-task beach-ecorocks) + (game-task beach-pelican) + (game-task beach-seagull) + (game-task misty-cannon) + (game-task misty-bike) + (game-task rolling-robbers) + (game-task rolling-plants) + (game-task rolling-ring-chase-1) + (game-task rolling-ring-chase-2) + (game-task swamp-battle) + (game-task swamp-tether-4) + (game-task swamp-tether-1) + (game-task swamp-tether-2) + (game-task swamp-tether-3) + (game-task ogre-boss) + (game-task village3-extra1) + (game-task snow-ram) + (game-task snow-bumpers) + (game-task snow-ball) + (game-task cave-gnawers) + (game-task cave-dark-crystals) + (game-task citadel-sage-blue) + (game-task citadel-sage-red) + (game-task citadel-sage-yellow) + (game-task citadel-sage-green))) + +(defun mod-rule-name ((id symbol)) + (cond + ((eq? id 'rule-grounded) "GROUNDED") + ((eq? id 'rule-hands-off) "ANGER MANAGEMENT") + ((eq? id 'rule-die) "DIE") + ((eq? id 'rule-lurker-pressure) "BLOODLUST") + ((eq? id 'rule-hitlist) "HITLIST") + ((eq? id 'rule-crate-break) "BULL AND CHINA") + ((eq? id 'rule-money-pressure) "CASH GRAB") + ((eq? id 'rule-fuel-pressure) "CELL PRESSURE") + ((eq? id 'rule-buzzer-pressure) "BUZZER BEATER") + ((eq? id 'rule-flop-this) "FLOP THIS") + ((eq? id 'complication-brittle-bones) "BRITTLE BONES") + ((eq? id 'complication-rotten-eggs) "ROTTEN EGGS") + ((eq? id 'complication-eco-allergy) "ECO ALLERGY") + ((eq? id 'rule-keep-it-low) "KEEP IT LOW") + ((eq? id 'complication-sizeable) "SIZEABLE CONTRIBUTION") + ((eq? id 'complication-ohko) "OHKO") + ((eq? id 'complication-mean-look) "MEAN LOOK") + ((eq? id 'complication-heating-balloons) "HEATING BALLOONS") + ((eq? id 'complication-disassociate) "DISASSOCIATE") + ((eq? id 'complication-bus-driver) "BUS DRIVER") + ((eq? id 'complication-wings-tied) "WINGS TIED") + ((eq? id 'complication-fuel-shortage) "FUEL SHORTAGE") + ((eq? id 'complication-hidden-treasure) "HIDDEN TREASURE") + ((eq? id 'complication-drought) "LIKE A STONE") + ((eq? id 'complication-jak-rabbit) "JAK RABBIT") + ((eq? id 'rule-survivor) "SURVIVOR") + ((eq? id 'rule-red-kill) "RED ECO KILL") + ((eq? id 'rule-blue-crate) "HANDS ON") + ((eq? id 'complication-yellow-pilled) "YELLOW-PILLED") + ((eq? id 'complication-red-light) "RED LIGHT") + ((eq? id 'rule-one-frame) "ONE FRAME") + ((eq? id 'rule-distancing) "SOCIAL DISTANCING") + (else "UNKNOWN"))) + +(defun mod-rule-description ((id symbol)) + (cond + ((eq? id 'rule-grounded) "DO NOT JUMP.") + ((eq? id 'rule-hands-off) "DO NOT PUNCH.") + ((eq? id 'rule-die) "KILL HIM. KILL HIM NOW.") + ((eq? id 'rule-lurker-pressure) "KILL 6 LURKERS.") + ((eq? id 'rule-hitlist) "KILL 3 UNIQUE LURKER TYPES.") + ((eq? id 'rule-crate-break) "BREAK 5 CRATES.") + ((eq? id 'rule-money-pressure) "COLLECT 20 ORBS.") + ((eq? id 'rule-fuel-pressure) "COLLECT A POWER CELL.") + ((eq? id 'rule-buzzer-pressure) "COLLECT 2 SCOUT FLIES.") + ((eq? id 'rule-flop-this) "LURKERS MAY ONLY BE KILLED WITH GROUND POUNDS.") + ((eq? id 'complication-brittle-bones) "JUMPS HAVE A 10% CHANCE TO DAMAGE YOU.") + ((eq? id 'complication-rotten-eggs) "ORBS KILL YOU.") + ((eq? id 'complication-eco-allergy) "ECO DAMAGES YOU.") + ((eq? id 'rule-keep-it-low) "DO NOT JUMP KICK.") + ((eq? id 'complication-sizeable) "ROLLJUMPS COST 3 ORBS.") + ((eq? id 'complication-heating-balloons) "COOLING BALLOONS MELT YOU.") + ((eq? id 'complication-drought) "JAK FORGOT HOW TO SWIM.") + ((eq? id 'complication-ohko) "JAK HAS ONE HEALTH.") + ((eq? id 'complication-mean-look) "JAK FREEZES WHEN LURKERS SEE HIM.") + ((eq? id 'complication-disassociate) "KILLING A LURKER DISMOUNTS FLUT FLUT.") + ((eq? id 'complication-bus-driver) "ZOOMER EXPLODES IF TOO SLOW.") + ((eq? id 'complication-fuel-shortage) "ALL POWER CELLS ARE GONE.") + ((eq? id 'complication-hidden-treasure) "ORBS ARE INVISIBLE.") + ((eq? id 'complication-wings-tied) "YOU CANNOT COLLECT CELLS ON FLUT FLUT.") + ((eq? id 'complication-jak-rabbit) "JUMP HEIGHTS ARE DOUBLED.") + ((eq? id 'rule-red-kill) "KILL A LURKER WITH RED ECO.") + ((eq? id 'rule-blue-crate) "DO NOT BREAK ANY CRATES WITH BLUE ECO.") + ((eq? id 'rule-survivor) "STAY ALIVE.") + ((eq? id 'complication-yellow-pilled) "YELLOW ECO PAUSES TIMER.") + ((eq? id 'complication-red-light) "RED ECO PAUSES TIMER.") + ((eq? id 'rule-one-frame) "DO A ONE-FRAME.") + ((eq? id 'rule-distancing) "DO NOT GET WITHIN 8 METERS OF A FLY CRATE.") + (else "NO DESCRIPTION AVAILABLE."))) + +(defun mod-rule-duration ((id symbol)) + (cond + ((eq? id 'rule-grounded) (seconds 20)) ;; 20 + ((eq? id 'rule-hands-off) (seconds 90)) ;; 90 + ((eq? id 'rule-die) (seconds 20)) ;; 20 + ((eq? id 'rule-lurker-pressure) (seconds 60)) ;; 60 + ((eq? id 'rule-hitlist) (seconds 45)) ;; 45 + ((eq? id 'rule-money-pressure) (seconds 30)) ;; 30 + ((eq? id 'rule-fuel-pressure) (seconds 30)) ;; 30 + ((eq? id 'rule-buzzer-pressure) (seconds 45)) ;; 45 + ((eq? id 'rule-flop-this) (seconds 90)) ;; 90 + ((eq? id 'complication-brittle-bones) (seconds 75)) ;; 75 + ((eq? id 'complication-rotten-eggs) (seconds 60)) ;; 60 + ((eq? id 'complication-eco-allergy) (seconds 60)) ;; 60 + ((eq? id 'rule-keep-it-low) (seconds 90)) ;; 90 + ((eq? id 'complication-sizeable) (seconds 60)) ;; 60 + ((eq? id 'complication-ohko) (seconds 60)) ;; 60 + ((eq? id 'complication-heating-balloons) (seconds 30)) ;; 30 + ((eq? id 'complication-mean-look) (seconds 60)) ;; 60 + ((eq? id 'complication-disassociate) (seconds 45)) ;; 45 + ((eq? id 'complication-bus-driver) (seconds 30)) ;; 30 + ((eq? id 'complication-wings-tied) (seconds 60)) ;; 60 + ((eq? id 'rule-survivor) (seconds 75)) ;; 75 + ((eq? id 'complication-fuel-shortage) (seconds 45)) ;; 45 + ((eq? id 'complication-hidden-treasure) (seconds 60)) ;; 60 + ((eq? id 'complication-drought) (seconds 45)) ;; 45 + ((eq? id 'complication-jak-rabbit) (seconds 60)) ;; 60 + ((eq? id 'rule-red-kill) (seconds 45)) ;; 45 + ((eq? id 'rule-blue-crate) (seconds 60)) ;; 60 + ((eq? id 'complication-yellow-pilled) (seconds 60)) ;; 60 + ((eq? id 'complication-red-light) (seconds 60)) ;; 60 + ((eq? id 'rule-one-frame) (seconds 45)) ;; 45 + ((eq? id 'rule-distancing) (seconds 75)) ;; 75 + (else (seconds 30)))) + +(defun mod-rule-counter-goal ((id symbol)) + (cond + ((eq? id 'rule-die) 1) + ((eq? id 'rule-lurker-pressure) 6) + ((eq? id 'rule-hitlist) 3) + ((eq? id 'rule-crate-break) 5) + ((eq? id 'rule-money-pressure) 20) + ((eq? id 'rule-fuel-pressure) 1) + ((eq? id 'rule-buzzer-pressure) 2) + ((eq? id 'rule-red-kill) 1) + ((eq? id 'rule-one-frame) 1) + (else 0))) + +(defun mod-rule-goal-kind ((id symbol)) + (cond + ((eq? id 'rule-lurker-pressure) 'counter) + ((eq? id 'rule-hitlist) 'counter) + ((eq? id 'rule-crate-break) 'counter) + ((eq? id 'rule-money-pressure) 'counter) + ((eq? id 'rule-fuel-pressure) 'event-once) + ((eq? id 'rule-buzzer-pressure) 'counter) + ((eq? id 'rule-die) 'event-once) + ((eq? id 'rule-one-frame) 'event-once) + ((eq? id 'rule-red-kill) 'event-once) + (else 'none))) + +(defun mod-rule-type ((id symbol)) + (cond + ((or (eq? id 'rule-keep-it-low) + (eq? id 'rule-die) + (eq? id 'rule-lurker-pressure) + (eq? id 'rule-hitlist) + (eq? id 'rule-crate-break) + (eq? id 'rule-money-pressure) + (eq? id 'rule-fuel-pressure) + (eq? id 'rule-buzzer-pressure) + (eq? id 'rule-flop-this) + (eq? id 'rule-grounded) + (eq? id 'rule-survivor) + (eq? id 'rule-hands-off) + (eq? id 'rule-red-kill) + (eq? id 'rule-blue-crate) + (eq? id 'rule-one-frame) + (eq? id 'rule-distancing) + ) + 'rule) + ((or + (eq? id 'complication-brittle-bones) + (eq? id 'complication-rotten-eggs) + (eq? id 'complication-eco-allergy) + (eq? id 'complication-sizeable) + (eq? id 'complication-ohko) + (eq? id 'complication-heating-balloons) + (eq? id 'complication-mean-look) + (eq? id 'complication-disassociate) + (eq? id 'complication-bus-driver) + (eq? id 'complication-wings-tied) + (eq? id 'complication-fuel-shortage) + (eq? id 'complication-jak-rabbit) + (eq? id 'complication-hidden-treasure) + (eq? id 'complication-drought) + (eq? id 'complication-yellow-pilled) + (eq? id 'complication-red-light) + ) + + 'complication) + (else 'unknown))) + +(defun mod-rule-active? ((id symbol)) + (let ((slot 0) (found #f)) + (while (and (< slot 3) (not found)) + (when (= (-> *mod-rule-active-slots* slot) id) + (set! found #t)) + (+! slot 1)) + found)) + +(defun mod-rule-pending? ((id symbol)) + (let ((slot 0) (found #f)) + (while (and (< slot 3) (not found)) + (when (= (-> *mod-rule-pending-slots* slot) id) + (set! found #t)) + (+! slot 1)) + found)) + +(defun mod-rule-active-slot-count () + "Count active and pending slots. Do not cache this: pending rolls are state too." + (let ((slot 0) + (count 0)) + (while (< slot 3) + (when (or (-> *mod-rule-active-slots* slot) + (-> *mod-rule-pending-slots* slot)) + (+! count 1)) + (+! slot 1)) + count)) + +(defun mod-rule-first-free-slot () + (let ((slot 0) + (result -1)) + (while (and (< slot 3) (< result 0)) + (when (and (not (-> *mod-rule-active-slots* slot)) + (not (-> *mod-rule-pending-slots* slot))) + (set! result slot)) + (+! slot 1)) + result)) + +(defun mod-rule-first-occupied-slot () + (let ((slot 0) + (result -1)) + (while (and (< slot 3) (< result 0)) + (when (or (-> *mod-rule-active-slots* slot) + (-> *mod-rule-pending-slots* slot)) + (set! result slot)) + (+! slot 1)) + result)) + +(defun mod-rule-find-active-slot ((id symbol)) + (let ((slot 0) + (result -1)) + (while (and (< slot 3) (< result 0)) + (when (= (-> *mod-rule-active-slots* slot) id) + (set! result slot)) + (+! slot 1)) + result)) + +(defun mod-rule-clear-slot ((slot int)) + (when (and (>= slot 0) (< slot 3)) + (set! (-> *mod-rule-active-slots* slot) #f) + (set! (-> *mod-rule-pending-slots* slot) #f) + (set! (-> *mod-rule-slot-punishments* slot) #f) + (set! (-> *mod-rule-slot-goals* slot) 0) + (set! (-> *mod-rule-slot-progress* slot) 0) + (set! (-> *mod-rule-slot-end-times* slot) 0) + (set! (-> *mod-rule-slot-roll-ready-times* slot) 0) + (set! (-> *mod-rule-slot-grace-periods* slot) 0)) + (none)) + +(defun mod-rule-punishment-duration ((punishment symbol)) + (cond + ((eq? punishment 'punishment-blind) (seconds 10)) + ((eq? punishment 'punishment-fall-damage) (seconds 120)) + ((eq? punishment 'punishment-trip) (seconds 120)) + (else 0))) + +(defun mod-rule-find-timed-punishment-slot ((punishment symbol)) + (let ((slot 0) + (result -1)) + (while (and (< slot 4) (< result 0)) + (when (= (-> *mod-rule-timed-punishments* slot) punishment) + (set! result slot)) + (+! slot 1)) + result)) + +(defun mod-rule-first-free-timed-punishment-slot () + (let ((slot 0) + (result -1)) + (while (and (< slot 4) (< result 0)) + (when (not (-> *mod-rule-timed-punishments* slot)) + (set! result slot)) + (+! slot 1)) + result)) + +(defun mod-rule-start-timed-punishment ((punishment symbol)) + (let ((duration (mod-rule-punishment-duration punishment))) + (when (> duration 0) + (let ((slot (mod-rule-find-timed-punishment-slot punishment))) + (when (< slot 0) + (set! slot (mod-rule-first-free-timed-punishment-slot))) + (when (>= slot 0) + (set! (-> *mod-rule-timed-punishments* slot) punishment) + (set! (-> *mod-rule-timed-punishment-end-times* slot) + (+ (current-time) duration)) + (set! (-> *mod-rule-timed-punishment-next-ticks* slot) + (+ (current-time) (seconds 1))))))) + (none)) + +(defun mod-rule-timed-punishment-active? ((punishment symbol)) + (let ((slot (mod-rule-find-timed-punishment-slot punishment))) + (and (>= slot 0) + (> (-> *mod-rule-timed-punishment-end-times* slot) (current-time))))) + +(defun mod-rule-clear-timed-punishment-slot ((slot int)) + (set! (-> *mod-rule-timed-punishments* slot) #f) + (set! (-> *mod-rule-timed-punishment-end-times* slot) 0) + (set! (-> *mod-rule-timed-punishment-next-ticks* slot) 0) + (none)) + +(defun mod-rule-end-timed-punishment ((punishment symbol)) + (case punishment + (('punishment-fall-damage) + (set! (-> *TARGET-bank* fall-far) (meters 30)) + (set! (-> *TARGET-bank* fall-far-inc) (meters 20)))) + (none)) + +(defun mod-rule-update-timed-punishments () + (dotimes (slot 4) + (let ((punishment (-> *mod-rule-timed-punishments* slot))) + (when punishment + (if (>= (current-time) (-> *mod-rule-timed-punishment-end-times* slot)) + (begin + (mod-rule-end-timed-punishment punishment) + (mod-rule-clear-timed-punishment-slot slot)) + (begin + (when (and (eq? punishment 'punishment-trip) + (>= (current-time) (-> *mod-rule-timed-punishment-next-ticks* slot))) + (set! (-> *mod-rule-timed-punishment-next-ticks* slot) + (+ (-> *mod-rule-timed-punishment-next-ticks* slot) (seconds 1))) + (when (= (knuth-rand-int-range 0 9) 0) + (send-event *target* 'loading)))))))) + (none)) + +(defun mod-rule-schedule-slot ((slot int) (id symbol) (apply-buffer symbol)) + (when (and (>= slot 0) (< slot 3) id) + (set! (-> *mod-rule-pending-slots* slot) id) + (set! (-> *mod-rule-slot-roll-ready-times* slot) (+ (current-time) (if apply-buffer *mod-rule-roll-buffer-time* 0)))) + (none)) + +(defun mod-rule-current-active-id () + (let ((slot 0) (found #f) (result #f)) + (while (and (< slot 3) (not found)) + (let ((candidate (-> *mod-rule-active-slots* slot))) + (when candidate + (set! result candidate) + (set! found #t))) + (+! slot 1)) + result)) + +(defun mod-rule-current-time-left () + (let ((slot 0) (found #f) (result 0)) + (while (and (< slot 3) (not found)) + (let ((candidate (-> *mod-rule-active-slots* slot))) + (when candidate + (set! result (max 0 (- (-> *mod-rule-slot-end-times* slot) (current-time)))) + (set! found #t))) + (+! slot 1)) + result)) + +(defun mod-rule-format-time-left ((time-left time-frame)) + (let* ((ticks (max 0 (the int time-left))) + (total-seconds (the int (/ ticks (seconds 1)))) + (minutes (the int (/ total-seconds 60))) + (seconds-part (the int (mod total-seconds 60))) + (deciseconds (the int (/ (* (mod ticks (seconds 1)) 10) + (seconds 1))))) + (string-format "~D:~2,'0D.~1,'0D" + minutes + seconds-part + deciseconds))) + + +(defun mod-rule-task-has-fuel-cell-entity? ((task game-task)) + (let ((level-idx 0) + (found #f)) + (while (and (< level-idx (-> *level* length)) (not found)) + (let* ((lev (-> *level* level level-idx)) + (entities (-> lev bsp level entity)) + (entity-idx 0)) + (while (and (< entity-idx (-> entities length)) (not found)) + (let ((ent (-> entities data entity-idx entity))) + (when (and ent + (type-type? (-> ent type) entity-actor) + (= (-> (the-as entity-actor ent) etype) fuel-cell) + (= (-> ent extra perm task) task)) + (set! found #t))) + (+! entity-idx 1))) + (+! level-idx 1)) + found)) + +(defun mod-rule-cell-task-complete? ((task game-task)) + (and (nonzero? task) + (!= task (game-task complete)) + (task-complete? *game-info* task))) + +(defun mod-rule-cell-task-blacklisted? ((task game-task)) + (let ((idx 0) + (blocked #f)) + (while (and (< idx (length *mod-rule-revert-cell-task-blacklist*)) (not blocked)) + (when (= (-> *mod-rule-revert-cell-task-blacklist* idx) task) + (set! blocked #t)) + (+! idx 1)) + blocked)) + +(defun mod-rule-cell-task-revert-eligible? ((task game-task)) + (and (mod-rule-cell-task-complete? task) + (not (mod-rule-cell-task-blacklisted? task)))) + +(defun mod-rule-collected-cell-candidate-count () + (let ((idx 0) (count 0)) + (while (< idx (length *mod-rule-all-cell-tasks*)) + (when (mod-rule-cell-task-revert-eligible? (-> *mod-rule-all-cell-tasks* idx)) + (+! count 1)) + (+! idx 1)) + count)) + +(defun mod-rule-can-revert-collected-cell? () + (> (mod-rule-collected-cell-candidate-count) 0)) + +(defun mod-rule-reopen-task-stage ((task game-task)) + ;; (when (task-closed? task (task-status need-hint)) + ;; (open-specific-task! task (task-status need-hint))) + ;; (when (task-closed? task (task-status need-introduction)) + ;; (open-specific-task! task (task-status need-introduction))) + ;; (when (task-closed? task (task-status need-reminder-a)) + ;; (open-specific-task! task (task-status need-reminder-a))) + ;; (when (task-closed? task (task-status need-reminder)) + ;; (open-specific-task! task (task-status need-reminder))) + (when (task-closed? task (task-status need-reward-speech)) + (open-specific-task! task (task-status need-reward-speech))) + (when (task-closed? task (task-status need-resolution)) + (open-specific-task! task (task-status need-resolution))) + (when (task-closed? task (task-status unknown)) + (open-specific-task! task (task-status unknown))) + (none)) + + (defun mod-rule-force-open-task-stage ((task game-task)) + ;; (when (task-exists? task (task-status need-hint)) + ;; (open-specific-task! task (task-status need-hint))) + ;; (when (task-exists? task (task-status need-introduction)) + ;; (open-specific-task! task (task-status need-introduction))) + ;; (when (task-exists? task (task-status need-reminder-a)) + ;; (open-specific-task! task (task-status need-reminder-a))) + ;; (when (task-exists? task (task-status need-reminder)) + ;; (open-specific-task! task (task-status need-reminder))) + (when (task-exists? task (task-status need-reward-speech)) + (open-specific-task! task (task-status need-reward-speech))) + (when (task-exists? task (task-status need-resolution)) + (open-specific-task! task (task-status need-resolution))) + (when (task-exists? task (task-status unknown)) + (open-specific-task! task (task-status unknown))) + (none)) + +(defun mod-rule-revert-random-collected-cell () + (let ((candidate-count (mod-rule-collected-cell-candidate-count))) + (when (> candidate-count 0) + (let* ((pick (knuth-rand-int-range 0 candidate-count)) + (remaining pick) + (idx 0) + (chosen-task (game-task none))) + (while (and (< idx (length *mod-rule-all-cell-tasks*)) (= chosen-task (game-task none))) + (let ((task (-> *mod-rule-all-cell-tasks* idx))) + (when (mod-rule-cell-task-revert-eligible? task) + (if (= remaining 0) + (set! chosen-task task) + (+! remaining -1)))) + (+! idx 1)) + (when (!= chosen-task (game-task none)) + (update-perm! (-> *game-info* task-perm-list data chosen-task) + 'game + (the-as entity-perm-status 1919)) + (logclear! (-> *game-info* task-perm-list data chosen-task status) + (entity-perm-status real-complete)) + (mod-rule-reopen-task-stage chosen-task) + (mod-rule-force-open-task-stage chosen-task) + (let ((perm-list (-> *game-info* perm-list)) + (idx2 0)) + (while (< idx2 (-> perm-list length)) + (let ((perm2 (-> perm-list data idx2))) + (when (= (-> perm2 task) chosen-task) + (update-perm! perm2 'game (the-as entity-perm-status 1919)))) + (+! idx2 1))) + (let ((level-idx 0) + (spawned #f)) + (while (< level-idx (-> *level* length)) + (let ((lev (-> *level* level level-idx))) + (when (= (-> lev status) 'active) + (let ((entities (-> lev bsp level entity)) + (entity-idx 0)) + (while (< entity-idx (-> entities length)) + (let ((ent (-> entities data entity-idx entity))) + (when (and ent + (type-type? (-> ent type) entity-actor) + (= (-> (the-as entity-actor ent) etype) fuel-cell) + (= (-> ent extra perm task) chosen-task)) + (update-perm! (-> ent extra perm) + 'game + (the-as entity-perm-status 1919)) + (when (not spawned) + (entity-birth-no-kill ent) + (set! spawned #t)))) + (+! entity-idx 1))))) + (+! level-idx 1))) + + (set! (-> *game-info* fuel) (max 0.0 (- (-> *game-info* fuel) 1.0))) + (set! *mod-rule-last-reverted-cell-task* (the int chosen-task)) + (set! *mod-rule-last-reverted-cell-until* (+ (current-time) (seconds 5))))))) + (none)) + +(defun target-on-racer? () + (if (not *target*) + #f + (let ((mode (send-event *target* 'query 'mode))) + (= mode 'racer)))) + +(defun target-on-flut? () + (if (not *target*) + #f + (let ((mode (send-event *target* 'query 'mode))) + (= mode 'flut)))) + +(defun target-on-racer-or-flut? () + (or (target-on-racer?) + (target-on-flut?))) + +(defun mod-rule-punishment-used-by-active-slot? ((punishment symbol)) + (let ((slot 0) + (used #f)) + (while (and (< slot 3) (not used)) + (when (and (-> *mod-rule-active-slots* slot) + (eq? (-> *mod-rule-slot-punishments* slot) punishment)) + (set! used #t)) + (+! slot 1)) + used)) + +(defun mod-rule-punishment-rollable? ((punishment symbol)) + (and (not (mod-rule-timed-punishment-active? punishment)) + (not (mod-rule-punishment-used-by-active-slot? punishment)))) + +(defun mod-rule-random-available-punishment () + (let ((has-money (>= (-> *game-info* money) 30.0)) + (has-warp (!= (-> (level-get-target-inside *level*) name) 'training)) + (has-revert (mod-rule-can-revert-collected-cell?)) + (can-add-extra-rule (< (mod-rule-active-slot-count) 2)) + (can-countdown (mod-rule-punishment-rollable? 'punishment-countdown)) + (can-fall-damage (and (not (target-on-racer-or-flut?)) (mod-rule-punishment-rollable? 'punishment-fall-damage))) + (can-trip (and (not (target-on-racer-or-flut?)) (mod-rule-punishment-rollable? 'punishment-trip))) + (can-money (mod-rule-punishment-rollable? 'punishment-money)) + (can-warp (mod-rule-punishment-rollable? 'punishment-warp)) + (can-revert (mod-rule-punishment-rollable? 'punishment-revert-cell)) + (can-extra-rule (mod-rule-punishment-rollable? 'punishment-extra-rule)) + (can-blind (mod-rule-punishment-rollable? 'punishment-blind)) + (count 0) + (roll 0)) + + ;; Count available punishments + (when can-countdown + (+! count 1)) + + (when can-fall-damage + (+! count 1)) + + (when can-trip + (+! count 1)) + + ;; (when can-blind + ;; (+! count 1)) + + (when (and has-money can-money) + (+! count 1)) + + (when (and has-warp can-warp) + (+! count 1)) + + (when (and has-revert can-revert) + (+! count 1)) + + (when (and can-add-extra-rule can-extra-rule) + (+! count 1)) + + ;; If every option got excluded (e.g. the other active rule already has + ;; countdown, we're on flut/racer, low on orbs, in training, nothing to + ;; revert, and a second rule is already active), fall back to countdown + ;; unconditionally rather than leaving the rule with no punishment. + (when (<= count 0) + (return 'punishment-countdown)) + + (set! roll (knuth-rand-int-range 0 count)) + + ;; Walk through the available punishments in order. + (set! count 0) + + (when can-countdown + (when (= roll count) + (return 'punishment-countdown)) + (+! count 1)) + + (when can-fall-damage + (when (= roll count) + (return 'punishment-fall-damage)) + (+! count 1)) + + (when can-trip + (when (= roll count) + (return 'punishment-trip)) + (+! count 1)) + + (when (and has-money can-money) + (when (= roll count) + (return 'punishment-money)) + (+! count 1)) + + (when (and has-warp can-warp) + (when (= roll count) + (return 'punishment-warp)) + (+! count 1)) + + (when (and has-revert can-revert) + (when (= roll count) + (return 'punishment-revert-cell)) + (+! count 1)) + + (when (and can-add-extra-rule can-extra-rule) + (when (= roll count) + (return 'punishment-extra-rule)) + (+! count 1)) + + ;; (when can-blind + ;; (when (= roll count) + ;; (return 'punishment-blind)) + ;; (+! count 1)) + + #f)) + +(defun mod-rule-on-activate ((id symbol)) + (cond + ((eq? id 'rule-hitlist) + (dotimes (slot 3) + (set! (-> *mod-rule-unique-enemy-killed-types* slot) #f))) + + ((eq? id 'rule-distancing) + (set! *closest-buzzer-dist* 1000000000.0)) + + ((eq? id 'complication-jak-rabbit) + (set! (-> *TARGET-bank* jump-height-min) (meters 2.02)) + (set! (-> *TARGET-bank* jump-height-max) (meters 7)) + (set! (-> *TARGET-bank* double-jump-height-min) (meters 2)) + (set! (-> *TARGET-bank* double-jump-height-max) (meters 5)) + (set! (-> *TARGET-bank* flip-jump-height-min) (meters 10)) + (set! (-> *TARGET-bank* flip-jump-height-max) (meters 14)) + (set! (-> *TARGET-bank* duck-jump-height-min) (meters 10)) + (set! (-> *TARGET-bank* duck-jump-height-max) (meters 14)) + (set! (-> *TARGET-bank* flop-jump-height-min) (meters 10)) + (set! (-> *TARGET-bank* flop-jump-height-max) (meters 14)) + (set! (-> *TARGET-bank* swim-jump-height-min) (meters 10)) + (set! (-> *TARGET-bank* swim-jump-height-max) (meters 10)) + (set! (-> *TARGET-bank* tube-jump-height-min) (meters 3.5)) + (set! (-> *TARGET-bank* tube-jump-height-max) (meters 5)) + (set! (-> *FLUT-bank* jump-height-min) (meters 10)) + (set! (-> *FLUT-bank* jump-height-max) (meters 14)) + (set! (-> *FLUT-bank* double-jump-height-min) (meters 2)) + (set! (-> *FLUT-bank* double-jump-height-max) (meters 4))) + + (else + (none))) + (none)) + +(defun refresh-money-actors () + (iterate-process-tree *active-pool* + (lambda ((p process)) + (when (type-type? (-> p type) money) + (logclear! (-> (the-as money p) draw status) (draw-status hidden)) + (restore-collide-with-as (-> (the-as money p) root)))) + *null-kernel-context*)) + +(defun mod-rule-deactivate-slot ((slot int)) + (when (and (>= slot 0) (< slot 3)) + (let ((id (-> *mod-rule-active-slots* slot))) + (when id + (cond + ((eq? id 'complication-ohko) + (set! (-> (the-as fact-info-target (-> *target* fact)) health) 3.0)) + ((eq? id 'complication-hidden-treasure) + (refresh-money-actors)) + ((eq? id 'complication-jak-rabbit) + (set! (-> *TARGET-bank* jump-height-min) (meters 1.01)) + (set! (-> *TARGET-bank* jump-height-max) (meters 3.5)) + (set! (-> *TARGET-bank* double-jump-height-min) (meters 1)) + (set! (-> *TARGET-bank* double-jump-height-max) (meters 2.5)) + (set! (-> *TARGET-bank* flip-jump-height-min) (meters 5)) + (set! (-> *TARGET-bank* flip-jump-height-max) (meters 7)) + (set! (-> *TARGET-bank* duck-jump-height-min) (meters 5)) + (set! (-> *TARGET-bank* duck-jump-height-max) (meters 7)) + (set! (-> *TARGET-bank* flop-jump-height-min) (meters 5)) + (set! (-> *TARGET-bank* flop-jump-height-max) (meters 7)) + (set! (-> *TARGET-bank* swim-jump-height-min) (meters 5)) + (set! (-> *TARGET-bank* swim-jump-height-max) (meters 5)) + (set! (-> *TARGET-bank* tube-jump-height-min) (meters 1.75)) + (set! (-> *TARGET-bank* tube-jump-height-max) (meters 2.5)) + (set! (-> *FLUT-bank* jump-height-min) (meters 5)) + (set! (-> *FLUT-bank* jump-height-max) (meters 7)) + (set! (-> *FLUT-bank* double-jump-height-min) (meters 1)) + (set! (-> *FLUT-bank* double-jump-height-max) (meters 2)))))) + (mod-rule-clear-slot slot)) + (none)) + +(defun mod-rule-activate-slot ((slot int) (id symbol)) + (when (and (>= slot 0) (< slot 3) id) + (mod-rule-deactivate-slot slot) + (set! (-> *mod-rule-active-slots* slot) id) + (set! *mod-rule-last-outcome* 'none) + (sound-play "warning") + (set! (-> *mod-rule-slot-goals* slot) (mod-rule-counter-goal id)) + (set! (-> *mod-rule-slot-progress* slot) 0) + (if (eq? (mod-rule-type id) 'rule) + (set! (-> *mod-rule-slot-punishments* slot) + (mod-rule-random-available-punishment)) + (set! (-> *mod-rule-slot-punishments* slot) #f)) + (set! (-> *mod-rule-slot-end-times* slot) (+ (current-time) (mod-rule-duration id))) + (set! (-> *mod-rule-slot-grace-periods* slot) (+ (current-time) (seconds 1))) + (mod-rule-on-activate id)) + (none)) + +(defun mod-rule-clear-all-timed-punishments () + (dotimes (slot 4) + (let ((punishment (-> *mod-rule-timed-punishments* slot))) + (when punishment + (mod-rule-end-timed-punishment punishment)) + (mod-rule-clear-timed-punishment-slot slot))) + (none)) + +(defun mod-rule-clear-player-hit-cache () + (dotimes (idx 64) + (set! (-> *mod-rule-player-hit-handles* idx) (the-as handle #f)) + (set! (-> *mod-rule-player-hit-modes* idx) #f)) + (set! *mod-rule-player-hit-write-idx* 0) + (none)) + +(defun mod-rule-clear-recent-rolls () + (dotimes (slot 8) + (set! (-> *mod-rule-recent-rolls* slot) #f)) + (none)) + +(defun mod-rules-reset () + (dotimes (slot 3) + (mod-rule-deactivate-slot slot)) + (mod-rule-clear-all-timed-punishments) + (dotimes (slot 3) + (set! (-> *mod-rule-unique-enemy-killed-types* slot) #f)) + (mod-rule-clear-player-hit-cache) + (mod-rule-clear-recent-rolls) + (set! *mod-rule-current-slot* 0) + (none)) + +(defun mod-rule-id-from-index ((idx int)) + (cond + ((= idx 0) 'rule-grounded) + ((= idx 1) 'rule-die) + ((= idx 2) 'rule-lurker-pressure) + ((= idx 3) 'rule-hitlist) + ((= idx 4) 'rule-crate-break) + ((= idx 5) 'complication-brittle-bones) + ((= idx 6) 'complication-rotten-eggs) + ((= idx 7) 'rule-keep-it-low) + ((= idx 8) 'rule-money-pressure) + ((= idx 9) 'rule-fuel-pressure) + ((= idx 10) 'complication-eco-allergy) + ((= idx 11) 'complication-sizeable) + ((= idx 12) 'complication-ohko) + ((= idx 13) 'complication-mean-look) + ((= idx 14) 'complication-heating-balloons) + ((= idx 15) 'rule-flop-this) + ((= idx 16) 'complication-disassociate) + ((= idx 17) 'complication-bus-driver) + ((= idx 18) 'rule-buzzer-pressure) + ((= idx 19) 'complication-wings-tied) + ((= idx 20) 'rule-survivor) + ((= idx 21) 'complication-fuel-shortage) + ((= idx 22) 'complication-drought) + ((= idx 23) 'rule-hands-off) + ((= idx 24) 'complication-jak-rabbit) + ((= idx 25) 'complication-hidden-treasure) + ((= idx 26) 'rule-red-kill) + ((= idx 27) 'rule-blue-crate) + ((= idx 28) 'complication-yellow-pilled) + ((= idx 29) 'rule-one-frame) + ((= idx 30) 'rule-distancing) + ((= idx 31) 'complication-red-light) + (else #f))) + +(defun mod-rule-random-id () + (mod-rule-id-from-index (knuth-rand-int-range 0 *mod-rule-id-count*))) + +(defun current-level-name () + (and *target* + (-> *target* current-level) + (-> *target* current-level name))) + +(defun collected-money-for-level ((lvl-idx int)) + (-> *game-info* money-per-level lvl-idx)) + +(defun collected-money-current-level () + (if (and *target* (-> *target* current-level)) + (let ((lvl-idx (-> *level-task-data-remap* (+ (-> *target* current-level info index) -1)))) + (collected-money-for-level lvl-idx)) + 0)) + +(defun max-money-for-level ((lvl-idx int)) + (-> *game-counts* data lvl-idx money-count)) + +(defun max-money-current-level () + (if (and *target* (-> *target* current-level)) + (let ((lvl-idx (-> *level-task-data-remap* (+ (-> *target* current-level info index) -1)))) + (max-money-for-level lvl-idx)) + 0)) + +(defun collected-buzzer-for-level ((lvl-idx int)) + (buzzer-count *game-info* (-> *level-task-data* lvl-idx task-info (-> *level-task-data* lvl-idx buzzer-task-index) task-id))) + +(defun collected-buzzer-current-level () + (if (and *target* (-> *target* current-level)) + (let ((lvl-idx (-> *level-task-data-remap* (+ (-> *target* current-level info index) -1)))) + (collected-buzzer-for-level lvl-idx)) + 0)) + +(defun collected-fuel-for-level ((lvl-idx int)) + (let ((completed-task-count 0)) + (dotimes (task-idx (-> *level-task-data* lvl-idx nb-of-tasks)) + (if (= (get-task-status (-> *level-task-data* lvl-idx task-info task-idx task-id)) + (task-status invalid)) + (+! completed-task-count 1))) + completed-task-count)) + +(defun collected-fuel-current-level () + (if (and *target* (-> *target* current-level)) + (let ((lvl-idx (-> *level-task-data-remap* (+ (-> *target* current-level info index) -1)))) + (collected-fuel-for-level lvl-idx)) + 0)) + +(defun max-fuel-for-level ((lvl-idx int)) + (-> *level-task-data* lvl-idx nb-of-tasks)) + +(defun max-fuel-current-level () + (if (and *target* (-> *target* current-level)) + (let ((lvl-idx (-> *level-task-data-remap* (+ (-> *target* current-level info index) -1)))) + (max-fuel-for-level lvl-idx)) + 0)) + +(defun target-fishing? () + (if (not *target*) + #f + (let ((mode (send-event *target* 'query 'mode))) + (= mode 'target-fishing)))) + +(defun target-billy-game? () + (if (not *target*) + #f + (let ((mode (send-event *target* 'query 'mode))) + (= mode 'target-billy-game)))) + +(defun mod-rule-record-roll! ((id symbol)) + (when id + (let ((slot 7)) + (while (> slot 0) + (set! (-> *mod-rule-recent-rolls* slot) + (-> *mod-rule-recent-rolls* (- slot 1))) + (+! slot -1)) + (set! (-> *mod-rule-recent-rolls* 0) id))) + (none)) + +(defun mod-rule-recent-roll-factor ((id symbol)) + (let ((slot 0) + (factor 8) + (found #f)) + (while (and (< slot 8) (not found)) + (when (= (-> *mod-rule-recent-rolls* slot) id) + (set! factor slot) + (set! found #t)) + (+! slot 1)) + factor)) + +(defun sparse-lurker-level? ((level symbol)) + (or (eq? level 'training) + (eq? level 'village1) + (eq? level 'jungleb) + (eq? level 'firecanyon) + (eq? level 'village2) + (eq? level 'rolling) + (eq? level 'ogre) + (eq? level 'village3) + (eq? level 'lavatube))) + +(defun sparse-hitlist-level? ((level symbol)) + (or (eq? level 'training) + (eq? level 'village1) + (eq? level 'village2) + (eq? level 'village3) + (eq? level 'jungleb) + (eq? level 'rolling) + (eq? level 'firecanyon) + (eq? level 'ogre) + (eq? level 'lavatube) + (eq? level 'citadel) + (eq? level 'finalboss))) + +(defun sparse-crate-level? ((level symbol)) + (or (eq? level 'village3) + (eq? level 'jungleb) + (eq? level 'lavatube) + (eq? level 'citadel) + (eq? level 'finalboss))) + +(defun red-eco-level? ((level symbol)) + (or (eq? level 'misty) + (eq? level 'snowy) + (eq? level 'citadel))) + +(defun yellow-eco-level? ((level symbol)) + (or (eq? level 'swamp) + (eq? level 'snowy) + (eq? level 'citadel) + (eq? level 'ogre) + (eq? level 'finalboss) + (eq? level 'lavatube) + (eq? level 'maincave) + (eq? level 'robocave) + (eq? level 'darkcave))) + +(defun no-blue-eco-level? ((level symbol)) + (or (eq? level 'maincave) + (eq? level 'darkcave) + (eq? level 'firecanyon) + (eq? level 'finalboss) + (eq? level 'village3) + (eq? level 'ogre) + (eq? level 'rolling) + (eq? level 'lavatube))) + +(defun sole-zoomer-level? ((level symbol)) + (or (eq? level 'firecanyon) + (eq? level 'rolling) + (string= (current-checkpoint-name) "ogre-race"))) + +(defun launcher-level? ((level symbol)) + (or (eq? level 'beach) + (eq? level 'jungle) + (eq? level 'jungleb) + (eq? level 'swamp) + (eq? level 'robocave) + (eq? level 'citadel))) + +(defun mod-rule-roll-priority ((id symbol)) + (let ((level-name (the symbol (current-level-name))) + (on-racer (target-on-racer?)) + (on-flut (target-on-flut?)) + (fuel-left (- (max-fuel-current-level) (collected-fuel-current-level))) + (money-left (- (max-money-current-level) (collected-money-current-level))) + (buzzer-left (- 7 (collected-buzzer-current-level))) + (is-minigame (or (target-fishing?) (target-billy-game?))) + (is-klaww (string= (current-checkpoint-name) "ogre-start")) + (racer-lavatube (or (and (target-on-racer?) (string= (current-checkpoint-name) "lavatube-start")) + (string= (current-checkpoint-name) "lavatube-middle") + (string= (current-checkpoint-name) "lavatube-after-ribbon")))) + + (cond + ;; Resources + ((eq? id 'rule-buzzer-pressure) + (if (or (< buzzer-left 2) (mod-rule-active? 'rule-distancing) is-minigame is-klaww + (eq? level-name 'jungleb) (eq? level-name 'finalboss) (eq? level-name 'sunkenb)) + 0 7)) + + ((eq? id 'rule-distancing) + (if (or (< buzzer-left 1) (mod-rule-active? 'rule-buzzer-pressure) is-klaww + (eq? level-name 'training) (eq? level-name 'jungleb) (eq? level-name 'finalboss)) + 0 10)) + + ((eq? id 'rule-blue-crate) + (if (no-blue-eco-level? level-name) 0 10)) + + ((eq? id 'rule-fuel-pressure) + (cond + ((or (< fuel-left 1) is-minigame is-klaww (eq? level-name 'jungleb) racer-lavatube (eq? level-name 'finalboss)) 0) + ((or (eq? level-name 'village1) (eq? level-name 'village2) (eq? level-name 'village3) + (mod-rule-active? 'complication-fuel-shortage)) 3) + (else 7))) + + ((eq? id 'rule-money-pressure) + (if (or (< money-left 30) is-minigame is-klaww (eq? level-name 'finalboss) + (mod-rule-active? 'complication-hidden-treasure)) + 0 6)) + + ((eq? id 'complication-hidden-treasure) + (if (or is-klaww (eq? level-name 'finalboss)) + 0 10)) + + ;; Enemies + ((or (eq? id 'complication-mean-look) (eq? id 'rule-lurker-pressure)) + (cond + ((sparse-lurker-level? level-name) 0) + ((eq? id 'complication-mean-look) 10) + ((or (target-fishing?) (eq? level-name 'finalboss)) 0) + (else 7))) + + ((eq? id 'rule-hitlist) + (if (or (sparse-hitlist-level? level-name) is-minigame) 0 10)) + + ((eq? id 'rule-flop-this) + (if (or (eq? level-name 'training) (eq? level-name 'lavatube) + (eq? level-name 'village3) (eq? level-name 'village2) (eq? level-name 'village1) + (eq? level-name 'rolling) is-klaww + (eq? level-name 'finalboss) (target-billy-game?)) + 0 10)) + + ((eq? id 'rule-red-kill) + (if (or (sparse-lurker-level? level-name) (not (red-eco-level? level-name)) + is-minigame) + 0 3)) + + ((eq? id 'complication-yellow-pilled) + (cond + (is-klaww 20) + ((yellow-eco-level? level-name) 6) + (else 0))) + + ((eq? id 'complication-red-light) + (if (red-eco-level? level-name) 6 0)) + + ;; Crates + ((eq? id 'rule-crate-break) + (if (or is-klaww is-minigame (sparse-crate-level? level-name)) 0 10)) + + ;; Movement + ((eq? id 'complication-jak-rabbit) + (if on-racer 0 8)) + + ((or (eq? id 'rule-grounded) (eq? id 'rule-die)) + (cond + ((or (eq? level-name 'training) (eq? level-name 'finalboss)) 0) + ((and (eq? id 'rule-grounded) is-klaww) 2) + ((eq? id 'rule-grounded) 10) + ((or (eq? level-name 'beach) (eq? level-name 'rolling)) 2) + (is-klaww 1) + ((or is-minigame (mod-rule-active? 'rule-survivor)) 0) + (else 4))) + + ((eq? id 'rule-survivor) + (cond + ((or (mod-rule-active? 'rule-die) (eq? level-name 'training) (eq? level-name 'village1)) 0) + ((or (eq? level-name 'village1) (eq? level-name 'village2)) 3) + (else 8))) + + ((or (eq? id 'rule-keep-it-low) (eq? id 'complication-sizeable) (eq? id 'rule-hands-off)) + (if (or on-racer on-flut (sole-zoomer-level? level-name)) 0 10)) + + ;; Special + ((eq? id 'rule-one-frame) + (if (or (not (launcher-level? level-name)) is-minigame) 0 5)) + + ((eq? id 'complication-drought) + (cond + ((or (eq? level-name 'jungleb) (eq? level-name 'firecanyon) (eq? level-name 'lavatube) + (eq? level-name 'citadel) (eq? level-name 'ogre) (eq? level-name 'finalboss) + (eq? level-name 'village3) (eq? level-name 'snow) (eq? level-name 'robocave)) 0) + ((or (eq? level-name 'maincave) (eq? level-name 'swamp) (eq? level-name 'rolling)) 3) + (else 8))) + + ((eq? id 'complication-eco-allergy) + (cond + ((eq? level-name 'village3) 0) + ((or (eq? level-name 'finalboss) is-klaww) 2) + (else 8))) + + ((eq? id 'complication-heating-balloons) + (if (or (eq? level-name 'firecanyon) (and (eq? level-name 'lavatube) on-racer)) 50 0)) + + ((eq? id 'complication-bus-driver) + (if (or (eq? level-name 'firecanyon) on-racer) 35 0)) + + ((eq? id 'complication-rotten-eggs) + (if (or is-klaww (eq? level-name 'finalboss)) 0 10)) + + ((or (eq? id 'complication-disassociate) (eq? id 'complication-wings-tied)) + (if on-flut 40 0)) + + ((eq? id 'complication-fuel-shortage) + (if (or (eq? level-name 'training) (eq? level-name 'finalboss)) 0 5)) + + ((or (eq? id 'complication-brittle-bones)) + (if (or (eq? level-name 'finalboss) is-klaww) 5 10)) + + (else 10)))) + +;; (defun mod-rule-can-roll-id? ((id symbol)) +;; (> (mod-rule-roll-priority id) 0)) + +;; (defun mod-rule-effective-roll-weight ((id symbol)) +;; (let ((weight (mod-rule-roll-priority id))) +;; (if (> weight 0) +;; (* weight (mod-rule-recent-roll-factor id)) +;; 0))) + +(defun mod-rule-pick-weighted-id () + (let ((idx 0) + (total 0)) + ;; Calculate total weight + (while (< idx *mod-rule-id-count*) + (let ((candidate (mod-rule-id-from-index idx))) + (when (and candidate + (not (mod-rule-active? candidate)) + (not (mod-rule-pending? candidate))) + (let ((priority (mod-rule-roll-priority candidate)) + (recent-factor (mod-rule-recent-roll-factor candidate)) + (weight 0)) + (set! weight (if (> priority 0) (* priority recent-factor) 0)) + (+! total weight)))) + (+! idx 1)) + + ;; Debug Print + (when (and *debug-segment* (> total 0)) + (format #t "--- Weighted Roll Debug (Total: ~D) ---~%" total) + (let ((debug-idx 0)) + (while (< debug-idx *mod-rule-id-count*) + (let ((candidate (mod-rule-id-from-index debug-idx))) + (when (and candidate + (not (mod-rule-active? candidate)) + (not (mod-rule-pending? candidate))) + (let* ((priority (mod-rule-roll-priority candidate)) + (recent-factor (mod-rule-recent-roll-factor candidate)) + (weight (if (> priority 0) (* priority recent-factor) 0))) + (let ((percent (* 100.0 (/ (the float weight) (the float total))))) + (format #t " ~S: Weight=~D (Prio: ~D, Rec: ~D) | ~2,1f%~%" + candidate weight priority recent-factor percent))))) + (+! debug-idx 1)))) + + ;; Selection + (if (> total 0) + (let ((roll (knuth-rand-int-range 0 total)) + (idx 0) + (chosen #f)) + (while (and (< idx *mod-rule-id-count*) (not chosen)) + (let ((candidate (mod-rule-id-from-index idx))) + (when (and candidate + (not (mod-rule-active? candidate)) + (not (mod-rule-pending? candidate))) + (let ((priority (mod-rule-roll-priority candidate)) + (recent-factor (mod-rule-recent-roll-factor candidate)) + (weight 0)) + (set! weight (if (> priority 0) (* priority recent-factor) 0)) + (when (> weight 0) + (if (< roll weight) + (set! chosen candidate) + (set! roll (- roll weight))))))) + (+! idx 1)) + (when *debug-segment* + (format #t "Weighted roll chose ~S~%" chosen)) + chosen) + #f))) + +(defun mod-rule-reroll-slot ((slot int) (apply-buffer symbol)) + (mod-rule-deactivate-slot slot) + (mod-rule-schedule-slot slot 'mod-rule-pending-roll apply-buffer) + (none)) + +(defun mod-rule-finish-slot ((slot int) (apply-buffer symbol)) + (if (> (mod-rule-active-slot-count) 1) + (mod-rule-deactivate-slot slot) + (mod-rule-reroll-slot slot apply-buffer)) + (none)) + +(defun mod-rule-counter-add-progress ((id symbol) (amount int)) + (when (> amount 0) + (let ((slot 0)) + (while (< slot 3) + (when (= (-> *mod-rule-active-slots* slot) id) + (let ((goal (-> *mod-rule-slot-goals* slot)) + (new-progress 0)) + (if (> goal 0) + (begin + (set! new-progress (min goal (+ (-> *mod-rule-slot-progress* slot) amount))) + (set! (-> *mod-rule-slot-progress* slot) new-progress) + (when (>= new-progress goal) + (sound-play "slider2001") + (set! *mod-rule-last-outcome* 'success) + (mod-rule-finish-slot slot #t))) + (set! (-> *mod-rule-slot-progress* slot) + (+ (-> *mod-rule-slot-progress* slot) amount))))) + (+! slot 1)))) + (none)) + +(defun mod-rule-on-orb-pickup ((amount int)) + (mod-rule-counter-add-progress 'rule-money-pressure amount) + (none)) + +(defun mod-rule-set-current ((id symbol)) + (mod-rule-activate-slot *mod-rule-current-slot* id) + (none)) + +(defun mod-rule-reroll-current ((apply-buffer symbol)) + (let ((slot (mod-rule-first-occupied-slot))) + (mod-rule-reroll-slot (if (>= slot 0) slot *mod-rule-current-slot*) apply-buffer)) + (none)) + +(defun mod-rule-random-punishment () + (mod-rule-random-available-punishment)) + +(defun mod-rule-refresh-active-rule-punishments () + (when (mod-rule-can-revert-collected-cell?) + (dotimes (slot 3) + (let ((id (-> *mod-rule-active-slots* slot))) + (when (and id (eq? (mod-rule-type id) 'rule)) + (set! (-> *mod-rule-slot-punishments* slot) (mod-rule-random-punishment)))))) + (none)) + +(defun mod-rule-on-cell-pickup ((amount int)) + (mod-rule-counter-add-progress 'rule-fuel-pressure amount) + ;; (mod-rule-refresh-active-rule-punishments) + (none)) + +(defun mod-rule-on-buzzer-pickup ((amount int)) + (mod-rule-counter-add-progress 'rule-buzzer-pressure amount) + (none)) + +(defun mod-rule-record-player-hit ((enemy process) (attack-mode symbol)) + (when (and enemy attack-mode) + (let ((idx *mod-rule-player-hit-write-idx*)) + (set! (-> *mod-rule-player-hit-handles* idx) (process->handle enemy)) + (set! (-> *mod-rule-player-hit-modes* idx) attack-mode) + (set! *mod-rule-player-hit-write-idx* (mod (+ idx 1) 64)))) + (none)) + +(defun mod-rule-last-player-hit-mode ((enemy process-drawable)) + (let ((enemy-handle (process->handle (the-as process enemy)))) + (when enemy-handle + (let ((count 0) + (idx (mod (+ *mod-rule-player-hit-write-idx* 63) 64))) + (while (< count 64) + (when (= (-> *mod-rule-player-hit-handles* idx) enemy-handle) + (return (-> *mod-rule-player-hit-modes* idx))) + (set! idx (if (= idx 0) 63 (- idx 1))) + (+! count 1))))) + #f) + +(defun mod-rule-enemy-killed-by-flop? ((enemy process-drawable)) + (when *target* + (let ((player-state (-> *target* state name))) + (if (or (= player-state 'target-flop) + ;; could not find a reliable way to detect flop so just excluding the situations where flut kills with dash, stomp, or eco + (and (target-on-flut?) (not (or (= player-state 'target-flut-running-attack) + (= player-state 'target-flut-double-jump) + (= player-state 'target-flut-falling))))) + #t #f)))) + +(defun mod-rule-enemy-killed-by-red-eco? ((enemy process-drawable)) + (eq? (mod-rule-last-player-hit-mode enemy) 'eco-red)) + +;; (defun mod-rule-string-contains? ((haystack string) (needle string)) +;; (let ((hay-len (length haystack)) +;; (needle-len (length needle)) +;; (idx 0)) +;; (if (or (<= needle-len 0) (< hay-len needle-len)) +;; #f +;; (begin +;; (while (<= idx (- hay-len needle-len)) +;; (when (string= (substring! *temp-string* haystack idx (+ idx needle-len)) needle) +;; (return #t)) +;; (+! idx 1)) +;; #f)))) + +(defun mod-rule-unique-enemy-type-seen? ((type-name symbol)) + (let ((slot 0) + (seen #f)) + (while (and (< slot 3) (not seen)) + (when (= (-> *mod-rule-unique-enemy-killed-types* slot) type-name) + (set! seen #t)) + (+! slot 1)) + seen)) + +(defun mod-rule-record-unique-enemy-type ((type-name symbol)) + (let ((slot 0) + (recorded #f)) + (when (not (mod-rule-unique-enemy-type-seen? type-name)) + (while (and (< slot 3) (not recorded)) + (when (not (-> *mod-rule-unique-enemy-killed-types* slot)) + (set! (-> *mod-rule-unique-enemy-killed-types* slot) type-name) + (set! recorded #t)) + (+! slot 1))) + recorded)) + +(defun mod-rule-on-crate-break ((amount int)) + (mod-rule-counter-add-progress 'rule-crate-break amount) + (none)) + +(defun mod-rule-punishment-name ((punishment symbol)) + (cond + ((eq? punishment 'punishment-money) "LOSE 50 ORBS") + ((eq? punishment 'punishment-warp) "WARP TO GEYSER ROCK") + ((eq? punishment 'punishment-countdown) "LOSE 30 SECONDS") + ((eq? punishment 'punishment-revert-cell) "REVERT A RANDOM CELL") + ((eq? punishment 'punishment-extra-rule) "EXTRA RULE") + ((eq? punishment 'punishment-blind) "BLIND FOR 10 SECONDS") + ((eq? punishment 'punishment-fall-damage) "MORE FALL DAMAGE FOR 2 MINUTES") + ((eq? punishment 'punishment-trip) "TRIP RANDOMLY FOR 2 MINUTES") + (else "NONE"))) + +(defun mod-rule-timed-punishment-name ((punishment symbol)) + (cond + ((eq? punishment 'punishment-fall-damage) "FALL DAMAGE") + ((eq? punishment 'punishment-trip) "TRIP") + (else (mod-rule-punishment-name punishment)))) + +(defun mod-rule-apply-extra-rule-punishment ((failed-slot int)) + (when (and (>= failed-slot 0) + (< failed-slot 3) + (< (mod-rule-active-slot-count) 2)) + (mod-rule-reroll-slot failed-slot #t) + (let ((extra-slot (mod-rule-first-free-slot))) + (when (>= extra-slot 0) + (mod-rule-reroll-slot extra-slot #t)))) + (none)) + +(defun mod-rule-apply-punishment ((punishment symbol)) + (cond + ((eq? punishment 'punishment-money) + (set! (-> *game-info* money) (max 0.0 (- (-> *game-info* money) 50.0)))) + ((eq? punishment 'punishment-warp) + (set! (-> *game-info* current-continue) (get-continue-by-name *game-info* "training-warp")) + (initialize! *game-info* 'die (the-as game-save #f) (the-as string #f))) + ((eq? punishment 'punishment-countdown) + (when *global-countdown-active?* + (set! *global-countdown-remaining-time* (+ *global-countdown-remaining-time* (- (seconds 30)))))) + ((eq? punishment 'punishment-revert-cell) + (mod-rule-revert-random-collected-cell)) + ((eq? punishment 'punishment-blind) + (set-blackout-frames (seconds 10))) + ((eq? punishment 'punishment-fall-damage) + (set! (-> *TARGET-bank* fall-far) (meters 7.0)) (set! (-> *TARGET-bank* fall-far-inc) (meters 10.0)) + (mod-rule-start-timed-punishment punishment)) + ((eq? punishment 'punishment-trip) + (none) + (mod-rule-start-timed-punishment punishment)) + (else + (none))) + (none)) + +(defun mod-rule-slot-in-grace-period? ((slot int)) + (and (>= slot 0) (< slot 3) (< (current-time) (-> *mod-rule-slot-grace-periods* slot)))) + +(defun mod-rule-handle-slot-failure ((slot int) (punishment symbol)) + (when (not (mod-rule-slot-in-grace-period? slot)) + (sound-play "caught-eel") + (set! *mod-rule-last-outcome* 'failed) + (if (eq? punishment 'punishment-extra-rule) + (begin + (mod-rule-apply-extra-rule-punishment slot) + (mod-rule-finish-slot slot #t)) + (begin + (mod-rule-apply-punishment punishment) + (mod-rule-finish-slot slot #t)))) + (none)) + +(defun mod-rule-handle-slot-expiry ((slot int)) + (when (and (>= slot 0) (< slot 3)) + (let ((id (-> *mod-rule-active-slots* slot))) + (when id + (let ((goal (-> *mod-rule-slot-goals* slot)) + (progress (-> *mod-rule-slot-progress* slot)) + (punishment (-> *mod-rule-slot-punishments* slot))) + + ;; Logic for handling failures + (when (and (> goal 0) (< progress goal) punishment) + (mod-rule-handle-slot-failure slot punishment)) + + (sound-play "cursor-up-down") + + ;; State resets were moved to mod-rule-deactivate-slot + (when (and (= id (-> *mod-rule-active-slots* slot)) + (or (<= goal 0) (>= progress goal) (not punishment))) + (set! *mod-rule-last-outcome* 'success) + (mod-rule-finish-slot slot #t)))))) + (none)) + +(defun mod-rule-punishment-for-id ((id symbol)) + (let ((slot 0) (found #f) (result #f)) + (while (and (< slot 3) (not found)) + (when (= (-> *mod-rule-active-slots* slot) id) + (set! result (-> *mod-rule-slot-punishments* slot)) + (set! found #t)) + (+! slot 1)) + result)) + +(defun mod-rule-reroll-id ((id symbol)) + (let ((slot 0) (found #f)) + (while (and (< slot 3) (not found)) + (when (= (-> *mod-rule-active-slots* slot) id) + (mod-rule-reroll-slot slot #t) + (set! found #t)) + (+! slot 1))) + (none)) + +(defun mod-rule-trigger-grounded () + (when (mod-rule-active? 'rule-grounded) + (let ((punishment (mod-rule-punishment-for-id 'rule-grounded))) + (when punishment + (let ((slot (mod-rule-find-active-slot 'rule-grounded))) + (when (>= slot 0) + (mod-rule-handle-slot-failure slot punishment)))))) + (none)) + +(defun mod-rule-trigger-blue-crate () + (when (mod-rule-active? 'rule-blue-crate) + (let ((punishment (mod-rule-punishment-for-id 'rule-blue-crate))) + (when punishment + (let ((slot (mod-rule-find-active-slot 'rule-blue-crate))) + (when (>= slot 0) + (mod-rule-handle-slot-failure slot punishment)))))) + (none)) + +(defun mod-rule-trigger-distancing () + (when (and (<= *closest-buzzer-dist* (meters 8)) (mod-rule-active? 'rule-distancing)) + (let ((punishment (mod-rule-punishment-for-id 'rule-distancing))) + (when punishment + (let ((slot (mod-rule-find-active-slot 'rule-distancing))) + (when (>= slot 0) + (mod-rule-handle-slot-failure slot punishment)))))) + (none)) + +(defun mod-rule-trigger-hands-off () + (when (mod-rule-active? 'rule-hands-off) + (let ((punishment (mod-rule-punishment-for-id 'rule-hands-off))) + (when punishment + (let ((slot (mod-rule-find-active-slot 'rule-hands-off))) + (when (>= slot 0) + (mod-rule-handle-slot-failure slot punishment)))))) + (none)) + +(defun mod-rule-trigger-brittle-bones () + (when (mod-rule-active? 'complication-brittle-bones) + (let ((roll (knuth-rand-int-range 0 100))) + (format #t "Brittle Bones roll: ~D~%" roll) + (when (and *target* (< roll 10)) + (send-event *target* 'attack #t (new 'static 'attack-info))))) + (none)) + +(defun mod-rule-trigger-keep-it-low () + (when (mod-rule-active? 'rule-keep-it-low) + (let ((punishment (mod-rule-punishment-for-id 'rule-keep-it-low))) + (when punishment + (let ((slot (mod-rule-find-active-slot 'rule-keep-it-low))) + (when (>= slot 0) + (mod-rule-handle-slot-failure slot punishment)))))) + (none)) + +(defun mod-rule-trigger-survivor () + (when (mod-rule-active? 'rule-survivor) + (let ((punishment (mod-rule-punishment-for-id 'rule-survivor))) + (when punishment + (let ((slot (mod-rule-find-active-slot 'rule-survivor))) + (when (>= slot 0) + (mod-rule-handle-slot-failure slot punishment)))))) + (none)) + +(defun mod-rule-trigger-rotten-eggs () + (when (and (not (movie?)) (mod-rule-active? 'complication-rotten-eggs)) + (send-event + *target* + 'attack-invinc + #f + (static-attack-info + ((mode (if (target-on-flut?) + 'endlessfall + 'melt))))) + + (none))) + +(defun mod-rule-trigger-eco-allergy () + (when (mod-rule-active? 'complication-eco-allergy) + (send-event *target* 'attack #t (new 'static 'attack-info))) + (none)) + +(defun mod-rule-trigger-sizeable () + (if (mod-rule-active? 'complication-sizeable) + (if (>= (-> *game-info* money) 3.0) + (begin + (set! (-> *game-info* money) (- (-> *game-info* money) 3.0)) + #t) + #f) + #t)) + +(defun mod-rule-trigger-ohko () + (when (and (> (-> (the-as fact-info-target (-> *target* fact)) health) 1.0) (and (mod-rule-active? 'complication-ohko) *target*)) + (set! (-> (the-as fact-info-target (-> *target* fact)) health) 1.0)) + (none)) + +;; (defun mod-rule-trigger-yellow-pilled () +;; (when (mod-rule-active? 'complication-yellow-pilled) *target* + +;; ) +;; (none)) + +(defun mod-rule-trigger-mean-look () + (when (mod-rule-active? 'complication-mean-look) + (logior! (-> *target* mask) (process-mask sleep)) + (set! *mean-look-clear-time* (+ (current-time) (* 3 300)))) + (none)) + +(defun mod-rule-trigger-lurker-pressure () + (when (mod-rule-active? 'rule-lurker-pressure) + (mod-rule-counter-add-progress 'rule-lurker-pressure 1)) + (none)) + +(defun mod-rule-trigger-flop-this ((flop-kill? symbol)) + (when (mod-rule-active? 'rule-flop-this) + (when (not flop-kill?) + (let ((punishment (mod-rule-punishment-for-id 'rule-flop-this))) + (when punishment + (let ((slot (mod-rule-find-active-slot 'rule-flop-this))) + (when (>= slot 0) + (mod-rule-handle-slot-failure slot punishment))))))) + (none)) + +(defun mod-rule-trigger-hitlist ((enemy-type symbol)) + (when (and (mod-rule-active? 'rule-hitlist) + enemy-type + (mod-rule-record-unique-enemy-type enemy-type)) + (mod-rule-counter-add-progress 'rule-hitlist 1)) + (none)) + +(defun mod-rule-trigger-red-eco ((red-eco-kill? symbol)) + (when (and (mod-rule-active? 'rule-red-kill) red-eco-kill?) + (mod-rule-counter-add-progress 'rule-red-kill 1)) + (none)) + +(defun mod-rule-trigger-heating-balloons () + (when (mod-rule-active? 'complication-heating-balloons) + (send-event *target* 'attack-invinc #f (static-attack-info ((mode 'melt))))) + (none)) + +(defun mod-rule-trigger-disassociate () + (when (and (target-on-flut?) (mod-rule-active? 'complication-disassociate) (not (movie?))) + (send-event *target* 'end-mode)) + (none)) + +(defun mod-rule-trigger-bus-driver () + (when (mod-rule-active? 'complication-bus-driver) + (when (not (movie?)) (target-attack-up *target* 'attack 'explode))) + (none)) + +;; (defun mod-rule-trigger-wings-tied () +;; (when (mod-rule-active? 'complication-wings-tied) +;; (none)) +;; (none)) + +;; (defun mod-rule-trigger-fuel-shortage () +;; (when (mod-rule-active? 'complication-fuel-shortage) +;; (none)) +;; (none)) + +;; (defun mod-rule-trigger-hidden-treasure () +;; (when (mod-rule-active? 'complication-hidden-treasure) +;; (none)) +;; (none)) + +;; (defun mod-rule-trigger-drought () +;; (when (mod-rule-active? 'complication-drought) +;; (set! *water-enable?* #f)) +;; (none)) + +(defun mod-rule-on-enemy-kill ((enemy-type symbol) (flop-kill? symbol) (red-eco-kill? symbol)) + (mod-rule-trigger-disassociate) + (mod-rule-trigger-lurker-pressure) + (mod-rule-trigger-hitlist enemy-type) + (mod-rule-trigger-flop-this flop-kill?) + (mod-rule-trigger-red-eco red-eco-kill?) + (none)) + +(defun mod-rule-on-jak-death () + (mod-rule-counter-add-progress 'rule-die 1) + (mod-rule-trigger-survivor) + (none)) + +(defun global-countdown-paused? () + (or (movie?) + (and *setting-control* (-> *setting-control* current movie)) + (and *target* + (logtest? (-> *target* state-flags) (state-flags grabbed))))) + +(defbehavior mod-rule-manager-proc process () + (when (not *global-countdown-lost?*) + (loop + (let ((elapsed (- (current-time) *mod-rule-last-update-time*))) + + ;; Safely intercept time-warps to adjust all absolute timers up or down + (when (or (< elapsed 0) (> elapsed (seconds 10))) + (dotimes (slot 3) + (when (-> *mod-rule-active-slots* slot) + (set! (-> *mod-rule-slot-end-times* slot) (+ (-> *mod-rule-slot-end-times* slot) elapsed))) + (when (-> *mod-rule-pending-slots* slot) + (set! (-> *mod-rule-slot-roll-ready-times* slot) (+ (-> *mod-rule-slot-roll-ready-times* slot) elapsed)))) + (dotimes (slot 4) + (when (-> *mod-rule-timed-punishments* slot) + (set! (-> *mod-rule-timed-punishment-end-times* slot) (+ (-> *mod-rule-timed-punishment-end-times* slot) elapsed)) + (set! (-> *mod-rule-timed-punishment-next-ticks* slot) (+ (-> *mod-rule-timed-punishment-next-ticks* slot) elapsed)))) + (set! elapsed 0)) + + (cond + ((global-countdown-paused?) + (when (> elapsed 0) + (dotimes (slot 3) + (when (-> *mod-rule-active-slots* slot) + (set! (-> *mod-rule-slot-end-times* slot) + (+ (-> *mod-rule-slot-end-times* slot) elapsed))) + (when (-> *mod-rule-pending-slots* slot) + (set! (-> *mod-rule-slot-roll-ready-times* slot) + (+ (-> *mod-rule-slot-roll-ready-times* slot) elapsed)))) + (dotimes (slot 4) + (when (-> *mod-rule-timed-punishments* slot) + (set! (-> *mod-rule-timed-punishment-end-times* slot) + (+ (-> *mod-rule-timed-punishment-end-times* slot) elapsed)) + (set! (-> *mod-rule-timed-punishment-next-ticks* slot) + (+ (-> *mod-rule-timed-punishment-next-ticks* slot) elapsed)))))) + (else + (when (and (> *mean-look-clear-time* 0) + (>= (current-time) *mean-look-clear-time*)) + (set! *mean-look-clear-time* 0) + (logclear! (-> *target* mask) (process-mask sleep))) + (mod-rule-update-timed-punishments) + (dotimes (slot 3) + (when (and (-> *mod-rule-active-slots* slot) + (>= (current-time) (-> *mod-rule-slot-end-times* slot))) + (mod-rule-handle-slot-expiry slot))) + (dotimes (slot 3) + (let ((pending-id (-> *mod-rule-pending-slots* slot))) + (when (and pending-id + (>= (current-time) (-> *mod-rule-slot-roll-ready-times* slot))) + (if (eq? pending-id 'mod-rule-pending-roll) + (let ((chosen (mod-rule-pick-weighted-id))) + (if chosen + (begin + (mod-rule-activate-slot slot chosen) + (mod-rule-record-roll! chosen)) + (mod-rule-clear-slot slot))) + (mod-rule-activate-slot slot pending-id))))))) + (set! *mod-rule-last-update-time* (current-time))) + (suspend))) + (none)) + +(defun mod-rules-start () + (mod-rules-reset) + (kill-by-name 'mod-rule-manager *active-pool*) + (mod-rule-reroll-current #t) + (process-spawn-function process mod-rule-manager-proc :name 'mod-rule-manager) + (none)) + +(defun mod-rules-resume () + (kill-by-name 'mod-rule-manager *active-pool*) + (when (= (mod-rule-active-slot-count) 0) + (mod-rule-reroll-current #t)) + + (process-spawn-function process mod-rule-manager-proc :name 'mod-rule-manager) + (none)) + +(defun mod-rule-draw-panel ((buf dma-buffer) + (x int) + (y int) + (id symbol) + (punishment symbol) + (goal int) + (progress int) + (time-left time-frame))) + +(defun mod-rule-nth-active-slot ((start-slot int)) + (let ((slot (max 0 start-slot))) + (while (and (< slot 3) (not (-> *mod-rule-active-slots* slot))) + (+! slot 1)) + (if (< slot 3) slot -1))) + +(defun mod-rule-draw-slot ((buf dma-buffer) (slot int) (y int)) + (let* ((id (-> *mod-rule-active-slots* slot)) + (punishment (-> *mod-rule-slot-punishments* slot)) + (goal (-> *mod-rule-slot-goals* slot)) + (progress (-> *mod-rule-slot-progress* slot)) + (show-progress? (and (> goal 0) (eq? (mod-rule-goal-kind id) 'counter))) + (time-left (max 0 (- (-> *mod-rule-slot-end-times* slot) (current-time)))) + (row y)) + (draw-string-xy-scaled (mod-rule-name id) buf 10 row (font-color green) (font-flags shadow kerning large) 0.5) + (+! row 10) + (draw-string-xy-scaled (mod-rule-description id) buf 10 row (font-color white) (font-flags shadow kerning large) 0.35) + (+! row 9) + (when punishment + (draw-string-xy-scaled "PUNISHMENT: " buf 10 row (font-color red) (font-flags shadow kerning large) 0.3) + (draw-string-xy-scaled (mod-rule-punishment-name punishment) buf 80 row (font-color white) (font-flags shadow kerning large) 0.3) + (+! row 8)) + (when show-progress? + (draw-string-xy-scaled "PROGRESS: " buf 10 row (font-color yellow) (font-flags shadow kerning large) 0.3) + (draw-string-xy-scaled (string-format "~D/~D" progress goal) buf 80 row (font-color white) (font-flags shadow kerning large) 0.5) + (+! row 7)) + (draw-string-xy-scaled (mod-rule-format-time-left time-left) buf 10 row (font-color white) (font-flags shadow kerning large) 0.5) + (+! row 12) + (when (eq? id 'rule-distancing) + (draw-string-xy-scaled "DISTANCE: " buf 10 row (font-color progress-percent) (font-flags shadow kerning large) 0.3) + (-! row 2) + (if (> *closest-buzzer-dist* 20000000) + (draw-string-xy-scaled "NONE" buf 66 row (font-color white) (font-flags shadow kerning large) 0.4) + (let* ((dist-meters (/ *closest-buzzer-dist* 4096.0)) + (tenths (the int (* dist-meters 10)))) + (draw-string-xy-scaled (string-format "~D.~D" (/ tenths 10) (mod tenths 10)) buf 66 row (font-color white) (font-flags shadow kerning large) 0.4))) + (+! row 8)) + row)) + +(defun mod-rule-draw-timed-punishments ((buf dma-buffer)) + (let ((row-count 0)) + (dotimes (slot 4) + (let ((punishment (-> *mod-rule-timed-punishments* slot))) + (when punishment + (let ((time-left (max 0 (- (-> *mod-rule-timed-punishment-end-times* slot) (current-time)))) + (row-y (+ 68 (* row-count 10)))) + (draw-string-xy-scaled (mod-rule-timed-punishment-name punishment) buf 395 row-y (font-color white) (font-flags shadow kerning large) 0.3) + (draw-string-xy-scaled (mod-rule-format-time-left time-left) buf 465 (- row-y 2) (font-color white) (font-flags shadow kerning large) 0.4) + (+! row-count 1)))))) + (none)) + +(defun mod-rule-draw-last-outcome ((buf dma-buffer) (y int)) + (cond + ((eq? *mod-rule-last-outcome* 'success) + (draw-string-xy-scaled + "GOOD JOB!" + buf + 10 + y + (font-color green) + (font-flags shadow kerning large) + 0.4)) + ((eq? *mod-rule-last-outcome* 'failed) + (draw-string-xy-scaled + "RULE BROKEN!" + buf + 10 + y + (font-color red) + (font-flags shadow kerning large) + 0.4))) + (none)) + +(defun mod-rule-draw-rolling-slot ((buf dma-buffer)) + (let ((slot 0) (rolling-id #f) (rolling-left 0)) + (while (and (< slot 3) (not rolling-id)) + (let ((pending (-> *mod-rule-pending-slots* slot))) + (when pending + (set! rolling-id pending) + (set! rolling-left (max 0 (- (-> *mod-rule-slot-roll-ready-times* slot) (current-time)))))) + (+! slot 1)) + (when rolling-id + ;; (cond + ;; ((eq? *mod-rule-last-outcome* 'success) + ;; (draw-string-xy-scaled "GOOD JOB!" buf 10 50 (font-color green) (font-flags shadow kerning large) 0.4)) + ;; ((eq? *mod-rule-last-outcome* 'failed) + ;; (draw-string-xy-scaled "RULE BROKEN!" buf 10 50 (font-color red) (font-flags shadow kerning large) 0.4))) + (draw-string-xy-scaled "NEW RULE IN " buf 10 60 (font-color yellow) (font-flags shadow kerning large) 0.4) + (draw-string-xy-scaled (mod-rule-format-time-left rolling-left) buf 96 57 (font-color white) (font-flags shadow kerning large) 0.6))) + (none)) + +(defun global-countdown-draw () + (cond + (*global-countdown-lost?* + (with-dma-buffer-add-bucket ((buf (-> (current-frame) debug-buf)) + (bucket-id debug-no-zbuf)) + (draw-string-xy "LOSE!" + buf + 20 + 20 + (font-color red) + (font-flags shadow kerning)))) + (*global-countdown-active?* + (with-dma-buffer-add-bucket ((global-buf (-> (current-frame) global-buf)) + (bucket-id debug-no-zbuf)) + (let* ((total-centis (max 0 (the int (/ (* *global-countdown-remaining-time* 100) + (seconds 1))))) + (total-seconds (the int (/ total-centis 100))) + (minutes (the int (/ total-seconds 60))) + (seconds-part (the int (mod total-seconds 60))) + (centis (the int (mod total-centis 100)))) + (draw-string-xy-scaled + (string-format "~1,'0D:~2,'0D.~2,'0D" + minutes + seconds-part + centis) + global-buf + 400 + 50 + (font-color red) + (font-flags shadow kerning large) + 0.8) + (mod-rule-draw-timed-punishments global-buf) + (let ((primary (mod-rule-nth-active-slot 0))) + (cond + ((>= primary 0) + (let ((end-row (mod-rule-draw-slot global-buf primary 40))) + (when (and *debug-segment* + (> *mod-rule-last-reverted-cell-until* (current-time))) + (draw-string-xy-scaled + (string-format + "Reverted Cell Task: ~D" + *mod-rule-last-reverted-cell-task*) + global-buf + 10 + (+ end-row 8) + (font-color red) + (font-flags shadow kerning) + 1.0)) + (let ((secondary (mod-rule-nth-active-slot (+ primary 1)))) + (cond + ((>= secondary 0) + (mod-rule-draw-last-outcome + global-buf + (+ end-row 8)) + (mod-rule-draw-slot + global-buf + secondary + 94)) + (else + (mod-rule-draw-last-outcome + global-buf + (+ end-row 8))))))) + (else + (mod-rule-draw-rolling-slot global-buf) + (mod-rule-draw-last-outcome global-buf 52))))))) + (else + (none))) + (none)) + +(defun global-countdown-add-seconds ((seconds-to-add float)) + (when *global-countdown-active?* + (set! *global-countdown-remaining-time* + (+ *global-countdown-remaining-time* (seconds seconds-to-add)))) + (none)) + +(defbehavior global-countdown-proc process () + (loop + (when (and *global-countdown-active?* (not *global-countdown-lost?*)) + (cond + ((or (global-countdown-paused?) + (and (or (and (mod-rule-active? 'complication-yellow-pilled) + (= (-> *target* fact eco-type) (pickup-type eco-yellow))) + (and (mod-rule-active? 'complication-red-light) + (= (-> *target* fact eco-type) (pickup-type eco-red)))) + (>= (-> *target* fact eco-level) 1.0))) + (set! *global-countdown-last-update-time* (current-time))) + (else + (let ((elapsed (- (current-time) *global-countdown-last-update-time*))) + + ;; Detect save/load time-warp (backwards or massive skip) + (when (or (< elapsed 0) (> elapsed (seconds 10))) + (set! elapsed 0)) + + (when (> elapsed 0) + (set! *global-countdown-remaining-time* (max 0 (- *global-countdown-remaining-time* elapsed)))) + + ;; Unconditionally update so the timer doesn't freeze + (set! *global-countdown-last-update-time* (current-time)) + + (when (<= *global-countdown-remaining-time* 0) + (set! *global-countdown-remaining-time* 0) + (set! *global-countdown-lost?* #t) + (set! *global-countdown-active?* #f) + ;; deactivate any currently-active rule effects right away + (kill-by-name 'mod-rule-manager *active-pool*) + (mod-rules-reset) + (logclear! (-> *target* mask) (process-mask sleep)) + (set! *mean-look-clear-time* 0)))))) + (global-countdown-draw) + (suspend)) + (none)) + +(defun global-countdown-reset () + (kill-by-name 'global-countdown *active-pool*) + (mod-rules-reset) + (set! *global-countdown-active?* #f) + (set! *global-countdown-lost?* #f) + (set! *global-countdown-remaining-time* (seconds 60)) ;; global timer start + (set! *global-countdown-last-update-time* (current-time)) + (set! *mod-rule-last-update-time* (current-time)) + (set! *mod-rule-last-reverted-cell-task* 0) + (set! *mod-rule-last-reverted-cell-until* 0) + (set! *mod-rule-last-outcome* 'none) + (none)) + +(defun global-countdown-start () + (global-countdown-reset) + (set! *global-countdown-active?* #t) + (mod-rules-start) + (process-spawn-function process global-countdown-proc :name 'global-countdown) + (none)) + +(defun global-countdown-save-value () + (if *global-countdown-active?* + (the int *global-countdown-remaining-time*) + 0)) + +(defun global-countdown-resume-from-save ((saved-time int)) + (cond + ((> saved-time 0) + ;; Saved file HAS a countdown. + (set! *global-countdown-remaining-time* saved-time) + (set! *global-countdown-last-update-time* (current-time)) + (cond + ((not *global-countdown-active?*) + ;; SCENARIO A: system was dormant (fresh boot / post-crash reload). + (set! *global-countdown-active?* #t) + (set! *global-countdown-lost?* #f) + (set! *mod-rule-last-update-time* (current-time)) + (kill-by-name 'global-countdown *active-pool*) + (mod-rules-start) + (process-spawn-function process global-countdown-proc :name 'global-countdown)) + (else + ;; SCENARIO B: system was already active (normal in-game save/load). + (mod-rules-resume) + (kill-by-name 'global-countdown *active-pool*) + (process-spawn-function process global-countdown-proc :name 'global-countdown)))) + (else + ;; SCENARIO C: no countdown saved in this file at all. + (when (or *global-countdown-active?* *global-countdown-lost?*) + (global-countdown-reset)))) + (none)) + +;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Define Approved Custom Functions/Macros to call in all mods +;;;;;;;;;;;;;;;;;;;;;;;;;; + +;; These are included with the mod base and you are welcome to use them in your mods! + ;;This function moves an actor to the given coordinates ;;example: (move-actor "farmer-3" 3.0 74.0 -120.0) (defun move-actor ((actor-name string) (x float) (y float) (z float)) diff --git a/goal_src/jak1/engine/mods/mod-custom-code.gc b/goal_src/jak1/engine/mods/mod-custom-code.gc index 71d631c764..cf1c6e2e2d 100644 --- a/goal_src/jak1/engine/mods/mod-custom-code.gc +++ b/goal_src/jak1/engine/mods/mod-custom-code.gc @@ -21,17 +21,54 @@ ;; Begin function defintions. ;;;;;;;;;;;;;;;;;;;;;;;;;; +;; (define-extern *water-enable?* (function (none))) + (defun runs-every-frame () ;;(increase-power-cell-by-one) This is a call to increase-power-cell-by-one defined in mod-common-functions.gc (if *show-input-display* (input-display-on) (input-display-off)) + + ;; (if (mod-rule-active? 'complication-drought) + ;; (set! *ocean-off* #t)) ;; restore if merc and generic water is figured out + + (when *debug-segment* + (when (and (cpad-pressed? 0 circle) + (cpad-hold? 0 right)) + (mod-rule-reroll-slot 0 #f)) + + (when (and (cpad-pressed? 0 circle) + (cpad-hold? 0 left)) + (mod-rule-reroll-slot 1 #f)) + + (when (and (cpad-pressed? 0 circle) + (cpad-hold? 0 l1) (cpad-hold? 0 r1)) + (global-countdown-start)) + + (when (and (cpad-pressed? 0 triangle) + (cpad-hold? 0 l1) (cpad-hold? 0 r1)) + (global-countdown-reset) + (kill-by-name 'mod-rule-manager *active-pool*)) + + (when (and (cpad-pressed? 0 circle) + (cpad-hold? 0 up)) + (global-countdown-add-seconds 20.0)) + + (when (and (cpad-pressed? 0 circle) + (cpad-hold? 0 down)) + (global-countdown-add-seconds -20.0))) + ;; ensure orb-placer is spawned/killed as requested, debug menu is updated (when *debug-segment* (orb-placer-maintenance)) + (mod-rule-trigger-ohko) + (mod-rule-trigger-distancing) (none)) (defun runs-on-orb-pickup ((parent process-tree)) (let* ((from-cache? (and parent (type-type? (-> parent type) orb-cache-top)))) ;; Code here runs on ANY orb pickup + (global-countdown-add-seconds 1.0) + (mod-rule-trigger-rotten-eggs) + (mod-rule-on-orb-pickup 1) (when from-cache? ;; Code here runs only if the orb was from an orb cache ) @@ -42,21 +79,27 @@ (defun runs-on-fly-pickup () ;; Code here runs on any scout fly pickup + (mod-rule-on-buzzer-pickup 1) + (global-countdown-add-seconds 7.0) (none)) (defun runs-on-cell-pickup ((cell-event symbol)) (case cell-event (('pickup) ;; Code here runs as soon as you pickup a powercell + (global-countdown-add-seconds 20.0) + (mod-rule-on-cell-pickup 1) ) (('cutscene-end) ;; Code here runs at the end of any powercell cutscene + )) (none)) (defun runs-on-eco-pickup ((eco-type pickup-type) (parent process-tree)) (let* ((from-vent? (and parent (type-type? (-> parent type) vent)))) ;; Code here runs as soon as you pickup ANY eco + (mod-rule-trigger-eco-allergy) (case eco-type (((pickup-type eco-yellow)) ;; Code here runs as soon as you pickup yellow eco @@ -82,18 +125,32 @@ ;; Code here runs every time jak spawns (loading a file new game or death) ;;uncomment this to use custom music for custom levels - the function is in mod-common-functions.gc ;;(process-spawn-function process music-manager-proc) + (none)) (defun runs-on-jak-death ((death-event symbol)) (case death-event (('dying) ;; Code here runs immediately every time jak dies, before any death animation or death cutscene + (mod-rule-on-jak-death) ) (('blackout) ;; Code here runs after jak dies (and any death cutscene finishes), during the blackout before he spawns )) (none)) +(defun runs-on-enemy-death ((enemy process-drawable)) + (let ((was-flop-kill? (mod-rule-enemy-killed-by-flop? enemy)) + (was-red-eco-kill? (mod-rule-enemy-killed-by-red-eco? enemy))) + (mod-rule-on-enemy-kill (the-as symbol (-> enemy type)) was-flop-kill? was-red-eco-kill?)) + (none)) + +(defun runs-on-crate-break ((crate process-drawable) (event symbol) (amount int)) + ;; Code here runs when a crate enters its break flow. + (when (and crate (eq? event 'break)) + (mod-rule-on-crate-break amount)) + (none)) + ;;;;;;;;;;;;;;;;;;;;;;;;;; ;; deprecated function defintions. ;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/goal_src/jak1/engine/target/target-handler.gc b/goal_src/jak1/engine/target/target-handler.gc index 41b15dc1a4..7e213ac7a1 100644 --- a/goal_src/jak1/engine/target/target-handler.gc +++ b/goal_src/jak1/engine/target/target-handler.gc @@ -3,6 +3,7 @@ (bundles "ENGINE.CGO" "GAME.CGO") (require "engine/target/logic-target.gc") (define-extern target-powerup-effect (function symbol none :behavior target)) +(define-extern mod-rule-record-player-hit (function process symbol none)) ;; DECOMP BEGINS @@ -247,7 +248,12 @@ (set! (-> v1-0 param 2) (the-as uint arg3)) (set! (-> v1-0 param 3) (the-as uint arg4)) (let ((gp-0 (send-event-function arg0 v1-0))) - (when (and gp-0 (!= gp-0 'push)) + (when (and gp-0 (!= gp-0 'push)) + (mod-rule-record-player-hit arg0 + (if (and (= (-> self fact eco-type) (pickup-type eco-red)) + (>= (-> self fact eco-level) 1.0)) + 'eco-red + (the-as symbol arg1))) (let ((v1-5 (-> self control danger-mode))) (cond ((or (= v1-5 'spin) (= v1-5 'spin-air)) diff --git a/goal_src/jak1/engine/target/target.gc b/goal_src/jak1/engine/target/target.gc index d9a32ebb39..9df64a936f 100644 --- a/goal_src/jak1/engine/target/target.gc +++ b/goal_src/jak1/engine/target/target.gc @@ -28,6 +28,14 @@ (define-extern target-send-attack (function process uint touching-shapes-entry int int symbol :behavior target)) (define-extern target-bonk-event-handler (function process int symbol event-message-block object :behavior target)) +(define-extern mod-rule-trigger-grounded (function none)) +(define-extern mod-rule-trigger-brittle-bones (function none)) +(define-extern mod-rule-trigger-keep-it-low (function none)) +(define-extern mod-rule-trigger-sizeable (function symbol)) +(define-extern mod-rule-trigger-hands-off (function none)) +(define-extern mod-trigger-rule-one-frame (function none)) +(define-extern mod-rule-counter-add-progress (function symbol int none)) +(define-extern *last-launch-time* (int)) ;; DECOMP BEGINS @@ -84,7 +92,7 @@ (if (and (cpad-pressed? (-> self control cpad number) circle) (can-feet?)) (go target-attack-air #f)) (when (= arg0 'target-eco-powerup) ;; og:preserve-this - High FPS Fix - (if (and (recently-pressed? x) + (when (and (recently-pressed? x) (not (logtest? (-> self water flags) (water-flags wt09))) (not (time-elapsed? (-> self state-time) (seconds 3))) (not (logtest? (-> self state-flags) (state-flags prevent-jump)))) @@ -244,7 +252,7 @@ (remove-exit) (go target-duck-stance)) ;; og:preserve-this - High FPS Fix - (if (and (recently-pressed? x) (can-jump? #f)) + (when (and (recently-pressed? x) (can-jump? #f)) (go target-jump (-> *TARGET-bank* jump-height-min) (-> *TARGET-bank* jump-height-max) (the-as surface #f))) ;; og:preserve-this - High FPS Fix (if (and (recently-pressed? circle) (can-feet?)) (go target-attack)) @@ -401,7 +409,7 @@ (remove-exit) (go target-stance)) ;; og:preserve-this - High FPS Fix - (if (and (recently-pressed? x) (can-jump? #f)) + (when (and (recently-pressed? x) (can-jump? #f)) (go target-jump (-> *TARGET-bank* jump-height-min) (-> *TARGET-bank* jump-height-max) (the-as surface #f))) ;; og:preserve-this - High FPS Fix (if (and (recently-pressed? circle) (can-feet?)) (go target-attack)) @@ -605,7 +613,7 @@ (behavior () ((-> self state-hook)) ;; og:preserve-this - High FPS Fix - (if (and (recently-pressed? x) (can-jump? #f)) + (when(and (recently-pressed? x) (can-jump? #f)) (go target-jump (-> *TARGET-bank* jump-height-min) (-> *TARGET-bank* jump-height-max) (the-as surface #f))) (if (and (cpad-pressed? (-> self control cpad number) circle) (can-feet?)) (go target-attack)) (if (can-hands? #t) (go target-running-attack)) @@ -756,9 +764,11 @@ ;; og:preserve-this - High FPS Fix (when (and (recently-pressed? x) (can-jump? #f)) (if (= (-> *cpad-list* cpads (-> self control unknown-cpad-info00 number) stick0-speed) 0.0) - (go target-high-jump (-> *TARGET-bank* duck-jump-height-min) (-> *TARGET-bank* duck-jump-height-max) 'duck) - (go target-jump (-> *TARGET-bank* jump-height-min) (-> *TARGET-bank* jump-height-max) (the-as surface #f)))) - (if (and (can-hands? #t) (can-exit-duck?)) + (begin + (go target-high-jump (-> *TARGET-bank* duck-jump-height-min) (-> *TARGET-bank* duck-jump-height-max) 'duck)) + (begin + (go target-jump (-> *TARGET-bank* jump-height-min) (-> *TARGET-bank* jump-height-max) (the-as surface #f))))) + (when (and (can-hands? #t) (can-exit-duck?)) (go target-attack-uppercut (-> *TARGET-bank* attack-jump-height-min) (-> *TARGET-bank* attack-jump-height-max))) (fall-test) (slide-down-test)) @@ -808,9 +818,11 @@ ;; og:preserve-this - High FPS Fix (when (and (recently-pressed? x) (can-jump? #f)) (if (= (-> *cpad-list* cpads (-> self control unknown-cpad-info00 number) stick0-speed) 0.0) - (go target-high-jump (-> *TARGET-bank* duck-jump-height-min) (-> *TARGET-bank* duck-jump-height-max) 'duck) - (go target-jump (-> *TARGET-bank* jump-height-min) (-> *TARGET-bank* jump-height-max) (the-as surface #f)))) - (if (and (can-hands? #t) (can-exit-duck?)) + (begin + (go target-high-jump (-> *TARGET-bank* duck-jump-height-min) (-> *TARGET-bank* duck-jump-height-max) 'duck)) + (begin + (go target-jump (-> *TARGET-bank* jump-height-min) (-> *TARGET-bank* jump-height-max) (the-as surface #f))))) + (when (and (can-hands? #t) (can-exit-duck?)) (go target-attack-uppercut (-> *TARGET-bank* attack-jump-height-min) (-> *TARGET-bank* attack-jump-height-max))) (fall-test) (slide-down-test)) @@ -833,6 +845,8 @@ :event target-jump-event-handler :enter (behavior ((arg0 float) (arg1 float) (arg2 surface)) + (mod-rule-trigger-brittle-bones) + (mod-rule-trigger-grounded) (when (= (-> self control unknown-symbol40) 'launch) (level-hint-spawn (text-id sidekick-launcher1) "sksp009d" (the-as entity #f) *entity-pool* (game-task none)) (level-hint-spawn (text-id sidekick-launcher2) "sksp009e" (the-as entity #f) *entity-pool* (game-task none)) @@ -845,6 +859,8 @@ (a1-5 (-> self control unknown-dword61)) (a2-5 (-> self control unknown-vector102))) (set! (-> a2-5 quad) (-> (the-as vector (-> self control unknown-dword62)) quad)) + (mod-rule-trigger-brittle-bones) + (mod-rule-trigger-grounded) (go target-launch (the-as float a0-9) (the-as symbol a1-5) a2-5 (-> self control unknown-dword63)))) (set-time! (-> self state-time)) (sound-play "jump" :vol 70) @@ -866,12 +882,13 @@ (fmax (-> self control unknown-float123) (* 0.003921569 (the float (-> *cpad-list* cpads (-> self control unknown-cpad-info00 number) abutton (abutton-idx x)))))) ;; og:preserve-this abutton indexing (target-falling-trans #f (the-as time-frame (if (ja-group? eichar-jump-loop-ja) 15 -1))) - (if (and (cpad-pressed? (-> self control cpad number) x) + (when (and (cpad-pressed? (-> self control cpad number) x) (< (vector-dot (-> self control dynam gravity-normal) (-> self control transv)) 12288.0) (and (< -61440.0 (vector-dot (-> self control dynam gravity-normal) (-> self control transv))) (not (logtest? (-> self water flag) (water-flag touch-water))) (not (logtest? (-> self state-flags) (state-flags prevent-jump))))) - (go target-double-jump (-> *TARGET-bank* double-jump-height-min) (-> *TARGET-bank* double-jump-height-max))) + (go target-double-jump (-> *TARGET-bank* double-jump-height-min) (-> *TARGET-bank* double-jump-height-max)) + ) (if (and (cpad-pressed? (-> self control cpad number) square) (< (vector-dot (-> self control dynam gravity-normal) (-> self control transv)) 26624.0) (and (< -61440.0 (vector-dot (-> self control dynam gravity-normal) (-> self control transv))) @@ -939,12 +956,16 @@ :event target-jump-event-handler :enter (behavior ((arg0 float) (arg1 float)) + (mod-rule-trigger-brittle-bones) + (mod-rule-trigger-grounded) (when (= (-> self control unknown-symbol40) 'launch) enter-state (let ((a0-3 (-> self control unknown-dword60)) (a1-1 (-> self control unknown-dword61)) (a2-0 (-> self control unknown-vector102))) (set! (-> a2-0 quad) (-> (the-as vector (-> self control unknown-dword62)) quad)) + (mod-rule-trigger-brittle-bones) + (mod-rule-trigger-grounded) (go target-launch (the-as float a0-3) (the-as symbol a1-1) a2-0 (-> self control unknown-dword63)))) (set-time! (-> self state-time)) (init-var-jump arg0 arg1 (the-as vector #t) (the-as vector #t) (-> self control transv)) @@ -986,15 +1007,20 @@ :event target-jump-event-handler :enter (behavior ((arg0 float) (arg1 float) (arg2 basic)) + (mod-rule-trigger-brittle-bones) + (mod-rule-trigger-grounded) (when (and (= (-> self control unknown-symbol40) 'launch) (!= arg2 'launch)) enter-state (let ((a0-3 (-> self control unknown-dword60)) (a1-1 (-> self control unknown-dword61)) (a2-1 (-> self control unknown-vector102))) (set! (-> a2-1 quad) (-> (the-as vector (-> self control unknown-dword62)) quad)) + (mod-rule-trigger-brittle-bones) + (mod-rule-trigger-grounded) (go target-launch (the-as float a0-3) (the-as symbol a1-1) a2-1 (-> self control unknown-dword63)))) (set! (-> self control unknown-uint31) (the-as uint arg2)) - (if (or (= arg2 'duck) (= arg2 'launch)) (go target-duck-high-jump arg0 arg1 (the-as symbol arg2))) + (when (or (= arg2 'duck) (= arg2 'launch)) + (go target-duck-high-jump arg0 arg1 (the-as symbol arg2))) (set-time! (-> self state-time)) (logclear! (-> self control status) (cshape-moving-flags onsurf onground tsurf)) (sound-play "jump" :pitch 0.3) @@ -1181,7 +1207,7 @@ :trans (behavior () ;; og:preserve-this - High FPS Fix - (if (and (recently-pressed? x) (can-jump? #f)) + (when (and (recently-pressed? x) (can-jump? #f)) (go target-jump (-> *TARGET-bank* jump-height-min) (-> *TARGET-bank* jump-height-max) (the-as surface #f))) (if (and (not (can-exit-duck?)) (can-duck?)) (go target-duck-stance)) (when (!= (-> *cpad-list* cpads (-> self control cpad number) stick0-speed) 0.0) @@ -1225,7 +1251,7 @@ (cpad-set-buzz! (-> *cpad-list* cpads 0) 1 153 (seconds 0.1)) (level-hint-spawn (text-id misty-eco-red-first-use) "sksp0072" (the-as entity #f) *entity-pool* (game-task none))) ;; og:preserve-this - High FPS Fix - (if (and (recently-pressed? x) (can-jump? #f)) + (when (and (recently-pressed? x) (can-jump? #f)) (go target-jump (-> *TARGET-bank* jump-height-min) (-> *TARGET-bank* jump-height-max) (the-as surface #f))) (suspend) (ja :num! (seek! max (-> self control current-surface align-speed)))) @@ -1277,6 +1303,7 @@ (set! (-> *run-attack-mods* turnvv) 655360.0) (target-start-attack) (target-danger-set! 'punch #f) + (mod-rule-trigger-hands-off) (if (or (< (fabs (-> self control local-slope-x)) 0.3) (< 0.3 (fabs (-> self control local-slope-z)))) (set! (-> self control bend-target) 1.0))) :exit @@ -1313,7 +1340,7 @@ (-> *TARGET-bank* smack-surface-height) (the-as process #f) target-hit)) - (if (and (cpad-pressed? (-> self control cpad number) x) + (when (and (cpad-pressed? (-> self control cpad number) x) (and (< 4096.0 (-> self control ctrl-xz-vel)) (or (time-elapsed? (-> self state-time) (seconds 0.1)) (not (logtest? (-> *cpad-list* cpads (-> self control cpad number) button0-abs 0) (pad-buttons square)))) @@ -1392,9 +1419,12 @@ (else (target-dangerous-event-handler proc argc message block))))) :enter (behavior ((arg0 symbol)) + (if (< (- (current-time) *last-launch-time*) (seconds 1.0)) + (mod-rule-counter-add-progress 'rule-one-frame 1)) (set-time! (-> self state-time)) (logclear! (-> self control status) (cshape-moving-flags onsurf onground tsurf)) (target-start-attack) + (mod-rule-trigger-keep-it-low) (target-danger-set! 'spin-air #f) (set! (-> self control mod-surface) *jump-attack-mods*) (let ((f0-1 (vector-dot (-> self control dynam gravity-normal) (-> self control transv)))) @@ -1494,6 +1524,8 @@ :event target-dangerous-event-handler :enter (behavior ((arg0 float) (arg1 float)) + (mod-rule-trigger-brittle-bones) + (mod-rule-trigger-grounded) (set-time! (-> self state-time)) (target-start-attack) (target-danger-set! 'uppercut #f) @@ -1542,7 +1574,7 @@ (the-as float 65502.96) (the-as float -163840.0) (the-as float (if (= (-> *cpad-list* cpads (-> self control cpad number) stick0-speed) 0.0) 0.0 68812.8)))) - (if (and (cpad-pressed? (-> self control cpad number) circle) + (when (and (cpad-pressed? (-> self control cpad number) circle) (can-feet?) (ja-group? eichar-attack-uppercut-ja) (>= (ja-aframe-num 0) 12.0)) @@ -1593,7 +1625,9 @@ (else (target-dangerous-event-handler proc argc message block))))) :enter (behavior ((arg0 float) (arg1 float) (arg2 float)) - (if (and (= (-> self fact eco-type) (pickup-type eco-yellow)) (>= (-> self fact eco-level) 1.0)) + (when (and (= (-> self fact eco-type) (pickup-type eco-yellow)) (>= (-> self fact eco-level) 1.0)) + (mod-rule-trigger-brittle-bones) + (mod-rule-trigger-grounded) (go target-yellow-jump-blast)) (if (= arg2 0.0) (set-forward-vel arg2) (set-forward-vel (-> self control ctrl-xz-vel))) (set-time! (-> self state-time)) @@ -1825,9 +1859,10 @@ (suspend) (ja :num! (seek!)) (set! f30-0 (* f30-0 (fmin 1.0 (-> self control unknown-float140)))))) - (if (and (or (not (time-elapsed? (the-as time-frame gp-0) (-> *TARGET-bank* wheel-jump-pre-window))) + (when (and (or (not (time-elapsed? (the-as time-frame gp-0) (-> *TARGET-bank* wheel-jump-pre-window))) (cpad-pressed? (-> self control cpad number) x)) - (can-jump? 'target-wheel-flip)) + (can-jump? 'target-wheel-flip) + (mod-rule-trigger-sizeable)) (go target-wheel-flip (-> *TARGET-bank* wheel-flip-height) (-> *TARGET-bank* wheel-flip-dist)))) (set-time! (-> self state-hook-time)) (set! (-> self state-hook) @@ -1837,7 +1872,9 @@ (set! (-> self state-hook) (the-as (function none :behavior target) nothing))) (else ;; og:preserve-this - High FPS Fix - (if (and (recently-pressed? x) (can-jump? 'target-wheel-flip)) + (when (and (recently-pressed? x) + (can-jump? 'target-wheel-flip) + (mod-rule-trigger-sizeable)) (go target-wheel-flip (-> *TARGET-bank* wheel-flip-height) (-> *TARGET-bank* wheel-flip-dist))))) (none))) (go target-duck-stance)) @@ -1847,6 +1884,8 @@ :event target-standard-event-handler :enter (behavior ((arg0 float) (arg1 float)) + (mod-rule-trigger-brittle-bones) + (mod-rule-trigger-grounded) (set! (-> self control mod-surface) *wheel-flip-mods*)) :exit target-exit :trans @@ -1917,7 +1956,7 @@ (set! (-> self state-hook) (the-as (function none :behavior target) nothing))) (else ;; og:preserve-this - High FPS Fix - (if (and (recently-pressed? x) (can-jump? #f)) + (when (and (recently-pressed? x) (can-jump? #f)) (go target-high-jump (-> *TARGET-bank* flip-jump-height-min) (-> *TARGET-bank* flip-jump-height-max) 'flip)))) (none))) (if (ja-group? eichar-jump-loop-ja) (go target-hit-ground #f) (go target-stance))) diff --git a/goal_src/jak1/engine/target/target2.gc b/goal_src/jak1/engine/target/target2.gc index 9313a50e56..62e6fe6c6f 100644 --- a/goal_src/jak1/engine/target/target2.gc +++ b/goal_src/jak1/engine/target/target2.gc @@ -7,6 +7,7 @@ (declare-type first-person-hud process) ;; DECOMP BEGINS +(define *last-launch-time* 0) (defstate target-load-wait (target) :event @@ -678,6 +679,8 @@ (vector-flatten! (-> self control transv) (-> self control transv) (-> self control edge-grab-edge-dir))) :code (behavior ((arg0 float) (arg1 float)) + (mod-rule-trigger-brittle-bones) + (mod-rule-trigger-grounded) (sound-play "jump") (send-event *camera* 'damp-up) (ja :group! eichar-pole-jump-loop-ja :num! min) @@ -772,6 +775,8 @@ (set! (-> a1-2 solid-only) #t) (when (not (fill-and-probe-using-spheres *collide-cache* a1-2)) (remove-exit) + (mod-rule-trigger-brittle-bones) + (mod-rule-trigger-grounded) (go target-edge-grab-jump (-> *TARGET-bank* edge-grab-jump-height-min) (-> *TARGET-bank* edge-grab-jump-height-max))))) (else (remove-exit) (go target-edge-grab-off))))) :code @@ -1566,6 +1571,8 @@ ((-> target-swim-stance exit)) (die-on-next-update! (-> self water bob)) (set! (-> self water align-offset) 0.0) + (mod-rule-trigger-brittle-bones) + (mod-rule-trigger-grounded) (logior! (-> self water flag) (water-flag jump-out))) :code (behavior ((arg0 float) (arg1 float)) @@ -1635,6 +1642,7 @@ (target-standard-event-handler proc argc message block))) :code (behavior ((arg0 float) (arg1 symbol) (arg2 vector) (arg3 int)) + (set! *last-launch-time* (current-time)) (set-time! (-> self state-time)) (set! (-> self control mod-surface) *turn-around-mods*) (ja-channel-push! 1 (seconds 0.15)) diff --git a/goal_src/jak1/kernel/gcommon.gc b/goal_src/jak1/kernel/gcommon.gc index 5a3d3eeba1..ba7ae4d58d 100644 --- a/goal_src/jak1/kernel/gcommon.gc +++ b/goal_src/jak1/kernel/gcommon.gc @@ -48,6 +48,12 @@ (defconstant BASIC_OFFSET 4) +;; The distance tracker +(define *closest-buzzer-dist* 1000000000.0) + +;; Tracks the last frame a crate updated the distance (using int instead of time-frame) +(define *closest-buzzer-frame* (the-as int 0)) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; Macros ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; diff --git a/goal_src/jak1/levels/beach/lurkerworm.gc b/goal_src/jak1/levels/beach/lurkerworm.gc index 4fa33ec5ce..3ba8d12555 100644 --- a/goal_src/jak1/levels/beach/lurkerworm.gc +++ b/goal_src/jak1/levels/beach/lurkerworm.gc @@ -217,7 +217,7 @@ (defbehavior lurkerworm-default-event-handler lurkerworm ((arg0 process) (arg1 int) (arg2 symbol) (arg3 event-message-block)) (case arg2 - (('attack) (-> arg3 param 1) (go lurkerworm-die)) + (('attack) (-> arg3 param 1) (notify-enemy-death self) (go lurkerworm-die)) (('touch) (if ((method-of-type touching-shapes-entry prims-touching?) (the-as touching-shapes-entry (-> arg3 param 0)) @@ -244,9 +244,10 @@ lurkerworm-default-post-behavior :code (behavior () (loop - (if (and (time-elapsed? (-> self state-time) (seconds 1)) + (when (and (time-elapsed? (-> self state-time) (seconds 1)) *target* (>= 81920.0 (vector-vector-distance (-> self root trans) (-> *target* control trans)))) + (mod-rule-trigger-mean-look) (go lurkerworm-spot)) (suspend))) :post lurkerworm-default-post-behavior) diff --git a/goal_src/jak1/levels/firecanyon/firecanyon-obs.gc b/goal_src/jak1/levels/firecanyon/firecanyon-obs.gc index f2d861191c..f6f92d4612 100644 --- a/goal_src/jak1/levels/firecanyon/firecanyon-obs.gc +++ b/goal_src/jak1/levels/firecanyon/firecanyon-obs.gc @@ -5,6 +5,8 @@ ;; DECOMP BEGINS +(define-extern mod-rule-trigger-heating-balloons (function none)) + (deftype balloon (process-drawable) ((root collide-shape :override)) (:states @@ -91,7 +93,7 @@ :event (behavior ((proc process) (argc int) (message symbol) (block event-message-block)) (case message - (('attack) (send-event proc 'heat -10.0) (go balloon-popping)))) + (('attack) (send-event proc 'heat -10.0) (mod-rule-trigger-heating-balloons)(go balloon-popping)))) :code (behavior () (transform-post) @@ -456,6 +458,7 @@ (ja-channel-set! 0) (clear-collide-with-as (-> self root)) (ja-post) + (runs-on-crate-break self 'break 8) (sound-play "dcrate-break") (process-spawn part-tracker :init diff --git a/goal_src/jak1/levels/flut_common/target-flut.gc b/goal_src/jak1/levels/flut_common/target-flut.gc index 1d61e0f3ab..6ddd736e73 100644 --- a/goal_src/jak1/levels/flut_common/target-flut.gc +++ b/goal_src/jak1/levels/flut_common/target-flut.gc @@ -528,6 +528,8 @@ (else (target-flut-standard-event-handler proc argc message block)))) :enter (behavior ((arg0 float) (arg1 float)) + (mod-rule-trigger-brittle-bones) + (mod-rule-trigger-grounded) (set-time! (-> self state-time)) (sound-play "jump" :pitch -0.5) (init-var-jump arg0 arg1 (the-as vector #t) (the-as vector #t) (-> self control transv)) @@ -607,6 +609,8 @@ event) :enter (behavior ((arg0 float) (arg1 float)) + (mod-rule-trigger-brittle-bones) + (mod-rule-trigger-grounded) (set-time! (-> self state-time)) (init-var-jump arg0 arg1 (the-as vector #t) (the-as vector #t) (-> self control transv)) (set! (-> self control dynam gravity-max) 40960.0) @@ -1095,10 +1099,10 @@ (go target-flut-stance)) (else (pickup-collectable! (-> self fact) (pickup-type eco-green) (the-as float -1000.0) (the-as handle #f)) - (go target-flut-death (-> gp-0 mode))))) + (mod-rule-on-jak-death) (go target-flut-death (-> gp-0 mode))))) (('water-vol 'sharkey) (pickup-collectable! (-> self fact) (pickup-type eco-green) (the-as float -1000.0) (the-as handle #f)) - (if (= (-> self game mode) 'play) (go target-flut-death (-> gp-0 mode)))) + (when (= (-> self game mode) 'play) (mod-rule-on-jak-death) (go target-flut-death (-> gp-0 mode)))) (('death) (pickup-collectable! (-> self fact) (pickup-type eco-green) (the-as float -1000.0) (the-as handle #f))) (else (pickup-collectable! (-> self fact) (pickup-type eco-green) (- (-> *FACT-bank* health-single-inc)) (the-as handle #f)))) @@ -1118,7 +1122,7 @@ (ja-channel-push! 1 (seconds 0.075)) (ja :group! eichar-flut-hit-back-ja :num! min)))) (target-hit-move gp-0 (target-hit-orient gp-0 s5-0) target-flut-falling-anim-trans f30-0))) - (if (and (= (-> self game mode) 'play) (>= 0.0 (-> self fact health))) (go target-flut-death (-> gp-0 mode)))) + (when (and (= (-> self game mode) 'play) (>= 0.0 (-> self fact health))) (mod-rule-on-jak-death) (go target-flut-death (-> gp-0 mode)))) (go target-flut-hit-ground)) :post target-flut-post) diff --git a/goal_src/jak1/levels/jungle/junglesnake.gc b/goal_src/jak1/levels/jungle/junglesnake.gc index 7953165646..aa1fd18205 100644 --- a/goal_src/jak1/levels/jungle/junglesnake.gc +++ b/goal_src/jak1/levels/jungle/junglesnake.gc @@ -101,7 +101,7 @@ (else (do-push-aways! (-> self root)) (send-event arg0 'shove (-> arg3 param 0) (static-attack-info ((shove-back (meters 2)) (shove-up (meters 0.5))))))))) - (('attack) (go junglesnake-die)))) + (('attack) (notify-enemy-death self) (go junglesnake-die)))) junglesnake-default-event-handler @@ -223,7 +223,9 @@ junglesnake-default-event-handler (when *target* (let* ((a0-1 (target-pos 0)) (f0-1 (- (-> a0-1 y) (-> self root trans y)))) - (if (and (>= 40960.0 f0-1) (>= 143360.0 (vector-vector-xz-distance a0-1 (-> self root trans)))) (go junglesnake-wake))))) + (when (and (>= 40960.0 f0-1) (>= 143360.0 (vector-vector-xz-distance a0-1 (-> self root trans)))) + (mod-rule-trigger-mean-look) + (go junglesnake-wake))))) :code (behavior () (logior! (-> self mask) (process-mask sleep-code)) diff --git a/goal_src/jak1/levels/jungleb/plant-boss.gc b/goal_src/jak1/levels/jungleb/plant-boss.gc index 711a59cdd5..417d3382c2 100644 --- a/goal_src/jak1/levels/jungleb/plant-boss.gc +++ b/goal_src/jak1/levels/jungleb/plant-boss.gc @@ -1100,7 +1100,7 @@ (ja-blend-eval) (suspend) (ja :num! (seek! (ja-aframe (the-as float 20.0) 0)))))) - (if (= (-> self health) 0.0) (go plant-boss-dead #f)) + (when (= (-> self health) 0.0) (notify-enemy-death self) (go plant-boss-dead #f)) (ja-no-eval :num! (seek!)) (while (not (ja-done? 0)) (suspend) diff --git a/goal_src/jak1/levels/maincave/baby-spider.gc b/goal_src/jak1/levels/maincave/baby-spider.gc index 390a6befdf..e33ab23a9c 100644 --- a/goal_src/jak1/levels/maincave/baby-spider.gc +++ b/goal_src/jak1/levels/maincave/baby-spider.gc @@ -259,6 +259,7 @@ baby-spider-default-event-handler :event baby-spider-default-event-handler :code (behavior () + (mod-rule-trigger-mean-look) (cond ((not *target*) (go-virtual nav-enemy-idle)) ((target-in-range? self (-> self nav-info notice-distance)) (go-virtual nav-enemy-chase)) @@ -455,6 +456,7 @@ baby-spider-default-event-handler :event baby-spider-default-event-handler :code (behavior () + (notify-enemy-death self) (cleanup-for-death self) (let ((v1-2 (-> self event-death))) (if v1-2 (send-event (ppointer->process (-> self parent)) v1-2))))) diff --git a/goal_src/jak1/levels/maincave/driller-lurker.gc b/goal_src/jak1/levels/maincave/driller-lurker.gc index af5c8385f2..16074a9091 100644 --- a/goal_src/jak1/levels/maincave/driller-lurker.gc +++ b/goal_src/jak1/levels/maincave/driller-lurker.gc @@ -533,6 +533,7 @@ :enter (behavior ((arg0 symbol)) (if arg0 (set-time! (-> self started-chasing-time))) + (mod-rule-trigger-mean-look) (set! (-> self mode) (the-as uint 3)) (set-time! (-> self state-time)) (set! (-> self targ-path-speed) 23552.0)) @@ -617,6 +618,7 @@ :event process-drawable-death-event-handler :code (behavior () + (notify-enemy-death self) (shut-down! (-> self neck)) (logclear! (-> self mask) (process-mask actor-pause)) (ja-channel-push! 1 (seconds 0.2)) diff --git a/goal_src/jak1/levels/maincave/gnawer.gc b/goal_src/jak1/levels/maincave/gnawer.gc index 773c85700c..841e4dc033 100644 --- a/goal_src/jak1/levels/maincave/gnawer.gc +++ b/goal_src/jak1/levels/maincave/gnawer.gc @@ -677,6 +677,7 @@ (logior! (-> self mask) (process-mask actor-pause))) :code (behavior () + (mod-rule-trigger-mean-look) (logclear! (-> self mask) (process-mask actor-pause)) (ja-channel-push! 1 (seconds 0.075)) (ja-no-eval :group! gnawer-notice-ja :num! (seek!) :frame-num 0.0) @@ -761,6 +762,7 @@ (defstate gnawer-die (gnawer) :enter (behavior () + (notify-enemy-death self) (let ((v1-0 (-> self segments))) (set! (-> self fall-trans quad) (-> v1-0 0 world-pos quad)) (vector-! (-> self root transv) (-> v1-0 0 world-pos) (-> self post-trans))) diff --git a/goal_src/jak1/levels/maincave/mother-spider.gc b/goal_src/jak1/levels/maincave/mother-spider.gc index 94e39a02fb..5ee3607168 100644 --- a/goal_src/jak1/levels/maincave/mother-spider.gc +++ b/goal_src/jak1/levels/maincave/mother-spider.gc @@ -655,6 +655,7 @@ :event mother-spider-default-event-handler :enter (behavior () + (mod-rule-trigger-mean-look) (set! (-> self hit?) #f) (set-time! (-> self state-time)) (set-time! (-> self last-player-in-air-time))) @@ -937,6 +938,7 @@ :event mother-spider-death-event-handler :enter (behavior () + (notify-enemy-death self) (shut-down! (-> self neck)) (set! (-> self thread-speed) 122880.0) (set! (-> self targ-dist-from-anchor) (+ -24576.0 (-> self dist-from-anchor))) @@ -983,6 +985,7 @@ (mother-spider-method-29 self #f #f)) :code (behavior () + (notify-enemy-death self) (let ((v1-2 (-> self entity extra perm))) (logior! (-> v1-2 status) (entity-perm-status user-set-from-cstage)) (set! (-> v1-2 user-int8 0) 1)) @@ -1001,6 +1004,7 @@ :event mother-spider-death-event-handler :code (behavior () + (notify-enemy-death self) (logclear! (-> self mask) (process-mask actor-pause)) (clear-collide-with-as (-> self root)) (set! (-> self skel postbind-function) #f) diff --git a/goal_src/jak1/levels/misty/balloonlurker.gc b/goal_src/jak1/levels/misty/balloonlurker.gc index e720e86670..a096593c72 100644 --- a/goal_src/jak1/levels/misty/balloonlurker.gc +++ b/goal_src/jak1/levels/misty/balloonlurker.gc @@ -577,6 +577,7 @@ (set! (-> self dead) #t)) :code (behavior () + (notify-enemy-death self) (local-vars (v1-27 symbol)) (process-entity-status! self (entity-perm-status bit-4) #t) (process-entity-status! self (entity-perm-status dead) #t) diff --git a/goal_src/jak1/levels/misty/quicksandlurker.gc b/goal_src/jak1/levels/misty/quicksandlurker.gc index 52244a7450..23ee1474b4 100644 --- a/goal_src/jak1/levels/misty/quicksandlurker.gc +++ b/goal_src/jak1/levels/misty/quicksandlurker.gc @@ -656,6 +656,7 @@ :trans quicksandlurker-check-hide-transition :code (behavior () + (mod-rule-trigger-mean-look) (process-spawn part-tracker :init part-tracker-init @@ -679,6 +680,7 @@ :event process-drawable-death-event-handler :code (behavior () + (notify-enemy-death self) (logclear! (-> self mask) (process-mask actor-pause)) (ja-channel-push! 1 (seconds 0.2)) (ja-no-eval :group! quicksandlurker-die-ja :num! (seek!) :frame-num 0.0) diff --git a/goal_src/jak1/levels/ogre/ogreboss.gc b/goal_src/jak1/levels/ogre/ogreboss.gc index 2468726f0a..a056eedb00 100644 --- a/goal_src/jak1/levels/ogre/ogreboss.gc +++ b/goal_src/jak1/levels/ogre/ogreboss.gc @@ -1332,6 +1332,7 @@ (defstate ogreboss-die (ogreboss) :code (behavior () + (notify-enemy-death self) (ogreboss-reset-camera) (send-event (handle->process (-> self boulder)) 'go-die) (ja-channel-push! 1 (seconds 0.2)) diff --git a/goal_src/jak1/levels/racer_common/racer-states.gc b/goal_src/jak1/levels/racer_common/racer-states.gc index 746250fc20..98659ebcaf 100644 --- a/goal_src/jak1/levels/racer_common/racer-states.gc +++ b/goal_src/jak1/levels/racer_common/racer-states.gc @@ -11,6 +11,8 @@ ;; DECOMP BEGINS +(define-extern mod-rule-on-jak-death (function none)) + (defstate target-racing-start (target) :event (behavior ((proc process) (argc int) (message symbol) (block event-message-block)) @@ -342,6 +344,8 @@ event) :enter (behavior ((arg0 float) (arg1 float) (arg2 symbol)) + (mod-rule-trigger-brittle-bones) + (mod-rule-trigger-grounded) (set! (-> self racer shock-offsetv) 0.0) (sound-play "zoomer-jump") (set-time! (-> self state-time)) @@ -569,7 +573,7 @@ (set! (-> self racer boost-target) 0.0) (set! (-> self racer boost-output) 0.0) (set! (-> self racer boost-time) 0) - (if (and (= (-> self game mode) 'play) (>= 0.0 (-> self fact health))) (go target-racing-death (-> self attack-info mode))) + (when (and (= (-> self game mode) 'play) (>= 0.0 (-> self fact health))) (mod-rule-on-jak-death) (go target-racing-death (-> self attack-info mode))) (case (-> self attack-info mode) (('endlessfall)) (('darkeco) @@ -767,6 +771,8 @@ exit) :code (behavior ((arg0 handle)) + (set! *racer-start-time* (current-time)) + ;; (format #t "get on~%") (set! (-> self control transv quad) (the-as uint128 0)) (set! (-> self alt-cam-pos quad) (-> self control camera-pos quad)) (logior! (-> self state-flags) (state-flags use-alt-cam-pos)) @@ -858,6 +864,8 @@ exit) :code (behavior ((arg0 handle)) + ;; (set! *racer-start-time* 0) + ;; (format #t "get off~%") (set-time! (-> self state-time)) (set! (-> self control mod-surface) *racer-mods*) (let ((a0-2 (-> *hud-parts* bike-speed))) (if a0-2 (set! (-> a0-2 0 deactivate-when-hidden) #t))) diff --git a/goal_src/jak1/levels/racer_common/target-racer.gc b/goal_src/jak1/levels/racer_common/target-racer.gc index 29d53b6e29..0d13d2b990 100644 --- a/goal_src/jak1/levels/racer_common/target-racer.gc +++ b/goal_src/jak1/levels/racer_common/target-racer.gc @@ -9,6 +9,9 @@ ;; DECOMP BEGINS +(define-extern mod-rule-trigger-bus-driver (function (none))) +(define *racer-start-time* 0) + (define *racer-mods* (new 'static 'surface @@ -500,12 +503,18 @@ (send-event self 'attack #f (static-attack-info ((mode 'heat))))) ;; og:preserve-this allow boost with debug segment enabled (if (and *cheat-mode* (or *debug-segment* PC_PORT) (cpad-pressed? 0 l2)) (send-event self 'boost 1.0)) + (let ((v1-218 (- (current-time) (-> self racer boost-time)))) (if (< v1-218 (-> self racer boost-duration)) (set! (-> self racer boost-curve) (* (-> *RACER-bank* boost-curve-max) (sin (* 32768.0 (/ (the float v1-218) (the float (-> self racer boost-duration))))))) (set! (-> self racer boost-curve) 0.0))) + ;; (format #t "Speed: ~F~%" (-> self control ctrl-xz-vel)) + (when (and (or (not (cpad-hold? (-> self control cpad number) x)) + (< (-> self control ctrl-xz-vel) 81500.0)) + (>= (- (current-time) *racer-start-time*) (seconds 3))) + (mod-rule-trigger-bus-driver)) (when (or (not (cpad-hold? (-> self control cpad number) x)) (< (-> self control ctrl-xz-vel) 4096.0) (= (-> self racer boost-level) 0.0)) @@ -520,6 +529,7 @@ (-> v1-241 id)) (set! (-> self racer boost-sound-id) (new 'static 'sound-id)) 0) + (set! (-> self racer boost-level) 0.0)) (set! (-> self racer boost-target) (+ (-> self racer boost-curve) (-> self racer boost-level))) (seek! (-> self racer boost-output) (-> self racer boost-target) (* 40960.0 (seconds-per-frame))) diff --git a/goal_src/jak1/levels/rolling/rolling-robber.gc b/goal_src/jak1/levels/rolling/rolling-robber.gc index e87ff0c842..f9c4cce0ab 100644 --- a/goal_src/jak1/levels/rolling/rolling-robber.gc +++ b/goal_src/jak1/levels/rolling/rolling-robber.gc @@ -95,7 +95,9 @@ (defbehavior robber-event-handler robber ((arg0 process) (arg1 int) (arg2 symbol) (arg3 event-message-block)) (case arg2 - (('attack) (go robber-die)))) + (('attack) + (notify-enemy-death self) + (go robber-die)))) (defbehavior robber-task-complete? robber () (process-entity-status! self (entity-perm-status complete) #t) diff --git a/goal_src/jak1/levels/snow/ice-cube.gc b/goal_src/jak1/levels/snow/ice-cube.gc index 5a4edc5532..c92500e4ed 100644 --- a/goal_src/jak1/levels/snow/ice-cube.gc +++ b/goal_src/jak1/levels/snow/ice-cube.gc @@ -1035,6 +1035,7 @@ (defstate ice-cube-shatter (ice-cube) :code (behavior () + (notify-enemy-death self) (logclear! (-> self mask) (process-mask actor-pause)) (let ((gp-0 (new 'stack-no-clear 'vector))) (vector<-cspace! gp-0 (joint-node ice-cube-lod0-jg main)) diff --git a/goal_src/jak1/levels/snow/snow-ram-boss.gc b/goal_src/jak1/levels/snow/snow-ram-boss.gc index 07bf5a9d23..2e963c3f4f 100644 --- a/goal_src/jak1/levels/snow/snow-ram-boss.gc +++ b/goal_src/jak1/levels/snow/snow-ram-boss.gc @@ -1000,7 +1000,7 @@ :event (behavior ((proc process) (argc int) (message symbol) (block event-message-block)) (case message - (('jump) (go ram-boss-jump-down proc)) + (('jump) (mod-rule-trigger-mean-look) (go ram-boss-jump-down proc)) (('touch 'attack) (nav-enemy-send-attack proc (the-as touching-shapes-entry (-> block param 0)) 'generic)))) :enter (behavior () @@ -1143,7 +1143,7 @@ (behavior () (when *target* (let ((f0-0 (vector-vector-distance (target-pos 0) (-> self collide-info trans)))) - (if (>= 18432.0 f0-0) (go ram-boss-tracking)))) + (when (>= 18432.0 f0-0) (mod-rule-trigger-mean-look) (go ram-boss-tracking)))) ((-> (method-of-type nav-enemy nav-enemy-patrol) trans)))) (defstate nav-enemy-chase (ram-boss) diff --git a/goal_src/jak1/levels/sunken/bully.gc b/goal_src/jak1/levels/sunken/bully.gc index f969fb1f71..7e34f821d8 100644 --- a/goal_src/jak1/levels/sunken/bully.gc +++ b/goal_src/jak1/levels/sunken/bully.gc @@ -235,7 +235,7 @@ v0-0) ((= (-> arg0 type) target) (cond - ((= (-> arg3 param 1) 'flop) (go bully-die) (return #t) v0-0) + ((= (-> arg3 param 1) 'flop) (notify-enemy-death self) (go bully-die) (return #t) v0-0) ((or (= arg2 'touch) (= arg2 'attack)) (cond ((>= (- (-> (target-pos 0) y) (-> self root trans y)) 6144.0) @@ -391,6 +391,7 @@ (set-target! (-> self neck) (target-pos 5)))) :code (behavior () + (mod-rule-trigger-mean-look) (set! (-> self travel-speed) 0.0) (ja-channel-push! 1 (seconds 0.075)) (set-vector! (-> self root transv) 0.0 (rand-vu-float-range 61440.0 90112.0) 0.0 1.0) diff --git a/goal_src/jak1/levels/sunken/puffer.gc b/goal_src/jak1/levels/sunken/puffer.gc index f72929d319..bd6275714c 100644 --- a/goal_src/jak1/levels/sunken/puffer.gc +++ b/goal_src/jak1/levels/sunken/puffer.gc @@ -87,7 +87,7 @@ (let ((f0-4 (atan (- (-> (the-as process-drawable v1-7) root trans x) (-> self root trans x)) (- (-> (the-as process-drawable v1-7) root trans z) (-> self root trans z))))) (quaternion-axis-angle! (-> self root quat) 0.0 1.0 0.0 f0-4)))) - (go puffer-die) + (notify-enemy-death self) (go puffer-die) (return #t)) (when (= (-> arg0 type) target) (when (send-event arg0 'attack (-> arg3 param 0) (static-attack-info ((shove-up (meters 1.5)) (shove-back (meters 2.5))))) @@ -342,7 +342,8 @@ (when (puffer-method-23 self #t) (set-time! (-> self picked-point-time)) (set! (-> self pick-new-point-delay) (rand-vu-int-range (seconds 3) (seconds 10))))) - (if (and (time-elapsed? (-> self state-time) (-> self reaction-delay)) (puffer-method-25 self (-> self notice-dist))) + (when (and (time-elapsed? (-> self state-time) (-> self reaction-delay)) (puffer-method-25 self (-> self notice-dist))) + (mod-rule-trigger-mean-look) (go puffer-attack)) (when (or (< (vector-vector-xz-distance (-> self root trans) (-> self dest-pos)) 8192.0) (time-elapsed? (-> self picked-point-time) (-> self pick-new-point-delay))) diff --git a/goal_src/jak1/levels/swamp/kermit.gc b/goal_src/jak1/levels/swamp/kermit.gc index e0ad142fd5..5f19982285 100644 --- a/goal_src/jak1/levels/swamp/kermit.gc +++ b/goal_src/jak1/levels/swamp/kermit.gc @@ -746,7 +746,7 @@ nav-enemy-default-event-handler (time-elapsed? (-> self state-time) (seconds 1))) (go kermit-idle)) (kermit-set-rotate-dir-to-nav-target) - (if (and (not (-> self airborne)) (nav-enemy-test-point-in-nav-mesh? (target-pos 0))) (go kermit-notice)) + (when (and (not (-> self airborne)) (nav-enemy-test-point-in-nav-mesh? (target-pos 0))) (mod-rule-trigger-mean-look) (go kermit-notice)) (if (logtest? (nav-control-flags navcf19) (-> self nav flags)) (kermit-get-new-patrol-point))) :code (behavior () diff --git a/goal_src/jak1/levels/swamp/swamp-bat.gc b/goal_src/jak1/levels/swamp/swamp-bat.gc index 7633b8d10a..163f02a083 100644 --- a/goal_src/jak1/levels/swamp/swamp-bat.gc +++ b/goal_src/jak1/levels/swamp/swamp-bat.gc @@ -68,7 +68,7 @@ (defbehavior swamp-bat-slave-event-handler swamp-bat-slave ((arg0 process) (arg1 int) (arg2 symbol) (arg3 event-message-block)) (case arg2 - (('attack) (logclear! (-> self mask) (process-mask actor-pause)) (go swamp-bat-slave-die (process->handle arg0))) + (('attack) (logclear! (-> self mask) (process-mask actor-pause)) (notify-enemy-death self) (go swamp-bat-slave-die (process->handle arg0))) (('touch) (level-hint-spawn (text-id swamp-bat-duck-hint) "sksp0156" (the-as entity #f) *entity-pool* (game-task none)) (send-event arg0 'attack (-> arg3 param 0) (new 'static 'attack-info))) @@ -213,7 +213,7 @@ swamp-bat-slave-event-handler (ja-channel-push! 1 (seconds 0.1)) (ja :group! swamp-bat-swoop-ja) (loop - (if (>= (swamp-bat-slave-method-20 self) 2.0) (go swamp-bat-slave-strafe)) + (when (>= (swamp-bat-slave-method-20 self) 2.0) (mod-rule-trigger-mean-look) (go swamp-bat-slave-strafe)) (ja :num! (loop!)) (suspend))) :post swamp-bat-slave-path-post) diff --git a/goal_src/jak1/pc/features/speedruns.gc b/goal_src/jak1/pc/features/speedruns.gc index dec9857512..8802e365b6 100644 --- a/goal_src/jak1/pc/features/speedruns.gc +++ b/goal_src/jak1/pc/features/speedruns.gc @@ -151,6 +151,8 @@ ;; (game-task citadel-buzzer) ;; )) +(define-extern global-countdown-start (function none)) + (defun is-speedrun-category? ((category symbol)) (and (-> *pc-settings* speedrunner-mode?) (name= category (enum->string speedrun-category (-> *speedrun-info* category))))) @@ -174,6 +176,7 @@ ;; reset has-started? flag so we can flip it back to #t when jak touches ground/out of blackout (false! (-> *speedrun-info* has-started?)) ;; start new game with specified category, if any (otherwise we're resetting current category) + (global-countdown-start) (if category (set! (-> *speedrun-info* category) category)) (case (-> *speedrun-info* category) (((speedrun-category full-game)) diff --git a/goal_src/jak1/pc/subtitle.gc b/goal_src/jak1/pc/subtitle.gc index abad192f8d..603a79ee80 100644 --- a/goal_src/jak1/pc/subtitle.gc +++ b/goal_src/jak1/pc/subtitle.gc @@ -452,10 +452,12 @@ (set! (-> self cur-channel) (pc-subtitle-channel movie)) (with-proc ((handle->process (-> *art-control* spool-lock))) (if *debug-segment* - (format *stdcon* - "movie spool detected:~%~3L~A~0L~%current spool frame is ~3L~D~0L~%" - (-> *art-control* active-stream) - (the int (ja-aframe-num 0)))))) + ;; (format *stdcon* + ;; "movie spool detected:~%~3L~A~0L~%current spool frame is ~3L~D~0L~%" + ;; (-> *art-control* active-stream) + ;; (the int (ja-aframe-num 0))) + (none) + ))) (*hint-semaphore* ;; there's a hint playing (set! (-> self spool-name) (-> *hint-semaphore* 0 sound-to-play)) @@ -464,12 +466,14 @@ (set! (-> self text-id) (-> *hint-semaphore* 0 text-id-to-display)) (set! (-> self cur-channel) (if (-> self spool-name) (pc-subtitle-channel hint-named) (pc-subtitle-channel hint))) (if *debug-segment* - (format *stdcon* - "hint detected!~%~3L~A~0L/~3L#x~X~0L~%current str pos is ~D (~3L~D~0L)~%" - (-> self spool-name) - (-> self text-id) - (current-str-pos (-> *hint-semaphore* 0 sound-id)) - (subtitle-str-adjust (current-str-pos (-> *hint-semaphore* 0 sound-id))))))) + ;; (format *stdcon* + ;; "hint detected!~%~3L~A~0L/~3L#x~X~0L~%current str pos is ~D (~3L~D~0L)~%" + ;; (-> self spool-name) + ;; (-> self text-id) + ;; (current-str-pos (-> *hint-semaphore* 0 sound-id)) + ;; (subtitle-str-adjust (current-str-pos (-> *hint-semaphore* 0 sound-id)))) + (none) + ))) ;; do subtitles ;; intro first time notice (if subtitles are disabled) (when (and (= (pc-subtitle-channel movie) (-> self cur-channel)) diff --git a/out/build/Release/bin/decompiler.exe b/out/build/Release/bin/decompiler.exe index 1bdd52b288..30ece914f8 100644 Binary files a/out/build/Release/bin/decompiler.exe and b/out/build/Release/bin/decompiler.exe differ diff --git a/out/build/Release/bin/extractor.exe b/out/build/Release/bin/extractor.exe index 524e775418..0b97ee9a82 100644 Binary files a/out/build/Release/bin/extractor.exe and b/out/build/Release/bin/extractor.exe differ diff --git a/out/build/Release/bin/gk.exe b/out/build/Release/bin/gk.exe index 8755a34ee7..bbecfae411 100644 Binary files a/out/build/Release/bin/gk.exe and b/out/build/Release/bin/gk.exe differ diff --git a/out/build/Release/bin/goalc.exe b/out/build/Release/bin/goalc.exe index 2f36ddc875..5c392cfaac 100644 Binary files a/out/build/Release/bin/goalc.exe and b/out/build/Release/bin/goalc.exe differ