#ifndef BUGFIX
#define BUGFIX
#pragma C+
/*
** Bugs.t -- bug fixes for TADS 2.2
**
** Please note that this module is intended to fix library bugs, not
** actual bugs within the TADS runtime itself.
**
** {BUGS FIXED}
** thing.cantReach() no longer quits w/o printing an error message if the
** player is not in a nestedroom. This was a problem if contentsReachable
** was hand-set to nil. cantReach() now also checks to see if the actor
** passed to it is _not_ an actor, in which case it prints a generic
** message.
** thing.doUnboard() no longer prints "Okay, you're no longer in xxx"
** whether or not you're actually IN xxx. By default, an object's
** statusPrep is "on", so doUnboard() now prints "Okay, you're no longer
** on xxx" unless you change statusPrep deliberately. This fixes a
** related bug with theFloor.
** thing.verDoTakeOut() now checks whether or not you can actually take
** the item.
** lockable.verDoUnlockWith() now checks to see if the door requires a key to
** be unlocked.
** Throwing items at the floor no longer results in the response "You
** miss."
** Looking at the floor no longer gives the default response "There's
** nothing on the floor" whether or not there is something in the room.
** My solution to this was to prevent any description of the floor's
** "contents" at all.
** vehicle messages now take statusPrep & outOfPrep into account.
** showcontcont() now checks for qsurfaces correctly.
** distantItems now allow you to ask or tell an actor about them without
** the odd response, "It is too far away."
** container, surface, transpaentItem, and openable have had doSearch added,
** so that searching them does not result in "You find nothing of interest."
** The qsurface class was left out of adv.t. I've added it back in.
** standVerb.action() now checks verDoUnboard() before calling doUnboard()
** thing.circularMessage() always assumed you were putting something _in_
** something else, despite the fact that circularMessage can be called
** from doPutOn.
** lightsource.doTurnon() now sets islit in the item _as well as_ isActive.
** reachable added to movableActor so that you can command objects which are
** in your inventory. This requires a change to visibleList: see below
** visibleList() now takes two arguments. This is to handle an obscure bug
** which occurs when the player commands an object in his/her inventory
** to do something to another object in his/her inventory. The change in
** visibleList() required a cosmetic change in deepverb.validDoList().
** dirPrep now has the abbreviations 'n' for 'north', etc.
**
**
** {BUGS TOYED WITH}
** There seems to be problems with the random number generator. To
** try to avoid such problems, a new function RAND is added.
**
**
** {BUGS REQUIRING WORKAROUNDS}
** If you modify basicMe, you must set noun='', like so:
** modify basicMe {
** noun = ''
** [your code here]
** If you do not do this, then all previous nouns by which basicMe was known
** will vanish.
** There is a problem with using firstobj()/lastobj() in preinit(): if you do
** so, you will get objects TWICE instead of once.
**
**
** This collection of bug fixes was compiled by Stephen Granade. It
** is public domain--do whatever you like with it. If you know of any
** other library bugs in TADS, please contact me for their inclusion in this
** module. I can be reached at
[email protected] or
[email protected].
**
** Credits:
** Unbeknownst to them, Neil deMause, Magnus Olsson, Andrew Pontious, and
** Patrick Kellum contributed to this file. Beknownst to him, Cody Sandifer
** reported the doSearch/lookIn bugs in container, surface, transparentItem,
** and openable; found the lightsource.islit problem; uncovered a bug when
** the player commanded an object in his/her inventory; and pointed out that
** qsurface had gone missing from adv.t.
**
** Version history:
** 2 Aug 96 -- Initial release
** 22 Aug 96 -- lockable bug fix added
** 2 Sep 96 -- cantReach bug fix added
** 20 Sep 96 -- More changes to theFloor, suggested by Magnus Olssen
** 22 Nov 96 -- Patch applied to vehicle, making it use outOfPrep
** 15 Jan 97 -- basicMe modification bug mentioned in docs
** 2 Mar 97 -- container, surface, transparentItem, and openable bugs fixed
** 12 Mar 97 -- v1.4 standVerb bug fix added
** 4 Apr 97 -- v1.5 modification to undo removed, as it was unnecessary
** 15 Apr 97 -- v1.6 thing.circularMessage fix
** 22 Jul 97 -- v1.7 lightsource.doTurnon/islit fix; inanimate object
** command fix
** 19 Jan 98 -- v1.8 dirPrep fix
**
** Current version: $Id: bugs.t,v 1.8 1998/01/20 01:15:59 sgranade Exp $
*/
modify thing
statusPrep = "on" // So that everything has a preposition
replace cantReach(actor) = {
// This first line is to handle the (slightly bizarre) case of the
// player commanding an inanimate object
if (!actor.isactor) {
"\^<<actor.thedesc>> <<actor.isThem ? "do" : "does">> not
respond. ";
return;
}
if (self.location == nil) {
if (actor.location.location)
"%You% can't reach that from <<actor.location.thedesc>>. ";
else "%You% can't reach that. "; // Added error message
return;
}
if (!self.location.isopenable || self.location.isopen)
self.location.cantReach(actor);
else "%You%'ll have to open <<self.location.thedesc>> first. ";
}
replace verDoUnboard(actor) = {
if (actor.location != self) {
"%You're% not <<self.statusPrep>> <<self.thedesc>>! ";
}
else if (self.location == nil) {
"%You% can't leave <<self.thedesc>>! ";
}
}
replace doUnboard(actor) = {
// Trivia: this is the only place "fastenitem" is referred to in adv.t
if (self.fastenitem) {
"%You%'ll have to unfasten <<actor.location.fastenitem.thedesc
>> first. ";
}
else {
"Okay, %you're% no longer <<self.statusPrep>> <<self.thedesc>>. ";
self.leaveRoom(actor);
actor.moveInto(self.location);
}
}
replace verDoTakeOut(actor, io) = {
if (io != nil && !self.isIn(io))
"\^<<self.thedesc>> isn't in <<io.thedesc>>. ";
else self.verDoTake(actor); // Make sure the obj can be taken
}
replace circularMessage(io) = {
local cont, prep;
// prep is set to 'on' if io.location is a surface, 'in' otherwise
prep = (io.location.issurface) ? 'on' : 'in';
"%You% can't put <<thedesc>> <<prep>> <<io.thedesc>>, because <<
io.thedesc>> <<io.isThem ? "are" : "is">> <<
io.location == self ? "already" : "">> <<prep>> <<
io.location.thedesc>>";
for (cont = io.location; cont != self; cont = cont.location) {
", which <<cont.isThem ? "are" : "is">> <<
cont.location == self ? "already" : "">> <<prep>> <<
cont.location.thedesc>>";
}
". ";
}
;
// lockable modified so that trying to unlock a lockable with a key when one
// isn't needed (i.e. mykey=nil) results in an error message
modify lockable
replace verDoUnlockWith(actor, io) = {
if (!self.islocked)
"<<self.isThem ? "They're" : "It's">> not locked! ";
else if (self.mykey == nil)
"%You% %do%n't need anything to unlock <<self.isThem ? "them" :
"it">>. ";
}
;
// theFloor modified so that "throw xxx at floor" doesn't result in
// "you miss," which seems rather silly. Its ldesc is also modified to
// prevent "There is nothing on the floor" messages
modify theFloor
ldesc = "It lies beneath you. "
ioThrowAt(actor, dobj) = {
"Thrown. ";
dobj.moveInto(actor.location);
}
;
// vehicle modified so that messages now take statusPrep & outOfPrep into
// account
modify vehicle
verDoBoard(actor) = {
if (actor.location == self)
"%You're% already <<self.statusPrep>> <<self.thedesc>>! ";
else if (actor.isCarrying(self))
"%You%'ll have to drop <<self.thedesc>> first! ";
}
doBoard(actor) = {
"Okay, %you're% now <<self.statusPrep>> <<self.thedesc>>. ";
actor.moveInto(self);
}
noexit = {
"%You're% not going anywhere until %you% get%s% <<self.outOfPrep>> <<
self.thedesc>>. ";
return (nil);
}
dobjGen(a, v, i, p) = {
if (a.isIn(self) && v != inspectVerb && v != getOutVerb &&
v != outVerb) {
"%You%'ll have to get <<self.outOfPrep>> <<self.thedesc>> first. ";
exit;
}
}
iobjGen(a, v, d, p) = {
if (a.isIn(self) && v != putVerb) {
"%You%'ll have to get <<self.outOfPrep>> <<self.thedesc>> first. ";
exit;
}
}
;
// container modified so doSearch() doesn't return "You find nothing of
// interest." doLookin() has also been decoupled from ldesc.
modify container
verDoSearch(actor) = {}
doSearch(actor) = {
if (self.contentsVisible && itemcnt(self.contents) != 0)
"In <<self.thedesc>> %you% see%s% <<listcont(self)>>. ";
else "There's nothing in <<self.thedesc>>. ";
}
doLookin(actor) = { self.doSearch(actor); }
;
// transparentItem also modified so doSearch() doesn't return "You find
// nothing of interest." doLookin() has also been decoupled from ldesc.
modify transparentItem
verDoSearch(actor) = {}
doSearch(actor) = {
if (self.contentsVisible && itemcnt(self.contents) != 0)
"In <<self.thedesc>> %you% see%s% <<listcont(self)>>. ";
else "There's nothing in <<self.thedesc>>. ";
}
doLookin(actor) = { self.doSearch(actor); }
;
// openable modified so doSearch() doesn't return "You find nothing of
// interest." Also the verDoSearch & verDoLookin functions take into account
// whether or not the openable is opened.
modify openable
verDoSearch(actor) = {
if (!self.isopen && !isclass(self, transparentItem))
"It's closed. ";
}
verDoLookin(actor) = { self.verDoSearch(actor); }
;
// surface modified so doSearch() doesn't return "You find nothing of
// interest."
modify surface
verDoSearch(actor) = {}
doSearch(actor) = {
if (itemcnt(self.contents) != 0)
"On <<self.thedesc>> %you% see%s% <<listcont(self)>>. ";
else "There's nothing in <<self.thedesc>>. ";
}
;
modify standVerb
outhideStatus = 0
action(actor) = {
if (actor.location == nil || actor.location.location == nil)
"%You're% already standing! ";
else {
self.outhideStatus = outhide(true);
actor.location.verDoUnboard(actor);
if (outhide(self.outhideStatus))
actor.location.verDoUnboard(actor);
else
actor.location.doUnboard(actor);
}
}
;
replace showcontcont: function(obj)
{
if (itemcnt(obj.contents)) {
if (obj.issurface) {
if (!obj.isqsurface) {
"Sitting on <<obj.thedesc>> is <<listcont(obj)>>. ";
}
}
else if (obj.contentsVisible && !obj.isqcontainer) {
"\^<<obj.thedesc>> seems to contain <<listcont(obj)>>. ";
}
}
if (obj.contentsVisible && !obj.isqcontainer)
listfixedcontcont( obj );
}
modify distantItem
dobjGen(a, v, i, p) = {
if (v != askVerb && v != tellVerb)
pass dobjGen;
}
;
// Fix problems w/lightsource
modify lightsource
islit = nil
doTurnon(actor) = {
local waslit = actor.location.islit;
// turn on the light
self.isActive = true;
self.islit = true; // This should set things to rights
"You switch on <<self.thedesc>>";
// if the room wasn't previously lit, and it is now, describe it
if (actor.location.islit && !waslit) {
", lighting the area.\b";
actor.location.enterRoom(actor);
}
else ".";
}
;
// Add reachable to movableActor so you can address things in your inventory
modify movableActor
reachable = []
;
// Add qsurface back in to adv.t.
class qsurface: surface
isqsurface = true
;
// Add 'n' etc. to dirPrep
modify dirPrep
preposition = 'n' 's' 'e' 'w' 'u' 'd'
;
// visibleList now takes two arguments: the object whose visible contents are
// being returned and the actor who is performing the verb whose validDoList
// called visibleList. This is for two reasons: one, so we don't double-count
// the actor's contents; two, so we can take the player's contents into
// account in the case that the player isn't the actor.
replace visibleList: function(obj, actor)
{
local ret = [];
local i, lst, len;
// Don't look in "nil" objects
if (obj == nil) return ret;
if (!isclass(obj, openable)
|| (isclass(obj, openable) && obj.isopen)
|| obj.contentsVisible)
{
lst = obj.contents;
len = length(lst);
ret += lst;
for (i = 1 ; i <= len ; ++i) {
if (lst[i] != actor) // Don't recurse into the actor
ret += visibleList(lst[i], actor);
}
// Since the "Me" object never shows up in room.contents, if Me is
// located in obj and actor != Me, add Me.contents to the list
if (actor != Me && Me.location == obj)
ret += visibleList(Me, actor);
}
return(ret);
}
// deepverb.validDoList changed to use new arguments to visibleList
modify deepverb
validDoList(actor, prep, iobj) = {
local ret;
local loc;
loc = actor.location;
while (loc.location) loc = loc.location;
ret = visibleList(actor, actor) + visibleList(loc, actor)
+ global.floatingList;
return(ret);
}
;
RAND: function(x)
{
return (((rand(16*x)-1)/16)+1);
}
#endif