2013-10-30 02:28:07 +00:00
|
|
|
/* 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.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "common/scummsys.h"
|
2013-11-04 11:28:10 +00:00
|
|
|
|
2013-10-30 02:28:07 +00:00
|
|
|
#include "common/config-manager.h"
|
|
|
|
#include "common/debug-channels.h"
|
|
|
|
#include "common/debug.h"
|
|
|
|
#include "common/events.h"
|
|
|
|
#include "common/file.h"
|
|
|
|
#include "common/random.h"
|
|
|
|
#include "common/fs.h"
|
|
|
|
#include "common/keyboard.h"
|
|
|
|
#include "common/substream.h"
|
2013-12-03 14:46:24 +00:00
|
|
|
#include "common/str.h"
|
2013-10-30 02:28:07 +00:00
|
|
|
|
|
|
|
#include "graphics/cursorman.h"
|
|
|
|
#include "graphics/surface.h"
|
|
|
|
#include "graphics/palette.h"
|
|
|
|
#include "graphics/pixelformat.h"
|
|
|
|
|
|
|
|
#include "engines/util.h"
|
|
|
|
#include "engines/advancedDetector.h"
|
|
|
|
|
|
|
|
#include "audio/audiostream.h"
|
|
|
|
|
|
|
|
#include "prince/prince.h"
|
|
|
|
#include "prince/font.h"
|
|
|
|
#include "prince/graphics.h"
|
|
|
|
#include "prince/script.h"
|
|
|
|
#include "prince/debugger.h"
|
|
|
|
#include "prince/object.h"
|
|
|
|
#include "prince/mob.h"
|
|
|
|
#include "prince/sound.h"
|
|
|
|
#include "prince/variatxt.h"
|
2013-11-04 11:28:10 +00:00
|
|
|
#include "prince/flags.h"
|
2013-11-05 21:20:46 +00:00
|
|
|
#include "prince/font.h"
|
|
|
|
#include "prince/mhwanh.h"
|
|
|
|
#include "prince/cursor.h"
|
2013-11-09 23:07:40 +00:00
|
|
|
#include "prince/archive.h"
|
2013-12-03 14:46:24 +00:00
|
|
|
#include "prince/hero.h"
|
2013-12-05 00:02:31 +00:00
|
|
|
#include "prince/resource.h"
|
2014-04-04 16:16:15 +02:00
|
|
|
#include "prince/animation.h"
|
2013-10-30 02:28:07 +00:00
|
|
|
|
|
|
|
namespace Prince {
|
|
|
|
|
2013-11-05 18:45:40 +00:00
|
|
|
void PrinceEngine::debugEngine(const char *s, ...) {
|
|
|
|
char buf[STRINGBUFLEN];
|
|
|
|
va_list va;
|
|
|
|
|
2014-05-07 21:55:25 +02:00
|
|
|
va_start(va, s);
|
|
|
|
vsnprintf(buf, STRINGBUFLEN, s, va);
|
|
|
|
va_end(va);
|
2013-11-05 18:45:40 +00:00
|
|
|
|
|
|
|
debug("Prince::Engine frame %08ld %s", _frameNr, buf);
|
|
|
|
}
|
2013-10-30 02:28:07 +00:00
|
|
|
|
|
|
|
PrinceEngine::PrinceEngine(OSystem *syst, const PrinceGameDescription *gameDesc) :
|
2013-12-10 00:26:42 +00:00
|
|
|
Engine(syst), _gameDescription(gameDesc), _graph(nullptr), _script(nullptr), _interpreter(nullptr), _flags(nullptr),
|
|
|
|
_locationNr(0), _debugger(nullptr), _midiPlayer(nullptr),
|
|
|
|
_cameraX(0), _newCameraX(0), _frameNr(0), _cursor1(nullptr), _cursor2(nullptr), _font(nullptr),
|
2014-05-07 21:55:25 +02:00
|
|
|
_suitcaseBmp(nullptr), _roomBmp(nullptr), _cursorNr(0), _picWindowX(0), _picWindowY(0) {
|
2013-11-02 01:29:26 +00:00
|
|
|
|
|
|
|
// Debug/console setup
|
|
|
|
DebugMan.addDebugChannel(DebugChannel::kScript, "script", "Prince Script debug channel");
|
|
|
|
DebugMan.addDebugChannel(DebugChannel::kEngine, "engine", "Prince Engine debug channel");
|
|
|
|
|
|
|
|
DebugMan.enableDebugChannel("script");
|
|
|
|
|
2013-12-03 14:46:24 +00:00
|
|
|
memset(_voiceStream, 0, sizeof(_voiceStream));
|
|
|
|
|
2013-11-04 11:28:10 +00:00
|
|
|
gDebugLevel = 10;
|
2013-10-30 02:28:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
PrinceEngine::~PrinceEngine() {
|
|
|
|
DebugMan.clearAllDebugChannels();
|
|
|
|
|
|
|
|
delete _rnd;
|
|
|
|
delete _debugger;
|
2013-11-05 21:20:46 +00:00
|
|
|
delete _cursor1;
|
|
|
|
delete _cursor2;
|
2013-10-30 02:28:07 +00:00
|
|
|
delete _midiPlayer;
|
2013-11-05 21:20:46 +00:00
|
|
|
delete _script;
|
2013-12-10 00:26:42 +00:00
|
|
|
delete _flags;
|
|
|
|
delete _interpreter;
|
2013-11-05 21:20:46 +00:00
|
|
|
delete _font;
|
|
|
|
delete _roomBmp;
|
2014-05-07 21:55:25 +02:00
|
|
|
delete _suitcaseBmp;
|
2013-11-08 23:17:20 +00:00
|
|
|
delete _variaTxt;
|
|
|
|
delete[] _talkTxt;
|
|
|
|
delete _graph;
|
2013-12-05 00:02:31 +00:00
|
|
|
delete _mainHero;
|
|
|
|
delete _secondHero;
|
2013-11-18 16:19:21 +00:00
|
|
|
|
2014-05-07 21:55:25 +02:00
|
|
|
for (uint i = 0; i < _objList.size(); ++i) {
|
2013-11-18 16:19:21 +00:00
|
|
|
delete _objList[i];
|
|
|
|
}
|
|
|
|
_objList.clear();
|
2013-10-30 02:28:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GUI::Debugger *PrinceEngine::getDebugger() {
|
|
|
|
return _debugger;
|
|
|
|
}
|
|
|
|
|
2013-11-09 23:07:40 +00:00
|
|
|
void PrinceEngine::init() {
|
2013-11-08 23:17:20 +00:00
|
|
|
|
2013-10-30 02:28:07 +00:00
|
|
|
const Common::FSNode gameDataDir(ConfMan.get("path"));
|
|
|
|
|
2013-11-05 18:45:40 +00:00
|
|
|
debugEngine("Adding all path: %s", gameDataDir.getPath().c_str());
|
2013-10-30 02:28:07 +00:00
|
|
|
|
2013-11-09 23:07:40 +00:00
|
|
|
PtcArchive *all = new PtcArchive();
|
|
|
|
if (!all->open("all/databank.ptc"))
|
|
|
|
error("Can't open all/databank.ptc");
|
|
|
|
|
|
|
|
PtcArchive *voices = new PtcArchive();
|
|
|
|
if (!voices->open("data/voices/databank.ptc"))
|
|
|
|
error("Can't open data/voices/databank.ptc");
|
|
|
|
|
2013-12-03 14:46:24 +00:00
|
|
|
PtcArchive *sound = new PtcArchive();
|
|
|
|
if (!sound->open("sound/databank.ptc"))
|
|
|
|
error("Can't open sound/databank.ptc");
|
|
|
|
|
2014-03-31 22:56:01 +02:00
|
|
|
SearchMan.addSubDirectoryMatching(gameDataDir, "all");
|
|
|
|
|
2013-11-09 23:07:40 +00:00
|
|
|
SearchMan.add("all", all);
|
2014-03-31 22:56:01 +02:00
|
|
|
SearchMan.add("voices", voices);
|
2013-12-03 14:46:24 +00:00
|
|
|
SearchMan.add("sound", sound);
|
2013-11-09 23:07:40 +00:00
|
|
|
|
|
|
|
_graph = new GraphicsMan(this);
|
|
|
|
|
|
|
|
_rnd = new Common::RandomSource("prince");
|
|
|
|
_debugger = new Debugger(this);
|
|
|
|
|
|
|
|
_midiPlayer = new MusicPlayer(this);
|
2013-11-05 21:20:46 +00:00
|
|
|
|
|
|
|
_font = new Font();
|
2014-03-31 19:28:54 +02:00
|
|
|
Resource::loadResource(_font, "font1.raw");
|
2013-11-18 16:19:21 +00:00
|
|
|
|
2014-05-07 21:55:25 +02:00
|
|
|
_suitcaseBmp = new MhwanhDecoder();
|
|
|
|
Resource::loadResource(_suitcaseBmp, "walizka");
|
2013-10-30 02:28:07 +00:00
|
|
|
|
2013-12-10 00:26:42 +00:00
|
|
|
_script = new Script();
|
2014-03-31 19:28:54 +02:00
|
|
|
Resource::loadResource(_script, "skrypt.dat");
|
2013-10-30 02:28:07 +00:00
|
|
|
|
2013-12-10 00:26:42 +00:00
|
|
|
_flags = new InterpreterFlags();
|
|
|
|
_interpreter = new Interpreter(this, _script, _flags);
|
|
|
|
|
2013-10-30 02:28:07 +00:00
|
|
|
_variaTxt = new VariaTxt();
|
2014-03-31 19:28:54 +02:00
|
|
|
Resource::loadResource(_variaTxt, "variatxt.dat");
|
2013-11-05 21:20:46 +00:00
|
|
|
|
|
|
|
_cursor1 = new Cursor();
|
2014-03-31 19:28:54 +02:00
|
|
|
Resource::loadResource(_cursor1, "mouse1.cur");
|
2013-11-05 21:20:46 +00:00
|
|
|
|
|
|
|
_cursor2 = new Cursor();
|
2014-03-31 19:28:54 +02:00
|
|
|
Resource::loadResource(_cursor2, "mouse2.cur");
|
2013-10-30 02:28:07 +00:00
|
|
|
|
2014-03-31 19:28:54 +02:00
|
|
|
Common::SeekableReadStream *talkTxtStream = SearchMan.createReadStreamForMember("talktxt.dat");
|
2013-10-30 02:28:07 +00:00
|
|
|
if (!talkTxtStream) {
|
|
|
|
error("Can't load talkTxtStream");
|
2013-11-08 23:17:20 +00:00
|
|
|
return;
|
2013-10-30 02:28:07 +00:00
|
|
|
}
|
|
|
|
_talkTxtSize = talkTxtStream->size();
|
|
|
|
_talkTxt = new byte[_talkTxtSize];
|
|
|
|
talkTxtStream->read(_talkTxt, _talkTxtSize);
|
|
|
|
|
|
|
|
delete talkTxtStream;
|
2013-11-18 16:19:21 +00:00
|
|
|
|
2014-03-29 22:23:20 +01:00
|
|
|
_roomBmp = new Image::BitmapDecoder();
|
2013-12-05 00:02:31 +00:00
|
|
|
|
2014-05-04 18:02:53 +02:00
|
|
|
_mainHero = new Hero(this);
|
|
|
|
_secondHero = new Hero(this);
|
2013-12-05 00:02:31 +00:00
|
|
|
|
|
|
|
_mainHero->loadAnimSet(0);
|
2013-11-08 23:17:20 +00:00
|
|
|
}
|
2013-10-30 02:28:07 +00:00
|
|
|
|
2013-11-08 23:17:20 +00:00
|
|
|
void PrinceEngine::showLogo() {
|
|
|
|
MhwanhDecoder logo;
|
2013-12-05 00:02:31 +00:00
|
|
|
if (Resource::loadResource(&logo, "logo.raw")) {
|
2013-11-08 23:17:20 +00:00
|
|
|
_graph->setPalette(logo.getPalette());
|
|
|
|
_graph->draw(0, 0, logo.getSurface());
|
2013-10-30 02:28:07 +00:00
|
|
|
_graph->update();
|
|
|
|
_system->delayMillis(700);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-08 23:17:20 +00:00
|
|
|
Common::Error PrinceEngine::run() {
|
2013-10-30 02:28:07 +00:00
|
|
|
|
2013-11-08 23:17:20 +00:00
|
|
|
init();
|
2013-10-30 02:28:07 +00:00
|
|
|
|
2013-11-08 23:17:20 +00:00
|
|
|
showLogo();
|
2013-10-30 02:28:07 +00:00
|
|
|
|
2013-11-08 23:17:20 +00:00
|
|
|
mainLoop();
|
2013-10-30 02:28:07 +00:00
|
|
|
|
2013-11-08 23:17:20 +00:00
|
|
|
return Common::kNoError;
|
2013-10-30 02:28:07 +00:00
|
|
|
}
|
|
|
|
|
2013-12-05 00:02:31 +00:00
|
|
|
bool AnimListItem::loadFromStream(Common::SeekableReadStream &stream) {
|
|
|
|
int32 pos = stream.pos();
|
|
|
|
|
|
|
|
uint16 type = stream.readUint16LE();
|
|
|
|
if (type == 0xFFFF) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
_type = type;
|
|
|
|
_fileNumber = stream.readUint16LE();
|
|
|
|
_startPhase = stream.readUint16LE();
|
|
|
|
_endPhase = stream.readUint16LE();
|
|
|
|
_loopPhase = stream.readUint16LE();
|
|
|
|
_x = stream.readSint16LE();
|
|
|
|
_y = stream.readSint16LE();
|
|
|
|
_loopType = stream.readUint16LE();
|
|
|
|
_nextAnim = stream.readUint16LE();
|
|
|
|
_flags = stream.readUint16LE();
|
|
|
|
|
|
|
|
debug("AnimListItem type %d, fileNumber %d, x %d, y %d, flags %d", _type, _fileNumber, _x, _y, _flags);
|
|
|
|
|
|
|
|
|
|
|
|
// 32 byte aligment
|
|
|
|
stream.seek(pos + 32);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-10-30 02:28:07 +00:00
|
|
|
bool PrinceEngine::loadLocation(uint16 locationNr) {
|
2013-12-03 14:46:24 +00:00
|
|
|
_flicPlayer.close();
|
|
|
|
|
|
|
|
memset(_textSlots, 0, sizeof(_textSlots));
|
2014-05-07 21:55:25 +02:00
|
|
|
for(uint32 sampleId = 0; sampleId < MAX_SAMPLES; sampleId++) {
|
2013-12-03 14:46:24 +00:00
|
|
|
stopSample(sampleId);
|
|
|
|
}
|
|
|
|
|
2013-11-05 18:45:40 +00:00
|
|
|
debugEngine("PrinceEngine::loadLocation %d", locationNr);
|
2013-10-30 02:28:07 +00:00
|
|
|
const Common::FSNode gameDataDir(ConfMan.get("path"));
|
|
|
|
SearchMan.remove(Common::String::format("%02d", _locationNr));
|
2013-11-18 16:19:21 +00:00
|
|
|
|
2013-10-30 02:28:07 +00:00
|
|
|
_locationNr = locationNr;
|
2013-11-18 16:19:21 +00:00
|
|
|
_debugger->_locationNr = locationNr;
|
|
|
|
_cameraX = 0;
|
|
|
|
_newCameraX = 0;
|
|
|
|
|
2013-12-10 00:26:42 +00:00
|
|
|
_flags->setFlagValue(Flags::CURRROOM, _locationNr);
|
|
|
|
_interpreter->stopBg();
|
2013-11-18 16:19:21 +00:00
|
|
|
|
|
|
|
changeCursor(0);
|
2013-10-30 02:28:07 +00:00
|
|
|
|
|
|
|
const Common::String locationNrStr = Common::String::format("%02d", _locationNr);
|
2013-11-05 18:45:40 +00:00
|
|
|
debugEngine("loadLocation %s", locationNrStr.c_str());
|
2013-11-09 23:07:40 +00:00
|
|
|
|
|
|
|
PtcArchive *locationArchive = new PtcArchive();
|
|
|
|
if (!locationArchive->open(locationNrStr + "/databank.ptc"))
|
|
|
|
error("Can't open location %s", locationNrStr.c_str());
|
|
|
|
|
|
|
|
SearchMan.add(locationNrStr, locationArchive);
|
2013-10-30 02:28:07 +00:00
|
|
|
|
2013-11-18 16:19:21 +00:00
|
|
|
const char *musName = MusicPlayer::_musTable[MusicPlayer::_musRoomTable[locationNr]];
|
|
|
|
_midiPlayer->loadMidi(musName);
|
|
|
|
|
|
|
|
// load location background, replace old one
|
2013-12-05 00:02:31 +00:00
|
|
|
Resource::loadResource(_roomBmp, "room");
|
2013-11-05 21:20:46 +00:00
|
|
|
if (_roomBmp->getSurface()) {
|
|
|
|
_sceneWidth = _roomBmp->getSurface()->w;
|
2013-10-30 02:28:07 +00:00
|
|
|
}
|
|
|
|
|
2014-04-04 16:16:15 +02:00
|
|
|
_mainHero->_zoomBitmap->clear();
|
|
|
|
Resource::loadResource(_mainHero->_zoomBitmap, "zoom", false);
|
|
|
|
|
2014-04-30 15:12:07 +02:00
|
|
|
_mainHero->_shadowBitmap->clear();
|
2014-05-07 21:55:25 +02:00
|
|
|
if (Resource::loadResource(_mainHero->_shadowBitmap, "shadow", false) == false) {
|
2014-04-30 15:12:07 +02:00
|
|
|
Resource::loadResource(_mainHero->_shadowBitmap, "shadow2", false);
|
|
|
|
}
|
|
|
|
|
2014-05-04 18:02:53 +02:00
|
|
|
_picWindowX = 0;
|
|
|
|
|
2014-05-01 13:14:06 +02:00
|
|
|
_mainHero->_lightX = _script->getLightX(_locationNr);
|
|
|
|
_mainHero->_lightY = _script->getLightY(_locationNr);
|
2014-05-02 22:31:18 +02:00
|
|
|
_mainHero->setShadowScale(_script->getShadowScale(_locationNr));
|
2014-05-01 13:14:06 +02:00
|
|
|
debug("lightX: %d", _mainHero->_lightX);
|
2014-05-01 16:40:12 +02:00
|
|
|
debug("lightY: %d", _mainHero->_lightY);
|
2014-05-01 13:14:06 +02:00
|
|
|
|
2013-11-09 23:07:40 +00:00
|
|
|
_mobList.clear();
|
2013-12-05 00:02:31 +00:00
|
|
|
Resource::loadResource(_mobList, "mob.lst", false);
|
2013-10-30 02:28:07 +00:00
|
|
|
|
2014-05-07 21:55:25 +02:00
|
|
|
for (uint32 i = 0; i < _objList.size(); i++) {
|
2013-11-18 16:19:21 +00:00
|
|
|
delete _objList[i];
|
|
|
|
}
|
|
|
|
_objList.clear();
|
2013-12-05 00:02:31 +00:00
|
|
|
Resource::loadResource(_objList, "obj.lst", false);
|
|
|
|
|
|
|
|
_animList.clear();
|
|
|
|
Resource::loadResource(_animList, "anim.lst", false);
|
2013-10-30 02:28:07 +00:00
|
|
|
|
2014-05-02 22:31:18 +02:00
|
|
|
_graph->makeShadowTable(70, _graph->_shadowTable70);
|
|
|
|
_graph->makeShadowTable(50, _graph->_shadowTable50);
|
|
|
|
|
2013-10-30 02:28:07 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-11-11 12:00:19 +00:00
|
|
|
void PrinceEngine::changeCursor(uint16 curId) {
|
|
|
|
_debugger->_cursorNr = curId;
|
|
|
|
|
2013-12-10 00:26:42 +00:00
|
|
|
const Graphics::Surface *curSurface = nullptr;
|
2013-10-30 02:28:07 +00:00
|
|
|
|
|
|
|
uint16 hotspotX = 0;
|
|
|
|
uint16 hotspotY = 0;
|
|
|
|
|
|
|
|
switch(curId) {
|
2014-05-07 21:55:25 +02:00
|
|
|
case 0:
|
|
|
|
CursorMan.showMouse(false);
|
|
|
|
return;
|
|
|
|
case 1:
|
|
|
|
curSurface = _cursor1->getSurface();
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
curSurface = _cursor2->getSurface();
|
|
|
|
hotspotX = curSurface->w >> 1;
|
|
|
|
hotspotY = curSurface->h >> 1;
|
|
|
|
break;
|
2013-10-30 02:28:07 +00:00
|
|
|
}
|
|
|
|
|
2013-11-05 21:20:46 +00:00
|
|
|
CursorMan.replaceCursorPalette(_roomBmp->getPalette(), 0, 255);
|
2013-10-30 02:28:07 +00:00
|
|
|
CursorMan.replaceCursor(
|
|
|
|
curSurface->getBasePtr(0, 0),
|
|
|
|
curSurface->w, curSurface->h,
|
|
|
|
hotspotX, hotspotY,
|
|
|
|
255, false,
|
|
|
|
&curSurface->format
|
|
|
|
);
|
|
|
|
CursorMan.showMouse(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool PrinceEngine::playNextFrame() {
|
|
|
|
if (!_flicPlayer.isVideoLoaded())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
const Graphics::Surface *s = _flicPlayer.decodeNextFrame();
|
|
|
|
if (s) {
|
2013-12-05 00:02:31 +00:00
|
|
|
_graph->drawTransparent(0, 0, s);
|
2013-10-30 02:28:07 +00:00
|
|
|
_graph->change();
|
|
|
|
} else if (_flicLooped) {
|
2014-05-07 21:55:25 +02:00
|
|
|
_flicPlayer.rewind();
|
|
|
|
playNextFrame();
|
|
|
|
}
|
2013-10-30 02:28:07 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-11-05 22:15:55 +00:00
|
|
|
void PrinceEngine::playSample(uint16 sampleId, uint16 loopType) {
|
2013-12-03 14:46:24 +00:00
|
|
|
if (_voiceStream[sampleId]) {
|
2013-11-05 22:15:55 +00:00
|
|
|
|
2013-12-03 14:46:24 +00:00
|
|
|
if (_mixer->isSoundIDActive(sampleId)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
Audio::AudioStream *audioStream = Audio::makeWAVStream(_voiceStream[sampleId], DisposeAfterUse::YES);
|
|
|
|
if (loopType) {
|
|
|
|
audioStream = new Audio::LoopingAudioStream((Audio::RewindableAudioStream*)audioStream, 0, DisposeAfterUse::NO);
|
|
|
|
}
|
|
|
|
_mixer->playStream(Audio::Mixer::kSFXSoundType, &_soundHandle[sampleId], audioStream, sampleId);
|
2013-11-05 22:15:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PrinceEngine::stopSample(uint16 sampleId) {
|
|
|
|
_mixer->stopID(sampleId);
|
2013-12-10 00:26:42 +00:00
|
|
|
_voiceStream[sampleId] = nullptr;
|
2013-12-03 14:46:24 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool PrinceEngine::loadSample(uint32 sampleSlot, const Common::String &streamName) {
|
|
|
|
// FIXME: This is just a workaround streamName is a path
|
|
|
|
// SOUND\\SCIERKA1.WAV for now only last path component is used
|
|
|
|
Common::String normalizedPath = lastPathComponent(streamName, '\\');
|
|
|
|
|
|
|
|
debugEngine("loadSample slot %d, name %s", sampleSlot, normalizedPath.c_str());
|
|
|
|
|
|
|
|
_mixer->stopID(sampleSlot);
|
2013-12-10 00:26:42 +00:00
|
|
|
_voiceStream[sampleSlot] = nullptr;
|
2013-12-03 14:46:24 +00:00
|
|
|
_voiceStream[sampleSlot] = SearchMan.createReadStreamForMember(normalizedPath);
|
2013-12-10 00:26:42 +00:00
|
|
|
if (_voiceStream[sampleSlot] == nullptr) {
|
2013-12-03 14:46:24 +00:00
|
|
|
error("Can't load sample %s to slot %d", normalizedPath.c_str(), sampleSlot);
|
|
|
|
}
|
2013-12-10 00:26:42 +00:00
|
|
|
return _voiceStream[sampleSlot] == nullptr;
|
2013-11-05 22:15:55 +00:00
|
|
|
}
|
|
|
|
|
2013-12-03 14:46:24 +00:00
|
|
|
bool PrinceEngine::loadVoice(uint32 slot, uint32 sampleSlot, const Common::String &streamName) {
|
2013-11-05 22:15:55 +00:00
|
|
|
debugEngine("Loading wav %s slot %d", streamName.c_str(), slot);
|
|
|
|
|
2013-11-10 22:49:34 +00:00
|
|
|
if (slot > MAXTEXTS) {
|
|
|
|
error("Text slot bigger than MAXTEXTS %d", MAXTEXTS);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-12-03 14:46:24 +00:00
|
|
|
_voiceStream[sampleSlot] = SearchMan.createReadStreamForMember(streamName);
|
|
|
|
if (!_voiceStream[sampleSlot]) {
|
2013-11-05 22:15:55 +00:00
|
|
|
error("Can't open %s", streamName.c_str());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-12-03 14:46:24 +00:00
|
|
|
uint32 id = _voiceStream[sampleSlot]->readUint32LE();
|
2014-05-07 21:55:25 +02:00
|
|
|
//if (id != 0x46464952) {
|
|
|
|
if (id != MKTAG('F', 'F', 'I', 'R')) {
|
2013-11-05 22:15:55 +00:00
|
|
|
error("It's not RIFF file %s", streamName.c_str());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-12-03 14:46:24 +00:00
|
|
|
_voiceStream[sampleSlot]->skip(0x20);
|
|
|
|
id = _voiceStream[sampleSlot]->readUint32LE();
|
2014-05-07 21:55:25 +02:00
|
|
|
//if (id != 0x61746164) {
|
|
|
|
if (id != MKTAG('a', 't', 'a', 'd')) {
|
2013-11-05 22:15:55 +00:00
|
|
|
error("No data section in %s id %04x", streamName.c_str(), id);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2013-12-03 14:46:24 +00:00
|
|
|
id = _voiceStream[sampleSlot]->readUint32LE();
|
2013-11-05 22:15:55 +00:00
|
|
|
debugEngine("SetVoice slot %d time %04x", slot, id);
|
|
|
|
id <<= 3;
|
|
|
|
id /= 22050;
|
|
|
|
id += 2;
|
|
|
|
|
|
|
|
_textSlots[slot]._time = id;
|
|
|
|
|
|
|
|
debugEngine("SetVoice slot %d time %04x", slot, id);
|
2013-12-03 14:46:24 +00:00
|
|
|
_voiceStream[sampleSlot]->seek(0);
|
2013-11-05 22:15:55 +00:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2013-10-30 02:28:07 +00:00
|
|
|
bool PrinceEngine::loadAnim(uint16 animNr, bool loop) {
|
|
|
|
Common::String streamName = Common::String::format("AN%02d", animNr);
|
|
|
|
Common::SeekableReadStream * flicStream = SearchMan.createReadStreamForMember(streamName);
|
|
|
|
|
|
|
|
if (!flicStream) {
|
|
|
|
error("Can't open %s", streamName.c_str());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!_flicPlayer.loadStream(flicStream)) {
|
|
|
|
error("Can't load flic stream %s", streamName.c_str());
|
|
|
|
}
|
|
|
|
|
2013-11-05 18:45:40 +00:00
|
|
|
debugEngine("%s loaded", streamName.c_str());
|
2014-05-07 21:55:25 +02:00
|
|
|
_flicLooped = loop;
|
2013-10-30 02:28:07 +00:00
|
|
|
_flicPlayer.start();
|
|
|
|
playNextFrame();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void PrinceEngine::scrollCameraLeft(int16 delta) {
|
|
|
|
if (_newCameraX > 0) {
|
|
|
|
if (_newCameraX < delta)
|
|
|
|
_newCameraX = 0;
|
|
|
|
else
|
|
|
|
_newCameraX -= delta;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PrinceEngine::scrollCameraRight(int16 delta) {
|
|
|
|
if (_newCameraX != _sceneWidth - 640) {
|
|
|
|
if (_sceneWidth - 640 < delta + _newCameraX)
|
|
|
|
delta += (_sceneWidth - 640) - (delta + _newCameraX);
|
|
|
|
_newCameraX += delta;
|
2013-11-05 18:45:40 +00:00
|
|
|
debugEngine("PrinceEngine::scrollCameraRight() _newCameraX = %d; delta = %d", _newCameraX, delta);
|
2013-10-30 02:28:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PrinceEngine::keyHandler(Common::Event event) {
|
|
|
|
uint16 nChar = event.kbd.keycode;
|
2013-11-04 11:28:10 +00:00
|
|
|
switch (nChar) {
|
|
|
|
case Common::KEYCODE_d:
|
|
|
|
if (event.kbd.hasFlags(Common::KBD_CTRL)) {
|
2013-10-30 02:28:07 +00:00
|
|
|
getDebugger()->attach();
|
|
|
|
}
|
2013-11-04 11:28:10 +00:00
|
|
|
break;
|
|
|
|
case Common::KEYCODE_LEFT:
|
|
|
|
scrollCameraLeft(32);
|
|
|
|
break;
|
|
|
|
case Common::KEYCODE_RIGHT:
|
|
|
|
scrollCameraRight(32);
|
|
|
|
break;
|
|
|
|
case Common::KEYCODE_ESCAPE:
|
2013-12-10 00:26:42 +00:00
|
|
|
_flags->setFlagValue(Flags::ESCAPED2, 1);
|
2013-11-04 11:28:10 +00:00
|
|
|
break;
|
2014-04-04 16:16:15 +02:00
|
|
|
case Common::KEYCODE_UP:
|
|
|
|
_mainHero->_phase++;
|
|
|
|
debugEngine("%d", _mainHero->_phase);
|
|
|
|
break;
|
|
|
|
case Common::KEYCODE_DOWN:
|
|
|
|
if(_mainHero->_phase > 0) {
|
|
|
|
_mainHero->_phase--;
|
|
|
|
}
|
|
|
|
debugEngine("%d", _mainHero->_phase);
|
|
|
|
break;
|
|
|
|
case Common::KEYCODE_w:
|
|
|
|
_mainHero->_lastDirection = _mainHero->UP;
|
|
|
|
debugEngine("UP");
|
|
|
|
break;
|
|
|
|
case Common::KEYCODE_s:
|
|
|
|
_mainHero->_lastDirection = _mainHero->DOWN;
|
|
|
|
debugEngine("DOWN");
|
|
|
|
break;
|
|
|
|
case Common::KEYCODE_a:
|
|
|
|
_mainHero->_lastDirection = _mainHero->LEFT;
|
|
|
|
debugEngine("LEFT");
|
|
|
|
break;
|
|
|
|
case Common::KEYCODE_f:
|
|
|
|
_mainHero->_lastDirection = _mainHero->RIGHT;
|
|
|
|
debugEngine("RIGHT");
|
|
|
|
break;
|
|
|
|
case Common::KEYCODE_1:
|
|
|
|
if(_mainHero->_state > 0) {
|
|
|
|
_mainHero->_state--;
|
|
|
|
}
|
|
|
|
debugEngine("%d", _mainHero->_state);
|
|
|
|
break;
|
|
|
|
case Common::KEYCODE_2:
|
|
|
|
_mainHero->_state++;
|
|
|
|
debugEngine("%d", _mainHero->_state);
|
|
|
|
break;
|
|
|
|
case Common::KEYCODE_i:
|
|
|
|
_mainHero->_middleY -= 10;
|
|
|
|
break;
|
|
|
|
case Common::KEYCODE_k:
|
|
|
|
_mainHero->_middleY += 10;
|
|
|
|
break;
|
|
|
|
case Common::KEYCODE_j:
|
|
|
|
_mainHero->_middleX -= 10;
|
|
|
|
break;
|
|
|
|
case Common::KEYCODE_l:
|
|
|
|
_mainHero->_middleX += 10;
|
|
|
|
break;
|
2013-10-30 02:28:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void PrinceEngine::hotspot() {
|
|
|
|
Common::Point mousepos = _system->getEventManager()->getMousePos();
|
|
|
|
Common::Point mousePosCamera(mousepos.x + _cameraX, mousepos.y);
|
|
|
|
|
2014-05-07 21:55:25 +02:00
|
|
|
for (Common::Array<Mob>::const_iterator it = _mobList.begin(); it != _mobList.end() ; it++) {
|
2013-11-09 23:07:40 +00:00
|
|
|
const Mob& mob = *it;
|
|
|
|
if (mob._visible)
|
2013-10-30 02:28:07 +00:00
|
|
|
continue;
|
2013-11-09 23:07:40 +00:00
|
|
|
if (mob._rect.contains(mousePosCamera)) {
|
2013-10-30 02:28:07 +00:00
|
|
|
uint16 textW = 0;
|
2013-11-09 23:07:40 +00:00
|
|
|
for (uint16 i = 0; i < mob._name.size(); ++i)
|
|
|
|
textW += _font->getCharWidth(mob._name[i]);
|
2013-10-30 02:28:07 +00:00
|
|
|
|
|
|
|
uint16 x = mousepos.x - textW/2;
|
|
|
|
if (x > _graph->_frontScreen->w)
|
|
|
|
x = 0;
|
|
|
|
|
|
|
|
if (x + textW > _graph->_frontScreen->w)
|
|
|
|
x = _graph->_frontScreen->w - textW;
|
|
|
|
|
2013-11-10 22:49:34 +00:00
|
|
|
uint16 y = mousepos.y - _font->getFontHeight();
|
|
|
|
if (y > _graph->_frontScreen->h)
|
|
|
|
y = _font->getFontHeight() - 2;
|
|
|
|
|
2013-11-05 21:20:46 +00:00
|
|
|
_font->drawString(
|
2013-10-30 02:28:07 +00:00
|
|
|
_graph->_frontScreen,
|
2013-11-09 23:07:40 +00:00
|
|
|
mob._name,
|
2013-10-30 02:28:07 +00:00
|
|
|
x,
|
2013-11-10 22:49:34 +00:00
|
|
|
y,
|
2013-10-30 02:28:07 +00:00
|
|
|
_graph->_frontScreen->w,
|
|
|
|
216
|
|
|
|
);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-02 01:29:26 +00:00
|
|
|
void PrinceEngine::printAt(uint32 slot, uint8 color, const char *s, uint16 x, uint16 y) {
|
|
|
|
|
|
|
|
debugC(1, DebugChannel::kEngine, "PrinceEngine::printAt slot %d, color %d, x %02d, y %02d, str %s", slot, color, x, y, s);
|
|
|
|
|
|
|
|
Text &text = _textSlots[slot];
|
|
|
|
text._str = s;
|
|
|
|
text._x = x;
|
|
|
|
text._y = y;
|
|
|
|
text._color = color;
|
2013-10-30 02:28:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
uint32 PrinceEngine::getTextWidth(const char *s) {
|
2014-05-07 21:55:25 +02:00
|
|
|
uint16 textW = 0;
|
2013-11-02 01:29:26 +00:00
|
|
|
while (*s) {
|
2014-05-07 21:55:25 +02:00
|
|
|
textW += _font->getCharWidth(*s) + _font->getKerningOffset(0, 0);
|
|
|
|
s++;
|
|
|
|
}
|
|
|
|
return textW;
|
2013-10-30 02:28:07 +00:00
|
|
|
}
|
|
|
|
|
2013-11-02 01:29:26 +00:00
|
|
|
void PrinceEngine::showTexts() {
|
|
|
|
for (uint32 slot = 0; slot < MAXTEXTS; ++slot) {
|
|
|
|
Text& text = _textSlots[slot];
|
|
|
|
if (!text._str && !text._time)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
Common::Array<Common::String> lines;
|
2013-11-05 21:20:46 +00:00
|
|
|
_font->wordWrapText(text._str, _graph->_frontScreen->w, lines);
|
2013-11-02 01:29:26 +00:00
|
|
|
|
2013-11-04 16:07:19 +00:00
|
|
|
for (uint8 i = 0; i < lines.size(); ++i) {
|
2013-11-05 21:20:46 +00:00
|
|
|
_font->drawString(
|
2013-11-02 01:29:26 +00:00
|
|
|
_graph->_frontScreen,
|
|
|
|
lines[i],
|
|
|
|
text._x - getTextWidth(lines[i].c_str())/2,
|
2013-11-05 21:20:46 +00:00
|
|
|
text._y - (lines.size() - i) * (_font->getFontHeight()),
|
2013-11-02 01:29:26 +00:00
|
|
|
_graph->_frontScreen->w,
|
|
|
|
text._color
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
--text._time;
|
2013-11-04 11:28:10 +00:00
|
|
|
if (text._time == 0) {
|
2013-12-10 00:26:42 +00:00
|
|
|
text._str = nullptr;
|
2013-11-04 11:28:10 +00:00
|
|
|
}
|
2013-11-02 01:29:26 +00:00
|
|
|
}
|
|
|
|
}
|
2013-10-30 02:28:07 +00:00
|
|
|
|
|
|
|
void PrinceEngine::drawScreen() {
|
2013-11-05 21:20:46 +00:00
|
|
|
const Graphics::Surface *roomSurface = _roomBmp->getSurface();
|
2013-10-30 02:28:07 +00:00
|
|
|
if (roomSurface) {
|
2013-11-05 21:20:46 +00:00
|
|
|
_graph->setPalette(_roomBmp->getPalette());
|
2014-05-04 18:02:53 +02:00
|
|
|
const Graphics::Surface visiblePart = roomSurface->getSubArea(Common::Rect(_picWindowX, 0, roomSurface->w, roomSurface->h));
|
2013-10-30 02:28:07 +00:00
|
|
|
_graph->draw(0, 0, &visiblePart);
|
|
|
|
}
|
|
|
|
|
2013-12-05 00:02:31 +00:00
|
|
|
if (_mainHero->_visible) {
|
|
|
|
const Graphics::Surface *mainHeroSurface = _mainHero->getSurface();
|
|
|
|
|
|
|
|
if (mainHeroSurface)
|
2014-04-04 16:16:15 +02:00
|
|
|
//_graph->drawTransparent(_mainHero->_middleX, _mainHero->_middleY, mainHeroSurface);
|
|
|
|
_graph->drawTransparent(_mainHero->_drawX, _mainHero->_drawY, mainHeroSurface);
|
2013-12-05 00:02:31 +00:00
|
|
|
}
|
|
|
|
|
2013-10-30 02:28:07 +00:00
|
|
|
playNextFrame();
|
|
|
|
|
|
|
|
//if (_objectList)
|
|
|
|
// _graph->drawTransparent(_objectList->getSurface());
|
2013-11-02 15:28:56 +00:00
|
|
|
|
2013-10-30 02:28:07 +00:00
|
|
|
hotspot();
|
|
|
|
|
2013-11-02 01:29:26 +00:00
|
|
|
showTexts();
|
2013-10-30 02:28:07 +00:00
|
|
|
|
|
|
|
getDebugger()->onFrame();
|
|
|
|
|
|
|
|
_graph->update();
|
|
|
|
}
|
|
|
|
|
|
|
|
void PrinceEngine::mainLoop() {
|
|
|
|
|
2013-11-14 14:44:24 +00:00
|
|
|
changeCursor(0);
|
2013-11-09 23:07:40 +00:00
|
|
|
|
2013-10-30 02:28:07 +00:00
|
|
|
while (!shouldQuit()) {
|
2013-11-02 02:02:53 +00:00
|
|
|
uint32 currentTime = _system->getMillis();
|
|
|
|
|
2013-10-30 02:28:07 +00:00
|
|
|
Common::Event event;
|
|
|
|
Common::EventManager *eventMan = _system->getEventManager();
|
|
|
|
while (eventMan->pollEvent(event)) {
|
|
|
|
switch (event.type) {
|
|
|
|
case Common::EVENT_KEYDOWN:
|
|
|
|
keyHandler(event);
|
|
|
|
break;
|
|
|
|
case Common::EVENT_KEYUP:
|
|
|
|
break;
|
|
|
|
case Common::EVENT_MOUSEMOVE:
|
|
|
|
break;
|
|
|
|
case Common::EVENT_LBUTTONDOWN:
|
|
|
|
case Common::EVENT_RBUTTONDOWN:
|
|
|
|
break;
|
|
|
|
case Common::EVENT_LBUTTONUP:
|
|
|
|
case Common::EVENT_RBUTTONUP:
|
|
|
|
break;
|
|
|
|
case Common::EVENT_QUIT:
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (shouldQuit())
|
|
|
|
return;
|
|
|
|
|
2013-11-14 14:44:24 +00:00
|
|
|
// TODO: Update all structures, animations, naks, heros etc.
|
2014-04-04 16:16:15 +02:00
|
|
|
_mainHero -> showHero();
|
2014-05-04 18:02:53 +02:00
|
|
|
if(_mainHero->_visible == 1) {
|
|
|
|
_mainHero -> scrollHero();
|
|
|
|
}
|
2013-11-14 14:44:24 +00:00
|
|
|
|
2013-12-10 00:26:42 +00:00
|
|
|
_interpreter->step();
|
2013-12-05 00:02:31 +00:00
|
|
|
|
2013-11-04 11:28:10 +00:00
|
|
|
drawScreen();
|
2013-10-30 02:28:07 +00:00
|
|
|
|
2013-11-02 02:02:53 +00:00
|
|
|
// Calculate the frame delay based off a desired frame time
|
|
|
|
int delay = 1000/15 - int32(_system->getMillis() - currentTime);
|
|
|
|
// Ensure non-negative
|
|
|
|
delay = delay < 0 ? 0 : delay;
|
|
|
|
_system->delayMillis(delay);
|
|
|
|
|
2013-10-30 02:28:07 +00:00
|
|
|
_cameraX = _newCameraX;
|
2013-11-02 15:28:56 +00:00
|
|
|
++_frameNr;
|
2013-11-09 23:07:40 +00:00
|
|
|
|
2013-11-11 12:00:19 +00:00
|
|
|
if (_debugger->_locationNr != _locationNr)
|
2013-11-09 23:07:40 +00:00
|
|
|
loadLocation(_debugger->_locationNr);
|
2013-11-11 12:00:19 +00:00
|
|
|
if (_debugger->_cursorNr != _cursorNr)
|
|
|
|
changeCursor(_debugger->_cursorNr);
|
2013-10-30 02:28:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} // End of namespace Prince
|
2013-11-04 11:40:22 +00:00
|
|
|
|
2013-10-30 02:28:07 +00:00
|
|
|
/* vim: set tabstop=4 expandtab!: */
|