1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #ifndef ULTIMA8_WORLD_CURRENTMAP_H
24 #define ULTIMA8_WORLD_CURRENTMAP_H
25 
26 #include "ultima/shared/std/containers.h"
27 #include "ultima/ultima8/usecode/intrinsics.h"
28 #include "ultima/ultima8/misc/direction.h"
29 
30 namespace Ultima {
31 namespace Ultima8 {
32 
33 class Map;
34 class Item;
35 class UCList;
36 class TeleportEgg;
37 class EggHatcherProcess;
38 
39 #define MAP_NUM_CHUNKS  64
40 #define MAP_NUM_TARGET_ITEMS 200
41 
42 class CurrentMap {
43 	friend class World;
44 public:
45 	CurrentMap();
46 	~CurrentMap();
47 
48 	void clear();
49 	void writeback();
50 	void loadMap(Map *map);
51 
52 	//! sets the currently loaded map, without any processing.
53 	//! (Should only be used for loading.)
setMap(Map * map)54 	void setMap(Map *map) {
55 		_currentMap = map;
56 	}
57 
58 	//! Get the map number of the CurrentMap
59 	uint32 getNum() const;
60 
getChunkSize()61 	unsigned int getChunkSize() const {
62 		return _mapChunkSize;
63 	}
64 
65 	//! Add an item to the beginning of the item list
66 	void addItem(Item *item);
67 
68 	//! Add an item to the end of the item list
69 	void addItemToEnd(Item *item);
70 
71 	void removeItemFromList(Item *item, int32 oldx, int32 oldy);
72 	void removeItem(Item *item);
73 
74 	//! Add an item to the list of possible targets (in Crusader)
75 	void addTargetItem(const Item *item);
76 	//! Remove an item from the list of possible targets (in Crusader)
77 	void removeTargetItem(const Item *item);
78 	//! Find the best target item in the given direction from the given start point.
79 	Item *findBestTargetItem(int32 x, int32 y, int32 z, Direction dir, DirectionMode dirmode);
80 
81 	//! Update the fast area for the cameras position
82 	void updateFastArea(int32 from_x, int32 from_y, int32 from_z, int32 to_x, int32 to_y, int32 to_z);
83 
84 	//! search an area for items matching a loopscript
85 	//! \param itemlist the list to return objids in
86 	//! \param loopscript the script to check items against
87 	//! \param scriptsize the size (in bytes) of the loopscript
88 	//! \param item the item around which you want to search, or 0.
89 	//!             if item is 0, search around (x,y)
90 	//! \param range the (square) range to search
91 	//! \param recurse if true, search in containers too
92 	//! \param x x coordinate of search center if item is 0.
93 	//! \param y y coordinate of search center if item is 0.
94 	void areaSearch(UCList *itemlist, const uint8 *loopscript,
95 	                uint32 scriptsize, const Item *item, uint16 range,
96 					bool recurse, int32 x = 0, int32 y = 0) const;
97 
98 	// Surface search: Search above and below an item.
99 	void surfaceSearch(UCList *itemlist, const uint8 *loopscript,
100 	                   uint32 scriptsize, const Item *item, bool above,
101 					   bool below, bool recurse = false) const;
102 
103 	// Surface search: Search above and below an item.
104 	void surfaceSearch(UCList *itemlist, const uint8 *loopscript,
105 	                   uint32 scriptsize, ObjId id,
106 	                   int32 origin[3], int32 dims[2],
107 	                   bool above, bool below, bool recurse = false) const;
108 
109 	// Collision detection. Returns true if the box [x,y,z]-[x-xd,y-yd,z+zd]
110 	// does not collide with any solid items.
111 	// Additionally:
112 	// * If support is not NULL, *support is set to the item supporting
113 	//   the given box, or 0 if it isn't supported.
114 	// * If roof is not NULL, *roof is set to the roof item with the lowest
115 	//   z coordinate that's over the box, or 0 if there is no roof above box.
116 	// * If blocker is not NULL, *blocker will be set to an item blocking
117 	//   the whole box if there is one, or 0 if there is no such item.
118 	// Ignores collisions which were already occurring at the start position.
119 	// NB: isValidPosition doesn't consider item 'item'.
120 	bool isValidPosition(int32 x, int32 y, int32 z,
121 	                     int32 startx, int32 starty, int32 startz,
122 	                     int xd, int yd, int zd, uint32 shapeflags,
123 	                     ObjId item, const Item **support = 0,
124 	                     ObjId *roof = 0, const Item **blocker = 0) const;
125 
126 	// Note that this version of isValidPosition does not look for start
127 	// position collisions.
128 	bool isValidPosition(int32 x, int32 y, int32 z,
129 	                     int xd, int yd, int zd, uint32 shapeflags,
130 	                     ObjId item, const Item **support = 0,
131 						 ObjId *roof = 0, const Item **blocker = 0) const;
132 
133 	// Note that this version of isValidPosition can not take 'flipped'
134 	// into account!
135 	bool isValidPosition(int32 x, int32 y, int32 z, uint32 shape,
136 	                     ObjId item, const Item **support = 0,
137 						 ObjId *roof = 0, const Item **blocker = 0) const;
138 
139 	//! Scan for a valid position for item in directions orthogonal to movedir
140 	bool scanForValidPosition(int32 x, int32 y, int32 z, const Item *item,
141 	                          Direction movedir, bool wantsupport,
142 	                          int32 &tx, int32 &ty, int32 &tz);
143 
144 	struct SweepItem {
SweepItemSweepItem145 		SweepItem(ObjId it, int32 ht, int32 et, bool touch,
146 		          bool touchfloor, bool block, uint8 dir)
147 			: _item(it), _hitTime(ht), _endTime(et), _touching(touch),
148 			  _touchingFloor(touchfloor), _blocking(block), _dirs(dir) { }
149 
150 		ObjId   _item;       // Item that was hit
151 
152 		//
153 		// The time values here are 'normalized' fixed point values
154 		// They range from 0 for the start of the move to 0x4000 for the end of
155 		// The move.
156 		//
157 		// Linear interpolate between the start and end positions using
158 		// hit_time to find where the moving item was when the hit occurs
159 		//
160 
161 		int32   _hitTime;   // if -1, already hitting when sweep started.
162 		int32   _endTime;   // if 0x4000, still hitting when sweep finished
163 
164 		bool    _touching;   // We are only touching (don't actually overlap)
165 		bool    _touchingFloor; // touching and directly below the moving item
166 
167 		bool    _blocking;   // This item blocks the moving item
168 
169 		uint8   _dirs; // Directions in which the item is being hit.
170 		// Bitmask. Bit 0 is x, 1 is y, 2 is z.
171 
172 		// Use this func to get the interpolated location of the hit
GetInterpolatedCoordsSweepItem173 		void GetInterpolatedCoords(int32 out[3], const int32 start[3], const int32 end[3]) const {
174 			for (int i = 0; i < 3; i++)
175 				out[i] = start[i] + ((end[i] - start[i]) * (_hitTime >= 0 ? _hitTime : 0) + (end[i] > start[i] ? 0x2000 : -0x2000)) / 0x4000;
176 		}
177 	};
178 
179 	//! Perform a sweepTest for an item move
180 	//! \param start Start point to sweep from.
181 	//! \param end End point to sweep to.
182 	//! \param dims Bounding size of item to check.
183 	//! \param shapeflags shapeflags of item to check.
184 	//! \param item ObjId of the item being checked. This will allow item to
185 	//!             be skipped from being tested against. Use 0 for no item.
186 	//! \param solid_only If true, only test solid items.
187 	//! \param hit Pointer to a list to fill with items hit. Items are sorted
188 	//!            by SweepItem::hit_time
189 	//! \return false if no items were hit.
190 	//!         true if any items were hit.
191 	bool sweepTest(const int32 start[3], const int32 end[3],
192 	               const int32 dims[3], uint32 shapeflags,
193 	               ObjId item, bool solid_only, Std::list<SweepItem> *hit) const;
194 
195 	TeleportEgg *findDestination(uint16 id);
196 
197 	// Not allowed to modify the list. Remember to use const_iterator
198 	const Std::list<Item *> *getItemList(int32 gx, int32 gy) const;
199 
isChunkFast(int32 cx,int32 cy)200 	bool isChunkFast(int32 cx, int32 cy) const {
201 		// CONSTANTS!
202 		if (cx < 0 || cy < 0 || cx >= MAP_NUM_CHUNKS || cy >= MAP_NUM_CHUNKS)
203 			return false;
204 		return (_fast[cy][cx / 32] & (1 << (cx & 31))) != 0;
205 	}
206 
207 	// A simple trace to find the top item at a specific xy point
208 	const Item *traceTopItem(int32 x, int32 y, int32 ztop, int32 zbot, ObjId ignore, uint32 shflags);
209 
210 	// Set the entire map as being 'fast'
211 	void setWholeMapFast();
212 
213 	void save(Common::WriteStream *ws);
214 	bool load(Common::ReadStream *rs, uint32 version);
215 
216 	INTRINSIC(I_canExistAt);
217 	INTRINSIC(I_canExistAtPoint);
218 
219 private:
220 	void loadItems(const Std::list<Item *> &itemlist, bool callCacheIn);
221 	void createEggHatcher();
222 
223 	//! clip the given map chunk numbers to iterate over them safely
224 	static void clipMapChunks(int &minx, int &maxx, int &miny, int &maxy);
225 
226 	Map *_currentMap;
227 
228 	// item lists. Lots of them :-)
229 	// items[x][y]
230 	Std::list<Item *> _items[MAP_NUM_CHUNKS][MAP_NUM_CHUNKS];
231 
232 	ProcId _eggHatcher;
233 
234 	// Fast area bit masks -> fast[ry][rx/32]&(1<<(rx&31));
235 	uint32 _fast[MAP_NUM_CHUNKS][MAP_NUM_CHUNKS / 32];
236 	int32 _fastXMin, _fastYMin, _fastXMax, _fastYMax;
237 
238 	int _mapChunkSize;
239 
240 	//! Items that are "targetable" in Crusader. It might be faster to store
241 	//! this in a more fancy data structure, but this works fine.
242 	ObjId _targets[MAP_NUM_TARGET_ITEMS];
243 
244 	void setChunkFast(int32 cx, int32 cy);
245 	void unsetChunkFast(int32 cx, int32 cy);
246 };
247 
248 } // End of namespace Ultima8
249 } // End of namespace Ultima
250 
251 #endif
252