// 触屏手柄按钮定义与默认布局(对应 web 版 settings.js 的 BUTTONS / DEFAULT_TOUCH)。 // 坐标 cx/cy 为相对舞台中心的百分比,size 为按钮边长(px)。 import 'package:iwbtc/src/core/save.dart'; class TouchBtn { final String key; final String label; final String shape; final String? label2; final double cx; final double cy; final double size; const TouchBtn(this.key, this.label, this.shape, this.cx, this.cy, this.size, [this.label2]); } const List BUTTONS = [ TouchBtn('left', '◀', 'round', 12, 80, 62), TouchBtn('right', '▶', 'round', 24, 80, 62), TouchBtn('jump', '跳', 'jump', 88, 84, 94), TouchBtn('shoot', '🔫', 'item', 79, 72, 54, '射击'), TouchBtn('down', '▼', 'item', 90, 72, 54, '下蹲'), TouchBtn('pause', '❚❚', 'sm', 81, 58, 42), TouchBtn('suicide', '↺', 'sm', 90, 58, 42), ]; const List> KEY_ACTIONS = >[ ['left', '左移'], ['right', '右移'], ['up', '上(上浮)'], ['jump', '跳跃'], ['shoot', '射击'], ['suicide', '自杀 / 重生'], ['restart', '整关重来'], ['pause', '暂停'], ]; Map defaultTouch() { final m = {}; for (final b in BUTTONS) { m[b.key] = { 'cx': b.cx, 'cy': b.cy, 'size': b.size, }; } return m; } Map defaultSettings() => { 'username': '', 'touch': defaultTouch(), 'keys': defaultBindings(), }; Map> defaultBindings() => >{ 'left': ['ArrowLeft', 'KeyA'], 'right': ['ArrowRight', 'KeyD'], 'up': ['ArrowUp', 'KeyW'], 'down': ['ArrowDown', 'KeyS'], 'jump': ['ShiftLeft', 'ShiftRight', 'Space', 'KeyZ', 'KeyN'], 'shoot': ['KeyX', 'KeyJ', 'KeyC', 'KeyM'], 'suicide': ['KeyR'], 'restart': ['Backspace'], 'pause': ['Escape', 'KeyP'], }; /// 读取/补全存档里的设置对象(保证 touch / keys 字段齐全)。 Map metaSettings() { final s = Save.settings(); if (s['username'] is! String) s['username'] = ''; if (s['touch'] is! Map) s['touch'] = defaultTouch(); if (s['keys'] is! Map) s['keys'] = defaultBindings(); return s; } String keyLabel(String code) { const Map M = { 'Space': '空格', 'ShiftLeft': 'Shift', 'ShiftRight': 'Shift', 'ControlLeft': 'Ctrl', 'ControlRight': 'Ctrl', 'AltLeft': 'Alt', 'AltRight': 'Alt', 'Tab': 'Tab', 'Backspace': '⌫', 'Escape': 'Esc', 'Enter': 'Enter', 'ArrowLeft': '←', 'ArrowRight': '→', 'ArrowUp': '↑', 'ArrowDown': '↓', 'MetaLeft': 'Win', 'MetaRight': 'Win', 'ContextMenu': '菜单', }; if (M.containsKey(code)) return M[code]!; if (code.startsWith('Key')) return code.substring(3); if (code.startsWith('Digit')) return code.substring(5); if (code.startsWith('Numpad')) return '小键' + code.substring(6); if (RegExp(r'^F\d{1,2}$').hasMatch(code)) return code; return code; }