diff --git a/lisp/game-objects.lisp b/lisp/game-objects.lisp index 080bc7e..585cc9b 100644 --- a/lisp/game-objects.lisp +++ b/lisp/game-objects.lisp @@ -35,6 +35,7 @@ (strength 0) (dexterity 0) (aggression 0) + (spawn-probability 0) (weapon "") (armour-class 0)) @@ -48,7 +49,7 @@ (defstruct weapon (name "") (description "") - (type "generic") + (type "") (damage 0)) @@ -69,21 +70,29 @@ ;; Same comment applies as above (let ((command (build-symbol (type-of game-object) "-" property))) (eval `(if (listp (,command ,game-object)) - ;; FIXME This gives problems with multiple values (setf (,command ,game-object) - (remove-if #'(lambda (x) (equalp x ,value)) + (remove-first-if #'(lambda (x) (equalp x ,value)) (,command ,game-object))) ;; TODO set numbers to 0, strings to "" (setf (,command ,game-object) NIL))))) -;; XXX This function is probably superfluous, as all place objects are stored -;; in world, with only their names recorded in the place +(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)) + (dolist (n name-list objects) + (setf objects (cons (get-game-object object-type 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 ((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." (funcall list-function object-type place))) - -(defun monster-strikes-player (monster player) - "The monster attacks a player" - ;;TODO - ) diff --git a/lisp/game-objects.lisp b/lisp/game-objects.lisp index 080bc7e..585cc9b 100644 --- a/lisp/game-objects.lisp +++ b/lisp/game-objects.lisp @@ -35,6 +35,7 @@ (strength 0) (dexterity 0) (aggression 0) + (spawn-probability 0) (weapon "") (armour-class 0)) @@ -48,7 +49,7 @@ (defstruct weapon (name "") (description "") - (type "generic") + (type "") (damage 0)) @@ -69,21 +70,29 @@ ;; Same comment applies as above (let ((command (build-symbol (type-of game-object) "-" property))) (eval `(if (listp (,command ,game-object)) - ;; FIXME This gives problems with multiple values (setf (,command ,game-object) - (remove-if #'(lambda (x) (equalp x ,value)) + (remove-first-if #'(lambda (x) (equalp x ,value)) (,command ,game-object))) ;; TODO set numbers to 0, strings to "" (setf (,command ,game-object) NIL))))) -;; XXX This function is probably superfluous, as all place objects are stored -;; in world, with only their names recorded in the place +(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)) + (dolist (n name-list objects) + (setf objects (cons (get-game-object object-type 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 ((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." (funcall list-function object-type place))) - -(defun monster-strikes-player (monster player) - "The monster attacks a player" - ;;TODO - ) diff --git a/lisp/interpreter.lisp b/lisp/interpreter.lisp index 8f69337..03d4886 100644 --- a/lisp/interpreter.lisp +++ b/lisp/interpreter.lisp @@ -99,7 +99,7 @@ ;; interpret an option command ((or (eql (aref line 0) #\Space) (eql (aref line 0) #\Tab)) - (setf line (string-left-trim '(#\Space #\Tab) line)) + (setf line (trim-whitespace line)) (set-object-attribute current-object (read-from-string line) (read-from-string (second (cut-string line (position #\space line)))))) diff --git a/lisp/game-objects.lisp b/lisp/game-objects.lisp index 080bc7e..585cc9b 100644 --- a/lisp/game-objects.lisp +++ b/lisp/game-objects.lisp @@ -35,6 +35,7 @@ (strength 0) (dexterity 0) (aggression 0) + (spawn-probability 0) (weapon "") (armour-class 0)) @@ -48,7 +49,7 @@ (defstruct weapon (name "") (description "") - (type "generic") + (type "") (damage 0)) @@ -69,21 +70,29 @@ ;; Same comment applies as above (let ((command (build-symbol (type-of game-object) "-" property))) (eval `(if (listp (,command ,game-object)) - ;; FIXME This gives problems with multiple values (setf (,command ,game-object) - (remove-if #'(lambda (x) (equalp x ,value)) + (remove-first-if #'(lambda (x) (equalp x ,value)) (,command ,game-object))) ;; TODO set numbers to 0, strings to "" (setf (,command ,game-object) NIL))))) -;; XXX This function is probably superfluous, as all place objects are stored -;; in world, with only their names recorded in the place +(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)) + (dolist (n name-list objects) + (setf objects (cons (get-game-object object-type 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 ((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." (funcall list-function object-type place))) - -(defun monster-strikes-player (monster player) - "The monster attacks a player" - ;;TODO - ) diff --git a/lisp/interpreter.lisp b/lisp/interpreter.lisp index 8f69337..03d4886 100644 --- a/lisp/interpreter.lisp +++ b/lisp/interpreter.lisp @@ -99,7 +99,7 @@ ;; interpret an option command ((or (eql (aref line 0) #\Space) (eql (aref line 0) #\Tab)) - (setf line (string-left-trim '(#\Space #\Tab) line)) + (setf line (trim-whitespace line)) (set-object-attribute current-object (read-from-string line) (read-from-string (second (cut-string line (position #\space line)))))) diff --git a/lisp/ui.lisp b/lisp/ui.lisp index 0fafe21..7d0c25d 100644 --- a/lisp/ui.lisp +++ b/lisp/ui.lisp @@ -25,6 +25,7 @@ (add-game-object player) (set-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player))) + (objectify-place-monsters (player-place player)) ;; The actual game loop (clear-screen) (let ((place (get-game-object 'place (player-place player)))) @@ -93,7 +94,8 @@ (format t "~&Players present: ~A" (string-from-list (place-player p))) (format t "~&Items: ~A" (string-from-list (place-item p))) (format t "~&NPCs: ~A" (string-from-list (place-npc p))) - (format t "~&Monsters: ~A" (string-from-list (place-monster p)))) + (format t "~&Monsters: ~A" (string-from-list + (list-place-objects 'monster p)))) (defun game-command (cmd player) "Execute a typed-in game command" @@ -194,6 +196,7 @@ (defun goto (player &optional location) "Go to the specified location" + ;; Look before you leap (unless location (format t "~&Please specify a location!") (return-from goto)) @@ -204,12 +207,14 @@ :test #'equalp)) (format t "~&This place does not border your current location!") (return-from goto)) + ;; Do all the necessary housekeeping (clear-screen) (debugging "~&~A is going to ~A." (player-name player) location) + (objectify-place-monsters location) (remove-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player)) (set-object-attribute player 'place location) - (set-object-attribute (get-game-object 'place (player-place player)) + (set-object-attribute (get-game-object 'place location) 'player (player-name player)) (describe-place location)) @@ -226,9 +231,9 @@ (let ((place (get-game-object 'place (player-place player))) (description NIL)) (macrolet ((set-descr (type) - (let ((place-descr (build-symbol type "-description")) - (place-object (build-symbol "place-" type))) - `(when (member object-name (,place-object place) + (let ((place-descr (build-symbol type "-description"))) + `(when (member object-name + (list-place-objects ',type place) :test #'equalp) (setf description (,place-descr (get-game-object ',type @@ -344,7 +349,8 @@ (format t "~&Please specify an opponent!") (return-from attack)) (unless (member opponent - (list-place-objects 'monster (player-place player)) + (list-place-objects 'monster + (get-game-object 'place (player-place player))) :test #'equalp) (format t "~&This monster is not here!") (return-from attack)) @@ -363,7 +369,6 @@ (if (> (+ (random 10) p-dex) (+ (random 10) m-dex)) (setf damage (calculate-damage p-str p-weapon m-dex m-ac)) (setf damage (- 0 (calculate-damage m-str m-weapon p-dex p-ac)))) - ;(break) (cond ((plusp damage) (decf (monster-health monster) damage) (format t "~&You hit! ~A points damage." damage) diff --git a/lisp/game-objects.lisp b/lisp/game-objects.lisp index 080bc7e..585cc9b 100644 --- a/lisp/game-objects.lisp +++ b/lisp/game-objects.lisp @@ -35,6 +35,7 @@ (strength 0) (dexterity 0) (aggression 0) + (spawn-probability 0) (weapon "") (armour-class 0)) @@ -48,7 +49,7 @@ (defstruct weapon (name "") (description "") - (type "generic") + (type "") (damage 0)) @@ -69,21 +70,29 @@ ;; Same comment applies as above (let ((command (build-symbol (type-of game-object) "-" property))) (eval `(if (listp (,command ,game-object)) - ;; FIXME This gives problems with multiple values (setf (,command ,game-object) - (remove-if #'(lambda (x) (equalp x ,value)) + (remove-first-if #'(lambda (x) (equalp x ,value)) (,command ,game-object))) ;; TODO set numbers to 0, strings to "" (setf (,command ,game-object) NIL))))) -;; XXX This function is probably superfluous, as all place objects are stored -;; in world, with only their names recorded in the place +(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)) + (dolist (n name-list objects) + (setf objects (cons (get-game-object object-type 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 ((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." (funcall list-function object-type place))) - -(defun monster-strikes-player (monster player) - "The monster attacks a player" - ;;TODO - ) diff --git a/lisp/interpreter.lisp b/lisp/interpreter.lisp index 8f69337..03d4886 100644 --- a/lisp/interpreter.lisp +++ b/lisp/interpreter.lisp @@ -99,7 +99,7 @@ ;; interpret an option command ((or (eql (aref line 0) #\Space) (eql (aref line 0) #\Tab)) - (setf line (string-left-trim '(#\Space #\Tab) line)) + (setf line (trim-whitespace line)) (set-object-attribute current-object (read-from-string line) (read-from-string (second (cut-string line (position #\space line)))))) diff --git a/lisp/ui.lisp b/lisp/ui.lisp index 0fafe21..7d0c25d 100644 --- a/lisp/ui.lisp +++ b/lisp/ui.lisp @@ -25,6 +25,7 @@ (add-game-object player) (set-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player))) + (objectify-place-monsters (player-place player)) ;; The actual game loop (clear-screen) (let ((place (get-game-object 'place (player-place player)))) @@ -93,7 +94,8 @@ (format t "~&Players present: ~A" (string-from-list (place-player p))) (format t "~&Items: ~A" (string-from-list (place-item p))) (format t "~&NPCs: ~A" (string-from-list (place-npc p))) - (format t "~&Monsters: ~A" (string-from-list (place-monster p)))) + (format t "~&Monsters: ~A" (string-from-list + (list-place-objects 'monster p)))) (defun game-command (cmd player) "Execute a typed-in game command" @@ -194,6 +196,7 @@ (defun goto (player &optional location) "Go to the specified location" + ;; Look before you leap (unless location (format t "~&Please specify a location!") (return-from goto)) @@ -204,12 +207,14 @@ :test #'equalp)) (format t "~&This place does not border your current location!") (return-from goto)) + ;; Do all the necessary housekeeping (clear-screen) (debugging "~&~A is going to ~A." (player-name player) location) + (objectify-place-monsters location) (remove-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player)) (set-object-attribute player 'place location) - (set-object-attribute (get-game-object 'place (player-place player)) + (set-object-attribute (get-game-object 'place location) 'player (player-name player)) (describe-place location)) @@ -226,9 +231,9 @@ (let ((place (get-game-object 'place (player-place player))) (description NIL)) (macrolet ((set-descr (type) - (let ((place-descr (build-symbol type "-description")) - (place-object (build-symbol "place-" type))) - `(when (member object-name (,place-object place) + (let ((place-descr (build-symbol type "-description"))) + `(when (member object-name + (list-place-objects ',type place) :test #'equalp) (setf description (,place-descr (get-game-object ',type @@ -344,7 +349,8 @@ (format t "~&Please specify an opponent!") (return-from attack)) (unless (member opponent - (list-place-objects 'monster (player-place player)) + (list-place-objects 'monster + (get-game-object 'place (player-place player))) :test #'equalp) (format t "~&This monster is not here!") (return-from attack)) @@ -363,7 +369,6 @@ (if (> (+ (random 10) p-dex) (+ (random 10) m-dex)) (setf damage (calculate-damage p-str p-weapon m-dex m-ac)) (setf damage (- 0 (calculate-damage m-str m-weapon p-dex p-ac)))) - ;(break) (cond ((plusp damage) (decf (monster-health monster) damage) (format t "~&You hit! ~A points damage." damage) diff --git a/lisp/util.lisp b/lisp/util.lisp index 3ad8306..3ce44e3 100644 --- a/lisp/util.lisp +++ b/lisp/util.lisp @@ -98,13 +98,13 @@ ;;; FUNCTIONS -; Some of these functions are probably quite inefficient (lots of consing) +;; Some of these functions are probably quite inefficient (lots of consing) -;; XXX DEPRECATED Not actually needed anywhere -(defun call-function (function-name &rest args) - "Save myself some quoting when calling a function from a generated symbol" - ;; Perhaps not very clean, but it works - (eval `(,function-name ,@args))) +(defun remove-first-if (fn lst) + "Remove the first element in a list that satisfies the given predicate" + (cond ((null lst) NIL) + ((funcall fn (car lst)) (cdr lst)) + (T (cons (car lst) (remove-first-if fn (cdr lst)))))) (defun average (&rest numbers) "Compute the average of the given numbers" @@ -205,17 +205,19 @@ (read-from-string (string-from-list components ""))) (defun make-list-function (container-type &optional (add-s t)) - "Return function to return a list of the names of all objects of the + "Return a function to return a list of the names of all objects of the specified type in the container struct" #'(lambda (object-type container) - (let ((get-objects (symbol-function (build-symbol container-type "-" - object-type (if add-s "s" "")))) - (get-object-name (symbol-function - (build-symbol object-type "-name"))) - (name-list NIL)) - (dolist (object (funcall get-objects container) name-list) + (let* ((get-objects (symbol-function + (build-symbol container-type "-" + object-type (if add-s "s" "")))) + (get-object-name (symbol-function + (build-symbol object-type "-name"))) + (objects (funcall get-objects container)) (name-list NIL)) + (dolist (o objects name-list) + (when (stringp o) (return objects)) (setf name-list - (cons (funcall get-object-name object) name-list)))))) + (cons (funcall get-object-name o) name-list)))))) (defun choose-number-option (option-list) "The user chooses one out of a list of options, the index is returned" diff --git a/lisp/game-objects.lisp b/lisp/game-objects.lisp index 080bc7e..585cc9b 100644 --- a/lisp/game-objects.lisp +++ b/lisp/game-objects.lisp @@ -35,6 +35,7 @@ (strength 0) (dexterity 0) (aggression 0) + (spawn-probability 0) (weapon "") (armour-class 0)) @@ -48,7 +49,7 @@ (defstruct weapon (name "") (description "") - (type "generic") + (type "") (damage 0)) @@ -69,21 +70,29 @@ ;; Same comment applies as above (let ((command (build-symbol (type-of game-object) "-" property))) (eval `(if (listp (,command ,game-object)) - ;; FIXME This gives problems with multiple values (setf (,command ,game-object) - (remove-if #'(lambda (x) (equalp x ,value)) + (remove-first-if #'(lambda (x) (equalp x ,value)) (,command ,game-object))) ;; TODO set numbers to 0, strings to "" (setf (,command ,game-object) NIL))))) -;; XXX This function is probably superfluous, as all place objects are stored -;; in world, with only their names recorded in the place +(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)) + (dolist (n name-list objects) + (setf objects (cons (get-game-object object-type 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 ((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." (funcall list-function object-type place))) - -(defun monster-strikes-player (monster player) - "The monster attacks a player" - ;;TODO - ) diff --git a/lisp/interpreter.lisp b/lisp/interpreter.lisp index 8f69337..03d4886 100644 --- a/lisp/interpreter.lisp +++ b/lisp/interpreter.lisp @@ -99,7 +99,7 @@ ;; interpret an option command ((or (eql (aref line 0) #\Space) (eql (aref line 0) #\Tab)) - (setf line (string-left-trim '(#\Space #\Tab) line)) + (setf line (trim-whitespace line)) (set-object-attribute current-object (read-from-string line) (read-from-string (second (cut-string line (position #\space line)))))) diff --git a/lisp/ui.lisp b/lisp/ui.lisp index 0fafe21..7d0c25d 100644 --- a/lisp/ui.lisp +++ b/lisp/ui.lisp @@ -25,6 +25,7 @@ (add-game-object player) (set-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player))) + (objectify-place-monsters (player-place player)) ;; The actual game loop (clear-screen) (let ((place (get-game-object 'place (player-place player)))) @@ -93,7 +94,8 @@ (format t "~&Players present: ~A" (string-from-list (place-player p))) (format t "~&Items: ~A" (string-from-list (place-item p))) (format t "~&NPCs: ~A" (string-from-list (place-npc p))) - (format t "~&Monsters: ~A" (string-from-list (place-monster p)))) + (format t "~&Monsters: ~A" (string-from-list + (list-place-objects 'monster p)))) (defun game-command (cmd player) "Execute a typed-in game command" @@ -194,6 +196,7 @@ (defun goto (player &optional location) "Go to the specified location" + ;; Look before you leap (unless location (format t "~&Please specify a location!") (return-from goto)) @@ -204,12 +207,14 @@ :test #'equalp)) (format t "~&This place does not border your current location!") (return-from goto)) + ;; Do all the necessary housekeeping (clear-screen) (debugging "~&~A is going to ~A." (player-name player) location) + (objectify-place-monsters location) (remove-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player)) (set-object-attribute player 'place location) - (set-object-attribute (get-game-object 'place (player-place player)) + (set-object-attribute (get-game-object 'place location) 'player (player-name player)) (describe-place location)) @@ -226,9 +231,9 @@ (let ((place (get-game-object 'place (player-place player))) (description NIL)) (macrolet ((set-descr (type) - (let ((place-descr (build-symbol type "-description")) - (place-object (build-symbol "place-" type))) - `(when (member object-name (,place-object place) + (let ((place-descr (build-symbol type "-description"))) + `(when (member object-name + (list-place-objects ',type place) :test #'equalp) (setf description (,place-descr (get-game-object ',type @@ -344,7 +349,8 @@ (format t "~&Please specify an opponent!") (return-from attack)) (unless (member opponent - (list-place-objects 'monster (player-place player)) + (list-place-objects 'monster + (get-game-object 'place (player-place player))) :test #'equalp) (format t "~&This monster is not here!") (return-from attack)) @@ -363,7 +369,6 @@ (if (> (+ (random 10) p-dex) (+ (random 10) m-dex)) (setf damage (calculate-damage p-str p-weapon m-dex m-ac)) (setf damage (- 0 (calculate-damage m-str m-weapon p-dex p-ac)))) - ;(break) (cond ((plusp damage) (decf (monster-health monster) damage) (format t "~&You hit! ~A points damage." damage) diff --git a/lisp/util.lisp b/lisp/util.lisp index 3ad8306..3ce44e3 100644 --- a/lisp/util.lisp +++ b/lisp/util.lisp @@ -98,13 +98,13 @@ ;;; FUNCTIONS -; Some of these functions are probably quite inefficient (lots of consing) +;; Some of these functions are probably quite inefficient (lots of consing) -;; XXX DEPRECATED Not actually needed anywhere -(defun call-function (function-name &rest args) - "Save myself some quoting when calling a function from a generated symbol" - ;; Perhaps not very clean, but it works - (eval `(,function-name ,@args))) +(defun remove-first-if (fn lst) + "Remove the first element in a list that satisfies the given predicate" + (cond ((null lst) NIL) + ((funcall fn (car lst)) (cdr lst)) + (T (cons (car lst) (remove-first-if fn (cdr lst)))))) (defun average (&rest numbers) "Compute the average of the given numbers" @@ -205,17 +205,19 @@ (read-from-string (string-from-list components ""))) (defun make-list-function (container-type &optional (add-s t)) - "Return function to return a list of the names of all objects of the + "Return a function to return a list of the names of all objects of the specified type in the container struct" #'(lambda (object-type container) - (let ((get-objects (symbol-function (build-symbol container-type "-" - object-type (if add-s "s" "")))) - (get-object-name (symbol-function - (build-symbol object-type "-name"))) - (name-list NIL)) - (dolist (object (funcall get-objects container) name-list) + (let* ((get-objects (symbol-function + (build-symbol container-type "-" + object-type (if add-s "s" "")))) + (get-object-name (symbol-function + (build-symbol object-type "-name"))) + (objects (funcall get-objects container)) (name-list NIL)) + (dolist (o objects name-list) + (when (stringp o) (return objects)) (setf name-list - (cons (funcall get-object-name object) name-list)))))) + (cons (funcall get-object-name o) name-list)))))) (defun choose-number-option (option-list) "The user chooses one out of a list of options, the index is returned" diff --git a/python/__pycache__/character.cpython-34.pyc b/python/__pycache__/character.cpython-34.pyc deleted file mode 100644 index 20a9d5a..0000000 --- a/python/__pycache__/character.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/lisp/game-objects.lisp b/lisp/game-objects.lisp index 080bc7e..585cc9b 100644 --- a/lisp/game-objects.lisp +++ b/lisp/game-objects.lisp @@ -35,6 +35,7 @@ (strength 0) (dexterity 0) (aggression 0) + (spawn-probability 0) (weapon "") (armour-class 0)) @@ -48,7 +49,7 @@ (defstruct weapon (name "") (description "") - (type "generic") + (type "") (damage 0)) @@ -69,21 +70,29 @@ ;; Same comment applies as above (let ((command (build-symbol (type-of game-object) "-" property))) (eval `(if (listp (,command ,game-object)) - ;; FIXME This gives problems with multiple values (setf (,command ,game-object) - (remove-if #'(lambda (x) (equalp x ,value)) + (remove-first-if #'(lambda (x) (equalp x ,value)) (,command ,game-object))) ;; TODO set numbers to 0, strings to "" (setf (,command ,game-object) NIL))))) -;; XXX This function is probably superfluous, as all place objects are stored -;; in world, with only their names recorded in the place +(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)) + (dolist (n name-list objects) + (setf objects (cons (get-game-object object-type 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 ((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." (funcall list-function object-type place))) - -(defun monster-strikes-player (monster player) - "The monster attacks a player" - ;;TODO - ) diff --git a/lisp/interpreter.lisp b/lisp/interpreter.lisp index 8f69337..03d4886 100644 --- a/lisp/interpreter.lisp +++ b/lisp/interpreter.lisp @@ -99,7 +99,7 @@ ;; interpret an option command ((or (eql (aref line 0) #\Space) (eql (aref line 0) #\Tab)) - (setf line (string-left-trim '(#\Space #\Tab) line)) + (setf line (trim-whitespace line)) (set-object-attribute current-object (read-from-string line) (read-from-string (second (cut-string line (position #\space line)))))) diff --git a/lisp/ui.lisp b/lisp/ui.lisp index 0fafe21..7d0c25d 100644 --- a/lisp/ui.lisp +++ b/lisp/ui.lisp @@ -25,6 +25,7 @@ (add-game-object player) (set-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player))) + (objectify-place-monsters (player-place player)) ;; The actual game loop (clear-screen) (let ((place (get-game-object 'place (player-place player)))) @@ -93,7 +94,8 @@ (format t "~&Players present: ~A" (string-from-list (place-player p))) (format t "~&Items: ~A" (string-from-list (place-item p))) (format t "~&NPCs: ~A" (string-from-list (place-npc p))) - (format t "~&Monsters: ~A" (string-from-list (place-monster p)))) + (format t "~&Monsters: ~A" (string-from-list + (list-place-objects 'monster p)))) (defun game-command (cmd player) "Execute a typed-in game command" @@ -194,6 +196,7 @@ (defun goto (player &optional location) "Go to the specified location" + ;; Look before you leap (unless location (format t "~&Please specify a location!") (return-from goto)) @@ -204,12 +207,14 @@ :test #'equalp)) (format t "~&This place does not border your current location!") (return-from goto)) + ;; Do all the necessary housekeeping (clear-screen) (debugging "~&~A is going to ~A." (player-name player) location) + (objectify-place-monsters location) (remove-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player)) (set-object-attribute player 'place location) - (set-object-attribute (get-game-object 'place (player-place player)) + (set-object-attribute (get-game-object 'place location) 'player (player-name player)) (describe-place location)) @@ -226,9 +231,9 @@ (let ((place (get-game-object 'place (player-place player))) (description NIL)) (macrolet ((set-descr (type) - (let ((place-descr (build-symbol type "-description")) - (place-object (build-symbol "place-" type))) - `(when (member object-name (,place-object place) + (let ((place-descr (build-symbol type "-description"))) + `(when (member object-name + (list-place-objects ',type place) :test #'equalp) (setf description (,place-descr (get-game-object ',type @@ -344,7 +349,8 @@ (format t "~&Please specify an opponent!") (return-from attack)) (unless (member opponent - (list-place-objects 'monster (player-place player)) + (list-place-objects 'monster + (get-game-object 'place (player-place player))) :test #'equalp) (format t "~&This monster is not here!") (return-from attack)) @@ -363,7 +369,6 @@ (if (> (+ (random 10) p-dex) (+ (random 10) m-dex)) (setf damage (calculate-damage p-str p-weapon m-dex m-ac)) (setf damage (- 0 (calculate-damage m-str m-weapon p-dex p-ac)))) - ;(break) (cond ((plusp damage) (decf (monster-health monster) damage) (format t "~&You hit! ~A points damage." damage) diff --git a/lisp/util.lisp b/lisp/util.lisp index 3ad8306..3ce44e3 100644 --- a/lisp/util.lisp +++ b/lisp/util.lisp @@ -98,13 +98,13 @@ ;;; FUNCTIONS -; Some of these functions are probably quite inefficient (lots of consing) +;; Some of these functions are probably quite inefficient (lots of consing) -;; XXX DEPRECATED Not actually needed anywhere -(defun call-function (function-name &rest args) - "Save myself some quoting when calling a function from a generated symbol" - ;; Perhaps not very clean, but it works - (eval `(,function-name ,@args))) +(defun remove-first-if (fn lst) + "Remove the first element in a list that satisfies the given predicate" + (cond ((null lst) NIL) + ((funcall fn (car lst)) (cdr lst)) + (T (cons (car lst) (remove-first-if fn (cdr lst)))))) (defun average (&rest numbers) "Compute the average of the given numbers" @@ -205,17 +205,19 @@ (read-from-string (string-from-list components ""))) (defun make-list-function (container-type &optional (add-s t)) - "Return function to return a list of the names of all objects of the + "Return a function to return a list of the names of all objects of the specified type in the container struct" #'(lambda (object-type container) - (let ((get-objects (symbol-function (build-symbol container-type "-" - object-type (if add-s "s" "")))) - (get-object-name (symbol-function - (build-symbol object-type "-name"))) - (name-list NIL)) - (dolist (object (funcall get-objects container) name-list) + (let* ((get-objects (symbol-function + (build-symbol container-type "-" + object-type (if add-s "s" "")))) + (get-object-name (symbol-function + (build-symbol object-type "-name"))) + (objects (funcall get-objects container)) (name-list NIL)) + (dolist (o objects name-list) + (when (stringp o) (return objects)) (setf name-list - (cons (funcall get-object-name object) name-list)))))) + (cons (funcall get-object-name o) name-list)))))) (defun choose-number-option (option-list) "The user chooses one out of a list of options, the index is returned" diff --git a/python/__pycache__/character.cpython-34.pyc b/python/__pycache__/character.cpython-34.pyc deleted file mode 100644 index 20a9d5a..0000000 --- a/python/__pycache__/character.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/client.cpython-34.pyc b/python/__pycache__/client.cpython-34.pyc deleted file mode 100644 index 077e656..0000000 --- a/python/__pycache__/client.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/lisp/game-objects.lisp b/lisp/game-objects.lisp index 080bc7e..585cc9b 100644 --- a/lisp/game-objects.lisp +++ b/lisp/game-objects.lisp @@ -35,6 +35,7 @@ (strength 0) (dexterity 0) (aggression 0) + (spawn-probability 0) (weapon "") (armour-class 0)) @@ -48,7 +49,7 @@ (defstruct weapon (name "") (description "") - (type "generic") + (type "") (damage 0)) @@ -69,21 +70,29 @@ ;; Same comment applies as above (let ((command (build-symbol (type-of game-object) "-" property))) (eval `(if (listp (,command ,game-object)) - ;; FIXME This gives problems with multiple values (setf (,command ,game-object) - (remove-if #'(lambda (x) (equalp x ,value)) + (remove-first-if #'(lambda (x) (equalp x ,value)) (,command ,game-object))) ;; TODO set numbers to 0, strings to "" (setf (,command ,game-object) NIL))))) -;; XXX This function is probably superfluous, as all place objects are stored -;; in world, with only their names recorded in the place +(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)) + (dolist (n name-list objects) + (setf objects (cons (get-game-object object-type 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 ((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." (funcall list-function object-type place))) - -(defun monster-strikes-player (monster player) - "The monster attacks a player" - ;;TODO - ) diff --git a/lisp/interpreter.lisp b/lisp/interpreter.lisp index 8f69337..03d4886 100644 --- a/lisp/interpreter.lisp +++ b/lisp/interpreter.lisp @@ -99,7 +99,7 @@ ;; interpret an option command ((or (eql (aref line 0) #\Space) (eql (aref line 0) #\Tab)) - (setf line (string-left-trim '(#\Space #\Tab) line)) + (setf line (trim-whitespace line)) (set-object-attribute current-object (read-from-string line) (read-from-string (second (cut-string line (position #\space line)))))) diff --git a/lisp/ui.lisp b/lisp/ui.lisp index 0fafe21..7d0c25d 100644 --- a/lisp/ui.lisp +++ b/lisp/ui.lisp @@ -25,6 +25,7 @@ (add-game-object player) (set-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player))) + (objectify-place-monsters (player-place player)) ;; The actual game loop (clear-screen) (let ((place (get-game-object 'place (player-place player)))) @@ -93,7 +94,8 @@ (format t "~&Players present: ~A" (string-from-list (place-player p))) (format t "~&Items: ~A" (string-from-list (place-item p))) (format t "~&NPCs: ~A" (string-from-list (place-npc p))) - (format t "~&Monsters: ~A" (string-from-list (place-monster p)))) + (format t "~&Monsters: ~A" (string-from-list + (list-place-objects 'monster p)))) (defun game-command (cmd player) "Execute a typed-in game command" @@ -194,6 +196,7 @@ (defun goto (player &optional location) "Go to the specified location" + ;; Look before you leap (unless location (format t "~&Please specify a location!") (return-from goto)) @@ -204,12 +207,14 @@ :test #'equalp)) (format t "~&This place does not border your current location!") (return-from goto)) + ;; Do all the necessary housekeeping (clear-screen) (debugging "~&~A is going to ~A." (player-name player) location) + (objectify-place-monsters location) (remove-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player)) (set-object-attribute player 'place location) - (set-object-attribute (get-game-object 'place (player-place player)) + (set-object-attribute (get-game-object 'place location) 'player (player-name player)) (describe-place location)) @@ -226,9 +231,9 @@ (let ((place (get-game-object 'place (player-place player))) (description NIL)) (macrolet ((set-descr (type) - (let ((place-descr (build-symbol type "-description")) - (place-object (build-symbol "place-" type))) - `(when (member object-name (,place-object place) + (let ((place-descr (build-symbol type "-description"))) + `(when (member object-name + (list-place-objects ',type place) :test #'equalp) (setf description (,place-descr (get-game-object ',type @@ -344,7 +349,8 @@ (format t "~&Please specify an opponent!") (return-from attack)) (unless (member opponent - (list-place-objects 'monster (player-place player)) + (list-place-objects 'monster + (get-game-object 'place (player-place player))) :test #'equalp) (format t "~&This monster is not here!") (return-from attack)) @@ -363,7 +369,6 @@ (if (> (+ (random 10) p-dex) (+ (random 10) m-dex)) (setf damage (calculate-damage p-str p-weapon m-dex m-ac)) (setf damage (- 0 (calculate-damage m-str m-weapon p-dex p-ac)))) - ;(break) (cond ((plusp damage) (decf (monster-health monster) damage) (format t "~&You hit! ~A points damage." damage) diff --git a/lisp/util.lisp b/lisp/util.lisp index 3ad8306..3ce44e3 100644 --- a/lisp/util.lisp +++ b/lisp/util.lisp @@ -98,13 +98,13 @@ ;;; FUNCTIONS -; Some of these functions are probably quite inefficient (lots of consing) +;; Some of these functions are probably quite inefficient (lots of consing) -;; XXX DEPRECATED Not actually needed anywhere -(defun call-function (function-name &rest args) - "Save myself some quoting when calling a function from a generated symbol" - ;; Perhaps not very clean, but it works - (eval `(,function-name ,@args))) +(defun remove-first-if (fn lst) + "Remove the first element in a list that satisfies the given predicate" + (cond ((null lst) NIL) + ((funcall fn (car lst)) (cdr lst)) + (T (cons (car lst) (remove-first-if fn (cdr lst)))))) (defun average (&rest numbers) "Compute the average of the given numbers" @@ -205,17 +205,19 @@ (read-from-string (string-from-list components ""))) (defun make-list-function (container-type &optional (add-s t)) - "Return function to return a list of the names of all objects of the + "Return a function to return a list of the names of all objects of the specified type in the container struct" #'(lambda (object-type container) - (let ((get-objects (symbol-function (build-symbol container-type "-" - object-type (if add-s "s" "")))) - (get-object-name (symbol-function - (build-symbol object-type "-name"))) - (name-list NIL)) - (dolist (object (funcall get-objects container) name-list) + (let* ((get-objects (symbol-function + (build-symbol container-type "-" + object-type (if add-s "s" "")))) + (get-object-name (symbol-function + (build-symbol object-type "-name"))) + (objects (funcall get-objects container)) (name-list NIL)) + (dolist (o objects name-list) + (when (stringp o) (return objects)) (setf name-list - (cons (funcall get-object-name object) name-list)))))) + (cons (funcall get-object-name o) name-list)))))) (defun choose-number-option (option-list) "The user chooses one out of a list of options, the index is returned" diff --git a/python/__pycache__/character.cpython-34.pyc b/python/__pycache__/character.cpython-34.pyc deleted file mode 100644 index 20a9d5a..0000000 --- a/python/__pycache__/character.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/client.cpython-34.pyc b/python/__pycache__/client.cpython-34.pyc deleted file mode 100644 index 077e656..0000000 --- a/python/__pycache__/client.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/define.cpython-34.pyc b/python/__pycache__/define.cpython-34.pyc deleted file mode 100644 index 397fe49..0000000 --- a/python/__pycache__/define.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/lisp/game-objects.lisp b/lisp/game-objects.lisp index 080bc7e..585cc9b 100644 --- a/lisp/game-objects.lisp +++ b/lisp/game-objects.lisp @@ -35,6 +35,7 @@ (strength 0) (dexterity 0) (aggression 0) + (spawn-probability 0) (weapon "") (armour-class 0)) @@ -48,7 +49,7 @@ (defstruct weapon (name "") (description "") - (type "generic") + (type "") (damage 0)) @@ -69,21 +70,29 @@ ;; Same comment applies as above (let ((command (build-symbol (type-of game-object) "-" property))) (eval `(if (listp (,command ,game-object)) - ;; FIXME This gives problems with multiple values (setf (,command ,game-object) - (remove-if #'(lambda (x) (equalp x ,value)) + (remove-first-if #'(lambda (x) (equalp x ,value)) (,command ,game-object))) ;; TODO set numbers to 0, strings to "" (setf (,command ,game-object) NIL))))) -;; XXX This function is probably superfluous, as all place objects are stored -;; in world, with only their names recorded in the place +(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)) + (dolist (n name-list objects) + (setf objects (cons (get-game-object object-type 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 ((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." (funcall list-function object-type place))) - -(defun monster-strikes-player (monster player) - "The monster attacks a player" - ;;TODO - ) diff --git a/lisp/interpreter.lisp b/lisp/interpreter.lisp index 8f69337..03d4886 100644 --- a/lisp/interpreter.lisp +++ b/lisp/interpreter.lisp @@ -99,7 +99,7 @@ ;; interpret an option command ((or (eql (aref line 0) #\Space) (eql (aref line 0) #\Tab)) - (setf line (string-left-trim '(#\Space #\Tab) line)) + (setf line (trim-whitespace line)) (set-object-attribute current-object (read-from-string line) (read-from-string (second (cut-string line (position #\space line)))))) diff --git a/lisp/ui.lisp b/lisp/ui.lisp index 0fafe21..7d0c25d 100644 --- a/lisp/ui.lisp +++ b/lisp/ui.lisp @@ -25,6 +25,7 @@ (add-game-object player) (set-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player))) + (objectify-place-monsters (player-place player)) ;; The actual game loop (clear-screen) (let ((place (get-game-object 'place (player-place player)))) @@ -93,7 +94,8 @@ (format t "~&Players present: ~A" (string-from-list (place-player p))) (format t "~&Items: ~A" (string-from-list (place-item p))) (format t "~&NPCs: ~A" (string-from-list (place-npc p))) - (format t "~&Monsters: ~A" (string-from-list (place-monster p)))) + (format t "~&Monsters: ~A" (string-from-list + (list-place-objects 'monster p)))) (defun game-command (cmd player) "Execute a typed-in game command" @@ -194,6 +196,7 @@ (defun goto (player &optional location) "Go to the specified location" + ;; Look before you leap (unless location (format t "~&Please specify a location!") (return-from goto)) @@ -204,12 +207,14 @@ :test #'equalp)) (format t "~&This place does not border your current location!") (return-from goto)) + ;; Do all the necessary housekeeping (clear-screen) (debugging "~&~A is going to ~A." (player-name player) location) + (objectify-place-monsters location) (remove-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player)) (set-object-attribute player 'place location) - (set-object-attribute (get-game-object 'place (player-place player)) + (set-object-attribute (get-game-object 'place location) 'player (player-name player)) (describe-place location)) @@ -226,9 +231,9 @@ (let ((place (get-game-object 'place (player-place player))) (description NIL)) (macrolet ((set-descr (type) - (let ((place-descr (build-symbol type "-description")) - (place-object (build-symbol "place-" type))) - `(when (member object-name (,place-object place) + (let ((place-descr (build-symbol type "-description"))) + `(when (member object-name + (list-place-objects ',type place) :test #'equalp) (setf description (,place-descr (get-game-object ',type @@ -344,7 +349,8 @@ (format t "~&Please specify an opponent!") (return-from attack)) (unless (member opponent - (list-place-objects 'monster (player-place player)) + (list-place-objects 'monster + (get-game-object 'place (player-place player))) :test #'equalp) (format t "~&This monster is not here!") (return-from attack)) @@ -363,7 +369,6 @@ (if (> (+ (random 10) p-dex) (+ (random 10) m-dex)) (setf damage (calculate-damage p-str p-weapon m-dex m-ac)) (setf damage (- 0 (calculate-damage m-str m-weapon p-dex p-ac)))) - ;(break) (cond ((plusp damage) (decf (monster-health monster) damage) (format t "~&You hit! ~A points damage." damage) diff --git a/lisp/util.lisp b/lisp/util.lisp index 3ad8306..3ce44e3 100644 --- a/lisp/util.lisp +++ b/lisp/util.lisp @@ -98,13 +98,13 @@ ;;; FUNCTIONS -; Some of these functions are probably quite inefficient (lots of consing) +;; Some of these functions are probably quite inefficient (lots of consing) -;; XXX DEPRECATED Not actually needed anywhere -(defun call-function (function-name &rest args) - "Save myself some quoting when calling a function from a generated symbol" - ;; Perhaps not very clean, but it works - (eval `(,function-name ,@args))) +(defun remove-first-if (fn lst) + "Remove the first element in a list that satisfies the given predicate" + (cond ((null lst) NIL) + ((funcall fn (car lst)) (cdr lst)) + (T (cons (car lst) (remove-first-if fn (cdr lst)))))) (defun average (&rest numbers) "Compute the average of the given numbers" @@ -205,17 +205,19 @@ (read-from-string (string-from-list components ""))) (defun make-list-function (container-type &optional (add-s t)) - "Return function to return a list of the names of all objects of the + "Return a function to return a list of the names of all objects of the specified type in the container struct" #'(lambda (object-type container) - (let ((get-objects (symbol-function (build-symbol container-type "-" - object-type (if add-s "s" "")))) - (get-object-name (symbol-function - (build-symbol object-type "-name"))) - (name-list NIL)) - (dolist (object (funcall get-objects container) name-list) + (let* ((get-objects (symbol-function + (build-symbol container-type "-" + object-type (if add-s "s" "")))) + (get-object-name (symbol-function + (build-symbol object-type "-name"))) + (objects (funcall get-objects container)) (name-list NIL)) + (dolist (o objects name-list) + (when (stringp o) (return objects)) (setf name-list - (cons (funcall get-object-name object) name-list)))))) + (cons (funcall get-object-name o) name-list)))))) (defun choose-number-option (option-list) "The user chooses one out of a list of options, the index is returned" diff --git a/python/__pycache__/character.cpython-34.pyc b/python/__pycache__/character.cpython-34.pyc deleted file mode 100644 index 20a9d5a..0000000 --- a/python/__pycache__/character.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/client.cpython-34.pyc b/python/__pycache__/client.cpython-34.pyc deleted file mode 100644 index 077e656..0000000 --- a/python/__pycache__/client.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/define.cpython-34.pyc b/python/__pycache__/define.cpython-34.pyc deleted file mode 100644 index 397fe49..0000000 --- a/python/__pycache__/define.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/interpreter.cpython-32.pyc b/python/__pycache__/interpreter.cpython-32.pyc deleted file mode 100644 index 2d36d9f..0000000 --- a/python/__pycache__/interpreter.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/lisp/game-objects.lisp b/lisp/game-objects.lisp index 080bc7e..585cc9b 100644 --- a/lisp/game-objects.lisp +++ b/lisp/game-objects.lisp @@ -35,6 +35,7 @@ (strength 0) (dexterity 0) (aggression 0) + (spawn-probability 0) (weapon "") (armour-class 0)) @@ -48,7 +49,7 @@ (defstruct weapon (name "") (description "") - (type "generic") + (type "") (damage 0)) @@ -69,21 +70,29 @@ ;; Same comment applies as above (let ((command (build-symbol (type-of game-object) "-" property))) (eval `(if (listp (,command ,game-object)) - ;; FIXME This gives problems with multiple values (setf (,command ,game-object) - (remove-if #'(lambda (x) (equalp x ,value)) + (remove-first-if #'(lambda (x) (equalp x ,value)) (,command ,game-object))) ;; TODO set numbers to 0, strings to "" (setf (,command ,game-object) NIL))))) -;; XXX This function is probably superfluous, as all place objects are stored -;; in world, with only their names recorded in the place +(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)) + (dolist (n name-list objects) + (setf objects (cons (get-game-object object-type 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 ((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." (funcall list-function object-type place))) - -(defun monster-strikes-player (monster player) - "The monster attacks a player" - ;;TODO - ) diff --git a/lisp/interpreter.lisp b/lisp/interpreter.lisp index 8f69337..03d4886 100644 --- a/lisp/interpreter.lisp +++ b/lisp/interpreter.lisp @@ -99,7 +99,7 @@ ;; interpret an option command ((or (eql (aref line 0) #\Space) (eql (aref line 0) #\Tab)) - (setf line (string-left-trim '(#\Space #\Tab) line)) + (setf line (trim-whitespace line)) (set-object-attribute current-object (read-from-string line) (read-from-string (second (cut-string line (position #\space line)))))) diff --git a/lisp/ui.lisp b/lisp/ui.lisp index 0fafe21..7d0c25d 100644 --- a/lisp/ui.lisp +++ b/lisp/ui.lisp @@ -25,6 +25,7 @@ (add-game-object player) (set-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player))) + (objectify-place-monsters (player-place player)) ;; The actual game loop (clear-screen) (let ((place (get-game-object 'place (player-place player)))) @@ -93,7 +94,8 @@ (format t "~&Players present: ~A" (string-from-list (place-player p))) (format t "~&Items: ~A" (string-from-list (place-item p))) (format t "~&NPCs: ~A" (string-from-list (place-npc p))) - (format t "~&Monsters: ~A" (string-from-list (place-monster p)))) + (format t "~&Monsters: ~A" (string-from-list + (list-place-objects 'monster p)))) (defun game-command (cmd player) "Execute a typed-in game command" @@ -194,6 +196,7 @@ (defun goto (player &optional location) "Go to the specified location" + ;; Look before you leap (unless location (format t "~&Please specify a location!") (return-from goto)) @@ -204,12 +207,14 @@ :test #'equalp)) (format t "~&This place does not border your current location!") (return-from goto)) + ;; Do all the necessary housekeeping (clear-screen) (debugging "~&~A is going to ~A." (player-name player) location) + (objectify-place-monsters location) (remove-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player)) (set-object-attribute player 'place location) - (set-object-attribute (get-game-object 'place (player-place player)) + (set-object-attribute (get-game-object 'place location) 'player (player-name player)) (describe-place location)) @@ -226,9 +231,9 @@ (let ((place (get-game-object 'place (player-place player))) (description NIL)) (macrolet ((set-descr (type) - (let ((place-descr (build-symbol type "-description")) - (place-object (build-symbol "place-" type))) - `(when (member object-name (,place-object place) + (let ((place-descr (build-symbol type "-description"))) + `(when (member object-name + (list-place-objects ',type place) :test #'equalp) (setf description (,place-descr (get-game-object ',type @@ -344,7 +349,8 @@ (format t "~&Please specify an opponent!") (return-from attack)) (unless (member opponent - (list-place-objects 'monster (player-place player)) + (list-place-objects 'monster + (get-game-object 'place (player-place player))) :test #'equalp) (format t "~&This monster is not here!") (return-from attack)) @@ -363,7 +369,6 @@ (if (> (+ (random 10) p-dex) (+ (random 10) m-dex)) (setf damage (calculate-damage p-str p-weapon m-dex m-ac)) (setf damage (- 0 (calculate-damage m-str m-weapon p-dex p-ac)))) - ;(break) (cond ((plusp damage) (decf (monster-health monster) damage) (format t "~&You hit! ~A points damage." damage) diff --git a/lisp/util.lisp b/lisp/util.lisp index 3ad8306..3ce44e3 100644 --- a/lisp/util.lisp +++ b/lisp/util.lisp @@ -98,13 +98,13 @@ ;;; FUNCTIONS -; Some of these functions are probably quite inefficient (lots of consing) +;; Some of these functions are probably quite inefficient (lots of consing) -;; XXX DEPRECATED Not actually needed anywhere -(defun call-function (function-name &rest args) - "Save myself some quoting when calling a function from a generated symbol" - ;; Perhaps not very clean, but it works - (eval `(,function-name ,@args))) +(defun remove-first-if (fn lst) + "Remove the first element in a list that satisfies the given predicate" + (cond ((null lst) NIL) + ((funcall fn (car lst)) (cdr lst)) + (T (cons (car lst) (remove-first-if fn (cdr lst)))))) (defun average (&rest numbers) "Compute the average of the given numbers" @@ -205,17 +205,19 @@ (read-from-string (string-from-list components ""))) (defun make-list-function (container-type &optional (add-s t)) - "Return function to return a list of the names of all objects of the + "Return a function to return a list of the names of all objects of the specified type in the container struct" #'(lambda (object-type container) - (let ((get-objects (symbol-function (build-symbol container-type "-" - object-type (if add-s "s" "")))) - (get-object-name (symbol-function - (build-symbol object-type "-name"))) - (name-list NIL)) - (dolist (object (funcall get-objects container) name-list) + (let* ((get-objects (symbol-function + (build-symbol container-type "-" + object-type (if add-s "s" "")))) + (get-object-name (symbol-function + (build-symbol object-type "-name"))) + (objects (funcall get-objects container)) (name-list NIL)) + (dolist (o objects name-list) + (when (stringp o) (return objects)) (setf name-list - (cons (funcall get-object-name object) name-list)))))) + (cons (funcall get-object-name o) name-list)))))) (defun choose-number-option (option-list) "The user chooses one out of a list of options, the index is returned" diff --git a/python/__pycache__/character.cpython-34.pyc b/python/__pycache__/character.cpython-34.pyc deleted file mode 100644 index 20a9d5a..0000000 --- a/python/__pycache__/character.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/client.cpython-34.pyc b/python/__pycache__/client.cpython-34.pyc deleted file mode 100644 index 077e656..0000000 --- a/python/__pycache__/client.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/define.cpython-34.pyc b/python/__pycache__/define.cpython-34.pyc deleted file mode 100644 index 397fe49..0000000 --- a/python/__pycache__/define.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/interpreter.cpython-32.pyc b/python/__pycache__/interpreter.cpython-32.pyc deleted file mode 100644 index 2d36d9f..0000000 --- a/python/__pycache__/interpreter.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/interpreter.cpython-34.pyc b/python/__pycache__/interpreter.cpython-34.pyc deleted file mode 100644 index 3d8f05b..0000000 --- a/python/__pycache__/interpreter.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/lisp/game-objects.lisp b/lisp/game-objects.lisp index 080bc7e..585cc9b 100644 --- a/lisp/game-objects.lisp +++ b/lisp/game-objects.lisp @@ -35,6 +35,7 @@ (strength 0) (dexterity 0) (aggression 0) + (spawn-probability 0) (weapon "") (armour-class 0)) @@ -48,7 +49,7 @@ (defstruct weapon (name "") (description "") - (type "generic") + (type "") (damage 0)) @@ -69,21 +70,29 @@ ;; Same comment applies as above (let ((command (build-symbol (type-of game-object) "-" property))) (eval `(if (listp (,command ,game-object)) - ;; FIXME This gives problems with multiple values (setf (,command ,game-object) - (remove-if #'(lambda (x) (equalp x ,value)) + (remove-first-if #'(lambda (x) (equalp x ,value)) (,command ,game-object))) ;; TODO set numbers to 0, strings to "" (setf (,command ,game-object) NIL))))) -;; XXX This function is probably superfluous, as all place objects are stored -;; in world, with only their names recorded in the place +(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)) + (dolist (n name-list objects) + (setf objects (cons (get-game-object object-type 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 ((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." (funcall list-function object-type place))) - -(defun monster-strikes-player (monster player) - "The monster attacks a player" - ;;TODO - ) diff --git a/lisp/interpreter.lisp b/lisp/interpreter.lisp index 8f69337..03d4886 100644 --- a/lisp/interpreter.lisp +++ b/lisp/interpreter.lisp @@ -99,7 +99,7 @@ ;; interpret an option command ((or (eql (aref line 0) #\Space) (eql (aref line 0) #\Tab)) - (setf line (string-left-trim '(#\Space #\Tab) line)) + (setf line (trim-whitespace line)) (set-object-attribute current-object (read-from-string line) (read-from-string (second (cut-string line (position #\space line)))))) diff --git a/lisp/ui.lisp b/lisp/ui.lisp index 0fafe21..7d0c25d 100644 --- a/lisp/ui.lisp +++ b/lisp/ui.lisp @@ -25,6 +25,7 @@ (add-game-object player) (set-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player))) + (objectify-place-monsters (player-place player)) ;; The actual game loop (clear-screen) (let ((place (get-game-object 'place (player-place player)))) @@ -93,7 +94,8 @@ (format t "~&Players present: ~A" (string-from-list (place-player p))) (format t "~&Items: ~A" (string-from-list (place-item p))) (format t "~&NPCs: ~A" (string-from-list (place-npc p))) - (format t "~&Monsters: ~A" (string-from-list (place-monster p)))) + (format t "~&Monsters: ~A" (string-from-list + (list-place-objects 'monster p)))) (defun game-command (cmd player) "Execute a typed-in game command" @@ -194,6 +196,7 @@ (defun goto (player &optional location) "Go to the specified location" + ;; Look before you leap (unless location (format t "~&Please specify a location!") (return-from goto)) @@ -204,12 +207,14 @@ :test #'equalp)) (format t "~&This place does not border your current location!") (return-from goto)) + ;; Do all the necessary housekeeping (clear-screen) (debugging "~&~A is going to ~A." (player-name player) location) + (objectify-place-monsters location) (remove-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player)) (set-object-attribute player 'place location) - (set-object-attribute (get-game-object 'place (player-place player)) + (set-object-attribute (get-game-object 'place location) 'player (player-name player)) (describe-place location)) @@ -226,9 +231,9 @@ (let ((place (get-game-object 'place (player-place player))) (description NIL)) (macrolet ((set-descr (type) - (let ((place-descr (build-symbol type "-description")) - (place-object (build-symbol "place-" type))) - `(when (member object-name (,place-object place) + (let ((place-descr (build-symbol type "-description"))) + `(when (member object-name + (list-place-objects ',type place) :test #'equalp) (setf description (,place-descr (get-game-object ',type @@ -344,7 +349,8 @@ (format t "~&Please specify an opponent!") (return-from attack)) (unless (member opponent - (list-place-objects 'monster (player-place player)) + (list-place-objects 'monster + (get-game-object 'place (player-place player))) :test #'equalp) (format t "~&This monster is not here!") (return-from attack)) @@ -363,7 +369,6 @@ (if (> (+ (random 10) p-dex) (+ (random 10) m-dex)) (setf damage (calculate-damage p-str p-weapon m-dex m-ac)) (setf damage (- 0 (calculate-damage m-str m-weapon p-dex p-ac)))) - ;(break) (cond ((plusp damage) (decf (monster-health monster) damage) (format t "~&You hit! ~A points damage." damage) diff --git a/lisp/util.lisp b/lisp/util.lisp index 3ad8306..3ce44e3 100644 --- a/lisp/util.lisp +++ b/lisp/util.lisp @@ -98,13 +98,13 @@ ;;; FUNCTIONS -; Some of these functions are probably quite inefficient (lots of consing) +;; Some of these functions are probably quite inefficient (lots of consing) -;; XXX DEPRECATED Not actually needed anywhere -(defun call-function (function-name &rest args) - "Save myself some quoting when calling a function from a generated symbol" - ;; Perhaps not very clean, but it works - (eval `(,function-name ,@args))) +(defun remove-first-if (fn lst) + "Remove the first element in a list that satisfies the given predicate" + (cond ((null lst) NIL) + ((funcall fn (car lst)) (cdr lst)) + (T (cons (car lst) (remove-first-if fn (cdr lst)))))) (defun average (&rest numbers) "Compute the average of the given numbers" @@ -205,17 +205,19 @@ (read-from-string (string-from-list components ""))) (defun make-list-function (container-type &optional (add-s t)) - "Return function to return a list of the names of all objects of the + "Return a function to return a list of the names of all objects of the specified type in the container struct" #'(lambda (object-type container) - (let ((get-objects (symbol-function (build-symbol container-type "-" - object-type (if add-s "s" "")))) - (get-object-name (symbol-function - (build-symbol object-type "-name"))) - (name-list NIL)) - (dolist (object (funcall get-objects container) name-list) + (let* ((get-objects (symbol-function + (build-symbol container-type "-" + object-type (if add-s "s" "")))) + (get-object-name (symbol-function + (build-symbol object-type "-name"))) + (objects (funcall get-objects container)) (name-list NIL)) + (dolist (o objects name-list) + (when (stringp o) (return objects)) (setf name-list - (cons (funcall get-object-name object) name-list)))))) + (cons (funcall get-object-name o) name-list)))))) (defun choose-number-option (option-list) "The user chooses one out of a list of options, the index is returned" diff --git a/python/__pycache__/character.cpython-34.pyc b/python/__pycache__/character.cpython-34.pyc deleted file mode 100644 index 20a9d5a..0000000 --- a/python/__pycache__/character.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/client.cpython-34.pyc b/python/__pycache__/client.cpython-34.pyc deleted file mode 100644 index 077e656..0000000 --- a/python/__pycache__/client.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/define.cpython-34.pyc b/python/__pycache__/define.cpython-34.pyc deleted file mode 100644 index 397fe49..0000000 --- a/python/__pycache__/define.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/interpreter.cpython-32.pyc b/python/__pycache__/interpreter.cpython-32.pyc deleted file mode 100644 index 2d36d9f..0000000 --- a/python/__pycache__/interpreter.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/interpreter.cpython-34.pyc b/python/__pycache__/interpreter.cpython-34.pyc deleted file mode 100644 index 3d8f05b..0000000 --- a/python/__pycache__/interpreter.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/item.cpython-34.pyc b/python/__pycache__/item.cpython-34.pyc deleted file mode 100644 index bc4b363..0000000 --- a/python/__pycache__/item.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/lisp/game-objects.lisp b/lisp/game-objects.lisp index 080bc7e..585cc9b 100644 --- a/lisp/game-objects.lisp +++ b/lisp/game-objects.lisp @@ -35,6 +35,7 @@ (strength 0) (dexterity 0) (aggression 0) + (spawn-probability 0) (weapon "") (armour-class 0)) @@ -48,7 +49,7 @@ (defstruct weapon (name "") (description "") - (type "generic") + (type "") (damage 0)) @@ -69,21 +70,29 @@ ;; Same comment applies as above (let ((command (build-symbol (type-of game-object) "-" property))) (eval `(if (listp (,command ,game-object)) - ;; FIXME This gives problems with multiple values (setf (,command ,game-object) - (remove-if #'(lambda (x) (equalp x ,value)) + (remove-first-if #'(lambda (x) (equalp x ,value)) (,command ,game-object))) ;; TODO set numbers to 0, strings to "" (setf (,command ,game-object) NIL))))) -;; XXX This function is probably superfluous, as all place objects are stored -;; in world, with only their names recorded in the place +(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)) + (dolist (n name-list objects) + (setf objects (cons (get-game-object object-type 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 ((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." (funcall list-function object-type place))) - -(defun monster-strikes-player (monster player) - "The monster attacks a player" - ;;TODO - ) diff --git a/lisp/interpreter.lisp b/lisp/interpreter.lisp index 8f69337..03d4886 100644 --- a/lisp/interpreter.lisp +++ b/lisp/interpreter.lisp @@ -99,7 +99,7 @@ ;; interpret an option command ((or (eql (aref line 0) #\Space) (eql (aref line 0) #\Tab)) - (setf line (string-left-trim '(#\Space #\Tab) line)) + (setf line (trim-whitespace line)) (set-object-attribute current-object (read-from-string line) (read-from-string (second (cut-string line (position #\space line)))))) diff --git a/lisp/ui.lisp b/lisp/ui.lisp index 0fafe21..7d0c25d 100644 --- a/lisp/ui.lisp +++ b/lisp/ui.lisp @@ -25,6 +25,7 @@ (add-game-object player) (set-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player))) + (objectify-place-monsters (player-place player)) ;; The actual game loop (clear-screen) (let ((place (get-game-object 'place (player-place player)))) @@ -93,7 +94,8 @@ (format t "~&Players present: ~A" (string-from-list (place-player p))) (format t "~&Items: ~A" (string-from-list (place-item p))) (format t "~&NPCs: ~A" (string-from-list (place-npc p))) - (format t "~&Monsters: ~A" (string-from-list (place-monster p)))) + (format t "~&Monsters: ~A" (string-from-list + (list-place-objects 'monster p)))) (defun game-command (cmd player) "Execute a typed-in game command" @@ -194,6 +196,7 @@ (defun goto (player &optional location) "Go to the specified location" + ;; Look before you leap (unless location (format t "~&Please specify a location!") (return-from goto)) @@ -204,12 +207,14 @@ :test #'equalp)) (format t "~&This place does not border your current location!") (return-from goto)) + ;; Do all the necessary housekeeping (clear-screen) (debugging "~&~A is going to ~A." (player-name player) location) + (objectify-place-monsters location) (remove-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player)) (set-object-attribute player 'place location) - (set-object-attribute (get-game-object 'place (player-place player)) + (set-object-attribute (get-game-object 'place location) 'player (player-name player)) (describe-place location)) @@ -226,9 +231,9 @@ (let ((place (get-game-object 'place (player-place player))) (description NIL)) (macrolet ((set-descr (type) - (let ((place-descr (build-symbol type "-description")) - (place-object (build-symbol "place-" type))) - `(when (member object-name (,place-object place) + (let ((place-descr (build-symbol type "-description"))) + `(when (member object-name + (list-place-objects ',type place) :test #'equalp) (setf description (,place-descr (get-game-object ',type @@ -344,7 +349,8 @@ (format t "~&Please specify an opponent!") (return-from attack)) (unless (member opponent - (list-place-objects 'monster (player-place player)) + (list-place-objects 'monster + (get-game-object 'place (player-place player))) :test #'equalp) (format t "~&This monster is not here!") (return-from attack)) @@ -363,7 +369,6 @@ (if (> (+ (random 10) p-dex) (+ (random 10) m-dex)) (setf damage (calculate-damage p-str p-weapon m-dex m-ac)) (setf damage (- 0 (calculate-damage m-str m-weapon p-dex p-ac)))) - ;(break) (cond ((plusp damage) (decf (monster-health monster) damage) (format t "~&You hit! ~A points damage." damage) diff --git a/lisp/util.lisp b/lisp/util.lisp index 3ad8306..3ce44e3 100644 --- a/lisp/util.lisp +++ b/lisp/util.lisp @@ -98,13 +98,13 @@ ;;; FUNCTIONS -; Some of these functions are probably quite inefficient (lots of consing) +;; Some of these functions are probably quite inefficient (lots of consing) -;; XXX DEPRECATED Not actually needed anywhere -(defun call-function (function-name &rest args) - "Save myself some quoting when calling a function from a generated symbol" - ;; Perhaps not very clean, but it works - (eval `(,function-name ,@args))) +(defun remove-first-if (fn lst) + "Remove the first element in a list that satisfies the given predicate" + (cond ((null lst) NIL) + ((funcall fn (car lst)) (cdr lst)) + (T (cons (car lst) (remove-first-if fn (cdr lst)))))) (defun average (&rest numbers) "Compute the average of the given numbers" @@ -205,17 +205,19 @@ (read-from-string (string-from-list components ""))) (defun make-list-function (container-type &optional (add-s t)) - "Return function to return a list of the names of all objects of the + "Return a function to return a list of the names of all objects of the specified type in the container struct" #'(lambda (object-type container) - (let ((get-objects (symbol-function (build-symbol container-type "-" - object-type (if add-s "s" "")))) - (get-object-name (symbol-function - (build-symbol object-type "-name"))) - (name-list NIL)) - (dolist (object (funcall get-objects container) name-list) + (let* ((get-objects (symbol-function + (build-symbol container-type "-" + object-type (if add-s "s" "")))) + (get-object-name (symbol-function + (build-symbol object-type "-name"))) + (objects (funcall get-objects container)) (name-list NIL)) + (dolist (o objects name-list) + (when (stringp o) (return objects)) (setf name-list - (cons (funcall get-object-name object) name-list)))))) + (cons (funcall get-object-name o) name-list)))))) (defun choose-number-option (option-list) "The user chooses one out of a list of options, the index is returned" diff --git a/python/__pycache__/character.cpython-34.pyc b/python/__pycache__/character.cpython-34.pyc deleted file mode 100644 index 20a9d5a..0000000 --- a/python/__pycache__/character.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/client.cpython-34.pyc b/python/__pycache__/client.cpython-34.pyc deleted file mode 100644 index 077e656..0000000 --- a/python/__pycache__/client.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/define.cpython-34.pyc b/python/__pycache__/define.cpython-34.pyc deleted file mode 100644 index 397fe49..0000000 --- a/python/__pycache__/define.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/interpreter.cpython-32.pyc b/python/__pycache__/interpreter.cpython-32.pyc deleted file mode 100644 index 2d36d9f..0000000 --- a/python/__pycache__/interpreter.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/interpreter.cpython-34.pyc b/python/__pycache__/interpreter.cpython-34.pyc deleted file mode 100644 index 3d8f05b..0000000 --- a/python/__pycache__/interpreter.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/item.cpython-34.pyc b/python/__pycache__/item.cpython-34.pyc deleted file mode 100644 index bc4b363..0000000 --- a/python/__pycache__/item.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/parser.cpython-34.pyc b/python/__pycache__/parser.cpython-34.pyc deleted file mode 100644 index ffeaf68..0000000 --- a/python/__pycache__/parser.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/lisp/game-objects.lisp b/lisp/game-objects.lisp index 080bc7e..585cc9b 100644 --- a/lisp/game-objects.lisp +++ b/lisp/game-objects.lisp @@ -35,6 +35,7 @@ (strength 0) (dexterity 0) (aggression 0) + (spawn-probability 0) (weapon "") (armour-class 0)) @@ -48,7 +49,7 @@ (defstruct weapon (name "") (description "") - (type "generic") + (type "") (damage 0)) @@ -69,21 +70,29 @@ ;; Same comment applies as above (let ((command (build-symbol (type-of game-object) "-" property))) (eval `(if (listp (,command ,game-object)) - ;; FIXME This gives problems with multiple values (setf (,command ,game-object) - (remove-if #'(lambda (x) (equalp x ,value)) + (remove-first-if #'(lambda (x) (equalp x ,value)) (,command ,game-object))) ;; TODO set numbers to 0, strings to "" (setf (,command ,game-object) NIL))))) -;; XXX This function is probably superfluous, as all place objects are stored -;; in world, with only their names recorded in the place +(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)) + (dolist (n name-list objects) + (setf objects (cons (get-game-object object-type 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 ((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." (funcall list-function object-type place))) - -(defun monster-strikes-player (monster player) - "The monster attacks a player" - ;;TODO - ) diff --git a/lisp/interpreter.lisp b/lisp/interpreter.lisp index 8f69337..03d4886 100644 --- a/lisp/interpreter.lisp +++ b/lisp/interpreter.lisp @@ -99,7 +99,7 @@ ;; interpret an option command ((or (eql (aref line 0) #\Space) (eql (aref line 0) #\Tab)) - (setf line (string-left-trim '(#\Space #\Tab) line)) + (setf line (trim-whitespace line)) (set-object-attribute current-object (read-from-string line) (read-from-string (second (cut-string line (position #\space line)))))) diff --git a/lisp/ui.lisp b/lisp/ui.lisp index 0fafe21..7d0c25d 100644 --- a/lisp/ui.lisp +++ b/lisp/ui.lisp @@ -25,6 +25,7 @@ (add-game-object player) (set-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player))) + (objectify-place-monsters (player-place player)) ;; The actual game loop (clear-screen) (let ((place (get-game-object 'place (player-place player)))) @@ -93,7 +94,8 @@ (format t "~&Players present: ~A" (string-from-list (place-player p))) (format t "~&Items: ~A" (string-from-list (place-item p))) (format t "~&NPCs: ~A" (string-from-list (place-npc p))) - (format t "~&Monsters: ~A" (string-from-list (place-monster p)))) + (format t "~&Monsters: ~A" (string-from-list + (list-place-objects 'monster p)))) (defun game-command (cmd player) "Execute a typed-in game command" @@ -194,6 +196,7 @@ (defun goto (player &optional location) "Go to the specified location" + ;; Look before you leap (unless location (format t "~&Please specify a location!") (return-from goto)) @@ -204,12 +207,14 @@ :test #'equalp)) (format t "~&This place does not border your current location!") (return-from goto)) + ;; Do all the necessary housekeeping (clear-screen) (debugging "~&~A is going to ~A." (player-name player) location) + (objectify-place-monsters location) (remove-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player)) (set-object-attribute player 'place location) - (set-object-attribute (get-game-object 'place (player-place player)) + (set-object-attribute (get-game-object 'place location) 'player (player-name player)) (describe-place location)) @@ -226,9 +231,9 @@ (let ((place (get-game-object 'place (player-place player))) (description NIL)) (macrolet ((set-descr (type) - (let ((place-descr (build-symbol type "-description")) - (place-object (build-symbol "place-" type))) - `(when (member object-name (,place-object place) + (let ((place-descr (build-symbol type "-description"))) + `(when (member object-name + (list-place-objects ',type place) :test #'equalp) (setf description (,place-descr (get-game-object ',type @@ -344,7 +349,8 @@ (format t "~&Please specify an opponent!") (return-from attack)) (unless (member opponent - (list-place-objects 'monster (player-place player)) + (list-place-objects 'monster + (get-game-object 'place (player-place player))) :test #'equalp) (format t "~&This monster is not here!") (return-from attack)) @@ -363,7 +369,6 @@ (if (> (+ (random 10) p-dex) (+ (random 10) m-dex)) (setf damage (calculate-damage p-str p-weapon m-dex m-ac)) (setf damage (- 0 (calculate-damage m-str m-weapon p-dex p-ac)))) - ;(break) (cond ((plusp damage) (decf (monster-health monster) damage) (format t "~&You hit! ~A points damage." damage) diff --git a/lisp/util.lisp b/lisp/util.lisp index 3ad8306..3ce44e3 100644 --- a/lisp/util.lisp +++ b/lisp/util.lisp @@ -98,13 +98,13 @@ ;;; FUNCTIONS -; Some of these functions are probably quite inefficient (lots of consing) +;; Some of these functions are probably quite inefficient (lots of consing) -;; XXX DEPRECATED Not actually needed anywhere -(defun call-function (function-name &rest args) - "Save myself some quoting when calling a function from a generated symbol" - ;; Perhaps not very clean, but it works - (eval `(,function-name ,@args))) +(defun remove-first-if (fn lst) + "Remove the first element in a list that satisfies the given predicate" + (cond ((null lst) NIL) + ((funcall fn (car lst)) (cdr lst)) + (T (cons (car lst) (remove-first-if fn (cdr lst)))))) (defun average (&rest numbers) "Compute the average of the given numbers" @@ -205,17 +205,19 @@ (read-from-string (string-from-list components ""))) (defun make-list-function (container-type &optional (add-s t)) - "Return function to return a list of the names of all objects of the + "Return a function to return a list of the names of all objects of the specified type in the container struct" #'(lambda (object-type container) - (let ((get-objects (symbol-function (build-symbol container-type "-" - object-type (if add-s "s" "")))) - (get-object-name (symbol-function - (build-symbol object-type "-name"))) - (name-list NIL)) - (dolist (object (funcall get-objects container) name-list) + (let* ((get-objects (symbol-function + (build-symbol container-type "-" + object-type (if add-s "s" "")))) + (get-object-name (symbol-function + (build-symbol object-type "-name"))) + (objects (funcall get-objects container)) (name-list NIL)) + (dolist (o objects name-list) + (when (stringp o) (return objects)) (setf name-list - (cons (funcall get-object-name object) name-list)))))) + (cons (funcall get-object-name o) name-list)))))) (defun choose-number-option (option-list) "The user chooses one out of a list of options, the index is returned" diff --git a/python/__pycache__/character.cpython-34.pyc b/python/__pycache__/character.cpython-34.pyc deleted file mode 100644 index 20a9d5a..0000000 --- a/python/__pycache__/character.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/client.cpython-34.pyc b/python/__pycache__/client.cpython-34.pyc deleted file mode 100644 index 077e656..0000000 --- a/python/__pycache__/client.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/define.cpython-34.pyc b/python/__pycache__/define.cpython-34.pyc deleted file mode 100644 index 397fe49..0000000 --- a/python/__pycache__/define.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/interpreter.cpython-32.pyc b/python/__pycache__/interpreter.cpython-32.pyc deleted file mode 100644 index 2d36d9f..0000000 --- a/python/__pycache__/interpreter.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/interpreter.cpython-34.pyc b/python/__pycache__/interpreter.cpython-34.pyc deleted file mode 100644 index 3d8f05b..0000000 --- a/python/__pycache__/interpreter.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/item.cpython-34.pyc b/python/__pycache__/item.cpython-34.pyc deleted file mode 100644 index bc4b363..0000000 --- a/python/__pycache__/item.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/parser.cpython-34.pyc b/python/__pycache__/parser.cpython-34.pyc deleted file mode 100644 index ffeaf68..0000000 --- a/python/__pycache__/parser.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/place.cpython-32.pyc b/python/__pycache__/place.cpython-32.pyc deleted file mode 100644 index 721a318..0000000 --- a/python/__pycache__/place.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/lisp/game-objects.lisp b/lisp/game-objects.lisp index 080bc7e..585cc9b 100644 --- a/lisp/game-objects.lisp +++ b/lisp/game-objects.lisp @@ -35,6 +35,7 @@ (strength 0) (dexterity 0) (aggression 0) + (spawn-probability 0) (weapon "") (armour-class 0)) @@ -48,7 +49,7 @@ (defstruct weapon (name "") (description "") - (type "generic") + (type "") (damage 0)) @@ -69,21 +70,29 @@ ;; Same comment applies as above (let ((command (build-symbol (type-of game-object) "-" property))) (eval `(if (listp (,command ,game-object)) - ;; FIXME This gives problems with multiple values (setf (,command ,game-object) - (remove-if #'(lambda (x) (equalp x ,value)) + (remove-first-if #'(lambda (x) (equalp x ,value)) (,command ,game-object))) ;; TODO set numbers to 0, strings to "" (setf (,command ,game-object) NIL))))) -;; XXX This function is probably superfluous, as all place objects are stored -;; in world, with only their names recorded in the place +(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)) + (dolist (n name-list objects) + (setf objects (cons (get-game-object object-type 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 ((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." (funcall list-function object-type place))) - -(defun monster-strikes-player (monster player) - "The monster attacks a player" - ;;TODO - ) diff --git a/lisp/interpreter.lisp b/lisp/interpreter.lisp index 8f69337..03d4886 100644 --- a/lisp/interpreter.lisp +++ b/lisp/interpreter.lisp @@ -99,7 +99,7 @@ ;; interpret an option command ((or (eql (aref line 0) #\Space) (eql (aref line 0) #\Tab)) - (setf line (string-left-trim '(#\Space #\Tab) line)) + (setf line (trim-whitespace line)) (set-object-attribute current-object (read-from-string line) (read-from-string (second (cut-string line (position #\space line)))))) diff --git a/lisp/ui.lisp b/lisp/ui.lisp index 0fafe21..7d0c25d 100644 --- a/lisp/ui.lisp +++ b/lisp/ui.lisp @@ -25,6 +25,7 @@ (add-game-object player) (set-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player))) + (objectify-place-monsters (player-place player)) ;; The actual game loop (clear-screen) (let ((place (get-game-object 'place (player-place player)))) @@ -93,7 +94,8 @@ (format t "~&Players present: ~A" (string-from-list (place-player p))) (format t "~&Items: ~A" (string-from-list (place-item p))) (format t "~&NPCs: ~A" (string-from-list (place-npc p))) - (format t "~&Monsters: ~A" (string-from-list (place-monster p)))) + (format t "~&Monsters: ~A" (string-from-list + (list-place-objects 'monster p)))) (defun game-command (cmd player) "Execute a typed-in game command" @@ -194,6 +196,7 @@ (defun goto (player &optional location) "Go to the specified location" + ;; Look before you leap (unless location (format t "~&Please specify a location!") (return-from goto)) @@ -204,12 +207,14 @@ :test #'equalp)) (format t "~&This place does not border your current location!") (return-from goto)) + ;; Do all the necessary housekeeping (clear-screen) (debugging "~&~A is going to ~A." (player-name player) location) + (objectify-place-monsters location) (remove-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player)) (set-object-attribute player 'place location) - (set-object-attribute (get-game-object 'place (player-place player)) + (set-object-attribute (get-game-object 'place location) 'player (player-name player)) (describe-place location)) @@ -226,9 +231,9 @@ (let ((place (get-game-object 'place (player-place player))) (description NIL)) (macrolet ((set-descr (type) - (let ((place-descr (build-symbol type "-description")) - (place-object (build-symbol "place-" type))) - `(when (member object-name (,place-object place) + (let ((place-descr (build-symbol type "-description"))) + `(when (member object-name + (list-place-objects ',type place) :test #'equalp) (setf description (,place-descr (get-game-object ',type @@ -344,7 +349,8 @@ (format t "~&Please specify an opponent!") (return-from attack)) (unless (member opponent - (list-place-objects 'monster (player-place player)) + (list-place-objects 'monster + (get-game-object 'place (player-place player))) :test #'equalp) (format t "~&This monster is not here!") (return-from attack)) @@ -363,7 +369,6 @@ (if (> (+ (random 10) p-dex) (+ (random 10) m-dex)) (setf damage (calculate-damage p-str p-weapon m-dex m-ac)) (setf damage (- 0 (calculate-damage m-str m-weapon p-dex p-ac)))) - ;(break) (cond ((plusp damage) (decf (monster-health monster) damage) (format t "~&You hit! ~A points damage." damage) diff --git a/lisp/util.lisp b/lisp/util.lisp index 3ad8306..3ce44e3 100644 --- a/lisp/util.lisp +++ b/lisp/util.lisp @@ -98,13 +98,13 @@ ;;; FUNCTIONS -; Some of these functions are probably quite inefficient (lots of consing) +;; Some of these functions are probably quite inefficient (lots of consing) -;; XXX DEPRECATED Not actually needed anywhere -(defun call-function (function-name &rest args) - "Save myself some quoting when calling a function from a generated symbol" - ;; Perhaps not very clean, but it works - (eval `(,function-name ,@args))) +(defun remove-first-if (fn lst) + "Remove the first element in a list that satisfies the given predicate" + (cond ((null lst) NIL) + ((funcall fn (car lst)) (cdr lst)) + (T (cons (car lst) (remove-first-if fn (cdr lst)))))) (defun average (&rest numbers) "Compute the average of the given numbers" @@ -205,17 +205,19 @@ (read-from-string (string-from-list components ""))) (defun make-list-function (container-type &optional (add-s t)) - "Return function to return a list of the names of all objects of the + "Return a function to return a list of the names of all objects of the specified type in the container struct" #'(lambda (object-type container) - (let ((get-objects (symbol-function (build-symbol container-type "-" - object-type (if add-s "s" "")))) - (get-object-name (symbol-function - (build-symbol object-type "-name"))) - (name-list NIL)) - (dolist (object (funcall get-objects container) name-list) + (let* ((get-objects (symbol-function + (build-symbol container-type "-" + object-type (if add-s "s" "")))) + (get-object-name (symbol-function + (build-symbol object-type "-name"))) + (objects (funcall get-objects container)) (name-list NIL)) + (dolist (o objects name-list) + (when (stringp o) (return objects)) (setf name-list - (cons (funcall get-object-name object) name-list)))))) + (cons (funcall get-object-name o) name-list)))))) (defun choose-number-option (option-list) "The user chooses one out of a list of options, the index is returned" diff --git a/python/__pycache__/character.cpython-34.pyc b/python/__pycache__/character.cpython-34.pyc deleted file mode 100644 index 20a9d5a..0000000 --- a/python/__pycache__/character.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/client.cpython-34.pyc b/python/__pycache__/client.cpython-34.pyc deleted file mode 100644 index 077e656..0000000 --- a/python/__pycache__/client.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/define.cpython-34.pyc b/python/__pycache__/define.cpython-34.pyc deleted file mode 100644 index 397fe49..0000000 --- a/python/__pycache__/define.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/interpreter.cpython-32.pyc b/python/__pycache__/interpreter.cpython-32.pyc deleted file mode 100644 index 2d36d9f..0000000 --- a/python/__pycache__/interpreter.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/interpreter.cpython-34.pyc b/python/__pycache__/interpreter.cpython-34.pyc deleted file mode 100644 index 3d8f05b..0000000 --- a/python/__pycache__/interpreter.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/item.cpython-34.pyc b/python/__pycache__/item.cpython-34.pyc deleted file mode 100644 index bc4b363..0000000 --- a/python/__pycache__/item.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/parser.cpython-34.pyc b/python/__pycache__/parser.cpython-34.pyc deleted file mode 100644 index ffeaf68..0000000 --- a/python/__pycache__/parser.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/place.cpython-32.pyc b/python/__pycache__/place.cpython-32.pyc deleted file mode 100644 index 721a318..0000000 --- a/python/__pycache__/place.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/place.cpython-34.pyc b/python/__pycache__/place.cpython-34.pyc deleted file mode 100644 index bf21fc7..0000000 --- a/python/__pycache__/place.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/lisp/game-objects.lisp b/lisp/game-objects.lisp index 080bc7e..585cc9b 100644 --- a/lisp/game-objects.lisp +++ b/lisp/game-objects.lisp @@ -35,6 +35,7 @@ (strength 0) (dexterity 0) (aggression 0) + (spawn-probability 0) (weapon "") (armour-class 0)) @@ -48,7 +49,7 @@ (defstruct weapon (name "") (description "") - (type "generic") + (type "") (damage 0)) @@ -69,21 +70,29 @@ ;; Same comment applies as above (let ((command (build-symbol (type-of game-object) "-" property))) (eval `(if (listp (,command ,game-object)) - ;; FIXME This gives problems with multiple values (setf (,command ,game-object) - (remove-if #'(lambda (x) (equalp x ,value)) + (remove-first-if #'(lambda (x) (equalp x ,value)) (,command ,game-object))) ;; TODO set numbers to 0, strings to "" (setf (,command ,game-object) NIL))))) -;; XXX This function is probably superfluous, as all place objects are stored -;; in world, with only their names recorded in the place +(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)) + (dolist (n name-list objects) + (setf objects (cons (get-game-object object-type 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 ((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." (funcall list-function object-type place))) - -(defun monster-strikes-player (monster player) - "The monster attacks a player" - ;;TODO - ) diff --git a/lisp/interpreter.lisp b/lisp/interpreter.lisp index 8f69337..03d4886 100644 --- a/lisp/interpreter.lisp +++ b/lisp/interpreter.lisp @@ -99,7 +99,7 @@ ;; interpret an option command ((or (eql (aref line 0) #\Space) (eql (aref line 0) #\Tab)) - (setf line (string-left-trim '(#\Space #\Tab) line)) + (setf line (trim-whitespace line)) (set-object-attribute current-object (read-from-string line) (read-from-string (second (cut-string line (position #\space line)))))) diff --git a/lisp/ui.lisp b/lisp/ui.lisp index 0fafe21..7d0c25d 100644 --- a/lisp/ui.lisp +++ b/lisp/ui.lisp @@ -25,6 +25,7 @@ (add-game-object player) (set-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player))) + (objectify-place-monsters (player-place player)) ;; The actual game loop (clear-screen) (let ((place (get-game-object 'place (player-place player)))) @@ -93,7 +94,8 @@ (format t "~&Players present: ~A" (string-from-list (place-player p))) (format t "~&Items: ~A" (string-from-list (place-item p))) (format t "~&NPCs: ~A" (string-from-list (place-npc p))) - (format t "~&Monsters: ~A" (string-from-list (place-monster p)))) + (format t "~&Monsters: ~A" (string-from-list + (list-place-objects 'monster p)))) (defun game-command (cmd player) "Execute a typed-in game command" @@ -194,6 +196,7 @@ (defun goto (player &optional location) "Go to the specified location" + ;; Look before you leap (unless location (format t "~&Please specify a location!") (return-from goto)) @@ -204,12 +207,14 @@ :test #'equalp)) (format t "~&This place does not border your current location!") (return-from goto)) + ;; Do all the necessary housekeeping (clear-screen) (debugging "~&~A is going to ~A." (player-name player) location) + (objectify-place-monsters location) (remove-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player)) (set-object-attribute player 'place location) - (set-object-attribute (get-game-object 'place (player-place player)) + (set-object-attribute (get-game-object 'place location) 'player (player-name player)) (describe-place location)) @@ -226,9 +231,9 @@ (let ((place (get-game-object 'place (player-place player))) (description NIL)) (macrolet ((set-descr (type) - (let ((place-descr (build-symbol type "-description")) - (place-object (build-symbol "place-" type))) - `(when (member object-name (,place-object place) + (let ((place-descr (build-symbol type "-description"))) + `(when (member object-name + (list-place-objects ',type place) :test #'equalp) (setf description (,place-descr (get-game-object ',type @@ -344,7 +349,8 @@ (format t "~&Please specify an opponent!") (return-from attack)) (unless (member opponent - (list-place-objects 'monster (player-place player)) + (list-place-objects 'monster + (get-game-object 'place (player-place player))) :test #'equalp) (format t "~&This monster is not here!") (return-from attack)) @@ -363,7 +369,6 @@ (if (> (+ (random 10) p-dex) (+ (random 10) m-dex)) (setf damage (calculate-damage p-str p-weapon m-dex m-ac)) (setf damage (- 0 (calculate-damage m-str m-weapon p-dex p-ac)))) - ;(break) (cond ((plusp damage) (decf (monster-health monster) damage) (format t "~&You hit! ~A points damage." damage) diff --git a/lisp/util.lisp b/lisp/util.lisp index 3ad8306..3ce44e3 100644 --- a/lisp/util.lisp +++ b/lisp/util.lisp @@ -98,13 +98,13 @@ ;;; FUNCTIONS -; Some of these functions are probably quite inefficient (lots of consing) +;; Some of these functions are probably quite inefficient (lots of consing) -;; XXX DEPRECATED Not actually needed anywhere -(defun call-function (function-name &rest args) - "Save myself some quoting when calling a function from a generated symbol" - ;; Perhaps not very clean, but it works - (eval `(,function-name ,@args))) +(defun remove-first-if (fn lst) + "Remove the first element in a list that satisfies the given predicate" + (cond ((null lst) NIL) + ((funcall fn (car lst)) (cdr lst)) + (T (cons (car lst) (remove-first-if fn (cdr lst)))))) (defun average (&rest numbers) "Compute the average of the given numbers" @@ -205,17 +205,19 @@ (read-from-string (string-from-list components ""))) (defun make-list-function (container-type &optional (add-s t)) - "Return function to return a list of the names of all objects of the + "Return a function to return a list of the names of all objects of the specified type in the container struct" #'(lambda (object-type container) - (let ((get-objects (symbol-function (build-symbol container-type "-" - object-type (if add-s "s" "")))) - (get-object-name (symbol-function - (build-symbol object-type "-name"))) - (name-list NIL)) - (dolist (object (funcall get-objects container) name-list) + (let* ((get-objects (symbol-function + (build-symbol container-type "-" + object-type (if add-s "s" "")))) + (get-object-name (symbol-function + (build-symbol object-type "-name"))) + (objects (funcall get-objects container)) (name-list NIL)) + (dolist (o objects name-list) + (when (stringp o) (return objects)) (setf name-list - (cons (funcall get-object-name object) name-list)))))) + (cons (funcall get-object-name o) name-list)))))) (defun choose-number-option (option-list) "The user chooses one out of a list of options, the index is returned" diff --git a/python/__pycache__/character.cpython-34.pyc b/python/__pycache__/character.cpython-34.pyc deleted file mode 100644 index 20a9d5a..0000000 --- a/python/__pycache__/character.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/client.cpython-34.pyc b/python/__pycache__/client.cpython-34.pyc deleted file mode 100644 index 077e656..0000000 --- a/python/__pycache__/client.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/define.cpython-34.pyc b/python/__pycache__/define.cpython-34.pyc deleted file mode 100644 index 397fe49..0000000 --- a/python/__pycache__/define.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/interpreter.cpython-32.pyc b/python/__pycache__/interpreter.cpython-32.pyc deleted file mode 100644 index 2d36d9f..0000000 --- a/python/__pycache__/interpreter.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/interpreter.cpython-34.pyc b/python/__pycache__/interpreter.cpython-34.pyc deleted file mode 100644 index 3d8f05b..0000000 --- a/python/__pycache__/interpreter.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/item.cpython-34.pyc b/python/__pycache__/item.cpython-34.pyc deleted file mode 100644 index bc4b363..0000000 --- a/python/__pycache__/item.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/parser.cpython-34.pyc b/python/__pycache__/parser.cpython-34.pyc deleted file mode 100644 index ffeaf68..0000000 --- a/python/__pycache__/parser.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/place.cpython-32.pyc b/python/__pycache__/place.cpython-32.pyc deleted file mode 100644 index 721a318..0000000 --- a/python/__pycache__/place.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/place.cpython-34.pyc b/python/__pycache__/place.cpython-34.pyc deleted file mode 100644 index bf21fc7..0000000 --- a/python/__pycache__/place.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/server.cpython-34.pyc b/python/__pycache__/server.cpython-34.pyc deleted file mode 100644 index 3ac6f1d..0000000 --- a/python/__pycache__/server.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/lisp/game-objects.lisp b/lisp/game-objects.lisp index 080bc7e..585cc9b 100644 --- a/lisp/game-objects.lisp +++ b/lisp/game-objects.lisp @@ -35,6 +35,7 @@ (strength 0) (dexterity 0) (aggression 0) + (spawn-probability 0) (weapon "") (armour-class 0)) @@ -48,7 +49,7 @@ (defstruct weapon (name "") (description "") - (type "generic") + (type "") (damage 0)) @@ -69,21 +70,29 @@ ;; Same comment applies as above (let ((command (build-symbol (type-of game-object) "-" property))) (eval `(if (listp (,command ,game-object)) - ;; FIXME This gives problems with multiple values (setf (,command ,game-object) - (remove-if #'(lambda (x) (equalp x ,value)) + (remove-first-if #'(lambda (x) (equalp x ,value)) (,command ,game-object))) ;; TODO set numbers to 0, strings to "" (setf (,command ,game-object) NIL))))) -;; XXX This function is probably superfluous, as all place objects are stored -;; in world, with only their names recorded in the place +(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)) + (dolist (n name-list objects) + (setf objects (cons (get-game-object object-type 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 ((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." (funcall list-function object-type place))) - -(defun monster-strikes-player (monster player) - "The monster attacks a player" - ;;TODO - ) diff --git a/lisp/interpreter.lisp b/lisp/interpreter.lisp index 8f69337..03d4886 100644 --- a/lisp/interpreter.lisp +++ b/lisp/interpreter.lisp @@ -99,7 +99,7 @@ ;; interpret an option command ((or (eql (aref line 0) #\Space) (eql (aref line 0) #\Tab)) - (setf line (string-left-trim '(#\Space #\Tab) line)) + (setf line (trim-whitespace line)) (set-object-attribute current-object (read-from-string line) (read-from-string (second (cut-string line (position #\space line)))))) diff --git a/lisp/ui.lisp b/lisp/ui.lisp index 0fafe21..7d0c25d 100644 --- a/lisp/ui.lisp +++ b/lisp/ui.lisp @@ -25,6 +25,7 @@ (add-game-object player) (set-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player))) + (objectify-place-monsters (player-place player)) ;; The actual game loop (clear-screen) (let ((place (get-game-object 'place (player-place player)))) @@ -93,7 +94,8 @@ (format t "~&Players present: ~A" (string-from-list (place-player p))) (format t "~&Items: ~A" (string-from-list (place-item p))) (format t "~&NPCs: ~A" (string-from-list (place-npc p))) - (format t "~&Monsters: ~A" (string-from-list (place-monster p)))) + (format t "~&Monsters: ~A" (string-from-list + (list-place-objects 'monster p)))) (defun game-command (cmd player) "Execute a typed-in game command" @@ -194,6 +196,7 @@ (defun goto (player &optional location) "Go to the specified location" + ;; Look before you leap (unless location (format t "~&Please specify a location!") (return-from goto)) @@ -204,12 +207,14 @@ :test #'equalp)) (format t "~&This place does not border your current location!") (return-from goto)) + ;; Do all the necessary housekeeping (clear-screen) (debugging "~&~A is going to ~A." (player-name player) location) + (objectify-place-monsters location) (remove-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player)) (set-object-attribute player 'place location) - (set-object-attribute (get-game-object 'place (player-place player)) + (set-object-attribute (get-game-object 'place location) 'player (player-name player)) (describe-place location)) @@ -226,9 +231,9 @@ (let ((place (get-game-object 'place (player-place player))) (description NIL)) (macrolet ((set-descr (type) - (let ((place-descr (build-symbol type "-description")) - (place-object (build-symbol "place-" type))) - `(when (member object-name (,place-object place) + (let ((place-descr (build-symbol type "-description"))) + `(when (member object-name + (list-place-objects ',type place) :test #'equalp) (setf description (,place-descr (get-game-object ',type @@ -344,7 +349,8 @@ (format t "~&Please specify an opponent!") (return-from attack)) (unless (member opponent - (list-place-objects 'monster (player-place player)) + (list-place-objects 'monster + (get-game-object 'place (player-place player))) :test #'equalp) (format t "~&This monster is not here!") (return-from attack)) @@ -363,7 +369,6 @@ (if (> (+ (random 10) p-dex) (+ (random 10) m-dex)) (setf damage (calculate-damage p-str p-weapon m-dex m-ac)) (setf damage (- 0 (calculate-damage m-str m-weapon p-dex p-ac)))) - ;(break) (cond ((plusp damage) (decf (monster-health monster) damage) (format t "~&You hit! ~A points damage." damage) diff --git a/lisp/util.lisp b/lisp/util.lisp index 3ad8306..3ce44e3 100644 --- a/lisp/util.lisp +++ b/lisp/util.lisp @@ -98,13 +98,13 @@ ;;; FUNCTIONS -; Some of these functions are probably quite inefficient (lots of consing) +;; Some of these functions are probably quite inefficient (lots of consing) -;; XXX DEPRECATED Not actually needed anywhere -(defun call-function (function-name &rest args) - "Save myself some quoting when calling a function from a generated symbol" - ;; Perhaps not very clean, but it works - (eval `(,function-name ,@args))) +(defun remove-first-if (fn lst) + "Remove the first element in a list that satisfies the given predicate" + (cond ((null lst) NIL) + ((funcall fn (car lst)) (cdr lst)) + (T (cons (car lst) (remove-first-if fn (cdr lst)))))) (defun average (&rest numbers) "Compute the average of the given numbers" @@ -205,17 +205,19 @@ (read-from-string (string-from-list components ""))) (defun make-list-function (container-type &optional (add-s t)) - "Return function to return a list of the names of all objects of the + "Return a function to return a list of the names of all objects of the specified type in the container struct" #'(lambda (object-type container) - (let ((get-objects (symbol-function (build-symbol container-type "-" - object-type (if add-s "s" "")))) - (get-object-name (symbol-function - (build-symbol object-type "-name"))) - (name-list NIL)) - (dolist (object (funcall get-objects container) name-list) + (let* ((get-objects (symbol-function + (build-symbol container-type "-" + object-type (if add-s "s" "")))) + (get-object-name (symbol-function + (build-symbol object-type "-name"))) + (objects (funcall get-objects container)) (name-list NIL)) + (dolist (o objects name-list) + (when (stringp o) (return objects)) (setf name-list - (cons (funcall get-object-name object) name-list)))))) + (cons (funcall get-object-name o) name-list)))))) (defun choose-number-option (option-list) "The user chooses one out of a list of options, the index is returned" diff --git a/python/__pycache__/character.cpython-34.pyc b/python/__pycache__/character.cpython-34.pyc deleted file mode 100644 index 20a9d5a..0000000 --- a/python/__pycache__/character.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/client.cpython-34.pyc b/python/__pycache__/client.cpython-34.pyc deleted file mode 100644 index 077e656..0000000 --- a/python/__pycache__/client.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/define.cpython-34.pyc b/python/__pycache__/define.cpython-34.pyc deleted file mode 100644 index 397fe49..0000000 --- a/python/__pycache__/define.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/interpreter.cpython-32.pyc b/python/__pycache__/interpreter.cpython-32.pyc deleted file mode 100644 index 2d36d9f..0000000 --- a/python/__pycache__/interpreter.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/interpreter.cpython-34.pyc b/python/__pycache__/interpreter.cpython-34.pyc deleted file mode 100644 index 3d8f05b..0000000 --- a/python/__pycache__/interpreter.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/item.cpython-34.pyc b/python/__pycache__/item.cpython-34.pyc deleted file mode 100644 index bc4b363..0000000 --- a/python/__pycache__/item.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/parser.cpython-34.pyc b/python/__pycache__/parser.cpython-34.pyc deleted file mode 100644 index ffeaf68..0000000 --- a/python/__pycache__/parser.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/place.cpython-32.pyc b/python/__pycache__/place.cpython-32.pyc deleted file mode 100644 index 721a318..0000000 --- a/python/__pycache__/place.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/place.cpython-34.pyc b/python/__pycache__/place.cpython-34.pyc deleted file mode 100644 index bf21fc7..0000000 --- a/python/__pycache__/place.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/server.cpython-34.pyc b/python/__pycache__/server.cpython-34.pyc deleted file mode 100644 index 3ac6f1d..0000000 --- a/python/__pycache__/server.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/world.cpython-32.pyc b/python/__pycache__/world.cpython-32.pyc deleted file mode 100644 index 4276447..0000000 --- a/python/__pycache__/world.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/lisp/game-objects.lisp b/lisp/game-objects.lisp index 080bc7e..585cc9b 100644 --- a/lisp/game-objects.lisp +++ b/lisp/game-objects.lisp @@ -35,6 +35,7 @@ (strength 0) (dexterity 0) (aggression 0) + (spawn-probability 0) (weapon "") (armour-class 0)) @@ -48,7 +49,7 @@ (defstruct weapon (name "") (description "") - (type "generic") + (type "") (damage 0)) @@ -69,21 +70,29 @@ ;; Same comment applies as above (let ((command (build-symbol (type-of game-object) "-" property))) (eval `(if (listp (,command ,game-object)) - ;; FIXME This gives problems with multiple values (setf (,command ,game-object) - (remove-if #'(lambda (x) (equalp x ,value)) + (remove-first-if #'(lambda (x) (equalp x ,value)) (,command ,game-object))) ;; TODO set numbers to 0, strings to "" (setf (,command ,game-object) NIL))))) -;; XXX This function is probably superfluous, as all place objects are stored -;; in world, with only their names recorded in the place +(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)) + (dolist (n name-list objects) + (setf objects (cons (get-game-object object-type 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 ((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." (funcall list-function object-type place))) - -(defun monster-strikes-player (monster player) - "The monster attacks a player" - ;;TODO - ) diff --git a/lisp/interpreter.lisp b/lisp/interpreter.lisp index 8f69337..03d4886 100644 --- a/lisp/interpreter.lisp +++ b/lisp/interpreter.lisp @@ -99,7 +99,7 @@ ;; interpret an option command ((or (eql (aref line 0) #\Space) (eql (aref line 0) #\Tab)) - (setf line (string-left-trim '(#\Space #\Tab) line)) + (setf line (trim-whitespace line)) (set-object-attribute current-object (read-from-string line) (read-from-string (second (cut-string line (position #\space line)))))) diff --git a/lisp/ui.lisp b/lisp/ui.lisp index 0fafe21..7d0c25d 100644 --- a/lisp/ui.lisp +++ b/lisp/ui.lisp @@ -25,6 +25,7 @@ (add-game-object player) (set-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player))) + (objectify-place-monsters (player-place player)) ;; The actual game loop (clear-screen) (let ((place (get-game-object 'place (player-place player)))) @@ -93,7 +94,8 @@ (format t "~&Players present: ~A" (string-from-list (place-player p))) (format t "~&Items: ~A" (string-from-list (place-item p))) (format t "~&NPCs: ~A" (string-from-list (place-npc p))) - (format t "~&Monsters: ~A" (string-from-list (place-monster p)))) + (format t "~&Monsters: ~A" (string-from-list + (list-place-objects 'monster p)))) (defun game-command (cmd player) "Execute a typed-in game command" @@ -194,6 +196,7 @@ (defun goto (player &optional location) "Go to the specified location" + ;; Look before you leap (unless location (format t "~&Please specify a location!") (return-from goto)) @@ -204,12 +207,14 @@ :test #'equalp)) (format t "~&This place does not border your current location!") (return-from goto)) + ;; Do all the necessary housekeeping (clear-screen) (debugging "~&~A is going to ~A." (player-name player) location) + (objectify-place-monsters location) (remove-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player)) (set-object-attribute player 'place location) - (set-object-attribute (get-game-object 'place (player-place player)) + (set-object-attribute (get-game-object 'place location) 'player (player-name player)) (describe-place location)) @@ -226,9 +231,9 @@ (let ((place (get-game-object 'place (player-place player))) (description NIL)) (macrolet ((set-descr (type) - (let ((place-descr (build-symbol type "-description")) - (place-object (build-symbol "place-" type))) - `(when (member object-name (,place-object place) + (let ((place-descr (build-symbol type "-description"))) + `(when (member object-name + (list-place-objects ',type place) :test #'equalp) (setf description (,place-descr (get-game-object ',type @@ -344,7 +349,8 @@ (format t "~&Please specify an opponent!") (return-from attack)) (unless (member opponent - (list-place-objects 'monster (player-place player)) + (list-place-objects 'monster + (get-game-object 'place (player-place player))) :test #'equalp) (format t "~&This monster is not here!") (return-from attack)) @@ -363,7 +369,6 @@ (if (> (+ (random 10) p-dex) (+ (random 10) m-dex)) (setf damage (calculate-damage p-str p-weapon m-dex m-ac)) (setf damage (- 0 (calculate-damage m-str m-weapon p-dex p-ac)))) - ;(break) (cond ((plusp damage) (decf (monster-health monster) damage) (format t "~&You hit! ~A points damage." damage) diff --git a/lisp/util.lisp b/lisp/util.lisp index 3ad8306..3ce44e3 100644 --- a/lisp/util.lisp +++ b/lisp/util.lisp @@ -98,13 +98,13 @@ ;;; FUNCTIONS -; Some of these functions are probably quite inefficient (lots of consing) +;; Some of these functions are probably quite inefficient (lots of consing) -;; XXX DEPRECATED Not actually needed anywhere -(defun call-function (function-name &rest args) - "Save myself some quoting when calling a function from a generated symbol" - ;; Perhaps not very clean, but it works - (eval `(,function-name ,@args))) +(defun remove-first-if (fn lst) + "Remove the first element in a list that satisfies the given predicate" + (cond ((null lst) NIL) + ((funcall fn (car lst)) (cdr lst)) + (T (cons (car lst) (remove-first-if fn (cdr lst)))))) (defun average (&rest numbers) "Compute the average of the given numbers" @@ -205,17 +205,19 @@ (read-from-string (string-from-list components ""))) (defun make-list-function (container-type &optional (add-s t)) - "Return function to return a list of the names of all objects of the + "Return a function to return a list of the names of all objects of the specified type in the container struct" #'(lambda (object-type container) - (let ((get-objects (symbol-function (build-symbol container-type "-" - object-type (if add-s "s" "")))) - (get-object-name (symbol-function - (build-symbol object-type "-name"))) - (name-list NIL)) - (dolist (object (funcall get-objects container) name-list) + (let* ((get-objects (symbol-function + (build-symbol container-type "-" + object-type (if add-s "s" "")))) + (get-object-name (symbol-function + (build-symbol object-type "-name"))) + (objects (funcall get-objects container)) (name-list NIL)) + (dolist (o objects name-list) + (when (stringp o) (return objects)) (setf name-list - (cons (funcall get-object-name object) name-list)))))) + (cons (funcall get-object-name o) name-list)))))) (defun choose-number-option (option-list) "The user chooses one out of a list of options, the index is returned" diff --git a/python/__pycache__/character.cpython-34.pyc b/python/__pycache__/character.cpython-34.pyc deleted file mode 100644 index 20a9d5a..0000000 --- a/python/__pycache__/character.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/client.cpython-34.pyc b/python/__pycache__/client.cpython-34.pyc deleted file mode 100644 index 077e656..0000000 --- a/python/__pycache__/client.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/define.cpython-34.pyc b/python/__pycache__/define.cpython-34.pyc deleted file mode 100644 index 397fe49..0000000 --- a/python/__pycache__/define.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/interpreter.cpython-32.pyc b/python/__pycache__/interpreter.cpython-32.pyc deleted file mode 100644 index 2d36d9f..0000000 --- a/python/__pycache__/interpreter.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/interpreter.cpython-34.pyc b/python/__pycache__/interpreter.cpython-34.pyc deleted file mode 100644 index 3d8f05b..0000000 --- a/python/__pycache__/interpreter.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/item.cpython-34.pyc b/python/__pycache__/item.cpython-34.pyc deleted file mode 100644 index bc4b363..0000000 --- a/python/__pycache__/item.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/parser.cpython-34.pyc b/python/__pycache__/parser.cpython-34.pyc deleted file mode 100644 index ffeaf68..0000000 --- a/python/__pycache__/parser.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/place.cpython-32.pyc b/python/__pycache__/place.cpython-32.pyc deleted file mode 100644 index 721a318..0000000 --- a/python/__pycache__/place.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/place.cpython-34.pyc b/python/__pycache__/place.cpython-34.pyc deleted file mode 100644 index bf21fc7..0000000 --- a/python/__pycache__/place.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/server.cpython-34.pyc b/python/__pycache__/server.cpython-34.pyc deleted file mode 100644 index 3ac6f1d..0000000 --- a/python/__pycache__/server.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/world.cpython-32.pyc b/python/__pycache__/world.cpython-32.pyc deleted file mode 100644 index 4276447..0000000 --- a/python/__pycache__/world.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/world.cpython-34.pyc b/python/__pycache__/world.cpython-34.pyc deleted file mode 100644 index 1814006..0000000 --- a/python/__pycache__/world.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/lisp/game-objects.lisp b/lisp/game-objects.lisp index 080bc7e..585cc9b 100644 --- a/lisp/game-objects.lisp +++ b/lisp/game-objects.lisp @@ -35,6 +35,7 @@ (strength 0) (dexterity 0) (aggression 0) + (spawn-probability 0) (weapon "") (armour-class 0)) @@ -48,7 +49,7 @@ (defstruct weapon (name "") (description "") - (type "generic") + (type "") (damage 0)) @@ -69,21 +70,29 @@ ;; Same comment applies as above (let ((command (build-symbol (type-of game-object) "-" property))) (eval `(if (listp (,command ,game-object)) - ;; FIXME This gives problems with multiple values (setf (,command ,game-object) - (remove-if #'(lambda (x) (equalp x ,value)) + (remove-first-if #'(lambda (x) (equalp x ,value)) (,command ,game-object))) ;; TODO set numbers to 0, strings to "" (setf (,command ,game-object) NIL))))) -;; XXX This function is probably superfluous, as all place objects are stored -;; in world, with only their names recorded in the place +(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)) + (dolist (n name-list objects) + (setf objects (cons (get-game-object object-type 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 ((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." (funcall list-function object-type place))) - -(defun monster-strikes-player (monster player) - "The monster attacks a player" - ;;TODO - ) diff --git a/lisp/interpreter.lisp b/lisp/interpreter.lisp index 8f69337..03d4886 100644 --- a/lisp/interpreter.lisp +++ b/lisp/interpreter.lisp @@ -99,7 +99,7 @@ ;; interpret an option command ((or (eql (aref line 0) #\Space) (eql (aref line 0) #\Tab)) - (setf line (string-left-trim '(#\Space #\Tab) line)) + (setf line (trim-whitespace line)) (set-object-attribute current-object (read-from-string line) (read-from-string (second (cut-string line (position #\space line)))))) diff --git a/lisp/ui.lisp b/lisp/ui.lisp index 0fafe21..7d0c25d 100644 --- a/lisp/ui.lisp +++ b/lisp/ui.lisp @@ -25,6 +25,7 @@ (add-game-object player) (set-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player))) + (objectify-place-monsters (player-place player)) ;; The actual game loop (clear-screen) (let ((place (get-game-object 'place (player-place player)))) @@ -93,7 +94,8 @@ (format t "~&Players present: ~A" (string-from-list (place-player p))) (format t "~&Items: ~A" (string-from-list (place-item p))) (format t "~&NPCs: ~A" (string-from-list (place-npc p))) - (format t "~&Monsters: ~A" (string-from-list (place-monster p)))) + (format t "~&Monsters: ~A" (string-from-list + (list-place-objects 'monster p)))) (defun game-command (cmd player) "Execute a typed-in game command" @@ -194,6 +196,7 @@ (defun goto (player &optional location) "Go to the specified location" + ;; Look before you leap (unless location (format t "~&Please specify a location!") (return-from goto)) @@ -204,12 +207,14 @@ :test #'equalp)) (format t "~&This place does not border your current location!") (return-from goto)) + ;; Do all the necessary housekeeping (clear-screen) (debugging "~&~A is going to ~A." (player-name player) location) + (objectify-place-monsters location) (remove-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player)) (set-object-attribute player 'place location) - (set-object-attribute (get-game-object 'place (player-place player)) + (set-object-attribute (get-game-object 'place location) 'player (player-name player)) (describe-place location)) @@ -226,9 +231,9 @@ (let ((place (get-game-object 'place (player-place player))) (description NIL)) (macrolet ((set-descr (type) - (let ((place-descr (build-symbol type "-description")) - (place-object (build-symbol "place-" type))) - `(when (member object-name (,place-object place) + (let ((place-descr (build-symbol type "-description"))) + `(when (member object-name + (list-place-objects ',type place) :test #'equalp) (setf description (,place-descr (get-game-object ',type @@ -344,7 +349,8 @@ (format t "~&Please specify an opponent!") (return-from attack)) (unless (member opponent - (list-place-objects 'monster (player-place player)) + (list-place-objects 'monster + (get-game-object 'place (player-place player))) :test #'equalp) (format t "~&This monster is not here!") (return-from attack)) @@ -363,7 +369,6 @@ (if (> (+ (random 10) p-dex) (+ (random 10) m-dex)) (setf damage (calculate-damage p-str p-weapon m-dex m-ac)) (setf damage (- 0 (calculate-damage m-str m-weapon p-dex p-ac)))) - ;(break) (cond ((plusp damage) (decf (monster-health monster) damage) (format t "~&You hit! ~A points damage." damage) diff --git a/lisp/util.lisp b/lisp/util.lisp index 3ad8306..3ce44e3 100644 --- a/lisp/util.lisp +++ b/lisp/util.lisp @@ -98,13 +98,13 @@ ;;; FUNCTIONS -; Some of these functions are probably quite inefficient (lots of consing) +;; Some of these functions are probably quite inefficient (lots of consing) -;; XXX DEPRECATED Not actually needed anywhere -(defun call-function (function-name &rest args) - "Save myself some quoting when calling a function from a generated symbol" - ;; Perhaps not very clean, but it works - (eval `(,function-name ,@args))) +(defun remove-first-if (fn lst) + "Remove the first element in a list that satisfies the given predicate" + (cond ((null lst) NIL) + ((funcall fn (car lst)) (cdr lst)) + (T (cons (car lst) (remove-first-if fn (cdr lst)))))) (defun average (&rest numbers) "Compute the average of the given numbers" @@ -205,17 +205,19 @@ (read-from-string (string-from-list components ""))) (defun make-list-function (container-type &optional (add-s t)) - "Return function to return a list of the names of all objects of the + "Return a function to return a list of the names of all objects of the specified type in the container struct" #'(lambda (object-type container) - (let ((get-objects (symbol-function (build-symbol container-type "-" - object-type (if add-s "s" "")))) - (get-object-name (symbol-function - (build-symbol object-type "-name"))) - (name-list NIL)) - (dolist (object (funcall get-objects container) name-list) + (let* ((get-objects (symbol-function + (build-symbol container-type "-" + object-type (if add-s "s" "")))) + (get-object-name (symbol-function + (build-symbol object-type "-name"))) + (objects (funcall get-objects container)) (name-list NIL)) + (dolist (o objects name-list) + (when (stringp o) (return objects)) (setf name-list - (cons (funcall get-object-name object) name-list)))))) + (cons (funcall get-object-name o) name-list)))))) (defun choose-number-option (option-list) "The user chooses one out of a list of options, the index is returned" diff --git a/python/__pycache__/character.cpython-34.pyc b/python/__pycache__/character.cpython-34.pyc deleted file mode 100644 index 20a9d5a..0000000 --- a/python/__pycache__/character.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/client.cpython-34.pyc b/python/__pycache__/client.cpython-34.pyc deleted file mode 100644 index 077e656..0000000 --- a/python/__pycache__/client.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/define.cpython-34.pyc b/python/__pycache__/define.cpython-34.pyc deleted file mode 100644 index 397fe49..0000000 --- a/python/__pycache__/define.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/interpreter.cpython-32.pyc b/python/__pycache__/interpreter.cpython-32.pyc deleted file mode 100644 index 2d36d9f..0000000 --- a/python/__pycache__/interpreter.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/interpreter.cpython-34.pyc b/python/__pycache__/interpreter.cpython-34.pyc deleted file mode 100644 index 3d8f05b..0000000 --- a/python/__pycache__/interpreter.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/item.cpython-34.pyc b/python/__pycache__/item.cpython-34.pyc deleted file mode 100644 index bc4b363..0000000 --- a/python/__pycache__/item.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/parser.cpython-34.pyc b/python/__pycache__/parser.cpython-34.pyc deleted file mode 100644 index ffeaf68..0000000 --- a/python/__pycache__/parser.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/place.cpython-32.pyc b/python/__pycache__/place.cpython-32.pyc deleted file mode 100644 index 721a318..0000000 --- a/python/__pycache__/place.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/place.cpython-34.pyc b/python/__pycache__/place.cpython-34.pyc deleted file mode 100644 index bf21fc7..0000000 --- a/python/__pycache__/place.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/server.cpython-34.pyc b/python/__pycache__/server.cpython-34.pyc deleted file mode 100644 index 3ac6f1d..0000000 --- a/python/__pycache__/server.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/world.cpython-32.pyc b/python/__pycache__/world.cpython-32.pyc deleted file mode 100644 index 4276447..0000000 --- a/python/__pycache__/world.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/world.cpython-34.pyc b/python/__pycache__/world.cpython-34.pyc deleted file mode 100644 index 1814006..0000000 --- a/python/__pycache__/world.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/atlantis.py b/python/atlantis.py deleted file mode 100644 index 2c5802f..0000000 --- a/python/atlantis.py +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 02/05/2015 -# - -VERSION = (0, 0, 8) #release, major revision, minor (git) revision - -import sys -import os -from server import Server -from client import Client - - -global clear -if "linux" in sys.platform: - clear = "clear" -elif "win" in sys.platform: - clear = "cls" - - -def start_menu(): - ''' - Show the start menu and process the player's input - ''' - global clear - os.system(clear) - try: #Print the Atlantis logo - fn = os.path.join(os.path.dirname(__file__), "banner.txt") - img_file = open(fn, "r") - logo = img_file.read() - img_file.close() - print(logo) - except IOError: - print("Could not find logo!") - print("Welcome! What do you want to do?") - print(" -> (S)tart a server") - print(" -> (J)oin game") - print(" -> (A)bout") - print(" -> (E)xit") - print() - choice = input("Please choose an option: ") - act_on_choice(choice) - -#TODO! -def act_on_choice(choice): - if choice == "s" or choice == "S": - print("What port do you want to start the server on?") #TODO indicate range! - server_port = input(">> ") - print("What world file do you want to load?") - print("(Specify an absolute or relative path)") - world_file = input(">> ") - Server(server_port, world_file) - elif choice == "j" or choice == "J": - print("What server do you want to connect to?") - print("Format: :") - server_address = input(">> ") - server_ip = server_address[:server_address.find(":")] - server_port = server_address[server_address.find(":")+1:] - Client(server_ip, server_port) - elif choice == "a" or choice == "A": - print_version() - print("(c) 2015 Daniel Vedder") - input("\nPlease press ENTER") - start_menu() - elif choice == "e" or choice == "E": - print("Goodbye!") - exit() - else: - print("Invalid choice!") - input("Please press ENTER") - start_menu() - - -def print_version(): - version_string = str(VERSION[0])+"."+str(VERSION[1])+"."+str(VERSION[2]) - print("Atlantis "+version_string) - print("Licensed under the terms of the GNU General Public License v3.") - -def print_help(): - help_text = """ -Usage: atlantis [options] - - --help -h Print this help text and exit - --version -v Print the version number and exit - --server Start an atlantis server on (requires --world) - --world Use as the world file (requires --server) - --client : Connect as a client to the server at : - -If no arguments are passed, atlantis starts an interactive interface. -""" - print_version() - print(help_text) - -def main(): - if "--version" in sys.argv or "-v" in sys.argv: print_version() - elif "--help" in sys.argv or "-h" in sys.argv: print_help() - elif "--server" in sys.argv and "--world" in sys.argv: - server_port = sys.argv[sys.argv.index("--server")+1] - world_file = sys.argv[sys.argv.index("--world")+1] - Server(server_port, world_file) - elif "--client" in sys.argv: - server_address = sys.argv[sys.argv.index("--client")+1] - server_ip = server_address[:server_address.find(":")] - server_port = server_address[server_address.find(":")+1:] - Client(server_ip, server_port) - else: - start_menu() - -if __name__ == "__main__": - main() diff --git a/lisp/game-objects.lisp b/lisp/game-objects.lisp index 080bc7e..585cc9b 100644 --- a/lisp/game-objects.lisp +++ b/lisp/game-objects.lisp @@ -35,6 +35,7 @@ (strength 0) (dexterity 0) (aggression 0) + (spawn-probability 0) (weapon "") (armour-class 0)) @@ -48,7 +49,7 @@ (defstruct weapon (name "") (description "") - (type "generic") + (type "") (damage 0)) @@ -69,21 +70,29 @@ ;; Same comment applies as above (let ((command (build-symbol (type-of game-object) "-" property))) (eval `(if (listp (,command ,game-object)) - ;; FIXME This gives problems with multiple values (setf (,command ,game-object) - (remove-if #'(lambda (x) (equalp x ,value)) + (remove-first-if #'(lambda (x) (equalp x ,value)) (,command ,game-object))) ;; TODO set numbers to 0, strings to "" (setf (,command ,game-object) NIL))))) -;; XXX This function is probably superfluous, as all place objects are stored -;; in world, with only their names recorded in the place +(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)) + (dolist (n name-list objects) + (setf objects (cons (get-game-object object-type 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 ((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." (funcall list-function object-type place))) - -(defun monster-strikes-player (monster player) - "The monster attacks a player" - ;;TODO - ) diff --git a/lisp/interpreter.lisp b/lisp/interpreter.lisp index 8f69337..03d4886 100644 --- a/lisp/interpreter.lisp +++ b/lisp/interpreter.lisp @@ -99,7 +99,7 @@ ;; interpret an option command ((or (eql (aref line 0) #\Space) (eql (aref line 0) #\Tab)) - (setf line (string-left-trim '(#\Space #\Tab) line)) + (setf line (trim-whitespace line)) (set-object-attribute current-object (read-from-string line) (read-from-string (second (cut-string line (position #\space line)))))) diff --git a/lisp/ui.lisp b/lisp/ui.lisp index 0fafe21..7d0c25d 100644 --- a/lisp/ui.lisp +++ b/lisp/ui.lisp @@ -25,6 +25,7 @@ (add-game-object player) (set-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player))) + (objectify-place-monsters (player-place player)) ;; The actual game loop (clear-screen) (let ((place (get-game-object 'place (player-place player)))) @@ -93,7 +94,8 @@ (format t "~&Players present: ~A" (string-from-list (place-player p))) (format t "~&Items: ~A" (string-from-list (place-item p))) (format t "~&NPCs: ~A" (string-from-list (place-npc p))) - (format t "~&Monsters: ~A" (string-from-list (place-monster p)))) + (format t "~&Monsters: ~A" (string-from-list + (list-place-objects 'monster p)))) (defun game-command (cmd player) "Execute a typed-in game command" @@ -194,6 +196,7 @@ (defun goto (player &optional location) "Go to the specified location" + ;; Look before you leap (unless location (format t "~&Please specify a location!") (return-from goto)) @@ -204,12 +207,14 @@ :test #'equalp)) (format t "~&This place does not border your current location!") (return-from goto)) + ;; Do all the necessary housekeeping (clear-screen) (debugging "~&~A is going to ~A." (player-name player) location) + (objectify-place-monsters location) (remove-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player)) (set-object-attribute player 'place location) - (set-object-attribute (get-game-object 'place (player-place player)) + (set-object-attribute (get-game-object 'place location) 'player (player-name player)) (describe-place location)) @@ -226,9 +231,9 @@ (let ((place (get-game-object 'place (player-place player))) (description NIL)) (macrolet ((set-descr (type) - (let ((place-descr (build-symbol type "-description")) - (place-object (build-symbol "place-" type))) - `(when (member object-name (,place-object place) + (let ((place-descr (build-symbol type "-description"))) + `(when (member object-name + (list-place-objects ',type place) :test #'equalp) (setf description (,place-descr (get-game-object ',type @@ -344,7 +349,8 @@ (format t "~&Please specify an opponent!") (return-from attack)) (unless (member opponent - (list-place-objects 'monster (player-place player)) + (list-place-objects 'monster + (get-game-object 'place (player-place player))) :test #'equalp) (format t "~&This monster is not here!") (return-from attack)) @@ -363,7 +369,6 @@ (if (> (+ (random 10) p-dex) (+ (random 10) m-dex)) (setf damage (calculate-damage p-str p-weapon m-dex m-ac)) (setf damage (- 0 (calculate-damage m-str m-weapon p-dex p-ac)))) - ;(break) (cond ((plusp damage) (decf (monster-health monster) damage) (format t "~&You hit! ~A points damage." damage) diff --git a/lisp/util.lisp b/lisp/util.lisp index 3ad8306..3ce44e3 100644 --- a/lisp/util.lisp +++ b/lisp/util.lisp @@ -98,13 +98,13 @@ ;;; FUNCTIONS -; Some of these functions are probably quite inefficient (lots of consing) +;; Some of these functions are probably quite inefficient (lots of consing) -;; XXX DEPRECATED Not actually needed anywhere -(defun call-function (function-name &rest args) - "Save myself some quoting when calling a function from a generated symbol" - ;; Perhaps not very clean, but it works - (eval `(,function-name ,@args))) +(defun remove-first-if (fn lst) + "Remove the first element in a list that satisfies the given predicate" + (cond ((null lst) NIL) + ((funcall fn (car lst)) (cdr lst)) + (T (cons (car lst) (remove-first-if fn (cdr lst)))))) (defun average (&rest numbers) "Compute the average of the given numbers" @@ -205,17 +205,19 @@ (read-from-string (string-from-list components ""))) (defun make-list-function (container-type &optional (add-s t)) - "Return function to return a list of the names of all objects of the + "Return a function to return a list of the names of all objects of the specified type in the container struct" #'(lambda (object-type container) - (let ((get-objects (symbol-function (build-symbol container-type "-" - object-type (if add-s "s" "")))) - (get-object-name (symbol-function - (build-symbol object-type "-name"))) - (name-list NIL)) - (dolist (object (funcall get-objects container) name-list) + (let* ((get-objects (symbol-function + (build-symbol container-type "-" + object-type (if add-s "s" "")))) + (get-object-name (symbol-function + (build-symbol object-type "-name"))) + (objects (funcall get-objects container)) (name-list NIL)) + (dolist (o objects name-list) + (when (stringp o) (return objects)) (setf name-list - (cons (funcall get-object-name object) name-list)))))) + (cons (funcall get-object-name o) name-list)))))) (defun choose-number-option (option-list) "The user chooses one out of a list of options, the index is returned" diff --git a/python/__pycache__/character.cpython-34.pyc b/python/__pycache__/character.cpython-34.pyc deleted file mode 100644 index 20a9d5a..0000000 --- a/python/__pycache__/character.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/client.cpython-34.pyc b/python/__pycache__/client.cpython-34.pyc deleted file mode 100644 index 077e656..0000000 --- a/python/__pycache__/client.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/define.cpython-34.pyc b/python/__pycache__/define.cpython-34.pyc deleted file mode 100644 index 397fe49..0000000 --- a/python/__pycache__/define.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/interpreter.cpython-32.pyc b/python/__pycache__/interpreter.cpython-32.pyc deleted file mode 100644 index 2d36d9f..0000000 --- a/python/__pycache__/interpreter.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/interpreter.cpython-34.pyc b/python/__pycache__/interpreter.cpython-34.pyc deleted file mode 100644 index 3d8f05b..0000000 --- a/python/__pycache__/interpreter.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/item.cpython-34.pyc b/python/__pycache__/item.cpython-34.pyc deleted file mode 100644 index bc4b363..0000000 --- a/python/__pycache__/item.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/parser.cpython-34.pyc b/python/__pycache__/parser.cpython-34.pyc deleted file mode 100644 index ffeaf68..0000000 --- a/python/__pycache__/parser.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/place.cpython-32.pyc b/python/__pycache__/place.cpython-32.pyc deleted file mode 100644 index 721a318..0000000 --- a/python/__pycache__/place.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/place.cpython-34.pyc b/python/__pycache__/place.cpython-34.pyc deleted file mode 100644 index bf21fc7..0000000 --- a/python/__pycache__/place.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/server.cpython-34.pyc b/python/__pycache__/server.cpython-34.pyc deleted file mode 100644 index 3ac6f1d..0000000 --- a/python/__pycache__/server.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/world.cpython-32.pyc b/python/__pycache__/world.cpython-32.pyc deleted file mode 100644 index 4276447..0000000 --- a/python/__pycache__/world.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/world.cpython-34.pyc b/python/__pycache__/world.cpython-34.pyc deleted file mode 100644 index 1814006..0000000 --- a/python/__pycache__/world.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/atlantis.py b/python/atlantis.py deleted file mode 100644 index 2c5802f..0000000 --- a/python/atlantis.py +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 02/05/2015 -# - -VERSION = (0, 0, 8) #release, major revision, minor (git) revision - -import sys -import os -from server import Server -from client import Client - - -global clear -if "linux" in sys.platform: - clear = "clear" -elif "win" in sys.platform: - clear = "cls" - - -def start_menu(): - ''' - Show the start menu and process the player's input - ''' - global clear - os.system(clear) - try: #Print the Atlantis logo - fn = os.path.join(os.path.dirname(__file__), "banner.txt") - img_file = open(fn, "r") - logo = img_file.read() - img_file.close() - print(logo) - except IOError: - print("Could not find logo!") - print("Welcome! What do you want to do?") - print(" -> (S)tart a server") - print(" -> (J)oin game") - print(" -> (A)bout") - print(" -> (E)xit") - print() - choice = input("Please choose an option: ") - act_on_choice(choice) - -#TODO! -def act_on_choice(choice): - if choice == "s" or choice == "S": - print("What port do you want to start the server on?") #TODO indicate range! - server_port = input(">> ") - print("What world file do you want to load?") - print("(Specify an absolute or relative path)") - world_file = input(">> ") - Server(server_port, world_file) - elif choice == "j" or choice == "J": - print("What server do you want to connect to?") - print("Format: :") - server_address = input(">> ") - server_ip = server_address[:server_address.find(":")] - server_port = server_address[server_address.find(":")+1:] - Client(server_ip, server_port) - elif choice == "a" or choice == "A": - print_version() - print("(c) 2015 Daniel Vedder") - input("\nPlease press ENTER") - start_menu() - elif choice == "e" or choice == "E": - print("Goodbye!") - exit() - else: - print("Invalid choice!") - input("Please press ENTER") - start_menu() - - -def print_version(): - version_string = str(VERSION[0])+"."+str(VERSION[1])+"."+str(VERSION[2]) - print("Atlantis "+version_string) - print("Licensed under the terms of the GNU General Public License v3.") - -def print_help(): - help_text = """ -Usage: atlantis [options] - - --help -h Print this help text and exit - --version -v Print the version number and exit - --server Start an atlantis server on (requires --world) - --world Use as the world file (requires --server) - --client : Connect as a client to the server at : - -If no arguments are passed, atlantis starts an interactive interface. -""" - print_version() - print(help_text) - -def main(): - if "--version" in sys.argv or "-v" in sys.argv: print_version() - elif "--help" in sys.argv or "-h" in sys.argv: print_help() - elif "--server" in sys.argv and "--world" in sys.argv: - server_port = sys.argv[sys.argv.index("--server")+1] - world_file = sys.argv[sys.argv.index("--world")+1] - Server(server_port, world_file) - elif "--client" in sys.argv: - server_address = sys.argv[sys.argv.index("--client")+1] - server_ip = server_address[:server_address.find(":")] - server_port = server_address[server_address.find(":")+1:] - Client(server_ip, server_port) - else: - start_menu() - -if __name__ == "__main__": - main() diff --git a/python/banner.txt b/python/banner.txt deleted file mode 100644 index 68d8ee8..0000000 --- a/python/banner.txt +++ /dev/null @@ -1,10 +0,0 @@ -==================================================== -|| \ / || -|| - O -MM _ MM ATLANTIS || -|| / \ ||_/.\_|| || -|| |*| |*| Lost worlds await || -|| ~~~~~| | A | |~~~~ || -|| ~~~~###########~~~ || -|| ~~~#############~~ (c) 2015 Daniel Vedder || -|| ~~~~~~~~~~~~~~~~~~ || -==================================================== diff --git a/lisp/game-objects.lisp b/lisp/game-objects.lisp index 080bc7e..585cc9b 100644 --- a/lisp/game-objects.lisp +++ b/lisp/game-objects.lisp @@ -35,6 +35,7 @@ (strength 0) (dexterity 0) (aggression 0) + (spawn-probability 0) (weapon "") (armour-class 0)) @@ -48,7 +49,7 @@ (defstruct weapon (name "") (description "") - (type "generic") + (type "") (damage 0)) @@ -69,21 +70,29 @@ ;; Same comment applies as above (let ((command (build-symbol (type-of game-object) "-" property))) (eval `(if (listp (,command ,game-object)) - ;; FIXME This gives problems with multiple values (setf (,command ,game-object) - (remove-if #'(lambda (x) (equalp x ,value)) + (remove-first-if #'(lambda (x) (equalp x ,value)) (,command ,game-object))) ;; TODO set numbers to 0, strings to "" (setf (,command ,game-object) NIL))))) -;; XXX This function is probably superfluous, as all place objects are stored -;; in world, with only their names recorded in the place +(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)) + (dolist (n name-list objects) + (setf objects (cons (get-game-object object-type 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 ((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." (funcall list-function object-type place))) - -(defun monster-strikes-player (monster player) - "The monster attacks a player" - ;;TODO - ) diff --git a/lisp/interpreter.lisp b/lisp/interpreter.lisp index 8f69337..03d4886 100644 --- a/lisp/interpreter.lisp +++ b/lisp/interpreter.lisp @@ -99,7 +99,7 @@ ;; interpret an option command ((or (eql (aref line 0) #\Space) (eql (aref line 0) #\Tab)) - (setf line (string-left-trim '(#\Space #\Tab) line)) + (setf line (trim-whitespace line)) (set-object-attribute current-object (read-from-string line) (read-from-string (second (cut-string line (position #\space line)))))) diff --git a/lisp/ui.lisp b/lisp/ui.lisp index 0fafe21..7d0c25d 100644 --- a/lisp/ui.lisp +++ b/lisp/ui.lisp @@ -25,6 +25,7 @@ (add-game-object player) (set-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player))) + (objectify-place-monsters (player-place player)) ;; The actual game loop (clear-screen) (let ((place (get-game-object 'place (player-place player)))) @@ -93,7 +94,8 @@ (format t "~&Players present: ~A" (string-from-list (place-player p))) (format t "~&Items: ~A" (string-from-list (place-item p))) (format t "~&NPCs: ~A" (string-from-list (place-npc p))) - (format t "~&Monsters: ~A" (string-from-list (place-monster p)))) + (format t "~&Monsters: ~A" (string-from-list + (list-place-objects 'monster p)))) (defun game-command (cmd player) "Execute a typed-in game command" @@ -194,6 +196,7 @@ (defun goto (player &optional location) "Go to the specified location" + ;; Look before you leap (unless location (format t "~&Please specify a location!") (return-from goto)) @@ -204,12 +207,14 @@ :test #'equalp)) (format t "~&This place does not border your current location!") (return-from goto)) + ;; Do all the necessary housekeeping (clear-screen) (debugging "~&~A is going to ~A." (player-name player) location) + (objectify-place-monsters location) (remove-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player)) (set-object-attribute player 'place location) - (set-object-attribute (get-game-object 'place (player-place player)) + (set-object-attribute (get-game-object 'place location) 'player (player-name player)) (describe-place location)) @@ -226,9 +231,9 @@ (let ((place (get-game-object 'place (player-place player))) (description NIL)) (macrolet ((set-descr (type) - (let ((place-descr (build-symbol type "-description")) - (place-object (build-symbol "place-" type))) - `(when (member object-name (,place-object place) + (let ((place-descr (build-symbol type "-description"))) + `(when (member object-name + (list-place-objects ',type place) :test #'equalp) (setf description (,place-descr (get-game-object ',type @@ -344,7 +349,8 @@ (format t "~&Please specify an opponent!") (return-from attack)) (unless (member opponent - (list-place-objects 'monster (player-place player)) + (list-place-objects 'monster + (get-game-object 'place (player-place player))) :test #'equalp) (format t "~&This monster is not here!") (return-from attack)) @@ -363,7 +369,6 @@ (if (> (+ (random 10) p-dex) (+ (random 10) m-dex)) (setf damage (calculate-damage p-str p-weapon m-dex m-ac)) (setf damage (- 0 (calculate-damage m-str m-weapon p-dex p-ac)))) - ;(break) (cond ((plusp damage) (decf (monster-health monster) damage) (format t "~&You hit! ~A points damage." damage) diff --git a/lisp/util.lisp b/lisp/util.lisp index 3ad8306..3ce44e3 100644 --- a/lisp/util.lisp +++ b/lisp/util.lisp @@ -98,13 +98,13 @@ ;;; FUNCTIONS -; Some of these functions are probably quite inefficient (lots of consing) +;; Some of these functions are probably quite inefficient (lots of consing) -;; XXX DEPRECATED Not actually needed anywhere -(defun call-function (function-name &rest args) - "Save myself some quoting when calling a function from a generated symbol" - ;; Perhaps not very clean, but it works - (eval `(,function-name ,@args))) +(defun remove-first-if (fn lst) + "Remove the first element in a list that satisfies the given predicate" + (cond ((null lst) NIL) + ((funcall fn (car lst)) (cdr lst)) + (T (cons (car lst) (remove-first-if fn (cdr lst)))))) (defun average (&rest numbers) "Compute the average of the given numbers" @@ -205,17 +205,19 @@ (read-from-string (string-from-list components ""))) (defun make-list-function (container-type &optional (add-s t)) - "Return function to return a list of the names of all objects of the + "Return a function to return a list of the names of all objects of the specified type in the container struct" #'(lambda (object-type container) - (let ((get-objects (symbol-function (build-symbol container-type "-" - object-type (if add-s "s" "")))) - (get-object-name (symbol-function - (build-symbol object-type "-name"))) - (name-list NIL)) - (dolist (object (funcall get-objects container) name-list) + (let* ((get-objects (symbol-function + (build-symbol container-type "-" + object-type (if add-s "s" "")))) + (get-object-name (symbol-function + (build-symbol object-type "-name"))) + (objects (funcall get-objects container)) (name-list NIL)) + (dolist (o objects name-list) + (when (stringp o) (return objects)) (setf name-list - (cons (funcall get-object-name object) name-list)))))) + (cons (funcall get-object-name o) name-list)))))) (defun choose-number-option (option-list) "The user chooses one out of a list of options, the index is returned" diff --git a/python/__pycache__/character.cpython-34.pyc b/python/__pycache__/character.cpython-34.pyc deleted file mode 100644 index 20a9d5a..0000000 --- a/python/__pycache__/character.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/client.cpython-34.pyc b/python/__pycache__/client.cpython-34.pyc deleted file mode 100644 index 077e656..0000000 --- a/python/__pycache__/client.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/define.cpython-34.pyc b/python/__pycache__/define.cpython-34.pyc deleted file mode 100644 index 397fe49..0000000 --- a/python/__pycache__/define.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/interpreter.cpython-32.pyc b/python/__pycache__/interpreter.cpython-32.pyc deleted file mode 100644 index 2d36d9f..0000000 --- a/python/__pycache__/interpreter.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/interpreter.cpython-34.pyc b/python/__pycache__/interpreter.cpython-34.pyc deleted file mode 100644 index 3d8f05b..0000000 --- a/python/__pycache__/interpreter.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/item.cpython-34.pyc b/python/__pycache__/item.cpython-34.pyc deleted file mode 100644 index bc4b363..0000000 --- a/python/__pycache__/item.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/parser.cpython-34.pyc b/python/__pycache__/parser.cpython-34.pyc deleted file mode 100644 index ffeaf68..0000000 --- a/python/__pycache__/parser.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/place.cpython-32.pyc b/python/__pycache__/place.cpython-32.pyc deleted file mode 100644 index 721a318..0000000 --- a/python/__pycache__/place.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/place.cpython-34.pyc b/python/__pycache__/place.cpython-34.pyc deleted file mode 100644 index bf21fc7..0000000 --- a/python/__pycache__/place.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/server.cpython-34.pyc b/python/__pycache__/server.cpython-34.pyc deleted file mode 100644 index 3ac6f1d..0000000 --- a/python/__pycache__/server.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/world.cpython-32.pyc b/python/__pycache__/world.cpython-32.pyc deleted file mode 100644 index 4276447..0000000 --- a/python/__pycache__/world.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/world.cpython-34.pyc b/python/__pycache__/world.cpython-34.pyc deleted file mode 100644 index 1814006..0000000 --- a/python/__pycache__/world.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/atlantis.py b/python/atlantis.py deleted file mode 100644 index 2c5802f..0000000 --- a/python/atlantis.py +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 02/05/2015 -# - -VERSION = (0, 0, 8) #release, major revision, minor (git) revision - -import sys -import os -from server import Server -from client import Client - - -global clear -if "linux" in sys.platform: - clear = "clear" -elif "win" in sys.platform: - clear = "cls" - - -def start_menu(): - ''' - Show the start menu and process the player's input - ''' - global clear - os.system(clear) - try: #Print the Atlantis logo - fn = os.path.join(os.path.dirname(__file__), "banner.txt") - img_file = open(fn, "r") - logo = img_file.read() - img_file.close() - print(logo) - except IOError: - print("Could not find logo!") - print("Welcome! What do you want to do?") - print(" -> (S)tart a server") - print(" -> (J)oin game") - print(" -> (A)bout") - print(" -> (E)xit") - print() - choice = input("Please choose an option: ") - act_on_choice(choice) - -#TODO! -def act_on_choice(choice): - if choice == "s" or choice == "S": - print("What port do you want to start the server on?") #TODO indicate range! - server_port = input(">> ") - print("What world file do you want to load?") - print("(Specify an absolute or relative path)") - world_file = input(">> ") - Server(server_port, world_file) - elif choice == "j" or choice == "J": - print("What server do you want to connect to?") - print("Format: :") - server_address = input(">> ") - server_ip = server_address[:server_address.find(":")] - server_port = server_address[server_address.find(":")+1:] - Client(server_ip, server_port) - elif choice == "a" or choice == "A": - print_version() - print("(c) 2015 Daniel Vedder") - input("\nPlease press ENTER") - start_menu() - elif choice == "e" or choice == "E": - print("Goodbye!") - exit() - else: - print("Invalid choice!") - input("Please press ENTER") - start_menu() - - -def print_version(): - version_string = str(VERSION[0])+"."+str(VERSION[1])+"."+str(VERSION[2]) - print("Atlantis "+version_string) - print("Licensed under the terms of the GNU General Public License v3.") - -def print_help(): - help_text = """ -Usage: atlantis [options] - - --help -h Print this help text and exit - --version -v Print the version number and exit - --server Start an atlantis server on (requires --world) - --world Use as the world file (requires --server) - --client : Connect as a client to the server at : - -If no arguments are passed, atlantis starts an interactive interface. -""" - print_version() - print(help_text) - -def main(): - if "--version" in sys.argv or "-v" in sys.argv: print_version() - elif "--help" in sys.argv or "-h" in sys.argv: print_help() - elif "--server" in sys.argv and "--world" in sys.argv: - server_port = sys.argv[sys.argv.index("--server")+1] - world_file = sys.argv[sys.argv.index("--world")+1] - Server(server_port, world_file) - elif "--client" in sys.argv: - server_address = sys.argv[sys.argv.index("--client")+1] - server_ip = server_address[:server_address.find(":")] - server_port = server_address[server_address.find(":")+1:] - Client(server_ip, server_port) - else: - start_menu() - -if __name__ == "__main__": - main() diff --git a/python/banner.txt b/python/banner.txt deleted file mode 100644 index 68d8ee8..0000000 --- a/python/banner.txt +++ /dev/null @@ -1,10 +0,0 @@ -==================================================== -|| \ / || -|| - O -MM _ MM ATLANTIS || -|| / \ ||_/.\_|| || -|| |*| |*| Lost worlds await || -|| ~~~~~| | A | |~~~~ || -|| ~~~~###########~~~ || -|| ~~~#############~~ (c) 2015 Daniel Vedder || -|| ~~~~~~~~~~~~~~~~~~ || -==================================================== diff --git a/python/character.py b/python/character.py deleted file mode 100644 index 4acbf34..0000000 --- a/python/character.py +++ /dev/null @@ -1,179 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# The character module is in charge of everything to do with the internal -# representation of a player character, including its race and class. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 02/05/2015 -# - -import item -from define import DefineCommand, register_define_command - - -class Race(object): - ''' - A class to describe the races inhabiting the game world - ''' - - def __init__(self, name, description="", - str_bonus=0, dex_bonus=0, - int_bonus=0, const_bonus=0): - ''' - Create a new race. Arguments: - name, description: the name and description of the new race - str_bonus, dex_bonus, int_bonus, const_bonus: attribute bonuses given - to all players of this race - ''' - self.name = name - self.description = description - self.bonuses = {'strength':str_bonus, 'dexterity':dex_bonus, - 'intelligence':int_bonus, 'constitution':const_bonus} - - def set_description(self, description): - self.description = description - - def set_strength(self, str_bonus): - self.bonuses['strength'] = str_bonus - - def set_dexterity(self, dex_bonus): - self.bonuses['dexterity'] = dex_bonus - - def set_constitution(self, const_bonus): - self.bonuses['constitution'] = const_bonus - - def set_intelligence(self, int_bonus): - self.bonuses['intelligence'] = int_bonus - - -class DefineRace(DefineCommand): - ''' - The ATL construct to define a race - ''' - - def __init__(self): - DefineCommand.__init__(self, "define-race", - "Define and describe a race of beings") - self.race = None - # FIXME: need new option methods to act as wrappers around self.race.x - # (self.race doesn't exist yet) - self.add_option("description", "A description of this race", - self.race.set_description) - self.add_option("strength", "The strength bonus that this race gets", - self.race.set_strength) - self.add_option("dexterity", "The dexterity bonus that this race gets", - self.race.set_dexterity) - self.add_option("constitution", "The constitution bonus that this race gets", - self.race.set_constitution) - self.add_option("intelligence", "The intelligence bonus that this race gets", - self.race.set_intelligence) - - def init_object(self, race_name): - self.race = None - self.race = Race(race_name) - - def return_object(self): - return self.race - -register_define_command(DefineRace()) - - -class CharacterClass(object): - ''' - A class to describe all the character classes a player can assume - ''' - - def __init__(self, name, description="", special_items=[]): - ''' - Create a new character class. Arguments: - name, description: the name and description of this character class - special_items: a list of item instances that all players of this - class carry from the start - ''' - self.name = name - self.description = description - self.items = special_items - - def set_description(self, description): - self.description = description - - def add_item(self, item_name): - new_item = item.get_item(item_name) - self.items.append(new_item) - - -class DefineClass(DefineCommand): - ''' - The ATL construct to define a character class - ''' - - def __init__(self): - DefineCommand.__init__(self, "define-class", - "Define and describe a character class") - self.char_class = None - # FIXME: need new option methods to act as wrappers around - # self.char_class.x (self.char_class doesn't exist yet) - self.add_option("description", "Describe this character class", - self.char_class.set_description) - self.add_option("item", "An item that all members of this class carry", - self.char_class.add_item) - - def init_object(self, class_name): - self.char_class = None - self.char_class = CharacterClass(name) - - def return_object(self): - return self.char_class - -register_define_command(DefineClass()) - - -class Player(object): - ''' - The Player class represents a game character. - ''' - - def __init__(self, name, start_health, - character_race, character_class, - start_attributes): - ''' - Create a player. Arguments: - name - The name of the character - start_health - The initial max HP - character_race - a race instance the character belongs to - character_class - a class instance that the character belongs to - start_attributes - A dict containing the player attributes - ('strength', 'constitution', 'dexterity', 'intelligence') - ''' - self.name = name - self.location = "" - self.health = start_health - self.attributes = start_attributes - self.weapons = [] - self.items = [] - - def change_location(self, new_place): - self.location = new_place - - def change_health(self, difference): - self.health = self.health+difference - - def increase_attribute(self, attribute, difference): - self.attributes[attribute] = self.attributes[attribute] + difference - - def add_item(self, item): - self.items.append(item) - - # Warning: might cause bugs! - def remove_item(self, item): - self.items.remove(item) - - def add_weapon(self, weapon): - self.items.append(weapon) - - # Warning: might cause bugs! - def remove_weapon(self, weapon): - self.items.remove(weapon) - diff --git a/lisp/game-objects.lisp b/lisp/game-objects.lisp index 080bc7e..585cc9b 100644 --- a/lisp/game-objects.lisp +++ b/lisp/game-objects.lisp @@ -35,6 +35,7 @@ (strength 0) (dexterity 0) (aggression 0) + (spawn-probability 0) (weapon "") (armour-class 0)) @@ -48,7 +49,7 @@ (defstruct weapon (name "") (description "") - (type "generic") + (type "") (damage 0)) @@ -69,21 +70,29 @@ ;; Same comment applies as above (let ((command (build-symbol (type-of game-object) "-" property))) (eval `(if (listp (,command ,game-object)) - ;; FIXME This gives problems with multiple values (setf (,command ,game-object) - (remove-if #'(lambda (x) (equalp x ,value)) + (remove-first-if #'(lambda (x) (equalp x ,value)) (,command ,game-object))) ;; TODO set numbers to 0, strings to "" (setf (,command ,game-object) NIL))))) -;; XXX This function is probably superfluous, as all place objects are stored -;; in world, with only their names recorded in the place +(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)) + (dolist (n name-list objects) + (setf objects (cons (get-game-object object-type 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 ((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." (funcall list-function object-type place))) - -(defun monster-strikes-player (monster player) - "The monster attacks a player" - ;;TODO - ) diff --git a/lisp/interpreter.lisp b/lisp/interpreter.lisp index 8f69337..03d4886 100644 --- a/lisp/interpreter.lisp +++ b/lisp/interpreter.lisp @@ -99,7 +99,7 @@ ;; interpret an option command ((or (eql (aref line 0) #\Space) (eql (aref line 0) #\Tab)) - (setf line (string-left-trim '(#\Space #\Tab) line)) + (setf line (trim-whitespace line)) (set-object-attribute current-object (read-from-string line) (read-from-string (second (cut-string line (position #\space line)))))) diff --git a/lisp/ui.lisp b/lisp/ui.lisp index 0fafe21..7d0c25d 100644 --- a/lisp/ui.lisp +++ b/lisp/ui.lisp @@ -25,6 +25,7 @@ (add-game-object player) (set-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player))) + (objectify-place-monsters (player-place player)) ;; The actual game loop (clear-screen) (let ((place (get-game-object 'place (player-place player)))) @@ -93,7 +94,8 @@ (format t "~&Players present: ~A" (string-from-list (place-player p))) (format t "~&Items: ~A" (string-from-list (place-item p))) (format t "~&NPCs: ~A" (string-from-list (place-npc p))) - (format t "~&Monsters: ~A" (string-from-list (place-monster p)))) + (format t "~&Monsters: ~A" (string-from-list + (list-place-objects 'monster p)))) (defun game-command (cmd player) "Execute a typed-in game command" @@ -194,6 +196,7 @@ (defun goto (player &optional location) "Go to the specified location" + ;; Look before you leap (unless location (format t "~&Please specify a location!") (return-from goto)) @@ -204,12 +207,14 @@ :test #'equalp)) (format t "~&This place does not border your current location!") (return-from goto)) + ;; Do all the necessary housekeeping (clear-screen) (debugging "~&~A is going to ~A." (player-name player) location) + (objectify-place-monsters location) (remove-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player)) (set-object-attribute player 'place location) - (set-object-attribute (get-game-object 'place (player-place player)) + (set-object-attribute (get-game-object 'place location) 'player (player-name player)) (describe-place location)) @@ -226,9 +231,9 @@ (let ((place (get-game-object 'place (player-place player))) (description NIL)) (macrolet ((set-descr (type) - (let ((place-descr (build-symbol type "-description")) - (place-object (build-symbol "place-" type))) - `(when (member object-name (,place-object place) + (let ((place-descr (build-symbol type "-description"))) + `(when (member object-name + (list-place-objects ',type place) :test #'equalp) (setf description (,place-descr (get-game-object ',type @@ -344,7 +349,8 @@ (format t "~&Please specify an opponent!") (return-from attack)) (unless (member opponent - (list-place-objects 'monster (player-place player)) + (list-place-objects 'monster + (get-game-object 'place (player-place player))) :test #'equalp) (format t "~&This monster is not here!") (return-from attack)) @@ -363,7 +369,6 @@ (if (> (+ (random 10) p-dex) (+ (random 10) m-dex)) (setf damage (calculate-damage p-str p-weapon m-dex m-ac)) (setf damage (- 0 (calculate-damage m-str m-weapon p-dex p-ac)))) - ;(break) (cond ((plusp damage) (decf (monster-health monster) damage) (format t "~&You hit! ~A points damage." damage) diff --git a/lisp/util.lisp b/lisp/util.lisp index 3ad8306..3ce44e3 100644 --- a/lisp/util.lisp +++ b/lisp/util.lisp @@ -98,13 +98,13 @@ ;;; FUNCTIONS -; Some of these functions are probably quite inefficient (lots of consing) +;; Some of these functions are probably quite inefficient (lots of consing) -;; XXX DEPRECATED Not actually needed anywhere -(defun call-function (function-name &rest args) - "Save myself some quoting when calling a function from a generated symbol" - ;; Perhaps not very clean, but it works - (eval `(,function-name ,@args))) +(defun remove-first-if (fn lst) + "Remove the first element in a list that satisfies the given predicate" + (cond ((null lst) NIL) + ((funcall fn (car lst)) (cdr lst)) + (T (cons (car lst) (remove-first-if fn (cdr lst)))))) (defun average (&rest numbers) "Compute the average of the given numbers" @@ -205,17 +205,19 @@ (read-from-string (string-from-list components ""))) (defun make-list-function (container-type &optional (add-s t)) - "Return function to return a list of the names of all objects of the + "Return a function to return a list of the names of all objects of the specified type in the container struct" #'(lambda (object-type container) - (let ((get-objects (symbol-function (build-symbol container-type "-" - object-type (if add-s "s" "")))) - (get-object-name (symbol-function - (build-symbol object-type "-name"))) - (name-list NIL)) - (dolist (object (funcall get-objects container) name-list) + (let* ((get-objects (symbol-function + (build-symbol container-type "-" + object-type (if add-s "s" "")))) + (get-object-name (symbol-function + (build-symbol object-type "-name"))) + (objects (funcall get-objects container)) (name-list NIL)) + (dolist (o objects name-list) + (when (stringp o) (return objects)) (setf name-list - (cons (funcall get-object-name object) name-list)))))) + (cons (funcall get-object-name o) name-list)))))) (defun choose-number-option (option-list) "The user chooses one out of a list of options, the index is returned" diff --git a/python/__pycache__/character.cpython-34.pyc b/python/__pycache__/character.cpython-34.pyc deleted file mode 100644 index 20a9d5a..0000000 --- a/python/__pycache__/character.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/client.cpython-34.pyc b/python/__pycache__/client.cpython-34.pyc deleted file mode 100644 index 077e656..0000000 --- a/python/__pycache__/client.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/define.cpython-34.pyc b/python/__pycache__/define.cpython-34.pyc deleted file mode 100644 index 397fe49..0000000 --- a/python/__pycache__/define.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/interpreter.cpython-32.pyc b/python/__pycache__/interpreter.cpython-32.pyc deleted file mode 100644 index 2d36d9f..0000000 --- a/python/__pycache__/interpreter.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/interpreter.cpython-34.pyc b/python/__pycache__/interpreter.cpython-34.pyc deleted file mode 100644 index 3d8f05b..0000000 --- a/python/__pycache__/interpreter.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/item.cpython-34.pyc b/python/__pycache__/item.cpython-34.pyc deleted file mode 100644 index bc4b363..0000000 --- a/python/__pycache__/item.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/parser.cpython-34.pyc b/python/__pycache__/parser.cpython-34.pyc deleted file mode 100644 index ffeaf68..0000000 --- a/python/__pycache__/parser.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/place.cpython-32.pyc b/python/__pycache__/place.cpython-32.pyc deleted file mode 100644 index 721a318..0000000 --- a/python/__pycache__/place.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/place.cpython-34.pyc b/python/__pycache__/place.cpython-34.pyc deleted file mode 100644 index bf21fc7..0000000 --- a/python/__pycache__/place.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/server.cpython-34.pyc b/python/__pycache__/server.cpython-34.pyc deleted file mode 100644 index 3ac6f1d..0000000 --- a/python/__pycache__/server.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/world.cpython-32.pyc b/python/__pycache__/world.cpython-32.pyc deleted file mode 100644 index 4276447..0000000 --- a/python/__pycache__/world.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/world.cpython-34.pyc b/python/__pycache__/world.cpython-34.pyc deleted file mode 100644 index 1814006..0000000 --- a/python/__pycache__/world.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/atlantis.py b/python/atlantis.py deleted file mode 100644 index 2c5802f..0000000 --- a/python/atlantis.py +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 02/05/2015 -# - -VERSION = (0, 0, 8) #release, major revision, minor (git) revision - -import sys -import os -from server import Server -from client import Client - - -global clear -if "linux" in sys.platform: - clear = "clear" -elif "win" in sys.platform: - clear = "cls" - - -def start_menu(): - ''' - Show the start menu and process the player's input - ''' - global clear - os.system(clear) - try: #Print the Atlantis logo - fn = os.path.join(os.path.dirname(__file__), "banner.txt") - img_file = open(fn, "r") - logo = img_file.read() - img_file.close() - print(logo) - except IOError: - print("Could not find logo!") - print("Welcome! What do you want to do?") - print(" -> (S)tart a server") - print(" -> (J)oin game") - print(" -> (A)bout") - print(" -> (E)xit") - print() - choice = input("Please choose an option: ") - act_on_choice(choice) - -#TODO! -def act_on_choice(choice): - if choice == "s" or choice == "S": - print("What port do you want to start the server on?") #TODO indicate range! - server_port = input(">> ") - print("What world file do you want to load?") - print("(Specify an absolute or relative path)") - world_file = input(">> ") - Server(server_port, world_file) - elif choice == "j" or choice == "J": - print("What server do you want to connect to?") - print("Format: :") - server_address = input(">> ") - server_ip = server_address[:server_address.find(":")] - server_port = server_address[server_address.find(":")+1:] - Client(server_ip, server_port) - elif choice == "a" or choice == "A": - print_version() - print("(c) 2015 Daniel Vedder") - input("\nPlease press ENTER") - start_menu() - elif choice == "e" or choice == "E": - print("Goodbye!") - exit() - else: - print("Invalid choice!") - input("Please press ENTER") - start_menu() - - -def print_version(): - version_string = str(VERSION[0])+"."+str(VERSION[1])+"."+str(VERSION[2]) - print("Atlantis "+version_string) - print("Licensed under the terms of the GNU General Public License v3.") - -def print_help(): - help_text = """ -Usage: atlantis [options] - - --help -h Print this help text and exit - --version -v Print the version number and exit - --server Start an atlantis server on (requires --world) - --world Use as the world file (requires --server) - --client : Connect as a client to the server at : - -If no arguments are passed, atlantis starts an interactive interface. -""" - print_version() - print(help_text) - -def main(): - if "--version" in sys.argv or "-v" in sys.argv: print_version() - elif "--help" in sys.argv or "-h" in sys.argv: print_help() - elif "--server" in sys.argv and "--world" in sys.argv: - server_port = sys.argv[sys.argv.index("--server")+1] - world_file = sys.argv[sys.argv.index("--world")+1] - Server(server_port, world_file) - elif "--client" in sys.argv: - server_address = sys.argv[sys.argv.index("--client")+1] - server_ip = server_address[:server_address.find(":")] - server_port = server_address[server_address.find(":")+1:] - Client(server_ip, server_port) - else: - start_menu() - -if __name__ == "__main__": - main() diff --git a/python/banner.txt b/python/banner.txt deleted file mode 100644 index 68d8ee8..0000000 --- a/python/banner.txt +++ /dev/null @@ -1,10 +0,0 @@ -==================================================== -|| \ / || -|| - O -MM _ MM ATLANTIS || -|| / \ ||_/.\_|| || -|| |*| |*| Lost worlds await || -|| ~~~~~| | A | |~~~~ || -|| ~~~~###########~~~ || -|| ~~~#############~~ (c) 2015 Daniel Vedder || -|| ~~~~~~~~~~~~~~~~~~ || -==================================================== diff --git a/python/character.py b/python/character.py deleted file mode 100644 index 4acbf34..0000000 --- a/python/character.py +++ /dev/null @@ -1,179 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# The character module is in charge of everything to do with the internal -# representation of a player character, including its race and class. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 02/05/2015 -# - -import item -from define import DefineCommand, register_define_command - - -class Race(object): - ''' - A class to describe the races inhabiting the game world - ''' - - def __init__(self, name, description="", - str_bonus=0, dex_bonus=0, - int_bonus=0, const_bonus=0): - ''' - Create a new race. Arguments: - name, description: the name and description of the new race - str_bonus, dex_bonus, int_bonus, const_bonus: attribute bonuses given - to all players of this race - ''' - self.name = name - self.description = description - self.bonuses = {'strength':str_bonus, 'dexterity':dex_bonus, - 'intelligence':int_bonus, 'constitution':const_bonus} - - def set_description(self, description): - self.description = description - - def set_strength(self, str_bonus): - self.bonuses['strength'] = str_bonus - - def set_dexterity(self, dex_bonus): - self.bonuses['dexterity'] = dex_bonus - - def set_constitution(self, const_bonus): - self.bonuses['constitution'] = const_bonus - - def set_intelligence(self, int_bonus): - self.bonuses['intelligence'] = int_bonus - - -class DefineRace(DefineCommand): - ''' - The ATL construct to define a race - ''' - - def __init__(self): - DefineCommand.__init__(self, "define-race", - "Define and describe a race of beings") - self.race = None - # FIXME: need new option methods to act as wrappers around self.race.x - # (self.race doesn't exist yet) - self.add_option("description", "A description of this race", - self.race.set_description) - self.add_option("strength", "The strength bonus that this race gets", - self.race.set_strength) - self.add_option("dexterity", "The dexterity bonus that this race gets", - self.race.set_dexterity) - self.add_option("constitution", "The constitution bonus that this race gets", - self.race.set_constitution) - self.add_option("intelligence", "The intelligence bonus that this race gets", - self.race.set_intelligence) - - def init_object(self, race_name): - self.race = None - self.race = Race(race_name) - - def return_object(self): - return self.race - -register_define_command(DefineRace()) - - -class CharacterClass(object): - ''' - A class to describe all the character classes a player can assume - ''' - - def __init__(self, name, description="", special_items=[]): - ''' - Create a new character class. Arguments: - name, description: the name and description of this character class - special_items: a list of item instances that all players of this - class carry from the start - ''' - self.name = name - self.description = description - self.items = special_items - - def set_description(self, description): - self.description = description - - def add_item(self, item_name): - new_item = item.get_item(item_name) - self.items.append(new_item) - - -class DefineClass(DefineCommand): - ''' - The ATL construct to define a character class - ''' - - def __init__(self): - DefineCommand.__init__(self, "define-class", - "Define and describe a character class") - self.char_class = None - # FIXME: need new option methods to act as wrappers around - # self.char_class.x (self.char_class doesn't exist yet) - self.add_option("description", "Describe this character class", - self.char_class.set_description) - self.add_option("item", "An item that all members of this class carry", - self.char_class.add_item) - - def init_object(self, class_name): - self.char_class = None - self.char_class = CharacterClass(name) - - def return_object(self): - return self.char_class - -register_define_command(DefineClass()) - - -class Player(object): - ''' - The Player class represents a game character. - ''' - - def __init__(self, name, start_health, - character_race, character_class, - start_attributes): - ''' - Create a player. Arguments: - name - The name of the character - start_health - The initial max HP - character_race - a race instance the character belongs to - character_class - a class instance that the character belongs to - start_attributes - A dict containing the player attributes - ('strength', 'constitution', 'dexterity', 'intelligence') - ''' - self.name = name - self.location = "" - self.health = start_health - self.attributes = start_attributes - self.weapons = [] - self.items = [] - - def change_location(self, new_place): - self.location = new_place - - def change_health(self, difference): - self.health = self.health+difference - - def increase_attribute(self, attribute, difference): - self.attributes[attribute] = self.attributes[attribute] + difference - - def add_item(self, item): - self.items.append(item) - - # Warning: might cause bugs! - def remove_item(self, item): - self.items.remove(item) - - def add_weapon(self, weapon): - self.items.append(weapon) - - # Warning: might cause bugs! - def remove_weapon(self, weapon): - self.items.remove(weapon) - diff --git a/python/client.py b/python/client.py deleted file mode 100644 index 93a18e3..0000000 --- a/python/client.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# This is the client module that the player interacts with. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 02/05/2015 -# - -class Client(object): - ''' - The client is the interface between the player and the server. - ''' - - def __init__(self, server_ip=None, server_port=None): - if not (server_ip and server_port): - print("What server IP do you want to connect to?") - server_ip = input(">> ") - print("What port are you using?") - server_port = input(">> ") - print("The client would connect to "+server_ip+":"+server_port) diff --git a/lisp/game-objects.lisp b/lisp/game-objects.lisp index 080bc7e..585cc9b 100644 --- a/lisp/game-objects.lisp +++ b/lisp/game-objects.lisp @@ -35,6 +35,7 @@ (strength 0) (dexterity 0) (aggression 0) + (spawn-probability 0) (weapon "") (armour-class 0)) @@ -48,7 +49,7 @@ (defstruct weapon (name "") (description "") - (type "generic") + (type "") (damage 0)) @@ -69,21 +70,29 @@ ;; Same comment applies as above (let ((command (build-symbol (type-of game-object) "-" property))) (eval `(if (listp (,command ,game-object)) - ;; FIXME This gives problems with multiple values (setf (,command ,game-object) - (remove-if #'(lambda (x) (equalp x ,value)) + (remove-first-if #'(lambda (x) (equalp x ,value)) (,command ,game-object))) ;; TODO set numbers to 0, strings to "" (setf (,command ,game-object) NIL))))) -;; XXX This function is probably superfluous, as all place objects are stored -;; in world, with only their names recorded in the place +(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)) + (dolist (n name-list objects) + (setf objects (cons (get-game-object object-type 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 ((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." (funcall list-function object-type place))) - -(defun monster-strikes-player (monster player) - "The monster attacks a player" - ;;TODO - ) diff --git a/lisp/interpreter.lisp b/lisp/interpreter.lisp index 8f69337..03d4886 100644 --- a/lisp/interpreter.lisp +++ b/lisp/interpreter.lisp @@ -99,7 +99,7 @@ ;; interpret an option command ((or (eql (aref line 0) #\Space) (eql (aref line 0) #\Tab)) - (setf line (string-left-trim '(#\Space #\Tab) line)) + (setf line (trim-whitespace line)) (set-object-attribute current-object (read-from-string line) (read-from-string (second (cut-string line (position #\space line)))))) diff --git a/lisp/ui.lisp b/lisp/ui.lisp index 0fafe21..7d0c25d 100644 --- a/lisp/ui.lisp +++ b/lisp/ui.lisp @@ -25,6 +25,7 @@ (add-game-object player) (set-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player))) + (objectify-place-monsters (player-place player)) ;; The actual game loop (clear-screen) (let ((place (get-game-object 'place (player-place player)))) @@ -93,7 +94,8 @@ (format t "~&Players present: ~A" (string-from-list (place-player p))) (format t "~&Items: ~A" (string-from-list (place-item p))) (format t "~&NPCs: ~A" (string-from-list (place-npc p))) - (format t "~&Monsters: ~A" (string-from-list (place-monster p)))) + (format t "~&Monsters: ~A" (string-from-list + (list-place-objects 'monster p)))) (defun game-command (cmd player) "Execute a typed-in game command" @@ -194,6 +196,7 @@ (defun goto (player &optional location) "Go to the specified location" + ;; Look before you leap (unless location (format t "~&Please specify a location!") (return-from goto)) @@ -204,12 +207,14 @@ :test #'equalp)) (format t "~&This place does not border your current location!") (return-from goto)) + ;; Do all the necessary housekeeping (clear-screen) (debugging "~&~A is going to ~A." (player-name player) location) + (objectify-place-monsters location) (remove-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player)) (set-object-attribute player 'place location) - (set-object-attribute (get-game-object 'place (player-place player)) + (set-object-attribute (get-game-object 'place location) 'player (player-name player)) (describe-place location)) @@ -226,9 +231,9 @@ (let ((place (get-game-object 'place (player-place player))) (description NIL)) (macrolet ((set-descr (type) - (let ((place-descr (build-symbol type "-description")) - (place-object (build-symbol "place-" type))) - `(when (member object-name (,place-object place) + (let ((place-descr (build-symbol type "-description"))) + `(when (member object-name + (list-place-objects ',type place) :test #'equalp) (setf description (,place-descr (get-game-object ',type @@ -344,7 +349,8 @@ (format t "~&Please specify an opponent!") (return-from attack)) (unless (member opponent - (list-place-objects 'monster (player-place player)) + (list-place-objects 'monster + (get-game-object 'place (player-place player))) :test #'equalp) (format t "~&This monster is not here!") (return-from attack)) @@ -363,7 +369,6 @@ (if (> (+ (random 10) p-dex) (+ (random 10) m-dex)) (setf damage (calculate-damage p-str p-weapon m-dex m-ac)) (setf damage (- 0 (calculate-damage m-str m-weapon p-dex p-ac)))) - ;(break) (cond ((plusp damage) (decf (monster-health monster) damage) (format t "~&You hit! ~A points damage." damage) diff --git a/lisp/util.lisp b/lisp/util.lisp index 3ad8306..3ce44e3 100644 --- a/lisp/util.lisp +++ b/lisp/util.lisp @@ -98,13 +98,13 @@ ;;; FUNCTIONS -; Some of these functions are probably quite inefficient (lots of consing) +;; Some of these functions are probably quite inefficient (lots of consing) -;; XXX DEPRECATED Not actually needed anywhere -(defun call-function (function-name &rest args) - "Save myself some quoting when calling a function from a generated symbol" - ;; Perhaps not very clean, but it works - (eval `(,function-name ,@args))) +(defun remove-first-if (fn lst) + "Remove the first element in a list that satisfies the given predicate" + (cond ((null lst) NIL) + ((funcall fn (car lst)) (cdr lst)) + (T (cons (car lst) (remove-first-if fn (cdr lst)))))) (defun average (&rest numbers) "Compute the average of the given numbers" @@ -205,17 +205,19 @@ (read-from-string (string-from-list components ""))) (defun make-list-function (container-type &optional (add-s t)) - "Return function to return a list of the names of all objects of the + "Return a function to return a list of the names of all objects of the specified type in the container struct" #'(lambda (object-type container) - (let ((get-objects (symbol-function (build-symbol container-type "-" - object-type (if add-s "s" "")))) - (get-object-name (symbol-function - (build-symbol object-type "-name"))) - (name-list NIL)) - (dolist (object (funcall get-objects container) name-list) + (let* ((get-objects (symbol-function + (build-symbol container-type "-" + object-type (if add-s "s" "")))) + (get-object-name (symbol-function + (build-symbol object-type "-name"))) + (objects (funcall get-objects container)) (name-list NIL)) + (dolist (o objects name-list) + (when (stringp o) (return objects)) (setf name-list - (cons (funcall get-object-name object) name-list)))))) + (cons (funcall get-object-name o) name-list)))))) (defun choose-number-option (option-list) "The user chooses one out of a list of options, the index is returned" diff --git a/python/__pycache__/character.cpython-34.pyc b/python/__pycache__/character.cpython-34.pyc deleted file mode 100644 index 20a9d5a..0000000 --- a/python/__pycache__/character.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/client.cpython-34.pyc b/python/__pycache__/client.cpython-34.pyc deleted file mode 100644 index 077e656..0000000 --- a/python/__pycache__/client.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/define.cpython-34.pyc b/python/__pycache__/define.cpython-34.pyc deleted file mode 100644 index 397fe49..0000000 --- a/python/__pycache__/define.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/interpreter.cpython-32.pyc b/python/__pycache__/interpreter.cpython-32.pyc deleted file mode 100644 index 2d36d9f..0000000 --- a/python/__pycache__/interpreter.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/interpreter.cpython-34.pyc b/python/__pycache__/interpreter.cpython-34.pyc deleted file mode 100644 index 3d8f05b..0000000 --- a/python/__pycache__/interpreter.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/item.cpython-34.pyc b/python/__pycache__/item.cpython-34.pyc deleted file mode 100644 index bc4b363..0000000 --- a/python/__pycache__/item.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/parser.cpython-34.pyc b/python/__pycache__/parser.cpython-34.pyc deleted file mode 100644 index ffeaf68..0000000 --- a/python/__pycache__/parser.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/place.cpython-32.pyc b/python/__pycache__/place.cpython-32.pyc deleted file mode 100644 index 721a318..0000000 --- a/python/__pycache__/place.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/place.cpython-34.pyc b/python/__pycache__/place.cpython-34.pyc deleted file mode 100644 index bf21fc7..0000000 --- a/python/__pycache__/place.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/server.cpython-34.pyc b/python/__pycache__/server.cpython-34.pyc deleted file mode 100644 index 3ac6f1d..0000000 --- a/python/__pycache__/server.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/world.cpython-32.pyc b/python/__pycache__/world.cpython-32.pyc deleted file mode 100644 index 4276447..0000000 --- a/python/__pycache__/world.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/world.cpython-34.pyc b/python/__pycache__/world.cpython-34.pyc deleted file mode 100644 index 1814006..0000000 --- a/python/__pycache__/world.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/atlantis.py b/python/atlantis.py deleted file mode 100644 index 2c5802f..0000000 --- a/python/atlantis.py +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 02/05/2015 -# - -VERSION = (0, 0, 8) #release, major revision, minor (git) revision - -import sys -import os -from server import Server -from client import Client - - -global clear -if "linux" in sys.platform: - clear = "clear" -elif "win" in sys.platform: - clear = "cls" - - -def start_menu(): - ''' - Show the start menu and process the player's input - ''' - global clear - os.system(clear) - try: #Print the Atlantis logo - fn = os.path.join(os.path.dirname(__file__), "banner.txt") - img_file = open(fn, "r") - logo = img_file.read() - img_file.close() - print(logo) - except IOError: - print("Could not find logo!") - print("Welcome! What do you want to do?") - print(" -> (S)tart a server") - print(" -> (J)oin game") - print(" -> (A)bout") - print(" -> (E)xit") - print() - choice = input("Please choose an option: ") - act_on_choice(choice) - -#TODO! -def act_on_choice(choice): - if choice == "s" or choice == "S": - print("What port do you want to start the server on?") #TODO indicate range! - server_port = input(">> ") - print("What world file do you want to load?") - print("(Specify an absolute or relative path)") - world_file = input(">> ") - Server(server_port, world_file) - elif choice == "j" or choice == "J": - print("What server do you want to connect to?") - print("Format: :") - server_address = input(">> ") - server_ip = server_address[:server_address.find(":")] - server_port = server_address[server_address.find(":")+1:] - Client(server_ip, server_port) - elif choice == "a" or choice == "A": - print_version() - print("(c) 2015 Daniel Vedder") - input("\nPlease press ENTER") - start_menu() - elif choice == "e" or choice == "E": - print("Goodbye!") - exit() - else: - print("Invalid choice!") - input("Please press ENTER") - start_menu() - - -def print_version(): - version_string = str(VERSION[0])+"."+str(VERSION[1])+"."+str(VERSION[2]) - print("Atlantis "+version_string) - print("Licensed under the terms of the GNU General Public License v3.") - -def print_help(): - help_text = """ -Usage: atlantis [options] - - --help -h Print this help text and exit - --version -v Print the version number and exit - --server Start an atlantis server on (requires --world) - --world Use as the world file (requires --server) - --client : Connect as a client to the server at : - -If no arguments are passed, atlantis starts an interactive interface. -""" - print_version() - print(help_text) - -def main(): - if "--version" in sys.argv or "-v" in sys.argv: print_version() - elif "--help" in sys.argv or "-h" in sys.argv: print_help() - elif "--server" in sys.argv and "--world" in sys.argv: - server_port = sys.argv[sys.argv.index("--server")+1] - world_file = sys.argv[sys.argv.index("--world")+1] - Server(server_port, world_file) - elif "--client" in sys.argv: - server_address = sys.argv[sys.argv.index("--client")+1] - server_ip = server_address[:server_address.find(":")] - server_port = server_address[server_address.find(":")+1:] - Client(server_ip, server_port) - else: - start_menu() - -if __name__ == "__main__": - main() diff --git a/python/banner.txt b/python/banner.txt deleted file mode 100644 index 68d8ee8..0000000 --- a/python/banner.txt +++ /dev/null @@ -1,10 +0,0 @@ -==================================================== -|| \ / || -|| - O -MM _ MM ATLANTIS || -|| / \ ||_/.\_|| || -|| |*| |*| Lost worlds await || -|| ~~~~~| | A | |~~~~ || -|| ~~~~###########~~~ || -|| ~~~#############~~ (c) 2015 Daniel Vedder || -|| ~~~~~~~~~~~~~~~~~~ || -==================================================== diff --git a/python/character.py b/python/character.py deleted file mode 100644 index 4acbf34..0000000 --- a/python/character.py +++ /dev/null @@ -1,179 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# The character module is in charge of everything to do with the internal -# representation of a player character, including its race and class. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 02/05/2015 -# - -import item -from define import DefineCommand, register_define_command - - -class Race(object): - ''' - A class to describe the races inhabiting the game world - ''' - - def __init__(self, name, description="", - str_bonus=0, dex_bonus=0, - int_bonus=0, const_bonus=0): - ''' - Create a new race. Arguments: - name, description: the name and description of the new race - str_bonus, dex_bonus, int_bonus, const_bonus: attribute bonuses given - to all players of this race - ''' - self.name = name - self.description = description - self.bonuses = {'strength':str_bonus, 'dexterity':dex_bonus, - 'intelligence':int_bonus, 'constitution':const_bonus} - - def set_description(self, description): - self.description = description - - def set_strength(self, str_bonus): - self.bonuses['strength'] = str_bonus - - def set_dexterity(self, dex_bonus): - self.bonuses['dexterity'] = dex_bonus - - def set_constitution(self, const_bonus): - self.bonuses['constitution'] = const_bonus - - def set_intelligence(self, int_bonus): - self.bonuses['intelligence'] = int_bonus - - -class DefineRace(DefineCommand): - ''' - The ATL construct to define a race - ''' - - def __init__(self): - DefineCommand.__init__(self, "define-race", - "Define and describe a race of beings") - self.race = None - # FIXME: need new option methods to act as wrappers around self.race.x - # (self.race doesn't exist yet) - self.add_option("description", "A description of this race", - self.race.set_description) - self.add_option("strength", "The strength bonus that this race gets", - self.race.set_strength) - self.add_option("dexterity", "The dexterity bonus that this race gets", - self.race.set_dexterity) - self.add_option("constitution", "The constitution bonus that this race gets", - self.race.set_constitution) - self.add_option("intelligence", "The intelligence bonus that this race gets", - self.race.set_intelligence) - - def init_object(self, race_name): - self.race = None - self.race = Race(race_name) - - def return_object(self): - return self.race - -register_define_command(DefineRace()) - - -class CharacterClass(object): - ''' - A class to describe all the character classes a player can assume - ''' - - def __init__(self, name, description="", special_items=[]): - ''' - Create a new character class. Arguments: - name, description: the name and description of this character class - special_items: a list of item instances that all players of this - class carry from the start - ''' - self.name = name - self.description = description - self.items = special_items - - def set_description(self, description): - self.description = description - - def add_item(self, item_name): - new_item = item.get_item(item_name) - self.items.append(new_item) - - -class DefineClass(DefineCommand): - ''' - The ATL construct to define a character class - ''' - - def __init__(self): - DefineCommand.__init__(self, "define-class", - "Define and describe a character class") - self.char_class = None - # FIXME: need new option methods to act as wrappers around - # self.char_class.x (self.char_class doesn't exist yet) - self.add_option("description", "Describe this character class", - self.char_class.set_description) - self.add_option("item", "An item that all members of this class carry", - self.char_class.add_item) - - def init_object(self, class_name): - self.char_class = None - self.char_class = CharacterClass(name) - - def return_object(self): - return self.char_class - -register_define_command(DefineClass()) - - -class Player(object): - ''' - The Player class represents a game character. - ''' - - def __init__(self, name, start_health, - character_race, character_class, - start_attributes): - ''' - Create a player. Arguments: - name - The name of the character - start_health - The initial max HP - character_race - a race instance the character belongs to - character_class - a class instance that the character belongs to - start_attributes - A dict containing the player attributes - ('strength', 'constitution', 'dexterity', 'intelligence') - ''' - self.name = name - self.location = "" - self.health = start_health - self.attributes = start_attributes - self.weapons = [] - self.items = [] - - def change_location(self, new_place): - self.location = new_place - - def change_health(self, difference): - self.health = self.health+difference - - def increase_attribute(self, attribute, difference): - self.attributes[attribute] = self.attributes[attribute] + difference - - def add_item(self, item): - self.items.append(item) - - # Warning: might cause bugs! - def remove_item(self, item): - self.items.remove(item) - - def add_weapon(self, weapon): - self.items.append(weapon) - - # Warning: might cause bugs! - def remove_weapon(self, weapon): - self.items.remove(weapon) - diff --git a/python/client.py b/python/client.py deleted file mode 100644 index 93a18e3..0000000 --- a/python/client.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# This is the client module that the player interacts with. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 02/05/2015 -# - -class Client(object): - ''' - The client is the interface between the player and the server. - ''' - - def __init__(self, server_ip=None, server_port=None): - if not (server_ip and server_port): - print("What server IP do you want to connect to?") - server_ip = input(">> ") - print("What port are you using?") - server_port = input(">> ") - print("The client would connect to "+server_ip+":"+server_port) diff --git a/python/define.py b/python/define.py deleted file mode 100644 index 8c22e73..0000000 --- a/python/define.py +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# This module houses the code for the define blocks of the Atlantis -# ATL language. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 04/05/2015 -# - - -class DefineCommand(object): - ''' - This is the super class for all define commands. It should be extended - for each individual command, like define-place, define-monster, etc. - - Each define command works by creating the relevant object, then fleshing - it out as more options are passed to it. Finally, the finished object is - returned. - ''' - - def __init__(self, name, doc_string): - self.name = name - self.doc_string = doc_string - self.option_registry = dict() - - def init_object(self, object_name): - ''' - Initialize the object this command creates - ''' - raise NotImplementedError - - def return_object(self): - ''' - Return the type of the game object ('place', 'npc', 'item' or - 'monster') and the object itself - ''' - raise NotImplementedError - - def pass_option(self, option_name, option_argument): - ''' - Pass this define command one of its options and the relevant argument - ''' - self.option_registry[option_name]["function"](option_argument) - - def add_option(self, option_name, option_docstring, option_function): - ''' - Add an option for this define command. Arguments: - option_name: The name of this option as it would appear in - an atl source file - option_docstring: A description of this option - option_function: The function that this option calls - (this function should take exactly one string argument) - ''' - option_dict = dict(docstring=option_docstring, function=option_function) - self.option_registry[option_name] = option_dict - - -define_command_registry = dict() - -def register_define_command(def_com): - define_command_registry[def_com.name] = def_com - -def get_define_command(command_name): - return define_command_registry[command_name] diff --git a/lisp/game-objects.lisp b/lisp/game-objects.lisp index 080bc7e..585cc9b 100644 --- a/lisp/game-objects.lisp +++ b/lisp/game-objects.lisp @@ -35,6 +35,7 @@ (strength 0) (dexterity 0) (aggression 0) + (spawn-probability 0) (weapon "") (armour-class 0)) @@ -48,7 +49,7 @@ (defstruct weapon (name "") (description "") - (type "generic") + (type "") (damage 0)) @@ -69,21 +70,29 @@ ;; Same comment applies as above (let ((command (build-symbol (type-of game-object) "-" property))) (eval `(if (listp (,command ,game-object)) - ;; FIXME This gives problems with multiple values (setf (,command ,game-object) - (remove-if #'(lambda (x) (equalp x ,value)) + (remove-first-if #'(lambda (x) (equalp x ,value)) (,command ,game-object))) ;; TODO set numbers to 0, strings to "" (setf (,command ,game-object) NIL))))) -;; XXX This function is probably superfluous, as all place objects are stored -;; in world, with only their names recorded in the place +(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)) + (dolist (n name-list objects) + (setf objects (cons (get-game-object object-type 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 ((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." (funcall list-function object-type place))) - -(defun monster-strikes-player (monster player) - "The monster attacks a player" - ;;TODO - ) diff --git a/lisp/interpreter.lisp b/lisp/interpreter.lisp index 8f69337..03d4886 100644 --- a/lisp/interpreter.lisp +++ b/lisp/interpreter.lisp @@ -99,7 +99,7 @@ ;; interpret an option command ((or (eql (aref line 0) #\Space) (eql (aref line 0) #\Tab)) - (setf line (string-left-trim '(#\Space #\Tab) line)) + (setf line (trim-whitespace line)) (set-object-attribute current-object (read-from-string line) (read-from-string (second (cut-string line (position #\space line)))))) diff --git a/lisp/ui.lisp b/lisp/ui.lisp index 0fafe21..7d0c25d 100644 --- a/lisp/ui.lisp +++ b/lisp/ui.lisp @@ -25,6 +25,7 @@ (add-game-object player) (set-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player))) + (objectify-place-monsters (player-place player)) ;; The actual game loop (clear-screen) (let ((place (get-game-object 'place (player-place player)))) @@ -93,7 +94,8 @@ (format t "~&Players present: ~A" (string-from-list (place-player p))) (format t "~&Items: ~A" (string-from-list (place-item p))) (format t "~&NPCs: ~A" (string-from-list (place-npc p))) - (format t "~&Monsters: ~A" (string-from-list (place-monster p)))) + (format t "~&Monsters: ~A" (string-from-list + (list-place-objects 'monster p)))) (defun game-command (cmd player) "Execute a typed-in game command" @@ -194,6 +196,7 @@ (defun goto (player &optional location) "Go to the specified location" + ;; Look before you leap (unless location (format t "~&Please specify a location!") (return-from goto)) @@ -204,12 +207,14 @@ :test #'equalp)) (format t "~&This place does not border your current location!") (return-from goto)) + ;; Do all the necessary housekeeping (clear-screen) (debugging "~&~A is going to ~A." (player-name player) location) + (objectify-place-monsters location) (remove-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player)) (set-object-attribute player 'place location) - (set-object-attribute (get-game-object 'place (player-place player)) + (set-object-attribute (get-game-object 'place location) 'player (player-name player)) (describe-place location)) @@ -226,9 +231,9 @@ (let ((place (get-game-object 'place (player-place player))) (description NIL)) (macrolet ((set-descr (type) - (let ((place-descr (build-symbol type "-description")) - (place-object (build-symbol "place-" type))) - `(when (member object-name (,place-object place) + (let ((place-descr (build-symbol type "-description"))) + `(when (member object-name + (list-place-objects ',type place) :test #'equalp) (setf description (,place-descr (get-game-object ',type @@ -344,7 +349,8 @@ (format t "~&Please specify an opponent!") (return-from attack)) (unless (member opponent - (list-place-objects 'monster (player-place player)) + (list-place-objects 'monster + (get-game-object 'place (player-place player))) :test #'equalp) (format t "~&This monster is not here!") (return-from attack)) @@ -363,7 +369,6 @@ (if (> (+ (random 10) p-dex) (+ (random 10) m-dex)) (setf damage (calculate-damage p-str p-weapon m-dex m-ac)) (setf damage (- 0 (calculate-damage m-str m-weapon p-dex p-ac)))) - ;(break) (cond ((plusp damage) (decf (monster-health monster) damage) (format t "~&You hit! ~A points damage." damage) diff --git a/lisp/util.lisp b/lisp/util.lisp index 3ad8306..3ce44e3 100644 --- a/lisp/util.lisp +++ b/lisp/util.lisp @@ -98,13 +98,13 @@ ;;; FUNCTIONS -; Some of these functions are probably quite inefficient (lots of consing) +;; Some of these functions are probably quite inefficient (lots of consing) -;; XXX DEPRECATED Not actually needed anywhere -(defun call-function (function-name &rest args) - "Save myself some quoting when calling a function from a generated symbol" - ;; Perhaps not very clean, but it works - (eval `(,function-name ,@args))) +(defun remove-first-if (fn lst) + "Remove the first element in a list that satisfies the given predicate" + (cond ((null lst) NIL) + ((funcall fn (car lst)) (cdr lst)) + (T (cons (car lst) (remove-first-if fn (cdr lst)))))) (defun average (&rest numbers) "Compute the average of the given numbers" @@ -205,17 +205,19 @@ (read-from-string (string-from-list components ""))) (defun make-list-function (container-type &optional (add-s t)) - "Return function to return a list of the names of all objects of the + "Return a function to return a list of the names of all objects of the specified type in the container struct" #'(lambda (object-type container) - (let ((get-objects (symbol-function (build-symbol container-type "-" - object-type (if add-s "s" "")))) - (get-object-name (symbol-function - (build-symbol object-type "-name"))) - (name-list NIL)) - (dolist (object (funcall get-objects container) name-list) + (let* ((get-objects (symbol-function + (build-symbol container-type "-" + object-type (if add-s "s" "")))) + (get-object-name (symbol-function + (build-symbol object-type "-name"))) + (objects (funcall get-objects container)) (name-list NIL)) + (dolist (o objects name-list) + (when (stringp o) (return objects)) (setf name-list - (cons (funcall get-object-name object) name-list)))))) + (cons (funcall get-object-name o) name-list)))))) (defun choose-number-option (option-list) "The user chooses one out of a list of options, the index is returned" diff --git a/python/__pycache__/character.cpython-34.pyc b/python/__pycache__/character.cpython-34.pyc deleted file mode 100644 index 20a9d5a..0000000 --- a/python/__pycache__/character.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/client.cpython-34.pyc b/python/__pycache__/client.cpython-34.pyc deleted file mode 100644 index 077e656..0000000 --- a/python/__pycache__/client.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/define.cpython-34.pyc b/python/__pycache__/define.cpython-34.pyc deleted file mode 100644 index 397fe49..0000000 --- a/python/__pycache__/define.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/interpreter.cpython-32.pyc b/python/__pycache__/interpreter.cpython-32.pyc deleted file mode 100644 index 2d36d9f..0000000 --- a/python/__pycache__/interpreter.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/interpreter.cpython-34.pyc b/python/__pycache__/interpreter.cpython-34.pyc deleted file mode 100644 index 3d8f05b..0000000 --- a/python/__pycache__/interpreter.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/item.cpython-34.pyc b/python/__pycache__/item.cpython-34.pyc deleted file mode 100644 index bc4b363..0000000 --- a/python/__pycache__/item.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/parser.cpython-34.pyc b/python/__pycache__/parser.cpython-34.pyc deleted file mode 100644 index ffeaf68..0000000 --- a/python/__pycache__/parser.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/place.cpython-32.pyc b/python/__pycache__/place.cpython-32.pyc deleted file mode 100644 index 721a318..0000000 --- a/python/__pycache__/place.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/place.cpython-34.pyc b/python/__pycache__/place.cpython-34.pyc deleted file mode 100644 index bf21fc7..0000000 --- a/python/__pycache__/place.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/server.cpython-34.pyc b/python/__pycache__/server.cpython-34.pyc deleted file mode 100644 index 3ac6f1d..0000000 --- a/python/__pycache__/server.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/world.cpython-32.pyc b/python/__pycache__/world.cpython-32.pyc deleted file mode 100644 index 4276447..0000000 --- a/python/__pycache__/world.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/world.cpython-34.pyc b/python/__pycache__/world.cpython-34.pyc deleted file mode 100644 index 1814006..0000000 --- a/python/__pycache__/world.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/atlantis.py b/python/atlantis.py deleted file mode 100644 index 2c5802f..0000000 --- a/python/atlantis.py +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 02/05/2015 -# - -VERSION = (0, 0, 8) #release, major revision, minor (git) revision - -import sys -import os -from server import Server -from client import Client - - -global clear -if "linux" in sys.platform: - clear = "clear" -elif "win" in sys.platform: - clear = "cls" - - -def start_menu(): - ''' - Show the start menu and process the player's input - ''' - global clear - os.system(clear) - try: #Print the Atlantis logo - fn = os.path.join(os.path.dirname(__file__), "banner.txt") - img_file = open(fn, "r") - logo = img_file.read() - img_file.close() - print(logo) - except IOError: - print("Could not find logo!") - print("Welcome! What do you want to do?") - print(" -> (S)tart a server") - print(" -> (J)oin game") - print(" -> (A)bout") - print(" -> (E)xit") - print() - choice = input("Please choose an option: ") - act_on_choice(choice) - -#TODO! -def act_on_choice(choice): - if choice == "s" or choice == "S": - print("What port do you want to start the server on?") #TODO indicate range! - server_port = input(">> ") - print("What world file do you want to load?") - print("(Specify an absolute or relative path)") - world_file = input(">> ") - Server(server_port, world_file) - elif choice == "j" or choice == "J": - print("What server do you want to connect to?") - print("Format: :") - server_address = input(">> ") - server_ip = server_address[:server_address.find(":")] - server_port = server_address[server_address.find(":")+1:] - Client(server_ip, server_port) - elif choice == "a" or choice == "A": - print_version() - print("(c) 2015 Daniel Vedder") - input("\nPlease press ENTER") - start_menu() - elif choice == "e" or choice == "E": - print("Goodbye!") - exit() - else: - print("Invalid choice!") - input("Please press ENTER") - start_menu() - - -def print_version(): - version_string = str(VERSION[0])+"."+str(VERSION[1])+"."+str(VERSION[2]) - print("Atlantis "+version_string) - print("Licensed under the terms of the GNU General Public License v3.") - -def print_help(): - help_text = """ -Usage: atlantis [options] - - --help -h Print this help text and exit - --version -v Print the version number and exit - --server Start an atlantis server on (requires --world) - --world Use as the world file (requires --server) - --client : Connect as a client to the server at : - -If no arguments are passed, atlantis starts an interactive interface. -""" - print_version() - print(help_text) - -def main(): - if "--version" in sys.argv or "-v" in sys.argv: print_version() - elif "--help" in sys.argv or "-h" in sys.argv: print_help() - elif "--server" in sys.argv and "--world" in sys.argv: - server_port = sys.argv[sys.argv.index("--server")+1] - world_file = sys.argv[sys.argv.index("--world")+1] - Server(server_port, world_file) - elif "--client" in sys.argv: - server_address = sys.argv[sys.argv.index("--client")+1] - server_ip = server_address[:server_address.find(":")] - server_port = server_address[server_address.find(":")+1:] - Client(server_ip, server_port) - else: - start_menu() - -if __name__ == "__main__": - main() diff --git a/python/banner.txt b/python/banner.txt deleted file mode 100644 index 68d8ee8..0000000 --- a/python/banner.txt +++ /dev/null @@ -1,10 +0,0 @@ -==================================================== -|| \ / || -|| - O -MM _ MM ATLANTIS || -|| / \ ||_/.\_|| || -|| |*| |*| Lost worlds await || -|| ~~~~~| | A | |~~~~ || -|| ~~~~###########~~~ || -|| ~~~#############~~ (c) 2015 Daniel Vedder || -|| ~~~~~~~~~~~~~~~~~~ || -==================================================== diff --git a/python/character.py b/python/character.py deleted file mode 100644 index 4acbf34..0000000 --- a/python/character.py +++ /dev/null @@ -1,179 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# The character module is in charge of everything to do with the internal -# representation of a player character, including its race and class. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 02/05/2015 -# - -import item -from define import DefineCommand, register_define_command - - -class Race(object): - ''' - A class to describe the races inhabiting the game world - ''' - - def __init__(self, name, description="", - str_bonus=0, dex_bonus=0, - int_bonus=0, const_bonus=0): - ''' - Create a new race. Arguments: - name, description: the name and description of the new race - str_bonus, dex_bonus, int_bonus, const_bonus: attribute bonuses given - to all players of this race - ''' - self.name = name - self.description = description - self.bonuses = {'strength':str_bonus, 'dexterity':dex_bonus, - 'intelligence':int_bonus, 'constitution':const_bonus} - - def set_description(self, description): - self.description = description - - def set_strength(self, str_bonus): - self.bonuses['strength'] = str_bonus - - def set_dexterity(self, dex_bonus): - self.bonuses['dexterity'] = dex_bonus - - def set_constitution(self, const_bonus): - self.bonuses['constitution'] = const_bonus - - def set_intelligence(self, int_bonus): - self.bonuses['intelligence'] = int_bonus - - -class DefineRace(DefineCommand): - ''' - The ATL construct to define a race - ''' - - def __init__(self): - DefineCommand.__init__(self, "define-race", - "Define and describe a race of beings") - self.race = None - # FIXME: need new option methods to act as wrappers around self.race.x - # (self.race doesn't exist yet) - self.add_option("description", "A description of this race", - self.race.set_description) - self.add_option("strength", "The strength bonus that this race gets", - self.race.set_strength) - self.add_option("dexterity", "The dexterity bonus that this race gets", - self.race.set_dexterity) - self.add_option("constitution", "The constitution bonus that this race gets", - self.race.set_constitution) - self.add_option("intelligence", "The intelligence bonus that this race gets", - self.race.set_intelligence) - - def init_object(self, race_name): - self.race = None - self.race = Race(race_name) - - def return_object(self): - return self.race - -register_define_command(DefineRace()) - - -class CharacterClass(object): - ''' - A class to describe all the character classes a player can assume - ''' - - def __init__(self, name, description="", special_items=[]): - ''' - Create a new character class. Arguments: - name, description: the name and description of this character class - special_items: a list of item instances that all players of this - class carry from the start - ''' - self.name = name - self.description = description - self.items = special_items - - def set_description(self, description): - self.description = description - - def add_item(self, item_name): - new_item = item.get_item(item_name) - self.items.append(new_item) - - -class DefineClass(DefineCommand): - ''' - The ATL construct to define a character class - ''' - - def __init__(self): - DefineCommand.__init__(self, "define-class", - "Define and describe a character class") - self.char_class = None - # FIXME: need new option methods to act as wrappers around - # self.char_class.x (self.char_class doesn't exist yet) - self.add_option("description", "Describe this character class", - self.char_class.set_description) - self.add_option("item", "An item that all members of this class carry", - self.char_class.add_item) - - def init_object(self, class_name): - self.char_class = None - self.char_class = CharacterClass(name) - - def return_object(self): - return self.char_class - -register_define_command(DefineClass()) - - -class Player(object): - ''' - The Player class represents a game character. - ''' - - def __init__(self, name, start_health, - character_race, character_class, - start_attributes): - ''' - Create a player. Arguments: - name - The name of the character - start_health - The initial max HP - character_race - a race instance the character belongs to - character_class - a class instance that the character belongs to - start_attributes - A dict containing the player attributes - ('strength', 'constitution', 'dexterity', 'intelligence') - ''' - self.name = name - self.location = "" - self.health = start_health - self.attributes = start_attributes - self.weapons = [] - self.items = [] - - def change_location(self, new_place): - self.location = new_place - - def change_health(self, difference): - self.health = self.health+difference - - def increase_attribute(self, attribute, difference): - self.attributes[attribute] = self.attributes[attribute] + difference - - def add_item(self, item): - self.items.append(item) - - # Warning: might cause bugs! - def remove_item(self, item): - self.items.remove(item) - - def add_weapon(self, weapon): - self.items.append(weapon) - - # Warning: might cause bugs! - def remove_weapon(self, weapon): - self.items.remove(weapon) - diff --git a/python/client.py b/python/client.py deleted file mode 100644 index 93a18e3..0000000 --- a/python/client.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# This is the client module that the player interacts with. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 02/05/2015 -# - -class Client(object): - ''' - The client is the interface between the player and the server. - ''' - - def __init__(self, server_ip=None, server_port=None): - if not (server_ip and server_port): - print("What server IP do you want to connect to?") - server_ip = input(">> ") - print("What port are you using?") - server_port = input(">> ") - print("The client would connect to "+server_ip+":"+server_port) diff --git a/python/define.py b/python/define.py deleted file mode 100644 index 8c22e73..0000000 --- a/python/define.py +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# This module houses the code for the define blocks of the Atlantis -# ATL language. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 04/05/2015 -# - - -class DefineCommand(object): - ''' - This is the super class for all define commands. It should be extended - for each individual command, like define-place, define-monster, etc. - - Each define command works by creating the relevant object, then fleshing - it out as more options are passed to it. Finally, the finished object is - returned. - ''' - - def __init__(self, name, doc_string): - self.name = name - self.doc_string = doc_string - self.option_registry = dict() - - def init_object(self, object_name): - ''' - Initialize the object this command creates - ''' - raise NotImplementedError - - def return_object(self): - ''' - Return the type of the game object ('place', 'npc', 'item' or - 'monster') and the object itself - ''' - raise NotImplementedError - - def pass_option(self, option_name, option_argument): - ''' - Pass this define command one of its options and the relevant argument - ''' - self.option_registry[option_name]["function"](option_argument) - - def add_option(self, option_name, option_docstring, option_function): - ''' - Add an option for this define command. Arguments: - option_name: The name of this option as it would appear in - an atl source file - option_docstring: A description of this option - option_function: The function that this option calls - (this function should take exactly one string argument) - ''' - option_dict = dict(docstring=option_docstring, function=option_function) - self.option_registry[option_name] = option_dict - - -define_command_registry = dict() - -def register_define_command(def_com): - define_command_registry[def_com.name] = def_com - -def get_define_command(command_name): - return define_command_registry[command_name] diff --git a/python/interpreter.py b/python/interpreter.py deleted file mode 100644 index 69d7fc1..0000000 --- a/python/interpreter.py +++ /dev/null @@ -1,112 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# This module reads in a world file and parses it, using the DefineCommands -# defined in other modules. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 02/05/2015 -# - -import os -from world import World -from define import DefineCommand, define_command_registry - - -class Parser(object): - ''' - The actual parser class. It reads in a world file and transforms it into - a series of DefineCommands, which are then executed - ''' - - def __init__(self, world_file_name): - self.line_command_registry = dict() - self.world = World() - self.add_line_commands() - self.world_file_name = world_file_name - - def load(self, world_file_name): - ''' - Load a world file and pass it on to the interpreter - ''' - try: - world_file = open(self.world_file_name, 'r') - atl_text = world_file.readlines() - world_file.close() - print("Loaded "+world_file_name) - except IOError: - print("Failed to load world file '"+world_file_name+"'!") - self.interpret_atl(atl_text) - return self.world - - def add_line_commands(self): - ''' - Line commands are ATL constructs that only take up one line - (unlike define commands). All line commands must be registered in - this method by adding them to the line_command_registry and setting - their key-value to the method to be called when their key appears - in an ATL source file. - ''' - #self.line_command_registry["load"] = self.secondary_load - self.line_command_registry["start-place"] = self.world.set_starting_place - - def interpret_atl(self, atl_source): - ''' - This method interprets a list of ATL source lines, passing them - on to the relevant commands. - ''' - define_command = None # The define command currently being executed - line_no = 0 - for line in atl_source: - # TODO allow for linebreaks - # while line[-2] == "\\": - # line = line + atl_source[line_no+1] - # line_no = line_no+1 - line_no = line_no + 1 - if line[-1] != "\n": - # make sure each line ends with a line break, otherwise we run - # into trouble with the last line - line = line+"\n" - if len(line) < 2 and define_command: - # Empty lines finish off define blocks - object_type, game_object = define_command.return_object() - self.world.add_object(object_type, game_object) - define_command = None - elif len(line) < 2 or line[0] == "#": - pass #comments and empty lines are ignored - else: - command = line.split()[0] - # execute a line command - if command in self.line_command_registry.keys(): - self.line_command_registry[command](line[line.find(" ")+1:-1]) - # start of a define block - elif command in define_command_registry.keys(): - define_command = define_command_registry[command] - object_name = line[line.find(" ")+1:-1] - define_command.init_object(object_name) - # parse an option command - elif line[0] == " " or line[0] == "\t": - while line[0] == " " or line[0] == "\t": - line = line[1:] - if line and define_command: - option_command = line.split()[0] - option_arg = line[line.find(" ")+1:-1] - define_command.pass_option(option_command, option_arg) - else: - # XXX: What should be done here? Do nothing, raise a - # syntax error, or something else? - print("Unrecognized syntax in line "+str(line_no)+"!") - - def secondary_load(self, atl_file_name): - ''' - --- DO NOT USE --- - - The function that prepares everything before loading in another ATL file - when the 'load' command is called. - - Currently leads to an endless recursion loop. - ''' - world_directory = os.path.split(self.world_file_name)[0] - load_file_name = os.path.join(world_directory, atl_file_name) - self.load(load_file_name) diff --git a/lisp/game-objects.lisp b/lisp/game-objects.lisp index 080bc7e..585cc9b 100644 --- a/lisp/game-objects.lisp +++ b/lisp/game-objects.lisp @@ -35,6 +35,7 @@ (strength 0) (dexterity 0) (aggression 0) + (spawn-probability 0) (weapon "") (armour-class 0)) @@ -48,7 +49,7 @@ (defstruct weapon (name "") (description "") - (type "generic") + (type "") (damage 0)) @@ -69,21 +70,29 @@ ;; Same comment applies as above (let ((command (build-symbol (type-of game-object) "-" property))) (eval `(if (listp (,command ,game-object)) - ;; FIXME This gives problems with multiple values (setf (,command ,game-object) - (remove-if #'(lambda (x) (equalp x ,value)) + (remove-first-if #'(lambda (x) (equalp x ,value)) (,command ,game-object))) ;; TODO set numbers to 0, strings to "" (setf (,command ,game-object) NIL))))) -;; XXX This function is probably superfluous, as all place objects are stored -;; in world, with only their names recorded in the place +(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)) + (dolist (n name-list objects) + (setf objects (cons (get-game-object object-type 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 ((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." (funcall list-function object-type place))) - -(defun monster-strikes-player (monster player) - "The monster attacks a player" - ;;TODO - ) diff --git a/lisp/interpreter.lisp b/lisp/interpreter.lisp index 8f69337..03d4886 100644 --- a/lisp/interpreter.lisp +++ b/lisp/interpreter.lisp @@ -99,7 +99,7 @@ ;; interpret an option command ((or (eql (aref line 0) #\Space) (eql (aref line 0) #\Tab)) - (setf line (string-left-trim '(#\Space #\Tab) line)) + (setf line (trim-whitespace line)) (set-object-attribute current-object (read-from-string line) (read-from-string (second (cut-string line (position #\space line)))))) diff --git a/lisp/ui.lisp b/lisp/ui.lisp index 0fafe21..7d0c25d 100644 --- a/lisp/ui.lisp +++ b/lisp/ui.lisp @@ -25,6 +25,7 @@ (add-game-object player) (set-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player))) + (objectify-place-monsters (player-place player)) ;; The actual game loop (clear-screen) (let ((place (get-game-object 'place (player-place player)))) @@ -93,7 +94,8 @@ (format t "~&Players present: ~A" (string-from-list (place-player p))) (format t "~&Items: ~A" (string-from-list (place-item p))) (format t "~&NPCs: ~A" (string-from-list (place-npc p))) - (format t "~&Monsters: ~A" (string-from-list (place-monster p)))) + (format t "~&Monsters: ~A" (string-from-list + (list-place-objects 'monster p)))) (defun game-command (cmd player) "Execute a typed-in game command" @@ -194,6 +196,7 @@ (defun goto (player &optional location) "Go to the specified location" + ;; Look before you leap (unless location (format t "~&Please specify a location!") (return-from goto)) @@ -204,12 +207,14 @@ :test #'equalp)) (format t "~&This place does not border your current location!") (return-from goto)) + ;; Do all the necessary housekeeping (clear-screen) (debugging "~&~A is going to ~A." (player-name player) location) + (objectify-place-monsters location) (remove-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player)) (set-object-attribute player 'place location) - (set-object-attribute (get-game-object 'place (player-place player)) + (set-object-attribute (get-game-object 'place location) 'player (player-name player)) (describe-place location)) @@ -226,9 +231,9 @@ (let ((place (get-game-object 'place (player-place player))) (description NIL)) (macrolet ((set-descr (type) - (let ((place-descr (build-symbol type "-description")) - (place-object (build-symbol "place-" type))) - `(when (member object-name (,place-object place) + (let ((place-descr (build-symbol type "-description"))) + `(when (member object-name + (list-place-objects ',type place) :test #'equalp) (setf description (,place-descr (get-game-object ',type @@ -344,7 +349,8 @@ (format t "~&Please specify an opponent!") (return-from attack)) (unless (member opponent - (list-place-objects 'monster (player-place player)) + (list-place-objects 'monster + (get-game-object 'place (player-place player))) :test #'equalp) (format t "~&This monster is not here!") (return-from attack)) @@ -363,7 +369,6 @@ (if (> (+ (random 10) p-dex) (+ (random 10) m-dex)) (setf damage (calculate-damage p-str p-weapon m-dex m-ac)) (setf damage (- 0 (calculate-damage m-str m-weapon p-dex p-ac)))) - ;(break) (cond ((plusp damage) (decf (monster-health monster) damage) (format t "~&You hit! ~A points damage." damage) diff --git a/lisp/util.lisp b/lisp/util.lisp index 3ad8306..3ce44e3 100644 --- a/lisp/util.lisp +++ b/lisp/util.lisp @@ -98,13 +98,13 @@ ;;; FUNCTIONS -; Some of these functions are probably quite inefficient (lots of consing) +;; Some of these functions are probably quite inefficient (lots of consing) -;; XXX DEPRECATED Not actually needed anywhere -(defun call-function (function-name &rest args) - "Save myself some quoting when calling a function from a generated symbol" - ;; Perhaps not very clean, but it works - (eval `(,function-name ,@args))) +(defun remove-first-if (fn lst) + "Remove the first element in a list that satisfies the given predicate" + (cond ((null lst) NIL) + ((funcall fn (car lst)) (cdr lst)) + (T (cons (car lst) (remove-first-if fn (cdr lst)))))) (defun average (&rest numbers) "Compute the average of the given numbers" @@ -205,17 +205,19 @@ (read-from-string (string-from-list components ""))) (defun make-list-function (container-type &optional (add-s t)) - "Return function to return a list of the names of all objects of the + "Return a function to return a list of the names of all objects of the specified type in the container struct" #'(lambda (object-type container) - (let ((get-objects (symbol-function (build-symbol container-type "-" - object-type (if add-s "s" "")))) - (get-object-name (symbol-function - (build-symbol object-type "-name"))) - (name-list NIL)) - (dolist (object (funcall get-objects container) name-list) + (let* ((get-objects (symbol-function + (build-symbol container-type "-" + object-type (if add-s "s" "")))) + (get-object-name (symbol-function + (build-symbol object-type "-name"))) + (objects (funcall get-objects container)) (name-list NIL)) + (dolist (o objects name-list) + (when (stringp o) (return objects)) (setf name-list - (cons (funcall get-object-name object) name-list)))))) + (cons (funcall get-object-name o) name-list)))))) (defun choose-number-option (option-list) "The user chooses one out of a list of options, the index is returned" diff --git a/python/__pycache__/character.cpython-34.pyc b/python/__pycache__/character.cpython-34.pyc deleted file mode 100644 index 20a9d5a..0000000 --- a/python/__pycache__/character.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/client.cpython-34.pyc b/python/__pycache__/client.cpython-34.pyc deleted file mode 100644 index 077e656..0000000 --- a/python/__pycache__/client.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/define.cpython-34.pyc b/python/__pycache__/define.cpython-34.pyc deleted file mode 100644 index 397fe49..0000000 --- a/python/__pycache__/define.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/interpreter.cpython-32.pyc b/python/__pycache__/interpreter.cpython-32.pyc deleted file mode 100644 index 2d36d9f..0000000 --- a/python/__pycache__/interpreter.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/interpreter.cpython-34.pyc b/python/__pycache__/interpreter.cpython-34.pyc deleted file mode 100644 index 3d8f05b..0000000 --- a/python/__pycache__/interpreter.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/item.cpython-34.pyc b/python/__pycache__/item.cpython-34.pyc deleted file mode 100644 index bc4b363..0000000 --- a/python/__pycache__/item.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/parser.cpython-34.pyc b/python/__pycache__/parser.cpython-34.pyc deleted file mode 100644 index ffeaf68..0000000 --- a/python/__pycache__/parser.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/place.cpython-32.pyc b/python/__pycache__/place.cpython-32.pyc deleted file mode 100644 index 721a318..0000000 --- a/python/__pycache__/place.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/place.cpython-34.pyc b/python/__pycache__/place.cpython-34.pyc deleted file mode 100644 index bf21fc7..0000000 --- a/python/__pycache__/place.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/server.cpython-34.pyc b/python/__pycache__/server.cpython-34.pyc deleted file mode 100644 index 3ac6f1d..0000000 --- a/python/__pycache__/server.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/world.cpython-32.pyc b/python/__pycache__/world.cpython-32.pyc deleted file mode 100644 index 4276447..0000000 --- a/python/__pycache__/world.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/world.cpython-34.pyc b/python/__pycache__/world.cpython-34.pyc deleted file mode 100644 index 1814006..0000000 --- a/python/__pycache__/world.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/atlantis.py b/python/atlantis.py deleted file mode 100644 index 2c5802f..0000000 --- a/python/atlantis.py +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 02/05/2015 -# - -VERSION = (0, 0, 8) #release, major revision, minor (git) revision - -import sys -import os -from server import Server -from client import Client - - -global clear -if "linux" in sys.platform: - clear = "clear" -elif "win" in sys.platform: - clear = "cls" - - -def start_menu(): - ''' - Show the start menu and process the player's input - ''' - global clear - os.system(clear) - try: #Print the Atlantis logo - fn = os.path.join(os.path.dirname(__file__), "banner.txt") - img_file = open(fn, "r") - logo = img_file.read() - img_file.close() - print(logo) - except IOError: - print("Could not find logo!") - print("Welcome! What do you want to do?") - print(" -> (S)tart a server") - print(" -> (J)oin game") - print(" -> (A)bout") - print(" -> (E)xit") - print() - choice = input("Please choose an option: ") - act_on_choice(choice) - -#TODO! -def act_on_choice(choice): - if choice == "s" or choice == "S": - print("What port do you want to start the server on?") #TODO indicate range! - server_port = input(">> ") - print("What world file do you want to load?") - print("(Specify an absolute or relative path)") - world_file = input(">> ") - Server(server_port, world_file) - elif choice == "j" or choice == "J": - print("What server do you want to connect to?") - print("Format: :") - server_address = input(">> ") - server_ip = server_address[:server_address.find(":")] - server_port = server_address[server_address.find(":")+1:] - Client(server_ip, server_port) - elif choice == "a" or choice == "A": - print_version() - print("(c) 2015 Daniel Vedder") - input("\nPlease press ENTER") - start_menu() - elif choice == "e" or choice == "E": - print("Goodbye!") - exit() - else: - print("Invalid choice!") - input("Please press ENTER") - start_menu() - - -def print_version(): - version_string = str(VERSION[0])+"."+str(VERSION[1])+"."+str(VERSION[2]) - print("Atlantis "+version_string) - print("Licensed under the terms of the GNU General Public License v3.") - -def print_help(): - help_text = """ -Usage: atlantis [options] - - --help -h Print this help text and exit - --version -v Print the version number and exit - --server Start an atlantis server on (requires --world) - --world Use as the world file (requires --server) - --client : Connect as a client to the server at : - -If no arguments are passed, atlantis starts an interactive interface. -""" - print_version() - print(help_text) - -def main(): - if "--version" in sys.argv or "-v" in sys.argv: print_version() - elif "--help" in sys.argv or "-h" in sys.argv: print_help() - elif "--server" in sys.argv and "--world" in sys.argv: - server_port = sys.argv[sys.argv.index("--server")+1] - world_file = sys.argv[sys.argv.index("--world")+1] - Server(server_port, world_file) - elif "--client" in sys.argv: - server_address = sys.argv[sys.argv.index("--client")+1] - server_ip = server_address[:server_address.find(":")] - server_port = server_address[server_address.find(":")+1:] - Client(server_ip, server_port) - else: - start_menu() - -if __name__ == "__main__": - main() diff --git a/python/banner.txt b/python/banner.txt deleted file mode 100644 index 68d8ee8..0000000 --- a/python/banner.txt +++ /dev/null @@ -1,10 +0,0 @@ -==================================================== -|| \ / || -|| - O -MM _ MM ATLANTIS || -|| / \ ||_/.\_|| || -|| |*| |*| Lost worlds await || -|| ~~~~~| | A | |~~~~ || -|| ~~~~###########~~~ || -|| ~~~#############~~ (c) 2015 Daniel Vedder || -|| ~~~~~~~~~~~~~~~~~~ || -==================================================== diff --git a/python/character.py b/python/character.py deleted file mode 100644 index 4acbf34..0000000 --- a/python/character.py +++ /dev/null @@ -1,179 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# The character module is in charge of everything to do with the internal -# representation of a player character, including its race and class. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 02/05/2015 -# - -import item -from define import DefineCommand, register_define_command - - -class Race(object): - ''' - A class to describe the races inhabiting the game world - ''' - - def __init__(self, name, description="", - str_bonus=0, dex_bonus=0, - int_bonus=0, const_bonus=0): - ''' - Create a new race. Arguments: - name, description: the name and description of the new race - str_bonus, dex_bonus, int_bonus, const_bonus: attribute bonuses given - to all players of this race - ''' - self.name = name - self.description = description - self.bonuses = {'strength':str_bonus, 'dexterity':dex_bonus, - 'intelligence':int_bonus, 'constitution':const_bonus} - - def set_description(self, description): - self.description = description - - def set_strength(self, str_bonus): - self.bonuses['strength'] = str_bonus - - def set_dexterity(self, dex_bonus): - self.bonuses['dexterity'] = dex_bonus - - def set_constitution(self, const_bonus): - self.bonuses['constitution'] = const_bonus - - def set_intelligence(self, int_bonus): - self.bonuses['intelligence'] = int_bonus - - -class DefineRace(DefineCommand): - ''' - The ATL construct to define a race - ''' - - def __init__(self): - DefineCommand.__init__(self, "define-race", - "Define and describe a race of beings") - self.race = None - # FIXME: need new option methods to act as wrappers around self.race.x - # (self.race doesn't exist yet) - self.add_option("description", "A description of this race", - self.race.set_description) - self.add_option("strength", "The strength bonus that this race gets", - self.race.set_strength) - self.add_option("dexterity", "The dexterity bonus that this race gets", - self.race.set_dexterity) - self.add_option("constitution", "The constitution bonus that this race gets", - self.race.set_constitution) - self.add_option("intelligence", "The intelligence bonus that this race gets", - self.race.set_intelligence) - - def init_object(self, race_name): - self.race = None - self.race = Race(race_name) - - def return_object(self): - return self.race - -register_define_command(DefineRace()) - - -class CharacterClass(object): - ''' - A class to describe all the character classes a player can assume - ''' - - def __init__(self, name, description="", special_items=[]): - ''' - Create a new character class. Arguments: - name, description: the name and description of this character class - special_items: a list of item instances that all players of this - class carry from the start - ''' - self.name = name - self.description = description - self.items = special_items - - def set_description(self, description): - self.description = description - - def add_item(self, item_name): - new_item = item.get_item(item_name) - self.items.append(new_item) - - -class DefineClass(DefineCommand): - ''' - The ATL construct to define a character class - ''' - - def __init__(self): - DefineCommand.__init__(self, "define-class", - "Define and describe a character class") - self.char_class = None - # FIXME: need new option methods to act as wrappers around - # self.char_class.x (self.char_class doesn't exist yet) - self.add_option("description", "Describe this character class", - self.char_class.set_description) - self.add_option("item", "An item that all members of this class carry", - self.char_class.add_item) - - def init_object(self, class_name): - self.char_class = None - self.char_class = CharacterClass(name) - - def return_object(self): - return self.char_class - -register_define_command(DefineClass()) - - -class Player(object): - ''' - The Player class represents a game character. - ''' - - def __init__(self, name, start_health, - character_race, character_class, - start_attributes): - ''' - Create a player. Arguments: - name - The name of the character - start_health - The initial max HP - character_race - a race instance the character belongs to - character_class - a class instance that the character belongs to - start_attributes - A dict containing the player attributes - ('strength', 'constitution', 'dexterity', 'intelligence') - ''' - self.name = name - self.location = "" - self.health = start_health - self.attributes = start_attributes - self.weapons = [] - self.items = [] - - def change_location(self, new_place): - self.location = new_place - - def change_health(self, difference): - self.health = self.health+difference - - def increase_attribute(self, attribute, difference): - self.attributes[attribute] = self.attributes[attribute] + difference - - def add_item(self, item): - self.items.append(item) - - # Warning: might cause bugs! - def remove_item(self, item): - self.items.remove(item) - - def add_weapon(self, weapon): - self.items.append(weapon) - - # Warning: might cause bugs! - def remove_weapon(self, weapon): - self.items.remove(weapon) - diff --git a/python/client.py b/python/client.py deleted file mode 100644 index 93a18e3..0000000 --- a/python/client.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# This is the client module that the player interacts with. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 02/05/2015 -# - -class Client(object): - ''' - The client is the interface between the player and the server. - ''' - - def __init__(self, server_ip=None, server_port=None): - if not (server_ip and server_port): - print("What server IP do you want to connect to?") - server_ip = input(">> ") - print("What port are you using?") - server_port = input(">> ") - print("The client would connect to "+server_ip+":"+server_port) diff --git a/python/define.py b/python/define.py deleted file mode 100644 index 8c22e73..0000000 --- a/python/define.py +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# This module houses the code for the define blocks of the Atlantis -# ATL language. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 04/05/2015 -# - - -class DefineCommand(object): - ''' - This is the super class for all define commands. It should be extended - for each individual command, like define-place, define-monster, etc. - - Each define command works by creating the relevant object, then fleshing - it out as more options are passed to it. Finally, the finished object is - returned. - ''' - - def __init__(self, name, doc_string): - self.name = name - self.doc_string = doc_string - self.option_registry = dict() - - def init_object(self, object_name): - ''' - Initialize the object this command creates - ''' - raise NotImplementedError - - def return_object(self): - ''' - Return the type of the game object ('place', 'npc', 'item' or - 'monster') and the object itself - ''' - raise NotImplementedError - - def pass_option(self, option_name, option_argument): - ''' - Pass this define command one of its options and the relevant argument - ''' - self.option_registry[option_name]["function"](option_argument) - - def add_option(self, option_name, option_docstring, option_function): - ''' - Add an option for this define command. Arguments: - option_name: The name of this option as it would appear in - an atl source file - option_docstring: A description of this option - option_function: The function that this option calls - (this function should take exactly one string argument) - ''' - option_dict = dict(docstring=option_docstring, function=option_function) - self.option_registry[option_name] = option_dict - - -define_command_registry = dict() - -def register_define_command(def_com): - define_command_registry[def_com.name] = def_com - -def get_define_command(command_name): - return define_command_registry[command_name] diff --git a/python/interpreter.py b/python/interpreter.py deleted file mode 100644 index 69d7fc1..0000000 --- a/python/interpreter.py +++ /dev/null @@ -1,112 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# This module reads in a world file and parses it, using the DefineCommands -# defined in other modules. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 02/05/2015 -# - -import os -from world import World -from define import DefineCommand, define_command_registry - - -class Parser(object): - ''' - The actual parser class. It reads in a world file and transforms it into - a series of DefineCommands, which are then executed - ''' - - def __init__(self, world_file_name): - self.line_command_registry = dict() - self.world = World() - self.add_line_commands() - self.world_file_name = world_file_name - - def load(self, world_file_name): - ''' - Load a world file and pass it on to the interpreter - ''' - try: - world_file = open(self.world_file_name, 'r') - atl_text = world_file.readlines() - world_file.close() - print("Loaded "+world_file_name) - except IOError: - print("Failed to load world file '"+world_file_name+"'!") - self.interpret_atl(atl_text) - return self.world - - def add_line_commands(self): - ''' - Line commands are ATL constructs that only take up one line - (unlike define commands). All line commands must be registered in - this method by adding them to the line_command_registry and setting - their key-value to the method to be called when their key appears - in an ATL source file. - ''' - #self.line_command_registry["load"] = self.secondary_load - self.line_command_registry["start-place"] = self.world.set_starting_place - - def interpret_atl(self, atl_source): - ''' - This method interprets a list of ATL source lines, passing them - on to the relevant commands. - ''' - define_command = None # The define command currently being executed - line_no = 0 - for line in atl_source: - # TODO allow for linebreaks - # while line[-2] == "\\": - # line = line + atl_source[line_no+1] - # line_no = line_no+1 - line_no = line_no + 1 - if line[-1] != "\n": - # make sure each line ends with a line break, otherwise we run - # into trouble with the last line - line = line+"\n" - if len(line) < 2 and define_command: - # Empty lines finish off define blocks - object_type, game_object = define_command.return_object() - self.world.add_object(object_type, game_object) - define_command = None - elif len(line) < 2 or line[0] == "#": - pass #comments and empty lines are ignored - else: - command = line.split()[0] - # execute a line command - if command in self.line_command_registry.keys(): - self.line_command_registry[command](line[line.find(" ")+1:-1]) - # start of a define block - elif command in define_command_registry.keys(): - define_command = define_command_registry[command] - object_name = line[line.find(" ")+1:-1] - define_command.init_object(object_name) - # parse an option command - elif line[0] == " " or line[0] == "\t": - while line[0] == " " or line[0] == "\t": - line = line[1:] - if line and define_command: - option_command = line.split()[0] - option_arg = line[line.find(" ")+1:-1] - define_command.pass_option(option_command, option_arg) - else: - # XXX: What should be done here? Do nothing, raise a - # syntax error, or something else? - print("Unrecognized syntax in line "+str(line_no)+"!") - - def secondary_load(self, atl_file_name): - ''' - --- DO NOT USE --- - - The function that prepares everything before loading in another ATL file - when the 'load' command is called. - - Currently leads to an endless recursion loop. - ''' - world_directory = os.path.split(self.world_file_name)[0] - load_file_name = os.path.join(world_directory, atl_file_name) - self.load(load_file_name) diff --git a/python/item.py b/python/item.py deleted file mode 100644 index f99092c..0000000 --- a/python/item.py +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# This module takes care of the internal representation of all game items. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 10/05/2015 -# - -import copy -from define import DefineCommand, register_define_command - - -class Item(object): - ''' - This is the actual Item class, which represents a game item. - ''' - - def __init__(self, name): - pass - - -class ItemCommand(DefineCommand): - ''' - The ATL construct to describe an item - ''' - - def __init__(self): - DefineCommand.__init__(self, "define-item", - "Describe a new item that players can interact with") - self.item = None - # TODO Add option commands - - def init_object(self, item_name): - self.item = None - self.item = Item(item_name) - - def return_object(self): - return self.item - -register_define_command(ItemCommand()) - -# A list of all the items that have been defined in the game world -item_registry = dict() - -def register_item(item): - item_registry[item.name] = item - -def get_item(self, item_name): - ''' - Returns a copy of the item object stored under the passed name. - This means that registry retains a "pure" instance of every item type - for future reference. - ''' - # is deepcopy the right copy method to use? if shallow copy is - # sufficient, we could just use the inbuilt dict.copy() - return copy.deepcopy(item_registry[item_name]) - diff --git a/lisp/game-objects.lisp b/lisp/game-objects.lisp index 080bc7e..585cc9b 100644 --- a/lisp/game-objects.lisp +++ b/lisp/game-objects.lisp @@ -35,6 +35,7 @@ (strength 0) (dexterity 0) (aggression 0) + (spawn-probability 0) (weapon "") (armour-class 0)) @@ -48,7 +49,7 @@ (defstruct weapon (name "") (description "") - (type "generic") + (type "") (damage 0)) @@ -69,21 +70,29 @@ ;; Same comment applies as above (let ((command (build-symbol (type-of game-object) "-" property))) (eval `(if (listp (,command ,game-object)) - ;; FIXME This gives problems with multiple values (setf (,command ,game-object) - (remove-if #'(lambda (x) (equalp x ,value)) + (remove-first-if #'(lambda (x) (equalp x ,value)) (,command ,game-object))) ;; TODO set numbers to 0, strings to "" (setf (,command ,game-object) NIL))))) -;; XXX This function is probably superfluous, as all place objects are stored -;; in world, with only their names recorded in the place +(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)) + (dolist (n name-list objects) + (setf objects (cons (get-game-object object-type 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 ((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." (funcall list-function object-type place))) - -(defun monster-strikes-player (monster player) - "The monster attacks a player" - ;;TODO - ) diff --git a/lisp/interpreter.lisp b/lisp/interpreter.lisp index 8f69337..03d4886 100644 --- a/lisp/interpreter.lisp +++ b/lisp/interpreter.lisp @@ -99,7 +99,7 @@ ;; interpret an option command ((or (eql (aref line 0) #\Space) (eql (aref line 0) #\Tab)) - (setf line (string-left-trim '(#\Space #\Tab) line)) + (setf line (trim-whitespace line)) (set-object-attribute current-object (read-from-string line) (read-from-string (second (cut-string line (position #\space line)))))) diff --git a/lisp/ui.lisp b/lisp/ui.lisp index 0fafe21..7d0c25d 100644 --- a/lisp/ui.lisp +++ b/lisp/ui.lisp @@ -25,6 +25,7 @@ (add-game-object player) (set-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player))) + (objectify-place-monsters (player-place player)) ;; The actual game loop (clear-screen) (let ((place (get-game-object 'place (player-place player)))) @@ -93,7 +94,8 @@ (format t "~&Players present: ~A" (string-from-list (place-player p))) (format t "~&Items: ~A" (string-from-list (place-item p))) (format t "~&NPCs: ~A" (string-from-list (place-npc p))) - (format t "~&Monsters: ~A" (string-from-list (place-monster p)))) + (format t "~&Monsters: ~A" (string-from-list + (list-place-objects 'monster p)))) (defun game-command (cmd player) "Execute a typed-in game command" @@ -194,6 +196,7 @@ (defun goto (player &optional location) "Go to the specified location" + ;; Look before you leap (unless location (format t "~&Please specify a location!") (return-from goto)) @@ -204,12 +207,14 @@ :test #'equalp)) (format t "~&This place does not border your current location!") (return-from goto)) + ;; Do all the necessary housekeeping (clear-screen) (debugging "~&~A is going to ~A." (player-name player) location) + (objectify-place-monsters location) (remove-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player)) (set-object-attribute player 'place location) - (set-object-attribute (get-game-object 'place (player-place player)) + (set-object-attribute (get-game-object 'place location) 'player (player-name player)) (describe-place location)) @@ -226,9 +231,9 @@ (let ((place (get-game-object 'place (player-place player))) (description NIL)) (macrolet ((set-descr (type) - (let ((place-descr (build-symbol type "-description")) - (place-object (build-symbol "place-" type))) - `(when (member object-name (,place-object place) + (let ((place-descr (build-symbol type "-description"))) + `(when (member object-name + (list-place-objects ',type place) :test #'equalp) (setf description (,place-descr (get-game-object ',type @@ -344,7 +349,8 @@ (format t "~&Please specify an opponent!") (return-from attack)) (unless (member opponent - (list-place-objects 'monster (player-place player)) + (list-place-objects 'monster + (get-game-object 'place (player-place player))) :test #'equalp) (format t "~&This monster is not here!") (return-from attack)) @@ -363,7 +369,6 @@ (if (> (+ (random 10) p-dex) (+ (random 10) m-dex)) (setf damage (calculate-damage p-str p-weapon m-dex m-ac)) (setf damage (- 0 (calculate-damage m-str m-weapon p-dex p-ac)))) - ;(break) (cond ((plusp damage) (decf (monster-health monster) damage) (format t "~&You hit! ~A points damage." damage) diff --git a/lisp/util.lisp b/lisp/util.lisp index 3ad8306..3ce44e3 100644 --- a/lisp/util.lisp +++ b/lisp/util.lisp @@ -98,13 +98,13 @@ ;;; FUNCTIONS -; Some of these functions are probably quite inefficient (lots of consing) +;; Some of these functions are probably quite inefficient (lots of consing) -;; XXX DEPRECATED Not actually needed anywhere -(defun call-function (function-name &rest args) - "Save myself some quoting when calling a function from a generated symbol" - ;; Perhaps not very clean, but it works - (eval `(,function-name ,@args))) +(defun remove-first-if (fn lst) + "Remove the first element in a list that satisfies the given predicate" + (cond ((null lst) NIL) + ((funcall fn (car lst)) (cdr lst)) + (T (cons (car lst) (remove-first-if fn (cdr lst)))))) (defun average (&rest numbers) "Compute the average of the given numbers" @@ -205,17 +205,19 @@ (read-from-string (string-from-list components ""))) (defun make-list-function (container-type &optional (add-s t)) - "Return function to return a list of the names of all objects of the + "Return a function to return a list of the names of all objects of the specified type in the container struct" #'(lambda (object-type container) - (let ((get-objects (symbol-function (build-symbol container-type "-" - object-type (if add-s "s" "")))) - (get-object-name (symbol-function - (build-symbol object-type "-name"))) - (name-list NIL)) - (dolist (object (funcall get-objects container) name-list) + (let* ((get-objects (symbol-function + (build-symbol container-type "-" + object-type (if add-s "s" "")))) + (get-object-name (symbol-function + (build-symbol object-type "-name"))) + (objects (funcall get-objects container)) (name-list NIL)) + (dolist (o objects name-list) + (when (stringp o) (return objects)) (setf name-list - (cons (funcall get-object-name object) name-list)))))) + (cons (funcall get-object-name o) name-list)))))) (defun choose-number-option (option-list) "The user chooses one out of a list of options, the index is returned" diff --git a/python/__pycache__/character.cpython-34.pyc b/python/__pycache__/character.cpython-34.pyc deleted file mode 100644 index 20a9d5a..0000000 --- a/python/__pycache__/character.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/client.cpython-34.pyc b/python/__pycache__/client.cpython-34.pyc deleted file mode 100644 index 077e656..0000000 --- a/python/__pycache__/client.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/define.cpython-34.pyc b/python/__pycache__/define.cpython-34.pyc deleted file mode 100644 index 397fe49..0000000 --- a/python/__pycache__/define.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/interpreter.cpython-32.pyc b/python/__pycache__/interpreter.cpython-32.pyc deleted file mode 100644 index 2d36d9f..0000000 --- a/python/__pycache__/interpreter.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/interpreter.cpython-34.pyc b/python/__pycache__/interpreter.cpython-34.pyc deleted file mode 100644 index 3d8f05b..0000000 --- a/python/__pycache__/interpreter.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/item.cpython-34.pyc b/python/__pycache__/item.cpython-34.pyc deleted file mode 100644 index bc4b363..0000000 --- a/python/__pycache__/item.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/parser.cpython-34.pyc b/python/__pycache__/parser.cpython-34.pyc deleted file mode 100644 index ffeaf68..0000000 --- a/python/__pycache__/parser.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/place.cpython-32.pyc b/python/__pycache__/place.cpython-32.pyc deleted file mode 100644 index 721a318..0000000 --- a/python/__pycache__/place.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/place.cpython-34.pyc b/python/__pycache__/place.cpython-34.pyc deleted file mode 100644 index bf21fc7..0000000 --- a/python/__pycache__/place.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/server.cpython-34.pyc b/python/__pycache__/server.cpython-34.pyc deleted file mode 100644 index 3ac6f1d..0000000 --- a/python/__pycache__/server.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/world.cpython-32.pyc b/python/__pycache__/world.cpython-32.pyc deleted file mode 100644 index 4276447..0000000 --- a/python/__pycache__/world.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/world.cpython-34.pyc b/python/__pycache__/world.cpython-34.pyc deleted file mode 100644 index 1814006..0000000 --- a/python/__pycache__/world.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/atlantis.py b/python/atlantis.py deleted file mode 100644 index 2c5802f..0000000 --- a/python/atlantis.py +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 02/05/2015 -# - -VERSION = (0, 0, 8) #release, major revision, minor (git) revision - -import sys -import os -from server import Server -from client import Client - - -global clear -if "linux" in sys.platform: - clear = "clear" -elif "win" in sys.platform: - clear = "cls" - - -def start_menu(): - ''' - Show the start menu and process the player's input - ''' - global clear - os.system(clear) - try: #Print the Atlantis logo - fn = os.path.join(os.path.dirname(__file__), "banner.txt") - img_file = open(fn, "r") - logo = img_file.read() - img_file.close() - print(logo) - except IOError: - print("Could not find logo!") - print("Welcome! What do you want to do?") - print(" -> (S)tart a server") - print(" -> (J)oin game") - print(" -> (A)bout") - print(" -> (E)xit") - print() - choice = input("Please choose an option: ") - act_on_choice(choice) - -#TODO! -def act_on_choice(choice): - if choice == "s" or choice == "S": - print("What port do you want to start the server on?") #TODO indicate range! - server_port = input(">> ") - print("What world file do you want to load?") - print("(Specify an absolute or relative path)") - world_file = input(">> ") - Server(server_port, world_file) - elif choice == "j" or choice == "J": - print("What server do you want to connect to?") - print("Format: :") - server_address = input(">> ") - server_ip = server_address[:server_address.find(":")] - server_port = server_address[server_address.find(":")+1:] - Client(server_ip, server_port) - elif choice == "a" or choice == "A": - print_version() - print("(c) 2015 Daniel Vedder") - input("\nPlease press ENTER") - start_menu() - elif choice == "e" or choice == "E": - print("Goodbye!") - exit() - else: - print("Invalid choice!") - input("Please press ENTER") - start_menu() - - -def print_version(): - version_string = str(VERSION[0])+"."+str(VERSION[1])+"."+str(VERSION[2]) - print("Atlantis "+version_string) - print("Licensed under the terms of the GNU General Public License v3.") - -def print_help(): - help_text = """ -Usage: atlantis [options] - - --help -h Print this help text and exit - --version -v Print the version number and exit - --server Start an atlantis server on (requires --world) - --world Use as the world file (requires --server) - --client : Connect as a client to the server at : - -If no arguments are passed, atlantis starts an interactive interface. -""" - print_version() - print(help_text) - -def main(): - if "--version" in sys.argv or "-v" in sys.argv: print_version() - elif "--help" in sys.argv or "-h" in sys.argv: print_help() - elif "--server" in sys.argv and "--world" in sys.argv: - server_port = sys.argv[sys.argv.index("--server")+1] - world_file = sys.argv[sys.argv.index("--world")+1] - Server(server_port, world_file) - elif "--client" in sys.argv: - server_address = sys.argv[sys.argv.index("--client")+1] - server_ip = server_address[:server_address.find(":")] - server_port = server_address[server_address.find(":")+1:] - Client(server_ip, server_port) - else: - start_menu() - -if __name__ == "__main__": - main() diff --git a/python/banner.txt b/python/banner.txt deleted file mode 100644 index 68d8ee8..0000000 --- a/python/banner.txt +++ /dev/null @@ -1,10 +0,0 @@ -==================================================== -|| \ / || -|| - O -MM _ MM ATLANTIS || -|| / \ ||_/.\_|| || -|| |*| |*| Lost worlds await || -|| ~~~~~| | A | |~~~~ || -|| ~~~~###########~~~ || -|| ~~~#############~~ (c) 2015 Daniel Vedder || -|| ~~~~~~~~~~~~~~~~~~ || -==================================================== diff --git a/python/character.py b/python/character.py deleted file mode 100644 index 4acbf34..0000000 --- a/python/character.py +++ /dev/null @@ -1,179 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# The character module is in charge of everything to do with the internal -# representation of a player character, including its race and class. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 02/05/2015 -# - -import item -from define import DefineCommand, register_define_command - - -class Race(object): - ''' - A class to describe the races inhabiting the game world - ''' - - def __init__(self, name, description="", - str_bonus=0, dex_bonus=0, - int_bonus=0, const_bonus=0): - ''' - Create a new race. Arguments: - name, description: the name and description of the new race - str_bonus, dex_bonus, int_bonus, const_bonus: attribute bonuses given - to all players of this race - ''' - self.name = name - self.description = description - self.bonuses = {'strength':str_bonus, 'dexterity':dex_bonus, - 'intelligence':int_bonus, 'constitution':const_bonus} - - def set_description(self, description): - self.description = description - - def set_strength(self, str_bonus): - self.bonuses['strength'] = str_bonus - - def set_dexterity(self, dex_bonus): - self.bonuses['dexterity'] = dex_bonus - - def set_constitution(self, const_bonus): - self.bonuses['constitution'] = const_bonus - - def set_intelligence(self, int_bonus): - self.bonuses['intelligence'] = int_bonus - - -class DefineRace(DefineCommand): - ''' - The ATL construct to define a race - ''' - - def __init__(self): - DefineCommand.__init__(self, "define-race", - "Define and describe a race of beings") - self.race = None - # FIXME: need new option methods to act as wrappers around self.race.x - # (self.race doesn't exist yet) - self.add_option("description", "A description of this race", - self.race.set_description) - self.add_option("strength", "The strength bonus that this race gets", - self.race.set_strength) - self.add_option("dexterity", "The dexterity bonus that this race gets", - self.race.set_dexterity) - self.add_option("constitution", "The constitution bonus that this race gets", - self.race.set_constitution) - self.add_option("intelligence", "The intelligence bonus that this race gets", - self.race.set_intelligence) - - def init_object(self, race_name): - self.race = None - self.race = Race(race_name) - - def return_object(self): - return self.race - -register_define_command(DefineRace()) - - -class CharacterClass(object): - ''' - A class to describe all the character classes a player can assume - ''' - - def __init__(self, name, description="", special_items=[]): - ''' - Create a new character class. Arguments: - name, description: the name and description of this character class - special_items: a list of item instances that all players of this - class carry from the start - ''' - self.name = name - self.description = description - self.items = special_items - - def set_description(self, description): - self.description = description - - def add_item(self, item_name): - new_item = item.get_item(item_name) - self.items.append(new_item) - - -class DefineClass(DefineCommand): - ''' - The ATL construct to define a character class - ''' - - def __init__(self): - DefineCommand.__init__(self, "define-class", - "Define and describe a character class") - self.char_class = None - # FIXME: need new option methods to act as wrappers around - # self.char_class.x (self.char_class doesn't exist yet) - self.add_option("description", "Describe this character class", - self.char_class.set_description) - self.add_option("item", "An item that all members of this class carry", - self.char_class.add_item) - - def init_object(self, class_name): - self.char_class = None - self.char_class = CharacterClass(name) - - def return_object(self): - return self.char_class - -register_define_command(DefineClass()) - - -class Player(object): - ''' - The Player class represents a game character. - ''' - - def __init__(self, name, start_health, - character_race, character_class, - start_attributes): - ''' - Create a player. Arguments: - name - The name of the character - start_health - The initial max HP - character_race - a race instance the character belongs to - character_class - a class instance that the character belongs to - start_attributes - A dict containing the player attributes - ('strength', 'constitution', 'dexterity', 'intelligence') - ''' - self.name = name - self.location = "" - self.health = start_health - self.attributes = start_attributes - self.weapons = [] - self.items = [] - - def change_location(self, new_place): - self.location = new_place - - def change_health(self, difference): - self.health = self.health+difference - - def increase_attribute(self, attribute, difference): - self.attributes[attribute] = self.attributes[attribute] + difference - - def add_item(self, item): - self.items.append(item) - - # Warning: might cause bugs! - def remove_item(self, item): - self.items.remove(item) - - def add_weapon(self, weapon): - self.items.append(weapon) - - # Warning: might cause bugs! - def remove_weapon(self, weapon): - self.items.remove(weapon) - diff --git a/python/client.py b/python/client.py deleted file mode 100644 index 93a18e3..0000000 --- a/python/client.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# This is the client module that the player interacts with. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 02/05/2015 -# - -class Client(object): - ''' - The client is the interface between the player and the server. - ''' - - def __init__(self, server_ip=None, server_port=None): - if not (server_ip and server_port): - print("What server IP do you want to connect to?") - server_ip = input(">> ") - print("What port are you using?") - server_port = input(">> ") - print("The client would connect to "+server_ip+":"+server_port) diff --git a/python/define.py b/python/define.py deleted file mode 100644 index 8c22e73..0000000 --- a/python/define.py +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# This module houses the code for the define blocks of the Atlantis -# ATL language. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 04/05/2015 -# - - -class DefineCommand(object): - ''' - This is the super class for all define commands. It should be extended - for each individual command, like define-place, define-monster, etc. - - Each define command works by creating the relevant object, then fleshing - it out as more options are passed to it. Finally, the finished object is - returned. - ''' - - def __init__(self, name, doc_string): - self.name = name - self.doc_string = doc_string - self.option_registry = dict() - - def init_object(self, object_name): - ''' - Initialize the object this command creates - ''' - raise NotImplementedError - - def return_object(self): - ''' - Return the type of the game object ('place', 'npc', 'item' or - 'monster') and the object itself - ''' - raise NotImplementedError - - def pass_option(self, option_name, option_argument): - ''' - Pass this define command one of its options and the relevant argument - ''' - self.option_registry[option_name]["function"](option_argument) - - def add_option(self, option_name, option_docstring, option_function): - ''' - Add an option for this define command. Arguments: - option_name: The name of this option as it would appear in - an atl source file - option_docstring: A description of this option - option_function: The function that this option calls - (this function should take exactly one string argument) - ''' - option_dict = dict(docstring=option_docstring, function=option_function) - self.option_registry[option_name] = option_dict - - -define_command_registry = dict() - -def register_define_command(def_com): - define_command_registry[def_com.name] = def_com - -def get_define_command(command_name): - return define_command_registry[command_name] diff --git a/python/interpreter.py b/python/interpreter.py deleted file mode 100644 index 69d7fc1..0000000 --- a/python/interpreter.py +++ /dev/null @@ -1,112 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# This module reads in a world file and parses it, using the DefineCommands -# defined in other modules. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 02/05/2015 -# - -import os -from world import World -from define import DefineCommand, define_command_registry - - -class Parser(object): - ''' - The actual parser class. It reads in a world file and transforms it into - a series of DefineCommands, which are then executed - ''' - - def __init__(self, world_file_name): - self.line_command_registry = dict() - self.world = World() - self.add_line_commands() - self.world_file_name = world_file_name - - def load(self, world_file_name): - ''' - Load a world file and pass it on to the interpreter - ''' - try: - world_file = open(self.world_file_name, 'r') - atl_text = world_file.readlines() - world_file.close() - print("Loaded "+world_file_name) - except IOError: - print("Failed to load world file '"+world_file_name+"'!") - self.interpret_atl(atl_text) - return self.world - - def add_line_commands(self): - ''' - Line commands are ATL constructs that only take up one line - (unlike define commands). All line commands must be registered in - this method by adding them to the line_command_registry and setting - their key-value to the method to be called when their key appears - in an ATL source file. - ''' - #self.line_command_registry["load"] = self.secondary_load - self.line_command_registry["start-place"] = self.world.set_starting_place - - def interpret_atl(self, atl_source): - ''' - This method interprets a list of ATL source lines, passing them - on to the relevant commands. - ''' - define_command = None # The define command currently being executed - line_no = 0 - for line in atl_source: - # TODO allow for linebreaks - # while line[-2] == "\\": - # line = line + atl_source[line_no+1] - # line_no = line_no+1 - line_no = line_no + 1 - if line[-1] != "\n": - # make sure each line ends with a line break, otherwise we run - # into trouble with the last line - line = line+"\n" - if len(line) < 2 and define_command: - # Empty lines finish off define blocks - object_type, game_object = define_command.return_object() - self.world.add_object(object_type, game_object) - define_command = None - elif len(line) < 2 or line[0] == "#": - pass #comments and empty lines are ignored - else: - command = line.split()[0] - # execute a line command - if command in self.line_command_registry.keys(): - self.line_command_registry[command](line[line.find(" ")+1:-1]) - # start of a define block - elif command in define_command_registry.keys(): - define_command = define_command_registry[command] - object_name = line[line.find(" ")+1:-1] - define_command.init_object(object_name) - # parse an option command - elif line[0] == " " or line[0] == "\t": - while line[0] == " " or line[0] == "\t": - line = line[1:] - if line and define_command: - option_command = line.split()[0] - option_arg = line[line.find(" ")+1:-1] - define_command.pass_option(option_command, option_arg) - else: - # XXX: What should be done here? Do nothing, raise a - # syntax error, or something else? - print("Unrecognized syntax in line "+str(line_no)+"!") - - def secondary_load(self, atl_file_name): - ''' - --- DO NOT USE --- - - The function that prepares everything before loading in another ATL file - when the 'load' command is called. - - Currently leads to an endless recursion loop. - ''' - world_directory = os.path.split(self.world_file_name)[0] - load_file_name = os.path.join(world_directory, atl_file_name) - self.load(load_file_name) diff --git a/python/item.py b/python/item.py deleted file mode 100644 index f99092c..0000000 --- a/python/item.py +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# This module takes care of the internal representation of all game items. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 10/05/2015 -# - -import copy -from define import DefineCommand, register_define_command - - -class Item(object): - ''' - This is the actual Item class, which represents a game item. - ''' - - def __init__(self, name): - pass - - -class ItemCommand(DefineCommand): - ''' - The ATL construct to describe an item - ''' - - def __init__(self): - DefineCommand.__init__(self, "define-item", - "Describe a new item that players can interact with") - self.item = None - # TODO Add option commands - - def init_object(self, item_name): - self.item = None - self.item = Item(item_name) - - def return_object(self): - return self.item - -register_define_command(ItemCommand()) - -# A list of all the items that have been defined in the game world -item_registry = dict() - -def register_item(item): - item_registry[item.name] = item - -def get_item(self, item_name): - ''' - Returns a copy of the item object stored under the passed name. - This means that registry retains a "pure" instance of every item type - for future reference. - ''' - # is deepcopy the right copy method to use? if shallow copy is - # sufficient, we could just use the inbuilt dict.copy() - return copy.deepcopy(item_registry[item_name]) - diff --git a/python/place.py b/python/place.py deleted file mode 100644 index 5e89425..0000000 --- a/python/place.py +++ /dev/null @@ -1,101 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# This module contains the code for a place (a delimited location -# in the world). -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 02/05/2015 -# - -from define import DefineCommand, register_define_command - - -class Place(object): - ''' - A Place is one discrete location in the game world, which can contain - players, monsters, items, NPCs, and links to other places. - ''' - - def __init__(self, name): - ''' - The constructor for a new place. - Instance variables: - name: the name of this place (compulsory) - description: a description string - neighbours: a list of place names of places bordering on this one - monsters: a list of instances of monsters - npc: a list of instances of NPCs - items: a list of instances of items - ''' - self.name = name - self.description = "" - self.neighbours = [] - self.monsters = [] - self.npc = [] - self.items = [] - - def set_description(self, description): - self.description = description - - def add_neighbour(self, place_name): - if place_name not in self.neighbours: - self.neighbours.append(place_name) - - def add_monster(self, monster): - self.monsters.append(monster) - - def add_npc(self, npc): - self.npc.append(npc) - - def add_item(self, item): - self.items.append(item) - - def remove_neighbour(self, place_name): - self.neighbours.remove(place_name) - - # XXX The following methods might cause problems if one attempts to - # remove just one instance of a list that contains several similar - # instances. - def remove_monster(self, monster): - self.monsters.remove(monster) - - def remove_npc(self, npc): - self.npc.remove(npc) - - def remove_item(self, item): - self.items.remove(item) - - -class DefinePlace(DefineCommand): - ''' - The Atlantis language construct to create a place. - ''' - - def __init__(self): - DefineCommand.__init__(self, "define-place", - "Describe a new location in the game world") - self.add_option("description", - "Describe this place", - self.set_description) - self.add_option("neighbour", - "Add a neighbouring place to this place", - self.add_neighbour) - - def init_object(self, place_name): - self.place = None - self.place = Place(name=place_name) - - def return_object(self): - return 'place', self.place - - # This could probably be transformed into a lambda function - def add_neighbour(self, arg): - self.place.add_neighbour(arg) - - # ...same with this one - def set_description(self, arg): - self.place.set_description(arg) - -register_define_command(DefinePlace()) diff --git a/lisp/game-objects.lisp b/lisp/game-objects.lisp index 080bc7e..585cc9b 100644 --- a/lisp/game-objects.lisp +++ b/lisp/game-objects.lisp @@ -35,6 +35,7 @@ (strength 0) (dexterity 0) (aggression 0) + (spawn-probability 0) (weapon "") (armour-class 0)) @@ -48,7 +49,7 @@ (defstruct weapon (name "") (description "") - (type "generic") + (type "") (damage 0)) @@ -69,21 +70,29 @@ ;; Same comment applies as above (let ((command (build-symbol (type-of game-object) "-" property))) (eval `(if (listp (,command ,game-object)) - ;; FIXME This gives problems with multiple values (setf (,command ,game-object) - (remove-if #'(lambda (x) (equalp x ,value)) + (remove-first-if #'(lambda (x) (equalp x ,value)) (,command ,game-object))) ;; TODO set numbers to 0, strings to "" (setf (,command ,game-object) NIL))))) -;; XXX This function is probably superfluous, as all place objects are stored -;; in world, with only their names recorded in the place +(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)) + (dolist (n name-list objects) + (setf objects (cons (get-game-object object-type 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 ((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." (funcall list-function object-type place))) - -(defun monster-strikes-player (monster player) - "The monster attacks a player" - ;;TODO - ) diff --git a/lisp/interpreter.lisp b/lisp/interpreter.lisp index 8f69337..03d4886 100644 --- a/lisp/interpreter.lisp +++ b/lisp/interpreter.lisp @@ -99,7 +99,7 @@ ;; interpret an option command ((or (eql (aref line 0) #\Space) (eql (aref line 0) #\Tab)) - (setf line (string-left-trim '(#\Space #\Tab) line)) + (setf line (trim-whitespace line)) (set-object-attribute current-object (read-from-string line) (read-from-string (second (cut-string line (position #\space line)))))) diff --git a/lisp/ui.lisp b/lisp/ui.lisp index 0fafe21..7d0c25d 100644 --- a/lisp/ui.lisp +++ b/lisp/ui.lisp @@ -25,6 +25,7 @@ (add-game-object player) (set-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player))) + (objectify-place-monsters (player-place player)) ;; The actual game loop (clear-screen) (let ((place (get-game-object 'place (player-place player)))) @@ -93,7 +94,8 @@ (format t "~&Players present: ~A" (string-from-list (place-player p))) (format t "~&Items: ~A" (string-from-list (place-item p))) (format t "~&NPCs: ~A" (string-from-list (place-npc p))) - (format t "~&Monsters: ~A" (string-from-list (place-monster p)))) + (format t "~&Monsters: ~A" (string-from-list + (list-place-objects 'monster p)))) (defun game-command (cmd player) "Execute a typed-in game command" @@ -194,6 +196,7 @@ (defun goto (player &optional location) "Go to the specified location" + ;; Look before you leap (unless location (format t "~&Please specify a location!") (return-from goto)) @@ -204,12 +207,14 @@ :test #'equalp)) (format t "~&This place does not border your current location!") (return-from goto)) + ;; Do all the necessary housekeeping (clear-screen) (debugging "~&~A is going to ~A." (player-name player) location) + (objectify-place-monsters location) (remove-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player)) (set-object-attribute player 'place location) - (set-object-attribute (get-game-object 'place (player-place player)) + (set-object-attribute (get-game-object 'place location) 'player (player-name player)) (describe-place location)) @@ -226,9 +231,9 @@ (let ((place (get-game-object 'place (player-place player))) (description NIL)) (macrolet ((set-descr (type) - (let ((place-descr (build-symbol type "-description")) - (place-object (build-symbol "place-" type))) - `(when (member object-name (,place-object place) + (let ((place-descr (build-symbol type "-description"))) + `(when (member object-name + (list-place-objects ',type place) :test #'equalp) (setf description (,place-descr (get-game-object ',type @@ -344,7 +349,8 @@ (format t "~&Please specify an opponent!") (return-from attack)) (unless (member opponent - (list-place-objects 'monster (player-place player)) + (list-place-objects 'monster + (get-game-object 'place (player-place player))) :test #'equalp) (format t "~&This monster is not here!") (return-from attack)) @@ -363,7 +369,6 @@ (if (> (+ (random 10) p-dex) (+ (random 10) m-dex)) (setf damage (calculate-damage p-str p-weapon m-dex m-ac)) (setf damage (- 0 (calculate-damage m-str m-weapon p-dex p-ac)))) - ;(break) (cond ((plusp damage) (decf (monster-health monster) damage) (format t "~&You hit! ~A points damage." damage) diff --git a/lisp/util.lisp b/lisp/util.lisp index 3ad8306..3ce44e3 100644 --- a/lisp/util.lisp +++ b/lisp/util.lisp @@ -98,13 +98,13 @@ ;;; FUNCTIONS -; Some of these functions are probably quite inefficient (lots of consing) +;; Some of these functions are probably quite inefficient (lots of consing) -;; XXX DEPRECATED Not actually needed anywhere -(defun call-function (function-name &rest args) - "Save myself some quoting when calling a function from a generated symbol" - ;; Perhaps not very clean, but it works - (eval `(,function-name ,@args))) +(defun remove-first-if (fn lst) + "Remove the first element in a list that satisfies the given predicate" + (cond ((null lst) NIL) + ((funcall fn (car lst)) (cdr lst)) + (T (cons (car lst) (remove-first-if fn (cdr lst)))))) (defun average (&rest numbers) "Compute the average of the given numbers" @@ -205,17 +205,19 @@ (read-from-string (string-from-list components ""))) (defun make-list-function (container-type &optional (add-s t)) - "Return function to return a list of the names of all objects of the + "Return a function to return a list of the names of all objects of the specified type in the container struct" #'(lambda (object-type container) - (let ((get-objects (symbol-function (build-symbol container-type "-" - object-type (if add-s "s" "")))) - (get-object-name (symbol-function - (build-symbol object-type "-name"))) - (name-list NIL)) - (dolist (object (funcall get-objects container) name-list) + (let* ((get-objects (symbol-function + (build-symbol container-type "-" + object-type (if add-s "s" "")))) + (get-object-name (symbol-function + (build-symbol object-type "-name"))) + (objects (funcall get-objects container)) (name-list NIL)) + (dolist (o objects name-list) + (when (stringp o) (return objects)) (setf name-list - (cons (funcall get-object-name object) name-list)))))) + (cons (funcall get-object-name o) name-list)))))) (defun choose-number-option (option-list) "The user chooses one out of a list of options, the index is returned" diff --git a/python/__pycache__/character.cpython-34.pyc b/python/__pycache__/character.cpython-34.pyc deleted file mode 100644 index 20a9d5a..0000000 --- a/python/__pycache__/character.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/client.cpython-34.pyc b/python/__pycache__/client.cpython-34.pyc deleted file mode 100644 index 077e656..0000000 --- a/python/__pycache__/client.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/define.cpython-34.pyc b/python/__pycache__/define.cpython-34.pyc deleted file mode 100644 index 397fe49..0000000 --- a/python/__pycache__/define.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/interpreter.cpython-32.pyc b/python/__pycache__/interpreter.cpython-32.pyc deleted file mode 100644 index 2d36d9f..0000000 --- a/python/__pycache__/interpreter.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/interpreter.cpython-34.pyc b/python/__pycache__/interpreter.cpython-34.pyc deleted file mode 100644 index 3d8f05b..0000000 --- a/python/__pycache__/interpreter.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/item.cpython-34.pyc b/python/__pycache__/item.cpython-34.pyc deleted file mode 100644 index bc4b363..0000000 --- a/python/__pycache__/item.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/parser.cpython-34.pyc b/python/__pycache__/parser.cpython-34.pyc deleted file mode 100644 index ffeaf68..0000000 --- a/python/__pycache__/parser.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/place.cpython-32.pyc b/python/__pycache__/place.cpython-32.pyc deleted file mode 100644 index 721a318..0000000 --- a/python/__pycache__/place.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/place.cpython-34.pyc b/python/__pycache__/place.cpython-34.pyc deleted file mode 100644 index bf21fc7..0000000 --- a/python/__pycache__/place.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/server.cpython-34.pyc b/python/__pycache__/server.cpython-34.pyc deleted file mode 100644 index 3ac6f1d..0000000 --- a/python/__pycache__/server.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/world.cpython-32.pyc b/python/__pycache__/world.cpython-32.pyc deleted file mode 100644 index 4276447..0000000 --- a/python/__pycache__/world.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/world.cpython-34.pyc b/python/__pycache__/world.cpython-34.pyc deleted file mode 100644 index 1814006..0000000 --- a/python/__pycache__/world.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/atlantis.py b/python/atlantis.py deleted file mode 100644 index 2c5802f..0000000 --- a/python/atlantis.py +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 02/05/2015 -# - -VERSION = (0, 0, 8) #release, major revision, minor (git) revision - -import sys -import os -from server import Server -from client import Client - - -global clear -if "linux" in sys.platform: - clear = "clear" -elif "win" in sys.platform: - clear = "cls" - - -def start_menu(): - ''' - Show the start menu and process the player's input - ''' - global clear - os.system(clear) - try: #Print the Atlantis logo - fn = os.path.join(os.path.dirname(__file__), "banner.txt") - img_file = open(fn, "r") - logo = img_file.read() - img_file.close() - print(logo) - except IOError: - print("Could not find logo!") - print("Welcome! What do you want to do?") - print(" -> (S)tart a server") - print(" -> (J)oin game") - print(" -> (A)bout") - print(" -> (E)xit") - print() - choice = input("Please choose an option: ") - act_on_choice(choice) - -#TODO! -def act_on_choice(choice): - if choice == "s" or choice == "S": - print("What port do you want to start the server on?") #TODO indicate range! - server_port = input(">> ") - print("What world file do you want to load?") - print("(Specify an absolute or relative path)") - world_file = input(">> ") - Server(server_port, world_file) - elif choice == "j" or choice == "J": - print("What server do you want to connect to?") - print("Format: :") - server_address = input(">> ") - server_ip = server_address[:server_address.find(":")] - server_port = server_address[server_address.find(":")+1:] - Client(server_ip, server_port) - elif choice == "a" or choice == "A": - print_version() - print("(c) 2015 Daniel Vedder") - input("\nPlease press ENTER") - start_menu() - elif choice == "e" or choice == "E": - print("Goodbye!") - exit() - else: - print("Invalid choice!") - input("Please press ENTER") - start_menu() - - -def print_version(): - version_string = str(VERSION[0])+"."+str(VERSION[1])+"."+str(VERSION[2]) - print("Atlantis "+version_string) - print("Licensed under the terms of the GNU General Public License v3.") - -def print_help(): - help_text = """ -Usage: atlantis [options] - - --help -h Print this help text and exit - --version -v Print the version number and exit - --server Start an atlantis server on (requires --world) - --world Use as the world file (requires --server) - --client : Connect as a client to the server at : - -If no arguments are passed, atlantis starts an interactive interface. -""" - print_version() - print(help_text) - -def main(): - if "--version" in sys.argv or "-v" in sys.argv: print_version() - elif "--help" in sys.argv or "-h" in sys.argv: print_help() - elif "--server" in sys.argv and "--world" in sys.argv: - server_port = sys.argv[sys.argv.index("--server")+1] - world_file = sys.argv[sys.argv.index("--world")+1] - Server(server_port, world_file) - elif "--client" in sys.argv: - server_address = sys.argv[sys.argv.index("--client")+1] - server_ip = server_address[:server_address.find(":")] - server_port = server_address[server_address.find(":")+1:] - Client(server_ip, server_port) - else: - start_menu() - -if __name__ == "__main__": - main() diff --git a/python/banner.txt b/python/banner.txt deleted file mode 100644 index 68d8ee8..0000000 --- a/python/banner.txt +++ /dev/null @@ -1,10 +0,0 @@ -==================================================== -|| \ / || -|| - O -MM _ MM ATLANTIS || -|| / \ ||_/.\_|| || -|| |*| |*| Lost worlds await || -|| ~~~~~| | A | |~~~~ || -|| ~~~~###########~~~ || -|| ~~~#############~~ (c) 2015 Daniel Vedder || -|| ~~~~~~~~~~~~~~~~~~ || -==================================================== diff --git a/python/character.py b/python/character.py deleted file mode 100644 index 4acbf34..0000000 --- a/python/character.py +++ /dev/null @@ -1,179 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# The character module is in charge of everything to do with the internal -# representation of a player character, including its race and class. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 02/05/2015 -# - -import item -from define import DefineCommand, register_define_command - - -class Race(object): - ''' - A class to describe the races inhabiting the game world - ''' - - def __init__(self, name, description="", - str_bonus=0, dex_bonus=0, - int_bonus=0, const_bonus=0): - ''' - Create a new race. Arguments: - name, description: the name and description of the new race - str_bonus, dex_bonus, int_bonus, const_bonus: attribute bonuses given - to all players of this race - ''' - self.name = name - self.description = description - self.bonuses = {'strength':str_bonus, 'dexterity':dex_bonus, - 'intelligence':int_bonus, 'constitution':const_bonus} - - def set_description(self, description): - self.description = description - - def set_strength(self, str_bonus): - self.bonuses['strength'] = str_bonus - - def set_dexterity(self, dex_bonus): - self.bonuses['dexterity'] = dex_bonus - - def set_constitution(self, const_bonus): - self.bonuses['constitution'] = const_bonus - - def set_intelligence(self, int_bonus): - self.bonuses['intelligence'] = int_bonus - - -class DefineRace(DefineCommand): - ''' - The ATL construct to define a race - ''' - - def __init__(self): - DefineCommand.__init__(self, "define-race", - "Define and describe a race of beings") - self.race = None - # FIXME: need new option methods to act as wrappers around self.race.x - # (self.race doesn't exist yet) - self.add_option("description", "A description of this race", - self.race.set_description) - self.add_option("strength", "The strength bonus that this race gets", - self.race.set_strength) - self.add_option("dexterity", "The dexterity bonus that this race gets", - self.race.set_dexterity) - self.add_option("constitution", "The constitution bonus that this race gets", - self.race.set_constitution) - self.add_option("intelligence", "The intelligence bonus that this race gets", - self.race.set_intelligence) - - def init_object(self, race_name): - self.race = None - self.race = Race(race_name) - - def return_object(self): - return self.race - -register_define_command(DefineRace()) - - -class CharacterClass(object): - ''' - A class to describe all the character classes a player can assume - ''' - - def __init__(self, name, description="", special_items=[]): - ''' - Create a new character class. Arguments: - name, description: the name and description of this character class - special_items: a list of item instances that all players of this - class carry from the start - ''' - self.name = name - self.description = description - self.items = special_items - - def set_description(self, description): - self.description = description - - def add_item(self, item_name): - new_item = item.get_item(item_name) - self.items.append(new_item) - - -class DefineClass(DefineCommand): - ''' - The ATL construct to define a character class - ''' - - def __init__(self): - DefineCommand.__init__(self, "define-class", - "Define and describe a character class") - self.char_class = None - # FIXME: need new option methods to act as wrappers around - # self.char_class.x (self.char_class doesn't exist yet) - self.add_option("description", "Describe this character class", - self.char_class.set_description) - self.add_option("item", "An item that all members of this class carry", - self.char_class.add_item) - - def init_object(self, class_name): - self.char_class = None - self.char_class = CharacterClass(name) - - def return_object(self): - return self.char_class - -register_define_command(DefineClass()) - - -class Player(object): - ''' - The Player class represents a game character. - ''' - - def __init__(self, name, start_health, - character_race, character_class, - start_attributes): - ''' - Create a player. Arguments: - name - The name of the character - start_health - The initial max HP - character_race - a race instance the character belongs to - character_class - a class instance that the character belongs to - start_attributes - A dict containing the player attributes - ('strength', 'constitution', 'dexterity', 'intelligence') - ''' - self.name = name - self.location = "" - self.health = start_health - self.attributes = start_attributes - self.weapons = [] - self.items = [] - - def change_location(self, new_place): - self.location = new_place - - def change_health(self, difference): - self.health = self.health+difference - - def increase_attribute(self, attribute, difference): - self.attributes[attribute] = self.attributes[attribute] + difference - - def add_item(self, item): - self.items.append(item) - - # Warning: might cause bugs! - def remove_item(self, item): - self.items.remove(item) - - def add_weapon(self, weapon): - self.items.append(weapon) - - # Warning: might cause bugs! - def remove_weapon(self, weapon): - self.items.remove(weapon) - diff --git a/python/client.py b/python/client.py deleted file mode 100644 index 93a18e3..0000000 --- a/python/client.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# This is the client module that the player interacts with. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 02/05/2015 -# - -class Client(object): - ''' - The client is the interface between the player and the server. - ''' - - def __init__(self, server_ip=None, server_port=None): - if not (server_ip and server_port): - print("What server IP do you want to connect to?") - server_ip = input(">> ") - print("What port are you using?") - server_port = input(">> ") - print("The client would connect to "+server_ip+":"+server_port) diff --git a/python/define.py b/python/define.py deleted file mode 100644 index 8c22e73..0000000 --- a/python/define.py +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# This module houses the code for the define blocks of the Atlantis -# ATL language. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 04/05/2015 -# - - -class DefineCommand(object): - ''' - This is the super class for all define commands. It should be extended - for each individual command, like define-place, define-monster, etc. - - Each define command works by creating the relevant object, then fleshing - it out as more options are passed to it. Finally, the finished object is - returned. - ''' - - def __init__(self, name, doc_string): - self.name = name - self.doc_string = doc_string - self.option_registry = dict() - - def init_object(self, object_name): - ''' - Initialize the object this command creates - ''' - raise NotImplementedError - - def return_object(self): - ''' - Return the type of the game object ('place', 'npc', 'item' or - 'monster') and the object itself - ''' - raise NotImplementedError - - def pass_option(self, option_name, option_argument): - ''' - Pass this define command one of its options and the relevant argument - ''' - self.option_registry[option_name]["function"](option_argument) - - def add_option(self, option_name, option_docstring, option_function): - ''' - Add an option for this define command. Arguments: - option_name: The name of this option as it would appear in - an atl source file - option_docstring: A description of this option - option_function: The function that this option calls - (this function should take exactly one string argument) - ''' - option_dict = dict(docstring=option_docstring, function=option_function) - self.option_registry[option_name] = option_dict - - -define_command_registry = dict() - -def register_define_command(def_com): - define_command_registry[def_com.name] = def_com - -def get_define_command(command_name): - return define_command_registry[command_name] diff --git a/python/interpreter.py b/python/interpreter.py deleted file mode 100644 index 69d7fc1..0000000 --- a/python/interpreter.py +++ /dev/null @@ -1,112 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# This module reads in a world file and parses it, using the DefineCommands -# defined in other modules. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 02/05/2015 -# - -import os -from world import World -from define import DefineCommand, define_command_registry - - -class Parser(object): - ''' - The actual parser class. It reads in a world file and transforms it into - a series of DefineCommands, which are then executed - ''' - - def __init__(self, world_file_name): - self.line_command_registry = dict() - self.world = World() - self.add_line_commands() - self.world_file_name = world_file_name - - def load(self, world_file_name): - ''' - Load a world file and pass it on to the interpreter - ''' - try: - world_file = open(self.world_file_name, 'r') - atl_text = world_file.readlines() - world_file.close() - print("Loaded "+world_file_name) - except IOError: - print("Failed to load world file '"+world_file_name+"'!") - self.interpret_atl(atl_text) - return self.world - - def add_line_commands(self): - ''' - Line commands are ATL constructs that only take up one line - (unlike define commands). All line commands must be registered in - this method by adding them to the line_command_registry and setting - their key-value to the method to be called when their key appears - in an ATL source file. - ''' - #self.line_command_registry["load"] = self.secondary_load - self.line_command_registry["start-place"] = self.world.set_starting_place - - def interpret_atl(self, atl_source): - ''' - This method interprets a list of ATL source lines, passing them - on to the relevant commands. - ''' - define_command = None # The define command currently being executed - line_no = 0 - for line in atl_source: - # TODO allow for linebreaks - # while line[-2] == "\\": - # line = line + atl_source[line_no+1] - # line_no = line_no+1 - line_no = line_no + 1 - if line[-1] != "\n": - # make sure each line ends with a line break, otherwise we run - # into trouble with the last line - line = line+"\n" - if len(line) < 2 and define_command: - # Empty lines finish off define blocks - object_type, game_object = define_command.return_object() - self.world.add_object(object_type, game_object) - define_command = None - elif len(line) < 2 or line[0] == "#": - pass #comments and empty lines are ignored - else: - command = line.split()[0] - # execute a line command - if command in self.line_command_registry.keys(): - self.line_command_registry[command](line[line.find(" ")+1:-1]) - # start of a define block - elif command in define_command_registry.keys(): - define_command = define_command_registry[command] - object_name = line[line.find(" ")+1:-1] - define_command.init_object(object_name) - # parse an option command - elif line[0] == " " or line[0] == "\t": - while line[0] == " " or line[0] == "\t": - line = line[1:] - if line and define_command: - option_command = line.split()[0] - option_arg = line[line.find(" ")+1:-1] - define_command.pass_option(option_command, option_arg) - else: - # XXX: What should be done here? Do nothing, raise a - # syntax error, or something else? - print("Unrecognized syntax in line "+str(line_no)+"!") - - def secondary_load(self, atl_file_name): - ''' - --- DO NOT USE --- - - The function that prepares everything before loading in another ATL file - when the 'load' command is called. - - Currently leads to an endless recursion loop. - ''' - world_directory = os.path.split(self.world_file_name)[0] - load_file_name = os.path.join(world_directory, atl_file_name) - self.load(load_file_name) diff --git a/python/item.py b/python/item.py deleted file mode 100644 index f99092c..0000000 --- a/python/item.py +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# This module takes care of the internal representation of all game items. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 10/05/2015 -# - -import copy -from define import DefineCommand, register_define_command - - -class Item(object): - ''' - This is the actual Item class, which represents a game item. - ''' - - def __init__(self, name): - pass - - -class ItemCommand(DefineCommand): - ''' - The ATL construct to describe an item - ''' - - def __init__(self): - DefineCommand.__init__(self, "define-item", - "Describe a new item that players can interact with") - self.item = None - # TODO Add option commands - - def init_object(self, item_name): - self.item = None - self.item = Item(item_name) - - def return_object(self): - return self.item - -register_define_command(ItemCommand()) - -# A list of all the items that have been defined in the game world -item_registry = dict() - -def register_item(item): - item_registry[item.name] = item - -def get_item(self, item_name): - ''' - Returns a copy of the item object stored under the passed name. - This means that registry retains a "pure" instance of every item type - for future reference. - ''' - # is deepcopy the right copy method to use? if shallow copy is - # sufficient, we could just use the inbuilt dict.copy() - return copy.deepcopy(item_registry[item_name]) - diff --git a/python/place.py b/python/place.py deleted file mode 100644 index 5e89425..0000000 --- a/python/place.py +++ /dev/null @@ -1,101 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# This module contains the code for a place (a delimited location -# in the world). -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 02/05/2015 -# - -from define import DefineCommand, register_define_command - - -class Place(object): - ''' - A Place is one discrete location in the game world, which can contain - players, monsters, items, NPCs, and links to other places. - ''' - - def __init__(self, name): - ''' - The constructor for a new place. - Instance variables: - name: the name of this place (compulsory) - description: a description string - neighbours: a list of place names of places bordering on this one - monsters: a list of instances of monsters - npc: a list of instances of NPCs - items: a list of instances of items - ''' - self.name = name - self.description = "" - self.neighbours = [] - self.monsters = [] - self.npc = [] - self.items = [] - - def set_description(self, description): - self.description = description - - def add_neighbour(self, place_name): - if place_name not in self.neighbours: - self.neighbours.append(place_name) - - def add_monster(self, monster): - self.monsters.append(monster) - - def add_npc(self, npc): - self.npc.append(npc) - - def add_item(self, item): - self.items.append(item) - - def remove_neighbour(self, place_name): - self.neighbours.remove(place_name) - - # XXX The following methods might cause problems if one attempts to - # remove just one instance of a list that contains several similar - # instances. - def remove_monster(self, monster): - self.monsters.remove(monster) - - def remove_npc(self, npc): - self.npc.remove(npc) - - def remove_item(self, item): - self.items.remove(item) - - -class DefinePlace(DefineCommand): - ''' - The Atlantis language construct to create a place. - ''' - - def __init__(self): - DefineCommand.__init__(self, "define-place", - "Describe a new location in the game world") - self.add_option("description", - "Describe this place", - self.set_description) - self.add_option("neighbour", - "Add a neighbouring place to this place", - self.add_neighbour) - - def init_object(self, place_name): - self.place = None - self.place = Place(name=place_name) - - def return_object(self): - return 'place', self.place - - # This could probably be transformed into a lambda function - def add_neighbour(self, arg): - self.place.add_neighbour(arg) - - # ...same with this one - def set_description(self, arg): - self.place.set_description(arg) - -register_define_command(DefinePlace()) diff --git a/python/server.py b/python/server.py deleted file mode 100644 index 2c1ce87..0000000 --- a/python/server.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# This is the server module which is in ultimately in charge of all game logic. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 02/05/2015 -# - -from interpreter import Parser -from world import World - -#TODO: lock file for the server! - -class Server(object): - ''' - This is the master server class in charge of setting up everything - necessary for a game. - ''' - - def __init__(self, port, world_file): - print("The server is still under construction!") - self.port = port - self.world_file = world_file - parser = Parser(self.world_file) - self.world = parser.load(self.world_file) - self.test_parser() - - def test_parser(self): - print("World loaded. Details:") - places = self.world.places.keys() - for p in places: - print("\nPlace: "+self.world.get_place(p).name) - print("Description: "+self.world.get_place(p).description) - print("Neighbours: "+str(self.world.get_place(p).neighbours)) - print("Starting place: "+self.world.starting_place) diff --git a/lisp/game-objects.lisp b/lisp/game-objects.lisp index 080bc7e..585cc9b 100644 --- a/lisp/game-objects.lisp +++ b/lisp/game-objects.lisp @@ -35,6 +35,7 @@ (strength 0) (dexterity 0) (aggression 0) + (spawn-probability 0) (weapon "") (armour-class 0)) @@ -48,7 +49,7 @@ (defstruct weapon (name "") (description "") - (type "generic") + (type "") (damage 0)) @@ -69,21 +70,29 @@ ;; Same comment applies as above (let ((command (build-symbol (type-of game-object) "-" property))) (eval `(if (listp (,command ,game-object)) - ;; FIXME This gives problems with multiple values (setf (,command ,game-object) - (remove-if #'(lambda (x) (equalp x ,value)) + (remove-first-if #'(lambda (x) (equalp x ,value)) (,command ,game-object))) ;; TODO set numbers to 0, strings to "" (setf (,command ,game-object) NIL))))) -;; XXX This function is probably superfluous, as all place objects are stored -;; in world, with only their names recorded in the place +(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)) + (dolist (n name-list objects) + (setf objects (cons (get-game-object object-type 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 ((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." (funcall list-function object-type place))) - -(defun monster-strikes-player (monster player) - "The monster attacks a player" - ;;TODO - ) diff --git a/lisp/interpreter.lisp b/lisp/interpreter.lisp index 8f69337..03d4886 100644 --- a/lisp/interpreter.lisp +++ b/lisp/interpreter.lisp @@ -99,7 +99,7 @@ ;; interpret an option command ((or (eql (aref line 0) #\Space) (eql (aref line 0) #\Tab)) - (setf line (string-left-trim '(#\Space #\Tab) line)) + (setf line (trim-whitespace line)) (set-object-attribute current-object (read-from-string line) (read-from-string (second (cut-string line (position #\space line)))))) diff --git a/lisp/ui.lisp b/lisp/ui.lisp index 0fafe21..7d0c25d 100644 --- a/lisp/ui.lisp +++ b/lisp/ui.lisp @@ -25,6 +25,7 @@ (add-game-object player) (set-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player))) + (objectify-place-monsters (player-place player)) ;; The actual game loop (clear-screen) (let ((place (get-game-object 'place (player-place player)))) @@ -93,7 +94,8 @@ (format t "~&Players present: ~A" (string-from-list (place-player p))) (format t "~&Items: ~A" (string-from-list (place-item p))) (format t "~&NPCs: ~A" (string-from-list (place-npc p))) - (format t "~&Monsters: ~A" (string-from-list (place-monster p)))) + (format t "~&Monsters: ~A" (string-from-list + (list-place-objects 'monster p)))) (defun game-command (cmd player) "Execute a typed-in game command" @@ -194,6 +196,7 @@ (defun goto (player &optional location) "Go to the specified location" + ;; Look before you leap (unless location (format t "~&Please specify a location!") (return-from goto)) @@ -204,12 +207,14 @@ :test #'equalp)) (format t "~&This place does not border your current location!") (return-from goto)) + ;; Do all the necessary housekeeping (clear-screen) (debugging "~&~A is going to ~A." (player-name player) location) + (objectify-place-monsters location) (remove-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player)) (set-object-attribute player 'place location) - (set-object-attribute (get-game-object 'place (player-place player)) + (set-object-attribute (get-game-object 'place location) 'player (player-name player)) (describe-place location)) @@ -226,9 +231,9 @@ (let ((place (get-game-object 'place (player-place player))) (description NIL)) (macrolet ((set-descr (type) - (let ((place-descr (build-symbol type "-description")) - (place-object (build-symbol "place-" type))) - `(when (member object-name (,place-object place) + (let ((place-descr (build-symbol type "-description"))) + `(when (member object-name + (list-place-objects ',type place) :test #'equalp) (setf description (,place-descr (get-game-object ',type @@ -344,7 +349,8 @@ (format t "~&Please specify an opponent!") (return-from attack)) (unless (member opponent - (list-place-objects 'monster (player-place player)) + (list-place-objects 'monster + (get-game-object 'place (player-place player))) :test #'equalp) (format t "~&This monster is not here!") (return-from attack)) @@ -363,7 +369,6 @@ (if (> (+ (random 10) p-dex) (+ (random 10) m-dex)) (setf damage (calculate-damage p-str p-weapon m-dex m-ac)) (setf damage (- 0 (calculate-damage m-str m-weapon p-dex p-ac)))) - ;(break) (cond ((plusp damage) (decf (monster-health monster) damage) (format t "~&You hit! ~A points damage." damage) diff --git a/lisp/util.lisp b/lisp/util.lisp index 3ad8306..3ce44e3 100644 --- a/lisp/util.lisp +++ b/lisp/util.lisp @@ -98,13 +98,13 @@ ;;; FUNCTIONS -; Some of these functions are probably quite inefficient (lots of consing) +;; Some of these functions are probably quite inefficient (lots of consing) -;; XXX DEPRECATED Not actually needed anywhere -(defun call-function (function-name &rest args) - "Save myself some quoting when calling a function from a generated symbol" - ;; Perhaps not very clean, but it works - (eval `(,function-name ,@args))) +(defun remove-first-if (fn lst) + "Remove the first element in a list that satisfies the given predicate" + (cond ((null lst) NIL) + ((funcall fn (car lst)) (cdr lst)) + (T (cons (car lst) (remove-first-if fn (cdr lst)))))) (defun average (&rest numbers) "Compute the average of the given numbers" @@ -205,17 +205,19 @@ (read-from-string (string-from-list components ""))) (defun make-list-function (container-type &optional (add-s t)) - "Return function to return a list of the names of all objects of the + "Return a function to return a list of the names of all objects of the specified type in the container struct" #'(lambda (object-type container) - (let ((get-objects (symbol-function (build-symbol container-type "-" - object-type (if add-s "s" "")))) - (get-object-name (symbol-function - (build-symbol object-type "-name"))) - (name-list NIL)) - (dolist (object (funcall get-objects container) name-list) + (let* ((get-objects (symbol-function + (build-symbol container-type "-" + object-type (if add-s "s" "")))) + (get-object-name (symbol-function + (build-symbol object-type "-name"))) + (objects (funcall get-objects container)) (name-list NIL)) + (dolist (o objects name-list) + (when (stringp o) (return objects)) (setf name-list - (cons (funcall get-object-name object) name-list)))))) + (cons (funcall get-object-name o) name-list)))))) (defun choose-number-option (option-list) "The user chooses one out of a list of options, the index is returned" diff --git a/python/__pycache__/character.cpython-34.pyc b/python/__pycache__/character.cpython-34.pyc deleted file mode 100644 index 20a9d5a..0000000 --- a/python/__pycache__/character.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/client.cpython-34.pyc b/python/__pycache__/client.cpython-34.pyc deleted file mode 100644 index 077e656..0000000 --- a/python/__pycache__/client.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/define.cpython-34.pyc b/python/__pycache__/define.cpython-34.pyc deleted file mode 100644 index 397fe49..0000000 --- a/python/__pycache__/define.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/interpreter.cpython-32.pyc b/python/__pycache__/interpreter.cpython-32.pyc deleted file mode 100644 index 2d36d9f..0000000 --- a/python/__pycache__/interpreter.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/interpreter.cpython-34.pyc b/python/__pycache__/interpreter.cpython-34.pyc deleted file mode 100644 index 3d8f05b..0000000 --- a/python/__pycache__/interpreter.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/item.cpython-34.pyc b/python/__pycache__/item.cpython-34.pyc deleted file mode 100644 index bc4b363..0000000 --- a/python/__pycache__/item.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/parser.cpython-34.pyc b/python/__pycache__/parser.cpython-34.pyc deleted file mode 100644 index ffeaf68..0000000 --- a/python/__pycache__/parser.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/place.cpython-32.pyc b/python/__pycache__/place.cpython-32.pyc deleted file mode 100644 index 721a318..0000000 --- a/python/__pycache__/place.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/place.cpython-34.pyc b/python/__pycache__/place.cpython-34.pyc deleted file mode 100644 index bf21fc7..0000000 --- a/python/__pycache__/place.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/server.cpython-34.pyc b/python/__pycache__/server.cpython-34.pyc deleted file mode 100644 index 3ac6f1d..0000000 --- a/python/__pycache__/server.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/world.cpython-32.pyc b/python/__pycache__/world.cpython-32.pyc deleted file mode 100644 index 4276447..0000000 --- a/python/__pycache__/world.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/world.cpython-34.pyc b/python/__pycache__/world.cpython-34.pyc deleted file mode 100644 index 1814006..0000000 --- a/python/__pycache__/world.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/atlantis.py b/python/atlantis.py deleted file mode 100644 index 2c5802f..0000000 --- a/python/atlantis.py +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 02/05/2015 -# - -VERSION = (0, 0, 8) #release, major revision, minor (git) revision - -import sys -import os -from server import Server -from client import Client - - -global clear -if "linux" in sys.platform: - clear = "clear" -elif "win" in sys.platform: - clear = "cls" - - -def start_menu(): - ''' - Show the start menu and process the player's input - ''' - global clear - os.system(clear) - try: #Print the Atlantis logo - fn = os.path.join(os.path.dirname(__file__), "banner.txt") - img_file = open(fn, "r") - logo = img_file.read() - img_file.close() - print(logo) - except IOError: - print("Could not find logo!") - print("Welcome! What do you want to do?") - print(" -> (S)tart a server") - print(" -> (J)oin game") - print(" -> (A)bout") - print(" -> (E)xit") - print() - choice = input("Please choose an option: ") - act_on_choice(choice) - -#TODO! -def act_on_choice(choice): - if choice == "s" or choice == "S": - print("What port do you want to start the server on?") #TODO indicate range! - server_port = input(">> ") - print("What world file do you want to load?") - print("(Specify an absolute or relative path)") - world_file = input(">> ") - Server(server_port, world_file) - elif choice == "j" or choice == "J": - print("What server do you want to connect to?") - print("Format: :") - server_address = input(">> ") - server_ip = server_address[:server_address.find(":")] - server_port = server_address[server_address.find(":")+1:] - Client(server_ip, server_port) - elif choice == "a" or choice == "A": - print_version() - print("(c) 2015 Daniel Vedder") - input("\nPlease press ENTER") - start_menu() - elif choice == "e" or choice == "E": - print("Goodbye!") - exit() - else: - print("Invalid choice!") - input("Please press ENTER") - start_menu() - - -def print_version(): - version_string = str(VERSION[0])+"."+str(VERSION[1])+"."+str(VERSION[2]) - print("Atlantis "+version_string) - print("Licensed under the terms of the GNU General Public License v3.") - -def print_help(): - help_text = """ -Usage: atlantis [options] - - --help -h Print this help text and exit - --version -v Print the version number and exit - --server Start an atlantis server on (requires --world) - --world Use as the world file (requires --server) - --client : Connect as a client to the server at : - -If no arguments are passed, atlantis starts an interactive interface. -""" - print_version() - print(help_text) - -def main(): - if "--version" in sys.argv or "-v" in sys.argv: print_version() - elif "--help" in sys.argv or "-h" in sys.argv: print_help() - elif "--server" in sys.argv and "--world" in sys.argv: - server_port = sys.argv[sys.argv.index("--server")+1] - world_file = sys.argv[sys.argv.index("--world")+1] - Server(server_port, world_file) - elif "--client" in sys.argv: - server_address = sys.argv[sys.argv.index("--client")+1] - server_ip = server_address[:server_address.find(":")] - server_port = server_address[server_address.find(":")+1:] - Client(server_ip, server_port) - else: - start_menu() - -if __name__ == "__main__": - main() diff --git a/python/banner.txt b/python/banner.txt deleted file mode 100644 index 68d8ee8..0000000 --- a/python/banner.txt +++ /dev/null @@ -1,10 +0,0 @@ -==================================================== -|| \ / || -|| - O -MM _ MM ATLANTIS || -|| / \ ||_/.\_|| || -|| |*| |*| Lost worlds await || -|| ~~~~~| | A | |~~~~ || -|| ~~~~###########~~~ || -|| ~~~#############~~ (c) 2015 Daniel Vedder || -|| ~~~~~~~~~~~~~~~~~~ || -==================================================== diff --git a/python/character.py b/python/character.py deleted file mode 100644 index 4acbf34..0000000 --- a/python/character.py +++ /dev/null @@ -1,179 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# The character module is in charge of everything to do with the internal -# representation of a player character, including its race and class. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 02/05/2015 -# - -import item -from define import DefineCommand, register_define_command - - -class Race(object): - ''' - A class to describe the races inhabiting the game world - ''' - - def __init__(self, name, description="", - str_bonus=0, dex_bonus=0, - int_bonus=0, const_bonus=0): - ''' - Create a new race. Arguments: - name, description: the name and description of the new race - str_bonus, dex_bonus, int_bonus, const_bonus: attribute bonuses given - to all players of this race - ''' - self.name = name - self.description = description - self.bonuses = {'strength':str_bonus, 'dexterity':dex_bonus, - 'intelligence':int_bonus, 'constitution':const_bonus} - - def set_description(self, description): - self.description = description - - def set_strength(self, str_bonus): - self.bonuses['strength'] = str_bonus - - def set_dexterity(self, dex_bonus): - self.bonuses['dexterity'] = dex_bonus - - def set_constitution(self, const_bonus): - self.bonuses['constitution'] = const_bonus - - def set_intelligence(self, int_bonus): - self.bonuses['intelligence'] = int_bonus - - -class DefineRace(DefineCommand): - ''' - The ATL construct to define a race - ''' - - def __init__(self): - DefineCommand.__init__(self, "define-race", - "Define and describe a race of beings") - self.race = None - # FIXME: need new option methods to act as wrappers around self.race.x - # (self.race doesn't exist yet) - self.add_option("description", "A description of this race", - self.race.set_description) - self.add_option("strength", "The strength bonus that this race gets", - self.race.set_strength) - self.add_option("dexterity", "The dexterity bonus that this race gets", - self.race.set_dexterity) - self.add_option("constitution", "The constitution bonus that this race gets", - self.race.set_constitution) - self.add_option("intelligence", "The intelligence bonus that this race gets", - self.race.set_intelligence) - - def init_object(self, race_name): - self.race = None - self.race = Race(race_name) - - def return_object(self): - return self.race - -register_define_command(DefineRace()) - - -class CharacterClass(object): - ''' - A class to describe all the character classes a player can assume - ''' - - def __init__(self, name, description="", special_items=[]): - ''' - Create a new character class. Arguments: - name, description: the name and description of this character class - special_items: a list of item instances that all players of this - class carry from the start - ''' - self.name = name - self.description = description - self.items = special_items - - def set_description(self, description): - self.description = description - - def add_item(self, item_name): - new_item = item.get_item(item_name) - self.items.append(new_item) - - -class DefineClass(DefineCommand): - ''' - The ATL construct to define a character class - ''' - - def __init__(self): - DefineCommand.__init__(self, "define-class", - "Define and describe a character class") - self.char_class = None - # FIXME: need new option methods to act as wrappers around - # self.char_class.x (self.char_class doesn't exist yet) - self.add_option("description", "Describe this character class", - self.char_class.set_description) - self.add_option("item", "An item that all members of this class carry", - self.char_class.add_item) - - def init_object(self, class_name): - self.char_class = None - self.char_class = CharacterClass(name) - - def return_object(self): - return self.char_class - -register_define_command(DefineClass()) - - -class Player(object): - ''' - The Player class represents a game character. - ''' - - def __init__(self, name, start_health, - character_race, character_class, - start_attributes): - ''' - Create a player. Arguments: - name - The name of the character - start_health - The initial max HP - character_race - a race instance the character belongs to - character_class - a class instance that the character belongs to - start_attributes - A dict containing the player attributes - ('strength', 'constitution', 'dexterity', 'intelligence') - ''' - self.name = name - self.location = "" - self.health = start_health - self.attributes = start_attributes - self.weapons = [] - self.items = [] - - def change_location(self, new_place): - self.location = new_place - - def change_health(self, difference): - self.health = self.health+difference - - def increase_attribute(self, attribute, difference): - self.attributes[attribute] = self.attributes[attribute] + difference - - def add_item(self, item): - self.items.append(item) - - # Warning: might cause bugs! - def remove_item(self, item): - self.items.remove(item) - - def add_weapon(self, weapon): - self.items.append(weapon) - - # Warning: might cause bugs! - def remove_weapon(self, weapon): - self.items.remove(weapon) - diff --git a/python/client.py b/python/client.py deleted file mode 100644 index 93a18e3..0000000 --- a/python/client.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# This is the client module that the player interacts with. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 02/05/2015 -# - -class Client(object): - ''' - The client is the interface between the player and the server. - ''' - - def __init__(self, server_ip=None, server_port=None): - if not (server_ip and server_port): - print("What server IP do you want to connect to?") - server_ip = input(">> ") - print("What port are you using?") - server_port = input(">> ") - print("The client would connect to "+server_ip+":"+server_port) diff --git a/python/define.py b/python/define.py deleted file mode 100644 index 8c22e73..0000000 --- a/python/define.py +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# This module houses the code for the define blocks of the Atlantis -# ATL language. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 04/05/2015 -# - - -class DefineCommand(object): - ''' - This is the super class for all define commands. It should be extended - for each individual command, like define-place, define-monster, etc. - - Each define command works by creating the relevant object, then fleshing - it out as more options are passed to it. Finally, the finished object is - returned. - ''' - - def __init__(self, name, doc_string): - self.name = name - self.doc_string = doc_string - self.option_registry = dict() - - def init_object(self, object_name): - ''' - Initialize the object this command creates - ''' - raise NotImplementedError - - def return_object(self): - ''' - Return the type of the game object ('place', 'npc', 'item' or - 'monster') and the object itself - ''' - raise NotImplementedError - - def pass_option(self, option_name, option_argument): - ''' - Pass this define command one of its options and the relevant argument - ''' - self.option_registry[option_name]["function"](option_argument) - - def add_option(self, option_name, option_docstring, option_function): - ''' - Add an option for this define command. Arguments: - option_name: The name of this option as it would appear in - an atl source file - option_docstring: A description of this option - option_function: The function that this option calls - (this function should take exactly one string argument) - ''' - option_dict = dict(docstring=option_docstring, function=option_function) - self.option_registry[option_name] = option_dict - - -define_command_registry = dict() - -def register_define_command(def_com): - define_command_registry[def_com.name] = def_com - -def get_define_command(command_name): - return define_command_registry[command_name] diff --git a/python/interpreter.py b/python/interpreter.py deleted file mode 100644 index 69d7fc1..0000000 --- a/python/interpreter.py +++ /dev/null @@ -1,112 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# This module reads in a world file and parses it, using the DefineCommands -# defined in other modules. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 02/05/2015 -# - -import os -from world import World -from define import DefineCommand, define_command_registry - - -class Parser(object): - ''' - The actual parser class. It reads in a world file and transforms it into - a series of DefineCommands, which are then executed - ''' - - def __init__(self, world_file_name): - self.line_command_registry = dict() - self.world = World() - self.add_line_commands() - self.world_file_name = world_file_name - - def load(self, world_file_name): - ''' - Load a world file and pass it on to the interpreter - ''' - try: - world_file = open(self.world_file_name, 'r') - atl_text = world_file.readlines() - world_file.close() - print("Loaded "+world_file_name) - except IOError: - print("Failed to load world file '"+world_file_name+"'!") - self.interpret_atl(atl_text) - return self.world - - def add_line_commands(self): - ''' - Line commands are ATL constructs that only take up one line - (unlike define commands). All line commands must be registered in - this method by adding them to the line_command_registry and setting - their key-value to the method to be called when their key appears - in an ATL source file. - ''' - #self.line_command_registry["load"] = self.secondary_load - self.line_command_registry["start-place"] = self.world.set_starting_place - - def interpret_atl(self, atl_source): - ''' - This method interprets a list of ATL source lines, passing them - on to the relevant commands. - ''' - define_command = None # The define command currently being executed - line_no = 0 - for line in atl_source: - # TODO allow for linebreaks - # while line[-2] == "\\": - # line = line + atl_source[line_no+1] - # line_no = line_no+1 - line_no = line_no + 1 - if line[-1] != "\n": - # make sure each line ends with a line break, otherwise we run - # into trouble with the last line - line = line+"\n" - if len(line) < 2 and define_command: - # Empty lines finish off define blocks - object_type, game_object = define_command.return_object() - self.world.add_object(object_type, game_object) - define_command = None - elif len(line) < 2 or line[0] == "#": - pass #comments and empty lines are ignored - else: - command = line.split()[0] - # execute a line command - if command in self.line_command_registry.keys(): - self.line_command_registry[command](line[line.find(" ")+1:-1]) - # start of a define block - elif command in define_command_registry.keys(): - define_command = define_command_registry[command] - object_name = line[line.find(" ")+1:-1] - define_command.init_object(object_name) - # parse an option command - elif line[0] == " " or line[0] == "\t": - while line[0] == " " or line[0] == "\t": - line = line[1:] - if line and define_command: - option_command = line.split()[0] - option_arg = line[line.find(" ")+1:-1] - define_command.pass_option(option_command, option_arg) - else: - # XXX: What should be done here? Do nothing, raise a - # syntax error, or something else? - print("Unrecognized syntax in line "+str(line_no)+"!") - - def secondary_load(self, atl_file_name): - ''' - --- DO NOT USE --- - - The function that prepares everything before loading in another ATL file - when the 'load' command is called. - - Currently leads to an endless recursion loop. - ''' - world_directory = os.path.split(self.world_file_name)[0] - load_file_name = os.path.join(world_directory, atl_file_name) - self.load(load_file_name) diff --git a/python/item.py b/python/item.py deleted file mode 100644 index f99092c..0000000 --- a/python/item.py +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# This module takes care of the internal representation of all game items. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 10/05/2015 -# - -import copy -from define import DefineCommand, register_define_command - - -class Item(object): - ''' - This is the actual Item class, which represents a game item. - ''' - - def __init__(self, name): - pass - - -class ItemCommand(DefineCommand): - ''' - The ATL construct to describe an item - ''' - - def __init__(self): - DefineCommand.__init__(self, "define-item", - "Describe a new item that players can interact with") - self.item = None - # TODO Add option commands - - def init_object(self, item_name): - self.item = None - self.item = Item(item_name) - - def return_object(self): - return self.item - -register_define_command(ItemCommand()) - -# A list of all the items that have been defined in the game world -item_registry = dict() - -def register_item(item): - item_registry[item.name] = item - -def get_item(self, item_name): - ''' - Returns a copy of the item object stored under the passed name. - This means that registry retains a "pure" instance of every item type - for future reference. - ''' - # is deepcopy the right copy method to use? if shallow copy is - # sufficient, we could just use the inbuilt dict.copy() - return copy.deepcopy(item_registry[item_name]) - diff --git a/python/place.py b/python/place.py deleted file mode 100644 index 5e89425..0000000 --- a/python/place.py +++ /dev/null @@ -1,101 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# This module contains the code for a place (a delimited location -# in the world). -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 02/05/2015 -# - -from define import DefineCommand, register_define_command - - -class Place(object): - ''' - A Place is one discrete location in the game world, which can contain - players, monsters, items, NPCs, and links to other places. - ''' - - def __init__(self, name): - ''' - The constructor for a new place. - Instance variables: - name: the name of this place (compulsory) - description: a description string - neighbours: a list of place names of places bordering on this one - monsters: a list of instances of monsters - npc: a list of instances of NPCs - items: a list of instances of items - ''' - self.name = name - self.description = "" - self.neighbours = [] - self.monsters = [] - self.npc = [] - self.items = [] - - def set_description(self, description): - self.description = description - - def add_neighbour(self, place_name): - if place_name not in self.neighbours: - self.neighbours.append(place_name) - - def add_monster(self, monster): - self.monsters.append(monster) - - def add_npc(self, npc): - self.npc.append(npc) - - def add_item(self, item): - self.items.append(item) - - def remove_neighbour(self, place_name): - self.neighbours.remove(place_name) - - # XXX The following methods might cause problems if one attempts to - # remove just one instance of a list that contains several similar - # instances. - def remove_monster(self, monster): - self.monsters.remove(monster) - - def remove_npc(self, npc): - self.npc.remove(npc) - - def remove_item(self, item): - self.items.remove(item) - - -class DefinePlace(DefineCommand): - ''' - The Atlantis language construct to create a place. - ''' - - def __init__(self): - DefineCommand.__init__(self, "define-place", - "Describe a new location in the game world") - self.add_option("description", - "Describe this place", - self.set_description) - self.add_option("neighbour", - "Add a neighbouring place to this place", - self.add_neighbour) - - def init_object(self, place_name): - self.place = None - self.place = Place(name=place_name) - - def return_object(self): - return 'place', self.place - - # This could probably be transformed into a lambda function - def add_neighbour(self, arg): - self.place.add_neighbour(arg) - - # ...same with this one - def set_description(self, arg): - self.place.set_description(arg) - -register_define_command(DefinePlace()) diff --git a/python/server.py b/python/server.py deleted file mode 100644 index 2c1ce87..0000000 --- a/python/server.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# This is the server module which is in ultimately in charge of all game logic. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 02/05/2015 -# - -from interpreter import Parser -from world import World - -#TODO: lock file for the server! - -class Server(object): - ''' - This is the master server class in charge of setting up everything - necessary for a game. - ''' - - def __init__(self, port, world_file): - print("The server is still under construction!") - self.port = port - self.world_file = world_file - parser = Parser(self.world_file) - self.world = parser.load(self.world_file) - self.test_parser() - - def test_parser(self): - print("World loaded. Details:") - places = self.world.places.keys() - for p in places: - print("\nPlace: "+self.world.get_place(p).name) - print("Description: "+self.world.get_place(p).description) - print("Neighbours: "+str(self.world.get_place(p).neighbours)) - print("Starting place: "+self.world.starting_place) diff --git a/python/ui.py b/python/ui.py deleted file mode 100644 index b78baae..0000000 --- a/python/ui.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# This is the (text) UI module - all commandline IO should pass through here. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 02/05/2015 -# - - -# -# DO NOT USE YET !!! -# - -class IO(object): - ''' - A wrapper to stdout, stderr and stdin. - Intendend for development work only! - ''' - - def __init__(self): - pass - - def error(self, error_text): - pass diff --git a/lisp/game-objects.lisp b/lisp/game-objects.lisp index 080bc7e..585cc9b 100644 --- a/lisp/game-objects.lisp +++ b/lisp/game-objects.lisp @@ -35,6 +35,7 @@ (strength 0) (dexterity 0) (aggression 0) + (spawn-probability 0) (weapon "") (armour-class 0)) @@ -48,7 +49,7 @@ (defstruct weapon (name "") (description "") - (type "generic") + (type "") (damage 0)) @@ -69,21 +70,29 @@ ;; Same comment applies as above (let ((command (build-symbol (type-of game-object) "-" property))) (eval `(if (listp (,command ,game-object)) - ;; FIXME This gives problems with multiple values (setf (,command ,game-object) - (remove-if #'(lambda (x) (equalp x ,value)) + (remove-first-if #'(lambda (x) (equalp x ,value)) (,command ,game-object))) ;; TODO set numbers to 0, strings to "" (setf (,command ,game-object) NIL))))) -;; XXX This function is probably superfluous, as all place objects are stored -;; in world, with only their names recorded in the place +(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)) + (dolist (n name-list objects) + (setf objects (cons (get-game-object object-type 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 ((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." (funcall list-function object-type place))) - -(defun monster-strikes-player (monster player) - "The monster attacks a player" - ;;TODO - ) diff --git a/lisp/interpreter.lisp b/lisp/interpreter.lisp index 8f69337..03d4886 100644 --- a/lisp/interpreter.lisp +++ b/lisp/interpreter.lisp @@ -99,7 +99,7 @@ ;; interpret an option command ((or (eql (aref line 0) #\Space) (eql (aref line 0) #\Tab)) - (setf line (string-left-trim '(#\Space #\Tab) line)) + (setf line (trim-whitespace line)) (set-object-attribute current-object (read-from-string line) (read-from-string (second (cut-string line (position #\space line)))))) diff --git a/lisp/ui.lisp b/lisp/ui.lisp index 0fafe21..7d0c25d 100644 --- a/lisp/ui.lisp +++ b/lisp/ui.lisp @@ -25,6 +25,7 @@ (add-game-object player) (set-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player))) + (objectify-place-monsters (player-place player)) ;; The actual game loop (clear-screen) (let ((place (get-game-object 'place (player-place player)))) @@ -93,7 +94,8 @@ (format t "~&Players present: ~A" (string-from-list (place-player p))) (format t "~&Items: ~A" (string-from-list (place-item p))) (format t "~&NPCs: ~A" (string-from-list (place-npc p))) - (format t "~&Monsters: ~A" (string-from-list (place-monster p)))) + (format t "~&Monsters: ~A" (string-from-list + (list-place-objects 'monster p)))) (defun game-command (cmd player) "Execute a typed-in game command" @@ -194,6 +196,7 @@ (defun goto (player &optional location) "Go to the specified location" + ;; Look before you leap (unless location (format t "~&Please specify a location!") (return-from goto)) @@ -204,12 +207,14 @@ :test #'equalp)) (format t "~&This place does not border your current location!") (return-from goto)) + ;; Do all the necessary housekeeping (clear-screen) (debugging "~&~A is going to ~A." (player-name player) location) + (objectify-place-monsters location) (remove-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player)) (set-object-attribute player 'place location) - (set-object-attribute (get-game-object 'place (player-place player)) + (set-object-attribute (get-game-object 'place location) 'player (player-name player)) (describe-place location)) @@ -226,9 +231,9 @@ (let ((place (get-game-object 'place (player-place player))) (description NIL)) (macrolet ((set-descr (type) - (let ((place-descr (build-symbol type "-description")) - (place-object (build-symbol "place-" type))) - `(when (member object-name (,place-object place) + (let ((place-descr (build-symbol type "-description"))) + `(when (member object-name + (list-place-objects ',type place) :test #'equalp) (setf description (,place-descr (get-game-object ',type @@ -344,7 +349,8 @@ (format t "~&Please specify an opponent!") (return-from attack)) (unless (member opponent - (list-place-objects 'monster (player-place player)) + (list-place-objects 'monster + (get-game-object 'place (player-place player))) :test #'equalp) (format t "~&This monster is not here!") (return-from attack)) @@ -363,7 +369,6 @@ (if (> (+ (random 10) p-dex) (+ (random 10) m-dex)) (setf damage (calculate-damage p-str p-weapon m-dex m-ac)) (setf damage (- 0 (calculate-damage m-str m-weapon p-dex p-ac)))) - ;(break) (cond ((plusp damage) (decf (monster-health monster) damage) (format t "~&You hit! ~A points damage." damage) diff --git a/lisp/util.lisp b/lisp/util.lisp index 3ad8306..3ce44e3 100644 --- a/lisp/util.lisp +++ b/lisp/util.lisp @@ -98,13 +98,13 @@ ;;; FUNCTIONS -; Some of these functions are probably quite inefficient (lots of consing) +;; Some of these functions are probably quite inefficient (lots of consing) -;; XXX DEPRECATED Not actually needed anywhere -(defun call-function (function-name &rest args) - "Save myself some quoting when calling a function from a generated symbol" - ;; Perhaps not very clean, but it works - (eval `(,function-name ,@args))) +(defun remove-first-if (fn lst) + "Remove the first element in a list that satisfies the given predicate" + (cond ((null lst) NIL) + ((funcall fn (car lst)) (cdr lst)) + (T (cons (car lst) (remove-first-if fn (cdr lst)))))) (defun average (&rest numbers) "Compute the average of the given numbers" @@ -205,17 +205,19 @@ (read-from-string (string-from-list components ""))) (defun make-list-function (container-type &optional (add-s t)) - "Return function to return a list of the names of all objects of the + "Return a function to return a list of the names of all objects of the specified type in the container struct" #'(lambda (object-type container) - (let ((get-objects (symbol-function (build-symbol container-type "-" - object-type (if add-s "s" "")))) - (get-object-name (symbol-function - (build-symbol object-type "-name"))) - (name-list NIL)) - (dolist (object (funcall get-objects container) name-list) + (let* ((get-objects (symbol-function + (build-symbol container-type "-" + object-type (if add-s "s" "")))) + (get-object-name (symbol-function + (build-symbol object-type "-name"))) + (objects (funcall get-objects container)) (name-list NIL)) + (dolist (o objects name-list) + (when (stringp o) (return objects)) (setf name-list - (cons (funcall get-object-name object) name-list)))))) + (cons (funcall get-object-name o) name-list)))))) (defun choose-number-option (option-list) "The user chooses one out of a list of options, the index is returned" diff --git a/python/__pycache__/character.cpython-34.pyc b/python/__pycache__/character.cpython-34.pyc deleted file mode 100644 index 20a9d5a..0000000 --- a/python/__pycache__/character.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/client.cpython-34.pyc b/python/__pycache__/client.cpython-34.pyc deleted file mode 100644 index 077e656..0000000 --- a/python/__pycache__/client.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/define.cpython-34.pyc b/python/__pycache__/define.cpython-34.pyc deleted file mode 100644 index 397fe49..0000000 --- a/python/__pycache__/define.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/interpreter.cpython-32.pyc b/python/__pycache__/interpreter.cpython-32.pyc deleted file mode 100644 index 2d36d9f..0000000 --- a/python/__pycache__/interpreter.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/interpreter.cpython-34.pyc b/python/__pycache__/interpreter.cpython-34.pyc deleted file mode 100644 index 3d8f05b..0000000 --- a/python/__pycache__/interpreter.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/item.cpython-34.pyc b/python/__pycache__/item.cpython-34.pyc deleted file mode 100644 index bc4b363..0000000 --- a/python/__pycache__/item.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/parser.cpython-34.pyc b/python/__pycache__/parser.cpython-34.pyc deleted file mode 100644 index ffeaf68..0000000 --- a/python/__pycache__/parser.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/place.cpython-32.pyc b/python/__pycache__/place.cpython-32.pyc deleted file mode 100644 index 721a318..0000000 --- a/python/__pycache__/place.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/place.cpython-34.pyc b/python/__pycache__/place.cpython-34.pyc deleted file mode 100644 index bf21fc7..0000000 --- a/python/__pycache__/place.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/server.cpython-34.pyc b/python/__pycache__/server.cpython-34.pyc deleted file mode 100644 index 3ac6f1d..0000000 --- a/python/__pycache__/server.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/world.cpython-32.pyc b/python/__pycache__/world.cpython-32.pyc deleted file mode 100644 index 4276447..0000000 --- a/python/__pycache__/world.cpython-32.pyc +++ /dev/null Binary files differ diff --git a/python/__pycache__/world.cpython-34.pyc b/python/__pycache__/world.cpython-34.pyc deleted file mode 100644 index 1814006..0000000 --- a/python/__pycache__/world.cpython-34.pyc +++ /dev/null Binary files differ diff --git a/python/atlantis.py b/python/atlantis.py deleted file mode 100644 index 2c5802f..0000000 --- a/python/atlantis.py +++ /dev/null @@ -1,114 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 02/05/2015 -# - -VERSION = (0, 0, 8) #release, major revision, minor (git) revision - -import sys -import os -from server import Server -from client import Client - - -global clear -if "linux" in sys.platform: - clear = "clear" -elif "win" in sys.platform: - clear = "cls" - - -def start_menu(): - ''' - Show the start menu and process the player's input - ''' - global clear - os.system(clear) - try: #Print the Atlantis logo - fn = os.path.join(os.path.dirname(__file__), "banner.txt") - img_file = open(fn, "r") - logo = img_file.read() - img_file.close() - print(logo) - except IOError: - print("Could not find logo!") - print("Welcome! What do you want to do?") - print(" -> (S)tart a server") - print(" -> (J)oin game") - print(" -> (A)bout") - print(" -> (E)xit") - print() - choice = input("Please choose an option: ") - act_on_choice(choice) - -#TODO! -def act_on_choice(choice): - if choice == "s" or choice == "S": - print("What port do you want to start the server on?") #TODO indicate range! - server_port = input(">> ") - print("What world file do you want to load?") - print("(Specify an absolute or relative path)") - world_file = input(">> ") - Server(server_port, world_file) - elif choice == "j" or choice == "J": - print("What server do you want to connect to?") - print("Format: :") - server_address = input(">> ") - server_ip = server_address[:server_address.find(":")] - server_port = server_address[server_address.find(":")+1:] - Client(server_ip, server_port) - elif choice == "a" or choice == "A": - print_version() - print("(c) 2015 Daniel Vedder") - input("\nPlease press ENTER") - start_menu() - elif choice == "e" or choice == "E": - print("Goodbye!") - exit() - else: - print("Invalid choice!") - input("Please press ENTER") - start_menu() - - -def print_version(): - version_string = str(VERSION[0])+"."+str(VERSION[1])+"."+str(VERSION[2]) - print("Atlantis "+version_string) - print("Licensed under the terms of the GNU General Public License v3.") - -def print_help(): - help_text = """ -Usage: atlantis [options] - - --help -h Print this help text and exit - --version -v Print the version number and exit - --server Start an atlantis server on (requires --world) - --world Use as the world file (requires --server) - --client : Connect as a client to the server at : - -If no arguments are passed, atlantis starts an interactive interface. -""" - print_version() - print(help_text) - -def main(): - if "--version" in sys.argv or "-v" in sys.argv: print_version() - elif "--help" in sys.argv or "-h" in sys.argv: print_help() - elif "--server" in sys.argv and "--world" in sys.argv: - server_port = sys.argv[sys.argv.index("--server")+1] - world_file = sys.argv[sys.argv.index("--world")+1] - Server(server_port, world_file) - elif "--client" in sys.argv: - server_address = sys.argv[sys.argv.index("--client")+1] - server_ip = server_address[:server_address.find(":")] - server_port = server_address[server_address.find(":")+1:] - Client(server_ip, server_port) - else: - start_menu() - -if __name__ == "__main__": - main() diff --git a/python/banner.txt b/python/banner.txt deleted file mode 100644 index 68d8ee8..0000000 --- a/python/banner.txt +++ /dev/null @@ -1,10 +0,0 @@ -==================================================== -|| \ / || -|| - O -MM _ MM ATLANTIS || -|| / \ ||_/.\_|| || -|| |*| |*| Lost worlds await || -|| ~~~~~| | A | |~~~~ || -|| ~~~~###########~~~ || -|| ~~~#############~~ (c) 2015 Daniel Vedder || -|| ~~~~~~~~~~~~~~~~~~ || -==================================================== diff --git a/python/character.py b/python/character.py deleted file mode 100644 index 4acbf34..0000000 --- a/python/character.py +++ /dev/null @@ -1,179 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# The character module is in charge of everything to do with the internal -# representation of a player character, including its race and class. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 02/05/2015 -# - -import item -from define import DefineCommand, register_define_command - - -class Race(object): - ''' - A class to describe the races inhabiting the game world - ''' - - def __init__(self, name, description="", - str_bonus=0, dex_bonus=0, - int_bonus=0, const_bonus=0): - ''' - Create a new race. Arguments: - name, description: the name and description of the new race - str_bonus, dex_bonus, int_bonus, const_bonus: attribute bonuses given - to all players of this race - ''' - self.name = name - self.description = description - self.bonuses = {'strength':str_bonus, 'dexterity':dex_bonus, - 'intelligence':int_bonus, 'constitution':const_bonus} - - def set_description(self, description): - self.description = description - - def set_strength(self, str_bonus): - self.bonuses['strength'] = str_bonus - - def set_dexterity(self, dex_bonus): - self.bonuses['dexterity'] = dex_bonus - - def set_constitution(self, const_bonus): - self.bonuses['constitution'] = const_bonus - - def set_intelligence(self, int_bonus): - self.bonuses['intelligence'] = int_bonus - - -class DefineRace(DefineCommand): - ''' - The ATL construct to define a race - ''' - - def __init__(self): - DefineCommand.__init__(self, "define-race", - "Define and describe a race of beings") - self.race = None - # FIXME: need new option methods to act as wrappers around self.race.x - # (self.race doesn't exist yet) - self.add_option("description", "A description of this race", - self.race.set_description) - self.add_option("strength", "The strength bonus that this race gets", - self.race.set_strength) - self.add_option("dexterity", "The dexterity bonus that this race gets", - self.race.set_dexterity) - self.add_option("constitution", "The constitution bonus that this race gets", - self.race.set_constitution) - self.add_option("intelligence", "The intelligence bonus that this race gets", - self.race.set_intelligence) - - def init_object(self, race_name): - self.race = None - self.race = Race(race_name) - - def return_object(self): - return self.race - -register_define_command(DefineRace()) - - -class CharacterClass(object): - ''' - A class to describe all the character classes a player can assume - ''' - - def __init__(self, name, description="", special_items=[]): - ''' - Create a new character class. Arguments: - name, description: the name and description of this character class - special_items: a list of item instances that all players of this - class carry from the start - ''' - self.name = name - self.description = description - self.items = special_items - - def set_description(self, description): - self.description = description - - def add_item(self, item_name): - new_item = item.get_item(item_name) - self.items.append(new_item) - - -class DefineClass(DefineCommand): - ''' - The ATL construct to define a character class - ''' - - def __init__(self): - DefineCommand.__init__(self, "define-class", - "Define and describe a character class") - self.char_class = None - # FIXME: need new option methods to act as wrappers around - # self.char_class.x (self.char_class doesn't exist yet) - self.add_option("description", "Describe this character class", - self.char_class.set_description) - self.add_option("item", "An item that all members of this class carry", - self.char_class.add_item) - - def init_object(self, class_name): - self.char_class = None - self.char_class = CharacterClass(name) - - def return_object(self): - return self.char_class - -register_define_command(DefineClass()) - - -class Player(object): - ''' - The Player class represents a game character. - ''' - - def __init__(self, name, start_health, - character_race, character_class, - start_attributes): - ''' - Create a player. Arguments: - name - The name of the character - start_health - The initial max HP - character_race - a race instance the character belongs to - character_class - a class instance that the character belongs to - start_attributes - A dict containing the player attributes - ('strength', 'constitution', 'dexterity', 'intelligence') - ''' - self.name = name - self.location = "" - self.health = start_health - self.attributes = start_attributes - self.weapons = [] - self.items = [] - - def change_location(self, new_place): - self.location = new_place - - def change_health(self, difference): - self.health = self.health+difference - - def increase_attribute(self, attribute, difference): - self.attributes[attribute] = self.attributes[attribute] + difference - - def add_item(self, item): - self.items.append(item) - - # Warning: might cause bugs! - def remove_item(self, item): - self.items.remove(item) - - def add_weapon(self, weapon): - self.items.append(weapon) - - # Warning: might cause bugs! - def remove_weapon(self, weapon): - self.items.remove(weapon) - diff --git a/python/client.py b/python/client.py deleted file mode 100644 index 93a18e3..0000000 --- a/python/client.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# This is the client module that the player interacts with. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 02/05/2015 -# - -class Client(object): - ''' - The client is the interface between the player and the server. - ''' - - def __init__(self, server_ip=None, server_port=None): - if not (server_ip and server_port): - print("What server IP do you want to connect to?") - server_ip = input(">> ") - print("What port are you using?") - server_port = input(">> ") - print("The client would connect to "+server_ip+":"+server_port) diff --git a/python/define.py b/python/define.py deleted file mode 100644 index 8c22e73..0000000 --- a/python/define.py +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# This module houses the code for the define blocks of the Atlantis -# ATL language. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 04/05/2015 -# - - -class DefineCommand(object): - ''' - This is the super class for all define commands. It should be extended - for each individual command, like define-place, define-monster, etc. - - Each define command works by creating the relevant object, then fleshing - it out as more options are passed to it. Finally, the finished object is - returned. - ''' - - def __init__(self, name, doc_string): - self.name = name - self.doc_string = doc_string - self.option_registry = dict() - - def init_object(self, object_name): - ''' - Initialize the object this command creates - ''' - raise NotImplementedError - - def return_object(self): - ''' - Return the type of the game object ('place', 'npc', 'item' or - 'monster') and the object itself - ''' - raise NotImplementedError - - def pass_option(self, option_name, option_argument): - ''' - Pass this define command one of its options and the relevant argument - ''' - self.option_registry[option_name]["function"](option_argument) - - def add_option(self, option_name, option_docstring, option_function): - ''' - Add an option for this define command. Arguments: - option_name: The name of this option as it would appear in - an atl source file - option_docstring: A description of this option - option_function: The function that this option calls - (this function should take exactly one string argument) - ''' - option_dict = dict(docstring=option_docstring, function=option_function) - self.option_registry[option_name] = option_dict - - -define_command_registry = dict() - -def register_define_command(def_com): - define_command_registry[def_com.name] = def_com - -def get_define_command(command_name): - return define_command_registry[command_name] diff --git a/python/interpreter.py b/python/interpreter.py deleted file mode 100644 index 69d7fc1..0000000 --- a/python/interpreter.py +++ /dev/null @@ -1,112 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# This module reads in a world file and parses it, using the DefineCommands -# defined in other modules. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 02/05/2015 -# - -import os -from world import World -from define import DefineCommand, define_command_registry - - -class Parser(object): - ''' - The actual parser class. It reads in a world file and transforms it into - a series of DefineCommands, which are then executed - ''' - - def __init__(self, world_file_name): - self.line_command_registry = dict() - self.world = World() - self.add_line_commands() - self.world_file_name = world_file_name - - def load(self, world_file_name): - ''' - Load a world file and pass it on to the interpreter - ''' - try: - world_file = open(self.world_file_name, 'r') - atl_text = world_file.readlines() - world_file.close() - print("Loaded "+world_file_name) - except IOError: - print("Failed to load world file '"+world_file_name+"'!") - self.interpret_atl(atl_text) - return self.world - - def add_line_commands(self): - ''' - Line commands are ATL constructs that only take up one line - (unlike define commands). All line commands must be registered in - this method by adding them to the line_command_registry and setting - their key-value to the method to be called when their key appears - in an ATL source file. - ''' - #self.line_command_registry["load"] = self.secondary_load - self.line_command_registry["start-place"] = self.world.set_starting_place - - def interpret_atl(self, atl_source): - ''' - This method interprets a list of ATL source lines, passing them - on to the relevant commands. - ''' - define_command = None # The define command currently being executed - line_no = 0 - for line in atl_source: - # TODO allow for linebreaks - # while line[-2] == "\\": - # line = line + atl_source[line_no+1] - # line_no = line_no+1 - line_no = line_no + 1 - if line[-1] != "\n": - # make sure each line ends with a line break, otherwise we run - # into trouble with the last line - line = line+"\n" - if len(line) < 2 and define_command: - # Empty lines finish off define blocks - object_type, game_object = define_command.return_object() - self.world.add_object(object_type, game_object) - define_command = None - elif len(line) < 2 or line[0] == "#": - pass #comments and empty lines are ignored - else: - command = line.split()[0] - # execute a line command - if command in self.line_command_registry.keys(): - self.line_command_registry[command](line[line.find(" ")+1:-1]) - # start of a define block - elif command in define_command_registry.keys(): - define_command = define_command_registry[command] - object_name = line[line.find(" ")+1:-1] - define_command.init_object(object_name) - # parse an option command - elif line[0] == " " or line[0] == "\t": - while line[0] == " " or line[0] == "\t": - line = line[1:] - if line and define_command: - option_command = line.split()[0] - option_arg = line[line.find(" ")+1:-1] - define_command.pass_option(option_command, option_arg) - else: - # XXX: What should be done here? Do nothing, raise a - # syntax error, or something else? - print("Unrecognized syntax in line "+str(line_no)+"!") - - def secondary_load(self, atl_file_name): - ''' - --- DO NOT USE --- - - The function that prepares everything before loading in another ATL file - when the 'load' command is called. - - Currently leads to an endless recursion loop. - ''' - world_directory = os.path.split(self.world_file_name)[0] - load_file_name = os.path.join(world_directory, atl_file_name) - self.load(load_file_name) diff --git a/python/item.py b/python/item.py deleted file mode 100644 index f99092c..0000000 --- a/python/item.py +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# This module takes care of the internal representation of all game items. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 10/05/2015 -# - -import copy -from define import DefineCommand, register_define_command - - -class Item(object): - ''' - This is the actual Item class, which represents a game item. - ''' - - def __init__(self, name): - pass - - -class ItemCommand(DefineCommand): - ''' - The ATL construct to describe an item - ''' - - def __init__(self): - DefineCommand.__init__(self, "define-item", - "Describe a new item that players can interact with") - self.item = None - # TODO Add option commands - - def init_object(self, item_name): - self.item = None - self.item = Item(item_name) - - def return_object(self): - return self.item - -register_define_command(ItemCommand()) - -# A list of all the items that have been defined in the game world -item_registry = dict() - -def register_item(item): - item_registry[item.name] = item - -def get_item(self, item_name): - ''' - Returns a copy of the item object stored under the passed name. - This means that registry retains a "pure" instance of every item type - for future reference. - ''' - # is deepcopy the right copy method to use? if shallow copy is - # sufficient, we could just use the inbuilt dict.copy() - return copy.deepcopy(item_registry[item_name]) - diff --git a/python/place.py b/python/place.py deleted file mode 100644 index 5e89425..0000000 --- a/python/place.py +++ /dev/null @@ -1,101 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# This module contains the code for a place (a delimited location -# in the world). -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 02/05/2015 -# - -from define import DefineCommand, register_define_command - - -class Place(object): - ''' - A Place is one discrete location in the game world, which can contain - players, monsters, items, NPCs, and links to other places. - ''' - - def __init__(self, name): - ''' - The constructor for a new place. - Instance variables: - name: the name of this place (compulsory) - description: a description string - neighbours: a list of place names of places bordering on this one - monsters: a list of instances of monsters - npc: a list of instances of NPCs - items: a list of instances of items - ''' - self.name = name - self.description = "" - self.neighbours = [] - self.monsters = [] - self.npc = [] - self.items = [] - - def set_description(self, description): - self.description = description - - def add_neighbour(self, place_name): - if place_name not in self.neighbours: - self.neighbours.append(place_name) - - def add_monster(self, monster): - self.monsters.append(monster) - - def add_npc(self, npc): - self.npc.append(npc) - - def add_item(self, item): - self.items.append(item) - - def remove_neighbour(self, place_name): - self.neighbours.remove(place_name) - - # XXX The following methods might cause problems if one attempts to - # remove just one instance of a list that contains several similar - # instances. - def remove_monster(self, monster): - self.monsters.remove(monster) - - def remove_npc(self, npc): - self.npc.remove(npc) - - def remove_item(self, item): - self.items.remove(item) - - -class DefinePlace(DefineCommand): - ''' - The Atlantis language construct to create a place. - ''' - - def __init__(self): - DefineCommand.__init__(self, "define-place", - "Describe a new location in the game world") - self.add_option("description", - "Describe this place", - self.set_description) - self.add_option("neighbour", - "Add a neighbouring place to this place", - self.add_neighbour) - - def init_object(self, place_name): - self.place = None - self.place = Place(name=place_name) - - def return_object(self): - return 'place', self.place - - # This could probably be transformed into a lambda function - def add_neighbour(self, arg): - self.place.add_neighbour(arg) - - # ...same with this one - def set_description(self, arg): - self.place.set_description(arg) - -register_define_command(DefinePlace()) diff --git a/python/server.py b/python/server.py deleted file mode 100644 index 2c1ce87..0000000 --- a/python/server.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# This is the server module which is in ultimately in charge of all game logic. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 02/05/2015 -# - -from interpreter import Parser -from world import World - -#TODO: lock file for the server! - -class Server(object): - ''' - This is the master server class in charge of setting up everything - necessary for a game. - ''' - - def __init__(self, port, world_file): - print("The server is still under construction!") - self.port = port - self.world_file = world_file - parser = Parser(self.world_file) - self.world = parser.load(self.world_file) - self.test_parser() - - def test_parser(self): - print("World loaded. Details:") - places = self.world.places.keys() - for p in places: - print("\nPlace: "+self.world.get_place(p).name) - print("Description: "+self.world.get_place(p).description) - print("Neighbours: "+str(self.world.get_place(p).neighbours)) - print("Starting place: "+self.world.starting_place) diff --git a/python/ui.py b/python/ui.py deleted file mode 100644 index b78baae..0000000 --- a/python/ui.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# This is the (text) UI module - all commandline IO should pass through here. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 02/05/2015 -# - - -# -# DO NOT USE YET !!! -# - -class IO(object): - ''' - A wrapper to stdout, stderr and stdin. - Intendend for development work only! - ''' - - def __init__(self): - pass - - def error(self, error_text): - pass diff --git a/python/world.py b/python/world.py deleted file mode 100644 index 2716b75..0000000 --- a/python/world.py +++ /dev/null @@ -1,102 +0,0 @@ -#!/usr/bin/python3 -# -# Atlantis is a framework for creating multi-user dungeon worlds. -# The world module saves the current state of the game world. -# -# Licensed under the terms of the GPLv3 -# author: Daniel Vedder -# date: 02/05/2015 -# - -import copy -import place -from character import Race, CharacterClass, Player -from item import Item - - -class World(object): - ''' - The World class saves and gives access to the current state - of the game world. - ''' - - # Originally it was intended that the world hold a copy of every game - # object defined, and thus act as a meta-registry. However, that will - # most likely lead to circular dependencies. (For example, CharacterClass - # requires access to the item registry. If the latter was located here in - # world.py, character.py would have to import world.py, which in turn - # imports character.py...) - # Thus it seems better that each registry be moved to the module that - # deals with the relevant game object. The item registry has already been - # moved to item.py. The monster registry needs to follow suit. I am not - # yet sure what to do with the place and NPC registries. - - def __init__(self): - ''' - The constructor initializes dicts consisting of name:object pairs - for all the places, players, NPCs, items and monsters available in - the world. - ''' - self.places = dict() - self.players = dict() - self.starting_place = None - self.npc = dict() - self.monsters = dict() - - def register_player(self, player): - if player.name not in self.players.keys(): - if player.place == None: - if self.starting_place: - player.place = self.starting_place - else: - print("No starting place set!") - self.players[player.name] = player - else: - print("Attempted to add a player that already exists!") - - def add_object(self, object_type, game_object): - ''' - Add a game object to the world. Acts as a wrapper around add_place, etc. - object_type: 'place', 'npc', 'item' or 'monster' - game_object: the actual object - ''' - if object_type == "place": - self.add_place(game_object) - elif object_type == "npc": - self.add_npc(game_object) - elif object_type == "monster": - self.add_monster(game_object) - - def add_place(self, place): - self.places[place.name] = place - - def add_monster(self, monster): - self.monsters[monster.name] = monster - - def add_npc(self, npc): - self.npc[npc.name] = npc - - def get_player(self, name): - return self.players[name] - - def get_place(self, name): - return self.places[name] - - def set_starting_place(self, place_name): - self.starting_place = place_name - - def get_npc(self, npc_name): - return self.npc[npc_name] - - - # TODO Move to monster.py - def get_monster(self, monster_name): - ''' - Returns a copy of the monster object stored under the passed name. - This means that World retains a "pure" instance of every monster type - for future reference. Each Place holds its own monster objects that - can be changed at will (e.g. in a fight). - ''' - # is deepcopy the right copy method to use? if shallow copy is - # sufficient, we could just use the inbuilt dict.copy() - return copy.deepcopy(self.monsters[monster_name])