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.
23 #include "Interface.h"
26 TileMap::TileMap(void)
30 LargeMap
= !core
->HasFeature(GF_SMALL_FOG
);
33 TileMap::~TileMap(void)
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
++) {
54 //this needs in case of a tileset switch (for extended night)
55 void TileMap::ClearOverlays()
59 for (i
= 0; i
< overlays
.size(); i
++) {
60 delete( overlays
[i
] );
63 for (i
= 0; i
< overlays
.size(); i
++) {
64 delete( rain_overlays
[i
]);
66 rain_overlays
.clear();
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();
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
);
83 TileObject
* TileMap::GetTile(unsigned int idx
)
85 if (idx
>= tiles
.size()) {
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] );
98 door
->closedIndex
= ClosedIndex
;
99 door
->SetTiles( indices
, count
);
100 door
->SetPolygon( false, closed
);
101 door
->SetPolygon( true, open
);
103 door
->SetScriptName( Name
);
104 doors
.push_back( door
);
108 Door
* TileMap::GetDoor(unsigned int idx
) const
110 if (idx
>= doors
.size()) {
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
;
125 doorpoly
= door
->closed
;
127 if (doorpoly
->BBox
.x
> p
.x
)
129 if (doorpoly
->BBox
.y
> p
.y
)
131 if (doorpoly
->BBox
.x
+ doorpoly
->BBox
.w
< p
.x
)
133 if (doorpoly
->BBox
.y
+ doorpoly
->BBox
.h
< p
.y
)
135 if (doorpoly
->PointIn( p
))
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
) {
149 if (door
->toOpen
[1].x
==p
.x
&& door
->toOpen
[1].y
==p
.y
) {
156 Door
* TileMap::GetDoor(const char* Name
) const
161 for (size_t i
= 0; i
< doors
.size(); i
++) {
162 Door
* door
= doors
[i
];
163 if (stricmp( door
->GetScriptName(), Name
) == 0)
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
)
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
)
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
)
207 overlays
[0]->Draw( screen
, rain_overlays
);
209 overlays
[0]->Draw( screen
, overlays
);
213 // Size of Fog-Of-War shadow tile (and bitmap)
216 // Ratio of bg tile size and fog tile size
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
;
237 Color black
= { 0, 0, 0, 255 };
239 Video
* vid
= core
->GetVideoDriver();
240 Region vp
= vid
->GetViewport();
244 if (( vp
.x
+ vp
.w
) > w
* CELL_SIZE
) {
245 vp
.x
= ( w
* CELL_SIZE
- vp
.w
);
250 if (( vp
.y
+ vp
.h
) > h
* CELL_SIZE
) {
251 vp
.y
= ( h
* CELL_SIZE
- vp
.h
);
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
;
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
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
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;
333 case 15: //this is black too
334 vid
->DrawRect(r
, black
, true, true);
339 if (! IS_VISIBLE( x
, y
)) {
340 // Invisible tiles are all gray
342 continue; // Don't draw 'invisible' fog
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
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;
401 case 15: //this is unseen too
411 void TileMap::AddContainer(Container
*c
)
413 containers
.push_back(c
);
416 Container
* TileMap::GetContainer(unsigned int idx
) const
418 if (idx
>= containers
.size()) {
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)
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
];
446 if (c
->outline
->BBox
.x
> position
.x
)
448 if (c
->outline
->BBox
.y
> position
.y
)
450 if (c
->outline
->BBox
.x
+ c
->outline
->BBox
.w
< position
.x
)
452 if (c
->outline
->BBox
.y
+ c
->outline
->BBox
.h
< position
.y
)
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()) {
464 if (c
->outline
->PointIn( position
))
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
];
480 if (c
->Pos
.x
!=position
.x
|| c
->Pos
.y
!=position
.y
) {
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()) {
498 int TileMap::CleanupContainer(Container
*container
)
500 if (container
->Type
!=IE_CONTAINER_PILE
)
502 if (container
->inventory
.GetSlotCount())
505 for (size_t i
= 0; i
< containers
.size(); i
++) {
506 if (containers
[i
]==container
) {
507 containers
.erase(containers
.begin()+i
);
512 printMessage("TileMap", " ", LIGHT_RED
);
513 printf("Invalid container cleanup: %s\n", container
->GetScriptName());
518 InfoPoint
* TileMap::AddInfoPoint(const char* Name
, unsigned short Type
,
519 Gem_Polygon
* outline
)
521 InfoPoint
* ip
= new InfoPoint();
522 ip
->SetScriptName( Name
);
525 ip
->Type
= ST_PROXIMITY
;
529 ip
->Type
= ST_TRIGGER
;
533 ip
->Type
= ST_TRAVEL
;
535 //this is just to satisfy whiny compilers
537 ip
->Type
= ST_PROXIMITY
;
540 ip
->outline
= outline
;
541 //ip->Active = true; //set active on creation
542 infoPoints
.push_back( 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
) )
557 if ((ip
->Type
==ST_PROXIMITY
) && !ip
->VisibleTrap(0) ) {
560 if (ip
->IsPortal()) {
561 // skip portals without PORTAL_CURSOR set
562 if (!(ip
->Trapped
& PORTAL_CURSOR
)) {
568 if (!(ip
->GetInternalFlag()&IF_ACTIVE
))
570 if (ip
->outline
->BBox
.x
> p
.x
)
572 if (ip
->outline
->BBox
.y
> p
.y
)
574 if (ip
->outline
->BBox
.x
+ ip
->outline
->BBox
.w
< p
.x
)
576 if (ip
->outline
->BBox
.y
+ ip
->outline
->BBox
.h
< p
.y
)
578 if (ip
->outline
->PointIn( p
))
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)
594 InfoPoint
* TileMap::GetInfoPoint(unsigned int idx
) const
596 if (idx
>= infoPoints
.size()) {
599 return infoPoints
[idx
];
602 InfoPoint
* TileMap::GetTravelTo(const char* Destination
) const
604 size_t i
=infoPoints
.size();
606 InfoPoint
*ip
= infoPoints
[i
];
608 if (ip
->Type
!=ST_TRAVEL
)
611 if (strnicmp( ip
->Destination
, Destination
, 8 ) == 0) {
618 InfoPoint
*TileMap::AdjustNearestTravel(Point
&p
)
621 InfoPoint
*best
= NULL
;
623 size_t i
=infoPoints
.size();
625 InfoPoint
*ip
= infoPoints
[i
];
627 if (ip
->Type
!=ST_TRAVEL
)
630 unsigned int dist
= Distance(p
, ip
);
631 if (dist
<(unsigned int) min
) {
642 Point
TileMap::GetMapSize()
644 return Point((short) (XCellCount
*64), (short) (YCellCount
*64));