---
layout: ../Site.layout.js
---
# Trying [NicCLIM game map editor](https://lispy-gopher-show.itch.io/nicclim) (with [ecl](https://ecl.common-lisp.dev/) [mcclim](https://mcclim.common-lisp.dev/main.html) [emacs](https://sachachua.com/blog/2025/08/2025-08-25-emacs-news/) [eev](http://anggtwu.net/#eev))
While my next steps with NicCLIM common lisp interface manager map editor are very exciting, I don't want this-first-release to be vacant. Or, I would like to know if it is. So I'm committed to giving it the old college try here and now, while I should be preparing more for my interview of https://as.tufts.edu/physics/people/faculty/ken-olum tomorrow (0UTC Wednesday / 8pm Tuesday Boston time on https://anonradio.net/ live/ https://communitymedia.video/c/screwtape_channel/videos archive) instead.
Edit: (Worth listening to the archive about modern sbcl getting 60+ hours on the Tufts supercomputer https://communitymedia.video/w/9kysH4ZwVuP4J4erZozqFT .)
I'm going to start NicCLIM in a background thread (remember, McCLIM `application-frame` *commands* are threadsafe), and drive it using `eepitch` from this markdown file.
If something doesn't work for you in this example, please howl at me [on the mastodon](https://gamerplus.org/@screwlisp/115093336986864299) or on https://lispy-gopher-show.itch.io/ .
EDIT: Note, on a sufficiently slow computer, sometimes McCLIM signals that it waited-too-long for a response. Just choose 0 (do nothing) and it will continue normally.
EDIT2: Since this demo includes setup, scroll halfway down to get to screenshots/pictures/commands of the action.
# Setup
First, download `nicclim.lisp` from https://lispy-gopher-show.itch.io/nicclim (Name your price ⇨ "just take me to the file") to `~/Downloads/nicclim.lisp`.
I wrote [a broad spectrum minimal common lisp / emacs / eev guide here](/fundamental/installing-lisp-etc/).
## eev / slime (superior lisp interaction mode for emacs)
```
• (setq inferior-lisp-program "ecl")
• (slime)
• (setq eepitch-buffer-name "*slime-repl ECL*")
```
(for me, at least). Remember that with `eev` we press `F8` to send a line to `eepitch-buffer-name` in emacs. If you aren't in emacs+eev you would come up with something different.
Incidentally, it is called `inferior-lisp-program` because the common lisp image is being controlled by (is inferior to) the emacs lisp major mode. Hence the joke-y names.
## Common lisp interface manager
If you used https://www.quicklisp.org/beta/ to install [McCLIM](https://codeberg.org/McCLIM/McCLIM) implementation of the [Common Lisp Interface Manager spec](http://bauhh.dyndns.org:8000/clim-spec/index.html),
```
(ql:quickload :mcclim)
```
or otherwise `(asdf:load-system :mcclim)`.
## My NicCLIM map editor
We use it from inside of `CLIM-USER`. `-USER` packages are designed to *absorb user code*. We are users of `CLIM` in this case: We are developers of our game maps. I am confident [Jack Daniel](https://turtleware.eu) would appreciate your contributions to developing McCLIM as well.
EDIT: Note that we end up using it `(in-package :nicclim)` in a moment instead to avoid some namespace stuff.
```
(in-package :clim-user)
(make-pathname :directory '(:relative "~/Downloads"))
(make-pathname :name "nicclim" :type "lisp")
(merge-pathnames ** *)
```
Phew! I *love and prefer* lisp's portably abstracted classic [pathname](http://lispm.de/docs/clhs/HyperSpec/Body/26_glo_p.htm#pathname) stuff, but it's hard to remember! I checked [the end of my article over here](/programming/tangle/) as an example. Back to it.
```
(compile-file * :load t)
```
(I use [* ** ***](http://lispm.de/docs/clhs/HyperSpec/Body/v__stst_.htm) to refer to recent results).
I use embeddable common lisp's [`compile-file`](http://lispm.de/docs/clhs/HyperSpec/Body/f_cmp_fi.htm) [-and-then-load extension](https://ecl.common-lisp.dev/static/files/manual/current-manual/System-building.html#Native-FASL). Conceivably,
```
(unless t
(compile-file *)
(load *)
) ; instead.
```
Well, finally we got setup.
# The Game (map editor)
I've been putting maps in `~/GAME/`.
```
(ensure-directories-exist #p"~/GAME/")
(uiop:chdir #p"~/GAME/")
(uiop:chdir #p"~/GAME/")
(with-open-file ; don't replace an existing file (error).
(*standard-output* "CURRENT"
:direction :output
:if-does-not-exist :create
:if-exists :error))
(ensure-directories-exist #p"MAP/")
(ensure-directories-exist #p"PIC/")
```
I'm going to be honest, I don't know why I have to issue that posix [`uiop:chdir`](https://asdf.common-lisp.dev/uiop.html#index-chdir) twice.
The idea is maps would go in `#p"~/GAME/MAP/"` and later pictures would go in `#p"~/GAME/PIC/"` with the idea that we can just use the symbol `map/room` for a room map and so forth.
## Start the ed' in a background thread
`mcclim` has already dragged in in [bordeaux-threads](https://bordeaux-threads.common-lisp.dev/).
```
(asdf:load-system :bordeaux-threads)
(in-package :nicclim)
(bt:make-thread
(lambda ()
(enclose-map "CURRENT")))
```
## Keyboard Shortcuts (screenshot)
I'm not alleging that I should win an award for layout design, but I squeezed a bunch of useful shortcuts into this layout (see the layouts menu).
## `create rect` some kind of starting map
While we would use a mixture of hotkeys and clim completions in practice, since I'm writing this markdown document, I'm going to adopt the approach of explicitly typing (well, autocompleting) full clim command forms by hand, then running [`execute-frame-command`](https://www.lispworks.com/documentation/lw80/clim/clim-ch9-8.htm#CLIM)execute-frame-command) (which is the low level interaction).
```
'(com-create-rect map/grass-5x5 5 5 (GRASS))
(execute-frame-command *nic* *)
```
EDIT: Earlier, we went `(in-package :nicclim)` to wholly circumscribe needing to type these so the below is deprecated.
well, that's kind of annoying. Because I don't export the command names since they're always used internally to the map editor, I have to pass them as internal symbols of the package, i.e. `nic::com-create-rect` for `com-create-rect` (and `nic:com-create-rect` would be an exported symbol). I guess Nicholas Martyanoff prefers people namespace like this in general.
## Change map to the new rect
```
'(com-change-map map/grass-5x5)
(execute-frame-command *nic* *)
```
I added the special variable `*nic*` to `nicclim` expose the-current `enclose-map` `application-frame`.
## Make a similar desert
```
'(com-create-rect map/desert-5x5 5 5 (sand))
(execute-frame-command *nic* *)
'(com-change-map map/desert-5x5)
(execute-frame-command *nic* *)
```
## `horizontal-cat` grass and desert
```
'(com-horizontal-cat map/grass-5x5 map/desert-5x5 map/gd-5x5 0 nil)
(execute-frame-command *nic* *)
'(com-change-map map/gd-5x5)
(execute-frame-command *nic* *)
```
## Add a tree and a skull fine details
Note this would be done with keyboard accelerators (hotkeys).
```
'(jump 4 2)
(execute-frame-command *nic* *)
'(set-cur1 tree)
(execute-frame-command *nic* *)
'(push-cur1)
(execute-frame-command *nic* *)
'(com-j)
(execute-frame-command *nic* *)
'(push-cur1)
(execute-frame-command *nic* *)
'(vec-mov 2 1)
(execute-frame-command *nic* *)
'(push-cur1)
(execute-frame-command *nic* *)
```
Out of interest, the hotkey version of this was
1. `C-M-i 4 2`
1. `C-M-u tree`
1. `C-M-o`
1. `M-S-j`
1. `C-M-o`
1. `C-S-i 2 1`
1. `C-M-o`
Some apologies for my unmnemonic choices! If you have the hotkey screenshot in clear view somewhere, it is possible to operate very fast using these keyboard accelerators.
## Write, copy a rect to a new file
Save cool bits of larger tiles.
```
'(com-writef map/dg)
(execute-frame-command *nic* *)
'(com-extract-rect map/dg map/treesand 4 7 2 4)
(execute-frame-command *nic* *)
'(com-cat map/treesand)
(execute-frame-command *nic* *)
```
On the right side of the screen:
```
*NIC*> '(com-cat map/treesand)
(COM-CAT MAP/TREESAND)
*NIC*> (execute-frame-command *nic* *)
T
(GRASS NICCLIM::TREE) (NICCLIM::SAND) (NICCLIM::SAND)
(GRASS NICCLIM::TREE) (NICCLIM::SAND) (NICCLIM::SAND)
```
## Copy that extracted tile back into our larger map
```
'(com-clobber-rect map/dg map/treesand map/dg2 3 0 2 2)
(execute-frame-command *nic* *)
'(com-change-map map/dg2)
(execute-frame-command *nic* *)
```
Whoops, it turns out x, y, width, height in `com-clobber-rect` is right-exclusive whereas `com-extract-rect` x, xlim, y, ylim is *right-inclusive* (I think?). However, we accidentally showed that the maps can be right-jagged, *and critically* that the slightly peculiar `com-clobber-rect` can be used to delete and insert rectangles from and into a larger map, potentially resizing it. So I am leaving this slightly odd outcome in.
# Conclusions
Firstly, there were lots of great insights ranging into small essays to [my Mastodon toot asking about gamedevs' map-editor experiences](https://gamerplus.org/@screwlisp/115086363022021385) which you should review at your leisure.
Viz *this article*, I think we saw NicCLIM can quickly perform large, medium, and VI-cursor scope changes while flicking between many 'map' ascii files. Oh, for reference, `MAP/TREESAND` looks like this internally:
```
(GRASS NICCLIM::TREE) (NICCLIM::SAND) (NICCLIM::SAND)
(GRASS NICCLIM::TREE) (NICCLIM::SAND) (NICCLIM::SAND)
```
which is just normal lisp symbols/lists, annoying explicit namespace aside. The idea is that transparency-using pictures will optionally on top of each other stacked from left-to-right in a future version.
The fact that this markdown document is executable source code reinforces the macro programming addition planned in the next major version.
I would really like it if you managed to give it a try, as [Larian](https://gamerplus.org/@Larian) is for his https://www.chroniclesofember.com/ ([Larian's interview](https://communitymedia.video/w/aiMfBwxdJC28wadDefufBF)).
Talk [on the Mastodon toot](https://gamerplus.org/@screwlisp/115093336986864299) as always please.
```
(GRASS) (GRASS) (GRASS) (GRASS NICCLIM::TREE) (NICCLIM::SAND) (NICCLIM::SAND) (NICCLIM::SAND) (NICCLIM::SAND) (NICCLIM::SAND) (NICCLIM::SAND) (NICCLIM::SAND)
(GRASS) (GRASS) (GRASS) (GRASS NICCLIM::TREE) (NICCLIM::SAND) (NICCLIM::SAND) (NICCLIM::SAND) (NICCLIM::SAND) (NICCLIM::SAND) (NICCLIM::SAND) (NICCLIM::SAND)
(GRASS) (GRASS) (GRASS) (GRASS) (GRASS NICCLIM::TREE) (NICCLIM::SAND) (NICCLIM::SAND) (NICCLIM::SAND) (NICCLIM::SAND) (NICCLIM::SAND)
(GRASS) (GRASS) (GRASS) (GRASS) (GRASS NICCLIM::TREE) (NICCLIM::SAND) (NICCLIM::SAND) (NICCLIM::SAND) (NICCLIM::SAND) (NICCLIM::SAND)
(GRASS) (GRASS) (GRASS) (GRASS) (GRASS) (NICCLIM::SAND) (NICCLIM::SAND NICCLIM::TREE) (NICCLIM::SAND) (NICCLIM::SAND) (NICCLIM::SAND)
```