Lisp soft console example game #first


<body>

Lisp soft console example game #first

Hey!

I made an example game that implies a lot of games I think.

The Committee is taking form (pkw, zyd, vidak, hopefully you, …)

1. Anatomy of game cartridges, first try

Let's just go through the whole single lisp file. You can feed it whatever floats your lisp boat, but ecumenically I made the cons-ole to walk this way.

2. :common-lisp/lisp-game-soft-cons/game/example

I guess hypothetically you should have a persistent game cartridge dir and then symlink that inside your extraction of lisp-game-soft-cons.tar.gz

Lmk what works for you

2.1. Package def

(uiop:define-package :lisp-game-soft-cons/game/example
    (:mix :lisp-game-soft-cons/console
     :lisp-game-soft-cons/display
          :clim :clim-lisp :cl)
  (:import-from :clx)
  (:export)
  (:nicknames :lafs/game/example))

(in-package :lafs/game/example)

Basically you have to mix those mixes to do what we're about to do in a moment.

I think :import-from :clx pulls in the :clx backend (the software cons-ole is otherwise backend agnostic, try it with your lispworks or allegro clims ;p).

I'm relying on the McCLIM libre child of the clim spec. https://codeberg.org/McCLIM/McCLIM http://bauhh.dyndns.org:8000/clim-spec/index.html

2.2. Parent classes

Game data should be slots in parent classes that the game-attached-to-display will inherit from. Here's what I did:

  (defclass directional-parent ()
  ((x-locn :initform '2)
   (y-locn :initform '2)))

(defclass cursory-parent ()
  ((f1-pressed :initform '(0 0))
   (f2-pressed :initform '(0 0))))

2.3. The main event

Calling this macro is what gets you what you saw in the video.

(make-tv
 (example-game (directional-parent cursory-parent))

 ((com-up com-left)
  (com-down com-right)
  (com-f1 com-f2))

 ((x-locn y-locn)
  (f1-pressed f2-pressed)))

I'll add a screenshot of the video ;p. It's kinda sm0l isn't it. Basically

  • make-tv is the macro
    • lambda list the first
      • example-game <- this symbol names the game application frame
      • (directional-parent cursory-parent) <- the classes a moment ago
    • lambda list the second
      • (each list) (becomes buttons) (in the) (button area). Must be regular.
    • lambda list III
      • (these are) (slots of)
      • The parent classes we named in the first lambda list These slots get smartly displayed in the screen northeast pane.

I guess that's it.

2.4. Game Commands

So rather than writing low level lisp, game commands are exactly and identically clim commands. I guess it's a bit long and repetitive, because we want to harness every drop of power these have.

Here, I just did "press button or key or say name, increases or decreases number in the northeast panel" basically, but the idea is what sophisticated commands would be typed and queried dynamically (like 'help' in the video).

So this is a bit long and repetitive, but you really only need to say what pressing :up in your game means once, and I guess it's pretty important.

Sorry for long. Conventionally, I want the main keys used to be

:up :down :left :right <- directions (shock)

:f1 :f2 <- "a and b" on the NES controller.

This is because we have typing and dynamic gui selectors appearing to do anything advanced. The "game controller" is the dir keys and a and b = f1 and f2.

(define-example-game-command (com-f1 :name "f1" :keystroke (:f1))
    ()
  (let ((frame *application-frame*))
    (with-slots (x-locn y-locn f1-pressed) frame
      (setf f1-pressed (list x-locn y-locn)))))


(define-example-game-command (com-f2 :name "f2" :keystroke (:f2))
    ()
  (let ((frame *application-frame*))
    (with-slots (x-locn y-locn f2-pressed) frame
      (setf f2-pressed (list x-locn y-locn)))))

(define-example-game-command (com-up :name "up" :keystroke (:up))
    ()
  (let ((frame *application-frame*))
    (with-slots (y-locn) frame (incf y-locn))))

(define-example-game-command (com-down :name "down" :keystroke (:down))
    ()
  (let ((frame *application-frame*))
    (with-slots (y-locn) frame (decf y-locn))))

(define-example-game-command (com-left :name "left" :keystroke (:left))
    ()
  (let ((frame *application-frame*))
    (with-slots (x-locn) frame (decf x-locn))))

(define-example-game-command (com-right :name "right" :keystroke (:right))
    ()
  (let ((frame *application-frame*))
    (with-slots (x-locn) frame (incf x-locn))))

Basically we need to explicitly attach commands to keyboard buttons. Since we want to be able to type, copy and paste, kill lines, undo, please only use unused, non-graphic characters. (function keys and arrows, hombre). This seems long, but we basically never need anything else in a large way, but it should be custom.

2.5. Displays and drawing

I didn't include png rendering yet, just circles and polygon stars. The star is an iconic historical CLIM drawing demo (check the UG).

(defun draw-star (Stream)
  (let ((star '(0 3 2 -3 -3 1/2 3 1/2 -2 -3)))
    (draw-polygon* stream star :closed t :filled nil)))

(defun target-star (stream x y)
  (with-room-for-graphics (stream)
    (with-translation (stream x y)
      (with-scaling (stream 10)
        (draw-star stream)))))

;;;; These are the display functions!
(defun example-game-main (frame pane) "
main display fun for example-game"
  (with-slots (f1-pressed f2-pressed) frame
    (target-star pane
                 (* 10 (first f1-pressed))
                 (* 10 (second f1-pressed)))
    (draw-circle* pane (* 10 (first f2-pressed))
                  (* -10 (second f2-pressed)) #o20 :filled nil)))

(defun example-game-subject (frame pane) "
subject display fun for example-game"
  (with-slots (f1-pressed f2-pressed) frame
    (let ((width 128) (height 128))
      (target-star pane
                   (Truncate width 2)
                   (truncate height 2)))))

(defun example-game-object (frame pane) "
object display fun for example-game"
  (with-slots (f2-pressed) frame
    (let ((width 128) (height 128))
      (draw-circle* pane (truncate width 2)
                    (truncate height 2) #o20 :filled nil))))

The star drawing example with a clim drawing transformations example, and the three defuns. The symbol needs to match

  • your-game-main For the big, central, main screen
  • your-game-subject For the northwest small display
  • your-game-object For the southeast small display

Okay and we are done

2.6. Plug game into display

(setq *tv-display*
      (find-application-frame 'example-game
                              :activate nil :create t))

There's actually a lot of subtelty, but let's not belabor it.

2.7. Comprehensive running instructions with quicklisp but without emacs

Sorry it's gonna be long, since we don't want to leave anyone behind. Links after

2.7.1. In term:

mkdir -p ~/common-lisp/
cd ~/common-lisp/
tar xzvf ~/Downloads/lisp-game-soft-cons.tar.gz
cd
sbcl

2.7.2. Now in sbcl (with quicklisp):

(ql:quickload :mcclim)
(asdf:load-system :lisp-game-soft-cons)
(in-package :lafs/game/example)
(run-frame-top-level *tv-display*)

I find it looks best when maximized.

Did you notice that when you close the display and reopen it, you continue from where you left off?

Author: screwlisp

Created: 2024-06-11 Tue 14:48

Validate

</body>

Files

lisp-game-soft-cons.tar.gz 4.3 kB
18 days ago

Get LISP GAME SOFT CONS

Download NowName your own price

Comments

Log in with itch.io to leave a comment.

Last postscript, toot to me on the mastodon: https://mastodon.sdf.org/@screwtape

Screenshot didn't attach...?

Related video