aboutsummaryrefslogtreecommitdiff
path: root/engines/parallaction/parallaction.cpp
diff options
context:
space:
mode:
authorNicola Mettifogo2007-08-24 20:14:51 +0000
committerNicola Mettifogo2007-08-24 20:14:51 +0000
commit37cdd1c69ad4a1750c3041d5cceeed676da9061f (patch)
treedfd19d7e1b667e16d319c5e599907c61ba16e18e /engines/parallaction/parallaction.cpp
parent2bfc4466df238fea2162466cca06de88c773e6c5 (diff)
downloadscummvm-rg350-37cdd1c69ad4a1750c3041d5cceeed676da9061f.tar.gz
scummvm-rg350-37cdd1c69ad4a1750c3041d5cceeed676da9061f.tar.bz2
scummvm-rg350-37cdd1c69ad4a1750c3041d5cceeed676da9061f.zip
First step in restructuring engine code:
- code has been consolidated in fewer files - new table-driven parsers/execution - some functions has been pushed down the engine hierarchy - Parallaction_br now inherits from Parallaction_ns svn-id: r28711
Diffstat (limited to 'engines/parallaction/parallaction.cpp')
-rw-r--r--engines/parallaction/parallaction.cpp584
1 files changed, 422 insertions, 162 deletions
diff --git a/engines/parallaction/parallaction.cpp b/engines/parallaction/parallaction.cpp
index af0a34e409..dedcddb8c7 100644
--- a/engines/parallaction/parallaction.cpp
+++ b/engines/parallaction/parallaction.cpp
@@ -53,7 +53,6 @@ char _saveData1[30] = { '\0' };
uint16 _language = 0;
char _slideText[2][40];
uint32 _engineFlags = 0;
-Zone *_activeZone = NULL;
uint16 _score = 1;
@@ -128,11 +127,9 @@ Parallaction::~Parallaction() {
delete _globalTable;
delete _callableNames;
- delete _commandsNames;
- delete _instructionNames;
+
delete _zoneTypeNames;
delete _zoneFlagNames;
- delete _locationStmt;
_animations.remove(&_char._ani);
@@ -168,6 +165,7 @@ int Parallaction::init() {
_backgroundInfo = 0;
_pathBuffer = 0;
+ _activeZone = 0;
_screenSize = _screenWidth * _screenHeight;
@@ -178,8 +176,6 @@ int Parallaction::init() {
memset(_locationNames, 0, 120*32);
- initOpcodes();
-
initInventory(); // needs to be pushed into subclass
_animations.push_front(&_char._ani);
@@ -840,7 +836,7 @@ void Table::addData(const char* s) {
}
-int Table::lookup(const char* s) {
+uint16 Table::lookup(const char* s) {
for (uint16 i = 0; i < _used; i++) {
if (!scumm_stricmp(_data[i], s)) return i + 1;
@@ -849,14 +845,12 @@ int Table::lookup(const char* s) {
return notFound;
}
-void Parallaction::pushParserTables(const Opcode* opcodes, Table *statements) {
-
+void Parallaction::pushParserTables(OpcodeSet *opcodes, Table *statements) {
_opcodes.push(_currentOpcodes);
_statements.push(_currentStatements);
_currentOpcodes = opcodes;
_currentStatements = statements;
-
}
void Parallaction::popParserTables() {
@@ -870,161 +864,427 @@ void Parallaction::parseStatement() {
assert(_currentOpcodes != 0);
_lookup = _currentStatements->lookup(_tokens[0]);
- (this->*(_currentOpcodes[_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(7, kDebugLocation, "freeLocation");
+
+ _soundMan->stopSfx(0);
+ _soundMan->stopSfx(1);
+ _soundMan->stopSfx(2);
+ _soundMan->stopSfx(3);
+
+ if (_localFlagNames)
+ delete _localFlagNames;
+
+ // HACK: prevents leakage. A routine like this
+ // should allocate memory at all, though.
+ if ((_engineFlags & kEngineQuit) == 0) {
+ _localFlagNames = new Table(120);
+ _localFlagNames->addData("visited");
+ }
+
+ _location._walkNodes.clear();
+
+ freeZones();
+ freeAnimations();
+
+ if (_location._comment) {
+ free(_location._comment);
+ }
+ _location._comment = NULL;
+
+ _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;
+}
+
+void Parallaction::switchBackground(const char* background, const char* mask) {
+// printf("switchBackground(%s)", name);
+
+ Palette pal;
+
+ uint16 v2 = 0;
+ if (!scumm_stricmp(background, "final")) {
+ _gfx->clearScreen(Gfx::kBitBack);
+ for (uint16 _si = 0; _si <= 32; _si++) {
+ pal.setEntry(_si, v2, v2, v2);
+ v2 += 4;
+ }
+
+ g_system->delayMillis(20);
+ _gfx->setPalette(pal);
+ _gfx->updateScreen();
+ }
+
+ setBackground(background, mask, mask);
+
+ return;
+}
+
+extern Zone *_hoverZone;
+extern Job *_jDrawLabel;
+extern Job *_jEraseLabel;
+
+void Parallaction::showSlide(const char *name) {
+
+ BackgroundInfo info;
+
+ _disk->loadSlide(info, name);
+
+ // TODO: avoid using screen buffers for displaying slides. Using a generic buffer
+ // allows for positioning of graphics as needed by Big Red Adventure.
+ // The main problem lies with menu, which relies on multiple buffers, mainly because
+ // it is crappy code.
+ _gfx->setBackground(&info.bg);
+ _gfx->setPalette(info.palette);
+ _gfx->copyScreen(Gfx::kBitBack, Gfx::kBitFront);
+
+ info.bg.free();
+ info.mask.free();
+ info.path.free();
+
+ return;
+}
+
+/*
+ changeLocation handles transitions between locations, and is able to display slides
+ between one and the other. The input parameter 'location' exists in some flavours:
+
+ 1 - [S].slide.[L]{.[C]}
+ 2 - [L]{.[C]}
+
+ where:
+
+ [S] is the slide to be shown
+ [L] is the location to switch to (immediately in case 2, or right after slide [S] in case 1)
+ [C] is the character to be selected, and is optional
+
+ The routine tells one form from the other by searching for the '.slide.'
+
+ NOTE: there exists one script in which [L] is not used in the case 1, but its use
+ is commented out, and would definitely crash the current implementation.
+*/
+void Parallaction::changeLocation(char *location) {
+ debugC(1, kDebugLocation, "changeLocation(%s)", location);
+
+ _soundMan->playLocationMusic(location);
+
+ // WORKAROUND: this if-statement has been added to avoid crashes caused by
+ // execution of label jobs after a location switch. The other workaround in
+ // Parallaction::runGame should have been rendered useless by this one.
+ if (_jDrawLabel != NULL) {
+ removeJob(_jDrawLabel);
+ removeJob(_jEraseLabel);
+ _jDrawLabel = NULL;
+ _jEraseLabel = NULL;
+ }
+
+
+ _hoverZone = NULL;
+ if (_engineFlags & kEngineBlockInput) {
+ changeCursor( kCursorArrow );
+ }
+
+ _animations.remove(&_char._ani);
+
+ freeLocation();
+ char buf[100];
+ strcpy(buf, location);
+
+ Common::StringList list;
+ char *tok = strtok(location, ".");
+ while (tok) {
+ list.push_back(tok);
+ tok = strtok(NULL, ".");
+ }
+
+ if (list.size() < 1 || list.size() > 4)
+ error("changeLocation: ill-formed location string '%s'", location);
+
+ if (list.size() > 1) {
+ if (list[1] == "slide") {
+ showSlide(list[0].c_str());
+ _gfx->setFont(_menuFont);
+ _gfx->displayCenteredString(14, _slideText[0]); // displays text on screen
+ _gfx->updateScreen();
+ waitUntilLeftClick();
+
+ list.remove_at(0); // removes slide name
+ list.remove_at(0); // removes 'slide'
+ }
+
+ // list is now only [L].{[C]} (see above comment)
+ if (list.size() == 2) {
+ changeCharacter(list[1].c_str());
+ strcpy(_characterName, list[1].c_str());
+ }
+ }
+
+ _animations.push_front(&_char._ani);
+
+ strcpy(_saveData1, list[0].c_str());
+ parseLocation(list[0].c_str());
+
+ _char._ani._oldPos.x = -1000;
+ _char._ani._oldPos.y = -1000;
+
+ _char._ani.field_50 = 0;
+ if (_location._startPosition.x != -1000) {
+ _char._ani._left = _location._startPosition.x;
+ _char._ani._top = _location._startPosition.y;
+ _char._ani._frame = _location._startFrame;
+ _location._startPosition.y = -1000;
+ _location._startPosition.x = -1000;
+ }
+
+
+ _gfx->copyScreen(Gfx::kBitBack, Gfx::kBitFront);
+ _gfx->copyScreen(Gfx::kBitBack, Gfx::kBit2);
+ _gfx->setBlackPalette();
+ _gfx->updateScreen();
+
+ if (_location._commands.size() > 0) {
+ runCommands(_location._commands);
+ runJobs();
+ _gfx->swapBuffers();
+ runJobs();
+ _gfx->swapBuffers();
+ }
+
+ if (_location._comment) {
+ doLocationEnterTransition();
+ }
+
+ runJobs();
+ _gfx->swapBuffers();
+
+ _gfx->setPalette(_gfx->_palette);
+ if (_location._aCommands.size() > 0) {
+ runCommands(_location._aCommands);
+ }
+
+ if (_hasLocationSound)
+ _soundMan->playSfx(_locationSound, 0, true);
+
+ debugC(1, kDebugLocation, "changeLocation() done");
+
+ return;
+
}
-void Parallaction::initOpcodes() {
-
- static const Opcode op0[] = {
- INSTRUCTION_PARSER(defLocal), // invalid opcode -> local definition
- INSTRUCTION_PARSER(animation), // on
- INSTRUCTION_PARSER(animation), // off
- INSTRUCTION_PARSER(x),
- INSTRUCTION_PARSER(y),
- INSTRUCTION_PARSER(z),
- INSTRUCTION_PARSER(f),
- INSTRUCTION_PARSER(loop),
- INSTRUCTION_PARSER(null), // endloop
- INSTRUCTION_PARSER(null), // show
- INSTRUCTION_PARSER(inc),
- INSTRUCTION_PARSER(inc), // dec
- INSTRUCTION_PARSER(set),
- INSTRUCTION_PARSER(put),
- INSTRUCTION_PARSER(call),
- INSTRUCTION_PARSER(null), // wait
- INSTRUCTION_PARSER(animation), // start
- INSTRUCTION_PARSER(sound),
- INSTRUCTION_PARSER(move)
- };
-
- _instructionParsers = op0;
-
-
- static const Opcode op1[] = {
- INSTRUCTION_OPCODE(invalid),
- INSTRUCTION_OPCODE(on),
- INSTRUCTION_OPCODE(off),
- INSTRUCTION_OPCODE(set), // x
- INSTRUCTION_OPCODE(set), // y
- INSTRUCTION_OPCODE(set), // z
- INSTRUCTION_OPCODE(set), // f
- INSTRUCTION_OPCODE(loop),
- INSTRUCTION_OPCODE(endloop),
- INSTRUCTION_OPCODE(null),
- INSTRUCTION_OPCODE(inc),
- INSTRUCTION_OPCODE(inc), // dec
- INSTRUCTION_OPCODE(set),
- INSTRUCTION_OPCODE(put),
- INSTRUCTION_OPCODE(call),
- INSTRUCTION_OPCODE(wait),
- INSTRUCTION_OPCODE(start),
- INSTRUCTION_OPCODE(sound),
- INSTRUCTION_OPCODE(move),
- INSTRUCTION_OPCODE(end)
- };
-
- _vm->_instructionOpcodes = op1;
-
- static const Opcode op2[] = {
- COMMAND_PARSER(invalid),
- COMMAND_PARSER(flags), // set
- COMMAND_PARSER(flags), // clear
- COMMAND_PARSER(animation), // start
- COMMAND_PARSER(zone), // speak
- COMMAND_PARSER(zone), // get
- COMMAND_PARSER(location), // location
- COMMAND_PARSER(zone), // open
- COMMAND_PARSER(zone), // close
- COMMAND_PARSER(zone), // on
- COMMAND_PARSER(zone), // off
- COMMAND_PARSER(call), // call
- COMMAND_PARSER(flags), // toggle
- COMMAND_PARSER(drop), // drop
- COMMAND_PARSER(null), // quit
- COMMAND_PARSER(move), // move
- COMMAND_PARSER(animation), // stop
- COMMAND_PARSER(endcommands), // endcommands
- COMMAND_PARSER(endcommands) // endzone
- };
-
- _commandParsers = op2;
-
- static const Opcode op3[] = {
- COMMAND_OPCODE(invalid),
- COMMAND_OPCODE(set),
- COMMAND_OPCODE(clear),
- COMMAND_OPCODE(start),
- COMMAND_OPCODE(speak),
- COMMAND_OPCODE(get),
- COMMAND_OPCODE(location),
- COMMAND_OPCODE(open),
- COMMAND_OPCODE(close),
- COMMAND_OPCODE(on),
- COMMAND_OPCODE(off),
- COMMAND_OPCODE(call),
- COMMAND_OPCODE(toggle),
- COMMAND_OPCODE(drop),
- COMMAND_OPCODE(quit),
- COMMAND_OPCODE(move),
- COMMAND_OPCODE(stop)
- };
-
- _commandOpcodes = op3;
-
-
- static const Opcode op4[] = {
- LOCATION_PARSER(invalid),
- LOCATION_PARSER(endlocation),
- LOCATION_PARSER(location),
- LOCATION_PARSER(disk),
- LOCATION_PARSER(nodes),
- LOCATION_PARSER(zone),
- LOCATION_PARSER(animation),
- LOCATION_PARSER(localflags),
- LOCATION_PARSER(commands),
- LOCATION_PARSER(acommands),
- LOCATION_PARSER(flags),
- LOCATION_PARSER(comment),
- LOCATION_PARSER(endcomment),
- LOCATION_PARSER(sound),
- LOCATION_PARSER(music),
- LOCATION_PARSER(redundant) // for redundant endanimation
- };
-
- _locationParsers = op4;
-
-
- static const Opcode op5[] = {
- ZONE_PARSER(invalid),
- ZONE_PARSER(limits),
- ZONE_PARSER(moveto),
- ZONE_PARSER(type),
- ZONE_PARSER(commands),
- ZONE_PARSER(label),
- ZONE_PARSER(flags),
- ZONE_PARSER(endzone)
- };
-
- _locationZoneParsers = op5;
-
- static const Opcode op6[] = {
- ANIM_PARSER(invalid),
- ANIM_PARSER(script),
- ANIM_PARSER(commands),
- ANIM_PARSER(type),
- ANIM_PARSER(label),
- ANIM_PARSER(flags),
- ANIM_PARSER(file),
- ANIM_PARSER(position),
- ANIM_PARSER(moveto),
- ANIM_PARSER(endanimation)
- };
-
- _locationAnimParsers = op6;
-
- _currentOpcodes = 0;
- _currentStatements = 0;
+// 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(1, kDebugLocation, "doLocationEnterTransition");
+
+ if (_localFlags[_currentLocationIndex] & kFlagsVisited) {
+ debugC(3, kDebugLocation, "skipping location transition");
+ return; // visited
+ }
+
+ Palette pal(_gfx->_palette);
+ pal.makeGrayscale();
+ _gfx->setPalette(pal);
+
+ jobRunScripts(NULL, NULL);
+ jobEraseAnimations(NULL, NULL);
+ jobDisplayAnimations(NULL, NULL);
+
+ _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();
+ }
+ debugC(1, kDebugLocation, "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(1, kDebugLocation, "freeZones: kEngineQuit = %i", _engineFlags & kEngineQuit);
+
+ ZoneList::iterator it = _zones.begin();
+
+ while ( it != _zones.end() ) {
+
+ Zone* z = *it;
+
+ // WORKAROUND: this huge condition is needed because we made TypeData a collection of structs
+ // instead of an union. So, merge->_obj1 and get->_icon were just aliases in the original engine,
+ // but we need to check it separately here. The same workaround is applied in hitZone.
+ if (((z->_top == -1) ||
+ ((z->_left == -2) && (
+ (((z->_type & 0xFFFF) == kZoneMerge) && ((isItemInInventory(MAKE_INVENTORY_ID(z->u.merge->_obj1)) != 0) || (isItemInInventory(MAKE_INVENTORY_ID(z->u.merge->_obj2)) != 0))) ||
+ (((z->_type & 0xFFFF) == kZoneGet) && ((isItemInInventory(MAKE_INVENTORY_ID(z->u.get->_icon)) != 0)))
+ ))) &&
+ ((_engineFlags & kEngineQuit) == 0)) {
+
+ debugC(1, kDebugLocation, "freeZones preserving zone '%s'", z->_label._text);
+
+ it++;
+
+ } else
+
+ it = _zones.erase(it);
+
+ }
+
+ return;
+}
+
+
+
+
+
+
+
+
+
+
+
+
} // namespace Parallaction