3 * Iter Vehemens ad Necem (IVAN)
4 * Copyright (C) Timo Kiviluoto
5 * Released under the GNU General
8 * See LICENSING which should be included
9 * along with this file for more details
13 /* Compiled through wmapset.cpp */
15 #define MAX_TEMPERATURE 27 //increase for a warmer world
16 #define LATITUDE_EFFECT 40 //increase for more effect
17 #define ALTITUDE_EFFECT 0.02
24 static const int DirX
[8] = { -1, -1, -1, 0, 0, 1, 1, 1 };
25 static const int DirY
[8] = { -1, 0, 1, -1, 1, -1, 0, 1 };
28 #define OceanType ocean::ProtoType.GetIndex()
29 #define SnowType snow::ProtoType.GetIndex()
30 #define GlacierType glacier::ProtoType.GetIndex()
31 #define EGForestType evergreenforest::ProtoType.GetIndex()
32 #define LForestType leafyforest::ProtoType.GetIndex()
33 #define SteppeType steppe::ProtoType.GetIndex()
34 #define DesertType desert::ProtoType.GetIndex()
35 #define JungleType jungle::ProtoType.GetIndex()
38 worldmap::worldmap () {}
39 continent
*worldmap::GetContinentUnder (v2 Pos
) const { return Continent
[ContinentBuffer
[Pos
.X
][Pos
.Y
]]; }
40 v2
worldmap::GetEntryPos (ccharacter
*, int I
) const { return EntryMap
.find(I
)->second
; }
41 continent
*worldmap::GetContinent (int I
) const { return Continent
[I
]; }
42 int worldmap::GetAltitude (v2 Pos
) { return AltitudeBuffer
[Pos
.X
][Pos
.Y
]; }
43 charactervector
&worldmap::GetPlayerGroup () { return PlayerGroup
; }
44 character
*worldmap::GetPlayerGroupMember (int c
) { return PlayerGroup
[c
]; }
47 worldmap::worldmap (int XSize
, int YSize
) : area(XSize
, YSize
) {
48 Map
= reinterpret_cast<wsquare
***>(area::Map
);
49 for (int x
= 0; x
< XSize
; ++x
) {
50 for (int y
= 0; y
< YSize
; ++y
) {
51 //Map[x][y] = new wsquare(this, v2(x, y));
52 //Map[x][y]->SetGWTerrain(ocean::Spawn());
57 AltitudeBuffer
= nullptr;
58 ContinentBuffer
= nullptr;
59 continent::TypeBuffer
= nullptr;
60 continent::AltitudeBuffer
= nullptr;
61 continent::ContinentBuffer
= nullptr;
65 worldmap::~worldmap () {
68 delete [] AltitudeBuffer
;
69 delete [] ContinentBuffer
;
70 for (uInt c
= 1; c
< Continent
.size(); ++c
) delete Continent
[c
];
71 for (uInt c
= 0; c
< PlayerGroup
.size(); ++c
) delete PlayerGroup
[c
];
72 Continent
.resize(1, 0);
74 continent::TypeBuffer
= nullptr;
75 continent::AltitudeBuffer
= nullptr;
76 continent::ContinentBuffer
= nullptr;
80 void worldmap::resetItAll (truth recreate
) {
81 Map
= reinterpret_cast<wsquare
***>(area::Map
);
82 for (int x
= 0; x
< XSize
; ++x
) {
83 for (int y
= 0; y
< YSize
; ++y
) {
84 if (Map
[x
][y
]) delete Map
[x
][y
];
86 Map
[x
][y
] = new wsquare(this, v2(x
, y
));
87 Map
[x
][y
]->SetGWTerrain(ocean::Spawn());
95 if (TypeBuffer
) delete [] TypeBuffer
;
96 if (AltitudeBuffer
) delete [] AltitudeBuffer
;
97 if (ContinentBuffer
) delete [] ContinentBuffer
;
98 Alloc2D(TypeBuffer
, XSize
, YSize
);
99 Alloc2D(AltitudeBuffer
, XSize
, YSize
);
100 Alloc2D(ContinentBuffer
, XSize
, YSize
);
101 continent::TypeBuffer
= TypeBuffer
;
102 continent::AltitudeBuffer
= AltitudeBuffer
;
103 continent::ContinentBuffer
= ContinentBuffer
;
108 void worldmap::Save (outputfile
&SaveFile
) const {
109 area::Save(SaveFile
);
110 SaveFile
.Write(reinterpret_cast<char*>(TypeBuffer
[0]), XSizeTimesYSize
*sizeof(uChar
));
111 SaveFile
.Write(reinterpret_cast<char*>(AltitudeBuffer
[0]), XSizeTimesYSize
*sizeof(short));
112 SaveFile
.Write(reinterpret_cast<char*>(ContinentBuffer
[0]), XSizeTimesYSize
*sizeof(uChar
));
113 for (feuLong c
= 0; c
< XSizeTimesYSize
; ++c
) Map
[0][c
]->Save(SaveFile
);
114 SaveFile
<< Continent
<< PlayerGroup
;
118 void worldmap::Load (inputfile
&SaveFile
) {
119 area::Load(SaveFile
);
120 Map
= reinterpret_cast<wsquare
***>(area::Map
);
121 Alloc2D(TypeBuffer
, XSize
, YSize
);
122 Alloc2D(AltitudeBuffer
, XSize
, YSize
);
123 Alloc2D(ContinentBuffer
, XSize
, YSize
);
124 SaveFile
.Read(reinterpret_cast<char*>(TypeBuffer
[0]), XSizeTimesYSize
*sizeof(uChar
));
125 SaveFile
.Read(reinterpret_cast<char*>(AltitudeBuffer
[0]), XSizeTimesYSize
*sizeof(short));
126 SaveFile
.Read(reinterpret_cast<char*>(ContinentBuffer
[0]), XSizeTimesYSize
*sizeof(uChar
));
127 continent::TypeBuffer
= TypeBuffer
;
128 continent::AltitudeBuffer
= AltitudeBuffer
;
129 continent::ContinentBuffer
= ContinentBuffer
;
130 for (int x
= 0; x
< XSize
; ++x
) {
131 for (int y
= 0; y
< YSize
; ++y
) {
133 Map
[x
][y
] = new wsquare(this, v2(x
, y
));
136 for (int x
= 0; x
< XSize
; ++x
) {
137 for (int y
= 0; y
< YSize
; ++y
) {
138 game::SetSquareInLoad(Map
[x
][y
]);
139 Map
[x
][y
]->Load(SaveFile
);
142 CalculateNeighbourBitmapPoses();
143 SaveFile
>> Continent
>> PlayerGroup
;
147 void worldmap::poiReset () {
148 for (int f
= 0; f
< game::poiCount(); ++f
) {
149 auto terra
= game::poiByIndex(f
);
150 terra
->SetRevealed(false);
151 terra
->SetPlaced(false);
152 terra
->SetGenerated(false);
153 auto cfg
= terra
->GetConfig();
154 // fix obvious scripting bugs
155 if (cfg
== NEW_ATTNAM
|| cfg
== UNDER_WATER_TUNNEL
|| cfg
== UNDER_WATER_TUNNEL_EXIT
|| cfg
== ELPURI_CAVE
) {
156 terra
->MustBeSkipped
= false;
158 //fprintf(stderr, "terra #%d prob is %d\n", terra->GetConfig(), terra->GetProbability());
159 terra
->MustBeSkipped
= (terra
->GetProbability() < RAND()%100+1);
160 //if (terra->MustBeSkipped) fprintf(stderr, "worldmap::poiReset: skipped POI with config #%d\n", terra->GetConfig());
166 void worldmap::poiPlaceAtMap (owterrain
*terra
, truth forceReveal
) {
167 if (!terra
) ABORT("cannot place nothing on worldmap!");
168 if (!terra
->IsGenerated()) ABORT("cannot place ungenerated something on worldmap!");
169 if (terra
->MustBeSkipped
) ABORT("cannot place skipped something on worldmap!");
170 // place it, if it is not placed yet
171 if (!terra
->IsPlaced()) {
172 terra
->SetPlaced(true);
173 GetWSquare(terra
->GetPosition())->ChangeOWTerrain(terra
->Clone());
174 SetEntryPos(terra
->GetConfig(), terra
->GetPosition());
175 //fprintf(stderr, "POI #%d placed at (%d,%d); attached dungeon is %d\n", terra->GetConfig(), terra->GetPosition().X, terra->GetPosition().Y, terra->GetAttachedDungeon());
177 //fprintf(stderr, "already placed...\n");
179 if (!IsValidPos(terra
->GetPosition())) ABORT("cannot place something on invalid worldmap position!");
180 if (forceReveal
|| (terra
->RevealEnvironmentInitially() && !terra
->IsRevealed())) {
181 //if (terra->IsRevealed()) fprintf(stderr, "re-revealing...\n");
182 terra
->SetRevealed(true);
183 RevealEnvironment(terra
->GetPosition(), 1);
188 ContinentVector
worldmap::poiFindPlacesFor (owterrain
*terra
, truth shuffle
) {
189 ContinentVector list
;
191 for (uInt c
= 1; c
< Continent
.size(); ++c
) {
192 if (terra
->MustBeSkipped
) continue;
193 //fprintf(stderr, " trying continent %u for %d...\n", c, terra->GetConfig());
194 if (terra
->IsSuitableContinent(Continent
[c
])) {
195 //fprintf(stderr, " FOUND!\n");
196 list
.push_back(Continent
[c
]);
199 if (shuffle
&& list
.size() > 1) {
200 for (uInt f
= 0; f
< list
.size(); ++f
) {
201 uInt swp
= (uInt
)RAND()%list
.size();
203 continent
*tmp
= list
[f
];
210 //fprintf(stderr, ">>>%u continent(s) for %d...\n", list.size(), terra->GetConfig());
215 static ContinentVector
createShuffledContinentList (const ContinentVector
&list
) {
217 for (auto &cont
: list
) res
.push_back(cont
);
218 if (res
.size() > 1) {
219 for (uInt f
= 0; f
< res
.size(); ++f
) {
220 uInt swp
= (uInt
)RAND()%res
.size();
222 continent
*tmp
= res
[f
];
232 truth
worldmap::poiIsOccupied (v2 pos
) {
233 //fprintf(stderr, "checking...\n");
234 for (int f
= 0; f
< game::poiCount(); ++f
) {
235 auto terra
= game::poiByIndex(f
);
236 if (!terra
) ABORT("Somone stole our terrain!");
237 if (!terra
->IsGenerated()) continue;
238 //fprintf(stderr, " checking #%d...\n", f);
239 if (terra
->GetPosition() == pos
) {
240 //fprintf(stderr, " CONFLICT!\n");
244 //fprintf(stderr, "checking complete!\n");
249 truth
worldmap::poiPlaceAttnamsAndUT (continent
*PetrusLikes
) {
250 if (!PetrusLikes
) return false; // oops
251 if (PetrusLikes
->GetSize() < 8) return false; // oops
253 if (!attnam
) ABORT("Who stole my Attnam?!");
254 if (!newattnam
) ABORT("Who stole my New Attnam?!");
255 if (!underwatertunnel
) ABORT("Who stole my UC Entry?!");
256 if (!underwatertunnelexit
) ABORT("Who stole my UC Exit?!");
258 v2 newattnamPos
= ERROR_V2
, tunnelEntryPos
= ERROR_V2
, tunnelExitPos
= ERROR_V2
;
261 truth Correct
= false;
262 for (int c1
= 0; c1
< 25; ++c1
) {
263 game::BusyAnimation();
264 for (int c2
= 1; c2
< 50; ++c2
) {
265 tunnelExitPos
= PetrusLikes
->GetRandomMember(-1, &Correct
);
266 if (!Correct
) return false; // no room, oops
268 for (int d1
= 0; d1
< 8; ++d1
) {
269 v2 Pos
= tunnelExitPos
+game::GetMoveVector(d1
);
270 if (IsValidPos(Pos
) && AltitudeBuffer
[Pos
.X
][Pos
.Y
] <= 0) {
271 int Distance
= 3+(RAND()&3);
276 tunnelEntryPos
= Pos
;
277 for (int c2
= 0; c2
< Distance
; ++c2
) {
278 tunnelEntryPos
+= game::GetMoveVector(d1
);
279 if (!IsValidPos(tunnelEntryPos
) || AltitudeBuffer
[tunnelEntryPos
.X
][tunnelEntryPos
.Y
] > 0) { Error
= true; break; }
283 for (x
= tunnelEntryPos
.X
-3; x
<= tunnelEntryPos
.X
+3; ++x
) {
284 for (y
= tunnelEntryPos
.Y
-3; y
<= tunnelEntryPos
.Y
+3; ++y
, ++Counter
) {
285 if (Counter
!= 0 && Counter
!= 6 && Counter
!= 42 && Counter
!= 48 &&
286 (!IsValidPos(x
, y
) || AltitudeBuffer
[x
][y
] > 0 || AltitudeBuffer
[x
][y
] < -350)) {
296 for (x
= 0; x
< XSize
; ++x
) if (TypeBuffer
[x
][tunnelEntryPos
.Y
] == JungleType
) { Error
= false; break; }
300 for (x
= tunnelEntryPos
.X
- 2; x
<= tunnelEntryPos
.X
+2; ++x
) {
301 for (y
= tunnelEntryPos
.Y
- 2; y
<= tunnelEntryPos
.Y
+2; ++y
, ++Counter
) {
302 if (Counter
!= 0 && Counter
!= 4 && Counter
!= 20 && Counter
!= 24) AltitudeBuffer
[x
][y
] /= 2;
306 AltitudeBuffer
[tunnelEntryPos
.X
][tunnelEntryPos
.Y
] = 1+RAND()%50;
307 TypeBuffer
[tunnelEntryPos
.X
][tunnelEntryPos
.Y
] = JungleType
;
308 GetWSquare(tunnelEntryPos
)->ChangeGWTerrain(jungle::Spawn());
311 for (NewAttnamIndex
= RAND()&7; NewAttnamIndex
== 7-d1
; NewAttnamIndex
= RAND()&7) {}
312 newattnamPos
= tunnelEntryPos
+game::GetMoveVector(NewAttnamIndex
);
314 static const int DiagonalDir
[4] = { 0, 2, 5, 7 };
315 static const int NotDiagonalDir
[4] = { 1, 3, 4, 6 };
316 static const int AdjacentDir
[4][2] = { { 0, 1 }, { 0, 2 }, { 1, 3 }, { 2, 3 } };
317 truth Raised
[] = { false, false, false, false };
319 for (int d2
= 0; d2
< 4; ++d2
) {
320 if (NotDiagonalDir
[d2
] != 7-d1
&& (NotDiagonalDir
[d2
] == NewAttnamIndex
|| !(RAND()&2))) {
321 v2 Pos
= tunnelEntryPos
+game::GetMoveVector(NotDiagonalDir
[d2
]);
322 AltitudeBuffer
[Pos
.X
][Pos
.Y
] = 1+RAND()%50;
323 TypeBuffer
[Pos
.X
][Pos
.Y
] = JungleType
;
324 GetWSquare(Pos
)->ChangeGWTerrain(jungle::Spawn());
329 for (int d2
= 0; d2
< 4; ++d2
) {
330 if (DiagonalDir
[d2
] != 7-d1
&&
331 (DiagonalDir
[d2
] == NewAttnamIndex
||
332 (Raised
[AdjacentDir
[d2
][0]] && Raised
[AdjacentDir
[d2
][1]] && !(RAND()&2)))) {
333 v2 Pos
= tunnelEntryPos
+game::GetMoveVector(DiagonalDir
[d2
]);
335 AltitudeBuffer
[Pos
.X
][Pos
.Y
] = 1+RAND()%50;
336 TypeBuffer
[Pos
.X
][Pos
.Y
] = JungleType
;
337 GetWSquare(Pos
)->ChangeGWTerrain(jungle::Spawn());
348 if (!Correct
) return false;
349 if (newattnamPos
== ERROR_V2
|| tunnelEntryPos
== ERROR_V2
|| tunnelExitPos
== ERROR_V2
) return false;
351 //fprintf(stderr, "UC and New Attnam were successfully placed...\n");
352 // tunnel entry, tunnel exit and New Attnam are ok, find a place for Attnam
353 game::BusyAnimation();
355 // spawn and reveal all special places
356 for (int f
= 0; f
< game::poiCount(); ++f
) {
357 auto terra
= game::poiByIndex(f
);
358 if (terra
->GetConfig() == NEW_ATTNAM
) { terra
->SetGenerated(true); terra
->SetPosition(newattnamPos
); poiPlaceAtMap(terra
); }
359 else if (terra
->GetConfig() == UNDER_WATER_TUNNEL
) { terra
->SetGenerated(true); terra
->SetPosition(tunnelEntryPos
); poiPlaceAtMap(terra
); }
360 else if (terra
->GetConfig() == UNDER_WATER_TUNNEL_EXIT
) { terra
->SetGenerated(true); terra
->SetPosition(tunnelExitPos
); poiPlaceAtMap(terra
); }
368 void worldmap::Generate () {
369 Alloc2D(OldAltitudeBuffer
, XSize
, YSize
);
370 Alloc2D(OldTypeBuffer
, XSize
, YSize
);
372 //continent* poiContinents[CONFIG_TABLE_SIZE]; // max number of configs
375 //fprintf(stderr, "generating new planet...\n");
376 resetItAll(true); // recreate
382 CalculateContinents();
384 if (Continent
.size() < 2) ABORT("Strange things happens in Universe...");
385 //fprintf(stderr, "%u continents generated...\n", Continent.size());
387 //fprintf(stderr, "resetting POI info...\n");
388 poiReset(); // this also sets `owterrain::MustBeSkipped`, using spawn probabilities
389 //memset(poiContinents, 0, sizeof(poiContinents));
391 // find place for attnam
392 if (!attnam
) ABORT("Who stole my Attnam?!");
394 game::BusyAnimation();
395 auto PerfectForAttnam
= poiFindPlacesFor(attnam
, true); // shuffle results
396 if (PerfectForAttnam
.size() == 0) {
397 //fprintf(stderr, "no country for old man...\n");
401 //fprintf(stderr, "trying to find room for other POIs...\n");
402 // try to place all initial places, and check if other places are (roughly) ok
403 continent
*PetrusLikes
= nullptr;
404 truth success
= false;
405 for (auto &cont
: PerfectForAttnam
) {
406 //fprintf(stderr, "trying to place special pois...\n");
407 if (!poiPlaceAttnamsAndUT(cont
)) continue; // alas
408 // WARNING! from here, we cannot continue checking continents on failure!
409 //fprintf(stderr, "checking other pois...\n");
411 for (int f
= 0; f
< game::poiCount(); ++f
) {
412 auto terra
= game::poiByIndex(f
);
413 if (terra
->MustBeSkipped
) continue;
414 if (terra
->IsGenerated()) continue;
415 if (terra
->GetWantContinentWith() != attnam
->GetConfig()) continue;
416 //fprintf(stderr, " f=%d; cidx=%d (%d)\n", f, terra->GetWantContinentWith(), attnam->GetConfig());
417 if (!terra
->IsSuitableContinent(cont
)) {
418 //fprintf(stderr, " OOPS!(0)\n");
419 if (!terra
->CanBeSkipped()) {
420 //fprintf(stderr, " OOPS!(1)\n");
424 terra
->MustBeSkipped
= true;
426 //poiContinents[f] = cont;
429 // we can't continue looping here
430 if (success
) PetrusLikes
= cont
; // ok, this continent can be used for Petrus' needs
433 if (!PetrusLikes
) continue; // alas
435 //TODO: check and assign other continents
436 // here, we should sort POI list in order of placement, pick continents, and so on
437 // but i'll leave that for some indefinite future
441 for (int f = 0; f < game::poiCount(); ++f) {
442 auto terra = game::poiByIndex(f);
443 if (terra->MustBeSkipped) continue;
444 if (terra->IsGenerated()) continue;
445 auto wantc = terra->GetWantContinentWith();
447 if (wantc == attnam->GetConfig()) {
448 // yep, already checked
449 poiContinents[f] = PetrusLikes;
452 // New Attnam or UC Entry island is too small to place anything
453 if (wantc == newattnam->GetConfig() || wantc == underwatertunnel->GetConfig()) {
454 ABORT("Cannot place anything near New Attnam!");
456 // list of suitable continents
457 ContinentVector clist;
458 // 0: pick random continent
460 clist = createShuffledContinentList(Continent);
468 //fprintf(stderr, "spawining other pois...\n");
470 for (int f
= 0; f
< game::poiCount(); ++f
) {
471 auto terra
= game::poiByIndex(f
);
472 if (terra
->MustBeSkipped
) continue;
473 if (terra
->IsGenerated()) continue;
474 //fprintf(stderr, "trying poicfg #%d...\n", terra->GetConfig());
475 //FIXME: find continent for this place
476 continent
*cont
= nullptr;
477 auto wantc
= terra
->GetWantContinentWith();
479 if (wantc
== attnam
->GetConfig() || wantc
== underwatertunnel
->GetConfig()) {
480 // yep, already checked
482 } else if (wantc
== 0) {
483 // pick random continent
484 auto clist
= createShuffledContinentList(Continent
);
485 for (auto &cc
: clist
) if (terra
->IsSuitableContinent(cc
)) { cont
= cc
; break; }
486 } else if (wantc
== newattnam
->GetConfig() || wantc
== underwatertunnel
->GetConfig()) {
487 // New Attnam or UC Entry island is too small to place anything
488 ABORT("Cannot place anything near New Attnam!");
492 // if we can skip this dungeon, then skip it, otherwise signal failure
493 if (!terra
->CanBeSkipped()) { success
= false; break; }
494 terra
->MustBeSkipped
= true;
495 success
= true; // in case this is last POI
498 // get random position for this poi
500 game::BusyAnimation();
501 auto possiblePlaces
= cont
->GetShuffledMembers(terra
->CanBeOnAnyTerrain() ? -1 : terra
->GetNativeGTerrainType());
503 for (auto &ppos
: possiblePlaces
) {
504 if (!poiIsOccupied(ppos
)) { poipos
= ppos
; success
= true; break; } // ok
507 // if we can skip this dungeon, then skip it, otherwise signal failure
508 if (!terra
->CanBeSkipped()) break;
509 terra
->MustBeSkipped
= true;
510 success
= true; // in case this is last POI
513 // ok, we can place it
514 //fprintf(stderr, "place poicfg #%d at (%d,%d)...\n", terra->GetConfig(), poipos.X, poipos.Y);
515 terra
->SetGenerated(true);
516 terra
->SetPosition(poipos
);
517 if (terra
->PlaceInitially()) poiPlaceAtMap(terra
);
520 // if something's failed, do it all again
521 if (!success
) continue;
523 // player just exited new attnam
524 PLAYER
->PutTo(newattnam
->GetPosition());
526 CalculateLuminances();
527 CalculateNeighbourBitmapPoses();
528 // break infinite loop, we're done
533 delete [] OldAltitudeBuffer
;
534 delete [] OldTypeBuffer
;
538 void worldmap::RandomizeAltitude () {
539 game::BusyAnimation();
540 for (int x
= 0; x
< XSize
; ++x
) {
541 for (int y
= 0; y
< YSize
; ++y
) {
542 AltitudeBuffer
[x
][y
] = 4000-RAND()%8000;
548 void worldmap::SmoothAltitude () {
549 for (int c
= 0; c
< 10; ++c
) {
550 game::BusyAnimation();
552 for (y
= 0; y
< YSize
; ++y
) SafeSmooth(0, y
);
553 for (x
= 1; x
< XSize
- 1; ++x
) {
555 for (y
= 1; y
< YSize
- 1; ++y
) FastSmooth(x
, y
);
556 SafeSmooth(x
, YSize
- 1);
558 for (y
= 0; y
< YSize
; ++y
) SafeSmooth(XSize
- 1, y
);
563 void worldmap::FastSmooth (int x
, int y
) {
564 sLong HeightNear
= 0;
566 for (d
= 0; d
< 4; ++d
) HeightNear
+= OldAltitudeBuffer
[x
+ DirX
[d
]][y
+ DirY
[d
]];
567 for (d
= 4; d
< 8; ++d
) HeightNear
+= AltitudeBuffer
[x
+ DirX
[d
]][y
+ DirY
[d
]];
568 OldAltitudeBuffer
[x
][y
] = AltitudeBuffer
[x
][y
];
569 AltitudeBuffer
[x
][y
] = HeightNear
>> 3;
573 void worldmap::SafeSmooth (int x
, int y
) {
574 sLong HeightNear
= 0;
575 int d
, SquaresNear
= 0;
576 for (d
= 0; d
< 4; ++d
) {
579 if (IsValidPos(X
, Y
)) {
580 HeightNear
+= OldAltitudeBuffer
[X
][Y
];
584 for (d
= 4; d
< 8; ++d
) {
587 if (IsValidPos(X
, Y
)) {
588 HeightNear
+= AltitudeBuffer
[X
][Y
];
592 OldAltitudeBuffer
[x
][y
] = AltitudeBuffer
[x
][y
];
593 AltitudeBuffer
[x
][y
] = HeightNear
/ SquaresNear
;
597 void worldmap::GenerateClimate () {
598 game::BusyAnimation();
599 for (int y
= 0; y
< YSize
; ++y
) {
600 double DistanceFromEquator
= fabs(double(y
) / YSize
- 0.5);
601 truth LatitudeRainy
= DistanceFromEquator
<= 0.05 || (DistanceFromEquator
> 0.25 && DistanceFromEquator
<= 0.45);
602 for (int x
= 0; x
< XSize
; ++x
) {
603 if (AltitudeBuffer
[x
][y
] <= 0) {
604 TypeBuffer
[x
][y
] = OceanType
;
607 truth Rainy
= LatitudeRainy
;
609 for(int d
= 0; d
< 8; ++d
) {
610 v2 Pos
= v2(x
, y
) + game::GetMoveVector(d
);
611 if (IsValidPos(Pos
) && AltitudeBuffer
[Pos
.X
][Pos
.Y
] <= 0) {
617 int Temperature
= int(MAX_TEMPERATURE
-DistanceFromEquator
*LATITUDE_EFFECT
-AltitudeBuffer
[x
][y
]*ALTITUDE_EFFECT
);
619 if (Temperature
<= COLD
) Type
= Rainy
? SnowType
: GlacierType
;
620 else if (Temperature
<= MEDIUM
) Type
= Rainy
? EGForestType
: SnowType
;
621 else if (Temperature
<= WARM
) Type
= Rainy
? LForestType
: SteppeType
;
622 else if (Temperature
<= HOT
) Type
= Rainy
? LForestType
: DesertType
;
623 else Type
= Rainy
? JungleType
: DesertType
;
624 TypeBuffer
[x
][y
] = Type
;
630 void worldmap::SmoothClimate () {
631 for (int c
= 0; c
< 3; ++c
) {
632 game::BusyAnimation();
633 for (int x
= 0; x
< XSize
; ++x
) {
634 for (int y
= 0; y
< YSize
; ++y
) {
635 if ((OldTypeBuffer
[x
][y
] = TypeBuffer
[x
][y
]) != OceanType
) {
636 TypeBuffer
[x
][y
] = WhatTerrainIsMostCommonAroundCurrentTerritorySquareIncludingTheSquareItself(x
, y
);
641 game::BusyAnimation();
642 for (int x
= 0; x
< XSize
; ++x
) {
643 for (int y
= 0; y
< YSize
; ++y
) {
644 auto terraProto
= protocontainer
<gwterrain
>::GetProto(TypeBuffer
[x
][y
]);
645 if (!terraProto
) ABORT("Oops! No gwterrain prototype for type #%d!\n", TypeBuffer
[x
][y
]);
646 Map
[x
][y
]->ChangeGWTerrain(terraProto
->Spawn());
653 #define ANALYZE_TYPE(type) {\
655 for (c = 0; c < u; ++c) if (T == UsedType[c]) { ++TypeAmount[c]; break; }\
656 if (c == u) { UsedType[u] = T; TypeAmount[u++] = 1; }\
661 int worldmap::WhatTerrainIsMostCommonAroundCurrentTerritorySquareIncludingTheSquareItself (int x
, int y
) {
665 UsedType
[0] = TypeBuffer
[x
][y
];
667 for (d
= 0; d
< 4; ++d
) {
670 if (IsValidPos(X
, Y
)) ANALYZE_TYPE(OldTypeBuffer
[X
][Y
]);
672 for (d
= 4; d
< 8; ++d
) {
675 if (IsValidPos(X
, Y
)) ANALYZE_TYPE(TypeBuffer
[X
][Y
]);
678 for (c
= 1; c
< u
; ++c
) if (TypeAmount
[c
] > TypeAmount
[MostCommon
] && UsedType
[c
] != OceanType
) MostCommon
= c
;
679 return UsedType
[MostCommon
];
683 void worldmap::CalculateContinents () {
684 for (uInt c
= 1; c
< Continent
.size(); ++c
) delete Continent
[c
];
685 Continent
.resize(1, 0);
686 memset(ContinentBuffer
[0], 0, XSizeTimesYSize
*sizeof(uChar
));
687 game::BusyAnimation();
688 for (int x
= 0; x
< XSize
; ++x
) {
689 for (int y
= 0; y
< YSize
; ++y
) {
690 if (AltitudeBuffer
[x
][y
] > 0) {
691 truth Attached
= false;
692 for (int d
= 0; d
< 8; ++d
) {
693 v2 Pos
= v2(x
, y
)+game::GetMoveVector(d
);
694 if (IsValidPos(Pos
)) {
695 cint NearCont
= ContinentBuffer
[Pos
.X
][Pos
.Y
];
697 cint ThisCont
= ContinentBuffer
[x
][y
];
699 if (ThisCont
!= NearCont
) {
700 if (Continent
[ThisCont
]->GetSize() < Continent
[NearCont
]->GetSize()) {
701 Continent
[ThisCont
]->AttachTo(Continent
[NearCont
]);
703 Continent
[NearCont
]->AttachTo(Continent
[ThisCont
]);
707 Continent
[NearCont
]->Add(v2(x
, y
));
714 if (Continent
.size() == 255) {
715 RemoveEmptyContinents();
716 if (Continent
.size() == 255) ABORT("Valpurus shall not carry more continents!");
718 continent
*NewContinent
= new continent(Continent
.size());
719 NewContinent
->Add(v2(x
, y
));
720 Continent
.push_back(NewContinent
);
725 RemoveEmptyContinents();
726 for (uInt c
= 1; c
< Continent
.size(); ++c
) Continent
[c
]->GenerateInfo();
730 void worldmap::RemoveEmptyContinents () {
731 for (uInt c
= 1; c
< Continent
.size(); ++c
) {
732 if (!Continent
[c
]->GetSize()) {
733 for (uInt i
= Continent
.size()-1; i
>= c
; --i
) {
734 if (Continent
[i
]->GetSize()) {
735 Continent
[i
]->AttachTo(Continent
[c
]);
737 Continent
.pop_back();
741 Continent
.pop_back();
749 void worldmap::Draw (truth
) const {
750 cint XMin
= Max(game::GetCamera().X
, 0);
751 cint YMin
= Max(game::GetCamera().Y
, 0);
752 cint XMax
= Min(XSize
, game::GetCamera().X
+game::GetScreenXSize());
753 cint YMax
= Min(YSize
, game::GetCamera().Y
+game::GetScreenYSize());
754 blitdata BlitData
= {
758 { TILE_SIZE
, TILE_SIZE
},
761 ALLOW_ANIMATE
|ALLOW_ALPHA
763 if (!game::GetSeeWholeMapCheatMode()) {
764 for (int x
= XMin
; x
< XMax
; ++x
) {
765 BlitData
.Dest
= game::CalculateScreenCoordinates(v2(x
, YMin
));
766 wsquare
**Square
= &Map
[x
][YMin
];
767 for (int y
= YMin
; y
< YMax
; ++y
, ++Square
, BlitData
.Dest
.Y
+= TILE_SIZE
) {
768 if ((*Square
)->LastSeen
) (*Square
)->Draw(BlitData
);
772 for (int x
= XMin
; x
< XMax
; ++x
) {
773 BlitData
.Dest
= game::CalculateScreenCoordinates(v2(x
, YMin
));
774 wsquare
**Square
= &Map
[x
][YMin
];
775 for (int y
= YMin
; y
< YMax
; ++y
, ++Square
, BlitData
.Dest
.Y
+= TILE_SIZE
) (*Square
)->Draw(BlitData
);
781 void worldmap::CalculateLuminances () {
782 for (feuLong c
= 0; c
< XSizeTimesYSize
; ++c
) Map
[0][c
]->CalculateLuminance();
786 void worldmap::CalculateNeighbourBitmapPoses () {
787 for (feuLong c
= 0; c
< XSizeTimesYSize
; ++c
) Map
[0][c
]->GetGWTerrain()->CalculateNeighbourBitmapPoses();
791 wsquare
*worldmap::GetNeighbourWSquare (v2 Pos
, int I
) const {
792 Pos
+= game::GetMoveVector(I
);
793 if (Pos
.X
>= 0 && Pos
.Y
>= 0 && Pos
.X
< XSize
&& Pos
.Y
< YSize
) return Map
[Pos
.X
][Pos
.Y
];
798 void worldmap::RevealEnvironment (v2 Pos
, int Radius
) {
800 femath::CalculateEnvironmentRectangle(Rect
, Border
, Pos
, Radius
);
801 for (int x
= Rect
.X1
; x
<= Rect
.X2
; ++x
)
802 for (int y
= Rect
.Y1
; y
<= Rect
.Y2
; ++y
)
803 Map
[x
][y
]->SignalSeen();
807 outputfile
&operator << (outputfile
&SaveFile
, const worldmap
*WorldMap
) {
808 WorldMap
->Save(SaveFile
);
813 inputfile
&operator >> (inputfile
&SaveFile
, worldmap
*&WorldMap
) {
814 WorldMap
= new worldmap
;
815 WorldMap
->Load(SaveFile
);
820 void worldmap::UpdateLOS () {
821 game::RemoveLOSUpdateRequest();
822 int Radius
= PLAYER
->GetLOSRange();
823 sLong RadiusSquare
= Radius
*Radius
;
824 v2 Pos
= PLAYER
->GetPos();
826 femath::CalculateEnvironmentRectangle(Rect
, Border
, Pos
, Radius
);
827 for (int x
= Rect
.X1
; x
<= Rect
.X2
; ++x
)
828 for (int y
= Rect
.Y1
; y
<= Rect
.Y2
; ++y
)
829 if (sLong(HypotSquare(Pos
.X
-x
, Pos
.Y
-y
)) <= RadiusSquare
) Map
[x
][y
]->SignalSeen();