Advertisement
Guest User

Untitled

a guest
Jul 25th, 2016
164
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lisp 13.70 KB | None | 0 0
  1. ;;;;; utility/entity.lisp
  2.  
  3. (in-package #:ostvingkel-sewers)
  4.  
  5. (declaim (optimize (speed 3)
  6.                    (space 3)
  7.                    (safety 0)))
  8.  
  9. (defstruct entity
  10.   (components (make-hash-table :test 'eq)
  11.               :type hash-table
  12.               :read-only t))
  13.  
  14. (let ((component-types (make-hash-table :test 'eq))
  15.       (component-dependencies (make-hash-table :test 'eq)))
  16.   (defun get-component-type (name)
  17.     (declare (type symbol name))
  18.     ([av]if (gethash name component-types)
  19.         it
  20.       (error "Attempt to find type of undefined component ~A"
  21.              name)))
  22.   (defun (setf get-component-type)
  23.       (type name)
  24.     (declare (type symbol name))
  25.     ([av]when (gethash name component-types)
  26.       (unless (equal type it)
  27.         (warn "Redefining type of component ~A"
  28.               name)))
  29.     (setf (gethash name component-types)
  30.           type))
  31.   (defun get-component-dependencies (name)
  32.     (declare (type symbol name))
  33.     (the list
  34.          ([av]if (gethash name component-dependencies)
  35.              it
  36.            (error "Attempt to find dependencies of undefined component ~A"
  37.                   name))))
  38.   (defun (setf get-component-dependencies)
  39.       (dependencies name)
  40.     (declare (type symbol name)
  41.              (type (or cons symbol)
  42.                    dependencies))
  43.     (let ((dependencies (mapcar #'ensure-list
  44.                                 dependencies)))
  45.       (declare (type list dependencies))
  46.       ([av]when (gethash name component-dependencies)
  47.         (unless (equal dependencies it)
  48.           (warn "Redefining dependencies of component ~A"
  49.                 name)))
  50.       (setf (gethash name component-dependencies)
  51.             dependencies))))
  52.  
  53. (defmacro define-component-type (name type &body dependencies)
  54.   `(eval-when (:compile-toplevel :load-toplevel :execute)
  55.      (setf (get-component-type ',name)
  56.            ',type)
  57.      (setf (get-component-dependencies ',name)
  58.            ',dependencies)
  59.      (defgeneric ,name (entity)
  60.        (:method ((entity entity))
  61.          (with-components entity (,name)
  62.            ,name))
  63.        (:method (component)
  64.          (if (typep component ',type)
  65.              (values component t)
  66.              (values nil nil))))
  67.      ',name))
  68.  
  69. (defmacro define-component-struct (name-and-options (&rest dependencies)
  70.                                    &body slot-descriptions)
  71.   (let ((name (car-or-x name-and-options)))
  72.     `(eval-when (:compile-toplevel :load-toplevel :execute)
  73.        (defstruct ,name-and-options
  74.          ,@slot-descriptions)
  75.        (setf (get-component-type ',name)
  76.              ',name)
  77.        (setf (get-component-dependencies ',name)
  78.              ',dependencies)
  79.        ,@(loop for slot-description in slot-descriptions
  80.             collect
  81.               (let ((slot (car-or-x slot-description)))
  82.                 `(progn
  83.                    (defmethod ,slot ((entity ,name))
  84.                      (values (,(symb name '- slot)
  85.                                entity)
  86.                              t))
  87.                    (defmethod ,slot ((entity entity))
  88.                      (with-components entity (,name)
  89.                        (,slot ,name)))
  90.                    (defmethod ,slot (entity)
  91.                      (declare (ignore entity))
  92.                      (values nil nil))
  93.                    ,(unless (getf (cddr slot-description)
  94.                                   :read-only)
  95.                       `(progn
  96.                          (defmethod (setf ,slot)
  97.                              (value (entity ,name))
  98.                            (setf (,(symb name '- slot)
  99.                                    entity)
  100.                                  value))
  101.                          (defmethod (setf ,slot)
  102.                              (value (entity entity))
  103.                            (with-components entity (,name)
  104.                              (setf (,slot ,name)
  105.                                    value)))
  106.                          (defmethod (setf ,slot)
  107.                              (value entity)
  108.                            (declare (ignore value entity))))))))
  109.        ',name)))
  110.  
  111. (defmacro define-component-class (name superclasses (&rest dependencies)
  112.                                   (&body slots)
  113.                                   &body options)
  114.   (let ((slots (mapcar #'ensure-list
  115.                        slots)))
  116.     `(eval-when (:compile-toplevel :load-toplevel :execute)
  117.        (defclass ,name (,@superclasses)
  118.          (,@(mapcar (lambda (slot)
  119.                       (cons (first slot)
  120.                             (plist-rem '(:accessor :reader :writer)
  121.                                        (rest slot))))
  122.                     slots))
  123.          ,@options)
  124.        (setf (get-component-type ',name)
  125.              ',name)
  126.        (setf (get-component-dependencies ',name)
  127.              ',dependencies)
  128.        ,@(loop for slot
  129.             in (remove-if-not (lambda (slot)
  130.                                 (let ((slot-options (rest slot)))
  131.                                   (or (getf slot-options :accessor)
  132.                                       (getf slot-options :reader)
  133.                                       (getf slot-options :writer))))
  134.                               slots)
  135.             collect
  136.               (destructuring-bind (slot-name &key accessor reader writer
  137.                                              &allow-other-keys)
  138.                   slot
  139.                 `(progn
  140.                    ,([a]when accessor
  141.                       `(progn
  142.                          (defmethod ,it ((entity ,name))
  143.                            (values (slot-value entity ',slot-name)
  144.                                    t))
  145.                          (defmethod ,it ((entity entity))
  146.                            (with-components entity (,name)
  147.                              (,it ,name)))
  148.                          (defmethod (setf ,it)
  149.                              (value (entity ,name))
  150.                            (setf (slot-value entity ',slot-name)
  151.                                  value))
  152.                          (defmethod (setf ,it)
  153.                              (value (entity entity))
  154.                            (with-components entity (,name)
  155.                              (setf (,it ,name)
  156.                                    value)))))
  157.                    ,([a]when reader
  158.                       `(progn
  159.                          (defmethod ,it ((entity ,name))
  160.                            (values (slot-value entity ',slot-name)
  161.                                    t))
  162.                          (defmethod ,it ((entity entity))
  163.                            (with-components entity (,name)
  164.                              (,it ,name)))))
  165.                    ,([a]when writer
  166.                       `(progn
  167.                          (defmethod (setf ,it)
  168.                              (value (entity ,name))
  169.                            (setf (slot-value entity ',slot-name)
  170.                                  value))
  171.                          (defmethod (setf ,it)
  172.                              (value (entity entity))
  173.                            (with-components entity (,name)
  174.                              (setf (,it ,name)
  175.                                    value))))))))
  176.        ',name)))
  177.  
  178. (defmacro entity (&body components)
  179.   (let ((g!entity (gensym "entity"))
  180.         (g!hash (gensym "hash"))
  181.         (g!init (gensym "init")))
  182.     `(the entity
  183.           (let* ((,g!entity (make-entity))
  184.                  (,g!hash (entity-components ,g!entity)))
  185.             (declare (ignorable ,g!hash)
  186.                      (type entity ,g!entity)
  187.                      (type hash-table ,g!hash))
  188.             ,@(loop for (component init)
  189.                  on components by #'cddr
  190.                  collect
  191.                    `(let ((,g!init ,init))
  192.                       (check-type ,g!init ,(get-component-type component))
  193.                       ,@(loop for dependency
  194.                            in (get-component-dependencies component)
  195.                            collect
  196.                              `(unless
  197.                                   (or ,@(loop for dep in dependency
  198.                                            collect
  199.                                              `(nth-value
  200.                                                1 (gethash ',dep ,g!hash))))
  201.                                 (error "Component ~A depends on one of ~
  202.    components ~A being present, but the dependency is not satisfied"
  203.                                        ',component ',dependency)))
  204.                       (setf (gethash ',component ,g!hash)
  205.                             ,g!init)))
  206.             ,g!entity))))
  207.  
  208. (defmacro add-component (entity component &body init)
  209.   (let ((g!entity (gensym "entity"))
  210.         (g!init (gensym "init")))
  211.     `(the entity
  212.           (let ((,g!entity ,entity)
  213.                 (,g!init (progn
  214.                            ,@init)))
  215.             (check-type ,g!init ,(get-component-type component))
  216.             ,@(loop for dependency in (get-component-dependencies component)
  217.                  collect
  218.                    `(assert (or ,@(loop for dep in dependency
  219.                                      collect
  220.                                        `(has-component ,g!entity ,dep)))))
  221.             (setf (gethash ',component (entity-components ,g!entity))
  222.                   ,g!init)
  223.             ,g!entity))))
  224.  
  225. (defmacro rem-component (entity component)
  226.   (let ((g!entity (gensym "entity")))
  227.     ;; TODO: Check that removing the specified component doesn't violate
  228.     ;; dependencies. Provide a parameter to decide whether to throw an error or
  229.     ;; remove all dependent components. The latter might be very useful for
  230.     ;; killing – just make all components responsible for behavior dependent on
  231.     ;; the component holding information about the status of the actor.
  232.     `(the entity
  233.           (let ((,g!entity ,entity))
  234.             (check-type ,g!entity entity)
  235.             (remhash ',component (entity-components ,g!entity))
  236.             ,g!entity))))
  237.  
  238. (defmacro has-component? (entity component)
  239.   (let ((g!entity (gensym "entity")))
  240.     `(the boolean
  241.           (let ((,g!entity ,entity))
  242.             (check-type ,g!entity entity)
  243.             (nth-value 1
  244.                        (gethash ',component (entity-components ,g!entity)))))))
  245.  
  246. (defmacro ensure-component (entity component &body init)
  247.   (let ((g!entity (gensym "entity")))
  248.     `(the entity
  249.           (let ((,g!entity ,entity))
  250.             (check-type ,g!entity entity)
  251.             (unless (has-component? ,g!entity ,component)
  252.               (add-component ,g!entity ,component
  253.                 ,@init))
  254.             ,g!entity))))
  255.  
  256. (defmacro get-component (entity component &optional default)
  257.   (let ((g!entity (gensym "entity")))
  258.     `(let ((,g!entity ,entity))
  259.        (check-type ,g!entity entity)
  260.        (gethash ',component (entity-components ,g!entity)
  261.                 ,default))))
  262.  
  263. (defmacro with-components (entity (&rest components)
  264.                                   &body body)
  265.   (let ((g!entity (gensym "entity")))
  266.     (labels ((_rec (components)
  267.                (if components
  268.                    (let* ((g!found? (gensym "found?"))
  269.                           (curr (first components))
  270.                           (type (second-or-x curr)))
  271.                      `(multiple-value-bind (,(car-or-x curr)
  272.                                             ,g!found?)
  273.                           (get-component ,g!entity ,type)
  274.                         (declare (ignorable ,(car-or-x curr))
  275.                                  (type boolean ,g!found?))
  276.                         (the (values t boolean)
  277.                              (if ,g!found?
  278.                                  ,(_rec (rest components))
  279.                                  (values nil nil)))))
  280.                    `(the (values t boolean)
  281.                          (values (progn
  282.                                    ,@body)
  283.                                  t)))))
  284.       `(the (values t boolean)
  285.             (let ((,g!entity ,entity))
  286.               (check-type ,g!entity entity)
  287.               ,(_rec components))))))
  288.  
  289. (defmacro define-entity-method (name (&rest args)
  290.                                 &body body)
  291.   (let ((declarations (loop for form in body
  292.                          while (and (consp form)
  293.                                     (eq (first form)
  294.                                         'declare))
  295.                          collect form)))
  296.     (labels ((_build-component-form (args)
  297.                ([a]if (first args)
  298.                    (if (and (consp it)
  299.                             (eq (second it)
  300.                                 'entity)
  301.                             (cddr it))
  302.                        `(with-components ,(first it)
  303.                             (,@(cddr it))
  304.                           ,(_build-component-form (rest args)))
  305.                        (_build-component-form (rest args)))
  306.                  (if declarations
  307.                      `(locally
  308.                           ,@declarations
  309.                         ,@(loop for form in body
  310.                              unless (and (consp form)
  311.                                          (eq (first form)
  312.                                              'declare))
  313.                              collect form))
  314.                      `(progn
  315.                         ,@body)))))
  316.       `(defmethod ,name (,@(mapcar (lambda (arg)
  317.                                      (if (consp arg)
  318.                                          (take 2 arg)
  319.                                          arg))
  320.                                    args))
  321.          ,@declarations
  322.          ,(_build-component-form args)))))
  323.  
  324. (defun copy-entity* (entity)
  325.   (check-type entity entity)
  326.   (the entity
  327.        (make-entity :components (copy-hash-table (entity-components entity)))))
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement