#charset "us-ascii" #include "adv3.h" #include "en_us.h" /* Tads-3 Memory Steve Breslin, 2004 email: versim@hotmail.com ==== Licence: Everyone is free to use this, but please provide me with all improvements and modifications. ==== About this module: This module makes Actors remember the last place they saw an object. This is all taken care of automatically without any extra work from the user. Also, we add a verb 'objects' (which lists all the movable objects that the player has come across, along with each one's remembered location, which of course does not necessarily reflect the current game state). Unfortunately, we don't model really robust memory. Other things we might remember in the future: which key goes with which door; whether the lamp was on or off, last we checked; and so on. At present it's just object-location last we knew. Note: If some actor takes an object in the presence of other actors, and the other actor.canSee(object) then returns nil by virtue of the object being in some other actor's inventory, he doesn't remember where the object has gone. We don't know if this is ideal behavior or not, but we've erred on the side of simplicity, as this is fairly easy to fix or customize as desirable. */ modify Actor /* a table for location memory. the key is the object, and the * value is the object's last known location. If you want to * check where the actor thinks an object is, just check * actor.locMemTab[object]. */ locMemTab = static new LookupTable /* When an actor sees an object, he remembers its location. */ setHasSeen(obj) { updateLocMem(obj); inherited(obj); } /* update the memory table if the object has a location. Don't * remember myself or objects located in me, and don't remember * objects that cannot be moved. */ updateLocMem(obj) { /* We don't add myself or locationless objects to the table. */ if (obj == self || !obj.location) return; /* We remember only objects which are portable, except we do * remember Actors. */ if (!obj.ofKind(NonPortable) || obj.ofKind(Actor)) { locMemTab[obj] = obj.location; } /* We recurse through contents also. */ foreach(local child in obj.contents) { if (locMemTab[child] != obj) if (canSee(child)) updateLocMem(child); } } /* Forget an object (because we no longer know where it is). * Note that we don't forget its containment, since we still * remember objects located within it, even if we don't know * where it is. */ forgetLocMem(obj) { locMemTab.removeElement(obj); } ; modify Thing /* when a thing moves, update the memory table for witnesses to the * move. * further, if it's an actor that moves, update his memory table * for information available in the new location. */ notifyMoveInto(newContainer) { inherited(newContainer); memoryErase(); } notifyInsert(obj) { inherited(obj); obj.memoryUpdate(); } /* memoryErase: called immediately before baseMoveInto(). * * Each actor who can see the moving object forgets its memory of * the object's location. */ memoryErase() { for (local actor = firstObj(Actor) ; actor ; actor = nextObj(actor, Actor)) { if (actor.canSee(self)) actor.forgetLocMem(self); } } /* memoryUpdate: called immediately after baseMoveInto(). */ memoryUpdate() { /* Each actor who can see the object in its new location * remembers that object in its new location. */ for (local actor = firstObj(Actor) ; actor ; actor = nextObj(actor, Actor)) { if (actor.locMemTab[self] != self.location) if (actor.canSee(self)) actor.updateLocMem(self); } /* Finally, if it's an actor that's moving, we update its * location memory based on what new information is provided * by the new location. */ if (ofKind(Actor)) { locMemTab.forEachAssoc( new function(key, val) { /* Only perform this operation if locMemTab[key] * no longer has the correct value. */ if (val != key.location) { /* if we can see the key, update its * remembered location. */ if (canSee(key)) updateLocMem(key); /* if we cannot see the key, but we can see its * value (its remembered location), we may need * to remove the key from the table, since we * no longer know where it is. However, we only * update the memory if we can tell that our * current memory information is outdated. */ /* Note that this update fails in the rare case * that we can see the contents of val but * cannot see val itself. To make the code more * rigorous (and far more time cosuming), one * would delete the if statement in the * following line. */ else if (canSee(val)) { /* We need to figure out whether or not * the actor can detect that key is no * longer located in val. * * If the actor could see the object if it * were in val, the actor can tell that the * object is no longer in val. */ local curLoc = key.location; key.baseMoveInto(val); if (canSee(key)) forgetLocMem(key); key.baseMoveInto(curLoc); } } } ); } } ; /* the 'objects' verb provides the player a list of all movable objects * in the game, with their locations (last he remembers). */ DefineIAction(Objects) execAction() { gActor.locMemTab.forEachAssoc( new function(key, val) { /* We don't list objects in the actor who's issuing the * command. */ if (!key.isIn(gActor)) { /* first list the object (in the remembered * location). */ "<> (<> <>"; /* if val is non-portable, simply list its * outermost room. (We assume that non-portable * objects don't change their location.) */ if (val.ofKind(NonPortable) && val != val.getOutermostRoom) { val = val.getOutermostRoom; ", <> <>"; } else { local oml = val; /* list the last remembered outermost location * of val if it differs from val. */ while (gActor.locMemTab[oml]) oml = gActor.locMemTab[oml]; if (val != oml) { if (oml.ofKind(NonPortable)) oml = oml.getOutermostRoom; ", <> <>"; } } ")\n"; } } ); } ; VerbRule(Objects) 'objects' : ObjectsAction verbPhrase = 'objects/objectsing' ;