1 /* GemRB - Infinity Engine Emulator
2  * Copyright (C) 2003 The GemRB Project
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  *
19  */
20 
21 #include "TileMap.h"
22 
23 #include "Interface.h"
24 #include "Video.h"
25 
26 #include "Scriptable/Container.h"
27 #include "Scriptable/Door.h"
28 #include "Scriptable/InfoPoint.h"
29 
30 namespace GemRB {
31 
TileMap(void)32 TileMap::TileMap(void)
33 {
34 }
35 
~TileMap(void)36 TileMap::~TileMap(void)
37 {
38 	ClearOverlays();
39 
40 	for (const InfoPoint *infoPoint : infoPoints) {
41 		delete infoPoint;
42 	}
43 
44 	for (const Door *door : doors) {
45 		delete door;
46 	}
47 }
48 
49 //this needs in case of a tileset switch (for extended night)
ClearOverlays()50 void TileMap::ClearOverlays()
51 {
52 	for (const TileOverlay *overlay : overlays) {
53 		delete overlay;
54 	}
55 	for (const TileOverlay *rain : rain_overlays) {
56 		delete rain;
57 	}
58 	overlays.clear();
59 	rain_overlays.clear();
60 }
61 
62 //tiled objects
AddTile(const char * ID,const char * Name,unsigned int Flags,unsigned short * openindices,int opencount,unsigned short * closeindices,int closecount)63 TileObject* TileMap::AddTile(const char *ID, const char* Name, unsigned int Flags,
64 	unsigned short* openindices, int opencount, unsigned short* closeindices, int closecount)
65 {
66 	TileObject* tile = new TileObject();
67 	tile->Flags=Flags;
68 	strnspccpy(tile->Name, Name, 32);
69 	strnlwrcpy(tile->Tileset, ID, 8);
70 	tile->SetOpenTiles( openindices, opencount );
71 	tile->SetClosedTiles( closeindices, closecount );
72 	tiles.push_back(tile);
73 	return tile;
74 }
75 
GetTile(unsigned int idx)76 TileObject* TileMap::GetTile(unsigned int idx)
77 {
78 	if (idx >= tiles.size()) {
79 		return NULL;
80 	}
81 	return tiles[idx];
82 }
83 
84 //doors
AddDoor(const char * ID,const char * Name,unsigned int Flags,int ClosedIndex,unsigned short * indices,int count,DoorTrigger && dt)85 Door* TileMap::AddDoor(const char *ID, const char* Name, unsigned int Flags,
86 	int ClosedIndex, unsigned short* indices, int count, DoorTrigger&& dt)
87 {
88 	Door* door = new Door(overlays[0], std::move(dt));
89 	door->Flags = Flags;
90 	door->closedIndex = ClosedIndex;
91 	door->SetTiles( indices, count );
92 	door->SetName( ID );
93 	door->SetScriptName( Name );
94 	doors.push_back( door );
95 	return door;
96 }
97 
GetDoor(size_t idx) const98 Door* TileMap::GetDoor(size_t idx) const
99 {
100 	if (idx >= doors.size()) {
101 		return NULL;
102 	}
103 	return doors[idx];
104 }
105 
GetDoor(const Point & p) const106 Door* TileMap::GetDoor(const Point &p) const
107 {
108 	for (Door* door : doors) {
109 		if (door->HitTest(p)) return door;
110 	}
111 	return NULL;
112 }
113 
GetDoorByPosition(const Point & p) const114 Door* TileMap::GetDoorByPosition(const Point &p) const
115 {
116 	for (Door *door : doors) {
117 		if (door->toOpen[0].x==p.x && door->toOpen[0].y==p.y) {
118 			return door;
119 		}
120 		if (door->toOpen[1].x==p.x && door->toOpen[1].y==p.y) {
121 			return door;
122 		}
123 	}
124 	return NULL;
125 }
126 
GetDoor(const char * Name) const127 Door* TileMap::GetDoor(const char* Name) const
128 {
129 	if (!Name) {
130 		return NULL;
131 	}
132 	for (Door *door : doors) {
133 		if (stricmp( door->GetScriptName(), Name ) == 0)
134 			return door;
135 	}
136 	return NULL;
137 }
138 
UpdateDoors()139 void TileMap::UpdateDoors()
140 {
141 	for (Door *door : doors) {
142 		door->SetNewOverlay(overlays[0]);
143 	}
144 }
145 
146 // used during time compression in bg1 ... but potentially problematic, so we don't enable it elsewhere
AutoLockDoors() const147 void TileMap::AutoLockDoors() const
148 {
149 	if (!core->HasFeature(GF_RANDOM_BANTER_DIALOGS)) return;
150 
151 	for (Door *door : doors) {
152 		if (door->CantAutoClose()) continue;
153 		if (core->Roll(1, 2, -1)) continue; // just a guess
154 		door->SetDoorOpen(false, false, 0);
155 	}
156 }
157 
158 //overlays, allow pushing of NULL
AddOverlay(TileOverlay * overlay)159 void TileMap::AddOverlay(TileOverlay* overlay)
160 {
161 	if (overlay) {
162 		if (overlay->w > XCellCount) {
163 			XCellCount = overlay->w;
164 		}
165 		if (overlay->h > YCellCount) {
166 			YCellCount = overlay->h;
167 		}
168 	}
169 	overlays.push_back( overlay );
170 }
171 
AddRainOverlay(TileOverlay * overlay)172 void TileMap::AddRainOverlay(TileOverlay* overlay)
173 {
174 	if (overlay) {
175 		if (overlay->w > XCellCount) {
176 			XCellCount = overlay->w;
177 		}
178 		if (overlay->h > YCellCount) {
179 			YCellCount = overlay->h;
180 		}
181 	}
182 	rain_overlays.push_back( overlay );
183 }
184 
DrawOverlays(const Region & viewport,bool rain,BlitFlags flags)185 void TileMap::DrawOverlays(const Region& viewport, bool rain, BlitFlags flags)
186 {
187 	overlays[0]->Draw(viewport, rain ? rain_overlays : overlays, flags);
188 }
189 
190 //containers
AddContainer(Container * c)191 void TileMap::AddContainer(Container *c)
192 {
193 	containers.push_back(c);
194 }
195 
GetContainer(unsigned int idx) const196 Container* TileMap::GetContainer(unsigned int idx) const
197 {
198 	if (idx >= containers.size()) {
199 		return NULL;
200 	}
201 	return containers[idx];
202 }
203 
GetContainer(const char * Name) const204 Container* TileMap::GetContainer(const char* Name) const
205 {
206 	for (Container *container : containers) {
207 		if (stricmp(container->GetScriptName(), Name) == 0) {
208 			return container;
209 		}
210 	}
211 	return NULL;
212 }
213 
214 //look for a container at position
215 //use type = IE_CONTAINER_PILE if you want to find ground piles only
216 //in this case, empty piles won't be found!
GetContainer(const Point & position,int type) const217 Container* TileMap::GetContainer(const Point &position, int type) const
218 {
219 	for (Container *container : containers) {
220 		if (type != -1 && type != container->Type) {
221 			continue;
222 		}
223 
224 		if (!container->BBox.PointInside(position)) continue;
225 
226 		//IE piles don't have polygons, the bounding box is enough for them
227 		if (container->Type == IE_CONTAINER_PILE) {
228 			//don't find empty piles if we look for any container
229 			//if we looked only for piles, then we still return them
230 			if ((type == -1) && !container->inventory.GetSlotCount()) {
231 				continue;
232 			}
233 			return container;
234 		} else if (container->outline->PointIn(position)) {
235 			return container;
236 		}
237 	}
238 	return NULL;
239 }
240 
GetContainerByPosition(const Point & position,int type) const241 Container* TileMap::GetContainerByPosition(const Point &position, int type) const
242 {
243 	for (Container *container : containers) {
244 		if (type != -1 && type != container->Type) {
245 			continue;
246 		}
247 
248 		if (container->Pos.x != position.x || container->Pos.y != position.y) {
249 			continue;
250 		}
251 
252 		//IE piles don't have polygons, the bounding box is enough for them
253 		if (container->Type == IE_CONTAINER_PILE) {
254 			//don't find empty piles if we look for any container
255 			//if we looked only for piles, then we still return them
256 			if ((type == -1) && !container->inventory.GetSlotCount()) {
257 				continue;
258 			}
259 			return container;
260 		}
261 		return container;
262 	}
263 	return NULL;
264 }
265 
CleanupContainer(Container * container)266 int TileMap::CleanupContainer(Container *container)
267 {
268 	if (container->Type!=IE_CONTAINER_PILE)
269 		return 0;
270 	if (container->inventory.GetSlotCount())
271 		return 0;
272 
273 	for (size_t i = 0; i < containers.size(); i++) {
274 		if (containers[i]==container) {
275 			containers.erase(containers.begin()+i);
276 			delete container;
277 			return 1;
278 		}
279 	}
280 	Log(ERROR, "TileMap", "Invalid container cleanup: %s",
281 		container->GetScriptName());
282 	return 1;
283 }
284 
285 //infopoints
AddInfoPoint(const char * Name,unsigned short Type,std::shared_ptr<Gem_Polygon> outline)286 InfoPoint* TileMap::AddInfoPoint(const char* Name, unsigned short Type, std::shared_ptr<Gem_Polygon> outline)
287 {
288 	InfoPoint* ip = new InfoPoint();
289 	ip->SetScriptName( Name );
290 	switch (Type) {
291 		case 0:
292 			ip->Type = ST_PROXIMITY;
293 			break;
294 
295 		case 1:
296 			ip->Type = ST_TRIGGER;
297 			break;
298 
299 		case 2:
300 			ip->Type = ST_TRAVEL;
301 			break;
302 		//this is just to satisfy whiny compilers
303 		default:
304 			ip->Type = ST_PROXIMITY;
305 			break;
306 	}
307 	ip->outline = outline;
308 	if (ip->outline)
309 		ip->BBox = outline->BBox;
310 	//ip->Active = true; //set active on creation
311 	infoPoints.push_back( ip );
312 	return ip;
313 }
314 
315 //if detectable is set, then only detectable infopoints will be returned
GetInfoPoint(const Point & p,bool detectable) const316 InfoPoint* TileMap::GetInfoPoint(const Point &p, bool detectable) const
317 {
318 	for (InfoPoint *infoPoint : infoPoints) {
319 		//these flags disable any kind of user interaction
320 		//scripts can still access an infopoint by name
321 		if (infoPoint->Flags & (INFO_DOOR | TRAP_DEACTIVATED))
322 			continue;
323 
324 		if (detectable) {
325 			if (infoPoint->Type == ST_PROXIMITY && !infoPoint->VisibleTrap(0)) {
326 				continue;
327 			}
328 			// skip portals without PORTAL_CURSOR set
329 			if (infoPoint->IsPortal() && !(infoPoint->Trapped & PORTAL_CURSOR)) {
330 					continue;
331 			}
332 		}
333 
334 		if (!(infoPoint->GetInternalFlag() & IF_ACTIVE))
335 			continue;
336 
337 		if (infoPoint->outline) {
338 			if (infoPoint->outline->PointIn(p)) {
339 				return infoPoint;
340 			}
341 		} else if (infoPoint->BBox.PointInside(p)) {
342 			return infoPoint;
343 		}
344 	}
345 	return NULL;
346 }
347 
GetInfoPoint(const char * Name) const348 InfoPoint* TileMap::GetInfoPoint(const char* Name) const
349 {
350 	for (InfoPoint *infoPoint : infoPoints) {
351 		if (stricmp(infoPoint->GetScriptName(), Name) == 0) {
352 			return infoPoint;
353 		}
354 	}
355 	return NULL;
356 }
357 
GetInfoPoint(unsigned int idx) const358 InfoPoint* TileMap::GetInfoPoint(unsigned int idx) const
359 {
360 	if (idx >= infoPoints.size()) {
361 		return NULL;
362 	}
363 	return infoPoints[idx];
364 }
365 
GetTravelTo(const char * Destination) const366 InfoPoint* TileMap::GetTravelTo(const char* Destination) const
367 {
368 	for (InfoPoint *infoPoint : infoPoints) {
369 		if (infoPoint->Type != ST_TRAVEL) continue;
370 
371 		if (strnicmp(infoPoint->Destination, Destination, 8) == 0) {
372 			return infoPoint;
373 		}
374 	}
375 	return NULL;
376 }
377 
AdjustNearestTravel(Point & p)378 InfoPoint *TileMap::AdjustNearestTravel(Point &p)
379 {
380 	int min = -1;
381 	InfoPoint *best = NULL;
382 
383 	for (InfoPoint *infoPoint : infoPoints) {
384 		if (infoPoint->Type != ST_TRAVEL) continue;
385 
386 		unsigned int dist = Distance(p, infoPoint);
387 		if (dist<(unsigned int) min) {
388 			min = dist;
389 			best = infoPoint;
390 		}
391 	}
392 	if (best) {
393 		p = best->Pos;
394 	}
395 	return best;
396 }
397 
GetMapSize()398 Size TileMap::GetMapSize()
399 {
400 	return Size((XCellCount*64), (YCellCount*64));
401 }
402 
403 }
404