public/js/ui/gameLogic.js becomes a re-export barrel. The bodies move verbatim into public/js/ui/gameLogic/*.js (shared, phases, camera, watch, bonds, shop, placement, draft, enemies, format, shortcuts, settings, result, loadout, panel). The barrel re-exports the same 96 names in the same order. isObj and int gain export on shared.js so sibling modules can import them; they stay off the barrel. No importer changes.
50 lines
2.5 KiB
JavaScript
50 lines
2.5 KiB
JavaScript
// ui/gameLogic/shortcuts.js — in-match keyboard shortcuts. Re-exported from ../gameLogic.js.
|
|
|
|
|
|
// ---- keyboard ---------------------------------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Map a keydown to a game shortcut (R refresh, F freeze, D level-up, Q retreat, X sell, Space ready, Esc close).
|
|
* Space means ready even while a HUD button has focus (a mouse click leaves the shop card / 刷新 focused, and
|
|
* Space must not re-trigger it); the caller prevents the button's own activation. Enter still activates buttons.
|
|
* @param {{ key?: string, code?: string, ctrlKey?: boolean, metaKey?: boolean, altKey?: boolean, repeat?: boolean, target?: any }} e
|
|
* @returns {'refresh'|'freeze'|'levelUp'|'retreat'|'sell'|'ready'|'escape'|null}
|
|
*/
|
|
export function shortcutFor(e) {
|
|
if (!e || e.ctrlKey || e.metaKey || e.altKey) return null;
|
|
const t = e.target;
|
|
const tag = t && typeof t.tagName === 'string' ? t.tagName.toUpperCase() : '';
|
|
if (tag === 'INPUT' || tag === 'TEXTAREA' || tag === 'SELECT' || t?.isContentEditable) return null;
|
|
if (e.key === 'Escape') return 'escape';
|
|
if (e.repeat) return null;
|
|
const code = e.code || '';
|
|
const key = typeof e.key === 'string' ? e.key.toLowerCase() : '';
|
|
if (code === 'KeyR' || key === 'r') return 'refresh';
|
|
if (code === 'KeyF' || key === 'f') return 'freeze';
|
|
if (code === 'KeyD' || key === 'd') return 'levelUp';
|
|
if (code === 'KeyQ' || key === 'q') return 'retreat';
|
|
if (code === 'KeyX' || key === 'x') return 'sell';
|
|
if (code === 'Space' || key === ' ') return 'ready';
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Whether a press on the field closes the open detail card: a card opened from the field itself (an own piece — tap,
|
|
* right-click or long press — or a battle / teammate unit). Shop, reward, bond-member and intel (enemy) cards stay.
|
|
* @param {{ kind?: string }|null|undefined} detail
|
|
*/
|
|
export const closesOnFieldPress = (detail) => detail?.kind === 'piece' || detail?.kind === 'unit';
|
|
|
|
/**
|
|
* Whether an open overlay swallows a game shortcut: a modal / the guide own the keyboard (Esc included — they close
|
|
* themselves); the 本局信息 / 敌方情报 drawer is a dialog too — only Esc (it closes the drawer) passes, R / F / D / Space
|
|
* never act behind it.
|
|
* @param {'refresh'|'freeze'|'levelUp'|'retreat'|'sell'|'ready'|'escape'|null} act shortcutFor
|
|
* @param {{ modal?: boolean, drawer?: boolean }} open
|
|
*/
|
|
export function shortcutBlocked(act, { modal = false, drawer = false } = {}) {
|
|
if (!act) return true;
|
|
if (modal) return true;
|
|
return !!drawer && act !== 'escape';
|
|
}
|