aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Gilbert2018-10-17 20:07:11 -0700
committerPaul Gilbert2018-12-08 19:05:59 -0800
commit22648145a91ae42ed0e1ab69b1dc9ad564e9fd1d (patch)
tree3517e8ec3eca2a36d2297101490c417bedd49b9e
parentb62e30b84c6dfa98942ca094f86ecc0538cebbec (diff)
downloadscummvm-rg350-22648145a91ae42ed0e1ab69b1dc9ad564e9fd1d.tar.gz
scummvm-rg350-22648145a91ae42ed0e1ab69b1dc9ad564e9fd1d.tar.bz2
scummvm-rg350-22648145a91ae42ed0e1ab69b1dc9ad564e9fd1d.zip
GLK: SCOTT: Initial conversion of ScottFree
-rw-r--r--engines/gargoyle/gargoyle.cpp9
-rw-r--r--engines/gargoyle/gargoyle.h6
-rw-r--r--engines/gargoyle/glk.cpp6
-rw-r--r--engines/gargoyle/glk.h6
-rw-r--r--engines/gargoyle/scott/scott.cpp1259
-rw-r--r--engines/gargoyle/scott/scott.h76
6 files changed, 1346 insertions, 16 deletions
diff --git a/engines/gargoyle/gargoyle.cpp b/engines/gargoyle/gargoyle.cpp
index 3457f5fa63..7953c684f8 100644
--- a/engines/gargoyle/gargoyle.cpp
+++ b/engines/gargoyle/gargoyle.cpp
@@ -32,10 +32,12 @@
namespace Gargoyle {
GargoyleEngine::GargoyleEngine(OSystem *syst, const GargoyleGameDescription *gameDesc) :
- _gameDescription(gameDesc), Engine(syst) {
+ _gameDescription(gameDesc), Engine(syst), _random("Gargoyle") {
+ _screen = nullptr;
}
GargoyleEngine::~GargoyleEngine() {
+ delete _screen;
}
void GargoyleEngine::initialize() {
@@ -46,11 +48,14 @@ void GargoyleEngine::initialize() {
DebugMan.addDebugChannel(kDebugSound, "sound", "Sound and Music handling");
initGraphics(640, 480, false);
+ _screen = new Graphics::Screen();
}
Common::Error GargoyleEngine::run() {
initialize();
- main();
+
+ // TODO: Pass proper gamefile
+ runGame(nullptr);
return Common::kNoError;
}
diff --git a/engines/gargoyle/gargoyle.h b/engines/gargoyle/gargoyle.h
index 09402487c2..37c1a22196 100644
--- a/engines/gargoyle/gargoyle.h
+++ b/engines/gargoyle/gargoyle.h
@@ -24,10 +24,12 @@
#define GARGOYLE_GARGOLE_H
#include "common/scummsys.h"
+#include "common/random.h"
#include "common/system.h"
#include "common/serializer.h"
#include "engines/advancedDetector.h"
#include "engines/engine.h"
+#include "graphics/screen.h"
namespace Gargoyle {
@@ -67,6 +69,8 @@ private:
void initialize();
protected:
const GargoyleGameDescription *_gameDescription;
+ Graphics::Screen *_screen;
+ Common::RandomSource _random;
int _loadSaveSlot;
// Engine APIs
@@ -76,7 +80,7 @@ protected:
/**
* Main game loop for the individual interpreters
*/
- virtual void main() = 0;
+ virtual void runGame(Common::SeekableReadStream *gameFile) = 0;
public:
GargoyleEngine(OSystem *syst, const GargoyleGameDescription *gameDesc);
virtual ~GargoyleEngine();
diff --git a/engines/gargoyle/glk.cpp b/engines/gargoyle/glk.cpp
index 350b40f4ec..82943bd497 100644
--- a/engines/gargoyle/glk.cpp
+++ b/engines/gargoyle/glk.cpp
@@ -191,11 +191,11 @@ void Glk::glk_put_char_stream(strid_t str, unsigned char ch) {
// TODO
}
-void Glk::glk_put_string(char *s) {
+void Glk::glk_put_string(const char *s) {
// TODO
}
-void Glk::glk_put_string_stream(strid_t str, char *s) {
+void Glk::glk_put_string_stream(strid_t str, const char *s) {
// TODO
}
@@ -203,7 +203,7 @@ void Glk::glk_put_buffer(char *buf, glui32 len) {
// TODO
}
-void Glk::glk_put_buffer_stream(strid_t str, char *buf, glui32 len) {
+void Glk::glk_put_buffer_stream(strid_t str, const char *buf, glui32 len) {
// TODO
}
diff --git a/engines/gargoyle/glk.h b/engines/gargoyle/glk.h
index eab6f0be70..55cd563ba3 100644
--- a/engines/gargoyle/glk.h
+++ b/engines/gargoyle/glk.h
@@ -94,10 +94,10 @@ public:
void glk_put_char(unsigned char ch);
void glk_put_char_stream(strid_t str, unsigned char ch);
- void glk_put_string(char *s);
- void glk_put_string_stream(strid_t str, char *s);
+ void glk_put_string(const char *s);
+ void glk_put_string_stream(strid_t str, const char *s);
void glk_put_buffer(char *buf, glui32 len);
- void glk_put_buffer_stream(strid_t str, char *buf, glui32 len);
+ void glk_put_buffer_stream(strid_t str, const char *buf, glui32 len);
void glk_set_style(glui32 styl);
void glk_set_style_stream(strid_t str, glui32 styl);
diff --git a/engines/gargoyle/scott/scott.cpp b/engines/gargoyle/scott/scott.cpp
index 9a1a6e324e..02096c6f27 100644
--- a/engines/gargoyle/scott/scott.cpp
+++ b/engines/gargoyle/scott/scott.cpp
@@ -25,16 +25,1265 @@
namespace Gargoyle {
namespace Scott {
-void Scott::main() {
+/*
+glkunix_argumentlist_t glkunix_arguments[] =
+{
+ { "-y", glkunix_arg_NoValue, "-y Generate 'You are', 'You are carrying' type messages for games that use these instead (eg Robin Of Sherwood)" },
+ { "-i", glkunix_arg_NoValue, "-i Generate 'I am' type messages (default)" },
+ { "-d", glkunix_arg_NoValue, "-d Debugging info on load " },
+ { "-s", glkunix_arg_NoValue, "-s Generate authentic Scott Adams driver light messages rather than other driver style ones (Light goes out in n turns..)" },
+ { "-t", glkunix_arg_NoValue, "-t Generate TRS80 style display (terminal width is 64 characters; a line <-----------------> is displayed after the top stuff; objects have periods after them instead of hyphens" },
+ { "-p", glkunix_arg_NoValue, "-p Use for prehistoric databases which don't use bit 16" },
+ { "-w", glkunix_arg_NoValue, "-w Disable upper window" },
+ { "", glkunix_arg_ValueFollows, "filename file to load" },
+
+ { nullptr, glkunix_arg_End, nullptr }
+};
+*/
+
+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;
+ }
+
+ Output("\
+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");
+ LoadDatabase(gameFile, (Options & DEBUGGING) ? 1 : 0);
+
+ while (!shouldQuit()) {
+ glk_tick();
+
+ PerformActions(0, 0);
+
+ Look();
+
+ if (GetInput(&vb, &no) == -1)
+ continue;
+ switch (PerformActions(vb, no)) {
+ case -1:
+ Output("I don't understand your command. ");
+ break;
+ case -2:
+ Output("I can't do that yet. ");
+ 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)
+ Output("Light has run out! ");
+ else
+ Output("Your light has run out. ");
+ }
+ 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) {
+ Output("Light runs out in ");
+ OutputNumber(GameHeader.LightTime);
+ Output(" turns. ");
+ } else {
+ if (GameHeader.LightTime % 5 == 0)
+ Output("Your light is growing dim. ");
+ }
+ }
+ }
+ }
+ }
+}
+
+void Scott::initialize() {
+ /*
+ int argc = data->argc;
+ char **argv = data->argv;
+
+ if (argc < 1)
+ return 0;
+
+ while (argv[1])
+ {
+ if (*argv[1] != '-')
+ break;
+ switch (argv[1][1])
+ {
+ case 'y':
+ Options |= YOUARE;
+ break;
+ case 'i':
+ Options &= ~YOUARE;
+ break;
+ case 'd':
+ Options |= DEBUGGING;
+ break;
+ case 's':
+ Options |= SCOTTLIGHT;
+ break;
+ case 't':
+ Options |= TRS80_STYLE;
+ break;
+ case 'p':
+ Options |= PREHISTORIC_LAMP;
+ break;
+ case 'w':
+ split_screen = 0;
+ break;
+ }
+ argv++;
+ argc--;
+ }
+
+ if (argc == 2)
+ {
+ game_file = argv[1];
+#ifdef GARGLK
+ const char *s;
+ if ((s = strrchr(game_file, '/')) != nullptr || (s = strrchr(game_file, '\\')) != nullptr)
+ {
+ garglk_set_story_name(s + 1);
+ }
+ else
+ {
+ garglk_set_story_name(game_file);
+ }
+#endif
+ }
+ */
+}
+
+void Scott::Display(winid_t w, const char *fmt, ...) {
+ 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());
+}
+
+void Scott::Delay(int seconds) {
event_t ev;
+
+ if (!glk_gestalt(gestalt_Timer, 0))
+ return;
+
+ glk_request_timer_events(1000 * seconds);
+
+ do
+ {
+ glk_select(&ev);
+ } while (ev.type != evtype_Timer);
+
+ glk_request_timer_events(0);
+}
+
+void Scott::Fatal(const char *x) {
+ error(x);
+}
+
+void Scott::ClearScreen(void) {
+ glk_window_clear(Bottom);
+}
+
+void *Scott::MemAlloc(int size) {
+ void *t = (void *)malloc(size);
+ if (t == nullptr)
+ Fatal("Out of memory");
+ return t;
+}
+
+bool Scott::RandomPercent(uint n) {
+ return _random.getRandomNumber(99) < n;
+}
+
+int Scott::CountCarried(void) {
+ int ct = 0;
+ int n = 0;
+ while (ct <= GameHeader.NumItems) {
+ if (Items[ct].Location == CARRIED)
+ n++;
+ ct++;
+ }
+ return n;
+}
+
+const char *Scott::MapSynonym(const char *word) {
+ 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;
+}
+
+int Scott::MatchUpItem(const char *text, int loc) {
+ const char *word = MapSynonym(text);
+ 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;
+}
+
+char *Scott::ReadString(Common::SeekableReadStream *f)
+{
+ 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 != '"') {
+ Fatal("Initial quote expected");
+ }
+
+ do {
+ c = f->readByte();
+ if (c == EOF)
+ Fatal("EOF in string");
+ if (c == '"')
+ {
+ 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++] = '?';
+ } while (1);
+
+ tmp[ct] = 0;
+ t = (char *)MemAlloc(ct + 1);
+ memcpy(t, tmp, ct + 1);
+ return t;
+}
+
+void Scott::LoadDatabase(Common::SeekableReadStream *f, int loud) {
+ 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, &lt, &mn, &trm);
+
+ GameHeader.NumItems = ni;
+ Items = (Item *)MemAlloc(sizeof(Item)*(ni + 1));
+ GameHeader.NumActions = na;
+ Actions = (Action *)MemAlloc(sizeof(Action)*(na + 1));
+ GameHeader.NumWords = nw;
+ GameHeader.WordLength = wl;
+ Verbs = (const char **)MemAlloc(sizeof(char *)*(nw + 1));
+ Nouns = (const char **)MemAlloc(sizeof(char *)*(nw + 1));
+ GameHeader.NumRooms = nr;
+ Rooms = (Room *)MemAlloc(sizeof(Room)*(nr + 1));
+ GameHeader.MaxCarry = mc;
+ GameHeader.PlayerRoom = pr;
+ GameHeader.Treasures = tr;
+ GameHeader.LightTime = lt;
+ LightRefill = lt;
+ GameHeader.NumMessages = mn;
+ Messages = (const char **)MemAlloc(sizeof(char *)*(mn + 1));
+ 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) {
+ Verbs[ct] = ReadString(f);
+ Nouns[ct] = ReadString(f);
+ 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]);
+
+ rp->Text = ReadString(f);
+ ct++;
+ rp++;
+ }
+
+ ct = 0;
+ if (loud)
+ debug("Reading %d messages.", mn);
+ while (ct<mn + 1) {
+ Messages[ct] = ReadString(f);
+ ct++;
+ }
+
+ ct = 0;
+ if (loud)
+ debug("Reading %d items.", ni);
+ ip = Items;
+ while (ct < ni + 1) {
+ ip->Text = ReadString(f);
+ 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) {
+ free(ReadString(f));
+ 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);
+}
+
+void Scott::Output(const char *a) {
+ Display(Bottom, "%s", a);
+}
+
+void Scott::OutputNumber(int a) {
+ Display(Bottom, "%d", a);
+}
+
+void Scott::Look(void) {
+ 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)
+ Display(Top, "You can't see. It is too dark!\n");
+ else
+ Display(Top, "I can't see. It is too dark!\n");
+ if (Options & TRS80_STYLE)
+ Display(Top, TRS80_LINE);
+ return;
+ }
+ r = &Rooms[MyLoc];
+ if (*r->Text == '*')
+ Display(Top, "%s\n", r->Text + 1);
+ else {
+ if (Options&YOUARE)
+ Display(Top, "You are in a %s\n", r->Text);
+ else
+ Display(Top, "I'm in a %s\n", r->Text);
+ }
+
+ ct = 0;
+ f = 0;
+ Display(Top, "\nObvious exits: ");
+ while (ct<6) {
+ if (r->Exits[ct] != 0)
+ {
+ if (f == 0)
+ f = 1;
+ else
+ Display(Top, ", ");
+ Display(Top, "%s", ExitNames[ct]);
+ }
+ ct++;
+ }
+
+ if (f == 0)
+ Display(Top, "none");
+ Display(Top, ".\n");
+ ct = 0;
+ f = 0;
+ pos = 0;
+ while (ct <= GameHeader.NumItems) {
+ if (Items[ct].Location == MyLoc) {
+ if (f == 0) {
+ if (Options & YOUARE) {
+ Display(Top, "\nYou can also see: ");
+ pos = 18;
+ } else {
+ Display(Top, "\nI can also see: ");
+ pos = 16;
+ }
+ f++;
+ } else if (!(Options & TRS80_STYLE)) {
+ Display(Top, " - ");
+ pos += 3;
+ }
+ if (pos + (int)strlen(Items[ct].Text) > (Width - 10)) {
+ pos = 0;
+ Display(Top, "\n");
+ }
+ Display(Top, "%s", Items[ct].Text);
+ pos += strlen(Items[ct].Text);
+ if (Options & TRS80_STYLE) {
+ Display(Top, ". ");
+ pos += 2;
+ }
+ }
+ ct++;
+ }
+
+ Display(Top, "\n");
+ if (Options & TRS80_STYLE)
+ Display(Top, TRS80_LINE);
+}
+
+int Scott::WhichWord(const char *word, const char **list) {
+ 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;
+}
+
+void Scott::LineInput(char *buf, size_t n) {
+ event_t ev;
+
+ glk_request_line_event(Bottom, buf, n - 1, 0);
+
do {
glk_select(&ev);
- switch (ev.type) {
- default:
- /* do nothing */
+
+ if (ev.type == evtype_LineInput)
break;
- }
+ else if (ev.type == evtype_Arrange && split_screen)
+ Look();
} while (ev.type != evtype_Quit);
+
+ buf[ev.val1] = 0;
+}
+
+void Scott::SaveGame(void) {
+ strid_t file;
+ frefid_t ref;
+ int ct;
+ Common::String msg;
+
+ ref = glk_fileref_create_by_prompt(fileusage_TextMode | fileusage_SavedGame, filemode_Write, 0);
+ if (ref == nullptr) return;
+
+ file = glk_stream_open_file(ref, filemode_Write, 0);
+ glk_fileref_destroy(ref);
+ if (file == nullptr) return;
+
+ for (ct = 0; ct < 16; ct++) {
+ 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());
+
+ for (ct = 0; ct <= GameHeader.NumItems; ct++) {
+ msg = Common::String::format("%hd\n", (short)Items[ct].Location);
+ glk_put_string_stream(file, msg.c_str());
+ }
+
+ glk_stream_close(file, nullptr);
+ Output("Saved.\n");
+}
+
+void Scott::LoadGame(void) {
+ strid_t file;
+ frefid_t ref;
+ char buf[128];
+ int ct = 0;
+ short lo;
+ short DarkFlag;
+
+ ref = glk_fileref_create_by_prompt(fileusage_TextMode | fileusage_SavedGame, filemode_Read, 0);
+ if (ref == nullptr) return;
+
+ file = glk_stream_open_file(ref, filemode_Read, 0);
+ glk_fileref_destroy(ref);
+ if (file == nullptr) return;
+
+ 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",
+ &BitFlags, &DarkFlag, &MyLoc, &CurrentCounter, &SavedRoom,
+ &GameHeader.LightTime);
+
+ /* Backward compatibility */
+ if (DarkFlag)
+ 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;
+ }
+}
+
+int Scott::GetInput(int *vb, int *no) {
+ char buf[256];
+ char verb[10], noun[10];
+ int vc, nc;
+ int num;
+
+ do {
+ do {
+ Output("\nTell me what to do ? ");
+ LineInput(buf, sizeof buf);
+ num = sscanf(buf, "%9s %9s", verb, noun);
+ } while (num == 0 || *buf == '\n');
+
+ if (xstrcasecmp(verb, "restore") == 0) {
+ LoadGame();
+ 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;
+ }
+ }
+ nc = WhichWord(verb, Nouns);
+ /* The Scott Adams system has a hack to avoid typing 'go' */
+ if (nc >= 1 && nc <= 6) {
+ vc = 1;
+ } else {
+ vc = WhichWord(verb, Verbs);
+ nc = WhichWord(noun, Nouns);
+ }
+ *vb = vc;
+ *no = nc;
+ if (vc == -1) {
+ Output("You use word(s) I don't know! ");
+ }
+ } while (vc == -1);
+
+ strcpy(NounText, noun); /* Needed by GET/DROP hack */
+ return 0;
+}
+
+int Scott::PerformLine(int ct) {
+ 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:
+ if (CountCarried() == 0)
+ return 0;
+ break;
+ case 11:
+ if (CountCarried())
+ 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) {
+ Output(Messages[act[cc]]);
+ Output("\n");
+ } else if (act[cc] > 101) {
+ Output(Messages[act[cc] - 50]);
+ Output("\n");
+ }
+ else {
+ switch (act[cc]) {
+ case 0:/* NOP */
+ break;
+ case 52:
+ if (CountCarried() == GameHeader.MaxCarry)
+ {
+ if (Options&YOUARE)
+ Output("You are carrying too much. ");
+ else
+ Output("I've too much to carry! ");
+ 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)
+ Output("You are dead.\n");
+ else
+ Output("I am dead.\n");
+ 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:
+ doneit: Output("The game is now over.\n");
+ glk_exit();
+ 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)
+ Output("You have stored ");
+ else
+ Output("I've stored ");
+ OutputNumber(n);
+ Output(" treasures. On a scale of 0 to 100, that rates ");
+ OutputNumber((n * 100) / GameHeader.Treasures);
+ Output(".\n");
+ if (n == GameHeader.Treasures)
+ {
+ Output("Well done.\n");
+ goto doneit;
+ }
+ break;
+ }
+ case 66:
+ {
+ int i = 0;
+ int f = 0;
+ if (Options&YOUARE)
+ Output("You are carrying:\n");
+ else
+ Output("I'm carrying:\n");
+ while (i <= GameHeader.NumItems)
+ {
+ if (Items[i].Location == CARRIED)
+ {
+ if (f == 1)
+ {
+ if (Options & TRS80_STYLE)
+ Output(". ");
+ else
+ Output(" - ");
+ }
+ f = 1;
+ Output(Items[i].Text);
+ }
+ i++;
+ }
+ if (f == 0)
+ Output("Nothing");
+ Output(".\n");
+ 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:
+ ClearScreen(); /* pdd. */
+ break;
+ case 71:
+ SaveGame();
+ 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:
+ OutputNumber(CurrentCounter);
+ 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:
+ Output(NounText);
+ break;
+ case 85:
+ Output(NounText);
+ Output("\n");
+ break;
+ case 86:
+ Output("\n");
+ 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:
+ Delay(2);
+ 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;
+}
+
+int Scott::PerformActions(int vb, int no) {
+ 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) {
+ Output("Give me a direction too.");
+ 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)
+ Output("Dangerous to move in the dark! ");
+ nl = Rooms[MyLoc].Exits[no - 1];
+ if (nl != 0) {
+ MyLoc = nl;
+ return 0;
+ }
+ if (d) {
+ if (Options&YOUARE)
+ Output("You fell down and broke your neck. ");
+ else
+ Output("I fell down and broke my neck. ");
+ glk_exit();
+ }
+ if (Options&YOUARE)
+ Output("You can't go in that direction. ");
+ else
+ Output("I can't go in that direction. ");
+ 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)) {
+ if ((vv == 0 && RandomPercent(nv)) || doagain ||
+ (vv != 0 && (nv == no || nv == 0))) {
+ int f2;
+ if (fl == -1)
+ fl = -2;
+ if ((f2 = PerformLine(ct)) > 0) {
+ /* 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) {
+ Output("It is dark.\n");
+ return 0;
+ }
+ while (i <= GameHeader.NumItems) {
+ if (Items[i].Location == MyLoc && Items[i].AutoGet != nullptr && Items[i].AutoGet[0] != '*') {
+ no = WhichWord(Items[i].AutoGet, Nouns);
+ disable_sysfunc = 1; /* Don't recurse into auto get ! */
+ PerformActions(vb, no); /* Recursively check each items table code */
+ disable_sysfunc = 0;
+ if (CountCarried() == GameHeader.MaxCarry) {
+ if (Options&YOUARE)
+ Output("You are carrying too much. ");
+ else
+ Output("I've too much to carry. ");
+ return 0;
+ }
+ Items[i].Location = CARRIED;
+ Output(Items[i].Text);
+ Output(": O.K.\n");
+ f = 1;
+ }
+ i++;
+ }
+ if (f == 0)
+ Output("Nothing taken.");
+ return 0;
+ }
+ if (no == -1)
+ {
+ Output("What ? ");
+ return 0;
+ }
+ if (CountCarried() == GameHeader.MaxCarry)
+ {
+ if (Options&YOUARE)
+ Output("You are carrying too much. ");
+ else
+ Output("I've too much to carry. ");
+ return 0;
+ }
+ item = MatchUpItem(NounText, MyLoc);
+ if (item == -1)
+ {
+ if (Options&YOUARE)
+ Output("It is beyond your power to do that. ");
+ else
+ Output("It's beyond my power to do that. ");
+ return 0;
+ }
+ Items[item].Location = CARRIED;
+ Output("O.K. ");
+ 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] != '*') {
+ no = WhichWord(Items[i].AutoGet, Nouns);
+ disable_sysfunc = 1;
+ PerformActions(vb, no);
+ disable_sysfunc = 0;
+ Items[i].Location = MyLoc;
+ Output(Items[i].Text);
+ Output(": O.K.\n");
+ f = 1;
+ }
+ i++;
+ }
+ if (f == 0)
+ Output("Nothing dropped.\n");
+ return 0;
+ }
+ if (no == -1) {
+ Output("What ? ");
+ return 0;
+ }
+ item = MatchUpItem(NounText, CARRIED);
+ if (item == -1) {
+ if (Options&YOUARE)
+ Output("It's beyond your power to do that.\n");
+ else
+ Output("It's beyond my power to do that.\n");
+ return 0;
+ }
+ Items[item].Location = MyLoc;
+ Output("O.K. ");
+ 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);
+ unsigned char c = '\0';
+
+ for (size_t idx = 0; idx < count; ++idx) {
+ if (idx > 0) {
+ while (f->pos() < f->size() && Common::isSpace(c))
+ c = f->readByte();
+ } else {
+ 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);
}
} // End of namespace Scott
diff --git a/engines/gargoyle/scott/scott.h b/engines/gargoyle/scott/scott.h
index 0e7d85cc72..52ad560ebe 100644
--- a/engines/gargoyle/scott/scott.h
+++ b/engines/gargoyle/scott/scott.h
@@ -45,6 +45,9 @@ namespace Scott {
#define TRS80_STYLE 8 // Display in style used on TRS-80
#define PREHISTORIC_LAMP 16 // Destroy the lamp (very old databases)
+#define TRS80_LINE "\n<------------------------------------------------------------>\n"
+#define MyLoc (GameHeader.PlayerRoom)
+
struct Header {
int Unknown;
int NumItems;
@@ -58,17 +61,30 @@ struct Header {
int LightTime;
int NumMessages;
int TreasureRoom;
+
+ Header() : Unknown(0), NumItems(0), NumActions(0), NumWords(0), NumRooms(0),
+ MaxCarry(0), PlayerRoom(0), Treasures(0), WordLength(0), LightTime(0),
+ NumMessages(0), TreasureRoom(0) {}
};
struct Action {
uint Vocab;
uint Condition[5];
uint action[2];
+
+ Action() : Vocab(0) {
+ Common::fill(&Condition[0], &Condition[5], 0);
+ Common::fill(&action[0], &action[2], 0);
+ }
};
struct Room {
char *Text;
short Exits[6];
+
+ Room() : Text(0) {
+ Common::fill(&Exits[0], &Exits[6], 0);
+ }
};
struct Item {
@@ -76,28 +92,84 @@ struct Item {
byte Location;
byte InitialLoc;
char *AutoGet;
+
+ Item() : Text(nullptr), Location(0), InitialLoc(0), AutoGet(nullptr) {}
};
struct Tail {
int Version;
int AdventureNumber;
int Unknown;
+
+ Tail() : Version(0), AdventureNumber(0), Unknown(0) {}
};
/**
* Scott Adams game interpreter
*/
class Scott : public Glk {
+private:
+ Header GameHeader;
+ Item *Items;
+ Room *Rooms;
+ const char **Verbs;
+ const char **Nouns;
+ const char **Messages;
+ Action *Actions;
+ int LightRefill;
+ char NounText[16];
+ int Counters[16]; ///< Range unknown
+ int CurrentCounter;
+ int SavedRoom;
+ int RoomSaved[16]; ///< Range unknown
+ int Options; ///< Option flags set
+ int Width; ///< Terminal width
+ int TopHeight; ///< Height of top window
+
+ bool split_screen;
+ winid_t Bottom, Top;
+ uint32 BitFlags; ///< Might be >32 flags - I haven't seen >32 yet
+private:
+ /**
+ * Initialization code
+ */
+ void initialize();
+
+ void Display(winid_t w, const char *fmt, ...);
+ void Delay(int seconds);
+ void Fatal(const char *x);
+ void ClearScreen(void);
+ void *MemAlloc(int size);
+ bool RandomPercent(uint n);
+ int CountCarried(void);
+ const char *MapSynonym(const char *word);
+ int MatchUpItem(const char *text, int loc);
+ char *ReadString(Common::SeekableReadStream *f);
+ void LoadDatabase(Common::SeekableReadStream *f, int loud);
+ void Output(const char *a);
+ void OutputNumber(int a);
+ void Look(void);
+ int WhichWord(const char *word, const char **list);
+ void LineInput(char *buf, size_t n);
+ void SaveGame(void);
+ void LoadGame(void);
+ int GetInput(int *vb, int *no);
+ int PerformLine(int ct);
+ int PerformActions(int vb, int no);
+
+ int xstrcasecmp(const char *, const char *);
+ int xstrncasecmp(const char *, const char *, size_t);
+ void readInts(Common::SeekableReadStream *f, size_t count, ...);
public:
/**
* Constructor
*/
- Scott(OSystem *syst, const GargoyleGameDescription *gameDesc) : Glk(syst, gameDesc) {}
+ Scott(OSystem *syst, const GargoyleGameDescription *gameDesc);
/**
* Execute the game
*/
- virtual void main();
+ virtual void runGame(Common::SeekableReadStream *gameFile) override;
};
} // End of namespace Scott