Pol  Revision:794
pol/los.cpp
Go to the documentation of this file.
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines