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))
(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))
(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))
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.
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.