mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-24 18:56:33 +00:00
2250 lines
49 KiB
C++
2250 lines
49 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.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* This code is based on original Tony Tough source code
|
|
*
|
|
* Copyright (c) 1997-2003 Nayma Software
|
|
*/
|
|
|
|
#include "common/memstream.h"
|
|
#include "common/scummsys.h"
|
|
#include "tony/mpal/mpalutils.h"
|
|
#include "tony/game.h"
|
|
#include "tony/loc.h"
|
|
#include "tony/tony.h"
|
|
|
|
namespace Tony {
|
|
|
|
using namespace ::Tony::MPAL;
|
|
|
|
/****************************************************************************\
|
|
* RMPalette Methods
|
|
\****************************************************************************/
|
|
|
|
void RMPalette::readFromStream(Common::ReadStream &ds) {
|
|
ds.read(_data, 1024);
|
|
}
|
|
|
|
/****************************************************************************\
|
|
* RMSlot Methods
|
|
\****************************************************************************/
|
|
|
|
void RMPattern::RMSlot::readFromStream(Common::ReadStream &ds, bool bLOX) {
|
|
// Type
|
|
byte type = ds.readByte();
|
|
_type = (RMPattern::RMSlotType)type;
|
|
|
|
// Data
|
|
_data = ds.readSint32LE();
|
|
|
|
// Position
|
|
_pos.readFromStream(ds);
|
|
|
|
// Generic flag
|
|
_flag = ds.readByte();
|
|
}
|
|
|
|
/****************************************************************************\
|
|
* RMPattern Methods
|
|
\****************************************************************************/
|
|
|
|
void RMPattern::readFromStream(Common::ReadStream &ds, bool bLOX) {
|
|
// Pattern name
|
|
if (!bLOX)
|
|
_name = readString(ds);
|
|
|
|
// Velocity
|
|
_speed = ds.readSint32LE();
|
|
|
|
// Position
|
|
_pos.readFromStream(ds);
|
|
|
|
// Flag for pattern looping
|
|
_bLoop = ds.readSint32LE();
|
|
|
|
// Number of slots
|
|
_nSlots = ds.readSint32LE();
|
|
|
|
// Create and read the slots
|
|
_slots = new RMSlot[_nSlots];
|
|
|
|
for (int i = 0; i < _nSlots && !ds.err(); i++) {
|
|
if (bLOX)
|
|
_slots[i].readFromStream(ds, true);
|
|
else
|
|
_slots[i].readFromStream(ds, false);
|
|
}
|
|
}
|
|
|
|
void RMPattern::updateCoord() {
|
|
_curPos = _pos + _slots[_nCurSlot].pos();
|
|
}
|
|
|
|
void RMPattern::stopSfx(RMSfx *sfx) {
|
|
for (int i = 0; i < _nSlots; i++) {
|
|
if (_slots[i]._type == SOUND) {
|
|
if (!sfx[_slots[i]._data]._name.empty() && sfx[_slots[i]._data]._name[0] == '_')
|
|
sfx[_slots[i]._data].stop();
|
|
else if (GLOBALS._bSkipSfxNoLoop)
|
|
sfx[_slots[i]._data].stop();
|
|
}
|
|
}
|
|
}
|
|
|
|
int RMPattern::init(RMSfx *sfx, bool bPlayP0, byte *bFlag) {
|
|
// Read the current time
|
|
_nStartTime = g_vm->getTime();
|
|
_nCurSlot = 0;
|
|
|
|
// Find the first frame of the pattern
|
|
int i = 0;
|
|
while (_slots[i]._type != SPRITE) {
|
|
assert(i + 1 < _nSlots);
|
|
i++;
|
|
}
|
|
|
|
_nCurSlot = i;
|
|
_nCurSprite = _slots[i]._data;
|
|
if (bFlag)
|
|
*bFlag = _slots[i]._flag;
|
|
|
|
// Calculate the current coordinates
|
|
updateCoord();
|
|
|
|
// Check for sound:
|
|
// If the slot is 0, play
|
|
// If speed == 0, must play unless it goes into loop '_', or if specified by the parameter
|
|
// If speed != 0, play only the loop
|
|
for (i = 0; i < _nSlots; i++) {
|
|
if (_slots[i]._type == SOUND) {
|
|
if (i == 0) {
|
|
if (!sfx[_slots[i]._data]._name.empty() && sfx[_slots[i]._data]._name[0] == '_') {
|
|
sfx[_slots[i]._data].setVolume(_slots[i].pos()._x);
|
|
sfx[_slots[i]._data].play(true);
|
|
} else {
|
|
sfx[_slots[i]._data].setVolume(_slots[i].pos()._x);
|
|
sfx[_slots[i]._data].play();
|
|
}
|
|
} else if (_speed == 0) {
|
|
if (bPlayP0) {
|
|
sfx[_slots[i]._data].setVolume(_slots[i].pos()._x);
|
|
sfx[_slots[i]._data].play();
|
|
} else if (!sfx[_slots[i]._data]._name.empty() && sfx[_slots[i]._data]._name[0] == '_') {
|
|
sfx[_slots[i]._data].setVolume(_slots[i].pos()._x);
|
|
sfx[_slots[i]._data].play(true);
|
|
}
|
|
} else {
|
|
if (_bLoop && !sfx[_slots[i]._data]._name.empty() && sfx[_slots[i]._data]._name[0] == '_') {
|
|
sfx[_slots[i]._data].setVolume(_slots[i].pos()._x);
|
|
sfx[_slots[i]._data].play(true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return _nCurSprite;
|
|
}
|
|
|
|
int RMPattern::update(uint32 hEndPattern, byte &bFlag, RMSfx *sfx) {
|
|
int CurTime = g_vm->getTime();
|
|
|
|
// If the speed is 0, then the pattern never advances
|
|
if (_speed == 0) {
|
|
CoroScheduler.pulseEvent(hEndPattern);
|
|
bFlag = _slots[_nCurSlot]._flag;
|
|
return _nCurSprite;
|
|
}
|
|
|
|
// Is it time to change the slots?
|
|
while (_nStartTime + _speed <= (uint32)CurTime) {
|
|
_nStartTime += _speed;
|
|
if (_slots[_nCurSlot]._type == SPRITE)
|
|
_nCurSlot++;
|
|
if (_nCurSlot == _nSlots) {
|
|
_nCurSlot = 0;
|
|
bFlag = _slots[_nCurSlot]._flag;
|
|
|
|
CoroScheduler.pulseEvent(hEndPattern);
|
|
|
|
// @@@ If there is no loop pattern, and there's a warning that it's the final
|
|
// frame, then remain on the last frame
|
|
if (!_bLoop) {
|
|
_nCurSlot = _nSlots - 1;
|
|
bFlag = _slots[_nCurSlot]._flag;
|
|
return _nCurSprite;
|
|
}
|
|
}
|
|
|
|
for (;;) {
|
|
switch (_slots[_nCurSlot]._type) {
|
|
case SPRITE:
|
|
// Read the next sprite
|
|
_nCurSprite = _slots[_nCurSlot]._data;
|
|
|
|
// Update the parent & child coordinates
|
|
updateCoord();
|
|
break;
|
|
|
|
case SOUND:
|
|
if (sfx != NULL) {
|
|
sfx[_slots[_nCurSlot]._data].setVolume(_slots[_nCurSlot].pos()._x);
|
|
|
|
if (sfx[_slots[_nCurSlot]._data]._name.empty() || sfx[_slots[_nCurSlot]._data]._name[0] != '_')
|
|
sfx[_slots[_nCurSlot]._data].play(false);
|
|
else
|
|
sfx[_slots[_nCurSlot]._data].play(true);
|
|
}
|
|
break;
|
|
|
|
case COMMAND:
|
|
assert(0);
|
|
break;
|
|
|
|
default:
|
|
assert(0);
|
|
break;
|
|
}
|
|
|
|
if (_slots[_nCurSlot]._type == SPRITE)
|
|
break;
|
|
_nCurSlot++;
|
|
}
|
|
}
|
|
|
|
// Return the current sprite
|
|
bFlag = _slots[_nCurSlot]._flag;
|
|
return _nCurSprite;
|
|
}
|
|
|
|
RMPattern::RMPattern() {
|
|
_slots = NULL;
|
|
_speed = 0;
|
|
_bLoop = 0;
|
|
_nSlots = 0;
|
|
_nCurSlot = 0;
|
|
_nCurSprite = 0;
|
|
_nStartTime = 0;
|
|
_slots = NULL;
|
|
}
|
|
|
|
/**
|
|
* Reads the position of the pattern
|
|
*/
|
|
RMPoint RMPattern::pos() {
|
|
return _curPos;
|
|
}
|
|
|
|
RMPattern::~RMPattern() {
|
|
if (_slots != NULL) {
|
|
delete[] _slots;
|
|
_slots = NULL;
|
|
}
|
|
}
|
|
|
|
/****************************************************************************\
|
|
* RMSprite Methods
|
|
\****************************************************************************/
|
|
|
|
void RMSprite::init(RMGfxSourceBuffer *buf) {
|
|
_buf = buf;
|
|
}
|
|
|
|
void RMSprite::LOXGetSizeFromStream(Common::SeekableReadStream &ds, int *dimx, int *dimy) {
|
|
uint32 pos = ds.pos();
|
|
|
|
*dimx = ds.readSint32LE();
|
|
*dimy = ds.readSint32LE();
|
|
|
|
ds.seek(pos);
|
|
}
|
|
|
|
void RMSprite::getSizeFromStream(Common::SeekableReadStream &ds, int *dimx, int *dimy) {
|
|
uint32 pos = ds.pos();
|
|
|
|
_name = readString(ds);
|
|
*dimx = ds.readSint32LE();
|
|
*dimy = ds.readSint32LE();
|
|
|
|
ds.seek(pos);
|
|
}
|
|
|
|
void RMSprite::readFromStream(Common::SeekableReadStream &ds, bool bLOX) {
|
|
// Sprite name
|
|
if (!bLOX)
|
|
_name = readString(ds);
|
|
|
|
// Dimensions
|
|
int dimx = ds.readSint32LE();
|
|
int dimy = ds.readSint32LE();
|
|
|
|
// Bounding box
|
|
_rcBox.readFromStream(ds);
|
|
|
|
// Unused space
|
|
if (!bLOX)
|
|
ds.skip(32);
|
|
|
|
// Create buffer and read
|
|
_buf->init(ds, dimx, dimy);
|
|
}
|
|
|
|
void RMSprite::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
|
|
_buf->draw(coroParam, bigBuf, prim);
|
|
}
|
|
|
|
void RMSprite::setPalette(byte *buf) {
|
|
((RMGfxSourceBufferPal *)_buf)->loadPalette(buf);
|
|
}
|
|
|
|
RMSprite::RMSprite() {
|
|
_buf = NULL;
|
|
}
|
|
|
|
RMSprite::~RMSprite() {
|
|
if (_buf) {
|
|
delete _buf;
|
|
_buf = NULL;
|
|
}
|
|
}
|
|
|
|
/****************************************************************************\
|
|
* RMSfx Methods
|
|
\****************************************************************************/
|
|
|
|
void RMSfx::readFromStream(Common::ReadStream &ds, bool bLOX) {
|
|
// sfx name
|
|
_name = readString(ds);
|
|
|
|
int size = ds.readSint32LE();
|
|
|
|
// Read the entire buffer into a MemoryReadStream
|
|
byte *buffer = (byte *)malloc(size);
|
|
ds.read(buffer, size);
|
|
Common::SeekableReadStream *stream = new Common::MemoryReadStream(buffer, size, DisposeAfterUse::YES);
|
|
|
|
// Create the sound effect
|
|
_fx = g_vm->createSFX(stream);
|
|
_fx->setLoop(false);
|
|
}
|
|
|
|
RMSfx::RMSfx() {
|
|
_fx = NULL;
|
|
_bPlayingLoop = false;
|
|
}
|
|
|
|
RMSfx::~RMSfx() {
|
|
if (_fx) {
|
|
_fx->release();
|
|
_fx = NULL;
|
|
}
|
|
}
|
|
|
|
void RMSfx::play(bool bLoop) {
|
|
if (_fx && !_bPlayingLoop) {
|
|
_fx->setLoop(bLoop);
|
|
_fx->play();
|
|
|
|
if (bLoop)
|
|
_bPlayingLoop = true;
|
|
}
|
|
}
|
|
|
|
void RMSfx::setVolume(int vol) {
|
|
if (_fx) {
|
|
_fx->setVolume(vol);
|
|
}
|
|
}
|
|
|
|
void RMSfx::pause(bool bPause) {
|
|
if (_fx) {
|
|
_fx->setPause(bPause);
|
|
}
|
|
}
|
|
|
|
void RMSfx::stop() {
|
|
if (_fx) {
|
|
_fx->stop();
|
|
_bPlayingLoop = false;
|
|
}
|
|
}
|
|
|
|
/****************************************************************************\
|
|
* RMItem Methods
|
|
\****************************************************************************/
|
|
|
|
int RMItem::getCurPattern() {
|
|
return _nCurPattern;
|
|
}
|
|
|
|
RMGfxSourceBuffer *RMItem::newItemSpriteBuffer(int dimx, int dimy, bool bPreRLE) {
|
|
if (_cm == CM_256) {
|
|
RMGfxSourceBuffer8RLE *spr;
|
|
|
|
if (_FX == 2) { // AB
|
|
spr = new RMGfxSourceBuffer8RLEWordAB;
|
|
} else if (_FX == 1) { // OMBRA+AA
|
|
if (dimx == -1 || dimx > 255)
|
|
spr = new RMGfxSourceBuffer8RLEWordAA;
|
|
else
|
|
spr = new RMGfxSourceBuffer8RLEByteAA;
|
|
|
|
spr->setAlphaBlendColor(_FXparm);
|
|
if (bPreRLE)
|
|
spr->setAlreadyCompressed();
|
|
} else {
|
|
if (dimx == -1 || dimx > 255)
|
|
spr = new RMGfxSourceBuffer8RLEWord;
|
|
else
|
|
spr = new RMGfxSourceBuffer8RLEByte;
|
|
|
|
if (bPreRLE)
|
|
spr->setAlreadyCompressed();
|
|
}
|
|
|
|
return spr;
|
|
} else
|
|
return new RMGfxSourceBuffer16;
|
|
}
|
|
|
|
bool RMItem::isIn(const RMPoint &pt, int *size) {
|
|
RMRect rc;
|
|
|
|
if (!_bIsActive)
|
|
return false;
|
|
|
|
// Search for the right bounding box to use - use the sprite's if it has one, otherwise use the generic one
|
|
if (_nCurPattern != 0 && !_sprites[_nCurSprite]._rcBox.isEmpty())
|
|
rc = _sprites[_nCurSprite]._rcBox + calculatePos();
|
|
else if (!_rcBox.isEmpty())
|
|
rc = _rcBox;
|
|
// If no box, return immediately
|
|
else
|
|
return false;
|
|
|
|
if (size != NULL)
|
|
*size = rc.size();
|
|
|
|
return rc.ptInRect(pt + _curScroll);
|
|
}
|
|
|
|
void RMItem::readFromStream(Common::SeekableReadStream &ds, bool bLOX) {
|
|
// MPAL code
|
|
_mpalCode = ds.readSint32LE();
|
|
|
|
// Object name
|
|
_name = readString(ds);
|
|
|
|
// Z (signed)
|
|
_z = ds.readSint32LE();
|
|
|
|
// Parent position
|
|
_pos.readFromStream(ds);
|
|
|
|
// Hotspot
|
|
_hot.readFromStream(ds);
|
|
|
|
// Bounding box
|
|
_rcBox.readFromStream(ds);
|
|
|
|
// Number of sprites, sound effects, and patterns
|
|
_nSprites = ds.readSint32LE();
|
|
_nSfx = ds.readSint32LE();
|
|
_nPatterns = ds.readSint32LE();
|
|
|
|
// Color mode
|
|
byte cm = ds.readByte();
|
|
_cm = (RMColorMode)cm;
|
|
|
|
// Flag for the presence of custom palette differences
|
|
_bPal = ds.readByte();
|
|
|
|
if (_cm == CM_256) {
|
|
// If there is a palette, read it in
|
|
if (_bPal)
|
|
_pal.readFromStream(ds);
|
|
}
|
|
|
|
// MPAL data
|
|
if (!bLOX)
|
|
ds.skip(20);
|
|
|
|
_FX = ds.readByte();
|
|
_FXparm = ds.readByte();
|
|
|
|
if (!bLOX)
|
|
ds.skip(106);
|
|
|
|
// Create sub-classes
|
|
if (_nSprites > 0)
|
|
_sprites = new RMSprite[_nSprites];
|
|
if (_nSfx > 0)
|
|
_sfx = new RMSfx[_nSfx];
|
|
_patterns = new RMPattern[_nPatterns + 1];
|
|
|
|
int dimx, dimy;
|
|
// Read in class data
|
|
if (!ds.err()) {
|
|
for (int i = 0; i < _nSprites && !ds.err(); i++) {
|
|
// Download the sprites
|
|
if (bLOX)
|
|
_sprites[i].LOXGetSizeFromStream(ds, &dimx, &dimy);
|
|
else
|
|
_sprites[i].getSizeFromStream(ds, &dimx, &dimy);
|
|
|
|
_sprites[i].init(newItemSpriteBuffer(dimx, dimy, bLOX));
|
|
_sprites[i].readFromStream(ds, bLOX);
|
|
|
|
if (_cm == CM_256 && _bPal)
|
|
_sprites[i].setPalette(_pal._data);
|
|
}
|
|
}
|
|
|
|
if (!ds.err()) {
|
|
for (int i = 0; i < _nSfx && !ds.err(); i++) {
|
|
_sfx[i].readFromStream(ds, bLOX);
|
|
}
|
|
}
|
|
|
|
// Read the pattern from pattern 1
|
|
if (!ds.err()) {
|
|
for (int i = 1; i <= _nPatterns && !ds.err(); i++)
|
|
_patterns[i].readFromStream(ds, bLOX);
|
|
}
|
|
|
|
// Initialize the current pattern
|
|
if (_bInitCurPattern)
|
|
setPattern(mpalQueryItemPattern(_mpalCode));
|
|
|
|
// Initialize the current activation state
|
|
_bIsActive = mpalQueryItemIsActive(_mpalCode);
|
|
}
|
|
|
|
RMGfxPrimitive *RMItem::newItemPrimitive() {
|
|
return new RMGfxPrimitive(this);
|
|
}
|
|
|
|
void RMItem::setScrollPosition(const RMPoint &scroll) {
|
|
_curScroll = scroll;
|
|
}
|
|
|
|
bool RMItem::doFrame(RMGfxTargetBuffer *bigBuf, bool bAddToList) {
|
|
int oldSprite = _nCurSprite;
|
|
|
|
// Pattern 0 = Do not draw anything!
|
|
if (_nCurPattern == 0)
|
|
return false;
|
|
|
|
// We do an update of the pattern, which also returns the current frame
|
|
if (_nCurPattern != 0) {
|
|
_nCurSprite = _patterns[_nCurPattern].update(_hEndPattern, _bCurFlag, _sfx);
|
|
|
|
// WORKAROUND: Currently, m_nCurSprite = -1 is used to flag that an item should be removed.
|
|
// However, this seems to be done inside a process waiting on an event pulsed inside the pattern
|
|
// Update method. So the value of m_nCurSprite = -1 is being destroyed with the return value
|
|
// replacing it. It may be that the current coroutine PulseEvent implementation is wrong somehow.
|
|
// In any case, a special check here is done for items that have ended
|
|
if (_nCurPattern == 0)
|
|
_nCurSprite = -1;
|
|
}
|
|
|
|
// If the function returned -1, it means that the pattern has finished
|
|
if (_nCurSprite == -1) {
|
|
// We have pattern 0, so leave. The class will self de-register from the OT list
|
|
_nCurPattern = 0;
|
|
return false;
|
|
}
|
|
|
|
// If we are not in the OT list, add ourselves
|
|
if (!_nInList && bAddToList)
|
|
bigBuf->addPrim(newItemPrimitive());
|
|
|
|
return oldSprite != _nCurSprite;
|
|
}
|
|
|
|
RMPoint RMItem::calculatePos() {
|
|
return _pos + _patterns[_nCurPattern].pos();
|
|
}
|
|
|
|
void RMItem::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
|
|
CORO_BEGIN_CONTEXT;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
// If CurSprite == -1, then the pattern is finished
|
|
if (_nCurSprite == -1)
|
|
return;
|
|
|
|
// Set the flag
|
|
prim->setFlag(_bCurFlag);
|
|
|
|
// Offset direction for scrolling
|
|
prim->getDst().offset(-_curScroll);
|
|
|
|
// We must offset the coordinates of the item inside the primitive
|
|
// It is estimated as nonno + (babbo + figlio)
|
|
prim->getDst().offset(calculatePos());
|
|
|
|
// No stretching, please
|
|
prim->setStretch(false);
|
|
|
|
// Now we turn to the generic surface drawing routines
|
|
CORO_INVOKE_2(_sprites[_nCurSprite].draw, bigBuf, prim);
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
/**
|
|
* Overloaded priority: it's based on Z ordering
|
|
*/
|
|
int RMItem::priority() {
|
|
return _z;
|
|
}
|
|
|
|
/**
|
|
* Pattern number
|
|
*/
|
|
int RMItem::numPattern() {
|
|
return _nPatterns;
|
|
}
|
|
|
|
void RMItem::removeThis(CORO_PARAM, bool &result) {
|
|
// Remove from the OT list if the current frame is -1 (pattern over)
|
|
result = (_nCurSprite == -1);
|
|
}
|
|
|
|
void RMItem::setStatus(int nStatus) {
|
|
_bIsActive = (nStatus > 0);
|
|
}
|
|
|
|
RMPoint RMItem::getHotspot() {
|
|
return _hot;
|
|
}
|
|
|
|
int RMItem::mpalCode() {
|
|
return _mpalCode;
|
|
}
|
|
|
|
void RMItem::setPattern(int nPattern, bool bPlayP0) {
|
|
assert(nPattern >= 0 && nPattern <= _nPatterns);
|
|
|
|
if (_sfx) {
|
|
if (_nCurPattern > 0)
|
|
_patterns[_nCurPattern].stopSfx(_sfx);
|
|
}
|
|
|
|
// Remember the current pattern
|
|
_nCurPattern = nPattern;
|
|
|
|
// Start the pattern to start the animation
|
|
if (_nCurPattern != 0)
|
|
_nCurSprite = _patterns[_nCurPattern].init(_sfx, bPlayP0, &_bCurFlag);
|
|
else {
|
|
_nCurSprite = -1;
|
|
|
|
// Look for the sound effect for pattern 0
|
|
if (bPlayP0) {
|
|
for (int i = 0; i < _nSfx; i++) {
|
|
if (_sfx[i]._name == "p0")
|
|
_sfx[i].play();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool RMItem::getName(Common::String &name) {
|
|
char buf[256];
|
|
|
|
mpalQueryItemName(_mpalCode, buf);
|
|
name = buf;
|
|
if (buf[0] == '\0')
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
void RMItem::unload() {
|
|
if (_patterns != NULL) {
|
|
delete[] _patterns;
|
|
_patterns = NULL;
|
|
}
|
|
|
|
if (_sprites != NULL) {
|
|
delete[] _sprites;
|
|
_sprites = NULL;
|
|
}
|
|
|
|
if (_sfx != NULL) {
|
|
delete[] _sfx;
|
|
_sfx = NULL;
|
|
}
|
|
}
|
|
|
|
RMItem::RMItem() {
|
|
_bCurFlag = 0;
|
|
_patterns = NULL;
|
|
_sprites = NULL;
|
|
_sfx = NULL;
|
|
_curScroll.set(0, 0);
|
|
_bInitCurPattern = true;
|
|
_nCurPattern = 0;
|
|
_z = 0;
|
|
_cm = CM_256;
|
|
_FX = 0;
|
|
_FXparm = 0;
|
|
_mpalCode = 0;
|
|
_nSprites = 0;
|
|
_nSfx = 0;
|
|
_nPatterns = 0;
|
|
_bPal = 0;
|
|
_nCurSprite = 0;
|
|
|
|
_bIsActive = false;
|
|
memset(_pal._data, 0, sizeof(_pal._data));
|
|
|
|
_hEndPattern = CoroScheduler.createEvent(false, false);
|
|
}
|
|
|
|
RMItem::~RMItem() {
|
|
unload();
|
|
CoroScheduler.closeEvent(_hEndPattern);
|
|
}
|
|
|
|
void RMItem::waitForEndPattern(CORO_PARAM, uint32 hCustomSkip) {
|
|
CORO_BEGIN_CONTEXT;
|
|
uint32 h[2];
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
if (_nCurPattern != 0) {
|
|
if (hCustomSkip == CORO_INVALID_PID_VALUE)
|
|
CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _hEndPattern, CORO_INFINITE);
|
|
else {
|
|
_ctx->h[0] = hCustomSkip;
|
|
_ctx->h[1] = _hEndPattern;
|
|
CORO_INVOKE_4(CoroScheduler.waitForMultipleObjects, 2, &_ctx->h[0], false, CORO_INFINITE);
|
|
}
|
|
}
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
void RMItem::changeHotspot(const RMPoint &pt) {
|
|
_hot = pt;
|
|
}
|
|
|
|
void RMItem::setInitCurPattern(bool status) {
|
|
_bInitCurPattern = status;
|
|
}
|
|
|
|
void RMItem::playSfx(int nSfx) {
|
|
if (nSfx < _nSfx)
|
|
_sfx[nSfx].play();
|
|
}
|
|
|
|
void RMItem::pauseSound(bool bPause) {
|
|
for (int i = 0; i < _nSfx; i++)
|
|
_sfx[i].pause(bPause);
|
|
}
|
|
|
|
/****************************************************************************\
|
|
* RMWipe Methods
|
|
\****************************************************************************/
|
|
|
|
RMWipe::RMWipe() {
|
|
_hUnregistered = CoroScheduler.createEvent(false, false);
|
|
_hEndOfFade = CoroScheduler.createEvent(false, false);
|
|
|
|
_bMustRegister = false;
|
|
_bUnregister = false;
|
|
_bEndFade = false;
|
|
_bFading = false;
|
|
_nFadeStep = 0;
|
|
}
|
|
|
|
RMWipe::~RMWipe() {
|
|
CoroScheduler.closeEvent(_hUnregistered);
|
|
CoroScheduler.closeEvent(_hEndOfFade);
|
|
}
|
|
|
|
int RMWipe::priority() {
|
|
return 200;
|
|
}
|
|
|
|
void RMWipe::unregister() {
|
|
RMGfxTask::unregister();
|
|
assert(_nInList == 0);
|
|
CoroScheduler.setEvent(_hUnregistered);
|
|
}
|
|
|
|
void RMWipe::removeThis(CORO_PARAM, bool &result) {
|
|
result = _bUnregister;
|
|
}
|
|
|
|
void RMWipe::waitForFadeEnd(CORO_PARAM) {
|
|
CORO_BEGIN_CONTEXT;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _hEndOfFade, CORO_INFINITE);
|
|
|
|
_bEndFade = true;
|
|
_bFading = false;
|
|
|
|
CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE);
|
|
CORO_INVOKE_2(CoroScheduler.waitForSingleObject, g_vm->_hEndOfFrame, CORO_INFINITE);
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
void RMWipe::closeFade() {
|
|
_wip0r.unload();
|
|
}
|
|
|
|
void RMWipe::initFade(int type) {
|
|
// Activate the fade
|
|
_bUnregister = false;
|
|
_bEndFade = false;
|
|
|
|
_nFadeStep = 0;
|
|
|
|
_bMustRegister = true;
|
|
|
|
RMRes res(RES_W_CIRCLE);
|
|
Common::SeekableReadStream *ds = res.getReadStream();
|
|
_wip0r.readFromStream(*ds);
|
|
delete ds;
|
|
|
|
_wip0r.setPattern(1);
|
|
|
|
_bFading = true;
|
|
}
|
|
|
|
void RMWipe::doFrame(RMGfxTargetBuffer &bigBuf) {
|
|
if (_bMustRegister) {
|
|
bigBuf.addPrim(new RMGfxPrimitive(this));
|
|
_bMustRegister = false;
|
|
}
|
|
|
|
if (_bFading) {
|
|
_wip0r.doFrame(&bigBuf, false);
|
|
|
|
_nFadeStep++;
|
|
|
|
if (_nFadeStep == 10) {
|
|
CoroScheduler.setEvent(_hEndOfFade);
|
|
}
|
|
}
|
|
}
|
|
|
|
void RMWipe::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
|
|
CORO_BEGIN_CONTEXT;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
if (_bFading) {
|
|
CORO_INVOKE_2(_wip0r.draw, bigBuf, prim);
|
|
}
|
|
|
|
if (_bEndFade)
|
|
Common::fill((byte *)bigBuf, (byte *)bigBuf + bigBuf.getDimx() * bigBuf.getDimy() * 2, 0x0);
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
/****************************************************************************\
|
|
* RMCharacter Methods
|
|
\****************************************************************************/
|
|
|
|
/****************************************************************************/
|
|
/* Find the shortest path between two nodes of the graph connecting the BOX */
|
|
/* Returns path along the vector path path[] */
|
|
/****************************************************************************/
|
|
|
|
bool RMCharacter::findPath(short source, short destination) {
|
|
static RMBox box[MAXBOXES]; // Matrix of adjacent boxes
|
|
static short nodeCost[MAXBOXES]; // Cost per node
|
|
static short valid[MAXBOXES]; // 0:Invalid 1:Valid 2:Saturated
|
|
static short nextNode[MAXBOXES]; // Next node
|
|
bool error = false;
|
|
RMBoxLoc *cur;
|
|
|
|
g_system->lockMutex(_csMove);
|
|
|
|
if (source == -1 || destination == -1) {
|
|
g_system->unlockMutex(_csMove);
|
|
return 0;
|
|
}
|
|
|
|
// Get the boxes
|
|
cur = _theBoxes->getBoxes(_curLocation);
|
|
|
|
// Make a backup copy to work on
|
|
for (int i = 0; i < cur->_numbBox; i++)
|
|
memcpy(&box[i], &cur->_boxes[i], sizeof(RMBox));
|
|
|
|
// Invalidate all nodes
|
|
for (int i = 0; i < cur->_numbBox; i++)
|
|
valid[i] = 0;
|
|
|
|
// Prepare source and variables for the procedure
|
|
nodeCost[source] = 0;
|
|
valid[source] = 1;
|
|
bool finish = false;
|
|
|
|
// Find the shortest path
|
|
while (!finish) {
|
|
short minCost = 32000; // Reset the minimum cost
|
|
error = true; // Possible error
|
|
|
|
// 1st cycle: explore possible new nodes
|
|
for (int i = 0; i < cur->_numbBox; i++) {
|
|
if (valid[i] == 1) {
|
|
error = false; // Failure de-bunked
|
|
int j = 0;
|
|
while (((box[i]._adj[j]) != 1) && (j < cur->_numbBox))
|
|
j++;
|
|
|
|
if (j >= cur->_numbBox)
|
|
valid[i] = 2; // nodo saturated?
|
|
else {
|
|
nextNode[i] = j;
|
|
if (nodeCost[i] + 1 < minCost)
|
|
minCost = nodeCost[i] + 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (error)
|
|
finish = true; // All nodes saturated
|
|
|
|
// 2nd cycle: adding new nodes that were found, saturate old nodes
|
|
for (int i = 0; i < cur->_numbBox; i++) {
|
|
if ((valid[i] == 1) && ((nodeCost[i] + 1) == minCost)) {
|
|
box[i]._adj[nextNode[i]] = 2;
|
|
nodeCost[nextNode[i]] = minCost;
|
|
valid[nextNode[i]] = 1;
|
|
for (int j = 0; j < cur->_numbBox; j++)
|
|
if (box[j]._adj[nextNode[i]] == 1)
|
|
box[j]._adj[nextNode[i]] = 0;
|
|
|
|
if (nextNode[i] == destination)
|
|
finish = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Remove the path from the adjacent modified matrixes
|
|
if (!error) {
|
|
_pathLength = nodeCost[destination];
|
|
short k = _pathLength;
|
|
_path[k] = destination;
|
|
|
|
while (_path[k] != source) {
|
|
int i = 0;
|
|
while (box[i]._adj[_path[k]] != 2)
|
|
i++;
|
|
k--;
|
|
_path[k] = i;
|
|
}
|
|
|
|
_pathLength++;
|
|
}
|
|
|
|
g_system->unlockMutex(_csMove);
|
|
|
|
return !error;
|
|
}
|
|
|
|
void RMCharacter::goTo(CORO_PARAM, RMPoint destcoord, bool bReversed) {
|
|
CORO_BEGIN_CONTEXT;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
if (_pos == destcoord) {
|
|
if (_minPath == 0) {
|
|
CORO_INVOKE_0(stop);
|
|
CoroScheduler.pulseEvent(_hEndOfPath);
|
|
return;
|
|
}
|
|
}
|
|
|
|
_status = WALK;
|
|
_lineStart = _pos;
|
|
_lineEnd = destcoord;
|
|
_dx = _lineStart._x - _lineEnd._x;
|
|
_dy = _lineStart._y - _lineEnd._y;
|
|
_fx = _dx;
|
|
_fy = _dy;
|
|
_dx = ABS(_dx);
|
|
_dy = ABS(_dy);
|
|
_walkSpeed = _curSpeed;
|
|
_walkCount = 0;
|
|
|
|
if (bReversed) {
|
|
while (0) ;
|
|
}
|
|
|
|
int nPatt = getCurPattern();
|
|
|
|
if (_dx > _dy) {
|
|
_slope = _fy / _fx;
|
|
if (_lineEnd._x < _lineStart._x)
|
|
_walkSpeed = -_walkSpeed;
|
|
_walkStatus = 1;
|
|
|
|
// Change the pattern for the new direction
|
|
_bNeedToStop = true;
|
|
if ((_walkSpeed < 0 && !bReversed) || (_walkSpeed >= 0 && bReversed)) {
|
|
if (nPatt != PAT_WALKLEFT)
|
|
setPattern(PAT_WALKLEFT);
|
|
} else {
|
|
if (nPatt != PAT_WALKRIGHT)
|
|
setPattern(PAT_WALKRIGHT);
|
|
}
|
|
} else {
|
|
_slope = _fx / _fy;
|
|
if (_lineEnd._y < _lineStart._y)
|
|
_walkSpeed = -_walkSpeed;
|
|
_walkStatus = 0;
|
|
|
|
_bNeedToStop = true;
|
|
if ((_walkSpeed < 0 && !bReversed) || (_walkSpeed >= 0 && bReversed)) {
|
|
if (nPatt != PAT_WALKUP)
|
|
setPattern(PAT_WALKUP);
|
|
} else {
|
|
if (nPatt != PAT_WALKDOWN)
|
|
setPattern(PAT_WALKDOWN);
|
|
}
|
|
}
|
|
|
|
_olddx = _dx;
|
|
_olddy = _dy;
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
RMPoint RMCharacter::searching(char UP, char DOWN, char RIGHT, char LEFT, RMPoint point) {
|
|
short steps;
|
|
RMPoint newPt, foundPt;
|
|
short minStep = 32000;
|
|
|
|
if (UP) {
|
|
newPt = point;
|
|
steps = 0;
|
|
while ((inWhichBox(newPt) == -1) && (newPt._y >= 0)) {
|
|
newPt._y--;
|
|
steps++;
|
|
}
|
|
if ((inWhichBox(newPt) != -1) && (steps < minStep) &&
|
|
findPath(inWhichBox(_pos), inWhichBox(newPt))) {
|
|
minStep = steps;
|
|
newPt._y--; // to avoid error?
|
|
foundPt = newPt;
|
|
}
|
|
}
|
|
|
|
if (DOWN) {
|
|
newPt = point;
|
|
steps = 0;
|
|
while ((inWhichBox(newPt) == -1) && (newPt._y < 480)) {
|
|
newPt._y++;
|
|
steps++;
|
|
}
|
|
if ((inWhichBox(newPt) != -1) && (steps < minStep) &&
|
|
findPath(inWhichBox(_pos), inWhichBox(newPt))) {
|
|
minStep = steps;
|
|
newPt._y++; // to avoid error?
|
|
foundPt = newPt;
|
|
}
|
|
}
|
|
|
|
if (RIGHT) {
|
|
newPt = point;
|
|
steps = 0;
|
|
while ((inWhichBox(newPt) == -1) && (newPt._x < 640)) {
|
|
newPt._x++;
|
|
steps++;
|
|
}
|
|
if ((inWhichBox(newPt) != -1) && (steps < minStep) &&
|
|
findPath(inWhichBox(_pos), inWhichBox(newPt))) {
|
|
minStep = steps;
|
|
newPt._x++; // to avoid error?
|
|
foundPt = newPt;
|
|
}
|
|
}
|
|
|
|
if (LEFT) {
|
|
newPt = point;
|
|
steps = 0;
|
|
while ((inWhichBox(newPt) == -1) && (newPt._x >= 0)) {
|
|
newPt._x--;
|
|
steps++;
|
|
}
|
|
if ((inWhichBox(newPt) != -1) && (steps < minStep) &&
|
|
findPath(inWhichBox(_pos), inWhichBox(newPt))) {
|
|
minStep = steps;
|
|
newPt._x--; // to avoid error?
|
|
foundPt = newPt;
|
|
}
|
|
}
|
|
|
|
if (minStep == 32000)
|
|
foundPt = point;
|
|
|
|
return foundPt;
|
|
}
|
|
|
|
RMPoint RMCharacter::nearestPoint(const RMPoint &point) {
|
|
return searching(1, 1, 1, 1, point);
|
|
}
|
|
|
|
short RMCharacter::scanLine(const RMPoint &point) {
|
|
int Ldx, Ldy, Lcount;
|
|
float Lfx, Lfy, Lslope;
|
|
RMPoint Lstart, Lend, Lscan;
|
|
signed char Lspeed, Lstatus;
|
|
|
|
Lstart = _pos;
|
|
Lend = point;
|
|
Ldx = Lstart._x - Lend._x;
|
|
Ldy = Lstart._y - Lend._y;
|
|
Lfx = Ldx;
|
|
Lfy = Ldy;
|
|
Ldx = ABS(Ldx);
|
|
Ldy = ABS(Ldy);
|
|
Lspeed = 1;
|
|
Lcount = 0;
|
|
|
|
if (Ldx > Ldy) {
|
|
Lslope = Lfy / Lfx;
|
|
if (Lend._x < Lstart._x)
|
|
Lspeed = -Lspeed;
|
|
Lstatus = 1;
|
|
} else {
|
|
Lslope = Lfx / Lfy;
|
|
if (Lend._y < Lstart._y)
|
|
Lspeed = - Lspeed;
|
|
Lstatus = 0;
|
|
}
|
|
|
|
Lscan = Lstart; // Start scanning
|
|
while (inWhichBox(Lscan) != -1) {
|
|
Lcount++;
|
|
if (Lstatus) {
|
|
Ldx = Lspeed * Lcount;
|
|
Ldy = (int)(Lslope * Ldx);
|
|
} else {
|
|
Ldy = Lspeed * Lcount;
|
|
Ldx = (int)(Lslope * Ldy);
|
|
}
|
|
|
|
Lscan._x = Lstart._x + Ldx;
|
|
Lscan._y = Lstart._y + Ldy;
|
|
|
|
if ((ABS(Lscan._x - Lend._x) <= 1) && (ABS(Lscan._y - Lend._y) <= 1))
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Calculates intersections between the straight line and the closest BBOX
|
|
*/
|
|
RMPoint RMCharacter::invScanLine(const RMPoint &point) {
|
|
RMPoint lStart = point; // Exchange!
|
|
RMPoint lEnd = _pos; // :-)
|
|
int lDx = lStart._x - lEnd._x;
|
|
int lDy = lStart._y - lEnd._y;
|
|
float lFx = lDx;
|
|
float lFy = lDy;
|
|
lDx = ABS(lDx);
|
|
lDy = ABS(lDy);
|
|
signed char lSpeed = 1;
|
|
int lCount = 0;
|
|
|
|
signed char lStatus;
|
|
float lSlope;
|
|
|
|
if (lDx > lDy) {
|
|
lSlope = lFy / lFx;
|
|
if (lEnd._x < lStart._x)
|
|
lSpeed = -lSpeed;
|
|
lStatus = 1;
|
|
} else {
|
|
lSlope = lFx / lFy;
|
|
if (lEnd._y < lStart._y)
|
|
lSpeed = -lSpeed;
|
|
lStatus = 0;
|
|
}
|
|
|
|
RMPoint lScan = lStart;
|
|
signed char lBox = -1;
|
|
|
|
for (;;) {
|
|
if (inWhichBox(lScan) != -1) {
|
|
if (inWhichBox(lScan) != lBox) {
|
|
if (inWhichBox(_pos) == inWhichBox(lScan) || findPath(inWhichBox(_pos), inWhichBox(lScan)))
|
|
return lScan;
|
|
else
|
|
lBox = inWhichBox(lScan);
|
|
}
|
|
}
|
|
|
|
lCount++;
|
|
if (lStatus) {
|
|
lDx = lSpeed * lCount;
|
|
lDy = (int)(lSlope * lDx);
|
|
} else {
|
|
lDy = lSpeed * lCount;
|
|
lDx = (int)(lSlope * lDy);
|
|
}
|
|
lScan._x = lStart._x + lDx;
|
|
lScan._y = lStart._y + lDy;
|
|
|
|
// WORKAROUND: Handles cases where the points never fall inside a bounding box
|
|
if (lScan._x < -100 || lScan._y < -100 || lScan._x >= 1000 || lScan._y >= 1000)
|
|
return point;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the HotSpot coordinate closest to the player
|
|
*/
|
|
|
|
RMPoint RMCharacter::nearestHotSpot(int sourcebox, int destbox) {
|
|
RMPoint hotspot;
|
|
int x, y;
|
|
int minDist = 10000000;
|
|
RMBoxLoc *cur = _theBoxes->getBoxes(_curLocation);
|
|
|
|
for (short cc = 0; cc < cur->_boxes[sourcebox]._numHotspot; cc++)
|
|
if ((cur->_boxes[sourcebox]._hotspot[cc]._destination) == destbox) {
|
|
x = ABS(cur->_boxes[sourcebox]._hotspot[cc]._hotx - _pos._x);
|
|
y = ABS(cur->_boxes[sourcebox]._hotspot[cc]._hoty - _pos._y);
|
|
|
|
if ((x * x + y * y) < minDist) {
|
|
minDist = x * x + y * y;
|
|
hotspot._x = cur->_boxes[sourcebox]._hotspot[cc]._hotx;
|
|
hotspot._y = cur->_boxes[sourcebox]._hotspot[cc]._hoty;
|
|
}
|
|
}
|
|
|
|
return hotspot;
|
|
}
|
|
|
|
void RMCharacter::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
|
|
CORO_BEGIN_CONTEXT;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
if (_bDrawNow) {
|
|
prim->getDst() += _fixedScroll;
|
|
|
|
CORO_INVOKE_2(RMItem::draw, bigBuf, prim);
|
|
}
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
void RMCharacter::newBoxEntered(int nBox) {
|
|
RMBoxLoc *cur;
|
|
|
|
// Recall on ExitBox
|
|
mpalQueryDoAction(3, _curLocation, _curBox);
|
|
|
|
cur = _theBoxes->getBoxes(_curLocation);
|
|
bool bOldReverse = cur->_boxes[_curBox]._bReversed;
|
|
_curBox = nBox;
|
|
|
|
// If Z is changed, we must remove it from the OT
|
|
if (cur->_boxes[_curBox]._destZ != _z) {
|
|
_bRemoveFromOT = true;
|
|
_z = cur->_boxes[_curBox]._destZ;
|
|
}
|
|
|
|
// Movement management is reversed, only if we are not in the shortest path. If we are in the shortest
|
|
// path, directly do the DoFrame
|
|
if (_bMovingWithoutMinpath) {
|
|
if ((cur->_boxes[_curBox]._bReversed && !bOldReverse) || (!cur->_boxes[_curBox]._bReversed && bOldReverse)) {
|
|
switch (getCurPattern()) {
|
|
case PAT_WALKUP:
|
|
setPattern(PAT_WALKDOWN);
|
|
break;
|
|
case PAT_WALKDOWN:
|
|
setPattern(PAT_WALKUP);
|
|
break;
|
|
case PAT_WALKRIGHT:
|
|
setPattern(PAT_WALKLEFT);
|
|
break;
|
|
case PAT_WALKLEFT:
|
|
setPattern(PAT_WALKRIGHT);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Recall On EnterBox
|
|
mpalQueryDoAction(2, _curLocation, _curBox);
|
|
}
|
|
|
|
void RMCharacter::doFrame(CORO_PARAM, RMGfxTargetBuffer *bigBuf, int loc) {
|
|
CORO_BEGIN_CONTEXT;
|
|
bool bEndNow;
|
|
RMBoxLoc *cur;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
_ctx->bEndNow = false;
|
|
_bEndOfPath = false;
|
|
_bDrawNow = (_curLocation == loc);
|
|
|
|
g_system->lockMutex(_csMove);
|
|
|
|
// If we're walking..
|
|
if (_status != STAND) {
|
|
// If we are going horizontally
|
|
if (_walkStatus == 1) {
|
|
_dx = _walkSpeed * _walkCount;
|
|
_dy = (int)(_slope * _dx);
|
|
_pos._x = _lineStart._x + _dx;
|
|
_pos._y = _lineStart._y + _dy;
|
|
|
|
// Right
|
|
if (((_walkSpeed > 0) && (_pos._x > _lineEnd._x)) || ((_walkSpeed < 0) && (_pos._x < _lineEnd._x))) {
|
|
_pos = _lineEnd;
|
|
_status = STAND;
|
|
_ctx->bEndNow = true;
|
|
}
|
|
}
|
|
|
|
// If we are going vertical
|
|
if (_walkStatus == 0) {
|
|
_dy = _walkSpeed * _walkCount;
|
|
_dx = (int)(_slope * _dy);
|
|
_pos._x = _lineStart._x + _dx;
|
|
_pos._y = _lineStart._y + _dy;
|
|
|
|
// Down
|
|
if (((_walkSpeed > 0) && (_pos._y > _lineEnd._y)) || ((_walkSpeed < 0) && (_pos._y < _lineEnd._y))) {
|
|
_pos = _lineEnd;
|
|
_status = STAND;
|
|
_ctx->bEndNow = true;
|
|
}
|
|
}
|
|
|
|
// Check if the character came out of the BOX in error, in which case he returns immediately
|
|
if (inWhichBox(_pos) == -1) {
|
|
_pos._x = _lineStart._x + _olddx;
|
|
_pos._y = _lineStart._y + _olddy;
|
|
}
|
|
|
|
// If we have just moved to a temporary location, and is over the shortest path, we stop permanently
|
|
if (_ctx->bEndNow && _minPath == 0) {
|
|
if (!_bEndOfPath)
|
|
CORO_INVOKE_0(stop);
|
|
_bEndOfPath = true;
|
|
CoroScheduler.pulseEvent(_hEndOfPath);
|
|
}
|
|
|
|
_walkCount++;
|
|
|
|
// Update the character Z. @@@ Should remove only if the Z was changed
|
|
|
|
// Check if the box was changed
|
|
if (!_theBoxes->isInBox(_curLocation, _curBox, _pos))
|
|
newBoxEntered(inWhichBox(_pos));
|
|
|
|
// Update the old coordinates
|
|
_olddx = _dx;
|
|
_olddy = _dy;
|
|
}
|
|
|
|
// If we stop
|
|
if (_status == STAND) {
|
|
// Check if there is still the shortest path to calculate
|
|
if (_minPath == 1) {
|
|
_ctx->cur = _theBoxes->getBoxes(_curLocation);
|
|
|
|
// If we still have to go through a box
|
|
if (_pathCount < _pathLength) {
|
|
// Check if the box we're going into is active
|
|
if (_ctx->cur->_boxes[_path[_pathCount - 1]]._bActive) {
|
|
// Move in a straight line towards the nearest hotspot, taking into account the reversing
|
|
// NEWBOX = path[pathcount-1]
|
|
CORO_INVOKE_2(goTo, nearestHotSpot(_path[_pathCount - 1], _path[_pathCount]), _ctx->cur->_boxes[_path[_pathCount - 1]]._bReversed);
|
|
_pathCount++;
|
|
} else {
|
|
// If the box is off, we can only block all
|
|
// @@@ Whilst this should not happen, because have improved
|
|
// the search for the minimum path
|
|
_minPath = 0;
|
|
if (!_bEndOfPath)
|
|
CORO_INVOKE_0(stop);
|
|
_bEndOfPath = true;
|
|
CoroScheduler.pulseEvent(_hEndOfPath);
|
|
}
|
|
} else {
|
|
// If we have already entered the last box, we just have to move in a straight line towards the
|
|
// point of arrival
|
|
// NEWBOX = InWhichBox(pathend)
|
|
_minPath = 0;
|
|
CORO_INVOKE_2(goTo, _pathEnd, _ctx->cur->_boxes[inWhichBox(_pathEnd)]._bReversed);
|
|
}
|
|
}
|
|
}
|
|
|
|
g_system->unlockMutex(_csMove);
|
|
|
|
// Invoke the DoFrame of the item
|
|
RMItem::doFrame(bigBuf);
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
bool RMCharacter::endOfPath() {
|
|
return _bEndOfPath;
|
|
}
|
|
|
|
void RMCharacter::stop(CORO_PARAM) {
|
|
CORO_BEGIN_CONTEXT;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
_bMoving = false;
|
|
|
|
// You never know..
|
|
_status = STAND;
|
|
_minPath = 0;
|
|
|
|
if (!_bNeedToStop)
|
|
return;
|
|
|
|
_bNeedToStop = false;
|
|
|
|
switch (getCurPattern()) {
|
|
case PAT_WALKUP:
|
|
setPattern(PAT_STANDUP);
|
|
break;
|
|
|
|
case PAT_WALKDOWN:
|
|
setPattern(PAT_STANDDOWN);
|
|
break;
|
|
|
|
case PAT_WALKLEFT:
|
|
setPattern(PAT_STANDLEFT);
|
|
break;
|
|
|
|
case PAT_WALKRIGHT:
|
|
setPattern(PAT_STANDRIGHT);
|
|
break;
|
|
|
|
default:
|
|
setPattern(PAT_STANDDOWN);
|
|
break;
|
|
}
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
/**
|
|
* Check if the character is moving
|
|
*/
|
|
bool RMCharacter::isMoving() {
|
|
return _bMoving;
|
|
}
|
|
|
|
inline int RMCharacter::inWhichBox(const RMPoint &pt) {
|
|
return _theBoxes->whichBox(_curLocation, pt);
|
|
}
|
|
|
|
void RMCharacter::move(CORO_PARAM, RMPoint pt, bool *result) {
|
|
CORO_BEGIN_CONTEXT;
|
|
RMPoint dest;
|
|
int numbox;
|
|
RMBoxLoc *cur;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
_bMoving = true;
|
|
|
|
// 0, 0 does not do anything, just stops the character
|
|
if (pt._x == 0 && pt._y == 0) {
|
|
_minPath = 0;
|
|
_status = STAND;
|
|
CORO_INVOKE_0(stop);
|
|
if (result)
|
|
*result = true;
|
|
return;
|
|
}
|
|
|
|
// If clicked outside the box
|
|
_ctx->numbox = inWhichBox(pt);
|
|
if (_ctx->numbox == -1) {
|
|
// Find neareste point inside the box
|
|
_ctx->dest = nearestPoint(pt);
|
|
|
|
// ???!??
|
|
if (_ctx->dest == pt)
|
|
_ctx->dest = invScanLine(pt);
|
|
|
|
pt = _ctx->dest;
|
|
_ctx->numbox = inWhichBox(pt);
|
|
}
|
|
|
|
_ctx->cur = _theBoxes->getBoxes(_curLocation);
|
|
|
|
_minPath = 0;
|
|
_status = STAND;
|
|
_bMovingWithoutMinpath = true;
|
|
if (scanLine(pt))
|
|
CORO_INVOKE_2(goTo, pt, _ctx->cur->_boxes[_ctx->numbox]._bReversed);
|
|
else if (findPath(inWhichBox(_pos), inWhichBox(pt))) {
|
|
_bMovingWithoutMinpath = false;
|
|
_minPath = 1;
|
|
_pathCount = 1;
|
|
_pathEnd = pt;
|
|
} else {
|
|
// @@@ This case is whether a hotspot is inside a box, but there is
|
|
// a path to get there. We use the InvScanLine to search around a point
|
|
_ctx->dest = invScanLine(pt);
|
|
pt = _ctx->dest;
|
|
|
|
if (scanLine(pt))
|
|
CORO_INVOKE_2(goTo, pt, _ctx->cur->_boxes[_ctx->numbox]._bReversed);
|
|
else if (findPath(inWhichBox(_pos), inWhichBox(pt))) {
|
|
_bMovingWithoutMinpath = false;
|
|
_minPath = 1;
|
|
_pathCount = 1;
|
|
_pathEnd = pt;
|
|
if (result)
|
|
*result = true;
|
|
} else {
|
|
if (result)
|
|
*result = false;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if (result)
|
|
*result = true;
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
void RMCharacter::setPosition(const RMPoint &pt, int newloc) {
|
|
RMBoxLoc *box;
|
|
|
|
_minPath = 0;
|
|
_status = STAND;
|
|
_pos = pt;
|
|
|
|
if (newloc != -1)
|
|
_curLocation = newloc;
|
|
|
|
// Update the character's Z value
|
|
box = _theBoxes->getBoxes(_curLocation);
|
|
_curBox = inWhichBox(_pos);
|
|
assert(_curBox != -1);
|
|
_z = box->_boxes[_curBox]._destZ;
|
|
_bRemoveFromOT = true;
|
|
}
|
|
|
|
void RMCharacter::waitForEndMovement(CORO_PARAM) {
|
|
CORO_BEGIN_CONTEXT;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
if (_bMoving)
|
|
CORO_INVOKE_2(CoroScheduler.waitForSingleObject, _hEndOfPath, CORO_INFINITE);
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
void RMCharacter::setFixedScroll(const RMPoint &fix) {
|
|
_fixedScroll = fix;
|
|
}
|
|
|
|
void RMCharacter::setSpeed(int speed) {
|
|
_curSpeed = speed;
|
|
}
|
|
|
|
void RMCharacter::removeThis(CORO_PARAM, bool &result) {
|
|
CORO_BEGIN_CONTEXT;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
if (_bRemoveFromOT)
|
|
result = true;
|
|
else
|
|
CORO_INVOKE_1(RMItem::removeThis, result);
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
RMCharacter::RMCharacter() {
|
|
_csMove = g_system->createMutex();
|
|
_hEndOfPath = CoroScheduler.createEvent(false, false);
|
|
_minPath = 0;
|
|
_curSpeed = 3;
|
|
_bRemoveFromOT = false;
|
|
_bMoving = false;
|
|
_curLocation = 0;
|
|
_curBox = 0;
|
|
_dx = _dy = 0;
|
|
_olddx = _olddy = 0;
|
|
_fx = _fy = _slope = 0;
|
|
_walkSpeed = _walkStatus = 0;
|
|
_nextBox = 0;
|
|
_pathLength = _pathCount = 0;
|
|
_status = STAND;
|
|
_theBoxes = NULL;
|
|
_walkCount = 0;
|
|
_bEndOfPath = false;
|
|
_bMovingWithoutMinpath = false;
|
|
_bDrawNow = false;
|
|
_bNeedToStop = false;
|
|
|
|
memset(_path, 0, sizeof(_path));
|
|
|
|
_pos.set(0, 0);
|
|
}
|
|
|
|
RMCharacter::~RMCharacter() {
|
|
g_system->deleteMutex(_csMove);
|
|
CoroScheduler.closeEvent(_hEndOfPath);
|
|
}
|
|
|
|
void RMCharacter::linkToBoxes(RMGameBoxes *boxes) {
|
|
_theBoxes = boxes;
|
|
}
|
|
|
|
/****************************************************************************\
|
|
* RMBox Methods
|
|
\****************************************************************************/
|
|
|
|
void RMBox::readFromStream(Common::ReadStream &ds) {
|
|
// Bbox
|
|
_left = ds.readSint32LE();
|
|
_top = ds.readSint32LE();
|
|
_right = ds.readSint32LE();
|
|
_bottom = ds.readSint32LE();
|
|
|
|
// Adjacency
|
|
for (int i = 0; i < MAXBOXES; i++) {
|
|
_adj[i] = ds.readSint32LE();
|
|
}
|
|
|
|
// Misc
|
|
_numHotspot = ds.readSint32LE();
|
|
_destZ = ds.readByte();
|
|
byte b = ds.readByte();
|
|
_bActive = b;
|
|
b = ds.readByte();
|
|
_bReversed = b;
|
|
|
|
// Reversed expansion space
|
|
for (int i = 0; i < 30; i++)
|
|
ds.readByte();
|
|
|
|
uint16 w;
|
|
// Hotspots
|
|
for (int i = 0; i < _numHotspot; i++) {
|
|
w = ds.readUint16LE();
|
|
_hotspot[i]._hotx = w;
|
|
w = ds.readUint16LE();
|
|
_hotspot[i]._hoty = w;
|
|
w = ds.readUint16LE();
|
|
_hotspot[i]._destination = w;
|
|
}
|
|
}
|
|
|
|
/****************************************************************************\
|
|
* RMBoxLoc Methods
|
|
\****************************************************************************/
|
|
|
|
RMBoxLoc::RMBoxLoc() {
|
|
_boxes = NULL;
|
|
_numbBox = 0;
|
|
}
|
|
|
|
RMBoxLoc::~RMBoxLoc() {
|
|
delete[] _boxes;
|
|
}
|
|
|
|
void RMBoxLoc::readFromStream(Common::ReadStream &ds) {
|
|
char buf[2];
|
|
|
|
// ID and version
|
|
buf[0] = ds.readByte();
|
|
buf[1] = ds.readByte();
|
|
byte ver = ds.readByte();
|
|
assert(buf[0] == 'B' && buf[1] == 'X');
|
|
assert(ver == 3);
|
|
|
|
// Number of boxes
|
|
_numbBox = ds.readSint32LE();
|
|
|
|
// Allocate memory for the boxes
|
|
_boxes = new RMBox[_numbBox];
|
|
|
|
// Read in boxes
|
|
for (int i = 0; i < _numbBox; i++)
|
|
_boxes[i].readFromStream(ds);
|
|
}
|
|
|
|
void RMBoxLoc::recalcAllAdj() {
|
|
for (int i = 0; i < _numbBox; i++) {
|
|
Common::fill(_boxes[i]._adj, _boxes[i]._adj + MAXBOXES, 0);
|
|
|
|
for (int j = 0; j < _boxes[i]._numHotspot; j++) {
|
|
if (_boxes[_boxes[i]._hotspot[j]._destination]._bActive)
|
|
_boxes[i]._adj[_boxes[i]._hotspot[j]._destination] = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/****************************************************************************\
|
|
* RMGameBoxes methods
|
|
\****************************************************************************/
|
|
|
|
RMGameBoxes::RMGameBoxes() {
|
|
_nLocBoxes = 0;
|
|
Common::fill(_allBoxes, _allBoxes + GAME_BOXES_SIZE, (RMBoxLoc *)NULL);
|
|
}
|
|
|
|
RMGameBoxes::~RMGameBoxes() {
|
|
for (int i = 1; i <= _nLocBoxes; ++i)
|
|
delete _allBoxes[i];
|
|
}
|
|
|
|
void RMGameBoxes::init() {
|
|
// Load boxes from disk
|
|
_nLocBoxes = 130;
|
|
for (int i = 1; i <= _nLocBoxes; i++) {
|
|
RMRes res(10000 + i);
|
|
|
|
Common::SeekableReadStream *ds = res.getReadStream();
|
|
|
|
_allBoxes[i] = new RMBoxLoc();
|
|
_allBoxes[i]->readFromStream(*ds);
|
|
|
|
_allBoxes[i]->recalcAllAdj();
|
|
|
|
delete ds;
|
|
}
|
|
}
|
|
|
|
void RMGameBoxes::close() {
|
|
}
|
|
|
|
RMBoxLoc *RMGameBoxes::getBoxes(int nLoc) {
|
|
return _allBoxes[nLoc];
|
|
}
|
|
|
|
int RMGameBoxes::getLocBoxesCount() const {
|
|
return _nLocBoxes;
|
|
}
|
|
|
|
bool RMGameBoxes::isInBox(int nLoc, int nBox, const RMPoint &pt) {
|
|
RMBoxLoc *cur = getBoxes(nLoc);
|
|
|
|
if ((pt._x >= cur->_boxes[nBox]._left) && (pt._x <= cur->_boxes[nBox]._right) &&
|
|
(pt._y >= cur->_boxes[nBox]._top) && (pt._y <= cur->_boxes[nBox]._bottom))
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
int RMGameBoxes::whichBox(int nLoc, const RMPoint &punto) {
|
|
RMBoxLoc *cur = getBoxes(nLoc);
|
|
|
|
if (!cur)
|
|
return -1;
|
|
|
|
for (int i = 0; i < cur->_numbBox; i++) {
|
|
if (cur->_boxes[i]._bActive) {
|
|
if ((punto._x >= cur->_boxes[i]._left) && (punto._x <= cur->_boxes[i]._right) &&
|
|
(punto._y >= cur->_boxes[i]._top) && (punto._y <= cur->_boxes[i]._bottom))
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
void RMGameBoxes::changeBoxStatus(int nLoc, int nBox, int status) {
|
|
_allBoxes[nLoc]->_boxes[nBox]._bActive = status;
|
|
_allBoxes[nLoc]->recalcAllAdj();
|
|
}
|
|
|
|
int RMGameBoxes::getSaveStateSize() {
|
|
int size = 4;
|
|
|
|
for (int i = 1; i <= _nLocBoxes; i++) {
|
|
size += 4;
|
|
size += _allBoxes[i]->_numbBox;
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
void RMGameBoxes::saveState(byte *state) {
|
|
// Save the number of locations with boxes
|
|
WRITE_LE_UINT32(state, _nLocBoxes);
|
|
state += 4;
|
|
|
|
// For each location, write out the number of boxes and their status
|
|
for (int i = 1; i <= _nLocBoxes; i++) {
|
|
WRITE_LE_UINT32(state, _allBoxes[i]->_numbBox);
|
|
state += 4;
|
|
|
|
for (int j = 0; j < _allBoxes[i]->_numbBox; j++)
|
|
*state++ = _allBoxes[i]->_boxes[j]._bActive;
|
|
}
|
|
}
|
|
|
|
void RMGameBoxes::loadState(byte *state) {
|
|
// Load number of items
|
|
int nloc = READ_LE_UINT32(state);
|
|
state += 4;
|
|
|
|
assert(nloc <= _nLocBoxes);
|
|
|
|
// For each location, read the number of boxes and their status
|
|
for (int i = 1; i <= nloc; i++) {
|
|
int nbox = READ_LE_UINT32(state);
|
|
state += 4;
|
|
|
|
for (int j = 0; j < nbox ; j++) {
|
|
if (j < _allBoxes[i]->_numbBox)
|
|
_allBoxes[i]->_boxes[j]._bActive = *state;
|
|
|
|
state++;
|
|
}
|
|
|
|
_allBoxes[i]->recalcAllAdj();
|
|
}
|
|
}
|
|
|
|
/****************************************************************************\
|
|
* RMLocation Methods
|
|
\****************************************************************************/
|
|
|
|
/**
|
|
* Standard constructor
|
|
*/
|
|
RMLocation::RMLocation() {
|
|
_nItems = 0;
|
|
_items = NULL;
|
|
_buf = NULL;
|
|
TEMPNumLoc = 0;
|
|
_cmode = CM_256;
|
|
}
|
|
|
|
RMPoint RMLocation::TEMPGetTonyStart() {
|
|
return TEMPTonyStart;
|
|
}
|
|
|
|
int RMLocation::TEMPGetNumLoc() {
|
|
return TEMPNumLoc;
|
|
}
|
|
|
|
/**
|
|
* Load a location (.LOC) from a given data stream
|
|
*
|
|
* @param ds Data stream
|
|
* @returns True if succeeded OK, false in case of error.
|
|
*/
|
|
bool RMLocation::load(Common::SeekableReadStream &ds) {
|
|
char id[3];
|
|
|
|
// Reset dirty rectangling
|
|
_prevScroll.set(-1, -1);
|
|
_prevFixedScroll.set(-1, -1);
|
|
|
|
// Check the ID
|
|
ds.read(id, 3);
|
|
|
|
// Check if we are in a LOX
|
|
if (id[0] == 'L' && id[1] == 'O' && id[2] == 'X')
|
|
return loadLOX(ds);
|
|
|
|
// Otherwise, check that it is a normal LOC
|
|
if (id[0] != 'L' || id[1] != 'O' || id[2] != 'C')
|
|
return false;
|
|
|
|
// Version
|
|
byte ver = ds.readByte();
|
|
assert(ver == 6);
|
|
|
|
// Location name
|
|
_name = readString(ds);
|
|
|
|
// Skip the MPAL bailouts (64 bytes)
|
|
TEMPNumLoc = ds.readSint32LE();
|
|
TEMPTonyStart._x = ds.readSint32LE();
|
|
TEMPTonyStart._y = ds.readSint32LE();
|
|
ds.skip(64 - 4 * 3);
|
|
|
|
// Skip flag associated with the background (?)
|
|
ds.skip(1);
|
|
|
|
// Location dimensions
|
|
int dimx = ds.readSint32LE();
|
|
int dimy = ds.readSint32LE();
|
|
_curScroll.set(0, 0);
|
|
|
|
// Read the color mode
|
|
byte cm = ds.readByte();
|
|
_cmode = (RMColorMode)cm;
|
|
|
|
// Initialize the source buffer and read the location
|
|
switch (_cmode) {
|
|
case CM_256:
|
|
_buf = new RMGfxSourceBuffer8;
|
|
break;
|
|
|
|
case CM_65K:
|
|
_buf = new RMGfxSourceBuffer16;
|
|
break;
|
|
|
|
default:
|
|
assert(0);
|
|
break;
|
|
};
|
|
|
|
// Initialize the surface, loading the palette if necessary
|
|
_buf->init(ds, dimx, dimy, true);
|
|
|
|
// Check the size of the location
|
|
//assert(dimy!=512);
|
|
|
|
// Number of objects
|
|
_nItems = ds.readSint32LE();
|
|
|
|
// Create and read in the objects
|
|
if (_nItems > 0)
|
|
_items = new RMItem[_nItems];
|
|
|
|
|
|
g_vm->freezeTime();
|
|
for (int i = 0; i < _nItems && !ds.err(); i++)
|
|
_items[i].readFromStream(ds);
|
|
g_vm->unfreezeTime();
|
|
|
|
return ds.err();
|
|
}
|
|
|
|
bool RMLocation::loadLOX(Common::SeekableReadStream &ds) {
|
|
// Version
|
|
byte ver = ds.readByte();
|
|
assert(ver == 1);
|
|
|
|
// Location name
|
|
_name = readString(ds);
|
|
|
|
// Location number
|
|
TEMPNumLoc = ds.readSint32LE();
|
|
TEMPTonyStart._x = ds.readSint32LE();
|
|
TEMPTonyStart._y = ds.readSint32LE();
|
|
|
|
// Dimensions
|
|
int dimx = ds.readSint32LE();
|
|
int dimy = ds.readSint32LE();
|
|
_curScroll.set(0, 0);
|
|
|
|
// It's always 65K (16-bit) mode
|
|
_cmode = CM_65K;
|
|
_buf = new RMGfxSourceBuffer16;
|
|
|
|
// Initialize the surface, loading in the palette if necessary
|
|
_buf->init(ds, dimx, dimy, true);
|
|
|
|
// Number of items
|
|
_nItems = ds.readSint32LE();
|
|
|
|
// Create and read objects
|
|
if (_nItems > 0)
|
|
_items = new RMItem[_nItems];
|
|
|
|
for (int i = 0; i < _nItems && !ds.err(); i++)
|
|
_items[i].readFromStream(ds, true);
|
|
|
|
return ds.err();
|
|
}
|
|
|
|
/**
|
|
* Draw method overloaded from RMGfxSourceBUffer8
|
|
*/
|
|
void RMLocation::draw(CORO_PARAM, RMGfxTargetBuffer &bigBuf, RMGfxPrimitive *prim) {
|
|
CORO_BEGIN_CONTEXT;
|
|
bool priorTracking;
|
|
bool hasChanges;
|
|
CORO_END_CONTEXT(_ctx);
|
|
|
|
CORO_BEGIN_CODE(_ctx);
|
|
|
|
// Set the position of the source scrolling
|
|
if (_buf->getDimy() > RM_SY || _buf->getDimx() > RM_SX) {
|
|
prim->setSrc(RMRect(_curScroll, _curScroll + RMPoint(640, 480)));
|
|
}
|
|
|
|
prim->setDst(_fixedScroll);
|
|
|
|
// Check whether dirty rects are being tracked, and if there are changes, leave tracking
|
|
// turned on so a dirty rect will be added for the entire background
|
|
_ctx->priorTracking = bigBuf.getTrackDirtyRects();
|
|
_ctx->hasChanges = (_prevScroll != _curScroll) || (_prevFixedScroll != _fixedScroll);
|
|
bigBuf.setTrackDirtyRects(_ctx->priorTracking && _ctx->hasChanges);
|
|
|
|
// Invoke the drawing method fo the image class, which will draw the location background
|
|
CORO_INVOKE_2(_buf->draw, bigBuf, prim);
|
|
|
|
if (_ctx->hasChanges) {
|
|
_prevScroll = _curScroll;
|
|
_prevFixedScroll = _fixedScroll;
|
|
}
|
|
bigBuf.setTrackDirtyRects(_ctx->priorTracking);
|
|
|
|
CORO_END_CODE;
|
|
}
|
|
|
|
/**
|
|
* Prepare a frame, adding the location to the OT list, and all the items that have changed animation frame.
|
|
*/
|
|
void RMLocation::doFrame(RMGfxTargetBuffer *bigBuf) {
|
|
// If the location is not in the OT list, add it in
|
|
if (!_nInList)
|
|
bigBuf->addPrim(new RMGfxPrimitive(this));
|
|
|
|
// Process all the location items
|
|
for (int i = 0; i < _nItems; i++)
|
|
_items[i].doFrame(bigBuf);
|
|
}
|
|
|
|
RMItem *RMLocation::getItemFromCode(uint32 dwCode) {
|
|
for (int i = 0; i < _nItems; i++) {
|
|
if (_items[i].mpalCode() == (int)dwCode)
|
|
return &_items[i];
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
RMItem *RMLocation::whichItemIsIn(const RMPoint &pt) {
|
|
int found = -1;
|
|
int foundSize = 0;
|
|
int size;
|
|
|
|
for (int i = 0; i < _nItems; i++) {
|
|
size = 0;
|
|
if (_items[i].isIn(pt, &size)) {
|
|
if (found == -1 || size < foundSize) {
|
|
foundSize = size;
|
|
found = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (found == -1)
|
|
return NULL;
|
|
else
|
|
return &_items[found];
|
|
}
|
|
|
|
RMLocation::~RMLocation() {
|
|
unload();
|
|
}
|
|
|
|
void RMLocation::unload() {
|
|
// Clear memory
|
|
if (_items) {
|
|
delete[] _items;
|
|
_items = NULL;
|
|
}
|
|
|
|
// Destroy the buffer
|
|
if (_buf) {
|
|
delete _buf;
|
|
_buf = NULL;
|
|
}
|
|
}
|
|
|
|
void RMLocation::updateScrolling(const RMPoint &ptShowThis) {
|
|
RMPoint oldScroll = _curScroll;
|
|
|
|
if (_curScroll._x + 250 > ptShowThis._x) {
|
|
_curScroll._x = ptShowThis._x - 250;
|
|
} else if (_curScroll._x + RM_SX - 250 < ptShowThis._x) {
|
|
_curScroll._x = ptShowThis._x + 250 - RM_SX;
|
|
} else if (ABS(_curScroll._x + RM_SX / 2 - ptShowThis._x) > 32 && _buf->getDimx() > RM_SX) {
|
|
if (_curScroll._x + RM_SX / 2 < ptShowThis._x)
|
|
_curScroll._x++;
|
|
else
|
|
_curScroll._x--;
|
|
}
|
|
|
|
if (_curScroll._y + 180 > ptShowThis._y) {
|
|
_curScroll._y = ptShowThis._y - 180;
|
|
} else if (_curScroll._y + RM_SY - 180 < ptShowThis._y) {
|
|
_curScroll._y = ptShowThis._y + 180 - RM_SY;
|
|
} else if (ABS(_curScroll._y + RM_SY / 2 - ptShowThis._y) > 16 && _buf->getDimy() > RM_SY) {
|
|
if (_curScroll._y + RM_SY / 2 < ptShowThis._y)
|
|
_curScroll._y++;
|
|
else
|
|
_curScroll._y--;
|
|
}
|
|
|
|
if (_curScroll._x < 0)
|
|
_curScroll._x = 0;
|
|
if (_curScroll._y < 0)
|
|
_curScroll._y = 0;
|
|
if (_curScroll._x + RM_SX > _buf->getDimx())
|
|
_curScroll._x = _buf->getDimx() - RM_SX;
|
|
if (_curScroll._y + RM_SY > _buf->getDimy())
|
|
_curScroll._y = _buf->getDimy() - RM_SY;
|
|
|
|
if (oldScroll != _curScroll) {
|
|
for (int i = 0; i < _nItems; i++)
|
|
_items[i].setScrollPosition(_curScroll);
|
|
}
|
|
}
|
|
|
|
void RMLocation::setFixedScroll(const RMPoint &scroll) {
|
|
_fixedScroll = scroll;
|
|
|
|
for (int i = 0; i < _nItems; i++)
|
|
_items[i].setScrollPosition(_curScroll - _fixedScroll);
|
|
}
|
|
|
|
void RMLocation::setScrollPosition(const RMPoint &scroll) {
|
|
RMPoint pt = scroll;
|
|
if (pt._x < 0)
|
|
pt._x = 0;
|
|
if (pt._y < 0)
|
|
pt._y = 0;
|
|
if (pt._x + RM_SX > _buf->getDimx())
|
|
pt._x = _buf->getDimx() - RM_SX;
|
|
if (pt._y + RM_SY > _buf->getDimy())
|
|
pt._y = _buf->getDimy() - RM_SY;
|
|
|
|
_curScroll = pt;
|
|
|
|
for (int i = 0; i < _nItems; i++)
|
|
_items[i].setScrollPosition(_curScroll);
|
|
}
|
|
|
|
|
|
void RMLocation::pauseSound(bool bPause) {
|
|
for (int i = 0; i < _nItems; i++)
|
|
_items[i].pauseSound(bPause);
|
|
}
|
|
|
|
/**
|
|
* Read the current scroll position
|
|
*/
|
|
RMPoint RMLocation::scrollPosition() {
|
|
return _curScroll;
|
|
}
|
|
|
|
/****************************************************************************\
|
|
* RMMessage Methods
|
|
\****************************************************************************/
|
|
|
|
RMMessage::RMMessage(uint32 dwId) {
|
|
load(dwId);
|
|
}
|
|
|
|
RMMessage::RMMessage() {
|
|
_lpMessage = NULL;
|
|
_nPeriods = 0;
|
|
for (int i = 0; i < 256; i++)
|
|
_lpPeriods[i] = 0;
|
|
}
|
|
|
|
RMMessage::~RMMessage() {
|
|
if (_lpMessage)
|
|
globalDestroy(_lpMessage);
|
|
}
|
|
|
|
void RMMessage::load(uint32 dwId) {
|
|
_lpMessage = mpalQueryMessage(dwId);
|
|
assert(_lpMessage != NULL);
|
|
|
|
if (_lpMessage)
|
|
parseMessage();
|
|
}
|
|
|
|
void RMMessage::parseMessage() {
|
|
char *p;
|
|
|
|
assert(_lpMessage != NULL);
|
|
|
|
_nPeriods = 1;
|
|
p = _lpPeriods[0] = _lpMessage;
|
|
|
|
for (;;) {
|
|
// Find the end of the current period
|
|
while (*p != '\0')
|
|
p++;
|
|
|
|
// If there is another '0' at the end of the string, the end has been found
|
|
p++;
|
|
if (*p == '\0')
|
|
break;
|
|
|
|
// Otherwise there is another line, and remember it's start
|
|
_lpPeriods[_nPeriods++] = p;
|
|
}
|
|
}
|
|
|
|
bool RMMessage::isValid() {
|
|
return _lpMessage != NULL;
|
|
}
|
|
|
|
int RMMessage::numPeriods() {
|
|
return _nPeriods;
|
|
}
|
|
|
|
char *RMMessage::period(int num) {
|
|
return _lpPeriods[num];
|
|
}
|
|
|
|
char *RMMessage::operator[](int num) {
|
|
return _lpPeriods[num];
|
|
}
|
|
|
|
} // End of namespace Tony
|