00001
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 #include "chunks.h"
00030 #include "chunkter.h"
00031 #include "gamewin.h"
00032 #include "gamemap.h"
00033 #include "shapeinf.h"
00034 #include "citerate.h"
00035 #include "egg.h"
00036 #include "objiter.h"
00037 #include "objs.h"
00038 #include "ordinfo.h"
00039 #include "game.h"
00040 #include "animate.h"
00041 #include "dir.h"
00042 #include "actors.h"
00043
00044 #if 0
00045 #include <iostream>
00046 using std::cout;
00047 using std::endl;
00048 #endif
00049
00050 #ifndef UNDER_CE
00051 using std::memset;
00052 using std::rand;
00053 #endif
00054
00055
00056
00057
00058
00059 Chunk_cache::Chunk_cache
00060 (
00061 ) : egg_objects(4)
00062 {
00063 memset((char *) &blocked[0], 0, sizeof(blocked));
00064 memset((char *) &eggs[0], 0, sizeof(eggs));
00065 }
00066
00067
00068
00069
00070
00071 Chunk_cache::~Chunk_cache
00072 (
00073 )
00074 {
00075 }
00076
00077
00078
00079
00080 unsigned long tmasks[16] = { 0x0L,
00081 0x1L,
00082 0x5L,
00083 0x15L,
00084 0x55L,
00085 0x155L,
00086 0x555L,
00087 0x1555L,
00088 0x5555L,
00089 0x15555L,
00090 0x55555L,
00091 0x155555L,
00092 0x555555L,
00093 0x1555555L,
00094 0x5555555L,
00095 0x15555555L
00096 };
00097
00098
00099
00100
00101
00102
00103
00104
00105 inline void Set_blocked_tile
00106 (
00107 unsigned long *blocked,
00108 int tx, int ty,
00109 int lift,
00110 int ztiles
00111 )
00112 {
00113 unsigned long& val = blocked[ty*c_tiles_per_chunk + tx];
00114
00115 unsigned long mask0 = tmasks[ztiles]<<2*lift;
00116 unsigned long mask1 = mask0<<1;
00117 unsigned long val0s = val&mask0;
00118 unsigned long Nval0s = (~val)&mask0;
00119 unsigned long val1s = val&mask1;
00120 unsigned long newval = val1s | (val0s<<1) | Nval0s | (val1s>>1);
00121
00122 val = (val&~(mask0|mask1)) | newval;
00123 }
00124
00125
00126
00127
00128
00129
00130
00131
00132 inline void Clear_blocked_tile
00133 (
00134 unsigned long *blocked,
00135 int tx, int ty,
00136 int lift,
00137 int ztiles
00138 )
00139 {
00140 unsigned long& val = blocked[ty*c_tiles_per_chunk + tx];
00141
00142 unsigned long mask0 = tmasks[ztiles]<<2*lift;
00143 unsigned long mask1 = mask0<<1;
00144 unsigned long val0s = val&mask0;
00145 unsigned long Nval0s = (~val)&mask0;
00146 unsigned long val1s = val&mask1;
00147 unsigned long newval = (val1s & (val0s<<1)) | ((val1s>>1) & Nval0s);
00148
00149 val = (val&~(mask0|mask1)) | newval;
00150 }
00151
00152
00153
00154
00155
00156 void Chunk_cache::set_blocked
00157 (
00158 int startx, int starty,
00159 int endx, int endy,
00160 int lift, int ztiles,
00161 bool set
00162 )
00163 {
00164 if (set)
00165 {
00166 for (int y = starty; y <= endy; y++)
00167 for (int x = startx; x <= endx; x++)
00168 Set_blocked_tile(blocked, x, y, lift, ztiles);
00169 }
00170 else
00171 {
00172 for (int y = starty; y <= endy; y++)
00173 for (int x = startx; x <= endx; x++)
00174 Clear_blocked_tile(blocked,x, y, lift, ztiles);
00175 }
00176 }
00177
00178
00179
00180
00181
00182 void Chunk_cache::update_object
00183 (
00184 Map_chunk *chunk,
00185 Game_object *obj,
00186 bool add
00187 )
00188 {
00189 Shape_info& info = obj->get_info();
00190 if (info.is_door())
00191 if (add)
00192 doors.append(obj);
00193 else
00194 doors.remove(obj);
00195 int ztiles = info.get_3d_height();
00196 if (!ztiles || !info.is_solid())
00197 return;
00198
00199 int endx = obj->get_tx();
00200 int endy = obj->get_ty();
00201 int frame = obj->get_framenum();
00202 int xtiles = info.get_3d_xtiles(frame);
00203 int ytiles = info.get_3d_ytiles(frame);
00204 int lift = obj->get_lift();
00205 if (xtiles == 1 && ytiles == 1)
00206 {
00207 if (add)
00208 Set_blocked_tile(blocked, endx, endy, lift, ztiles);
00209 else
00210 Clear_blocked_tile(blocked, endx, endy, lift, ztiles);
00211 return;
00212 }
00213 Rectangle footprint = obj->get_footprint();
00214
00215 Chunk_intersect_iterator next_chunk(footprint);
00216 Rectangle tiles;
00217 int cx, cy;
00218 while (next_chunk.get_next(tiles, cx, cy))
00219 gmap->get_chunk(cx, cy)->set_blocked(tiles.x, tiles.y,
00220 tiles.x + tiles.w - 1, tiles.y + tiles.h - 1, lift,
00221 ztiles, add);
00222 }
00223
00224
00225
00226
00227
00228
00229 void Chunk_cache::set_egged
00230 (
00231 Egg_object *egg,
00232 Rectangle& tiles,
00233 bool add
00234 )
00235 {
00236
00237 int eggnum = egg_objects.find(egg);
00238 if (add)
00239 {
00240 if (eggnum < 0)
00241 eggnum = egg_objects.put(egg);
00242 if (eggnum > 15)
00243 eggnum = 15;
00244 short mask = (1<<eggnum);
00245 int stopx = tiles.x + tiles.w, stopy = tiles.y + tiles.h;
00246 for (int ty = tiles.y; ty < stopy; ++ty)
00247 for (int tx = tiles.x; tx < stopx; ++tx)
00248 eggs[ty*c_tiles_per_chunk + tx] |= mask;
00249 }
00250 else
00251 {
00252 if (eggnum < 0 || eggnum >= egg_objects.size())
00253 return;
00254 egg_objects[eggnum] = NULL;
00255 if (eggnum >= 15)
00256 {
00257 for (Egg_vector::const_iterator it =
00258 egg_objects.begin() + 15;
00259 it != egg_objects.end(); ++it)
00260 if (*it != 0)
00261
00262 return;
00263 eggnum = 15;
00264 }
00265 short mask = ~(1<<eggnum);
00266 int stopx = tiles.x + tiles.w, stopy = tiles.y + tiles.h;
00267 for (int ty = tiles.y; ty < stopy; ty++)
00268 for (int tx = tiles.x; tx < stopx; tx++)
00269 eggs[ty*c_tiles_per_chunk + tx] &= mask;
00270 }
00271 }
00272
00273
00274
00275
00276
00277 void Chunk_cache::update_egg
00278 (
00279 Map_chunk *chunk,
00280 Egg_object *egg,
00281 bool add
00282 )
00283 {
00284
00285 Rectangle foot = egg->get_area();
00286 if (!foot.w)
00287 return;
00288 Rectangle crect;
00289 int cx, cy;
00290 if (egg->is_solid_area())
00291 {
00292 Chunk_intersect_iterator all(foot);
00293 while (all.get_next(crect, cx, cy))
00294 gmap->get_chunk(cx, cy)->set_egged(egg, crect, add);
00295 return;
00296 }
00297
00298 Rectangle top(foot.x, foot.y, foot.w, 1);
00299 Rectangle bottom(foot.x, foot.y + foot.h - 1, foot.w, 1);
00300 Rectangle left(foot.x, foot.y + 1, 1, foot.h - 2);
00301 Rectangle right(foot.x + foot.w - 1, foot.y + 1, 1, foot.h - 2);
00302
00303 Chunk_intersect_iterator tops(top);
00304 while (tops.get_next(crect, cx, cy))
00305 gmap->get_chunk(cx, cy)->set_egged(egg, crect, add);
00306 Chunk_intersect_iterator bottoms(bottom);
00307 while (bottoms.get_next(crect, cx, cy))
00308 gmap->get_chunk(cx, cy)->set_egged(egg, crect, add);
00309 Chunk_intersect_iterator lefts(left);
00310 while (lefts.get_next(crect, cx, cy))
00311 gmap->get_chunk(cx, cy)->set_egged(egg, crect, add);
00312 Chunk_intersect_iterator rights(right);
00313 while (rights.get_next(crect, cx, cy))
00314 gmap->get_chunk(cx, cy)->set_egged(egg, crect, add);
00315
00316 }
00317
00318
00319
00320
00321
00322 void Chunk_cache::setup
00323 (
00324 Map_chunk *chunk
00325 )
00326 {
00327 Game_object *obj;
00328 Object_iterator next(chunk->get_objects());
00329 while ((obj = next.get_next()) != 0)
00330 if (obj->is_egg())
00331 update_egg(chunk, (Egg_object *) obj, 1);
00332 else
00333 update_object(chunk, obj, 1);
00334
00335 obj_list = chunk;
00336 }
00337
00338
00339
00340
00341
00342
00343
00344 inline int Chunk_cache::get_highest_blocked
00345 (
00346 int lift,
00347 unsigned long tflags
00348 )
00349 {
00350 int i;
00351 for (i = lift - 1; i >= 0 && !(tflags & (3<<(2*i))); i--)
00352 ;
00353 return i;
00354 }
00355
00356
00357
00358
00359
00360
00361
00362 int Chunk_cache::get_highest_blocked
00363 (
00364 int lift,
00365 int tx, int ty
00366 )
00367 {
00368 return get_highest_blocked(lift, blocked[ty*c_tiles_per_chunk + tx]);
00369 }
00370
00371
00372
00373
00374
00375
00376
00377 inline int Chunk_cache::get_lowest_blocked
00378 (
00379 int lift,
00380 unsigned long tflags
00381 )
00382 {
00383 int i;
00384 for (i = lift; i < 16 && !(tflags & (3<<(2*i))); i++)
00385 ;
00386 if (i == 16) return -1;
00387 return i;
00388 }
00389
00390
00391
00392
00393
00394
00395
00396 int Chunk_cache::get_lowest_blocked
00397 (
00398 int lift,
00399 int tx, int ty
00400 )
00401 {
00402 return get_lowest_blocked(lift, blocked[ty*c_tiles_per_chunk + tx]);
00403 }
00404
00405
00406
00407
00408
00409 inline void Check_terrain
00410 (
00411 Map_chunk *nlist,
00412 int tx, int ty,
00413 int& terrain
00414
00415 )
00416 {
00417 ShapeID flat = nlist->get_flat(tx, ty);
00418 if (!flat.is_invalid())
00419 {
00420 if (flat.get_info().is_water())
00421 terrain |= 2;
00422 else if (flat.get_info().is_solid())
00423 terrain |= 4;
00424 else
00425 terrain |= 1;
00426 }
00427
00428 }
00429
00430
00431
00432
00433
00434
00435
00436
00437
00438 int Chunk_cache::is_blocked
00439 (
00440 int height,
00441
00442 int lift,
00443 int tx, int ty,
00444 int& new_lift,
00445 const int move_flags,
00446 int max_drop,
00447 int max_rise
00448
00449 )
00450 {
00451
00452
00453
00454 if (move_flags & MOVE_ETHEREAL)
00455 {
00456 new_lift = lift;
00457 return 0;
00458 }
00459
00460 unsigned long tflags = blocked[ty*c_tiles_per_chunk + tx];
00461
00462 if (max_rise == -1)
00463 max_rise = (move_flags & MOVE_FLY) ? max_drop : 1;
00464 int max_lift = lift + max_rise;
00465 if (max_lift > 15)
00466 max_lift = 15;
00467 for (new_lift = lift; new_lift <= max_lift; new_lift++)
00468 {
00469 if ((tflags & (3 << (2*new_lift))) == 0)
00470 {
00471 int new_high = get_lowest_blocked(new_lift, tflags);
00472
00473 if (new_high == -1 || new_high >= (new_lift + height))
00474 break;
00475 }
00476 }
00477 if (new_lift > max_lift)
00478 {
00479 new_lift = get_highest_blocked(lift, tflags) + 1;
00480 if (new_lift >= lift)
00481 return 1;
00482 int new_high = get_lowest_blocked(new_lift, tflags);
00483 if (new_high != -1 && new_high < new_lift + height)
00484 return 1;
00485 }
00486 if (new_lift <= lift)
00487 {
00488 new_lift = (move_flags & MOVE_NODROP) ? lift :
00489 get_highest_blocked(lift, tflags) + 1;
00490
00491 if (lift - new_lift > max_drop)
00492 {
00493 if (move_flags & MOVE_MAPEDIT)
00494 new_lift = lift - max_drop;
00495 else
00496 return 1;
00497 }
00498 int new_high = get_lowest_blocked (new_lift, tflags);
00499
00500
00501 if (new_high != -1 && new_high < (new_lift + height))
00502 return 1;
00503 }
00504
00505
00506
00507
00508 if (new_lift == 0)
00509 {
00510 if (move_flags & MOVE_MAPEDIT)
00511 return 0;
00512 int ter = 0;
00513 Check_terrain (obj_list, tx, ty, ter);
00514 if (ter & 2)
00515 {
00516 if (move_flags & (MOVE_FLY+MOVE_SWIM))
00517 return 0;
00518 else
00519 return 1;
00520 }
00521 else if (ter & 1)
00522 {
00523 if (move_flags & (MOVE_FLY|MOVE_WALK))
00524 return 0;
00525 else
00526 return 1;
00527 }
00528 else if (ter & 4)
00529 {
00530 if (move_flags & MOVE_FLY)
00531 return 0;
00532 else
00533 return 1;
00534 }
00535 else
00536 return 0;
00537 }
00538 else if (move_flags & (MOVE_FLY|MOVE_WALK))
00539 return 0;
00540
00541 return 1;
00542 }
00543
00544
00545
00546
00547
00548 void Chunk_cache::activate_eggs
00549 (
00550 Game_object *obj,
00551 Map_chunk *chunk,
00552 int tx, int ty, int tz,
00553 int from_tx, int from_ty,
00554 unsigned short eggbits,
00555 bool now
00556 )
00557 {
00558 int i;
00559 for (i = 0; i < 8*(int)sizeof(eggbits) - 1 && eggbits;
00560 i++, eggbits = eggbits >> 1)
00561 {
00562 Egg_object *egg;
00563 if ((eggbits&1) && i < egg_objects.size() &&
00564 (egg = egg_objects[i]) &&
00565 egg->is_active(obj, tx, ty, tz, from_tx, from_ty))
00566 {
00567 egg->activate(obj, now);
00568 if (chunk->get_cache() != this)
00569 return;
00570 }
00571 }
00572 if (eggbits)
00573 {
00574
00575
00576 int sz = egg_objects.size();
00577 for ( ; i < sz; i++)
00578 {
00579 Egg_object *egg = egg_objects[i];
00580 if (egg && egg->is_active(obj, tx, ty, tz, from_tx, from_ty))
00581 {
00582 egg->activate(obj, now);
00583 if (chunk->get_cache() != this)
00584 return;
00585 }
00586 }
00587 }
00588 }
00589
00590
00591
00592
00593
00594 Game_object *Chunk_cache::find_door
00595 (
00596 Tile_coord tile
00597 )
00598 {
00599 for (Game_object_vector::iterator it = doors.begin();
00600 it != doors.end(); ++it)
00601 if ((*it)->blocks(tile))
00602 return *it;
00603 return 0;
00604 }
00605
00606
00607
00608
00609
00610 Map_chunk::Map_chunk
00611 (
00612 int chunkx, int chunky
00613 ) : objects(0), terrain(0), first_nonflat(0), ice_dungeon(0x00),
00614 dungeon_levels(0), cache(0), roof(0),
00615 dungeon_lights(0), non_dungeon_lights(0),
00616 cx(chunkx), cy(chunky), from_below(0), from_right(0),
00617 from_below_right(0)
00618 {
00619 }
00620
00621
00622
00623
00624
00625 Map_chunk::~Map_chunk
00626 (
00627 )
00628 {
00629 delete cache;
00630 delete [] dungeon_levels;
00631 }
00632
00633
00634
00635
00636
00637
00638 void Map_chunk::set_terrain
00639 (
00640 Chunk_terrain *ter
00641 )
00642 {
00643 if (terrain)
00644 {
00645 terrain->remove_client();
00646
00647 Game_object_vector removes;
00648 {
00649 Object_iterator it(get_objects());
00650 Game_object *each;
00651 while ((each = it.get_next()) != 0)
00652
00653 if (each->as_terrain())
00654 removes.push_back(each);
00655 }
00656 for (Game_object_vector::const_iterator it=removes.begin();
00657 it!=removes.end(); ++it)
00658 (*it)->remove_this();
00659 }
00660 terrain = ter;
00661 terrain->add_client();
00662
00663 for (int tiley = 0; tiley < c_tiles_per_chunk; tiley++)
00664 for (int tilex = 0; tilex < c_tiles_per_chunk; tilex++)
00665 {
00666 ShapeID id = ter->get_flat(tilex, tiley);
00667 Shape_frame *shape = id.get_shape();
00668 if (shape && shape->is_rle())
00669 {
00670 int shapenum = id.get_shapenum(),
00671 framenum = id.get_framenum();
00672 Shape_info& info = id.get_info();
00673 Game_object *obj = info.is_animated() ?
00674 new Animated_object(shapenum,
00675 framenum, tilex, tiley)
00676 : new Terrain_game_object(shapenum,
00677 framenum, tilex, tiley);
00678 add(obj);
00679 }
00680 }
00681 }
00682
00683
00684
00685
00686
00687 void Map_chunk::add_dependencies
00688 (
00689 Game_object *newobj,
00690 Ordering_info& newinfo
00691 )
00692 {
00693 Game_object *obj;
00694 Nonflat_object_iterator next(this);
00695 while ((obj = next.get_next()) != 0)
00696 {
00697
00698
00699 int newcmp = Game_object::compare(newinfo, obj);
00700 int cmp = newcmp == -1 ? 1 : newcmp == 1 ? 0 : -1;
00701 if (!cmp)
00702 {
00703 newobj->dependencies.put(obj);
00704 obj->dependors.put(newobj);
00705 }
00706 else if (cmp == 1)
00707 {
00708 obj->dependencies.put(newobj);
00709 newobj->dependors.put(obj);
00710 }
00711 }
00712 }
00713
00714
00715
00716
00717
00718
00719
00720
00721 inline Map_chunk *Map_chunk::add_outside_dependencies
00722 (
00723 int cx, int cy,
00724 Game_object *newobj,
00725 Ordering_info& newinfo
00726 )
00727 {
00728 Map_chunk *chunk = gmap->get_chunk(cx, cy);
00729 chunk->add_dependencies(newobj, newinfo);
00730 return chunk;
00731 }
00732
00733
00734
00735
00736
00737
00738
00739 void Map_chunk::add
00740 (
00741 Game_object *newobj
00742 )
00743 {
00744 newobj->cx = get_cx();
00745 newobj->cy = get_cy();
00746 Ordering_info ord(gwin, newobj);
00747
00748 if (first_nonflat)
00749 objects.insert_before(newobj, first_nonflat);
00750 else
00751 objects.append(newobj);
00752
00753 if (newobj->get_lift() || ord.info.get_3d_height())
00754 {
00755 int ty = newobj->get_ty();
00756
00757 add_dependencies(newobj, ord);
00758 if (from_below)
00759 add_outside_dependencies(cx, INCR_CHUNK(cy),
00760 newobj, ord);
00761 if (from_right)
00762 add_outside_dependencies(INCR_CHUNK(cx), cy,
00763 newobj, ord);
00764 if (from_below_right)
00765 add_outside_dependencies(INCR_CHUNK(cx),
00766 INCR_CHUNK(cy), newobj, ord);
00767
00768
00769 bool ext_left = (newobj->get_tx() - ord.xs) < 0 && cx > 0;
00770 bool ext_above = (newobj->get_ty() - ord.ys) < 0 && cy > 0;
00771 if (ext_left)
00772 {
00773 add_outside_dependencies(DECR_CHUNK(cx), cy,
00774 newobj, ord)->from_right++;
00775 if (ext_above)
00776 add_outside_dependencies(DECR_CHUNK(cx),
00777 DECR_CHUNK(cy),
00778 newobj, ord)->from_below_right++;
00779 }
00780 if (ext_above)
00781 add_outside_dependencies(cx, DECR_CHUNK(cy),
00782 newobj, ord)->from_below++;
00783 first_nonflat = newobj;
00784 }
00785 if (cache)
00786 cache->update_object(this, newobj, 1);
00787 if (ord.info.is_light_source())
00788 if (dungeon_levels && is_dungeon(newobj->get_tx(),
00789 newobj->get_ty()))
00790 dungeon_lights++;
00791 else
00792 non_dungeon_lights++;
00793 if (newobj->get_lift() >= 5)
00794 {
00795 if (ord.info.get_shape_class() == Shape_info::building)
00796 roof = 1;
00797 }
00798 }
00799
00800
00801
00802
00803
00804 void Map_chunk::add_egg
00805 (
00806 Egg_object *egg
00807 )
00808 {
00809 add(egg);
00810 egg->set_area();
00811
00812 need_cache()->update_egg(this, egg, 1);
00813 }
00814
00815
00816
00817
00818
00819 void Map_chunk::remove_egg
00820 (
00821 Egg_object *egg
00822 )
00823 {
00824 remove(egg);
00825 if (cache)
00826 cache->update_egg(this, egg, 0);
00827 }
00828
00829
00830
00831
00832
00833
00834 void Map_chunk::remove
00835 (
00836 Game_object *remove
00837 )
00838 {
00839 if (cache)
00840 cache->update_object(this, remove, 0);
00841 remove->clear_dependencies();
00842 Game_map *gmap = gwin->get_map();
00843 Shape_info& info = remove->get_info();
00844
00845 int frame = remove->get_framenum(), tx = remove->get_tx(),
00846 ty = remove->get_ty();
00847
00848 bool ext_left = (tx - info.get_3d_xtiles(frame)) < 0 && cx > 0;
00849 bool ext_above = (ty - info.get_3d_ytiles(frame)) < 0 && cy > 0;
00850 if (ext_left)
00851 {
00852 gmap->get_chunk(cx - 1, cy)->from_below_right--;
00853 if (ext_above)
00854 gmap->get_chunk(cx - 1, cy - 1)->from_below_right--;
00855 }
00856 if (ext_above)
00857 gmap->get_chunk(cx, cy - 1)->from_below--;
00858 if (info.is_light_source())
00859 if (dungeon_levels && is_dungeon(tx, ty))
00860 dungeon_lights--;
00861 else
00862 non_dungeon_lights--;
00863 if (remove == first_nonflat)
00864 {
00865 first_nonflat = remove->get_next();
00866 if (first_nonflat == objects.get_first())
00867 first_nonflat = 0;
00868 }
00869 objects.remove(remove);
00870 remove->set_invalid();
00871 }
00872
00873
00874
00875
00876
00877
00878
00879
00880
00881 int Map_chunk::is_blocked
00882 (
00883 int height,
00884 int lift,
00885 int startx, int starty,
00886 int xtiles, int ytiles,
00887 int& new_lift,
00888 const int move_flags,
00889 int max_drop,
00890 int max_rise
00891
00892 )
00893 {
00894 Game_map *gmap = gwin->get_map();
00895 int tx, ty;
00896 new_lift = 0;
00897 startx %= c_num_tiles;
00898 starty %= c_num_tiles;
00899 int stopy = (starty + ytiles)%c_num_tiles,
00900 stopx = (startx + xtiles)%c_num_tiles;
00901 for (ty = starty; ty != stopy; ty = INCR_TILE(ty))
00902 {
00903 int cy = ty/c_tiles_per_chunk, rty = ty%c_tiles_per_chunk;
00904 for (tx = startx; tx != stopx; tx = INCR_TILE(tx))
00905 {
00906 int this_lift;
00907 Map_chunk *olist = gmap->get_chunk(
00908 tx/c_tiles_per_chunk, cy);
00909 olist->setup_cache();
00910 if (olist->is_blocked(height, lift,
00911 tx%c_tiles_per_chunk,
00912 rty, this_lift, move_flags, max_drop,max_rise))
00913 return (1);
00914
00915 new_lift = this_lift > new_lift ?
00916 this_lift : new_lift;
00917 }
00918 }
00919 return (0);
00920 }
00921
00922
00923
00924
00925
00926
00927
00928
00929 int Map_chunk::is_blocked
00930 (
00931 Tile_coord& tile,
00932 int height,
00933 const int move_flags,
00934 int max_drop,
00935 int max_rise
00936
00937 )
00938 {
00939
00940 Game_map *gmap = gwin->get_map();
00941 Map_chunk *chunk = gmap->get_chunk_safely(
00942 tile.tx/c_tiles_per_chunk, tile.ty/c_tiles_per_chunk);
00943 if (!chunk)
00944 return 0;
00945 chunk->setup_cache();
00946 int new_lift;
00947 if (chunk->is_blocked(height, tile.tz, tile.tx%c_tiles_per_chunk,
00948 tile.ty%c_tiles_per_chunk, new_lift, move_flags, max_drop,
00949 max_rise))
00950 return (1);
00951 tile.tz = new_lift;
00952 return (0);
00953 }
00954
00955
00956
00957
00958
00959
00960 int Map_chunk::is_blocked
00961 (
00962
00963 int xtiles, int ytiles, int ztiles,
00964 Tile_coord from,
00965 Tile_coord& to,
00966 const int move_flags,
00967 int max_drop,
00968 int max_rise
00969
00970 )
00971 {
00972 int vertx0, vertx1;
00973
00974 int horizx0, horizx1;
00975
00976 int verty0, verty1;
00977
00978 int horizy0, horizy1;
00979
00980
00981 horizx0 = (to.tx + 1 - xtiles + c_num_tiles)%c_num_tiles;
00982 horizx1 = INCR_TILE(to.tx);
00983 if (Tile_coord::gte(to.tx, from.tx))
00984 {
00985 vertx0 = INCR_TILE(from.tx);
00986 vertx1 = INCR_TILE(to.tx);
00987 }
00988 else
00989 {
00990 vertx0 = (to.tx + 1 - xtiles + c_num_tiles)%c_num_tiles;
00991 vertx1 = (from.tx + 1 - xtiles + c_num_tiles)%c_num_tiles;
00992 }
00993 verty0 = (to.ty + 1 - ytiles + c_num_tiles)%c_num_tiles;
00994 verty1 = INCR_TILE(to.ty);
00995 if (Tile_coord::gte(to.ty, from.ty))
00996 {
00997 horizy0 = INCR_TILE(from.ty);
00998 horizy1 = INCR_TILE(to.ty);
00999 if (to.ty != from.ty)
01000 verty1 = DECR_TILE(verty1);
01001 }
01002 else
01003 {
01004 horizy0 = (to.ty + 1 - ytiles + c_num_tiles)%c_num_tiles;
01005 horizy1 = (from.ty + 1 - ytiles + c_num_tiles)%c_num_tiles;
01006
01007 verty0 = INCR_TILE(verty0);
01008 }
01009 int x, y;
01010 int new_lift = from.tz;
01011 int new_lift0 = -1;
01012 #ifdef DEBUG
01013 assert(Tile_coord::gte(horizy1, horizy0));
01014 assert(Tile_coord::gte(horizx1, horizx0));
01015 assert(Tile_coord::gte(verty1, verty0));
01016 assert(Tile_coord::gte(vertx1, vertx0));
01017 #endif
01018 for (y = horizy0; y != horizy1; y = INCR_TILE(y))
01019 {
01020 int cy = y/c_tiles_per_chunk, rty = y%c_tiles_per_chunk;
01021 for (x = horizx0; x != horizx1; x = INCR_TILE(x))
01022 {
01023 Map_chunk *olist = gmap->get_chunk(
01024 x/c_tiles_per_chunk, cy);
01025 olist->setup_cache();
01026 int rtx = x%c_tiles_per_chunk;
01027 if (olist->is_blocked(ztiles, from.tz, rtx, rty,
01028 new_lift, move_flags, max_drop, max_rise))
01029 return 1;
01030 if (new_lift != from.tz)
01031 if (new_lift0 == -1)
01032 new_lift0 = new_lift;
01033 else if (new_lift != new_lift0)
01034 return (1);
01035 }
01036 }
01037
01038 for (x = vertx0; x != vertx1; x = INCR_TILE(x))
01039 {
01040 int cx = x/c_tiles_per_chunk, rtx = x%c_tiles_per_chunk;
01041 for (y = verty0; y != verty1; y = INCR_TILE(y))
01042 {
01043 Map_chunk *olist = gmap->get_chunk(
01044 cx, y/c_tiles_per_chunk);
01045 olist->setup_cache();
01046 int rty = y%c_tiles_per_chunk;
01047 if (olist->is_blocked(ztiles, from.tz, rtx, rty,
01048 new_lift, move_flags, max_drop, max_rise))
01049 return 1;
01050 if (new_lift != from.tz)
01051 if (new_lift0 == -1)
01052 new_lift0 = new_lift;
01053 else if (new_lift != new_lift0)
01054 return (1);
01055 }
01056 }
01057 to.tz = new_lift;
01058 return (0);
01059 }
01060
01061
01062
01063
01064
01065
01066
01067
01068 static Tile_coord *Get_square
01069 (
01070 Tile_coord& pos,
01071 int dist
01072 )
01073 {
01074 Tile_coord *square = new Tile_coord[8*dist];
01075
01076 square[0] = Tile_coord(DECR_TILE(pos.tx, dist),
01077 DECR_TILE(pos.ty, dist), pos.tz);
01078 int i;
01079 int len = 2*dist + 1;
01080 int out = 1;
01081 for (i = 1; i < len; i++, out++)
01082 square[out] = Tile_coord(INCR_TILE(square[out - 1].tx),
01083 square[out - 1].ty, pos.tz);
01084
01085 for (i = 1; i < len; i++, out++)
01086 square[out] = Tile_coord(square[out - 1].tx,
01087 INCR_TILE(square[out - 1].ty), pos.tz);
01088
01089 for (i = 1; i < len; i++, out++)
01090 square[out] = Tile_coord(DECR_TILE(square[out - 1].tx),
01091 square[out - 1].ty, pos.tz);
01092
01093 for (i = 1; i < len - 1; i++, out++)
01094 square[out] = Tile_coord(square[out - 1].tx,
01095 DECR_TILE(square[out - 1].ty), pos.tz);
01096 return square;
01097 }
01098
01099
01100
01101
01102
01103
01104
01105 inline bool Check_spot
01106 (
01107 Map_chunk::Find_spot_where where,
01108 int tx, int ty, int tz
01109 )
01110 {
01111 Game_map *gmap = Game_window::get_instance()->get_map();
01112 int cx = tx/c_tiles_per_chunk, cy = ty/c_tiles_per_chunk;
01113 Map_chunk *chunk = gmap->get_chunk_safely(cx, cy);
01114 return (where == Map_chunk::inside) ==
01115 (chunk->is_roof(tx % c_tiles_per_chunk,
01116 ty % c_tiles_per_chunk, tz) < 31);
01117 }
01118
01119
01120
01121
01122
01123
01124
01125 Tile_coord Map_chunk::find_spot
01126 (
01127 Tile_coord pos,
01128 int dist,
01129
01130 int shapenum,
01131 int framenum,
01132 int max_drop,
01133 int dir,
01134
01135 Find_spot_where where
01136 )
01137 {
01138 Shape_info& info = ShapeID::get_info(shapenum);
01139 int xs = info.get_3d_xtiles(framenum);
01140 int ys = info.get_3d_ytiles(framenum);
01141 int zs = info.get_3d_height();
01142
01143
01144 const int mflags = MOVE_WALK|MOVE_FLY;
01145 int new_lift;
01146
01147 if (!Map_chunk::is_blocked(zs, pos.tz, pos.tx - xs + 1,
01148 pos.ty - ys + 1, xs, ys, new_lift, mflags, max_drop))
01149 return Tile_coord(pos.tx, pos.ty, new_lift);
01150 if (dir < 0)
01151 dir = rand()%8;
01152 dir = (dir + 1)%8;
01153 for (int d = 1; d <= dist; d++)
01154 {
01155 int square_cnt = 8*d ;
01156
01157 Tile_coord *square = Get_square(pos, d);
01158 int index = dir*d;
01159
01160 index = (index - d/2 + square_cnt)%square_cnt;
01161 for (int cnt = square_cnt; cnt; cnt--, index++)
01162 {
01163 Tile_coord& p = square[index%square_cnt];
01164 if (!Map_chunk::is_blocked(zs, p.tz, p.tx - xs + 1,
01165 p.ty - ys + 1, xs, ys, new_lift, mflags,
01166 max_drop) &&
01167 (where == anywhere ||
01168 Check_spot(where, p.tx, p.ty, new_lift)))
01169 {
01170 Tile_coord ret(p.tx, p.ty, new_lift);
01171 delete [] square;
01172 return ret;
01173 }
01174 }
01175 delete [] square;
01176 }
01177 return Tile_coord(-1, -1, -1);
01178 }
01179
01180
01181
01182
01183
01184
01185
01186
01187 Tile_coord Map_chunk::find_spot
01188 (
01189 Tile_coord pos,
01190 int dist,
01191
01192 Game_object *obj,
01193 int max_drop,
01194 Find_spot_where where
01195 )
01196 {
01197 Tile_coord t2 = obj->get_tile();
01198
01199 int dir = (int) Get_direction(pos.ty - t2.ty, t2.tx - pos.tx);
01200 return find_spot(pos, dist, obj->get_shapenum(), obj->get_framenum(),
01201 max_drop, dir, where);
01202 }
01203
01204
01205
01206
01207
01208
01209
01210 int Map_chunk::find_in_area
01211 (
01212 Game_object_vector& vec,
01213 Rectangle area,
01214 int shapenum,
01215 int framenum
01216 )
01217 {
01218 int savesize = vec.size();
01219
01220 Chunk_intersect_iterator next_chunk(area);
01221 Rectangle tiles;
01222 int eachcx, eachcy;
01223 Game_map *gmap = gwin->get_map();
01224 while (next_chunk.get_next(tiles, eachcx, eachcy))
01225 {
01226 Map_chunk *chunk = gmap->get_chunk_safely(eachcx, eachcy);
01227 if (!chunk)
01228 continue;
01229 Object_iterator next(chunk->objects);
01230 Game_object *each;
01231 while ((each = next.get_next()) != 0)
01232 if (each->get_shapenum() == shapenum &&
01233 each->get_framenum() == framenum &&
01234 tiles.has_point(each->get_tx(), each->get_ty()))
01235 vec.append(each);
01236 }
01237 return vec.size() - savesize;
01238 }
01239
01240
01241
01242
01243
01244
01245
01246 void Map_chunk::try_all_eggs
01247 (
01248 Game_object *obj,
01249 int tx, int ty, int tz,
01250 int from_tx, int from_ty
01251 )
01252 {
01253 static int norecurse = 0;
01254 if (norecurse)
01255 return;
01256 norecurse++;
01257 Game_map *gmap = gwin->get_map();
01258 Tile_coord pos = obj->get_tile();
01259 const int dist = 32;
01260 Rectangle area(pos.tx - dist, pos.ty - dist, 2*dist, 2*dist);
01261
01262 Chunk_intersect_iterator next_chunk(area);
01263 Rectangle tiles;
01264 int eachcx, eachcy;
01265 Egg_vector eggs(40);
01266
01267 while (next_chunk.get_next(tiles, eachcx, eachcy))
01268 {
01269 Map_chunk *chunk = gmap->get_chunk_safely(eachcx, eachcy);
01270 if (!chunk)
01271 continue;
01272 chunk->setup_cache();
01273 Object_iterator next(chunk->objects);
01274 Game_object *each;
01275 while ((each = next.get_next()) != 0)
01276 if (each->is_egg())
01277 {
01278 Egg_object *egg = (Egg_object *) each;
01279
01280 if (egg->get_type() != Egg_object::jukebox &&
01281
01282 egg->get_type() != Egg_object::teleport &&
01283 egg->is_active(obj,
01284 tx, ty, tz, from_tx, from_ty))
01285 eggs.push_back(egg);
01286 }
01287 }
01288 for (Egg_vector::const_iterator it = eggs.begin(); it != eggs.end();
01289 ++it)
01290 (*it)->activate(obj);
01291 norecurse--;
01292 }
01293
01294
01295
01296
01297
01298 void Map_chunk::add_dungeon_levels
01299 (
01300 Rectangle& tiles, unsigned int lift
01301 )
01302 {
01303 if (!dungeon_levels)
01304 {
01305 dungeon_levels = new unsigned char[256/2];
01306 memset(dungeon_levels, 0, 256/2);
01307 }
01308 int endy = tiles.y + tiles.h, endx = tiles.x + tiles.w;
01309 for (int ty = tiles.y; ty < endy; ty++)
01310 {
01311 for (int tx = tiles.x; tx < endx; tx++)
01312 {
01313 int tnum = (ty*c_tiles_per_chunk + tx)/2;
01314 if (GAME_SI)
01315 lift = 5;
01316 if (tx % 2)
01317 {
01318 dungeon_levels[tnum] &= 0x0F;
01319 dungeon_levels[tnum] |= lift << 4;
01320 }
01321 else
01322 {
01323 dungeon_levels[tnum] &= 0xF0;
01324 dungeon_levels[tnum] |= lift;
01325 }
01326 }
01327 }
01328 }
01329
01330
01331
01332
01333
01334 void Map_chunk::setup_dungeon_levels
01335 (
01336 )
01337 {
01338 Game_map *gmap = gwin->get_map();
01339
01340 Object_iterator next(objects);
01341 Game_object *each;
01342 while ((each = next.get_next()) != 0)
01343 {
01344 int shnum = each->get_shapenum();
01345
01346 if (shnum == 983 || shnum == 969 || shnum == 183 ||
01347 shnum == 182 || shnum == 180 || shnum == 324 ||
01348 ((shnum == 941 || shnum == 394) &&
01349 Game::get_game_type() == SERPENT_ISLE))
01350 {
01351
01352 Rectangle area =
01353 (shnum == 941 && each->get_framenum() == 0)
01354 ? Rectangle(cx*c_tiles_per_chunk,
01355 cy*c_tiles_per_chunk,
01356 c_tiles_per_chunk,
01357 c_tiles_per_chunk)
01358 : each->get_footprint();
01359
01360
01361 Chunk_intersect_iterator next_chunk(area);
01362 Rectangle tiles;
01363 int cx, cy;
01364 while (next_chunk.get_next(tiles, cx, cy))
01365 gmap->get_chunk(cx, cy)->add_dungeon_levels(
01366 tiles, each->get_lift());
01367 }
01368 else if (Game::get_game_type() == SERPENT_ISLE && (
01369 shnum == 436 || shnum == 437 || shnum == 444 ||
01370 shnum == 448 || shnum == 466 || shnum == 477))
01371 {
01372
01373
01374 ice_dungeon |= 1 << ( (each->get_tx()>>3) + 2*(each->get_ty()>>3) );
01375
01376 Rectangle area = each->get_footprint();
01377
01378
01379 Chunk_intersect_iterator next_chunk(area);
01380 Rectangle tiles;
01381 int cx, cy;
01382 while (next_chunk.get_next(tiles, cx, cy))
01383 gmap->get_chunk(cx, cy)->add_dungeon_levels(
01384 tiles, each->get_lift());
01385 }
01386 }
01387 if (dungeon_levels)
01388 {
01389 dungeon_lights = non_dungeon_lights = 0;
01390 next.reset();
01391 while ((each = next.get_next()) != 0)
01392 if (each->get_info().is_light_source())
01393 if (is_dungeon(each->get_tx(), each->get_ty()))
01394 dungeon_lights++;
01395 else
01396 non_dungeon_lights++;
01397 }
01398 }
01399
01400
01401
01402
01403
01404
01405 void Map_chunk::gravity
01406 (
01407 Rectangle area,
01408 int lift
01409 )
01410 {
01411 Game_object_vector dropped(20);
01412
01413 Chunk_intersect_iterator next_chunk(area);
01414 Rectangle tiles;
01415 int cx, cy, new_lift;
01416 while (next_chunk.get_next(tiles, cx, cy))
01417 {
01418 Map_chunk *chunk = gmap->get_chunk(cx, cy);
01419 Object_iterator objs(chunk->objects);
01420 Game_object *obj;
01421 while ((obj = objs.get_next()) != 0)
01422 {
01423 if (!obj->is_dragable() &&
01424 !obj->get_info().is_npc())
01425 continue;
01426 Tile_coord t = obj->get_tile();
01427
01428 Rectangle foot = obj->get_footprint();
01429
01430 if (t.tz >= lift && foot.intersects(area) &&
01431
01432 !is_blocked(1, t.tz - 1, foot.x, foot.y,
01433 foot.w, foot.h, new_lift,
01434 MOVE_ALL_TERRAIN, 0) &&
01435 new_lift < t.tz)
01436 dropped.push_back(obj);
01437 }
01438 }
01439 Game_object_vector::const_iterator it;
01440
01441 for (it = dropped.begin(); it != dropped.end(); ++it)
01442 {
01443 Game_object *obj = *it;
01444 Tile_coord t = obj->get_tile();
01445
01446 Rectangle foot = obj->get_footprint();
01447
01448 if (!is_blocked(1, t.tz - 1, foot.x, foot.y,
01449 foot.w, foot.h, new_lift, MOVE_ALL_TERRAIN, 100) &&
01450 new_lift < t.tz)
01451 {
01452 obj->move(t.tx, t.ty, new_lift);
01453 gravity(foot, obj->get_lift() +
01454 obj->get_info().get_3d_height());
01455 }
01456 }
01457 #if 0
01458 for (it = dropped.begin(); it != dropped.end(); ++it)
01459 {
01460 Game_object *obj = *it;
01461
01462 Rectangle foot = obj->get_footprint();
01463 gravity(foot, obj->get_lift() +
01464 obj->get_info().get_3d_height());
01465 }
01466 #endif
01467 }
01468
01469
01470
01471
01472
01473
01474
01475
01476
01477
01478
01479 int Map_chunk::is_roof(int tx, int ty, int lift)
01480 {
01481 #if 1
01482 int height = get_lowest_blocked (lift+4, tx, ty);
01483 #else
01484 int height = get_lowest_blocked (lift+2, tx, ty);
01485 #endif
01486 if (height == -1) return 31;
01487 return height;
01488 }
01489
01490 void Map_chunk::kill_cache()
01491 {
01492
01493 if (terrain) terrain->remove_client();
01494 terrain = 0;
01495
01496
01497 delete cache;
01498 cache = 0;
01499
01500
01501 delete [] dungeon_levels;
01502 dungeon_levels = 0;
01503 }
01504
01505 int Map_chunk::get_obj_actors(Game_object_vector &removes, Actor_vector &actors)
01506 {
01507 int buf_size = 0;
01508 bool failed = false;
01509
01510
01511 Object_iterator it(get_objects());
01512 Game_object *each;
01513 while ((each = it.get_next()) != 0)
01514 {
01515 Actor *actor = each->as_actor();
01516
01517
01518 if (actor == 0 || (each->is_monster() && each->get_flag(Obj_flags::is_temporary))) {
01519 removes.push_back(each);
01520 int ireg_size = each->get_ireg_size();
01521
01522 if (ireg_size < 0) failed = true;
01523 else buf_size += ireg_size;
01524 }
01525
01526 else {
01527 actors.push_back(actor);
01528 }
01529 }
01530
01531 return failed?-1:buf_size;
01532 }