00001
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
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"
00053 #include "ucsched.h"
00054 #include "gamewin.h"
00055 #include "bodies.h"
00056 #include "game.h"
00057 #include "effects.h"
00058 #include "objiter.cc"
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
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
00095
00096
00097 Chunk_terrain *Game_map::read_terrain
00098 (
00099 int chunk_num
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
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
00126
00127
00128 Game_map::~Game_map
00129 (
00130 )
00131 {
00132 clear();
00133 delete chunks;
00134 delete map_patches;
00135 }
00136
00137
00138
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() ||
00159 !patch_exists)
00160 throw f;
00161 ofstream ochunks;
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
00170 chunks->seekg(0, ios::end);
00171
00172 num_chunk_terrains = chunks->tellg()/
00173 (c_tiles_per_chunk*c_tiles_per_chunk*2);
00174
00175 chunk_terrains.resize(num_chunk_terrains);
00176 read_all_terrain = map_modified = false;
00177 std::ifstream u7map;
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())
00188 throw f;
00189 nomap = true;
00190 }
00191 for (int schunk = 0; schunk < c_num_schunks*c_num_schunks; schunk++)
00192 {
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);
00199 int scx = 16*(schunk%12);
00200 uint8 *mapdata = buf;
00201
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
00208
00209
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
00221
00222
00223 void Game_map::clear
00224 (
00225 )
00226 {
00227
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;
00240 chunks = 0;
00241 map_modified = false;
00242
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
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
00265 int firstsx = (scrolltx - 1)/c_tiles_per_schunk,
00266 firstsy = (scrollty - 1)/c_tiles_per_schunk;
00267
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
00273 int stopsx = (lastsx + 1)%c_num_schunks,
00274 stopsy = (lastsy + 1)%c_num_schunks;
00275
00276
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
00282 int schunk = 12*sy + sx;
00283
00284 if (!schunk_read[schunk])
00285 get_superchunk_objects(schunk);
00286 }
00287 }
00288
00289
00290
00291
00292
00293 void Game_map::get_map_objects
00294 (
00295 int schunk
00296 )
00297 {
00298 int scy = 16*(schunk/12);
00299 int scx = 16*(schunk%12);
00300
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
00308
00309
00310
00311 void Game_map::get_chunk_objects
00312 (
00313 int cx, int cy
00314 )
00315 {
00316
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
00325
00326
00327 void Game_map::get_all_terrain
00328 (
00329 )
00330 {
00331 if (read_all_terrain)
00332 return;
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
00342
00343
00344 void Game_map::set_chunk_terrain
00345 (
00346 int cx, int cy,
00347 int chunknum
00348 )
00349 {
00350 terrain_map[cx][cy] = chunknum;
00351 get_chunk_objects(cx, cy);
00352 }
00353
00354
00355
00356
00357
00358
00359
00360 char *Game_map::get_schunk_file_name
00361 (
00362 char *prefix,
00363 int schunk,
00364 char *fname
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
00378
00379
00380 void Game_map::write_static
00381 (
00382 )
00383 {
00384 U7mkdir("<PATCH>", 0755);
00385
00386
00387
00388 int schunk;
00389 for (schunk = 0; schunk < c_num_schunks*c_num_schunks; schunk++)
00390
00391 if (schunk_modified[schunk])
00392 write_ifix_objects(schunk);
00393 int cnt = chunk_terrains.size();
00394 int i;
00395 for (i = 0; i < cnt; i++)
00396 if (chunk_terrains[i] && chunk_terrains[i]->is_modified())
00397 break;
00398 if (i < cnt)
00399 {
00400 get_all_terrain();
00401 ofstream ochunks;
00402
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;
00426 U7open(u7map, PATCH_U7MAP);
00427 for (schunk = 0; schunk < c_num_schunks*c_num_schunks; schunk++)
00428 {
00429 int scy = 16*(schunk/12);
00430 int scx = 16*(schunk%12);
00431 uint8 buf[16*16*2];
00432 uint8 *mapdata = buf;
00433
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
00447
00448
00449
00450
00451 void Game_map::write_ifix_objects
00452 (
00453 int schunk
00454 )
00455 {
00456 char fname[128];
00457 ofstream ifix_stream;
00458 U7open(ifix_stream, get_schunk_file_name(PATCH_U7IFIX, schunk, fname));
00459 StreamDataSource ifix(&ifix_stream);
00460
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);
00466 int scx = 16*(schunk%12);
00467
00468 for (int cy = 0; cy < 16; cy++)
00469 for (int cx = 0; cx < 16; cx++)
00470 {
00471
00472 long start = ifix.getPos();
00473 Write4(tptr, start);
00474 Map_chunk *chunk = get_chunk(scx + cx,
00475 scy + cy);
00476
00477 Object_iterator_backwards next(chunk);
00478 Game_object *obj;
00479 while ((obj = next.get_next()) != 0)
00480 obj->write_ifix(&ifix);
00481
00482 Write4(tptr, ifix.getPos() - start);
00483 }
00484 ifix.seek(0x80);
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
00496
00497
00498 void Game_map::get_ifix_objects
00499 (
00500 int schunk
00501 )
00502 {
00503 char fname[128];
00504 ifstream ifix_stream;
00505 if (is_system_path_defined("<PATCH>") &&
00506
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())
00516 throw f;
00517 return;
00518 }
00519 StreamDataSource ifix(&ifix_stream);
00520 int scy = 16*(schunk/12);
00521 int scx = 16*(schunk%12);
00522
00523 for (int cy = 0; cy < 16; cy++)
00524 for (int cx = 0; cx < 16; cx++)
00525 {
00526
00527 int chunk_num = cy*16 + cx;
00528 ifix.seek(0x80 + chunk_num*8);
00529
00530 long shapesoff = ifix.read4();
00531 if (!shapesoff)
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
00541
00542
00543 void Game_map::get_ifix_chunk_objects
00544 (
00545 DataSource* ifix,
00546 long filepos,
00547 int cnt,
00548 int cx, int cy
00549 )
00550 {
00551 Game_window *gwin = Game_window::get_instance();
00552 ifix->seek(filepos);
00553
00554 unsigned char *entries = new unsigned char[4*cnt];
00555 unsigned char *ent = entries;
00556 ifix->read(reinterpret_cast<char*>(entries), 4*cnt);
00557
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;
00572 olist->setup_dungeon_levels();
00573 }
00574
00575
00576
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
00584
00585
00586 void Game_map::write_scheduled
00587 (
00588 DataSource* ireg,
00589 Game_object *obj,
00590 bool write_mark
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);
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
00617
00618
00619 void Game_map::write_ireg
00620 (
00621 )
00622 {
00623
00624
00625 for (int schunk = 0; schunk < c_num_schunks*c_num_schunks; schunk++)
00626
00627 if (schunk_cache[schunk] && schunk_cache_sizes[schunk] >= 0) {
00628
00629 char fname[128];
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
00636 write_ireg_objects(schunk);
00637 }
00638 }
00639
00640
00641
00642
00643
00644
00645
00646 void Game_map::write_ireg_objects
00647 (
00648 int schunk
00649 )
00650 {
00651 char fname[128];
00652 ofstream ireg_stream;
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
00665
00666
00667
00668
00669 void Game_map::write_ireg_objects
00670 (
00671 int schunk,
00672 DataSource *ireg
00673 )
00674 {
00675 int scy = 16*(schunk/12);
00676 int scx = 16*(schunk%12);
00677
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
00685 Object_iterator_backwards next(chunk);
00686 while ((obj = next.get_next()) != 0)
00687 obj->write_ireg(ireg);
00688 ireg->write2(0);
00689 }
00690 }
00691
00692
00693
00694
00695
00696
00697 void Game_map::get_ireg_objects
00698 (
00699 int schunk
00700 )
00701 {
00702 char fname[128];
00703 ifstream ireg_stream;
00704 DataSource *ireg = 0;
00705
00706 if (schunk_cache[schunk] && schunk_cache_sizes[schunk] >= 0) {
00707
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;
00722 }
00723 ireg = new StreamDataSource (&ireg_stream);
00724 }
00725 int scy = 16*(schunk/12);
00726 int scx = 16*(schunk%12);
00727 read_ireg_objects(ireg, scx, scy);
00728
00729 if (schunk == 10*12 + 11 && Game::get_game_type() == SERPENT_ISLE)
00730 {
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
00747
00748
00749 void Read_special_ireg
00750 (
00751 DataSource *ireg,
00752 Game_object *obj
00753 )
00754 {
00755 int type = ireg->read1();
00756 int len = ireg->read2();
00757 unsigned char *buf = new unsigned char[len];
00758 ireg->read(reinterpret_cast<char*>(buf), len);
00759 if (type == IREG_UCSCRIPT)
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
00780
00781
00782 void Game_map::read_special_ireg
00783 (
00784 DataSource *ireg,
00785 Game_object *obj
00786 )
00787 {
00788 unsigned char entlen;
00789 while ((entlen = ireg->peek()) == IREG_SPECIAL && !ireg->eof())
00790 {
00791 ireg->read1();
00792 unsigned char type = ireg->peek();
00793 if (type == IREG_ENDMARK)
00794 {
00795 ireg->read1();
00796 return;
00797 }
00798 Read_special_ireg(ireg, obj);
00799 }
00800 }
00801
00802
00803
00804
00805
00806 static Egg_object *Create_egg
00807 (
00808 unsigned char *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];
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
00831
00832
00833 inline unsigned long Get_quality_flags
00834 (
00835 unsigned char qualbyte
00836 )
00837 {
00838 return ((qualbyte&1) << Obj_flags::invisible) |
00839 (((qualbyte>>3)&1) << Obj_flags::okay_to_take);
00840 }
00841
00842
00843
00844
00845
00846
00847 void Game_map::read_ireg_objects
00848 (
00849 DataSource *ireg,
00850 int scx, int scy,
00851 Game_object *container,
00852 unsigned long flags
00853 )
00854 {
00855 int entlen;
00856 sint8 index_id = -1;
00857 Game_object *last_obj = 0;
00858 Game_window *gwin = Game_window::get_instance();
00859
00860 while (((entlen = ireg->read1(), !ireg->eof())))
00861 {
00862
00863
00864
00865 if (!entlen || entlen == 1)
00866 {
00867 if (container)
00868 return;
00869 else
00870 continue;
00871 }
00872
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
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;
00893 }
00894 unsigned char entry[18];
00895 ireg->read(reinterpret_cast<char*>(entry), entlen);
00896 int cx = entry[0] >> 4;
00897 int cy = entry[1] >> 4;
00898
00899 int tilex = entry[0] & 0xf;
00900 int tiley = entry[1] & 0xf;
00901
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;
00909
00910
00911
00912 if (entlen == 10)
00913 {
00914
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)
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
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 {
00946 oflags = Get_quality_flags(quality);
00947 quality = 0;
00948 }
00949 }
00950 else if (entlen == 12)
00951 {
00952 type = entry[4] + 256*entry[5];
00953 lift = entry[9] >> 4;
00954 quality = entry[7];
00955 oflags =
00956 Get_quality_flags(entry[11]);
00957 if (shnum == 330)
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)
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
00987
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 {
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
01013 if (type)
01014 {
01015 read_ireg_objects(ireg, scx, scy, obj,
01016 oflags & ~(1<<Obj_flags::invisible));
01017 obj->elements_read();
01018 }
01019 }
01020 else
01021 {
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
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;
01037
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
01056
01057
01058 Ireg_game_object *Game_map::create_ireg_object
01059 (
01060 Shape_info& info,
01061 int shnum, int frnum,
01062 int tilex, int tiley,
01063 int lift
01064 )
01065 {
01066 if (info.is_field())
01067 {
01068 if (shnum == 895 ||
01069 shnum == 561)
01070 return new Field_object(shnum, frnum, tilex, tiley,
01071 lift, Egg_object::fire_field);
01072 else if (shnum == 900)
01073 return new Field_object(shnum, frnum, tilex, tiley,
01074 lift, Egg_object::poison_field);
01075 else if (shnum == 902)
01076 return new Field_object(shnum, frnum, tilex, tiley,
01077 lift, Egg_object::sleep_field);
01078 else if (shnum == 756)
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)
01086 return new Egglike_game_object(
01087 shnum, frnum, tilex, tiley, lift);
01088 if (shnum == 848 || shnum == 268)
01089 return new Mirror_object(shnum, frnum, tilex, tiley, lift);
01090 else if (shnum == 761)
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
01112
01113
01114 Ireg_game_object *Game_map::create_ireg_object
01115 (
01116 int shnum, int frnum
01117 )
01118 {
01119 return create_ireg_object(ShapeID::get_info(shnum),
01120 shnum, frnum, 0, 0, 0);
01121 }
01122
01123
01124
01125
01126
01127 Ifix_game_object *Game_map::create_ifix_object
01128 (
01129 int shnum, int frnum
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
01140
01141
01142 void Game_map::get_superchunk_objects
01143 (
01144 int schunk
01145 )
01146 {
01147
01148 get_map_objects(schunk);
01149
01150 get_ifix_objects(schunk);
01151
01152 get_ireg_objects(schunk);
01153
01154 schunk_read[schunk] = 1;
01155 map_patches->apply(schunk);
01156 }
01157
01158
01159
01160
01161
01162
01163
01164 bool Game_map::locate_terrain
01165 (
01166 int tnum,
01167 int& cx, int& cy,
01168
01169 bool upwards
01170 )
01171 {
01172 int cnum;
01173 int cstop;
01174 int dir;
01175 if (upwards)
01176 {
01177 cstop = -1;
01178 dir = -1;
01179 if (cx == -1)
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 {
01196 cx = chunkx;
01197 cy = chunky;
01198
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;
01208 }
01209
01210
01211
01212
01213
01214
01215
01216 bool Game_map::swap_terrains
01217 (
01218 int tnum
01219 )
01220 {
01221 if (tnum < 0 || tnum >= chunk_terrains.size() - 1)
01222 return false;
01223 map_modified = true;
01224
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
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
01245
01246
01247
01248
01249
01250
01251 bool Game_map::insert_terrain
01252 (
01253 int tnum,
01254 bool dup
01255 )
01256 {
01257 if (tnum < -1 || tnum >= chunk_terrains.size())
01258 return false;
01259 get_all_terrain();
01260 map_modified = true;
01261 unsigned char buf[16*16*2];
01262 if (dup && tnum >= 0)
01263 {
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
01279 chunk_terrains.insert(chunk_terrains.begin() + tnum + 1, new_terrain);
01280
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;
01286
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
01299
01300
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;
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 {
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
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
01333
01334
01335 void Game_map::commit_terrain_edits
01336 (
01337 )
01338 {
01339 int num_terrains = chunk_terrains.size();
01340
01341 unsigned char *ters = new unsigned char[num_terrains];
01342 memset(ters, 0, num_terrains);
01343
01344 for (int i = 0; i < num_terrains; i++)
01345 if (chunk_terrains[i] && chunk_terrains[i]->commit_edits())
01346 ters[i] = 1;
01347
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
01355 chunk->set_terrain(chunk->get_terrain());
01356 }
01357 delete [] ters;
01358 }
01359
01360
01361
01362
01363
01364 void Game_map::abort_terrain_edits
01365 (
01366 )
01367 {
01368 int num_terrains = chunk_terrains.size();
01369
01370 for (int i = 0; i < num_terrains; i++)
01371 if (chunk_terrains[i])
01372 chunk_terrains[i]->abort_edits();
01373 }
01374
01375
01376
01377
01378
01379 void Game_map::find_unused_shapes
01380 (
01381 unsigned char *found,
01382 int foundlen
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
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;
01404 int nshapes = sman->get_shapes().get_num_shapes();
01405 if (maxbits > nshapes)
01406 maxbits = nshapes;
01407
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++)
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)
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++)
01437 if (!(found[i/8]&(1<<(i%8))))
01438 cout << "Shape " << i << " not found in game" << endl;
01439 }
01440
01441
01442
01443
01444
01445
01446
01447
01448 Game_object *Game_map::locate_shape
01449 (
01450 int shapenum,
01451 bool upwards,
01452 Game_object *start
01453 )
01454 {
01455 int cx = -1, cy = 0;
01456 int dir = 1;
01457 int stop = c_num_chunks;
01458 if (upwards)
01459 {
01460 dir = -1;
01461 stop = -1;
01462 cx = c_num_chunks;
01463 cy = c_num_chunks - 1;
01464 }
01465 Game_object *obj = 0;
01466 if (start)
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)
01487 {
01488 cx += dir;
01489 if (cx == stop)
01490 {
01491 cy += dir;
01492 if (cy == stop)
01493 break;
01494 cx -= dir*c_num_chunks;
01495 }
01496 Map_chunk *chunk = get_chunk(cx, cy);
01497
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
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
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
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;
01569
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
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
01600 if (schunk_cache[schunk]) {
01601 delete [] schunk_cache[schunk];
01602 schunk_cache[schunk] = 0;
01603 schunk_cache_sizes[schunk] = -1;
01604 }
01605
01606
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
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
01625 for (Actor_vector::const_iterator act=actors.begin(); act!=actors.end(); ++act) {
01626 (*act)->cache_out();
01627 }
01628
01629
01630 for (cy = 0; cy < 16; cy++) for (cx = 0; cx < 16; cx++) {
01631
01632 objects[scx + cx][scy + cy]->kill_cache();
01633 }
01634
01635 schunk_modified[schunk] = false;
01636 map_modified = save_map_modified;
01637 }