/* 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. * */ /* * This code is based on original Tony Tough source code * * Copyright (c) 1997-2003 Nayma Software */ #include "common/savefile.h" #include "tony/mpal/lzo.h" #include "tony/mpal/mpalutils.h" #include "tony/custom.h" #include "tony/gfxengine.h" #include "tony/tony.h" namespace Tony { /****************************************************************************\ * RMGfxEngine Methods \****************************************************************************/ void ExitAllIdles(CORO_PARAM, const void *param) { CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); int nCurLoc = *(const int *)param; CORO_BEGIN_CODE(_ctx); // Closes idle GLOBALS.bSkipSfxNoLoop = true; CORO_INVOKE_2(mpalEndIdlePoll, nCurLoc, NULL); GLOBALS.bIdleExited = true; GLOBALS.bSkipSfxNoLoop = false; CORO_END_CODE; } RMGfxEngine::RMGfxEngine() { // Create big buffer where the frame will be rendered _bigBuf.create(RM_BBX, RM_BBY, 16); _bigBuf.offsetY(RM_SKIPY); _csMainLoop = NULL; _nCurLoc = 0; _curAction = TA_GOTO; _curActionObj = 0; _nWipeType = 0; _hWipeEvent = 0; _nWipeStep = 0; _bMustEnterMenu = false; _bWiping = false; _bGUIOption = false; _bGUIInterface = false; _bGUIInventory = false; _bAlwaysDrawMouse = false; _bOption = false; _bLocationLoaded = false; _bInput = false; } RMGfxEngine::~RMGfxEngine() { // Close the buffer _bigBuf.destroy(); g_system->deleteMutex(_csMainLoop); } void RMGfxEngine::openOptionScreen(CORO_PARAM, int type) { CORO_BEGIN_CONTEXT; bool bRes; CORO_END_CONTEXT(_ctx); CORO_BEGIN_CODE(_ctx); _ctx->bRes = false; if (type == 0) CORO_INVOKE_2(_opt.Init, _bigBuf, _ctx->bRes); else if (type == 1) CORO_INVOKE_3(_opt.InitLoadMenuOnly, _bigBuf, true, _ctx->bRes); else if (type == 2) CORO_INVOKE_2(_opt.InitNoLoadSave, _bigBuf, _ctx->bRes); else if (type == 3) CORO_INVOKE_3(_opt.InitLoadMenuOnly, _bigBuf, false, _ctx->bRes); else if (type == 4) CORO_INVOKE_3(_opt.InitSaveMenuOnly, _bigBuf, false, _ctx->bRes); if (_ctx->bRes) { _vm->pauseSound(true); disableInput(); _inv.EndCombine(); _curActionObj = 0; _curAction = TA_GOTO; _point.SetAction(_curAction); _point.SetSpecialPointer(RMPointer::PTR_NONE); _point.SetCustomPointer(NULL); enableMouse(); _vm->grabThumbnail(); // Exists the IDLE to avoid premature death in loading _bMustEnterMenu = true; if (type == 1 || type == 2) { GLOBALS.bIdleExited = true; } else { CORO_INVOKE_0(_tony.StopNoAction); GLOBALS.bIdleExited = false; CoroScheduler.createProcess(ExitAllIdles, &_nCurLoc, sizeof(int)); } } CORO_END_CODE; } void RMGfxEngine::doFrame(CORO_PARAM, bool bDrawLocation) { CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); CORO_BEGIN_CODE(_ctx); g_system->lockMutex(_csMainLoop); // Poll of input devices _input.poll(); if (_bMustEnterMenu && GLOBALS.bIdleExited) { _bOption = true; _bMustEnterMenu = false; GLOBALS.bIdleExited = false; } if (_bOption) { CORO_INVOKE_1(_opt.DoFrame, &_input); _bOption = !_opt.IsClosing(); if (!_bOption) { disableMouse(); enableInput(); mpalStartIdlePoll(_nCurLoc); _vm->pauseSound(false); } } if (bDrawLocation && _bLocationLoaded) { // Location and objects _loc.DoFrame(&_bigBuf); // Check the mouse input if (_bInput && !_tony.InAction()) { // If we are on the inventory, it is it who controls all input if (_inv.HaveFocus(_input.mousePos()) && !_inter.Active()) { // Left Click // ********** if (_input.mouseLeftClicked()/* && m_itemName.IsItemSelected()*/) { // Left click activates the combine, if we are on an object if (_inv.LeftClick(_input.mousePos(), _curActionObj)) { _curAction = TA_COMBINE; _point.SetAction(_curAction); } } else // Right Click // *********** if (_input.mouseRightClicked()) { if (_itemName.IsItemSelected()) { _curActionObj = 0; _inv.RightClick(_input.mousePos()); } else _inv.RightClick(_input.mousePos()); } else // Right Release // ************* if (_input.mouseRightReleased()) { if (_inv.RightRelease(_input.mousePos(), _curAction)) { CORO_INVOKE_3(_tony.MoveAndDoAction, _itemName.GetHotspot(), _itemName.GetSelectedItem(), _curAction); _curAction = TA_GOTO; _point.SetAction(_curAction); } } } else { // Options Menu // ************ if (_bGUIOption) { if (!_tony.InAction() && _bInput) { if ((_input.mouseLeftClicked() && _input.mousePos().x < 3 && _input.mousePos().y < 3)) { CORO_INVOKE_1(openOptionScreen, 0); goto SKIPCLICKSINISTRO; } else if (_input.getAsyncKeyState(Common::KEYCODE_ESCAPE)) CORO_INVOKE_1(openOptionScreen, 0); else if (!_vm->getIsDemo()) { if (_input.getAsyncKeyState(Common::KEYCODE_F3) || _input.getAsyncKeyState(Common::KEYCODE_F5)) // Save game screen CORO_INVOKE_1(openOptionScreen, 3); else if (_input.getAsyncKeyState(Common::KEYCODE_F2) || _input.getAsyncKeyState(Common::KEYCODE_F7)) // Load game screen CORO_INVOKE_1(openOptionScreen, 4); } } } // Left Click // ************** if (_input.mouseLeftClicked() && !_inter.Active()) { if (_curAction != TA_COMBINE) CORO_INVOKE_3(_tony.MoveAndDoAction, _itemName.GetHotspot(), _itemName.GetSelectedItem(), _point.CurAction()); else if (_itemName.GetSelectedItem() != NULL) CORO_INVOKE_4(_tony.MoveAndDoAction, _itemName.GetHotspot(), _itemName.GetSelectedItem(), TA_COMBINE, _curActionObj); if (_curAction == TA_COMBINE) { _inv.EndCombine(); _point.SetSpecialPointer(RMPointer::PTR_NONE); } _curAction = TA_GOTO; _point.SetAction(_curAction); } SKIPCLICKSINISTRO: // Right Click // ************ if (_curAction == TA_COMBINE) { // During a combine, it cancels it if (_input.mouseRightClicked()) { _inv.EndCombine(); _curActionObj = 0; _curAction = TA_GOTO; _point.SetAction(_curAction); _point.SetSpecialPointer(RMPointer::PTR_NONE); } } else if (_input.mouseRightClicked() && _itemName.IsItemSelected() && _point.GetSpecialPointer() == RMPointer::PTR_NONE) { if (_bGUIInterface) { // Before opening the interface, replaces GOTO _curAction = TA_GOTO; _curActionObj = 0; _point.SetAction(_curAction); _inter.Clicked(_input.mousePos()); } } // Right Release // ************* if (_input.mouseRightReleased()) { if (_bGUIInterface) { if (_inter.Released(_input.mousePos(), _curAction)) { _point.SetAction(_curAction); CORO_INVOKE_3(_tony.MoveAndDoAction, _itemName.GetHotspot(), _itemName.GetSelectedItem(), _curAction); _curAction = TA_GOTO; _point.SetAction(_curAction); } } } } // Update the name under the mouse pointer _itemName.SetMouseCoord(_input.mousePos()); if (!_inter.Active() && !_inv.MiniActive()) CORO_INVOKE_4(_itemName.DoFrame, _bigBuf, _loc, _point, _inv); } // Interface & Inventory _inter.DoFrame(_bigBuf, _input.mousePos()); _inv.DoFrame(_bigBuf, _point, _input.mousePos(), (!_tony.InAction() && !_inter.Active() && _bGUIInventory)); } // Animate Tony CORO_INVOKE_2(_tony.DoFrame, &_bigBuf, _nCurLoc); // Update screen scrolling to keep Tony in focus if (_tony.MustUpdateScrolling() && _bLocationLoaded) { RMPoint showThis = _tony.Position(); showThis.y -= 60; _loc.UpdateScrolling(showThis); } if (_bLocationLoaded) _tony.SetScrollPosition(_loc.ScrollPosition()); if ((!_tony.InAction() && _bInput) || _bAlwaysDrawMouse) { _point.SetCoord(_input.mousePos()); _point.DoFrame(&_bigBuf); } // ********************** // Draw the list in the OT // ********************** CORO_INVOKE_0(_bigBuf.drawOT); #define FSTEP (480/32) // Wipe if (_bWiping) { switch (_nWipeType) { case 1: if (!(_rcWipeEllipse.bottom - _rcWipeEllipse.top >= FSTEP * 2)) { CoroScheduler.setEvent(_hWipeEvent); _nWipeType = 3; break; } _rcWipeEllipse.top += FSTEP; _rcWipeEllipse.left += FSTEP; _rcWipeEllipse.right -= FSTEP; _rcWipeEllipse.bottom -= FSTEP; break; case 2: if (!(_rcWipeEllipse.bottom - _rcWipeEllipse.top < 480 - FSTEP)) { CoroScheduler.setEvent(_hWipeEvent); _nWipeType = 3; break; } _rcWipeEllipse.top -= FSTEP; _rcWipeEllipse.left -= FSTEP; _rcWipeEllipse.right += FSTEP; _rcWipeEllipse.bottom += FSTEP; break; } } g_system->unlockMutex(_csMainLoop); CORO_END_CODE; } void RMGfxEngine::initCustomDll(void) { setupGlobalVars(&_tony, &_point, &_vm->_theBoxes, &_loc, &_inv, &_input); } void RMGfxEngine::itemIrq(uint32 dwItem, int nPattern, int nStatus) { RMItem *item; assert(GLOBALS.GfxEngine); if (GLOBALS.GfxEngine->_bLocationLoaded) { item = GLOBALS.GfxEngine->_loc.GetItemFromCode(dwItem); if (item != NULL) { if (nPattern != -1) { if (GLOBALS.bPatIrqFreeze) mainFreeze(); item->SetPattern(nPattern, true); if (GLOBALS.bPatIrqFreeze) mainUnfreeze(); } if (nStatus != -1) item->SetStatus(nStatus); } } } void RMGfxEngine::initForNewLocation(int nLoc, RMPoint ptTonyStart, RMPoint start) { if (start.x == -1 || start.y == -1) { start.x = ptTonyStart.x - RM_SX / 2; start.y = ptTonyStart.y - RM_SY / 2; } _loc.SetScrollPosition(start); if (ptTonyStart.x == 0 && ptTonyStart.y == 0) { } else { _tony.SetPosition(ptTonyStart, nLoc); _tony.SetScrollPosition(start); } _curAction = TA_GOTO; _point.SetCustomPointer(NULL); _point.SetSpecialPointer(RMPointer::PTR_NONE); _point.SetAction(_curAction); _inter.Reset(); _inv.Reset(); mpalStartIdlePoll(_nCurLoc); } uint32 RMGfxEngine::loadLocation(int nLoc, RMPoint ptTonyStart, RMPoint start) { bool bLoaded; int i; _nCurLoc = nLoc; bLoaded = false; for (i = 0; i < 5; i++) { // Try the loading of the location RMRes res(_nCurLoc); if (!res.IsValid()) continue; _loc.Load(res); initForNewLocation(nLoc, ptTonyStart, start); bLoaded = true; break; } if (!bLoaded) error("Location was not loaded"); if (_bOption) _opt.ReInit(_bigBuf); _bLocationLoaded = true; // On entering the location return CORO_INVALID_PID_VALUE; //mpalQueryDoAction(0,m_nCurLoc,0); } void RMGfxEngine::unloadLocation(CORO_PARAM, bool bDoOnExit, uint32 *result) { CORO_BEGIN_CONTEXT; uint32 h; CORO_END_CONTEXT(_ctx); CORO_BEGIN_CODE(_ctx); // Release the location CORO_INVOKE_2(mpalEndIdlePoll, _nCurLoc, NULL); // On Exit? if (bDoOnExit) { _ctx->h = mpalQueryDoAction(1, _nCurLoc, 0); if (_ctx->h != CORO_INVALID_PID_VALUE) CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _ctx->h, CORO_INFINITE); } mainFreeze(); _bLocationLoaded = false; _bigBuf.clearOT(); _loc.Unload(); if (result != NULL) *result = CORO_INVALID_PID_VALUE; CORO_END_CODE; } void RMGfxEngine::init() { // Screen loading RMResRaw *raw; RMGfxSourceBuffer16 *load = NULL; INIT_GFX16_FROMRAW(20038, load); _bigBuf.addPrim(new RMGfxPrimitive(load)); _bigBuf.drawOT(Common::nullContext); _bigBuf.clearOT(); delete load; _vm->_window.GetNewFrame(*this, NULL); GLOBALS.bPatIrqFreeze = true; // Activate GUI _bGUIOption = true; _bGUIInterface = true; _bGUIInventory = true; GLOBALS.bSkipSfxNoLoop = false; _bMustEnterMenu = false; GLOBALS.bIdleExited = false; _bOption = false; _bWiping = false; _hWipeEvent = CoroScheduler.createEvent(false, false); // Create the freeze event _csMainLoop = g_system->createMutex(); // Initialise the IRQ function for items for MPAL GLOBALS.GfxEngine = this; mpalInstallItemIrq(itemIrq); // Initialise the input _input.init(); // Initialise the mouse pointer _point.Init(); // Initialise Tony _tony.init(); _tony.LinkToBoxes(&_vm->_theBoxes); // Initialise the inventory and the interface _inv.Init(); _inter.Init(); // Download the location and set priorities @@@@@ _bLocationLoaded = false; enableInput(); // Starting the game _tony.ExecuteAction(20, 1, 0); } void RMGfxEngine::close(void) { _bigBuf.clearOT(); _inter.Close(); _inv.Close(); _tony.Close(); _point.Close(); _input.close(); } void RMGfxEngine::switchFullscreen(bool bFull) { } void RMGfxEngine::GDIControl(bool bCon) { } void RMGfxEngine::enableInput(void) { _bInput = true; } void RMGfxEngine::disableInput(void) { _bInput = false; _inter.Reset(); } void RMGfxEngine::enableMouse(void) { _bAlwaysDrawMouse = true; } void RMGfxEngine::disableMouse(void) { _bAlwaysDrawMouse = false; } void RMGfxEngine::freeze(void) { g_system->lockMutex(_csMainLoop); } void RMGfxEngine::unfreeze(void) { g_system->unlockMutex(_csMainLoop); } void CharsSaveAll(Common::OutSaveFile *f); void CharsLoadAll(Common::InSaveFile *f); void MCharResetCodes(void); void SaveChangedHotspot(Common::OutSaveFile *f); void LoadChangedHotspot(Common::InSaveFile *f); void ReapplyChangedHotspot(void); void RestoreMusic(CORO_PARAM); void SaveMusic(Common::OutSaveFile *f); void LoadMusic(Common::InSaveFile *f); #define TONY_SAVEGAME_VERSION 8 void RMGfxEngine::saveState(const Common::String &fn, byte *curThumb, const Common::String &name) { Common::OutSaveFile *f; byte *state; uint thumbsize; uint size; int i; char buf[4]; RMPoint tp = _tony.Position(); // Saving: MPAL variables, current location, and Tony inventory position // For now, we only save the MPAL state size = mpalGetSaveStateSize(); state = new byte[size]; mpalSaveState(state); thumbsize = 160 * 120 * 2; buf[0] = 'R'; buf[1] = 'M'; buf[2] = 'S'; buf[3] = TONY_SAVEGAME_VERSION; f = g_system->getSavefileManager()->openForSaving(fn); if (f == NULL) return; f->write(buf, 4); f->writeUint32LE(thumbsize); f->write(curThumb, thumbsize); // Difficulty level i = mpalQueryGlobalVar("VERSIONEFACILE"); f->writeByte(i); i = strlen(name.c_str()); f->writeByte(i); f->write(name.c_str(), i); f->writeUint32LE(_nCurLoc); f->writeUint32LE(tp.x); f->writeUint32LE(tp.y); f->writeUint32LE(size); f->write(state, size); delete[] state; // Inventory size = _inv.GetSaveStateSize(); state = new byte[size]; _inv.SaveState(state); f->writeUint32LE(size); f->write(state, size); delete[] state; // boxes size = _vm->_theBoxes.GetSaveStateSize(); state = new byte[size]; _vm->_theBoxes.SaveState(state); f->writeUint32LE(size); f->write(state, size); delete[] state; // New Ver5 bool bStat; // Saves the state of the shepherdess and show yourself bStat = _tony.GetPastorella(); f->writeByte(bStat); bStat = _inter.GetPalesati(); f->writeByte(bStat); // Save the chars CharsSaveAll(f); // Save the options f->writeByte(GLOBALS.bCfgInvLocked); f->writeByte(GLOBALS.bCfgInvNoScroll); f->writeByte(GLOBALS.bCfgTimerizedText); f->writeByte(GLOBALS.bCfgInvUp); f->writeByte(GLOBALS.bCfgAnni30); f->writeByte(GLOBALS.bCfgAntiAlias); f->writeByte(GLOBALS.bCfgSottotitoli); f->writeByte(GLOBALS.bCfgTransparence); f->writeByte(GLOBALS.bCfgInterTips); f->writeByte(GLOBALS.bCfgDubbing); f->writeByte(GLOBALS.bCfgMusic); f->writeByte(GLOBALS.bCfgSFX); f->writeByte(GLOBALS.nCfgTonySpeed); f->writeByte(GLOBALS.nCfgTextSpeed); f->writeByte(GLOBALS.nCfgDubbingVolume); f->writeByte(GLOBALS.nCfgMusicVolume); f->writeByte(GLOBALS.nCfgSFXVolume); // Save the hotspots SaveChangedHotspot(f); // Save the music SaveMusic(f); f->finalize(); delete f; } void RMGfxEngine::loadState(CORO_PARAM, const Common::String &fn) { // PROBLEM: You should change the location in a separate process to do the OnEnter CORO_BEGIN_CONTEXT; Common::InSaveFile *f; byte *state, *statecmp; uint size, sizecmp; char buf[4]; RMPoint tp; int loc; int ver; int i; CORO_END_CONTEXT(_ctx); CORO_BEGIN_CODE(_ctx); _ctx->f = g_system->getSavefileManager()->openForLoading(fn); if (_ctx->f == NULL) return; _ctx->f->read(_ctx->buf, 4); if (_ctx->buf[0] != 'R' || _ctx->buf[1] != 'M' || _ctx->buf[2] != 'S') { delete _ctx->f; return; } _ctx->ver = _ctx->buf[3]; if (_ctx->ver == 0 || _ctx->ver > TONY_SAVEGAME_VERSION) { delete _ctx->f; return; } if (_ctx->ver >= 0x3) { // There is a thumbnail. If the version is between 5 and 7, it's compressed if ((_ctx->ver >= 0x5) && (_ctx->ver <= 0x7)) { _ctx->i = 0; _ctx->i = _ctx->f->readUint32LE(); _ctx->f->seek(_ctx->i); } else { if (_ctx->ver >= 8) // Skip thumbnail size _ctx->f->skip(4); _ctx->f->seek(160 * 120 * 2, SEEK_CUR); } } if (_ctx->ver >= 0x5) { // Skip the difficulty level _ctx->f->seek(1, SEEK_CUR); } if (_ctx->ver >= 0x4) { // Skip the savegame name, which serves no purpose _ctx->i = _ctx->f->readByte(); _ctx->f->seek(_ctx->i, SEEK_CUR); } _ctx->loc = _ctx->f->readUint32LE(); _ctx->tp.x = _ctx->f->readUint32LE(); _ctx->tp.y = _ctx->f->readUint32LE(); _ctx->size = _ctx->f->readUint32LE(); if ((_ctx->ver >= 0x5) && (_ctx->ver <= 7)) { // MPAL was packed! _ctx->sizecmp = _ctx->f->readUint32LE(); _ctx->state = new byte[_ctx->size]; _ctx->statecmp = new byte[_ctx->sizecmp]; _ctx->f->read(_ctx->statecmp, _ctx->sizecmp); lzo1x_decompress(_ctx->statecmp, _ctx->sizecmp, _ctx->state, &_ctx->size); delete[] _ctx->statecmp; } else { // Read uncompressed MPAL data _ctx->state = new byte[_ctx->size]; _ctx->f->read(_ctx->state, _ctx->size); } mpalLoadState(_ctx->state); delete[] _ctx->state; // Inventory _ctx->size = _ctx->f->readUint32LE(); _ctx->state = new byte[_ctx->size]; _ctx->f->read(_ctx->state, _ctx->size); _inv.LoadState(_ctx->state); delete[] _ctx->state; if (_ctx->ver >= 0x2) { // Versione 2: box please _ctx->size = _ctx->f->readUint32LE(); _ctx->state = new byte[_ctx->size]; _ctx->f->read(_ctx->state, _ctx->size); _vm->_theBoxes.LoadState(_ctx->state); delete[] _ctx->state; } if (_ctx->ver >= 5) { // Versione 5 bool bStat = false; bStat = _ctx->f->readByte(); _tony.SetPastorella(bStat); bStat = _ctx->f->readByte(); _inter.SetPalesati(bStat); CharsLoadAll(_ctx->f); } if (_ctx->ver >= 6) { // Load options GLOBALS.bCfgInvLocked = _ctx->f->readByte(); GLOBALS.bCfgInvNoScroll = _ctx->f->readByte(); GLOBALS.bCfgTimerizedText = _ctx->f->readByte(); GLOBALS.bCfgInvUp = _ctx->f->readByte(); GLOBALS.bCfgAnni30 = _ctx->f->readByte(); GLOBALS.bCfgAntiAlias = _ctx->f->readByte(); GLOBALS.bCfgSottotitoli = _ctx->f->readByte(); GLOBALS.bCfgTransparence = _ctx->f->readByte(); GLOBALS.bCfgInterTips = _ctx->f->readByte(); GLOBALS.bCfgDubbing = _ctx->f->readByte(); GLOBALS.bCfgMusic = _ctx->f->readByte(); GLOBALS.bCfgSFX = _ctx->f->readByte(); GLOBALS.nCfgTonySpeed = _ctx->f->readByte(); GLOBALS.nCfgTextSpeed = _ctx->f->readByte(); GLOBALS.nCfgDubbingVolume = _ctx->f->readByte(); GLOBALS.nCfgMusicVolume = _ctx->f->readByte(); GLOBALS.nCfgSFXVolume = _ctx->f->readByte(); // Load hotspots LoadChangedHotspot(_ctx->f); } if (_ctx->ver >= 7) { LoadMusic(_ctx->f); } delete _ctx->f; CORO_INVOKE_2(unloadLocation, false, NULL); loadLocation(_ctx->loc, _ctx->tp, RMPoint(-1, -1)); _tony.SetPattern(RMTony::PAT_STANDRIGHT); mainUnfreeze(); // On older versions, need to an enter action if (_ctx->ver < 5) mpalQueryDoAction(0, _ctx->loc, 0); else { // In the new ones, we just reset the mcode MCharResetCodes(); } if (_ctx->ver >= 6) ReapplyChangedHotspot(); CORO_INVOKE_0(RestoreMusic); _bGUIInterface = true; _bGUIInventory = true; _bGUIOption = true; CORO_END_CODE; } void RMGfxEngine::pauseSound(bool bPause) { if (_bLocationLoaded) _loc.PauseSound(bPause); } void RMGfxEngine::initWipe(int type) { _bWiping = true; _nWipeType = type; _nWipeStep = 0; if (_nWipeType == 1) _rcWipeEllipse = Common::Rect(80, 0, 640 - 80, 480); else if (_nWipeType == 2) _rcWipeEllipse = Common::Rect(320 - FSTEP, 240 - FSTEP, 320 + FSTEP, 240 + FSTEP); } void RMGfxEngine::closeWipe(void) { _bWiping = false; } void RMGfxEngine::waitWipeEnd(CORO_PARAM) { CoroScheduler.waitForSingleObject(coroParam, _hWipeEvent, CORO_INFINITE); } bool RMGfxEngine::canLoadSave() { return _bInput && !_tony.InAction() && !_vm->getIsDemo(); } } // End of namespace Tony