scummvm/engines/agi/agi.h
2024-04-15 18:23:47 -07:00

1040 lines
26 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#ifndef AGI_AGI_H
#define AGI_AGI_H
#include "common/scummsys.h"
#include "common/error.h"
#include "common/util.h"
#include "common/file.h"
#include "common/keyboard.h"
#include "common/rect.h"
#include "common/rendermode.h"
#include "common/stack.h"
#include "common/str.h"
#include "common/system.h"
#include "engines/engine.h"
#include "gui/debugger.h"
// AGI resources
#include "agi/console.h"
#include "agi/view.h"
#include "agi/picture.h"
#include "agi/logic.h"
#include "agi/sound.h"
namespace Common {
class RandomSource;
}
/**
* This is the namespace of the AGI engine.
*
* Status of this engine: ???
*
* Games using this engine:
* - Early Sierra adventure games
* - many fan made games
* - Mickey's Space Adventure (Pre-AGI)
* - Winnie the Pooh in the Hundred Acre Wood (Pre-AGI)
* - Troll's Tale (Pre-AGI)
*/
namespace Agi {
typedef signed int Err;
//
// Version and other definitions
//
#define TITLE "AGI engine"
#define DIR_ "dir"
#define LOGDIR "logdir"
#define PICDIR "picdir"
#define VIEWDIR "viewdir"
#define SNDDIR "snddir"
#define OBJECTS "object"
#define WORDS "words.tok"
#define MAX_DIRECTORY_ENTRIES 256
#define MAX_CONTROLLERS 256
#define MAX_VARS 256
#define MAX_FLAGS (256 >> 3)
#define SCREENOBJECTS_MAX 255 // KQ3 uses o255!
#define SCREENOBJECTS_EGO_ENTRY 0 // first entry is ego
#define MAX_WORDS 20
#define MAX_STRINGS 24 // MAX_STRINGS + 1 used for get.num
#define MAX_STRINGLEN 40
#define MAX_CONTROLLER_KEYMAPPINGS 39
#define SAVEDGAME_DESCRIPTION_LEN 30
#define _EMPTY 0xfffff
#define EGO_OWNED 0xff
#define EGO_OWNED_V1 0xf9
#define CRYPT_KEY_SIERRA "Avis Durgan"
#define CRYPT_KEY_AGDS "Alex Simkin"
#define ADD_PIC 1
#define ADD_VIEW 2
#define CMD_BSIZE 12
enum {
NO_GAMEDIR = 0,
GAMEDIR
};
enum AgiGameType {
GType_PreAGI = 0,
GType_V1 = 1,
GType_V2 = 2,
GType_V3 = 3
};
enum AgiGameFeatures {
GF_AGIMOUSE = (1 << 0), // marks games created with AGIMOUSE, disables "Click-to-walk mouse interface"
GF_AGDS = (1 << 1), // marks games created with AGDS - all using AGI version 2.440
GF_AGI256 = (1 << 2), // marks fanmade AGI-256 games
GF_FANMADE = (1 << 3), // marks fanmade games
GF_2GSOLDSOUND = (1 << 5),
GF_EXTCHAR = (1 << 6) // use WORDS.TOK.EXTENDED
};
enum BooterDisks {
BooterDisk1 = 0,
BooterDisk2 = 1
};
enum AgiGameID {
GID_AGIDEMO,
GID_BC,
GID_DDP,
GID_GOLDRUSH, // V3
GID_KQ1,
GID_KQ2,
GID_KQ3,
GID_KQ4,
GID_LSL1,
GID_MH1, // V3
GID_MH2, // V3
GID_MIXEDUP,
GID_PQ1,
GID_SQ1,
GID_SQ2,
GID_XMASCARD,
GID_FANMADE,
GID_GETOUTTASQ, // Fanmade
GID_MICKEY, // PreAGI
GID_WINNIE, // PreAGI
GID_TROLL // PreAGI
};
enum AGIErrors {
errOK = 0,
errDoNothing,
errBadCLISwitch,
errInvalidAGIFile,
errBadFileOpen,
errNotEnoughMemory,
errBadResource,
errUnknownAGIVersion,
errNoLoopsInView,
errViewDataError,
errNoGameList,
errIOError,
errUnk = 127
};
enum kDebugLevels {
kDebugLevelMain = 1 << 0,
kDebugLevelResources = 1 << 1,
kDebugLevelSprites = 1 << 2,
kDebugLevelInventory = 1 << 3,
kDebugLevelInput = 1 << 4,
kDebugLevelMenu = 1 << 5,
kDebugLevelScripts = 1 << 6,
kDebugLevelSound = 1 << 7,
kDebugLevelText = 1 << 8,
kDebugLevelSavegame = 1 << 9
};
/**
* AGI resources.
*/
enum {
RESOURCETYPE_LOGIC = 1,
RESOURCETYPE_SOUND,
RESOURCETYPE_VIEW,
RESOURCETYPE_PICTURE
};
enum {
RES_LOADED = 0x01,
RES_COMPRESSED = 0x40,
RES_PICTURE_V3_NIBBLE_PARM = 0x80 // Flag that gets set for picture resources,
// which use a nibble instead of a byte as F0+F2 parameters
};
enum {
lCOMMAND_MODE = 1,
lTEST_MODE
};
struct gameIdList {
gameIdList *next;
uint32 version;
uint32 crc;
char *gName;
char *switches;
};
struct Mouse {
int button;
Common::Point pos;
Mouse() : button(0) {}
};
// Used by AGI Mouse protocol 1.0 for v27 (i.e. button pressed -variable).
enum AgiMouseButton {
kAgiMouseButtonUp, // Mouse button is up (not pressed)
kAgiMouseButtonLeft, // Left mouse button
kAgiMouseButtonRight, // Right mouse button
kAgiMouseButtonMiddle // Middle mouse button
};
/**
* AGI variables.
*/
enum {
VM_VAR_CURRENT_ROOM = 0, // 0
VM_VAR_PREVIOUS_ROOM, // 1
VM_VAR_BORDER_TOUCH_EGO, // 2
VM_VAR_SCORE, // 3
VM_VAR_BORDER_CODE, // 4
VM_VAR_BORDER_TOUCH_OBJECT, // 5
VM_VAR_EGO_DIRECTION, // 6
VM_VAR_MAX_SCORE, // 7
VM_VAR_FREE_PAGES, // 8
VM_VAR_WORD_NOT_FOUND, // 9
VM_VAR_TIME_DELAY, // 10
VM_VAR_SECONDS, // 11
VM_VAR_MINUTES, // 12
VM_VAR_HOURS, // 13
VM_VAR_DAYS, // 14
VM_VAR_JOYSTICK_SENSITIVITY, // 15
VM_VAR_EGO_VIEW_RESOURCE, // 16
VM_VAR_AGI_ERROR_CODE, // 17
VM_VAR_AGI_ERROR_INFO, // 18
VM_VAR_KEY, // 19
VM_VAR_COMPUTER, // 20
VM_VAR_WINDOW_AUTO_CLOSE_TIMER, // 21
VM_VAR_SOUNDGENERATOR, // 22
VM_VAR_VOLUME, // 23
VM_VAR_MAX_INPUT_CHARACTERS, // 24
VM_VAR_SELECTED_INVENTORY_ITEM, // 25
VM_VAR_MONITOR = 26, // 26
VM_VAR_MOUSE_BUTTONSTATE = 27, // 27
VM_VAR_MOUSE_X = 28, // 28
VM_VAR_MOUSE_Y = 29 // 29
};
/**
* Different monitor types.
* Used with AGI variable 26 i.e. vMonitor.
*/
enum AgiMonitorType {
kAgiMonitorCga = 0,
//kAgiMonitorTandy = 1, // Not sure about this
kAgiMonitorHercules = 2,
kAgiMonitorEga = 3
//kAgiMonitorVga = 4 // Not sure about this
};
/**
* Different computer types.
* Used with AGI variable 20 i.e. vComputer.
*/
enum AgiComputerType {
kAgiComputerPC = 0,
kAgiComputerAtariST = 4,
kAgiComputerAmiga = 5,
kAgiComputerApple2GS = 7
};
enum AgiSoundType {
kAgiSoundPC = 1,
kAgiSoundTandy = 3, // Tandy (This value is also used by the Amiga AGI and Apple IIGS AGI)
kAgiSound2GSOld = 8 // Apple IIGS's Gold Rush! (Version 1.0M 1989-02-28 (CE), AGI 3.003) uses value 8
};
/**
* AGI flags
*/
enum {
VM_FLAG_EGO_WATER = 0, // 0
VM_FLAG_EGO_INVISIBLE,
VM_FLAG_ENTERED_CLI,
VM_FLAG_EGO_TOUCHED_P2,
VM_FLAG_SAID_ACCEPTED_INPUT,
VM_FLAG_NEW_ROOM_EXEC, // 5
VM_FLAG_RESTART_GAME,
VM_FLAG_SCRIPT_BLOCKED,
VM_FLAG_JOY_SENSITIVITY,
VM_FLAG_SOUND_ON,
VM_FLAG_DEBUGGER_ON, // 10
VM_FLAG_LOGIC_ZERO_FIRST_TIME,
VM_FLAG_RESTORE_JUST_RAN,
VM_FLAG_STATUS_SELECTS_ITEMS,
VM_FLAG_MENUS_ACCESSIBLE,
VM_FLAG_OUTPUT_MODE, // 15
VM_FLAG_AUTO_RESTART
};
struct AgiControllerKeyMapping {
uint16 keycode;
byte controllerSlot;
AgiControllerKeyMapping() : keycode(0), controllerSlot(0) {}
};
struct AgiObject {
int location;
Common::String name;
};
struct AgiDir {
uint8 volume;
uint32 offset;
uint32 len;
uint32 clen;
// 0 = not in mem, can be freed
// 1 = in mem, can be released
// 2 = not in mem, cant be released
// 3 = in mem, cant be released
// 0x40 = was compressed
uint8 flags;
void reset() {
volume = 0;
offset = 0;
len = 0;
clen = 0;
flags = 0;
}
AgiDir() { reset(); }
};
struct AgiBlock {
bool active;
int16 x1, y1;
int16 x2, y2;
AgiBlock() : active(false), x1(0), y1(0), x2(0), y2(0) {}
};
struct ScriptPos {
int script;
int curIP;
};
enum CycleInnerLoopType {
CYCLE_INNERLOOP_GETSTRING = 0,
CYCLE_INNERLOOP_GETNUMBER,
CYCLE_INNERLOOP_INVENTORY,
CYCLE_INNERLOOP_MENU_VIA_KEYBOARD,
CYCLE_INNERLOOP_MENU_VIA_MOUSE,
CYCLE_INNERLOOP_SYSTEMUI_SELECTSAVEDGAMESLOT,
CYCLE_INNERLOOP_SYSTEMUI_VERIFICATION,
CYCLE_INNERLOOP_MESSAGEBOX,
CYCLE_INNERLOOP_HAVEKEY
};
typedef Common::Array<int16> SavedGameSlotIdArray;
/**
* AGI game structure.
* This structure contains all global data of an AGI game executed
* by the interpreter.
*/
struct AgiGame {
AgiEngine *_vm;
// TODO: Check whether adjMouseX and adjMouseY must be saved and loaded when using savegames.
// If they must be then loading and saving is partially broken at the moment.
int adjMouseX; /**< last given adj.ego.move.to.x.y-command's 1st parameter */
int adjMouseY; /**< last given adj.ego.move.to.x.y-command's 2nd parameter */
char name[8]; /**< lead in id (e.g. `GR' for goldrush) */
char id[8]; /**< game id */
uint32 crc; /**< game CRC */
// game flags and variables
uint8 flags[MAX_FLAGS]; /**< 256 1-bit flags combined into a total of 32 bytes */
uint8 vars[MAX_VARS]; /**< 256 variables */
// internal variables
int16 horizon; /**< horizon y coordinate */
bool cycleInnerLoopActive;
int16 cycleInnerLoopType;
int16 curLogicNr; /**< current logic number */
Common::Array<ScriptPos> execStack;
// internal flags
bool playerControl; /**< player is in control */
bool exitAllLogics; /**< break cycle after new.room */
bool pictureShown; /**< show.pic has been issued */
// windows
AgiBlock block;
// graphics & text
bool gfxMode;
unsigned int numObjects;
bool controllerOccurred[MAX_CONTROLLERS]; /**< keyboard keypress events */
AgiControllerKeyMapping controllerKeyMapping[MAX_CONTROLLER_KEYMAPPINGS];
char strings[MAX_STRINGS + 1][MAX_STRINGLEN]; /**< strings */
// directory entries for resources
AgiDir dirLogic[MAX_DIRECTORY_ENTRIES];
AgiDir dirPic[MAX_DIRECTORY_ENTRIES];
AgiDir dirView[MAX_DIRECTORY_ENTRIES];
AgiDir dirSound[MAX_DIRECTORY_ENTRIES];
// resources
AgiPicture pictures[MAX_DIRECTORY_ENTRIES]; /**< AGI picture resources */
AgiLogic logics[MAX_DIRECTORY_ENTRIES]; /**< AGI logic resources */
AgiView views[MAX_DIRECTORY_ENTRIES]; /**< AGI view resources */
AgiSound *sounds[MAX_DIRECTORY_ENTRIES]; /**< Pointers to AGI sound resources */
AgiLogic *_curLogic;
// view table
ScreenObjEntry screenObjTable[SCREENOBJECTS_MAX];
ScreenObjEntry addToPicView;
bool automaticSave; /**< set by CmdSetSimple() */
char automaticSaveDescription[SAVEDGAME_DESCRIPTION_LEN + 1];
Common::Rect mouseFence; /**< rectangle set by fence.mouse command */
bool mouseEnabled; /**< if mouse is supposed to be active */
bool mouseHidden; /**< if mouse is currently hidden */
// IF condition handling
bool testResult;
int max_logics;
int logic_list[256];
// used to detect situations, where the game shows some text and changes rooms right afterwards
// for example Space Quest 2 intro right at the start
// or Space Quest 2, when entering the vent also right at the start
// The developers assumed that loading the new room would take a bit.
// In ScummVM it's basically done in an instant, which means that
// the text would only get shown for a split second.
// We delay a bit as soon as such situations get detected.
bool nonBlockingTextShown;
int16 nonBlockingTextCyclesLeft;
bool automaticRestoreGame;
uint16 appleIIgsSpeedControllerSlot;
int appleIIgsSpeedLevel;
const char *getString(int number);
void setString(int number, const char *str);
void setAppleIIgsSpeedLevel(int appleIIgsSpeedLevel);
AgiGame() {
_vm = nullptr;
adjMouseX = 0;
adjMouseY = 0;
memset(name, 0, sizeof(name));
memset(id, 0, sizeof(id));
crc = 0;
memset(flags, 0, sizeof(flags));
memset(vars, 0, sizeof(vars));
horizon = 0;
cycleInnerLoopActive = false;
cycleInnerLoopType = 0;
curLogicNr = 0;
// execStack is defaulted by Common::Array constructor
playerControl = false;
exitAllLogics = false;
pictureShown = false;
// block defaulted by AgiBlock constructor
gfxMode = false;
numObjects = 0;
memset(controllerOccurred, 0, sizeof(controllerOccurred));
// controllerKeyMapping defaulted by AgiControllerKeyMapping constructor
memset(strings, 0, sizeof(strings));
// dirLogic cleared by AgiDir constructor
// dirPic cleared by AgiDir constructor
// dirView cleared by AgiDir constructor
// dirSound cleared by AgiDir constructor
// pictures cleared by AgiPicture constructor
// logics cleared by AgiLogic constructor
// views cleared by AgiView constructor
memset(sounds, 0, sizeof(sounds));
_curLogic = nullptr;
// screenObjTable cleared by ScreenObjEntry constructor
// addToPicView cleared by ScreenObjEntry constructor
automaticSave = false;
memset(automaticSaveDescription, 0, sizeof(automaticSaveDescription));
// mouseFence cleared by Common::Rect constructor
mouseEnabled = false;
mouseHidden = false;
testResult = false;
max_logics = 0;
memset(logic_list, 0, sizeof(logic_list));
nonBlockingTextShown = false;
nonBlockingTextCyclesLeft = 0;
automaticRestoreGame = false;
appleIIgsSpeedControllerSlot = 0xffff; // we didn't add yet speed menu
appleIIgsSpeedLevel = 2; // normal speed
}
};
class AgiLoader {
public:
AgiLoader() {}
virtual ~AgiLoader() {}
virtual int init() = 0;
virtual int detectGame() = 0;
virtual int loadResource(int16 resourceType, int16 resourceNr) = 0;
virtual void unloadResource(int16 resourceType, int16 resourceNr) = 0;
virtual int loadObjects(const char *fname) = 0;
virtual int loadWords(const char *fname) = 0;
};
class AgiLoader_v1 : public AgiLoader {
private:
AgiEngine *_vm;
Common::Path _filenameDisk0;
Common::Path _filenameDisk1;
int loadDir_DDP(AgiDir *agid, int offset, int max);
int loadDir_BC(AgiDir *agid, int offset, int max);
uint8 *loadVolRes(AgiDir *agid);
public:
AgiLoader_v1(AgiEngine *vm);
int init() override;
int detectGame() override;
int loadResource(int16 resourceType, int16 resourceNr) override;
void unloadResource(int16 resourceType, int16 resourceNr) override;
int loadObjects(const char *fname) override;
int loadWords(const char *fname) override;
};
class AgiLoader_v2 : public AgiLoader {
private:
AgiEngine *_vm;
bool _hasV3VolumeFormat;
int loadDir(AgiDir *agid, const char *fname);
uint8 *loadVolRes(AgiDir *agid);
bool detectV3VolumeFormat();
public:
AgiLoader_v2(AgiEngine *vm) {
_vm = vm;
_hasV3VolumeFormat = false;
}
int init() override;
int detectGame() override;
int loadResource(int16 resourceType, int16 resourceNr) override;
void unloadResource(int16 resourceType, int16 resourceNr) override;
int loadObjects(const char *fname) override;
int loadWords(const char *fname) override;
};
class AgiLoader_v3 : public AgiLoader {
private:
AgiEngine *_vm;
int loadDir(AgiDir *agid, Common::File *fp, uint32 offs, uint32 len);
uint8 *loadVolRes(AgiDir *agid);
public:
AgiLoader_v3(AgiEngine *vm) {
_vm = vm;
}
int init() override;
int detectGame() override;
int loadResource(int16 resourceType, int16 resourceNr) override;
void unloadResource(int16 resourceType, int16 resourceNr) override;
int loadObjects(const char *fname) override;
int loadWords(const char *fname) override;
};
class GfxFont;
class GfxMgr;
class SpritesMgr;
class InventoryMgr;
class TextMgr;
class GfxMenu;
class SystemUI;
class Words;
struct AGIGameDescription;
// Image stack support
struct ImageStackElement {
uint8 type;
uint8 pad;
int16 parm1;
int16 parm2;
int16 parm3;
int16 parm4;
int16 parm5;
int16 parm6;
int16 parm7;
};
#define TICK_SECONDS 20
#define KEY_QUEUE_SIZE 16
class AgiBase : public ::Engine {
protected:
// Engine API
Common::Error init();
virtual Common::Error go() = 0;
Common::Error run() override {
Common::Error err;
err = init();
if (err.getCode() != Common::kNoError)
return err;
return go();
}
bool hasFeature(EngineFeature f) const override;
virtual void initialize() = 0;
void initRenderMode();
public:
Words *_words;
GfxFont *_font;
GfxMgr *_gfx;
Common::RenderMode _renderMode;
AgiDebug _debug;
AgiGame _game;
Common::RandomSource *_rnd;
SoundMgr *_sound;
Mouse _mouse;
bool _noSaveLoadAllowed;
virtual bool promptIsEnabled() {
return false;
}
virtual int getKeypress() = 0;
virtual bool isKeypress() = 0;
virtual void clearKeyQueue() = 0;
AgiBase(OSystem *syst, const AGIGameDescription *gameDesc);
~AgiBase() override;
virtual void clearImageStack() = 0;
virtual void recordImageStackCall(uint8 type, int16 p1, int16 p2, int16 p3,
int16 p4, int16 p5, int16 p6, int16 p7) = 0;
virtual void replayImageStackCall(uint8 type, int16 p1, int16 p2, int16 p3,
int16 p4, int16 p5, int16 p6, int16 p7) = 0;
virtual void releaseImageStack() = 0;
int _soundemu;
bool getFlag(int16 flagNr);
void setFlag(int16 flagNr, bool newState);
void flipFlag(int16 flagNr);
const AGIGameDescription *_gameDescription;
uint32 _gameFeatures;
uint16 _gameVersion;
uint32 getGameID() const;
uint32 getFeatures() const;
uint16 getVersion() const;
uint16 getGameType() const;
Common::Language getLanguage() const;
bool isLanguageRTL() const;
Common::Platform getPlatform() const;
const char *getGameMD5() const;
void initFeatures();
void initVersion();
const char *getDiskName(uint16 id);
bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override;
bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override;
const byte *getFontData();
void cycleInnerLoopActive(int16 loopType) {
_game.cycleInnerLoopActive = true;
_game.cycleInnerLoopType = loopType;
};
void cycleInnerLoopInactive() {
_game.cycleInnerLoopActive = false;
};
bool cycleInnerLoopIsActive() {
return _game.cycleInnerLoopActive;
}
};
enum AgiArtificialDelayTriggerType {
ARTIFICIALDELAYTYPE_NEWROOM = 0,
ARTIFICIALDELAYTYPE_NEWPICTURE = 1,
ARTIFICIALDELAYTYPE_END = -1
};
struct AgiArtificialDelayEntry {
uint32 gameId;
Common::Platform platform;
AgiArtificialDelayTriggerType triggerType;
int16 orgNr;
int16 newNr;
uint16 millisecondsDelay;
};
typedef void (*AgiOpCodeFunction)(AgiGame *state, AgiEngine *vm, uint8 *p);
struct AgiOpCodeEntry {
const char *name;
const char *parameters;
AgiOpCodeFunction functionPtr;
uint16 parameterSize;
};
struct AgiOpCodeDefinitionEntry {
const char *name;
const char *parameters;
AgiOpCodeFunction functionPtr;
};
class AgiEngine : public AgiBase {
protected:
// Engine APIs
Common::Error go() override;
void initialize() override;
public:
AgiEngine(OSystem *syst, const AGIGameDescription *gameDesc);
~AgiEngine() override;
bool promptIsEnabled() override;
Common::Error loadGameState(int slot) override;
Common::Error saveGameState(int slot, const Common::String &description, bool isAutosave = false) override;
private:
int _keyQueue[KEY_QUEUE_SIZE];
int _keyQueueStart;
int _keyQueueEnd;
bool _allowSynthetic;
bool checkPriority(ScreenObjEntry *v);
bool checkCollision(ScreenObjEntry *v);
bool checkPosition(ScreenObjEntry *v);
int _firstSlot;
public:
Common::Array<AgiObject> _objects; // objects in the game
SavedGameSlotIdArray getSavegameSlotIds();
bool getSavegameInformation(int16 slotId, Common::String &saveDescription, uint32 &saveDate, uint32 &saveTime, bool &saveIsValid);
int saveGame(const Common::String &fileName, const Common::String &descriptionString);
int loadGame(const Common::String &fileName, bool checkId = true);
bool saveGameDialog();
bool saveGameAutomatic();
bool loadGameDialog();
bool loadGameAutomatic();
int doSave(int slot, const Common::String &desc);
int doLoad(int slot, bool showMessages);
int scummVMSaveLoadDialog(bool isSave);
uint8 *_intobj;
bool _restartGame;
SpritesMgr *_sprites;
TextMgr *_text;
InventoryMgr *_inventory;
PictureMgr *_picture;
AgiLoader *_loader;
GfxMenu *_menu;
SystemUI *_systemUI;
Common::DumpFile *_logFile; // File used for the log() agi command.
Common::Stack<ImageStackElement> _imageStack;
void clearImageStack() override;
void recordImageStackCall(uint8 type, int16 p1, int16 p2, int16 p3,
int16 p4, int16 p5, int16 p6, int16 p7) override;
void replayImageStackCall(uint8 type, int16 p1, int16 p2, int16 p3,
int16 p4, int16 p5, int16 p6, int16 p7) override;
void releaseImageStack() override;
void wait(uint32 msec, bool busy = false);
int agiInit();
void agiDeinit();
int agiLoadResource(int16 resourceType, int16 resourceNr);
void agiUnloadResource(int16 resourceType, int16 resourceNr);
void agiUnloadResources();
int getKeypress() override;
bool isKeypress() override;
void clearKeyQueue() override;
byte getVar(int16 varNr);
void setVar(int16 varNr, byte newValue);
private:
void applyVolumeToMixer();
public:
void syncSoundSettings() override;
public:
void decrypt(uint8 *mem, int len);
uint16 processAGIEvents();
int runGame();
void newRoom(int16 newRoomNr);
void resetControllers();
void interpretCycle();
int playGame();
void allowSynthetic(bool);
void processScummVMEvents();
void checkQuickLoad();
const Common::String getTargetName() const { return _targetName; }
// Objects
public:
int loadObjects(const char *fname);
int loadObjects(Common::File &fp);
const char *objectName(uint16 objectNr);
int objectGetLocation(uint16 objectNr);
void objectSetLocation(uint16 objectNr, int location);
private:
int decodeObjects(uint8 *mem, uint32 flen);
int readObjects(Common::File &fp, int flen);
// Logic
public:
int decodeLogic(int16 logicNr);
void unloadLogic(int16 logicNr);
int runLogic(int16 logicNr);
void debugConsole(int lognum, int mode, const char *str);
bool testIfCode(int16 logicNr);
void executeAgiCommand(uint8 op, uint8 *p);
private:
bool _veryFirstInitialCycle; /**< signals, that currently the very first cycle is executed (restarts, etc. do not count!) */
uint32 _instructionCounter; /**< counts every instruction, that got executed, can wrap around */
bool _setVolumeBrokenFangame;
void resetGetVarSecondsHeuristic();
void getVarSecondsHeuristicTrigger();
uint32 _getVarSecondsHeuristicLastInstructionCounter; /**< last time VM_VAR_SECONDS were read */
uint16 _getVarSecondsHeuristicCounter; /**< how many times heuristic was triggered */
uint32 _playTimeInSecondsAdjust; /**< milliseconds to adjust for calculating current play time in seconds, see setVarSecondsTrigger() */
void setVarSecondsTrigger(byte newSeconds);
public:
// Some submethods of testIfCode
void skipInstruction(byte op);
void skipInstructionsUntil(byte v);
bool testObjRight(uint8 n, uint8 x1, uint8 y1, uint8 x2, uint8 y2);
bool testObjCenter(uint8 n, uint8 x1, uint8 y1, uint8 x2, uint8 y2);
bool testObjInBox(uint8 n, uint8 x1, uint8 y1, uint8 x2, uint8 y2);
bool testPosn(uint8 n, uint8 x1, uint8 y1, uint8 x2, uint8 y2);
bool testSaid(uint8 nwords, uint8 *cc);
bool testController(uint8 cont);
bool testCompareStrings(uint8 s1, uint8 s2);
// View
private:
void updateView(ScreenObjEntry *screenObj);
public:
void setView(ScreenObjEntry *screenObj, int16 viewNr);
void setLoop(ScreenObjEntry *screenObj, int16 loopNr);
void setCel(ScreenObjEntry *screenObj, int16 celNr);
void clipViewCoordinates(ScreenObjEntry *screenObj);
void startUpdate(ScreenObjEntry *viewPtr);
void stopUpdate(ScreenObjEntry *viewPtr);
void updateScreenObjTable();
void unloadView(int16 viewNr);
int decodeView(byte *resourceData, uint16 resourceSize, int16 viewNr);
private:
void unpackViewCelData(AgiViewCel *celData, byte *compressedData, uint16 compressedSize);
void unpackViewCelDataAGI256(AgiViewCel *celData, byte *compressedData, uint16 compressedSize);
public:
bool isEgoView(const ScreenObjEntry *screenObj);
// Motion
private:
int checkStep(int delta, int step);
bool checkBlock(int16 x, int16 y);
void changePos(ScreenObjEntry *screenObj);
void motionWander(ScreenObjEntry *screenObj);
void motionFollowEgo(ScreenObjEntry *screenObj);
void motionMoveObj(ScreenObjEntry *screenObj);
void motionMoveObjStop(ScreenObjEntry *screenObj);
void checkMotion(ScreenObjEntry *screenObj);
public:
void motionActivated(ScreenObjEntry *screenObj);
void cyclerActivated(ScreenObjEntry *screenObj);
void checkAllMotions();
void moveObj(ScreenObjEntry *screenObj);
void inDestination(ScreenObjEntry *screenObj);
void fixPosition(int16 screenObjNr);
void fixPosition(ScreenObjEntry *screenObj);
void updatePosition();
int getDirection(int16 objX, int16 objY, int16 destX, int16 destY, int16 stepSize);
bool _keyHoldMode;
Common::KeyCode _keyHoldModeLastKey;
// Keyboard
int doPollKeyboard();
bool handleMouseClicks(uint16 &key);
bool handleController(uint16 key);
bool showPredictiveDialog();
int waitKey();
int waitAnyKey();
void nonBlockingText_IsShown();
void nonBlockingText_Forget();
void artificialDelay_Reset();
void artificialDelay_CycleDone();
uint16 artificialDelay_SearchTable(AgiArtificialDelayTriggerType triggerType, int16 orgNr, int16 newNr);
void artificialDelayTrigger_NewRoom(int16 newRoomNr);
void artificialDelayTrigger_DrawPicture(int16 newPictureNr);
private:
int16 _artificialDelayCurrentRoom;
int16 _artificialDelayCurrentPicture;
public:
void redrawScreen();
void inGameTimerReset(uint32 newPlayTime = 0);
void inGameTimerResetPassedCycles();
uint32 inGameTimerGet();
uint32 inGameTimerGetPassedCycles();
void inGameTimerUpdate();
private:
uint32 _lastUsedPlayTimeInCycles; // 40 per second
uint32 _lastUsedPlayTimeInSeconds; // actual seconds
uint32 _passedPlayTimeCycles; // increased by 1 every time we passed a cycle
private:
AgiOpCodeEntry _opCodes[256]; // always keep those at 256, so that there is no way for invalid memory access
AgiOpCodeEntry _opCodesCond[256];
void setupOpCodes(uint16 version);
public:
const AgiOpCodeEntry *getOpCodesTable() { return _opCodes; }
};
} // End of namespace Agi
#endif /* AGI_AGI_H */