made some changes to the UI, removed leaderboards. updated gamesaves
This commit is contained in:
+66
-8
@@ -33,6 +33,7 @@ export const INPUT_ACTIONS = [
|
||||
'targetParty3',
|
||||
'targetParty4',
|
||||
'targetParty5',
|
||||
'targetParty6',
|
||||
'toggleTargetGroup',
|
||||
'pause',
|
||||
] as const
|
||||
@@ -60,6 +61,7 @@ export const ACTION_LABELS: Record<InputAction, string> = {
|
||||
targetParty3: 'Target Party Member 3',
|
||||
targetParty4: 'Target Party Member 4',
|
||||
targetParty5: 'Target Party Member 5',
|
||||
targetParty6: 'Target Party Member 6',
|
||||
toggleTargetGroup: 'Switch Raid Target Group',
|
||||
pause: 'Pause Menu',
|
||||
}
|
||||
@@ -85,6 +87,7 @@ export const DEFAULT_BINDINGS: Record<InputDevice, InputBindings> = {
|
||||
targetParty3: 'F3',
|
||||
targetParty4: 'F4',
|
||||
targetParty5: 'F5',
|
||||
targetParty6: 'F6',
|
||||
toggleTargetGroup: 'Tab',
|
||||
pause: 'Escape',
|
||||
},
|
||||
@@ -108,6 +111,7 @@ export const DEFAULT_BINDINGS: Record<InputDevice, InputBindings> = {
|
||||
targetParty3: 'Button15',
|
||||
targetParty4: 'Button13',
|
||||
targetParty5: 'Button4',
|
||||
targetParty6: 'Button11',
|
||||
toggleTargetGroup: 'Button6',
|
||||
pause: 'Button9',
|
||||
},
|
||||
@@ -202,13 +206,19 @@ function bindingGroup(action: InputAction) {
|
||||
}
|
||||
|
||||
function isVisible(element: HTMLElement) {
|
||||
if (element.hidden || element.getAttribute('aria-hidden') === 'true') return false
|
||||
return element.getClientRects().length > 0
|
||||
}
|
||||
|
||||
function focusableElements() {
|
||||
const keyboard = document.querySelector<HTMLElement>('.controller-keyboard')
|
||||
const pauseMenu = document.querySelector<HTMLElement>('.pause-screen')
|
||||
const scope: ParentNode = keyboard ?? pauseMenu ?? document
|
||||
const dialog = Array.from(
|
||||
document.querySelectorAll<HTMLElement>(
|
||||
'.result-screen, .binding-capture, .dual-startup-prompt',
|
||||
),
|
||||
).find(isVisible)
|
||||
const scope: ParentNode = keyboard ?? pauseMenu ?? dialog ?? document
|
||||
return Array.from(
|
||||
scope.querySelectorAll<HTMLElement>(
|
||||
'button:not(:disabled), input:not(:disabled), select:not(:disabled), textarea:not(:disabled), [tabindex]:not([tabindex="-1"])',
|
||||
@@ -256,7 +266,22 @@ function moveFocus(action: InputAction) {
|
||||
const next = ranked[0]?.candidate
|
||||
if (!next) return
|
||||
next.focus({ preventScroll: true })
|
||||
next.scrollIntoView({ behavior: 'smooth', block: 'nearest', inline: 'nearest' })
|
||||
}
|
||||
|
||||
function hasUiOverlay() {
|
||||
return Array.from(
|
||||
document.querySelectorAll<HTMLElement>(
|
||||
'.pause-screen, .result-screen, .binding-capture, .dual-startup-prompt, .controller-keyboard',
|
||||
),
|
||||
).some(isVisible)
|
||||
}
|
||||
|
||||
function isCombatTargetAction(action: InputAction) {
|
||||
return action.startsWith('navigate')
|
||||
|| action.startsWith('targetParty')
|
||||
|| action === 'previousTarget'
|
||||
|| action === 'nextTarget'
|
||||
|| action === 'toggleTargetGroup'
|
||||
}
|
||||
|
||||
const BUTTON_LABELS: Record<number, string> = {
|
||||
@@ -372,6 +397,7 @@ export function InputProvider({ children }: { children: ReactNode }) {
|
||||
const keyboardInputRef = useRef(keyboardInput)
|
||||
const previousTokensRef = useRef(new Set<string>())
|
||||
const repeatRef = useRef<Record<string, number>>({})
|
||||
const lastCombatNavigationRef = useRef(0)
|
||||
|
||||
useEffect(() => {
|
||||
bindingsRef.current = bindings
|
||||
@@ -416,18 +442,29 @@ export function InputProvider({ children }: { children: ReactNode }) {
|
||||
}, [])
|
||||
|
||||
const dispatchAction = useCallback((action: InputAction, device: InputDevice) => {
|
||||
const uiOverlay = hasUiOverlay()
|
||||
const combatActive = Boolean(document.querySelector('[data-combat-active="true"]'))
|
||||
if (combatActive && !uiOverlay && isCombatTargetAction(action)) {
|
||||
const now = performance.now()
|
||||
if (now - lastCombatNavigationRef.current < 125) return
|
||||
lastCombatNavigationRef.current = now
|
||||
}
|
||||
|
||||
setLastDevice(device)
|
||||
document.documentElement.dataset.inputDevice = device
|
||||
const combatActive = Boolean(document.querySelector('[data-combat-active="true"]'))
|
||||
|
||||
if (action.startsWith('navigate')) {
|
||||
if (!combatActive) moveFocus(action)
|
||||
if (uiOverlay || !combatActive) moveFocus(action)
|
||||
} else if (action === 'confirm') {
|
||||
const active = document.activeElement
|
||||
if (isTextInput(active)) {
|
||||
setKeyboardInput(active)
|
||||
window.requestAnimationFrame(() => focusFirstControl())
|
||||
} else if (active instanceof HTMLElement && active.matches('button, [role="button"]')) {
|
||||
} else if (
|
||||
active instanceof HTMLElement
|
||||
&& active.matches('button:not(:disabled), [role="button"]')
|
||||
&& isVisible(active)
|
||||
) {
|
||||
active.click()
|
||||
} else {
|
||||
focusFirstControl()
|
||||
@@ -435,7 +472,7 @@ export function InputProvider({ children }: { children: ReactNode }) {
|
||||
} else if (action === 'back') {
|
||||
if (keyboardInputRef.current) {
|
||||
closeKeyboard()
|
||||
} else if (!combatActive) {
|
||||
} else if (uiOverlay || !combatActive) {
|
||||
const backButton = Array.from(
|
||||
document.querySelectorAll<HTMLButtonElement>('.back-button:not(:disabled)'),
|
||||
).find(isVisible)
|
||||
@@ -458,18 +495,28 @@ export function InputProvider({ children }: { children: ReactNode }) {
|
||||
const combatActive = Boolean(
|
||||
document.querySelector('[data-combat-active="true"]'),
|
||||
)
|
||||
const uiOverlay = hasUiOverlay()
|
||||
const menuDpadActions: Partial<Record<string, InputAction>> = {
|
||||
Button12: 'navigateUp',
|
||||
Button13: 'navigateDown',
|
||||
Button14: 'navigateLeft',
|
||||
Button15: 'navigateRight',
|
||||
}
|
||||
const uiPriority = [
|
||||
'navigateUp',
|
||||
'navigateDown',
|
||||
'navigateLeft',
|
||||
'navigateRight',
|
||||
'confirm',
|
||||
'back',
|
||||
] satisfies InputAction[]
|
||||
const directTargetActions = [
|
||||
'targetParty1',
|
||||
'targetParty2',
|
||||
'targetParty3',
|
||||
'targetParty4',
|
||||
'targetParty5',
|
||||
'targetParty6',
|
||||
'toggleTargetGroup',
|
||||
] satisfies InputAction[]
|
||||
const combatPriority = [
|
||||
@@ -487,7 +534,11 @@ export function InputProvider({ children }: { children: ReactNode }) {
|
||||
'navigateLeft',
|
||||
'navigateRight',
|
||||
] satisfies InputAction[]
|
||||
const action = combatActive && preferencesRef.current.directPartyTargeting
|
||||
const action = menuDpadActions[token] && (!combatActive || uiOverlay)
|
||||
? menuDpadActions[token]
|
||||
: uiOverlay
|
||||
? uiPriority.find((candidate) => bindingsRef.current.controller[candidate] === token)
|
||||
: combatActive && preferencesRef.current.directPartyTargeting
|
||||
? [...directTargetActions, ...combatPriority].find(
|
||||
(candidate) => bindingsRef.current.controller[candidate] === token,
|
||||
)
|
||||
@@ -541,8 +592,13 @@ export function InputProvider({ children }: { children: ReactNode }) {
|
||||
const ensureFocus = () => {
|
||||
const combatActive = document.querySelector('[data-combat-active="true"]')
|
||||
if (combatActive) return
|
||||
const candidates = focusableElements()
|
||||
const active = document.activeElement
|
||||
const activeIsUsable = active instanceof HTMLElement
|
||||
&& candidates.includes(active)
|
||||
&& isVisible(active)
|
||||
if (
|
||||
document.activeElement === document.body
|
||||
(!activeIsUsable || document.activeElement === document.body)
|
||||
&& !keyboardInputRef.current
|
||||
&& !captureRef.current
|
||||
) {
|
||||
@@ -553,6 +609,8 @@ export function InputProvider({ children }: { children: ReactNode }) {
|
||||
window.requestAnimationFrame(ensureFocus)
|
||||
})
|
||||
observer.observe(document.getElementById('root') ?? document.body, {
|
||||
attributes: true,
|
||||
attributeFilter: ['aria-hidden', 'class', 'disabled', 'hidden', 'style'],
|
||||
childList: true,
|
||||
subtree: true,
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user