scummvm/engines/prince/prince.cpp

4847 lines
119 KiB
C++

/* 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"
#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"
#include "common/str.h"
#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"
#include "prince/flags.h"
#include "prince/font.h"
#include "prince/mhwanh.h"
#include "prince/cursor.h"
#include "prince/archive.h"
#include "prince/hero.h"
#include "prince/resource.h"
#include "prince/animation.h"
#include "prince/option_text.h"
#include "prince/curve_values.h"
#include "prince/detection.h"
namespace Prince {
void PrinceEngine::debugEngine(const char *s, ...) {
char buf[STRINGBUFLEN];
va_list va;
va_start(va, s);
vsnprintf(buf, STRINGBUFLEN, s, va);
va_end(va);
debug("Prince::Engine %s", buf);
}
PrinceEngine::PrinceEngine(OSystem *syst, const PrinceGameDescription *gameDesc) :
Engine(syst), _gameDescription(gameDesc), _graph(nullptr), _script(nullptr), _interpreter(nullptr), _flags(nullptr),
_locationNr(0), _debugger(nullptr), _midiPlayer(nullptr), _room(nullptr),
_cursor1(nullptr), _cursor2(nullptr), _cursor3(nullptr), _font(nullptr),
_suitcaseBmp(nullptr), _roomBmp(nullptr), _cursorNr(0), _picWindowX(0), _picWindowY(0), _randomSource("prince"),
_invLineX(134), _invLineY(176), _invLine(5), _invLines(3), _invLineW(70), _invLineH(76), _maxInvW(72), _maxInvH(76),
_invLineSkipX(2), _invLineSkipY(3), _showInventoryFlag(false), _inventoryBackgroundRemember(false),
_mst_shadow(0), _mst_shadow2(0), _candleCounter(0), _invX1(53), _invY1(18), _invWidth(536), _invHeight(438),
_invCurInside(false), _optionsFlag(false), _optionEnabled(0), _invExamY(120), _invMaxCount(2), _invCounter(0),
_optionsMob(-1), _currentPointerNumber(1), _selectedMob(-1), _selectedItem(0), _selectedMode(0),
_optionsWidth(210), _optionsHeight(170), _invOptionsWidth(210), _invOptionsHeight(130), _optionsStep(20),
_invOptionsStep(20), _optionsNumber(7), _invOptionsNumber(5), _optionsColor1(236), _optionsColor2(252),
_dialogWidth(600), _dialogHeight(0), _dialogLineSpace(10), _dialogColor1(220), _dialogColor2(223),
_dialogFlag(false), _dialogLines(0), _dialogText(nullptr), _mouseFlag(1),
_roomPathBitmap(nullptr), _roomPathBitmapTemp(nullptr), _coordsBufEnd(nullptr), _coordsBuf(nullptr), _coords(nullptr),
_traceLineLen(0), _rembBitmapTemp(nullptr), _rembBitmap(nullptr), _rembMask(0), _rembX(0), _rembY(0), _fpX(0), _fpY(0),
_checkBitmapTemp(nullptr), _checkBitmap(nullptr), _checkMask(0), _checkX(0), _checkY(0), _traceLineFirstPointFlag(false),
_tracePointFirstPointFlag(false), _coordsBuf2(nullptr), _coords2(nullptr), _coordsBuf3(nullptr), _coords3(nullptr),
_shanLen(0), _directionTable(nullptr), _currentMidi(0), _lightX(0), _lightY(0), _curveData(nullptr), _curvPos(0),
_creditsData(nullptr), _creditsDataSize(0), _currentTime(0), _zoomBitmap(nullptr), _shadowBitmap(nullptr), _transTable(nullptr),
_flcFrameSurface(nullptr), _shadScaleValue(0), _shadLineLen(0), _scaleValue(0), _dialogImage(nullptr), _mobTranslationData(nullptr),
_mobTranslationSize(0) {
// Debug/console setup
DebugMan.addDebugChannel(DebugChannel::kScript, "script", "Prince Script debug channel");
DebugMan.addDebugChannel(DebugChannel::kEngine, "engine", "Prince Engine debug channel");
DebugMan.enableDebugChannel("script");
memset(_audioStream, 0, sizeof(_audioStream));
gDebugLevel = 10;
}
PrinceEngine::~PrinceEngine() {
DebugMan.clearAllDebugChannels();
delete _rnd;
delete _debugger;
delete _cursor1;
delete _cursor3;
delete _midiPlayer;
delete _script;
delete _flags;
delete _interpreter;
delete _font;
delete _roomBmp;
delete _suitcaseBmp;
delete _variaTxt;
free(_talkTxt);
free(_invTxt);
free(_dialogDat);
delete _graph;
delete _room;
if (_cursor2 != nullptr) {
_cursor2->free();
delete _cursor2;
}
for (uint i = 0; i < _objList.size(); i++) {
delete _objList[i];
}
_objList.clear();
free(_objSlot);
for (uint32 i = 0; i < _pscrList.size(); i++) {
delete _pscrList[i];
}
_pscrList.clear();
for (uint i = 0; i < _maskList.size(); i++) {
free(_maskList[i]._data);
}
_maskList.clear();
_drawNodeList.clear();
clearBackAnimList();
_backAnimList.clear();
freeAllNormAnims();
_normAnimList.clear();
for (uint i = 0; i < _allInvList.size(); i++) {
_allInvList[i]._surface->free();
delete _allInvList[i]._surface;
}
_allInvList.clear();
_optionsPic->free();
delete _optionsPic;
_optionsPicInInventory->free();
delete _optionsPicInInventory;
for (uint i = 0; i < _mainHero->_moveSet.size(); i++) {
delete _mainHero->_moveSet[i];
}
for (uint i = 0; i < _secondHero->_moveSet.size(); i++) {
delete _secondHero->_moveSet[i];
}
delete _mainHero;
delete _secondHero;
free(_roomPathBitmap);
free(_roomPathBitmapTemp);
free(_coordsBuf);
_mobPriorityList.clear();
freeAllSamples();
free(_zoomBitmap);
free(_shadowBitmap);
free(_transTable);
free(_curveData);
free(_shadowLine);
free(_creditsData);
if (_dialogImage != nullptr) {
_dialogImage->free();
delete _dialogImage;
}
free(_mobTranslationData);
}
GUI::Debugger *PrinceEngine::getDebugger() {
return _debugger;
}
void PrinceEngine::init() {
const Common::FSNode gameDataDir(ConfMan.get("path"));
debugEngine("Adding all path: %s", gameDataDir.getPath().c_str());
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");
PtcArchive *sound = new PtcArchive();
if (!sound->open("sound/databank.ptc"))
error("Can't open sound/databank.ptc");
PtcArchive *translation = new PtcArchive();
if (getLanguage() != Common::PL_POL && getLanguage() != Common::DE_DEU) {
if (!translation->openTranslation("all/prince_translation.dat"))
error("Can't open prince_translation.dat");
}
SearchMan.addSubDirectoryMatching(gameDataDir, "all");
SearchMan.add("all", all);
SearchMan.add("voices", voices);
SearchMan.add("sound", sound);
if (getLanguage() != Common::PL_POL && getLanguage() != Common::DE_DEU) {
SearchMan.add("translation", translation);
}
_graph = new GraphicsMan(this);
_rnd = new Common::RandomSource("prince");
_midiPlayer = new MusicPlayer(this);
if (getLanguage() == Common::DE_DEU) {
_font = new Font();
Resource::loadResource(_font, "font3.raw", true);
} else {
_font = new Font();
Resource::loadResource(_font, "font1.raw", true);
}
_suitcaseBmp = new MhwanhDecoder();
Resource::loadResource(_suitcaseBmp, "walizka", true);
_script = new Script(this);
Resource::loadResource(_script, "skrypt.dat", true);
_flags = new InterpreterFlags();
_interpreter = new Interpreter(this, _script, _flags);
_debugger = new Debugger(this, _flags);
_variaTxt = new VariaTxt();
if (getLanguage() == Common::PL_POL || getLanguage() == Common::DE_DEU) {
Resource::loadResource(_variaTxt, "variatxt.dat", true);
} else {
Resource::loadResource(_variaTxt, "variatxt_translate.dat", true);
}
_cursor1 = new Cursor();
Resource::loadResource(_cursor1, "mouse1.cur", true);
_cursor3 = new Cursor();
Resource::loadResource(_cursor3, "mouse2.cur", true);
Common::SeekableReadStream *talkTxtStream;
if (getLanguage() == Common::PL_POL || getLanguage() == Common::DE_DEU) {
talkTxtStream = SearchMan.createReadStreamForMember("talktxt.dat");
} else {
talkTxtStream = SearchMan.createReadStreamForMember("talktxt_translate.dat");
}
if (!talkTxtStream) {
error("Can't load talkTxtStream");
return;
}
_talkTxtSize = talkTxtStream->size();
_talkTxt = (byte *)malloc(_talkTxtSize);
talkTxtStream->read(_talkTxt, _talkTxtSize);
delete talkTxtStream;
Common::SeekableReadStream *invTxtStream;
if (getLanguage() == Common::PL_POL || getLanguage() == Common::DE_DEU) {
invTxtStream = SearchMan.createReadStreamForMember("invtxt.dat");
} else {
invTxtStream = SearchMan.createReadStreamForMember("invtxt_translate.dat");
}
if (!invTxtStream) {
error("Can't load invTxtStream");
return;
}
_invTxtSize = invTxtStream->size();
_invTxt = (byte *)malloc(_invTxtSize);
invTxtStream->read(_invTxt, _invTxtSize);
delete invTxtStream;
loadAllInv();
Common::SeekableReadStream *dialogDatStream = SearchMan.createReadStreamForMember("dialog.dat");
if (!dialogDatStream) {
error("Can't load dialogDatStream");
return;
}
_dialogDatSize = dialogDatStream->size();
_dialogDat = (byte *)malloc(_dialogDatSize);
dialogDatStream->read(_dialogDat, _dialogDatSize);
delete dialogDatStream;
_optionsPic = new Graphics::Surface();
_optionsPic->create(_optionsWidth, _optionsHeight, Graphics::PixelFormat::createFormatCLUT8());
Common::Rect picRect(0, 0, _optionsWidth, _optionsHeight);
_optionsPic->fillRect(picRect, _graph->kShadowColor);
_optionsPicInInventory = new Graphics::Surface();
_optionsPicInInventory->create(_invOptionsWidth, _invOptionsHeight, Graphics::PixelFormat::createFormatCLUT8());
Common::Rect invPicRect(0, 0, _invOptionsWidth, _invOptionsHeight);
_optionsPicInInventory->fillRect(invPicRect, _graph->kShadowColor);
_roomBmp = new Image::BitmapDecoder();
_room = new Room();
_mainHero = new Hero(this, _graph);
_secondHero = new Hero(this, _graph);
_secondHero->_maxBoredom = 140;
_secondHero->loadAnimSet(3);
_roomPathBitmap = (byte *)malloc(kPathBitmapLen);
_roomPathBitmapTemp = (byte *)malloc(kPathBitmapLen);
_coordsBuf = (byte *)malloc(kTracePts * 4);
_coords = _coordsBuf;
_coordsBufEnd = _coordsBuf + kTracePts * 4 - 4;
BackgroundAnim tempBackAnim;
tempBackAnim._seq._currRelative = 0;
for (int i = 0; i < kMaxBackAnims; i++) {
_backAnimList.push_back(tempBackAnim);
}
Anim tempAnim;
tempAnim._animData = nullptr;
tempAnim._shadowData = nullptr;
for (int i = 0; i < kMaxNormAnims; i++) {
_normAnimList.push_back(tempAnim);
}
_objSlot = (uint16 *)malloc(kMaxObjects * sizeof(uint16));
for (int i = 0; i < kMaxObjects; i++) {
_objSlot[i] = 0xFF;
}
_zoomBitmap = (byte *)malloc(kZoomBitmapLen);
_shadowBitmap = (byte *)malloc(2 * kShadowBitmapSize);
_transTable = (byte *)malloc(kTransTableSize);
_curveData = (int16 *)malloc(2 * kCurveLen * sizeof(int16));
_shadowLine = (byte *)malloc(kShadowLineArraySize);
Common::SeekableReadStream *creditsDataStream;
if (getLanguage() == Common::PL_POL || getLanguage() == Common::DE_DEU) {
creditsDataStream = SearchMan.createReadStreamForMember("credits.dat");
} else {
creditsDataStream = SearchMan.createReadStreamForMember("credits_translate.dat");
}
if (!creditsDataStream) {
error("Can't load creditsDataStream");
return;
}
_creditsDataSize = creditsDataStream->size();
_creditsData = (byte *)malloc(_creditsDataSize);
creditsDataStream->read(_creditsData, _creditsDataSize);
delete creditsDataStream;
if (getLanguage() != Common::PL_POL && getLanguage() != Common::DE_DEU) {
loadMobTranslationTexts();
}
}
void PrinceEngine::showLogo() {
MhwanhDecoder logo;
if (Resource::loadResource(&logo, "logo.raw", true)) {
loadSample(0, "LOGO.WAV");
playSample(0, 0);
_graph->draw(_graph->_frontScreen, logo.getSurface());
_graph->change();
_graph->update(_graph->_frontScreen);
setPalette(logo.getPalette());
uint32 logoStart = _system->getMillis();
while (_system->getMillis() < logoStart + 5000) {
Common::Event event;
Common::EventManager *eventMan = _system->getEventManager();
while (eventMan->pollEvent(event)) {
switch (event.type) {
case Common::EVENT_KEYDOWN:
if (event.kbd.keycode == Common::KEYCODE_ESCAPE) {
stopSample(0);
return;
}
break;
case Common::EVENT_LBUTTONDOWN:
stopSample(0);
return;
default:
break;
}
}
if (shouldQuit()) {
return;
}
}
}
}
Common::Error PrinceEngine::run() {
syncSoundSettings();
int startGameSlot = ConfMan.hasKey("save_slot") ? ConfMan.getInt("save_slot") : -1;
init();
if (startGameSlot == -1) {
showLogo();
} else {
loadLocation(59); // load intro location - easiest way to set everything up
loadGame(startGameSlot);
}
mainLoop();
return Common::kNoError;
}
void PrinceEngine::pauseEngineIntern(bool pause) {
Engine::pauseEngineIntern(pause);
if (pause) {
_midiPlayer->pause();
}
else {
_midiPlayer->resume();
}
}
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);
//debug("startPhase %d, endPhase %d, loopPhase %d", _startPhase, _endPhase, _loopPhase);
// 32 byte aligment
stream.seek(pos + 32);
return true;
}
bool PrinceEngine::loadLocation(uint16 locationNr) {
blackPalette();
_flicPlayer.close();
memset(_textSlots, 0, sizeof(_textSlots));
freeAllSamples();
debugEngine("PrinceEngine::loadLocation %d", locationNr);
const Common::FSNode gameDataDir(ConfMan.get("path"));
SearchMan.remove(Common::String::format("%02d", _locationNr));
_locationNr = locationNr;
_debugger->_locationNr = locationNr;
_flags->setFlagValue(Flags::CURRROOM, _locationNr);
_interpreter->stopBg();
changeCursor(0);
const Common::String locationNrStr = Common::String::format("%02d", _locationNr);
debugEngine("loadLocation %s", locationNrStr.c_str());
PtcArchive *locationArchive = new PtcArchive();
if (!locationArchive->open(locationNrStr + "/databank.ptc"))
error("Can't open location %s", locationNrStr.c_str());
SearchMan.add(locationNrStr, locationArchive);
loadMusic(_locationNr);
// load location background, replace old one
Resource::loadResource(_roomBmp, "room", true);
if (_roomBmp->getSurface()) {
_sceneWidth = _roomBmp->getSurface()->w;
}
loadZoom(_zoomBitmap, kZoomBitmapLen, "zoom");
loadShadow(_shadowBitmap, kShadowBitmapSize, "shadow", "shadow2");
loadTrans(_transTable, "trans");
loadPath("path");
for (uint32 i = 0; i < _pscrList.size(); i++) {
delete _pscrList[i];
}
_pscrList.clear();
Resource::loadResource(_pscrList, "pscr.lst", false);
loadMobPriority("mobpri");
_mobList.clear();
if (getGameType() == kPrinceDataDE) {
const Common::String mobLstName = Common::String::format("mob%02d.lst", _locationNr);
debug("name: %s", mobLstName.c_str());
Resource::loadResource(_mobList, mobLstName.c_str(), false);
} else if (getGameType() == kPrinceDataPL) {
Resource::loadResource(_mobList, "mob.lst", false);
}
if (getLanguage() != Common::PL_POL && getLanguage() != Common::DE_DEU) {
// update Mob texts for translated version
setMobTranslationTexts();
}
_animList.clear();
Resource::loadResource(_animList, "anim.lst", false);
for (uint32 i = 0; i < _objList.size(); i++) {
delete _objList[i];
}
_objList.clear();
Resource::loadResource(_objList, "obj.lst", false);
_room->loadRoom(_script->getRoomOffset(_locationNr));
for (uint i = 0; i < _maskList.size(); i++) {
free(_maskList[i]._data);
}
_maskList.clear();
_script->loadAllMasks(_maskList, _room->_nak);
_picWindowX = 0;
_lightX = _script->getLightX(_locationNr);
_lightY = _script->getLightY(_locationNr);
setShadowScale(_script->getShadowScale(_locationNr));
for (uint i = 0; i < _mobList.size(); i++) {
_mobList[i]._visible = _script->getMobVisible(_room->_mobs, i);
}
_script->installObjects(_room->_obj);
freeAllNormAnims();
clearBackAnimList();
_script->installBackAnims(_backAnimList, _room->_backAnim);
_graph->makeShadowTable(70, _graph->_shadowTable70);
_graph->makeShadowTable(50, _graph->_shadowTable50);
_mainHero->freeOldMove();
_secondHero->freeOldMove();
_mainHero->scrollHero();
return true;
}
void PrinceEngine::setShadowScale(int32 shadowScale) {
shadowScale = 100 - shadowScale;
if (!shadowScale) {
_shadScaleValue = 10000;
} else {
_shadScaleValue = 10000 / shadowScale;
}
}
void PrinceEngine::plotShadowLinePoint(int x, int y, int color, void *data) {
PrinceEngine *vm = (PrinceEngine *)data;
WRITE_LE_UINT16(&vm->_shadowLine[vm->_shadLineLen * 4], x);
WRITE_LE_UINT16(&vm->_shadowLine[vm->_shadLineLen * 4 + 2], y);
vm->_shadLineLen++;
}
void PrinceEngine::changeCursor(uint16 curId) {
_debugger->_cursorNr = curId;
_mouseFlag = curId;
_flags->setFlagValue(Flags::MOUSEENABLED, curId);
const Graphics::Surface *curSurface = nullptr;
switch (curId) {
case 0:
CursorMan.showMouse(false);
_optionsFlag = 0;
_selectedMob = -1;
return;
case 1:
curSurface = _cursor1->getSurface();
break;
case 2:
curSurface = _cursor2;
break;
case 3:
curSurface = _cursor3->getSurface();
Common::Point mousePos = _system->getEventManager()->getMousePos();
mousePos.x = CLIP(mousePos.x, (int16) 315, (int16) 639);
mousePos.y = CLIP(mousePos.y, (int16) 0, (int16) 170);
_system->warpMouse(mousePos.x, mousePos.y);
break;
}
CursorMan.replaceCursorPalette(_roomBmp->getPalette(), 0, 255);
CursorMan.replaceCursor(
curSurface->getBasePtr(0, 0),
curSurface->w, curSurface->h,
0, 0,
255, false,
&curSurface->format
);
CursorMan.showMouse(true);
}
void PrinceEngine::makeInvCursor(int itemNr) {
const Graphics::Surface *cur1Surface = _cursor1->getSurface();
int cur1W = cur1Surface->w;
int cur1H = cur1Surface->h;
const Common::Rect cur1Rect(0, 0, cur1W, cur1H);
const Graphics::Surface *itemSurface = _allInvList[itemNr].getSurface();
int itemW = itemSurface->w;
int itemH = itemSurface->h;
int cur2W = cur1W + itemW / 2;
int cur2H = cur1H + itemH / 2;
if (_cursor2 != nullptr) {
_cursor2->free();
delete _cursor2;
}
_cursor2 = new Graphics::Surface();
_cursor2->create(cur2W, cur2H, Graphics::PixelFormat::createFormatCLUT8());
Common::Rect cur2Rect(0, 0, cur2W, cur2H);
_cursor2->fillRect(cur2Rect, 255);
_cursor2->copyRectToSurface(*cur1Surface, 0, 0, cur1Rect);
const byte *src1 = (const byte *)itemSurface->getBasePtr(0, 0);
byte *dst1 = (byte *)_cursor2->getBasePtr(cur1W, cur1H);
if (itemH % 2) {
itemH--;
}
if (itemW % 2) {
itemW--;
}
for (int y = 0; y < itemH; y++) {
const byte *src2 = src1;
byte *dst2 = dst1;
if (y % 2 == 0) {
for (int x = 0; x < itemW; x++, src2++) {
if (x % 2 == 0) {
if (*src2) {
*dst2 = *src2;
} else {
*dst2 = 255;
}
dst2++;
}
}
dst1 += _cursor2->pitch;
}
src1 += itemSurface->pitch;
}
}
bool PrinceEngine::loadMusic(int musNumber) {
uint8 midiNumber = MusicPlayer::_musRoomTable[musNumber];
if (midiNumber) {
if (midiNumber != 100) {
if (_currentMidi != midiNumber) {
_currentMidi = midiNumber;
const char *musName = MusicPlayer::_musTable[_currentMidi];
_midiPlayer->loadMidi(musName);
}
}
} else {
stopMusic();
}
return true;
}
void PrinceEngine::stopMusic() {
if (_midiPlayer->isPlaying()) {
_midiPlayer->stop();
}
}
bool PrinceEngine::playNextFLCFrame() {
if (!_flicPlayer.isVideoLoaded())
return false;
const Graphics::Surface *s = _flicPlayer.decodeNextFrame();
if (s) {
_graph->drawTransparentSurface(_graph->_frontScreen, 0, 0, s, 255);
_graph->change();
_flcFrameSurface = s;
} else if (_flicLooped) {
_flicPlayer.rewind();
playNextFLCFrame();
} else if (_flcFrameSurface) {
_graph->drawTransparentSurface(_graph->_frontScreen, 0, 0, _flcFrameSurface, 255);
_graph->change();
}
return true;
}
void PrinceEngine::playSample(uint16 sampleId, uint16 loopType) {
if (_audioStream[sampleId]) {
if (_mixer->isSoundIDActive(sampleId)) {
return;
}
_audioStream[sampleId]->rewind();
if (sampleId < 28) {
_mixer->playStream(Audio::Mixer::kSFXSoundType, &_soundHandle[sampleId], _audioStream[sampleId], sampleId, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO);
} else {
_mixer->playStream(Audio::Mixer::kSpeechSoundType, &_soundHandle[sampleId], _audioStream[sampleId], sampleId, Audio::Mixer::kMaxChannelVolume, 0, DisposeAfterUse::NO);
}
}
}
void PrinceEngine::stopSample(uint16 sampleId) {
_mixer->stopID(sampleId);
}
void PrinceEngine::stopAllSamples() {
_mixer->stopAll();
}
void PrinceEngine::freeSample(uint16 sampleId) {
stopSample(sampleId);
if (_audioStream[sampleId] != nullptr) {
delete _audioStream[sampleId];
_audioStream[sampleId] = nullptr;
}
}
void PrinceEngine::freeAllSamples() {
for (int sampleId = 0; sampleId < kMaxSamples; sampleId++) {
freeSample(sampleId);
}
}
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, '\\');
// WALKAROUND: Wrong name in script, not existing sound in data files
if (!normalizedPath.compareTo("9997BEKA.WAV")) {
return 0;
}
debugEngine("loadSample slot %d, name %s", sampleSlot, normalizedPath.c_str());
freeSample(sampleSlot);
Common::SeekableReadStream *sampleStream = SearchMan.createReadStreamForMember(normalizedPath);
if (sampleStream == nullptr) {
delete sampleStream;
error("Can't load sample %s to slot %d", normalizedPath.c_str(), sampleSlot);
}
_audioStream[sampleSlot] = Audio::makeWAVStream(sampleStream, DisposeAfterUse::NO);
delete sampleStream;
return true;
}
bool PrinceEngine::loadVoice(uint32 slot, uint32 sampleSlot, const Common::String &streamName) {
debugEngine("Loading wav %s slot %d", streamName.c_str(), slot);
if (slot >= kMaxTexts) {
error("Text slot bigger than MAXTEXTS %d", kMaxTexts - 1);
return false;
}
freeSample(sampleSlot);
Common::SeekableReadStream *sampleStream = SearchMan.createReadStreamForMember(streamName);
if (sampleStream == nullptr) {
debug("Can't open %s", streamName.c_str());
return false;
}
uint32 id = sampleStream->readUint32LE();
if (id != MKTAG('F', 'F', 'I', 'R')) {
error("It's not RIFF file %s", streamName.c_str());
return false;
}
sampleStream->skip(0x20);
id = sampleStream->readUint32LE();
if (id != MKTAG('a', 't', 'a', 'd')) {
error("No data section in %s id %04x", streamName.c_str(), id);
return false;
}
id = sampleStream->readUint32LE();
debugEngine("SetVoice slot %d time %04x", slot, id);
id <<= 3;
id /= 22050;
id += 2;
_textSlots[slot]._time = id;
if (!slot) {
_mainHero->_talkTime = id;
} else if (slot == 1) {
_secondHero->_talkTime = id;
}
debugEngine("SetVoice slot %d time %04x", slot, id);
sampleStream->seek(SEEK_SET);
_audioStream[sampleSlot] = Audio::makeWAVStream(sampleStream, DisposeAfterUse::NO);
delete sampleStream;
return true;
}
void PrinceEngine::setVoice(uint16 slot, uint32 sampleSlot, uint16 flag) {
Common::String sampleName;
uint32 currentString = _interpreter->getCurrentString();
if (currentString >= 80000) {
uint32 nr = currentString - 80000;
sampleName = Common::String::format("%02d0%02d-%02d.WAV", nr / 100, nr % 100, flag);
} else if (currentString >= 70000) {
sampleName = Common::String::format("inv%02d-01.WAV", currentString - 70000);
} else if (currentString >= 60000) {
sampleName = Common::String::format("M%04d-%02d.WAV", currentString - 60000, flag);
} else if (currentString >= 2000) {
return;
} else if (flag >= 100) {
sampleName = Common::String::format("%03d-%03d.WAV", currentString, flag);
} else {
sampleName = Common::String::format("%03d-%02d.WAV", currentString, flag);
}
loadVoice(slot, sampleSlot, sampleName);
}
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());
}
debugEngine("%s loaded", streamName.c_str());
_flicLooped = loop;
_flicPlayer.start();
playNextFLCFrame();
return true;
}
bool PrinceEngine::loadZoom(byte *zoomBitmap, uint32 dataSize, const char *resourceName) {
Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(resourceName);
if (!stream) {
delete stream;
return false;
}
if (stream->read(zoomBitmap, dataSize) != dataSize) {
free(zoomBitmap);
delete stream;
return false;
}
delete stream;
return true;
}
bool PrinceEngine::loadShadow(byte *shadowBitmap, uint32 dataSize, const char *resourceName1, const char *resourceName2) {
Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(resourceName1);
if (!stream) {
delete stream;
return false;
}
if (stream->read(shadowBitmap, dataSize) != dataSize) {
free(shadowBitmap);
delete stream;
return false;
}
Common::SeekableReadStream *stream2 = SearchMan.createReadStreamForMember(resourceName2);
if (!stream2) {
delete stream;
delete stream2;
return false;
}
byte *shadowBitmap2 = shadowBitmap + dataSize;
if (stream2->read(shadowBitmap2, dataSize) != dataSize) {
free(shadowBitmap);
delete stream;
delete stream2;
return false;
}
delete stream;
delete stream2;
return true;
}
bool PrinceEngine::loadTrans(byte *transTable, const char *resourceName) {
Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(resourceName);
if (!stream) {
delete stream;
for (int i = 0; i < 256; i++) {
for (int j = 0; j < 256; j++) {
transTable[i * 256 + j] = j;
}
}
return true;
}
if (stream->read(transTable, kTransTableSize) != kTransTableSize) {
delete stream;
return false;
}
delete stream;
return true;
}
bool PrinceEngine::loadPath(const char *resourceName) {
Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(resourceName);
if (!stream) {
delete stream;
return false;
}
if (stream->read(_roomPathBitmap, kPathBitmapLen) != kPathBitmapLen) {
delete stream;
return false;
}
delete stream;
return true;
}
bool PrinceEngine::loadAllInv() {
for (int i = 0; i < kMaxInv; i++) {
InvItem tempInvItem;
const Common::String invStreamName = Common::String::format("INV%02d", i);
Common::SeekableReadStream *invStream = SearchMan.createReadStreamForMember(invStreamName);
if (!invStream) {
delete invStream;
return true;
}
tempInvItem._x = invStream->readUint16LE();
tempInvItem._y = invStream->readUint16LE();
int width = invStream->readUint16LE();
int height = invStream->readUint16LE();
tempInvItem._surface = new Graphics::Surface();
tempInvItem._surface->create(width, height, Graphics::PixelFormat::createFormatCLUT8());
for (int h = 0; h < tempInvItem._surface->h; h++) {
invStream->read(tempInvItem._surface->getBasePtr(0, h), tempInvItem._surface->w);
}
_allInvList.push_back(tempInvItem);
delete invStream;
}
return true;
}
bool PrinceEngine::loadMobPriority(const char *resourceName) {
Common::SeekableReadStream *stream = SearchMan.createReadStreamForMember(resourceName);
if (!stream) {
delete stream;
return false;
}
_mobPriorityList.clear();
uint mobId;
while (1) {
mobId = stream->readUint32LE();
if (mobId == 0xFFFFFFFF) {
break;
}
_mobPriorityList.push_back(mobId);
}
delete stream;
return true;
}
void PrinceEngine::loadMobTranslationTexts() {
Common::SeekableReadStream *mobTranslationStream = SearchMan.createReadStreamForMember("mob_translate.dat");
if (!mobTranslationStream) {
error("Can't load mob_translate.dat");
}
_mobTranslationSize = mobTranslationStream->size();
_mobTranslationData = (byte *)malloc(_mobTranslationSize);
mobTranslationStream->read(_mobTranslationData, _mobTranslationSize);
delete mobTranslationStream;
}
void PrinceEngine::setMobTranslationTexts() {
int locationOffset = READ_UINT16(_mobTranslationData + (_locationNr - 1) * 2);
if (locationOffset) {
byte *locationText = _mobTranslationData + locationOffset;
for (uint i = 0; i < _mobList.size(); i++) {
byte c;
locationText++;
_mobList[i]._name.clear();
while ((c = *locationText)) {
_mobList[i]._name += c;
locationText++;
}
locationText++;
_mobList[i]._examText.clear();
c = *locationText;
locationText++;
if (c) {
_mobList[i]._examText += c;
do {
c = *locationText;
_mobList[i]._examText += c;
locationText++;
} while (c != 255);
}
}
}
}
void PrinceEngine::keyHandler(Common::Event event) {
uint16 nChar = event.kbd.keycode;
switch (nChar) {
case Common::KEYCODE_d:
if (event.kbd.hasFlags(Common::KBD_CTRL)) {
getDebugger()->attach();
}
break;
case Common::KEYCODE_z:
if (_flags->getFlagValue(Flags::POWERENABLED)) {
_flags->setFlagValue(Flags::MBFLAG, 1);
}
break;
case Common::KEYCODE_x:
if (_flags->getFlagValue(Flags::POWERENABLED)) {
_flags->setFlagValue(Flags::MBFLAG, 2);
}
break;
case Common::KEYCODE_ESCAPE:
_flags->setFlagValue(Flags::ESCAPED2, 1);
break;
}
}
int PrinceEngine::getMob(Common::Array<Mob> &mobList, bool usePriorityList, int posX, int posY) {
Common::Point pointPos(posX, posY);
int mobListSize;
if (usePriorityList) {
mobListSize = _mobPriorityList.size();
} else {
mobListSize = mobList.size();
}
for (int mobNumber = 0; mobNumber < mobListSize; mobNumber++) {
Mob *mob = nullptr;
if (usePriorityList) {
mob = &mobList[_mobPriorityList[mobNumber]];
} else {
mob = &mobList[mobNumber];
}
if (mob->_visible) {
continue;
}
int type = mob->_type & 7;
switch (type) {
case 0:
case 1:
//normal_mob
if (!mob->_rect.contains(pointPos)) {
continue;
}
break;
case 3:
//mob_obj
if (mob->_mask < kMaxObjects) {
int nr = _objSlot[mob->_mask];
if (nr != 0xFF) {
Object &obj = *_objList[nr];
Common::Rect objectRect(obj._x, obj._y, obj._x + obj._width, obj._y + obj._height);
if (objectRect.contains(pointPos)) {
Graphics::Surface *objSurface = obj.getSurface();
byte *pixel = (byte *)objSurface->getBasePtr(posX - obj._x, posY - obj._y);
if (*pixel != 255) {
break;
}
}
}
}
continue;
break;
case 2:
case 5:
//check_ba_mob
if (!_backAnimList[mob->_mask].backAnims.empty()) {
int currentAnim = _backAnimList[mob->_mask]._seq._currRelative;
Anim &backAnim = _backAnimList[mob->_mask].backAnims[currentAnim];
if (backAnim._animData != nullptr) {
if (!backAnim._state) {
Common::Rect backAnimRect(backAnim._currX, backAnim._currY, backAnim._currX + backAnim._currW, backAnim._currY + backAnim._currH);
if (backAnimRect.contains(pointPos)) {
int phase = backAnim._showFrame;
int phaseFrameIndex = backAnim._animData->getPhaseFrameIndex(phase);
Graphics::Surface *backAnimSurface = backAnim._animData->getFrame(phaseFrameIndex);
byte pixel = *(byte *)backAnimSurface->getBasePtr(posX - backAnim._currX, posY - backAnim._currY);
if (pixel != 255) {
if (type == 5) {
if (mob->_rect.contains(pointPos)) {
break;
}
} else {
break;
}
}
}
}
}
}
continue;
break;
default:
//not_part_ba
continue;
break;
}
if (usePriorityList) {
return _mobPriorityList[mobNumber];
} else {
return mobNumber;
}
}
return -1;
}
int PrinceEngine::checkMob(Graphics::Surface *screen, Common::Array<Mob> &mobList, bool usePriorityList) {
if (_mouseFlag == 0 || _mouseFlag == 3) {
return -1;
}
Common::Point mousePos = _system->getEventManager()->getMousePos();
int mobNumber = getMob(mobList, usePriorityList, mousePos.x + _picWindowX, mousePos.y);
if (mobNumber != -1) {
Common::String mobName = mobList[mobNumber]._name;
if (getLanguage() == Common::DE_DEU) {
for (uint i = 0; i < mobName.size(); i++) {
switch (mobName[i]) {
case '\xc4':
mobName.setChar('\x83', i);
break;
case '\xd6':
mobName.setChar('\x84', i);
break;
case '\xdc':
mobName.setChar('\x85', i);
break;
case '\xdf':
mobName.setChar('\x7f', i);
break;
case '\xe4':
mobName.setChar('\x80', i);
break;
case '\xf6':
mobName.setChar('\x81', i);
break;
case '\xfc':
mobName.setChar('\x82', i);
break;
}
}
}
uint16 textW = getTextWidth(mobName.c_str());
uint16 x = mousePos.x - textW / 2;
if (x > screen->w) {
x = 0;
}
if (x + textW > screen->w) {
x = screen->w - textW;
}
uint16 y = mousePos.y - _font->getFontHeight();
if (y > screen->h) {
y = _font->getFontHeight() - 2;
}
_font->drawString(screen, mobName, x, y, screen->w, 216);
}
return mobNumber;
}
void PrinceEngine::printAt(uint32 slot, uint8 color, 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);
if (getLanguage() == Common::DE_DEU)
correctStringDEU(s);
Text &text = _textSlots[slot];
text._str = s;
text._x = x;
text._y = y;
text._color = color;
int lines = calcTextLines(s);
text._time = calcTextTime(lines);
}
int PrinceEngine::calcTextLines(const char *s) {
int lines = 1;
while (*s) {
if (*s == '\n') {
lines++;
}
s++;
}
return lines;
}
int PrinceEngine::calcTextTime(int numberOfLines) {
return numberOfLines * 30;
}
void PrinceEngine::correctStringDEU(char *s) {
while (*s) {
switch (*s) {
case '\xc4':
*s = '\x83';
break;
case '\xd6':
*s = '\x84';
break;
case '\xdc':
*s = '\x85';
break;
case '\xdf':
*s = '\x7f';
break;
case '\xe4':
*s = '\x80';
break;
case '\xf6':
*s = '\x81';
break;
case '\xfc':
*s = '\x82';
break;
}
s++;
}
}
uint32 PrinceEngine::getTextWidth(const char *s) {
uint16 textW = 0;
while (*s) {
textW += _font->getCharWidth(*s) + _font->getKerningOffset(0, 0);
s++;
}
return textW;
}
void PrinceEngine::showTexts(Graphics::Surface *screen) {
for (uint32 slot = 0; slot < kMaxTexts; slot++) {
if (_showInventoryFlag && slot) {
// only slot 0 for inventory
break;
}
Text& text = _textSlots[slot];
if (!text._str && !text._time) {
continue;
}
int x = text._x;
int y = text._y;
if (!_showInventoryFlag) {
x -= _picWindowX;
y -= _picWindowY;
}
Common::Array<Common::String> lines;
_font->wordWrapText(text._str, _graph->_frontScreen->w, lines);
int wideLine = 0;
for (uint i = 0; i < lines.size(); i++) {
int textLen = getTextWidth(lines[i].c_str());
if (textLen > wideLine) {
wideLine = textLen;
}
}
int leftBorderText = 6;
if (x + wideLine / 2 > kNormalWidth - leftBorderText) {
x = kNormalWidth - leftBorderText - wideLine / 2;
}
if (x - wideLine / 2 < leftBorderText) {
x = leftBorderText + wideLine / 2;
}
int textSkip = 2;
for (uint i = 0; i < lines.size(); i++) {
int drawX = x - getTextWidth(lines[i].c_str()) / 2;
int drawY = y - 10 - (lines.size() - i) * (_font->getFontHeight() - textSkip);
if (drawX < 0) {
drawX = 0;
}
if (drawY < 0) {
drawY = 0;
}
_font->drawString(screen, lines[i], drawX, drawY, screen->w, text._color);
}
text._time--;
if (!text._time) {
text._str = nullptr;
}
}
}
bool PrinceEngine::spriteCheck(int sprWidth, int sprHeight, int destX, int destY) {
destX -= _picWindowX;
destY -= _picWindowY;
// if x1 is on visible part of screen
if (destX < 0) {
if (destX + sprWidth < 1) {
//x2 is negative - out of window
return false;
}
}
// if x1 is outside of screen on right side
if (destX >= kNormalWidth) {
return false;
}
if (destY < 0) {
if (destY + sprHeight < 1) {
//y2 is negative - out of window
return false;
}
}
if (destY >= kNormalHeight) {
return false;
}
return true;
}
// CheckNak
void PrinceEngine::checkMasks(int x1, int y1, int sprWidth, int sprHeight, int z) {
int x2 = x1 + sprWidth - 1;
int y2 = y1 + sprHeight - 1;
if (x1 < 0) {
x1 = 0;
}
for (uint i = 0; i < _maskList.size(); i++) {
if (!_maskList[i]._state && !_maskList[i]._flags) {
if (_maskList[i]._z > z) {
if (_maskList[i]._x1 <= x2 && _maskList[i]._x2 >= x1) {
if (_maskList[i]._y1 <= y2 && _maskList[i]._y2 >= y1) {
_maskList[i]._state = 1;
}
}
}
}
}
}
// ClsNak
void PrinceEngine::clsMasks() {
for (uint i = 0; i < _maskList.size(); i++) {
if (_maskList[i]._state) {
_maskList[i]._state = 0;
}
}
}
// InsertNakladki
void PrinceEngine::insertMasks(Graphics::Surface *originalRoomSurface) {
for (uint i = 0; i < _maskList.size(); i++) {
if (_maskList[i]._state) {
if (_maskList[i]._data != nullptr) {
showMask(i, originalRoomSurface);
} else {
error("insertMasks() - Wrong mask data- nr %d", i);
}
}
}
}
// ShowNak
void PrinceEngine::showMask(int maskNr, Graphics::Surface *originalRoomSurface) {
if (!_maskList[maskNr]._flags) {
if (spriteCheck(_maskList[maskNr]._width, _maskList[maskNr]._height, _maskList[maskNr]._x1, _maskList[maskNr]._y1)) {
int destX = _maskList[maskNr]._x1 - _picWindowX;
int destY = _maskList[maskNr]._y1 - _picWindowY;
DrawNode newDrawNode;
newDrawNode.posX = destX;
newDrawNode.posY = destY;
newDrawNode.posZ = _maskList[maskNr]._z;
newDrawNode.width = _maskList[maskNr]._width;
newDrawNode.height = _maskList[maskNr]._height;
newDrawNode.s = nullptr;
newDrawNode.originalRoomSurface = originalRoomSurface;
newDrawNode.data = _maskList[maskNr].getMask();
newDrawNode.drawFunction = &_graph->drawMaskDrawNode;
_drawNodeList.push_back(newDrawNode);
}
}
}
void PrinceEngine::showSprite(Graphics::Surface *spriteSurface, int destX, int destY, int destZ) {
if (spriteCheck(spriteSurface->w, spriteSurface->h, destX, destY)) {
destX -= _picWindowX;
destY -= _picWindowY;
DrawNode newDrawNode;
newDrawNode.posX = destX;
newDrawNode.posY = destY;
newDrawNode.posZ = destZ;
newDrawNode.width = 0;
newDrawNode.height = 0;
newDrawNode.s = spriteSurface;
newDrawNode.originalRoomSurface = nullptr;
newDrawNode.data = _transTable;
newDrawNode.drawFunction = &_graph->drawTransparentWithTransDrawNode;
_drawNodeList.push_back(newDrawNode);
}
}
void PrinceEngine::showSpriteShadow(Graphics::Surface *shadowSurface, int destX, int destY, int destZ) {
if (spriteCheck(shadowSurface->w, shadowSurface->h, destX, destY)) {
destX -= _picWindowX;
destY -= _picWindowY;
DrawNode newDrawNode;
newDrawNode.posX = destX;
newDrawNode.posY = destY;
newDrawNode.posZ = destZ;
newDrawNode.width = 0;
newDrawNode.height = 0;
newDrawNode.s = shadowSurface;
newDrawNode.originalRoomSurface = nullptr;
newDrawNode.data = _graph->_shadowTable70;
newDrawNode.drawFunction = &_graph->drawAsShadowDrawNode;
_drawNodeList.push_back(newDrawNode);
}
}
void PrinceEngine::showAnim(Anim &anim) {
//ShowFrameCode
//ShowAnimFrame
int phase = anim._showFrame;
int phaseFrameIndex = anim._animData->getPhaseFrameIndex(phase);
int x = anim._x + anim._animData->getPhaseOffsetX(phase);
int y = anim._y + anim._animData->getPhaseOffsetY(phase);
int animFlag = anim._flags;
int checkMaskFlag = (animFlag & 1);
int maxFrontFlag = (animFlag & 2);
int specialZFlag = anim._nextAnim;
int z = anim._nextAnim;
Graphics::Surface *animSurface = anim._animData->getFrame(phaseFrameIndex);
int frameWidth = animSurface->w;
int frameHeight = animSurface->h;
int shadowZ = 0;
if (checkMaskFlag) {
if (!anim._nextAnim) {
z = y + frameHeight - 1;
}
checkMasks(x, y, frameWidth, frameHeight, z);
}
if (specialZFlag) {
z = specialZFlag;
} else if (maxFrontFlag) {
z = kMaxPicHeight + 1;
} else {
z = y + frameHeight - 1;
}
shadowZ = z;
anim._currX = x;
anim._currY = y;
anim._currW = frameWidth;
anim._currH = frameHeight;
showSprite(animSurface, x, y, z);
// make_special_shadow
if ((anim._flags & 0x80)) {
if (animSurface) {
DrawNode newDrawNode;
newDrawNode.posX = x;
newDrawNode.posY = y + animSurface->h - anim._shadowBack;
newDrawNode.posZ = Hero::kHeroShadowZ;
newDrawNode.width = 0;
newDrawNode.height = 0;
newDrawNode.scaleValue = _scaleValue;
newDrawNode.originalRoomSurface = nullptr;
newDrawNode.data = this;
newDrawNode.drawFunction = &Hero::showHeroShadow;
newDrawNode.s = animSurface;
_drawNodeList.push_back(newDrawNode);
}
}
//ShowFrameCodeShadow
//ShowAnimFrameShadow
if (anim._shadowData != nullptr) {
int shadowPhaseFrameIndex = anim._shadowData->getPhaseFrameIndex(phase);
int shadowX = anim._shadowData->getBaseX() + anim._shadowData->getPhaseOffsetX(phase);
int shadowY = anim._shadowData->getBaseY() + anim._shadowData->getPhaseOffsetY(phase);
Graphics::Surface *shadowSurface = anim._shadowData->getFrame(shadowPhaseFrameIndex);
int shadowFrameWidth = shadowSurface->w;
int shadowFrameHeight = shadowSurface->h;
if (checkMaskFlag) {
checkMasks(shadowX, shadowY, shadowFrameWidth, shadowFrameHeight, shadowY + shadowFrameWidth - 1);
}
if (!shadowZ) {
if (maxFrontFlag) {
shadowZ = kMaxPicHeight + 1;
} else {
shadowZ = shadowY + shadowFrameWidth - 1;
}
}
showSpriteShadow(shadowSurface, shadowX, shadowY, shadowZ);
}
}
void PrinceEngine::showNormAnims() {
for (int i = 0; i < kMaxNormAnims; i++) {
Anim &anim = _normAnimList[i];
if (anim._animData != nullptr) {
int phaseCount = anim._animData->getPhaseCount();
if (!anim._state) {
if (anim._frame == anim._lastFrame - 1) {
if (anim._loopType) {
if (anim._loopType == 1) {
anim._frame = anim._loopFrame;
} else {
continue;
}
}
} else {
anim._frame++;
}
anim._showFrame = anim._frame;
if (anim._showFrame >= phaseCount) {
anim._showFrame = phaseCount - 1;
}
showAnim(anim);
}
}
}
}
void PrinceEngine::setBackAnim(Anim &backAnim) {
int start = backAnim._basaData._start;
if (start != -1) {
backAnim._frame = start;
backAnim._showFrame = start;
backAnim._loopFrame = start;
}
int end = backAnim._basaData._end;
if (end != -1) {
backAnim._lastFrame = end;
}
backAnim._state = 0;
}
void PrinceEngine::showBackAnims() {
for (int i = 0; i < kMaxBackAnims; i++) {
BAS &seq = _backAnimList[i]._seq;
int activeSubAnim = seq._currRelative;
if (!_backAnimList[i].backAnims.empty()) {
if (_backAnimList[i].backAnims[activeSubAnim]._animData != nullptr) {
if (!_backAnimList[i].backAnims[activeSubAnim]._state) {
seq._counter++;
if (seq._type == 2) {
if (!seq._currRelative) {
if (seq._counter >= seq._data) {
if (seq._anims > 2) {
seq._currRelative = _randomSource.getRandomNumber(seq._anims - 2) + 1;
activeSubAnim = seq._currRelative;
seq._current = _backAnimList[i].backAnims[activeSubAnim]._basaData._num;
}
setBackAnim(_backAnimList[i].backAnims[activeSubAnim]);
seq._counter = 0;
}
}
}
if (seq._type == 3) {
if (!seq._currRelative) {
if (seq._counter < seq._data2) {
continue;
} else {
setBackAnim(_backAnimList[i].backAnims[activeSubAnim]);
}
}
}
if (_backAnimList[i].backAnims[activeSubAnim]._frame == _backAnimList[i].backAnims[activeSubAnim]._lastFrame - 1) {
_backAnimList[i].backAnims[activeSubAnim]._frame = _backAnimList[i].backAnims[activeSubAnim]._loopFrame;
switch (seq._type) {
case 1:
if (seq._anims > 1) {
int rnd;
do {
rnd = _randomSource.getRandomNumber(seq._anims - 1);
} while (rnd == seq._currRelative);
seq._currRelative = rnd;
seq._current = _backAnimList[i].backAnims[rnd]._basaData._num;
activeSubAnim = rnd;
setBackAnim(_backAnimList[i].backAnims[activeSubAnim]);
seq._counter = 0;
}
break;
case 2:
if (seq._currRelative) {
seq._currRelative = 0;
seq._current = _backAnimList[i].backAnims[0]._basaData._num;
activeSubAnim = 0;
setBackAnim(_backAnimList[i].backAnims[activeSubAnim]);
seq._counter = 0;
}
break;
case 3:
seq._currRelative = 0;
seq._current = _backAnimList[i].backAnims[0]._basaData._num;
seq._counter = 0;
seq._data2 = _randomSource.getRandomNumber(seq._data - 1);
continue; // for bug in original game
break;
}
} else {
_backAnimList[i].backAnims[activeSubAnim]._frame++;
}
_backAnimList[i].backAnims[activeSubAnim]._showFrame = _backAnimList[i].backAnims[activeSubAnim]._frame;
showAnim(_backAnimList[i].backAnims[activeSubAnim]);
}
}
}
}
}
void PrinceEngine::removeSingleBackAnim(int slot) {
if (!_backAnimList[slot].backAnims.empty()) {
for (uint j = 0; j < _backAnimList[slot].backAnims.size(); j++) {
if (_backAnimList[slot].backAnims[j]._animData != nullptr) {
delete _backAnimList[slot].backAnims[j]._animData;
_backAnimList[slot].backAnims[j]._animData = nullptr;
}
if (_backAnimList[slot].backAnims[j]._shadowData != nullptr) {
delete _backAnimList[slot].backAnims[j]._shadowData;
_backAnimList[slot].backAnims[j]._shadowData = nullptr;
}
}
_backAnimList[slot].backAnims.clear();
_backAnimList[slot]._seq._currRelative = 0;
}
}
void PrinceEngine::clearBackAnimList() {
for (int i = 0; i < kMaxBackAnims; i++) {
removeSingleBackAnim(i);
}
}
void PrinceEngine::grabMap() {
_graph->_frontScreen->copyFrom(*_roomBmp->getSurface());
showObjects();
runDrawNodes();
_graph->_mapScreen->copyFrom(*_graph->_frontScreen);
}
void PrinceEngine::initZoomIn(int slot) {
freeZoomObject(slot);
Object *object = _objList[slot];
if (object != nullptr) {
Graphics::Surface *zoomSource = object->getSurface();
if (zoomSource != nullptr) {
object->_flags |= 0x8000;
object->_zoomSurface = new Graphics::Surface();
object->_zoomSurface->create(zoomSource->w, zoomSource->h, Graphics::PixelFormat::createFormatCLUT8());
object->_zoomSurface->fillRect(Common::Rect(zoomSource->w, zoomSource->h), 0xFF);
object->_zoomTime = 20;
}
}
}
void PrinceEngine::initZoomOut(int slot) {
freeZoomObject(slot);
Object *object = _objList[slot];
if (object != nullptr) {
Graphics::Surface *zoomSource = object->getSurface();
if (zoomSource != nullptr) {
object->_flags |= 0x4000;
object->_zoomSurface = new Graphics::Surface();
object->_zoomSurface->copyFrom(*zoomSource);
object->_zoomTime = 10;
}
}
}
void PrinceEngine::doZoomIn(int slot) {
Object *object = _objList[slot];
if (object != nullptr) {
Graphics::Surface *orgSurface = object->getSurface();
if (orgSurface != nullptr) {
byte *src1 = (byte *)orgSurface->getBasePtr(0, 0);
byte *dst1 = (byte *)object->_zoomSurface->getBasePtr(0, 0);
int x = 0;
int surfaceHeight = orgSurface->h;
for (int y = 0; y < surfaceHeight; y++) {
byte *src2 = src1;
byte *dst2 = dst1;
int w = orgSurface->w - x;
src2 += x;
dst2 += x;
while (w > 0) {
int randVal = _randomSource.getRandomNumber(zoomInStep - 1);
if (randVal < w) {
*(dst2 + randVal) = *(src2 + randVal);
src2 += zoomInStep;
dst2 += zoomInStep;
} else if (y + 1 != surfaceHeight) {
*(dst1 + orgSurface->pitch + randVal - w) = *(src1 + orgSurface->pitch + randVal - w);
}
w -= zoomInStep;
}
x = -1 * w;
src1 += orgSurface->pitch;
dst1 += orgSurface->pitch;
}
}
}
}
void PrinceEngine::doZoomOut(int slot) {
Object *object = _objList[slot];
if (object != nullptr) {
Graphics::Surface *orgSurface = object->getSurface();
if (orgSurface != nullptr) {
byte *dst1 = (byte *)object->_zoomSurface->getBasePtr(0, 0);
int x = 0;
int surfaceHeight = orgSurface->h;
for (int y = 0; y < surfaceHeight; y++) {
byte *dst2 = dst1;
int w = orgSurface->w - x;
dst2 += x;
while (w > 0) {
int randVal = _randomSource.getRandomNumber(zoomInStep - 1);
if (randVal < w) {
*(dst2 + randVal) = 255;
dst2 += zoomInStep;
} else if (y + 1 != surfaceHeight) {
*(dst1 + orgSurface->pitch + randVal - w) = 255;
}
w -= zoomInStep;
}
x = -1 * w;
dst1 += orgSurface->pitch;
}
}
}
}
void PrinceEngine::freeZoomObject(int slot) {
Object *object = _objList[slot];
if (object != nullptr) {
if (object->_zoomSurface != nullptr) {
object->_zoomSurface->free();
delete object->_zoomSurface;
object->_zoomSurface = nullptr;
}
}
}
void PrinceEngine::showObjects() {
for (int i = 0; i < kMaxObjects; i++) {
int nr = _objSlot[i];
if (nr != 0xFF) {
Graphics::Surface *objSurface = nullptr;
if ((_objList[nr]->_flags & 0x8000)) {
_objList[nr]->_zoomTime--;
if (!_objList[nr]->_zoomTime) {
freeZoomObject(nr);
_objList[nr]->_flags &= 0x7FFF;
objSurface = _objList[nr]->getSurface();
} else {
doZoomIn(nr);
objSurface = _objList[nr]->_zoomSurface;
}
} else if ((_objList[nr]->_flags & 0x4000)) {
_objList[nr]->_zoomTime--;
if (!_objList[nr]->_zoomTime) {
freeZoomObject(nr);
_objList[nr]->_flags &= 0xBFFF;
objSurface = _objList[nr]->getSurface();
} else {
doZoomOut(nr);
objSurface = _objList[nr]->_zoomSurface;
}
} else {
objSurface = _objList[nr]->getSurface();
}
if (objSurface != nullptr) {
if (spriteCheck(objSurface->w, objSurface->h, _objList[nr]->_x, _objList[nr]->_y)) {
int destX = _objList[nr]->_x - _picWindowX;
int destY = _objList[nr]->_y - _picWindowY;
DrawNode newDrawNode;
newDrawNode.posX = destX;
newDrawNode.posY = destY;
newDrawNode.posZ = _objList[nr]->_z;
newDrawNode.width = 0;
newDrawNode.height = 0;
newDrawNode.s = objSurface;
newDrawNode.originalRoomSurface = nullptr;
if ((_objList[nr]->_flags & 0x2000)) {
newDrawNode.data = nullptr;
newDrawNode.drawFunction = &_graph->drawBackSpriteDrawNode;
} else {
newDrawNode.data = _transTable;
if (_flags->getFlagValue(Flags::NOANTIALIAS)) {
newDrawNode.drawFunction = &_graph->drawTransparentDrawNode;
} else {
newDrawNode.drawFunction = &_graph->drawTransparentWithTransDrawNode;
}
}
_drawNodeList.push_back(newDrawNode);
}
if ((_objList[nr]->_flags & 1)) {
checkMasks(_objList[nr]->_x, _objList[nr]->_y, objSurface->w, objSurface->h, _objList[nr]->_z);
}
}
}
}
}
void PrinceEngine::showParallax() {
if (!_pscrList.empty()) {
for (uint i = 0; i < _pscrList.size(); i++) {
Graphics::Surface *pscrSurface = _pscrList[i]->getSurface();
if (pscrSurface != nullptr) {
int x = _pscrList[i]->_x - (_pscrList[i]->_step * _picWindowX / 4);
int y = _pscrList[i]->_y;
int z = PScr::kPScrZ;
if (spriteCheck(pscrSurface->w, pscrSurface->h, x, y)) {
showSprite(pscrSurface, x, y, z);
}
}
}
}
}
bool PrinceEngine::compareDrawNodes(DrawNode d1, DrawNode d2) {
if (d1.posZ < d2.posZ) {
return true;
}
return false;
}
void PrinceEngine::runDrawNodes() {
Common::sort(_drawNodeList.begin(), _drawNodeList.end(), compareDrawNodes);
for (uint i = 0; i < _drawNodeList.size(); i++) {
(*_drawNodeList[i].drawFunction)(_graph->_frontScreen, &_drawNodeList[i]);
}
_graph->change();
}
void PrinceEngine::drawScreen() {
if (!_showInventoryFlag || _inventoryBackgroundRemember) {
clsMasks();
_mainHero->showHero();
_mainHero->scrollHero();
_mainHero->drawHero();
_secondHero->showHero();
_secondHero->_drawX -= _picWindowX;
_secondHero->drawHero();
const Graphics::Surface *roomSurface;
if (_locationNr != 50) {
roomSurface = _roomBmp->getSurface();
} else {
roomSurface = _graph->_mapScreen;
}
Graphics::Surface visiblePart;
if (roomSurface) {
visiblePart = roomSurface->getSubArea(Common::Rect(_picWindowX, 0, roomSurface->w, roomSurface->h));
_graph->draw(_graph->_frontScreen, &visiblePart);
}
showBackAnims();
showNormAnims();
playNextFLCFrame();
showObjects();
if (roomSurface) {
insertMasks(&visiblePart);
}
showParallax();
runDrawNodes();
_drawNodeList.clear();
if (!_inventoryBackgroundRemember && !_dialogFlag) {
if (!_optionsFlag) {
_selectedMob = checkMob(_graph->_frontScreen, _mobList, true);
}
showTexts(_graph->_frontScreen);
checkOptions();
} else {
_inventoryBackgroundRemember = false;
}
showPower();
getDebugger()->onFrame();
} else {
displayInventory();
}
}
void PrinceEngine::blackPalette() {
byte *paletteBackup = (byte *)malloc(256 * 3);
byte *blackPalette1 = (byte *)malloc(256 * 3);
int fadeStep = kFadeStep - 1;
for (int i = 0; i < kFadeStep; i++) {
_system->getPaletteManager()->grabPalette(paletteBackup, 0, 256);
for (int j = 0; j < 256; j++) {
blackPalette1[3 * j] = paletteBackup[3 * j] * fadeStep / 4;
blackPalette1[3 * j + 1] = paletteBackup[3 * j + 1] * fadeStep / 4;
blackPalette1[3 * j + 2] = paletteBackup[3 * j + 2] * fadeStep / 4;
}
fadeStep--;
_graph->setPalette(blackPalette1);
_system->updateScreen();
Common::Event event;
Common::EventManager *eventMan = _system->getEventManager();
eventMan->pollEvent(event);
if (shouldQuit()) {
free(paletteBackup);
free(blackPalette1);
return;
}
pausePrinceEngine();
}
free(paletteBackup);
free(blackPalette1);
}
void PrinceEngine::setPalette(const byte *palette) {
if (palette != nullptr) {
byte *blackPalette_ = (byte *)malloc(256 * 3);
int fadeStep = 0;
for (int i = 0; i <= kFadeStep; i++) {
for (int j = 0; j < 256; j++) {
blackPalette_[3 * j] = palette[3 * j] * fadeStep / 4;
blackPalette_[3 * j + 1] = palette[3 * j + 1] * fadeStep / 4;
blackPalette_[3 * j + 2] = palette[3 * j + 2] * fadeStep / 4;
}
fadeStep++;
_graph->setPalette(blackPalette_);
_system->updateScreen();
Common::Event event;
Common::EventManager *eventMan = _system->getEventManager();
eventMan->pollEvent(event);
if (shouldQuit()) {
_graph->setPalette(palette);
free(blackPalette_);
return;
}
pausePrinceEngine();
}
_graph->setPalette(palette);
free(blackPalette_);
}
}
void PrinceEngine::pausePrinceEngine(int fps) {
int delay = 1000 / fps - int32(_system->getMillis() - _currentTime);
delay = delay < 0 ? 0 : delay;
_system->delayMillis(delay);
_currentTime = _system->getMillis();
}
void PrinceEngine::addInv(int heroId, int item, bool addItemQuiet) {
Hero *hero = nullptr;
if (!heroId) {
hero = _mainHero;
} else if (heroId == 1) {
hero = _secondHero;
}
if (hero != nullptr) {
if (hero->_inventory.size() < kMaxItems) {
if (item != 0x7FFF) {
hero->_inventory.push_back(item);
}
if (!addItemQuiet) {
addInvObj();
}
_interpreter->setResult(0);
} else {
_interpreter->setResult(1);
}
}
}
void PrinceEngine::remInv(int heroId, int item) {
Hero *hero = nullptr;
if (!heroId) {
hero = _mainHero;
} else if (heroId == 1) {
hero = _secondHero;
}
if (hero != nullptr) {
for (uint i = 0; i < hero->_inventory.size(); i++) {
if (hero->_inventory[i] == item) {
hero->_inventory.remove_at(i);
_interpreter->setResult(0);
return;
}
}
}
_interpreter->setResult(1);
}
void PrinceEngine::clearInv(int heroId) {
switch (heroId) {
case 0:
_mainHero->_inventory.clear();
break;
case 1:
_secondHero->_inventory.clear();
break;
default:
error("clearInv() - wrong hero slot");
break;
}
}
void PrinceEngine::swapInv(int heroId) {
Common::Array<int> tempInv;
Hero *hero = nullptr;
if (!heroId) {
hero = _mainHero;
} else if (heroId == 1) {
hero = _secondHero;
}
if (hero != nullptr) {
for (uint i = 0; i < hero->_inventory.size(); i++) {
tempInv.push_back(hero->_inventory[i]);
}
hero->_inventory.clear();
for (uint i = 0; i < hero->_inventory2.size(); i++) {
hero->_inventory.push_back(hero->_inventory2[i]);
}
hero->_inventory2.clear();
for (uint i = 0; i < tempInv.size(); i++) {
hero->_inventory2.push_back(tempInv[i]);
}
tempInv.clear();
}
}
void PrinceEngine::addInvObj() {
changeCursor(0);
prepareInventoryToView();
_inventoryBackgroundRemember = true;
drawScreen();
Graphics::Surface *suitcase = _suitcaseBmp->getSurface();
if (!_flags->getFlagValue(Flags::CURSEBLINK)) {
loadSample(27, "PRZEDMIO.WAV");
playSample(27, 0);
_mst_shadow2 = 1;
while (_mst_shadow2 < 512) {
rememberScreenInv();
_graph->drawTransparentSurface(_graph->_screenForInventory, 0, 0, suitcase);
drawInvItems();
_graph->update(_graph->_screenForInventory);
_mst_shadow2 += 50;
Common::Event event;
Common::EventManager *eventMan = _system->getEventManager();
eventMan->pollEvent(event);
if (shouldQuit()) {
return;
}
pausePrinceEngine();
}
while (_mst_shadow2 > 256) {
rememberScreenInv();
_graph->drawTransparentSurface(_graph->_screenForInventory, 0, 0, suitcase);
drawInvItems();
_graph->update(_graph->_screenForInventory);
_mst_shadow2 -= 42;
Common::Event event;
Common::EventManager *eventMan = _system->getEventManager();
eventMan->pollEvent(event);
if (shouldQuit()) {
return;
}
pausePrinceEngine();
}
} else {
//CURSEBLINK:
for (int i = 0; i < 3; i++) {
_mst_shadow2 = 256;
while (_mst_shadow2 < 512) {
rememberScreenInv();
_graph->drawTransparentSurface(_graph->_screenForInventory, 0, 0, suitcase);
drawInvItems();
_graph->update(_graph->_screenForInventory);
_mst_shadow2 += 50;
Common::Event event;
Common::EventManager *eventMan = _system->getEventManager();
eventMan->pollEvent(event);
if (shouldQuit()) {
return;
}
pausePrinceEngine();
}
while (_mst_shadow2 > 256) {
rememberScreenInv();
_graph->drawTransparentSurface(_graph->_screenForInventory, 0, 0, suitcase);
drawInvItems();
_graph->update(_graph->_screenForInventory);
_mst_shadow2 -= 50;
Common::Event event;
Common::EventManager *eventMan = _system->getEventManager();
eventMan->pollEvent(event);
if (shouldQuit()) {
return;
}
pausePrinceEngine();
}
}
}
_mst_shadow2 = 0;
for (int i = 0; i < 20; i++) {
rememberScreenInv();
_graph->drawTransparentSurface(_graph->_screenForInventory, 0, 0, suitcase);
drawInvItems();
_graph->update(_graph->_screenForInventory);
Common::Event event;
Common::EventManager *eventMan = _system->getEventManager();
eventMan->pollEvent(event);
if (shouldQuit()) {
return;
}
pausePrinceEngine();
}
}
void PrinceEngine::rememberScreenInv() {
_graph->_screenForInventory->copyFrom(*_graph->_frontScreen);
}
void PrinceEngine::inventoryFlagChange(bool inventoryState) {
if (inventoryState) {
_showInventoryFlag = true;
_inventoryBackgroundRemember = true;
} else {
_showInventoryFlag = false;
}
}
void PrinceEngine::prepareInventoryToView() {
_invMobList.clear();
int invItem = _mainHero->_inventory.size();
_invLine = invItem / 3;
if (invItem % 3) {
_invLine++;
}
if (_invLine < 4) {
_invLine = 4;
}
_maxInvW = (374 - 2 * _invLine) / _invLine;
_invLineW = _maxInvW - 2;
int currInvX = _invLineX;
int currInvY = _invLineY;
Common::MemoryReadStream stream(_invTxt, _invTxtSize);
byte c;
uint item = 0;
for (int i = 0; i < _invLines; i++) {
for (int j = 0; j < _invLine; j++) {
Mob tempMobItem;
if (item < _mainHero->_inventory.size()) {
int itemNr = _mainHero->_inventory[item];
tempMobItem._visible = 0;
tempMobItem._mask = itemNr;
tempMobItem._rect = Common::Rect(currInvX + _picWindowX, currInvY, currInvX + _picWindowX + _invLineW - 1, currInvY + _invLineH - 1);
tempMobItem._type = 0; // to work with checkMob()
tempMobItem._name = "";
tempMobItem._examText = "";
int txtOffset = READ_LE_UINT32(&_invTxt[itemNr * 8]);
int examTxtOffset = READ_LE_UINT32(&_invTxt[itemNr * 8 + 4]);
stream.seek(txtOffset);
while ((c = stream.readByte())) {
tempMobItem._name += c;
}
stream.seek(examTxtOffset);
while ((c = stream.readByte())) {
tempMobItem._examText += c;
}
_invMobList.push_back(tempMobItem);
}
currInvX += _invLineW + _invLineSkipX;
item++;
}
currInvX = _invLineX;
currInvY += _invLineSkipY + _invLineH;
}
}
void PrinceEngine::drawInvItems() {
int currInvX = _invLineX;
int currInvY = _invLineY;
uint item = 0;
for (int i = 0; i < _invLines; i++) {
for (int j = 0; j < _invLine; j++) {
if (item < _mainHero->_inventory.size()) {
int itemNr = _mainHero->_inventory[item];
_mst_shadow = 0;
if (_mst_shadow2) {
if (!_flags->getFlagValue(Flags::CURSEBLINK)) {
if (item + 1 == _mainHero->_inventory.size()) { // last item in inventory
_mst_shadow = 1;
}
} else if (itemNr == 1 || itemNr == 3 || itemNr == 4 || itemNr == 7) {
_mst_shadow = 1;
}
}
int drawX = currInvX;
int drawY = currInvY;
Graphics::Surface *itemSurface = nullptr;
if (itemNr != 68) {
itemSurface = _allInvList[itemNr].getSurface();
if (itemSurface->h < _maxInvH) {
drawY += (_maxInvH - itemSurface->h) / 2;
}
} else {
// candle item:
if (_candleCounter == 8) {
_candleCounter = 0;
}
itemNr = _candleCounter;
_candleCounter++;
itemNr &= 7;
itemNr += 71;
itemSurface = _allInvList[itemNr].getSurface();
drawY += _allInvList[itemNr]._y + (_maxInvH - 76) / 2 - 200;
}
if (itemSurface->w < _maxInvW) {
drawX += (_maxInvW - itemSurface->w) / 2;
}
if (!_mst_shadow) {
_graph->drawTransparentSurface(_graph->_screenForInventory, drawX, drawY, itemSurface);
} else {
_mst_shadow = _mst_shadow2;
_graph->drawTransparentWithBlendSurface(_graph->_screenForInventory, drawX, drawY, itemSurface);
}
}
currInvX += _invLineW + _invLineSkipX;
item++;
}
currInvX = _invLineX;
currInvY += _invLineSkipY + _invLineH;
}
}
void PrinceEngine::walkTo() {
if (_mainHero->_visible) {
_mainHero->freeHeroAnim();
_mainHero->freeOldMove();
_interpreter->storeNewPC(_script->_scriptInfo.usdCode);
int destX, destY;
if (_optionsMob != -1) {
destX = _mobList[_optionsMob]._examPosition.x;
destY = _mobList[_optionsMob]._examPosition.y;
_mainHero->_destDirection = _mobList[_optionsMob]._examDirection;
} else {
Common::Point mousePos = _system->getEventManager()->getMousePos();
destX = mousePos.x + _picWindowX;
destY = mousePos.y + _picWindowY;
_mainHero->_destDirection = 0;
}
_mainHero->_coords = makePath(kMainHero, _mainHero->_middleX, _mainHero->_middleY, destX, destY);
if (_mainHero->_coords != nullptr) {
_mainHero->_currCoords = _mainHero->_coords;
_mainHero->_dirTab = _directionTable;
_mainHero->_currDirTab = _directionTable;
_directionTable = nullptr;
_mainHero->_state = Hero::kHeroStateMove;
moveShandria();
}
}
}
void PrinceEngine::moveRunHero(int heroId, int x, int y, int dir, bool runHeroFlag) {
Hero *hero = nullptr;
if (!heroId) {
hero = _mainHero;
} else if (heroId == 1) {
hero = _secondHero;
}
if (hero != nullptr) {
if (dir) {
hero->_destDirection = dir;
}
if (x || y) {
hero->freeOldMove();
hero->_coords = makePath(heroId, hero->_middleX, hero->_middleY, x, y);
if (hero->_coords != nullptr) {
hero->_currCoords = hero->_coords;
hero->_dirTab = _directionTable;
hero->_currDirTab = _directionTable;
_directionTable = nullptr;
if (runHeroFlag) {
hero->_state = Hero::kHeroStateRun;
} else {
hero->_state = Hero::kHeroStateMove;
}
if (heroId == kMainHero && _mouseFlag) {
moveShandria();
}
}
} else {
hero->freeOldMove();
hero->_state = Hero::kHeroStateTurn;
}
hero->freeHeroAnim();
hero->_visible = 1;
}
}
void PrinceEngine::leftMouseButton() {
_flags->setFlagValue(Flags::ESCAPED2, 1); // skip intro animation
_flags->setFlagValue(Flags::LMOUSE, 1);
if (_flags->getFlagValue(Flags::POWERENABLED)) {
_flags->setFlagValue(Flags::MBFLAG, 1);
}
if (_mouseFlag) {
int option = 0;
int optionEvent = -1;
if (_optionsFlag) {
if (_optionEnabled < _optionsNumber && _optionEnabled != -1) {
option = _optionEnabled;
_optionsFlag = 0;
} else {
return;
}
} else {
_optionsMob = _selectedMob;
if (_optionsMob == -1) {
walkTo();
return;
}
option = 0;
}
//do_option
if (_currentPointerNumber != 2) {
//skip_use_code
int optionScriptOffset = _room->getOptionOffset(option);
if (optionScriptOffset != 0) {
optionEvent = _script->scanMobEvents(_optionsMob, optionScriptOffset);
}
if (optionEvent == -1) {
if (!option) {
walkTo();
return;
} else {
optionEvent = _script->getOptionStandardOffset(option);
}
}
} else if (_selectedMode) {
//give_item
if (_room->_itemGive) {
optionEvent = _script->scanMobEventsWithItem(_optionsMob, _room->_itemGive, _selectedItem);
}
if (optionEvent == -1) {
//standard_giveitem
optionEvent = _script->_scriptInfo.stdGiveItem;
}
} else {
if (_room->_itemUse) {
optionEvent = _script->scanMobEventsWithItem(_optionsMob, _room->_itemUse, _selectedItem);
_flags->setFlagValue(Flags::SELITEM, _selectedItem);
}
if (optionEvent == -1) {
//standard_useitem
optionEvent = _script->_scriptInfo.stdUseItem;
}
}
_interpreter->storeNewPC(optionEvent);
_flags->setFlagValue(Flags::CURRMOB, _selectedMob);
_selectedMob = -1;
_optionsMob = -1;
} else {
if (!_flags->getFlagValue(Flags::POWERENABLED)) {
if (!_flags->getFlagValue(Flags::NOCLSTEXT)) {
for (int slot = 0; slot < kMaxTexts; slot++) {
if (slot != 9) {
Text& text = _textSlots[slot];
if (!text._str) {
continue;
}
text._str = 0;
text._time = 0;
}
}
_mainHero->_talkTime = 0;
_secondHero->_talkTime = 0;
}
}
}
}
void PrinceEngine::rightMouseButton() {
if (_flags->getFlagValue(Flags::POWERENABLED)) {
_flags->setFlagValue(Flags::MBFLAG, 2);
}
if (_mouseFlag && _mouseFlag != 3) {
_mainHero->freeOldMove();
_secondHero->freeOldMove();
_interpreter->storeNewPC(0);
if (_currentPointerNumber < 2) {
enableOptions(true);
} else {
_currentPointerNumber = 1;
changeCursor(1);
}
}
}
void PrinceEngine::inventoryLeftMouseButton() {
if (!_mouseFlag) {
_textSlots[0]._time = 0;
_textSlots[0]._str = nullptr;
stopSample(28);
}
if (_optionsFlag == 1) {
if (_selectedMob != -1) {
if (_optionEnabled < _invOptionsNumber) {
_optionsFlag = 0;
} else {
return;
}
} else {
error("PrinceEngine::inventoryLeftMouseButton() - optionsFlag = 1, selectedMob = 0");
if (_currentPointerNumber == 2) {
changeCursor(1);
_currentPointerNumber = 1;
_selectedMob = -1;
_optionsMob = -1;
return;
} else {
return;
}
}
} else {
if (_selectedMob != -1) {
if (_currentPointerNumber != 2) {
if (_invMobList[_selectedMob]._mask != 29) {
_optionEnabled = 0;
} else {
// map item
_optionEnabled = 1;
}
} else {
//use_item_on_item
int invObjUU = _script->scanMobEventsWithItem(_invMobList[_selectedMob]._mask, _script->_scriptInfo.invObjUU, _selectedItem);
if (invObjUU == -1) {
int textNr = 80011; // "I can't do it."
if (_selectedItem == 31 || _invMobList[_selectedMob]._mask == 31) {
textNr = 80020; // "Nothing is happening."
}
_interpreter->setCurrentString(textNr);
printAt(0, 216, (char *)_variaTxt->getString(textNr - 80000), kNormalWidth / 2, 100);
setVoice(0, 28, 1);
playSample(28, 0);
_selectedMob = -1;
_optionsMob = -1;
return;
} else {
_interpreter->storeNewPC(invObjUU);
_flags->setFlagValue(Flags::CURRMOB, _invMobList[_selectedMob]._mask);
_showInventoryFlag = false;
}
}
} else {
return;
}
}
//do_option
if (_optionEnabled == 0) {
int invObjExamEvent = _script->scanMobEvents(_invMobList[_selectedMob]._mask, _script->_scriptInfo.invObjExam);
if (invObjExamEvent == -1) {
// do_standard
printAt(0, 216, (char *)_invMobList[_selectedMob]._examText.c_str(), kNormalWidth / 2, _invExamY);
_interpreter->setCurrentString(_invMobList[_selectedMob]._mask + 70000);
setVoice(0, 28, 1);
playSample(28, 0);
// disableuseuse
changeCursor(0);
_currentPointerNumber = 1;
} else {
_interpreter->storeNewPC(invObjExamEvent);
_flags->setFlagValue(Flags::CURRMOB, _invMobList[_selectedMob]._mask);
_showInventoryFlag = false;
}
} else if (_optionEnabled == 1) {
// not_examine
int invObjUse = _script->scanMobEvents(_invMobList[_selectedMob]._mask, _script->_scriptInfo.invObjUse);
if (invObjUse == -1) {
// do_standard_use
_selectedMode = 0;
_selectedItem = _invMobList[_selectedMob]._mask;
makeInvCursor(_invMobList[_selectedMob]._mask);
_currentPointerNumber = 2;
changeCursor(2);
} else {
_interpreter->storeNewPC(invObjUse);
_flags->setFlagValue(Flags::CURRMOB, _invMobList[_selectedMob]._mask);
_showInventoryFlag = false;
}
} else if (_optionEnabled == 4) {
// do_standard_give
_selectedMode = 1;
_selectedItem = _invMobList[_selectedMob]._mask;
makeInvCursor(_invMobList[_selectedMob]._mask);
_currentPointerNumber = 2;
changeCursor(2);
} else {
// use_item_on_item
int invObjUU = _script->scanMobEventsWithItem(_invMobList[_selectedMob]._mask, _script->_scriptInfo.invObjUU, _selectedItem);
if (invObjUU == -1) {
int textNr = 80011; // "I can't do it."
if (_selectedItem == 31 || _invMobList[_selectedMob]._mask == 31) {
textNr = 80020; // "Nothing is happening."
}
_interpreter->setCurrentString(textNr);
printAt(0, 216, (char *)_variaTxt->getString(textNr - 80000), kNormalWidth / 2, 100);
setVoice(0, 28, 1);
playSample(28, 0);
} else {
_interpreter->storeNewPC(invObjUU);
_flags->setFlagValue(Flags::CURRMOB, _invMobList[_selectedMob]._mask);
_showInventoryFlag = false;
}
}
_selectedMob = -1;
_optionsMob = -1;
}
void PrinceEngine::inventoryRightMouseButton() {
if (_textSlots[0]._str == nullptr) {
enableOptions(false);
}
}
void PrinceEngine::enableOptions(bool checkType) {
if (_optionsFlag != 1) {
changeCursor(1);
_currentPointerNumber = 1;
if (_selectedMob != -1) {
if (checkType) {
if (_mobList[_selectedMob]._type & 0x100) {
return;
}
}
Common::Point mousePos = _system->getEventManager()->getMousePos();
int x1 = mousePos.x - _optionsWidth / 2;
int x2 = mousePos.x + _optionsWidth / 2;
if (x1 < 0) {
x1 = 0;
x2 = _optionsWidth;
} else if (x2 >= kNormalWidth) {
x1 = kNormalWidth - _optionsWidth;
x2 = kNormalWidth;
}
int y1 = mousePos.y - 10;
if (y1 < 0) {
y1 = 0;
}
if (y1 + _optionsHeight >= kNormalHeight) {
y1 = kNormalHeight - _optionsHeight;
}
_optionsMob = _selectedMob;
_optionsX = x1;
_optionsY = y1;
_optionsFlag = 1;
}
}
}
void PrinceEngine::checkOptions() {
if (_optionsFlag) {
Common::Rect optionsRect(_optionsX, _optionsY, _optionsX + _optionsWidth, _optionsY + _optionsHeight);
Common::Point mousePos = _system->getEventManager()->getMousePos();
if (!optionsRect.contains(mousePos)) {
_optionsFlag = 0;
_selectedMob = -1;
return;
}
_graph->drawAsShadowSurface(_graph->_frontScreen, _optionsX, _optionsY, _optionsPic, _graph->_shadowTable50);
_optionEnabled = -1;
int optionsYCord = mousePos.y - (_optionsY + 16);
if (optionsYCord >= 0) {
int selectedOptionNr = optionsYCord / _optionsStep;
if (selectedOptionNr < _optionsNumber) {
_optionEnabled = selectedOptionNr;
}
}
int optionsColor;
int textY = _optionsY + 16;
for (int i = 0; i < _optionsNumber; i++) {
if (i != _optionEnabled) {
optionsColor = _optionsColor1;
} else {
optionsColor = _optionsColor2;
}
Common::String optText;
switch(getLanguage()) {
case Common::PL_POL:
optText = optionsTextPL[i];
break;
case Common::DE_DEU:
optText = optionsTextDE[i];
break;
case Common::EN_ANY:
optText = optionsTextEN[i];
break;
default:
break;
};
uint16 textW = getTextWidth(optText.c_str());
uint16 textX = _optionsX + _optionsWidth / 2 - textW / 2;
_font->drawString(_graph->_frontScreen, optText, textX, textY, textW, optionsColor);
textY += _optionsStep;
}
}
}
void PrinceEngine::checkInvOptions() {
if (_optionsFlag) {
Common::Rect optionsRect(_optionsX, _optionsY, _optionsX + _invOptionsWidth, _optionsY + _invOptionsHeight);
Common::Point mousePos = _system->getEventManager()->getMousePos();
if (!optionsRect.contains(mousePos)) {
_optionsFlag = 0;
_selectedMob = -1;
return;
}
_graph->drawAsShadowSurface(_graph->_screenForInventory, _optionsX, _optionsY, _optionsPicInInventory, _graph->_shadowTable50);
_optionEnabled = -1;
int optionsYCord = mousePos.y - (_optionsY + 16);
if (optionsYCord >= 0) {
int selectedOptionNr = optionsYCord / _invOptionsStep;
if (selectedOptionNr < _invOptionsNumber) {
_optionEnabled = selectedOptionNr;
}
}
int optionsColor;
int textY = _optionsY + 16;
for (int i = 0; i < _invOptionsNumber; i++) {
if (i != _optionEnabled) {
optionsColor = _optionsColor1;
} else {
optionsColor = _optionsColor2;
}
Common::String invText;
switch(getLanguage()) {
case Common::PL_POL:
invText = invOptionsTextPL[i];
break;
case Common::DE_DEU:
invText = invOptionsTextDE[i];
break;
case Common::EN_ANY:
invText = invOptionsTextEN[i];
break;
default:
error("Unknown game language %d", getLanguage());
break;
};
uint16 textW = getTextWidth(invText.c_str());
uint16 textX = _optionsX + _invOptionsWidth / 2 - textW / 2;
_font->drawString(_graph->_screenForInventory, invText, textX, textY, _graph->_screenForInventory->w, optionsColor);
textY += _invOptionsStep;
}
}
}
void PrinceEngine::displayInventory() {
_mainHero->freeOldMove();
_secondHero->freeOldMove();
_interpreter->setFgOpcodePC(0);
stopAllSamples();
prepareInventoryToView();
while (!shouldQuit()) {
if (_textSlots[0]._str != nullptr) {
changeCursor(0);
} else {
changeCursor(_currentPointerNumber);
Common::Rect inventoryRect(_invX1, _invY1, _invX1 + _invWidth, _invY1 + _invHeight);
Common::Point mousePos = _system->getEventManager()->getMousePos();
if (!_invCurInside && inventoryRect.contains(mousePos)) {
_invCurInside = true;
}
if (_invCurInside && !inventoryRect.contains(mousePos)) {
inventoryFlagChange(false);
_invCurInside = false;
break;
}
}
rememberScreenInv();
Graphics::Surface *suitcase = _suitcaseBmp->getSurface();
_graph->drawTransparentSurface(_graph->_screenForInventory, 0, 0, suitcase);
drawInvItems();
showTexts(_graph->_screenForInventory);
if (!_optionsFlag && _textSlots[0]._str == nullptr) {
_selectedMob = checkMob(_graph->_screenForInventory, _invMobList, false);
}
checkInvOptions();
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_LBUTTONDOWN:
inventoryLeftMouseButton();
break;
case Common::EVENT_RBUTTONDOWN:
inventoryRightMouseButton();
break;
default:
break;
}
}
if (!_showInventoryFlag) {
break;
}
if (shouldQuit())
return;
getDebugger()->onFrame();
_graph->update(_graph->_screenForInventory);
pausePrinceEngine();
}
if (_currentPointerNumber == 2) {
_flags->setFlagValue(Flags::SELITEM, _selectedItem);
} else {
_flags->setFlagValue(Flags::SELITEM, 0);
}
}
void PrinceEngine::createDialogBox(int dialogBoxNr) {
_dialogLines = 0;
int amountOfDialogOptions = 0;
int dialogDataValue = (int)READ_LE_UINT32(_dialogData);
byte c;
int sentenceNumber;
_dialogText = _dialogBoxAddr[dialogBoxNr];
byte *dialogText = _dialogText;
while ((sentenceNumber = *dialogText) != 0xFF) {
dialogText++;
if (!(dialogDataValue & (1 << sentenceNumber))) {
_dialogLines += calcTextLines((const char *)dialogText);
amountOfDialogOptions++;
}
do {
c = *dialogText;
dialogText++;
} while (c);
}
_dialogHeight = _font->getFontHeight() * _dialogLines + _dialogLineSpace * (amountOfDialogOptions + 1);
_dialogImage = new Graphics::Surface();
_dialogImage->create(_dialogWidth, _dialogHeight, Graphics::PixelFormat::createFormatCLUT8());
Common::Rect dBoxRect(0, 0, _dialogWidth, _dialogHeight);
_dialogImage->fillRect(dBoxRect, _graph->kShadowColor);
}
void PrinceEngine::dialogRun() {
_dialogFlag = true;
while (!shouldQuit()) {
_interpreter->stepBg();
drawScreen();
int dialogX = (640 - _dialogWidth) / 2;
int dialogY = 460 - _dialogHeight;
_graph->drawAsShadowSurface(_graph->_frontScreen, dialogX, dialogY, _dialogImage, _graph->_shadowTable50);
int dialogSkipLeft = 14;
int dialogSkipUp = 10;
int dialogTextX = dialogX + dialogSkipLeft;
int dialogTextY = dialogY + dialogSkipUp;
Common::Point mousePos = _system->getEventManager()->getMousePos();
byte c;
int sentenceNumber;
byte *dialogText = _dialogText;
byte *dialogCurrentText = nullptr;
int dialogSelected = -1;
int dialogDataValue = (int)READ_LE_UINT32(_dialogData);
while ((sentenceNumber = *dialogText) != 0xFF) {
dialogText++;
int actualColor = _dialogColor1;
if (!(dialogDataValue & (1 << sentenceNumber))) {
if (getLanguage() == Common::DE_DEU) {
correctStringDEU((char *)dialogText);
}
Common::Array<Common::String> lines;
_font->wordWrapText((const char *)dialogText, _graph->_frontScreen->w, lines);
Common::Rect dialogOption(dialogTextX, dialogTextY - dialogSkipUp / 2, dialogX + _dialogWidth - dialogSkipLeft, dialogTextY + lines.size() * _font->getFontHeight() + dialogSkipUp / 2 - 1);
if (dialogOption.contains(mousePos)) {
actualColor = _dialogColor2;
dialogSelected = sentenceNumber;
dialogCurrentText = dialogText;
}
for (uint j = 0; j < lines.size(); j++) {
_font->drawString(_graph->_frontScreen, lines[j], dialogTextX, dialogTextY, _graph->_frontScreen->w, actualColor);
dialogTextY += _font->getFontHeight();
}
dialogTextY += _dialogLineSpace;
}
do {
c = *dialogText;
dialogText++;
} while (c);
}
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_LBUTTONDOWN:
if (dialogSelected != -1) {
dialogLeftMouseButton(dialogCurrentText, dialogSelected);
_dialogFlag = false;
}
break;
default:
break;
}
}
if (shouldQuit()) {
return;
}
if (!_dialogFlag) {
break;
}
getDebugger()->onFrame();
_graph->update(_graph->_frontScreen);
pausePrinceEngine();
}
_dialogImage->free();
delete _dialogImage;
_dialogImage = nullptr;
_dialogFlag = false;
}
void PrinceEngine::dialogLeftMouseButton(byte *string, int dialogSelected) {
_interpreter->setString(string);
talkHero(0);
int dialogDataValue = (int)READ_LE_UINT32(_dialogData);
dialogDataValue |= (1u << dialogSelected);
WRITE_LE_UINT32(_dialogData, dialogDataValue);
_flags->setFlagValue(Flags::BOXSEL, dialogSelected + 1);
setVoice(0, 28, dialogSelected + 1);
_flags->setFlagValue(Flags::VOICE_H_LINE, _dialogOptLines[dialogSelected * 4]);
_flags->setFlagValue(Flags::VOICE_A_LINE, _dialogOptLines[dialogSelected * 4 + 1]);
_flags->setFlagValue(Flags::VOICE_B_LINE, _dialogOptLines[dialogSelected * 4 + 2]);
_interpreter->setString(_dialogOptAddr[dialogSelected]);
}
void PrinceEngine::talkHero(int slot) {
// heroSlot = textSlot (slot 0 or 1)
Text &text = _textSlots[slot];
int lines = calcTextLines((const char *)_interpreter->getString());
int time = lines * 30;
if (slot == 0) {
text._color = 220; // TODO - test this
_mainHero->_state = Hero::kHeroStateTalk;
_mainHero->_talkTime = time;
text._x = _mainHero->_middleX;
text._y = _mainHero->_middleY - _mainHero->_scaledFrameYSize;
} else {
text._color = _flags->getFlagValue(Flags::KOLOR); // TODO - test this
_secondHero->_state = Hero::kHeroStateTalk;
_secondHero->_talkTime = time;
text._x = _secondHero->_middleX;
text._y = _secondHero->_middleY - _secondHero->_scaledFrameYSize;
}
text._time = time;
if (getLanguage() == Common::DE_DEU) {
correctStringDEU((char *)_interpreter->getString());
}
text._str = (const char *)_interpreter->getString();
_interpreter->increaseString();
}
void PrinceEngine::doTalkAnim(int animNumber, int slot, AnimType animType) {
Text &text = _textSlots[slot];
int lines = calcTextLines((const char *)_interpreter->getString());
int time = lines * 30;
if (animType == kNormalAnimation) {
Anim &normAnim = _normAnimList[animNumber];
if (normAnim._animData != nullptr) {
if (!normAnim._state) {
if (normAnim._currW && normAnim._currH) {
text._color = _flags->getFlagValue(Flags::KOLOR);
text._x = normAnim._currX + normAnim._currW / 2;
text._y = normAnim._currY - 10;
}
}
}
} else if (animType == kBackgroundAnimation) {
if (!_backAnimList[animNumber].backAnims.empty()) {
int currAnim = _backAnimList[animNumber]._seq._currRelative;
Anim &backAnim = _backAnimList[animNumber].backAnims[currAnim];
if (backAnim._animData != nullptr) {
if (!backAnim._state) {
if (backAnim._currW && backAnim._currH) {
text._color = _flags->getFlagValue(Flags::KOLOR);
text._x = backAnim._currX + backAnim._currW / 2;
text._y = backAnim._currY - 10;
}
}
}
}
} else {
error("doTalkAnim() - wrong animType: %d", animType);
}
text._time = time;
if (getLanguage() == Common::DE_DEU) {
correctStringDEU((char *)_interpreter->getString());
}
text._str = (const char *)_interpreter->getString();
_interpreter->increaseString();
}
void PrinceEngine::freeNormAnim(int slot) {
if (!_normAnimList.empty()) {
_normAnimList[slot]._state = 1;
if (_normAnimList[slot]._animData != nullptr) {
delete _normAnimList[slot]._animData;
_normAnimList[slot]._animData = nullptr;
}
if (_normAnimList[slot]._shadowData != nullptr) {
delete _normAnimList[slot]._shadowData;
_normAnimList[slot]._shadowData = nullptr;
}
}
}
void PrinceEngine::freeAllNormAnims() {
for (int i = 0; i < kMaxNormAnims; i++) {
freeNormAnim(i);
}
}
void PrinceEngine::getCurve() {
_flags->setFlagValue(Flags::TORX1, _curveData[_curvPos]);
_flags->setFlagValue(Flags::TORY1, _curveData[_curvPos + 1]);
_curvPos += 2;
}
void PrinceEngine::makeCurve() {
_curvPos = 0;
int x1 = _flags->getFlagValue(Flags::TORX1);
int y1 = _flags->getFlagValue(Flags::TORY1);
int x2 = _flags->getFlagValue(Flags::TORX2);
int y2 = _flags->getFlagValue(Flags::TORY2);
for (int i = 0; i < kCurveLen; i++) {
int sum1 = x1 * curveValues[i][0];
sum1 += (x2 + (x1 - x2) / 2) * curveValues[i][1];
sum1 += x2 * curveValues[i][2];
sum1 += x2 * curveValues[i][3];
int sum2 = y1 * curveValues[i][0];
sum2 += (y2 - 20) * curveValues[i][1];
sum2 += (y2 - 10) * curveValues[i][2];
sum2 += y2 * curveValues[i][3];
_curveData[i * 2] = (sum1 >> 15);
_curveData[i * 2 + 1] = (sum2 >> 15);
}
}
void PrinceEngine::mouseWeirdo() {
if (_mouseFlag == 3) {
int weirdDir = _randomSource.getRandomNumber(3);
Common::Point mousePos = _system->getEventManager()->getMousePos();
switch (weirdDir) {
case 0:
mousePos.x += kCelStep;
break;
case 1:
mousePos.x -= kCelStep;
break;
case 2:
mousePos.y += kCelStep;
break;
case 3:
mousePos.y -= kCelStep;
break;
}
mousePos.x = CLIP(mousePos.x, (int16) 315, (int16) 639);
_flags->setFlagValue(Flags::MXFLAG, mousePos.x);
mousePos.y = CLIP(mousePos.y, (int16) 0, (int16) 170);
_flags->setFlagValue(Flags::MYFLAG, mousePos.y);
_system->warpMouse(mousePos.x, mousePos.y);
}
}
void PrinceEngine::showPower() {
if (_flags->getFlagValue(Flags::POWERENABLED)) {
int power = _flags->getFlagValue(Flags::POWER);
byte *dst = (byte *)_graph->_frontScreen->getBasePtr(kPowerBarPosX, kPowerBarPosY);
for (int y = 0; y < kPowerBarHeight; y++) {
byte *dst2 = dst;
for (int x = 0; x < kPowerBarWidth; x++, dst2++) {
*dst2 = kPowerBarBackgroundColor;
}
dst += _graph->_frontScreen->pitch;
}
if (power) {
dst = (byte *)_graph->_frontScreen->getBasePtr(kPowerBarPosX, kPowerBarGreenPosY);
for (int y = 0; y < kPowerBarGreenHeight; y++) {
byte *dst2 = dst;
for (int x = 0; x < power + 1; x++, dst2++) {
if (x < 58) {
*dst2 = kPowerBarGreenColor1;
} else {
*dst2 = kPowerBarGreenColor2;
}
}
dst += _graph->_frontScreen->pitch;
}
}
_graph->change();
}
}
void PrinceEngine::scrollCredits() {
byte *scrollAdress = _creditsData;
while (!shouldQuit()) {
for (int scrollPos = 0; scrollPos > -23; scrollPos--) {
const Graphics::Surface *roomSurface = _roomBmp->getSurface();
if (roomSurface) {
_graph->draw(_graph->_frontScreen, roomSurface);
}
char *s = (char *)scrollAdress;
int drawY = scrollPos;
for (int i = 0; i < 22; i++) {
Common::String line;
char *linePos = s;
while ((*linePos != 13)) {
line += *linePos;
linePos++;
}
if (!line.empty()) {
int drawX = (kNormalWidth - getTextWidth(line.c_str())) / 2;
_font->drawString(_graph->_frontScreen, line, drawX, drawY, _graph->_frontScreen->w, 217);
}
char letter1;
bool gotIt1 = false;
do {
letter1 = *s;
s++;
if (letter1 == 13) {
if (*s == 10) {
s++;
}
if (*s != 35) {
gotIt1 = true;
}
break;
}
} while (letter1 != 35);
if (gotIt1) {
drawY += 23;
} else {
break;
}
}
Common::Event event;
Common::EventManager *eventMan = _system->getEventManager();
while (eventMan->pollEvent(event)) {
if (event.type == Common::EVENT_KEYDOWN) {
if (event.kbd.keycode == Common::KEYCODE_ESCAPE) {
blackPalette();
return;
}
}
}
if (shouldQuit()) {
return;
}
_graph->change();
_graph->update(_graph->_frontScreen);
pausePrinceEngine(kFPS * 2);
}
char letter2;
byte *scan2 = scrollAdress;
bool gotIt2 = false;
do {
letter2 = *scan2;
scan2++;
if (letter2 == 13) {
if (*scan2 == 10) {
scan2++;
}
if (*scan2 != 35) {
gotIt2 = true;
}
break;
}
} while (letter2 != 35);
if (gotIt2) {
scrollAdress = scan2;
} else {
break;
}
}
blackPalette();
}
// Modified version of Graphics::drawLine() to allow breaking the loop and return value
int PrinceEngine::drawLine(int x0, int y0, int x1, int y1, int (*plotProc)(int, int, void *), void *data) {
// Bresenham's line algorithm, as described by Wikipedia
const bool steep = ABS(y1 - y0) > ABS(x1 - x0);
if (steep) {
SWAP(x0, y0);
SWAP(x1, y1);
}
const int delta_x = ABS(x1 - x0);
const int delta_y = ABS(y1 - y0);
const int delta_err = delta_y;
int x = x0;
int y = y0;
int err = 0;
const int x_step = (x0 < x1) ? 1 : -1;
const int y_step = (y0 < y1) ? 1 : -1;
int stopFlag = 0;
if (steep)
stopFlag = (*plotProc)(y, x, data);
else
stopFlag = (*plotProc)(x, y, data);
while (x != x1 && !stopFlag) {
x += x_step;
err += delta_err;
if (2 * err > delta_x) {
y += y_step;
err -= delta_x;
}
if (steep)
stopFlag = (*plotProc)(y, x, data);
else
stopFlag = (*plotProc)(x, y, data);
}
return stopFlag;
}
int PrinceEngine::getPixelAddr(byte *pathBitmap, int x, int y) {
int mask = 128 >> (x & 7);
byte value = pathBitmap[x / 8 + y * 80];
return (mask & value);
}
void PrinceEngine::findPoint(int x, int y) {
_fpX = x;
_fpY = y;
if (getPixelAddr(_roomPathBitmap, x, y)) {
return;
}
int fpL = x;
int fpU = y;
int fpR = x;
int fpD = y;
while (1) {
if (fpD != kMaxPicHeight) {
if (getPixelAddr(_roomPathBitmap, x, fpD)) {
_fpX = x;
_fpY = fpD;
break;
}
fpD++;
}
if (fpU) {
if (getPixelAddr(_roomPathBitmap, x, fpU)) {
_fpX = x;
_fpY = fpU;
break;
}
fpU--;
}
if (fpL) {
if (getPixelAddr(_roomPathBitmap, fpL, y)) {
_fpX = fpL;
_fpY = y;
break;
}
fpL--;
}
if (fpR != _sceneWidth) {
if (getPixelAddr(_roomPathBitmap, fpR, y)) {
_fpX = fpR;
_fpY = y;
break;
}
fpR++;
}
if (!fpU && (fpD == kMaxPicHeight)) {
if (!fpL && (fpR == _sceneWidth)) {
break;
}
}
}
}
Direction PrinceEngine::makeDirection(int x1, int y1, int x2, int y2) {
if (x1 != x2) {
if (y1 != y2) {
if (x1 > x2) {
if (y1 > y2) {
if (x1 - x2 >= y1 - y2) {
return kDirLU;
} else {
return kDirUL;
}
} else {
if (x1 - x2 >= y2 - y1) {
return kDirLD;
} else {
return kDirDL;
}
}
} else {
if (y1 > y2) {
if (x2 - x1 >= y1 - y2) {
return kDirRU;
} else {
return kDirUR;
}
} else {
if (x2 - x1 >= y2 - y1) {
return kDirRD;
} else {
return kDirDR;
}
}
}
} else {
if (x1 >= x2) {
return kDirL;
} else {
return kDirR;
}
}
} else {
if (y1 >= y2) {
return kDirU;
} else {
return kDirD;
}
}
}
void PrinceEngine::specialPlot(int x, int y) {
if (_coords < _coordsBufEnd) {
WRITE_LE_UINT16(_coords, x);
_coords += 2;
WRITE_LE_UINT16(_coords, y);
_coords += 2;
specialPlot2(x, y);
}
}
void PrinceEngine::specialPlot2(int x, int y) {
int mask = 128 >> (x & 7);
_roomPathBitmapTemp[x / 8 + y * 80] |= mask;
}
void PrinceEngine::specialPlotInside(int x, int y) {
if (_coords < _coordsBufEnd) {
WRITE_LE_UINT16(_coords, x);
_coords += 2;
WRITE_LE_UINT16(_coords, y);
_coords += 2;
}
}
int PrinceEngine::plotTraceLine(int x, int y, void *data) {
PrinceEngine *traceLine = (PrinceEngine *)data;
if (!traceLine->_traceLineFirstPointFlag) {
if (!traceLine->getPixelAddr(traceLine->_roomPathBitmapTemp, x, y)) {
if (traceLine->getPixelAddr(traceLine->_roomPathBitmap, x, y)) {
traceLine->specialPlotInside(x, y);
traceLine->_traceLineLen++;
return 0;
} else {
return -1;
}
} else {
return 1;
}
} else {
traceLine->_traceLineFirstPointFlag = false;
return 0;
}
}
int PrinceEngine::leftDownDir() {
if (!checkLeftDownDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkLeftDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkDownDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkLeftUpDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkRightDownDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkUpDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkRightDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkRightUpDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
return -1;
}
int PrinceEngine::leftDir() {
if (!checkLeftDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkLeftUpDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkLeftDownDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkUpDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkDownDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkRightUpDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkRightDownDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkRightDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
return -1;
}
int PrinceEngine::leftUpDir() {
if (!checkLeftUpDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkLeftDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkUpDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkLeftDownDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkRightUpDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkDownDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkRightDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkRightDownDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
return -1;
}
int PrinceEngine::rightDownDir() {
if (!checkRightDownDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkRightDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkDownDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkRightUpDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkLeftDownDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkUpDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkLeftDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkLeftUpDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
return -1;
}
int PrinceEngine::rightDir() {
if (!checkRightDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkRightUpDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkRightDownDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkUpDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkDownDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkLeftUpDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkLeftDownDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkLeftDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
return -1;
}
int PrinceEngine::rightUpDir() {
if (!checkRightUpDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkRightDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkUpDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkRightDownDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkLeftUpDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkDownDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkLeftDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkLeftDownDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
return -1;
}
int PrinceEngine::upLeftDir() {
if (!checkLeftUpDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkUpDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkLeftDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkRightUpDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkLeftDownDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkRightDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkDownDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkRightDownDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
return -1;
}
int PrinceEngine::upDir() {
if (!checkUpDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkLeftUpDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkRightUpDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkLeftDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkRightDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkLeftDownDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkRightDownDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkDownDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
return -1;
}
int PrinceEngine::upRightDir() {
if (!checkRightUpDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkUpDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkRightDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkLeftUpDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkRightDownDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkLeftDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkDownDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkLeftDownDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
return -1;
}
int PrinceEngine::downLeftDir() {
if (!checkLeftDownDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkDownDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkLeftDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkRightDownDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkLeftUpDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkRightDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkUpDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkRightUpDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
return -1;
}
int PrinceEngine::downDir() {
if (!checkDownDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkRightDownDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkLeftDownDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkRightDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkLeftDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkRightUpDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkLeftUpDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkUpDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
return -1;
}
int PrinceEngine::downRightDir() {
if (!checkRightDownDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkDownDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkRightDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkLeftDownDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkRightUpDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkLeftDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkUpDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
if (!checkLeftUpDir()) {
specialPlot(_checkX, _checkY);
return 0;
}
return -1;
}
int PrinceEngine::cpe() {
if ((*(_checkBitmap - kPBW) & _checkMask)) {
if ((*(_checkBitmap + kPBW) & _checkMask)) {
int value;
switch (_checkMask) {
case 128:
value = READ_LE_UINT16(_checkBitmap - 1);
value &= 0x4001;
if (value != 0x4001) {
return 0;
}
break;
case 64:
value = *_checkBitmap;
value &= 0xA0;
if (value != 0xA0) {
return 0;
}
break;
case 32:
value = *_checkBitmap;
value &= 0x50;
if (value != 0x50) {
return 0;
}
break;
case 16:
value = *_checkBitmap;
value &= 0x28;
if (value != 0x28) {
return 0;
}
break;
case 8:
value = *_checkBitmap;
value &= 0x14;
if (value != 0x14) {
return 0;
}
break;
case 4:
value = *_checkBitmap;
value &= 0xA;
if (value != 0xA) {
return 0;
}
break;
case 2:
value = *_checkBitmap;
value &= 0x5;
if (value != 0x5) {
return 0;
}
break;
case 1:
value = READ_LE_UINT16(_checkBitmap);
value &= 0x8002;
if (value != 0x8002) {
return 0;
}
break;
default:
error("Wrong _checkMask value - cpe()");
break;
}
_checkX = _rembX;
_checkY = _rembY;
_checkBitmapTemp = _rembBitmapTemp;
_checkBitmap = _rembBitmap;
_checkMask = _rembMask;
return -1;
}
return 0;
}
return 0;
}
int PrinceEngine::checkLeftDownDir() {
if (_checkX && _checkY != (kMaxPicHeight / 2 - 1)) {
int tempMask = _checkMask;
if (tempMask != 128) {
tempMask <<= 1;
if ((*(_checkBitmap + kPBW) & tempMask)) {
if (!(*(_checkBitmapTemp + kPBW) & tempMask)) {
_checkBitmap += kPBW;
_checkBitmapTemp += kPBW;
_checkMask = tempMask;
} else {
return 1;
}
} else {
return -1;
}
} else {
if ((*(_checkBitmap + kPBW - 1) & 1)) {
if (!(*(_checkBitmapTemp + kPBW - 1) & 1)) {
_checkBitmap += (kPBW - 1);
_checkBitmapTemp += (kPBW - 1);
_checkMask = 1;
} else {
return 1;
}
} else {
return -1;
}
}
_checkX--;
_checkY++;
return cpe();
} else {
return -1;
}
}
int PrinceEngine::checkLeftDir() {
if (_checkX) {
int tempMask = _checkMask;
if (tempMask != 128) {
tempMask <<= 1;
if ((*(_checkBitmap) & tempMask)) {
if (!(*(_checkBitmapTemp) & tempMask)) {
_checkMask = tempMask;
} else {
return 1;
}
} else {
return -1;
}
} else {
if ((*(_checkBitmap - 1) & 1)) {
if (!(*(_checkBitmapTemp - 1) & 1)) {
_checkBitmap--;
_checkBitmapTemp--;
_checkMask = 1;
} else {
return 1;
}
} else {
return -1;
}
}
_checkX--;
return cpe();
} else {
return -1;
}
}
int PrinceEngine::checkDownDir() {
if (_checkY != (kMaxPicHeight / 2 - 1)) {
if ((*(_checkBitmap + kPBW) & _checkMask)) {
if (!(*(_checkBitmapTemp + kPBW) & _checkMask)) {
_checkBitmap += kPBW;
_checkBitmapTemp += kPBW;
_checkY++;
return cpe();
} else {
return 1;
}
} else {
return -1;
}
} else {
return -1;
}
}
int PrinceEngine::checkUpDir() {
if (_checkY) {
if ((*(_checkBitmap - kPBW) & _checkMask)) {
if (!(*(_checkBitmapTemp - kPBW) & _checkMask)) {
_checkBitmap -= kPBW;
_checkBitmapTemp -= kPBW;
_checkY--;
return cpe();
} else {
return 1;
}
} else {
return -1;
}
} else {
return -1;
}
}
int PrinceEngine::checkRightDir() {
if (_checkX != (kMaxPicWidth / 2 - 1)) {
int tempMask = _checkMask;
if (tempMask != 1) {
tempMask >>= 1;
if ((*(_checkBitmap) & tempMask)) {
if (!(*(_checkBitmapTemp) & tempMask)) {
_checkMask = tempMask;
} else {
return 1;
}
} else {
return -1;
}
} else {
if ((*(_checkBitmap + 1) & 128)) {
if (!(*(_checkBitmapTemp + 1) & 128)) {
_checkBitmap++;
_checkBitmapTemp++;
_checkMask = 128;
} else {
return 1;
}
} else {
return -1;
}
}
_checkX++;
return cpe();
} else {
return -1;
}
}
int PrinceEngine::checkLeftUpDir() {
if (_checkX && _checkY) {
int tempMask = _checkMask;
if (tempMask != 128) {
tempMask <<= 1;
if ((*(_checkBitmap - kPBW) & tempMask)) {
if (!(*(_checkBitmapTemp - kPBW) & tempMask)) {
_checkBitmap -= kPBW;
_checkBitmapTemp -= kPBW;
_checkMask = tempMask;
} else {
return 1;
}
} else {
return -1;
}
} else {
if ((*(_checkBitmap - (kPBW + 1)) & 1)) {
if (!(*(_checkBitmapTemp - (kPBW + 1)) & 1)) {
_checkBitmap -= (kPBW + 1);
_checkBitmapTemp -= (kPBW + 1);
_checkMask = 1;
} else {
return 1;
}
} else {
return -1;
}
}
_checkX--;
_checkY--;
return cpe();
} else {
return -1;
}
}
int PrinceEngine::checkRightDownDir() {
if (_checkX != (kMaxPicWidth / 2 - 1) && _checkY != (kMaxPicHeight / 2 - 1)) {
int tempMask = _checkMask;
if (tempMask != 1) {
tempMask >>= 1;
if ((*(_checkBitmap + kPBW) & tempMask)) {
if (!(*(_checkBitmapTemp + kPBW) & tempMask)) {
_checkBitmap += kPBW;
_checkBitmapTemp += kPBW;
_checkMask = tempMask;
} else {
return 1;
}
} else {
return -1;
}
} else {
if ((*(_checkBitmap + kPBW + 1) & 128)) {
if (!(*(_checkBitmapTemp + kPBW + 1) & 128)) {
_checkBitmap += kPBW + 1;
_checkBitmapTemp += kPBW + 1;
_checkMask = 128;
} else {
return 1;
}
} else {
return -1;
}
}
_checkX++;
_checkY++;
return cpe();
} else {
return -1;
}
}
int PrinceEngine::checkRightUpDir() {
if (_checkX != (kMaxPicWidth / 2 - 1) && _checkY) {
int tempMask = _checkMask;
if (tempMask != 1) {
tempMask >>= 1;
if ((*(_checkBitmap - kPBW) & tempMask)) {
if (!(*(_checkBitmapTemp - kPBW) & tempMask)) {
_checkBitmap -= kPBW;
_checkBitmapTemp -= kPBW;
_checkMask = tempMask;
} else {
return 1;
}
} else {
return -1;
}
} else {
if ((*(_checkBitmap - kPBW + 1) & 128)) {
if (!(*(_checkBitmapTemp - kPBW + 1) & 128)) {
_checkBitmap -= (kPBW - 1);
_checkBitmapTemp -= (kPBW - 1);
_checkMask = 128;
} else {
return 1;
}
} else {
return -1;
}
}
_checkX++;
_checkY--;
return cpe();
} else {
return -1;
}
}
bool PrinceEngine::tracePath(int x1, int y1, int x2, int y2) {
for (uint i = 0; i < kPathBitmapLen; i++) {
_roomPathBitmapTemp[i] = 0;
}
if (x1 != x2 || y1 != y2) {
if (getPixelAddr(_roomPathBitmap, x1, y1)) {
if (getPixelAddr(_roomPathBitmap, x2, y2)) {
_coords = _coordsBuf;
specialPlot(x1, y1);
int x = x1;
int y = y1;
while (1) {
int btx = x;
int bty = y;
byte *bcad = _coords;
_traceLineLen = 0;
_traceLineFirstPointFlag = true;
int drawLineFlag = drawLine(x, y, x2, y2, &this->plotTraceLine, this);
if (!drawLineFlag) {
return true;
} else if (drawLineFlag == -1 && _traceLineLen >= 2) {
byte *tempCorrds = bcad;
while (tempCorrds != _coords) {
x = READ_LE_UINT16(tempCorrds);
y = READ_LE_UINT16(tempCorrds + 2);
tempCorrds += 4;
specialPlot2(x, y);
}
} else {
_coords = bcad;
x = btx;
y = bty;
}
Direction dir = makeDirection(x, y, x2, y2);
_rembBitmapTemp = &_roomPathBitmapTemp[x / 8 + y * 80];
_rembBitmap = &_roomPathBitmap[x / 8 + y * 80];
_rembMask = 128 >> (x & 7);
_rembX = x;
_rembY = y;
_checkBitmapTemp = _rembBitmapTemp;
_checkBitmap = _rembBitmap;
_checkMask = _rembMask;
_checkX = _rembX;
_checkY = _rembY;
int result;
switch (dir) {
case kDirLD:
result = leftDownDir();
break;
case kDirL:
result = leftDir();
break;
case kDirLU:
result = leftUpDir();
break;
case kDirRD:
result = rightDownDir();
break;
case kDirR:
result = rightDir();
break;
case kDirRU:
result = rightUpDir();
break;
case kDirUL:
result = upLeftDir();
break;
case kDirU:
result = upDir();
break;
case kDirUR:
result = upRightDir();
break;
case kDirDL:
result = downLeftDir();
break;
case kDirD:
result = downDir();
break;
case kDirDR:
result = downRightDir();
break;
default:
result = -1;
error("tracePath: wrong direction %d", dir);
break;
}
if (result) {
byte *tempCoords = _coords;
tempCoords -= 4;
if (tempCoords > _coordsBuf) {
int tempX = READ_LE_UINT16(tempCoords);
int tempY = READ_LE_UINT16(tempCoords + 2);
if (_checkX == tempX && _checkY == tempY) {
_coords = tempCoords;
}
x = READ_LE_UINT16(tempCoords);
y = READ_LE_UINT16(tempCoords + 2);
} else {
return false;
}
} else {
x = _checkX;
y = _checkY;
}
}
return true;
} else {
error("tracePath: wrong destination point");
}
} else {
error("tracePath: wrong start point");
}
} else {
error("tracePath: same point");
}
}
void PrinceEngine::specialPlotInside2(int x, int y) {
WRITE_LE_UINT16(_coords2, x);
_coords2 += 2;
WRITE_LE_UINT16(_coords2, y);
_coords2 += 2;
}
int PrinceEngine::plotTracePoint(int x, int y, void *data) {
PrinceEngine *tracePoint = (PrinceEngine *)data;
if (!tracePoint->_tracePointFirstPointFlag) {
if (tracePoint->getPixelAddr(tracePoint->_roomPathBitmap, x, y)) {
tracePoint->specialPlotInside2(x, y);
return 0;
} else {
return -1;
}
} else {
tracePoint->_tracePointFirstPointFlag = false;
return 0;
}
}
void PrinceEngine::approxPath() {
byte *oldCoords;
_coords2 = _coordsBuf2;
byte *tempCoordsBuf = _coordsBuf; // first point on path
byte *tempCoords = _coords;
if (tempCoordsBuf != tempCoords) {
tempCoords -= 4; // last point on path
while (tempCoordsBuf != tempCoords) {
int x1 = READ_LE_UINT16(tempCoords);
int y1 = READ_LE_UINT16(tempCoords + 2);
int x2 = READ_LE_UINT16(tempCoordsBuf);
int y2 = READ_LE_UINT16(tempCoordsBuf + 2);
tempCoordsBuf += 4;
//TracePoint
oldCoords = _coords2;
if (_coords2 == _coordsBuf2) {
WRITE_LE_UINT16(_coords2, x1);
WRITE_LE_UINT16(_coords2 + 2, y1);
_coords2 += 4;
} else {
int testX = READ_LE_UINT16(_coords2 - 4);
int testY = READ_LE_UINT16(_coords2 - 2);
if (testX != x1 || testY != y1) {
WRITE_LE_UINT16(_coords2, x1);
WRITE_LE_UINT16(_coords2 + 2, y1);
_coords2 += 4;
}
}
_tracePointFirstPointFlag = true;
bool drawLineFlag = drawLine(x1, y1, x2, y2, &this->plotTracePoint, this);
if (!drawLineFlag) {
tempCoords = tempCoordsBuf - 4;
tempCoordsBuf = _coordsBuf;
} else {
_coords2 = oldCoords;
}
}
}
}
void PrinceEngine::freeDirectionTable() {
if (_directionTable != nullptr) {
free(_directionTable);
_directionTable = nullptr;
}
}
int PrinceEngine::scanDirectionsFindNext(byte *tempCoordsBuf, int xDiff, int yDiff) {
int tempX, tempY, direction;
tempX = Hero::kHeroDirLeft;
if (xDiff < 0) {
tempX = Hero::kHeroDirRight;
}
tempY = Hero::kHeroDirUp;
if (yDiff < 0) {
tempY = Hero::kHeroDirDown;
}
while (1) {
int againPointX1 = READ_LE_UINT16(tempCoordsBuf);
int againPointY1 = READ_LE_UINT16(tempCoordsBuf + 2);
tempCoordsBuf += 4;
if (tempCoordsBuf == _coords) {
direction = tempX;
break;
}
int dX = againPointX1 - READ_LE_UINT16(tempCoordsBuf);
int dY = againPointY1 - READ_LE_UINT16(tempCoordsBuf + 2);
if (dX != xDiff) {
direction = tempY;
break;
}
if (dY != yDiff) {
direction = tempX;
break;
}
}
return direction;
}
void PrinceEngine::scanDirections() {
freeDirectionTable();
byte *tempCoordsBuf = _coordsBuf;
if (tempCoordsBuf != _coords) {
int size = (_coords - tempCoordsBuf) / 4 + 1; // number of coord points plus one for end marker
_directionTable = (byte *)malloc(size);
byte *tempDirTab = _directionTable;
int direction = -1;
int lastDirection = -1;
while (1) {
int x1 = READ_LE_UINT16(tempCoordsBuf);
int y1 = READ_LE_UINT16(tempCoordsBuf + 2);
tempCoordsBuf += 4;
if (tempCoordsBuf == _coords) {
break;
}
int x2 = READ_LE_UINT16(tempCoordsBuf);
int y2 = READ_LE_UINT16(tempCoordsBuf + 2);
int xDiff = x1 - x2;
int yDiff = y1 - y2;
if (xDiff) {
if (yDiff) {
if (lastDirection != -1) {
direction = lastDirection;
if (direction == Hero::kHeroDirLeft) {
if (xDiff < 0) {
direction = scanDirectionsFindNext(tempCoordsBuf, xDiff, yDiff);
}
} else if (direction == Hero::kHeroDirRight) {
if (xDiff >= 0) {
direction = scanDirectionsFindNext(tempCoordsBuf, xDiff, yDiff);
}
} else if (direction == Hero::kHeroDirUp) {
if (yDiff < 0) {
direction = scanDirectionsFindNext(tempCoordsBuf, xDiff, yDiff);
}
} else {
if (yDiff >= 0) {
direction = scanDirectionsFindNext(tempCoordsBuf, xDiff, yDiff);
}
}
} else {
direction = scanDirectionsFindNext(tempCoordsBuf, xDiff, yDiff);
}
} else {
direction = Hero::kHeroDirLeft;
if (xDiff < 0) {
direction = Hero::kHeroDirRight;
}
}
} else {
if (yDiff) {
direction = Hero::kHeroDirUp;
if (yDiff < 0) {
direction = Hero::kHeroDirDown;
}
} else {
direction = lastDirection;
}
}
lastDirection = direction;
*tempDirTab = direction;
tempDirTab++;
}
*tempDirTab = *(tempDirTab - 1);
tempDirTab++;
*tempDirTab = 0;
}
}
void PrinceEngine::moveShandria() {
int shanLen1 = _shanLen;
if (_flags->getFlagValue(Flags::SHANDOG)) {
_secondHero->freeHeroAnim();
_secondHero->freeOldMove();
byte *shanCoords = _mainHero->_currCoords + shanLen1 * 4 - 4;
int shanX = READ_LE_UINT16(shanCoords - 4);
int shanY = READ_LE_UINT16(shanCoords - 2);
int xDiff = shanX - _secondHero->_middleX;
if (xDiff < 0) {
xDiff *= -1;
}
int yDiff = shanY - _secondHero->_middleY;
if (yDiff < 0) {
yDiff *= -1;
}
shanCoords -= 4;
if (shanCoords != _mainHero->_currCoords) {
yDiff *= 1.5;
int shanDis = xDiff * xDiff + yDiff * yDiff;
if (shanDis >= kMinDistance) {
while (1) {
shanCoords -= 4;
if (shanCoords == _mainHero->_currCoords) {
break;
}
int x = READ_LE_UINT16(shanCoords);
int y = READ_LE_UINT16(shanCoords + 2);
int pointDiffX = x - shanX;
if (pointDiffX < 0) {
pointDiffX *= -1;
}
int pointDiffY = y - shanY;
if (pointDiffY < 0) {
pointDiffY *= -1;
}
pointDiffY *= 1.5;
int distance = pointDiffX * pointDiffX + pointDiffY * pointDiffY;
if (distance >= kMinDistance) {
break;
}
}
int pathSizeDiff = (shanCoords - _mainHero->_currCoords) / 4;
int destDir = *(_mainHero->_currDirTab + pathSizeDiff);
_secondHero->_destDirection = destDir;
int destX = READ_LE_UINT16(shanCoords);
int destY = READ_LE_UINT16(shanCoords + 2);
_secondHero->_coords = makePath(kSecondHero, _secondHero->_middleX, _secondHero->_middleY, destX, destY);
if (_secondHero->_coords != nullptr) {
_secondHero->_currCoords = _secondHero->_coords;
int delay = shanLen1 - _shanLen;
if (delay < 6) {
delay = 6;
}
_secondHero->_moveDelay = delay / 2;
_secondHero->_state = Hero::kHeroStateDelayMove;
_secondHero->_dirTab = _directionTable;
_secondHero->_currDirTab = _directionTable;
_directionTable = nullptr;
}
}
}
}
}
byte *PrinceEngine::makePath(int heroId, int currX, int currY, int destX, int destY) {
int realDestX = destX;
int realDestY = destY;
_flags->setFlagValue(Flags::MOVEDESTX, destX);
_flags->setFlagValue(Flags::MOVEDESTY, destY);
int x1 = currX / 2;
int y1 = currY / 2;
int x2 = destX / 2;
int y2 = destY / 2;
if ((x1 != x2) || (y1 != y2)) {
findPoint(x1, y1);
if (!getPixelAddr(_roomPathBitmap, _fpX, _fpY)) {
return nullptr;
}
if ((x1 != _fpX) || (y1 != _fpY)) {
x1 = _fpX;
y1 = _fpY;
}
findPoint(x2, y2);
if (!getPixelAddr(_roomPathBitmap, _fpX, _fpY)) {
return nullptr;
}
if ((x2 != _fpX) || (y2 != _fpY)) {
x2 = _fpX;
y2 = _fpY;
if (!_flags->getFlagValue(Flags::EXACTMOVE)) {
realDestX = x2 * 2;
realDestY = y2 * 2;
_flags->setFlagValue(Flags::MOVEDESTX, realDestX);
_flags->setFlagValue(Flags::MOVEDESTY, realDestY);
} else {
return nullptr;
}
}
if ((x1 == x2) && (y1 == y2)) {
if (!heroId) {
_mainHero->freeOldMove();
_mainHero->_state = Hero::kHeroStateTurn;
} else if (heroId == 1) {
_secondHero->freeOldMove();
_secondHero->_state = Hero::kHeroStateTurn;
}
return nullptr;
}
int pathLen1 = 0;
int pathLen2 = 0;
int stX = x1;
int stY = y1;
int sizeCoords2 = 0;
if (tracePath(x1, y1, x2, y2)) {
allocCoords2();
approxPath();
sizeCoords2 = _coords2 - _coordsBuf2;
for (int i = 0; i < sizeCoords2; i++) {
_coordsBuf[i] = _coordsBuf2[i];
}
_coords = _coordsBuf + sizeCoords2;
approxPath();
_coordsBuf3 = _coordsBuf2;
_coordsBuf2 = nullptr;
_coords3 = _coords2;
_coords2 = nullptr;
pathLen1 = _coords3 - _coordsBuf3;
}
if (tracePath(x2, y2, x1, y1)) {
allocCoords2();
approxPath();
sizeCoords2 = _coords2 - _coordsBuf2;
for (int i = 0; i < sizeCoords2; i++) {
_coordsBuf[i] = _coordsBuf2[i];
}
_coords = _coordsBuf + sizeCoords2;
approxPath();
pathLen2 = _coords2 - _coordsBuf2;
}
byte *chosenCoordsBuf = _coordsBuf2;
byte *choosenCoords = _coords2;
int choosenLength = pathLen1;
if (pathLen1 < pathLen2) {
chosenCoordsBuf = _coordsBuf3;
choosenCoords = _coords3;
choosenLength = pathLen2;
}
if (choosenLength) {
if (chosenCoordsBuf != nullptr) {
int tempXBegin = READ_LE_UINT16(chosenCoordsBuf);
int tempYBegin = READ_LE_UINT16(chosenCoordsBuf + 2);
if (stX != tempXBegin || stY != tempYBegin) {
SWAP(chosenCoordsBuf, choosenCoords);
chosenCoordsBuf -= 4;
byte *tempCoordsBuf = _coordsBuf;
while (1) {
int cord = READ_LE_UINT32(chosenCoordsBuf);
WRITE_LE_UINT32(tempCoordsBuf, cord);
tempCoordsBuf += 4;
if (chosenCoordsBuf == choosenCoords) {
break;
}
chosenCoordsBuf -= 4;
}
_coords = tempCoordsBuf;
} else {
int sizeChoosen = choosenCoords - chosenCoordsBuf;
for (int i = 0; i < sizeChoosen; i++) {
_coordsBuf[i] = chosenCoordsBuf[i];
}
_coords = _coordsBuf + sizeChoosen;
}
WRITE_LE_UINT32(_coords, 0xFFFFFFFF);
freeCoords2();
freeCoords3();
scanDirections();
byte *tempCoordsBuf = _coordsBuf;
byte *tempCoords = _coords;
byte *newCoords;
if (tempCoordsBuf != tempCoords) {
int normCoordsSize = _coords - _coordsBuf + 4;
newCoords = (byte *)malloc(normCoordsSize);
byte *newCoordsBegin = newCoords;
while (tempCoordsBuf != tempCoords) {
int newValueX = READ_LE_UINT16(tempCoordsBuf);
WRITE_LE_UINT16(newCoords, newValueX * 2);
newCoords += 2;
int newValueY = READ_LE_UINT16(tempCoordsBuf + 2);
WRITE_LE_UINT16(newCoords, newValueY * 2);
newCoords += 2;
tempCoordsBuf += 4;
}
WRITE_LE_UINT16(newCoords - 4, realDestX);
WRITE_LE_UINT16(newCoords - 2, realDestY);
WRITE_LE_UINT32(newCoords, 0xFFFFFFFF);
newCoords += 4;
_shanLen = (newCoords - newCoordsBegin);
_shanLen /= 4;
return newCoordsBegin;
}
}
}
_coords = _coordsBuf;
freeCoords2();
freeCoords3();
return nullptr;
} else {
if (!heroId) {
_mainHero->freeOldMove();
_mainHero->_state = Hero::kHeroStateTurn;
} else if (heroId == 1) {
_secondHero->freeOldMove();
_secondHero->_state = Hero::kHeroStateTurn;
}
return nullptr;
}
}
void PrinceEngine::allocCoords2() {
if (_coordsBuf2 == nullptr) {
_coordsBuf2 = (byte *)malloc(kTracePts * 4);
_coords2 = _coordsBuf2;
}
}
void PrinceEngine::freeCoords2() {
if (_coordsBuf2 != nullptr) {
free(_coordsBuf2);
_coordsBuf2 = nullptr;
_coords2 = nullptr;
}
}
void PrinceEngine::freeCoords3() {
if (_coordsBuf3 != nullptr) {
free(_coordsBuf3);
_coordsBuf3 = nullptr;
_coords3 = nullptr;
}
}
void PrinceEngine::openInventoryCheck() {
if (!_optionsFlag) {
if (_mouseFlag == 1 || _mouseFlag == 2) {
if (_mainHero->_visible) {
if (!_flags->getFlagValue(Flags::INVALLOWED)) {
// 29 - Basement, 50 - Map
if (_locationNr != 29 && _locationNr != 50) {
Common::Point mousePos = _system->getEventManager()->getMousePos();
if (mousePos.y < 4 && !_showInventoryFlag) {
_invCounter++;
} else {
_invCounter = 0;
}
if (_invCounter >= _invMaxCount) {
inventoryFlagChange(true);
}
}
}
}
}
}
}
void PrinceEngine::mainLoop() {
changeCursor(0);
_currentTime = _system->getMillis();
while (!shouldQuit()) {
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_LBUTTONDOWN:
leftMouseButton();
break;
case Common::EVENT_RBUTTONDOWN:
rightMouseButton();
break;
default:
break;
}
}
if (shouldQuit()) {
return;
}
// for "throw a rock" mini-game
mouseWeirdo();
_interpreter->stepBg();
_interpreter->stepFg();
drawScreen();
_graph->update(_graph->_frontScreen);
openInventoryCheck();
pausePrinceEngine();
}
}
} // End of namespace Prince