diff --git a/goal_src/jak3/dgos/game.gd b/goal_src/jak3/dgos/game.gd index a063c35d71c..fcf0fa02330 100644 --- a/goal_src/jak3/dgos/game.gd +++ b/goal_src/jak3/dgos/game.gd @@ -240,7 +240,10 @@ "game-task.o" "game-save.o" "settings.o" + "autosplit-h.o" ;; added + "autosplit.o" ;; added "popup-menu-h.o" ;; added + "speedruns-h.o" ;; added "mood-tables.o" "mood-tables2.o" "mood.o" @@ -344,6 +347,7 @@ "mech-h.o" "menu.o" "popup-menu.o" ;; added + "speedruns.o" ;; added "drawable.o" "drawable-group.o" "drawable-inline-array.o" diff --git a/goal_src/jak3/kernel-defs.gc b/goal_src/jak3/kernel-defs.gc index 852d4915503..5d65d70f5d7 100644 --- a/goal_src/jak3/kernel-defs.gc +++ b/goal_src/jak3/kernel-defs.gc @@ -249,6 +249,7 @@ ;; Jak 3 Specific Kernel Definitions (define *pc-waiting-on-rpc?* symbol) (define *pc-rpc-error?* symbol) + (define-extern pc-get-last-rpc-error (function string none)) (define-extern pc-fetch-external-race-times (function string none)) (define-extern pc-fetch-external-speedrun-times (function string none)) @@ -260,6 +261,27 @@ (define-extern pc-get-num-external-speedrun-times (function string int)) (define-extern pc-get-num-external-highscores (function string int)) +;; Speedrunner Mode Stuff +(define-extern pc-sr-mode-get-practice-entries-amount (function int)) +(define-extern pc-sr-mode-get-practice-entry-name (function int string none)) +(define-extern pc-sr-mode-get-practice-entry-continue-point (function int string none)) +(define-extern pc-sr-mode-get-practice-entry-history-success (function int int)) +(define-extern pc-sr-mode-get-practice-entry-history-attempts (function int int)) +(define-extern pc-sr-mode-get-practice-entry-session-success (function int int)) +(define-extern pc-sr-mode-get-practice-entry-session-attempts (function int int)) +(define-extern pc-sr-mode-get-practice-entry-avg-time (function int string none)) +(define-extern pc-sr-mode-get-practice-entry-fastest-time (function int string none)) +(define-extern pc-sr-mode-record-practice-entry-attempt! (function int symbol (pointer float) symbol)) +(declare-type speedrun-practice-objective structure) +(define-extern pc-sr-mode-init-practice-info! (function int speedrun-practice-objective none)) +;; TODO - a menu to dump out the 3 numbers with a pre-generated name to the file +(define-extern pc-sr-mode-get-custom-category-amount (function int)) +(define-extern pc-sr-mode-get-custom-category-name (function int string none)) +(define-extern pc-sr-mode-get-custom-category-continue-point (function int string none)) +(declare-type speedrun-custom-category structure) +(define-extern pc-sr-mode-init-custom-category-info! (function int speedrun-custom-category none)) +(define-extern pc-sr-mode-dump-new-custom-category (function speedrun-custom-category none)) + (define-extern file-stream-open (function file-stream string symbol file-stream)) (define-extern file-stream-close (function file-stream file-stream)) (define-extern file-stream-length (function file-stream int)) diff --git a/goal_src/jak3/kernel/gstring.gc b/goal_src/jak3/kernel/gstring.gc index af70c6fba2b..e2a78be40ab 100644 --- a/goal_src/jak3/kernel/gstring.gc +++ b/goal_src/jak3/kernel/gstring.gc @@ -815,8 +815,16 @@ (define *temp-string* (new 'global 'string 2048 (the-as string #f))) +;; og:preserve-this +(define *temp-string2* (new 'global 'string 2048 (the string #f))) + (#when PC_PORT (define *pc-encoded-temp-string* (new 'global 'string 2048 (the-as string #f)))) +(#when PC_PORT + (define *pc-cpp-temp-string* + "A convenient place to retrieve a string from C++" + (new 'global 'string 2048 (the string #f)))) + (kmemclose) (defmacro string-format (&rest args) diff --git a/goal_src/jak3/levels/desert/boss/terraformer-head.gc b/goal_src/jak3/levels/desert/boss/terraformer-head.gc index cbefa1bd82b..c1cf64726e5 100644 --- a/goal_src/jak3/levels/desert/boss/terraformer-head.gc +++ b/goal_src/jak3/levels/desert/boss/terraformer-head.gc @@ -2603,6 +2603,9 @@ :trans (behavior () (cond ((= (-> self hit-points) 0.0) + ;; og:preserve-this + (#when PC_PORT + (set! (-> *autosplit-info-jak3* errol-dead?) 1)) (let ((a1-0 (new 'stack-no-clear 'event-message-block))) (set! (-> a1-0 from) (process->ppointer self)) (set! (-> a1-0 num-params) 0) diff --git a/goal_src/jak3/pc/features/autosplit-h.gc b/goal_src/jak3/pc/features/autosplit-h.gc new file mode 100644 index 00000000000..fefd7099d76 --- /dev/null +++ b/goal_src/jak3/pc/features/autosplit-h.gc @@ -0,0 +1,163 @@ +;;-*-Lisp-*- +(in-package goal) + +;; LiveSplit ASL requires all settings to initalized _before_ you connect the process +;; Therefore everything has to be laid out in a predictable fashion before hand +;; So this is a lot of hard-coding, but not too bad when just copied from the debug menu code +;; +;; DO NOT change the order, appending to the end is safe! + +(deftype autosplit-info (structure) + (;; Version Info + (version-major uint16) + (version-minor uint16) + ;; General stats + (num-orbs uint32) + (num-skullgems uint32) + (errol-dead? uint8) + (all-collectables-acquired? uint8) + (padding-stats uint8 198) ;; padding for future growth + ;; loading/cutscene/control related info + (game-hash uint32) + (in-cutscene? uint8) + (is-loading? uint8) + (padding-controls uint8 200) ;; padding for future growth + ;; need-resolution tasks + (res-arena-training-1 uint8) + (res-arena-fight-1 uint8) + (res-wascity-chase uint8) + (res-wascity-pre-game uint8) + (res-desert-turtle-training uint8) + (res-desert-course-race uint8) + (res-desert-artifact-race-1 uint8) + (res-wascity-leaper-race uint8) + (res-desert-hover uint8) + (res-arena-fight-2 uint8) + (res-desert-catch-lizards uint8) + (res-desert-rescue uint8) + (res-wascity-gungame uint8) + (res-arena-fight-3 uint8) + (res-nest-eggs uint8) + (res-temple-climb uint8) + (res-desert-glide uint8) + (res-volcano-darkeco uint8) + (res-temple-oracle uint8) + (res-desert-oasis-defense uint8) + (res-temple-tests uint8) + (res-comb-travel uint8) + (res-mine-explore uint8) + (res-mine-blow uint8) + (res-mine-boss uint8) + (res-sewer-met-hum uint8) + (res-city-vehicle-training uint8) + (res-city-port-fight uint8) + (res-city-port-attack uint8) + (res-city-gun-course-1 uint8) + (res-city-sniper-fight uint8) + (res-sewer-kg-met uint8) + (res-city-destroy-darkeco uint8) + (res-forest-kill-plants uint8) + (res-city-destroy-grid uint8) + (res-city-hijack-vehicle uint8) + (res-city-port-assault uint8) + (res-city-gun-course-2 uint8) + (res-city-blow-barricade uint8) + (res-city-protect-hq uint8) + (res-sewer-hum-kg uint8) + (res-city-power-game uint8) + (res-desert-artifact-race-2 uint8) + (res-nest-hunt uint8) + (res-desert-beast-battle uint8) + (res-desert-jump-mission uint8) + (res-desert-chase-marauders uint8) + (res-forest-ring-chase uint8) + (res-factory-sky-battle uint8) + (res-factory-assault uint8) + (res-factory-boss uint8) + (res-temple-defend uint8) + (res-wascity-defend uint8) + (res-forest-turn-on-machine uint8) + (res-precursor-tour uint8) + (res-city-blow-tower uint8) + (res-tower-destroy uint8) + (res-palace-ruins-patrol uint8) + (res-palace-ruins-attack uint8) + (res-comb-wild-ride uint8) + (res-precursor-destroy-ship uint8) + (res-desert-final-boss uint8) + (res-city-win uint8) + (res-desert-bbush-get-to-1 uint8) + (res-desert-bbush-get-to-2 uint8) + (res-desert-bbush-get-to-3 uint8) + (res-desert-bbush-get-to-4 uint8) + (res-desert-bbush-get-to-5 uint8) + (res-desert-bbush-get-to-6 uint8) + (res-desert-bbush-get-to-7 uint8) + (res-desert-bbush-get-to-8 uint8) + (res-desert-bbush-get-to-9 uint8) + (res-desert-bbush-get-to-11 uint8) + (res-desert-bbush-get-to-12 uint8) + (res-desert-bbush-get-to-14 uint8) + (res-desert-bbush-get-to-16 uint8) + (res-desert-bbush-get-to-17 uint8) + (res-wascity-bbush-get-to-18 uint8) + (res-desert-bbush-get-to-19 uint8) + (res-wascity-bbush-get-to-20 uint8) + (res-wascity-bbush-get-to-21 uint8) + (res-wascity-bbush-get-to-22 uint8) + (res-wascity-bbush-get-to-23 uint8) + (res-wascity-bbush-get-to-24 uint8) + (res-wascity-bbush-get-to-25 uint8) + (res-city-bbush-get-to-26 uint8) + (res-city-bbush-get-to-27 uint8) + (res-city-bbush-get-to-28 uint8) + (res-city-bbush-get-to-29 uint8) + (res-city-bbush-get-to-30 uint8) + (res-city-bbush-get-to-31 uint8) + (res-city-bbush-get-to-32 uint8) + (res-city-bbush-get-to-33 uint8) + (res-city-bbush-get-to-34 uint8) + (res-city-bbush-get-to-35 uint8) + (res-city-bbush-get-to-36 uint8) + (res-city-bbush-get-to-37 uint8) + (res-city-bbush-get-to-38 uint8) + (res-city-bbush-get-to-39 uint8) + (res-city-bbush-get-to-40 uint8) + (res-city-bbush-get-to-41 uint8) + (res-city-bbush-get-to-42 uint8) + (res-city-bbush-get-to-43 uint8) + (res-city-bbush-get-to-44 uint8) + (res-desert-bbush-ring-1 uint8) + (res-desert-bbush-ring-2 uint8) + (res-wascity-bbush-ring-3 uint8) + (res-wascity-bbush-ring-4 uint8) + (res-city-bbush-ring-5 uint8) + (res-city-bbush-ring-6 uint8) + (res-desert-bbush-egg-spider-1 uint8) + (res-desert-bbush-spirit-chase-1 uint8) + (res-wascity-bbush-spirit-chase-2 uint8) + (res-city-bbush-spirit-chase-3 uint8) + (res-desert-bbush-timer-chase-1 uint8) + (res-wascity-bbush-timer-chase-2 uint8) + (res-desert-bbush-air-time uint8) + (res-desert-bbush-total-air-time uint8) + (res-desert-bbush-jump-distance uint8) + (res-desert-bbush-total-jump-distance uint8) + (res-desert-bbush-roll-count uint8) + (res-desert-bbush-time-trial-1 uint8) + (res-desert-bbush-rally uint8) + (res-city-bbush-port-attack uint8) + (res-desert-rescue-bbush uint8) + (res-city-gun-course-play-for-fun uint8) + (res-city-jetboard-bbush uint8) + (res-desert-bbush-destroy-interceptors uint8) + ;; TODO misc other task-nodes + ;; TODO - orbs in level X + ;; end marker just to make things look nice in a memory view + (end-marker uint8 4)) + (:methods + (reset! (_type_) object) + (update! (_type_) object) + (debug-draw (_type_) object))) + +(define-extern *autosplit-info-jak3* autosplit-info) diff --git a/goal_src/jak3/pc/features/autosplit.gc b/goal_src/jak3/pc/features/autosplit.gc new file mode 100644 index 00000000000..023e09dda88 --- /dev/null +++ b/goal_src/jak3/pc/features/autosplit.gc @@ -0,0 +1,261 @@ +;;-*-Lisp-*- +(in-package goal) +(define *autosplit-info-jak3* (new 'static 'autosplit-info)) + +(pc-init-autosplitter-struct) + +;; Setup Version +(set! (-> *autosplit-info-jak3* version-major) 0) + +(set! (-> *autosplit-info-jak3* version-minor) 1) + +;; Setup markers +(charp<-string (-> *autosplit-info-jak3* end-marker) "end") + +;; Setup Padding +(charp<-string (-> *autosplit-info-jak3* padding-stats) "padding-stats!") + +(charp<-string (-> *autosplit-info-jak3* padding-controls) "padding-controls!") + +(defconstant MAX_ORBS 600) + +(defconstant AUTOSPLITTER_DEBUG #f) + +(defmacro autosplit-flag-task-complete! (field-name task-name) + "Given a field name in the autosplitter struct, and a [[game-task]] name to check, sets either a 0 or a 1" + `(begin + (if (!= (-> this ,field-name) (if (task-complete? *game-info* (game-task ,task-name)) 1 0)) + (format 0 "AUTOSPLIT for ~A~%" (quote ,task-name))) + (set! (-> this ,field-name) (if (task-complete? *game-info* (game-task ,task-name)) 1 0)))) + +(defmacro autosplit-flag-task-node-closed! (field-name task-node-name) + "Given a field name in the autosplitter struct, and a [[game-task-node]] name to check, sets either a 0 or a 1" + `(begin + (if (!= (-> this ,field-name) (if (task-node-closed? (game-task-node ,task-node-name)) 1 0)) + (format 0 "AUTOSPLIT for ~A~%" (quote ,task-node-name))) + (set! (-> this ,field-name) (if (task-node-closed? (game-task-node ,task-node-name)) 1 0)))) + +(defmethod update! ((this autosplit-info)) + ;; general statistics + ;; when we are blacked out in loads the value of these are temporarily 0, and that messes with the auto splitter. + (let ((in-blackout? (>= (-> *game-info* blackout-time) (current-time)))) + (when (not in-blackout?) + (set! (-> this num-orbs) (the int (-> *game-info* skill-total))) + (set! (-> this num-skullgems) (the int (-> *game-info* gem-total))) + ;; ending conditions + ;; all collectables + ;; - check for all features + ;; - check for all vehicles + ;; - check for all inventory items + ;; - check for all orbs + (set! (-> this all-collectables-acquired?) + (if (and (logtesta? (-> *game-info* features) + (game-feature jakc + board + board-launch + board-zap + darkeco + darkjak + darkjak + darkjak-smack + darkjak-bomb0 + darkjak-bomb1 + lighteco + lightjak + lightjak-regen + lightjak-swoop + lightjak-freeze + lightjak-shield + gun + gun-red-1 + gun-yellow-1 + gun-blue-1 + gun-dark-1 + gun-red-2 + gun-yellow-2 + gun-blue-2 + gun-dark-2 + gun-red-3 + gun-yellow-3 + gun-blue-3 + gun-dark-3)) + (logtesta? (-> *game-info* vehicles) (game-vehicles v-turtle v-snake v-scorpion v-toad v-fox v-rhino v-mirage v-x-ride)) + (logtesta? (-> *game-info* items) + (game-items amulet0 + amulet1 + amulet2 + pass-front-gate + seal-of-mar + cypher-gliph + artifact-holocube + artifact-av-reflector + artifact-av-prism + artifact-av-generator + artifact-av-map + light-eco-crystal0 + light-eco-crystal1 + light-eco-crystal2 + light-eco-crystal3 + dark-eco-crystal0 + dark-eco-crystal1 + dark-eco-crystal2 + dark-eco-crystal3)) + (>= (-> this num-orbs) MAX_ORBS)) + 1 + 0)))) + ;; loading/cutscene related flags + (set! (-> this in-cutscene?) (if (movie?) 1 0)) + ;; need resolution flags + (autosplit-flag-task-complete! res-arena-training-1 arena-training-1) + (autosplit-flag-task-complete! res-arena-fight-1 arena-fight-1) + (autosplit-flag-task-complete! res-wascity-chase wascity-chase) + (autosplit-flag-task-complete! res-wascity-pre-game wascity-pre-game) + (autosplit-flag-task-complete! res-desert-turtle-training desert-turtle-training) + (autosplit-flag-task-complete! res-desert-course-race desert-course-race) + (autosplit-flag-task-complete! res-desert-artifact-race-1 desert-artifact-race-1) + (autosplit-flag-task-complete! res-wascity-leaper-race wascity-leaper-race) + (autosplit-flag-task-complete! res-desert-hover desert-hover) + (autosplit-flag-task-complete! res-arena-fight-2 arena-fight-2) + (autosplit-flag-task-complete! res-desert-catch-lizards desert-catch-lizards) + (autosplit-flag-task-complete! res-desert-rescue desert-rescue) + (autosplit-flag-task-complete! res-wascity-gungame wascity-gungame) + (autosplit-flag-task-complete! res-arena-fight-3 arena-fight-3) + (autosplit-flag-task-complete! res-nest-eggs nest-eggs) + (autosplit-flag-task-complete! res-temple-climb temple-climb) + (autosplit-flag-task-complete! res-desert-glide desert-glide) + (autosplit-flag-task-complete! res-volcano-darkeco volcano-darkeco) + (autosplit-flag-task-complete! res-temple-oracle temple-oracle) + (autosplit-flag-task-complete! res-desert-oasis-defense desert-oasis-defense) + (autosplit-flag-task-complete! res-temple-tests temple-tests) + (autosplit-flag-task-complete! res-comb-travel comb-travel) + (autosplit-flag-task-complete! res-mine-explore mine-explore) + (autosplit-flag-task-complete! res-mine-blow mine-blow) + (autosplit-flag-task-complete! res-mine-boss mine-boss) + (autosplit-flag-task-complete! res-sewer-met-hum sewer-met-hum) + (autosplit-flag-task-complete! res-city-vehicle-training city-vehicle-training) + (autosplit-flag-task-complete! res-city-port-fight city-port-fight) + (autosplit-flag-task-complete! res-city-port-attack city-port-attack) + (autosplit-flag-task-complete! res-city-gun-course-1 city-gun-course-1) + (autosplit-flag-task-complete! res-city-sniper-fight city-sniper-fight) + (autosplit-flag-task-complete! res-sewer-kg-met sewer-kg-met) + (autosplit-flag-task-complete! res-city-destroy-darkeco city-destroy-darkeco) + (autosplit-flag-task-complete! res-forest-kill-plants forest-kill-plants) + (autosplit-flag-task-complete! res-city-destroy-grid city-destroy-grid) + (autosplit-flag-task-complete! res-city-hijack-vehicle city-hijack-vehicle) + (autosplit-flag-task-complete! res-city-port-assault city-port-assault) + (autosplit-flag-task-complete! res-city-gun-course-2 city-gun-course-2) + (autosplit-flag-task-complete! res-city-blow-barricade city-blow-barricade) + (autosplit-flag-task-complete! res-city-protect-hq city-protect-hq) + (autosplit-flag-task-complete! res-sewer-hum-kg sewer-hum-kg) + (autosplit-flag-task-complete! res-city-power-game city-power-game) + (autosplit-flag-task-complete! res-desert-artifact-race-2 desert-artifact-race-2) + (autosplit-flag-task-complete! res-nest-hunt nest-hunt) + (autosplit-flag-task-complete! res-desert-beast-battle desert-beast-battle) + (autosplit-flag-task-complete! res-desert-jump-mission desert-jump-mission) + (autosplit-flag-task-complete! res-desert-chase-marauders desert-chase-marauders) + (autosplit-flag-task-complete! res-forest-ring-chase forest-ring-chase) + (autosplit-flag-task-complete! res-factory-sky-battle factory-sky-battle) + (autosplit-flag-task-complete! res-factory-assault factory-assault) + (autosplit-flag-task-complete! res-factory-boss factory-boss) + (autosplit-flag-task-complete! res-temple-defend temple-defend) + (autosplit-flag-task-complete! res-wascity-defend wascity-defend) + (autosplit-flag-task-complete! res-forest-turn-on-machine forest-turn-on-machine) + (autosplit-flag-task-complete! res-precursor-tour precursor-tour) + (autosplit-flag-task-complete! res-city-blow-tower city-blow-tower) + (autosplit-flag-task-complete! res-tower-destroy tower-destroy) + (autosplit-flag-task-complete! res-palace-ruins-patrol palace-ruins-patrol) + (autosplit-flag-task-complete! res-palace-ruins-attack palace-ruins-attack) + (autosplit-flag-task-complete! res-comb-wild-ride comb-wild-ride) + (autosplit-flag-task-complete! res-precursor-destroy-ship precursor-destroy-ship) + (autosplit-flag-task-complete! res-desert-final-boss desert-final-boss) + (autosplit-flag-task-complete! res-city-win city-win) + (autosplit-flag-task-complete! res-desert-bbush-get-to-1 desert-bbush-get-to-1) + (autosplit-flag-task-complete! res-desert-bbush-get-to-2 desert-bbush-get-to-2) + (autosplit-flag-task-complete! res-desert-bbush-get-to-3 desert-bbush-get-to-3) + (autosplit-flag-task-complete! res-desert-bbush-get-to-4 desert-bbush-get-to-4) + (autosplit-flag-task-complete! res-desert-bbush-get-to-5 desert-bbush-get-to-5) + (autosplit-flag-task-complete! res-desert-bbush-get-to-6 desert-bbush-get-to-6) + (autosplit-flag-task-complete! res-desert-bbush-get-to-7 desert-bbush-get-to-7) + (autosplit-flag-task-complete! res-desert-bbush-get-to-8 desert-bbush-get-to-8) + (autosplit-flag-task-complete! res-desert-bbush-get-to-9 desert-bbush-get-to-9) + (autosplit-flag-task-complete! res-desert-bbush-get-to-11 desert-bbush-get-to-11) + (autosplit-flag-task-complete! res-desert-bbush-get-to-12 desert-bbush-get-to-12) + (autosplit-flag-task-complete! res-desert-bbush-get-to-14 desert-bbush-get-to-14) + (autosplit-flag-task-complete! res-desert-bbush-get-to-16 desert-bbush-get-to-16) + (autosplit-flag-task-complete! res-desert-bbush-get-to-17 desert-bbush-get-to-17) + (autosplit-flag-task-complete! res-wascity-bbush-get-to-18 wascity-bbush-get-to-18) + (autosplit-flag-task-complete! res-desert-bbush-get-to-19 desert-bbush-get-to-19) + (autosplit-flag-task-complete! res-wascity-bbush-get-to-20 wascity-bbush-get-to-20) + (autosplit-flag-task-complete! res-wascity-bbush-get-to-21 wascity-bbush-get-to-21) + (autosplit-flag-task-complete! res-wascity-bbush-get-to-22 wascity-bbush-get-to-22) + (autosplit-flag-task-complete! res-wascity-bbush-get-to-23 wascity-bbush-get-to-23) + (autosplit-flag-task-complete! res-wascity-bbush-get-to-24 wascity-bbush-get-to-24) + (autosplit-flag-task-complete! res-wascity-bbush-get-to-25 wascity-bbush-get-to-25) + (autosplit-flag-task-complete! res-city-bbush-get-to-26 city-bbush-get-to-26) + (autosplit-flag-task-complete! res-city-bbush-get-to-27 city-bbush-get-to-27) + (autosplit-flag-task-complete! res-city-bbush-get-to-28 city-bbush-get-to-28) + (autosplit-flag-task-complete! res-city-bbush-get-to-29 city-bbush-get-to-29) + (autosplit-flag-task-complete! res-city-bbush-get-to-30 city-bbush-get-to-30) + (autosplit-flag-task-complete! res-city-bbush-get-to-31 city-bbush-get-to-31) + (autosplit-flag-task-complete! res-city-bbush-get-to-32 city-bbush-get-to-32) + (autosplit-flag-task-complete! res-city-bbush-get-to-33 city-bbush-get-to-33) + (autosplit-flag-task-complete! res-city-bbush-get-to-34 city-bbush-get-to-34) + (autosplit-flag-task-complete! res-city-bbush-get-to-35 city-bbush-get-to-35) + (autosplit-flag-task-complete! res-city-bbush-get-to-36 city-bbush-get-to-36) + (autosplit-flag-task-complete! res-city-bbush-get-to-37 city-bbush-get-to-37) + (autosplit-flag-task-complete! res-city-bbush-get-to-38 city-bbush-get-to-38) + (autosplit-flag-task-complete! res-city-bbush-get-to-39 city-bbush-get-to-39) + (autosplit-flag-task-complete! res-city-bbush-get-to-40 city-bbush-get-to-40) + (autosplit-flag-task-complete! res-city-bbush-get-to-41 city-bbush-get-to-41) + (autosplit-flag-task-complete! res-city-bbush-get-to-42 city-bbush-get-to-42) + (autosplit-flag-task-complete! res-city-bbush-get-to-43 city-bbush-get-to-43) + (autosplit-flag-task-complete! res-city-bbush-get-to-44 city-bbush-get-to-44) + (autosplit-flag-task-complete! res-desert-bbush-ring-1 desert-bbush-ring-1) + (autosplit-flag-task-complete! res-desert-bbush-ring-2 desert-bbush-ring-2) + (autosplit-flag-task-complete! res-wascity-bbush-ring-3 wascity-bbush-ring-3) + (autosplit-flag-task-complete! res-wascity-bbush-ring-4 wascity-bbush-ring-4) + (autosplit-flag-task-complete! res-city-bbush-ring-5 city-bbush-ring-5) + (autosplit-flag-task-complete! res-city-bbush-ring-6 city-bbush-ring-6) + (autosplit-flag-task-complete! res-desert-bbush-egg-spider-1 desert-bbush-egg-spider-1) + (autosplit-flag-task-complete! res-desert-bbush-spirit-chase-1 desert-bbush-spirit-chase-1) + (autosplit-flag-task-complete! res-wascity-bbush-spirit-chase-2 wascity-bbush-spirit-chase-2) + (autosplit-flag-task-complete! res-city-bbush-spirit-chase-3 city-bbush-spirit-chase-3) + (autosplit-flag-task-complete! res-desert-bbush-timer-chase-1 desert-bbush-timer-chase-1) + (autosplit-flag-task-complete! res-wascity-bbush-timer-chase-2 wascity-bbush-timer-chase-2) + (autosplit-flag-task-complete! res-desert-bbush-air-time desert-bbush-air-time) + (autosplit-flag-task-complete! res-desert-bbush-total-air-time desert-bbush-total-air-time) + (autosplit-flag-task-complete! res-desert-bbush-jump-distance desert-bbush-jump-distance) + (autosplit-flag-task-complete! res-desert-bbush-total-jump-distance desert-bbush-total-jump-distance) + (autosplit-flag-task-complete! res-desert-bbush-roll-count desert-bbush-roll-count) + (autosplit-flag-task-complete! res-desert-bbush-time-trial-1 desert-bbush-time-trial-1) + (autosplit-flag-task-complete! res-desert-bbush-rally desert-bbush-rally) + (autosplit-flag-task-complete! res-city-bbush-port-attack city-bbush-port-attack) + (autosplit-flag-task-complete! res-desert-rescue-bbush desert-rescue-bbush) + (autosplit-flag-task-complete! res-city-gun-course-play-for-fun city-gun-course-play-for-fun) + (autosplit-flag-task-complete! res-city-jetboard-bbush city-jetboard-bbush) + (autosplit-flag-task-complete! res-desert-bbush-destroy-interceptors desert-bbush-destroy-interceptors) + ;; misc other tasks + ; (autosplit-flag-task-node-closed! tomb-poles-poles tomb-poles-poles) ;; left tomb/daxter boulder start + ; (autosplit-flag-task-node-closed! fortress-save-friends-introduction fortress-save-friends-introduction) ;; talk to torn (before rescue friends) + ; (autosplit-flag-task-node-closed! sewer-escort-get-gun sewer-escort-get-gun) ;; get peacemaker + ; (autosplit-flag-task-node-closed! forest-protect-introduction forest-protect-introduction) ;; talk to onin (protect samos) + ; (autosplit-flag-task-node-closed! forest-protect-meeting forest-protect-meeting) ;; talk to samos (protect samos) + ;; debug only, draw stuff to the screen so i don't have to stare at a memory editor + (if AUTOSPLITTER_DEBUG (debug-draw this))) + +(defmethod reset! ((this autosplit-info)) + (set! (-> this game-hash) (pc-get-unix-timestamp)) + (set! (-> this errol-dead?) 0)) + +(defmethod debug-draw ((this autosplit-info)) + (format (clear *temp-string*) "errol-dead?: ~D~%" (-> this errol-dead?)) + (format *temp-string* "all-collectables-acquired?: ~D~%" (-> this all-collectables-acquired?)) + (with-dma-buffer-add-bucket ((buf (-> (current-frame) global-buf)) (bucket-id debug-no-zbuf1)) + ;; reset bucket settings prior to drawing - font won't do this for us, and + ;; draw-raw-image can sometimes mess them up. (intro sequence) + (dma-buffer-add-gs-set-flusha buf + (alpha-1 (new 'static 'gs-alpha :b #x1 :d #x1)) + (tex1-1 (new 'static 'gs-tex1 :mmag #x1 :mmin #x1))) + (let ((font-ctx (new 'stack 'font-context *font-default-matrix* 10 50 0.0 (font-color default) (font-flags shadow kerning large)))) + (set! (-> font-ctx scale) 0.325) + (draw-string-adv *temp-string* buf font-ctx)))) diff --git a/goal_src/jak3/pc/features/speedruns-h.gc b/goal_src/jak3/pc/features/speedruns-h.gc new file mode 100644 index 00000000000..3d5bede3235 --- /dev/null +++ b/goal_src/jak3/pc/features/speedruns-h.gc @@ -0,0 +1,134 @@ +;;-*-Lisp-*- +(in-package goal) + +;; TEST - safe with malformed entries + +(deftype speedrun-timer (process) + ((draw? symbol) + (started? symbol) + (stopped? symbol) + (start-time time-frame) + (end-time time-frame) + (recorded-time float)) + (:methods + (draw-timer (_type_) object) + (start! (_type_) object) + (reset! (_type_) object) + (stop! (_type_) float)) + (:state-methods + idle)) + +(defbehavior speedrun-timer-init-by-other speedrun-timer () + (false! (-> self draw?)) + (false! (-> self started?)) + (set! (-> self start-time) 0) + (set! (-> self end-time) 0) + (set! (-> self recorded-time) 0.0) + (go-virtual idle)) + +(defstate idle (speedrun-timer) + :virtual #t + :code + (behavior () + (loop + (when (-> self draw?) + (draw-timer self)) + (suspend)))) + +;; TODO - put in util +(deftype objective-zone (process) + ((start? symbol) + (v1 vector :inline) + (v2 vector :inline) + (on-enter (function none)) + (on-exit (function none))) + (:methods + (draw-zone (_type_) object)) + (:state-methods + waiting-for-player + player-inside)) + +(deftype objective-zone-init-params (structure) + ((v1 vector :inline) + (v2 vector :inline))) + +(defenum speedrun-practice-flags + (none)) + +;; reset method +(deftype speedrun-practice-objective (structure) + ((index int32) + (flags speedrun-practice-flags) + (completed-task game-task) + (features game-feature) + (secrets game-secrets) + (vehicles game-vehicles) + (starting-position vector) + (starting-rotation vector) + (starting-camera-position vector) + (starting-camera-rotation matrix) + (end-task game-task) + (start-zone-init-params objective-zone-init-params) + (start-zone (pointer objective-zone)) + (end-zone-init-params objective-zone-init-params) + (end-zone (pointer objective-zone))) + (:methods + (draw-info (_type_) object) + (reset! (_type_) object))) + +(defenum speedrun-category + :type uint32 + ;; Main Categories + (newgame-normal 0) + (newgame-heromode 1) + ;; TODO - add ILs and such later + ;; there's no point in adding categories that just start from a new-game and have later restrictions + ;; because we aren't going to modify the code to make that possible + ;; ie. removing mars tomb skip if you pick "all missions" + ;; Random one for experimentation + (all-cheats-allowed 999) + (custom 9999)) + +(deftype speedrun-custom-category (structure) + ((index int32) + (secrets game-secrets) + (features game-feature) + (vehicles game-vehicles) + (forbidden-features game-feature) + (pc-cheats pc-cheats) + (completed-task game-task))) + +(deftype speedrun-info (structure) + ((category speedrun-category) + (active-custom-category speedrun-custom-category) + (dump-custom-category speedrun-custom-category) + (display-run-info? symbol) + (practicing? symbol) + (active-practice-objective speedrun-practice-objective) + (waiting-to-record-practice-attempt? symbol) + (run-started-at time-frame)) + (:methods + (set-category! (_type_ speedrun-category) object) + (start-run! (_type_) object) + (enforce-settings! (_type_) object) + (update! (_type_) object) + (draw-run-info (_type_) object))) + +(define-extern *speedrun-info* speedrun-info) + +(defenum speedrun-menu-command + :type uint32 + (reset 0) + (exit 1)) + +(deftype speedrun-manager (process) + ((popup-menu (pointer popup-menu)) + (ignore-menu-toggle? symbol) + (opened-with-start? symbol) + (timer (pointer speedrun-timer))) + (:methods + (draw-menu (_type_) object)) + (:state-methods + idle)) + +(define-extern *speedrun-manager* (pointer speedrun-manager)) diff --git a/goal_src/jak3/pc/features/speedruns.gc b/goal_src/jak3/pc/features/speedruns.gc new file mode 100644 index 00000000000..398c6f6afe3 --- /dev/null +++ b/goal_src/jak3/pc/features/speedruns.gc @@ -0,0 +1,674 @@ +;;-*-Lisp-*- +(in-package goal) + +;; TODO later - customize menu open keybind + +(define-extern task-close! (function string symbol)) + +(define-extern *pc-dead-pool* dead-pool) + +(define *speedrun-info* (new 'static 'speedrun-info)) + +(set! (-> *speedrun-info* active-custom-category) (new 'static 'speedrun-custom-category)) + +(set! (-> *speedrun-info* dump-custom-category) (new 'static 'speedrun-custom-category)) + +(set! (-> *speedrun-info* active-practice-objective) (new 'static 'speedrun-practice-objective)) + +(set! (-> *speedrun-info* active-practice-objective starting-position) (new 'static 'vector)) + +(set! (-> *speedrun-info* active-practice-objective starting-rotation) (new 'static 'vector)) + +(set! (-> *speedrun-info* active-practice-objective starting-camera-position) (new 'static 'vector)) + +(set! (-> *speedrun-info* active-practice-objective starting-camera-rotation) (new 'static 'matrix)) + +(set! (-> *speedrun-info* active-practice-objective start-zone-init-params) (new 'static 'objective-zone-init-params)) + +(set! (-> *speedrun-info* active-practice-objective end-zone-init-params) (new 'static 'objective-zone-init-params)) + +(defmethod draw-timer ((this speedrun-timer)) + (clear *temp-string*) + (clear *pc-encoded-temp-string*) + (cond + ((-> this started?) + (format *temp-string* "~,,2fs~%" (* (the float (- (current-time) (-> this start-time))) 0.0033333334))) + ((and (!= 0 (-> this end-time))) + (format *temp-string* "~,,2fs~%" (* (the float (- (-> this end-time) (-> this start-time))) 0.0033333334))) + (else (format *temp-string* "0.0s~%"))) + (when *target* + (format *temp-string* "~,,2M~%" (-> *target* control ctrl-xz-vel))) + (pc-encode-utf8-string *temp-string* *pc-encoded-temp-string*) + (with-dma-buffer-add-bucket ((buf (-> (current-frame) global-buf)) (bucket-id debug-no-zbuf1)) + ;; reset bucket settings prior to drawing - font won't do this for us, and + ;; draw-raw-image can sometimes mess them up. (intro sequence) + (dma-buffer-add-gs-set-flusha buf + (alpha-1 (new 'static 'gs-alpha :b #x1 :d #x1)) + (tex1-1 (new 'static 'gs-tex1 :mmag #x1 :mmin #x1))) + (let ((font-ctx (new 'stack 'font-context *font-default-matrix* 256 350 0.0 (font-color default) (font-flags middle shadow kerning large)))) + (set! (-> font-ctx scale) 0.325) + (draw-string-adv *pc-encoded-temp-string* buf font-ctx)))) + +(defmethod start! ((this speedrun-timer)) + (true! (-> this started?)) + (false! (-> this stopped?)) + (set-time! (-> this start-time)) + (set! (-> this end-time) 0)) + +(defmethod reset! ((this speedrun-timer)) + (false! (-> this started?)) + (false! (-> this stopped?)) + (set! (-> this start-time) 0) + (set! (-> this end-time) 0)) + +(defmethod stop! ((this speedrun-timer)) + (when (not (-> this stopped?)) + (false! (-> this started?)) + (true! (-> this stopped?)) + (set-time! (-> this end-time)) + (set! (-> this recorded-time) (* (the float (- (-> this end-time) (-> this start-time))) 0.0033333334))) + (-> this recorded-time)) + +(defmethod set-category! ((this speedrun-info) (category speedrun-category)) + (set! (-> this category) category)) + +(defmethod start-run! ((this speedrun-info)) + ;; randomize game id so the autosplitter knows to restart + (reset! *autosplit-info-jak3*) + ;; turn on speedrun verification display + (true! (-> this display-run-info?)) + (send-event (ppointer->process *speedrun-manager*) 'start-run) + ;; ensure any required settings are enabled + (enforce-settings! this) + ;; finalize any category specific setup code + (case (-> this category) + (((speedrun-category newgame-normal)) + (initialize! *game-info* 'game (the game-save #f) "intro-start" (the resetter-spec #f))) + (((speedrun-category newgame-heromode)) + (initialize! *game-info* 'game (the game-save #f) "intro-start-hero" (the resetter-spec #f))) + (((speedrun-category all-cheats-allowed)) + (initialize! *game-info* 'game (the game-save #f) "intro-start" (the resetter-spec #f))) + (((speedrun-category custom)) + (set-master-mode 'game) + (send-event (ppointer->process (-> *speedrun-manager* 0 popup-menu)) 'close-menu) + (process-spawn-function process + (lambda :behavior process () + (clear *temp-string*) + (pc-sr-mode-get-custom-category-continue-point (-> *speedrun-info* active-custom-category index) *temp-string*) + (if (string= *temp-string* "") + (initialize! *game-info* 'game (the game-save #f) "intro-start" (the resetter-spec #f)) + (initialize! *game-info* 'game (the game-save #f) *temp-string* (the resetter-spec #f))) + (until (and *target* (= (-> *target* next-state name) 'target-stance)) + (suspend)) + (when (nonzero? (-> *speedrun-info* active-custom-category completed-task)) + (task-resolution-close! (-> *speedrun-info* active-custom-category completed-task))))))) + (if (!= -1 (-> *game-info* auto-save-which)) (set! (-> *setting-control* user-default auto-save) #t))) + +(defmethod enforce-settings! ((this speedrun-info)) + (true! (-> *pc-settings* ps2-actor-vis?)) ;; force PS2 actor visibility + (set-frame-rate! *pc-settings* 60 #t) ;; force FPS to `60` + ;; For posterity, the main reason why changing the cheats is useful is for two main reasons: + ;; - If you are playing a category that requires cheats (ie. a turbo jetboard one) you'd + ;; probably like the game to automatically set the appropriate ones for you + ;; - If you are playing a category that forbids cheats, you wouldn't want your run invalidated because you forgot + (case (-> this category) + (((speedrun-category newgame-normal) (speedrun-category newgame-heromode)) + ;; disable any active cheats + (set! (-> *pc-settings* cheats) (pc-cheats))) + (((speedrun-category custom)) + (set! (-> *game-info* secrets) (-> *speedrun-info* active-custom-category secrets)) + (logior! (-> *game-info* features) (-> *speedrun-info* active-custom-category features)) + (logclear! (-> *game-info* features) (-> *speedrun-info* active-custom-category forbidden-features)) + (logior! (-> *game-info* vehicles) (-> *speedrun-info* active-custom-category vehicles)) + (set! (-> *pc-settings* cheats) (-> *speedrun-info* active-custom-category pc-cheats))))) + +(defmethod draw-zone ((this objective-zone)) + (add-debug-box #t + (bucket-id debug) + (-> this v1) + (-> this v2) + (if (-> this start?) (static-rgba #xff #xff #x00 #x80) (static-rgba #xff #x00 #xff #x80)))) + +(defstate waiting-for-player (objective-zone) + :virtual #t + :event + (behavior ((proc process) (argc int) (message symbol) (block event-message-block)) + #t) + :trans + (behavior () + ;; Check to see if we have entered the zone + (let ((min-point-x (fmin (-> self v1 x) (-> self v2 x))) + (min-point-y (fmin (-> self v1 y) (-> self v2 y))) + (min-point-z (fmin (-> self v1 z) (-> self v2 z))) + (max-point-x (fmax (-> self v1 x) (-> self v2 x))) + (max-point-y (fmax (-> self v1 y) (-> self v2 y))) + (max-point-z (fmax (-> self v1 z) (-> self v2 z))) + (pos (target-pos 0))) + (when (and (and (<= min-point-x (-> pos x)) (<= (-> pos x) max-point-x)) + (and (<= min-point-y (-> pos y)) (<= (-> pos y) max-point-y)) + (and (<= min-point-z (-> pos z)) (<= (-> pos z) max-point-z))) + (when (nonzero? (-> self on-enter)) + ((-> self on-enter))) + (go-virtual player-inside)))) + :code + (behavior () + (loop + (draw-zone self) + (suspend)))) + +(defstate player-inside (objective-zone) + :virtual #t + :trans + (behavior () + ;; Check to see if we have entered the zone + (let ((min-point-x (fmin (-> self v1 x) (-> self v2 x))) + (min-point-y (fmin (-> self v1 y) (-> self v2 y))) + (min-point-z (fmin (-> self v1 z) (-> self v2 z))) + (max-point-x (fmax (-> self v1 x) (-> self v2 x))) + (max-point-y (fmax (-> self v1 y) (-> self v2 y))) + (max-point-z (fmax (-> self v1 z) (-> self v2 z))) + (pos (target-pos 0))) + (when (not (and (and (<= min-point-x (-> pos x)) (<= (-> pos x) max-point-x)) + (and (<= min-point-y (-> pos y)) (<= (-> pos y) max-point-y)) + (and (<= min-point-z (-> pos z)) (<= (-> pos z) max-point-z)))) + (when (nonzero? (-> self on-exit)) + ((-> self on-exit))) + (go-virtual waiting-for-player)))) + :code + (behavior () + (loop + (draw-zone self) + (suspend)))) + +(defbehavior objective-zone-init-by-other objective-zone ((start? symbol) (params objective-zone-init-params)) + (set! (-> self start?) start?) + (vector-copy! (-> self v1) (-> params v1)) + (vector-copy! (-> self v2) (-> params v2)) + (go-virtual waiting-for-player)) + +(defmethod draw-info ((this speedrun-practice-objective)) + (clear *temp-string*) + (clear *pc-encoded-temp-string*) + (pc-sr-mode-get-practice-entry-name (-> this index) *pc-encoded-temp-string*) + (format *temp-string* "Practicing: ~S~%" *pc-encoded-temp-string*) + (if (> (pc-sr-mode-get-practice-entry-history-attempts (-> this index)) 0) + (format *temp-string* + "History: ~D/~D (~,,2f%)~%" + (pc-sr-mode-get-practice-entry-history-success (-> this index)) + (pc-sr-mode-get-practice-entry-history-attempts (-> this index)) + (* 100.0 + (/ (the float (pc-sr-mode-get-practice-entry-history-success (-> this index))) + (the float (pc-sr-mode-get-practice-entry-history-attempts (-> this index)))))) + (format *temp-string* "History: --~%")) + (if (> (pc-sr-mode-get-practice-entry-session-attempts (-> this index)) 0) + (format *temp-string* + "Session: ~D/~D (~,,2f%)~%" + (pc-sr-mode-get-practice-entry-session-success (-> this index)) + (pc-sr-mode-get-practice-entry-session-attempts (-> this index)) + (* 100.0 + (/ (the float (pc-sr-mode-get-practice-entry-session-success (-> this index))) + (the float (pc-sr-mode-get-practice-entry-session-attempts (-> this index)))))) + (format *temp-string* "Session: --~%")) + (pc-sr-mode-get-practice-entry-avg-time (-> this index) *pc-encoded-temp-string*) + (format *temp-string* "Average Time: ~Ss~%" *pc-encoded-temp-string*) + (pc-sr-mode-get-practice-entry-fastest-time (-> this index) *pc-encoded-temp-string*) + (format *temp-string* "Fastest Time: ~Ss~%" *pc-encoded-temp-string*) + (format *temp-string* "\c91 L3: Reset~%") + (pc-encode-utf8-string *temp-string* *pc-encoded-temp-string*) + (with-dma-buffer-add-bucket ((buf (-> (current-frame) global-buf)) (bucket-id debug-no-zbuf2)) + ;; reset bucket settings prior to drawing - font won't do this for us, and + ;; draw-raw-image can sometimes mess them up. (intro sequence) + (dma-buffer-add-gs-set-flusha buf + (alpha-1 (new 'static 'gs-alpha :b #x1 :d #x1)) + (tex1-1 (new 'static 'gs-tex1 :mmag #x1 :mmin #x1))) + (let ((font-ctx (new 'stack 'font-context *font-default-matrix* 510 20 0.0 (font-color default) (font-flags right shadow kerning large)))) + (set! (-> font-ctx scale) 0.325) + (draw-string-adv *pc-encoded-temp-string* buf font-ctx)))) + +(defmethod reset! ((this speedrun-practice-objective)) + ;; record attempt if attempt was started + (when (-> *speedrun-info* waiting-to-record-practice-attempt?) + (pc-sr-mode-record-practice-entry-attempt! (-> this index) + #f + (&-> (the speedrun-timer (ppointer->process (-> *speedrun-manager* 0 timer))) recorded-time))) + ;; TODO - load checkpoint if not already in that checkpoint + ;; TODO - set features / cheats / completed-task / etc + ;; Update player position + (vector-copy! (-> *target* root trans) (-> this starting-position)) + (vector-copy! (-> *target* root quat) (-> this starting-rotation)) + ;; - get off jetboard and reset speed + (vector-copy! (-> *target* control transv) *zero-vector*) + (send-event *target* 'change-mode 'normal) + ;; Update camera position and rotation + (vector-copy! (-> *camera-combiner* trans) (-> this starting-camera-position)) + (matrix-identity! (-> *camera-combiner* inv-camera-rot)) + (matrix-copy! (-> *camera-combiner* inv-camera-rot) (-> this starting-camera-rotation)) + (process-spawn-function process + (lambda :behavior process () + (suspend) + (send-event *camera* 'teleport) + (deactivate self))) + (cam-master-activate-slave #f)) + +(define *speedrun-popup-menu-entries* + (new 'static + 'boxed-array + :type + popup-menu-entry + (new 'static + 'popup-menu-button + :label "Reset" + :on-confirm + (lambda () + (send-event (ppointer->process *speedrun-manager*) 'invoke (speedrun-menu-command reset)) + (send-event (-> *speedrun-manager* 0 popup-menu 0) 'close-menu))) + (new 'static + 'popup-menu-submenu + :label "Built-in Category Select" + :entries + (new 'static + 'boxed-array + :type + popup-menu-entry + (new 'static + 'popup-menu-flag + :label "Normal" + :on-confirm + (lambda () + (set-category! *speedrun-info* (speedrun-category newgame-normal))) + :is-toggled? + (lambda () + (= (-> *speedrun-info* category) (speedrun-category newgame-normal)))) + (new 'static + 'popup-menu-flag + :label "Hero Mode" + :on-confirm + (lambda () + (set-category! *speedrun-info* (speedrun-category newgame-heromode))) + :is-toggled? + (lambda () + (= (-> *speedrun-info* category) (speedrun-category newgame-heromode)))) + (new 'static + 'popup-menu-flag + :label "All Cheats Allowed" + :on-confirm + (lambda () + (set-category! *speedrun-info* (speedrun-category all-cheats-allowed))) + :is-toggled? + (lambda () + (= (-> *speedrun-info* category) (speedrun-category all-cheats-allowed)))))) + (new 'static + 'popup-menu-dynamic-submenu + :label "Custom Category Select" + :get-length + (lambda () + (pc-sr-mode-get-custom-category-amount)) + :get-entry-label + (lambda ((index int) (str-dest string)) + (pc-sr-mode-get-custom-category-name index str-dest)) + :on-entry-confirm + (lambda ((index int)) + ;; hydrate from cpp + (pc-sr-mode-init-custom-category-info! index (-> *speedrun-info* active-custom-category)) + (set-category! *speedrun-info* (speedrun-category custom))) + :entry-selected? + (lambda ((index int)) + (and (= (-> *speedrun-info* category) (speedrun-category custom)) + (= index (-> *speedrun-info* active-custom-category index))))) + ;; TODO - disabled until finalized + ; (new 'static + ; 'popup-menu-dynamic-submenu + ; :label "Practice select" + ; :entry-disabled? + ; (lambda () + ; (not (-> *speedrun-info* practicing?))) + ; :get-length + ; (lambda () + ; (pc-sr-mode-get-practice-entries-amount)) + ; :get-entry-label + ; (lambda ((index int) (str-dest string)) + ; (pc-sr-mode-get-practice-entry-name index str-dest)) + ; :on-entry-confirm + ; (lambda ((index int)) + ; ;; turn on timer + ; (set! (-> (the speedrun-timer (ppointer->process (-> *speedrun-manager* 0 timer))) draw?) #t) + ; ;; tear down old processes + ; (when (nonzero? (-> *speedrun-info* active-practice-objective start-zone)) + ; (deactivate (-> *speedrun-info* active-practice-objective start-zone 0))) + ; (when (nonzero? (-> *speedrun-info* active-practice-objective end-zone)) + ; (deactivate (-> *speedrun-info* active-practice-objective end-zone 0))) + ; ;; init from cpp + ; (pc-sr-mode-init-practice-info! index (-> *speedrun-info* active-practice-objective)) + ; ;; startup new processes + ; (set! (-> *speedrun-info* active-practice-objective start-zone) + ; (the (pointer objective-zone) + ; (process-spawn objective-zone #t (-> *speedrun-info* active-practice-objective start-zone-init-params)))) + ; (set! (-> *speedrun-info* active-practice-objective start-zone 0 on-exit) + ; (lambda () + ; (start! (the speedrun-timer (ppointer->process (-> *speedrun-manager* 0 timer)))) + ; (set! (-> *speedrun-info* waiting-to-record-practice-attempt?) #t) + ; (none))) + ; (set! (-> *speedrun-info* active-practice-objective start-zone 0 on-enter) + ; (lambda () + ; (when (and *target* (>= (-> *target* control ctrl-xz-vel) (meters 30.0))) + ; (vector-copy! (-> *target* control transv) *zero-vector*)) + ; (set! (-> *speedrun-info* waiting-to-record-practice-attempt?) #f) + ; (reset! (the speedrun-timer (ppointer->process (-> *speedrun-manager* 0 timer)))) + ; (none))) + ; (when (= 0 (-> *speedrun-info* active-practice-objective end-task)) + ; (set! (-> *speedrun-info* active-practice-objective end-zone) + ; (the (pointer objective-zone) + ; (process-spawn objective-zone #f (-> *speedrun-info* active-practice-objective end-zone-init-params)))) + ; (set! (-> *speedrun-info* active-practice-objective end-zone 0 on-enter) + ; (lambda () + ; (when (-> *speedrun-info* waiting-to-record-practice-attempt?) + ; (stop! (the speedrun-timer (ppointer->process (-> *speedrun-manager* 0 timer)))) + ; (if (pc-sr-mode-record-practice-entry-attempt! (-> *speedrun-info* active-practice-objective index) + ; #t + ; (&-> (the speedrun-timer (ppointer->process (-> *speedrun-manager* 0 timer))) recorded-time)) + ; (sound-play "skill-pickup") + ; (sound-play "menu-pick")) + ; (set! (-> *speedrun-info* waiting-to-record-practice-attempt?) #f)) + ; (none)))) + ; (set! (-> *speedrun-info* practicing?) #t) + ; (reset! (-> *speedrun-info* active-practice-objective)) + ; (set-master-mode 'game) + ; (send-event (ppointer->process (-> *speedrun-manager* 0 popup-menu)) 'close-menu)) + ; :entry-selected? + ; (lambda ((index int)) + ; (and (-> *speedrun-info* practicing?) (= index (-> *speedrun-info* active-practice-objective index))))) + ; (new 'static + ; 'popup-menu-button + ; :label "Stop practicing" + ; :entry-disabled? + ; (lambda () + ; (not (-> *speedrun-info* practicing?))) + ; :on-confirm + ; (lambda () + ; (when (-> *speedrun-info* practicing?) + ; (when (nonzero? (-> *speedrun-info* active-practice-objective start-zone)) + ; (deactivate (-> *speedrun-info* active-practice-objective start-zone 0))) + ; (when (nonzero? (-> *speedrun-info* active-practice-objective end-zone)) + ; (deactivate (-> *speedrun-info* active-practice-objective end-zone 0)))) + ; (set! (-> *speedrun-info* practicing?) #f) + ; (set! (-> (the speedrun-timer (ppointer->process (-> *speedrun-manager* 0 timer))) draw?) #f))) + (new 'static + 'popup-menu-submenu + :label "Tools" + :entries + (new 'static + 'boxed-array + :type + popup-menu-entry + (new 'static + 'popup-menu-submenu + :label "Create custom category" + :entries + (new 'static + 'boxed-array + :type + popup-menu-entry + (new 'static + 'popup-menu-dynamic-submenu + :label "Select secrets" + :get-length + (lambda () + 58) + :get-entry-label + (lambda ((index int) (str-dest string)) + (copy-string<-string str-dest (bitfield->string game-secrets index))) + :on-entry-confirm + (lambda ((index int)) + (logxor! (-> *speedrun-info* dump-custom-category secrets) (shl 1 index))) + :entry-selected? + (lambda ((index int)) + (logtest? (-> *speedrun-info* dump-custom-category secrets) (shl 1 index))) + :on-reset + (lambda () + (set! (-> *speedrun-info* dump-custom-category secrets) (game-secrets)))) + (new 'static + 'popup-menu-dynamic-submenu + :label "Select features" + :get-length + (lambda () + 58) + :get-entry-label + (lambda ((index int) (str-dest string)) + (copy-string<-string str-dest (bitfield->string game-feature index))) + :on-entry-confirm + (lambda ((index int)) + (logxor! (-> *speedrun-info* dump-custom-category features) (shl 1 index))) + :entry-selected? + (lambda ((index int)) + (logtest? (-> *speedrun-info* dump-custom-category features) (shl 1 index))) + :on-reset + (lambda () + (set! (-> *speedrun-info* dump-custom-category features) (game-feature)))) + (new 'static + 'popup-menu-dynamic-submenu + :label "Select vehicles" + :get-length + (lambda () + 8) + :get-entry-label + (lambda ((index int) (str-dest string)) + (copy-string<-string str-dest (bitfield->string game-vehicles index))) + :on-entry-confirm + (lambda ((index int)) + (logxor! (-> *speedrun-info* dump-custom-category vehicles) (shl 1 index))) + :entry-selected? + (lambda ((index int)) + (logtest? (-> *speedrun-info* dump-custom-category vehicles) (shl 1 index))) + :on-reset + (lambda () + (set! (-> *speedrun-info* dump-custom-category vehicles) (game-vehicles)))) + (new 'static + 'popup-menu-dynamic-submenu + :label "Forbid features" + :get-length + (lambda () + 58) + :get-entry-label + (lambda ((index int) (str-dest string)) + (copy-string<-string str-dest (bitfield->string game-feature index))) + :on-entry-confirm + (lambda ((index int)) + (logxor! (-> *speedrun-info* dump-custom-category forbidden-features) (shl 1 index))) + :entry-selected? + (lambda ((index int)) + (logtest? (-> *speedrun-info* dump-custom-category forbidden-features) (shl 1 index))) + :on-reset + (lambda () + (set! (-> *speedrun-info* dump-custom-category forbidden-features) (game-feature)))) + (new 'static + 'popup-menu-dynamic-submenu + :label "Select cheats" + :get-length + (lambda () + 17) + :get-entry-label + (lambda ((index int) (str-dest string)) + (copy-string<-string str-dest (bitfield->string pc-cheats index))) + :on-entry-confirm + (lambda ((index int)) + (logxor! (-> *speedrun-info* dump-custom-category pc-cheats) (shl 1 index))) + :entry-selected? + (lambda ((index int)) + (logtest? (-> *speedrun-info* dump-custom-category pc-cheats) (shl 1 index))) + :on-reset + (lambda () + (set! (-> *speedrun-info* dump-custom-category pc-cheats) (pc-cheats)))) + (new 'static + 'popup-menu-dynamic-submenu + :label "Select completed task" + :get-length + (lambda () + (dec (the int (game-task max)))) + :get-entry-label + (lambda ((index int) (str-dest string)) + (copy-string<-string str-dest (enum->string game-task index))) + :on-entry-confirm + (lambda ((index int)) + (set! (-> *speedrun-info* dump-custom-category completed-task) (the game-task index))) + :entry-selected? + (lambda ((index int)) + (= (-> *speedrun-info* dump-custom-category completed-task) (the game-task index))) + :on-reset + (lambda () + (set! (-> *speedrun-info* dump-custom-category completed-task) (game-task none)))) + (new 'static + 'popup-menu-button + :label "Save new category to file" + :on-confirm + (lambda () + (pc-sr-mode-dump-new-custom-category (-> *speedrun-info* dump-custom-category)))))))) + (new 'static + 'popup-menu-button + :label "Exit" + :on-confirm + (lambda () + (send-event (ppointer->process *speedrun-manager*) 'invoke (speedrun-menu-command exit)))))) + +(define *speedrun-manager* (the (pointer speedrun-manager) #f)) + +(defbehavior speedrun-manager-init-by-other speedrun-manager () + (process-mask-clear! (-> self mask) menu pause) + (set! *speedrun-manager* (the (pointer speedrun-manager) (process->ppointer self))) + (set! (-> self popup-menu) (process-spawn popup-menu "Speedrun Menu" *speedrun-popup-menu-entries* :to self)) + (set! (-> self timer) (process-spawn speedrun-timer :to self)) + (set! (-> self ignore-menu-toggle?) #f) + (set! (-> self opened-with-start?) #f) + (set! (-> *speedrun-info* practicing?) #f) + (set! (-> *speedrun-info* waiting-to-record-practice-attempt?) #f) + (go-virtual idle)) + +(defmethod update! ((this speedrun-info)) + "A per frame update for speedrunning related stuff" + ;; Ensure the speedrunner menu process is enabled or destroyed + (when (and (-> *pc-settings* speedrunner-mode?) (not *speedrun-manager*)) + (process-spawn speedrun-manager :from *pc-dead-pool* :to *pc-pool*)) + (when (and (not (-> *pc-settings* speedrunner-mode?)) *speedrun-manager*) + (deactivate (-> *speedrun-manager* 0))) + ;; do speedrunner mode things + (when (-> *pc-settings* speedrunner-mode?) + ;; Update auto-splitter struct + (update! *autosplit-info-jak3*) + ;; see if we should stop drawing the run info (when you finish arena training) + (when (and (!= (-> this category) (speedrun-category custom)) (task-complete? *game-info* (game-task arena-training-1))) + (false! (-> this display-run-info?))) + ;; Draw info to the screen + (when (and (not (-> *speedrun-info* practicing?)) (-> this display-run-info?)) + (draw-run-info this)) + ;; enforce settings even if they've changed them + (enforce-settings! this) + ;; draw objective info if practicing + (when (-> *speedrun-info* practicing?) + (draw-info (-> this active-practice-objective))))) + +(defmethod draw-run-info ((this speedrun-info)) + "Draw speedrun related settings in the bottom left corner" + (when (and (-> *pc-settings* speedrunner-mode?) (-> this display-run-info?)) + (clear *temp-string*) + (clear *pc-encoded-temp-string*) + (clear *pc-cpp-temp-string*) + (cond + ((= (-> this category) (speedrun-category custom)) + (pc-sr-mode-get-custom-category-name (-> this active-custom-category index) *pc-cpp-temp-string*) + (format *temp-string* + "Category: ~S~%Secrets: ~D~%Features: ~D~%Forbidden Features: ~D~%Cheats: ~D~%Version: ~S~%" + *pc-cpp-temp-string* + (-> this active-custom-category secrets) + (-> this active-custom-category features) + (-> this active-custom-category forbidden-features) + (-> this active-custom-category pc-cheats) + *pc-settings-built-sha*)) + (else + (format *temp-string* + "Category: ~S~%PC Cheats: ~S~%Frame Rate: ~D~%PS2 Actor Vis?: ~S~%Version: ~S~%" + (enum->string speedrun-category (-> this category)) + (if (= (-> *pc-settings* cheats) (pc-cheats)) "None" (pc-cheats->string (-> *pc-settings* cheats) *temp-string2*)) + (-> *pc-settings* target-fps) + (if (-> *pc-settings* ps2-actor-vis?) "true" "false") + *pc-settings-built-sha*))) + (pc-encode-utf8-string *temp-string* *pc-encoded-temp-string*) + (with-dma-buffer-add-bucket ((buf (-> (current-frame) global-buf)) (bucket-id debug-no-zbuf2)) + ;; reset bucket settings prior to drawing - font won't do this for us, and + ;; draw-raw-image can sometimes mess them up. (intro sequence) + (dma-buffer-add-gs-set-flusha buf + (alpha-1 (new 'static 'gs-alpha :b #x1 :d #x1)) + (tex1-1 (new 'static 'gs-tex1 :mmag #x1 :mmin #x1))) + (let ((font-ctx (new 'stack + 'font-context + *font-default-matrix* + 510 + (if (= (-> this category) (speedrun-category custom)) 355 365) + 0.0 + (font-color default) + (font-flags right shadow kerning large)))) + (set! (-> font-ctx scale) 0.325) + (draw-string-adv *pc-encoded-temp-string* buf font-ctx))))) + +;; Speedrun Menu + +(defmethod deactivate ((this speedrun-manager)) + (set! *speedrun-manager* (the (pointer speedrun-manager) #f)) + (call-parent-method this)) + +(defmethod draw-menu ((this speedrun-manager)) + ;; don't allow the menu to open during blackouts, apparently causes bugs + (if (< (-> *game-info* blackout-time) (current-time)) + ;; handle opening and closing the menu + (cond + ((!= (-> *pc-settings* speedrunner-mode-custom-bind) 0) + ;; the user has let go of the keybind completely or partially, allow the bind to trigger again + (when (and (-> this ignore-menu-toggle?) + (!= (cpad-hold 0) (logior (cpad-hold 0) (-> *pc-settings* speedrunner-mode-custom-bind)))) + (false! (-> this ignore-menu-toggle?))) + ;; bind handler + (when (and (not (-> this ignore-menu-toggle?)) + (= (cpad-hold 0) (logior (cpad-hold 0) (-> *pc-settings* speedrunner-mode-custom-bind)))) + (send-event (ppointer->process (-> this popup-menu)) 'open-menu) + (logclear! (cpad-hold 0) (-> *pc-settings* speedrunner-mode-custom-bind)) + (logclear! (cpad-pressed 0) (-> *pc-settings* speedrunner-mode-custom-bind)) + (true! (-> this ignore-menu-toggle?)))) + (else + (when (and (-> this ignore-menu-toggle?) + (or (not (cpad-hold? 0 l1)) (not (cpad-hold? 0 r1))) + (or (and (-> this opened-with-start?) (not (cpad-hold? 0 start))) + (and (not (-> this opened-with-start?)) (not (cpad-hold? 0 select))))) + (set! (-> this ignore-menu-toggle?) #f)) + (when (and (cpad-hold? 0 l1) + (cpad-hold? 0 r1) + (or (cpad-hold? 0 select) (cpad-hold? 0 start)) + (not (-> this ignore-menu-toggle?))) + (send-event (ppointer->process (-> this popup-menu)) 'open-menu) + (cpad-clear! 0 l1 r1) + (cond + ((cpad-hold? 0 select) (cpad-clear! 0 select) (false! (-> this opened-with-start?))) + ((cpad-hold? 0 start) (cpad-clear! 0 start) (true! (-> this opened-with-start?)))) + (true! (-> this ignore-menu-toggle?))))))) + +(defstate idle (speedrun-manager) + :virtual #t + :event + (behavior ((proc process) (argc int) (message symbol) (block event-message-block)) + (case message + (('start-run) (set-time! (-> *speedrun-info* run-started-at))) + (('invoke) + (case (-> block param 0) + (((speedrun-menu-command reset)) (start-run! *speedrun-info*)) + (((speedrun-menu-command exit)) (set-master-mode 'game) (send-event (ppointer->process (-> self popup-menu)) 'close-menu)) + (else (format 0 "nyi: invoke ~D~%" (-> block param 0))))))) + :trans + (behavior () + (draw-menu self)) + :code + (behavior () + (until #f + (when (and (-> *speedrun-info* practicing?) (cpad-pressed? 0 l3)) + (reset! (-> *speedrun-info* active-practice-objective))) + (when (and (-> *speedrun-info* display-run-info?) + (= (-> *speedrun-info* category) (speedrun-category custom)) + (time-elapsed? (-> *speedrun-info* run-started-at) (seconds 15))) + (false! (-> *speedrun-info* display-run-info?))) + (suspend)))) diff --git a/goal_src/jak3/pc/pckernel-impl.gc b/goal_src/jak3/pc/pckernel-impl.gc index 524ca7d6e9f..74f835b68c4 100644 --- a/goal_src/jak3/pc/pckernel-impl.gc +++ b/goal_src/jak3/pc/pckernel-impl.gc @@ -42,6 +42,10 @@ (weather-good) ) +(defun pc-cheats->string ((cheats pc-cheats) (buf object)) + (bit-enum->string pc-cheats cheats buf) + buf) + ;; pc enum for languages. this is the game's languages + custom ones. (defenum pc-language :type uint16 @@ -72,7 +76,7 @@ (custom 999) ;; temp ) -;; The Jak 2 version of the pc-settings object. +;; The Jak 3 version of the pc-settings object. (deftype pc-settings-jak3 (pc-settings) (;; cheats (cheats pc-cheats) diff --git a/goal_src/jak3/pc/pckernel.gc b/goal_src/jak3/pc/pckernel.gc index 0386e45d8cc..c684f355b81 100644 --- a/goal_src/jak3/pc/pckernel.gc +++ b/goal_src/jak3/pc/pckernel.gc @@ -303,9 +303,9 @@ (defmethod update-speedrun ((obj pc-settings-jak3)) "update speedrun module" ;; TODO - update to new with-profiler syntax - ;; (with-profiler "speedrun-update" - ;(update! *speedrun-info*) - ;;) + ; (with-profiler "speedrun-update" + (update! *speedrun-info*) + ; ) (none)) (defmethod update-video-hacks ((obj pc-settings-jak3))