1 /***************************************************************************
2  *   Copyright (C) 2009 by Andrey Afletdinov <fheroes2@gmail.com>          *
3  *                                                                         *
4  *   Part of the Free Heroes2 Engine:                                      *
5  *   http://sourceforge.net/projects/fheroes2                              *
6  *                                                                         *
7  *   This program is free software; you can redistribute it and/or modify  *
8  *   it under the terms of the GNU General Public License as published by  *
9  *   the Free Software Foundation; either version 2 of the License, or     *
10  *   (at your option) any later version.                                   *
11  *                                                                         *
12  *   This program is distributed in the hope that it will be useful,       *
13  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
14  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
15  *   GNU General Public License for more details.                          *
16  *                                                                         *
17  *   You should have received a copy of the GNU General Public License     *
18  *   along with this program; if not, write to the                         *
19  *   Free Software Foundation, Inc.,                                       *
20  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
21  ***************************************************************************/
22 
23 #include <algorithm>
24 
25 #include "artifact.h"
26 #include "campaign_data.h"
27 #include "campaign_savedata.h"
28 #include "castle.h"
29 #include "game_over.h"
30 #include "heroes.h"
31 #include "icn.h"
32 #include "kingdom.h"
33 #include "logging.h"
34 #include "maps_objects.h"
35 #include "maps_tiles.h"
36 #include "mp2.h"
37 #include "mp2_helper.h"
38 #include "race.h"
39 #include "rand.h"
40 #include "serialize.h"
41 #include "settings.h"
42 #include "tools.h"
43 #include "translations.h"
44 #include "world.h"
45 
46 namespace
47 {
48     const int32_t ultimateArtifactOffset = 9;
49 
getUltimateArtifact()50     Artifact getUltimateArtifact()
51     {
52         if ( Settings::Get().isCampaignGameType() ) {
53             const Campaign::ScenarioVictoryCondition victoryCondition = Campaign::getCurrentScenarioVictoryCondition();
54             if ( victoryCondition == Campaign::ScenarioVictoryCondition::OBTAIN_ULTIMATE_CROWN ) {
55                 return Artifact::ULTIMATE_CROWN;
56             }
57             else if ( victoryCondition == Campaign::ScenarioVictoryCondition::OBTAIN_SPHERE_NEGATION ) {
58                 return Artifact::SPHERE_NEGATION;
59             }
60         }
61 
62         return Artifact::Rand( Artifact::ART_ULTIMATE );
63     }
64 
fixCastleNames(AllCastles & castles)65     void fixCastleNames( AllCastles & castles )
66     {
67         // Find castles with no names.
68         std::vector<Castle *> castleWithNoName;
69         std::set<std::string> castleNames;
70 
71         for ( Castle * castle : castles ) {
72             if ( castle == nullptr ) {
73                 // How do we have an empty pointer in this container?
74                 assert( 0 );
75                 continue;
76             }
77 
78             const std::string & name = castle->GetName();
79 
80             if ( name.empty() ) {
81                 castleWithNoName.emplace_back( castle );
82             }
83             else {
84                 castleNames.emplace( name );
85             }
86         }
87 
88         if ( castleWithNoName.empty() ) {
89             return;
90         }
91 
92         for ( Castle * castle : castleWithNoName ) {
93             castle->setName( castleNames );
94             castleNames.emplace( castle->GetName() );
95         }
96     }
97 }
98 
99 namespace GameStatic
100 {
101     extern u32 uniq;
102 }
103 
LoadMapMP2(const std::string & filename)104 bool World::LoadMapMP2( const std::string & filename )
105 {
106     Reset();
107     Defaults();
108 
109     StreamFile fs;
110     if ( !fs.open( filename, "rb" ) ) {
111         DEBUG_LOG( DBG_GAME | DBG_ENGINE, DBG_WARN, "file not found " << filename.c_str() );
112         return false;
113     }
114 
115     // check (mp2, mx2) ID
116     if ( fs.getBE32() != 0x5C000000 )
117         return false;
118 
119     // endof
120     const size_t endof_mp2 = fs.size();
121     fs.seek( endof_mp2 - 4 );
122 
123     // read uniq
124     GameStatic::uniq = fs.getLE32();
125 
126     // offset data
127     fs.seek( MP2::MP2OFFSETDATA - 2 * 4 );
128 
129     // width
130     switch ( fs.getLE32() ) {
131     case Maps::SMALL:
132         width = Maps::SMALL;
133         break;
134     case Maps::MEDIUM:
135         width = Maps::MEDIUM;
136         break;
137     case Maps::LARGE:
138         width = Maps::LARGE;
139         break;
140     case Maps::XLARGE:
141         width = Maps::XLARGE;
142         break;
143     default:
144         width = 0;
145         break;
146     }
147 
148     // height
149     switch ( fs.getLE32() ) {
150     case Maps::SMALL:
151         height = Maps::SMALL;
152         break;
153     case Maps::MEDIUM:
154         height = Maps::MEDIUM;
155         break;
156     case Maps::LARGE:
157         height = Maps::LARGE;
158         break;
159     case Maps::XLARGE:
160         height = Maps::XLARGE;
161         break;
162     default:
163         height = 0;
164         break;
165     }
166 
167     if ( width == 0 || height == 0 || width != height ) {
168         DEBUG_LOG( DBG_GAME, DBG_WARN, "incrrect maps size" );
169         return false;
170     }
171 
172     const int32_t worldSize = width * height;
173 
174     // seek to ADDONS block
175     fs.skip( worldSize * MP2::SIZEOFMP2TILE );
176 
177     // read all addons
178     std::vector<MP2::mp2addon_t> vec_mp2addons( fs.getLE32() /* count mp2addon_t */ );
179 
180     for ( MP2::mp2addon_t & mp2addon : vec_mp2addons ) {
181         MP2::loadAddon( fs, mp2addon );
182     }
183 
184     const size_t endof_addons = fs.tell();
185     DEBUG_LOG( DBG_GAME, DBG_INFO, "read all tiles addons, tellg: " << endof_addons );
186 
187     // offset data
188     fs.seek( MP2::MP2OFFSETDATA );
189 
190     vec_tiles.resize( worldSize );
191 
192     // In the future we need to check 3 things which could point that this map is The Price of Loyalty version:
193     // - new object types
194     // - new artifact types on map
195     // - new artifact types in hero's bag
196 
197     MapsIndexes vec_object; // index maps for OBJ_CASTLE, OBJ_HEROES, OBJ_SIGN, OBJ_BOTTLE, OBJ_EVENT
198     vec_object.reserve( 100 );
199 
200     // read all tiles
201     for ( int32_t i = 0; i < worldSize; ++i ) {
202         Maps::Tiles & tile = vec_tiles[i];
203 
204         MP2::mp2tile_t mp2tile;
205         MP2::loadTile( fs, mp2tile );
206 
207         tile.Init( i, mp2tile );
208 
209         // Read extra information if it's present.
210         size_t addonIndex = mp2tile.nextAddonIndex;
211         while ( addonIndex > 0 ) {
212             if ( vec_mp2addons.size() <= addonIndex ) {
213                 DEBUG_LOG( DBG_GAME, DBG_WARN, "index out of range" );
214                 break;
215             }
216             tile.AddonsPushLevel1( vec_mp2addons[addonIndex] );
217             tile.AddonsPushLevel2( vec_mp2addons[addonIndex] );
218             addonIndex = vec_mp2addons[addonIndex].nextAddonIndex;
219         }
220 
221         tile.AddonsSort();
222 
223         switch ( mp2tile.mapObjectType ) {
224         case MP2::OBJ_RNDTOWN:
225         case MP2::OBJ_RNDCASTLE:
226         case MP2::OBJ_CASTLE:
227         case MP2::OBJ_HEROES:
228         case MP2::OBJ_SIGN:
229         case MP2::OBJ_BOTTLE:
230         case MP2::OBJ_EVENT:
231         case MP2::OBJ_SPHINX:
232         case MP2::OBJ_JAIL:
233             vec_object.push_back( i );
234             break;
235         default:
236             break;
237         }
238     }
239 
240     DEBUG_LOG( DBG_GAME, DBG_INFO, "read all tiles, tellg: " << fs.tell() );
241 
242     // after addons
243     fs.seek( endof_addons );
244 
245     // cood castles
246     // 72 x 3 byte (cx, cy, id)
247     for ( int32_t i = 0; i < 72; ++i ) {
248         const uint32_t cx = fs.get();
249         const uint32_t cy = fs.get();
250         const uint32_t id = fs.get();
251 
252         // empty block
253         if ( 0xFF == cx && 0xFF == cy )
254             continue;
255 
256         switch ( id ) {
257         case 0x00: // tower: knight
258         case 0x80: // castle: knight
259             vec_castles.AddCastle( new Castle( cx, cy, Race::KNGT ) );
260             break;
261 
262         case 0x01: // tower: barbarian
263         case 0x81: // castle: barbarian
264             vec_castles.AddCastle( new Castle( cx, cy, Race::BARB ) );
265             break;
266 
267         case 0x02: // tower: sorceress
268         case 0x82: // castle: sorceress
269             vec_castles.AddCastle( new Castle( cx, cy, Race::SORC ) );
270             break;
271 
272         case 0x03: // tower: warlock
273         case 0x83: // castle: warlock
274             vec_castles.AddCastle( new Castle( cx, cy, Race::WRLK ) );
275             break;
276 
277         case 0x04: // tower: wizard
278         case 0x84: // castle: wizard
279             vec_castles.AddCastle( new Castle( cx, cy, Race::WZRD ) );
280             break;
281 
282         case 0x05: // tower: necromancer
283         case 0x85: // castle: necromancer
284             vec_castles.AddCastle( new Castle( cx, cy, Race::NECR ) );
285             break;
286 
287         case 0x06: // tower: random
288         case 0x86: // castle: random
289             vec_castles.AddCastle( new Castle( cx, cy, Race::NONE ) );
290             break;
291 
292         default:
293             DEBUG_LOG( DBG_GAME, DBG_WARN,
294                        "castle block: "
295                            << "unknown id: " << id << ", maps index: " << cx + cy * width );
296             break;
297         }
298         // preload in to capture objects cache
299         map_captureobj.Set( Maps::GetIndexFromAbsPoint( cx, cy ), MP2::OBJ_CASTLE, Color::NONE );
300     }
301 
302     DEBUG_LOG( DBG_GAME, DBG_INFO, "read coord castles, tellg: " << fs.tell() );
303     fs.seek( endof_addons + ( 72 * 3 ) );
304 
305     // cood resource kingdoms
306     // 144 x 3 byte (cx, cy, id)
307     for ( int32_t i = 0; i < 144; ++i ) {
308         const uint32_t cx = fs.get();
309         const uint32_t cy = fs.get();
310         const uint32_t id = fs.get();
311 
312         // empty block
313         if ( 0xFF == cx && 0xFF == cy )
314             continue;
315 
316         switch ( id ) {
317         // mines: wood
318         case 0x00:
319             map_captureobj.Set( Maps::GetIndexFromAbsPoint( cx, cy ), MP2::OBJ_SAWMILL, Color::NONE );
320             break;
321         // mines: mercury
322         case 0x01:
323             map_captureobj.Set( Maps::GetIndexFromAbsPoint( cx, cy ), MP2::OBJ_ALCHEMYLAB, Color::NONE );
324             break;
325         // mines: ore
326         case 0x02:
327         // mines: sulfur
328         case 0x03:
329         // mines: crystal
330         case 0x04:
331         // mines: gems
332         case 0x05:
333         // mines: gold
334         case 0x06:
335             map_captureobj.Set( Maps::GetIndexFromAbsPoint( cx, cy ), MP2::OBJ_MINES, Color::NONE );
336             break;
337         // lighthouse
338         case 0x64:
339             map_captureobj.Set( Maps::GetIndexFromAbsPoint( cx, cy ), MP2::OBJ_LIGHTHOUSE, Color::NONE );
340             break;
341         // dragon city
342         case 0x65:
343             map_captureobj.Set( Maps::GetIndexFromAbsPoint( cx, cy ), MP2::OBJ_DRAGONCITY, Color::NONE );
344             break;
345         // abandoned mines
346         case 0x67:
347             map_captureobj.Set( Maps::GetIndexFromAbsPoint( cx, cy ), MP2::OBJ_ABANDONEDMINE, Color::NONE );
348             break;
349         default:
350             DEBUG_LOG( DBG_GAME, DBG_WARN,
351                        "kingdom block: "
352                            << "unknown id: " << id << ", maps index: " << cx + cy * width );
353             break;
354         }
355     }
356 
357     DEBUG_LOG( DBG_GAME, DBG_INFO, "read coord other resource, tellg: " << fs.tell() );
358     fs.seek( endof_addons + ( 72 * 3 ) + ( 144 * 3 ) );
359 
360     // byte: num obelisks (01 default)
361     fs.skip( 1 );
362 
363     // count final mp2 blocks
364     u32 countblock = 0;
365     while ( true ) {
366         const u32 l = fs.get();
367         const u32 h = fs.get();
368 
369         if ( 0 == h && 0 == l )
370             break;
371         else {
372             countblock = 256 * h + l - 1;
373         }
374     }
375 
376     // castle or heroes or (events, rumors, etc)
377     for ( u32 ii = 0; ii < countblock; ++ii ) {
378         s32 findobject = -1;
379 
380         // read block
381         size_t sizeblock = fs.getLE16();
382         std::vector<u8> pblock = fs.getRaw( sizeblock );
383 
384         for ( MapsIndexes::const_iterator it_index = vec_object.begin(); it_index != vec_object.end() && findobject < 0; ++it_index ) {
385             const Maps::Tiles & tile = vec_tiles[*it_index];
386 
387             // orders(quantity2, quantity1)
388             u32 orders = ( tile.GetQuantity2() ? tile.GetQuantity2() : 0 );
389             orders <<= 8;
390             orders |= tile.GetQuantity1();
391 
392             if ( orders && !( orders % 0x08 ) && ( ii + 1 == orders / 0x08 ) )
393                 findobject = *it_index;
394         }
395 
396         if ( 0 <= findobject ) {
397             const Maps::Tiles & tile = vec_tiles[findobject];
398 
399             switch ( tile.GetObject() ) {
400             case MP2::OBJ_CASTLE:
401                 // add castle
402                 if ( MP2::SIZEOFMP2CASTLE != pblock.size() ) {
403                     DEBUG_LOG( DBG_GAME, DBG_WARN,
404                                "read castle: "
405                                    << "incorrect size block: " << pblock.size() );
406                 }
407                 else {
408                     Castle * castle = getCastleEntrance( Maps::GetPoint( findobject ) );
409                     if ( castle ) {
410                         castle->LoadFromMP2( pblock );
411                         map_captureobj.SetColor( tile.GetIndex(), castle->GetColor() );
412                     }
413                     else {
414                         DEBUG_LOG( DBG_GAME, DBG_WARN,
415                                    "load castle: "
416                                        << "not found, index: " << findobject );
417                     }
418                 }
419                 break;
420             case MP2::OBJ_RNDTOWN:
421             case MP2::OBJ_RNDCASTLE:
422                 // add rnd castle
423                 if ( MP2::SIZEOFMP2CASTLE != pblock.size() ) {
424                     DEBUG_LOG( DBG_GAME, DBG_WARN,
425                                "read castle: "
426                                    << "incorrect size block: " << pblock.size() );
427                 }
428                 else {
429                     // Random castle's entrance tile is marked as OBJ_RNDCASTLE or OBJ_RNDTOWN instead of OBJ_CASTLE.
430                     Castle * castle = getCastle( Maps::GetPoint( findobject ) );
431                     if ( castle ) {
432                         castle->LoadFromMP2( pblock );
433                         Maps::UpdateCastleSprite( castle->GetCenter(), castle->GetRace(), castle->isCastle(), true );
434                         Maps::ReplaceRandomCastleObjectId( castle->GetCenter() );
435                         map_captureobj.SetColor( tile.GetIndex(), castle->GetColor() );
436                     }
437                     else {
438                         DEBUG_LOG( DBG_GAME, DBG_WARN,
439                                    "load castle: "
440                                        << "not found, index: " << findobject );
441                     }
442                 }
443                 break;
444             case MP2::OBJ_JAIL:
445                 // add jail
446                 if ( MP2::SIZEOFMP2HEROES != pblock.size() ) {
447                     DEBUG_LOG( DBG_GAME, DBG_WARN,
448                                "read heroes: "
449                                    << "incorrect size block: " << pblock.size() );
450                 }
451                 else {
452                     int race = Race::KNGT;
453                     switch ( pblock[0x3c] ) {
454                     case 1:
455                         race = Race::BARB;
456                         break;
457                     case 2:
458                         race = Race::SORC;
459                         break;
460                     case 3:
461                         race = Race::WRLK;
462                         break;
463                     case 4:
464                         race = Race::WZRD;
465                         break;
466                     case 5:
467                         race = Race::NECR;
468                         break;
469                     default:
470                         break;
471                     }
472 
473                     Heroes * hero = GetFreemanHeroes( race );
474 
475                     if ( hero ) {
476                         hero->LoadFromMP2( findobject, Color::NONE, hero->GetRace(), StreamBuf( pblock ) );
477                         hero->SetModes( Heroes::JAIL );
478                     }
479                 }
480                 break;
481             case MP2::OBJ_HEROES:
482                 // add heroes
483                 if ( MP2::SIZEOFMP2HEROES != pblock.size() ) {
484                     DEBUG_LOG( DBG_GAME, DBG_WARN,
485                                "read heroes: "
486                                    << "incorrect size block: " << pblock.size() );
487                 }
488                 else {
489                     std::pair<int, int> colorRace = Maps::Tiles::ColorRaceFromHeroSprite( tile.GetObjectSpriteIndex() );
490                     const Kingdom & kingdom = GetKingdom( colorRace.first );
491 
492                     if ( colorRace.second == Race::RAND && colorRace.first != Color::NONE )
493                         colorRace.second = kingdom.GetRace();
494 
495                     // check heroes max count
496                     if ( kingdom.AllowRecruitHero( false, 0 ) ) {
497                         Heroes * hero = nullptr;
498 
499                         if ( pblock[17] && pblock[18] < Heroes::BAX )
500                             hero = vec_heroes.Get( pblock[18] );
501 
502                         if ( !hero || !hero->isFreeman() )
503                             hero = vec_heroes.GetFreeman( colorRace.second );
504 
505                         if ( hero )
506                             hero->LoadFromMP2( findobject, colorRace.first, colorRace.second, StreamBuf( pblock ) );
507                     }
508                     else {
509                         DEBUG_LOG( DBG_GAME, DBG_WARN, "load heroes maximum" );
510                     }
511                 }
512                 break;
513             case MP2::OBJ_SIGN:
514             case MP2::OBJ_BOTTLE:
515                 // add sign or buttle
516                 if ( MP2::SIZEOFMP2SIGN - 1 < pblock.size() && 0x01 == pblock[0] ) {
517                     MapSign * obj = new MapSign();
518                     obj->LoadFromMP2( findobject, StreamBuf( pblock ) );
519                     map_objects.add( obj );
520                 }
521                 break;
522             case MP2::OBJ_EVENT:
523                 // add event maps
524                 if ( MP2::SIZEOFMP2EVENT - 1 < pblock.size() && 0x01 == pblock[0] ) {
525                     MapEvent * obj = new MapEvent();
526                     obj->LoadFromMP2( findobject, StreamBuf( pblock ) );
527                     map_objects.add( obj );
528                 }
529                 break;
530             case MP2::OBJ_SPHINX:
531                 // add riddle sphinx
532                 if ( MP2::SIZEOFMP2RIDDLE - 1 < pblock.size() && 0x00 == pblock[0] ) {
533                     MapSphinx * obj = new MapSphinx();
534                     obj->LoadFromMP2( findobject, StreamBuf( pblock ) );
535                     map_objects.add( obj );
536                 }
537                 break;
538             default:
539                 break;
540             }
541         }
542         // other events
543         else if ( 0x00 == pblock[0] ) {
544             // add event day
545             if ( MP2::SIZEOFMP2EVENT - 1 < pblock.size() && 1 == pblock[42] ) {
546                 vec_eventsday.emplace_back();
547                 vec_eventsday.back().LoadFromMP2( StreamBuf( pblock ) );
548             }
549             // add rumors
550             else if ( MP2::SIZEOFMP2RUMOR - 1 < pblock.size() ) {
551                 if ( pblock[8] ) {
552                     vec_rumors.push_back( StreamBuf( &pblock[8], pblock.size() - 8 ).toString() );
553                     DEBUG_LOG( DBG_GAME, DBG_INFO, "add rumors: " << vec_rumors.back() );
554                 }
555             }
556         }
557         // debug
558         else {
559             DEBUG_LOG( DBG_GAME, DBG_WARN, "read maps: unknown block addons, size: " << pblock.size() );
560         }
561     }
562 
563     fixCastleNames( vec_castles );
564 
565     // clear artifact flags to correctly generate random artifacts
566     fheroes2::ResetArtifactStats();
567 
568     const Settings & conf = Settings::Get();
569 
570     // do not let the player get a random artifact that allows him to win the game
571     if ( ( conf.ConditionWins() & GameOver::WINS_ARTIFACT ) == GameOver::WINS_ARTIFACT && !conf.WinsFindUltimateArtifact() ) {
572         const Artifact art = conf.WinsFindArtifactID();
573 
574         fheroes2::ExcludeArtifactFromRandom( art.GetID() );
575     }
576 
577     ProcessNewMap();
578 
579     DEBUG_LOG( DBG_GAME, DBG_INFO, "end load" );
580     return true;
581 }
582 
ProcessNewMap()583 void World::ProcessNewMap()
584 {
585     // modify other objects
586     for ( size_t i = 0; i < vec_tiles.size(); ++i ) {
587         Maps::Tiles & tile = vec_tiles[i];
588         Maps::Tiles::fixTileObjectType( tile );
589 
590         switch ( tile.GetObject() ) {
591         case MP2::OBJ_WITCHSHUT:
592         case MP2::OBJ_SHRINE1:
593         case MP2::OBJ_SHRINE2:
594         case MP2::OBJ_SHRINE3:
595         case MP2::OBJ_STONELITHS:
596         case MP2::OBJ_FOUNTAIN:
597         case MP2::OBJ_EVENT:
598         case MP2::OBJ_BOAT:
599         case MP2::OBJ_RNDARTIFACT:
600         case MP2::OBJ_RNDARTIFACT1:
601         case MP2::OBJ_RNDARTIFACT2:
602         case MP2::OBJ_RNDARTIFACT3:
603         case MP2::OBJ_RNDRESOURCE:
604         case MP2::OBJ_WATERCHEST:
605         case MP2::OBJ_TREASURECHEST:
606         case MP2::OBJ_ARTIFACT:
607         case MP2::OBJ_RESOURCE:
608         case MP2::OBJ_MAGICGARDEN:
609         case MP2::OBJ_WATERWHEEL:
610         case MP2::OBJ_WINDMILL:
611         case MP2::OBJ_WAGON:
612         case MP2::OBJ_SKELETON:
613         case MP2::OBJ_LEANTO:
614         case MP2::OBJ_CAMPFIRE:
615         case MP2::OBJ_FLOTSAM:
616         case MP2::OBJ_SHIPWRECKSURVIROR:
617         case MP2::OBJ_DERELICTSHIP:
618         case MP2::OBJ_SHIPWRECK:
619         case MP2::OBJ_GRAVEYARD:
620         case MP2::OBJ_PYRAMID:
621         case MP2::OBJ_DAEMONCAVE:
622         case MP2::OBJ_ABANDONEDMINE:
623         case MP2::OBJ_ALCHEMYLAB:
624         case MP2::OBJ_SAWMILL:
625         case MP2::OBJ_MINES:
626         case MP2::OBJ_TREEKNOWLEDGE:
627         case MP2::OBJ_BARRIER:
628         case MP2::OBJ_TRAVELLERTENT:
629         case MP2::OBJ_MONSTER:
630         case MP2::OBJ_RNDMONSTER:
631         case MP2::OBJ_RNDMONSTER1:
632         case MP2::OBJ_RNDMONSTER2:
633         case MP2::OBJ_RNDMONSTER3:
634         case MP2::OBJ_RNDMONSTER4:
635         case MP2::OBJ_ANCIENTLAMP:
636         case MP2::OBJ_WATCHTOWER:
637         case MP2::OBJ_EXCAVATION:
638         case MP2::OBJ_CAVE:
639         case MP2::OBJ_TREEHOUSE:
640         case MP2::OBJ_ARCHERHOUSE:
641         case MP2::OBJ_GOBLINHUT:
642         case MP2::OBJ_DWARFCOTT:
643         case MP2::OBJ_HALFLINGHOLE:
644         case MP2::OBJ_PEASANTHUT:
645         case MP2::OBJ_THATCHEDHUT:
646         case MP2::OBJ_RUINS:
647         case MP2::OBJ_TREECITY:
648         case MP2::OBJ_WAGONCAMP:
649         case MP2::OBJ_DESERTTENT:
650         case MP2::OBJ_TROLLBRIDGE:
651         case MP2::OBJ_DRAGONCITY:
652         case MP2::OBJ_CITYDEAD:
653             tile.QuantityUpdate();
654             break;
655 
656         case MP2::OBJ_WATERALTAR:
657         case MP2::OBJ_AIRALTAR:
658         case MP2::OBJ_FIREALTAR:
659         case MP2::OBJ_EARTHALTAR:
660         case MP2::OBJ_BARROWMOUNDS:
661             tile.QuantityReset();
662             tile.QuantityUpdate();
663             break;
664 
665         case MP2::OBJ_HEROES: {
666             // remove map editor sprite
667             if ( MP2::GetICNObject( tile.GetObjectTileset() ) == ICN::MINIHERO )
668                 tile.Remove( tile.GetObjectUID() );
669 
670             tile.SetHeroes( GetHeroes( Maps::GetPoint( static_cast<int32_t>( i ) ) ) );
671             break;
672         }
673 
674         default:
675             break;
676         }
677     }
678 
679     // add heroes to kingdoms
680     vec_kingdoms.AddHeroes( vec_heroes );
681 
682     // add castles to kingdoms
683     vec_kingdoms.AddCastles( vec_castles );
684 
685     const Settings & conf = Settings::Get();
686 
687     // update wins, loss conditions
688     if ( GameOver::WINS_HERO & conf.ConditionWins() ) {
689         const Heroes * hero = GetHeroes( conf.WinsMapsPositionObject() );
690         heroes_cond_wins = hero ? hero->GetID() : Heroes::UNKNOWN;
691     }
692     if ( GameOver::LOSS_HERO & conf.ConditionLoss() ) {
693         Heroes * hero = GetHeroes( conf.LossMapsPositionObject() );
694         if ( hero ) {
695             heroes_cond_loss = hero->GetID();
696             hero->SetModes( Heroes::NOTDISMISS | Heroes::NOTDEFAULTS );
697         }
698     }
699 
700     // Set Ultimate Artifact.
701     fheroes2::Point ultimate_pos;
702     MapsTiles::iterator it = std::find_if( vec_tiles.begin(), vec_tiles.end(), []( const Maps::Tiles & tile ) { return tile.isObject( MP2::OBJ_RNDULTIMATEARTIFACT ); } );
703     if ( vec_tiles.end() == it ) {
704         // generate position for ultimate
705         MapsIndexes pools;
706         pools.reserve( vec_tiles.size() / 2 );
707 
708         for ( size_t i = 0; i < vec_tiles.size(); ++i ) {
709             const Maps::Tiles & tile = vec_tiles[i];
710             const int32_t x = tile.GetIndex() % width;
711             if ( x < ultimateArtifactOffset || x >= width - ultimateArtifactOffset )
712                 continue;
713 
714             const int32_t y = tile.GetIndex() / width;
715             if ( y < ultimateArtifactOffset || y >= height - ultimateArtifactOffset )
716                 continue;
717 
718             if ( tile.GoodForUltimateArtifact() )
719                 pools.emplace_back( tile.GetIndex() );
720         }
721 
722         if ( !pools.empty() ) {
723             const int32_t pos = Rand::Get( pools );
724             ultimate_artifact.Set( pos, getUltimateArtifact() );
725             ultimate_pos = Maps::GetPoint( pos );
726         }
727     }
728     else {
729         // remove ultimate artifact sprite
730         it->Remove( it->GetObjectUID() );
731         it->setAsEmpty();
732         ultimate_artifact.Set( it->GetIndex(), getUltimateArtifact() );
733         ultimate_pos = ( *it ).GetCenter();
734     }
735 
736     PostLoad( true );
737 
738     // play with hero
739     vec_kingdoms.ApplyPlayWithStartingHero();
740 
741     // play with debug hero
742     if ( IS_DEVEL() ) {
743         // get first castle position
744         Kingdom & kingdom = GetKingdom( Color::GetFirst( Players::HumanColors() ) );
745 
746         if ( !kingdom.GetCastles().empty() ) {
747             const Castle * castle = kingdom.GetCastles().front();
748             const fheroes2::Point & cp = castle->GetCenter();
749             Heroes * hero = vec_heroes.Get( Heroes::DEBUG_HERO );
750 
751             if ( hero && !world.GetTiles( cp.x, cp.y + 1 ).GetHeroes() ) {
752                 hero->Recruit( castle->GetColor(), fheroes2::Point( cp.x, cp.y + 1 ) );
753             }
754         }
755     }
756 
757     vec_rumors.emplace_back( _( "The ultimate artifact is really the %{name}." ) );
758     StringReplace( vec_rumors.back(), "%{name}", ultimate_artifact.GetName() );
759 
760     vec_rumors.emplace_back( _( "The ultimate artifact may be found in the %{name} regions of the world." ) );
761 
762     if ( height / 3 > ultimate_pos.y ) {
763         if ( width / 3 > ultimate_pos.x )
764             StringReplace( vec_rumors.back(), "%{name}", _( "north-west" ) );
765         else if ( 2 * width / 3 > ultimate_pos.x )
766             StringReplace( vec_rumors.back(), "%{name}", _( "north" ) );
767         else
768             StringReplace( vec_rumors.back(), "%{name}", _( "north-east" ) );
769     }
770     else if ( 2 * height / 3 > ultimate_pos.y ) {
771         if ( width / 3 > ultimate_pos.x )
772             StringReplace( vec_rumors.back(), "%{name}", _( "west" ) );
773         else if ( 2 * width / 3 > ultimate_pos.x )
774             StringReplace( vec_rumors.back(), "%{name}", _( "center" ) );
775         else
776             StringReplace( vec_rumors.back(), "%{name}", _( "east" ) );
777     }
778     else {
779         if ( width / 3 > ultimate_pos.x )
780             StringReplace( vec_rumors.back(), "%{name}", _( "south-west" ) );
781         else if ( 2 * width / 3 > ultimate_pos.x )
782             StringReplace( vec_rumors.back(), "%{name}", _( "south" ) );
783         else
784             StringReplace( vec_rumors.back(), "%{name}", _( "south-east" ) );
785     }
786 
787     vec_rumors.emplace_back( _( "The truth is out there." ) );
788     vec_rumors.emplace_back( _( "The dark side is stronger." ) );
789     vec_rumors.emplace_back( _( "The end of the world is near." ) );
790     vec_rumors.emplace_back( _( "The bones of Lord Slayer are buried in the foundation of the arena." ) );
791     vec_rumors.emplace_back( _( "A Black Dragon will take out a Titan any day of the week." ) );
792     vec_rumors.emplace_back( _( "He told her: Yada yada yada...  and then she said: Blah, blah, blah..." ) );
793     vec_rumors.emplace_back( _( "An unknown force is being ressurected..." ) );
794 
795     vec_rumors.emplace_back( _( "Check the newest version of game at\nhttps://github.com/ihhub/\nfheroes2/releases" ) );
796 }
797