Initial commit
This commit is contained in:
902
engines/saga2/terrain.cpp
Normal file
902
engines/saga2/terrain.cpp
Normal file
@@ -0,0 +1,902 @@
|
||||
/* 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 3 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
|
||||
* aint32 with this program; if not, write to the Free Software
|
||||
*
|
||||
*
|
||||
* Based on the original sources
|
||||
* Faery Tale II -- The Halls of the Dead
|
||||
* (c) 1993-1996 The Wyrmkeep Entertainment Co.
|
||||
*/
|
||||
|
||||
#include "saga2/saga2.h"
|
||||
#include "saga2/idtypes.h"
|
||||
#include "saga2/tile.h"
|
||||
#include "saga2/tileline.h"
|
||||
#include "saga2/actor.h"
|
||||
|
||||
namespace Saga2 {
|
||||
|
||||
|
||||
extern WorldMapData *mapList;
|
||||
|
||||
static int16 prevMapNum;
|
||||
static StaticTilePoint prevCoords = {(int16)minint16, (int16)minint16, (int16)minint16};
|
||||
static MetaTilePtr prevMeta = nullptr;
|
||||
|
||||
/* ===================================================================== *
|
||||
Terrain damage info
|
||||
* ===================================================================== */
|
||||
|
||||
void drown(GameObject *obj) {
|
||||
if (isActor(obj)) {
|
||||
Actor *a = (Actor *) obj;
|
||||
if (!a->hasEffect(kActorWaterBreathe)) {
|
||||
if (g_vm->_rnd->getRandomNumber(kDrowningDamageOddsYes + kDrowningDamageOddsNo - 1) > kDrowningDamageOddsNo - 1) {
|
||||
a->acceptDamage(a->thisID(), kDrowningDamagePerFrame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void lavaDamage(GameObject *obj) {
|
||||
if (isActor(obj)) {
|
||||
Actor *a = (Actor *) obj;
|
||||
if (a->resists(kResistHeat))
|
||||
return;
|
||||
}
|
||||
if (g_vm->_rnd->getRandomNumber(kHeatDamageOddsYes + kHeatDamageOddsNo - 1) > kHeatDamageOddsNo - 1) {
|
||||
obj->acceptDamage(obj->thisID(), kHeatDamagePerFrame, kDamageHeat, kHeatDamageDicePerFrame, 6);
|
||||
}
|
||||
}
|
||||
|
||||
void coldDamage(GameObject *obj) {
|
||||
if (isActor(obj)) {
|
||||
Actor *a = (Actor *) obj;
|
||||
if (a->resists(kResistCold))
|
||||
return;
|
||||
}
|
||||
if (g_vm->_rnd->getRandomNumber(kColdDamageOddsYes + kColdDamageOddsNo - 1) > kColdDamageOddsNo - 1) {
|
||||
obj->acceptDamage(obj->thisID(), kColdDamagePerFrame, kDamageCold, kColdDamageDicePerFrame, 6);
|
||||
}
|
||||
}
|
||||
|
||||
void terrainDamageSlash(GameObject *obj) {
|
||||
if (g_vm->_rnd->getRandomNumber(kTerrainDamageOddsYes + kTerrainDamageOddsNo - 1) > kTerrainDamageOddsNo - 1) {
|
||||
obj->acceptDamage(obj->thisID(), kTerrainDamagePerFrame, kDamageSlash, kTerrainDamageDicePerFrame, 6);
|
||||
}
|
||||
}
|
||||
|
||||
void terrainDamageBash(GameObject *obj) {
|
||||
if (g_vm->_rnd->getRandomNumber(kTerrainDamageOddsYes + kTerrainDamageOddsNo - 1) > kTerrainDamageOddsNo - 1) {
|
||||
obj->acceptDamage(obj->thisID(), kTerrainDamagePerFrame, kDamageImpact, kTerrainDamageDicePerFrame, 6);
|
||||
}
|
||||
}
|
||||
|
||||
void fallingDamage(GameObject *obj, int16 speed) {
|
||||
if (isActor(obj)) {
|
||||
Actor *a = (Actor *) obj;
|
||||
if (!a->hasEffect(kActorSlowFall)) {
|
||||
a->acceptDamage(a->thisID(), (MAX(0, speed - 16)*kFallingDamageMult) / kFallingDamageDiv);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ===================================================================== *
|
||||
Function to get the terrain bits for a single UV location (with mask)
|
||||
* ===================================================================== */
|
||||
|
||||
uint32 tileTerrain(
|
||||
int16 mapNum,
|
||||
const TilePoint &pt,
|
||||
int16 mask,
|
||||
int16 minZ,
|
||||
int16 maxZ) {
|
||||
WorldMapData *map = &mapList[mapNum];
|
||||
|
||||
TilePoint metaCoords = pt >> kPlatShift,
|
||||
origin = metaCoords << kPlatShift,
|
||||
coords = pt - origin;
|
||||
MetaTilePtr metaPtr;
|
||||
uint32 terrain = 0;
|
||||
|
||||
// A simple method for avoiding looking up the metatile again.
|
||||
/* if ( prevMeta
|
||||
&& prevMapNum == mapNum
|
||||
&& prevCoords == metaCoords )
|
||||
metaPtr = prevMeta;
|
||||
else
|
||||
*/
|
||||
{
|
||||
// Look up the metatile on the map.
|
||||
metaPtr = prevMeta = map->lookupMeta(metaCoords);
|
||||
prevMapNum = mapNum;
|
||||
prevCoords.set(metaCoords.u, metaCoords.v, metaCoords.z);
|
||||
}
|
||||
|
||||
if (metaPtr == nullptr) return 0L;
|
||||
|
||||
for (int i = 0; i < kMaxPlatforms; i++) {
|
||||
Platform *p;
|
||||
|
||||
if ((p = metaPtr->fetchPlatform(mapNum, i)) == nullptr)
|
||||
continue;
|
||||
|
||||
if (p->flags & kPlVisible) {
|
||||
int16 height;
|
||||
TileInfo *ti;
|
||||
int16 trFlags;
|
||||
|
||||
ti = p->fetchTile(
|
||||
mapNum,
|
||||
coords,
|
||||
origin,
|
||||
height,
|
||||
trFlags);
|
||||
|
||||
if (ti) {
|
||||
int16 tileMinZ = height,
|
||||
tileMaxZ = height;
|
||||
int32 combinedMask = ti->combinedTerrainMask();
|
||||
|
||||
if (combinedMask & kTerrainRaised)
|
||||
tileMaxZ += ti->attrs.terrainHeight;
|
||||
if (combinedMask & kTerrainWater)
|
||||
tileMinZ -= ti->attrs.terrainHeight;
|
||||
|
||||
if (tileMinZ < maxZ
|
||||
&& tileMaxZ >= minZ) {
|
||||
uint32 terrainResult = 0,
|
||||
tileFgdTerrain = (1 << ti->attrs.fgdTerrain),
|
||||
tileBgdTerrain = (1 << ti->attrs.bgdTerrain);
|
||||
|
||||
// If only checking the top of raised terrain treat it
|
||||
// as if it were normal terrain.
|
||||
if (minZ >= tileMaxZ) {
|
||||
if (tileFgdTerrain & kTerrainSupportingRaised)
|
||||
tileFgdTerrain = kTerrainNormal;
|
||||
if (tileBgdTerrain & kTerrainSupportingRaised)
|
||||
tileBgdTerrain = kTerrainNormal;
|
||||
}
|
||||
|
||||
// If this tile is sensitive to being walked on,
|
||||
// set the "sensitive" flag.
|
||||
if (trFlags & kTrTileSensitive)
|
||||
terrainResult |= kTerrainActive;
|
||||
|
||||
if (mask & ti->attrs.terrainMask)
|
||||
terrainResult |= tileFgdTerrain;
|
||||
|
||||
if (mask & ~ti->attrs.terrainMask)
|
||||
terrainResult |= tileBgdTerrain;
|
||||
|
||||
// This prevents actors from walking through
|
||||
// catwalks and other surfaces which have no bottom.
|
||||
|
||||
if ((terrainResult & kTerrainSolidSurface)
|
||||
&& height > minZ + kMaxStepHeight) {
|
||||
terrainResult |= kTerrainStone;
|
||||
}
|
||||
|
||||
terrain |= terrainResult;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return terrain;
|
||||
}
|
||||
|
||||
/* ===================================================================== *
|
||||
Function to get the terrain infor for a rectilinear volume
|
||||
* ===================================================================== */
|
||||
|
||||
uint16 uMaxMasks[4] = { 0x0000, 0x000F, 0x00FF, 0x0FFF },
|
||||
uMinMasks[4] = { 0xFFFF, 0xFFF0, 0xFF00, 0xF000 },
|
||||
vMaxMasks[4] = { 0x0000, 0x1111, 0x3333, 0x7777 },
|
||||
vMinMasks[4] = { 0xFFFF, 0xEEEE, 0xCCCC, 0x8888 };
|
||||
|
||||
uint32 volumeTerrain(int16 mapNum, const TileRegion &vol) {
|
||||
uint32 terrain = 0; // accumulated terrain
|
||||
TilePoint tilePt;
|
||||
TileRegion footprint,
|
||||
subPos,
|
||||
volume;
|
||||
|
||||
// Convert to subtile coords
|
||||
volume.min.u = vol.min.u >> kSubTileShift;
|
||||
volume.min.v = vol.min.v >> kSubTileShift;
|
||||
volume.max.u = (vol.max.u + kSubTileMask) >> kSubTileShift;
|
||||
volume.max.v = (vol.max.v + kSubTileMask) >> kSubTileShift;
|
||||
volume.min.z = vol.min.z;
|
||||
volume.max.z = vol.max.z;
|
||||
|
||||
// Calculate the footprint of the object (in subtile coords)
|
||||
footprint.min.u = volume.min.u >> kTileSubShift;
|
||||
footprint.min.v = volume.min.v >> kTileSubShift;
|
||||
footprint.max.u = volume.max.u >> kTileSubShift;
|
||||
footprint.max.v = volume.max.v >> kTileSubShift;
|
||||
|
||||
// Calculate which subtiles the region falls upon.
|
||||
subPos.min.u = volume.min.u & kSubTileMask;
|
||||
subPos.min.v = volume.min.v & kSubTileMask;
|
||||
subPos.max.u = volume.max.u & kSubTileMask;
|
||||
subPos.max.v = volume.max.v & kSubTileMask;
|
||||
|
||||
tilePt.z = 0;
|
||||
|
||||
for (tilePt.v = footprint.min.v;
|
||||
tilePt.v <= footprint.max.v;
|
||||
tilePt.v++) {
|
||||
uint16 vSectionMask = 0xFFFF;
|
||||
|
||||
if (tilePt.v == footprint.min.v)
|
||||
vSectionMask &= vMinMasks[subPos.min.v];
|
||||
|
||||
if (tilePt.v == footprint.max.v)
|
||||
vSectionMask &= vMaxMasks[subPos.max.v];
|
||||
|
||||
for (tilePt.u = footprint.min.u;
|
||||
tilePt.u <= footprint.max.u;
|
||||
tilePt.u++) {
|
||||
uint16 uSectionMask = vSectionMask;
|
||||
|
||||
if (tilePt.u == footprint.min.u)
|
||||
uSectionMask &= uMinMasks[subPos.min.u];
|
||||
|
||||
if (tilePt.u == footprint.max.u)
|
||||
uSectionMask &= uMaxMasks[subPos.max.u];
|
||||
|
||||
terrain |= tileTerrain(
|
||||
mapNum,
|
||||
tilePt,
|
||||
uSectionMask,
|
||||
volume.min.z,
|
||||
volume.max.z);
|
||||
}
|
||||
}
|
||||
return terrain;
|
||||
}
|
||||
|
||||
uint32 volumeTerrain(
|
||||
int16 mapNum,
|
||||
const TilePoint &pos,
|
||||
int16 objSection,
|
||||
int16 objHeight) {
|
||||
uint32 terrain = 0; // accumulated terrain
|
||||
TileRegion volume;
|
||||
|
||||
// Calculate the volume the object occupies
|
||||
volume.min.u = pos.u - objSection;
|
||||
volume.min.v = pos.v - objSection;
|
||||
volume.max.u = pos.u + objSection;
|
||||
volume.max.v = pos.v + objSection;
|
||||
volume.min.z = pos.z;
|
||||
volume.max.z = pos.z + objHeight;
|
||||
|
||||
terrain = volumeTerrain(mapNum, volume);
|
||||
|
||||
return terrain;
|
||||
}
|
||||
|
||||
uint32 volumeTerrain(
|
||||
int16 mapNum,
|
||||
const TilePoint &pos,
|
||||
int16 uCross,
|
||||
int16 vCross,
|
||||
int16 objHeight) {
|
||||
uint32 terrain = 0; // accumulated terrain
|
||||
TileRegion volume;
|
||||
|
||||
// Calculate the volume the object occupies
|
||||
volume.min.u = pos.u - uCross;
|
||||
volume.min.v = pos.v - vCross;
|
||||
volume.max.u = pos.u + uCross;
|
||||
volume.max.v = pos.v + vCross;
|
||||
volume.min.z = pos.z;
|
||||
volume.max.z = pos.z + objHeight;
|
||||
|
||||
if (debugChannelSet(-1, kDebugTiles)) {
|
||||
TilePoint minUminV(volume.min.u, volume.min.v, volume.min.z);
|
||||
TilePoint maxUminV(volume.max.u, volume.min.v, volume.min.z);
|
||||
TilePoint maxUmaxV(volume.max.u, volume.max.v, volume.min.z);
|
||||
TilePoint minUmaxV(volume.min.u, volume.max.v, volume.min.z);
|
||||
|
||||
TPLine(minUminV, maxUminV, 7);
|
||||
TPLine(maxUminV, maxUmaxV, 7);
|
||||
TPLine(maxUmaxV, minUmaxV, 7);
|
||||
TPLine(minUmaxV, minUminV, 7);
|
||||
}
|
||||
|
||||
terrain = volumeTerrain(mapNum, volume);
|
||||
|
||||
return terrain;
|
||||
}
|
||||
|
||||
/* ===================================================================== *
|
||||
Function to get the terrain info for linear area
|
||||
* ===================================================================== */
|
||||
|
||||
uint16 uMask[4] = { 0x000F, 0x00F0, 0x0F00, 0xF000 },
|
||||
vMask[4] = { 0x1111, 0x2222, 0x4444, 0x8888 };
|
||||
|
||||
uint32 lineTerrain(
|
||||
int16 mapNum,
|
||||
const TilePoint &from,
|
||||
const TilePoint &to,
|
||||
uint32 opaqueTerrain) {
|
||||
uint32 terrain = 0;
|
||||
|
||||
TilePoint curSubTile,
|
||||
destSubTile,
|
||||
tilePt;
|
||||
|
||||
int8 uStep,
|
||||
vStep;
|
||||
|
||||
uint16 uDiff,
|
||||
vDiff;
|
||||
|
||||
uint16 errorTerm = 0;
|
||||
|
||||
uint16 subTileMask_ = 0;
|
||||
|
||||
int16 tileStartZ,
|
||||
minZ,
|
||||
maxZ;
|
||||
|
||||
int32 curZ,
|
||||
zStep;
|
||||
|
||||
TilePoint prevPoint = from;
|
||||
TilePoint tempPoint;
|
||||
|
||||
// Calculate starting subtile coordinates
|
||||
curSubTile.u = from.u >> kSubTileShift;
|
||||
curSubTile.v = from.v >> kSubTileShift;
|
||||
curSubTile.z = tileStartZ = from.z;
|
||||
|
||||
// Calculate destination subtil coordinates
|
||||
destSubTile.u = to.u >> kSubTileShift;
|
||||
destSubTile.v = to.v >> kSubTileShift;
|
||||
destSubTile.z = to.z;
|
||||
|
||||
tilePt.u = curSubTile.u >> kTileSubShift;
|
||||
tilePt.v = curSubTile.v >> kTileSubShift;
|
||||
tilePt.z = 0;
|
||||
|
||||
if (destSubTile.u > curSubTile.u) {
|
||||
uStep = 1;
|
||||
uDiff = destSubTile.u - curSubTile.u;
|
||||
} else {
|
||||
uStep = -1;
|
||||
uDiff = curSubTile.u - destSubTile.u;
|
||||
}
|
||||
|
||||
if (destSubTile.v > curSubTile.v) {
|
||||
vStep = 1;
|
||||
vDiff = destSubTile.v - curSubTile.v;
|
||||
} else {
|
||||
vStep = -1;
|
||||
vDiff = curSubTile.v - destSubTile.v;
|
||||
}
|
||||
|
||||
if (uDiff == 0 && vDiff == 0) return 0;
|
||||
|
||||
curZ = (int32)curSubTile.z << 16;
|
||||
zStep = ((int32)(destSubTile.z - curSubTile.z) << 16);
|
||||
if (zStep > 0) {
|
||||
minZ = tileStartZ;
|
||||
maxZ = curSubTile.z;
|
||||
} else {
|
||||
minZ = curSubTile.z;
|
||||
maxZ = tileStartZ;
|
||||
}
|
||||
|
||||
if (uDiff > vDiff) {
|
||||
// U difference is greater
|
||||
|
||||
zStep /= uDiff;
|
||||
|
||||
for (;
|
||||
curSubTile.u != destSubTile.u;
|
||||
curSubTile.u += uStep) {
|
||||
curZ += zStep;
|
||||
|
||||
if ((curSubTile.u >> kTileSubShift) != tilePt.u) {
|
||||
curSubTile.z = curZ >> 16;
|
||||
|
||||
terrain |= tileTerrain(
|
||||
mapNum,
|
||||
tilePt,
|
||||
subTileMask_,
|
||||
minZ,
|
||||
maxZ + 1);
|
||||
if (terrain & opaqueTerrain) return terrain;
|
||||
|
||||
tilePt.u = curSubTile.u >> kTileSubShift;
|
||||
tileStartZ = curSubTile.z;
|
||||
subTileMask_ = 0;
|
||||
}
|
||||
|
||||
subTileMask_ |= (uMask[curSubTile.u & kSubTileMask] &
|
||||
vMask[curSubTile.v & kSubTileMask]);
|
||||
|
||||
if (debugChannelSet(-1, kDebugTiles)) {
|
||||
tempPoint.u = curSubTile.u << kTileSubShift;
|
||||
tempPoint.v = curSubTile.v << kTileSubShift;
|
||||
tempPoint.z = curSubTile.z;
|
||||
TPLine(prevPoint, tempPoint);
|
||||
prevPoint = tempPoint;
|
||||
}
|
||||
|
||||
errorTerm += vDiff;
|
||||
if (errorTerm >= uDiff) {
|
||||
errorTerm -= uDiff;
|
||||
curSubTile.v += vStep;
|
||||
|
||||
if ((curSubTile.v >> kTileSubShift) != tilePt.z) {
|
||||
curSubTile.z = curZ >> 16;
|
||||
|
||||
terrain |= tileTerrain(
|
||||
mapNum,
|
||||
tilePt,
|
||||
subTileMask_,
|
||||
minZ,
|
||||
maxZ + 1);
|
||||
if (terrain & opaqueTerrain) return terrain;
|
||||
|
||||
tilePt.v = curSubTile.v >> kTileSubShift;
|
||||
tileStartZ = curSubTile.z;
|
||||
subTileMask_ = 0;
|
||||
}
|
||||
|
||||
subTileMask_ |= (uMask[curSubTile.u & kSubTileMask] &
|
||||
vMask[curSubTile.v & kSubTileMask]);
|
||||
|
||||
if (debugChannelSet(-1, kDebugTiles)) {
|
||||
tempPoint.u = curSubTile.u << kTileSubShift;
|
||||
tempPoint.v = curSubTile.v << kTileSubShift;
|
||||
tempPoint.z = curSubTile.z;
|
||||
TPLine(prevPoint, tempPoint);
|
||||
prevPoint = tempPoint;
|
||||
warning("***************************");
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// V difference is greater
|
||||
|
||||
zStep /= vDiff;
|
||||
|
||||
for (;
|
||||
curSubTile.v != destSubTile.v;
|
||||
curSubTile.v += vStep) {
|
||||
curZ += zStep;
|
||||
|
||||
if ((curSubTile.v >> kTileSubShift) != tilePt.v) {
|
||||
curSubTile.z = curZ >> 16;
|
||||
|
||||
terrain |= tileTerrain(
|
||||
mapNum,
|
||||
tilePt,
|
||||
subTileMask_,
|
||||
minZ,
|
||||
maxZ + 1);
|
||||
if (terrain & opaqueTerrain) return terrain;
|
||||
|
||||
tilePt.v = curSubTile.v >> kTileSubShift;
|
||||
tileStartZ = curSubTile.z;
|
||||
subTileMask_ = 0;
|
||||
}
|
||||
|
||||
subTileMask_ |= (uMask[curSubTile.u & kSubTileMask] &
|
||||
vMask[curSubTile.v & kSubTileMask]);
|
||||
|
||||
if (debugChannelSet(-1, kDebugTiles)) {
|
||||
tempPoint.u = curSubTile.u << kTileSubShift;
|
||||
tempPoint.v = curSubTile.v << kTileSubShift;
|
||||
tempPoint.z = curSubTile.z;
|
||||
TPLine(prevPoint, tempPoint);
|
||||
prevPoint = tempPoint;
|
||||
}
|
||||
|
||||
errorTerm += uDiff;
|
||||
if (errorTerm >= vDiff) {
|
||||
errorTerm -= vDiff;
|
||||
curSubTile.u += uStep;
|
||||
|
||||
if ((curSubTile.u >> kTileSubShift) != tilePt.u) {
|
||||
curSubTile.z = curZ >> 16;
|
||||
|
||||
terrain |= tileTerrain(
|
||||
mapNum,
|
||||
tilePt,
|
||||
subTileMask_,
|
||||
minZ,
|
||||
maxZ + 1);
|
||||
if (terrain & opaqueTerrain) return terrain;
|
||||
|
||||
tilePt.u = curSubTile.u >> kTileSubShift;
|
||||
tileStartZ = curSubTile.z;
|
||||
subTileMask_ = 0;
|
||||
}
|
||||
|
||||
subTileMask_ |= (uMask[curSubTile.u & kSubTileMask] &
|
||||
vMask[curSubTile.v & kSubTileMask]);
|
||||
|
||||
if (debugChannelSet(-1, kDebugTiles)) {
|
||||
tempPoint.u = curSubTile.u << kTileSubShift;
|
||||
tempPoint.v = curSubTile.v << kTileSubShift;
|
||||
tempPoint.z = curSubTile.z;
|
||||
TPLine(prevPoint, tempPoint);
|
||||
prevPoint = tempPoint;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
curSubTile.z = curZ >> 16;
|
||||
|
||||
terrain |= tileTerrain(
|
||||
mapNum,
|
||||
tilePt,
|
||||
subTileMask_,
|
||||
minZ,
|
||||
maxZ);
|
||||
|
||||
return terrain;
|
||||
}
|
||||
|
||||
|
||||
/* ===================================================================== *
|
||||
Function to return slope information
|
||||
* ===================================================================== */
|
||||
|
||||
// This function determines the height of the tile's actual
|
||||
// surface, given by the tile slope information. If there are
|
||||
// several tiles within the same space, then it uses the highest
|
||||
// one who's base is below the character's feet. Tiles which
|
||||
// have no supporting surfaces are not considered.
|
||||
//
|
||||
// This routine now also returns a bunch of information about the
|
||||
// tile which forms the surface.
|
||||
|
||||
int16 tileSlopeHeight(
|
||||
const TilePoint &pt,
|
||||
int16 mapNum,
|
||||
int objectHeight,
|
||||
StandingTileInfo *stiResult,
|
||||
uint8 *platformResult) {
|
||||
// Calculate coordinates of tile, metatile, and subtile
|
||||
TilePoint tileCoords = pt >> kTileUVShift,
|
||||
metaCoords = tileCoords >> kPlatShift,
|
||||
origin = metaCoords << kPlatShift,
|
||||
coords = tileCoords - origin,
|
||||
subTile((pt.u >> kSubTileShift) & kSubTileMask,
|
||||
(pt.v >> kSubTileShift) & kSubTileMask,
|
||||
0);
|
||||
|
||||
MetaTilePtr metaPtr;
|
||||
StandingTileInfo highestTile, // Represents highest tile which is below
|
||||
// object's base
|
||||
lowestTile; // Represents lowest tile at tile position
|
||||
int16 supportHeight,
|
||||
highestSupportHeight,
|
||||
lowestSupportHeight;
|
||||
uint8 i,
|
||||
highestSupportPlatform = 0,
|
||||
lowestSupportPlatform = 0;
|
||||
|
||||
// Look up the metatile on the map.
|
||||
metaPtr = prevMeta = mapList[mapNum].lookupMeta(metaCoords);
|
||||
prevMapNum = mapNum;
|
||||
prevCoords.set(metaCoords.u, metaCoords.v, metaCoords.z);
|
||||
|
||||
if (metaPtr != nullptr) {
|
||||
highestTile.surfaceTile = lowestTile.surfaceTile = nullptr;
|
||||
highestSupportHeight = -100;
|
||||
lowestSupportHeight = 0x7FFF;
|
||||
|
||||
// Search each platform until we find a tile which is under
|
||||
// the character.
|
||||
|
||||
for (i = 0; i < kMaxPlatforms; i++) {
|
||||
Platform *p;
|
||||
|
||||
if ((p = metaPtr->fetchPlatform(mapNum, i)) == nullptr)
|
||||
continue;
|
||||
|
||||
if (p->flags & kPlVisible) {
|
||||
TileInfo *ti;
|
||||
StandingTileInfo sti;
|
||||
|
||||
// Get the tile, and its base height
|
||||
ti = p->fetchTAGInstance(
|
||||
mapNum,
|
||||
coords,
|
||||
origin,
|
||||
sti);
|
||||
|
||||
if (ti) {
|
||||
int16 tileBase = sti.surfaceHeight;
|
||||
int32 subTileTerrain =
|
||||
ti->attrs.testTerrain(calcSubTileMask(subTile.u,
|
||||
subTile.v));
|
||||
if (subTileTerrain & kTerrainInsubstantial)
|
||||
continue;
|
||||
else if (subTileTerrain & kTerrainSupportingRaised)
|
||||
// calculate height of raised surface
|
||||
supportHeight = sti.surfaceHeight +
|
||||
ti->attrs.terrainHeight;
|
||||
else if (subTileTerrain & kTerrainWater) {
|
||||
// calculate depth of water
|
||||
supportHeight = sti.surfaceHeight -
|
||||
ti->attrs.terrainHeight;
|
||||
tileBase = supportHeight;
|
||||
} else
|
||||
// calculate height of unraised surface
|
||||
supportHeight = sti.surfaceHeight +
|
||||
ptHeight(TilePoint(pt.u & kTileUVMask,
|
||||
pt.v & kTileUVMask,
|
||||
0),
|
||||
ti->attrs.cornerHeight);
|
||||
|
||||
// See if the tile is a potential supporting surface
|
||||
if (tileBase < pt.z + objectHeight
|
||||
&& supportHeight >= highestSupportHeight
|
||||
&& (ti->combinedTerrainMask() &
|
||||
(kTerrainSurface | kTerrainRaised))) {
|
||||
highestTile = sti;
|
||||
highestSupportHeight = supportHeight;
|
||||
highestSupportPlatform = i;
|
||||
} else if (highestTile.surfaceTile == nullptr &&
|
||||
supportHeight <= lowestSupportHeight &&
|
||||
(ti->combinedTerrainMask() &
|
||||
(kTerrainSurface | kTerrainRaised))) {
|
||||
lowestTile = sti;
|
||||
lowestSupportHeight = supportHeight;
|
||||
lowestSupportPlatform = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (highestTile.surfaceTile) {
|
||||
if (stiResult) *stiResult = highestTile;
|
||||
if (platformResult) *platformResult = highestSupportPlatform;
|
||||
return highestSupportHeight;
|
||||
}
|
||||
if (lowestTile.surfaceTile) {
|
||||
if (stiResult) *stiResult = lowestTile;
|
||||
if (platformResult) *platformResult = lowestSupportPlatform;
|
||||
return lowestSupportHeight;
|
||||
}
|
||||
}
|
||||
|
||||
if (stiResult) {
|
||||
stiResult->surfaceTile = nullptr;
|
||||
stiResult->surfaceTAG = nullptr;
|
||||
stiResult->surfaceHeight = 0;
|
||||
}
|
||||
if (platformResult) *platformResult = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Old-style version of tileSlopeHeight()
|
||||
int16 tileSlopeHeight(
|
||||
const TilePoint &pt,
|
||||
GameObject *obj,
|
||||
StandingTileInfo *stiResult,
|
||||
uint8 *platformResult) {
|
||||
assert(obj);
|
||||
assert(obj->proto());
|
||||
return tileSlopeHeight(
|
||||
pt,
|
||||
obj->getMapNum(),
|
||||
obj->proto()->height,
|
||||
stiResult,
|
||||
platformResult);
|
||||
}
|
||||
|
||||
// A version of tileSlopeHeight that takes an explicit map number
|
||||
int16 tileSlopeHeight(
|
||||
const TilePoint &pt,
|
||||
int mapNum,
|
||||
GameObject *obj,
|
||||
StandingTileInfo *stiResult,
|
||||
uint8 *platformResult) {
|
||||
assert(obj);
|
||||
assert(obj->proto());
|
||||
return tileSlopeHeight(
|
||||
pt,
|
||||
mapNum,
|
||||
obj->proto()->height,
|
||||
stiResult,
|
||||
platformResult);
|
||||
}
|
||||
|
||||
/* ===================================================================== *
|
||||
Test functions
|
||||
* ===================================================================== */
|
||||
|
||||
// return terrain that object is currently interacting with
|
||||
uint32 objectTerrain(GameObject *obj, StandingTileInfo &sti) {
|
||||
int16 mapNum = obj->getMapNum();
|
||||
ProtoObj *proto = obj->proto();
|
||||
uint32 terrain;
|
||||
TilePoint loc = obj->getLocation();
|
||||
|
||||
sti.surfaceTAG = nullptr;
|
||||
|
||||
terrain = volumeTerrain(mapNum,
|
||||
loc,
|
||||
proto->crossSection,
|
||||
proto->height);
|
||||
|
||||
// If one of the tiles we're standing on is active,
|
||||
// double check to see if we're really standing on it.
|
||||
|
||||
if (terrain & kTerrainActive) {
|
||||
int16 tHeight;
|
||||
|
||||
// Determine the height of the landscape we're on
|
||||
|
||||
tHeight = tileSlopeHeight(loc, obj, &sti);
|
||||
|
||||
// If the character is indeed standing ON the landscape
|
||||
// REM: This depends on the nature of the tile I think!!!
|
||||
|
||||
if (sti.surfaceTile == nullptr
|
||||
|| sti.surfaceTAG == nullptr
|
||||
|| !(sti.surfaceRef.flags & kTrTileSensitive)
|
||||
|| loc.z >= tHeight + 2
|
||||
/* || loc.z >= standingTile->attrs.terrainHeight */) {
|
||||
terrain &= ~kTerrainActive;
|
||||
}
|
||||
}
|
||||
|
||||
return terrain;
|
||||
}
|
||||
|
||||
// return terrain that object is currently interacting with
|
||||
int16 checkBlocked(
|
||||
GameObject *obj,
|
||||
int16 mapNum,
|
||||
const TilePoint &loc,
|
||||
GameObject **blockResultObj) {
|
||||
ProtoObj *proto = obj->proto();
|
||||
uint8 height = proto->height;
|
||||
int32 terrain;
|
||||
GameObject *blockObj;
|
||||
GameWorld *world;
|
||||
|
||||
if (blockResultObj) *blockResultObj = nullptr;
|
||||
|
||||
|
||||
// check to make sure the actor recognizes terrain
|
||||
if (!isActor(obj) || !((Actor *) obj)->hasEffect(kActorNoncorporeal)) {
|
||||
TilePoint testLoc = loc;
|
||||
|
||||
testLoc.z = MAX<int16>(loc.z, 8);
|
||||
|
||||
terrain = volumeTerrain(mapNum,
|
||||
testLoc,
|
||||
proto->crossSection,
|
||||
height);
|
||||
|
||||
// Check for intersection with a wall or obstacle
|
||||
if (terrain & kTerrainRaised) return kBlockageTerrain;
|
||||
}
|
||||
|
||||
// See if object collided with an object
|
||||
world = (GameWorld *)GameObject::objectAddress(mapList[mapNum].worldID);
|
||||
blockObj = objectCollision(obj, world, loc);
|
||||
if (blockObj) {
|
||||
if (blockResultObj) *blockResultObj = blockObj;
|
||||
return kBlockageObject;
|
||||
}
|
||||
|
||||
return kBlockageNone;
|
||||
}
|
||||
|
||||
// return terrain that object is currently interacting with
|
||||
int16 checkBlocked(
|
||||
GameObject *obj,
|
||||
const TilePoint &loc,
|
||||
GameObject **blockResultObj) {
|
||||
return checkBlocked(obj, obj->getMapNum(), loc, blockResultObj);
|
||||
}
|
||||
|
||||
// same as checkBlocked() above but includes additional "walking off
|
||||
// cliff" check
|
||||
int16 checkWalkable(
|
||||
GameObject *obj,
|
||||
const TilePoint &loc,
|
||||
GameObject **blockResultObj) {
|
||||
int16 result;
|
||||
int16 supportHeight;
|
||||
StandingTileInfo sti;
|
||||
|
||||
if ((result = checkBlocked(obj, loc, blockResultObj)) != kBlockageNone)
|
||||
return result;
|
||||
|
||||
supportHeight = tileSlopeHeight(loc, obj, &sti);
|
||||
|
||||
if (supportHeight < loc.z - kMaxStepHeight * 4)
|
||||
return kBlockageTerrain;
|
||||
|
||||
if (sti.surfaceTile != nullptr) {
|
||||
int16 subTileU,
|
||||
subTileV,
|
||||
mask;
|
||||
|
||||
subTileU = (loc.u & kTileUVMask) >> kSubTileShift;
|
||||
subTileV = (loc.v & kTileUVMask) >> kSubTileShift;
|
||||
mask = 1 << ((subTileU << kSubTileShift) + subTileV);
|
||||
|
||||
// If the suporting subtile is funiture consider this blocked
|
||||
if (sti.surfaceTile->attrs.testTerrain(mask) & kTerrainFurniture)
|
||||
return kBlockageTerrain;
|
||||
}
|
||||
|
||||
return kBlockageNone;
|
||||
}
|
||||
|
||||
// return terrain that object is currently interacting with
|
||||
int16 checkContact(
|
||||
GameObject *obj,
|
||||
const TilePoint &loc,
|
||||
GameObject **blockResultObj) {
|
||||
int16 mapNum = obj->getMapNum();
|
||||
ProtoObj *proto = obj->proto();
|
||||
int32 terrain;
|
||||
GameObject *blockObj;
|
||||
GameWorld *world;
|
||||
|
||||
if (blockResultObj) *blockResultObj = nullptr;
|
||||
|
||||
terrain = volumeTerrain(mapNum,
|
||||
loc,
|
||||
proto->crossSection,
|
||||
proto->height);
|
||||
|
||||
// Check for intersection with a wall or obstacle
|
||||
if (terrain & kTerrainRaised) return kBlockageTerrain;
|
||||
|
||||
// Check for intersection with slope of the terrain.
|
||||
if (((terrain & kTerrainSurface)
|
||||
&& loc.z <= tileSlopeHeight(loc, obj))
|
||||
|| (!(terrain & kTerrainWater)
|
||||
&& loc.z <= 0))
|
||||
return kBlockageTerrain;
|
||||
|
||||
// See if object collided with an object
|
||||
world = (GameWorld *)GameObject::objectAddress(mapList[mapNum].worldID);
|
||||
blockObj = objectCollision(obj, world, loc);
|
||||
if (blockObj) {
|
||||
if (blockResultObj) *blockResultObj = blockObj;
|
||||
return kBlockageObject;
|
||||
}
|
||||
|
||||
return kBlockageNone;
|
||||
}
|
||||
|
||||
} // end of namespace Saga2
|
||||
Reference in New Issue
Block a user