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, ©Location);
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