---
layout: ../Site.layout.js
---
# [LispGameJam NicCLIM](https://itch.io/jam/autumn-lisp-game-jam-2025/topic/5489995/who-is-doing-common-lisp-and-how) game movement / call for you to submit an s-expression level to coauthor it with me
This article added the game movement mechanics and we are about done! Just need to make some s-expression maps.
Even though my map-editor has hextille game-map cursor movement, my *gamejam* cursor controls are going to have their own command/s, since they basically *are* the game logic as well as notion of movement.
<img src="../game-mechanics.gif">
EDIT: You could contribute a level to this, or you could use this as a base to do entirely your own thing (ask me on the Mastodon if you want any help or commentary with your own thing).
Back to the implementation bits
## Game Movement Needs
1. Cannot walk through impassable objects. I will define these to be symbols occuring in some list, `*impassable*`.
2. After game-moving, any `radio` symbols in the new cell are executed in turn.
3. Moving should be controlled by ←↓↑→=hjkl With ⎇↑ and ⎇↓ to complete the hexagonal movement on the hextille.
## NicCLIM setup as normal now (keep scrolling)
```
• (setq inferior-lisp-program "ecl")
• (setq eepitch-buffer-name "*slime-repl ECL*")
• (slime)
(ql:Quickload :McCLIM)
(compile-file "~/Downloads/nicclim.lisp" :load t)
(in-package :nic)
(string '~/game)
(ensure-directories-exist *)
(uiop:chdir (string '~/game/))
(uiop:chdir (string '~/game/)) ;; Twice, for some reason.
```
## Defining game movement
There are two problems with writing this:
1. representing a rectangular grid as hextille, even and odd rows are different. This effects up, down, the other up and the other down.
1. Even though *I have `com-peek` which does this*, I think that nesting commands is a performance problem on slow machines (like all of mine).
My approach was to explicitly write the peek. Even though we could write macros to make the code look mathematically cuter, well, we can just literally write what is literally happening. So with some apologies. All of them are just
1. Peek in the movement direction. If the `intersection` with `*impassable*` is non-`nil`, do not move, else move.
1. If we moved, run any radio-selections in that cell.
1. Walking off an edge in the game is implicitly an error. This is intentional, since you can make what you wanted to happen instead happen in the lisp debugger, for example. Wall your edges maybe. Walking off the edge not-in-a-game is not considered an error by `NicCLIM` itself.
This looks kinda long, but I am just specifying what directions mean on odd/even rows and you can basically skip past this.
```
(defvar *impassable*
'())
(defmacro
do-events ()
`(with-slots
(table-list cursor-locn)
*application-frame*
(let ((contents
(copy-list
(nth (cadr cursor-locn)
(nth (car cursor-locn)
table-list)))))
(dolist (c contents)
(when (get c :radio-choices)
(execute-frame-command
*application-frame*
`(com-popup ,c)))))))
(define-map-editor-command (com-game-h :name t
:keystroke :left)
()
(with-slots (table-list cursor-locn) *application-frame*
(unless
(intersection *impassable*
(nth (1- (cadr cursor-locn))
(nth (car cursor-locn)
table-list)))
(execute-frame-command *application-frame* '(com-h))
(do-events))))
(define-map-editor-command (com-game-j :name t
:keystroke :down)
()
(with-slots (table-list cursor-locn) *application-frame*
(unless
(intersection *impassable*
(nth (+ (cadr cursor-locn)
(if (oddp (car cursor-locn))
1 0))
(nth (1+ (car cursor-locn))
table-list)))
(execute-frame-command *application-frame* '(com-j))
(do-events))))
(define-map-editor-command (com-game-k :name t
:keystroke :up)
()
(with-slots (table-list cursor-locn) *application-frame*
(unless
(intersection *impassable*
(nth (+ (cadr cursor-locn)
(if (evenp (car cursor-locn))
-1 0))
(nth (1- (car cursor-locn))
table-list)))
(execute-frame-command *application-frame* '(com-k))
(do-events))))
(define-map-editor-command (com-game-l :name t
:keystroke :right)
()
(with-slots (table-list cursor-locn) *application-frame*
(unless
(intersection *impassable*
(nth (1+ (cadr cursor-locn))
(nth (car cursor-locn)
table-list)))
(execute-frame-command *application-frame* '(com-l))
(do-events))))
(define-map-editor-command (com-game-kl :name t
:keystroke
(:up :control))
()
(with-slots (table-list cursor-locn) *application-frame*
(unless
(intersection *impassable*
(nth (+ (1+ (cadr cursor-locn))
(if (evenp (car cursor-locn))
-1 0))
(nth (1- (car cursor-locn))
table-list)))
(execute-frame-command *application-frame* '(com-kl))
(do-events))))
(define-map-editor-command (com-game-jh :name t
:keystroke
(:down :control))
()
(with-slots
(table-list cursor-locn) *application-frame*
(unless
(intersection *impassable*
(nth (+ (1- (cadr cursor-locn))
(if (oddp (car cursor-locn))
1 0))
(nth (1+ (car cursor-locn))
table-list)))
(execute-frame-command *application-frame* '(com-jh))
(do-events))))
```
Okay, decode the symbol into movement, then execute any contents that are radio-button-choices.
### See that working on a `:radio-choice`-haver
I threw together an example. We can see that `(EXAMPLE)`, which is radio select example symbol for NicCLIM, triggers a radio select with the game movement (you can't see it, but I am using the left arrowkey ← to move left, and com-l (M-S-l) to move right). The arrowkeys are the game controls, while hjkl are the NicCLIM controls. The left arrow game movement triggers the radio button, and the left arrow game movement fails to pass the wall since I `push`ed wall to `*impassable*`.
<img src="../game-mechanics.gif">
Well, I want *you personally* to contribute a level or map or whatever these are, so let us go over that exhaustively. Currently the radio button *chooses something*, i.e. sets the `cur2` cursor according to the choice, but I will add a game button to activate-cursor-2 sometime this weekend. I am imagining the main choice as *"go up/down these stairs"*.
# Everything to make that level
## The map
Sooo the map, however *you* would make it is
```
(WALL) (WALL) (WALL) (WALL) (WALL) (WALL) (WALL)
(WALL) NIL NIL NIL NIL NIL (WALL)
(WALL) NIL NIL NIL NIL NIL (WALL)
(WALL) NIL NIL (EXAMPLE) NIL NIL (WALL)
(WALL) NIL NIL NIL NIL NIL (WALL)
(WALL) NIL NIL NIL NIL NIL (WALL)
(WALL) (WALL) (WALL) (WALL) (WALL) (WALL) (WALL)
```
this file (in my working directory, it is `~/GAME/WALL-RADIO.MAP`) is just an s-expression file right? You could make this as a tsv with a spreadsheet program. You can have more than one thing in a list; the one closer to the end of the list will print last, or display on top if they are images.
## Walls are impassable
```
(push 'wall *impassable*)
```
deep, I know.
## The `EXAMPLE` radio choice from before
```
(setf (get 'EXAMPLE :radio-description)
'("Lots of
text!"
asdf grass solid
(1 2 (3 4) 5))
(get 'EXAMPLE :radio-lisp-hook)
(list (lambda () 'nothing)
(lambda () 'nothing-else))
(get 'EXAMPLE :radio-choices)
'(member
("choice the first" (etc 1))
("choice the second" (etc 2))
("choice the third" (etc 3))))
```
The `:radio-description` is a list where symbols with a `:bitmap` property that is a path to an image will inline images, and everything else will be understood as text (including newlines).
`:radio-lisp-hook`: obviously those `lambda`s do nothing, I just put that there in case I wanted to run an audio player in the background with a shell call or something.
`:radio-choices` : this is just what this is like in `clim`. The text displayed on the radio box is the first thing like "choice the first", what *is returned* (i.e. what the cursor 2 gets set to) is the *second thing* (like `(etc 1)`).
[We saw pictures in a previous devlog](/lispgames/the-other-threeish-checkmarks/).
Anyway, now I am hoping to be inundated with s-expression maps. We could imagine that everyone who wants to co-author this lispgamejam game with me could send me / link me their own s-expression file just like this (and any symbols/radio-choices you used, and art).
If you did not get how to do something, just tell me what you wanted to happen and I will make it so.
Two days left of the jam, we could have a lot of levels!
# `#unix_surrealism` : https://analognowhere.com/ is contributing reuseable art to this game
So if you want some incredible surrealist art to go with your symbols, snip it out of technomage comics and scale it down (I think 50x50 pixels is probably a good size).
You can contribute your own art or do whatever compatible with [lispgamejam](https://itch.io/jam/autumn-lisp-game-jam-2025) but I will be working in terms of technomage and openblade.
# Conclusions
This was a pretty conclusive article
# Fin.
See you [on the Mastodon](https://gamerplus.org/@screwlisp/115504909459721353) *where I am looking forward to you inundating me with s-expression files like the above, possibly using technomage or other art, to make up our shared **dungeons of technomage doom** lispgamejam submission. (I will submit NicCLIM with a game map / lisp config tar there at the end of this weekend).