1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "twine/scene/grid.h"
24 #include "common/endian.h"
25 #include "common/memstream.h"
26 #include "common/textconsole.h"
27 #include "twine/debugger/debug_grid.h"
28 #include "twine/menu/interface.h"
29 #include "twine/parser/blocklibrary.h"
30 #include "twine/renderer/redraw.h"
31 #include "twine/renderer/renderer.h"
32 #include "twine/renderer/screens.h"
33 #include "twine/resources/resources.h"
34 #include "twine/scene/actor.h"
35 #include "twine/scene/collision.h"
36 #include "twine/scene/scene.h"
37 #include "twine/twine.h"
38 
39 #define CELLING_GRIDS_START_INDEX 120
40 
41 namespace TwinE {
42 
Grid(TwinEEngine * engine)43 Grid::Grid(TwinEEngine *engine) : _engine(engine) {
44 	_blockBufferSize = GRID_SIZE_X * GRID_SIZE_Z * GRID_SIZE_Y * sizeof(BlockEntry);
45 	_blockBuffer = (uint8 *)malloc(_blockBufferSize);
46 }
47 
~Grid()48 Grid::~Grid() {
49 	free(_blockBuffer);
50 	for (int32 i = 0; i < ARRAYSIZE(_brickMaskTable); i++) {
51 		free(_brickMaskTable[i]);
52 	}
53 	for (int32 i = 0; i < ARRAYSIZE(_brickTable); i++) {
54 		free(_brickTable[i]);
55 	}
56 	free(_currentGrid);
57 	free(_brickInfoBuffer);
58 	free(_bricksDataBuffer);
59 }
60 
init(int32 w,int32 h)61 void Grid::init(int32 w, int32 h) {
62 	const int32 numbrickentries = (1 + (w + 24) / 24);
63 	const size_t brickDataBufferSize = numbrickentries * MAXBRICKS * sizeof(BrickEntry);
64 	_bricksDataBuffer = (BrickEntry *)malloc(brickDataBufferSize);
65 	_brickInfoBufferSize = numbrickentries * sizeof(int16);
66 	_brickInfoBuffer = (int16 *)malloc(_brickInfoBufferSize);
67 }
68 
copyGridMask(int32 index,int32 x,int32 y,const Graphics::ManagedSurface & buffer)69 void Grid::copyGridMask(int32 index, int32 x, int32 y, const Graphics::ManagedSurface &buffer) {
70 	uint8 *ptr = _brickMaskTable[index];
71 
72 	int32 left = x + *(ptr + 2);
73 	int32 top = y + *(ptr + 3);
74 	int32 right = *ptr + left - 1;
75 	int32 bottom = *(ptr + 1) + top - 1;
76 
77 	if (left > _engine->_interface->_clip.right || right < _engine->_interface->_clip.left || bottom < _engine->_interface->_clip.top || top > _engine->_interface->_clip.bottom) {
78 		return;
79 	}
80 
81 	ptr += 4;
82 
83 	int32 absX = left;
84 	int32 absY = top;
85 
86 	int32 vSize = (bottom - top) + 1;
87 
88 	if (vSize <= 0) {
89 		return;
90 	}
91 
92 	int32 offset = -((right - left) - _engine->width()) - 1;
93 
94 	right++;
95 	bottom++;
96 
97 	// if line on top aren't in the blitting area...
98 	if (absY < _engine->_interface->_clip.top) {
99 		int numOfLineToRemove = _engine->_interface->_clip.top - absY;
100 
101 		vSize -= numOfLineToRemove;
102 		if (vSize <= 0) {
103 			return;
104 		}
105 
106 		absY += numOfLineToRemove;
107 
108 		do {
109 			int lineDataSize;
110 
111 			lineDataSize = *(ptr++);
112 			ptr += lineDataSize;
113 		} while (--numOfLineToRemove);
114 	}
115 
116 	// reduce the vSize to remove lines on bottom
117 	if (absY + vSize - 1 > _engine->_interface->_clip.bottom) {
118 		vSize = _engine->_interface->_clip.bottom - absY + 1;
119 		if (vSize <= 0) {
120 			return;
121 		}
122 	}
123 
124 	uint8 *outPtr = (uint8 *)_engine->_frontVideoBuffer.getBasePtr(left, absY);
125 	const uint8 *inPtr = (const uint8 *)buffer.getBasePtr(left, absY);
126 
127 	do {
128 		int32 height = *(ptr++);
129 
130 		do {
131 			int32 width = *(ptr++); // skip size
132 			outPtr += width;
133 			inPtr += width;
134 
135 			absX += width;
136 
137 			height--;
138 			if (!height) {
139 				break;
140 			}
141 
142 			width = *(ptr++); // copy size
143 
144 			for (int32 j = 0; j < width; j++) {
145 				if (absX >= _engine->_interface->_clip.left && absX <= _engine->_interface->_clip.right) {
146 					*outPtr = *inPtr;
147 				}
148 
149 				absX++;
150 				outPtr++;
151 				inPtr++;
152 			}
153 		} while (--height);
154 
155 		absX = left;
156 
157 		outPtr += offset;
158 		inPtr += offset;
159 	} while (--vSize);
160 }
161 
getBrickEntry(int32 j,int32 i) const162 const BrickEntry* Grid::getBrickEntry(int32 j, int32 i) const {
163 	return &_bricksDataBuffer[j * MAXBRICKS + i];
164 }
165 
drawOverModelActor(int32 x,int32 y,int32 z)166 void Grid::drawOverModelActor(int32 x, int32 y, int32 z) {
167 	const int32 copyBlockPhysLeft = ((_engine->_interface->_clip.left + 24) / 24) - 1;
168 	const int32 copyBlockPhysRight = ((_engine->_interface->_clip.right + 24) / 24);
169 
170 	for (int32 j = copyBlockPhysLeft; j <= copyBlockPhysRight; j++) {
171 		for (int32 i = 0; i < _brickInfoBuffer[j]; i++) {
172 			const BrickEntry *currBrickEntry = getBrickEntry(j, i);
173 
174 			if (currBrickEntry->posY + 38 > _engine->_interface->_clip.top && currBrickEntry->posY <= _engine->_interface->_clip.bottom && currBrickEntry->y >= y) {
175 				if (currBrickEntry->x + currBrickEntry->z > z + x) {
176 					copyGridMask(currBrickEntry->index, (j * 24) - 24, currBrickEntry->posY, _engine->_workVideoBuffer);
177 				}
178 			}
179 		}
180 	}
181 }
182 
drawOverSpriteActor(int32 x,int32 y,int32 z)183 void Grid::drawOverSpriteActor(int32 x, int32 y, int32 z) {
184 	const int32 copyBlockPhysLeft = ((_engine->_interface->_clip.left + 24) / 24) - 1;
185 	const int32 copyBlockPhysRight = (_engine->_interface->_clip.right + 24) / 24;
186 
187 	for (int32 j = copyBlockPhysLeft; j <= copyBlockPhysRight; j++) {
188 		for (int32 i = 0; i < _brickInfoBuffer[j]; i++) {
189 			const BrickEntry *currBrickEntry = getBrickEntry(j, i);
190 
191 			if (currBrickEntry->posY + 38 > _engine->_interface->_clip.top && currBrickEntry->posY <= _engine->_interface->_clip.bottom && currBrickEntry->y >= y) {
192 				if (currBrickEntry->x == x && currBrickEntry->z == z) {
193 					copyGridMask(currBrickEntry->index, (j * 24) - 24, currBrickEntry->posY, _engine->_workVideoBuffer);
194 				}
195 
196 				if (currBrickEntry->x > x || currBrickEntry->z > z) {
197 					copyGridMask(currBrickEntry->index, (j * 24) - 24, currBrickEntry->posY, _engine->_workVideoBuffer);
198 				}
199 			}
200 		}
201 	}
202 }
203 
processGridMask(const uint8 * buffer,uint8 * ptr)204 void Grid::processGridMask(const uint8 *buffer, uint8 *ptr) {
205 	const uint8 width = *buffer++;
206 	uint8 height = *buffer++;
207 	const uint8 offsetX = *buffer++;
208 	const uint8 offsetY = *buffer++;
209 	const int32 maxY = offsetY + height;
210 
211 	*ptr++ = width;
212 	*ptr++ = height;
213 	*ptr++ = offsetX;
214 	*ptr++ = offsetY;
215 
216 	uint8 *targetPtrPos = ptr;
217 
218 	for (int32 y = offsetY; y < maxY; ++y) {
219 		uint8 numOfBlock = 0;
220 		uint8 opaquePixels = 0;
221 		uint8 *numOfBlockTargetPtr = targetPtrPos;
222 
223 		targetPtrPos++;
224 
225 		const uint8 numRuns = *buffer++;
226 
227 		// the first time isn't skip. the skip size is 0 in that case
228 		if (bits(*buffer, 6, 2) != 0) {
229 			*targetPtrPos++ = 0;
230 			numOfBlock++;
231 		}
232 
233 		for (uint8 run = 0; run < numRuns; ++run) {
234 			const uint8 runSpec = *buffer++;
235 			const uint8 runLength = bits(runSpec, 0, 6) + 1;
236 			const uint8 type = bits(runSpec, 6, 2);
237 			if (type == 2) {
238 				opaquePixels += runLength;
239 				buffer++;
240 			} else if (type == 1) {
241 				opaquePixels += runLength;
242 				buffer += runLength;
243 			} else { // skip (type 3)
244 				if (opaquePixels) {
245 					*targetPtrPos++ = opaquePixels; // write down the number of pixel passed so far
246 					numOfBlock++;
247 					opaquePixels = 0;
248 				}
249 				*targetPtrPos++ = runLength; //write skip
250 				numOfBlock++;
251 			}
252 		}
253 
254 		if (opaquePixels) {
255 			*targetPtrPos++ = opaquePixels;
256 			numOfBlock++;
257 
258 			opaquePixels = 0;
259 		}
260 
261 		*numOfBlockTargetPtr = numOfBlock;
262 	}
263 }
264 
createGridMask()265 void Grid::createGridMask() {
266 	for (int32 b = 0; b < NUM_BRICKS; b++) {
267 		if (!_brickUsageTable[b]) {
268 			continue;
269 		}
270 		if (_brickMaskTable[b]) {
271 			free(_brickMaskTable[b]);
272 		}
273 		_brickMaskTable[b] = (uint8 *)malloc(_brickSizeTable[b]);
274 		processGridMask(_brickTable[b], _brickMaskTable[b]);
275 	}
276 }
277 
getSpriteSize(int32 offset,int32 * width,int32 * height,const uint8 * spritePtr)278 void Grid::getSpriteSize(int32 offset, int32 *width, int32 *height, const uint8 *spritePtr) {
279 	spritePtr += READ_LE_INT32(spritePtr + offset * 4);
280 
281 	*width = *spritePtr;
282 	*height = *(spritePtr + 1);
283 }
284 
loadGridBricks()285 void Grid::loadGridBricks() {
286 	uint32 firstBrick = 60000;
287 	uint32 lastBrick = 0;
288 	uint32 currentBllEntryIdx = 1;
289 
290 	memset(_brickSizeTable, 0, sizeof(_brickSizeTable));
291 	memset(_brickUsageTable, 0, sizeof(_brickUsageTable));
292 
293 	// get block libraries usage bits
294 	const uint8 *ptrToBllBits = _currentGrid + (_currentGridSize - 32);
295 
296 	// for all bits under the 32bytes (256bits)
297 	for (uint32 i = 1; i < 256; i++) {
298 		const uint8 currentBitByte = *(ptrToBllBits + (i / 8));
299 		const uint8 currentBitMask = 1 << (7 - (i & 7));
300 
301 		if (currentBitByte & currentBitMask) {
302 			const BlockData *currentBllPtr = getBlockLibrary(currentBllEntryIdx);
303 			for (const BlockDataEntry &entry : currentBllPtr->entries) {
304 				uint16 brickIdx = entry.brickIdx;
305 				if (!brickIdx) {
306 					continue;
307 				}
308 				brickIdx--;
309 				if (brickIdx <= firstBrick) {
310 					firstBrick = brickIdx;
311 				}
312 
313 				if (brickIdx > lastBrick) {
314 					lastBrick = brickIdx;
315 				}
316 
317 				_brickUsageTable[brickIdx] = 1;
318 			}
319 		}
320 		++currentBllEntryIdx;
321 	}
322 
323 	for (uint32 i = firstBrick; i <= lastBrick; i++) {
324 		if (!_brickUsageTable[i]) {
325 			free(_brickTable[i]);
326 			_brickTable[i] = nullptr;
327 			continue;
328 		}
329 		_brickSizeTable[i] = HQR::getAllocEntry(&_brickTable[i], Resources::HQR_LBA_BRK_FILE, i);
330 		if (_brickSizeTable[i] == 0) {
331 			warning("Failed to load isometric brick index %i", i);
332 		}
333 	}
334 }
335 
createGridColumn(const uint8 * gridEntry,uint32 gridEntrySize,uint8 * dest,uint32 destSize)336 void Grid::createGridColumn(const uint8 *gridEntry, uint32 gridEntrySize, uint8 *dest, uint32 destSize) {
337 	Common::MemoryReadStream stream(gridEntry, gridEntrySize);
338 	Common::MemoryWriteStream outstream(dest, destSize);
339 	int32 brickCount = stream.readByte();
340 
341 	do {
342 		const int32 flag = stream.readByte();
343 		const int32 blockCount = bits(flag, 0, 6) + 1;
344 		const int32 type = bits(flag, 6, 2);
345 		if (type == 0) {
346 			for (int32 i = 0; i < blockCount; i++) {
347 				outstream.writeUint16LE(0);
348 			}
349 		} else if (type == 1) {
350 			for (int32 i = 0; i < blockCount; i++) {
351 				outstream.writeUint16LE(stream.readUint16LE());
352 			}
353 		} else {
354 			const uint16 gridIdx = stream.readUint16LE();
355 			for (int32 i = 0; i < blockCount; i++) {
356 				outstream.writeUint16LE(gridIdx);
357 			}
358 		}
359 		assert(!outstream.err());
360 		assert(!stream.err());
361 	} while (--brickCount);
362 }
363 
createCellingGridColumn(const uint8 * gridEntry,uint32 gridEntrySize,uint8 * dest,uint32 destSize)364 void Grid::createCellingGridColumn(const uint8 *gridEntry, uint32 gridEntrySize, uint8 *dest, uint32 destSize) {
365 	Common::MemoryReadStream stream(gridEntry, gridEntrySize);
366 	Common::SeekableMemoryWriteStream outstream(dest, destSize);
367 	int32 brickCount = stream.readByte();
368 
369 	do {
370 		const int32 flag = stream.readByte();
371 		const int32 blockCount = bits(flag, 0, 6) + 1;
372 		const int32 type = bits(flag, 6, 2);
373 
374 		if (type == 0) {
375 			for (int32 i = 0; i < blockCount; i++) {
376 				outstream.seek(outstream.pos() + 2);
377 			}
378 		} else if (type == 1) {
379 			for (int32 i = 0; i < blockCount; i++) {
380 				outstream.writeUint16LE(stream.readUint16LE());
381 			}
382 		} else {
383 			const uint16 gridIdx = stream.readUint16LE();
384 			for (int32 i = 0; i < blockCount; i++) {
385 				outstream.writeUint16LE(gridIdx);
386 			}
387 		}
388 		assert(!outstream.err());
389 		assert(!stream.err());
390 	} while (--brickCount);
391 }
392 
createGridMap()393 void Grid::createGridMap() {
394 	int32 blockOffset = 0;
395 
396 	for (int32 z = 0; z < GRID_SIZE_Z; z++) {
397 		const int32 gridIdx = z * GRID_SIZE_X;
398 
399 		for (int32 x = 0; x < GRID_SIZE_X; x++) {
400 			const int32 gridOffset = READ_LE_UINT16(_currentGrid + 2 * (x + gridIdx));
401 			createGridColumn(_currentGrid + gridOffset, _currentGridSize - gridOffset, _blockBuffer + blockOffset, _blockBufferSize - blockOffset);
402 			blockOffset += 2 * GRID_SIZE_Y;
403 		}
404 	}
405 }
406 
createCellingGridMap(const uint8 * gridPtr,int32 gridPtrSize)407 void Grid::createCellingGridMap(const uint8 *gridPtr, int32 gridPtrSize) {
408 	int32 currGridOffset = 0;
409 	int32 blockOffset = 0;
410 
411 	for (int32 z = 0; z < GRID_SIZE_Z; z++) {
412 		const uint8 *tempGridPtr = gridPtr + currGridOffset;
413 
414 		for (int32 x = 0; x < GRID_SIZE_X; x++) {
415 			const int gridOffset = READ_LE_UINT16(tempGridPtr);
416 			tempGridPtr += 2;
417 			createCellingGridColumn(gridPtr + gridOffset, gridPtrSize - gridOffset, _blockBuffer + blockOffset, _blockBufferSize - blockOffset);
418 			blockOffset += 2 * GRID_SIZE_Y;
419 		}
420 		currGridOffset += GRID_SIZE_X + GRID_SIZE_Z;
421 	}
422 }
423 
initGrid(int32 index)424 bool Grid::initGrid(int32 index) {
425 	// load grids from file
426 	_currentGridSize = HQR::getAllocEntry(&_currentGrid, Resources::HQR_LBA_GRI_FILE, index);
427 	if (_currentGridSize == 0) {
428 		warning("Failed to load grid index: %i", index);
429 		return false;
430 	}
431 
432 	// load layouts from file
433 	if (!_currentBlockLibrary.loadFromHQR(Resources::HQR_LBA_BLL_FILE, index, _engine->isLBA1())) {
434 		warning("Failed to load block library index: %i", index);
435 		return false;
436 	}
437 
438 	loadGridBricks();
439 
440 	createGridMask();
441 
442 	createGridMap();
443 
444 	return true;
445 }
446 
initCellingGrid(int32 index)447 bool Grid::initCellingGrid(int32 index) {
448 	uint8 *gridPtr = nullptr;
449 
450 	// load grids from file
451 	const int realIndex = index + CELLING_GRIDS_START_INDEX;
452 	const int32 gridSize = HQR::getAllocEntry(&gridPtr, Resources::HQR_LBA_GRI_FILE, realIndex);
453 	if (gridSize == 0) {
454 		warning("Failed to load grid index %i", realIndex);
455 		return false;
456 	}
457 
458 	createCellingGridMap(gridPtr, gridSize);
459 	free(gridPtr);
460 	_engine->_redraw->_reqBgRedraw = true;
461 	return true;
462 }
463 
drawBrick(int32 index,int32 posX,int32 posY)464 bool Grid::drawBrick(int32 index, int32 posX, int32 posY) {
465 	return drawBrickSprite(index, posX, posY, _brickTable[index], false);
466 }
467 
drawSprite(int32 index,int32 posX,int32 posY,const uint8 * ptr)468 bool Grid::drawSprite(int32 index, int32 posX, int32 posY, const uint8 *ptr) {
469 	ptr = ptr + READ_LE_INT32(ptr + index * 4);
470 	return drawBrickSprite(index, posX, posY, ptr, true);
471 }
472 
drawSprite(int32 posX,int32 posY,const SpriteData & ptr,int spriteIndex)473 bool Grid::drawSprite(int32 posX, int32 posY, const SpriteData &ptr, int spriteIndex) {
474 	const int32 left = posX + ptr.offsetX(spriteIndex);
475 	if (left > _engine->_interface->_clip.right) {
476 		return false;
477 	}
478 	const int32 right = ptr.surface(spriteIndex).w + left;
479 	if (right < _engine->_interface->_clip.left) {
480 		return false;
481 	}
482 	const int32 top = posY + ptr.offsetY(spriteIndex);
483 	if (top > _engine->_interface->_clip.bottom) {
484 		return false;
485 	}
486 	const int32 bottom = ptr.surface(spriteIndex).h + top;
487 	if (bottom < _engine->_interface->_clip.top) {
488 		return false;
489 	}
490 
491 	const Common::Point pos(left, top);
492 	_engine->_frontVideoBuffer.transBlitFrom(ptr.surface(spriteIndex), pos);
493 	return true;
494 }
495 
496 // WARNING: Rewrite this function to have better performance
drawBrickSprite(int32 index,int32 posX,int32 posY,const uint8 * ptr,bool isSprite)497 bool Grid::drawBrickSprite(int32 index, int32 posX, int32 posY, const uint8 *ptr, bool isSprite) {
498 	if (!_engine->_interface->_clip.isValidRect()) {
499 		return false;
500 	}
501 
502 	const int32 left = posX + *(ptr + 2);
503 	if (left > _engine->_interface->_clip.right) {
504 		return false;
505 	}
506 	const int32 right = *ptr + left;
507 	if (right < _engine->_interface->_clip.left) {
508 		return false;
509 	}
510 	const int32 top = posY + *(ptr + 3);
511 	if (top > _engine->_interface->_clip.bottom) {
512 		return false;
513 	}
514 	const int32 bottom = (int32)*(ptr + 1) + top;
515 	if (bottom < _engine->_interface->_clip.top) {
516 		return false;
517 	}
518 	const int32 maxY = MIN(bottom, (int32)_engine->_interface->_clip.bottom);
519 
520 	ptr += 4;
521 
522 	int32 x = left;
523 
524 	//if (left >= textWindowLeft-2 && top >= textWindowTop-2 && right <= textWindowRight-2 && bottom <= textWindowBottom-2) // crop
525 	{
526 		for (int32 y = top; y < maxY; ++y) {
527 			const uint8 rleAmount = *ptr++;
528 			for (int32 run = 0; run < rleAmount; ++run) {
529 				const uint8 rleMask = *ptr++;
530 				const uint8 iterations = bits(rleMask, 0, 6) + 1;
531 				const uint8 type = bits(rleMask, 6, 2);
532 				if (type == 0) {
533 					x += iterations;
534 					continue;
535 				}
536 				if (y < _engine->_interface->_clip.top || x >= _engine->_interface->_clip.right || x + iterations < _engine->_interface->_clip.left) {
537 					if (type == 1) {
538 						ptr += iterations;
539 					} else {
540 						++ptr;
541 					}
542 					x += iterations;
543 					continue;
544 				}
545 				if (type == 1) {
546 					uint8 *out = (uint8 *)_engine->_frontVideoBuffer.getBasePtr(x, y);
547 					for (uint8 i = 0; i < iterations; i++) {
548 						if (x >= _engine->_interface->_clip.left && x < _engine->_interface->_clip.right) {
549 							*out = *ptr;
550 						}
551 
552 						++out;
553 						++x;
554 						++ptr;
555 					}
556 				} else {
557 					const uint8 pixel = *ptr++;
558 					uint8 *out = (uint8 *)_engine->_frontVideoBuffer.getBasePtr(x, y);
559 					for (uint8 i = 0; i < iterations; i++) {
560 						if (x >= _engine->_interface->_clip.left && x < _engine->_interface->_clip.right) {
561 							*out = pixel;
562 						}
563 
564 						++out;
565 						++x;
566 					}
567 				}
568 			}
569 			x = left;
570 		}
571 	}
572 
573 	Common::Rect rect(left, top, right, bottom);
574 	_engine->_frontVideoBuffer.addDirtyRect(rect);
575 
576 	return true;
577 }
578 
getBlockBufferGround(const IVec3 & pos,int32 & ground)579 const uint8 *Grid::getBlockBufferGround(const IVec3 &pos, int32 &ground) {
580 	const IVec3 &collision = updateCollisionCoordinates(pos.x, pos.y, pos.z);
581 	const uint8 *ptr = _blockBuffer
582 					   + collision.y * sizeof(int16)
583 					   + collision.x * GRID_SIZE_Y * sizeof(int16)
584 					   + collision.z * GRID_SIZE_X * GRID_SIZE_Y * sizeof(int16);
585 
586 	int32 collisionY = collision.y;
587 	while (collisionY) {
588 		if (READ_LE_INT16(ptr)) { // found the ground
589 			break;
590 		}
591 		collisionY--;
592 		ptr -= sizeof(int16);
593 	}
594 
595 	_engine->_collision->_collision.y = collisionY;
596 	ground = (int16)((collisionY + 1) * BRICK_HEIGHT);
597 
598 	return ptr;
599 }
600 
getBlockPointer(int32 blockIdx,int32 brickIdx) const601 const BlockDataEntry* Grid::getBlockPointer(int32 blockIdx, int32 brickIdx) const {
602 	const BlockData *blockPtr = getBlockLibrary(blockIdx);
603 	return &blockPtr->entries[brickIdx];
604 }
605 
getBlockLibrary(int32 blockIdx) const606 const BlockData *Grid::getBlockLibrary(int32 blockIdx) const {
607 	return _currentBlockLibrary.getLayout(blockIdx - 1);
608 }
609 
getBrickPos(int32 x,int32 y,int32 z)610 void Grid::getBrickPos(int32 x, int32 y, int32 z) {
611 	_brickPixelPosX = (x - z) * 24 + _engine->width() / 2 - GRID_SIZE_X / 2;
612 	_brickPixelPosY = ((x + z) * 12) - (y * 15) + _engine->height() / 2 - GRID_SIZE_Y;
613 }
614 
drawColumnGrid(int32 blockIdx,int32 brickBlockIdx,int32 x,int32 y,int32 z)615 void Grid::drawColumnGrid(int32 blockIdx, int32 brickBlockIdx, int32 x, int32 y, int32 z) {
616 	const BlockDataEntry *blockPtr = getBlockPointer(blockIdx, brickBlockIdx);
617 	const uint8 brickShape = blockPtr->brickShape;
618 	const uint8 brickSound = blockPtr->brickType;
619 	const uint16 brickIdx = blockPtr->brickIdx;
620 	if (!brickIdx) {
621 		return;
622 	}
623 
624 	getBrickPos(x - _newCamera.x, y - _newCamera.y, z - _newCamera.z);
625 
626 	if (_brickPixelPosX < -24) {
627 		return;
628 	}
629 	if (_brickPixelPosX >= _engine->width()) {
630 		return;
631 	}
632 	if (_brickPixelPosY < -38) {
633 		return;
634 	}
635 	if (_brickPixelPosY >= _engine->height()) {
636 		return;
637 	}
638 
639 	// draw the background brick
640 	drawBrick(brickIdx - 1, _brickPixelPosX, _brickPixelPosY);
641 
642 	int32 brickBuffIdx = (_brickPixelPosX + 24) / 24;
643 
644 	if (_brickInfoBuffer[brickBuffIdx] >= MAXBRICKS) {
645 		warning("GRID: brick buffer exceeded");
646 		return;
647 	}
648 
649 	BrickEntry *currBrickEntry = &_bricksDataBuffer[brickBuffIdx * MAXBRICKS + _brickInfoBuffer[brickBuffIdx]];
650 
651 	currBrickEntry->x = x;
652 	currBrickEntry->y = y;
653 	currBrickEntry->z = z;
654 	currBrickEntry->posX = _brickPixelPosX;
655 	currBrickEntry->posY = _brickPixelPosY;
656 	currBrickEntry->index = brickIdx - 1;
657 	currBrickEntry->shape = brickShape;
658 	currBrickEntry->sound = brickSound;
659 
660 	_brickInfoBuffer[brickBuffIdx]++;
661 }
662 
redrawGrid()663 void Grid::redrawGrid() {
664 	_camera.x = _newCamera.x * BRICK_SIZE;
665 	_camera.y = _newCamera.y * BRICK_HEIGHT;
666 	_camera.z = _newCamera.z * BRICK_SIZE;
667 
668 	memset(_brickInfoBuffer, 0, _brickInfoBufferSize);
669 
670 	if (!_engine->_scene->_enableGridTileRendering) {
671 		return;
672 	}
673 
674 	for (int32 z = 0; z < GRID_SIZE_Z; z++) {
675 		for (int32 x = 0; x < GRID_SIZE_X; x++) {
676 			for (int32 y = 0; y < GRID_SIZE_Y; y++) {
677 				const BlockEntry entry = getBlockEntry(x, y, z);
678 				if (entry.blockIdx) {
679 					drawColumnGrid(entry.blockIdx, entry.brickBlockIdx, x, y, z);
680 				}
681 			}
682 		}
683 	}
684 }
685 
getBlockEntry(int32 x,int32 y,int32 z) const686 BlockEntry Grid::getBlockEntry(int32 x, int32 y, int32 z) const {
687 	const uint8 *blockBufferPtr = _blockBuffer;
688 	blockBufferPtr += x * GRID_SIZE_Y * 2;
689 	blockBufferPtr += y * 2;
690 	blockBufferPtr += (z * GRID_SIZE_X * 2) * GRID_SIZE_Y;
691 
692 	BlockEntry entry;
693 	entry.blockIdx = *blockBufferPtr;
694 	entry.brickBlockIdx = *(blockBufferPtr + 1);
695 	return entry;
696 }
697 
getBrickShape(int32 x,int32 y,int32 z)698 ShapeType Grid::getBrickShape(int32 x, int32 y, int32 z) {
699 	const IVec3 &collision = updateCollisionCoordinates(x, y, z);
700 
701 	if (collision.x < 0 || collision.x >= GRID_SIZE_X) {
702 		return ShapeType::kNone;
703 	}
704 
705 	if (collision.y <= -1) {
706 		return ShapeType::kSolid;
707 	}
708 
709 	if (collision.y < 0 || collision.y >= GRID_SIZE_Y || collision.z < 0 || collision.z >= GRID_SIZE_Z) {
710 		return ShapeType::kNone;
711 	}
712 
713 	const BlockEntry entry = getBlockEntry(collision.x, collision.y, collision.z);
714 	if (entry.blockIdx) {
715 		const BlockDataEntry *blockPtr = getBlockPointer(entry.blockIdx, entry.brickBlockIdx);
716 		return (ShapeType)blockPtr->brickShape;
717 	}
718 	return (ShapeType)entry.brickBlockIdx;
719 }
720 
updateCollisionCoordinates(int32 x,int32 y,int32 z)721 const IVec3 &Grid::updateCollisionCoordinates(int32 x, int32 y, int32 z) {
722 	_engine->_collision->_collision.x = (x + BRICK_HEIGHT) / BRICK_SIZE;
723 	_engine->_collision->_collision.y = y / BRICK_HEIGHT;
724 	_engine->_collision->_collision.z = (z + BRICK_HEIGHT) / BRICK_SIZE;
725 	return _engine->_collision->_collision;
726 }
727 
getBrickShapeFull(int32 x,int32 y,int32 z,int32 y2)728 ShapeType Grid::getBrickShapeFull(int32 x, int32 y, int32 z, int32 y2) {
729 	const IVec3 &collision = updateCollisionCoordinates(x, y, z);
730 
731 	if (collision.x < 0 || collision.x >= GRID_SIZE_X) {
732 		return ShapeType::kNone;
733 	}
734 
735 	if (collision.y <= -1) {
736 		return ShapeType::kSolid;
737 	}
738 
739 	if (collision.y < 0 || collision.y >= GRID_SIZE_Y || collision.z < 0 || collision.z >= GRID_SIZE_Z) {
740 		return ShapeType::kNone;
741 	}
742 
743 	uint8 *blockBufferPtr = _blockBuffer;
744 	blockBufferPtr += collision.x * GRID_SIZE_Y * 2;
745 	blockBufferPtr += collision.y * 2;
746 	blockBufferPtr += (collision.z * GRID_SIZE_X * 2) * GRID_SIZE_Y;
747 
748 	uint8 blockIdx = *blockBufferPtr;
749 
750 	if (blockIdx) {
751 		const uint8 tmpBrickIdx = *(blockBufferPtr + 1);
752 		const BlockDataEntry *blockPtr = getBlockPointer(blockIdx, tmpBrickIdx);
753 		const ShapeType brickShape = (ShapeType)blockPtr->brickShape;
754 
755 		const int32 newY = (y2 + (BRICK_HEIGHT - 1)) / BRICK_HEIGHT;
756 		int32 currY = collision.y;
757 
758 		for (int32 i = 0; i < newY; i++) {
759 			if (currY >= GRID_SIZE_Y) {
760 				return brickShape;
761 			}
762 
763 			blockBufferPtr += 2;
764 			currY++;
765 
766 			if (READ_LE_INT16(blockBufferPtr) != 0) {
767 				return ShapeType::kSolid;
768 			}
769 		}
770 
771 		return brickShape;
772 	}
773 	const ShapeType brickShape = (ShapeType) * (blockBufferPtr + 1);
774 
775 	const int32 newY = (y2 + (BRICK_HEIGHT - 1)) / BRICK_HEIGHT;
776 	int32 currY = collision.y;
777 
778 	for (int32 i = 0; i < newY; i++) {
779 		if (currY >= GRID_SIZE_Y) {
780 			return brickShape;
781 		}
782 
783 		blockBufferPtr += 2;
784 		currY++;
785 
786 		if (READ_LE_INT16(blockBufferPtr) != 0) {
787 			return ShapeType::kSolid;
788 		}
789 	}
790 
791 	return ShapeType::kNone;
792 }
793 
getBrickSoundType(int32 x,int32 y,int32 z)794 uint8 Grid::getBrickSoundType(int32 x, int32 y, int32 z) {
795 	const IVec3 &collision = updateCollisionCoordinates(x, y, z);
796 
797 	if (collision.x < 0 || collision.x >= GRID_SIZE_X) {
798 		return 0; // none
799 	}
800 
801 	if (collision.y <= -1) {
802 		return 1; // solid
803 	}
804 
805 	if (collision.y < 0 || collision.y >= GRID_SIZE_Y || collision.z < 0 || collision.z >= GRID_SIZE_Z) {
806 		return 0; // none
807 	}
808 
809 	const BlockEntry entry = getBlockEntry(collision.x, collision.y, collision.z);
810 	if (entry.blockIdx) {
811 		const BlockDataEntry *blockPtr = getBlockPointer(entry.blockIdx, entry.brickBlockIdx);
812 		return blockPtr->brickType;
813 	}
814 
815 	return 0xF0U;
816 }
817 
centerOnActor(const ActorStruct * actor)818 void Grid::centerOnActor(const ActorStruct* actor) {
819 	_newCamera.x = (actor->_pos.x + BRICK_HEIGHT) / BRICK_SIZE;
820 	_newCamera.y = (actor->_pos.y + BRICK_HEIGHT) / BRICK_HEIGHT;
821 	_newCamera.z = (actor->_pos.z + BRICK_HEIGHT) / BRICK_SIZE;
822 	_engine->_redraw->_reqBgRedraw = true;
823 }
824 
centerScreenOnActor()825 void Grid::centerScreenOnActor() {
826 	if (_engine->_disableScreenRecenter) {
827 		return;
828 	}
829 	if (_engine->_debugGrid->_useFreeCamera) {
830 		return;
831 	}
832 
833 	ActorStruct *actor = _engine->_scene->getActor(_engine->_scene->_currentlyFollowedActor);
834 	_engine->_renderer->projectPositionOnScreen(actor->_pos.x - (_newCamera.x * BRICK_SIZE),
835 	                                   actor->_pos.y - (_newCamera.y * BRICK_HEIGHT),
836 	                                   actor->_pos.z - (_newCamera.z * BRICK_SIZE));
837 	// TODO: these border values should get scaled for higher resolutions
838 	if (_engine->_renderer->_projPos.x < 80 || _engine->_renderer->_projPos.x >= _engine->width() - 60 || _engine->_renderer->_projPos.y < 80 || _engine->_renderer->_projPos.y >= _engine->height() - 50) {
839 		_newCamera.x = ((actor->_pos.x + BRICK_HEIGHT) / BRICK_SIZE) + (((actor->_pos.x + BRICK_HEIGHT) / BRICK_SIZE) - _newCamera.x) / 2;
840 		_newCamera.y = actor->_pos.y / BRICK_HEIGHT;
841 		_newCamera.z = ((actor->_pos.z + BRICK_HEIGHT) / BRICK_SIZE) + (((actor->_pos.z + BRICK_HEIGHT) / BRICK_SIZE) - _newCamera.z) / 2;
842 
843 		if (_newCamera.x >= GRID_SIZE_X) {
844 			_newCamera.x = GRID_SIZE_X - 1;
845 		}
846 
847 		if (_newCamera.z >= GRID_SIZE_Z) {
848 			_newCamera.z = GRID_SIZE_Z - 1;
849 		}
850 
851 		_engine->_redraw->_reqBgRedraw = true;
852 	}
853 }
854 
855 } // namespace TwinE
856