Initial commit
This commit is contained in:
87
engines/glk/comprehend/charset.cpp
Normal file
87
engines/glk/comprehend/charset.cpp
Normal file
@@ -0,0 +1,87 @@
|
||||
/* 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/comprehend/charset.h"
|
||||
#include "common/file.h"
|
||||
#include "common/md5.h"
|
||||
#include "graphics/surface.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace Comprehend {
|
||||
|
||||
void FixedFont::drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const {
|
||||
assert(dst->format.bytesPerPixel == 4);
|
||||
assert(chr >= 32 && chr < 128);
|
||||
|
||||
for (int yp = 0; yp < 8; ++yp) {
|
||||
if ((y + yp) < 0 || (y + yp) >= dst->h)
|
||||
continue;
|
||||
|
||||
uint32 *lineP = (uint32 *)dst->getBasePtr(x, y + yp);
|
||||
byte bits = _data[chr - 32][yp];
|
||||
|
||||
for (int xp = x; xp < (x + 8); ++xp, ++lineP, bits >>= 1) {
|
||||
if ((xp >= 0) && (xp < dst->w) && (bits & 1))
|
||||
*lineP = color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------*/
|
||||
|
||||
CharSet::CharSet() : FixedFont() {
|
||||
Common::File f;
|
||||
if (!f.open("charset.gda"))
|
||||
error("Could not open char set");
|
||||
|
||||
uint version = f.readUint16LE();
|
||||
if (version != 0x1100)
|
||||
error("Unknown char set version");
|
||||
|
||||
f.seek(4);
|
||||
for (int idx = 0; idx < 128 - 32; ++idx)
|
||||
f.read(&_data[idx][0], 8);
|
||||
|
||||
f.close();
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------*/
|
||||
|
||||
TalismanFont::TalismanFont() : FixedFont() {
|
||||
// Extra strings are (annoyingly) stored in the game binary
|
||||
Common::File f;
|
||||
if (!f.open("novel.exe"))
|
||||
error("novel.exe is a required file");
|
||||
|
||||
Common::String md5 = Common::computeStreamMD5AsString(f, 1024);
|
||||
|
||||
if (md5 == "0e7f002971acdb055f439020363512ce" || md5 == "2e18c88ce352ebea3e14177703a0485f") {
|
||||
for (int idx = 0; idx < 128 - 32; ++idx)
|
||||
f.read(&_data[idx][0], 8);
|
||||
} else {
|
||||
error("Unrecognised novel.exe encountered");
|
||||
}
|
||||
|
||||
f.close();
|
||||
}
|
||||
|
||||
} // namespace Comprehend
|
||||
} // namespace Glk
|
||||
103
engines/glk/comprehend/charset.h
Normal file
103
engines/glk/comprehend/charset.h
Normal file
@@ -0,0 +1,103 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GLK_COMPREHEND_CHARSET_H
|
||||
#define GLK_COMPREHEND_CHARSET_H
|
||||
|
||||
#include "graphics/font.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace Comprehend {
|
||||
|
||||
/**
|
||||
* Fixed width base font
|
||||
*/
|
||||
class FixedFont : public Graphics::Font {
|
||||
protected:
|
||||
byte _data[128 - 32][8];
|
||||
|
||||
public:
|
||||
~FixedFont() override {}
|
||||
|
||||
/**
|
||||
*/
|
||||
int getFontHeight() const override {
|
||||
return 8;
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the maximum width of the font.
|
||||
*/
|
||||
int getMaxCharWidth() const override {
|
||||
return 8;
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the width of a specific character.
|
||||
*/
|
||||
int getCharWidth(uint32 chr) const override {
|
||||
return 8;
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the kerning offset between two characters.
|
||||
*/
|
||||
int getKerningOffset(uint32 left, uint32 right) const override {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the bounding box of a character. It is assumed that
|
||||
* the character shall be drawn at position (0, 0).
|
||||
*/
|
||||
Common::Rect getBoundingBox(uint32 chr) const override {
|
||||
assert(chr < 127);
|
||||
return Common::Rect(0, 0, 8, 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw a character at a specific point on a surface.
|
||||
*/
|
||||
void drawChar(Graphics::Surface *dst, uint32 chr, int x, int y, uint32 color) const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* Font loaded from charset.gda
|
||||
*/
|
||||
class CharSet : public FixedFont {
|
||||
public:
|
||||
CharSet();
|
||||
~CharSet() override {}
|
||||
};
|
||||
|
||||
/**
|
||||
* Talisman font directly from the executable
|
||||
*/
|
||||
class TalismanFont : public FixedFont {
|
||||
public:
|
||||
TalismanFont();
|
||||
~TalismanFont() override {}
|
||||
};
|
||||
|
||||
} // End of namespace Comprehend
|
||||
} // End of namespace Glk
|
||||
|
||||
#endif
|
||||
296
engines/glk/comprehend/comprehend.cpp
Normal file
296
engines/glk/comprehend/comprehend.cpp
Normal file
@@ -0,0 +1,296 @@
|
||||
/* 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/comprehend/comprehend.h"
|
||||
#include "glk/comprehend/debugger.h"
|
||||
#include "glk/comprehend/draw_surface.h"
|
||||
#include "glk/comprehend/game.h"
|
||||
#include "glk/comprehend/game_cc.h"
|
||||
#include "glk/comprehend/game_data.h"
|
||||
#include "glk/comprehend/game_oo.h"
|
||||
#include "glk/comprehend/game_tm.h"
|
||||
#include "glk/comprehend/game_tr1.h"
|
||||
#include "glk/comprehend/game_tr2.h"
|
||||
#include "glk/comprehend/pics.h"
|
||||
#include "glk/quetzal.h"
|
||||
#include "common/config-manager.h"
|
||||
#include "common/ustr.h"
|
||||
#include "engines/util.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace Comprehend {
|
||||
|
||||
// Even with no ScummVM scaling, internally we do a 2x scaling to
|
||||
// render on a 640x480 window, to allow for better looking text
|
||||
#define SCALE_FACTOR 2
|
||||
|
||||
Comprehend *g_comprehend;
|
||||
|
||||
Comprehend::Comprehend(OSystem *syst, const GlkGameDescription &gameDesc) : GlkAPI(syst, gameDesc),
|
||||
_topWindow(nullptr), _bottomWindow(nullptr), _roomDescWindow(nullptr),
|
||||
_drawSurface(nullptr), _game(nullptr), _pics(nullptr), _saveSlot(-1),
|
||||
_graphicsEnabled(true), _drawFlags(0), _disableSaves(false) {
|
||||
g_comprehend = this;
|
||||
}
|
||||
|
||||
Comprehend::~Comprehend() {
|
||||
delete _drawSurface;
|
||||
delete _game;
|
||||
SearchMan.remove("Pics"); // This also deletes it
|
||||
|
||||
g_comprehend = nullptr;
|
||||
}
|
||||
|
||||
void Comprehend::initGraphicsMode() {
|
||||
Graphics::PixelFormat pixelFormat(2, 5, 6, 5, 0, 11, 5, 0, 0);
|
||||
initGraphics(640, 400, &pixelFormat);
|
||||
}
|
||||
|
||||
void Comprehend::createConfiguration() {
|
||||
GlkAPI::createConfiguration();
|
||||
switchToWhiteOnBlack();
|
||||
}
|
||||
|
||||
void Comprehend::runGame() {
|
||||
initialize();
|
||||
|
||||
// Lookup game
|
||||
createGame();
|
||||
|
||||
_game->loadGame();
|
||||
_game->playGame();
|
||||
|
||||
deinitialize();
|
||||
}
|
||||
|
||||
void Comprehend::initialize() {
|
||||
_bottomWindow = (TextBufferWindow *)glk_window_open(nullptr, 0, 0, wintype_TextBuffer, 1);
|
||||
glk_set_window(_bottomWindow);
|
||||
|
||||
showGraphics();
|
||||
_topWindow->fillRect(0, Rect(0, 0, _topWindow->_w, _topWindow->_h));
|
||||
|
||||
// Initialize drawing surface, and the archive that abstracts
|
||||
// the room and item graphics as as individual files
|
||||
_drawSurface = new DrawSurface();
|
||||
_pics = new Pics();
|
||||
SearchMan.add("Pics", _pics, 99, true);
|
||||
|
||||
// Check for savegame to load
|
||||
_saveSlot = ConfMan.hasKey("save_slot") ? ConfMan.getInt("save_slot") : -1;
|
||||
}
|
||||
|
||||
void Comprehend::deinitialize() {
|
||||
glk_window_close(_topWindow);
|
||||
glk_window_close(_bottomWindow);
|
||||
glk_window_close(_roomDescWindow);
|
||||
}
|
||||
|
||||
void Comprehend::createDebugger() {
|
||||
setDebugger(new Debugger());
|
||||
}
|
||||
|
||||
void Comprehend::createGame() {
|
||||
if (_gameDescription._gameId == "crimsoncrown")
|
||||
_game = new CrimsonCrownGame();
|
||||
else if (_gameDescription._gameId == "ootopos")
|
||||
_game = new OOToposGame();
|
||||
else if (_gameDescription._gameId == "talisman")
|
||||
_game = new TalismanGame();
|
||||
else if (_gameDescription._gameId == "transylvania")
|
||||
_game = new TransylvaniaGame1();
|
||||
else if (_gameDescription._gameId == "transylvaniav2")
|
||||
_game = new TransylvaniaGame2();
|
||||
else
|
||||
error("Unknown game");
|
||||
}
|
||||
|
||||
void Comprehend::print(const char *fmt, ...) {
|
||||
va_list argp;
|
||||
va_start(argp, fmt);
|
||||
Common::String msg = Common::String::vformat(fmt, argp);
|
||||
va_end(argp);
|
||||
|
||||
glk_put_string_stream(glk_window_get_stream(_bottomWindow), msg.c_str());
|
||||
}
|
||||
|
||||
void Comprehend::print_u32_internal(const Common::U32String *fmt, ...) {
|
||||
Common::U32String outputMsg;
|
||||
|
||||
va_list argp;
|
||||
va_start(argp, fmt);
|
||||
Common::U32String::vformat(fmt->begin(), fmt->end(), outputMsg, argp);
|
||||
va_end(argp);
|
||||
|
||||
glk_put_string_stream_uni(glk_window_get_stream(_bottomWindow), outputMsg.u32_str());
|
||||
}
|
||||
|
||||
void Comprehend::printRoomDesc(const Common::String &desc) {
|
||||
if (_roomDescWindow) {
|
||||
glk_window_clear(_roomDescWindow);
|
||||
|
||||
// Get the grid width and do a word wrap
|
||||
uint width;
|
||||
glk_window_get_size(_roomDescWindow, &width, nullptr);
|
||||
Common::String str = desc;
|
||||
str.wordWrap(width - 2);
|
||||
str += '\n';
|
||||
|
||||
// Display the room description
|
||||
while (!str.empty()) {
|
||||
size_t idx = str.findFirstOf('\n');
|
||||
Common::String line = Common::String::format(" %s", Common::String(str.c_str(), str.c_str() + idx + 1).c_str());
|
||||
glk_put_string_stream(glk_window_get_stream(_roomDescWindow), line.c_str());
|
||||
|
||||
str = Common::String(str.c_str() + idx + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Comprehend::readLine(char *buffer, size_t maxLen) {
|
||||
event_t ev;
|
||||
|
||||
glk_request_line_event(_bottomWindow, buffer, maxLen - 1, 0);
|
||||
|
||||
for (;;) {
|
||||
glk_select(&ev);
|
||||
if (ev.type == evtype_Quit) {
|
||||
glk_cancel_line_event(_bottomWindow, &ev);
|
||||
return;
|
||||
} else if (ev.type == evtype_LineInput)
|
||||
break;
|
||||
}
|
||||
|
||||
buffer[ev.val1] = 0;
|
||||
debug(1, "\n> %s", buffer);
|
||||
}
|
||||
|
||||
int Comprehend::readChar() {
|
||||
glk_request_char_event(_bottomWindow);
|
||||
setDisableSaves(true);
|
||||
|
||||
event_t ev;
|
||||
while (ev.type != evtype_CharInput) {
|
||||
glk_select(&ev);
|
||||
|
||||
if (ev.type == evtype_Quit) {
|
||||
glk_cancel_char_event(_bottomWindow);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
setDisableSaves(false);
|
||||
return ev.val1;
|
||||
}
|
||||
|
||||
Common::Error Comprehend::readSaveData(Common::SeekableReadStream *rs) {
|
||||
Common::Serializer s(rs, nullptr);
|
||||
_game->synchronizeSave(s);
|
||||
|
||||
_game->_updateFlags = UPDATE_ALL;
|
||||
|
||||
if (isInputLineActive()) {
|
||||
// Restored game using GMM, so update grpahics and print room description
|
||||
g_comprehend->print("\n");
|
||||
_game->update();
|
||||
|
||||
g_comprehend->print("> ");
|
||||
}
|
||||
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
Common::Error Comprehend::writeGameData(Common::WriteStream *ws) {
|
||||
Common::Serializer s(nullptr, ws);
|
||||
_game->synchronizeSave(s);
|
||||
|
||||
return Common::kNoError;
|
||||
}
|
||||
|
||||
bool Comprehend::loadLauncherSavegameIfNeeded() {
|
||||
if (_saveSlot != -1) {
|
||||
return loadGameState(_saveSlot).getCode() == Common::kNoError;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Comprehend::drawPicture(uint pictureNum) {
|
||||
if (_topWindow) {
|
||||
// Clear the picture cache before each drawing in OO-Topos. Wearing the goggles
|
||||
// can producing different versions of the same scene, so we can't cache it
|
||||
if (_gameDescription._gameId == "ootopos")
|
||||
_pictures->clear();
|
||||
|
||||
glk_image_draw_scaled(_topWindow, pictureNum,
|
||||
20 * SCALE_FACTOR, 0, G_RENDER_WIDTH * SCALE_FACTOR, G_RENDER_HEIGHT * SCALE_FACTOR);
|
||||
}
|
||||
}
|
||||
|
||||
void Comprehend::drawLocationPicture(int pictureNum, bool clearBg) {
|
||||
drawPicture(pictureNum + (clearBg ? LOCATIONS_OFFSET : LOCATIONS_NO_BG_OFFSET));
|
||||
}
|
||||
|
||||
void Comprehend::drawItemPicture(int pictureNum) {
|
||||
drawPicture(pictureNum + ITEMS_OFFSET);
|
||||
}
|
||||
|
||||
void Comprehend::clearScreen(bool isBright) {
|
||||
drawPicture(isBright ? BRIGHT_ROOM : DARK_ROOM);
|
||||
}
|
||||
|
||||
bool Comprehend::toggleGraphics() {
|
||||
if (_topWindow) {
|
||||
// Remove the picture window
|
||||
glk_window_close(_topWindow);
|
||||
_topWindow = nullptr;
|
||||
_graphicsEnabled = false;
|
||||
|
||||
// Add the room description window
|
||||
_roomDescWindow = (TextGridWindow *)glk_window_open(_bottomWindow,
|
||||
winmethod_Above | winmethod_Fixed, 5, wintype_TextGrid, 1);
|
||||
return false;
|
||||
|
||||
} else {
|
||||
glk_window_close(_roomDescWindow);
|
||||
_roomDescWindow = nullptr;
|
||||
|
||||
// Create the window again
|
||||
showGraphics();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void Comprehend::showGraphics() {
|
||||
if (!_topWindow) {
|
||||
_topWindow = (GraphicsWindow *)glk_window_open(_bottomWindow,
|
||||
winmethod_Above | winmethod_Fixed,
|
||||
160 * SCALE_FACTOR, wintype_Graphics, 2);
|
||||
_graphicsEnabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool Comprehend::isInputLineActive() const {
|
||||
return _bottomWindow->_lineRequest || _bottomWindow->_lineRequestUni;
|
||||
}
|
||||
|
||||
} // namespace Comprehend
|
||||
} // namespace Glk
|
||||
235
engines/glk/comprehend/comprehend.h
Normal file
235
engines/glk/comprehend/comprehend.h
Normal file
@@ -0,0 +1,235 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GLK_COMPREHEND_COMPREHEND_H
|
||||
#define GLK_COMPREHEND_COMPREHEND_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "glk/comprehend/game.h"
|
||||
#include "glk/glk_api.h"
|
||||
#include "glk/window_graphics.h"
|
||||
#include "glk/window_text_buffer.h"
|
||||
#include "glk/window_text_grid.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace Comprehend {
|
||||
|
||||
class DrawSurface;
|
||||
class Pics;
|
||||
|
||||
#define EXTRA_STRING_TABLE(x) (0x8200 | (x))
|
||||
|
||||
struct GameStrings {
|
||||
uint16 game_restart;
|
||||
};
|
||||
|
||||
/**
|
||||
* Comprehend engine
|
||||
*/
|
||||
class Comprehend : public GlkAPI {
|
||||
private:
|
||||
int _saveSlot; ///< Save slot when loading savegame from launcher
|
||||
bool _graphicsEnabled;
|
||||
bool _disableSaves;
|
||||
public:
|
||||
GraphicsWindow *_topWindow;
|
||||
TextGridWindow *_roomDescWindow;
|
||||
TextBufferWindow *_bottomWindow;
|
||||
DrawSurface *_drawSurface;
|
||||
ComprehendGame *_game;
|
||||
Pics *_pics;
|
||||
uint _drawFlags;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Initialization code
|
||||
*/
|
||||
void initialize();
|
||||
|
||||
/**
|
||||
* Deinitialization
|
||||
*/
|
||||
void deinitialize();
|
||||
|
||||
/**
|
||||
* Create the debugger
|
||||
*/
|
||||
void createDebugger() override;
|
||||
|
||||
/**
|
||||
* Creates the appropriate game class
|
||||
*/
|
||||
void createGame();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Loads the configuration
|
||||
*/
|
||||
void createConfiguration() override;
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
Comprehend(OSystem *syst, const GlkGameDescription &gameDesc);
|
||||
|
||||
~Comprehend() override;
|
||||
|
||||
/**
|
||||
* Returns the running interpreter type
|
||||
*/
|
||||
InterpreterType getInterpreterType() const override {
|
||||
return INTERPRETER_SCOTT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the graphics
|
||||
*/
|
||||
void initGraphicsMode() override;
|
||||
|
||||
/**
|
||||
* Execute the game
|
||||
*/
|
||||
void runGame() override;
|
||||
|
||||
/**
|
||||
* Handles loading a savegame selected from the launcher
|
||||
*/
|
||||
bool loadLauncherSavegameIfNeeded();
|
||||
|
||||
/**
|
||||
* Load a savegame from the passed Quetzal file chunk stream
|
||||
*/
|
||||
Common::Error readSaveData(Common::SeekableReadStream *rs) override;
|
||||
|
||||
/**
|
||||
* Save the game. The passed write stream represents access to the UMem chunk
|
||||
* in the Quetzal save file that will be created
|
||||
*/
|
||||
Common::Error writeGameData(Common::WriteStream *ws) override;
|
||||
|
||||
/**
|
||||
* Print string to the buffer window
|
||||
*/
|
||||
void print(const char *fmt, ...);
|
||||
|
||||
/**
|
||||
* Print unicode-string to the buffer window
|
||||
*/
|
||||
template<class... TParam>
|
||||
void print(const Common::U32String &fmt, TParam... param);
|
||||
|
||||
/**
|
||||
* Prints the room description in the room description window
|
||||
*/
|
||||
void printRoomDesc(const Common::String &desc);
|
||||
|
||||
/**
|
||||
* Read an input line
|
||||
*/
|
||||
void readLine(char *buffer, size_t maxLen);
|
||||
|
||||
/**
|
||||
* Read in a character
|
||||
*/
|
||||
int readChar();
|
||||
|
||||
/**
|
||||
* Draw a picture
|
||||
*/
|
||||
void drawPicture(uint pictureNum);
|
||||
|
||||
/**
|
||||
* Draw a location image
|
||||
*/
|
||||
void drawLocationPicture(int pictureNum, bool clearBg = true);
|
||||
|
||||
/**
|
||||
* Draw an item image
|
||||
*/
|
||||
void drawItemPicture(int pictureNum);
|
||||
|
||||
/**
|
||||
* Clear the picture area
|
||||
*/
|
||||
void clearScreen(bool isBright);
|
||||
|
||||
/**
|
||||
* Toggles whether the picture window is visible
|
||||
*/
|
||||
bool toggleGraphics();
|
||||
|
||||
/**
|
||||
* Ensures the picture window is visible
|
||||
*/
|
||||
void showGraphics();
|
||||
|
||||
/**
|
||||
* Returns true if the graphics area is visible
|
||||
*/
|
||||
bool isGraphicsEnabled() const {
|
||||
return _graphicsEnabled;
|
||||
}
|
||||
|
||||
ComprehendGame *getGame() const {
|
||||
return _game;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if a savegame can be loaded
|
||||
*/
|
||||
bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override {
|
||||
return !_disableSaves && GlkAPI::canLoadGameStateCurrently();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the game can be saved
|
||||
*/
|
||||
bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override {
|
||||
return !_disableSaves && GlkAPI::canSaveGameStateCurrently();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether saving and loading is currently available
|
||||
*/
|
||||
void setDisableSaves(bool flag) {
|
||||
_disableSaves = flag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if an input line is currently active
|
||||
*/
|
||||
bool isInputLineActive() const;
|
||||
|
||||
private:
|
||||
void print_u32_internal(const Common::U32String *fmt, ...);
|
||||
};
|
||||
|
||||
template<class... TParam>
|
||||
void Comprehend::print(const Common::U32String &fmt, TParam... param) {
|
||||
print_u32_internal(&fmt, Common::forward<TParam>(param)...);
|
||||
}
|
||||
|
||||
extern Comprehend *g_comprehend;
|
||||
|
||||
} // End of namespace Comprehend
|
||||
} // End of namespace Glk
|
||||
|
||||
#endif
|
||||
166
engines/glk/comprehend/debugger.cpp
Normal file
166
engines/glk/comprehend/debugger.cpp
Normal file
@@ -0,0 +1,166 @@
|
||||
/* 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/comprehend/debugger.h"
|
||||
#include "glk/comprehend/comprehend.h"
|
||||
#include "glk/comprehend/pics.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace Comprehend {
|
||||
|
||||
Debugger *g_debugger;
|
||||
|
||||
Debugger::Debugger() : Glk::Debugger(), _invLimit(true) {
|
||||
g_debugger = this;
|
||||
registerCmd("dump", WRAP_METHOD(Debugger, cmdDump));
|
||||
registerCmd("floodfills", WRAP_METHOD(Debugger, cmdFloodfills));
|
||||
registerCmd("room", WRAP_METHOD(Debugger, cmdRoom));
|
||||
registerCmd("itemroom", WRAP_METHOD(Debugger, cmdItemRoom));
|
||||
registerCmd("findstring", WRAP_METHOD(Debugger, cmdFindString));
|
||||
registerCmd("draw", WRAP_METHOD(Debugger, cmdDraw));
|
||||
registerCmd("invlimit", WRAP_METHOD(Debugger, cmdInventoryLimit));
|
||||
}
|
||||
|
||||
Debugger::~Debugger() {
|
||||
g_debugger = nullptr;
|
||||
}
|
||||
|
||||
void Debugger::print(const char *fmt, ...) {
|
||||
va_list argp;
|
||||
va_start(argp, fmt);
|
||||
Common::String msg = Common::String::vformat(fmt, argp);
|
||||
va_end(argp);
|
||||
|
||||
debugPrintf("%s", msg.c_str());
|
||||
debugN("%s", msg.c_str());
|
||||
}
|
||||
|
||||
bool Debugger::cmdDump(int argc, const char **argv) {
|
||||
Common::String type = (argc >= 2) ? argv[1] : "";
|
||||
uint param = (argc == 3) ? strToInt(argv[2]) : 0;
|
||||
ComprehendGame *game = g_comprehend->_game;
|
||||
|
||||
if (!dumpGameData(game, type, param))
|
||||
debugPrintf("Unknown dump option\n");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Debugger::cmdFloodfills(int argc, const char **argv) {
|
||||
if (argc == 2 && !strcmp(argv[1], "off")) {
|
||||
g_comprehend->_drawFlags |= IMAGEF_NO_PAINTING;
|
||||
debugPrintf("Floodfills are off\n");
|
||||
} else {
|
||||
g_comprehend->_drawFlags &= ~IMAGEF_NO_PAINTING;
|
||||
debugPrintf("Floodfills are on\n");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Debugger::cmdRoom(int argc, const char **argv) {
|
||||
ComprehendGame *game = g_comprehend->getGame();
|
||||
|
||||
if (argc == 1) {
|
||||
debugPrintf("Current room = %d\n", game->_currentRoom);
|
||||
return true;
|
||||
} else {
|
||||
game->move_to(strToInt(argv[1]));
|
||||
game->update_graphics();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Debugger::cmdItemRoom(int argc, const char **argv) {
|
||||
ComprehendGame *game = g_comprehend->getGame();
|
||||
|
||||
if (argc == 1) {
|
||||
debugPrintf("itemroom <item> [<room>]\n");
|
||||
} else {
|
||||
Item *item = game->get_item(strToInt(argv[1]));
|
||||
|
||||
if (argc == 2) {
|
||||
debugPrintf("Item room = %d\n", item->_room);
|
||||
} else {
|
||||
int room = strToInt(argv[2]);
|
||||
if (room == 0)
|
||||
room = game->_currentRoom;
|
||||
bool visibleChange = item->_room == game->_currentRoom ||
|
||||
room == game->_currentRoom;
|
||||
|
||||
item->_room = room;
|
||||
|
||||
if (visibleChange) {
|
||||
game->_updateFlags |= UPDATE_GRAPHICS_ITEMS;
|
||||
game->update_graphics();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Debugger::cmdFindString(int argc, const char **argv) {
|
||||
ComprehendGame *game = g_comprehend->getGame();
|
||||
|
||||
if (argc == 1) {
|
||||
debugPrintf("findstring <string>\n");
|
||||
|
||||
} else {
|
||||
for (int arrNum = 0; arrNum < 2; ++arrNum) {
|
||||
const StringTable &table = (arrNum == 0) ? game->_strings : game->_strings2;
|
||||
const char *name = (arrNum == 0) ? "_strings" : "_strings2";
|
||||
|
||||
for (uint idx = 0; idx < table.size(); ++idx) {
|
||||
if (table[idx].contains(argv[1]))
|
||||
debugPrintf("%s[%u] = %s\n", name, idx, table[idx].c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Debugger::cmdDraw(int argc, const char **argv) {
|
||||
if (argc == 1) {
|
||||
debugPrintf("draw <number>\n");
|
||||
return true;
|
||||
} else {
|
||||
g_comprehend->drawLocationPicture(strToInt(argv[1]), true);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool Debugger::cmdInventoryLimit(int argc, const char **argv) {
|
||||
if (argc == 1) {
|
||||
debugPrintf("invlimit on|off\n");
|
||||
} else {
|
||||
_invLimit = !strcmp(argv[1], "on") || !strcmp(argv[1], "true");
|
||||
debugPrintf("inventory limit is now %s\n", _invLimit ? "on" : "off");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Comprehend
|
||||
} // namespace Glk
|
||||
83
engines/glk/comprehend/debugger.h
Normal file
83
engines/glk/comprehend/debugger.h
Normal file
@@ -0,0 +1,83 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GLK_COMPREHEND_DEBUGGER_H
|
||||
#define GLK_COMPREHEND_DEBUGGER_H
|
||||
|
||||
#include "glk/debugger.h"
|
||||
#include "glk/comprehend/debugger_dumper.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace Comprehend {
|
||||
|
||||
class Debugger : public Glk::Debugger, public DebuggerDumper {
|
||||
private:
|
||||
/**
|
||||
* Dump data
|
||||
*/
|
||||
bool cmdDump(int argc, const char **argv);
|
||||
|
||||
/**
|
||||
* Sets whether floodfills are done when rendering images
|
||||
*/
|
||||
bool cmdFloodfills(int argc, const char **argv);
|
||||
|
||||
/**
|
||||
* Sets or lists the current room
|
||||
*/
|
||||
bool cmdRoom(int argc, const char **argv);
|
||||
|
||||
/**
|
||||
* Sets or lists the room for an item
|
||||
*/
|
||||
bool cmdItemRoom(int argc, const char **argv);
|
||||
|
||||
/**
|
||||
* Find a string given a partial specified
|
||||
*/
|
||||
bool cmdFindString(int argc, const char **argv);
|
||||
|
||||
/**
|
||||
* Draw an image to the screen
|
||||
*/
|
||||
bool cmdDraw(int argc, const char **argv);
|
||||
|
||||
/**
|
||||
* Flags whether to turn the inventory limit on or off
|
||||
*/
|
||||
bool cmdInventoryLimit(int argc, const char **argv);
|
||||
|
||||
protected:
|
||||
void print(const char *fmt, ...) override;
|
||||
|
||||
public:
|
||||
bool _invLimit;
|
||||
public:
|
||||
Debugger();
|
||||
virtual ~Debugger();
|
||||
};
|
||||
|
||||
extern Debugger *g_debugger;
|
||||
|
||||
} // End of namespace Comprehend
|
||||
} // End of namespace Glk
|
||||
|
||||
#endif
|
||||
444
engines/glk/comprehend/debugger_dumper.cpp
Normal file
444
engines/glk/comprehend/debugger_dumper.cpp
Normal file
@@ -0,0 +1,444 @@
|
||||
/* 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/comprehend/debugger_dumper.h"
|
||||
#include "glk/comprehend/dictionary.h"
|
||||
#include "glk/comprehend/game.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace Comprehend {
|
||||
|
||||
DebuggerDumper::DebuggerDumper() : _game(nullptr) {
|
||||
_opcodes[OPCODE_HAVE_OBJECT] = "have_object";
|
||||
_opcodes[OPCODE_NOT_HAVE_OBJECT] = "not_have_object";
|
||||
_opcodes[OPCODE_HAVE_CURRENT_OBJECT] = "have_current_object";
|
||||
_opcodes[OPCODE_NOT_HAVE_CURRENT_OBJECT] = "not_have_current_object";
|
||||
|
||||
_opcodes[OPCODE_OBJECT_IS_NOT_NOWHERE] = "object_is_not_nowhere";
|
||||
|
||||
_opcodes[OPCODE_CURRENT_IS_OBJECT] = "current_is_object";
|
||||
_opcodes[OPCODE_CURRENT_OBJECT_NOT_TAKEABLE] = "current_object_not_takeable";
|
||||
|
||||
_opcodes[OPCODE_CURRENT_OBJECT_NOT_IN_ROOM] = "current_object_not_in_room";
|
||||
_opcodes[OPCODE_CURRENT_OBJECT_IS_NOWHERE] = "current_object_is_nowhere";
|
||||
|
||||
_opcodes[OPCODE_CURRENT_OBJECT_NOT_PRESENT] = "current_object_not_present";
|
||||
|
||||
_opcodes[OPCODE_TAKE_OBJECT] = "take_object";
|
||||
_opcodes[OPCODE_TAKE_CURRENT_OBJECT] = "take_current_object";
|
||||
_opcodes[OPCODE_DROP_OBJECT] = "drop_object";
|
||||
_opcodes[OPCODE_DROP_CURRENT_OBJECT] = "drop_current_object";
|
||||
|
||||
_opcodes[OPCODE_OR] = "or";
|
||||
_opcodes[OPCODE_IN_ROOM] = "in_room";
|
||||
_opcodes[OPCODE_VAR_EQ1] = "var_eq1";
|
||||
_opcodes[OPCODE_VAR_EQ2] = "var_eq2";
|
||||
_opcodes[OPCODE_VAR_GT1] = "var_gt1";
|
||||
_opcodes[OPCODE_VAR_GT2] = "var_gt2";
|
||||
_opcodes[OPCODE_VAR_GTE1] = "var_gte1";
|
||||
_opcodes[OPCODE_VAR_GTE2] = "var_gte2";
|
||||
_opcodes[OPCODE_CURRENT_OBJECT_NOT_VALID] = "current_object_not_valid";
|
||||
_opcodes[OPCODE_INVENTORY_FULL] = "inventory_full";
|
||||
_opcodes[OPCODE_INVENTORY_FULL_X] = "inventory_full_x";
|
||||
_opcodes[OPCODE_OBJECT_PRESENT] = "object_present";
|
||||
_opcodes[OPCODE_ELSE] = "else";
|
||||
_opcodes[OPCODE_OBJECT_IN_ROOM] = "object_in_room";
|
||||
_opcodes[OPCODE_TEST_FLAG] = "test_flag";
|
||||
_opcodes[OPCODE_CURRENT_OBJECT_IN_ROOM] = "current_object_in_room";
|
||||
_opcodes[OPCODE_CURRENT_OBJECT_PRESENT] = "current_object_present";
|
||||
_opcodes[OPCODE_TEST_ROOM_FLAG] = "test_room_flag";
|
||||
_opcodes[OPCODE_NOT_IN_ROOM] = "not_in_room";
|
||||
_opcodes[OPCODE_OBJECT_NOT_PRESENT] = "object_not_present";
|
||||
_opcodes[OPCODE_OBJECT_NOT_IN_ROOM] = "object_not_in_room";
|
||||
_opcodes[OPCODE_TEST_NOT_FLAG] = "test_not_flag";
|
||||
_opcodes[OPCODE_OBJECT_IS_NOWHERE] = "object_is_nowhere";
|
||||
_opcodes[OPCODE_TEST_NOT_ROOM_FLAG] = "test_not_room_flag";
|
||||
_opcodes[OPCODE_INVENTORY] = "inventory";
|
||||
_opcodes[OPCODE_MOVE_OBJECT_TO_ROOM] = "move_object_to_room";
|
||||
_opcodes[OPCODE_SAVE_ACTION] = "save_action";
|
||||
_opcodes[OPCODE_CLEAR_LINE] = "clear_line";
|
||||
_opcodes[OPCODE_MOVE_TO_ROOM] = "move_to_room";
|
||||
_opcodes[OPCODE_VAR_ADD] = "var_add";
|
||||
_opcodes[OPCODE_SET_ROOM_DESCRIPTION] = "set_room_description";
|
||||
_opcodes[OPCODE_MOVE_OBJECT_TO_CURRENT_ROOM] = "move_object_to_current_room";
|
||||
_opcodes[OPCODE_VAR_SUB] = "var_sub";
|
||||
_opcodes[OPCODE_SET_OBJECT_DESCRIPTION] = "set_object_description";
|
||||
_opcodes[OPCODE_SET_OBJECT_LONG_DESCRIPTION] = "set_object_long_description";
|
||||
_opcodes[OPCODE_MOVE_DEFAULT] = "move_default";
|
||||
_opcodes[OPCODE_PRINT] = "print";
|
||||
_opcodes[OPCODE_REMOVE_OBJECT] = "remove_object";
|
||||
_opcodes[OPCODE_SET_FLAG] = "set_flag";
|
||||
_opcodes[OPCODE_CALL_FUNC] = "call_func";
|
||||
_opcodes[OPCODE_CALL_FUNC2] = "call_func2";
|
||||
_opcodes[OPCODE_TURN_TICK] = "turn_tick";
|
||||
_opcodes[OPCODE_CLEAR_FLAG] = "clear_flag";
|
||||
_opcodes[OPCODE_INVENTORY_ROOM] = "inventory_room";
|
||||
_opcodes[OPCODE_SPECIAL] = "special";
|
||||
_opcodes[OPCODE_SET_ROOM_GRAPHIC] = "set_room_graphic";
|
||||
_opcodes[OPCODE_SET_OBJECT_GRAPHIC] = "set_object_graphic";
|
||||
_opcodes[OPCODE_REMOVE_CURRENT_OBJECT] = "remove_current_object";
|
||||
_opcodes[OPCODE_MOVE_DIR] = "move_dir";
|
||||
_opcodes[OPCODE_VAR_INC] = "var_inc";
|
||||
_opcodes[OPCODE_VAR_DEC] = "var_dec";
|
||||
_opcodes[OPCODE_MOVE_CURRENT_OBJECT_TO_ROOM] = "move_current_object_to_room";
|
||||
_opcodes[OPCODE_DESCRIBE_CURRENT_OBJECT] = "describe_current_object";
|
||||
_opcodes[OPCODE_SET_STRING_REPLACEMENT1] = "set_string_replacement1";
|
||||
_opcodes[OPCODE_SET_STRING_REPLACEMENT2] = "set_string_replacement2";
|
||||
_opcodes[OPCODE_SET_STRING_REPLACEMENT3] = "set_string_replacement3";
|
||||
_opcodes[OPCODE_SET_CURRENT_NOUN_STRING_REPLACEMENT] = "set_current_noun_string_replacement";
|
||||
_opcodes[OPCODE_DRAW_ROOM] = "draw_room";
|
||||
_opcodes[OPCODE_DRAW_OBJECT] = "draw_object";
|
||||
_opcodes[OPCODE_WAIT_KEY] = "wait_key";
|
||||
_opcodes[OPCODE_TEST_FALSE] = "test_false";
|
||||
_opcodes[OPCODE_OBJECT_CAN_TAKE] = "object_can_take";
|
||||
_opcodes[OPCODE_OBJECT_TAKEABLE] = "object_takeable";
|
||||
_opcodes[OPCODE_CLEAR_INVISIBLE] = "clear_invisible";
|
||||
_opcodes[OPCODE_SET_INVISIBLE] = "set_invisible";
|
||||
_opcodes[OPCODE_CLEAR_CAN_TAKE] = "clear_can_take";
|
||||
_opcodes[OPCODE_SET_CAN_TAKE] = "set_can_take";
|
||||
_opcodes[OPCODE_SET_FLAG40] = "set_flag40";
|
||||
_opcodes[OPCODE_CLEAR_FLAG40] = "clear_flag40";
|
||||
_opcodes[OPCODE_RANDOM_MSG] = "random_msg";
|
||||
_opcodes[OPCODE_SET_WORD] = "set_word";
|
||||
_opcodes[OPCODE_CLEAR_WORD] = "clear_word";
|
||||
}
|
||||
|
||||
Common::String DebuggerDumper::dumpInstruction(ComprehendGame *game,
|
||||
const FunctionState *func_state, const Instruction *instr) {
|
||||
uint i;
|
||||
int str_index, str_table;
|
||||
ScriptOpcode opcode = _game->getScriptOpcode(instr);
|
||||
Common::String line;
|
||||
|
||||
if (func_state)
|
||||
line = Common::String::format("[or=%d,and=%d,test=%d,else=%d]",
|
||||
func_state->_orCount, func_state->_and,
|
||||
func_state->_testResult, func_state->_elseResult);
|
||||
|
||||
line += Common::String::format(" [%.2x] ", instr->_opcode);
|
||||
if (_opcodes.contains(opcode)) {
|
||||
if (_game->_comprehendVersion == 2 && !instr->_isCommand && (instr->_opcode & 0x40) != 0)
|
||||
line += "!";
|
||||
line += _opcodes[opcode];
|
||||
} else {
|
||||
line += "unknown";
|
||||
}
|
||||
|
||||
if (instr->_nr_operands) {
|
||||
line += "(";
|
||||
for (i = 0; i < instr->_nr_operands; i++)
|
||||
line += Common::String::format("%.2x%s",
|
||||
instr->_operand[i],
|
||||
i == (instr->_nr_operands - 1) ? ")" : ", ");
|
||||
}
|
||||
|
||||
switch (opcode) {
|
||||
case OPCODE_PRINT:
|
||||
case OPCODE_SET_ROOM_DESCRIPTION:
|
||||
case OPCODE_SET_OBJECT_DESCRIPTION:
|
||||
case OPCODE_SET_OBJECT_LONG_DESCRIPTION:
|
||||
|
||||
if (opcode == OPCODE_PRINT) {
|
||||
str_index = instr->_operand[0];
|
||||
str_table = instr->_operand[1];
|
||||
} else {
|
||||
str_index = instr->_operand[1];
|
||||
str_table = instr->_operand[2];
|
||||
}
|
||||
|
||||
line += Common::String::format(" %s", game->instrStringLookup(str_index, str_table).c_str());
|
||||
break;
|
||||
|
||||
case OPCODE_SET_STRING_REPLACEMENT1:
|
||||
case OPCODE_SET_STRING_REPLACEMENT2:
|
||||
case OPCODE_SET_STRING_REPLACEMENT3:
|
||||
str_index = instr->_operand[0] - 1;
|
||||
if (str_index < 0 || str_index >= (int)game->_replaceWords.size())
|
||||
warning("invalid string replacement index - %d", str_index);
|
||||
else
|
||||
line += Common::String::format(" %s", game->_replaceWords[str_index].c_str());
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return line;
|
||||
}
|
||||
|
||||
void DebuggerDumper::dumpFunctions() {
|
||||
uint i;
|
||||
|
||||
print("Functions (%u entries)\n", _game->_functions.size());
|
||||
for (i = 0; i < _game->_functions.size(); i++)
|
||||
dumpFunction(i);
|
||||
}
|
||||
|
||||
void DebuggerDumper::dumpFunction(uint functionNum) {
|
||||
const Function &func = _game->_functions[functionNum];
|
||||
|
||||
print("[%.4x] (%u instructions)\n", functionNum, func.size());
|
||||
for (uint i = 0; i < func.size(); i++) {
|
||||
Common::String line = dumpInstruction(_game, nullptr, &func[i]);
|
||||
print("%s\n", line.c_str());
|
||||
}
|
||||
|
||||
print("\n");
|
||||
}
|
||||
|
||||
void DebuggerDumper::dumpActionTable() {
|
||||
Action *action;
|
||||
uint i, j;
|
||||
|
||||
print("Action tables: %u tables\n", _game->_actions.size());
|
||||
|
||||
for (uint tableNum = 0; tableNum < _game->_actions.size(); ++tableNum) {
|
||||
ActionTable &table = _game->_actions[tableNum];
|
||||
|
||||
print("Action table #u (%u entries)\n", tableNum, table.size());
|
||||
for (i = 0; i < table.size(); i++) {
|
||||
action = &table[i];
|
||||
|
||||
print(" [%.4x] ", i);
|
||||
|
||||
for (j = 0; j < action->_nr_words; j++)
|
||||
print("%.2x ", action->_words[j]);
|
||||
|
||||
print("-> %.4x\n", action->_function);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int DebuggerDumper::wordIndexCompare(const Word &a, const Word &b) {
|
||||
if (a._index > b._index)
|
||||
return 1;
|
||||
if (a._index < b._index)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DebuggerDumper::dumpDictionary() {
|
||||
Common::Array<Word> dictionary;
|
||||
|
||||
/* Sort the dictionary by index */
|
||||
dictionary = _game->_words;
|
||||
Common::sort(dictionary.begin(), dictionary.end(), wordIndexCompare);
|
||||
|
||||
print("Dictionary (%u words)\n", dictionary.size());
|
||||
for (uint i = 0; i < dictionary.size(); i++) {
|
||||
const Word &word = dictionary[i];
|
||||
print(" [%.2x] %.2x %s\n", word._index, word._type, word._word);
|
||||
}
|
||||
}
|
||||
|
||||
void DebuggerDumper::dumpWordMap() {
|
||||
Word *word[3];
|
||||
char str[3][7];
|
||||
WordMap *map;
|
||||
uint i, j;
|
||||
|
||||
print("Word pairs (%u entries)\n", _game->_wordMaps.size());
|
||||
for (i = 0; i < _game->_wordMaps.size(); i++) {
|
||||
map = &_game->_wordMaps[i];
|
||||
|
||||
for (j = 0; j < 3; j++) {
|
||||
word[j] = dict_find_word_by_index_type(
|
||||
_game, map->_word[j]._index, map->_word[j]._type);
|
||||
if (word[j])
|
||||
snprintf(str[j], sizeof(str[j]),
|
||||
"%s", word[j]->_word);
|
||||
else
|
||||
snprintf(str[j], sizeof(str[j]), "%.2x:%.2x ",
|
||||
map->_word[j]._index, map->_word[j]._type);
|
||||
}
|
||||
|
||||
print(" [%.2x] %-6s %-6s -> %-6s\n",
|
||||
i, str[0], str[1], str[2]);
|
||||
}
|
||||
}
|
||||
|
||||
void DebuggerDumper::dumpRooms() {
|
||||
Room *room;
|
||||
uint i;
|
||||
|
||||
// Room zero acts as the players inventory
|
||||
print("Rooms (%u entries)\n", (uint)_game->_rooms.size() - 1);
|
||||
for (i = 1; i < _game->_rooms.size(); i++) {
|
||||
room = &_game->_rooms[i];
|
||||
|
||||
print(" [%.2x] flags=%.2x, graphic=%.2x\n",
|
||||
i, room->_flags, room->_graphic);
|
||||
print(" %s\n", _game->stringLookup(room->_stringDesc).c_str());
|
||||
print(" n: %.2x s: %.2x e: %.2x w: %.2x\n",
|
||||
room->_direction[DIRECTION_NORTH],
|
||||
room->_direction[DIRECTION_SOUTH],
|
||||
room->_direction[DIRECTION_EAST],
|
||||
room->_direction[DIRECTION_WEST]);
|
||||
print(" u: %.2x d: %.2x i: %.2x o: %.2x\n",
|
||||
room->_direction[DIRECTION_UP],
|
||||
room->_direction[DIRECTION_DOWN],
|
||||
room->_direction[DIRECTION_IN],
|
||||
room->_direction[DIRECTION_OUT]);
|
||||
print("\n");
|
||||
}
|
||||
}
|
||||
|
||||
void DebuggerDumper::dumpItems() {
|
||||
Item *item;
|
||||
uint i, j;
|
||||
|
||||
print("Items (%u entries)\n", _game->_items.size());
|
||||
for (i = 0; i < _game->_items.size(); i++) {
|
||||
item = &_game->_items[i];
|
||||
|
||||
print(" [%.2x] %s\n", i + 1,
|
||||
item->_stringDesc ? _game->stringLookup(item->_stringDesc).c_str() : "");
|
||||
if (_game->_comprehendVersion == 2)
|
||||
print(" long desc: %s\n",
|
||||
_game->stringLookup(item->_longString).c_str());
|
||||
|
||||
print(" words: ");
|
||||
for (j = 0; j < _game->_words.size(); j++)
|
||||
if (_game->_words[j]._index == item->_word &&
|
||||
(_game->_words[j]._type & WORD_TYPE_NOUN_MASK))
|
||||
print("%s ", _game->_words[j]._word);
|
||||
print("\n");
|
||||
print(" flags=%.2x (takeable=%d, weight=%d)\n",
|
||||
item->_flags, !!(item->_flags & ITEMF_CAN_TAKE),
|
||||
(item->_flags & ITEMF_WEIGHT_MASK));
|
||||
print(" room=%.2x, graphic=%.2x\n",
|
||||
item->_room, item->_graphic);
|
||||
print("\n");
|
||||
}
|
||||
}
|
||||
|
||||
void DebuggerDumper::dumpStringTable(Common::StringArray &table) {
|
||||
uint i;
|
||||
|
||||
for (i = 0; i < table.size(); i++)
|
||||
print("[%.4x] %s\n", i, table[i].c_str());
|
||||
}
|
||||
|
||||
void DebuggerDumper::dumpGameDataStrings() {
|
||||
print("Main string table (%u entries)\n",
|
||||
_game->_strings.size());
|
||||
dumpStringTable(_game->_strings);
|
||||
}
|
||||
|
||||
void DebuggerDumper::dumpExtraStrings() {
|
||||
print("Extra strings (%u entries)\n",
|
||||
_game->_strings2.size());
|
||||
dumpStringTable(_game->_strings2);
|
||||
}
|
||||
|
||||
void DebuggerDumper::dumpReplaceWords() {
|
||||
uint i;
|
||||
|
||||
print("Replacement words (%u entries)\n",
|
||||
_game->_replaceWords.size());
|
||||
for (i = 0; i < _game->_replaceWords.size(); i++)
|
||||
print(" [%.2x] %s\n", i + 1, _game->_replaceWords[i].c_str());
|
||||
}
|
||||
|
||||
void DebuggerDumper::dumpHeader() {
|
||||
GameHeader *header = &_game->_header;
|
||||
uint16 *dir_table = header->room_direction_table;
|
||||
|
||||
print("Game header:\n");
|
||||
print(" magic: %.4x\n", header->magic);
|
||||
|
||||
print(" functions: %.4x\n", header->addr_vm);
|
||||
print(" dictionary: %.4x\n", header->addr_dictionary);
|
||||
print(" word map pairs: %.4x\n", header->addr_word_map);
|
||||
print(" room desc strings: %.4x\n", header->room_desc_table);
|
||||
print(" room north: %.4x\n", dir_table[DIRECTION_NORTH]);
|
||||
print(" room south: %.4x\n", dir_table[DIRECTION_SOUTH]);
|
||||
print(" room east: %.4x\n", dir_table[DIRECTION_EAST]);
|
||||
print(" room west: %.4x\n", dir_table[DIRECTION_WEST]);
|
||||
print(" room up: %.4x\n", dir_table[DIRECTION_UP]);
|
||||
print(" room down: %.4x\n", dir_table[DIRECTION_DOWN]);
|
||||
print(" room in: %.4x\n", dir_table[DIRECTION_IN]);
|
||||
print(" room out: %.4x\n", dir_table[DIRECTION_OUT]);
|
||||
print(" room flags: %.4x\n", header->room_flags_table);
|
||||
print(" room images: %.4x\n", header->room_graphics_table);
|
||||
print(" item locations: %.4x\n", header->addr_item_locations);
|
||||
print(" item flags: %.4x\n", header->addr_item_flags);
|
||||
print(" item words: %.4x\n", header->addr_item_word);
|
||||
print(" item desc strings: %.4x\n", header->addr_item_strings);
|
||||
print(" item images: %.4x\n", header->addr_item_graphics);
|
||||
print(" string table: %.4x\n", header->addr_strings);
|
||||
print(" string table end: %.4x\n", header->addr_strings_end);
|
||||
}
|
||||
|
||||
void DebuggerDumper::dumpState() {
|
||||
print("Current room: %.2x\n", _game->_currentRoom);
|
||||
print("Carry weight %d/%d\n\n",
|
||||
_game->_variables[VAR_INVENTORY_WEIGHT],
|
||||
_game->_variables[VAR_INVENTORY_LIMIT]);
|
||||
|
||||
print("Flags:\n");
|
||||
for (uint i = 0; i < ARRAY_SIZE(_game->_flags); i++)
|
||||
print(" [%.2x]: %d\n", i, _game->_flags[i]);
|
||||
print("\n");
|
||||
|
||||
print("Variables:\n");
|
||||
for (uint i = 0; i < ARRAY_SIZE(_game->_variables); i++)
|
||||
print(" [%.2x]: %5d (0x%.4x)\n",
|
||||
i, _game->_variables[i],
|
||||
_game->_variables[i]);
|
||||
print("\n");
|
||||
}
|
||||
|
||||
bool DebuggerDumper::dumpGameData(ComprehendGame *game, const Common::String &type, int param) {
|
||||
_game = game;
|
||||
|
||||
if (type == "header")
|
||||
dumpHeader();
|
||||
else if (type == "strings")
|
||||
dumpGameDataStrings();
|
||||
else if (type == "extra_strings")
|
||||
dumpExtraStrings();
|
||||
else if (type == "rooms")
|
||||
dumpRooms();
|
||||
else if (type == "items")
|
||||
dumpItems();
|
||||
else if (type == "dictionary")
|
||||
dumpDictionary();
|
||||
else if (type == "word_map")
|
||||
dumpWordMap();
|
||||
else if (type == "actions")
|
||||
dumpActionTable();
|
||||
else if (type == "functions")
|
||||
dumpFunctions();
|
||||
else if (type == "function")
|
||||
dumpFunction(param);
|
||||
else if (type == "replace_words")
|
||||
dumpReplaceWords();
|
||||
else if (type == "state")
|
||||
dumpState();
|
||||
else
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Comprehend
|
||||
} // namespace Glk
|
||||
77
engines/glk/comprehend/debugger_dumper.h
Normal file
77
engines/glk/comprehend/debugger_dumper.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GLK_COMPREHEND_DEBUGGER_DUMPER_H
|
||||
#define GLK_COMPREHEND_DEBUGGER_DUMPER_H
|
||||
|
||||
#include "common/hashmap.h"
|
||||
#include "common/str-array.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace Comprehend {
|
||||
|
||||
class ComprehendGame;
|
||||
struct FunctionState;
|
||||
struct Instruction;
|
||||
struct Word;
|
||||
|
||||
class DebuggerDumper {
|
||||
private:
|
||||
Common::HashMap<byte, Common::String> _opcodes;
|
||||
ComprehendGame *_game;
|
||||
|
||||
private:
|
||||
void dumpFunctions();
|
||||
void dumpFunction(uint functionNum);
|
||||
void dumpActionTable();
|
||||
static int wordIndexCompare(const Word &a, const Word &b);
|
||||
void dumpDictionary();
|
||||
void dumpWordMap();
|
||||
void dumpRooms();
|
||||
void dumpItems();
|
||||
void dumpStringTable(Common::StringArray &table);
|
||||
void dumpGameDataStrings();
|
||||
void dumpExtraStrings();
|
||||
void dumpReplaceWords();
|
||||
void dumpHeader();
|
||||
void dumpState();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Prints out dumped text
|
||||
*/
|
||||
virtual void print(const char *fmt, ...) = 0;
|
||||
|
||||
public:
|
||||
DebuggerDumper();
|
||||
virtual ~DebuggerDumper() {}
|
||||
|
||||
Common::String dumpInstruction(ComprehendGame *game,
|
||||
const FunctionState *func_state, const Instruction *instr);
|
||||
|
||||
bool dumpGameData(ComprehendGame *game, const Common::String &type,
|
||||
int param = 0);
|
||||
};
|
||||
|
||||
} // namespace Comprehend
|
||||
} // namespace Glk
|
||||
|
||||
#endif
|
||||
126
engines/glk/comprehend/detection.cpp
Normal file
126
engines/glk/comprehend/detection.cpp
Normal file
@@ -0,0 +1,126 @@
|
||||
/* 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/comprehend/detection.h"
|
||||
#include "glk/comprehend/detection_tables.h"
|
||||
#include "glk/blorb.h"
|
||||
#include "common/file.h"
|
||||
#include "common/md5.h"
|
||||
#include "engines/game.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace Comprehend {
|
||||
|
||||
void ComprehendMetaEngine::getSupportedGames(PlainGameList &games) {
|
||||
for (const PlainGameDescriptor *pd = COMPREHEND_GAME_LIST; pd->gameId; ++pd)
|
||||
games.push_back(*pd);
|
||||
}
|
||||
|
||||
const GlkDetectionEntry* ComprehendMetaEngine::getDetectionEntries() {
|
||||
static Common::Array<GlkDetectionEntry> entries;
|
||||
for (const ComprehendDetectionEntry *entry = COMPREHEND_GAMES; entry->_gameId; ++entry) {
|
||||
GlkDetectionEntry detection = {
|
||||
entry->_gameId,
|
||||
entry->_filename,
|
||||
entry->_md5,
|
||||
0,
|
||||
Common::EN_ANY,
|
||||
Common::kPlatformUnknown
|
||||
};
|
||||
entries.push_back(detection);
|
||||
}
|
||||
|
||||
entries.push_back({nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
0,
|
||||
Common::UNK_LANG,
|
||||
Common::kPlatformUnknown});
|
||||
|
||||
return entries.data();
|
||||
}
|
||||
|
||||
GameDescriptor ComprehendMetaEngine::findGame(const char *gameId) {
|
||||
for (const PlainGameDescriptor *pd = COMPREHEND_GAME_LIST; pd->gameId; ++pd) {
|
||||
if (!strcmp(gameId, pd->gameId)) {
|
||||
GameDescriptor gd = *pd;
|
||||
Common::String s(pd->gameId);
|
||||
gd._supportLevel = (s == "transylvaniav2" || s == "talisman") ?
|
||||
kUnstableGame : kTestingGame;
|
||||
return gd;
|
||||
}
|
||||
}
|
||||
|
||||
return GameDescriptor::empty();
|
||||
}
|
||||
|
||||
bool ComprehendMetaEngine::detectGames(const Common::FSList &fslist, DetectedGames &gameList) {
|
||||
// Loop through the files of the folder
|
||||
for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
|
||||
// Check for a recognised filename
|
||||
if (file->isDirectory())
|
||||
continue;
|
||||
|
||||
// Check if file occurs in the list
|
||||
Common::String filename = file->getName();
|
||||
bool isPossible = false;
|
||||
const ComprehendDetectionEntry *p = COMPREHEND_GAMES;
|
||||
for (; p->_gameId && !isPossible; ++p)
|
||||
isPossible = filename.equalsIgnoreCase(p->_filename);
|
||||
|
||||
if (!isPossible)
|
||||
continue;
|
||||
|
||||
// Get the file's MD5
|
||||
Common::File gameFile;
|
||||
if (!gameFile.open(*file))
|
||||
continue;
|
||||
Common::String md5 = Common::computeStreamMD5AsString(gameFile, 5000);
|
||||
gameFile.close();
|
||||
|
||||
// Iterate through the known games
|
||||
p = COMPREHEND_GAMES;
|
||||
for (; p->_gameId; ++p) {
|
||||
if (filename.equalsIgnoreCase(p->_filename)) {
|
||||
// Check for an md5 match
|
||||
if (p->_md5 == md5) {
|
||||
// Found a match
|
||||
PlainGameDescriptor gameDesc = findGame(p->_gameId);
|
||||
GlkDetectedGame gd(p->_gameId, gameDesc.description, filename);
|
||||
gameList.push_back(gd);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return !gameList.empty();
|
||||
}
|
||||
|
||||
void ComprehendMetaEngine::detectClashes(Common::StringMap &map) {
|
||||
for (const PlainGameDescriptor *pd = COMPREHEND_GAME_LIST; pd->gameId; ++pd) {
|
||||
if (map.contains(pd->gameId))
|
||||
error("Duplicate game Id found - %s", pd->gameId);
|
||||
map[pd->gameId] = "";
|
||||
}
|
||||
}
|
||||
|
||||
} // End of namespace Comprehend
|
||||
} // End of namespace Glk
|
||||
64
engines/glk/comprehend/detection.h
Normal file
64
engines/glk/comprehend/detection.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GLK_COMPREHEND_DETECTION
|
||||
#define GLK_COMPREHEND_DETECTION
|
||||
|
||||
#include "common/fs.h"
|
||||
#include "common/hash-str.h"
|
||||
#include "engines/game.h"
|
||||
#include "glk/detection.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace Comprehend {
|
||||
|
||||
class ComprehendMetaEngine {
|
||||
public:
|
||||
/**
|
||||
* Get a list of supported games
|
||||
*/
|
||||
static void getSupportedGames(PlainGameList &games);
|
||||
|
||||
/**
|
||||
* Get the detection entries
|
||||
*/
|
||||
static const GlkDetectionEntry* getDetectionEntries();
|
||||
|
||||
/**
|
||||
* Returns a game description for the given game Id, if it's supported
|
||||
*/
|
||||
static GameDescriptor findGame(const char *gameId);
|
||||
|
||||
/**
|
||||
* Detect supported games
|
||||
*/
|
||||
static bool detectGames(const Common::FSList &fslist, DetectedGames &gameList);
|
||||
|
||||
/**
|
||||
* Check for game Id clashes with other sub-engines
|
||||
*/
|
||||
static void detectClashes(Common::StringMap &map);
|
||||
};
|
||||
|
||||
} // End of namespace Comprehend
|
||||
} // End of namespace Glk
|
||||
|
||||
#endif
|
||||
57
engines/glk/comprehend/detection_tables.h
Normal file
57
engines/glk/comprehend/detection_tables.h
Normal file
@@ -0,0 +1,57 @@
|
||||
/* 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 "common/language.h"
|
||||
#include "engines/game.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace Comprehend {
|
||||
|
||||
const PlainGameDescriptor COMPREHEND_GAME_LIST[] = {
|
||||
{"transylvania", "Transylvania"},
|
||||
{"crimsoncrown", "Crimson Crown"},
|
||||
{"ootopos", "OO-Topos"},
|
||||
|
||||
{"transylvaniav2", "Transylvania (V2)"},
|
||||
{"talisman", "Talisman: Challenging the Sands of Time"},
|
||||
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
struct ComprehendDetectionEntry {
|
||||
const char *const _gameId;
|
||||
const char *const _filename;
|
||||
const char *const _md5;
|
||||
};
|
||||
|
||||
const ComprehendDetectionEntry COMPREHEND_GAMES[] = {
|
||||
{ "transylvania", "tr.gda", "22e08633eea02ceee49b909dfd982d22" },
|
||||
{ "crimsoncrown", "cc1.gda", "f2abf019675ac5c9bcfd81032bc7787b" },
|
||||
{ "ootopos", "g0", "56460c1ee669c253607534155d7e9db4" },
|
||||
|
||||
{ "transylvaniav2", "g0", "384cbf0cd50888310fd33574e6baf880" },
|
||||
{ "talisman", "g0", "35770d4815e610b5252e3fcd9f11def3" },
|
||||
|
||||
{nullptr, nullptr, nullptr}
|
||||
};
|
||||
|
||||
} // End of namespace Comprehend
|
||||
} // End of namespace Glk
|
||||
92
engines/glk/comprehend/dictionary.cpp
Normal file
92
engines/glk/comprehend/dictionary.cpp
Normal file
@@ -0,0 +1,92 @@
|
||||
/* 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/comprehend/comprehend.h"
|
||||
#include "glk/comprehend/game.h"
|
||||
#include "glk/comprehend/game_data.h"
|
||||
#include "glk/comprehend/dictionary.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace Comprehend {
|
||||
|
||||
static bool word_match(Word *word, const char *string) {
|
||||
/* Words less than 6 characters must match exactly */
|
||||
if (strlen(word->_word) < 6 && strlen(string) != strlen(word->_word))
|
||||
return false;
|
||||
|
||||
return strncmp(word->_word, string, strlen(word->_word)) == 0;
|
||||
}
|
||||
|
||||
Word *dict_find_word_by_string(ComprehendGame *game,
|
||||
const char *string) {
|
||||
uint i;
|
||||
|
||||
if (!string)
|
||||
return nullptr;
|
||||
|
||||
for (i = 0; i < game->_words.size(); i++)
|
||||
if (word_match(&game->_words[i], string))
|
||||
return &game->_words[i];
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Word *dict_find_word_by_index_type(ComprehendGame *game,
|
||||
uint8 index, uint8 type) {
|
||||
uint i;
|
||||
|
||||
for (i = 0; i < game->_words.size(); i++) {
|
||||
if (game->_words[i]._index == index &&
|
||||
game->_words[i]._type == type)
|
||||
return &game->_words[i];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Word *find_dict_word_by_index(ComprehendGame *game,
|
||||
uint8 index, uint8 type_mask) {
|
||||
uint i;
|
||||
|
||||
for (i = 0; i < game->_words.size(); i++) {
|
||||
if (game->_words[i]._index == index &&
|
||||
(game->_words[i]._type & type_mask) != 0)
|
||||
return &game->_words[i];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool dict_match_index_type(ComprehendGame *game, const char *word,
|
||||
uint8 index, uint8 type_mask) {
|
||||
uint i;
|
||||
|
||||
for (i = 0; i < game->_words.size(); i++)
|
||||
if (game->_words[i]._index == index &&
|
||||
((game->_words[i]._type & type_mask) != 0) &&
|
||||
word_match(&game->_words[i], word))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace Comprehend
|
||||
} // namespace Glk
|
||||
43
engines/glk/comprehend/dictionary.h
Normal file
43
engines/glk/comprehend/dictionary.h
Normal file
@@ -0,0 +1,43 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GLK_COMPREHEND_DICTIONARY_H
|
||||
#define GLK_COMPREHEND_DICTIONARY_H
|
||||
|
||||
namespace Glk {
|
||||
namespace Comprehend {
|
||||
|
||||
class ComprehendGame;
|
||||
struct Word;
|
||||
|
||||
Word *find_dict_word_by_index(ComprehendGame *game,
|
||||
uint8 index, uint8 type_mask);
|
||||
Word *dict_find_word_by_index_type(ComprehendGame *game,
|
||||
uint8 index, uint8 type);
|
||||
Word *dict_find_word_by_string(ComprehendGame *game,
|
||||
const char *string);
|
||||
bool dict_match_index_type(ComprehendGame *game, const char *word,
|
||||
uint8 index, uint8 type_mask);
|
||||
|
||||
} // namespace Comprehend
|
||||
} // namespace Glk
|
||||
|
||||
#endif
|
||||
446
engines/glk/comprehend/draw_surface.cpp
Normal file
446
engines/glk/comprehend/draw_surface.cpp
Normal file
@@ -0,0 +1,446 @@
|
||||
/* 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/comprehend/draw_surface.h"
|
||||
#include "glk/comprehend/comprehend.h"
|
||||
#include "glk/comprehend/pics.h"
|
||||
#include "glk/window_graphics.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace Comprehend {
|
||||
|
||||
const uint32 Surface::PEN_COLORS[8] = {
|
||||
G_COLOR_BLACK,
|
||||
RGB(0x00, 0x66, 0x00),
|
||||
RGB(0x00, 0xff, 0x00),
|
||||
G_COLOR_WHITE,
|
||||
G_COLOR_BLACK,
|
||||
RGB(0x00, 0xff, 0xff),
|
||||
RGB(0xff, 0x00, 0xff),
|
||||
RGB(0xff, 0x00, 0x00),
|
||||
};
|
||||
|
||||
/* Used by Transylvania and Crimson Crown */
|
||||
const uint32 Surface::DEFAULT_COLOR_TABLE[256] = {
|
||||
G_COLOR_WHITE, // 00
|
||||
G_COLOR_DARK_BLUE, // 01
|
||||
G_COLOR_GRAY1, // 02
|
||||
G_COLOR_DARK_RED, // 03
|
||||
G_COLOR_GRAY2, // 04
|
||||
0, G_COLOR_GRAY3, 0, 0, 0, 0, 0, 0,
|
||||
G_COLOR_BROWN1, G_COLOR_DARK_PURPLE, 0,
|
||||
|
||||
0, 0, G_COLOR_DARK_RED, G_COLOR_BROWN2, 0, 0, 0,
|
||||
G_COLOR_DARK_BLUE, G_COLOR_BLACK, 0, 0, 0, 0, 0, 0, G_COLOR_DARK_PURPLE,
|
||||
|
||||
G_COLOR_DARK_PURPLE, 0, G_COLOR_DARK_RED, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, G_COLOR_DARK_PURPLE, 0, 0, 0, 0,
|
||||
|
||||
0, 0, 0, 0, G_COLOR_WHITE, G_COLOR_GRAY0, RGB(0xb5, 0x6c, 0x47),
|
||||
0, 0, 0, 0, 0, G_COLOR_CYAN, G_COLOR_DARK_RED,
|
||||
G_COLOR_DARK_GREEN1, G_COLOR_DARK_GREEN2,
|
||||
|
||||
G_COLOR_DARK_PURPLE, 0, G_COLOR_DITHERED_PINK, 0, 0,
|
||||
G_COLOR_BROWN2, G_COLOR_DARK_RED, G_COLOR_DARK_BLUE,
|
||||
G_COLOR_DARK_BLUE, G_COLOR_DARK_BLUE, 0, 0, 0,
|
||||
G_COLOR_WHITE, G_COLOR_BROWN2, G_COLOR_BROWN2,
|
||||
|
||||
G_COLOR_BLACK, G_COLOR_DARK_PURPLE, 0, G_COLOR_GRAY2,
|
||||
G_COLOR_BROWN2, 0, 0, G_COLOR_AQUA, 0, 0, G_COLOR_GREEN,
|
||||
G_COLOR_DARK_BLUE, G_COLOR_DARK_PURPLE, G_COLOR_BROWN1,
|
||||
G_COLOR_BROWN2, 0,
|
||||
|
||||
G_COLOR_DARK_PURPLE, G_COLOR_LIGHT_ORANGE, 0, 0,
|
||||
G_COLOR_ORANGE, G_COLOR_RED, G_COLOR_DARK_RED, 0, 0, 0,
|
||||
G_COLOR_DARK_BLUE, G_COLOR_DARK_PURPLE, 0, 0, 0, 0,
|
||||
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
G_COLOR_BLACK, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
/* Used by OO-topos */
|
||||
/* FIXME - incomplete */
|
||||
const uint32 Surface::COLOR_TABLE_1[256] = {
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
RGB(0x80, 0x00, 0x00),
|
||||
0,
|
||||
RGB(0xe6, 0xe6, 0x00),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
RGB(0xc0, 0x00, 0x00),
|
||||
RGB(0x80, 0x00, 0x00),
|
||||
G_COLOR_ORANGE,
|
||||
0,
|
||||
|
||||
0,
|
||||
G_COLOR_BROWN1,
|
||||
RGB(0x00, 0x00, 0x66),
|
||||
RGB(0x33, 0x99, 0xff),
|
||||
0,
|
||||
RGB(0xe8, 0xe8, 0xe8),
|
||||
RGB(0x99, 0xcc, 0xff),
|
||||
0,
|
||||
RGB(0x99, 0x33, 0x33),
|
||||
RGB(0xcc, 0x66, 0x00),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
|
||||
G_COLOR_GRAY3,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
RGB(0x99, 0x33, 0x00),
|
||||
G_COLOR_CYAN,
|
||||
0,
|
||||
0,
|
||||
RGB(0x66, 0x00, 0x33),
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
|
||||
G_COLOR_AQUA,
|
||||
G_COLOR_GRAY2,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
G_COLOR_DARK_BLUE,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
G_COLOR_GRAY1,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
|
||||
};
|
||||
|
||||
const uint32 *Surface::COLOR_TABLES[2] = {
|
||||
DEFAULT_COLOR_TABLE,
|
||||
COLOR_TABLE_1,
|
||||
};
|
||||
|
||||
static const byte SHAPE_DATA[32][8] = {
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0x80 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 1 },
|
||||
{ 1, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0x80 },
|
||||
{ 0x80, 0, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 1, 3 },
|
||||
{ 3, 1, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0, 0x80, 0xC0 },
|
||||
{ 0xC0, 0x80, 0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 3, 7, 7 },
|
||||
{ 7, 7, 3, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0xC0, 0xE0, 0xE0 },
|
||||
{ 0xE0, 0xE0, 0xC0, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 3, 0x0F, 0x0F, 0x1F, 0x1F },
|
||||
{ 0x1F, 0x1F, 0x0F, 0x0F, 3, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0xC0, 0xF0, 0xF0, 0xF8, 0xF8 },
|
||||
{ 0xF8, 0xF8, 0xF0, 0xF0, 0xC0, 0, 0, 0 },
|
||||
{ 0, 3, 0x1F, 0x3F, 0x3F, 0x3F, 0x7F, 0x7F },
|
||||
{ 0x7F, 0x7F, 0x3F, 0x3F, 0x3F, 0x1F, 3, 0 },
|
||||
{ 0, 0xC0, 0xF8, 0xFC, 0xFC, 0xFC, 0xFE, 0xFE },
|
||||
{ 0xFE, 0xFE, 0xFC, 0xFC, 0xFC, 0xF8, 0xC0, 0 },
|
||||
{ 0, 0, 0, 0, 1, 8, 2, 0 },
|
||||
{ 0x0A, 0, 4, 0, 0, 0, 0, 0 },
|
||||
{ 0, 0, 0, 0, 0, 0x20, 0, 0x90 },
|
||||
{ 0, 0xA0, 0, 0x80, 0, 0, 0, 0 },
|
||||
{ 0, 2, 8, 0x12, 1, 0x24, 0x0B, 3 },
|
||||
{ 0x23, 9, 0x22, 0x0A, 4, 1, 0, 0 },
|
||||
{ 0, 0x20, 0x80, 0x28, 0, 0xD4, 0xC0, 0xE4 },
|
||||
{ 0xE8, 0x90, 0x44, 0xA8, 0, 0x50, 0, 0 }
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------*/
|
||||
|
||||
void Surface::reset() {
|
||||
create(G_RENDER_WIDTH, G_RENDER_HEIGHT,
|
||||
Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0));
|
||||
}
|
||||
|
||||
void Surface::setColorTable(uint index) {
|
||||
if (index >= ARRAY_SIZE(COLOR_TABLES)) {
|
||||
warning("Bad color table %d - using default", index);
|
||||
_colorTable = DEFAULT_COLOR_TABLE;
|
||||
}
|
||||
|
||||
_colorTable = COLOR_TABLES[index];
|
||||
}
|
||||
|
||||
uint Surface::getPenColor(uint8 param) const {
|
||||
return PEN_COLORS[param];
|
||||
}
|
||||
|
||||
uint32 Surface::getFillColor(uint8 index) {
|
||||
unsigned color;
|
||||
|
||||
color = _colorTable[index];
|
||||
if (!color) {
|
||||
/* Unknown color - use ugly purple */
|
||||
debugC(kDebugGraphics, "Unknown color %.2x", index);
|
||||
return RGB(0xff, 0x00, 0xff);
|
||||
}
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
void Surface::drawLine(int16 x1, int16 y1, int16 x2, int16 y2, uint32 color) {
|
||||
#if 1
|
||||
Graphics::ManagedSurface::drawLine(x1, y1, x2, y2, color);
|
||||
#else
|
||||
bool swapped = false;
|
||||
int deltaX = -1, deltaY = -1;
|
||||
int xDiff = x1 - x2, yDiff = y1 - y2;
|
||||
|
||||
// Draw pixel at starting point
|
||||
drawPixel(x1, y1, color);
|
||||
|
||||
// Figure out the deltas movement for creating the line
|
||||
if (xDiff < 0) {
|
||||
deltaX = 1;
|
||||
xDiff = -xDiff;
|
||||
}
|
||||
if (yDiff < 0) {
|
||||
deltaY = 1;
|
||||
yDiff = -yDiff;
|
||||
}
|
||||
|
||||
if (xDiff < yDiff) {
|
||||
swapped = true;
|
||||
SWAP(xDiff, yDiff);
|
||||
SWAP(deltaX, deltaY);
|
||||
SWAP(x1, y1);
|
||||
}
|
||||
|
||||
int temp1 = yDiff;
|
||||
int temp2 = yDiff - xDiff;
|
||||
int temp3 = temp2;
|
||||
|
||||
// Iterate to draw the remaining pixels of the line
|
||||
for (int ctr = xDiff; ctr > 0; --ctr) {
|
||||
x1 += deltaX;
|
||||
|
||||
if (temp3 >= 0) {
|
||||
y1 += deltaY;
|
||||
temp3 += temp2;
|
||||
} else {
|
||||
temp3 += temp1;
|
||||
}
|
||||
|
||||
int xp = x1, yp = y1;
|
||||
if (swapped)
|
||||
SWAP(xp, yp);
|
||||
|
||||
drawPixel(xp, yp, color);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Surface::drawBox(int16 x1, int16 y1, int16 x2, int16 y2, uint32 color) {
|
||||
if (x1 > x2)
|
||||
SWAP(x1, x2);
|
||||
if (y1 > y2)
|
||||
SWAP(y1, y2);
|
||||
|
||||
Common::Rect r(x1, y1, x2 + 1, y2 + 1);
|
||||
frameRect(r, color);
|
||||
}
|
||||
|
||||
void Surface::drawFilledBox(int16 x1, int16 y1, int16 x2, int16 y2, uint32 color) {
|
||||
if (x1 > x2)
|
||||
SWAP(x1, x2);
|
||||
if (y1 > y2)
|
||||
SWAP(y1, y2);
|
||||
|
||||
Common::Rect r(x1, y1, x2 + 1, y2 + 1);
|
||||
fillRect(r, color);
|
||||
}
|
||||
|
||||
void Surface::drawShape(int16 x, int16 y, Shape shapeType, uint32 fillColor) {
|
||||
uint shapeNum = (uint)shapeType * 4;
|
||||
|
||||
// Outer loop to draw the shape across a 2x2 grid of 8x8 sub-shapes
|
||||
for (int shapeX = 0; shapeX <= 8; shapeX += 8) {
|
||||
for (int shapeY = 0; shapeY <= 8; shapeY += 8, ++shapeNum) {
|
||||
// Inner loop for character
|
||||
for (int charY = 0; charY < 8; ++charY) {
|
||||
int yp = y + shapeY + charY;
|
||||
if (yp < 0 || yp >= this->h)
|
||||
continue;
|
||||
|
||||
int xp = x + shapeX;
|
||||
uint32 *lineP = (uint32 *)getBasePtr(xp, yp);
|
||||
byte bits = SHAPE_DATA[shapeNum][charY];
|
||||
|
||||
for (int charX = 0; charX < 8; ++lineP, ++charX, ++xp, bits <<= 1) {
|
||||
if (xp >= 0 && xp < this->w && (bits & 0x80) != 0)
|
||||
*lineP = fillColor;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Surface::drawPixel(int16 x, int16 y, uint32 color) {
|
||||
if (x >= 0 && y >= 0 && x < this->w && y < this->h) {
|
||||
uint32 *ptr = (uint32 *)getBasePtr(x, y);
|
||||
*ptr = color;
|
||||
}
|
||||
}
|
||||
|
||||
uint32 Surface::getPixelColor(int16 x, int16 y) const {
|
||||
assert(x >= 0 && y >= 0 && x < this->w && y < this->h);
|
||||
const uint32 *ptr = (const uint32 *)getBasePtr(x, y);
|
||||
return *ptr;
|
||||
}
|
||||
|
||||
void Surface::clearScreen(uint32 color) {
|
||||
fillRect(Common::Rect(0, 0, this->w, this->h), color);
|
||||
}
|
||||
|
||||
void Surface::drawCircle(int16 x, int16 y, int16 diameter, uint32 color) {
|
||||
int invert = -diameter;
|
||||
int delta = 0;
|
||||
|
||||
do {
|
||||
drawPixel(x - delta, y - diameter, color);
|
||||
drawPixel(x + delta, y - diameter, color);
|
||||
drawPixel(x + delta, y + diameter, color);
|
||||
drawPixel(x - delta, y + diameter, color);
|
||||
|
||||
drawPixel(x + diameter, y - delta, color);
|
||||
drawPixel(x - diameter, y - delta, color);
|
||||
drawPixel(x - diameter, y + delta, color);
|
||||
drawPixel(x + diameter, y + delta, color);
|
||||
|
||||
invert += (delta * 2) + 1;
|
||||
++delta;
|
||||
if (!((uint)invert & 0x80)) {
|
||||
invert += 2;
|
||||
diameter <<= 1;
|
||||
invert -= diameter;
|
||||
diameter >>= 1;
|
||||
--diameter;
|
||||
}
|
||||
} while (diameter >= delta);
|
||||
}
|
||||
|
||||
/*--------------------------------------------------------------------------*/
|
||||
|
||||
bool FloodFillSurface::isPixelWhite(int16 x, int16 y) const {
|
||||
if (x < 0 || y < 0 || x >= this->w || y >= this->h) {
|
||||
return false;
|
||||
} else {
|
||||
byte r, g, b;
|
||||
format.colorToRGB(getPixelColor(x, y), r, g, b);
|
||||
return r == 255 && g == 255 && b == 255;
|
||||
}
|
||||
}
|
||||
|
||||
void FloodFillSurface::dumpToScreen() {
|
||||
Graphics::ManagedSurface s(w * 2, h * 2, g_system->getScreenFormat());
|
||||
s.transBlitFrom(*this, Common::Rect(0, 0, w, h), Common::Rect(0, 0, w * 2, h * 2), 0x888888);
|
||||
|
||||
g_system->copyRectToScreen(s.getPixels(), s.pitch, 0, 0, w * 2, h * 2);
|
||||
g_system->updateScreen();
|
||||
}
|
||||
|
||||
void FloodFillSurface::floodFill(int16 x, int16 y, uint32 fillColor) {
|
||||
if (y == this->h)
|
||||
y = this->h - 1;
|
||||
else if (y > this->h)
|
||||
return;
|
||||
|
||||
if (!isPixelWhite(x, y))
|
||||
return;
|
||||
|
||||
floodFillRow(x, y, fillColor);
|
||||
}
|
||||
|
||||
void FloodFillSurface::floodFillRow(int16 x, int16 y, uint32 fillColor) {
|
||||
int x1, x2, i;
|
||||
|
||||
// Left end of scanline
|
||||
for (x1 = x; x1 > 0; x1--)
|
||||
if (!isPixelWhite(x1 - 1, y))
|
||||
break;
|
||||
|
||||
// Right end of scanline
|
||||
for (x2 = x; x2 < this->w; x2++)
|
||||
if (!isPixelWhite(x2 + 1, y))
|
||||
break;
|
||||
|
||||
drawLine(x1, y, x2, y, fillColor);
|
||||
|
||||
//dumpToScreen();
|
||||
|
||||
// Scanline above
|
||||
if (y > 0) {
|
||||
for (i = x1; i <= x2; i++)
|
||||
if (isPixelWhite(i, y - 1))
|
||||
floodFillRow(i, y - 1, fillColor);
|
||||
}
|
||||
|
||||
// Scanline below
|
||||
if (y < (this->h - 1)) {
|
||||
for (i = x1; i <= x2; i++)
|
||||
if (isPixelWhite(i, y + 1))
|
||||
floodFillRow(i, y + 1, fillColor);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Comprehend
|
||||
} // namespace Glk
|
||||
128
engines/glk/comprehend/draw_surface.h
Normal file
128
engines/glk/comprehend/draw_surface.h
Normal file
@@ -0,0 +1,128 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GLK_COMPREHEND_GRAPHICS_H
|
||||
#define GLK_COMPREHEND_GRAPHICS_H
|
||||
|
||||
#include "common/scummsys.h"
|
||||
#include "graphics/managed_surface.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace Comprehend {
|
||||
|
||||
#define G_RENDER_WIDTH 280
|
||||
#define G_RENDER_HEIGHT 160
|
||||
|
||||
#define RGB(r, g, b) (uint32)(((r) << 24) | ((g) << 16) | ((b) << 8) | 0xff)
|
||||
|
||||
#define G_COLOR_BLACK 0x000000ff
|
||||
#define G_COLOR_WHITE 0xffffffff
|
||||
#define G_COLOR_CYAN 0x3366ffff
|
||||
#define G_COLOR_YELLOW 0xffff00ff
|
||||
#define G_COLOR_RED 0xff0000ff
|
||||
|
||||
#define G_COLOR_GRAY0 0x202020ff
|
||||
#define G_COLOR_GRAY1 0x404040ff
|
||||
#define G_COLOR_GRAY2 0x808080ff
|
||||
#define G_COLOR_GRAY3 0xc0c0c0ff
|
||||
|
||||
#define G_COLOR_LIGHT_ORANGE 0xff9966ff
|
||||
#define G_COLOR_ORANGE 0xff9900ff
|
||||
#define G_COLOR_DARK_PURPLE 0x666699ff
|
||||
#define G_COLOR_DARK_BLUE 0x000099ff
|
||||
|
||||
#define G_COLOR_DARK_RED 0xcc0033ff
|
||||
#define G_COLOR_DITHERED_PINK 0xff6699ff
|
||||
|
||||
#define G_COLOR_DARK_GREEN1 0x009966ff
|
||||
#define G_COLOR_DARK_GREEN2 0x003300ff
|
||||
|
||||
#define G_COLOR_AQUA 0x33ccccff
|
||||
|
||||
#define G_COLOR_GREEN 0x33cc00ff
|
||||
|
||||
#define G_COLOR_BROWN1 0x7a5200ff
|
||||
#define G_COLOR_BROWN2 0x663300ff
|
||||
|
||||
enum Shape {
|
||||
SHAPE_PIXEL = 0,
|
||||
SHAPE_BOX = 1,
|
||||
SHAPE_CIRCLE_TINY = 2,
|
||||
SHAPE_CIRCLE_SMALL = 3,
|
||||
SHAPE_CIRCLE_MED = 4,
|
||||
SHAPE_CIRCLE_LARGE = 5,
|
||||
SHAPE_A = 6,
|
||||
SHAPE_SPRAY = 7
|
||||
};
|
||||
|
||||
|
||||
class Surface : public Graphics::ManagedSurface {
|
||||
private:
|
||||
static const uint32 PEN_COLORS[8];
|
||||
static const uint32 DEFAULT_COLOR_TABLE[256];
|
||||
static const uint32 COLOR_TABLE_1[256];
|
||||
static const uint32 *COLOR_TABLES[2];
|
||||
|
||||
public:
|
||||
const uint32 *_colorTable;
|
||||
public:
|
||||
Surface() : _colorTable(DEFAULT_COLOR_TABLE) {
|
||||
reset();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the surface to the correct size and pixel format
|
||||
*/
|
||||
void reset();
|
||||
|
||||
void setColorTable(uint index);
|
||||
uint getPenColor(uint8 param) const;
|
||||
uint32 getFillColor(uint8 index);
|
||||
|
||||
void drawLine(int16 x1, int16 y1, int16 x2, int16 y2, uint32 color);
|
||||
void drawBox(int16 x1, int16 y1, int16 x2, int16 y2, uint32 color);
|
||||
void drawFilledBox(int16 x1, int16 y1, int16 x2, int16 y2, uint32 color);
|
||||
void drawShape(int16 x, int16 y, Shape shapeType, uint32 fill_color);
|
||||
void drawPixel(int16 x, int16 y, uint32 color);
|
||||
uint32 getPixelColor(int16 x, int16 y) const;
|
||||
void clearScreen(uint32 color);
|
||||
void drawCircle(int16 x, int16 y, int16 diameter, uint32 color);
|
||||
void drawCirclePoint(int16 x, int16 y);
|
||||
};
|
||||
|
||||
class FloodFillSurface : public Surface {
|
||||
private:
|
||||
bool isPixelWhite(int16 x, int16 y) const;
|
||||
|
||||
void floodFillRow(int16 x, int16 y, uint32 fillColor);
|
||||
public:
|
||||
void floodFill(int16 x, int16 y, uint32 fillColor);
|
||||
|
||||
void dumpToScreen();
|
||||
};
|
||||
|
||||
class DrawSurface : public FloodFillSurface {
|
||||
};
|
||||
|
||||
} // namespace Comprehend
|
||||
} // namespace Glk
|
||||
|
||||
#endif
|
||||
119
engines/glk/comprehend/file_buf.cpp
Normal file
119
engines/glk/comprehend/file_buf.cpp
Normal file
@@ -0,0 +1,119 @@
|
||||
/* 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/comprehend/file_buf.h"
|
||||
#include "common/algorithm.h"
|
||||
#include "common/file.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace Comprehend {
|
||||
|
||||
FileBuffer::FileBuffer(const Common::String &filename) : _pos(0) {
|
||||
// Open the file
|
||||
Common::File f;
|
||||
if (!f.open(Common::Path(filename)))
|
||||
error("Could not open - %s", filename.c_str());
|
||||
|
||||
_data.resize(f.size());
|
||||
_readBytes.resize(f.size());
|
||||
f.read(&_data[0], f.size());
|
||||
}
|
||||
|
||||
FileBuffer::FileBuffer(Common::ReadStream *stream, size_t size) : _pos(0) {
|
||||
_data.resize(size);
|
||||
_readBytes.resize(size);
|
||||
stream->read(&_data[0], size);
|
||||
}
|
||||
|
||||
|
||||
bool FileBuffer::exists(const Common::String &filename) {
|
||||
return Common::File::exists(Common::Path(filename));
|
||||
}
|
||||
|
||||
void FileBuffer::close() {
|
||||
_data.clear();
|
||||
_readBytes.clear();
|
||||
_pos = 0;
|
||||
}
|
||||
|
||||
bool FileBuffer::seek(int64 offset, int whence) {
|
||||
switch (whence) {
|
||||
case SEEK_SET:
|
||||
_pos = offset;
|
||||
break;
|
||||
case SEEK_CUR:
|
||||
_pos += offset;
|
||||
break;
|
||||
case SEEK_END:
|
||||
_pos = (int)_data.size() + offset;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32 FileBuffer::read(void *dataPtr, uint32 dataSize) {
|
||||
int32 bytesRead = CLIP((int32)dataSize, (int32)0, (int32)_data.size() - _pos);
|
||||
if (bytesRead) {
|
||||
Common::fill(&_readBytes[_pos], &_readBytes[_pos] + bytesRead, true);
|
||||
Common::copy(&_data[_pos], &_data[_pos] + bytesRead, (byte *)dataPtr);
|
||||
_pos += bytesRead;
|
||||
}
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
size_t FileBuffer::strlen(bool *eof) {
|
||||
uint8 *end;
|
||||
|
||||
if (eof)
|
||||
*eof = false;
|
||||
|
||||
end = (uint8 *)memchr(&_data[_pos], '\0', size() - _pos);
|
||||
if (!end) {
|
||||
// No null terminator - string is remaining length
|
||||
if (eof)
|
||||
*eof = true;
|
||||
return size() - _pos;
|
||||
}
|
||||
|
||||
return end - &_data[_pos];
|
||||
}
|
||||
|
||||
void FileBuffer::showUnmarked() {
|
||||
int i, start = -1;
|
||||
|
||||
for (i = 0; i < (int)_data.size(); i++) {
|
||||
if (!_readBytes[i] && start == -1)
|
||||
start = i;
|
||||
|
||||
if ((_readBytes[i] || i == (int)_data.size() - 1) && start != -1) {
|
||||
warning("%.4x - %.4x unmarked (%d bytes)\n",
|
||||
start, i - 1, i - start);
|
||||
start = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Comprehend
|
||||
} // namespace Glk
|
||||
86
engines/glk/comprehend/file_buf.h
Normal file
86
engines/glk/comprehend/file_buf.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GLK_COMPREHEND_FILE_BUF_H
|
||||
#define GLK_COMPREHEND_FILE_BUF_H
|
||||
|
||||
#include "common/array.h"
|
||||
#include "common/memstream.h"
|
||||
#include "common/stream.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace Comprehend {
|
||||
|
||||
struct FileBuffer : public Common::SeekableReadStream {
|
||||
private:
|
||||
Common::Array<byte> _data;
|
||||
Common::Array<bool> _readBytes;
|
||||
int32 _pos;
|
||||
|
||||
public:
|
||||
FileBuffer() : _pos(0) {}
|
||||
FileBuffer(const Common::String &filename);
|
||||
FileBuffer(Common::ReadStream *stream, size_t size);
|
||||
static bool exists(const Common::String &filename);
|
||||
void close();
|
||||
|
||||
int64 pos() const override {
|
||||
return _pos;
|
||||
}
|
||||
int64 size() const override {
|
||||
return _data.size();
|
||||
}
|
||||
bool seek(int64 offset, int whence = SEEK_SET) override;
|
||||
|
||||
bool eos() const override {
|
||||
return _pos >= (int)_data.size();
|
||||
}
|
||||
uint32 read(void *dataPtr, uint32 dataSize) override;
|
||||
|
||||
const byte *dataPtr() const {
|
||||
return &_data[_pos];
|
||||
}
|
||||
size_t strlen(bool *eof = nullptr);
|
||||
|
||||
/*
|
||||
* Debugging function to show regions of a file that have not been read.
|
||||
*/
|
||||
void showUnmarked();
|
||||
};
|
||||
|
||||
#define file_buf_get_array(fb, type, base, array, member, size) \
|
||||
do { \
|
||||
uint __i; \
|
||||
for (__i = (base); __i < (base) + (size); __i++) \
|
||||
(array)[__i].member = fb->read##type(); \
|
||||
} while (0)
|
||||
|
||||
#define file_buf_get_array_u8(fb, base, array, member, size) \
|
||||
file_buf_get_array(fb, Byte, base, array, member, size)
|
||||
|
||||
#define file_buf_get_array_le16(fb, base, array, member, size) \
|
||||
file_buf_get_array(fb, Uint16LE, base, array, member, size)
|
||||
|
||||
|
||||
} // namespace Comprehend
|
||||
} // namespace Glk
|
||||
|
||||
#endif
|
||||
938
engines/glk/comprehend/game.cpp
Normal file
938
engines/glk/comprehend/game.cpp
Normal file
@@ -0,0 +1,938 @@
|
||||
/* 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/comprehend/game.h"
|
||||
#include "common/debug-channels.h"
|
||||
#include "common/translation.h"
|
||||
#include "glk/comprehend/comprehend.h"
|
||||
#include "glk/comprehend/debugger.h"
|
||||
#include "glk/comprehend/dictionary.h"
|
||||
#include "glk/comprehend/draw_surface.h"
|
||||
#include "glk/comprehend/game_data.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace Comprehend {
|
||||
|
||||
void Sentence::clear() {
|
||||
for (uint idx = 0; idx < 4; ++idx)
|
||||
_words[idx].clear();
|
||||
for (uint idx = 0; idx < 6; ++idx)
|
||||
_formattedWords[idx] = 0;
|
||||
|
||||
_nr_words = 0;
|
||||
_specialOpcodeVal2 = 0;
|
||||
}
|
||||
|
||||
void Sentence::copyFrom(const Sentence &src, bool copyNoun) {
|
||||
for (uint idx = (copyNoun ? 0 : 1); idx < 6; ++idx)
|
||||
_formattedWords[idx] = src._formattedWords[idx];
|
||||
}
|
||||
|
||||
void Sentence::format() {
|
||||
for (uint idx = 0; idx < 6; ++idx)
|
||||
_formattedWords[idx] = 0;
|
||||
byte wordTypes[5] = { 0, 0, 0, 0, 0 };
|
||||
|
||||
for (uint idx = 0; idx < _nr_words; ++idx) {
|
||||
const Word &w = _words[idx];
|
||||
|
||||
if (w._type & 8) {
|
||||
if (w._type < 24) {
|
||||
int index, type;
|
||||
|
||||
if (w._type & 0xf0 & wordTypes[2]) {
|
||||
index = _formattedWords[2];
|
||||
type = wordTypes[2];
|
||||
} else if (w._type & 0xf0 & wordTypes[3]) {
|
||||
index = _formattedWords[3];
|
||||
type = wordTypes[3];
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!_formattedWords[2]) {
|
||||
_formattedWords[2] = index;
|
||||
wordTypes[2] = type;
|
||||
} else if (!_formattedWords[3]) {
|
||||
_formattedWords[3] = index;
|
||||
wordTypes[3] = type;
|
||||
}
|
||||
} else {
|
||||
if (w._type == 8)
|
||||
_specialOpcodeVal2 = 1;
|
||||
else if (w._type == 9)
|
||||
_specialOpcodeVal2 = 2;
|
||||
}
|
||||
} else {
|
||||
int val = w._type & 0xf0;
|
||||
|
||||
if (val) {
|
||||
if ((w._type & 1) && !_formattedWords[0]) {
|
||||
_formattedWords[0] = w._index;
|
||||
} else if (!_formattedWords[2]) {
|
||||
_formattedWords[2] = w._index;
|
||||
wordTypes[2] = val;
|
||||
} else if (!_formattedWords[3]) {
|
||||
_formattedWords[3] = w._index;
|
||||
wordTypes[3] = val;
|
||||
}
|
||||
} else if (w._type & 1) {
|
||||
if (!_formattedWords[0]) {
|
||||
_formattedWords[0] = w._index;
|
||||
} else if (!_formattedWords[1]) {
|
||||
_formattedWords[1] = w._index;
|
||||
}
|
||||
} else if (w._type == 2) {
|
||||
if (!_formattedWords[4])
|
||||
_formattedWords[4] = w._index;
|
||||
} else if (w._type == 4) {
|
||||
if (!_formattedWords[5])
|
||||
_formattedWords[5] = w._index;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------*/
|
||||
|
||||
|
||||
ComprehendGame::ComprehendGame() : _gameStrings(nullptr), _ended(false),
|
||||
_functionNum(0), _specialOpcode(0), _nounState(NOUNSTATE_INITIAL),
|
||||
_inputLineIndex(0), _currentRoomCopy(-1), _redoLine(REDO_NONE) {
|
||||
Common::fill(&_inputLine[0], &_inputLine[INPUT_LINE_SIZE], 0);
|
||||
}
|
||||
|
||||
ComprehendGame::~ComprehendGame() {
|
||||
}
|
||||
|
||||
void ComprehendGame::synchronizeSave(Common::Serializer &s) {
|
||||
uint dir, i;
|
||||
size_t nr_rooms, nr_items;
|
||||
|
||||
s.syncAsUint16LE(_currentRoom);
|
||||
|
||||
// Variables
|
||||
for (i = 0; i < ARRAY_SIZE(_variables); i++)
|
||||
s.syncAsUint16LE(_variables[i]);
|
||||
|
||||
// Flags
|
||||
for (i = 0; i < ARRAY_SIZE(_flags); i++)
|
||||
s.syncAsByte(_flags[i]);
|
||||
|
||||
// Rooms. Note that index 0 is the player's inventory
|
||||
nr_rooms = _rooms.size();
|
||||
s.syncAsByte(nr_rooms);
|
||||
assert(nr_rooms == _rooms.size());
|
||||
|
||||
for (i = 1; i < _rooms.size(); ++i) {
|
||||
s.syncAsUint16LE(_rooms[i]._stringDesc);
|
||||
for (dir = 0; dir < NR_DIRECTIONS; dir++)
|
||||
s.syncAsByte(_rooms[i]._direction[dir]);
|
||||
|
||||
s.syncAsByte(_rooms[i]._flags);
|
||||
s.syncAsByte(_rooms[i]._graphic);
|
||||
}
|
||||
|
||||
// Objects
|
||||
nr_items = _items.size();
|
||||
s.syncAsByte(nr_items);
|
||||
assert(nr_items == _items.size());
|
||||
|
||||
for (i = 0; i < _items.size(); ++i)
|
||||
_items[i].synchronize(s);
|
||||
|
||||
_redoLine = REDO_NONE;
|
||||
}
|
||||
|
||||
Common::String ComprehendGame::stringLookup(uint16 index) {
|
||||
uint16 string;
|
||||
uint8 table;
|
||||
|
||||
/*
|
||||
* There are two tables of strings. The first is stored in the main
|
||||
* game data file, and the second is stored in multiple string files.
|
||||
*
|
||||
* In instructions string indexes are split into a table and index
|
||||
* value. In other places such as the save files strings from the
|
||||
* main table are occasionally just a straight 16-bit index. We
|
||||
* convert all string indexes to the former case so that we can handle
|
||||
* them the same everywhere.
|
||||
*/
|
||||
table = (index >> 8) & 0xff;
|
||||
string = index & 0xff;
|
||||
|
||||
switch (table) {
|
||||
case 0x81:
|
||||
case 0x01:
|
||||
string += 0x100;
|
||||
/* Fall-through */
|
||||
case 0x00:
|
||||
case 0x80:
|
||||
if (string < _strings.size())
|
||||
return _strings[string];
|
||||
break;
|
||||
|
||||
case 0x83:
|
||||
string += 0x100;
|
||||
/* Fall-through */
|
||||
case 0x02:
|
||||
case 0x82:
|
||||
if (string < _strings2.size())
|
||||
return _strings2[string];
|
||||
break;
|
||||
}
|
||||
|
||||
return Common::String::format("BAD_STRING(%.4x)", index);
|
||||
}
|
||||
|
||||
Common::String ComprehendGame::instrStringLookup(uint8 index, uint8 table) {
|
||||
return stringLookup(table << 8 | index);
|
||||
}
|
||||
|
||||
int ComprehendGame::console_get_key() {
|
||||
return g_comprehend->readChar();
|
||||
}
|
||||
|
||||
void ComprehendGame::console_println(const char *text) {
|
||||
const char *replace, *word = nullptr, *p = text;
|
||||
char bad_word[64];
|
||||
int word_len = 0;
|
||||
|
||||
if (!text) {
|
||||
g_comprehend->print("\n");
|
||||
return;
|
||||
}
|
||||
|
||||
while (*p) {
|
||||
switch (*p) {
|
||||
case '\n':
|
||||
word = nullptr;
|
||||
word_len = 0;
|
||||
g_comprehend->print("\n");
|
||||
p++;
|
||||
break;
|
||||
|
||||
case '@':
|
||||
/* Replace word */
|
||||
if (_currentReplaceWord >= _replaceWords.size()) {
|
||||
snprintf(bad_word, sizeof(bad_word),
|
||||
"[BAD_REPLACE_WORD(%.2x)]",
|
||||
_currentReplaceWord);
|
||||
word = bad_word;
|
||||
} else {
|
||||
word = _replaceWords[_currentReplaceWord].c_str();
|
||||
}
|
||||
word_len = strlen(word);
|
||||
p++;
|
||||
break;
|
||||
|
||||
default:
|
||||
/* Find next space */
|
||||
word_len = strcspn(p, " \n");
|
||||
if (word_len == 0)
|
||||
break;
|
||||
|
||||
/*
|
||||
* If this word contains a replacement symbol, then
|
||||
* print everything before the symbol.
|
||||
*/
|
||||
replace = strchr(p, '@');
|
||||
if (replace)
|
||||
word_len = replace - p;
|
||||
|
||||
word = p;
|
||||
p += word_len;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!word || !word_len)
|
||||
continue;
|
||||
|
||||
Common::String wordStr(word, word_len);
|
||||
g_comprehend->print("%s", wordStr.c_str());
|
||||
|
||||
if (*p == ' ') {
|
||||
g_comprehend->print(" ");
|
||||
p++;
|
||||
|
||||
/* Skip any double spaces */
|
||||
while (*p == ' ')
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
g_comprehend->print("\n");
|
||||
}
|
||||
|
||||
Room *ComprehendGame::get_room(uint16 index) {
|
||||
/* Room zero is reserved for the players inventory */
|
||||
if (index == 0)
|
||||
error("Room index 0 (player inventory) is invalid");
|
||||
|
||||
if (index >= (int)_rooms.size())
|
||||
error("Room index %d is invalid", index);
|
||||
|
||||
return &_rooms[index];
|
||||
}
|
||||
|
||||
Item *ComprehendGame::get_item(uint16 index) {
|
||||
if (index >= _items.size())
|
||||
error("Bad item %d\n", index);
|
||||
|
||||
return &_items[index];
|
||||
}
|
||||
|
||||
void ComprehendGame::game_save() {
|
||||
int c;
|
||||
|
||||
console_println(_strings[STRING_SAVE_GAME].c_str());
|
||||
|
||||
c = console_get_key();
|
||||
if (g_comprehend->shouldQuit())
|
||||
return;
|
||||
|
||||
if (c < '1' || c > '3') {
|
||||
/*
|
||||
* The original Comprehend games just silently ignore any
|
||||
* invalid selection.
|
||||
*/
|
||||
console_println("Invalid save game number");
|
||||
return;
|
||||
}
|
||||
|
||||
g_comprehend->saveGameState(c - '0', _("Savegame"));
|
||||
}
|
||||
|
||||
void ComprehendGame::game_restore() {
|
||||
int c;
|
||||
|
||||
console_println(_strings[STRING_RESTORE_GAME].c_str());
|
||||
|
||||
c = console_get_key();
|
||||
if (g_comprehend->shouldQuit())
|
||||
return;
|
||||
|
||||
if (c < '1' || c > '3') {
|
||||
/*
|
||||
* The original Comprehend games just silently ignore any
|
||||
* invalid selection.
|
||||
*/
|
||||
console_println("Invalid save game number");
|
||||
return;
|
||||
}
|
||||
|
||||
(void)g_comprehend->loadGameState(c - '0');
|
||||
}
|
||||
|
||||
bool ComprehendGame::handle_restart() {
|
||||
console_println(stringLookup(_gameStrings->game_restart).c_str());
|
||||
_ended = false;
|
||||
|
||||
if (tolower(console_get_key()) == 'r') {
|
||||
loadGame();
|
||||
_updateFlags = UPDATE_ALL;
|
||||
return true;
|
||||
} else {
|
||||
g_comprehend->quitGame();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Item *ComprehendGame::get_item_by_noun(byte noun) {
|
||||
uint i;
|
||||
|
||||
if (!noun)
|
||||
return nullptr;
|
||||
|
||||
/*
|
||||
* FIXME - in oo-topos the word 'box' matches more than one object
|
||||
* (the box and the snarl-in-a-box). The player is unable
|
||||
* to drop the latter because this will match the former.
|
||||
*/
|
||||
for (i = 0; i < _items.size(); i++)
|
||||
if (_items[i]._word == noun)
|
||||
return &_items[i];
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int ComprehendGame::get_item_id(byte noun) {
|
||||
for (int i = 0; i < (int)_items.size(); i++)
|
||||
if (_items[i]._word == noun)
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void ComprehendGame::update_graphics() {
|
||||
Item *item;
|
||||
Room *room;
|
||||
int type;
|
||||
uint i;
|
||||
|
||||
if (!g_comprehend->isGraphicsEnabled())
|
||||
return;
|
||||
|
||||
type = roomIsSpecial(_currentRoomCopy, nullptr);
|
||||
|
||||
switch (type) {
|
||||
case ROOM_IS_DARK:
|
||||
if (_updateFlags & UPDATE_GRAPHICS)
|
||||
g_comprehend->clearScreen(false);
|
||||
break;
|
||||
|
||||
case ROOM_IS_TOO_BRIGHT:
|
||||
if (_updateFlags & UPDATE_GRAPHICS)
|
||||
g_comprehend->clearScreen(true);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (_updateFlags & UPDATE_GRAPHICS) {
|
||||
room = get_room(_currentRoom);
|
||||
g_comprehend->drawLocationPicture(room->_graphic - 1);
|
||||
}
|
||||
|
||||
if ((_updateFlags & UPDATE_GRAPHICS) ||
|
||||
(_updateFlags & UPDATE_GRAPHICS_ITEMS)) {
|
||||
for (i = 0; i < _items.size(); i++) {
|
||||
item = &_items[i];
|
||||
|
||||
if (item->_room == _currentRoom &&
|
||||
item->_graphic != 0)
|
||||
g_comprehend->drawItemPicture(item->_graphic - 1);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ComprehendGame::describe_objects_in_current_room() {
|
||||
Item *item;
|
||||
size_t count = 0;
|
||||
uint i;
|
||||
|
||||
for (i = 0; i < _items.size(); i++) {
|
||||
item = &_items[i];
|
||||
|
||||
if (item->_room == _currentRoom && item->_stringDesc != 0
|
||||
&& !(item->_flags & ITEMF_INVISIBLE))
|
||||
count++;
|
||||
}
|
||||
|
||||
if (count > 0) {
|
||||
console_println(stringLookup(STRING_YOU_SEE).c_str());
|
||||
|
||||
for (i = 0; i < _items.size(); i++) {
|
||||
item = &_items[i];
|
||||
|
||||
if (item->_room == _currentRoom && item->_stringDesc != 0
|
||||
&& !(item->_flags & ITEMF_INVISIBLE))
|
||||
console_println(stringLookup(item->_stringDesc).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ComprehendGame::updateRoomDesc() {
|
||||
Room *room = get_room(_currentRoom);
|
||||
uint room_desc_string = room->_stringDesc;
|
||||
roomIsSpecial(_currentRoom, &room_desc_string);
|
||||
|
||||
Common::String desc = stringLookup(room_desc_string);
|
||||
g_comprehend->printRoomDesc(desc);
|
||||
}
|
||||
|
||||
void ComprehendGame::update() {
|
||||
Room *room = get_room(_currentRoom);
|
||||
uint room_type, room_desc_string;
|
||||
|
||||
update_graphics();
|
||||
|
||||
/* Check if the room is special (dark, too bright, etc) */
|
||||
room_desc_string = room->_stringDesc;
|
||||
room_type = roomIsSpecial(_currentRoom, &room_desc_string);
|
||||
|
||||
if (_updateFlags & UPDATE_ROOM_DESC) {
|
||||
Common::String desc = stringLookup(room_desc_string);
|
||||
console_println(desc.c_str());
|
||||
g_comprehend->printRoomDesc(desc.c_str());
|
||||
}
|
||||
|
||||
if ((_updateFlags & UPDATE_ITEM_LIST) && room_type == ROOM_IS_NORMAL)
|
||||
describe_objects_in_current_room();
|
||||
|
||||
_updateFlags = 0;
|
||||
}
|
||||
|
||||
void ComprehendGame::move_to(uint8 room) {
|
||||
if (room >= (int)_rooms.size())
|
||||
error("Attempted to move to invalid room %.2x\n", room);
|
||||
|
||||
_currentRoom = _currentRoomCopy = room;
|
||||
_updateFlags = (UPDATE_GRAPHICS | UPDATE_ROOM_DESC |
|
||||
UPDATE_ITEM_LIST);
|
||||
}
|
||||
|
||||
size_t ComprehendGame::num_objects_in_room(int room) {
|
||||
size_t count = 0, i;
|
||||
|
||||
for (i = 0; i < _items.size(); i++)
|
||||
if (_items[i]._room == room)
|
||||
count++;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
void ComprehendGame::move_object(Item *item, int new_room) {
|
||||
unsigned obj_weight = item->_flags & ITEMF_WEIGHT_MASK;
|
||||
|
||||
if (item->_room == new_room)
|
||||
return;
|
||||
|
||||
if (item->_room == ROOM_INVENTORY) {
|
||||
/* Removed from player's inventory */
|
||||
_variables[VAR_INVENTORY_WEIGHT] -= obj_weight;
|
||||
}
|
||||
if (new_room == ROOM_INVENTORY) {
|
||||
/* Moving to the player's inventory */
|
||||
_variables[VAR_INVENTORY_WEIGHT] += obj_weight;
|
||||
}
|
||||
|
||||
if (item->_room == _currentRoom) {
|
||||
/* Item moved away from the current room */
|
||||
_updateFlags |= UPDATE_GRAPHICS;
|
||||
|
||||
} else if (new_room == _currentRoom) {
|
||||
/*
|
||||
* Item moved into the current room. Only the item needs a
|
||||
* redraw, not the whole room.
|
||||
*/
|
||||
_updateFlags |= (UPDATE_GRAPHICS_ITEMS |
|
||||
UPDATE_ITEM_LIST);
|
||||
}
|
||||
|
||||
item->_room = new_room;
|
||||
}
|
||||
|
||||
void ComprehendGame::eval_instruction(FunctionState *func_state,
|
||||
const Function &func, uint functionOffset, const Sentence *sentence) {
|
||||
|
||||
const Instruction *instr = &func[functionOffset];
|
||||
|
||||
if (DebugMan.isDebugChannelEnabled(kDebugScripts)) {
|
||||
Common::String line;
|
||||
if (!instr->_isCommand) {
|
||||
line += "? ";
|
||||
} else {
|
||||
if (func_state->_testResult)
|
||||
line += "+ ";
|
||||
else
|
||||
line += "- ";
|
||||
}
|
||||
|
||||
line += Common::String::format("%.2x ", functionOffset);
|
||||
line += g_debugger->dumpInstruction(this, func_state, instr);
|
||||
debugC(kDebugScripts, "%s", line.c_str());
|
||||
}
|
||||
|
||||
if (func_state->_orCount)
|
||||
func_state->_orCount--;
|
||||
|
||||
if (instr->_isCommand) {
|
||||
bool do_command;
|
||||
|
||||
func_state->_inCommand = true;
|
||||
do_command = func_state->_testResult;
|
||||
|
||||
if (func_state->_orCount != 0)
|
||||
g_comprehend->print("Warning: or_count == %d\n",
|
||||
func_state->_orCount);
|
||||
func_state->_orCount = 0;
|
||||
|
||||
if (!do_command)
|
||||
return;
|
||||
|
||||
func_state->_elseResult = false;
|
||||
func_state->_executed = true;
|
||||
|
||||
} else {
|
||||
if (func_state->_inCommand) {
|
||||
/* Finished command sequence - clear test result */
|
||||
func_state->_inCommand = false;
|
||||
func_state->_testResult = false;
|
||||
func_state->_and = false;
|
||||
}
|
||||
}
|
||||
|
||||
execute_opcode(instr, sentence, func_state);
|
||||
}
|
||||
|
||||
void ComprehendGame::eval_function(uint functionNum, const Sentence *sentence) {
|
||||
FunctionState func_state;
|
||||
uint i;
|
||||
|
||||
const Function &func = _functions[functionNum];
|
||||
func_state._elseResult = true;
|
||||
func_state._executed = false;
|
||||
|
||||
debugC(kDebugScripts, "Start of function %.4x", functionNum);
|
||||
|
||||
for (i = 0; i < func.size(); i++) {
|
||||
if (func_state._executed && !func[i]._isCommand) {
|
||||
/*
|
||||
* At least one command has been executed and the
|
||||
* current instruction is a test. Exit the function.
|
||||
*/
|
||||
break;
|
||||
}
|
||||
|
||||
eval_instruction(&func_state, func, i, sentence);
|
||||
}
|
||||
|
||||
debugC(kDebugScripts, "End of function %.4x\n", functionNum);
|
||||
}
|
||||
|
||||
void ComprehendGame::skip_whitespace(const char **p) {
|
||||
while (**p && Common::isSpace(**p))
|
||||
(*p)++;
|
||||
}
|
||||
|
||||
void ComprehendGame::skip_non_whitespace(const char **p) {
|
||||
while (**p && !Common::isSpace(**p) && **p != ',' && **p != '\n')
|
||||
(*p)++;
|
||||
}
|
||||
|
||||
bool ComprehendGame::handle_sentence(Sentence *sentence) {
|
||||
if (sentence->_nr_words == 1 && !strcmp(sentence->_words[0]._word, "quit")) {
|
||||
g_comprehend->quitGame();
|
||||
return true;
|
||||
}
|
||||
|
||||
// Set up default sentence
|
||||
Common::Array<byte> words;
|
||||
const byte *src = &sentence->_formattedWords[0];
|
||||
|
||||
if (src[1] && src[3]) {
|
||||
words.clear();
|
||||
|
||||
for (int idx = 0; idx < 4; ++idx)
|
||||
words.push_back(src[idx]);
|
||||
|
||||
if (handle_sentence(0, sentence, words))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (src[1]) {
|
||||
words.clear();
|
||||
|
||||
for (int idx = 0; idx < 3; ++idx)
|
||||
words.push_back(src[idx]);
|
||||
|
||||
if (handle_sentence(1, sentence, words))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (src[3] && src[4]) {
|
||||
words.clear();
|
||||
|
||||
words.push_back(src[4]);
|
||||
words.push_back(src[0]);
|
||||
words.push_back(src[2]);
|
||||
words.push_back(src[3]);
|
||||
|
||||
if (handle_sentence(2, sentence, words))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (src[4]) {
|
||||
words.clear();
|
||||
|
||||
words.push_back(src[4]);
|
||||
words.push_back(src[0]);
|
||||
words.push_back(src[2]);
|
||||
|
||||
if (handle_sentence(3, sentence, words))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (src[3]) {
|
||||
words.clear();
|
||||
|
||||
words.push_back(src[0]);
|
||||
words.push_back(src[2]);
|
||||
words.push_back(src[3]);
|
||||
|
||||
if (handle_sentence(4, sentence, words))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (src[2]) {
|
||||
words.clear();
|
||||
|
||||
words.push_back(src[0]);
|
||||
words.push_back(src[2]);
|
||||
|
||||
if (handle_sentence(5, sentence, words))
|
||||
return true;
|
||||
}
|
||||
|
||||
if (src[0]) {
|
||||
words.clear();
|
||||
words.push_back(src[0]);
|
||||
|
||||
if (handle_sentence(6, sentence, words))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ComprehendGame::handle_sentence(uint tableNum, Sentence *sentence, Common::Array<byte> &words) {
|
||||
const ActionTable &table = _actions[tableNum];
|
||||
|
||||
for (uint i = 0; i < table.size(); i++) {
|
||||
const Action &action = table[i];
|
||||
|
||||
// Check for a match on the words of the action
|
||||
bool isMatch = true;
|
||||
for (uint idx = 0; idx < action._nr_words && isMatch; ++idx)
|
||||
isMatch = action._words[idx] == words[idx];
|
||||
|
||||
if (isMatch) {
|
||||
// Match
|
||||
_functionNum = action._function;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// No matching action
|
||||
return false;
|
||||
}
|
||||
|
||||
void ComprehendGame::handleAction(Sentence *sentence) {
|
||||
_specialOpcode = 0;
|
||||
|
||||
if (_functionNum == 0) {
|
||||
console_println(stringLookup(STRING_DONT_UNDERSTAND).c_str());
|
||||
} else {
|
||||
eval_function(_functionNum, sentence);
|
||||
_functionNum = 0;
|
||||
eval_function(0, nullptr);
|
||||
}
|
||||
|
||||
handleSpecialOpcode();
|
||||
}
|
||||
|
||||
void ComprehendGame::read_sentence(Sentence *sentence) {
|
||||
bool sentence_end = false;
|
||||
const char *word_string, *p = &_inputLine[_inputLineIndex];
|
||||
Word *word;
|
||||
|
||||
sentence->clear();
|
||||
for (;;) {
|
||||
// Get the next word
|
||||
skip_whitespace(&p);
|
||||
word_string = p;
|
||||
skip_non_whitespace(&p);
|
||||
|
||||
Common::String wordStr(word_string, p);
|
||||
|
||||
// Check for end of sentence
|
||||
// FIXME: The below is a hacked simplified version of how the
|
||||
// original handles cases like "get item1, item2"
|
||||
if (*p == ',' || *p == '\n' || wordStr.equalsIgnoreCase("and")) {
|
||||
// Sentence separator
|
||||
++p;
|
||||
sentence_end = true;
|
||||
} else if (*p == '\0') {
|
||||
sentence_end = true;
|
||||
}
|
||||
|
||||
/* Find the dictionary word for this */
|
||||
word = dict_find_word_by_string(this, wordStr.c_str());
|
||||
if (!word)
|
||||
sentence->_words[sentence->_nr_words].clear();
|
||||
else
|
||||
sentence->_words[sentence->_nr_words] = *word;
|
||||
|
||||
sentence->_nr_words++;
|
||||
|
||||
if (sentence->_nr_words >= ARRAY_SIZE(sentence->_words) ||
|
||||
sentence_end)
|
||||
break;
|
||||
}
|
||||
|
||||
parse_sentence_word_pairs(sentence);
|
||||
sentence->format();
|
||||
|
||||
_inputLineIndex = p - _inputLine;
|
||||
}
|
||||
|
||||
void ComprehendGame::parse_sentence_word_pairs(Sentence *sentence) {
|
||||
if (sentence->_nr_words < 2)
|
||||
return;
|
||||
|
||||
// Iterate through the pairs
|
||||
for (uint idx = 0; idx < _wordMaps.size(); ++idx) {
|
||||
for (int firstWord = 0; firstWord < (int)sentence->_nr_words - 1; ++firstWord) {
|
||||
for (int secondWord = firstWord + 1; secondWord < (int)sentence->_nr_words; ) {
|
||||
if (sentence->_words[firstWord] == _wordMaps[idx]._word[0] &&
|
||||
sentence->_words[secondWord] == _wordMaps[idx]._word[1]) {
|
||||
// Found a word pair match
|
||||
// Delete the second word
|
||||
for (; secondWord < (int)sentence->_nr_words - 1; ++secondWord)
|
||||
sentence->_words[secondWord] = sentence->_words[secondWord + 1];
|
||||
|
||||
sentence->_words[sentence->_nr_words - 1].clear();
|
||||
sentence->_nr_words--;
|
||||
|
||||
// Replace the first word with the target
|
||||
sentence->_words[firstWord] = _wordMaps[idx]._word[2];
|
||||
} else {
|
||||
// Move to next word
|
||||
++secondWord;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ComprehendGame::doBeforeTurn() {
|
||||
// Make a copy of the current room
|
||||
_currentRoomCopy = _currentRoom;
|
||||
|
||||
beforeTurn();
|
||||
|
||||
if (!_ended)
|
||||
update();
|
||||
}
|
||||
|
||||
void ComprehendGame::beforeTurn() {
|
||||
// Run the each turn functions
|
||||
eval_function(0, nullptr);
|
||||
}
|
||||
|
||||
void ComprehendGame::read_input() {
|
||||
Sentence tempSentence;
|
||||
bool handled;
|
||||
|
||||
turn:
|
||||
doBeforeTurn();
|
||||
if (_ended)
|
||||
return;
|
||||
|
||||
// If we're in full screen text, we can afford a blank row between
|
||||
// any game response and the next line of text
|
||||
if (!g_comprehend->isGraphicsEnabled())
|
||||
g_comprehend->print("\n");
|
||||
|
||||
beforePrompt();
|
||||
|
||||
for (;;) {
|
||||
_redoLine = REDO_NONE;
|
||||
g_comprehend->print("> ");
|
||||
g_comprehend->readLine(_inputLine, INPUT_LINE_SIZE);
|
||||
if (g_comprehend->shouldQuit())
|
||||
return;
|
||||
|
||||
_inputLineIndex = 0;
|
||||
if (strlen(_inputLine) == 0) {
|
||||
// Empty line, so toggle picture window visibility
|
||||
if (!g_comprehend->toggleGraphics())
|
||||
updateRoomDesc();
|
||||
g_comprehend->print(_("Picture window toggled\n"));
|
||||
|
||||
_updateFlags |= UPDATE_GRAPHICS;
|
||||
update_graphics();
|
||||
continue;
|
||||
}
|
||||
|
||||
afterPrompt();
|
||||
|
||||
if (_redoLine == REDO_NONE)
|
||||
break;
|
||||
else if (_redoLine == REDO_TURN)
|
||||
goto turn;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
NounState prevNounState = _nounState;
|
||||
_nounState = NOUNSTATE_STANDARD;
|
||||
|
||||
read_sentence(&tempSentence);
|
||||
_sentence.copyFrom(tempSentence, tempSentence._formattedWords[0] || prevNounState != NOUNSTATE_STANDARD);
|
||||
|
||||
handled = handle_sentence(&_sentence);
|
||||
handleAction(&_sentence);
|
||||
|
||||
if (!handled)
|
||||
return;
|
||||
|
||||
/* FIXME - handle the 'before you can continue' case */
|
||||
if (_inputLine[_inputLineIndex] == '\0')
|
||||
break;
|
||||
}
|
||||
|
||||
afterTurn();
|
||||
}
|
||||
|
||||
void ComprehendGame::playGame() {
|
||||
if (!g_comprehend->loadLauncherSavegameIfNeeded())
|
||||
beforeGame();
|
||||
|
||||
_updateFlags = (uint)UPDATE_ALL;
|
||||
while (!g_comprehend->shouldQuit()) {
|
||||
read_input();
|
||||
|
||||
if (_ended && !handle_restart())
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint ComprehendGame::getRandomNumber(uint max) const {
|
||||
return g_comprehend->getRandomNumber(max);
|
||||
}
|
||||
|
||||
void ComprehendGame::doMovementVerb(uint verbNum) {
|
||||
assert(verbNum >= 1 && verbNum <= NR_DIRECTIONS);
|
||||
Room *room = get_room(_currentRoom);
|
||||
byte newRoom = room->_direction[verbNum - 1];
|
||||
|
||||
if (newRoom)
|
||||
move_to(newRoom);
|
||||
else
|
||||
console_println(_strings[0].c_str());
|
||||
}
|
||||
|
||||
void ComprehendGame::weighInventory() {
|
||||
_totalInventoryWeight = 0;
|
||||
if (!g_debugger->_invLimit)
|
||||
// Allow for an unlimited number of items in inventory
|
||||
return;
|
||||
|
||||
for (int idx = _itemCount - 1; idx > 0; --idx) {
|
||||
Item *item = get_item(idx);
|
||||
if (item->_room == ROOM_INVENTORY)
|
||||
_totalInventoryWeight += item->_flags & ITEMF_WEIGHT_MASK;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Comprehend
|
||||
} // namespace Glk
|
||||
204
engines/glk/comprehend/game.h
Normal file
204
engines/glk/comprehend/game.h
Normal file
@@ -0,0 +1,204 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GLK_COMPREHEND_GAME_H
|
||||
#define GLK_COMPREHEND_GAME_H
|
||||
|
||||
#include "glk/comprehend/game_data.h"
|
||||
#include "common/array.h"
|
||||
#include "common/serializer.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace Comprehend {
|
||||
|
||||
#define ROOM_IS_NORMAL 0
|
||||
#define ROOM_IS_DARK 1
|
||||
#define ROOM_IS_TOO_BRIGHT 2
|
||||
#define INPUT_LINE_SIZE 1024
|
||||
|
||||
enum NounState { NOUNSTATE_STANDARD = 0, NOUNSTATE_QUERY = 1, NOUNSTATE_INITIAL = 2 };
|
||||
|
||||
enum RedoLine { REDO_NONE, REDO_PROMPT, REDO_TURN };
|
||||
|
||||
struct GameStrings;
|
||||
struct Sentence;
|
||||
|
||||
struct Sentence {
|
||||
Word _words[20];
|
||||
size_t _nr_words;
|
||||
byte _formattedWords[6];
|
||||
byte _specialOpcodeVal2;
|
||||
|
||||
Sentence() {
|
||||
clear();
|
||||
}
|
||||
|
||||
bool empty() const {
|
||||
return !_formattedWords[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the sentence
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* Copies from another sentence to this one
|
||||
*/
|
||||
void copyFrom(const Sentence &src, bool copyNoun = true);
|
||||
|
||||
/**
|
||||
* Splits up the array of _words into a _formattedWords
|
||||
* array, placing the words in appropriate noun, verb, etc.
|
||||
* positions appropriately
|
||||
*/
|
||||
void format();
|
||||
};
|
||||
|
||||
class ComprehendGame : public GameData {
|
||||
protected:
|
||||
bool _ended;
|
||||
NounState _nounState;
|
||||
Sentence _sentence;
|
||||
char _inputLine[INPUT_LINE_SIZE];
|
||||
int _inputLineIndex;
|
||||
int _currentRoomCopy;
|
||||
int _functionNum;
|
||||
int _specialOpcode;
|
||||
RedoLine _redoLine;
|
||||
public:
|
||||
const GameStrings *_gameStrings;
|
||||
|
||||
private:
|
||||
void describe_objects_in_current_room();
|
||||
void eval_instruction(FunctionState *func_state,
|
||||
const Function &func, uint functionOffset,
|
||||
const Sentence *sentence);
|
||||
void skip_whitespace(const char **p);
|
||||
void skip_non_whitespace(const char **p);
|
||||
bool handle_sentence(Sentence *sentence);
|
||||
bool handle_sentence(uint tableNum, Sentence *sentence, Common::Array<byte> &words);
|
||||
void read_sentence(Sentence *sentence);
|
||||
void parse_sentence_word_pairs(Sentence *sentence);
|
||||
void read_input();
|
||||
void doBeforeTurn();
|
||||
|
||||
protected:
|
||||
void game_save();
|
||||
void game_restore();
|
||||
void game_restart() {
|
||||
_ended = true;
|
||||
}
|
||||
virtual bool handle_restart();
|
||||
|
||||
virtual void execute_opcode(const Instruction *instr, const Sentence *sentence,
|
||||
FunctionState *func_state) = 0;
|
||||
|
||||
int console_get_key();
|
||||
void console_println(const char *text);
|
||||
void move_object(Item *item, int new_room);
|
||||
|
||||
/*
|
||||
* Comprehend functions consist of test and command instructions (if the MSB
|
||||
* of the opcode is set then it is a command). Functions are parsed by
|
||||
* evaluating each test until a command instruction is encountered. If the
|
||||
* overall result of the tests was true then the command instructions are
|
||||
* executed until either a test instruction is found or the end of the function
|
||||
* is reached. Otherwise the commands instructions are skipped over and the
|
||||
* next test sequence (if there is one) is tried.
|
||||
*/
|
||||
void eval_function(uint functionNum, const Sentence *sentence);
|
||||
|
||||
void parse_header(FileBuffer *fb) override {
|
||||
GameData::parse_header(fb);
|
||||
}
|
||||
|
||||
Item *get_item_by_noun(byte noun);
|
||||
int get_item_id(byte noun);
|
||||
void weighInventory();
|
||||
size_t num_objects_in_room(int room);
|
||||
void doMovementVerb(uint verbNum);
|
||||
|
||||
public:
|
||||
ComprehendGame();
|
||||
virtual ~ComprehendGame();
|
||||
|
||||
/**
|
||||
* Called before the game starts
|
||||
*/
|
||||
virtual void beforeGame() {}
|
||||
|
||||
/**
|
||||
* Called just before the prompt for user input
|
||||
*/
|
||||
virtual void beforePrompt() {}
|
||||
|
||||
/**
|
||||
* Called after input has been entered.
|
||||
*/
|
||||
virtual void afterPrompt() {}
|
||||
|
||||
/**
|
||||
* Called before the start of a game turn
|
||||
*/
|
||||
virtual void beforeTurn();
|
||||
|
||||
/**
|
||||
* Called at the end of a game turn
|
||||
*/
|
||||
virtual void afterTurn() {}
|
||||
|
||||
/**
|
||||
* Called when an action function has been selected
|
||||
*/
|
||||
virtual void handleAction(Sentence *sentence);
|
||||
|
||||
virtual int roomIsSpecial(uint room_index, uint *room_desc_string) {
|
||||
return ROOM_IS_NORMAL;
|
||||
}
|
||||
virtual void handleSpecialOpcode() {}
|
||||
|
||||
virtual void synchronizeSave(Common::Serializer &s);
|
||||
|
||||
virtual ScriptOpcode getScriptOpcode(const Instruction *instr) = 0;
|
||||
|
||||
Common::String stringLookup(uint16 index);
|
||||
Common::String instrStringLookup(uint8 index, uint8 table);
|
||||
|
||||
virtual void playGame();
|
||||
|
||||
void move_to(uint8 room);
|
||||
Room *get_room(uint16 index);
|
||||
Item *get_item(uint16 index);
|
||||
void updateRoomDesc();
|
||||
void update();
|
||||
void update_graphics();
|
||||
|
||||
/**
|
||||
* Gets a random number
|
||||
*/
|
||||
uint getRandomNumber(uint max) const;
|
||||
};
|
||||
|
||||
} // namespace Comprehend
|
||||
} // namespace Glk
|
||||
|
||||
#endif
|
||||
181
engines/glk/comprehend/game_cc.cpp
Normal file
181
engines/glk/comprehend/game_cc.cpp
Normal file
@@ -0,0 +1,181 @@
|
||||
/* 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/comprehend/game_cc.h"
|
||||
#include "glk/comprehend/comprehend.h"
|
||||
#include "glk/comprehend/pics.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace Comprehend {
|
||||
|
||||
static const GameStrings CC1_STRINGS = {0x9};
|
||||
|
||||
CrimsonCrownGame::CrimsonCrownGame() : ComprehendGameV1(),
|
||||
_diskNum(1), _newDiskNum(1) {
|
||||
setupDisk(1);
|
||||
}
|
||||
|
||||
void CrimsonCrownGame::setupDisk(uint diskNum) {
|
||||
assert(diskNum == 1 || diskNum == 2);
|
||||
|
||||
_gameDataFile = Common::String::format("cc%u.gda", diskNum);
|
||||
|
||||
_stringFiles.clear();
|
||||
_stringFiles.push_back(Common::String::format("ma.ms%u", diskNum).c_str());
|
||||
|
||||
_locationGraphicFiles.clear();
|
||||
_locationGraphicFiles.push_back(Common::String::format("ra.ms%u", diskNum));
|
||||
_locationGraphicFiles.push_back(Common::String::format("rb.ms%u", diskNum));
|
||||
if (diskNum == 1)
|
||||
_locationGraphicFiles.push_back("RC.ms1");
|
||||
|
||||
_itemGraphicFiles.clear();
|
||||
_itemGraphicFiles.push_back(Common::String::format("oa.ms%u", diskNum));
|
||||
_itemGraphicFiles.push_back(Common::String::format("ob.ms%u", diskNum));
|
||||
|
||||
if (diskNum == 1)
|
||||
_gameStrings = &CC1_STRINGS;
|
||||
else
|
||||
_gameStrings = nullptr;
|
||||
|
||||
_titleGraphicFile = "cctitle.ms1";
|
||||
_diskNum = diskNum;
|
||||
}
|
||||
|
||||
void CrimsonCrownGame::beforeGame() {
|
||||
// Draw the title
|
||||
g_comprehend->drawPicture(TITLE_IMAGE);
|
||||
g_comprehend->readChar();
|
||||
}
|
||||
|
||||
void CrimsonCrownGame::synchronizeSave(Common::Serializer &s) {
|
||||
if (s.isSaving()) {
|
||||
s.syncAsByte(_diskNum);
|
||||
} else {
|
||||
// Get the disk the save is for. The beforeTurn call allows
|
||||
// for the currently loaded disk to be switched if necessary
|
||||
s.syncAsByte(_newDiskNum);
|
||||
beforeTurn();
|
||||
}
|
||||
|
||||
ComprehendGame::synchronizeSave(s);
|
||||
}
|
||||
|
||||
void CrimsonCrownGame::handleSpecialOpcode() {
|
||||
switch (_specialOpcode) {
|
||||
case 1:
|
||||
// Crystyal ball cutscene
|
||||
if (_diskNum == 1) {
|
||||
crystalBallCutscene();
|
||||
} else {
|
||||
throneCutscene();
|
||||
}
|
||||
break;
|
||||
|
||||
case 3:
|
||||
// Game over - failure
|
||||
game_restart();
|
||||
break;
|
||||
|
||||
case 5:
|
||||
if (_diskNum == 1) {
|
||||
// Finished disk 1
|
||||
g_comprehend->readChar();
|
||||
g_comprehend->drawLocationPicture(41);
|
||||
console_println(_strings2[26].c_str());
|
||||
g_comprehend->readChar();
|
||||
|
||||
_newDiskNum = 2;
|
||||
move_to(21);
|
||||
console_println(_strings[407].c_str());
|
||||
|
||||
} else {
|
||||
// Won the game
|
||||
g_comprehend->drawLocationPicture(29, false);
|
||||
g_comprehend->drawItemPicture(20);
|
||||
console_println(stringLookup(0x21c).c_str());
|
||||
console_println(stringLookup(0x21d).c_str());
|
||||
|
||||
g_comprehend->readChar();
|
||||
g_comprehend->quitGame();
|
||||
}
|
||||
break;
|
||||
|
||||
case 6:
|
||||
game_save();
|
||||
break;
|
||||
|
||||
case 7:
|
||||
game_restore();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CrimsonCrownGame::crystalBallCutscene() {
|
||||
g_comprehend->showGraphics();
|
||||
|
||||
for (int screenNum = 38; screenNum <= 40; ++screenNum) {
|
||||
g_comprehend->drawLocationPicture(screenNum);
|
||||
g_comprehend->readChar();
|
||||
if (g_comprehend->shouldQuit())
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void CrimsonCrownGame::throneCutscene() {
|
||||
// Show the screen
|
||||
update();
|
||||
console_println(stringLookup(0x20A).c_str());
|
||||
|
||||
// Handle what happens in climatic showdown
|
||||
eval_function(14, nullptr);
|
||||
}
|
||||
|
||||
void CrimsonCrownGame::beforePrompt() {
|
||||
// Clear the Sabrina/Erik action flags
|
||||
_flags[0xa] = 0;
|
||||
_flags[0xb] = 0;
|
||||
}
|
||||
|
||||
void CrimsonCrownGame::beforeTurn() {
|
||||
if (_newDiskNum != _diskNum) {
|
||||
setupDisk(_newDiskNum);
|
||||
loadGame();
|
||||
move_to(_currentRoom);
|
||||
}
|
||||
|
||||
ComprehendGameV1::beforeTurn();
|
||||
}
|
||||
|
||||
bool CrimsonCrownGame::handle_restart() {
|
||||
if (_diskNum != 1) {
|
||||
setupDisk(1);
|
||||
loadGame();
|
||||
}
|
||||
|
||||
return ComprehendGame::handle_restart();
|
||||
}
|
||||
|
||||
} // namespace Comprehend
|
||||
} // namespace Glk
|
||||
64
engines/glk/comprehend/game_cc.h
Normal file
64
engines/glk/comprehend/game_cc.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GLK_COMPREHEND_GAME_CC_H
|
||||
#define GLK_COMPREHEND_GAME_CC_H
|
||||
|
||||
#include "glk/comprehend/game_opcodes.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace Comprehend {
|
||||
|
||||
class CrimsonCrownGame : public ComprehendGameV1 {
|
||||
private:
|
||||
uint _diskNum;
|
||||
uint _newDiskNum;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Cutscene triggered when looking at crystal ball
|
||||
*/
|
||||
void crystalBallCutscene();
|
||||
|
||||
/**
|
||||
* Start of throneroom cutscene
|
||||
*/
|
||||
void throneCutscene();
|
||||
|
||||
protected:
|
||||
bool handle_restart() override;
|
||||
public:
|
||||
CrimsonCrownGame();
|
||||
~CrimsonCrownGame() override {}
|
||||
|
||||
void beforeGame() override;
|
||||
void beforePrompt() override;
|
||||
void beforeTurn() override;
|
||||
void handleSpecialOpcode() override;
|
||||
void synchronizeSave(Common::Serializer &s) override;
|
||||
|
||||
void setupDisk(uint diskNum);
|
||||
};
|
||||
|
||||
} // namespace Comprehend
|
||||
} // namespace Glk
|
||||
|
||||
#endif
|
||||
726
engines/glk/comprehend/game_data.cpp
Normal file
726
engines/glk/comprehend/game_data.cpp
Normal file
@@ -0,0 +1,726 @@
|
||||
/* 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/comprehend/game_data.h"
|
||||
#include "glk/comprehend/comprehend.h"
|
||||
#include "glk/comprehend/dictionary.h"
|
||||
#include "glk/comprehend/draw_surface.h"
|
||||
#include "glk/comprehend/file_buf.h"
|
||||
#include "glk/comprehend/game.h"
|
||||
#include "glk/comprehend/pics.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace Comprehend {
|
||||
|
||||
static const char CHARSET[] = "..abcdefghijklmnopqrstuvwxyz .";
|
||||
static const char SPECIAL_CHARSET[] = "[]\n!\"#$%&'(),-/0123456789:;?<>";
|
||||
|
||||
#define STRING_FILE_COUNT 64
|
||||
|
||||
void FunctionState::clear() {
|
||||
_testResult = true;
|
||||
_elseResult = false;
|
||||
_orCount = 0;
|
||||
_and = false;
|
||||
_inCommand = false;
|
||||
_executed = false;
|
||||
_notComparison = false;
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------*/
|
||||
|
||||
void Room::clear() {
|
||||
_flags = 0;
|
||||
_graphic = 0;
|
||||
_stringDesc = 0;
|
||||
Common::fill(&_direction[0], &_direction[NR_DIRECTIONS], 0);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------*/
|
||||
|
||||
void Item::clear() {
|
||||
_stringDesc = 0;
|
||||
_longString = 0;
|
||||
_room = 0;
|
||||
_flags = 0;
|
||||
_word = 0;
|
||||
_graphic = 0;
|
||||
}
|
||||
|
||||
void Item::synchronize(Common::Serializer &s) {
|
||||
s.syncAsUint16LE(_stringDesc);
|
||||
s.syncAsUint16LE(_longString);
|
||||
s.syncAsByte(_room);
|
||||
s.syncAsByte(_flags);
|
||||
s.syncAsByte(_word);
|
||||
s.syncAsByte(_graphic);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------*/
|
||||
|
||||
void Word::clear() {
|
||||
WordIndex::clear();
|
||||
Common::fill(&_word[0], &_word[7], '\0');
|
||||
}
|
||||
|
||||
Word &Word::operator=(const WordIndex &src) {
|
||||
_index = src._index;
|
||||
_type = src._type;
|
||||
Common::fill(&_word[0], &_word[7], '\0');
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Word::load(FileBuffer *fb) {
|
||||
fb->read(_word, 6);
|
||||
|
||||
// Decode
|
||||
for (int j = 0; j < 6; j++)
|
||||
_word[j] = tolower((char)(_word[j] ^ 0xaa));
|
||||
|
||||
// Strip off trailing spaces
|
||||
_word[6] = '\0';
|
||||
for (int j = 5; j > 0 && _word[j] == ' '; --j)
|
||||
_word[j] = '\0';
|
||||
|
||||
_index = fb->readByte();
|
||||
_type = fb->readByte();
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------*/
|
||||
|
||||
void WordMap::clear() {
|
||||
_flags = 0;
|
||||
for (int idx = 0; idx < 3; ++idx)
|
||||
_word[idx].clear();
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------*/
|
||||
|
||||
void Action::clear() {
|
||||
_nr_words = 0;
|
||||
_function = 0;
|
||||
Common::fill(&_words[0], &_words[4], 0);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------*/
|
||||
|
||||
Instruction::Instruction(byte opcode, byte op1, byte op2, byte op3) : _opcode(opcode) {
|
||||
_operand[0] = op1;
|
||||
_operand[1] = op2;
|
||||
_operand[2] = op3;
|
||||
}
|
||||
|
||||
void Instruction::clear() {
|
||||
_opcode = 0;
|
||||
_nr_operands = 0;
|
||||
_isCommand = false;
|
||||
Common::fill(&_operand[0], &_operand[3], 0);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------*/
|
||||
|
||||
void GameHeader::clear() {
|
||||
magic = 0;
|
||||
room_desc_table = 0;
|
||||
room_flags_table = 0;
|
||||
room_graphics_table = 0;
|
||||
nr_items = 0;
|
||||
addr_item_locations = 0;
|
||||
addr_item_flags = 0;
|
||||
addr_item_word = 0;
|
||||
addr_item_strings = 0;
|
||||
addr_item_graphics = 0;
|
||||
addr_dictionary = 0;
|
||||
addr_word_map = 0;
|
||||
addr_strings = 0;
|
||||
addr_strings_end = 0;
|
||||
|
||||
Common::fill(&addr_actions[0], &addr_actions[7], 0);
|
||||
Common::fill(&room_direction_table[0], &room_direction_table[NR_DIRECTIONS], 0);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------*/
|
||||
|
||||
void GameData::clearGame() {
|
||||
_header.clear();
|
||||
_magicWord = 0;
|
||||
_comprehendVersion = 0;
|
||||
_startRoom = 0;
|
||||
_currentRoom = 0;
|
||||
_currentReplaceWord = 0;
|
||||
_wordFlags = 0;
|
||||
_updateFlags = 0;
|
||||
_colorTable = 0;
|
||||
_itemCount = 0;
|
||||
_totalInventoryWeight = 0;
|
||||
|
||||
_strings.clear();
|
||||
_strings2.clear();
|
||||
_rooms.clear();
|
||||
_items.clear();
|
||||
_wordMaps.clear();
|
||||
_actions.clear();
|
||||
_functions.clear();
|
||||
_replaceWords.clear();
|
||||
|
||||
Common::fill(&_flags[0], &_flags[MAX_FLAGS], false);
|
||||
Common::fill(&_variables[0], &_variables[MAX_VARIABLES], 0);
|
||||
}
|
||||
|
||||
void GameData::parse_header_le16(FileBuffer *fb, uint16 *val) {
|
||||
*val = fb->readUint16LE();
|
||||
*val += _magicWord;
|
||||
}
|
||||
|
||||
uint8 GameData::parse_vm_instruction(FileBuffer *fb,
|
||||
Instruction *instr) {
|
||||
uint i;
|
||||
|
||||
/* Get the opcode */
|
||||
instr->_opcode = fb->readByte();
|
||||
instr->_nr_operands = opcode_nr_operands(instr->_opcode);
|
||||
|
||||
/* Get the operands */
|
||||
for (i = 0; i < instr->_nr_operands; i++)
|
||||
instr->_operand[i] = fb->readByte();
|
||||
|
||||
instr->_isCommand = opcode_is_command(instr->_opcode);
|
||||
|
||||
return instr->_opcode;
|
||||
}
|
||||
|
||||
#define MAX_FUNCTION_SIZE 0x100
|
||||
|
||||
void GameData::parse_function(FileBuffer *fb, Function *func) {
|
||||
const uint8 *p;
|
||||
uint8 opcode;
|
||||
|
||||
p = (const uint8 *)memchr(fb->dataPtr(), 0x00, fb->size() - fb->pos());
|
||||
if (!p)
|
||||
error("bad function @ %.4x", (int)fb->pos());
|
||||
|
||||
for (;;) {
|
||||
Instruction instruction;
|
||||
|
||||
opcode = parse_vm_instruction(fb, &instruction);
|
||||
if (opcode == 0)
|
||||
break;
|
||||
|
||||
func->push_back(instruction);
|
||||
assert(func->size() <= MAX_FUNCTION_SIZE);
|
||||
}
|
||||
|
||||
assert(fb->dataPtr() == (p + 1));
|
||||
}
|
||||
|
||||
void GameData::parse_vm(FileBuffer *fb) {
|
||||
fb->seek(_header.addr_vm);
|
||||
|
||||
while (1) {
|
||||
Function func;
|
||||
|
||||
parse_function(fb, &func);
|
||||
if (func.empty())
|
||||
break;
|
||||
|
||||
_functions.push_back(func);
|
||||
|
||||
// WORKAROUND: Parsing functions for Talisman
|
||||
if (_functions.size() == 0x1d8 && g_vm->getGameID() == "talisman")
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void GameData::parse_action_tables(FileBuffer *fb) {
|
||||
uint8 verb, count;
|
||||
uint i, j;
|
||||
|
||||
_actions.clear();
|
||||
_actions.resize(7);
|
||||
|
||||
const byte NUM_WORDS[7] = { 3, 2, 3, 2, 2, 1, 0 };
|
||||
|
||||
for (int tableNum = 0; tableNum < 7; ++tableNum) {
|
||||
ActionTable &table = _actions[tableNum];
|
||||
|
||||
fb->seek(_header.addr_actions[tableNum]);
|
||||
while (1) {
|
||||
verb = fb->readByte();
|
||||
if (verb == 0)
|
||||
break;
|
||||
|
||||
count = fb->readByte();
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
Action action;
|
||||
action._nr_words = NUM_WORDS[tableNum] + 1;
|
||||
action._words[0] = verb;
|
||||
|
||||
for (j = 1; j < action._nr_words; j++)
|
||||
action._words[j] = fb->readByte();
|
||||
action._function = fb->readUint16LE();
|
||||
|
||||
table.push_back(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameData::parse_dictionary(FileBuffer *fb) {
|
||||
fb->seek(_header.addr_dictionary);
|
||||
|
||||
for (uint i = 0; i < _words.size(); i++)
|
||||
_words[i].load(fb);
|
||||
}
|
||||
|
||||
void GameData::parse_word_map(FileBuffer *fb) {
|
||||
uint8 index, type;
|
||||
uint i;
|
||||
|
||||
_wordMaps.clear();
|
||||
fb->seek(_header.addr_word_map);
|
||||
|
||||
/*
|
||||
* Parse the word pair table. Each entry has a pair of dictionary
|
||||
* index/type values for a first and second word.
|
||||
*/
|
||||
while (1) {
|
||||
WordMap map;
|
||||
|
||||
index = fb->readByte();
|
||||
type = fb->readByte();
|
||||
if (type == 0 && index == 0) {
|
||||
/* End of pairs */
|
||||
break;
|
||||
}
|
||||
|
||||
map._word[0]._index = index;
|
||||
map._word[0]._type = type;
|
||||
map._flags = fb->readByte();
|
||||
map._word[1]._index = fb->readByte();
|
||||
map._word[1]._type = fb->readByte();
|
||||
|
||||
_wordMaps.push_back(map);
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the target word table. Each entry has a dictionary
|
||||
* index/type. The first and second words from above map to the
|
||||
* target word here. E.g. 'go north' -> 'north'.
|
||||
*/
|
||||
fb->seek(_header.addr_word_map_target);
|
||||
|
||||
for (i = 0; i < _wordMaps.size(); i++) {
|
||||
WordMap &map = _wordMaps[i];
|
||||
|
||||
map._word[2]._index = fb->readByte();
|
||||
map._word[2]._type = fb->readByte();
|
||||
}
|
||||
}
|
||||
|
||||
void GameData::parse_items(FileBuffer *fb) {
|
||||
size_t nr_items = _header.nr_items;
|
||||
_items.resize(nr_items);
|
||||
|
||||
/* Item descriptions */
|
||||
fb->seek(_header.addr_item_strings);
|
||||
file_buf_get_array_le16(fb, 0, _items, _stringDesc, nr_items);
|
||||
|
||||
if (_comprehendVersion == 2) {
|
||||
/* Comprehend version 2 adds long string descriptions */
|
||||
fb->seek(_header.addr_item_strings +
|
||||
(_items.size() * sizeof(uint16)));
|
||||
file_buf_get_array_le16(fb, 0, _items, _longString, nr_items);
|
||||
}
|
||||
|
||||
/* Item flags */
|
||||
fb->seek(_header.addr_item_flags);
|
||||
file_buf_get_array_u8(fb, 0, _items, _flags, nr_items);
|
||||
|
||||
/* Item word */
|
||||
fb->seek(_header.addr_item_word);
|
||||
file_buf_get_array_u8(fb, 0, _items, _word, nr_items);
|
||||
|
||||
/* Item locations */
|
||||
fb->seek(_header.addr_item_locations);
|
||||
file_buf_get_array_u8(fb, 0, _items, _room, nr_items);
|
||||
|
||||
/* Item graphic */
|
||||
fb->seek(_header.addr_item_graphics);
|
||||
file_buf_get_array_u8(fb, 0, _items, _graphic, nr_items);
|
||||
}
|
||||
|
||||
void GameData::parse_rooms(FileBuffer *fb) {
|
||||
size_t nr_rooms = _rooms.size() - 1;
|
||||
int i;
|
||||
|
||||
/* Room exit directions */
|
||||
for (i = 0; i < NR_DIRECTIONS; i++) {
|
||||
fb->seek(_header.room_direction_table[i]);
|
||||
file_buf_get_array_u8(fb, 1, _rooms, _direction[i], nr_rooms);
|
||||
}
|
||||
|
||||
/* Room string descriptions */
|
||||
fb->seek(_header.room_desc_table);
|
||||
file_buf_get_array_le16(fb, 1, _rooms, _stringDesc, nr_rooms);
|
||||
|
||||
/* Room flags */
|
||||
fb->seek(_header.room_flags_table);
|
||||
file_buf_get_array_u8(fb, 1, _rooms, _flags, nr_rooms);
|
||||
|
||||
/* Room graphic */
|
||||
fb->seek(_header.room_graphics_table);
|
||||
file_buf_get_array_u8(fb, 1, _rooms, _graphic, nr_rooms);
|
||||
}
|
||||
|
||||
uint64 GameData::string_get_chunk(uint8 *string) {
|
||||
uint64 c, val = 0;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 5; i++) {
|
||||
c = string[i] & 0xff;
|
||||
val |= (c << ((4 - i) * 8));
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
char GameData::decode_string_elem(uint8 c, bool capital, bool special) {
|
||||
if (special) {
|
||||
if (c < sizeof(SPECIAL_CHARSET) - 1)
|
||||
return SPECIAL_CHARSET[c];
|
||||
} else {
|
||||
if (c < sizeof(CHARSET) - 1) {
|
||||
c = CHARSET[c];
|
||||
if (capital) {
|
||||
/*
|
||||
* A capital space means that the character
|
||||
* is dynamically replaced by at runtime.
|
||||
* We use the character '@' since it cannot
|
||||
* otherwise appear in strings.
|
||||
*/
|
||||
if (c == ' ')
|
||||
return '@';
|
||||
return c - 0x20;
|
||||
} else {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Unknown character
|
||||
g_comprehend->print("Unknown char %d, caps=%d, special=%d\n", c, capital, special);
|
||||
return '*';
|
||||
}
|
||||
|
||||
Common::String GameData::parseString(FileBuffer *fb) {
|
||||
bool capital_next = false, special_next = false;
|
||||
unsigned i, j;
|
||||
uint64 chunk;
|
||||
uint8 elem, *encoded;
|
||||
char c;
|
||||
size_t encoded_len;
|
||||
Common::String string;
|
||||
|
||||
encoded_len = fb->strlen();
|
||||
|
||||
/* Get the encoded string */
|
||||
encoded = (uint8 *)malloc(encoded_len + 5);
|
||||
Common::fill(encoded, encoded + encoded_len + 5, 0);
|
||||
fb->read(encoded, encoded_len);
|
||||
|
||||
/* Skip over the zero byte */
|
||||
if (fb->pos() < fb->size())
|
||||
fb->skip(1);
|
||||
|
||||
for (i = 0; i < encoded_len; i += 5) {
|
||||
chunk = string_get_chunk(&encoded[i]);
|
||||
|
||||
for (j = 0; j < 8; j++) {
|
||||
elem = (chunk >> (35 - (5 * j))) & 0x1f;
|
||||
|
||||
if (elem == 0)
|
||||
goto done;
|
||||
if (elem == 0x1e) {
|
||||
capital_next = true;
|
||||
} else if (elem == 0x1f) {
|
||||
special_next = true;
|
||||
} else {
|
||||
c = decode_string_elem(elem, capital_next,
|
||||
special_next);
|
||||
special_next = false;
|
||||
capital_next = false;
|
||||
string += c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
free(encoded);
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
void GameData::parse_string_table(FileBuffer *fb, uint start_addr,
|
||||
uint32 end_addr, StringTable *table) {
|
||||
if (start_addr < end_addr) {
|
||||
fb->seek(start_addr);
|
||||
while (1) {
|
||||
table->push_back(parseString(fb));
|
||||
if (fb->pos() >= (int32)end_addr)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameData::parse_variables(FileBuffer *fb) {
|
||||
uint i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(_variables); i++)
|
||||
_variables[i] = fb->readUint16LE();
|
||||
}
|
||||
|
||||
void GameData::parse_flags(FileBuffer *fb) {
|
||||
uint i, flag_index = 0;
|
||||
int bit;
|
||||
uint8 bitmask;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(_flags) / 8; i++) {
|
||||
bitmask = fb->readByte();
|
||||
for (bit = 7; bit >= 0; bit--) {
|
||||
_flags[flag_index] = !!(bitmask & (1 << bit));
|
||||
flag_index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameData::parse_replace_words(FileBuffer *fb) {
|
||||
size_t len;
|
||||
bool eof;
|
||||
|
||||
/* FIXME - Rename addr_strings_end */
|
||||
fb->seek(_header.addr_strings_end);
|
||||
|
||||
/* FIXME - what is this for */
|
||||
fb->skip(2);
|
||||
|
||||
for (;;) {
|
||||
len = fb->strlen(&eof);
|
||||
if (len == 0)
|
||||
break;
|
||||
|
||||
_replaceWords.push_back(Common::String((const char *)fb->dataPtr(), len));
|
||||
fb->skip(len + (eof ? 0 : 1));
|
||||
if (eof)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void GameData::parse_header(FileBuffer *fb) {
|
||||
GameHeader *header = &_header;
|
||||
uint16 dummy, addr_dictionary_end;
|
||||
|
||||
fb->seek(0);
|
||||
header->magic = fb->readUint16LE();
|
||||
fb->skip(2); // Unknown in earlier versions
|
||||
|
||||
switch (header->magic) {
|
||||
case 0x2000: /* Transylvania, Crimson Crown disk one */
|
||||
case 0x4800: /* Crimson Crown disk two */
|
||||
_comprehendVersion = 1;
|
||||
_magicWord = (uint16)(-0x5a00 + 0x4);
|
||||
break;
|
||||
|
||||
case 0x8bc3: /* Transylvania v2 */
|
||||
case 0x93f0: /* OO-Topos */
|
||||
case 0xa429: /* Talisman */
|
||||
_comprehendVersion = 2;
|
||||
_magicWord = (uint16)-0x5a00;
|
||||
|
||||
// Actions table starts right at the start of the file
|
||||
fb->seek(0);
|
||||
break;
|
||||
|
||||
default:
|
||||
error("Unknown game_data magic %.4x\n", header->magic);
|
||||
break;
|
||||
}
|
||||
|
||||
/* Basic data */
|
||||
for (int idx = 0; idx < 7; ++idx)
|
||||
parse_header_le16(fb, &header->addr_actions[idx]);
|
||||
|
||||
parse_header_le16(fb, &header->addr_vm);
|
||||
parse_header_le16(fb, &header->addr_dictionary);
|
||||
|
||||
parse_header_le16(fb, &header->addr_word_map);
|
||||
parse_header_le16(fb, &header->addr_word_map_target);
|
||||
addr_dictionary_end = header->addr_word_map;
|
||||
|
||||
/* Rooms */
|
||||
parse_header_le16(fb, &header->room_desc_table);
|
||||
parse_header_le16(fb, &header->room_direction_table[DIRECTION_NORTH]);
|
||||
parse_header_le16(fb, &header->room_direction_table[DIRECTION_SOUTH]);
|
||||
parse_header_le16(fb, &header->room_direction_table[DIRECTION_EAST]);
|
||||
parse_header_le16(fb, &header->room_direction_table[DIRECTION_WEST]);
|
||||
parse_header_le16(fb, &header->room_direction_table[DIRECTION_UP]);
|
||||
parse_header_le16(fb, &header->room_direction_table[DIRECTION_DOWN]);
|
||||
parse_header_le16(fb, &header->room_direction_table[DIRECTION_IN]);
|
||||
parse_header_le16(fb, &header->room_direction_table[DIRECTION_OUT]);
|
||||
parse_header_le16(fb, &header->room_flags_table);
|
||||
parse_header_le16(fb, &header->room_graphics_table);
|
||||
|
||||
/*
|
||||
* Objects.
|
||||
*
|
||||
* Layout is dependent on comprehend version.
|
||||
*/
|
||||
if (_comprehendVersion == 1) {
|
||||
parse_header_le16(fb, &header->addr_item_locations);
|
||||
parse_header_le16(fb, &header->addr_item_flags);
|
||||
parse_header_le16(fb, &header->addr_item_word);
|
||||
parse_header_le16(fb, &header->addr_item_strings);
|
||||
parse_header_le16(fb, &header->addr_item_graphics);
|
||||
|
||||
header->nr_items = (header->addr_item_word -
|
||||
header->addr_item_flags);
|
||||
|
||||
} else {
|
||||
parse_header_le16(fb, &header->addr_item_strings);
|
||||
parse_header_le16(fb, &header->addr_item_word);
|
||||
parse_header_le16(fb, &header->addr_item_locations);
|
||||
parse_header_le16(fb, &header->addr_item_flags);
|
||||
parse_header_le16(fb, &header->addr_item_graphics);
|
||||
|
||||
header->nr_items = (header->addr_item_flags -
|
||||
header->addr_item_locations);
|
||||
}
|
||||
|
||||
parse_header_le16(fb, &header->addr_strings);
|
||||
parse_header_le16(fb, &dummy);
|
||||
parse_header_le16(fb, &header->addr_strings_end);
|
||||
|
||||
fb->skip(1);
|
||||
_startRoom = fb->readByte();
|
||||
fb->skip(1);
|
||||
|
||||
parse_variables(fb);
|
||||
parse_flags(fb);
|
||||
|
||||
fb->skip(9);
|
||||
_itemCount = fb->readByte();
|
||||
|
||||
_rooms.resize(header->room_direction_table[DIRECTION_SOUTH] -
|
||||
header->room_direction_table[DIRECTION_NORTH] + 1);
|
||||
|
||||
_words.resize((addr_dictionary_end - header->addr_dictionary) / 8);
|
||||
}
|
||||
|
||||
void GameData::load_extra_string_file(const StringFile &stringFile) {
|
||||
FileBuffer fb(stringFile._filename);
|
||||
|
||||
if (stringFile._baseOffset > 0) {
|
||||
// Explicit offset specified, so read the strings in sequentially
|
||||
uint endOffset = stringFile._endOffset;
|
||||
if (!endOffset)
|
||||
endOffset = fb.size();
|
||||
|
||||
parse_string_table(&fb, stringFile._baseOffset, endOffset, &_strings2);
|
||||
} else {
|
||||
// Standard strings file. Has a 4-byte header we can ignore,
|
||||
// followed by 64 2-byte string offsets
|
||||
fb.seek(4);
|
||||
uint fileSize = fb.size();
|
||||
|
||||
// Read in the index
|
||||
uint16 index[STRING_FILE_COUNT];
|
||||
Common::fill(&index[0], &index[STRING_FILE_COUNT], 0);
|
||||
|
||||
for (int i = 0; i < STRING_FILE_COUNT; ++i) {
|
||||
uint v = fb.readUint16LE();
|
||||
if (v > fileSize)
|
||||
break;
|
||||
|
||||
index[i] = v;
|
||||
}
|
||||
|
||||
// Iterate through parsing the strings
|
||||
for (int i = 0; i < STRING_FILE_COUNT; ++i) {
|
||||
if (index[i]) {
|
||||
fb.seek(index[i] + 4);
|
||||
_strings2.push_back(parseString(&fb));
|
||||
} else {
|
||||
_strings2.push_back("");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GameData::load_extra_string_files() {
|
||||
_strings2.clear();
|
||||
_strings2.reserve(STRING_FILE_COUNT * _stringFiles.size() + 1);
|
||||
|
||||
for (uint i = 0; i < _stringFiles.size(); i++) {
|
||||
// TODO: Is this needed for other than OO-Topos?
|
||||
if (_comprehendVersion == 2 && (i == 0 || i == 4))
|
||||
_strings2.push_back("");
|
||||
|
||||
load_extra_string_file(_stringFiles[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void GameData::loadGameData() {
|
||||
FileBuffer fb(_gameDataFile);
|
||||
|
||||
clearGame();
|
||||
|
||||
parse_header(&fb);
|
||||
parse_rooms(&fb);
|
||||
parse_items(&fb);
|
||||
parse_dictionary(&fb);
|
||||
parse_word_map(&fb);
|
||||
if (g_comprehend->getGameID() != "talisman")
|
||||
parse_string_table(&fb, _header.addr_strings, _header.addr_strings_end, &_strings);
|
||||
load_extra_string_files();
|
||||
parse_vm(&fb);
|
||||
parse_action_tables(&fb);
|
||||
parse_replace_words(&fb);
|
||||
}
|
||||
|
||||
void GameData::loadGame() {
|
||||
/* Load the main game data file */
|
||||
loadGameData();
|
||||
|
||||
if (g_comprehend->isGraphicsEnabled()) {
|
||||
// Set up the picture archive
|
||||
g_comprehend->_pics->load(_locationGraphicFiles,
|
||||
_itemGraphicFiles, _titleGraphicFile);
|
||||
|
||||
if (_colorTable)
|
||||
g_comprehend->_drawSurface->setColorTable(_colorTable);
|
||||
}
|
||||
|
||||
// FIXME: This can be merged, don't need to keep start room around
|
||||
_currentRoom = _startRoom;
|
||||
}
|
||||
|
||||
} // namespace Comprehend
|
||||
} // namespace Glk
|
||||
475
engines/glk/comprehend/game_data.h
Normal file
475
engines/glk/comprehend/game_data.h
Normal file
@@ -0,0 +1,475 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GLK_COMPREHEND_GAME_DATA_H
|
||||
#define GLK_COMPREHEND_GAME_DATA_H
|
||||
|
||||
#include "glk/comprehend/file_buf.h"
|
||||
#include "common/serializer.h"
|
||||
#include "common/str-array.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace Comprehend {
|
||||
|
||||
#define MAX_FLAGS 256
|
||||
#define MAX_VARIABLES 128
|
||||
#define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0]))
|
||||
|
||||
class ComprehendGame;
|
||||
|
||||
enum {
|
||||
DIRECTION_NORTH,
|
||||
DIRECTION_SOUTH,
|
||||
DIRECTION_EAST,
|
||||
DIRECTION_WEST,
|
||||
DIRECTION_UP,
|
||||
DIRECTION_DOWN,
|
||||
DIRECTION_IN,
|
||||
DIRECTION_OUT,
|
||||
NR_DIRECTIONS
|
||||
};
|
||||
|
||||
|
||||
enum ScriptOpcode {
|
||||
OPCODE_UNKNOWN,
|
||||
OPCODE_HAVE_OBJECT,
|
||||
OPCODE_OR,
|
||||
OPCODE_IN_ROOM,
|
||||
OPCODE_VAR_EQ1,
|
||||
OPCODE_VAR_EQ2,
|
||||
OPCODE_VAR_GT1,
|
||||
OPCODE_VAR_GT2,
|
||||
OPCODE_VAR_GTE1,
|
||||
OPCODE_VAR_GTE2,
|
||||
OPCODE_CURRENT_IS_OBJECT,
|
||||
OPCODE_OBJECT_PRESENT,
|
||||
OPCODE_ELSE,
|
||||
OPCODE_OBJECT_IN_ROOM,
|
||||
OPCODE_CURRENT_OBJECT_NOT_VALID,
|
||||
OPCODE_INVENTORY_FULL,
|
||||
OPCODE_INVENTORY_FULL_X,
|
||||
OPCODE_TEST_FLAG,
|
||||
OPCODE_CURRENT_OBJECT_IN_ROOM,
|
||||
OPCODE_HAVE_CURRENT_OBJECT,
|
||||
OPCODE_OBJECT_IS_NOT_NOWHERE,
|
||||
OPCODE_CURRENT_OBJECT_PRESENT,
|
||||
OPCODE_TEST_ROOM_FLAG,
|
||||
OPCODE_NOT_HAVE_OBJECT,
|
||||
OPCODE_NOT_IN_ROOM,
|
||||
OPCODE_CURRENT_OBJECT_NOT_IN_ROOM,
|
||||
OPCODE_OBJECT_NOT_IN_ROOM,
|
||||
OPCODE_TEST_NOT_FLAG,
|
||||
OPCODE_NOT_HAVE_CURRENT_OBJECT,
|
||||
OPCODE_OBJECT_IS_NOWHERE,
|
||||
OPCODE_OBJECT_NOT_PRESENT,
|
||||
OPCODE_CURRENT_OBJECT_IS_NOWHERE,
|
||||
OPCODE_CURRENT_OBJECT_NOT_PRESENT,
|
||||
OPCODE_CURRENT_OBJECT_NOT_TAKEABLE,
|
||||
OPCODE_TEST_NOT_ROOM_FLAG,
|
||||
OPCODE_INVENTORY,
|
||||
OPCODE_TAKE_OBJECT,
|
||||
OPCODE_MOVE_OBJECT_TO_ROOM,
|
||||
OPCODE_SAVE_ACTION,
|
||||
OPCODE_CLEAR_LINE,
|
||||
OPCODE_MOVE_TO_ROOM,
|
||||
OPCODE_VAR_ADD,
|
||||
OPCODE_SET_ROOM_DESCRIPTION,
|
||||
OPCODE_MOVE_OBJECT_TO_CURRENT_ROOM,
|
||||
OPCODE_VAR_SUB,
|
||||
OPCODE_SET_OBJECT_DESCRIPTION,
|
||||
OPCODE_SET_OBJECT_LONG_DESCRIPTION,
|
||||
OPCODE_MOVE_DEFAULT,
|
||||
OPCODE_PRINT,
|
||||
OPCODE_REMOVE_OBJECT,
|
||||
OPCODE_SET_FLAG,
|
||||
OPCODE_CALL_FUNC,
|
||||
OPCODE_CALL_FUNC2,
|
||||
OPCODE_TURN_TICK,
|
||||
OPCODE_CLEAR_FLAG,
|
||||
OPCODE_INVENTORY_ROOM,
|
||||
OPCODE_TAKE_CURRENT_OBJECT,
|
||||
OPCODE_SPECIAL,
|
||||
OPCODE_DROP_OBJECT,
|
||||
OPCODE_DROP_CURRENT_OBJECT,
|
||||
OPCODE_SET_ROOM_GRAPHIC,
|
||||
OPCODE_SET_OBJECT_GRAPHIC,
|
||||
OPCODE_REMOVE_CURRENT_OBJECT,
|
||||
OPCODE_MOVE_DIR,
|
||||
OPCODE_VAR_INC,
|
||||
OPCODE_VAR_DEC,
|
||||
OPCODE_MOVE_CURRENT_OBJECT_TO_ROOM,
|
||||
OPCODE_DESCRIBE_CURRENT_OBJECT,
|
||||
OPCODE_SET_STRING_REPLACEMENT1,
|
||||
OPCODE_SET_STRING_REPLACEMENT2,
|
||||
OPCODE_SET_STRING_REPLACEMENT3,
|
||||
OPCODE_SET_CURRENT_NOUN_STRING_REPLACEMENT,
|
||||
OPCODE_DRAW_ROOM,
|
||||
OPCODE_DRAW_OBJECT,
|
||||
OPCODE_WAIT_KEY,
|
||||
OPCODE_TEST_FALSE,
|
||||
OPCODE_CAN_TAKE,
|
||||
OPCODE_TOO_HEAVY,
|
||||
OPCODE_OBJECT_TAKEABLE,
|
||||
OPCODE_OBJECT_CAN_TAKE,
|
||||
OPCODE_CLEAR_INVISIBLE,
|
||||
OPCODE_SET_INVISIBLE,
|
||||
OPCODE_CLEAR_CAN_TAKE,
|
||||
OPCODE_SET_CAN_TAKE,
|
||||
OPCODE_CLEAR_FLAG40,
|
||||
OPCODE_SET_FLAG40,
|
||||
OPCODE_RANDOM_MSG,
|
||||
OPCODE_SET_WORD,
|
||||
OPCODE_CLEAR_WORD
|
||||
};
|
||||
|
||||
/* Game state update flags */
|
||||
#define UPDATE_GRAPHICS (1 << 0) /* Implies UPDATE_GRAPHICS_ITEMS */
|
||||
#define UPDATE_GRAPHICS_ITEMS (1 << 1)
|
||||
#define UPDATE_ROOM_DESC (1 << 2)
|
||||
#define UPDATE_ITEM_LIST (1 << 3)
|
||||
#define UPDATE_ALL (~0U)
|
||||
|
||||
/* Action types */
|
||||
enum {
|
||||
ACTION_VERB_VERB_NOUN_NOUN,
|
||||
ACTION_VERB_NOUN_JOIN_NOUN,
|
||||
ACTION_VERB_JOIN_NOUN,
|
||||
ACTION_VERB_DIR_NOUN,
|
||||
ACTION_VERB_NOUN_NOUN,
|
||||
ACTION_VERB_NOUN,
|
||||
ACTION_VERB_OPT_NOUN
|
||||
};
|
||||
|
||||
/* Standard strings (main string table) */
|
||||
#define STRING_CANT_GO 0
|
||||
#define STRING_DONT_UNDERSTAND 1
|
||||
#define STRING_YOU_SEE 2
|
||||
#define STRING_INVENTORY 3
|
||||
#define STRING_INVENTORY_EMPTY 4
|
||||
#define STRING_BEFORE_CONTINUE 5
|
||||
#define STRING_SAVE_GAME 6
|
||||
#define STRING_RESTORE_GAME 7
|
||||
|
||||
/* Special variables */
|
||||
#define VAR_INVENTORY_WEIGHT 0
|
||||
#define VAR_INVENTORY_LIMIT 1
|
||||
#define VAR_TURN_COUNT 2
|
||||
|
||||
/* Special rooms */
|
||||
#define ROOM_INVENTORY 0x00
|
||||
#define ROOM_CONTAINER 0xfe
|
||||
#define ROOM_NOWHERE 0xff
|
||||
|
||||
/* Item flags */
|
||||
enum ItemFlag {
|
||||
ITEMF_WEIGHT_MASK = 0x7,
|
||||
ITEMF_CAN_TAKE = 1 << 3,
|
||||
ITEMF_UNKNOWN = 1 << 6,
|
||||
ITEMF_INVISIBLE = 1 << 7
|
||||
};
|
||||
|
||||
/* Word types */
|
||||
#define WORD_TYPE_VERB 0x01
|
||||
#define WORD_TYPE_JOIN 0x02
|
||||
#define WORD_TYPE_FEMALE 0x10
|
||||
#define WORD_TYPE_MALE 0x20
|
||||
#define WORD_TYPE_NOUN 0x40
|
||||
#define WORD_TYPE_NOUN_PLURAL 0x80
|
||||
#define WORD_TYPE_NOUN_MASK (WORD_TYPE_FEMALE | WORD_TYPE_MALE | \
|
||||
WORD_TYPE_NOUN | WORD_TYPE_NOUN_PLURAL)
|
||||
|
||||
struct FunctionState {
|
||||
bool _testResult;
|
||||
bool _elseResult;
|
||||
uint _orCount;
|
||||
bool _and;
|
||||
bool _inCommand;
|
||||
bool _executed;
|
||||
bool _notComparison;
|
||||
|
||||
FunctionState() {
|
||||
clear();
|
||||
}
|
||||
|
||||
void clear();
|
||||
};
|
||||
|
||||
struct Room {
|
||||
uint8 _direction[NR_DIRECTIONS];
|
||||
uint8 _flags;
|
||||
uint8 _graphic;
|
||||
uint16 _stringDesc;
|
||||
|
||||
Room() {
|
||||
clear();
|
||||
}
|
||||
|
||||
void clear();
|
||||
};
|
||||
|
||||
struct Item {
|
||||
uint16 _stringDesc;
|
||||
uint16 _longString; /* Only used by version 2 */
|
||||
uint8 _room;
|
||||
uint8 _flags;
|
||||
uint8 _word;
|
||||
uint8 _graphic;
|
||||
|
||||
Item() {
|
||||
clear();
|
||||
}
|
||||
|
||||
void clear();
|
||||
|
||||
void synchronize(Common::Serializer &s);
|
||||
};
|
||||
|
||||
struct WordIndex {
|
||||
uint8 _index;
|
||||
uint8 _type;
|
||||
|
||||
WordIndex() {
|
||||
clear();
|
||||
}
|
||||
|
||||
void clear() {
|
||||
_index = _type = 0;
|
||||
}
|
||||
|
||||
bool operator==(WordIndex &src) {
|
||||
return _index == src._index && _type == src._type;
|
||||
}
|
||||
|
||||
bool operator()() const {
|
||||
return _index != 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct Word : public WordIndex {
|
||||
char _word[7];
|
||||
|
||||
Word() : WordIndex() {
|
||||
Word::clear();
|
||||
}
|
||||
|
||||
void clear();
|
||||
|
||||
void load(FileBuffer *fb);
|
||||
|
||||
Word &operator=(const WordIndex &src);
|
||||
};
|
||||
|
||||
struct WordMap {
|
||||
/* <word[0]>, <word[1]> == <word[2]> */
|
||||
WordIndex _word[3];
|
||||
uint8 _flags;
|
||||
|
||||
WordMap() {
|
||||
clear();
|
||||
}
|
||||
|
||||
void clear();
|
||||
};
|
||||
|
||||
struct Action {
|
||||
size_t _nr_words;
|
||||
uint8 _words[4];
|
||||
uint16 _function;
|
||||
|
||||
Action() {
|
||||
clear();
|
||||
}
|
||||
|
||||
void clear();
|
||||
};
|
||||
|
||||
struct Instruction {
|
||||
uint8 _opcode;
|
||||
size_t _nr_operands;
|
||||
uint8 _operand[3];
|
||||
bool _isCommand;
|
||||
|
||||
Instruction() {
|
||||
clear();
|
||||
}
|
||||
|
||||
Instruction(byte opcode, byte op1 = 0, byte op2 = 0, byte op3 = 0);
|
||||
|
||||
void clear();
|
||||
};
|
||||
|
||||
typedef Common::Array<Instruction> Function;
|
||||
|
||||
typedef Common::StringArray StringTable;
|
||||
|
||||
struct StringFile {
|
||||
Common::String _filename;
|
||||
uint32 _baseOffset;
|
||||
uint32 _endOffset;
|
||||
|
||||
StringFile() : _baseOffset(0), _endOffset(0) {
|
||||
}
|
||||
StringFile(const char *fname, uint32 baseOfs = 0, uint32 endO = 0) :
|
||||
_filename(fname), _baseOffset(baseOfs), _endOffset(endO) {
|
||||
}
|
||||
};
|
||||
|
||||
struct GameHeader {
|
||||
uint16 magic;
|
||||
|
||||
uint16 room_desc_table;
|
||||
uint16 room_direction_table[NR_DIRECTIONS];
|
||||
uint16 room_flags_table;
|
||||
uint16 room_graphics_table;
|
||||
|
||||
size_t nr_items;
|
||||
uint16 addr_item_locations;
|
||||
uint16 addr_item_flags;
|
||||
uint16 addr_item_word;
|
||||
uint16 addr_item_strings;
|
||||
uint16 addr_item_graphics;
|
||||
|
||||
uint16 addr_dictionary;
|
||||
uint16 addr_word_map;
|
||||
uint16 addr_word_map_target;
|
||||
|
||||
uint16 addr_strings;
|
||||
uint16 addr_strings_end;
|
||||
|
||||
uint16 addr_actions[7];
|
||||
|
||||
uint16 addr_vm; // FIXME - functions
|
||||
|
||||
GameHeader() {
|
||||
clear();
|
||||
}
|
||||
|
||||
void clear();
|
||||
};
|
||||
|
||||
typedef Common::Array<Action> ActionTable;
|
||||
|
||||
class GameData {
|
||||
private:
|
||||
uint16 _magicWord;
|
||||
protected:
|
||||
Common::String _gameDataFile;
|
||||
Common::Array<StringFile> _stringFiles;
|
||||
Common::StringArray _locationGraphicFiles;
|
||||
Common::StringArray _itemGraphicFiles;
|
||||
Common::String _titleGraphicFile;
|
||||
uint _colorTable;
|
||||
|
||||
public:
|
||||
GameHeader _header;
|
||||
|
||||
uint _comprehendVersion;
|
||||
|
||||
Common::Array<Room> _rooms;
|
||||
uint8 _currentRoom;
|
||||
uint8 _startRoom;
|
||||
uint8 _itemCount;
|
||||
uint8 _totalInventoryWeight;
|
||||
|
||||
Common::Array<Item> _items;
|
||||
Common::Array<Word> _words;
|
||||
|
||||
StringTable _strings;
|
||||
StringTable _strings2;
|
||||
|
||||
bool _flags[MAX_FLAGS];
|
||||
uint16 _variables[MAX_VARIABLES];
|
||||
|
||||
uint8 _currentReplaceWord;
|
||||
uint8 _wordFlags;
|
||||
uint _updateFlags;
|
||||
|
||||
Common::Array<WordMap> _wordMaps;
|
||||
Common::Array<ActionTable> _actions;
|
||||
Common::Array<Function> _functions;
|
||||
Common::StringArray _replaceWords;
|
||||
|
||||
private:
|
||||
size_t opcode_nr_operands(uint8 opcode) const {
|
||||
// Number of operands is encoded in the low 2 bits
|
||||
return opcode & 0x3;
|
||||
}
|
||||
|
||||
bool opcode_is_command(uint8 opcode) const {
|
||||
/* If the MSB is set the instruction is a command */
|
||||
return opcode & 0x80;
|
||||
}
|
||||
|
||||
void load_extra_string_files();
|
||||
void load_extra_string_file(const StringFile &stringFile);
|
||||
void parse_header_le16(FileBuffer *fb, uint16 *val);
|
||||
uint8 parse_vm_instruction(FileBuffer *fb, Instruction *instr);
|
||||
void parse_function(FileBuffer *fb, Function *func);
|
||||
void parse_vm(FileBuffer *fb);
|
||||
void parse_action_tables(FileBuffer *fb);
|
||||
void parse_dictionary(FileBuffer *fb);
|
||||
void parse_word_map(FileBuffer *fb);
|
||||
void parse_items(FileBuffer *fb);
|
||||
void parse_rooms(FileBuffer *fb);
|
||||
uint64 string_get_chunk(uint8 *string);
|
||||
char decode_string_elem(uint8 c, bool capital, bool special);
|
||||
|
||||
void parse_string_table(FileBuffer *fb, uint start_addr,
|
||||
uint32 end_addr, StringTable *table);
|
||||
void parse_variables(FileBuffer *fb);
|
||||
void parse_flags(FileBuffer *fb);
|
||||
void parse_replace_words(FileBuffer *fb);
|
||||
|
||||
void loadGameData();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* Game strings are stored using 5-bit characters. By default a character
|
||||
* value maps to the lower-case letter table. If a character has the value 0x1e
|
||||
* then the next character is upper-case. An upper-case space is used to
|
||||
* specify that the character should be replaced at runtime (like a '%s'
|
||||
* specifier). If a character has the value 0x1f then the next character is
|
||||
* taken from the symbols table.
|
||||
*/
|
||||
Common::String parseString(FileBuffer *fb);
|
||||
|
||||
/**
|
||||
* The main game data file header has the offsets for where each bit of
|
||||
* game data is. The offsets have a magic constant value added to them.
|
||||
*/
|
||||
virtual void parse_header(FileBuffer *fb);
|
||||
|
||||
public:
|
||||
GameData() {
|
||||
clearGame();
|
||||
}
|
||||
virtual ~GameData() {
|
||||
clearGame();
|
||||
}
|
||||
|
||||
void clearGame();
|
||||
void loadGame();
|
||||
};
|
||||
|
||||
} // namespace Comprehend
|
||||
} // namespace Glk
|
||||
|
||||
#endif
|
||||
412
engines/glk/comprehend/game_oo.cpp
Normal file
412
engines/glk/comprehend/game_oo.cpp
Normal file
@@ -0,0 +1,412 @@
|
||||
/* 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/comprehend/game_oo.h"
|
||||
#include "glk/comprehend/comprehend.h"
|
||||
#include "glk/comprehend/draw_surface.h"
|
||||
#include "glk/comprehend/pics.h"
|
||||
#include "common/md5.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace Comprehend {
|
||||
|
||||
enum OOToposRoomFlag {
|
||||
OO_ROOM_IN_SHIP = 1,
|
||||
OO_ROOM_FLAG_DARK = 2
|
||||
};
|
||||
|
||||
enum OOToposFlag {
|
||||
OO_FLAG_9 = 9,
|
||||
OO_FLAG_13 = 13,
|
||||
OO_FLAG_22 = 22,
|
||||
OO_BRIGHT_ROOM = 25,
|
||||
OO_FLAG_WEARING_GOGGLES = 27,
|
||||
OO_FLAG_FLASHLIGHT_ON = 39,
|
||||
OO_FLAG_43 = 43,
|
||||
OO_FLAG_44 = 44,
|
||||
OO_FLAG_SUFFICIENT_FUEL = 51,
|
||||
OO_FLAG_REVERSE_VIDEO = 53, // Effect of wearing goggles
|
||||
OO_FLAG_TOO_DARK = 55,
|
||||
OO_FLAG_TOO_BRIGHT = 56,
|
||||
OO_FLAG_58 = 58,
|
||||
OO_FLAG_59 = 59,
|
||||
OO_FLAG_READY_TO_DEPART = 60,
|
||||
OO_TRACTOR_BEAM = 71
|
||||
};
|
||||
|
||||
enum OOToposItem {
|
||||
ITEM_SERUM_VIAL = 39
|
||||
};
|
||||
|
||||
static const GameStrings OO_STRINGS = {
|
||||
EXTRA_STRING_TABLE(154)
|
||||
};
|
||||
|
||||
OOToposGame::OOToposGame() : ComprehendGameV2(), _restartMode(RESTART_IMMEDIATE),
|
||||
_noFloodfill(UNSET), _stringVal1(0), _stringVal2(0),
|
||||
_printComputerMsg(true), _shipNotWorking(false) {
|
||||
_gameDataFile = "g0";
|
||||
|
||||
// Extra strings are (annoyingly) stored in the game binary
|
||||
Common::File f;
|
||||
if (!f.open("novel.exe"))
|
||||
error("novel.exe is a required file");
|
||||
|
||||
Common::String md5 = Common::computeStreamMD5AsString(f, 1024);
|
||||
f.close();
|
||||
|
||||
if (md5 == "3fc2072f6996b17d2f21f0a92e53cdcc") {
|
||||
// DOS version from if-archive
|
||||
_stringFiles.push_back(StringFile("NOVEL.EXE", 0x16564, 0x17640));
|
||||
_stringFiles.push_back(StringFile("NOVEL.EXE", 0x17702, 0x18600));
|
||||
_stringFiles.push_back(StringFile("NOVEL.EXE", 0x186b2, 0x19b80));
|
||||
_stringFiles.push_back(StringFile("NOVEL.EXE", 0x19c62, 0x1a590));
|
||||
_stringFiles.push_back(StringFile("NOVEL.EXE", 0x1a634, 0x1b080));
|
||||
} else if (md5 == "e26858f2aaa9dcc28f468b07902813c5") {
|
||||
// DOS version from graphicsmagician.com
|
||||
_stringFiles.push_back(StringFile("NOVEL.EXE", 0x164c4, 0x175a0));
|
||||
_stringFiles.push_back(StringFile("NOVEL.EXE", 0x17662, 0x18560));
|
||||
_stringFiles.push_back(StringFile("NOVEL.EXE", 0x18612, 0x19ae0));
|
||||
_stringFiles.push_back(StringFile("NOVEL.EXE", 0x19bc2, 0x1a4f0));
|
||||
_stringFiles.push_back(StringFile("NOVEL.EXE", 0x1a594, 0x1afe0));
|
||||
} else {
|
||||
error("Unrecognised novel.exe encountered");
|
||||
}
|
||||
|
||||
_locationGraphicFiles.push_back("RA");
|
||||
_locationGraphicFiles.push_back("RB");
|
||||
_locationGraphicFiles.push_back("RC");
|
||||
_locationGraphicFiles.push_back("RD");
|
||||
_locationGraphicFiles.push_back("RE");
|
||||
_itemGraphicFiles.push_back("OA");
|
||||
_itemGraphicFiles.push_back("OB");
|
||||
_itemGraphicFiles.push_back("OC");
|
||||
_itemGraphicFiles.push_back("OD");
|
||||
|
||||
_colorTable = 1;
|
||||
_gameStrings = &OO_STRINGS;
|
||||
_titleGraphicFile = "t0";
|
||||
}
|
||||
|
||||
void OOToposGame::beforeGame() {
|
||||
// Draw the title
|
||||
g_comprehend->drawPicture(TITLE_IMAGE);
|
||||
|
||||
// Print game information
|
||||
console_println("Story by Michael and Muffy Berlyn, graphics by Raim und Redlich and Brian Poff");
|
||||
console_println("IBM version by Jeffrey A. Jay. Copyright 1987 POLARWARE, Inc.");
|
||||
g_comprehend->readChar();
|
||||
|
||||
g_comprehend->glk_window_clear(g_comprehend->_bottomWindow);
|
||||
}
|
||||
|
||||
int OOToposGame::roomIsSpecial(uint room_index, uint *roomDescString) {
|
||||
Room *room = &_rooms[room_index];
|
||||
|
||||
// Is the room dark
|
||||
if ((room->_flags & OO_ROOM_FLAG_DARK) &&
|
||||
!(_flags[OO_FLAG_FLASHLIGHT_ON])) {
|
||||
if (roomDescString)
|
||||
*roomDescString = 0xb3;
|
||||
return ROOM_IS_DARK;
|
||||
}
|
||||
|
||||
// Is the room too bright
|
||||
if (room_index == OO_BRIGHT_ROOM &&
|
||||
!_flags[OO_FLAG_WEARING_GOGGLES]) {
|
||||
if (roomDescString)
|
||||
*roomDescString = 0x1c;
|
||||
return ROOM_IS_TOO_BRIGHT;
|
||||
}
|
||||
|
||||
return ROOM_IS_NORMAL;
|
||||
}
|
||||
|
||||
void OOToposGame::beforeTurn() {
|
||||
ComprehendGameV2::beforeTurn();
|
||||
|
||||
if (_flags[OO_FLAG_TOO_DARK]) {
|
||||
// Show placeholder room if room is too dark
|
||||
_currentRoom = 55;
|
||||
_updateFlags |= UPDATE_GRAPHICS;
|
||||
} else if (_flags[OO_FLAG_TOO_BRIGHT]) {
|
||||
// Show placeholder room if room is too bright
|
||||
_currentRoom = 54;
|
||||
_updateFlags |= UPDATE_GRAPHICS;
|
||||
} else {
|
||||
YesNo nff = _flags[OO_FLAG_REVERSE_VIDEO] ? YES : NO;
|
||||
|
||||
if (_noFloodfill != nff) {
|
||||
_noFloodfill = nff;
|
||||
_updateFlags |= UPDATE_GRAPHICS | UPDATE_ROOM_DESC;
|
||||
|
||||
if (_noFloodfill == YES)
|
||||
g_comprehend->_drawFlags |= IMAGEF_REVERSE;
|
||||
else
|
||||
g_comprehend->_drawFlags &= ~IMAGEF_REVERSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OOToposGame::beforePrompt() {
|
||||
// Handle the computer console if in front of it
|
||||
computerConsole();
|
||||
}
|
||||
|
||||
void OOToposGame::afterPrompt() {
|
||||
ComprehendGameV2::afterPrompt();
|
||||
|
||||
// WORKAROUND: Allow for the Apple 2 password in the DOS version
|
||||
if (!scumm_stricmp(_inputLine, "vug957a"))
|
||||
Common::strcpy_s(_inputLine, "tse957x");
|
||||
|
||||
if (_currentRoom != _currentRoomCopy)
|
||||
_updateFlags |= UPDATE_GRAPHICS;
|
||||
_currentRoom = _currentRoomCopy;
|
||||
}
|
||||
|
||||
void OOToposGame::handleSpecialOpcode() {
|
||||
switch (_specialOpcode) {
|
||||
case 1:
|
||||
// Update guard location
|
||||
randomizeGuardLocation();
|
||||
break;
|
||||
|
||||
case 2:
|
||||
_restartMode = RESTART_IMMEDIATE;
|
||||
game_restart();
|
||||
break;
|
||||
|
||||
case 3:
|
||||
_restartMode = RESTART_WITH_MSG;
|
||||
game_restart();
|
||||
break;
|
||||
|
||||
case 4:
|
||||
_restartMode = RESTART_WITHOUT_MSG;
|
||||
game_restart();
|
||||
break;
|
||||
|
||||
case 5:
|
||||
// Won the game
|
||||
g_comprehend->quitGame();
|
||||
break;
|
||||
|
||||
case 6:
|
||||
// Save game
|
||||
game_save();
|
||||
break;
|
||||
|
||||
case 7:
|
||||
// Restore game
|
||||
game_restore();
|
||||
break;
|
||||
|
||||
case 8:
|
||||
// Computer response
|
||||
computerResponse();
|
||||
randomizeGuardLocation();
|
||||
break;
|
||||
|
||||
case 9:
|
||||
// Checks the ship fuel
|
||||
checkShipFuel();
|
||||
randomizeGuardLocation();
|
||||
break;
|
||||
|
||||
case 10:
|
||||
// Checks whether the ship is working
|
||||
checkShipWorking();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool OOToposGame::handle_restart() {
|
||||
_ended = false;
|
||||
|
||||
if (_restartMode != RESTART_IMMEDIATE) {
|
||||
if (_restartMode == RESTART_WITH_MSG)
|
||||
console_println(stringLookup(_gameStrings->game_restart).c_str());
|
||||
|
||||
if (tolower(console_get_key()) != 'r') {
|
||||
g_comprehend->quitGame();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
loadGame();
|
||||
_updateFlags = UPDATE_ALL;
|
||||
return true;
|
||||
}
|
||||
|
||||
void OOToposGame::synchronizeSave(Common::Serializer &s) {
|
||||
if (s.isSaving())
|
||||
_currentRoom = _currentRoomCopy;
|
||||
|
||||
ComprehendGameV2::synchronizeSave(s);
|
||||
|
||||
if (s.isLoading()) {
|
||||
_noFloodfill = UNSET;
|
||||
_currentRoomCopy = _currentRoom;
|
||||
|
||||
beforeTurn();
|
||||
}
|
||||
}
|
||||
|
||||
void OOToposGame::randomizeGuardLocation() {
|
||||
Item *item = get_item(22);
|
||||
if (_flags[OO_FLAG_13] && item->_room != _currentRoom) {
|
||||
if (getRandomNumber(255) > 128 && (_currentRoom == 3 || _currentRoom == 6))
|
||||
item->_room = _currentRoom;
|
||||
}
|
||||
}
|
||||
|
||||
void OOToposGame::computerConsole() {
|
||||
if (_currentRoom == 57) {
|
||||
if (!_flags[OO_FLAG_9]) {
|
||||
// Mission Code:
|
||||
console_println(_strings2[129].c_str());
|
||||
} else if (!_flags[OO_FLAG_58]) {
|
||||
// Welcome back! I was wondering if you would be returning
|
||||
console_println(_strings2[131].c_str());
|
||||
_flags[OO_FLAG_58] = true;
|
||||
_printComputerMsg = true;
|
||||
checkShipWorking();
|
||||
} else if (_flags[OO_FLAG_59]) {
|
||||
checkShipDepart();
|
||||
} else if (_flags[OO_FLAG_43]) {
|
||||
// We can reach Mealy Sukas with the fuel we have left
|
||||
console_println(_strings2[142].c_str());
|
||||
_flags[OO_FLAG_59] = true;
|
||||
|
||||
if (_flags[OO_FLAG_44])
|
||||
// The currency on Mealy Sukas is the 'frod'
|
||||
console_println(_strings2[144].c_str());
|
||||
else
|
||||
// Without evaluation data as to the current fuel prices
|
||||
console_println(_strings2[143].c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OOToposGame::computerResponse() {
|
||||
console_println(_strings2[145].c_str());
|
||||
if (_flags[OO_FLAG_43])
|
||||
console_println(_strings2[144].c_str());
|
||||
else
|
||||
console_println(_strings2[152].c_str());
|
||||
}
|
||||
|
||||
void OOToposGame::checkShipWorking() {
|
||||
_stringVal1 = 164;
|
||||
_stringVal2 = 0;
|
||||
|
||||
// Iterate through the ship's flags
|
||||
for (int idx = 42; idx < 51; ++idx, ++_stringVal1) {
|
||||
if (!_flags[idx]) {
|
||||
if (!_stringVal2) {
|
||||
// The following components are not installed
|
||||
printComputerMsg(_strings2[132].c_str());
|
||||
_stringVal2 = 1;
|
||||
}
|
||||
|
||||
// Power Cylinder
|
||||
printComputerMsg(_strings[_stringVal1].c_str());
|
||||
}
|
||||
}
|
||||
|
||||
_shipNotWorking = _stringVal2 != 0;
|
||||
if (!_shipNotWorking)
|
||||
// The ship is in working order
|
||||
printComputerMsg(_strings2[153].c_str());
|
||||
}
|
||||
|
||||
void OOToposGame::checkShipFuel() {
|
||||
const byte ITEMS[7] = { 24, 27, 28, 29, 30, 31, 32 };
|
||||
_variables[0x4b] = 0;
|
||||
_stringVal1 = 68;
|
||||
_stringVal2 = 0;
|
||||
|
||||
for (int idx = 168; idx < 175; ++idx, ++_stringVal1, ++_stringVal2) {
|
||||
if (_flags[idx]) {
|
||||
Item *item = get_item(ITEMS[_stringVal2] - 1);
|
||||
if (item->_room == ROOM_INVENTORY || (get_room(item->_room)->_flags & OO_ROOM_IN_SHIP) != 0) {
|
||||
Instruction varAdd(0x86, 0x4B, _stringVal1);
|
||||
execute_opcode(&varAdd, nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Computer: "Our current evaluation...
|
||||
Instruction strReplace(0xC9, 0x4B);
|
||||
execute_opcode(&strReplace, nullptr, nullptr);
|
||||
printComputerMsg(_strings2[146].c_str());
|
||||
|
||||
FunctionState funcState;
|
||||
Instruction test(2, 75, 76);
|
||||
execute_opcode(&test, nullptr, nullptr);
|
||||
|
||||
if (funcState._testResult) {
|
||||
// Computer: "We should now have enough
|
||||
_flags[OO_FLAG_SUFFICIENT_FUEL] = true;
|
||||
printComputerMsg(_strings2[151].c_str());
|
||||
} else {
|
||||
_flags[OO_FLAG_SUFFICIENT_FUEL] = false;
|
||||
}
|
||||
}
|
||||
|
||||
void OOToposGame::checkShipDepart() {
|
||||
_printComputerMsg = false;
|
||||
checkShipWorking();
|
||||
checkShipFuel();
|
||||
_printComputerMsg = true;
|
||||
|
||||
if (!_shipNotWorking && _flags[OO_FLAG_SUFFICIENT_FUEL]) {
|
||||
Item *item = get_item(ITEM_SERUM_VIAL - 1);
|
||||
if (item->_room == ROOM_INVENTORY || (get_room(item->_room)->_flags & OO_ROOM_IN_SHIP) != 0) {
|
||||
if (!_flags[OO_TRACTOR_BEAM]) {
|
||||
// I detect a tractor beam
|
||||
console_println(_strings2[77].c_str());
|
||||
} else if (!_flags[OO_FLAG_READY_TO_DEPART]) {
|
||||
// All systems check. Ready to depart
|
||||
_flags[OO_FLAG_22] = true;
|
||||
console_println(_strings2[79].c_str());
|
||||
} else {
|
||||
// Please close the airlock
|
||||
console_println(_strings2[76].c_str());
|
||||
}
|
||||
} else {
|
||||
// The serum vial is not aboard the ship
|
||||
console_println(_strings2[78].c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void OOToposGame::printComputerMsg(const char *str) {
|
||||
if (_printComputerMsg)
|
||||
console_println(str);
|
||||
}
|
||||
|
||||
} // namespace Comprehend
|
||||
} // namespace Glk
|
||||
93
engines/glk/comprehend/game_oo.h
Normal file
93
engines/glk/comprehend/game_oo.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GLK_COMPREHEND_GAME_OO_H
|
||||
#define GLK_COMPREHEND_GAME_OO_H
|
||||
|
||||
#include "glk/comprehend/game_opcodes.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace Comprehend {
|
||||
|
||||
enum RestartMode { RESTART_IMMEDIATE, RESTART_WITH_MSG, RESTART_WITHOUT_MSG };
|
||||
|
||||
enum YesNo { NO, YES, UNSET };
|
||||
|
||||
class OOToposGame : public ComprehendGameV2 {
|
||||
private:
|
||||
RestartMode _restartMode;
|
||||
YesNo _noFloodfill;
|
||||
int _stringVal1, _stringVal2;
|
||||
bool _printComputerMsg, _shipNotWorking;
|
||||
|
||||
/**
|
||||
* Randomizes a guard to different locations
|
||||
*/
|
||||
void randomizeGuardLocation();
|
||||
|
||||
/**
|
||||
* Handles the computer console
|
||||
*/
|
||||
void computerConsole();
|
||||
|
||||
/**
|
||||
* Handles displaying a computer response
|
||||
*/
|
||||
void computerResponse();
|
||||
|
||||
/**
|
||||
* Checks whether the ship is in working order
|
||||
*/
|
||||
void checkShipWorking();
|
||||
|
||||
/**
|
||||
* Tests if the player has enough to purchase needed ship fuel
|
||||
*/
|
||||
void checkShipFuel();
|
||||
|
||||
/**
|
||||
* Checks whether the ship can depart, printing out the computer's response
|
||||
*/
|
||||
void checkShipDepart();
|
||||
|
||||
/**
|
||||
* A wrapped version of console_println that only prints the passed string
|
||||
* if the _addStringFlag is set
|
||||
*/
|
||||
void printComputerMsg(const char *str);
|
||||
public:
|
||||
OOToposGame();
|
||||
~OOToposGame() override {}
|
||||
|
||||
void beforeGame() override;
|
||||
void beforeTurn() override;
|
||||
void beforePrompt() override;
|
||||
void afterPrompt() override;
|
||||
int roomIsSpecial(uint room_index, uint *room_desc_string) override;
|
||||
void handleSpecialOpcode() override;
|
||||
bool handle_restart() override;
|
||||
void synchronizeSave(Common::Serializer &s) override;
|
||||
};
|
||||
|
||||
} // namespace Comprehend
|
||||
} // namespace Glk
|
||||
|
||||
#endif
|
||||
871
engines/glk/comprehend/game_opcodes.cpp
Normal file
871
engines/glk/comprehend/game_opcodes.cpp
Normal file
@@ -0,0 +1,871 @@
|
||||
/* 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
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "glk/comprehend/game_opcodes.h"
|
||||
#include "glk/comprehend/game_data.h"
|
||||
#include "glk/comprehend/comprehend.h"
|
||||
#include "glk/comprehend/debugger.h"
|
||||
#include "common/algorithm.h"
|
||||
#include "common/textconsole.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace Comprehend {
|
||||
|
||||
ComprehendGameOpcodes::ComprehendGameOpcodes() {
|
||||
Common::fill(&_opcodeMap[0], &_opcodeMap[0x100], OPCODE_UNKNOWN);
|
||||
}
|
||||
|
||||
void ComprehendGameOpcodes::execute_opcode(const Instruction *instr, const Sentence *sentence,
|
||||
FunctionState *func_state) {
|
||||
byte verb = sentence ? sentence->_formattedWords[0] : 0;
|
||||
byte noun = sentence ? sentence->_formattedWords[2] : 0;
|
||||
Room *room = get_room(_currentRoom);
|
||||
Item *item;
|
||||
uint index;
|
||||
|
||||
byte opcode = getOpcode(instr);
|
||||
switch (_opcodeMap[opcode]) {
|
||||
case OPCODE_CALL_FUNC:
|
||||
case OPCODE_CALL_FUNC2:
|
||||
// Note: CALL_FUNC2 in the original did some extra backing of data which is
|
||||
// redundant in the ScummVM version, so it can be handled the same as CALL_FUNC.
|
||||
index = instr->_operand[0];
|
||||
if (instr->_operand[1] == 0x81)
|
||||
index += 256;
|
||||
if (index >= _functions.size())
|
||||
error("Bad function %.4x >= %.4x\n", index, _functions.size());
|
||||
|
||||
eval_function(index, sentence);
|
||||
break;
|
||||
|
||||
case OPCODE_CLEAR_CAN_TAKE:
|
||||
item = get_item_by_noun(noun);
|
||||
item->_flags &= ~ITEMF_CAN_TAKE;
|
||||
break;
|
||||
|
||||
case OPCODE_CLEAR_FLAG:
|
||||
_flags[instr->_operand[0]] = false;
|
||||
break;
|
||||
|
||||
case OPCODE_CLEAR_FLAG40:
|
||||
item = getItem(instr);
|
||||
item->_flags &= ~ITEMF_UNKNOWN;
|
||||
break;
|
||||
|
||||
case OPCODE_CLEAR_INVISIBLE:
|
||||
item = get_item_by_noun(noun);
|
||||
item->_flags &= ~ITEMF_INVISIBLE;
|
||||
break;
|
||||
|
||||
case OPCODE_CLEAR_WORD:
|
||||
item = getItem(instr);
|
||||
item->_word = 0;
|
||||
break;
|
||||
|
||||
case OPCODE_CURRENT_OBJECT_NOT_VALID:
|
||||
func_set_test_result(func_state, !noun);
|
||||
break;
|
||||
|
||||
case OPCODE_CURRENT_IS_OBJECT:
|
||||
item = get_item_by_noun(noun);
|
||||
func_set_test_result(func_state, item != nullptr);
|
||||
break;
|
||||
|
||||
case OPCODE_ELSE:
|
||||
func_state->_testResult = func_state->_elseResult;
|
||||
break;
|
||||
|
||||
case OPCODE_HAVE_CURRENT_OBJECT:
|
||||
item = get_item_by_noun(noun);
|
||||
func_set_test_result(func_state, item->_room == ROOM_INVENTORY);
|
||||
break;
|
||||
|
||||
case OPCODE_HAVE_OBJECT:
|
||||
item = getItem(instr);
|
||||
func_set_test_result(func_state, item->_room == ROOM_INVENTORY);
|
||||
break;
|
||||
|
||||
case OPCODE_IN_ROOM:
|
||||
func_set_test_result(func_state, _currentRoom == instr->_operand[0]);
|
||||
break;
|
||||
|
||||
case OPCODE_INVENTORY: {
|
||||
uint count = num_objects_in_room(ROOM_INVENTORY);
|
||||
if (count == 0) {
|
||||
console_println(stringLookup(STRING_INVENTORY_EMPTY).c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
console_println(stringLookup(STRING_INVENTORY).c_str());
|
||||
for (uint i = 0; i < _items.size(); i++) {
|
||||
item = &_items[i];
|
||||
if (item->_room == ROOM_INVENTORY)
|
||||
g_comprehend->print("%s\n",
|
||||
stringLookup(item->_stringDesc).c_str());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case OPCODE_MOVE_DEFAULT:
|
||||
// Move in the direction dictated by the current verb
|
||||
if (verb - 1 >= NR_DIRECTIONS)
|
||||
error("Bad verb %d in move", verb);
|
||||
|
||||
if (room->_direction[verb - 1])
|
||||
move_to(room->_direction[verb - 1]);
|
||||
else
|
||||
console_println(stringLookup(STRING_CANT_GO).c_str());
|
||||
break;
|
||||
|
||||
case OPCODE_MOVE_OBJECT_TO_CURRENT_ROOM:
|
||||
item = getItem(instr);
|
||||
move_object(item, _currentRoom);
|
||||
break;
|
||||
|
||||
case OPCODE_MOVE_OBJECT_TO_ROOM:
|
||||
item = getItem(instr);
|
||||
move_object(item, instr->_operand[1]);
|
||||
break;
|
||||
|
||||
case OPCODE_MOVE_TO_ROOM:
|
||||
if (instr->_operand[0] != 0xff)
|
||||
move_to(instr->_operand[0]);
|
||||
break;
|
||||
|
||||
case OPCODE_OBJECT_IN_ROOM:
|
||||
item = getItem(instr);
|
||||
func_set_test_result(func_state, item->_room == instr->_operand[1]);
|
||||
break;
|
||||
|
||||
case OPCODE_OBJECT_IS_NOWHERE:
|
||||
item = getItem(instr);
|
||||
func_set_test_result(func_state, item->_room == ROOM_NOWHERE);
|
||||
break;
|
||||
|
||||
case OPCODE_OBJECT_PRESENT:
|
||||
item = getItem(instr);
|
||||
func_set_test_result(func_state, item->_room == _currentRoom);
|
||||
break;
|
||||
|
||||
case OPCODE_OR:
|
||||
if (func_state->_orCount) {
|
||||
func_state->_orCount += 2;
|
||||
} else {
|
||||
func_state->_testResult = false;
|
||||
func_state->_orCount += 3;
|
||||
}
|
||||
break;
|
||||
|
||||
case OPCODE_PRINT:
|
||||
console_println(instrStringLookup(instr->_operand[0], instr->_operand[1]).c_str());
|
||||
break;
|
||||
|
||||
case OPCODE_RANDOM_MSG: {
|
||||
int msgId = (instr->_operand[2] << 8 | instr->_operand[1]) +
|
||||
getRandomNumber(instr->_operand[0] - 1);
|
||||
console_println(stringLookup(msgId).c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
case OPCODE_REMOVE_OBJECT:
|
||||
item = getItem(instr);
|
||||
move_object(item, ROOM_NOWHERE);
|
||||
break;
|
||||
|
||||
case OPCODE_SAVE_ACTION:
|
||||
// Causes the next sentence inputed to re-use the first word of the current one.
|
||||
// As far as I'm aware, this is only used for handling responses to questions
|
||||
_nounState = NOUNSTATE_QUERY;
|
||||
// fall-through
|
||||
|
||||
case OPCODE_CLEAR_LINE:
|
||||
// Resets the input line, removing any pending further actions that were specified
|
||||
Common::fill(&_inputLine[0], &_inputLine[INPUT_LINE_SIZE], 0);
|
||||
_inputLineIndex = 0;
|
||||
break;
|
||||
|
||||
case OPCODE_SET_CAN_TAKE:
|
||||
item = get_item_by_noun(noun);
|
||||
item->_flags |= ITEMF_CAN_TAKE;
|
||||
break;
|
||||
|
||||
case OPCODE_SET_FLAG:
|
||||
_flags[instr->_operand[0]] = true;
|
||||
break;
|
||||
|
||||
case OPCODE_SET_FLAG40:
|
||||
item = getItem(instr);
|
||||
item->_flags |= ITEMF_UNKNOWN;
|
||||
break;
|
||||
|
||||
case OPCODE_SET_INVISIBLE:
|
||||
item = get_item_by_noun(noun);
|
||||
item->_flags |= ITEMF_INVISIBLE;
|
||||
break;
|
||||
|
||||
case OPCODE_SET_OBJECT_DESCRIPTION:
|
||||
item = getItem(instr);
|
||||
item->_stringDesc = (instr->_operand[2] << 8) | instr->_operand[1];
|
||||
break;
|
||||
|
||||
case OPCODE_SET_ROOM_DESCRIPTION:
|
||||
room = get_room(instr->_operand[0]);
|
||||
switch (instr->_operand[2]) {
|
||||
case 0x80:
|
||||
room->_stringDesc = instr->_operand[1];
|
||||
break;
|
||||
case 0x81:
|
||||
room->_stringDesc = instr->_operand[1] + 0x100;
|
||||
break;
|
||||
case 0x82:
|
||||
room->_stringDesc = instr->_operand[1] + 0x200;
|
||||
break;
|
||||
default:
|
||||
error("Bad string desc %.2x:%.2x\n", instr->_operand[1], instr->_operand[2]);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case OPCODE_SET_ROOM_GRAPHIC:
|
||||
room = get_room(instr->_operand[0]);
|
||||
room->_graphic = instr->_operand[1];
|
||||
if (instr->_operand[0] == _currentRoom)
|
||||
_updateFlags |= UPDATE_GRAPHICS;
|
||||
break;
|
||||
|
||||
case OPCODE_SET_STRING_REPLACEMENT1:
|
||||
_currentReplaceWord = (instr->_operand[0] & 0x80) - 1;
|
||||
break;
|
||||
|
||||
case OPCODE_SET_STRING_REPLACEMENT2:
|
||||
_currentReplaceWord = instr->_operand[0] - 1;
|
||||
break;
|
||||
|
||||
case OPCODE_SET_WORD:
|
||||
item = getItem(instr);
|
||||
item->_word = instr->_operand[1];
|
||||
break;
|
||||
|
||||
case OPCODE_SPECIAL:
|
||||
// Game specific opcode
|
||||
_specialOpcode = instr->_operand[0];
|
||||
break;
|
||||
|
||||
case OPCODE_TAKE_CURRENT_OBJECT:
|
||||
item = get_item_by_noun(noun);
|
||||
if (!item)
|
||||
error("Attempt to take object failed\n");
|
||||
|
||||
move_object(item, ROOM_INVENTORY);
|
||||
break;
|
||||
|
||||
case OPCODE_TAKE_OBJECT:
|
||||
item = getItem(instr);
|
||||
move_object(item, ROOM_INVENTORY);
|
||||
break;
|
||||
|
||||
case OPCODE_TEST_FLAG:
|
||||
func_set_test_result(func_state, _flags[instr->_operand[0]]);
|
||||
break;
|
||||
|
||||
case OPCODE_TEST_ROOM_FLAG:
|
||||
func_set_test_result(func_state, room->_flags & instr->_operand[0]);
|
||||
break;
|
||||
|
||||
case OPCODE_TURN_TICK:
|
||||
_variables[VAR_TURN_COUNT]++;
|
||||
break;
|
||||
|
||||
case OPCODE_VAR_ADD:
|
||||
_variables[instr->_operand[0]] += _variables[instr->_operand[1]];
|
||||
break;
|
||||
|
||||
case OPCODE_VAR_DEC:
|
||||
_variables[instr->_operand[0]]--;
|
||||
break;
|
||||
|
||||
case OPCODE_VAR_EQ2:
|
||||
func_set_test_result(func_state,
|
||||
_variables[instr->_operand[0]] == _variables[instr->_operand[1]]);
|
||||
break;
|
||||
|
||||
case OPCODE_VAR_GT1:
|
||||
func_set_test_result(func_state,
|
||||
_variables[0] >
|
||||
_variables[instr->_operand[0]]);
|
||||
break;
|
||||
|
||||
case OPCODE_VAR_GT2:
|
||||
func_set_test_result(func_state, _variables[instr->_operand[0]] >
|
||||
_variables[instr->_operand[1]]);
|
||||
break;
|
||||
|
||||
case OPCODE_VAR_GTE1:
|
||||
func_set_test_result(func_state,
|
||||
_variables[0] >=
|
||||
_variables[instr->_operand[0]]);
|
||||
break;
|
||||
|
||||
case OPCODE_VAR_GTE2:
|
||||
func_set_test_result(func_state,
|
||||
_variables[instr->_operand[0]] >=
|
||||
_variables[instr->_operand[1]]);
|
||||
break;
|
||||
|
||||
case OPCODE_VAR_EQ1:
|
||||
func_set_test_result(func_state,
|
||||
_variables[0] ==
|
||||
_variables[instr->_operand[0]]);
|
||||
break;
|
||||
|
||||
case OPCODE_VAR_INC:
|
||||
_variables[instr->_operand[0]]++;
|
||||
break;
|
||||
|
||||
case OPCODE_VAR_SUB:
|
||||
_variables[instr->_operand[0]] -= _variables[instr->_operand[1]];
|
||||
break;
|
||||
|
||||
default:
|
||||
if (instr->_opcode & 0x80) {
|
||||
warning("Unhandled command opcode %.2x", opcode);
|
||||
} else {
|
||||
warning("Unhandled test opcode %.2x - returning false", opcode);
|
||||
func_set_test_result(func_state, false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ComprehendGameOpcodes::func_set_test_result(FunctionState *func_state, bool value) {
|
||||
if (func_state->_orCount == 0) {
|
||||
/* And */
|
||||
if (func_state->_and) {
|
||||
if (!value)
|
||||
func_state->_testResult = false;
|
||||
} else {
|
||||
func_state->_testResult = value;
|
||||
func_state->_and = true;
|
||||
}
|
||||
|
||||
} else {
|
||||
/* Or */
|
||||
if (value)
|
||||
func_state->_testResult = value;
|
||||
}
|
||||
}
|
||||
|
||||
bool ComprehendGameOpcodes::isItemPresent(Item *item) const {
|
||||
return item && (
|
||||
item->_room == _currentRoom || item->_room == ROOM_INVENTORY
|
||||
|| item->_room == ROOM_CONTAINER
|
||||
);
|
||||
}
|
||||
|
||||
Item *ComprehendGameOpcodes::getItem(const Instruction *instr) {
|
||||
return get_item(instr->_operand[0] - 1);
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------*/
|
||||
|
||||
ComprehendGameV1::ComprehendGameV1() {
|
||||
_opcodeMap[0x01] = OPCODE_HAVE_OBJECT;
|
||||
_opcodeMap[0x02] = OPCODE_VAR_GT2;
|
||||
_opcodeMap[0x04] = OPCODE_OR;
|
||||
_opcodeMap[0x05] = OPCODE_IN_ROOM;
|
||||
_opcodeMap[0x06] = OPCODE_VAR_EQ2;
|
||||
_opcodeMap[0x08] = OPCODE_CURRENT_IS_OBJECT;
|
||||
_opcodeMap[0x09] = OPCODE_OBJECT_PRESENT;
|
||||
_opcodeMap[0x0a] = OPCODE_VAR_GTE2;
|
||||
_opcodeMap[0x0c] = OPCODE_ELSE;
|
||||
_opcodeMap[0x0e] = OPCODE_OBJECT_IN_ROOM;
|
||||
_opcodeMap[0x14] = OPCODE_CURRENT_OBJECT_NOT_VALID;
|
||||
_opcodeMap[0x18] = OPCODE_INVENTORY_FULL;
|
||||
_opcodeMap[0x19] = OPCODE_TEST_FLAG;
|
||||
_opcodeMap[0x1d] = OPCODE_CURRENT_OBJECT_IN_ROOM;
|
||||
_opcodeMap[0x20] = OPCODE_HAVE_CURRENT_OBJECT;
|
||||
_opcodeMap[0x21] = OPCODE_OBJECT_IS_NOT_NOWHERE;
|
||||
_opcodeMap[0x24] = OPCODE_CURRENT_OBJECT_PRESENT;
|
||||
_opcodeMap[0x25] = OPCODE_VAR_GT1;
|
||||
_opcodeMap[0x29] = OPCODE_VAR_EQ1;
|
||||
_opcodeMap[0x2d] = OPCODE_VAR_GTE1;
|
||||
_opcodeMap[0x31] = OPCODE_TEST_ROOM_FLAG;
|
||||
_opcodeMap[0x41] = OPCODE_NOT_HAVE_OBJECT;
|
||||
_opcodeMap[0x45] = OPCODE_NOT_IN_ROOM;
|
||||
_opcodeMap[0x48] = OPCODE_CURRENT_OBJECT_NOT_PRESENT;
|
||||
_opcodeMap[0x49] = OPCODE_OBJECT_NOT_IN_ROOM;
|
||||
_opcodeMap[0x4E] = OPCODE_TEST_FALSE;
|
||||
_opcodeMap[0x50] = OPCODE_CURRENT_OBJECT_IS_NOWHERE;
|
||||
_opcodeMap[0x59] = OPCODE_TEST_NOT_FLAG;
|
||||
_opcodeMap[0x5D] = OPCODE_TEST_FALSE;
|
||||
_opcodeMap[0x60] = OPCODE_NOT_HAVE_CURRENT_OBJECT;
|
||||
_opcodeMap[0x61] = OPCODE_OBJECT_IS_NOWHERE;
|
||||
_opcodeMap[0x64] = OPCODE_CURRENT_OBJECT_NOT_IN_ROOM;
|
||||
_opcodeMap[0x68] = OPCODE_CURRENT_OBJECT_NOT_TAKEABLE;
|
||||
_opcodeMap[0x71] = OPCODE_TEST_NOT_ROOM_FLAG;
|
||||
_opcodeMap[0x80] = OPCODE_INVENTORY;
|
||||
_opcodeMap[0x81] = OPCODE_TAKE_OBJECT;
|
||||
_opcodeMap[0x82] = OPCODE_MOVE_OBJECT_TO_ROOM;
|
||||
_opcodeMap[0x83] = OPCODE_RANDOM_MSG;
|
||||
_opcodeMap[0x84] = OPCODE_SAVE_ACTION;
|
||||
_opcodeMap[0x85] = OPCODE_MOVE_TO_ROOM;
|
||||
_opcodeMap[0x86] = OPCODE_VAR_ADD;
|
||||
_opcodeMap[0x87] = OPCODE_SET_ROOM_DESCRIPTION;
|
||||
_opcodeMap[0x88] = OPCODE_CLEAR_LINE;
|
||||
_opcodeMap[0x89] = OPCODE_MOVE_OBJECT_TO_CURRENT_ROOM;
|
||||
_opcodeMap[0x8a] = OPCODE_VAR_SUB;
|
||||
_opcodeMap[0x8b] = OPCODE_SET_OBJECT_DESCRIPTION;
|
||||
_opcodeMap[0x8c] = OPCODE_MOVE_DEFAULT;
|
||||
_opcodeMap[0x8d] = OPCODE_SET_CAN_TAKE;
|
||||
_opcodeMap[0x8e] = OPCODE_PRINT;
|
||||
_opcodeMap[0x91] = OPCODE_CLEAR_CAN_TAKE;
|
||||
_opcodeMap[0x95] = OPCODE_REMOVE_OBJECT;
|
||||
_opcodeMap[0x99] = OPCODE_SET_FLAG;
|
||||
_opcodeMap[0x92] = OPCODE_CALL_FUNC;
|
||||
_opcodeMap[0x98] = OPCODE_TURN_TICK;
|
||||
_opcodeMap[0x9a] = OPCODE_SET_WORD;
|
||||
_opcodeMap[0x9d] = OPCODE_CLEAR_FLAG;
|
||||
_opcodeMap[0x9e] = OPCODE_INVENTORY_ROOM;
|
||||
_opcodeMap[0xa0] = OPCODE_TAKE_CURRENT_OBJECT;
|
||||
_opcodeMap[0xa1] = OPCODE_SPECIAL;
|
||||
_opcodeMap[0xa4] = OPCODE_DROP_CURRENT_OBJECT;
|
||||
_opcodeMap[0xa2] = OPCODE_SET_ROOM_GRAPHIC;
|
||||
_opcodeMap[0xad] = OPCODE_CLEAR_WORD;
|
||||
_opcodeMap[0xb0] = OPCODE_REMOVE_CURRENT_OBJECT;
|
||||
_opcodeMap[0xb1] = OPCODE_MOVE_DIR;
|
||||
_opcodeMap[0xb5] = OPCODE_SET_STRING_REPLACEMENT1;
|
||||
_opcodeMap[0xb9] = OPCODE_SET_STRING_REPLACEMENT2;
|
||||
_opcodeMap[0xbd] = OPCODE_VAR_INC;
|
||||
_opcodeMap[0xc1] = OPCODE_VAR_DEC;
|
||||
_opcodeMap[0xc5] = OPCODE_SET_STRING_REPLACEMENT3;
|
||||
_opcodeMap[0xc9] = OPCODE_MOVE_CURRENT_OBJECT_TO_ROOM;
|
||||
_opcodeMap[0xcd] = OPCODE_CLEAR_INVISIBLE;
|
||||
_opcodeMap[0xd1] = OPCODE_SET_INVISIBLE;
|
||||
_opcodeMap[0xd5] = OPCODE_CLEAR_FLAG40;
|
||||
_opcodeMap[0xd9] = OPCODE_SET_FLAG40;
|
||||
}
|
||||
|
||||
void ComprehendGameV1::execute_opcode(const Instruction *instr, const Sentence *sentence,
|
||||
FunctionState *func_state) {
|
||||
byte noun = sentence ? sentence->_formattedWords[2] : 0;
|
||||
Room *room = get_room(_currentRoom);
|
||||
Item *item;
|
||||
uint count;
|
||||
|
||||
switch (_opcodeMap[getOpcode(instr)]) {
|
||||
case OPCODE_INVENTORY_FULL:
|
||||
item = get_item_by_noun(noun);
|
||||
|
||||
if (g_debugger->_invLimit)
|
||||
func_set_test_result(func_state, _variables[VAR_INVENTORY_WEIGHT] +
|
||||
(item->_flags & ITEMF_WEIGHT_MASK) > _variables[VAR_INVENTORY_LIMIT]);
|
||||
else
|
||||
// Allow for an unlimited number of items in inventory
|
||||
func_set_test_result(func_state, false);
|
||||
break;
|
||||
|
||||
case OPCODE_OBJECT_NOT_PRESENT:
|
||||
item = getItem(instr);
|
||||
func_set_test_result(func_state, !isItemPresent(item));
|
||||
break;
|
||||
|
||||
case OPCODE_SET_STRING_REPLACEMENT3:
|
||||
_currentReplaceWord = instr->_operand[0] - 1;
|
||||
break;
|
||||
|
||||
/*--------------------------------------*/
|
||||
|
||||
case OPCODE_TEST_NOT_ROOM_FLAG:
|
||||
func_set_test_result(func_state,
|
||||
!(room->_flags & instr->_operand[0]));
|
||||
break;
|
||||
|
||||
case OPCODE_NOT_IN_ROOM:
|
||||
func_set_test_result(func_state,
|
||||
_currentRoom != instr->_operand[0]);
|
||||
break;
|
||||
|
||||
case OPCODE_OBJECT_NOT_IN_ROOM:
|
||||
item = getItem(instr);
|
||||
func_set_test_result(func_state, !item || item->_room != _currentRoom);
|
||||
break;
|
||||
|
||||
case OPCODE_CURRENT_OBJECT_NOT_IN_ROOM:
|
||||
item = get_item_by_noun(noun);
|
||||
func_set_test_result(func_state, !item || item->_room != _currentRoom);
|
||||
break;
|
||||
|
||||
case OPCODE_DESCRIBE_CURRENT_OBJECT:
|
||||
/*
|
||||
* This opcode is only used in version 2
|
||||
* FIXME - unsure what the single operand is for.
|
||||
*/
|
||||
item = get_item_by_noun(noun);
|
||||
g_comprehend->print("%s\n", stringLookup(item->_longString).c_str());
|
||||
break;
|
||||
|
||||
case OPCODE_CURRENT_OBJECT_IN_ROOM: {
|
||||
/* FIXME - use common code for these two ops */
|
||||
bool test = false;
|
||||
|
||||
if (noun) {
|
||||
for (uint i = 0; i < _items.size(); i++) {
|
||||
Item *itemP = &_items[i];
|
||||
|
||||
if (itemP->_word == noun && itemP->_room == instr->_operand[0]) {
|
||||
test = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func_set_test_result(func_state, test);
|
||||
break;
|
||||
}
|
||||
|
||||
case OPCODE_CURRENT_OBJECT_PRESENT:
|
||||
item = get_item_by_noun(noun);
|
||||
if (item)
|
||||
func_set_test_result(func_state,
|
||||
item->_room == _currentRoom);
|
||||
else
|
||||
func_set_test_result(func_state, false);
|
||||
break;
|
||||
|
||||
case OPCODE_NOT_HAVE_CURRENT_OBJECT:
|
||||
item = get_item_by_noun(noun);
|
||||
func_set_test_result(func_state,
|
||||
!item || item->_room != ROOM_INVENTORY);
|
||||
break;
|
||||
|
||||
case OPCODE_NOT_HAVE_OBJECT:
|
||||
item = getItem(instr);
|
||||
func_set_test_result(func_state,
|
||||
item->_room != ROOM_INVENTORY);
|
||||
break;
|
||||
|
||||
case OPCODE_CURRENT_OBJECT_NOT_TAKEABLE:
|
||||
item = get_item_by_noun(noun);
|
||||
if (!item)
|
||||
func_set_test_result(func_state, true);
|
||||
else
|
||||
func_set_test_result(func_state,
|
||||
!(item->_flags & ITEMF_CAN_TAKE));
|
||||
break;
|
||||
|
||||
case OPCODE_CURRENT_OBJECT_IS_NOWHERE:
|
||||
item = get_item_by_noun(noun);
|
||||
func_set_test_result(func_state, item && item->_room == ROOM_NOWHERE);
|
||||
break;
|
||||
|
||||
case OPCODE_OBJECT_IS_NOT_NOWHERE:
|
||||
item = getItem(instr);
|
||||
func_set_test_result(func_state, item->_room != ROOM_NOWHERE);
|
||||
break;
|
||||
|
||||
case OPCODE_CURRENT_OBJECT_NOT_PRESENT:
|
||||
item = get_item_by_noun(noun);
|
||||
func_set_test_result(func_state, !isItemPresent(item));
|
||||
break;
|
||||
|
||||
case OPCODE_REMOVE_CURRENT_OBJECT:
|
||||
item = get_item_by_noun(noun);
|
||||
move_object(item, ROOM_NOWHERE);
|
||||
break;
|
||||
|
||||
case OPCODE_INVENTORY_ROOM:
|
||||
count = num_objects_in_room(instr->_operand[0]);
|
||||
if (count == 0) {
|
||||
console_println(stringLookup(instr->_operand[1] + 1).c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
console_println(stringLookup(instr->_operand[1]).c_str());
|
||||
for (uint i = 0; i < _items.size(); i++) {
|
||||
item = &_items[i];
|
||||
if (item->_room == instr->_operand[0])
|
||||
g_comprehend->print("%s\n",
|
||||
stringLookup(item->_stringDesc).c_str());
|
||||
}
|
||||
break;
|
||||
|
||||
case OPCODE_MOVE_CURRENT_OBJECT_TO_ROOM:
|
||||
item = get_item_by_noun(noun);
|
||||
if (!item)
|
||||
error("Bad current object\n");
|
||||
|
||||
move_object(item, instr->_operand[0]);
|
||||
break;
|
||||
|
||||
case OPCODE_DROP_OBJECT:
|
||||
item = getItem(instr);
|
||||
move_object(item, _currentRoom);
|
||||
break;
|
||||
|
||||
case OPCODE_DROP_CURRENT_OBJECT:
|
||||
item = get_item_by_noun(noun);
|
||||
if (!item)
|
||||
error("Attempt to take object failed\n");
|
||||
|
||||
move_object(item, _currentRoom);
|
||||
break;
|
||||
|
||||
case OPCODE_TEST_NOT_FLAG:
|
||||
func_set_test_result(func_state,
|
||||
!_flags[instr->_operand[0]]);
|
||||
break;
|
||||
|
||||
case OPCODE_TEST_FALSE:
|
||||
// The original had two opcodes mapped to the same code that does
|
||||
// a test, but ignores the result, and is always false
|
||||
func_set_test_result(func_state, false);
|
||||
break;
|
||||
|
||||
case OPCODE_SET_CURRENT_NOUN_STRING_REPLACEMENT:
|
||||
#if 1
|
||||
error("TODO: OPCODE_SET_CURRENT_NOUN_STRING_REPLACEMENT");
|
||||
#else
|
||||
/*
|
||||
* FIXME - Not sure what the operand is for,
|
||||
* maybe capitalisation?
|
||||
*/
|
||||
if (noun && (noun->_type & WORD_TYPE_NOUN_PLURAL))
|
||||
_currentReplaceWord = 3;
|
||||
else if (noun && (noun->_type & WORD_TYPE_FEMALE))
|
||||
_currentReplaceWord = 0;
|
||||
else if (noun && (noun->_type & WORD_TYPE_MALE))
|
||||
_currentReplaceWord = 1;
|
||||
else
|
||||
_currentReplaceWord = 2;
|
||||
#endif
|
||||
break;
|
||||
|
||||
case OPCODE_MOVE_DIR:
|
||||
doMovementVerb(instr->_operand[0]);
|
||||
break;
|
||||
|
||||
default:
|
||||
ComprehendGameOpcodes::execute_opcode(instr, sentence, func_state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------*/
|
||||
|
||||
ComprehendGameV2::ComprehendGameV2() {
|
||||
_opcodeMap[0x01] = OPCODE_HAVE_OBJECT;
|
||||
_opcodeMap[0x02] = OPCODE_VAR_GT2;
|
||||
_opcodeMap[0x04] = OPCODE_OR;
|
||||
_opcodeMap[0x05] = OPCODE_IN_ROOM;
|
||||
_opcodeMap[0x06] = OPCODE_VAR_EQ2;
|
||||
_opcodeMap[0x08] = OPCODE_CURRENT_IS_OBJECT;
|
||||
_opcodeMap[0x09] = OPCODE_VAR_GT1;
|
||||
_opcodeMap[0x0a] = OPCODE_VAR_GTE2;
|
||||
_opcodeMap[0x0c] = OPCODE_ELSE;
|
||||
_opcodeMap[0x0d] = OPCODE_VAR_EQ1;
|
||||
_opcodeMap[0x11] = OPCODE_OBJECT_IS_NOWHERE;
|
||||
_opcodeMap[0x14] = OPCODE_CURRENT_OBJECT_NOT_VALID;
|
||||
_opcodeMap[0x15] = OPCODE_INVENTORY_FULL_X;
|
||||
_opcodeMap[0x19] = OPCODE_TEST_FLAG;
|
||||
_opcodeMap[0x1d] = OPCODE_TEST_ROOM_FLAG;
|
||||
_opcodeMap[0x20] = OPCODE_HAVE_CURRENT_OBJECT;
|
||||
_opcodeMap[0x21] = OPCODE_OBJECT_PRESENT;
|
||||
_opcodeMap[0x22] = OPCODE_OBJECT_IN_ROOM;
|
||||
_opcodeMap[0x25] = OPCODE_OBJECT_TAKEABLE;
|
||||
_opcodeMap[0x29] = OPCODE_INVENTORY_FULL;
|
||||
_opcodeMap[0x2d] = OPCODE_OBJECT_CAN_TAKE;
|
||||
_opcodeMap[0x80] = OPCODE_INVENTORY;
|
||||
_opcodeMap[0x81] = OPCODE_TAKE_OBJECT;
|
||||
_opcodeMap[0x83] = OPCODE_RANDOM_MSG;
|
||||
_opcodeMap[0x84] = OPCODE_SAVE_ACTION;
|
||||
_opcodeMap[0x85] = OPCODE_MOVE_TO_ROOM;
|
||||
_opcodeMap[0x86] = OPCODE_VAR_ADD;
|
||||
_opcodeMap[0x87] = OPCODE_SET_ROOM_DESCRIPTION;
|
||||
_opcodeMap[0x88] = OPCODE_CLEAR_LINE;
|
||||
_opcodeMap[0x89] = OPCODE_SPECIAL;
|
||||
_opcodeMap[0x8a] = OPCODE_VAR_SUB;
|
||||
_opcodeMap[0x8b] = OPCODE_SET_OBJECT_DESCRIPTION;
|
||||
_opcodeMap[0x8c] = OPCODE_MOVE_DEFAULT;
|
||||
_opcodeMap[0x8e] = OPCODE_PRINT;
|
||||
_opcodeMap[0x8f] = OPCODE_SET_OBJECT_LONG_DESCRIPTION;
|
||||
_opcodeMap[0x90] = OPCODE_WAIT_KEY;
|
||||
_opcodeMap[0x92] = OPCODE_CALL_FUNC;
|
||||
_opcodeMap[0x95] = OPCODE_CLEAR_WORD;
|
||||
_opcodeMap[0x96] = OPCODE_CALL_FUNC2;
|
||||
_opcodeMap[0x98] = OPCODE_TURN_TICK;
|
||||
_opcodeMap[0x99] = OPCODE_SET_FLAG;
|
||||
_opcodeMap[0x9a] = OPCODE_SET_WORD;
|
||||
_opcodeMap[0x9d] = OPCODE_CLEAR_FLAG;
|
||||
_opcodeMap[0xa0] = OPCODE_TAKE_CURRENT_OBJECT;
|
||||
_opcodeMap[0xa1] = OPCODE_CLEAR_FLAG40;
|
||||
_opcodeMap[0xa2] = OPCODE_MOVE_OBJECT_TO_ROOM;
|
||||
_opcodeMap[0xa5] = OPCODE_SET_FLAG40;
|
||||
_opcodeMap[0xa9] = OPCODE_CLEAR_INVISIBLE;
|
||||
_opcodeMap[0xad] = OPCODE_SET_INVISIBLE;
|
||||
_opcodeMap[0xc1] = OPCODE_VAR_DEC;
|
||||
_opcodeMap[0xc2] = OPCODE_SET_ROOM_GRAPHIC;
|
||||
_opcodeMap[0xc5] = OPCODE_SET_STRING_REPLACEMENT3;
|
||||
_opcodeMap[0xc9] = OPCODE_SET_STRING_REPLACEMENT1;
|
||||
_opcodeMap[0xcd] = OPCODE_SET_STRING_REPLACEMENT2;
|
||||
_opcodeMap[0xd1] = OPCODE_MOVE_DIR;
|
||||
_opcodeMap[0xd5] = OPCODE_DRAW_ROOM;
|
||||
_opcodeMap[0xd9] = OPCODE_DRAW_OBJECT;
|
||||
_opcodeMap[0xdd] = OPCODE_VAR_INC;
|
||||
_opcodeMap[0xe1] = OPCODE_MOVE_OBJECT_TO_CURRENT_ROOM;
|
||||
_opcodeMap[0xe5] = OPCODE_SET_CAN_TAKE;
|
||||
_opcodeMap[0xe9] = OPCODE_CLEAR_CAN_TAKE;
|
||||
_opcodeMap[0xed] = OPCODE_REMOVE_OBJECT;
|
||||
|
||||
#if 0
|
||||
_opcodeMap[0x9e] = OPCODE_INVENTORY_ROOM;
|
||||
_opcodeMap[0xc6] = OPCODE_SET_OBJECT_GRAPHIC;
|
||||
_opcodeMap[0xf0] = OPCODE_DROP_CURRENT_OBJECT;
|
||||
_opcodeMap[0xfc] = OPCODE_REMOVE_CURRENT_OBJECT;
|
||||
#endif
|
||||
}
|
||||
|
||||
void ComprehendGameV2::execute_opcode(const Instruction *instr, const Sentence *sentence,
|
||||
FunctionState *func_state) {
|
||||
Instruction instrCopy;
|
||||
byte noun = sentence ? sentence->_formattedWords[2] : 0;
|
||||
Room *room = get_room(_currentRoom);
|
||||
Item *item;
|
||||
|
||||
// In case a single opcode is being executed outside of a function, use a dummy function state
|
||||
FunctionState dummyState;
|
||||
if (!func_state)
|
||||
func_state = &dummyState;
|
||||
|
||||
if ((instr->_opcode & 0x30) == 0x30) {
|
||||
// First operand comes from entered sentence noun, shifting out existing operands
|
||||
instrCopy = *instr;
|
||||
instrCopy._operand[2] = instrCopy._operand[1];
|
||||
instrCopy._operand[1] = instrCopy._operand[0];
|
||||
instrCopy._operand[0] = get_item_id(noun) + 1;
|
||||
instr = &instrCopy;
|
||||
}
|
||||
|
||||
func_state->_notComparison = (instr->_opcode & 0x40) != 0;
|
||||
|
||||
switch (_opcodeMap[getOpcode(instr)]) {
|
||||
case OPCODE_CLEAR_INVISIBLE:
|
||||
item = get_item_by_noun(noun);
|
||||
item->_flags &= ~ITEMF_INVISIBLE;
|
||||
break;
|
||||
|
||||
case OPCODE_DRAW_OBJECT:
|
||||
g_comprehend->drawItemPicture(instr->_operand[0] - 1);
|
||||
break;
|
||||
|
||||
case OPCODE_DRAW_ROOM:
|
||||
g_comprehend->drawLocationPicture(instr->_operand[0] - 1);
|
||||
g_comprehend->readChar();
|
||||
break;
|
||||
|
||||
case OPCODE_INVENTORY_FULL:
|
||||
item = get_item_by_noun(noun);
|
||||
|
||||
weighInventory();
|
||||
func_set_test_result(func_state, _totalInventoryWeight + (item->_flags & ITEMF_WEIGHT_MASK) >
|
||||
_variables[VAR_INVENTORY_LIMIT]);
|
||||
break;
|
||||
|
||||
case OPCODE_INVENTORY_FULL_X:
|
||||
item = get_item_by_noun(noun);
|
||||
|
||||
weighInventory();
|
||||
func_set_test_result(func_state, _totalInventoryWeight + (item->_flags & ITEMF_WEIGHT_MASK) >
|
||||
_variables[instr->_operand[1]]);
|
||||
break;
|
||||
|
||||
case OPCODE_MOVE_DIR:
|
||||
if (room->_direction[instr->_operand[0] - 1])
|
||||
move_to(room->_direction[instr->_operand[0] - 1]);
|
||||
else
|
||||
console_println(stringLookup(STRING_CANT_GO).c_str());
|
||||
break;
|
||||
|
||||
case OPCODE_OBJECT_TAKEABLE:
|
||||
// WORKAROUND: Trying to get non-items in OO-Topos
|
||||
func_set_test_result(func_state, instr->_operand[0]
|
||||
&& (getItem(instr)->_flags & ITEMF_WEIGHT_MASK) != ITEMF_WEIGHT_MASK);
|
||||
break;
|
||||
|
||||
case OPCODE_OBJECT_CAN_TAKE:
|
||||
item = getItem(instr);
|
||||
func_set_test_result(func_state, item->_flags & ITEMF_CAN_TAKE);
|
||||
break;
|
||||
|
||||
case OPCODE_SET_OBJECT_GRAPHIC:
|
||||
item = getItem(instr);
|
||||
item->_graphic = instr->_operand[1];
|
||||
if (item->_room == _currentRoom)
|
||||
_updateFlags |= UPDATE_GRAPHICS;
|
||||
break;
|
||||
|
||||
case OPCODE_SET_OBJECT_LONG_DESCRIPTION:
|
||||
item = getItem(instr);
|
||||
item->_longString = (instr->_operand[2] << 8) | instr->_operand[1];
|
||||
break;
|
||||
|
||||
case OPCODE_SET_STRING_REPLACEMENT3: {
|
||||
int articleNum, bits = _wordFlags;
|
||||
for (articleNum = 3; articleNum >= 0; --articleNum, bits <<= 1) {
|
||||
if (bits >= 0x100)
|
||||
break;
|
||||
}
|
||||
if (articleNum == -1)
|
||||
articleNum = 2;
|
||||
|
||||
_currentReplaceWord = instr->_operand[0] + articleNum - 1;
|
||||
break;
|
||||
}
|
||||
|
||||
case OPCODE_WAIT_KEY:
|
||||
console_get_key();
|
||||
break;
|
||||
|
||||
default:
|
||||
ComprehendGameOpcodes::execute_opcode(instr, sentence, func_state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
byte ComprehendGameV2::getOpcode(const Instruction *instr) {
|
||||
// Special pre-processing for opcodes
|
||||
byte opcode = instr->_opcode;
|
||||
if (!(opcode & 0x80))
|
||||
opcode &= 0x3f;
|
||||
if ((opcode & 0x30) == 0x30) {
|
||||
opcode = (opcode & ~0x10) + 1;
|
||||
}
|
||||
|
||||
return opcode;
|
||||
}
|
||||
|
||||
void ComprehendGameV2::func_set_test_result(FunctionState *func_state, bool value) {
|
||||
ComprehendGameOpcodes::func_set_test_result(func_state, value ^ func_state->_notComparison);
|
||||
}
|
||||
|
||||
|
||||
} // namespace Comprehend
|
||||
} // namespace Glk
|
||||
82
engines/glk/comprehend/game_opcodes.h
Normal file
82
engines/glk/comprehend/game_opcodes.h
Normal file
@@ -0,0 +1,82 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GLK_COMPREHEND_GAME_OPCODES_H
|
||||
#define GLK_COMPREHEND_GAME_OPCODES_H
|
||||
|
||||
#include "glk/comprehend/game.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace Comprehend {
|
||||
|
||||
/**
|
||||
* Intermediate derived game class that handles script opcodes common to both
|
||||
* version 1 and version 2 of the engine
|
||||
*/
|
||||
class ComprehendGameOpcodes : public ComprehendGame {
|
||||
protected:
|
||||
ScriptOpcode _opcodeMap[0x100];
|
||||
|
||||
void execute_opcode(const Instruction *instr, const Sentence *sentence, FunctionState *func_state) override;
|
||||
|
||||
Item *getItem(const Instruction *instr);
|
||||
|
||||
virtual void func_set_test_result(FunctionState *func_state, bool value);
|
||||
bool isItemPresent(Item *item) const;
|
||||
public:
|
||||
ComprehendGameOpcodes();
|
||||
|
||||
virtual byte getOpcode(const Instruction *instr) {
|
||||
return instr->_opcode;
|
||||
}
|
||||
ScriptOpcode getScriptOpcode(const Instruction *instr) override {
|
||||
return _opcodeMap[getOpcode(instr)];
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Version 1 Comprehend game
|
||||
*/
|
||||
class ComprehendGameV1 : public ComprehendGameOpcodes {
|
||||
protected:
|
||||
void execute_opcode(const Instruction *instr, const Sentence *sentence, FunctionState *func_state) override;
|
||||
public:
|
||||
ComprehendGameV1();
|
||||
};
|
||||
|
||||
/**
|
||||
* Version 2 Comprehend game
|
||||
*/
|
||||
class ComprehendGameV2 : public ComprehendGameOpcodes {
|
||||
protected:
|
||||
void execute_opcode(const Instruction *instr, const Sentence *sentence, FunctionState *func_state) override;
|
||||
public:
|
||||
ComprehendGameV2();
|
||||
|
||||
byte getOpcode(const Instruction *instr) override;
|
||||
void func_set_test_result(FunctionState *func_state, bool value) override;
|
||||
};
|
||||
|
||||
} // namespace Comprehend
|
||||
} // namespace Glk
|
||||
|
||||
#endif
|
||||
184
engines/glk/comprehend/game_tm.cpp
Normal file
184
engines/glk/comprehend/game_tm.cpp
Normal file
@@ -0,0 +1,184 @@
|
||||
/* 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/comprehend/comprehend.h"
|
||||
#include "glk/comprehend/game_tm.h"
|
||||
#include "glk/comprehend/pics.h"
|
||||
#include "common/md5.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace Comprehend {
|
||||
|
||||
TalismanGame::TalismanGame() : ComprehendGameV2() {
|
||||
_gameDataFile = "G0";
|
||||
|
||||
_locationGraphicFiles.push_back("RA");
|
||||
_locationGraphicFiles.push_back("RB");
|
||||
_locationGraphicFiles.push_back("RC");
|
||||
_locationGraphicFiles.push_back("RD");
|
||||
_locationGraphicFiles.push_back("RE");
|
||||
_locationGraphicFiles.push_back("RF");
|
||||
_locationGraphicFiles.push_back("RG");
|
||||
_itemGraphicFiles.push_back("OA");
|
||||
_itemGraphicFiles.push_back("OB");
|
||||
_itemGraphicFiles.push_back("OE");
|
||||
_itemGraphicFiles.push_back("OF");
|
||||
|
||||
_titleGraphicFile = "t0";
|
||||
}
|
||||
|
||||
#define STRINGS_SEGMENT1 0x16490
|
||||
#define STRINGS_SEGMENT2 0x22fa0
|
||||
#define BANKS_COUNT 15
|
||||
#define STRINGS_PER_BANK 64
|
||||
|
||||
void TalismanGame::loadStrings() {
|
||||
int bankOffsets[BANKS_COUNT];
|
||||
int stringOffsets[STRINGS_PER_BANK + 1];
|
||||
|
||||
_strings.clear();
|
||||
_strings2.clear();
|
||||
|
||||
Common::File f;
|
||||
if (!f.open("novel.exe"))
|
||||
error("novel.exe is a required file");
|
||||
|
||||
Common::String md5 = Common::computeStreamMD5AsString(f, 1024);
|
||||
if (md5 != "0e7f002971acdb055f439020363512ce" && md5 != "2e18c88ce352ebea3e14177703a0485f")
|
||||
error("Unrecognised novel.exe encountered");
|
||||
|
||||
const int STRING_SEGMENTS[2] = { STRINGS_SEGMENT1, STRINGS_SEGMENT2 };
|
||||
|
||||
// TODO: Figure out use of string segment 2
|
||||
for (int strings = 0; strings < 1; ++strings) {
|
||||
f.seek(STRING_SEGMENTS[strings]);
|
||||
for (int bank = 0; bank < BANKS_COUNT; ++bank)
|
||||
bankOffsets[bank] = f.readUint16LE();
|
||||
|
||||
// Iterate through the banks loading the strings
|
||||
for (int bank = 0; bank < BANKS_COUNT; ++bank) {
|
||||
if (!bankOffsets[bank])
|
||||
continue;
|
||||
|
||||
f.seek(STRING_SEGMENTS[strings] + bankOffsets[bank]);
|
||||
for (int strNum = 0; strNum <= STRINGS_PER_BANK; ++strNum)
|
||||
stringOffsets[strNum] = f.readUint16LE();
|
||||
|
||||
for (int strNum = 0; strNum < STRINGS_PER_BANK; ++strNum) {
|
||||
int size = stringOffsets[strNum + 1] - stringOffsets[strNum];
|
||||
if (size < 0)
|
||||
size = 0xfff;
|
||||
|
||||
f.seek(STRING_SEGMENTS[strings] + bankOffsets[bank] + stringOffsets[strNum]);
|
||||
FileBuffer fb(&f, size);
|
||||
Common::String str = parseString(&fb);
|
||||
|
||||
if (bank < 8)
|
||||
_strings.push_back(str);
|
||||
else
|
||||
_strings2.push_back(str);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TalismanGame::playGame() {
|
||||
loadStrings();
|
||||
ComprehendGameV2::playGame();
|
||||
}
|
||||
|
||||
void TalismanGame::beforeGame() {
|
||||
// Draw the title
|
||||
g_comprehend->drawPicture(TITLE_IMAGE);
|
||||
|
||||
// Print game information
|
||||
console_println("Story by Bruce X.Hoffman. Graphics by Ray Redlich and Brian Poff");
|
||||
console_println("Project managed and IBM version by Jeffrey A. Jay. "
|
||||
"Copyright 1987 POLARWARE Inc.");
|
||||
g_comprehend->readChar();
|
||||
|
||||
g_comprehend->glk_window_clear(g_comprehend->_bottomWindow);
|
||||
}
|
||||
|
||||
void TalismanGame::beforeTurn() {
|
||||
_variables[0x62] = g_vm->getRandomNumber(255);
|
||||
|
||||
_functionNum = 17;
|
||||
handleAction(nullptr);
|
||||
}
|
||||
|
||||
void TalismanGame::beforePrompt() {
|
||||
_functionNum = 14;
|
||||
handleAction(nullptr);
|
||||
}
|
||||
|
||||
void TalismanGame::afterPrompt() {
|
||||
if (_savedAction.empty()) {
|
||||
_functionNum = 19;
|
||||
handleAction(nullptr);
|
||||
if (_redoLine == REDO_NONE && _flags[3])
|
||||
_redoLine = REDO_PROMPT;
|
||||
} else {
|
||||
Common::strcpy_s(_inputLine, _savedAction.c_str());
|
||||
_savedAction.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void TalismanGame::handleAction(Sentence *sentence) {
|
||||
if (_flags[62] && _functionNum != _variables[125]) {
|
||||
_variables[124] = _functionNum;
|
||||
_functionNum = _variables[126];
|
||||
}
|
||||
|
||||
ComprehendGameV2::handleAction(sentence);
|
||||
}
|
||||
|
||||
void TalismanGame::handleSpecialOpcode() {
|
||||
switch (_specialOpcode) {
|
||||
case 15:
|
||||
// Switch to text screen mode
|
||||
if (g_comprehend->isGraphicsEnabled()) {
|
||||
g_comprehend->toggleGraphics();
|
||||
updateRoomDesc();
|
||||
}
|
||||
|
||||
_functionNum = 19;
|
||||
handleAction(nullptr);
|
||||
_redoLine = REDO_TURN;
|
||||
break;
|
||||
|
||||
case 17:
|
||||
// Switch to graphics mode
|
||||
if (!g_comprehend->isGraphicsEnabled())
|
||||
g_comprehend->toggleGraphics();
|
||||
|
||||
_updateFlags |= UPDATE_ALL;
|
||||
update();
|
||||
_redoLine = REDO_TURN;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Comprehend
|
||||
} // namespace Glk
|
||||
54
engines/glk/comprehend/game_tm.h
Normal file
54
engines/glk/comprehend/game_tm.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GLK_COMPREHEND_GAME_TM_H
|
||||
#define GLK_COMPREHEND_GAME_TM_H
|
||||
|
||||
#include "glk/comprehend/game_opcodes.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace Comprehend {
|
||||
|
||||
class TalismanGame : public ComprehendGameV2 {
|
||||
private:
|
||||
Common::String _savedAction;
|
||||
private:
|
||||
/**
|
||||
* Load strings from the executable
|
||||
*/
|
||||
void loadStrings();
|
||||
public:
|
||||
TalismanGame();
|
||||
~TalismanGame() override {}
|
||||
|
||||
void playGame() override;
|
||||
void beforeGame() override;
|
||||
void beforeTurn() override;
|
||||
void beforePrompt() override;
|
||||
void afterPrompt() override;
|
||||
void handleAction(Sentence *sentence) override;
|
||||
void handleSpecialOpcode() override;
|
||||
};
|
||||
|
||||
} // namespace Comprehend
|
||||
} // namespace Glk
|
||||
|
||||
#endif
|
||||
287
engines/glk/comprehend/game_tr1.cpp
Normal file
287
engines/glk/comprehend/game_tr1.cpp
Normal file
@@ -0,0 +1,287 @@
|
||||
/* 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/comprehend/comprehend.h"
|
||||
#include "glk/comprehend/game_data.h"
|
||||
#include "glk/comprehend/game_tr1.h"
|
||||
#include "glk/comprehend/pics.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace Comprehend {
|
||||
|
||||
enum RoomId {
|
||||
ROOM_CLAY_HUT = 7,
|
||||
ROOM_FIELD = 26
|
||||
};
|
||||
|
||||
enum RoomFlag {
|
||||
ROOMFLAG_FOREST = 1 << 0,
|
||||
ROOMFLAG_WEREWOLF = 1 << 6,
|
||||
ROOMFLAG_VAMPIRE = 1 << 7
|
||||
};
|
||||
|
||||
enum ItemId {
|
||||
ITEM_GOBLIN = 9,
|
||||
ITEM_SILVER_BULLET = 21,
|
||||
ITEM_BLACK_CAT = 23,
|
||||
ITEM_WEREWOLF = 33,
|
||||
ITEM_VAMPIRE = 38
|
||||
};
|
||||
|
||||
struct TransylvaniaMonster {
|
||||
uint8 _object;
|
||||
uint8 _deadFlag;
|
||||
uint _roomAllowFlag;
|
||||
uint _minTurnsBefore;
|
||||
uint _randomness;
|
||||
};
|
||||
|
||||
|
||||
const TransylvaniaMonster TransylvaniaGame1::WEREWOLF = {
|
||||
ITEM_WEREWOLF, 7, ROOMFLAG_WEREWOLF, 10, 190
|
||||
};
|
||||
|
||||
const TransylvaniaMonster TransylvaniaGame1::VAMPIRE = {
|
||||
ITEM_VAMPIRE, 5, ROOMFLAG_VAMPIRE, 0, 200
|
||||
};
|
||||
|
||||
static const GameStrings TR_STRINGS = {
|
||||
EXTRA_STRING_TABLE(0x8a)
|
||||
};
|
||||
|
||||
|
||||
TransylvaniaGame1::TransylvaniaGame1() : ComprehendGameV1(),
|
||||
_miceReleased(false) {
|
||||
_gameDataFile = "tr.gda";
|
||||
|
||||
_stringFiles.push_back("MA.MS1");
|
||||
_stringFiles.push_back("MB.MS1");
|
||||
_stringFiles.push_back("MC.MS1");
|
||||
_stringFiles.push_back("MD.MS1");
|
||||
_stringFiles.push_back("ME.MS1");
|
||||
|
||||
_locationGraphicFiles.push_back("RA.MS1");
|
||||
_locationGraphicFiles.push_back("RB.MS1");
|
||||
_locationGraphicFiles.push_back("RC.MS1");
|
||||
|
||||
_itemGraphicFiles.push_back("OA.MS1");
|
||||
_itemGraphicFiles.push_back("OB.MS1");
|
||||
_itemGraphicFiles.push_back("OC.MS1");
|
||||
|
||||
_titleGraphicFile = "trtitle.ms1";
|
||||
_gameStrings = &TR_STRINGS;
|
||||
}
|
||||
|
||||
bool TransylvaniaGame1::updateMonster(const TransylvaniaMonster *monsterInfo) {
|
||||
Item *monster;
|
||||
Room *room;
|
||||
uint16 turn_count;
|
||||
|
||||
room = &_rooms[_currentRoom];
|
||||
if (!(room->_flags & monsterInfo->_roomAllowFlag))
|
||||
return false;
|
||||
|
||||
turn_count = _variables[VAR_TURN_COUNT];
|
||||
monster = get_item(monsterInfo->_object);
|
||||
|
||||
if (monster->_room == _currentRoom) {
|
||||
// The monster is in the current room - leave it there
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!_flags[monsterInfo->_deadFlag] &&
|
||||
turn_count > monsterInfo->_minTurnsBefore) {
|
||||
/*
|
||||
* The monster is alive and allowed to move to the current
|
||||
* room. Randomly decide whether on not to. If not, move
|
||||
* it back to limbo.
|
||||
*/
|
||||
if (getRandomNumber(255) > monsterInfo->_randomness) {
|
||||
move_object(monster, _currentRoom);
|
||||
_variables[15] = turn_count + 1;
|
||||
} else {
|
||||
move_object(monster, ROOM_NOWHERE);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TransylvaniaGame1::isMonsterInRoom(const TransylvaniaMonster *monsterInfo) {
|
||||
Item *monster = get_item(monsterInfo->_object);
|
||||
return monster->_room == _currentRoom;
|
||||
}
|
||||
|
||||
int TransylvaniaGame1::roomIsSpecial(uint room_index, uint *roomDescString) {
|
||||
Room *room = &_rooms[room_index];
|
||||
|
||||
if (room_index == 0x28) {
|
||||
if (roomDescString)
|
||||
*roomDescString = room->_stringDesc;
|
||||
return ROOM_IS_DARK;
|
||||
}
|
||||
|
||||
return ROOM_IS_NORMAL;
|
||||
}
|
||||
|
||||
void TransylvaniaGame1::beforeTurn() {
|
||||
Room *room;
|
||||
|
||||
if (!isMonsterInRoom(&WEREWOLF) && !isMonsterInRoom(&VAMPIRE)) {
|
||||
if (_currentRoom == ROOM_CLAY_HUT) {
|
||||
Item *blackCat = get_item(ITEM_BLACK_CAT);
|
||||
if (blackCat->_room == _currentRoom && getRandomNumber(255) >= 128)
|
||||
console_println(_strings[109].c_str());
|
||||
goto done;
|
||||
|
||||
} else if (_currentRoom == ROOM_FIELD) {
|
||||
Item *goblin = get_item(ITEM_GOBLIN);
|
||||
if (goblin->_room == _currentRoom)
|
||||
console_println(_strings[94 + getRandomNumber(3)].c_str());
|
||||
goto done;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (updateMonster(&WEREWOLF) || updateMonster(&VAMPIRE))
|
||||
goto done;
|
||||
|
||||
room = &_rooms[_currentRoom];
|
||||
if ((room->_flags & ROOMFLAG_FOREST) && (_variables[VAR_TURN_COUNT] % 255) >= 4
|
||||
&& getRandomNumber(255) < 40) {
|
||||
int stringNum = _miceReleased ? 108 : 107;
|
||||
console_println(_strings[stringNum].c_str());
|
||||
|
||||
// Until the mice are released, an eagle moves player to a random room
|
||||
if (!_miceReleased) {
|
||||
// Get new room to get moved to
|
||||
int roomNum = getRandomNumber(3) + 1;
|
||||
if (roomNum == _currentRoom)
|
||||
roomNum += 15;
|
||||
|
||||
move_to(roomNum);
|
||||
|
||||
// Make sure Werwolf and Vampire aren't present
|
||||
get_item(ITEM_WEREWOLF)->_room = 0xff;
|
||||
get_item(ITEM_VAMPIRE)->_room = 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
ComprehendGameV1::beforeTurn();
|
||||
}
|
||||
|
||||
void TransylvaniaGame1::synchronizeSave(Common::Serializer &s) {
|
||||
ComprehendGame::synchronizeSave(s);
|
||||
s.syncAsByte(_miceReleased);
|
||||
|
||||
// As a post-step, ensure the vampire and werewolf aren't present
|
||||
get_item(ITEM_WEREWOLF)->_room = 0xff;
|
||||
get_item(ITEM_VAMPIRE)->_room = 0xff;
|
||||
}
|
||||
|
||||
void TransylvaniaGame1::handleSpecialOpcode() {
|
||||
switch (_specialOpcode) {
|
||||
case 1:
|
||||
// Mice have been released
|
||||
_miceReleased = true;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
// Gun is fired. Drop the bullet in a random room
|
||||
get_item(ITEM_SILVER_BULLET)->_room = getRandomNumber(7) + 1;
|
||||
_updateFlags |= UPDATE_GRAPHICS;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
case 4:
|
||||
// Game over - failure
|
||||
console_println(_strings2[138].c_str());
|
||||
game_restart();
|
||||
break;
|
||||
|
||||
case 5:
|
||||
// Won the game
|
||||
g_comprehend->showGraphics();
|
||||
g_comprehend->drawLocationPicture(40);
|
||||
game_restart();
|
||||
break;
|
||||
|
||||
case 6:
|
||||
game_save();
|
||||
break;
|
||||
|
||||
case 7:
|
||||
game_restore();
|
||||
break;
|
||||
|
||||
case 8:
|
||||
// Restart game
|
||||
game_restart();
|
||||
break;
|
||||
|
||||
case 9:
|
||||
// Show the Zin screen in response to doing
|
||||
// 'sing some enchanted evening' in his cabin.
|
||||
g_comprehend->showGraphics();
|
||||
g_comprehend->drawLocationPicture(41);
|
||||
console_get_key();
|
||||
_updateFlags |= UPDATE_GRAPHICS;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#define READ_LINE do { \
|
||||
g_comprehend->readLine(buffer, sizeof(buffer)); \
|
||||
if (g_comprehend->shouldQuit()) return; \
|
||||
} while (strlen(buffer) == 0)
|
||||
|
||||
void TransylvaniaGame1::beforeGame() {
|
||||
char buffer[128];
|
||||
g_comprehend->setDisableSaves(true);
|
||||
|
||||
// Draw the title
|
||||
g_comprehend->drawPicture(TITLE_IMAGE);
|
||||
|
||||
// Print game information
|
||||
console_println("Story and graphics by Antonio Antiochia.");
|
||||
console_println("IBM version by Jeffrey A. Jay. Copyright 1987 POLARWARE, Inc.");
|
||||
g_comprehend->readChar();
|
||||
|
||||
// Welcome to Transylvania - sign your name
|
||||
console_println(_strings[0x20].c_str());
|
||||
READ_LINE;
|
||||
|
||||
// The player's name is stored in word 0
|
||||
_replaceWords[0] = Common::String(buffer);
|
||||
|
||||
// And your next of kin - This isn't stored by the game
|
||||
console_println(_strings[0x21].c_str());
|
||||
READ_LINE;
|
||||
|
||||
g_comprehend->setDisableSaves(false);
|
||||
}
|
||||
|
||||
} // namespace Comprehend
|
||||
} // namespace Glk
|
||||
54
engines/glk/comprehend/game_tr1.h
Normal file
54
engines/glk/comprehend/game_tr1.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GLK_COMPREHEND_GAME_TR1_H
|
||||
#define GLK_COMPREHEND_GAME_TR1_H
|
||||
|
||||
#include "glk/comprehend/game_opcodes.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace Comprehend {
|
||||
|
||||
struct TransylvaniaMonster;
|
||||
|
||||
class TransylvaniaGame1 : public ComprehendGameV1 {
|
||||
private:
|
||||
static const TransylvaniaMonster WEREWOLF;
|
||||
static const TransylvaniaMonster VAMPIRE;
|
||||
bool _miceReleased;
|
||||
|
||||
bool updateMonster(const TransylvaniaMonster *monsterInfo);
|
||||
bool isMonsterInRoom(const TransylvaniaMonster *monsterInfo);
|
||||
public:
|
||||
TransylvaniaGame1();
|
||||
~TransylvaniaGame1() override {}
|
||||
|
||||
void beforeGame() override;
|
||||
void beforeTurn() override;
|
||||
void synchronizeSave(Common::Serializer &s) override;
|
||||
int roomIsSpecial(uint room_index, uint *roomDescString) override;
|
||||
void handleSpecialOpcode() override;
|
||||
};
|
||||
|
||||
} // namespace Comprehend
|
||||
} // namespace Glk
|
||||
|
||||
#endif
|
||||
280
engines/glk/comprehend/game_tr2.cpp
Normal file
280
engines/glk/comprehend/game_tr2.cpp
Normal file
@@ -0,0 +1,280 @@
|
||||
/* 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/comprehend/comprehend.h"
|
||||
#include "glk/comprehend/game_data.h"
|
||||
#include "glk/comprehend/game_tr2.h"
|
||||
#include "glk/comprehend/pics.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace Comprehend {
|
||||
|
||||
enum RoomId {
|
||||
ROOM_CLAY_HUT = 7,
|
||||
ROOM_FIELD = 26
|
||||
};
|
||||
|
||||
enum RoomFlag {
|
||||
ROOMFLAG_FOREST = 1 << 0,
|
||||
ROOMFLAG_WEREWOLF = 1 << 6,
|
||||
ROOMFLAG_VAMPIRE = 1 << 7
|
||||
};
|
||||
|
||||
enum ItemId {
|
||||
ITEM_GOBLIN = 9,
|
||||
ITEM_SILVER_BULLET = 21,
|
||||
ITEM_BLACK_CAT = 23,
|
||||
ITEM_WEREWOLF = 33,
|
||||
ITEM_VAMPIRE = 38
|
||||
};
|
||||
|
||||
struct TransylvaniaMonster {
|
||||
uint8 _object;
|
||||
uint8 _deadFlag;
|
||||
uint _roomAllowFlag;
|
||||
uint _minTurnsBefore;
|
||||
uint _randomness;
|
||||
};
|
||||
|
||||
|
||||
const TransylvaniaMonster TransylvaniaGame2::WEREWOLF = {
|
||||
ITEM_WEREWOLF, 7, ROOMFLAG_WEREWOLF, 10, 190
|
||||
};
|
||||
|
||||
const TransylvaniaMonster TransylvaniaGame2::VAMPIRE = {
|
||||
ITEM_VAMPIRE, 5, ROOMFLAG_VAMPIRE, 0, 200
|
||||
};
|
||||
|
||||
static const GameStrings TR_STRINGS = {
|
||||
EXTRA_STRING_TABLE(0x8a)
|
||||
};
|
||||
|
||||
|
||||
TransylvaniaGame2::TransylvaniaGame2() : ComprehendGameV2(),
|
||||
_miceReleased(false) {
|
||||
_gameDataFile = "g0";
|
||||
|
||||
_locationGraphicFiles.push_back("RA");
|
||||
_locationGraphicFiles.push_back("RB");
|
||||
_locationGraphicFiles.push_back("RC");
|
||||
_itemGraphicFiles.push_back("OA");
|
||||
_itemGraphicFiles.push_back("OB");
|
||||
_itemGraphicFiles.push_back("OC");
|
||||
|
||||
_titleGraphicFile = "t0";
|
||||
_gameStrings = &TR_STRINGS;
|
||||
}
|
||||
|
||||
bool TransylvaniaGame2::updateMonster(const TransylvaniaMonster *monsterInfo) {
|
||||
Item *monster;
|
||||
Room *room;
|
||||
uint16 turn_count;
|
||||
|
||||
room = &_rooms[_currentRoom];
|
||||
if (!(room->_flags & monsterInfo->_roomAllowFlag))
|
||||
return false;
|
||||
|
||||
turn_count = _variables[VAR_TURN_COUNT];
|
||||
monster = get_item(monsterInfo->_object);
|
||||
|
||||
if (monster->_room == _currentRoom) {
|
||||
// The monster is in the current room - leave it there
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!_flags[monsterInfo->_deadFlag] &&
|
||||
turn_count > monsterInfo->_minTurnsBefore) {
|
||||
/*
|
||||
* The monster is alive and allowed to move to the current
|
||||
* room. Randomly decide whether on not to. If not, move
|
||||
* it back to limbo.
|
||||
*/
|
||||
if (getRandomNumber(255) > monsterInfo->_randomness) {
|
||||
move_object(monster, _currentRoom);
|
||||
_variables[15] = turn_count + 1;
|
||||
} else {
|
||||
move_object(monster, ROOM_NOWHERE);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TransylvaniaGame2::isMonsterInRoom(const TransylvaniaMonster *monsterInfo) {
|
||||
Item *monster = get_item(monsterInfo->_object);
|
||||
return monster->_room == _currentRoom;
|
||||
}
|
||||
|
||||
int TransylvaniaGame2::roomIsSpecial(uint room_index, uint *roomDescString) {
|
||||
Room *room = &_rooms[room_index];
|
||||
|
||||
if (room_index == 0x28) {
|
||||
if (roomDescString)
|
||||
*roomDescString = room->_stringDesc;
|
||||
return ROOM_IS_DARK;
|
||||
}
|
||||
|
||||
return ROOM_IS_NORMAL;
|
||||
}
|
||||
|
||||
void TransylvaniaGame2::beforeTurn() {
|
||||
Room *room;
|
||||
|
||||
if (!isMonsterInRoom(&WEREWOLF) && !isMonsterInRoom(&VAMPIRE)) {
|
||||
if (_currentRoom == ROOM_CLAY_HUT) {
|
||||
Item *blackCat = get_item(ITEM_BLACK_CAT);
|
||||
if (blackCat->_room == _currentRoom && getRandomNumber(255) >= 128)
|
||||
console_println(_strings[109].c_str());
|
||||
goto done;
|
||||
|
||||
} else if (_currentRoom == ROOM_FIELD) {
|
||||
Item *goblin = get_item(ITEM_GOBLIN);
|
||||
if (goblin->_room == _currentRoom)
|
||||
console_println(_strings[94 + getRandomNumber(3)].c_str());
|
||||
goto done;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
if (updateMonster(&WEREWOLF) || updateMonster(&VAMPIRE))
|
||||
goto done;
|
||||
|
||||
room = &_rooms[_currentRoom];
|
||||
if ((room->_flags & ROOMFLAG_FOREST) && (_variables[VAR_TURN_COUNT] % 255) >= 4
|
||||
&& getRandomNumber(255) < 40) {
|
||||
int stringNum = _miceReleased ? 108 : 107;
|
||||
console_println(_strings[stringNum].c_str());
|
||||
|
||||
// Until the mice are released, an eagle moves player to a random room
|
||||
if (!_miceReleased) {
|
||||
// Get new room to get moved to
|
||||
int roomNum = getRandomNumber(3) + 1;
|
||||
if (roomNum == _currentRoom)
|
||||
roomNum += 15;
|
||||
|
||||
move_to(roomNum);
|
||||
|
||||
// Make sure Werwolf and Vampire aren't present
|
||||
get_item(ITEM_WEREWOLF)->_room = 0xff;
|
||||
get_item(ITEM_VAMPIRE)->_room = 0xff;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
ComprehendGameV2::beforeTurn();
|
||||
}
|
||||
|
||||
void TransylvaniaGame2::synchronizeSave(Common::Serializer &s) {
|
||||
ComprehendGame::synchronizeSave(s);
|
||||
s.syncAsByte(_miceReleased);
|
||||
|
||||
// As a post-step, ensure the vampire and werewolf aren't present
|
||||
get_item(ITEM_WEREWOLF)->_room = 0xff;
|
||||
get_item(ITEM_VAMPIRE)->_room = 0xff;
|
||||
}
|
||||
|
||||
void TransylvaniaGame2::handleSpecialOpcode() {
|
||||
switch (_specialOpcode) {
|
||||
case 1:
|
||||
// Mice have been released
|
||||
_miceReleased = true;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
// Gun is fired. Drop the bullet in a random room
|
||||
get_item(ITEM_SILVER_BULLET)->_room = getRandomNumber(7) + 1;
|
||||
_updateFlags |= UPDATE_GRAPHICS;
|
||||
break;
|
||||
|
||||
case 3:
|
||||
case 4:
|
||||
// Game over - failure
|
||||
console_println(_strings2[138].c_str());
|
||||
game_restart();
|
||||
break;
|
||||
|
||||
case 5:
|
||||
// Won the game
|
||||
g_comprehend->showGraphics();
|
||||
g_comprehend->drawLocationPicture(40);
|
||||
game_restart();
|
||||
break;
|
||||
|
||||
case 6:
|
||||
game_save();
|
||||
break;
|
||||
|
||||
case 7:
|
||||
game_restore();
|
||||
break;
|
||||
|
||||
case 8:
|
||||
// Restart game
|
||||
game_restart();
|
||||
break;
|
||||
|
||||
case 9:
|
||||
// Show the Zin screen in response to doing
|
||||
// 'sing some enchanted evening' in his cabin.
|
||||
g_comprehend->showGraphics();
|
||||
g_comprehend->drawLocationPicture(41);
|
||||
console_get_key();
|
||||
_updateFlags |= UPDATE_GRAPHICS;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#define READ_LINE do { \
|
||||
g_comprehend->readLine(buffer, sizeof(buffer)); \
|
||||
if (g_comprehend->shouldQuit()) return; \
|
||||
} while (strlen(buffer) == 0)
|
||||
|
||||
void TransylvaniaGame2::beforeGame() {
|
||||
char buffer[128];
|
||||
g_comprehend->setDisableSaves(true);
|
||||
|
||||
// Draw the title
|
||||
g_comprehend->drawPicture(TITLE_IMAGE);
|
||||
|
||||
// Print game information
|
||||
console_println("Story and graphics by Antonio Antiochia.");
|
||||
console_println("IBM version by Jeffrey A. Jay. Copyright 1987 POLARWARE, Inc.");
|
||||
g_comprehend->readChar();
|
||||
|
||||
// Welcome to Transylvania - sign your name
|
||||
console_println(_strings[0x20].c_str());
|
||||
READ_LINE;
|
||||
|
||||
// The player's name is stored in word 0
|
||||
_replaceWords[0] = Common::String(buffer);
|
||||
|
||||
// And your next of kin - This isn't stored by the game
|
||||
console_println(_strings[0x21].c_str());
|
||||
READ_LINE;
|
||||
|
||||
g_comprehend->setDisableSaves(false);
|
||||
}
|
||||
|
||||
} // namespace Comprehend
|
||||
} // namespace Glk
|
||||
54
engines/glk/comprehend/game_tr2.h
Normal file
54
engines/glk/comprehend/game_tr2.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GLK_COMPREHEND_GAME_TR2_H
|
||||
#define GLK_COMPREHEND_GAME_TR2_H
|
||||
|
||||
#include "glk/comprehend/game_opcodes.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace Comprehend {
|
||||
|
||||
struct TransylvaniaMonster;
|
||||
|
||||
class TransylvaniaGame2 : public ComprehendGameV2 {
|
||||
private:
|
||||
static const TransylvaniaMonster WEREWOLF;
|
||||
static const TransylvaniaMonster VAMPIRE;
|
||||
bool _miceReleased;
|
||||
|
||||
bool updateMonster(const TransylvaniaMonster *monsterInfo);
|
||||
bool isMonsterInRoom(const TransylvaniaMonster *monsterInfo);
|
||||
public:
|
||||
TransylvaniaGame2();
|
||||
~TransylvaniaGame2() override {}
|
||||
|
||||
void beforeGame() override;
|
||||
void beforeTurn() override;
|
||||
void synchronizeSave(Common::Serializer &s) override;
|
||||
int roomIsSpecial(uint room_index, uint *roomDescString) override;
|
||||
void handleSpecialOpcode() override;
|
||||
};
|
||||
|
||||
} // namespace Comprehend
|
||||
} // namespace Glk
|
||||
|
||||
#endif
|
||||
447
engines/glk/comprehend/pics.cpp
Normal file
447
engines/glk/comprehend/pics.cpp
Normal file
@@ -0,0 +1,447 @@
|
||||
/* 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/comprehend/pics.h"
|
||||
#include "common/memstream.h"
|
||||
#include "glk/comprehend/charset.h"
|
||||
#include "glk/comprehend/comprehend.h"
|
||||
#include "glk/comprehend/draw_surface.h"
|
||||
#include "glk/comprehend/file_buf.h"
|
||||
#include "glk/comprehend/game.h"
|
||||
#include "glk/comprehend/game_data.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace Comprehend {
|
||||
|
||||
#define IMAGES_PER_FILE 16
|
||||
|
||||
enum Opcode {
|
||||
OPCODE_END = 0,
|
||||
OPCODE_SET_TEXT_POS = 1,
|
||||
OPCODE_SET_PEN_COLOR = 2,
|
||||
OPCODE_TEXT_CHAR = 3,
|
||||
OPCODE_SET_SHAPE = 4,
|
||||
OPCODE_TEXT_OUTLINE = 5,
|
||||
OPCODE_SET_FILL_COLOR = 6,
|
||||
OPCODE_END2 = 7,
|
||||
OPCODE_MOVE_TO = 8,
|
||||
OPCODE_DRAW_BOX = 9,
|
||||
OPCODE_DRAW_LINE = 10,
|
||||
OPCODE_DRAW_CIRCLE = 11,
|
||||
OPCODE_DRAW_SHAPE = 12,
|
||||
OPCODE_DELAY = 13,
|
||||
OPCODE_PAINT = 14,
|
||||
OPCODE_RESET = 15
|
||||
};
|
||||
|
||||
enum SpecialOpcode {
|
||||
RESETOP_0 = 0,
|
||||
RESETOP_RESET = 1,
|
||||
RESETOP_OO_TOPOS_UNKNOWN = 3
|
||||
};
|
||||
|
||||
/*-------------------------------------------------------*/
|
||||
|
||||
uint32 Pics::ImageContext::getFillColor() const {
|
||||
uint color = _fillColor;
|
||||
|
||||
// FIXME: Properly display text color in Crimson Crown
|
||||
if (g_vm->getGameID() == "crimsoncrown" && color == 0x000000ff)
|
||||
color = G_COLOR_WHITE;
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
void Pics::ImageContext::lineFixes() {
|
||||
// WORKAROUND: Fix lines on title screens so floodfill works correctly
|
||||
if (g_vm->getGameID() == "transylvania" && _picIndex == 9999) {
|
||||
_drawSurface->drawLine(191, 31, 192, 31, G_COLOR_BLACK); // v
|
||||
_drawSurface->drawLine(196, 50, 197, 50, G_COLOR_BLACK); // a
|
||||
_drawSurface->drawLine(203, 49, 204, 49, G_COLOR_BLACK);
|
||||
_drawSurface->drawLine(197, 53, 202, 53, G_COLOR_BLACK);
|
||||
_drawSurface->drawLine(215, 51, 220, 51, G_COLOR_BLACK); // n
|
||||
_drawSurface->drawLine(221, 51, 222, 51, G_COLOR_BLACK);
|
||||
_drawSurface->drawLine(228, 50, 229, 50, G_COLOR_BLACK);
|
||||
_drawSurface->drawLine(217, 59, 220, 59, G_COLOR_BLACK);
|
||||
_drawSurface->drawLine(212, 49, 212, 50, G_COLOR_BLACK);
|
||||
_drawSurface->drawLine(213, 49, 213, 52, G_COLOR_WHITE);
|
||||
_drawSurface->drawLine(235, 52, 236, 61, G_COLOR_BLACK); // i
|
||||
_drawSurface->drawLine(237, 61, 238, 61, G_COLOR_BLACK);
|
||||
}
|
||||
|
||||
if (g_vm->getGameID() == "crimsoncrown" && _picIndex == 9999 && _x == 67 && _y == 55) {
|
||||
_drawSurface->drawLine(78, 28, 77, 29, G_COLOR_WHITE);
|
||||
_drawSurface->drawLine(71, 43, 69, 47, G_COLOR_WHITE);
|
||||
_drawSurface->drawLine(67, 57, 68, 56, G_COLOR_WHITE);
|
||||
_drawSurface->drawLine(79, 101, 80, 101, G_COLOR_WHITE);
|
||||
_drawSurface->drawLine(183, 101, 184, 100, G_COLOR_WHITE);
|
||||
_drawSurface->drawLine(193, 47, 193, 48, G_COLOR_WHITE);
|
||||
_drawSurface->drawLine(68, 48, 71, 48, G_COLOR_BLACK);
|
||||
}
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------*/
|
||||
|
||||
Pics::ImageFile::ImageFile(const Common::String &filename, bool isSingleImage) : _filename(filename) {
|
||||
Common::File f;
|
||||
uint16 version;
|
||||
int i;
|
||||
|
||||
if (!f.open(_filename))
|
||||
error("Could not open file - %s", filename.c_str());
|
||||
|
||||
if (isSingleImage) {
|
||||
// It's a title image file, which has only a single image with no
|
||||
// table of image offsets
|
||||
_imageOffsets.resize(1);
|
||||
_imageOffsets[0] = 4;
|
||||
return;
|
||||
}
|
||||
|
||||
version = f.readUint16LE();
|
||||
if (version == 0x1000)
|
||||
f.seek(4);
|
||||
else
|
||||
f.seek(0);
|
||||
|
||||
// Get the image offsets in the file
|
||||
_imageOffsets.resize(IMAGES_PER_FILE);
|
||||
for (i = 0; i < IMAGES_PER_FILE; i++) {
|
||||
_imageOffsets[i] = f.readUint16LE();
|
||||
if (version == 0x1000)
|
||||
_imageOffsets[i] += 4;
|
||||
}
|
||||
}
|
||||
|
||||
void Pics::ImageFile::draw(uint index, ImageContext *ctx) const {
|
||||
if (!ctx->_file.open(_filename))
|
||||
error("Opening image file");
|
||||
|
||||
ctx->_file.seek(_imageOffsets[index]);
|
||||
|
||||
for (bool done = false; !done;) {
|
||||
done = doImageOp(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
bool Pics::ImageFile::doImageOp(Pics::ImageContext *ctx) const {
|
||||
uint8 opcode;
|
||||
uint16 a, b;
|
||||
|
||||
opcode = ctx->_file.readByte();
|
||||
debugCN(kDebugGraphics, " %.4x [%.2x]: ", (int)ctx->_file.pos() - 1, opcode);
|
||||
|
||||
byte param = opcode & 0xf;
|
||||
opcode >>= 4;
|
||||
|
||||
switch (opcode) {
|
||||
case OPCODE_END:
|
||||
case OPCODE_END2:
|
||||
// End of the rendering
|
||||
debugC(kDebugGraphics, "End of image");
|
||||
return true;
|
||||
|
||||
case OPCODE_SET_TEXT_POS:
|
||||
a = imageGetOperand(ctx) + (param & 1 ? 256 : 0);
|
||||
b = imageGetOperand(ctx);
|
||||
debugC(kDebugGraphics, "set_text_pos(%d, %d)", a, b);
|
||||
|
||||
ctx->_textX = a;
|
||||
ctx->_textY = b;
|
||||
break;
|
||||
|
||||
case OPCODE_SET_PEN_COLOR:
|
||||
debugC(kDebugGraphics, "set_pen_color(%.2x)", opcode);
|
||||
if (!(ctx->_drawFlags & IMAGEF_NO_FILL))
|
||||
ctx->_penColor = ctx->_drawSurface->getPenColor(param);
|
||||
break;
|
||||
|
||||
case OPCODE_TEXT_CHAR:
|
||||
case OPCODE_TEXT_OUTLINE:
|
||||
// Text outline mode draws a bunch of pixels that sort of looks like the char
|
||||
// TODO: See if the outline mode is ever used
|
||||
if (opcode == OPCODE_TEXT_OUTLINE)
|
||||
warning("TODO: Implement drawing text outlines");
|
||||
|
||||
a = imageGetOperand(ctx);
|
||||
if (a < 0x20 || a >= 0x7f) {
|
||||
warning("Invalid character - %c", a);
|
||||
a = '?';
|
||||
}
|
||||
|
||||
debugC(kDebugGraphics, "draw_char(%c)", a);
|
||||
ctx->_font->drawChar(ctx->_drawSurface, a, ctx->_textX, ctx->_textY, ctx->getFillColor());
|
||||
ctx->_textX += ctx->_font->getCharWidth(a);
|
||||
break;
|
||||
|
||||
case OPCODE_SET_SHAPE:
|
||||
debugC(kDebugGraphics, "set_shape_type(%.2x)", param);
|
||||
|
||||
if (param == 8) {
|
||||
// FIXME: This appears to be a _shape type. Only used by OO-Topos
|
||||
warning("TODO: Shape type 8");
|
||||
ctx->_shape = SHAPE_PIXEL;
|
||||
} else {
|
||||
ctx->_shape = (Shape)param;
|
||||
}
|
||||
break;
|
||||
|
||||
case OPCODE_SET_FILL_COLOR:
|
||||
a = imageGetOperand(ctx);
|
||||
debugC(kDebugGraphics, "set_fill_color(%.2x)", a);
|
||||
ctx->_fillColor = ctx->_drawSurface->getFillColor(a);
|
||||
break;
|
||||
|
||||
case OPCODE_MOVE_TO:
|
||||
a = imageGetOperand(ctx) + (param & 1 ? 256 : 0);
|
||||
b = imageGetOperand(ctx);
|
||||
|
||||
debugC(kDebugGraphics, "move_to(%d, %d)", a, b);
|
||||
ctx->_x = a;
|
||||
ctx->_y = b;
|
||||
break;
|
||||
|
||||
case OPCODE_DRAW_BOX:
|
||||
a = imageGetOperand(ctx) + (param & 1 ? 256 : 0);
|
||||
b = imageGetOperand(ctx);
|
||||
|
||||
debugC(kDebugGraphics, "draw_box (%d, %d) - (%d, %d)",
|
||||
ctx->_x, ctx->_y, a, b);
|
||||
|
||||
ctx->_drawSurface->drawBox(ctx->_x, ctx->_y, a, b, ctx->_penColor);
|
||||
break;
|
||||
|
||||
case OPCODE_DRAW_LINE:
|
||||
a = imageGetOperand(ctx) + (param & 1 ? 256 : 0);
|
||||
b = imageGetOperand(ctx);
|
||||
|
||||
debugC(kDebugGraphics, "draw_line (%d, %d) - (%d, %d)",
|
||||
ctx->_x, ctx->_y, a, b);
|
||||
ctx->_drawSurface->drawLine(ctx->_x, ctx->_y, a, b, ctx->_penColor);
|
||||
|
||||
ctx->_x = a;
|
||||
ctx->_y = b;
|
||||
break;
|
||||
|
||||
case OPCODE_DRAW_CIRCLE:
|
||||
a = imageGetOperand(ctx);
|
||||
debugC(kDebugGraphics, "draw_circle (%d, %d) diameter=%d",
|
||||
ctx->_x, ctx->_y, a);
|
||||
|
||||
ctx->_drawSurface->drawCircle(ctx->_x, ctx->_y, a, ctx->_penColor);
|
||||
break;
|
||||
|
||||
case OPCODE_DRAW_SHAPE:
|
||||
a = imageGetOperand(ctx) + (param & 1 ? 256 : 0);
|
||||
b = imageGetOperand(ctx);
|
||||
debugC(kDebugGraphics, "draw_shape(%d, %d), style=%.2x, fill=%.2x",
|
||||
a, b, ctx->_shape, ctx->_fillColor);
|
||||
|
||||
if (!(ctx->_drawFlags & IMAGEF_NO_FILL))
|
||||
ctx->_drawSurface->drawShape(a, b, ctx->_shape, ctx->_fillColor);
|
||||
break;
|
||||
|
||||
case OPCODE_DELAY:
|
||||
// The original allowed for rendering to be paused briefly. We don't do
|
||||
// that in ScummVM, and just show the finished rendered image
|
||||
(void)imageGetOperand(ctx);
|
||||
break;
|
||||
|
||||
case OPCODE_PAINT:
|
||||
a = imageGetOperand(ctx) + (param & 1 ? 256 : 0);
|
||||
b = imageGetOperand(ctx);
|
||||
if (opcode & 0x1)
|
||||
a += 255;
|
||||
|
||||
debugC(kDebugGraphics, "paint(%d, %d)", a, b);
|
||||
ctx->lineFixes();
|
||||
if (!(ctx->_drawFlags & IMAGEF_NO_FILL))
|
||||
ctx->_drawSurface->floodFill(a, b, ctx->_fillColor);
|
||||
break;
|
||||
|
||||
#if 0
|
||||
// FIXME: The reset case was causing room outside cell to be drawn all white
|
||||
case OPCODE_RESET:
|
||||
a = imageGetOperand(ctx);
|
||||
doResetOp(ctx, a);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
|
||||
//ctx->_drawSurface->dumpToScreen();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Pics::ImageFile::doResetOp(ImageContext *ctx, byte param) const {
|
||||
switch (param) {
|
||||
case RESETOP_0:
|
||||
// In Transylvania this sub-opcode is a do nothing
|
||||
break;
|
||||
|
||||
case RESETOP_RESET:
|
||||
// TODO: Calls same reset that first gets called when rendering starts.
|
||||
// Figure out what the implication of resetting the variables does
|
||||
break;
|
||||
|
||||
case RESETOP_OO_TOPOS_UNKNOWN:
|
||||
// TODO: This is called for some scenes in OO-Topis. Figure out what it does
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint16 Pics::ImageFile::imageGetOperand(ImageContext *ctx) const {
|
||||
return ctx->_file.readByte();
|
||||
}
|
||||
|
||||
/*-------------------------------------------------------*/
|
||||
|
||||
Pics::Pics() : _font(nullptr) {
|
||||
if (Common::File::exists("charset.gda"))
|
||||
_font = new CharSet();
|
||||
else if (g_comprehend->getGameID() == "talisman")
|
||||
_font = new TalismanFont();
|
||||
}
|
||||
|
||||
Pics::~Pics() {
|
||||
delete _font;
|
||||
}
|
||||
|
||||
void Pics::clear() {
|
||||
_rooms.clear();
|
||||
_items.clear();
|
||||
}
|
||||
|
||||
void Pics::load(const Common::StringArray &roomFiles,
|
||||
const Common::StringArray &itemFiles,
|
||||
const Common::String &titleFile) {
|
||||
clear();
|
||||
|
||||
for (uint idx = 0; idx < roomFiles.size(); ++idx)
|
||||
_rooms.push_back(ImageFile(roomFiles[idx]));
|
||||
for (uint idx = 0; idx < itemFiles.size(); ++idx)
|
||||
_items.push_back(ImageFile(itemFiles[idx]));
|
||||
|
||||
if (!titleFile.empty())
|
||||
_title = ImageFile(titleFile, true);
|
||||
}
|
||||
|
||||
int Pics::getPictureNumber(const Common::String &filename) const {
|
||||
// Ensure prefix and suffix
|
||||
if (!filename.hasPrefixIgnoreCase("pic") ||
|
||||
!filename.hasSuffixIgnoreCase(".raw"))
|
||||
return -1;
|
||||
|
||||
// Get the number part
|
||||
Common::String num(filename.c_str() + 3, filename.size() - 7);
|
||||
if (num.empty() || !Common::isDigit(num[0]))
|
||||
return -1;
|
||||
|
||||
return atoi(num.c_str());
|
||||
}
|
||||
|
||||
bool Pics::hasFile(const Common::Path &path) const {
|
||||
Common::String name = path.baseName();
|
||||
int num = getPictureNumber(name);
|
||||
if (num == -1)
|
||||
return false;
|
||||
|
||||
if (num == DARK_ROOM || num == BRIGHT_ROOM || num == TITLE_IMAGE)
|
||||
return true;
|
||||
if (num >= ITEMS_OFFSET && num < (int)(ITEMS_OFFSET + _items.size() * IMAGES_PER_FILE))
|
||||
return true;
|
||||
if (num < ITEMS_OFFSET && (num % 100) < (int)(_rooms.size() * IMAGES_PER_FILE))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int Pics::listMembers(Common::ArchiveMemberList &list) const {
|
||||
return list.size();
|
||||
}
|
||||
|
||||
const Common::ArchiveMemberPtr Pics::getMember(const Common::Path &path) const {
|
||||
if (!hasFile(path))
|
||||
return Common::ArchiveMemberPtr();
|
||||
|
||||
return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(path, *this));
|
||||
}
|
||||
|
||||
Common::SeekableReadStream *Pics::createReadStreamForMember(const Common::Path &path) const {
|
||||
Common::String name = path.baseName();
|
||||
// Get the picture number
|
||||
int num = getPictureNumber(name);
|
||||
if (num == -1 || !hasFile(path))
|
||||
return nullptr;
|
||||
|
||||
// Draw the image
|
||||
drawPicture(num);
|
||||
|
||||
// Create a stream with the data for the surface
|
||||
Common::MemoryReadWriteStream *stream =
|
||||
new Common::MemoryReadWriteStream(DisposeAfterUse::YES);
|
||||
const DrawSurface &ds = *g_comprehend->_drawSurface;
|
||||
stream->writeUint16LE(ds.w);
|
||||
stream->writeUint16LE(ds.h);
|
||||
stream->writeUint16LE(0); // Palette size
|
||||
stream->write(ds.getPixels(), ds.w * ds.h * 4);
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
void Pics::drawPicture(int pictureNum) const {
|
||||
ImageContext ctx(g_comprehend->_drawSurface, _font, g_comprehend->_drawFlags, pictureNum);
|
||||
|
||||
if (pictureNum == DARK_ROOM) {
|
||||
ctx._drawSurface->clearScreen(G_COLOR_BLACK);
|
||||
|
||||
} else if (pictureNum == BRIGHT_ROOM) {
|
||||
ctx._drawSurface->clearScreen(G_COLOR_WHITE);
|
||||
|
||||
} else if (pictureNum == TITLE_IMAGE) {
|
||||
ctx._drawSurface->clearScreen(G_COLOR_WHITE);
|
||||
_title.draw(0, &ctx);
|
||||
|
||||
} else if (pictureNum >= ITEMS_OFFSET) {
|
||||
pictureNum -= ITEMS_OFFSET;
|
||||
ctx._drawSurface->clear(0);
|
||||
_items[pictureNum / IMAGES_PER_FILE].draw(
|
||||
pictureNum % IMAGES_PER_FILE, &ctx);
|
||||
|
||||
} else {
|
||||
if (pictureNum < LOCATIONS_NO_BG_OFFSET) {
|
||||
ctx._drawSurface->clearScreen((ctx._drawFlags & IMAGEF_REVERSE) ? G_COLOR_BLACK : G_COLOR_WHITE);
|
||||
if (ctx._drawFlags & IMAGEF_REVERSE)
|
||||
ctx._penColor = RGB(255, 255, 255);
|
||||
} else {
|
||||
ctx._drawSurface->clear(0);
|
||||
}
|
||||
pictureNum %= 100;
|
||||
_rooms[pictureNum / IMAGES_PER_FILE].draw(
|
||||
pictureNum % IMAGES_PER_FILE, &ctx);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Comprehend
|
||||
} // namespace Glk
|
||||
150
engines/glk/comprehend/pics.h
Normal file
150
engines/glk/comprehend/pics.h
Normal file
@@ -0,0 +1,150 @@
|
||||
/* 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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef GLK_COMPREHEND_PICS_H
|
||||
#define GLK_COMPREHEND_PICS_H
|
||||
|
||||
#include "glk/comprehend/draw_surface.h"
|
||||
#include "common/archive.h"
|
||||
#include "common/file.h"
|
||||
#include "common/str-array.h"
|
||||
#include "graphics/font.h"
|
||||
|
||||
namespace Glk {
|
||||
namespace Comprehend {
|
||||
|
||||
enum ImageFlag {
|
||||
IMAGEF_REVERSE = 1 << 0,
|
||||
IMAGEF_NO_PAINTING = 1 << 1,
|
||||
IMAGEF_NO_FILL = IMAGEF_REVERSE | IMAGEF_NO_PAINTING
|
||||
};
|
||||
|
||||
enum {
|
||||
LOCATIONS_OFFSET = 0,
|
||||
LOCATIONS_NO_BG_OFFSET = 100,
|
||||
ITEMS_OFFSET = 200,
|
||||
DARK_ROOM = 1000,
|
||||
BRIGHT_ROOM = 1001,
|
||||
TITLE_IMAGE = 9999
|
||||
};
|
||||
|
||||
class Pics : public Common::Archive {
|
||||
struct ImageContext {
|
||||
Common::File _file;
|
||||
uint _picIndex;
|
||||
DrawSurface *_drawSurface;
|
||||
Graphics::Font *_font;
|
||||
uint _drawFlags;
|
||||
|
||||
uint16 _x;
|
||||
uint16 _y;
|
||||
uint32 _penColor;
|
||||
uint32 _fillColor;
|
||||
Shape _shape;
|
||||
|
||||
uint16 _textX;
|
||||
uint16 _textY;
|
||||
|
||||
ImageContext(DrawSurface *drawSurface, Graphics::Font *font, uint flags, uint picIndex) :
|
||||
_drawSurface(drawSurface), _font(font), _drawFlags(flags), _picIndex(picIndex),
|
||||
_x(0), _y(0), _penColor(G_COLOR_BLACK), _fillColor(G_COLOR_BLACK),
|
||||
_shape(SHAPE_CIRCLE_LARGE), _textX(0), _textY(0) {
|
||||
}
|
||||
|
||||
uint32 getFillColor() const;
|
||||
void lineFixes();
|
||||
};
|
||||
|
||||
struct ImageFile {
|
||||
private:
|
||||
Common::Array<uint16> _imageOffsets;
|
||||
Common::Path _filename;
|
||||
|
||||
private:
|
||||
bool doImageOp(ImageContext *ctx) const;
|
||||
uint16 imageGetOperand(ImageContext *ctx) const;
|
||||
void doResetOp(ImageContext *ctx, byte param) const;
|
||||
public:
|
||||
ImageFile() {}
|
||||
ImageFile(const Common::String &filename, bool isSingleImage = false);
|
||||
|
||||
void draw(uint index, ImageContext *ctx) const;
|
||||
};
|
||||
|
||||
private:
|
||||
Common::Array<ImageFile> _rooms;
|
||||
Common::Array<ImageFile> _items;
|
||||
ImageFile _title;
|
||||
Graphics::Font *_font;
|
||||
|
||||
private:
|
||||
/**
|
||||
* Returns the image number if the passed filename is a picture
|
||||
*/
|
||||
int getPictureNumber(const Common::String &filename) const;
|
||||
|
||||
/**
|
||||
* Draw the specified picture
|
||||
*/
|
||||
void drawPicture(int pictureNum) const;
|
||||
|
||||
public:
|
||||
Pics();
|
||||
~Pics();
|
||||
|
||||
void clear();
|
||||
|
||||
void load(const Common::StringArray &roomFiles,
|
||||
const Common::StringArray &itemFiles,
|
||||
const Common::String &titleFile);
|
||||
|
||||
/**
|
||||
* Check if a member with the given name is present in the Archive.
|
||||
* Patterns are not allowed, as this is meant to be a quick File::exists()
|
||||
* replacement.
|
||||
*/
|
||||
bool hasFile(const Common::Path &path) const override;
|
||||
|
||||
/**
|
||||
* Add all members of the Archive to list.
|
||||
* Must only append to list, and not remove elements from it.
|
||||
*
|
||||
* @return the number of names added to list
|
||||
*/
|
||||
int listMembers(Common::ArchiveMemberList &list) const override;
|
||||
|
||||
/**
|
||||
* Returns a ArchiveMember representation of the given file.
|
||||
*/
|
||||
const Common::ArchiveMemberPtr getMember(const Common::Path &path) const override;
|
||||
|
||||
/**
|
||||
* Create a stream bound to a member with the specified name in the
|
||||
* archive. If no member with this name exists, 0 is returned.
|
||||
* @return the newly created input stream
|
||||
*/
|
||||
Common::SeekableReadStream *createReadStreamForMember(const Common::Path &path) const override;
|
||||
};
|
||||
|
||||
} // namespace Comprehend
|
||||
} // namespace Glk
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user