mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-17 15:18:11 +00:00
5fadff59f9
We now only test for events in testKeypressed() without updating the game cycle at all (NAGI doesn't update the game cycle either). This fixes the slowdowns in some animations where have.key() is issued, like Manannan's lightnings in the intro of KQ3 and the bullets in the intro of PQ1
1109 lines
27 KiB
C++
1109 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/rendermode.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"
|
|
|
|
#include "gui/predictivedialog.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;
|
|
|
|
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();
|
|
int doSave(int slot, const Common::String &desc);
|
|
int doLoad(int slot, bool showMessages);
|
|
int scummVMSaveLoadDialog(bool isSave);
|
|
|
|
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(bool onlyCheckForEvents = false);
|
|
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);
|
|
|
|
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();
|
|
bool matchWord();
|
|
|
|
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 */
|