125 lines
3.8 KiB
Dart
125 lines
3.8 KiB
Dart
// 输入:IWBT 经典键位(Shift 跳 / Z 跳 / X 射击 / R 重来)。
|
|
// 与 web 版 input.js 1:1 对应。键码沿用 Web 的 KeyboardEvent.code 字符串,
|
|
// 由 UI 层的键盘监听器把 Flutter 按键翻译成 code 后喂给 onKey()。
|
|
import 'dart:convert';
|
|
|
|
class Input {
|
|
/// 动作 -> 默认绑定的按键 code 列表。
|
|
static final Map<String, List<String>> bind = {
|
|
'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'], // IWBT 标准:自杀并回到最近存档点
|
|
'restart': ['Backspace'], // 整关从头再来(放弃所有存档点)
|
|
'pause': ['Escape', 'KeyP'],
|
|
'menu': ['KeyQ'],
|
|
};
|
|
|
|
static final List<String> actions =
|
|
bind.keys.toList(); // ['left','right',...]
|
|
|
|
static final Map<String, List<String>> _defaultBind = {
|
|
for (final e in bind.entries) e.key: List<String>.from(e.value),
|
|
};
|
|
|
|
static final Set<String> down = {}; // 当前按住的原始 code
|
|
static final Map<String, bool> now = {for (final a in bind.keys) a: false};
|
|
static final Map<String, bool> prev = {for (final a in bind.keys) a: false};
|
|
static final Map<String, bool> touch = {}; // 触屏虚拟按键 action -> bool
|
|
|
|
static bool enabled = true;
|
|
|
|
static bool held(String a) => now[a] ?? false;
|
|
static bool hit(String a) => (now[a] ?? false) && !(prev[a] ?? false);
|
|
static bool released(String a) => !(now[a] ?? false) && (prev[a] ?? false);
|
|
static bool raw(String c) => down.contains(c);
|
|
static bool anyHit() {
|
|
for (final k in now.keys) {
|
|
if (now[k]! && !(prev[k] ?? false)) return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static void clear() {
|
|
down.clear();
|
|
for (final k in now.keys) now[k] = false;
|
|
for (final k in prev.keys) prev[k] = false;
|
|
}
|
|
|
|
/// 每逻辑帧末尾调用。
|
|
static void endFrame() {
|
|
for (final k in now.keys) prev[k] = now[k]!;
|
|
}
|
|
|
|
static void _refresh() {
|
|
for (final act in bind.keys) {
|
|
bool v = false;
|
|
for (final c in bind[act]!) {
|
|
if (down.contains(c)) {
|
|
v = true;
|
|
break;
|
|
}
|
|
}
|
|
if (touch[act] ?? false) v = true;
|
|
now[act] = enabled ? v : false;
|
|
}
|
|
}
|
|
|
|
/// 原始键盘事件(Web code 字符串)。
|
|
static void onKey(String code, bool isDown) {
|
|
if (isDown) {
|
|
down.add(code);
|
|
} else {
|
|
down.remove(code);
|
|
}
|
|
_refresh();
|
|
}
|
|
|
|
// ---------------- 触屏虚拟按键 ----------------
|
|
static void setTouch(String action, bool on) {
|
|
touch.putIfAbsent(action, () => false);
|
|
if (touch[action] == on) return;
|
|
touch[action] = on;
|
|
_refresh();
|
|
}
|
|
|
|
static void clearTouch() {
|
|
bool any = false;
|
|
for (final k in touch.keys) {
|
|
if (touch[k]!) {
|
|
touch[k] = false;
|
|
any = true;
|
|
}
|
|
}
|
|
if (any) _refresh();
|
|
}
|
|
|
|
// ---------------- 键位自定义(设置界面调用)----------------
|
|
static Map<String, List<String>> defaultBindings() =>
|
|
{for (final e in bind.entries) e.key: List<String>.from(e.value)};
|
|
|
|
static Map<String, List<String>> snapshotBindings() =>
|
|
{for (final e in bind.entries) e.key: List<String>.from(e.value)};
|
|
|
|
static void applyBindings(Map<String, List<String>>? map) {
|
|
if (map == null) return;
|
|
for (final act in map.keys) {
|
|
if (bind.containsKey(act)) bind[act] = List<String>.from(map[act]!);
|
|
}
|
|
}
|
|
|
|
/// 持久化用:转 JSON 字符串。
|
|
static String toJson() => jsonEncode(bind);
|
|
static void fromJson(String? s) {
|
|
if (s == null) return;
|
|
try {
|
|
final m = (jsonDecode(s) as Map).cast<String, dynamic>();
|
|
final decoded = m.map((k, v) => MapEntry(k, List<String>.from(v as List)));
|
|
applyBindings(decoded);
|
|
} catch (_) {}
|
|
}
|
|
}
|