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 * aint32 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 * Based on the original sources
23 * Faery Tale II -- The Halls of the Dead
24 * (c) 1993-1996 The Wyrmkeep Entertainment Co.
25 */
26
27 #include "saga2/saga2.h"
28 #include "saga2/idtypes.h"
29 #include "saga2/tile.h"
30 #include "saga2/tileline.h"
31 #include "saga2/actor.h"
32
33 namespace Saga2 {
34
35
36 extern WorldMapData *mapList;
37
38 static int16 prevMapNum;
39 static StaticTilePoint prevCoords = {(int16)minint16, (int16)minint16, (int16)minint16};
40 static MetaTilePtr prevMeta = NULL;
41
42 /* ===================================================================== *
43 Terrain damage info
44 * ===================================================================== */
45
drown(GameObject * obj)46 void drown(GameObject *obj) {
47 if (isActor(obj)) {
48 Actor *a = (Actor *) obj;
49 if (!a->hasEffect(actorWaterBreathe)) {
50 if (g_vm->_rnd->getRandomNumber(drowningDamageOddsYes + drowningDamageOddsNo - 1) > drowningDamageOddsNo - 1) {
51 a->acceptDamage(a->thisID(), drowningDamagePerFrame);
52 }
53 }
54 }
55
56 }
57
lavaDamage(GameObject * obj)58 void lavaDamage(GameObject *obj) {
59 if (isActor(obj)) {
60 Actor *a = (Actor *) obj;
61 if (a->resists(resistHeat))
62 return;
63 }
64 if (g_vm->_rnd->getRandomNumber(heatDamageOddsYes + heatDamageOddsNo - 1) > heatDamageOddsNo - 1) {
65 obj->acceptDamage(obj->thisID(), heatDamagePerFrame, kDamageHeat, heatDamageDicePerFrame, 6);
66 }
67 }
68
coldDamage(GameObject * obj)69 void coldDamage(GameObject *obj) {
70 if (isActor(obj)) {
71 Actor *a = (Actor *) obj;
72 if (a->resists(resistCold))
73 return;
74 }
75 if (g_vm->_rnd->getRandomNumber(coldDamageOddsYes + coldDamageOddsNo - 1) > coldDamageOddsNo - 1) {
76 obj->acceptDamage(obj->thisID(), coldDamagePerFrame, kDamageCold, coldDamageDicePerFrame, 6);
77 }
78 }
79
terrainDamageSlash(GameObject * obj)80 void terrainDamageSlash(GameObject *obj) {
81 if (g_vm->_rnd->getRandomNumber(terrainDamageOddsYes + terrainDamageOddsNo - 1) > terrainDamageOddsNo - 1) {
82 obj->acceptDamage(obj->thisID(), terrainDamagePerFrame, kDamageSlash, terrainDamageDicePerFrame, 6);
83 }
84 }
85
terrainDamageBash(GameObject * obj)86 void terrainDamageBash(GameObject *obj) {
87 if (g_vm->_rnd->getRandomNumber(terrainDamageOddsYes + terrainDamageOddsNo - 1) > terrainDamageOddsNo - 1) {
88 obj->acceptDamage(obj->thisID(), terrainDamagePerFrame, kDamageImpact, terrainDamageDicePerFrame, 6);
89 }
90 }
91
fallingDamage(GameObject * obj,int16 speed)92 void fallingDamage(GameObject *obj, int16 speed) {
93 if (isActor(obj)) {
94 Actor *a = (Actor *) obj;
95 if (!a->hasEffect(actorSlowFall)) {
96 a->acceptDamage(a->thisID(), (MAX(0, speed - 16)*fallingDamageMult) / fallingDamageDiv);
97 }
98 }
99
100 }
101
102
103
104 /* ===================================================================== *
105 Function to get the terrain bits for a single UV location (with mask)
106 * ===================================================================== */
107
tileTerrain(int16 mapNum,const TilePoint & pt,int16 mask,int16 minZ,int16 maxZ)108 uint32 tileTerrain(
109 int16 mapNum,
110 const TilePoint &pt,
111 int16 mask,
112 int16 minZ,
113 int16 maxZ) {
114 WorldMapData *map = &mapList[mapNum];
115
116 TilePoint metaCoords = pt >> kPlatShift,
117 origin = metaCoords << kPlatShift,
118 coords = pt - origin;
119 MetaTilePtr metaPtr;
120 uint32 terrain = 0;
121
122 // A simple method for avoiding looking up the metatile again.
123 /* if ( prevMeta
124 && prevMapNum == mapNum
125 && prevCoords == metaCoords )
126 metaPtr = prevMeta;
127 else
128 */
129 {
130 // Look up the metatile on the map.
131 metaPtr = prevMeta = map->lookupMeta(metaCoords);
132 prevMapNum = mapNum;
133 prevCoords.set(metaCoords.u, metaCoords.v, metaCoords.z);
134 }
135
136 if (metaPtr == NULL) return 0L;
137
138 for (int i = 0; i < maxPlatforms; i++) {
139 Platform *p;
140
141 if ((p = metaPtr->fetchPlatform(mapNum, i)) == NULL)
142 continue;
143
144 if (p->flags & plVisible) {
145 int16 height;
146 TileInfo *ti;
147 int16 trFlags;
148
149 ti = p->fetchTile(
150 mapNum,
151 coords,
152 origin,
153 height,
154 trFlags);
155
156 if (ti) {
157 int16 tileMinZ = height,
158 tileMaxZ = height;
159 int32 combinedMask = ti->combinedTerrainMask();
160
161 if (combinedMask & terrainRaised)
162 tileMaxZ += ti->attrs.terrainHeight;
163 if (combinedMask & terrainWater)
164 tileMinZ -= ti->attrs.terrainHeight;
165
166 if (tileMinZ < maxZ
167 && tileMaxZ >= minZ) {
168 uint32 terrainResult = 0,
169 tileFgdTerrain = (1 << ti->attrs.fgdTerrain),
170 tileBgdTerrain = (1 << ti->attrs.bgdTerrain);
171
172 // If only checking the top of raised terrain treat it
173 // as if it were normal terrain.
174 if (minZ >= tileMaxZ) {
175 if (tileFgdTerrain & terrainSupportingRaised)
176 tileFgdTerrain = terrainNormal;
177 if (tileBgdTerrain & terrainSupportingRaised)
178 tileBgdTerrain = terrainNormal;
179 }
180
181 // If this tile is sensitive to being walked on,
182 // set the "sensitive" flag.
183 if (trFlags & trTileSensitive)
184 terrainResult |= terrainActive;
185
186 if (mask & ti->attrs.terrainMask)
187 terrainResult |= tileFgdTerrain;
188
189 if (mask & ~ti->attrs.terrainMask)
190 terrainResult |= tileBgdTerrain;
191
192 // This prevents actors from walking through
193 // catwalks and other surfaces which have no bottom.
194
195 if ((terrainResult & terrainSolidSurface)
196 && height > minZ + kMaxStepHeight) {
197 terrainResult |= terrainStone;
198 }
199
200 terrain |= terrainResult;
201 }
202 }
203 }
204 }
205 return terrain;
206 }
207
208 /* ===================================================================== *
209 Function to get the terrain infor for a rectilinear volume
210 * ===================================================================== */
211
212 uint16 uMaxMasks[4] = { 0x0000, 0x000F, 0x00FF, 0x0FFF },
213 uMinMasks[4] = { 0xFFFF, 0xFFF0, 0xFF00, 0xF000 },
214 vMaxMasks[4] = { 0x0000, 0x1111, 0x3333, 0x7777 },
215 vMinMasks[4] = { 0xFFFF, 0xEEEE, 0xCCCC, 0x8888 };
216
volumeTerrain(int16 mapNum,const TileRegion & vol)217 uint32 volumeTerrain(int16 mapNum, const TileRegion &vol) {
218 uint32 terrain = 0; // accumulated terrain
219 TilePoint tilePt;
220 TileRegion footprint,
221 subPos,
222 volume;
223
224 // Convert to subtile coords
225 volume.min.u = vol.min.u >> kSubTileShift;
226 volume.min.v = vol.min.v >> kSubTileShift;
227 volume.max.u = (vol.max.u + kSubTileMask) >> kSubTileShift;
228 volume.max.v = (vol.max.v + kSubTileMask) >> kSubTileShift;
229 volume.min.z = vol.min.z;
230 volume.max.z = vol.max.z;
231
232 // Calculate the footprint of the object (in subtile coords)
233 footprint.min.u = volume.min.u >> kTileSubShift;
234 footprint.min.v = volume.min.v >> kTileSubShift;
235 footprint.max.u = volume.max.u >> kTileSubShift;
236 footprint.max.v = volume.max.v >> kTileSubShift;
237
238 // Calculate which subtiles the region falls upon.
239 subPos.min.u = volume.min.u & kSubTileMask;
240 subPos.min.v = volume.min.v & kSubTileMask;
241 subPos.max.u = volume.max.u & kSubTileMask;
242 subPos.max.v = volume.max.v & kSubTileMask;
243
244 tilePt.z = 0;
245
246 for (tilePt.v = footprint.min.v;
247 tilePt.v <= footprint.max.v;
248 tilePt.v++) {
249 uint16 vSectionMask = 0xFFFF;
250
251 if (tilePt.v == footprint.min.v)
252 vSectionMask &= vMinMasks[subPos.min.v];
253
254 if (tilePt.v == footprint.max.v)
255 vSectionMask &= vMaxMasks[subPos.max.v];
256
257 for (tilePt.u = footprint.min.u;
258 tilePt.u <= footprint.max.u;
259 tilePt.u++) {
260 uint16 uSectionMask = vSectionMask;
261
262 if (tilePt.u == footprint.min.u)
263 uSectionMask &= uMinMasks[subPos.min.u];
264
265 if (tilePt.u == footprint.max.u)
266 uSectionMask &= uMaxMasks[subPos.max.u];
267
268 terrain |= tileTerrain(
269 mapNum,
270 tilePt,
271 uSectionMask,
272 volume.min.z,
273 volume.max.z);
274 }
275 }
276 return terrain;
277 }
278
volumeTerrain(int16 mapNum,const TilePoint & pos,int16 objSection,int16 objHeight)279 uint32 volumeTerrain(
280 int16 mapNum,
281 const TilePoint &pos,
282 int16 objSection,
283 int16 objHeight) {
284 uint32 terrain = 0; // accumulated terrain
285 TileRegion volume;
286
287 // Calculate the volume the object occupies
288 volume.min.u = pos.u - objSection;
289 volume.min.v = pos.v - objSection;
290 volume.max.u = pos.u + objSection;
291 volume.max.v = pos.v + objSection;
292 volume.min.z = pos.z;
293 volume.max.z = pos.z + objHeight;
294
295 terrain = volumeTerrain(mapNum, volume);
296
297 return terrain;
298 }
299
volumeTerrain(int16 mapNum,const TilePoint & pos,int16 uCross,int16 vCross,int16 objHeight)300 uint32 volumeTerrain(
301 int16 mapNum,
302 const TilePoint &pos,
303 int16 uCross,
304 int16 vCross,
305 int16 objHeight) {
306 uint32 terrain = 0; // accumulated terrain
307 TileRegion volume;
308
309 // Calculate the volume the object occupies
310 volume.min.u = pos.u - uCross;
311 volume.min.v = pos.v - vCross;
312 volume.max.u = pos.u + uCross;
313 volume.max.v = pos.v + vCross;
314 volume.min.z = pos.z;
315 volume.max.z = pos.z + objHeight;
316
317 if (debugChannelSet(-1, kDebugTiles)) {
318 TilePoint minUminV(volume.min.u, volume.min.v, volume.min.z);
319 TilePoint maxUminV(volume.max.u, volume.min.v, volume.min.z);
320 TilePoint maxUmaxV(volume.max.u, volume.max.v, volume.min.z);
321 TilePoint minUmaxV(volume.min.u, volume.max.v, volume.min.z);
322
323 TPLine(minUminV, maxUminV, 7);
324 TPLine(maxUminV, maxUmaxV, 7);
325 TPLine(maxUmaxV, minUmaxV, 7);
326 TPLine(minUmaxV, minUminV, 7);
327 }
328
329 terrain = volumeTerrain(mapNum, volume);
330
331 return terrain;
332 }
333
334 /* ===================================================================== *
335 Function to get the terrain info for linear area
336 * ===================================================================== */
337
338 uint16 uMask[4] = { 0x000F, 0x00F0, 0x0F00, 0xF000 },
339 vMask[4] = { 0x1111, 0x2222, 0x4444, 0x8888 };
340
lineTerrain(int16 mapNum,const TilePoint & from,const TilePoint & to,uint32 opaqueTerrain)341 uint32 lineTerrain(
342 int16 mapNum,
343 const TilePoint &from,
344 const TilePoint &to,
345 uint32 opaqueTerrain) {
346 uint32 terrain = 0;
347
348 TilePoint curSubTile,
349 destSubTile,
350 tilePt;
351
352 int8 uStep,
353 vStep;
354
355 uint16 uDiff,
356 vDiff;
357
358 uint16 errorTerm = 0;
359
360 uint16 subTileMask_ = 0;
361
362 int16 tileStartZ,
363 minZ,
364 maxZ;
365
366 int32 curZ,
367 zStep;
368
369 TilePoint prevPoint = from;
370 TilePoint tempPoint;
371
372 // Calculate starting subtile coordinates
373 curSubTile.u = from.u >> kSubTileShift;
374 curSubTile.v = from.v >> kSubTileShift;
375 curSubTile.z = tileStartZ = from.z;
376
377 // Calculate destination subtil coordinates
378 destSubTile.u = to.u >> kSubTileShift;
379 destSubTile.v = to.v >> kSubTileShift;
380 destSubTile.z = to.z;
381
382 tilePt.u = curSubTile.u >> kTileSubShift;
383 tilePt.v = curSubTile.v >> kTileSubShift;
384 tilePt.z = 0;
385
386 if (destSubTile.u > curSubTile.u) {
387 uStep = 1;
388 uDiff = destSubTile.u - curSubTile.u;
389 } else {
390 uStep = -1;
391 uDiff = curSubTile.u - destSubTile.u;
392 }
393
394 if (destSubTile.v > curSubTile.v) {
395 vStep = 1;
396 vDiff = destSubTile.v - curSubTile.v;
397 } else {
398 vStep = -1;
399 vDiff = curSubTile.v - destSubTile.v;
400 }
401
402 if (uDiff == 0 && vDiff == 0) return 0;
403
404 curZ = (int32)curSubTile.z << 16;
405 zStep = ((int32)(destSubTile.z - curSubTile.z) << 16);
406 if (zStep > 0) {
407 minZ = tileStartZ;
408 maxZ = curSubTile.z;
409 } else {
410 minZ = curSubTile.z;
411 maxZ = tileStartZ;
412 }
413
414 if (uDiff > vDiff) {
415 // U difference is greater
416
417 zStep /= uDiff;
418
419 for (;
420 curSubTile.u != destSubTile.u;
421 curSubTile.u += uStep) {
422 curZ += zStep;
423
424 if ((curSubTile.u >> kTileSubShift) != tilePt.u) {
425 curSubTile.z = curZ >> 16;
426
427 terrain |= tileTerrain(
428 mapNum,
429 tilePt,
430 subTileMask_,
431 minZ,
432 maxZ + 1);
433 if (terrain & opaqueTerrain) return terrain;
434
435 tilePt.u = curSubTile.u >> kTileSubShift;
436 tileStartZ = curSubTile.z;
437 subTileMask_ = 0;
438 }
439
440 subTileMask_ |= (uMask[curSubTile.u & kSubTileMask] &
441 vMask[curSubTile.v & kSubTileMask]);
442
443 if (debugChannelSet(-1, kDebugTiles)) {
444 tempPoint.u = curSubTile.u << kTileSubShift;
445 tempPoint.v = curSubTile.v << kTileSubShift;
446 tempPoint.z = curSubTile.z;
447 TPLine(prevPoint, tempPoint);
448 prevPoint = tempPoint;
449 }
450
451 errorTerm += vDiff;
452 if (errorTerm >= uDiff) {
453 errorTerm -= uDiff;
454 curSubTile.v += vStep;
455
456 if ((curSubTile.v >> kTileSubShift) != tilePt.z) {
457 curSubTile.z = curZ >> 16;
458
459 terrain |= tileTerrain(
460 mapNum,
461 tilePt,
462 subTileMask_,
463 minZ,
464 maxZ + 1);
465 if (terrain & opaqueTerrain) return terrain;
466
467 tilePt.v = curSubTile.v >> kTileSubShift;
468 tileStartZ = curSubTile.z;
469 subTileMask_ = 0;
470 }
471
472 subTileMask_ |= (uMask[curSubTile.u & kSubTileMask] &
473 vMask[curSubTile.v & kSubTileMask]);
474
475 if (debugChannelSet(-1, kDebugTiles)) {
476 tempPoint.u = curSubTile.u << kTileSubShift;
477 tempPoint.v = curSubTile.v << kTileSubShift;
478 tempPoint.z = curSubTile.z;
479 TPLine(prevPoint, tempPoint);
480 prevPoint = tempPoint;
481 warning("***************************");
482 }
483 }
484 }
485 } else {
486 // V difference is greater
487
488 zStep /= vDiff;
489
490 for (;
491 curSubTile.v != destSubTile.v;
492 curSubTile.v += vStep) {
493 curZ += zStep;
494
495 if ((curSubTile.v >> kTileSubShift) != tilePt.v) {
496 curSubTile.z = curZ >> 16;
497
498 terrain |= tileTerrain(
499 mapNum,
500 tilePt,
501 subTileMask_,
502 minZ,
503 maxZ + 1);
504 if (terrain & opaqueTerrain) return terrain;
505
506 tilePt.v = curSubTile.v >> kTileSubShift;
507 tileStartZ = curSubTile.z;
508 subTileMask_ = 0;
509 }
510
511 subTileMask_ |= (uMask[curSubTile.u & kSubTileMask] &
512 vMask[curSubTile.v & kSubTileMask]);
513
514 if (debugChannelSet(-1, kDebugTiles)) {
515 tempPoint.u = curSubTile.u << kTileSubShift;
516 tempPoint.v = curSubTile.v << kTileSubShift;
517 tempPoint.z = curSubTile.z;
518 TPLine(prevPoint, tempPoint);
519 prevPoint = tempPoint;
520 }
521
522 errorTerm += uDiff;
523 if (errorTerm >= vDiff) {
524 errorTerm -= vDiff;
525 curSubTile.u += uStep;
526
527 if ((curSubTile.u >> kTileSubShift) != tilePt.u) {
528 curSubTile.z = curZ >> 16;
529
530 terrain |= tileTerrain(
531 mapNum,
532 tilePt,
533 subTileMask_,
534 minZ,
535 maxZ + 1);
536 if (terrain & opaqueTerrain) return terrain;
537
538 tilePt.u = curSubTile.u >> kTileSubShift;
539 tileStartZ = curSubTile.z;
540 subTileMask_ = 0;
541 }
542
543 subTileMask_ |= (uMask[curSubTile.u & kSubTileMask] &
544 vMask[curSubTile.v & kSubTileMask]);
545
546 if (debugChannelSet(-1, kDebugTiles)) {
547 tempPoint.u = curSubTile.u << kTileSubShift;
548 tempPoint.v = curSubTile.v << kTileSubShift;
549 tempPoint.z = curSubTile.z;
550 TPLine(prevPoint, tempPoint);
551 prevPoint = tempPoint;
552 }
553 }
554 }
555 }
556
557 curSubTile.z = curZ >> 16;
558
559 terrain |= tileTerrain(
560 mapNum,
561 tilePt,
562 subTileMask_,
563 minZ,
564 maxZ);
565
566 return terrain;
567 }
568
569
570 /* ===================================================================== *
571 Function to return slope information
572 * ===================================================================== */
573
574 // This function determines the height of the tile's actual
575 // surface, given by the tile slope information. If there are
576 // several tiles within the same space, then it uses the highest
577 // one who's base is below the character's feet. Tiles which
578 // have no supporting surfaces are not considered.
579 //
580 // This routine now also returns a bunch of information about the
581 // tile which forms the surface.
582
tileSlopeHeight(const TilePoint & pt,int16 mapNum,int objectHeight,StandingTileInfo * stiResult,uint8 * platformResult)583 int16 tileSlopeHeight(
584 const TilePoint &pt,
585 int16 mapNum,
586 int objectHeight,
587 StandingTileInfo *stiResult,
588 uint8 *platformResult) {
589 // Calculate coordinates of tile, metatile, and subtile
590 TilePoint tileCoords = pt >> kTileUVShift,
591 metaCoords = tileCoords >> kPlatShift,
592 origin = metaCoords << kPlatShift,
593 coords = tileCoords - origin,
594 subTile((pt.u >> kSubTileShift) & kSubTileMask,
595 (pt.v >> kSubTileShift) & kSubTileMask,
596 0);
597
598 MetaTilePtr metaPtr;
599 StandingTileInfo highestTile, // Represents highest tile which is below
600 // object's base
601 lowestTile; // Represents lowest tile at tile position
602 int16 supportHeight,
603 highestSupportHeight,
604 lowestSupportHeight;
605 uint8 i,
606 highestSupportPlatform = 0,
607 lowestSupportPlatform = 0;
608
609 // Look up the metatile on the map.
610 metaPtr = prevMeta = mapList[mapNum].lookupMeta(metaCoords);
611 prevMapNum = mapNum;
612 prevCoords.set(metaCoords.u, metaCoords.v, metaCoords.z);
613
614 if (metaPtr != NULL) {
615 highestTile.surfaceTile = lowestTile.surfaceTile = NULL;
616 highestSupportHeight = -100;
617 lowestSupportHeight = 0x7FFF;
618
619 // Search each platform until we find a tile which is under
620 // the character.
621
622 for (i = 0; i < maxPlatforms; i++) {
623 Platform *p;
624
625 if ((p = metaPtr->fetchPlatform(mapNum, i)) == NULL)
626 continue;
627
628 if (p->flags & plVisible) {
629 TileInfo *ti;
630 StandingTileInfo sti;
631
632 // Get the tile, and its base height
633 ti = p->fetchTAGInstance(
634 mapNum,
635 coords,
636 origin,
637 sti);
638
639 if (ti) {
640 int16 tileBase = sti.surfaceHeight;
641 int32 subTileTerrain =
642 ti->attrs.testTerrain(calcSubTileMask(subTile.u,
643 subTile.v));
644 if (subTileTerrain & terrainInsubstantial)
645 continue;
646 else if (subTileTerrain & terrainSupportingRaised)
647 // calculate height of raised surface
648 supportHeight = sti.surfaceHeight +
649 ti->attrs.terrainHeight;
650 else if (subTileTerrain & terrainWater) {
651 // calculate depth of water
652 supportHeight = sti.surfaceHeight -
653 ti->attrs.terrainHeight;
654 tileBase = supportHeight;
655 } else
656 // calculate height of unraised surface
657 supportHeight = sti.surfaceHeight +
658 ptHeight(TilePoint(pt.u & kTileUVMask,
659 pt.v & kTileUVMask,
660 0),
661 ti->attrs.cornerHeight);
662
663 // See if the tile is a potential supporting surface
664 if (tileBase < pt.z + objectHeight
665 && supportHeight >= highestSupportHeight
666 && (ti->combinedTerrainMask() &
667 (terrainSurface | terrainRaised))) {
668 highestTile = sti;
669 highestSupportHeight = supportHeight;
670 highestSupportPlatform = i;
671 } else if (highestTile.surfaceTile == NULL &&
672 supportHeight <= lowestSupportHeight &&
673 (ti->combinedTerrainMask() &
674 (terrainSurface | terrainRaised))) {
675 lowestTile = sti;
676 lowestSupportHeight = supportHeight;
677 lowestSupportPlatform = i;
678 }
679 }
680 }
681 }
682
683 if (highestTile.surfaceTile) {
684 if (stiResult) *stiResult = highestTile;
685 if (platformResult) *platformResult = highestSupportPlatform;
686 return highestSupportHeight;
687 }
688 if (lowestTile.surfaceTile) {
689 if (stiResult) *stiResult = lowestTile;
690 if (platformResult) *platformResult = lowestSupportPlatform;
691 return lowestSupportHeight;
692 }
693 }
694
695 if (stiResult) {
696 stiResult->surfaceTile = NULL;
697 stiResult->surfaceTAG = NULL;
698 stiResult->surfaceHeight = 0;
699 }
700 if (platformResult) *platformResult = 0;
701 return 0;
702 }
703
704 // Old-style version of tileSlopeHeight()
tileSlopeHeight(const TilePoint & pt,GameObject * obj,StandingTileInfo * stiResult,uint8 * platformResult)705 int16 tileSlopeHeight(
706 const TilePoint &pt,
707 GameObject *obj,
708 StandingTileInfo *stiResult,
709 uint8 *platformResult) {
710 assert(obj);
711 assert(obj->proto());
712 return tileSlopeHeight(
713 pt,
714 obj->getMapNum(),
715 obj->proto()->height,
716 stiResult,
717 platformResult);
718 }
719
720 // A version of tileSlopeHeight that takes an explicit map number
tileSlopeHeight(const TilePoint & pt,int mapNum,GameObject * obj,StandingTileInfo * stiResult,uint8 * platformResult)721 int16 tileSlopeHeight(
722 const TilePoint &pt,
723 int mapNum,
724 GameObject *obj,
725 StandingTileInfo *stiResult,
726 uint8 *platformResult) {
727 assert(obj);
728 assert(obj->proto());
729 return tileSlopeHeight(
730 pt,
731 mapNum,
732 obj->proto()->height,
733 stiResult,
734 platformResult);
735 }
736
737 /* ===================================================================== *
738 Test functions
739 * ===================================================================== */
740
741 // return terrain that object is currently interacting with
objectTerrain(GameObject * obj,StandingTileInfo & sti)742 uint32 objectTerrain(GameObject *obj, StandingTileInfo &sti) {
743 int16 mapNum = obj->getMapNum();
744 ProtoObj *proto = obj->proto();
745 uint32 terrain;
746 TilePoint loc = obj->getLocation();
747
748 sti.surfaceTAG = NULL;
749
750 terrain = volumeTerrain(mapNum,
751 loc,
752 proto->crossSection,
753 proto->height);
754
755 // If one of the tiles we're standing on is active,
756 // double check to see if we're really standing on it.
757
758 if (terrain & terrainActive) {
759 int16 tHeight;
760
761 // Determine the height of the landscape we're on
762
763 tHeight = tileSlopeHeight(loc, obj, &sti);
764
765 // If the character is indeed standing ON the landscape
766 // REM: This depends on the nature of the tile I think!!!
767
768 if (sti.surfaceTile == NULL
769 || sti.surfaceTAG == NULL
770 || !(sti.surfaceRef.flags & trTileSensitive)
771 || loc.z >= tHeight + 2
772 /* || loc.z >= standingTile->attrs.terrainHeight */) {
773 terrain &= ~terrainActive;
774 }
775 }
776
777 return terrain;
778 }
779
780 // return terrain that object is currently interacting with
checkBlocked(GameObject * obj,int16 mapNum,const TilePoint & loc,GameObject ** blockResultObj)781 int16 checkBlocked(
782 GameObject *obj,
783 int16 mapNum,
784 const TilePoint &loc,
785 GameObject **blockResultObj) {
786 ProtoObj *proto = obj->proto();
787 uint8 height = proto->height;
788 int32 terrain;
789 GameObject *blockObj;
790 GameWorld *world;
791
792 if (blockResultObj) *blockResultObj = NULL;
793
794
795 // check to make sure the actor recognizes terrain
796 if (!isActor(obj) || !((Actor *) obj)->hasEffect(actorNoncorporeal)) {
797 TilePoint testLoc = loc;
798
799 testLoc.z = MAX<int16>(loc.z, 8);
800
801 terrain = volumeTerrain(mapNum,
802 testLoc,
803 proto->crossSection,
804 height);
805
806 // Check for intersection with a wall or obstacle
807 if (terrain & terrainRaised) return blockageTerrain;
808 }
809
810 // See if object collided with an object
811 world = (GameWorld *)GameObject::objectAddress(mapList[mapNum].worldID);
812 blockObj = objectCollision(obj, world, loc);
813 if (blockObj) {
814 if (blockResultObj) *blockResultObj = blockObj;
815 return blockageObject;
816 }
817
818 return blockageNone;
819 }
820
821 // return terrain that object is currently interacting with
checkBlocked(GameObject * obj,const TilePoint & loc,GameObject ** blockResultObj)822 int16 checkBlocked(
823 GameObject *obj,
824 const TilePoint &loc,
825 GameObject **blockResultObj) {
826 return checkBlocked(obj, obj->getMapNum(), loc, blockResultObj);
827 }
828
829 // same as checkBlocked() above but includes additional "walking off
830 // cliff" check
checkWalkable(GameObject * obj,const TilePoint & loc,GameObject ** blockResultObj)831 int16 checkWalkable(
832 GameObject *obj,
833 const TilePoint &loc,
834 GameObject **blockResultObj) {
835 int16 result;
836 int16 supportHeight;
837 StandingTileInfo sti;
838
839 if ((result = checkBlocked(obj, loc, blockResultObj)) != blockageNone)
840 return result;
841
842 supportHeight = tileSlopeHeight(loc, obj, &sti);
843
844 if (supportHeight < loc.z - kMaxStepHeight * 4)
845 return blockageTerrain;
846
847 if (sti.surfaceTile != NULL) {
848 int16 subTileU,
849 subTileV,
850 mask;
851
852 subTileU = (loc.u & kTileUVMask) >> kSubTileShift;
853 subTileV = (loc.v & kTileUVMask) >> kSubTileShift;
854 mask = 1 << ((subTileU << kSubTileShift) + subTileV);
855
856 // If the suporting subtile is funiture consider this blocked
857 if (sti.surfaceTile->attrs.testTerrain(mask) & terrainFurniture)
858 return blockageTerrain;
859 }
860
861 return blockageNone;
862 }
863
864 // return terrain that object is currently interacting with
checkContact(GameObject * obj,const TilePoint & loc,GameObject ** blockResultObj)865 int16 checkContact(
866 GameObject *obj,
867 const TilePoint &loc,
868 GameObject **blockResultObj) {
869 int16 mapNum = obj->getMapNum();
870 ProtoObj *proto = obj->proto();
871 int32 terrain;
872 GameObject *blockObj;
873 GameWorld *world;
874
875 if (blockResultObj) *blockResultObj = NULL;
876
877 terrain = volumeTerrain(mapNum,
878 loc,
879 proto->crossSection,
880 proto->height);
881
882 // Check for intersection with a wall or obstacle
883 if (terrain & terrainRaised) return blockageTerrain;
884
885 // Check for intersection with slope of the terrain.
886 if (((terrain & terrainSurface)
887 && loc.z <= tileSlopeHeight(loc, obj))
888 || (!(terrain & terrainWater)
889 && loc.z <= 0))
890 return blockageTerrain;
891
892 // See if object collided with an object
893 world = (GameWorld *)GameObject::objectAddress(mapList[mapNum].worldID);
894 blockObj = objectCollision(obj, world, loc);
895 if (blockObj) {
896 if (blockResultObj) *blockResultObj = blockObj;
897 return blockageObject;
898 }
899
900 return blockageNone;
901 }
902
903 } // end of namespace Saga2
904