gamemap.cc

Go to the documentation of this file.
00001 
00005 /*
00006  *
00007  *  Copyright (C) 1998-1999  Jeffrey S. Freedman
00008  *  Copyright (C) 2000-2001  The Exult Team
00009  *
00010  *  This program is free software; you can redistribute it and/or modify
00011  *  it under the terms of the GNU General Public License as published by
00012  *  the Free Software Foundation; either version 2 of the License, or
00013  *  (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 #ifndef ALPHA_LINUX_CXX
00030 #  include <cstdlib>
00031 #  include <cstring>
00032 #  include <cstdarg>
00033 #  include <cstdio>
00034 #endif
00035 
00036 #include "gamemap.h"
00037 #include "objs.h"
00038 #include "chunks.h"
00039 #include "mappatch.h"
00040 #include "fnames.h"
00041 #include "utils.h"
00042 #include "shapeinf.h"
00043 #include "objiter.h"
00044 #include "Flex.h"
00045 #include "exceptions.h"
00046 #include "animate.h"
00047 #include "barge.h"
00048 #include "spellbook.h"
00049 #include "virstone.h"
00050 #include "egg.h"
00051 #include "jawbone.h"
00052 #include "actors.h" /* For Dead_body, which should be moved. */
00053 #include "ucsched.h"
00054 #include "gamewin.h"  /* With some work, could get rid of this. */
00055 #include "bodies.h"
00056 #include "game.h"
00057 #include "effects.h"
00058 #include "objiter.cc" /* Yes we #include the .cc here on purpose! Please don't "fix" this */
00059 #include "databuf.h"
00060 #include <fstream>
00061 
00062 #ifndef UNDER_CE
00063 using std::cerr;
00064 using std::cout;
00065 using std::endl;
00066 using std::istream;
00067 using std::ifstream;
00068 using std::ios;
00069 using std::memcpy;
00070 using std::memset;
00071 using std::ofstream;
00072 using std::rand;
00073 using std::strcmp;
00074 using std::strcpy;
00075 using std::string;
00076 using std::strlen;
00077 using std::srand;
00078 using std::vector;
00079 #endif
00080 
00081 /*
00082  *  Create a chunk.
00083  */
00084 
00085 Map_chunk *Game_map::create_chunk
00086   (
00087   int cx, int cy
00088   )
00089   {
00090   return (objects[cx][cy] = new Map_chunk(cx, cy));
00091   }
00092 
00093 /*
00094  *  Read in a terrain chunk.
00095  */
00096 
00097 Chunk_terrain *Game_map::read_terrain
00098   (
00099   int chunk_num     // Want this one from u7chunks.
00100   )
00101   {
00102   assert(chunk_num >= 0 && chunk_num < chunk_terrains.size());
00103   unsigned char buf[16*16*2]; 
00104   chunks->seekg(chunk_num * 512);
00105   chunks->read(reinterpret_cast<char*>(buf), sizeof(buf));
00106   Chunk_terrain *ter = new Chunk_terrain(&buf[0]);
00107   chunk_terrains.put(chunk_num, ter);
00108   return ter;
00109   }
00110 
00111 /*
00112  *  Create game window.
00113  */
00114 
00115 Game_map::Game_map
00116   (
00117   ) : 
00118             chunk_terrains(0), read_all_terrain(false), 
00119       map_modified(false),
00120       map_patches(new Map_patch_collection), chunks(0)
00121   {
00122   }
00123 
00124 /*
00125  *  Deleting map.
00126  */
00127 
00128 Game_map::~Game_map
00129   (
00130   )
00131   {
00132   clear();      // Delete all objects, chunks.
00133   delete chunks;
00134   delete map_patches;
00135   }
00136 
00137 /*
00138  *  Initialize for new/restored game.
00139  */
00140 
00141 void Game_map::init
00142   (
00143   )
00144   {
00145   if (chunks)
00146     delete chunks;
00147   chunks = new ifstream;
00148   int num_chunk_terrains;
00149   bool patch_exists = is_system_path_defined("<PATCH>");
00150   if (patch_exists && U7exists(PATCH_U7CHUNKS))
00151     U7open(*chunks, PATCH_U7CHUNKS);
00152   else try
00153     {
00154     U7open(*chunks, U7CHUNKS);
00155     }
00156   catch(const file_exception & f)
00157     {
00158     if (!Game::is_editing() ||  // Ok if map-editing.
00159         !patch_exists)  // But only if patch exists.
00160       throw f;
00161     ofstream ochunks; // Create one in 'patch'.
00162     U7open(ochunks, PATCH_U7CHUNKS);
00163     unsigned char buf[16*16*2]; 
00164     memset(&buf[0], 0, sizeof(buf));
00165     ochunks.write((char *) buf, sizeof(buf));
00166     ochunks.close();
00167     U7open(*chunks, PATCH_U7CHUNKS);
00168     }
00169           // Get to end so we can get length.
00170   chunks->seekg(0, ios::end);
00171           // 2 bytes/tile.
00172   num_chunk_terrains = chunks->tellg()/
00173         (c_tiles_per_chunk*c_tiles_per_chunk*2);
00174           // Resize list to hold all.
00175   chunk_terrains.resize(num_chunk_terrains);
00176   read_all_terrain = map_modified = false;
00177   std::ifstream u7map;    // Read in map.
00178   bool nomap = false;
00179   if (is_system_path_defined("<PATCH>") && U7exists(PATCH_U7MAP))
00180     U7open(u7map, PATCH_U7MAP);
00181   else try 
00182     {
00183     U7open(u7map, U7MAP);
00184     }
00185   catch(const file_exception & f)
00186     {
00187     if (!Game::is_editing())  // Ok if map-editing.
00188       throw f;
00189     nomap = true;
00190     }
00191   for (int schunk = 0; schunk < c_num_schunks*c_num_schunks; schunk++)
00192   {     // Read in the chunk #'s.
00193     unsigned char buf[16*16*2];
00194     if (nomap)
00195       memset(&buf[0], 0, sizeof(buf));
00196     else
00197       u7map.read(reinterpret_cast<char *>(buf), sizeof(buf));
00198     int scy = 16*(schunk/12);// Get abs. chunk coords.
00199     int scx = 16*(schunk%12);
00200     uint8 *mapdata = buf;
00201           // Go through chunks.
00202     for (int cy = 0; cy < 16; cy++)
00203       for (int cx = 0; cx < 16; cx++)
00204         terrain_map[scx+cx][scy+cy] = Read2(mapdata);
00205   }
00206   u7map.close();
00207           // Clear object lists, flags.
00208   // No casting _should_ be necessary at this point.
00209   // Who needs this?
00210   memset(reinterpret_cast<char*>(objects), 0, sizeof(objects));
00211   memset(reinterpret_cast<char*>(schunk_read), 0, sizeof(schunk_read));
00212   memset(reinterpret_cast<char*>(schunk_modified), 0, 
00213             sizeof(schunk_modified));
00214 
00215   memset(schunk_cache, 0, sizeof (schunk_cache));
00216   memset(schunk_cache_sizes, -1, sizeof(schunk_cache_sizes));
00217   }
00218 
00219 /*
00220  *  Clear out world's contents.  Should be used during a 'restore'.
00221  */
00222 
00223 void Game_map::clear
00224   (
00225   )
00226   {
00227           // Delete all chunks (& their objs).
00228   for (int y = 0; y < c_num_chunks; y++)
00229     for (int x = 0; x < c_num_chunks; x++)
00230       {
00231       delete objects[x][y];
00232       objects[x][y] = 0;
00233       }
00234   int cnt = chunk_terrains.size();
00235   int i;
00236   for (i = 0; i < cnt; i++)
00237     delete chunk_terrains[i];
00238   chunk_terrains.resize(0);
00239   delete chunks;      // Close 'u7chunks'.
00240   chunks = 0;
00241   map_modified = false;
00242           // Clear 'read' flags.
00243   memset(reinterpret_cast<char*>(schunk_read), 0, sizeof(schunk_read));
00244   memset(reinterpret_cast<char*>(schunk_modified), 0, 
00245             sizeof(schunk_modified));
00246 
00247   for (i = 0; i < 144; i++) delete [] schunk_cache[i];
00248   memset(schunk_cache, 0, sizeof (schunk_cache));
00249   memset(schunk_cache_sizes, -1, sizeof(schunk_cache_sizes));
00250 
00251   }
00252 
00253 /*
00254  *  Read in superchunk data to cover the screen.
00255  */
00256 
00257 void Game_map::read_map_data
00258   (
00259   )
00260   {
00261   Game_window *gwin = Game_window::get_instance();
00262   int scrolltx = gwin->get_scrolltx(), scrollty = gwin->get_scrollty();
00263   int w = gwin->get_width(), h = gwin->get_height();
00264           // Start one tile to left.
00265   int firstsx = (scrolltx - 1)/c_tiles_per_schunk, 
00266       firstsy = (scrollty - 1)/c_tiles_per_schunk;
00267           // End 8 tiles to right.
00268   int lastsx = (scrolltx + (w + c_tilesize - 2)/c_tilesize + 
00269         c_tiles_per_chunk/2)/c_tiles_per_schunk;
00270   int lastsy = (scrollty + (h + c_tilesize - 2)/c_tilesize + 
00271         c_tiles_per_chunk/2)/c_tiles_per_schunk;
00272           // Watch for wrapping.
00273   int stopsx = (lastsx + 1)%c_num_schunks,
00274       stopsy = (lastsy + 1)%c_num_schunks;
00275           // Read in "map", "ifix" objects for
00276           //  all visible superchunks.
00277   for (int sy = firstsy; sy != stopsy; sy = (sy + 1)%c_num_schunks)
00278     for (int sx = firstsx; sx != stopsx; 
00279             sx = (sx + 1)%c_num_schunks)
00280       {
00281           // Figure superchunk #.
00282       int schunk = 12*sy + sx;
00283           // Read it if necessary.
00284       if (!schunk_read[schunk])
00285         get_superchunk_objects(schunk);
00286       }
00287   } 
00288 
00289 /*
00290  *  Get the map objects and scenery for a superchunk.
00291  */
00292 
00293 void Game_map::get_map_objects
00294   (
00295   int schunk      // Superchunk # (0-143).
00296   )
00297   {
00298   int scy = 16*(schunk/12); // Get abs. chunk coords.
00299   int scx = 16*(schunk%12);
00300           // Go through chunks.
00301   for (int cy = 0; cy < 16; cy++)
00302     for (int cx = 0; cx < 16; cx++)
00303       get_chunk_objects(scx + cx, scy + cy);
00304   }
00305 
00306 /*
00307  *  Read in terrain graphics data into window's image.  (May also be
00308  *  called during map-editing if the chunknum changes.)
00309  */
00310 
00311 void Game_map::get_chunk_objects
00312   (
00313   int cx, int cy      // Chunk index within map.
00314   )
00315   {
00316           // Get list we'll store into.
00317   Map_chunk *chunk = get_chunk(cx, cy);
00318   int chunk_num = terrain_map[cx][cy];
00319   Chunk_terrain *ter = get_terrain(chunk_num);
00320   chunk->set_terrain(ter);
00321   }
00322 
00323 /*
00324  *  Read in all terrain chunks (for editing).
00325  */
00326 
00327 void Game_map::get_all_terrain
00328   (
00329   )
00330   {
00331   if (read_all_terrain)
00332     return;     // Already done.
00333   int num_chunk_terrains = chunk_terrains.size();
00334   for (int i = 0; i < num_chunk_terrains; i++)
00335     if (!chunk_terrains[i])
00336       read_terrain(i);
00337   read_all_terrain = true;
00338   }
00339 
00340 /*
00341  *  Set a chunk to a new terrain (during map-editing).
00342  */
00343 
00344 void Game_map::set_chunk_terrain
00345   (
00346   int cx, int cy,     // Coords. of chunk to change.
00347   int chunknum      // New chunk #.
00348   )
00349   {
00350   terrain_map[cx][cy] = chunknum; // Set map.
00351   get_chunk_objects(cx, cy);  // Set chunk to it.
00352   }
00353 
00354 /*
00355  *  Get the name of an ireg or ifix file.
00356  *
00357  *  Output: ->fname, where name is stored.
00358  */
00359 
00360 char *Game_map::get_schunk_file_name
00361   (
00362   char *prefix,     // "ireg" or "ifix".
00363   int schunk,     // Superchunk # (0-143).
00364   char *fname     // Name is stored here.
00365   )
00366   {
00367   strcpy(fname, prefix);
00368   int len = strlen(fname);
00369   fname[len] = '0' + schunk/16;
00370   int lb = schunk%16;
00371   fname[len + 1] = lb < 10 ? ('0' + lb) : ('a' + (lb - 10));
00372   fname[len + 2] = 0;
00373   return (fname);
00374   }
00375 
00376 /*
00377  *  Write out the 'static' map files.
00378  */
00379 
00380 void Game_map::write_static
00381   (
00382   )
00383   {
00384   U7mkdir("<PATCH>", 0755);   // Create dir if not already there. Don't
00385                   // use PATCHDAT define cause it has a
00386                   // trailing slash
00387 
00388   int schunk;     // Write each superchunk to 'static'.
00389   for (schunk = 0; schunk < c_num_schunks*c_num_schunks; schunk++)
00390           // Only write what we've modified.
00391     if (schunk_modified[schunk])
00392       write_ifix_objects(schunk);
00393   int cnt = chunk_terrains.size();
00394   int i;        // Any terrains modified?
00395   for (i = 0; i < cnt; i++)
00396     if (chunk_terrains[i] && chunk_terrains[i]->is_modified())
00397       break;
00398   if (i < cnt)      // Got to update.
00399     {
00400     get_all_terrain();  // IMPORTANT:  Get all in memory.
00401     ofstream ochunks; // Open file for chunks data.
00402           // This truncates the file.
00403     U7open(ochunks, PATCH_U7CHUNKS);
00404     for (i = 0; i < cnt; i++)
00405       {
00406       Chunk_terrain *ter = chunk_terrains[i];
00407       unsigned char data[512];
00408       if (ter)
00409         {
00410         ter->write_flats(data);
00411         ter->set_modified(false);
00412         }
00413       else
00414         {
00415         memset(&data[0], 0, 512);
00416         cerr << "NULL terrain.  U7chunks may be bad."
00417                 << endl;
00418         }
00419       ochunks.write(reinterpret_cast<char*>(data), 512);
00420       }
00421     if (!ochunks.good())
00422       throw file_write_exception(U7CHUNKS);
00423     ochunks.close();
00424     }
00425   std::ofstream u7map;    // Write out map.
00426   U7open(u7map, PATCH_U7MAP);
00427   for (schunk = 0; schunk < c_num_schunks*c_num_schunks; schunk++)
00428     {
00429     int scy = 16*(schunk/12);// Get abs. chunk coords.
00430     int scx = 16*(schunk%12);
00431     uint8 buf[16*16*2];
00432     uint8 *mapdata = buf;
00433           // Go through chunks.
00434     for (int cy = 0; cy < 16; cy++)
00435       for (int cx = 0; cx < 16; cx++)
00436         Write2(mapdata, terrain_map[scx+cx][scy+cy]);
00437     u7map.write(reinterpret_cast<char*>(buf), sizeof(buf));
00438     }
00439   if (!u7map.good())
00440     throw file_write_exception(U7MAP);
00441   u7map.close();
00442   map_modified = false;
00443   }
00444 
00445 /*
00446  *  Write out one of the "u7ifix" files.
00447  *
00448  *  Output: Errors reported.
00449  */
00450 
00451 void Game_map::write_ifix_objects
00452   (
00453   int schunk      // Superchunk # (0-143).
00454   )
00455   {
00456   char fname[128];    // Set up name.
00457   ofstream ifix_stream; // There it is.
00458   U7open(ifix_stream, get_schunk_file_name(PATCH_U7IFIX, schunk, fname));
00459   StreamDataSource ifix(&ifix_stream);
00460           // +++++Use game title.
00461   const int count = c_chunks_per_schunk*c_chunks_per_schunk;
00462   Flex::write_header(&ifix, "Exult",  count);
00463   uint8 table[2*count*4];
00464   uint8 *tptr = &table[0];
00465   int scy = 16*(schunk/12); // Get abs. chunk coords.
00466   int scx = 16*(schunk%12);
00467           // Go through chunks.
00468   for (int cy = 0; cy < 16; cy++)
00469     for (int cx = 0; cx < 16; cx++)
00470       {
00471           // Store file position in table.
00472       long start = ifix.getPos();
00473       Write4(tptr, start);
00474       Map_chunk *chunk = get_chunk(scx + cx,
00475                      scy + cy);
00476           // Restore original order (sort of).
00477       Object_iterator_backwards next(chunk);
00478       Game_object *obj;
00479       while ((obj = next.get_next()) != 0)
00480         obj->write_ifix(&ifix);
00481           // Store IFIX data length.
00482       Write4(tptr, ifix.getPos() - start);
00483       }
00484   ifix.seek(0x80);  // Write table.
00485   ifix.write(reinterpret_cast<char *>(&table[0]), sizeof(table));
00486   ifix_stream.flush();
00487   int result = ifix_stream.good();
00488   if (!result)
00489     throw file_write_exception(fname);
00490   schunk_modified[schunk] = false;
00491   return;
00492   }
00493 
00494 /*
00495  *  Read in the objects for a superchunk from one of the "u7ifix" files.
00496  */
00497 
00498 void Game_map::get_ifix_objects
00499   (
00500   int schunk      // Superchunk # (0-143).
00501   )
00502   {
00503   char fname[128];    // Set up name.
00504   ifstream ifix_stream; // There it is.
00505   if (is_system_path_defined("<PATCH>") &&
00506           // First check for patch.
00507       U7exists(get_schunk_file_name(PATCH_U7IFIX, schunk, fname)))
00508     U7open(ifix_stream, fname);
00509   else try
00510     {
00511     U7open(ifix_stream, get_schunk_file_name(U7IFIX, schunk, fname));
00512     }
00513     catch(const file_exception & f)
00514     {
00515     if (!Game::is_editing())  // Ok if map-editing.
00516       throw f;
00517     return;
00518     }
00519   StreamDataSource ifix(&ifix_stream);
00520   int scy = 16*(schunk/12); // Get abs. chunk coords.
00521   int scx = 16*(schunk%12);
00522           // Go through chunks.
00523   for (int cy = 0; cy < 16; cy++)
00524     for (int cx = 0; cx < 16; cx++)
00525       {
00526           // Get to index entry for chunk.
00527       int chunk_num = cy*16 + cx;
00528       ifix.seek(0x80 + chunk_num*8);
00529           // Get location, length.
00530       long shapesoff = ifix.read4();
00531       if (!shapesoff) // Nothing there?
00532         continue;
00533       unsigned long shapeslen = ifix.read4();
00534       get_ifix_chunk_objects(&ifix, shapesoff, shapeslen/4,
00535         scx + cx, scy + cy);
00536       }
00537   }
00538 
00539 /*
00540  *  Get the objects from one ifix chunk entry onto the screen.
00541  */
00542 
00543 void Game_map::get_ifix_chunk_objects
00544   (
00545   DataSource* ifix,
00546   long filepos,     // Where chunk's data lies.
00547   int cnt,      // # entries (objects).
00548   int cx, int cy      // Absolute chunk #'s.
00549   )
00550   {
00551   Game_window *gwin = Game_window::get_instance();
00552   ifix->seek(filepos);    // Get to actual shape.
00553           // Get buffer to hold entries' indices.
00554   unsigned char *entries = new unsigned char[4*cnt];
00555   unsigned char *ent = entries; // Read them in.
00556   ifix->read(reinterpret_cast<char*>(entries), 4*cnt);
00557           // Get object list for chunk.
00558   Map_chunk *olist = get_chunk(cx, cy);
00559   for (int i = 0; i < cnt; i++, ent += 4)
00560     {
00561     Ifix_game_object *obj;
00562     int shnum = ent[2]+256*(ent[3]&3);
00563     Shape_info& info = ShapeID::get_info(shnum);
00564     if (info.is_animated() || info.has_sfx())
00565       obj = new Animated_ifix_object(ent);
00566     else
00567       obj = new Ifix_game_object(ent);
00568 
00569     olist->add(obj);
00570     }
00571   delete[] entries;   // Done with buffer.
00572   olist->setup_dungeon_levels();  // Should have all dungeon pieces now.
00573   }
00574 
00575 /*
00576  *  Constants for IREG files:
00577  */
00578 #define IREG_SPECIAL  255   // Precedes special entries.
00579 #define IREG_UCSCRIPT 1   // Saved Usecode_script for object.
00580 #define IREG_ENDMARK  2   // Just an 'end' mark.
00581 
00582 /*
00583  *  Write out scheduled usecode for an object.
00584  */
00585 
00586 void Game_map::write_scheduled
00587   (
00588   DataSource* ireg,
00589   Game_object *obj,
00590   bool write_mark     // Write an IREG_ENDMARK if true.
00591   )
00592   {
00593   for (Usecode_script *scr = Usecode_script::find(obj); scr;
00594           scr = Usecode_script::find(obj, scr))
00595     {
00596     unsigned char buf[256];
00597     int len = scr->save(buf, sizeof(buf));
00598     if (len < 0)
00599       cerr << "Error saving Usecode script" << endl;
00600     else if (len > 0)
00601       {
00602       ireg->write1(IREG_SPECIAL);
00603       ireg->write1(IREG_UCSCRIPT);
00604       ireg->write2(len);  // Store length.
00605       ireg->write(reinterpret_cast<char*>(buf), len);
00606       }
00607     }
00608   if (write_mark)
00609     {
00610     ireg->write1(IREG_SPECIAL);
00611     ireg->write1(IREG_ENDMARK);
00612     }
00613   }
00614 
00615 /*
00616  *  Write modified 'u7ireg' files.
00617  */
00618 
00619 void Game_map::write_ireg
00620   (
00621   )
00622   {
00623 
00624           // Write each superchunk to Iregxx.
00625   for (int schunk = 0; schunk < c_num_schunks*c_num_schunks; schunk++)
00626           // Only write what we've read.
00627     if (schunk_cache[schunk] && schunk_cache_sizes[schunk] >= 0) {
00628       // It's loaded in a memory buffer
00629       char fname[128];    // Set up name.
00630       ofstream ireg_stream;
00631       U7open(ireg_stream, get_schunk_file_name(U7IREG, schunk, fname));
00632       ireg_stream.write(schunk_cache[schunk], schunk_cache_sizes[schunk]);
00633     }
00634     else if (schunk_read[schunk]) {
00635       // It's active
00636       write_ireg_objects(schunk);
00637     }
00638   }
00639 
00640 /*
00641  *  Write out one of the "u7ireg" files.
00642  *
00643  *  Output: 0 if error, which is reported.
00644  */
00645 
00646 void Game_map::write_ireg_objects
00647   (
00648   int schunk      // Superchunk # (0-143).
00649   )
00650   {
00651   char fname[128];    // Set up name.
00652   ofstream ireg_stream;     // There it is.
00653   U7open(ireg_stream, get_schunk_file_name(U7IREG, schunk, fname));
00654   StreamDataSource ireg(&ireg_stream);
00655   write_ireg_objects (schunk, &ireg);
00656   ireg_stream.flush();
00657   int result = ireg_stream.good();
00658   if (!result) throw file_write_exception(fname);
00659   return;
00660   }
00661 
00662 
00663 /*
00664  *  Write out one of the "u7ireg" files.
00665  *
00666  *  Output: 0 if error, which is reported.
00667  */
00668 
00669 void Game_map::write_ireg_objects
00670   (
00671   int schunk,     // Superchunk # (0-143).
00672   DataSource *ireg
00673   )
00674   {
00675   int scy = 16*(schunk/12); // Get abs. chunk coords.
00676   int scx = 16*(schunk%12);
00677           // Go through chunks.
00678   for (int cy = 0; cy < 16; cy++)
00679     for (int cx = 0; cx < 16; cx++)
00680       {
00681       Map_chunk *chunk = get_chunk(scx + cx,
00682                      scy + cy);
00683       Game_object *obj;
00684           // Restore original order (sort of).
00685       Object_iterator_backwards next(chunk);
00686       while ((obj = next.get_next()) != 0)
00687         obj->write_ireg(ireg);
00688       ireg->write2(0);// End with 2 0's.
00689       }
00690   }
00691 
00692 /*
00693  *  Read in the objects for a superchunk from one of the "u7ireg" files.
00694  *  (These are the moveable objects.)
00695  */
00696 
00697 void Game_map::get_ireg_objects
00698   (
00699   int schunk      // Superchunk # (0-143).
00700   )
00701   {
00702   char fname[128];    // Set up name.
00703   ifstream ireg_stream;     // There it is.
00704   DataSource *ireg = 0;
00705 
00706   if (schunk_cache[schunk] && schunk_cache_sizes[schunk] >= 0) {
00707     // No items
00708     if (schunk_cache_sizes[schunk] == 0) return;
00709     ireg = new BufferDataSource (schunk_cache[schunk], schunk_cache_sizes[schunk]);
00710 #ifdef DEBUG
00711     std::cout << "Reading " << get_schunk_file_name(U7IREG, schunk, fname) << " from memory" << std::endl;
00712 #endif
00713   }
00714   else {
00715     try
00716     {
00717       U7open(ireg_stream, get_schunk_file_name(U7IREG, schunk, fname));
00718     }
00719     catch(const file_exception & f)
00720     {
00721       return;     // Just don't show them.
00722     }
00723     ireg = new StreamDataSource (&ireg_stream);
00724   }
00725   int scy = 16*(schunk/12); // Get abs. chunk coords.
00726   int scx = 16*(schunk%12);
00727   read_ireg_objects(ireg, scx, scy);
00728           // A fixup:
00729   if (schunk == 10*12 + 11 && Game::get_game_type() == SERPENT_ISLE)
00730     {     // Lever in SilverSeed:
00731     Game_object_vector vec;
00732     if (Game_object::find_nearby(vec, Tile_coord(2936, 2726, 0),
00733           787, 0, 0, c_any_qual, 5))
00734       vec[0]->move(2937, 2727, 2);
00735     }
00736   delete ireg;
00737   if (schunk_cache[schunk]) {
00738     delete [] schunk_cache[schunk];
00739     schunk_cache[schunk] = 0;
00740     schunk_cache_sizes[schunk] = -1;
00741   }
00742 
00743   }
00744 
00745 /*
00746  *  Read in a 'special' IREG entry (one starting with 255).
00747  */
00748 
00749 void Read_special_ireg
00750   (
00751   DataSource *ireg,
00752   Game_object *obj    // Last object read.
00753   )
00754   {
00755   int type = ireg->read1();   // Get type.
00756   int len = ireg->read2();    // Length of rest.
00757   unsigned char *buf = new unsigned char[len];
00758   ireg->read(reinterpret_cast<char*>(buf), len);
00759   if (type == IREG_UCSCRIPT)  // Usecode script?
00760     {
00761     Usecode_script *scr = Usecode_script::restore(obj, buf, len);
00762     if (scr)
00763       {
00764       scr->start(scr->get_delay());
00765 #if 0
00766       COUT("Restored script for '" << 
00767         item_names[obj->get_shapenum()]
00768               << "'" << endl);
00769       scr->print(cout); cout << endl;
00770 #endif
00771       }
00772     }
00773   else
00774     cerr << "Unknown special IREG entry: " << type << endl;
00775   delete [] buf;
00776   }
00777 
00778 /*
00779  *  Read in a 'special' IREG entry (one starting with 255).
00780  */
00781 
00782 void Game_map::read_special_ireg
00783   (
00784   DataSource *ireg,
00785   Game_object *obj    // Last object read.
00786   )
00787   {
00788   unsigned char entlen;
00789   while ((entlen = ireg->peek()) == IREG_SPECIAL && !ireg->eof())
00790     {
00791     ireg->read1();    // Eat the IREG_SPECIAL.
00792     unsigned char type = ireg->peek();
00793     if (type == IREG_ENDMARK)
00794       {   // End of list.
00795       ireg->read1();
00796       return;
00797       }
00798     Read_special_ireg(ireg, obj);
00799     }
00800   }
00801 
00802 /*
00803  *  Create an "egg".
00804  */
00805 
00806 static Egg_object *Create_egg
00807   (
00808   unsigned char *entry,   // 1-byte ireg entry.
00809   bool animated
00810   )
00811   {
00812   int shnum = entry[2]+256*(entry[3]&3);
00813   int frnum = entry[3] >> 2;
00814   unsigned short type = entry[4] + 256*entry[5];
00815   int prob = entry[6];    // Probability (1-100).
00816   int data1 = entry[7] + 256*entry[8];
00817   int lift = entry[9] >> 4;
00818   int data2 = entry[10] + 256*entry[11];
00819   Egg_object *obj = animated ?
00820     new Animated_egg_object(shnum, frnum,
00821       entry[0]&0xf, entry[1]&0xf, lift, type, prob,
00822             data1, data2)
00823     : new Egg_object(shnum, frnum,
00824       entry[0]&0xf, entry[1]&0xf, lift, type, prob,
00825             data1, data2);
00826   return (obj);
00827   }
00828 
00829 /*
00830  *  Containers and items classed as 'quality_flags' have a byte of flags.
00831  *  This routine returns them converted into Object_flags.
00832  */
00833 inline unsigned long Get_quality_flags
00834   (
00835   unsigned char qualbyte    // Quality byte containing flags.
00836   )
00837   {
00838   return  ((qualbyte&1) << Obj_flags::invisible) |
00839     (((qualbyte>>3)&1) << Obj_flags::okay_to_take);
00840   }
00841 
00842 /*
00843  *  Read a list of ireg objects.  They are either placed in the desired
00844  *  game chunk, or added to their container.
00845  */
00846 
00847 void Game_map::read_ireg_objects
00848   (
00849   DataSource *ireg,     // File to read from.
00850   int scx, int scy,   // Abs. chunk coords. of superchunk.
00851   Game_object *container,   // Container, or null.
00852   unsigned long flags   // Usecode item flags.
00853   )
00854   {
00855   int entlen;     // Gets entry length.
00856   sint8 index_id = -1;
00857   Game_object *last_obj = 0;  // Last one read in this call.
00858   Game_window *gwin = Game_window::get_instance();
00859           // Go through entries.
00860   while (((entlen = ireg->read1(), !ireg->eof())))
00861     {
00862 
00863     // Skip 0's & ends of containers.
00864 
00865     if (!entlen || entlen == 1)
00866     {
00867       if (container)
00868         return; // Skip 0's & ends of containers.
00869       else
00870         continue;
00871     }
00872     // Detect the 2 byte index id
00873     else if (entlen == 2)
00874     {
00875       index_id = (sint8) ireg->read2();
00876       continue;
00877     }
00878     else if (entlen == IREG_SPECIAL)
00879       {
00880       Read_special_ireg(ireg, last_obj);
00881       continue;
00882       }
00883           // Get copy of flags.
00884     unsigned long oflags = flags & ~(1<<Obj_flags::is_temporary);
00885     if (entlen != 6 && entlen != 10 && entlen != 12 && 
00886                 entlen != 18)
00887       {
00888       long pos = ireg->getPos();
00889       cout << "Unknown entlen " << entlen << " at pos. " <<
00890           pos << endl;
00891       ireg->seek(pos + entlen);
00892       continue; // Only know these two types.
00893       }
00894     unsigned char entry[18];// Get entry.
00895     ireg->read(reinterpret_cast<char*>(entry), entlen);
00896     int cx = entry[0] >> 4; // Get chunk indices within schunk.
00897     int cy = entry[1] >> 4;
00898           // Get coord. #'s where shape goes.
00899     int tilex = entry[0] & 0xf;
00900     int tiley = entry[1] & 0xf;
00901           // Get shape #, frame #.
00902     int shnum = entry[2]+256*(entry[3]&3);
00903     int frnum = entry[3] >> 2;
00904 
00905     Shape_info& info = ShapeID::get_info(shnum);
00906     unsigned int lift, quality, type;
00907     Ireg_game_object *obj;
00908     int is_egg = 0;   // Fields are eggs.
00909           // An "egg"?
00910 
00911     // Has flag byte(s)
00912     if (entlen == 10)
00913     {
00914       // Temporary
00915       if (entry[6] & 1) oflags |= 1<<Obj_flags::is_temporary;
00916     }
00917     
00918     if (info.get_shape_class() == Shape_info::hatchable)
00919       {
00920       bool anim = info.is_animated() || info.has_sfx();
00921       Egg_object *egg = Create_egg(entry, anim);
00922       get_chunk(scx + cx, scy + cy)->add_egg(egg);
00923       last_obj = egg;
00924       continue;
00925       }
00926     else if (entlen == 6 || entlen == 10) // Simple entry?
00927       {
00928       type = 0;
00929       lift = entry[4] >> 4;
00930       quality = entry[5];
00931       obj = create_ireg_object(info, shnum, frnum,
00932               tilex, tiley, lift);
00933       is_egg = obj->is_egg();
00934 
00935           // Wierd use of flag:
00936       if (info.has_quantity())
00937         {
00938         if (!(quality&0x80))
00939           oflags &= 
00940             ~(1<<Obj_flags::okay_to_take);
00941         else
00942           quality &= 0x7f;
00943         }
00944       else if (info.has_quality_flags())
00945         { // Use those flags instead of deflt.
00946         oflags = Get_quality_flags(quality);
00947         quality = 0;
00948         }
00949       }
00950     else if (entlen == 12)  // Container?
00951       {
00952       type = entry[4] + 256*entry[5];
00953       lift = entry[9] >> 4;
00954       quality = entry[7];
00955       oflags =  // Override flags (I think).
00956         Get_quality_flags(entry[11]);
00957       if (shnum == 330)// Virtue stone?
00958         {
00959         Virtue_stone_object *v = 
00960            new Virtue_stone_object(shnum, frnum, tilex,
00961             tiley, lift);
00962         v->set_pos(entry[4], entry[5], entry[6],
00963                 entry[7]);
00964         obj = v;
00965         type = 0;
00966         }
00967       else if (shnum == 961)
00968         {
00969         Barge_object *b = new Barge_object(
00970             shnum, frnum, tilex, tiley, lift,
00971           entry[4], entry[5],
00972           (quality>>1)&3);
00973         obj = b;
00974         if (!gwin->get_moving_barge() && 
00975               (quality&(1<<3)))
00976           gwin->set_moving_barge(b);
00977         }
00978       else if (Game::get_game_type() == SERPENT_ISLE &&
00979         shnum == 555) // serpent jawbone
00980         {
00981         obj = new Jawbone_object(shnum, frnum,
00982           tilex, tiley, lift, entry[10]);
00983         }
00984       else if (Game::get_game_type() == SERPENT_ISLE && 
00985          shnum == 400 && frnum == 8 && quality == 1)
00986         // Gwenno. Ugly hack to fix bug without having to start
00987         // a new game. Remove someday... (added 20010820)
00988         {
00989         Dead_body *b = new Dead_body(400, 8, 
00990             tilex, tiley, lift, 149);
00991         obj = b;
00992         gwin->set_body(149, b);
00993         }
00994       else if (quality == 1 && 
00995            (entry[8] >= 0x80 || 
00996           Game::get_game_type() == SERPENT_ISLE)) 
00997         {   // NPC's body.
00998         int npc_num = (entry[8] - 0x80) & 0xFF;
00999         Dead_body *b = new Dead_body(shnum, frnum, 
01000             tilex, tiley, lift, npc_num);
01001         obj = b;
01002         gwin->set_body(npc_num, b);
01003         }
01004       else if (Is_body(shnum)) {
01005         obj = new Dead_body(
01006             shnum, frnum, tilex, tiley, lift, -1);
01007       }
01008       else
01009         obj = new Container_game_object(
01010             shnum, frnum, tilex, tiley, lift,
01011               entry[10]);
01012           // Read container's objects.
01013       if (type) // (0 if empty.)
01014         { // Don't pass along invisibility!
01015         read_ireg_objects(ireg, scx, scy, obj, 
01016           oflags & ~(1<<Obj_flags::invisible));
01017         obj->elements_read();
01018         }
01019       }
01020     else      // Length 18 means it's a spellbook.
01021       {   // Get all 9 spell bytes.
01022       quality = 0;
01023       unsigned char circles[9];
01024       memcpy(&circles[0], &entry[4], 5);
01025       lift = entry[9] >> 4;
01026       memcpy(&circles[5], &entry[10], 4);
01027       uint8 *ptr = &entry[14];
01028           // 3 unknowns, then bookmark.
01029       unsigned char bmark = ptr[3];
01030       obj = new Spellbook_object(
01031         shnum, frnum, tilex, tiley, lift,
01032         &circles[0], bmark);
01033       }
01034     obj->set_quality(quality);
01035     obj->set_flags(oflags);
01036     last_obj = obj;   // Save as last read.
01037           // Add, but skip volume check.
01038     if (container)
01039       {
01040       if (index_id != -1 && 
01041           container->add_readied(obj, index_id, 1, 1))
01042         continue;
01043       else if (container->add(obj, 1))
01044         continue;
01045       }
01046     Map_chunk *chunk = get_chunk(scx + cx, scy + cy);
01047     if (is_egg)
01048       chunk->add_egg((Egg_object *) obj);
01049     else
01050       chunk->add(obj);
01051     }
01052   }
01053 
01054 /*
01055  *  Create non-container IREG objects.
01056  */
01057 
01058 Ireg_game_object *Game_map::create_ireg_object
01059   (
01060   Shape_info& info,   // Info. about shape.
01061   int shnum, int frnum,   // Shape, frame.
01062   int tilex, int tiley,   // Tile within chunk.
01063   int lift      // Desired lift.
01064   )
01065   {
01066   if (info.is_field())    // (These are all animated.)
01067     {     // Check shapes.
01068     if (shnum == 895 || // Fire.
01069         shnum == 561) // SI - blue flame.
01070       return new Field_object(shnum, frnum, tilex, tiley,
01071           lift, Egg_object::fire_field);
01072     else if (shnum == 900)  // Poison.
01073       return new Field_object(shnum, frnum, tilex, tiley,
01074           lift, Egg_object::poison_field);
01075     else if (shnum == 902)  // Sleep.
01076       return new Field_object(shnum, frnum, tilex, tiley,
01077           lift, Egg_object::sleep_field);
01078     else if (shnum == 756)  // Caltrops.
01079       return new Field_object(shnum, frnum, tilex, tiley,
01080           lift, Egg_object::caltrops_field);
01081     }
01082   if (info.is_animated() || info.has_sfx())
01083     return new Animated_ireg_object(
01084            shnum, frnum, tilex, tiley, lift);
01085   if (shnum == 607)   // Path.
01086     return new Egglike_game_object(
01087           shnum, frnum, tilex, tiley, lift);
01088   if (shnum == 848 || shnum == 268) // Mirror
01089     return new Mirror_object(shnum, frnum, tilex, tiley, lift);
01090   else if (shnum == 761)    // Spellbook.
01091     {
01092     static unsigned char circles[9] = {0};
01093     return new Spellbook_object(
01094         shnum, frnum, tilex, tiley, lift,
01095         &circles[0], 0);
01096     }
01097   else if (info.get_shape_class() == Shape_info::container)
01098     {
01099     if (shnum == 555 && GAME_SI)
01100       return new Jawbone_object(shnum, frnum, tilex, tiley,
01101                   lift);
01102     else
01103       return new Container_game_object(shnum, frnum, 
01104               tilex, tiley, lift);
01105     }
01106   else
01107     return new Ireg_game_object(shnum, frnum, tilex, tiley, lift);
01108   }
01109 
01110 /*
01111  *  Create non-container IREG objects.
01112  */
01113 
01114 Ireg_game_object *Game_map::create_ireg_object
01115   (
01116   int shnum, int frnum    // Shape, frame.
01117   )
01118   {
01119   return create_ireg_object(ShapeID::get_info(shnum), 
01120             shnum, frnum, 0, 0, 0);
01121   }
01122 
01123 /*
01124  *  Create 'fixed' (landscape, building) objects.
01125  */
01126 
01127 Ifix_game_object *Game_map::create_ifix_object
01128   (
01129   int shnum, int frnum    // Shape, frame.
01130   )
01131   {
01132   Shape_info& info = ShapeID::get_info(shnum);
01133   return (info.is_animated() || info.has_sfx())
01134       ? new Animated_ifix_object(shnum, frnum, 0, 0, 0)
01135       : new Ifix_game_object(shnum, frnum, 0, 0, 0);
01136   }
01137 
01138 /*
01139  *  Read in the objects in a superchunk.
01140  */
01141 
01142 void Game_map::get_superchunk_objects
01143   (
01144   int schunk      // Superchunk #.
01145   )
01146   {
01147 //  CYCLE_RED_PLASMA();
01148   get_map_objects(schunk);  // Get map objects/scenery.
01149 //  CYCLE_RED_PLASMA();
01150   get_ifix_objects(schunk); // Get objects from ifix.
01151 //  CYCLE_RED_PLASMA();
01152   get_ireg_objects(schunk); // Get moveable objects.
01153 //  CYCLE_RED_PLASMA();
01154   schunk_read[schunk] = 1;  // Done this one now.
01155   map_patches->apply(schunk); // Move/delete objects.
01156   }
01157 
01158 /*
01159  *  Locate a chunk with a given terrain # and center the view on it.
01160  *
01161  *  Output: true if found, else 0.
01162  */
01163 
01164 bool Game_map::locate_terrain
01165   (
01166   int tnum,     // # in u7chunks.
01167   int& cx, int& cy,   // Chunk to start at, or (-1,-1).
01168           //   Updated with chunk found.
01169   bool upwards      // If true, search upwards.
01170   )
01171   {
01172   int cnum;     // Chunk #, counting L-R, T-B.
01173   int cstop;      // Stop when cnum == cstop.
01174   int dir;
01175   if (upwards)
01176     {
01177     cstop = -1;
01178     dir = -1;
01179     if (cx == -1)   // Start at end?
01180       cnum = c_num_chunks*c_num_chunks - 1;
01181     else
01182       cnum = cy*c_num_chunks + cx - 1;
01183     }
01184   else
01185     {
01186     cstop = c_num_chunks*c_num_chunks;
01187     dir = 1;
01188     cnum = (cx == -1) ? 0 : cy*c_num_chunks + cx + 1;
01189     }
01190   while (cnum != cstop)
01191     {
01192     int chunky = cnum/c_num_chunks;
01193     int chunkx = cnum%c_num_chunks;
01194     if (terrain_map[chunkx][chunky] == tnum)
01195       {   // Return chunk # found.
01196       cx = chunkx;
01197       cy = chunky;
01198           // Center window over chunk found.
01199       Game_window::get_instance()->center_view(Tile_coord(
01200         cx*c_tiles_per_chunk + c_tiles_per_chunk/2,
01201         cy*c_tiles_per_chunk + c_tiles_per_chunk/2, 
01202                   0));
01203       return true;
01204       }
01205     cnum += dir;
01206     }
01207   return false;     // Failed.
01208   }
01209 
01210 /*
01211  *  Swap two adjacent terrain #'s, keeping the map looking the same.
01212  *
01213  *  Output: false if unsuccessful.
01214  */
01215 
01216 bool Game_map::swap_terrains
01217   (
01218   int tnum      // Swap tnum and tnum + 1.
01219   )
01220   {
01221   if (tnum < 0 || tnum >= chunk_terrains.size() - 1)
01222     return false;   // Out of bounds.
01223   map_modified = true;
01224           // Swap in list.
01225   Chunk_terrain *tmp = get_terrain(tnum);
01226   tmp->set_modified();
01227   chunk_terrains[tnum] = get_terrain(tnum + 1);
01228   chunk_terrains[tnum]->set_modified();
01229   chunk_terrains[tnum + 1] = tmp;
01230           // Update terrain map.
01231   for (int cy = 0; cy < c_num_chunks; cy++)
01232     for (int cx = 0; cx < c_num_chunks; cx++)
01233       {
01234       if (terrain_map[cx][cy] == tnum)
01235         terrain_map[cx][cy]++;
01236       else if (terrain_map[cx][cy] == tnum + 1)
01237         terrain_map[cx][cy]--;
01238       }
01239   Game_window::get_instance()->set_all_dirty();
01240   return true;
01241   }
01242 
01243 /*
01244  *  Insert a new terrain after a given one, and push all the others up
01245  *  so the map looks the same.  The new terrain is filled with
01246  *  (shape, frame) == (0, 0) unless 'dup' is passed 'true'.
01247  *
01248  *  Output: False if unsuccessful.
01249  */
01250 
01251 bool Game_map::insert_terrain
01252   (
01253   int tnum,     // Insert after this one (may be -1).
01254   bool dup      // If true, duplicate #tnum.
01255   )
01256   {
01257   if (tnum < -1 || tnum >= chunk_terrains.size())
01258     return false;   // Invalid #.
01259   get_all_terrain();    // Need all of 'u7chunks' read in.
01260   map_modified = true;
01261   unsigned char buf[16*16*2]; // Set up buffer with shape #'s.
01262   if (dup && tnum >= 0)
01263     {     // Want to duplicate given terrain.
01264     Chunk_terrain *ter = chunk_terrains[tnum];
01265     unsigned char *data = &buf[0];
01266     for (int ty = 0; ty < c_tiles_per_chunk; ty++)
01267       for (int tx = 0; tx < c_tiles_per_chunk; tx++)
01268       {
01269       ShapeID id = ter->get_flat(tx, ty);
01270       *data++ = id.get_shapenum()&0xff;
01271       *data++ = ((id.get_shapenum()>>8)&3) | 
01272             (id.get_framenum()<<2);
01273       }
01274     }
01275   else
01276     memset(reinterpret_cast<char*>(buf), 0, sizeof(buf));
01277   Chunk_terrain *new_terrain = new Chunk_terrain(&buf[0]);
01278           // Insert in list.
01279   chunk_terrains.insert(chunk_terrains.begin() + tnum + 1, new_terrain);
01280           // Indicate terrains are modified.
01281   int num_chunk_terrains = chunk_terrains.size();
01282   for (int i = tnum + 1; i < num_chunk_terrains; i++)
01283     chunk_terrains[i]->set_modified();
01284   if (tnum + 1 == num_chunk_terrains - 1)
01285     return true;    // Inserted at end of list.
01286           // Update terrain map.
01287   for (int cy = 0; cy < c_num_chunks; cy++)
01288     for (int cx = 0; cx < c_num_chunks; cx++)
01289       {
01290       if (terrain_map[cx][cy] > tnum)
01291         terrain_map[cx][cy]++;
01292       }
01293   Game_window::get_instance()->set_all_dirty();
01294   return true;
01295   }
01296 
01297 /*
01298  *  Remove a terrain, updating the map.
01299  *
01300  *  Output: false if unsuccessful.
01301  */
01302 
01303 bool Game_map::delete_terrain
01304   (
01305   int tnum
01306   )
01307   {
01308   if (tnum < 0 || tnum >= chunk_terrains.size())
01309     return false;   // Out of bounds.
01310   map_modified = true;
01311   int sz = chunk_terrains.size();
01312   delete chunk_terrains[tnum];
01313   for (int i = tnum + 1; i < sz; i++)
01314     {     // Move the rest downwards.
01315     Chunk_terrain *tmp = get_terrain(i);
01316     tmp->set_modified();
01317     chunk_terrains[i - 1] = tmp;
01318     }
01319   chunk_terrains.resize(sz - 1);
01320           // Update terrain map.
01321   for (int cy = 0; cy < c_num_chunks; cy++)
01322     for (int cx = 0; cx < c_num_chunks; cx++)
01323       {
01324       if (terrain_map[cx][cy] >= tnum)
01325         terrain_map[cx][cy]--;
01326       }
01327   Game_window::get_instance()->set_all_dirty();
01328   return true;
01329   }
01330 
01331 /*
01332  *  Commit edits made to terrain chunks.
01333  */
01334 
01335 void Game_map::commit_terrain_edits
01336   (
01337   )
01338   {
01339   int num_terrains = chunk_terrains.size();
01340           // Create list of flags.
01341   unsigned char *ters = new unsigned char[num_terrains];
01342   memset(ters, 0, num_terrains);
01343           // Commit edits.
01344   for (int i = 0; i < num_terrains; i++)
01345     if (chunk_terrains[i] && chunk_terrains[i]->commit_edits())
01346       ters[i] = 1;
01347           // Update terrain map.
01348   for (int cy = 0; cy < c_num_chunks; cy++)
01349     for (int cx = 0; cx < c_num_chunks; cx++)
01350       {
01351       Map_chunk *chunk = objects[cx][cy];
01352       if (chunk && ters[terrain_map[cx][cy]] != 0 &&
01353           chunk->get_terrain())
01354           // Reload objects.
01355         chunk->set_terrain(chunk->get_terrain());
01356       }
01357   delete [] ters;
01358   }
01359 
01360 /*
01361  *  Abort edits made to terrain chunks.
01362  */
01363 
01364 void Game_map::abort_terrain_edits
01365   (
01366   )
01367   {
01368   int num_terrains = chunk_terrains.size();
01369           // Abort edits.
01370   for (int i = 0; i < num_terrains; i++)
01371     if (chunk_terrains[i])
01372       chunk_terrains[i]->abort_edits();
01373   }
01374 
01375 /*
01376  *  Find all unused shapes in game.  This can take a while!!
01377  */
01378 
01379 void Game_map::find_unused_shapes
01380   (
01381   unsigned char *found,   // Bits set for shapes found.
01382   int foundlen      // # bytes.
01383   )
01384   {
01385   memset(found, 0, foundlen);
01386   Game_window *gwin = Game_window::get_instance();
01387   Shape_manager *sman = Shape_manager::get_instance();
01388   cout << "Reading all chunks";
01389           // Read in EVERYTHING!
01390   for (int sc = 0; sc < c_num_schunks*c_num_schunks; sc++)
01391     {
01392     cout << '.';
01393     cout.flush();
01394     char msg[80];
01395     snprintf(msg, sizeof(msg), "Scanning superchunk %d", sc);
01396     gwin->get_effects()->center_text(msg);
01397     gwin->paint();
01398     gwin->show();
01399     if (!schunk_read[sc])
01400       get_superchunk_objects(sc);
01401     }
01402   cout << endl;
01403   int maxbits = foundlen*8; // Total #bits in 'found'.
01404   int nshapes = sman->get_shapes().get_num_shapes();
01405   if (maxbits > nshapes)
01406     maxbits = nshapes;
01407           // Go through chunks.
01408   for (int cy = 0; cy < c_num_chunks; cy++)
01409     for (int cx = 0; cx < c_num_chunks; cx++)
01410       {
01411       Map_chunk *chunk = get_chunk(cx, cy);
01412       Recursive_object_iterator all(chunk->get_objects());
01413       Game_object *obj;
01414       while ((obj = all.get_next()) != 0)
01415         {
01416         int shnum = obj->get_shapenum();
01417         if (shnum >= 0 && shnum < maxbits)
01418           found[shnum/8] |= (1<<(shnum%8));
01419         }
01420       }
01421   int i;
01422   for (i = 0; i < maxbits; i++) // Add all possible monsters.
01423     {
01424     Shape_info& info = ShapeID::get_info(i);
01425     Monster_info *minf = info.get_monster_info();
01426     if (minf)
01427       found[i/8] |= (1<<(i%8));
01428     Weapon_info *winf = info.get_weapon_info();
01429     if (winf)   // Get projectiles for weapons.
01430       {
01431       int proj = winf->get_projectile();
01432       if (proj > 0 && proj < maxbits)
01433         found[proj/8] |= (1<<(proj%8));
01434       }
01435     }
01436   for (i = 0x96; i < maxbits; i++)  // Ignore flats (<0x96).
01437     if (!(found[i/8]&(1<<(i%8))))
01438       cout << "Shape " << i << " not found in game" << endl;
01439   }
01440 
01441 /*
01442  *  Look throughout the map for a given shape.  The search starts at
01443  *  the first currently-selected shape, if possible.
01444  *
01445  *  Output: ->object if found, else 0.
01446  */
01447 
01448 Game_object *Game_map::locate_shape
01449   (
01450   int shapenum,     // Desired shape.
01451   bool upwards,     // If true, search upwards.
01452   Game_object *start    // Start here if !0.
01453   )
01454   {
01455   int cx = -1, cy = 0;    // Before chunk to search.
01456   int dir = 1;      // Direction to increment.
01457   int stop = c_num_chunks;
01458   if (upwards)
01459     {
01460     dir = -1;
01461     stop = -1;
01462     cx = c_num_chunks;  // Past last chunk.
01463     cy = c_num_chunks - 1;
01464     }
01465   Game_object *obj = 0;
01466   if (start)      // Start here.
01467     {
01468     Game_object *owner = start->get_outermost();
01469     cx = owner->get_cx();
01470     cy = owner->get_cy();
01471     if (upwards)
01472       {
01473       Recursive_object_iterator_backwards next(start);
01474       while ((obj = next.get_next()) != 0)
01475         if (obj->get_shapenum() == shapenum)
01476           break;
01477       }
01478     else
01479       {
01480       Recursive_object_iterator next(start);
01481       while ((obj = next.get_next()) != 0)
01482         if (obj->get_shapenum() == shapenum)
01483           break;
01484       }
01485     }
01486   while (!obj)      // Not found yet?
01487     {
01488     cx += dir;    // Next chunk.
01489     if (cx == stop)   // Past (either) end?
01490       {
01491       cy += dir;
01492       if (cy == stop)
01493         break;  // All done.
01494       cx -= dir*c_num_chunks;
01495       }
01496     Map_chunk *chunk = get_chunk(cx, cy);
01497           // Make sure objs. are read.
01498     int sx = cx/c_chunks_per_schunk, sy = cy/c_chunks_per_schunk;
01499     int schunk = sy*c_num_schunks + sx;
01500     if (!schunk_read[schunk])
01501       get_superchunk_objects(schunk);
01502     if (upwards)
01503       {
01504       Recursive_object_iterator_backwards next(
01505               chunk->get_objects());
01506       while ((obj = next.get_next()) != 0)
01507         if (obj->get_shapenum() == shapenum)
01508           break;
01509       }
01510     else
01511       {
01512       Recursive_object_iterator next(chunk->get_objects());
01513       while ((obj = next.get_next()) != 0)
01514         if (obj->get_shapenum() == shapenum)
01515           break;
01516       }
01517     }
01518   return obj;
01519   }
01520 
01521 /*
01522  *  Do a cache out. (x, y) is the center
01523  */
01524 
01525 void Game_map::cache_out(int cx, int cy)
01526 {
01527   int sx = cx / c_chunks_per_schunk;
01528   int sy = cy / c_chunks_per_schunk;
01529   bool chunk_flags[12][12];
01530 
01531 #ifdef DEBUG
01532   std::cout << "Want to cache out around super chunk: " << (sy*12 + sx) << " = "  << sx << ", " << sy << std::endl;
01533 #endif
01534 
01535   std::memset(chunk_flags, 0, sizeof(chunk_flags));
01536 
01537   // We cache out all but the 9 directly around the pov
01538 
01539   chunk_flags[(sy+11)%12][(sx+11)%12] = true;
01540   chunk_flags[(sy+11)%12][sx] = true;
01541   chunk_flags[(sy+11)%12][(sx+1)%12] = true;
01542 
01543   chunk_flags[sy][(sx+11)%12] = true;
01544   chunk_flags[sy][sx] = true;
01545   chunk_flags[sy][(sx+1)%12] = true;
01546 
01547   chunk_flags[(sy+1)%12][(sx+11)%12] = true;
01548   chunk_flags[(sy+1)%12][sx] = true;
01549   chunk_flags[(sy+1)%12][(sx+1)%12] = true;
01550 
01551   for (sy = 0; sy < 12; sy++) for (sx = 0; sx < 12; sx++) {
01552     if (chunk_flags[sy][sx]) continue;
01553 
01554     int schunk = sy*12 + sx;
01555     if (schunk_read[schunk] && !schunk_modified[schunk]) cache_out_schunk(schunk);
01556   }
01557 }
01558 
01559 void Game_map::cache_out_schunk(int schunk)
01560 {
01561   // Get abs. chunk coords.
01562   const int scy = 16*(schunk/12);
01563   const int scx = 16*(schunk%12);
01564   int cy, cx;
01565   bool save_map_modified = map_modified;
01566 
01567   if (schunk_modified[schunk])
01568     return;     // NEVER cache out modified chunks.
01569   // Our vectors
01570   Game_object_vector removes;
01571   Actor_vector actors;
01572 
01573   int buf_size = 0;
01574 
01575 #ifdef DEBUG
01576   std::cout << "Killing superchunk: " << schunk << std::endl;
01577 #endif
01578   // Go through chunks and get all the items
01579   for (cy = 0; cy < 16; cy++) for (cx = 0; cx < 16; cx++) {
01580 
01581     int size = objects[scx + cx][scy + cy]->get_obj_actors(removes, actors);
01582 
01583     if (size < 0) {
01584 #ifdef DEBUG
01585       std::cerr << "Failed attempting to kill superchunk" << std::endl;
01586 #endif
01587       return;
01588     }
01589 
01590     buf_size += size + 2;
01591   }
01592 
01593   schunk_read[schunk] = false;
01594 
01595 #ifdef DEBUG
01596   std::cout << "Buffer size of " << buf_size << " bytes required to store super chunk" << std::endl;
01597 #endif
01598 
01599   // Clear old (this shouldn't happen)
01600   if (schunk_cache[schunk]) {
01601     delete [] schunk_cache[schunk];
01602     schunk_cache[schunk] = 0;
01603     schunk_cache_sizes[schunk] = -1;
01604   }
01605 
01606   // Create new
01607   schunk_cache[schunk] = new char[buf_size];
01608   schunk_cache_sizes[schunk] = buf_size;
01609 
01610   BufferDataSource ds(schunk_cache[schunk], schunk_cache_sizes[schunk]);
01611 
01612   write_ireg_objects(schunk, &ds);
01613 
01614 #ifdef DEBUG
01615   std::cout << "Wrote " << ds.getPos() << " bytes" << std::endl;
01616 #endif
01617 
01618   // Now remove the objects
01619   for (Game_object_vector::const_iterator it=removes.begin(); it!=removes.end(); ++it) {
01620     (*it)->delete_contents();
01621     (*it)->remove_this();
01622   }
01623 
01624   // Now disable the actors
01625   for (Actor_vector::const_iterator act=actors.begin(); act!=actors.end(); ++act) {
01626     (*act)->cache_out();
01627   }
01628 
01629   // Go through chunks and finish up
01630   for (cy = 0; cy < 16; cy++) for (cx = 0; cx < 16; cx++) {
01631 
01632     objects[scx + cx][scy + cy]->kill_cache();
01633   }
01634           // Removing objs. sets these flags.
01635   schunk_modified[schunk] = false;
01636   map_modified = save_map_modified;
01637 }

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