mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-27 12:16:59 +00:00
a556368c99
svn-id: r29657
1084 lines
22 KiB
C++
1084 lines
22 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* $URL$
|
|
* $Id$
|
|
*
|
|
*/
|
|
|
|
#include "common/config-manager.h"
|
|
#include "common/events.h"
|
|
#include "common/file.h"
|
|
#include "common/util.h"
|
|
|
|
#include "sound/mididrv.h"
|
|
#include "sound/mixer.h"
|
|
|
|
#include "parallaction/parallaction.h"
|
|
#include "parallaction/debug.h"
|
|
#include "parallaction/sound.h"
|
|
|
|
|
|
|
|
namespace Parallaction {
|
|
|
|
// FIXME: remove this
|
|
Parallaction *_vm = NULL;
|
|
|
|
// public stuff
|
|
|
|
uint16 _mouseButtons = 0;
|
|
|
|
|
|
char _saveData1[30] = { '\0' };
|
|
uint16 _language = 0;
|
|
char _slideText[2][40];
|
|
uint32 _engineFlags = 0;
|
|
|
|
uint16 _score = 1;
|
|
char _password[8];
|
|
|
|
Command * _forwardedCommands[20] = {
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL
|
|
};
|
|
|
|
char _forwardedAnimationNames[20][20];
|
|
uint16 _numForwards = 0;
|
|
|
|
uint32 _commandFlags = 0;
|
|
uint16 _introSarcData3 = 200;
|
|
uint16 _introSarcData2 = 1;
|
|
|
|
// private stuff
|
|
|
|
|
|
Parallaction::Parallaction(OSystem *syst, const PARALLACTIONGameDescription *gameDesc) :
|
|
Engine(syst), _gameDescription(gameDesc), _char(this) {
|
|
|
|
// FIXME: Fingolfin asks: why is there a FIXME here? Please either clarify what
|
|
// needs fixing, or remove it!
|
|
// FIXME
|
|
_vm = this;
|
|
|
|
_mouseHidden = false;
|
|
|
|
Common::File::addDefaultDirectory( _gameDataPath );
|
|
|
|
Common::addSpecialDebugLevel(kDebugDialogue, "dialogue", "Dialogues debug level");
|
|
Common::addSpecialDebugLevel(kDebugParser, "parser", "Parser debug level");
|
|
Common::addSpecialDebugLevel(kDebugDisk, "disk", "Disk debug level");
|
|
Common::addSpecialDebugLevel(kDebugWalk, "walk", "Walk debug level");
|
|
Common::addSpecialDebugLevel(kDebugGraphics, "gfx", "Gfx debug level");
|
|
Common::addSpecialDebugLevel(kDebugExec, "exec", "Execution debug level");
|
|
Common::addSpecialDebugLevel(kDebugInput, "input", "Input debug level");
|
|
Common::addSpecialDebugLevel(kDebugAudio, "audio", "Audio debug level");
|
|
Common::addSpecialDebugLevel(kDebugMenu, "menu", "Menu debug level");
|
|
Common::addSpecialDebugLevel(kDebugInventory, "inventory", "Inventory debug level");
|
|
|
|
syst->getEventManager()->registerRandomSource(_rnd, "parallaction");
|
|
}
|
|
|
|
|
|
Parallaction::~Parallaction() {
|
|
delete _debugger;
|
|
|
|
freeBackground();
|
|
delete _backgroundInfo;
|
|
|
|
delete _globalTable;
|
|
|
|
delete _callableNames;
|
|
delete _localFlagNames;
|
|
delete _zoneTypeNames;
|
|
delete _zoneFlagNames;
|
|
|
|
_animations.remove(&_char._ani);
|
|
|
|
freeLocation();
|
|
|
|
freeCharacter();
|
|
destroyInventory();
|
|
|
|
delete _gfx;
|
|
delete _soundMan;
|
|
delete _disk;
|
|
}
|
|
|
|
|
|
int Parallaction::init() {
|
|
|
|
_engineFlags = 0;
|
|
_objectsNames = NULL;
|
|
_globalTable = NULL;
|
|
_hasLocationSound = false;
|
|
_transCurrentHoverItem = 0;
|
|
_actionAfterWalk = false; // actived when the character needs to move before taking an action
|
|
_activeItem._index = 0;
|
|
_activeItem._id = 0;
|
|
_procCurrentHoverItem = -1;
|
|
_baseTime = 0;
|
|
_numLocations = 0;
|
|
_location._startPosition.x = -1000;
|
|
_location._startPosition.y = -1000;
|
|
_location._startFrame = 0;
|
|
_location._comment = NULL;
|
|
_location._endComment = NULL;
|
|
|
|
_backgroundInfo = 0;
|
|
_pathBuffer = 0;
|
|
_activeZone = 0;
|
|
|
|
_screenSize = _screenWidth * _screenHeight;
|
|
|
|
_backgroundInfo = new BackgroundInfo;
|
|
|
|
strcpy(_characterName1, "null");
|
|
|
|
memset(_locationNames, 0, NUM_LOCATIONS * 32);
|
|
|
|
initInventory(); // needs to be pushed into subclass
|
|
|
|
_hoverZone = NULL;
|
|
|
|
_animations.push_front(&_char._ani);
|
|
_gfx = new Gfx(this);
|
|
|
|
_debugger = new Debugger(this);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// FIXME: the engine has 3 event loops. The following routine hosts the main one,
|
|
// and it's called from 8 different places in the code. There exist 2 more specialised
|
|
// loops which could possibly be merged into this one with some effort in changing
|
|
// caller code, i.e. adding condition checks.
|
|
//
|
|
uint16 Parallaction::readInput() {
|
|
|
|
Common::Event e;
|
|
uint16 KeyDown = 0;
|
|
|
|
_mouseButtons = kMouseNone;
|
|
|
|
Common::EventManager *eventMan = _system->getEventManager();
|
|
while (eventMan->pollEvent(e)) {
|
|
|
|
switch (e.type) {
|
|
case Common::EVENT_KEYDOWN:
|
|
if (e.kbd.flags == Common::KBD_CTRL && e.kbd.keycode == 'd')
|
|
_debugger->attach();
|
|
if (getFeatures() & GF_DEMO) break;
|
|
if (e.kbd.keycode == Common::KEYCODE_l) KeyDown = kEvLoadGame;
|
|
if (e.kbd.keycode == Common::KEYCODE_s) KeyDown = kEvSaveGame;
|
|
break;
|
|
|
|
case Common::EVENT_LBUTTONDOWN:
|
|
_mouseButtons = kMouseLeftDown;
|
|
_mousePos = e.mouse;
|
|
break;
|
|
|
|
case Common::EVENT_LBUTTONUP:
|
|
_mouseButtons = kMouseLeftUp;
|
|
_mousePos = e.mouse;
|
|
break;
|
|
|
|
case Common::EVENT_RBUTTONDOWN:
|
|
_mouseButtons = kMouseRightDown;
|
|
_mousePos = e.mouse;
|
|
break;
|
|
|
|
case Common::EVENT_RBUTTONUP:
|
|
_mouseButtons = kMouseRightUp;
|
|
_mousePos = e.mouse;
|
|
break;
|
|
|
|
case Common::EVENT_MOUSEMOVE:
|
|
_mousePos = e.mouse;
|
|
break;
|
|
|
|
case Common::EVENT_QUIT:
|
|
// TODO: don't quit() here, just have caller routines to check
|
|
// on kEngineQuit and exit gracefully to allow the engine to shut down
|
|
_engineFlags |= kEngineQuit;
|
|
g_system->quit();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (_debugger->isAttached())
|
|
_debugger->onFrame();
|
|
|
|
return KeyDown;
|
|
|
|
}
|
|
|
|
// FIXME: see comment for readInput()
|
|
void waitUntilLeftClick() {
|
|
|
|
do {
|
|
_vm->readInput();
|
|
_vm->_gfx->updateScreen();
|
|
g_system->delayMillis(30);
|
|
} while (_mouseButtons != kMouseLeftUp);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
void Parallaction::runGame() {
|
|
|
|
_inputMode = kInputModeGame;
|
|
|
|
while ((_engineFlags & kEngineQuit) == 0) {
|
|
updateInput();
|
|
|
|
if (_activeZone) {
|
|
Zone *z = _activeZone; // speak Zone or sound
|
|
_activeZone = NULL;
|
|
runZone(z);
|
|
}
|
|
|
|
if (_engineFlags & kEngineChangeLocation) {
|
|
changeLocation(_location._name);
|
|
}
|
|
|
|
if (_inputMode == kInputModeGame) {
|
|
eraseAnimations();
|
|
runScripts();
|
|
walk();
|
|
runJobs();
|
|
drawAnimations();
|
|
}
|
|
|
|
updateView();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void Parallaction::updateView() {
|
|
|
|
if ((_engineFlags & kEnginePauseJobs) && (_engineFlags & kEngineInventory) == 0) {
|
|
return;
|
|
}
|
|
|
|
_gfx->animatePalette();
|
|
_gfx->swapBuffers();
|
|
g_system->delayMillis(30);
|
|
}
|
|
|
|
|
|
void Parallaction::processInput(InputData *data) {
|
|
|
|
switch (data->_event) {
|
|
case kEvEnterZone:
|
|
debugC(2, kDebugInput, "processInput: kEvEnterZone");
|
|
_gfx->setLabel(data->_label);
|
|
break;
|
|
|
|
case kEvExitZone:
|
|
debugC(2, kDebugInput, "processInput: kEvExitZone");
|
|
_gfx->setLabel(0);
|
|
break;
|
|
|
|
case kEvAction:
|
|
debugC(2, kDebugInput, "processInput: kEvAction");
|
|
_procCurrentHoverItem = -1;
|
|
_hoverZone = NULL;
|
|
pauseJobs();
|
|
runZone(data->_zone);
|
|
resumeJobs();
|
|
break;
|
|
|
|
case kEvOpenInventory:
|
|
_procCurrentHoverItem = -1;
|
|
_hoverZone = NULL;
|
|
_gfx->setLabel(0);
|
|
if (hitZone(kZoneYou, _mousePos.x, _mousePos.y) == 0) {
|
|
setArrowCursor();
|
|
}
|
|
pauseJobs();
|
|
openInventory();
|
|
break;
|
|
|
|
case kEvCloseInventory: // closes inventory and possibly select item
|
|
closeInventory();
|
|
setInventoryCursor(data->_inventoryIndex);
|
|
resumeJobs();
|
|
break;
|
|
|
|
case kEvHoverInventory:
|
|
highlightInventoryItem(_procCurrentHoverItem, 12); // disable
|
|
highlightInventoryItem(data->_inventoryIndex, 19); // enable
|
|
_procCurrentHoverItem = data->_inventoryIndex;
|
|
break;
|
|
|
|
case kEvWalk:
|
|
debugC(2, kDebugInput, "processInput: kEvWalk");
|
|
_hoverZone = NULL;
|
|
setArrowCursor();
|
|
_char.scheduleWalk(data->_mousePos.x, data->_mousePos.y);
|
|
break;
|
|
|
|
case kEvQuitGame:
|
|
_engineFlags |= kEngineQuit;
|
|
break;
|
|
|
|
case kEvSaveGame:
|
|
_hoverZone = NULL;
|
|
saveGame();
|
|
setArrowCursor();
|
|
break;
|
|
|
|
case kEvLoadGame:
|
|
_hoverZone = NULL;
|
|
loadGame();
|
|
setArrowCursor();
|
|
break;
|
|
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Parallaction::updateGameInput() {
|
|
|
|
int16 keyDown = readInput();
|
|
|
|
debugC(3, kDebugInput, "translateInput: input flags (%i, %i, %i, %i)",
|
|
!_mouseHidden,
|
|
(_engineFlags & kEngineBlockInput) == 0,
|
|
(_engineFlags & kEngineWalking) == 0,
|
|
(_engineFlags & kEngineChangeLocation) == 0
|
|
);
|
|
|
|
if ((_mouseHidden) ||
|
|
(_engineFlags & kEngineBlockInput) ||
|
|
(_engineFlags & kEngineWalking) ||
|
|
(_engineFlags & kEngineChangeLocation)) {
|
|
|
|
return;
|
|
}
|
|
|
|
if (keyDown == kEvQuitGame) {
|
|
_input._event = kEvQuitGame;
|
|
} else
|
|
if (keyDown == kEvSaveGame) {
|
|
_input._event = kEvSaveGame;
|
|
} else
|
|
if (keyDown == kEvLoadGame) {
|
|
_input._event = kEvLoadGame;
|
|
} else {
|
|
_input._mousePos = _mousePos;
|
|
_input._event = kEvNone;
|
|
if (!translateGameInput()) {
|
|
translateInventoryInput();
|
|
}
|
|
}
|
|
|
|
if (_input._event != kEvNone)
|
|
processInput(&_input);
|
|
|
|
}
|
|
|
|
void Parallaction::updateCommentInput() {
|
|
waitUntilLeftClick();
|
|
|
|
_gfx->hideDialogueStuff();
|
|
_gfx->setHalfbriteMode(false);
|
|
|
|
_inputMode = kInputModeGame;
|
|
}
|
|
|
|
void Parallaction::updateInput() {
|
|
|
|
switch (_inputMode) {
|
|
case kInputModeComment:
|
|
updateCommentInput();
|
|
break;
|
|
|
|
case kInputModeGame:
|
|
updateGameInput();
|
|
break;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
bool Parallaction::translateGameInput() {
|
|
|
|
if ((_engineFlags & kEnginePauseJobs) || (_engineFlags & kEngineInventory)) {
|
|
return false;
|
|
}
|
|
|
|
if (_actionAfterWalk) {
|
|
// if walking is over, then take programmed action
|
|
_input._event = kEvAction;
|
|
_actionAfterWalk = false;
|
|
return true;
|
|
}
|
|
|
|
if (_mouseButtons == kMouseRightDown) {
|
|
// right button down shows inventory
|
|
|
|
if (hitZone(kZoneYou, _mousePos.x, _mousePos.y) && (_activeItem._id != 0)) {
|
|
_activeItem._index = (_activeItem._id >> 16) & 0xFFFF;
|
|
_engineFlags |= kEngineDragging;
|
|
}
|
|
|
|
_input._event = kEvOpenInventory;
|
|
_transCurrentHoverItem = -1;
|
|
return true;
|
|
}
|
|
|
|
// test if mouse is hovering on an interactive zone for the currently selected inventory item
|
|
Zone *z = hitZone(_activeItem._id, _mousePos.x, _mousePos.y);
|
|
|
|
if (((_mouseButtons == kMouseLeftUp) && (_activeItem._id == 0) && ((_engineFlags & kEngineWalking) == 0)) && ((z == NULL) || ((z->_type & 0xFFFF) != kZoneCommand))) {
|
|
_input._event = kEvWalk;
|
|
return true;
|
|
}
|
|
|
|
if ((z != _hoverZone) && (_hoverZone != NULL)) {
|
|
_hoverZone = NULL;
|
|
_input._event = kEvExitZone;
|
|
return true;
|
|
}
|
|
|
|
if (z == NULL) {
|
|
_input._event = kEvNone;
|
|
return true;
|
|
}
|
|
|
|
if ((_hoverZone == NULL) && ((z->_flags & kFlagsNoName) == 0)) {
|
|
_hoverZone = z;
|
|
_input._event = kEvEnterZone;
|
|
_input._label = &z->_label;
|
|
return true;
|
|
}
|
|
|
|
if ((_mouseButtons == kMouseLeftUp) && ((_activeItem._id != 0) || ((z->_type & 0xFFFF) == kZoneCommand))) {
|
|
|
|
_input._zone = z;
|
|
if (z->_flags & kFlagsNoWalk) {
|
|
// character doesn't need to walk to take specified action
|
|
_input._event = kEvAction;
|
|
|
|
} else {
|
|
// action delayed: if Zone defined a moveto position the character is programmed to move there,
|
|
// else it will move to the mouse position
|
|
_input._event = kEvWalk;
|
|
_actionAfterWalk = true;
|
|
if (z->_moveTo.y != 0) {
|
|
_input._mousePos = z->_moveTo;
|
|
}
|
|
}
|
|
|
|
beep();
|
|
setArrowCursor();
|
|
return true;
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
bool Parallaction::translateInventoryInput() {
|
|
|
|
if ((_engineFlags & kEngineInventory) == 0) {
|
|
return false;
|
|
}
|
|
|
|
// in inventory
|
|
int16 _si = getHoverInventoryItem(_mousePos.x, _mousePos.y);
|
|
|
|
if (_mouseButtons == kMouseRightUp) {
|
|
// right up hides inventory
|
|
|
|
_input._event = kEvCloseInventory;
|
|
_input._inventoryIndex = getHoverInventoryItem(_mousePos.x, _mousePos.y);
|
|
highlightInventoryItem(_transCurrentHoverItem, 12); // disable
|
|
|
|
if ((_engineFlags & kEngineDragging) == 0) {
|
|
return true;
|
|
}
|
|
|
|
_engineFlags &= ~kEngineDragging;
|
|
Zone *z = hitZone(kZoneMerge, _activeItem._index, getInventoryItemIndex(_input._inventoryIndex));
|
|
|
|
if (z != NULL) {
|
|
dropItem(z->u.merge->_obj1);
|
|
dropItem(z->u.merge->_obj2);
|
|
addInventoryItem(z->u.merge->_obj3);
|
|
runCommands(z->_commands);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
if (_si == _transCurrentHoverItem) {
|
|
_input._event = kEvNone;
|
|
return true;
|
|
}
|
|
|
|
_transCurrentHoverItem = _si;
|
|
_input._event = kEvHoverInventory;
|
|
_input._inventoryIndex = _si;
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
uint32 Parallaction::getElapsedTime() {
|
|
return g_system->getMillis() - _baseTime;
|
|
}
|
|
|
|
void Parallaction::resetTimer() {
|
|
_baseTime = g_system->getMillis();
|
|
return;
|
|
}
|
|
|
|
|
|
void Parallaction::waitTime(uint32 t) {
|
|
|
|
uint32 v4 = 0;
|
|
|
|
while (v4 < t * (1000 / 18.2)) {
|
|
v4 = getElapsedTime();
|
|
}
|
|
|
|
resetTimer();
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
void Parallaction::showCursor(bool visible) {
|
|
_mouseHidden = !visible;
|
|
g_system->showMouse(visible);
|
|
}
|
|
|
|
|
|
|
|
void Parallaction::freeCharacter() {
|
|
debugC(1, kDebugExec, "freeCharacter()");
|
|
|
|
delete _objectsNames;
|
|
_objectsNames = 0;
|
|
|
|
_char.free();
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
helper function to provide *descending* ordering of the job list
|
|
(higher priorities values comes first in the list)
|
|
*/
|
|
int compareJobPriority(const JobPointer &j1, const JobPointer &j2) {
|
|
return (j1->_job->_tag >= j2->_job->_tag ? -1 : 1);
|
|
}
|
|
|
|
Job *Parallaction::addJob(uint functionId, void *parm, uint16 tag) {
|
|
debugC(9, kDebugExec, "addJob(%i)", tag);
|
|
|
|
Job *v8 = new Job;
|
|
|
|
v8->_parm = parm;
|
|
v8->_tag = tag;
|
|
v8->_finished = 0;
|
|
v8->_count = 0;
|
|
|
|
JobOpcode *op = createJobOpcode(functionId, v8);
|
|
|
|
_jobs.insertSorted(op, compareJobPriority);
|
|
|
|
return v8;
|
|
}
|
|
|
|
void Parallaction::removeJob(Job *j) {
|
|
debugC(9, kDebugExec, "addJob(%i)", j->_tag);
|
|
|
|
j->_finished = 1;
|
|
return;
|
|
}
|
|
|
|
void Parallaction::pauseJobs() {
|
|
debugC(9, kDebugExec, "pausing jobs execution");
|
|
|
|
_engineFlags |= kEnginePauseJobs;
|
|
return;
|
|
}
|
|
|
|
void Parallaction::resumeJobs() {
|
|
debugC(9, kDebugExec, "resuming jobs execution");
|
|
|
|
_engineFlags &= ~kEnginePauseJobs;
|
|
return;
|
|
}
|
|
|
|
void Parallaction::runJobs() {
|
|
|
|
if (_engineFlags & kEnginePauseJobs) return;
|
|
|
|
JobList::iterator it = _jobs.begin();
|
|
while (it != _jobs.end()) {
|
|
Job *job = (*it)->_job;
|
|
if (job->_finished == 1)
|
|
it = _jobs.erase(it);
|
|
else
|
|
it++;
|
|
}
|
|
|
|
it = _jobs.begin();
|
|
while (it != _jobs.end()) {
|
|
Job *job = (*it)->_job;
|
|
debugC(9, kDebugExec, "runJobs: %i", job->_tag);
|
|
(*(*it))();
|
|
it++;
|
|
}
|
|
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|
|
void Parallaction::pushParserTables(OpcodeSet *opcodes, Table *statements) {
|
|
_opcodes.push(_currentOpcodes);
|
|
_statements.push(_currentStatements);
|
|
|
|
_currentOpcodes = opcodes;
|
|
_currentStatements = statements;
|
|
}
|
|
|
|
void Parallaction::popParserTables() {
|
|
assert(_opcodes.size() > 0);
|
|
|
|
_currentOpcodes = _opcodes.pop();
|
|
_currentStatements = _statements.pop();
|
|
}
|
|
|
|
void Parallaction::parseStatement() {
|
|
assert(_currentOpcodes != 0);
|
|
|
|
_lookup = _currentStatements->lookup(_tokens[0]);
|
|
|
|
debugC(9, kDebugParser, "parseStatement: %s (lookup = %i)", _tokens[0], _lookup);
|
|
|
|
(*(*_currentOpcodes)[_lookup])();
|
|
}
|
|
|
|
|
|
|
|
|
|
Animation *Parallaction::findAnimation(const char *name) {
|
|
|
|
for (AnimationList::iterator it = _animations.begin(); it != _animations.end(); it++)
|
|
if (!scumm_stricmp((*it)->_label._text, name)) return *it;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void Parallaction::freeAnimations() {
|
|
_animations.clear();
|
|
return;
|
|
}
|
|
|
|
int compareAnimationZ(const AnimationPointer &a1, const AnimationPointer &a2) {
|
|
if (a1->_z == a2->_z) return 0;
|
|
return (a1->_z < a2->_z ? -1 : 1);
|
|
}
|
|
|
|
void Parallaction::sortAnimations() {
|
|
_char._ani._z = _char._ani.height() + _char._ani._top;
|
|
_animations.sort(compareAnimationZ);
|
|
return;
|
|
}
|
|
|
|
|
|
void Parallaction::allocateLocationSlot(const char *name) {
|
|
// WORKAROUND: the original code erroneously incremented
|
|
// _currentLocationIndex, thus producing inconsistent
|
|
// savegames. This workaround modified the following loop
|
|
// and if-statement, so the code exactly matches the one
|
|
// in Big Red Adventure.
|
|
_currentLocationIndex = -1;
|
|
uint16 _di = 0;
|
|
while (_locationNames[_di][0] != '\0') {
|
|
if (!scumm_stricmp(_locationNames[_di], name)) {
|
|
_currentLocationIndex = _di;
|
|
}
|
|
_di++;
|
|
}
|
|
|
|
if (_di == 120)
|
|
error("No more location slots available. Please report this immediately to ScummVM team.");
|
|
|
|
if (_currentLocationIndex == -1) {
|
|
strcpy(_locationNames[_numLocations], name);
|
|
_currentLocationIndex = _numLocations;
|
|
|
|
_numLocations++;
|
|
_locationNames[_numLocations][0] = '\0';
|
|
_localFlags[_numLocations] = 0;
|
|
} else {
|
|
_localFlags[_currentLocationIndex] |= kFlagsVisited; // 'visited'
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void Parallaction::freeLocation() {
|
|
debugC(2, kDebugExec, "freeLocation");
|
|
|
|
_soundMan->stopSfx(0);
|
|
_soundMan->stopSfx(1);
|
|
_soundMan->stopSfx(2);
|
|
_soundMan->stopSfx(3);
|
|
|
|
_localFlagNames->clear();
|
|
|
|
_location._walkNodes.clear();
|
|
|
|
freeZones();
|
|
freeAnimations();
|
|
|
|
free(_location._comment);
|
|
_location._comment = 0;
|
|
|
|
_location._commands.clear();
|
|
_location._aCommands.clear();
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|
|
void Parallaction::freeBackground() {
|
|
|
|
if (!_backgroundInfo)
|
|
return;
|
|
|
|
_backgroundInfo->bg.free();
|
|
_backgroundInfo->mask.free();
|
|
_backgroundInfo->path.free();
|
|
|
|
_pathBuffer = 0;
|
|
|
|
}
|
|
|
|
void Parallaction::setBackground(const char* name, const char* mask, const char* path) {
|
|
|
|
_disk->loadScenery(*_backgroundInfo, name, mask, path);
|
|
|
|
_gfx->setPalette(_backgroundInfo->palette);
|
|
_gfx->_palette.clone(_backgroundInfo->palette);
|
|
_gfx->setBackground(&_backgroundInfo->bg);
|
|
|
|
if (_backgroundInfo->mask.data)
|
|
_gfx->setMask(&_backgroundInfo->mask);
|
|
|
|
if (_backgroundInfo->path.data)
|
|
_pathBuffer = &_backgroundInfo->path;
|
|
|
|
return;
|
|
}
|
|
|
|
void Parallaction::showLocationComment(const char *text, bool end) {
|
|
|
|
_gfx->setFont(_dialogueFont);
|
|
|
|
int16 w, h;
|
|
_gfx->getStringExtent(const_cast<char*>(text), 130, &w, &h);
|
|
|
|
Common::Rect r(w + (end ? 5 : 10), h + 5);
|
|
r.moveTo(5, 5);
|
|
|
|
_gfx->floodFill(Gfx::kBitFront, r, 0);
|
|
r.grow(-2);
|
|
_gfx->floodFill(Gfx::kBitFront, r, 1);
|
|
_gfx->displayWrappedString(const_cast<char*>(text), 3, 5, 0, 130);
|
|
|
|
_gfx->updateScreen();
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// displays transition before a new location
|
|
//
|
|
// clears screen (in white??)
|
|
// shows location comment (if any)
|
|
// waits for mouse click
|
|
// fades towards game palette
|
|
//
|
|
void Parallaction::doLocationEnterTransition() {
|
|
debugC(2, kDebugExec, "doLocationEnterTransition");
|
|
|
|
if (!_location._comment) {
|
|
return;
|
|
}
|
|
|
|
if (_localFlags[_currentLocationIndex] & kFlagsVisited) {
|
|
debugC(2, kDebugExec, "skipping location transition");
|
|
return; // visited
|
|
}
|
|
|
|
Palette pal(_gfx->_palette);
|
|
pal.makeGrayscale();
|
|
_gfx->setPalette(pal);
|
|
|
|
runScripts();
|
|
drawAnimations();
|
|
|
|
_gfx->swapBuffers();
|
|
_gfx->copyScreen(Gfx::kBitFront, Gfx::kBitBack);
|
|
|
|
showLocationComment(_location._comment, false);
|
|
waitUntilLeftClick();
|
|
|
|
_gfx->copyScreen(Gfx::kBitBack, Gfx::kBitFront );
|
|
|
|
// fades maximum intensity palette towards approximation of main palette
|
|
for (uint16 _si = 0; _si<6; _si++) {
|
|
pal.fadeTo(_gfx->_palette, 4);
|
|
_gfx->setPalette(pal);
|
|
waitTime( 1 );
|
|
_gfx->updateScreen();
|
|
}
|
|
|
|
_gfx->setPalette(_gfx->_palette);
|
|
|
|
debugC(2, kDebugExec, "doLocationEnterTransition completed");
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
Zone *Parallaction::findZone(const char *name) {
|
|
|
|
for (ZoneList::iterator it = _zones.begin(); it != _zones.end(); it++) {
|
|
if (!scumm_stricmp((*it)->_label._text, name)) return *it;
|
|
}
|
|
|
|
return findAnimation(name);
|
|
}
|
|
|
|
|
|
void Parallaction::freeZones() {
|
|
debugC(2, kDebugExec, "freeZones: kEngineQuit = %i", _engineFlags & kEngineQuit);
|
|
|
|
ZoneList::iterator it = _zones.begin();
|
|
|
|
while ( it != _zones.end() ) {
|
|
|
|
// NOTE : this condition has been relaxed compared to the original, to allow the engine
|
|
// to retain special - needed - zones that were lost across location switches.
|
|
Zone* z = *it;
|
|
if (((z->_top == -1) || (z->_left == -2)) && ((_engineFlags & kEngineQuit) == 0)) {
|
|
debugC(2, kDebugExec, "freeZones preserving zone '%s'", z->_label._text);
|
|
it++;
|
|
} else {
|
|
it = _zones.erase(it);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
const char Character::_prefixMini[] = "mini";
|
|
const char Character::_suffixTras[] = "tras";
|
|
const char Character::_empty[] = "\0";
|
|
|
|
|
|
Character::Character(Parallaction *vm) : _vm(vm), _builder(&_ani) {
|
|
_talk = NULL;
|
|
_head = NULL;
|
|
_objs = NULL;
|
|
|
|
_dummy = false;
|
|
|
|
_ani._left = 150;
|
|
_ani._top = 100;
|
|
_ani._z = 10;
|
|
_ani._oldPos.x = -1000;
|
|
_ani._oldPos.y = -1000;
|
|
_ani._frame = 0;
|
|
_ani._flags = kFlagsActive | kFlagsNoName;
|
|
_ani._type = kZoneYou;
|
|
_ani._label._cnv.pixels = NULL;
|
|
_ani._label._text = strdup("yourself");
|
|
}
|
|
|
|
void Character::getFoot(Common::Point &foot) {
|
|
foot.x = _ani._left + _ani.width() / 2;
|
|
foot.y = _ani._top + _ani.height();
|
|
}
|
|
|
|
void Character::setFoot(const Common::Point &foot) {
|
|
_ani._left = foot.x - _ani.width() / 2;
|
|
_ani._top = foot.y - _ani.height();
|
|
}
|
|
|
|
void Character::scheduleWalk(int16 x, int16 y) {
|
|
if ((_ani._flags & kFlagsRemove) || (_ani._flags & kFlagsActive) == 0) {
|
|
return;
|
|
}
|
|
|
|
_walkPath = _builder.buildPath(x, y);
|
|
|
|
// WalkNodeList *list = _builder.buildPath(x, y);
|
|
// _vm->addJob(kJobWalk, list, kPriority19 );
|
|
|
|
_engineFlags |= kEngineWalking;
|
|
}
|
|
|
|
void Character::free() {
|
|
|
|
delete _ani._cnv;
|
|
delete _talk;
|
|
delete _head;
|
|
delete _objs;
|
|
|
|
_ani._cnv = NULL;
|
|
_talk = NULL;
|
|
_head = NULL;
|
|
_objs = NULL;
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
// Various ways of detecting character modes used to exist
|
|
// inside the engine, so they have been unified in the two
|
|
// following macros.
|
|
// Mini characters are those used in far away shots, like
|
|
// the highway scenery, while Dummy characters are a mere
|
|
// workaround to keep the engine happy when showing slides.
|
|
// As a sidenote, standard sized characters' names start
|
|
// with a lowercase 'd'.
|
|
#define IS_MINI_CHARACTER(s) (((s)[0] == 'm'))
|
|
#define IS_DUMMY_CHARACTER(s) (((s)[0] == 'D'))
|
|
|
|
void Character::setName(const char *name) {
|
|
const char *begin = name;
|
|
const char *end = begin + strlen(name);
|
|
|
|
_prefix = _empty;
|
|
|
|
_dummy = IS_DUMMY_CHARACTER(name);
|
|
|
|
if (!_dummy) {
|
|
if (_engineFlags & kEngineTransformedDonna) {
|
|
_suffix = _suffixTras;
|
|
} else {
|
|
const char *s = strstr(name, "tras");
|
|
if (s) {
|
|
_engineFlags |= kEngineTransformedDonna;
|
|
_suffix = _suffixTras;
|
|
end = s;
|
|
} else {
|
|
_suffix = _empty;
|
|
}
|
|
}
|
|
if (IS_MINI_CHARACTER(name)) {
|
|
_prefix = _prefixMini;
|
|
begin = name+4;
|
|
}
|
|
}
|
|
|
|
memset(_baseName, 0, 30);
|
|
strncpy(_baseName, begin, end - begin);
|
|
sprintf(_name, "%s%s", _prefix, _baseName);
|
|
sprintf(_fullName, "%s%s%s", _prefix, _baseName, _suffix);
|
|
}
|
|
|
|
const char *Character::getName() const {
|
|
return _name;
|
|
}
|
|
|
|
const char *Character::getBaseName() const {
|
|
return _baseName;
|
|
}
|
|
|
|
const char *Character::getFullName() const {
|
|
return _fullName;
|
|
}
|
|
|
|
bool Character::dummy() const {
|
|
return _dummy;
|
|
}
|
|
|
|
void Parallaction::beep() {
|
|
_soundMan->playSfx("beep", 3, false);
|
|
}
|
|
|
|
void Parallaction::scheduleLocationSwitch(const char *location) {
|
|
strcpy(_location._name, location);
|
|
_engineFlags |= kEngineChangeLocation;
|
|
}
|
|
|
|
|
|
} // namespace Parallaction
|