Newsgroups: rec.arts.int-fiction
Path: gmd.de!nntp.gmd.de!Germany.EU.net!EU.net!uknet!pipex!dircon!rheged!simon
From: simon@rheged.dircon.co.uk (Simon Brooke)
Subject: Artoris the bear (was Re: Intelligent NPC design (Bear?)
Message-ID: <Cxu654.ov@rheged.dircon.co.uk>
Organization: none. Disorganization: total.
References: <37jpjf$k98@nntp1.u.washington.edu>
Date: Mon, 17 Oct 1994 21:24:38 GMT
Lines: 250

In article <37jpjf$k98@nntp1.u.washington.edu>,
The Grim Reaper <scythe@u.washington.edu> wrote:
>Reading various messages on the simulation thread, I saw somebody's suggestion
>that someone create a really realistic animal... I think they said a brown 
>bear.  Well, how about if we do that as a group?  If possible, keeping the
>discussion to generic ideas, not language-specific ones.  Just to get started,
>let's say we're going to create the aforementioned bear.  Well, the bear will
>wander around the game, trying to fulfill a couple personal goals.  What
>should they be?  I expect for a bear, eating and sleeping are going to be
>its main priorities.  This means we'll have to keep track of how tired
>and how hungry the bear is.  Perhaps its reaction to the player as well.

OK, chaps, here is Artoris. He's a bit quickly hacked together, but he
works, and fulfills the spec. He works in the context of my own ADE,
and so to understand the code you need to understand a bit about how
that works. You'll see that it's LisP. If you have trouble with it, it
started off in Cambridge LISP but is in the process of being ported to
XLISP. Artoris works in the XLISP version, but uses a lot of Cambridge
LISP idiom.

Within my ADE, a function StandardPreMove takes care of motivating
players to eat when starving, drink when seriously thirsty, flee from
serious danger, and other vital necessities; consequently, Artoris' own
code doesn't do anything about this. Further, my ADE doesn't
differentiate between 'real' players (those controlled by a human
sitting at a keyboard) and NPCs: the 'real' player is just the player
who is currently the value of the variable *activePlayer*.
Incidentally, this allows me to have a verb 'become', as in:

HERMIT -> look at the prince

 THE PRINCE IS A TALL, RATHER INEFFECTIVE LOOKING YOUNG MAN WITH JUG EARS
 THE PRINCE IS CARRYING A MINER'S LAMP
 THE PRINCE IS CARRYING AN OLD SATCHEL

HERMIT -> become the prince

PRINCE -> look at the hermit

 THE HERMIT IS AN AGED MAN WITH A GREY BEARD

Anyway, here's Artoris

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;                                                              ;;
;;      Artoris.lsp                                             ;;
;;                                                              ;;
;;      The player 'Artoris': a large brown bear.               ;;
;;                                                              ;;
;;      Author   : Simon Brooke                                 ;;
;;      Created  : 17:10:94                                     ;;
;;      Copyright: (c) Simon Brooke 1994                        ;;
;;                                                              ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

(defun Artoris (self)			;; Player move function for
					;; Artoris, the big brown bear
  (let ((others (PlayersAt (LocationOf self)))
					;; bind 'others' to other
					;; players who are here,
	(food				;; and 'food' to any
					;; food that's lying around.
	 (Remove nil 
		 (CamMapCar (RObjectsAt (LocationOf self))
			    '(lambda (o) 
			       (cond ((Edible o) o)))))))
					;; Using RObjectsAt implies he
					;; can detect things in 
					;; containers... well that's 
					;; what his nose is for.

    ;; OK: first things first. Is there anything to be eaten? This
    ;; takes priority even over sleeping!

    (cond
     (food				;; if there's food here
      (put self 'foodPath (list (LocationOf self)))
					;; remember where it was...
      (cond 
       ((ilessp (Fed self) 100)		;; if more than mildly hungry...
	(cond 
	 (others			;; if anyone else is present...
	  (Say "GrrrrRRRRR")		;; growl, and
	  (CamMapCar others 
		     '(lambda (other)
			(cond
			 ((lessp (AttitudeOf self other) 0)
					;; unless feeling friendly...
			  (Hit self nil other)))))))
					;; lash out at them.
	(Eat self (NameOf (Choose food)))))))
					;; eat some of the food...

    ;; dealt with food. Anywhere hereabouts to sleep?
    ;; (this bear knows his priorities)

    (cond
     ((Indoor (LocationOf self))	;; good place to sleep...
      (put self 'sleepPath (list (LocationOf self)))))
					;; remember where it is

    ;; Anyone here I don't like? Note: the fact that this is
    ;; coded independently from the bear's sleeping conditions
    ;; means that if he's asleep but doesn't like you he'll 
    ;; wake up and go for you!

    (CamMapCar others			;; for each other player here
	       '(lambda (other)
		  (cond 
		   ((and (lessp (AttitudeOf self other) -10)
					;; if I don't like them...
			 (not (Frightens self other)))
					;; and I doesn't fear them...
		    (Hit self nil other)))))
					;; lash out.

    ;; Remove 'sleeping' from Artoris' list of adjectives. If
    ;; he is sleeping, we'll put it back...

    (put self 'adjs (Remove 'sleeping (get self 'adjs)))

    ;; That deals with the preliminaries. Now decide how to
    ;; move

    (cond
     ((and (get self 'foodPath)
	   (lessp (Fed self) (length (get self 'foodPath))))
					;; If I will get seriously hungry
					;; by the time I get back to the 
					;; food I know about...
      (Move self 
	    (Adjacent (LocationOf self)(car (get self 'foodPath))))
					;; move back up the food path...
      (put self 'foodPath (cdr (get self 'foodPath)))
					;; shortening it as we go...
      (put self 'sleepPath 
	   (ExtendPath (LocationOf self) (get self 'sleepPath))))
					;; but extending the sleep path.

     ((and				;; Artoris is nocturnal:
       (greaterp *time* 80)		;; If the time's later than 8.00am
       (lessp *time* 200))		;; and earlier than 8.00pm then...
      (cond 
       ((Indoor (LocationOf self))	;; if I'm indoors, then
	                                ;; fine; I'll sleep here (Indoor
					;; includes caves)
	(Report '(the bear snores noisily))
					;; (Report only shows if the 
					;; real player is here)
	(put self 'adjs 
	     (cons 'sleeping (get self 'adjs))))
					;; set my first adjective -- the
					;; one shown in short description --
					;; to 'sleeping'.

       ((get self 'sleepPath)		;; Otherwise, if I know of
					;; somewhere safe to sleep...
	(Move self 
	      (Adjacent (LocationOf self)(car (get self 'sleepPath))))
					;; move back up the sleep path...
	(put self 'sleepPath (cdr (get self 'sleepPath)))
					;; shortening it as we go...
	(put self 'foodPath 
	     (ExtendPath (LocationOf self) (get self 'foodPath))))))
					;; but extending the food path.
     (t
      (Searcher self)			;; Otherwise, use standard
					;; environment search move function,
      (put self 'foodPath 
	   (ExtendPath (LocationOf self) (get self 'foodPath)))
					;; but extend the food path...
      (put self 'sleepPath 
	   (ExtendPath (LocationOf self) (get self 'sleepPath)))))
					;; and the sleep path.
    ))					;; That's all, folks.


;; Note: The 'Searcher' move function performs an exhaustive search
;; on the environment, by always choosing the least recently visited
;; adjacent room to visit next. However, it doesn't pass closed doors.

;; Now for Artoris' properties:

(put 'Artoris 'adjs '(brown big fierce ugly))
					;; Descriptive adjectives

(put 'Artoris 'avoid '(quicksand sea scree bog))
					;; Stay away from dangerous 
                                        ;; places

(put 'Artoris 'attitude '((fawn . -20)(doe . -20)))
					;; Giving him strong hostility
					;; to the smaller deer will
					;; make him kill and eat them

(put 'Artoris 'burden nil)		;; 'E ain't carryin' nuffink

(put 'Artoris 'defaultAttitude -10)	;; Normally hostile

(put 'Artoris 'description '(a huge\, lumbering brown bear\, greying at
			       the muzzle))
					;; Canned text for when you look 
					;; at him.

(put 'Artoris 'drunk 100)		;; He's drunk (water, you fool)
					;; quite recently

(flag '(Artoris) 'edible)		;; But you'd need to kill him first

(put 'Artoris 'fed 100)			;; and he's eaten quite recently

(put 'Artoris 'health 3000)		;; About three times as strong
					;; as a person, when healthy

(put 'Artoris 'location 'glade)		;; That's where he starts

(put 'Artoris 'names '(bear))		;; Only he knows his name, and 
					;; he's not telling...

(put 'Artoris 'pronoun 'it)		;; He's a he, actually...

(put 'Artoris 'script '((woffle (Say 'Grrrrrrr))))
					;; Artoris doesn't really talk,
					;; but will respond when spoken
					;; to...

(put 'Artoris 'size 96)		        ;; Eight feet tall

(put 'Artoris 'strength 3000)		;; About three times as strong
					;; as a person at start of play

(put 'Artoris 'weight 12000)		;; about 50 stone (is that too
					;; heavy?

(put 'Artoris 'whenGiven		;; When given something...
     '(lambda (obj donor)
	(cond ((and (Edible obj)	;; if its edible...
		    (lessp (Fed 'Artoris) 100))
					;; and he's hungry....
	       (Eat 'Artoris obj)	;; he'll eat it...
	       (SetAttitudeOf 'Artoris donor 
			      (iplus (AttitudeOf 'Artoris donor) 10)))
					;; and get substantially more friendly
	      (t (Drop obj 'Artoris)	;; Otherwise he'll just drop it, and...
		 (Report '(the bear growls menacingly))))))

-- 
---------------
"There is no point in making further promises now about reducing
taxation: nobody believes us." Edward Heath, October 1994
