Logo Search packages:      
Sourcecode: scummvm version File versions  Download package

saga.cpp

/* 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: https://scummvm.svn.sourceforge.net/svnroot/scummvm/scummvm/tags/release-0-11-1/engines/saga/saga.cpp $
 * $Id: saga.cpp 30944 2008-02-23 22:50:18Z sev $
 *
 */



#include "common/file.h"
#include "common/config-manager.h"
#include "common/system.h"
#include "common/events.h"

#include "sound/mixer.h"

#include "saga/saga.h"

#include "saga/rscfile.h"
#include "saga/gfx.h"
#include "saga/render.h"
#include "saga/actor.h"
#include "saga/animation.h"
#include "saga/console.h"
#include "saga/events.h"
#include "saga/font.h"
#include "saga/interface.h"
#include "saga/isomap.h"
#include "saga/puzzle.h"
#include "saga/script.h"
#include "saga/scene.h"
#include "saga/sndres.h"
#include "saga/sprite.h"
#include "saga/sound.h"
#include "saga/music.h"
#include "saga/palanim.h"
#include "saga/objectmap.h"
#include "saga/sagaresnames.h"

namespace Saga {

#define MAX_TIME_DELTA 100

SagaEngine::SagaEngine(OSystem *syst, const SAGAGameDescription *gameDesc)
      : Engine(syst), _gameDescription(gameDesc) {

      _leftMouseButtonPressed = _rightMouseButtonPressed = false;

      _console = NULL;
      _quit = false;

      _resource = NULL;
      _sndRes = NULL;
      _events = NULL;
      _font = NULL;
      _sprite = NULL;
      _anim = NULL;
      _script = NULL;
      _interface = NULL;
      _actor = NULL;
      _palanim = NULL;
      _scene = NULL;
      _isoMap = NULL;
      _gfx = NULL;
      _console = NULL;
      _render = NULL;
      _music = NULL;
      _sound = NULL;
      _puzzle = NULL;

      _frameCount = 0;
      _globalFlags = 0;
      _mouseClickCount = 0;
      memset(_ethicsPoints, 0, sizeof(_ethicsPoints));

      // The Linux version of Inherit the Earth puts all data files in an
      // 'itedata' sub-directory, except for voices.rsc
      Common::File::addDefaultDirectory(_gameDataPath + "itedata/");

      // The Windows version of Inherit the Earth puts various data files in
      // other subdirectories.
      Common::File::addDefaultDirectory(_gameDataPath + "graphics/");
      Common::File::addDefaultDirectory(_gameDataPath + "music/");
      Common::File::addDefaultDirectory(_gameDataPath + "sound/");

      // The Multi-OS version puts the voices file in the root directory of
      // the CD. The rest of the data files are in game/itedata
      Common::File::addDefaultDirectory(_gameDataPath + "game/itedata/");

      // Mac CD Wyrmkeep
      Common::File::addDefaultDirectory(_gameDataPath + "patch/");

      // Setup mixer
      if (!_mixer->isReady()) {
            warning("Sound initialization failed.");
      }

      _displayClip.left = _displayClip.top = 0;
      syst->getEventManager()->registerRandomSource(_rnd, "saga");
}

SagaEngine::~SagaEngine() {
      if (_scene != NULL) {
            if (_scene->isSceneLoaded()) {
                  _scene->endScene();
            }
      }

      delete _puzzle;
      delete _sndRes;
      delete _events;
      delete _font;
      delete _sprite;
      delete _anim;
      delete _script;
      delete _interface;
      delete _actor;
      delete _palanim;
      delete _scene;
      delete _isoMap;
      delete _render;
      delete _music;
      delete _sound;
      delete _gfx;
      delete _console;

      delete _resource;
}

int SagaEngine::init() {
      _soundVolume = ConfMan.getInt("sfx_volume") / 25;
      _musicVolume = ConfMan.getInt("music_volume") / 25;
      _subtitlesEnabled = ConfMan.getBool("subtitles");
      _readingSpeed = getTalkspeed();
      _copyProtection = ConfMan.getBool("copy_protection");
      _gf_wyrmkeep = false;
      _gf_compressed_sounds = false;

      if (_readingSpeed > 3)
            _readingSpeed = 0;

      _resource = new Resource(this);

      // Detect game and open resource files
      if (!initGame()) {
            GUIErrorMessage("Error loading game resources.");
            return FAILURE;
      }

      // Initialize engine modules
      _sndRes = new SndRes(this);
      _events = new Events(this);
      _font = new Font(this);
      _sprite = new Sprite(this);
      _anim = new Anim(this);
      _script = new Script(this);
      _interface = new Interface(this); // requires script module
      _scene = new Scene(this);
      _actor = new Actor(this);
      _palanim = new PalAnim(this);
      _isoMap = new IsoMap(this);
      _puzzle = new Puzzle(this);

      // System initialization

      _previousTicks = _system->getMillis();

      // Initialize graphics
      _gfx = new Gfx(this, _system, getDisplayWidth(), getDisplayHeight());

      // Graphics driver should be initialized before console
      _console = new Console(this);

      // Graphics should be initialized before music
      int midiDriver = MidiDriver::detectMusicDriver(MDT_MIDI | MDT_ADLIB | MDT_PREFER_MIDI);
      bool native_mt32 = ((midiDriver == MD_MT32) || ConfMan.getBool("native_mt32"));
      bool adlib = (midiDriver == MD_ADLIB);

      MidiDriver *driver = MidiDriver::createMidi(midiDriver);
      if (native_mt32)
            driver->property(MidiDriver::PROP_CHANNEL_MASK, 0x03FE);

      _music = new Music(this, _mixer, driver, _musicVolume);
      _music->setNativeMT32(native_mt32);
      _music->setAdlib(adlib);

      if (!_musicVolume) {
            debug(1, "Music disabled.");
      }

      _render = new Render(this, _system);
      if (!_render->initialized()) {
            return FAILURE;
      }

      // Initialize system specific sound
      _sound = new Sound(this, _mixer, _soundVolume);
      if (!_soundVolume) {
            debug(1, "Sound disabled.");
      }

      _interface->converseInit();
      _script->setVerb(_script->getVerbType(kVerbWalkTo));

      _music->setVolume(-1, 1);

      _gfx->initPalette();

      if (_voiceFilesExist) {
            if (getGameType() == GType_IHNM) {
                  if (!ConfMan.hasKey("voices")) {
                        _voicesEnabled = true;
                        ConfMan.setBool("voices", true);
                  } else {
                        _voicesEnabled = ConfMan.getBool("voices");
                  }
            } else {
                  _voicesEnabled = true;
            }
      }

      // FIXME: This is the ugly way of reducing redraw overhead. It works
      //        well for 320x200 but it's unclear how well it will work for
      //        640x480.

      if (getGameType() == GType_ITE)
            _system->setFeatureState(OSystem::kFeatureAutoComputeDirtyRects, true);

      return SUCCESS;
}

int SagaEngine::go() {
      int msec = 0;

      _previousTicks = _system->getMillis();

      if (ConfMan.hasKey("start_scene")) {
            _scene->changeScene(ConfMan.getInt("start_scene"), 0, kTransitionNoFade);
      } else if (ConfMan.hasKey("boot_param")) {
            if (getGameType() == GType_ITE)
                  _interface->addToInventory(_actor->objIndexToId(ITE_OBJ_MAGIC_HAT));
            _scene->changeScene(ConfMan.getInt("boot_param"), 0, kTransitionNoFade);
      } else if (ConfMan.hasKey("save_slot")) {
            // First scene sets up palette
            _scene->changeScene(getStartSceneNumber(), 0, kTransitionNoFade);
            _events->handleEvents(0); // Process immediate events

            _interface->setMode(kPanelMain);
            char *fileName;
            fileName = calcSaveFileName(ConfMan.getInt("save_slot"));
            load(fileName);
      } else {
            _framesEsc = 0;
            _scene->startScene();
      }

      uint32 currentTicks;

      while (!_quit) {
            if (_console->isAttached())
                  _console->onFrame();

            if (_render->getFlags() & RF_RENDERPAUSE) {
                  // Freeze time while paused
                  _previousTicks = _system->getMillis();
            } else {
                  currentTicks = _system->getMillis();
                  // Timer has rolled over after 49 days
                  if (currentTicks < _previousTicks)
                        msec = 0;
                  else {
                        msec = currentTicks - _previousTicks;
                        _previousTicks = currentTicks;
                  }
                  if (msec > MAX_TIME_DELTA) {
                        msec = MAX_TIME_DELTA;
                  }

                  // Since Puzzle and forced text are actorless, we do them here
                  if (_puzzle->isActive() || _actor->isForcedTextShown()) {
                        _actor->handleSpeech(msec);
                  } else if (!_scene->isInIntro()) {
                        if (_interface->getMode() == kPanelMain ||
                                    _interface->getMode() == kPanelConverse ||
                                    _interface->getMode() == kPanelCutaway ||
                                    _interface->getMode() == kPanelNull ||
                                    _interface->getMode() == kPanelChapterSelection)
                              _actor->direct(msec);
                  }

                  _events->handleEvents(msec);
                  _script->executeThreads(msec);
            }
            // Per frame processing
            _render->drawScene();
            _system->delayMillis(10);
      }

      return 0;
}

void SagaEngine::loadStrings(StringsTable &stringsTable, const byte *stringsPointer, size_t stringsLength) {
      uint16 stringsCount;
      size_t offset;
      int i;

      if (stringsLength == 0) {
            error("SagaEngine::loadStrings() Error loading strings list resource");
      }

      stringsTable.stringsPointer = (byte*)malloc(stringsLength);
      memcpy(stringsTable.stringsPointer, stringsPointer, stringsLength);


      MemoryReadStreamEndian scriptS(stringsTable.stringsPointer, stringsLength, isBigEndian()); //TODO: get endianess from context

      offset = scriptS.readUint16();
      stringsCount = offset / 2;
      stringsTable.strings = (const char **)malloc(stringsCount * sizeof(*stringsTable.strings));
      i = 0;
      scriptS.seek(0);
      while (i < stringsCount) {
            offset = scriptS.readUint16();
            if (offset == stringsLength) {
                  stringsCount = i;
                  stringsTable.strings = (const char **)realloc(stringsTable.strings, stringsCount * sizeof(*stringsTable.strings));
                  break;
            }
            if (offset > stringsLength) {
                  // This case should never occur, but apparently it does in the Italian fan
                  // translation of IHNM
                  warning("SagaEngine::loadStrings wrong strings table");
                  stringsCount = i;
                  stringsTable.strings = (const char **)realloc(stringsTable.strings, stringsCount * sizeof(*stringsTable.strings));
                  break;
            }
            stringsTable.strings[i] = (const char *)stringsTable.stringsPointer + offset;
            debug(9, "string[%i]=%s", i, stringsTable.strings[i]);
            i++;
      }
      stringsTable.stringsCount = stringsCount;
}

const char *SagaEngine::getObjectName(uint16 objectId) {
      ActorData *actor;
      ObjectData *obj;
      const HitZone *hitZone;

      // Disable the object names in IHNM when the chapter is 8
      if (getGameType() == GType_IHNM && _scene->currentChapterNumber() == 8)
            return "";

      switch (objectTypeId(objectId)) {
      case kGameObjectObject:
            obj = _actor->getObj(objectId);
            if (getGameType() == GType_ITE)
                  return _script->_mainStrings.getString(obj->_nameIndex);
            return _actor->_objectsStrings.getString(obj->_nameIndex);
      case kGameObjectActor:
            actor = _actor->getActor(objectId);
            return _actor->_actorsStrings.getString(actor->_nameIndex);
      case kGameObjectHitZone:
            hitZone = _scene->_objectMap->getHitZone(objectIdToIndex(objectId));

            if (hitZone == NULL)
                  return "";

            return _scene->_sceneStrings.getString(hitZone->getNameIndex());
      }
      warning("SagaEngine::getObjectName name not found for 0x%X", objectId);
      return NULL;
}

const char *SagaEngine::getTextString(int textStringId) {
      const char *string;
      int lang = 0;

      switch (getLanguage()) {
            case Common::DE_DEU:
                  lang = 1;
                  break;
            case Common::IT_ITA:
                  lang = 2;
                  break;
            case Common::ES_ESP:
                  lang = 3;
                  break;
            default:
                  lang = 0;
                  break;
      }

      string = ITEinterfaceTextStrings[lang][textStringId];
      if (!string)
            string = ITEinterfaceTextStrings[0][textStringId];

      return string;
}

void SagaEngine::getExcuseInfo(int verb, const char *&textString, int &soundResourceId) {
      textString = NULL;

      if (verb == _script->getVerbType(kVerbPickUp)) {
            textString = getTextString(kTextICantPickup);
            soundResourceId = RID_BOAR_VOICE_007;
      } else
            if (verb == _script->getVerbType(kVerbLookAt)) {
            textString = getTextString(kTextNothingSpecial);
            soundResourceId = RID_BOAR_VOICE_006;
            }
            if (verb == _script->getVerbType(kVerbOpen)) {
            textString = getTextString(kTextNoPlaceToOpen);
            soundResourceId = RID_BOAR_VOICE_000;
            }
            if (verb == _script->getVerbType(kVerbClose)) {
            textString = getTextString(kTextNoOpening);
            soundResourceId = RID_BOAR_VOICE_002;
            }
            if (verb == _script->getVerbType(kVerbUse)) {
            textString = getTextString(kTextDontKnow);
            soundResourceId = RID_BOAR_VOICE_005;
      }
}

ColorId SagaEngine::KnownColor2ColorId(KnownColor knownColor) {
      ColorId colorId = kITEColorTransBlack;

      if (getGameType() == GType_ITE) {
            switch (knownColor) {
            case(kKnownColorTransparent):
                  colorId = kITEColorTransBlack;
                  break;
            case (kKnownColorBrightWhite):
                  colorId = kITEColorBrightWhite;
                  break;
            case (kKnownColorWhite):
                  colorId = kITEColorWhite;
                  break;
            case (kKnownColorBlack):
                  colorId = kITEColorBlack;
                  break;
            case (kKnownColorSubtitleTextColor):
                  colorId = (ColorId)255;
                  break;
            case (kKnownColorVerbText):
                  colorId = kITEColorBlue;
                  break;
            case (kKnownColorVerbTextShadow):
                  colorId = kITEColorBlack;
                  break;
            case (kKnownColorVerbTextActive):
                  colorId = (ColorId)96;
                  break;

            default:
                  error("SagaEngine::KnownColor2ColorId unknown color %i", knownColor);
            }
      } else if (getGameType() == GType_IHNM) {
            // The default colors in the Spanish version of IHNM are shifted by one
            // Fixes bug #1848016 - "IHNM: Wrong Subtitles Color (Spanish)"
            int offset = (getGameId() == GID_IHNM_CD_ES) ? 1 : 0;

            switch (knownColor) {
            case(kKnownColorTransparent):
                  colorId = (ColorId)(249 - offset);
                  break;
            case (kKnownColorBrightWhite):
                  colorId = (ColorId)(251 - offset);
                  break;
            case (kKnownColorWhite):
                  colorId = (ColorId)(251 - offset);
                  break;
            case (kKnownColorBlack):
                  colorId = (ColorId)(249 - offset);
                  break;
            case (kKnownColorVerbText):
                  colorId = (ColorId)(253 - offset);
                  break;
            case (kKnownColorVerbTextShadow):
                  colorId = (ColorId)(15 - offset);
                  break;
            case (kKnownColorVerbTextActive):
                  colorId = (ColorId)(252 - offset);
                  break;

            default:
                  error("SagaEngine::KnownColor2ColorId unknown color %i", knownColor);
            }
      }
      return colorId;
}

void SagaEngine::setTalkspeed(int talkspeed) {
      ConfMan.setInt("talkspeed", (talkspeed * 255 + 3 / 2) / 3);
}

int SagaEngine::getTalkspeed() {
      return (ConfMan.getInt("talkspeed") * 3 + 255 / 2) / 255;
}

} // End of namespace Saga

Generated by  Doxygen 1.6.0   Back to index