Initial commit

This commit is contained in:
2026-02-02 04:50:13 +01:00
commit 5b11698731
22592 changed files with 7677434 additions and 0 deletions

1
engines/queen/POTFILES Normal file
View File

@@ -0,0 +1 @@
engines/queen/metaengine.cpp

243
engines/queen/bankman.cpp Normal file
View File

@@ -0,0 +1,243 @@
/* 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 "queen/bankman.h"
#include "queen/resource.h"
#include "common/debug.h"
namespace Queen {
BankManager::BankManager(Resource *res)
: _res(res) {
memset(_frames, 0, sizeof(_frames));
memset(_banks, 0, sizeof(_banks));
}
BankManager::~BankManager() {
for (uint32 i = 0; i < MAX_BANKS_NUMBER; ++i) {
close(i);
}
eraseFrames(true);
}
void BankManager::load(const char *bankname, uint32 bankslot) {
debug(9, "BankManager::load(%s, %d)", bankname, bankslot);
assert(bankslot < MAX_BANKS_NUMBER);
PackedBank *bank = &_banks[bankslot];
if (!scumm_stricmp(bankname, bank->name)) {
debug(9, "BankManager::load() bank '%s' already loaded", bankname);
return;
}
close(bankslot);
if (_res->getPlatform() == Common::kPlatformAmiga && !_res->fileExists(bankname)) {
debug(9, "BankManager::load() bank '%s' doesn't exist", bankname);
return;
}
bank->data = _res->loadFile(bankname);
if (_res->getPlatform() == Common::kPlatformAmiga) {
uint16 entries = READ_BE_UINT16(bank->data + 4);
debug(9, "BankManager::load() entries = %d", entries);
assert(entries < MAX_BANK_SIZE);
uint32 offset = 6;
_banks[bankslot].indexes[0] = offset;
for (uint16 i = 1; i <= entries; ++i) {
_banks[bankslot].indexes[i] = offset;
uint16 dataSize = READ_BE_UINT16(bank->data + offset + 10);
offset += dataSize + 12;
}
} else {
uint16 entries = READ_LE_UINT16(bank->data);
debug(9, "BankManager::load() entries = %d", entries);
assert(entries < MAX_BANK_SIZE);
uint32 offset = 2;
_banks[bankslot].indexes[0] = offset;
for (uint16 i = 1; i <= entries; ++i) {
_banks[bankslot].indexes[i] = offset;
uint16 w = READ_LE_UINT16(bank->data + offset + 0);
uint16 h = READ_LE_UINT16(bank->data + offset + 2);
offset += w * h + 8;
}
}
// mark this bank as loaded
Common::strcpy_s(bank->name, bankname);
}
static void convertPlanarBitmap(uint8 *dst, int dstPitch, const uint8 *src, int w, int h, int plane) {
assert(w != 0 && h != 0);
int planarSize = plane * h * w * 2;
uint8 *planarBuf = new uint8[ planarSize ];
uint8 *dstPlanar = planarBuf;
while (planarSize > 0) {
if (src[0] == 0) {
int count = src[1];
memset(dstPlanar, 0, count);
dstPlanar += count;
src += 2;
planarSize -= count;
} else {
*dstPlanar++ = *src++;
--planarSize;
}
}
src = planarBuf;
int i = 0;
int planeSize = h * w * 2;
while (h--) {
for (int x = 0; x < w * 2; ++x) {
for (int b = 0; b < 8; ++b) {
const uint8 mask = (1 << (7 - b));
uint8 color = 0;
for (int p = 0; p < plane; ++p) {
if (src[planeSize * p + i] & mask) {
color |= (1 << p);
}
}
dst[8 * x + b] = color;
}
++i;
}
dst += dstPitch;
}
delete[] planarBuf;
}
void BankManager::unpack(uint32 srcframe, uint32 dstframe, uint32 bankslot) {
debug(9, "BankManager::unpack(%d, %d, %d)", srcframe, dstframe, bankslot);
assert(bankslot < MAX_BANKS_NUMBER);
PackedBank *bank = &_banks[bankslot];
assert(bank->data != nullptr);
assert(dstframe < MAX_FRAMES_NUMBER);
BobFrame *bf = &_frames[dstframe];
delete[] bf->data;
bf->data = nullptr;
const uint8 *p = bank->data + bank->indexes[srcframe];
if (_res->getPlatform() == Common::kPlatformAmiga) {
uint16 w = READ_BE_UINT16(p + 0);
uint16 h = READ_BE_UINT16(p + 2);
uint16 plane = READ_BE_UINT16(p + 4);
bf->xhotspot = READ_BE_UINT16(p + 6);
bf->yhotspot = READ_BE_UINT16(p + 8);
bf->width = w * 16;
bf->height = h;
uint32 size = bf->width * bf->height;
if (size != 0) {
bf->data = new uint8[ size ];
convertPlanarBitmap(bf->data, bf->width, p + 12, w, h, plane);
}
} else {
bf->width = READ_LE_UINT16(p + 0);
bf->height = READ_LE_UINT16(p + 2);
bf->xhotspot = READ_LE_UINT16(p + 4);
bf->yhotspot = READ_LE_UINT16(p + 6);
uint32 size = bf->width * bf->height;
if (size != 0) {
bf->data = new uint8[ size ];
memcpy(bf->data, p + 8, size);
}
}
}
void BankManager::overpack(uint32 srcframe, uint32 dstframe, uint32 bankslot) {
debug(9, "BankManager::overpack(%d, %d, %d)", srcframe, dstframe, bankslot);
assert(bankslot < MAX_BANKS_NUMBER);
PackedBank *bank = &_banks[bankslot];
assert(bank->data != nullptr);
assert(dstframe < MAX_FRAMES_NUMBER);
BobFrame *bf = &_frames[dstframe];
const uint8 *p = bank->data + bank->indexes[srcframe];
if (_res->getPlatform() == Common::kPlatformAmiga) {
uint16 w = READ_BE_UINT16(p + 0);
uint16 h = READ_BE_UINT16(p + 2);
uint16 plane = READ_BE_UINT16(p + 4);
uint16 src_w = w * 16;
uint16 src_h = h;
if (bf->width < src_w || bf->height < src_h) {
unpack(srcframe, dstframe, bankslot);
} else {
convertPlanarBitmap(bf->data, bf->width, p + 12, w, h, plane);
}
} else {
uint16 src_w = READ_LE_UINT16(p + 0);
uint16 src_h = READ_LE_UINT16(p + 2);
// unpack if destination frame is smaller than source
if (bf->width < src_w || bf->height < src_h) {
unpack(srcframe, dstframe, bankslot);
} else {
// copy data 'over' destination frame (without updating frame header)
memcpy(bf->data, p + 8, src_w * src_h);
}
}
}
void BankManager::close(uint32 bankslot) {
debug(9, "BankManager::close(%d)", bankslot);
assert(bankslot < MAX_BANKS_NUMBER);
PackedBank *bank = &_banks[bankslot];
delete[] bank->data;
memset(bank, 0, sizeof(PackedBank));
}
BobFrame *BankManager::fetchFrame(uint32 index) {
debug(9, "BankManager::fetchFrame(%d)", index);
assert(index < MAX_FRAMES_NUMBER);
BobFrame *bf = &_frames[index];
assert((bf->width == 0 && bf->height == 0) || bf->data != nullptr);
return bf;
}
void BankManager::eraseFrame(uint32 index) {
debug(9, "BankManager::eraseFrame(%d)", index);
assert(index < MAX_FRAMES_NUMBER);
BobFrame *bf = &_frames[index];
delete[] bf->data;
memset(bf, 0, sizeof(BobFrame));
}
void BankManager::eraseFrames(bool joe) {
for (uint32 i = joe ? 0 : FRAMES_JOE; i < MAX_FRAMES_NUMBER; ++i) {
eraseFrame(i);
}
}
} // End of namespace Queen

84
engines/queen/bankman.h Normal file
View File

@@ -0,0 +1,84 @@
/* 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 QUEEN_BANKMAN_H
#define QUEEN_BANKMAN_H
#include "common/util.h"
#include "queen/structs.h"
namespace Queen {
class Resource;
class BankManager {
public:
BankManager(Resource *res);
~BankManager();
//! load a bank into the specified slot
void load(const char *bankname, uint32 bankslot);
//! unpack a frame from a loaded bank
void unpack(uint32 srcframe, uint32 dstframe, uint32 bankslot);
//! unpack a frame over an existing one from a loaded bank
void overpack(uint32 srcframe, uint32 dstframe, uint32 bankslot);
//! close a bank
void close(uint32 bankslot);
//! get a reference to unpacked frame
BobFrame *fetchFrame(uint32 index);
//! erase a frame
void eraseFrame(uint32 index);
//! erase all unpacked frames
void eraseFrames(bool joe);
enum {
MAX_BANK_SIZE = 110,
MAX_FRAMES_NUMBER = 256,
MAX_BANKS_NUMBER = 18
};
private:
struct PackedBank {
uint32 indexes[MAX_BANK_SIZE];
uint8 *data;
char name[20];
};
//! unpacked bob frames
BobFrame _frames[MAX_FRAMES_NUMBER];
//! banked bob frames
PackedBank _banks[MAX_BANKS_NUMBER];
Resource *_res;
};
} // End of namespace Queen
#endif

1326
engines/queen/command.cpp Normal file

File diff suppressed because it is too large Load Diff

235
engines/queen/command.h Normal file
View 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 QUEEN_COMMAND_H
#define QUEEN_COMMAND_H
#include "common/util.h"
#include "queen/structs.h"
namespace Queen {
class QueenEngine;
class CmdText {
public:
static CmdText *makeCmdTextInstance(uint8 y, QueenEngine *vm);
CmdText(uint8 y, QueenEngine *vm);
virtual ~CmdText() {}
//! reset the command sentence
void clear();
//! display the command sentence using the specified color
void display(InkColor color, const char *command = 0, bool outlined = false);
//! display a temporary command sentence using the specified parameters
void displayTemp(InkColor color, Verb v);
//! display a temporary command sentence using the specified parameters
virtual void displayTemp(InkColor color, const char *name, bool outlined);
//! set the verb for the command sentence
void setVerb(Verb v);
//! set the link word (between verb and object) for the command sentence
virtual void addLinkWord(Verb v);
//! add an object name to the command sentence
virtual void addObject(const char *objName);
//! returns true if the command sentence is empty
bool isEmpty() const { return _command[0] == 0; }
enum {
MAX_COMMAND_LEN = 256,
COMMAND_Y_POS = 151
};
protected:
//! buffer containing the current command sentence
char _command[MAX_COMMAND_LEN];
uint8 _y;
QueenEngine *_vm;
};
struct CmdState {
void init();
Verb oldVerb, verb;
Verb action;
int16 oldNoun, noun;
int commandLevel;
int16 subject[2];
Verb selAction;
int16 selNoun;
};
class Command {
public:
Command(QueenEngine *vm);
~Command();
//! initialize command construction
void clear(bool clearTexts);
//! execute last constructed command
void executeCurrentAction();
//! get player input and construct command from it
void updatePlayer();
//! read all command arrays from stream
void readCommandsFrom(byte *&ptr);
enum {
MAX_MATCHING_CMDS = 50
};
private:
//! get a reference to the ObjectData for the specified room object
ObjectData *findObjectData(uint16 objRoomNum) const;
//! get a reference to the ItemData for the specified inventory object
ItemData *findItemData(Verb invNum) const;
//! execute the current command
int16 executeCommand(uint16 comId, int16 condResult);
//! move Joe to the specified position, handling new room switching
int16 makeJoeWalkTo(int16 x, int16 y, int16 objNum, Verb v, bool mustWalk);
//! update command state with current selected action
void grabCurrentSelection();
//! update command state with current selected object
void grabSelectedObject(int16 objNum, uint16 objState, uint16 objName);
//! update command state with current selected inventory object
void grabSelectedItem();
//! update command state with current selected room object
void grabSelectedNoun();
//! update command state with current selected verb
void grabSelectedVerb();
//! if the description is a cutaway file, execute it
bool executeIfCutaway(const char *description);
//! if the description is a dialog file, execute it
bool executeIfDialog(const char *description);
//! handle a wrong/invalid user action
bool handleWrongAction();
//! make Joe speak something for a wrong/invalid action
void sayInvalidAction(Verb action, int16 subj1, int16 subj2);
//! update an object state
void changeObjectState(Verb action, int16 obj, int16 song, bool cutDone);
//! reset current action
void cleanupCurrentAction();
//! OPEN_CLOSE_OTHER(OBJECT_DATA[S][4])
void openOrCloseAssociatedObject(Verb action, int16 obj);
//! update gamestates - P1_SET_CONDITIONS
int16 setConditions(uint16 command, bool lastCmd);
//! turn on/off areas - P2_SET_AREAS
void setAreas(uint16 command);
//! hide/show objects, redisplay if in the same room as Joe - P3_SET_OBJECTS
void setObjects(uint16 command);
//! inserts/deletes items (inventory) - P4_SET_ITEMS
void setItems(uint16 command);
//! update description for object and returns description number to use
uint16 nextObjectDescription(ObjectDescription *objDesc, uint16 firstDesc);
//! speak description of selected object
void lookAtSelectedObject();
//! get the current object under the cursor
void lookForCurrentObject(int16 cx, int16 cy);
//! get the current icon panel under the cursor (inventory item or verb)
void lookForCurrentIcon(int16 cx, int16 cy);
//! returns true if the verb is an action verb
bool isVerbAction(Verb v) const { return (v >= VERB_PANEL_COMMAND_FIRST && v <= VERB_PANEL_COMMAND_LAST) || (v == VERB_WALK_TO); }
//! return true if the verb is an inventory item
bool isVerbInv(Verb v) const { return v >= VERB_INV_FIRST && v <= VERB_INV_LAST; }
//! returns true if the specified verb is an inventory scroll
bool isVerbInvScroll(Verb v) const { return v == VERB_SCROLL_UP || v == VERB_SCROLL_DOWN; }
//! commands list for each possible action
CmdListData *_cmdList;
uint16 _numCmdList;
//! commands list for areas
CmdArea *_cmdArea;
uint16 _numCmdArea;
//! commands list for objects
CmdObject *_cmdObject;
uint16 _numCmdObject;
//! commands list for inventory
CmdInventory *_cmdInventory;
uint16 _numCmdInventory;
//! commands list for gamestate
CmdGameState *_cmdGameState;
uint16 _numCmdGameState;
//! textual form of the command (displayed between room and panel areas)
CmdText *_cmdText;
//! flag indicating that the current command is fully constructed
bool _parse;
//! state of current constructed command
CmdState _state;
//! last user selection
int _mouseKey, _selPosX, _selPosY;
QueenEngine *_vm;
};
} // End of namespace Queen
#endif

View File

@@ -0,0 +1,3 @@
# This file is included from the main "configure" script
# add_engine [name] [desc] [build-by-default] [subengines] [base games] [deps] [components]
add_engine queen "Flight of the Amazon Queen" yes "" "" "" "midi"

146
engines/queen/credits.cpp Normal file
View File

@@ -0,0 +1,146 @@
/* 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 "queen/credits.h"
#include "queen/display.h"
#include "queen/queen.h"
#include "queen/resource.h"
namespace Queen {
Credits::Credits(QueenEngine *vm, const char* filename) :
_vm(vm), _running(true), _count(0), _pause(0), _justify(0), _fontSize(0), _color(0), _zone(0), _lineNum(0) {
_vm->resource()->loadTextFile(filename, _credits);
}
void Credits::nextRoom() {
if (-1 == _pause) {
_pause = 0;
_vm->display()->clearTexts(0, 199);
}
}
void Credits::update() {
if (!_running)
return;
if (_pause > 0) {
_pause--;
if (!_pause)
_vm->display()->clearTexts(0, 199);
return;
}
/* wait until next room */
if (-1 == _pause)
return;
while (_lineNum < _credits.size()) {
const char *line = _credits[_lineNum].c_str();
++_lineNum;
if (0 == memcmp(line, "EN", 2)) {
_running = false;
return;
}
if ('.' == line[0]) {
int i;
switch (tolower(line[1])) {
case 'l' :
_justify = 0;
break;
case 'c' :
_justify = 1;
break;
case 'r' :
_justify = 2;
break;
case 's' :
_fontSize = 0;
break;
case 'b' :
_fontSize = 1;
break;
case 'p' :
_pause = atoi(&line[3]);
_pause *= 10;
/* wait until next room */
if (0 == _pause)
_pause = -1;
for (i = 0; i < _count; i++) {
_vm->display()->textCurrentColor(_list[i].color);
_vm->display()->setText(_list[i].x, _list[i].y, _list[i].text);
}
_count = 0;
return;
case 'i' :
_color = atoi(&line[3]);
if (_vm->resource()->getPlatform() == Common::kPlatformAmiga) {
_color &= 31;
}
break;
case '1' :
case '2' :
case '3' :
case '4' :
case '5' :
case '6' :
case '7' :
case '8' :
case '9' :
_zone = line[1] - '1';
break;
default:
break;
}
} else {
assert(_count < ARRAYSIZE(_list));
_list[_count].text = line;
_list[_count].color = _color;
_list[_count].fontSize = _fontSize;
switch (_justify) {
case 0:
_list[_count].x = (_zone % 3) * (320 / 3) + 8;
break;
case 1:
_list[_count].x = (_zone % 3) * (320 / 3) + 54 - _vm->display()->textWidth(line) / 2;
if (_list[_count].x < 8)
_list[_count].x = 8;
break;
case 2:
_list[_count].x = (_zone % 3) * (320 / 3) + 100 - _vm->display()->textWidth(line);
break;
default:
break;
}
_list[_count].y = (_zone / 3) * (200 / 3) + (_count * 10);
_count++;
}
}
_running = false;
}
} // End of namespace Queen

88
engines/queen/credits.h Normal file
View File

@@ -0,0 +1,88 @@
/* 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 QUEEN_CREDITS_H
#define QUEEN_CREDITS_H
#include "common/str-array.h"
#include "queen/defs.h"
namespace Queen {
class QueenEngine;
class LineReader;
class Credits {
public:
Credits(QueenEngine *vm, const char* filename);
//! update/display credits for current room
void update();
//! handles room switching
void nextRoom();
//! returns true if the credits are running
bool running() const { return _running; }
private:
struct Line {
short x, y, color, fontSize;
const char *text;
};
//! contains the formatted lines of texts to display
Line _list[19];
//! true if end of credits description hasn't been reached
bool _running;
//! number of elements in _list array
int _count;
//! pause counts for next room
int _pause;
//! current text justification mode
int _justify;
//! current font size (unused ?)
int _fontSize;
//! current text color
int _color;
//! current text position
int _zone;
uint _lineNum;
//! contains the credits description
Common::StringArray _credits;
QueenEngine *_vm;
};
} // End of namespace Queen
#endif

5
engines/queen/credits.pl Normal file
View File

@@ -0,0 +1,5 @@
begin_section("Queen");
add_person("David Eriksson", "twogood", "(retired)");
add_person("Gregory Montoir", "cyx", "(retired)");
add_person("Joost Peters", "joostp", "");
end_section();

1325
engines/queen/cutaway.cpp Normal file

File diff suppressed because it is too large Load Diff

264
engines/queen/cutaway.h Normal file
View File

@@ -0,0 +1,264 @@
/* 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 QUEEN_CUTAWAY_H
#define QUEEN_CUTAWAY_H
#include "common/util.h"
#include "queen/structs.h"
namespace Queen {
class QueenEngine;
class Cutaway {
public:
//! Public interface to run a cutaway from a file
static void run(const char *filename, char *nextFilename, QueenEngine *vm);
//! Collection of constants used by QueenCutaway
enum {
PREVIOUS_ROOM = 0,
CURRENT_ROOM = 0,
OBJECT_ROOMFADE = -1,
PERSON_JOE = -1,
OBJECT_JOE = 0,
MAX_PERSON_COUNT = 6,
CUTAWAY_BANK = 8,
MAX_BANK_NAME_COUNT = 5,
MAX_FILENAME_LENGTH = 12,
MAX_FILENAME_SIZE = (MAX_FILENAME_LENGTH + 1),
MAX_PERSON_FACE_COUNT = 13,
MAX_STRING_LENGTH = 255,
MAX_STRING_SIZE = (MAX_STRING_LENGTH + 1),
LEFT = 1,
RIGHT = 2,
FRONT = 3,
BACK = 4
};
//! Different kinds of cutaway objects
enum ObjectType {
OBJECT_TYPE_ANIMATION = 0,
OBJECT_TYPE_PERSON = 1,
OBJECT_TYPE_NO_ANIMATION = 2,
OBJECT_TYPE_TEXT_SPEAK = 3,
OBJECT_TYPE_TEXT_DISPLAY_AND_SPEAK = 4,
OBJECT_TYPE_TEXT_DISPLAY = 5
};
private:
//! Data for a cutaway object
struct CutawayObject {
int16 objectNumber; // 0 = JOE, -1 = MESSAGE
int16 moveToX;
int16 moveToY;
int16 bank; // 0 = PBOB, 13 = Joe Bank, else BANK NAMEstr()
int16 animList;
int16 execute; // 1 Yes, 0 No
int16 limitBobX1;
int16 limitBobY1;
int16 limitBobX2;
int16 limitBobY2;
int16 specialMove;
int16 animType; // 0 - Packet, 1 - Amal, 2 - Unpack
int16 fromObject;
int16 bobStartX;
int16 bobStartY;
int16 room;
int16 scale;
// Variables derived from the variables above
int song;
//! People to turn on
int person[MAX_PERSON_COUNT];
//! Number of elements used in _person array
int personCount;
};
struct CutawayAnim {
int16 object;
int16 unpackFrame; // Frame to unpack
int16 speed;
int16 bank;
int16 mx;
int16 my;
int16 cx;
int16 cy;
int16 scale;
int16 currentFrame; // Index to Current Frame
int16 originalFrame; // Index to Original Object Frame
int16 song;
bool flip; // set this if unpackFrame is negative
};
struct ObjectDataBackup {
int index;
int16 name;
int16 image;
};
struct PersonFace {
int16 index;
int16 image;
};
QueenEngine *_vm;
//! Raw .cut file data (without 20 byte header)
byte *_fileData;
//! COMPANEL
int16 _comPanel;
//! Game state data inside of _fileDat
byte *_gameStatePtr;
//! Actual cutaway data inside of _fileData
byte *_objectData;
//! Pointer to next sentence string in _fileData
uint16 _nextSentenceOff;
//! ???
bool _roomFade;
//! Number of cutaway objects at _cutawayData
int16 _cutawayObjectCount;
//! This cutaway is followed by another
bool _anotherCutaway;
//! Room before cutaway
int _initialRoom;
//! Temporary room for cutaway
int _temporaryRoom;
//! Room to stay in
int _finalRoom;
//! Bank names
char _bankNames[MAX_BANK_NAME_COUNT][MAX_FILENAME_SIZE];
//! Filename without ".cut"
char _basename[MAX_FILENAME_SIZE];
//! Name of .dog file
char _talkFile[MAX_FILENAME_SIZE];
//! Person to talk to
int16 _talkTo;
//! Used by changeRooms
ObjectDataBackup _personData[MAX_PERSON_COUNT];
//! Number of elements used in _personData array
int _personDataCount;
//! Used by handlePersonRecord()
PersonFace _personFace[MAX_PERSON_FACE_COUNT];
//! Number of entries in _personFace array
int _personFaceCount;
//! Play this song when leaving cutaway
int16 _lastSong;
//! Song played before running comic.cut
int16 _songBeforeComic;
int16 _currentImage;
Cutaway(const char *filename, QueenEngine *vm);
~Cutaway();
//! Run this cutaway object
void run(char *nextFilename);
//! Load cutaway data from file
void load(const char *filename);
//! Used by load to read string data
void loadStrings(uint16 offset);
//! Get persons
const byte *turnOnPeople(const byte *ptr, CutawayObject &object);
//! Limit the BOB
void limitBob(CutawayObject &object);
//! This cutaway object occurs in another room
void changeRooms(CutawayObject &object);
//! Get the object type for this CutawayObject
ObjectType getObjectType(CutawayObject &object);
//! Perform actions for an animation
const byte *handleAnimation(const byte *ptr, CutawayObject &object);
//! Perform actions for a person record
void handlePersonRecord(int index, CutawayObject &object, const char *sentence);
//! Perform text actions
void handleText(int index, ObjectType type, CutawayObject &object, const char *sentence);
//! Restore Logic::_objectData from _personData
void restorePersonData();
//! Stop the cutaway
void stop();
//! Update game state after cutaway
void updateGameState();
//! Prepare for talk after cutaway
void talk(char *nextFilename);
//! Get CutawayAnim data from ptr and return new ptr
const byte *getCutawayAnim(const byte *ptr, int header, CutawayAnim &anim);
//! Special animation
int makeComplexAnimation(int16 currentImage, CutawayAnim *objAnim, int frameCount);
//! Read a CutawayObject from ptr and return new ptr
static const byte *getCutawayObject(const byte *ptr, CutawayObject &object);
//! Dump a CutawayObject with debug()
void dumpCutawayObject(int index, CutawayObject &object);
//! Used by handleText()
int countSpaces(ObjectType type, const char *segment);
//! Scale Joe
int scale(CutawayObject &object);
//! Dump CutawayAnum data with debug()
static void dumpCutawayAnim(CutawayAnim &anim);
bool inRange(int16 x, int16 l, int16 h) const { return (x <= h && x >= l); }
};
} // End of namespace Queen
#endif

204
engines/queen/debug.cpp Normal file
View 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/>.
*
*/
#include "common/scummsys.h"
#include "common/util.h"
#include "queen/debug.h"
#include "queen/graphics.h"
#include "queen/logic.h"
#include "queen/queen.h"
#include "queen/resource.h"
#include "queen/sound.h"
namespace Queen {
Debugger::Debugger(QueenEngine *vm)
: _vm(vm), _flags(0) {
registerCmd("areas", WRAP_METHOD(Debugger, Cmd_Areas));
registerCmd("asm", WRAP_METHOD(Debugger, Cmd_Asm));
registerCmd("bob", WRAP_METHOD(Debugger, Cmd_Bob));
registerCmd("bobs", WRAP_METHOD(Debugger, Cmd_PrintBobs));
registerCmd("gs", WRAP_METHOD(Debugger, Cmd_GameState));
registerCmd("info", WRAP_METHOD(Debugger, Cmd_Info));
registerCmd("items", WRAP_METHOD(Debugger, Cmd_Items));
registerCmd("room", WRAP_METHOD(Debugger, Cmd_Room));
registerCmd("song", WRAP_METHOD(Debugger, Cmd_Song));
}
void Debugger::preEnter() {
GUI::Debugger::preEnter();
}
void Debugger::postEnter() {
GUI::Debugger::postEnter();
_vm->graphics()->setupMouseCursor();
}
static bool isNumeric(const char *arg) {
const char *str = arg;
bool retVal = true;
while (retVal && (*str != '\0')) {
retVal = Common::isDigit(*str++);
}
return retVal;
}
bool Debugger::Cmd_Asm(int argc, const char **argv) {
if (argc == 2 && isNumeric(argv[1])) {
uint16 sm = atoi(argv[1]);
_vm->logic()->executeSpecialMove(sm);
return false;
} else {
debugPrintf("Usage: %s smnum\n", argv[0]);
}
return true;
}
bool Debugger::Cmd_Areas(int argc, const char **argv) {
_flags ^= DF_DRAW_AREAS;
debugPrintf("Room areas display %s\n", (_flags & DF_DRAW_AREAS) != 0 ? "on" : "off");
return true;
}
bool Debugger::Cmd_Bob(int argc, const char **argv) {
if (argc >= 3 && isNumeric(argv[1])) {
int bobNum = atoi(argv[1]);
if (bobNum >= Graphics::MAX_BOBS_NUMBER) {
debugPrintf("Bob %d is out of range (range: 0 - %d)\n", bobNum, Graphics::MAX_BOBS_NUMBER);
} else {
int param = 0;
if (argc > 3 && isNumeric(argv[3])) {
param = atoi(argv[3]);
} else {
debugPrintf("Invalid parameter for bob command '%s'\n", argv[2]);
}
BobSlot *bob = _vm->graphics()->bob(bobNum);
if (!strcmp(argv[2], "toggle")) {
bob->active = !bob->active;
debugPrintf("bob[%d].active = %d\n", bobNum, bob->active);
} else if (!strcmp(argv[2], "x")) {
bob->x = param;
debugPrintf("bob[%d].x = %d\n", bobNum, bob->x);
} else if (!strcmp(argv[2], "y")) {
bob->y = param;
debugPrintf("bob[%d].y = %d\n", bobNum, bob->y);
} else if (!strcmp(argv[2], "frame")) {
bob->frameNum = param;
debugPrintf("bob[%d].frameNum = %d\n", bobNum, bob->frameNum);
} else if (!strcmp(argv[2], "speed")) {
bob->speed = param;
debugPrintf("bob[%d].speed = %d\n", bobNum, bob->speed);
} else {
debugPrintf("Unknown bob command '%s'\n", argv[2]);
}
}
} else {
debugPrintf("Usage: %s bobnum command parameter\n", argv[0]);
}
return true;
}
bool Debugger::Cmd_GameState(int argc, const char **argv) {
uint16 slot;
if ((argc == 2 || argc == 3) && isNumeric(argv[1])) {
slot = atoi(argv[1]);
debugPrintf("GAMESTATE[%d] ", slot);
debugPrintf("%s %d\n", (argc == 2) ? "is" : "was", _vm->logic()->gameState(slot));
if (argc == 3) {
if (isNumeric(argv[1])) {
_vm->logic()->gameState(slot, atoi(argv[2]));
debugPrintf("now %d\n", _vm->logic()->gameState(slot));
} else {
debugPrintf("Usage: %s slotnum <value>\n", argv[0]);
}
}
} else {
debugPrintf("Usage: %s slotnum <value>\n", argv[0]);
}
return true;
}
bool Debugger::Cmd_Info(int argc, const char **argv) {
debugPrintf("Version: %s\n", _vm->resource()->getJASVersion());
debugPrintf("Audio compression: %d\n", _vm->resource()->getCompression());
return true;
}
bool Debugger::Cmd_Items(int argc, const char **argv) {
int n = _vm->logic()->itemDataCount();
ItemData *item = _vm->logic()->itemData(1);
while (n--) {
item->name = ABS(item->name);
++item;
}
debugPrintf("Enabled all inventory items\n");
return true;
}
bool Debugger::Cmd_PrintBobs(int argc, const char**argv) {
int i;
BobSlot *bob = _vm->graphics()->bob(0);
debugPrintf("+------------------------------------+\n");
debugPrintf("|# | x| y|f|scl|frm|a|m|spd| ex| ey|\n");
debugPrintf("+--+---+---+-+---+---+-+-+---+---+---+\n");
for (i = 0; i < Graphics::MAX_BOBS_NUMBER; ++i, ++bob) {
if (bob->active) {
debugPrintf("|%2d|%3d|%3d|%1d|%3d|%3d|%1d|%1d|%3d|%3d|%3d|\n",
i, bob->x, bob->y, bob->xflip, bob->scale, bob->frameNum,
bob->animating, bob->moving, bob->speed, bob->endx, bob->endy);
}
}
debugPrintf("+--------------------------------+\n");
return true;
}
bool Debugger::Cmd_Room(int argc, const char **argv) {
if (argc == 2 && isNumeric(argv[1])) {
uint16 roomNum = atoi(argv[1]);
_vm->logic()->joePos(0, 0);
_vm->logic()->newRoom(roomNum);
_vm->logic()->entryObj(_vm->logic()->roomData(roomNum) + 1);
return false;
} else {
debugPrintf("Current room: %d (%s), use '%s <roomnum>' to switch\n",
_vm->logic()->currentRoom(),
_vm->logic()->roomName(_vm->logic()->currentRoom()),
argv[0]);
}
return true;
}
bool Debugger::Cmd_Song(int argc, const char **argv) {
if (argc == 2 && isNumeric(argv[1])) {
int16 songNum = atoi(argv[1]);
_vm->sound()->playSong(songNum);
debugPrintf("Playing song %d\n", songNum);
} else {
debugPrintf("Usage: %s songnum\n", argv[0]);
}
return true;
}
} // End of namespace Queen

63
engines/queen/debug.h Normal file
View File

@@ -0,0 +1,63 @@
/* 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 QUEEN_DEBUG_H
#define QUEEN_DEBUG_H
#include "gui/debugger.h"
namespace Queen {
class QueenEngine;
class Debugger : public GUI::Debugger {
public:
Debugger(QueenEngine *vm);
int flags() const { return _flags; }
enum {
DF_DRAW_AREAS = 1 << 0
};
private:
void preEnter() override;
void postEnter() override;
private:
bool Cmd_Areas(int argc, const char **argv);
bool Cmd_Asm(int argc, const char **argv);
bool Cmd_Bob(int argc, const char **argv);
bool Cmd_GameState(int argc, const char **argv);
bool Cmd_Info(int argc, const char **argv);
bool Cmd_Items(int argc, const char **argv);
bool Cmd_PrintBobs(int argc, const char **argv);
bool Cmd_Room(int argc, const char **argv);
bool Cmd_Song(int argc, const char **argv);
private:
QueenEngine *_vm;
int _flags;
};
} // End of namespace Queen
#endif

335
engines/queen/defs.h Normal file
View File

@@ -0,0 +1,335 @@
/* 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 QUEEN_DEFS_H
#define QUEEN_DEFS_H
namespace Queen {
#define SAVEGAME_SIZE 24622
enum GameFeatures {
GF_DEMO = 1 << 0, // demo
GF_TALKIE = 1 << 1, // equivalent to cdrom version check
GF_FLOPPY = 1 << 2, // floppy, ie. non-talkie version
GF_INTERVIEW = 1 << 3, // interview demo
GF_REBUILT = 1 << 4 // version rebuilt with the 'compression_queen' tool
};
enum {
COMPRESSION_NONE = 0,
COMPRESSION_MP3 = 1,
COMPRESSION_OGG = 2,
COMPRESSION_FLAC = 3
};
enum Version {
VER_ENG_FLOPPY = 0,
VER_ENG_TALKIE = 1,
VER_FRE_FLOPPY = 2,
VER_FRE_TALKIE = 3,
VER_GER_FLOPPY = 4,
VER_GER_TALKIE = 5,
VER_ITA_FLOPPY = 6,
VER_ITA_TALKIE = 7,
VER_SPA_TALKIE = 8,
VER_HEB_TALKIE = 9,
VER_DEMO_PCGAMES = 10,
VER_DEMO = 11,
VER_INTERVIEW = 12,
VER_AMI_ENG_FLOPPY = 13,
VER_AMI_DEMO = 14,
VER_AMI_INTERVIEW = 15,
VER_AMI_GER_FLOPPY = 16,
VER_COUNT = 17
};
enum {
GAME_SCREEN_WIDTH = 320,
GAME_SCREEN_HEIGHT = 200,
ROOM_ZONE_HEIGHT = 150,
PANEL_ZONE_HEIGHT = 50
};
enum {
FRAMES_JOE = 38,
FRAMES_JOURNAL = 40
};
enum Direction {
DIR_LEFT = 1,
DIR_RIGHT = 2,
DIR_FRONT = 3,
DIR_BACK = 4
};
enum InkColor {
INK_BG_PANEL = 0,
INK_JOURNAL,
INK_PINNACLE_ROOM,
INK_CMD_SELECT,
INK_CMD_NORMAL,
INK_TALK_NORMAL,
INK_JOE,
INK_OUTLINED_TEXT,
INK_COUNT
};
enum {
ITEM_NONE = 0,
ITEM_BAT,
ITEM_JOURNAL,
ITEM_KNIFE,
ITEM_COCONUT_HALVES,
ITEM_BEEF_JERKY,
ITEM_PROPELLER,
ITEM_BANANA,
ITEM_VINE,
ITEM_SLOTH_HAIR,
ITEM_COMIC_BOOK,
ITEM_FLOWER,
ITEM_BEETLE,
ITEM_ORCHID,
ITEM_DICTIONARY,
ITEM_DEATH_MASH,
ITEM_PERFUME,
ITEM_TYRANNO_HORN,
ITEM_LOTION,
ITEM_RECORD,
ITEM_VACUUM_CLEANER,
ITEM_NET,
ITEM_ALCOHOL,
ITEM_ROCKET_PACK,
ITEM_SOME_MONEY,
ITEM_CHEESE_BITZ,
ITEM_DOG_FOOD,
ITEM_CAN_OPENER,
ITEM_LETTER,
ITEM_SQUEAKY_TOY,
ITEM_KEY,
ITEM_BOOK,
ITEM_PIECE_OF_PAPER,
ITEM_ROCKET_PLAN,
ITEM_PADLOCK_KEY,
ITEM_RIB_CAGE,
ITEM_SKULL,
ITEM_LEG_BONE,
ITEM_BAT2,
ITEM_MAKESHIFT_TOCH,
ITEM_LIGHTER,
ITEM_GREEN_JEWEL,
ITEM_PICK,
ITEM_STONE_KEY,
ITEM_BLUE_JEWEL,
ITEM_CRYSTAL_SKULL,
ITEM_TREE_SAP,
ITEM_DINO_RAY_GUN,
ITEM_BRANCHES,
ITEM_WIG,
ITEM_TOWEL,
ITEM_OTHER_SHEET,
ITEM_SHEET,
ITEM_SHEET_ROPE,
ITEM_CROWBAR,
ITEM_COMEDY_BREASTS,
ITEM_DRESS,
ITEM_KEY2,
ITEM_CLOTHES,
ITEM_HAY,
ITEM_OIL,
ITEM_CHICKEN,
ITEM_LIT_TORCH,
ITEM_OPENED_DOG_FOOD,
ITEM_SOME_MONEY2,
ITEM_SOME_MORE_MONEY,
ITEM_PEELED_BANANA,
ITEM_STONE_DISC,
ITEM_GNARLED_VINE,
ITEM_FLINT,
ITEM_LIGHTER2,
ITEM_REST_OF_BEEF_JERKY,
ITEM_LOTS_OF_MONEY,
ITEM_HEAPS_OF_MONEY,
ITEM_OPEN_BOOK,
ITEM_REST_OF_THE_CHEESE_BITZ,
ITEM_SCISSORS,
ITEM_PENCIL,
ITEM_SUPER_WEENIE_SERUM,
ITEM_MUMMY_WRAPPINGS,
ITEM_COCONUT,
ITEM_ID_CARD,
ITEM_BIT_OF_STONE,
ITEM_CHUNK_OF_ROCK,
ITEM_BIG_STICK,
ITEM_STICKY_BIT_OF_STONE,
ITEM_STICKY_CHUNK_OF_ROCK,
ITEM_DEATH_MASK2,
ITEM_CHEFS_SURPRISE,
ITEM_STICKY_BAT,
ITEM_REST_OF_WRAPPINGS,
ITEM_BANANA2,
ITEM_MUG,
ITEM_FILE,
ITEM_POCKET_ROCKET_BLUEPRINTS,
ITEM_HAND_PUPPET,
ITEM_ARM_BONE,
ITEM_CROWN,
ITEM_COMIC_COUPON,
ITEM_TORN_PAGE
};
enum {
ROOM_JUNGLE_INSIDE_PLANE = 1,
ROOM_JUNGLE_OUTSIDE_PLANE = 2,
ROOM_JUNGLE_BRIDGE = 4,
ROOM_JUNGLE_GORILLA_1 = 6,
ROOM_JUNGLE_PINNACLE = 7,
ROOM_JUNGLE_SLOTH = 8,
ROOM_JUNGLE_BUD_SKIP = 9,
ROOM_JUNGLE_BEETLE = 11,
ROOM_JUNGLE_MISSIONARY = 13,
ROOM_JUNGLE_GORILLA_2 = 14,
ROOM_AMAZON_ENTRANCE = 16,
ROOM_AMAZON_HIDEOUT = 17,
ROOM_AMAZON_THRONE = 18,
ROOM_AMAZON_JAIL = 19,
ROOM_VILLAGE = 20,
ROOM_TRADER_BOBS = 21,
ROOM_FLODA_OUTSIDE = 22,
ROOM_FLODA_KITCHEN = 26,
ROOM_FLODA_LOCKERROOM = 27,
ROOM_FLODA_KLUNK = 30,
ROOM_FLODA_HENRY = 32,
ROOM_FLODA_OFFICE = 35,
ROOM_FLODA_JAIL = 41,
ROOM_FLODA_FRONTDESK = 103,
ROOM_TEMPLE_OUTSIDE = 43,
ROOM_TEMPLE_MUMMIES = 46,
ROOM_TEMPLE_ZOMBIES = 50,
ROOM_TEMPLE_TREE = 51,
ROOM_TEMPLE_SNAKE = 53,
ROOM_TEMPLE_LIZARD_LASER = 55,
ROOM_TEMPLE_MAZE = 58,
ROOM_TEMPLE_MAZE_2 = 59,
ROOM_TEMPLE_MAZE_3 = 60,
ROOM_TEMPLE_MAZE_4 = 61,
ROOM_TEMPLE_MAZE_5 = 100,
ROOM_TEMPLE_MAZE_6 = 101,
ROOM_VALLEY_CARCASS = 67,
ROOM_HOTEL_UPSTAIRS = 70,
ROOM_HOTEL_DOWNSTAIRS = 71,
ROOM_HOTEL_LOLA = 72,
ROOM_HOTEL_LOBBY = 73,
ROOM_CAR_CHASE = 74,
ROOM_FINAL_FIGHT = 69,
ROOM_INTRO_RITA_JOE_HEADS = 116,
ROOM_INTRO_EXPLOSION = 123,
//special
SPARKY_OUTSIDE_HOTEL = 77,
DEATH_MASK = 79,
IBI_LOGO = 82,
COMIC_1 = 87,
COMIC_2 = 88,
COMIC_3 = 89,
ROOM_UNUSED_INTRO_1 = 90,
ROOM_UNUSED_INTRO_2 = 91,
ROOM_UNUSED_INTRO_3 = 92,
ROOM_UNUSED_INTRO_4 = 93,
ROOM_UNUSED_INTRO_5 = 94,
FOTAQ_LOGO = 95,
WARNER_LOGO = 126,
FAYE_HEAD = 37,
AZURA_HEAD = 106,
FRANK_HEAD = 107,
ROOM_ENDING_CREDITS = 110,
ROOM_JOURNAL = 200 // dummy value to keep Display methods happy
};
//! GameState vars
enum {
VAR_HOTEL_ITEMS_REMOVED = 3,
VAR_JOE_DRESSING_MODE = 19,
VAR_BYPASS_ZOMBIES = 21,
VAR_BYPASS_FLODA_RECEPTIONIST = 35,
VAR_GUARDS_TURNED_ON = 85,
VAR_HOTEL_ESCAPE_STATE = 93,
VAR_INTRO_PLAYED = 117,
VAR_AZURA_IN_LOVE = 167
};
enum Verb {
VERB_NONE = 0,
VERB_PANEL_COMMAND_FIRST = 1,
VERB_OPEN = 1,
VERB_CLOSE = 2,
VERB_MOVE = 3,
// no verb 4
VERB_GIVE = 5,
VERB_USE = 6,
VERB_PICK_UP = 7,
VERB_LOOK_AT = 9,
VERB_TALK_TO = 8,
VERB_PANEL_COMMAND_LAST = 9,
VERB_WALK_TO = 10,
VERB_SCROLL_UP = 11,
VERB_SCROLL_DOWN = 12,
VERB_DIGIT_FIRST = 13,
VERB_DIGIT_1 = 13,
VERB_DIGIT_2 = 14,
VERB_DIGIT_3 = 15,
VERB_DIGIT_4 = 16,
VERB_DIGIT_LAST = 16,
VERB_INV_FIRST = VERB_DIGIT_FIRST,
VERB_INV_1 = VERB_DIGIT_1,
VERB_INV_2 = VERB_DIGIT_2,
VERB_INV_3 = VERB_DIGIT_3,
VERB_INV_4 = VERB_DIGIT_4,
VERB_INV_LAST = VERB_DIGIT_LAST,
VERB_USE_JOURNAL = 20,
VERB_SKIP_TEXT = 101,
VERB_PREP_WITH = 11,
VERB_PREP_TO = 12
};
} // End of namespace Queen
#endif

543
engines/queen/detection.cpp Normal file
View File

@@ -0,0 +1,543 @@
/* 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 "base/plugins.h"
#include "engines/advancedDetector.h"
#include "common/file.h"
#include "queen/defs.h"
#include "queen/detection.h"
#include "queen/version.h"
static const PlainGameDescriptor queenGames[] = {
{"queen", "Flight of the Amazon Queen"},
{nullptr, nullptr}
};
namespace Queen {
static const QueenGameDescription gameDescriptions[] = {
// Amiga Demo - English
{
{
"queen",
"Demo",
AD_ENTRY1s("queen.1", "f7a1a37ac93bf763b1569231237cb4d8", 563335),
Common::EN_ANY,
Common::kPlatformAmiga,
ADGF_DEMO,
GUIO1(GUIO_NOSPEECH)
},
},
// Amiga Interview Demo - English
{
{
"queen",
"Interview",
AD_ENTRY1s("queen.1", "f5d42a18d8f5689480413871410663d7", 597032),
Common::EN_ANY,
Common::kPlatformAmiga,
ADGF_DEMO,
GUIO1(GUIO_NOSPEECH)
},
},
// DOS Demo - English
{
{
"queen",
"Demo",
AD_ENTRY1s("queen.1", "f39334d8133840aa3bcbd733c12937cf", 3732177),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_DEMO,
GUIO1(GUIO_NOSPEECH)
},
},
// DOS Demo - English (from Bugreport #6946)
{
{
"queen",
"Demo Alt",
AD_ENTRY1s("queen.1", "2871fc6f8090f37fa1a0c556a1c97460", 3735447),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_DEMO,
GUIO1(GUIO_NOSPEECH)
},
},
// DOS Interview Demo - English
{
{
"queen",
"Interview",
AD_ENTRY1s("queen.1", "30b3291f37665bf24d9482b183cb2f67", 1915913),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_DEMO,
GUIO1(GUIO_NOSPEECH)
},
},
// DOS Interview Demo - Russian
{
{
"queen",
"Interview",
AD_ENTRY1s("queen.1c", "246dd55f475c9ea6524c556227fd0383", 1889658),
Common::RU_RUS,
Common::kPlatformDOS,
ADGF_DEMO,
GUIO1(GUIO_NOSPEECH)
},
},
// PCGAMES DOS Demo - English
{
{
"queen",
"Demo",
AD_ENTRY1s("queen.1", "f39334d8133840aa3bcbd733c12937cf", 3724538),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_DEMO,
GUIO1(GUIO_NOSPEECH)
},
},
// Amiga Floppy - English
{
{
"queen",
"Floppy",
AD_ENTRY1s("queen.1", "9c209c2cbc1730e3138663c4fd29c2e8", 351775),
Common::EN_ANY,
Common::kPlatformAmiga,
ADGF_NO_FLAGS,
GUIO1(GUIO_NOSPEECH)
},
},
// Amiga Floppy - German. Bugreport #12313
{
{
"queen",
"Floppy",
AD_ENTRY1s("queen.1", "b545c73010236dc022bad51c59120a75", 344575),
Common::DE_DEU,
Common::kPlatformAmiga,
ADGF_NO_FLAGS,
GUIO1(GUIO_NOSPEECH)
},
},
// DOS Floppy - English
{
{
"queen",
"Floppy",
AD_ENTRY1s("queen.1", "979a33954634fae674b59711ef423f40", 22677657),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_TAILMD5,
GUIO1(GUIO_NOSPEECH)
},
},
// DOS CD - English
{
{
"queen",
"CD",
AD_ENTRY1s("queen.1", "b6302bccf70463de3d5faf0f0628f742", 190787021),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_ALT_INTRO)
},
},
// DOS Floppy - French
{
{
"queen",
"Floppy",
AD_ENTRY1s("queen.1", "f5e827645d3c887be3bdf4729d847756", 22157304),
Common::FR_FRA,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GUIO_NOSPEECH)
},
},
// DOS Floppy - Russian (From Bugreport #6946) - Saveliev Translation
{
{
"queen",
"Floppy",
AD_ENTRY1s("queen.1", "6e30974bfab8e5f5180363831d204ba0", 22677657),
Common::RU_RUS,
Common::kPlatformDOS,
ADGF_TAILMD5,
GUIO1(GUIO_NOSPEECH)
},
},
// DOS CD - French
{
{
"queen",
"CD",
AD_ENTRY1s("queen.1", "6fd5486a0db75bae2e023b575c3d6a5d", 186689095),
Common::FR_FRA,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_ALT_INTRO)
},
},
// DOS Floppy - German
{
{
"queen",
"Floppy",
AD_ENTRY1s("queen.1", "f5e827645d3c887be3bdf4729d847756", 22240013),
Common::DE_DEU,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GUIO_NOSPEECH)
},
},
// DOS CD - German
{
{
"queen",
"CD",
AD_ENTRY1s("queen.1", "551d595be8af890fc4cb8533c9c5f5f1", 217648975),
Common::DE_DEU,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_ALT_INTRO)
},
},
// DOS CD - Hebrew
{
{
"queen",
"CD",
AD_ENTRY1s("queen.1", "b6302bccf70463de3d5faf0f0628f742", 190705558),
Common::HE_ISR,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO2(GAMEOPTION_ALT_INTRO, GAMEOPTION_ALT_FONT)
},
},
// DOS Floppy - Italian
{
{
"queen",
"Floppy",
AD_ENTRY1s("queen.1", "f5e827645d3c887be3bdf4729d847756", 22461366),
Common::IT_ITA,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GUIO_NOSPEECH)
},
},
// DOS CD - Italian
{
{
"queen",
"CD",
AD_ENTRY1s("queen.1", "b6302bccf70463de3d5faf0f0628f742", 190795582),
Common::IT_ITA,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_ALT_INTRO)
},
},
// DOS CD - Spanish
{
{
"queen",
"CD",
AD_ENTRY1s("queen.1", "b6302bccf70463de3d5faf0f0628f742", 190730602),
Common::ES_ESP,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_ALT_INTRO)
},
},
// DOS CD - English (Compressed Freeware Release v1.0)
{
{
"queen",
"CD",
AD_ENTRY1s("queen.1c", "a0749bb8b72e537ead1a63a3dde1443d", 54108887),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_ALT_INTRO)
},
},
// DOS CD - English (Compressed Freeware Release v1.1)
{
{
"queen",
"CD",
AD_ENTRY1s("queen.1c", "21fd690b372f8a6289f6f33bc986276c", 51222412),
Common::EN_ANY,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_ALT_INTRO)
},
},
// DOS CD - French (Compressed Freeware Release v1.0)
{
{
"queen",
"CD",
AD_ENTRY1s("queen.1c", "67e3020f8a35e1df7b1c753b5aaa71e1", 97382620),
Common::FR_FRA,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_ALT_INTRO)
},
},
// DOS CD - German (Compressed Freeware Release v1.0)
{
{
"queen",
"CD",
AD_ENTRY1s("queen.1c", "28f78dbec7e20f603a10c2f8ea889a5c", 108738717),
Common::DE_DEU,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_ALT_INTRO)
},
},
// DOS CD - Hebrew (Compressed Freeware Release v1.0)
{
{
"queen",
"CD",
AD_ENTRY1s("queen.1c", "4d52d8780613ef27a2b779caecb20a21", 99391805),
Common::HE_ISR,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO2(GAMEOPTION_ALT_INTRO, GAMEOPTION_ALT_FONT)
},
},
// DOS CD - Hebrew
{
{
"queen",
"CD",
AD_ENTRY1s("queen.1c", "a88effce52227a1842636ce753c2e646", 100455438),
Common::HE_ISR,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO2(GAMEOPTION_ALT_INTRO, GAMEOPTION_ALT_FONT)
},
},
// DOS CD - Italian (Compressed Freeware Release v1.0)
{
{
"queen",
"CD",
AD_ENTRY1s("queen.1c", "2f72b715ed753cf905a37cdcc7ea611e", 98327801),
Common::IT_ITA,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_ALT_INTRO)
},
},
// DOS CD - Hungarian (Compressed Freeware Release v1.02)
{
{
"queen",
"CD",
AD_ENTRY1s("queen.1c", "21fd690b372f8a6289f6f33bc986276c", 51329031),
Common::HU_HUN,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_ALT_INTRO)
},
},
// DOS CD - Russian (Compressed Freeware Release v1.0)
{
{
"queen",
"CD",
AD_ENTRY1s("queen.1c", "908d04940d40537d32c50a8429cd8631", 51222412),
Common::RU_RUS,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_ALT_INTRO)
},
},
// TODO: Freeware Release for Spanish DOS CD is missing.
#if 0
// DOS CD - Spanish (Compressed Freeware Release v1.0)
{
{
"queen",
"CD",
AD_ENTRY1s("queen.1c", NULL, ?),
Common::ES_ESP,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_ALT_INTRO)
},
},
#endif
// GoG.com Release - German
{
{
"queen",
"GOG.com",
AD_ENTRY1s("queen.1", "28f78dbec7e20f603a10c2f8ea889a5c", 108738717),
Common::DE_DEU,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_ALT_INTRO)
},
},
// GoG.com Release - French
{
{
"queen",
"GOG.com",
AD_ENTRY1s("queen.1", "67e3020f8a35e1df7b1c753b5aaa71e1", 97382620),
Common::FR_FRA,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_ALT_INTRO)
},
},
// GoG.com Release - Italian
{
{
"queen",
"GOG.com",
AD_ENTRY1s("queen.1", "2f72b715ed753cf905a37cdcc7ea611e", 98327801),
Common::IT_ITA,
Common::kPlatformDOS,
ADGF_NO_FLAGS,
GUIO1(GAMEOPTION_ALT_INTRO)
},
},
{ AD_TABLE_END_MARKER }
};
} // End of namespace Queen
class QueenMetaEngineDetection : public AdvancedMetaEngineDetection<Queen::QueenGameDescription> {
public:
QueenMetaEngineDetection() : AdvancedMetaEngineDetection(Queen::gameDescriptions, queenGames) {
}
const char *getName() const override {
return "queen";
}
const char *getEngineName() const override {
return "Flight of the Amazon Queen";
}
const char *getOriginalCopyright() const override {
return "Flight of the Amazon Queen (C) John Passfield and Steve Stamatiadis";
}
ADDetectedGame fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist, ADDetectedGameExtraInfo **extra) const override;
};
ADDetectedGame QueenMetaEngineDetection::fallbackDetect(const FileMap &allFiles, const Common::FSList &fslist, ADDetectedGameExtraInfo **extra) const {
static ADGameDescription desc;
// Iterate over all files in the given directory
for (Common::FSList::const_iterator file = fslist.begin(); file != fslist.end(); ++file) {
if (file->isDirectory()) {
continue;
}
if (file->getName().equalsIgnoreCase("queen.1") || file->getName().equalsIgnoreCase("queen.1c")) {
Common::File dataFile;
if (!dataFile.open(*file)) {
continue;
}
Queen::DetectedGameVersion version;
if (Queen::detectVersion(&version, &dataFile)) {
desc.gameId = "queen";
desc.language = version.language;
desc.platform = version.platform;
desc.flags = ADGF_NO_FLAGS;
desc.guiOptions = GUIO0();
if (version.features & Queen::GF_DEMO) {
desc.extra = "Demo";
desc.flags = ADGF_DEMO;
desc.guiOptions = GUIO_NOSPEECH;
} else if (version.features & Queen::GF_INTERVIEW) {
desc.extra = "Interview";
desc.flags = ADGF_DEMO;
desc.guiOptions = GUIO_NOSPEECH;
} else if (version.features & Queen::GF_FLOPPY) {
desc.extra = "Floppy";
desc.guiOptions = GUIO_NOSPEECH;
} else if (version.features & Queen::GF_TALKIE) {
desc.extra = "CD";
desc.guiOptions = GAMEOPTION_ALT_INTRO;
if (desc.language == Common::HE_ISR)
desc.guiOptions = GUIO2(GAMEOPTION_ALT_INTRO, GAMEOPTION_ALT_FONT);
}
return ADDetectedGame(&desc);
}
}
}
return ADDetectedGame();
}
REGISTER_PLUGIN_STATIC(QUEEN_DETECTION, PLUGIN_TYPE_ENGINE_DETECTION, QueenMetaEngineDetection);

38
engines/queen/detection.h Normal file
View File

@@ -0,0 +1,38 @@
/* 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 QUEEN_DETECTION_H
#define QUEEN_DETECTION_H
namespace Queen {
struct QueenGameDescription {
AD_GAME_DESCRIPTION_HELPERS(desc);
ADGameDescription desc;
};
#define GAMEOPTION_ALT_INTRO GUIO_GAMEOPTIONS1
#define GAMEOPTION_ALT_FONT GUIO_GAMEOPTIONS2
} // End of namespace Queen
#endif // QUEEN_DETECTION_H

1839
engines/queen/display.cpp Normal file

File diff suppressed because it is too large Load Diff

284
engines/queen/display.h Normal file
View File

@@ -0,0 +1,284 @@
/* 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 QUEEN_DISPLAY_H
#define QUEEN_DISPLAY_H
#include "common/str.h"
#include "common/util.h"
#include "common/random.h"
#include "queen/defs.h"
class OSystem;
namespace Common {
struct Rect;
}
namespace Queen {
class QueenEngine;
class Display {
public:
Display(QueenEngine *vm, OSystem *system);
~Display();
//! initialize dynalum for the specified room
void dynalumInit(const char *roomName, uint16 roomNum);
//! update dynalum for the current room
void dynalumUpdate(int16 x, int16 y);
//! update the palette
void palSet(const uint8 *pal, int start, int end, bool updateScreen = false);
//! setup palette for Joe's dress
void palSetJoeDress();
//! setup palette for Joe's normal clothes
void palSetJoeNormal();
//! setup palette for panel and inventory objects
void palSetPanel();
//! fade the current palette in
void palFadeIn(uint16 roomNum, bool dynalum = false, int16 dynaX = 0, int16 dynaY = 0);
//! fade the current palette out
void palFadeOut(uint16 roomNum);
//! grey the panel area (used when panel is disabled)
void palGreyPanel();
//! scroll some palette colors
void palScroll(int start, int end);
void palSetAmigaColor(uint8 color, uint16 rgb);
//! custom palette effect for the specified room
void palCustomColors(uint16 roomNum);
//! custom palette scroll for the specified room
void palCustomScroll(uint16 roomNum);
//! process a 'palette flash' effect
void palCustomFlash();
void palCustomLightsOff(uint16 roomNum);
void palCustomLightsOn(uint16 roomNum);
//! mark all palette entries as dirty
void palSetAllDirty() { _pal.dirtyMin = 0; _pal.dirtyMax = 255; }
//! returns the number of colors used by the room
int getNumColorsForRoom(uint16 room) const;
//! returns true if we shouldn't fade the palette in the specified room
bool isPalFadingDisabled(uint16 room) const;
//! change fullscreen/panel mode
void screenMode(int comPanel, bool inCutaway);
void prepareUpdate();
void update(bool dynalum = false, int16 dynaX = 0, int16 dynaY = 0);
void setupPanel();
void setupNewRoom(const char *name, uint16 room);
void drawBobSprite(const uint8 *data, uint16 x, uint16 y, uint16 w, uint16 h, uint16 pitch, bool xflip);
void drawBobPasteDown(const uint8 *data, uint16 x, uint16 y, uint16 w, uint16 h);
void drawInventoryItem(const uint8 *data, uint16 x, uint16 y, uint16 w, uint16 h);
void blit(uint8 *dstBuf, uint16 dstPitch, uint16 x, uint16 y, const uint8 *srcBuf, uint16 srcPitch, uint16 w, uint16 h, bool xflip, bool masked);
void fill(uint8 *dstBuf, uint16 dstPitch, uint16 x, uint16 y, uint16 w, uint16 h, uint8 color);
//! decode PCX picture data
void decodePCX(const uint8 *src, uint32 srcSize, uint8 *dst, uint16 dstPitch, uint16 *w, uint16 *h, uint8 *pal, uint16 palStart, uint16 palEnd);
//! decode IFF picture data
void decodeIFF(const uint8 *src, uint32 srcSize, uint8 *dst, uint16 dstPitch, uint16 *w, uint16 *h, uint8 *pal, uint16 palStart, uint16 palEnd, uint8 colorBase = 0);
void horizontalScrollUpdate(int16 xCamera);
void horizontalScroll(int16 scroll);
int16 horizontalScroll() const { return _horizontalScroll; }
void fullscreen(bool fs) { _fullRefresh = 2; _fullscreen = fs; }
bool fullscreen() const { return _fullscreen; }
//! mark the specified block as dirty
void setDirtyBlock(uint16 x, uint16 y, uint16 w, uint16 h);
//! force a full refresh (bypassing the dirtyblocks rendering), on next screen update
void forceFullRefresh() { _fullRefresh = 2; }
//! change mouse cursor bitmap
void setMouseCursor(uint8 *buf, uint16 w, uint16 h);
//! show/hide mouse cursor
void showMouseCursor(bool show);
//! initialize font, compute justification sizes
void initFont();
//! add the specified text to the texts list
void setText(uint16 x, uint16 y, const char *text, bool outlined = true);
//! add the specified text to the texts list
void setTextCentered(uint16 y, const char *text, bool outlined = true);
//! draw the text lists
void drawTexts();
//! remove entries from the texts list
void clearTexts(uint16 y1, uint16 y2);
void setupInkColors();
uint8 getInkColor(InkColor color) const { return _inkColors[color]; }
//! change the current text color
void textCurrentColor(uint8 color) { _curTextColor = color; }
//! change the text color for the specified texts list entry
void textColor(uint16 y, uint8 color) { _texts[y].color = color; }
//! Set the focus rectangle to the speaking character
void setFocusRect(const Common::Rect& rect);
int textCenterX(const char *text) const;
uint16 textWidth(const char *text) const;
uint16 textWidth(const char *text, uint16 len) const;
void drawChar(uint16 x, uint16 y, uint8 color, const uint8 *chr);
void drawText(uint16 x, uint16 y, uint8 color, const char *text, bool outlined = true);
void drawBox(int16 x1, int16 y1, int16 x2, int16 y2, uint8 col);
void shake(bool reset);
void blankScreen();
void blankScreenEffect1();
void blankScreenEffect2();
void blankScreenEffect3();
private:
enum {
FADE_SPEED = 16,
D_BLOCK_W = 8,
D_BLOCK_H = 8
};
enum BufferDimension {
BACKDROP_W = 640,
BACKDROP_H = 200,
SCREEN_W = 320,
SCREEN_H = 200,
PANEL_W = 320,
PANEL_H = 50
};
struct {
uint8 *room;
uint8 *screen;
uint8 *panel;
int dirtyMin, dirtyMax;
bool scrollable;
} _pal;
struct Dynalum {
bool valid;
uint8 *mskBuf;
uint32 mskSize;
int8 *lumBuf;
uint32 lumSize;
uint8 prevColMask;
void clear() {
valid = false;
mskBuf = nullptr;
mskSize = 0;
lumBuf = nullptr;
lumSize = 0;
prevColMask = 0;
}
};
struct TextSlot {
uint16 x;
uint8 color;
Common::String text;
bool outlined;
void clear() {
x = 0;
color = 0;
text = "";
outlined = false;
}
};
uint8 *_screenBuf;
uint8 *_panelBuf;
uint8 *_backdropBuf;
uint8 _fullRefresh;
uint8 *_dirtyBlocks;
uint16 _dirtyBlocksWidth, _dirtyBlocksHeight;
bool _fullscreen;
uint16 _horizontalScroll;
uint16 _bdWidth, _bdHeight;
const char *_imageExt;
//! texts list
TextSlot _texts[GAME_SCREEN_HEIGHT];
//! current text color to use for display
uint8 _curTextColor;
//! font justification sizes
uint8 _charWidth[256];
uint8 _inkColors[INK_COUNT];
Common::RandomSource _rnd;
Dynalum _dynalum;
OSystem *_system;
QueenEngine *_vm;
const uint8 *_font;
static const uint8 _fontRegular[];
static const uint8 _fontHebrew[];
static const uint8 _fontHebrewAdvBrew[];
static const uint8 _fontRussian[];
static const uint8 _fontGreek[];
static const uint8 _palJoeClothes[];
static const uint8 _palJoeDress[];
};
} // End of namespace Queen
#endif

1629
engines/queen/graphics.cpp Normal file

File diff suppressed because it is too large Load Diff

283
engines/queen/graphics.h Normal file
View File

@@ -0,0 +1,283 @@
/* 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 QUEEN_GRAPHICS_H
#define QUEEN_GRAPHICS_H
#include "common/util.h"
#include "queen/structs.h"
namespace Queen {
struct BobSlot {
bool active;
//! current position
int16 x, y;
//! bounding box
Box box;
bool xflip;
//! shrinking percentage
uint16 scale;
//! associated BobFrame
uint16 frameNum;
//! 'direction' for the next frame (-1, 1)
int frameDir;
//! animation stuff
bool animating;
struct {
int16 speed, speedBak;
//! string based animation
struct {
const AnimFrame *buffer;
const AnimFrame *curPos;
} string;
//! normal moving animation
struct {
bool rebound;
uint16 firstFrame, lastFrame;
} normal;
} anim;
bool moving;
//! moving speed
int16 speed;
//! move along x axis instead of y
bool xmajor;
//! moving direction
int8 xdir, ydir;
//! destination point
int16 endx, endy;
uint16 dx, dy;
uint16 total;
void curPos(int16 xx, int16 yy);
void move(int16 dstx, int16 dsty, int16 spd);
void moveOneStep();
void animOneStep();
void animString(const AnimFrame *animBuf);
void animNormal(uint16 firstFrame, uint16 lastFrame, uint16 speed, bool rebound, bool xflip);
void scaleWalkSpeed(uint16 ms);
void clear();
void clear(const Box *defaultBox);
};
class QueenEngine;
class Graphics {
public:
Graphics(QueenEngine *vm);
~Graphics();
//! unpacks control frames (ie. arrows)
void unpackControlBank();
//! setup dialog arrows
void setupArrows();
//! setup mouse cursor
void setupMouseCursor();
//! draw a bob
void drawBob(const BobSlot *bs, const BobFrame *bf, const Box *box, int16 x, int16 y);
//! draw an inventory item
void drawInventoryItem(uint32 frameNum, uint16 x, uint16 y);
//! draw a bob directly on the backdrop bitmap
void pasteBob(uint16 objNum, uint16 image);
//! resize a bobframe
void shrinkFrame(const BobFrame *bf, uint16 percentage);
//! animate/move bobs and sort them
void sortBobs();
//! draw all the sorted bobs
void drawBobs();
//! clear all setup bobs
void clearBobs();
//! stop all animating/movings bobs
void stopBobs();
//! returns a reference to the specified bob
BobSlot *bob(int index);
void clearBob(int index) { bob(index)->clear(&_defaultBox); }
//! display a text 'near' the specified bob
void setBobText(const BobSlot *bob, const char *text, int textX, int textY, int color, int flags);
//! handles parallax scrolling for the specified room
void handleParallax(uint16 roomNum);
void setupNewRoom(const char *room, uint16 roomNum, int16 *furniture, uint16 furnitureCount);
void setBobCutawayAnim(uint16 bobNum, bool xflip, const AnimFrame *af, uint8 frameCount);
void fillAnimBuffer(const char *anim, AnimFrame *af);
uint16 countAnimFrames(const char *anim);
void setupObjectAnim(const GraphicData *gd, uint16 firstImage, uint16 bobNum, bool visible);
uint16 setupPersonAnim(const ActorData *ad, const char *anim, uint16 curImage);
void resetPersonAnim(uint16 bobNum);
void erasePersonAnim(uint16 bobNum);
void eraseAllAnims();
uint16 refreshObject(uint16 obj);
void setupRoomFurniture(int16 *furniture, uint16 furnitureCount);
void setupRoomObjects();
uint16 setupPerson(uint16 noun, uint16 curImage);
uint16 allocPerson(uint16 noun, uint16 curImage);
uint16 personFrames(uint16 bobNum) const { return _personFrames[bobNum]; }
void clearPersonFrames() { memset(_personFrames, 0, sizeof(_personFrames)); }
uint16 numFrames() const { return _numFrames; }
uint16 numStaticFurniture() const { return _numFurnitureStatic; }
uint16 numAnimatedFurniture() const { return _numFurnitureAnimated; }
uint16 numFurnitureFrames() const { return _numFurnitureStatic + _numFurnitureAnimatedLen; }
void putCameraOnBob(int bobNum) { _cameraBob = bobNum; }
void update(uint16 room);
enum {
ARROW_BOB_UP = 62,
ARROW_BOB_DOWN = 63,
MAX_BOBS_NUMBER = 64,
MAX_STRING_LENGTH = 255,
MAX_STRING_SIZE = (MAX_STRING_LENGTH + 1),
BOB_SHRINK_BUF_SIZE = 60000
};
private:
BobSlot _bobs[MAX_BOBS_NUMBER];
//! bobs to display
BobSlot *_sortedBobs[MAX_BOBS_NUMBER];
//! number of bobs to display
uint16 _sortedBobsCount;
//! used to scale a BobFrame
BobFrame _shrinkBuffer;
//! in-game objects/persons animations
AnimFrame _newAnim[17][30];
//! cutaway objects/persons animations
AnimFrame _cutAnim[21][30];
uint16 _personFrames[4];
//! number of animated furniture in current room
uint16 _numFurnitureAnimated;
//! number of static furniture in current room
uint16 _numFurnitureStatic;
//! total number of frames for the animated furniture
uint16 _numFurnitureAnimatedLen;
//! current number of frames unpacked
uint16 _numFrames;
//! bob number followed by camera
int _cameraBob;
QueenEngine *_vm;
const Box _defaultBox;
const Box _gameScreenBox;
const Box _fullScreenBox;
};
class BamScene {
public:
BamScene(QueenEngine *vm);
void playSfx();
void prepareAnimation();
void updateCarAnimation();
void updateFightAnimation();
void saveState(byte *&ptr);
void loadState(uint32 ver, byte *&ptr);
enum {
BOB_OBJ1 = 5,
BOB_OBJ2 = 6,
BOB_FX = 7
};
enum {
F_STOP = 0,
F_PLAY = 1,
F_REQ_STOP = 2
};
uint16 _flag, _index;
private:
struct BamDataObj {
int16 x, y;
int16 frame;
};
struct BamDataBlock {
BamDataObj obj1; // truck / Frank
BamDataObj obj2; // Rico / robot
BamDataObj fx;
int16 sfx;
};
BobSlot *_obj1;
BobSlot *_obj2;
BobSlot *_objfx;
bool _screenShaked;
const BamDataBlock *_fightData;
uint16 _lastSoundIndex;
QueenEngine *_vm;
static const BamDataBlock _carData[];
static const BamDataBlock _fight1Data[];
static const BamDataBlock _fight2Data[];
static const BamDataBlock _fight3Data[];
static const BamDataBlock _fight4Data[];
};
} // End of namespace Queen
#endif

271
engines/queen/grid.cpp Normal file
View File

@@ -0,0 +1,271 @@
/* 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 "queen/grid.h"
#include "queen/display.h"
#include "queen/logic.h"
#include "queen/queen.h"
#include "common/debug.h"
namespace Queen {
Grid::Grid(QueenEngine *vm)
: _vm(vm) {
memset(_zones, 0, sizeof(_zones));
}
Grid::~Grid() {
delete[] _objMax;
delete[] _areaMax;
delete[] _area;
delete[] _objectBox;
}
void Grid::readDataFrom(uint16 numObjects, uint16 numRooms, byte *&ptr) {
uint16 i, j;
_numRoomAreas = numRooms;
_objMax = new int16[_numRoomAreas + 1];
_areaMax = new int16[_numRoomAreas + 1];
_area = new Area[_numRoomAreas + 1][MAX_AREAS_NUMBER];
_objMax[0] = 0;
_areaMax[0] = 0;
// _area[0][] cleared by default constructor
for (i = 1; i <= _numRoomAreas; i++) {
_objMax[i] = (int16)READ_BE_INT16(ptr); ptr += 2;
_areaMax[i] = (int16)READ_BE_INT16(ptr); ptr += 2;
// _area[i][0] cleared by default constructor
for (j = 1; j <= _areaMax[i]; j++) {
assert(j < MAX_AREAS_NUMBER);
_area[i][j].readFromBE(ptr);
}
}
_objectBox = new Box[numObjects + 1];
// _objectBox[0] cleared by default constructor
for (i = 1; i <= numObjects; i++) {
_objectBox[i].readFromBE(ptr);
}
}
void Grid::setZone(GridScreen screen, uint16 zoneNum, uint16 x1, uint16 y1, uint16 x2, uint16 y2) {
debug(9, "Grid::setZone(%d, %d, (%d,%d), (%d,%d))", screen, zoneNum, x1, y1, x2, y2);
assert(zoneNum < MAX_ZONES_NUMBER);
ZoneSlot *pzs = &_zones[screen][zoneNum];
pzs->valid = true;
pzs->box.x1 = x1;
pzs->box.y1 = y1;
pzs->box.x2 = x2;
pzs->box.y2 = y2;
}
void Grid::setZone(GridScreen screen, uint16 zoneNum, const Box &box) {
debug(9, "Grid::setZone(%d, %d, (%d,%d), (%d,%d))", screen, zoneNum, box.x1, box.y1, box.x2, box.y2);
assert(zoneNum < MAX_ZONES_NUMBER);
ZoneSlot *pzs = &_zones[screen][zoneNum];
pzs->valid = true;
pzs->box = box;
}
uint16 Grid::findZoneForPos(GridScreen screen, uint16 x, uint16 y) const {
debug(9, "Logic::findZoneForPos(%d, (%d,%d))", screen, x, y);
int i;
if (screen == GS_PANEL) {
y -= ROOM_ZONE_HEIGHT;
}
for (i = 1; i < MAX_ZONES_NUMBER; ++i) {
const ZoneSlot *pzs = &_zones[screen][i];
if (pzs->valid && pzs->box.contains(x, y)) {
return i;
}
}
return 0;
}
uint16 Grid::findAreaForPos(GridScreen screen, uint16 x, uint16 y) const {
uint16 room = _vm->logic()->currentRoom();
uint16 zoneNum = findZoneForPos(screen, x, y);
if (zoneNum <= _objMax[room]) {
zoneNum = 0;
} else {
zoneNum -= _objMax[room];
}
return zoneNum;
}
void Grid::clear(GridScreen screen) {
debug(9, "Grid::clear(%d)", screen);
for (int i = 1; i < MAX_ZONES_NUMBER; ++i) {
_zones[screen][i].valid = false;
}
}
void Grid::setupNewRoom(uint16 room, uint16 firstRoomObjNum) {
debug(9, "Grid::setupNewRoom()");
clear(GS_ROOM);
uint16 i;
uint16 zoneNum;
// setup objects zones
uint16 maxObjRoom = _objMax[room];
zoneNum = 1;
for (i = firstRoomObjNum + 1; i <= firstRoomObjNum + maxObjRoom; ++i) {
if (_vm->logic()->objectData(i)->name != 0) {
if (room == 41 && i == 303) {
// WORKAROUND bug #2913: In the room 41, the bounding box of the
// stairs (object 303) doesn't match with the room picture. With the
// original box dimensions, Joe could walk "above" the stairs, giving
// the impression of floating in the air.
// To fix this, the bounding box is set relative to the position of
// the cabinet (object 295).
uint16 y1 = _objectBox[295].y2 + 1;
setZone(GS_ROOM, zoneNum, _objectBox[i].x1, y1, _objectBox[i].x2, _objectBox[i].y2);
} else {
setZone(GS_ROOM, zoneNum, _objectBox[i]);
}
}
++zoneNum;
}
// setup room zones (areas)
uint16 maxAreaRoom = _areaMax[room];
for (zoneNum = 1; zoneNum <= maxAreaRoom; ++zoneNum) {
setZone(GS_ROOM, maxObjRoom + zoneNum, _area[room][zoneNum].box);
}
}
void Grid::setupPanel() {
for (int i = 0; i <= 7; ++i) {
uint16 x = i * 20;
setZone(GS_PANEL, i + 1, x, 10, x + 19, 49);
}
// inventory scrolls
setZone(GS_PANEL, 9, 160, 10, 179, 29);
setZone(GS_PANEL, 10, 160, 30, 179, 49);
// inventory items
setZone(GS_PANEL, 11, 180, 10, 213, 49);
setZone(GS_PANEL, 12, 214, 10, 249, 49);
setZone(GS_PANEL, 13, 250, 10, 284, 49);
setZone(GS_PANEL, 14, 285, 10, 320, 49);
}
void Grid::drawZones() {
for (int i = 1; i < MAX_ZONES_NUMBER; ++i) {
const ZoneSlot *pzs = &_zones[GS_ROOM][i];
if (pzs->valid) {
const Box *b = &pzs->box;
_vm->display()->drawBox(b->x1, b->y1, b->x2, b->y2, 3);
}
}
}
const Box *Grid::zone(GridScreen screen, uint16 index) const {
const ZoneSlot *zs = &_zones[screen][index];
assert(zs->valid);
return &zs->box;
}
Verb Grid::findVerbUnderCursor(int16 cursorx, int16 cursory) const {
static const Verb pv[] = {
VERB_NONE,
VERB_OPEN,
VERB_CLOSE,
VERB_MOVE,
VERB_GIVE,
VERB_LOOK_AT,
VERB_PICK_UP,
VERB_TALK_TO,
VERB_USE,
VERB_SCROLL_UP,
VERB_SCROLL_DOWN,
VERB_INV_1,
VERB_INV_2,
VERB_INV_3,
VERB_INV_4,
};
return pv[findZoneForPos(GS_PANEL, cursorx, cursory)];
}
uint16 Grid::findObjectUnderCursor(int16 cursorx, int16 cursory) const {
uint16 roomObj = 0;
if (cursory < ROOM_ZONE_HEIGHT) {
int16 x = cursorx + _vm->display()->horizontalScroll();
roomObj = findZoneForPos(GS_ROOM, x, cursory);
}
return roomObj;
}
uint16 Grid::findObjectNumber(uint16 zoneNum) const {
// l.316-327 select.c
uint16 room = _vm->logic()->currentRoom();
uint16 obj = zoneNum;
uint16 objectMax = _objMax[room];
debug(9, "Grid::findObjectNumber(%X, %X)", zoneNum, objectMax);
if (zoneNum > objectMax) {
// this is an area box, check for associated object
obj = _area[room][zoneNum - objectMax].object;
if (obj != 0) {
// there is an object, get its number
obj -= _vm->logic()->currentRoomData();
}
}
return obj;
}
uint16 Grid::findScale(uint16 x, uint16 y) const {
uint16 room = _vm->logic()->currentRoom();
uint16 scale = 100;
uint16 areaNum = findAreaForPos(GS_ROOM, x, y);
if (areaNum != 0) {
scale = _area[room][areaNum].calcScale(y);
}
return scale;
}
void Grid::saveState(byte *&ptr) {
uint16 i, j;
for (i = 1; i <= _numRoomAreas; ++i) {
for (j = 1; j <= _areaMax[i]; ++j) {
_area[i][j].writeToBE(ptr);
}
}
}
void Grid::loadState(uint32 ver, byte *&ptr) {
uint16 i, j;
for (i = 1; i <= _numRoomAreas; ++i) {
for (j = 1; j <= _areaMax[i]; ++j) {
_area[i][j].readFromBE(ptr);
}
}
}
} // End of namespace Queen

135
engines/queen/grid.h Normal file
View File

@@ -0,0 +1,135 @@
/* 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 QUEEN_GRID_H
#define QUEEN_GRID_H
#include "common/util.h"
#include "queen/structs.h"
namespace Queen {
enum GridScreen {
GS_ROOM = 0,
GS_PANEL = 1,
GS_COUNT = 2
};
class QueenEngine;
class Grid {
public:
Grid(QueenEngine *vm);
~Grid();
//! read areas data from specified stream
void readDataFrom(uint16 numObjects, uint16 numRooms, byte *&ptr);
//! defines a new zone
void setZone(GridScreen screen, uint16 zoneNum, uint16 x1, uint16 y1, uint16 x2, uint16 y2);
//! defines a new zone
void setZone(GridScreen screen, uint16 zoneNum, const Box &box);
//! find the zone number containing the specified point
uint16 findZoneForPos(GridScreen screen, uint16 x, uint16 y) const;
//! find the area number containing the specified point
uint16 findAreaForPos(GridScreen screen, uint16 x, uint16 y) const;
//! clear the zones for current room
void clear(GridScreen screen);
//! setup objects zones for the specified room
void setupNewRoom(uint16 room, uint16 firstRoomObjNum);
//! setup panel zones
void setupPanel();
//! draw the zones for current room (debug only)
void drawZones();
//! retuns a reference to the specified zone
const Box *zone(GridScreen screen, uint16 index) const;
//! get the verb for the specified cursor position
Verb findVerbUnderCursor(int16 cursorx, int16 cursory) const;
//! get the object for the specified cursor position
uint16 findObjectUnderCursor(int16 cursorx, int16 cursory) const;
//! get the object for the specified zone number
uint16 findObjectNumber(uint16 zoneNum) const;
//! get scale for the specified position
uint16 findScale(uint16 x, uint16 y) const;
//! returns a reference to the specfied room area
Area *area(int room, int num) const { return &_area[room][num]; }
//! returns the number of areas in this room
uint16 areaMax(int room) const { return _areaMax[room]; }
//! returns the number of objects in this room
uint16 objMax(int room) const { return _objMax[room]; }
void saveState(byte *&ptr);
void loadState(uint32 ver, byte *&ptr);
enum {
MAX_ZONES_NUMBER = 32,
MAX_AREAS_NUMBER = 11
};
private:
struct ZoneSlot {
bool valid;
Box box;
};
//! current room zones
ZoneSlot _zones[GS_COUNT][MAX_ZONES_NUMBER];
//! number of objects for each room
int16 *_objMax;
//! number of areas for each room
int16 *_areaMax;
//! areas for each room
Area (*_area)[MAX_AREAS_NUMBER];
//! total number of room areas
uint16 _numRoomAreas;
//! box/zone for each objects
Box *_objectBox;
QueenEngine *_vm;
};
} // End of namespace Queen
#endif

176
engines/queen/input.cpp Normal file
View File

@@ -0,0 +1,176 @@
/* 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/events.h"
#include "common/system.h"
#include "common/textconsole.h"
#include "queen/queen.h"
#include "queen/input.h"
namespace Queen {
const verbAction Input::_verbActions[] = {
{ VERB_OPEN, kActionOpen },
{ VERB_CLOSE, kActionClose },
{ VERB_MOVE, kActionMove },
{ VERB_GIVE, kActionGive },
{ VERB_LOOK_AT, kActionLook },
{ VERB_PICK_UP, kActionPickUp },
{ VERB_TALK_TO, kActionTalk },
{ VERB_USE, kActionUse }
};
Input::Input(Common::Language language, OSystem *system) :
_system(system), _eventMan(system->getEventManager()), _fastMode(false),
_keyVerb(VERB_NONE), _cutawayRunning(false), _canQuit(false),
_cutawayQuit(false), _dialogueRunning(false), _talkQuit(false),
_quickSave(false), _quickLoad(false), _inKey(kActionNone),
_mouseButton(0), _idleTime(0) {
}
void Input::delay(uint amount) {
if (_fastMode && amount > DELAY_SHORT) {
amount = DELAY_SHORT;
}
if (_idleTime < DELAY_SCREEN_BLANKER) {
_idleTime += amount;
}
uint32 end = _system->getMillis() + amount;
do {
Common::Event event;
while (_eventMan->pollEvent(event)) {
_idleTime = 0;
switch (event.type) {
case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
if (event.customType == kActionFastMode) {
_fastMode = !_fastMode;
} else {
_inKey = event.customType;
}
break;
case Common::EVENT_LBUTTONDOWN:
_mouseButton |= MOUSE_LBUTTON;
break;
case Common::EVENT_RBUTTONDOWN:
_mouseButton |= MOUSE_RBUTTON;
if (_dialogueRunning)
_talkQuit = true;
break;
case Common::EVENT_RETURN_TO_LAUNCHER:
case Common::EVENT_QUIT:
if (_cutawayRunning)
_cutawayQuit = true;
// Allow using close button while dialogue is running
if (_dialogueRunning)
_talkQuit = true;
return;
default:
break;
}
}
_system->updateScreen();
if (amount == 0)
break;
_system->delayMillis((amount > 10) ? 10 : amount);
} while (_system->getMillis() < end);
}
void Input::checkKeys() {
if (_inKey)
debug(6, "[Input::checkKeys] _inKey = %i", _inKey);
switch (_inKey) {
case kActionSkipText:
_keyVerb = VERB_SKIP_TEXT;
break;
case kActionScrollUp:
_keyVerb = VERB_SCROLL_UP;
break;
case kActionScrollDown:
_keyVerb = VERB_SCROLL_DOWN;
break;
case kActionInvSlot1:
_keyVerb = VERB_DIGIT_1;
break;
case kActionInvSlot2:
_keyVerb = VERB_DIGIT_2;
break;
case kActionInvSlot3:
_keyVerb = VERB_DIGIT_3;
break;
case kActionInvSlot4:
_keyVerb = VERB_DIGIT_4;
break;
case kActionSkipCutaway: // skip cutaway / dialogue
if (_canQuit) {
if (_cutawayRunning) {
debug(6, "[Input::checkKeys] Setting _cutawayQuit to true");
_cutawayQuit = true;
}
if (_dialogueRunning)
_talkQuit = true;
}
break;
case kActionJournal: // use Journal
if (_cutawayRunning) {
if (_canQuit) {
_keyVerb = VERB_USE_JOURNAL;
_cutawayQuit = _talkQuit = true;
}
} else {
_keyVerb = VERB_USE_JOURNAL;
if (_canQuit)
_talkQuit = true;
}
break;
case kActionSave: // quicksave
_quickSave = true;
break;
case kActionLoad: // quickload
_quickLoad = true;
break;
default:
for (int i = 0; i < ARRAYSIZE(_verbActions); ++i) {
if (_inKey == _verbActions[i]._action) {
_keyVerb = _verbActions[i]._verb;
break;
}
}
break;
}
_inKey = kActionNone; // reset
}
Common::Point Input::getMousePos() const {
return _eventMan->getMousePos();
}
} // End of namespace Queen

143
engines/queen/input.h Normal file
View File

@@ -0,0 +1,143 @@
/* 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 QUEEN_INPUT_H
#define QUEEN_INPUT_H
#include "common/language.h"
#include "common/rect.h"
#include "common/events.h"
#include "queen/defs.h"
class OSystem;
namespace Queen {
struct verbAction {
Verb _verb;
Common::CustomEventType _action;
};
class Input {
public:
//! Adjust here to change delays!
enum {
DELAY_SHORT = 10,
DELAY_NORMAL = 100, // 5 * 20ms
DELAY_SCREEN_BLANKER = 5 * 60 * 1000
};
enum {
MOUSE_LBUTTON = 1,
MOUSE_RBUTTON = 2
};
Input(Common::Language language, OSystem *system);
void delay(uint amount);
//! convert input to verb
void checkKeys();
//! use instead of KEYVERB=0
void clearKeyVerb() { _keyVerb = VERB_NONE; }
void canQuit(bool cq) { _canQuit = cq; }
bool cutawayRunning() const { return _cutawayRunning; }
void cutawayRunning(bool running) { _cutawayRunning = running; }
bool cutawayQuit() const { return _cutawayQuit; }
void cutawayQuitReset() { _cutawayQuit = false; }
void dialogueRunning(bool running) { _dialogueRunning = running; }
bool talkQuit() const { return _talkQuit; }
void talkQuitReset() { _talkQuit = false; }
bool quickSave() const { return _quickSave; }
void quickSaveReset() { _quickSave = false; }
bool quickLoad() const { return _quickLoad; }
void quickLoadReset() { _quickLoad = false; }
bool fastMode() const { return _fastMode; }
void fastMode(bool fm) { _fastMode = fm; }
Verb keyVerb() const { return _keyVerb; }
Common::Point getMousePos() const;
int mouseButton() const { return _mouseButton; }
void clearMouseButton() { _mouseButton = 0; }
//! returns user idle time (used by Display, to trigger the screensaver)
uint32 idleTime() const { return _idleTime; }
private:
//! used to get keyboard and mouse events
OSystem *_system;
Common::EventManager *_eventMan;
//! some cutaways require update() run faster
bool _fastMode;
//! the current verb received from keyboard
Verb _keyVerb;
//! set if a cutaway is running
bool _cutawayRunning;
//! set this if we can quit
bool _canQuit;
//! moved Cutaway::_quit here
bool _cutawayQuit;
//! set if a dialogue is running
bool _dialogueRunning;
//! moved Talk::_quit here
bool _talkQuit;
//! set if quicksave requested
bool _quickSave;
//! set if quickload requested
bool _quickLoad;
//! set by delay();
Common::CustomEventType _inKey;
//! set by delay();
int _mouseButton;
//! user idle time
uint32 _idleTime;
//! verbs matching the actions
static const verbAction _verbActions[];
};
} // End of namespace Queen
#endif

654
engines/queen/journal.cpp Normal file
View File

@@ -0,0 +1,654 @@
/* 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/error.h"
#include "common/events.h"
#include "common/system.h"
#include "queen/journal.h"
#include "queen/bankman.h"
#include "queen/display.h"
#include "queen/graphics.h"
#include "queen/grid.h"
#include "queen/logic.h"
#include "queen/music.h"
#include "queen/queen.h"
#include "queen/resource.h"
#include "queen/sound.h"
#include "backends/keymapper/keymapper.h"
namespace Queen {
Journal::Journal(QueenEngine *vm)
: _vm(vm) {
_currentSavePage = 0;
_currentSaveSlot = 0;
}
void Journal::use() {
BobSlot *joe = _vm->graphics()->bob(0);
_prevJoeX = joe->x;
_prevJoeY = joe->y;
_panelMode = PM_NORMAL;
_system = g_system;
_panelTextCount = 0;
memset(_panelTextY, 0, sizeof(_panelTextY));
memset(&_textField, 0, sizeof(_textField));
memset(_saveDescriptions, 0, sizeof(_saveDescriptions));
_vm->findGameStateDescriptions(_saveDescriptions);
setup();
redraw();
update();
_vm->display()->palFadeIn(ROOM_JOURNAL);
Common::Keymapper *keymapper = g_system->getEventManager()->getKeymapper();
keymapper->getKeymap("game-shortcuts")->setEnabled(false);
keymapper->getKeymap("journal")->setEnabled(true);
_quitMode = QM_LOOP;
while (_quitMode == QM_LOOP) {
Common::Event event;
Common::EventManager *eventMan = _system->getEventManager();
while (eventMan->pollEvent(event)) {
switch (event.type) {
case Common::EVENT_CUSTOM_ENGINE_ACTION_START:
handleAction(event.customType);
break;
case Common::EVENT_KEYDOWN:
handleKeyDown(event.kbd.ascii, event.kbd.keycode);
break;
case Common::EVENT_LBUTTONDOWN:
handleMouseDown(event.mouse.x, event.mouse.y);
break;
case Common::EVENT_WHEELUP:
handleMouseWheel(-1);
break;
case Common::EVENT_WHEELDOWN:
handleMouseWheel(1);
break;
case Common::EVENT_RETURN_TO_LAUNCHER:
case Common::EVENT_QUIT:
return;
default:
break;
}
}
_system->delayMillis(20);
_system->updateScreen();
}
keymapper->getKeymap("journal")->setEnabled(false);
keymapper->getKeymap("game-shortcuts")->setEnabled(true);
_vm->writeOptionSettings();
_vm->display()->clearTexts(0, GAME_SCREEN_HEIGHT - 1);
_vm->graphics()->putCameraOnBob(0);
if (_quitMode == QM_CONTINUE) {
continueGame();
}
}
void Journal::continueGame() {
_vm->display()->fullscreen(false);
_vm->display()->forceFullRefresh();
_vm->logic()->joePos(_prevJoeX, _prevJoeY);
_vm->logic()->joeCutFacing(_vm->logic()->joeFacing());
_vm->logic()->oldRoom(_vm->logic()->currentRoom());
_vm->logic()->displayRoom(_vm->logic()->currentRoom(), RDM_FADE_JOE, 0, 0, false);
}
void Journal::setup() {
_vm->display()->palFadeOut(_vm->logic()->currentRoom());
_vm->display()->horizontalScroll(0);
_vm->display()->fullscreen(true);
_vm->graphics()->clearBobs();
_vm->display()->clearTexts(0, GAME_SCREEN_HEIGHT - 1);
_vm->bankMan()->eraseFrames(false);
_vm->display()->textCurrentColor(_vm->display()->getInkColor(INK_JOURNAL));
_vm->grid()->clear(GS_ROOM);
for (int i = 0; i < MAX_ZONES; ++i) {
const Zone *zn = &_zones[i];
_vm->grid()->setZone(GS_ROOM, zn->num, zn->x1, zn->y1, zn->x2, zn->y2);
}
_vm->display()->setupNewRoom("journal", ROOM_JOURNAL);
_vm->bankMan()->load("journal.BBK", JOURNAL_BANK);
for (int f = 1; f <= 20; ++f) {
int frameNum = JOURNAL_FRAMES + f;
_vm->bankMan()->unpack(f, frameNum, JOURNAL_BANK);
BobFrame *bf = _vm->bankMan()->fetchFrame(frameNum);
bf->xhotspot = 0;
bf->yhotspot = 0;
if (f == FRAME_INFO_BOX) { // adjust info box hot spot to put it always on top
bf->yhotspot = 200;
}
}
_vm->bankMan()->close(JOURNAL_BANK);
_textField.x = 136;
_textField.y = 9;
_textField.w = 146;
_textField.h = 13;
}
void Journal::redraw() {
drawNormalPanel();
drawConfigPanel();
drawSaveDescriptions();
drawSaveSlot();
}
void Journal::update() {
_vm->graphics()->sortBobs();
_vm->display()->prepareUpdate();
_vm->graphics()->drawBobs();
if (_textField.enabled) {
int16 x = _textField.x + _textField.posCursor;
int16 y = _textField.y + _currentSaveSlot * _textField.h + 8;
_vm->display()->drawBox(x, y, x + 6, y, _vm->display()->getInkColor(INK_JOURNAL));
}
_vm->display()->forceFullRefresh();
_vm->display()->update();
_system->updateScreen();
}
void Journal::showBob(int bobNum, int16 x, int16 y, int frameNum) {
BobSlot *bob = _vm->graphics()->bob(bobNum);
bob->curPos(x, y);
bob->frameNum = JOURNAL_FRAMES + frameNum;
}
void Journal::hideBob(int bobNum) {
_vm->graphics()->bob(bobNum)->active = false;
}
void Journal::drawSaveDescriptions() {
for (int i = 0; i < NUM_SAVES_PER_PAGE; ++i) {
int n = _currentSavePage * 10 + i;
char nb[4];
Common::sprintf_s(nb, "%d", n + 1);
int y = _textField.y + i * _textField.h;
_vm->display()->setText(_textField.x, y, _saveDescriptions[n], false);
_vm->display()->setText(_textField.x - 27, y + 1, nb, false);
}
// highlight current page
showBob(BOB_SAVE_PAGE, 300, 3 + _currentSavePage * 15, 6 + _currentSavePage);
}
void Journal::drawSaveSlot() {
showBob(BOB_SAVE_DESC, 130, 6 + _currentSaveSlot * 13, 17);
}
void Journal::enterYesNoPanelMode(int16 prevZoneNum, int titleNum) {
_panelMode = PM_YES_NO;
_prevZoneNum = prevZoneNum;
drawYesNoPanel(titleNum);
}
void Journal::exitYesNoPanelMode() {
_panelMode = PM_NORMAL;
if (_prevZoneNum == ZN_MAKE_ENTRY) {
closeTextField();
}
redraw();
}
void Journal::enterInfoPanelMode() {
_panelMode = PM_INFO_BOX;
_vm->display()->clearTexts(0, GAME_SCREEN_HEIGHT - 1);
drawInfoPanel();
}
void Journal::exitInfoPanelMode() {
_vm->display()->clearTexts(0, GAME_SCREEN_HEIGHT - 1);
hideBob(BOB_INFO_BOX);
redraw();
_panelMode = PM_NORMAL;
}
void Journal::handleKeyDown(uint16 ascii, int keycode) {
switch (_panelMode) {
case PM_INFO_BOX:
break;
case PM_YES_NO:
if (_textField.enabled) {
updateTextField(ascii, keycode);
}
break;
case PM_NORMAL:
break;
default:
break;
}
}
void Journal::handleAction(Common::CustomEventType action) {
switch (_panelMode) {
case PM_INFO_BOX:
break;
case PM_YES_NO:
if (action == kActionCloseJournal) {
exitYesNoPanelMode();
}
break;
case PM_NORMAL:
if (action == kActionCloseJournal) {
_quitMode = QM_CONTINUE;
}
break;
default:
break;
}
}
void Journal::handleMouseWheel(int inc) {
if (_panelMode == PM_NORMAL) {
int curSave = _currentSavePage * NUM_SAVES_PER_PAGE + _currentSaveSlot + inc;
if (curSave >= 0 && curSave < NUM_SAVES_PER_PAGE * 10) {
_currentSavePage = curSave / NUM_SAVES_PER_PAGE;
_currentSaveSlot = curSave % NUM_SAVES_PER_PAGE;
drawSaveDescriptions();
drawSaveSlot();
update();
}
}
}
void Journal::handleMouseDown(int x, int y) {
int val;
int16 zoneNum = _vm->grid()->findZoneForPos(GS_ROOM, x, y);
switch (_panelMode) {
case PM_INFO_BOX:
exitInfoPanelMode();
break;
case PM_YES_NO:
if (zoneNum == ZN_YES) {
_panelMode = PM_NORMAL;
int currentSlot = _currentSavePage * 10 + _currentSaveSlot;
switch (_prevZoneNum) {
case ZN_REVIEW_ENTRY:
if (_saveDescriptions[currentSlot][0]) {
_vm->graphics()->clearBobs();
_vm->display()->palFadeOut(ROOM_JOURNAL);
_vm->sound()->stopSong();
_vm->loadGameState(currentSlot);
_vm->display()->clearTexts(0, GAME_SCREEN_HEIGHT - 1);
_quitMode = QM_RESTORE;
} else {
exitYesNoPanelMode();
}
break;
case ZN_MAKE_ENTRY:
if (_textField.text[0]) {
closeTextField();
_vm->saveGameState(currentSlot, _textField.text);
_quitMode = QM_CONTINUE;
} else {
exitYesNoPanelMode();
}
break;
case ZN_GIVEUP:
_quitMode = QM_CONTINUE;
_vm->quitGame();
break;
default:
break;
}
} else if (zoneNum == ZN_NO) {
exitYesNoPanelMode();
}
break;
case PM_NORMAL:
switch (zoneNum) {
case ZN_REVIEW_ENTRY:
enterYesNoPanelMode(zoneNum, TXT_REVIEW_ENTRY);
break;
case ZN_MAKE_ENTRY:
initTextField(_saveDescriptions[_currentSavePage * 10 + _currentSaveSlot]);
enterYesNoPanelMode(zoneNum, TXT_MAKE_ENTRY);
break;
case ZN_CLOSE:
_quitMode = QM_CONTINUE;
break;
case ZN_GIVEUP:
enterYesNoPanelMode(zoneNum, TXT_GIVE_UP);
break;
case ZN_TEXT_SPEED:
val = (x - 136) * QueenEngine::MAX_TEXT_SPEED / (266 - 136);
_vm->talkSpeed(val);
drawConfigPanel();
break;
case ZN_SFX_TOGGLE:
_vm->sound()->toggleSfx();
drawConfigPanel();
break;
case ZN_MUSIC_VOLUME:
val = (x - 136) * Audio::Mixer::kMaxMixerVolume / (266 - 136);
_vm->sound()->setVolume(val);
drawConfigPanel();
break;
case ZN_DESC_1:
case ZN_DESC_2:
case ZN_DESC_3:
case ZN_DESC_4:
case ZN_DESC_5:
case ZN_DESC_6:
case ZN_DESC_7:
case ZN_DESC_8:
case ZN_DESC_9:
case ZN_DESC_10:
_currentSaveSlot = zoneNum - ZN_DESC_1;
drawSaveSlot();
break;
case ZN_PAGE_A:
case ZN_PAGE_B:
case ZN_PAGE_C:
case ZN_PAGE_D:
case ZN_PAGE_E:
case ZN_PAGE_F:
case ZN_PAGE_G:
case ZN_PAGE_H:
case ZN_PAGE_I:
case ZN_PAGE_J:
_currentSavePage = zoneNum - ZN_PAGE_A;
drawSaveDescriptions();
break;
case ZN_INFO_BOX:
enterInfoPanelMode();
break;
case ZN_MUSIC_TOGGLE:
_vm->sound()->toggleMusic();
if (_vm->sound()->musicOn()) {
_vm->sound()->playLastSong();
} else {
_vm->sound()->stopSong();
}
drawConfigPanel();
break;
case ZN_VOICE_TOGGLE:
_vm->sound()->toggleSpeech();
drawConfigPanel();
break;
case ZN_TEXT_TOGGLE:
_vm->subtitles(!_vm->subtitles());
drawConfigPanel();
break;
default:
break;
}
break;
default:
break;
}
update();
}
static void removeLeadingAndTrailingSpaces(char *dst, size_t dstSize, const char* src) {
assert(dstSize > 0);
size_t srcLen = strlen(src);
if (0 == srcLen) {
dst[0] = '\0';
return;
}
size_t firstNonSpaceIndex;
for (firstNonSpaceIndex = 0; firstNonSpaceIndex < srcLen; ++firstNonSpaceIndex) {
if (src[firstNonSpaceIndex] != ' ')
break;
}
if (firstNonSpaceIndex == srcLen) {
dst[0] = '\0';
return;
}
size_t lastNonSpaceIndex = srcLen - 1;
while (src[lastNonSpaceIndex] == ' ')
--lastNonSpaceIndex;
uint newLen = lastNonSpaceIndex - firstNonSpaceIndex + 1;
assert(newLen < dstSize);
for (size_t i = 0; i < newLen; ++i) {
dst[i] = src[firstNonSpaceIndex + i];
}
dst[newLen] = '\0';
}
void Journal::drawPanelText(int y, const char *text) {
debug(7, "Journal::drawPanelText(%d, '%s')", y, text);
char s[128];
removeLeadingAndTrailingSpaces(s, 128, text); // necessary for spanish version
// draw the substrings
char *p = strchr(s, ' ');
if (!p) {
int x = (128 - _vm->display()->textWidth(s)) / 2;
_vm->display()->setText(x, y, s, false);
assert(_panelTextCount < MAX_PANEL_TEXTS);
_panelTextY[_panelTextCount++] = y;
} else {
*p++ = '\0';
if (_vm->resource()->getLanguage() == Common::HE_ISR) {
drawPanelText(y - 5, p);
drawPanelText(y + 5, s);
} else {
drawPanelText(y - 5, s);
drawPanelText(y + 5, p);
}
}
}
void Journal::drawCheckBox(bool active, int bobNum, int16 x, int16 y, int frameNum) {
if (active) {
showBob(bobNum, x, y, frameNum);
} else {
hideBob(bobNum);
}
}
void Journal::drawSlideBar(int value, int maxValue, int bobNum, int16 y, int frameNum) {
showBob(bobNum, 136 + value * (266 - 136) / maxValue, y, frameNum);
}
void Journal::drawPanel(const int *frames, const int *titles, int n) {
for (int i = 0; i < _panelTextCount; ++i) {
_vm->display()->clearTexts(_panelTextY[i], _panelTextY[i]);
}
_panelTextCount = 0;
int bobNum = 1;
int y = 8;
while (n--) {
showBob(bobNum++, 32, y, *frames++);
drawPanelText(y + 12, _vm->logic()->joeResponse(*titles++));
y += 48;
}
}
void Journal::drawNormalPanel() {
static const int frames[] = { FRAME_BLUE_1, FRAME_BLUE_2, FRAME_BLUE_1, FRAME_ORANGE };
static const int titles[] = { TXT_REVIEW_ENTRY, TXT_MAKE_ENTRY, TXT_CLOSE, TXT_GIVE_UP };
drawPanel(frames, titles, 4);
}
void Journal::drawYesNoPanel(int titleNum) {
static const int frames[] = { FRAME_GREY, FRAME_BLUE_1, FRAME_BLUE_2 };
const int titles[] = { titleNum, TXT_YES, TXT_NO };
drawPanel(frames, titles, 3);
hideBob(BOB_LEFT_RECT_4);
hideBob(BOB_TALK_SPEED);
hideBob(BOB_SFX_TOGGLE);
hideBob(BOB_MUSIC_VOLUME);
hideBob(BOB_SPEECH_TOGGLE);
hideBob(BOB_TEXT_TOGGLE);
hideBob(BOB_MUSIC_TOGGLE);
}
void Journal::drawConfigPanel() {
_vm->checkOptionSettings();
drawSlideBar(_vm->talkSpeed(), QueenEngine::MAX_TEXT_SPEED, BOB_TALK_SPEED, 164, FRAME_BLUE_PIN);
drawSlideBar(_vm->sound()->getVolume(), Audio::Mixer::kMaxMixerVolume, BOB_MUSIC_VOLUME, 177, FRAME_GREEN_PIN);
drawCheckBox(_vm->sound()->sfxOn(), BOB_SFX_TOGGLE, 221, 155, FRAME_CHECK_BOX);
drawCheckBox(_vm->sound()->speechOn(), BOB_SPEECH_TOGGLE, 158, 155, FRAME_CHECK_BOX);
drawCheckBox(_vm->subtitles(), BOB_TEXT_TOGGLE, 125, 167, FRAME_CHECK_BOX);
drawCheckBox(_vm->sound()->musicOn(), BOB_MUSIC_TOGGLE, 125, 181, FRAME_CHECK_BOX);
}
void Journal::drawInfoPanel() {
showBob(BOB_INFO_BOX, 72, 221, FRAME_INFO_BOX);
const char *ver = _vm->resource()->getJASVersion();
switch (ver[0]) {
case 'P':
_vm->display()->setTextCentered(132, "PC Hard Drive", false);
break;
case 'C':
_vm->display()->setTextCentered(132, "PC CD-ROM", false);
break;
case 'a':
_vm->display()->setTextCentered(132, "Amiga A500/600", false);
break;
default:
break;
}
switch (ver[1]) {
case 'E':
_vm->display()->setTextCentered(144, "English", false);
break;
case 'F' :
_vm->display()->setTextCentered(144, "Fran\x87""ais", false);
break;
case 'G':
_vm->display()->setTextCentered(144, "Deutsch", false);
break;
case 'H':
_vm->display()->setTextCentered(144, "Hebrew", false);
break;
case 'I':
_vm->display()->setTextCentered(144, "Italiano", false);
break;
case 'S':
_vm->display()->setTextCentered(144, "Espa\xA4""ol", false);
break;
default:
break;
}
char versionId[13];
Common::sprintf_s(versionId, "Version %c.%c%c", ver[2], ver[3], ver[4]);
_vm->display()->setTextCentered(156, versionId, false);
}
void Journal::initTextField(const char *desc) {
_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true);
_textField.enabled = true;
_textField.posCursor = _vm->display()->textWidth(desc);
_textField.textCharsCount = strlen(desc);
memset(_textField.text, 0, sizeof(_textField.text));
Common::strcpy_s(_textField.text, desc);
}
void Journal::updateTextField(uint16 ascii, int keycode) {
bool dirty = false;
switch (keycode) {
case Common::KEYCODE_BACKSPACE:
if (_textField.textCharsCount > 0) {
--_textField.textCharsCount;
_textField.text[_textField.textCharsCount] = '\0';
dirty = true;
}
break;
case Common::KEYCODE_RETURN:
case Common::KEYCODE_KP_ENTER:
if (_textField.text[0]) {
closeTextField();
int currentSlot = _currentSavePage * 10 + _currentSaveSlot;
_vm->saveGameState(currentSlot, _textField.text);
_quitMode = QM_CONTINUE;
}
break;
default:
if (Common::isPrint((char)ascii) &&
_textField.textCharsCount < (sizeof(_textField.text) - 1) &&
_vm->display()->textWidth(_textField.text) < _textField.w) {
_textField.text[_textField.textCharsCount] = (char)ascii;
++_textField.textCharsCount;
dirty = true;
}
break;
}
if (dirty) {
_vm->display()->setText(_textField.x, _textField.y + _currentSaveSlot * _textField.h, _textField.text, false);
_textField.posCursor = _vm->display()->textWidth(_textField.text);
update();
}
}
void Journal::closeTextField() {
_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
_textField.enabled = false;
}
const Journal::Zone Journal::_zones[] = {
{ ZN_REVIEW_ENTRY, 32, 8, 96, 40 },
{ ZN_MAKE_ENTRY, 32, 56, 96, 88 }, // == ZN_YES
{ ZN_CLOSE, 32, 104, 96, 136 }, // == ZN_NO
{ ZN_GIVEUP, 32, 152, 96, 184 },
{ ZN_TEXT_SPEED, 136, 169, 265, 176 },
{ ZN_SFX_TOGGLE, 197, 155, 231, 164 },
{ ZN_MUSIC_VOLUME, 136, 182, 265, 189 },
{ ZN_DESC_1, 131, 7, 290, 18 },
{ ZN_DESC_2, 131, 20, 290, 31 },
{ ZN_DESC_3, 131, 33, 290, 44 },
{ ZN_DESC_4, 131, 46, 290, 57 },
{ ZN_DESC_5, 131, 59, 290, 70 },
{ ZN_DESC_6, 131, 72, 290, 83 },
{ ZN_DESC_7, 131, 85, 290, 96 },
{ ZN_DESC_8, 131, 98, 290, 109 },
{ ZN_DESC_9, 131, 111, 290, 122 },
{ ZN_DESC_10, 131, 124, 290, 135 },
{ ZN_PAGE_A, 300, 4, 319, 17 },
{ ZN_PAGE_B, 300, 19, 319, 32 },
{ ZN_PAGE_C, 300, 34, 319, 47 },
{ ZN_PAGE_D, 300, 49, 319, 62 },
{ ZN_PAGE_E, 300, 64, 319, 77 },
{ ZN_PAGE_F, 300, 79, 319, 92 },
{ ZN_PAGE_G, 300, 94, 319, 107 },
{ ZN_PAGE_H, 300, 109, 319, 122 },
{ ZN_PAGE_I, 300, 124, 319, 137 },
{ ZN_PAGE_J, 300, 139, 319, 152 },
{ ZN_INFO_BOX, 273, 146, 295, 189 },
{ ZN_MUSIC_TOGGLE, 109, 181, 135, 190 },
{ ZN_VOICE_TOGGLE, 134, 155, 168, 164 },
{ ZN_TEXT_TOGGLE, 109, 168, 135, 177 }
};
} // End of namespace Queen

208
engines/queen/journal.h Normal file
View File

@@ -0,0 +1,208 @@
/* 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 QUEEN_JOURNAL_H
#define QUEEN_JOURNAL_H
#include "common/util.h"
class OSystem;
namespace Queen {
class QueenEngine;
class Journal {
public:
Journal(QueenEngine *vm);
void use();
enum {
JOURNAL_BANK = 8,
JOURNAL_FRAMES = 40
};
enum {
ZN_REVIEW_ENTRY = 1,
ZN_MAKE_ENTRY = 2,
ZN_YES = ZN_MAKE_ENTRY,
ZN_CLOSE = 3,
ZN_NO = ZN_CLOSE,
ZN_GIVEUP = 4,
ZN_TEXT_SPEED = 5,
ZN_SFX_TOGGLE = 6,
ZN_MUSIC_VOLUME = 7,
ZN_DESC_1 = 8,
ZN_DESC_2 = 9,
ZN_DESC_3 = 10,
ZN_DESC_4 = 11,
ZN_DESC_5 = 12,
ZN_DESC_6 = 13,
ZN_DESC_7 = 14,
ZN_DESC_8 = 15,
ZN_DESC_9 = 16,
ZN_DESC_10 = 17,
ZN_PAGE_A = 18,
ZN_PAGE_B = 19,
ZN_PAGE_C = 20,
ZN_PAGE_D = 21,
ZN_PAGE_E = 22,
ZN_PAGE_F = 23,
ZN_PAGE_G = 24,
ZN_PAGE_H = 25,
ZN_PAGE_I = 26,
ZN_PAGE_J = 27,
ZN_INFO_BOX = 28,
ZN_MUSIC_TOGGLE = 29,
ZN_VOICE_TOGGLE = 30,
ZN_TEXT_TOGGLE = 31
};
enum {
BOB_LEFT_RECT_1 = 1,
BOB_LEFT_RECT_2 = 2,
BOB_LEFT_RECT_3 = 3,
BOB_LEFT_RECT_4 = 4,
BOB_TALK_SPEED = 5,
BOB_SFX_TOGGLE = 6,
BOB_MUSIC_VOLUME = 7,
BOB_SAVE_DESC = 8,
BOB_SAVE_PAGE = 9,
BOB_SPEECH_TOGGLE = 10,
BOB_TEXT_TOGGLE = 11,
BOB_MUSIC_TOGGLE = 12,
BOB_INFO_BOX = 13
};
enum {
FRAME_BLUE_1 = 1,
FRAME_BLUE_2 = 2,
FRAME_ORANGE = 3,
FRAME_GREY = 5,
FRAME_CHECK_BOX = 16,
FRAME_BLUE_PIN = 18,
FRAME_GREEN_PIN = 19,
FRAME_INFO_BOX = 20
};
enum {
TXT_CLOSE = 30,
TXT_GIVE_UP = 31,
TXT_MAKE_ENTRY = 32,
TXT_REVIEW_ENTRY = 33,
TXT_YES = 34,
TXT_NO = 35
};
enum {
NUM_SAVES_PER_PAGE = 10,
MAX_PANEL_TEXTS = 8,
MAX_ZONES = 31
};
enum PanelMode {
PM_NORMAL,
PM_INFO_BOX,
PM_YES_NO
};
enum QuitMode {
QM_LOOP,
QM_RESTORE,
QM_CONTINUE
};
private:
void continueGame();
void setup();
void redraw();
void update();
void showBob(int bobNum, int16 x, int16 y, int frameNum);
void hideBob(int bobNum);
void drawSaveDescriptions();
void drawSaveSlot();
void enterYesNoPanelMode(int16 prevZoneNum, int titleNum);
void exitYesNoPanelMode();
void enterInfoPanelMode();
void exitInfoPanelMode();
void handleMouseWheel(int inc);
void handleMouseDown(int x, int y);
void handleKeyDown(uint16 ascii, int keycode);
void handleAction(Common::CustomEventType action);
void drawPanelText(int y, const char *text);
void drawCheckBox(bool active, int bobNum, int16 x, int16 y, int frameNum);
void drawSlideBar(int value, int maxValue, int bobNum, int16 y, int frameNum);
void drawPanel(const int *frames, const int *titles, int n);
void drawNormalPanel();
void drawYesNoPanel(int titleNum);
void drawConfigPanel();
void drawInfoPanel();
void initTextField(const char *desc);
void updateTextField(uint16 ascii, int keycode);
void closeTextField();
struct TextField {
bool enabled;
int posCursor;
uint textCharsCount;
char text[32];
int x, y;
int w, h;
};
struct Zone {
int num;
int16 x1, y1, x2, y2;
};
PanelMode _panelMode;
QuitMode _quitMode;
int _currentSavePage;
int _currentSaveSlot;
int _prevJoeX, _prevJoeY;
int _panelTextCount;
int _panelTextY[MAX_PANEL_TEXTS];
TextField _textField;
uint16 _prevZoneNum;
char _saveDescriptions[100][32];
OSystem *_system;
QueenEngine *_vm;
static const Zone _zones[MAX_ZONES];
};
} // End of namespace Queen
#endif

2225
engines/queen/logic.cpp Normal file

File diff suppressed because it is too large Load Diff

412
engines/queen/logic.h Normal file
View 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/>.
*
*/
#ifndef QUEEN_LOGIC_H
#define QUEEN_LOGIC_H
#include "common/str-array.h"
#include "common/util.h"
#include "queen/structs.h"
namespace Queen {
enum RoomDisplayMode {
RDM_FADE_NOJOE = 0, // fade in, hide Joe
RDM_FADE_JOE = 1, // fade in, display Joe
RDM_NOFADE_JOE = 2, // screen does not dissolve into view
RDM_FADE_JOE_XY = 3 // display Joe at the current X, Y coords
};
enum JoeWalkMode {
JWM_NORMAL = 0,
JWM_MOVE = 1,
JWM_EXECUTE = 2,
JWM_SPEAK = 3
};
enum {
JSO_OBJECT_DESCRIPTION = 0,
JSO_OBJECT_NAME,
JSO_ROOM_NAME,
JSO_VERB_NAME,
JSO_JOE_RESPONSE,
JSO_ACTOR_ANIM,
JSO_ACTOR_NAME,
JSO_ACTOR_FILE,
JSO_COUNT
};
class Credits;
class Journal;
class QueenEngine;
class Logic {
public:
Logic(QueenEngine *vm);
virtual ~Logic();
uint16 currentRoom() const { return _currentRoom; }
void currentRoom(uint16 room) {
assert(room >= 1 && room <= _numRooms);
_currentRoom = room;
}
uint16 oldRoom() const { return _oldRoom; }
void oldRoom(uint16 room) {
assert(room <= _numRooms);
_oldRoom = room;
}
uint16 newRoom() const { return _newRoom; }
void newRoom(uint16 room) {
assert(room <= _numRooms);
_newRoom = room;
}
ObjectData *objectData(int index) const { return &_objectData[index]; }
uint16 roomData(int room) const { return _roomData[room]; }
GraphicData *graphicData(int index) const { return &_graphicData[index]; }
ItemData *itemData(int index) const { return &_itemData[index]; }
uint16 itemDataCount() const { return _numItems; }
uint16 findBob(uint16 obj) const;
uint16 findFrame(uint16 obj) const;
uint16 objectForPerson(uint16 bobnum) const;
WalkOffData *walkOffPointForObject(int16 obj) const;
uint16 walkOffCount() const { return _numWalkOffs; }
WalkOffData *walkOffData(int index) const { return &_walkOffData[index]; }
uint16 currentRoomData() const { return _roomData[_currentRoom]; }
GraphicAnim *graphicAnim(int index) const { return &_graphicAnim[index]; }
uint16 graphicAnimCount() const { return _numGraphicAnim; }
ObjectDescription *objectDescription(uint16 objNum) const { return &_objectDescription[objNum]; }
uint16 objectDescriptionCount() const { return _numObjDesc; }
uint16 currentRoomSfx() const { return _sfxName[_currentRoom]; }
uint16 joeFacing() const { return _joe.facing; }
uint16 joeX() const { return _joe.x; }
uint16 joeY() const { return _joe.y; }
JoeWalkMode joeWalk() const { return _joe.walk; }
uint16 joeScale() const { return _joe.scale; }
uint16 joeCutFacing() const { return _joe.cutFacing; }
uint16 joePrevFacing() const { return _joe.prevFacing; }
void joeFacing(uint16 dir) { _joe.facing = dir; }
void joePos(uint16 x, uint16 y) { _joe.x = x; _joe.y = y; }
void joeWalk(JoeWalkMode walking);
void joeScale(uint16 scale) { _joe.scale = scale; }
void joeCutFacing(uint16 dir) { _joe.cutFacing = dir; }
void joePrevFacing(uint16 dir) { _joe.prevFacing = dir; }
int16 gameState(int index) const;
void gameState(int index, int16 newValue);
TalkSelected *talkSelected(int index) { return &_talkSelected[index]; }
const char *roomName(uint16 roomNum) const;
const char *objectName(uint16 objNum) const;
const char *objectTextualDescription(uint16 objNum) const;
const char *joeResponse(int i) const;
const char *verbName(Verb v) const;
const char *actorAnim(int num) const;
const char *actorName(int num) const;
const char *actorFile(int num) const;
void eraseRoom();
void setupRoom(const char *room, int comPanel, bool inCutaway);
void displayRoom(uint16 room, RoomDisplayMode mode, uint16 joeScale, int comPanel, bool inCutaway);
int16 entryObj() const { return _entryObj; }
void entryObj(int16 obj) { _entryObj = obj; }
ActorData *findActor(uint16 noun, const char *name = NULL) const;
bool initPerson(uint16 noun, const char *actorName, bool loadBank, Person *pp);
uint16 findPersonNumber(uint16 obj, uint16 room) const;
//! load banks used for Joe animation
void loadJoeBanks(const char *animBank, const char *standBank);
//! load the various bobs needed to animate Joe
void setupJoe();
//! setup Joe at the right place when entering a room
void setupJoeInRoom(bool autoPosition, uint16 scale);
uint16 joeFace();
void joeGrab(int16 grabState);
//! change Joe clothes to dress
void joeUseDress(bool showCut);
//! restore Joe clothes
void joeUseClothes(bool showCut);
//! change Joe clothes to underwear
void joeUseUnderwear();
void makeJoeSpeak(uint16 descNum, bool objectType = false);
void makePersonSpeak(const char *sentence, Person *person, const char *voiceFilePrefix);
//! start the specified dialogue
void startDialogue(const char *dlgFile, int personInRoom, char *cutaway);
//! play the specified cutaway
void playCutaway(const char *cutFile, char *next = NULL);
//! initialize the inventory
void inventorySetup();
//! get the inventory item for the specified inventory slot
uint16 findInventoryItem(int invSlot) const;
//! refresh inventory contents
void inventoryRefresh();
int16 previousInventoryItem(int16 first) const;
int16 nextInventoryItem(int16 first) const;
void removeDuplicateItems();
uint16 numItemsInventory() const;
void inventoryInsertItem(uint16 itemNum, bool refresh = true);
void inventoryDeleteItem(uint16 itemNum, bool refresh = true);
void inventoryScroll(uint16 count, bool up);
void removeHotelItemsFromInventory();
//! copy data from dummy object to object
void objectCopy(int dummyObjectIndex, int objectIndex);
//! handle a particular event when Joe walks on this area
void handleSpecialArea(Direction facing, uint16 areaNum, uint16 walkDataNum);
//! handle the pinnacle room (== room chooser in the jungle)
void handlePinnacleRoom();
void update();
void saveState(byte *&ptr);
void loadState(uint32 ver, byte *&ptr);
//! called after a save state has been loaded
void setupRestoredGame();
//! ugly hack from original code
void sceneReset() { _scene = 0; }
//! make a scene
void sceneStart();
//! stop making a scene
void sceneStop();
void changeRoom();
//! enter the Journal (save/load, configuration)
virtual void useJournal() = 0;
//! execute a special move
void executeSpecialMove(uint16 sm);
void startCredits(const char *filename);
void stopCredits();
void start();
enum {
JOE_RESPONSE_MAX = 40,
DEFAULT_TALK_SPEED = 7 * 3,
GAME_STATE_COUNT = 211,
TALK_SELECTED_COUNT = 86
};
typedef void (Logic::*SpecialMoveProc)();
protected:
void readQueenJas();
void asmMakeJoeUseDress();
void asmMakeJoeUseNormalClothes();
void asmMakeJoeUseUnderwear();
void asmSwitchToDressPalette();
void asmSwitchToNormalPalette();
void asmStartCarAnimation();
void asmStopCarAnimation();
void asmStartFightAnimation();
void asmWaitForFrankPosition();
void asmMakeFrankGrowing();
void asmMakeRobotGrowing();
void asmShrinkRobot();
void asmEndGame();
void asmPutCameraOnDino();
void asmPutCameraOnJoe();
void asmAltIntroPanRight();
void asmAltIntroPanLeft();
void asmSetAzuraInLove();
void asmPanRightFromJoe();
void asmSetLightsOff();
void asmSetLightsOn();
void asmSetManequinAreaOn();
void asmPanToJoe();
void asmTurnGuardOn();
void asmPanLeft320To144();
void asmSmooch();
void asmSmoochNoScroll();
void asmMakeLightningHitPlane();
void asmScaleBlimp();
void asmScaleEnding();
void asmWaitForCarPosition();
void asmShakeScreen();
void asmAttemptPuzzle();
void asmScaleTitle();
void asmScrollTitle();
void asmPanRightToHugh();
void asmMakeWhiteFlash();
void asmPanRightToJoeAndRita();
void asmPanLeftToBomb();
void asmEndDemo();
void asmInterviewIntro();
void asmEndInterview();
virtual bool changeToSpecialRoom() = 0;
virtual void setupSpecialMoveTable() = 0;
uint16 _currentRoom;
uint16 _oldRoom;
uint16 _newRoom;
//! total number of room in game
uint16 _numRooms;
//! first object number in room
uint16 *_roomData;
//! background music to play in room
uint16 *_sfxName;
//! bounding box of object
Box *_objectBox;
//! inventory items
ItemData *_itemData;
uint16 _numItems;
GraphicData *_graphicData;
uint16 _numGraphics;
ObjectData *_objectData;
uint16 _numObjects;
ObjectDescription *_objectDescription;
uint16 _numObjDesc;
ActorData *_actorData;
uint16 _numActors;
//! walk off point for an object
WalkOffData *_walkOffData;
uint16 _numWalkOffs;
FurnitureData *_furnitureData;
uint16 _numFurniture;
GraphicAnim *_graphicAnim;
uint16 _numGraphicAnim;
//! actor initial position in room is _walkOffData[_entryObj]
int16 _entryObj;
Common::StringArray _jasStringList;
int _jasStringOffset[JSO_COUNT];
uint16 _numDescriptions;
uint16 _numNames;
uint16 _numAAnim;
uint16 _numAName;
uint16 _numAFile;
struct {
uint16 x, y;
uint16 facing, cutFacing, prevFacing;
JoeWalkMode walk;
uint16 scale;
} _joe;
int16 _gameState[GAME_STATE_COUNT];
TalkSelected _talkSelected[TALK_SELECTED_COUNT];
//! inventory items
int16 _inventoryItem[4];
//! puzzle counter for room T7
uint8 _puzzleAttemptCount;
//! cutscene counter
int _scene;
SpecialMoveProc _specialMoves[40];
Credits *_credits;
Journal *_journal;
QueenEngine *_vm;
};
class LogicDemo : public Logic {
public:
LogicDemo(QueenEngine *vm) : Logic(vm) {}
void useJournal() override;
protected:
bool changeToSpecialRoom() override;
void setupSpecialMoveTable() override;
};
class LogicInterview : public Logic {
public:
LogicInterview(QueenEngine *vm) : Logic(vm) {}
void useJournal() override;
protected:
bool changeToSpecialRoom() override;
void setupSpecialMoveTable() override;
};
class LogicGame : public Logic {
public:
LogicGame(QueenEngine *vm) : Logic(vm) {}
void useJournal() override;
protected:
bool changeToSpecialRoom() override;
void setupSpecialMoveTable() override;
};
} // End of namespace Queen
#endif

View File

@@ -0,0 +1,321 @@
/* 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 "engines/advancedDetector.h"
#include "common/savefile.h"
#include "common/system.h"
#include "common/translation.h"
#include "queen/input.h"
#include "queen/queen.h"
#include "queen/resource.h"
#include "queen/detection.h"
#include "backends/keymapper/action.h"
#include "backends/keymapper/keymapper.h"
#include "backends/keymapper/standard-actions.h"
static const ADExtraGuiOptionsMap optionsList[] = {
{
GAMEOPTION_ALT_INTRO,
{
_s("Alternative intro"),
_s("Use an alternative game intro (CD version only)"),
"alt_intro",
false,
0,
0
}
},
{
GAMEOPTION_ALT_FONT,
{
_s("Improved font"),
_s("Use an easier to read custom font"),
"alt_font",
false,
0,
0
}
},
AD_EXTRA_GUI_OPTIONS_TERMINATOR
};
class QueenMetaEngine : public AdvancedMetaEngine<Queen::QueenGameDescription> {
public:
const char *getName() const override {
return "queen";
}
const ADExtraGuiOptionsMap *getAdvancedExtraGuiOptions() const override {
return optionsList;
}
bool hasFeature(MetaEngineFeature f) const override;
Common::Error createInstance(OSystem *syst, Engine **engine, const Queen::QueenGameDescription *desc) const override;
SaveStateList listSaves(const char *target) const override;
int getMaximumSaveSlot() const override { return 99; }
bool removeSaveState(const char *target, int slot) const override;
int getAutosaveSlot() const override { return 99; }
Common::KeymapArray initKeymaps(const char *target) const override;
};
bool QueenMetaEngine::hasFeature(MetaEngineFeature f) const {
return
(f == kSupportsListSaves) ||
(f == kSupportsLoadingDuringStartup) ||
(f == kSupportsDeleteSave);
}
SaveStateList QueenMetaEngine::listSaves(const char *target) const {
Common::SaveFileManager *saveFileMan = g_system->getSavefileManager();
Common::StringArray filenames;
char saveDesc[32];
Common::String pattern("queen.s##");
filenames = saveFileMan->listSavefiles(pattern);
SaveStateList saveList;
for (Common::StringArray::const_iterator file = filenames.begin(); file != filenames.end(); ++file) {
// Obtain the last 2 digits of the filename, since they correspond to the save slot
int slotNum = atoi(file->c_str() + file->size() - 2);
if (slotNum >= 0 && slotNum <= 99) {
Common::InSaveFile *in = saveFileMan->openForLoading(*file);
if (in) {
for (int i = 0; i < 4; i++)
in->readUint32BE();
in->read(saveDesc, 32);
saveList.push_back(SaveStateDescriptor(this, slotNum, saveDesc));
delete in;
}
}
}
// Sort saves based on slot number.
Common::sort(saveList.begin(), saveList.end(), SaveStateDescriptorSlotComparator());
return saveList;
}
bool QueenMetaEngine::removeSaveState(const char *target, int slot) const {
Common::String filename = Common::String::format("queen.s%02d", slot);
return g_system->getSavefileManager()->removeSavefile(filename);
}
Common::Error QueenMetaEngine::createInstance(OSystem *syst, Engine **engine, const Queen::QueenGameDescription *desc) const {
*engine = new Queen::QueenEngine(syst); //FIXME ,desc);
return Common::kNoError;
}
static const char *const CommandKeys[] = {
"ocmglptu", // English
"osbgpnre", // German
"ofdnepau", // French
"acsdgpqu", // Italian
"ocmglptu", // Hebrew
"acodmthu" // Spanish
};
const char *CurrentCommandKeys;
Common::KeymapArray QueenMetaEngine::initKeymaps(const char *target) const {
using namespace Common;
using namespace Queen;
Keymap *engineKeymap = new Keymap(Keymap::kKeymapTypeGame, "queen-default", _("Default keymappings"));
Keymap *gameKeymap = new Keymap(Keymap::kKeymapTypeGame, "game-shortcuts", _("Game keymappings"));
Keymap *journalKeymap = new Keymap(Keymap::kKeymapTypeGame, "journal", _("Journal keymappings"));
Action *act;
Language language = parseLanguage(ConfMan.get("language"));
switch (language) {
case EN_ANY:
case EL_GRC:
case RU_RUS:
CurrentCommandKeys = CommandKeys[0];
break;
case DE_DEU:
CurrentCommandKeys = CommandKeys[1];
break;
case FR_FRA:
CurrentCommandKeys = CommandKeys[2];
break;
case IT_ITA:
CurrentCommandKeys = CommandKeys[3];
break;
case HE_ISR:
CurrentCommandKeys = CommandKeys[4];
break;
case ES_ESP:
CurrentCommandKeys = CommandKeys[5];
break;
default:
error("Unknown language");
break;
}
act = new Action(kStandardActionLeftClick, _("Move / Interact"));
act->setLeftClickEvent();
act->addDefaultInputMapping("MOUSE_LEFT");
act->addDefaultInputMapping("JOY_A");
engineKeymap->addAction(act);
act = new Action(kStandardActionRightClick, _("Move / Skip / Use default action with inventory item"));
act->setRightClickEvent();
act->addDefaultInputMapping("MOUSE_RIGHT");
act->addDefaultInputMapping("JOY_B");
engineKeymap->addAction(act);
act = new Action("FASTMODE", _("Toggle fast mode"));
act->setCustomEngineActionEvent(kActionFastMode);
act->addDefaultInputMapping("C+f");
act->addDefaultInputMapping("JOY_Y");
gameKeymap->addAction(act);
act = new Action("SKIPTEXT", _("Skip text"));
act->setCustomEngineActionEvent(kActionSkipText);
act->addDefaultInputMapping("SPACE");
gameKeymap->addAction(act);
act = new Action("INVSCROLLUP", _("Scroll inventory up"));
act->setCustomEngineActionEvent(kActionScrollUp);
act->addDefaultInputMapping("COMMA");
act->addDefaultInputMapping("JOY_UP");
gameKeymap->addAction(act);
act = new Action("INVSCROLLDOWN", _("Scroll inventory down"));
act->setCustomEngineActionEvent(kActionScrollDown);
act->addDefaultInputMapping("PERIOD");
act->addDefaultInputMapping("JOY_DOWN");
gameKeymap->addAction(act);
act = new Action("INVITEM1", _("Inventory item 1"));
act->setCustomEngineActionEvent(kActionInvSlot1);
act->addDefaultInputMapping("1");
gameKeymap->addAction(act);
act = new Action("INVITEM2", _("Inventory item 2"));
act->setCustomEngineActionEvent(kActionInvSlot2);
act->addDefaultInputMapping("2");
gameKeymap->addAction(act);
act = new Action("INVITEM3", _("Inventory item 3"));
act->setCustomEngineActionEvent(kActionInvSlot3);
act->addDefaultInputMapping("3");
gameKeymap->addAction(act);
act = new Action("INVITEM4", _("Inventory item 4"));
act->setCustomEngineActionEvent(kActionInvSlot4);
act->addDefaultInputMapping("4");
gameKeymap->addAction(act);
act = new Action("SKIPCUTAWAY", _("Skip cutaway / dialog"));
act->setCustomEngineActionEvent(kActionSkipCutaway);
act->addDefaultInputMapping("ESCAPE");
gameKeymap->addAction(act);
act = new Action("USEJOURNAL", _("Use journal"));
act->setCustomEngineActionEvent(kActionJournal);
act->addDefaultInputMapping("F1");
act->addDefaultInputMapping("F5");
act->addDefaultInputMapping("JOY_X");
gameKeymap->addAction(act);
act = new Action("QUICKSAVE", _("Quick save"));
act->setCustomEngineActionEvent(kActionSave);
act->addDefaultInputMapping("F11");
act->addDefaultInputMapping("JOY_LEFT_TRIGGER");
gameKeymap->addAction(act);
act = new Action("QUICKLOAD", _("Quick load"));
act->setCustomEngineActionEvent(kActionLoad);
act->addDefaultInputMapping("F12");
act->addDefaultInputMapping("JOY_RIGHT_TRIGGER");
gameKeymap->addAction(act);
act = new Action("OPEN", _("Open"));
act->setCustomEngineActionEvent(kActionOpen);
act->addDefaultInputMapping(String(CurrentCommandKeys[0]));
gameKeymap->addAction(act);
act = new Action("CLOSE", _("Close"));
act->setCustomEngineActionEvent(kActionClose);
act->addDefaultInputMapping(String(CurrentCommandKeys[1]));
gameKeymap->addAction(act);
act = new Action("MOVE", _("Move"));
act->setCustomEngineActionEvent(kActionMove);
act->addDefaultInputMapping(String(CurrentCommandKeys[2]));
gameKeymap->addAction(act);
act = new Action("GIVE", _("Give"));
act->setCustomEngineActionEvent(kActionGive);
act->addDefaultInputMapping(String(CurrentCommandKeys[3]));
gameKeymap->addAction(act);
act = new Action("LOOK", _("Look at"));
act->setCustomEngineActionEvent(kActionLook);
act->addDefaultInputMapping(String(CurrentCommandKeys[4]));
gameKeymap->addAction(act);
act = new Action("PICKUP", _("Pick up"));
act->setCustomEngineActionEvent(kActionPickUp);
act->addDefaultInputMapping(String(CurrentCommandKeys[5]));
gameKeymap->addAction(act);
act = new Action("TALK", _("Talk to"));
act->setCustomEngineActionEvent(kActionTalk);
act->addDefaultInputMapping(String(CurrentCommandKeys[6]));
gameKeymap->addAction(act);
act = new Action("USE", _("Use"));
act->setCustomEngineActionEvent(kActionUse);
act->addDefaultInputMapping(String(CurrentCommandKeys[7]));
gameKeymap->addAction(act);
act = new Action("CLOSE", _("Close journal"));
act->setCustomEngineActionEvent(kActionCloseJournal);
act->addDefaultInputMapping("ESCAPE");
act->addDefaultInputMapping("JOY_X");
journalKeymap->addAction(act);
KeymapArray keymaps(3);
keymaps[0] = engineKeymap;
keymaps[1] = gameKeymap;
keymaps[2] = journalKeymap;
journalKeymap->setEnabled(false);
return keymaps;
}
#if PLUGIN_ENABLED_DYNAMIC(QUEEN)
REGISTER_PLUGIN_DYNAMIC(QUEEN, PLUGIN_TYPE_ENGINE, QueenMetaEngine);
#else
REGISTER_PLUGIN_STATIC(QUEEN, PLUGIN_TYPE_ENGINE, QueenMetaEngine);
#endif

544
engines/queen/midiadlib.cpp Normal file
View File

@@ -0,0 +1,544 @@
/* 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/endian.h"
#include "common/textconsole.h"
#include "engines/queen/midiadlib.h"
namespace Queen {
int AdLibMidiDriver::open() {
_isOpen = true;
_opl = OPL::Config::create();
if (!_opl || !_opl->init())
error("Failed to create OPL");
adlibSetupCard();
for (int i = 0; i < 11; ++i) {
_adlibChannelsVolume[i] = 0;
adlibSetNoteVolume(i, 0);
adlibTurnNoteOff(i);
}
_opl->start(new Common::Functor0Mem<void, AdLibMidiDriver>(this, &AdLibMidiDriver::onTimer));
return 0;
}
void AdLibMidiDriver::close() {
delete _opl;
}
void AdLibMidiDriver::send(uint32 b) {
int channel = b & 15;
int cmd = (b >> 4) & 7;
int param1 = (b >> 8) & 255;
int param2 = (b >> 16) & 255;
switch (cmd) {
case 0:
adlibTurnNoteOff(channel);
break;
case 1:
handleMidiEvent0x90_NoteOn(channel, param1, param2);
break;
case 3:
break;
case 5:
adlibSetNoteVolume(channel, param1);
_adlibChannelsVolume[channel] = param1;
break;
case 6:
adlibSetPitchBend(channel, param1 | (param2 << 7));
break;
default:
// warning("Unhandled cmd %d channel %d (0x%X)", cmd, channel, b);
break;
}
}
void AdLibMidiDriver::setVolume(uint32 volume) {
for (int i = 0; i < _midiNumberOfChannels; ++i)
adlibSetChannelVolume(i, volume * 64 / 256 + 64);
}
void AdLibMidiDriver::metaEvent(byte type, const byte *data, uint16 length) {
int event = 0;
if (length > 4 && READ_BE_UINT32(data) == 0x3F00) {
event = data[4];
switch (event) {
case 1:
if (length == 34) {
handleSequencerSpecificMetaEvent1(data[5], data + 6);
return;
}
break;
case 2:
if (length == 6) {
handleSequencerSpecificMetaEvent2(data[5]);
return;
}
break;
case 3:
if (length == 6) {
handleSequencerSpecificMetaEvent3(data[5]);
return;
}
break;
default:
break;
}
}
warning("Unhandled meta event %d len %d", event, length);
}
void AdLibMidiDriver::setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) {
_adlibTimerProc = timerProc;
_adlibTimerParam = timerParam;
}
void AdLibMidiDriver::onTimer() {
if (_adlibTimerProc)
(*_adlibTimerProc)(_adlibTimerParam);
}
void AdLibMidiDriver::handleSequencerSpecificMetaEvent1(int channel, const uint8 *data) {
for (int i = 0; i < 28; ++i) {
_adlibMetaSequenceData[i] = data[i];
}
if (_midiNumberOfChannels > channel) {
const uint8 *p;
if (_adlibRhythmEnabled) {
p = &_adlibChannelsKeyScalingTable2[channel * 2];
} else {
p = &_adlibChannelsKeyScalingTable1[channel * 2];
}
adlibSetupChannel(p[0], _adlibMetaSequenceData, _adlibMetaSequenceData[26]);
if (p[1] != 255) {
adlibSetupChannel(p[1], _adlibMetaSequenceData + 13, _adlibMetaSequenceData[27]);
}
}
}
void AdLibMidiDriver::handleSequencerSpecificMetaEvent2(uint8 value) {
_adlibRhythmEnabled = value;
_midiNumberOfChannels = _adlibRhythmEnabled ? 11 : 9;
adlibSetAmpVibratoRhythm();
}
void AdLibMidiDriver::handleSequencerSpecificMetaEvent3(uint8 value) {
adlibSetNoteMul(value);
}
void AdLibMidiDriver::handleMidiEvent0x90_NoteOn(int channel, int param1, int param2) { // note, volume
if (param2 == 0) {
adlibTurnNoteOff(channel);
_adlibChannelsVolume[channel] = param2;
} else {
adlibSetNoteVolume(channel, param2);
_adlibChannelsVolume[channel] = param2;
adlibTurnNoteOff(channel);
adlibTurnNoteOn(channel, param1);
}
}
void AdLibMidiDriver::adlibWrite(uint8 port, uint8 value) {
_opl->writeReg(port, value);
}
void AdLibMidiDriver::adlibSetupCard() {
for (int i = 1; i <= 0xF5; ++i) {
adlibWrite(i, 0);
}
adlibWrite(4, 6);
for (int i = 0; i < 9; ++i) {
_midiChannelsNote2Table[i] = 8192;
_midiChannelsOctTable[i] = 0;
_midiChannelsNote1Table[i] = 0;
_midiChannelsFreqTable[i] = 0;
}
memset(_adlibChannelsLevelKeyScalingTable, 127, 11);
memset(_adlibChannelsVolumeTable, 128, 11);
adlibSetupChannels(0);
adlibResetAmpVibratoRhythm(0, 0, 0);
adlibSetNoteMul(1);
adlibSetWaveformSelect(1);
}
void AdLibMidiDriver::adlibSetupChannels(int fl) {
if (fl != 0) {
_midiChannelsNote1Table[8] = 24;
_midiChannelsNote2Table[8] = 8192;
adlibPlayNote(8);
_midiChannelsNote1Table[7] = 31;
_midiChannelsNote2Table[7] = 8192;
adlibPlayNote(7);
}
_adlibRhythmEnabled = fl;
_midiNumberOfChannels = fl ? 11 : 9;
_adlibVibratoRhythm = 0;
_adlibAMDepthEq48 = 0;
_adlibVibratoDepthEq14 = 0;
_adlibKeyboardSplitOn = 0;
adlibResetChannels();
adlibSetAmpVibratoRhythm();
}
void AdLibMidiDriver::adlibResetAmpVibratoRhythm(int am, int vib, int kso) {
_adlibAMDepthEq48 = am;
_adlibVibratoDepthEq14 = vib;
_adlibKeyboardSplitOn = kso;
adlibSetAmpVibratoRhythm();
adlibSetCSMKeyboardSplit();
}
void AdLibMidiDriver::adlibResetChannels() {
for (int i = 0; i < 18; ++i) {
adlibSetupChannelFromSequence(i, _adlibChannelsNoFeedback[i] ? _adlibInitSequenceData2 : _adlibInitSequenceData1, 0);
}
if (_adlibRhythmEnabled) {
adlibSetupChannelFromSequence(12, _adlibInitSequenceData3, 0);
adlibSetupChannelFromSequence(15, _adlibInitSequenceData4, 0);
adlibSetupChannelFromSequence(16, _adlibInitSequenceData5, 0);
adlibSetupChannelFromSequence(14, _adlibInitSequenceData6, 0);
adlibSetupChannelFromSequence(17, _adlibInitSequenceData7, 0);
adlibSetupChannelFromSequence(13, _adlibInitSequenceData8, 0);
}
}
void AdLibMidiDriver::adlibSetAmpVibratoRhythm() {
uint8 value = 0;
if (_adlibAMDepthEq48) {
value |= 0x80;
}
if (_adlibVibratoDepthEq14) {
value |= 0x40;
}
if (_adlibRhythmEnabled) {
value |= 0x20;
}
adlibWrite(0xBD, value | _adlibVibratoRhythm);
}
void AdLibMidiDriver::adlibSetCSMKeyboardSplit() {
uint8 value = _adlibKeyboardSplitOn ? 0x40 : 0;
adlibWrite(8, value);
}
void AdLibMidiDriver::adlibSetNoteMul(int mul) {
if (mul > 12) {
mul = 12;
} else if (mul < 1) {
mul = 1;
}
_adlibNoteMul = mul;
}
void AdLibMidiDriver::adlibSetWaveformSelect(int fl) {
_adlibWaveformSelect = fl ? 0x20 : 0;
for (int i = 0; i < 18; ++i) {
adlibWrite(0xE0 + _adlibChannelsMappingTable1[i], 0);
}
adlibWrite(1, _adlibWaveformSelect);
}
void AdLibMidiDriver::adlibSetPitchBend(int channel, int range) {
if ((_adlibRhythmEnabled && channel <= 6) || channel < 9) {
if (range > 16383) {
range = 16383;
}
_midiChannelsNote2Table[channel] = range;
adlibPlayNote(channel);
}
}
void AdLibMidiDriver::adlibPlayNote(int channel) {
_midiChannelsFreqTable[channel] = adlibPlayNoteHelper(channel, _midiChannelsNote1Table[channel], _midiChannelsNote2Table[channel], _midiChannelsOctTable[channel]);
}
uint8 AdLibMidiDriver::adlibPlayNoteHelper(int channel, int note1, int note2, int oct) {
int n = ((note2 * _midiChannelsNoteTable[channel]) >> 8) - 8192;
if (n != 0) {
n >>= 5;
n *= _adlibNoteMul;
}
n += (note1 << 8) + 8;
n >>= 4;
if (n < 0) {
n = 0;
} else if (n > 1535) {
n = 1535;
}
int index = (((n >> 4) % 12) << 4) | (n & 0xF);
int f = _midiNoteFreqTable[index];
int o = (n >> 4) / 12 - 1;
if (f < 0) {
++o;
}
if (o < 0) {
++o;
f >>= 1;
}
adlibWrite(0xA0 + channel, f & 0xFF);
int value = ((f >> 8) & 3) | (o << 2) | oct;
adlibWrite(0xB0 + channel, value);
return value;
}
void AdLibMidiDriver::adlibTurnNoteOff(int channel) {
if ((_adlibRhythmEnabled && channel <= 6) || channel < 9) {
_midiChannelsOctTable[channel] = 0;
_midiChannelsFreqTable[channel] &= ~0x20;
adlibWrite(0xB0 + channel, _midiChannelsFreqTable[channel]);
} else if (_adlibRhythmEnabled && channel <= 10) {
_adlibVibratoRhythm &= ~(1 << (4 - (channel - 6)));
adlibSetAmpVibratoRhythm();
}
}
void AdLibMidiDriver::adlibTurnNoteOn(int channel, int note) {
note -= 12;
if (note < 0) {
note = 0;
}
if ((_adlibRhythmEnabled && channel <= 6) || channel < 9) {
_midiChannelsNote1Table[channel] = note;
_midiChannelsOctTable[channel] = 0x20;
adlibPlayNote(channel);
} else if (_adlibRhythmEnabled && channel <= 10) {
if (channel == 6) {
_midiChannelsNote1Table[6] = note;
adlibPlayNote(channel);
} else if (channel == 8 && _midiChannelsNote1Table[8] == note) {
_midiChannelsNote1Table[8] = note;
_midiChannelsNote1Table[7] = note + 7;
adlibPlayNote(8);
adlibPlayNote(7);
}
_adlibVibratoRhythm = 1 << (4 - (channel - 6));
adlibSetAmpVibratoRhythm();
}
}
void AdLibMidiDriver::adlibSetupChannelFromSequence(int channel, const uint8 *src, int fl) {
for (int i = 0; i < 13; ++i) {
_adlibSetupChannelSequence2[i] = src[i];
}
adlibSetupChannel(channel, _adlibSetupChannelSequence2, fl);
}
void AdLibMidiDriver::adlibSetupChannel(int channel, const uint16 *src, int fl) {
for (int i = 0; i < 13; ++i) {
_adlibSetupChannelSequence1[14 * channel + i] = src[i];
}
_adlibSetupChannelSequence1[14 * channel + 13] = fl & 3;
adlibSetupChannelHelper(channel);
}
void AdLibMidiDriver::adlibSetNoteVolume(int channel, int volume) {
if (_midiNumberOfChannels > channel) {
if (volume > 127) {
volume = 127;
}
_adlibChannelsLevelKeyScalingTable[channel] = volume;
const uint8 *p;
if (_adlibRhythmEnabled) {
p = &_adlibChannelsKeyScalingTable2[channel * 2];
} else {
p = &_adlibChannelsKeyScalingTable1[channel * 2];
}
adlibSetChannel0x40(p[0]);
if (p[1] != 255) {
adlibSetChannel0x40(p[1]);
}
}
}
void AdLibMidiDriver::adlibSetChannelVolume(int channel, uint8 volume) {
if (channel < (_adlibRhythmEnabled ? 11 : 9))
_adlibChannelsVolumeTable[channel] = volume;
}
void AdLibMidiDriver::adlibSetupChannelHelper(int channel) {
adlibSetAmpVibratoRhythm();
adlibSetCSMKeyboardSplit();
adlibSetChannel0x40(channel);
adlibSetChannel0xC0(channel);
adlibSetChannel0x60(channel);
adlibSetChannel0x80(channel);
adlibSetChannel0x20(channel);
adlibSetChannel0xE0(channel);
}
void AdLibMidiDriver::adlibSetChannel0x40(int channel) {
int index, value, fl;
if (_adlibRhythmEnabled) {
index = _adlibChannelsMappingTable3[channel];
} else {
index = _adlibChannelsMappingTable2[channel];
}
value = 63 - (_adlibSetupChannelSequence1[channel * 14 + 8] & 63);
fl = 0;
if (_adlibRhythmEnabled && index > 6) {
fl = -1;
}
if (_adlibChannelsNoFeedback[channel] || _adlibSetupChannelSequence1[channel * 14 + 12] == 0 || fl != 0) {
value = ((_adlibChannelsLevelKeyScalingTable[index] * value) + 64) >> 7;
}
value = (_adlibChannelsVolumeTable[index] * value * 2) >> 8;
if (value > 63) {
value = 63;
}
value = 63 - value;
value |= _adlibSetupChannelSequence1[channel * 14] << 6;
adlibWrite(0x40 + _adlibChannelsMappingTable1[channel], value);
}
void AdLibMidiDriver::adlibSetChannel0xC0(int channel) {
if (_adlibChannelsNoFeedback[channel] == 0) {
const uint8 *p = &_adlibSetupChannelSequence1[channel * 14];
uint8 value = p[2] << 1;
if (p[12] == 0) {
value |= 1;
}
adlibWrite(0xC0 + _adlibChannelsMappingTable2[channel], value);
}
}
void AdLibMidiDriver::adlibSetChannel0x60(int channel) {
const uint8 *p = &_adlibSetupChannelSequence1[channel * 14];
uint8 value = (p[3] << 4) | (p[6] & 15);
adlibWrite(0x60 + _adlibChannelsMappingTable1[channel], value);
}
void AdLibMidiDriver::adlibSetChannel0x80(int channel) {
const uint8 *p = &_adlibSetupChannelSequence1[channel * 14];
uint8 value = (p[4] << 4) | (p[7] & 15);
adlibWrite(0x80 + _adlibChannelsMappingTable1[channel], value);
}
void AdLibMidiDriver::adlibSetChannel0x20(int channel) {
const uint8 *p = &_adlibSetupChannelSequence1[channel * 14];
uint8 value = p[1] & 15;
if (p[9]) {
value |= 0x80;
}
if (p[10]) {
value |= 0x40;
}
if (p[5]) {
value |= 0x20;
}
if (p[11]) {
value |= 0x10;
}
adlibWrite(0x20 + _adlibChannelsMappingTable1[channel], value);
}
void AdLibMidiDriver::adlibSetChannel0xE0(int channel) {
uint8 value = 0;
if (_adlibWaveformSelect) {
const uint8 *p = &_adlibSetupChannelSequence1[channel * 14];
value = p[13] & 3;
}
adlibWrite(0xE0 + _adlibChannelsMappingTable1[channel], value);
}
const uint8 AdLibMidiDriver::_adlibChannelsMappingTable1[] = {
0, 1, 2, 3, 4, 5, 8, 9, 10, 11, 12, 13, 16, 17, 18, 19, 20, 21
};
const uint8 AdLibMidiDriver::_adlibChannelsNoFeedback[] = {
0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1
};
const uint8 AdLibMidiDriver::_adlibChannelsMappingTable2[] = {
0, 1, 2, 0, 1, 2, 3, 4, 5, 3, 4, 5, 6, 7, 8, 6, 7, 8
};
const uint8 AdLibMidiDriver::_adlibChannelsMappingTable3[] = {
0, 1, 2, 0, 1, 2, 3, 4, 5, 3, 4, 5, 6, 10, 8, 6, 7, 9
};
const uint8 AdLibMidiDriver::_adlibChannelsKeyScalingTable1[] = {
0, 3, 1, 4, 2, 5, 6, 9, 7, 10, 8, 11, 12, 15, 13, 16, 14, 17
};
const uint8 AdLibMidiDriver::_adlibChannelsKeyScalingTable2[] = {
0, 3, 1, 4, 2, 5, 6, 9, 7, 10, 8, 11, 12, 15, 16, 255, 14, 255, 17, 255, 13, 255
};
const uint8 AdLibMidiDriver::_adlibInitSequenceData1[] = {
1, 1, 3, 15, 5, 0, 1, 3, 15, 0, 0, 0, 1, 0
};
const uint8 AdLibMidiDriver::_adlibInitSequenceData2[] = {
0, 1, 1, 15, 7, 0, 2, 4, 0, 0, 0, 1, 0, 0
};
const uint8 AdLibMidiDriver::_adlibInitSequenceData3[] = {
0, 0, 0, 10, 4, 0, 8, 12, 11, 0, 0, 0, 1, 0
};
const uint8 AdLibMidiDriver::_adlibInitSequenceData4[] = {
0, 0, 0, 13, 4, 0, 6, 15, 0, 0, 0, 0, 1, 0
};
const uint8 AdLibMidiDriver::_adlibInitSequenceData5[] = {
0, 12, 0, 15, 11, 0, 8, 5, 0, 0, 0, 0, 0, 0
};
const uint8 AdLibMidiDriver::_adlibInitSequenceData6[] = {
0, 4, 0, 15, 11, 0, 7, 5, 0, 0, 0, 0, 0, 0
};
const uint8 AdLibMidiDriver::_adlibInitSequenceData7[] = {
0, 1, 0, 15, 11, 0, 5, 5, 0, 0, 0, 0, 0, 0
};
const uint8 AdLibMidiDriver::_adlibInitSequenceData8[] = {
0, 1, 0, 15, 11, 0, 7, 5, 0, 0, 0, 0, 0, 0
};
const int16 AdLibMidiDriver::_midiChannelsNoteTable[] = {
256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256
};
const int16 AdLibMidiDriver::_midiNoteFreqTable[] = {
690, 692, 695, 697, 700, 702, 705, 707, 710, 713, 715, 718,
720, 723, 726, 728, 731, 733, 736, 739, 741, 744, 747, 749,
752, 755, 758, 760, 763, 766, 769, 771, 774, 777, 780, 783,
785, 788, 791, 794, 797, 800, 803, 806, 809, 811, 814, 817,
820, 823, 826, 829, 832, 835, 838, 841, 844, 847, 850, 854,
857, 860, 863, 866, 869, 872, 875, 879, 882, 885, 888, 891,
895, 898, 901, 904, 908, 911, 914, 917, 921, 924, 927, 931,
934, 937, 941, 944, 948, 951, 955, 958, 961, 965, 968, 972,
975, 979, 983, 986, 990, 993, 997, 1000, 1004, 1008, 1011, 1015,
1019, 1022, -511, -509, -507, -505, -504, -502, -500, -498, -496, -494,
-492, -490, -488, -486, -484, -482, -480, -479, -477, -475, -473, -471,
-469, -467, -465, -463, -460, -458, -456, -454, -452, -450, -448, -446,
-444, -442, -440, -438, -436, -433, -431, -429, -427, -425, -423, -420,
-418, -416, -414, -412, -409, -407, -405, -403, -401, -398, -396, -394,
-391, -389, -387, -385, -382, -380, -378, -375, -373, -371, -368, -366,
-363, -361, -359, -356, -354, -351, -349, -347, -344, -342, -339, -337
};
} // End of namespace Queen

127
engines/queen/midiadlib.h Normal file
View File

@@ -0,0 +1,127 @@
/* 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 "audio/fmopl.h"
#include "audio/mididrv.h"
namespace Queen {
class AdLibMidiDriver : public MidiDriver {
public:
AdLibMidiDriver() {
_adlibWaveformSelect = 0;
_isOpen = false;
}
~AdLibMidiDriver() override {}
// MidiDriver
int open() override;
void close() override;
void send(uint32 b) override;
void metaEvent(byte type, const byte *data, uint16 length) override;
MidiChannel *allocateChannel() override { return 0; }
MidiChannel *getPercussionChannel() override { return 0; }
void setTimerCallback(void *timerParam, Common::TimerManager::TimerProc timerProc) override;
bool isOpen() const override { return _isOpen; }
uint32 getBaseTempo() override { return 1000000 / OPL::OPL::kDefaultCallbackFrequency; }
void setVolume(uint32 volume);
private:
void handleMidiEvent0x90_NoteOn(int channel, int param1, int param2);
void handleSequencerSpecificMetaEvent1(int channel, const uint8 *data);
void handleSequencerSpecificMetaEvent2(uint8 value);
void handleSequencerSpecificMetaEvent3(uint8 value);
void adlibWrite(uint8 port, uint8 value);
void adlibSetupCard();
void adlibSetupChannels(int fl);
void adlibResetAmpVibratoRhythm(int am, int vib, int kso);
void adlibResetChannels();
void adlibSetAmpVibratoRhythm();
void adlibSetCSMKeyboardSplit();
void adlibSetNoteMul(int mul);
void adlibSetWaveformSelect(int fl);
void adlibSetPitchBend(int channel, int range);
void adlibPlayNote(int channel);
uint8 adlibPlayNoteHelper(int channel, int note1, int note2, int oct);
void adlibTurnNoteOff(int channel);
void adlibTurnNoteOn(int channel, int note);
void adlibSetupChannelFromSequence(int channel, const uint8 *src, int fl);
void adlibSetupChannel(int channel, const uint16 *src, int fl);
void adlibSetNoteVolume(int channel, int volume);
void adlibSetChannelVolume(int channel, uint8 volume);
void adlibSetupChannelHelper(int channel);
void adlibSetChannel0x40(int channel);
void adlibSetChannel0xC0(int channel);
void adlibSetChannel0x60(int channel);
void adlibSetChannel0x80(int channel);
void adlibSetChannel0x20(int channel);
void adlibSetChannel0xE0(int channel);
void onTimer();
OPL::OPL *_opl;
int _midiNumberOfChannels;
int _adlibNoteMul;
int _adlibWaveformSelect;
int _adlibAMDepthEq48;
int _adlibVibratoDepthEq14;
int _adlibRhythmEnabled;
int _adlibKeyboardSplitOn;
int _adlibVibratoRhythm;
uint8 _midiChannelsFreqTable[9];
uint8 _adlibChannelsLevelKeyScalingTable[11];
uint8 _adlibSetupChannelSequence1[14 * 18];
uint16 _adlibSetupChannelSequence2[14];
int16 _midiChannelsNote2Table[9];
uint8 _midiChannelsNote1Table[9];
uint8 _midiChannelsOctTable[9];
uint16 _adlibChannelsVolume[11];
uint16 _adlibMetaSequenceData[28];
uint8 _adlibChannelsVolumeTable[11];
bool _isOpen;
Common::TimerManager::TimerProc _adlibTimerProc;
void *_adlibTimerParam;
static const uint8 _adlibChannelsMappingTable1[];
static const uint8 _adlibChannelsNoFeedback[];
static const uint8 _adlibChannelsMappingTable2[];
static const uint8 _adlibChannelsMappingTable3[];
static const uint8 _adlibChannelsKeyScalingTable1[];
static const uint8 _adlibChannelsKeyScalingTable2[];
static const uint8 _adlibInitSequenceData1[];
static const uint8 _adlibInitSequenceData2[];
static const uint8 _adlibInitSequenceData3[];
static const uint8 _adlibInitSequenceData4[];
static const uint8 _adlibInitSequenceData5[];
static const uint8 _adlibInitSequenceData6[];
static const uint8 _adlibInitSequenceData7[];
static const uint8 _adlibInitSequenceData8[];
static const int16 _midiChannelsNoteTable[];
static const int16 _midiNoteFreqTable[];
};
} // End of namespace Queen

44
engines/queen/module.mk Normal file
View File

@@ -0,0 +1,44 @@
MODULE := engines/queen
MODULE_OBJS := \
bankman.o \
command.o \
credits.o \
cutaway.o \
debug.o \
display.o \
graphics.o \
grid.o \
input.o \
journal.o \
logic.o \
metaengine.o \
midiadlib.o \
music.o \
musicdata.o \
queen.o \
resource.o \
restables.o \
sound.o \
state.o \
talk.o \
version.o \
walk.o
# This module can be built as a plugin
ifeq ($(ENABLE_QUEEN), DYNAMIC_PLUGIN)
PLUGIN := 1
endif
# Include common rules
include $(srcdir)/rules.mk
# Detection objecs
DETECT_OBJS += $(MODULE)/detection.o
# Skip building the following objects if a static
# module is enabled, because it already has the contents.
ifneq ($(ENABLE_QUEEN), STATIC_PLUGIN)
# External dependencies for detection.
DETECT_OBJS += $(MODULE)/version.o
endif

370
engines/queen/music.cpp Normal file
View File

@@ -0,0 +1,370 @@
/* 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/config-manager.h"
#include "common/events.h"
#include "queen/midiadlib.h"
#include "queen/music.h"
#include "queen/queen.h"
#include "queen/resource.h"
#include "queen/sound.h"
#include "audio/midiparser.h"
namespace Queen {
MidiMusic::MidiMusic(QueenEngine *vm)
: _isPlaying(false), _isLooping(false),
_randomLoop(false), _masterVolume(192),
_buf(nullptr), _rnd("queenMusic") {
memset(_channelsTable, 0, sizeof(_channelsTable));
_queuePos = _lastSong = _currentSong = 0;
queueClear();
MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MT32);
_adlib = (MidiDriver::getMusicType(dev) == MT_ADLIB);
_nativeMT32 = ((MidiDriver::getMusicType(dev) == MT_MT32) || ConfMan.getBool("native_mt32"));
const char *musicDataFile;
if (vm->resource()->isDemo()) {
_tune = Sound::_tuneDemo;
musicDataFile = "AQ8.RL";
} else {
_tune = Sound::_tune;
musicDataFile = "AQ.RL";
}
if (_adlib) {
musicDataFile = "AQBANK.MUS";
}
_musicData = vm->resource()->loadFile(musicDataFile, 0, &_musicDataSize);
_numSongs = READ_LE_UINT16(_musicData);
_tune = vm->resource()->isDemo() ? Sound::_tuneDemo : Sound::_tune;
if (_adlib) {
// int infoOffset = _numSongs * 4 + 2;
// if (READ_LE_UINT16(_musicData + 2) != infoOffset) {
// defaultAdLibVolume = _musicData[infoOffset];
// }
_driver = new AdLibMidiDriver();
} else {
_driver = MidiDriver::createMidi(dev);
if (_nativeMT32) {
_driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);
}
}
assert(_driver);
int ret = _driver->open();
assert(ret == 0);
_driver->setTimerCallback(this, &timerCallback);
if (_nativeMT32)
_driver->sendMT32Reset();
else
_driver->sendGMReset();
_parser = MidiParser::createParser_SMF();
_parser->setMidiDriver(this);
_parser->setTimerRate(_driver->getBaseTempo());
}
MidiMusic::~MidiMusic() {
_driver->setTimerCallback(nullptr, nullptr);
_parser->unloadMusic();
delete _parser;
_driver->close();
delete _driver;
delete[] _buf;
delete[] _musicData;
}
void MidiMusic::setVolume(int volume) {
volume = CLIP(volume, 0, 255);
if (_masterVolume == volume)
return;
_masterVolume = volume;
for (int i = 0; i < 16; ++i) {
if (_channelsTable[i])
_channelsTable[i]->volume(_channelsVolume[i] * _masterVolume / 255);
}
if (_adlib)
static_cast<AdLibMidiDriver*>(_driver)->setVolume(volume);
}
void MidiMusic::playSong(uint16 songNum) {
queueClear();
queueSong(songNum);
playMusic();
}
bool MidiMusic::queueSong(uint16 songNum) {
if (songNum >= _numSongs && songNum < 1000) {
// this happens at the end of the car chase, where we try to play song 176,
// see Sound::_tune[], entry 39
debug(3, "Trying to queue an invalid song number %d, max %d", songNum, _numSongs);
return false;
}
uint8 emptySlots = 0;
for (int i = 0; i < MUSIC_QUEUE_SIZE; i++)
if (!_songQueue[i])
emptySlots++;
if (!emptySlots)
return false;
// Work around bug in Roland music, note that these numbers are 'one-off'
// from the original code
if (!_adlib && (songNum == 88 || songNum == 89))
songNum = 62;
_songQueue[MUSIC_QUEUE_SIZE - emptySlots] = songNum;
return true;
}
void MidiMusic::queueClear() {
_lastSong = _songQueue[0];
_queuePos = 0;
_isLooping = _randomLoop = false;
memset(_songQueue, 0, sizeof(_songQueue));
}
void MidiMusic::send(uint32 b) {
if (_adlib) {
_driver->send(b);
return;
}
byte channel = (byte)(b & 0x0F);
if ((b & 0xFFF0) == 0x07B0) {
// Adjust volume changes by master volume
byte volume = (byte)((b >> 16) & 0x7F);
_channelsVolume[channel] = volume;
volume = volume * _masterVolume / 255;
b = (b & 0xFF00FFFF) | (volume << 16);
} else if ((b & 0xF0) == 0xC0 && !_nativeMT32) {
b = (b & 0xFFFF00FF) | MidiDriver::_mt32ToGm[(b >> 8) & 0xFF] << 8;
} else if ((b & 0xFFF0) == 0x007BB0) {
//Only respond to All Notes Off if this channel
//has currently been allocated
if (!_channelsTable[channel])
return;
}
//Work around annoying loud notes in certain Roland Floda tunes
if (channel == 3 && _currentSong == 90)
return;
if (channel == 4 && _currentSong == 27)
return;
if (channel == 5 && _currentSong == 38)
return;
if (!_channelsTable[channel])
_channelsTable[channel] = (channel == 9) ? _driver->getPercussionChannel() : _driver->allocateChannel();
if (_channelsTable[channel])
_channelsTable[channel]->send(b);
}
void MidiMusic::metaEvent(byte type, const byte *data, uint16 length) {
switch (type) {
case 0x2F: // End of Track
if (_isLooping || _songQueue[1]) {
playMusic();
} else {
stopMusic();
}
break;
case 0x7F: // Specific
if (_adlib) {
_driver->metaEvent(type, data, length);
}
break;
default:
// warning("Unhandled meta event: %02x", type);
break;
}
}
void MidiMusic::onTimer() {
Common::StackLock lock(_mutex);
if (_isPlaying)
_parser->onTimer();
}
void MidiMusic::queueTuneList(int16 tuneList) {
queueClear();
//Jungle is the only part of the game that uses multiple tunelists.
//For the sake of code simplification we just hardcode the extended list ourselves
if ((tuneList + 1) == 3) {
_randomLoop = true;
int i = 0;
while (Sound::_jungleList[i])
queueSong(Sound::_jungleList[i++] - 1);
return;
}
int mode = _tune[tuneList].mode;
switch (mode) {
case 0: // random loop
_randomLoop = true;
setLoop(false);
break;
case 1: // sequential loop
setLoop(_songQueue[1] == 0);
break;
case 2: // play once
default:
setLoop(false);
break;
}
int i = 0;
while (_tune[tuneList].tuneNum[i])
queueSong(_tune[tuneList].tuneNum[i++] - 1);
if (_randomLoop)
_queuePos = randomQueuePos();
}
void MidiMusic::playMusic() {
if (!_songQueue[0]) {
debug(5, "MidiMusic::playMusic - Music queue is empty");
return;
}
uint16 songNum = _songQueue[_queuePos];
//Special type
// > 1000 && < 2000 -> queue different tunelist
// 2000 -> repeat music from previous queue
if (songNum > 999) {
if ((songNum + 1) == 2000) {
songNum = _lastSong;
queueClear();
queueSong(songNum);
} else {
queueTuneList(songNum - 1000);
_queuePos = _randomLoop ? randomQueuePos() : 0;
songNum = _songQueue[_queuePos];
}
}
byte *prevSong = _musicData + songOffset(_currentSong);
if (*prevSong == 'C' || *prevSong == 'c') {
if (_buf) {
delete[] _buf;
_buf = nullptr;
}
}
_currentSong = songNum;
if (!songNum) {
stopMusic();
return;
}
byte *musicPtr = _musicData + songOffset(songNum);
uint32 size = songLength(songNum);
if (*musicPtr == 'C' || *musicPtr == 'c') {
uint32 packedSize = songLength(songNum) - 0x200;
_buf = new uint16[packedSize];
uint16 *data = (uint16 *)(musicPtr + 1);
byte *idx = ((byte *)data) + 0x200;
for (uint i = 0; i < packedSize; i++)
#if defined(SCUMM_NEED_ALIGNMENT)
memcpy(&_buf[i], (byte *)((byte *)data + *(idx + i) * sizeof(uint16)), sizeof(uint16));
#else
_buf[i] = data[*(idx + i)];
#endif
musicPtr = ((byte *)_buf) + ((*musicPtr == 'c') ? 1 : 0);
size = packedSize * 2;
}
stopMusic();
Common::StackLock lock(_mutex);
_parser->loadMusic(musicPtr, size);
_parser->setTrack(0);
_isPlaying = true;
debug(8, "Playing song %d [queue position: %d]", songNum, _queuePos);
queueUpdatePos();
}
void MidiMusic::queueUpdatePos() {
if (_randomLoop) {
_queuePos = randomQueuePos();
} else {
if (_queuePos < (MUSIC_QUEUE_SIZE - 1) && _songQueue[_queuePos + 1])
_queuePos++;
else if (_isLooping)
_queuePos = 0;
}
}
uint8 MidiMusic::randomQueuePos() {
int queueSize = 0;
for (int i = 0; i < MUSIC_QUEUE_SIZE; i++)
if (_songQueue[i])
queueSize++;
if (!queueSize)
return 0;
return (uint8)_rnd.getRandomNumber(queueSize - 1) & 0xFF;
}
void MidiMusic::stopMusic() {
Common::StackLock lock(_mutex);
_isPlaying = false;
_parser->unloadMusic();
}
uint32 MidiMusic::songOffset(uint16 songNum) const {
uint16 offsLo = READ_LE_UINT16(_musicData + (songNum * 4) + 2);
uint16 offsHi = READ_LE_UINT16(_musicData + (songNum * 4) + 4);
return (offsHi << 4) | offsLo;
}
uint32 MidiMusic::songLength(uint16 songNum) const {
if (songNum < _numSongs)
return (songOffset(songNum + 1) - songOffset(songNum));
return (_musicDataSize - songOffset(songNum));
}
void MidiMusic::toggleVChange() {
setVolume(_vToggle ? (getVolume() * 2) : (getVolume() / 2));
_vToggle = !_vToggle;
}
} // End of namespace Queen

101
engines/queen/music.h Normal file
View File

@@ -0,0 +1,101 @@
/* 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 QUEEN_MUSIC_H
#define QUEEN_MUSIC_H
#include "common/util.h"
#include "common/mutex.h"
#include "common/random.h"
#include "audio/mididrv.h"
class MidiParser;
namespace Queen {
struct TuneData;
class QueenEngine;
class MidiMusic : public MidiDriver_BASE {
public:
MidiMusic(QueenEngine *vm);
~MidiMusic() override;
void setVolume(int volume);
int getVolume() const { return _masterVolume; }
void playSong(uint16 songNum);
void stopSong() { stopMusic(); }
void playMusic();
void stopMusic();
void setLoop(bool loop) { _isLooping = loop; }
void queueTuneList(int16 tuneList);
bool queueSong(uint16 songNum);
void queueClear();
void toggleVChange();
// MidiDriver_BASE interface implementation
void send(uint32 b) override;
void metaEvent(byte type, const byte *data, uint16 length) override;
protected:
enum {
MUSIC_QUEUE_SIZE = 14
};
void queueUpdatePos();
uint8 randomQueuePos();
void onTimer();
uint32 songOffset(uint16 songNum) const;
uint32 songLength(uint16 songNum) const;
static void timerCallback(void *refCon) { ((MidiMusic *)refCon)->onTimer(); }
MidiDriver *_driver;
MidiParser *_parser;
MidiChannel *_channelsTable[16];
byte _channelsVolume[16];
bool _adlib;
bool _nativeMT32;
Common::Mutex _mutex;
Common::RandomSource _rnd;
bool _isPlaying;
bool _isLooping;
bool _randomLoop;
byte _masterVolume;
uint8 _queuePos;
int16 _currentSong;
int16 _lastSong; //first song from previous queue
int16 _songQueue[MUSIC_QUEUE_SIZE];
uint16 _numSongs;
uint16 *_buf;
uint32 _musicDataSize;
bool _vToggle;
byte *_musicData;
const TuneData *_tune;
};
} // End of namespace Queen
#endif

1910
engines/queen/musicdata.cpp Normal file

File diff suppressed because it is too large Load Diff

382
engines/queen/queen.cpp Normal file
View File

@@ -0,0 +1,382 @@
/* 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 "base/plugins.h"
#include "common/config-manager.h"
#include "common/events.h"
#include "common/file.h"
#include "common/fs.h"
#include "common/savefile.h"
#include "common/system.h"
#include "common/textconsole.h"
#include "engines/util.h"
#include "queen/queen.h"
#include "queen/bankman.h"
#include "queen/command.h"
#include "queen/cutaway.h"
#include "queen/debug.h"
#include "queen/display.h"
#include "queen/graphics.h"
#include "queen/grid.h"
#include "queen/input.h"
#include "queen/logic.h"
#include "queen/resource.h"
#include "queen/sound.h"
#include "queen/talk.h"
#include "queen/walk.h"
namespace Queen {
QueenEngine::QueenEngine(OSystem *syst)
: Engine(syst), _bam(nullptr), _bankMan(nullptr), _command(nullptr), _debugger(nullptr),
_display(nullptr), _graphics(nullptr), _grid(nullptr), _input(nullptr), _logic(nullptr),
_sound(nullptr), _resource(nullptr), _walk(nullptr), _gameStarted(false),
randomizer("queen") {
}
QueenEngine::~QueenEngine() {
delete _bam;
delete _resource;
delete _bankMan;
delete _command;
delete _display;
delete _graphics;
delete _grid;
delete _input;
delete _logic;
delete _sound;
delete _walk;
//_debugger is deleted by Engine
}
void QueenEngine::registerDefaultSettings() {
ConfMan.registerDefault("talkspeed", Logic::DEFAULT_TALK_SPEED);
ConfMan.registerDefault("subtitles", true);
_subtitles = true;
}
void QueenEngine::checkOptionSettings() {
// check talkspeed value
if (_talkSpeed < MIN_TEXT_SPEED) {
_talkSpeed = MIN_TEXT_SPEED;
} else if (_talkSpeed > MAX_TEXT_SPEED) {
_talkSpeed = MAX_TEXT_SPEED;
}
// demo and interview versions don't have speech at all
if (_sound->speechOn() && (_resource->isDemo() || _resource->isInterview())) {
_sound->speechToggle(false);
}
// ensure text is always on when voice is off
if (!_sound->speechOn()) {
_subtitles = true;
}
}
void QueenEngine::syncSoundSettings() {
Engine::syncSoundSettings();
readOptionSettings();
}
void QueenEngine::readOptionSettings() {
bool mute = false;
if (ConfMan.hasKey("mute"))
mute = ConfMan.getBool("mute");
_sound->setVolume(ConfMan.getInt("music_volume"));
_sound->musicToggle(!(mute || ConfMan.getBool("music_mute")));
_sound->sfxToggle(!(mute || ConfMan.getBool("sfx_mute")));
_sound->speechToggle(!(mute || ConfMan.getBool("speech_mute")));
_talkSpeed = (ConfMan.getInt("talkspeed") * (MAX_TEXT_SPEED - MIN_TEXT_SPEED) + 255 / 2) / 255 + MIN_TEXT_SPEED;
_subtitles = ConfMan.getBool("subtitles");
checkOptionSettings();
}
void QueenEngine::writeOptionSettings() {
ConfMan.setInt("music_volume", _sound->getVolume());
ConfMan.setBool("music_mute", !_sound->musicOn());
ConfMan.setBool("sfx_mute", !_sound->sfxOn());
ConfMan.setInt("talkspeed", ((_talkSpeed - MIN_TEXT_SPEED) * 255 + (MAX_TEXT_SPEED - MIN_TEXT_SPEED) / 2) / (MAX_TEXT_SPEED - MIN_TEXT_SPEED));
ConfMan.setBool("speech_mute", !_sound->speechOn());
ConfMan.setBool("subtitles", _subtitles);
ConfMan.flushToDisk();
}
void QueenEngine::update(bool checkPlayerInput) {
_graphics->update(_logic->currentRoom());
_logic->update();
int frameDelay = (_lastUpdateTime + Input::DELAY_NORMAL - _system->getMillis());
if (frameDelay <= 0) {
frameDelay = 1;
}
_input->delay(frameDelay);
_lastUpdateTime = _system->getMillis();
if (!_resource->isInterview()) {
_display->palCustomScroll(_logic->currentRoom());
}
BobSlot *joe = _graphics->bob(0);
_display->update(joe->active, joe->x, joe->y);
_input->checkKeys();
if (canLoadOrSave()) {
if (_input->quickSave()) {
_input->quickSaveReset();
saveGameState(SLOT_QUICKSAVE, "Quicksave");
}
if (_input->quickLoad()) {
_input->quickLoadReset();
loadGameState(SLOT_QUICKSAVE);
}
}
if (!_input->cutawayRunning()) {
if (checkPlayerInput) {
_command->updatePlayer();
}
if (_input->idleTime() >= Input::DELAY_SCREEN_BLANKER) {
_display->blankScreen();
}
}
_sound->updateMusic();
}
bool QueenEngine::canLoadOrSave() const {
return !_input->cutawayRunning() && !(_resource->isDemo() || _resource->isInterview()) && _gameStarted;
}
bool QueenEngine::canLoadGameStateCurrently(Common::U32String *msg) {
return canLoadOrSave();
}
bool QueenEngine::canSaveGameStateCurrently(Common::U32String *msg) {
return canLoadOrSave();
}
Common::Error QueenEngine::saveGameState(int slot, const Common::String &desc, bool isAutosave) {
debug(3, "Saving game to slot %d", slot);
char name[20];
Common::Error err = Common::kNoError;
makeGameStateName(slot, name);
Common::OutSaveFile *file = _saveFileMan->openForSaving(name);
if (file) {
// save data
byte *saveData = new byte[SAVESTATE_MAX_SIZE];
byte *p = saveData;
_bam->saveState(p);
_grid->saveState(p);
_logic->saveState(p);
_sound->saveState(p);
uint32 dataSize = p - saveData;
assert(dataSize < SAVESTATE_MAX_SIZE);
// write header
file->writeUint32BE('SCVM');
file->writeUint32BE(SAVESTATE_CUR_VER);
file->writeUint32BE(0);
file->writeUint32BE(dataSize);
char description[32];
Common::strlcpy(description, desc.c_str(), sizeof(description));
file->write(description, sizeof(description));
// write save data
file->write(saveData, dataSize);
file->finalize();
// check for errors
if (file->err()) {
warning("Can't write file '%s'. (Disk full?)", name);
err = Common::kWritingFailed;
}
delete[] saveData;
delete file;
} else {
warning("Can't create file '%s', game not saved", name);
err = Common::kCreatingFileFailed;
}
return err;
}
Common::Error QueenEngine::loadGameState(int slot) {
debug(3, "Loading game from slot %d", slot);
Common::Error err = Common::kNoError;
GameStateHeader header;
Common::InSaveFile *file = readGameStateHeader(slot, &header);
if (file && header.dataSize != 0) {
byte *saveData = new byte[header.dataSize];
byte *p = saveData;
if (file->read(saveData, header.dataSize) != header.dataSize) {
warning("Error reading savegame file");
err = Common::kReadingFailed;
} else {
_bam->loadState(header.version, p);
_grid->loadState(header.version, p);
_logic->loadState(header.version, p);
_sound->loadState(header.version, p);
if (header.dataSize != (uint32)(p - saveData)) {
warning("Corrupted savegame file");
err = Common::kReadingFailed; // FIXME
} else {
_logic->setupRestoredGame();
}
}
delete[] saveData;
delete file;
} else {
err = Common::kReadingFailed;
}
return err;
}
Common::InSaveFile *QueenEngine::readGameStateHeader(int slot, GameStateHeader *gsh) {
char name[20];
makeGameStateName(slot, name);
Common::InSaveFile *file = _saveFileMan->openForLoading(name);
if (file && file->readUint32BE() == MKTAG('S','C','V','M')) {
gsh->version = file->readUint32BE();
gsh->flags = file->readUint32BE();
gsh->dataSize = file->readUint32BE();
file->read(gsh->description, sizeof(gsh->description));
} else {
memset(gsh, 0, sizeof(GameStateHeader));
}
return file;
}
Common::String QueenEngine::getSaveStateName(int slot) const {
if (slot == SLOT_LISTPREFIX) {
return "queen.s??";
} else if (slot == SLOT_AUTOSAVE) {
slot = getAutosaveSlot();
}
assert(slot >= 0);
return Common::String::format("queen.s%02d", slot);
}
void QueenEngine::makeGameStateName(int slot, char *buf) const {
Common::String name = getSaveStateName(slot);
Common::strcpy_s(buf, 20, name.c_str());
}
int QueenEngine::getGameStateSlot(const char *filename) const {
int i = -1;
const char *slot = strrchr(filename, '.');
if (slot && (slot[1] == 's' || slot[1] == 'S')) {
i = atoi(slot + 2);
}
return i;
}
void QueenEngine::findGameStateDescriptions(char descriptions[100][32]) {
char prefix[20];
makeGameStateName(SLOT_LISTPREFIX, prefix);
Common::StringArray filenames = _saveFileMan->listSavefiles(prefix);
for (Common::StringArray::const_iterator it = filenames.begin(); it != filenames.end(); ++it) {
int i = getGameStateSlot(it->c_str());
if (i >= 0 && i < SAVESTATE_MAX_NUM) {
GameStateHeader header;
Common::InSaveFile *f = readGameStateHeader(i, &header);
Common::strcpy_s(descriptions[i], header.description);
delete f;
}
}
}
bool Queen::QueenEngine::hasFeature(EngineFeature f) const {
return
(f == kSupportsReturnToLauncher) ||
(f == kSupportsLoadingDuringRuntime) ||
(f == kSupportsSavingDuringRuntime) ||
(f == kSupportsSubtitleOptions);
}
Common::Error QueenEngine::run() {
initGraphics(GAME_SCREEN_WIDTH, GAME_SCREEN_HEIGHT);
_resource = new Resource();
_bam = new BamScene(this);
_bankMan = new BankManager(_resource);
_command = new Command(this);
_debugger = new Debugger(this);
setDebugger(_debugger);
_display = new Display(this, _system);
_graphics = new Graphics(this);
_grid = new Grid(this);
_input = new Input(_resource->getLanguage(), _system);
if (_resource->isDemo()) {
_logic = new LogicDemo(this);
} else if (_resource->isInterview()) {
_logic = new LogicInterview(this);
} else {
_logic = new LogicGame(this);
}
_sound = Sound::makeSoundInstance(_mixer, this, _resource->getCompression());
_walk = new Walk(this);
//_talkspeedScale = (MAX_TEXT_SPEED - MIN_TEXT_SPEED) / 255.0;
registerDefaultSettings();
// Setup mixer
syncSoundSettings();
_logic->start();
_gameStarted = true;
if (ConfMan.hasKey("save_slot") && canLoadOrSave()) {
loadGameState(ConfMan.getInt("save_slot"));
}
_lastUpdateTime = _system->getMillis();
while (!shouldQuit()) {
if (_logic->newRoom() > 0) {
_logic->update();
_logic->oldRoom(_logic->currentRoom());
_logic->currentRoom(_logic->newRoom());
_logic->changeRoom();
_display->fullscreen(false);
if (_logic->currentRoom() == _logic->newRoom()) {
_logic->newRoom(0);
}
} else if (_logic->joeWalk() == JWM_EXECUTE) {
_logic->joeWalk(JWM_NORMAL);
_command->executeCurrentAction();
} else {
_logic->joeWalk(JWM_NORMAL);
update(true);
}
}
return Common::kNoError;
}
} // End of namespace Queen

174
engines/queen/queen.h Normal file
View File

@@ -0,0 +1,174 @@
/* 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 QUEEN_QUEEN_H
#define QUEEN_QUEEN_H
#include "engines/engine.h"
#include "common/random.h"
namespace Common {
class SeekableReadStream;
}
/**
* This is the namespace of the Queen engine.
*
* Status of this engine: ???
*
* Games using this engine:
* - Flight of the Amazon Queen
*/
namespace Queen {
enum QUEENActions {
kActionNone,
kActionFastMode,
kActionSkipText,
kActionScrollUp,
kActionScrollDown,
kActionInvSlot1,
kActionInvSlot2,
kActionInvSlot3,
kActionInvSlot4,
kActionSkipCutaway,
kActionJournal,
kActionSave,
kActionLoad,
kActionOpen,
kActionClose,
kActionMove,
kActionGive,
kActionLook,
kActionPickUp,
kActionTalk,
kActionUse,
kActionCloseJournal,
};
struct GameStateHeader {
uint32 version;
uint32 flags;
uint32 dataSize;
char description[32];
};
class BamScene;
class BankManager;
class Command;
class Debugger;
class Display;
class Graphics;
class Grid;
class Input;
class Logic;
class Resource;
class Sound;
class Walk;
class QueenEngine : public Engine {
public:
QueenEngine(OSystem *syst);
~QueenEngine() override;
BamScene *bam() const { return _bam; }
BankManager *bankMan() const { return _bankMan; }
Command *command() const { return _command; }
Debugger *debugger() const { return _debugger; }
Display *display() const { return _display; }
Graphics *graphics() const { return _graphics; }
Grid *grid() const { return _grid; }
Input *input() const { return _input; }
Logic *logic() const { return _logic; }
Resource *resource() const { return _resource; }
Sound *sound() const { return _sound; }
Walk *walk() const { return _walk; }
Common::RandomSource randomizer;
void registerDefaultSettings();
void checkOptionSettings();
void readOptionSettings();
void writeOptionSettings();
int talkSpeed() const { return _talkSpeed; }
void talkSpeed(int speed) { _talkSpeed = speed; }
bool subtitles() const { return _subtitles; }
void subtitles(bool enable) { _subtitles = enable; }
void update(bool checkPlayerInput = false);
bool canLoadOrSave() const;
bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override;
bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override;
Common::Error saveGameState(int slot, const Common::String &desc, bool isAutosave = false) override;
Common::Error loadGameState(int slot) override;
int getAutosaveSlot() const override { return 99; }
Common::String getSaveStateName(int slot) const override;
void makeGameStateName(int slot, char *buf) const;
int getGameStateSlot(const char *filename) const;
void findGameStateDescriptions(char descriptions[100][32]);
Common::SeekableReadStream *readGameStateHeader(int slot, GameStateHeader *gsh);
enum {
SAVESTATE_CUR_VER = 1,
SAVESTATE_MAX_NUM = 100,
SAVESTATE_MAX_SIZE = 30000,
SLOT_LISTPREFIX = -2,
SLOT_AUTOSAVE = -1,
SLOT_QUICKSAVE = 0,
MIN_TEXT_SPEED = 4,
MAX_TEXT_SPEED = 100
};
protected:
// Engine APIs
Common::Error run() override;
bool hasFeature(EngineFeature f) const override;
void syncSoundSettings() override;
int _talkSpeed;
bool _subtitles;
uint32 _lastUpdateTime;
bool _gameStarted;
BamScene *_bam;
BankManager *_bankMan;
Command *_command;
Debugger *_debugger;
Display *_display;
Graphics *_graphics;
Grid *_grid;
Input *_input;
Logic *_logic;
Resource *_resource;
Sound *_sound;
Walk *_walk;
};
} // End of namespace Queen
#endif

193
engines/queen/resource.cpp Normal file
View File

@@ -0,0 +1,193 @@
/* 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/debug.h"
#include "common/endian.h"
#include "common/config-manager.h"
#include "common/substream.h"
#include "common/textconsole.h"
#include "queen/resource.h"
namespace Queen {
const char *const Resource::_tableFilename = "queen.tbl";
static int compareResourceEntry(const void *a, const void *b) {
const char *filename = (const char *)a;
const ResourceEntry *entry = (const ResourceEntry *)b;
return strcmp(filename, entry->filename);
}
Resource::Resource()
: _resourceEntries(0), _resourceTable(nullptr) {
memset(&_version, 0, sizeof(_version));
_currentResourceFileNum = 1;
if (!_resourceFile.open("queen.1c")) {
if (!_resourceFile.open("queen.1")) {
error("Could not open resource file 'queen.1[c]'");
}
}
if (!detectVersion(&_version, &_resourceFile)) {
error("Unable to detect game version");
}
if (_version.features & GF_REBUILT) {
readTableEntries(&_resourceFile);
} else {
readTableFile(_version.queenTblVersion, _version.queenTblOffset);
}
checkJASVersion();
debug(5, "Detected game version: %s, which has %d resource entries", _version.str, _resourceEntries);
}
Resource::~Resource() {
_resourceFile.close();
if (_resourceTable != _resourceTablePEM10)
delete[] _resourceTable;
}
ResourceEntry *Resource::resourceEntry(const char *filename) const {
assert(filename[0] && strlen(filename) < 14);
Common::String entryName(filename);
entryName.toUppercase();
ResourceEntry *re = nullptr;
re = (ResourceEntry *)bsearch(entryName.c_str(), _resourceTable, _resourceEntries, sizeof(ResourceEntry), compareResourceEntry);
return re;
}
uint8 *Resource::loadFile(const char *filename, uint32 skipBytes, uint32 *size) {
debug(7, "Resource::loadFile('%s')", filename);
ResourceEntry *re = resourceEntry(filename);
assert(re != nullptr);
uint32 sz = re->size - skipBytes;
if (size != nullptr) {
*size = sz;
}
byte *dstBuf = new byte[sz];
seekResourceFile(re->bundle, re->offset + skipBytes);
_resourceFile.read(dstBuf, sz);
return dstBuf;
}
void Resource::loadTextFile(const char *filename, Common::StringArray &stringList) {
debug(7, "Resource::loadTextFile('%s')", filename);
ResourceEntry *re = resourceEntry(filename);
assert(re != nullptr);
seekResourceFile(re->bundle, re->offset);
Common::SeekableSubReadStream stream(&_resourceFile, re->offset, re->offset + re->size);
while (true) {
Common::String tmp = stream.readLine();
if (stream.eos() || stream.err())
break;
stringList.push_back(tmp);
}
}
void Resource::checkJASVersion() {
if (_version.platform == Common::kPlatformAmiga) {
// don't bother verifying the JAS version string with these versions,
// it will be done at the end of Logic::readQueenJas, anyway
return;
}
ResourceEntry *re = resourceEntry("QUEEN.JAS");
assert(re != nullptr);
uint32 offset = re->offset;
if (isDemo())
offset += JAS_VERSION_OFFSET_DEMO;
else if (isInterview())
offset += JAS_VERSION_OFFSET_INTV;
else
offset += JAS_VERSION_OFFSET_PC;
seekResourceFile(re->bundle, offset);
char versionStr[6];
_resourceFile.read(versionStr, 6);
if (strcmp(_version.str, versionStr))
error("Verifying game version failed! (expected: '%s', found: '%s')", _version.str, versionStr);
}
void Resource::seekResourceFile(int num, uint32 offset) {
if (_currentResourceFileNum != num) {
debug(7, "Opening resource file %d, current %d", num, _currentResourceFileNum);
_resourceFile.close();
char name[20];
Common::sprintf_s(name, "queen.%d", num);
if (!_resourceFile.open(name)) {
error("Could not open resource file '%s'", name);
}
_currentResourceFileNum = num;
}
_resourceFile.seek(offset);
}
void Resource::readTableFile(uint8 version, uint32 offset) {
Common::File tableFile;
tableFile.open(_tableFilename);
if (tableFile.isOpen() && tableFile.readUint32BE() == MKTAG('Q','T','B','L')) {
uint32 tableVersion = tableFile.readUint32BE();
if (version > tableVersion) {
error("The game you are trying to play requires version %d of queen.tbl, "
"you have version %d ; please update it", version, tableVersion);
}
tableFile.seek(offset);
readTableEntries(&tableFile);
} else {
// check if it is the english floppy version, for which we have a hardcoded version of the table
if (strcmp(_version.str, "PEM10") == 0) {
_resourceEntries = 1076;
_resourceTable = _resourceTablePEM10;
} else {
error("Could not find tablefile '%s'", _tableFilename);
}
}
}
void Resource::readTableEntries(Common::File *file) {
_resourceEntries = file->readUint16BE();
_resourceTable = new ResourceEntry[_resourceEntries];
for (uint16 i = 0; i < _resourceEntries; ++i) {
ResourceEntry *re = &_resourceTable[i];
file->read(re->filename, 12);
re->filename[12] = '\0';
re->bundle = file->readByte();
re->offset = file->readUint32BE();
re->size = file->readUint32BE();
}
}
Common::File *Resource::findSound(const char *filename, uint32 *size) {
assert(strstr(filename, ".SB") != nullptr || strstr(filename, ".AMR") != nullptr || strstr(filename, ".INS") != nullptr);
ResourceEntry *re = resourceEntry(filename);
if (re) {
*size = re->size;
seekResourceFile(re->bundle, re->offset);
return &_resourceFile;
}
return nullptr;
}
} // End of namespace Queen

118
engines/queen/resource.h Normal file
View File

@@ -0,0 +1,118 @@
/* 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 QUEEN_RESOURCE_H
#define QUEEN_RESOURCE_H
#include "common/file.h"
#include "common/str-array.h"
#include "common/language.h"
#include "common/platform.h"
#include "queen/defs.h"
#include "queen/version.h"
namespace Queen {
struct ResourceEntry {
char filename[13];
uint8 bundle;
uint32 offset;
uint32 size;
};
class Resource {
public:
Resource();
~Resource();
//! loads a binary file
uint8 *loadFile(const char *filename, uint32 skipBytes = 0, uint32 *size = NULL);
//! loads a text file
void loadTextFile(const char *filename, Common::StringArray &stringList);
//! returns true if the file is present in the resource
bool fileExists(const char *filename) const { return resourceEntry(filename) != NULL; }
//! returns a reference to a sound file
Common::File *findSound(const char *filename, uint32 *size);
bool isDemo() const { return (_version.features & GF_DEMO) != 0; }
bool isInterview() const { return (_version.features & GF_INTERVIEW) != 0; }
bool isFloppy() const { return (_version.features & GF_FLOPPY) != 0; }
bool isCD() const { return (_version.features & GF_TALKIE) != 0; }
//! returns compression type for audio files
uint8 getCompression() const { return _version.compression; }
//! returns JAS version string (contains language, platform and version information)
const char *getJASVersion() const { return _version.str; }
//! returns the language of the game
Common::Language getLanguage() const { return _version.language; }
Common::Platform getPlatform() const { return _version.platform; }
enum {
JAS_VERSION_OFFSET_DEMO = 0x119A8,
JAS_VERSION_OFFSET_INTV = 0xCF8,
JAS_VERSION_OFFSET_PC = 0x12484
};
protected:
Common::File _resourceFile;
int _currentResourceFileNum;
DetectedGameVersion _version;
//! number of entries in resource table
uint32 _resourceEntries;
ResourceEntry *_resourceTable;
//! verify the version of the selected game
void checkJASVersion();
//! returns a reference to the ReseourceEntry for the specified filename
ResourceEntry *resourceEntry(const char *filename) const;
//! seeks resource file to specific bundle and file offset
void seekResourceFile(int num, uint32 offset);
//! extract the resource table for the specified game version
void readTableFile(uint8 version, uint32 offset);
//! read the resource table from the specified file
void readTableEntries(Common::File *file);
//! resource table filename (queen.tbl)
static const char *const _tableFilename;
//! resource table for english floppy version
static ResourceEntry _resourceTablePEM10[];
};
} // End of namespace Queen
#endif

1106
engines/queen/restables.cpp Normal file

File diff suppressed because it is too large Load Diff

777
engines/queen/sound.cpp Normal file
View File

@@ -0,0 +1,777 @@
/* 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/config-manager.h"
#include "common/endian.h"
#include "common/memstream.h"
#include "common/textconsole.h"
#include "queen/sound.h"
#include "queen/input.h"
#include "queen/logic.h"
#include "queen/music.h"
#include "queen/queen.h"
#include "queen/resource.h"
#include "audio/audiostream.h"
#include "audio/decoders/flac.h"
#include "audio/decoders/mp3.h"
#include "audio/decoders/raw.h"
#include "audio/decoders/vorbis.h"
#include "audio/mods/rjp1.h"
#define SB_HEADER_SIZE_V104 110
#define SB_HEADER_SIZE_V110 122
namespace Queen {
// The sounds in the PC versions are all played at 11840 Hz. Unfortunately, we
// did not know that at the time, so there are plenty of compressed versions
// which claim that they should be played at 11025 Hz. This "wrapper" class
// works around that.
class AudioStreamWrapper : public Audio::AudioStream {
protected:
Audio::AudioStream *_stream;
int _rate;
public:
AudioStreamWrapper(Audio::AudioStream *stream) {
_stream = stream;
int rate = _stream->getRate();
// A file where the sample rate claims to be 11025 Hz is
// probably compressed with the old tool. We force the real
// sample rate, which is 11840 Hz.
//
// However, a file compressed with the newer tool is not
// guaranteed to have a sample rate of 11840 Hz. LAME will
// automatically resample it to 12000 Hz. So in all other
// cases, we use the rate from the file.
if (rate == 11025)
_rate = 11840;
else
_rate = rate;
}
~AudioStreamWrapper() override {
delete _stream;
}
int readBuffer(int16 *buffer, const int numSamples) override {
return _stream->readBuffer(buffer, numSamples);
}
bool isStereo() const override {
return _stream->isStereo();
}
bool endOfData() const override {
return _stream->endOfData();
}
bool endOfStream() const override {
return _stream->endOfStream();
}
int getRate() const override {
return _rate;
}
};
class SilentSound : public PCSound {
public:
SilentSound(Audio::Mixer *mixer, QueenEngine *vm) : PCSound(mixer, vm) {}
protected:
void playSoundData(Common::File *f, uint32 size, Audio::SoundHandle *soundHandle) override {
// Do nothing
}
};
class SBSound : public PCSound {
public:
SBSound(Audio::Mixer *mixer, QueenEngine *vm) : PCSound(mixer, vm) {}
protected:
void playSoundData(Common::File *f, uint32 size, Audio::SoundHandle *soundHandle) override;
};
#ifdef USE_MAD
class MP3Sound : public PCSound {
public:
MP3Sound(Audio::Mixer *mixer, QueenEngine *vm) : PCSound(mixer, vm) {}
protected:
void playSoundData(Common::File *f, uint32 size, Audio::SoundHandle *soundHandle) override {
Common::SeekableReadStream *tmp = f->readStream(size);
assert(tmp);
_mixer->playStream(Audio::Mixer::kSFXSoundType, soundHandle, new AudioStreamWrapper(Audio::makeMP3Stream(tmp, DisposeAfterUse::YES)));
}
};
#endif
#ifdef USE_VORBIS
class OGGSound : public PCSound {
public:
OGGSound(Audio::Mixer *mixer, QueenEngine *vm) : PCSound(mixer, vm) {}
protected:
void playSoundData(Common::File *f, uint32 size, Audio::SoundHandle *soundHandle) override {
Common::SeekableReadStream *tmp = f->readStream(size);
assert(tmp);
_mixer->playStream(Audio::Mixer::kSFXSoundType, soundHandle, new AudioStreamWrapper(Audio::makeVorbisStream(tmp, DisposeAfterUse::YES)));
}
};
#endif
#ifdef USE_FLAC
class FLACSound : public PCSound {
public:
FLACSound(Audio::Mixer *mixer, QueenEngine *vm) : PCSound(mixer, vm) {}
protected:
void playSoundData(Common::File *f, uint32 size, Audio::SoundHandle *soundHandle) override {
Common::SeekableReadStream *tmp = f->readStream(size);
assert(tmp);
_mixer->playStream(Audio::Mixer::kSFXSoundType, soundHandle, new AudioStreamWrapper(Audio::makeFLACStream(tmp, DisposeAfterUse::YES)));
}
};
#endif // #ifdef USE_FLAC
Sound::Sound(Audio::Mixer *mixer, QueenEngine *vm) :
_mixer(mixer), _vm(vm), _sfxToggle(true), _speechToggle(true), _musicToggle(true),
_speechSfxExists(false), _lastOverride(0), _musicVolume(0) {
}
Sound *Sound::makeSoundInstance(Audio::Mixer *mixer, QueenEngine *vm, uint8 compression) {
if (vm->resource()->getPlatform() == Common::kPlatformAmiga)
return new AmigaSound(mixer, vm);
switch (compression) {
case COMPRESSION_NONE:
return new SBSound(mixer, vm);
case COMPRESSION_MP3:
#ifndef USE_MAD
warning("Using MP3 compressed datafile, but MP3 support not compiled in");
return new SilentSound(mixer, vm);
#else
return new MP3Sound(mixer, vm);
#endif
case COMPRESSION_OGG:
#ifndef USE_VORBIS
warning("Using OGG compressed datafile, but OGG support not compiled in");
return new SilentSound(mixer, vm);
#else
return new OGGSound(mixer, vm);
#endif
case COMPRESSION_FLAC:
#ifndef USE_FLAC
warning("Using FLAC compressed datafile, but FLAC support not compiled in");
return new SilentSound(mixer, vm);
#else
return new FLACSound(mixer, vm);
#endif
default:
warning("Unknown compression type");
return new SilentSound(mixer, vm);
}
}
void Sound::setVolume(int vol) {
if (ConfMan.hasKey("mute") && ConfMan.getBool("mute"))
_musicVolume = 0;
else
_musicVolume = vol;
_mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, _musicVolume);
}
void Sound::saveState(byte *&ptr) {
WRITE_BE_UINT16(ptr, _lastOverride); ptr += 2;
}
void Sound::loadState(uint32 ver, byte *&ptr) {
_lastOverride = (int16)READ_BE_INT16(ptr); ptr += 2;
}
PCSound::PCSound(Audio::Mixer *mixer, QueenEngine *vm)
: Sound(mixer, vm) {
_music = new MidiMusic(vm);
}
PCSound::~PCSound() {
delete _music;
}
void PCSound::playSfx(uint16 sfx) {
if (sfxOn() && sfx != 0)
playSound(_sfxName[sfx - 1], false);
}
void PCSound::playSong(int16 songNum) {
if (songNum <= 0) {
_music->stopSong();
return;
}
int16 newTune;
if (_vm->resource()->isDemo()) {
if (songNum == 17) {
_music->stopSong();
return;
}
newTune = _songDemo[songNum - 1].tuneList[0] - 1;
} else {
newTune = _song[songNum - 1].tuneList[0] - 1;
}
if (_tune[newTune].sfx[0]) {
playSfx(_tune[newTune].sfx[0]);
return;
}
if (!musicOn())
return;
int overrideCmd = (_vm->resource()->isDemo()) ? _songDemo[songNum - 1].overrideCmd : _song[songNum - 1].overrideCmd;
switch (overrideCmd) {
// Override all songs
case 1:
break;
// Alter song settings (such as volume) and exit
case 2:
_music->toggleVChange();
default:
return;
}
_lastOverride = songNum;
_music->queueTuneList(newTune);
_music->playMusic();
}
void PCSound::stopSong() {
_music->stopSong();
}
void PCSound::playSpeech(const char *base) {
if (speechOn()) {
playSound(base, true);
}
}
void PCSound::setVolume(int vol) {
Sound::setVolume(vol);
_music->setVolume(vol);
}
void PCSound::playSound(const char *base, bool isSpeech) {
char name[13];
Common::strcpy_s(name, base);
// alter filename to add zeros and append ".SB"
for (int i = 0; i < 8; i++) {
if (name[i] == ' ')
name[i] = '0';
}
Common::strcat_s(name, ".SB");
if (isSpeech) {
// Add _vm->shouldQuit() check here, otherwise game gets stuck
// in an infinite loop if we try to quit while a sound is playing...
while (_mixer->isSoundHandleActive(_speechHandle) && !_vm->shouldQuit()) {
_vm->input()->delay(10);
}
} else {
_mixer->stopHandle(_sfxHandle);
}
uint32 size;
Common::File *f = _vm->resource()->findSound(name, &size);
if (f) {
playSoundData(f, size, isSpeech ? &_speechHandle : &_sfxHandle);
_speechSfxExists = isSpeech;
} else {
_speechSfxExists = false;
}
}
void SBSound::playSoundData(Common::File *f, uint32 size, Audio::SoundHandle *soundHandle) {
// In order to simplify the code, we don't parse the .sb header but hard-code the
// values. Refer to tracker item #3590 for details on the format/fields.
int headerSize;
f->seek(2, SEEK_CUR);
uint16 version = f->readUint16LE();
switch (version) {
case 104:
headerSize = SB_HEADER_SIZE_V104;
break;
case 110:
headerSize = SB_HEADER_SIZE_V110;
break;
default:
warning("Unhandled SB file version %d, defaulting to 104", version);
headerSize = SB_HEADER_SIZE_V104;
break;
}
f->seek(headerSize - 4, SEEK_CUR);
size -= headerSize;
uint8 *sound = (uint8 *)malloc(size);
if (sound) {
f->read(sound, size);
Audio::Mixer::SoundType type = (soundHandle == &_speechHandle) ? Audio::Mixer::kSpeechSoundType : Audio::Mixer::kSFXSoundType;
Audio::AudioStream *stream = Audio::makeRawStream(sound, size, 11840, Audio::FLAG_UNSIGNED);
_mixer->playStream(type, soundHandle, stream);
}
}
AmigaSound::AmigaSound(Audio::Mixer *mixer, QueenEngine *vm)
: Sound(mixer, vm), _fanfareRestore(0), _fanfareCount(0), _fluteCount(0) {
}
void AmigaSound::playSfx(uint16 sfx) {
if (_vm->logic()->currentRoom() == 111) {
// lightning sound
playSound("88SSSSSS");
}
}
void AmigaSound::playSong(int16 song) {
debug(2, "Sound::playSong %d override %d", song, _lastOverride);
if (song < 0) {
stopSong();
return;
}
// remap song numbers for the Amiga
switch (song) {
case 1:
case 2:
song = 39;
break;
case 37:
case 52:
case 196:
song = 90;
break;
case 38:
case 89:
song = 3;
break;
case 24:
case 158:
song = 117;
break;
case 71:
case 72:
case 73:
case 75:
song = 133;
break;
case 203:
song = 67;
break;
case 145:
song = 140;
break;
case 53:
case 204:
song = 44;
break;
case 136:
case 142:
case 179:
song = 86;
break;
case 101:
case 102:
case 143:
song = 188;
break;
case 65:
case 62:
song = 69;
break;
case 118:
case 119:
song = 137;
break;
case 130:
case 131:
song = 59;
break;
case 174:
case 175:
song = 57;
break;
case 171:
case 121:
song = 137;
break;
case 138:
case 170:
case 149:
song = 28;
break;
case 122:
case 180:
case 83:
case 98:
song = 83;
break;
case 20:
case 33:
song = 34;
break;
case 29:
case 35:
song = 36;
break;
case 7:
case 9:
case 10:
song = 11;
break;
case 110:
song = 94;
break;
case 111:
song = 95;
break;
case 30:
song = 43;
break;
case 76:
song = 27;
break;
case 194:
case 195:
song = 32;
break;
default:
break;
}
if (_lastOverride != 32 && _lastOverride != 44) {
if (playSpecialSfx(song)) {
return;
}
}
if (_lastOverride == song && _mixer->isSoundHandleActive(_modHandle)) {
return;
}
switch (song) {
// hotel
case 39:
playModule("HOTEL", 1);
break;
case 19:
playModule("HOTEL", 3);
break;
case 34:
playModule("HOTEL", 2);
break;
case 36:
playModule("HOTEL", 4);
_fanfareRestore = _lastOverride;
_fanfareCount = 60;
break;
// jungle
case 40:
playModule("JUNG", 1);
_fanfareRestore = _lastOverride;
_fanfareCount = 80;
_fluteCount = 100;
break;
case 3:
playModule("JUNG", 2);
_fluteCount = 100;
break;
// temple
case 54:
playModule("TEMPLE", 1);
break;
case 12:
playModule("TEMPLE", 2);
break;
case 11:
playModule("TEMPLE", 3);
break;
case 31:
playModule("TEMPLE", 4);
_fanfareRestore = _lastOverride;
_fanfareCount = 80;
break;
// floda
case 41:
playModule("FLODA", 4);
_fanfareRestore = _lastOverride;
_fanfareCount = 60;
break;
case 13:
playModule("FLODA", 3);
break;
case 16:
playModule("FLODA", 1);
break;
case 17:
playModule("FLODA", 2);
break;
case 43:
playModule("FLODA", 5);
break;
// end credits
case 67:
playModule("TITLE", 1);
break;
// intro credits
case 88:
playModule("TITLE", 1);
break;
// valley
case 90:
playModule("AWESTRUK", 1);
break;
// confrontation
case 91:
playModule("'JUNGLE'", 1);
break;
// Frank
case 46:
playModule("FRANK", 1);
break;
// trader bob
case 6:
playModule("BOB", 1);
break;
// azura
case 44:
playModule("AZURA", 1);
break;
// amazon fortress
case 21:
playModule("FORT", 1);
break;
// rocket
case 32:
playModule("ROCKET", 1);
break;
// robot
case 92:
playModule("ROBOT", 1);
break;
default:
// song not available in the amiga version
return;
}
_lastOverride = song;
}
void AmigaSound::stopSfx() {
_mixer->stopHandle(_sfxHandle);
}
void AmigaSound::stopSong() {
_mixer->stopHandle(_modHandle);
_fanfareCount = _fluteCount = 0;
}
void AmigaSound::updateMusic() {
if (_fanfareCount > 0) {
--_fanfareCount;
if (_fanfareCount == 0) {
playSong(_fanfareRestore);
}
}
if (_fluteCount > 0 && (_lastOverride == 40 || _lastOverride == 3)) {
--_fluteCount;
if (_fluteCount == 0) {
playPattern("JUNG", 5 + _vm->randomizer.getRandomNumber(6));
_fluteCount = 100;
}
}
}
void AmigaSound::playSound(const char *base) {
debug(7, "AmigaSound::playSound(%s)", base);
char soundName[20];
Common::sprintf_s(soundName, "%s.AMR", base);
uint32 soundSize;
Common::File *f = _vm->resource()->findSound(soundName, &soundSize);
if (f) {
uint8 *soundData = (uint8 *)malloc(soundSize);
if (soundData) {
f->read(soundData, soundSize);
Audio::AudioStream *stream = Audio::makeRawStream(soundData, soundSize, 11025, 0);
_mixer->playStream(Audio::Mixer::kSFXSoundType, &_sfxHandle, stream);
}
}
}
Audio::AudioStream *AmigaSound::loadModule(const char *base, int num) {
debug(7, "AmigaSound::loadModule(%s, %d)", base, num);
char name[20];
// load song/pattern data
uint32 sngDataSize;
Common::sprintf_s(name, "%s.SNG", base);
uint8 *sngData = _vm->resource()->loadFile(name, 0, &sngDataSize);
Common::MemoryReadStream sngStr(sngData, sngDataSize);
// load instruments/wave data
uint32 insDataSize;
Common::sprintf_s(name, "%s.INS", base);
uint8 *insData = _vm->resource()->loadFile(name, 0, &insDataSize);
Common::MemoryReadStream insStr(insData, insDataSize);
Audio::AudioStream *stream = Audio::makeRjp1Stream(&sngStr, &insStr, num, _mixer->getOutputRate());
delete[] sngData;
delete[] insData;
return stream;
}
void AmigaSound::playModule(const char *base, int song) {
_mixer->stopHandle(_modHandle);
Audio::AudioStream *stream = loadModule(base, song);
if (stream) {
_mixer->playStream(Audio::Mixer::kMusicSoundType, &_modHandle, stream);
}
_fanfareCount = 0;
}
void AmigaSound::playPattern(const char *base, int pattern) {
_mixer->stopHandle(_patHandle);
Audio::AudioStream *stream = loadModule(base, -pattern);
if (stream) {
_mixer->playStream(Audio::Mixer::kSFXSoundType, &_patHandle, stream);
}
}
bool AmigaSound::playSpecialSfx(int16 sfx) {
switch (sfx) {
case 5: // normal volume
break;
case 15: // soft volume
break;
case 14: // medium volume
break;
case 25: // open door
playSound("116BSSSS");
break;
case 26: // close door
playSound("105ASSSS");
break;
case 56: // light switch
playSound("27SSSSSS");
break;
case 57: // hydraulic doors open
playSound("96SSSSSS");
break;
case 58: // hydraulic doors close
playSound("97SSSSSS");
break;
case 59: // metallic door slams
playSound("105SSSSS");
break;
case 63: // oracle rezzes in
playSound("132SSSSS");
break;
case 27: // cloth slide 1
playSound("135SSSSS");
break;
case 83: // splash
playSound("18SSSSSS");
break;
case 85: // aggression enhancer
playSound("138BSSSS");
break;
case 68: // dino ray
playSound("138SSSSS");
break;
case 140: // dino transformation
playSound("55BSSSSS");
break;
case 141: // experimental laser
playSound("55SSSSSS");
break;
case 94: // plane hatch open
playSound("3SSSSSSS");
break;
case 95: // plane hatch close
playSound("4SSSSSSS");
break;
case 117: // oracle rezzes out
playSound("70SSSSSS");
break;
case 124: // dino horn
playSound("103SSSSS");
break;
case 127: // punch
playSound("128SSSSS");
break;
case 128: // body hits ground
playSound("129SSSSS");
break;
case 137: // explosion
playSound("88SSSSSS");
break;
case 86: // stone door grind 1
playSound("1001SSSS");
break;
case 188: // stone door grind 2
playSound("1002SSSS");
break;
case 28: // cloth slide 2
playSound("1005SSSS");
break;
case 151: // rattle bars
playSound("115SSSSS");
break;
case 152: // door dissolves
playSound("56SSSSSS");
break;
case 153: // altar slides
playSound("85SSSSSS");
break;
case 166 : // pull lever
playSound("1008SSSS");
break;
case 182: // zap Frank
playSound("1023SSSS");
break;
case 69: // splorch
playSound("137ASSSS");
break;
case 70: // robot laser
playSound("61SSSSSS");
break;
case 133: // pick hits stone
playSound("71SSSSSS");
break;
case 165: // press button
playSound("1007SSSS");
break;
default:
return false;
}
return true;
}
} // End of namespace Queen

183
engines/queen/sound.h Normal file
View File

@@ -0,0 +1,183 @@
/* 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 QUEEN_SOUND_H
#define QUEEN_SOUND_H
#include "audio/mixer.h"
namespace Audio {
class AudioStream;
}
namespace Common {
class File;
}
namespace Queen {
struct SongData {
int16 tuneList[5];
int16 volume;
int16 tempo;
int16 reverb;
int16 overrideCmd;
int16 ignore;
};
struct TuneData {
int16 tuneNum[9];
int16 sfx[2];
int16 mode;
int16 delay;
};
class MidiMusic;
class QueenEngine;
class Sound {
public:
Sound(Audio::Mixer *mixer, QueenEngine *vm);
virtual ~Sound() {}
/**
* Factory method for subclasses of class Sound.
*/
static Sound *makeSoundInstance(Audio::Mixer *mixer, QueenEngine *vm, uint8 compression);
virtual void playSfx(uint16 sfx) {}
virtual void playSong(int16 songNum) {}
virtual void playSpeech(const char *base) {}
virtual void stopSfx() {}
virtual void stopSong() {}
virtual void stopSpeech() {}
virtual bool isSpeechActive() const { return false; }
virtual bool isSfxActive() const { return false; }
virtual void updateMusic() {}
virtual void setVolume(int vol);
virtual int getVolume() { return _musicVolume; }
void playLastSong() { playSong(_lastOverride); }
bool sfxOn() const { return _sfxToggle; }
void sfxToggle(bool val) { _sfxToggle = val; }
void toggleSfx() { _sfxToggle = !_sfxToggle; }
bool speechOn() const { return _speechToggle; }
void speechToggle(bool val) { _speechToggle = val; }
void toggleSpeech() { _speechToggle = !_speechToggle; }
bool musicOn() const { return _musicToggle; }
void musicToggle(bool val) { _musicToggle = val; }
void toggleMusic() { _musicToggle = !_musicToggle; }
bool speechSfxExists() const { return _speechSfxExists; }
int16 lastOverride() const { return _lastOverride; }
void saveState(byte *&ptr);
void loadState(uint32 ver, byte *&ptr);
static const SongData _songDemo[];
static const SongData _song[];
static const TuneData _tuneDemo[];
static const TuneData _tune[];
static const char *const _sfxName[];
static const int16 _jungleList[];
protected:
Audio::Mixer *_mixer;
QueenEngine *_vm;
bool _sfxToggle;
bool _speechToggle;
bool _musicToggle;
bool _speechSfxExists;
int16 _lastOverride;
int _musicVolume;
};
class PCSound : public Sound {
public:
PCSound(Audio::Mixer *mixer, QueenEngine *vm);
~PCSound() override;
void playSfx(uint16 sfx) override;
void playSpeech(const char *base) override;
void playSong(int16 songNum) override;
void stopSfx() override { _mixer->stopHandle(_sfxHandle); }
void stopSong() override;
void stopSpeech() override { _mixer->stopHandle(_speechHandle); }
bool isSpeechActive() const override { return _mixer->isSoundHandleActive(_speechHandle); }
bool isSfxActive() const override { return _mixer->isSoundHandleActive(_sfxHandle); }
void setVolume(int vol) override;
protected:
void playSound(const char *base, bool isSpeech);
virtual void playSoundData(Common::File *f, uint32 size, Audio::SoundHandle *soundHandle) = 0;
Audio::SoundHandle _sfxHandle;
Audio::SoundHandle _speechHandle;
MidiMusic *_music;
};
class AmigaSound : public Sound {
public:
AmigaSound(Audio::Mixer *mixer, QueenEngine *vm);
void playSfx(uint16 sfx) override;
void playSong(int16 song) override;
void stopSfx() override;
void stopSong() override;
bool isSfxActive() const override { return _mixer->isSoundHandleActive(_sfxHandle); }
void updateMusic() override;
protected:
void playSound(const char *base);
Audio::AudioStream *loadModule(const char *base, int song);
void playModule(const char *base, int song);
void playPattern(const char *base, int pattern);
bool playSpecialSfx(int16 sfx);
int16 _fanfareRestore;
int _fanfareCount, _fluteCount;
Audio::SoundHandle _modHandle;
Audio::SoundHandle _patHandle;
Audio::SoundHandle _sfxHandle;
};
} // End of namespace Queen
#endif

131
engines/queen/state.cpp Normal file
View File

@@ -0,0 +1,131 @@
/* 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 "queen/state.h"
namespace Queen {
Direction State::findDirection(uint16 state) {
static const Direction sd[] = {
DIR_BACK,
DIR_RIGHT,
DIR_LEFT,
DIR_FRONT
};
return sd[(state >> 2) & 3];
}
StateTalk State::findTalk(uint16 state) {
return (state & (1 << 9)) ? STATE_TALK_TALK : STATE_TALK_MUTE;
}
StateGrab State::findGrab(uint16 state) {
static const StateGrab sg[] = {
STATE_GRAB_NONE,
STATE_GRAB_DOWN,
STATE_GRAB_UP,
STATE_GRAB_MID
};
return sg[state & 3];
}
StateOn State::findOn(uint16 state) {
return (state & (1 << 8)) ? STATE_ON_ON : STATE_ON_OFF;
}
Verb State::findDefaultVerb(uint16 state) {
static const Verb sdv[] = {
VERB_NONE,
VERB_OPEN,
VERB_NONE,
VERB_CLOSE,
VERB_NONE,
VERB_NONE,
VERB_LOOK_AT,
VERB_MOVE,
VERB_GIVE,
VERB_TALK_TO,
VERB_NONE,
VERB_NONE,
VERB_USE,
VERB_NONE,
VERB_PICK_UP,
VERB_NONE
};
return sdv[(state >> 4) & 0xF];
}
StateUse State::findUse(uint16 state) {
return (state & (1 << 10)) ? STATE_USE : STATE_USE_ON;
}
void State::alterOn(uint16 *objState, StateOn state) {
switch (state) {
case STATE_ON_ON:
*objState |= (1 << 8);
break;
case STATE_ON_OFF:
*objState &= ~(1 << 8);
break;
default:
break;
}
}
void State::alterDefaultVerb(uint16 *objState, Verb v) {
uint16 val;
switch (v) {
case VERB_OPEN:
val = 1;
break;
case VERB_CLOSE:
val = 3;
break;
case VERB_MOVE:
val = 7;
break;
case VERB_GIVE:
val = 8;
break;
case VERB_USE:
val = 12;
break;
case VERB_PICK_UP:
val = 14;
break;
case VERB_TALK_TO:
val = 9;
break;
case VERB_LOOK_AT:
val = 6;
break;
default:
val = 0;
break;
}
*objState = (*objState & ~0xF0) | (val << 4);
}
} // End of namespace Queen

112
engines/queen/state.h Normal file
View File

@@ -0,0 +1,112 @@
/* 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 QUEEN_STATE_H
#define QUEEN_STATE_H
#include "common/util.h"
#include "queen/defs.h"
namespace Queen {
enum StateTalk {
STATE_TALK_TALK,
STATE_TALK_MUTE
};
enum StateGrab {
STATE_GRAB_NONE,
STATE_GRAB_DOWN,
STATE_GRAB_UP,
STATE_GRAB_MID
};
enum StateOn {
STATE_ON_ON,
STATE_ON_OFF
};
enum StateUse {
STATE_USE,
STATE_USE_ON
};
/*!
Each object/item in game has a state field.
(refer to ObjectData and ItemData).
<table>
<tr>
<td>Name</td>
<td>Bits</td>
<td>Description</td>
</tr>
<tr>
<td>USE</td>
<td>10</td>
<td>Use</td>
</tr>
<tr>
<td>TALK</td>
<td>9</td>
<td>Talk</td>
</tr>
<tr>
<td>ON</td>
<td>8</td>
<td>On/Off</td>
</tr>
<tr>
<td>DEF</td>
<td>7,6,5,4</td>
<td>Default verb command</td>
</tr>
<tr>
<td>DIR</td>
<td>3,2</td>
<td>Direction to face for the object</td>
</tr>
<tr>
<td>GRAB</td>
<td>1,0</td>
<td>Grab Direction</td>
</tr>
</table>
*/
struct State {
static Direction findDirection(uint16 state);
static StateTalk findTalk(uint16 state);
static StateGrab findGrab(uint16 state);
static StateOn findOn(uint16 state);
static Verb findDefaultVerb(uint16 state);
static StateUse findUse(uint16 state);
static void alterOn(uint16 *objState, StateOn state);
static void alterDefaultVerb(uint16 *objState, Verb v);
};
} // End of namespace Queen
#endif

584
engines/queen/structs.h Normal file
View File

@@ -0,0 +1,584 @@
/* 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 QUEEN_STRUCTS_H
#define QUEEN_STRUCTS_H
#include "queen/defs.h"
#include "common/endian.h"
namespace Queen {
struct Box {
int16 x1, y1, x2, y2;
Box()
: x1(0), y1(0), x2(0), y2(0) {
}
Box(int16 xx1, int16 yy1, int16 xx2, int16 yy2)
: x1(xx1), y1(yy1), x2(xx2), y2(yy2) {
}
void readFromBE(byte *&ptr) {
x1 = (int16)READ_BE_UINT16(ptr); ptr += 2;
y1 = (int16)READ_BE_UINT16(ptr); ptr += 2;
x2 = (int16)READ_BE_UINT16(ptr); ptr += 2;
y2 = (int16)READ_BE_UINT16(ptr); ptr += 2;
}
void writeToBE(byte *&ptr) {
WRITE_BE_UINT16(ptr, x1); ptr += 2;
WRITE_BE_UINT16(ptr, y1); ptr += 2;
WRITE_BE_UINT16(ptr, x2); ptr += 2;
WRITE_BE_UINT16(ptr, y2); ptr += 2;
}
int16 xDiff() const {
return x2 - x1;
}
int16 yDiff() const {
return y2 - y1;
}
bool intersects(int16 x, int16 y, uint16 w, uint16 h) const {
return (x + w > x1) && (y + h > y1) && (x <= x2) && (y <= y2);
}
bool contains(int16 x, int16 y) const {
return (x >= x1) && (x <= x2) && (y >= y1) && (y <= y2);
}
bool operator==(const Box &b) const {
return (x1 == b.x1) && (x2 == b.x2) && (y1 == b.y1) && (y2 == b.y2);
}
};
struct Area {
//! bitmask of connected areas
int16 mapNeighbors;
//! coordinates defining area limits
Box box;
//! scaling factors for bobs actors
uint16 bottomScaleFactor, topScaleFactor;
//! entry in ObjectData, object lying in this area
uint16 object;
Area()
: mapNeighbors(0), bottomScaleFactor(0), topScaleFactor(0), object(0) {
}
void readFromBE(byte *&ptr) {
mapNeighbors = (int16)READ_BE_UINT16(ptr); ptr += 2;
box.readFromBE(ptr);
bottomScaleFactor = READ_BE_UINT16(ptr); ptr += 2;
topScaleFactor = READ_BE_UINT16(ptr); ptr += 2;
object = READ_BE_UINT16(ptr); ptr += 2;
}
void writeToBE(byte *&ptr) {
WRITE_BE_UINT16(ptr, mapNeighbors); ptr += 2;
box.writeToBE(ptr);
WRITE_BE_UINT16(ptr, bottomScaleFactor); ptr += 2;
WRITE_BE_UINT16(ptr, topScaleFactor); ptr += 2;
WRITE_BE_UINT16(ptr, object); ptr += 2;
}
uint16 calcScale(int16 y) const {
uint16 dy = box.yDiff();
int16 ds = scaleDiff();
uint16 scale = 0;
if (dy) // Prevent division-by-zero
scale = ((((y - box.y1) * 100) / dy) * ds) / 100 + bottomScaleFactor;
if (scale == 0)
scale = 100;
return scale;
}
int16 scaleDiff() const {
return (int16)(topScaleFactor - bottomScaleFactor);
}
};
struct WalkOffData {
//! entry in ObjectData
int16 entryObj;
//! coordinates to reach
uint16 x, y;
void readFromBE(byte *&ptr) {
entryObj = (int16)READ_BE_UINT16(ptr); ptr += 2;
x = READ_BE_UINT16(ptr); ptr += 2;
y = READ_BE_UINT16(ptr); ptr += 2;
}
void writeToBE(byte *&ptr) {
WRITE_BE_UINT16(ptr, entryObj); ptr += 2;
WRITE_BE_UINT16(ptr, x); ptr += 2;
WRITE_BE_UINT16(ptr, y); ptr += 2;
}
};
struct GraphicData {
//! coordinates of object
uint16 x, y;
//! bank bobframes
/*!
<table>
<tr>
<td>lastFrame == 0</td>
<td>non-animated bob (one frame)</td>
</tr>
<tr>
<td>lastFrame < 0</td>
<td>rebound animation</td>
</tr>
<tr>
<td>firstFrame < 0</td>
<td>BobSlot::animString (animation is described by a string)</td>
</tr>
<tr>
<td>firstFrame > 0</td>
<td>BobSlot::animNormal (animation is a sequence of frames)</td>
</tr>
</table>
*/
int16 firstFrame, lastFrame;
//! moving speed of object
uint16 speed;
void readFromBE(byte *&ptr) {
x = READ_BE_UINT16(ptr); ptr += 2;
y = READ_BE_UINT16(ptr); ptr += 2;
firstFrame = (int16)READ_BE_UINT16(ptr); ptr += 2;
lastFrame = (int16)READ_BE_UINT16(ptr); ptr += 2;
speed = READ_BE_UINT16(ptr); ptr += 2;
}
};
struct ObjectData {
//! entry in OBJECT_NAME (<0: object is hidden, 0: object has been deleted)
int16 name;
//! coordinates of object
uint16 x, y;
//! entry in OBJECT_DESCR
uint16 description;
//! associated object
int16 entryObj;
//! room in which this object is available
uint16 room;
//! state of the object (grab direction, on/off, default command...)
uint16 state;
//! entry in GraphicData
/*!
<table>
<tr>
<td>value</td>
<td>description</td>
</tr>
<tr>
<td>]-4000..-10]</td>
<td>graphic image turned off</td>
</tr>
<tr>
<td>-4</td>
<td>person object (right facing)</td>
</tr>
<tr>
<td>-3</td>
<td>person object (left facing)</td>
</tr>
<tr>
<td>-2</td>
<td>animated bob (off)</td>
</tr>
<tr>
<td>-1</td>
<td>static bob (off)</td>
</tr>
<tr>
<td>0</td>
<td>object deleted</td>
</tr>
<tr>
<td>]0..5000]</td>
<td>static or animated bob (on)</td>
</tr>
<tr>
<td>]5000.. [</td>
<td>'paste down' bob</td>
</tr>
</table>
*/
int16 image;
void readFromBE(byte *&ptr) {
name = (int16)READ_BE_UINT16(ptr); ptr += 2;
x = READ_BE_UINT16(ptr); ptr += 2;
y = READ_BE_UINT16(ptr); ptr += 2;
description = READ_BE_UINT16(ptr); ptr += 2;
entryObj = (int16)READ_BE_UINT16(ptr); ptr += 2;
room = READ_BE_UINT16(ptr); ptr += 2;
state = READ_BE_UINT16(ptr); ptr += 2;
image = (int16)READ_BE_UINT16(ptr); ptr += 2;
}
void writeToBE(byte *&ptr) {
WRITE_BE_UINT16(ptr, name); ptr += 2;
WRITE_BE_UINT16(ptr, x); ptr += 2;
WRITE_BE_UINT16(ptr, y); ptr += 2;
WRITE_BE_UINT16(ptr, description); ptr += 2;
WRITE_BE_UINT16(ptr, entryObj); ptr += 2;
WRITE_BE_UINT16(ptr, room); ptr += 2;
WRITE_BE_UINT16(ptr, state); ptr += 2;
WRITE_BE_UINT16(ptr, image); ptr += 2;
}
};
struct ObjectDescription {
//! entry in ObjectData or ItemData
uint16 object;
//! type of the description
/*!
refer to select.c l.75-101
<table>
<tr>
<td>value</td>
<td>description</td>
</tr>
<tr>
<td>0</td>
<td>random but starts at first description</td>
<tr>
<td>1</td>
<td>random</td>
</tr>
<tr>
<td>2</td>
<td>sequential with loop</td>
</tr>
<tr>
<td>3</td>
<td>sequential and set description to last</td>
</tr>
</table>
*/
uint16 type;
//! last entry possible in OBJECT_DESCR for this object
uint16 lastDescription;
//! last description number used (in order to avoid re-using it)
uint16 lastSeenNumber;
void readFromBE(byte *&ptr) {
object = READ_BE_UINT16(ptr); ptr += 2;
type = READ_BE_UINT16(ptr); ptr += 2;
lastDescription = READ_BE_UINT16(ptr); ptr += 2;
lastSeenNumber = READ_BE_UINT16(ptr); ptr += 2;
}
void writeToBE(byte *&ptr) {
WRITE_BE_UINT16(ptr, object); ptr += 2;
WRITE_BE_UINT16(ptr, type); ptr += 2;
WRITE_BE_UINT16(ptr, lastDescription); ptr += 2;
WRITE_BE_UINT16(ptr, lastSeenNumber); ptr += 2;
}
};
struct ItemData {
//! entry in OBJECT_NAME
int16 name;
//! entry in OBJECT_DESCR
uint16 description;
//! state of the object
uint16 state;
//! bank bobframe
uint16 frame;
//! entry in OBJECT_DESCR (>0 if available)
int16 sfxDescription;
void readFromBE(byte *&ptr) {
name = (int16)READ_BE_UINT16(ptr); ptr += 2;
description = READ_BE_UINT16(ptr); ptr += 2;
state = READ_BE_UINT16(ptr); ptr += 2;
frame = READ_BE_UINT16(ptr); ptr += 2;
sfxDescription = (int16)READ_BE_UINT16(ptr); ptr += 2;
}
void writeToBE(byte *&ptr) {
WRITE_BE_UINT16(ptr, name); ptr += 2;
WRITE_BE_UINT16(ptr, description); ptr += 2;
WRITE_BE_UINT16(ptr, state); ptr += 2;
WRITE_BE_UINT16(ptr, frame); ptr += 2;
WRITE_BE_UINT16(ptr, sfxDescription); ptr += 2;
}
};
struct ActorData {
//! room in which the actor is
int16 room;
//! bob number associated to this actor
int16 bobNum;
//! entry in ACTOR_NAME
uint16 name;
//! gamestate entry/value, actor is valid if GAMESTATE[slot] == value
int16 gsSlot, gsValue;
//! spoken text color
uint16 color;
//! bank bobframe for standing position of the actor
uint16 bobFrameStanding;
//! initial coordinates in the room
uint16 x, y;
//! entry in ACTOR_ANIM
uint16 anim;
//! bank to use to load the actor file
uint16 bankNum;
//! entry in ACTOR_FILE
uint16 file;
void readFromBE(byte *&ptr) {
room = (int16)READ_BE_UINT16(ptr); ptr += 2;
bobNum = (int16)READ_BE_UINT16(ptr); ptr += 2;
name = READ_BE_UINT16(ptr); ptr += 2;
gsSlot = (int16)READ_BE_UINT16(ptr); ptr += 2;
gsValue = (int16)READ_BE_UINT16(ptr); ptr += 2;
color = READ_BE_UINT16(ptr); ptr += 2;
bobFrameStanding = READ_BE_UINT16(ptr); ptr += 2;
x = READ_BE_UINT16(ptr); ptr += 2;
y = READ_BE_UINT16(ptr); ptr += 2;
anim = READ_BE_UINT16(ptr); ptr += 2;
bankNum = READ_BE_UINT16(ptr); ptr += 2;
file = READ_BE_UINT16(ptr); ptr += 2;
// Fix the actor data (see queen.c - l.1518-1519). When there is no
// valid actor file, we must load the data from the objects room bank.
// This bank has number 15 (not 10 as in the data files).
if (file == 0) {
bankNum = 15;
}
}
};
struct CmdListData {
//! action to perform
Verb verb;
//! first object used in the action
int16 nounObj1;
//! second object used in the action
int16 nounObj2;
//! song to play (>0: playbefore, <0: playafter)
int16 song;
//! if set, P2_SET_AREAS must be called (using CmdArea)
bool setAreas;
//! if set, P3_SET_OBJECTS must be called (using CmdObject)
bool setObjects;
//! if set, P4_SET_ITEMS must be called (using CmdInventory)
bool setItems;
//! if set, P1_SET_CONDITIONS must be called (using CmdGameState)
bool setConditions;
//! graphic image order
int16 imageOrder;
//! special section to execute (refer to execute.c l.423-451)
int16 specialSection;
void readFromBE(byte *&ptr) {
verb = (Verb)READ_BE_UINT16(ptr); ptr += 2;
nounObj1 = (int16)READ_BE_UINT16(ptr); ptr += 2;
nounObj2 = (int16)READ_BE_UINT16(ptr); ptr += 2;
song = (int16)READ_BE_UINT16(ptr); ptr += 2;
setAreas = READ_BE_UINT16(ptr) != 0; ptr += 2;
setObjects = READ_BE_UINT16(ptr) != 0; ptr += 2;
setItems = READ_BE_UINT16(ptr) != 0; ptr += 2;
setConditions = READ_BE_UINT16(ptr) != 0; ptr += 2;
imageOrder = (int16)READ_BE_UINT16(ptr); ptr += 2;
specialSection = (int16)READ_BE_UINT16(ptr); ptr += 2;
}
bool match(const Verb& v, int16 obj1, int16 obj2) const {
return verb == v && nounObj1 == obj1 && nounObj2 == obj2;
}
};
struct CmdArea {
//! CmdListData number
int16 id;
//! area to turn off/on (<0: off, >0: on)
int16 area;
//! room in which the area must be changed
uint16 room;
void readFromBE(byte *&ptr) {
id = (int16)READ_BE_UINT16(ptr); ptr += 2;
area = (int16)READ_BE_UINT16(ptr); ptr += 2;
room = READ_BE_UINT16(ptr); ptr += 2;
}
};
struct CmdObject {
//! CmdListData number
int16 id;
//! >0: show, <0: hide
int16 dstObj;
//! >0: copy from srcObj, 0: nothing, -1: delete dstObj
int16 srcObj;
void readFromBE(byte *&ptr) {
id = (int16)READ_BE_UINT16(ptr); ptr += 2;
dstObj = (int16)READ_BE_UINT16(ptr); ptr += 2;
srcObj = (int16)READ_BE_UINT16(ptr); ptr += 2;
}
};
struct CmdInventory {
//! CmdListData number
int16 id;
//! <0: delete, >0: add
int16 dstItem;
//! >0: valid
int16 srcItem;
void readFromBE(byte *&ptr) {
id = (int16)READ_BE_UINT16(ptr); ptr += 2;
dstItem = (int16)READ_BE_UINT16(ptr); ptr += 2;
srcItem = (int16)READ_BE_UINT16(ptr); ptr += 2;
}
};
struct CmdGameState {
//! CmdListData number
int16 id;
int16 gameStateSlot;
int16 gameStateValue;
uint16 speakValue;
void readFromBE(byte *&ptr) {
id = (int16)READ_BE_UINT16(ptr); ptr += 2;
gameStateSlot = (int16)READ_BE_UINT16(ptr); ptr += 2;
gameStateValue = (int16)READ_BE_UINT16(ptr); ptr += 2;
speakValue = READ_BE_UINT16(ptr); ptr += 2;
}
};
struct FurnitureData {
//! room in which the furniture are
int16 room;
//! furniture object number
/*!
<table>
<tr>
<td>range</td>
<td>type</td>
</tr>
<tr>
<td>]0..5000]</td>
<td>static or animated</td>
</tr>
<tr>
<td>]5000..[</td>
<td>paste down</td>
</tr>
</table>
*/
int16 objNum;
void readFromBE(byte *&ptr) {
room = (int16)READ_BE_UINT16(ptr); ptr += 2;
objNum = (int16)READ_BE_UINT16(ptr); ptr += 2;
}
};
struct GraphicAnim {
int16 keyFrame;
int16 frame;
uint16 speed;
void readFromBE(byte *&ptr) {
keyFrame = (int16)READ_BE_UINT16(ptr); ptr += 2;
frame = (int16)READ_BE_UINT16(ptr); ptr += 2;
speed = READ_BE_UINT16(ptr); ptr += 2;
}
};
struct AnimFrame {
uint16 frame;
uint16 speed;
};
struct Person {
//! actor settings to use
const ActorData *actor;
//! actor name
const char *name;
//! animation string
const char *anim;
//! current frame
uint16 bobFrame;
};
struct TalkSelected {
bool hasTalkedTo;
int16 values[4];
void readFromBE(byte *&ptr) {
hasTalkedTo = READ_BE_UINT16(ptr) != 0; ptr += 2;
for (int i = 0; i < 4; i++) {
values[i] = (int16)READ_BE_UINT16(ptr); ptr += 2;
}
}
void writeToBE(byte *&ptr) {
WRITE_BE_UINT16(ptr, (uint16)hasTalkedTo); ptr += 2;
for (int i = 0; i < 4; i++) {
WRITE_BE_UINT16(ptr, values[i]); ptr += 2;
}
}
};
struct BobFrame {
uint16 width, height;
uint16 xhotspot, yhotspot;
uint8 *data;
};
} // End of namespace Queen
#endif

1815
engines/queen/talk.cpp Normal file

File diff suppressed because it is too large Load Diff

227
engines/queen/talk.h Normal file
View File

@@ -0,0 +1,227 @@
/* 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 QUEEN_TALK_H
#define QUEEN_TALK_H
#include "common/util.h"
#include "queen/structs.h"
namespace Queen {
class QueenEngine;
class Talk {
public:
//! Public interface to run a talk from a file
static void talk(const char *filename, int personInRoom, char *cutawayFilename, QueenEngine *vm);
//! Public interface to speak a sentence
static bool speak(const char *sentence, Person *person, const char *voiceFilePrefix, QueenEngine *vm);
//! Read a string from ptr and update offset
static void getString(const byte *ptr, uint16 &offset, char *str, int maxLength, int align = 2);
private:
//! Collection of constants used by Talk
enum {
LINE_HEIGHT = 10,
MAX_STRING_LENGTH = 255,
MAX_STRING_SIZE = (MAX_STRING_LENGTH + 1),
MAX_TEXT_WIDTH = (320-18),
PUSHUP = 4,
ARROW_ZONE_UP = 5,
ARROW_ZONE_DOWN = 6,
DOG_HEADER_SIZE = 20,
OPTION_TEXT_MARGIN = 24
};
//! Special commands for speech
enum {
SPEAK_DEFAULT = 0,
SPEAK_FACE_LEFT = -1,
SPEAK_FACE_RIGHT = -2,
SPEAK_FACE_FRONT = -3,
SPEAK_FACE_BACK = -4,
SPEAK_ORACLE = -5,
SPEAK_UNKNOWN_6 = -6,
SPEAK_AMAL_ON = -7,
SPEAK_PAUSE = -8,
SPEAK_NONE = -9
};
struct DialogueNode {
int16 head;
int16 dialogueNodeValue1;
int16 gameStateIndex;
int16 gameStateValue;
};
struct SpeechParameters {
const char *name;
signed char state,faceDirection;
signed char body,bf,rf,af;
const char *animation;
signed char ff;
};
QueenEngine *_vm;
//! Raw .dog file data (without 20 byte header)
byte *_fileData;
//! Number of dialogue levels
int16 _levelMax;
//! Unique key for this dialogue
int16 _uniqueKey;
//! Used to select voice files
int16 _talkKey;
int16 _jMax;
//! Used by findDialogueString
int16 _pMax;
// Update game state efter dialogue
int16 _gameState[2];
int16 _testValue[2];
int16 _itemNumber[2];
//! String data
uint16 _person1PtrOff;
//! Cutaway data
uint16 _cutawayPtrOff;
//! Data used if we have talked to the person before
uint16 _person2PtrOff;
//! Data used if we haven't talked to the person before
uint16 _joePtrOff;
//! Is a talking head
bool _talkHead;
//! IDs for sentences
DialogueNode _dialogueTree[18][6];
//! Greeting from person Joe has talked to before
char _person2String[MAX_STRING_SIZE];
int _oldSelectedSentenceIndex;
int _oldSelectedSentenceValue;
char _talkString[5][MAX_STRING_SIZE];
char _joeVoiceFilePrefix[5][MAX_STRING_SIZE];
static const SpeechParameters _speechParameters[];
Talk(QueenEngine *vm);
~Talk();
//! Perform talk in file and return a cutaway filename
void talk(const char *filename, int personInRoom, char *cutawayFilename);
byte *loadDialogFile(const char *filename);
//! Load talk data from .dog file
void load(const char *filename);
//! First things spoken
void initialTalk();
//! Find a string in the dialogue tree
void findDialogueString(uint16 offset, int16 id, int16 max, char *str);
//! Get TalkSelected struct for this talk
TalkSelected *talkSelected();
//! Interface to the TalkSelected struct
bool hasTalkedTo() { return talkSelected()->hasTalkedTo; }
//! Interface to the TalkSelected struct
void setHasTalkedTo() { talkSelected()->hasTalkedTo = true; }
//! Get a selected value
int16 selectedValue(int index) {
return talkSelected()->values[index-1];
}
//! Set a selected value
void selectedValue(int index, int16 value) {
talkSelected()->values[index-1] = value;
}
//! The sentence will not be displayed again
void disableSentence(int oldLevel, int selectedSentence);
//! Select what to say
int16 selectSentence();
//! Speak sentence
bool speak(const char *sentence, Person *person, const char *voiceFilePrefix);
//! Convert command in sentence to command code
int getSpeakCommand(const Person *person, const char *sentence, unsigned &index);
//! Speak a part of a sentence
void speakSegment(
const char *segmentStart,
int length,
Person *person,
int command,
const char *voiceFilePrefix,
int index);
void headStringAnimation(const SpeechParameters *parameters, int bobNum, int bankNum);
void stringAnimation(const SpeechParameters *parameters, int startFrame, int bankNum);
void defaultAnimation(
const char *segment,
bool isJoe,
const SpeechParameters *parameters,
int startFrame,
int bankNum);
int countSpaces(const char *segment);
//! Get special parameters for speech
const SpeechParameters *findSpeechParameters(
const char *name,
int state,
int faceDirection);
int splitOption(const char *str, char optionText[5][MAX_STRING_SIZE]);
int splitOptionHebrew(const char *str, char optionText[5][MAX_STRING_SIZE]);
int splitOptionDefault(const char *str, char optionText[5][MAX_STRING_SIZE]);
};
} // End of namespace Queen
#endif

159
engines/queen/version.cpp Normal file
View File

@@ -0,0 +1,159 @@
/* 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 "queen/version.h"
#include "queen/defs.h"
#include "common/config-manager.h"
#include "common/file.h"
namespace Queen {
//! known FOTAQ versions
static const RetailGameVersion gameVersions[] = {
{ "PEM10", 1, 0x00000008, 22677657 },
{ "CEM10", 1, 0x0000584E, 190787021 },
{ "PFM10", 1, 0x0002CD93, 22157304 },
{ "CFM10", 1, 0x00032585, 186689095 },
{ "PGM10", 1, 0x00059ACA, 22240013 },
{ "CGM10", 1, 0x0005F2A7, 217648975 },
{ "PIM10", 1, 0x000866B1, 22461366 },
{ "CIM10", 1, 0x0008BEE2, 190795582 },
{ "CSM10", 1, 0x000B343C, 190730602 },
{ "CHM10", 1, 0x000DA981, 190705558 },
{ "PE100", 1, 0x00101EC6, 3724538 },
{ "PE100", 1, 0x00102B7F, 3732177 },
{ "PEint", 1, 0x00103838, 1915913 },
{ "aEM10", 2, 0x00103F1E, 351775 },
{ "CE101", 2, 0x00107D8D, 563335 },
{ "PE100", 2, 0x001086D4, 597032 },
{ "aGM10", 3, 0x00108C6A, 344575 }
};
bool detectVersion(DetectedGameVersion *ver, Common::File *f) {
memset(ver, 0, sizeof(DetectedGameVersion));
if (f->readUint32BE() == MKTAG('Q','T','B','L')) {
f->read(ver->str, 6);
f->skip(2);
ver->compression = f->readByte();
ver->features = GF_REBUILT;
ver->queenTblVersion = 0;
ver->queenTblOffset = 0;
} else {
const RetailGameVersion *gameVersion = detectGameVersionFromSize(f->size());
if (gameVersion == nullptr) {
warning("Unknown/unsupported FOTAQ version");
return false;
}
Common::strcpy_s(ver->str, gameVersion->str);
ver->compression = COMPRESSION_NONE;
ver->features = 0;
ver->queenTblVersion = gameVersion->queenTblVersion;
ver->queenTblOffset = gameVersion->queenTblOffset;
// Handle game versions for which versionStr information is irrevelant
if (gameVersion == &gameVersions[VER_AMI_DEMO]) { // CE101
ver->language = Common::EN_ANY;
ver->features |= GF_FLOPPY | GF_DEMO;
ver->platform = Common::kPlatformAmiga;
return true;
}
if (gameVersion == &gameVersions[VER_AMI_INTERVIEW]) { // PE100
ver->language = Common::EN_ANY;
ver->features |= GF_FLOPPY | GF_INTERVIEW;
ver->platform = Common::kPlatformAmiga;
return true;
}
}
switch (ver->str[1]) {
case 'E':
if (Common::parseLanguage(ConfMan.get("language")) == Common::RU_RUS) {
ver->language = Common::RU_RUS;
} else if (Common::parseLanguage(ConfMan.get("language")) == Common::EL_GRC) {
ver->language = Common::EL_GRC;
} else {
ver->language = Common::EN_ANY;
}
break;
case 'F':
ver->language = Common::FR_FRA;
break;
case 'G':
ver->language = Common::DE_DEU;
break;
case 'H':
ver->language = Common::HE_ISR;
break;
case 'I':
ver->language = Common::IT_ITA;
break;
case 'S':
ver->language = Common::ES_ESP;
break;
case 'g':
ver->language = Common::EL_GRC;
break;
case 'R':
ver->language = Common::RU_RUS;
break;
default:
error("Invalid language id '%c'", ver->str[1]);
break;
}
switch (ver->str[0]) {
case 'P':
ver->features |= GF_FLOPPY;
ver->platform = Common::kPlatformDOS;
break;
case 'C':
ver->features |= GF_TALKIE;
ver->platform = Common::kPlatformDOS;
break;
case 'a':
ver->features |= GF_FLOPPY;
ver->platform = Common::kPlatformAmiga;
break;
default:
error("Invalid platform id '%c'", ver->str[0]);
break;
}
if (strcmp(ver->str + 2, "100") == 0 || strcmp(ver->str + 2, "101") == 0) {
ver->features |= GF_DEMO;
} else if (strcmp(ver->str + 2, "int") == 0) {
ver->features |= GF_INTERVIEW;
}
return true;
}
const RetailGameVersion *detectGameVersionFromSize(uint32 size) {
for (int i = 0; i < VER_COUNT; ++i) {
if (gameVersions[i].dataFileSize == size) {
return &gameVersions[i];
}
}
return nullptr;
}
} // End of namespace Queen

59
engines/queen/version.h Normal file
View File

@@ -0,0 +1,59 @@
/* 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 QUEEN_VERSION_H
#define QUEEN_VERSION_H
#include "common/language.h"
#include "common/platform.h"
namespace Common {
class File;
}
namespace Queen {
struct DetectedGameVersion {
Common::Platform platform;
Common::Language language;
uint8 features;
uint8 compression;
char str[6];
uint8 queenTblVersion;
uint32 queenTblOffset;
};
struct RetailGameVersion {
char str[6];
uint8 queenTblVersion;
uint32 queenTblOffset;
uint32 dataFileSize;
};
//! detect game version
bool detectVersion(DetectedGameVersion *ver, Common::File *f);
//! detect game version based on queen.1 datafile size
const RetailGameVersion *detectGameVersionFromSize(uint32 size);
} // End of namespace Queen
#endif

563
engines/queen/walk.cpp Normal file
View File

@@ -0,0 +1,563 @@
/* 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/textconsole.h"
#include "queen/walk.h"
#include "queen/bankman.h"
#include "queen/input.h"
#include "queen/logic.h"
#include "queen/graphics.h"
#include "queen/grid.h"
#include "queen/queen.h"
namespace Queen {
const MovePersonData Walk::_moveData[] = {
{ "COMPY", -1, -6, 1, 6, 0, 0, 0, 0, 12, 12, 1, 14 },
{ "DEINO", -1, -8, 1, 8, 0, 0, 0, 0, 11, 11, 1, 10 },
{ "FAYE", -1, -6, 1, 6, 13, 18, 7, 12, 19, 22, 2, 5 },
{ "GUARDS", -1, -6, 1, 6, 0, 0, 0, 0, 7, 7, 2, 5 },
{ "PRINCESS1", -1, -6, 1, 6, 13, 18, 7, 12, 19, 21, 2, 5 },
{ "PRINCESS2", -1, -6, 1, 6, 13, 18, 7, 12, 19, 21, 2, 5 },
{ "AMGUARD", -1, -6, 1, 6, 13, 18, 7, 12, 19, 21, 2, 5 },
{ "SPARKY", -1, -6, 1, 6, 13, 18, 7, 12, 21, 20, 2, 5 },
{ "LOLA_SHOWER", -1, -6, 55, 60, 0, 0, 0, 0, 7, 7, 2, 5 },
{ "LOLA", -24, -29, 24, 29, 0, 0, 0, 0, 30, 30, 2, 5 },
{ "BOB", -15, -20, 15, 20, 21, 26, 0, 0, 27, 29, 2, 5 },
{ "CHEF", -1, -4, 1, 4, 0, 0, 0, 0, 1, 5, 2, 4 },
{ "HENRY", -1, -6, 1, 6, 0, 0, 0, 0, 7, 7, 2, 6 },
{ "ANDERSON", -1, -6, 1, 6, 0, 0, 0, 0, 7, 7, 2, 5 },
{ "JASPAR", -4, -9, 4, 9, 16, 21, 10, 15, 1, 3, 1, 10 },
{ "PYGMY", -7, -12, 7, 12, 0, 0, 0, 0, 27, 27, 2, 5 },
{ "FRANK", 7, 12, 1, 6, 0, 0, 0, 0, 13, 13, 2, 4 },
{ "WEDGEWOOD", -20, -25, 20, 25, 0, 0, 0, 0, 1, 1, 1, 5 },
{ "TMPD", -1, -6, 1, 6, 13, 18, 7, 12, 19, 21, 2, 5 },
{ "IAN", -1, -6, 1, 6, 0, 0, 0, 0, 7, 7, 2, 6 },
{ "*", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
};
Walk::Walk(QueenEngine *vm)
: _vm(vm) {
}
void Walk::animateJoePrepare() {
// queen.c l.2748-2788
uint16 i;
for (i = 1; i <= _walkDataCount; ++i) {
WalkData *pwd = &_walkData[i];
if (pwd->dx < 0) {
pwd->anim.set(11, 18, DIR_LEFT);
} else {
pwd->anim.set(11, 18, DIR_RIGHT);
}
int16 k = ABS(pwd->dy);
int16 ds = pwd->area->scaleDiff();
if (ds > 0) {
k *= ((k * ds) / pwd->area->box.yDiff()) / 2;
}
if (ABS(pwd->dx) < k) {
if (pwd->dy < 0) {
if (ds < 0) {
pwd->anim.set(19, 24, DIR_FRONT);
} else {
pwd->anim.set(25, 30, DIR_BACK);
}
} else if (pwd->dy > 0) {
if (ds < 0) {
pwd->anim.set(25, 30, DIR_BACK);
} else {
pwd->anim.set(19, 24, DIR_FRONT);
}
}
}
}
}
void Walk::animateJoe() {
// queen.c l.2789-2835
uint16 lastDirection = 0;
uint16 i;
BobSlot *pbs = _vm->graphics()->bob(0);
_vm->logic()->joeFacing(_walkData[1].anim.facing);
_vm->logic()->joeScale(_walkData[1].area->calcScale(pbs->y));
_vm->logic()->joeFace();
for (i = 1; i <= _walkDataCount && !_joeInterrupted; ++i) {
WalkData *pwd = &_walkData[i];
// area has been turned off, see if we should execute a cutaway
if (pwd->area->mapNeighbors < 0) {
// queen.c l.2838-2911
_vm->logic()->handleSpecialArea(pwd->anim.facing, pwd->areaNum, i);
_joeMoveBlock = true;
return;
}
if (lastDirection != pwd->anim.facing) {
pbs->animNormal(pwd->anim.firstFrame, pwd->anim.lastFrame, 1, false, false);
}
uint16 moveSpeed = _vm->grid()->findScale(pbs->x, pbs->y) * 6 / 100;
pbs->move(pbs->x + pwd->dx, pbs->y + pwd->dy, moveSpeed);
pbs->xflip = (pbs->xdir < 0);
while (pbs->moving) {
// adjust Joe's movespeed according to scale
pbs->scale = pwd->area->calcScale(pbs->y);
_vm->logic()->joeScale(pbs->scale);
pbs->scaleWalkSpeed(6);
_vm->update(true);
if (_vm->input()->cutawayQuit() || _vm->logic()->joeWalk() == JWM_EXECUTE || _vm->shouldQuit()) {
stopJoe();
break;
}
}
lastDirection = pwd->anim.facing;
}
_vm->logic()->joeFacing(lastDirection);
}
void Walk::animatePersonPrepare(const MovePersonData *mpd, int direction) {
// queen.c l.2469-2572
int i;
for (i = 1; i <= _walkDataCount; ++i) {
WalkData *pwd = &_walkData[i];
if (pwd->dx < 0) {
pwd->anim.set(mpd->walkLeft1, mpd->walkLeft2, DIR_LEFT);
} else if (pwd->dx > 0) {
pwd->anim.set(mpd->walkRight1, mpd->walkRight2, DIR_RIGHT);
} else {
if (ABS(mpd->walkLeft1) == ABS(mpd->walkRight1)) {
pwd->anim.set(mpd->walkRight1, mpd->walkRight2, DIR_RIGHT);
} else {
// we have specific moves for this actor, see what direction they were last facing
if (direction == -3) {
// previously facing right
pwd->anim.set(mpd->walkLeft1, mpd->walkLeft2, DIR_LEFT);
} else {
// previously facing left
pwd->anim.set(mpd->walkRight1, mpd->walkRight2, DIR_RIGHT);
}
}
}
int16 k = ABS(pwd->dy);
int16 ds = pwd->area->scaleDiff();
if (ds > 0) {
k *= ((k * ds) / pwd->area->box.yDiff()) / 2;
}
if (ABS(pwd->dx) < k) {
if (pwd->dy < 0) {
if (mpd->walkBack1 > 0) {
pwd->anim.set(mpd->walkBack1, mpd->walkBack2, DIR_BACK);
} else if (pwd->dx < 0) {
pwd->anim.set(mpd->walkLeft1, mpd->walkLeft2, DIR_BACK);
} else {
pwd->anim.set(mpd->walkRight1, mpd->walkRight2, DIR_BACK);
}
} else if (pwd->dy > 0) {
if (mpd->walkFront1 > 0) {
pwd->anim.set(mpd->walkFront1, mpd->walkFront2, DIR_FRONT);
} else if (ABS(mpd->walkLeft1) == ABS(mpd->walkRight1)) {
if (pwd->dx < 0) {
pwd->anim.set(mpd->walkLeft1, mpd->walkLeft2, DIR_FRONT);
} else {
pwd->anim.set(mpd->walkRight1, mpd->walkRight2, DIR_FRONT);
}
} else {
// we have a special move for left/right, so select that instead!
if (direction == -3) {
// previously facing right
pwd->anim.set(mpd->walkLeft1, mpd->walkLeft2, DIR_FRONT);
} else {
// previously facing left
pwd->anim.set(mpd->walkRight1, mpd->walkRight2, DIR_FRONT);
}
}
}
}
}
}
void Walk::animatePerson(const MovePersonData *mpd, uint16 image, uint16 bobNum, uint16 bankNum, int direction) {
// queen.c l.2572-2651
BobSlot *pbs = _vm->graphics()->bob(bobNum);
// check to see which way person should be facing
if (mpd->walkLeft1 == mpd->walkRight1) {
pbs->xflip = (direction == -3);
} else {
// they have special walk for left and right, so don't flip
pbs->xflip = false;
}
uint16 i;
for (i = 1; i <= _walkDataCount; ++i) {
WalkData *pwd = &_walkData[i];
// unpack necessary frames for bob animation
uint16 dstFrame = image;
uint16 srcFrame = ABS(pwd->anim.firstFrame);
while (srcFrame <= ABS(pwd->anim.lastFrame)) {
_vm->bankMan()->unpack(srcFrame, dstFrame, bankNum);
++dstFrame;
++srcFrame;
}
// pass across bobs direction ONLY if walk is a mirror flip!
if (ABS(mpd->walkLeft1) == ABS(mpd->walkRight1)) {
pbs->animNormal(image, dstFrame - 1, mpd->animSpeed, false, pbs->xflip);
} else {
pbs->animNormal(image, dstFrame - 1, mpd->animSpeed, false, false);
}
// move other actors at correct speed relative to scale
uint16 moveSpeed = _vm->grid()->findScale(pbs->x, pbs->y) * mpd->moveSpeed / 100;
pbs->move(pbs->x + pwd->dx, pbs->y + pwd->dy, moveSpeed);
// flip if one set of frames for actor
if (mpd->walkLeft1 < 0 || ABS(mpd->walkLeft1) == ABS(mpd->walkRight1)) {
pbs->xflip = pwd->dx < 0;
}
while (pbs->moving) {
_vm->update();
pbs->scale = pwd->area->calcScale(pbs->y);
pbs->scaleWalkSpeed(mpd->moveSpeed);
if (_vm->input()->cutawayQuit() || _vm->shouldQuit()) {
stopPerson(bobNum);
break;
}
}
}
}
int16 Walk::moveJoe(int direction, int16 endx, int16 endy, bool inCutaway) {
_joeInterrupted = false;
_joeMoveBlock = false;
int16 can = 0;
initWalkData();
uint16 oldx = _vm->graphics()->bob(0)->x;
uint16 oldy = _vm->graphics()->bob(0)->y;
_vm->logic()->joeWalk(JWM_MOVE);
uint16 oldPos = _vm->grid()->findAreaForPos(GS_ROOM, oldx, oldy);
uint16 newPos = _vm->grid()->findAreaForPos(GS_ROOM, endx, endy);
debug(9, "Walk::moveJoe(%d, %d, %d, %d, %d) - old = %d, new = %d", direction, oldx, oldy, endx, endy, oldPos, newPos);
// if in cutaway, allow Joe to walk anywhere
if (newPos == 0 && inCutaway) {
incWalkData(oldx, oldy, endx, endy, oldPos);
} else {
if (calc(oldPos, newPos, oldx, oldy, endx, endy)) {
if (_walkDataCount > 0) {
animateJoePrepare();
animateJoe();
if (_joeInterrupted) {
can = -1;
}
}
} else {
// path has been blocked, make Joe say so
_vm->logic()->makeJoeSpeak(4);
can = -1;
}
}
_vm->graphics()->bob(0)->animating = false;
if (_joeMoveBlock) {
can = -2;
_joeMoveBlock = false;
} else if (direction > 0) {
_vm->logic()->joeFacing(direction);
}
_vm->logic()->joePrevFacing(_vm->logic()->joeFacing());
_vm->logic()->joeFace();
return can;
}
int16 Walk::movePerson(const Person *pp, int16 endx, int16 endy, uint16 curImage, int direction) {
if (endx == 0 && endy == 0) {
warning("Walk::movePerson() - endx == 0 && endy == 0");
return 0;
}
int16 can = 0;
initWalkData();
uint16 bobNum = pp->actor->bobNum;
uint16 bankNum = pp->actor->bankNum;
uint16 oldx = _vm->graphics()->bob(bobNum)->x;
uint16 oldy = _vm->graphics()->bob(bobNum)->y;
uint16 oldPos = _vm->grid()->findAreaForPos(GS_ROOM, oldx, oldy);
uint16 newPos = _vm->grid()->findAreaForPos(GS_ROOM, endx, endy);
debug(9, "Walk::movePerson(%d, %d, %d, %d, %d) - old = %d, new = %d", direction, oldx, oldy, endx, endy, oldPos, newPos);
// find MovePersonData associated to Person
const MovePersonData *mpd = _moveData;
while (mpd->name[0] != '*') {
if (scumm_stricmp(mpd->name, pp->name) == 0) {
break;
}
++mpd;
}
if (calc(oldPos, newPos, oldx, oldy, endx, endy)) {
if (_walkDataCount > 0) {
animatePersonPrepare(mpd, direction);
animatePerson(mpd, curImage, bobNum, bankNum, direction);
}
} else {
can = -1;
}
uint16 standingFrame = 31 + bobNum;
// make other person face the right direction
BobSlot *pbs = _vm->graphics()->bob(bobNum);
pbs->endx = endx;
pbs->endy = endy;
pbs->animating = false;
pbs->scale = _walkData[_walkDataCount].area->calcScale(endy);
if (_walkData[_walkDataCount].anim.facing == DIR_BACK) {
_vm->bankMan()->unpack(mpd->backStandingFrame, standingFrame, bankNum);
} else {
_vm->bankMan()->unpack(mpd->frontStandingFrame, standingFrame, bankNum);
}
uint16 obj = _vm->logic()->objectForPerson(bobNum);
if (_walkData[_walkDataCount].dx < 0) {
_vm->logic()->objectData(obj)->image = -3;
pbs->xflip = true;
} else {
_vm->logic()->objectData(obj)->image = -4;
pbs->xflip = false;
}
pbs->frameNum = standingFrame;
return can;
}
void Walk::stopJoe() {
BobSlot *pbs = _vm->graphics()->bob(0);
pbs->moving = false;
_joeInterrupted = true;
}
void Walk::stopPerson(uint16 bobNum) {
BobSlot *pbs = _vm->graphics()->bob(bobNum);
pbs->x = pbs->endx;
pbs->y = pbs->endy;
pbs->moving = false;
}
bool Walk::calc(uint16 oldPos, uint16 newPos, int16 oldx, int16 oldy, int16 x, int16 y) {
// if newPos is outside of an AREA then traverse Y axis until an AREA is found
if (newPos == 0) {
newPos = findAreaPosition(&x, &y, true);
}
// do the same for oldPos in case Joe somehow sits on the border of an AREA
// and does not register
if (oldPos == 0) {
oldPos = findAreaPosition(&oldx, &oldy, false);
}
if (oldPos == newPos) {
incWalkData(oldx, oldy, x, y, newPos);
return true;
} else if (calcPath(oldPos, newPos)) {
uint16 i;
int16 px = oldx;
int16 py = oldy;
for (i = 2; i <= _areaListCount; ++i) {
uint16 a1 = _areaList[i - 1];
uint16 a2 = _areaList[i];
const Area *pa1 = &_roomArea[a1];
const Area *pa2 = &_roomArea[a2];
uint16 x1 = calcC(pa1->box.x1, pa1->box.x2, pa2->box.x1, pa2->box.x2, px);
uint16 y1 = calcC(pa1->box.y1, pa1->box.y2, pa2->box.y1, pa2->box.y2, py);
incWalkData(px, py, x1, y1, a1);
px = x1;
py = y1;
}
incWalkData(px, py, x, y, newPos);
return true;
}
return false;
}
int16 Walk::calcC(int16 c1, int16 c2, int16 c3, int16 c4, int16 lastc) {
int16 s1 = MAX(c1, c3);
int16 s2 = MIN(c2, c4);
int16 c;
if ((lastc >= s1 && lastc <= s2) || (lastc >= s2 && lastc <= s1)) {
c = lastc;
} else {
c = (s1 + s2) / 2;
}
return c;
}
int16 Walk::findAreaPosition(int16 *x, int16 *y, bool recalibrate) {
// In order to locate the nearest available area, the original algorithm
// computes the horizontal and vertical distances for each available area.
// Unlike the original, we also compute the diagonal distance.
// To get an example of this in action, in the room D1, make Joe walking
// to the wall at the right of the window (just above the radiator). On the
// original game, Joe will go to the left door...
uint16 i;
uint16 pos = 1;
uint32 minDist = (uint32)~0;
const Box *b = &_roomArea[1].box;
for (i = 1; i <= _roomAreaCount; ++i) {
b = &_roomArea[i].box;
uint16 dx1 = ABS(b->x1 - *x);
uint16 dx2 = ABS(b->x2 - *x);
uint16 dy1 = ABS(b->y1 - *y);
uint16 dy2 = ABS(b->y2 - *y);
uint16 csx = MIN(dx1, dx2);
uint16 csy = MIN(dy1, dy2);
bool inX = (*x >= b->x1) && (*x <= b->x2);
bool inY = (*y >= b->y1) && (*y <= b->y2);
uint32 dist = minDist;
if (!inX && !inY) {
dist = csx * csx + csy * csy;
} else if (inX) {
dist = csy * csy;
} else if (inY) {
dist = csx * csx;
}
if (dist < minDist) {
minDist = dist;
pos = i;
}
}
// we now have the closest area near X,Y, so we can recalibrate
// the X,Y coord to be in this area
if (recalibrate) {
b = &_roomArea[pos].box;
if (*x < b->x1) *x = b->x1;
if (*x > b->x2) *x = b->x2;
if (*y < b->y1) *y = b->y1;
if (*y > b->y2) *y = b->y2;
}
return pos;
}
uint16 Walk::findFreeArea(uint16 area) const {
uint16 testArea;
uint16 freeArea = 0;
uint16 map = ABS(_roomArea[area].mapNeighbors);
for (testArea = 1; testArea <= _roomAreaCount; ++testArea) {
int b = _roomAreaCount - testArea;
if (map & (1 << b)) {
// connecting area, check if it's been struck off
if (!isAreaStruck(testArea)) {
// legitimate connecting area, keep it
freeArea = testArea;
break;
}
}
}
return freeArea;
}
bool Walk::isAreaStruck(uint16 area) const {
uint16 i;
bool found = false;
for (i = 1; i <= _areaStrikeCount; ++i) {
if (_areaStrike[i] == area) {
found = true;
break;
}
}
return found;
}
bool Walk::calcPath(uint16 oldArea, uint16 newArea) {
debug(9, "Walk::calcPath(%d, %d)", oldArea, newArea);
_areaList[1] = _areaStrike[1] = oldArea;
_areaListCount = _areaStrikeCount = 1;
uint16 area = oldArea;
while (_areaListCount > 0 && area != newArea) {
area = findFreeArea(area);
if (!area) {
// wrong path, rolling back
_areaList[_areaListCount] = 0;
--_areaListCount;
area = _areaList[_areaListCount];
} else {
++_areaListCount;
assert(_areaListCount < MAX_WALK_DATA);
_areaList[_areaListCount] = area;
if (!isAreaStruck(area)) {
++_areaStrikeCount;
assert(_areaStrikeCount < MAX_WALK_DATA);
_areaStrike[_areaStrikeCount] = area;
}
}
}
return _areaList[1] != 0;
}
void Walk::initWalkData() {
uint16 curRoom = _vm->logic()->currentRoom();
_roomArea = _vm->grid()->area(curRoom, 0);
_roomAreaCount = _vm->grid()->areaMax(curRoom);
_walkDataCount = 0;
memset(_walkData, 0, sizeof(_walkData));
_areaStrikeCount = 0;
memset(_areaStrike, 0, sizeof(_areaStrike));
_areaListCount = 0;
memset(_areaList, 0, sizeof(_areaList));
}
void Walk::incWalkData(int16 px, int16 py, int16 x, int16 y, uint16 areaNum) {
debug(9, "Walk::incWalkData(%d, %d, %d)", (x - px), (y - py), areaNum);
if (px != x || py != y) {
++_walkDataCount;
assert(_walkDataCount < MAX_WALK_DATA);
WalkData *pwd = &_walkData[_walkDataCount];
pwd->dx = x - px;
pwd->dy = y - py;
pwd->area = &_roomArea[areaNum];
pwd->areaNum = areaNum;
}
}
} // End of namespace Queen

143
engines/queen/walk.h Normal file
View File

@@ -0,0 +1,143 @@
/* 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 QUEEN_WALK_H
#define QUEEN_WALK_H
#include "common/util.h"
#include "queen/structs.h"
namespace Queen {
struct MovePersonAnim {
int16 firstFrame;
int16 lastFrame;
Direction facing;
void set(int16 ff, int16 lf, Direction dir) {
firstFrame = ff;
lastFrame = lf;
facing = dir;
}
};
struct WalkData {
int16 dx, dy;
const Area *area;
uint16 areaNum;
MovePersonAnim anim;
};
struct MovePersonData {
const char *name;
int16 walkLeft1, walkLeft2;
int16 walkRight1, walkRight2;
int16 walkBack1, walkBack2;
int16 walkFront1, walkFront2;
uint16 frontStandingFrame;
uint16 backStandingFrame;
uint16 animSpeed;
uint16 moveSpeed;
};
class QueenEngine;
class Walk {
public:
Walk(QueenEngine *vm);
int16 moveJoe(int direction, int16 endx, int16 endy, bool inCutaway);
int16 movePerson(const Person *pp, int16 endx, int16 endy, uint16 curImage, int direction);
void stopJoe();
void stopPerson(uint16 bobNum);
enum {
MAX_WALK_DATA = 16
};
private:
void animateJoePrepare();
void animateJoe();
void animatePersonPrepare(const MovePersonData *mpd, int direction);
void animatePerson(const MovePersonData *mpd, uint16 image, uint16 bobNum, uint16 bankNum, int direction);
/// compute transition coordinate
static int16 calcC(int16 c1, int16 c2, int16 c3, int16 c4, int16 lastc);
/// find area for position
int16 findAreaPosition(int16 *x, int16 *y, bool recalibrate);
/// find an area not already struck
uint16 findFreeArea(uint16 area) const;
/// return true if the area is already on the walking path
bool isAreaStruck(uint16 area) const;
/// calculates the path list from oldArea to newArea
bool calcPath(uint16 oldArea, uint16 newArea);
/// resets path computed in calcPath()
void initWalkData();
/// add an area to the path
void incWalkData(int16 px, int16 py, int16 x, int16 y, uint16 area);
/// compute path (and populates _walkData) from current position to the new one
bool calc(uint16 oldPos, uint16 newPos, int16 oldx, int16 oldy, int16 x, int16 y);
/// areas for current room
const Area *_roomArea;
/// number of areas for current room
uint16 _roomAreaCount;
/// walking steps
WalkData _walkData[MAX_WALK_DATA];
/// number of walking steps
uint16 _walkDataCount;
uint16 _areaStrike[MAX_WALK_DATA];
uint16 _areaStrikeCount;
uint16 _areaList[MAX_WALK_DATA];
uint16 _areaListCount;
/// set if stopJoe() is called
bool _joeInterrupted;
/// set if handleSpecialArea() is called
bool _joeMoveBlock;
QueenEngine *_vm;
/// persons walking animation data
static const MovePersonData _moveData[];
};
} // End of namespace Queen
#endif

496
engines/queen/xref.txt Normal file
View File

@@ -0,0 +1,496 @@
$Id$
Cross-reference for functions and variables for the original source code and
the ScummVM implementation.
BANKS
=====
erase() BankManager::close
freeallframes() BankManager::eraseFrames(true)
freeframes() BankManager::eraseFrames(false)
loadbank() BankManager::load
overpack() BankManager::overpack
unpack() BankManager::unpack
COMMAND
=======
ALTER_DEFAULT() *not needed* (use State::alterDefaultVerb)
CLEAR_COMMAND() Command::clear
EXECUTE_ACTION() Command::executeCurrentAction
FIND_DEFAULT() *not needed* (use State::findDefaultVerb)
LOOK() Command::lookAtSelectedObject
LOOK_ICON(),LOOK_ITEM() Command::lookForCurrentIcon
LOOK_ROOM() Command::lookForCurrentObject
OPEN_CLOSE_OTHER() Command::openOrCloseAssociatedObject
P1_SET_CONDITIONS() Command::setConditions
P2_SET_AREAS() Command::setAreas
P3_SET_OBJECTS() Command::setObjects
P4_SET_ITEMS() Command::setItems
SELECT() Command::grabCurrentSelection
SELECT_ITEM() Command::grabSelectedItem
SELECT_NOUN() Command::grabSelectedNoun
SELECT_VERB() Command::grabSelectedVerb
WALK() Command::makeJoeWalkTo
-
ACTION Command::_state.action
ACTION2 Command::_state.selAction
CLEVEL Command::_state.commandLevel
COM_A Command::_cmdArea
COM_A_MAX Command::_numCmdArea
COM_O Command::_cmdObject
COM_O_MAX Command::_numCmdObject
COM_G Command::_cmdGameState
COM_G_MAX Command::_numCmdGameState
COM_I Command::_cmdInventory
COM_I_MAX Command::_numCmdInventory
COM_LIST Command::_cmdList
COM_LIST_MAX Command::_numCmdList
COMMANDstr Command::_cmdText
DEFCOMM Command::_state.defaultVerb
MKEY Command::_mouseKey
OLDVERB,VERB Command::_state.*verb
OLDNOUN,NOUN Command::_state.*noun
NOUN2 Command::_state.selNoun
PARSE Command::_parse
SUBJ1,SUBJ2 Command::_state.subject*
CREDIT SCRIPTING SYSTEM
=======================
Cinit() Credits::Credits()
Ctext() *not needed* (included in Credits::update)
Cupdate() Credits::update
-
Ccol Credits::_color
Ccount Credits::_count
Cfp
Cflag Credits::_running
Cfontsize Credits::_fontSize
Cjustify Credits::_justify
Cpausecount Credits::_pause
Czone Credits::_zone
CUTAWAY
=======
action_special_move() Cutaway::actionSpecialMove
CUTAWAY() Cutaway::run
MAKE_COMPLEX_ANIM() Cutaway::makeComplexAnimation
SCENE_START() Logic::sceneStart
SCENE_END() Logic::sceneStop
-
CUTON Input::_cutawayRunning
CUTQUIT Input::_cutawayQuit
FINAL_ROOM Cutaway::_finalRoom
IROOM Cutaway::_initialRoom
OBJ_CUT
OBJ_ANIM
OLDBANK
PERSON_DATA
SCENE Logic::_scene
TROOM Cutaway::_temporaryRoom
DEBUG
=====
cd_sample_check()
debuginfo() Debugger::Cmd_Info
select_new_room() Debugger::Cmd_Room
-
AREAVAR (boolean, if true display objects/areas boxes)
GAME SETTINGS
=============
game_load() Logic::gameLoad
game_save() Logic::gameSave
-
config_request
MUSICTOGGLE Sound::_musicToggle / ConfMan.("music_mute")
SFXTOGGLE Sound::_sfxToggle / ConfMan.("sfx_mute")
TALKSPD QueenEngine::_talkSpeed / ConfMan.("talkspeed")
TEXTTOGGLE QueenEngine::_subtitles / ConfMan.("subtitles")
VersionStr GameVersion::versionString
VOICETOGGLE Sound::_speechToggle / ConfMan.("speech_mute")
VOLUME ? / ConfMan.("master_volume")
GRAPHICS
========
bob() Graphics::drawBob
CHECK_PARALLAX() Graphics::handleParallax
clearallbobs() Graphics::clearBobs
clearbob() BobSlot::clear
DISP_OBJECTS() Graphics::setupRoomObjects
drawbobs() Graphics::drawBobs
invbob() Graphics::drawInventoryItem
loadbackdrop() *not needed* (included in Display::setupNewRoom)
loadpanel() Display::setupPanel
MAKE_SPEAK_BOB() Graphics::setBobText
makeanim() BobSlot::animNormal
movebob() BobSlot::move
pastebob() Graphics::pasteBob
REDISP_OBJECT() Graphics::refreshObject
requestor()
shrinkbob() Graphics::shrinkFrame
sortbobs() Graphics::sortBobs
stringanim() BobSlot::animString
-
bobs Graphics::_bobs
cambob Graphics::_cameraBob
sortedbobs Graphics::_sortedBobs
INPUT
=====
check_keys() Input::checkKeys()
get_key() *not needed*
-
drawmouseflag *not needed* (equivalent to _display->showMouseCursor(bool))
key_commands Input::_currentCommandKeys
key_language Input::_commandKeys
KEYVERB Input::_keyVerb
MouseButton Input::_mouseButton
mouseflag *not needed*
no_check_keys Input::_noCheckKeys
INVENTORY
=========
DEL_ITEM_NUM() Logic::inventoryDeleteItem
INS_ITEM_NUM() Logic::inventoryInsertItem
INVDWN() Logic::inventoryScroll
INVENTORY() Logic::inventoryRefresh
INVUP() Logic::inventoryScroll
SETUP_ITEMS() Logic::inventorySetup
-
INV1,INV2,INV3,INV4 Logic::_inventoryItem
JOE
===
FACE_JOE() Logic::joeFace
GRAB_DIR(),GRAB_JOE() Logic::joeGrab
SETUP_HERO() Logic::setupJoeInRoom
SETUP_JOE() Logic::setupJoe
USE_UNDERWEAR() Logic::joeUseUnderwear
USE_CLOTHES() Logic::joeUseClothes
USE_DRESS() Logic::joeUseDress
-
CUTJOEF Logic::_joe.cutFacing
JOE_RESPstr Logic::_joeResponse
JOEF,JX,JY,JDIR Logic::_joe.*
JOEWALK Logic::_joe.walk
JOURNAL
=======
clearlefttext() Journal::clearPanelTexts
drawnames() Journal::drawSaveDescriptions
findsaves() Journal::findSaveDescriptions
menutext() Journal::drawPanelText
predrawbobs() Journal::drawConfigPanel / Journal::drawNormalPanel
prompt_do() *not needed*
USE_JOURNAL() Logic::useJournal
waitmousezone() *not needed*
-
choice Journal::_currentSaveSlot
decbase Journal::_currentSavePage
in_journal *not needed* (the hack in puttext() seems useless and CHECK_PARALLAX() is never called)
save_descriptions Journal::_saveDescriptions
walkgameload *not needed ?*
LOGIC
=====
CHECK_PLAYER() QueenEngine::update
CUTAWAY_SPECIAL() Logic::removeHotelItemsFromInventory
DISP_ROOM() Logic::displayRoom
FIND_BOB() Logic::findBob
FIND_FRAME() Logic::findFrame
FIND_GRAPHIC() Logic::graphicData
P3_COPY_FROM() Logic::objectCopy
R_MAP() Logic::handlePinnacleRoom
restart_game()
SETUP_BOBS() Graphics::unpackControlBank / Graphics::setupMouseCursor
SETUP_FURNITURE() Graphics::setupRoomFurniture
SETUP_ROOM() Logic::changeRoom
SETUP_SCREENS() *not needed* (only calls Display::setupPanel)
SETUP_VARS() *not needed* (equivalent to Command::clear(), SCENE=0, clear(gamestate))
update() QueenEngine::update
-
A_ANIMstr Logic::_aAnim
A_ANIM_MAX Logic::_numAAnim
A_NAMEstr Logic::_aName
A_NAME_MAX Logic::_numAName
A_FILEstr Logic::_aFile
A_FILE_MAX Logic::_numAFile
ACTOR_DATA_MAX Logic::_numActors
bamflag BamScene::_flag
bamindex BamScene::_index
DESCTOT Logic::_numDescriptions
ENTRY_OBJ Logic::_entryObj
FMAX Logic::_numFurnitureStatic
FMAXA Logic::_numFurnitureAnimated
FMAXLEN Logic::_numFurnitureAnimatedLen
FRAMES Logic::_numFrames
FURN_DATA_MAX Logic::_numFurniture
GAMESTATE Logic::_gameState
GRAPHIC_ANIM_MAX Logic::_numGraphicAnim
GRAPHIC_DATA Logic::_graphicData
GRAPHIC_MAX Logic::_numGraphics
ITEMTOT Logic::_numItems
ITEM_DATA Logic::_itemData
NAMETOT Logic::_numNames
OBJ_DESC_DATA Logic::_objectDescription
OBJ_DESC_MAX Logic::_numObjDesc
OBJECT_DATA Logic::_objectData
OBJECT_DESCRstr Logic::_objDescription
OBJECT_NAMEstr Logic::_objName
OBJTOT Logic::_numObjects
OLDROOM,ROOM,NEW_ROOM Logic::_*oom
ROOMTOT Logic::_numRooms
ROOM_DATA Logic::_roomData
ROOM_NAMEstr Logic::_roomName
SFACTOR Logic::_joe.scale
VERB_NAMEstr Logic::_verbName
WALK_OFF_DATA Logic::_walkOffData
WALK_OFF_MAX Logic::_numWalkOffs
PERSONS
=======
ALLOCATE_PERSON() Logic::allocatePersonFrames
CREATE_ANIM() Graphics::setupPersonAnim
SET_PERSON_DATA() Logic::initPerson
SETUP_PERSON() Logic::setupPersonInRoom
OBJ_PERSON() Logic::objectForPerson
-
NEW_ANIM Graphics::_newAnim
PERSON_FACE
PERSON_FACE_MAX
PERSON_FRAMES Logic::_personFrames
P_ANIMstr Person.anim
P_NAMEstr Person.name
P_STAND,P_BNUM,P_ROOM Person.actor->*
P_BANK,P_X,P_Y,P_COLOR Person.actor->*
P_VALUE,P_GAMES Person.actor->*
SFRAME Person.bobFrame
RESOURCE
========
tflen() Resource::fileSize
topen() Resource::loadFile
tseek() *not needed*
SCREEN
======
Box() Display::drawBox
calc_screen_scroll() Display::horizontalScrollUpdate
changejoepal() Display::palSetJoe*
check_colors() Display::palCustomColors
check_pal_scroll Display::palCustomScroll
clearpanel() Display::prepareUpdate
drawbackdrop() Display::prepareUpdate
drawpanel() Display::prepareUpdate
drawscreen() Display::update
dynalum() Display::dynalumUpdate
fade_panel() Display::palGreyPanel
fadein() Display::palFadeIn
fadeout() Display::palFadeOut
flashspecial() Display::palCustomFlash
loadfont() Display::initFont
palscroll() Display::palScroll
putcharacter() Display::drawChar
setpal() Display::palSet
-
BDxres Display::_bdWidth
BDyres Display::_bdHeight
clothespal Display::_palJoeClothes
COMPANEL *not needed* (argument)
dresspal Display::_palJoeDress
font Display::_font
font_sizes Display::_charWidth
FULLSCREEN Display::_fullscreen
nopalscroll Display::_pal.scrollable
palette Display::_pal.room
panelflag *not needed* (redundant with fullscreen)
scrollx Display::_horizontalScroll
tpal Display::_pal.screen
SOUND
=====
alter_current_volume()
playsong() Sound::playSong()
sfxbusy() Sound::waitSfxFinished()
sfxplay() Sound::playSfx()
-
song[] Sound::_song[]
tunelist[] Sound::_tune[]
CURRSONG Music::_currentSong
SFXNAME Sound::_sfxName
VOLUME
STATE
=====
ALTER_STATE() State::alterState*
FIND_STATE() State::findState*
-
TALK
====
FIND_SACTION() Talk::findSpeechParameters
MOVE_SPEAK() *not needed* (included in Talk::getSpeakCommand)
SPEAK() Talk::speak
SPEAK_SUB() Talk::speakSegment
talk() Talk::talk
TALK_PROC() Talk::talk
-
A1,A12
actiondata Talk::_speechParameters
HEAD
JMAX
JOEKEYstr
LEVEL
LEVELMAX
OLDLEVEL
OLDS
OWALK
PERstr
PKEYstr
TALKHEAD
TALKQUIT Input::_talkQuit
TALKstr
TALK_SELECTED Logic::_talkSelected
TEXTS
=====
blanktexts() Display::clearTexts
drawtext() Display::drawTexts
Ink() Display::textCurrentColor
MIDDLE() Display::textCenterX / Display::textSetCentered
text() Display::setText
textlen() Display::textWidth
-
textcol Display::_curTextColor
texts Display::_texts
WALK
====
CALC_PATH() Walk::calcPath
CALC_WALK() Walk::incWalkData
CALC_X() Walk::calcC
CALC_Y() Walk::calcC
CALCSCALE() Area::calcScale
FIND_FREE_AREA Walk::findFreeArea
FIND_NEWP() Walk::findAreaPosition
FIND_OLDP() Walk::findAreaPosition
MOVE_JOE() Walk::moveJoe
MOVE_OTHER() Walk::movePerson
-
AREALIST Walk::_areaList
AREASTRIKE Walk::_areaStrike
movdata Walk::_moveData
WALK_DATA Walk::_walkData
WALKI Walk::_walkDataCount
ZONES
=====
ClearZones() Grid::clear
FIND_SCALE() Grid::findScale
FIND_VERB() Grid::findVerbUnderCursor
SETUP_PANEL_ZONES() Grid::setupPanel
SETUP_ZONES() Grid::setupNewRoom
SetZone() Grid::setZone
zone() Grid::findZoneForPos / Logic::findAreaForPos
-
AREA Grid::_area
AREAMAX Grid::_areaMax
OBJECT_BOX Grid::_objectBox
OBJMAX Grid::_objMax
zones Grid::_zones
(UNSORTED)
==========
in() Cutaway::inRange
find_cd_cut() findCdCut
find_cd_desc() *not needed* (included in Logic::joeSpeak)
-
Kstr
bank9
NEWDEF,
M,A,
FRAME,
AM,
WX,WY,
PX,PY,
LD,FD
DESC2,DESC
PERSON_OBJ
FS,FE,FACE,
TY,
DY,
I2,
N,V,
ds,bs,
bx,by,
dx,dy,
SFAC,FDIR,
OBJ,E,T,
CH,
OLDG,S2,S1,ITEM,TYPE,C,
NAME,TL,TI,TS,WC,IMAGE,
D,P,LI,R
CANTQUIT !Input::_canQuit
(NO NEED TO BE GLOBAL)
======================
Nstr,F1,F2,F3,F4,F5,F6,F7,F8,SF,BF,AS,MS // MOVE_OTHER (struct movdata *)
Nstr,S,F,BODY,BF,RF,AF,SANIMstr,FF // FIND_SACTION (struct action *)
CURRBOB // SETUP_FURNITURE, REDISP_OBJECT, DISP_OBJECTS
PSY,PSX,CSX,DX1,DX2,DY1,DY2,PCF,CCF,CSY // FIND_NEWP, FIND_OLDP
tx,ty,SFRAME,EFRAME,SPEED // FIND_GRAPHIC
AREAMAXv
CURRY
OMAX,OMAXA
TEMPA
BANK,BNUM
DIFF // LOOK local var
RET // P1_SET_CONDITIONS local var
BS,DS // CALC_SCALE
SX,SY,
NEWA // FIND_FREE_AREA local
IX,IY // Cutaway locals
COM // EXECUTE_ACTION local
COMMAX // EXECUTE_ACTION local
COND // EXECUTE_ACTION local
CURRCOM // EXECUTE_ACTION local
GSET // P1_SET_CONDITIONS local
A2 // EXECUTE_ACTION local
TEMPI // P1_SET_CONDITIONS local
MAPC // findFreeArea local var
NEWP,OLDP // locals in joeMove && personMove
OLDX,X,OLDY,Y // passed as arguments
X2,X1,XD,YD // incWalkData && findFreeArea locals
Gstr // not needed, grab state
Pstr // not needed, FIND_STATE result
OUTLINE // not needed, textSet() Graphics::parameter
FTOT // queen.c/SETUP_FURNITURE local var
OBJMAXv // == Logic::_objMax[Logic::_currentRoom]
TEMPstr
WORDstr
JOE2str,PERSON2str // locals in Talk::initialTalk
SUBJECT
tmpbamflag