Files
jak-project/goal_src/jak2/pc/pckernel.gc
T
Tyler Wilding d1a6c60eb8 game: disable keyboard input by default, give users a way to enable it via the imgui menu (#3295)
It was narrowed down recently that a lot of people have issues with the
controller input because of Steam Input working as intended. Steam Input
can be configured to replicate controller inputs as keyboard inputs (for
example, pressing X on your controller presses Enter on the keyboard).

This results in the problem of "jumping pauses the game" and similar
issues. This is a consequence of the intended behaviour of the game
listening to all input sources at the same time.

Since the vast majority of players are using controllers over keyboards,
it makes sense to disable the keyboard input by default to solve this
problem. However that makes things awkward for users that want to use
the keyboard (how do they enable the setting). The solution is a new
imgui option in the settings menu:
![Screenshot 2024-01-07
141224](https://github.com/open-goal/jak-project/assets/13153231/6f9ffa2d-be7a-433d-b698-15b70210e97e)

**Known issue that I don't care about** -- in Jak 1's menu code, since
the flags are controlled by pointers to values instead of a lambda like
in jak 2, the menu won't update live with the imgui option. This has no
functional impact and I don't care enough to fix it.

I also made the pc-settings.gc file persist on first load if the file
wasn't found. Hopefully this helps diagnose the support issues related
to the black screen.

# Why not just ignore the keyboard inputs for a period of time?

This won't work, the keyboard is polled every frame. Therefore if you
hold down the X button on your controller, steam is continuously
signaling that `Enter` is held down on the keyboard.

Yes it would be possible to completely disable the keyboard while the
controller is being used, but this defeats the purpose of creating an
input system that allows multiple input sources at the same time.

With an explicit option, not only can the user decide the behaviour they
want (do they want the keyboard ignored or simultaneously listened to)
but we avoid breaking strange edge-cases in usage leading to never
ending complexity:
- ie. imagine steam input sends events to the mouse, well you can't
disable the mouse while using the keyboard because most times people are
using mouse and keyboard
- ie. a user that wants to hold a direction with the keyboard and press
buttons on the controller in tandem (something i frequently do while
TAS'ing, to move in a perfect straight line)
2024-02-23 18:19:07 -05:00

913 lines
39 KiB
Common Lisp

;;-*-Lisp-*-
(in-package goal)
#|
This file runs the game-specific version of the pckernel.
See pckernel-common.gc for the bulk of the pckernel.
|#
(define-extern get-active-mission-description (function discord-info string))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; pc cheats list
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(deftype pc-cheat-info (basic)
((name text-id)
(unlock text-id)
(unlock-func symbol) ;; function symbol
(skill int) ;; skill points required. leave as 0 if you only want a custom unlock func
(flag pc-cheats)
(can-toggle symbol) ;; how it can be toggled
;; only show after this point in the story
(avail-after game-task-node)
(avail-after-hero game-task-node)
)
)
;;;;; pc cheat unlock functions
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun pc-cheat-health-bars-unlock ()
"#t = cheat unlock requirements met. #f = locked"
(>= (get-count-for-enemy (-> *pc-settings* stats kill-stats) 'civilian) (-> *pc-cheat-state* kill-civvie-target)))
(defun pc-cheat-vehicle-health-bars-unlock ()
"#t = cheat unlock requirements met. #f = locked"
#f)
(defun pc-cheat-board-fast-unlock ()
"#t = cheat unlock requirements met. #f = locked"
(cond
;; haven't reached sewer valve task yet or not in sewers or at the start of sewers
((or (not (task-node-closed? (game-task-node sewer-board-introduction)))
(not (aif (level-get-target-inside *level*) (= (-> it name) 'sewer)))
(aif (get-continue-by-name *game-info* "sewer-start") (< (vector-vector-distance (target-pos 0) (-> it trans)) (meters 20))))
(set! (-> *pc-cheat-state* sewer-valve-on) #b000)
(set! (-> *pc-cheat-state* sewer-valve-start-time) 0)
(set! (-> *pc-cheat-state* sewer-valve-end-time) 0)
#f)
;; during sewer valve task
((not (task-node-closed? (game-task-node sewer-board-resolution)))
(set! (-> *pc-cheat-state* sewer-valve-end-time) (get-current-time))
(when (= (get-base-height *ocean-map*) -216498.17)
(set! (-> *pc-cheat-state* sewer-valve-start-time) (get-current-time))
)
;; no cheating!
(cond
((task-node-open? (game-task-node sewer-board-drain))
(aif (process-by-ename "sew-valve-6")
(if (and (= 'turn (-> it next-state name)) (= (-> *pc-cheat-state* sewer-valve-on) #b000))
(logior! (-> *pc-cheat-state* sewer-valve-on) #b001)))
(aif (process-by-ename "sew-valve-7")
(if (and (= 'turn (-> it next-state name)) (= (-> *pc-cheat-state* sewer-valve-on) #b001))
(logior! (-> *pc-cheat-state* sewer-valve-on) #b010)))
)
((and (task-node-closed? (game-task-node sewer-board-drain)) (= (-> *pc-cheat-state* sewer-valve-on) #b011))
(logior! (-> *pc-cheat-state* sewer-valve-on) #b100))
)
(when (and (= *cheat-mode* 'debug) (!= (-> *pc-cheat-state* sewer-valve-end-time) (-> *pc-cheat-state* sewer-valve-start-time)))
(format *stdcon* "sewer-valve time: ~0e/~D v: ~3B~%" (- (-> *pc-cheat-state* sewer-valve-end-time) (-> *pc-cheat-state* sewer-valve-start-time))
(-> *pc-cheat-state* sewer-valve-target-seconds)
(-> *pc-cheat-state* sewer-valve-on))
)
#f)
;; sewer valve task done!
(else
(let (
(time-taken (- (-> *pc-cheat-state* sewer-valve-end-time) (-> *pc-cheat-state* sewer-valve-start-time))))
(and (< 0 time-taken)
(< time-taken (seconds (-> *pc-cheat-state* sewer-valve-target-seconds)))
(= #b111 (-> *pc-cheat-state* sewer-valve-on)))
)
)
))
(defun pc-cheat-statistics-unlock ()
"#t = cheat unlock requirements met. #f = locked"
(killed-each-metalhead?))
(defun pc-cheat-suck-in-all-unlock ()
"#t = cheat unlock requirements met. #f = locked"
#f)
(defun pc-cheat-fast-travel-unlock ()
"#t = cheat unlock requirements met. #f = locked"
#f)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;; pc cheat unlock functions end
(defmacro static-pc-cheat-info (name flag can-toggle avail-after &key (skill 0) &key (unlock (null true-func)))
"helper for making a new static pc-cheat-info
unlock is a pair in format (unlock-text-id unlock-func) where unlock-func returns #t if the unlock requirement is met and #f otherwise
skill is the skill requirement. until the requirement is met (and avail-after is closed), it will show 'X required' in the menu
avail-after can be a single task-node or a pair of two, car is for normal game and cadr for hero mode"
`(new 'static 'pc-cheat-info :name (text-id ,name)
:unlock (text-id ,(car unlock))
:unlock-func (quote ,(cadr unlock))
:skill ,skill
:flag (pc-cheats ,flag)
:avail-after (game-task-node ,(if (pair? avail-after) (car avail-after) avail-after))
:avail-after-hero (game-task-node ,(if (pair? avail-after) (cadr avail-after) avail-after))
:can-toggle (quote ,can-toggle))
)
(defmacro def-pc-cheat-list (name &rest items)
"helper for making a list of pc cheats. see static-pc-cheat-info for parameters"
`(define ,name (new 'static 'boxed-array :type pc-cheat-info ,@(apply (lambda (x) `(static-pc-cheat-info ,@x)) items)))
)
;; the list of cheats
(def-pc-cheat-list *pc-cheats-list*
;; name cheat flag can-toggle avail-after
(progress-cheats-music-player music-player #f fortress-escape-introduction)
(progress-cheats-real-time-of-day real-time-of-day #t fortress-escape-resolution)
(progress-cheats-board-tricks board-tricks #t stadium-board1-gold)
(progress-cheats-health-bars health-bars #t palace-boss-resolution :unlock (progress-cheats-health-bars-unlock pc-cheat-health-bars-unlock))
;(progress-cheats-vehicle-health-bars vehicle-health-bars #t palace-boss-resolution :unlock (progress-cheats-vehicle-health-bars-unlock pc-cheat-vehicle-health-bars-unlock))
(progress-cheats-weather-bad weather-bad #t fortress-escape-resolution :skill 85)
(progress-cheats-weather-good weather-good #t fortress-escape-resolution :skill 85)
;(progress-cheats-suck-in-all suck-in-all #t forest-scouts-get-board :unlock (progress-cheats-suck-in-all-unlock pc-cheat-suck-in-all-unlock))
(progress-cheats-turbo-board turbo-board #t sewer-board-introduction :unlock (progress-cheats-turbo-board-unlock pc-cheat-board-fast-unlock))
;(progress-cheats-fast-travel fast-travel auto city-defend-stadium-introduction :unlock (progress-cheats-fast-travel-unlock pc-cheat-fast-travel-unlock))
;(progress-cheats-orb-tracker orb-tracker auto nest-boss-resolution)
(progress-cheats-no-textures no-textures #t nest-boss-resolution :skill 115)
(progress-cheats-vehicle-invuln vehicle-invuln #t nest-boss-resolution :skill 185)
;(progress-cheats-fast-movies fast-movies #t nest-boss-resolution :skill 200)
;(progress-cheats-slow-movies slow-movies #t nest-boss-resolution :skill 200)
;(progress-cheats-fast-speed fast-speed #t nest-boss-resolution :skill 250)
;(progress-cheats-slow-speed slow-speed #t nest-boss-resolution :skill 250)
;(progress-cheats-statistics statistics auto atoll-water-introduction :unlock (progress-cheats-statistics-unlock pc-cheat-statistics-unlock))
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; music player list
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defenum music-player-flava
:bitfield #t :type uint8
(default)
(gun)
(board)
(mech)
(darkjak)
(pilot)
)
(deftype music-player-track-info (basic)
((text text-id)
(name symbol)
(mode int8)
(icon int16)
(flava music-player-flava)
(avail-after game-task-node)
)
)
(defmacro static-music-track-info (name &key text &key avail-after &key (mode 0) &key icon &key (flava ()))
`(new 'static 'music-player-track-info :text (text-id ,text) :name ,name :icon ,icon :avail-after (game-task-node ,avail-after) :mode ,mode :flava (music-player-flava ,@flava))
)
(define *music-player-tracks* (new 'static 'boxed-array :type music-player-track-info
(static-music-track-info 'city1 :mode 0 :icon 1 :text progress-music-player-city :avail-after fortress-escape-resolution :flava (gun board pilot))
(static-music-track-info 'city1 :mode 1 :icon 1 :text progress-music-player-city-battle :avail-after city-help-kid-resolution)
(static-music-track-info 'ruins :mode 0 :icon 4 :text progress-music-player-ruins :avail-after ruins-tower-resolution :flava (gun board mech darkjak))
(static-music-track-info 'atoll :mode 0 :icon 6 :text progress-music-player-atoll :avail-after atoll-water-resolution :flava (gun darkjak board))
(static-music-track-info 'sewer :mode 0 :icon 3 :text progress-music-player-sewer :avail-after fortress-dump-resolution :flava (gun board mech darkjak))
(static-music-track-info 'sewer :mode 1 :icon 3 :text progress-music-player-sewer-battle :avail-after fortress-dump-resolution)
(static-music-track-info 'danger11 :mode 0 :icon 3 :text progress-music-player-danger11 :avail-after fortress-dump-resolution)
(static-music-track-info 'atoll :mode 1 :icon 6 :text progress-music-player-atoll-battle :avail-after atoll-sig-resolution)
(static-music-track-info 'strip :mode 0 :icon 5 :text progress-music-player-strip :avail-after strip-rescue-resolution :flava (gun board mech darkjak))
(static-music-track-info 'mountain :mode 0 :icon 8 :text progress-music-player-mountain :avail-after mountain-collection-resolution :flava (gun board mech darkjak))
(static-music-track-info 'mountain :mode 1 :icon 8 :text progress-music-player-mountain-battle :avail-after mountain-collection-resolution)
(static-music-track-info 'palcab :mode 0 :icon 11 :text progress-music-player-palcab :avail-after palace-cable-resolution :flava (gun board darkjak))
(static-music-track-info 'forest :mode 0 :icon 9 :text progress-music-player-forest :avail-after forest-scouts-resolution :flava (gun board mech darkjak))
(static-music-track-info 'forest :mode 1 :icon 9 :text progress-music-player-forest-battle :avail-after forest-scouts-resolution)
(static-music-track-info 'danger9 :mode 0 :icon 1 :text progress-music-player-danger9 :avail-after city-intercept-tanker-resolution)
(static-music-track-info 'race :mode 0 :icon 2 :text progress-music-player-race :avail-after stadium-race-class3-resolution)
(static-music-track-info 'danger3 :mode 0 :icon 1 :text progress-music-player-danger3 :avail-after city-play-onin-game-resolution)
(static-music-track-info 'danger1 :mode 0 :icon 8 :text progress-music-player-battle :avail-after canyon-insert-items-resolution)
(static-music-track-info 'danger2 :mode 0 :icon 12 :text progress-music-player-danger2 :avail-after tomb-boss-door)
(static-music-track-info 'danger7 :mode 0 :icon 15 :text progress-music-player-danger7 :avail-after tomb-boss-resolution)
(static-music-track-info 'danger10 :mode 0 :icon 10 :text progress-music-player-danger10 :avail-after drill-mech-resolution)
(static-music-track-info 'palcab :mode 1 :icon 14 :text progress-music-player-palcab-battle :avail-after castle-boss-resolution)
(static-music-track-info 'danger6 :mode 0 :icon 14 :text progress-music-player-danger6 :avail-after castle-boss-resolution)
(static-music-track-info 'danger4 :mode 0 :icon 17 :text progress-music-player-danger4 :avail-after nest-boss-resolution)
))
;; automatically add the default flava to all tracks that had flavas marked
(dotimes (i (-> *music-player-tracks* length))
(if (nonzero? (-> *music-player-tracks* i flava))
(logior! (-> *music-player-tracks* i flava) (music-player-flava default)))
)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; fancy controller LED fader mechanics
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(deftype led-fader-state (structure)
((enable? symbol)
(amount float)
(cur-color vector :inline)
(start-color vector :inline)
(end-color vector :inline)
)
(:methods
(enable (_type_ vector) int)
(update (_type_ float float) vector)
(disable (_type_) int)
)
)
(defmethod enable ((this led-fader-state) (start-from vector))
"begin transition."
(when (-> this enable?)
(disable this))
(vector-copy! (-> this start-color) start-from)
(set! (-> this amount) 0.0)
(true! (-> this enable?))
0)
(defmethod disable ((this led-fader-state))
"disable transition."
(set! (-> this amount) 0.0)
(update this 0.0 0.1)
(false! (-> this enable?))
0)
(defun vector3-lerp! ((dest vector) (a vector) (b vector) (alpha float))
"Linearly interpolate between two vectors. Alpha isn't clamped.
w will be set to what's in vector a."
(rlet ((vf0 :class vf)
(vf1 :class vf)
(vf2 :class vf)
(vf3 :class vf)
(vf4 :class vf)
)
(init-vf0-vector)
(.lvf vf1 (&-> a quad))
(.lvf vf2 (&-> b quad))
(.mov vf4 alpha)
(.add.x.vf vf3 vf1 vf0 :mask #b1000)
(.sub.vf vf2 vf2 vf1)
(.mul.x.vf vf2 vf2 vf4)
(.add.vf vf3 vf1 vf2 :mask #b111)
(.svf (&-> dest quad) vf3)
dest
)
)
(defun vector3-copy!! ((dest vector) (src vector))
"copy just the xyz fields of src into dest"
(rlet ((vf0 :class vf)
(dest-vf :class vf)
(src-vf :class vf))
(init-vf0-vector)
(.lvf dest-vf (&-> dest quad))
(.lvf src-vf (&-> src quad))
(.add.vf dest-vf vf0 src-vf :mask #b111)
(.svf (&-> dest quad) dest-vf)
dest
)
)
(defmethod update ((this led-fader-state) (to float) (duration float))
"disable transition."
(when (-> this enable?)
(seek! (-> this amount) to (/ (-> *target* clock seconds-per-frame) duration))
(vector4-lerp! (-> this cur-color) (-> this start-color) (-> this end-color) (-> this amount))
(if (and (= to 0.0) (= 0.0 (-> this amount)))
(false! (-> this enable?)))
)
(-> this cur-color))
;; global vars
(define *led-fader-state* (new 'static 'led-fader-state :enable? #f))
(define *led-darkjak-color* (static-vector 0.5 0.0 0.5 1.0))
(define *led-tomb-simon-off-color* (static-vector 0.0 0.0 0.0 1.0))
(define *led-tomb-simon-color* (static-vector 0.0 0.0 0.0 1.0))
(define *led-wanted-flash-color* (static-vector 1.0 0.0 0.0 1.0))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; methods
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defmethod initialize ((obj pc-settings-jak2))
"initial initialize method to be run after allocating"
(set! (-> obj music-unlocked) (new 'global 'bit-array (-> *music-player-tracks* length)))
((method-of-type pc-settings initialize) obj)
obj)
(defmethod set-game-setting! ((obj pc-settings-jak2) (setting symbol) (value symbol))
(case setting
(('video-mode)
(set! (-> *setting-control* user-current video-mode) #f)
(set! (-> *setting-control* user-default video-mode) value)
)
(('aspect-ratio)
(set! (-> *setting-control* user-default aspect-ratio) value)
)
(else
(format #t "unknown setting ~A (~A) to set-game-setting!" setting value))
)
)
(defmethod get-game-setting ((obj pc-settings-jak2) (setting symbol))
(case setting
(('video-mode)
(-> *setting-control* user-default video-mode)
)
(('aspect-ratio)
(-> *setting-control* user-default aspect-ratio)
)
(else
(format #t "unknown setting ~A to get-game-setting" setting)
#f)
)
)
(defmethod set-game-language! ((obj pc-settings-jak2) (lang language-enum))
(set! (-> *setting-control* user-default language) lang)
)
(defmethod get-game-language ((obj pc-settings-jak2))
(get-current-language)
)
(defmethod update ((obj pc-settings-jak2))
"Set the default misc settings"
((method-of-type pc-settings update) obj)
(set! *hires-sky* (-> obj hires-clouds?))
(when (not (led-enabled? obj))
(disable *led-fader-state*)
)
(none))
(defun real-movie? ()
"are we in an actual cutscene and should letterbox the view?"
(and (!= #f *scene-player*) (nonzero? movie?) (movie?)))
(defmethod update-discord-rpc ((obj pc-settings-jak2))
"update discord rpc module"
(let ((info (new 'stack 'discord-info)))
(set! (-> info orb-count) (-> *game-info* skill-total))
(set! (-> info gem-count) (-> *game-info* gem-total))
(set! (-> info death-count) (-> *game-info* total-deaths))
(set! (-> info task) "unknown")
(set! (-> info status) (get-active-mission-description info))
;; grab the name of the level we're in
(cond
((or (aif (level-get *level* 'title) (= (-> it status) 'active))
(and *progress-process*
(= 'title (-> *progress-process* 0 state-stack 0))))
;; in title screen.
(set! (-> info level) (symbol->string 'title))
(set! (-> info status) "In title screen"))
(else
(set! (-> info level) (aif (-> *load-state* vis-nick) (symbol->string it) "unknown")))
)
(set! (-> info cutscene?) (real-movie?))
(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 - update to new with-profiler syntax
(pc-discord-rpc-update info)
)
(none))
(defmethod update-speedrun ((obj pc-settings-jak2))
"update speedrun module"
;; TODO - update to new with-profiler syntax
;; (with-profiler "speedrun-update"
(update! *speedrun-info*)
;;)
(none))
(defmethod update-video-hacks ((obj pc-settings-jak2))
"update the graphics hacks used for the progress menu. ugh."
(set! (-> (get-video-params) relative-x-scale) (-> obj aspect-ratio-reciprocal))
(set! (-> (get-video-params) relative-x-scale-reciprical) (-> obj aspect-ratio-scale))
)
(defmethod eligible-for-fast-elevator? ((obj pc-settings-jak2) (proc process))
"is this a valid process for a fast elevator?"
(and (-> obj fast-elevator?) (not (or (string= (-> proc name) "drill-lift-1")
(string= (-> proc name) "drill-lift-2"))))
)
(defmethod get-airlock-speed ((obj pc-settings-jak2))
"return the current speed modifier for airlocks"
(if (-> obj fast-airlock?)
(-> *pc-cheat-state* airlock-speed)
1.0))
(defmethod get-airlock-close-speed ((obj pc-settings-jak2))
"return the current closing speed modifier for airlocks"
(if (-> obj fast-airlock?)
(-> *pc-cheat-state* airlock-close-speed)
1.0))
(defmethod led-enabled? ((obj pc-settings-jak2))
"should the controller led be set?"
(or (-> obj controller-led-hp?)
(-> obj controller-led-status?)
))
(defmethod update-led ((obj pc-settings-jak2))
"set the controller led color by modifying the controller-led-color vector"
;; default color is just blue.
(set-vector-xyz! (-> obj controller-led-color) 0.0 0.0 1.0)
(when *target*
(let ((disable-fader? #t)
(simon-plat (the process #f)))
(when (-> obj controller-led-hp?)
;; flicker led according to hp. lower hp = faster and more intense flicker
(cond
((= (-> *target* fact health) 0.0)
;; dead. just set to minimum brightness.
(set! (-> obj controller-led-color a) (-> obj controller-led-min-brightness))
)
(else
(let ((flicker-speed (lerp-scale 2.0 0.0
(-> *target* fact health)
1.0 (-> *FACT-bank* health-max-default)))
(flicker-amp (lerp-scale (- 1.0 (-> obj controller-led-min-brightness)) (- 1.0 (-> obj controller-led-max-brightness))
(-> *target* fact health)
1.0 (-> *FACT-bank* health-max-default)))
)
(set! (-> obj controller-led-color a) (- 1.0 (* flicker-amp (/ (+ 1.0 (sin (* flicker-speed (degrees (current-time))))) 2.0))))
)
)
)
)
(when (-> obj controller-led-status?)
(set-vector-xyz! (-> obj controller-led-color) 1.0 1.0 1.0)
(cond
;; simon plat
((set! simon-plat (search-process-tree *active-pool*
(lambda ((proc process)) (and (= (-> proc type symbol) 'tomb-plat-simon)
(-> proc next-state)
(symbol-member? (-> proc next-state name) '(appear show-sequence idle))))))
(set! disable-fader? #f)
(case (-> simon-plat next-state name)
(('appear)
;; simon plats appearing - fade to black
(vector-copy! *led-tomb-simon-color* *led-tomb-simon-off-color*)
(vector-copy! (-> *led-fader-state* end-color) *led-tomb-simon-off-color*)
(if (not (-> *led-fader-state* enable?))
(enable *led-fader-state* (-> obj controller-led-color)))
)
(('show-sequence)
;; showing simon sequence - use only the flashing color
(vector-copy! (-> obj controller-led-color) *led-tomb-simon-color*)
;; set fader color to max, if it's not somehow.
(when (!= (-> *led-fader-state* amount) 1.0)
(set! (-> *led-fader-state* amount) 1.0))
)
(('idle)
;; playing simon sequence - simon blocks set end-color here
)
)
(update *led-fader-state* 1.0 2.0)
(unless (= (-> simon-plat next-state name) 'show-sequence)
(vector3-copy!! (-> obj controller-led-color) (-> *led-fader-state* cur-color)))
)
;; simon plat block
((set! simon-plat (search-process-tree *active-pool*
(lambda ((proc process)) (and (= (-> proc type symbol) 'tomb-simon-block)
(-> proc next-state)
(= (-> proc next-state name) 'dangerous)))))
;; simon plat mistake - fade to black (start color was set by simon block)
(set! disable-fader? #f)
(vector-copy! (-> *led-fader-state* end-color) *led-tomb-simon-off-color*)
(update *led-fader-state* 1.0 1.5)
(vector3-copy!! (-> obj controller-led-color) (-> *led-fader-state* cur-color))
)
;; gun
((and (nonzero? (-> *target* gun)) (focus-test? *target* gun))
(case (-> *target* gun gun-type)
(((pickup-type eco-yellow))
(set-vector-xyz! (-> obj controller-led-color) 1.0 0.75 0.125))
(((pickup-type eco-red))
(set-vector-xyz! (-> obj controller-led-color) 0.65 0.0 0.0))
(((pickup-type eco-blue))
(set-vector-xyz! (-> obj controller-led-color) 0.4375 0.8125 1.0))
(((pickup-type eco-dark))
(set-vector-xyz! (-> obj controller-led-color) 0.6875 0.6 0.78125))
)
)
;; darkjak
((and (nonzero? (-> *target* darkjak)) (focus-test? *target* dark))
(vector-copy! (-> *led-fader-state* end-color) *led-darkjak-color*)
(set! disable-fader? #f)
(if (not (-> *led-fader-state* enable?))
(enable *led-fader-state* (-> obj controller-led-color)))
(if (and (-> *target* next-state) (= (-> *target* next-state name) 'target-darkjak-get-off))
(update *led-fader-state* 0.0 0.75)
(update *led-fader-state* 1.0 0.3))
(vector3-copy!! (-> obj controller-led-color) (-> *led-fader-state* cur-color))
)
;; indax
((focus-test? *target* indax)
(set-vector-xyz! (-> obj controller-led-color) 1.0 0.5 0.0)
)
;; mech
((focus-test? *target* mech)
(set-vector-xyz! (-> obj controller-led-color) 1.0 1.0 0.0)
)
;; board
((focus-test? *target* board)
(set-vector-xyz! (-> obj controller-led-color) 0.0 1.0 1.0)
)
)
;; wanted flash
(awhen (the hud-map (process-by-name "hud-map" *active-pool*))
(when (not (hidden? it))
(let ((flash-amount (/ (+ (sin (degrees (-> it values 1 current))) 1.0) 2)))
(vector3-lerp! (-> obj controller-led-color) (-> obj controller-led-color) *led-wanted-flash-color* flash-amount)
))
)
)
(when disable-fader?
(disable *led-fader-state*))
))
#t)
(defmacro flava-unlocked? (flava)
"return #t if the specified flava is unlocked"
`(-> *pc-settings* flava-unlocked ,flava))
(defun inside-city? ()
"are we inside haven city?"
(symbol-member? (-> *game-info* current-continue vis-nick) ;; TODO get actual level we're in?
'(ctysluma ctyslumb ctyslumc
ctygena ctygenb ctygenc
ctymarka ctymarkb
ctyfarma ctyfarmb
ctyinda ctyindb
ctypal ctyport stadium)))
(defmethod update-cheats ((obj pc-settings-jak2))
"run cheats."
;; run cheats here.
;;;;;;;;;;;;;;;;;;;
(when (pc-cheats? (-> obj cheats) real-time-of-day)
(let ((date (new 'stack-no-clear 'scf-time)))
(scf-get-time date)
(when (zero? (-> date stat))
(let* ((cur-time (-> *display* bg-clock frame-counter))
(day-len (seconds 1440)) ;; a full in-game day
(want-hour (bcd->dec (-> date hour)))
(want-minute (bcd->dec (-> date minute)))
(target-hour-frame (/ (the int (* (fsec 3600) want-hour)) 60))
(target-minute-frame (/ (the int (* (fsec 60) want-minute)) 60))
)
(set! (-> *display* bg-clock frame-counter) (+ (- cur-time (mod cur-time day-len)) day-len (+ target-hour-frame target-minute-frame)))
))
))
;; turbo jet board cheat
(cond
((and (pc-cheats? (-> obj cheats) turbo-board)
*target*
(focus-test? *target* board)
(inside-city?))
(set-setting! 'string-spline-max-move 'abs (* (-> *pc-cheat-state* turbo-board-speed) (meters 2)) 0)
(set-setting! 'string-spline-accel 'abs (* (-> *pc-cheat-state* turbo-board-speed) (meters 0.045)) 0)
(set-setting! 'string-spline-max-move-player 'abs (* (-> *pc-cheat-state* turbo-board-speed) (meters 1.5)) 0)
(set-setting! 'string-spline-accel-player 'abs (* (-> *pc-cheat-state* turbo-board-speed) (meters 0.035)) 0)
(set-cheat-state-flag! turbo-board)
)
(else
(remove-setting! 'string-spline-max-move)
(remove-setting! 'string-spline-accel)
(remove-setting! 'string-spline-max-move-player)
(remove-setting! 'string-spline-accel-player)
(clear-cheat-state-flag! turbo-board)
)
)
(pc-set-gfx-hack (pc-gfx-hack no-tex) (pc-cheats? (-> obj cheats) no-textures))
;; run cheats end!!!
;;;;;;;;;;;;;;;;;;;;
;; check unlocked cheats
;;;;;;;;;;;;;;;;;;;;;;;;
(dotimes (i (-> *pc-cheats-list* length))
;; reveals cheats if they have been purchased, purchases cheats if they have been unlocked, unlocks cheats if they have been enabled.
;; the cheat process requires the steps to be filled in this order, see sequential checking below
(logior! (-> *pc-settings* cheats-revealed) (logior! (-> *pc-settings* cheats-purchased) (logior! (-> *pc-settings* cheats-unlocked) (-> *pc-settings* cheats))))
(let* ((cheat (-> *pc-cheats-list* i))
(cost (-> cheat skill))
(unlock-func (the (function symbol) (-> cheat unlock-func value))))
(when (if (logtest? (-> *game-info* secrets) (game-secrets hero-mode))
(task-node-closed? (-> cheat avail-after-hero))
(task-node-closed? (-> cheat avail-after)))
(logior! (-> obj cheats-revealed) (-> cheat flag))
(when (>= (-> *game-info* skill-total) cost)
(logior! (-> obj cheats-purchased) (-> cheat flag))
(when (or (zero? unlock-func)
(not unlock-func)
(unlock-func))
(logior! (-> obj cheats-unlocked) (-> cheat flag)))))
(case (-> cheat can-toggle)
((#f)
(when (logtest? (-> obj cheats-unlocked) (-> cheat flag))
(logior! (-> obj cheats) (-> cheat flag)))
)
)))
0)
(defmethod update-music-log ((obj pc-settings-jak2))
"update the music log"
(dotimes (i (-> *music-player-tracks* length))
(when (or (logtest? (-> *game-info* secrets) (game-secrets hero-mode))
(task-node-closed? (-> *music-player-tracks* i avail-after)))
(set-bit (-> obj music-unlocked) i)
)
)
(true! (-> obj flava-unlocked 0)) ;; default always unlocked
(if (task-node-closed? (game-task-node city-red-gun-training-resolution)) (true! (-> obj flava-unlocked 1))) ;; gun
(if (task-node-closed? (game-task-node forest-scouts-resolution)) (true! (-> obj flava-unlocked 2))) ;; board
(if (task-node-closed? (game-task-node ruins-mech-resolution)) (true! (-> obj flava-unlocked 3))) ;; mech
(if (task-node-closed? (game-task-node city-oracle-introduction)) (true! (-> obj flava-unlocked 4))) ;; darkjak
(if (task-node-closed? (game-task-node city-vehicle-training-resolution)) (true! (-> obj flava-unlocked 5))) ;; pilot
0)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; file I/O
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun bit-array<-int64 ((arr bit-array) (start-offset int) (val int))
"starting from start-offset at arr, fill the next 64 bits of the array from an int64"
(let ((i start-offset)
(end-offset (min (+ start-offset 64) (-> arr length))))
(while (< i end-offset)
(if (nonzero? (logand val (ash 1 (- i start-offset))))
(set-bit arr i))
(1+! i)
)
val)
)
(defun int64<-bit-array ((arr bit-array) (start-offset int))
"starting from start-offset at arr, pack the next 64 bits into a single value and return it"
(let ((val 0)
(i start-offset)
(end-offset (min (+ start-offset 64) (-> arr length))))
(while (< i end-offset)
(if (get-bit arr i)
(logior! val (ash 1 (- i start-offset))))
(1+! i)
)
val)
)
(defmethod handle-input-settings ((obj pc-settings-jak2) (file file-stream))
"handle the text parsing input for the 'settings' group"
((method-of-type pc-settings handle-input-settings) obj file)
(case-str *pc-temp-string*
(("fast-airlock?") (set! (-> obj fast-airlock?) (file-stream-read-symbol file)))
(("fast-elevator?") (set! (-> obj fast-elevator?) (file-stream-read-symbol file)))
(("fast-progress?") (set! (-> obj fast-progress?) (file-stream-read-symbol file)))
(("smooth-minimap?") (set! (-> obj smooth-minimap?) (file-stream-read-symbol file)))
(("hires-clouds?") (set! (-> obj hires-clouds?) (file-stream-read-symbol file)))
(("text-language") (set! (-> obj text-language) (the-as pc-language (file-stream-read-int file))))
(("controller-led-status?") (set! (-> obj controller-led-status?) (file-stream-read-symbol file)))
(("speedrunner-mode-custom-bind") (set! (-> obj speedrunner-mode-custom-bind) (file-stream-read-int file)))
(("cheats") (set! (-> obj cheats) (the-as pc-cheats (file-stream-read-int file))))
(("cheats-revealed") (set! (-> obj cheats-revealed) (the-as pc-cheats (file-stream-read-int file))))
(("cheats-purchased") (set! (-> obj cheats-purchased) (the-as pc-cheats (file-stream-read-int file))))
(("cheats-unlocked") (set! (-> obj cheats-unlocked) (the-as pc-cheats (file-stream-read-int file))))
(("cheats-backup") (set! (-> obj cheats-backup) (the-as pc-cheats (file-stream-read-int file))))
(("music-unlocked")
(dotimes (i (/ (align64 (-> obj music-unlocked length)) 64))
(bit-array<-int64 (-> obj music-unlocked) (* i 64) (file-stream-read-int file))
)
)
(("flava-unlocked")
(dotimes (i 6)
(set! (-> obj flava-unlocked i) (file-stream-read-symbol file))
)
)
(("stats")
(dosettings (file)
(case-str *pc-temp-string*
(("kill-stats")
(initialize (-> obj stats kill-stats))
(dosettings (file)
(let ((enemy-stats (alloc-slot (-> obj stats kill-stats) (string->symbol *pc-temp-string*))))
(dosettings (file)
(let ((source (string->kill-source *pc-temp-string*))
(amount (file-stream-read-int file)))
(when (!= source (kill-stats-source unknown))
(set! (-> enemy-stats sources source) amount)
)
)
)
)
)
)
)
)
)
)
0)
(defmethod handle-output-settings ((obj pc-settings-jak2) (file file-stream))
"handle the text writing output for the 'settings' group"
((method-of-type pc-settings handle-output-settings) obj file)
(format file " (fast-airlock? ~A)~%" (-> obj fast-airlock?))
(format file " (fast-elevator? ~A)~%" (-> obj fast-elevator?))
(format file " (fast-progress? ~A)~%" (-> obj fast-progress?))
(format file " (smooth-minimap? ~A)~%" (-> obj smooth-minimap?))
(format file " (hires-clouds? ~A)~%" (-> obj hires-clouds?))
(format file " (text-language ~D)~%" (-> obj text-language))
(format file " (controller-led-status? ~A)~%" (-> obj controller-led-status?))
(format file " (speedrunner-mode-custom-bind ~D)~%" (-> obj speedrunner-mode-custom-bind))
(format file " (cheats #x~x)~%" (-> obj cheats))
(format file " (cheats-revealed #x~x)~%" (-> obj cheats-revealed))
(format file " (cheats-purchased #x~x)~%" (-> obj cheats-purchased))
(format file " (cheats-unlocked #x~x)~%" (-> obj cheats-unlocked))
(format file " (cheats-backup #x~x)~%" (-> obj cheats-backup))
(format file " (music-unlocked")
(dotimes (i (/ (align64 (-> obj music-unlocked length)) 64))
(format file " #x~x" (int64<-bit-array (-> obj music-unlocked) (* i 64)))
)
(format file ")~%")
(format file " (flava-unlocked")
(dotimes (i 6)
(format file " ~A" (-> obj flava-unlocked i))
)
(format file ")~%")
(format file " (stats~%")
(format file " (kill-stats~%")
(dotimes (i KILL_STATS_MAX_ENEMY_TYPES)
(when (-> obj stats kill-stats enemies i name)
(format file " (~A~%" (-> obj stats kill-stats enemies i name))
(dotimes (ii KILL_STATS_MAX_SOURCE)
(when (nonzero? (-> obj stats kill-stats enemies i sources ii))
(format file " (~A ~D)~%" (string->symbol (kill-source->string (the kill-stats-source ii))) (-> obj stats kill-stats enemies i sources ii))
))
(format file " )~%")
))
(format file " )~%")
(format file " )~%")
0)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; PC settings
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(define-once *pc-settings* (new 'global 'pc-settings-jak2))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; other
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defun draw-build-revision ()
(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.
(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)))
(clear *pc-encoded-temp-string*)
(clear *temp-string*)
(format *temp-string* "<COLOR_WHITE>~S" *pc-settings-built-sha*)
(pc-encode-utf8-string *temp-string* *pc-encoded-temp-string*)
(let ((font-ctx (new 'stack 'font-context *font-default-matrix* 2 403 0.0 (font-color default) (font-flags shadow kerning large))))
(set! (-> font-ctx scale) 0.325)
(draw-string-adv *pc-encoded-temp-string* buf font-ctx))))
(defun print-level-types ((lev level))
"print the level-type linked list for a level"
(format #t "print-level-types for ~A~%" (-> lev nickname))
(let ((cur-type (-> lev level-type)))
(while (and cur-type (nonzero? cur-type) (= type (-> cur-type type)))
(format #t "~A~%" cur-type)
(set! cur-type (the type (-> cur-type method-table 8))))
(format #t "~%"))
)
(defun-debug pc-cheat->string ((cheat pc-cheats))
(doenum (name val pc-cheats)
(if (= cheat val)
(return name))
)
"*unknown*")
(defun-debug print-cheat-status (out)
(dotimes (i (-> *pc-cheats-list* length))
(let ((flag (-> *pc-cheats-list* i flag)))
(cond
((logtest? (-> *pc-settings* cheats) flag) (format out " ~20S(#x~6x): enabled~%" (pc-cheat->string flag) flag))
((logtest? (-> *pc-settings* cheats-unlocked) flag) (format out " ~20S(#x~6x): unlocked~%" (pc-cheat->string flag) flag))
((logtest? (-> *pc-settings* cheats-purchased) flag) (format out " ~20S(#x~6x): purchased~%" (pc-cheat->string flag) flag))
((logtest? (-> *pc-settings* cheats-revealed) flag) (format out " ~20S(#x~6x): revealed~%" (pc-cheat->string flag) flag))
(else (format out " ~20S(#x~6x): locked~%" (pc-cheat->string flag) flag))
)
))
out)
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; process pools
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; the actor pool for PC processes! it has space for 4 processes, with 16K of space.
(define *pc-dead-pool* (new 'global 'dead-pool 4 (* 16 1024) "*pc-dead-pool*"))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;; progress adjustments
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defconstant CENTER_X (/ 512 2))
(defun adjust-game-x-centered ((origin int) (x float))
"given an x position ranging from [0, 512) adjust for aspect ratio towards the origin point specified
such that it does not get stretched away with the framebuffer"
(+ origin (* (- x origin) (-> *pc-settings* aspect-ratio-reciprocal))))
(defmacro adjust-game-x (x)
`(adjust-game-x-centered CENTER_X ,x))
(defmacro adjust-game-x-int (x-float)
`(the int (adjust-game-x-centered CENTER_X (the float ,x-float))))