aboutsummaryrefslogtreecommitdiff
path: root/engines/parallaction/parallaction.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'engines/parallaction/parallaction.cpp')
-rw-r--r--engines/parallaction/parallaction.cpp450
1 files changed, 406 insertions, 44 deletions
diff --git a/engines/parallaction/parallaction.cpp b/engines/parallaction/parallaction.cpp
index afa246a9f8..f69e39519c 100644
--- a/engines/parallaction/parallaction.cpp
+++ b/engines/parallaction/parallaction.cpp
@@ -35,6 +35,7 @@
#include "parallaction/input.h"
#include "parallaction/parallaction.h"
#include "parallaction/debug.h"
+#include "parallaction/saveload.h"
#include "parallaction/sound.h"
@@ -47,7 +48,6 @@ Parallaction *_vm = NULL;
// public stuff
char _saveData1[30] = { '\0' };
-uint16 _language = 0;
uint32 _engineFlags = 0;
uint16 _score = 1;
@@ -100,8 +100,6 @@ Parallaction::~Parallaction() {
cleanupGui();
- delete _comboArrow;
-
delete _localFlagNames;
delete _gfx;
delete _soundMan;
@@ -116,17 +114,13 @@ int Parallaction::init() {
_objectsNames = NULL;
_globalFlagsNames = NULL;
_location._hasSound = false;
- _baseTime = 0;
_numLocations = 0;
- _gameToLoad = -1;
_location._startPosition.x = -1000;
_location._startPosition.y = -1000;
_location._startFrame = 0;
_location._comment = NULL;
_location._endComment = NULL;
- _quit = false;
-
_pathBuffer = 0;
_screenSize = _screenWidth * _screenHeight;
@@ -137,6 +131,7 @@ int Parallaction::init() {
initInventory(); // needs to be pushed into subclass
+ // this needs _disk to be already setup
_input = new Input(this);
_gfx = new Gfx(this);
@@ -146,19 +141,10 @@ int Parallaction::init() {
_menuHelper = 0;
setupBalloonManager();
- syncSoundSettings();
return 0;
}
-
-void Parallaction::clearSet(OpcodeSet &opcodes) {
- for (Common::Array<const Opcode*>::iterator i = opcodes.begin(); i != opcodes.end(); ++i)
- delete *i;
- opcodes.clear();
-}
-
-
void Parallaction::updateView() {
if ((_engineFlags & kEnginePauseJobs) && (_input->_inputMode != Input::kInputModeInventory)) {
@@ -167,7 +153,7 @@ void Parallaction::updateView() {
_gfx->animatePalette();
_gfx->updateScreen();
- g_system->delayMillis(30);
+ _vm->_system->delayMillis(30);
}
@@ -282,7 +268,15 @@ void Parallaction::freeLocation() {
return;
}
+void Parallaction::showSlide(const char *name, int x, int y) {
+ BackgroundInfo *info = new BackgroundInfo;
+ _disk->loadSlide(*info, name);
+ info->x = (x == CENTER_LABEL_HORIZONTAL) ? ((_vm->_screenWidth - info->width) >> 1) : x;
+ info->y = (y == CENTER_LABEL_VERTICAL) ? ((_vm->_screenHeight - info->height) >> 1) : y;
+
+ _gfx->setBackground(kBackgroundSlide, info);
+}
void Parallaction::freeBackground() {
@@ -307,23 +301,19 @@ void Parallaction::showLocationComment(const char *text, bool end) {
}
-void Parallaction::processInput(InputData *data) {
- if (!data) {
- return;
- }
-
- switch (data->_event) {
+void Parallaction::processInput(int event) {
+ switch (event) {
case kEvSaveGame:
_input->stopHovering();
- saveGame();
- setArrowCursor();
+ _saveLoad->saveGame();
+ _input->setArrowCursor();
break;
case kEvLoadGame:
_input->stopHovering();
- loadGame();
- setArrowCursor();
+ _saveLoad->loadGame();
+ _input->setArrowCursor();
break;
}
@@ -333,22 +323,19 @@ void Parallaction::processInput(InputData *data) {
void Parallaction::runGame() {
- InputData *data = _input->updateInput();
- if (_vm->quit())
+ int event = _input->updateInput();
+ if (_engineFlags & kEngineQuit)
return;
runGuiFrame();
runDialogueFrame();
runCommentFrame();
- if (_vm->quit())
- return;
-
if (_input->_inputMode == Input::kInputModeGame) {
- processInput(data);
+ processInput(event);
runPendingZones();
- if (_vm->quit())
+ if (_engineFlags & kEngineQuit)
return;
if (_engineFlags & kEngineChangeLocation) {
@@ -411,7 +398,7 @@ void Parallaction::doLocationEnterTransition() {
pal.fadeTo(_gfx->_palette, 4);
_gfx->setPalette(pal);
_gfx->updateScreen();
- g_system->delayMillis(20);
+ _vm->_system->delayMillis(20);
}
_gfx->setPalette(_gfx->_palette);
@@ -439,6 +426,387 @@ uint32 Parallaction::getLocationFlags() {
+void Parallaction::drawAnimations() {
+ debugC(9, kDebugExec, "Parallaction_ns::drawAnimations()\n");
+
+ uint16 layer = 0, scale = 100;
+
+ for (AnimationList::iterator it = _location._animations.begin(); it != _location._animations.end(); it++) {
+
+ AnimationPtr anim = *it;
+ GfxObj *obj = anim->gfxobj;
+
+ // Validation is performed here, so that every animation is affected, instead that only the ones
+ // who *own* a script. In fact, some scripts can change values in other animations.
+ // The right way to do this would be to enforce validation when any variable is modified from
+ // a script.
+ anim->validateScriptVars();
+
+ if ((anim->_flags & kFlagsActive) && ((anim->_flags & kFlagsRemove) == 0)) {
+
+ if (anim->_flags & kFlagsNoMasked)
+ layer = LAYER_FOREGROUND;
+ else {
+ if (getGameType() == GType_Nippon) {
+ // Layer in NS depends on where the animation is on the screen, for each animation.
+ layer = _gfx->_backgroundInfo->getLayer(anim->getFrameY() + anim->height());
+ } else {
+ // Layer in BRA is calculated from Z value. For characters it is the same as NS,
+ // but other animations can have Z set from scripts independently from their
+ // position on the screen.
+ layer = _gfx->_backgroundInfo->getLayer(anim->getZ());
+ }
+ }
+
+ if (getGameType() == GType_BRA) {
+ if (anim->_flags & (kFlagsScaled | kFlagsCharacter)) {
+ if (anim->getZ() <= _location._zeta0) {
+ if (anim->getZ() >= _location._zeta1) {
+ scale = ((anim->getZ() - _location._zeta1) * (100 - _location._zeta2)) / (_location._zeta0 - _location._zeta1) + _location._zeta2;
+ } else {
+ scale = _location._zeta2;
+ }
+ }
+ }
+ }
+
+ if (obj) {
+ _gfx->showGfxObj(obj, true);
+ obj->frame = anim->getF();
+ obj->x = anim->getX();
+ obj->y = anim->getY();
+ obj->z = anim->getZ();
+ obj->layer = layer;
+ obj->scale = scale;
+ }
+ }
+
+ if (((anim->_flags & kFlagsActive) == 0) && (anim->_flags & kFlagsRemove)) {
+ anim->_flags &= ~kFlagsRemove;
+ }
+
+ if ((anim->_flags & kFlagsActive) && (anim->_flags & kFlagsRemove)) {
+ anim->_flags &= ~kFlagsActive;
+ anim->_flags |= kFlagsRemove;
+ if (obj) {
+ _gfx->showGfxObj(obj, false);
+ }
+ }
+ }
+
+ debugC(9, kDebugExec, "Parallaction_ns::drawAnimations done()\n");
+
+ return;
+}
+
+
+void Parallaction::showZone(ZonePtr z, bool visible) {
+ if (!z) {
+ return;
+ }
+
+ if (visible) {
+ z->_flags &= ~kFlagsRemove;
+ z->_flags |= kFlagsActive;
+ } else {
+ z->_flags |= kFlagsRemove;
+ }
+
+ if ((z->_type & 0xFFFF) == kZoneGet) {
+ _gfx->showGfxObj(z->u.get->gfxobj, visible);
+
+ GetData *data = z->u.get;
+ if (data->hasMask && _gfx->_backgroundInfo->hasMask) {
+ if (visible) {
+ _gfx->_backgroundInfo->mask.bltOr(data->gfxobj->x, data->gfxobj->y, data->_mask[0], 0, 0, data->_mask->w, data->_mask->h);
+ } else {
+ _gfx->_backgroundInfo->mask.bltCopy(data->gfxobj->x, data->gfxobj->y, data->_mask[1], 0, 0, data->_mask->w, data->_mask->h);
+ }
+ }
+ }
+}
+
+
+//
+// ZONE TYPE: EXAMINE
+//
+
+void Parallaction::enterCommentMode(ZonePtr z) {
+ if (!z) {
+ return;
+ }
+
+ _commentZone = z;
+
+ ExamineData *data = _commentZone->u.examine;
+
+ if (!data->_description) {
+ return;
+ }
+
+ // TODO: move this balloons stuff into DialogueManager and BalloonManager
+ if (getGameType() == GType_Nippon) {
+ int id;
+ if (data->_filename) {
+ if (data->_cnv == 0) {
+ data->_cnv = _disk->loadStatic(data->_filename);
+ }
+
+ _gfx->setHalfbriteMode(true);
+ _balloonMan->setSingleBalloon(data->_description, 0, 90, 0, BalloonManager::kNormalColor);
+ Common::Rect r;
+ data->_cnv->getRect(0, r);
+ id = _gfx->setItem(data->_cnv, 140, (_screenHeight - r.height())/2);
+ _gfx->setItemFrame(id, 0);
+ id = _gfx->setItem(_char._head, 100, 152);
+ _gfx->setItemFrame(id, 0);
+ } else {
+ _balloonMan->setSingleBalloon(data->_description, 140, 10, 0, BalloonManager::kNormalColor);
+ id = _gfx->setItem(_char._talk, 190, 80);
+ _gfx->setItemFrame(id, 0);
+ }
+ } else
+ if (getGameType() == GType_BRA) {
+ _balloonMan->setSingleBalloon(data->_description, 0, 0, 1, BalloonManager::kNormalColor);
+ int id = _gfx->setItem(_char._talk, 10, 80);
+ _gfx->setItemFrame(id, 0);
+ }
+
+ _input->_inputMode = Input::kInputModeComment;
+}
+
+void Parallaction::exitCommentMode() {
+ _input->_inputMode = Input::kInputModeGame;
+
+ hideDialogueStuff();
+ _gfx->setHalfbriteMode(false);
+
+ _cmdExec->run(_commentZone->_commands, _commentZone);
+ _commentZone = nullZonePtr;
+}
+
+void Parallaction::runCommentFrame() {
+ if (_input->_inputMode != Input::kInputModeComment) {
+ return;
+ }
+
+ if (_input->getLastButtonEvent() == kMouseLeftUp) {
+ exitCommentMode();
+ }
+}
+
+
+void Parallaction::runZone(ZonePtr z) {
+ debugC(3, kDebugExec, "runZone (%s)", z->_name);
+
+ uint16 subtype = z->_type & 0xFFFF;
+
+ debugC(3, kDebugExec, "type = %x, object = %x", subtype, (z->_type & 0xFFFF0000) >> 16);
+ switch(subtype) {
+
+ case kZoneExamine:
+ enterCommentMode(z);
+ return;
+
+ case kZoneGet:
+ pickupItem(z);
+ break;
+
+ case kZoneDoor:
+ if (z->_flags & kFlagsLocked) break;
+ updateDoor(z, !(z->_flags & kFlagsClosed));
+ break;
+
+ case kZoneHear:
+ _soundMan->playSfx(z->u.hear->_name, z->u.hear->_channel, (z->_flags & kFlagsLooping) == kFlagsLooping, 60);
+ break;
+
+ case kZoneSpeak:
+ enterDialogueMode(z);
+ return;
+ }
+
+ debugC(3, kDebugExec, "runZone completed");
+
+ _cmdExec->run(z->_commands, z);
+
+ return;
+}
+
+//
+// ZONE TYPE: DOOR
+//
+void Parallaction::updateDoor(ZonePtr z, bool close) {
+ z->_flags = close ? (z->_flags |= kFlagsClosed) : (z->_flags &= ~kFlagsClosed);
+
+ if (z->u.door->gfxobj) {
+ uint frame = (close ? 0 : 1);
+// z->u.door->gfxobj->setFrame(frame);
+ z->u.door->gfxobj->frame = frame;
+ }
+
+ return;
+}
+
+
+
+//
+// ZONE TYPE: GET
+//
+
+bool Parallaction::pickupItem(ZonePtr z) {
+ if (z->_flags & kFlagsFixed) {
+ return false;
+ }
+
+ int slot = addInventoryItem(z->u.get->_icon);
+ if (slot != -1) {
+ showZone(z, false);
+ }
+
+ return (slot != -1);
+}
+
+// FIXME: input coordinates must be offseted to handle scrolling!
+bool Parallaction::checkSpecialZoneBox(ZonePtr z, uint32 type, uint x, uint y) {
+ // not a special zone
+ if ((z->getX() != -2) && (z->getX() != -3)) {
+ return false;
+ }
+
+ // 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 freeZones.
+ if ((((z->_type & 0xFFFF) == kZoneMerge) && (((x == z->u.merge->_obj1) && (y == z->u.merge->_obj2)) || ((x == z->u.merge->_obj2) && (y == z->u.merge->_obj1)))) ||
+ (((z->_type & 0xFFFF) == kZoneGet) && ((x == z->u.get->_icon) || (y == z->u.get->_icon)))) {
+
+ // WORKAROUND for bug 2070751: special zones are only used in NS, to allow the
+ // the EXAMINE/USE action to be applied on some particular item in the inventory.
+ // The usage a verb requires at least an item match, so type can't be 0, as it
+ // was in the original code. This bug has been here since the beginning, and was
+ // hidden by label code, which filtered the bogus matches produced here.
+
+ // look for action + item match
+ if (z->_type == type)
+ return true;
+ // look for item match, but don't accept 0 types
+ if (((z->_type & 0xFFFF0000) == type) && (type))
+ return true;
+ }
+
+ return false;
+}
+
+// FIXME: input coordinates must be offseted to handle scrolling!
+bool Parallaction::checkZoneBox(ZonePtr z, uint32 type, uint x, uint y) {
+ if (z->_flags & kFlagsRemove)
+ return false;
+
+ debugC(5, kDebugExec, "checkZoneBox for %s (type = %x, x = %i, y = %i)", z->_name, type, x, y);
+
+ Common::Rect r;
+ z->getBox(r);
+ r.right++; // adjust border because Common::Rect doesn't include bottom-right edge
+ r.bottom++;
+
+ r.grow(-1); // allows some tolerance for mouse click
+
+ if (!r.contains(x, y)) {
+
+ // check for special zones (items defined in common.loc)
+ if (checkSpecialZoneBox(z, type, x, y))
+ return true;
+
+ if (z->getX() != -1)
+ return false;
+ if ((int)x < _char._ani->getFrameX())
+ return false;
+ if ((int)x > (_char._ani->getFrameX() + _char._ani->width()))
+ return false;
+ if ((int)y < _char._ani->getFrameY())
+ return false;
+ if ((int)y > (_char._ani->getFrameY() + _char._ani->height()))
+ return false;
+ }
+
+ // normal Zone
+ if ((type == 0) && ((z->_type & 0xFFFF0000) == 0))
+ return true;
+ if (z->_type == type)
+ return true;
+ if ((z->_type & 0xFFFF0000) == type)
+ return true;
+
+ return false;
+}
+
+// FIXME: input coordinates must be offseted to handle scrolling!
+bool Parallaction::checkLinkedAnimBox(ZonePtr z, uint32 type, uint x, uint y) {
+ if (z->_flags & kFlagsRemove)
+ return false;
+
+ if ((z->_flags & kFlagsAnimLinked) == 0)
+ return false;
+
+ debugC(5, kDebugExec, "checkLinkedAnimBox for %s (type = %x, x = %i, y = %i)", z->_name, type, x, y);
+
+ AnimationPtr anim = z->_linkedAnim;
+ Common::Rect r(anim->getFrameX(), anim->getFrameY(), anim->getFrameX() + anim->width() + 1, anim->getFrameY() + anim->height() + 1);
+
+ if (!r.contains(x, y)) {
+ return false;
+ }
+
+ // NOTE: the implementation of the following lines is a different in the
+ // original... it is working so far, though
+ if ((type == 0) && ((z->_type & 0xFFFF0000) == 0))
+ return true;
+ if (z->_type == type)
+ return true;
+ if ((z->_type & 0xFFFF0000) == type)
+ return true;
+
+ return false;
+}
+
+ZonePtr Parallaction::hitZone(uint32 type, uint16 x, uint16 y) {
+ uint16 _di = y;
+ uint16 _si = x;
+
+ for (ZoneList::iterator it = _location._zones.begin(); it != _location._zones.end(); it++) {
+ if (checkLinkedAnimBox(*it, type, x, y)) {
+ return *it;
+ }
+ if (checkZoneBox(*it, type, x, y)) {
+ return *it;
+ }
+ }
+
+
+ int16 _a, _b, _c, _d, _e, _f;
+ for (AnimationList::iterator ait = _location._animations.begin(); ait != _location._animations.end(); ait++) {
+
+ AnimationPtr a = *ait;
+
+ _a = (a->_flags & kFlagsActive) ? 1 : 0; // _a: active Animation
+ _e = ((_si >= a->getFrameX() + a->width()) || (_si <= a->getFrameX())) ? 0 : 1; // _e: horizontal range
+ _f = ((_di >= a->getFrameY() + a->height()) || (_di <= a->getFrameY())) ? 0 : 1; // _f: vertical range
+
+ _b = ((type != 0) || (a->_type == kZoneYou)) ? 0 : 1; // _b: (no type specified) AND (Animation is not the character)
+ _c = (a->_type & 0xFFFF0000) ? 0 : 1; // _c: Animation is not an object
+ _d = ((a->_type & 0xFFFF0000) != type) ? 0 : 1; // _d: Animation is an object of the same type
+
+ if ((_a != 0 && _e != 0 && _f != 0) && ((_b != 0 && _c != 0) || (a->_type == type) || (_d != 0))) {
+
+ return a;
+
+ }
+
+ }
+
+ return nullZonePtr;
+}
+
ZonePtr Parallaction::findZone(const char *name) {
@@ -451,7 +819,7 @@ ZonePtr Parallaction::findZone(const char *name) {
void Parallaction::freeZones() {
- debugC(2, kDebugExec, "freeZones: _vm->_quit = %i", _vm->_quit);
+ debugC(2, kDebugExec, "freeZones: kEngineQuit = %i", _engineFlags & kEngineQuit);
ZoneList::iterator it = _location._zones.begin();
@@ -460,8 +828,7 @@ void Parallaction::freeZones() {
// 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->getY() == -1) || (z->getX() == -2)) && ((_vm->quit()) == 0)) {
+ if (((z->getY() == -1) || (z->getX() == -2)) && ((_engineFlags & kEngineQuit) == 0)) {
debugC(2, kDebugExec, "freeZones preserving zone '%s'", z->_name);
it++;
} else {
@@ -475,11 +842,6 @@ void Parallaction::freeZones() {
return;
}
-void Parallaction::syncSoundSettings() {
- _mixer->setVolumeForSoundType(Audio::Mixer::kMusicSoundType, ConfMan.getInt("music_volume"));
- _mixer->setVolumeForSoundType(Audio::Mixer::kSFXSoundType, ConfMan.getInt("sfx_volume") / 6);
- _mixer->setVolumeForSoundType(Audio::Mixer::kSpeechSoundType, ConfMan.getInt("speech_volume"));
-}
enum {
WALK_LEFT = 0,
@@ -528,7 +890,7 @@ Character::Character(Parallaction *vm) : _vm(vm), _ani(new Animation) {
_ani->setY(100);
_ani->setZ(10);
_ani->setF(0);
- _ani->_flags = kFlagsActive | kFlagsNoName;
+ _ani->_flags = kFlagsActive | kFlagsNoName | kFlagsCharacter;
_ani->_type = kZoneYou;
strncpy(_ani->_name, "yourself", ZONENAME_LENGTH);