scummvm/saga/isomap.cpp
Andrew Kurushin 8ea88c95d6 - fixed tile doors drawing
- right mouse action fixed

svn-id: r17347
2005-04-03 15:32:04 +00:00

1421 lines
34 KiB
C++

/* ScummVM - Scumm Interpreter
* Copyright (C) 2004-2005 The ScummVM project
*
* The ReInherit Engine is (C)2000-2003 by Daniel Balsom.
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* $Header$
*
*/
// Isometric level module
#include "saga/saga.h"
#include "saga/gfx.h"
#include "saga/resnames.h"
#include "saga/scene.h"
#include "saga/isomap.h"
#include "saga/stream.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},
};
IsoMap::IsoMap(SagaEngine *vm) : _vm(vm) {
_tileData = NULL;
_tilesCount = 0;
_tilePlatformList = NULL;
_tilePlatformsCount = 0;
_metaTileList = NULL;
_metaTilesCount = 0;
_multiTable = NULL;
_multiCount = 0;
_multiTableData = NULL;
_multiDataCount = 0;
_viewScroll.x = (128 - 8) * 16;
_viewScroll.x = (128 - 8) * 16 - 64;
_viewDiff = 1;
}
void IsoMap::loadImages(const byte *resourcePointer, size_t resourceLength) {
IsoTileData *tileData;
uint16 i;
if (resourceLength == 0) {
error("IsoMap::loadImages wrong resourceLength");
}
_tileData = (byte*)malloc(resourceLength);
_tileDataLength = resourceLength;
memcpy(_tileData, resourcePointer, resourceLength);
MemoryReadStreamEndian readS(_tileData, _tileDataLength, IS_BIG_ENDIAN);
readS.readUint16(); // skip
_tilesCount = readS.readUint16();
_tilesCount = _tilesCount / SAGA_ISOTILEDATA_LEN;
readS.seek(0);
_tilesTable = (IsoTileData *)malloc(_tilesCount * sizeof(*_tilesTable));
if (_tilesTable == NULL) {
memoryError("IsoMap::loadImages");
}
for (i = 0; i < _tilesCount; i++) {
tileData = &_tilesTable[i];
tileData->height = readS.readByte();
tileData->attributes = readS.readSByte();
tileData->offset = readS.readUint16();
tileData->terrainMask = readS.readUint16();
tileData->FGDBGDAttr = readS.readByte();
readS.readByte(); //skip
}
}
void IsoMap::loadPlatforms(const byte * resourcePointer, size_t resourceLength) {
TilePlatformData *tilePlatformData;
uint16 i, x, y;
if (resourceLength == 0) {
error("IsoMap::loadPlatforms wrong resourceLength");
}
MemoryReadStreamEndian readS(resourcePointer, resourceLength, IS_BIG_ENDIAN);
_tilePlatformsCount = resourceLength / SAGA_TILEPLATFORMDATA_LEN;
_tilePlatformList = (TilePlatformData *)malloc(_tilePlatformsCount * sizeof(*_tilePlatformList));
if (_tilePlatformList == NULL) {
memoryError("IsoMap::loadPlatforms");
}
for (i = 0; i < _tilePlatformsCount; 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 byte * resourcePointer, size_t resourceLength) {
uint16 x, y;
if (resourceLength != SAGA_TILEMAP_LEN) {
error("IsoMap::loadMap wrong resourceLength");
}
MemoryReadStreamEndian readS(resourcePointer, resourceLength, IS_BIG_ENDIAN);
_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 byte * resourcePointer, size_t resourceLength) {
MetaTileData *metaTileData;
uint16 i, j;
if (resourceLength == 0) {
error("IsoMap::loadMetaTiles wrong resourceLength");
}
MemoryReadStreamEndian readS(resourcePointer, resourceLength, IS_BIG_ENDIAN);
_metaTilesCount = resourceLength / SAGA_METATILEDATA_LEN;
_metaTileList = (MetaTileData *)malloc(_metaTilesCount * sizeof(*_metaTileList));
if (_metaTileList == NULL) {
memoryError("IsoMap::loadMetaTiles");
}
for (i = 0; i < _metaTilesCount; 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 byte * resourcePointer, size_t resourceLength) {
MultiTileEntryData *multiTileEntryData;
uint16 i;
int16 offsetDiff;
if (resourceLength < 2) {
error("IsoMap::loadMetaTiles wrong resourceLength");
}
MemoryReadStreamEndian readS(resourcePointer, resourceLength, IS_BIG_ENDIAN);
_multiCount = readS.readUint16();
_multiTable = (MultiTileEntryData *)malloc(_multiCount * sizeof(*_multiTable));
if (_multiTable == NULL) {
memoryError("IsoMap::loadMulti");
}
for (i = 0; i < _multiCount; 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 < _multiCount; i++) {
_multiTable[i].offset -= offsetDiff;
}
_multiDataCount = (readS.size() - readS.pos()) / 2;
_multiTableData = (int16 *)malloc(_multiDataCount * sizeof(*_multiTableData));
for (i = 0; i < _multiDataCount; i++) {
_multiTableData[i] = readS.readSint16();
}
}
void IsoMap::freeMem() {
free(_tileData);
_tileData = NULL;
_tilesCount = 0;
free(_tilePlatformList);
_tilePlatformList = NULL;
_tilePlatformsCount = 0;
free(_metaTileList);
_metaTileList = NULL;
_metaTilesCount = 0;
free(_multiTable);
_multiTable = NULL;
_multiCount = 0;
free(_multiTableData);
_multiTableData = NULL;
_multiDataCount = 0;
}
void IsoMap::adjustScroll(bool jump) {
Point playerPoint;
Point minScrollPos;
Point maxScrollPos;
tileCoordsToScreenPoint(_vm->_actor->_centerActor->location, playerPoint);
if (_vm->_scene->currentSceneResourceId() == RID_ITE_OVERMAP_SCENE) {
_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->getDisplayWidth()/2;
playerPoint.y += _viewScroll.y - _vm->getSceneHeight()/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() == RID_ITE_OVERMAP_SCENE) {
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 < _multiCount; 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(*_multiTableData);
offset += multiTileEntryData->offset;
if (offset + sizeof(*_multiTableData) - 1 >= _multiDataCount * sizeof(*_multiTableData)) {
error("wrong multiTileEntryData->offset");
}
tiles = (int16*)((byte*)_multiTableData + offset);
tileIndex = *tiles;
if (tileIndex >= 256) {
warning("something terrible happened");
return 1;
}
return tileIndex;
}
}
return 1;
}
int IsoMap::draw(SURFACE *ds) {
Rect isoRect(_vm->getDisplayWidth(), _vm->getDisplayInfo().sceneHeight);
drawRect(ds, &isoRect, 0);
_tileClip = isoRect;
drawTiles(ds, NULL);
return SUCCESS;
}
void IsoMap::drawSprite(SURFACE *ds, 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;
Rect clip(_vm->getDisplayWidth(),_vm->getSceneHeight());
_vm->_sprite->getScaledSpriteBuffer(spriteList,spriteNumber,scale, width, height, xAlign, yAlign, spriteBuffer);
spritePointer.x = screenPosition.x + xAlign;
spritePointer.y = screenPosition.y + yAlign;
_tileClip.left = spritePointer.x;
_tileClip.top = spritePointer.y;
_tileClip.right = spritePointer.x + width;
_tileClip.bottom = spritePointer.y + height;
if (_tileClip.left < 0) {
_tileClip.left = 0;
}
if (_tileClip.right > _vm->getDisplayWidth()) {
_tileClip.right = _vm->getDisplayWidth();
}
if (_tileClip.top < 0) {
_tileClip.top = 0;
}
if (_tileClip.bottom > _vm->getSceneHeight()) {
_tileClip.bottom = _vm->getSceneHeight();
}
_vm->_sprite->drawClip(ds, clip, spritePointer, width, height, spriteBuffer);
drawTiles(ds, &location);
}
void IsoMap::drawTiles(SURFACE *ds, 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->getDisplayWidth() + 128;
workAreaHeight = _vm->getDisplayInfo().sceneHeight + 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 = clamp( 0, u2, SAGA_TILEMAP_W - 1);
vc = clamp( 0, v2, 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(ds, metaTileIndex, metaTileX, rLocation, u2 << 3, v2 << 3);
} else {
drawMetaTile(ds, 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 = clamp( 0, u2, SAGA_TILEMAP_W - 1);
vc = clamp( 0, v2, 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(ds, metaTileIndex, metaTileX, rLocation, u2 << 3, v2 << 3);
} else {
drawMetaTile(ds, metaTileIndex, metaTileX, u2 << 3, v2 << 3);
}
}
metaTileY.y += 64;
}
}
void IsoMap::drawSpriteMetaTile(SURFACE *ds, uint16 metaTileIndex, const Point &point, Location &location, int16 absU, int16 absV) {
MetaTileData * metaTile;
uint16 high;
int16 platformIndex;
Point platformPoint;
platformPoint = point;
if (_metaTilesCount <= 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( ds, platformIndex, platformPoint, location, absU, absV, high );
}
}
}
void IsoMap::drawMetaTile(SURFACE *ds, uint16 metaTileIndex, const Point &point, int16 absU, int16 absV) {
MetaTileData * metaTile;
uint16 high;
int16 platformIndex;
Point platformPoint;
platformPoint = point;
if (_metaTilesCount <= 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( ds, platformIndex, platformPoint, absU, absV, high );
}
}
}
void IsoMap::drawSpritePlatform(SURFACE *ds, 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 (_tilePlatformsCount <= 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(ds, tileIndex, s, &copyLocation);
}
}
}
}
}
void IsoMap::drawPlatform(SURFACE *ds, uint16 platformIndex, const Point &point, int16 absU, int16 absV, int16 absH) {
TilePlatformData *tilePlatform;
int16 u, v;
Point s;
Point s0;
uint16 tileIndex;
if (_tilePlatformsCount <= 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(ds, tileIndex, s, NULL);
}
}
}
}
}
#define THRESH0 0
#define THRESH8 8
#define THRESH16 16
void IsoMap::drawTile(SURFACE *ds, 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 >= _tilesCount) {
error("IsoMap::drawTile wrong tileIndex");
}
if (point.x + SAGA_ISOTILE_WIDTH < _tileClip.left) {
return;
}
if (point.x - SAGA_ISOTILE_WIDTH >= _tileClip.right) {
return;
}
tilePointer = _tileData + _tilesTable[tileIndex].offset;
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 = (byte *)ds->pixels + drawPoint.x + (row * ds->pitch);
col = drawPoint.x;
for (;;) {
bgRunCount = *readPointer++;
widthCount += bgRunCount;
if (widthCount >= SAGA_ISOTILE_WIDTH) {
break;
}
drawPointer += bgRunCount;
col += bgRunCount;
fgRunCount = *readPointer++;
widthCount += fgRunCount;
count = 0;
while ((col < _tileClip.left) && (count < fgRunCount)) {
count++;
col++;
}
while ((col < _tileClip.right) && (count < fgRunCount)) {
assert((uint)ds->pixels <= (uint)(drawPointer + count));
assert(((uint)ds->pixels + (_vm->getDisplayWidth() * _vm->getDisplayHeight())) > (uint)(drawPointer + count));
drawPointer[count] = readPointer[count];
count++;
col++;
}
readPointer += fgRunCount;
drawPointer += fgRunCount;
}
} else {
for (;;) {
bgRunCount = *readPointer++;
widthCount += bgRunCount;
if (widthCount >= SAGA_ISOTILE_WIDTH) {
break;
}
fgRunCount = *readPointer++;
widthCount += fgRunCount;
readPointer += fgRunCount;
}
}
}
}
//
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;
}
IsoTileData *IsoMap::getTile(int16 u, int16 v, int16 z) {
int16 mtileU;
int16 mtileV;
int16 uc;
int16 vc;
int16 u0;
int16 v0;
int16 tileIndex;
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 NULL;
case kEdgeTypeFill0:
break;
case kEdgeTypeFill1:
metaTileIndex = 1;
break;
case kEdgeTypeRpt:
uc = clamp( 0, mtileU, SAGA_TILEMAP_W - 1);
vc = clamp( 0, mtileV, 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 (_metaTilesCount <= metaTileIndex) {
error("IsoMap::getTile wrong metaTileIndex");
}
platformIndex = _metaTileList[metaTileIndex].stack[z];
if (platformIndex < 0) {
return NULL;
}
if (_tilePlatformsCount <= platformIndex) {
error("IsoMap::getTile wrong platformIndex");
}
tileIndex = _tilePlatformList[platformIndex].tiles[u0][v0];
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; \
} \
}
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_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;
int i;
ActorData *actor;
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 (i = 0; i < _vm->_actor->_actorsCount; i++) {
actor = _vm->_actor->_actors[i];
if (actor->disabled) continue;
if (actor->sceneNumber != _vm->_scene->currentSceneNumber()) 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;
}
void IsoMap::findTilePath(ActorData* actor, const Location &start, const Location &end) {
ActorData *other;
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() != RID_ITE_OVERMAP_SCENE)) {
for (i = 0; i < _vm->_actor->_actorsCount; i++) {
other = _vm->_actor->_actors[i];
if (other->disabled) continue;
if (other->sceneNumber != _vm->_scene->currentSceneNumber()) continue;
if (other==actor) 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) {
if (actor->tileDirectionsAlloced < i) {
actor->tileDirectionsAlloced = i;
actor->tileDirections = (byte*)realloc(actor->tileDirections, actor->tileDirectionsAlloced * sizeof(*actor->tileDirections));
}
memcpy(actor->tileDirections, res, i );
}
}
void IsoMap::setTileDoorState(int doorNumber, int doorState) {
MultiTileEntryData *multiTileEntryData;
if ((doorNumber < 0) || (doorNumber >= _multiCount)) {
error("setTileDoorState: doorNumber >= _multiCount");
}
multiTileEntryData = &_multiTable[doorNumber];
multiTileEntryData->currentState = doorState;
}
static const int16 directions[8][2] = {
{ 16, 16},
{ 16, 0},
{ 16, -16},
{ 0, -16},
{ -16, -16},
{ -16, 0},
{ -16, 16},
{ 0, 16}
};
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() == RID_ITE_OVERMAP_SCENE){
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