mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-27 04:07:05 +00:00
4748 lines
118 KiB
C++
4748 lines
118 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/system.h"
|
|
#include "common/events.h"
|
|
#include "common/debug-channels.h"
|
|
#include "common/archive.h"
|
|
#include "common/config-manager.h"
|
|
#include "common/savefile.h"
|
|
#include "common/memstream.h"
|
|
|
|
#include "engines/advancedDetector.h"
|
|
#include "engines/util.h"
|
|
#include "graphics/palette.h"
|
|
#include "graphics/surface.h"
|
|
#include "graphics/thumbnail.h"
|
|
#include "gui/saveload.h"
|
|
#include "gui/about.h"
|
|
#include "gui/message.h"
|
|
#include "toon/resource.h"
|
|
#include "toon/toon.h"
|
|
#include "toon/anim.h"
|
|
#include "toon/picture.h"
|
|
#include "toon/hotspot.h"
|
|
#include "toon/flux.h"
|
|
#include "toon/drew.h"
|
|
#include "toon/path.h"
|
|
|
|
namespace Toon {
|
|
|
|
void ToonEngine::init() {
|
|
_currentScriptRegion = 0;
|
|
_resources = new Resources(this);
|
|
_animationManager = new AnimationManager(this);
|
|
_moviePlayer = new Movie(this, new ToonstruckSmackerDecoder());
|
|
_hotspots = new Hotspots(this);
|
|
|
|
_mainSurface = new Graphics::Surface();
|
|
_mainSurface->create(TOON_BACKBUFFER_WIDTH, TOON_BACKBUFFER_HEIGHT, Graphics::PixelFormat::createFormatCLUT8());
|
|
|
|
_finalPalette = new uint8[768];
|
|
_backupPalette = new uint8[768];
|
|
_additionalPalette1 = new uint8[69];
|
|
_additionalPalette2 = new uint8[69];
|
|
_cutawayPalette = new uint8[768];
|
|
_universalPalette = new uint8[96];
|
|
_fluxPalette = new uint8[24];
|
|
|
|
memset(_finalPalette, 0, 768);
|
|
memset(_backupPalette, 0, 768);
|
|
memset(_additionalPalette1, 0, 69);
|
|
memset(_additionalPalette2, 0, 69);
|
|
memset(_cutawayPalette, 0, 768);
|
|
memset(_universalPalette, 0, 96);
|
|
memset(_fluxPalette, 0, 24);
|
|
|
|
_conversationData = new int16[4096];
|
|
memset(_conversationData, 0, 4096 * sizeof(int16));
|
|
|
|
_shouldQuit = false;
|
|
_scriptStep = 0;
|
|
|
|
_cursorOffsetX = 0;
|
|
_cursorOffsetY = 0;
|
|
_currentHotspotItem = 0;
|
|
|
|
_currentTextLine = 0;
|
|
_currentTextLineId = -1;
|
|
_currentTextLineX = 0;
|
|
_currentTextLineY = 0;
|
|
_currentTextLineCharacterId = 0;
|
|
|
|
_saveBufferStream = new Common::MemoryWriteStreamDynamic(DisposeAfterUse::YES);
|
|
|
|
_firstFrame = false;
|
|
|
|
const Common::FSNode gameDataDir(ConfMan.get("path"));
|
|
SearchMan.addSubDirectoryMatching(gameDataDir, "MISC");
|
|
SearchMan.addSubDirectoryMatching(gameDataDir, "ACT1");
|
|
SearchMan.addSubDirectoryMatching(gameDataDir, "ACT2");
|
|
|
|
syncSoundSettings();
|
|
|
|
_pathFinding = new PathFinding();
|
|
|
|
resources()->openPackage("LOCAL.PAK");
|
|
resources()->openPackage("ONETIME.PAK");
|
|
resources()->openPackage("DREW.PAK");
|
|
|
|
for (int32 i = 0; i < 32; i++)
|
|
_characters[i] = NULL;
|
|
|
|
_characters[0] = new CharacterDrew(this);
|
|
_characters[1] = new CharacterFlux(this);
|
|
_drew = _characters[0];
|
|
_flux = _characters[1];
|
|
|
|
|
|
|
|
// preload walk anim for flux and drew
|
|
_drew->loadWalkAnimation("STNDWALK.CAF");
|
|
_drew->setupPalette();
|
|
_drew->loadShadowAnimation("SHADOW.CAF");
|
|
|
|
_flux->loadWalkAnimation("FXSTWALK.CAF");
|
|
_flux->loadShadowAnimation("SHADOW.CAF");
|
|
|
|
loadAdditionalPalette("UNIVERSE.PAL", 3);
|
|
loadAdditionalPalette("FLUX.PAL", 4);
|
|
setupGeneralPalette();
|
|
|
|
_script_func = new ScriptFunc(this);
|
|
_gameState = new State();
|
|
_gameState->_conversationData = _conversationData;
|
|
|
|
memset(_sceneAnimations, 0, sizeof(_sceneAnimations));
|
|
memset(_sceneAnimationScripts, 0, sizeof(_sceneAnimationScripts));
|
|
|
|
_drew->setVisible(false);
|
|
_flux->setVisible(false);
|
|
|
|
_gameState->_currentChapter = 1;
|
|
initChapter();
|
|
loadCursor();
|
|
initFonts();
|
|
|
|
_dialogIcons = new Animation(this);
|
|
_dialogIcons->loadAnimation("DIALOGUE.CAF");
|
|
|
|
_inventoryIcons = new Animation(this);
|
|
_inventoryIcons->loadAnimation("INVENTRY.CAF");
|
|
|
|
_inventoryIconSlots = new Animation(this);
|
|
_inventoryIconSlots->loadAnimation("ICONSLOT.CAF");
|
|
|
|
_genericTexts = new TextResource(this);
|
|
_genericTexts->loadTextResource("GENERIC.TRE");
|
|
|
|
_audioManager = new AudioManager(this, _mixer);
|
|
_audioManager->loadAudioPack(0, "GENERIC.SVI", "GENERIC.SVL");
|
|
_audioManager->loadAudioPack(2, "GENERIC.SEI", "GENERIC.SEL");
|
|
|
|
_lastMouseButton = 0;
|
|
_mouseButton = 0;
|
|
_lastRenderTime = _system->getMillis();
|
|
}
|
|
|
|
void ToonEngine::waitForScriptStep() {
|
|
// Wait after a specified number of script steps when executing a script
|
|
// to lower CPU usage
|
|
if (++_scriptStep >= 40) {
|
|
_system->delayMillis(1);
|
|
_scriptStep = 0;
|
|
}
|
|
}
|
|
|
|
void ToonEngine::parseInput() {
|
|
|
|
Common::EventManager *_event = _system->getEventManager();
|
|
|
|
_mouseX = _event->getMousePos().x;
|
|
_mouseY = _event->getMousePos().y;
|
|
_mouseButton = _event->getButtonState();
|
|
|
|
Common::Event event;
|
|
while (_event->pollEvent(event)) {
|
|
|
|
bool hasModifier = event.kbd.hasFlags(Common::KBD_ALT|Common::KBD_CTRL|Common::KBD_SHIFT);
|
|
switch (event.type) {
|
|
case Common::EVENT_KEYDOWN:
|
|
if ((event.kbd.ascii == 27 || event.kbd.ascii == 32) && !hasModifier) {
|
|
_audioManager->stopCurrentVoice();
|
|
}
|
|
if (event.kbd.keycode == Common::KEYCODE_F5 && !hasModifier) {
|
|
if (canSaveGameStateCurrently())
|
|
saveGame(-1, "");
|
|
}
|
|
if (event.kbd.keycode == Common::KEYCODE_F6 && !hasModifier) {
|
|
if (canLoadGameStateCurrently())
|
|
loadGame(-1);
|
|
}
|
|
if (event.kbd.ascii == 't' && !hasModifier) {
|
|
_showConversationText = !_showConversationText;
|
|
}
|
|
if (event.kbd.ascii == 'm' && !hasModifier) {
|
|
_audioManager->muteMusic(!_audioManager->isMusicMuted());
|
|
}
|
|
if (event.kbd.ascii == 'd' && !hasModifier) {
|
|
_audioManager->muteVoice(!_audioManager->isVoiceMuted());
|
|
}
|
|
if (event.kbd.ascii == 's' && !hasModifier) {
|
|
_audioManager->muteSfx(!_audioManager->isSfxMuted());
|
|
}
|
|
|
|
if (event.kbd.flags & Common::KBD_ALT) {
|
|
int slotNum = event.kbd.ascii - '0';
|
|
if (slotNum >= 0 && slotNum <= 9 && canSaveGameStateCurrently()) {
|
|
if (saveGame(slotNum, "")) {
|
|
// ok
|
|
Common::String buf = Common::String::format("Saved game in slot #%d ", slotNum);
|
|
GUI::TimedMessageDialog dialog(buf, 1000);
|
|
dialog.runModal();
|
|
} else {
|
|
Common::String buf = Common::String::format("Could not quick save into slot #%d", slotNum);
|
|
GUI::MessageDialog dialog(buf, "OK", 0);
|
|
dialog.runModal();
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
if (event.kbd.flags & Common::KBD_CTRL) {
|
|
int slotNum = event.kbd.ascii - '0';
|
|
if (slotNum >= 0 && slotNum <= 9 && canLoadGameStateCurrently()) {
|
|
if (loadGame(slotNum)) {
|
|
// ok
|
|
Common::String buf = Common::String::format("Savegame #%d quick loaded", slotNum);
|
|
GUI::TimedMessageDialog dialog(buf, 1000);
|
|
dialog.runModal();
|
|
} else {
|
|
Common::String buf = Common::String::format("Could not quick load the savegame #%d", slotNum);
|
|
GUI::MessageDialog dialog(buf, "OK", 0);
|
|
warning("%s", buf.c_str());
|
|
dialog.runModal();
|
|
}
|
|
}
|
|
|
|
if (event.kbd.keycode == Common::KEYCODE_d) {
|
|
_console->attach();
|
|
_console->onFrame();
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!_gameState->_inConversation && !_gameState->_mouseHidden && !_gameState->_inInventory) {
|
|
selectHotspot();
|
|
clickEvent();
|
|
}
|
|
}
|
|
|
|
void ToonEngine::enableTimer(int32 timerId) {
|
|
_gameState->_timerEnabled[timerId] = true;
|
|
}
|
|
void ToonEngine::setTimer(int32 timerId, int32 timerWait) {
|
|
_gameState->_timerTimeout[timerId] = getOldMilli() + timerWait * getTickLength();
|
|
_gameState->_timerDelay[timerId] = timerWait;
|
|
}
|
|
void ToonEngine::disableTimer(int32 timerId) {
|
|
_gameState->_timerEnabled[timerId] = false;
|
|
}
|
|
void ToonEngine::updateTimers() {
|
|
for (int32 i = 0; i < 2; i++) {
|
|
if (_gameState->_timerEnabled[i]) {
|
|
if (_gameState->_timerDelay[i] > -1 && getOldMilli() > _gameState->_timerTimeout[i]) {
|
|
if (i == 0) {
|
|
|
|
EMCState *status = &_scriptState[_currentScriptRegion];
|
|
_script->init(status, &_scriptData);
|
|
|
|
// setup registers
|
|
status->regs[0] = _mouseX;
|
|
status->regs[1] = _mouseY;
|
|
status->regs[2] = 0;
|
|
|
|
_currentScriptRegion++;
|
|
|
|
_script->start(status, 7);
|
|
while (_script->run(status))
|
|
waitForScriptStep();
|
|
|
|
_currentScriptRegion--;
|
|
|
|
_gameState->_timerTimeout[i] = getOldMilli() + _gameState->_timerDelay[i] * getTickLength();
|
|
|
|
return;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ToonEngine::updateScrolling(bool force, int32 timeIncrement) {
|
|
static int32 lastScrollOffset = 320;
|
|
if (!_audioManager->voiceStillPlaying() && !_gameState->_currentScrollLock && (_drew->getFlag() & 1) == 0) {
|
|
if (_drew->getFacing() & 3) {
|
|
if (_drew->getFacing() <= 4)
|
|
lastScrollOffset = 200;
|
|
else
|
|
lastScrollOffset = 440;
|
|
}
|
|
|
|
if (_gameState->_inCutaway || _gameState->_inInventory || _gameState->_inCloseUp)
|
|
return;
|
|
|
|
int32 desiredScrollValue = _drew->getX() - lastScrollOffset;
|
|
|
|
if ((_gameState->_locations[_gameState->_currentScene]._flags & 0x80) == 0) {
|
|
if (desiredScrollValue < 0)
|
|
desiredScrollValue = 0;
|
|
if (desiredScrollValue >= _currentPicture->getWidth() - TOON_SCREEN_WIDTH)
|
|
desiredScrollValue = _currentPicture->getWidth() - TOON_SCREEN_WIDTH;
|
|
|
|
if (force) {
|
|
_gameState->_currentScrollValue = desiredScrollValue;
|
|
return;
|
|
} else {
|
|
if (_gameState->_currentScrollValue < desiredScrollValue) {
|
|
_gameState->_currentScrollValue += timeIncrement / 2;
|
|
|
|
if (_gameState->_currentScrollValue > desiredScrollValue)
|
|
_gameState->_currentScrollValue = desiredScrollValue;
|
|
} else if (_gameState->_currentScrollValue > desiredScrollValue) {
|
|
_gameState->_currentScrollValue -= timeIncrement / 2;
|
|
|
|
if (_gameState->_currentScrollValue < desiredScrollValue)
|
|
_gameState->_currentScrollValue = desiredScrollValue;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ToonEngine::update(int32 timeIncrement) {
|
|
// to make sure we're updating the game at 5fps at least
|
|
if (timeIncrement > 200)
|
|
timeIncrement = 200;
|
|
|
|
updateAnimationSceneScripts(timeIncrement);
|
|
updateCharacters(timeIncrement);
|
|
updateTimer(timeIncrement);
|
|
updateTimers();
|
|
updateScrolling(false, timeIncrement);
|
|
_audioManager->updateAmbientSFX();
|
|
_animationManager->update(timeIncrement);
|
|
_cursorAnimationInstance->update(timeIncrement);
|
|
|
|
if (!_audioManager->voiceStillPlaying()) {
|
|
_currentTextLine = 0;
|
|
_currentTextLineId = -1;
|
|
}
|
|
}
|
|
|
|
void ToonEngine::updateTimer(int32 timeIncrement) {
|
|
if (_gameState->_gameTimer > 0) {
|
|
debugC(0, 0xfff, "updateTimer(%d)", (int)timeIncrement);
|
|
_gameState->_gameTimer -= timeIncrement;
|
|
if (_gameState->_gameTimer < 0)
|
|
_gameState->_gameTimer = 0;
|
|
}
|
|
}
|
|
|
|
void ToonEngine::render() {
|
|
|
|
if (_dirtyAll) {
|
|
if (_gameState->_inCutaway)
|
|
_currentCutaway->draw(*_mainSurface, 0, 0, 0, 0);
|
|
else
|
|
_currentPicture->draw(*_mainSurface, 0, 0, 0, 0);
|
|
_dirtyRects.push_back(Common::Rect(0, 0, TOON_BACKBUFFER_WIDTH, TOON_BACKBUFFER_HEIGHT));
|
|
} else {
|
|
if (_gameState->_inCutaway)
|
|
_currentCutaway->drawWithRectList(*_mainSurface, 0, 0, 0, 0, _dirtyRects);
|
|
else
|
|
_currentPicture->drawWithRectList(*_mainSurface, 0, 0, 0, 0, _dirtyRects);
|
|
}
|
|
|
|
clearDirtyRects();
|
|
|
|
//_currentMask->drawMask(*_mainSurface, 0, 0, 0, 0);
|
|
_animationManager->render();
|
|
|
|
drawInfoLine();
|
|
drawConversationLine();
|
|
drawConversationIcons();
|
|
drawSack();
|
|
//drawPalette(); // used to debug the current palette
|
|
//_drew->plotPath(*_mainSurface); // used to debug path finding
|
|
|
|
#if 0
|
|
if (_mouseX > 0 && _mouseX < 640 && _mouseY > 0 && _mouseY < 400) {
|
|
Common::String test;
|
|
test = Common::String::format("%d %d / mask %d layer %d z %d", _mouseX, _mouseY, getMask()->getData(_mouseX, _mouseY), getLayerAtPoint(_mouseX, _mouseY), getZAtPoint(_mouseX, _mouseY));
|
|
|
|
int32 c = *(uint8 *)_mainSurface->getBasePtr(_mouseX, _mouseY);
|
|
test = Common::String::format("%d %d / color id %d %d,%d,%d", _mouseX, _mouseY, c, _finalPalette[c * 3 + 0], _finalPalette[c * 3 + 1], _finalPalette[c * 3 + 2]);
|
|
|
|
_fontRenderer->setFont(_fontToon);
|
|
_fontRenderer->renderText(40, 150, test, 0);
|
|
}
|
|
#endif
|
|
|
|
if (_needPaletteFlush) {
|
|
flushPalette(false);
|
|
_needPaletteFlush = false;
|
|
}
|
|
|
|
if (_firstFrame) {
|
|
copyToVirtualScreen(false);
|
|
fadeIn(5);
|
|
_firstFrame = false;
|
|
} else {
|
|
copyToVirtualScreen(true);
|
|
}
|
|
|
|
// add a little sleep here
|
|
int32 newMillis = (int32)_system->getMillis();
|
|
int32 sleepMs = 1; // Minimum delay to allow thread scheduling
|
|
if ((newMillis - _lastRenderTime) < _tickLength * 2)
|
|
sleepMs = _tickLength * 2 - (newMillis - _lastRenderTime);
|
|
assert(sleepMs >= 0);
|
|
_system->delayMillis(sleepMs);
|
|
_lastRenderTime = _system->getMillis();
|
|
}
|
|
|
|
void ToonEngine::doMagnifierEffect() {
|
|
int32 posX = _mouseX + state()->_currentScrollValue - _cursorOffsetX;
|
|
int32 posY = _mouseY - _cursorOffsetY - 2;
|
|
|
|
Graphics::Surface &surface = *_mainSurface;
|
|
|
|
// fast sqrt table lookup (values up to 144 only)
|
|
static const byte intSqrt[] = {
|
|
0, 1, 1, 1, 2, 2, 2, 2, 2, 3,
|
|
3, 3, 3, 3, 3, 3, 4, 4, 4, 4,
|
|
4, 4, 4, 4, 4, 5, 5, 5, 5, 5,
|
|
5, 5, 5, 5, 5, 5, 6, 6, 6, 6,
|
|
6, 6, 6, 6, 6, 6, 6, 6, 6, 7,
|
|
7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
|
|
7, 7, 7, 7, 8, 8, 8, 8, 8, 8,
|
|
8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
|
|
8, 9, 9, 9, 9, 9, 9, 9, 9, 9,
|
|
9, 9, 9, 9, 9, 9, 9, 9, 9, 9,
|
|
10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
|
|
10, 10, 10, 10, 10, 10, 10, 10, 10, 10,
|
|
10, 11, 11, 11, 11, 11, 11, 11, 11, 11,
|
|
11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
|
|
11, 11, 11, 11, 12
|
|
};
|
|
|
|
byte tempBuffer[25 * 25];
|
|
for (int32 y = -12; y <= 12; y++) {
|
|
int32 cy = CLIP<int32>(posY + y, 0, TOON_BACKBUFFER_HEIGHT-1);
|
|
for (int32 x = -12; x <= 12; x++) {
|
|
int32 cx = CLIP<int32>(posX + x, 0, TOON_BACKBUFFER_WIDTH-1);
|
|
int32 destPitch = surface.pitch;
|
|
uint8 *curRow = (uint8 *)surface.pixels + cy * destPitch + cx;
|
|
tempBuffer[(y + 12) * 25 + x + 12] = *curRow;
|
|
}
|
|
}
|
|
|
|
for (int32 y = -12; y <= 12; y++) {
|
|
int32 cy = CLIP<int32>(posY + y, 0, TOON_BACKBUFFER_HEIGHT-1);
|
|
for (int32 x = -12; x <= 12; x++) {
|
|
int32 dist = y * y + x * x;
|
|
if (dist > 144)
|
|
continue;
|
|
int32 cx = CLIP<int32>(posX + x, 0, TOON_BACKBUFFER_WIDTH-1);
|
|
int32 destPitch = surface.pitch;
|
|
uint8 *curRow = (uint8 *)surface.pixels + cy * destPitch + cx;
|
|
int32 lerp = (512 + intSqrt[dist] * 256 / 12);
|
|
*curRow = tempBuffer[(y * lerp / 1024 + 12) * 25 + x * lerp / 1024 + 12];
|
|
}
|
|
}
|
|
}
|
|
|
|
void ToonEngine::copyToVirtualScreen(bool updateScreen) {
|
|
// render cursor last
|
|
if (!_gameState->_mouseHidden) {
|
|
if (_cursorAnimationInstance->getFrame() == 7 && _cursorAnimationInstance->getAnimation() == _cursorAnimation) // magnifier icon needs a special effect
|
|
doMagnifierEffect();
|
|
_cursorAnimationInstance->setPosition(_mouseX - 40 + state()->_currentScrollValue - _cursorOffsetX, _mouseY - 40 - _cursorOffsetY, 0, false);
|
|
_cursorAnimationInstance->render();
|
|
}
|
|
|
|
// Handle dirty rects here
|
|
static int32 lastScroll = 0;
|
|
|
|
if (_dirtyAll || _gameState->_currentScrollValue != lastScroll) {
|
|
// we have to refresh everything in case of scrolling.
|
|
_system->copyRectToScreen((byte *)_mainSurface->pixels + state()->_currentScrollValue, TOON_BACKBUFFER_WIDTH, 0, 0, TOON_SCREEN_WIDTH, TOON_SCREEN_HEIGHT);
|
|
} else {
|
|
|
|
int32 offX = 0;
|
|
for (uint i = 0; i < _oldDirtyRects.size(); i++) {
|
|
Common::Rect rect = _oldDirtyRects[i];
|
|
rect.translate(-state()->_currentScrollValue, 0);
|
|
offX = 0;
|
|
if (rect.right <= 0)
|
|
continue;
|
|
if (rect.left < 0) {
|
|
offX = -rect.left;
|
|
rect.left = 0;
|
|
}
|
|
rect.clip(TOON_SCREEN_WIDTH, TOON_SCREEN_HEIGHT);
|
|
if (rect.left >= 0 && rect.top >= 0 && rect.right - rect.left > 0 && rect.bottom - rect.top > 0) {
|
|
_system->copyRectToScreen((byte *)_mainSurface->pixels + _oldDirtyRects[i].left + offX + _oldDirtyRects[i].top * TOON_BACKBUFFER_WIDTH, TOON_BACKBUFFER_WIDTH, rect.left , rect.top, rect.right - rect.left, rect.bottom - rect.top);
|
|
}
|
|
}
|
|
|
|
for (uint i = 0; i < _dirtyRects.size(); i++) {
|
|
Common::Rect rect = _dirtyRects[i];
|
|
rect.translate(-state()->_currentScrollValue, 0);
|
|
offX = 0;
|
|
if (rect.right <= 0)
|
|
continue;
|
|
if (rect.left < 0) {
|
|
offX = -rect.left;
|
|
rect.left = 0;
|
|
}
|
|
rect.clip(TOON_SCREEN_WIDTH, TOON_SCREEN_HEIGHT);
|
|
if (rect.left >= 0 && rect.top >= 0 && rect.right - rect.left > 0 && rect.bottom - rect.top > 0) {
|
|
_system->copyRectToScreen((byte *)_mainSurface->pixels + _dirtyRects[i].left + offX + _dirtyRects[i].top * TOON_BACKBUFFER_WIDTH, TOON_BACKBUFFER_WIDTH, rect.left , rect.top, rect.right - rect.left, rect.bottom - rect.top);
|
|
}
|
|
}
|
|
}
|
|
lastScroll = _gameState->_currentScrollValue;
|
|
|
|
if (updateScreen) {
|
|
_system->updateScreen();
|
|
_shouldQuit = shouldQuit(); // update game quit flag - this shouldn't be called all the time, as it's a virtual function
|
|
}
|
|
}
|
|
|
|
void ToonEngine::doFrame() {
|
|
|
|
if (_gameState->_inInventory) {
|
|
renderInventory();
|
|
} else {
|
|
render();
|
|
|
|
int32 currentTimer = _system->getMillis();
|
|
|
|
update(currentTimer - _oldTimer);
|
|
_oldTimer = currentTimer;
|
|
_oldTimer2 = currentTimer;
|
|
}
|
|
parseInput();
|
|
}
|
|
|
|
enum MainMenuSelections {
|
|
MAINMENUHOTSPOT_NONE = 0,
|
|
MAINMENUHOTSPOT_START = 1,
|
|
MAINMENUHOTSPOT_INTRO = 2,
|
|
MAINMENUHOTSPOT_LOADGAME = 3,
|
|
MAINMENUHOTSPOT_HOTKEYS = 4,
|
|
MAINMENUHOTSPOT_CREDITS = 5,
|
|
MAINMENUHOTSPOT_QUIT = 6,
|
|
MAINMENUHOTSPOT_HOTKEYSCLOSE = 7
|
|
};
|
|
|
|
enum MainMenuMasks {
|
|
MAINMENUMASK_BASE = 1,
|
|
MAINMENUMASK_HOTKEYS = 2,
|
|
MAINMENUMASK_EVERYWHERE = 3
|
|
};
|
|
|
|
struct MainMenuFile {
|
|
int menuMask;
|
|
int id;
|
|
const char *animationFile;
|
|
int animateOnFrame;
|
|
};
|
|
|
|
#define MAINMENU_ENTRYCOUNT 12
|
|
static const MainMenuFile mainMenuFiles[] = {
|
|
{ MAINMENUMASK_BASE, MAINMENUHOTSPOT_START, "STARTBUT.CAF", 0 }, // "Start" button
|
|
{ MAINMENUMASK_BASE, MAINMENUHOTSPOT_INTRO, "INTROBUT.CAF", 0 }, // "Intro" button
|
|
{ MAINMENUMASK_BASE, MAINMENUHOTSPOT_LOADGAME, "LOADBUT.CAF", 0 }, // "Load Game" button
|
|
{ MAINMENUMASK_BASE, MAINMENUHOTSPOT_HOTKEYS, "HOTBUT.CAF", 0 }, // "Hot Keys" button
|
|
{ MAINMENUMASK_BASE, MAINMENUHOTSPOT_CREDITS, "CREDBUT.CAF", 0 }, // "Credits" button
|
|
{ MAINMENUMASK_BASE, MAINMENUHOTSPOT_QUIT, "QUITBUT.CAF", 0 }, // "Quit" button
|
|
{ MAINMENUMASK_BASE, MAINMENUHOTSPOT_NONE, "LEGALTXT.CAF", 0 }, // Legal Text
|
|
|
|
{ MAINMENUMASK_EVERYWHERE, MAINMENUHOTSPOT_NONE, "TOONGLOW.CAF", 6 }, // Clown glow
|
|
{ MAINMENUMASK_EVERYWHERE, MAINMENUHOTSPOT_NONE, "TOONSTRK.CAF", 6 }, // Toonstruck title
|
|
{ MAINMENUMASK_EVERYWHERE, MAINMENUHOTSPOT_NONE, "EYEGLOW.CAF", 4 }, // Clown eye glow
|
|
{ MAINMENUMASK_EVERYWHERE, MAINMENUHOTSPOT_NONE, "PROPHEAD.CAF", 4 }, // Clown propellor head
|
|
{ MAINMENUMASK_HOTKEYS, MAINMENUHOTSPOT_HOTKEYSCLOSE, "HOTKEYS.CAF", 0 } // Hotkeys display - clicking on it will close hotkeys
|
|
};
|
|
|
|
struct MainMenuEntry {
|
|
int menuMask;
|
|
int id;
|
|
Animation *animation;
|
|
Common::Rect rect;
|
|
int animateOnFrame;
|
|
int animateCurFrame;
|
|
int activeFrame;
|
|
};
|
|
|
|
bool ToonEngine::showMainmenu(bool &loadedGame) {
|
|
Picture *mainmenuPicture = new Picture(this);
|
|
mainmenuPicture->loadPicture("TITLESCR.CPS");
|
|
mainmenuPicture->setupPalette();
|
|
flushPalette(false);
|
|
|
|
MainMenuEntry entries[MAINMENU_ENTRYCOUNT];
|
|
|
|
for (int entryNr = 0; entryNr < MAINMENU_ENTRYCOUNT; entryNr++) {
|
|
entries[entryNr].menuMask = mainMenuFiles[entryNr].menuMask;
|
|
entries[entryNr].id = mainMenuFiles[entryNr].id;
|
|
entries[entryNr].animation = new Animation(this);
|
|
entries[entryNr].animation->loadAnimation(mainMenuFiles[entryNr].animationFile);
|
|
if (entries[entryNr].id != MAINMENUHOTSPOT_NONE)
|
|
entries[entryNr].rect = entries[entryNr].animation->getRect();
|
|
entries[entryNr].animateOnFrame = mainMenuFiles[entryNr].animateOnFrame;
|
|
entries[entryNr].animateCurFrame = 0;
|
|
entries[entryNr].activeFrame = 0;
|
|
}
|
|
|
|
setCursor(1);
|
|
|
|
bool doExit = false;
|
|
bool exitGame = false;
|
|
int clickingOn, clickRelease;
|
|
int menuMask = MAINMENUMASK_BASE;
|
|
Common::SeekableReadStream *mainmenuMusicFile = NULL;
|
|
AudioStreamInstance *mainmenuMusic = NULL;
|
|
bool musicPlaying = false;
|
|
|
|
_gameState->_inMenu = true;
|
|
dirtyAllScreen();
|
|
|
|
while (!doExit) {
|
|
clickingOn = MAINMENUHOTSPOT_NONE;
|
|
clickRelease = false;
|
|
|
|
if (!musicPlaying) {
|
|
mainmenuMusicFile = resources()->openFile("BR091013.MUS");
|
|
if (mainmenuMusicFile) {
|
|
mainmenuMusic = new AudioStreamInstance(_audioManager, _mixer, mainmenuMusicFile, true);
|
|
mainmenuMusic->play(false);
|
|
musicPlaying = true;
|
|
}
|
|
else {
|
|
musicPlaying = false;
|
|
}
|
|
}
|
|
|
|
while (!clickRelease) {
|
|
|
|
if (_dirtyAll) {
|
|
mainmenuPicture->draw(*_mainSurface, 0, 0, 0, 0);
|
|
addDirtyRect(0, 0, TOON_SCREEN_WIDTH, TOON_SCREEN_HEIGHT);
|
|
} else {
|
|
mainmenuPicture->drawWithRectList(*_mainSurface, 0, 0, 0, 0, _dirtyRects);
|
|
}
|
|
|
|
clearDirtyRects();
|
|
|
|
for (int entryNr = 0; entryNr < MAINMENU_ENTRYCOUNT; entryNr++) {
|
|
if (entries[entryNr].menuMask & menuMask) {
|
|
if (entries[entryNr].animateOnFrame) {
|
|
entries[entryNr].animateCurFrame++;
|
|
if (entries[entryNr].animateOnFrame <= entries[entryNr].animateCurFrame) {
|
|
entries[entryNr].activeFrame++;
|
|
if (entries[entryNr].activeFrame >= entries[entryNr].animation->_numFrames)
|
|
entries[entryNr].activeFrame = 0;
|
|
entries[entryNr].animateCurFrame = 0;
|
|
}
|
|
}
|
|
int32 frameNr = entries[entryNr].activeFrame;
|
|
if ((entries[entryNr].id == clickingOn) && (clickingOn != MAINMENUHOTSPOT_NONE))
|
|
frameNr = 1;
|
|
entries[entryNr].animation->drawFrame(*_mainSurface, frameNr, 0, 0);
|
|
}
|
|
}
|
|
|
|
if (_needPaletteFlush) {
|
|
flushPalette(false);
|
|
_needPaletteFlush = false;
|
|
}
|
|
|
|
parseInput();
|
|
copyToVirtualScreen(true);
|
|
_system->delayMillis(17);
|
|
|
|
if (_mouseButton & 1) {
|
|
// left mouse button pushed down
|
|
clickingOn = MAINMENUHOTSPOT_NONE;
|
|
for (int entryNr = 0; entryNr < MAINMENU_ENTRYCOUNT; entryNr++) {
|
|
if (entries[entryNr].menuMask & menuMask) {
|
|
if (entries[entryNr].id != MAINMENUHOTSPOT_NONE) {
|
|
if (entries[entryNr].rect.contains(_mouseX, _mouseY))
|
|
clickingOn = entries[entryNr].id;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
// left mouse button released/not pushed down
|
|
if (clickingOn != MAINMENUHOTSPOT_NONE)
|
|
clickRelease = true;
|
|
}
|
|
if (_shouldQuit) {
|
|
clickingOn = MAINMENUHOTSPOT_NONE;
|
|
clickRelease = true;
|
|
doExit = true;
|
|
}
|
|
}
|
|
|
|
if (clickingOn != MAINMENUHOTSPOT_NONE) {
|
|
_audioManager->playSFX(10, 128, true);
|
|
}
|
|
|
|
switch (clickingOn) {
|
|
case MAINMENUHOTSPOT_HOTKEYS:
|
|
menuMask = MAINMENUMASK_HOTKEYS;
|
|
continue;
|
|
case MAINMENUHOTSPOT_HOTKEYSCLOSE:
|
|
menuMask = MAINMENUMASK_BASE;
|
|
continue;
|
|
}
|
|
|
|
if (musicPlaying) {
|
|
//stop music
|
|
mainmenuMusic->stop(false);
|
|
delete mainmenuMusicFile;
|
|
musicPlaying = false;
|
|
}
|
|
|
|
switch (clickingOn) {
|
|
case MAINMENUHOTSPOT_START:
|
|
// Start game (actually exit main menu)
|
|
loadedGame = false;
|
|
doExit = true;
|
|
break;
|
|
case MAINMENUHOTSPOT_INTRO:
|
|
// Play intro movies
|
|
getMoviePlayer()->play("209_1M.SMK", 0x10);
|
|
getMoviePlayer()->play("209_2M.SMK", 0x10);
|
|
getMoviePlayer()->play("209_3M.SMK", 0x10);
|
|
break;
|
|
case MAINMENUHOTSPOT_LOADGAME:
|
|
doExit = loadGame(-1);
|
|
loadedGame = doExit;
|
|
exitGame = false;
|
|
break;
|
|
case MAINMENUHOTSPOT_CREDITS:
|
|
// Play credits movie
|
|
getMoviePlayer()->play("CREDITS.SMK", 0x0);
|
|
break;
|
|
case MAINMENUHOTSPOT_QUIT:
|
|
exitGame = true;
|
|
doExit = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
_gameState->_inMenu = false;
|
|
|
|
//delete mainmenuMusic;
|
|
for (int entryNr = 0; entryNr < MAINMENU_ENTRYCOUNT; entryNr++)
|
|
delete entries[entryNr].animation;
|
|
delete mainmenuPicture;
|
|
|
|
return !exitGame;
|
|
}
|
|
|
|
Common::Error ToonEngine::run() {
|
|
|
|
if (!loadToonDat())
|
|
return Common::kUnknownError;
|
|
|
|
initGraphics(TOON_SCREEN_WIDTH, TOON_SCREEN_HEIGHT, true);
|
|
init();
|
|
|
|
// do we need to load directly a game?
|
|
bool loadedGame = false;
|
|
int32 slot = ConfMan.getInt("save_slot");
|
|
if (slot > -1) {
|
|
loadedGame = loadGame(slot);
|
|
}
|
|
|
|
if (!loadedGame) {
|
|
|
|
// play producer intro
|
|
getMoviePlayer()->play("VIELOGOM.SMK", 0x10);
|
|
|
|
// show mainmenu
|
|
if (!showMainmenu(loadedGame)) {
|
|
return Common::kNoError;
|
|
}
|
|
}
|
|
|
|
//loadScene(17);
|
|
//loadScene(37);
|
|
if (!loadedGame) {
|
|
newGame();
|
|
}
|
|
|
|
while (!_shouldQuit && _gameState->_currentScene != -1)
|
|
doFrame();
|
|
return Common::kNoError;
|
|
}
|
|
|
|
ToonEngine::ToonEngine(OSystem *syst, const ADGameDescription *gameDescription)
|
|
: Engine(syst), _gameDescription(gameDescription),
|
|
_language(gameDescription->language), _rnd("toon") {
|
|
_tickLength = 16;
|
|
_currentPicture = NULL;
|
|
_inventoryPicture = NULL;
|
|
_currentMask = NULL;
|
|
_showConversationText = true;
|
|
_isDemo = _gameDescription->flags & ADGF_DEMO;
|
|
|
|
DebugMan.addDebugChannel(kDebugAnim, "Anim", "Animation debug level");
|
|
DebugMan.addDebugChannel(kDebugCharacter, "Character", "Character debug level");
|
|
DebugMan.addDebugChannel(kDebugAudio, "Audio", "Audio debug level");
|
|
DebugMan.addDebugChannel(kDebugHotspot, "Hotspot", "Hotspot debug level");
|
|
DebugMan.addDebugChannel(kDebugFont, "Font", "Font debug level");
|
|
DebugMan.addDebugChannel(kDebugPath, "Path", "Path debug level");
|
|
DebugMan.addDebugChannel(kDebugMovie, "Movie", "Movie debug level");
|
|
DebugMan.addDebugChannel(kDebugPicture, "Picture", "Picture debug level");
|
|
DebugMan.addDebugChannel(kDebugResource, "Resource", "Resource debug level");
|
|
DebugMan.addDebugChannel(kDebugState, "State", "State debug level");
|
|
DebugMan.addDebugChannel(kDebugTools, "Tools", "Tools debug level");
|
|
DebugMan.addDebugChannel(kDebugText, "Text", "Text debug level");
|
|
|
|
_resources = NULL;
|
|
_animationManager = NULL;
|
|
_moviePlayer = NULL;
|
|
_mainSurface = NULL;
|
|
|
|
_finalPalette = NULL;
|
|
_backupPalette = NULL;
|
|
_additionalPalette1 = NULL;
|
|
_additionalPalette2 = NULL;
|
|
_additionalPalette2Present = false;
|
|
_cutawayPalette = NULL;
|
|
_universalPalette = NULL;
|
|
_fluxPalette = NULL;
|
|
|
|
_roomScaleData = NULL;
|
|
_shadowLUT = NULL;
|
|
|
|
_conversationData = NULL;
|
|
|
|
_fontRenderer = NULL;
|
|
_fontToon = NULL;
|
|
_fontEZ = NULL;
|
|
_hotspots = NULL;
|
|
_genericTexts = NULL;
|
|
_roomTexts = NULL;
|
|
_script_func = NULL;
|
|
_script = NULL;
|
|
|
|
_mouseX = 0;
|
|
_mouseY = 0;
|
|
_mouseButton = 0;
|
|
_lastMouseButton = 0;
|
|
|
|
_saveBufferStream = NULL;
|
|
|
|
_pathFinding = NULL;
|
|
_console = new ToonConsole(this);
|
|
|
|
_cursorAnimation = NULL;
|
|
_cursorAnimationInstance = NULL;
|
|
_dialogIcons = NULL;
|
|
_inventoryIcons = NULL;
|
|
_inventoryIconSlots = NULL;
|
|
_genericTexts = NULL;
|
|
_audioManager = NULL;
|
|
_gameState = NULL;
|
|
|
|
_locationDirNotVisited = NULL;
|
|
_locationDirVisited = NULL;
|
|
_specialInfoLine = NULL;
|
|
|
|
for (int i = 0; i < 64; i++) {
|
|
_sceneAnimations[i]._active = false;
|
|
}
|
|
|
|
for (int i = 0; i < 32; i++) {
|
|
_characters[i] = NULL;
|
|
}
|
|
|
|
memset(&_scriptData, 0, sizeof(EMCData));
|
|
|
|
switch (_language) {
|
|
case Common::EN_GRB:
|
|
case Common::EN_USA:
|
|
case Common::EN_ANY:
|
|
_gameVariant = 0;
|
|
break;
|
|
case Common::FR_FRA:
|
|
_gameVariant = 1;
|
|
break;
|
|
case Common::DE_DEU:
|
|
_gameVariant = 2;
|
|
break;
|
|
case Common::RU_RUS:
|
|
_gameVariant = 3;
|
|
break;
|
|
case Common::ES_ESP:
|
|
_gameVariant = 4;
|
|
break;
|
|
default:
|
|
// 0 - english
|
|
_gameVariant = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
ToonEngine::~ToonEngine() {
|
|
delete _currentPicture;
|
|
delete _currentMask;
|
|
delete _inventoryPicture;
|
|
|
|
delete _resources;
|
|
delete _animationManager;
|
|
delete _moviePlayer;
|
|
|
|
if (_mainSurface) {
|
|
_mainSurface->free();
|
|
delete _mainSurface;
|
|
}
|
|
|
|
delete[] _finalPalette;
|
|
delete[] _backupPalette;
|
|
delete[] _additionalPalette1;
|
|
delete[] _additionalPalette2;
|
|
delete[] _cutawayPalette;
|
|
delete[] _universalPalette;
|
|
delete[] _fluxPalette;
|
|
|
|
delete[] _roomScaleData;
|
|
delete[] _shadowLUT;
|
|
|
|
delete[] _conversationData;
|
|
|
|
delete _fontRenderer;
|
|
delete _fontToon;
|
|
delete _fontEZ;
|
|
delete _hotspots;
|
|
delete _genericTexts;
|
|
delete _roomTexts;
|
|
delete _script_func;
|
|
|
|
_script->unload(&_scriptData);
|
|
delete _script;
|
|
|
|
delete _saveBufferStream;
|
|
|
|
delete _pathFinding;
|
|
|
|
for (int32 i = 0; i < 64; i++) {
|
|
if (_sceneAnimations[i]._active) {
|
|
// see if one character shares this instance
|
|
for (int32 c = 0; c < 32; c++) {
|
|
if (_characters[c] && _characters[c]->getAnimationInstance() == _sceneAnimations[i]._animInstance) {
|
|
_characters[c]->setAnimationInstance(0);
|
|
}
|
|
}
|
|
delete _sceneAnimations[i]._originalAnimInstance;
|
|
delete _sceneAnimations[i]._animation;
|
|
}
|
|
}
|
|
|
|
for (int32 i = 0; i < 32; i++)
|
|
delete _characters[i];
|
|
|
|
delete _cursorAnimation;
|
|
delete _cursorAnimationInstance;
|
|
delete _dialogIcons;
|
|
delete _inventoryIcons;
|
|
delete _inventoryIconSlots;
|
|
//delete _genericTexts;
|
|
delete _audioManager;
|
|
delete _gameState;
|
|
|
|
unloadToonDat();
|
|
|
|
DebugMan.clearAllDebugChannels();
|
|
delete _console;
|
|
}
|
|
|
|
void ToonEngine::flushPalette(bool deferFlushToNextRender) {
|
|
|
|
if (deferFlushToNextRender) {
|
|
_needPaletteFlush = true;
|
|
return;
|
|
}
|
|
_needPaletteFlush = false;
|
|
_system->getPaletteManager()->setPalette(_finalPalette, 0, 256);
|
|
}
|
|
void ToonEngine::setPaletteEntries(uint8 *palette, int32 offset, int32 num) {
|
|
memcpy(_finalPalette + offset * 3, palette, num * 3);
|
|
flushPalette();
|
|
}
|
|
|
|
void ToonEngine::simpleUpdate(bool waitCharacterToTalk) {
|
|
int32 elapsedTime = _system->getMillis() - _oldTimer2;
|
|
_oldTimer2 = _system->getMillis();
|
|
_oldTimer = _oldTimer2;
|
|
|
|
if (!_audioManager->voiceStillPlaying() && !waitCharacterToTalk) {
|
|
_currentTextLine = 0;
|
|
_currentTextLineId = -1;
|
|
}
|
|
|
|
updateCharacters(elapsedTime);
|
|
updateAnimationSceneScripts(elapsedTime);
|
|
updateTimer(elapsedTime);
|
|
_animationManager->update(elapsedTime);
|
|
_audioManager->updateAmbientSFX();
|
|
render();
|
|
}
|
|
|
|
void ToonEngine::fixPaletteEntries(uint8 *palette, int num) {
|
|
// some color values are coded on 6bits ( for old 6bits DAC )
|
|
for (int32 i = 0; i < num * 3; i++) {
|
|
int32 a = palette[i];
|
|
a = a * 4;
|
|
if (a > 255)
|
|
a = 255;
|
|
palette[i] = a;
|
|
}
|
|
}
|
|
|
|
// adapted from KyraEngine
|
|
void ToonEngine::updateAnimationSceneScripts(int32 timeElapsed) {
|
|
static int32 numReentrant = 0;
|
|
numReentrant++;
|
|
const int startScript = _lastProcessedSceneScript;
|
|
|
|
_updatingSceneScriptRunFlag = true;
|
|
|
|
do {
|
|
if (_sceneAnimationScripts[_lastProcessedSceneScript]._lastTimer <= _system->getMillis() &&
|
|
!_sceneAnimationScripts[_lastProcessedSceneScript]._frozen && !_sceneAnimationScripts[_lastProcessedSceneScript]._frozenForConversation) {
|
|
_animationSceneScriptRunFlag = true;
|
|
|
|
while (_animationSceneScriptRunFlag && _sceneAnimationScripts[_lastProcessedSceneScript]._lastTimer <= _system->getMillis() && !_shouldQuit) {
|
|
if (!_script->run(&_sceneAnimationScripts[_lastProcessedSceneScript]._state))
|
|
_animationSceneScriptRunFlag = false;
|
|
|
|
//waitForScriptStep();
|
|
|
|
if (_sceneAnimationScripts[_lastProcessedSceneScript]._frozen || _sceneAnimationScripts[_lastProcessedSceneScript]._frozenForConversation)
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
if (!_script->isValid(&_sceneAnimationScripts[_lastProcessedSceneScript]._state)) {
|
|
_script->start(&_sceneAnimationScripts[_lastProcessedSceneScript]._state, 9 + _lastProcessedSceneScript);
|
|
_animationSceneScriptRunFlag = false;
|
|
}
|
|
|
|
++_lastProcessedSceneScript;
|
|
if (_lastProcessedSceneScript >= state()->_locations[state()->_currentScene]._numSceneAnimations)
|
|
_lastProcessedSceneScript = 0;
|
|
|
|
} while (_lastProcessedSceneScript != startScript && !_shouldQuit);
|
|
|
|
_updatingSceneScriptRunFlag = false;
|
|
numReentrant--;
|
|
}
|
|
|
|
void ToonEngine::loadScene(int32 SceneId, bool forGameLoad) {
|
|
_firstFrame = true;
|
|
|
|
_gameState->_lastVisitedScene = _gameState->_currentScene;
|
|
_gameState->_currentScene = SceneId;
|
|
|
|
_saveBufferStream->seek(0);
|
|
|
|
if (SceneId == -1) {
|
|
// this scene -1 is loaded at the very end of the game
|
|
getAudioManager()->stopMusic();
|
|
getMoviePlayer()->play("CREDITS.SMK");
|
|
return;
|
|
}
|
|
|
|
// find out in what chapter we are (the script function ProcessToNextChapter is actually not called )
|
|
// the location flag has the chapter info in it
|
|
int32 flag = _gameState->_locations[SceneId]._flags;
|
|
if (flag) {
|
|
_gameState->_currentChapter = 0;
|
|
do {
|
|
_gameState->_currentChapter++;
|
|
flag >>= 1;
|
|
} while ((flag & 1) == 0);
|
|
}
|
|
|
|
for (int32 i = 0; i < 8; i++) {
|
|
if (_characters[i]) _characters[i]->setFlag(0);
|
|
}
|
|
_drew->playStandingAnim();
|
|
_drew->setVisible(true);
|
|
|
|
// hide flux in chapter 2
|
|
if (_gameState->_currentChapter == 1) {
|
|
_flux->playStandingAnim();
|
|
_flux->setVisible(true);
|
|
} else {
|
|
_flux->setVisible(false);
|
|
}
|
|
|
|
_lastMouseButton = 0;
|
|
_mouseButton = 0;
|
|
_currentHotspotItem = 0;
|
|
|
|
if (!forGameLoad) {
|
|
_gameState->_sackVisible = true;
|
|
_gameState->_inCloseUp = false;
|
|
_gameState->_inConversation = false;
|
|
_gameState->_inInventory = false;
|
|
_gameState->_inCutaway = false;
|
|
_gameState->_currentScrollValue = 0;
|
|
_gameState->_currentScrollLock = false;
|
|
_gameState->_inCloseUp = false;
|
|
}
|
|
|
|
if (_gameState->_mouseState >= 0)
|
|
addItemToInventory(_gameState->_mouseState);
|
|
|
|
_gameState->_mouseState = -1;
|
|
_mouseButton = 0;
|
|
_lastMouseButton = 0x3;
|
|
|
|
Common::String locationName = state()->_locations[SceneId]._name;
|
|
|
|
// load package
|
|
resources()->openPackage(createRoomFilename(locationName + ".PAK"));
|
|
|
|
loadAdditionalPalette(locationName + ".NPP", 0);
|
|
|
|
_additionalPalette2Present = false;
|
|
loadAdditionalPalette(locationName + ".NP2", 1);
|
|
|
|
loadAdditionalPalette(locationName + ".CUP", 2);
|
|
|
|
// load artwork
|
|
delete _currentPicture;
|
|
_currentPicture = new Picture(this);
|
|
_currentPicture->loadPicture(locationName + ".CPS");
|
|
_currentPicture->setupPalette();
|
|
|
|
delete _currentMask;
|
|
_currentMask = new Picture(this);
|
|
if (_currentMask->loadPicture(locationName + ".MSC"))
|
|
_pathFinding->init(_currentMask);
|
|
|
|
delete _roomTexts;
|
|
_roomTexts = new TextResource(this);
|
|
_roomTexts->loadTextResource(locationName + ".TRE");
|
|
|
|
uint32 fileSize;
|
|
uint8 *sceneData = resources()->getFileData(locationName + ".DAT", &fileSize);
|
|
if (sceneData) {
|
|
delete[] _roomScaleData;
|
|
_roomScaleData = new uint8[fileSize];
|
|
memcpy(_roomScaleData, sceneData, fileSize);
|
|
}
|
|
|
|
_audioManager->loadAudioPack(1, locationName + ".SVI", createRoomFilename(locationName + ".SVL"));
|
|
_audioManager->loadAudioPack(3, locationName + ".SEI", locationName + ".SEL");
|
|
|
|
if (state()->_locations[SceneId]._flags & 0x40) {
|
|
Common::String cutaway = state()->_locations[SceneId]._cutaway;
|
|
_hotspots->LoadRif(locationName + ".RIC", cutaway + ".RIC");
|
|
} else {
|
|
_hotspots->LoadRif(locationName + ".RIC", "");
|
|
}
|
|
restoreRifFlags(_gameState->_currentScene);
|
|
|
|
uint32 convfileSize;
|
|
uint8 *convData = resources()->getFileData(locationName + ".CNV", &convfileSize);
|
|
if (convData) {
|
|
assert(convfileSize < 4096 * sizeof(int16));
|
|
memcpy(_conversationData , convData, convfileSize);
|
|
prepareConversations();
|
|
}
|
|
|
|
// load script
|
|
|
|
_oldTimer = _system->getMillis();
|
|
_oldTimer2 = _oldTimer;
|
|
|
|
// fix the weird scaling issue during one frame when entering new scene
|
|
_drew->update(0);
|
|
_flux->update(0);
|
|
|
|
_script->unload(&_scriptData);
|
|
Common::String emcfile = locationName + ".EMC";
|
|
_script->load(emcfile.c_str(), &_scriptData, &_script_func->_opcodes);
|
|
_script->init(&_scriptState[0], &_scriptData);
|
|
_script->init(&_scriptState[1], &_scriptData);
|
|
_script->init(&_scriptState[2], &_scriptData);
|
|
_script->init(&_scriptState[3], &_scriptData);
|
|
|
|
//_script->RoomScript->Decompile("decomp.txt");
|
|
//RoomScript->Decompile2("decomp2.txt");
|
|
|
|
for (int i = 0; i < state()->_locations[SceneId]._numSceneAnimations; i++) {
|
|
_sceneAnimationScripts[i]._data = &_scriptData;
|
|
_script->init(&_sceneAnimationScripts[i]._state, _sceneAnimationScripts[i]._data);
|
|
if (!forGameLoad) {
|
|
_script->start(&_sceneAnimationScripts[i]._state, 9 + i);
|
|
_sceneAnimationScripts[i]._lastTimer = _system->getMillis();
|
|
_sceneAnimationScripts[i]._frozen = false;
|
|
_sceneAnimationScripts[i]._frozenForConversation = false;
|
|
}
|
|
}
|
|
|
|
playRoomMusic();
|
|
|
|
_lastProcessedSceneScript = 0;
|
|
_gameState->_locations[SceneId]._visited = true;
|
|
|
|
setupGeneralPalette();
|
|
createShadowLUT();
|
|
|
|
state()->_mouseHidden = false;
|
|
|
|
clearDirtyRects();
|
|
dirtyAllScreen();
|
|
|
|
if (!forGameLoad) {
|
|
|
|
_script->start(&_scriptState[0], 0);
|
|
|
|
while (_script->run(&_scriptState[0]))
|
|
waitForScriptStep();
|
|
|
|
_script->start(&_scriptState[0], 8);
|
|
|
|
while (_script->run(&_scriptState[0]))
|
|
waitForScriptStep();
|
|
|
|
if (_gameState->_nextSpecialEnterX != -1 && _gameState->_nextSpecialEnterY != -1) {
|
|
_drew->forcePosition(_gameState->_nextSpecialEnterX, _gameState->_nextSpecialEnterY);
|
|
_gameState->_nextSpecialEnterX = -1;
|
|
_gameState->_nextSpecialEnterY = -1;
|
|
}
|
|
|
|
_script->start(&_scriptState[0], 3);
|
|
|
|
while (_script->run(&_scriptState[0]))
|
|
waitForScriptStep();
|
|
|
|
_script->start(&_scriptState[0], 4);
|
|
|
|
while (_script->run(&_scriptState[0]))
|
|
waitForScriptStep();
|
|
|
|
}
|
|
}
|
|
|
|
void ToonEngine::setupGeneralPalette() {
|
|
setPaletteEntries(_additionalPalette1, 232, 23);
|
|
setPaletteEntries(_universalPalette, 200, 32);
|
|
setPaletteEntries(_fluxPalette, 192, 8);
|
|
|
|
if (_drew)
|
|
_drew->setupPalette();
|
|
}
|
|
|
|
void ToonEngine::loadAdditionalPalette(const Common::String &fileName, int32 mode) {
|
|
|
|
uint32 size = 0;
|
|
uint8 *palette = resources()->getFileData(fileName, &size);
|
|
if (!palette)
|
|
return;
|
|
|
|
switch (mode) {
|
|
case 0:
|
|
memcpy(_additionalPalette1, palette, 69);
|
|
fixPaletteEntries(_additionalPalette1, 23);
|
|
break;
|
|
case 1:
|
|
memcpy(_additionalPalette2, palette, 69);
|
|
fixPaletteEntries(_additionalPalette2, 23);
|
|
_additionalPalette2Present = true;
|
|
break;
|
|
case 2:
|
|
memcpy(_cutawayPalette, palette, size);
|
|
fixPaletteEntries(_cutawayPalette, size/3);
|
|
break;
|
|
case 3:
|
|
memcpy(_universalPalette, palette, 96);
|
|
fixPaletteEntries(_universalPalette, 32);
|
|
break;
|
|
case 4:
|
|
memcpy(_fluxPalette, palette, 24);
|
|
fixPaletteEntries(_fluxPalette , 8);
|
|
break;
|
|
default:
|
|
warning("loadAdditionalPalette() - Unknown mode");
|
|
}
|
|
}
|
|
|
|
void ToonEngine::initChapter() {
|
|
|
|
EMCData data;
|
|
EMCState status;
|
|
memset(&data, 0, sizeof(data));
|
|
memset(&status, 0, sizeof(status));
|
|
|
|
delete _script;
|
|
_script = new EMCInterpreter(this);
|
|
|
|
_script->load("_START01.EMC", &data, &_script_func->_opcodes);
|
|
_script->init(&status, &data);
|
|
_script->start(&status, 0);
|
|
while (_script->run(&status))
|
|
waitForScriptStep();
|
|
|
|
_script->unload(&data);
|
|
|
|
setupGeneralPalette();
|
|
}
|
|
|
|
void ToonEngine::loadCursor() {
|
|
delete _cursorAnimation;
|
|
_cursorAnimation = new Animation(this);
|
|
_cursorAnimation->loadAnimation("MOUSE.CAF");
|
|
delete _cursorAnimationInstance;
|
|
_cursorAnimationInstance = _animationManager->createNewInstance(kAnimationCursor);
|
|
_cursorAnimationInstance->setAnimation(_cursorAnimation);
|
|
_cursorAnimationInstance->setVisible(true);
|
|
_cursorAnimationInstance->setFrame(0);
|
|
_cursorAnimationInstance->setAnimationRange(0, 0);
|
|
_cursorAnimationInstance->setFps(8);
|
|
|
|
setCursor(5);
|
|
}
|
|
|
|
void ToonEngine::setCursor(int32 type, bool inventory, int32 offsetX, int offsetY) {
|
|
|
|
static const int32 offsets[] = {
|
|
0, 1, 1, 6, 7, 1, 8, 10, 18, 10,
|
|
28, 8, 36, 10, 46, 10, 56, 10, 66, 10,
|
|
76, 10, 86, 10, 96, 10, 106, 10, 116, 10,
|
|
126, 10
|
|
};
|
|
|
|
if (!inventory) {
|
|
_cursorAnimationInstance->setAnimation(_cursorAnimation);
|
|
_cursorAnimationInstance->setAnimationRange(offsets[type * 2 + 0], offsets[type * 2 + 0] + offsets[type * 2 + 1] - 1);
|
|
_cursorAnimationInstance->playAnimation();
|
|
} else {
|
|
_cursorAnimationInstance->setAnimation(_inventoryIcons);
|
|
_cursorAnimationInstance->setAnimationRange(type, type);
|
|
_cursorAnimationInstance->playAnimation();
|
|
}
|
|
|
|
_cursorOffsetX = offsetX;
|
|
_cursorOffsetY = offsetY;
|
|
}
|
|
|
|
void ToonEngine::setSceneAnimationScriptUpdate(bool enable) {
|
|
_animationSceneScriptRunFlag = enable;
|
|
}
|
|
|
|
bool ToonEngine::isUpdatingSceneAnimation() {
|
|
return _updatingSceneScriptRunFlag;
|
|
}
|
|
|
|
int32 ToonEngine::getCurrentUpdatingSceneAnimation() {
|
|
return _lastProcessedSceneScript;
|
|
}
|
|
|
|
int32 ToonEngine::randRange(int32 minStart, int32 maxStart) {
|
|
return _rnd.getRandomNumberRng(minStart, maxStart);
|
|
}
|
|
|
|
int32 ToonEngine::runEventScript(int32 x, int32 y, int32 mode, int32 id, int32 scriptId) {
|
|
|
|
if (_currentScriptRegion >= 4)
|
|
return 0;
|
|
|
|
EMCState *status = &_scriptState[_currentScriptRegion];
|
|
_script->init(status, &_scriptData);
|
|
|
|
// setup registers
|
|
status->regs[0] = x;
|
|
status->regs[1] = y;
|
|
status->regs[2] = 0;
|
|
status->regs[3] = 0;
|
|
status->regs[4] = _gameState->_mouseState; //
|
|
status->regs[5] = 0;
|
|
status->regs[6] = scriptId;
|
|
status->regs[7] = mode;
|
|
status->regs[8] = id;
|
|
|
|
_currentScriptRegion++;
|
|
|
|
_script->start(status, 1);
|
|
while (_script->run(status))
|
|
waitForScriptStep();
|
|
|
|
_currentScriptRegion--;
|
|
|
|
return status->regs[2];
|
|
}
|
|
|
|
void ToonEngine::clickEvent() {
|
|
bool leftButton = false;
|
|
bool rightButton = false;
|
|
|
|
if ((_lastMouseButton & 0x1) == 0 && (_mouseButton & 0x1) == 1)
|
|
leftButton = true;
|
|
if ((_lastMouseButton & 0x2) == 0 && (_mouseButton & 0x2) == 2)
|
|
rightButton = true;
|
|
|
|
_lastMouseButton = _mouseButton;
|
|
if (!leftButton && !rightButton)
|
|
return;
|
|
|
|
if (_gameState->_sackVisible) {
|
|
if (_mouseX > 0 && _mouseX < 40 && _mouseY > 356 && _mouseY < 396) {
|
|
if (_gameState->_mouseState >= 0 && !rightButton) {
|
|
addItemToInventory(_gameState->_mouseState);
|
|
setCursor(0, false, 0, 0);
|
|
_currentHotspotItem = 0;
|
|
return;
|
|
} else {
|
|
showInventory();
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
// with inventory
|
|
if (rightButton && _gameState->_mouseState >= 0) {
|
|
addItemToInventory(_gameState->_mouseState);
|
|
setCursor(0, false, 0, 0);
|
|
_currentHotspotItem = 0;
|
|
return;
|
|
}
|
|
|
|
int32 mouseX = _mouseX;
|
|
if (_gameState->_inCutaway) {
|
|
mouseX += TOON_BACKBUFFER_WIDTH;
|
|
}
|
|
|
|
// find hotspot
|
|
int32 hot = _hotspots->Find(mouseX + state()->_currentScrollValue , _mouseY);
|
|
HotspotData *currentHot = 0;
|
|
if (hot > -1) {
|
|
currentHot = _hotspots->Get(hot);
|
|
}
|
|
|
|
if (_currentHotspotItem == -3) {
|
|
if (_gameState->_mouseState <= 0) {
|
|
if (leftButton)
|
|
createMouseItem(104);
|
|
else
|
|
characterTalk(1104);
|
|
}
|
|
}
|
|
if (_currentHotspotItem == -4) {
|
|
if (_gameState->_mouseState >= 0) {
|
|
if (leftButton)
|
|
if (!handleInventoryOnInventory(0, _gameState->_mouseState)) {
|
|
playSoundWrong();
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (!currentHot) {
|
|
int16 xx, yy;
|
|
|
|
if (_gameState->_inCutaway || _gameState->_inInventory || _gameState->_inCloseUp)
|
|
return;
|
|
|
|
if (_pathFinding->findClosestWalkingPoint(_mouseX + _gameState->_currentScrollValue , _mouseY, &xx, &yy))
|
|
_drew->walkTo(xx, yy);
|
|
return;
|
|
}
|
|
|
|
int commandId = 0;
|
|
if (_gameState->_mouseState < 0) {
|
|
// left or right click
|
|
if (rightButton)
|
|
commandId = 2 + 8;
|
|
else
|
|
commandId = 0 + 8;
|
|
} else {
|
|
commandId = 2 * (_gameState->_mouseState - 1) + 16;
|
|
}
|
|
|
|
_drew->stopWalk();
|
|
|
|
int16 command = currentHot->getData(commandId);
|
|
int16 argument = currentHot->getData(commandId + 1);
|
|
int16 priority = currentHot->getPriority();
|
|
|
|
if (!_gameState->_inCutaway && !_gameState->_inCloseUp) {
|
|
if (leftButton && (currentHot->getData(4) != 2 || _gameState->_mouseState >= 0) && currentHot->getData(5) != -1) {
|
|
if (currentHot->getData(5)) {
|
|
if (!_drew->walkTo(currentHot->getData(5), currentHot->getData(6))) {
|
|
// walk was canceled ?
|
|
return;
|
|
}
|
|
} else {
|
|
if (!_drew->walkTo(_mouseX + _gameState->_currentScrollValue, _mouseY)) {
|
|
// walk was canceled ?
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int32 result = 0;
|
|
|
|
switch (command) {
|
|
case 1:
|
|
sayLines(1, argument);
|
|
break;
|
|
case 2:
|
|
result = runEventScript(_mouseX, _mouseY, command, argument, priority);
|
|
break;
|
|
case 3:
|
|
runEventScript(_mouseX, _mouseY, command, argument, priority);
|
|
result = 0;
|
|
break;
|
|
case 4:
|
|
playSFX(argument, 128);
|
|
break;
|
|
case 5:
|
|
break;
|
|
case 6:
|
|
createMouseItem(argument);
|
|
currentHot->setData(7, -1);
|
|
break;
|
|
case 7:
|
|
// switch to CloseUp
|
|
break;
|
|
case 8:
|
|
// face flux
|
|
sayLines(1, argument);
|
|
break;
|
|
case 9:
|
|
case 10:
|
|
sayLines(2, argument);
|
|
break;
|
|
case 11:
|
|
sayLines(3, argument);
|
|
break;
|
|
default:
|
|
playSoundWrong();
|
|
return;
|
|
}
|
|
|
|
if (result == 3) {
|
|
int32 val = _scriptState[_currentScriptRegion].regs[4];
|
|
currentHot->setData(4, currentHot->getData(4) & val);
|
|
}
|
|
if (result == 2 || result == 3) {
|
|
int32 val = _scriptState[_currentScriptRegion].regs[6];
|
|
currentHot->setData(7, val);
|
|
}
|
|
|
|
if (result == 1) {
|
|
int32 val = _scriptState[_currentScriptRegion].regs[4];
|
|
currentHot->setData(4, currentHot->getData(4) & val);
|
|
}
|
|
}
|
|
|
|
void ToonEngine::selectHotspot() {
|
|
int16 x1 = 0;
|
|
int16 x2 = 0;
|
|
int16 y1 = 0;
|
|
int16 y2 = 0;
|
|
|
|
int16 mouseX = _mouseX;
|
|
|
|
if (_gameState->_inCutaway)
|
|
mouseX += TOON_BACKBUFFER_WIDTH;
|
|
|
|
if (_gameState->_sackVisible) {
|
|
if (_mouseX > 0 && _mouseX < 40 && _mouseY > 356 && _mouseY < 396) {
|
|
_currentHotspotItem = -2;
|
|
|
|
if (_gameState->_mouseState < 0) {
|
|
int mode = 3;
|
|
setCursor(mode);
|
|
} else {
|
|
setCursor(_gameState->_mouseState, true, -18, -14);
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (_gameState->_mouseState > 0) {
|
|
// picked drew?
|
|
getDrew()->getAnimationInstance()->getRect(&x1, &y1, &x2, &y2);
|
|
if (_mouseX + _gameState->_currentScrollValue >= x1 && _mouseX + _gameState->_currentScrollValue <= x2 && _mouseY >= y1 && _mouseY <= y2) {
|
|
_currentHotspotItem = -4;
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (getFlux()->getVisible()) {
|
|
getFlux()->getAnimationInstance()->getRect(&x1, &y1, &x2, &y2);
|
|
if (_mouseX + _gameState->_currentScrollValue >= x1 && _mouseX + _gameState->_currentScrollValue <= x2 && _mouseY >= y1 && _mouseY <= y2) {
|
|
_currentHotspotItem = -3;
|
|
|
|
if (_gameState->_mouseState < 0) {
|
|
int mode = 3;
|
|
setCursor(mode);
|
|
} else {
|
|
setCursor(_gameState->_mouseState, true, -18, -14);
|
|
}
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
int32 hot = _hotspots->Find(mouseX + state()->_currentScrollValue, _mouseY);
|
|
if (hot != -1) {
|
|
HotspotData *hotspot = _hotspots->Get(hot);
|
|
int32 item = hotspot->getData(14);
|
|
if (hotspot->getType() == 3)
|
|
item += 2000;
|
|
|
|
// update palette based on ticks if we're in "use from inventory mode"
|
|
if (_gameState->_mouseState >= 0) {
|
|
|
|
int32 tick = _system->getMillis() / _tickLength;
|
|
int32 animReverse = tick & 0x10;
|
|
int32 animStep = tick & 0xf;
|
|
|
|
byte color[3];
|
|
if (animReverse == 0) {
|
|
color[0] = 16 * animStep;
|
|
color[1] = 0;
|
|
color[2] = 0;
|
|
} else {
|
|
color[0] = 16 * (15 - animStep);
|
|
color[1] = 0;
|
|
color[2] = 0;
|
|
}
|
|
setPaletteEntries(color, 255, 1);
|
|
}
|
|
|
|
#if 0
|
|
if (item == _currentHotspotItem)
|
|
return;
|
|
#endif
|
|
_currentHotspotItem = item;
|
|
if (_gameState->_mouseState < 0) {
|
|
int mode = hotspot->getMode();
|
|
setCursor(mode);
|
|
} else {
|
|
setCursor(_gameState->_mouseState, true, -18, -14);
|
|
}
|
|
} else {
|
|
_currentHotspotItem = 0;
|
|
|
|
if (_gameState->_mouseState < 0) {
|
|
setCursor(0);
|
|
} else {
|
|
byte color[3];
|
|
color[0] = 0;
|
|
color[1] = 0;
|
|
color[2] = 0;
|
|
setCursor(_gameState->_mouseState, true, -18, -14);
|
|
setPaletteEntries(color, 255, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ToonEngine::exitScene() {
|
|
fadeOut(5);
|
|
|
|
// disable all scene animation
|
|
for (int32 i = 0; i < 64; i++) {
|
|
if (_sceneAnimations[i]._active) {
|
|
delete _sceneAnimations[i]._animation;
|
|
_sceneAnimations[i]._active = false;
|
|
_animationManager->removeInstance(_sceneAnimations[i]._animInstance);
|
|
|
|
// see if one character shares this instance
|
|
for (int32 c = 0; c < 32; c++) {
|
|
if (_characters[c] && _characters[c]->getAnimationInstance() == _sceneAnimations[i]._animInstance) {
|
|
_characters[c]->setAnimationInstance(NULL);
|
|
}
|
|
}
|
|
|
|
delete _sceneAnimations[i]._originalAnimInstance;
|
|
_sceneAnimations[i]._animInstance = NULL;
|
|
_sceneAnimations[i]._animation = NULL;
|
|
_sceneAnimations[i]._originalAnimInstance = NULL;
|
|
}
|
|
}
|
|
for (int32 i = 0; i < 64; i++) {
|
|
_sceneAnimationScripts[i]._frozen = true;
|
|
_sceneAnimationScripts[i]._active = false;
|
|
}
|
|
|
|
// remove all characters except drew and flux
|
|
for (int32 i = 0; i < 8; i++) {
|
|
if (_characters[i] != _drew && _characters[i] != _flux) {
|
|
if (_characters[i]) {
|
|
delete _characters[i];
|
|
_characters[i] = 0;
|
|
}
|
|
} else {
|
|
_characters[i]->stopSpecialAnim();
|
|
}
|
|
}
|
|
|
|
for (int32 i = 0; i < 2; i++) {
|
|
_gameState->_timerEnabled[i] = false;
|
|
}
|
|
|
|
// put back our item if inventory if needed
|
|
if (_gameState->_mouseState >= 0) {
|
|
addItemToInventory(_gameState->_mouseState);
|
|
_gameState->_mouseState = -1;
|
|
}
|
|
|
|
_audioManager->killAllAmbientSFX();
|
|
_audioManager->stopAllSfxs();
|
|
_audioManager->stopCurrentVoice();
|
|
_currentTextLine = 0;
|
|
_currentTextLineId = -1;
|
|
_currentTextLineCharacterId = 0;
|
|
|
|
Common::String locationName = _gameState->_locations[_gameState->_currentScene]._name;
|
|
resources()->closePackage(createRoomFilename(locationName + ".PAK"));
|
|
|
|
_drew->stopWalk();
|
|
_flux->stopWalk();
|
|
|
|
storeRifFlags(_gameState->_currentScene);
|
|
}
|
|
|
|
// flip between the cutaway scene and the normal scene
|
|
void ToonEngine::flipScreens() {
|
|
_gameState->_inCloseUp = !_gameState->_inCloseUp;
|
|
|
|
if (_gameState->_inCloseUp) {
|
|
_gameState->_currentScrollValue = TOON_SCREEN_WIDTH;
|
|
setPaletteEntries(_cutawayPalette, 1, 128);
|
|
if (_additionalPalette2Present)
|
|
setPaletteEntries(_additionalPalette2, 232, 23);
|
|
} else {
|
|
_gameState->_currentScrollValue = 0;
|
|
_currentPicture->setupPalette();
|
|
setupGeneralPalette();
|
|
}
|
|
flushPalette();
|
|
}
|
|
|
|
void ToonEngine::fadeIn(int32 numFrames) {
|
|
for (int32 f = 0; f < numFrames; f++) {
|
|
|
|
uint8 vmpalette[3 * 256];
|
|
for (int32 i = 0; i < 256; i++) {
|
|
vmpalette[i * 3 + 0] = f * _finalPalette[i * 3 + 0] / (numFrames - 1);
|
|
vmpalette[i * 3 + 1] = f * _finalPalette[i * 3 + 1] / (numFrames - 1);
|
|
vmpalette[i * 3 + 2] = f * _finalPalette[i * 3 + 2] / (numFrames - 1);
|
|
}
|
|
_system->getPaletteManager()->setPalette(vmpalette, 0, 256);
|
|
_system->updateScreen();
|
|
_system->delayMillis(_tickLength);
|
|
}
|
|
}
|
|
|
|
void ToonEngine::fadeOut(int32 numFrames) {
|
|
|
|
uint8 oldpalette[3 * 256];
|
|
_system->getPaletteManager()->grabPalette(oldpalette, 0, 256);
|
|
|
|
for (int32 f = 0; f < numFrames; f++) {
|
|
uint8 vmpalette[3 * 256];
|
|
for (int32 i = 0; i < 256; i++) {
|
|
vmpalette[i * 3 + 0] = (numFrames - f - 1) * oldpalette[i * 3 + 0] / (numFrames - 1);
|
|
vmpalette[i * 3 + 1] = (numFrames - f - 1) * oldpalette[i * 3 + 1] / (numFrames - 1);
|
|
vmpalette[i * 3 + 2] = (numFrames - f - 1) * oldpalette[i * 3 + 2] / (numFrames - 1);
|
|
}
|
|
_system->getPaletteManager()->setPalette(vmpalette, 0, 256);
|
|
_system->updateScreen();
|
|
_system->delayMillis(_tickLength);
|
|
}
|
|
}
|
|
|
|
void ToonEngine::initFonts() {
|
|
_fontRenderer = new FontRenderer(this);
|
|
_fontToon = new Animation(this);
|
|
_fontToon->loadAnimation("TOONFONT.CAF");
|
|
|
|
_fontEZ = new Animation(this);
|
|
_fontEZ->loadAnimation("EZFONT.CAF");
|
|
}
|
|
|
|
void ToonEngine::drawInfoLine() {
|
|
if (_currentHotspotItem != 0 && !_gameState->_mouseHidden && !_gameState->_inConversation) {
|
|
const char *infoTool = NULL;
|
|
if (_currentHotspotItem >= 0 && _currentHotspotItem < 2000) {
|
|
infoTool = _roomTexts->getText(_currentHotspotItem);
|
|
} else if (_currentHotspotItem <= -1) {
|
|
// static const char * const specialInfoLine[] = { "Exit non defined", "Bottomless Bag", "Flux", "Drew Blanc" };
|
|
infoTool = _specialInfoLine[-1 - _currentHotspotItem];
|
|
} else {
|
|
int32 loc = _currentHotspotItem - 2000;
|
|
// location names are hardcoded ...
|
|
infoTool = getLocationString(loc, _gameState->_locations[loc]._visited);
|
|
}
|
|
if (infoTool) {
|
|
_fontRenderer->setFontColor(0xc8, 0xdd, 0xe3);
|
|
_fontRenderer->setFont(_fontToon);
|
|
_fontRenderer->renderText(320 + _gameState->_currentScrollValue, 398, infoTool, 5);
|
|
}
|
|
}
|
|
}
|
|
|
|
Common::WriteStream *ToonEngine::getSaveBufferStream() {
|
|
return _saveBufferStream;
|
|
}
|
|
|
|
const char *ToonEngine::getLocationString(int32 locationId, bool alreadyVisited) {
|
|
if (alreadyVisited)
|
|
return _locationDirVisited[locationId];
|
|
else
|
|
return _locationDirNotVisited[locationId];
|
|
}
|
|
|
|
int32 ToonEngine::getScaleAtPoint(int32 x, int32 y) {
|
|
if (!_currentMask)
|
|
return 1024;
|
|
|
|
// clamp values
|
|
x = MIN<int32>(TOON_BACKBUFFER_WIDTH - 1, MAX<int32>(0, x));
|
|
y = MIN<int32>(TOON_BACKBUFFER_HEIGHT - 1, MAX<int32>(0, y));
|
|
|
|
int32 maskData = _currentMask->getData(x, y) & 0x1f;
|
|
return _roomScaleData[maskData + 2] * 1024 / 100;
|
|
}
|
|
|
|
int32 ToonEngine::getLayerAtPoint(int32 x, int32 y) {
|
|
if (!_currentMask)
|
|
return 0;
|
|
|
|
// clamp values
|
|
x = MIN<int32>(TOON_BACKBUFFER_WIDTH - 1, MAX<int32>(0, x));
|
|
y = MIN<int32>(TOON_BACKBUFFER_HEIGHT - 1, MAX<int32>(0, y));
|
|
|
|
int32 maskData = _currentMask->getData(x, y) & 0x1f;
|
|
return _roomScaleData[maskData + 130] << 5;
|
|
}
|
|
|
|
int32 ToonEngine::getZAtPoint(int32 x, int32 y) {
|
|
if (!_currentMask)
|
|
return 0;
|
|
return _currentMask->getData(x, y) & 0x1f;
|
|
}
|
|
|
|
void ToonEngine::storeRifFlags(int32 location) {
|
|
|
|
if (_gameState->_locations[location]._numRifBoxes != _hotspots->getCount()) {
|
|
_gameState->_locations[location]._numRifBoxes = _hotspots->getCount();
|
|
}
|
|
|
|
for (int32 i = 0; i < _hotspots->getCount(); i++) {
|
|
_gameState->_locations[location]._rifBoxesFlags[i * 2 + 0] = _hotspots->Get(i)->getData(4);
|
|
_gameState->_locations[location]._rifBoxesFlags[i * 2 + 1] = _hotspots->Get(i)->getData(7);
|
|
}
|
|
}
|
|
|
|
void ToonEngine::restoreRifFlags(int32 location) {
|
|
if (_hotspots) {
|
|
if (!_gameState->_locations[location]._visited) {
|
|
for (int32 i = 0; i < _hotspots->getCount(); i++) {
|
|
_gameState->_locations[location]._rifBoxesFlags[i * 2 + 0] = _hotspots->Get(i)->getData(4);
|
|
_gameState->_locations[location]._rifBoxesFlags[i * 2 + 1] = _hotspots->Get(i)->getData(7);
|
|
}
|
|
_gameState->_locations[location]._numRifBoxes = _hotspots->getCount();
|
|
} else {
|
|
if (_gameState->_locations[location]._numRifBoxes != _hotspots->getCount())
|
|
return;
|
|
|
|
for (int32 i = 0; i < _hotspots->getCount(); i++) {
|
|
_hotspots->Get(i)->setData(4, _gameState->_locations[location]._rifBoxesFlags[i * 2 + 0]);
|
|
_hotspots->Get(i)->setData(7, _gameState->_locations[location]._rifBoxesFlags[i * 2 + 1]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ToonEngine::sayLines(int numLines, int dialogId) {
|
|
// exit conversation state
|
|
|
|
// if (inInventory)
|
|
// character_talks(dialogid, -1, 0, 0);
|
|
// else
|
|
|
|
#if 0
|
|
int oldShowMouse = 0;
|
|
|
|
if (Game.MouseHiddenCount <= 0) {
|
|
Game.MouseHiddenCount = 1;
|
|
oldShowMouse = 1;
|
|
}
|
|
#endif
|
|
|
|
int32 currentLine = dialogId;
|
|
|
|
for (int32 i = 0; i < numLines; i++) {
|
|
if (!characterTalk(currentLine))
|
|
break;
|
|
|
|
while (_audioManager->voiceStillPlaying() && !_shouldQuit)
|
|
doFrame();
|
|
|
|
// find next line
|
|
if (currentLine < 1000)
|
|
currentLine = _roomTexts->getNext(currentLine);
|
|
else
|
|
currentLine = _genericTexts->getNext(currentLine - 1000) + 1000;
|
|
}
|
|
|
|
#if 0
|
|
if (oldShowMouse)
|
|
Game.MouseHiddenCount = 0;
|
|
#endif
|
|
}
|
|
|
|
int32 ToonEngine::simpleCharacterTalk(int32 dialogid) {
|
|
int32 myId = 0;
|
|
|
|
if (_audioManager->voiceStillPlaying())
|
|
_audioManager->stopCurrentVoice();
|
|
|
|
if (dialogid < 1000) {
|
|
myId = _roomTexts->getId(dialogid);
|
|
_audioManager->playVoice(myId, false);
|
|
} else {
|
|
myId = _genericTexts->getId(dialogid - 1000);
|
|
_audioManager->playVoice(myId, true);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
void ToonEngine::playTalkAnimOnCharacter(int32 animID, int32 characterId, bool talker) {
|
|
if (animID || talker) {
|
|
if (characterId == 0) {
|
|
_drew->playAnim(animID, 0, (talker ? 8 : 0) + 2);
|
|
} else if (characterId == 1) {
|
|
// stop flux if he is walking
|
|
if (_flux->getFlag() & 1) {
|
|
_flux->stopWalk();
|
|
}
|
|
_flux->playAnim(animID, 0, (talker ? 8 : 0) + 2);
|
|
_flux->setFlag(_flux->getFlag() | 1);
|
|
} else {
|
|
Character *character = getCharacterById(characterId);
|
|
if (character) {
|
|
character->playAnim(animID, 0, (talker ? 8 : 0) + 2);
|
|
}
|
|
}
|
|
} else {
|
|
Character *character = getCharacterById(characterId);
|
|
if (character)
|
|
character->setAnimFlag(character->getAnimFlag() | 1);
|
|
}
|
|
}
|
|
|
|
int32 ToonEngine::characterTalk(int32 dialogid, bool blocking) {
|
|
if (blocking == false && _audioManager->voiceStillPlaying()) {
|
|
if (_currentTextLineCharacterId == 0 || _currentTextLineCharacterId == 1) {
|
|
// Drew or Flux is already talking, and this voice is not important
|
|
// skip it
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int32 myId = 0;
|
|
char *myLine;
|
|
if (dialogid < 1000) {
|
|
myLine = _roomTexts->getText(dialogid);
|
|
myId = dialogid;
|
|
} else {
|
|
myLine = _genericTexts->getText(dialogid - 1000);
|
|
myId = dialogid - 1000;
|
|
}
|
|
|
|
if (!myLine)
|
|
return 0;
|
|
|
|
bool oldMouseHidden = _gameState->_mouseHidden;
|
|
if (blocking) {
|
|
_gameState->_mouseHidden = true;
|
|
}
|
|
|
|
// get what is before the string
|
|
int a = READ_LE_UINT16(myLine - 2);
|
|
char *b = myLine - 2 - 4 * a;
|
|
|
|
char *c = b - 2; // v6
|
|
int numParticipants = READ_LE_UINT16(c); // num dialogue participants
|
|
|
|
char *e = c - 2 - 4 * numParticipants;
|
|
READ_LE_UINT16(e);
|
|
|
|
// if one voice is still playing, wait !
|
|
if (blocking) {
|
|
while (_audioManager->voiceStillPlaying() && !_shouldQuit)
|
|
doFrame();
|
|
|
|
char *cc = c;
|
|
Character *waitChar;
|
|
for (int32 i = 0; i < numParticipants - 1; i++) {
|
|
// listener
|
|
int32 listenerId = READ_LE_UINT16(cc - 2);
|
|
cc -= 4;
|
|
waitChar = getCharacterById(listenerId);
|
|
if (waitChar) {
|
|
while ((waitChar->getAnimFlag() & 0x10) == 0x10 && !_shouldQuit)
|
|
doFrame();
|
|
}
|
|
|
|
}
|
|
int32 talkerId = READ_LE_UINT16(cc - 2);
|
|
|
|
waitChar = getCharacterById(talkerId);
|
|
if (waitChar && !_gameState->_inInventory) {
|
|
while ((waitChar->getAnimFlag() & 0x10) == 0x10 && !_shouldQuit)
|
|
doFrame();
|
|
}
|
|
} else {
|
|
if (_audioManager->voiceStillPlaying())
|
|
_audioManager->stopCurrentVoice();
|
|
}
|
|
|
|
for (int32 i = 0; i < numParticipants - 1; i++) {
|
|
// listener
|
|
int32 listenerId = READ_LE_UINT16(c - 2);
|
|
int32 listenerAnimId = READ_LE_UINT16(c - 4);
|
|
if (blocking) playTalkAnimOnCharacter(listenerAnimId, listenerId, false);
|
|
c -= 4;
|
|
}
|
|
|
|
int32 talkerId = READ_LE_UINT16(c - 2);
|
|
int32 talkerAnimId = READ_LE_UINT16(c - 4);
|
|
|
|
_currentTextLine = myLine;
|
|
_currentTextLineCharacterId = talkerId;
|
|
_currentTextLineId = dialogid;
|
|
|
|
if (blocking) {
|
|
Character *character = getCharacterById(talkerId);
|
|
if (character)
|
|
character->setTalking(true);
|
|
|
|
playTalkAnimOnCharacter(talkerAnimId, talkerId, true);
|
|
|
|
// set once more the values, they may have been overwritten when the engine
|
|
// waits for the character to be ready.
|
|
_currentTextLine = myLine;
|
|
_currentTextLineCharacterId = talkerId;
|
|
_currentTextLineId = dialogid;
|
|
} else {
|
|
Character *character = getCharacterById(talkerId);
|
|
if (character)
|
|
character->stopSpecialAnim();
|
|
}
|
|
|
|
debugC(0, 0xfff, "Talker = %d (num participants : %d) will say '%s'", (int)talkerId , (int)numParticipants, myLine);
|
|
|
|
getTextPosition(talkerId, &_currentTextLineX, &_currentTextLineY);
|
|
|
|
if (dialogid < 1000) {
|
|
myId = _roomTexts->getId(dialogid);
|
|
_audioManager->playVoice(myId, false);
|
|
} else {
|
|
myId = _genericTexts->getId(dialogid - 1000);
|
|
_audioManager->playVoice(myId, true);
|
|
}
|
|
|
|
if (blocking) {
|
|
while (_audioManager->voiceStillPlaying() && !_shouldQuit)
|
|
doFrame();
|
|
_gameState->_mouseHidden = oldMouseHidden && _gameState->_mouseHidden;
|
|
|
|
Character *character = getCharacterById(talkerId);
|
|
if (character)
|
|
character->setTalking(false);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
void ToonEngine::haveAConversation(int32 convId) {
|
|
setCursor(0);
|
|
|
|
_gameState->_inConversation = true;
|
|
_gameState->_showConversationIcons = false;
|
|
_gameState->_exitConversation = false;
|
|
_gameState->_sackVisible = false;
|
|
Conversation *conv = &state()->_conversationState[convId];
|
|
_gameState->_currentConversationId = convId;
|
|
|
|
// change the music to the "conversation" music if needed.
|
|
playRoomMusic();
|
|
|
|
if (conv->_enable) {
|
|
// fix dialog script based on new flags
|
|
for (int32 i = 0; i < 10; i++) {
|
|
if (conv->state[i]._data2 == 1 || conv->state[i]._data2 == 3) {
|
|
if (getConversationFlag(_gameState->_currentScene, conv->state[i]._data3))
|
|
conv->state[i]._data2 = 1;
|
|
else
|
|
conv->state[i]._data2 = 3;
|
|
}
|
|
}
|
|
|
|
// if current voice stream sub 15130
|
|
processConversationClick(conv , 2);
|
|
doFrame();
|
|
}
|
|
|
|
_mouseButton = 0;
|
|
_gameState->_firstConverstationLine = true;
|
|
|
|
while (!_gameState->_exitConversation && !_shouldQuit) {
|
|
_gameState->_mouseHidden = false;
|
|
_gameState->_showConversationIcons = true;
|
|
int32 oldMouseButton = _mouseButton;
|
|
while (!_shouldQuit) {
|
|
doFrame();
|
|
|
|
if (_mouseButton != 0) {
|
|
if (!oldMouseButton)
|
|
break;
|
|
} else {
|
|
oldMouseButton = 0;
|
|
}
|
|
}
|
|
int selected = -1;
|
|
int a = 0;
|
|
for (int i = 0; i < 10; i++) {
|
|
if (conv->state[i]._data2 == 1) {
|
|
if (_mouseX > 50 + a * 60 && _mouseX < 100 + a * 60 && _mouseY >= 336 && _mouseY <= 386) {
|
|
selected = i;
|
|
break;
|
|
}
|
|
a++;
|
|
}
|
|
}
|
|
|
|
if (_shouldQuit)
|
|
return;
|
|
|
|
_gameState->_showConversationIcons = false;
|
|
_gameState->_mouseHidden = 1;
|
|
|
|
if (selected < 0 || selected == 1 || selected == 3) {
|
|
if (_gameState->_firstConverstationLine)
|
|
processConversationClick(conv, 3);
|
|
else
|
|
processConversationClick(conv, 1);
|
|
break;
|
|
} else {
|
|
processConversationClick(conv, selected);
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < 10; i++) {
|
|
if (conv->state[i]._data2 == 2) {
|
|
if (i != 3)
|
|
conv->state[i]._data2 = 1;
|
|
}
|
|
}
|
|
|
|
_gameState->_exitConversation = false;
|
|
_gameState->_inConversation = false;
|
|
_gameState->_currentConversationId = -1;
|
|
_gameState->_mouseHidden = false;
|
|
_gameState->_sackVisible = true;
|
|
|
|
// switch back to original music
|
|
playRoomMusic();
|
|
}
|
|
|
|
void ToonEngine::drawConversationIcons() {
|
|
if (!_gameState->_inConversation || !_gameState->_showConversationIcons)
|
|
return;
|
|
int32 aa = 50 + _gameState->_currentScrollValue;
|
|
for (int32 i = 0; i < 10; i++) {
|
|
if (_gameState->_conversationState[_gameState->_currentConversationId].state[i]._data2 == 1) {
|
|
_dialogIcons->drawFrame(*_mainSurface, (i + _gameState->_currentScene) & 7, aa, 336);
|
|
_dialogIcons->drawFrame(*_mainSurface, 7 + _gameState->_conversationState[_gameState->_currentConversationId].state[i]._data3, aa, 339);
|
|
aa += 60;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ToonEngine::prepareConversations() {
|
|
Conversation *allConvs = _gameState->_conversationState;
|
|
for (int32 i = 0; i < 60; i++) {
|
|
|
|
allConvs[i].state[0]._data2 = 1;
|
|
if (!allConvs[i].state[0]._data3) {
|
|
allConvs[i].state[0]._data3 = 1;
|
|
}
|
|
allConvs[i].state[1]._data2 = 1;
|
|
allConvs[i].state[1]._data3 = 6;
|
|
allConvs[i].state[3]._data2 = 2;
|
|
|
|
}
|
|
int numConversations = READ_LE_UINT16(_conversationData + 1);
|
|
int16 *curConversation = _conversationData + 3;
|
|
for (int i = 0; i < numConversations; i++) {
|
|
Conversation *conv = &allConvs[ READ_LE_UINT16(curConversation)];
|
|
if (!conv->_enable) {
|
|
|
|
conv->_enable = 1;
|
|
|
|
int16 offset1 = READ_LE_UINT16(curConversation + 1);
|
|
void *convData1 = (char *)_conversationData + offset1;
|
|
conv->state[0]._data4 = convData1;
|
|
|
|
int16 offset2 = READ_LE_UINT16(curConversation + 2);
|
|
void *convData2 = (char *)_conversationData + offset2;
|
|
conv->state[1]._data4 = convData2;
|
|
|
|
int16 offset3 = READ_LE_UINT16(curConversation + 3);
|
|
void *convData3 = (char *)_conversationData + offset3;
|
|
conv->state[2]._data4 = convData3;
|
|
|
|
int16 offset4 = READ_LE_UINT16(curConversation + 4);
|
|
void *convData4 = (char *)_conversationData + offset4;
|
|
conv->state[3]._data4 = convData4;
|
|
}
|
|
curConversation += 5;
|
|
}
|
|
}
|
|
|
|
void ToonEngine::processConversationClick(Conversation *conv, int32 status) {
|
|
Conversation::ConvState *v2 = (Conversation::ConvState *)&conv->state[status];
|
|
|
|
int16 *i = (int16 *)((char *)v2->_data4 + 2);
|
|
|
|
_gameState->_firstConverstationLine = false;
|
|
while (READ_LE_INT16(i) >= 0) {
|
|
if (READ_LE_INT16(i) < 100) {
|
|
if (_gameState->_exitConversation == false) {
|
|
characterTalk(READ_LE_INT16(i + 1));
|
|
}
|
|
} else {
|
|
runConversationCommand(&i);
|
|
}
|
|
i += 2;
|
|
}
|
|
|
|
int16 command = READ_LE_INT16(i);
|
|
int16 value = READ_LE_INT16(i + 1);
|
|
|
|
if (command == -1) {
|
|
v2->_data2 = 0;
|
|
} else if (command == -2) {
|
|
v2->_data4 = (char *)_conversationData + value;
|
|
v2->_data3 = READ_LE_INT16(v2->_data4);
|
|
} else if (command == -3) {
|
|
v2->_data2 = 2;
|
|
v2->_data4 = (char *)_conversationData + value;
|
|
v2->_data3 = READ_LE_INT16(v2->_data4);
|
|
}
|
|
|
|
int16 *v7 = i + 2;
|
|
int16 v8 = READ_LE_INT16(v7);
|
|
if (v8 == -1) {
|
|
_gameState->_mouseHidden = false;
|
|
} else {
|
|
while (v8 != -1) {
|
|
v7 += 1;
|
|
int16 *v14 = (int16 *)((char *)_conversationData + v8);
|
|
|
|
// find free dialogue slot
|
|
for (int j = 0; j < 10; j++) {
|
|
if (!conv->state[j]._data2) {
|
|
conv->state[j]._data3 = READ_LE_INT16(v14);
|
|
conv->state[j]._data4 = v14;
|
|
if (getConversationFlag(_gameState->_currentScene, conv->state[j]._data3))
|
|
conv->state[j]._data2 = 1;
|
|
else
|
|
conv->state[j]._data2 = 3;
|
|
|
|
v8 = READ_LE_INT16(v7);
|
|
if (v8 == -1)
|
|
return;
|
|
else
|
|
break; // restarts while loop;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// hardcoded conversation flag to know if one dialogue icon must be displayed or not
|
|
// based on game events...
|
|
int32 ToonEngine::getConversationFlag(int32 locationId, int32 param) {
|
|
if (locationId == 1) {
|
|
if (param == 0x34)
|
|
return _gameState->getGameFlag(93);
|
|
|
|
if (param != 55)
|
|
return 1;
|
|
|
|
if (!_gameState->getGameFlag(262))
|
|
return 1;
|
|
|
|
return 0;
|
|
} else if (locationId == 2) {
|
|
if (param == 36 && _gameState->getGameFlag(149))
|
|
return 0;
|
|
return 1;
|
|
} else if (locationId == 7) {
|
|
if (param == 30)
|
|
return _gameState->getGameFlag(132);
|
|
else
|
|
return 1;
|
|
} else if (locationId == 8) {
|
|
if (param == 0x20) {
|
|
if (!_gameState->getGameFlag(73) && !_gameState->getGameFlag(151) && !_gameState->getGameFlag(152) && !_gameState->getGameFlag(153))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
if (param == 33) {
|
|
if (!_gameState->getGameFlag(73) && !_gameState->getGameFlag(151) && !_gameState->getGameFlag(152) && !_gameState->getGameFlag(153))
|
|
return 0;
|
|
return 1;
|
|
}
|
|
} else if (locationId == 0xb) {
|
|
if (param == 0x12) {
|
|
if (!_gameState->hasItemInInventory(71))
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
if (param == 74) {
|
|
if (_gameState->hasItemInInventory(71))
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
return 1;
|
|
} else if (locationId == 0xc) {
|
|
if (param == 0x3d && _gameState->getGameFlag(154)) {
|
|
return 0;
|
|
}
|
|
if (param == 76 && !_gameState->getGameFlag(79)) {
|
|
return 0;
|
|
}
|
|
if (param == 0x4e && !_gameState->hasItemInInventory(32)) {
|
|
return 0;
|
|
}
|
|
if (param == 0x4f && !_gameState->hasItemInInventory(92)) {
|
|
return 0;
|
|
}
|
|
if (param == 80 && !_gameState->hasItemInInventory(91)) {
|
|
return 0;
|
|
}
|
|
if (param == 0x4d && _gameState->getGameFlag(79)) {
|
|
return 0;
|
|
}
|
|
} else if (locationId == 0xd) {
|
|
if (param == 0x2f && _gameState->getGameFlag(81)) {
|
|
return 0;
|
|
}
|
|
if (param == 48 && _gameState->getGameFlag(81)) {
|
|
return 0;
|
|
}
|
|
} else if (locationId == 0x10) {
|
|
switch (param) {
|
|
case 0x3e8:
|
|
if (!(_gameState->_gameGlobalData[30] & 1))
|
|
return 0;
|
|
break;
|
|
case 0x3e9:
|
|
if (!(_gameState->_gameGlobalData[30] & 2))
|
|
return 0;
|
|
break;
|
|
case 0x3ea:
|
|
if (!(_gameState->_gameGlobalData[30] & 4))
|
|
return 0;
|
|
break;
|
|
case 0x3eb:
|
|
if (!(_gameState->_gameGlobalData[30] & 8))
|
|
return 0;
|
|
break;
|
|
case 0x3ec:
|
|
if (!(_gameState->_gameGlobalData[30] & 16))
|
|
return 0;
|
|
break;
|
|
case 0x3ed:
|
|
if (!(_gameState->_gameGlobalData[30] & 32))
|
|
return 0;
|
|
break;
|
|
case 0x3ee:
|
|
if (!(_gameState->_gameGlobalData[30] & 64))
|
|
return 0;
|
|
break;
|
|
default:
|
|
break;
|
|
};
|
|
return 1;
|
|
} else if (locationId == 0x12) {
|
|
if (param == 0x28 && _gameState->getGameFlag(91)) {
|
|
return 0;
|
|
}
|
|
if (param == 41 && (!_gameState->getGameFlag(96) || _gameState->getGameFlag(91))) {
|
|
return 0;
|
|
}
|
|
} else if (locationId == 0x13) {
|
|
if (param == 0x32 && _gameState->getGameFlag(107)) {
|
|
return 0;
|
|
}
|
|
if (param == 68 && !_gameState->getGameFlag(107)) {
|
|
return 0;
|
|
}
|
|
} else if (locationId == 0x14) {
|
|
if (param == 1000 && !_gameState->getGameFlag(82)) {
|
|
return 0;
|
|
}
|
|
} else if (locationId == 0x25) {
|
|
if (param == 7 && _gameState->_gameGlobalData[28] != 1) {
|
|
return 0;
|
|
}
|
|
if (param == 8 && _gameState->_gameGlobalData[28] != 1) {
|
|
return 0;
|
|
}
|
|
if (param == 9 && _gameState->_gameGlobalData[28] != 1) {
|
|
return 0;
|
|
}
|
|
if (param == 75 && _gameState->_gameGlobalData[28] != 2) {
|
|
return 0;
|
|
}
|
|
} else if (locationId == 72) {
|
|
if (param == 63 && _gameState->getGameFlag(105)) {
|
|
return 0;
|
|
}
|
|
if (param == 67 && !_gameState->getGameFlag(105)) {
|
|
return 0;
|
|
}
|
|
if (param == 0x40 && !_gameState->getGameFlag(105)) {
|
|
return 0;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
int32 ToonEngine::runConversationCommand(int16 **command) {
|
|
|
|
int16 *v5 = *command;
|
|
|
|
int v2 = READ_LE_INT16(v5);
|
|
int v4 = READ_LE_INT16(v5 + 1);
|
|
int result = v2 - 100;
|
|
switch (v2) {
|
|
case 100:
|
|
result = runEventScript(_mouseX, _mouseY, 2, v4, 0);
|
|
break;
|
|
case 101:
|
|
_gameState->_exitConversation = true;
|
|
break;
|
|
case 102:
|
|
playSoundWrong();
|
|
break;
|
|
case 104:
|
|
*command = (int16 *)((char *)_conversationData + v4 - 4);
|
|
break;
|
|
//
|
|
case 105:
|
|
if (getConversationFlag(_gameState->_currentScene, v4)) {
|
|
result = READ_LE_INT16(*command + 2);
|
|
*command = (int16 *)((char *)_conversationData + result - 4);
|
|
} else {
|
|
int16 *newPtr = *command + 1;
|
|
*command = newPtr;
|
|
}
|
|
break;
|
|
case 103:
|
|
return result;
|
|
break;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
int32 ToonEngine::waitTicks(int32 numTicks, bool breakOnMouseClick) {
|
|
uint32 nextTime = _system->getMillis() + numTicks * _tickLength;
|
|
while (_system->getMillis() < nextTime || numTicks == -1) {
|
|
//if (!_animationSceneScriptRunFlag)
|
|
// break;
|
|
updateAnimationSceneScripts(0);
|
|
getMouseEvent();
|
|
simpleUpdate();
|
|
|
|
if (breakOnMouseClick && (_mouseButton & 0x2))
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void ToonEngine::renderInventory() {
|
|
if (!_gameState->_inInventory)
|
|
return;
|
|
|
|
if (!_dirtyAll) {
|
|
_inventoryPicture->drawWithRectList(*_mainSurface, 0, 0, 0, 0, _dirtyRects);
|
|
} else {
|
|
_inventoryPicture->draw(*_mainSurface, 0, 0, 0, 0);
|
|
_dirtyRects.push_back(Common::Rect(0, 0, TOON_SCREEN_WIDTH, TOON_SCREEN_HEIGHT));
|
|
}
|
|
clearDirtyRects();
|
|
|
|
// draw items on screen
|
|
for (int32 i = 0; i < _gameState->_numInventoryItems; i++) {
|
|
int32 x = 57 * (i % 7) + 114;
|
|
int32 y = ((9 * (i % 7)) & 0xf) + 56 * (i / 7) + 80;
|
|
_inventoryIconSlots->drawFrame(*_mainSurface, i % 12, x + _gameState->_currentScrollValue, y);
|
|
if (_gameState->_inventory[i])
|
|
_inventoryIcons->drawFrame(*_mainSurface, _gameState->_inventory[i], x + _gameState->_currentScrollValue + 2, y + 2);
|
|
}
|
|
|
|
drawConversationLine();
|
|
if (!_audioManager->voiceStillPlaying()) {
|
|
_currentTextLineCharacterId = -1;
|
|
_currentTextLine = 0;
|
|
_currentTextLineId = -1;
|
|
}
|
|
|
|
if (_firstFrame) {
|
|
copyToVirtualScreen(false);
|
|
_firstFrame = false;
|
|
fadeIn(5);
|
|
}
|
|
copyToVirtualScreen();
|
|
}
|
|
|
|
int32 ToonEngine::showInventory() {
|
|
int32 oldScrollValue = _gameState->_currentScrollValue;
|
|
|
|
delete _inventoryPicture;
|
|
_inventoryPicture = new Picture(this);
|
|
fadeOut(5);
|
|
_inventoryPicture->loadPicture("SACK128.CPS");
|
|
_inventoryPicture->setupPalette();
|
|
dirtyAllScreen();
|
|
|
|
if (_gameState->_mouseState >= 0) {
|
|
setCursor(_gameState->_mouseState, true, -18, -14);
|
|
|
|
// make sure we have a free spot
|
|
if (!_gameState->hasItemInInventory(0)) {
|
|
_gameState->_inventory[_gameState->_numInventoryItems] = 0;
|
|
_gameState->_numInventoryItems++;
|
|
}
|
|
} else {
|
|
setCursor(0);
|
|
}
|
|
|
|
_gameState->_inInventory = true;
|
|
_gameState->_currentScrollValue = 0;
|
|
|
|
int32 oldMouseButton = 0x3;
|
|
int32 justPressedButton = 0;
|
|
_firstFrame = true;
|
|
|
|
while (!_shouldQuit) {
|
|
getMouseEvent();
|
|
|
|
justPressedButton = _mouseButton & ~oldMouseButton;
|
|
oldMouseButton = _mouseButton;
|
|
|
|
if (justPressedButton & 0x3) {
|
|
// find out what object we're on
|
|
int32 foundObj = -1;
|
|
for (int32 i = 0; i < _gameState->_numInventoryItems; i++) {
|
|
int32 x = 57 * (i % 7) + 114;
|
|
int32 y = ((9 * (i % 7)) & 0xf) + 56 * (i / 7) + 80;
|
|
if (_mouseX >= (_gameState->_currentScrollValue + x - 6) &&
|
|
_mouseX <= (_gameState->_currentScrollValue + x + 44 + 7) &&
|
|
_mouseY >= y - 6 && _mouseY <= y + 50) {
|
|
foundObj = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (justPressedButton & 0x1) {
|
|
if (_gameState->_mouseState < 0) {
|
|
if (foundObj >= 0) {
|
|
// take an object
|
|
int32 item = _gameState->_inventory[foundObj];
|
|
|
|
int32 modItem = getSpecialInventoryItem(item);
|
|
if (modItem) {
|
|
if (modItem == -1) {
|
|
_gameState->_mouseState = item;
|
|
_gameState->_inventory[foundObj] = 0;
|
|
} else {
|
|
_gameState->_mouseState = modItem;
|
|
if (!_gameState->hasItemInInventory(0)) {
|
|
_gameState->_inventory[_gameState->_numInventoryItems] = 0;
|
|
_gameState->_numInventoryItems++;
|
|
}
|
|
}
|
|
|
|
setCursor(_gameState->_mouseState, true, -18, -14);
|
|
}
|
|
|
|
} else {
|
|
break;
|
|
}
|
|
} else {
|
|
if (foundObj >= 0 && _gameState->_inventory[foundObj] == 0) { // empty place
|
|
_gameState->_inventory[foundObj] = _gameState->_mouseState;
|
|
setCursor(0, false);
|
|
_gameState->_mouseState = -1;
|
|
} else if (foundObj >= 0 && _gameState->_inventory[foundObj] > 0) {
|
|
if (!handleInventoryOnInventory(_gameState->_mouseState, _gameState->_inventory[foundObj]))
|
|
playSoundWrong();
|
|
} else {
|
|
// quit the inventory mode with the icon
|
|
break;
|
|
}
|
|
}
|
|
|
|
} else if (justPressedButton & 0x2) { // right button
|
|
if (foundObj >= 0) {
|
|
// talk about the object
|
|
if (!handleInventoryOnInventory(_gameState->_inventory[foundObj], -1))
|
|
characterTalk(1000 + _gameState->_inventory[foundObj]);
|
|
} else {
|
|
// go out
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
renderInventory();
|
|
_system->delayMillis(10);
|
|
}
|
|
|
|
_gameState->_currentScrollValue = oldScrollValue;
|
|
_gameState->_inInventory = false;
|
|
_mouseButton = 0;
|
|
_lastMouseButton = 0x3;
|
|
|
|
fadeOut(5);
|
|
if (_gameState->_inCloseUp) {
|
|
_gameState->_inCloseUp = false;
|
|
flipScreens();
|
|
} else if (_gameState->_inCutaway) {
|
|
_currentCutaway->setupPalette();
|
|
setupGeneralPalette();
|
|
} else {
|
|
_currentPicture->setupPalette();
|
|
setupGeneralPalette();
|
|
}
|
|
flushPalette();
|
|
dirtyAllScreen();
|
|
_firstFrame = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void ToonEngine::getMouseEvent() {
|
|
Common::EventManager *_event = _system->getEventManager();
|
|
|
|
Common::Event event;
|
|
while (_event->pollEvent(event) && !_shouldQuit)
|
|
;
|
|
|
|
_mouseX = _event->getMousePos().x;
|
|
_mouseY = _event->getMousePos().y;
|
|
_mouseButton = _event->getButtonState();
|
|
}
|
|
|
|
void ToonEngine::drawSack() {
|
|
if (_gameState->_sackVisible) {
|
|
_inventoryIcons->drawFrame(*_mainSurface, 0, _gameState->_currentScrollValue, 356);
|
|
}
|
|
}
|
|
|
|
void ToonEngine::addItemToInventory(int32 item) {
|
|
|
|
if (item == 103 || item == 104 || item == 89 || item == 82) {
|
|
// can't add that to inventory
|
|
_gameState->_mouseState = -1;
|
|
return;
|
|
}
|
|
|
|
if (item == 41) {
|
|
// confiscated inventory
|
|
for (int32 i = 0; i < _gameState->_numConfiscatedInventoryItems; i++)
|
|
addItemToInventory(_gameState->_confiscatedInventory[i]);
|
|
|
|
_gameState->_numConfiscatedInventoryItems = 0;
|
|
_gameState->_mouseState = -1;
|
|
return;
|
|
}
|
|
|
|
for (int32 i = 0; i < _gameState->_numInventoryItems; i++) {
|
|
if (_gameState->_inventory[i] == 0) {
|
|
_gameState->_inventory[i] = item;
|
|
_gameState->_mouseState = -1;
|
|
return;
|
|
}
|
|
}
|
|
_gameState->_inventory[_gameState->_numInventoryItems] = item;
|
|
_gameState->_numInventoryItems++;
|
|
_gameState->_mouseState = -1;
|
|
}
|
|
|
|
void ToonEngine::createMouseItem(int32 item) {
|
|
_gameState->_mouseState = item;
|
|
setCursor(_gameState->_mouseState, true, -18, -14);
|
|
}
|
|
|
|
void ToonEngine::deleteMouseItem() {
|
|
_gameState->_mouseState = -1;
|
|
rearrangeInventory();
|
|
setCursor(0);
|
|
}
|
|
|
|
void ToonEngine::showCutaway(const Common::String &cutawayPicture) {
|
|
_gameState->_inCutaway = true;
|
|
_currentCutaway = new Picture(this);
|
|
if (cutawayPicture.empty()) {
|
|
Common::String name = _gameState->_locations[_gameState->_currentScene]._cutaway;
|
|
_currentCutaway->loadPicture(name + ".CPS");
|
|
} else {
|
|
_currentCutaway->loadPicture(cutawayPicture);
|
|
}
|
|
_currentCutaway->setupPalette();
|
|
_oldScrollValue = _gameState->_currentScrollValue;
|
|
_gameState->_currentScrollValue = 0;
|
|
dirtyAllScreen();
|
|
flushPalette();
|
|
}
|
|
|
|
void ToonEngine::hideCutaway() {
|
|
_gameState->_inCutaway = false;
|
|
_gameState->_sackVisible = true;
|
|
delete _currentCutaway;
|
|
_gameState->_currentScrollValue = _oldScrollValue;
|
|
_currentCutaway = 0;
|
|
_currentPicture->setupPalette();
|
|
dirtyAllScreen();
|
|
flushPalette();
|
|
}
|
|
|
|
void ToonEngine::updateCharacters(int32 timeElapsed) {
|
|
for (int32 i = 0; i < 8; i++) {
|
|
if (_characters[i]) {
|
|
_characters[i]->update(timeElapsed);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ToonEngine::drawPalette() {
|
|
for (int32 i = 0; i < 256; i++) {
|
|
int32 x = i % 32;
|
|
int32 y = i / 32;
|
|
_mainSurface->fillRect(Common::Rect(x * 16, y * 16, x * 16 + 16, y * 16 + 16), i);
|
|
}
|
|
}
|
|
|
|
void ToonEngine::rearrangeInventory() {
|
|
for (int32 i = 0; i < _gameState->_numInventoryItems; i++) {
|
|
if (_gameState->_inventory[i] == 0) {
|
|
// move all the following items from one
|
|
for (int32 j = i + 1; j < _gameState->_numInventoryItems; j++) {
|
|
_gameState->_inventory[j - 1] = _gameState->_inventory[j];
|
|
}
|
|
_gameState->_numInventoryItems--;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ToonEngine::newGame() {
|
|
|
|
if (_isDemo) {
|
|
addItemToInventory(59);
|
|
addItemToInventory(67);
|
|
addItemToInventory(11);
|
|
addItemToInventory(19);
|
|
loadScene(22);
|
|
//loadScene(_gameState->_currentScene);
|
|
} else {
|
|
//loadScene(4);
|
|
loadScene(_gameState->_currentScene);
|
|
}
|
|
}
|
|
|
|
void ToonEngine::playSFX(int32 id, int32 volume) {
|
|
if (id < 0)
|
|
_audioManager->playSFX(-id + 1, volume, true);
|
|
else
|
|
_audioManager->playSFX(id , volume, false);
|
|
}
|
|
|
|
void ToonEngine::playSoundWrong() {
|
|
_audioManager->playSFX(randRange(0,7), 128, true);
|
|
}
|
|
|
|
void ToonEngine::getTextPosition(int32 characterId, int32 *retX, int32 *retY) {
|
|
if (characterId < 0)
|
|
characterId = 0;
|
|
|
|
// default position is the center of current screen
|
|
*retX = _gameState->_currentScrollValue + 320;
|
|
*retY = 70;
|
|
|
|
// hardcoded special cases...
|
|
if (characterId == 0) {
|
|
// drew
|
|
int32 x = _drew->getX();
|
|
int32 y = _drew->getY();
|
|
if (x >= _gameState->_currentScrollValue && x <= _gameState->_currentScrollValue + TOON_SCREEN_WIDTH) {
|
|
if (!_gameState->_inCutaway && !_gameState->_inInventory) {
|
|
*retX = x;
|
|
*retY = y - ((_drew->getScale() * 256 / 1024) >> 1) - 45;
|
|
}
|
|
}
|
|
} else if (characterId == 1) {
|
|
// flux
|
|
int16 x = _flux->getX();
|
|
int16 y = _flux->getY();
|
|
if (x >= _gameState->_currentScrollValue && x <= _gameState->_currentScrollValue + TOON_SCREEN_WIDTH) {
|
|
if (!_gameState->_inCutaway) {
|
|
*retX = x;
|
|
*retY = y - ((_drew->getScale() * 100 / 1024) >> 1) - 30;
|
|
}
|
|
}
|
|
} else if (characterId == 5 || characterId == 39) {
|
|
*retX = 80;
|
|
*retY = 120;
|
|
} else if (characterId == 14) {
|
|
*retX = 257;
|
|
*retY = 132;
|
|
} else if (characterId == 18) {
|
|
*retX = 80;
|
|
*retY = 180;
|
|
} else if (characterId == 21) {
|
|
*retX = 363;
|
|
*retY = 193;
|
|
} else if (characterId == 23) {
|
|
*retX = 532;
|
|
*retY = 178;
|
|
} else if (characterId == 33) {
|
|
*retX = 167;
|
|
*retY = 172;
|
|
} else {
|
|
|
|
// more "standard" code by character
|
|
Character *character = getCharacterById(characterId);
|
|
if (character && !_gameState->_inCutaway) {
|
|
if (character->getAnimationInstance()) {
|
|
if (character->getX() >= _gameState->_currentScrollValue && character->getX() <= _gameState->_currentScrollValue + TOON_SCREEN_WIDTH) {
|
|
int16 x1= 0, y1 = 0, x2 = 0, y2 = 0;
|
|
character->getAnimationInstance()->getRect(&x1, &y1, &x2, &y2);
|
|
*retX = (x1 + x2) / 2;
|
|
*retY = y1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Character *ToonEngine::getCharacterById(int32 charId) {
|
|
for (int32 i = 0; i < 8; i++) {
|
|
if (_characters[i] && _characters[i]->getId() == charId)
|
|
return _characters[i];
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void ToonEngine::drawConversationLine() {
|
|
if (_currentTextLine && _showConversationText) {
|
|
_fontRenderer->setFontColorByCharacter(_currentTextLineCharacterId);
|
|
_fontRenderer->setFont(_fontToon);
|
|
_fontRenderer->renderMultiLineText(_currentTextLineX, _currentTextLineY, _currentTextLine, 0);
|
|
}
|
|
}
|
|
|
|
void ToonEngine::pauseEngineIntern(bool pause) {
|
|
|
|
Engine::pauseEngineIntern(pause);
|
|
|
|
static int32 pauseStart = 0;
|
|
if (pause) {
|
|
pauseStart = _system->getMillis();
|
|
|
|
} else {
|
|
_oldTimer = _system->getMillis();
|
|
_oldTimer2 = _oldTimer;
|
|
|
|
int32 diff = _oldTimer - pauseStart;
|
|
|
|
// we have to add the difference between the start and the current time
|
|
// to all "timer based" values.
|
|
for (int32 i = 0; i < _gameState->_locations[_gameState->_currentScene]._numSceneAnimations; i++) {
|
|
_sceneAnimationScripts[i]._lastTimer += diff;
|
|
}
|
|
for (int32 i = 0; i < 8; i++) {
|
|
if (_characters[i]) {
|
|
_characters[i]->updateTimers(diff);
|
|
}
|
|
}
|
|
|
|
_gameState->_timerTimeout[0] += diff;
|
|
_gameState->_timerTimeout[1] += diff;
|
|
}
|
|
}
|
|
|
|
bool ToonEngine::canSaveGameStateCurrently() {
|
|
return !_gameState->_inMenu && !_gameState->_inInventory && !_gameState->_inConversation && !_gameState->_inCutaway && !_gameState->_mouseHidden && !_moviePlayer->isPlaying();
|
|
}
|
|
|
|
bool ToonEngine::canLoadGameStateCurrently() {
|
|
return !_gameState->_inMenu && !_gameState->_inInventory && !_gameState->_inConversation && !_gameState->_inCutaway && !_gameState->_mouseHidden && !_moviePlayer->isPlaying();
|
|
}
|
|
|
|
Common::String ToonEngine::getSavegameName(int nr) {
|
|
return _targetName + Common::String::format(".%03d", nr);
|
|
}
|
|
|
|
bool ToonEngine::saveGame(int32 slot, const Common::String &saveGameDesc) {
|
|
int16 savegameId;
|
|
Common::String savegameDescription;
|
|
|
|
if (slot == -1) {
|
|
GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser("Save game:", "Save", true);
|
|
savegameId = dialog->runModalWithCurrentTarget();
|
|
savegameDescription = dialog->getResultString();
|
|
delete dialog;
|
|
} else {
|
|
savegameId = slot;
|
|
if (!saveGameDesc.empty()) {
|
|
savegameDescription = saveGameDesc;
|
|
} else {
|
|
savegameDescription = Common::String::format("Quick save #%d", (int)slot);
|
|
}
|
|
}
|
|
|
|
if (savegameId < 0)
|
|
return false; // dialog aborted
|
|
|
|
Common::String savegameFile = getSavegameName(savegameId);
|
|
Common::OutSaveFile *saveFile = _saveFileMan->openForSaving(savegameFile);
|
|
if (!saveFile)
|
|
return false;
|
|
|
|
// save savegame header
|
|
saveFile->writeSint32BE(TOON_SAVEGAME_VERSION);
|
|
|
|
if (savegameDescription == "") {
|
|
savegameDescription = "Untitled savegame";
|
|
}
|
|
|
|
saveFile->writeSint16BE(savegameDescription.size() + 1);
|
|
saveFile->write(savegameDescription.c_str(), savegameDescription.size() + 1);
|
|
|
|
Graphics::saveThumbnail(*saveFile);
|
|
|
|
TimeDate curTime;
|
|
_system->getTimeAndDate(curTime);
|
|
|
|
uint32 saveDate = (curTime.tm_mday & 0xFF) << 24 | ((curTime.tm_mon + 1) & 0xFF) << 16 | ((curTime.tm_year + 1900) & 0xFFFF);
|
|
uint16 saveTime = (curTime.tm_hour & 0xFF) << 8 | ((curTime.tm_min) & 0xFF);
|
|
|
|
saveFile->writeUint32BE(saveDate);
|
|
saveFile->writeUint16BE(saveTime);
|
|
|
|
// save global state
|
|
_gameState->save(saveFile);
|
|
_gameState->saveConversations(saveFile);
|
|
_hotspots->save(saveFile);
|
|
|
|
// save current time to be able to patch the time when loading
|
|
saveFile->writeSint32BE(getOldMilli());
|
|
|
|
// save script states
|
|
for (int32 i = 0; i < 4; i++) {
|
|
_script->saveState(&_scriptState[i], saveFile);
|
|
}
|
|
|
|
// save animation script states
|
|
for (int32 i = 0; i < state()->_locations[_gameState->_currentScene]._numSceneAnimations; i++) {
|
|
saveFile->writeByte(_sceneAnimationScripts[i]._active);
|
|
saveFile->writeByte(_sceneAnimationScripts[i]._frozen);
|
|
saveFile->writeSint32BE(_sceneAnimationScripts[i]._lastTimer);
|
|
_script->saveState(&_sceneAnimationScripts[i]._state, saveFile);
|
|
}
|
|
|
|
// save scene animations
|
|
for (int32 i = 0; i < 64; i++) {
|
|
_sceneAnimations[i].save(this, saveFile);
|
|
}
|
|
|
|
for (int32 i = 0; i < 8; i++) {
|
|
if (_characters[i]) {
|
|
saveFile->writeSByte(i);
|
|
_characters[i]->save(saveFile);
|
|
}
|
|
}
|
|
saveFile->writeSByte(-1);
|
|
|
|
// save "command buffer"
|
|
saveFile->writeSint16BE(_saveBufferStream->pos());
|
|
if (_saveBufferStream->pos() > 0) {
|
|
saveFile->write(_saveBufferStream->getData(), _saveBufferStream->pos());
|
|
saveFile->writeSint16BE(0);
|
|
}
|
|
|
|
delete saveFile;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ToonEngine::loadGame(int32 slot) {
|
|
int16 savegameId;
|
|
|
|
if (slot == -1) {
|
|
GUI::SaveLoadChooser *dialog = new GUI::SaveLoadChooser("Restore game:", "Restore", false);
|
|
savegameId = dialog->runModalWithCurrentTarget();
|
|
delete dialog;
|
|
} else {
|
|
savegameId = slot;
|
|
}
|
|
if (savegameId < 0)
|
|
return false; // dialog aborted
|
|
|
|
Common::String savegameFile = getSavegameName(savegameId);
|
|
Common::InSaveFile *loadFile = _saveFileMan->openForLoading(savegameFile);
|
|
if (!loadFile)
|
|
return false;
|
|
|
|
int32 saveGameVersion = loadFile->readSint32BE();
|
|
if (saveGameVersion != TOON_SAVEGAME_VERSION) {
|
|
delete loadFile;
|
|
return false;
|
|
}
|
|
int32 saveGameNameSize = loadFile->readSint16BE();
|
|
loadFile->skip(saveGameNameSize);
|
|
|
|
// We don't need the thumbnail here, so just read it and discard it
|
|
Graphics::skipThumbnail(*loadFile);
|
|
|
|
loadFile->skip(6); // date & time skip
|
|
|
|
if (_gameState->_currentScene != -1) {
|
|
exitScene();
|
|
}
|
|
|
|
_gameState->load(loadFile);
|
|
loadScene(_gameState->_currentScene, true);
|
|
_gameState->loadConversations(loadFile);
|
|
_hotspots->load(loadFile);
|
|
|
|
// read the old time
|
|
int32 savedTime = loadFile->readSint32BE();
|
|
int32 timerDiff = _system->getMillis() - savedTime;
|
|
|
|
// load script states
|
|
for (int32 i = 0; i < 4; i++) {
|
|
_script->loadState(&_scriptState[i], loadFile);
|
|
}
|
|
|
|
// load animation script states
|
|
for (int32 i = 0; i < state()->_locations[_gameState->_currentScene]._numSceneAnimations; i++) {
|
|
_sceneAnimationScripts[i]._active = loadFile->readByte();
|
|
_sceneAnimationScripts[i]._frozen = loadFile->readByte();
|
|
_sceneAnimationScripts[i]._frozenForConversation = false;
|
|
int32 oldTimer = loadFile->readSint32BE();
|
|
_sceneAnimationScripts[i]._lastTimer = MAX<int32>(0, oldTimer + timerDiff);
|
|
_script->loadState(&_sceneAnimationScripts[i]._state, loadFile);
|
|
}
|
|
|
|
// load scene animations
|
|
for (int32 i = 0; i < 64; i++) {
|
|
_sceneAnimations[i].load(this, loadFile);
|
|
}
|
|
|
|
// scene animations have to be added in reverse order in animation manager to preserve the z order
|
|
for (int32 i = 63; i >= 0; i--) {
|
|
if (_sceneAnimations[i]._active && _sceneAnimations[i]._animInstance) {
|
|
_animationManager->addInstance(_sceneAnimations[i]._animInstance);
|
|
}
|
|
}
|
|
|
|
_gameState->_timerTimeout[0] += timerDiff;
|
|
_gameState->_timerTimeout[1] += timerDiff;
|
|
|
|
_gameState->_conversationData = _conversationData;
|
|
_firstFrame = true;
|
|
|
|
// read characters info
|
|
while (1) {
|
|
int8 c = loadFile->readSByte();
|
|
if (c < 0)
|
|
break;
|
|
|
|
if (!_characters[c]) {
|
|
_characters[c] = new Character(this);
|
|
}
|
|
_characters[c]->load(loadFile);
|
|
//_characters[c]->setVisible(true);
|
|
_characters[c]->update(0);
|
|
}
|
|
|
|
// load "command buffer"
|
|
int32 size = loadFile->readSint16BE();
|
|
if (size) {
|
|
uint8 *buf = new uint8[size + 2];
|
|
loadFile->read(buf, size + 2);
|
|
|
|
Common::MemoryReadStream rStr(buf, size + 2);
|
|
while (1) {
|
|
int16 command = rStr.readSint16BE();
|
|
if (!command) break;
|
|
switch (command) {
|
|
case 1: {
|
|
int16 frame = rStr.readSint16BE();
|
|
int16 animLen = rStr.readSint16BE();
|
|
char animName[32];
|
|
rStr.read(animName, animLen);
|
|
int16 x = rStr.readSint16BE();
|
|
int16 y = rStr.readSint16BE();
|
|
// int16 z = rStr.readSint16BE();
|
|
// int16 layerZ = rStr.readSint16BE();
|
|
rStr.readSint16BE();
|
|
rStr.readSint16BE();
|
|
|
|
Animation *anim = new Animation(this);
|
|
anim->loadAnimation(animName);
|
|
anim->drawFrameOnPicture(frame, x, y);
|
|
delete anim;
|
|
break;
|
|
}
|
|
case 2: {
|
|
int16 x = rStr.readSint16BE();
|
|
int16 y = rStr.readSint16BE();
|
|
int16 x1 = rStr.readSint16BE();
|
|
int16 y1 = rStr.readSint16BE();
|
|
makeLineNonWalkable(x, y, x1, y1);
|
|
break;
|
|
}
|
|
case 3: {
|
|
int16 x = rStr.readSint16BE();
|
|
int16 y = rStr.readSint16BE();
|
|
int16 x1 = rStr.readSint16BE();
|
|
int16 y1 = rStr.readSint16BE();
|
|
makeLineWalkable(x, y, x1, y1);
|
|
break;
|
|
}
|
|
case 4: {
|
|
int16 x = rStr.readSint16BE();
|
|
int16 y = rStr.readSint16BE();
|
|
getMask()->floodFillNotWalkableOnMask(x, y);
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
_saveBufferStream->write(buf, size);
|
|
delete[] buf;
|
|
}
|
|
delete loadFile;
|
|
|
|
// setup correct palette if we are in a closeup/cutaway or not.
|
|
if (_gameState->_inCloseUp) {
|
|
_gameState->_inCloseUp = false;
|
|
flipScreens();
|
|
} else if (_gameState->_inCutaway) {
|
|
_currentCutaway->setupPalette();
|
|
setupGeneralPalette();
|
|
} else {
|
|
_currentPicture->setupPalette();
|
|
setupGeneralPalette();
|
|
}
|
|
flushPalette();
|
|
|
|
return true;
|
|
}
|
|
|
|
// another special case for inventory
|
|
int32 ToonEngine::getSpecialInventoryItem(int32 item) {
|
|
// butter
|
|
if (item == 12) {
|
|
for (int32 i = 0; i < _gameState->_numInventoryItems; i++) {
|
|
if (_gameState->_inventory[i] == 12)
|
|
_gameState->_inventory[i] = 11;
|
|
}
|
|
return 11;
|
|
|
|
} else if (item == 84) {
|
|
if (_gameState->getGameFlag(26)) {
|
|
characterTalk(1726);
|
|
return 0;
|
|
} else {
|
|
if (!_gameState->hasItemInInventory(102) && !_gameState->hasItemInInventory(90) && !_gameState->hasItemInInventory(89)) {
|
|
characterTalk(1416);
|
|
return 102;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
void ToonEngine::initCharacter(int32 characterId, int32 animScriptId, int32 sceneAnimationId, int32 animToPlayId) {
|
|
// find a new index
|
|
int32 characterIndex = -1;
|
|
for (int32 i = 0; i < 8; i++) {
|
|
if (_characters[i] && _characters[i]->getId() == characterId) {
|
|
characterIndex = i;
|
|
break;
|
|
}
|
|
|
|
if (!_characters[i]) {
|
|
characterIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (characterIndex == -1) {
|
|
return;
|
|
}
|
|
|
|
_characters[characterIndex] = new Character(this);
|
|
_characters[characterIndex]->setId(characterId);
|
|
_characters[characterIndex]->setAnimScript(animScriptId);
|
|
_characters[characterIndex]->setDefaultSpecialAnimationId(animToPlayId);
|
|
_characters[characterIndex]->setSceneAnimationId(sceneAnimationId);
|
|
_characters[characterIndex]->setFlag(0);
|
|
_characters[characterIndex]->setVisible(true);
|
|
if (sceneAnimationId != -1)
|
|
_characters[characterIndex]->setAnimationInstance(_sceneAnimations[sceneAnimationId]._animInstance);
|
|
}
|
|
|
|
int32 ToonEngine::handleInventoryOnFlux(int32 itemId) {
|
|
|
|
switch (itemId) {
|
|
case 8:
|
|
sayLines(1, 1332);
|
|
break;
|
|
case 0x14:
|
|
case 0x15:
|
|
case 0x45:
|
|
sayLines(1, 1304);
|
|
break;
|
|
case 0x68:
|
|
_gameState->_mouseState = 0;
|
|
setCursor(0, false, 0, 0);
|
|
break;
|
|
case 116:
|
|
sayLines(1, 1306);
|
|
break;
|
|
default:
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void ToonEngine::storePalette() {
|
|
memcpy(_backupPalette, _finalPalette, 768);
|
|
}
|
|
|
|
void ToonEngine::restorePalette() {
|
|
memcpy(_finalPalette, _backupPalette, 768);
|
|
flushPalette();
|
|
}
|
|
|
|
const char *ToonEngine::getSpecialConversationMusic(int32 conversationId) {
|
|
static const char * const specialMusic[] = {
|
|
0, 0,
|
|
"BR091013", "BR091013",
|
|
"NET1214", "NET1214",
|
|
0, 0,
|
|
"CAR1365B", "CAR1365B",
|
|
0, 0,
|
|
0, 0,
|
|
"CAR14431", "CAR14431",
|
|
0, 0,
|
|
0, 0,
|
|
"SCD16520", "SCD16520",
|
|
"SCD16520", "SCD16520",
|
|
"SCD16522", "SCD16522",
|
|
0, 0,
|
|
"KPM8719", "KPM8719",
|
|
0, 0,
|
|
"CAR1368B", "CAR1368B",
|
|
0, 0,
|
|
0, 0,
|
|
"KPM6337", "KPM6337",
|
|
"CAR20471", "CAR20471",
|
|
"CAR136_1", "KPM87_57",
|
|
0, 0,
|
|
"CAR13648", "CAR13648",
|
|
0, 0,
|
|
0, 0,
|
|
0, 0,
|
|
0, 0,
|
|
"SCD16526", "SCD16526",
|
|
0, 0,
|
|
0, 0,
|
|
0, 0,
|
|
0, 0,
|
|
0, 0,
|
|
0, 0,
|
|
0, 0,
|
|
0, 0,
|
|
0, 0,
|
|
0, 0,
|
|
0, 0,
|
|
0, 0,
|
|
0, 0,
|
|
0, 0,
|
|
0, 0,
|
|
0, 0,
|
|
0, 0,
|
|
0, 0,
|
|
0, 0,
|
|
0, 0,
|
|
0, 0,
|
|
0, 0,
|
|
0, 0,
|
|
0, 0,
|
|
0, 0,
|
|
0, 0,
|
|
0, 0,
|
|
0, 0,
|
|
0, 0,
|
|
0, 0,
|
|
0, 0,
|
|
0, 0,
|
|
0, 0,
|
|
0, 0,
|
|
0, 0,
|
|
0, 0
|
|
};
|
|
|
|
return specialMusic[randRange(0, 1) + conversationId * 2];
|
|
}
|
|
|
|
void ToonEngine::viewInventoryItem(const Common::String &str, int32 lineId, int32 itemDest) {
|
|
storePalette();
|
|
fadeOut(5);
|
|
|
|
Picture *pic = new Picture(this);
|
|
pic->loadPicture(str);
|
|
pic->setupPalette();
|
|
dirtyAllScreen();
|
|
flushPalette();
|
|
|
|
if (lineId) {
|
|
characterTalk(lineId, false);
|
|
}
|
|
|
|
uint32 oldMouseButton = _mouseButton;
|
|
uint32 justPressedButton = 0;
|
|
_firstFrame = true;
|
|
|
|
int32 oldScrollValue = _gameState->_currentScrollValue;
|
|
_gameState->_currentScrollValue = 0;
|
|
|
|
while (!_shouldQuit) {
|
|
getMouseEvent();
|
|
|
|
justPressedButton = _mouseButton & ~oldMouseButton;
|
|
oldMouseButton = _mouseButton;
|
|
|
|
if (justPressedButton) {
|
|
break;
|
|
}
|
|
|
|
if (!_dirtyAll) {
|
|
pic->drawWithRectList(*_mainSurface, 0, 0, 0, 0, _dirtyRects);
|
|
} else {
|
|
pic->draw(*_mainSurface, 0, 0, 0, 0);
|
|
_dirtyRects.push_back(Common::Rect(0, 0, TOON_SCREEN_WIDTH, TOON_SCREEN_HEIGHT));
|
|
}
|
|
clearDirtyRects();
|
|
|
|
drawConversationLine();
|
|
if (!_audioManager->voiceStillPlaying()) {
|
|
_currentTextLineCharacterId = -1;
|
|
_currentTextLine = 0;
|
|
_currentTextLineId = -1;
|
|
}
|
|
|
|
if (_firstFrame) {
|
|
copyToVirtualScreen(false);
|
|
_firstFrame = false;
|
|
fadeIn(5);
|
|
}
|
|
|
|
copyToVirtualScreen();
|
|
}
|
|
|
|
fadeOut(5);
|
|
dirtyAllScreen();
|
|
restorePalette();
|
|
_firstFrame = true;
|
|
_gameState->_currentScrollValue = oldScrollValue;
|
|
delete pic;
|
|
}
|
|
|
|
int32 ToonEngine::handleInventoryOnInventory(int32 itemDest, int32 itemSrc) {
|
|
switch (itemDest) {
|
|
case 0:
|
|
return handleInventoryOnDrew(itemSrc);
|
|
case 1:
|
|
if (itemSrc == 71) {
|
|
sayLines(2, 1212);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 5:
|
|
if (itemSrc == 15) {
|
|
characterTalk(1492);
|
|
} else if (itemSrc == 0x2f) {
|
|
characterTalk(1488);
|
|
} else if (itemSrc == 88) {
|
|
sayLines(2, 1478);
|
|
} else {
|
|
return 0;
|
|
}
|
|
break;
|
|
case 6:
|
|
if (itemSrc == -1) {
|
|
viewInventoryItem("BLUEPRNT.CPS", 1006, itemDest);
|
|
return 1;
|
|
} else
|
|
return 0;
|
|
break;
|
|
case 8:
|
|
if (itemSrc == -1) {
|
|
viewInventoryItem("BOOK.CPS", 0, itemDest);
|
|
return 1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
break;
|
|
case 11:
|
|
if (itemSrc == 0xb) {
|
|
_gameState->_mouseState = -1;
|
|
replaceItemFromInventory(11, 12);
|
|
setCursor(0, false, 0, 0);
|
|
rearrangeInventory();
|
|
return 1;
|
|
//
|
|
} else if (itemSrc == 24) {
|
|
characterTalk(1244);
|
|
return 1;
|
|
} else if (itemSrc == 0x1a || itemSrc == 0x40 || itemSrc == 71) {
|
|
sayLines(2, 1212);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 12:
|
|
if (itemSrc == 24) {
|
|
characterTalk(1244);
|
|
return 1;
|
|
} else if (itemSrc == 0x1a || itemSrc == 0x40 || itemSrc == 71) {
|
|
sayLines(2, 1212);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 13:
|
|
if (itemSrc == 0x35 || itemSrc == 0x36) {
|
|
characterTalk(1204);
|
|
return 1;
|
|
} else if (itemSrc >= 0x6b && itemSrc <= 0x72) {
|
|
characterTalk(1312);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 14:
|
|
if (itemSrc == -1) {
|
|
deleteItemFromInventory(14);
|
|
addItemToInventory(15);
|
|
addItemToInventory(42);
|
|
rearrangeInventory();
|
|
return 1;
|
|
} else if (itemSrc == 43) {
|
|
characterTalk(1410);
|
|
return 1;
|
|
} else if (itemSrc == 49) {
|
|
characterTalk(1409);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 16:
|
|
if (itemSrc == 55) {
|
|
characterTalk(1400);
|
|
replaceItemFromInventory(55, 98);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 19:
|
|
if (itemSrc == 0x34) {
|
|
characterTalk(1322);
|
|
return 1;
|
|
} else if (itemSrc == 107) {
|
|
sayLines(2 , 1300);
|
|
replaceItemFromInventory(107, 111);
|
|
_gameState->_mouseState = -1;
|
|
setCursor(0, false, 0, 0);
|
|
rearrangeInventory();
|
|
return 1;
|
|
} else if (itemSrc == 0x6c) {
|
|
sayLines(2, 1300);
|
|
replaceItemFromInventory(108, 112);
|
|
_gameState->_mouseState = -1;
|
|
setCursor(0, false, 0, 0);
|
|
rearrangeInventory();
|
|
return 1;
|
|
} else if (itemSrc == 0x6d) {
|
|
sayLines(2, 1300);
|
|
replaceItemFromInventory(109, 113);
|
|
_gameState->_mouseState = -1;
|
|
setCursor(0, false, 0, 0);
|
|
rearrangeInventory();
|
|
return 1;
|
|
} else if (itemSrc == 110) {
|
|
sayLines(2, 1300);
|
|
replaceItemFromInventory(110, 114);
|
|
_gameState->_mouseState = -1;
|
|
setCursor(0, false, 0, 0);
|
|
rearrangeInventory();
|
|
return 1;
|
|
}
|
|
break;
|
|
case 20:
|
|
if (itemSrc == 35) {
|
|
createMouseItem(21);
|
|
replaceItemFromInventory(35, 36);
|
|
return 1;
|
|
} else if (itemSrc == 0x24) {
|
|
createMouseItem(21);
|
|
replaceItemFromInventory(36, 37);
|
|
return 1;
|
|
} else if (itemSrc == 37) {
|
|
deleteItemFromInventory(37);
|
|
createMouseItem(21);
|
|
rearrangeInventory();
|
|
return 1;
|
|
} else if (itemSrc == 0x6b || itemSrc == 0x6c || itemSrc == 0x6f || itemSrc == 108 || itemSrc == 112) {
|
|
sayLines(2, 1292);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 21:
|
|
switch (itemSrc) {
|
|
|
|
case 107:
|
|
characterTalk(1296);
|
|
replaceItemFromInventory(107, 109);
|
|
_gameState->_mouseState = -1;
|
|
setCursor(0, false, 0, 0);
|
|
rearrangeInventory();
|
|
return 1;
|
|
case 108:
|
|
characterTalk(1298);
|
|
replaceItemFromInventory(108, 110);
|
|
_gameState->_mouseState = -1;
|
|
setCursor(0, false, 0, 0);
|
|
rearrangeInventory();
|
|
return 1;
|
|
case 111:
|
|
characterTalk(1296);
|
|
replaceItemFromInventory(111, 113);
|
|
_gameState->_mouseState = -1;
|
|
setCursor(0, false, 0, 0);
|
|
rearrangeInventory();
|
|
return 1;
|
|
case 112:
|
|
characterTalk(1298);
|
|
replaceItemFromInventory(112, 114);
|
|
_gameState->_mouseState = -1;
|
|
setCursor(0, false, 0, 0);
|
|
rearrangeInventory();
|
|
return 1;
|
|
}
|
|
break;
|
|
case 22:
|
|
if (itemSrc == 32) {
|
|
characterTalk(1252);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 24:
|
|
if (itemSrc == 0xc) {
|
|
characterTalk(1244);
|
|
return 1;
|
|
} else if (itemSrc == 79) {
|
|
characterTalk(1280);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 26:
|
|
if (itemSrc == 0x5e) {
|
|
characterTalk(1316);
|
|
return 1;
|
|
} else if (itemSrc == 95) {
|
|
characterTalk(1320);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 31:
|
|
if (itemSrc == 61) {
|
|
characterTalk(1412);
|
|
deleteItemFromInventory(61);
|
|
createMouseItem(62);
|
|
rearrangeInventory();
|
|
return 1;
|
|
}
|
|
break;
|
|
case 32:
|
|
if (itemSrc == 22) {
|
|
characterTalk(1252);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 33:
|
|
if (itemSrc == 117) {
|
|
characterTalk(1490);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 34:
|
|
if (itemSrc == 61) {
|
|
characterTalk(1414);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 35:
|
|
if (itemSrc == -1) {
|
|
characterTalk(1035);
|
|
return 1;
|
|
} else if (itemSrc == 20) {
|
|
replaceItemFromInventory(20, 21);
|
|
createMouseItem(36);
|
|
return 1;
|
|
} else if (itemSrc == 68) {
|
|
replaceItemFromInventory(68, 69);
|
|
createMouseItem(36);
|
|
return 1;
|
|
} else if (itemSrc >= 107 && itemSrc <= 114) {
|
|
characterTalk(1314);
|
|
return 1;
|
|
} else {
|
|
characterTalk(1208);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 36:
|
|
if (itemSrc == -1) {
|
|
characterTalk(1035);
|
|
return 1;
|
|
} else if (itemSrc == 20) {
|
|
replaceItemFromInventory(20, 21);
|
|
createMouseItem(37);
|
|
return 1;
|
|
} else if (itemSrc == 68) {
|
|
replaceItemFromInventory(68, 69);
|
|
createMouseItem(37);
|
|
return 1;
|
|
} else if (itemSrc >= 107 && itemSrc <= 114) {
|
|
characterTalk(1314);
|
|
return 1;
|
|
} else {
|
|
characterTalk(1208);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 37:
|
|
if (itemSrc == -1) {
|
|
characterTalk(1035);
|
|
return 1;
|
|
} else if (itemSrc == 20) {
|
|
replaceItemFromInventory(20, 21);
|
|
_gameState->_mouseState = -1;
|
|
setCursor(0, false, 0, 0);
|
|
rearrangeInventory();
|
|
return 1;
|
|
} else if (itemSrc == 68) {
|
|
replaceItemFromInventory(68, 69);
|
|
_gameState->_mouseState = -1;
|
|
setCursor(0, false, 0, 0);
|
|
rearrangeInventory();
|
|
return 1;
|
|
} else if (itemSrc >= 107 && itemSrc <= 114) {
|
|
characterTalk(1314);
|
|
return 1;
|
|
} else {
|
|
characterTalk(1208);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 38:
|
|
if (itemSrc == 15) {
|
|
characterTalk(1492);
|
|
return 1;
|
|
} else if (itemSrc == 0x2f) {
|
|
characterTalk(1488);
|
|
return 1;
|
|
} else if (itemSrc == 88) {
|
|
sayLines(2, 1478);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 40:
|
|
if (itemSrc == 53) {
|
|
replaceItemFromInventory(53, 54);
|
|
characterTalk(1222);
|
|
return 1;
|
|
} else if (itemSrc == 0x36) {
|
|
characterTalk(1228);
|
|
return 1;
|
|
} else if (itemSrc == 0x5b) {
|
|
characterTalk(1230);
|
|
return 1;
|
|
} else if (itemSrc == 92) {
|
|
characterTalk(1220);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 43:
|
|
if (itemSrc == 14) {
|
|
characterTalk(1410);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 47:
|
|
if (itemSrc == -1)
|
|
characterTalk(1047);
|
|
else
|
|
characterTalk(1488);
|
|
|
|
return 1;
|
|
case 49:
|
|
if (itemSrc == 0xe) {
|
|
characterTalk(1409);
|
|
return 1;
|
|
} else if (itemSrc == 38 || itemSrc == 5 || itemSrc == 0x42) {
|
|
characterTalk(1476);
|
|
return 1;
|
|
} else if (itemSrc == 0x34) {
|
|
characterTalk(1260);
|
|
return 1;
|
|
} else if (itemSrc == 0x47) {
|
|
characterTalk(1246);
|
|
return 1;
|
|
} else if (itemSrc == 0x36) {
|
|
sayLines(2, 1324);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 52:
|
|
if (itemSrc == 0x13) {
|
|
characterTalk(1322);
|
|
return 1;
|
|
} else if (itemSrc == 94) {
|
|
characterTalk(1282);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 53:
|
|
if (itemSrc == 40) {
|
|
createMouseItem(54);
|
|
characterTalk(1222);
|
|
return 1;
|
|
} else if (itemSrc == 0x31) {
|
|
sayLines(2, 1324);
|
|
return 1;
|
|
} else if (itemSrc == 0x34) {
|
|
characterTalk(1310);
|
|
return 1;
|
|
} else if (itemSrc == 91) {
|
|
characterTalk(1218);
|
|
return 1;
|
|
}
|
|
|
|
break;
|
|
case 54:
|
|
if (itemSrc == 40) {
|
|
characterTalk(1228);
|
|
return 1;
|
|
} else if (itemSrc == 0x34) {
|
|
characterTalk(1310);
|
|
return 1;
|
|
} else if (itemSrc == 0x5b) {
|
|
characterTalk(1226);
|
|
replaceItemFromInventory(91, 92);
|
|
return 1;
|
|
} else if (itemSrc == 92) {
|
|
characterTalk(1220);
|
|
return 1;
|
|
}
|
|
|
|
break;
|
|
case 55:
|
|
if (itemSrc == 16) {
|
|
createMouseItem(98);
|
|
characterTalk(1400);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 61:
|
|
if (itemSrc == 0x1f) {
|
|
characterTalk(1412);
|
|
deleteItemFromInventory(31);
|
|
createMouseItem(62);
|
|
rearrangeInventory();
|
|
return 1;
|
|
} else if (itemSrc == 0x21 || itemSrc == 0x22) {
|
|
characterTalk(1414);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 64:
|
|
if (itemSrc == 0xb) {
|
|
sayLines(2, 1212);
|
|
return 1;
|
|
} else if (itemSrc == 0x5e || itemSrc == 0x5f) {
|
|
characterTalk(1318);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 66:
|
|
if (itemSrc == 15) {
|
|
characterTalk(1492);
|
|
return 1;
|
|
} else if (itemSrc == 0x2f) {
|
|
characterTalk(1488);
|
|
return 1;
|
|
} else if (itemSrc == 88) {
|
|
sayLines(2, 1478);
|
|
characterTalk(1478);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 67:
|
|
if (itemSrc == 79) {
|
|
sayLines(2, 1212);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 68:
|
|
if (itemSrc == 35) {
|
|
createMouseItem(69);
|
|
replaceItemFromInventory(35, 36);
|
|
return 1;
|
|
} else if (itemSrc == 0x24) {
|
|
createMouseItem(69);
|
|
replaceItemFromInventory(36, 37);
|
|
return 1;
|
|
} else if (itemSrc == 37) {
|
|
deleteItemFromInventory(37);
|
|
createMouseItem(69);
|
|
rearrangeInventory();
|
|
return 1;
|
|
} else if (itemSrc == 0x6b || itemSrc == 113 || itemSrc == 0x6f || itemSrc == 109) {
|
|
sayLines(2, 1288);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 69:
|
|
switch (itemSrc) {
|
|
case 107:
|
|
characterTalk(1296);
|
|
replaceItemFromInventory(107, 108);
|
|
_gameState->_mouseState = -1;
|
|
setCursor(0, false, 0, 0);
|
|
rearrangeInventory();
|
|
return 1;
|
|
case 109:
|
|
characterTalk(1298);
|
|
replaceItemFromInventory(109, 110);
|
|
_gameState->_mouseState = -1;
|
|
setCursor(0, false, 0, 0);
|
|
rearrangeInventory();
|
|
return 1;
|
|
case 111:
|
|
characterTalk(1296);
|
|
replaceItemFromInventory(111, 112);
|
|
_gameState->_mouseState = -1;
|
|
setCursor(0, false, 0, 0);
|
|
rearrangeInventory();
|
|
return 1;
|
|
case 113:
|
|
characterTalk(1298);
|
|
replaceItemFromInventory(113, 114);
|
|
_gameState->_mouseState = -1;
|
|
setCursor(0, false, 0, 0);
|
|
rearrangeInventory();
|
|
return 1;
|
|
}
|
|
break;
|
|
case 71:
|
|
if (itemSrc == 0xc || itemSrc == 1 || itemSrc == 0x41 || itemSrc == 67 || itemSrc == 0x4c || itemSrc == 57) {
|
|
sayLines(2, 1212);
|
|
return 1;
|
|
} else if (itemSrc == 79) {
|
|
characterTalk(1238);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 79:
|
|
if (itemSrc == 1 || itemSrc == 67 || itemSrc == 76 || itemSrc == 57 || itemSrc == 0x41) {
|
|
sayLines(2, 1212);
|
|
return 1;
|
|
} else if (itemSrc == 0x18) {
|
|
characterTalk(1280);
|
|
return 1;
|
|
} else if (itemSrc == 0x47) {
|
|
characterTalk(1238);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 82:
|
|
if (itemSrc == 84) {
|
|
sayLines(2, 1424);
|
|
return 1;
|
|
} else if (itemSrc == 0x58) {
|
|
deleteItemFromInventory(88);
|
|
createMouseItem(89);
|
|
rearrangeInventory();
|
|
characterTalk(1428);
|
|
return 1;
|
|
} else if (itemSrc == 117) {
|
|
sayLines(2, 1496);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 84:
|
|
if (itemSrc == 0x58) {
|
|
replaceItemFromInventory(88, 90);
|
|
characterTalk(1090);
|
|
return 1;
|
|
} else if (itemSrc == 117) {
|
|
characterTalk(1494);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 88:
|
|
if (itemSrc == 82) {
|
|
deleteItemFromInventory(82);
|
|
createMouseItem(89);
|
|
rearrangeInventory();
|
|
characterTalk(1428);
|
|
return 1;
|
|
} else if (itemSrc == 0x54) {
|
|
createMouseItem(90);
|
|
characterTalk(1090);
|
|
return 1;
|
|
} else if (itemSrc == 102) {
|
|
deleteItemFromInventory(102);
|
|
createMouseItem(90);
|
|
rearrangeInventory();
|
|
characterTalk(1090);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 89:
|
|
if (itemSrc == 117) {
|
|
sayLines(2, 1496);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 90:
|
|
if (itemSrc == 117) {
|
|
sayLines(2, 1494);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 91:
|
|
if (itemSrc == 0x28) {
|
|
characterTalk(1230);
|
|
return 1;
|
|
} else if (itemSrc == 54) {
|
|
createMouseItem(92);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 92:
|
|
if (itemSrc == 0x28 || itemSrc == 54) {
|
|
characterTalk(1220);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 94:
|
|
if (itemSrc == 26) {
|
|
characterTalk(1316);
|
|
return 1;
|
|
} else if (itemSrc == 0x34) {
|
|
characterTalk(1282);
|
|
return 1;
|
|
} else if (itemSrc == 64) {
|
|
characterTalk(1318);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 95:
|
|
if (itemSrc == 26) {
|
|
characterTalk(1320);
|
|
return 1;
|
|
} else if (itemSrc == 0x40) {
|
|
characterTalk(1318);
|
|
return 1;
|
|
} else if (itemSrc == 115) {
|
|
characterTalk(1284);
|
|
replaceItemFromInventory(115, 116);
|
|
createMouseItem(93);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 96:
|
|
if (itemSrc == 0x34) {
|
|
characterTalk(1234);
|
|
return 1;
|
|
} else if (itemSrc == 71) {
|
|
sayLines(2, 1212);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 97:
|
|
if (itemSrc == 15) {
|
|
characterTalk(1492);
|
|
return 1;
|
|
} else if (itemSrc == 0x2f) {
|
|
characterTalk(1488);
|
|
return 1;
|
|
} else if (itemSrc == 88) {
|
|
sayLines(2, 1478);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 100:
|
|
if (itemSrc == 117) {
|
|
characterTalk(1490);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 102:
|
|
if (itemSrc == -1) {
|
|
characterTalk(1102);
|
|
return 1;
|
|
} else if (itemSrc == 84) {
|
|
_gameState->_mouseState = -1;
|
|
setCursor(0, false, 0, 0);
|
|
rearrangeInventory();
|
|
characterTalk(1418);
|
|
return 1;
|
|
} else if (itemSrc == 88) {
|
|
deleteItemFromInventory(88);
|
|
createMouseItem(90);
|
|
rearrangeInventory();
|
|
characterTalk(1090);
|
|
return 1;
|
|
} else if (itemSrc == 117) {
|
|
characterTalk(1494);
|
|
return 1;
|
|
} else {
|
|
characterTalk(1426);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 106:
|
|
if (itemSrc == 13) {
|
|
characterTalk(1308);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 107:
|
|
if (itemSrc == 19) {
|
|
sayLines(2, 1300);
|
|
deleteItemFromInventory(19);
|
|
createMouseItem(111);
|
|
rearrangeInventory();
|
|
return 1;
|
|
} else if (itemSrc == 0x15) {
|
|
characterTalk(1296);
|
|
deleteItemFromInventory(21);
|
|
createMouseItem(109);
|
|
rearrangeInventory();
|
|
return 1;
|
|
} else if (itemSrc == 0x23) {
|
|
characterTalk(1314);
|
|
return 1;
|
|
} else if (itemSrc == 69) {
|
|
characterTalk(1296);
|
|
deleteItemFromInventory(69);
|
|
createMouseItem(108);
|
|
rearrangeInventory();
|
|
return 1;
|
|
}
|
|
break;
|
|
case 108:
|
|
if (itemSrc == 19) {
|
|
sayLines(2, 1300);
|
|
deleteItemFromInventory(19);
|
|
createMouseItem(112);
|
|
rearrangeInventory();
|
|
return 1;
|
|
} else if (itemSrc == 0x15) {
|
|
characterTalk(1298);
|
|
deleteItemFromInventory(21);
|
|
createMouseItem(110);
|
|
rearrangeInventory();
|
|
return 1;
|
|
} else if (itemSrc == 35) {
|
|
characterTalk(1314);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 109:
|
|
if (itemSrc == 19) {
|
|
sayLines(2, 1300);
|
|
deleteItemFromInventory(19);
|
|
createMouseItem(113);
|
|
rearrangeInventory();
|
|
return 1;
|
|
} else if (itemSrc == 0x23) {
|
|
characterTalk(1314);
|
|
return 1;
|
|
} else if (itemSrc == 69) {
|
|
characterTalk(1298);
|
|
deleteItemFromInventory(69);
|
|
createMouseItem(110);
|
|
rearrangeInventory();
|
|
return 1;
|
|
}
|
|
break;
|
|
case 110:
|
|
if (itemSrc == 0x13) {
|
|
sayLines(2, 1300);
|
|
deleteItemFromInventory(19);
|
|
createMouseItem(114);
|
|
rearrangeInventory();
|
|
return 1;
|
|
} else if (itemSrc == 35) {
|
|
characterTalk(1314);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 111:
|
|
if (itemSrc == 21) {
|
|
characterTalk(1296);
|
|
deleteItemFromInventory(21);
|
|
createMouseItem(113);
|
|
rearrangeInventory();
|
|
return 1;
|
|
} else if (itemSrc == 0x23) {
|
|
characterTalk(1314);
|
|
return 1;
|
|
} else if (itemSrc == 69) {
|
|
characterTalk(1296);
|
|
deleteItemFromInventory(69);
|
|
createMouseItem(112);
|
|
rearrangeInventory();
|
|
return 1;
|
|
}
|
|
break;
|
|
case 112:
|
|
if (itemSrc == 0x15) {
|
|
characterTalk(1298);
|
|
deleteItemFromInventory(21);
|
|
createMouseItem(114);
|
|
rearrangeInventory();
|
|
return 1;
|
|
} else if (itemSrc == 35) {
|
|
characterTalk(1314);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 113:
|
|
if (itemSrc == 0x23) {
|
|
characterTalk(1314);
|
|
return 1;
|
|
} else if (itemSrc == 69) {
|
|
characterTalk(1298);
|
|
deleteItemFromInventory(69);
|
|
createMouseItem(114);
|
|
rearrangeInventory();
|
|
return 1;
|
|
}
|
|
break;
|
|
case 114:
|
|
if (itemSrc == 35) {
|
|
characterTalk(1314);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 115:
|
|
if (itemSrc == 95) {
|
|
replaceItemFromInventory(95, 93);
|
|
createMouseItem(116);
|
|
return 1;
|
|
}
|
|
break;
|
|
case 117:
|
|
if (itemSrc == 90 || itemSrc == 33) {
|
|
characterTalk(1490);
|
|
} else if (itemSrc == 102 || itemSrc == 84) {
|
|
characterTalk(1494);
|
|
} else if (itemSrc == 0x59 || itemSrc == 0x52) {
|
|
characterTalk(1496);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
int32 ToonEngine::handleInventoryOnDrew(int32 itemId) {
|
|
switch (itemId) {
|
|
case 1:
|
|
sayLines(1, 1232);
|
|
return 1;
|
|
case 2:
|
|
sayLines(2, 1202);
|
|
return 1;
|
|
case 7:
|
|
if (_gameState->_currentScene == 32) {
|
|
runEventScript(_mouseX, _mouseY, 2, 107, 0);
|
|
} else if (_gameState->_currentScene < 37) {
|
|
sayLines(2, 1258);
|
|
} else {
|
|
sayLines(2, 1462);
|
|
}
|
|
return 1;
|
|
case 8:
|
|
sayLines(2, 1328);
|
|
return 1;
|
|
case 0xc:
|
|
sayLines(1, 1266);
|
|
return 1;
|
|
case 0xd:
|
|
sayLines(1, 1206);
|
|
return 1;
|
|
case 16:
|
|
sayLines(1, 1438);
|
|
return 1;
|
|
case 0x12:
|
|
if (_gameState->_currentScene == 30) {
|
|
runEventScript(_mouseX, _mouseY, 2, 106, 0);
|
|
_gameState->_mouseState = -1;
|
|
} else {
|
|
sayLines(2, 1200);
|
|
}
|
|
return 1;
|
|
case 0x14:
|
|
sayLines(1, 1216);
|
|
return 1;
|
|
case 22:
|
|
if (_gameState->_currentScene != 39 && _gameState->_currentScene != 50 && _gameState->_currentScene != 49) {
|
|
if (_gameState->_currentScene < 37) {
|
|
sayLines(1, 1256);
|
|
} else {
|
|
sayLines(1, 1456);
|
|
}
|
|
} else {
|
|
runEventScript(_mouseX, _mouseY, 2, 100 , 0);
|
|
}
|
|
return 1;
|
|
case 0x18:
|
|
sayLines(1, 1216);
|
|
return 1;
|
|
case 0x23:
|
|
sayLines(1, 1210);
|
|
return 1;
|
|
case 0x31:
|
|
sayLines(1, 1262);
|
|
return 1;
|
|
case 50:
|
|
if (_gameState->_currentScene == 37) {
|
|
runEventScript(_mouseX, _mouseY, 2, 103, 0);
|
|
return 1;
|
|
};
|
|
break;
|
|
case 0x36:
|
|
if (_gameState->_currentScene == 46) {
|
|
runEventScript(_mouseX, _mouseY, 2, 102, 0);
|
|
} else {
|
|
sayLines(1, 1224);
|
|
}
|
|
return 1;
|
|
case 0x37:
|
|
sayLines(1, 1408);
|
|
return 1;
|
|
case 0x20:
|
|
sayLines(1, 1254);
|
|
return 1;
|
|
case 0x21:
|
|
sayLines(1, 1268);
|
|
return 1;
|
|
case 0x22:
|
|
if (_gameState->_currentScene == 52) {
|
|
runEventScript(_mouseX, _mouseY, 2, 104, 0);
|
|
return 1;
|
|
} else {
|
|
_gameState->_mouseHidden = true;
|
|
_drew->setFacing(4);
|
|
sayLines(1, 1465);
|
|
sayLines(1, randRange(0, 1) + 1468);
|
|
createMouseItem(33);
|
|
_gameState->_mouseHidden = false;
|
|
return 1;
|
|
}
|
|
break;
|
|
case 31:
|
|
sayLines(1, 1436);
|
|
return 1;
|
|
case 0x1a:
|
|
sayLines(1, 1216);
|
|
return 1;
|
|
case 0x39:
|
|
sayLines(1, 1270);
|
|
return 1;
|
|
case 0x3a:
|
|
sayLines(1, 1444);
|
|
return 1;
|
|
case 0x3b:
|
|
sayLines(1, 1272);
|
|
return 1;
|
|
case 0x3f:
|
|
if (_gameState->_currentScene != 10 && _gameState->_currentScene != 30 && _gameState->_currentScene != 22) {
|
|
sayLines(1, 1274);
|
|
} else {
|
|
runEventScript(_mouseX, _mouseY, 2, 109, 0);
|
|
}
|
|
return 1;
|
|
case 0x41:
|
|
sayLines(1, 1232);
|
|
return 1;
|
|
|
|
case 0x4b:
|
|
if (_gameState->_currentScene != 53) {
|
|
_gameState->_mouseHidden = true;
|
|
_drew->setFacing(4);
|
|
sayLines(1, 1437);
|
|
sayLines(2, 1440);
|
|
_gameState->_mouseHidden = false;
|
|
} else {
|
|
runEventScript(_mouseX, _mouseY, 2 , 101, 0);
|
|
}
|
|
return 1;
|
|
case 79:
|
|
sayLines(1, 1242);
|
|
return 1;
|
|
case 0x4c:
|
|
sayLines(1, 1232);
|
|
return 1;
|
|
case 71:
|
|
sayLines(1, 1250);
|
|
return 1;
|
|
case 0x43:
|
|
sayLines(1, 1216);
|
|
return 1;
|
|
case 0x60:
|
|
sayLines(2, 1236);
|
|
return 1;
|
|
case 99:
|
|
if (_gameState->_currentScene == 43) {
|
|
runEventScript(_mouseX, _mouseY, 2, 105, 0);
|
|
}
|
|
_gameState->_mouseState = -1;
|
|
setCursor(0, false, 0, 0);
|
|
sayLines(1, 1555);
|
|
return 1;
|
|
case 0x5a:
|
|
sayLines(1, 1432);
|
|
return 1;
|
|
case 0x58:
|
|
sayLines(1, 1432);
|
|
return 1;
|
|
case 0x65:
|
|
if (_gameState->_currentScene == 52) {
|
|
runEventScript(_mouseX, _mouseY, 2, 104, 0);
|
|
} else {
|
|
_gameState->_mouseHidden = true;
|
|
_drew->setFacing(4);
|
|
sayLines(1, 1464);
|
|
sayLines(1, 1468 + randRange(0, 1));
|
|
createMouseItem(100);
|
|
_gameState->_mouseHidden = false;
|
|
}
|
|
return 1;
|
|
case 0x74:
|
|
sayLines(1, 1286);
|
|
return 1;
|
|
case 0x75:
|
|
sayLines(1, 1482);
|
|
return 1;
|
|
case 118:
|
|
sayLines(2, 1500);
|
|
return 1;
|
|
case 115:
|
|
sayLines(1, 1216);
|
|
return 1;
|
|
case 0x67:
|
|
if (_gameState->_currentScene == 52 || _gameState->_currentScene == 53) {
|
|
runEventScript(_mouseX, _mouseY, 2, 108, 0);
|
|
}
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void ToonEngine::deleteItemFromInventory(int32 item) {
|
|
for (int32 i = 0; i < _gameState->_numInventoryItems; i++) {
|
|
if (_gameState->_inventory[i] == item) {
|
|
_gameState->_inventory[i] = 0;
|
|
rearrangeInventory();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ToonEngine::replaceItemFromInventory(int32 item, int32 newitem) {
|
|
for (int32 i = 0; i < _gameState->_numInventoryItems; i++) {
|
|
if (_gameState->_inventory[i] == item) {
|
|
_gameState->_inventory[i] = newitem;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
int32 ToonEngine::pauseSceneAnimationScript(int32 animScriptId, int32 tickToWait) {
|
|
int32 nextTicks = getTickLength() * tickToWait + getSceneAnimationScript(animScriptId)->_lastTimer;
|
|
if (nextTicks < getOldMilli()) {
|
|
getSceneAnimationScript(animScriptId)->_lastTimer = getOldMilli() + getTickLength() * tickToWait;
|
|
} else {
|
|
getSceneAnimationScript(animScriptId)->_lastTimer = nextTicks;
|
|
}
|
|
return nextTicks;
|
|
}
|
|
|
|
Common::String ToonEngine::createRoomFilename(const Common::String& name) {
|
|
Common::String file = Common::String::format("ACT%d/%s/%s", _gameState->_currentChapter, _gameState->_locations[_gameState->_currentScene]._name, name.c_str());
|
|
return file;
|
|
}
|
|
|
|
void ToonEngine::createShadowLUT() {
|
|
// here we create the redirection table that will be used to draw shadows
|
|
// for each color of the palette we find the closest color in the palette that could be used for shadowed color.
|
|
|
|
// In the original program, the scale factor is 0.77f
|
|
// we will use 77 / 100 here.
|
|
|
|
if (!_shadowLUT) {
|
|
_shadowLUT = new uint8[256];
|
|
}
|
|
|
|
uint32 scaleNum = 77;
|
|
uint32 scaleDenom = 100;
|
|
|
|
for (int32 i = 0; i < 255; i++) {
|
|
|
|
// goal color
|
|
uint32 destR = _finalPalette[i * 3 + 0] * scaleNum / scaleDenom;
|
|
uint32 destG = _finalPalette[i * 3 + 1] * scaleNum / scaleDenom;
|
|
uint32 destB = _finalPalette[i * 3 + 2] * scaleNum / scaleDenom;
|
|
|
|
// search only in the "picture palette" which is in colors 1-128 and 200-255
|
|
int32 colorDist = 0xffffff;
|
|
int32 foundColor = 0;
|
|
|
|
for (int32 c = 1; c < 129; c++) {
|
|
|
|
int32 diffR = _finalPalette[c * 3 + 0] - destR;
|
|
int32 diffG = _finalPalette[c * 3 + 1] - destG;
|
|
int32 diffB = _finalPalette[c * 3 + 2] - destB;
|
|
|
|
if (colorDist > diffR * diffR + diffG * diffG + diffB * diffB) {
|
|
colorDist = diffR * diffR + diffG * diffG + diffB * diffB;
|
|
foundColor = c;
|
|
}
|
|
}
|
|
|
|
for (int32 c = 200; c < 256; c++) {
|
|
|
|
int32 diffR = _finalPalette[c * 3 + 0] - destR;
|
|
int32 diffG = _finalPalette[c * 3 + 1] - destG;
|
|
int32 diffB = _finalPalette[c * 3 + 2] - destB;
|
|
|
|
if (colorDist > diffR * diffR + diffG * diffG + diffB * diffB) {
|
|
colorDist = diffR * diffR + diffG * diffG + diffB * diffB;
|
|
foundColor = c;
|
|
}
|
|
}
|
|
|
|
_shadowLUT[i] = foundColor;
|
|
|
|
}
|
|
}
|
|
|
|
bool ToonEngine::loadToonDat() {
|
|
Common::File in;
|
|
Common::String msg;
|
|
int majVer, minVer;
|
|
|
|
in.open("toon.dat");
|
|
|
|
if (!in.isOpen()) {
|
|
msg = "You're missing the 'toon.dat' file. Get it from the ScummVM website";
|
|
GUIErrorMessage(msg);
|
|
warning("%s", msg.c_str());
|
|
return false;
|
|
}
|
|
|
|
// Read header
|
|
char buf[4+1];
|
|
in.read(buf, 4);
|
|
buf[4] = '\0';
|
|
|
|
if (strcmp(buf, "TOON")) {
|
|
msg = "File 'toon.dat' is corrupt. Get it from the ScummVM website";
|
|
GUIErrorMessage(msg);
|
|
warning("%s", msg.c_str());
|
|
return false;
|
|
}
|
|
|
|
majVer = in.readByte();
|
|
minVer = in.readByte();
|
|
|
|
if ((majVer != TOON_DAT_VER_MAJ) || (minVer != TOON_DAT_VER_MIN)) {
|
|
msg = Common::String::format("File 'toon.dat' is wrong version. Expected %d.%d but got %d.%d. Get it from the ScummVM website", TOON_DAT_VER_MAJ, TOON_DAT_VER_MIN, majVer, minVer);
|
|
GUIErrorMessage(msg);
|
|
warning("%s", msg.c_str());
|
|
|
|
return false;
|
|
}
|
|
|
|
_numVariant = in.readUint16BE();
|
|
|
|
_locationDirNotVisited = loadTextsVariants(in);
|
|
_locationDirVisited = loadTextsVariants(in);
|
|
_specialInfoLine = loadTextsVariants(in);
|
|
|
|
return true;
|
|
}
|
|
|
|
void ToonEngine::unloadToonDat() {
|
|
unloadTextsVariants(_locationDirNotVisited);
|
|
unloadTextsVariants(_locationDirVisited);
|
|
unloadTextsVariants(_specialInfoLine);
|
|
}
|
|
|
|
char **ToonEngine::loadTextsVariants(Common::File &in) {
|
|
int numTexts;
|
|
int entryLen;
|
|
int len;
|
|
char **res = 0;
|
|
char *pos = 0;
|
|
|
|
for (int varnt = 0; varnt < _numVariant; varnt++) {
|
|
numTexts = in.readUint16BE();
|
|
entryLen = in.readUint16BE();
|
|
pos = (char *)malloc(entryLen);
|
|
if (varnt == _gameVariant) {
|
|
res = (char **)malloc(sizeof(char *) * numTexts);
|
|
res[0] = pos;
|
|
in.read(res[0], entryLen);
|
|
res[0] += DATAALIGNMENT;
|
|
} else {
|
|
in.read(pos, entryLen);
|
|
free(pos);
|
|
continue;
|
|
}
|
|
|
|
pos += DATAALIGNMENT;
|
|
|
|
for (int i = 1; i < numTexts; i++) {
|
|
pos -= 2;
|
|
|
|
len = READ_BE_UINT16(pos);
|
|
pos += 2 + len;
|
|
|
|
if (varnt == _gameVariant)
|
|
res[i] = pos;
|
|
}
|
|
}
|
|
|
|
return res;
|
|
}
|
|
|
|
void ToonEngine::unloadTextsVariants(char **texts) {
|
|
if (!texts)
|
|
return;
|
|
|
|
free(*texts - DATAALIGNMENT);
|
|
free(texts);
|
|
}
|
|
|
|
void ToonEngine::makeLineNonWalkable(int32 x, int32 y, int32 x2, int32 y2) {
|
|
_currentMask->drawLineOnMask(x, y, x2, y2, false);
|
|
}
|
|
|
|
void ToonEngine::makeLineWalkable(int32 x, int32 y, int32 x2, int32 y2) {
|
|
_currentMask->drawLineOnMask(x, y, x2, y2, true);
|
|
}
|
|
|
|
void ToonEngine::playRoomMusic() {
|
|
if (_gameState->_inConversation) {
|
|
const char* music = getSpecialConversationMusic(_gameState->_currentConversationId);
|
|
if (music) {
|
|
_audioManager->playMusic(_gameState->_locations[_gameState->_currentScene]._name, music);
|
|
return;
|
|
}
|
|
}
|
|
|
|
_audioManager->playMusic(_gameState->_locations[_gameState->_currentScene]._name, _gameState->_locations[_gameState->_currentScene]._music);
|
|
}
|
|
|
|
void ToonEngine::dirtyAllScreen()
|
|
{
|
|
_dirtyRects.clear();
|
|
_dirtyAll = true;
|
|
}
|
|
|
|
void ToonEngine::addDirtyRect( int32 left, int32 top, int32 right, int32 bottom ) {
|
|
left = MIN<int32>(MAX<int32>(left, 0), TOON_BACKBUFFER_WIDTH);
|
|
right = MIN<int32>(MAX<int32>(right, 0), TOON_BACKBUFFER_WIDTH);
|
|
top = MIN<int32>(MAX<int32>(top, 0), TOON_BACKBUFFER_HEIGHT);
|
|
bottom = MIN<int32>(MAX<int32>(bottom, 0), TOON_BACKBUFFER_HEIGHT);
|
|
|
|
if (bottom - top <= 0 || right - left <= 0)
|
|
return;
|
|
|
|
Common::Rect rect(left, top, right, bottom);
|
|
|
|
for (uint32 i = 0; i < _dirtyRects.size(); i++) {
|
|
if (_dirtyRects[i].contains(rect))
|
|
return;
|
|
if (rect.contains(_dirtyRects[i])) {
|
|
_dirtyRects.remove_at(i);
|
|
i--;
|
|
}
|
|
}
|
|
|
|
// check also in the old rect (of the old frame)
|
|
for (int32 i = _oldDirtyRects.size() - 1 ; i >= 0; i--) {
|
|
if (rect.contains(_oldDirtyRects[i])) {
|
|
_oldDirtyRects.remove_at(i);
|
|
}
|
|
}
|
|
|
|
_dirtyRects.push_back(rect);
|
|
}
|
|
|
|
void ToonEngine::clearDirtyRects() {
|
|
_oldDirtyRects = _dirtyRects;
|
|
_dirtyRects.clear();
|
|
_dirtyAll = false;
|
|
}
|
|
void SceneAnimation::save(ToonEngine *vm, Common::WriteStream *stream) {
|
|
stream->writeByte(_active);
|
|
stream->writeSint32BE(_id);
|
|
|
|
if (!_active)
|
|
return;
|
|
|
|
if (_animInstance) {
|
|
stream->writeByte(1);
|
|
_animInstance->save(stream);
|
|
} else {
|
|
stream->writeByte(0);
|
|
}
|
|
|
|
if (!_animation) {
|
|
stream->writeByte(0);
|
|
} else {
|
|
stream->writeByte(strlen(_animation->_name) + 1);
|
|
stream->write(_animation->_name, strlen(_animation->_name) + 1);
|
|
}
|
|
}
|
|
void SceneAnimation::load(ToonEngine *vm, Common::ReadStream *stream) {
|
|
|
|
_active = stream->readByte();
|
|
_id = stream->readSint32BE();
|
|
|
|
if (!_active)
|
|
return;
|
|
|
|
if (stream->readByte() == 1) {
|
|
_animInstance = vm->getAnimationManager()->createNewInstance(kAnimationScene);
|
|
_animInstance->load(stream);
|
|
// we add them at the end of loading in reverse order
|
|
//vm->getAnimationManager()->addInstance(_animInstance);
|
|
_originalAnimInstance = _animInstance;
|
|
} else {
|
|
_animInstance = NULL;
|
|
_originalAnimInstance = NULL;
|
|
}
|
|
|
|
// load animation if any
|
|
char animationName[256];
|
|
*animationName = 0;
|
|
int8 strSize = stream->readByte();
|
|
if (!strSize) {
|
|
_animation = 0;
|
|
if (_animInstance)
|
|
_animInstance->setAnimation(0);
|
|
} else {
|
|
stream->read(animationName, strSize);
|
|
animationName[strSize] = 0;
|
|
|
|
_animation = new Animation(vm);
|
|
_animation->loadAnimation(animationName);
|
|
|
|
if (_animInstance) {
|
|
_animInstance->setAnimation(_animation, false);
|
|
}
|
|
}
|
|
}
|
|
|
|
} // End of namespace Toon
|