Files
2026-02-02 04:50:13 +01:00

799 lines
23 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 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
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "common/endian.h"
#include "common/memstream.h"
#include "common/textconsole.h"
#include "twine/debugger/debug_state.h"
#include "twine/menu/interface.h"
#include "twine/parser/blocklibrary.h"
#include "twine/renderer/redraw.h"
#include "twine/renderer/renderer.h"
#include "twine/renderer/screens.h"
#include "twine/resources/resources.h"
#include "twine/scene/actor.h"
#include "twine/scene/collision.h"
#include "twine/scene/graph.h"
#include "twine/scene/grid.h"
#include "twine/scene/scene.h"
#include "twine/shared.h"
#include "twine/twine.h"
#define CELLING_GRIDS_START_INDEX 120
namespace TwinE {
Grid::Grid(TwinEEngine *engine) : _engine(engine) {
_blockBufferSize = SIZE_CUBE_X * SIZE_CUBE_Z * SIZE_CUBE_Y * sizeof(BlockEntry);
_bufCube = (uint8 *)malloc(_blockBufferSize);
}
Grid::~Grid() {
free(_bufCube);
for (int32 i = 0; i < ARRAYSIZE(_brickMaskTable); i++) {
free(_brickMaskTable[i]);
}
for (int32 i = 0; i < ARRAYSIZE(_bufferBrick); i++) {
free(_bufferBrick[i]);
}
free(_currentGrid);
free(_nbBrickColon);
free(_listBrickColon);
}
void Grid::init(int32 w, int32 h) {
const int32 numbrickentries = (1 + (w + 24) / 24);
const size_t brickDataBufferSize = numbrickentries * MAX_BRICKS * sizeof(BrickEntry);
_listBrickColon = (BrickEntry *)malloc(brickDataBufferSize);
_brickInfoBufferSize = numbrickentries * sizeof(int16);
_nbBrickColon = (int16 *)malloc(_brickInfoBufferSize);
}
void Grid::copyMask(int32 index, int32 x, int32 y, const Graphics::ManagedSurface &buffer) {
if (_engine->_debugState->_disableGridRendering) {
return;
}
uint8 *ptr = _brickMaskTable[index];
int32 left = x + *(ptr + 2);
int32 top = y + *(ptr + 3);
int32 right = *ptr + left - 1;
int32 bottom = *(ptr + 1) + top - 1;
if (left > _engine->_interface->_clip.right || right < _engine->_interface->_clip.left || bottom < _engine->_interface->_clip.top || top > _engine->_interface->_clip.bottom) {
return;
}
ptr += 4;
int32 absX = left;
int32 absY = top;
int32 vSize = (bottom - top) + 1;
if (vSize <= 0) {
return;
}
int32 offset = -((right - left) - _engine->width()) - 1;
right++;
bottom++;
// if line on top aren't in the blitting area...
if (absY < _engine->_interface->_clip.top) {
int numOfLineToRemove = _engine->_interface->_clip.top - absY;
vSize -= numOfLineToRemove;
if (vSize <= 0) {
return;
}
absY += numOfLineToRemove;
do {
int lineDataSize;
lineDataSize = *(ptr++);
ptr += lineDataSize;
} while (--numOfLineToRemove);
}
// reduce the vSize to remove lines on bottom
if (absY + vSize - 1 > _engine->_interface->_clip.bottom) {
vSize = _engine->_interface->_clip.bottom - absY + 1;
if (vSize <= 0) {
return;
}
}
uint8 *outPtr = (uint8 *)_engine->_frontVideoBuffer.getBasePtr(left, absY);
const uint8 *inPtr = (const uint8 *)buffer.getBasePtr(left, absY);
do {
int32 height = *(ptr++);
do {
int32 width = *(ptr++); // skip size
outPtr += width;
inPtr += width;
absX += width;
height--;
if (!height) {
break;
}
width = *(ptr++); // copy size
for (int32 j = 0; j < width; j++) {
if (absX >= _engine->_interface->_clip.left && absX <= _engine->_interface->_clip.right) {
*outPtr = *inPtr;
}
absX++;
outPtr++;
inPtr++;
}
} while (--height);
absX = left;
outPtr += offset;
inPtr += offset;
} while (--vSize);
}
const BrickEntry *Grid::getBrickEntry(int32 j, int32 i) const {
return &_listBrickColon[j * MAX_BRICKS + i];
}
void Grid::drawOverBrick(int32 x, int32 y, int32 z) {
const int32 startCol = ((_engine->_interface->_clip.left + 24) / 24) - 1;
const int32 endCol = ((_engine->_interface->_clip.right + 24) / 24);
for (int32 col = startCol; col <= endCol; col++) {
for (int32 i = 0; i < _nbBrickColon[col]; i++) {
const BrickEntry *currBrickEntry = getBrickEntry(col, i);
if (currBrickEntry->posY + 38 > _engine->_interface->_clip.top && currBrickEntry->posY <= _engine->_interface->_clip.bottom && currBrickEntry->y >= y) {
if (currBrickEntry->x + currBrickEntry->z > z + x) {
copyMask(currBrickEntry->index, (col * 24) - 24, currBrickEntry->posY, _engine->_workVideoBuffer);
}
}
}
}
}
void Grid::drawOverBrick3(int32 x, int32 y, int32 z) {
const int32 startCol = ((_engine->_interface->_clip.left + 24) / 24) - 1;
const int32 endCol = (_engine->_interface->_clip.right + 24) / 24;
for (int32 col = startCol; col <= endCol; col++) {
for (int32 i = 0; i < _nbBrickColon[col]; i++) {
const BrickEntry *currBrickEntry = getBrickEntry(col, i);
if (currBrickEntry->posY + 38 > _engine->_interface->_clip.top && currBrickEntry->posY <= _engine->_interface->_clip.bottom && currBrickEntry->y >= y) {
if (currBrickEntry->x == x && currBrickEntry->z == z) {
copyMask(currBrickEntry->index, (col * 24) - 24, currBrickEntry->posY, _engine->_workVideoBuffer);
}
if (currBrickEntry->x > x || currBrickEntry->z > z) {
copyMask(currBrickEntry->index, (col * 24) - 24, currBrickEntry->posY, _engine->_workVideoBuffer);
}
}
}
}
}
void Grid::processGridMask(const uint8 *buffer, uint8 *ptr) {
const uint8 width = *buffer++;
uint8 height = *buffer++;
const uint8 offsetX = *buffer++;
const uint8 offsetY = *buffer++;
const int32 maxY = offsetY + height;
*ptr++ = width;
*ptr++ = height;
*ptr++ = offsetX;
*ptr++ = offsetY;
uint8 *targetPtrPos = ptr;
for (int32 y = offsetY; y < maxY; ++y) {
uint8 numOfBlock = 0;
uint8 opaquePixels = 0;
uint8 *numOfBlockTargetPtr = targetPtrPos;
targetPtrPos++;
const uint8 numRuns = *buffer++;
// the first time isn't skip. the skip size is 0 in that case
if (bits(*buffer, 6, 2) != 0) {
*targetPtrPos++ = 0;
numOfBlock++;
}
for (uint8 run = 0; run < numRuns; ++run) {
const uint8 runSpec = *buffer++;
const uint8 runLength = bits(runSpec, 0, 6) + 1;
const uint8 type = bits(runSpec, 6, 2);
if (type == 2) {
opaquePixels += runLength;
buffer++;
} else if (type == 1) { // 0x40
opaquePixels += runLength;
buffer += runLength;
} else { // skip (type 3)
if (opaquePixels) {
*targetPtrPos++ = opaquePixels; // write down the number of pixel passed so far
numOfBlock++;
opaquePixels = 0;
}
*targetPtrPos++ = runLength; // write skip
numOfBlock++;
}
}
if (opaquePixels) {
*targetPtrPos++ = opaquePixels;
numOfBlock++;
opaquePixels = 0;
}
*numOfBlockTargetPtr = numOfBlock;
}
}
void Grid::createGridMask() {
for (int32 b = 0; b < NUM_BRICKS; b++) {
if (!_brickUsageTable[b]) {
continue;
}
if (_brickMaskTable[b]) {
free(_brickMaskTable[b]);
}
_brickMaskTable[b] = (uint8 *)malloc(_brickSizeTable[b]);
processGridMask(_bufferBrick[b], _brickMaskTable[b]);
}
}
void Grid::getSpriteSize(int32 offset, int32 *width, int32 *height, const uint8 *spritePtr) {
spritePtr += READ_LE_INT32(spritePtr + offset * 4);
*width = *spritePtr;
*height = *(spritePtr + 1);
}
void Grid::loadGridBricks() {
uint32 firstBrick = 60000;
uint32 lastBrick = 0;
uint32 currentBllEntryIdx = 1;
memset(_brickSizeTable, 0, sizeof(_brickSizeTable));
memset(_brickUsageTable, 0, sizeof(_brickUsageTable));
// get block libraries usage bits
const uint8 *ptrToBllBits = _currentGrid + (_currentGridSize - 32);
// for all bits under the 32bytes (256bits)
for (uint32 i = 1; i < 256; i++) {
const uint8 currentBitByte = *(ptrToBllBits + (i / 8));
const uint8 currentBitMask = 1 << (7 - (i & 7));
if (currentBitByte & currentBitMask) {
const BlockData *currentBllPtr = getBlockLibrary(currentBllEntryIdx);
for (const BlockDataEntry &entry : currentBllPtr->entries) {
uint16 brickIdx = entry.brickIdx;
if (!brickIdx) {
continue;
}
brickIdx--;
if (brickIdx <= firstBrick) {
firstBrick = brickIdx;
}
if (brickIdx > lastBrick) {
lastBrick = brickIdx;
}
_brickUsageTable[brickIdx] = 1;
}
}
++currentBllEntryIdx;
}
for (uint32 i = firstBrick; i <= lastBrick; i++) {
if (!_brickUsageTable[i]) {
free(_bufferBrick[i]);
_bufferBrick[i] = nullptr;
continue;
}
_brickSizeTable[i] = HQR::getAllocEntry(&_bufferBrick[i], Resources::HQR_LBA_BRK_FILE, i);
if (_brickSizeTable[i] == 0) {
warning("Failed to load isometric brick index %i", i);
}
}
}
void Grid::decompColumn(const uint8 *gridEntry, uint32 gridEntrySize, uint8 *dest, uint32 destSize) { // DecompColonne
Common::MemoryReadStream stream(gridEntry, gridEntrySize);
Common::MemoryWriteStream outstream(dest, destSize);
int32 brickCount = stream.readByte();
do {
const int32 flag = stream.readByte();
const int32 blockCount = bits(flag, 0, 6) + 1;
const int32 type = bits(flag, 6, 2);
if (type == 0) {
for (int32 i = 0; i < blockCount; i++) {
outstream.writeUint16LE(0);
}
} else if (type == 1) { // 0x40
for (int32 i = 0; i < blockCount; i++) {
outstream.writeUint16LE(stream.readUint16LE());
}
} else {
const uint16 gridIdx = stream.readUint16LE();
for (int32 i = 0; i < blockCount; i++) {
outstream.writeUint16LE(gridIdx);
}
}
assert(!outstream.err());
assert(!stream.err());
} while (--brickCount);
}
void Grid::calcGraphMsk(const uint8 *gridEntry, uint32 gridEntrySize, uint8 *dest, uint32 destSize) {
Common::MemoryReadStream stream(gridEntry, gridEntrySize);
Common::SeekableMemoryWriteStream outstream(dest, destSize);
int32 brickCount = stream.readByte();
do {
const int32 flag = stream.readByte();
const int32 blockCount = bits(flag, 0, 6) + 1;
const int32 type = bits(flag, 6, 2);
if (type == 0) {
for (int32 i = 0; i < blockCount; i++) {
outstream.seek(outstream.pos() + 2);
}
} else if (type == 1) {
for (int32 i = 0; i < blockCount; i++) {
outstream.writeUint16LE(stream.readUint16LE());
}
} else {
const uint16 gridIdx = stream.readUint16LE();
for (int32 i = 0; i < blockCount; i++) {
outstream.writeUint16LE(gridIdx);
}
}
assert(!outstream.err());
assert(!stream.err());
} while (--brickCount);
}
void Grid::copyMapToCube() {
int32 blockOffset = 0;
for (int32 z = 0; z < SIZE_CUBE_Z; z++) {
const int32 gridIdx = z * SIZE_CUBE_X;
for (int32 x = 0; x < SIZE_CUBE_X; x++) {
const int32 gridOffset = READ_LE_UINT16(_currentGrid + 2 * (x + gridIdx));
decompColumn(_currentGrid + gridOffset, _currentGridSize - gridOffset, _bufCube + blockOffset, _blockBufferSize - blockOffset);
blockOffset += 2 * SIZE_CUBE_Y;
}
}
}
void Grid::createCellingGridMap(const uint8 *gridPtr, int32 gridPtrSize) { // MixteMapToCube
int32 currGridOffset = 0;
int32 blockOffset = 0;
for (int32 z = 0; z < SIZE_CUBE_Z; z++) {
const uint8 *tempGridPtr = gridPtr + currGridOffset;
for (int32 x = 0; x < SIZE_CUBE_X; x++) {
const int gridOffset = READ_LE_UINT16(tempGridPtr);
tempGridPtr += 2;
calcGraphMsk(gridPtr + gridOffset, gridPtrSize - gridOffset, _bufCube + blockOffset, _blockBufferSize - blockOffset);
blockOffset += 2 * SIZE_CUBE_Y;
}
currGridOffset += SIZE_CUBE_X + SIZE_CUBE_Z;
}
}
bool Grid::initGrid(int32 index) {
// load grids from file
_currentGridSize = HQR::getAllocEntry(&_currentGrid, Resources::HQR_LBA_GRI_FILE, index);
if (_currentGridSize == 0) {
warning("Failed to load grid index: %i", index);
return false;
}
// load layouts from file
if (!_currentBlockLibrary.loadFromHQR(Resources::HQR_LBA_BLL_FILE, index, _engine->isLBA1())) {
warning("Failed to load block library index: %i", index);
return false;
}
loadGridBricks();
createGridMask();
copyMapToCube();
return true;
}
bool Grid::initCellingGrid(int32 index) { // IncrustGrm
uint8 *gridPtr = nullptr;
// load grids from file
const int realIndex = index + CELLING_GRIDS_START_INDEX;
const int32 gridSize = HQR::getAllocEntry(&gridPtr, Resources::HQR_LBA_GRI_FILE, realIndex);
if (gridSize == 0) {
warning("Failed to load grid index %i", realIndex);
return false;
}
createCellingGridMap(gridPtr, gridSize);
free(gridPtr);
_engine->_redraw->_firstTime = true;
return true;
}
bool Grid::drawGraph(int32 index, int32 posX, int32 posY) { // AffGraph
return drawGraph(posX, posY, _bufferBrick[index], false);
}
bool Grid::drawGraph(int32 index, int32 posX, int32 posY, const uint8 *ptr) { // AffGraph
ptr = ptr + READ_LE_INT32(ptr + index * 4); // GetGraph, GetMask
return drawGraph(posX, posY, ptr, true);
}
bool Grid::drawSprite(int32 posX, int32 posY, const SpriteData &ptr, int spriteIndex) {
const int32 left = posX + ptr.offsetX(spriteIndex);
if (left >= _engine->_interface->_clip.right) {
return false;
}
const int32 right = ptr.surface(spriteIndex).w + left;
if (right < _engine->_interface->_clip.left) {
return false;
}
const int32 top = posY + ptr.offsetY(spriteIndex);
if (top >= _engine->_interface->_clip.bottom) {
return false;
}
const int32 bottom = ptr.surface(spriteIndex).h + top;
if (bottom < _engine->_interface->_clip.top) {
return false;
}
const Common::Point pos(left, top);
_engine->_frontVideoBuffer.transBlitFrom(ptr.surface(spriteIndex), pos);
return true;
}
bool Grid::drawGraph(int32 posX, int32 posY, const uint8 *pGraph, bool isSprite) {
if (_engine->_debugState->_disableGridRendering) {
return false;
}
const Common::Rect &clip = _engine->_interface->_clip;
TwineScreen &frontVideoBuffer = _engine->_frontVideoBuffer;
return TwinE::drawGraph(posX, posY, pGraph, isSprite, frontVideoBuffer, clip);
}
const uint8 *Grid::getBlockBufferGround(const IVec3 &pos, int32 &ground) {
const IVec3 &collision = updateCollisionCoordinates(pos.x, pos.y, pos.z);
const uint8 *ptr = _bufCube + collision.y * 2 + collision.x * SIZE_CUBE_Y * 2 + collision.z * SIZE_CUBE_X * SIZE_CUBE_Y * 2;
int32 collisionY = collision.y;
while (collisionY) {
if (READ_LE_INT16(ptr)) { // found the ground - sizeof(BlockEntry);
break;
}
collisionY--;
ptr -= sizeof(int16);
}
_engine->_collision->_collision.y = collisionY;
ground = (int16)((collisionY + 1) * SIZE_BRICK_Y);
return ptr;
}
const BlockDataEntry *Grid::getAdrBlock(int32 blockIdx, int32 brickIdx) const {
const BlockData *blockPtr = getBlockLibrary(blockIdx);
return &blockPtr->entries[brickIdx];
}
const BlockData *Grid::getBlockLibrary(int32 blockIdx) const {
return _currentBlockLibrary.getLayout(blockIdx - 1);
}
void Grid::map2Screen(int32 x, int32 y, int32 z, int32 &posx, int32 &posy) const {
posx = (x - z) * 24 + _engine->width() / 2 - SIZE_CUBE_X / 2;
posy = ((x + z) * 12) - (y * 15) + _engine->height() / 2 - SIZE_CUBE_Y;
}
void Grid::drawBrickBlock(int32 blockIdx, int32 brickBlockIdx, int32 x, int32 y, int32 z) { // AffBrickBlock
const BlockDataEntry *blockPtr = getAdrBlock(blockIdx, brickBlockIdx);
const uint8 brickShape = blockPtr->brickShape;
const uint8 brickSound = blockPtr->brickType;
const uint16 numBrick = blockPtr->brickIdx;
if (!numBrick) {
return;
}
int32 brickPixelPosX = 0;
int32 brickPixelPosY = 0;
map2Screen(x - _startCube.x, y - _startCube.y, z - _startCube.z, brickPixelPosX, brickPixelPosY);
if (brickPixelPosX < -24) {
return;
}
if (brickPixelPosX >= _engine->width()) {
return;
}
if (brickPixelPosY < -38) {
return;
}
if (brickPixelPosY >= _engine->height()) {
return;
}
// draw the background brick
drawGraph(numBrick - 1, brickPixelPosX, brickPixelPosY);
int32 col = (brickPixelPosX + 24) / 24;
if (_nbBrickColon[col] >= MAX_BRICKS) {
warning("GRID: brick buffer exceeded");
return;
}
BrickEntry *pColonBrick = &_listBrickColon[col * MAX_BRICKS + _nbBrickColon[col]];
pColonBrick->x = x;
pColonBrick->y = y;
pColonBrick->z = z;
pColonBrick->posX = brickPixelPosX;
pColonBrick->posY = brickPixelPosY;
pColonBrick->index = numBrick - 1;
pColonBrick->shape = brickShape;
pColonBrick->sound = brickSound;
_nbBrickColon[col]++;
}
void Grid::redrawGrid() { // AffGrille
_worldCube.x = _startCube.x * SIZE_BRICK_XZ;
_worldCube.y = _startCube.y * SIZE_BRICK_Y;
_worldCube.z = _startCube.z * SIZE_BRICK_XZ;
const IVec3 &projPos = _engine->_renderer->projectPoint(-_worldCube);
_engine->_redraw->_projPosScreen.x = projPos.x;
_engine->_redraw->_projPosScreen.y = projPos.y;
memset(_nbBrickColon, 0, _brickInfoBufferSize);
if (!_engine->_scene->_flagRenderGrid) {
return;
}
_engine->_screens->clearScreen();
for (int32 z = 0; z < SIZE_CUBE_Z; z++) {
for (int32 x = 0; x < SIZE_CUBE_X; x++) {
for (int32 y = 0; y < SIZE_CUBE_Y; y++) {
const BlockEntry entry = getBlockEntry(x, y, z);
if (entry.blockIdx) {
drawBrickBlock(entry.blockIdx, entry.brickBlockIdx, x, y, z);
}
}
}
}
}
BlockEntry Grid::getBlockEntry(int32 xmap, int32 ymap, int32 zmap) const {
const uint8 *pCube = _bufCube;
const int32 size = 2; // sizeof(BlockEntry);
pCube += xmap * SIZE_CUBE_Y * size;
pCube += ymap * size;
pCube += zmap * (SIZE_CUBE_X * SIZE_CUBE_Y * size);
BlockEntry entry;
entry.blockIdx = *pCube;
entry.brickBlockIdx = *(pCube + 1);
return entry;
}
ShapeType Grid::worldColBrick(int32 x, int32 y, int32 z) {
const IVec3 &collision = updateCollisionCoordinates(x, y, z);
if (collision.y <= -1) {
return ShapeType::kSolid;
}
if (collision.x < 0 || collision.x >= SIZE_CUBE_X) {
return ShapeType::kNone;
}
if (collision.y < 0 || collision.y >= SIZE_CUBE_Y) {
return ShapeType::kNone;
}
if (collision.z < 0 || collision.z >= SIZE_CUBE_Z) {
return ShapeType::kNone;
}
const BlockEntry entry = getBlockEntry(collision.x, collision.y, collision.z);
if (entry.blockIdx) {
const BlockDataEntry *blockPtr = getAdrBlock(entry.blockIdx, entry.brickBlockIdx);
return (ShapeType)blockPtr->brickShape;
}
return (ShapeType)entry.brickBlockIdx; // eventually transparent color
}
const IVec3 &Grid::updateCollisionCoordinates(int32 x, int32 y, int32 z) {
_engine->_collision->_collision.x = (x + DEMI_BRICK_XZ) / SIZE_BRICK_XZ;
_engine->_collision->_collision.y = y / SIZE_BRICK_Y;
_engine->_collision->_collision.z = (z + DEMI_BRICK_XZ) / SIZE_BRICK_XZ;
return _engine->_collision->_collision;
}
bool Grid::shouldCheckWaterCol(int32 actorIdx) const {
if (actorIdx == OWN_ACTOR_SCENE_INDEX) {
ActorStruct *ptrobj = _engine->_scene->getActor(actorIdx);
if (_engine->_actor->_heroBehaviour != HeroBehaviourType::kProtoPack
&& ptrobj->_flags.bComputeCollisionWithFloor
&& !ptrobj->_flags.bIsInvisible
&& !ptrobj->_workFlags.bIsFalling
&& ptrobj->_carryBy == -1) {
return true;
}
}
return false;
}
ShapeType Grid::worldColBrickFull(int32 x, int32 y, int32 z, int32 y2, int32 actorIdx) {
const IVec3 &collision = updateCollisionCoordinates(x, y, z);
if (collision.y <= -1) {
return ShapeType::kSolid;
}
if (collision.x < 0 || collision.x >= SIZE_CUBE_X || collision.z < 0 || collision.z >= SIZE_CUBE_Z) {
return ShapeType::kNone;
}
bool checkWater = shouldCheckWaterCol(actorIdx);
uint8 *pCube = _bufCube;
pCube += collision.x * SIZE_CUBE_Y * 2;
pCube += collision.y * 2;
pCube += collision.z * (SIZE_CUBE_X * SIZE_CUBE_Y * 2);
uint8 block = *pCube;
ShapeType brickShape;
const uint8 tmpBrickIdx = *(pCube + 1);
if (block) {
const BlockDataEntry *blockPtr = getAdrBlock(block, tmpBrickIdx);
if (checkWater && blockPtr->brickType == WATER_BRICK) {
brickShape = ShapeType::kSolid; // full collision
} else {
brickShape = (ShapeType)blockPtr->brickShape;
}
} else {
brickShape = (ShapeType)tmpBrickIdx; // maybe transparency
if (checkWater) {
uint8 *pCode = pCube;
for (y = collision.y - 1; y >= 0; y--) {
pCode -= 2;
uint8 code = *pCode;
if (code) {
const BlockDataEntry *blockPtr = getAdrBlock(block, 0);
if (blockPtr->brickType == WATER_BRICK) {
// Special check mount funfrock
if (_engine->_scene->_numCube != LBA1SceneId::Polar_Island_on_the_rocky_peak) {
// full collision
return ShapeType::kSolid;
}
}
break; // stop parsing at first encountered brick
}
}
}
}
int32 ymax = (y2 + (SIZE_BRICK_Y - 1)) / SIZE_BRICK_Y;
// check full height
for (y = collision.y; ymax > 0 && y < (SIZE_CUBE_Y - 1); --ymax, y++) {
pCube += 2;
if (READ_LE_INT16(pCube)) {
return ShapeType::kSolid;
}
}
return brickShape;
}
uint8 Grid::worldCodeBrick(int32 x, int32 y, int32 z) {
uint8 code = 0xF0U;
if (y > -1) {
const IVec3 &collision = updateCollisionCoordinates(x, y, z);
const BlockEntry entry = getBlockEntry(collision.x, collision.y, collision.z);
if (entry.blockIdx) {
const BlockDataEntry *blockPtr = getAdrBlock(entry.blockIdx, entry.brickBlockIdx);
code = blockPtr->brickType;
}
}
return code;
}
void Grid::centerOnActor(const ActorStruct *actor) {
_startCube.x = (actor->_posObj.x + SIZE_BRICK_Y) / SIZE_BRICK_XZ;
_startCube.y = (actor->_posObj.y + SIZE_BRICK_Y) / SIZE_BRICK_Y;
_startCube.z = (actor->_posObj.z + SIZE_BRICK_Y) / SIZE_BRICK_XZ;
_engine->_redraw->_firstTime = true;
}
void Grid::centerScreenOnActor() {
if (_engine->_cameraZone) {
return;
}
if (_engine->_debugState->_useFreeCamera) {
return;
}
ActorStruct *actor = _engine->_scene->getActor(_engine->_scene->_numObjFollow);
const IVec3 projPos = _engine->_renderer->projectPoint(actor->_posObj.x - (_startCube.x * SIZE_BRICK_XZ),
actor->_posObj.y - (_startCube.y * SIZE_BRICK_Y),
actor->_posObj.z - (_startCube.z * SIZE_BRICK_XZ));
// TODO: these border values should get scaled for higher resolutions
if (projPos.x < 80 || projPos.x >= _engine->width() - 60 || projPos.y < 80 || projPos.y >= _engine->height() - 50) {
_startCube.x = ((actor->_posObj.x + SIZE_BRICK_Y) / SIZE_BRICK_XZ) + (((actor->_posObj.x + SIZE_BRICK_Y) / SIZE_BRICK_XZ) - _startCube.x) / 2;
_startCube.y = actor->_posObj.y / SIZE_BRICK_Y;
_startCube.z = ((actor->_posObj.z + SIZE_BRICK_Y) / SIZE_BRICK_XZ) + (((actor->_posObj.z + SIZE_BRICK_Y) / SIZE_BRICK_XZ) - _startCube.z) / 2;
if (_startCube.x >= SIZE_CUBE_X) {
_startCube.x = SIZE_CUBE_X - 1;
}
if (_startCube.z >= SIZE_CUBE_Z) {
_startCube.z = SIZE_CUBE_Z - 1;
}
_engine->_redraw->_firstTime = true;
}
}
} // namespace TwinE