diff --git a/ATL/game-objects.atl b/ATL/game-objects.atl index 9e87c8f..a860e54 100644 --- a/ATL/game-objects.atl +++ b/ATL/game-objects.atl @@ -20,4 +20,12 @@ define-weapon "Anaklusmos" description "Riptide, a sword for heroes!" - damage 4 \ No newline at end of file + damage 4 + +define-npc "Hades" + description "Hades, Lord of the Dead!" + says "Beware, mortal - do not tempt me!" + +define-npc "Charon" + description "A robe-clad ghoul, dread ferryman of the Styx." + says "..." \ No newline at end of file diff --git a/ATL/game-objects.atl b/ATL/game-objects.atl index 9e87c8f..a860e54 100644 --- a/ATL/game-objects.atl +++ b/ATL/game-objects.atl @@ -20,4 +20,12 @@ define-weapon "Anaklusmos" description "Riptide, a sword for heroes!" - damage 4 \ No newline at end of file + damage 4 + +define-npc "Hades" + description "Hades, Lord of the Dead!" + says "Beware, mortal - do not tempt me!" + +define-npc "Charon" + description "A robe-clad ghoul, dread ferryman of the Styx." + says "..." \ No newline at end of file diff --git a/ATL/lisp-test.atl b/ATL/lisp-test.atl index aa60c23..7ec9840 100644 --- a/ATL/lisp-test.atl +++ b/ATL/lisp-test.atl @@ -14,19 +14,29 @@ description "This is where you want to be when you are six feet under..." neighbour "Styx" neighbour "Fields of Punishment" + neighbour "Fields of Asphodel" define-place "Fields of Punishment" description "Precisely where you do NOT want to end up..." neighbour "Styx" neighbour "Elysium" + neighbour "Fields of Asphodel" monster "Fury" +define-place "Fields of Asphodel" + description "Nothing special. Really, nothing special at all. +Just a whole load of dead people..." + neighbour "Fields of Punishment" + neighbour "Elysium" + npc "Hades" + define-place "Styx" description "The great river that all must cross - but woe to those who do!" neighbour "Fields of Punishment" neighbour "Elysium" neighbour "Nowhere" item "Anaklusmos" + npc "Charon" load-file lisp-test.atl ;Testing whether recursive loading is prevented load-file races-classes.atl diff --git a/ATL/game-objects.atl b/ATL/game-objects.atl index 9e87c8f..a860e54 100644 --- a/ATL/game-objects.atl +++ b/ATL/game-objects.atl @@ -20,4 +20,12 @@ define-weapon "Anaklusmos" description "Riptide, a sword for heroes!" - damage 4 \ No newline at end of file + damage 4 + +define-npc "Hades" + description "Hades, Lord of the Dead!" + says "Beware, mortal - do not tempt me!" + +define-npc "Charon" + description "A robe-clad ghoul, dread ferryman of the Styx." + says "..." \ No newline at end of file diff --git a/ATL/lisp-test.atl b/ATL/lisp-test.atl index aa60c23..7ec9840 100644 --- a/ATL/lisp-test.atl +++ b/ATL/lisp-test.atl @@ -14,19 +14,29 @@ description "This is where you want to be when you are six feet under..." neighbour "Styx" neighbour "Fields of Punishment" + neighbour "Fields of Asphodel" define-place "Fields of Punishment" description "Precisely where you do NOT want to end up..." neighbour "Styx" neighbour "Elysium" + neighbour "Fields of Asphodel" monster "Fury" +define-place "Fields of Asphodel" + description "Nothing special. Really, nothing special at all. +Just a whole load of dead people..." + neighbour "Fields of Punishment" + neighbour "Elysium" + npc "Hades" + define-place "Styx" description "The great river that all must cross - but woe to those who do!" neighbour "Fields of Punishment" neighbour "Elysium" neighbour "Nowhere" item "Anaklusmos" + npc "Charon" load-file lisp-test.atl ;Testing whether recursive loading is prevented load-file races-classes.atl diff --git a/lisp/atlantis.lisp b/lisp/atlantis.lisp index 6c7698d..d16bccc 100644 --- a/lisp/atlantis.lisp +++ b/lisp/atlantis.lisp @@ -92,6 +92,7 @@ (defun start-menu () "Show the start menu and take a choice from the user" + (clear-screen) (print-text-file "banner.txt") (format t "~&~%Welcome! What do you want to do?") (setf options '("Start a server" "Join a game" "Play single-player" diff --git a/ATL/game-objects.atl b/ATL/game-objects.atl index 9e87c8f..a860e54 100644 --- a/ATL/game-objects.atl +++ b/ATL/game-objects.atl @@ -20,4 +20,12 @@ define-weapon "Anaklusmos" description "Riptide, a sword for heroes!" - damage 4 \ No newline at end of file + damage 4 + +define-npc "Hades" + description "Hades, Lord of the Dead!" + says "Beware, mortal - do not tempt me!" + +define-npc "Charon" + description "A robe-clad ghoul, dread ferryman of the Styx." + says "..." \ No newline at end of file diff --git a/ATL/lisp-test.atl b/ATL/lisp-test.atl index aa60c23..7ec9840 100644 --- a/ATL/lisp-test.atl +++ b/ATL/lisp-test.atl @@ -14,19 +14,29 @@ description "This is where you want to be when you are six feet under..." neighbour "Styx" neighbour "Fields of Punishment" + neighbour "Fields of Asphodel" define-place "Fields of Punishment" description "Precisely where you do NOT want to end up..." neighbour "Styx" neighbour "Elysium" + neighbour "Fields of Asphodel" monster "Fury" +define-place "Fields of Asphodel" + description "Nothing special. Really, nothing special at all. +Just a whole load of dead people..." + neighbour "Fields of Punishment" + neighbour "Elysium" + npc "Hades" + define-place "Styx" description "The great river that all must cross - but woe to those who do!" neighbour "Fields of Punishment" neighbour "Elysium" neighbour "Nowhere" item "Anaklusmos" + npc "Charon" load-file lisp-test.atl ;Testing whether recursive loading is prevented load-file races-classes.atl diff --git a/lisp/atlantis.lisp b/lisp/atlantis.lisp index 6c7698d..d16bccc 100644 --- a/lisp/atlantis.lisp +++ b/lisp/atlantis.lisp @@ -92,6 +92,7 @@ (defun start-menu () "Show the start menu and take a choice from the user" + (clear-screen) (print-text-file "banner.txt") (format t "~&~%Welcome! What do you want to do?") (setf options '("Start a server" "Join a game" "Play single-player" diff --git a/lisp/game-objects.lisp b/lisp/game-objects.lisp index bf64898..c2f6917 100644 --- a/lisp/game-objects.lisp +++ b/lisp/game-objects.lisp @@ -67,7 +67,7 @@ ;; Same comment applies as above (let ((command (build-symbol (type-of game-object) "-" property))) (eval `(if (listp (,command ,game-object)) - ;; TODO This is going to give problems with multiple values + ;; FIXME This is going to give problems with multiple values ;; (but will that scenario ever take place?) (setf (,command ,game-object) (remove-if #'(lambda (x) (equalp x ,value)) diff --git a/ATL/game-objects.atl b/ATL/game-objects.atl index 9e87c8f..a860e54 100644 --- a/ATL/game-objects.atl +++ b/ATL/game-objects.atl @@ -20,4 +20,12 @@ define-weapon "Anaklusmos" description "Riptide, a sword for heroes!" - damage 4 \ No newline at end of file + damage 4 + +define-npc "Hades" + description "Hades, Lord of the Dead!" + says "Beware, mortal - do not tempt me!" + +define-npc "Charon" + description "A robe-clad ghoul, dread ferryman of the Styx." + says "..." \ No newline at end of file diff --git a/ATL/lisp-test.atl b/ATL/lisp-test.atl index aa60c23..7ec9840 100644 --- a/ATL/lisp-test.atl +++ b/ATL/lisp-test.atl @@ -14,19 +14,29 @@ description "This is where you want to be when you are six feet under..." neighbour "Styx" neighbour "Fields of Punishment" + neighbour "Fields of Asphodel" define-place "Fields of Punishment" description "Precisely where you do NOT want to end up..." neighbour "Styx" neighbour "Elysium" + neighbour "Fields of Asphodel" monster "Fury" +define-place "Fields of Asphodel" + description "Nothing special. Really, nothing special at all. +Just a whole load of dead people..." + neighbour "Fields of Punishment" + neighbour "Elysium" + npc "Hades" + define-place "Styx" description "The great river that all must cross - but woe to those who do!" neighbour "Fields of Punishment" neighbour "Elysium" neighbour "Nowhere" item "Anaklusmos" + npc "Charon" load-file lisp-test.atl ;Testing whether recursive loading is prevented load-file races-classes.atl diff --git a/lisp/atlantis.lisp b/lisp/atlantis.lisp index 6c7698d..d16bccc 100644 --- a/lisp/atlantis.lisp +++ b/lisp/atlantis.lisp @@ -92,6 +92,7 @@ (defun start-menu () "Show the start menu and take a choice from the user" + (clear-screen) (print-text-file "banner.txt") (format t "~&~%Welcome! What do you want to do?") (setf options '("Start a server" "Join a game" "Play single-player" diff --git a/lisp/game-objects.lisp b/lisp/game-objects.lisp index bf64898..c2f6917 100644 --- a/lisp/game-objects.lisp +++ b/lisp/game-objects.lisp @@ -67,7 +67,7 @@ ;; Same comment applies as above (let ((command (build-symbol (type-of game-object) "-" property))) (eval `(if (listp (,command ,game-object)) - ;; TODO This is going to give problems with multiple values + ;; FIXME This is going to give problems with multiple values ;; (but will that scenario ever take place?) (setf (,command ,game-object) (remove-if #'(lambda (x) (equalp x ,value)) diff --git a/lisp/ui.lisp b/lisp/ui.lisp index 4e9a9cd..509d606 100644 --- a/lisp/ui.lisp +++ b/lisp/ui.lisp @@ -14,6 +14,7 @@ (let ((player NIL)) (defun play-game (player-name) "The main game loop" + (clear-screen) ;; Initialize the player if necessary (when (null player) (setf player (get-game-object 'player player-name))) @@ -25,6 +26,7 @@ (set-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player))) ;; The actual game loop + (clear-screen) (let ((place (get-game-object 'place (player-place player)))) (describe-place place) (input-string command) @@ -83,9 +85,10 @@ (defun describe-place (p) "Print out a complete description of place p" (when (stringp p) (setf p (get-game-object 'place p))) - (format t "~&~%~A" (string-upcase (place-name p))) + (format t "~&~A" (string-upcase (place-name p))) (format t "~&~%~A" (place-description p)) - (format t "~&Neighbouring places: ~A" (string-from-list (place-neighbour p))) + (format t "~&~%Neighbouring places: ~A" + (string-from-list (place-neighbour p))) (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))) @@ -111,9 +114,9 @@ ;; A list of all in-game commands. Each new command must be registered here. (defvar *commands* '(help place player - goto pickup drop + goto pickup drop talk weapon fight shoot - save)) + about save clear)) ;;; The following commands don't take any arguments except for a player @@ -123,10 +126,12 @@ Commands: help - Show this list of game commands quit/exit - Exit the game +clear - Clear the screen place - Describe the current location player - Describe your player goto - Go to a neighbouring location about - Show a description of this entity +talk - Talk to an NPC pickup - Pick up an item lying around drop - Drop the item shoot - Take a shot at a monster @@ -140,6 +145,11 @@ "Describe the player's current location (wrapper function)" (describe-place (player-place player))) +(defun clear (player) + "Clear the screen (wrapper function)" + (clear-screen) + (place player)) + (defun player (p) "Print a description of this player" (let ((tab (string #\tab))) @@ -164,24 +174,25 @@ ;;; These next functions have to take two arguments (the argument ;;; to the function and a player instance). -(defun save (player &optional game-file) - "Save a game to file (wrapper method around save-world)" - ;; This permissions check could give problems in single-player mode - ;; (unless (player-game-admin player) - ;; (format t "~&Sorry, you do not have the permissions for this action!") - (unless game-file - (format t "~&Where do you want to save the game?") - (input-string game-file)) - (when (y-or-n-p "Save game to ~A?" game-file) - (save-world game-file) - (format t "~&Game saved."))) +(let ((last-save NIL)) + (defun save (player &optional game-file) + "Save a game to file (wrapper method around save-world)" + ;; XXX Include a permissions check (only allow admins to save)? + ;; Could give problems in single-player mode. + (cond (game-file (setf last-save game-file)) + ((and last-save (not game-file)) (setf game-file last-save)) + ((not (or last-save game-file)) + (format t "~&Where do you want to save the game?") + (input-string game-file))) + (when (y-or-n-p "Save game to ~A?" game-file) + (save-world game-file) + (format t "~&Game saved.")))) (defun goto (player &optional location) "Go to the specified location" (unless location (format t "~&Please specify a location!") (return-from goto)) - (debugging "~&~A is going to ~A." (player-name player) location) (when (symbolp location) (setf location (symbol-name location))) (when (not (member location (place-neighbour (get-game-object 'place @@ -189,6 +200,8 @@ :test #'equalp)) (format t "~&This place does not border your current location!") (return-from goto)) + (clear-screen) + (debugging "~&~A is going to ~A." (player-name player) location) (remove-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player)) (set-object-attribute player 'place location) @@ -196,13 +209,54 @@ 'player (player-name player)) (describe-place location)) -(defun about (player &optional object) +(defun about (player &optional object-name) "Print a description of this object" - (unless object + (unless object-name (format t "~&Please specify the object you wish to inspect!") (return-from about)) - ;; TODO - ) + ;; TODO There's got to be a more elegant way of doing this... + (let ((place (get-game-object 'place (player-place player))) + (description NIL)) + (macrolet ((set-descr (place-object place-description object-type) + `(when (member object-name (,place-object place) + :test #'equalp) + (setf description (,place-description + (get-game-object + ',object-type + object-name)))))) + (set-descr place-item item-description item) + (set-descr place-monster monster-description monster) + (set-descr place-npc npc-description npc)) + (if description + (format t "~&(~A) ~A" object-name description) + (format t "~&Could not find ~A!" object-name)))) + + + ;; (cond ((member object-name (place-item place)) + ;; (setf description (item-description + ;; (get-game-object 'item object-name)))) + ;; ((member object-name (place-monster place)) + ;; (setf description (monster-description + ;; (get-game-object 'monster object-name)))) + ;; ((member object-name (place-monster place)) + ;; (setf description (monster-description + ;; (get-game-object 'monster object-name)))) + ;; (t (format t "~&Could not find ~A!" object-name) + ;; (return-from about))) + ;; (format t "~&~A" description))) + +(defun talk (player &optional npc-name) + "Talk to the desired NPC" + ;; TODO Add interactive facility + (unless npc-name + (format t "~&Please specify an NPC to talk to!") + (return-from talk)) + (let* ((place (get-game-object 'place (player-place player))) + (npc (when (member npc-name (place-npc place) :test #'equalp) + (get-game-object 'npc npc-name)))) + (if npc + (format t "~&~A: ~A" (string-upcase npc-name) (npc-says npc)) + (format t "~&~A is not here!" npc-name)))) (defun pickup (player &optional item-name) "The player picks up an item" diff --git a/ATL/game-objects.atl b/ATL/game-objects.atl index 9e87c8f..a860e54 100644 --- a/ATL/game-objects.atl +++ b/ATL/game-objects.atl @@ -20,4 +20,12 @@ define-weapon "Anaklusmos" description "Riptide, a sword for heroes!" - damage 4 \ No newline at end of file + damage 4 + +define-npc "Hades" + description "Hades, Lord of the Dead!" + says "Beware, mortal - do not tempt me!" + +define-npc "Charon" + description "A robe-clad ghoul, dread ferryman of the Styx." + says "..." \ No newline at end of file diff --git a/ATL/lisp-test.atl b/ATL/lisp-test.atl index aa60c23..7ec9840 100644 --- a/ATL/lisp-test.atl +++ b/ATL/lisp-test.atl @@ -14,19 +14,29 @@ description "This is where you want to be when you are six feet under..." neighbour "Styx" neighbour "Fields of Punishment" + neighbour "Fields of Asphodel" define-place "Fields of Punishment" description "Precisely where you do NOT want to end up..." neighbour "Styx" neighbour "Elysium" + neighbour "Fields of Asphodel" monster "Fury" +define-place "Fields of Asphodel" + description "Nothing special. Really, nothing special at all. +Just a whole load of dead people..." + neighbour "Fields of Punishment" + neighbour "Elysium" + npc "Hades" + define-place "Styx" description "The great river that all must cross - but woe to those who do!" neighbour "Fields of Punishment" neighbour "Elysium" neighbour "Nowhere" item "Anaklusmos" + npc "Charon" load-file lisp-test.atl ;Testing whether recursive loading is prevented load-file races-classes.atl diff --git a/lisp/atlantis.lisp b/lisp/atlantis.lisp index 6c7698d..d16bccc 100644 --- a/lisp/atlantis.lisp +++ b/lisp/atlantis.lisp @@ -92,6 +92,7 @@ (defun start-menu () "Show the start menu and take a choice from the user" + (clear-screen) (print-text-file "banner.txt") (format t "~&~%Welcome! What do you want to do?") (setf options '("Start a server" "Join a game" "Play single-player" diff --git a/lisp/game-objects.lisp b/lisp/game-objects.lisp index bf64898..c2f6917 100644 --- a/lisp/game-objects.lisp +++ b/lisp/game-objects.lisp @@ -67,7 +67,7 @@ ;; Same comment applies as above (let ((command (build-symbol (type-of game-object) "-" property))) (eval `(if (listp (,command ,game-object)) - ;; TODO This is going to give problems with multiple values + ;; FIXME This is going to give problems with multiple values ;; (but will that scenario ever take place?) (setf (,command ,game-object) (remove-if #'(lambda (x) (equalp x ,value)) diff --git a/lisp/ui.lisp b/lisp/ui.lisp index 4e9a9cd..509d606 100644 --- a/lisp/ui.lisp +++ b/lisp/ui.lisp @@ -14,6 +14,7 @@ (let ((player NIL)) (defun play-game (player-name) "The main game loop" + (clear-screen) ;; Initialize the player if necessary (when (null player) (setf player (get-game-object 'player player-name))) @@ -25,6 +26,7 @@ (set-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player))) ;; The actual game loop + (clear-screen) (let ((place (get-game-object 'place (player-place player)))) (describe-place place) (input-string command) @@ -83,9 +85,10 @@ (defun describe-place (p) "Print out a complete description of place p" (when (stringp p) (setf p (get-game-object 'place p))) - (format t "~&~%~A" (string-upcase (place-name p))) + (format t "~&~A" (string-upcase (place-name p))) (format t "~&~%~A" (place-description p)) - (format t "~&Neighbouring places: ~A" (string-from-list (place-neighbour p))) + (format t "~&~%Neighbouring places: ~A" + (string-from-list (place-neighbour p))) (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))) @@ -111,9 +114,9 @@ ;; A list of all in-game commands. Each new command must be registered here. (defvar *commands* '(help place player - goto pickup drop + goto pickup drop talk weapon fight shoot - save)) + about save clear)) ;;; The following commands don't take any arguments except for a player @@ -123,10 +126,12 @@ Commands: help - Show this list of game commands quit/exit - Exit the game +clear - Clear the screen place - Describe the current location player - Describe your player goto - Go to a neighbouring location about - Show a description of this entity +talk - Talk to an NPC pickup - Pick up an item lying around drop - Drop the item shoot - Take a shot at a monster @@ -140,6 +145,11 @@ "Describe the player's current location (wrapper function)" (describe-place (player-place player))) +(defun clear (player) + "Clear the screen (wrapper function)" + (clear-screen) + (place player)) + (defun player (p) "Print a description of this player" (let ((tab (string #\tab))) @@ -164,24 +174,25 @@ ;;; These next functions have to take two arguments (the argument ;;; to the function and a player instance). -(defun save (player &optional game-file) - "Save a game to file (wrapper method around save-world)" - ;; This permissions check could give problems in single-player mode - ;; (unless (player-game-admin player) - ;; (format t "~&Sorry, you do not have the permissions for this action!") - (unless game-file - (format t "~&Where do you want to save the game?") - (input-string game-file)) - (when (y-or-n-p "Save game to ~A?" game-file) - (save-world game-file) - (format t "~&Game saved."))) +(let ((last-save NIL)) + (defun save (player &optional game-file) + "Save a game to file (wrapper method around save-world)" + ;; XXX Include a permissions check (only allow admins to save)? + ;; Could give problems in single-player mode. + (cond (game-file (setf last-save game-file)) + ((and last-save (not game-file)) (setf game-file last-save)) + ((not (or last-save game-file)) + (format t "~&Where do you want to save the game?") + (input-string game-file))) + (when (y-or-n-p "Save game to ~A?" game-file) + (save-world game-file) + (format t "~&Game saved.")))) (defun goto (player &optional location) "Go to the specified location" (unless location (format t "~&Please specify a location!") (return-from goto)) - (debugging "~&~A is going to ~A." (player-name player) location) (when (symbolp location) (setf location (symbol-name location))) (when (not (member location (place-neighbour (get-game-object 'place @@ -189,6 +200,8 @@ :test #'equalp)) (format t "~&This place does not border your current location!") (return-from goto)) + (clear-screen) + (debugging "~&~A is going to ~A." (player-name player) location) (remove-object-attribute (get-game-object 'place (player-place player)) 'player (player-name player)) (set-object-attribute player 'place location) @@ -196,13 +209,54 @@ 'player (player-name player)) (describe-place location)) -(defun about (player &optional object) +(defun about (player &optional object-name) "Print a description of this object" - (unless object + (unless object-name (format t "~&Please specify the object you wish to inspect!") (return-from about)) - ;; TODO - ) + ;; TODO There's got to be a more elegant way of doing this... + (let ((place (get-game-object 'place (player-place player))) + (description NIL)) + (macrolet ((set-descr (place-object place-description object-type) + `(when (member object-name (,place-object place) + :test #'equalp) + (setf description (,place-description + (get-game-object + ',object-type + object-name)))))) + (set-descr place-item item-description item) + (set-descr place-monster monster-description monster) + (set-descr place-npc npc-description npc)) + (if description + (format t "~&(~A) ~A" object-name description) + (format t "~&Could not find ~A!" object-name)))) + + + ;; (cond ((member object-name (place-item place)) + ;; (setf description (item-description + ;; (get-game-object 'item object-name)))) + ;; ((member object-name (place-monster place)) + ;; (setf description (monster-description + ;; (get-game-object 'monster object-name)))) + ;; ((member object-name (place-monster place)) + ;; (setf description (monster-description + ;; (get-game-object 'monster object-name)))) + ;; (t (format t "~&Could not find ~A!" object-name) + ;; (return-from about))) + ;; (format t "~&~A" description))) + +(defun talk (player &optional npc-name) + "Talk to the desired NPC" + ;; TODO Add interactive facility + (unless npc-name + (format t "~&Please specify an NPC to talk to!") + (return-from talk)) + (let* ((place (get-game-object 'place (player-place player))) + (npc (when (member npc-name (place-npc place) :test #'equalp) + (get-game-object 'npc npc-name)))) + (if npc + (format t "~&~A: ~A" (string-upcase npc-name) (npc-says npc)) + (format t "~&~A is not here!" npc-name)))) (defun pickup (player &optional item-name) "The player picks up an item" diff --git a/lisp/util.lisp b/lisp/util.lisp index 379ef0d..7ea5102 100644 --- a/lisp/util.lisp +++ b/lisp/util.lisp @@ -206,6 +206,13 @@ ;; Basically just a utility wrapper (nth (choose-number-option option-list) option-list)) +(defun clear-screen () + "Clear the screen in an OS-dependent manner" + ;; NOTE: only works with CLISP! (ext:shell function used) + (cond ((member ':unix *features*) (ext:shell "clear")) + ((member ':win32 *features*) (ext:shell "cls")) + (t (debugging "~&clear-screen is not supported on this operating system!")))) + (defun repl () "Launch a read-eval-print loop" (let ((expr (simple-input expr "lisp >")))