diff --git a/common/util/FontUtils.cpp b/common/util/FontUtils.cpp index 934803921d..1c2d7c7a0b 100644 --- a/common/util/FontUtils.cpp +++ b/common/util/FontUtils.cpp @@ -1124,8 +1124,46 @@ static std::vector s_replace_info_jak2 = { {"~Y~-6Hº~Z~+10H", "°"}, // Color / Emphasis + {"~[~0L", ""}, {"~[~1L", ""}, - {"~[~32L", ""}}; + {"~[~2L", ""}, + {"~[~3L", ""}, + {"~[~4L", ""}, + {"~[~5L", ""}, + {"~[~6L", ""}, + {"~[~7L", ""}, + {"~[~8L", ""}, + {"~[~9L", ""}, + {"~[~10L", ""}, + {"~[~11L", ""}, + {"~[~12L", ""}, + {"~[~13L", ""}, + {"~[~14L", ""}, + {"~[~15L", ""}, + {"~[~16L", ""}, + {"~[~17L", ""}, + {"~[~18L", ""}, + {"~[~19L", ""}, + {"~[~20L", ""}, + {"~[~21L", ""}, + {"~[~22L", ""}, + {"~[~23L", ""}, + {"~[~24L", ""}, + {"~[~25L", ""}, + {"~[~26L", ""}, + {"~[~27L", ""}, + {"~[~28L", ""}, + {"~[~29L", ""}, + {"~[~30L", ""}, + {"~[~31L", ""}, + {"~[~32L", ""}, + {"~[~33L", ""}, + {"~[~34L", ""}, + {"~[~35L", ""}, + {"~[~36L", ""}, + {"~[~37L", ""}, + {"~[~38L", ""}, + {"~[~39L", ""}}; static std::vector s_encode_info_jak2 = { {"ˇ", {0x10}}, // caron diff --git a/game/kernel/jak1/kmachine.cpp b/game/kernel/jak1/kmachine.cpp index 68665b50a6..2961e3ece7 100644 --- a/game/kernel/jak1/kmachine.cpp +++ b/game/kernel/jak1/kmachine.cpp @@ -292,7 +292,7 @@ void InitIOP() { } } -AutoSplitterBlock gAutoSplitterBlock; +AutoSplitterBlock g_auto_splitter_block_jak1; /*! * Initialize GOAL Runtime. This is the main initialization which is called before entering @@ -357,7 +357,8 @@ int InitMachine() { } // TODO - better place to put this? - gAutoSplitterBlock.pointer_to_symbol = + // TODO - yes, see jak2's code! + g_auto_splitter_block_jak1.pointer_to_symbol = (u64)g_ee_main_mem + intern_from_c("*autosplit-info-jak1*")->value; lg::info("InitListenerConnect"); diff --git a/game/kernel/jak1/kmachine.h b/game/kernel/jak1/kmachine.h index 6e6c4152a5..0983875db7 100644 --- a/game/kernel/jak1/kmachine.h +++ b/game/kernel/jak1/kmachine.h @@ -45,5 +45,5 @@ struct AutoSplitterBlock { const char marker[20] = "UnLiStEdStRaTs_JaK1"; u64 pointer_to_symbol = 0; }; -extern AutoSplitterBlock gAutoSplitterBlock; +extern AutoSplitterBlock g_auto_splitter_block_jak1; } // namespace jak1 diff --git a/game/kernel/jak2/kmachine.cpp b/game/kernel/jak2/kmachine.cpp index a6cae3539e..780923d850 100644 --- a/game/kernel/jak2/kmachine.cpp +++ b/game/kernel/jak2/kmachine.cpp @@ -362,7 +362,8 @@ void InitIOP() { } printf("InitIOP OK\n"); } -AutoSplitterBlock gAutoSplitterBlock; + +AutoSplitterBlock g_auto_splitter_block_jak2; int InitMachine() { // heap_start = malloc(0x10); @@ -662,7 +663,7 @@ void pc_set_levels(u32 lev_list) { } void init_autosplit_struct() { - gAutoSplitterBlock.pointer_to_symbol = + g_auto_splitter_block_jak2.pointer_to_symbol = (u64)g_ee_main_mem + (u64)intern_from_c("*autosplit-info-jak2*")->value(); } @@ -741,6 +742,12 @@ inline u64 bool_to_symbol(const bool val) { // return bool_to_symbol(false); //} +void encode_utf8_string(u32 src_str_ptr, u32 str_dest_ptr) { + auto str = std::string(Ptr(src_str_ptr).c()->data()); + std::string converted = get_font_bank(GameTextVersion::JAK2)->convert_utf8_to_game(str); + strcpy(Ptr(str_dest_ptr).c()->data(), converted.c_str()); +} + void InitMachine_PCPort() { // PC Port added functions init_common_pc_port_functions( @@ -756,6 +763,7 @@ void InitMachine_PCPort() { make_function_symbol_from_c("__pc-set-levels", (void*)pc_set_levels); make_function_symbol_from_c("__pc-get-tex-remap", (void*)lookup_jak2_texture_dest_offset); make_function_symbol_from_c("pc-init-autosplitter-struct", (void*)init_autosplit_struct); + make_function_symbol_from_c("pc-encode-utf8-string", (void*)encode_utf8_string); // discord rich presence make_function_symbol_from_c("pc-discord-rpc-update", (void*)update_discord_rpc); diff --git a/game/kernel/jak2/kmachine.h b/game/kernel/jak2/kmachine.h index eb6821962a..0d39a55581 100644 --- a/game/kernel/jak2/kmachine.h +++ b/game/kernel/jak2/kmachine.h @@ -111,6 +111,6 @@ struct AutoSplitterBlock { u64 pointer_to_symbol = 0; }; -extern AutoSplitterBlock gAutoSplitterBlock; +extern AutoSplitterBlock g_auto_splitter_block_jak2; } // namespace jak2 diff --git a/goal_src/jak1/pc/features/speedruns.gc b/goal_src/jak1/pc/features/speedruns.gc index f72f691d0f..9d1de04c18 100644 --- a/goal_src/jak1/pc/features/speedruns.gc +++ b/goal_src/jak1/pc/features/speedruns.gc @@ -157,7 +157,7 @@ (initialize! *game-info* 'game (the-as game-save #f) "jungle-start") ) (((speedrun-category il-misty)) - ;; spawn at start of misty + ;; spawn at start of misty (initialize! *game-info* 'game (the-as game-save #f) "misty-start") ) (((speedrun-category il-firecanyon)) @@ -240,7 +240,7 @@ (set! (-> *setting-control* default auto-save) #t) (none) ) - + (defun setup-speedrun-post-blackout () (when (and (-> *speedrun-info* needs-post-blackout-setup?) (>= (-> *display* base-frame-counter) (-> *game-info* blackout-time))) (set! (-> *speedrun-info* needs-post-blackout-setup?) #f) diff --git a/goal_src/jak1/pc/pckernel-common.gc b/goal_src/jak1/pc/pckernel-common.gc index 68fc83e07b..ce9576a74c 100644 --- a/goal_src/jak1/pc/pckernel-common.gc +++ b/goal_src/jak1/pc/pckernel-common.gc @@ -295,8 +295,7 @@ (update-discord-rpc obj) ;; update auto-splitter info - (when (-> *pc-settings* speedrunner-mode?) - (update-speedrun obj)) + (update-speedrun obj) (when (not (-> obj use-vis?)) (update-video-hacks obj) diff --git a/goal_src/jak1/pc/pckernel.gc b/goal_src/jak1/pc/pckernel.gc index 4d93c5a764..31ce6f1fac 100644 --- a/goal_src/jak1/pc/pckernel.gc +++ b/goal_src/jak1/pc/pckernel.gc @@ -292,7 +292,7 @@ (defmethod update-speedrun pc-settings-jak1 ((obj pc-settings-jak1)) "update speedrun module" - (with-profiler "speedrun-update-jak1" + (with-profiler "speedrun-update" (speedrun-mode-update)) (none)) diff --git a/goal_src/jak2/dgos/game.gd b/goal_src/jak2/dgos/game.gd index 2014869786..8008b0c52d 100644 --- a/goal_src/jak2/dgos/game.gd +++ b/goal_src/jak2/dgos/game.gd @@ -238,8 +238,8 @@ "settings.o" "autosplit-h.o" ;; added "autosplit.o" ;; added + "popup-menu-h.o" ;; added "speedruns-h.o" ;; added - "speedruns.o" ;; added "mood-tables.o" "mood-tables2.o" "mood.o" @@ -327,6 +327,8 @@ "board-states.o" "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/jak2/engine/game/game-save.gc b/goal_src/jak2/engine/game/game-save.gc index c029a7785d..67c3908142 100644 --- a/goal_src/jak2/engine/game/game-save.gc +++ b/goal_src/jak2/engine/game/game-save.gc @@ -1778,7 +1778,9 @@ auto-save-post ) (suspend) ) - (activate-progress *dproc* 'icon-info) + ;; og:preserve-this Disable the auto-save prompt in speedrunner mode, we get it + (when (not (-> *pc-settings* speedrunner-mode?)) + (activate-progress *dproc* 'icon-info)) (while *progress-process* (suspend) ) diff --git a/goal_src/jak2/engine/game/main.gc b/goal_src/jak2/engine/game/main.gc index 7b2a203071..356f027a43 100644 --- a/goal_src/jak2/engine/game/main.gc +++ b/goal_src/jak2/engine/game/main.gc @@ -922,6 +922,8 @@ ;; draw and update menus (with-profiler 'menu-hook *profile-menu-hook-color* (*menu-hook*) + (when *speedrun-menu* + (draw! (-> *speedrun-menu* 0))) ) ;; load text files as needed from the menu update diff --git a/goal_src/jak2/engine/level/level-info.gc b/goal_src/jak2/engine/level/level-info.gc index b40b1799ef..bfc9737d74 100644 --- a/goal_src/jak2/engine/level/level-info.gc +++ b/goal_src/jak2/engine/level/level-info.gc @@ -3308,6 +3308,31 @@ ) :want-sound (new 'static 'array symbol 3 'ctywide1 'forexit1 'forexit2) ) + ;; og:preserve-this Added to enable fast-game-starts with hero mode + (new 'static 'continue-point + :name "game-start-hero" + :level 'prison + :flags (continue-flags game-start hero-mode) + :trans (new 'static 'vector :x 1942413.2 :y 34479.31 :z 275525.62 :w 1.0) + :quat (new 'static 'vector :y 0.4562 :w 0.8898) + :camera-trans (new 'static 'vector :x 1921090.8 :y 53425.766 :z 237949.75 :w 1.0) + :camera-rot (new 'static 'inline-array vector3s 3 + (new 'static 'vector3s :data (new 'static 'array float 3 0.869 0.0 -0.4947)) + (new 'static 'vector3s :data (new 'static 'array float 3 0.0663 0.9909 0.1165)) + (new 'static 'vector3s :data (new 'static 'array float 3 0.4903 -0.134 0.8611)) + ) + :on-goto #f + :vis-nick 'prison + :want (new 'static 'inline-array level-buffer-state 6 + (new 'static 'level-buffer-state :name 'prison :display? 'display :force-vis? #f :force-inside? #f) + (new 'static 'level-buffer-state :name 'forexita :display? #f :force-vis? #f :force-inside? #f) + (new 'static 'level-buffer-state :name #f :display? #f :force-vis? #f :force-inside? #f) + (new 'static 'level-buffer-state :name #f :display? #f :force-vis? #f :force-inside? #f) + (new 'static 'level-buffer-state :name #f :display? #f :force-vis? #f :force-inside? #f) + (new 'static 'level-buffer-state :name #f :display? #f :force-vis? #f :force-inside? #f) + ) + :want-sound (new 'static 'array symbol 3 'ctywide1 'forexit1 'forexit2) + ) (new 'static 'continue-point :name "prison-intro-start" :level 'prison @@ -15698,4 +15723,4 @@ (cons! *level-load-list* 'teststdd) (cons! *level-load-list* 'wasall) (cons! *level-load-list* 'stadocc) - ) \ No newline at end of file + ) diff --git a/goal_src/jak2/engine/target/target-death.gc b/goal_src/jak2/engine/target/target-death.gc index 2477d8cb33..da613eef5f 100644 --- a/goal_src/jak2/engine/target/target-death.gc +++ b/goal_src/jak2/engine/target/target-death.gc @@ -293,6 +293,14 @@ ) (cond ((logtest? (-> arg0 flags) (continue-flags game-start)) + ;; og:preserve-this Added to make fast-game-start work with hero-mode + (when (logtest? (-> arg0 flags) (continue-flags hero-mode)) + (logior! (-> self game secrets) (game-secrets hero-mode)) + (logior! (-> self game purchase-secrets) (game-secrets hero-mode)) + ;; TODO - there might be a better way to do this, the way hero-mode is set kinda feels like a side-effect + ;; `update-task-masks` gets called during the `intro-start-hero` via `setting-control::apply-settings` + ;; Which runs the following line if hero-mode is enabled + (set! (-> *game-info* features) (game-feature gun gun-yellow gun-red gun-blue gun-dark))) (case *kernel-boot-message* (('kiosk) (let ((s5-5 (ppointer->handle (auto-save-command 'restore 0 0 *default-pool* #f)))) diff --git a/goal_src/jak2/engine/ui/progress/progress.gc b/goal_src/jak2/engine/ui/progress/progress.gc index 8ace4296f1..e86e0b6f61 100644 --- a/goal_src/jak2/engine/ui/progress/progress.gc +++ b/goal_src/jak2/engine/ui/progress/progress.gc @@ -86,15 +86,22 @@ arg0 ) -(defun progress-intro-start ((arg0 symbol)) +(defun progress-intro-start ((hero-mode? symbol)) (set! (-> *game-info* mode) 'play) - (if arg0 - (initialize! *game-info* 'game (the-as game-save #f) "intro-start-hero") - (initialize! *game-info* 'game (the-as game-save #f) "intro-start") - ) + (cond + ;; Start a new game differently if speedrunning mode is active + ((= (-> *pc-settings* speedrunner-mode?) #t) + (if hero-mode? + (set-category! *speedrun-info* (speedrun-category newgame-heromode)) + (set-category! *speedrun-info* (speedrun-category newgame-normal))) + (start-run! *speedrun-info*)) + ;; start the game normally + (else + (if hero-mode? + (initialize! *game-info* 'game (the-as game-save #f) "intro-start-hero") + (initialize! *game-info* 'game (the-as game-save #f) "intro-start")))) (set-master-mode 'game) - 0 - ) + 0) (defmethod init-defaults progress ((obj progress)) "Initialize default menu settings." diff --git a/goal_src/jak2/kernel-defs.gc b/goal_src/jak2/kernel-defs.gc index 8274001bc7..2a7a9c629e 100644 --- a/goal_src/jak2/kernel-defs.gc +++ b/goal_src/jak2/kernel-defs.gc @@ -220,6 +220,8 @@ (define-extern pc-is-imgui-visible? (function symbol)) (define-extern pc-rand (function int)) +(define-extern pc-encode-utf8-string (function string string none)) + ;; Used to dump fixture data for the editor, re-enable end-to-end if you need it ;; (define-extern has-level-been-dumped-lights? (function string symbol)) ;; (define-extern has-level-been-dumped-regions? (function string symbol)) diff --git a/goal_src/jak2/kernel/gstring.gc b/goal_src/jak2/kernel/gstring.gc index bd1964d04e..90a8aa9b70 100644 --- a/goal_src/jak2/kernel/gstring.gc +++ b/goal_src/jak2/kernel/gstring.gc @@ -809,6 +809,8 @@ ) ) +(define *pc-encoded-temp-string* (new 'global 'string 2048 (the-as string #f))) + (kmemclose) diff --git a/goal_src/jak2/pc/features/autosplit-h.gc b/goal_src/jak2/pc/features/autosplit-h.gc index 88f5c993d0..7f9a3b407c 100644 --- a/goal_src/jak2/pc/features/autosplit-h.gc +++ b/goal_src/jak2/pc/features/autosplit-h.gc @@ -7,7 +7,7 @@ ;; ;; DO NOT change the order, appending to the end is safe! -(deftype autosplit-info-jak2 (structure) +(deftype autosplit-info (structure) (;; Version Info (version-major uint16) (version-minor uint16) @@ -127,8 +127,9 @@ (res-stadium-burning-bush-race-class1-r uint8) ;; TODO - orbs in level X ;; end marker just to make things look nice in a memory view - (end-marker uint8 4))) + (end-marker uint8 4)) + (:methods + (reset! (_type_) none) + (update! (_type_) none))) -(define-extern *autosplit-info-jak2* autosplit-info-jak2) -(define-extern update-autosplit-info-jak2 (function none)) -(define-extern update-autosplit-jak2-new-game (function none)) +(define-extern *autosplit-info-jak2* autosplit-info) diff --git a/goal_src/jak2/pc/features/autosplit.gc b/goal_src/jak2/pc/features/autosplit.gc index 60e9ceb385..02ae27c4ff 100644 --- a/goal_src/jak2/pc/features/autosplit.gc +++ b/goal_src/jak2/pc/features/autosplit.gc @@ -1,7 +1,7 @@ ;;-*-Lisp-*- (in-package goal) -(define *autosplit-info-jak2* (new 'static 'autosplit-info-jak2)) +(define *autosplit-info-jak2* (new 'static 'autosplit-info)) (pc-init-autosplitter-struct) ;; Setup Version (set! (-> *autosplit-info-jak2* version-major) 0) @@ -14,16 +14,14 @@ (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" - `(set! (-> *autosplit-info-jak2* ,field-name) (if (task-complete? *game-info* (game-task ,task-name)) 1 0))) + `(set! (-> this ,field-name) (if (task-complete? *game-info* (game-task ,task-name)) 1 0))) -(defun update-autosplit-info-jak2 () +(defmethod update! autosplit-info ((this autosplit-info)) ;; general statistics - (set! (-> *autosplit-info-jak2* num-orbs) (the int (-> *game-info* skill-total))) - (set! (-> *autosplit-info-jak2* num-skullgems) (the int (-> *game-info* gem-total))) - + (set! (-> this num-orbs) (the int (-> *game-info* skill-total))) + (set! (-> this num-skullgems) (the int (-> *game-info* gem-total))) ;; loading/cutscene related flags - (set! (-> *autosplit-info-jak2* in-cutscene?) (if (movie?) 1 0)) - + (set! (-> this in-cutscene?) (if (movie?) 1 0)) ;; need resolution flags (autosplit-flag-task-complete! res-fortress-escape fortress-escape) (autosplit-flag-task-complete! res-city-help-kid city-help-kid) @@ -129,10 +127,9 @@ (autosplit-flag-task-complete! res-stadium-burning-bush-race-class3-r stadium-burning-bush-race-class3-r) (autosplit-flag-task-complete! res-stadium-burning-bush-race-class2-r stadium-burning-bush-race-class2-r) (autosplit-flag-task-complete! res-stadium-burning-bush-race-class1-r stadium-burning-bush-race-class1-r) - (none)) -(defun update-autosplit-jak2-new-game () - (set! (-> *autosplit-info-jak2* game-hash) (pc-get-unix-timestamp)) +(defmethod reset! autosplit-info ((this autosplit-info)) + (set! (-> this game-hash) (pc-get-unix-timestamp)) (none)) diff --git a/goal_src/jak2/pc/features/speedruns-h.gc b/goal_src/jak2/pc/features/speedruns-h.gc index 0d1b523ae7..6cc284b34d 100644 --- a/goal_src/jak2/pc/features/speedruns-h.gc +++ b/goal_src/jak2/pc/features/speedruns-h.gc @@ -5,32 +5,41 @@ (defenum speedrun-category :type uint32 ;; Main Categories - (full-game 0) ;; Full game run (any category for now, can repurpose as Any% if we split them out later) - ;; (full-game-100 1) ;; 100% - ;; (full-game-allMission 2) ;; All Missions - - ;; Individual Levels - (il-placeholder1 100) ;; Geyser Rock IL - (il-placeholdere3 101) ;; Sandover Village IL - (il-placeholder3 102) ;; Sentinel Beach IL - - - ;; Extension Categories (TBD) + (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" ) +(deftype speedrun-info (structure) + ((category speedrun-category) + (display-run-info? symbol)) + (:methods + (set-category! (_type_ speedrun-category) none) + (start-run! (_type_) none) + (enforce-settings! (_type_) none) + (hide-run-info! (_type_) none) + (update! (_type_) none) + (draw-run-info! (_type_) none))) -(deftype speedrun-info-jak2 (structure) - ( - (category speedrun-category) - (needs-post-blackout-setup? symbol) - (should-display? symbol) - ) - ) +(define-extern *speedrun-info* speedrun-info) -(define-extern start-speedrun (function speedrun-category none)) -(define-extern is-speedrun-category? (function symbol symbol)) -(define-extern hide-speedrun-display (function none)) -(define-extern speedrun-start-full-game-run (function none)) -(define-extern setup-speedrun-post-blackout (function none)) -(define-extern speedrun-mode-update (function none)) -(define-extern speedrun-draw-settings (function none)) +(defenum speedrun-menu-command + :type uint32 + (reset 0) + (exit 1)) + +(deftype speedrun-menu (process-drawable) + ((popup-menu popup-menu) + (draw-menu? symbol) + (ignore-menu-toggle? symbol)) + (:methods + (draw! (_type_) none)) + (:states + idle)) + +(define-extern *speedrun-popup-menu* popup-menu) +(define-extern *speedrun-menu* (pointer speedrun-menu)) +(define-extern speedrun-menu-init (function none :behavior speedrun-menu)) diff --git a/goal_src/jak2/pc/features/speedruns.gc b/goal_src/jak2/pc/features/speedruns.gc index fcb859b58e..bacc566454 100644 --- a/goal_src/jak2/pc/features/speedruns.gc +++ b/goal_src/jak2/pc/features/speedruns.gc @@ -1,87 +1,160 @@ ;;-*-Lisp-*- (in-package goal) -(define *speedrun-info* (new 'static 'speedrun-info-jak2 :should-display? #t :needs-post-blackout-setup? #f)) +(define *speedrun-info* (new 'static 'speedrun-info)) -(defun is-speedrun-category? ((category symbol)) - (and (-> *pc-settings* speedrunner-mode?) - (name= category (enum->string speedrun-category (-> *speedrun-info* category))) - ) - ) +(defmethod set-category! speedrun-info ((this speedrun-info) (category speedrun-category)) + (set! (-> this category) category) + (none)) -(defun start-speedrun ((category speedrun-category)) +(defmethod start-run! speedrun-info ((this speedrun-info)) ;; randomize game id so the autosplitter knows to restart - (update-autosplit-jak2-new-game) - ;; turn on speedrun verification display and flip flag for setting up speedrun after initialize! - (set! (-> *speedrun-info* should-display?) #t) - ;; start new game with specified checkpoint, if any (otherwise we're resetting current category) - (when category - (set! (-> *speedrun-info* category) category)) - ;; TODO - ensure any required settings are enabled - (set-frame-rate! *pc-settings* 60 #t) - (none) - ) + (reset! *autosplit-info-jak2*) + ;; turn on speedrun verification display + (set! (-> this display-run-info?) #t) + ;; 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-as game-save #f) "game-start")) + (((speedrun-category newgame-heromode)) + (initialize! *game-info* 'game (the-as game-save #f) "game-start-hero"))) + ;; enable auto saving by default, no downside on PC + ;; this has to come after the `initialize!` call as it sets it to false! + (set! (-> *setting-control* user-default auto-save) #t) + (none)) -;; TODO - fix me -(define post-blackout? #f) +(defmethod enforce-settings! speedrun-info ((this speedrun-info)) + (set! (-> *pc-settings* ps2-actor-vis?) #t) ;; force PS2 actor visibility + (set-frame-rate! *pc-settings* 60 #t) ;; force FPS to `60` + (set! (-> *pc-settings* cheats) (the-as pc-cheats #x0)) ;; disable any active cheats + (none)) -(defun setup-speedrun-post-blackout () - (when post-blackout? - (set! (-> *speedrun-info* needs-post-blackout-setup?) #f) - (case (-> *speedrun-info* category) - (((speedrun-category full-game)) - ;; setup a full-game run here - (none) - ) - (else - ;;speedrun-category was not recognized, print an error to the screen and maybe play a sound. - ) - ) - ;; reset speedrun display (gets hidden on first collectable pickup) - (set! (-> *speedrun-info* should-display?) #t) - ;; reset actors to ensure any above changes take effect - (reset-actors 'life) - ;; autosave any changes back to file - (auto-save-command 'auto-save 0 0 *default-pool* #f) - ) - (none) - ) +(defmethod hide-run-info! speedrun-info ((this speedrun-info)) + (set! (-> this display-run-info?) #f) + (none)) -(defun hide-speedrun-display () - (set! (-> *speedrun-info* should-display?) #f) - (none) - ) - -(defun speedrun-mode-update () +(defmethod update! speedrun-info ((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-menu*)) + (process-spawn speedrun-menu :init speedrun-menu-init #f :to *entity-pool*)) + (when (and (not (-> *pc-settings* speedrunner-mode?)) + *speedrun-menu*) + (deactivate (-> *speedrun-menu* 0))) + ;; do speedrunner mode things (when (-> *pc-settings* speedrunner-mode?) - ;; Update auto-splitter - (update-autosplit-info-jak2) - ;; Draw info to the screen - (speedrun-draw-settings) - ;;Disable any active cheats - ;; (set! (-> *pc-settings* cheats) (the-as pc-cheats #x0)) - ;; Run after-blackout speedrun setup (if needed) - (setup-speedrun-post-blackout) - ) + ;; Update auto-splitter struct + (update! *autosplit-info-jak2*) + ;; see if we should stop drawing the run info (when you escape the fortress!) + (when (task-complete? *game-info* (game-task fortress-escape)) + (set! (-> this display-run-info?) #f)) + (when (-> this display-run-info?) + ;; Draw info to the screen + (draw-run-info! this)) + ;; enforce settings even if they've changed them + (enforce-settings! this)) (none)) -(defun speedrun-start-full-game-run () - ;; start a full game speedrun, save file is already selected so don't default to first slot - (start-speedrun (speedrun-category full-game)) - (none)) - -(defun speedrun-draw-settings () +(defmethod draw-run-info! speedrun-info ((this speedrun-info)) "Draw speedrun related settings in the bottom left corner" (when (and (-> *pc-settings* speedrunner-mode?) - (not (paused?)) - (-> *speedrun-info* should-display?)) - (with-dma-buffer-add-bucket ((buf (-> (current-frame) global-buf)) - (bucket-id debug-no-zbuf1)) - (draw-string-xy (string-format "OpenGOAL Version: ~S ~%Speedrun mode: ~A ~%Category: ~S ~%Is fortress escaped: ~D" - *pc-settings-built-sha* - (-> *pc-settings* speedrunner-mode?) - (enum->string speedrun-category (-> *speedrun-info* category)) - (-> *autosplit-info-jak2* res-fortress-escape)) - buf 0 (- 220 (* 8 4)) (font-color progress-old-yellow) (font-flags shadow kerning)))) + (-> this display-run-info?)) + (clear *temp-string*) + (format *temp-string* "PC Cheats: ~D~%Frame Rate: ~D~%PS2 Actor Vis?: ~S~%Version: ~S~%" + (the-as int (-> *pc-settings* cheats)) + (-> *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-zbuf1)) + (let ((font-ctx (new 'stack 'font-context *font-default-matrix* 510 375 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)))) + (none)) + +;; Speedrun Menu + +(define *speedrun-popup-menu* + (new 'static 'popup-menu + :entries (new 'static 'boxed-array :type popup-menu-entry + (new 'static 'popup-menu-button :label "Reset" :on-press (lambda () (send-event (ppointer->process *speedrun-menu*) 'invoke (speedrun-menu-command reset)))) + (new 'static 'popup-menu-button :label "Exit" :on-press (lambda () (send-event (ppointer->process *speedrun-menu*) 'invoke (speedrun-menu-command exit)))) + ) + ) + ) + +(define *speedrun-menu* (the-as (pointer speedrun-menu) #f)) + +(defbehavior speedrun-menu-init speedrun-menu () + (set! *speedrun-menu* (the-as (pointer speedrun-menu) (process->ppointer self))) + (set! (-> *speedrun-menu* 0 popup-menu) *speedrun-popup-menu*) + (set! (-> *speedrun-menu* 0 draw-menu?) #f) + (set! (-> *speedrun-menu* 0 ignore-menu-toggle?) #f) + (go idle) + (none)) + +(defmethod deactivate speedrun-menu ((this speedrun-menu)) + (set! *speedrun-menu* (the-as (pointer speedrun-menu) #f)) + ((method-of-type process-drawable deactivate) this) + (none)) + +(defstate idle (speedrun-menu) + :event (behavior ((proc process) (arg1 int) (event-type symbol) (event event-message-block)) + (case event-type + (('invoke) + (case (-> event param 0) + (((speedrun-menu-command reset)) + (start-run! *speedrun-info*)) + (((speedrun-menu-command exit)) + (set-master-mode 'game) + (set! (-> self draw-menu?) #f)) + (else + (format 0 "nyi: invoke ~D~%" (-> event param 0)))))) + (the-as object 0)) + :trans (behavior () + (none)) + :code (behavior () + (until #f (suspend) ) + (none)) + :post (behavior () + (none))) + +(defmethod draw! speedrun-menu ((this speedrun-menu)) + ;; handle opening and closing the menu + ;; allow the menu to be toggled again once one of the currents is released + (when (and (-> this ignore-menu-toggle?) + (or (not (cpad-hold? 0 l1)) (not (cpad-hold? 0 r1)) (not (cpad-hold? 0 select)))) + (set! (-> this ignore-menu-toggle?) #f)) + (when (and (cpad-hold? 0 l1) (cpad-hold? 0 r1) (cpad-hold? 0 select) + (not (-> this ignore-menu-toggle?))) + (cond + ((= *master-mode* 'game) + (set-master-mode 'menu) + (set! (-> this draw-menu?) #t)) + ((= *master-mode* 'menu) + (set-master-mode 'game) + (set! (-> this draw-menu?) #f))) + (logclear! (-> *cpad-list* cpads 0 button0-abs 0) (pad-buttons l1)) + (logclear! (-> *cpad-list* cpads 0 button0-rel 0) (pad-buttons l1)) + (logclear! (-> *cpad-list* cpads 0 button0-abs 0) (pad-buttons r1)) + (logclear! (-> *cpad-list* cpads 0 button0-rel 0) (pad-buttons r1)) + (logclear! (-> *cpad-list* cpads 0 button0-abs 0) (pad-buttons select)) + (logclear! (-> *cpad-list* cpads 0 button0-rel 0) (pad-buttons select)) + (set! (-> this ignore-menu-toggle?) #t)) + (when (-> this draw-menu?) + ;; handle any inputs for within the menu + (cond + ((cpad-pressed? 0 triangle select) + (set! (-> this draw-menu?) #f)) + ((cpad-pressed? 0 up) + (move-up! (-> this popup-menu))) + ((cpad-pressed? 0 down) + (move-down! (-> this popup-menu))) + ((cpad-pressed? 0 x) + (press! (-> this popup-menu)))) + ;; draw it + (draw! (-> this popup-menu))) (none)) diff --git a/goal_src/jak2/pc/pckernel.gc b/goal_src/jak2/pc/pckernel.gc index b3f9dce905..af88272ab2 100644 --- a/goal_src/jak2/pc/pckernel.gc +++ b/goal_src/jak2/pc/pckernel.gc @@ -161,15 +161,17 @@ (set! (-> info time-of-day) (-> *time-of-day-context* time)) (set! (-> info percent-complete) (calculate-percentage *game-info*)) (set! (-> info focus-status) (if *target* (-> *target* focus-status) 0)) - ;; TODO - wrapping in `with-profiler` causes an error, fix it + ;; TODO - update to new with-profiler syntax (pc-discord-rpc-update info) ) (none)) (defmethod update-speedrun pc-settings-jak2 ((obj pc-settings-jak2)) "update speedrun module" - (when (-> *pc-settings* speedrunner-mode?) - (speedrun-mode-update)) + ;; TODO - update to new with-profiler syntax + ;; (with-profiler "speedrun-update" + (update! *speedrun-info*) + ;;) (none)) (defmethod update-video-hacks pc-settings-jak2 ((obj pc-settings-jak2)) diff --git a/goal_src/jak2/pc/util/popup-menu-h.gc b/goal_src/jak2/pc/util/popup-menu-h.gc new file mode 100644 index 0000000000..23b6d065c8 --- /dev/null +++ b/goal_src/jak2/pc/util/popup-menu-h.gc @@ -0,0 +1,25 @@ +;;-*-Lisp-*- +(in-package goal) + +;; A debug-menu style popup menu, a lightweight way to make a context menu that doesn't involve the progress code +;; and isn't debug-only +;; +;; Currently only supports a single 1-level menu of buttons, add more features as required + +(deftype popup-menu-entry (basic) + ((label string) + (on-press (function none))) + (:methods + (draw! (_type_ font-context dma-buffer) none))) + +(deftype popup-menu-button (popup-menu-entry) ()) + +(deftype popup-menu (basic) + ((entries (array popup-menu-entry)) + (curr-entry-index int32)) + (:methods + (draw! (_type_) none) + (move-up! (_type_) none) + (move-down! (_type_) none) + (press! (_type_) none) + (get-widest-label (_type_ font-context) float))) diff --git a/goal_src/jak2/pc/util/popup-menu.gc b/goal_src/jak2/pc/util/popup-menu.gc new file mode 100644 index 0000000000..19bcd54dc7 --- /dev/null +++ b/goal_src/jak2/pc/util/popup-menu.gc @@ -0,0 +1,61 @@ +;;-*-Lisp-*- +(in-package goal) + +(defmethod draw! popup-menu-entry ((this popup-menu-entry) (font-ctx font-context) (dma-buf dma-buffer)) + (none)) + +(defmethod draw! popup-menu-button ((this popup-menu-button) (font-ctx font-context) (dma-buf dma-buffer)) + (let ((old-x (-> font-ctx origin x)) + (old-y (-> font-ctx origin y))) + (pc-encode-utf8-string (-> this label) *pc-encoded-temp-string*) + (draw-string-adv *pc-encoded-temp-string* dma-buf font-ctx) + (set! (-> font-ctx origin x) old-x) + (set! (-> font-ctx origin y) old-y)) + (none)) + +(defmethod draw! popup-menu ((this popup-menu)) + (let ((font-ctx (new 'debug 'font-context *font-default-matrix* 0 0 0.0 (font-color default) (font-flags shadow kerning large)))) + (set! (-> font-ctx scale) 0.35) + (set! (-> font-ctx origin x) 15.0) + (set! (-> font-ctx origin y) 75.0) + (with-dma-buffer-add-bucket ((buf (-> (current-frame) global-buf)) (bucket-id debug-no-zbuf1)) + ;; background border + (draw-sprite2d-xy buf 6 64 + (+ 17 (the int (get-widest-label this font-ctx))) ;; width + (+ 17 (* 15 (-> this entries length))) ;; height + (new 'static 'rgba :r 255 :g 255 :b 255 :a 75)) + ;; background + (draw-sprite2d-xy buf 7 65 + (+ 15 (the int (get-widest-label this font-ctx))) ;; width + (+ 15 (* 15 (-> this entries length))) ;; height + (new 'static 'rgba :r 0 :g 0 :b 0 :a 255)) + ;; menu contents + (dotimes (i (-> this entries length)) + (if (= i (-> this curr-entry-index)) + (set! (-> font-ctx color) (font-color cyan)) + (set! (-> font-ctx color) (font-color default))) + (draw! (-> this entries i) font-ctx buf) + (set! (-> font-ctx origin y) (+ 15.0 (-> font-ctx origin y))))) + ) + (none)) + +(defmethod move-up! popup-menu ((this popup-menu)) + (set! (-> this curr-entry-index) (max 0 (dec (-> this curr-entry-index)))) + (none)) + +(defmethod move-down! popup-menu ((this popup-menu)) + (set! (-> this curr-entry-index) (min (dec (-> this entries length)) (inc (-> this curr-entry-index)))) + (none)) + +(defmethod press! popup-menu ((this popup-menu)) + (let ((entry (-> this entries (-> this curr-entry-index)))) + ((-> entry on-press))) + (none)) + +(defmethod get-widest-label popup-menu ((this popup-menu) (font-ctx font-context)) + (let ((max-len 0.0)) + (dotimes (i (-> this entries length)) + (let ((label-len (-> (get-string-length (-> this entries i label) font-ctx) length))) + (when (> label-len max-len) + (set! max-len label-len)))) + max-len))