;;-*-Lisp-*- (in-package goal) (defmethod call-on-load ((this generic-progress-state-entry)) (when (and (nonzero? (-> this on-load)) (!= (-> this on-load) #f)) ((-> this on-load))) (none)) (defmethod call-get-value-fn ((this menu-generic-boolean-option)) (if (and (nonzero? (-> this get-value-fn)) (!= (-> this get-value-fn) #f)) ((-> this get-value-fn)) #f)) (defmethod call-on-confirm ((this menu-generic-boolean-option) (val symbol)) (when (and (nonzero? (-> this on-confirm)) (!= (-> this on-confirm) #f)) ((-> this on-confirm) val)) (none)) (defmethod call-get-item-index-fn ((this menu-generic-carousel-option)) (if (and (nonzero? (-> this get-item-index-fn)) (!= (-> this get-item-index-fn) #f)) ((-> this get-item-index-fn)) 0)) (defmethod call-on-confirm ((this menu-generic-carousel-option) (val int) (the-progress progress)) (when (and (nonzero? (-> this on-confirm)) (-> this on-confirm)) ((-> this on-confirm) val the-progress)) (none)) (defmethod call-on-confirm ((this menu-generic-button-option) (the-progress progress)) (when (and (nonzero? (-> this on-confirm)) (-> this on-confirm)) ((-> this on-confirm) the-progress)) (none)) (defmethod call-on-confirm ((this menu-generic-confirm-option)) (when (and (nonzero? (-> this on-confirm)) (!= (-> this on-confirm) #f)) ((-> this on-confirm))) (none)) (defmethod call-get-value-fn ((this menu-generic-slider-option)) (if (and (nonzero? (-> this get-value-fn)) (!= (-> this get-value-fn) #f)) ((-> this get-value-fn)) 0.0)) (defmethod call-on-confirm ((this menu-generic-slider-option) (val float)) (when (and (nonzero? (-> this on-confirm)) (!= (-> this on-confirm) #f)) ((-> this on-confirm) val)) (none)) (defmethod init! ((this progress-pc-generic-store)) (set! (-> this clear-screen?) #f) (set! (-> this keybind-select-time) 0) (set! (-> this current-highscore-page-index) 0) (none)) (defmethod call-on-confirm ((this menu-generic-details-confirm-entry)) (when (and (nonzero? (-> this on-confirm)) (!= (-> this on-confirm) #f)) ((-> this on-confirm))) (none)) (defmethod navigate! ((this progress-pc-generic-store) (progress progress) (target menu-option-list) (on-load (function none))) (when (< (-> this history-stack-index) 9) ;; hard-coded length ;; if this is the first history entry, we push the current page as well ;; if it's not, then we update the current entry before proceeding to the next page (if (has-history? this) (let ((stack-entry (-> this history-stack (-> this history-stack-index)))) (set! (-> stack-entry hover-index) (-> this current-menu-hover-index)) (set! (-> stack-entry scroll-index) (the int (-> this current-menu-scroll-index))) (set! (-> stack-entry progress-id) (-> progress current))) (let ((stack-entry (-> this history-stack (-> this history-stack-index)))) (set! (-> stack-entry ref) (-> progress current-options)) (set! (-> stack-entry on-load) on-load) (set! (-> stack-entry hover-index) (-> this current-menu-hover-index)) (set! (-> stack-entry scroll-index) (the int (-> this current-menu-scroll-index))) (set! (-> stack-entry progress-id) (-> progress current)))) ;; push the target history entry now (inc! (-> this history-stack-index)) ;; Create a new stack entry (let ((stack-entry (-> this history-stack (-> this history-stack-index)))) (set! (-> stack-entry ref) target) (set! (-> stack-entry on-load) on-load) (set! (-> stack-entry hover-index) 0) (set! (-> stack-entry progress-id) 'generic-menu)) ;; Update global state (set! (-> this clear-screen?) #t) (set! (-> progress next) 'generic-menu) (set! (-> progress selected-option) #f)) (none)) (defmethod back! ((this progress-pc-generic-store) (progress progress)) (set! (-> this history-stack-index) (max 0 (dec (-> this history-stack-index)))) (set! (-> this clear-screen?) #t) (set! (-> progress next) 'generic-menu) (set! (-> progress selected-option) #f) (none)) (defmethod has-history? ((this progress-pc-generic-store)) (> (-> this history-stack-index) 0)) (defmethod clear-history-if-empty! ((this progress-pc-generic-store)) "if we have no history, just reset the entry. This is so we don't preserve state after completely exiting the menu" (when (not (has-history? this)) (set! (-> this history-stack-index) 0) (set! (-> this clear-screen?) #f) (set! (-> this current-menu-hover-index) 0) (set! (-> this current-menu-scroll-index) 0.0) (let ((stack-entry (-> this history-stack (-> this history-stack-index)))) (set! (-> stack-entry hover-index) 0) (set! (-> stack-entry ref) #f) (set! (-> stack-entry on-load) #f) (set! (-> stack-entry progress-id) #f))) (none)) (defmethod clear-history! ((this progress-pc-generic-store)) (set! (-> this history-stack-index) 0) (set! (-> this clear-screen?) #t) (set! (-> this current-menu-hover-index) 0) (set! (-> this current-menu-scroll-index) 0.0) (none)) (defmethod on-mount! ((this menu-generic-option)) (set! (-> this mounted?) #t) (none)) ;; TODO - call the parent methods, when there is an elegant way to do so (not having to know the ID) (defmethod on-mount! ((this menu-generic-carousel-option)) (set! (-> this mounted?) #t) (set! (-> this item-index) 0) (none)) (defmethod on-mount! ((this menu-generic-scrolling-page)) (set! (-> this mounted?) #t) (set! (-> this selected-option-index) -1) (none)) (defmethod on-mount! ((this menu-generic-details-page)) (set! (-> this mounted?) #t) (set! (-> this selected-entry-index) -1) (none)) (defmethod on-mount! ((this menu-generic-confirm-option)) (set! (-> this mounted?) #t) (set! (-> this confirmed?) #f) (none)) (defun controller-keybind->input-index ((bind controller-keybind)) (case bind (((controller-keybind l-analog-up)) 1) (((controller-keybind l-analog-down)) 1) (((controller-keybind l-analog-left)) 0) (((controller-keybind l-analog-right)) 0) (((controller-keybind r-analog-up)) 3) (((controller-keybind r-analog-down)) 3) (((controller-keybind r-analog-left)) 2) (((controller-keybind r-analog-right)) 2) (else (the int bind)))) (defmethod on-mount! ((this menu-generic-details-keybind-entry)) (set! (-> this mounted?) #t) (set! (-> this bind-info port) 0) ;; always port 0 for now (set! (-> this bind-info device-type) (the int (-> this device-type))) (set! (-> this bind-info for-buttons?) #t) (set! (-> this bind-info analog-min-range?) #f) (set! (-> this bind-info input-idx) (controller-keybind->input-index (-> this keybind))) (case (-> this keybind) (((controller-keybind l-analog-up) (controller-keybind l-analog-down) (controller-keybind l-analog-left) (controller-keybind l-analog-right) (controller-keybind r-analog-up) (controller-keybind r-analog-down) (controller-keybind r-analog-left) (controller-keybind r-analog-right)) (set! (-> this bind-info for-buttons?) #f) (case (-> this keybind) (((controller-keybind l-analog-up) (controller-keybind l-analog-left) (controller-keybind r-analog-up) (controller-keybind r-analog-left)) (set! (-> this bind-info analog-min-range?) #t))))) (none)) ;; Progress Code Overrides (defmethod respond-to-cpad ((this progress)) (mc-get-slot-info 0 *progress-save-info*) ;; ND originally did this...called this every frame in the game options, looked like a hack ;; there's probably a way to consistently do it synchronously (load-level-text-files (the-as int (-> *pc-settings* text-language))) (when (-> this current-options) (let ((option-array (-> this current-options options)) (has-generic-history? (has-history? *progress-pc-generic-store*))) (when (and option-array (= (-> this menu-transition) 0.0)) (let ((in-generic-page? (and (= (-> option-array length) 1) (or (type? (-> option-array 0) menu-generic-scrolling-page) (type? (-> option-array 0) menu-generic-details-page))))) (cond ;; If it's a scrolling menu, we have to pull from it's list of items (in-generic-page? ;; update the scrolling list (respond-progress (the-as menu-option (-> option-array 0)) this (and (= (-> this menu-transition) 0.0) (-> this selected-option)))) ;; Do a bounds check to avoid crashing ((>= (-> this option-index) (-> option-array length)) (format 0 "respond-to-cpad:progress: Option index ~D out of bounds~%" (-> this option-index))) (else (respond-progress (the-as menu-option (-> option-array (-> this option-index))) this (and (= (-> this menu-transition) 0.0) (-> this selected-option))))) (cond ((-> this selected-option) (cond ((cpad-pressed? 0 confirm) (set! (-> this selected-option) #f) ) ((cpad-pressed? 0 triangle) (when (not in-generic-page?) (if (= (-> this current-options) *main-options*) (sound-play "window-contract") (sound-play "generic-beep"))) (set! (-> this selected-option) #f)))) (else (cond ((cpad-pressed? 0 up l-analog-up) (cond ((= (-> this current-options) *main-options*) (sound-play "ring-select")) ((!= (length option-array) 1) (sound-play "roll-over"))) ;; skip over hidden secrets menu (if (and (= *title-pc* (-> this current-options)) (not (memcard-unlocked-secrets? #f)) (= (-> this option-index) 4)) (set! (-> this option-index) 3)) (cond ((> (-> this want-option-index) 0) (set! (-> this want-option-index) -1)) ((< -2 (-> this want-option-index)) (+! (-> this want-option-index) -1)))) ((cpad-pressed? 0 down l-analog-down) (cond ((= (-> this current-options) *main-options*) (sound-play "ring-select")) ((!= (length option-array) 1) (sound-play "roll-over"))) ;; skip over hidden secrets menu (if (and (= *title-pc* (-> this current-options)) (not (memcard-unlocked-secrets? #f)) (= (-> this option-index) 2)) (set! (-> this option-index) 3)) (cond ((< (-> this want-option-index) 0) (set! (-> this want-option-index) 1)) ((< (-> this want-option-index) 2) (+! (-> this want-option-index) 1)))) ((cpad-pressed? 0 confirm) (cpad-clear! 0 confirm) (when (and (not in-generic-page?) (not (-> *progress-state* clear-screen))) (sound-play "generic-beep")) ;; don't "select" the page link (when (!= (-> this next) 'generic-menu) (set! (-> this selected-option) #t))) ((cpad-pressed? 0 triangle) ;; if we are within a generic managed menu option, handle the stack elsewhere ;; TODO - this likely has issues if we have a nested-non-generic menu, but deal with that use-case if it's ever ;; actually employed (when (and (not has-generic-history?) (can-go-back? this)) (cpad-clear! 0 triangle) (if (and (= (-> *progress-state* starting-state) 'main) (!= (-> this current-options) *main-options*)) (sound-play "window-contract") (sound-play "generic-beep")) (clear-history-if-empty! *progress-pc-generic-store*) (pop-state this)))))))))) (none)) ;; Component implementation ;; - input handling (defmethod respond-progress ((this menu-generic-scrolling-page) (progress progress) (selected? symbol)) "Handle progress menu navigation logic." (let ((selected-item? #f)) (if (= (-> this selected-option-index) -1) (cond ((or (cpad-pressed? 0 down l-analog-down) (and (cpad-hold? 0 down l-analog-down) (>= (- (current-time) (the-as int (-> this last-move))) (seconds 0.2)))) (set! (-> this last-move) (current-time)) (cond ((< (-> *progress-pc-generic-store* current-menu-hover-index) (dec (-> this menu-options length))) (+! (-> *progress-pc-generic-store* current-menu-hover-index) 1) (sound-play "roll-over")) (else (set! (-> *progress-pc-generic-store* current-menu-hover-index) 0) (sound-play "roll-over")))) ((or (cpad-pressed? 0 up l-analog-up) (and (cpad-hold? 0 up l-analog-up) (>= (- (current-time) (the-as int (-> this last-move))) (seconds 0.2)))) (set! (-> this last-move) (current-time)) (+! (-> *progress-pc-generic-store* current-menu-hover-index) -1) (sound-play "roll-over") (when (< (-> *progress-pc-generic-store* current-menu-hover-index) 0) (set! (-> *progress-pc-generic-store* current-menu-hover-index) (dec (-> this menu-options length))))) ((cpad-pressed? 0 confirm) (when (< (-> *progress-pc-generic-store* current-menu-hover-index) (-> this menu-options length)) (let ((menu-option (-> this menu-options (-> *progress-pc-generic-store* current-menu-hover-index)))) ;; if the option is disabled, return early (when (and (type? menu-option menu-generic-option) (nonzero? (-> (the-as menu-generic-option menu-option) should-disable?)) (!= (-> (the-as menu-generic-option menu-option) should-disable?) #f) ((-> (the-as menu-generic-option menu-option) should-disable?))) (sound-play "roll-over") (cpad-clear! 0 confirm) (return 0)) ;; ignore confirm if it's a link (cond ;; TODO the condition for menu-generic-to-resolutions-option shouldn't be required if it ;; followed the existing pattern of being a page that contains options, instead of a single option that is a page ;; There are already components that do a similar thing (see menu-generic-details-page) ((or (type? menu-option menu-generic-link-option) (type? menu-option menu-generic-to-resolutions-option)) (set! selected-item? #t)) (else (set! (-> this selected-option-index) (-> *progress-pc-generic-store* current-menu-hover-index)) (sound-play "generic-beep")))))) ((cpad-pressed? 0 triangle) ;; we are in a sub-page, time to go back (when (has-history? *progress-pc-generic-store*) (back! *progress-pc-generic-store* progress) (sound-play "generic-beep")))) ;; menu option already selected (cond ((cpad-pressed? 0 confirm) (set! (-> this selected-option-index) -1)) ((cpad-pressed? 0 triangle) (set! (-> this selected-option-index) -1)))) ;; propagate event to menu-option (when (< (-> *progress-pc-generic-store* current-menu-hover-index) (-> this menu-options length)) (respond-progress (-> this menu-options (-> *progress-pc-generic-store* current-menu-hover-index)) progress (or selected? selected-item?)))) 0) (defmethod respond-progress ((this menu-generic-boolean-option) (progress progress) (selected? symbol)) (if selected? (cond ((cpad-pressed? 0 left l-analog-left right l-analog-right) (if (-> this value) (set! (-> this value) #f) (set! (-> this value) #t)) (sound-play "generic-beep")) ((cpad-pressed? 0 confirm) (call-on-confirm this (-> this value)) (sound-play "generic-beep"))) (cond ((cpad-pressed? 0 confirm) ;; set the value, this is so we edit the component's state and not the actual underlying value ;; in other words, don't change the setting until the user has actually confirmed the change! (set! (-> this value) (call-get-value-fn this))))) 0) (defmethod respond-progress ((this menu-generic-carousel-option) (progress progress) (selected? symbol)) (if selected? (cond ((cpad-pressed? 0 left l-analog-left) (dec! (-> this item-index)) (when (< (-> this item-index) 0) (set! (-> this item-index) (dec (num-items this)))) (sound-play "generic-beep")) ((cpad-pressed? 0 right l-analog-right) (inc! (-> this item-index)) (when (>= (-> this item-index) (num-items this)) (set! (-> this item-index) 0)) (sound-play "generic-beep")) ((cpad-pressed? 0 confirm) (call-on-confirm this (-> this item-index) progress) (sound-play "generic-beep"))) (cond ((cpad-pressed? 0 confirm) ;; set the value, this is so we edit the component's state and not the actual underlying value ;; in other words, don't change the setting until the user has actually confirmed the change! (set! (-> this item-index) (call-get-item-index-fn this))))) 0) (defmethod respond-progress ((this menu-generic-link-option) (progress progress) (selected? symbol)) (when (and selected? (cpad-pressed? 0 confirm)) (navigate! *progress-pc-generic-store* progress (-> this target) (-> this on-load)) (set! (-> progress selected-option) #f) (sound-play "generic-beep")) 0) (defmethod respond-progress ((this menu-generic-button-option) (progress progress) (selected? symbol)) (cond ((cpad-pressed? 0 confirm) (cpad-clear! 0 confirm) (call-on-confirm this progress) (sound-play "generic-beep"))) 0) (defmethod respond-progress ((this menu-generic-confirm-option) (progress progress) (selected? symbol)) (if selected? (cond ((cpad-pressed? 0 left l-analog-left right l-analog-right) (if (-> this confirmed?) (set! (-> this confirmed?) #f) (set! (-> this confirmed?) #t)) (sound-play "generic-beep")) ((cpad-pressed? 0 confirm) (call-on-confirm this) (sound-play "generic-beep"))) (cond ((cpad-pressed? 0 confirm) (set! (-> this confirmed?) #f)))) 0) (defmethod respond-progress ((this menu-generic-slider-option) (progress progress) (selected? symbol)) (if selected? (cond ((and (> (-> this value) (-> this min-value)) (cpad-hold? 0 left l-analog-left)) ;; TODO - might not work well on higher frame-rates (set! (-> this value) (fmax (- (-> this value) (-> this step)) (-> this min-value))) (when (< (seconds 0.03) (- (current-time) (-> this last-sound-played))) (set! (-> this last-sound-played) (current-time)) (sound-play-by-name (static-sound-name "menu-slide") (new-sound-id) 512 0 0 (sound-group sfx) #t))) ((and (< (-> this value) (-> this max-value)) (cpad-hold? 0 right l-analog-right)) ;; TODO - might not work well on higher frame-rates (set! (-> this value) (fmin (+ (-> this value) (-> this step)) (-> this max-value))) (when (< (seconds 0.03) (- (current-time) (-> this last-sound-played))) (set! (-> this last-sound-played) (current-time)) (sound-play-by-name (static-sound-name "menu-slide") (new-sound-id) 512 0 0 (sound-group sfx) #t))) ((cpad-pressed? 0 confirm) (call-on-confirm this (-> this value)) (sound-play "generic-beep"))) (cond ((cpad-pressed? 0 confirm) ;; set the value, this is so we edit the component's state and not the actual underlying value ;; in other words, don't change the setting until the user has actually confirmed the change! (set! (-> this value) (call-get-value-fn this))))) 0) (defmethod respond-progress ((this menu-generic-details-page) (progress progress) (selected? symbol)) (if (= (-> this selected-entry-index) -1) (cond ((or (cpad-pressed? 0 down l-analog-down) (and (cpad-hold? 0 down l-analog-down) (>= (- (current-time) (the-as int (-> this last-move))) (seconds 0.2)))) (set! (-> this last-move) (current-time)) (cond ((< (-> *progress-pc-generic-store* current-menu-hover-index) (dec (-> this entries length))) (+! (-> *progress-pc-generic-store* current-menu-hover-index) 1) (sound-play "roll-over")) (else (set! (-> *progress-pc-generic-store* current-menu-hover-index) 0) (sound-play "roll-over")))) ((or (cpad-pressed? 0 up l-analog-up) (and (cpad-hold? 0 up l-analog-up) (>= (- (current-time) (the-as int (-> this last-move))) (seconds 0.2)))) (set! (-> this last-move) (current-time)) (+! (-> *progress-pc-generic-store* current-menu-hover-index) -1) (sound-play "roll-over") (when (< (-> *progress-pc-generic-store* current-menu-hover-index) 0) (set! (-> *progress-pc-generic-store* current-menu-hover-index) (dec (-> this entries length))))) ((cpad-pressed? 0 confirm) (when (< (-> *progress-pc-generic-store* current-menu-hover-index) (-> this entries length)) (set! (-> this selected-entry-index) (-> *progress-pc-generic-store* current-menu-hover-index)) (sound-play "generic-beep"))) ((cpad-pressed? 0 triangle) ;; we are in a sub-page, time to go back (when (has-history? *progress-pc-generic-store*) (back! *progress-pc-generic-store* progress) (sound-play "score-slide")))) ;; menu option already selected (cond ((cpad-pressed? 0 confirm) (set! (-> this selected-entry-index) -1)) ((cpad-pressed? 0 triangle) (set! (-> this selected-entry-index) -1)) ;; keybinds ((nonzero? (-> *progress-pc-generic-store* keybind-select-time)) ;; when the bind has been set, or it's expired (when (or (not (pc-waiting-for-bind?)) (>= (- (current-time) (the-as int (-> *progress-pc-generic-store* keybind-select-time))) (seconds 5.0))) (set! (-> *progress-pc-generic-store* keybind-select-time) 0) (set! (-> this selected-entry-index) -1) (set! (-> progress selected-option) #f) (cond ((not (pc-waiting-for-bind?)) (sound-play "generic-beep")) (else (pc-stop-waiting-for-bind!) (sound-play "roll-over"))))))) ;; propagate event to menu-option (when (< (-> *progress-pc-generic-store* current-menu-hover-index) (-> this entries length)) (respond-progress (-> this entries (-> *progress-pc-generic-store* current-menu-hover-index)) progress selected?)) 0) (defmethod respond-progress ((this menu-generic-details-keybind-entry) (progress progress) (selected? symbol)) (when (not selected?) (cond ((cpad-pressed? 0 confirm) (let ((bind-info (-> this bind-info))) (pc-set-waiting-for-bind! (-> bind-info device-type) (not (-> bind-info for-buttons?)) (-> bind-info analog-min-range?) (-> bind-info input-idx))) (set! (-> *progress-pc-generic-store* keybind-select-time) (current-time))))) 0) (defmethod respond-progress ((this menu-generic-details-confirm-entry) (progress progress) (selected? symbol)) (if selected? (cond ((cpad-pressed? 0 left l-analog-left right l-analog-right) (if (-> this confirmed?) (set! (-> this confirmed?) #f) (set! (-> this confirmed?) #t)) (sound-play "generic-beep")) ((cpad-pressed? 0 confirm) (call-on-confirm this) (sound-play "generic-beep"))) (cond ((cpad-pressed? 0 confirm) (set! (-> this confirmed?) #f)))) 0) ;; - rest of component logic (defmethod num-items ((this menu-generic-carousel-option)) "Get the number of items in the carousel" (if (and (nonzero? (-> this items)) (!= (-> this items) #f)) (-> this items length) (if (and (and (nonzero? (-> this get-max-size-fn)) (!= (-> this get-max-size-fn) #f)) (and (nonzero? (-> this get-item-label-fn)) (!= (-> this get-max-size-fn) #f))) ((-> this get-max-size-fn)) 0))) (defmethod get-item-label ((this menu-generic-carousel-option) (item-index int)) "Gets the string label of the currently choosen item, preferring the `items` array if it exists" (if (and (nonzero? (-> this items)) (!= (-> this items) #f)) (lookup-text! *common-text* (-> this items item-index) #f) (if (and (nonzero? (-> this get-item-label-fn)) (!= (-> this get-max-size-fn) #f)) ((-> this get-item-label-fn) item-index) "#ERROR"))) ;; TODO - it could be possible to map certain controllers to buttons (ie. dualshock controllers could have nice mappings instead of SDL named ones) (defmethod get-keybind-string ((this menu-generic-details-keybind-entry)) (case (-> this keybind) (((controller-keybind cross)) ;; the PS2 TRC says it should be pronounced "ecks", hence why every game that speaks the buttons out loud says X, plus at least 80% of people also already say X (i assume in part because of that). ;; the official spelling is probably "cross" (see PS2 development reference manuals), but that has less weight in the context of displaying the name to the user, I think? ;; "cross" seems to be mainly a UK thing (those weirdos) (if (language? uk-english) "~Y~22L<~Z~Y~27L*~Z~Y~1L>~Z~Y~23L[~Z~+26H Cross" "~Y~22L<~Z~Y~27L*~Z~Y~1L>~Z~Y~23L[~Z~+26H X")) (((controller-keybind square)) "~Y~22L<~Z~Y~24L#~Z~Y~1L>~Z~Y~23L[~Z~+26H S~+7Vq~-7Vuare") (((controller-keybind circle)) "~Y~22L<~Z~Y~25L@~Z~Y~1L>~Z~Y~23L[~Z~+26H Circle") (((controller-keybind triangle)) "~Y~22L<~Z~Y~26L;~Z~Y~1L>~Z~Y~23L[~Z~+26H Trian~+7Vg~-7Vle") (((controller-keybind dpad-up)) "~Y~0L\ca1~Z~3L~+17H~-13V\ca2~Z~0L~+17H~+14V\ca0~Z~0L~+32H\ca3~Z~+56H D-Pad U~+7Vp~-7V") (((controller-keybind dpad-down)) "~Y~0L\ca1~Z~0L~+17H~-13V\ca2~Z~3L~+17H~+14V\ca0~Z~0L~+32H\ca3~Z~+56H D-Pad Down") (((controller-keybind dpad-right)) "~Y~0L\ca1~Z~0L~+17H~-13V\ca2~Z~0L~+17H~+14V\ca0~Z~3L~+32H\ca3~Z~+56H D-Pad Ri~+7Vg~-7Vht") (((controller-keybind dpad-left)) "~Y~3L\ca1~Z~0L~+17H~-13V\ca2~Z~0L~+17H~+14V\ca0~Z~0L~+32H\ca3~Z~+56H D-Pad Left") (((controller-keybind l1)) "~Y~22L~-2H~-12V\ca6\ca7~Z~22L~-2H~+17V\cb0\cb1~Z~1L~+4H~+3V\c95~Z~+38H L1") (((controller-keybind l2)) "~Y~22L~-2H~-6V\ca8\ca9~Z~22L~-2H~+16V\cb2\cb3~Z~1L~+5H~-2V\c97~Z~+38H L2") (((controller-keybind l3)) "\c91 L3") (((controller-keybind r1)) "~Y~22L~-2H~-12V\ca6\ca7~Z~22L~-2H~+17V\cb0\cb1~Z~1L~+6H~+3V\c94~Z~+38H R1") (((controller-keybind r2)) "~Y~22L~-2H~-6V\ca8\ca9~Z~22L~-2H~+16V\cb2\cb3~Z~1L~+5H~-2V\c96~Z~+38H R2") (((controller-keybind r3)) "\c91 R3") (((controller-keybind select)) "\c92 Select") (((controller-keybind start)) "\c92 Start") (((controller-keybind l-analog-up)) "~Y~1L\c91~Z~-17V~6L\c98~Z~+26H Left Analo~+7Vg~-7V U~+7Vp~-7V") (((controller-keybind l-analog-down)) "~Y~1L\c91~Z~+15V~6L\c9c~Z~+26H Left Analo~+7Vg~-7V Down") (((controller-keybind l-analog-left)) "~Y~1L\c91~Z~-17H~6L\c9e~Z~+26H Left Analo~+7Vg~-7V Left") (((controller-keybind l-analog-right)) "~Y~1L\c91~Z~+17H~6L\ca4~Z~+26H Left Analo~+7Vg~-7V Ri~+7Vg~-7Vht") (((controller-keybind r-analog-up)) "~Y~1L\c91~Z~-17V~6L\c98~Z~+26H Ri~+7Vg~-7Vht Analo~+7Vg~-7V U~+7Vp~-7V") (((controller-keybind r-analog-down)) "~Y~1L\c91~Z~+15V~6L\c9c~Z~+26H Ri~+7Vg~-7Vht Analo~+7Vg~-7V Down") (((controller-keybind r-analog-left)) "~Y~1L\c91~Z~-17H~6L\c9e~Z~+26H Ri~+7Vg~-7Vht Analo~+7Vg~-7V Left") (((controller-keybind r-analog-right)) "~Y~1L\c91~Z~+17H~6L\ca4~Z~+26H Ri~+7Vg~-7Vht Analo~+7Vg~-7V Ri~+7Vg~-7Vht") (else "Unknown Bind")))