Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 20 additions & 9 deletions common/configs/keybind_catalog.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
"edit_prev_word",
"edit_return",
"losradar",
"luaui selector",
"movereset",
"moverotate",
"moveslow",
Expand Down Expand Up @@ -277,11 +276,13 @@
},
{
"action": "repeat 0",
"label": "actions.unitStates.repeatOff"
"label": "actions.unitStates.repeatOff",
"description": "commands.repeat_tooltip"
},
{
"action": "repeat 1",
"label": "actions.unitStates.repeatOn"
"label": "actions.unitStates.repeatOn",
"description": "commands.repeat_tooltip"
},
{
"prefix": "trajectory_toggle ",
Expand All @@ -294,27 +295,33 @@
},
{
"action": "firestate 0",
"label": "actions.unitStates.fireHold"
"label": "actions.unitStates.fireHold",
"description": "commands.firestate_hold_fire_descr"
},
{
"action": "firestate 1",
"label": "actions.unitStates.fireReturn"
"label": "actions.unitStates.fireReturn",
"description": "commands.firestate_return_fire_descr"
},
{
"action": "firestate 2",
"label": "actions.unitStates.fireAtWill"
"label": "actions.unitStates.fireAtWill",
"description": "commands.firestate_fire_at_will_descr"
},
{
"action": "movestate 0",
"label": "actions.unitStates.moveHold"
"label": "actions.unitStates.moveHold",
"description": "commands.movestate_tooltip"
},
{
"action": "movestate 1",
"label": "actions.unitStates.moveManeuver"
"label": "actions.unitStates.moveManeuver",
"description": "commands.movestate_tooltip"
},
{
"action": "movestate 2",
"label": "actions.unitStates.moveRoam"
"label": "actions.unitStates.moveRoam",
"description": "commands.movestate_tooltip"
}
]
},
Expand Down Expand Up @@ -707,6 +714,10 @@
"action": "options",
"label": "actions.interfaceDisplay.settings"
},
{
"action": "luaui selector",
"label": "actions.interfaceDisplay.widgetSelector"
},
{
"action": "hideinterface",
"label": "actions.cameraModes.interface"
Expand Down
1 change: 1 addition & 0 deletions common/configs/keybind_catalog.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"properties": {
"action": { "type": "string", "description": "Bind command (command + space-separated args), exactly as passed to /bind and reported by GetKeyBindings." },
"label": { "type": "string", "description": "i18n key for the display label." },
"description": { "type": "string", "description": "i18n key for a sentence saying what the action does, for a tooltip. Optional: without one a surface may fall back to the command card's tooltip (commands.<name>_tooltip) or the engine's command description (cmd.<command>)." },
"alwaysModifier": { "type": "string", "enum": ["any", "shift"], "description": "Modifier this action always tolerates, so a surface neither shows it nor lets the player pick it. \"any\" binds with the engine's Any+ qualifier and fires whatever is held. \"shift\" has no engine equivalent, so the binding is written twice, bare and Shift+, and both halves move together. Fixed per action rather than chosen." }
}
},
Expand Down
4 changes: 4 additions & 0 deletions common/configs/keybind_defaults.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
"profiles": [
{
"name": "Grid",
"description": "ui.keybinds.presets.grid",
"binds": [
{
"keyset": "esc",
Expand Down Expand Up @@ -1337,6 +1338,7 @@
},
{
"name": "Grid (60% Keyboard)",
"description": "ui.keybinds.presets.grid60",
"binds": [
{
"keyset": "esc",
Expand Down Expand Up @@ -2582,6 +2584,7 @@
},
{
"name": "Legacy",
"description": "ui.keybinds.presets.legacy",
"binds": [
{
"keyset": "esc",
Expand Down Expand Up @@ -4367,6 +4370,7 @@
},
{
"name": "Legacy (60% Keyboard)",
"description": "ui.keybinds.presets.legacy60",
"binds": [
{
"keyset": "esc",
Expand Down
5 changes: 5 additions & 0 deletions common/configs/keybind_defaults.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
"minLength": 1,
"description": "Display name, and the id a surface stores as the active selection. Unique across both lists."
},
"description": {
"type": "string",
"minLength": 1,
"description": "i18n key for a sentence or two saying what the profile is for, shown where a surface lets the player pick one. A surface without that translation shows the key's own text."
},
"fakeMeta": {
"type": "string",
"description": "Key to treat as the Meta modifier, if the profile wants one."
Expand Down
27 changes: 21 additions & 6 deletions common/configs/keybinds.README.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,20 @@ shape as the shipped ones plus an `active` field naming the selected profile. Th
is per-install rather than shared, but its format is the contract - a surface that can
read one can read the other.

A player's profile carries `basedOn`, the name of the shipped profile it descends from:
recorded when it was forked or duplicated, and otherwise (imported, or made before the
field existed, or naming a profile that no longer ships) inferred on load as the shipped
profile it differs from on the fewest actions, and written back. That is what lets a
surface say which keys the player changed and what the default was.

A shipped profile may carry `description`, an i18n key for a sentence saying what the
profile is for, shown wherever a surface lets the player pick one.

A profile travels as text in the bind-file form the engine loads, headed by a
`// keybind editor profile: <name>` comment: that is what the in-game Export copies to
the clipboard and what Import reads back, and the same text a player would put in
`uikeys.txt` by hand.

- **Which profile are we on?** Read `active` from the player's profile store. If it names
nothing that exists in either file, fall back to the first shipped profile.
- **Apply a profile.** Write its binds out as `bind <keyset> <action>` lines (plus a
Expand Down Expand Up @@ -153,9 +167,10 @@ three namespaces above and none of that.
- A widget/mod action-declaration API, so widgets register their own bindable actions
(with label + category + description) into the catalog at runtime instead of only being
editable when already bound.
- Command descriptions / tooltips. The engine ships per-command descriptions in the shared
`cmd.*` i18n namespace (in `interface.json`, localized like everything else), so a future
iteration can show them by resolving `cmd.<command>` (falling back to `cmd.<command>._description`
for the few structured commands, and `cmd.luarules.<command>` for gadget commands) at display
time - no catalog change needed, since the catalog already carries the command per row. Widget/mod
actions have no `cmd.*` entry, so their descriptions depend on the declaration API above.
- Command descriptions for every action. A catalog item may carry `description`, an i18n
key for a tooltip sentence; without one the in-game editor falls back to the command
card's tooltip (`commands.<name>_tooltip`, for a row labelled `commands.<name>`) and then
to the engine's command description (`cmd.<command>`, `cmd.<command>._description` for the
structured ones, `cmd.luarules.<command>` for gadget commands). Roughly a third of the
catalog still has none of those. Widget/mod actions have no `cmd.*` entry, so their
descriptions depend on the declaration API above.
3 changes: 2 additions & 1 deletion language/en/commands.json
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,8 @@
"unitStats": "Unit stats",
"saveGame": "Save game",
"screenshot": "Take screenshot",
"firingRange": "Display firing range"
"firingRange": "Display firing range",
"widgetSelector": "Widget selector"
},
"gameControl": {
"voteForcestart": "Vote forcestart",
Expand Down
56 changes: 49 additions & 7 deletions language/en/interface.json
Original file line number Diff line number Diff line change
Expand Up @@ -240,26 +240,69 @@
"title": "Keybinds",
"editor": {
"allCategories": "All",
"search": "Search...",
"search": "Search actions or keys...",
"boundTo": "Bound to %{keys}",
"preset": "Preset",
"defaultTag": "Default",
"pressKey": "Press a key or mouse button...",
"newProfile": "New Profile",
"newProfile": "New Preset",
"duplicate": "Duplicate",
"duplicateTooltip": "Make a copy of this preset that you can change.",
"edit": "Edit",
"editTooltip": "Rename or delete this preset.",
"editLockedTooltip": "Default presets cannot be renamed or deleted. Duplicate this one to get a copy you can change.",
"delete": "Delete",
"save": "Save",
"saveAsNew": "Save as New",
"noticeDefault": "Default presets cannot be changed. Your edits will be saved as a new preset.",
"noticeDefaultUnsaved": "Unsaved changes (Ctrl+Z undoes). Saving creates a new preset and leaves the default as it is.",
"noticeUnsaved": "Unsaved changes (Ctrl+Z undoes the last edit).",
"changed": "Changed",
"changedCount": "Changed (%{n})",
"changedTooltip": "Every action whose keys differ from %{name}. Restore default keybinds by clicking the button containing the defualt keybind on the right.",
"conflict": "%{keys} is also bound to: %{actions}",
"conflictFirst": "%{action} (tried first)",
"conflictOrder": "Actions sharing a key are tried in the order they were bound; the first to take the key wins.",
"conflictShipped": "The game ships them on one key; the one bound first is tried first.",
"conflictCapture": "Also bound to: %{actions}",
"defaultIn": "Default in %{name}: %{keys}",
"defaultNone": "Not bound in %{name}",
"revertHint": "Click to put the default keybind back.",
"revertNone": "none",
"revertChip": "default: %{keys}",
"presetDefault": "A default preset. It cannot be changed; editing it saves a new preset of your own.",
"presetBasedOn": "Your preset, based on %{name}.",
"presetOwn": "Your preset.",
"export": "Export",
"exportTooltip": "Copy this preset to the clipboard as text, to share or keep.",
"exportDone": "\"%{name}\" is on the clipboard. Paste it anywhere to share or keep it; Import reads it back in.",
"import": "Import",
"importTooltip": "Read a preset from text on the clipboard and add it as a new preset.",
"importTitle": "Import preset",
"importEmpty": "The clipboard is empty. Copy a preset's text first, then try again.",
"importSummary": "%{n} keybinds found",
"importErrors": "%{n} lines could not be read and will be dropped",
"importNone": "No keybinds found in the clipboard text",
"ok": "OK",
"reset": "Discard Changes",
"resetConfirm": "Discard your unsaved keybind changes?",
"saveTitle": "Save as new profile",
"saveTitle": "Save as new preset",
"discard": "Discard",
"unsavedTitle": "Unsaved changes",
"unsavedMessage": "Your keybind changes have not been saved yet.",
"duplicateTitle": "Duplicate profile",
"editTitle": "Edit profile",
"duplicateTitle": "Duplicate preset",
"editTitle": "Edit preset",
"deleteConfirm": "Delete \"%{name}\"? This cannot be undone.",
"applyFailedTitle": "Could not apply profile",
"applyFailedTitle": "Could not apply preset",
"applyFailedMessage": "\"%{name}\" could not be written to disk, so your keybinds are unchanged. Check that the game folder is writable.",
"accept": "Accept",
"cancel": "Cancel"
},
"presets": {
"grid": "Builds through the grid menu: QWER, ASDF and ZXCV pick a slot in the build grid, so the same key builds the same slot for every builder. Orders sit on the keys around them.",
"grid60": "The Grid preset for keyboards without a function row: map views, camera anchors and the other F-key actions move to Meta + number, and what sat on ` moves to Meta + Q.",
"legacy": "The classic layout: a key per building for build hotkeys, orders on their original keys, and the build menu rather than the grid.",
"legacy60": "The Legacy preset for keyboards without a function row: map views, camera views and the other F-key actions move to Meta + number."
}
},
"blueprint": {
Expand Down Expand Up @@ -740,7 +783,6 @@
"factorydefaults": "Factory defaults",
"cancel": "Cancel",
"confirm": "Confirm",
"hint": "Click to toggle. Right-click sends it to the front of its layer, middle-click to the back.",
"factorydefaultswarn": "This throws away every interface setting you have: which widgets are on, their positions, and anything you have configured in them. LuaUI reloads immediately. It cannot be undone.",
"unloadallwarn": "Switches off every widget in the list at once. Your settings are kept, and you can switch them back on one at a time.",
"disallowuserwarn": "Stops loading widgets from your own LuaUI folder, leaving only the ones the game ships. LuaUI reloads immediately.",
Expand Down
3 changes: 1 addition & 2 deletions luarules/gadgets/cmd_dev_helpers.lua
Original file line number Diff line number Diff line change
Expand Up @@ -543,8 +543,7 @@ if gadgetHandler:IsSyncedCode() then
subPermission = "modmarker"
end

local bypassSyncedAuthorization = cmd == "godmode" or cmd == "godmodeally"
if not bypassSyncedAuthorization and not isAuthorized(playerID, subPermission) then
if not isAuthorized(playerID, subPermission) then
return
end

Expand Down
Loading
Loading