RSS Feed

Lisp Project of the Day

beast

You can support this project by donating at:

Donate using PatreonDonate using Liberapay

beastgames

Documentation😀
Docstrings😀
Tests 😀
Examples😀
RepositoryActivity🤨
CI 🥺

Today's overview is about the Beast. This is a small library, written by @stevelosh for describing a game world.

Game world is full of different entities. Each entity has it a type and characteristics or aspects.

"Beast" is a simple wrapper over CLOS, which allows you to define aspects and to mix them when defining a new entity type.

Here is a nice example from projects README:

(define-aspect throwable accuracy damage)
(define-aspect edible nutrition-value)

(define-entity dart (throwable))
(define-entity cheese (edible))
(define-entity pie (throwable edible))

(define-system rot-food ((e edible))
  (decf (edible/nutrition-value e))
  (when (zerop (edible/nutrition-value e))
    (destroy-entity e)))

(defparameter *steel-dart* 
  (create-entity 'dart
    :throwable/accuracy 0.9
    :throwable/damage 10))

(defparameter *hunk-of-swiss*
  (create-entity 'cheese
    :edible/nutrition-value 50))

(defparameter *banana-cream-pie*
  (create-entity 'pie
    :throwable/accuracy 0.3
    :throwable/damage 5
    :edible/nutrition-value 30))

Macro (beast:define-aspect throwable accuracy damage) is expanded into such class-definition:

(progn
 (defclass throwable nil
           ((throwable/accuracy :accessor throwable/accuracy :initarg
             :throwable/accuracy)
            (throwable/damage :accessor throwable/damage :initarg
             :throwable/damage)))
 (defun throwable? (beast::object) (typep beast::object 'throwable))
 (beast::initialize-aspect-index 'throwable)
 (find-class 'throwable))

Macro (beast:define-entity pie (throwable edible)) also transformed into a class which inherits all aspects and also has a class-level slot with a list of aspects:

(progn
 (defclass pie (beast:entity throwable edible)
           ((beast::%beast/aspects :allocation :class :initform
             '(throwable edible))))
 (defun pie? (beast::object) (typep beast::object 'pie))
 (find-class 'pie))

Function create-entity not only creates a CLOS instance but also adds it to special indexes of entities of this type and entities having these aspects.

Macro define-system defines two functions:

(progn
  (declaim
   (ftype (function ((and beast:entity edible)) (values null &optional))
          rot-food)
   (notinline rot-food))
 
  (defun rot-food (e)
    (decf (edible/nutrition-value e))
    (when (zerop (edible/nutrition-value e)) (beast:destroy-entity e))
    nil)
  
  (defun run-rot-food ()
    (let ((#:argument-indexes1011 (gethash 'rot-food beast::*system-index*)))
      (loop :for #:entity1012 :being :the beast::hash-values :of (first
                                                                  #:argument-indexes1011)
            :do (locally
                    (declare (type (and beast:entity edible) #:entity1012))
                  (rot-food #:entity1012)))))
  (beast::initialize-system-index 'rot-food #'rot-food '((e edible)))
  'rot-food)

So, this macro defines a code which will be applied to all "edible" objects in the game.

I think Beast is the great library for defining game objects!

You can find more information about this library in its great documentation.


Brought to you by 40Ants under Creative Commons License