mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-28 20:55:19 +00:00
1118 lines
27 KiB
C++
1118 lines
27 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 2
|
|
* of the License, or (at your option) any later version.
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
*/
|
|
|
|
#ifndef AGI_H
|
|
#define AGI_H
|
|
|
|
#include "common/scummsys.h"
|
|
#include "common/error.h"
|
|
#include "common/util.h"
|
|
#include "common/file.h"
|
|
#include "common/rect.h"
|
|
#include "common/stack.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_DIRS 256
|
|
#define MAX_VARS 256
|
|
#define MAX_FLAGS (256 >> 3)
|
|
#define MAX_VIEWTABLE 255 // KQ3 uses o255!
|
|
#define MAX_WORDS 20
|
|
#define MAX_STRINGS 24 // MAX_STRINGS + 1 used for get.num
|
|
#define MAX_STRINGLEN 40
|
|
#define MAX_CONTROLLERS 39
|
|
|
|
#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 MSG_BOX_COLOR 0x0f // White
|
|
#define MSG_BOX_TEXT 0x00 // Black
|
|
#define MSG_BOX_LINE 0x04 // Red
|
|
#define BUTTON_BORDER 0x00 // Black
|
|
#define STATUS_FG 0x00 // Black
|
|
#define STATUS_BG 0x0f // White
|
|
|
|
#define ADD_PIC 1
|
|
#define ADD_VIEW 2
|
|
|
|
#define CMD_BSIZE 12
|
|
|
|
enum AgiGameID {
|
|
GID_AGIDEMO,
|
|
GID_BC,
|
|
GID_DDP,
|
|
GID_GOLDRUSH,
|
|
GID_KQ1,
|
|
GID_KQ2,
|
|
GID_KQ3,
|
|
GID_KQ4,
|
|
GID_LSL1,
|
|
GID_MH1,
|
|
GID_MH2,
|
|
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 AgiGameType {
|
|
GType_PreAGI = 0,
|
|
GType_V1 = 1,
|
|
GType_V2 = 2,
|
|
GType_V3 = 3
|
|
};
|
|
|
|
enum BooterDisks {
|
|
BooterDisk1 = 0,
|
|
BooterDisk2 = 1
|
|
};
|
|
|
|
//
|
|
// GF_OLDAMIGAV20 means that the interpreter is an old Amiga AGI interpreter that
|
|
// uses value 20 for the computer type (v20 i.e. vComputer) rather than the usual value 5.
|
|
//
|
|
// GF_CLIPCOORDS means that views' coordinates must be clipped at least in commands
|
|
// position and position.v.
|
|
//
|
|
enum AgiGameFeatures {
|
|
GF_AGIMOUSE = (1 << 0),
|
|
GF_AGDS = (1 << 1),
|
|
GF_AGI256 = (1 << 2),
|
|
GF_AGI256_2 = (1 << 3),
|
|
GF_AGIPAL = (1 << 4),
|
|
GF_MACGOLDRUSH = (1 << 5),
|
|
GF_FANMADE = (1 << 6),
|
|
GF_MENUS = (1 << 7),
|
|
GF_ESCPAUSE = (1 << 8),
|
|
GF_OLDAMIGAV20 = (1 << 9),
|
|
GF_CLIPCOORDS = (1 << 10),
|
|
GF_2GSOLDSOUND = (1 << 11)
|
|
};
|
|
|
|
struct AGIGameDescription;
|
|
|
|
enum {
|
|
NO_GAMEDIR = 0,
|
|
GAMEDIR
|
|
};
|
|
|
|
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 {
|
|
rLOGIC = 1,
|
|
rSOUND,
|
|
rVIEW,
|
|
rPICTURE
|
|
};
|
|
|
|
enum {
|
|
RES_LOADED = 1,
|
|
RES_COMPRESSED = 0x40
|
|
};
|
|
|
|
enum {
|
|
lCOMMAND_MODE = 1,
|
|
lTEST_MODE
|
|
};
|
|
|
|
struct gameIdList {
|
|
gameIdList *next;
|
|
uint32 version;
|
|
uint32 crc;
|
|
char *gName;
|
|
char *switches;
|
|
};
|
|
|
|
struct Mouse {
|
|
int button;
|
|
int x;
|
|
int y;
|
|
};
|
|
|
|
// 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
|
|
};
|
|
|
|
enum GameId {
|
|
GID_AGI = 1
|
|
};
|
|
|
|
#define WIN_TO_PIC_X(x) ((x) / 2)
|
|
#define WIN_TO_PIC_Y(y) ((y) < 8 ? 999 : (y) >= (8 + _HEIGHT) ? 999 : (y) - 8)
|
|
|
|
/**
|
|
* AGI variables.
|
|
*/
|
|
enum {
|
|
vCurRoom = 0, // 0
|
|
vPrevRoom,
|
|
vBorderTouchEgo,
|
|
vScore,
|
|
vBorderCode,
|
|
vBorderTouchObj, // 5
|
|
vEgoDir,
|
|
vMaxScore,
|
|
vFreePages,
|
|
vWordNotFound,
|
|
vTimeDelay, // 10
|
|
vSeconds,
|
|
vMinutes,
|
|
vHours,
|
|
vDays,
|
|
vJoystickSensitivity, // 15
|
|
vEgoViewResource,
|
|
vAgiErrCode,
|
|
vAgiErrCodeInfo,
|
|
vKey,
|
|
vComputer, // 20
|
|
vWindowReset,
|
|
vSoundgen,
|
|
vVolume,
|
|
vMaxInputChars,
|
|
vSelItem, // 25
|
|
vMonitor
|
|
};
|
|
|
|
/**
|
|
* 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.
|
|
*
|
|
* At least these Amiga AGI versions use value 5:
|
|
* 2.082 (King's Quest I v1.0U 1986)
|
|
* 2.090 (King's Quest III v1.01 1986-11-08)
|
|
* x.yyy (Black Cauldron v2.00 1987-06-14)
|
|
* x.yyy (Larry I v1.05 1987-06-26)
|
|
* 2.107 (King's Quest II v2.0J. Date is probably 1987-01-29)
|
|
* 2.202 (Space Quest II v2.0F)
|
|
* 2.310 (Police Quest I v2.0B 1989-02-22)
|
|
* 2.316 (Gold Rush! v2.05 1989-03-09)
|
|
* 2.333 (King's Quest III v2.15 1989-11-15)
|
|
*
|
|
* At least these Amiga AGI versions use value 20:
|
|
* 2.082 (Space Quest I v1.2 1986)
|
|
* x.yyy (Manhunter NY 1.06 3/18/89)
|
|
* 2.333 (Manhunter SF 3.06 8/17/89)
|
|
*
|
|
*/
|
|
enum AgiComputerType {
|
|
kAgiComputerPC = 0,
|
|
kAgiComputerAtariST = 4,
|
|
kAgiComputerAmiga = 5, // Newer Amiga AGI interpreters' value (Commonly used)
|
|
kAgiComputerApple2GS = 7,
|
|
kAgiComputerAmigaOld = 20 // Older Amiga AGI interpreters' value (Seldom used)
|
|
};
|
|
|
|
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 {
|
|
fEgoWater = 0, // 0
|
|
fEgoInvisible,
|
|
fEnteredCli,
|
|
fEgoTouchedP2,
|
|
fSaidAcceptedInput,
|
|
fNewRoomExec, // 5
|
|
fRestartGame,
|
|
fScriptBlocked,
|
|
fJoySensitivity,
|
|
fSoundOn,
|
|
fDebuggerOn, // 10
|
|
fLogicZeroFirsttime,
|
|
fRestoreJustRan,
|
|
fStatusSelectsItems,
|
|
fMenusWork,
|
|
fOutputMode, // 15
|
|
fAutoRestart
|
|
};
|
|
|
|
enum AgiSlowliness {
|
|
kPauseRoom = 1500,
|
|
kPausePicture = 500
|
|
};
|
|
|
|
struct AgiController {
|
|
uint16 keycode;
|
|
uint8 controller;
|
|
};
|
|
|
|
struct AgiObject {
|
|
int location;
|
|
char *name;
|
|
};
|
|
|
|
struct AgiWord {
|
|
int id;
|
|
char *word;
|
|
};
|
|
|
|
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;
|
|
};
|
|
|
|
struct AgiBlock {
|
|
int active;
|
|
int x1, y1;
|
|
int x2, y2;
|
|
uint8 *buffer; // used for window background
|
|
};
|
|
|
|
/** AGI text color (Background and foreground color). */
|
|
struct AgiTextColor {
|
|
/** Creates an AGI text color. Uses black text on white background by default. */
|
|
AgiTextColor(int fgColor = 0x00, int bgColor = 0x0F) : fg(fgColor), bg(bgColor) {}
|
|
|
|
/** Get an AGI text color with swapped foreground and background color. */
|
|
AgiTextColor swap() const { return AgiTextColor(bg, fg); }
|
|
|
|
int fg; ///< Foreground color (Used for text).
|
|
int bg; ///< Background color (Used for text's background).
|
|
};
|
|
|
|
/**
|
|
* AGI button style (Amiga or PC).
|
|
*
|
|
* Supports positive and negative button types (Used with Amiga-style only):
|
|
* Positive buttons do what the dialog was opened for.
|
|
* Negative buttons cancel what the dialog was opened for.
|
|
* Restart-dialog example: Restart-button is positive, Cancel-button negative.
|
|
* Paused-dialog example: Continue-button is positive.
|
|
*/
|
|
struct AgiButtonStyle {
|
|
// Public constants etc
|
|
public:
|
|
static const int
|
|
// Amiga colors (Indexes into the Amiga-ish palette)
|
|
amigaBlack = 0x00, ///< Accurate, is #000000 (24-bit RGB)
|
|
amigaWhite = 0x0F, ///< Practically accurate, is close to #FFFFFF (24-bit RGB)
|
|
amigaGreen = 0x02, ///< Quite accurate, should be #008A00 (24-bit RGB)
|
|
amigaOrange = 0x0C, ///< Inaccurate, too much blue, should be #FF7500 (24-bit RGB)
|
|
amigaPurple = 0x0D, ///< Inaccurate, too much green, should be #FF00FF (24-bit RGB)
|
|
amigaRed = 0x04, ///< Quite accurate, should be #BD0000 (24-bit RGB)
|
|
amigaCyan = 0x0B, ///< Inaccurate, too much red, should be #00FFDE (24-bit RGB)
|
|
// PC colors (Indexes into the EGA-palette)
|
|
pcBlack = 0x00,
|
|
pcWhite = 0x0F;
|
|
|
|
// Public methods
|
|
public:
|
|
/**
|
|
* Get the color of the button with the given state and type using current style.
|
|
*
|
|
* @param hasFocus True if button has focus, false otherwise.
|
|
* @param pressed True if button is being pressed, false otherwise.
|
|
* @param positive True if button is positive, false if button is negative. Only matters for Amiga-style buttons.
|
|
*/
|
|
AgiTextColor getColor(bool hasFocus, bool pressed, bool positive = true) const;
|
|
|
|
/**
|
|
* Get the color of a button with the given base color and state ignoring current style.
|
|
* Swaps foreground and background color when the button has focus or is being pressed.
|
|
*
|
|
* @param hasFocus True if button has focus, false otherwise.
|
|
* @param pressed True if button is being pressed, false otherwise.
|
|
* @param baseFgColor Foreground color of the button when it has no focus and is not being pressed.
|
|
* @param baseBgColor Background color of the button when it has no focus and is not being pressed.
|
|
*/
|
|
AgiTextColor getColor(bool hasFocus, bool pressed, int baseFgColor, int baseBgColor) const;
|
|
|
|
/**
|
|
* Get the color of a button with the given base color and state ignoring current style.
|
|
* Swaps foreground and background color when the button has focus or is being pressed.
|
|
*
|
|
* @param hasFocus True if button has focus, false otherwise.
|
|
* @param pressed True if button is being pressed, false otherwise.
|
|
* @param baseColor Color of the button when it has no focus and is not being pressed.
|
|
*/
|
|
AgiTextColor getColor(bool hasFocus, bool pressed, const AgiTextColor &baseColor) const;
|
|
|
|
/**
|
|
* How many pixels to offset the shown text diagonally down and to the right.
|
|
* Currently only used for pressed PC-style buttons.
|
|
*/
|
|
int getTextOffset(bool hasFocus, bool pressed) const;
|
|
|
|
/**
|
|
* Show border around the button?
|
|
* Currently border is only used for in focus or pressed Amiga-style buttons
|
|
* when in inauthentic Amiga-style mode.
|
|
*/
|
|
bool getBorder(bool hasFocus, bool pressed) const;
|
|
|
|
/**
|
|
* Set Amiga-button style.
|
|
*
|
|
* @param amigaStyle Set Amiga-button style if true, otherwise set PC-button style.
|
|
* @param olderAgi If true then use older AGI style in Amiga-mode, otherwise use newer.
|
|
* @param authenticAmiga If true then don't use a border around buttons in Amiga-mode, otherwise use.
|
|
*/
|
|
void setAmigaStyle(bool amigaStyle = true, bool olderAgi = false, bool authenticAmiga = false);
|
|
|
|
/**
|
|
* Set PC-button style.
|
|
* @param pcStyle Set PC-button style if true, otherwise set default Amiga-button style.
|
|
*/
|
|
void setPcStyle(bool pcStyle = true);
|
|
|
|
// Public constructors
|
|
public:
|
|
/**
|
|
* Create a button style based on the given rendering mode.
|
|
* @param renderMode If Common::kRenderAmiga then creates default Amiga-button style, otherwise PC-style.
|
|
*/
|
|
AgiButtonStyle(Common::RenderMode renderMode = Common::kRenderDefault);
|
|
|
|
// Private member variables
|
|
private:
|
|
bool _amigaStyle; ///< Use Amiga-style buttons if true, otherwise use PC-style buttons.
|
|
bool _olderAgi; ///< Use older AGI style in Amiga-style mode.
|
|
bool _authenticAmiga; ///< Don't use border around buttons in Amiga-style mode.
|
|
};
|
|
|
|
struct ScriptPos {
|
|
int script;
|
|
int curIP;
|
|
};
|
|
|
|
enum {
|
|
EGO_VIEW_TABLE = 0,
|
|
HORIZON = 36,
|
|
_WIDTH = 160,
|
|
_HEIGHT = 168
|
|
};
|
|
|
|
enum InputMode {
|
|
INPUT_NORMAL = 0x01,
|
|
INPUT_GETSTRING = 0x02,
|
|
INPUT_MENU = 0x03,
|
|
INPUT_NONE = 0x04
|
|
};
|
|
|
|
enum State {
|
|
STATE_INIT = 0x00,
|
|
STATE_LOADED = 0x01,
|
|
STATE_RUNNING = 0x02
|
|
};
|
|
|
|
enum {
|
|
SBUF16_OFFSET = 0,
|
|
SBUF256_OFFSET = ((_WIDTH) * (_HEIGHT)),
|
|
FROM_SBUF16_TO_SBUF256_OFFSET = ((SBUF256_OFFSET) - (SBUF16_OFFSET)),
|
|
FROM_SBUF256_TO_SBUF16_OFFSET = ((SBUF16_OFFSET) - (SBUF256_OFFSET))
|
|
};
|
|
|
|
/**
|
|
* AGI game structure.
|
|
* This structure contains all global data of an AGI game executed
|
|
* by the interpreter.
|
|
*/
|
|
struct AgiGame {
|
|
AgiEngine *_vm;
|
|
|
|
State state; /**< state of the interpreter */
|
|
|
|
// 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 */
|
|
uint8 vars[MAX_VARS]; /**< 256 variables */
|
|
|
|
// internal variables
|
|
int horizon; /**< horizon y coordinate */
|
|
int lineStatus; /**< line number to put status on */
|
|
int lineUserInput; /**< line to put user input on */
|
|
int lineMinPrint; /**< num lines to print on */
|
|
int cursorPos; /**< column where the input cursor is */
|
|
byte inputBuffer[40]; /**< buffer for user input */
|
|
byte echoBuffer[40]; /**< buffer for echo.line */
|
|
int keypress;
|
|
|
|
InputMode inputMode; /**< keyboard input mode */
|
|
bool inputEnabled; /**< keyboard input enabled */
|
|
int lognum; /**< current logic number */
|
|
Common::Array<ScriptPos> execStack;
|
|
|
|
// internal flags
|
|
int playerControl; /**< player is in control */
|
|
int statusLine; /**< status line on/off */
|
|
int clockEnabled; /**< clock is on/off */
|
|
int exitAllLogics; /**< break cycle after new.room */
|
|
int pictureShown; /**< show.pic has been issued */
|
|
int hasPrompt; /**< input prompt has been printed */
|
|
#define ID_AGDS 0x00000001
|
|
#define ID_AMIGA 0x00000002
|
|
int gameFlags; /**< agi options flags */
|
|
|
|
uint8 priTable[_HEIGHT];/**< priority table */
|
|
|
|
// windows
|
|
uint32 msgBoxTicks; /**< timed message box tick counter */
|
|
AgiBlock block;
|
|
AgiBlock window;
|
|
int hasWindow;
|
|
|
|
// graphics & text
|
|
int gfxMode;
|
|
char cursorChar;
|
|
unsigned int colorFg;
|
|
unsigned int colorBg;
|
|
|
|
uint8 *sbufOrig; /**< Pointer to the 160x336 AGI screen buffer that contains vertically two 160x168 screens (16 color and 256 color). */
|
|
uint8 *sbuf16c; /**< 160x168 16 color (+control line & priority information) AGI screen buffer. Points at sbufOrig + SBUF16_OFFSET. */
|
|
uint8 *sbuf256c; /**< 160x168 256 color AGI screen buffer (For AGI256 and AGI256-2 support). Points at sbufOrig + SBUF256_OFFSET. */
|
|
uint8 *sbuf; /**< Currently chosen AGI screen buffer (sbuf256c if AGI256 or AGI256-2 is used, otherwise sbuf16c). */
|
|
|
|
// player command line
|
|
AgiWord egoWords[MAX_WORDS];
|
|
int numEgoWords;
|
|
|
|
unsigned int numObjects;
|
|
|
|
bool controllerOccured[MAX_DIRS]; /**< keyboard keypress events */
|
|
AgiController controllers[MAX_CONTROLLERS];
|
|
|
|
char strings[MAX_STRINGS + 1][MAX_STRINGLEN]; /**< strings */
|
|
|
|
// directory entries for resources
|
|
AgiDir dirLogic[MAX_DIRS];
|
|
AgiDir dirPic[MAX_DIRS];
|
|
AgiDir dirView[MAX_DIRS];
|
|
AgiDir dirSound[MAX_DIRS];
|
|
|
|
// resources
|
|
AgiPicture pictures[MAX_DIRS]; /**< AGI picture resources */
|
|
AgiLogic logics[MAX_DIRS]; /**< AGI logic resources */
|
|
AgiView views[MAX_DIRS]; /**< AGI view resources */
|
|
AgiSound *sounds[MAX_DIRS]; /**< Pointers to AGI sound resources */
|
|
|
|
AgiLogic *_curLogic;
|
|
|
|
// words
|
|
Common::Array<AgiWord*> words[26];
|
|
|
|
// view table
|
|
VtEntry viewTable[MAX_VIEWTABLE];
|
|
|
|
int32 ver; /**< detected game version */
|
|
|
|
int simpleSave; /**< select simple savegames */
|
|
|
|
Common::Rect mouseFence; /**< rectangle set by fence.mouse command */
|
|
|
|
// IF condition handling
|
|
int testResult;
|
|
|
|
|
|
int max_logics;
|
|
int logic_list[256];
|
|
};
|
|
|
|
class AgiLoader {
|
|
public:
|
|
|
|
AgiLoader() {}
|
|
virtual ~AgiLoader() {}
|
|
|
|
virtual int init() = 0;
|
|
virtual int deinit() = 0;
|
|
virtual int detectGame() = 0;
|
|
virtual int loadResource(int, int) = 0;
|
|
virtual int unloadResource(int, int) = 0;
|
|
virtual int loadObjects(const char *) = 0;
|
|
virtual int loadWords(const char *) = 0;
|
|
};
|
|
|
|
class AgiLoader_v1 : public AgiLoader {
|
|
private:
|
|
AgiEngine *_vm;
|
|
Common::String _filenameDisk0;
|
|
Common::String _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);
|
|
|
|
virtual int init();
|
|
virtual int deinit();
|
|
virtual int detectGame();
|
|
virtual int loadResource(int, int);
|
|
virtual int unloadResource(int, int);
|
|
virtual int loadObjects(const char *);
|
|
virtual int loadWords(const char *);
|
|
};
|
|
|
|
class AgiLoader_v2 : public AgiLoader {
|
|
private:
|
|
AgiEngine *_vm;
|
|
|
|
int loadDir(AgiDir *agid, const char *fname);
|
|
uint8 *loadVolRes(AgiDir *agid);
|
|
|
|
public:
|
|
|
|
AgiLoader_v2(AgiEngine *vm) {
|
|
_vm = vm;
|
|
}
|
|
|
|
virtual int init();
|
|
virtual int deinit();
|
|
virtual int detectGame();
|
|
virtual int loadResource(int, int);
|
|
virtual int unloadResource(int, int);
|
|
virtual int loadObjects(const char *);
|
|
virtual int loadWords(const char *);
|
|
};
|
|
|
|
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;
|
|
}
|
|
|
|
virtual int init();
|
|
virtual int deinit();
|
|
virtual int detectGame();
|
|
virtual int loadResource(int, int);
|
|
virtual int unloadResource(int, int);
|
|
virtual int loadObjects(const char *);
|
|
virtual int loadWords(const char *);
|
|
};
|
|
|
|
|
|
class GfxMgr;
|
|
class SpritesMgr;
|
|
class Menu;
|
|
|
|
// Image stack support
|
|
struct ImageStackElement {
|
|
uint8 type;
|
|
uint8 pad;
|
|
int16 parm1;
|
|
int16 parm2;
|
|
int16 parm3;
|
|
int16 parm4;
|
|
int16 parm5;
|
|
int16 parm6;
|
|
int16 parm7;
|
|
};
|
|
|
|
struct StringData {
|
|
int x;
|
|
int y;
|
|
int len;
|
|
int str;
|
|
};
|
|
|
|
#define TICK_SECONDS 20
|
|
|
|
#define KEY_QUEUE_SIZE 16
|
|
|
|
class AgiBase : public ::Engine {
|
|
protected:
|
|
// Engine API
|
|
Common::Error init();
|
|
virtual Common::Error go() = 0;
|
|
virtual Common::Error run() {
|
|
Common::Error err;
|
|
err = init();
|
|
if (err.getCode() != Common::kNoError)
|
|
return err;
|
|
return go();
|
|
}
|
|
virtual bool hasFeature(EngineFeature f) const;
|
|
|
|
virtual void initialize() = 0;
|
|
|
|
void initRenderMode();
|
|
|
|
public:
|
|
GfxMgr *_gfx;
|
|
|
|
AgiButtonStyle _defaultButtonStyle;
|
|
AgiButtonStyle _buttonStyle;
|
|
Common::RenderMode _renderMode;
|
|
volatile uint32 _clockCount;
|
|
AgiDebug _debug;
|
|
AgiGame _game;
|
|
Common::RandomSource *_rnd;
|
|
|
|
SoundMgr *_sound;
|
|
|
|
Mouse _mouse;
|
|
|
|
bool _noSaveLoadAllowed;
|
|
|
|
virtual void pollTimer() = 0;
|
|
virtual int getKeypress() = 0;
|
|
virtual bool isKeypress() = 0;
|
|
virtual void clearKeyQueue() = 0;
|
|
|
|
AgiBase(OSystem *syst, const AGIGameDescription *gameDesc);
|
|
~AgiBase();
|
|
|
|
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;
|
|
virtual int saveGame(const Common::String &fileName, const Common::String &saveName) = 0;
|
|
virtual int loadGame(const Common::String &fileName, bool checkId = true) = 0;
|
|
|
|
int _soundemu;
|
|
|
|
int getflag(int);
|
|
void setflag(int, int);
|
|
void flipflag(int);
|
|
|
|
const AGIGameDescription *_gameDescription;
|
|
|
|
uint32 _gameFeatures;
|
|
uint16 _gameVersion;
|
|
|
|
uint32 getGameID() const;
|
|
uint32 getFeatures() const;
|
|
uint16 getVersion() const;
|
|
uint16 getGameType() const;
|
|
Common::Language getLanguage() const;
|
|
Common::Platform getPlatform() const;
|
|
const char *getGameMD5() const;
|
|
void initFeatures();
|
|
void setFeature(uint32 feature);
|
|
void initVersion();
|
|
void setVersion(uint16 version);
|
|
|
|
const char *getDiskName(uint16 id);
|
|
|
|
bool canLoadGameStateCurrently();
|
|
bool canSaveGameStateCurrently();
|
|
};
|
|
|
|
typedef void (*AgiCommand)(AgiGame *state, uint8 *p);
|
|
|
|
class AgiEngine : public AgiBase {
|
|
protected:
|
|
// Engine APIs
|
|
virtual Common::Error go();
|
|
|
|
void initialize();
|
|
|
|
uint32 _lastSaveTime;
|
|
|
|
public:
|
|
AgiEngine(OSystem *syst, const AGIGameDescription *gameDesc);
|
|
virtual ~AgiEngine();
|
|
|
|
Common::Error loadGameState(int slot);
|
|
Common::Error saveGameState(int slot, const Common::String &desc);
|
|
|
|
private:
|
|
uint32 _lastTick;
|
|
|
|
int _keyQueue[KEY_QUEUE_SIZE];
|
|
int _keyQueueStart;
|
|
int _keyQueueEnd;
|
|
|
|
bool _allowSynthetic;
|
|
|
|
int checkPriority(VtEntry *v);
|
|
int checkCollision(VtEntry *v);
|
|
int checkPosition(VtEntry *v);
|
|
|
|
void parseFeatures();
|
|
|
|
int _firstSlot;
|
|
|
|
public:
|
|
AgiObject *_objects; // objects in the game
|
|
|
|
StringData _stringdata;
|
|
|
|
Common::String getSavegameFilename(int num) const;
|
|
void getSavegameDescription(int num, char *buf, bool showEmpty = true);
|
|
int selectSlot();
|
|
int saveGame(const Common::String &fileName, const Common::String &saveName);
|
|
int loadGame(const Common::String &fileName, bool checkId = true);
|
|
int saveGameDialog();
|
|
int saveGameSimple();
|
|
int loadGameDialog();
|
|
int loadGameSimple();
|
|
|
|
uint8 *_intobj;
|
|
InputMode _oldMode;
|
|
bool _restartGame;
|
|
|
|
Menu* _menu;
|
|
bool _menuSelected;
|
|
|
|
char _lastSentence[40];
|
|
|
|
SpritesMgr *_sprites;
|
|
PictureMgr *_picture;
|
|
AgiLoader *_loader; // loader
|
|
|
|
Common::Stack<ImageStackElement> _imageStack;
|
|
|
|
void clearImageStack();
|
|
void recordImageStackCall(uint8 type, int16 p1, int16 p2, int16 p3,
|
|
int16 p4, int16 p5, int16 p6, int16 p7);
|
|
void replayImageStackCall(uint8 type, int16 p1, int16 p2, int16 p3,
|
|
int16 p4, int16 p5, int16 p6, int16 p7);
|
|
void releaseImageStack();
|
|
|
|
void pause(uint32 msec);
|
|
|
|
Console *_console;
|
|
GUI::Debugger *getDebugger() { return _console; }
|
|
|
|
int agiInit();
|
|
int agiDeinit();
|
|
int agiDetectGame();
|
|
int agiLoadResource(int, int);
|
|
int agiUnloadResource(int, int);
|
|
void agiUnloadResources();
|
|
|
|
virtual void pollTimer();
|
|
virtual int getKeypress();
|
|
virtual bool isKeypress();
|
|
virtual void clearKeyQueue();
|
|
|
|
void initPriTable();
|
|
|
|
void newInputMode(InputMode mode);
|
|
void oldInputMode();
|
|
|
|
int getvar(int);
|
|
void setvar(int, int);
|
|
void decrypt(uint8 *mem, int len);
|
|
void releaseSprites();
|
|
int mainCycle();
|
|
int viewPictures();
|
|
int runGame();
|
|
void inventory();
|
|
void updateTimer();
|
|
int getAppDir(char *appDir, unsigned int size);
|
|
|
|
int setupV2Game(int ver);
|
|
int setupV3Game(int ver);
|
|
|
|
void newRoom(int n);
|
|
void resetControllers();
|
|
void interpretCycle();
|
|
int playGame();
|
|
|
|
void printItem(int n, int fg, int bg);
|
|
int findItem();
|
|
int showItems();
|
|
void selectItems(int n);
|
|
|
|
void allowSynthetic(bool);
|
|
void processEvents();
|
|
void checkQuickLoad();
|
|
|
|
// Objects
|
|
public:
|
|
int showObjects();
|
|
int loadObjects(const char *fname);
|
|
int loadObjects(Common::File &fp);
|
|
void unloadObjects();
|
|
const char *objectName(unsigned int);
|
|
int objectGetLocation(unsigned int);
|
|
void objectSetLocation(unsigned int, int);
|
|
private:
|
|
int decodeObjects(uint8 *mem, uint32 flen);
|
|
int readObjects(Common::File &fp, int flen);
|
|
int allocObjects(int);
|
|
|
|
// Logic
|
|
public:
|
|
int decodeLogic(int);
|
|
void unloadLogic(int);
|
|
int runLogic(int);
|
|
void debugConsole(int, int, const char *);
|
|
int testIfCode(int);
|
|
void executeAgiCommand(uint8, uint8 *);
|
|
|
|
public:
|
|
// Some submethods of testIfCode
|
|
void skipInstruction(byte op);
|
|
void skipInstructionsUntil(byte v);
|
|
uint8 testObjRight(uint8, uint8, uint8, uint8, uint8);
|
|
uint8 testObjCenter(uint8, uint8, uint8, uint8, uint8);
|
|
uint8 testObjInBox(uint8, uint8, uint8, uint8, uint8);
|
|
uint8 testPosn(uint8, uint8, uint8, uint8, uint8);
|
|
uint8 testSaid(uint8, uint8 *);
|
|
uint8 testController(uint8);
|
|
uint8 testKeypressed();
|
|
uint8 testCompareStrings(uint8, uint8);
|
|
|
|
// View
|
|
private:
|
|
|
|
void lSetCel(VtEntry *v, int n);
|
|
void lSetLoop(VtEntry *v, int n);
|
|
void updateView(VtEntry *v);
|
|
|
|
public:
|
|
|
|
void setCel(VtEntry *, int);
|
|
void clipViewCoordinates(VtEntry *v);
|
|
void setLoop(VtEntry *, int);
|
|
void setView(VtEntry *, int);
|
|
void startUpdate(VtEntry *);
|
|
void stopUpdate(VtEntry *);
|
|
void updateViewtable();
|
|
void unloadView(int);
|
|
int decodeView(int);
|
|
void addToPic(int, int, int, int, int, int, int);
|
|
void drawObj(int);
|
|
bool isEgoView(const VtEntry *v);
|
|
|
|
// Words
|
|
public:
|
|
int showWords();
|
|
int loadWords(const char *);
|
|
int loadWords_v1(Common::File &f);
|
|
void unloadWords();
|
|
int findWord(const char *word, int *flen);
|
|
void dictionaryWords(char *);
|
|
|
|
// Motion
|
|
private:
|
|
int checkStep(int delta, int step);
|
|
int checkBlock(int x, int y);
|
|
void changePos(VtEntry *v);
|
|
void motionWander(VtEntry *v);
|
|
void motionFollowEgo(VtEntry *v);
|
|
void motionMoveObj(VtEntry *v);
|
|
void checkMotion(VtEntry *v);
|
|
|
|
public:
|
|
void checkAllMotions();
|
|
void moveObj(VtEntry *);
|
|
void inDestination(VtEntry *);
|
|
void fixPosition(int);
|
|
void updatePosition();
|
|
int getDirection(int x0, int y0, int x, int y, int s);
|
|
|
|
bool _egoHoldKey;
|
|
|
|
// Keyboard
|
|
void initWords();
|
|
void cleanInput();
|
|
int doPollKeyboard();
|
|
void cleanKeyboard();
|
|
void handleKeys(int);
|
|
void handleGetstring(int);
|
|
int handleController(int);
|
|
void getString(int, int, int, int);
|
|
uint16 agiGetKeypress();
|
|
int waitKey();
|
|
int waitAnyKey();
|
|
|
|
// Text
|
|
public:
|
|
int messageBox(const char *);
|
|
int selectionBox(const char *, const char **);
|
|
void closeWindow();
|
|
void drawWindow(int, int, int, int);
|
|
void printText(const char *, int, int, int, int, int, int, bool checkerboard = false);
|
|
void printTextConsole(const char *, int, int, int, int, int);
|
|
int print(const char *, int, int, int);
|
|
char *wordWrapString(const char *, int *);
|
|
char *agiSprintf(const char *);
|
|
void writeStatus();
|
|
void writePrompt();
|
|
void clearPrompt(bool useBlackBg = false);
|
|
void clearLines(int, int, int);
|
|
void flushLines(int, int);
|
|
bool predictiveDialog();
|
|
|
|
private:
|
|
void printStatus(const char *message, ...) GCC_PRINTF(2, 3);
|
|
void printText2(int l, const char *msg, int foff, int xoff, int yoff, int len, int fg, int bg, bool checkerboard = false);
|
|
void blitTextbox(const char *p, int y, int x, int len);
|
|
void eraseTextbox();
|
|
void loadDict();
|
|
bool matchWord();
|
|
|
|
// Predictive dialog
|
|
// TODO: Move this to a separate class
|
|
char *_predictiveDictText;
|
|
char **_predictiveDictLine;
|
|
int32 _predictiveDictLineCount;
|
|
char *_predictiveDictActLine;
|
|
Common::String _currentCode;
|
|
Common::String _currentWord;
|
|
int _wordNumber;
|
|
bool _predictiveDialogRunning;
|
|
public:
|
|
char _predictiveResult[40];
|
|
|
|
private:
|
|
AgiCommand _agiCommands[183];
|
|
AgiCommand _agiCondCommands[256];
|
|
|
|
void setupOpcodes();
|
|
|
|
public:
|
|
int _timerHack; // Workaround for timer loop in MH1 logic 153
|
|
};
|
|
|
|
} // End of namespace Agi
|
|
|
|
#endif /* AGI_H */
|