diff --git a/ATL/atl-mode.el b/ATL/atl-mode.el index 406fda4..d0eaa0d 100644 --- a/ATL/atl-mode.el +++ b/ATL/atl-mode.el @@ -14,7 +14,7 @@ '("define-place" "define-item" "define-monster" "define-npc" "define-race" "define-place" "define-weapon" "define-class" "define-player" "define-quest" - "name-world" "load-file") + "define-function" "name-world" "load-file") '() ;; other commands (adjust this?) '("\\.atl$") ;; files for which to activate this mode '(linum-mode) ;; other functions to call diff --git a/ATL/atl-mode.el b/ATL/atl-mode.el index 406fda4..d0eaa0d 100644 --- a/ATL/atl-mode.el +++ b/ATL/atl-mode.el @@ -14,7 +14,7 @@ '("define-place" "define-item" "define-monster" "define-npc" "define-race" "define-place" "define-weapon" "define-class" "define-player" "define-quest" - "name-world" "load-file") + "define-function" "name-world" "load-file") '() ;; other commands (adjust this?) '("\\.atl$") ;; files for which to activate this mode '(linum-mode) ;; other functions to call diff --git a/ATL/test/lisp-test.atl b/ATL/test/lisp-test.atl index b68d944..eed1c62 100644 --- a/ATL/test/lisp-test.atl +++ b/ATL/test/lisp-test.atl @@ -45,6 +45,21 @@ item "Anaklusmos" npc "Charon" +define-function "Light" + docstring "Gives a player night-vision" + player night-vision T + print "You can now see in the dark!" + +define-function "Spawn Fury" + docstring "Spawn a Fury in this place" + place monster "Fury" + print "Watch out! A Kindly One!" + +define-function "Heal" + docstring "Heal the player by 1HP" + player health +1 + print "+1 HP" + load-file lisp-test.atl ;Testing whether recursive loading is prevented load-file races-classes.atl load-file game-objects.atl diff --git a/ATL/atl-mode.el b/ATL/atl-mode.el index 406fda4..d0eaa0d 100644 --- a/ATL/atl-mode.el +++ b/ATL/atl-mode.el @@ -14,7 +14,7 @@ '("define-place" "define-item" "define-monster" "define-npc" "define-race" "define-place" "define-weapon" "define-class" "define-player" "define-quest" - "name-world" "load-file") + "define-function" "name-world" "load-file") '() ;; other commands (adjust this?) '("\\.atl$") ;; files for which to activate this mode '(linum-mode) ;; other functions to call diff --git a/ATL/test/lisp-test.atl b/ATL/test/lisp-test.atl index b68d944..eed1c62 100644 --- a/ATL/test/lisp-test.atl +++ b/ATL/test/lisp-test.atl @@ -45,6 +45,21 @@ item "Anaklusmos" npc "Charon" +define-function "Light" + docstring "Gives a player night-vision" + player night-vision T + print "You can now see in the dark!" + +define-function "Spawn Fury" + docstring "Spawn a Fury in this place" + place monster "Fury" + print "Watch out! A Kindly One!" + +define-function "Heal" + docstring "Heal the player by 1HP" + player health +1 + print "+1 HP" + load-file lisp-test.atl ;Testing whether recursive loading is prevented load-file races-classes.atl load-file game-objects.atl diff --git a/lisp/atlantis.lisp b/lisp/atlantis.lisp index cb1958c..226fddc 100644 --- a/lisp/atlantis.lisp +++ b/lisp/atlantis.lisp @@ -29,7 +29,8 @@ :strength 6 :constitution 12 :dexterity 19 :intelligence 14 :money 100 :game-admin T - :weapon "Anaklusmos"))) + :item '("Anaklusmos") + :weapon "Lightning bolt"))) (add-player player) (play-game (player-name player)))) diff --git a/ATL/atl-mode.el b/ATL/atl-mode.el index 406fda4..d0eaa0d 100644 --- a/ATL/atl-mode.el +++ b/ATL/atl-mode.el @@ -14,7 +14,7 @@ '("define-place" "define-item" "define-monster" "define-npc" "define-race" "define-place" "define-weapon" "define-class" "define-player" "define-quest" - "name-world" "load-file") + "define-function" "name-world" "load-file") '() ;; other commands (adjust this?) '("\\.atl$") ;; files for which to activate this mode '(linum-mode) ;; other functions to call diff --git a/ATL/test/lisp-test.atl b/ATL/test/lisp-test.atl index b68d944..eed1c62 100644 --- a/ATL/test/lisp-test.atl +++ b/ATL/test/lisp-test.atl @@ -45,6 +45,21 @@ item "Anaklusmos" npc "Charon" +define-function "Light" + docstring "Gives a player night-vision" + player night-vision T + print "You can now see in the dark!" + +define-function "Spawn Fury" + docstring "Spawn a Fury in this place" + place monster "Fury" + print "Watch out! A Kindly One!" + +define-function "Heal" + docstring "Heal the player by 1HP" + player health +1 + print "+1 HP" + load-file lisp-test.atl ;Testing whether recursive loading is prevented load-file races-classes.atl load-file game-objects.atl diff --git a/lisp/atlantis.lisp b/lisp/atlantis.lisp index cb1958c..226fddc 100644 --- a/lisp/atlantis.lisp +++ b/lisp/atlantis.lisp @@ -29,7 +29,8 @@ :strength 6 :constitution 12 :dexterity 19 :intelligence 14 :money 100 :game-admin T - :weapon "Anaklusmos"))) + :item '("Anaklusmos") + :weapon "Lightning bolt"))) (add-player player) (play-game (player-name player)))) diff --git a/lisp/game-objects.lisp b/lisp/game-objects.lisp index cb5c5f3..697e2e8 100644 --- a/lisp/game-objects.lisp +++ b/lisp/game-objects.lisp @@ -19,7 +19,8 @@ (item NIL) (monster NIL) (npc NIL) - (dark NIL)) + (dark NIL) + (function "")) ;;; WORK IN PROGRESS >>> @@ -63,6 +64,13 @@ (money 0) (experience 0)) +(defstruct game-function + (name "") + (docstring "") + (place NIL) + (player NIL) + (print "")) + (defun set-object-attribute (game-object property value) "Set the attribute 'property' of 'game-object' to 'value'" ;; Here follows Lisp magic :D (that took ages to get right...) @@ -79,29 +87,35 @@ "Remove 'value' from the attribute 'property' in 'game-object'" ;; Same comment applies as above (let ((command (build-symbol (type-of game-object) "-" property))) - (eval `(if (listp (,command ,game-object)) - (setf (,command ,game-object) - (remove-first-if #'(lambda (x) (equalp x ,value)) - (,command ,game-object))) - ;; TODO set numbers to 0, strings to "" - (setf (,command ,game-object) NIL))))) + (eval `(cond ((listp (,command ,game-object)) + (setf (,command ,game-object) + (remove-first-if #'(lambda (x) (equalp x ,value)) + (,command ,game-object)))) + ((numberp (,command ,game-object)) + (setf (,command ,game-object) 0)) + ((stringp (,command ,game-object)) + (setf (,command ,game-object) "")) + (t (setf (,command ,game-object) NIL)))))) (defun objectify-name-list (object-type name-list) "Turn all the string names in name-list into instances of the object type" ;; Basically the inverse of a make-list-function function (cf. util.lisp) - (let ((objects NIL)) + (let ((objects NIL) (copy-fn (build-symbol "copy-" object-type))) (dolist (n name-list objects) - (setf objects (cons (get-game-object object-type n) objects))))) + (if (stringp n) + (setf objects + (cons (funcall copy-fn (get-game-object object-type n)) + objects)) + (setf objects (cons n objects)))))) (defun objectify-place-monsters (place) "Objectify all the monsters in this place" - ;; XXX This introduces a side effect! - (let* ((p (if (place-p place) place (get-game-object 'place place))) - (monster-list (objectify-name-list 'monster (place-monster p)))) - (if (monster-p (first (place-monster p))) - (return-from objectify-place-monsters place) - (setf (place-monster p) monster-list)))) + (let* ((p (if (place-p place) place (get-game-object 'place place)))) + (setf (place-monster p) + (objectify-name-list 'monster (place-monster p))) + p)) + (let ((list-function (make-list-function 'place NIL))) (defun list-place-objects (object-type place) "Get a list of the names of all the place's objects of this type." @@ -118,3 +132,28 @@ (npc-description (get-game-object 'npc object-name))) (t NIL)))) +(defun run-game-function (function player) + "Execute this game function" + (let* ((fn (if (game-function-p function) function + (get-game-object 'game-function function))) + (player (if (player-p player) player + (get-game-object 'player player))) + (place (get-game-object 'place (player-place player)))) + (dolist (game-obj (list player place)) + ;; Iterate through each element in the function that modifies + ;; this game object + (dolist (element (funcall (build-symbol "game-function-" + (type-of game-obj)) fn)) + (let* ((element (if (listp element) element (list element))) + (attr (first element)) (value (second element)) + ;; FIXME +1 gets transformed to "1"... + (mod (aref (to-string value) 0)) + (orig-value (funcall (build-symbol (type-of game-obj) + #\- attr) game-obj))) + ;; Update the value of the specified attribute + (if (= (length element) 1) + (set-object-attribute game-obj attr T) + (if (or (eq mod #\+) (eq mod #\-)) + (set-object-attribute game-obj attr + (+ orig-value value)) + (set-object-attribute game-obj attr value)))))))) diff --git a/ATL/atl-mode.el b/ATL/atl-mode.el index 406fda4..d0eaa0d 100644 --- a/ATL/atl-mode.el +++ b/ATL/atl-mode.el @@ -14,7 +14,7 @@ '("define-place" "define-item" "define-monster" "define-npc" "define-race" "define-place" "define-weapon" "define-class" "define-player" "define-quest" - "name-world" "load-file") + "define-function" "name-world" "load-file") '() ;; other commands (adjust this?) '("\\.atl$") ;; files for which to activate this mode '(linum-mode) ;; other functions to call diff --git a/ATL/test/lisp-test.atl b/ATL/test/lisp-test.atl index b68d944..eed1c62 100644 --- a/ATL/test/lisp-test.atl +++ b/ATL/test/lisp-test.atl @@ -45,6 +45,21 @@ item "Anaklusmos" npc "Charon" +define-function "Light" + docstring "Gives a player night-vision" + player night-vision T + print "You can now see in the dark!" + +define-function "Spawn Fury" + docstring "Spawn a Fury in this place" + place monster "Fury" + print "Watch out! A Kindly One!" + +define-function "Heal" + docstring "Heal the player by 1HP" + player health +1 + print "+1 HP" + load-file lisp-test.atl ;Testing whether recursive loading is prevented load-file races-classes.atl load-file game-objects.atl diff --git a/lisp/atlantis.lisp b/lisp/atlantis.lisp index cb1958c..226fddc 100644 --- a/lisp/atlantis.lisp +++ b/lisp/atlantis.lisp @@ -29,7 +29,8 @@ :strength 6 :constitution 12 :dexterity 19 :intelligence 14 :money 100 :game-admin T - :weapon "Anaklusmos"))) + :item '("Anaklusmos") + :weapon "Lightning bolt"))) (add-player player) (play-game (player-name player)))) diff --git a/lisp/game-objects.lisp b/lisp/game-objects.lisp index cb5c5f3..697e2e8 100644 --- a/lisp/game-objects.lisp +++ b/lisp/game-objects.lisp @@ -19,7 +19,8 @@ (item NIL) (monster NIL) (npc NIL) - (dark NIL)) + (dark NIL) + (function "")) ;;; WORK IN PROGRESS >>> @@ -63,6 +64,13 @@ (money 0) (experience 0)) +(defstruct game-function + (name "") + (docstring "") + (place NIL) + (player NIL) + (print "")) + (defun set-object-attribute (game-object property value) "Set the attribute 'property' of 'game-object' to 'value'" ;; Here follows Lisp magic :D (that took ages to get right...) @@ -79,29 +87,35 @@ "Remove 'value' from the attribute 'property' in 'game-object'" ;; Same comment applies as above (let ((command (build-symbol (type-of game-object) "-" property))) - (eval `(if (listp (,command ,game-object)) - (setf (,command ,game-object) - (remove-first-if #'(lambda (x) (equalp x ,value)) - (,command ,game-object))) - ;; TODO set numbers to 0, strings to "" - (setf (,command ,game-object) NIL))))) + (eval `(cond ((listp (,command ,game-object)) + (setf (,command ,game-object) + (remove-first-if #'(lambda (x) (equalp x ,value)) + (,command ,game-object)))) + ((numberp (,command ,game-object)) + (setf (,command ,game-object) 0)) + ((stringp (,command ,game-object)) + (setf (,command ,game-object) "")) + (t (setf (,command ,game-object) NIL)))))) (defun objectify-name-list (object-type name-list) "Turn all the string names in name-list into instances of the object type" ;; Basically the inverse of a make-list-function function (cf. util.lisp) - (let ((objects NIL)) + (let ((objects NIL) (copy-fn (build-symbol "copy-" object-type))) (dolist (n name-list objects) - (setf objects (cons (get-game-object object-type n) objects))))) + (if (stringp n) + (setf objects + (cons (funcall copy-fn (get-game-object object-type n)) + objects)) + (setf objects (cons n objects)))))) (defun objectify-place-monsters (place) "Objectify all the monsters in this place" - ;; XXX This introduces a side effect! - (let* ((p (if (place-p place) place (get-game-object 'place place))) - (monster-list (objectify-name-list 'monster (place-monster p)))) - (if (monster-p (first (place-monster p))) - (return-from objectify-place-monsters place) - (setf (place-monster p) monster-list)))) + (let* ((p (if (place-p place) place (get-game-object 'place place)))) + (setf (place-monster p) + (objectify-name-list 'monster (place-monster p))) + p)) + (let ((list-function (make-list-function 'place NIL))) (defun list-place-objects (object-type place) "Get a list of the names of all the place's objects of this type." @@ -118,3 +132,28 @@ (npc-description (get-game-object 'npc object-name))) (t NIL)))) +(defun run-game-function (function player) + "Execute this game function" + (let* ((fn (if (game-function-p function) function + (get-game-object 'game-function function))) + (player (if (player-p player) player + (get-game-object 'player player))) + (place (get-game-object 'place (player-place player)))) + (dolist (game-obj (list player place)) + ;; Iterate through each element in the function that modifies + ;; this game object + (dolist (element (funcall (build-symbol "game-function-" + (type-of game-obj)) fn)) + (let* ((element (if (listp element) element (list element))) + (attr (first element)) (value (second element)) + ;; FIXME +1 gets transformed to "1"... + (mod (aref (to-string value) 0)) + (orig-value (funcall (build-symbol (type-of game-obj) + #\- attr) game-obj))) + ;; Update the value of the specified attribute + (if (= (length element) 1) + (set-object-attribute game-obj attr T) + (if (or (eq mod #\+) (eq mod #\-)) + (set-object-attribute game-obj attr + (+ orig-value value)) + (set-object-attribute game-obj attr value)))))))) diff --git a/lisp/interpreter.lisp b/lisp/interpreter.lisp index 76d2dc9..d2edd72 100644 --- a/lisp/interpreter.lisp +++ b/lisp/interpreter.lisp @@ -38,6 +38,7 @@ (defcommand define-item item) (defcommand define-npc npc) (defcommand define-quest quest) +(defcommand define-function game-function) (let ((world-directory NIL) @@ -90,7 +91,6 @@ (position #\space line)))))) (error "~&ERROR: unrecognized syntax: '~A'" line)))) ;; interpret an option command - ;; TODO allow binary options (options without an argument) ((or (eql (aref line 0) #\Space) (eql (aref line 0) #\Tab)) (let ((options (extract-elements line))) @@ -99,9 +99,15 @@ (first options) T)) (2 (set-object-attribute current-object (first options) (second options))) + (3 (if (game-function-p current-object) + (set-object-attribute current-object + (first options) (cdr options)) + (error "~&ERROR: too many arguments: '~A'" + line))) ;; FIXME gives problems with lines like this: ;; " ;commented" - (t (error "~&Too many arguments: '~A'" line))))) + (t (error "~&ERROR: too many arguments: '~A'" + line))))) (T ;; can't happen (error "~&ERROR: unrecognized syntax: '~A'" line)))))) diff --git a/ATL/atl-mode.el b/ATL/atl-mode.el index 406fda4..d0eaa0d 100644 --- a/ATL/atl-mode.el +++ b/ATL/atl-mode.el @@ -14,7 +14,7 @@ '("define-place" "define-item" "define-monster" "define-npc" "define-race" "define-place" "define-weapon" "define-class" "define-player" "define-quest" - "name-world" "load-file") + "define-function" "name-world" "load-file") '() ;; other commands (adjust this?) '("\\.atl$") ;; files for which to activate this mode '(linum-mode) ;; other functions to call diff --git a/ATL/test/lisp-test.atl b/ATL/test/lisp-test.atl index b68d944..eed1c62 100644 --- a/ATL/test/lisp-test.atl +++ b/ATL/test/lisp-test.atl @@ -45,6 +45,21 @@ item "Anaklusmos" npc "Charon" +define-function "Light" + docstring "Gives a player night-vision" + player night-vision T + print "You can now see in the dark!" + +define-function "Spawn Fury" + docstring "Spawn a Fury in this place" + place monster "Fury" + print "Watch out! A Kindly One!" + +define-function "Heal" + docstring "Heal the player by 1HP" + player health +1 + print "+1 HP" + load-file lisp-test.atl ;Testing whether recursive loading is prevented load-file races-classes.atl load-file game-objects.atl diff --git a/lisp/atlantis.lisp b/lisp/atlantis.lisp index cb1958c..226fddc 100644 --- a/lisp/atlantis.lisp +++ b/lisp/atlantis.lisp @@ -29,7 +29,8 @@ :strength 6 :constitution 12 :dexterity 19 :intelligence 14 :money 100 :game-admin T - :weapon "Anaklusmos"))) + :item '("Anaklusmos") + :weapon "Lightning bolt"))) (add-player player) (play-game (player-name player)))) diff --git a/lisp/game-objects.lisp b/lisp/game-objects.lisp index cb5c5f3..697e2e8 100644 --- a/lisp/game-objects.lisp +++ b/lisp/game-objects.lisp @@ -19,7 +19,8 @@ (item NIL) (monster NIL) (npc NIL) - (dark NIL)) + (dark NIL) + (function "")) ;;; WORK IN PROGRESS >>> @@ -63,6 +64,13 @@ (money 0) (experience 0)) +(defstruct game-function + (name "") + (docstring "") + (place NIL) + (player NIL) + (print "")) + (defun set-object-attribute (game-object property value) "Set the attribute 'property' of 'game-object' to 'value'" ;; Here follows Lisp magic :D (that took ages to get right...) @@ -79,29 +87,35 @@ "Remove 'value' from the attribute 'property' in 'game-object'" ;; Same comment applies as above (let ((command (build-symbol (type-of game-object) "-" property))) - (eval `(if (listp (,command ,game-object)) - (setf (,command ,game-object) - (remove-first-if #'(lambda (x) (equalp x ,value)) - (,command ,game-object))) - ;; TODO set numbers to 0, strings to "" - (setf (,command ,game-object) NIL))))) + (eval `(cond ((listp (,command ,game-object)) + (setf (,command ,game-object) + (remove-first-if #'(lambda (x) (equalp x ,value)) + (,command ,game-object)))) + ((numberp (,command ,game-object)) + (setf (,command ,game-object) 0)) + ((stringp (,command ,game-object)) + (setf (,command ,game-object) "")) + (t (setf (,command ,game-object) NIL)))))) (defun objectify-name-list (object-type name-list) "Turn all the string names in name-list into instances of the object type" ;; Basically the inverse of a make-list-function function (cf. util.lisp) - (let ((objects NIL)) + (let ((objects NIL) (copy-fn (build-symbol "copy-" object-type))) (dolist (n name-list objects) - (setf objects (cons (get-game-object object-type n) objects))))) + (if (stringp n) + (setf objects + (cons (funcall copy-fn (get-game-object object-type n)) + objects)) + (setf objects (cons n objects)))))) (defun objectify-place-monsters (place) "Objectify all the monsters in this place" - ;; XXX This introduces a side effect! - (let* ((p (if (place-p place) place (get-game-object 'place place))) - (monster-list (objectify-name-list 'monster (place-monster p)))) - (if (monster-p (first (place-monster p))) - (return-from objectify-place-monsters place) - (setf (place-monster p) monster-list)))) + (let* ((p (if (place-p place) place (get-game-object 'place place)))) + (setf (place-monster p) + (objectify-name-list 'monster (place-monster p))) + p)) + (let ((list-function (make-list-function 'place NIL))) (defun list-place-objects (object-type place) "Get a list of the names of all the place's objects of this type." @@ -118,3 +132,28 @@ (npc-description (get-game-object 'npc object-name))) (t NIL)))) +(defun run-game-function (function player) + "Execute this game function" + (let* ((fn (if (game-function-p function) function + (get-game-object 'game-function function))) + (player (if (player-p player) player + (get-game-object 'player player))) + (place (get-game-object 'place (player-place player)))) + (dolist (game-obj (list player place)) + ;; Iterate through each element in the function that modifies + ;; this game object + (dolist (element (funcall (build-symbol "game-function-" + (type-of game-obj)) fn)) + (let* ((element (if (listp element) element (list element))) + (attr (first element)) (value (second element)) + ;; FIXME +1 gets transformed to "1"... + (mod (aref (to-string value) 0)) + (orig-value (funcall (build-symbol (type-of game-obj) + #\- attr) game-obj))) + ;; Update the value of the specified attribute + (if (= (length element) 1) + (set-object-attribute game-obj attr T) + (if (or (eq mod #\+) (eq mod #\-)) + (set-object-attribute game-obj attr + (+ orig-value value)) + (set-object-attribute game-obj attr value)))))))) diff --git a/lisp/interpreter.lisp b/lisp/interpreter.lisp index 76d2dc9..d2edd72 100644 --- a/lisp/interpreter.lisp +++ b/lisp/interpreter.lisp @@ -38,6 +38,7 @@ (defcommand define-item item) (defcommand define-npc npc) (defcommand define-quest quest) +(defcommand define-function game-function) (let ((world-directory NIL) @@ -90,7 +91,6 @@ (position #\space line)))))) (error "~&ERROR: unrecognized syntax: '~A'" line)))) ;; interpret an option command - ;; TODO allow binary options (options without an argument) ((or (eql (aref line 0) #\Space) (eql (aref line 0) #\Tab)) (let ((options (extract-elements line))) @@ -99,9 +99,15 @@ (first options) T)) (2 (set-object-attribute current-object (first options) (second options))) + (3 (if (game-function-p current-object) + (set-object-attribute current-object + (first options) (cdr options)) + (error "~&ERROR: too many arguments: '~A'" + line))) ;; FIXME gives problems with lines like this: ;; " ;commented" - (t (error "~&Too many arguments: '~A'" line))))) + (t (error "~&ERROR: too many arguments: '~A'" + line))))) (T ;; can't happen (error "~&ERROR: unrecognized syntax: '~A'" line)))))) diff --git a/lisp/player.lisp b/lisp/player.lisp index ce00de2..fac2547 100644 --- a/lisp/player.lisp +++ b/lisp/player.lisp @@ -27,7 +27,7 @@ (level 0) (max-health 50) (health 50) - (night-vision NIL) + (night-vision NIL) ;This needs to be shifted into a list of abilities (game-admin NIL)) ;; How many XP are needed to level up? diff --git a/ATL/atl-mode.el b/ATL/atl-mode.el index 406fda4..d0eaa0d 100644 --- a/ATL/atl-mode.el +++ b/ATL/atl-mode.el @@ -14,7 +14,7 @@ '("define-place" "define-item" "define-monster" "define-npc" "define-race" "define-place" "define-weapon" "define-class" "define-player" "define-quest" - "name-world" "load-file") + "define-function" "name-world" "load-file") '() ;; other commands (adjust this?) '("\\.atl$") ;; files for which to activate this mode '(linum-mode) ;; other functions to call diff --git a/ATL/test/lisp-test.atl b/ATL/test/lisp-test.atl index b68d944..eed1c62 100644 --- a/ATL/test/lisp-test.atl +++ b/ATL/test/lisp-test.atl @@ -45,6 +45,21 @@ item "Anaklusmos" npc "Charon" +define-function "Light" + docstring "Gives a player night-vision" + player night-vision T + print "You can now see in the dark!" + +define-function "Spawn Fury" + docstring "Spawn a Fury in this place" + place monster "Fury" + print "Watch out! A Kindly One!" + +define-function "Heal" + docstring "Heal the player by 1HP" + player health +1 + print "+1 HP" + load-file lisp-test.atl ;Testing whether recursive loading is prevented load-file races-classes.atl load-file game-objects.atl diff --git a/lisp/atlantis.lisp b/lisp/atlantis.lisp index cb1958c..226fddc 100644 --- a/lisp/atlantis.lisp +++ b/lisp/atlantis.lisp @@ -29,7 +29,8 @@ :strength 6 :constitution 12 :dexterity 19 :intelligence 14 :money 100 :game-admin T - :weapon "Anaklusmos"))) + :item '("Anaklusmos") + :weapon "Lightning bolt"))) (add-player player) (play-game (player-name player)))) diff --git a/lisp/game-objects.lisp b/lisp/game-objects.lisp index cb5c5f3..697e2e8 100644 --- a/lisp/game-objects.lisp +++ b/lisp/game-objects.lisp @@ -19,7 +19,8 @@ (item NIL) (monster NIL) (npc NIL) - (dark NIL)) + (dark NIL) + (function "")) ;;; WORK IN PROGRESS >>> @@ -63,6 +64,13 @@ (money 0) (experience 0)) +(defstruct game-function + (name "") + (docstring "") + (place NIL) + (player NIL) + (print "")) + (defun set-object-attribute (game-object property value) "Set the attribute 'property' of 'game-object' to 'value'" ;; Here follows Lisp magic :D (that took ages to get right...) @@ -79,29 +87,35 @@ "Remove 'value' from the attribute 'property' in 'game-object'" ;; Same comment applies as above (let ((command (build-symbol (type-of game-object) "-" property))) - (eval `(if (listp (,command ,game-object)) - (setf (,command ,game-object) - (remove-first-if #'(lambda (x) (equalp x ,value)) - (,command ,game-object))) - ;; TODO set numbers to 0, strings to "" - (setf (,command ,game-object) NIL))))) + (eval `(cond ((listp (,command ,game-object)) + (setf (,command ,game-object) + (remove-first-if #'(lambda (x) (equalp x ,value)) + (,command ,game-object)))) + ((numberp (,command ,game-object)) + (setf (,command ,game-object) 0)) + ((stringp (,command ,game-object)) + (setf (,command ,game-object) "")) + (t (setf (,command ,game-object) NIL)))))) (defun objectify-name-list (object-type name-list) "Turn all the string names in name-list into instances of the object type" ;; Basically the inverse of a make-list-function function (cf. util.lisp) - (let ((objects NIL)) + (let ((objects NIL) (copy-fn (build-symbol "copy-" object-type))) (dolist (n name-list objects) - (setf objects (cons (get-game-object object-type n) objects))))) + (if (stringp n) + (setf objects + (cons (funcall copy-fn (get-game-object object-type n)) + objects)) + (setf objects (cons n objects)))))) (defun objectify-place-monsters (place) "Objectify all the monsters in this place" - ;; XXX This introduces a side effect! - (let* ((p (if (place-p place) place (get-game-object 'place place))) - (monster-list (objectify-name-list 'monster (place-monster p)))) - (if (monster-p (first (place-monster p))) - (return-from objectify-place-monsters place) - (setf (place-monster p) monster-list)))) + (let* ((p (if (place-p place) place (get-game-object 'place place)))) + (setf (place-monster p) + (objectify-name-list 'monster (place-monster p))) + p)) + (let ((list-function (make-list-function 'place NIL))) (defun list-place-objects (object-type place) "Get a list of the names of all the place's objects of this type." @@ -118,3 +132,28 @@ (npc-description (get-game-object 'npc object-name))) (t NIL)))) +(defun run-game-function (function player) + "Execute this game function" + (let* ((fn (if (game-function-p function) function + (get-game-object 'game-function function))) + (player (if (player-p player) player + (get-game-object 'player player))) + (place (get-game-object 'place (player-place player)))) + (dolist (game-obj (list player place)) + ;; Iterate through each element in the function that modifies + ;; this game object + (dolist (element (funcall (build-symbol "game-function-" + (type-of game-obj)) fn)) + (let* ((element (if (listp element) element (list element))) + (attr (first element)) (value (second element)) + ;; FIXME +1 gets transformed to "1"... + (mod (aref (to-string value) 0)) + (orig-value (funcall (build-symbol (type-of game-obj) + #\- attr) game-obj))) + ;; Update the value of the specified attribute + (if (= (length element) 1) + (set-object-attribute game-obj attr T) + (if (or (eq mod #\+) (eq mod #\-)) + (set-object-attribute game-obj attr + (+ orig-value value)) + (set-object-attribute game-obj attr value)))))))) diff --git a/lisp/interpreter.lisp b/lisp/interpreter.lisp index 76d2dc9..d2edd72 100644 --- a/lisp/interpreter.lisp +++ b/lisp/interpreter.lisp @@ -38,6 +38,7 @@ (defcommand define-item item) (defcommand define-npc npc) (defcommand define-quest quest) +(defcommand define-function game-function) (let ((world-directory NIL) @@ -90,7 +91,6 @@ (position #\space line)))))) (error "~&ERROR: unrecognized syntax: '~A'" line)))) ;; interpret an option command - ;; TODO allow binary options (options without an argument) ((or (eql (aref line 0) #\Space) (eql (aref line 0) #\Tab)) (let ((options (extract-elements line))) @@ -99,9 +99,15 @@ (first options) T)) (2 (set-object-attribute current-object (first options) (second options))) + (3 (if (game-function-p current-object) + (set-object-attribute current-object + (first options) (cdr options)) + (error "~&ERROR: too many arguments: '~A'" + line))) ;; FIXME gives problems with lines like this: ;; " ;commented" - (t (error "~&Too many arguments: '~A'" line))))) + (t (error "~&ERROR: too many arguments: '~A'" + line))))) (T ;; can't happen (error "~&ERROR: unrecognized syntax: '~A'" line)))))) diff --git a/lisp/player.lisp b/lisp/player.lisp index ce00de2..fac2547 100644 --- a/lisp/player.lisp +++ b/lisp/player.lisp @@ -27,7 +27,7 @@ (level 0) (max-health 50) (health 50) - (night-vision NIL) + (night-vision NIL) ;This needs to be shifted into a list of abilities (game-admin NIL)) ;; How many XP are needed to level up? diff --git a/lisp/ui.lisp b/lisp/ui.lisp index 2bdc02b..9723339 100644 --- a/lisp/ui.lisp +++ b/lisp/ui.lisp @@ -85,6 +85,7 @@ "Print out a complete description of place p" ;; XXX This has become slightly ugly with the addition of darkness... (when (stringp p) (setf p (get-game-object 'place p))) + (objectify-place-monsters p) (format t "~&~A" (string-upcase (place-name p))) (format t "~&~%~A" (if (and (place-dark p) (not show-dark)) "You do not see a thing in here. It's too dark!" @@ -119,7 +120,7 @@ '(help place player goto pickup drop talk trade - equip attack + equip attack spell about save clear)) ;;; The following commands don't take any arguments except for a player @@ -167,6 +168,8 @@ (player-intelligence p) tab (player-strength p)) (format t "~&Constitution: ~A~ADexterity: ~A" (player-constitution p) tab (player-dexterity p)) + (format t "~&Night vision: ~A" + (if (player-night-vision p) "yes" "no")) (format t "~&=====") (format t "~&Weapon: ~A" (player-weapon p)) ;; XXX This will need adjusting for large item numbers @@ -368,7 +371,10 @@ (format t "~&This monster is not here!") (return-from attack)) ;; Bind all relevant values to variables (saves typing later) - (let* ((monster (get-game-object 'monster opponent)) + (let* ((place (get-game-object 'place (player-place player))) + (monster (dolist (m (place-monster place)) + (when (equalp (monster-name m) opponent) + (return m)))) (m-str (monster-strength monster)) (m-dex (monster-dexterity monster)) (m-ac (monster-armour-class monster)) @@ -393,8 +399,7 @@ (format t "~&You hit! ~A points damage." damage) (when (> 1 (monster-health monster)) (let ((experience (round (average m-str m-dex)))) - (remove-object-attribute - (get-game-object 'place (player-place player)) + (remove-object-attribute place 'monster monster) (add-player-experience player experience) (format t "~&You killed the monster! ") @@ -413,3 +418,16 @@ (decf damage (random def-dex)) (decf damage def-ac) (if (minusp damage) 0 damage))) + +(defun spell (player &optional spell) + "Call this game function" + ;; TODO Remove this/change it so that not all game-functions can be called + (cond ((null spell) + (format t "~&Please specify a spell to cast!") + (return-from spell)) + ((not (member spell (list-world-objects 'game-function) :test #'equalp)) + (format t "~&This spell does not exist!") + (return-from spell)) + (t (format t "~&~A" (game-function-print + (get-game-object 'game-function spell))) + (run-game-function spell player)))) diff --git a/ATL/atl-mode.el b/ATL/atl-mode.el index 406fda4..d0eaa0d 100644 --- a/ATL/atl-mode.el +++ b/ATL/atl-mode.el @@ -14,7 +14,7 @@ '("define-place" "define-item" "define-monster" "define-npc" "define-race" "define-place" "define-weapon" "define-class" "define-player" "define-quest" - "name-world" "load-file") + "define-function" "name-world" "load-file") '() ;; other commands (adjust this?) '("\\.atl$") ;; files for which to activate this mode '(linum-mode) ;; other functions to call diff --git a/ATL/test/lisp-test.atl b/ATL/test/lisp-test.atl index b68d944..eed1c62 100644 --- a/ATL/test/lisp-test.atl +++ b/ATL/test/lisp-test.atl @@ -45,6 +45,21 @@ item "Anaklusmos" npc "Charon" +define-function "Light" + docstring "Gives a player night-vision" + player night-vision T + print "You can now see in the dark!" + +define-function "Spawn Fury" + docstring "Spawn a Fury in this place" + place monster "Fury" + print "Watch out! A Kindly One!" + +define-function "Heal" + docstring "Heal the player by 1HP" + player health +1 + print "+1 HP" + load-file lisp-test.atl ;Testing whether recursive loading is prevented load-file races-classes.atl load-file game-objects.atl diff --git a/lisp/atlantis.lisp b/lisp/atlantis.lisp index cb1958c..226fddc 100644 --- a/lisp/atlantis.lisp +++ b/lisp/atlantis.lisp @@ -29,7 +29,8 @@ :strength 6 :constitution 12 :dexterity 19 :intelligence 14 :money 100 :game-admin T - :weapon "Anaklusmos"))) + :item '("Anaklusmos") + :weapon "Lightning bolt"))) (add-player player) (play-game (player-name player)))) diff --git a/lisp/game-objects.lisp b/lisp/game-objects.lisp index cb5c5f3..697e2e8 100644 --- a/lisp/game-objects.lisp +++ b/lisp/game-objects.lisp @@ -19,7 +19,8 @@ (item NIL) (monster NIL) (npc NIL) - (dark NIL)) + (dark NIL) + (function "")) ;;; WORK IN PROGRESS >>> @@ -63,6 +64,13 @@ (money 0) (experience 0)) +(defstruct game-function + (name "") + (docstring "") + (place NIL) + (player NIL) + (print "")) + (defun set-object-attribute (game-object property value) "Set the attribute 'property' of 'game-object' to 'value'" ;; Here follows Lisp magic :D (that took ages to get right...) @@ -79,29 +87,35 @@ "Remove 'value' from the attribute 'property' in 'game-object'" ;; Same comment applies as above (let ((command (build-symbol (type-of game-object) "-" property))) - (eval `(if (listp (,command ,game-object)) - (setf (,command ,game-object) - (remove-first-if #'(lambda (x) (equalp x ,value)) - (,command ,game-object))) - ;; TODO set numbers to 0, strings to "" - (setf (,command ,game-object) NIL))))) + (eval `(cond ((listp (,command ,game-object)) + (setf (,command ,game-object) + (remove-first-if #'(lambda (x) (equalp x ,value)) + (,command ,game-object)))) + ((numberp (,command ,game-object)) + (setf (,command ,game-object) 0)) + ((stringp (,command ,game-object)) + (setf (,command ,game-object) "")) + (t (setf (,command ,game-object) NIL)))))) (defun objectify-name-list (object-type name-list) "Turn all the string names in name-list into instances of the object type" ;; Basically the inverse of a make-list-function function (cf. util.lisp) - (let ((objects NIL)) + (let ((objects NIL) (copy-fn (build-symbol "copy-" object-type))) (dolist (n name-list objects) - (setf objects (cons (get-game-object object-type n) objects))))) + (if (stringp n) + (setf objects + (cons (funcall copy-fn (get-game-object object-type n)) + objects)) + (setf objects (cons n objects)))))) (defun objectify-place-monsters (place) "Objectify all the monsters in this place" - ;; XXX This introduces a side effect! - (let* ((p (if (place-p place) place (get-game-object 'place place))) - (monster-list (objectify-name-list 'monster (place-monster p)))) - (if (monster-p (first (place-monster p))) - (return-from objectify-place-monsters place) - (setf (place-monster p) monster-list)))) + (let* ((p (if (place-p place) place (get-game-object 'place place)))) + (setf (place-monster p) + (objectify-name-list 'monster (place-monster p))) + p)) + (let ((list-function (make-list-function 'place NIL))) (defun list-place-objects (object-type place) "Get a list of the names of all the place's objects of this type." @@ -118,3 +132,28 @@ (npc-description (get-game-object 'npc object-name))) (t NIL)))) +(defun run-game-function (function player) + "Execute this game function" + (let* ((fn (if (game-function-p function) function + (get-game-object 'game-function function))) + (player (if (player-p player) player + (get-game-object 'player player))) + (place (get-game-object 'place (player-place player)))) + (dolist (game-obj (list player place)) + ;; Iterate through each element in the function that modifies + ;; this game object + (dolist (element (funcall (build-symbol "game-function-" + (type-of game-obj)) fn)) + (let* ((element (if (listp element) element (list element))) + (attr (first element)) (value (second element)) + ;; FIXME +1 gets transformed to "1"... + (mod (aref (to-string value) 0)) + (orig-value (funcall (build-symbol (type-of game-obj) + #\- attr) game-obj))) + ;; Update the value of the specified attribute + (if (= (length element) 1) + (set-object-attribute game-obj attr T) + (if (or (eq mod #\+) (eq mod #\-)) + (set-object-attribute game-obj attr + (+ orig-value value)) + (set-object-attribute game-obj attr value)))))))) diff --git a/lisp/interpreter.lisp b/lisp/interpreter.lisp index 76d2dc9..d2edd72 100644 --- a/lisp/interpreter.lisp +++ b/lisp/interpreter.lisp @@ -38,6 +38,7 @@ (defcommand define-item item) (defcommand define-npc npc) (defcommand define-quest quest) +(defcommand define-function game-function) (let ((world-directory NIL) @@ -90,7 +91,6 @@ (position #\space line)))))) (error "~&ERROR: unrecognized syntax: '~A'" line)))) ;; interpret an option command - ;; TODO allow binary options (options without an argument) ((or (eql (aref line 0) #\Space) (eql (aref line 0) #\Tab)) (let ((options (extract-elements line))) @@ -99,9 +99,15 @@ (first options) T)) (2 (set-object-attribute current-object (first options) (second options))) + (3 (if (game-function-p current-object) + (set-object-attribute current-object + (first options) (cdr options)) + (error "~&ERROR: too many arguments: '~A'" + line))) ;; FIXME gives problems with lines like this: ;; " ;commented" - (t (error "~&Too many arguments: '~A'" line))))) + (t (error "~&ERROR: too many arguments: '~A'" + line))))) (T ;; can't happen (error "~&ERROR: unrecognized syntax: '~A'" line)))))) diff --git a/lisp/player.lisp b/lisp/player.lisp index ce00de2..fac2547 100644 --- a/lisp/player.lisp +++ b/lisp/player.lisp @@ -27,7 +27,7 @@ (level 0) (max-health 50) (health 50) - (night-vision NIL) + (night-vision NIL) ;This needs to be shifted into a list of abilities (game-admin NIL)) ;; How many XP are needed to level up? diff --git a/lisp/ui.lisp b/lisp/ui.lisp index 2bdc02b..9723339 100644 --- a/lisp/ui.lisp +++ b/lisp/ui.lisp @@ -85,6 +85,7 @@ "Print out a complete description of place p" ;; XXX This has become slightly ugly with the addition of darkness... (when (stringp p) (setf p (get-game-object 'place p))) + (objectify-place-monsters p) (format t "~&~A" (string-upcase (place-name p))) (format t "~&~%~A" (if (and (place-dark p) (not show-dark)) "You do not see a thing in here. It's too dark!" @@ -119,7 +120,7 @@ '(help place player goto pickup drop talk trade - equip attack + equip attack spell about save clear)) ;;; The following commands don't take any arguments except for a player @@ -167,6 +168,8 @@ (player-intelligence p) tab (player-strength p)) (format t "~&Constitution: ~A~ADexterity: ~A" (player-constitution p) tab (player-dexterity p)) + (format t "~&Night vision: ~A" + (if (player-night-vision p) "yes" "no")) (format t "~&=====") (format t "~&Weapon: ~A" (player-weapon p)) ;; XXX This will need adjusting for large item numbers @@ -368,7 +371,10 @@ (format t "~&This monster is not here!") (return-from attack)) ;; Bind all relevant values to variables (saves typing later) - (let* ((monster (get-game-object 'monster opponent)) + (let* ((place (get-game-object 'place (player-place player))) + (monster (dolist (m (place-monster place)) + (when (equalp (monster-name m) opponent) + (return m)))) (m-str (monster-strength monster)) (m-dex (monster-dexterity monster)) (m-ac (monster-armour-class monster)) @@ -393,8 +399,7 @@ (format t "~&You hit! ~A points damage." damage) (when (> 1 (monster-health monster)) (let ((experience (round (average m-str m-dex)))) - (remove-object-attribute - (get-game-object 'place (player-place player)) + (remove-object-attribute place 'monster monster) (add-player-experience player experience) (format t "~&You killed the monster! ") @@ -413,3 +418,16 @@ (decf damage (random def-dex)) (decf damage def-ac) (if (minusp damage) 0 damage))) + +(defun spell (player &optional spell) + "Call this game function" + ;; TODO Remove this/change it so that not all game-functions can be called + (cond ((null spell) + (format t "~&Please specify a spell to cast!") + (return-from spell)) + ((not (member spell (list-world-objects 'game-function) :test #'equalp)) + (format t "~&This spell does not exist!") + (return-from spell)) + (t (format t "~&~A" (game-function-print + (get-game-object 'game-function spell))) + (run-game-function spell player)))) diff --git a/lisp/world.lisp b/lisp/world.lisp index 02b33c3..29c6346 100644 --- a/lisp/world.lisp +++ b/lisp/world.lisp @@ -27,8 +27,7 @@ (items NIL) (weapons NIL) (quests NIL) - (starting-place "") - (starting-money 0)) + (game-functions NIL)) (setf *world* (make-world)) ;XXX Move this to another module?