made some changes to the UI, removed leaderboards. updated gamesaves
This commit is contained in:
+155
-126
@@ -24,6 +24,7 @@ import {
|
||||
|
||||
export function SettingsScreen({ onBack }: { onBack: () => void }) {
|
||||
const [device, setDevice] = useState<InputDevice>('controller')
|
||||
const [settingsTab, setSettingsTab] = useState<'display' | 'input' | 'bindings'>('display')
|
||||
const [displayMessage, setDisplayMessage] = useState('')
|
||||
const [androidDisplays, setAndroidDisplays] = useState<AndroidDisplay[]>([])
|
||||
const {
|
||||
@@ -50,6 +51,7 @@ export function SettingsScreen({ onBack }: { onBack: () => void }) {
|
||||
'targetParty3',
|
||||
'targetParty4',
|
||||
'targetParty5',
|
||||
'targetParty6',
|
||||
'toggleTargetGroup',
|
||||
])
|
||||
const visibleActions = INPUT_ACTIONS.filter((action) => (
|
||||
@@ -95,138 +97,165 @@ export function SettingsScreen({ onBack }: { onBack: () => void }) {
|
||||
<button className="back-button" onClick={onBack} type="button">Back</button>
|
||||
</div>
|
||||
|
||||
<section className="dual-screen-settings">
|
||||
<div>
|
||||
<p className="eyebrow">Display</p>
|
||||
<h2>AYN Thor Dual-Screen Mode</h2>
|
||||
<p>
|
||||
The upper display shows enemy and party health. The lower display
|
||||
keeps targeting, resources, skills, and cooldowns.
|
||||
</p>
|
||||
</div>
|
||||
<div className="dual-screen-actions">
|
||||
<nav className="settings-tabs" role="tablist" aria-label="Settings sections">
|
||||
{([
|
||||
{ key: 'display', label: 'Display' },
|
||||
{ key: 'input', label: 'Input' },
|
||||
{ key: 'bindings', label: 'Bindings' },
|
||||
] as const).map((tab) => (
|
||||
<button
|
||||
className={dualScreenEnabled ? 'selected' : ''}
|
||||
onClick={() => {
|
||||
setDualScreenEnabled(!dualScreenEnabled)
|
||||
setDisplayMessage('')
|
||||
}}
|
||||
aria-selected={settingsTab === tab.key}
|
||||
className={settingsTab === tab.key ? 'selected' : ''}
|
||||
key={tab.key}
|
||||
onClick={() => setSettingsTab(tab.key)}
|
||||
role="tab"
|
||||
type="button"
|
||||
>
|
||||
{dualScreenEnabled ? 'Dual-Screen Enabled' : 'Enable Dual-Screen'}
|
||||
</button>
|
||||
<button onClick={launchTopDisplay} type="button">
|
||||
{topDisplayConnected ? 'Companion Connected' : 'Open Companion Display'}
|
||||
</button>
|
||||
</div>
|
||||
<small>
|
||||
{displayMessage || (
|
||||
topDisplayConnected
|
||||
? 'The companion display is connected and receiving live combat data.'
|
||||
: 'Open the companion display before starting combat.'
|
||||
)}
|
||||
</small>
|
||||
{nativeDualScreen && androidDisplays.length > 0 && (
|
||||
<div className="android-display-list">
|
||||
{androidDisplays.map((display) => (
|
||||
<span key={display.id}>
|
||||
<strong>{display.isCurrent ? 'Current' : 'Secondary'} #{display.id}</strong>
|
||||
{display.width}×{display.height} at {Math.round(display.refreshRate)} Hz
|
||||
{display.isPresentation ? ' - Presentation' : ''}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
|
||||
<div className="settings-heading">
|
||||
<div>
|
||||
<p className="eyebrow">Input</p>
|
||||
<h2>Keybindings</h2>
|
||||
</div>
|
||||
<p>Select an action, then press the new key or controller control.</p>
|
||||
</div>
|
||||
|
||||
<section className="controller-preferences">
|
||||
<div>
|
||||
<p className="eyebrow">Targeting</p>
|
||||
<h3>Direct Party Keybinds</h3>
|
||||
<p>
|
||||
Assign party slots directly. In raids, use the group-switch binding
|
||||
to alternate between members 1-5 and 6-10.
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
aria-pressed={directPartyTargeting}
|
||||
className={directPartyTargeting ? 'selected' : ''}
|
||||
onClick={() => setDirectPartyTargeting(!directPartyTargeting)}
|
||||
type="button"
|
||||
>
|
||||
{directPartyTargeting ? 'Direct Targeting On' : 'Direct Targeting Off'}
|
||||
</button>
|
||||
<div className="controller-icon-options">
|
||||
<span>Controller Icons</span>
|
||||
{(['xbox', 'playstation', 'nintendo'] as const).map((style) => (
|
||||
<button
|
||||
aria-pressed={controllerIconStyle === style}
|
||||
className={controllerIconStyle === style ? 'selected' : ''}
|
||||
key={style}
|
||||
onClick={() => setControllerIconStyle(style)}
|
||||
type="button"
|
||||
>
|
||||
<ControllerStylePreview iconStyle={style} />
|
||||
<span className="controller-style-name">{CONTROLLER_STYLE_LABELS[style]}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div className="binding-tabs">
|
||||
<button
|
||||
className={device === 'controller' ? 'selected' : ''}
|
||||
onClick={() => setDevice('controller')}
|
||||
type="button"
|
||||
>
|
||||
Controller
|
||||
</button>
|
||||
<button
|
||||
className={device === 'pc' ? 'selected' : ''}
|
||||
onClick={() => setDevice('pc')}
|
||||
type="button"
|
||||
>
|
||||
PC
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="binding-list">
|
||||
{visibleActions.map((action) => (
|
||||
<button
|
||||
className={capture?.device === device && capture.action === action ? 'listening' : ''}
|
||||
key={action}
|
||||
onClick={() => beginCapture(device, action)}
|
||||
type="button"
|
||||
>
|
||||
<span>{ACTION_LABELS[action]}</span>
|
||||
<kbd>
|
||||
{capture?.device === device && capture.action === action
|
||||
? 'Press a control...'
|
||||
: (
|
||||
<ControllerBindingLabel
|
||||
binding={bindings[device][action]}
|
||||
iconStyle={controllerIconStyle}
|
||||
/>
|
||||
)}
|
||||
</kbd>
|
||||
{tab.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<footer className="settings-footer">
|
||||
<span>Bindings are saved automatically on this device.</span>
|
||||
<button className="text-button" onClick={() => resetBindings(device)} type="button">
|
||||
Reset {device === 'pc' ? 'PC' : 'Controller'} Defaults
|
||||
</button>
|
||||
</footer>
|
||||
{settingsTab === 'display' && (
|
||||
<section className="dual-screen-settings settings-tab-panel">
|
||||
<div>
|
||||
<p className="eyebrow">Display</p>
|
||||
<h2>AYN Thor Dual-Screen Mode</h2>
|
||||
<p>
|
||||
The upper display shows enemy and party health. The lower display
|
||||
keeps targeting, resources, skills, and cooldowns.
|
||||
</p>
|
||||
</div>
|
||||
<div className="dual-screen-actions">
|
||||
<button
|
||||
className={dualScreenEnabled ? 'selected' : ''}
|
||||
onClick={() => {
|
||||
setDualScreenEnabled(!dualScreenEnabled)
|
||||
setDisplayMessage('')
|
||||
}}
|
||||
type="button"
|
||||
>
|
||||
{dualScreenEnabled ? 'Dual-Screen Enabled' : 'Enable Dual-Screen'}
|
||||
</button>
|
||||
<button onClick={launchTopDisplay} type="button">
|
||||
{topDisplayConnected ? 'Companion Connected' : 'Open Companion Display'}
|
||||
</button>
|
||||
</div>
|
||||
<small>
|
||||
{displayMessage || (
|
||||
topDisplayConnected
|
||||
? 'The companion display is connected and receiving live combat data.'
|
||||
: 'Open the companion display before starting combat.'
|
||||
)}
|
||||
</small>
|
||||
{nativeDualScreen && androidDisplays.length > 0 && (
|
||||
<div className="android-display-list">
|
||||
{androidDisplays.map((display) => (
|
||||
<span key={display.id}>
|
||||
<strong>{display.isCurrent ? 'Current' : 'Secondary'} #{display.id}</strong>
|
||||
{display.width}x{display.height} at {Math.round(display.refreshRate)} Hz
|
||||
{display.isPresentation ? ' - Presentation' : ''}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
)}
|
||||
|
||||
{settingsTab === 'input' && (
|
||||
<section className="controller-preferences settings-tab-panel">
|
||||
<div>
|
||||
<p className="eyebrow">Targeting</p>
|
||||
<h3>Direct Party Keybinds</h3>
|
||||
<p>
|
||||
Assign party slots directly. In raids, use the group-switch binding
|
||||
to alternate between members 1-6, 7-12, and 13-18.
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
aria-pressed={directPartyTargeting}
|
||||
className={directPartyTargeting ? 'selected' : ''}
|
||||
onClick={() => setDirectPartyTargeting(!directPartyTargeting)}
|
||||
type="button"
|
||||
>
|
||||
{directPartyTargeting ? 'Direct Targeting On' : 'Direct Targeting Off'}
|
||||
</button>
|
||||
<div className="controller-icon-options">
|
||||
<span>Controller Icons</span>
|
||||
{(['xbox', 'playstation', 'nintendo'] as const).map((style) => (
|
||||
<button
|
||||
aria-pressed={controllerIconStyle === style}
|
||||
className={controllerIconStyle === style ? 'selected' : ''}
|
||||
key={style}
|
||||
onClick={() => setControllerIconStyle(style)}
|
||||
type="button"
|
||||
>
|
||||
<ControllerStylePreview iconStyle={style} />
|
||||
<span className="controller-style-name">{CONTROLLER_STYLE_LABELS[style]}</span>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{settingsTab === 'bindings' && (
|
||||
<section className="settings-bindings-panel settings-tab-panel">
|
||||
<div className="settings-heading">
|
||||
<div>
|
||||
<p className="eyebrow">Input</p>
|
||||
<h2>Keybindings</h2>
|
||||
</div>
|
||||
<p>Select an action, then press the new key or controller control.</p>
|
||||
</div>
|
||||
|
||||
<div className="binding-tabs">
|
||||
<button
|
||||
className={device === 'controller' ? 'selected' : ''}
|
||||
onClick={() => setDevice('controller')}
|
||||
type="button"
|
||||
>
|
||||
Controller
|
||||
</button>
|
||||
<button
|
||||
className={device === 'pc' ? 'selected' : ''}
|
||||
onClick={() => setDevice('pc')}
|
||||
type="button"
|
||||
>
|
||||
PC
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="binding-list">
|
||||
{visibleActions.map((action) => (
|
||||
<button
|
||||
className={capture?.device === device && capture.action === action ? 'listening' : ''}
|
||||
key={action}
|
||||
onClick={() => beginCapture(device, action)}
|
||||
type="button"
|
||||
>
|
||||
<span>{ACTION_LABELS[action]}</span>
|
||||
<kbd>
|
||||
{capture?.device === device && capture.action === action
|
||||
? 'Press a control...'
|
||||
: (
|
||||
<ControllerBindingLabel
|
||||
binding={bindings[device][action]}
|
||||
iconStyle={controllerIconStyle}
|
||||
/>
|
||||
)}
|
||||
</kbd>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<footer className="settings-footer">
|
||||
<span>Bindings are saved automatically on this device.</span>
|
||||
<button className="text-button" onClick={() => resetBindings(device)} type="button">
|
||||
Reset {device === 'pc' ? 'PC' : 'Controller'} Defaults
|
||||
</button>
|
||||
</footer>
|
||||
</section>
|
||||
)}
|
||||
|
||||
{capture && (
|
||||
<div className="binding-capture" role="dialog" aria-modal="true">
|
||||
|
||||
Reference in New Issue
Block a user