mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-30 14:14:43 +00:00
88913c0139
This tries to make our code a bit more compliant with our code formatting conventions. For future use, this is the command I used: git ls-files "*.cpp" "*.h" | xargs sed -i -e 's/[ \t]*$//'
1661 lines
40 KiB
C++
1661 lines
40 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.
|
|
*
|
|
*/
|
|
|
|
// Isometric level module
|
|
|
|
#include "saga/saga.h"
|
|
#include "saga/gfx.h"
|
|
#include "saga/scene.h"
|
|
#include "saga/isomap.h"
|
|
#include "saga/render.h"
|
|
|
|
namespace Saga {
|
|
|
|
enum MaskRules {
|
|
kMaskRuleNever = 0,
|
|
kMaskRuleAlways,
|
|
kMaskRuleUMIN,
|
|
kMaskRuleUMID,
|
|
kMaskRuleUMAX,
|
|
kMaskRuleVMIN,
|
|
kMaskRuleVMID,
|
|
kMaskRuleVMAX,
|
|
kMaskRuleYMIN,
|
|
kMaskRuleYMID,
|
|
kMaskRuleYMAX,
|
|
kMaskRuleUVMAX,
|
|
kMaskRuleUVMIN,
|
|
kMaskRuleUorV,
|
|
kMaskRuleUandV
|
|
};
|
|
|
|
|
|
static const IsoMap::TilePoint normalDirTable[8] = {
|
|
{ 1, 1, 0, SAGA_DIAG_NORMAL_COST},
|
|
{ 1, 0, 0, SAGA_STRAIGHT_NORMAL_COST},
|
|
{ 1,-1, 0, SAGA_DIAG_NORMAL_COST},
|
|
{ 0,-1, 0, SAGA_STRAIGHT_NORMAL_COST},
|
|
{-1,-1, 0, SAGA_DIAG_NORMAL_COST},
|
|
{-1, 0, 0, SAGA_STRAIGHT_NORMAL_COST},
|
|
{-1, 1, 0, SAGA_DIAG_NORMAL_COST},
|
|
{ 0, 1, 0, SAGA_STRAIGHT_NORMAL_COST},
|
|
};
|
|
|
|
static const IsoMap::TilePoint easyDirTable[8] = {
|
|
{ 1, 1, 0, SAGA_DIAG_EASY_COST},
|
|
{ 1, 0, 0, SAGA_STRAIGHT_EASY_COST},
|
|
{ 1,-1, 0, SAGA_DIAG_EASY_COST},
|
|
{ 0,-1, 0, SAGA_STRAIGHT_EASY_COST},
|
|
{-1,-1, 0, SAGA_DIAG_EASY_COST},
|
|
{-1, 0, 0, SAGA_STRAIGHT_EASY_COST},
|
|
{-1, 1, 0, SAGA_DIAG_EASY_COST},
|
|
{ 0, 1, 0, SAGA_STRAIGHT_EASY_COST},
|
|
};
|
|
|
|
static const IsoMap::TilePoint hardDirTable[8] = {
|
|
{ 1, 1, 0, SAGA_DIAG_HARD_COST},
|
|
{ 1, 0, 0, SAGA_STRAIGHT_HARD_COST},
|
|
{ 1,-1, 0, SAGA_DIAG_HARD_COST},
|
|
{ 0,-1, 0, SAGA_STRAIGHT_HARD_COST},
|
|
{-1,-1, 0, SAGA_DIAG_HARD_COST},
|
|
{-1, 0, 0, SAGA_STRAIGHT_HARD_COST},
|
|
{-1, 1, 0, SAGA_DIAG_HARD_COST},
|
|
{ 0, 1, 0, SAGA_STRAIGHT_HARD_COST},
|
|
};
|
|
|
|
static const int16 directions[8][2] = {
|
|
{ 16, 16},
|
|
{ 16, 0},
|
|
{ 16, -16},
|
|
{ 0, -16},
|
|
{ -16, -16},
|
|
{ -16, 0},
|
|
{ -16, 16},
|
|
{ 0, 16}
|
|
};
|
|
|
|
IsoMap::IsoMap(SagaEngine *vm) : _vm(vm) {
|
|
_viewScroll.x = (128 - 8) * 16;
|
|
_viewScroll.x = (128 - 8) * 16 - 64;
|
|
_viewDiff = 1;
|
|
}
|
|
|
|
void IsoMap::loadImages(const ByteArray &resourceData) {
|
|
IsoTileData *tileData;
|
|
uint16 i;
|
|
size_t offsetDiff;
|
|
|
|
if (resourceData.empty()) {
|
|
error("IsoMap::loadImages wrong resourceLength");
|
|
}
|
|
|
|
|
|
ByteArrayReadStreamEndian readS(resourceData, _vm->isBigEndian());
|
|
readS.readUint16(); // skip
|
|
i = readS.readUint16();
|
|
i = i / SAGA_ISOTILEDATA_LEN;
|
|
_tilesTable.resize(i);
|
|
Common::Array<size_t> tempOffsets;
|
|
tempOffsets.resize(_tilesTable.size());
|
|
readS.seek(0);
|
|
|
|
|
|
for (i = 0; i < _tilesTable.size(); i++) {
|
|
tileData = &_tilesTable[i];
|
|
tileData->height = readS.readByte();
|
|
tileData->attributes = readS.readSByte();
|
|
tempOffsets[i] = readS.readUint16();
|
|
tileData->terrainMask = readS.readUint16();
|
|
tileData->FGDBGDAttr = readS.readByte();
|
|
readS.readByte(); //skip
|
|
}
|
|
|
|
offsetDiff = readS.pos();
|
|
|
|
_tileData.resize(resourceData.size() - offsetDiff);
|
|
memcpy(_tileData.getBuffer(), resourceData.getBuffer() + offsetDiff, _tileData.size());
|
|
|
|
for (i = 0; i < _tilesTable.size(); i++) {
|
|
_tilesTable[i].tilePointer = _tileData.getBuffer() + tempOffsets[i] - offsetDiff;
|
|
}
|
|
}
|
|
|
|
void IsoMap::loadPlatforms(const ByteArray &resourceData) {
|
|
TilePlatformData *tilePlatformData;
|
|
uint16 i, x, y;
|
|
|
|
if (resourceData.empty()) {
|
|
error("IsoMap::loadPlatforms wrong resourceLength");
|
|
}
|
|
|
|
ByteArrayReadStreamEndian readS(resourceData, _vm->isBigEndian());
|
|
|
|
i = resourceData.size() / SAGA_TILEPLATFORMDATA_LEN;
|
|
_tilePlatformList.resize(i);
|
|
|
|
for (i = 0; i < _tilePlatformList.size(); i++) {
|
|
tilePlatformData = &_tilePlatformList[i];
|
|
tilePlatformData->metaTile = readS.readSint16();
|
|
tilePlatformData->height = readS.readSint16();
|
|
tilePlatformData->highestPixel = readS.readSint16();
|
|
tilePlatformData->vBits = readS.readByte();
|
|
tilePlatformData->uBits = readS.readByte();
|
|
for (x = 0; x < SAGA_PLATFORM_W; x++) {
|
|
for (y = 0; y < SAGA_PLATFORM_W; y++) {
|
|
tilePlatformData->tiles[x][y] = readS.readSint16();
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void IsoMap::loadMap(const ByteArray &resourceData) {
|
|
uint16 x, y;
|
|
|
|
if (resourceData.size() != SAGA_TILEMAP_LEN) {
|
|
error("IsoMap::loadMap wrong resource length %d", resourceData.size());
|
|
}
|
|
|
|
ByteArrayReadStreamEndian readS(resourceData, _vm->isBigEndian());
|
|
_tileMap.edgeType = readS.readByte();
|
|
readS.readByte(); //skip
|
|
|
|
for (x = 0; x < SAGA_TILEMAP_W; x++) {
|
|
for (y = 0; y < SAGA_TILEMAP_H; y++) {
|
|
_tileMap.tilePlatforms[x][y] = readS.readSint16();
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
void IsoMap::loadMetaTiles(const ByteArray &resourceData) {
|
|
MetaTileData *metaTileData;
|
|
uint16 i, j;
|
|
|
|
if (resourceData.empty()) {
|
|
error("IsoMap::loadMetaTiles wrong resourceLength");
|
|
}
|
|
|
|
ByteArrayReadStreamEndian readS(resourceData, _vm->isBigEndian());
|
|
i = resourceData.size() / SAGA_METATILEDATA_LEN;
|
|
_metaTileList.resize(i);
|
|
|
|
for (i = 0; i < _metaTileList.size(); i++) {
|
|
metaTileData = &_metaTileList[i];
|
|
metaTileData->highestPlatform = readS.readUint16();
|
|
metaTileData->highestPixel = readS.readUint16();
|
|
for (j = 0; j < SAGA_MAX_PLATFORM_H; j++) {
|
|
metaTileData->stack[j] = readS.readSint16();
|
|
}
|
|
}
|
|
}
|
|
|
|
void IsoMap::loadMulti(const ByteArray &resourceData) {
|
|
MultiTileEntryData *multiTileEntryData;
|
|
uint16 i;
|
|
int16 offsetDiff;
|
|
|
|
if (resourceData.size() < 2) {
|
|
error("IsoMap::loadMetaTiles wrong resourceLength");
|
|
}
|
|
|
|
ByteArrayReadStreamEndian readS(resourceData, _vm->isBigEndian());
|
|
i = readS.readUint16();
|
|
_multiTable.resize(i);
|
|
|
|
for (i = 0; i < _multiTable.size(); i++) {
|
|
multiTileEntryData = &_multiTable[i];
|
|
readS.readUint32();//skip
|
|
multiTileEntryData->offset = readS.readSint16();
|
|
multiTileEntryData->u = readS.readByte();
|
|
multiTileEntryData->v = readS.readByte();
|
|
multiTileEntryData->h = readS.readByte();
|
|
multiTileEntryData->uSize = readS.readByte();
|
|
multiTileEntryData->vSize = readS.readByte();
|
|
multiTileEntryData->numStates = readS.readByte();
|
|
multiTileEntryData->currentState = readS.readByte();
|
|
readS.readByte();//skip
|
|
}
|
|
|
|
offsetDiff = (readS.pos() - 2);
|
|
|
|
for (i = 0; i < _multiTable.size(); i++) {
|
|
_multiTable[i].offset -= offsetDiff;
|
|
}
|
|
|
|
uint16 multiDataCount = (readS.size() - readS.pos()) / 2;
|
|
|
|
_multiTableData.resize(multiDataCount);
|
|
for (i = 0; i < _multiTableData.size(); i++) {
|
|
_multiTableData[i] = readS.readSint16();
|
|
}
|
|
}
|
|
|
|
void IsoMap::clear() {
|
|
_tilesTable.clear();
|
|
_tilePlatformList.clear();
|
|
_metaTileList.clear();
|
|
_multiTable.clear();
|
|
_tileData.clear();
|
|
_multiTableData.clear();
|
|
}
|
|
|
|
void IsoMap::adjustScroll(bool jump) {
|
|
Point playerPoint;
|
|
Point minScrollPos;
|
|
Point maxScrollPos;
|
|
|
|
|
|
tileCoordsToScreenPoint(_vm->_actor->_centerActor->_location, playerPoint);
|
|
|
|
if (_vm->_scene->currentSceneResourceId() == ITE_SCENE_OVERMAP) {
|
|
_mapPosition.x = (playerPoint.x + _viewScroll.x) * 30 / 100 - (381);
|
|
_mapPosition.y = (playerPoint.y + _viewScroll.y) * 30 / 100 - (342);
|
|
}
|
|
|
|
if (_vm->_actor->_centerActor != _vm->_actor->_protagonist) {
|
|
playerPoint.y -= 24;
|
|
}
|
|
playerPoint.y -= 28;
|
|
|
|
playerPoint.x += _viewScroll.x - _vm->getDisplayInfo().width/2;
|
|
playerPoint.y += _viewScroll.y - _vm->_scene->getHeight()/2;
|
|
|
|
minScrollPos.x = playerPoint.x - SAGA_SCROLL_LIMIT_X1;
|
|
minScrollPos.y = playerPoint.y - SAGA_SCROLL_LIMIT_Y1;
|
|
|
|
maxScrollPos.x = playerPoint.x + SAGA_SCROLL_LIMIT_X1;
|
|
maxScrollPos.y = playerPoint.y + SAGA_SCROLL_LIMIT_Y2;
|
|
|
|
if (jump) {
|
|
if (_viewScroll.y < minScrollPos.y) {
|
|
_viewScroll.y = minScrollPos.y;
|
|
}
|
|
if (_viewScroll.y > maxScrollPos.y) {
|
|
_viewScroll.y = maxScrollPos.y;
|
|
}
|
|
if (_viewScroll.x < minScrollPos.x) {
|
|
_viewScroll.x = minScrollPos.x;
|
|
}
|
|
if (_viewScroll.x > maxScrollPos.x) {
|
|
_viewScroll.x = maxScrollPos.x;
|
|
}
|
|
} else {
|
|
_viewScroll.y = smoothSlide(_viewScroll.y, minScrollPos.y, maxScrollPos.y);
|
|
_viewScroll.x = smoothSlide(_viewScroll.x, minScrollPos.x, maxScrollPos.x);
|
|
}
|
|
|
|
if (_vm->_scene->currentSceneResourceId() == ITE_SCENE_OVERMAP) {
|
|
ObjectData *obj;
|
|
uint16 objectId;
|
|
objectId = _vm->_actor->objIndexToId(ITE_OBJ_MAP);
|
|
obj = _vm->_actor->getObj(objectId);
|
|
if (obj->_sceneNumber != ITE_SCENE_INV) {
|
|
_viewScroll.x = 1552 + 8;
|
|
_viewScroll.y = 1456 + 8;
|
|
}
|
|
}
|
|
}
|
|
|
|
int16 IsoMap::findMulti(int16 tileIndex, int16 absU, int16 absV, int16 absH) {
|
|
MultiTileEntryData *multiTileEntryData;
|
|
int16 ru;
|
|
int16 rv;
|
|
int16 mu;
|
|
int16 mv;
|
|
int16 state;
|
|
uint16 i, offset;
|
|
int16 *tiles;
|
|
|
|
ru = (tileIndex >> 13) & 0x03;
|
|
rv = (tileIndex >> 11) & 0x03;
|
|
mu = absU - ru;
|
|
mv = absV - rv;
|
|
|
|
tileIndex = 0;
|
|
for (i = 0; i < _multiTable.size(); i++) {
|
|
multiTileEntryData = &_multiTable[i];
|
|
|
|
if ((multiTileEntryData->u == mu) &&
|
|
(multiTileEntryData->v == mv) &&
|
|
(multiTileEntryData->h == absH)) {
|
|
state = multiTileEntryData->currentState;
|
|
|
|
offset = (ru + state * multiTileEntryData->uSize) * multiTileEntryData->vSize + rv;
|
|
offset *= sizeof(int16);
|
|
offset += multiTileEntryData->offset;
|
|
if (offset + sizeof(int16) > _multiTableData.size() * sizeof(int16)) {
|
|
error("wrong multiTileEntryData->offset");
|
|
}
|
|
tiles = (int16*)((byte*)&_multiTableData.front() + offset);
|
|
tileIndex = *tiles;
|
|
if (tileIndex >= 256) {
|
|
warning("something terrible happened");
|
|
return 1;
|
|
}
|
|
return tileIndex;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
void IsoMap::draw() {
|
|
_tileClip = _vm->_scene->getSceneClip();
|
|
_vm->_gfx->drawRect(_tileClip, 0);
|
|
drawTiles(NULL);
|
|
}
|
|
|
|
void IsoMap::setMapPosition(int x, int y) {
|
|
_mapPosition.x = x;
|
|
_mapPosition.y = y;
|
|
}
|
|
|
|
void IsoMap::drawSprite(SpriteList &spriteList, int spriteNumber, const Location &location, const Point &screenPosition, int scale) {
|
|
int width;
|
|
int height;
|
|
int xAlign;
|
|
int yAlign;
|
|
const byte *spriteBuffer;
|
|
Point spritePointer;
|
|
|
|
_vm->_sprite->getScaledSpriteBuffer(spriteList, spriteNumber, scale, width, height, xAlign, yAlign, spriteBuffer);
|
|
|
|
spritePointer.x = screenPosition.x + xAlign;
|
|
spritePointer.y = screenPosition.y + yAlign;
|
|
|
|
_tileClip.left = CLIP<int>(spritePointer.x, 0, _vm->getDisplayInfo().width);
|
|
_tileClip.right = CLIP<int>(spritePointer.x + width, 0, _vm->getDisplayInfo().width);
|
|
_tileClip.top = CLIP<int>(spritePointer.y, 0, _vm->_scene->getHeight());
|
|
_tileClip.bottom = CLIP<int>(spritePointer.y + height, 0, _vm->_scene->getHeight());
|
|
|
|
_vm->_sprite->drawClip(spritePointer, width, height, spriteBuffer, true);
|
|
drawTiles(&location);
|
|
}
|
|
|
|
|
|
void IsoMap::drawTiles(const Location *location) {
|
|
Point view1;
|
|
Point fineScroll;
|
|
Point tileScroll;
|
|
Point metaTileY;
|
|
Point metaTileX;
|
|
int16 u0, v0,
|
|
u1, v1,
|
|
u2, v2,
|
|
uc, vc;
|
|
uint16 metaTileIndex;
|
|
Location rLocation;
|
|
int16 workAreaWidth;
|
|
int16 workAreaHeight;
|
|
|
|
tileScroll.x = _viewScroll.x >> 4;
|
|
tileScroll.y = _viewScroll.y >> 4;
|
|
|
|
fineScroll.x = _viewScroll.x & 0xf;
|
|
fineScroll.y = _viewScroll.y & 0xf;
|
|
|
|
view1.x = tileScroll.x - (8 * SAGA_TILEMAP_W);
|
|
view1.y = (8 * SAGA_TILEMAP_W) - tileScroll.y;
|
|
|
|
u0 = ((view1.y + 64) * 2 + view1.x) >> 4;
|
|
v0 = ((view1.y + 64) * 2 - view1.x) >> 4;
|
|
|
|
metaTileY.x = (u0 - v0) * 128 - (view1.x * 16 + fineScroll.x);
|
|
metaTileY.y = (view1.y * 16 - fineScroll.y) - (u0 + v0) * 64;
|
|
|
|
workAreaWidth = _vm->getDisplayInfo().width + 128;
|
|
workAreaHeight = _vm->_scene->getHeight() + 128 + 80;
|
|
|
|
for (u1 = u0, v1 = v0; metaTileY.y < workAreaHeight; u1--, v1--) {
|
|
metaTileX = metaTileY;
|
|
|
|
for (u2 = u1, v2 = v1; metaTileX.x < workAreaWidth; u2++, v2--, metaTileX.x += 256) {
|
|
|
|
uc = u2 & (SAGA_TILEMAP_W - 1);
|
|
vc = v2 & (SAGA_TILEMAP_W - 1);
|
|
|
|
if (uc != u2 || vc != v2) {
|
|
metaTileIndex = 0;
|
|
switch ( _tileMap.edgeType) {
|
|
case kEdgeTypeBlack:
|
|
continue;
|
|
case kEdgeTypeFill0:
|
|
break;
|
|
case kEdgeTypeFill1:
|
|
metaTileIndex = 1;
|
|
break;
|
|
case kEdgeTypeRpt:
|
|
uc = CLIP<int16>(u2, 0, SAGA_TILEMAP_W - 1);
|
|
vc = CLIP<int16>(v2, 0, SAGA_TILEMAP_W - 1);
|
|
metaTileIndex = _tileMap.tilePlatforms[uc][vc];
|
|
break;
|
|
case kEdgeTypeWrap:
|
|
metaTileIndex = _tileMap.tilePlatforms[uc][vc];
|
|
break;
|
|
}
|
|
} else {
|
|
metaTileIndex = _tileMap.tilePlatforms[uc][vc];
|
|
}
|
|
|
|
if (location != NULL) {
|
|
rLocation.u() = location->u() - (u2 << 7);
|
|
rLocation.v() = location->v() - (v2 << 7);
|
|
rLocation.z = location->z;
|
|
drawSpriteMetaTile(metaTileIndex, metaTileX, rLocation, u2 << 3, v2 << 3);
|
|
} else {
|
|
drawMetaTile(metaTileIndex, metaTileX, u2 << 3, v2 << 3);
|
|
}
|
|
}
|
|
|
|
metaTileY.y += 64;
|
|
|
|
metaTileX = metaTileY;
|
|
|
|
metaTileX.x -= 128;
|
|
|
|
for (u2 = u1 - 1, v2 = v1; metaTileX.x < workAreaWidth; u2++, v2--, metaTileX.x += 256) {
|
|
|
|
uc = u2 & (SAGA_TILEMAP_W - 1);
|
|
vc = v2 & (SAGA_TILEMAP_W - 1);
|
|
|
|
if (uc != u2 || vc != v2) {
|
|
metaTileIndex = 0;
|
|
switch ( _tileMap.edgeType) {
|
|
case kEdgeTypeBlack:
|
|
continue;
|
|
case kEdgeTypeFill0:
|
|
break;
|
|
case kEdgeTypeFill1:
|
|
metaTileIndex = 1;
|
|
break;
|
|
case kEdgeTypeRpt:
|
|
uc = CLIP<int16>(u2, 0, SAGA_TILEMAP_W - 1);
|
|
vc = CLIP<int16>(v2, 0, SAGA_TILEMAP_W - 1);
|
|
metaTileIndex = _tileMap.tilePlatforms[uc][vc];
|
|
break;
|
|
case kEdgeTypeWrap:
|
|
metaTileIndex = _tileMap.tilePlatforms[uc][vc];
|
|
break;
|
|
}
|
|
} else {
|
|
metaTileIndex = _tileMap.tilePlatforms[uc][vc];
|
|
}
|
|
|
|
if (location != NULL) {
|
|
rLocation.u() = location->u() - (u2 << 7);
|
|
rLocation.v() = location->v() - (v2 << 7);
|
|
rLocation.z = location->z;
|
|
drawSpriteMetaTile(metaTileIndex, metaTileX, rLocation, u2 << 3, v2 << 3);
|
|
} else {
|
|
drawMetaTile(metaTileIndex, metaTileX, u2 << 3, v2 << 3);
|
|
}
|
|
}
|
|
metaTileY.y += 64;
|
|
}
|
|
|
|
}
|
|
|
|
void IsoMap::drawSpriteMetaTile(uint16 metaTileIndex, const Point &point, Location &location, int16 absU, int16 absV) {
|
|
MetaTileData * metaTile;
|
|
uint16 high;
|
|
int16 platformIndex;
|
|
Point platformPoint;
|
|
platformPoint = point;
|
|
|
|
if (_metaTileList.size() <= metaTileIndex) {
|
|
error("IsoMap::drawMetaTile wrong metaTileIndex");
|
|
}
|
|
|
|
metaTile = &_metaTileList[metaTileIndex];
|
|
|
|
if (metaTile->highestPlatform > 18) {
|
|
metaTile->highestPlatform = 0;
|
|
}
|
|
|
|
for (high = 0; high <= metaTile->highestPlatform; high++, platformPoint.y -= 8, location.z -= 8) {
|
|
assert(SAGA_MAX_PLATFORM_H > high);
|
|
platformIndex = metaTile->stack[high];
|
|
|
|
if (platformIndex >= 0) {
|
|
drawSpritePlatform(platformIndex, platformPoint, location, absU, absV, high);
|
|
}
|
|
}
|
|
}
|
|
|
|
void IsoMap::drawMetaTile(uint16 metaTileIndex, const Point &point, int16 absU, int16 absV) {
|
|
MetaTileData * metaTile;
|
|
uint16 high;
|
|
int16 platformIndex;
|
|
Point platformPoint;
|
|
platformPoint = point;
|
|
|
|
if (_metaTileList.size() <= metaTileIndex) {
|
|
error("IsoMap::drawMetaTile wrong metaTileIndex");
|
|
}
|
|
|
|
metaTile = &_metaTileList[metaTileIndex];
|
|
|
|
if (metaTile->highestPlatform > 18) {
|
|
metaTile->highestPlatform = 0;
|
|
}
|
|
|
|
for (high = 0; high <= metaTile->highestPlatform; high++, platformPoint.y -= 8) {
|
|
assert(SAGA_MAX_PLATFORM_H > high);
|
|
platformIndex = metaTile->stack[high];
|
|
|
|
if (platformIndex >= 0) {
|
|
drawPlatform(platformIndex, platformPoint, absU, absV, high);
|
|
}
|
|
}
|
|
}
|
|
|
|
void IsoMap::drawSpritePlatform(uint16 platformIndex, const Point &point, const Location &location, int16 absU, int16 absV, int16 absH) {
|
|
TilePlatformData *tilePlatform;
|
|
int16 u, v;
|
|
Point s;
|
|
Point s0;
|
|
uint16 tileIndex;
|
|
Location copyLocation(location);
|
|
|
|
if (_tilePlatformList.size() <= platformIndex) {
|
|
error("IsoMap::drawPlatform wrong platformIndex");
|
|
}
|
|
|
|
tilePlatform = &_tilePlatformList[platformIndex];
|
|
|
|
if ((point.y <= _tileClip.top) || (point.y - SAGA_MAX_TILE_H - SAGA_PLATFORM_W * SAGA_TILE_NOMINAL_H >= _tileClip.bottom)) {
|
|
return;
|
|
}
|
|
|
|
s0 = point;
|
|
s0.y -= (((SAGA_PLATFORM_W - 1) + (SAGA_PLATFORM_W - 1)) * 8);
|
|
|
|
for (v = SAGA_PLATFORM_W - 1,
|
|
copyLocation.v() = location.v() - ((SAGA_PLATFORM_W - 1) << 4);
|
|
v >= 0 && s0.y - SAGA_MAX_TILE_H < _tileClip.bottom && s0.x - 128 < _tileClip.right;
|
|
v--, copyLocation.v() += 16, s0.x += 16, s0.y += 8) {
|
|
|
|
if ((tilePlatform->vBits & (1 << v)) == 0) {
|
|
continue;
|
|
}
|
|
|
|
if (s0.x + 128 + 32 < _tileClip.left) {
|
|
continue;
|
|
}
|
|
|
|
s = s0;
|
|
|
|
for (u = SAGA_PLATFORM_W - 1,
|
|
copyLocation.u() = location.u() - ((SAGA_PLATFORM_W - 1) << 4);
|
|
u >= 0 && s.x + 32 > _tileClip.left && s.y - SAGA_MAX_TILE_H < _tileClip.bottom;
|
|
u--, copyLocation.u() += 16, s.x -= 16, s.y += 8) {
|
|
if (s.x < _tileClip.right && s.y > _tileClip.top) {
|
|
|
|
tileIndex = tilePlatform->tiles[u][v];
|
|
if (tileIndex != 0) {
|
|
if (tileIndex & SAGA_MULTI_TILE) {
|
|
tileIndex = findMulti(tileIndex, absU + u, absV + v, absH);
|
|
}
|
|
|
|
drawTile(tileIndex, s, ©Location);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void IsoMap::drawPlatform(uint16 platformIndex, const Point &point, int16 absU, int16 absV, int16 absH) {
|
|
TilePlatformData *tilePlatform;
|
|
int16 u, v;
|
|
Point s;
|
|
Point s0;
|
|
uint16 tileIndex;
|
|
|
|
if (_tilePlatformList.size() <= platformIndex) {
|
|
error("IsoMap::drawPlatform wrong platformIndex");
|
|
}
|
|
|
|
tilePlatform = &_tilePlatformList[platformIndex];
|
|
|
|
if ((point.y <= _tileClip.top) || (point.y - SAGA_MAX_TILE_H - SAGA_PLATFORM_W * SAGA_TILE_NOMINAL_H >= _tileClip.bottom)) {
|
|
return;
|
|
}
|
|
|
|
s0 = point;
|
|
s0.y -= (((SAGA_PLATFORM_W - 1) + (SAGA_PLATFORM_W - 1)) * 8);
|
|
|
|
for (v = SAGA_PLATFORM_W - 1;
|
|
v >= 0 && s0.y - SAGA_MAX_TILE_H < _tileClip.bottom && s0.x - 128 < _tileClip.right;
|
|
v--, s0.x += 16, s0.y += 8) {
|
|
|
|
if ((tilePlatform->vBits & (1 << v)) == 0) {
|
|
continue;
|
|
}
|
|
|
|
if (s0.x + 128 + 32 < _tileClip.left) {
|
|
continue;
|
|
}
|
|
|
|
s = s0;
|
|
|
|
for (u = SAGA_PLATFORM_W - 1;
|
|
u >= 0 && s.x + 32 > _tileClip.left && s.y - SAGA_MAX_TILE_H < _tileClip.bottom;
|
|
u--, s.x -= 16, s.y += 8) {
|
|
if (s.x < _tileClip.right && s.y > _tileClip.top) {
|
|
|
|
tileIndex = tilePlatform->tiles[u][v];
|
|
if (tileIndex > 1) {
|
|
if (tileIndex & SAGA_MULTI_TILE) {
|
|
tileIndex = findMulti(tileIndex, absU + u, absV + v, absH);
|
|
}
|
|
|
|
drawTile(tileIndex, s, NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#define THRESH0 0
|
|
#define THRESH8 8
|
|
#define THRESH16 16
|
|
|
|
void IsoMap::drawTile(uint16 tileIndex, const Point &point, const Location *location) {
|
|
const byte *tilePointer;
|
|
const byte *readPointer;
|
|
byte *drawPointer;
|
|
Point drawPoint;
|
|
int height;
|
|
int widthCount = 0;
|
|
int row, col, count, lowBound;
|
|
int bgRunCount;
|
|
int fgRunCount;
|
|
|
|
if (tileIndex >= _tilesTable.size()) {
|
|
error("IsoMap::drawTile wrong tileIndex");
|
|
}
|
|
|
|
|
|
if (point.x + SAGA_ISOTILE_WIDTH < _tileClip.left) {
|
|
return;
|
|
}
|
|
|
|
if (point.x - SAGA_ISOTILE_WIDTH >= _tileClip.right) {
|
|
return;
|
|
}
|
|
|
|
tilePointer = _tilesTable[tileIndex].tilePointer;
|
|
height = _tilesTable[tileIndex].height;
|
|
|
|
if ((height <= 8) || (height > 64)) {
|
|
return;
|
|
}
|
|
|
|
drawPoint = point;
|
|
drawPoint.y -= height;
|
|
|
|
if (drawPoint.y >= _tileClip.bottom) {
|
|
return;
|
|
}
|
|
|
|
if (location != NULL) {
|
|
if (location->z <= -16) {
|
|
if (location->z <= -48) {
|
|
if (location->u() < -THRESH8 || location->v() < -THRESH8) {
|
|
return;
|
|
}
|
|
} else {
|
|
if (location->u() < THRESH0 || location->v() < THRESH0) {
|
|
return;
|
|
}
|
|
}
|
|
} else {
|
|
if (location->z >= 16) {
|
|
return;
|
|
} else {
|
|
switch (_tilesTable[tileIndex].GetMaskRule()) {
|
|
case kMaskRuleNever:
|
|
return;
|
|
case kMaskRuleAlways:
|
|
break;
|
|
case kMaskRuleUMIN:
|
|
if (location->u() < THRESH0) {
|
|
return;
|
|
}
|
|
break;
|
|
case kMaskRuleUMID:
|
|
if (location->u() < THRESH8) {
|
|
return;
|
|
}
|
|
break;
|
|
case kMaskRuleUMAX:
|
|
if (location->u() < THRESH16) {
|
|
return;
|
|
}
|
|
break;
|
|
case kMaskRuleVMIN:
|
|
if (location->v() < THRESH0) {
|
|
return;
|
|
}
|
|
break;
|
|
case kMaskRuleVMID:
|
|
if (location->v() < THRESH8) {
|
|
return;
|
|
}
|
|
break;
|
|
case kMaskRuleVMAX:
|
|
if (location->v() < THRESH16) {
|
|
return;
|
|
}
|
|
break;
|
|
case kMaskRuleYMIN:
|
|
if (location->uv() < THRESH0 * 2) {
|
|
return;
|
|
}
|
|
break;
|
|
case kMaskRuleYMID:
|
|
if (location->uv() < THRESH8 * 2) {
|
|
return;
|
|
}
|
|
break;
|
|
case kMaskRuleYMAX:
|
|
if (location->uv() < THRESH16 * 2) {
|
|
return;
|
|
}
|
|
break;
|
|
case kMaskRuleUVMAX:
|
|
if (location->u() < THRESH16 && location->v() < THRESH16) {
|
|
return;
|
|
}
|
|
break;
|
|
case kMaskRuleUVMIN:
|
|
if (location->u() < THRESH0 || location->v() < THRESH0) {
|
|
return;
|
|
}
|
|
break;
|
|
case kMaskRuleUorV:
|
|
if (location->u() < THRESH8 && location->v() < THRESH8) {
|
|
return;
|
|
}
|
|
break;
|
|
case kMaskRuleUandV:
|
|
if (location->u() < THRESH8 || location->v() < THRESH8) {
|
|
return;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
readPointer = tilePointer;
|
|
lowBound = MIN((int)(drawPoint.y + height), (int)_tileClip.bottom);
|
|
for (row = drawPoint.y; row < lowBound; row++) {
|
|
widthCount = 0;
|
|
if (row >= _tileClip.top) {
|
|
drawPointer = _vm->_gfx->getBackBufferPixels() + drawPoint.x + (row * _vm->_gfx->getBackBufferPitch());
|
|
col = drawPoint.x;
|
|
for (;;) {
|
|
bgRunCount = *readPointer++;
|
|
widthCount += bgRunCount;
|
|
if (widthCount >= SAGA_ISOTILE_WIDTH) {
|
|
break;
|
|
}
|
|
|
|
drawPointer += bgRunCount;
|
|
col += bgRunCount;
|
|
fgRunCount = *readPointer++;
|
|
widthCount += fgRunCount;
|
|
|
|
count = 0;
|
|
int colDiff = _tileClip.left - col;
|
|
if (colDiff > 0) {
|
|
if (colDiff > fgRunCount) {
|
|
colDiff = fgRunCount;
|
|
}
|
|
count = colDiff;
|
|
col += colDiff;
|
|
}
|
|
|
|
colDiff = _tileClip.right - col;
|
|
if (colDiff > 0) {
|
|
int countDiff = fgRunCount - count;
|
|
if (colDiff > countDiff) {
|
|
colDiff = countDiff;
|
|
}
|
|
if (colDiff > 0) {
|
|
byte *dst = (byte *)(drawPointer + count);
|
|
assert(_vm->_gfx->getBackBufferPixels() <= dst);
|
|
assert((_vm->_gfx->getBackBufferPixels() + (_vm->getDisplayInfo().width * _vm->getDisplayInfo().height)) >= (byte *)(dst + colDiff));
|
|
memcpy(dst, (readPointer + count), colDiff);
|
|
col += colDiff;
|
|
}
|
|
}
|
|
|
|
readPointer += fgRunCount;
|
|
drawPointer += fgRunCount;
|
|
}
|
|
} else {
|
|
for (;;) {
|
|
bgRunCount = *readPointer++;
|
|
widthCount += bgRunCount;
|
|
if (widthCount >= SAGA_ISOTILE_WIDTH) {
|
|
break;
|
|
}
|
|
|
|
fgRunCount = *readPointer++;
|
|
widthCount += fgRunCount;
|
|
|
|
readPointer += fgRunCount;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Compute dirty rect
|
|
int rectX = MAX<int>(drawPoint.x, 0);
|
|
int rectY = MAX<int>(drawPoint.y, 0);
|
|
int rectX2 = MIN<int>(drawPoint.x + SAGA_ISOTILE_WIDTH, _tileClip.right);
|
|
int rectY2 = lowBound;
|
|
_vm->_render->addDirtyRect(Common::Rect(rectX, rectY, rectX2, rectY2));
|
|
}
|
|
|
|
bool IsoMap::checkDragonPoint(int16 u, int16 v, uint16 direction) {
|
|
DragonPathCell *pathCell;
|
|
|
|
if ((u < 1) || (u >= SAGA_DRAGON_SEARCH_DIAMETER - 1) || (v < 1) || (v >= SAGA_DRAGON_SEARCH_DIAMETER - 1)) {
|
|
return false;
|
|
}
|
|
|
|
pathCell = _dragonSearchArray.getPathCell(u, v);
|
|
|
|
if (pathCell->visited) {
|
|
return false;
|
|
}
|
|
|
|
pathCell->visited = 1;
|
|
pathCell->direction = direction;
|
|
return true;
|
|
}
|
|
|
|
void IsoMap::pushDragonPoint(int16 u, int16 v, uint16 direction) {
|
|
DragonTilePoint *tilePoint;
|
|
DragonPathCell *pathCell;
|
|
|
|
if ((u < 1) || (u >= SAGA_DRAGON_SEARCH_DIAMETER - 1) || (v < 1) || (v >= SAGA_DRAGON_SEARCH_DIAMETER - 1)) {
|
|
return;
|
|
}
|
|
|
|
pathCell = _dragonSearchArray.getPathCell(u, v);
|
|
|
|
if (pathCell->visited) {
|
|
return;
|
|
}
|
|
|
|
tilePoint = _dragonSearchArray.getQueue(_queueCount);
|
|
_queueCount++;
|
|
if (_queueCount >= SAGA_SEARCH_QUEUE_SIZE) {
|
|
_queueCount = 0;
|
|
}
|
|
|
|
tilePoint->u = u;
|
|
tilePoint->v = v;
|
|
tilePoint->direction = direction;
|
|
|
|
pathCell->visited = 1;
|
|
pathCell->direction = direction;
|
|
}
|
|
|
|
void IsoMap::pushPoint(int16 u, int16 v, uint16 cost, uint16 direction) {
|
|
int16 upper;
|
|
int16 lower;
|
|
int16 mid;
|
|
TilePoint *tilePoint;
|
|
PathCell *pathCell;
|
|
|
|
upper = _queueCount;
|
|
lower = 0;
|
|
|
|
if ((u < 1) || (u >= SAGA_SEARCH_DIAMETER - 1) || (v < 1) || (v >= SAGA_SEARCH_DIAMETER - 1)) {
|
|
return;
|
|
}
|
|
|
|
pathCell = _searchArray.getPathCell(u, v);
|
|
|
|
if ((pathCell->visited) && (pathCell->cost <= cost)) {
|
|
return;
|
|
}
|
|
|
|
if (_queueCount >= SAGA_SEARCH_QUEUE_SIZE) {
|
|
return;
|
|
}
|
|
|
|
while (1) {
|
|
mid = (upper + lower) / 2;
|
|
tilePoint = _searchArray.getQueue(mid);
|
|
|
|
if (upper <= lower) {
|
|
break;
|
|
}
|
|
|
|
if (cost < tilePoint->cost) {
|
|
lower = mid + 1;
|
|
} else {
|
|
upper = mid;
|
|
}
|
|
}
|
|
|
|
if (mid < _queueCount) {
|
|
memmove(tilePoint + 1, tilePoint, (_queueCount - mid) * sizeof (*tilePoint));
|
|
}
|
|
_queueCount++;
|
|
|
|
tilePoint->u = u;
|
|
tilePoint->v = v;
|
|
tilePoint->cost = cost;
|
|
tilePoint->direction = direction;
|
|
|
|
pathCell->visited = 1;
|
|
pathCell->direction = direction;
|
|
pathCell->cost = cost;
|
|
}
|
|
|
|
int16 IsoMap::getTileIndex(int16 u, int16 v, int16 z) {
|
|
int16 mtileU;
|
|
int16 mtileV;
|
|
int16 uc;
|
|
int16 vc;
|
|
int16 u0;
|
|
int16 v0;
|
|
int16 platformIndex;
|
|
int16 metaTileIndex;
|
|
|
|
mtileU = u >> 3;
|
|
mtileV = v >> 3;
|
|
uc = mtileU & (SAGA_TILEMAP_W - 1);
|
|
vc = mtileV & (SAGA_TILEMAP_W - 1);
|
|
u0 = u & (SAGA_PLATFORM_W - 1);
|
|
v0 = v & (SAGA_PLATFORM_W - 1);
|
|
|
|
if ((uc != mtileU) || (vc != mtileV)) {
|
|
metaTileIndex = 0;
|
|
switch ( _tileMap.edgeType) {
|
|
case kEdgeTypeBlack:
|
|
return 0;
|
|
case kEdgeTypeFill0:
|
|
break;
|
|
case kEdgeTypeFill1:
|
|
metaTileIndex = 1;
|
|
break;
|
|
case kEdgeTypeRpt:
|
|
uc = CLIP<int16>(mtileU, 0, SAGA_TILEMAP_W - 1);
|
|
vc = CLIP<int16>(mtileV, 0, SAGA_TILEMAP_W - 1);
|
|
metaTileIndex = _tileMap.tilePlatforms[uc][vc];
|
|
break;
|
|
case kEdgeTypeWrap:
|
|
metaTileIndex = _tileMap.tilePlatforms[uc][vc];
|
|
break;
|
|
}
|
|
} else {
|
|
metaTileIndex = _tileMap.tilePlatforms[uc][vc];
|
|
}
|
|
|
|
if (_metaTileList.size() <= (uint)metaTileIndex) {
|
|
error("IsoMap::getTile wrong metaTileIndex");
|
|
}
|
|
|
|
platformIndex = _metaTileList[metaTileIndex].stack[z];
|
|
if (platformIndex < 0) {
|
|
return 0;
|
|
}
|
|
|
|
if (_tilePlatformList.size() <= (uint)platformIndex) {
|
|
error("IsoMap::getTile wrong platformIndex");
|
|
}
|
|
|
|
return _tilePlatformList[platformIndex].tiles[u0][v0];
|
|
}
|
|
|
|
IsoTileData *IsoMap::getTile(int16 u, int16 v, int16 z) {
|
|
int16 tileIndex;
|
|
|
|
tileIndex = getTileIndex(u, v, z);
|
|
|
|
if (tileIndex == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
if (tileIndex & SAGA_MULTI_TILE) {
|
|
tileIndex = findMulti(tileIndex, u, v, z);
|
|
}
|
|
|
|
return &_tilesTable[tileIndex];
|
|
}
|
|
|
|
void IsoMap::testPossibleDirections(int16 u, int16 v, uint16 terraComp[8], int skipCenter) {
|
|
IsoTileData *tile;
|
|
uint16 fgdMask;
|
|
uint16 bgdMask;
|
|
uint16 mask;
|
|
|
|
|
|
memset(terraComp, 0, 8 * sizeof(uint16));
|
|
|
|
#define FILL_MASK(index, testMask) \
|
|
if (mask & testMask) { \
|
|
terraComp[index] |= fgdMask; \
|
|
} \
|
|
if (~mask & testMask) { \
|
|
terraComp[index] |= bgdMask; \
|
|
}
|
|
|
|
#define TEST_TILE_PROLOG(offsetU, offsetV) \
|
|
tile = getTile(u + offsetU, v + offsetV , _platformHeight); \
|
|
if (tile != NULL) { \
|
|
fgdMask = tile->GetFGDMask(); \
|
|
bgdMask = tile->GetBGDMask(); \
|
|
mask = tile->terrainMask;
|
|
|
|
#define TEST_TILE_EPILOG(index) \
|
|
} else { \
|
|
if (_vm->_actor->_protagonist->_location.z > 0) { \
|
|
terraComp[index] = SAGA_IMPASSABLE; \
|
|
} \
|
|
}
|
|
|
|
#define TEST_TILE_END }
|
|
|
|
TEST_TILE_PROLOG(0, 0)
|
|
if (skipCenter) {
|
|
if ((mask & 0x0660) && (fgdMask & SAGA_IMPASSABLE)) {
|
|
fgdMask = 0;
|
|
}
|
|
if ((~mask & 0x0660) && (bgdMask & SAGA_IMPASSABLE)) {
|
|
bgdMask = 0;
|
|
}
|
|
}
|
|
|
|
FILL_MASK(0, 0xcc00)
|
|
FILL_MASK(1, 0x6600)
|
|
FILL_MASK(2, 0x3300)
|
|
FILL_MASK(3, 0x0330)
|
|
FILL_MASK(4, 0x0033)
|
|
FILL_MASK(5, 0x0066)
|
|
FILL_MASK(6, 0x00cc)
|
|
FILL_MASK(7, 0x0cc0)
|
|
TEST_TILE_END
|
|
|
|
TEST_TILE_PROLOG(1, 1)
|
|
FILL_MASK(0, 0x0673)
|
|
TEST_TILE_EPILOG(0)
|
|
|
|
|
|
TEST_TILE_PROLOG(1, 0)
|
|
FILL_MASK(0, 0x0008)
|
|
FILL_MASK(1, 0x0666)
|
|
FILL_MASK(2, 0x0001)
|
|
TEST_TILE_EPILOG(1)
|
|
|
|
|
|
TEST_TILE_PROLOG(1, -1)
|
|
FILL_MASK(2, 0x06ec)
|
|
TEST_TILE_EPILOG(2)
|
|
|
|
TEST_TILE_PROLOG(0, 1)
|
|
FILL_MASK(0, 0x1000)
|
|
FILL_MASK(7, 0x0770)
|
|
FILL_MASK(6, 0x0001)
|
|
TEST_TILE_EPILOG(7)
|
|
|
|
|
|
TEST_TILE_PROLOG(0, -1)
|
|
FILL_MASK(2, 0x8000)
|
|
FILL_MASK(3, 0x0ee0)
|
|
FILL_MASK(4, 0x0008)
|
|
TEST_TILE_EPILOG(3)
|
|
|
|
|
|
TEST_TILE_PROLOG(-1, 1)
|
|
FILL_MASK(6, 0x3670)
|
|
TEST_TILE_EPILOG(6)
|
|
|
|
|
|
TEST_TILE_PROLOG(-1, 0)
|
|
FILL_MASK(6, 0x8000)
|
|
FILL_MASK(5, 0x6660)
|
|
FILL_MASK(4, 0x1000)
|
|
TEST_TILE_EPILOG(5)
|
|
|
|
TEST_TILE_PROLOG(-1, -1)
|
|
FILL_MASK(4, 0xce60)
|
|
TEST_TILE_EPILOG(4)
|
|
}
|
|
|
|
void IsoMap::placeOnTileMap(const Location &start, Location &result, int16 distance, uint16 direction) {
|
|
int16 bestDistance;
|
|
int16 bestU;
|
|
int16 bestV;
|
|
int16 uBase;
|
|
int16 vBase;
|
|
int16 u;
|
|
int16 v;
|
|
TilePoint tilePoint;
|
|
uint16 dir;
|
|
int16 dist;
|
|
uint16 terraComp[8];
|
|
const TilePoint *tdir;
|
|
uint16 terrainMask;
|
|
|
|
bestDistance = 0;
|
|
|
|
|
|
uBase = (start.u() >> 4) - SAGA_SEARCH_CENTER;
|
|
vBase = (start.v() >> 4) - SAGA_SEARCH_CENTER;
|
|
|
|
bestU = SAGA_SEARCH_CENTER;
|
|
bestV = SAGA_SEARCH_CENTER;
|
|
|
|
_platformHeight = _vm->_actor->_protagonist->_location.z / 8;
|
|
|
|
memset( &_searchArray, 0, sizeof(_searchArray));
|
|
|
|
for (ActorDataArray::const_iterator actor = _vm->_actor->_actors.begin(); actor != _vm->_actor->_actors.end(); ++actor) {
|
|
if (!actor->_inScene) continue;
|
|
|
|
u = (actor->_location.u() >> 4) - uBase;
|
|
v = (actor->_location.v() >> 4) - vBase;
|
|
if ((u >= 0) && (u < SAGA_SEARCH_DIAMETER) &&
|
|
(v >= 0) && (v < SAGA_SEARCH_DIAMETER) &&
|
|
((u != SAGA_SEARCH_CENTER) || (v != SAGA_SEARCH_CENTER))) {
|
|
_searchArray.getPathCell(u, v)->visited = 1;
|
|
}
|
|
}
|
|
|
|
_queueCount = 0;
|
|
pushPoint(SAGA_SEARCH_CENTER, SAGA_SEARCH_CENTER, 0, 0);
|
|
|
|
while (_queueCount > 0) {
|
|
|
|
_queueCount--;
|
|
tilePoint = *_searchArray.getQueue(_queueCount);
|
|
|
|
|
|
dist = ABS(tilePoint.u - SAGA_SEARCH_CENTER) + ABS(tilePoint.v - SAGA_SEARCH_CENTER);
|
|
|
|
if (dist > bestDistance) {
|
|
bestU = tilePoint.u;
|
|
bestV = tilePoint.v;
|
|
bestDistance = dist;
|
|
|
|
if (dist >= distance) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
testPossibleDirections(uBase + tilePoint.u, vBase + tilePoint.v, terraComp, 0);
|
|
|
|
|
|
for (dir = 0; dir < 8; dir++) {
|
|
terrainMask = terraComp[dir];
|
|
|
|
if (terrainMask & SAGA_IMPASSABLE) {
|
|
continue;
|
|
}
|
|
|
|
if (dir == direction) {
|
|
tdir = &easyDirTable[ dir ];
|
|
} else {
|
|
if (dir + 1 == direction || dir - 1 == direction) {
|
|
tdir = &normalDirTable[ dir ];
|
|
} else {
|
|
tdir = &hardDirTable[ dir ];
|
|
}
|
|
}
|
|
|
|
pushPoint(tilePoint.u + tdir->u,tilePoint.v + tdir->v, tilePoint.cost + tdir->cost, dir);
|
|
}
|
|
}
|
|
|
|
result.u() = ((uBase + bestU) << 4) + 8;
|
|
result.v() = ((vBase + bestV) << 4) + 8;
|
|
}
|
|
|
|
bool IsoMap::findNearestChasm(int16 &u0, int16 &v0, uint16 &direction) {
|
|
int16 u, v;
|
|
uint16 i;
|
|
u = u0;
|
|
v = v0;
|
|
|
|
for (i = 1; i < 5; i++) {
|
|
if (getTile( u - i, v, 6) == NULL) {
|
|
u0 = u - i - 1;
|
|
v0 = v;
|
|
direction = kDirDownLeft;
|
|
return true;
|
|
}
|
|
|
|
if (getTile( u, v - i, 6) == NULL) {
|
|
u0 = u;
|
|
v0 = v - i - 1;
|
|
direction = kDirDownRight;
|
|
return true;
|
|
}
|
|
|
|
if (getTile( u - i, v - i, 6) == NULL) {
|
|
u0 = u - i - 1;
|
|
v0 = v - i - 1;
|
|
direction = kDirDown;
|
|
return true;
|
|
}
|
|
|
|
if (getTile( u + i, v - i, 6) == NULL) {
|
|
u0 = u + i + 1;
|
|
v0 = v - i - 1;
|
|
direction = kDirDownRight;
|
|
return true;
|
|
}
|
|
|
|
if (getTile( u - i, v + i, 6) == NULL) {
|
|
u0 = u + i + 1;
|
|
v0 = v - i - 1;
|
|
direction = kDirLeft;
|
|
return true;
|
|
}
|
|
}
|
|
|
|
for (i = 1; i < 5; i++) {
|
|
if (getTile( u + i, v, 6) == NULL) {
|
|
u0 = u + i + 1;
|
|
v0 = v;
|
|
direction = kDirUpRight;
|
|
return true;
|
|
}
|
|
|
|
if (getTile( u, v + i, 6) == NULL) {
|
|
u0 = u;
|
|
v0 = v + i + 1;
|
|
direction = kDirUpLeft;
|
|
return true;
|
|
}
|
|
|
|
if (getTile( u + i, v + i, 6) == NULL) {
|
|
u0 = u + i + 1;
|
|
v0 = v + i + 1;
|
|
direction = kDirUp;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void IsoMap::findDragonTilePath(ActorData* actor,const Location &start, const Location &end, uint16 initialDirection) {
|
|
byte *res;
|
|
int i;
|
|
int16 u;
|
|
int16 v;
|
|
int16 u1;
|
|
int16 v1;
|
|
uint16 dir;
|
|
|
|
int16 bestDistance;
|
|
int16 bestU;
|
|
int16 bestV;
|
|
|
|
int16 uBase;
|
|
int16 vBase;
|
|
int16 uFinish;
|
|
int16 vFinish;
|
|
DragonPathCell *pcell;
|
|
IsoTileData *tile;
|
|
uint16 mask;
|
|
DragonTilePoint *tilePoint;
|
|
|
|
int16 dist;
|
|
bool first;
|
|
|
|
bestDistance = SAGA_DRAGON_SEARCH_DIAMETER;
|
|
bestU = SAGA_DRAGON_SEARCH_CENTER,
|
|
bestV = SAGA_DRAGON_SEARCH_CENTER;
|
|
|
|
uBase = (start.u() >> 4) - SAGA_DRAGON_SEARCH_CENTER;
|
|
vBase = (start.v() >> 4) - SAGA_DRAGON_SEARCH_CENTER;
|
|
uFinish = (end.u() >> 4) - uBase;
|
|
vFinish = (end.v() >> 4) - vBase;
|
|
|
|
_platformHeight = _vm->_actor->_protagonist->_location.z / 8;
|
|
|
|
memset( &_dragonSearchArray, 0, sizeof(_dragonSearchArray));
|
|
|
|
for (u = 0; u < SAGA_DRAGON_SEARCH_DIAMETER; u++) {
|
|
for (v = 0; v < SAGA_DRAGON_SEARCH_DIAMETER; v++) {
|
|
|
|
pcell = _dragonSearchArray.getPathCell(u, v);
|
|
|
|
u1 = uBase + u;
|
|
v1 = vBase + v;
|
|
|
|
if ((u1 > 127) || (u1 < 48) || (v1 > 127) || (v1 < 0)) {
|
|
pcell->visited = 1;
|
|
continue;
|
|
}
|
|
|
|
tile = getTile(u1, v1, _platformHeight);
|
|
if (tile != NULL) {
|
|
mask = tile->terrainMask;
|
|
if (((mask != 0) && (tile->GetFGDAttr() >= kTerrBlock)) ||
|
|
((mask != 0xFFFF) && (tile->GetBGDAttr() >= kTerrBlock))) {
|
|
pcell->visited = 1;
|
|
}
|
|
} else {
|
|
pcell->visited = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
first = true;
|
|
_queueCount = _readCount = 0;
|
|
pushDragonPoint( SAGA_DRAGON_SEARCH_CENTER, SAGA_DRAGON_SEARCH_CENTER, initialDirection);
|
|
|
|
while (_queueCount != _readCount) {
|
|
|
|
tilePoint = _dragonSearchArray.getQueue(_readCount++);
|
|
if (_readCount >= SAGA_SEARCH_QUEUE_SIZE) {
|
|
_readCount = 0;
|
|
}
|
|
|
|
|
|
dist = ABS(tilePoint->u - uFinish) + ABS(tilePoint->v - vFinish);
|
|
|
|
if (dist < bestDistance) {
|
|
|
|
bestU = tilePoint->u;
|
|
bestV = tilePoint->v;
|
|
bestDistance = dist;
|
|
if (dist == 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
switch (tilePoint->direction) {
|
|
case kDirUpRight:
|
|
if (checkDragonPoint( tilePoint->u + 1, tilePoint->v + 0, kDirUpRight)) {
|
|
pushDragonPoint( tilePoint->u + 2, tilePoint->v + 0, kDirUpRight);
|
|
pushDragonPoint( tilePoint->u + 1, tilePoint->v + 1, kDirUpLeft);
|
|
pushDragonPoint( tilePoint->u + 1, tilePoint->v - 1, kDirDownRight);
|
|
}
|
|
break;
|
|
case kDirDownRight:
|
|
if (checkDragonPoint( tilePoint->u + 0, tilePoint->v - 1, kDirDownRight)) {
|
|
pushDragonPoint( tilePoint->u + 0, tilePoint->v - 2, kDirDownRight);
|
|
pushDragonPoint( tilePoint->u + 1, tilePoint->v - 1, kDirUpRight);
|
|
pushDragonPoint( tilePoint->u - 1, tilePoint->v - 1, kDirDownLeft);
|
|
}
|
|
break;
|
|
case kDirDownLeft:
|
|
if (checkDragonPoint( tilePoint->u - 1, tilePoint->v + 0, kDirDownLeft)) {
|
|
pushDragonPoint( tilePoint->u - 2, tilePoint->v + 0, kDirDownLeft);
|
|
pushDragonPoint( tilePoint->u - 1, tilePoint->v - 1, kDirDownRight);
|
|
pushDragonPoint( tilePoint->u - 1, tilePoint->v + 1, kDirUpLeft);
|
|
}
|
|
break;
|
|
case kDirUpLeft:
|
|
if (checkDragonPoint( tilePoint->u + 0, tilePoint->v + 1, kDirUpLeft)) {
|
|
pushDragonPoint( tilePoint->u + 0, tilePoint->v + 2, kDirUpLeft);
|
|
pushDragonPoint( tilePoint->u - 1, tilePoint->v + 1, kDirDownLeft);
|
|
pushDragonPoint( tilePoint->u + 1, tilePoint->v + 1, kDirUpRight);
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (first && (_queueCount == _readCount)) {
|
|
pushDragonPoint( tilePoint->u + 1, tilePoint->v + 0, kDirUpRight);
|
|
pushDragonPoint( tilePoint->u + 0, tilePoint->v - 1, kDirDownRight);
|
|
pushDragonPoint( tilePoint->u - 1, tilePoint->v + 0, kDirDownLeft);
|
|
pushDragonPoint( tilePoint->u + 0, tilePoint->v + 1, kDirUpLeft);
|
|
}
|
|
first = false;
|
|
}
|
|
|
|
res = &_pathDirections[SAGA_MAX_PATH_DIRECTIONS];
|
|
i = 0;
|
|
while ((bestU != SAGA_DRAGON_SEARCH_CENTER) || (bestV != SAGA_DRAGON_SEARCH_CENTER)) {
|
|
pcell = _dragonSearchArray.getPathCell(bestU, bestV);
|
|
|
|
*--res = pcell->direction;
|
|
i++;
|
|
if (i >= SAGA_MAX_PATH_DIRECTIONS) {
|
|
break;
|
|
}
|
|
|
|
dir = (pcell->direction + 4) & 0x07;
|
|
|
|
bestU += normalDirTable[dir].u;
|
|
bestV += normalDirTable[dir].v;
|
|
}
|
|
|
|
/* if (i > 64) {
|
|
i = 64;
|
|
}*/
|
|
|
|
actor->_walkStepsCount = i;
|
|
if (i) {
|
|
actor->_tileDirections.resize(i);
|
|
memcpy(&actor->_tileDirections.front(), res, i);
|
|
}
|
|
|
|
}
|
|
|
|
void IsoMap::findTilePath(ActorData* actor, const Location &start, const Location &end) {
|
|
int i;
|
|
int16 u;
|
|
int16 v;
|
|
int16 bestDistance;
|
|
int16 bestU;
|
|
int16 bestV;
|
|
|
|
int16 uBase;
|
|
int16 vBase;
|
|
int16 uFinish;
|
|
int16 vFinish;
|
|
|
|
TilePoint tilePoint;
|
|
uint16 dir;
|
|
int16 dist;
|
|
uint16 terraComp[8];
|
|
const TilePoint *tdir;
|
|
uint16 terrainMask;
|
|
const PathCell *pcell;
|
|
byte *res;
|
|
|
|
|
|
bestDistance = SAGA_SEARCH_DIAMETER;
|
|
bestU = SAGA_SEARCH_CENTER,
|
|
bestV = SAGA_SEARCH_CENTER;
|
|
|
|
uBase = (start.u() >> 4) - SAGA_SEARCH_CENTER;
|
|
vBase = (start.v() >> 4) - SAGA_SEARCH_CENTER;
|
|
uFinish = (end.u() >> 4) - uBase;
|
|
vFinish = (end.v() >> 4) - vBase;
|
|
|
|
_platformHeight = _vm->_actor->_protagonist->_location.z / 8;
|
|
|
|
|
|
|
|
memset( &_searchArray, 0, sizeof(_searchArray));
|
|
|
|
if (!(actor->_actorFlags & kActorNoCollide) &&
|
|
(_vm->_scene->currentSceneResourceId() != ITE_SCENE_OVERMAP)) {
|
|
for (ActorDataArray::const_iterator other = _vm->_actor->_actors.begin(); other != _vm->_actor->_actors.end(); ++other) {
|
|
if (!other->_inScene) continue;
|
|
if (other->_id == actor->_id) continue;
|
|
|
|
u = (other->_location.u() >> 4) - uBase;
|
|
v = (other->_location.v() >> 4) - vBase;
|
|
if ((u >= 1) && (u < SAGA_SEARCH_DIAMETER) &&
|
|
(v >= 1) && (v < SAGA_SEARCH_DIAMETER) &&
|
|
((u != SAGA_SEARCH_CENTER) || (v != SAGA_SEARCH_CENTER))) {
|
|
_searchArray.getPathCell(u, v)->visited = 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
_queueCount = 0;
|
|
pushPoint(SAGA_SEARCH_CENTER, SAGA_SEARCH_CENTER, 0, 0);
|
|
|
|
|
|
while (_queueCount > 0) {
|
|
|
|
_queueCount--;
|
|
tilePoint = *_searchArray.getQueue(_queueCount);
|
|
|
|
if (tilePoint.cost > 100 && actor == _vm->_actor->_protagonist) continue;
|
|
|
|
dist = ABS(tilePoint.u - uFinish) + ABS(tilePoint.v - vFinish);
|
|
|
|
if (dist < bestDistance) {
|
|
bestU = tilePoint.u;
|
|
bestV = tilePoint.v;
|
|
bestDistance = dist;
|
|
|
|
if (dist == 0) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
testPossibleDirections(uBase + tilePoint.u, vBase + tilePoint.v, terraComp,
|
|
(tilePoint.u == SAGA_SEARCH_CENTER && tilePoint.v == SAGA_SEARCH_CENTER));
|
|
|
|
for (dir = 0; dir < 8; dir++) {
|
|
terrainMask = terraComp[dir];
|
|
|
|
if (terrainMask & SAGA_IMPASSABLE) {
|
|
continue;
|
|
} else {
|
|
if (terrainMask & (1 << kTerrRough)) {
|
|
tdir = &hardDirTable[ dir ];
|
|
} else {
|
|
if (terrainMask & (1 << kTerrNone)) {
|
|
tdir = &normalDirTable[ dir ];
|
|
} else {
|
|
tdir = &easyDirTable[ dir ];
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
pushPoint(tilePoint.u + tdir->u, tilePoint.v + tdir->v, tilePoint.cost + tdir->cost, dir);
|
|
}
|
|
}
|
|
|
|
res = &_pathDirections[SAGA_MAX_PATH_DIRECTIONS];
|
|
i = 0;
|
|
while ((bestU != SAGA_SEARCH_CENTER) || (bestV != SAGA_SEARCH_CENTER)) {
|
|
pcell = _searchArray.getPathCell(bestU, bestV);
|
|
|
|
*--res = pcell->direction;
|
|
i++;
|
|
if (i >= SAGA_MAX_PATH_DIRECTIONS) {
|
|
break;
|
|
}
|
|
|
|
dir = (pcell->direction + 4) & 0x07;
|
|
|
|
bestU += normalDirTable[dir].u;
|
|
bestV += normalDirTable[dir].v;
|
|
}
|
|
|
|
/* if (i > 64) {
|
|
i = 64;
|
|
}*/
|
|
|
|
actor->_walkStepsCount = i;
|
|
if (i) {
|
|
actor->_tileDirections.resize(i);
|
|
memcpy(&actor->_tileDirections.front(), res, i);
|
|
}
|
|
}
|
|
|
|
void IsoMap::setTileDoorState(int doorNumber, int doorState) {
|
|
MultiTileEntryData *multiTileEntryData;
|
|
|
|
if ((doorNumber < 0) || ((uint)doorNumber >= _multiTable.size())) {
|
|
error("setTileDoorState: doorNumber >= _multiTable.size()");
|
|
}
|
|
|
|
multiTileEntryData = &_multiTable[doorNumber];
|
|
multiTileEntryData->currentState = doorState;
|
|
}
|
|
|
|
bool IsoMap::nextTileTarget(ActorData* actor) {
|
|
uint16 dir;
|
|
|
|
if (actor->_walkStepIndex >= actor->_walkStepsCount) {
|
|
return false;
|
|
}
|
|
|
|
|
|
actor->_actionDirection = dir = actor->_tileDirections[actor->_walkStepIndex++];
|
|
|
|
actor->_partialTarget.u() =
|
|
(actor->_location.u() & ~0x0f) + 8 + directions[dir][0];
|
|
|
|
actor->_partialTarget.v() =
|
|
(actor->_location.v() & ~0x0f) + 8 + directions[dir][1];
|
|
|
|
|
|
if (dir == 0) {
|
|
actor->_facingDirection = kDirUp;
|
|
} else {
|
|
if (dir == 4) {
|
|
actor->_facingDirection = kDirDown;
|
|
} else {
|
|
if (dir < 4) {
|
|
actor->_facingDirection = kDirRight;
|
|
} else {
|
|
actor->_facingDirection = kDirLeft;
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void IsoMap::screenPointToTileCoords(const Point &position, Location &location) {
|
|
Point mPos(position);
|
|
int x,y;
|
|
|
|
if (_vm->_scene->currentSceneResourceId() == ITE_SCENE_OVERMAP){
|
|
if (mPos.y < 16) {
|
|
mPos.y = 16;
|
|
}
|
|
}
|
|
|
|
x = mPos.x + _viewScroll.x - (128 * SAGA_TILEMAP_W) - 16;
|
|
y = mPos.y + _viewScroll.y - (128 * SAGA_TILEMAP_W) + _vm->_actor->_protagonist->_location.z;
|
|
|
|
location.u() = (x - y * 2) >> 1;
|
|
location.v() = - (x + y * 2) >> 1;
|
|
location.z = _vm->_actor->_protagonist->_location.z;
|
|
}
|
|
|
|
} // End of namespace Saga
|