1 /** 2 ** Chunks.h - Chunks (16x16 tiles) on the map. 3 ** 4 ** Written: 10/1/98 - JSF 5 **/ 6 7 /* 8 Copyright (C) 2001 The Exult Team 9 10 This program is free software; you can redistribute it and/or 11 modify it under the terms of the GNU General Public License 12 as published by the Free Software Foundation; either version 2 13 of the License, or (at your option) any later version. 14 15 This program is distributed in the hope that it will be useful, 16 but WITHOUT ANY WARRANTY; without even the implied warranty of 17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 GNU General Public License for more details. 19 20 You should have received a copy of the GNU General Public License 21 along with this program; if not, write to the Free Software 22 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 23 */ 24 25 #ifndef CHUNKS_H 26 #define CHUNKS_H 27 28 29 #include "chunkter.h" 30 #include "exult_constants.h" 31 #include "objlist.h" 32 #include "rect.h" 33 #include "shapeid.h" 34 #include "tiles.h" 35 36 #include <memory> 37 #include <set> 38 39 class Map_chunk; 40 class Egg_object; 41 class Game_object; 42 class Game_map; 43 class Actor; 44 class Npc_actor; 45 class Image_buffer8; 46 class Chunk_terrain; 47 class ODataSource; 48 class Ordering_info; 49 50 /* 51 * Data cached for a chunk to speed up processing, but which doesn't need 52 * to be saved to disk: 53 */ 54 class Chunk_cache : public Game_singletons, 55 public std::enable_shared_from_this<Chunk_cache> { 56 public: 57 // For each tile, 2 bits for each lift 58 // level for #objs blocking there, so 59 // 8 lifts are represented. 60 using blocked8z = std::unique_ptr<uint16[]>; 61 62 private: 63 Map_chunk *obj_list; 64 // One for each 8 lifts. 65 std::vector<blocked8z> blocked; 66 // ->eggs which influence this chunk. 67 std::vector<Egg_object *> egg_objects; 68 // Bit #i (0-14) set means that the 69 // tile is within egg_object[i]'s 70 // influence. Bit 15 means it's 1 or 71 // more of egg_objects[15-(num_eggs-1)]. 72 unsigned short eggs[256]; 73 // Keep special list of doors. 74 std::set<Game_object *> doors; 75 get_num_eggs()76 int get_num_eggs() { 77 return egg_objects.size(); 78 } 79 blocked8z &new_blocked_level(int zlevel); need_blocked_level(int zlevel)80 blocked8z &need_blocked_level(int zlevel) { 81 return (static_cast<unsigned>(zlevel) >= blocked.size() || !blocked[zlevel]) 82 ? new_blocked_level(zlevel) : blocked[zlevel]; 83 } 84 // Set/unset blocked region. 85 void set_blocked(int startx, int starty, int endx, int endy, 86 int lift, int ztiles); 87 void clear_blocked(int startx, int starty, int endx, int endy, 88 int lift, int ztiles); 89 // Add/remove object. 90 void update_object(Map_chunk *chunk, 91 Game_object *obj, bool add); 92 // Set area within egg's influence. 93 void set_egged(Egg_object *egg, TileRect &tiles, bool add); 94 // Add egg. 95 void update_egg(Map_chunk *chunk, Egg_object *egg, bool add); 96 // Set up with chunk's data. 97 void setup(Map_chunk *chunk); 98 void set_tflags(int tx, int ty, int maxz); // Setup flags. 99 // Get highest lift blocked below a 100 // given level for a desired tile. 101 int get_highest_blocked(int lift); 102 int get_highest_blocked(int lift, int tx, int ty); 103 int get_lowest_blocked(int lift); 104 int get_lowest_blocked(int lift, int tx, int ty); 105 // Is a spot occupied or inaccessible 106 // to an NPC? 107 bool is_blocked(int height, int lift, int tx, int ty, int &new_lift, 108 const int move_flags, int max_drop = 1, int max_rise = -1); 109 // Activate eggs nearby. 110 void activate_eggs(Game_object *obj, Map_chunk *chunk, 111 int tx, int ty, int tz, int from_tx, int from_ty, 112 unsigned short eggbits, bool now); activate_eggs(Game_object * obj,Map_chunk * chunk,int tx,int ty,int tz,int from_tx,int from_ty,bool now)113 void activate_eggs(Game_object *obj, Map_chunk *chunk, 114 int tx, int ty, int tz, int from_tx, int from_ty, bool now) { 115 unsigned short eggbits = eggs[ 116 (ty % c_tiles_per_chunk) * c_tiles_per_chunk + 117 (tx % c_tiles_per_chunk)]; 118 if (eggbits) 119 activate_eggs(obj, chunk, tx, ty, tz, 120 from_tx, from_ty, eggbits, now); 121 } 122 // Find door blocking given tile. 123 Game_object *find_door(const Tile_coord& t); 124 125 public: 126 friend class Map_chunk; 127 Chunk_cache(); 128 129 // Is there something on this tile? is_tile_occupied(int tx,int ty,int tz)130 inline bool is_tile_occupied(int tx, int ty, int tz) { 131 const auto *b8 = static_cast<unsigned>(tz / 8) < blocked.size() ? blocked[tz / 8].get() : nullptr; 132 return b8 && (b8[ty * c_tiles_per_chunk + tx] & (3 << (2 * (tz % 8)))); 133 } 134 }; 135 136 /* 137 * Game objects are stored in a list for each chunk, sorted from top-to- 138 * bottom, left-to-right. 139 */ 140 class Map_chunk : public Game_singletons { 141 Game_map *map; // Map we're a part of. 142 Chunk_terrain *terrain; // Flat landscape tiles. 143 // ->first in list of all objs. 'Flat' 144 // obs. (lift=0,ht=0) stored 1st. 145 Object_list objects; 146 // ->first nonflat in 'objects'. 147 // Counts of overlapping objects from 148 // chunks below, to right. 149 Game_object *first_nonflat; 150 unsigned char from_below, from_right, from_below_right; 151 unsigned char ice_dungeon; // For SI, chunk split into 4 quadrants 152 std::unique_ptr<unsigned char[]> dungeon_levels; // A 'dungeon' level value for each tile. 153 std::shared_ptr<Chunk_cache> cache; // Data for chunks near player. 154 unsigned char roof; // 1 if a roof present. 155 // # light sources in chunk. 156 std::set<Game_object*> dungeon_lights; 157 std::set<Game_object*> non_dungeon_lights; 158 unsigned char cx, cy; // Absolute chunk coords. of this. 159 bool selected; // For 'select_chunks' mode. 160 void add_dungeon_levels(TileRect &tiles, unsigned int lift); 161 void add_dependencies(Game_object *newobj, 162 Ordering_info &newinfo); 163 static Map_chunk *add_outside_dependencies(int cx, 164 int cy, Game_object *newobj, Ordering_info &newinfo); 165 166 public: 167 friend class Npc_actor; 168 friend class Game_object; 169 Map_chunk(Game_map *m, int chunkx, int chunky); get_map()170 Game_map *get_map() const { 171 return map; 172 } get_terrain()173 Chunk_terrain *get_terrain() const { 174 return terrain; 175 } 176 void set_terrain(Chunk_terrain *ter); 177 void add(Game_object *obj); // Add an object. 178 void add_egg(Egg_object *egg); // Add/remove an egg. 179 void remove_egg(Egg_object *egg); 180 void remove(Game_object *remove); // Remove an object. 181 // Apply gravity over given area. 182 static void gravity(TileRect const &area, int lift); 183 // Is there a roof? Returns height 184 int is_roof(int tx, int ty, int lift); 185 get_objects()186 Object_list &get_objects() { 187 return objects; 188 } get_first_nonflat()189 Game_object *get_first_nonflat() const { 190 return first_nonflat; 191 } 192 get_cx()193 int get_cx() const { 194 return cx; 195 } get_cy()196 int get_cy() const { 197 return cy; 198 } set_selected(bool tf)199 void set_selected(bool tf) { 200 selected = tf; 201 } is_selected()202 bool is_selected() const { 203 return selected; 204 } get_dungeon_lights()205 const std::set<Game_object*>& get_dungeon_lights() const { // Get #lights. 206 return dungeon_lights; 207 } get_non_dungeon_lights()208 const std::set<Game_object*>& get_non_dungeon_lights() const { 209 return non_dungeon_lights; 210 } get_flat(int tilex,int tiley)211 ShapeID get_flat(int tilex, int tiley) const { 212 return terrain ? terrain->get_flat(tilex, tiley) 213 : ShapeID(); 214 } get_rendered_flats()215 Image_buffer8 *get_rendered_flats() { 216 return terrain ? terrain->get_rendered_flats() : nullptr; 217 } 218 // Get/create/setup cache. get_cache()219 Chunk_cache *get_cache() const { 220 return cache.get(); 221 } need_cache()222 Chunk_cache *need_cache() { 223 setup_cache(); 224 return get_cache(); 225 } setup_cache()226 void setup_cache() { 227 if (!cache) { 228 cache = std::make_shared<Chunk_cache>(); 229 cache->setup(this); 230 } 231 } 232 // Set/unset blocked region. set_blocked(int startx,int starty,int endx,int endy,int lift,int ztiles,bool set)233 void set_blocked(int startx, int starty, int endx, int endy, 234 int lift, int ztiles, bool set) { 235 Chunk_cache *cache = need_cache(); 236 if (set) 237 cache->set_blocked(startx, starty, endx, endy, 238 lift, ztiles); 239 else 240 cache->clear_blocked(startx, starty, endx, endy, 241 lift, ztiles); 242 } 243 // Get highest lift blocked. get_highest_blocked(int lift,int tx,int ty)244 int get_highest_blocked(int lift, int tx, int ty) { 245 return need_cache()->get_highest_blocked(lift, tx, ty); 246 } get_lowest_blocked(int lift,int tx,int ty)247 int get_lowest_blocked(int lift, int tx, int ty) { 248 return need_cache()->get_lowest_blocked(lift, tx, ty); 249 } 250 // Is a spot occupied or inaccessible 251 // to an NPC? 252 bool is_blocked(int height, int lift, int tx, int ty, int &new_lift, 253 const int move_flags, int max_drop = 1, int max_rise = -1) { 254 return cache->is_blocked(height, lift, tx, ty, new_lift, 255 move_flags, max_drop, max_rise); 256 } 257 // Check range. 258 static bool is_blocked(int height, int lift, int startx, int starty, 259 int xtiles, int ytiles, int &new_lift, const int move_flags, 260 int max_drop, int max_rise = -1); 261 // Check absolute tile. 262 static bool is_blocked(Tile_coord &tile, int height, 263 const int move_flags, int max_drop = 1, int max_rise = -1); 264 // Check for > 1x1 object. 265 static bool is_blocked(int xtiles, int ytiles, int ztiles, 266 Tile_coord const &from, Tile_coord &to, const int move_flags, 267 int max_drop = 1, int max_rise = -1); 268 // Check tile WITHIN chunk. is_tile_occupied(int tx,int ty,int tz)269 bool is_tile_occupied(int tx, int ty, int tz) { 270 return need_cache()->is_tile_occupied(tx, ty, tz); 271 } 272 enum Find_spot_where { // For find_spot() below. 273 anywhere = 0, 274 inside, // Must be inside. 275 outside // Must be outside, 276 }; 277 // Find spot for an object. 278 static Tile_coord find_spot(Tile_coord pos, int dist, 279 int shapenum, int framenum, int max_drop = 0, int dir = -1, 280 Find_spot_where where = anywhere); 281 // For approaching 'pos' by an object: 282 static Tile_coord find_spot(Tile_coord const &pos, int dist, 283 Game_object *obj, int max_drop = 0, 284 Find_spot_where where = anywhere); 285 // Set area within egg's influence. set_egged(Egg_object * egg,TileRect & tiles,bool add)286 void set_egged(Egg_object *egg, TileRect &tiles, bool add) { 287 need_cache()->set_egged(egg, tiles, add); 288 } 289 void activate_eggs(Game_object *obj, int tx, int ty, int tz, 290 int from_tx, int from_ty, bool now = false) { 291 need_cache()->activate_eggs(obj, 292 this, tx, ty, tz, from_tx, from_ty, now); 293 } 294 // Find door blocking given tile. find_door(Tile_coord const & t)295 Game_object *find_door(Tile_coord const &t) { 296 return need_cache()->find_door(t); 297 } 298 static int find_in_area(std::vector<Game_object *> &vec, TileRect const &area, 299 int shapenum, int framenum); 300 // Use this when teleported in. 301 static void try_all_eggs(Game_object *obj, int tx, int ty, int tz, 302 int from_tx, int from_ty); 303 void setup_dungeon_levels(); // Set up after IFIX objs. read. has_dungeon()304 inline bool has_dungeon() { // Any tiles within dungeon? 305 return dungeon_levels != nullptr; 306 } 307 308 // NOTE: The following should only be 309 // called if has_dungeon()==1. is_dungeon(int tx,int ty)310 inline int is_dungeon(int tx, int ty) { 311 // Is object within dungeon? (returns height) 312 return dungeon_levels[ty * c_tiles_per_chunk + tx]; 313 } 314 // Is the dungeon an ICE dungeon.NOTE: This is a 315 // Hack and splits the chunk into 4 parts. Only if is_ice_dungeon(int tx,int ty)316 inline bool is_ice_dungeon(int tx, int ty) { // all 4 are ice, will we have an ice dungeon 317 ignore_unused_variable_warning(tx, ty); 318 return ice_dungeon == 0x0F;//0 != ((ice_dungeon >> ( (tx>>3) + 2*(ty>>3) ) )&1); 319 } 320 321 // Kill the items and the cache 322 void kill_cache(); 323 // Get all objects and actors for use when writing memory cache. 324 // returns size require to save 325 int get_obj_actors(std::vector<Game_object *> &removes, 326 std::vector<Actor *> &actors); 327 328 void write(ODataSource& out, bool v2); 329 }; 330 331 #endif 332