luv

Workshop wiki

hotbar.lisp

mcclim/hotbar.lisp

system luvcraft/mcclim · 39 definitions · on GitHub

A real McCLIM hotbar, composited into luvcraft's existing game canvas.

in-package#:mcluv
defclasshotbar-pane
application-pane
defunhotbar-material-color
block
destructuring-bind
redgreenblue
luvcraft:block-kind-display-colorblock
make-rgb-colorredgreenblue
defunhotbar-scaled-color
componentsscale
destructuring-bind
redgreenblue
components
make-rgb-color
min1.0
min1.0
*greenscale
min1.0
*bluescale
defunhotbar-material-ink
blocktopbottom

Give block a restrained top-lit gradient from top to bottom.

let
components
luvcraft:block-kind-display-colorblock
make-linear-gradient0top0bottom
hotbar-scaled-colorcomponents1.18
hotbar-scaled-colorcomponents0.68
defunhotbar-selection-ink
topbottom

A quiet neutral edge for the currently selected material.

make-linear-gradient0top0bottom
make-rgb-color0.780.820.80
make-rgb-color0.400.460.48
defunhotbar-terminal-display
let
focus
luvcraft:luvcraft-session-modal-focus
hotbar-sessionframe
defunhotbar-visible-state-for
alexandria:if-let
list:terminaldisplay
luvcraft:terminal-display-modedisplay
list:blocks
luvcraft:luvcraft-session-selected-block
hotbar-sessionframe
defparameter*terminal-display-modes*'
:shell:film

The wall modes the hotbar offers, in slot order.

A presentation layer which teaches change-terminal-display-mode a new mode appends it here and gets a numbered slot; nothing else has to change.

defparameter*terminal-display-mode-colors*'
:shell0.120.440.30
:film0.470.240.58
:telegram0.160.420.62

Geometry

One function decides where a slot is, so painting and hit-testing cannot disagree about which one the player clicked.

defconstant+hotbar-width+612
defconstant+hotbar-height+104
defconstant+hotbar-pad+12
defconstant+hotbar-gap+6
defconstant+hotbar-slot-top+10
defconstant+hotbar-mode-slot-width+156

How wide a named mode slot is, rather than a share of the whole bar.

Nine identical wells want to divide the instrument between them. Two or three named modes do not: a slot stretched to half the bar puts its word alone in a field of empty metal, which reads as a mistake rather than as a button.

defunhotbar-slot-geometry
countindex&keywidth

Left, top, right, and bottom of slot index of count.

Without width the slots divide the bar between them. With it they are exactly that wide and the row is centred in the bar instead.

let*
slot-width
orwidth
/
-content
floatcount
total
+
*countslot-width
left
+origin
*index
+slot-width+hotbar-gap+
defunhotbar-slot-at
countu&keywidth

The slot index at horizontal texture fraction U, or NIL between slots.

loopforindexbelowcountdo
multiple-value-bind
lefttoprightbottom
declare
ignoretopbottom
let
when
and
<=leftx
<xright
returnindex
defundraw-hotbar-shell
panemedium

The instrument the slots sit in: one raised chassis, quietly lit.

Cover the whole sheet first. The chassis is drawn at the bar's declared size, and a sheet that is larger than that -- a frame outliving a resize -- would otherwise show through as bare white.

with-bounding-rectangle*
lefttoprightbottom
pane
draw-rectangle*panelefttoprightbottom:ink
make-rgb-color0.0120.0140.018
draw-analytic-rounded-rectangle*medium00+hotbar-width++hotbar-height+:radius14:ink
make-linear-gradient000+hotbar-height+
make-rgb-color0.200.210.23
make-rgb-color0.0550.0600.070
draw-analytic-rounded-rectangle*medium22:radius12:ink
make-linear-gradient000+hotbar-height+
make-rgb-color0.0850.0920.105
make-rgb-color0.0300.0340.040

A single bright hairline along the top edge is what reads as "made of something" without drawing a border around every slot.

draw-line*pane121.51.5:ink
make-rgb-color0.420.450.50
:line-thickness1
defundraw-hotbar-slot
panemediumlefttoprightbottom&keyselected-p
accent'
0.950.860.55

One recessed well, lit from inside when it is the chosen one.

draw-analytic-rounded-rectangle*mediumlefttoprightbottom:radius8:ink
ifselected-p
make-linear-gradient0top0bottom
make-rgb-color0.0280.0320.038
make-rgb-color0.0550.0600.068
whenselected-p
draw-analytic-rounded-rectangle*medium
-left2
-top2
+right2
+bottom2
:radius10:fillednil:ink
draw-analytic-rounded-rectangle*medium
-left1
-top1
+right1
+bottom1
:radius9:fillednil:ink

The well's own top edge, darker than the chassis, is the whole recess.

draw-line*pane
+left3
+top0.5
-right3
+top0.5
:ink
make-rgb-color0.0140.0160.020
defundraw-hotbar-caption
panemediumtext&key
ink'
0.930.930.90

The one label the bar carries: what is currently chosen.

let
draw-analytic-rounded-rectangle*mediumtopbottom:radius7:ink
make-rgb-color0.0200.0230.028
draw-text*panetext
/
+topbottom
2.0
:align-x:center:align-y:center:text-size12:ink
apply#'make-rgb-colorink
defparameter*terminal-display-mode-glyphs*'
:shell."$"
:film."▶"
:telegram."@"

One mark per mode. A chooser of three words all the same size makes the player read; a chooser of three shapes lets them recognize.

Kept to marks the game's own font actually carries. A missing glyph draws as nothing at all, which is worse than a plain one: the slot that has a mark and the slot that has none stop looking like the same kind of thing.

defunpaint-terminal-mode-hotbar
panedisplay

Paint the focused terminal's semantic modes into pane.

with-sheet-medium
mediumpane
let
current
luvcraft:terminal-display-modedisplay
loopformodein*terminal-display-modes*forindexfrom0forselected-p=
eqmodecurrent
foraccent=do
multiple-value-bind
lefttoprightbottom
draw-hotbar-slotpanemediumlefttoprightbottom:selected-pselected-p:accentaccent

A mode slot is wide and short, so the mark and the word sit beside each other on one line rather than stacked into each other's space.

let
middle
/
+topbottom
2.0
draw-text*pane
+left32
middle:align-x:center:align-y:center:text-size20:ink
hotbar-scaled-coloraccent
ifselected-p2.21.5
draw-text*pane
string-capitalize
symbol-namemode
+left54
middle:align-x:left:align-y:center:text-size15:ink
ifselected-p
make-rgb-color0.980.980.96
make-rgb-color0.620.640.66

The badge is whatever key actually reaches this mode. A wall in shell mode hands every plain key to the PTY, so a bare number here would be an instruction to type one.

alexandria:when-let
draw-text*panehint
-right9
+top11
:align-x:right:align-y:center:text-size10:ink
ifselected-p
make-rgb-color0.800.820.86
make-rgb-color0.460.480.52
draw-hotbar-captionpanemedium
formatnil"~A wall"
string-capitalize
symbol-namecurrent
defmethodhandle-repaint
region
declare
ignoreregion
let*
frame
pane-framepane
session
hotbar-sessionframe
selected
luvcraft:luvcraft-session-selected-blocksession
blocks
luvcraft:block-inventory-quickbar-blocks
luvcraft:luvcraft-session-inventorysession
alexandria:when-let
return-fromhandle-repaintnil
with-sheet-medium
mediumpane
let
count
max1
lengthblocks
loopforblockinblocksforindexfrom0forselected-p=
eqblockselected
do
multiple-value-bind
lefttoprightbottom
draw-hotbar-slotpanemediumlefttoprightbottom:selected-pselected-p

The cube is the material: the same procedural faces the world is built from, not a swatch of its average colour.

draw-block-iconpaneblock
-
/
+leftright
2.0
20
-
/
+topbottom
2.0
21
40

The tenth slot's key is 0, at the end of the number row.

draw-text*pane
formatnil"~D"
mod
1+index
10
+left7
+top10
:align-x:left:align-y:center:text-size10:ink
ifselected-p
make-rgb-color0.980.950.82
make-rgb-color0.460.480.52

Nine labels is nine things to read; the chosen one is the only name that answers a question the player is actually asking.

draw-hotbar-captionpanemedium
string-capitalize
substitute#\Space#\-
symbol-name
luvcraft:block-kind-nameselected
define-application-frameluvcraft-hotbar
session:initarg:session:readerhotbar-session
visible-selection:initformnil:accessorhotbar-visible-selection
:menu-barnil
:panes
bar
make-pane'hotbar-pane
:layouts
default
horizontallybar
defunrepaint-hotbar

Repaint and publish one complete material palette.

let
mirror
sheet-direct-mirror
frame-top-level-sheetframe
check-typemirrorluv-gpu-mirror
setf
hotbar-visible-selectionframe
frame
defmethodluvcraft:luvcraft-overlay-stage
declare
ignoreoverlay
:hud
defunhotbar-screen-state
overlay
let*
source-size
viewport-size
multiple-value-list
luv:canvas-logical-size
luvcraft:luvcraft-session-canvas
widget-overlay-sessionoverlay
source-width
firstsource-size
source-height
secondsource-size
viewport-width
firstviewport-size
viewport-height
secondviewport-size
scale
min1.0
/
-viewport-width24.0
source-width
half-width
/
*source-widthscale
viewport-width
half-height
/
*source-heightscale
viewport-height
bottom-margin
/28.0viewport-height
center-y
-1.0bottom-marginhalf-height
make-array12:element-type'single-float:initial-contents
mapcar
lambda
value
coercevalue'single-float
list0.0center-y0.01.0half-width0.00.00.00.0half-height0.00.0
luv:zdefmethod
sessionpasssurface-texture
declare
ignorepass
prepare-direct-widget-overlayoverlaysessionsurface-texture
overlay
luv:zdefmethod
declare
ignoresession
let*
frame
widget-overlay-frameoverlay
unless
equalstate
hotbar-visible-selectionframe
overlay
defmethodluvcraft:handle-luvcraft-overlay-event
declare
ignorecanvas
alexandria:when-let
uv
luvcraft-widget-texture-coordinateoverlay
luv:canvas-pointer-event-xevent
luv:canvas-pointer-event-yevent
when
and
eq:left
luv:canvas-pointer-event-buttonevent
let*
frame
widget-overlay-frameoverlay

hotbar-slot-at reads the same geometry the painter does, so a click in the gap between two slots chooses neither rather than guessing.

ifdisplay
let
count
length
luvcraft:block-inventory-quickbar-blocks
luvcraft:luvcraft-session-inventorysession
alexandria:when-let
slot
hotbar-slot-atcount
firstuv
t
defmethodluvcraft:handle-luvcraft-focus-control-event
declare
ignoredisplay
some
luvcraft:luvcraft-session-overlayssession
defmethodluvcraft:luvcraft-focus-score
declare
ignoreoverlaysession
nil
defmethodluvcraft:luvcraft-overlay-focus-insets
declare
ignoresession
let*
height
second
multiple-value-list
luv:canvas-logical-size
luvcraft:luvcraft-session-canvas
widget-overlay-sessionoverlay
center-y
arefstate1
half-height
abs
arefstate9
bottom-inset
*0.5height
+
-1.0center-y
half-height
values0.00.00.0bottom-inset
defunopen-luvcraft-hotbar
session&key
title"luvcraft block palette"

Create and attach the screen-space McCLIM block palette for session.

let*
port
find-port:server-path'
:luv-gpu
manager
or
first
climi::frame-managersport
make-instance'luv-frame-manager:portport
frame
let
*embedded-mirror-target*
luvcraft:luvcraft-session-canvassession
*embedded-mirror-context*
luvcraft::luvcraft-session-contextsession
*embedded-mirror-device*
luvcraft::luvcraft-session-devicesession
make-application-frame'luvcraft-hotbar:frame-managermanager:enablet:sessionsession
setf
frame-pretty-nameframe
title
hotbar-visible-selectionframe
let*
mirror
sheet-direct-mirror
frame-top-level-sheetframe
overlay
make-instance'luvcraft-hotbar-overlay:sessionsession:frameframe:mirrormirror
setf
mirror-compositormirror
overlay
when
typepmirror'luv-gpu-mirror
overlay
defunclose-luvcraft-hotbar
overlay

Remove and release an open-luvcraft-hotbar overlay.

check-typeoverlayluvcraft-hotbar-overlay
luvcraft:remove-luvcraft-overlay
widget-overlay-sessionoverlay
overlay
nil