factored out the EFFv2 saving into EFFImporter
[gemrb.git] / gemrb / core / TileMap.cpp
blobe00920d63ebebc97c6d936e6ff0e7cfc621c9412
1 /* GemRB - Infinity Engine Emulator
2 * Copyright (C) 2003 The GemRB Project
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.
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.
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.
21 #include "TileMap.h"
23 #include "Interface.h"
24 #include "Video.h"
26 TileMap::TileMap(void)
28 XCellCount = 0;
29 YCellCount = 0;
30 LargeMap = !core->HasFeature(GF_SMALL_FOG);
33 TileMap::~TileMap(void)
35 size_t i;
37 for (i = 0; i < overlays.size(); i++) {
38 delete( overlays[i] );
40 for (i = 0; i < overlays.size(); i++) {
41 delete( rain_overlays[i]);
43 for (i = 0; i < infoPoints.size(); i++) {
44 delete( infoPoints[i] );
46 for (i = 0; i < containers.size(); i++) {
47 delete( containers[i] );
49 for (i = 0; i < doors.size(); i++) {
50 delete( doors[i] );
54 //this needs in case of a tileset switch (for extended night)
55 void TileMap::ClearOverlays()
57 size_t i;
59 for (i = 0; i < overlays.size(); i++) {
60 delete( overlays[i] );
62 overlays.clear();
63 for (i = 0; i < overlays.size(); i++) {
64 delete( rain_overlays[i]);
66 rain_overlays.clear();
69 //tiled objects
70 TileObject* TileMap::AddTile(const char *ID, const char* Name, unsigned int Flags,
71 unsigned short* openindices, int opencount, unsigned short* closeindices, int closecount)
73 TileObject* tile = new TileObject();
74 tile->Flags=Flags;
75 strnspccpy(tile->Name, Name, 32);
76 strnlwrcpy(tile->Tileset, ID, 8);
77 tile->SetOpenTiles( openindices, opencount );
78 tile->SetClosedTiles( closeindices, closecount );
79 tiles.push_back(tile);
80 return tile;
83 TileObject* TileMap::GetTile(unsigned int idx)
85 if (idx >= tiles.size()) {
86 return NULL;
88 return tiles[idx];
91 //doors
92 Door* TileMap::AddDoor(const char *ID, const char* Name, unsigned int Flags,
93 int ClosedIndex, unsigned short* indices, int count,
94 Gem_Polygon* open, Gem_Polygon* closed)
96 Door* door = new Door( overlays[0] );
97 door->Flags = Flags;
98 door->closedIndex = ClosedIndex;
99 door->SetTiles( indices, count );
100 door->SetPolygon( false, closed );
101 door->SetPolygon( true, open );
102 door->SetName( ID );
103 door->SetScriptName( Name );
104 doors.push_back( door );
105 return door;
108 Door* TileMap::GetDoor(unsigned int idx) const
110 if (idx >= doors.size()) {
111 return NULL;
113 return doors[idx];
116 Door* TileMap::GetDoor(const Point &p) const
118 for (size_t i = 0; i < doors.size(); i++) {
119 Gem_Polygon *doorpoly;
121 Door* door = doors[i];
122 if (door->Flags&DOOR_OPEN)
123 doorpoly = door->open;
124 else
125 doorpoly = door->closed;
127 if (doorpoly->BBox.x > p.x)
128 continue;
129 if (doorpoly->BBox.y > p.y)
130 continue;
131 if (doorpoly->BBox.x + doorpoly->BBox.w < p.x)
132 continue;
133 if (doorpoly->BBox.y + doorpoly->BBox.h < p.y)
134 continue;
135 if (doorpoly->PointIn( p ))
136 return door;
138 return NULL;
141 Door* TileMap::GetDoorByPosition(const Point &p) const
143 for (size_t i = 0; i < doors.size(); i++) {
144 Door* door = doors[i];
146 if (door->toOpen[0].x==p.x && door->toOpen[0].y==p.y) {
147 return door;
149 if (door->toOpen[1].x==p.x && door->toOpen[1].y==p.y) {
150 return door;
153 return NULL;
156 Door* TileMap::GetDoor(const char* Name) const
158 if (!Name) {
159 return NULL;
161 for (size_t i = 0; i < doors.size(); i++) {
162 Door* door = doors[i];
163 if (stricmp( door->GetScriptName(), Name ) == 0)
164 return door;
166 return NULL;
169 void TileMap::UpdateDoors()
171 for (size_t i = 0; i < doors.size(); i++) {
172 Door* door = doors[i];
173 door->SetNewOverlay(overlays[0]);
177 //overlays, allow pushing of NULL
178 void TileMap::AddOverlay(TileOverlay* overlay)
180 if (overlay) {
181 if (overlay->w > XCellCount) {
182 XCellCount = overlay->w;
184 if (overlay->h > YCellCount) {
185 YCellCount = overlay->h;
188 overlays.push_back( overlay );
191 void TileMap::AddRainOverlay(TileOverlay* overlay)
193 if (overlay) {
194 if (overlay->w > XCellCount) {
195 XCellCount = overlay->w;
197 if (overlay->h > YCellCount) {
198 YCellCount = overlay->h;
201 rain_overlays.push_back( overlay );
204 void TileMap::DrawOverlays(Region screen, int rain)
206 if (rain) {
207 overlays[0]->Draw( screen, rain_overlays );
208 } else {
209 overlays[0]->Draw( screen, overlays );
213 // Size of Fog-Of-War shadow tile (and bitmap)
214 #define CELL_SIZE 32
216 // Ratio of bg tile size and fog tile size
217 #define CELL_RATIO 2
219 // Returns 1 if map at (x;y) was explored, else 0. Points outside map are
220 // always considered as explored
221 #define IS_EXPLORED( x, y ) (((x) < 0 || (x) >= w || (y) < 0 || (y) >= h) ? 1 : (explored_mask[(w * (y) + (x)) / 8] & (1 << ((w * (y) + (x)) % 8))))
223 #define IS_VISIBLE( x, y ) (((x) < 0 || (x) >= w || (y) < 0 || (y) >= h) ? 1 : (visible_mask[(w * (y) + (x)) / 8] & (1 << ((w * (y) + (x)) % 8))))
225 #define FOG(i) vid->BlitSprite( core->FogSprites[i], r.x, r.y, true, &r )
228 void TileMap::DrawFogOfWar(ieByte* explored_mask, ieByte* visible_mask, Region viewport)
230 // viewport - pos & size of the control
231 int w = XCellCount * CELL_RATIO;
232 int h = YCellCount * CELL_RATIO;
233 if (LargeMap) {
234 w++;
235 h++;
237 Color black = { 0, 0, 0, 255 };
239 Video* vid = core->GetVideoDriver();
240 Region vp = vid->GetViewport();
242 vp.w = viewport.w;
243 vp.h = viewport.h;
244 if (( vp.x + vp.w ) > w * CELL_SIZE) {
245 vp.x = ( w * CELL_SIZE - vp.w );
247 if (vp.x < 0) {
248 vp.x = 0;
250 if (( vp.y + vp.h ) > h * CELL_SIZE) {
251 vp.y = ( h * CELL_SIZE - vp.h );
253 if (vp.y < 0) {
254 vp.y = 0;
256 int sx = ( vp.x ) / CELL_SIZE;
257 int sy = ( vp.y ) / CELL_SIZE;
258 int dx = sx + vp.w / CELL_SIZE + 2;
259 int dy = sy + vp.h / CELL_SIZE + 2;
260 int x0 = sx * CELL_SIZE - vp.x;
261 int y0 = sy * CELL_SIZE - vp.y;
262 if (LargeMap) {
263 x0 -= CELL_SIZE / 2;
264 y0 -= CELL_SIZE / 2;
265 dx++;
266 dy++;
268 for (int y = sy; y < dy && y < h; y++) {
269 for (int x = sx; x < dx && x < w; x++) {
270 Region r = Region(x0 + viewport.x + ( (x - sx) * CELL_SIZE ), y0 + viewport.y + ( (y - sy) * CELL_SIZE ), CELL_SIZE, CELL_SIZE);
271 if (! IS_EXPLORED( x, y )) {
272 // Unexplored tiles are all black
273 vid->DrawRect(r, black, true, true);
274 continue; // Don't draw 'invisible' fog
276 else {
277 // If an explored tile is adjacent to an
278 // unexplored one, we draw border sprite
279 // (gradient black <-> transparent)
280 // Tiles in four cardinal directions have these
281 // values.
283 // 1
284 // 2 8
285 // 4
287 // Values of those unexplored are
288 // added together, the resulting number being
289 // an index of shadow sprite to use. For now,
290 // some tiles are made 'on the fly' by
291 // drawing two or more tiles
293 int e = ! IS_EXPLORED( x, y - 1);
294 if (! IS_EXPLORED( x - 1, y )) e |= 2;
295 if (! IS_EXPLORED( x, y + 1 )) e |= 4;
296 if (! IS_EXPLORED( x + 1, y )) e |= 8;
298 switch (e) {
299 case 1:
300 case 2:
301 case 3:
302 case 4:
303 case 6:
304 case 8:
305 case 9:
306 case 12:
307 FOG( e );
308 break;
309 case 5:
310 FOG( 1 );
311 FOG( 4 );
312 break;
313 case 7:
314 FOG( 3 );
315 FOG( 6 );
316 break;
317 case 10:
318 FOG( 2 );
319 FOG( 8 );
320 break;
321 case 11:
322 FOG( 3 );
323 FOG( 9 );
324 break;
325 case 13:
326 FOG( 9 );
327 FOG( 12 );
328 break;
329 case 14:
330 FOG( 6 );
331 FOG( 12 );
332 break;
333 case 15: //this is black too
334 vid->DrawRect(r, black, true, true);
335 break;
339 if (! IS_VISIBLE( x, y )) {
340 // Invisible tiles are all gray
341 FOG( 16 );
342 continue; // Don't draw 'invisible' fog
344 else {
345 // If a visible tile is adjacent to an
346 // invisible one, we draw border sprite
347 // (gradient gray <-> transparent)
348 // Tiles in four cardinal directions have these
349 // values.
351 // 1
352 // 2 8
353 // 4
355 // Values of those invisible are
356 // added together, the resulting number being
357 // an index of shadow sprite to use. For now,
358 // some tiles are made 'on the fly' by
359 // drawing two or more tiles
361 int e = ! IS_VISIBLE( x, y - 1);
362 if (! IS_VISIBLE( x - 1, y )) e |= 2;
363 if (! IS_VISIBLE( x, y + 1 )) e |= 4;
364 if (! IS_VISIBLE( x + 1, y )) e |= 8;
366 switch (e) {
367 case 1:
368 case 2:
369 case 3:
370 case 4:
371 case 6:
372 case 8:
373 case 9:
374 case 12:
375 FOG( 16 + e );
376 break;
377 case 5:
378 FOG( 16 + 1 );
379 FOG( 16 + 4 );
380 break;
381 case 7:
382 FOG( 16 + 3 );
383 FOG( 16 + 6 );
384 break;
385 case 10:
386 FOG( 16 + 2 );
387 FOG( 16 + 8 );
388 break;
389 case 11:
390 FOG( 16 + 3 );
391 FOG( 16 + 9 );
392 break;
393 case 13:
394 FOG( 16 + 9 );
395 FOG( 16 + 12 );
396 break;
397 case 14:
398 FOG( 16 + 6 );
399 FOG( 16 + 12 );
400 break;
401 case 15: //this is unseen too
402 FOG( 16 );
403 break;
410 //containers
411 void TileMap::AddContainer(Container *c)
413 containers.push_back(c);
416 Container* TileMap::GetContainer(unsigned int idx) const
418 if (idx >= containers.size()) {
419 return NULL;
421 return containers[idx];
424 Container* TileMap::GetContainer(const char* Name) const
426 for (size_t i = 0; i < containers.size(); i++) {
427 Container* cn = containers[i];
428 if (stricmp( cn->GetScriptName(), Name ) == 0)
429 return cn;
431 return NULL;
434 //look for a container at position
435 //use type = IE_CONTAINER_PILE if you want to find ground piles only
436 //in this case, empty piles won't be found!
437 Container* TileMap::GetContainer(const Point &position, int type) const
439 for (size_t i = 0; i < containers.size(); i++) {
440 Container* c = containers[i];
441 if (type!=-1) {
442 if (c->Type!=type) {
443 continue;
446 if (c->outline->BBox.x > position.x)
447 continue;
448 if (c->outline->BBox.y > position.y)
449 continue;
450 if (c->outline->BBox.x + c->outline->BBox.w < position.x)
451 continue;
452 if (c->outline->BBox.y + c->outline->BBox.h < position.y)
453 continue;
455 //IE piles don't have polygons, the bounding box is enough for them
456 if (c->Type == IE_CONTAINER_PILE) {
457 //don't find empty piles if we look for any container
458 //if we looked only for piles, then we still return them
459 if ((type==-1) && !c->inventory.GetSlotCount()) {
460 continue;
462 return c;
464 if (c->outline->PointIn( position ))
465 return c;
467 return NULL;
470 Container* TileMap::GetContainerByPosition(const Point &position, int type) const
472 for (size_t i = 0; i < containers.size(); i++) {
473 Container* c = containers[i];
474 if (type!=-1) {
475 if (c->Type!=type) {
476 continue;
480 if (c->Pos.x!=position.x || c->Pos.y!=position.y) {
481 continue;
484 //IE piles don't have polygons, the bounding box is enough for them
485 if (c->Type == IE_CONTAINER_PILE) {
486 //don't find empty piles if we look for any container
487 //if we looked only for piles, then we still return them
488 if ((type==-1) && !c->inventory.GetSlotCount()) {
489 continue;
491 return c;
493 return c;
495 return NULL;
498 int TileMap::CleanupContainer(Container *container)
500 if (container->Type!=IE_CONTAINER_PILE)
501 return 0;
502 if (container->inventory.GetSlotCount())
503 return 0;
505 for (size_t i = 0; i < containers.size(); i++) {
506 if (containers[i]==container) {
507 containers.erase(containers.begin()+i);
508 delete container;
509 return 1;
512 printMessage("TileMap", " ", LIGHT_RED);
513 printf("Invalid container cleanup: %s\n", container->GetScriptName());
514 return 1;
517 //infopoints
518 InfoPoint* TileMap::AddInfoPoint(const char* Name, unsigned short Type,
519 Gem_Polygon* outline)
521 InfoPoint* ip = new InfoPoint();
522 ip->SetScriptName( Name );
523 switch (Type) {
524 case 0:
525 ip->Type = ST_PROXIMITY;
526 break;
528 case 1:
529 ip->Type = ST_TRIGGER;
530 break;
532 case 2:
533 ip->Type = ST_TRAVEL;
534 break;
535 //this is just to satisfy whiny compilers
536 default:
537 ip->Type = ST_PROXIMITY;
538 break;
540 ip->outline = outline;
541 //ip->Active = true; //set active on creation
542 infoPoints.push_back( ip );
543 return ip;
546 //if detectable is set, then only detectable infopoints will be returned
547 InfoPoint* TileMap::GetInfoPoint(const Point &p, bool detectable) const
549 for (size_t i = 0; i < infoPoints.size(); i++) {
550 InfoPoint* ip = infoPoints[i];
551 //these flags disable any kind of user interaction
552 //scripts can still access an infopoint by name
553 if (ip->Flags&(INFO_DOOR|TRAP_DEACTIVATED) )
554 continue;
556 if (detectable) {
557 if ((ip->Type==ST_PROXIMITY) && !ip->VisibleTrap(0) ) {
558 continue;
560 if (ip->IsPortal()) {
561 // skip portals without PORTAL_CURSOR set
562 if (!(ip->Trapped & PORTAL_CURSOR)) {
563 continue;
568 if (!(ip->GetInternalFlag()&IF_ACTIVE))
569 continue;
570 if (ip->outline->BBox.x > p.x)
571 continue;
572 if (ip->outline->BBox.y > p.y)
573 continue;
574 if (ip->outline->BBox.x + ip->outline->BBox.w < p.x)
575 continue;
576 if (ip->outline->BBox.y + ip->outline->BBox.h < p.y)
577 continue;
578 if (ip->outline->PointIn( p ))
579 return ip;
581 return NULL;
584 InfoPoint* TileMap::GetInfoPoint(const char* Name) const
586 for (size_t i = 0; i < infoPoints.size(); i++) {
587 InfoPoint* ip = infoPoints[i];
588 if (stricmp( ip->GetScriptName(), Name ) == 0)
589 return ip;
591 return NULL;
594 InfoPoint* TileMap::GetInfoPoint(unsigned int idx) const
596 if (idx >= infoPoints.size()) {
597 return NULL;
599 return infoPoints[idx];
602 InfoPoint* TileMap::GetTravelTo(const char* Destination) const
604 size_t i=infoPoints.size();
605 while (i--) {
606 InfoPoint *ip = infoPoints[i];
608 if (ip->Type!=ST_TRAVEL)
609 continue;
611 if (strnicmp( ip->Destination, Destination, 8 ) == 0) {
612 return ip;
615 return NULL;
618 InfoPoint *TileMap::AdjustNearestTravel(Point &p)
620 int min = -1;
621 InfoPoint *best = NULL;
623 size_t i=infoPoints.size();
624 while (i--) {
625 InfoPoint *ip = infoPoints[i];
627 if (ip->Type!=ST_TRAVEL)
628 continue;
630 unsigned int dist = Distance(p, ip);
631 if (dist<(unsigned int) min) {
632 min = dist;
633 best = ip;
636 if (best) {
637 p = best->Pos;
639 return best;
642 Point TileMap::GetMapSize()
644 return Point((short) (XCellCount*64), (short) (YCellCount*64));