Files
scummvm-cursorfix/engines/glk/alan3/instance.cpp
2026-02-02 04:50:13 +01:00

1175 lines
33 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "glk/alan3/instance.h"
#include "glk/alan3/memory.h"
#include "glk/alan3/syserr.h"
#include "glk/alan3/attribute.h"
#include "glk/alan3/current.h"
#include "glk/alan3/container.h"
#include "glk/alan3/debug.h"
#include "glk/alan3/checkentry.h"
#include "glk/alan3/glkio.h"
#include "glk/alan3/inter.h"
#include "glk/alan3/options.h"
#include "glk/alan3/output.h"
#include "glk/alan3/class.h"
#include "glk/alan3/msg.h"
#include "glk/alan3/actor.h"
#include "glk/alan3/literal.h"
#include "glk/alan3/dictionary.h"
#include "glk/alan3/location.h"
#include "glk/alan3/compatibility.h"
namespace Glk {
namespace Alan3 {
/* PUBLIC DATA */
InstanceEntry *instances; /* Instance table pointer */
AdminEntry *admin; /* Administrative data about instances */
AttributeEntry *attributes; /* Dynamic attribute values */
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
void AdminEntry::synchronize(Common::Serializer &s) {
s.syncAsUint32LE(location);
Aword attr = 0;
s.syncAsUint32LE(attr);
s.syncAsUint32LE(alreadyDescribed);
s.syncAsUint32LE(visitsCount);
s.syncAsUint32LE(script);
s.syncAsUint32LE(step);
s.syncAsUint32LE(waitCount);
}
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/* Instance query methods */
/*======================================================================*/
bool isA(int instance, int ancestor) {
int parent;
if (isLiteral(instance))
parent = literals[instance - header->instanceMax]._class;
else
parent = instances[instance].parent;
while (parent != 0 && parent != ancestor)
parent = classes[parent].parent;
return (parent != 0);
}
bool isAObject(int instance) {
return isA(instance, OBJECT);
}
bool isAContainer(int instance) {
return instance != 0 && !isLiteral(instance) && instances[instance].container != 0;
}
bool isAActor(int instance) {
return isA(instance, ACTOR);
}
bool isALocation(int instance) {
return isA(instance, LOCATION);
}
bool isLiteral(int instance) {
return instance > (int)header->instanceMax;
}
bool isANumeric(int instance) {
return isLiteral(instance) && literals[literalFromInstance(instance)].type == NUMERIC_LITERAL;
}
bool isAString(int instance) {
return isLiteral(instance) && literals[literalFromInstance(instance)].type == STRING_LITERAL;
}
/*======================================================================*/
bool isOpaque(int container) {
return getInstanceAttribute(container, OPAQUEATTRIBUTE);
}
/*======================================================================*/
void setInstanceAttribute(int instance, int attribute, Aptr value) {
char str[80];
if (instance > 0 && instance <= (int)header->instanceMax) {
setAttribute(admin[instance].attributes, attribute, value);
if (isALocation(instance) && attribute != VISITSATTRIBUTE)
/* If it wasn't the VISITSATTRIBUTE the location may have
changed so describe next time */
admin[instance].visitsCount = 0;
} else {
Common::sprintf_s(str, "Can't SET/MAKE instance (%d).", instance);
syserr(str);
}
}
/*======================================================================*/
void setInstanceStringAttribute(int instance, int attribute, char *string) {
deallocate(fromAptr(getInstanceAttribute(instance, attribute)));
setInstanceAttribute(instance, attribute, toAptr(string));
}
/*======================================================================*/
void setInstanceSetAttribute(int instance, int attribute, Aptr set) {
freeSet((Set *)fromAptr(getInstanceAttribute(instance, attribute)));
setInstanceAttribute(instance, attribute, set);
}
/*----------------------------------------------------------------------*/
static Aptr literalAttribute(int literal, int attribute) {
if (isPreBeta3(header->version)) {
if (attribute == 1)
return literals[literalFromInstance(literal)].value;
else
return 0;
} else {
if (attribute == 0)
return literals[literalFromInstance(literal)].value;
else
return getAttribute(admin[header->instanceMax].attributes, attribute);
}
return (EOD);
}
/*======================================================================*/
Aptr getInstanceAttribute(int instance, int attribute) {
char str[80];
if (isLiteral(instance))
return literalAttribute(instance, attribute);
else {
if (instance > 0 && instance <= (int)header->instanceMax) {
if (attribute == -1)
return locationOf(instance);
else
return getAttribute(admin[instance].attributes, attribute);
} else {
Common::sprintf_s(str, "Can't ATTRIBUTE item (%d).", instance);
syserr(str);
}
}
return (EOD);
}
/*======================================================================*/
char *getInstanceStringAttribute(int instance, int attribute) {
return scumm_strdup((char *)fromAptr(getInstanceAttribute(instance, attribute)));
}
/*======================================================================*/
Set *getInstanceSetAttribute(int instance, int attribute) {
return copySet((Set *)fromAptr(getInstanceAttribute(instance, attribute)));
}
/*----------------------------------------------------------------------*/
static void verifyInstance(int instance, const char *action) {
char message[200];
if (instance == 0) {
Common::sprintf_s(message, "Can't %s instance (%d).", action, instance);
syserr(message);
} else if (instance > (int)header->instanceMax) {
Common::sprintf_s(message, "Can't %s instance (%d > instanceMax).", action, instance);
syserr(message);
}
}
/*======================================================================*/
bool isHere(int id, ATrans transitivity) {
verifyInstance(id, "HERE");
return isAt(id, current.location, transitivity);
}
/*======================================================================*/
bool isNearby(int instance, ATrans transitivity) {
verifyInstance(instance, "NEARBY");
if (isALocation(instance))
return exitto(current.location, instance);
else
return exitto(current.location, where(instance, transitivity));
}
/*======================================================================*/
bool isNear(int instance, int other, ATrans trans) {
Aint l1, l2;
verifyInstance(instance, "NEAR");
if (isALocation(instance))
l1 = instance;
else
l1 = where(instance, trans);
if (isALocation(other))
l2 = other;
else
l2 = where(other, trans);
return exitto(l2, l1);
}
/*======================================================================*/
/* Look in a container to see if the instance is in it. */
bool isIn(int instance, int container, ATrans trans) {
int loc;
if (!isAContainer(container))
syserr("IN in a non-container.");
if (trans == DIRECT)
return admin[instance].location == container;
else {
loc = admin[instance].location;
if (trans == INDIRECT && loc != 0 && !isA(loc, LOCATION))
loc = admin[loc].location;
while (loc != 0 && !isA(loc, LOCATION))
if (loc == container)
return TRUE;
else
loc = admin[loc].location;
return FALSE;
}
}
/*======================================================================*/
/* Look see if an instance is AT another. */
bool isAt(int instance, int other, ATrans trans) {
if (instance == 0 || other == 0) return FALSE;
if (isALocation(instance)) {
/* Nested locations */
/* TODO - What if the other is not a location? */
int curr = admin[instance].location;
switch (trans) {
case DIRECT:
return admin[instance].location == other;
case INDIRECT:
if (curr == other)
return FALSE;
curr = admin[curr].location;
// fall through
case TRANSITIVE:
while (curr != 0) {
if (curr == other)
return TRUE;
else
curr = admin[curr].location;
}
return FALSE;
default:
break;
}
syserr("Unexpected value in switch in isAt() for location");
return FALSE;
} else if (isALocation(other)) {
/* Instance is not a location but other is */
switch (trans) {
case DIRECT:
return admin[instance].location == other;
case INDIRECT:
if (admin[instance].location == other)
return FALSE; /* Directly, so not Indirectly */
// fall through
case TRANSITIVE: {
int location = locationOf(instance);
int curr = other;
while (curr != 0) {
if (curr == location)
return TRUE;
else
curr = admin[curr].location;
}
return FALSE;
}
default:
break;
}
syserr("Unexpected value in switch in isAt() for non-location");
return FALSE;
} else {
/* Other is also not a location */
switch (trans) {
case DIRECT:
return positionOf(instance) == admin[other].location;
case INDIRECT: {
int location = locationOf(instance);
int curr = other;
if (location == curr)
return FALSE;
else
curr = admin[curr].location;
while (curr != 0) {
if (curr == location)
return TRUE;
else
curr = admin[curr].location;
}
return FALSE;
}
case TRANSITIVE: {
int location = locationOf(other);
int curr = locationOf(instance);
bool ok = FALSE;
while (curr != 0 && !ok) {
if (curr == location)
ok = TRUE;
else
curr = admin[curr].location;
}
return ok;
}
default:
break;
}
syserr("Unexpected value in switch in isAt() for non-location");
return FALSE;
}
}
/*======================================================================*/
/* Return the *location* of an instance, transitively, i.e. the first
location instance found when traversing the containment/position
links. If that didn't turn up a location see if it was in a
container that is somewhere, or a THING that is nowhere. It might
also be an ENTITY which is always everywhere so take that to mean
where the hero is. */
int locationOf(int instance) {
int position;
int container = 0;
verifyInstance(instance, "get LOCATION of");
position = admin[instance].location;
while (position != 0 && !isALocation(position)) {
container = position; /* Remember innermost container */
position = admin[position].location;
}
if (position > NOWHERE) /* It was a location so return that */
return position;
else {
/* If we did not find a location then it might be in a container */
if (container != 0)
instance = container;
/* If the instance or the container it was in is a THING then its nowhere. */
if (isA(instance, THING))
return NOWHERE; /* #nowhere */
else if (isALocation(instance))
return NO_LOCATION; /* No location */
else
return locationOf(HERO);
}
}
/*======================================================================*/
/* Return the current position of an instance, directly or not */
/* TODO: this will be a possible duplicate of where() */
int positionOf(int instance) {
return admin[instance].location;
}
/*======================================================================*/
/* Return the current position of an instance, directly or not */
int where(int instance, ATrans trans) {
verifyInstance(instance, "WHERE");
if (isALocation(instance))
return 0;
else if (trans == DIRECT)
return admin[instance].location;
else
return locationOf(instance);
}
/*----------------------------------------------------------------------*/
static bool executeInheritedMentioned(CONTEXT, int cls) {
if (cls == 0) return FALSE;
if (classes[cls].mentioned) {
R0CALL1(interpret, classes[cls].mentioned)
return TRUE;
} else {
bool flag;
R0FUNC1(executeInheritedMentioned, flag, classes[cls].parent)
return flag;
}
}
/*----------------------------------------------------------------------*/
static bool mention(CONTEXT, int instance) {
if (instances[instance].mentioned) {
R0CALL1(interpret, instances[instance].mentioned)
return TRUE;
} else {
bool flag;
R0FUNC1(executeInheritedMentioned, flag, instances[instance].parent)
return flag;
}
}
/*======================================================================*/
void sayInstance(CONTEXT, int instance) {
#ifdef SAY_INSTANCE_WITH_PLAYER_WORDS_IF_PARAMETER
int p, i;
/* Find the id in the parameters... */
if (params != NULL)
for (p = 0; params[p].code != EOD; p++)
if (params[p].code == instance) {
/* Found it so.. */
if (params[p].firstWord == EOD) /* Any words he used? */
break; /* No... */
else { /* Yes, so use them... */
char *capitalized;
/* Assuming the noun is the last word we can simply output the adjectives... */
for (i = params[p].firstWord; i <= params[p].lastWord - 1; i++)
output((char *)pointerTo(dict[wrds[i]].wrd));
/* ... and then the noun, capitalized if necessary */
if (header->capitalizeNouns) {
capitalized = scumm_strdup((char *)pointerTo(dict[wrds[params[p].lastWord]].wrd));
capitalized[0] = IsoToUpperCase(capitalized[0]);
output(capitalized);
deallocate(capitalized);
} else
output((char *)pointerTo(dict[wrds[params[p].lastWord]].wrd));
}
return;
}
#endif
bool flag;
FUNC1(mention, flag, instance)
if (!flag)
CALL1(interpret, instances[instance].name)
}
/*======================================================================*/
void sayInteger(int value) {
char buf[25];
if (isHere(HERO, /*FALSE*/ TRANSITIVE)) {
Common::sprintf_s(buf, "%d", value);
output(buf);
}
}
/*======================================================================*/
void sayString(char *string) {
if (isHere(HERO, /*FALSE*/ TRANSITIVE))
output(string);
deallocate(string);
}
/*----------------------------------------------------------------------*/
static void sayLiteral(int literal) {
char *str;
if (isANumeric(literal))
sayInteger(literals[literal - header->instanceMax].value);
else {
str = (char *)scumm_strdup((char *)fromAptr(literals[literal - header->instanceMax].value));
sayString(str);
}
}
/*----------------------------------------------------------------------*/
static char *wordWithCode(int classBit, int code) {
int w;
char str[50];
for (w = 0; w < dictionarySize; w++)
if (dictionary[w].code == (Aword)code && ((classBit & dictionary[w].classBits) != 0))
return (char *)pointerTo(dictionary[w].string);
Common::sprintf_s(str, "Could not find word of class %d with code %d.", classBit, code);
syserr(str);
return nullptr;
}
/*----------------------------------------------------------------------*/
static bool sayInheritedDefiniteForm(CONTEXT, int cls) {
if (cls == 0) {
syserr("No default definite article");
return FALSE;
} else {
if (classes[cls].definite.address) {
R0CALL1(interpret, classes[cls].definite.address)
return classes[cls].definite.isForm;
} else {
bool flag;
R0FUNC1(sayInheritedDefiniteForm, flag, classes[cls].parent)
return flag;
}
}
}
/*----------------------------------------------------------------------*/
static void sayDefinite(CONTEXT, int instance) {
if (instances[instance].definite.address) {
CALL1(interpret, instances[instance].definite.address)
if (!instances[instance].definite.isForm)
CALL1(sayInstance, instance)
} else {
bool flag;
FUNC1(sayInheritedDefiniteForm, flag, instances[instance].parent)
if (!flag)
CALL1(sayInstance, instance)
}
}
/*----------------------------------------------------------------------*/
static bool sayInheritedIndefiniteForm(CONTEXT, int cls) {
if (cls == 0) {
syserr("No default indefinite article");
return FALSE;
} else {
if (classes[cls].indefinite.address) {
R0CALL1(interpret, classes[cls].indefinite.address)
return classes[cls].indefinite.isForm;
} else {
bool flag;
R0FUNC1(sayInheritedIndefiniteForm, flag, classes[cls].parent)
return flag;
}
}
}
/*----------------------------------------------------------------------*/
static void sayIndefinite(CONTEXT, int instance) {
if (instances[instance].indefinite.address) {
CALL1(interpret, instances[instance].indefinite.address)
if (!instances[instance].indefinite.isForm)
CALL1(sayInstance, instance)
} else {
bool flag;
FUNC1(sayInheritedIndefiniteForm, flag, instances[instance].parent)
if (!flag)
CALL1(sayInstance, instance)
}
}
/*----------------------------------------------------------------------*/
static bool sayInheritedNegativeForm(CONTEXT, int cls) {
if (cls == 0) {
syserr("No default negative form");
return FALSE;
} else {
if (classes[cls].negative.address) {
R0CALL1(interpret, classes[cls].negative.address)
return classes[cls].negative.isForm;
} else {
bool flag;
R0FUNC1(sayInheritedNegativeForm, flag, classes[cls].parent)
return flag;
}
}
}
/*----------------------------------------------------------------------*/
static void sayNegative(CONTEXT, int instance) {
if (instances[instance].negative.address) {
CALL1(interpret, instances[instance].negative.address)
if (!instances[instance].negative.isForm)
CALL1(sayInstance, instance)
} else {
bool flag;
FUNC1(sayInheritedNegativeForm, flag, instances[instance].parent)
if (!flag)
CALL1(sayInstance, instance)
}
}
/*----------------------------------------------------------------------*/
static void sayInheritedPronoun(CONTEXT, int instance) {
if (instance == 0)
syserr("No default pronoun");
else {
if (classes[instance].pronoun != 0)
output(wordWithCode(PRONOUN_BIT, classes[instance].pronoun));
else
CALL1(sayInheritedPronoun, classes[instance].parent)
}
}
/*----------------------------------------------------------------------*/
static void sayPronoun(CONTEXT, int instance) {
if (instances[instance].pronoun != 0)
output(wordWithCode(PRONOUN_BIT, instances[instance].pronoun));
else
CALL1(sayInheritedPronoun, instances[instance].parent)
}
/*----------------------------------------------------------------------*/
static void sayArticleOrForm(CONTEXT, int id, SayForm form) {
if (!isLiteral(id)) {
switch (form) {
case SAY_DEFINITE:
CALL1(sayDefinite, id)
break;
case SAY_INDEFINITE:
CALL1(sayIndefinite, id)
break;
case SAY_NEGATIVE:
CALL1(sayNegative, id)
break;
case SAY_PRONOUN:
CALL1(sayPronoun, id)
break;
case SAY_SIMPLE:
CALL1(say, id)
break;
default:
syserr("Unexpected form in 'sayArticleOrForm()'");
}
} else {
CALL1(say, id)
}
}
/*======================================================================*/
void say(CONTEXT, int instance) {
Aword previousInstance = current.instance;
current.instance = instance;
if (isHere(HERO, /*FALSE*/ TRANSITIVE)) {
if (isLiteral(instance))
sayLiteral(instance);
else {
verifyInstance(instance, "SAY");
sayInstance(context, instance);
}
}
current.instance = previousInstance;
}
/*======================================================================*/
void sayForm(CONTEXT, int instance, SayForm form) {
Aword previousInstance = current.instance;
current.instance = instance;
sayArticleOrForm(context, instance, form);
current.instance = previousInstance;
}
/*======================================================================*/
bool isDescribable(int instance) {
return isAObject(instance) || isAActor(instance);
}
/*----------------------------------------------------------------------*/
static bool inheritsDescriptionFrom(int cls) {
if (classes[cls].description != 0)
return TRUE;
else if (classes[cls].parent != 0)
return inheritsDescriptionFrom(classes[cls].parent);
else
return FALSE;
}
/*======================================================================*/
bool hasDescription(int instance) {
if (instances[instance].description != 0)
return TRUE;
else if (instances[instance].parent != 0)
return inheritsDescriptionFrom(instances[instance].parent);
else
return FALSE;
}
/*----------------------------------------------------------------------*/
static void describeClass(CONTEXT, int instance) {
if (classes[instance].description != 0) {
/* This class has a description, run it */
CALL1(interpret, classes[instance].description)
} else {
/* Search up the inheritance tree, if any, to find a description */
if (classes[instance].parent != 0)
CALL1(describeClass, classes[instance].parent)
}
}
/*======================================================================*/
void describeAnything(CONTEXT, int instance) {
if (instances[instance].description != 0) {
/* This instance has its own description, run it */
CALL1(interpret, instances[instance].description)
} else {
/* Search up the inheritance tree to find a description */
if (instances[instance].parent != 0)
CALL1(describeClass, instances[instance].parent)
}
admin[instance].alreadyDescribed = TRUE;
}
/*----------------------------------------------------------------------*/
static void describeObject(CONTEXT, int object) {
if (hasDescription(object)) {
CALL1(describeAnything, object)
} else {
printMessageWithInstanceParameter(M_SEE_START, object);
printMessage(M_SEE_END);
if (instances[object].container != 0)
CALL1(describeContainer, object)
}
admin[object].alreadyDescribed = TRUE;
}
/*----------------------------------------------------------------------*/
static bool inheritedDescriptionCheck(CONTEXT, int cls) {
if (cls == 0) return TRUE;
bool flag;
R0FUNC1(inheritedDescriptionCheck, flag, classes[cls].parent)
if (!flag) return FALSE;
if (classes[cls].descriptionChecks == 0) return TRUE;
R0FUNC2(checksFailed, flag, classes[cls].descriptionChecks, TRUE)
return !flag;
}
/*----------------------------------------------------------------------*/
static bool descriptionCheck(CONTEXT, int instance) {
int previousInstance = current.instance;
bool r;
current.instance = instance;
if (inheritedDescriptionCheck(context, instances[instance].parent)) {
if (instances[instance].checks == 0)
r = TRUE;
else
r = !checksFailed(context, instances[instance].checks, TRUE);
}
else
r = FALSE;
current.instance = previousInstance;
return r;
}
/*======================================================================*/
void describeInstances(CONTEXT) {
uint i;
int lastInstanceFound = 0;
int found = 0;
/* First describe every object here with its own description */
for (i = 1; i <= header->instanceMax; i++)
if (admin[i].location == current.location && isAObject(i) &&
!admin[i].alreadyDescribed && hasDescription(i))
CALL1(describe, i)
/* Then list all things without a description */
for (i = 1; i <= header->instanceMax; i++)
if (admin[i].location == current.location
&& !admin[i].alreadyDescribed
&& isAObject(i)
&& descriptionCheck(context, i)) {
if (found == 0)
printMessageWithInstanceParameter(M_SEE_START, i);
else if (found > 1)
printMessageWithInstanceParameter(M_SEE_COMMA, lastInstanceFound);
admin[i].alreadyDescribed = TRUE;
// TODO : isOpaque()
if (instances[i].container && containerSize(i, DIRECT) > 0 && !getInstanceAttribute(i, OPAQUEATTRIBUTE)) {
if (found > 0)
printMessageWithInstanceParameter(M_SEE_AND, i);
printMessage(M_SEE_END);
CALL1(describeContainer, i)
found = 0;
continue; /* Actually start another list. */
}
found++;
lastInstanceFound = i;
}
if (found > 0) {
if (found > 1) {
printMessageWithInstanceParameter(M_SEE_AND, lastInstanceFound);
}
printMessage(M_SEE_END);
}
/* Finally all actors with a separate description */
for (i = 1; i <= header->instanceMax; i++)
if (admin[i].location == current.location && i != HERO && isAActor(i)
&& !admin[i].alreadyDescribed)
CALL1(describe, i)
/* Clear the describe flag for all instances */
for (i = 1; i <= header->instanceMax; i++)
admin[i].alreadyDescribed = FALSE;
}
/*======================================================================*/
bool describe(CONTEXT, int instance) {
bool descriptionOk;
int previousInstance = current.instance;
current.instance = instance;
verifyInstance(instance, "DESCRIBE");
if (descriptionCheck(context, instance)) {
descriptionOk = TRUE;
if (isAObject(instance)) {
describeObject(context, instance);
} else if (isAActor(instance)) {
describeActor(context, instance);
} else
describeAnything(context, instance);
} else
descriptionOk = FALSE;
current.instance = previousInstance;
return descriptionOk;
}
/*----------------------------------------------------------------------*/
static void locateIntoContainer(CONTEXT, Aword theInstance, Aword theContainer) {
if (!isA(theInstance, containers[instances[theContainer].container]._class))
printMessageUsing2InstanceParameters(M_CANNOTCONTAIN, theContainer, theInstance);
else if (passesContainerLimits(context, theContainer, theInstance))
admin[theInstance].location = theContainer;
else
abortPlayerCommand(context);
}
/*----------------------------------------------------------------------*/
static void locateLocation(Aword loc, Aword whr) {
Aint l = whr;
/* Ensure this does not create a recursive location chain */
while (l != 0) {
if (admin[l].location == (int)loc)
apperr("Locating a location that would create a recursive loop of locations containing each other.");
else
l = admin[l].location;
}
admin[loc].location = whr;
}
/*----------------------------------------------------------------------*/
static void locateObject(CONTEXT, Aword obj, Aword whr) {
if (isAContainer(whr)) { /* Into a container */
locateIntoContainer(context, obj, whr);
} else {
admin[obj].location = whr;
/* Make sure the location is described since it's changed */
admin[whr].visitsCount = 0;
}
}
/*----------------------------------------------------------------------*/
static void traceEnteredClass(Aint theClass, bool empty) {
printf("\n<ENTERED in class ");
printf("%s", idOfClass(theClass));
printf("[%d]%s>\n", theClass, empty ? " is empty" : ":");
}
/*----------------------------------------------------------------------*/
static void traceEnteredInstance(CONTEXT, Aint instance, bool empty) {
printf("\n<ENTERED in instance ");
traceSay(context, instance);
printf("[%d]%s>\n", instance, empty ? " is empty" : "");
}
/*----------------------------------------------------------------------*/
static void executeInheritedEntered(CONTEXT, Aint theClass) {
if (theClass == 0) return;
CALL1(executeInheritedEntered, classes[theClass].parent)
if (traceSectionOption)
traceEnteredClass(theClass, classes[theClass].entered == 0);
if (classes[theClass].entered) {
CALL1(interpret, classes[theClass].entered)
}
}
/*----------------------------------------------------------------------*/
static void executeEntered(CONTEXT, Aint instance) {
int currentInstance = current.instance;
current.instance = instance;
if (admin[instance].location != 0)
CALL1(executeEntered, admin[instance].location)
CALL1(executeInheritedEntered, instances[instance].parent)
if (traceSectionOption)
CALL2(traceEnteredInstance, instance, instances[instance].entered == 0)
if (instances[instance].entered != 0) {
CALL1(interpret, instances[instance].entered)
}
current.instance = currentInstance;
}
/*----------------------------------------------------------------------*/
static int getVisits(int location) {
return getInstanceAttribute(location, VISITSATTRIBUTE);
}
/*----------------------------------------------------------------------*/
static void incrementVisits(int location) {
setInstanceAttribute(location, VISITSATTRIBUTE, getVisits(location) + 1);
if (admin[location].location != 0)
/* Nested location, so increment that too */
incrementVisits(admin[location].location);
}
/*----------------------------------------------------------------------*/
static void revisited(CONTEXT) {
if (anyOutput)
para();
CALL1(say, where(HERO, DIRECT))
printMessage(M_AGAIN);
newline();
CALL0(describeInstances)
}
/*----------------------------------------------------------------------*/
static bool shouldBeDescribed(void) {
if (!isPreBeta5(header->version))
return getVisits(admin[HERO].location) % (current.visits + 1) == 0
|| admin[admin[HERO].location].visitsCount == 0;
else
return admin[admin[HERO].location].visitsCount % (current.visits + 1) == 0;
}
/*----------------------------------------------------------------------*/
static void locateActor(CONTEXT, Aint movingActor, Aint whr) {
Aint previousCurrentLocation = current.location;
Aint previousActorLocation = admin[movingActor].location;
Aint previousActor = current.actor;
Aint previousInstance = current.instance;
/* Before leaving, remember that we visited the location */
if (!isPreBeta5(header->version))
if (movingActor == (int)HERO)
incrementVisits(where(HERO, DIRECT));
/* TODO Actors locating into containers is dubious, anyway as it
is now it allows the hero to be located into a container. And what
happens with current location if so... */
if (isAContainer(whr))
CALL2(locateIntoContainer, movingActor, whr)
else {
current.location = whr;
admin[movingActor].location = whr;
}
/* Now we have moved, so show what is needed... */
current.instance = current.location;
/* Execute possible entered */
current.actor = movingActor;
if (previousActorLocation != current.location) {
CALL1(executeEntered, current.location)
}
current.instance = previousInstance;
current.actor = previousActor;
if (movingActor == (int)HERO) {
if (shouldBeDescribed()) {
CALL0(look)
} else {
CALL0(revisited)
}
admin[where(HERO, DIRECT)].visitsCount++;
} else
/* Ensure that the location will be described to the hero next time */
admin[whr].visitsCount = 0;
if (current.actor != movingActor)
current.location = previousCurrentLocation;
current.instance = previousInstance;
}
/*----------------------------------------------------------------------*/
static void traceExtract(CONTEXT, int instance, int containerId, const char *what) {
if (traceSectionOption) {
printf("\n<EXTRACT from ");
traceSay(context, instance);
printf("[%d, container %d], %s:>\n", instance, containerId, what);
}
}
/*----------------------------------------------------------------------*/
static void containmentLoopError(CONTEXT, int instance, int whr) {
ParameterArray parameters = newParameterArray();
if (isPreBeta4(header->version))
output("That would be to put something inside itself.");
else if (whr == instance) {
addParameterForInstance(parameters, instance);
printMessageWithParameters(M_CONTAINMENT_LOOP, parameters);
} else {
addParameterForInstance(parameters, instance);
addParameterForInstance(parameters, whr);
printMessageWithParameters(M_CONTAINMENT_LOOP2, parameters);
}
free(parameters);
CALL1(error, NO_MSG)
}
/*----------------------------------------------------------------------*/
static void runExtractStatements(CONTEXT, int instance, int containerId) {
ContainerEntry *theContainer = &containers[containerId];
if (theContainer->extractStatements != 0) {
CALL3(traceExtract, instance, containerId, "Executing")
CALL1(interpret, theContainer->extractStatements)
}
}
/*----------------------------------------------------------------------*/
static bool runExtractChecks(CONTEXT, int instance, int containerId) {
ContainerEntry *theContainer = &containers[containerId];
if (theContainer->extractChecks != 0) {
R0CALL3(traceExtract, instance, containerId, "Checking")
if (checksFailed(context, theContainer->extractChecks, EXECUTE_CHECK_BODY_ON_FAIL)) {
fail = TRUE;
return FALSE; /* Failed! */
}
}
return TRUE; /* Passed! */
}
/*======================================================================*/
void locate(CONTEXT, int instance, int whr) {
int containerId;
int previousInstance = current.instance;
verifyInstance(instance, "LOCATE");
verifyInstance(whr, "LOCATE AT");
/* Will this create a containment loop? */
if (whr == instance || (isAContainer(instance) && isIn(whr, instance, TRANSITIVE)))
CALL2(containmentLoopError, instance, whr)
/* First check if the instance is in a container, if so run extract checks */
if (isAContainer(admin[instance].location)) { /* In something? */
int loc = admin[instance].location;
/* Run all nested extraction checks */
while (isAContainer(loc)) {
current.instance = loc;
containerId = instances[loc].container;
if (!runExtractChecks(context, instance, containerId)) {
current.instance = previousInstance;
return;
}
runExtractStatements(context, instance, containerId);
loc = admin[loc].location;
}
current.instance = previousInstance;
}
if (isAActor(instance)) {
CALL2(locateActor, instance, whr)
} else if (isALocation(instance)) {
locateLocation(instance, whr);
} else {
CALL2(locateObject, instance, whr)
}
gameStateChanged = TRUE;
}
} // End of namespace Alan3
} // End of namespace Glk