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 // Isometric level module
24 
25 #include "saga/saga.h"
26 #include "saga/gfx.h"
27 #include "saga/scene.h"
28 #include "saga/isomap.h"
29 #include "saga/render.h"
30 
31 namespace Saga {
32 
33 enum MaskRules {
34 	kMaskRuleNever = 0,
35 	kMaskRuleAlways,
36 	kMaskRuleUMIN,
37 	kMaskRuleUMID,
38 	kMaskRuleUMAX,
39 	kMaskRuleVMIN,
40 	kMaskRuleVMID,
41 	kMaskRuleVMAX,
42 	kMaskRuleYMIN,
43 	kMaskRuleYMID,
44 	kMaskRuleYMAX,
45 	kMaskRuleUVMAX,
46 	kMaskRuleUVMIN,
47 	kMaskRuleUorV,
48 	kMaskRuleUandV
49 };
50 
51 
52 static const IsoMap::TilePoint normalDirTable[8] = {
53 	{ 1, 1, 0, SAGA_DIAG_NORMAL_COST},
54 	{ 1, 0, 0, SAGA_STRAIGHT_NORMAL_COST},
55 	{ 1,-1, 0, SAGA_DIAG_NORMAL_COST},
56 	{ 0,-1, 0, SAGA_STRAIGHT_NORMAL_COST},
57 	{-1,-1, 0, SAGA_DIAG_NORMAL_COST},
58 	{-1, 0, 0, SAGA_STRAIGHT_NORMAL_COST},
59 	{-1, 1, 0, SAGA_DIAG_NORMAL_COST},
60 	{ 0, 1, 0, SAGA_STRAIGHT_NORMAL_COST},
61 };
62 
63 static const IsoMap::TilePoint easyDirTable[8] = {
64 	{ 1, 1, 0, SAGA_DIAG_EASY_COST},
65 	{ 1, 0, 0, SAGA_STRAIGHT_EASY_COST},
66 	{ 1,-1, 0, SAGA_DIAG_EASY_COST},
67 	{ 0,-1, 0, SAGA_STRAIGHT_EASY_COST},
68 	{-1,-1, 0, SAGA_DIAG_EASY_COST},
69 	{-1, 0, 0, SAGA_STRAIGHT_EASY_COST},
70 	{-1, 1, 0, SAGA_DIAG_EASY_COST},
71 	{ 0, 1, 0, SAGA_STRAIGHT_EASY_COST},
72 };
73 
74 static const IsoMap::TilePoint hardDirTable[8] = {
75 	{ 1, 1, 0, SAGA_DIAG_HARD_COST},
76 	{ 1, 0, 0, SAGA_STRAIGHT_HARD_COST},
77 	{ 1,-1, 0, SAGA_DIAG_HARD_COST},
78 	{ 0,-1, 0, SAGA_STRAIGHT_HARD_COST},
79 	{-1,-1, 0, SAGA_DIAG_HARD_COST},
80 	{-1, 0, 0, SAGA_STRAIGHT_HARD_COST},
81 	{-1, 1, 0, SAGA_DIAG_HARD_COST},
82 	{ 0, 1, 0, SAGA_STRAIGHT_HARD_COST},
83 };
84 
85 static const int16 directions[8][2] = {
86 	{	16,		16},
87 	{	16,		0},
88 	{	16,		-16},
89 	{	0,		-16},
90 	{	-16,	-16},
91 	{	-16,	0},
92 	{	-16,	16},
93 	{	0,		16}
94 };
95 
IsoMap(SagaEngine * vm)96 IsoMap::IsoMap(SagaEngine *vm) : _vm(vm) {
97 	_viewScroll.x = (128 - 8) * 16;
98 	_viewScroll.y = (128 - 8) * 16 - 64;
99 	_viewDiff = 1;
100 	_platformHeight = 0;
101 	_queueCount = _readCount = 0;
102 
103 	for (int i = 0; i < SAGA_DRAGON_SEARCH_DIAMETER; i++)
104 		for (int j = 0; j < SAGA_DRAGON_SEARCH_DIAMETER; j++)
105 			_dragonSearchArray.cell[i][j].visited = _dragonSearchArray.cell[i][j].direction = 0;
106 
107 	for (int i = 0; i < SAGA_SEARCH_DIAMETER; i++)
108 		for (int j = 0; j < SAGA_SEARCH_DIAMETER; j++)
109 			_searchArray.cell[i][j].visited = _searchArray.cell[i][j].direction = 0;
110 
111 	for (int i = 0; i < SAGA_SEARCH_QUEUE_SIZE; i++) {
112 		memset(&_dragonSearchArray.queue[i], 0, sizeof(DragonTilePoint));
113 		memset(&_searchArray.queue[i], 0, sizeof(TilePoint));
114 	}
115 
116 	memset(&_tileMap, 0, sizeof(TileMapData));
117 }
118 
loadImages(const ByteArray & resourceData)119 void IsoMap::loadImages(const ByteArray &resourceData) {
120 	IsoTileData *tileData;
121 	uint16 i;
122 	size_t offsetDiff;
123 
124 	if (resourceData.empty()) {
125 		error("IsoMap::loadImages wrong resourceLength");
126 	}
127 
128 
129 	ByteArrayReadStreamEndian readS(resourceData, _vm->isBigEndian());
130 	readS.readUint16(); // skip
131 	i = readS.readUint16();
132 	i = i / SAGA_ISOTILEDATA_LEN;
133 	_tilesTable.resize(i);
134 	Common::Array<size_t> tempOffsets;
135 	tempOffsets.resize(_tilesTable.size());
136 	readS.seek(0);
137 
138 
139 	for (i = 0; i < _tilesTable.size(); i++) {
140 		tileData = &_tilesTable[i];
141 		tileData->height = readS.readByte();
142 		tileData->attributes = readS.readSByte();
143 		tempOffsets[i] = readS.readUint16();
144 		tileData->terrainMask = readS.readUint16();
145 		tileData->FGDBGDAttr = readS.readByte();
146 		readS.readByte(); //skip
147 	}
148 
149 	offsetDiff = readS.pos();
150 
151 	_tileData.resize(resourceData.size() - offsetDiff);
152 	memcpy(_tileData.getBuffer(), resourceData.getBuffer() + offsetDiff, _tileData.size());
153 
154 	for (i = 0; i < _tilesTable.size(); i++) {
155 		_tilesTable[i].tilePointer = _tileData.getBuffer() + tempOffsets[i] - offsetDiff;
156 	}
157 }
158 
loadPlatforms(const ByteArray & resourceData)159 void IsoMap::loadPlatforms(const ByteArray &resourceData) {
160 	TilePlatformData *tilePlatformData;
161 	uint16 i, x, y;
162 
163 	if (resourceData.empty()) {
164 		error("IsoMap::loadPlatforms wrong resourceLength");
165 	}
166 
167 	ByteArrayReadStreamEndian readS(resourceData, _vm->isBigEndian());
168 
169 	i = resourceData.size() / SAGA_TILEPLATFORMDATA_LEN;
170 	_tilePlatformList.resize(i);
171 
172 	for (i = 0; i < _tilePlatformList.size(); i++) {
173 		tilePlatformData = &_tilePlatformList[i];
174 		tilePlatformData->metaTile = readS.readSint16();
175 		tilePlatformData->height = readS.readSint16();
176 		tilePlatformData->highestPixel = readS.readSint16();
177 		tilePlatformData->vBits = readS.readByte();
178 		tilePlatformData->uBits = readS.readByte();
179 		for (x = 0; x < SAGA_PLATFORM_W; x++) {
180 			for (y = 0; y < SAGA_PLATFORM_W; y++) {
181 				tilePlatformData->tiles[x][y] = readS.readSint16();
182 			}
183 		}
184 	}
185 
186 }
187 
loadMap(const ByteArray & resourceData)188 void IsoMap::loadMap(const ByteArray &resourceData) {
189 	uint16 x, y;
190 
191 	if (resourceData.size() != SAGA_TILEMAP_LEN) {
192 		error("IsoMap::loadMap wrong resource length %d", resourceData.size());
193 	}
194 
195 	ByteArrayReadStreamEndian readS(resourceData, _vm->isBigEndian());
196 	_tileMap.edgeType = readS.readByte();
197 	readS.readByte(); //skip
198 
199 	for (x = 0; x < SAGA_TILEMAP_W; x++) {
200 		for (y = 0; y < SAGA_TILEMAP_H; y++) {
201 			_tileMap.tilePlatforms[x][y] = readS.readSint16();
202 		}
203 	}
204 
205 }
206 
loadMetaTiles(const ByteArray & resourceData)207 void IsoMap::loadMetaTiles(const ByteArray &resourceData) {
208 	MetaTileData *metaTileData;
209 	uint16 i, j;
210 
211 	if (resourceData.empty()) {
212 		error("IsoMap::loadMetaTiles wrong resourceLength");
213 	}
214 
215 	ByteArrayReadStreamEndian readS(resourceData, _vm->isBigEndian());
216 	i = resourceData.size() / SAGA_METATILEDATA_LEN;
217 	_metaTileList.resize(i);
218 
219 	for (i = 0; i < _metaTileList.size(); i++) {
220 		metaTileData = &_metaTileList[i];
221 		metaTileData->highestPlatform = readS.readUint16();
222 		metaTileData->highestPixel = readS.readUint16();
223 		for (j = 0; j < SAGA_MAX_PLATFORM_H; j++) {
224 			metaTileData->stack[j] = readS.readSint16();
225 		}
226 	}
227 }
228 
loadMulti(const ByteArray & resourceData)229 void IsoMap::loadMulti(const ByteArray &resourceData) {
230 	MultiTileEntryData *multiTileEntryData;
231 	uint16 i;
232 	int16 offsetDiff;
233 
234 	if (resourceData.size() < 2) {
235 		error("IsoMap::loadMetaTiles wrong resourceLength");
236 	}
237 
238 	ByteArrayReadStreamEndian readS(resourceData, _vm->isBigEndian());
239 	i = readS.readUint16();
240 	_multiTable.resize(i);
241 
242 	for (i = 0; i < _multiTable.size(); i++) {
243 		multiTileEntryData = &_multiTable[i];
244 		readS.readUint32();//skip
245 		multiTileEntryData->offset = readS.readSint16();
246 		multiTileEntryData->u = readS.readByte();
247 		multiTileEntryData->v = readS.readByte();
248 		multiTileEntryData->h = readS.readByte();
249 		multiTileEntryData->uSize = readS.readByte();
250 		multiTileEntryData->vSize = readS.readByte();
251 		multiTileEntryData->numStates = readS.readByte();
252 		multiTileEntryData->currentState = readS.readByte();
253 		readS.readByte();//skip
254 	}
255 
256 	offsetDiff = (readS.pos() - 2);
257 
258 	for (i = 0; i < _multiTable.size(); i++) {
259 		_multiTable[i].offset -= offsetDiff;
260 	}
261 
262 	uint16 multiDataCount = (readS.size() - readS.pos()) / 2;
263 
264 	_multiTableData.resize(multiDataCount);
265 	for (i = 0; i < _multiTableData.size(); i++) {
266 		_multiTableData[i] = readS.readSint16();
267 	}
268 }
269 
clear()270 void IsoMap::clear() {
271 	_tilesTable.clear();
272 	_tilePlatformList.clear();
273 	_metaTileList.clear();
274 	_multiTable.clear();
275 	_tileData.clear();
276 	_multiTableData.clear();
277 }
278 
adjustScroll(bool jump)279 void IsoMap::adjustScroll(bool jump) {
280 	Point playerPoint;
281 	Point minScrollPos;
282 	Point maxScrollPos;
283 
284 
285 	tileCoordsToScreenPoint(_vm->_actor->_centerActor->_location, playerPoint);
286 
287 	if (_vm->_scene->currentSceneResourceId() == ITE_SCENE_OVERMAP) {
288 		_mapPosition.x = (playerPoint.x + _viewScroll.x) * 30 / 100 - (381);
289 		_mapPosition.y = (playerPoint.y + _viewScroll.y) * 30 / 100 - (342);
290 	}
291 
292 	if (_vm->_actor->_centerActor != _vm->_actor->_protagonist) {
293 		playerPoint.y -= 24;
294 	}
295 	playerPoint.y -= 28;
296 
297 	playerPoint.x += _viewScroll.x - _vm->getDisplayInfo().width/2;
298 	playerPoint.y += _viewScroll.y - _vm->_scene->getHeight()/2;
299 
300 	minScrollPos.x = playerPoint.x - SAGA_SCROLL_LIMIT_X1;
301 	minScrollPos.y = playerPoint.y - SAGA_SCROLL_LIMIT_Y1;
302 
303 	maxScrollPos.x = playerPoint.x + SAGA_SCROLL_LIMIT_X1;
304 	maxScrollPos.y = playerPoint.y + SAGA_SCROLL_LIMIT_Y2;
305 
306 	if (jump) {
307 		_viewScroll.x = CLIP(_viewScroll.x, minScrollPos.x, maxScrollPos.x);
308 		_viewScroll.y = CLIP(_viewScroll.y, minScrollPos.y, maxScrollPos.y);
309 	} else {
310 		_viewScroll.y = smoothSlide(_viewScroll.y, minScrollPos.y, maxScrollPos.y);
311 		_viewScroll.x = smoothSlide(_viewScroll.x, minScrollPos.x, maxScrollPos.x);
312 	}
313 
314 	if (_vm->_scene->currentSceneResourceId() == ITE_SCENE_OVERMAP) {
315 		ObjectData *obj;
316 		uint16 objectId;
317 		objectId = _vm->_actor->objIndexToId(ITE_OBJ_MAP);
318 		obj = _vm->_actor->getObj(objectId);
319 		if (obj->_sceneNumber != ITE_SCENE_INV) {
320 			_viewScroll.x = 1552 + 8;
321 			_viewScroll.y = 1456 + 8;
322 		}
323 	}
324 }
325 
findMulti(int16 tileIndex,int16 absU,int16 absV,int16 absH)326 int16 IsoMap::findMulti(int16 tileIndex, int16 absU, int16 absV, int16 absH) {
327 	MultiTileEntryData *multiTileEntryData;
328 	int16 ru;
329 	int16 rv;
330 	int16 mu;
331 	int16 mv;
332 	int16 state;
333 	uint16 i, offset;
334 	int16 *tiles;
335 
336 	ru = (tileIndex >> 13) & 0x03;
337 	rv = (tileIndex >> 11) & 0x03;
338 	mu = absU - ru;
339 	mv = absV - rv;
340 
341 	tileIndex = 0;
342 	for (i = 0; i < _multiTable.size(); i++) {
343 		multiTileEntryData = &_multiTable[i];
344 
345 		if ((multiTileEntryData->u == mu) &&
346 			(multiTileEntryData->v == mv) &&
347 			(multiTileEntryData->h == absH)) {
348 			state = multiTileEntryData->currentState;
349 
350 			offset = (ru + state * multiTileEntryData->uSize) * multiTileEntryData->vSize + rv;
351 			offset *= sizeof(int16);
352 			offset += multiTileEntryData->offset;
353 			if (offset + sizeof(int16) > _multiTableData.size() * sizeof(int16)) {
354 				error("wrong multiTileEntryData->offset");
355 			}
356 			tiles = (int16 *)((byte *)&_multiTableData.front() + offset);
357 			tileIndex = *tiles;
358 			if (tileIndex >= 256) {
359 				warning("something terrible happened");
360 				return 1;
361 			}
362 			return tileIndex;
363 		}
364 	}
365 
366 	return 1;
367 }
368 
draw()369 void IsoMap::draw() {
370 	_tileClip = _vm->_scene->getSceneClip();
371 	_vm->_gfx->drawRect(_tileClip, 0);
372 	drawTiles(NULL);
373 }
374 
setMapPosition(int x,int y)375 void IsoMap::setMapPosition(int x, int y) {
376 	_mapPosition.x = x;
377 	_mapPosition.y = y;
378 }
379 
drawSprite(SpriteList & spriteList,int spriteNumber,const Location & location,const Point & screenPosition,int scale)380 void IsoMap::drawSprite(SpriteList &spriteList, int spriteNumber, const Location &location, const Point &screenPosition, int scale) {
381 	int width;
382 	int height;
383 	int xAlign;
384 	int yAlign;
385 	const byte *spriteBuffer;
386 	Point spritePointer;
387 
388 	_vm->_sprite->getScaledSpriteBuffer(spriteList, spriteNumber, scale, width, height, xAlign, yAlign, spriteBuffer);
389 
390 	spritePointer.x = screenPosition.x + xAlign;
391 	spritePointer.y = screenPosition.y + yAlign;
392 
393 	_tileClip.left = CLIP<int>(spritePointer.x, 0, _vm->getDisplayInfo().width);
394 	_tileClip.right = CLIP<int>(spritePointer.x + width, 0, _vm->getDisplayInfo().width);
395 	_tileClip.top = CLIP<int>(spritePointer.y, 0, _vm->_scene->getHeight());
396 	_tileClip.bottom = CLIP<int>(spritePointer.y + height, 0, _vm->_scene->getHeight());
397 
398 	_vm->_sprite->drawClip(spritePointer, width, height, spriteBuffer, true);
399 	drawTiles(&location);
400 }
401 
402 
drawTiles(const Location * location)403 void IsoMap::drawTiles(const Location *location) {
404 	Point view1;
405 	Point fineScroll;
406 	Point tileScroll;
407 	Point metaTileY;
408 	Point metaTileX;
409 	int16 u0, v0,
410 		  u1, v1,
411 		  u2, v2,
412 		  uc, vc;
413 	uint16 metaTileIndex;
414 	Location rLocation;
415 	int16 workAreaWidth;
416 	int16 workAreaHeight;
417 
418 	tileScroll.x = _viewScroll.x >> 4;
419 	tileScroll.y = _viewScroll.y >> 4;
420 
421 	fineScroll.x = _viewScroll.x & 0xf;
422 	fineScroll.y = _viewScroll.y & 0xf;
423 
424 	view1.x = tileScroll.x - (8 * SAGA_TILEMAP_W);
425 	view1.y = (8 * SAGA_TILEMAP_W) - tileScroll.y;
426 
427 	u0 = ((view1.y + 64) * 2 + view1.x) >> 4;
428 	v0 = ((view1.y + 64) * 2 - view1.x) >> 4;
429 
430 	metaTileY.x = (u0 - v0) * 128 - (view1.x * 16 + fineScroll.x);
431 	metaTileY.y = (view1.y * 16 - fineScroll.y) - (u0 + v0) * 64;
432 
433 	workAreaWidth = _vm->getDisplayInfo().width + 128;
434 	workAreaHeight = _vm->_scene->getHeight() + 128 + 80;
435 
436 	for (u1 = u0, v1 = v0; metaTileY.y < workAreaHeight; u1--, v1--) {
437 		metaTileX = metaTileY;
438 
439 		for (u2 = u1, v2 = v1; metaTileX.x < workAreaWidth; u2++, v2--, metaTileX.x += 256) {
440 
441 			uc = u2 & (SAGA_TILEMAP_W - 1);
442 			vc = v2 & (SAGA_TILEMAP_W - 1);
443 
444 			if (uc != u2 || vc != v2) {
445 				metaTileIndex = 0;
446 				switch (_tileMap.edgeType) {
447 				case kEdgeTypeBlack:
448 					continue;
449 				case kEdgeTypeFill0:
450 					break;
451 				case kEdgeTypeFill1:
452 					metaTileIndex = 1;
453 					break;
454 				case kEdgeTypeRpt:
455 					uc = CLIP<int16>(u2, 0, SAGA_TILEMAP_W - 1);
456 					vc = CLIP<int16>(v2, 0, SAGA_TILEMAP_W - 1);
457 					metaTileIndex = _tileMap.tilePlatforms[uc][vc];
458 					break;
459 				case kEdgeTypeWrap:
460 					metaTileIndex = _tileMap.tilePlatforms[uc][vc];
461 					break;
462 				}
463 			} else {
464 				metaTileIndex = _tileMap.tilePlatforms[uc][vc];
465 			}
466 
467 			if (location != NULL) {
468 				rLocation.u() = location->u() - (u2 << 7);
469 				rLocation.v() = location->v() - (v2 << 7);
470 				rLocation.z = location->z;
471 				drawSpriteMetaTile(metaTileIndex, metaTileX, rLocation, u2 << 3, v2 << 3);
472 			} else {
473 				drawMetaTile(metaTileIndex, metaTileX, u2 << 3, v2 << 3);
474 			}
475 		}
476 
477 		metaTileY.y += 64;
478 
479 		metaTileX = metaTileY;
480 
481 		metaTileX.x -= 128;
482 
483 		for (u2 = u1 - 1, v2 = v1; metaTileX.x < workAreaWidth; u2++, v2--, metaTileX.x += 256) {
484 
485 			uc = u2 & (SAGA_TILEMAP_W - 1);
486 			vc = v2 & (SAGA_TILEMAP_W - 1);
487 
488 			if (uc != u2 || vc != v2) {
489 				metaTileIndex = 0;
490 				switch (_tileMap.edgeType) {
491 				case kEdgeTypeBlack:
492 					continue;
493 				case kEdgeTypeFill0:
494 					break;
495 				case kEdgeTypeFill1:
496 					metaTileIndex = 1;
497 					break;
498 				case kEdgeTypeRpt:
499 					uc = CLIP<int16>(u2, 0, SAGA_TILEMAP_W - 1);
500 					vc = CLIP<int16>(v2, 0, SAGA_TILEMAP_W - 1);
501 					metaTileIndex = _tileMap.tilePlatforms[uc][vc];
502 					break;
503 				case kEdgeTypeWrap:
504 					metaTileIndex = _tileMap.tilePlatforms[uc][vc];
505 					break;
506 				}
507 			} else {
508 				metaTileIndex = _tileMap.tilePlatforms[uc][vc];
509 			}
510 
511 			if (location != NULL) {
512 				rLocation.u() = location->u() - (u2 << 7);
513 				rLocation.v() = location->v() - (v2 << 7);
514 				rLocation.z = location->z;
515 				drawSpriteMetaTile(metaTileIndex, metaTileX, rLocation, u2 << 3, v2 << 3);
516 			} else {
517 				drawMetaTile(metaTileIndex, metaTileX, u2 << 3, v2 << 3);
518 			}
519 		}
520 		metaTileY.y += 64;
521 	}
522 
523 }
524 
drawSpriteMetaTile(uint16 metaTileIndex,const Point & point,Location & location,int16 absU,int16 absV)525 void IsoMap::drawSpriteMetaTile(uint16 metaTileIndex, const Point &point, Location &location, int16 absU, int16 absV) {
526 	MetaTileData * metaTile;
527 	uint16 high;
528 	int16 platformIndex;
529 	Point platformPoint;
530 	platformPoint = point;
531 
532 	if (_metaTileList.size() <= metaTileIndex) {
533 		error("IsoMap::drawMetaTile wrong metaTileIndex");
534 	}
535 
536 	metaTile = &_metaTileList[metaTileIndex];
537 
538 	if (metaTile->highestPlatform > 18) {
539 		metaTile->highestPlatform = 0;
540 	}
541 
542 	for (high = 0; high <= metaTile->highestPlatform; high++, platformPoint.y -= 8, location.z -= 8) {
543 		assert(SAGA_MAX_PLATFORM_H > high);
544 		platformIndex = metaTile->stack[high];
545 
546 		if (platformIndex >= 0) {
547 			drawSpritePlatform(platformIndex, platformPoint, location, absU, absV, high);
548 		}
549 	}
550 }
551 
drawMetaTile(uint16 metaTileIndex,const Point & point,int16 absU,int16 absV)552 void IsoMap::drawMetaTile(uint16 metaTileIndex, const Point &point, int16 absU, int16 absV) {
553 	MetaTileData * metaTile;
554 	uint16 high;
555 	int16 platformIndex;
556 	Point platformPoint;
557 	platformPoint = point;
558 
559 	if (_metaTileList.size() <= metaTileIndex) {
560 		error("IsoMap::drawMetaTile wrong metaTileIndex");
561 	}
562 
563 	metaTile = &_metaTileList[metaTileIndex];
564 
565 	if (metaTile->highestPlatform > 18) {
566 		metaTile->highestPlatform = 0;
567 	}
568 
569 	for (high = 0; high <= metaTile->highestPlatform; high++, platformPoint.y -= 8) {
570 		assert(SAGA_MAX_PLATFORM_H > high);
571 		platformIndex = metaTile->stack[high];
572 
573 		if (platformIndex >= 0) {
574 			drawPlatform(platformIndex, platformPoint, absU, absV, high);
575 		}
576 	}
577 }
578 
drawSpritePlatform(uint16 platformIndex,const Point & point,const Location & location,int16 absU,int16 absV,int16 absH)579 void IsoMap::drawSpritePlatform(uint16 platformIndex, const Point &point, const Location &location, int16 absU, int16 absV, int16 absH) {
580 	TilePlatformData *tilePlatform;
581 	int16 u, v;
582 	Point s;
583 	Point s0;
584 	uint16 tileIndex;
585 	Location copyLocation(location);
586 
587 	if (_tilePlatformList.size() <= platformIndex) {
588 		error("IsoMap::drawPlatform wrong platformIndex");
589 	}
590 
591 	tilePlatform = &_tilePlatformList[platformIndex];
592 
593 	if ((point.y <= _tileClip.top) || (point.y - SAGA_MAX_TILE_H - SAGA_PLATFORM_W * SAGA_TILE_NOMINAL_H >= _tileClip.bottom)) {
594 		return;
595 	}
596 
597 	s0 = point;
598 	s0.y -= (((SAGA_PLATFORM_W - 1) + (SAGA_PLATFORM_W - 1)) * 8);
599 
600 	for (v = SAGA_PLATFORM_W - 1,
601 		copyLocation.v() = location.v() - ((SAGA_PLATFORM_W - 1) << 4);
602 		v >= 0 && s0.y - SAGA_MAX_TILE_H < _tileClip.bottom && s0.x - 128 < _tileClip.right;
603 		v--, copyLocation.v() += 16, s0.x += 16, s0.y += 8) {
604 
605 		if ((tilePlatform->vBits & (1 << v)) == 0) {
606 			continue;
607 		}
608 
609 		if (s0.x + 128 + 32 < _tileClip.left) {
610 			continue;
611 		}
612 
613 		s = s0;
614 
615 		for (u = SAGA_PLATFORM_W - 1,
616 			copyLocation.u() = location.u() - ((SAGA_PLATFORM_W - 1) << 4);
617 			 u >= 0 && s.x + 32 > _tileClip.left && s.y - SAGA_MAX_TILE_H < _tileClip.bottom;
618 			 u--, copyLocation.u() += 16, s.x -= 16, s.y += 8) {
619 			if (s.x < _tileClip.right && s.y > _tileClip.top) {
620 
621 				tileIndex = tilePlatform->tiles[u][v];
622 				if (tileIndex != 0) {
623 					if (tileIndex & SAGA_MULTI_TILE) {
624 						tileIndex = findMulti(tileIndex, absU + u, absV + v, absH);
625 					}
626 
627 					drawTile(tileIndex, s, &copyLocation);
628 				}
629 			}
630 		}
631 	}
632 }
633 
drawPlatform(uint16 platformIndex,const Point & point,int16 absU,int16 absV,int16 absH)634 void IsoMap::drawPlatform(uint16 platformIndex, const Point &point, int16 absU, int16 absV, int16 absH) {
635 	TilePlatformData *tilePlatform;
636 	int16 u, v;
637 	Point s;
638 	Point s0;
639 	uint16 tileIndex;
640 
641 	if (_tilePlatformList.size() <= platformIndex) {
642 		error("IsoMap::drawPlatform wrong platformIndex");
643 	}
644 
645 	tilePlatform = &_tilePlatformList[platformIndex];
646 
647 	if ((point.y <= _tileClip.top) || (point.y - SAGA_MAX_TILE_H - SAGA_PLATFORM_W * SAGA_TILE_NOMINAL_H >= _tileClip.bottom)) {
648 		return;
649 	}
650 
651 	s0 = point;
652 	s0.y -= (((SAGA_PLATFORM_W - 1) + (SAGA_PLATFORM_W - 1)) * 8);
653 
654 	for (v = SAGA_PLATFORM_W - 1;
655 		v >= 0 && s0.y - SAGA_MAX_TILE_H < _tileClip.bottom && s0.x - 128 < _tileClip.right;
656 		v--, s0.x += 16, s0.y += 8) {
657 
658 		if ((tilePlatform->vBits & (1 << v)) == 0) {
659 			continue;
660 		}
661 
662 		if (s0.x + 128 + 32 < _tileClip.left) {
663 			continue;
664 		}
665 
666 		s = s0;
667 
668 		for (u = SAGA_PLATFORM_W - 1;
669 			u >= 0 && s.x + 32 > _tileClip.left && s.y - SAGA_MAX_TILE_H < _tileClip.bottom;
670 			u--, s.x -= 16, s.y += 8) {
671 			if (s.x < _tileClip.right && s.y > _tileClip.top) {
672 
673 				tileIndex = tilePlatform->tiles[u][v];
674 				if (tileIndex > 1) {
675 					if (tileIndex & SAGA_MULTI_TILE) {
676 						tileIndex = findMulti(tileIndex, absU + u, absV + v, absH);
677 					}
678 
679 					drawTile(tileIndex, s, NULL);
680 				}
681 			}
682 		}
683 	}
684 }
685 
686 #define THRESH0			0
687 #define THRESH8			8
688 #define THRESH16		16
689 
drawTile(uint16 tileIndex,const Point & point,const Location * location)690 void IsoMap::drawTile(uint16 tileIndex, const Point &point, const Location *location) {
691 	const byte *tilePointer;
692 	const byte *readPointer;
693 	byte *drawPointer;
694 	Point drawPoint;
695 	int height;
696 	int widthCount = 0;
697 	int row, col, count, lowBound;
698 	int bgRunCount;
699 	int fgRunCount;
700 
701 	if (tileIndex >= _tilesTable.size()) {
702 		error("IsoMap::drawTile wrong tileIndex");
703 	}
704 
705 
706 	if (point.x + SAGA_ISOTILE_WIDTH < _tileClip.left) {
707 		return;
708 	}
709 
710 	if (point.x - SAGA_ISOTILE_WIDTH >= _tileClip.right) {
711 		return;
712 	}
713 
714 	tilePointer = _tilesTable[tileIndex].tilePointer;
715 	height = _tilesTable[tileIndex].height;
716 
717 	if ((height <= 8) || (height > 64)) {
718 		return;
719 	}
720 
721 	drawPoint = point;
722 	drawPoint.y -= height;
723 
724 	if (drawPoint.y >= _tileClip.bottom) {
725 		return;
726 	}
727 
728 	if (location != NULL) {
729 		if (location->z <= -16) {
730 			if (location->z <= -48) {
731 				if (location->u() < -THRESH8 || location->v() < -THRESH8) {
732 					return;
733 				}
734 			} else {
735 				if (location->u() < THRESH0 || location->v() < THRESH0) {
736 					return;
737 				}
738 			}
739 		} else {
740 			if (location->z >= 16) {
741 				return;
742 			} else {
743 				switch (_tilesTable[tileIndex].getMaskRule()) {
744 				case kMaskRuleNever:
745 					return;
746 				case kMaskRuleAlways:
747 					break;
748 				case kMaskRuleUMIN:
749 					if (location->u() < THRESH0) {
750 						return;
751 					}
752 					break;
753 				case kMaskRuleUMID:
754 					if (location->u() < THRESH8) {
755 						return;
756 					}
757 					break;
758 				case kMaskRuleUMAX:
759 					if (location->u() < THRESH16) {
760 						return;
761 					}
762 					break;
763 				case kMaskRuleVMIN:
764 					if (location->v() < THRESH0) {
765 						return;
766 					}
767 					break;
768 				case kMaskRuleVMID:
769 					if (location->v() < THRESH8) {
770 						return;
771 					}
772 					break;
773 				case kMaskRuleVMAX:
774 					if (location->v() < THRESH16) {
775 						return;
776 					}
777 					break;
778 				case kMaskRuleYMIN:
779 					if (location->uv() < THRESH0 * 2) {
780 						return;
781 					}
782 					break;
783 				case kMaskRuleYMID:
784 					if (location->uv() < THRESH8 * 2) {
785 						return;
786 					}
787 					break;
788 				case kMaskRuleYMAX:
789 					if (location->uv() < THRESH16 * 2) {
790 						return;
791 					}
792 					break;
793 				case kMaskRuleUVMAX:
794 					if (location->u() < THRESH16 && location->v() < THRESH16) {
795 						return;
796 					}
797 					break;
798 				case kMaskRuleUVMIN:
799 					if (location->u() < THRESH0 || location->v() < THRESH0) {
800 						return;
801 					}
802 					break;
803 				case kMaskRuleUorV:
804 					if (location->u() < THRESH8 && location->v() < THRESH8) {
805 						return;
806 					}
807 					break;
808 				case kMaskRuleUandV:
809 					if (location->u() < THRESH8 || location->v() < THRESH8) {
810 						return;
811 					}
812 					break;
813 				}
814 			}
815 		}
816 	}
817 
818 	readPointer = tilePointer;
819 	lowBound = MIN((int)(drawPoint.y + height), (int)_tileClip.bottom);
820 	for (row = drawPoint.y; row < lowBound; row++) {
821 		widthCount = 0;
822 		if (row >= _tileClip.top) {
823 			drawPointer = _vm->_gfx->getBackBufferPixels() + drawPoint.x + (row * _vm->_gfx->getBackBufferPitch());
824 			col = drawPoint.x;
825 			for (;;) {
826 				bgRunCount = *readPointer++;
827 				widthCount += bgRunCount;
828 				if (widthCount >= SAGA_ISOTILE_WIDTH) {
829 					break;
830 				}
831 
832 				drawPointer += bgRunCount;
833 				col += bgRunCount;
834 				fgRunCount = *readPointer++;
835 				widthCount += fgRunCount;
836 
837 				count = 0;
838 				int colDiff = _tileClip.left - col;
839 				if (colDiff > 0) {
840 					if (colDiff > fgRunCount) {
841 						colDiff = fgRunCount;
842 					}
843 					count = colDiff;
844 					col += colDiff;
845 				}
846 
847 				colDiff = _tileClip.right - col;
848 				if (colDiff > 0) {
849 					int countDiff = fgRunCount - count;
850 					if (colDiff > countDiff) {
851 						colDiff = countDiff;
852 					}
853 					if (colDiff > 0) {
854 						byte *dst = (byte *)(drawPointer + count);
855 						assert(_vm->_gfx->getBackBufferPixels() <= dst);
856 						assert((_vm->_gfx->getBackBufferPixels() + (_vm->getDisplayInfo().width * _vm->getDisplayInfo().height)) >= (byte *)(dst + colDiff));
857 						memcpy(dst, (readPointer + count), colDiff);
858 						col += colDiff;
859 					}
860 				}
861 
862 				readPointer += fgRunCount;
863 				drawPointer += fgRunCount;
864 			}
865 		} else {
866 			for (;;) {
867 				bgRunCount = *readPointer++;
868 				widthCount += bgRunCount;
869 				if (widthCount >= SAGA_ISOTILE_WIDTH) {
870 					break;
871 				}
872 
873 				fgRunCount = *readPointer++;
874 				widthCount += fgRunCount;
875 
876 				readPointer += fgRunCount;
877 			}
878 		}
879 	}
880 
881 	// Compute dirty rect
882 	int rectX = MAX<int>(drawPoint.x, 0);
883 	int rectY = MAX<int>(drawPoint.y, 0);
884 	int rectX2 = MIN<int>(drawPoint.x + SAGA_ISOTILE_WIDTH, _tileClip.right);
885 	int rectY2 = lowBound;
886 	_vm->_render->addDirtyRect(Common::Rect(rectX, rectY, rectX2, rectY2));
887 }
888 
checkDragonPoint(int16 u,int16 v,uint16 direction)889 bool IsoMap::checkDragonPoint(int16 u, int16 v, uint16 direction) {
890 	DragonPathCell *pathCell;
891 
892 	if ((u < 1) || (u >= SAGA_DRAGON_SEARCH_DIAMETER - 1) || (v < 1) || (v >= SAGA_DRAGON_SEARCH_DIAMETER - 1)) {
893 			return false;
894 	}
895 
896 	pathCell = _dragonSearchArray.getPathCell(u, v);
897 
898 	if (pathCell->visited) {
899 		return false;
900 	}
901 
902 	pathCell->visited = 1;
903 	pathCell->direction = direction;
904 	return true;
905 }
906 
pushDragonPoint(int16 u,int16 v,uint16 direction)907 void IsoMap::pushDragonPoint(int16 u, int16 v, uint16 direction) {
908 	DragonTilePoint *tilePoint;
909 	DragonPathCell *pathCell;
910 
911 	if ((u < 1) || (u >= SAGA_DRAGON_SEARCH_DIAMETER - 1) || (v < 1) || (v >= SAGA_DRAGON_SEARCH_DIAMETER - 1)) {
912 			return;
913 	}
914 
915 	pathCell = _dragonSearchArray.getPathCell(u, v);
916 
917 	if (pathCell->visited) {
918 		return;
919 	}
920 
921 	tilePoint = _dragonSearchArray.getQueue(_queueCount);
922 	_queueCount++;
923 	if (_queueCount >= SAGA_SEARCH_QUEUE_SIZE) {
924 		_queueCount = 0;
925 	}
926 
927 	tilePoint->u = u;
928 	tilePoint->v = v;
929 	tilePoint->direction = direction;
930 
931 	pathCell->visited = 1;
932 	pathCell->direction = direction;
933 }
934 
pushPoint(int16 u,int16 v,uint16 cost,uint16 direction)935 void IsoMap::pushPoint(int16 u, int16 v, uint16 cost, uint16 direction) {
936 	int16 upper;
937 	int16 lower;
938 	int16 mid;
939 	TilePoint *tilePoint;
940 	PathCell *pathCell;
941 
942 	upper = _queueCount;
943 	lower = 0;
944 
945 	if ((u < 1) || (u >= SAGA_SEARCH_DIAMETER - 1) || (v < 1) || (v >= SAGA_SEARCH_DIAMETER - 1)) {
946 			return;
947 	}
948 
949 	pathCell = _searchArray.getPathCell(u, v);
950 
951 	if ((pathCell->visited) && (pathCell->cost <= cost)) {
952 		return;
953 	}
954 
955 	if (_queueCount >= SAGA_SEARCH_QUEUE_SIZE) {
956 		return;
957 	}
958 
959 	while (1) {
960 		mid = (upper + lower) / 2;
961 		tilePoint = _searchArray.getQueue(mid);
962 
963 		if (upper <= lower) {
964 			break;
965 		}
966 
967 		if (cost < tilePoint->cost) {
968 			lower = mid + 1;
969 		} else {
970 			upper = mid;
971 		}
972 	}
973 
974 	if (mid < _queueCount) {
975 		memmove(tilePoint + 1, tilePoint, (_queueCount - mid) * sizeof (*tilePoint));
976 	}
977 	_queueCount++;
978 
979 	tilePoint->u = u;
980 	tilePoint->v = v;
981 	tilePoint->cost = cost;
982 	tilePoint->direction = direction;
983 
984 	pathCell->visited = 1;
985 	pathCell->direction = direction;
986 	pathCell->cost = cost;
987 }
988 
getTileIndex(int16 u,int16 v,int16 z)989 int16 IsoMap::getTileIndex(int16 u, int16 v, int16 z) {
990 	int16 mtileU;
991 	int16 mtileV;
992 	int16 uc;
993 	int16 vc;
994 	int16 u0;
995 	int16 v0;
996 	int16 platformIndex;
997 	int16 metaTileIndex;
998 
999 	mtileU = u >> 3;
1000 	mtileV = v >> 3;
1001 	uc = mtileU & (SAGA_TILEMAP_W - 1);
1002 	vc = mtileV & (SAGA_TILEMAP_W - 1);
1003 	u0 = u & (SAGA_PLATFORM_W - 1);
1004 	v0 = v & (SAGA_PLATFORM_W - 1);
1005 
1006 	if ((uc != mtileU) || (vc != mtileV)) {
1007 		metaTileIndex = 0;
1008 		switch (_tileMap.edgeType) {
1009 		case kEdgeTypeBlack:
1010 			return 0;
1011 		case kEdgeTypeFill0:
1012 			break;
1013 		case kEdgeTypeFill1:
1014 			metaTileIndex = 1;
1015 			break;
1016 		case kEdgeTypeRpt:
1017 			uc = CLIP<int16>(mtileU, 0, SAGA_TILEMAP_W - 1);
1018 			vc = CLIP<int16>(mtileV, 0, SAGA_TILEMAP_W - 1);
1019 			metaTileIndex = _tileMap.tilePlatforms[uc][vc];
1020 			break;
1021 		case kEdgeTypeWrap:
1022 			metaTileIndex = _tileMap.tilePlatforms[uc][vc];
1023 			break;
1024 		}
1025 	} else {
1026 		metaTileIndex = _tileMap.tilePlatforms[uc][vc];
1027 	}
1028 
1029 	if (_metaTileList.size() <= (uint)metaTileIndex) {
1030 		error("IsoMap::getTile wrong metaTileIndex");
1031 	}
1032 
1033 	platformIndex = _metaTileList[metaTileIndex].stack[z];
1034 	if (platformIndex < 0) {
1035 		return 0;
1036 	}
1037 
1038 	if (_tilePlatformList.size() <= (uint)platformIndex) {
1039 		error("IsoMap::getTile wrong platformIndex");
1040 	}
1041 
1042 	return _tilePlatformList[platformIndex].tiles[u0][v0];
1043 }
1044 
getTile(int16 u,int16 v,int16 z)1045 IsoTileData *IsoMap::getTile(int16 u, int16 v, int16 z) {
1046 	int16 tileIndex;
1047 
1048 	tileIndex = getTileIndex(u, v, z);
1049 
1050 	if (tileIndex == 0) {
1051 		return NULL;
1052 	}
1053 
1054 	if (tileIndex & SAGA_MULTI_TILE) {
1055 		tileIndex = findMulti(tileIndex, u, v, z);
1056 	}
1057 
1058 	return &_tilesTable[tileIndex];
1059 }
1060 
testPossibleDirections(int16 u,int16 v,uint16 terraComp[8],int skipCenter)1061 void IsoMap::testPossibleDirections(int16 u, int16 v, uint16 terraComp[8], int skipCenter) {
1062 	IsoTileData *tile;
1063 	uint16 fgdMask;
1064 	uint16 bgdMask;
1065 	uint16 mask;
1066 
1067 
1068 	memset(terraComp, 0, 8 * sizeof(uint16));
1069 
1070 #define FILL_MASK(index, testMask)		\
1071 	if (mask & testMask) {				\
1072 		terraComp[index] |= fgdMask;	\
1073 	}									\
1074 	if (~mask & testMask) {				\
1075 		terraComp[index] |= bgdMask;	\
1076 	}
1077 
1078 #define TEST_TILE_PROLOG(offsetU, offsetV)						\
1079 	tile = getTile(u + offsetU, v + offsetV , _platformHeight);	\
1080 	if (tile != NULL) {											\
1081 		fgdMask = tile->getFGDMask();							\
1082 		bgdMask = tile->getBGDMask();							\
1083 		mask = tile->terrainMask;
1084 
1085 #define TEST_TILE_EPILOG(index)									\
1086 	} else {													\
1087 		if (_vm->_actor->_protagonist->_location.z > 0) {		\
1088 			terraComp[index] = SAGA_IMPASSABLE;					\
1089 		}														\
1090 	}
1091 
1092 #define TEST_TILE_END	}
1093 
1094 	TEST_TILE_PROLOG(0, 0)
1095 		if (skipCenter) {
1096 			if ((mask & 0x0660) && (fgdMask & SAGA_IMPASSABLE)) {
1097 				fgdMask = 0;
1098 			}
1099 			if ((~mask & 0x0660) && (bgdMask & SAGA_IMPASSABLE)) {
1100 				bgdMask = 0;
1101 			}
1102 		}
1103 
1104 		FILL_MASK(0, 0xcc00)
1105 		FILL_MASK(1, 0x6600)
1106 		FILL_MASK(2, 0x3300)
1107 		FILL_MASK(3, 0x0330)
1108 		FILL_MASK(4, 0x0033)
1109 		FILL_MASK(5, 0x0066)
1110 		FILL_MASK(6, 0x00cc)
1111 		FILL_MASK(7, 0x0cc0)
1112 	TEST_TILE_END
1113 
1114 	TEST_TILE_PROLOG(1, 1)
1115 		FILL_MASK(0, 0x0673)
1116 	TEST_TILE_EPILOG(0)
1117 
1118 
1119 	TEST_TILE_PROLOG(1, 0)
1120 		FILL_MASK(0, 0x0008)
1121 		FILL_MASK(1, 0x0666)
1122 		FILL_MASK(2, 0x0001)
1123 	TEST_TILE_EPILOG(1)
1124 
1125 
1126 	TEST_TILE_PROLOG(1, -1)
1127 		FILL_MASK(2, 0x06ec)
1128 	TEST_TILE_EPILOG(2)
1129 
1130 	TEST_TILE_PROLOG(0, 1)
1131 		FILL_MASK(0, 0x1000)
1132 		FILL_MASK(7, 0x0770)
1133 		FILL_MASK(6, 0x0001)
1134 	TEST_TILE_EPILOG(7)
1135 
1136 
1137 	TEST_TILE_PROLOG(0, -1)
1138 		FILL_MASK(2, 0x8000)
1139 		FILL_MASK(3, 0x0ee0)
1140 		FILL_MASK(4, 0x0008)
1141 	TEST_TILE_EPILOG(3)
1142 
1143 
1144 	TEST_TILE_PROLOG(-1, 1)
1145 		FILL_MASK(6, 0x3670)
1146 	TEST_TILE_EPILOG(6)
1147 
1148 
1149 	TEST_TILE_PROLOG(-1, 0)
1150 		FILL_MASK(6, 0x8000)
1151 		FILL_MASK(5, 0x6660)
1152 		FILL_MASK(4, 0x1000)
1153 	TEST_TILE_EPILOG(5)
1154 
1155 	TEST_TILE_PROLOG(-1, -1)
1156 		FILL_MASK(4, 0xce60)
1157 	TEST_TILE_EPILOG(4)
1158 }
1159 
placeOnTileMap(const Location & start,Location & result,int16 distance,uint16 direction)1160 void IsoMap::placeOnTileMap(const Location &start, Location &result, int16 distance, uint16 direction) {
1161 	int16 bestDistance;
1162 	int16 bestU;
1163 	int16 bestV;
1164 	int16 uBase;
1165 	int16 vBase;
1166 	int16 u;
1167 	int16 v;
1168 	TilePoint tilePoint;
1169 	uint16 dir;
1170 	int16 dist;
1171 	uint16 terraComp[8];
1172 	const TilePoint *tdir;
1173 	uint16 terrainMask;
1174 
1175 	bestDistance = 0;
1176 
1177 
1178 	uBase = (start.u() >> 4) - SAGA_SEARCH_CENTER;
1179 	vBase = (start.v() >> 4) - SAGA_SEARCH_CENTER;
1180 
1181 	bestU = SAGA_SEARCH_CENTER;
1182 	bestV = SAGA_SEARCH_CENTER;
1183 
1184 	_platformHeight = _vm->_actor->_protagonist->_location.z / 8;
1185 
1186 	memset(&_searchArray, 0, sizeof(_searchArray));
1187 
1188 	for (ActorDataArray::const_iterator actor = _vm->_actor->_actors.begin(); actor != _vm->_actor->_actors.end(); ++actor) {
1189 		if (!actor->_inScene) continue;
1190 
1191 		u = (actor->_location.u() >> 4) - uBase;
1192 		v = (actor->_location.v() >> 4) - vBase;
1193 		if ((u >= 0) && (u < SAGA_SEARCH_DIAMETER) &&
1194 			(v >= 0) && (v < SAGA_SEARCH_DIAMETER) &&
1195 			((u != SAGA_SEARCH_CENTER) || (v != SAGA_SEARCH_CENTER))) {
1196 			_searchArray.getPathCell(u, v)->visited = 1;
1197 		}
1198 	}
1199 
1200 	_queueCount = 0;
1201 	pushPoint(SAGA_SEARCH_CENTER, SAGA_SEARCH_CENTER, 0, 0);
1202 
1203 	while (_queueCount > 0) {
1204 
1205 		_queueCount--;
1206 		tilePoint = *_searchArray.getQueue(_queueCount);
1207 
1208 
1209 		dist = ABS(tilePoint.u - SAGA_SEARCH_CENTER) + ABS(tilePoint.v - SAGA_SEARCH_CENTER);
1210 
1211 		if (dist > bestDistance) {
1212 			bestU = tilePoint.u;
1213 			bestV = tilePoint.v;
1214 			bestDistance = dist;
1215 
1216 			if (dist >= distance) {
1217 				break;
1218 			}
1219 		}
1220 
1221 		testPossibleDirections(uBase + tilePoint.u, vBase + tilePoint.v, terraComp, 0);
1222 
1223 
1224 		for (dir = 0; dir < 8; dir++) {
1225 			terrainMask = terraComp[dir];
1226 
1227 			if (terrainMask & SAGA_IMPASSABLE) {
1228 				continue;
1229 			}
1230 
1231 			if (dir == direction) {
1232 				tdir = &easyDirTable[ dir ];
1233 			} else {
1234 				if (dir + 1 == direction || dir - 1 == direction) {
1235 					tdir = &normalDirTable[ dir ];
1236 				} else {
1237 					tdir = &hardDirTable[ dir ];
1238 				}
1239 			}
1240 
1241 			pushPoint(tilePoint.u + tdir->u, tilePoint.v + tdir->v, tilePoint.cost + tdir->cost, dir);
1242 		}
1243 	}
1244 
1245 	result.u() = ((uBase + bestU) << 4) + 8;
1246 	result.v() = ((vBase + bestV) << 4) + 8;
1247 }
1248 
findNearestChasm(int16 & u0,int16 & v0,uint16 & direction)1249 bool IsoMap::findNearestChasm(int16 &u0, int16 &v0, uint16 &direction) {
1250 	int16 u, v;
1251 	uint16 i;
1252 	u = u0;
1253 	v = v0;
1254 
1255 	for (i = 1; i < 5; i++) {
1256 		if (getTile(u - i, v, 6) == NULL) {
1257 			u0 = u - i - 1;
1258 			v0 = v;
1259 			direction = kDirDownLeft;
1260 			return true;
1261 		}
1262 
1263 		if (getTile(u, v - i, 6) == NULL) {
1264 			u0 = u;
1265 			v0 = v - i - 1;
1266 			direction = kDirDownRight;
1267 			return true;
1268 		}
1269 
1270 		if (getTile(u - i, v - i, 6) == NULL) {
1271 			u0 = u - i - 1;
1272 			v0 = v - i - 1;
1273 			direction = kDirDown;
1274 			return true;
1275 		}
1276 
1277 		if (getTile(u + i, v - i, 6) == NULL) {
1278 			u0 = u + i + 1;
1279 			v0 = v - i - 1;
1280 			direction = kDirDownRight;
1281 			return true;
1282 		}
1283 
1284 		if (getTile(u - i, v + i, 6) == NULL) {
1285 			u0 = u + i + 1;
1286 			v0 = v - i - 1;
1287 			direction = kDirLeft;
1288 			return true;
1289 		}
1290 	}
1291 
1292 	for (i = 1; i < 5; i++) {
1293 		if (getTile(u + i, v, 6) == NULL) {
1294 			u0 = u + i + 1;
1295 			v0 = v;
1296 			direction = kDirUpRight;
1297 			return true;
1298 		}
1299 
1300 		if (getTile(u, v + i, 6) == NULL) {
1301 			u0 = u;
1302 			v0 = v + i + 1;
1303 			direction = kDirUpLeft;
1304 			return true;
1305 		}
1306 
1307 		if (getTile(u + i, v + i, 6) == NULL) {
1308 			u0 = u + i + 1;
1309 			v0 = v + i + 1;
1310 			direction = kDirUp;
1311 			return true;
1312 		}
1313 	}
1314 	return false;
1315 }
1316 
findDragonTilePath(ActorData * actor,const Location & start,const Location & end,uint16 initialDirection)1317 void IsoMap::findDragonTilePath(ActorData* actor, const Location &start, const Location &end, uint16 initialDirection) {
1318 	byte *res;
1319 	int i;
1320 	int16 u;
1321 	int16 v;
1322 	int16 u1;
1323 	int16 v1;
1324 	uint16 dir;
1325 
1326 	int16 bestDistance;
1327 	int16 bestU;
1328 	int16 bestV;
1329 
1330 	int16 uBase;
1331 	int16 vBase;
1332 	int16 uFinish;
1333 	int16 vFinish;
1334 	DragonPathCell *pcell;
1335 	IsoTileData *tile;
1336 	uint16 mask;
1337 	DragonTilePoint *tilePoint;
1338 
1339 	int16 dist;
1340 	bool first;
1341 
1342 	bestDistance = SAGA_DRAGON_SEARCH_DIAMETER;
1343 	bestU = SAGA_DRAGON_SEARCH_CENTER;
1344 	bestV = SAGA_DRAGON_SEARCH_CENTER;
1345 
1346 	uBase = (start.u() >> 4) - SAGA_DRAGON_SEARCH_CENTER;
1347 	vBase = (start.v() >> 4) - SAGA_DRAGON_SEARCH_CENTER;
1348 	uFinish = (end.u() >> 4) - uBase;
1349 	vFinish = (end.v() >> 4) - vBase;
1350 
1351 	_platformHeight = _vm->_actor->_protagonist->_location.z / 8;
1352 
1353 	memset(&_dragonSearchArray, 0, sizeof(_dragonSearchArray));
1354 
1355 	for (u = 0; u < SAGA_DRAGON_SEARCH_DIAMETER; u++) {
1356 		for (v = 0; v < SAGA_DRAGON_SEARCH_DIAMETER; v++) {
1357 
1358 			pcell = _dragonSearchArray.getPathCell(u, v);
1359 
1360 			u1 = uBase + u;
1361 			v1 = vBase + v;
1362 
1363 			if ((u1 > 127) || (u1 < 48) || (v1 > 127) || (v1 < 0)) {
1364 				pcell->visited = 1;
1365 				continue;
1366 			}
1367 
1368 			tile = getTile(u1, v1, _platformHeight);
1369 			if (tile != NULL) {
1370 				mask = tile->terrainMask;
1371 				if (((mask != 0     ) && (tile->getFGDAttr() >= kTerrBlock)) ||
1372 				    ((mask != 0xFFFF) && (tile->getBGDAttr() >= kTerrBlock))) {
1373 					pcell->visited = 1;
1374 				}
1375 			} else {
1376 				pcell->visited = 1;
1377 			}
1378 		}
1379 	}
1380 
1381 	first = true;
1382 	_queueCount = _readCount = 0;
1383 	pushDragonPoint(SAGA_DRAGON_SEARCH_CENTER, SAGA_DRAGON_SEARCH_CENTER, initialDirection);
1384 
1385 	while (_queueCount != _readCount) {
1386 
1387 		tilePoint = _dragonSearchArray.getQueue(_readCount++);
1388 		if (_readCount >= SAGA_SEARCH_QUEUE_SIZE) {
1389 			_readCount = 0;
1390 		}
1391 
1392 
1393 		dist = ABS(tilePoint->u - uFinish) + ABS(tilePoint->v - vFinish);
1394 
1395 		if (dist < bestDistance) {
1396 
1397 			bestU = tilePoint->u;
1398 			bestV = tilePoint->v;
1399 			bestDistance = dist;
1400 			if (dist == 0) {
1401 				break;
1402 			}
1403 		}
1404 
1405 		switch (tilePoint->direction) {
1406 			case kDirUpRight:
1407 				if (checkDragonPoint(tilePoint->u + 1, tilePoint->v + 0, kDirUpRight)) {
1408 					pushDragonPoint(tilePoint->u + 2, tilePoint->v + 0, kDirUpRight);
1409 					pushDragonPoint(tilePoint->u + 1, tilePoint->v + 1, kDirUpLeft);
1410 					pushDragonPoint(tilePoint->u + 1, tilePoint->v - 1, kDirDownRight);
1411 				}
1412 				break;
1413 			case kDirDownRight:
1414 				if (checkDragonPoint(tilePoint->u + 0, tilePoint->v - 1, kDirDownRight)) {
1415 					pushDragonPoint(tilePoint->u + 0, tilePoint->v - 2, kDirDownRight);
1416 					pushDragonPoint(tilePoint->u + 1, tilePoint->v - 1, kDirUpRight);
1417 					pushDragonPoint(tilePoint->u - 1, tilePoint->v - 1, kDirDownLeft);
1418 				}
1419 				break;
1420 			case kDirDownLeft:
1421 				if (checkDragonPoint(tilePoint->u - 1, tilePoint->v + 0, kDirDownLeft)) {
1422 					pushDragonPoint(tilePoint->u - 2, tilePoint->v + 0, kDirDownLeft);
1423 					pushDragonPoint(tilePoint->u - 1, tilePoint->v - 1, kDirDownRight);
1424 					pushDragonPoint(tilePoint->u - 1, tilePoint->v + 1, kDirUpLeft);
1425 				}
1426 				break;
1427 			case kDirUpLeft:
1428 				if (checkDragonPoint(tilePoint->u + 0, tilePoint->v + 1, kDirUpLeft)) {
1429 					pushDragonPoint(tilePoint->u + 0, tilePoint->v + 2, kDirUpLeft);
1430 					pushDragonPoint(tilePoint->u - 1, tilePoint->v + 1, kDirDownLeft);
1431 					pushDragonPoint(tilePoint->u + 1, tilePoint->v + 1, kDirUpRight);
1432 				}
1433 				break;
1434 		}
1435 
1436 		if (first && (_queueCount == _readCount)) {
1437 			pushDragonPoint(tilePoint->u + 1, tilePoint->v + 0, kDirUpRight);
1438 			pushDragonPoint(tilePoint->u + 0, tilePoint->v - 1, kDirDownRight);
1439 			pushDragonPoint(tilePoint->u - 1, tilePoint->v + 0, kDirDownLeft);
1440 			pushDragonPoint(tilePoint->u + 0, tilePoint->v + 1, kDirUpLeft);
1441 		}
1442 		first = false;
1443 	}
1444 
1445 	res = &_pathDirections[SAGA_MAX_PATH_DIRECTIONS];
1446 	i = 0;
1447 	while ((bestU != SAGA_DRAGON_SEARCH_CENTER) || (bestV != SAGA_DRAGON_SEARCH_CENTER)) {
1448 		pcell = _dragonSearchArray.getPathCell(bestU, bestV);
1449 
1450 		*--res = pcell->direction;
1451 		i++;
1452 		if (i >= SAGA_MAX_PATH_DIRECTIONS) {
1453 			break;
1454 		}
1455 
1456 		dir = (pcell->direction + 4) & 0x07;
1457 
1458 		bestU += normalDirTable[dir].u;
1459 		bestV += normalDirTable[dir].v;
1460 	}
1461 
1462 /*	if (i > 64) {
1463 		i = 64;
1464 	}*/
1465 
1466 	actor->_walkStepsCount = i;
1467 	if (i) {
1468 		actor->_tileDirections.resize(i);
1469 		memcpy(&actor->_tileDirections.front(), res, i);
1470 	}
1471 
1472 }
1473 
findTilePath(ActorData * actor,const Location & start,const Location & end)1474 void IsoMap::findTilePath(ActorData* actor, const Location &start, const Location &end) {
1475 	int i;
1476 	int16 u;
1477 	int16 v;
1478 	int16 bestDistance;
1479 	int16 bestU;
1480 	int16 bestV;
1481 
1482 	int16 uBase;
1483 	int16 vBase;
1484 	int16 uFinish;
1485 	int16 vFinish;
1486 
1487 	TilePoint tilePoint;
1488 	uint16 dir;
1489 	int16 dist;
1490 	uint16 terraComp[8];
1491 	const TilePoint *tdir;
1492 	uint16 terrainMask;
1493 	const PathCell *pcell;
1494 	byte *res;
1495 
1496 
1497 	bestDistance = SAGA_SEARCH_DIAMETER;
1498 	bestU = SAGA_SEARCH_CENTER;
1499 	bestV = SAGA_SEARCH_CENTER;
1500 
1501 	uBase = (start.u() >> 4) - SAGA_SEARCH_CENTER;
1502 	vBase = (start.v() >> 4) - SAGA_SEARCH_CENTER;
1503 	uFinish = (end.u() >> 4) - uBase;
1504 	vFinish = (end.v() >> 4) - vBase;
1505 
1506 	_platformHeight = _vm->_actor->_protagonist->_location.z / 8;
1507 
1508 
1509 
1510 	memset(&_searchArray, 0, sizeof(_searchArray));
1511 
1512 	if (!(actor->_actorFlags & kActorNoCollide) &&
1513 		(_vm->_scene->currentSceneResourceId() != ITE_SCENE_OVERMAP)) {
1514 			for (ActorDataArray::const_iterator other = _vm->_actor->_actors.begin(); other != _vm->_actor->_actors.end(); ++other) {
1515 				if (!other->_inScene) continue;
1516 				if (other->_id == actor->_id) continue;
1517 
1518 				u = (other->_location.u() >> 4) - uBase;
1519 				v = (other->_location.v() >> 4) - vBase;
1520 				if ((u >= 1) && (u < SAGA_SEARCH_DIAMETER) &&
1521 					(v >= 1) && (v < SAGA_SEARCH_DIAMETER) &&
1522 					((u != SAGA_SEARCH_CENTER) || (v != SAGA_SEARCH_CENTER))) {
1523 						_searchArray.getPathCell(u, v)->visited = 1;
1524 					}
1525 			}
1526 		}
1527 
1528 	_queueCount = 0;
1529 	pushPoint(SAGA_SEARCH_CENTER, SAGA_SEARCH_CENTER, 0, 0);
1530 
1531 
1532 	while (_queueCount > 0) {
1533 
1534 		_queueCount--;
1535 		tilePoint = *_searchArray.getQueue(_queueCount);
1536 
1537 		if (tilePoint.cost > 100 && actor == _vm->_actor->_protagonist) continue;
1538 
1539 		dist = ABS(tilePoint.u - uFinish) + ABS(tilePoint.v - vFinish);
1540 
1541 		if (dist < bestDistance) {
1542 			bestU = tilePoint.u;
1543 			bestV = tilePoint.v;
1544 			bestDistance = dist;
1545 
1546 			if (dist == 0) {
1547 				break;
1548 			}
1549 		}
1550 
1551 		testPossibleDirections(uBase + tilePoint.u, vBase + tilePoint.v, terraComp,
1552 			(tilePoint.u == SAGA_SEARCH_CENTER && tilePoint.v == SAGA_SEARCH_CENTER));
1553 
1554 		for (dir = 0; dir < 8; dir++) {
1555 			terrainMask = terraComp[dir];
1556 
1557 			if (terrainMask & SAGA_IMPASSABLE) {
1558 				continue;
1559 			} else {
1560 				if (terrainMask & (1 << kTerrRough)) {
1561 					tdir = &hardDirTable[ dir ];
1562 				} else {
1563 					if (terrainMask & (1 << kTerrNone)) {
1564 						tdir = &normalDirTable[ dir ];
1565 					} else {
1566 						tdir = &easyDirTable[ dir ];
1567 					}
1568 				}
1569 			}
1570 
1571 
1572 			pushPoint(tilePoint.u + tdir->u, tilePoint.v + tdir->v, tilePoint.cost + tdir->cost, dir);
1573 		}
1574 	}
1575 
1576 	res = &_pathDirections[SAGA_MAX_PATH_DIRECTIONS];
1577 	i = 0;
1578 	while ((bestU != SAGA_SEARCH_CENTER) || (bestV != SAGA_SEARCH_CENTER)) {
1579 		pcell = _searchArray.getPathCell(bestU, bestV);
1580 
1581 		*--res = pcell->direction;
1582 		i++;
1583 		if (i >= SAGA_MAX_PATH_DIRECTIONS) {
1584 			break;
1585 		}
1586 
1587 		dir = (pcell->direction + 4) & 0x07;
1588 
1589 		bestU += normalDirTable[dir].u;
1590 		bestV += normalDirTable[dir].v;
1591 	}
1592 
1593 /*	if (i > 64) {
1594 		i = 64;
1595 	}*/
1596 
1597 	actor->_walkStepsCount = i;
1598 	if (i) {
1599 		actor->_tileDirections.resize(i);
1600 		memcpy(&actor->_tileDirections.front(), res, i);
1601 	}
1602 }
1603 
setTileDoorState(int doorNumber,int doorState)1604 void IsoMap::setTileDoorState(int doorNumber, int doorState) {
1605 	MultiTileEntryData *multiTileEntryData;
1606 
1607 	if ((doorNumber < 0) || ((uint)doorNumber >= _multiTable.size())) {
1608 		error("setTileDoorState: doorNumber >= _multiTable.size()");
1609 	}
1610 
1611 	multiTileEntryData = &_multiTable[doorNumber];
1612 	multiTileEntryData->currentState = doorState;
1613 }
1614 
nextTileTarget(ActorData * actor)1615 bool IsoMap::nextTileTarget(ActorData* actor) {
1616 	uint16 dir;
1617 
1618 	if (actor->_walkStepIndex >= actor->_walkStepsCount) {
1619 		return false;
1620 	}
1621 
1622 
1623 	actor->_actionDirection = dir = actor->_tileDirections[actor->_walkStepIndex++];
1624 
1625 	actor->_partialTarget.u() =
1626 		(actor->_location.u() & ~0x0f) + 8 + directions[dir][0];
1627 
1628 	actor->_partialTarget.v() =
1629 		(actor->_location.v() & ~0x0f) + 8 + directions[dir][1];
1630 
1631 
1632 	if (dir == 0) {
1633 		actor->_facingDirection = kDirUp;
1634 	} else {
1635 		if (dir == 4) {
1636 			actor->_facingDirection = kDirDown;
1637 		} else {
1638 			if (dir < 4) {
1639 				actor->_facingDirection = kDirRight;
1640 			} else {
1641 				actor->_facingDirection = kDirLeft;
1642 			}
1643 		}
1644 	}
1645 
1646 	return true;
1647 }
1648 
screenPointToTileCoords(const Point & position,Location & location)1649 void IsoMap::screenPointToTileCoords(const Point &position, Location &location) {
1650 	Point mPos(position);
1651 	int x, y;
1652 
1653 	if (_vm->_scene->currentSceneResourceId() == ITE_SCENE_OVERMAP){
1654 		if (mPos.y < 16) {
1655 			mPos.y = 16;
1656 		}
1657 	}
1658 
1659 	x = mPos.x + _viewScroll.x - (128 * SAGA_TILEMAP_W) - 16;
1660 	y = mPos.y + _viewScroll.y - (128 * SAGA_TILEMAP_W) + _vm->_actor->_protagonist->_location.z;
1661 
1662 	location.u() = (x - y * 2) >> 1;
1663 	location.v() = - (x + y * 2) >> 1;
1664 	location.z = _vm->_actor->_protagonist->_location.z;
1665 }
1666 
1667 } // End of namespace Saga
1668