melee / ranged effects
[gemrb.git] / gemrb / core / WorldMap.cpp
blob3b1e1053ec24d51f1c8597defbf592807af56e18
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 "WorldMap.h"
23 #include "win32def.h"
25 #include "Game.h"
26 #include "Interface.h"
27 #include "Video.h"
29 #include <list>
31 WMPAreaEntry::WMPAreaEntry()
33 MapIcon = NULL;
34 StrCaption = NULL;
35 StrTooltip = NULL;
38 WMPAreaEntry::~WMPAreaEntry()
40 if (StrCaption) {
41 core->FreeString(StrCaption);
43 if (StrTooltip) {
44 core->FreeString(StrTooltip);
46 core->GetVideoDriver()->FreeSprite(MapIcon);
49 void WMPAreaEntry::SetAreaStatus(ieDword arg, int op)
51 switch (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()
63 if (!StrCaption) {
64 StrCaption = core->GetString(LocCaptionName);
66 return StrCaption;
69 const char* WMPAreaEntry::GetTooltip()
71 if (!StrTooltip) {
72 StrTooltip = core->GetString(LocTooltipName);
74 return StrTooltip;
77 static int gradients[5]={18,22,19,3,4};
79 void WMPAreaEntry::SetPalette(int gradient, Sprite2D* MapIcon)
81 if (!MapIcon) return;
82 Palette *palette = new Palette;
83 core->GetPalette( gradient&255, 256, palette->col );
84 MapIcon->SetPalette(palette);
87 Sprite2D *WMPAreaEntry::GetMapIcon(AnimationFactory *bam)
89 if (!bam) {
90 return NULL;
92 if (!MapIcon) {
93 int color = -1;
94 int frame = 0;
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];
104 frame = 0;
106 MapIcon = bam->GetFrame((ieWord) frame, (ieByte) IconSeq);
107 if (!MapIcon) {
108 printf("WMPAreaEntry::GetMapIcon failed for frame %d, seq %d\n", frame, IconSeq);
109 return NULL;
111 if (color>=0) {
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);
118 MapIcon->acquire();
119 return MapIcon;
122 ieDword WMPAreaEntry::GetAreaStatus()
124 ieDword tmp = AreaStatus;
125 if (core->HasFeature(GF_KNOW_WORLD) ) {
126 tmp |=WMP_ENTRY_VISITED;
128 return tmp;
131 WorldMap::WorldMap(void)
133 MapMOS = NULL;
134 Distances = NULL;
135 GotHereFrom = NULL;
136 bam = NULL;
139 //Allocate AE and AL only in Core, otherwise Win32 will
140 //be buggy
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()) {
160 abort();
162 //altering an existing entry
163 if (x<area_entries.size()) {
164 if (area_entries[x]) {
165 delete area_entries[x];
167 area_entries[x]=ae;
168 return;
170 //adding a new entry
171 area_entries.push_back(ae);
174 void WorldMap::InsertAreaLink(unsigned int idx, unsigned int dir, WMPAreaLink *arealink)
176 unsigned int pos;
177 WMPAreaEntry *ae;
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]++;
190 continue;
192 if(ae->AreaLinksIndex[k]>=idx) {
193 ae->AreaLinksIndex[k]++;
197 //update the link count, just in case
198 AreaLinksCount++;
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()) {
210 abort();
212 //altering an existing link
213 if (x<area_links.size()) {
214 if (area_links[x]) {
215 delete area_links[x];
217 area_links[x]=al;
218 return;
220 //adding a new link
221 area_links.push_back(al);
224 WorldMap::~WorldMap(void)
226 unsigned int i;
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] );
234 if (MapMOS) {
235 core->GetVideoDriver()->FreeSprite(MapMOS);
237 if (Distances) {
238 free(Distances);
240 if (GotHereFrom) {
241 free(GotHereFrom);
243 if (bam) bam = NULL;
246 void WorldMap::SetMapIcons(AnimationFactory *newicons)
248 bam = newicons;
250 void WorldMap::SetMapMOS(Sprite2D *newmos)
252 if (MapMOS) {
253 core->GetVideoDriver()->FreeSprite(MapMOS);
255 MapMOS = newmos;
258 WMPAreaEntry* WorldMap::GetArea(const ieResRef AreaName, unsigned int &i) const
260 i=(unsigned int) area_entries.size();
261 while (i--) {
262 if (!strnicmp(AreaName, area_entries[i]->AreaName,8)) {
263 return area_entries[i];
266 return NULL;
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
275 int value = 0;
276 ieResRef tmp;
278 sscanf(&AreaName[2],"%4d", &value);
279 do {
280 snprintf(tmp, 9, "%.2s%04d", AreaName, value);
281 WMPAreaEntry* ret = GetArea(tmp, i);
282 if (ret) {
283 return ret;
285 if (value%1000 == 0) break;
286 value--;
288 while(1); //value%1000 should protect us from infinite loops
289 i = -1;
290 return NULL;
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);
300 if (direction==-1) {
301 return 0;
304 if (direction<0 || direction>3) {
305 printMessage("WorldMap","", LIGHT_RED);
306 printf("CalculateDistances for invalid direction: %s\n", AreaName);
307 return -1;
310 unsigned int i;
311 if (!GetArea(AreaName, i)) {
312 printMessage("WorldMap","", LIGHT_RED);
313 printf("CalculateDistances for invalid Area: %s\n", AreaName);
314 return -1;
316 if (Distances) {
317 free(Distances);
319 if (GotHereFrom) {
320 free(GotHereFrom);
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()) {
339 i=pending.front();
340 pending.pop_front();
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);
350 break;
352 for(;j<k;j++) {
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);
378 free(seen_entry);
379 return 0;
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];
390 if (link_index>=j) {
391 j+=ae->AreaLinksCount[direction];
392 if(link_index<j) {
393 return i;
398 return (ieDword) -1;
401 WMPAreaLink *WorldMap::GetLink(const ieResRef A, const ieResRef B) const
403 unsigned int i,j,k;
405 WMPAreaEntry *ae=GetArea( A, i );
406 if (!ae) {
407 return NULL;
409 //looking for destination area, returning the first link found
410 for (i=0;i<4;i++) {
411 j=ae->AreaLinksCount[i];
412 k=ae->AreaLinksIndex[i];
413 while(j--) {
414 WMPAreaLink *al = area_links[k++];
415 WMPAreaEntry *ae2 = area_entries[al->AreaIndex];
416 //or arearesref?
417 if (strnicmp(ae2->AreaName, B, 8)==0) {
418 return al;
422 return NULL;
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
430 if (!GotHereFrom) {
431 return NULL;
433 unsigned int i;
434 WMPAreaEntry *ae=GetArea( AreaName, i ); //target area
435 if (!ae) {
436 printMessage("WorldMap","",LIGHT_RED);
437 printf("No such area: %s\n", AreaName);
438 return NULL;
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");
448 abort();
452 printf("Walkpath size is: %d\n",(int) walkpath.size());
453 if (!walkpath.size()) {
454 return NULL;
456 std::list<WMPAreaLink*>::iterator p=walkpath.begin();
457 WMPAreaLink *lastpath;
458 encounter=false;
459 do {
460 lastpath = *p;
461 if (lastpath->EncounterChance > (unsigned int) (rand()%100)) {
462 encounter=true;
463 break;
465 p++;
467 while(p!=walkpath.end() );
468 return lastpath;
471 int WorldMap::GetDistance(const ieResRef AreaName) const
473 if (!Distances) {
474 return -1;
476 unsigned int i;
477 if (GetArea( AreaName, i )) {
478 return Distances[i];
480 return -1;
483 void WorldMap::UpdateAreaVisibility(const ieResRef AreaName, int direction)
485 unsigned int i;
487 WMPAreaEntry* ae=GetArea(AreaName,i);
488 if (!ae)
489 return;
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)
495 return;
496 i=ae->AreaLinksCount[direction];
497 while (i--) {
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)
509 unsigned int i;
510 WMPAreaEntry* ae=GetArea(AreaName,i);
511 if (!ae)
512 return;
513 ae->SetAreaStatus(Bits, Op);
516 void WorldMap::UpdateReachableAreas()
518 AutoTable tab("worlde");
519 if (!tab) {
520 return;
522 Game *game = core->GetGame();
523 if (!game) {
524 return;
526 int idx = tab->GetRowCount();
527 while (idx--) {
528 // 2da rows in format <name> <variable name> <area>
529 // we set the first three flags for <area> if <variable name> is set
530 ieDword varval = 0;
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)
542 CurrentMap = 0;
543 MapCount = count;
544 all_maps = (WorldMap **) calloc(count, sizeof(WorldMap *) );
545 single = true;
548 WorldMapArray::~WorldMapArray()
550 unsigned int i;
552 for (i = 0; i<MapCount; i++) {
553 if (all_maps[i]) {
554 delete all_maps[i];
557 free( all_maps );
560 unsigned int WorldMapArray::FindAndSetCurrentMap(const ieResRef area)
562 unsigned int i, idx;
564 for (i = CurrentMap; i<MapCount; i++) {
565 if (all_maps[i]->GetArea (area, idx) ) {
566 CurrentMap = i;
567 return i;
570 for (i = 0; i<CurrentMap; i++) {
571 if (all_maps[i]->GetArea (area, idx) ) {
572 CurrentMap = i;
573 return i;
576 return CurrentMap;
579 void WorldMapArray::SetWorldMap(WorldMap *m, unsigned int index)
581 if (index<MapCount) {
582 all_maps[index]=m;
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];