|
Pol
Revision:794
|
00001 /* 00002 History 00003 ======= 00004 00005 00006 Notes 00007 ======= 00008 00009 */ 00010 00011 #include "../clib/stl_inc.h" 00012 00013 #ifdef _MSC_VER 00014 # pragma warning( disable: 4786 ) 00015 #endif 00016 00017 00018 #include <stdlib.h> 00019 00020 #include "../clib/strutil.h" 00021 00022 #include "item/item.h" 00023 #include "polcfg.h" 00024 #include "uobject.h" 00025 #include "uvars.h" 00026 #include "ustruct.h" 00027 #include "uofile.h" 00028 #include "udatfile.h" 00029 #include "uworld.h" 00030 #include "los.h" 00031 00032 #define USE_2D_LOS 0 00033 #define USE_3D_LOS 1 00034 namespace Pol { 00035 namespace Core { 00036 const int los_range = 20; 00037 const int z_los_range = 60; // unused as yet 00038 00039 /* 00040 To determine LOS, a 3D line is drawn from the top of the attacker 00041 to the top of the target. 00042 00043 An 3D extension of Bresenham's algorithm is used to draw the line. 00044 00045 Each point along the way is checked for items and statics of any kind. 00046 Only integer addition and subtraction is required. 00047 00048 The line always starts at the point with the lowest Y-coordinate, 00049 or if the Y coordinates are the same, the point with the lowest 00050 Z-coordinate. This way, if A -> B has LOS, then B -> A has LOS, 00051 always. (Bresenham lines can occupy different points, given 00052 the same endpoints, depending on which point is used as the start) 00053 00054 Zero-height items (floor tiles, mostly) are treated as 1-height 00055 items, sitting at 1 lower Z coordinate. This makes floors solid. 00056 (A weirdness: On a floor at height 72, characters walk around at Z=73, 00057 but items are placed at Z=72. This is somewhat unfortunate.) 00058 00059 Ghosts standing in the same place as a door should be handled 00060 correctly, because the start and end points are checked for 00061 obstacles. I'm not sure how windows will fit in the picture. 00062 Indeed, there are probably flags in statics that I should pay 00063 attention to (floor, blocking, window, stairway) but I don't yet. 00064 00065 Note, all obstacles found to obstruct the LOS are checked 00066 to make sure they are neither the attacker nor the target, 00067 since both endpoints are checked. This is actually unnecessary 00068 when both attacker and defender are characters, which ends up 00069 being all the time when it matters. 00070 00071 Possible optimizations/improvements: 00072 Read statics file once per X/Y change, rather than each X/Y/Z change 00073 */ 00074 00075 /**************************** 3D LOS CHECKING *******************************/ 00076 00077 #if USE_3D_LOS 00078 00079 00080 bool dynamic_item_blocks_los( const LosObj& att, const LosObj& target, 00081 int x, int y, int z, Realm* realm ) 00082 { 00083 unsigned short wx, wy; 00084 w_convert( x, y, wx, wy ); 00085 00086 ZoneItems& witems = realm->zone[wx][wy].items; 00087 00088 for ( ZoneItems::iterator itr = witems.begin(), end = witems.end(); itr != end; ++itr ) 00089 { 00090 Item* item = *itr; 00091 if ( ( item->x == x ) && 00092 ( item->y == y ) && 00093 ( tile_flags( item->graphic ) & USTRUCT_TILE::FLAG_WALKBLOCK ) ) 00094 { 00095 if ( z >= item->z && z < item->z + item->height ) 00096 { 00097 // NOTE, we don't break with an early 'false' indicator 00098 // if the serials DO match, because ghosts can be in the 00099 // same place as a door, and the line can start at either 00100 // the attacker or the target. 00101 00102 if ( item->serial != target.serial && item->serial != att.serial ) 00103 { 00104 #if ENABLE_POLTEST_OUTPUT 00105 INFO_PRINT << "LOS blocked by " << item->description( ) << "\n"; 00106 #endif 00107 return true; 00108 } 00109 } 00110 } 00111 } 00112 return false; 00113 } 00114 00115 bool static_item_blocks_los( int x, int y, int z ) 00116 { 00117 typedef vector<StaticRec> StaticRecVector; 00118 static StaticRecVector vec; 00119 vec.clear(); 00120 readstatics( vec, x, y ); 00121 readmultis( vec, x, y ); 00122 for ( StaticRecVector::const_iterator itr = vec.begin(); 00123 itr != vec.end(); 00124 ++itr ) 00125 { 00126 int ob_ht = tileheight( itr->graphic ); 00127 int ob_z = itr->z; 00128 #if ENABLE_POLTEST_OUTPUT 00129 INFO_PRINT << "static type 0x" << fmt::hexu( itr->graphic ) 00130 << " (flags 0x" << fmt::hexu( tile_flags( itr->graphic ) ) << ", ht=" << ob_ht << ")" 00131 << " at z-coord " << (int)itr->z << "\n"; 00132 #endif 00133 00134 if ( ob_ht == 0 ) 00135 { 00136 --ob_z; 00137 ++ob_ht; 00138 } 00139 00140 00141 00142 if ( z >= ob_z && z < ob_z + ob_ht ) 00143 { 00144 #if ENABLE_POLTEST_OUTPUT 00145 INFO_PRINT << "LOS blocked by static object\n"; 00146 #endif 00147 return true; 00148 } 00149 } 00150 return false; 00151 } 00152 00153 // Is any object occupying point (x,y,z) that is neither attacker nor target? 00154 bool los_blocked( const LosObj& att, const LosObj& target, 00155 int x, int y, int z ) 00156 { 00157 // if the target inhabits the location, LOS can't be blocked: 00158 if ( att.x == x && 00159 att.y == y && 00160 att.z <= z && 00161 z < att.z + att.obj_height ) 00162 { 00163 return false; 00164 } 00165 if ( target.x == x && 00166 target.y == y && 00167 target.z <= z && 00168 z < target.z + target.obj_height ) 00169 { 00170 return false; 00171 } 00172 00173 if ( config.exp_los_checks_map ) 00174 { 00175 USTRUCT_MAPINFO mi; 00176 rawmapinfo( x, y, &mi ); 00177 if ( ( landtile_flags[mi.landtile] & 00178 ( USTRUCT_TILE::FLAG_BLOCKING | USTRUCT_TILE::FLAG_WALL ) ) 00179 == ( USTRUCT_TILE::FLAG_BLOCKING | USTRUCT_TILE::FLAG_WALL ) ) 00180 return true; 00181 } 00182 00183 return dynamic_item_blocks_los( att, target, x, y, z ) || 00184 static_item_blocks_los( x, y, z ); 00185 } 00186 00187 // absolute value of a 00188 #define ABS(a) (((a)<0) ? -(a) : (a)) 00189 00190 // take sign of a, either -1, 0, or 1 00191 #define ZSGN(a) (((a)<0) ? -1 : (a)>0 ? 1 : 0) 00192 00193 bool uo_has_los( const LosObj& att, const LosObj& tgt ) 00194 { 00195 int x1, y1, z1; // one of the endpoints 00196 int x2, y2, z2; // the other endpoint 00197 int xd, yd, zd; 00198 int x, y, z; 00199 int ax, ay, az; 00200 int sx, sy, sz; 00201 int dx, dy, dz; 00202 00203 if ( ( att.y < tgt.y ) || 00204 ( att.y == tgt.y && att.z < tgt.z ) ) 00205 { 00206 x1 = att.x; y1 = att.y; z1 = att.z + att.look_height; 00207 x2 = tgt.x; y2 = tgt.y; z2 = tgt.z + tgt.look_height; 00208 } 00209 else 00210 { 00211 x1 = tgt.x; y1 = tgt.y; z1 = tgt.z + tgt.look_height; 00212 x2 = att.x; y2 = att.y; z2 = att.z + att.look_height; 00213 } 00214 00215 dx = x2 - x1; 00216 dy = y2 - y1; 00217 dz = z2 - z1; 00218 00219 if ( abs( dx ) > los_range || abs( dy ) > los_range ) 00220 return false; 00221 00222 if ( !dx && !dy ) 00223 { 00224 if ( !dz ) 00225 return true; 00226 if ( att.z <= tgt.z && 00227 tgt.z <= att.z + att.obj_height ) 00228 { 00229 return true; 00230 } 00231 if ( att.z <= tgt.z + tgt.look_height && 00232 tgt.z + tgt.look_height <= att.z + att.obj_height ) 00233 { 00234 return true; 00235 } 00236 } 00237 00238 ax = ABS( dx ) << 1; 00239 ay = ABS( dy ) << 1; 00240 az = ABS( dz ) << 1; 00241 00242 sx = ZSGN( dx ); 00243 sy = ZSGN( dy ); 00244 sz = ZSGN( dz ); 00245 00246 x = x1; 00247 y = y1; 00248 z = z1; 00249 00250 if ( ax >= ay && ax >= az ) // x dominant 00251 { 00252 yd = ay - ( ax >> 1 ); 00253 zd = az - ( ax >> 1 ); 00254 00255 for ( ;; ) 00256 { 00257 if ( los_blocked( att, tgt, x, y, z ) ) 00258 return false; 00259 00260 if ( x == x2 ) 00261 { 00262 return true; 00263 } 00264 00265 if ( yd >= 0 ) 00266 { 00267 y += sy; 00268 yd -= ax; 00269 } 00270 00271 if ( zd >= 0 ) 00272 { 00273 z += sz; 00274 zd -= ax; 00275 } 00276 00277 x += sx; 00278 yd += ay; 00279 zd += az; 00280 00281 } 00282 } 00283 else if ( ay >= ax && ay >= az ) //y dominant 00284 { 00285 xd = ax - ( ay >> 1 ); 00286 zd = az - ( ay >> 1 ); 00287 00288 for ( ;; ) 00289 { 00290 if ( los_blocked( att, tgt, x, y, z ) ) 00291 return false; 00292 00293 if ( y == y2 ) 00294 { 00295 return true; 00296 } 00297 00298 if ( xd >= 0 ) 00299 { 00300 x += sx; 00301 xd -= ay; 00302 } 00303 00304 if ( zd >= 0 ) 00305 { 00306 z += sz; 00307 zd -= ay; 00308 } 00309 00310 y += sy; 00311 xd += ax; 00312 zd += az; 00313 00314 } 00315 } 00316 else // z dominant 00317 { 00318 xd = ax - ( az >> 1 ); 00319 yd = ay - ( az >> 1 ); 00320 00321 for ( ;; ) 00322 { 00323 if ( los_blocked( att, tgt, x, y, z ) ) 00324 return false; 00325 00326 if ( z == z2 ) 00327 { 00328 return true; 00329 } 00330 00331 if ( xd >= 0 ) 00332 { 00333 x += sx; 00334 xd -= az; 00335 } 00336 00337 if ( yd >= 0 ) 00338 { 00339 y += sy; 00340 yd -= az; 00341 } 00342 00343 z += sz; 00344 xd += ax; 00345 yd += ay; 00346 00347 } 00348 } 00349 return true; 00350 } 00351 00352 00353 #endif 00354 00355 /****************************************************************************/ 00356 00357 /* Original 3D bresenham algorithm from comp.unix.sources 00358 00359 // 00360 // line3d was dervied from DigitalLine.c published as "Digital Line Drawing" 00361 // by Paul Heckbert from "Graphics Gems", Academic Press, 1990 00362 // 00363 // 3D modifications by Bob Pendleton. The original source code was in the public 00364 // domain, the author of the 3D version places his modifications in the 00365 // public domain as well. 00366 // 00367 // line3d uses Bresenham's algorithm to generate the 3 dimensional points on a 00368 // line from (x1, y1, z1) to (x2, y2, z2) 00369 // 00370 // 00371 00372 // find maximum of a and b 00373 #define MAX(a,b) (((a)>(b))?(a):(b)) 00374 00375 // absolute value of a 00376 #define ABS(a) (((a)<0) ? -(a) : (a)) 00377 00378 // take sign of a, either -1, 0, or 1 00379 #define ZSGN(a) (((a)<0) ? -1 : (a)>0 ? 1 : 0) 00380 00381 point3d(x, y, z) 00382 int x, y, z; 00383 { 00384 00385 //output the point as you see fit 00386 00387 } 00388 00389 line3d(x1, y1, x2, y2, z1, z2) 00390 int x1, y1, x2, y2, z1, z2; 00391 { 00392 int xd, yd, zd; 00393 int x, y, z; 00394 int ax, ay, az; 00395 int sx, sy, sz; 00396 int dx, dy, dz; 00397 00398 dx = x2 - x1; 00399 dy = y2 - y1; 00400 dz = z2 - z1; 00401 00402 ax = ABS(dx) << 1; 00403 ay = ABS(dy) << 1; 00404 az = ABS(dz) << 1; 00405 00406 sx = ZSGN(dx); 00407 sy = ZSGN(dy); 00408 sz = ZSGN(dz); 00409 00410 x = x1; 00411 y = y1; 00412 z = z1; 00413 00414 if (ax >= MAX(ay, az)) // x dominant 00415 { 00416 yd = ay - (ax >> 1); 00417 zd = az - (ax >> 1); 00418 for (;;) 00419 { 00420 point3d(x, y, z); 00421 if (x == x2) 00422 { 00423 return; 00424 } 00425 00426 if (yd >= 0) 00427 { 00428 y += sy; 00429 yd -= ax; 00430 } 00431 00432 if (zd >= 0) 00433 { 00434 z += sz; 00435 zd -= ax; 00436 } 00437 00438 x += sx; 00439 yd += ay; 00440 zd += az; 00441 } 00442 } 00443 else if (ay >= MAX(ax, az)) //y dominant 00444 { 00445 xd = ax - (ay >> 1); 00446 zd = az - (ay >> 1); 00447 for (;;) 00448 { 00449 point3d(x, y, z); 00450 if (y == y2) 00451 { 00452 return; 00453 } 00454 00455 if (xd >= 0) 00456 { 00457 x += sx; 00458 xd -= ay; 00459 } 00460 00461 if (zd >= 0) 00462 { 00463 z += sz; 00464 zd -= ay; 00465 } 00466 00467 y += sy; 00468 xd += ax; 00469 zd += az; 00470 } 00471 } 00472 else if (az >= MAX(ax, ay)) // z dominant 00473 { 00474 xd = ax - (az >> 1); 00475 yd = ay - (az >> 1); 00476 for (;;) 00477 { 00478 point3d(x, y, z); 00479 if (z == z2) 00480 { 00481 return; 00482 } 00483 00484 if (xd >= 0) 00485 { 00486 x += sx; 00487 xd -= az; 00488 } 00489 00490 if (yd >= 0) 00491 { 00492 y += sy; 00493 yd -= az; 00494 } 00495 00496 z += sz; 00497 xd += ax; 00498 yd += ay; 00499 } 00500 } 00501 } 00502 00503 */ 00504 00505 } 00506 }