luv

Workshop wiki

commands.lisp

luvcraft/clim/commands.lisp

system luvcraft · 42 definitions · on GitHub

in-package#:luvcraft.clim

The verbs. Each one is the body that handle-canvas-event used to run inline behind an EQ test on a key name, now carrying its own name, its own human label, and its own keystroke as data in a table.

define-command
com-toggle-inventory:command-tableluvcraft-world:name"Toggle Inventory":keystroke
#\i
define-command
com-toggle-phone:command-tableluvcraft-world:name"Toggle Phone":keystroke
#\f
define-command
com-toggle-ball:command-tableluvcraft-world:name"Toggle Ball":keystroke
#\b
define-command
com-toggle-metabar:command-tableluvcraft-world:name"Toggle Metabar":keystroke
:return
define-command
com-toggle-lobby-panel:command-tableluvcraft-window:name"Toggle Lobby Panel"
define-command
com-toggle-focus:command-tableluvcraft-world:name"Toggle Focus":keystroke
:tab

The window layer. Fullscreen belongs to the window rather than to anything inside it, and Shift-Tab is the gesture that gets the keyboard back from whatever has taken it -- including a shell reading raw keys, which is exactly the surface that must not be able to swallow it.

define-command
com-toggle-tracy-capture:command-tableluvcraft-window:name"Toggle Tracy Capture":keystroke
:f9

Commands run on the canvas thread. start-tracy initially calls whichever thread invokes it "main", which is right during process startup but would give this live-started lane the wrong ownership name.

define-command
com-start-tracy-capture:command-tableluvcraft-window:name"Start Tracy Capture"
define-command
com-stop-tracy-capture:command-tableluvcraft-window:name"Stop Tracy Capture"
define-command
com-open-last-tracy-capture:command-tableluvcraft-window:name"Open Last Tracy Capture"
define-command
com-reveal-last-tracy-capture:command-tableluvcraft-window:name"Reveal Last Tracy Capture"
define-command
com-toggle-fullscreen:command-tableluvcraft-window:name"Toggle Fullscreen":keystroke
:f11
define-command
com-leave-focus:command-tableluvcraft-window:name"Leave Focus":keystroke
:tab:shift
defparameter*presentation-timing-pathname*#P"build/kms-presentation.csv""Where the in-game timing command writes its latest KMS presentation run."
defunexport-luvcraft-presentation-timing
session&optional

Export session's direct-display Vulkan timing data, when it has any.

let
context
luvcraft:luvcraft-session-contextsession
when
let
when
andsnapshot
not
eq:not-direct-display
luv:presentation-timing-snapshot-reasonsnapshot
define-command
com-export-presentation-timing:command-tableluvcraft-window:name"Export Presentation Timing"

Control-Q quits from anywhere, including modal focus. On a KMSDRM console there is no window manager to deliver a close request, so without a quit key the game owns the machine until someone kills it over SSH. It lives in the window layer for the same reason Shift-Tab does: no surface may be able to swallow it. Q is the layout's letter q, wherever that key sits -- gestures match the unshifted character, so a Dvorak console quits from its own Q.

define-command
com-quit:command-tableluvcraft-window:name"Quit":keystroke
#\q:control
let
unwind-protect
unless
luv:close-canvas
luvcraft:luvcraft-session-canvassession

Keyboard equivalents of the pointer buttons, for consoles and laptops without a working click.

defundvorak-controls-p
string-equal
or
uiop:getenv"LUV_KEYBOARD_LAYOUT"
""
"dvorak"
defunadd-place-block-keystroke

Bind place to QWERTY E's physical key, independently of its layout text.

Clear the older character bindings too when this definition is loaded into a live image. On Wayland a Dvorak physical D carries both key name :D and character #\e; a #\e place binding would outrank walking right.

dolist
key'
:e#\e#\.
remove-keystroke-from-command-table'luvcraft-world
listkey
:errorpnil
add-keystroke-to-command-table'luvcraft-world'
:e
:command'
com-place-block
:errorpnil
define-command
com-mine-block:command-tableluvcraft-world:name"Mine Block":keystroke
#\x
define-command
com-pick-block:command-tableluvcraft-world:name"Pick Block":keystroke
#\c

TrackPoint look without a button: M captures and releases the pointer by keyboard alone.

define-command
com-toggle-pointer-capture:command-tableluvcraft-world:name"Toggle Pointer Capture":keystroke
#\m
let*
capture-p
not
luvcraft:luvcraft-session-pointer-captured-psession
luv:set-canvas-relative-pointer-mode
luvcraft:luvcraft-session-canvassession
capture-p
setf
luvcraft:luvcraft-session-pointer-captured-psession
capture-p

Named but unbound: escape now shows the keymap, which releases the pointer on its way. This stays reachable by name, from a listener or a script.

define-command
com-release-pointer:command-tableluvcraft-world:name"Release Pointer"
let
when
luvcraft:luvcraft-session-pointer-captured-psession
luv:set-canvas-relative-pointer-mode
luvcraft:luvcraft-session-canvassession
nil
setf
luvcraft:luvcraft-session-pointer-captured-psession
nil
define-command
com-select-quickbar-slot:command-tableluvcraft-world:name"Select Quickbar Slot"
slot'integer:prompt"quickbar slot"

The quickbar is how many slots the number row reaches, while the block a slot names is looked up in the whole inventory: a slot past the end of the bar selects nothing rather than something further down the list.

let
when
<=1slot
length
luvcraft:block-inventory-quickbar-blocks
luvcraft:luvcraft-session-inventorysession

The number row selects a quickbar slot. Ten commands would say the same thing ten times; a keystroke item of type :function instead builds the command object for the digit that was pressed, which is the ordinary CLIM way to bind a family of keys to one verb with an argument. The 0 key ends the row where it sits on the keyboard, as the tenth slot.

loopforslotfrom1to10do
add-keystroke-to-command-table'luvcraft-world
list
digit-char
modslot10
:function
let
slotslot
lambda
gesturenumeric-argument
declare
ignoregesturenumeric-argument
list'com-select-quickbar-slotslot
:documentation
formatnil"Select quickbar slot ~D"slot
:errorpnil

Movement.

A direction key is a start and a stop rather than a state someone reads out of a key table later, so what the game holds is the urge itself: a rider on an animal and a player on their own feet run the same commands and set the same intent, and a gamepad or a recorded demo could run them too.

define-command
com-start-walking:command-tableluvcraft-movement:name"Start Walking"
direction'keyword:prompt"direction"
let*
player
luvcraft:luvcraft-session-playersession
whenplayer
luvcraft:cancel-body-movementplayer"cancelled by manual player input"
setf
luvcraft:movement-urging-p
luvcraft:luvcraft-session-movement-intentsession
direction
t
define-command
com-stop-walking:command-tableluvcraft-movement-release:name"Stop Walking"
direction'keyword:prompt"direction"
setf
luvcraft:movement-urging-p
luvcraft:luvcraft-session-movement-intent
direction
nil
define-command
com-jump:command-tableluvcraft-movement:name"Jump":keystroke
:space:any
let*
player
luvcraft:luvcraft-session-playersession
whenplayer
luvcraft:cancel-body-movementplayer"cancelled by manual player input"
setf
luvcraft:movement-intent-jump-requested-p
luvcraft:luvcraft-session-movement-intentsession
t
defunwalk-keys-for-layout
append
if
eqlayout:dvorak
'
#\,:forward
#\o:backward
#\a:left
#\e:right
'
:w:forward
:s:backward
:a:left
:d:right
'
:shift-left:sprint
:shift-right:sprint
defparameter*walk-keys*

Which key urges which direction. The arrows are deliberately absent: they belong to looking (see *LOOK-KEYS*), a mouse for a console whose pointer has no working buttons.

Bound with :ANY because a movement key means the same thing however it is modified: Shift-W is a player sprinting forward, not a player pressing some other key.

defunadd-walk-keystrokes

Bind every walking key to its start on the way down and its stop on the up.

Definition reloads change the live tables too, so clear both layouts before installing the currently configured one.

dolist
key'
:w:s:a:d#\,#\o#\a#\e:shift-left:shift-right
dolist
remove-keystroke-from-command-tabletable
listkey:any
:errorpnil
loopfor
keydirection
in*walk-keys*do
flet
walk-item
command
let
directiondirection
lambda
gesturenumeric-argument
declare
ignoregesturenumeric-argument
listcommanddirection
add-keystroke-to-command-table'luvcraft-movement
listkey:any
:function
walk-item'com-start-walking
:errorpnil
add-keystroke-to-command-table'luvcraft-movement-release
listkey:any
:function
walk-item'com-stop-walking
:errorpnil

Looking.

The arrow keys turn the camera the way holding a mouse-look would: a start and a stop urging a look intent that advance-luvcraft-keyboard-look integrates every frame. They live in the world layer rather than the movement table so that a ridden animal, whose focus offers exactly the movement vocabulary, is steered with WASD and never has its camera wrenched by a stray arrow.

define-command
com-start-looking:command-tableluvcraft-world:name"Start Looking"
direction'keyword:prompt"direction"
setf
luvcraft:movement-urging-p
luvcraft:luvcraft-session-look-intent
direction
t
define-command
com-stop-looking:command-tableluvcraft-world-release:name"Stop Looking"
direction'keyword:prompt"direction"
setf
luvcraft:movement-urging-p
luvcraft:luvcraft-session-look-intent
direction
nil
defparameter*look-keys*'
:up:up
:down:down
:left:left
:right:right
"Which arrow urges the camera which way."
defunadd-look-keystrokes

Bind every arrow to its look on the way down and its stop on the up.

loopfor
keydirection
in*look-keys*do
flet
look-item
command
let
directiondirection
lambda
gesturenumeric-argument
declare
ignoregesturenumeric-argument
listcommanddirection
add-keystroke-to-command-table'luvcraft-world
listkey:any
:function
look-item'com-start-looking
:errorpnil
add-keystroke-to-command-table'luvcraft-world-release
listkey:any
:function
look-item'com-stop-looking
:errorpnil

A rider's focus offers exactly the movement table, so steering an animal and walking are the same commands reaching the same intent. Everything else a rider presses still falls to the ride itself, which wants none of it.

At a terminal.

A wall in shell mode gives every key to the PTY, which is what makes it a shell and not a menu; so the keys that change what the wall *is* have to be ones no shell wants. The command modifier is that: a terminal never sees it, and on this platform it is already how one switches between things.

define-command
com-set-terminal-mode:command-tableluvcraft-terminal:name"Set Wall Mode"
mode'keyword:prompt"wall mode"
let*
focus
luvcraft:luvcraft-session-modal-focussession
defunterminal-mode-gesture
mode

The keystroke that chooses mode, by its place in the wall's own mode list.

alexandria:when-let
when
<index9
list
digit-char
1+index
:super
defunadd-terminal-mode-keystrokes

Bind each wall mode to the command modifier and its slot number.

dolist
alexandria:when-let
add-keystroke-to-command-table'luvcraft-terminalgesture:function
let
modemode
lambda
gesturenumeric-argument
declare
ignoregesturenumeric-argument
list'com-set-terminal-modemode
:errorpnil

The wall's own chooser asks what key reaches a mode rather than printing a number and hoping. Specializing on SYMBOL beats the core's T default.

defmethodluvcraft:luvcraft-key-hint
modesymbol
alexandria:when-let