Initial commit
This commit is contained in:
798
engines/twine/scene/grid.cpp
Normal file
798
engines/twine/scene/grid.cpp
Normal file
@@ -0,0 +1,798 @@
|
||||
/* 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
|
||||
Reference in New Issue
Block a user