Files
jak-project/goal_src/jak2/pc/progress/progress-pc.gc
Tyler Wilding af6de539b5 jak1/jak2: Persist sound settings, play-hints, subtitles and vibration settings in pc-settings instead of the memory card file (#3612)
In the original game, they had no choice but to use the memory card file
as their method of persisting settings. We are not limited by such
things.

It's inconvenient to have to load your save-file when launching the game
to initialize these settings to your liking, it's also confusing
behaviour to even some players that have played the game heavily for
over a decade. We can do better by globally saving these settings to the
`pc-settings` file instead.

Originally I only migrated the volume settings, then i figured it would
be nice to also have play-hints and subtitles settings persisted. More
could debatably be moved (language is a big one...) but these were the
low hanging fruit.

I also reduced the default volumes as that is something else that has
come up a few times.
2024-07-31 00:01:18 -04:00

1223 lines
48 KiB
Common Lisp

;;-*-Lisp-*-
(in-package goal)
#|
Additional PC port specific file for overriding/expanding the progress menu
This gives us more freedom to write code how we want.
|#
(deftype progress-global-state-pc (basic)
((decoration-draw-time uint64)
(aspect-ratio-ratio-index int8)
(frame-rate-choice-index int8)
(frame-rate-disclaimer-time time-frame)
(music-player-track music-player-track-info)
(music-player-flava int8)
(music-player-selected symbol)
)
)
(define *progress-state-pc* (new 'static 'progress-global-state-pc :music-player-track #f))
(defmacro when-not-drawn-decoration (&rest body)
`(when (> (-> *display* real-clock integral-frame-counter) (-> *progress-state-pc* decoration-draw-time))
,@body
(set! (-> *progress-state-pc* decoration-draw-time) (-> *display* real-clock integral-frame-counter))))
(defun set-progress-frame-rate-index ((idx int))
"Set the frame rate option index accordingly."
(when idx
(set! (-> *progress-state-pc* frame-rate-choice-index) idx)
(return 0)
)
;; default to 60 fps
(set! (-> *progress-state-pc* frame-rate-choice-index) 0)
;; lookup entry that matches
(dotimes (i (-> *frame-rate-options* length))
(if (= (-> *pc-settings* target-fps) (-> *frame-rate-options* i))
(set! (-> *progress-state-pc* frame-rate-choice-index) i))
)
0)
(defmethod init-defaults ((obj progress))
"Initialize default menu settings."
(set! (-> *progress-state* aspect-ratio-choice) (get-aspect-ratio))
(set! (-> *progress-state* video-mode-choice) (get-video-mode))
(set! (-> *progress-state* yes-no-choice) #f)
(set! (-> *progress-state* on-off-choice) #f)
(set! (-> *progress-state* color-flash-counter) 0)
(set! (-> *progress-state* game-options-item-selected) 0)
(set! (-> *progress-state* game-options-item-picked) #f)
(set! (-> *progress-state* game-options-vibrations) (-> *pc-settings* memcard-vibration?))
(set! (-> *progress-state* game-options-subtitles) (-> *pc-settings* memcard-subtitles?))
(set! (-> *progress-state* game-options-language-index)
(the-as int (-> *setting-control* user-default language))
)
(set! (-> *progress-state* game-options-subtitle-language-index)
(the-as int (-> *setting-control* user-default subtitle-language))
)
(set! (-> *progress-state* graphic-options-item-selected) 0)
(set! (-> *progress-state* graphic-options-item-picked) #f)
(set! (-> *progress-state* graphic-options-aspect-ratio) (get-aspect-ratio))
(set! (-> *progress-state* graphic-options-progressive-scan)
(-> *setting-control* user-default use-progressive-scan)
)
(set! (-> *progress-state* qr-options-item-selected) 0)
(set! (-> *progress-state* qr-options-item-picked) #f)
(set! (-> *progress-state* total-num-tasks) 0)
(set! (-> *progress-state* secrets-unlocked) #f)
(set! (-> *progress-state* clear-screen) #f)
(set! (-> obj sliding) 0.0)
(set! (-> obj sliding-height) 0.0)
(set! (-> obj sliding-off) 1.0)
(set! (-> obj scanlines-alpha) 0.0)
(set! (-> (the-as menu-on-off-game-vibrations-option (-> *game-options* options 0)) value-to-modify)
(&-> *pc-settings* memcard-vibration?)
)
(set! (-> (the-as menu-on-off-game-subtitles-option (-> *game-options* options 1)) value-to-modify)
(&-> *setting-control* user-default subtitle)
)
(set! (-> (the-as menu-language-option (-> *game-options* options 2)) language-selection)
(-> *setting-control* user-current language)
)
(set! (-> (the-as menu-language-option (-> *game-options* options 2)) language-direction) #t)
(set! (-> (the-as menu-language-option (-> *game-options* options 2)) language-transition) #f)
(set! (-> (the-as menu-language-option (-> *game-options* options 2)) language-x-offset) 0)
(set! (-> (the-as menu-on-off-option (-> *game-options-japan* options 0)) value-to-modify)
(&-> *pc-settings* memcard-vibration?)
)
(set! (-> (the-as menu-on-off-option (-> *game-options-demo* options 0)) value-to-modify)
(&-> *pc-settings* memcard-vibration?)
)
(set! (-> (the-as menu-on-off-option (-> *graphic-options* options 2)) value-to-modify)
(&-> *setting-control* user-default use-progressive-scan)
)
(set! (-> (the-as menu-on-off-option (-> *graphic-title-options-pal* options 2)) value-to-modify)
(&-> *setting-control* user-default use-progressive-scan)
)
;; (set! (-> (the-as menu-slider-option (-> *sound-options* options 0)) value-to-modify)
;; (&-> *pc-settings* memcard-volume-sfx)
;; )
;; (set! (-> (the-as menu-slider-option (-> *sound-options* options 1)) value-to-modify)
;; (&-> *pc-settings* memcard-volume-music)
;; )
;; (set! (-> (the-as menu-slider-option (-> *sound-options* options 2)) value-to-modify)
;; (&-> *pc-settings* memcard-volume-dialog)
;; )
(set! (-> (the-as menu-missions-option (-> *missions-options* options 0)) task-line-index) 0)
(set-setting-by-param *setting-control* 'extra-bank '((force2 menu1)) 0 0)
;; PC STATE BEGIN
;; --------------
(init! *progress-pc-generic-store*)
(set! (-> *progress-state-pc* music-player-track) #f)
)
(defun progress-pc-fetch-external-times ((highscore-index int))
(when (-> *pc-settings* speedrunner-mode?)
(case highscore-index
((0) (pc-fetch-external-highscores "scatter"))
((1) (pc-fetch-external-highscores "blaster"))
((2) (pc-fetch-external-highscores "vulcan"))
((3) (pc-fetch-external-highscores "peacemaker"))
((4) (pc-fetch-external-highscores "jetboard"))
((5) (pc-fetch-external-race-times "class3"))
((6) (pc-fetch-external-race-times "class2"))
((7) (pc-fetch-external-race-times "class1"))
((8) (pc-fetch-external-race-times "port"))
((9) (pc-fetch-external-race-times "erol"))
((10) (pc-fetch-external-race-times "class3rev"))
((11) (pc-fetch-external-race-times "class2rev"))
((12) (pc-fetch-external-race-times "class1rev"))
((13) (pc-fetch-external-highscores "onin"))
((14) (pc-fetch-external-highscores "mash"))
((15) (pc-fetch-external-speedrun-times "any"))
((16) (pc-fetch-external-speedrun-times "anyhoverless"))
((17) (pc-fetch-external-speedrun-times "allmissions"))
((18) (pc-fetch-external-speedrun-times "100"))
((19) (pc-fetch-external-speedrun-times "anyorbs"))
((20) (pc-fetch-external-speedrun-times "anyhero")))))
(defmethod set-menu-options ((obj progress) (arg0 symbol))
"Set the menu options for the menu state specified by arg0."
(set! (-> obj current-options) #f)
(case arg0
(('go-away)
(go (method-of-object obj go-away))
)
(('main)
(set! (-> obj current-options) (cond
(*cheat-mode*
*main-options*
)
((= *kernel-boot-message* 'kiosk)
*main-kiosk-options*
)
((demo?)
*main-demo-options*
)
(else
*main-options*
)
)
)
)
(('game-options)
(set! (-> obj current-options)
(if (demo?) *game-options-demo* *game-options-pc*))
)
(('game-options-title)
(set! (-> obj current-options)
(if (demo?) *game-options-demo* *game-options-title-pc*))
)
(('graphic-options)
(set! (-> obj current-options) *graphic-options-pc*)
)
(('sound-options)
(set! (-> obj current-options) *sound-options-pc*)
)
(('select-load 'select-save)
(set! (-> obj current-options) *load-save-options*)
)
(('select-save-title)
(set! (-> obj current-options) *save-options-title*)
)
(('select-save-title-hero)
(logior! (-> *game-info* purchase-secrets) (game-secrets hero-mode))
(logior! (-> *game-info* secrets) (game-secrets hero-mode))
(set! (-> obj current-options) *save-options-title*)
)
(('loading 'saving 'creating 'formatting)
(set! (-> obj current-options) *loading-options*)
)
(('unformatted-card 'insufficient-space 'no-memory-card)
(set! (-> obj current-options) *insufficient-space-options*)
)
(('secrets-insufficient-space 'secrets-no-memory-card)
(set! (-> obj current-options) *secrets-insufficient-space-options*)
)
(('insert-card)
(set! (-> obj current-options) *insert-card-options*)
)
(('error-loading 'error-saving 'error-formatting 'error-creating)
(set! (-> obj current-options) *error-loading-options*)
)
(('error-auto-saving)
(set! (-> obj current-options) *error-auto-saving-options*)
)
(('card-removed)
(set! (-> obj current-options) *card-removed-options*)
)
(('error-disc-removed)
(set! (-> obj current-options) *error-disc-removed-options*)
)
(('error-reading)
(set! (-> obj current-options) *error-reading-options*)
)
(('icon-info)
(set! (-> obj current-options) *icon-info-options*)
)
(('format-card)
(set! (-> obj current-options) *format-card-options*)
)
(('already-exists)
(set! (-> obj current-options) *already-exists-options*)
)
(('create-game)
(set! (-> obj current-options) *create-game-options*)
)
(('video-mode-warning)
(set! (-> obj current-options) *video-mode-warning-options*)
)
(('video-mode-ok)
(set! (-> obj current-options) *video-mode-ok-options*)
)
(('progressive-mode-warning)
(set! (-> obj current-options) *progressive-mode-warning-options*)
)
(('progressive-mode-ok)
(set! (-> obj current-options) *progressive-mode-ok-options*)
)
(('quit)
(set! (-> obj current-options) *quit-options*)
)
(('title)
(set! (-> obj current-options) *title-pc*)
)
(('title-options)
(set! (-> obj current-options) *options-pc*)
)
(('select-start 'select-pre-start 'select-kiosk-start)
(set! (-> obj current-options) *select-start-options*)
(set! (-> (the-as menu-select-start-option (-> (the-as (array menu-option) (-> *select-start-options* options)) 0))
task-index
)
0
)
0
)
(('select-scene)
(set! (-> obj current-options) *select-scene-options*)
(set! (-> (the-as menu-select-scene-option (-> (the-as (array menu-option) (-> *select-scene-options* options)) 0))
task-index
)
0
)
0
)
(('select-scene-special)
(set! (-> *progress-state* starting-state) 'title)
(set! (-> obj state-pos) 0)
(let ((v1-67 (-> obj state-pos)))
(set! (-> obj state-stack v1-67) 'title)
(set! (-> obj option-index-stack v1-67) 3)
(let ((v1-68 (+ v1-67 1)))
(set! (-> obj state-stack v1-68) 'unlocked-secrets)
(set! (-> obj option-index-stack v1-68) (-> obj option-index))
(set! (-> obj state-pos) (+ v1-68 1))
)
)
(dotimes (s5-1 (-> obj state-pos))
(format
#t
"select-scene-special: ~S ~D~%"
(symbol->string (-> obj state-stack s5-1))
(-> obj option-index-stack s5-1)
)
)
(set! (-> obj current-options) *select-scene-options*)
)
(('bigmap)
(set! (-> obj current-options) *bigmap-options*)
)
(('missions)
(set! (-> *progress-state* missions-total-spacing) 0.0)
(set! (-> (the-as menu-missions-option (-> (the-as (array menu-option) (-> *missions-options* options)) 0))
task-line-index
)
0
)
(set! (-> obj current-options) *missions-options*)
)
(('highscores)
(set! (-> (the-as menu-highscores-option (-> (the-as (array menu-option) (-> *highscores-options* options)) 0))
page-index
)
0
)
(set! (-> (the-as menu-highscores-option (-> (the-as (array menu-option) (-> *highscores-options* options)) 0))
prev-page-index
)
0
)
(set! (-> obj current-options) *highscores-options*)
;; ensure external data is fetched if applicable
(progress-pc-fetch-external-times 0)
(set! (-> *progress-pc-generic-store* current-highscore-page-index) 0)
)
(('secret)
(set! (-> obj secret-buying) #f)
(set! (-> obj current-options) *secret-options*)
)
(('quit-restart)
(set! (-> obj current-options) *quit-restart-options*)
)
(('unlocked-secrets)
(set! (-> obj current-options) *unlocked-secrets-pc*)
)
(('aspect-ratio-custom)
(set! (-> *progress-state-pc* aspect-ratio-ratio-index) 0)
(set! (-> obj current-options) *aspect-ratio-custom-options*)
)
(('music-player)
(set! (-> *progress-state-pc* music-player-selected) #f)
(set! (-> (the-as menu-music-player-option (-> *music-player-options* options 0)) excitement) 0)
(set! (-> obj current-options) *music-player-options*)
(when (aif (level-get *level* 'title) (= (-> it status) 'active))
(remove-setting *setting-control* (handle->process (-> *game-info* controller 0)) 'music-volume)
)
)
(('resolutions)
(set! (-> (the-as menu-resolution-option (-> *resolutions-options* options 0)) selected-index) -1)
(set! (-> obj current-options) *resolutions-options*)
)
(('fps-disclaimer)
(set-time! (-> *progress-state-pc* frame-rate-disclaimer-time))
(set! (-> obj current-options) *frame-rate-disclaimer-options*)
)
(('generic-menu)
;; a single condition to handle all generic menu links
(let ((curr-history-entry (-> *progress-pc-generic-store* history-stack
(-> *progress-pc-generic-store* history-stack-index))))
;; call the on-load function is appropriate, this can be used to perform some kind of generic setup
;; for the particular route
(call-on-load curr-history-entry)
;; restore state from the history entry
(set! (-> *progress-pc-generic-store* clear-screen?) #f)
(set! (-> *progress-pc-generic-store* current-menu-hover-index) (-> curr-history-entry hover-index))
(set! (-> *progress-pc-generic-store* current-menu-scroll-index) (the float (-> curr-history-entry scroll-index)))
(if (!= (-> curr-history-entry progress-id) 'generic-menu)
;; do a recursive call to hit the relevant case in this switch
(set-menu-options obj (-> curr-history-entry progress-id))
;; otherwise, set the options
(set! (-> obj current-options) (-> curr-history-entry ref)))))
)
(when (= (-> obj current-options) #f)
(format #t "Didn't find new menu settings!!~%")
(pop-state obj)
)
0
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; CUSTOM MENU OPTIONS
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defmacro min-max-wrap-around+! (var inc minimum maximum)
`(set! ,var (min-max-wrap-around (+ ,var ,inc) ,minimum ,maximum)))
(defmethod respond-progress ((obj menu-aspect-ratio-custom-option) (arg0 progress) (arg1 symbol))
"Handle progress menu navigation logic."
(let ((aspect-custom-val-ptr (case (-> *progress-state-pc* aspect-ratio-ratio-index)
((0)
(&-> *pc-settings* aspect-custom-x))
((1)
(&-> *pc-settings* aspect-custom-y))
(else
(&-> *pc-settings* aspect-custom-x))
)))
(cond
((cpad-pressed? 0 left l-analog-left)
(when (< 0 (-> *progress-state-pc* aspect-ratio-ratio-index))
(sound-play "roll-over")
(1-! (-> *progress-state-pc* aspect-ratio-ratio-index))
)
)
((cpad-pressed? 0 right l-analog-right)
(when (> 1 (-> *progress-state-pc* aspect-ratio-ratio-index))
(sound-play "roll-over")
(1+! (-> *progress-state-pc* aspect-ratio-ratio-index))
)
)
((cpad-pressed? 0 up l-analog-up)
(when (> 99 (-> aspect-custom-val-ptr))
(sound-play "roll-over")
(1+! (-> aspect-custom-val-ptr))
)
)
((cpad-pressed? 0 down l-analog-down)
(when (< 1 (-> aspect-custom-val-ptr))
(sound-play "roll-over")
(1-! (-> aspect-custom-val-ptr))
)
)
((cpad-pressed? 0 triangle)
(cpad-clear! 0 triangle)
(sound-play "generic-beep")
(pop-state arg0)
)
((cpad-pressed? 0 confirm)
(cpad-clear! 0 confirm)
(when (>= (/ (the float (-> *pc-settings* aspect-custom-x)) (the float (-> *pc-settings* aspect-custom-y))) (/ 4.0 3.0))
(set! (-> *setting-control* user-default aspect-ratio) 'aspect4x3)
(set-aspect! *pc-settings* (-> *pc-settings* aspect-custom-x) (-> *pc-settings* aspect-custom-y))
(sound-play "generic-beep")
(pop-state arg0)
)
)
))
0
)
(defmethod respond-progress menu-frame-rate-disclaimer-option ((this menu-frame-rate-disclaimer-option) (arg0 progress) (arg1 symbol))
"Handle progress menu navigation logic."
(when (time-elapsed? (-> *progress-state-pc* frame-rate-disclaimer-time) (seconds 3))
(let ((choice (&-> *progress-state* yes-no-choice))
(sound? #f)
)
(cond
((cpad-pressed? 0 left l-analog-left)
(set! sound? (not (-> choice 0)))
(set! (-> choice 0) #t)
)
((cpad-pressed? 0 right l-analog-right)
(set! sound? (-> choice 0))
(set! (-> choice 0) #f)
)
((cpad-pressed? 0 confirm)
(cpad-clear! 0 confirm)
(cond
((-> *progress-state* yes-no-choice)
(true! *frame-rate-disclaimer-seen?*)
(sound-play "generic-beep")
(set-frame-rate! *pc-settings* (-> *frame-rate-options* (-> *progress-state-pc* frame-rate-choice-index)) #t)
(commit-to-file *pc-settings*)
(pop-state arg0)
)
(else
(sound-play "generic-beep")
(set-progress-frame-rate-index (the int #f))
(pop-state arg0)
)
)
)
)
(if sound?
(sound-play "generic-beep")
)
)
)
0
)
(defbehavior play-music-player progress ((info music-player-track-info) (flava int))
"play a music track using music player track info."
(set! (-> *progress-state-pc* music-player-track) info)
(set-setting! 'music (-> info name) 0.0 0)
(set-setting! 'sound-flava #f 999999.0 flava)
(set-setting! 'sound-mode #f 0.0 (-> info mode))
(apply-settings *setting-control*)
(sound-group-continue (sound-group music))
0)
(defbehavior stop-music-player progress ()
"play a music track using music player track info."
(remove-setting! 'music)
(remove-setting! 'sound-flava)
(remove-setting! 'sound-mode)
(sound-set-midi-reg 0 0)
(sound-set-midi-reg 2 0)
(sound-set-midi-reg 16 0)
(set! (-> *progress-state-pc* music-player-track) #f)
;(min! (-> *setting-control* user-current sound-excitement) 0.0)
(sound-set-midi-reg 16 (the int (* 100.0 (-> *setting-control* user-current sound-excitement))))
(set! (-> *setting-control* sound-excitement-change-time) (-> *display* base-clock frame-counter))
(apply-settings *setting-control*)
(sound-group-pause (sound-group music))
0)
(defun count-bits ((val int))
"return the number of bits set to 1."
(let ((bits 0))
(dotimes (i 64)
(if (logtest? val (ash 1 i))
(1+! bits))
)
bits)
)
(defun bit-on-offset ((bits int) (n int))
"return the bit offset of where the n'th bit that is set to 1 is"
(let ((bit-on-i 0))
(dotimes (i 64)
(if (logtest? bits (ash 1 i))
(if (= bit-on-i n)
(return i)
(1+! bit-on-i)))
))
-1)
(defmethod respond-progress ((obj menu-music-player-option) (arg0 progress) (arg1 symbol))
"Handle progress menu navigation logic."
(if (= 0.0 (-> obj max-scroll))
(set! (-> obj max-scroll) (+ -141.0 (* (-> *music-player-tracks* length) 23))))
;; control logic
(let ((scroll-sound? #f)
(track-max (1- (-> *music-player-tracks* length)))
(flava-max (1- (count-bits (the-as int (-> *music-player-tracks* (-> obj music-index) flava)))))
(old-excitement (-> obj excitement))
(play? #f))
;; excitement controls
(cond
((cpad-pressed? 0 right l-analog-right)
(seekl! (-> obj excitement) 3 1))
((cpad-pressed? 0 left l-analog-left)
(seekl! (-> obj excitement) 0 1))
)
(when (!= (-> obj excitement) old-excitement)
(sound-play "generic-beep")
(sound-set-midi-reg 16 (* 25 (-> obj excitement))) ;; play new excitement track
(unless (or (< (-> obj excitement) old-excitement) (process-by-name "sound-stinger" arg0))
(process-spawn-function process :to arg0 :name "sound-stinger"
(lambda :behavior process ((stinger int))
(let ((state-time (current-time)))
(format 0 "set stinger ~D~%" stinger)
(sound-set-midi-reg 0 stinger)
(until (>= (- (current-time) state-time) (seconds 0.5))
(suspend))
(sound-set-midi-reg 0 0)
)
)
(+ 9 (-> obj excitement)))
)
(unless (process-by-name "sound-excitement" arg0)
(process-spawn-function process :to arg0 :name "sound-excitement"
(lambda :behavior process ((excite-fade int))
(let ((state-time (current-time)))
(format 0 "set excitement fade ~D~%" (* 25 excite-fade))
(sound-set-midi-reg 2 (* 25 excite-fade)) ;; fade out old excitement track
(until (>= (- (current-time) state-time) (seconds 0.8))
(suspend))
)
)
old-excitement)
)
)
(cond
((or (cpad-pressed? 0 down l-analog-down)
(and (cpad-hold? 0 down l-analog-down) (>= (- (current-time) (-> obj last-move)) (seconds 0.2))))
(set! (-> obj last-move) (current-time))
(true! scroll-sound?)
(cond
((-> *progress-state-pc* music-player-selected)
(min-max-wrap-around+! (-> obj flava-index) 1 0 flava-max))
(else
(min-max-wrap-around+! (-> obj music-index) 1 0 track-max)
(if (= (-> obj music-index) 0)
(set! (-> obj current-scroll) 0.0)))
))
((or (cpad-pressed? 0 up l-analog-up)
(and (cpad-hold? 0 up l-analog-up) (>= (- (current-time) (-> obj last-move)) (seconds 0.2))))
(set! (-> obj last-move) (current-time))
(true! scroll-sound?)
(cond
((-> *progress-state-pc* music-player-selected)
(min-max-wrap-around+! (-> obj flava-index) -1 0 flava-max))
(else
(min-max-wrap-around+! (-> obj music-index) -1 0 track-max)
(if (= (-> obj music-index) track-max)
(set! (-> obj current-scroll) (-> obj max-scroll))))
))
((cpad-pressed? 0 confirm)
(cpad-clear! 0 confirm)
(sound-play "generic-beep")
(cond
;; track is locked. do nothing.
((not (get-bit (-> *pc-settings* music-unlocked) (-> obj music-index)))
)
;; we have a track with a submenu selected
((-> *progress-state-pc* music-player-selected)
(let ((flava (bit-on-offset (the-as int (-> *music-player-tracks* (-> obj music-index) flava)) (-> obj flava-index))))
(cond
((flava-unlocked? flava)
(set! (-> *progress-state-pc* music-player-flava) flava)
(true! play?)
;; exit submenu
(false! (-> *progress-state-pc* music-player-selected))
)
(else
;; do nothing.
)
)
)
)
;; we have a track with a submenu that's not open, open it
((nonzero? (-> *music-player-tracks* (-> obj music-index) flava))
;; same track that's currently playing, we can just reuse the flava position that's being used
;; otherwise use zero.
(if (= (-> *progress-state-pc* music-player-track) (-> *music-player-tracks* (-> obj music-index)))
(set! (-> obj flava-index) (count-bits (logand (1- (ash 1 (-> *progress-state-pc* music-player-flava))) (the-as int (-> *music-player-tracks* (-> obj music-index) flava)))))
(set! (-> obj flava-index) 0))
(true! (-> *progress-state-pc* music-player-selected)))
;; no submenu required, just play track.
(else
(set! (-> *progress-state-pc* music-player-flava) 0)
(true! play?))
)
(when play?
(play-music-player (-> *music-player-tracks* (-> obj music-index)) (-> *progress-state-pc* music-player-flava))
)
)
((cpad-pressed? 0 triangle)
(cpad-clear! 0 triangle)
(sound-play "generic-beep")
(cond
((-> *progress-state-pc* music-player-selected)
(false! (-> *progress-state-pc* music-player-selected)))
(else
(stop-music-player)
(pop-state arg0))
)
)
)
(when scroll-sound?
(sound-play "roll-over"))
)
0
)
(defun calculate-current-resolution-index ((want-aspect float) (want-w int) (want-h int))
(let ((res-w 0)
(res-h 0)
(res-aspect 0.0)
(res-valid 0))
(dotimes (i (pc-get-num-resolutions))
(pc-get-resolution i (& res-w) (& res-h))
(set! res-aspect (/ (the float res-w) (the float res-h)))
(when (or (= (pc-get-display-mode) 'windowed)
(< (fabs (- want-aspect res-aspect)) 0.05))
(if (and (= res-h want-h) (= res-w want-w))
(return res-valid))
(1+! res-valid))
)
(cond
;; 512x416
((and (= want-w 512) (= want-h 416))
res-valid)
;; 512x208
((and (= want-w 512) (= want-h 208))
(1+ res-valid))
;; nothing found
(else
-1)
))
)
(defglobalconstant DEBUG_RESOLUTION_OPTION #f)
(defmethod respond-progress ((this menu-resolution-option) (arg0 progress) (arg1 symbol))
"Handle progress menu navigation logic."
;; control logic
(let ((scroll-sound? #f)
(resolutions-valid 0)
(resolution-found #f)
(cur-w 0)
(cur-h 0)
(select-w -1)
(select-h -1)
)
(set! (-> this num-resolutions) (pc-get-num-resolutions))
(cond
;; valid state
((> (-> this num-resolutions) 0)
(true! (-> this valid?))
;; this code isnt the greatest...
;; the logic here is that, unlike jak 1, we don't build a list of resolutions and then scroll through that.
;; we calculate the list every frame, keeping track of the index that is highlighted.
;; this allows for infinite resolutions and for it to update live according to monitor changes.
(cond
((fullscreen?)
;; grab current window sizes for aspect ratio purposes (only relevant for fullscreen)
(pc-get-window-size (&-> this win-w) (&-> this win-h))
(set! cur-w (-> *pc-settings* width))
(set! cur-h (-> *pc-settings* height))
(set! (-> this win-aspect) (/ (the float (-> this win-w)) (the float (-> this win-h))))
(#when DEBUG_RESOLUTION_OPTION (format *stdcon* "fullscreen mode~%"))
)
(else
(set! cur-w (-> *pc-settings* window-width))
(set! cur-h (-> *pc-settings* window-height))
(set! (-> this win-aspect) (/ (the float cur-w) (the float cur-h)))
(#when DEBUG_RESOLUTION_OPTION (format *stdcon* "windowed mode~%"))
)
)
;; check if we want to automatically pick something already
(when (= -1 (-> this selected-index))
(set! (-> this selected-index) (calculate-current-resolution-index (-> this win-aspect) cur-w cur-h))
(#when DEBUG_RESOLUTION_OPTION (format #t "recalc index got ~D~%" (-> this selected-index)))
;; update if not invalid?
(cond
((= (-> this selected-index) -1)
(set! (-> this selected-index) 0) ;; give up, just select the first one by default.
)
(else
(set! select-w cur-w)
(set! select-h cur-h)
(true! resolution-found)
(set! (-> this scroll-index) (the float (-> this selected-index)))
)
)
)
;; if we haven't automatically picked a resolution, iterate and find the one highlighted
(when (not resolution-found)
(#when DEBUG_RESOLUTION_OPTION (format *stdcon* "finding resolution ~D~%" (-> this selected-index)))
(dotimes (i (-> this num-resolutions))
;; count "valid" resolutions
(let ((this-w 0) (this-h 0) (this-aspect 0.0))
(pc-get-resolution i (& this-w) (& this-h))
(set! this-aspect (/ (the float this-w) (the float this-h)))
(when (or (= (pc-get-display-mode) 'windowed)
(< (fabs (- (-> this win-aspect) this-aspect)) 0.05))
;; this is the highlighted option - save it
(when (= resolutions-valid (-> this selected-index))
(set! select-w this-w) (set! select-h this-h)
(true! resolution-found)
;;(set! i (-> this num-resolutions)) ;; abandon loop
)
(1+! resolutions-valid)
))
)
)
;; if still didn't find it, it must be one of the hardcoded cases
(cond
(resolution-found
;; yay
)
;; 512x416
((and (fullscreen?) (= resolutions-valid (-> this selected-index)))
(set! select-w 512)
(set! select-h 416)
)
;; 512x208
((and (fullscreen?) (= (1+ resolutions-valid) (-> this selected-index)))
(set! select-w 512)
(set! select-h 208)
)
;; STILL nothing? I don't know how this happens, just don't even try then.
(else
(false! (-> this valid?))
)
)
;; hardcoded cases
(when (fullscreen?)
(+! resolutions-valid 2))
)
;; not sure what happened, but there's not even any resolutions
(else
(false! (-> this valid?))
)
)
(cond
((-> this valid?)
(#when DEBUG_RESOLUTION_OPTION
(format *stdcon* "selected: ~D (~D x ~D)~%" (-> this selected-index) select-w select-h)
(format *stdcon* "current: ~D x ~D~%" cur-w cur-h)
(format *stdcon* "resolution: ~D valid (out of ~D)~%" resolutions-valid (-> this num-resolutions))
)
(cond
;; navigate up
((or (cpad-pressed? 0 up l-analog-up)
(and (cpad-hold? 0 up l-analog-up) (time-elapsed? (-> this last-move) (seconds 0.2))))
(set-time! (-> this last-move))
(true! scroll-sound?)
(min-max-wrap-around+! (-> this selected-index) -1 0 (1- resolutions-valid))
)
;; navigate down
((or (cpad-pressed? 0 down l-analog-down)
(and (cpad-hold? 0 down l-analog-down) (time-elapsed? (-> this last-move) (seconds 0.2))))
(set-time! (-> this last-move))
(true! scroll-sound?)
(min-max-wrap-around+! (-> this selected-index) 1 0 (1- resolutions-valid))
)
;; select
((cpad-pressed? 0 confirm)
(cpad-clear! 0 confirm)
(set-window-size! *pc-settings* select-w select-h)
(pc-settings-save)
(pop-state arg0))
;; exit
((cpad-pressed? 0 triangle)
(cpad-clear! 0 triangle)
(pop-state arg0))
)
)
(else
;; just triangle to exit
(when (cpad-pressed? 0 triangle)
(cpad-clear! 0 triangle)
(pop-state arg0))
)
)
(when scroll-sound?
(sound-play "roll-over"))
)
0
)
(defun hide-progress-screen ()
(when (and *progress-process* *progress-state* (!= (-> *progress-state* starting-state) 'title))
(stop-music-player)
(set-next-state (-> *progress-process* 0) 'go-away 0)
(clear-history! *progress-pc-generic-store*)
)
0
(none)
)
;; NOTE - this technically includes the gold/silver/bronze times
;; maybe figure out how to omit them eventually, (not sure if there is anything that differentiates them)
;; From a highscores perspective it doesn't really matter, the default scores are awful
(define-extern get-highscore-score (function int int))
(defun progress-pc-get-num-nonzero-local-scores ((highscore-index int))
(let ((count 0)
(score-ptr (get-game-score-ref *game-info* (get-highscore-score highscore-index))))
(dotimes (idx 8)
(when (> (-> score-ptr idx) 0)
(inc! count)))
count))
(defun progress-pc-max-external-scores ((highscore-index int))
(case highscore-index
((0) (pc-get-num-external-highscores "scatter"))
((1) (pc-get-num-external-highscores "blaster"))
((2) (pc-get-num-external-highscores "vulcan"))
((3) (pc-get-num-external-highscores "peacemaker"))
((4) (pc-get-num-external-highscores "jetboard"))
((5) (pc-get-num-external-race-times "class3"))
((6) (pc-get-num-external-race-times "class2"))
((7) (pc-get-num-external-race-times "class1"))
((8) (pc-get-num-external-race-times "port"))
((9) (pc-get-num-external-race-times "erol"))
((10) (pc-get-num-external-race-times "class3rev"))
((11) (pc-get-num-external-race-times "class2rev"))
((12) (pc-get-num-external-race-times "class1rev"))
((13) (pc-get-num-external-highscores "onin"))
((14) (pc-get-num-external-highscores "mash"))))
(defun progress-pc-max-highscore-rows ((highscore-index int))
(case highscore-index
((0) (+ (progress-pc-get-num-nonzero-local-scores highscore-index) (pc-get-num-external-highscores "scatter")))
((1) (+ (progress-pc-get-num-nonzero-local-scores highscore-index) (pc-get-num-external-highscores "blaster")))
((2) (+ (progress-pc-get-num-nonzero-local-scores highscore-index) (pc-get-num-external-highscores "vulcan")))
((3) (+ (progress-pc-get-num-nonzero-local-scores highscore-index) (pc-get-num-external-highscores "peacemaker")))
((4) (+ (progress-pc-get-num-nonzero-local-scores highscore-index) (pc-get-num-external-highscores "jetboard")))
((5) (+ (progress-pc-get-num-nonzero-local-scores highscore-index) (pc-get-num-external-race-times "class3")))
((6) (+ (progress-pc-get-num-nonzero-local-scores highscore-index) (pc-get-num-external-race-times "class2")))
((7) (+ (progress-pc-get-num-nonzero-local-scores highscore-index) (pc-get-num-external-race-times "class1")))
((8) (+ (progress-pc-get-num-nonzero-local-scores highscore-index) (pc-get-num-external-race-times "port")))
((9) (+ (progress-pc-get-num-nonzero-local-scores highscore-index) (pc-get-num-external-race-times "erol")))
((10) (+ (progress-pc-get-num-nonzero-local-scores highscore-index) (pc-get-num-external-race-times "class3rev")))
((11) (+ (progress-pc-get-num-nonzero-local-scores highscore-index) (pc-get-num-external-race-times "class2rev")))
((12) (+ (progress-pc-get-num-nonzero-local-scores highscore-index) (pc-get-num-external-race-times "class1rev")))
((13) (+ (progress-pc-get-num-nonzero-local-scores highscore-index) (pc-get-num-external-highscores "onin")))
((14) (+ (progress-pc-get-num-nonzero-local-scores highscore-index) (pc-get-num-external-highscores "mash")))
((15) (pc-get-num-external-speedrun-times "any"))
((16) (pc-get-num-external-speedrun-times "anyhoverless"))
((17) (pc-get-num-external-speedrun-times "allmissions"))
((18) (pc-get-num-external-speedrun-times "100"))
((19) (pc-get-num-external-speedrun-times "anyorbs"))
((20) (pc-get-num-external-speedrun-times "anyhero"))
(else 8)))
(defmethod respond-progress menu-highscores-option ((this menu-highscores-option) (progress progress) (selected? symbol))
"Handle progress menu navigation logic."
(set! (-> progress sliding) (seek-ease (-> progress sliding) 0.0 (* 0.1 (-> self clock time-adjust-ratio)) 0.3 (* 0.001 (-> self clock time-adjust-ratio))))
(set! (-> progress sliding-off) (seek-ease (-> progress sliding-off) 1.0 (* 0.1 (-> self clock time-adjust-ratio)) -0.3 (* 0.001 (-> self clock time-adjust-ratio))))
(when (and (!= *pc-waiting-on-rpc?* #t) (-> *bigmap* progress-minimap))
(let ((play-sound? #f))
(cond
((or (cpad-pressed? 0 right l-analog-right)
(and (cpad-hold? 0 right l-analog-right) (>= (- (current-time) (-> this last-move)) (seconds 0.2))))
(when (< 1 (get-num-highscores))
(set! (-> this last-move) (current-time))
(set! play-sound? #t)
(set! (-> this prev-page-index) (-> this page-index))
(set! (-> this page-index) (get-next-highscore (-> this page-index)))
;; ensure external data is fetched if applicable
(progress-pc-fetch-external-times (-> this page-index))
(set! (-> *progress-pc-generic-store* current-highscore-page-index) 0)
(set! (-> progress sliding) 1.0)
(set! (-> progress sliding-off) 0.0)
(set! (-> this slide-dir) -1.0)))
((or (cpad-pressed? 0 left l-analog-left)
(and (cpad-hold? 0 left l-analog-left) (>= (- (current-time) (-> this last-move)) (seconds 0.2))))
(when (< 1 (get-num-highscores))
(set! (-> this last-move) (current-time))
(set! (-> this prev-page-index) (-> this page-index))
(set! (-> this page-index) (get-prev-highscore (-> this page-index)))
;; ensure external data is fetched if applicable
(progress-pc-fetch-external-times (-> this page-index))
(set! (-> *progress-pc-generic-store* current-highscore-page-index) 0)
(set! play-sound? #t)
(set! (-> progress sliding) -1.0)
(set! (-> progress sliding-off) 0.0)
(set! (-> this slide-dir) 1.0)))
((or (cpad-pressed? 0 down l-analog-down)
(and (cpad-hold? 0 down l-analog-down) (>= (- (current-time) (-> this last-move)) (seconds 0.2))))
(when (and (-> *pc-settings* speedrunner-mode?)
(< (* (inc (-> *progress-pc-generic-store* current-highscore-page-index)) 8) (progress-pc-max-highscore-rows (-> this page-index))))
(set! (-> this last-move) (current-time))
(sound-play "secrets-scroll")
(inc! (-> *progress-pc-generic-store* current-highscore-page-index))))
((or (cpad-pressed? 0 up l-analog-up)
(and (cpad-hold? 0 up l-analog-up) (>= (- (current-time) (-> this last-move)) (seconds 0.2))))
(when (and (-> *pc-settings* speedrunner-mode?)
(> (-> *progress-pc-generic-store* current-highscore-page-index) 0))
(set! (-> this last-move) (current-time))
(sound-play "secrets-scroll")
(dec! (-> *progress-pc-generic-store* current-highscore-page-index))))
((cpad-pressed? 0 triangle confirm)
(set! (-> *progress-pc-generic-store* current-highscore-page-index) 0)
(logclear! (-> *cpad-list* cpads 0 button0-abs 0) (pad-buttons triangle confirm))
(logclear! (-> *cpad-list* cpads 0 button0-rel 0) (pad-buttons triangle confirm))
(set! (-> progress sliding) 0.0)
(set! (-> progress sliding-off) 1.0)
(if (!= (-> *progress-state* starting-state) 'title)
(sound-play "window-contract")
(sound-play "generic-beep"))
(pop-state progress)))
(when play-sound?
(sound-play "score-slide"))))
0)
(defmethod respond-progress ((this menu-sub-menu-option) (arg0 progress) (arg1 symbol))
"Handle progress menu navigation logic."
(when (and (-> *progress-state* secrets-unlocked)
(not (memcard-unlocked-secrets? #f))
(or (= (-> arg0 current-options) *title-pc*)
(= (-> arg0 current-options) *unlocked-secrets*)
(= (-> arg0 current-options) *select-scene-options*)
(= (-> arg0 current-options) *select-start-options*)
(= (-> arg0 current-options) *save-options-title*)
)
)
(set! (-> *progress-state* secrets-unlocked) #f)
(set! (-> arg0 state-pos) 0)
(set-next-state arg0 'secrets-insufficient-space 0)
)
(when (= (-> this name) (text-id progress-continue-without-saving))
(let ((a1-3 (get-state-check-card arg0 (-> arg0 current))))
(set-next-state arg0 a1-3 0)
)
)
(when (and (= (-> this name) (text-id progress-root-secrets))
(= *title-pc* (-> arg0 current-options))
(not (memcard-unlocked-secrets? #f))
(= (-> arg0 option-index) 4)
)
(set! (-> arg0 option-index) 0)
0
)
(when (cpad-pressed? 0 confirm)
(cpad-clear! 0 confirm)
(cond
((= (-> this name) (text-id progress-demo-exit))
(case *kernel-boot-message*
(('demo-shared)
(set! *master-exit* 'force)
(set-master-mode 'game)
)
(('demo)
(set! (-> *game-info* mode) 'play)
(initialize! *game-info* 'game (the-as game-save #f) "demo-restart")
)
)
)
((= (-> this name) (text-id progress-continue-without-saving))
(progress-intro-start (logtest? (-> *game-info* purchase-secrets) (game-secrets hero-mode)))
)
((= (-> this next-state) 'back)
(pop-state arg0)
)
(else
(sound-play "generic-beep")
(push-state arg0)
(set-next-state arg0 (-> this next-state) 0)
)
)
)
0
)
(defmethod respond-progress ((this menu-secret-option) (arg0 progress) (arg1 symbol))
"Handle progress menu navigation logic."
(let* ((hero? (logtest? (-> *game-info* secrets) (game-secrets hero-mode)))
(min-item (if (not hero?)
0
(-> this num-items)
)
)
(max-item (if (not hero?)
(+ (-> this num-items) -1)
(+ (-> this num-items) -1 (-> this num-hero-items))
)
)
(num-pc-items (-> *pc-cheats-list* length)))
(seek-ease! (-> arg0 sliding) 0.0 (* 0.1 (-> self clock time-adjust-ratio)) 0.3 (* 0.001 (-> self clock time-adjust-ratio)))
(cond
((and hero? (< (-> this item-index) min-item))
(set! (-> this item-index) min-item)
(set! (-> this prev-item-index) min-item)
)
((and (not hero?) (< (+ max-item num-pc-items) (-> this item-index)))
(set! (-> this item-index) min-item)
(set! (-> this prev-item-index) min-item)
)
)
(menu-update-purchase-secrets this)
(when (-> *bigmap* progress-minimap)
(let ((scroll-sound? #f))
(cond
((= arg1 #t)
(when (cpad-pressed? 0 confirm)
(cpad-clear! 0 confirm)
(sound-play "generic-beep")
(set! (-> arg0 selected-option) #f)
)
)
(else
(cond
((or (cpad-pressed? 0 down l-analog-down)
(and (cpad-hold? 0 down l-analog-down) (time-elapsed? (-> this last-move) (seconds 0.2)))
)
(set-time! (-> this last-move))
(when (< (-> this item-index) (+ max-item num-pc-items))
(set! (-> this prev-item-index) (-> this item-index))
(set! scroll-sound? #t)
(+! (-> this item-index) 1)
(set! (-> arg0 sliding) 1.0)
(set! (-> arg0 sliding-off) 0.0)
)
)
((or (cpad-pressed? 0 up l-analog-up)
(and (cpad-hold? 0 up l-analog-up) (time-elapsed? (-> this last-move) (seconds 0.2)))
)
(set-time! (-> this last-move))
(when (< min-item (-> this item-index))
(set! (-> this prev-item-index) (-> this item-index))
(+! (-> this item-index) -1)
(set! scroll-sound? #t)
(set! (-> arg0 sliding) -1.0)
(set! (-> arg0 sliding-off) 0.0)
)
)
((cpad-pressed? 0 confirm)
(cpad-clear! 0 confirm)
(cond
((<= (-> this item-index) max-item)
(let* ((item (-> this item-index))
(cost (-> this secret-items item cost))
(flag (-> this secret-items item flag))
(on-off? (= (-> this secret-items item can-toggle) #t))
(avail-after (-> this secret-items item avail-after))
(skill (the int (-> *game-info* skill))))
(cond
((and (logtest? (-> *game-info* purchase-secrets) flag) on-off?)
(set! (-> arg0 selected-option) #t)
(sound-play "generic-beep")
)
((= (-> this secret-items item can-toggle) 'auto)
)
((logtest? (-> *game-info* purchase-secrets) flag)
(set! (-> arg0 selected-option) #t)
(sound-play "generic-beep")
)
)
)
)
(else
(let* ((cheat (-> *pc-cheats-list* (- (-> this item-index) max-item 1))))
(cond
((or (not (logtest? (-> *pc-settings* cheats-revealed) (-> cheat flag))) (not (logtest? (-> *pc-settings* cheats-purchased) (-> cheat flag))))
;; unknown cheat or not enough money. GET MORE!!
;; nothing happens
)
((not (logtest? (-> *pc-settings* cheats-unlocked) (-> cheat flag)))
;; known cheat, enough money, but LOCKED!
(set! (-> arg0 selected-option) #t)
(sound-play "generic-beep")
)
((= (-> cheat can-toggle) #t)
;; known cheat, unlocked, toggleable
(logxor! (-> *pc-settings* cheats) (-> cheat flag))
(sound-play "generic-beep")
)
((= (-> cheat can-toggle) 'auto)
)
(else
;; known cheat and unlocked! does something else automatically i guess
(set! (-> arg0 selected-option) #t)
(sound-play "generic-beep")
)
)
)
)
)
)
((cpad-pressed? 0 triangle)
(cpad-clear! 0 triangle)
(if (!= (-> *progress-state* starting-state) 'title)
(sound-play "window-contract")
(sound-play "generic-beep")
)
(pop-state arg0)
)
)
(if scroll-sound?
(sound-play "secrets-scroll")
)
)
)
)
)
)
(cpad-clear! 0 confirm)
0
)
(defmethod respond-progress ((this menu-exit-game-option) (arg0 progress) (selected? symbol))
"Handle progress menu navigation logic."
(let ((choice (&-> *progress-state* yes-no-choice))
(gp-0 #f)
)
(when selected?
(cond
((cpad-pressed? 0 left l-analog-left)
(when (not (-> choice 0))
(set! gp-0 #t)
(set! (-> choice 0) #t)
)
)
((cpad-pressed? 0 right l-analog-right)
(set! gp-0 (-> choice 0))
(set! (-> choice 0) #f)
)
((cpad-pressed? 0 confirm)
(if (-> choice 0)
(kernel-shutdown (runtime-exit-status exit))
)
)
)
)
(if gp-0
(sound-play "generic-beep")
)
)
0
)