scummvm/engines/parallaction/parallaction.cpp

606 lines
13 KiB
C++
Raw Normal View History

/* 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/input.h"
#include "parallaction/parallaction.h"
#include "parallaction/debug.h"
2007-05-01 16:21:16 +00:00
#include "parallaction/sound.h"
2007-05-13 14:38:05 +00:00
namespace Parallaction {
// FIXME: remove this
Parallaction *_vm = NULL;
// public stuff
char _saveData1[30] = { '\0' };
uint16 _language = 0;
uint32 _engineFlags = 0;
2007-03-11 00:04:50 +00:00
uint16 _score = 1;
char _password[8];
uint32 _commandFlags = 0;
// private stuff
2007-03-11 00:04:50 +00:00
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;
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");
2007-05-13 12:41:42 +00:00
Common::addSpecialDebugLevel(kDebugMenu, "menu", "Menu debug level");
Common::addSpecialDebugLevel(kDebugInventory, "inventory", "Inventory debug level");
syst->getEventManager()->registerRandomSource(_rnd, "parallaction");
}
Parallaction::~Parallaction() {
delete _debugger;
delete _globalTable;
delete _callableNames;
delete _localFlagNames;
freeLocation();
freeCharacter();
destroyInventory();
delete _gfx;
delete _soundMan;
delete _disk;
}
int Parallaction::init() {
_engineFlags = 0;
_objectsNames = NULL;
_globalTable = NULL;
_location._hasSound = false;
_baseTime = 0;
_numLocations = 0;
_location._startPosition.x = -1000;
_location._startPosition.y = -1000;
_location._startFrame = 0;
_location._comment = NULL;
_location._endComment = NULL;
2007-08-11 13:13:56 +00:00
_pathBuffer = 0;
_screenSize = _screenWidth * _screenHeight;
2007-08-11 13:13:56 +00:00
strcpy(_characterName1, "null");
memset(_locationNames, 0, NUM_LOCATIONS * 32);
initInventory(); // needs to be pushed into subclass
_input = new Input(this);
_gfx = new Gfx(this);
_debugger = new Debugger(this);
return 0;
}
void Parallaction::updateView() {
if ((_engineFlags & kEnginePauseJobs) && (_engineFlags & kEngineInventory) == 0) {
return;
}
_gfx->animatePalette();
2008-01-28 20:40:05 +00:00
_gfx->updateScreen();
g_system->delayMillis(30);
}
void Parallaction::freeCharacter() {
debugC(1, kDebugExec, "freeCharacter()");
2007-11-18 13:22:38 +00:00
delete _objectsNames;
_objectsNames = 0;
_char.free();
return;
}
void Parallaction::pauseJobs() {
debugC(9, kDebugExec, "pausing jobs execution");
2007-05-13 12:41:42 +00:00
_engineFlags |= kEnginePauseJobs;
return;
}
void Parallaction::resumeJobs() {
debugC(9, kDebugExec, "resuming jobs execution");
2007-05-13 12:41:42 +00:00
_engineFlags &= ~kEnginePauseJobs;
return;
}
AnimationPtr Parallaction::findAnimation(const char *name) {
for (AnimationList::iterator it = _location._animations.begin(); it != _location._animations.end(); it++)
if (!scumm_stricmp((*it)->_name, name)) return *it;
return nullAnimationPtr;
}
void Parallaction::freeAnimations() {
_location._animations.clear();
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 {
setLocationFlags(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();
_gfx->clearGfxObjects();
freeBackground();
_location._programs.clear();
freeZones();
freeAnimations();
2007-11-18 13:22:38 +00:00
free(_location._comment);
_location._comment = 0;
_location._commands.clear();
_location._aCommands.clear();
return;
}
void Parallaction::freeBackground() {
_gfx->freeBackground();
_pathBuffer = 0;
}
void Parallaction::setBackground(const char* name, const char* mask, const char* path) {
_gfx->setBackground(kBackgroundLocation, name, mask, path);
_pathBuffer = &_gfx->_backgroundInfo.path;
return;
}
void Parallaction::showLocationComment(const char *text, bool end) {
_gfx->setLocationBalloon(const_cast<char*>(text), end);
}
void Parallaction::processInput(InputData *data) {
switch (data->_event) {
case kEvEnterZone:
debugC(2, kDebugInput, "processInput: kEvEnterZone");
_gfx->setFloatingLabel(data->_label);
break;
case kEvExitZone:
debugC(2, kDebugInput, "processInput: kEvExitZone");
_gfx->setFloatingLabel(0);
break;
case kEvAction:
debugC(2, kDebugInput, "processInput: kEvAction");
_input->stopHovering();
pauseJobs();
runZone(data->_zone);
resumeJobs();
break;
case kEvOpenInventory:
_input->stopHovering();
_gfx->setFloatingLabel(0);
if (hitZone(kZoneYou, data->_mousePos.x, data->_mousePos.y) == 0) {
setArrowCursor();
}
pauseJobs();
openInventory();
break;
case kEvCloseInventory: // closes inventory and possibly select item
closeInventory();
setInventoryCursor(data->_inventoryIndex);
resumeJobs();
break;
case kEvHoverInventory:
highlightInventoryItem(data->_inventoryIndex); // enable
break;
case kEvWalk:
debugC(2, kDebugInput, "processInput: kEvWalk");
_input->stopHovering();
setArrowCursor();
_char.scheduleWalk(data->_mousePos.x, data->_mousePos.y);
break;
case kEvQuitGame:
_engineFlags |= kEngineQuit;
break;
case kEvSaveGame:
_input->stopHovering();
saveGame();
setArrowCursor();
break;
case kEvLoadGame:
_input->stopHovering();
loadGame();
setArrowCursor();
break;
}
return;
}
void Parallaction::runGame() {
InputData *data = _input->updateInput();
if (data->_event != kEvNone) {
processInput(data);
}
runPendingZones();
if (_engineFlags & kEngineChangeLocation) {
changeLocation(_location._name);
}
_gfx->beginFrame();
if (_input->_inputMode == Input::kInputModeGame) {
runScripts();
walk();
drawAnimations();
}
// change this to endFrame?
updateView();
}
// 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");
2007-11-18 13:22:38 +00:00
if (!_location._comment) {
return;
}
if (getLocationFlags() & kFlagsVisited) {
debugC(2, kDebugExec, "skipping location transition");
return; // visited
}
Palette pal(_gfx->_palette);
pal.makeGrayscale();
_gfx->setPalette(pal);
runScripts();
2007-11-18 21:32:22 +00:00
drawAnimations();
2008-01-28 20:40:05 +00:00
_gfx->updateScreen();
showLocationComment(_location._comment, false);
_input->waitUntilLeftClick();
_gfx->freeBalloons();
// fades maximum intensity palette towards approximation of main palette
for (uint16 _si = 0; _si<6; _si++) {
pal.fadeTo(_gfx->_palette, 4);
_gfx->setPalette(pal);
_gfx->updateScreen();
g_system->delayMillis(20);
}
_gfx->setPalette(_gfx->_palette);
debugC(2, kDebugExec, "doLocationEnterTransition completed");
return;
}
void Parallaction::setLocationFlags(uint32 flags) {
_localFlags[_currentLocationIndex] |= flags;
}
void Parallaction::clearLocationFlags(uint32 flags) {
_localFlags[_currentLocationIndex] &= ~flags;
}
void Parallaction::toggleLocationFlags(uint32 flags) {
_localFlags[_currentLocationIndex] ^= flags;
}
uint32 Parallaction::getLocationFlags() {
return _localFlags[_currentLocationIndex];
}
ZonePtr Parallaction::findZone(const char *name) {
for (ZoneList::iterator it = _location._zones.begin(); it != _location._zones.end(); it++) {
if (!scumm_stricmp((*it)->_name, name)) return *it;
}
return findAnimation(name);
}
void Parallaction::freeZones() {
debugC(2, kDebugExec, "freeZones: kEngineQuit = %i", _engineFlags & kEngineQuit);
ZoneList::iterator it = _location._zones.begin();
while ( it != _location._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.
ZonePtr z = *it;
if (((z->_top == -1) || (z->_left == -2)) && ((_engineFlags & kEngineQuit) == 0)) {
debugC(2, kDebugExec, "freeZones preserving zone '%s'", z->_name);
it++;
} else {
it = _location._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), _ani(new Animation), _builder(_ani) {
_talk = NULL;
_head = NULL;
_objs = NULL;
2007-10-30 19:41:55 +00:00
_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;
strncpy(_ani->_name, "yourself", ZONENAME_LENGTH);
}
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);
_engineFlags |= kEngineWalking;
}
void Character::free() {
2007-11-18 13:22:38 +00:00
delete _talk;
delete _head;
delete _objs;
_ani->gfxobj = 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;
2007-10-30 19:41:55 +00:00
_dummy = IS_DUMMY_CHARACTER(name);
2007-10-30 19:41:55 +00:00
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;
}
2007-10-30 19:41:55 +00:00
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