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.
26 #include "Interface.h"
31 WMPAreaEntry::WMPAreaEntry()
38 WMPAreaEntry::~WMPAreaEntry()
41 core
->FreeString(StrCaption
);
44 core
->FreeString(StrTooltip
);
46 core
->GetVideoDriver()->FreeSprite(MapIcon
);
49 void WMPAreaEntry::SetAreaStatus(ieDword arg
, int op
)
52 case BM_SET
: AreaStatus
= arg
; break;
53 case BM_OR
: AreaStatus
|= arg
; break;
54 case BM_NAND
: AreaStatus
&= ~arg
; break;
55 case BM_XOR
: AreaStatus
^= arg
; break;
56 case BM_AND
: AreaStatus
&= arg
; break;
58 core
->GetVideoDriver()->FreeSprite(MapIcon
);
61 const char* WMPAreaEntry::GetCaption()
64 StrCaption
= core
->GetString(LocCaptionName
);
69 const char* WMPAreaEntry::GetTooltip()
72 StrTooltip
= core
->GetString(LocTooltipName
);
77 static int gradients
[5]={18,22,19,3,4};
79 void WMPAreaEntry::SetPalette(int gradient
, Sprite2D
* MapIcon
)
82 Palette
*palette
= new Palette
;
83 core
->GetPalette( gradient
&255, 256, palette
->col
);
84 MapIcon
->SetPalette(palette
);
87 Sprite2D
*WMPAreaEntry::GetMapIcon(AnimationFactory
*bam
)
95 switch (AreaStatus
&(WMP_ENTRY_ACCESSIBLE
|WMP_ENTRY_VISITED
))
97 case WMP_ENTRY_ACCESSIBLE
: frame
= 0; break;
98 case WMP_ENTRY_VISITED
: frame
= 4; break;
99 case WMP_ENTRY_ACCESSIBLE
|WMP_ENTRY_VISITED
: frame
= 1; break;
100 case 0: frame
= 2; break;
102 if (bam
->GetCycleSize(IconSeq
)<5) {
103 color
= gradients
[frame
];
106 MapIcon
= bam
->GetFrame((ieWord
) frame
, (ieByte
) IconSeq
);
108 printf("WMPAreaEntry::GetMapIcon failed for frame %d, seq %d\n", frame
, IconSeq
);
112 // Note: should a game use the same map icon for two different
113 // map locations, we have to duplicate the MapIcon sprite here.
114 // This doesn't occur in BG1, so no need to do that for the moment.
115 SetPalette(color
, MapIcon
);
122 ieDword
WMPAreaEntry::GetAreaStatus()
124 ieDword tmp
= AreaStatus
;
125 if (core
->HasFeature(GF_KNOW_WORLD
) ) {
126 tmp
|=WMP_ENTRY_VISITED
;
131 WorldMap::WorldMap(void)
139 //Allocate AE and AL only in Core, otherwise Win32 will
141 void WorldMap::AddAreaEntry(WMPAreaEntry
*ae
)
143 area_entries
.push_back(ae
);
146 void WorldMap::AddAreaLink(WMPAreaLink
*al
)
148 area_links
.push_back(al
);
151 WMPAreaEntry
*WorldMap::GetNewAreaEntry() const
153 return new WMPAreaEntry();
156 void WorldMap::SetAreaEntry(unsigned int x
, WMPAreaEntry
*ae
)
158 //if index is too large, we break
159 if (x
>area_entries
.size()) {
162 //altering an existing entry
163 if (x
<area_entries
.size()) {
164 if (area_entries
[x
]) {
165 delete area_entries
[x
];
171 area_entries
.push_back(ae
);
174 void WorldMap::InsertAreaLink(unsigned int idx
, unsigned int dir
, WMPAreaLink
*arealink
)
179 WMPAreaLink
*al
= new WMPAreaLink();
180 memcpy(al
, arealink
, sizeof(WMPAreaLink
) );
181 unsigned int max
= area_links
.size();
182 area_links
.insert(area_links
.begin()+idx
,al
);
184 max
= area_entries
.size();
185 for(pos
= 0; pos
<max
; pos
++) {
186 ae
= area_entries
[pos
];
187 for (unsigned int k
=0;k
<4;k
++) {
188 if ((pos
==idx
) && (k
==dir
)) {
189 ae
->AreaLinksCount
[k
]++;
192 if(ae
->AreaLinksIndex
[k
]>=idx
) {
193 ae
->AreaLinksIndex
[k
]++;
197 //update the link count, just in case
201 void WorldMap::SetAreaLink(unsigned int x
, WMPAreaLink
*arealink
)
203 WMPAreaLink
*al
=new WMPAreaLink();
205 //change this to similar code as above if WMPAreaLink gets non-struct members
206 memcpy( al
,arealink
,sizeof(WMPAreaLink
) );
208 //if index is too large, we break
209 if (x
>area_links
.size()) {
212 //altering an existing link
213 if (x
<area_links
.size()) {
215 delete area_links
[x
];
221 area_links
.push_back(al
);
224 WorldMap::~WorldMap(void)
228 for (i
= 0; i
< area_entries
.size(); i
++) {
229 delete( area_entries
[i
] );
231 for (i
= 0; i
< area_links
.size(); i
++) {
232 delete( area_links
[i
] );
235 core
->GetVideoDriver()->FreeSprite(MapMOS
);
246 void WorldMap::SetMapIcons(AnimationFactory
*newicons
)
250 void WorldMap::SetMapMOS(Sprite2D
*newmos
)
253 core
->GetVideoDriver()->FreeSprite(MapMOS
);
258 WMPAreaEntry
* WorldMap::GetArea(const ieResRef AreaName
, unsigned int &i
) const
260 i
=(unsigned int) area_entries
.size();
262 if (!strnicmp(AreaName
, area_entries
[i
]->AreaName
,8)) {
263 return area_entries
[i
];
269 //Find Worldmap location by nearest area with a smaller number
270 //Counting backwards, stop at 1000 boundaries.
271 //It is not possible to simply round to 1000, because there are
272 //WMP entries like AR8001, and we need to find the best match
273 WMPAreaEntry
* WorldMap::FindNearestEntry(const ieResRef AreaName
, unsigned int &i
) const
278 sscanf(&AreaName
[2],"%4d", &value
);
280 snprintf(tmp
, 9, "%.2s%04d", AreaName
, value
);
281 WMPAreaEntry
* ret
= GetArea(tmp
, i
);
285 if (value
%1000 == 0) break;
288 while(1); //value%1000 should protect us from infinite loops
293 //this is a pathfinding algorithm
294 //you have to find the optimal path
295 int WorldMap::CalculateDistances(const ieResRef AreaName
, int direction
)
297 //first, update reachable/visible areas by worlde.2da if exists
298 UpdateReachableAreas();
299 UpdateAreaVisibility(AreaName
, direction
);
304 if (direction
<0 || direction
>3) {
305 printMessage("WorldMap","", LIGHT_RED
);
306 printf("CalculateDistances for invalid direction: %s\n", AreaName
);
311 if (!GetArea(AreaName
, i
)) {
312 printMessage("WorldMap","", LIGHT_RED
);
313 printf("CalculateDistances for invalid Area: %s\n", AreaName
);
323 printMessage("WorldMap","", GREEN
);
324 printf("CalculateDistances for Area: %s\n", AreaName
);
326 size_t memsize
=sizeof(int) * area_entries
.size();
327 Distances
= (int *) malloc( memsize
);
328 GotHereFrom
= (int *) malloc( memsize
);
329 memset( Distances
, -1, memsize
);
330 memset( GotHereFrom
, -1, memsize
);
331 Distances
[i
] = 0; //setting our own distance
332 GotHereFrom
[i
] = -1; //we didn't move
334 int *seen_entry
= (int *) malloc( memsize
);
336 std::list
<int> pending
;
337 pending
.push_back(i
);
338 while(pending
.size()) {
341 WMPAreaEntry
* ae
=area_entries
[i
];
342 memset( seen_entry
, -1, memsize
);
343 //all directions should be used
344 for(int d
=0;d
<4;d
++) {
345 int j
=ae
->AreaLinksIndex
[d
];
346 int k
=j
+ae
->AreaLinksCount
[d
];
347 if ((size_t) k
>area_links
.size()) {
348 printMessage("WorldMap","The worldmap file is corrupted... and it would crash right now!\n",RED
);
349 printf("Entry #: %d Direction: %d\n",i
,d
);
353 WMPAreaLink
* al
= area_links
[j
];
354 WMPAreaEntry
* ae2
= area_entries
[al
->AreaIndex
];
355 unsigned int mydistance
= (unsigned int) Distances
[i
];
357 // we must only process the FIRST seen link to each area from this one
358 if (seen_entry
[al
->AreaIndex
] != -1) continue;
359 seen_entry
[al
->AreaIndex
] = 0;
361 if ( ( (ae->GetAreaStatus() & WMP_ENTRY_PASSABLE) == WMP_ENTRY_PASSABLE) &&
362 ( (ae2->GetAreaStatus() & WMP_ENTRY_WALKABLE) == WMP_ENTRY_WALKABLE)
364 if ( (ae2
->GetAreaStatus() & WMP_ENTRY_WALKABLE
) == WMP_ENTRY_WALKABLE
) {
365 // al->Flags is the entry direction
366 mydistance
+= al
->DistanceScale
* 4;
367 //nonexisting distance is the biggest!
368 if ((unsigned) Distances
[al
->AreaIndex
] > mydistance
) {
369 Distances
[al
->AreaIndex
] = mydistance
;
370 GotHereFrom
[al
->AreaIndex
] = j
;
371 pending
.push_back(al
->AreaIndex
);
382 //returns the index of the area owning this link
383 unsigned int WorldMap::WhoseLinkAmI(int link_index
) const
385 for (unsigned int i
=0;i
<AreaEntriesCount
;i
++) {
386 WMPAreaEntry
*ae
=area_entries
[i
];
387 for (int direction
=0;direction
<4;direction
++)
389 int j
=ae
->AreaLinksIndex
[direction
];
391 j
+=ae
->AreaLinksCount
[direction
];
401 WMPAreaLink
*WorldMap::GetLink(const ieResRef A
, const ieResRef B
) const
405 WMPAreaEntry
*ae
=GetArea( A
, i
);
409 //looking for destination area, returning the first link found
411 j
=ae
->AreaLinksCount
[i
];
412 k
=ae
->AreaLinksIndex
[i
];
414 WMPAreaLink
*al
= area_links
[k
++];
415 WMPAreaEntry
*ae2
= area_entries
[al
->AreaIndex
];
417 if (strnicmp(ae2
->AreaName
, B
, 8)==0) {
425 //call this function to find out which area we fall into
426 //not necessarily the target area
427 //if it isn't the same, then a random encounter happened!
428 WMPAreaLink
*WorldMap::GetEncounterLink(const ieResRef AreaName
, bool &encounter
) const
434 WMPAreaEntry
*ae
=GetArea( AreaName
, i
); //target area
436 printMessage("WorldMap","",LIGHT_RED
);
437 printf("No such area: %s\n", AreaName
);
440 std::list
<WMPAreaLink
*> walkpath
;
441 printf("Gathering path information for: %s\n", AreaName
);
442 while (GotHereFrom
[i
]!=-1) {
443 printf("Adding path to %d\n", i
);
444 walkpath
.push_back(area_links
[GotHereFrom
[i
]]);
445 i
= WhoseLinkAmI(GotHereFrom
[i
]);
446 if (i
==(ieDword
) -1) {
447 printf("Something has been screwed up here (incorrect path)!\n");
452 printf("Walkpath size is: %d\n",(int) walkpath
.size());
453 if (!walkpath
.size()) {
456 std::list
<WMPAreaLink
*>::iterator p
=walkpath
.begin();
457 WMPAreaLink
*lastpath
;
461 if (lastpath
->EncounterChance
> (unsigned int) (rand()%100)) {
467 while(p
!=walkpath
.end() );
471 int WorldMap::GetDistance(const ieResRef AreaName
) const
477 if (GetArea( AreaName
, i
)) {
483 void WorldMap::UpdateAreaVisibility(const ieResRef AreaName
, int direction
)
487 WMPAreaEntry
* ae
=GetArea(AreaName
,i
);
490 //we are here, so we visited and it is visible too (i guess)
491 printf("Updated Area visibility: %s (visited, and visible)\n", AreaName
);
493 ae
->SetAreaStatus(WMP_ENTRY_VISITED
|WMP_ENTRY_VISIBLE
, BM_OR
);
494 if (direction
<0 || direction
>3)
496 i
=ae
->AreaLinksCount
[direction
];
498 WMPAreaLink
* al
= area_links
[ae
->AreaLinksIndex
[direction
]+i
];
499 WMPAreaEntry
* ae2
= area_entries
[al
->AreaIndex
];
500 if (ae2
->GetAreaStatus()&WMP_ENTRY_ADJACENT
) {
501 printf("Updated Area visibility: %s (accessible, and visible)\n", ae2
->AreaName
);
502 ae2
->SetAreaStatus(WMP_ENTRY_VISIBLE
|WMP_ENTRY_ACCESSIBLE
, BM_OR
);
507 void WorldMap::SetAreaStatus(const ieResRef AreaName
, int Bits
, int Op
)
510 WMPAreaEntry
* ae
=GetArea(AreaName
,i
);
513 ae
->SetAreaStatus(Bits
, Op
);
516 void WorldMap::UpdateReachableAreas()
518 AutoTable
tab("worlde");
522 Game
*game
= core
->GetGame();
526 int idx
= tab
->GetRowCount();
528 // 2da rows in format <name> <variable name> <area>
529 // we set the first three flags for <area> if <variable name> is set
531 const char *varname
= tab
->QueryField(idx
, 0);
532 if (game
->locals
->Lookup(varname
, varval
) && varval
) {
533 const char *areaname
= tab
->QueryField(idx
, 1);
534 SetAreaStatus(areaname
, WMP_ENTRY_VISIBLE
| WMP_ENTRY_ADJACENT
| WMP_ENTRY_ACCESSIBLE
, BM_OR
);
539 /****************** WorldMapArray *******************/
540 WorldMapArray::WorldMapArray(unsigned int count
)
544 all_maps
= (WorldMap
**) calloc(count
, sizeof(WorldMap
*) );
548 WorldMapArray::~WorldMapArray()
552 for (i
= 0; i
<MapCount
; i
++) {
560 unsigned int WorldMapArray::FindAndSetCurrentMap(const ieResRef area
)
564 for (i
= CurrentMap
; i
<MapCount
; i
++) {
565 if (all_maps
[i
]->GetArea (area
, idx
) ) {
570 for (i
= 0; i
<CurrentMap
; i
++) {
571 if (all_maps
[i
]->GetArea (area
, idx
) ) {
579 void WorldMapArray::SetWorldMap(WorldMap
*m
, unsigned int index
)
581 if (index
<MapCount
) {
586 WorldMap
*WorldMapArray::NewWorldMap(unsigned int index
)
588 if (all_maps
[index
]) {
589 delete all_maps
[index
];
591 all_maps
[index
] = new WorldMap();
592 return all_maps
[index
];