mirror of
https://github.com/libretro/scummvm.git
synced 2025-02-13 07:14:59 +00:00
![Colin Snover](/assets/img/avatar_default.png)
This flag is removed for a few reasons: * Engines universally set this flag to true for widths > 320, which made it redundant everywhere; * This flag functioned primarily as a "force 1x scaler" flag, since its behaviour was almost completely undocumented and users would need to figure out that they'd need an explicit non-default scaler set to get a scaler to operate at widths > 320; * (Most importantly) engines should not be in the business of deciding how the backend may choose to render its virtual screen. The choice of rendering behaviour belongs to the user, and the backend, in that order. A nearby future commit restores the default1x scaler behaviour in the SDL backend code for the moment, but in the future it is my hope that there will be a better configuration UI to allow users to specify how they want scaling to work for high resolutions.
1241 lines
32 KiB
C++
1241 lines
32 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 "graphics/cursorman.h"
|
|
#include "gnap/gnap.h"
|
|
#include "gnap/datarchive.h"
|
|
#include "gnap/gamesys.h"
|
|
#include "gnap/resource.h"
|
|
#include "gnap/sound.h"
|
|
|
|
#include "common/config-manager.h"
|
|
#include "common/debug-channels.h"
|
|
#include "common/timer.h"
|
|
|
|
#include "engines/util.h"
|
|
|
|
namespace Gnap {
|
|
|
|
static const int kCursors[] = {
|
|
LOOK_CURSOR,
|
|
GRAB_CURSOR,
|
|
TALK_CURSOR,
|
|
PLAT_CURSOR
|
|
};
|
|
|
|
static const int kDisabledCursors[] = {
|
|
NOLOOK_CURSOR,
|
|
NOGRAB_CURSOR,
|
|
NOTALK_CURSOR,
|
|
NOPLAT_CURSOR
|
|
};
|
|
|
|
static const char *kCursorNames[] = {
|
|
"LOOK_CURSOR",
|
|
"GRAB_CURSOR",
|
|
"TALK_CURSOR",
|
|
"PLAT_CURSOR",
|
|
"NOLOOK_CURSOR",
|
|
"NOGRAB_CURSOR",
|
|
"NOTALK_CURSOR",
|
|
"NOPLAT_CURSOR",
|
|
"EXIT_L_CURSOR",
|
|
"EXIT_R_CURSOR",
|
|
"EXIT_U_CURSOR",
|
|
"EXIT_D_CURSOR",
|
|
"EXIT_NE_CURSOR",
|
|
"EXIT_NW_CURSOR",
|
|
"EXIT_SE_CURSOR",
|
|
"EXIT_SW_CURSOR",
|
|
"WAIT_CURSOR"
|
|
};
|
|
|
|
|
|
static const int kCursorSpriteIds[30] = {
|
|
0x005, 0x008, 0x00A, 0x004, 0x009, 0x003,
|
|
0x006, 0x007, 0x00D, 0x00F, 0x00B, 0x00C,
|
|
0x019, 0x01C, 0x015, 0x014, 0x010, 0x01A,
|
|
0x018, 0x013, 0x011, 0x012, 0x01B, 0x016,
|
|
0x017, 0x01D, 0x01E, 0x01F, 0x76A, 0x76B
|
|
};
|
|
|
|
static const char *kSceneNames[] = {
|
|
"open", "pigpn", "truck", "creek", "mafrm", "frbrn", "inbrn", "crash",
|
|
"porch", "barbk", "kitch", "bar", "juke", "wash", "john", "jkbox",
|
|
"brawl", "stret", "frtoy", "intoy", "frgro", "park", "cash", "ingro",
|
|
"frcir", "booth", "circ", "outcl", "incln", "monk", "elcir", "beer",
|
|
"pig2", "trk2", "creek", "frbrn", "inbrn", "mafrm", "infrm", "efair",
|
|
"fair", "souv", "chick", "ship", "kiss", "disco", "boot", "can",
|
|
"can2", "drive", "tung", "puss", "space", "phone", "can3"
|
|
};
|
|
|
|
GnapEngine::GnapEngine(OSystem *syst, const ADGameDescription *gd) :
|
|
Engine(syst), _gameDescription(gd) {
|
|
|
|
DebugMan.addDebugChannel(kDebugBasic, "basic", "Basic debug level");
|
|
|
|
_random = new Common::RandomSource("gnap");
|
|
|
|
Engine::syncSoundSettings();
|
|
|
|
_exe = nullptr;
|
|
_dat = nullptr;
|
|
_spriteCache = nullptr;
|
|
_soundCache = nullptr;
|
|
_sequenceCache = nullptr;
|
|
_gameSys = nullptr;
|
|
_soundMan = nullptr;
|
|
_debugger = nullptr;
|
|
_gnap = nullptr;
|
|
_plat = nullptr;
|
|
_font = nullptr;
|
|
_scene = nullptr;
|
|
_music = nullptr;
|
|
_tempThumbnail = nullptr;
|
|
_menuBackgroundSurface = nullptr;
|
|
_menuQuitQuerySprite = nullptr;
|
|
_largeSprite = nullptr;
|
|
_menuSaveLoadSprite = nullptr;
|
|
_menuSprite2 = nullptr;
|
|
_menuSprite1 = nullptr;
|
|
_spriteHandle = nullptr;
|
|
_cursorSprite = nullptr;
|
|
_pauseSprite = nullptr;
|
|
_backgroundSurface = nullptr;
|
|
|
|
_wasSavegameLoaded = false;
|
|
_isWaiting = false;
|
|
_sceneWaiting = false;
|
|
_menuDone = false;
|
|
_sceneDone = false;
|
|
_isLeavingScene = false;
|
|
_isStockDatLoaded = false;
|
|
_gameDone = false;
|
|
_isPaused = false;
|
|
_sceneSavegameLoaded = false;
|
|
|
|
for (int i = 0; i < kMaxTimers; ++i)
|
|
_savedTimers[i] = _timers[i] = 0;
|
|
|
|
_mousePos = Common::Point(0, 0);
|
|
_currGrabCursorX = _currGrabCursorY = 0;
|
|
|
|
_idleTimerIndex = -1;
|
|
_menuStatus = 0;
|
|
_menuSpritesIndex = -1;
|
|
_savegameIndex = -1;
|
|
_gridMinX = 0;
|
|
_gridMinY = 0;
|
|
_gridMaxX = 0;
|
|
_gridMaxY = 0;
|
|
_toyUfoNextSequenceId = -1;
|
|
_toyUfoSequenceId = -1;
|
|
_toyUfoId = -1;
|
|
_toyUfoActionStatus = -1;
|
|
_toyUfoX = 0;
|
|
_toyUfoY = 0;
|
|
_s18GarbageCanPos = 0;
|
|
|
|
for (int i = 0; i < 7; i++)
|
|
_savegameSprites[i] = nullptr;
|
|
for (int i = 0; i < 30; i++)
|
|
_menuInventorySprites[i] = nullptr;
|
|
|
|
_newSceneNum = 0;
|
|
_inventory = 0;
|
|
_gameFlags = 0;
|
|
_hotspotsCount = 0;
|
|
_sceneClickedHotspot = -1;
|
|
_newCursorValue = 0;
|
|
_cursorValue = 0;
|
|
_verbCursor = 0;
|
|
_cursorIndex = -1;
|
|
_leftClickMouseX = 0;
|
|
_leftClickMouseY = 0;
|
|
_grabCursorSprite = nullptr;
|
|
_grabCursorSpriteIndex = 0;
|
|
_newGrabCursorSpriteIndex = 0;
|
|
_fullScreenSprite = nullptr;
|
|
_fullScreenSpriteId = 0;
|
|
_deviceX1 = 0;
|
|
_deviceY1 = 0;
|
|
_soundTimerIndexA = 0;
|
|
_soundTimerIndexB = 0;
|
|
_soundTimerIndexC = 0;
|
|
_loadGameSlot = -1;
|
|
_lastUpdateClock = 0;
|
|
_prevSceneNum = -1;
|
|
_currentSceneNum = -1;
|
|
}
|
|
|
|
GnapEngine::~GnapEngine() {
|
|
delete _random;
|
|
delete _music;
|
|
delete _tempThumbnail;
|
|
}
|
|
|
|
Common::Error GnapEngine::run() {
|
|
// Initialize the graphics mode to RGBA8888
|
|
#if defined(SCUMM_BIG_ENDIAN)
|
|
Graphics::PixelFormat format = Graphics::PixelFormat(4, 8, 8, 8, 8, 0, 8, 16, 24);
|
|
#else
|
|
Graphics::PixelFormat format = Graphics::PixelFormat(4, 8, 8, 8, 8, 24, 16, 8, 0);
|
|
#endif
|
|
initGraphics(800, 600, &format);
|
|
|
|
// We do not support color conversion yet
|
|
if (_system->getScreenFormat() != format)
|
|
return Common::kUnsupportedColorMode;
|
|
|
|
_lastUpdateClock = 0;
|
|
|
|
// >>>>> Variable initialization
|
|
_cursorIndex = -1;
|
|
_verbCursor = 1;
|
|
|
|
if (ConfMan.hasKey("save_slot"))
|
|
_loadGameSlot = ConfMan.getInt("save_slot");
|
|
|
|
invClear();
|
|
clearFlags();
|
|
|
|
_grabCursorSprite = nullptr;
|
|
_newGrabCursorSpriteIndex = -1;
|
|
_backgroundSurface = nullptr;
|
|
_isStockDatLoaded = false;
|
|
_gameDone = false;
|
|
_isPaused = false;
|
|
_pauseSprite = nullptr;
|
|
|
|
////////////////////////////////////////////////////////////////////////////
|
|
|
|
_exe = new Common::PEResources();
|
|
if (!_exe->loadFromEXE("ufos.exe"))
|
|
error("Could not load ufos.exe");
|
|
|
|
#ifdef USE_FREETYPE2
|
|
Common::SeekableReadStream *stream = _exe->getResource(Common::kPEFont, 2000);
|
|
_font = Graphics::loadTTFFont(*stream, 24);
|
|
if (!_font)
|
|
warning("Unable to load font");
|
|
delete stream;
|
|
#else
|
|
_font = nullptr;
|
|
#endif
|
|
|
|
_dat = new DatManager();
|
|
_spriteCache = new SpriteCache(_dat);
|
|
_soundCache = new SoundCache(_dat);
|
|
_sequenceCache = new SequenceCache(_dat);
|
|
_gameSys = new GameSys(this);
|
|
_soundMan = new SoundMan(this);
|
|
_debugger = new Debugger();
|
|
_gnap = new PlayerGnap(this);
|
|
_plat = new PlayerPlat(this);
|
|
|
|
_menuBackgroundSurface = nullptr;
|
|
|
|
initGlobalSceneVars();
|
|
mainLoop();
|
|
|
|
delete _plat;
|
|
delete _gnap;
|
|
delete _soundMan;
|
|
delete _gameSys;
|
|
delete _sequenceCache;
|
|
delete _soundCache;
|
|
delete _spriteCache;
|
|
delete _dat;
|
|
delete _debugger;
|
|
delete _font;
|
|
delete _exe;
|
|
|
|
return Common::kNoError;
|
|
}
|
|
|
|
void GnapEngine::updateEvents() {
|
|
Common::Event event;
|
|
|
|
while (_eventMan->pollEvent(event)) {
|
|
switch (event.type) {
|
|
case Common::EVENT_KEYDOWN:
|
|
// Check for debugger
|
|
if (event.kbd.keycode == Common::KEYCODE_d && (event.kbd.flags & Common::KBD_CTRL)) {
|
|
// Attach to the debugger
|
|
_debugger->attach();
|
|
_debugger->onFrame();
|
|
}
|
|
|
|
_keyPressState[event.kbd.keycode] = true;
|
|
_keyDownState[event.kbd.keycode] = true;
|
|
break;
|
|
case Common::EVENT_KEYUP:
|
|
_keyDownState[event.kbd.keycode] = false;
|
|
break;
|
|
case Common::EVENT_MOUSEMOVE:
|
|
_mousePos = event.mouse;
|
|
break;
|
|
case Common::EVENT_LBUTTONUP:
|
|
_mouseButtonState._left = false;
|
|
break;
|
|
case Common::EVENT_LBUTTONDOWN:
|
|
_leftClickMouseX = event.mouse.x;
|
|
_leftClickMouseY = event.mouse.y;
|
|
_mouseButtonState._left = true;
|
|
_mouseClickState._left = true;
|
|
break;
|
|
case Common::EVENT_RBUTTONUP:
|
|
_mouseButtonState._right = false;
|
|
break;
|
|
case Common::EVENT_RBUTTONDOWN:
|
|
_mouseButtonState._right = true;
|
|
_mouseClickState._right = true;
|
|
break;
|
|
case Common::EVENT_QUIT:
|
|
quitGame();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void GnapEngine::gameUpdateTick() {
|
|
updateEvents();
|
|
|
|
if (shouldQuit()) {
|
|
_gameDone = true;
|
|
_sceneDone = true;
|
|
}
|
|
|
|
int currClock = _system->getMillis();
|
|
if (currClock >= _lastUpdateClock + 66) {
|
|
_gameSys->fatUpdate();
|
|
_gameSys->drawSprites();
|
|
_gameSys->updateScreen();
|
|
_gameSys->updatePlaySounds();
|
|
_gameSys->_gameSysClock++;
|
|
updateTimers();
|
|
_lastUpdateClock = currClock;
|
|
}
|
|
|
|
_soundMan->update();
|
|
_system->updateScreen();
|
|
_system->delayMillis(5);
|
|
}
|
|
|
|
void GnapEngine::saveTimers() {
|
|
for (int i = 0; i < kMaxTimers; ++i )
|
|
_savedTimers[i] = _timers[i];
|
|
}
|
|
|
|
void GnapEngine::restoreTimers() {
|
|
for (int i = 0; i < kMaxTimers; ++i )
|
|
_timers[i] = _savedTimers[i];
|
|
}
|
|
|
|
void GnapEngine::pauseGame() {
|
|
if (!_isPaused) {
|
|
saveTimers();
|
|
hideCursor();
|
|
setGrabCursorSprite(-1);
|
|
_pauseSprite = _gameSys->createSurface(0x1076C);
|
|
_gameSys->insertSpriteDrawItem(_pauseSprite, (800 - _pauseSprite->w) / 2, (600 - _pauseSprite->h) / 2, 356);
|
|
_lastUpdateClock = 0;
|
|
gameUpdateTick();
|
|
playMidi("pause.mid");
|
|
_isPaused = true;
|
|
}
|
|
}
|
|
|
|
void GnapEngine::resumeGame() {
|
|
if (_isPaused) {
|
|
restoreTimers();
|
|
_gameSys->removeSpriteDrawItem(_pauseSprite, 356);
|
|
_lastUpdateClock = 0;
|
|
gameUpdateTick();
|
|
deleteSurface(&_pauseSprite);
|
|
stopMidi();
|
|
_isPaused = false;
|
|
clearAllKeyStatus1();
|
|
_mouseClickState._left = false;
|
|
_mouseClickState._right = false;
|
|
showCursor();
|
|
_gameSys->_gameSysClock = 0;
|
|
_gameSys->_lastUpdateClock = 0;
|
|
}
|
|
}
|
|
|
|
void GnapEngine::updatePause() {
|
|
while (_isPaused && !_gameDone) {
|
|
gameUpdateTick();
|
|
if (isKeyStatus1(Common::KEYCODE_p)) {
|
|
clearKeyStatus1(Common::KEYCODE_p);
|
|
resumeGame();
|
|
}
|
|
}
|
|
}
|
|
|
|
int GnapEngine::getRandom(int max) {
|
|
return _random->getRandomNumber(max - 1);
|
|
}
|
|
|
|
int GnapEngine::readSavegameDescription(int savegameNum, Common::String &description) {
|
|
description = Common::String::format("Savegame %d", savegameNum);
|
|
return 0;
|
|
}
|
|
|
|
int GnapEngine::loadSavegame(int savegameNum) {
|
|
return 1;
|
|
}
|
|
|
|
void GnapEngine::delayTicks(int val, int idx = 0, bool updateCursor = false) {
|
|
int startTick = _timers[idx];
|
|
|
|
_timers[idx] = val;
|
|
|
|
while (_timers[idx] && !_gameDone) {
|
|
gameUpdateTick();
|
|
|
|
if (updateCursor)
|
|
updateGrabCursorSprite(0, 0);
|
|
}
|
|
|
|
startTick -= _timers[idx];
|
|
if (startTick < 0)
|
|
startTick = 0;
|
|
|
|
_timers[idx] = startTick;
|
|
}
|
|
|
|
void GnapEngine::delayTicksA(int val, int idx) {
|
|
delayTicks(val, idx);
|
|
}
|
|
|
|
void GnapEngine::delayTicksCursor(int val) {
|
|
delayTicks(val, 0, true);
|
|
}
|
|
|
|
void GnapEngine::setHotspot(int index, int16 x1, int16 y1, int16 x2, int16 y2, uint16 flags,
|
|
int16 walkX, int16 walkY) {
|
|
_hotspots[index]._rect = Common::Rect(x1, y1, x2, y2);
|
|
_hotspots[index]._flags = flags;
|
|
_hotspotsWalkPos[index] = Common::Point(walkX, walkY);
|
|
}
|
|
|
|
int GnapEngine::getHotspotIndexAtPos(Common::Point pos) {
|
|
for (int i = 0; i < _hotspotsCount; ++i) {
|
|
if (!_hotspots[i].isFlag(SF_DISABLED) && _hotspots[i].isPointInside(pos))
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void GnapEngine::updateCursorByHotspot() {
|
|
if (!_isWaiting) {
|
|
int hotspotIndex = getHotspotIndexAtPos(_mousePos);
|
|
|
|
if (_debugger->_showHotspotNumber) {
|
|
// NOTE This causes some display glitches
|
|
char t[256];
|
|
sprintf(t, "hotspot = %2d", hotspotIndex);
|
|
if (!_font)
|
|
_gameSys->fillSurface(nullptr, 10, 10, 80, 16, 0, 0, 0);
|
|
else
|
|
_gameSys->fillSurface(nullptr, 8, 9, _font->getStringWidth(t) + 10, _font->getFontHeight() + 2, 0, 0, 0);
|
|
_gameSys->drawTextToSurface(nullptr, 10, 10, 255, 255, 255, t);
|
|
}
|
|
|
|
if (hotspotIndex < 0)
|
|
setCursor(kDisabledCursors[_verbCursor]);
|
|
else if (_hotspots[hotspotIndex]._flags & SF_EXIT_L_CURSOR)
|
|
setCursor(EXIT_L_CURSOR);
|
|
else if (_hotspots[hotspotIndex]._flags & SF_EXIT_R_CURSOR)
|
|
setCursor(EXIT_R_CURSOR);
|
|
else if (_hotspots[hotspotIndex]._flags & SF_EXIT_U_CURSOR)
|
|
setCursor(EXIT_U_CURSOR);
|
|
else if (_hotspots[hotspotIndex]._flags & SF_EXIT_D_CURSOR)
|
|
setCursor(EXIT_D_CURSOR);
|
|
else if (_hotspots[hotspotIndex]._flags & SF_EXIT_NE_CURSOR)
|
|
setCursor(EXIT_NE_CURSOR);
|
|
else if (_hotspots[hotspotIndex]._flags & SF_EXIT_NW_CURSOR)
|
|
setCursor(EXIT_NW_CURSOR);
|
|
else if (_hotspots[hotspotIndex]._flags & SF_EXIT_SE_CURSOR)
|
|
setCursor(EXIT_SE_CURSOR);
|
|
else if (_hotspots[hotspotIndex]._flags & SF_EXIT_SW_CURSOR)
|
|
setCursor(EXIT_SW_CURSOR);
|
|
else if (_hotspots[hotspotIndex]._flags & (1 << _verbCursor))
|
|
setCursor(kCursors[_verbCursor]);
|
|
else
|
|
setCursor(kDisabledCursors[_verbCursor]);
|
|
}
|
|
// Update platypus hotspot
|
|
_hotspots[0]._rect = Common::Rect(_gridMinX + 75 * _plat->_pos.x - 30, _gridMinY + 48 * _plat->_pos.y - 100
|
|
, _gridMinX + 75 * _plat->_pos.x + 30, _gridMinY + 48 * _plat->_pos.y);
|
|
}
|
|
|
|
int GnapEngine::getClickedHotspotId() {
|
|
int result = -1;
|
|
if (_isWaiting)
|
|
_mouseClickState._left = false;
|
|
else if (_mouseClickState._left) {
|
|
int hotspotIndex = getHotspotIndexAtPos(Common::Point(_leftClickMouseX, _leftClickMouseY));
|
|
if (hotspotIndex >= 0) {
|
|
_mouseClickState._left = false;
|
|
_timers[3] = 300;
|
|
result = hotspotIndex;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
int GnapEngine::getInventoryItemSpriteNum(int index) {
|
|
return kCursorSpriteIds[index];
|
|
}
|
|
|
|
void GnapEngine::updateMouseCursor() {
|
|
if (_mouseClickState._right) {
|
|
// Switch through the verb cursors
|
|
_mouseClickState._right = false;
|
|
_timers[3] = 300;
|
|
_verbCursor = (_verbCursor + 1) % 4;
|
|
if (!isFlag(kGFPlatypus) && _verbCursor == PLAT_CURSOR && _cursorValue == 1)
|
|
_verbCursor = (_verbCursor + 1) % 4;
|
|
if (!_isWaiting)
|
|
setCursor(kDisabledCursors[_verbCursor]);
|
|
setGrabCursorSprite(-1);
|
|
}
|
|
if (_isWaiting && ((_gnap->_actionStatus < 0 && _plat->_actionStatus < 0) || _sceneWaiting)) {
|
|
setCursor(kDisabledCursors[_verbCursor]);
|
|
showCursor();
|
|
_isWaiting = false;
|
|
} else if (!_isWaiting && (_gnap->_actionStatus >= 0 || _plat->_actionStatus >= 0) && !_sceneWaiting) {
|
|
setCursor(WAIT_CURSOR);
|
|
hideCursor();
|
|
_isWaiting = true;
|
|
}
|
|
}
|
|
|
|
void GnapEngine::setVerbCursor(int verbCursor) {
|
|
_verbCursor = verbCursor;
|
|
if (!_isWaiting)
|
|
setCursor(kDisabledCursors[_verbCursor]);
|
|
}
|
|
|
|
void GnapEngine::setCursor(int cursorIndex) {
|
|
if (_cursorIndex != cursorIndex) {
|
|
const char *cursorName = kCursorNames[cursorIndex];
|
|
Graphics::WinCursorGroup *cursorGroup = Graphics::WinCursorGroup::createCursorGroup(*_exe, Common::WinResourceID(cursorName));
|
|
if (cursorGroup) {
|
|
Graphics::Cursor *cursor = cursorGroup->cursors[0].cursor;
|
|
CursorMan.replaceCursor(cursor->getSurface(), cursor->getWidth(), cursor->getHeight(),
|
|
cursor->getHotspotX(), cursor->getHotspotY(), cursor->getKeyColor());
|
|
CursorMan.replaceCursorPalette(cursor->getPalette(), 0, 256);
|
|
delete cursorGroup;
|
|
}
|
|
_cursorIndex = cursorIndex;
|
|
}
|
|
}
|
|
|
|
void GnapEngine::showCursor() {
|
|
CursorMan.showMouse(true);
|
|
}
|
|
|
|
void GnapEngine::hideCursor() {
|
|
CursorMan.showMouse(false);
|
|
}
|
|
|
|
void GnapEngine::setGrabCursorSprite(int index) {
|
|
freeGrabCursorSprite();
|
|
if (index >= 0) {
|
|
createGrabCursorSprite(makeRid(1, kCursorSpriteIds[index]));
|
|
setVerbCursor(GRAB_CURSOR);
|
|
}
|
|
_grabCursorSpriteIndex = index;
|
|
}
|
|
|
|
void GnapEngine::createGrabCursorSprite(int spriteId) {
|
|
_grabCursorSprite = _gameSys->createSurface(spriteId);
|
|
_gameSys->insertSpriteDrawItem(_grabCursorSprite,
|
|
_mousePos.x - (_grabCursorSprite->w / 2),
|
|
_mousePos.y - (_grabCursorSprite->h / 2),
|
|
300);
|
|
delayTicks(5);
|
|
}
|
|
|
|
void GnapEngine::freeGrabCursorSprite() {
|
|
if (_grabCursorSprite) {
|
|
_gameSys->removeSpriteDrawItem(_grabCursorSprite, 300);
|
|
_gameSys->removeSpriteDrawItem(_grabCursorSprite, 301);
|
|
delayTicks(5);
|
|
deleteSurface(&_grabCursorSprite);
|
|
}
|
|
}
|
|
|
|
void GnapEngine::updateGrabCursorSprite(int x, int y) {
|
|
if (_grabCursorSprite) {
|
|
int newGrabCursorX = _mousePos.x - (_grabCursorSprite->w / 2) - x;
|
|
int newGrabCursorY = _mousePos.y - (_grabCursorSprite->h / 2) - y;
|
|
if (_currGrabCursorX != newGrabCursorX || _currGrabCursorY != newGrabCursorY) {
|
|
_currGrabCursorX = newGrabCursorX;
|
|
_currGrabCursorY = newGrabCursorY;
|
|
Common::Rect rect(newGrabCursorX, newGrabCursorY,
|
|
newGrabCursorX + _grabCursorSprite->w, newGrabCursorY + _grabCursorSprite->h);
|
|
_gameSys->invalidateGrabCursorSprite(300, rect, _grabCursorSprite, _grabCursorSprite);
|
|
}
|
|
}
|
|
}
|
|
|
|
void GnapEngine::invClear() {
|
|
_inventory = 0;
|
|
}
|
|
|
|
void GnapEngine::invAdd(int itemId) {
|
|
_inventory |= (1 << itemId);
|
|
}
|
|
|
|
void GnapEngine::invRemove(int itemId) {
|
|
_inventory &= ~(1 << itemId);
|
|
}
|
|
|
|
bool GnapEngine::invHas(int itemId) {
|
|
return (_inventory & (1 << itemId)) != 0;
|
|
}
|
|
|
|
void GnapEngine::clearFlags() {
|
|
_gameFlags = 0;
|
|
}
|
|
|
|
void GnapEngine::setFlag(int num) {
|
|
_gameFlags |= (1 << num);
|
|
}
|
|
|
|
void GnapEngine::clearFlag(int num) {
|
|
_gameFlags &= ~(1 << num);
|
|
}
|
|
|
|
bool GnapEngine::isFlag(int num) {
|
|
return (_gameFlags & (1 << num)) != 0;
|
|
}
|
|
|
|
Graphics::Surface *GnapEngine::addFullScreenSprite(int resourceId, int id) {
|
|
_fullScreenSpriteId = id;
|
|
_fullScreenSprite = _gameSys->createSurface(resourceId);
|
|
_gameSys->insertSpriteDrawItem(_fullScreenSprite, 0, 0, id);
|
|
return _fullScreenSprite;
|
|
}
|
|
|
|
void GnapEngine::removeFullScreenSprite() {
|
|
_gameSys->removeSpriteDrawItem(_fullScreenSprite, _fullScreenSpriteId);
|
|
deleteSurface(&_fullScreenSprite);
|
|
}
|
|
|
|
void GnapEngine::showFullScreenSprite(int resourceId) {
|
|
hideCursor();
|
|
setGrabCursorSprite(-1);
|
|
addFullScreenSprite(resourceId, 256);
|
|
while (!_mouseClickState._left && !isKeyStatus1(Common::KEYCODE_ESCAPE)
|
|
&& !isKeyStatus1(Common::KEYCODE_SPACE) && !isKeyStatus1(Common::KEYCODE_RETURN) && !_gameDone) {
|
|
gameUpdateTick();
|
|
}
|
|
_mouseClickState._left = false;
|
|
clearKeyStatus1(Common::KEYCODE_ESCAPE);
|
|
clearKeyStatus1(Common::KEYCODE_RETURN);
|
|
clearKeyStatus1(Common::KEYCODE_SPACE);
|
|
removeFullScreenSprite();
|
|
showCursor();
|
|
}
|
|
|
|
void GnapEngine::queueInsertDeviceIcon() {
|
|
_gameSys->insertSequence(0x10849, 20, 0, 0, kSeqNone, 0, _deviceX1, _deviceY1);
|
|
}
|
|
|
|
void GnapEngine::insertDeviceIconActive() {
|
|
_gameSys->insertSequence(0x1084A, 21, 0, 0, kSeqNone, 0, _deviceX1, _deviceY1);
|
|
}
|
|
|
|
void GnapEngine::removeDeviceIconActive() {
|
|
_gameSys->removeSequence(0x1084A, 21, true);
|
|
}
|
|
|
|
void GnapEngine::setDeviceHotspot(int hotspotIndex, int x1, int y1, int x2, int y2) {
|
|
_deviceX1 = x1;
|
|
_deviceY1 = y1;
|
|
int deviceX2 = x2;
|
|
int deviceY2 = y2;
|
|
if (x1 == -1)
|
|
_deviceX1 = 730;
|
|
if (x2 == -1)
|
|
deviceX2 = 780;
|
|
if (y1 == -1)
|
|
_deviceY1 = 14;
|
|
if (y2 == -1)
|
|
deviceY2 = 79;
|
|
|
|
_hotspots[hotspotIndex]._rect = Common::Rect(_deviceX1, _deviceY1, deviceX2, deviceY2);
|
|
_hotspots[hotspotIndex]._flags = SF_TALK_CURSOR | SF_GRAB_CURSOR | SF_LOOK_CURSOR;
|
|
}
|
|
|
|
int GnapEngine::getSequenceTotalDuration(int resourceId) {
|
|
SequenceResource *sequenceResource = _sequenceCache->get(resourceId);
|
|
int maxValue = 0;
|
|
for (int i = 0; i < sequenceResource->_animationsCount; ++i) {
|
|
SequenceAnimation *animation = &sequenceResource->_animations[i];
|
|
if (animation->_additionalDelay + animation->_maxTotalDuration > maxValue)
|
|
maxValue = animation->_additionalDelay + animation->_maxTotalDuration;
|
|
}
|
|
int totalDuration = maxValue + sequenceResource->_totalDuration;
|
|
_sequenceCache->release(resourceId);
|
|
return totalDuration;
|
|
}
|
|
|
|
bool GnapEngine::isSoundPlaying(int resourceId) {
|
|
return _soundMan->isSoundPlaying(resourceId);
|
|
}
|
|
|
|
void GnapEngine::playSound(int resourceId, bool looping) {
|
|
debugC(kDebugBasic, "playSound(%08X, %d)", resourceId, looping);
|
|
_soundMan->playSound(resourceId, looping);
|
|
}
|
|
|
|
void GnapEngine::stopSound(int resourceId) {
|
|
_soundMan->stopSound(resourceId);
|
|
}
|
|
|
|
void GnapEngine::setSoundVolume(int resourceId, int volume) {
|
|
_soundMan->setSoundVolume(resourceId, volume);
|
|
}
|
|
|
|
void GnapEngine::updateTimers() {
|
|
for (int i = 0; i < kMaxTimers; ++i)
|
|
if (_timers[i] > 0)
|
|
--_timers[i];
|
|
}
|
|
|
|
void GnapEngine::initGameFlags(int num) {
|
|
invClear();
|
|
invAdd(kItemMagazine);
|
|
switch (num) {
|
|
case 1:
|
|
setFlag(kGFPlatypusTalkingToAssistant);
|
|
break;
|
|
case 2:
|
|
clearFlags();
|
|
break;
|
|
case 3:
|
|
invAdd(kItemDiceQuarterHole);
|
|
clearFlags();
|
|
break;
|
|
case 4:
|
|
invAdd(kItemDiceQuarterHole);
|
|
invAdd(kItemHorn);
|
|
invAdd(kItemLightbulb);
|
|
clearFlags();
|
|
setFlag(kGFPlatypus);
|
|
setFlag(kGFMudTaken);
|
|
setFlag(kGFNeedleTaken);
|
|
setFlag(kGFTwigTaken);
|
|
setFlag(kGFUnk04);
|
|
setFlag(kGFKeysTaken);
|
|
setFlag(kGFGrassTaken);
|
|
setFlag(kGFBarnPadlockOpen);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void GnapEngine::loadStockDat() {
|
|
if (!_isStockDatLoaded) {
|
|
_isStockDatLoaded = true;
|
|
_dat->open(1, "stock_n.dat");
|
|
// The pre-loading of data is skipped as it's no longer required on modern hardware
|
|
}
|
|
}
|
|
|
|
void GnapEngine::mainLoop() {
|
|
_newCursorValue = 1;
|
|
_cursorValue = -1;
|
|
_newSceneNum = 0;
|
|
_currentSceneNum = 55;
|
|
_prevSceneNum = 55;
|
|
invClear();
|
|
clearFlags();
|
|
_grabCursorSpriteIndex = -1;
|
|
_grabCursorSprite = nullptr;
|
|
|
|
loadStockDat();
|
|
|
|
if (_loadGameSlot != -1) {
|
|
// Load a savegame
|
|
int slot = _loadGameSlot;
|
|
_loadGameSlot = -1;
|
|
loadGameState(slot);
|
|
_wasSavegameLoaded = true;
|
|
|
|
showCursor();
|
|
}
|
|
|
|
while (!_gameDone) {
|
|
debugC(kDebugBasic, "New scene: %d", _newSceneNum);
|
|
|
|
_prevSceneNum = _currentSceneNum;
|
|
_currentSceneNum = _newSceneNum;
|
|
|
|
debugC(kDebugBasic, "GnapEngine::mainLoop() _prevSceneNum: %d; _currentSceneNum: %d", _prevSceneNum, _currentSceneNum);
|
|
|
|
if (_newCursorValue != _cursorValue) {
|
|
debugC(kDebugBasic, "_newCursorValue: %d", _newCursorValue);
|
|
_cursorValue = _newCursorValue;
|
|
if (!_wasSavegameLoaded)
|
|
initGameFlags(_cursorValue);
|
|
}
|
|
|
|
_sceneSavegameLoaded = _wasSavegameLoaded;
|
|
_wasSavegameLoaded = false;
|
|
|
|
initScene();
|
|
|
|
runSceneLogic();
|
|
afterScene();
|
|
|
|
_soundMan->stopAll();
|
|
|
|
// Force purge all resources
|
|
_sequenceCache->purge(true);
|
|
_soundCache->purge(true);
|
|
_spriteCache->purge(true);
|
|
}
|
|
|
|
if (_backgroundSurface)
|
|
deleteSurface(&_backgroundSurface);
|
|
|
|
_dat->close(1);
|
|
}
|
|
|
|
void GnapEngine::initScene() {
|
|
Common::String datFilename;
|
|
|
|
_isLeavingScene = false;
|
|
_sceneDone = false;
|
|
_newSceneNum = 55;
|
|
_gnap->_actionStatus = -1;
|
|
_plat->_actionStatus = -1;
|
|
_gnap->initBrainPulseRndValue();
|
|
hideCursor();
|
|
clearAllKeyStatus1();
|
|
_mouseClickState._left = false;
|
|
_mouseClickState._right = false;
|
|
_sceneClickedHotspot = -1;
|
|
|
|
datFilename = Common::String::format("%s_n.dat", kSceneNames[_currentSceneNum]);
|
|
|
|
debugC(kDebugBasic, "GnapEngine::initScene() datFilename: %s", datFilename.c_str());
|
|
|
|
_dat->open(0, datFilename.c_str());
|
|
|
|
int backgroundId = initSceneLogic();
|
|
|
|
if (!_backgroundSurface) {
|
|
if (_currentSceneNum != 0)
|
|
_backgroundSurface = _gameSys->loadBitmap(makeRid(1, 0x8AA));
|
|
else
|
|
_backgroundSurface = _gameSys->loadBitmap(makeRid(0, backgroundId));
|
|
_gameSys->setBackgroundSurface(_backgroundSurface, 0, 500, 1, 1000);
|
|
}
|
|
|
|
if (_currentSceneNum != 0 && _currentSceneNum != 16 && _currentSceneNum != 47 &&
|
|
_currentSceneNum != 48 && _currentSceneNum != 54) {
|
|
_gameSys->drawBitmap(backgroundId);
|
|
}
|
|
|
|
if ((_cursorValue == 4 && isFlag(kGFGnapControlsToyUFO)) || _currentSceneNum == 41)
|
|
playSound(makeRid(1, 0x8F6), true);
|
|
|
|
}
|
|
|
|
void GnapEngine::endSceneInit() {
|
|
showCursor();
|
|
if (_newGrabCursorSpriteIndex >= 0)
|
|
setGrabCursorSprite(_newGrabCursorSpriteIndex);
|
|
}
|
|
|
|
void GnapEngine::afterScene() {
|
|
if (_gameDone)
|
|
return;
|
|
|
|
if (_newCursorValue == _cursorValue && _newSceneNum != 0 && _newSceneNum != 16 &&
|
|
_newSceneNum != 47 && _newSceneNum != 48 && _newSceneNum != 54 && _newSceneNum != 49 &&
|
|
_newSceneNum != 50 && _newSceneNum != 51 && _newSceneNum != 52)
|
|
_newGrabCursorSpriteIndex = _grabCursorSpriteIndex;
|
|
else
|
|
_newGrabCursorSpriteIndex = -1;
|
|
|
|
setGrabCursorSprite(-1);
|
|
|
|
_gameSys->requestClear2(false);
|
|
_gameSys->requestClear1();
|
|
_gameSys->waitForUpdate();
|
|
|
|
_gameSys->requestClear2(false);
|
|
_gameSys->requestClear1();
|
|
_gameSys->waitForUpdate();
|
|
|
|
screenEffect(0, 0, 0, 0);
|
|
|
|
_dat->close(0);
|
|
|
|
for (int animationIndex = 0; animationIndex < 12; ++animationIndex)
|
|
_gameSys->setAnimation(0, 0, animationIndex);
|
|
|
|
clearKeyStatus1(Common::KEYCODE_p);
|
|
|
|
_mouseClickState._left = false;
|
|
_mouseClickState._right = false;
|
|
|
|
}
|
|
|
|
void GnapEngine::checkGameKeys() {
|
|
if (isKeyStatus1(Common::KEYCODE_p)) {
|
|
clearKeyStatus1(Common::KEYCODE_p);
|
|
pauseGame();
|
|
updatePause();
|
|
}
|
|
}
|
|
|
|
void GnapEngine::startSoundTimerA(int timerIndex) {
|
|
_soundTimerIndexA = timerIndex;
|
|
_timers[timerIndex] = getRandom(50) + 100;
|
|
}
|
|
|
|
int GnapEngine::playSoundA() {
|
|
static const int kSoundIdsA[] = {
|
|
0x93E, 0x93F, 0x941, 0x942, 0x943, 0x944,
|
|
0x945, 0x946, 0x947, 0x948, 0x949
|
|
};
|
|
|
|
int soundId = -1;
|
|
|
|
if (!_timers[_soundTimerIndexA]) {
|
|
_timers[_soundTimerIndexA] = getRandom(50) + 100;
|
|
soundId = kSoundIdsA[getRandom(11)];
|
|
playSound(soundId | 0x10000, false);
|
|
}
|
|
return soundId;
|
|
}
|
|
|
|
void GnapEngine::startSoundTimerB(int timerIndex) {
|
|
_soundTimerIndexB = timerIndex;
|
|
_timers[timerIndex] = getRandom(50) + 150;
|
|
}
|
|
|
|
int GnapEngine::playSoundB() {
|
|
static const int kSoundIdsB[] = {
|
|
0x93D, 0x929, 0x92A, 0x92B, 0x92C, 0x92D,
|
|
0x92E, 0x92F, 0x930, 0x931, 0x932, 0x933,
|
|
0x934, 0x935, 0x936, 0x937, 0x938, 0x939,
|
|
0x93A
|
|
};
|
|
|
|
int soundId = -1;
|
|
|
|
if (!_timers[_soundTimerIndexB]) {
|
|
_timers[_soundTimerIndexB] = getRandom(50) + 150;
|
|
soundId = kSoundIdsB[getRandom(19)];
|
|
playSound(soundId | 0x10000, false);
|
|
}
|
|
return soundId;
|
|
}
|
|
|
|
void GnapEngine::startSoundTimerC(int timerIndex) {
|
|
_soundTimerIndexC = timerIndex;
|
|
_timers[timerIndex] = getRandom(50) + 150;
|
|
}
|
|
|
|
int GnapEngine::playSoundC() {
|
|
static const int kSoundIdsC[] = {
|
|
0x918, 0x91F, 0x920, 0x922, 0x923, 0x924,
|
|
0x926
|
|
};
|
|
|
|
int soundId = -1;
|
|
|
|
if (!_timers[_soundTimerIndexC]) {
|
|
_timers[_soundTimerIndexC] = getRandom(50) + 150;
|
|
soundId = kSoundIdsC[getRandom(7)] ;
|
|
playSound(soundId | 0x10000, false);
|
|
}
|
|
return soundId;
|
|
}
|
|
|
|
void GnapEngine::startIdleTimer(int timerIndex) {
|
|
_idleTimerIndex = timerIndex;
|
|
_timers[timerIndex] = 3000;
|
|
}
|
|
|
|
void GnapEngine::updateIdleTimer() {
|
|
if (!_timers[_idleTimerIndex]) {
|
|
_timers[_idleTimerIndex] = 3000;
|
|
_gameSys->insertSequence(0x1088B, 255, 0, 0, kSeqNone, 0, 0, 75);
|
|
}
|
|
}
|
|
|
|
void GnapEngine::screenEffect(int dir, byte r, byte g, byte b) {
|
|
int startVal = 0;
|
|
if (dir == 1)
|
|
startVal = 300;
|
|
|
|
for (int y = startVal; y < startVal + 300 && !_gameDone; y += 50) {
|
|
_gameSys->fillSurface(nullptr, 0, y, 800, 50, r, g, b);
|
|
_gameSys->fillSurface(nullptr, 0, 549 - y + 1, 800, 50, r, g, b);
|
|
gameUpdateTick();
|
|
_system->delayMillis(50);
|
|
}
|
|
}
|
|
|
|
bool GnapEngine::isKeyStatus1(int key) {
|
|
return _keyPressState[key];
|
|
}
|
|
|
|
bool GnapEngine::isKeyStatus2(int key) {
|
|
return _keyDownState[key];
|
|
}
|
|
|
|
void GnapEngine::clearKeyStatus1(int key) {
|
|
_keyPressState[key] = false;
|
|
_keyDownState[key] = false;
|
|
}
|
|
|
|
void GnapEngine::clearAllKeyStatus1() {
|
|
memset(_keyPressState, 0, sizeof(_keyPressState));
|
|
memset(_keyDownState, 0, sizeof(_keyDownState));
|
|
}
|
|
|
|
void GnapEngine::deleteSurface(Graphics::Surface **surface) {
|
|
if (surface && *surface) {
|
|
(*surface)->free();
|
|
delete *surface;
|
|
*surface = nullptr;
|
|
}
|
|
}
|
|
|
|
bool GnapEngine::testWalk(int animationIndex, int someStatus, int gridX1, int gridY1, int gridX2, int gridY2) {
|
|
if (_mouseClickState._left && someStatus == _gnap->_actionStatus) {
|
|
_isLeavingScene = false;
|
|
_gameSys->setAnimation(0, 0, animationIndex);
|
|
_gnap->_actionStatus = -1;
|
|
_plat->_actionStatus = -1;
|
|
_gnap->walkTo(Common::Point(gridX1, gridY1), -1, -1, 1);
|
|
_plat->walkTo(Common::Point(gridX2, gridY2), -1, -1, 1);
|
|
_mouseClickState._left = false;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void GnapEngine::doCallback(int callback) {
|
|
switch (callback) {
|
|
case 8:
|
|
case 10:
|
|
case 20:
|
|
_scene->updateAnimationsCb();
|
|
break;
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
void GnapEngine::initGlobalSceneVars() {
|
|
// Shared by scenes 17 && 18
|
|
_s18GarbageCanPos = 8;
|
|
|
|
// Toy UFO
|
|
_toyUfoId = 0;
|
|
_toyUfoActionStatus = -1;
|
|
_toyUfoX = 0;
|
|
_toyUfoY = 50;
|
|
}
|
|
|
|
void GnapEngine::playSequences(int fullScreenSpriteId, int sequenceId1, int sequenceId2, int sequenceId3) {
|
|
setGrabCursorSprite(-1);
|
|
_gameSys->setAnimation(sequenceId2, _gnap->_id, 0);
|
|
_gameSys->insertSequence(sequenceId2, _gnap->_id,
|
|
makeRid(_gnap->_sequenceDatNum, _gnap->_sequenceId), _gnap->_id,
|
|
kSeqSyncWait, 0, 15 * (5 * _gnap->_pos.x - 25), 48 * (_gnap->_pos.y - 8));
|
|
_gnap->_sequenceId = sequenceId2;
|
|
_gnap->_sequenceDatNum = 0;
|
|
while (_gameSys->getAnimationStatus(0) != 2 && !_gameDone)
|
|
gameUpdateTick();
|
|
hideCursor();
|
|
addFullScreenSprite(fullScreenSpriteId, 255);
|
|
_gameSys->setAnimation(sequenceId1, 256, 0);
|
|
_gameSys->insertSequence(sequenceId1, 256, 0, 0, kSeqNone, 0, 0, 0);
|
|
while (_gameSys->getAnimationStatus(0) != 2 && !_gameDone)
|
|
gameUpdateTick();
|
|
_gameSys->setAnimation(sequenceId3, _gnap->_id, 0);
|
|
_gameSys->insertSequence(sequenceId3, _gnap->_id,
|
|
makeRid(_gnap->_sequenceDatNum, _gnap->_sequenceId), _gnap->_id,
|
|
kSeqSyncWait, 0, 15 * (5 * _gnap->_pos.x - 25), 48 * (_gnap->_pos.y - 8));
|
|
removeFullScreenSprite();
|
|
showCursor();
|
|
_gnap->_sequenceId = sequenceId3;
|
|
}
|
|
|
|
void GnapEngine::toyUfoSetStatus(int flagNum) {
|
|
clearFlag(kGFUnk16);
|
|
clearFlag(kGFJointTaken);
|
|
clearFlag(kGFUnk18);
|
|
clearFlag(kGFGroceryStoreHatTaken);
|
|
setFlag(flagNum);
|
|
}
|
|
|
|
int GnapEngine::toyUfoGetSequenceId() {
|
|
if (isFlag(kGFUnk16))
|
|
return 0x84E;
|
|
if (isFlag(kGFJointTaken))
|
|
return 0x84B;
|
|
if (isFlag(kGFUnk18))
|
|
return 0x84D;
|
|
if (isFlag(kGFGroceryStoreHatTaken))
|
|
return 0x84C;
|
|
return 0x84E;
|
|
}
|
|
|
|
bool GnapEngine::toyUfoCheckTimer() {
|
|
if (!isFlag(kGFGnapControlsToyUFO) || isFlag(kGFUnk18) || _timers[9] ||
|
|
_toyUfoSequenceId == 0x870 || _toyUfoSequenceId == 0x871 || _toyUfoSequenceId == 0x872 || _toyUfoSequenceId == 0x873)
|
|
return false;
|
|
_sceneDone = true;
|
|
_newSceneNum = 41;
|
|
return true;
|
|
}
|
|
|
|
void GnapEngine::toyUfoFlyTo(int destX, int destY, int minX, int maxX, int minY, int maxY, int animationIndex) {
|
|
GridStruct flyNodes[34];
|
|
|
|
if (destX == -1)
|
|
destX = _leftClickMouseX;
|
|
|
|
if (destY == -1)
|
|
destY = _leftClickMouseY;
|
|
|
|
int clippedDestX = CLIP(destX, minX, maxX);
|
|
int clippedDestY = CLIP(destY, minY, maxY);
|
|
int dirX = 0, dirY = 0; // 0, -1 or 1
|
|
|
|
if (clippedDestX != _toyUfoX)
|
|
dirX = (clippedDestX - _toyUfoX) / ABS(clippedDestX - _toyUfoX);
|
|
|
|
if (clippedDestY != _toyUfoY)
|
|
dirY = (clippedDestY - _toyUfoY) / ABS(clippedDestY - _toyUfoY);
|
|
|
|
int deltaX = ABS(clippedDestX - _toyUfoX);
|
|
int deltaY = ABS(clippedDestY - _toyUfoY);
|
|
|
|
int i = 0;
|
|
if (deltaY > deltaX) {
|
|
int flyDirYIncr = 32;
|
|
int gridDistY = deltaY / flyDirYIncr;
|
|
int curMove = 0;
|
|
while (curMove < deltaY && i < 34) {
|
|
if (gridDistY - 5 >= i) {
|
|
flyDirYIncr = MIN(36, 8 * i + 8);
|
|
} else {
|
|
flyDirYIncr = MAX(6, flyDirYIncr - 3);
|
|
}
|
|
curMove += flyDirYIncr;
|
|
flyNodes[i]._gridX1 = _toyUfoX + dirX * deltaX * curMove / deltaY;
|
|
flyNodes[i]._gridY1 = _toyUfoY + dirY * curMove;
|
|
++i;
|
|
}
|
|
} else {
|
|
int flyDirXIncr = 36;
|
|
int gridDistX = deltaX / flyDirXIncr;
|
|
int curMove = 0;
|
|
while (curMove < deltaX && i < 34) {
|
|
if (gridDistX - 5 >= i) {
|
|
flyDirXIncr = MIN(38, 8 * i + 8);
|
|
} else {
|
|
flyDirXIncr = MAX(6, flyDirXIncr - 3);
|
|
}
|
|
curMove += flyDirXIncr;
|
|
flyNodes[i]._gridX1 = _toyUfoX + dirX * curMove;
|
|
flyNodes[i]._gridY1 = _toyUfoY + dirY * deltaY * curMove / deltaX;
|
|
++i;
|
|
}
|
|
}
|
|
|
|
int nodesCount = i - 1;
|
|
|
|
_toyUfoX = clippedDestX;
|
|
_toyUfoY = clippedDestY;
|
|
|
|
if (nodesCount > 0) {
|
|
int seqId = 0;
|
|
if (isFlag(kGFUnk16))
|
|
seqId = 0x867;
|
|
else if (isFlag(kGFJointTaken))
|
|
seqId = 0x84F;
|
|
else if (isFlag(kGFUnk18))
|
|
seqId = 0x85F;
|
|
else if (isFlag(kGFGroceryStoreHatTaken))
|
|
seqId = 0x857;
|
|
else
|
|
error("Unhandled flag in GnapEngine::toyUfoFlyTo(): 0x%x", _gameFlags);
|
|
flyNodes[0]._sequenceId = seqId;
|
|
flyNodes[0]._id = 0;
|
|
_gameSys->insertSequence(seqId | 0x10000, 0,
|
|
_toyUfoSequenceId | 0x10000, _toyUfoId,
|
|
kSeqSyncWait, 0, flyNodes[0]._gridX1 - 365, flyNodes[0]._gridY1 - 128);
|
|
for (i = 1; i < nodesCount; ++i) {
|
|
flyNodes[i]._sequenceId = seqId + (i % 8);
|
|
flyNodes[i]._id = i;
|
|
_gameSys->insertSequence(flyNodes[i]._sequenceId | 0x10000, flyNodes[i]._id,
|
|
flyNodes[i - 1]._sequenceId | 0x10000, flyNodes[i - 1]._id,
|
|
kSeqSyncWait, 0,
|
|
flyNodes[i]._gridX1 - 365, flyNodes[i]._gridY1 - 128);
|
|
}
|
|
|
|
_toyUfoSequenceId = flyNodes[nodesCount - 1]._sequenceId;
|
|
_toyUfoId = flyNodes[nodesCount - 1]._id;
|
|
|
|
if (animationIndex >= 0)
|
|
_gameSys->setAnimation(_toyUfoSequenceId | 0x10000, _toyUfoId, animationIndex);
|
|
|
|
}
|
|
}
|
|
|
|
void GnapEngine::playMidi(const char *name) {
|
|
if (_music)
|
|
return;
|
|
|
|
_music = new MusicPlayer(name);
|
|
_music->playSMF(true);
|
|
}
|
|
|
|
void GnapEngine::stopMidi() {
|
|
if (_music) {
|
|
_music->stop();
|
|
delete _music;
|
|
_music = nullptr;
|
|
}
|
|
}
|
|
} // End of namespace Gnap
|