scummvm/saga/saga.cpp

441 lines
11 KiB
C++
Raw Normal View History

/* ScummVM - Scumm Interpreter
* Copyright (C) 2004-2005 The ScummVM project
*
* The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* $Header$
*
*/
#include "common/stdafx.h"
2004-03-14 23:39:41 +00:00
#include "base/gameDetector.h"
#include "base/plugins.h"
#include "backends/fs/fs.h"
#include "common/file.h"
#include "common/config-manager.h"
#include "common/system.h"
#include "sound/mixer.h"
2004-03-14 23:39:41 +00:00
2004-08-02 16:20:35 +00:00
#include "saga/saga.h"
#include "saga/rscfile.h"
2004-08-02 16:20:35 +00:00
#include "saga/gfx.h"
#include "saga/render.h"
#include "saga/actor.h"
#include "saga/animation.h"
2004-08-10 18:31:33 +00:00
#include "saga/console.h"
#include "saga/events.h"
2004-08-03 00:06:18 +00:00
#include "saga/font.h"
#include "saga/interface.h"
2004-08-02 16:20:35 +00:00
#include "saga/isomap.h"
#include "saga/puzzle.h"
2004-08-02 16:20:35 +00:00
#include "saga/script.h"
2004-08-04 20:28:57 +00:00
#include "saga/scene.h"
2004-08-02 16:20:35 +00:00
#include "saga/sndres.h"
2004-08-03 01:07:34 +00:00
#include "saga/sprite.h"
2004-08-02 16:20:35 +00:00
#include "saga/sound.h"
#include "saga/music.h"
#include "saga/palanim.h"
#include "saga/objectmap.h"
#include "saga/resnames.h"
2004-03-14 23:39:41 +00:00
static const GameSettings saga_games[] = {
{"ite", "Inherit the Earth", 0},
{"ihnm", "I Have No Mouth and I Must Scream", GF_DEFAULT_TO_1X_SCALER },
{0, 0, 0}
};
2004-03-14 23:39:41 +00:00
GameList Engine_SAGA_gameList() {
GameList games;
const GameSettings *g = saga_games;
while (g->name) {
games.push_back(*g);
g++;
}
return games;
2004-03-14 23:39:41 +00:00
}
DetectedGameList Engine_SAGA_detectGames(const FSList &fslist) {
return Saga::GAME_ProbeGame(fslist);
2004-03-14 23:39:41 +00:00
}
Engine *Engine_SAGA_create(GameDetector *detector, OSystem *syst) {
return new Saga::SagaEngine(detector, syst);
2004-03-14 23:39:41 +00:00
}
REGISTER_PLUGIN(SAGA, "SAGA Engine")
2004-03-14 23:39:41 +00:00
namespace Saga {
#define MAX_TIME_DELTA 100
2004-03-14 23:39:41 +00:00
SagaEngine::SagaEngine(GameDetector *detector, OSystem *syst)
2005-05-23 18:53:36 +00:00
: Engine(syst),
_targetName(detector->_targetName) {
2004-03-14 23:39:41 +00:00
_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;
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/");
2004-03-14 23:39:41 +00:00
// 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/");
2004-03-14 23:39:41 +00:00
// Setup mixer
if (!_mixer->isReady()) {
warning("Sound initialization failed.");
}
_displayClip.left = _displayClip.top = 0;
2004-03-14 23:39:41 +00:00
}
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;
2004-03-14 23:39:41 +00:00
}
void SagaEngine::errorString(const char *buf1, char *buf2) {
strcpy(buf2, buf1);
}
int SagaEngine::init(GameDetector &detector) {
_soundVolume = ConfMan.getInt("sfx_volume") / 25;
_musicVolume = ConfMan.getInt("music_volume") / 25;
_subtitlesEnabled = ConfMan.getBool("subtitles");
_readingSpeed = ConfMan.getInt("talkspeed");
if (_readingSpeed > 3)
_readingSpeed = 0;
_resource = new Resource(this);
// Add some default directories
// Win32 demo & full game
Common::File::addDefaultDirectory("graphics");
Common::File::addDefaultDirectory("music");
Common::File::addDefaultDirectory("sound");
// Linux demo
Common::File::addDefaultDirectory("itedata");
// Mac demos & full game
Common::File::addDefaultDirectory("patch");
2004-05-01 13:19:15 +00:00
// Process command line
2004-05-01 13:19:15 +00:00
// Detect game and open resource files
if (!initGame()) {
return FAILURE;
}
2004-05-01 13:19:15 +00:00
// Initialize engine modules
2004-04-28 23:54:40 +00:00
_sndRes = new SndRes(this);
_events = new Events(this);
2004-08-03 00:06:18 +00:00
_font = new Font(this);
2004-08-03 01:07:34 +00:00
_sprite = new Sprite(this);
_anim = new Anim(this);
_script = new Script(this);
_interface = new Interface(this); // requires script module
_scene = new Scene(this);
2004-08-02 15:47:42 +00:00
_actor = new Actor(this);
_palanim = new PalAnim(this);
_isoMap = new IsoMap(this);
_puzzle = new Puzzle(this);
2004-05-01 13:19:15 +00:00
// System initialization
_previousTicks = _system->getMillis();
// Initialize graphics
_gfx = new Gfx(this, _system, getDisplayWidth(), getDisplayHeight(), detector);
// Graphics driver should be initialized before console
_console = new Console(this);
// Graphics should be initialized before music
int midiDriver = MidiDriver::detectMusicDriver(MDT_NATIVE | MDT_ADLIB | MDT_PREFER_NATIVE);
bool native_mt32 = (ConfMan.getBool("native_mt32") || (midiDriver == MD_MT32));
bool adlib = false;
MidiDriver *driver = MidiDriver::createMidi(midiDriver);
if (!driver) {
driver = MidiDriver_ADLIB_create(_mixer);
adlib = true;
} else 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.");
}
2004-03-14 23:39:41 +00:00
_render = new Render(this, _system);
2004-07-31 23:00:48 +00:00
if (!_render->initialized()) {
return FAILURE;
}
2004-05-01 13:19:15 +00:00
// Initialize system specific sound
_sound = new Sound(this, _mixer, _soundVolume);
if (!_soundVolume) {
debug(1, "Sound disabled.");
}
_interface->converseInit();
_script->setVerb(kVerbWalkTo);
_music->setVolume(-1, 1);
_gfx->initPalette();
return SUCCESS;
}
int SagaEngine::go() {
int msec = 0;
_previousTicks = _system->getMillis();
2005-07-29 17:58:00 +00:00
if (ConfMan.hasKey("start_scene")) {
_scene->changeScene(ConfMan.getInt("start_scene"), 0, kTransitionNoFade);
2005-08-04 10:48:54 +00:00
} else if (ConfMan.hasKey("boot_param")) {
if (getGameType() == GType_ITE)
_interface->addToInventory(_actor->objIndexToId(ITE_OBJ_MAGIC_HAT));
2005-08-04 10:48:54 +00:00
_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
char *fileName;
fileName = calcSaveFileName(ConfMan.getInt("save_slot"));
load(fileName);
_interface->setMode(kPanelMain);
} else {
_framesEsc = 0;
_scene->startScene();
}
uint32 currentTicks;
while (!_quit) {
if (_console->isAttached())
_console->onFrame();
2004-07-31 23:00:48 +00:00
if (_render->getFlags() & RF_RENDERPAUSE) {
2004-05-01 13:19:15 +00:00
// 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 is actorless, we do it here
if (_puzzle->isActive()) {
_actor->handleSpeech(msec);
} else if (!_scene->isInIntro()) {
if (_interface->getMode() == kPanelMain ||
_interface->getMode() == kPanelConverse ||
_interface->getMode() == kPanelCutaway ||
_interface->getMode() == kPanelNull ||
_interface->getMode() == kPanelChapterSelection)
2005-01-06 15:29:17 +00:00
_actor->direct(msec);
}
_events->handleEvents(msec);
_script->executeThreads(msec);
}
2004-05-01 13:19:15 +00:00
// Per frame processing
2004-07-31 23:00:48 +00:00
_render->drawScene();
_system->delayMillis(10);
2004-05-01 13:19:15 +00:00
}
2005-07-29 17:58:00 +00:00
return 0;
}
void SagaEngine::loadStrings(StringsTable &stringsTable, const byte *stringsPointer, size_t stringsLength) {
uint16 stringsCount;
size_t offset;
int i;
2005-07-29 17:58:00 +00:00
if (stringsLength == 0) {
error("SagaEngine::loadStrings() Error loading strings list resource");
}
stringsTable.stringsPointer = (byte*)malloc(stringsLength);
memcpy(stringsTable.stringsPointer, stringsPointer, stringsLength);
2005-07-29 17:58:00 +00:00
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));
2005-07-29 17:58:00 +00:00
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) {
error("SagaEngine::loadStrings wrong strings table");
}
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;
switch (objectTypeId(objectId)) {
2005-03-07 11:49:59 +00:00
case kGameObjectObject:
obj = _actor->getObj(objectId);
if (getGameType() == GType_ITE)
return _script->_mainStrings.getString(obj->_nameIndex);
return _actor->_objectsStrings.getString(obj->_nameIndex);
2005-07-29 17:58:00 +00:00
case kGameObjectActor:
actor = _actor->getActor(objectId);
return _actor->_actorsStrings.getString(actor->_nameIndex);
2005-03-07 11:49:59 +00:00
case kGameObjectHitZone:
hitZone = _scene->_objectMap->getHitZone(objectIdToIndex(objectId));
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 = (getLanguage() == Common::DE_DEU) ? 1 : 0;
string = ITEinterfaceTextStrings[lang][textStringId];
if (!string)
string = ITEinterfaceTextStrings[0][textStringId];
return string;
}
void SagaEngine::getExcuseInfo(int verb, const char *&textString, int &soundResourceId) {
textString = NULL;
switch (verb) {
2005-03-07 11:49:59 +00:00
case kVerbPickUp:
textString = getTextString(kTextICantPickup);
2005-03-07 11:49:59 +00:00
soundResourceId = RID_BOAR_VOICE_007;
break;
2005-07-29 17:58:00 +00:00
case kVerbLookAt:
textString = getTextString(kTextNothingSpecial);
2005-03-07 11:49:59 +00:00
soundResourceId = RID_BOAR_VOICE_006;
break;
2005-07-29 17:58:00 +00:00
case kVerbOpen:
textString = getTextString(kTextNoPlaceToOpen);
2005-03-07 11:49:59 +00:00
soundResourceId = RID_BOAR_VOICE_000;
break;
2005-07-29 17:58:00 +00:00
case kVerbClose:
textString = getTextString(kTextNoOpening);
2005-03-07 11:49:59 +00:00
soundResourceId = RID_BOAR_VOICE_002;
break;
2005-07-29 17:58:00 +00:00
case kVerbUse:
textString = getTextString(kTextDontKnow);
2005-03-07 11:49:59 +00:00
soundResourceId = RID_BOAR_VOICE_005;
break;
}
}
} // End of namespace Saga