2018-10-15 21:47:27 -07:00
|
|
|
/* 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 2
|
|
|
|
* 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, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2018-10-16 22:29:06 -07:00
|
|
|
#include "gargoyle/scott/scott.h"
|
2018-11-02 20:39:01 -07:00
|
|
|
#include "common/config-manager.h"
|
2018-10-15 21:47:27 -07:00
|
|
|
|
|
|
|
namespace Gargoyle {
|
|
|
|
namespace Scott {
|
|
|
|
|
2018-10-17 20:07:11 -07:00
|
|
|
Scott::Scott(OSystem *syst, const GargoyleGameDescription *gameDesc) : Glk(syst, gameDesc),
|
|
|
|
Items(nullptr), Rooms(nullptr), Verbs(nullptr), Nouns(nullptr), Messages(nullptr),
|
|
|
|
Actions(nullptr), CurrentCounter(0), SavedRoom(0), Options(0), Width(0), TopHeight(0),
|
|
|
|
split_screen(true), Bottom(0), Top(0), BitFlags(0) {
|
|
|
|
Common::fill(&NounText[0], &NounText[16], '\0');
|
|
|
|
Common::fill(&Counters[0], &Counters[16], 0);
|
|
|
|
Common::fill(&RoomSaved[0], &RoomSaved[16], 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Scott::runGame(Common::SeekableReadStream *gameFile) {
|
|
|
|
int vb, no;
|
|
|
|
initialize();
|
|
|
|
|
|
|
|
Bottom = glk_window_open(0, 0, 0, wintype_TextBuffer, 1);
|
|
|
|
if (Bottom == nullptr)
|
|
|
|
glk_exit();
|
|
|
|
glk_set_window(Bottom);
|
|
|
|
|
|
|
|
if (Options & TRS80_STYLE) {
|
|
|
|
Width = 64;
|
|
|
|
TopHeight = 11;
|
|
|
|
} else {
|
|
|
|
Width = 80;
|
|
|
|
TopHeight = 10;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (split_screen) {
|
|
|
|
Top = glk_window_open(Bottom, winmethod_Above | winmethod_Fixed, TopHeight, wintype_TextGrid, 0);
|
|
|
|
if (Top == nullptr) {
|
|
|
|
split_screen = 0;
|
|
|
|
Top = Bottom;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Top = Bottom;
|
|
|
|
}
|
|
|
|
|
2018-11-02 20:27:51 -07:00
|
|
|
output("\
|
2018-10-17 20:07:11 -07:00
|
|
|
Scott Free, A Scott Adams game driver in C.\n\
|
|
|
|
Release 1.14, (c) 1993,1994,1995 Swansea University Computer Society.\n\
|
|
|
|
Distributed under the GNU software license\n\n");
|
2018-11-02 20:27:51 -07:00
|
|
|
loadDatabase(gameFile, (Options & DEBUGGING) ? 1 : 0);
|
2018-10-17 20:07:11 -07:00
|
|
|
|
2018-11-04 14:19:30 -08:00
|
|
|
// Check for savegame
|
|
|
|
if (ConfMan.hasKey("save_slot")) {
|
|
|
|
int saveSlot = ConfMan.getInt("save_slot");
|
|
|
|
if (saveSlot >= 0)
|
|
|
|
loadGameState(saveSlot);
|
|
|
|
}
|
|
|
|
|
2018-10-17 20:07:11 -07:00
|
|
|
while (!shouldQuit()) {
|
|
|
|
glk_tick();
|
|
|
|
|
2018-11-02 20:27:51 -07:00
|
|
|
performActions(0, 0);
|
2018-10-17 20:07:11 -07:00
|
|
|
|
2018-11-02 20:27:51 -07:00
|
|
|
look();
|
2018-10-17 20:07:11 -07:00
|
|
|
|
2018-11-02 20:27:51 -07:00
|
|
|
if (getInput(&vb, &no) == -1)
|
2018-10-17 20:07:11 -07:00
|
|
|
continue;
|
2018-10-27 22:19:38 -07:00
|
|
|
if (g_vm->shouldQuit())
|
2018-11-02 20:13:17 -07:00
|
|
|
break;
|
2018-10-27 22:19:38 -07:00
|
|
|
|
2018-11-02 20:27:51 -07:00
|
|
|
switch (performActions(vb, no)) {
|
2018-10-17 20:07:11 -07:00
|
|
|
case -1:
|
2018-11-02 20:27:51 -07:00
|
|
|
output("I don't understand your command. ");
|
2018-10-17 20:07:11 -07:00
|
|
|
break;
|
|
|
|
case -2:
|
2018-11-02 20:27:51 -07:00
|
|
|
output("I can't do that yet. ");
|
2018-10-17 20:07:11 -07:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Brian Howarth games seem to use -1 for forever */
|
|
|
|
if (Items[LIGHT_SOURCE].Location/*==-1*/ != DESTROYED && GameHeader.LightTime != -1) {
|
|
|
|
GameHeader.LightTime--;
|
|
|
|
if (GameHeader.LightTime < 1) {
|
|
|
|
BitFlags |= (1 << LIGHTOUTBIT);
|
|
|
|
if (Items[LIGHT_SOURCE].Location == CARRIED ||
|
|
|
|
Items[LIGHT_SOURCE].Location == MyLoc) {
|
|
|
|
if (Options&SCOTTLIGHT)
|
2018-11-02 20:27:51 -07:00
|
|
|
output("Light has run out! ");
|
2018-10-17 20:07:11 -07:00
|
|
|
else
|
2018-11-02 20:27:51 -07:00
|
|
|
output("Your light has run out. ");
|
2018-10-17 20:07:11 -07:00
|
|
|
}
|
|
|
|
if (Options&PREHISTORIC_LAMP)
|
|
|
|
Items[LIGHT_SOURCE].Location = DESTROYED;
|
|
|
|
} else if (GameHeader.LightTime < 25) {
|
|
|
|
if (Items[LIGHT_SOURCE].Location == CARRIED ||
|
|
|
|
Items[LIGHT_SOURCE].Location == MyLoc) {
|
|
|
|
|
|
|
|
if (Options&SCOTTLIGHT) {
|
2018-11-02 20:27:51 -07:00
|
|
|
output("Light runs out in ");
|
|
|
|
outputNumber(GameHeader.LightTime);
|
|
|
|
output(" turns. ");
|
2018-10-17 20:07:11 -07:00
|
|
|
} else {
|
|
|
|
if (GameHeader.LightTime % 5 == 0)
|
2018-11-02 20:27:51 -07:00
|
|
|
output("Your light is growing dim. ");
|
2018-10-17 20:07:11 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Scott::initialize() {
|
2018-11-02 20:39:01 -07:00
|
|
|
if (ConfMan.hasKey("YOUARE")) {
|
|
|
|
if (ConfMan.getBool("YOUARE"))
|
2018-10-17 20:07:11 -07:00
|
|
|
Options |= YOUARE;
|
|
|
|
else
|
2018-11-02 20:39:01 -07:00
|
|
|
Options &= ~YOUARE;
|
2018-10-17 20:07:11 -07:00
|
|
|
}
|
2018-11-02 20:39:01 -07:00
|
|
|
if (gDebugLevel > 0)
|
|
|
|
Options |= DEBUGGING;
|
|
|
|
if (ConfMan.hasKey("SCOTTLIGHT") && ConfMan.getBool("SCOTTLIGHT"))
|
|
|
|
Options |= SCOTTLIGHT;
|
|
|
|
if (ConfMan.hasKey("TRS80_STYLE") && ConfMan.getBool("TRS80_STYLE"))
|
|
|
|
Options |= TRS80_STYLE;
|
|
|
|
if (ConfMan.hasKey("PREHISTORIC_LAMP") && ConfMan.getBool("PREHISTORIC_LAMP"))
|
|
|
|
Options |= PREHISTORIC_LAMP;
|
2018-10-17 20:07:11 -07:00
|
|
|
}
|
|
|
|
|
2018-11-02 20:27:51 -07:00
|
|
|
void Scott::display(winid_t w, const char *fmt, ...) {
|
2018-10-17 20:07:11 -07:00
|
|
|
va_list ap;
|
|
|
|
|
|
|
|
va_start(ap, fmt);
|
|
|
|
Common::String msg = Common::String::vformat(fmt, ap);
|
|
|
|
va_end(ap);
|
|
|
|
|
|
|
|
glk_put_string_stream(glk_window_get_stream(w), msg.c_str());
|
|
|
|
}
|
|
|
|
|
2018-11-02 20:27:51 -07:00
|
|
|
void Scott::delay(int seconds) {
|
2018-10-16 21:06:07 -07:00
|
|
|
event_t ev;
|
2018-10-17 20:07:11 -07:00
|
|
|
|
|
|
|
if (!glk_gestalt(gestalt_Timer, 0))
|
|
|
|
return;
|
|
|
|
|
|
|
|
glk_request_timer_events(1000 * seconds);
|
|
|
|
|
2018-10-17 20:15:05 -07:00
|
|
|
do {
|
2018-10-17 20:07:11 -07:00
|
|
|
glk_select(&ev);
|
2018-10-17 20:15:05 -07:00
|
|
|
} while (ev.type != evtype_Timer && ev.type != evtype_Quit);
|
2018-10-17 20:07:11 -07:00
|
|
|
|
|
|
|
glk_request_timer_events(0);
|
|
|
|
}
|
|
|
|
|
2018-11-02 20:27:51 -07:00
|
|
|
void Scott::fatal(const char *x) {
|
2018-10-17 20:07:11 -07:00
|
|
|
error(x);
|
|
|
|
}
|
|
|
|
|
2018-11-02 20:27:51 -07:00
|
|
|
void Scott::clearScreen(void) {
|
2018-10-17 20:07:11 -07:00
|
|
|
glk_window_clear(Bottom);
|
|
|
|
}
|
|
|
|
|
2018-11-02 20:27:51 -07:00
|
|
|
void *Scott::memAlloc(int size) {
|
2018-10-17 20:07:11 -07:00
|
|
|
void *t = (void *)malloc(size);
|
|
|
|
if (t == nullptr)
|
2018-11-02 20:27:51 -07:00
|
|
|
fatal("Out of memory");
|
2018-10-17 20:07:11 -07:00
|
|
|
return t;
|
|
|
|
}
|
|
|
|
|
2018-11-02 20:27:51 -07:00
|
|
|
bool Scott::randomPercent(uint n) {
|
2018-10-17 20:07:11 -07:00
|
|
|
return _random.getRandomNumber(99) < n;
|
|
|
|
}
|
|
|
|
|
2018-11-02 20:27:51 -07:00
|
|
|
int Scott::countCarried(void) {
|
2018-10-17 20:07:11 -07:00
|
|
|
int ct = 0;
|
|
|
|
int n = 0;
|
|
|
|
while (ct <= GameHeader.NumItems) {
|
|
|
|
if (Items[ct].Location == CARRIED)
|
|
|
|
n++;
|
|
|
|
ct++;
|
|
|
|
}
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
2018-11-02 20:27:51 -07:00
|
|
|
const char *Scott::mapSynonym(const char *word) {
|
2018-10-17 20:07:11 -07:00
|
|
|
int n = 1;
|
|
|
|
const char *tp;
|
|
|
|
static char lastword[16]; /* Last non synonym */
|
|
|
|
while (n <= GameHeader.NumWords) {
|
|
|
|
tp = Nouns[n];
|
|
|
|
if (*tp == '*')
|
|
|
|
tp++;
|
|
|
|
else
|
|
|
|
strcpy(lastword, tp);
|
|
|
|
if (xstrncasecmp(word, tp, GameHeader.WordLength) == 0)
|
|
|
|
return lastword;
|
|
|
|
n++;
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2018-11-02 20:27:51 -07:00
|
|
|
int Scott::matchUpItem(const char *text, int loc) {
|
|
|
|
const char *word = mapSynonym(text);
|
2018-10-17 20:07:11 -07:00
|
|
|
int ct = 0;
|
|
|
|
|
|
|
|
if (word == nullptr)
|
|
|
|
word = text;
|
|
|
|
|
|
|
|
while (ct <= GameHeader.NumItems) {
|
|
|
|
if (Items[ct].AutoGet && Items[ct].Location == loc &&
|
|
|
|
xstrncasecmp(Items[ct].AutoGet, word, GameHeader.WordLength) == 0)
|
|
|
|
return ct;
|
|
|
|
ct++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2018-11-02 20:27:51 -07:00
|
|
|
char *Scott::readString(Common::SeekableReadStream *f) {
|
2018-10-17 20:07:11 -07:00
|
|
|
char tmp[1024];
|
|
|
|
char *t;
|
|
|
|
int c, nc;
|
|
|
|
int ct = 0;
|
|
|
|
do {
|
|
|
|
c = f->readByte();
|
|
|
|
} while (f->pos() < f->size() && Common::isSpace(c));
|
|
|
|
if (c != '"') {
|
2018-11-02 20:27:51 -07:00
|
|
|
fatal("Initial quote expected");
|
2018-10-17 20:07:11 -07:00
|
|
|
}
|
|
|
|
|
2018-10-17 20:15:05 -07:00
|
|
|
for (;;) {
|
|
|
|
if (f->pos() >= f->size())
|
2018-11-02 20:27:51 -07:00
|
|
|
fatal("EOF in string");
|
2018-10-17 20:15:05 -07:00
|
|
|
|
|
|
|
c = f->readByte();
|
|
|
|
if (c == '"') {
|
2018-10-17 20:07:11 -07:00
|
|
|
nc = f->readByte();
|
|
|
|
if (nc != '"') {
|
|
|
|
f->seek(-1, SEEK_CUR);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (c == '`')
|
|
|
|
c = '"'; /* pdd */
|
|
|
|
|
|
|
|
/* Ensure a valid Glk newline is sent. */
|
|
|
|
if (c == '\n')
|
|
|
|
tmp[ct++] = 10;
|
|
|
|
/* Special case: assume CR is part of CRLF in a
|
|
|
|
* DOS-formatted file, and ignore it.
|
|
|
|
*/
|
|
|
|
else if (c == 13)
|
|
|
|
;
|
|
|
|
/* Pass only ASCII to Glk; the other reasonable option
|
|
|
|
* would be to pass Latin-1, but it's probably safe to
|
|
|
|
* assume that Scott Adams games are ASCII only.
|
|
|
|
*/
|
|
|
|
else if ((c >= 32 && c <= 126))
|
|
|
|
tmp[ct++] = c;
|
|
|
|
else
|
|
|
|
tmp[ct++] = '?';
|
2018-10-27 22:09:01 -07:00
|
|
|
}
|
2018-10-17 20:07:11 -07:00
|
|
|
|
|
|
|
tmp[ct] = 0;
|
2018-11-02 20:27:51 -07:00
|
|
|
t = (char *)memAlloc(ct + 1);
|
2018-10-17 20:07:11 -07:00
|
|
|
memcpy(t, tmp, ct + 1);
|
|
|
|
return t;
|
|
|
|
}
|
|
|
|
|
2018-11-02 20:39:01 -07:00
|
|
|
void Scott::loadDatabase(Common::SeekableReadStream *f, bool loud) {
|
2018-10-17 20:07:11 -07:00
|
|
|
int unused, ni, na, nw, nr, mc, pr, tr, wl, lt, mn, trm;
|
|
|
|
int ct;
|
|
|
|
int lo;
|
|
|
|
Action *ap;
|
|
|
|
Room *rp;
|
|
|
|
Item *ip;
|
|
|
|
/* Load the header */
|
|
|
|
|
|
|
|
readInts(f, 12, &unused, &ni, &na, &nw, &nr, &mc, &pr, &tr, &wl, <, &mn, &trm);
|
|
|
|
|
|
|
|
GameHeader.NumItems = ni;
|
2018-11-02 20:27:51 -07:00
|
|
|
Items = (Item *)memAlloc(sizeof(Item)*(ni + 1));
|
2018-10-17 20:07:11 -07:00
|
|
|
GameHeader.NumActions = na;
|
2018-11-02 20:27:51 -07:00
|
|
|
Actions = (Action *)memAlloc(sizeof(Action)*(na + 1));
|
2018-10-17 20:07:11 -07:00
|
|
|
GameHeader.NumWords = nw;
|
|
|
|
GameHeader.WordLength = wl;
|
2018-11-02 20:27:51 -07:00
|
|
|
Verbs = (const char **)memAlloc(sizeof(char *)*(nw + 1));
|
|
|
|
Nouns = (const char **)memAlloc(sizeof(char *)*(nw + 1));
|
2018-10-17 20:07:11 -07:00
|
|
|
GameHeader.NumRooms = nr;
|
2018-11-02 20:27:51 -07:00
|
|
|
Rooms = (Room *)memAlloc(sizeof(Room)*(nr + 1));
|
2018-10-17 20:07:11 -07:00
|
|
|
GameHeader.MaxCarry = mc;
|
|
|
|
GameHeader.PlayerRoom = pr;
|
|
|
|
GameHeader.Treasures = tr;
|
|
|
|
GameHeader.LightTime = lt;
|
|
|
|
LightRefill = lt;
|
|
|
|
GameHeader.NumMessages = mn;
|
2018-11-02 20:27:51 -07:00
|
|
|
Messages = (const char **)memAlloc(sizeof(char *)*(mn + 1));
|
2018-10-17 20:07:11 -07:00
|
|
|
GameHeader.TreasureRoom = trm;
|
|
|
|
|
|
|
|
/* Load the actions */
|
|
|
|
|
|
|
|
ct = 0;
|
|
|
|
ap = Actions;
|
|
|
|
if (loud)
|
|
|
|
debug("Reading %d actions.", na);
|
|
|
|
while (ct < na + 1) {
|
|
|
|
readInts(f, 8,
|
|
|
|
&ap->Vocab,
|
|
|
|
&ap->Condition[0],
|
|
|
|
&ap->Condition[1],
|
|
|
|
&ap->Condition[2],
|
|
|
|
&ap->Condition[3],
|
|
|
|
&ap->Condition[4],
|
|
|
|
&ap->action[0],
|
|
|
|
&ap->action[1]);
|
|
|
|
ap++;
|
|
|
|
ct++;
|
|
|
|
}
|
|
|
|
|
|
|
|
ct = 0;
|
|
|
|
if (loud)
|
|
|
|
debug("Reading %d word pairs.", nw);
|
|
|
|
while (ct<nw + 1) {
|
2018-11-02 20:27:51 -07:00
|
|
|
Verbs[ct] = readString(f);
|
|
|
|
Nouns[ct] = readString(f);
|
2018-10-17 20:07:11 -07:00
|
|
|
ct++;
|
|
|
|
}
|
|
|
|
ct = 0;
|
|
|
|
rp = Rooms;
|
|
|
|
if (loud)
|
|
|
|
debug("Reading %d rooms.", nr);
|
|
|
|
while (ct<nr + 1) {
|
|
|
|
readInts(f, 6,
|
|
|
|
&rp->Exits[0], &rp->Exits[1], &rp->Exits[2],
|
|
|
|
&rp->Exits[3], &rp->Exits[4], &rp->Exits[5]);
|
|
|
|
|
2018-11-02 20:27:51 -07:00
|
|
|
rp->Text = readString(f);
|
2018-10-17 20:07:11 -07:00
|
|
|
ct++;
|
|
|
|
rp++;
|
|
|
|
}
|
|
|
|
|
|
|
|
ct = 0;
|
|
|
|
if (loud)
|
|
|
|
debug("Reading %d messages.", mn);
|
|
|
|
while (ct<mn + 1) {
|
2018-11-02 20:27:51 -07:00
|
|
|
Messages[ct] = readString(f);
|
2018-10-17 20:07:11 -07:00
|
|
|
ct++;
|
|
|
|
}
|
|
|
|
|
|
|
|
ct = 0;
|
|
|
|
if (loud)
|
|
|
|
debug("Reading %d items.", ni);
|
|
|
|
ip = Items;
|
|
|
|
while (ct < ni + 1) {
|
2018-11-02 20:27:51 -07:00
|
|
|
ip->Text = readString(f);
|
2018-10-17 20:07:11 -07:00
|
|
|
ip->AutoGet = strchr(ip->Text, '/');
|
|
|
|
/* Some games use // to mean no auto get/drop word! */
|
|
|
|
if (ip->AutoGet && strcmp(ip->AutoGet, "//") && strcmp(ip->AutoGet, "/*")) {
|
|
|
|
char *t;
|
|
|
|
*ip->AutoGet++ = 0;
|
|
|
|
t = strchr(ip->AutoGet, '/');
|
|
|
|
if (t != nullptr)
|
|
|
|
*t = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
readInts(f, 1, &lo);
|
|
|
|
ip->Location = (unsigned char)lo;
|
|
|
|
ip->InitialLoc = ip->Location;
|
|
|
|
ip++;
|
|
|
|
ct++;
|
|
|
|
}
|
|
|
|
ct = 0;
|
|
|
|
/* Discard Comment Strings */
|
|
|
|
while (ct<na + 1) {
|
2018-11-02 20:27:51 -07:00
|
|
|
free(readString(f));
|
2018-10-17 20:07:11 -07:00
|
|
|
ct++;
|
|
|
|
}
|
|
|
|
|
|
|
|
readInts(f, 1, &ct);
|
|
|
|
if (loud)
|
|
|
|
debug("Version %d.%02d of Adventure ", ct / 100, ct % 100);
|
|
|
|
readInts(f, 1, &ct);
|
|
|
|
|
|
|
|
if (loud)
|
|
|
|
debug("%d.\nLoad Complete.\n", ct);
|
|
|
|
}
|
|
|
|
|
2018-11-02 20:27:51 -07:00
|
|
|
void Scott::output(const char *a) {
|
|
|
|
display(Bottom, "%s", a);
|
2018-10-17 20:07:11 -07:00
|
|
|
}
|
|
|
|
|
2018-11-02 20:27:51 -07:00
|
|
|
void Scott::outputNumber(int a) {
|
|
|
|
display(Bottom, "%d", a);
|
2018-10-17 20:07:11 -07:00
|
|
|
}
|
|
|
|
|
2018-11-02 20:27:51 -07:00
|
|
|
void Scott::look(void) {
|
2018-10-17 20:07:11 -07:00
|
|
|
static char *ExitNames[6] = { "North", "South", "East", "West", "Up", "Down" };
|
|
|
|
Room *r;
|
|
|
|
int ct, f;
|
|
|
|
int pos;
|
|
|
|
|
|
|
|
if (split_screen)
|
|
|
|
glk_window_clear(Top);
|
|
|
|
|
|
|
|
if ((BitFlags&(1 << DARKBIT)) && Items[LIGHT_SOURCE].Location != CARRIED
|
|
|
|
&& Items[LIGHT_SOURCE].Location != MyLoc) {
|
|
|
|
if (Options&YOUARE)
|
2018-11-02 20:27:51 -07:00
|
|
|
display(Top, "You can't see. It is too dark!\n");
|
2018-10-17 20:07:11 -07:00
|
|
|
else
|
2018-11-02 20:27:51 -07:00
|
|
|
display(Top, "I can't see. It is too dark!\n");
|
2018-10-17 20:07:11 -07:00
|
|
|
if (Options & TRS80_STYLE)
|
2018-11-02 20:27:51 -07:00
|
|
|
display(Top, TRS80_LINE);
|
2018-10-17 20:07:11 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
r = &Rooms[MyLoc];
|
|
|
|
if (*r->Text == '*')
|
2018-11-02 20:27:51 -07:00
|
|
|
display(Top, "%s\n", r->Text + 1);
|
2018-10-17 20:07:11 -07:00
|
|
|
else {
|
|
|
|
if (Options&YOUARE)
|
2018-11-02 20:27:51 -07:00
|
|
|
display(Top, "You are in a %s\n", r->Text);
|
2018-10-17 20:07:11 -07:00
|
|
|
else
|
2018-11-02 20:27:51 -07:00
|
|
|
display(Top, "I'm in a %s\n", r->Text);
|
2018-10-17 20:07:11 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
ct = 0;
|
|
|
|
f = 0;
|
2018-11-02 20:27:51 -07:00
|
|
|
display(Top, "\nObvious exits: ");
|
2018-10-17 20:07:11 -07:00
|
|
|
while (ct<6) {
|
|
|
|
if (r->Exits[ct] != 0)
|
|
|
|
{
|
|
|
|
if (f == 0)
|
|
|
|
f = 1;
|
|
|
|
else
|
2018-11-02 20:27:51 -07:00
|
|
|
display(Top, ", ");
|
|
|
|
display(Top, "%s", ExitNames[ct]);
|
2018-10-17 20:07:11 -07:00
|
|
|
}
|
|
|
|
ct++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (f == 0)
|
2018-11-02 20:27:51 -07:00
|
|
|
display(Top, "none");
|
|
|
|
display(Top, ".\n");
|
2018-10-17 20:07:11 -07:00
|
|
|
ct = 0;
|
|
|
|
f = 0;
|
|
|
|
pos = 0;
|
|
|
|
while (ct <= GameHeader.NumItems) {
|
|
|
|
if (Items[ct].Location == MyLoc) {
|
|
|
|
if (f == 0) {
|
|
|
|
if (Options & YOUARE) {
|
2018-11-02 20:27:51 -07:00
|
|
|
display(Top, "\nYou can also see: ");
|
2018-10-17 20:07:11 -07:00
|
|
|
pos = 18;
|
|
|
|
} else {
|
2018-11-02 20:27:51 -07:00
|
|
|
display(Top, "\nI can also see: ");
|
2018-10-17 20:07:11 -07:00
|
|
|
pos = 16;
|
|
|
|
}
|
|
|
|
f++;
|
|
|
|
} else if (!(Options & TRS80_STYLE)) {
|
2018-11-02 20:27:51 -07:00
|
|
|
display(Top, " - ");
|
2018-10-17 20:07:11 -07:00
|
|
|
pos += 3;
|
|
|
|
}
|
|
|
|
if (pos + (int)strlen(Items[ct].Text) > (Width - 10)) {
|
|
|
|
pos = 0;
|
2018-11-02 20:27:51 -07:00
|
|
|
display(Top, "\n");
|
2018-10-17 20:07:11 -07:00
|
|
|
}
|
2018-11-02 20:27:51 -07:00
|
|
|
display(Top, "%s", Items[ct].Text);
|
2018-10-17 20:07:11 -07:00
|
|
|
pos += strlen(Items[ct].Text);
|
|
|
|
if (Options & TRS80_STYLE) {
|
2018-11-02 20:27:51 -07:00
|
|
|
display(Top, ". ");
|
2018-10-17 20:07:11 -07:00
|
|
|
pos += 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ct++;
|
|
|
|
}
|
|
|
|
|
2018-11-02 20:27:51 -07:00
|
|
|
display(Top, "\n");
|
2018-10-17 20:07:11 -07:00
|
|
|
if (Options & TRS80_STYLE)
|
2018-11-02 20:27:51 -07:00
|
|
|
display(Top, TRS80_LINE);
|
2018-10-17 20:07:11 -07:00
|
|
|
}
|
|
|
|
|
2018-11-02 20:27:51 -07:00
|
|
|
int Scott::whichWord(const char *word, const char **list) {
|
2018-10-17 20:07:11 -07:00
|
|
|
int n = 1;
|
|
|
|
int ne = 1;
|
|
|
|
const char *tp;
|
|
|
|
while (ne <= GameHeader.NumWords) {
|
|
|
|
tp = list[ne];
|
|
|
|
if (*tp == '*')
|
|
|
|
tp++;
|
|
|
|
else
|
|
|
|
n = ne;
|
|
|
|
if (xstrncasecmp(word, tp, GameHeader.WordLength) == 0)
|
|
|
|
return n;
|
|
|
|
ne++;
|
|
|
|
}
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2018-11-02 20:27:51 -07:00
|
|
|
void Scott::lineInput(char *buf, size_t n) {
|
2018-10-17 20:07:11 -07:00
|
|
|
event_t ev;
|
|
|
|
|
|
|
|
glk_request_line_event(Bottom, buf, n - 1, 0);
|
|
|
|
|
2018-10-16 21:06:07 -07:00
|
|
|
do {
|
|
|
|
glk_select(&ev);
|
2018-10-27 22:19:38 -07:00
|
|
|
if (ev.type == evtype_Quit)
|
|
|
|
return;
|
|
|
|
else if (ev.type == evtype_LineInput)
|
2018-10-16 21:06:07 -07:00
|
|
|
break;
|
2018-10-17 20:07:11 -07:00
|
|
|
else if (ev.type == evtype_Arrange && split_screen)
|
2018-11-02 20:27:51 -07:00
|
|
|
look();
|
2018-10-16 21:06:07 -07:00
|
|
|
} while (ev.type != evtype_Quit);
|
2018-10-17 20:07:11 -07:00
|
|
|
|
|
|
|
buf[ev.val1] = 0;
|
|
|
|
}
|
|
|
|
|
2018-11-02 20:27:51 -07:00
|
|
|
void Scott::saveGame(void) {
|
2018-11-03 23:04:32 -07:00
|
|
|
frefid_t ref = glk_fileref_create_by_prompt(fileusage_TextMode | fileusage_SavedGame,
|
|
|
|
filemode_Write, 0);
|
|
|
|
if (ref == nullptr)
|
|
|
|
return;
|
2018-10-17 20:07:11 -07:00
|
|
|
|
2018-11-03 23:04:32 -07:00
|
|
|
int slot = ref->_slotNumber;
|
|
|
|
Common::String desc = ref->_description;
|
2018-10-17 20:07:11 -07:00
|
|
|
glk_fileref_destroy(ref);
|
|
|
|
|
2018-11-03 23:04:32 -07:00
|
|
|
saveGameState(slot, desc);
|
|
|
|
}
|
|
|
|
|
|
|
|
Common::Error Scott::saveGameState(int slot, const Common::String &desc) {
|
|
|
|
Common::String msg;
|
|
|
|
FileReference ref;
|
|
|
|
ref._slotNumber = slot;
|
|
|
|
ref._description = desc;
|
|
|
|
|
|
|
|
strid_t file = glk_stream_open_file(&ref, filemode_Write, 0);
|
|
|
|
if (file == nullptr)
|
|
|
|
return Common::kWritingFailed;
|
|
|
|
|
|
|
|
for (int ct = 0; ct < 16; ct++) {
|
2018-10-17 20:07:11 -07:00
|
|
|
msg = Common::String::format("%d %d\n", Counters[ct], RoomSaved[ct]);
|
|
|
|
glk_put_string_stream(file, msg.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
msg = Common::String::format("%lu %d %hd %d %d %hd\n",
|
|
|
|
BitFlags, (BitFlags&(1 << DARKBIT)) ? 1 : 0,
|
|
|
|
MyLoc, CurrentCounter, SavedRoom, GameHeader.LightTime);
|
|
|
|
glk_put_string_stream(file, msg.c_str());
|
|
|
|
|
2018-11-03 23:04:32 -07:00
|
|
|
for (int ct = 0; ct <= GameHeader.NumItems; ct++) {
|
2018-10-17 20:07:11 -07:00
|
|
|
msg = Common::String::format("%hd\n", (short)Items[ct].Location);
|
|
|
|
glk_put_string_stream(file, msg.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
glk_stream_close(file, nullptr);
|
2018-11-02 20:27:51 -07:00
|
|
|
output("Saved.\n");
|
2018-11-03 23:04:32 -07:00
|
|
|
|
|
|
|
return Common::kNoError;
|
2018-10-17 20:07:11 -07:00
|
|
|
}
|
|
|
|
|
2018-11-02 20:27:51 -07:00
|
|
|
void Scott::loadGame(void) {
|
2018-11-03 23:04:32 -07:00
|
|
|
frefid_t ref = glk_fileref_create_by_prompt(fileusage_TextMode | fileusage_SavedGame,
|
|
|
|
filemode_Read, 0);
|
|
|
|
if (ref == nullptr)
|
|
|
|
return;
|
|
|
|
|
|
|
|
int slotNumber = ref->_slotNumber;
|
|
|
|
glk_fileref_destroy(ref);
|
|
|
|
|
|
|
|
loadGameState(slotNumber);
|
|
|
|
}
|
|
|
|
|
|
|
|
Common::Error Scott::loadGameState(int slot) {
|
2018-10-17 20:07:11 -07:00
|
|
|
strid_t file;
|
|
|
|
char buf[128];
|
|
|
|
int ct = 0;
|
|
|
|
short lo;
|
2018-11-03 23:04:32 -07:00
|
|
|
short darkFlag;
|
2018-10-17 20:07:11 -07:00
|
|
|
|
2018-11-03 23:04:32 -07:00
|
|
|
FileReference ref;
|
|
|
|
ref._slotNumber = slot;
|
2018-10-17 20:07:11 -07:00
|
|
|
|
2018-11-03 23:04:32 -07:00
|
|
|
file = glk_stream_open_file(&ref, filemode_Read, 0);
|
|
|
|
if (file == nullptr)
|
|
|
|
return Common::kReadingFailed;
|
2018-10-17 20:07:11 -07:00
|
|
|
|
|
|
|
for (ct = 0; ct<16; ct++) {
|
|
|
|
glk_get_line_stream(file, buf, sizeof buf);
|
|
|
|
sscanf(buf, "%d %d", &Counters[ct], &RoomSaved[ct]);
|
|
|
|
}
|
|
|
|
|
|
|
|
glk_get_line_stream(file, buf, sizeof buf);
|
|
|
|
sscanf(buf, "%ld %hd %d %d %d %d\n",
|
2018-11-03 23:04:32 -07:00
|
|
|
&BitFlags, &darkFlag, &MyLoc, &CurrentCounter, &SavedRoom,
|
2018-10-17 20:07:11 -07:00
|
|
|
&GameHeader.LightTime);
|
|
|
|
|
2018-11-03 23:04:32 -07:00
|
|
|
// Backward compatibility
|
|
|
|
if (darkFlag)
|
2018-10-17 20:07:11 -07:00
|
|
|
BitFlags |= (1 << 15);
|
|
|
|
for (ct = 0; ct <= GameHeader.NumItems; ct++) {
|
|
|
|
glk_get_line_stream(file, buf, sizeof buf);
|
|
|
|
sscanf(buf, "%hd\n", &lo);
|
|
|
|
Items[ct].Location = (unsigned char)lo;
|
|
|
|
}
|
2018-11-03 23:04:32 -07:00
|
|
|
|
|
|
|
return Common::kNoError;
|
2018-10-17 20:07:11 -07:00
|
|
|
}
|
|
|
|
|
2018-11-02 20:27:51 -07:00
|
|
|
int Scott::getInput(int *vb, int *no) {
|
2018-10-17 20:07:11 -07:00
|
|
|
char buf[256];
|
|
|
|
char verb[10], noun[10];
|
|
|
|
int vc, nc;
|
|
|
|
int num;
|
|
|
|
|
|
|
|
do {
|
|
|
|
do {
|
2018-11-02 20:27:51 -07:00
|
|
|
output("\nTell me what to do ? ");
|
|
|
|
lineInput(buf, sizeof buf);
|
2018-10-27 22:19:38 -07:00
|
|
|
if (g_vm->shouldQuit())
|
|
|
|
return 0;
|
|
|
|
|
2018-10-17 20:07:11 -07:00
|
|
|
num = sscanf(buf, "%9s %9s", verb, noun);
|
|
|
|
} while (num == 0 || *buf == '\n');
|
|
|
|
|
|
|
|
if (xstrcasecmp(verb, "restore") == 0) {
|
2018-11-02 20:27:51 -07:00
|
|
|
loadGame();
|
2018-10-17 20:07:11 -07:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (num == 1)
|
|
|
|
*noun = 0;
|
|
|
|
if (*noun == 0 && strlen(verb) == 1) {
|
|
|
|
switch (Common::isUpper((unsigned char)*verb) ? tolower((unsigned char)*verb) : *verb) {
|
|
|
|
case 'n':strcpy(verb, "NORTH"); break;
|
|
|
|
case 'e':strcpy(verb, "EAST"); break;
|
|
|
|
case 's':strcpy(verb, "SOUTH"); break;
|
|
|
|
case 'w':strcpy(verb, "WEST"); break;
|
|
|
|
case 'u':strcpy(verb, "UP"); break;
|
|
|
|
case 'd':strcpy(verb, "DOWN"); break;
|
|
|
|
/* Brian Howarth interpreter also supports this */
|
|
|
|
case 'i':strcpy(verb, "INVENTORY"); break;
|
|
|
|
}
|
|
|
|
}
|
2018-11-02 20:27:51 -07:00
|
|
|
nc = whichWord(verb, Nouns);
|
2018-10-17 20:07:11 -07:00
|
|
|
/* The Scott Adams system has a hack to avoid typing 'go' */
|
|
|
|
if (nc >= 1 && nc <= 6) {
|
|
|
|
vc = 1;
|
|
|
|
} else {
|
2018-11-02 20:27:51 -07:00
|
|
|
vc = whichWord(verb, Verbs);
|
|
|
|
nc = whichWord(noun, Nouns);
|
2018-10-17 20:07:11 -07:00
|
|
|
}
|
|
|
|
*vb = vc;
|
|
|
|
*no = nc;
|
|
|
|
if (vc == -1) {
|
2018-11-02 20:27:51 -07:00
|
|
|
output("You use word(s) I don't know! ");
|
2018-10-17 20:07:11 -07:00
|
|
|
}
|
|
|
|
} while (vc == -1);
|
|
|
|
|
|
|
|
strcpy(NounText, noun); /* Needed by GET/DROP hack */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-11-02 20:27:51 -07:00
|
|
|
int Scott::performLine(int ct) {
|
2018-10-17 20:07:11 -07:00
|
|
|
int continuation = 0;
|
|
|
|
int param[5], pptr = 0;
|
|
|
|
int act[4];
|
|
|
|
int cc = 0;
|
|
|
|
|
|
|
|
while (cc<5) {
|
|
|
|
int cv, dv;
|
|
|
|
cv = Actions[ct].Condition[cc];
|
|
|
|
dv = cv / 20;
|
|
|
|
cv %= 20;
|
|
|
|
switch (cv) {
|
|
|
|
case 0:
|
|
|
|
param[pptr++] = dv;
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
if (Items[dv].Location != CARRIED)
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
if (Items[dv].Location != MyLoc)
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
if (Items[dv].Location != CARRIED&&
|
|
|
|
Items[dv].Location != MyLoc)
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
if (MyLoc != dv)
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
case 5:
|
|
|
|
if (Items[dv].Location == MyLoc)
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
if (Items[dv].Location == CARRIED)
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
case 7:
|
|
|
|
if (MyLoc == dv)
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
case 8:
|
|
|
|
if ((BitFlags&(1 << dv)) == 0)
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
case 9:
|
|
|
|
if (BitFlags&(1 << dv))
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
case 10:
|
2018-11-02 20:27:51 -07:00
|
|
|
if (countCarried() == 0)
|
2018-10-17 20:07:11 -07:00
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
case 11:
|
2018-11-02 20:27:51 -07:00
|
|
|
if (countCarried())
|
2018-10-17 20:07:11 -07:00
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
case 12:
|
|
|
|
if (Items[dv].Location == CARRIED || Items[dv].Location == MyLoc)
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
case 13:
|
|
|
|
if (Items[dv].Location == 0)
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
case 14:
|
|
|
|
if (Items[dv].Location)
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
case 15:
|
|
|
|
if (CurrentCounter>dv)
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
case 16:
|
|
|
|
if (CurrentCounter <= dv)
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
case 17:
|
|
|
|
if (Items[dv].Location != Items[dv].InitialLoc)
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
case 18:
|
|
|
|
if (Items[dv].Location == Items[dv].InitialLoc)
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
case 19:/* Only seen in Brian Howarth games so far */
|
|
|
|
if (CurrentCounter != dv)
|
|
|
|
return 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
cc++;
|
|
|
|
}
|
|
|
|
/* Actions */
|
|
|
|
act[0] = Actions[ct].action[0];
|
|
|
|
act[2] = Actions[ct].action[1];
|
|
|
|
act[1] = act[0] % 150;
|
|
|
|
act[3] = act[2] % 150;
|
|
|
|
act[0] /= 150;
|
|
|
|
act[2] /= 150;
|
|
|
|
cc = 0;
|
|
|
|
pptr = 0;
|
|
|
|
while (cc<4)
|
|
|
|
{
|
|
|
|
if (act[cc] >= 1 && act[cc] < 52) {
|
2018-11-02 20:27:51 -07:00
|
|
|
output(Messages[act[cc]]);
|
|
|
|
output("\n");
|
2018-10-17 20:07:11 -07:00
|
|
|
} else if (act[cc] > 101) {
|
2018-11-02 20:27:51 -07:00
|
|
|
output(Messages[act[cc] - 50]);
|
|
|
|
output("\n");
|
2018-10-17 20:07:11 -07:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
switch (act[cc]) {
|
|
|
|
case 0:/* NOP */
|
|
|
|
break;
|
|
|
|
case 52:
|
2018-11-02 20:27:51 -07:00
|
|
|
if (countCarried() == GameHeader.MaxCarry)
|
2018-10-17 20:07:11 -07:00
|
|
|
{
|
|
|
|
if (Options&YOUARE)
|
2018-11-02 20:27:51 -07:00
|
|
|
output("You are carrying too much. ");
|
2018-10-17 20:07:11 -07:00
|
|
|
else
|
2018-11-02 20:27:51 -07:00
|
|
|
output("I've too much to carry! ");
|
2018-10-17 20:07:11 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
Items[param[pptr++]].Location = CARRIED;
|
|
|
|
break;
|
|
|
|
case 53:
|
|
|
|
Items[param[pptr++]].Location = MyLoc;
|
|
|
|
break;
|
|
|
|
case 54:
|
|
|
|
MyLoc = param[pptr++];
|
|
|
|
break;
|
|
|
|
case 55:
|
|
|
|
Items[param[pptr++]].Location = 0;
|
|
|
|
break;
|
|
|
|
case 56:
|
|
|
|
BitFlags |= 1 << DARKBIT;
|
|
|
|
break;
|
|
|
|
case 57:
|
|
|
|
BitFlags &= ~(1 << DARKBIT);
|
|
|
|
break;
|
|
|
|
case 58:
|
|
|
|
BitFlags |= (1 << param[pptr++]);
|
|
|
|
break;
|
|
|
|
case 59:
|
|
|
|
Items[param[pptr++]].Location = 0;
|
|
|
|
break;
|
|
|
|
case 60:
|
|
|
|
BitFlags &= ~(1 << param[pptr++]);
|
|
|
|
break;
|
|
|
|
case 61:
|
|
|
|
if (Options&YOUARE)
|
2018-11-02 20:27:51 -07:00
|
|
|
output("You are dead.\n");
|
2018-10-17 20:07:11 -07:00
|
|
|
else
|
2018-11-02 20:27:51 -07:00
|
|
|
output("I am dead.\n");
|
2018-10-17 20:07:11 -07:00
|
|
|
BitFlags &= ~(1 << DARKBIT);
|
|
|
|
MyLoc = GameHeader.NumRooms;/* It seems to be what the code says! */
|
|
|
|
break;
|
|
|
|
case 62:
|
|
|
|
{
|
|
|
|
/* Bug fix for some systems - before it could get parameters wrong */
|
|
|
|
int i = param[pptr++];
|
|
|
|
Items[i].Location = param[pptr++];
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 63:
|
2018-11-02 20:27:51 -07:00
|
|
|
doneit: output("The game is now over.\n");
|
2018-10-17 20:07:11 -07:00
|
|
|
glk_exit();
|
2018-10-18 20:13:06 -07:00
|
|
|
break;
|
2018-10-17 20:07:11 -07:00
|
|
|
case 64:
|
|
|
|
break;
|
|
|
|
case 65:
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
int n = 0;
|
|
|
|
while (i <= GameHeader.NumItems)
|
|
|
|
{
|
|
|
|
if (Items[i].Location == GameHeader.TreasureRoom &&
|
|
|
|
*Items[i].Text == '*')
|
|
|
|
n++;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
if (Options&YOUARE)
|
2018-11-02 20:27:51 -07:00
|
|
|
output("You have stored ");
|
2018-10-17 20:07:11 -07:00
|
|
|
else
|
2018-11-02 20:27:51 -07:00
|
|
|
output("I've stored ");
|
|
|
|
outputNumber(n);
|
|
|
|
output(" treasures. On a scale of 0 to 100, that rates ");
|
|
|
|
outputNumber((n * 100) / GameHeader.Treasures);
|
|
|
|
output(".\n");
|
2018-10-17 20:07:11 -07:00
|
|
|
if (n == GameHeader.Treasures)
|
|
|
|
{
|
2018-11-02 20:27:51 -07:00
|
|
|
output("Well done.\n");
|
2018-10-17 20:07:11 -07:00
|
|
|
goto doneit;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 66:
|
|
|
|
{
|
|
|
|
int i = 0;
|
|
|
|
int f = 0;
|
|
|
|
if (Options&YOUARE)
|
2018-11-02 20:27:51 -07:00
|
|
|
output("You are carrying:\n");
|
2018-10-17 20:07:11 -07:00
|
|
|
else
|
2018-11-02 20:27:51 -07:00
|
|
|
output("I'm carrying:\n");
|
2018-10-17 20:07:11 -07:00
|
|
|
while (i <= GameHeader.NumItems)
|
|
|
|
{
|
|
|
|
if (Items[i].Location == CARRIED)
|
|
|
|
{
|
|
|
|
if (f == 1)
|
|
|
|
{
|
|
|
|
if (Options & TRS80_STYLE)
|
2018-11-02 20:27:51 -07:00
|
|
|
output(". ");
|
2018-10-17 20:07:11 -07:00
|
|
|
else
|
2018-11-02 20:27:51 -07:00
|
|
|
output(" - ");
|
2018-10-17 20:07:11 -07:00
|
|
|
}
|
|
|
|
f = 1;
|
2018-11-02 20:27:51 -07:00
|
|
|
output(Items[i].Text);
|
2018-10-17 20:07:11 -07:00
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
if (f == 0)
|
2018-11-02 20:27:51 -07:00
|
|
|
output("Nothing");
|
|
|
|
output(".\n");
|
2018-10-17 20:07:11 -07:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 67:
|
|
|
|
BitFlags |= (1 << 0);
|
|
|
|
break;
|
|
|
|
case 68:
|
|
|
|
BitFlags &= ~(1 << 0);
|
|
|
|
break;
|
|
|
|
case 69:
|
|
|
|
GameHeader.LightTime = LightRefill;
|
|
|
|
Items[LIGHT_SOURCE].Location = CARRIED;
|
|
|
|
BitFlags &= ~(1 << LIGHTOUTBIT);
|
|
|
|
break;
|
|
|
|
case 70:
|
2018-11-02 20:27:51 -07:00
|
|
|
clearScreen(); /* pdd. */
|
2018-10-17 20:07:11 -07:00
|
|
|
break;
|
|
|
|
case 71:
|
2018-11-02 20:27:51 -07:00
|
|
|
saveGame();
|
2018-10-17 20:07:11 -07:00
|
|
|
break;
|
|
|
|
case 72:
|
|
|
|
{
|
|
|
|
int i1 = param[pptr++];
|
|
|
|
int i2 = param[pptr++];
|
|
|
|
int t = Items[i1].Location;
|
|
|
|
Items[i1].Location = Items[i2].Location;
|
|
|
|
Items[i2].Location = t;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 73:
|
|
|
|
continuation = 1;
|
|
|
|
break;
|
|
|
|
case 74:
|
|
|
|
Items[param[pptr++]].Location = CARRIED;
|
|
|
|
break;
|
|
|
|
case 75:
|
|
|
|
{
|
|
|
|
int i1, i2;
|
|
|
|
i1 = param[pptr++];
|
|
|
|
i2 = param[pptr++];
|
|
|
|
Items[i1].Location = Items[i2].Location;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 76: /* Looking at adventure .. */
|
|
|
|
break;
|
|
|
|
case 77:
|
|
|
|
if (CurrentCounter >= 0)
|
|
|
|
CurrentCounter--;
|
|
|
|
break;
|
|
|
|
case 78:
|
2018-11-02 20:27:51 -07:00
|
|
|
outputNumber(CurrentCounter);
|
2018-10-17 20:07:11 -07:00
|
|
|
break;
|
|
|
|
case 79:
|
|
|
|
CurrentCounter = param[pptr++];
|
|
|
|
break;
|
|
|
|
case 80:
|
|
|
|
{
|
|
|
|
int t = MyLoc;
|
|
|
|
MyLoc = SavedRoom;
|
|
|
|
SavedRoom = t;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 81:
|
|
|
|
{
|
|
|
|
/* This is somewhat guessed. Claymorgue always
|
|
|
|
seems to do select counter n, thing, select counter n,
|
|
|
|
but uses one value that always seems to exist. Trying
|
|
|
|
a few options I found this gave sane results on ageing */
|
|
|
|
int t = param[pptr++];
|
|
|
|
int c1 = CurrentCounter;
|
|
|
|
CurrentCounter = Counters[t];
|
|
|
|
Counters[t] = c1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 82:
|
|
|
|
CurrentCounter += param[pptr++];
|
|
|
|
break;
|
|
|
|
case 83:
|
|
|
|
CurrentCounter -= param[pptr++];
|
|
|
|
if (CurrentCounter < -1)
|
|
|
|
CurrentCounter = -1;
|
|
|
|
/* Note: This seems to be needed. I don't yet
|
|
|
|
know if there is a maximum value to limit too */
|
|
|
|
break;
|
|
|
|
case 84:
|
2018-11-02 20:27:51 -07:00
|
|
|
output(NounText);
|
2018-10-17 20:07:11 -07:00
|
|
|
break;
|
|
|
|
case 85:
|
2018-11-02 20:27:51 -07:00
|
|
|
output(NounText);
|
|
|
|
output("\n");
|
2018-10-17 20:07:11 -07:00
|
|
|
break;
|
|
|
|
case 86:
|
2018-11-02 20:27:51 -07:00
|
|
|
output("\n");
|
2018-10-17 20:07:11 -07:00
|
|
|
break;
|
|
|
|
case 87:
|
|
|
|
{
|
|
|
|
/* Changed this to swap location<->roomflag[x]
|
|
|
|
not roomflag 0 and x */
|
|
|
|
int p = param[pptr++];
|
|
|
|
int sr = MyLoc;
|
|
|
|
MyLoc = RoomSaved[p];
|
|
|
|
RoomSaved[p] = sr;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case 88:
|
2018-11-02 20:27:51 -07:00
|
|
|
delay(2);
|
2018-10-17 20:07:11 -07:00
|
|
|
break;
|
|
|
|
case 89:
|
|
|
|
pptr++;
|
|
|
|
/* SAGA draw picture n */
|
|
|
|
/* Spectrum Seas of Blood - start combat ? */
|
|
|
|
/* Poking this into older spectrum games causes a crash */
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
error("Unknown action %d [Param begins %d %d]\n",
|
|
|
|
act[cc], param[pptr], param[pptr + 1]);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cc++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1 + continuation;
|
|
|
|
}
|
|
|
|
|
2018-11-02 20:27:51 -07:00
|
|
|
int Scott::performActions(int vb, int no) {
|
2018-10-17 20:07:11 -07:00
|
|
|
static int disable_sysfunc = 0; /* Recursion lock */
|
|
|
|
int d = BitFlags&(1 << DARKBIT);
|
|
|
|
|
|
|
|
int ct = 0;
|
|
|
|
int fl;
|
|
|
|
int doagain = 0;
|
|
|
|
if (vb == 1 && no == -1) {
|
2018-11-02 20:27:51 -07:00
|
|
|
output("Give me a direction too.");
|
2018-10-17 20:07:11 -07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (vb == 1 && no >= 1 && no <= 6) {
|
|
|
|
int nl;
|
|
|
|
if (Items[LIGHT_SOURCE].Location == MyLoc ||
|
|
|
|
Items[LIGHT_SOURCE].Location == CARRIED)
|
|
|
|
d = 0;
|
|
|
|
if (d)
|
2018-11-02 20:27:51 -07:00
|
|
|
output("Dangerous to move in the dark! ");
|
2018-10-17 20:07:11 -07:00
|
|
|
nl = Rooms[MyLoc].Exits[no - 1];
|
|
|
|
if (nl != 0) {
|
|
|
|
MyLoc = nl;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (d) {
|
|
|
|
if (Options&YOUARE)
|
2018-11-02 20:27:51 -07:00
|
|
|
output("You fell down and broke your neck. ");
|
2018-10-17 20:07:11 -07:00
|
|
|
else
|
2018-11-02 20:27:51 -07:00
|
|
|
output("I fell down and broke my neck. ");
|
2018-10-17 20:07:11 -07:00
|
|
|
glk_exit();
|
|
|
|
}
|
|
|
|
if (Options&YOUARE)
|
2018-11-02 20:27:51 -07:00
|
|
|
output("You can't go in that direction. ");
|
2018-10-17 20:07:11 -07:00
|
|
|
else
|
2018-11-02 20:27:51 -07:00
|
|
|
output("I can't go in that direction. ");
|
2018-10-17 20:07:11 -07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
fl = -1;
|
|
|
|
while (ct <= GameHeader.NumActions) {
|
|
|
|
int vv, nv;
|
|
|
|
vv = Actions[ct].Vocab;
|
|
|
|
/* Think this is now right. If a line we run has an action73
|
|
|
|
run all following lines with vocab of 0,0 */
|
|
|
|
if (vb != 0 && (doagain&&vv != 0))
|
|
|
|
break;
|
|
|
|
/* Oops.. added this minor cockup fix 1.11 */
|
|
|
|
if (vb != 0 && !doagain && fl == 0)
|
|
|
|
break;
|
|
|
|
nv = vv % 150;
|
|
|
|
vv /= 150;
|
|
|
|
if ((vv == vb) || (doagain&&Actions[ct].Vocab == 0)) {
|
2018-11-02 20:27:51 -07:00
|
|
|
if ((vv == 0 && randomPercent(nv)) || doagain ||
|
2018-10-17 20:07:11 -07:00
|
|
|
(vv != 0 && (nv == no || nv == 0))) {
|
|
|
|
int f2;
|
|
|
|
if (fl == -1)
|
|
|
|
fl = -2;
|
2018-11-02 20:27:51 -07:00
|
|
|
if ((f2 = performLine(ct)) > 0) {
|
2018-10-17 20:07:11 -07:00
|
|
|
/* ahah finally figured it out ! */
|
|
|
|
fl = 0;
|
|
|
|
if (f2 == 2)
|
|
|
|
doagain = 1;
|
|
|
|
if (vb != 0 && doagain == 0)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ct++;
|
|
|
|
|
|
|
|
/* Previously this did not check ct against
|
|
|
|
* GameHeader.NumActions and would read past the end of
|
|
|
|
* Actions. I don't know what should happen on the last
|
|
|
|
* action, but doing nothing is better than reading one
|
|
|
|
* past the end.
|
|
|
|
* --Chris
|
|
|
|
*/
|
|
|
|
if (ct <= GameHeader.NumActions && Actions[ct].Vocab != 0)
|
|
|
|
doagain = 0;
|
|
|
|
}
|
|
|
|
if (fl != 0 && disable_sysfunc == 0) {
|
|
|
|
int item;
|
|
|
|
if (Items[LIGHT_SOURCE].Location == MyLoc ||
|
|
|
|
Items[LIGHT_SOURCE].Location == CARRIED)
|
|
|
|
d = 0;
|
|
|
|
if (vb == 10 || vb == 18) {
|
|
|
|
/* Yes they really _are_ hardcoded values */
|
|
|
|
if (vb == 10) {
|
|
|
|
if (xstrcasecmp(NounText, "ALL") == 0) {
|
|
|
|
int i = 0;
|
|
|
|
int f = 0;
|
|
|
|
|
|
|
|
if (d) {
|
2018-11-02 20:27:51 -07:00
|
|
|
output("It is dark.\n");
|
2018-10-17 20:07:11 -07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
while (i <= GameHeader.NumItems) {
|
|
|
|
if (Items[i].Location == MyLoc && Items[i].AutoGet != nullptr && Items[i].AutoGet[0] != '*') {
|
2018-11-02 20:27:51 -07:00
|
|
|
no = whichWord(Items[i].AutoGet, Nouns);
|
2018-10-17 20:07:11 -07:00
|
|
|
disable_sysfunc = 1; /* Don't recurse into auto get ! */
|
2018-11-02 20:27:51 -07:00
|
|
|
performActions(vb, no); /* Recursively check each items table code */
|
2018-10-17 20:07:11 -07:00
|
|
|
disable_sysfunc = 0;
|
2018-11-02 20:27:51 -07:00
|
|
|
if (countCarried() == GameHeader.MaxCarry) {
|
2018-10-17 20:07:11 -07:00
|
|
|
if (Options&YOUARE)
|
2018-11-02 20:27:51 -07:00
|
|
|
output("You are carrying too much. ");
|
2018-10-17 20:07:11 -07:00
|
|
|
else
|
2018-11-02 20:27:51 -07:00
|
|
|
output("I've too much to carry. ");
|
2018-10-17 20:07:11 -07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
Items[i].Location = CARRIED;
|
2018-11-02 20:27:51 -07:00
|
|
|
output(Items[i].Text);
|
|
|
|
output(": O.K.\n");
|
2018-10-17 20:07:11 -07:00
|
|
|
f = 1;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
if (f == 0)
|
2018-11-02 20:27:51 -07:00
|
|
|
output("Nothing taken.");
|
2018-10-17 20:07:11 -07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (no == -1)
|
|
|
|
{
|
2018-11-02 20:27:51 -07:00
|
|
|
output("What ? ");
|
2018-10-17 20:07:11 -07:00
|
|
|
return 0;
|
|
|
|
}
|
2018-11-02 20:27:51 -07:00
|
|
|
if (countCarried() == GameHeader.MaxCarry)
|
2018-10-17 20:07:11 -07:00
|
|
|
{
|
|
|
|
if (Options&YOUARE)
|
2018-11-02 20:27:51 -07:00
|
|
|
output("You are carrying too much. ");
|
2018-10-17 20:07:11 -07:00
|
|
|
else
|
2018-11-02 20:27:51 -07:00
|
|
|
output("I've too much to carry. ");
|
2018-10-17 20:07:11 -07:00
|
|
|
return 0;
|
|
|
|
}
|
2018-11-02 20:27:51 -07:00
|
|
|
item = matchUpItem(NounText, MyLoc);
|
2018-10-17 20:07:11 -07:00
|
|
|
if (item == -1)
|
|
|
|
{
|
|
|
|
if (Options&YOUARE)
|
2018-11-02 20:27:51 -07:00
|
|
|
output("It is beyond your power to do that. ");
|
2018-10-17 20:07:11 -07:00
|
|
|
else
|
2018-11-02 20:27:51 -07:00
|
|
|
output("It's beyond my power to do that. ");
|
2018-10-17 20:07:11 -07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
Items[item].Location = CARRIED;
|
2018-11-02 20:27:51 -07:00
|
|
|
output("O.K. ");
|
2018-10-17 20:07:11 -07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (vb == 18) {
|
|
|
|
if (xstrcasecmp(NounText, "ALL") == 0) {
|
|
|
|
int i = 0;
|
|
|
|
int f = 0;
|
|
|
|
while (i <= GameHeader.NumItems) {
|
|
|
|
if (Items[i].Location == CARRIED && Items[i].AutoGet && Items[i].AutoGet[0] != '*') {
|
2018-11-02 20:27:51 -07:00
|
|
|
no = whichWord(Items[i].AutoGet, Nouns);
|
2018-10-17 20:07:11 -07:00
|
|
|
disable_sysfunc = 1;
|
2018-11-02 20:27:51 -07:00
|
|
|
performActions(vb, no);
|
2018-10-17 20:07:11 -07:00
|
|
|
disable_sysfunc = 0;
|
|
|
|
Items[i].Location = MyLoc;
|
2018-11-02 20:27:51 -07:00
|
|
|
output(Items[i].Text);
|
|
|
|
output(": O.K.\n");
|
2018-10-17 20:07:11 -07:00
|
|
|
f = 1;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
if (f == 0)
|
2018-11-02 20:27:51 -07:00
|
|
|
output("Nothing dropped.\n");
|
2018-10-17 20:07:11 -07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (no == -1) {
|
2018-11-02 20:27:51 -07:00
|
|
|
output("What ? ");
|
2018-10-17 20:07:11 -07:00
|
|
|
return 0;
|
|
|
|
}
|
2018-11-02 20:27:51 -07:00
|
|
|
item = matchUpItem(NounText, CARRIED);
|
2018-10-17 20:07:11 -07:00
|
|
|
if (item == -1) {
|
|
|
|
if (Options&YOUARE)
|
2018-11-02 20:27:51 -07:00
|
|
|
output("It's beyond your power to do that.\n");
|
2018-10-17 20:07:11 -07:00
|
|
|
else
|
2018-11-02 20:27:51 -07:00
|
|
|
output("It's beyond my power to do that.\n");
|
2018-10-17 20:07:11 -07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
Items[item].Location = MyLoc;
|
2018-11-02 20:27:51 -07:00
|
|
|
output("O.K. ");
|
2018-10-17 20:07:11 -07:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return fl;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Scott::xstrcasecmp(const char *s1, const char *s2) {
|
|
|
|
const unsigned char
|
|
|
|
*us1 = (const unsigned char *)s1,
|
|
|
|
*us2 = (const unsigned char *)s2;
|
|
|
|
|
|
|
|
while (tolower(*us1) == tolower(*us2++))
|
|
|
|
if (*us1++ == '\0')
|
|
|
|
return (0);
|
|
|
|
return (tolower(*us1) - tolower(*--us2));
|
|
|
|
}
|
|
|
|
|
|
|
|
int Scott::xstrncasecmp(const char *s1, const char *s2, size_t n) {
|
|
|
|
if (n != 0) {
|
|
|
|
const unsigned char
|
|
|
|
*us1 = (const unsigned char *)s1,
|
|
|
|
*us2 = (const unsigned char *)s2;
|
|
|
|
|
|
|
|
do {
|
|
|
|
if (tolower(*us1) != tolower(*us2++))
|
|
|
|
return (tolower(*us1) - tolower(*--us2));
|
|
|
|
if (*us1++ == '\0')
|
|
|
|
break;
|
|
|
|
} while (--n != 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Scott::readInts(Common::SeekableReadStream *f, size_t count, ...) {
|
|
|
|
va_list va;
|
|
|
|
va_start(va, count);
|
2018-10-27 22:09:01 -07:00
|
|
|
unsigned char c = f->readByte();
|
2018-10-17 20:07:11 -07:00
|
|
|
|
|
|
|
for (size_t idx = 0; idx < count; ++idx) {
|
2018-10-27 22:09:01 -07:00
|
|
|
while (f->pos() < f->size() && Common::isSpace(c))
|
2018-10-17 20:07:11 -07:00
|
|
|
c = f->readByte();
|
|
|
|
|
|
|
|
// Get the next value
|
|
|
|
int *val = (int *)va_arg(va, int);
|
|
|
|
*val = 0;
|
|
|
|
while (Common::isDigit(c)) {
|
|
|
|
*val = (*val * 10) + (c - '0');
|
|
|
|
c = f->readByte();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
va_end(va);
|
2018-10-16 21:06:07 -07:00
|
|
|
}
|
2018-10-15 21:47:27 -07:00
|
|
|
|
|
|
|
} // End of namespace Scott
|
|
|
|
} // End of namespace Gargoyle
|