chunks.cc

Go to the documentation of this file.
00001 
00007 /*
00008 Copyright (C) 2000 The Exult Team
00009 
00010 This program is free software; you can redistribute it and/or
00011 modify it under the terms of the GNU General Public License
00012 as published by the Free Software Foundation; either version 2
00013 of the License, or (at your option) any later version.
00014 
00015 This program is distributed in the hope that it will be useful,
00016 but WITHOUT ANY WARRANTY; without even the implied warranty of
00017 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018 GNU General Public License for more details.
00019 
00020 You should have received a copy of the GNU General Public License
00021 along with this program; if not, write to the Free Software
00022 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00023 */
00024 
00025 #ifdef HAVE_CONFIG_H
00026 #  include <config.h>
00027 #endif
00028 
00029 #include "chunks.h"
00030 #include "chunkter.h"
00031 #include "gamewin.h"
00032 #include "gamemap.h"
00033 #include "shapeinf.h"
00034 #include "citerate.h"
00035 #include "egg.h"
00036 #include "objiter.h"
00037 #include "objs.h"
00038 #include "ordinfo.h"
00039 #include "game.h"
00040 #include "animate.h"
00041 #include "dir.h"
00042 #include "actors.h"
00043 
00044 #if 0
00045 #include <iostream>
00046 using std::cout;
00047 using std::endl;
00048 #endif
00049 
00050 #ifndef UNDER_CE
00051 using std::memset;
00052 using std::rand;
00053 #endif
00054 
00055 /*
00056  *  Create the cached data storage for a chunk.
00057  */
00058 
00059 Chunk_cache::Chunk_cache
00060   (
00061   ) : egg_objects(4)
00062   {
00063   memset((char *) &blocked[0], 0, sizeof(blocked));
00064   memset((char *) &eggs[0], 0, sizeof(eggs));
00065   }
00066 
00067 /*
00068  *  Delete cache.
00069  */
00070 
00071 Chunk_cache::~Chunk_cache
00072   (
00073   )
00074   {
00075   }
00076 
00077 /*
00078  *  This mask gives the low bits (b0) for a given # of ztiles.
00079  */
00080 unsigned long tmasks[16] = {    0x0L,
00081                 0x1L,
00082                 0x5L,
00083                0x15L,
00084                0x55L,
00085               0x155L,
00086               0x555L,
00087              0x1555L,
00088              0x5555L,
00089             0x15555L,
00090             0x55555L,
00091            0x155555L,
00092            0x555555L,
00093           0x1555555L,
00094           0x5555555L,
00095          0x15555555L
00096       };
00097 
00098 /*
00099  *  Set (actually, increment count) for a given tile.
00100  *  Want: 00 => 01, 01 => 10,
00101  *    10 => 11, 11 => 11.
00102  *  So: newb0 = !b0 OR b1,
00103  *    newb1 =  b1 OR b0
00104  */
00105 inline void Set_blocked_tile
00106   (
00107   unsigned long *blocked,   // 16x16 flags,
00108   int tx, int ty,     // Tile #'s (0-15).
00109   int lift,     // Starting lift to set.
00110   int ztiles      // # tiles along z-axis.
00111   )
00112   {
00113   unsigned long& val = blocked[ty*c_tiles_per_chunk + tx];
00114           // Get mask for the bit0's:
00115   unsigned long mask0 = tmasks[ztiles]<<2*lift;
00116   unsigned long mask1 = mask0<<1; // Mask for the bit1's.
00117   unsigned long val0s = val&mask0;
00118   unsigned long Nval0s = (~val)&mask0;
00119   unsigned long val1s = val&mask1;
00120   unsigned long newval = val1s | (val0s<<1) | Nval0s | (val1s>>1);
00121           // Replace old values with new.
00122   val = (val&~(mask0|mask1)) | newval;
00123   }
00124 
00125 /*
00126  *  Clear (actually, decrement count) for a given tile.
00127  *  Want: 00 => 00, 01 => 00,
00128  *    10 => 01, 11 => 10.
00129  *  So: newb0 =  b1 AND !b0
00130  *    newb1 =  b1 AND  b0
00131  */
00132 inline void Clear_blocked_tile
00133   (
00134   unsigned long *blocked,   // 16x16 flags,
00135   int tx, int ty,     // Tile #'s (0-15).
00136   int lift,     // Starting lift to set.
00137   int ztiles      // # tiles along z-axis.
00138   )
00139   {
00140   unsigned long& val = blocked[ty*c_tiles_per_chunk + tx];
00141           // Get mask for the bit0's:
00142   unsigned long mask0 = tmasks[ztiles]<<2*lift;
00143   unsigned long mask1 = mask0<<1; // Mask for the bit1's.
00144   unsigned long val0s = val&mask0;
00145   unsigned long Nval0s = (~val)&mask0;
00146   unsigned long val1s = val&mask1;
00147   unsigned long newval = (val1s & (val0s<<1)) | ((val1s>>1) & Nval0s);
00148           // Replace old values with new.
00149   val = (val&~(mask0|mask1)) | newval;
00150   }
00151 
00152 /*
00153  *  Set/unset the blocked flags in a region.
00154  */
00155 
00156 void Chunk_cache::set_blocked
00157   (
00158   int startx, int starty,   // Starting tile #'s.
00159   int endx, int endy,   // Ending tile #'s.
00160   int lift, int ztiles,   // Lift, height info.
00161   bool set        // 1 to add, 0 to remove.
00162   )
00163   {
00164   if (set)
00165     {
00166     for (int y = starty; y <= endy; y++)
00167       for (int x = startx; x <= endx; x++)
00168         Set_blocked_tile(blocked, x, y, lift, ztiles);
00169     }
00170   else
00171     {
00172     for (int y = starty; y <= endy; y++)
00173       for (int x = startx; x <= endx; x++)
00174         Clear_blocked_tile(blocked,x, y, lift, ztiles);
00175     }
00176   }
00177 
00178 /*
00179  *  Add/remove an object to/from the cache.
00180  */
00181 
00182 void Chunk_cache::update_object
00183   (
00184   Map_chunk *chunk,
00185   Game_object *obj,
00186   bool add        // 1 to add, 0 to remove.
00187   )
00188   {
00189   Shape_info& info = obj->get_info();
00190   if (info.is_door())   // Special door list.
00191     if (add)
00192       doors.append(obj);
00193     else
00194       doors.remove(obj);
00195   int ztiles = info.get_3d_height(); 
00196   if (!ztiles || !info.is_solid())
00197     return;     // Skip if not an obstacle.
00198           // Get lower-right corner of obj.
00199   int endx = obj->get_tx();
00200   int endy = obj->get_ty();
00201   int frame = obj->get_framenum();// Get footprint dimensions.
00202   int xtiles = info.get_3d_xtiles(frame);
00203   int ytiles = info.get_3d_ytiles(frame);
00204   int lift = obj->get_lift();
00205   if (xtiles == 1 && ytiles == 1) // Simplest case?
00206     {
00207     if (add)
00208       Set_blocked_tile(blocked, endx, endy, lift, ztiles);
00209     else
00210       Clear_blocked_tile(blocked, endx, endy, lift, ztiles);
00211     return;
00212     }
00213   Rectangle footprint = obj->get_footprint();
00214           // Go through interesected chunks.
00215   Chunk_intersect_iterator next_chunk(footprint);
00216   Rectangle tiles;
00217   int cx, cy;
00218   while (next_chunk.get_next(tiles, cx, cy))
00219     gmap->get_chunk(cx, cy)->set_blocked(tiles.x, tiles.y, 
00220       tiles.x + tiles.w - 1, tiles.y + tiles.h - 1, lift,
00221                 ztiles, add);
00222   }
00223 
00224 /*
00225  *  Set a rectangle of tiles within this chunk to be under the influence
00226  *  of a given egg, or clear it.
00227  */
00228 
00229 void Chunk_cache::set_egged
00230   (
00231   Egg_object *egg,
00232   Rectangle& tiles,   // Range of tiles within chunk.
00233   bool add        // 1 to add, 0 to remove.
00234   )
00235   {
00236           // Egg already there?
00237   int eggnum = egg_objects.find(egg);
00238   if (add)
00239     {
00240     if (eggnum < 0)   // No, so add it.
00241       eggnum = egg_objects.put(egg);
00242     if (eggnum > 15)  // We only have 16 bits.
00243       eggnum = 15;
00244     short mask = (1<<eggnum);
00245     int stopx = tiles.x + tiles.w, stopy = tiles.y + tiles.h;
00246     for (int ty = tiles.y; ty < stopy; ++ty)
00247       for (int tx = tiles.x; tx < stopx; ++tx)
00248         eggs[ty*c_tiles_per_chunk + tx] |= mask;
00249     }
00250   else        // Remove.
00251     {
00252     if (eggnum < 0 || eggnum >= egg_objects.size())
00253       return;   // Not there.
00254     egg_objects[eggnum] = NULL;
00255     if (eggnum >= 15) // We only have 16 bits.
00256       {   // Last one at 15 or above?
00257       for (Egg_vector::const_iterator it = 
00258         egg_objects.begin() + 15; 
00259           it != egg_objects.end(); ++it)
00260         if (*it != 0)
00261           // No, so leave bits alone.
00262           return;
00263       eggnum = 15;
00264       }
00265     short mask = ~(1<<eggnum);
00266     int stopx = tiles.x + tiles.w, stopy = tiles.y + tiles.h;
00267     for (int ty = tiles.y; ty < stopy; ty++)
00268       for (int tx = tiles.x; tx < stopx; tx++)
00269         eggs[ty*c_tiles_per_chunk + tx] &= mask;
00270     }
00271   }
00272 
00273 /*
00274  *  Add/remove an egg to the cache.
00275  */
00276 
00277 void Chunk_cache::update_egg
00278   (
00279   Map_chunk *chunk,
00280   Egg_object *egg,
00281   bool add        // 1 to add, 0 to remove.
00282   )
00283   {
00284           // Get footprint with abs. tiles.
00285   Rectangle foot = egg->get_area();
00286   if (!foot.w)
00287     return;     // Empty (probability = 0).
00288   Rectangle crect;    // Gets tiles within each chunk.
00289   int cx, cy;
00290   if (egg->is_solid_area())
00291     {     // Do solid rectangle.
00292     Chunk_intersect_iterator all(foot);
00293     while (all.get_next(crect, cx, cy))
00294       gmap->get_chunk(cx, cy)->set_egged(egg, crect, add);
00295     return;
00296     }
00297           // Just do the perimeter.
00298   Rectangle top(foot.x, foot.y, foot.w, 1);
00299   Rectangle bottom(foot.x, foot.y + foot.h - 1, foot.w, 1);
00300   Rectangle left(foot.x, foot.y + 1, 1, foot.h - 2);
00301   Rectangle right(foot.x + foot.w - 1, foot.y + 1, 1, foot.h - 2);
00302           // Go through intersected chunks.
00303   Chunk_intersect_iterator tops(top);
00304   while (tops.get_next(crect, cx, cy))
00305     gmap->get_chunk(cx, cy)->set_egged(egg, crect, add);
00306   Chunk_intersect_iterator bottoms(bottom);
00307   while (bottoms.get_next(crect, cx, cy))
00308     gmap->get_chunk(cx, cy)->set_egged(egg, crect, add);
00309   Chunk_intersect_iterator lefts(left);
00310   while (lefts.get_next(crect, cx, cy))
00311     gmap->get_chunk(cx, cy)->set_egged(egg, crect, add);
00312   Chunk_intersect_iterator rights(right);
00313   while (rights.get_next(crect, cx, cy))
00314     gmap->get_chunk(cx, cy)->set_egged(egg, crect, add);
00315 
00316   }
00317 
00318 /*
00319  *  Create the cached data for a chunk.
00320  */
00321 
00322 void Chunk_cache::setup
00323   (
00324   Map_chunk *chunk
00325   )
00326   {
00327   Game_object *obj;   // Set 'blocked' tiles.
00328   Object_iterator next(chunk->get_objects());
00329   while ((obj = next.get_next()) != 0)
00330     if (obj->is_egg())
00331       update_egg(chunk, (Egg_object *) obj, 1);
00332     else
00333       update_object(chunk, obj, 1);
00334       
00335   obj_list = chunk;
00336   }
00337 
00338 /*
00339  *  Get highest blocked lift below a given level for a given tile.
00340  *
00341  *  Output: Highest lift that's blocked by an object, or -1 if none.
00342  */
00343 
00344 inline int Chunk_cache::get_highest_blocked
00345   (
00346   int lift,     // Look below this lift.
00347   unsigned long tflags    // Flags for desired tile.
00348   )
00349   {
00350   int i;        // Look downwards.
00351   for (i = lift - 1; i >= 0 && !(tflags & (3<<(2*i))); i--)
00352     ;
00353   return i;
00354   }
00355 
00356 /*
00357  *  Get highest blocked lift below a given level for a given tile.
00358  *
00359  *  Output: Highest lift that's blocked by an object, or -1 if none.
00360  */
00361 
00362 int Chunk_cache::get_highest_blocked
00363   (
00364   int lift,     // Look below this lift.
00365   int tx, int ty      // Square to test.
00366   )
00367   {
00368   return get_highest_blocked(lift, blocked[ty*c_tiles_per_chunk + tx]);
00369   }
00370 
00371 /*
00372  *  Get highest blocked lift below a given level for a given tile.
00373  *
00374  *  Output: Highest lift that's blocked by an object, or -1 if none.
00375  */
00376 
00377 inline int Chunk_cache::get_lowest_blocked
00378   (
00379   int lift,     // Look above this lift.
00380   unsigned long tflags    // Flags for desired tile.
00381   )
00382   {
00383   int i;        // Look upward.
00384   for (i = lift; i < 16 && !(tflags & (3<<(2*i))); i++)
00385     ;
00386   if (i == 16) return -1;
00387   return i;
00388   }
00389 
00390 /*
00391  *  Get lowest blocked lift above a given level for a given tile.
00392  *
00393  *  Output: Lowest lift that's blocked by an object, or -1 if none.
00394  */
00395 
00396 int Chunk_cache::get_lowest_blocked
00397   (
00398   int lift,     // Look below this lift.
00399   int tx, int ty      // Square to test.
00400   )
00401   {
00402   return get_lowest_blocked(lift, blocked[ty*c_tiles_per_chunk + tx]);
00403   }
00404 
00405 /*
00406  *  See if a tile is water or land.
00407  */
00408 
00409 inline void Check_terrain
00410   (
00411   Map_chunk *nlist, // Chunk.
00412   int tx, int ty,     // Tile within chunk.
00413   int& terrain      // Sets: bit0 if land, bit1 if water,
00414           //   bit2 if solid.
00415   )
00416   {
00417   ShapeID flat = nlist->get_flat(tx, ty);
00418   if (!flat.is_invalid())
00419     {
00420     if (flat.get_info().is_water())
00421       terrain |= 2;
00422     else if (flat.get_info().is_solid())
00423       terrain |= 4;
00424     else
00425       terrain |= 1;
00426     }
00427 
00428   }
00429 
00430 /*
00431  *  Is a given square occupied at a given lift?
00432  *
00433  *  Output: 1 if so, else 0.
00434  *    If 0 (tile is free), new_lift contains the new height that
00435  *       an actor will be at if he walks onto the tile.
00436  */
00437 
00438 int Chunk_cache::is_blocked
00439   (
00440   int height,     // Height (in tiles) of obj. being
00441           //   tested.
00442   int lift,     // Given lift.
00443   int tx, int ty,     // Square to test.
00444   int& new_lift,      // New lift returned.
00445   const int move_flags,
00446   int max_drop,     // Max. drop/rise allowed.
00447   int max_rise      // Max. rise, or -1 to use old beha-
00448           //   viour (max_drop if FLY, else 1).
00449   )
00450 {
00451 
00452   // Ethereal beings always return not blocked
00453   // and can only move horizontally
00454   if (move_flags & MOVE_ETHEREAL)
00455   {
00456     new_lift = lift;
00457     return 0;
00458   }
00459           // Get bits.
00460   unsigned long tflags = blocked[ty*c_tiles_per_chunk + tx];
00461           // Figure max lift allowed.
00462   if (max_rise == -1)
00463     max_rise = (move_flags & MOVE_FLY) ? max_drop : 1;
00464   int max_lift = lift + max_rise;
00465   if (max_lift > 15)
00466     max_lift = 15;    // As high as we can go.
00467   for (new_lift = lift; new_lift <= max_lift; new_lift++)
00468     {
00469     if ((tflags & (3 << (2*new_lift))) == 0)
00470       {   // Not blocked?
00471       int new_high = get_lowest_blocked(new_lift, tflags);
00472           // Not blocked above?
00473       if (new_high == -1 || new_high >= (new_lift + height))
00474         break;  // Okay.
00475       }
00476     }
00477   if (new_lift > max_lift)  // Spot not found at lift or higher?
00478     {     // Look downwards.
00479     new_lift = get_highest_blocked(lift, tflags) + 1;
00480     if (new_lift >= lift) // Couldn't drop?
00481       return 1;
00482     int new_high = get_lowest_blocked(new_lift, tflags);
00483     if (new_high != -1 && new_high < new_lift + height)
00484       return 1; // Still blocked above.
00485     }
00486   if (new_lift <= lift)   // Not going up?  See if falling.
00487     {
00488     new_lift =  (move_flags & MOVE_NODROP) ? lift :
00489         get_highest_blocked(lift, tflags) + 1;
00490           // Don't allow fall of > max_drop.
00491     if (lift - new_lift > max_drop)
00492       {   // Map-editing?  Suspend in air there.
00493       if (move_flags & MOVE_MAPEDIT)
00494         new_lift = lift - max_drop;
00495       else
00496         return 1;
00497       }
00498     int new_high = get_lowest_blocked (new_lift, tflags);
00499   
00500     // Make sure that where we want to go is tall enough for us
00501     if (new_high != -1 && new_high < (new_lift + height)) 
00502       return 1;
00503     }
00504   
00505   // Found a new place to go, lets test if we can actually move there
00506   
00507   // Lift 0 tests
00508   if (new_lift == 0)
00509   {
00510     if (move_flags & MOVE_MAPEDIT)
00511       return 0; // Map-editor, so anything is okay.
00512     int ter = 0;
00513     Check_terrain (obj_list, tx, ty, ter);
00514     if (ter & 2)    // Water
00515     {
00516       if (move_flags & (MOVE_FLY+MOVE_SWIM))
00517         return 0;
00518       else
00519         return 1;
00520     }
00521     else if (ter & 1) // Land
00522     {
00523       if (move_flags & (MOVE_FLY|MOVE_WALK))
00524         return 0;
00525       else
00526         return 1;
00527     }
00528     else if (ter & 4) // Blocked
00529     {
00530       if (move_flags & MOVE_FLY)
00531         return 0;
00532       else
00533         return 1;
00534     }
00535     else  // Other
00536       return 0;
00537   }
00538   else if (move_flags & (MOVE_FLY|MOVE_WALK))
00539     return 0;
00540 
00541   return 1;
00542 }
00543 
00544 /*
00545  *  Activate nearby eggs.
00546  */
00547 
00548 void Chunk_cache::activate_eggs
00549   (
00550   Game_object *obj,   // Object (actor) that's near.
00551   Map_chunk *chunk,   // Chunk this is attached to.
00552   int tx, int ty, int tz,   // Tile (absolute).
00553   int from_tx, int from_ty, // Tile walked from.
00554   unsigned short eggbits,   // Eggs[tile].
00555   bool now      // Do them immediately.
00556   )
00557 {
00558   int i;        // Go through eggs.
00559   for (i = 0; i < 8*(int)sizeof(eggbits) - 1 && eggbits; 
00560             i++, eggbits = eggbits >> 1)
00561   {
00562     Egg_object *egg;
00563     if ((eggbits&1) && i < egg_objects.size() &&
00564         (egg = egg_objects[i]) &&
00565         egg->is_active(obj, tx, ty, tz, from_tx, from_ty))
00566     {
00567       egg->activate(obj, now);
00568       if (chunk->get_cache() != this)
00569         return; // A teleport could have deleted us!
00570     }
00571   }
00572   if (eggbits)      // Check 15th bit.
00573   {       // DON'T use an iterator here, since
00574           //   the list can change as eggs are
00575           //   activated, causing a CRASH!
00576     int sz = egg_objects.size();
00577     for (  ; i < sz; i++)
00578     {
00579       Egg_object *egg = egg_objects[i];
00580       if (egg && egg->is_active(obj, tx, ty, tz, from_tx, from_ty))
00581       {
00582         egg->activate(obj, now);
00583         if (chunk->get_cache() != this)
00584           return; // A teleport could have deleted us!      
00585       }
00586     }
00587   }
00588 }
00589 
00590 /*
00591  *  Find door blocking a given tile.
00592  */
00593 
00594 Game_object *Chunk_cache::find_door
00595   (
00596   Tile_coord tile
00597   )
00598   {
00599   for (Game_object_vector::iterator it = doors.begin();
00600             it != doors.end(); ++it)
00601     if ((*it)->blocks(tile))
00602       return *it; // Found it.
00603   return 0;
00604   }
00605 
00606 /*
00607  *  Create list for a given chunk.
00608  */
00609 
00610 Map_chunk::Map_chunk
00611   (
00612   int chunkx, int chunky    // Absolute chunk coords.
00613   ) : objects(0), terrain(0), first_nonflat(0), ice_dungeon(0x00),
00614       dungeon_levels(0), cache(0), roof(0),
00615       dungeon_lights(0), non_dungeon_lights(0),
00616       cx(chunkx), cy(chunky), from_below(0), from_right(0),
00617       from_below_right(0)
00618   {
00619   }
00620 
00621 /*
00622  *  Delete all objects contained within.
00623  */
00624 
00625 Map_chunk::~Map_chunk
00626   (
00627   )
00628   {
00629   delete cache;
00630   delete [] dungeon_levels;
00631   }
00632 
00633 /*
00634  *  Set terrain.  Even if the terrain is the same, it still reloads the
00635  *  'flat' objects.
00636  */
00637 
00638 void Map_chunk::set_terrain
00639   (
00640   Chunk_terrain *ter
00641   )
00642   {
00643   if (terrain)
00644     {
00645     terrain->remove_client();
00646           // Remove objs. from terrain.
00647     Game_object_vector removes;
00648     {     // Separate scope for Object_iterator.
00649     Object_iterator it(get_objects());
00650     Game_object *each;
00651     while ((each = it.get_next()) != 0)
00652           // Kind of nasty, I know:
00653       if (each->as_terrain())
00654         removes.push_back(each);
00655     }
00656     for (Game_object_vector::const_iterator it=removes.begin(); 
00657             it!=removes.end(); ++it)
00658       (*it)->remove_this();
00659     }
00660   terrain = ter;
00661   terrain->add_client();
00662           // Get RLE objects in chunk.
00663   for (int tiley = 0; tiley < c_tiles_per_chunk; tiley++)
00664     for (int tilex = 0; tilex < c_tiles_per_chunk; tilex++)
00665       {
00666       ShapeID id = ter->get_flat(tilex, tiley);
00667       Shape_frame *shape = id.get_shape();
00668       if (shape && shape->is_rle())
00669         {
00670         int shapenum = id.get_shapenum(),
00671             framenum = id.get_framenum();
00672         Shape_info& info = id.get_info();
00673         Game_object *obj = info.is_animated() ?
00674           new Animated_object(shapenum,
00675                 framenum, tilex, tiley)
00676           : new Terrain_game_object(shapenum,
00677                 framenum, tilex, tiley);
00678         add(obj);
00679         }
00680       }
00681   }
00682 
00683 /*
00684  *  Add rendering dependencies for a new object.
00685  */
00686 
00687 void Map_chunk::add_dependencies
00688   (
00689   Game_object *newobj,    // Object to add.
00690   Ordering_info& newinfo    // Info. for new object's ordering.
00691   )
00692   {
00693   Game_object *obj;   // Figure dependencies.
00694   Nonflat_object_iterator next(this);
00695   while ((obj = next.get_next()) != 0)
00696     {
00697     //cout << "Here " << __LINE__ << " " << obj << endl;
00698     /* Compare returns -1 if lt, 0 if dont_care, 1 if gt. */
00699     int newcmp = Game_object::compare(newinfo, obj);
00700     int cmp = newcmp == -1 ? 1 : newcmp == 1 ? 0 : -1;
00701     if (!cmp)   // Bigger than this object?
00702       {
00703       newobj->dependencies.put(obj);
00704       obj->dependors.put(newobj);
00705       }
00706     else if (cmp == 1)  // Smaller than?
00707       {
00708       obj->dependencies.put(newobj);
00709       newobj->dependors.put(obj);
00710       }
00711     }
00712   }
00713 
00714 /*
00715  *  Add rendering dependencies for a new object to another chunk.
00716  *  NOTE:  This is static.
00717  *
00718  *  Output: ->chunk that was checked.
00719  */
00720 
00721 inline Map_chunk *Map_chunk::add_outside_dependencies
00722   (
00723   int cx, int cy,     // Chunk to check.
00724   Game_object *newobj,    // Object to add.
00725   Ordering_info& newinfo    // Info. for new object's ordering.
00726   )
00727   {
00728   Map_chunk *chunk = gmap->get_chunk(cx, cy);
00729   chunk->add_dependencies(newobj, newinfo);
00730   return chunk;
00731   }
00732 
00733 /*
00734  *  Add a game object to a chunk's list.
00735  *
00736  *  Newobj's cx and cy fields are set to this chunk.
00737  */
00738 
00739 void Map_chunk::add
00740   (
00741   Game_object *newobj   // Object to add.
00742   )
00743   {
00744   newobj->cx = get_cx();    // Set object's chunk.
00745   newobj->cy = get_cy();
00746   Ordering_info ord(gwin, newobj);
00747           // Put past flats.
00748   if (first_nonflat)
00749     objects.insert_before(newobj, first_nonflat);
00750   else
00751     objects.append(newobj);
00752           // Not flat?
00753   if (newobj->get_lift() || ord.info.get_3d_height())
00754     {     // Deal with dependencies.
00755     int ty = newobj->get_ty();
00756           // First this chunk.
00757     add_dependencies(newobj, ord);
00758     if (from_below)   // Overlaps from below?
00759       add_outside_dependencies(cx, INCR_CHUNK(cy), 
00760               newobj, ord);
00761     if (from_right)   // Overlaps from right?
00762       add_outside_dependencies(INCR_CHUNK(cx), cy, 
00763                 newobj, ord);
00764     if (from_below_right)
00765       add_outside_dependencies(INCR_CHUNK(cx), 
00766           INCR_CHUNK(cy), newobj, ord);
00767           // See if newobj extends outside.
00768   /* Let's try boundary. YES.  This helps with statues through roofs!*/
00769     bool ext_left = (newobj->get_tx() - ord.xs) < 0 && cx > 0;
00770     bool ext_above = (newobj->get_ty() - ord.ys) < 0 && cy > 0;
00771     if (ext_left)
00772       {
00773       add_outside_dependencies(DECR_CHUNK(cx), cy, 
00774             newobj, ord)->from_right++;
00775       if (ext_above)
00776         add_outside_dependencies(DECR_CHUNK(cx), 
00777                DECR_CHUNK(cy),
00778           newobj, ord)->from_below_right++;
00779       }
00780     if (ext_above)
00781       add_outside_dependencies(cx, DECR_CHUNK(cy),
00782           newobj, ord)->from_below++;
00783     first_nonflat = newobj; // Inserted before old first_nonflat.
00784     }
00785   if (cache)      // Add to cache.
00786     cache->update_object(this, newobj, 1);
00787   if (ord.info.is_light_source()) // Count light sources.
00788     if (dungeon_levels && is_dungeon(newobj->get_tx(),
00789               newobj->get_ty()))
00790       dungeon_lights++;
00791     else
00792       non_dungeon_lights++;
00793   if (newobj->get_lift() >= 5)  // Looks like a roof?
00794     {
00795     if (ord.info.get_shape_class() == Shape_info::building)
00796       roof = 1;
00797     }
00798   }
00799 
00800 /*
00801  *  Add an egg.
00802  */
00803 
00804 void Map_chunk::add_egg
00805   (
00806   Egg_object *egg
00807   )
00808   {
00809   add(egg);     // Add it normally.
00810   egg->set_area();
00811 // Messed up Moonshade after Banes if (cache)   // Add to cache.
00812   need_cache()->update_egg(this, egg, 1);
00813   }
00814 
00815 /*
00816  *  Remove an egg.
00817  */
00818 
00819 void Map_chunk::remove_egg
00820   (
00821   Egg_object *egg
00822   )
00823   {
00824   remove(egg);      // Remove it normally.
00825   if (cache)      // Remove from cache.
00826     cache->update_egg(this, egg, 0);
00827   }
00828 
00829 /*
00830  *  Remove a game object from this list.  The object's cx and cy fields
00831  *  are set to invalid #'s (255,255).
00832  */
00833 
00834 void Map_chunk::remove
00835   (
00836   Game_object *remove
00837   )
00838   {
00839   if (cache)      // Remove from cache.
00840     cache->update_object(this, remove, 0);
00841   remove->clear_dependencies(); // Remove all dependencies.
00842   Game_map *gmap = gwin->get_map();
00843   Shape_info& info = remove->get_info();
00844           // See if it extends outside.
00845   int frame = remove->get_framenum(), tx = remove->get_tx(),
00846           ty = remove->get_ty();
00847   /* Let's try boundary. YES.  Helps with statues through roofs. */
00848   bool ext_left = (tx - info.get_3d_xtiles(frame)) < 0 && cx > 0;
00849   bool ext_above = (ty - info.get_3d_ytiles(frame)) < 0 && cy > 0;
00850   if (ext_left)
00851     {
00852     gmap->get_chunk(cx - 1, cy)->from_below_right--;
00853     if (ext_above)
00854       gmap->get_chunk(cx - 1, cy - 1)->from_below_right--;
00855     }
00856   if (ext_above)
00857     gmap->get_chunk(cx, cy - 1)->from_below--;
00858   if (info.is_light_source()) // Count light sources.
00859     if (dungeon_levels && is_dungeon(tx, ty))
00860       dungeon_lights--;
00861     else
00862       non_dungeon_lights--;
00863   if (remove == first_nonflat)  // First nonflat?
00864     {     // Update.
00865     first_nonflat = remove->get_next();
00866     if (first_nonflat == objects.get_first())
00867       first_nonflat = 0;
00868     }
00869   objects.remove(remove);   // Remove from list.
00870   remove->set_invalid();    // No longer part of world.
00871   }
00872 
00873 /*
00874  *  Is a given rectangle of tiles blocked at a given lift?
00875  *
00876  *  Output: 1 if so, else 0.
00877  *    If 0 (tile is free), new_lift contains the new height that
00878  *       an actor will be at if he walks onto the tile.
00879  */
00880 
00881 int Map_chunk::is_blocked
00882   (
00883   int height,     // Height (along lift) to check.
00884   int lift,     // Starting lift.
00885   int startx, int starty,   // Starting tile coords.
00886   int xtiles, int ytiles,   // Width, height in tiles.
00887   int& new_lift,      // New lift returned.
00888   const int move_flags,
00889   int max_drop,     // Max. drop/rise allowed.
00890   int max_rise      // Max. rise, or -1 to use old beha-
00891           //   viour (max_drop if FLY, else 1).
00892   )
00893   {
00894   Game_map *gmap = gwin->get_map();
00895   int tx, ty;
00896   new_lift = 0;
00897   startx %= c_num_tiles;    // Watch for wrapping.
00898   starty %= c_num_tiles;
00899   int stopy = (starty + ytiles)%c_num_tiles, 
00900       stopx = (startx + xtiles)%c_num_tiles;
00901   for (ty = starty; ty != stopy; ty = INCR_TILE(ty))
00902     {     // Get y chunk, tile-in-chunk.
00903     int cy = ty/c_tiles_per_chunk, rty = ty%c_tiles_per_chunk;
00904     for (tx = startx; tx != stopx; tx = INCR_TILE(tx))
00905       {
00906       int this_lift;
00907       Map_chunk *olist = gmap->get_chunk(
00908           tx/c_tiles_per_chunk, cy);
00909       olist->setup_cache();
00910       if (olist->is_blocked(height, lift, 
00911         tx%c_tiles_per_chunk,
00912         rty, this_lift, move_flags, max_drop,max_rise))
00913         return (1);
00914           // Take highest one.
00915       new_lift = this_lift > new_lift ?
00916           this_lift : new_lift;
00917       }
00918     }
00919   return (0);
00920   }
00921 
00922 /*
00923  *  Check an absolute tile position.
00924  *
00925  *  Output: 1 if blocked, 0 otherwise.
00926  *    Tile.tz may be updated for stepping onto square.
00927  */
00928 
00929 int Map_chunk::is_blocked
00930   (
00931   Tile_coord& tile,
00932   int height,     // Height in tiles to check.
00933   const int move_flags,
00934   int max_drop,     // Max. drop/rise allowed.
00935   int max_rise      // Max. rise, or -1 to use old beha-
00936           //   viour (max_drop if FLY, else 1).
00937   )
00938   {
00939           // Get chunk tile is in.
00940   Game_map *gmap = gwin->get_map();
00941   Map_chunk *chunk = gmap->get_chunk_safely(
00942       tile.tx/c_tiles_per_chunk, tile.ty/c_tiles_per_chunk);
00943   if (!chunk)     // Outside the world?
00944     return 0;   // Then it's not blocked.
00945   chunk->setup_cache();   // Be sure cache is present.
00946   int new_lift;     // Check it within chunk.
00947   if (chunk->is_blocked(height, tile.tz, tile.tx%c_tiles_per_chunk,
00948         tile.ty%c_tiles_per_chunk, new_lift, move_flags, max_drop,
00949                 max_rise))
00950     return (1);
00951   tile.tz = new_lift;
00952   return (0);
00953   }
00954 
00955 /*
00956  *  This one is used to see if an object of dims. possibly > 1X1 can
00957  *  step onto an adjacent square.
00958  */
00959 
00960 int Map_chunk::is_blocked
00961   (
00962           // Object dims:
00963   int xtiles, int ytiles, int ztiles,
00964   Tile_coord from,    // Stepping from here.
00965   Tile_coord& to,     // Stepping to here.  Tz updated.
00966   const int move_flags,
00967   int max_drop,     // Max drop/rise allowed.
00968   int max_rise      // Max. rise, or -1 to use old beha-
00969           //   viour (max_drop if FLY, else 1).
00970   )
00971   {
00972   int vertx0, vertx1;   // Get x-coords. of vert. block
00973           //   to right/left.
00974   int horizx0, horizx1;   // Get x-coords of horiz. block
00975           //   above/below.
00976   int verty0, verty1;   // Get y-coords of horiz. block
00977           //   above/below.
00978   int horizy0, horizy1;   // Get y-coords of vert. block
00979           //   to right/left.
00980           // !Watch for wrapping.
00981   horizx0 = (to.tx + 1 - xtiles + c_num_tiles)%c_num_tiles;
00982   horizx1 = INCR_TILE(to.tx);
00983   if (Tile_coord::gte(to.tx, from.tx))    // Moving right?
00984     {     // Start to right of hot spot.
00985     vertx0 = INCR_TILE(from.tx);
00986     vertx1 = INCR_TILE(to.tx);  // Stop past dest.
00987     }
00988   else        // Moving left?
00989     {
00990     vertx0 = (to.tx + 1 - xtiles + c_num_tiles)%c_num_tiles;
00991     vertx1 = (from.tx + 1 - xtiles + c_num_tiles)%c_num_tiles;
00992     }
00993   verty0 = (to.ty + 1 - ytiles + c_num_tiles)%c_num_tiles;
00994   verty1 = INCR_TILE(to.ty);
00995   if (Tile_coord::gte(to.ty, from.ty))    // Moving down?
00996     {     // Start below hot spot.
00997     horizy0 = INCR_TILE(from.ty); 
00998     horizy1 = INCR_TILE(to.ty); // End past dest.
00999     if (to.ty != from.ty) // Includes bottom of vert. area.
01000       verty1 = DECR_TILE(verty1);
01001     }
01002   else        // Moving up?
01003     {
01004     horizy0 = (to.ty + 1 - ytiles + c_num_tiles)%c_num_tiles;
01005     horizy1 = (from.ty + 1 - ytiles + c_num_tiles)%c_num_tiles;
01006           // Includes top of vert. area.
01007     verty0 = INCR_TILE(verty0);
01008     }
01009   int x, y;     // Go through horiz. part.
01010   int new_lift = from.tz;
01011   int new_lift0 = -1;   // All lift changes must be same.
01012 #ifdef DEBUG
01013   assert(Tile_coord::gte(horizy1, horizy0));
01014   assert(Tile_coord::gte(horizx1, horizx0));
01015   assert(Tile_coord::gte(verty1, verty0));
01016   assert(Tile_coord::gte(vertx1, vertx0));
01017 #endif
01018   for (y = horizy0; y != horizy1; y = INCR_TILE(y))
01019     {     // Get y chunk, tile-in-chunk.
01020     int cy = y/c_tiles_per_chunk, rty = y%c_tiles_per_chunk;
01021     for (x = horizx0; x != horizx1; x = INCR_TILE(x))
01022       {
01023       Map_chunk *olist = gmap->get_chunk(
01024           x/c_tiles_per_chunk, cy);
01025       olist->setup_cache();
01026       int rtx = x%c_tiles_per_chunk;
01027       if (olist->is_blocked(ztiles, from.tz, rtx, rty,
01028         new_lift, move_flags, max_drop, max_rise))
01029         return 1;
01030       if (new_lift != from.tz)
01031         if (new_lift0 == -1)
01032           new_lift0 = new_lift;
01033         else if (new_lift != new_lift0)
01034           return (1);
01035       }
01036     }
01037           // Do vert. block.
01038   for (x = vertx0; x != vertx1; x = INCR_TILE(x))
01039     {     // Get x chunk, tile-in-chunk.
01040     int cx = x/c_tiles_per_chunk, rtx = x%c_tiles_per_chunk;
01041     for (y = verty0; y != verty1; y = INCR_TILE(y))
01042       {
01043       Map_chunk *olist = gmap->get_chunk(
01044           cx, y/c_tiles_per_chunk);
01045       olist->setup_cache();
01046       int rty = y%c_tiles_per_chunk;
01047       if (olist->is_blocked(ztiles, from.tz, rtx, rty,
01048         new_lift, move_flags, max_drop, max_rise))
01049         return 1;
01050       if (new_lift != from.tz)
01051         if (new_lift0 == -1)
01052           new_lift0 = new_lift;
01053         else if (new_lift != new_lift0)
01054           return (1);
01055       }
01056     }
01057   to.tz = new_lift;
01058   return (0);     // All clear.
01059   }
01060 
01061 /*
01062  *  Get the list of tiles in a square perimeter around a given tile.
01063  *
01064  *  Output: List (8*dist) of tiles, starting in Northwest corner and going
01065  *       clockwise.  List is on heap.
01066  */
01067 
01068 static Tile_coord *Get_square
01069   (
01070   Tile_coord& pos,    // Center of square.
01071   int dist      // Distance to perimeter (>0)
01072   )
01073   {
01074   Tile_coord *square = new Tile_coord[8*dist];
01075           // Upper left corner:
01076   square[0] = Tile_coord(DECR_TILE(pos.tx, dist), 
01077           DECR_TILE(pos.ty, dist), pos.tz);
01078   int i;        // Start with top row.
01079   int len = 2*dist + 1;
01080   int out = 1;
01081   for (i = 1; i < len; i++, out++)
01082     square[out] = Tile_coord(INCR_TILE(square[out - 1].tx),
01083       square[out - 1].ty, pos.tz);
01084           // Down right side.
01085   for (i = 1; i < len; i++, out++)
01086     square[out] = Tile_coord(square[out - 1].tx,
01087       INCR_TILE(square[out - 1].ty), pos.tz);
01088           // Bottom, going back to left.
01089   for (i = 1; i < len; i++, out++)
01090     square[out] = Tile_coord(DECR_TILE(square[out - 1].tx),
01091       square[out - 1].ty, pos.tz);
01092           // Left side, going up.
01093   for (i = 1; i < len - 1; i++, out++)
01094     square[out] = Tile_coord(square[out - 1].tx,
01095       DECR_TILE(square[out - 1].ty), pos.tz);
01096   return square;
01097   }
01098 
01099 /*
01100  *  Check a spot against the 'where' paramater to find_spot.
01101  *
01102  *  Output: true if it passes.
01103  */
01104 
01105 inline bool Check_spot
01106   (
01107   Map_chunk::Find_spot_where where,
01108   int tx, int ty, int tz
01109   )
01110   {
01111   Game_map *gmap = Game_window::get_instance()->get_map();
01112   int cx = tx/c_tiles_per_chunk, cy = ty/c_tiles_per_chunk;
01113   Map_chunk *chunk = gmap->get_chunk_safely(cx, cy);
01114   return (where == Map_chunk::inside) == 
01115         (chunk->is_roof(tx % c_tiles_per_chunk, 
01116           ty % c_tiles_per_chunk, tz) < 31);
01117   }
01118 
01119 /*
01120  *  Find a free area for an object of a given shape, looking outwards.
01121  *
01122  *  Output: Tile if successful, else (-1, -1, -1).
01123  */
01124 
01125 Tile_coord Map_chunk::find_spot
01126   (
01127   Tile_coord pos,     // Starting point.
01128   int dist,     // Distance to look outwards.  (0 means
01129           //   only check 'pos'.
01130   int shapenum,     // Shape, frame to find spot for.
01131   int framenum,
01132   int max_drop,     // Allow to drop by this much.
01133   int dir,      // Preferred direction (0-7), or -1 for
01134           //   random.
01135   Find_spot_where where   // Inside/outside.
01136   )
01137   {
01138   Shape_info& info = ShapeID::get_info(shapenum);
01139   int xs = info.get_3d_xtiles(framenum);
01140   int ys = info.get_3d_ytiles(framenum);
01141   int zs = info.get_3d_height();
01142           // The 'MOVE_FLY' flag really means
01143           //   we can look upwards by max_drop.
01144   const int mflags = MOVE_WALK|MOVE_FLY;
01145   int new_lift;
01146           // Start with original position.
01147   if (!Map_chunk::is_blocked(zs, pos.tz, pos.tx - xs + 1,
01148     pos.ty - ys + 1, xs, ys, new_lift, mflags, max_drop))
01149     return Tile_coord(pos.tx, pos.ty, new_lift);
01150   if (dir < 0)
01151     dir = rand()%8;   // Choose dir. randomly.
01152   dir = (dir + 1)%8;    // Make NW the 0 point.
01153   for (int d = 1; d <= dist; d++) // Look outwards.
01154     {
01155     int square_cnt = 8*d  ;// # tiles in square's perim.
01156           // Get square (starting in NW).
01157     Tile_coord *square = Get_square(pos, d);
01158     int index = dir*d;  // Get index of preferred spot.
01159           // Get start of preferred range.
01160     index = (index - d/2 + square_cnt)%square_cnt;
01161     for (int cnt = square_cnt; cnt; cnt--, index++)
01162       {
01163       Tile_coord& p = square[index%square_cnt];
01164       if (!Map_chunk::is_blocked(zs, p.tz, p.tx - xs + 1,
01165         p.ty - ys + 1, xs, ys, new_lift, mflags,
01166                 max_drop) &&
01167           (where == anywhere || 
01168           Check_spot(where, p.tx, p.ty, new_lift)))
01169         { // Use tile before deleting.
01170         Tile_coord ret(p.tx, p.ty, new_lift);
01171         delete [] square;
01172         return ret;
01173         }
01174       }
01175     delete [] square;
01176     }
01177   return Tile_coord(-1, -1, -1);
01178   }
01179 
01180 /*
01181  *  Find a free area for an object (usually an NPC) that we want to
01182  *  approach a given position.
01183  *
01184  *  Output: Tile if successful, else (-1, -1, -1).
01185  */
01186 
01187 Tile_coord Map_chunk::find_spot
01188   (
01189   Tile_coord pos,     // Starting point.
01190   int dist,     // Distance to look outwards.  (0 means
01191           //   only check 'pos'.
01192   Game_object *obj,   // Object that we want to move.
01193   int max_drop,     // Allow to drop by this much.
01194   Find_spot_where where   // Inside/outside.
01195   )
01196   {
01197   Tile_coord t2 = obj->get_tile();
01198           // Get direction from pos. to object.
01199   int dir = (int) Get_direction(pos.ty - t2.ty, t2.tx - pos.tx);
01200   return find_spot(pos, dist, obj->get_shapenum(), obj->get_framenum(),
01201       max_drop, dir, where);
01202   }
01203 
01204 /*
01205  *  Find all desired objects within a given rectangle.
01206  *
01207  *  Output: # found, appended to vec.
01208  */
01209 
01210 int Map_chunk::find_in_area
01211   (
01212   Game_object_vector& vec,  // Returned here.
01213   Rectangle area,     // Area to search.
01214   int shapenum,
01215   int framenum
01216   )
01217   {
01218   int savesize = vec.size();
01219           // Go through interesected chunks.
01220   Chunk_intersect_iterator next_chunk(area);
01221   Rectangle tiles;    // (Tiles within intersected chunk).
01222   int eachcx, eachcy;
01223   Game_map *gmap = gwin->get_map();
01224   while (next_chunk.get_next(tiles, eachcx, eachcy))
01225     {
01226     Map_chunk *chunk = gmap->get_chunk_safely(eachcx, eachcy);
01227     if (!chunk)
01228       continue;
01229     Object_iterator next(chunk->objects);
01230     Game_object *each;
01231     while ((each = next.get_next()) != 0)
01232       if (each->get_shapenum() == shapenum &&
01233           each->get_framenum() == framenum &&
01234           tiles.has_point(each->get_tx(), each->get_ty()))
01235         vec.append(each);
01236     }
01237   return vec.size() - savesize;
01238   }
01239 
01240 
01241 
01242 /*
01243  *  Test all nearby eggs when you've teleported in.
01244  */
01245 
01246 void Map_chunk::try_all_eggs
01247   (
01248   Game_object *obj,   // Object (actor) that's near.
01249   int tx, int ty, int tz,   // Tile (absolute).
01250   int from_tx, int from_ty  // Tile walked from.
01251   )
01252   {
01253   static int norecurse = 0; // NO recursion here.
01254   if (norecurse)
01255     return;
01256   norecurse++;
01257   Game_map *gmap = gwin->get_map();
01258   Tile_coord pos = obj->get_tile();
01259   const int dist = 32;    // See if this works okay.
01260   Rectangle area(pos.tx - dist, pos.ty - dist, 2*dist, 2*dist);
01261           // Go through interesected chunks.
01262   Chunk_intersect_iterator next_chunk(area);
01263   Rectangle tiles;    // (Ignored).
01264   int eachcx, eachcy;
01265   Egg_vector eggs(40);    // Get them here first, as activating
01266           //   an egg could affect chunk's list.
01267   while (next_chunk.get_next(tiles, eachcx, eachcy))
01268     {
01269     Map_chunk *chunk = gmap->get_chunk_safely(eachcx, eachcy);
01270     if (!chunk)
01271       continue;
01272     chunk->setup_cache(); // I think we should do this.
01273     Object_iterator next(chunk->objects);
01274     Game_object *each;
01275     while ((each = next.get_next()) != 0)
01276       if (each->is_egg())
01277         {
01278         Egg_object *egg = (Egg_object *) each;
01279           // Music eggs are causing problems.
01280         if (egg->get_type() != Egg_object::jukebox &&
01281           // And don't teleport a 2nd time.
01282             egg->get_type() != Egg_object::teleport &&
01283                 egg->is_active(obj,
01284             tx, ty, tz, from_tx, from_ty))
01285           eggs.push_back(egg);
01286         }
01287     }
01288   for (Egg_vector::const_iterator it = eggs.begin(); it != eggs.end();
01289                   ++it)
01290     (*it)->activate(obj);
01291   norecurse--;
01292   }
01293 
01294 /*
01295  *  Add a rectangle of dungeon tiles (but only if higher!).
01296  */
01297 
01298 void Map_chunk::add_dungeon_levels
01299   (
01300   Rectangle& tiles, unsigned int lift
01301   )
01302 {
01303   if (!dungeon_levels)
01304   {     // First one found.
01305     dungeon_levels = new unsigned char[256/2];
01306     memset(dungeon_levels, 0, 256/2);
01307   }
01308   int endy = tiles.y + tiles.h, endx = tiles.x + tiles.w;
01309   for (int ty = tiles.y; ty < endy; ty++)
01310   {
01311     for (int tx = tiles.x; tx < endx; tx++)
01312     {
01313       int tnum = (ty*c_tiles_per_chunk + tx)/2;
01314       if (GAME_SI)  // SI has roofs at random levels!!
01315         lift = 5;
01316       if (tx % 2)
01317       {
01318         dungeon_levels[tnum] &= 0x0F;
01319         dungeon_levels[tnum] |= lift << 4;
01320       }
01321       else
01322       {
01323         dungeon_levels[tnum] &= 0xF0;
01324         dungeon_levels[tnum] |= lift;
01325       }
01326     }
01327   }
01328 }
01329 
01330 /*
01331  *  Set up the dungeon levels (after IFIX objects read).
01332  */
01333 
01334 void Map_chunk::setup_dungeon_levels
01335   (
01336   )
01337 {
01338   Game_map *gmap = gwin->get_map();
01339 
01340   Object_iterator next(objects);
01341   Game_object *each;
01342   while ((each = next.get_next()) != 0)
01343   {
01344     int shnum = each->get_shapenum();
01345           // Test for mountain-tops.
01346     if (shnum == 983 || shnum == 969 || shnum == 183 ||
01347         shnum == 182 || shnum == 180 || shnum == 324 || 
01348         ((shnum == 941 || shnum == 394) && 
01349           Game::get_game_type() == SERPENT_ISLE))
01350     {
01351       // SI shape 941, frame 0 => do whole chunk (I think).
01352       Rectangle area = 
01353         (shnum == 941 && each->get_framenum() == 0)
01354         ? Rectangle(cx*c_tiles_per_chunk,
01355               cy*c_tiles_per_chunk,
01356               c_tiles_per_chunk,
01357               c_tiles_per_chunk)
01358         : each->get_footprint();
01359 
01360           // Go through interesected chunks.
01361       Chunk_intersect_iterator next_chunk(area);
01362       Rectangle tiles;// Rel. tiles.
01363       int cx, cy;
01364       while (next_chunk.get_next(tiles, cx, cy))
01365         gmap->get_chunk(cx, cy)->add_dungeon_levels(
01366             tiles, each->get_lift());
01367     }     // Ice Dungeon Pieces in SI
01368     else if (Game::get_game_type() == SERPENT_ISLE && (
01369       shnum == 436 || shnum == 437 || shnum == 444 ||
01370       shnum == 448  || shnum == 466 || shnum == 477))
01371     {
01372       // HACK ALERT! This gets 320x200 to work, but it is a hack
01373       // This is not exactly accurate.
01374       ice_dungeon |= 1 << ( (each->get_tx()>>3) + 2*(each->get_ty()>>3) );
01375 
01376       Rectangle area = each->get_footprint();
01377 
01378           // Go through interesected chunks.
01379       Chunk_intersect_iterator next_chunk(area);
01380       Rectangle tiles;// Rel. tiles.
01381       int cx, cy;
01382       while (next_chunk.get_next(tiles, cx, cy))
01383         gmap->get_chunk(cx, cy)->add_dungeon_levels(
01384                 tiles, each->get_lift());
01385     }
01386   }
01387   if (dungeon_levels)   // Recount lights.
01388     {
01389     dungeon_lights = non_dungeon_lights = 0;
01390     next.reset();
01391     while ((each = next.get_next()) != 0)
01392       if (each->get_info().is_light_source())
01393         if (is_dungeon(each->get_tx(), each->get_ty()))
01394           dungeon_lights++;
01395         else
01396           non_dungeon_lights++;
01397     }
01398 }
01399 
01400 /*
01401  *  Recursively apply gravity over a given rectangle that is known to be
01402  *  unblocked below a given lift.
01403  */
01404 
01405 void Map_chunk::gravity
01406   (
01407   Rectangle area,     // Unblocked tiles (in abs. coords).
01408   int lift      // Lift where tiles are free.
01409   )
01410   {
01411   Game_object_vector dropped(20); // Gets list of objs. that dropped.
01412           // Go through interesected chunks.
01413   Chunk_intersect_iterator next_chunk(area);
01414   Rectangle tiles;    // Rel. tiles.  Not used.
01415   int cx, cy, new_lift;
01416   while (next_chunk.get_next(tiles, cx, cy))
01417     {
01418     Map_chunk *chunk = gmap->get_chunk(cx, cy);
01419     Object_iterator objs(chunk->objects);
01420     Game_object *obj;
01421     while ((obj = objs.get_next()) != 0)
01422       {   // We DO want NPC's to fall.
01423       if (!obj->is_dragable() && 
01424             !obj->get_info().is_npc())
01425         continue;
01426       Tile_coord t = obj->get_tile();
01427           // Get footprint.
01428       Rectangle foot = obj->get_footprint();
01429           // Above area?
01430       if (t.tz >= lift && foot.intersects(area) &&
01431           // Unblocked below itself?
01432           !is_blocked(1, t.tz - 1, foot.x, foot.y,
01433           foot.w, foot.h, new_lift,
01434             MOVE_ALL_TERRAIN, 0) &&
01435           new_lift < t.tz)
01436         dropped.push_back(obj);
01437       }
01438     }
01439   Game_object_vector::const_iterator it;
01440           // Drop each one found.
01441   for (it = dropped.begin(); it != dropped.end(); ++it)
01442     {
01443     Game_object *obj = *it;
01444     Tile_coord t = obj->get_tile();
01445           // Get footprint.
01446     Rectangle foot = obj->get_footprint();
01447           // Let drop as far as possible.
01448     if (!is_blocked(1, t.tz - 1, foot.x, foot.y,
01449       foot.w, foot.h, new_lift, MOVE_ALL_TERRAIN, 100) &&
01450                   new_lift < t.tz)
01451       {   // Drop & recurse.
01452       obj->move(t.tx, t.ty, new_lift);
01453       gravity(foot, obj->get_lift() +
01454           obj->get_info().get_3d_height());
01455       }
01456     }
01457 #if 0
01458   for (it = dropped.begin(); it != dropped.end(); ++it)
01459     {     // Recurse on each one.
01460     Game_object *obj = *it;
01461           // Get footprint.
01462     Rectangle foot = obj->get_footprint();
01463     gravity(foot, obj->get_lift() +
01464           obj->get_info().get_3d_height());
01465     }
01466 #endif
01467   }
01468 
01469 /*
01470  *  Finds if there is a 'roof' above lift in tile (tx, ty)
01471  *  of the chunk. Point is taken 4 above lift
01472  *
01473  *  Roof can be any object, not just a literal roof
01474  *
01475  *  Output: height of the roof.
01476  *  A return of 31 means no roof
01477  *
01478  */
01479 int Map_chunk::is_roof(int tx, int ty, int lift)
01480 {
01481 #if 1   /* Might be lying on bed at lift==2. */
01482   int height = get_lowest_blocked (lift+4, tx, ty);
01483 #else   /* But this is even worse! */
01484   int height = get_lowest_blocked (lift+2, tx, ty);
01485 #endif
01486   if (height == -1) return 31;
01487   return height;
01488 }
01489 
01490 void Map_chunk::kill_cache()
01491 {
01492   // Get rid of terrain
01493   if (terrain) terrain->remove_client();
01494   terrain = 0;
01495 
01496   // Now remove the cachce
01497   delete cache;
01498   cache = 0;
01499 
01500   // Delete dungeon bits
01501   delete [] dungeon_levels;
01502   dungeon_levels = 0;
01503 }
01504 
01505 int Map_chunk::get_obj_actors(Game_object_vector &removes, Actor_vector &actors)
01506 {
01507   int buf_size = 0;
01508   bool failed = false;
01509 
01510   // Separate scope for Object_iterator.
01511   Object_iterator it(get_objects());
01512   Game_object *each;
01513   while ((each = it.get_next()) != 0)
01514   {
01515     Actor *actor = each->as_actor();
01516 
01517     // Normal objects and monsters
01518     if (actor == 0 || (each->is_monster() && each->get_flag(Obj_flags::is_temporary))) {
01519       removes.push_back(each);
01520       int ireg_size = each->get_ireg_size();
01521 
01522       if (ireg_size < 0) failed = true;
01523       else buf_size += ireg_size;
01524     }
01525       // Actors/NPCs here
01526     else {
01527       actors.push_back(actor);
01528     }
01529   }
01530 
01531   return failed?-1:buf_size;
01532 }

Generated on Mon Jul 9 14:42:49 2007 for ExultEngine by  doxygen 1.5.1