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