2013-02-04 13:29:59 +03:00
/*
* MapFormatH3M . cpp , part of VCMI engine
*
* Authors : listed in file AUTHORS in main folder
*
* License : GNU General Public License v2 .0 or later
* Full text of license available in license . txt file , in main folder
*
*/
# include "StdInc.h"
2014-01-30 21:56:31 +03:00
# include "MapFormatH3M.h"
2023-04-02 18:56:10 +02:00
2013-02-04 13:29:59 +03:00
# include "CMap.h"
2023-04-02 18:56:10 +02:00
# include "MapReaderH3M.h"
2013-02-04 13:29:59 +03:00
# include "../CCreatureHandler.h"
2013-12-29 14:27:38 +03:00
# include "../CGeneralTextHandler.h"
2013-02-04 13:29:59 +03:00
# include "../CHeroHandler.h"
2023-04-05 13:08:41 +02:00
# include "../CSkillHandler.h"
# include "../CStopWatch.h"
# include "../RiverHandler.h"
# include "../RoadHandler.h"
# include "../TerrainHandler.h"
# include "../TextOperations.h"
# include "../VCMI_Lib.h"
# include "../filesystem/CBinaryReader.h"
# include "../filesystem/Filesystem.h"
2014-06-05 14:19:47 +03:00
# include "../mapObjects/CObjectClassesHandler.h"
2014-06-05 19:52:14 +03:00
# include "../mapObjects/MapObjects.h"
2023-04-05 13:08:41 +02:00
# include "../spells/CSpellHandler.h"
2013-02-04 13:29:59 +03:00
2023-04-02 18:56:10 +02:00
# include <boost/crc.hpp>
2022-07-26 15:07:42 +02:00
VCMI_LIB_NAMESPACE_BEGIN
2013-02-04 13:29:59 +03:00
2023-03-14 17:54:37 +02:00
static std : : string convertMapName ( std : : string input )
{
boost : : algorithm : : to_lower ( input ) ;
boost : : algorithm : : trim ( input ) ;
2023-04-03 14:02:15 +02:00
size_t slashPos = input . find_last_of ( ' / ' ) ;
2023-03-14 17:54:37 +02:00
2023-04-05 13:08:41 +02:00
if ( slashPos ! = std : : string : : npos )
2023-03-14 17:54:37 +02:00
return input . substr ( slashPos + 1 ) ;
return input ;
}
2023-02-25 17:44:15 +02:00
CMapLoaderH3M : : CMapLoaderH3M ( const std : : string & mapName , const std : : string & modName , const std : : string & encodingName , CInputStream * stream )
2023-02-24 16:15:45 +02:00
: map ( nullptr )
2023-04-02 18:56:10 +02:00
, reader ( new MapReaderH3M ( stream ) )
2023-02-24 16:15:45 +02:00
, inputStream ( stream )
2023-03-14 17:54:37 +02:00
, mapName ( convertMapName ( mapName ) )
2023-02-25 17:44:15 +02:00
, modName ( modName )
2023-02-25 01:18:15 +02:00
, fileEncoding ( encodingName )
2013-02-04 13:29:59 +03:00
{
}
2023-02-11 18:30:06 +02:00
//must be instantiated in .cpp file for access to complete types of all member fields
CMapLoaderH3M : : ~ CMapLoaderH3M ( ) = default ;
2013-02-04 13:29:59 +03:00
std : : unique_ptr < CMap > CMapLoaderH3M : : loadMap ( )
{
// Init map object by parsing the input buffer
map = new CMap ( ) ;
mapHeader = std : : unique_ptr < CMapHeader > ( dynamic_cast < CMapHeader * > ( map ) ) ;
init ( ) ;
2017-07-12 21:01:10 +02:00
return std : : unique_ptr < CMap > ( dynamic_cast < CMap * > ( mapHeader . release ( ) ) ) ;
2013-02-04 13:29:59 +03:00
}
std : : unique_ptr < CMapHeader > CMapLoaderH3M : : loadMapHeader ( )
{
// Read header
2022-12-07 23:36:20 +02:00
mapHeader = std : : make_unique < CMapHeader > ( ) ;
2013-02-04 13:29:59 +03:00
readHeader ( ) ;
return std : : move ( mapHeader ) ;
}
void CMapLoaderH3M : : init ( )
{
2023-04-02 18:56:10 +02:00
//TODO: get rid of double input process
2013-02-07 20:02:15 +03:00
si64 temp_size = inputStream - > getSize ( ) ;
inputStream - > seek ( 0 ) ;
2023-02-11 18:30:06 +02:00
auto * temp_buffer = new ui8 [ temp_size ] ;
2023-04-05 13:08:41 +02:00
inputStream - > read ( temp_buffer , temp_size ) ;
2013-02-07 20:02:15 +03:00
2013-02-04 13:29:59 +03:00
// Compute checksum
2023-04-05 13:08:41 +02:00
boost : : crc_32_type result ;
2013-02-07 20:02:15 +03:00
result . process_bytes ( temp_buffer , temp_size ) ;
2013-02-04 13:29:59 +03:00
map - > checksum = result . checksum ( ) ;
2023-04-05 13:08:41 +02:00
delete [ ] temp_buffer ;
2013-02-07 20:02:15 +03:00
inputStream - > seek ( 0 ) ;
2013-02-04 13:29:59 +03:00
readHeader ( ) ;
2013-05-19 01:30:48 +03:00
map - > allHeroes . resize ( map - > allowedHeroes . size ( ) ) ;
2013-02-04 13:29:59 +03:00
readDisposedHeroes ( ) ;
2023-04-03 19:51:02 +02:00
readMapOptions ( ) ;
2013-02-04 13:29:59 +03:00
readAllowedArtifacts ( ) ;
readAllowedSpellsAbilities ( ) ;
readRumors ( ) ;
readPredefinedHeroes ( ) ;
readTerrain ( ) ;
readDefInfo ( ) ;
readObjects ( ) ;
readEvents ( ) ;
2014-04-01 14:53:28 +03:00
map - > calculateGuardingGreaturePositions ( ) ;
2016-11-13 12:38:42 +02:00
afterRead ( ) ;
2013-02-04 13:29:59 +03:00
}
void CMapLoaderH3M : : readHeader ( )
{
// Map version
2023-04-02 18:56:10 +02:00
mapHeader - > version = static_cast < EMapFormat > ( reader - > readUInt32 ( ) ) ;
2023-04-10 13:01:09 +02:00
if ( mapHeader - > version = = EMapFormat : : HOTA )
2013-02-04 13:29:59 +03:00
{
2023-04-04 14:40:16 +02:00
uint32_t hotaVersion = reader - > readUInt32 ( ) ;
2023-04-03 19:51:02 +02:00
features = MapFormatFeaturesH3M : : find ( mapHeader - > version , hotaVersion ) ;
reader - > setFormatLevel ( mapHeader - > version , hotaVersion ) ;
2023-04-03 17:56:49 +02:00
2023-04-09 00:03:43 +02:00
if ( hotaVersion > 0 )
{
reader - > skipZero ( 1 ) ;
//TODO: HotA
2023-04-12 00:02:01 +02:00
bool isArenaMap = reader - > readBool ( ) ;
if ( isArenaMap )
2023-04-10 19:11:19 +02:00
{
2023-04-12 00:02:01 +02:00
logGlobal - > warn ( " Map '%s': Arena maps are not supported! " , mapName ) ;
2023-04-10 19:11:19 +02:00
}
2023-04-09 00:03:43 +02:00
}
2023-04-05 13:08:41 +02:00
if ( hotaVersion > 1 )
2023-04-03 17:56:49 +02:00
{
2023-04-15 16:20:19 +02:00
[[maybe_unused]] uint8_t unknown = reader - > readUInt32 ( ) ;
2023-04-04 14:40:16 +02:00
assert ( unknown = = 12 ) ;
2023-04-03 17:56:49 +02:00
}
2013-02-04 13:29:59 +03:00
}
2023-04-03 19:51:02 +02:00
else
2013-02-04 13:29:59 +03:00
{
2023-04-03 19:51:02 +02:00
features = MapFormatFeaturesH3M : : find ( mapHeader - > version , 0 ) ;
reader - > setFormatLevel ( mapHeader - > version , 0 ) ;
2013-02-04 13:29:59 +03:00
}
2023-04-12 00:59:55 +02:00
// include basic mod
if ( mapHeader - > version = = EMapFormat : : WOG )
mapHeader - > mods [ " wake-of-gods " ] ;
2013-02-04 13:29:59 +03:00
// Read map name, description, dimensions,...
2023-02-24 16:15:45 +02:00
mapHeader - > areAnyPlayers = reader - > readBool ( ) ;
2023-04-03 14:02:15 +02:00
mapHeader - > height = mapHeader - > width = reader - > readInt32 ( ) ;
2023-02-24 16:15:45 +02:00
mapHeader - > twoLevel = reader - > readBool ( ) ;
2023-02-25 17:44:15 +02:00
mapHeader - > name = readLocalizedString ( " header.name " ) ;
mapHeader - > description = readLocalizedString ( " header.description " ) ;
2023-02-24 16:15:45 +02:00
mapHeader - > difficulty = reader - > readInt8 ( ) ;
2023-04-02 18:56:10 +02:00
if ( features . levelAB )
2023-02-24 16:15:45 +02:00
mapHeader - > levelLimit = reader - > readUInt8 ( ) ;
2013-02-04 13:29:59 +03:00
else
mapHeader - > levelLimit = 0 ;
readPlayerInfo ( ) ;
readVictoryLossConditions ( ) ;
readTeamInfo ( ) ;
readAllowedHeroes ( ) ;
}
void CMapLoaderH3M : : readPlayerInfo ( )
{
for ( int i = 0 ; i < mapHeader - > players . size ( ) ; + + i )
{
2023-04-02 18:56:10 +02:00
auto & playerInfo = mapHeader - > players [ i ] ;
2013-02-04 13:29:59 +03:00
2023-04-02 18:56:10 +02:00
playerInfo . canHumanPlay = reader - > readBool ( ) ;
playerInfo . canComputerPlay = reader - > readBool ( ) ;
2013-02-04 13:29:59 +03:00
2023-04-02 18:56:10 +02:00
// If nobody can play with this player - skip loading of these properties
if ( ( ! ( playerInfo . canHumanPlay | | playerInfo . canComputerPlay ) ) )
2013-02-04 13:29:59 +03:00
{
2023-04-05 13:08:41 +02:00
if ( features . levelROE )
2023-04-02 18:56:10 +02:00
reader - > skipUnused ( 6 ) ;
2023-04-05 13:08:41 +02:00
if ( features . levelAB )
2023-04-02 18:56:10 +02:00
reader - > skipUnused ( 6 ) ;
2023-04-05 13:08:41 +02:00
if ( features . levelSOD )
2023-04-02 18:56:10 +02:00
reader - > skipUnused ( 1 ) ;
2013-02-04 13:29:59 +03:00
continue ;
}
2023-04-02 18:56:10 +02:00
playerInfo . aiTactic = static_cast < EAiTactic : : EAiTactic > ( reader - > readUInt8 ( ) ) ;
2013-02-04 13:29:59 +03:00
2023-04-02 18:56:10 +02:00
if ( features . levelSOD )
reader - > skipUnused ( 1 ) ; //TODO: check meaning?
2013-02-04 13:29:59 +03:00
2023-04-11 16:46:08 +02:00
std : : set < FactionID > allowedFactions ;
2013-02-04 13:29:59 +03:00
2023-04-02 18:56:10 +02:00
reader - > readBitmask ( allowedFactions , features . factionsBytes , features . factionsCount , false ) ;
2013-02-04 13:29:59 +03:00
2023-04-02 18:56:10 +02:00
const bool isFactionRandom = playerInfo . isFactionRandom = reader - > readBool ( ) ;
const bool allFactionsAllowed = isFactionRandom & & allowedFactions . size ( ) = = features . factionsCount ;
2021-11-08 18:49:04 +02:00
if ( ! allFactionsAllowed )
2023-04-02 18:56:10 +02:00
playerInfo . allowedFactions = allowedFactions ;
2013-02-04 13:29:59 +03:00
2023-04-02 18:56:10 +02:00
playerInfo . hasMainTown = reader - > readBool ( ) ;
if ( playerInfo . hasMainTown )
2013-02-04 13:29:59 +03:00
{
2023-04-02 18:56:10 +02:00
if ( features . levelAB )
2013-02-04 13:29:59 +03:00
{
2023-04-02 18:56:10 +02:00
playerInfo . generateHeroAtMainTown = reader - > readBool ( ) ;
reader - > skipUnused ( 1 ) ; //TODO: check meaning?
2013-02-04 13:29:59 +03:00
}
else
{
2023-04-02 18:56:10 +02:00
playerInfo . generateHeroAtMainTown = true ;
2013-02-04 13:29:59 +03:00
}
2023-04-02 18:56:10 +02:00
playerInfo . posOfMainTown = reader - > readInt3 ( ) ;
2013-02-04 13:29:59 +03:00
}
2023-04-02 18:56:10 +02:00
playerInfo . hasRandomHero = reader - > readBool ( ) ;
playerInfo . mainCustomHeroId = reader - > readHero ( ) . getNum ( ) ;
2013-02-04 13:29:59 +03:00
2023-04-02 18:56:10 +02:00
if ( playerInfo . mainCustomHeroId ! = - 1 )
2013-02-04 13:29:59 +03:00
{
2023-04-02 18:56:10 +02:00
playerInfo . mainCustomHeroPortrait = reader - > readHero ( ) . getNum ( ) ;
playerInfo . mainCustomHeroName = readLocalizedString ( TextIdentifier ( " header " , " player " , i , " mainHeroName " ) ) ;
2013-02-04 13:29:59 +03:00
}
2023-04-02 18:56:10 +02:00
if ( features . levelAB )
2013-02-04 13:29:59 +03:00
{
2023-04-02 18:56:10 +02:00
reader - > skipUnused ( 1 ) ; //TODO: check meaning?
2023-04-03 14:02:15 +02:00
uint32_t heroCount = reader - > readUInt32 ( ) ;
2013-02-04 13:29:59 +03:00
for ( int pp = 0 ; pp < heroCount ; + + pp )
{
SHeroName vv ;
2023-02-24 16:15:45 +02:00
vv . heroId = reader - > readUInt8 ( ) ;
2023-04-05 13:08:41 +02:00
vv . heroName = readLocalizedString ( TextIdentifier ( " header " , " heroNames " , vv . heroId ) ) ;
2013-02-04 13:29:59 +03:00
2023-04-02 18:56:10 +02:00
playerInfo . heroesNames . push_back ( vv ) ;
2013-02-04 13:29:59 +03:00
}
}
}
}
2023-04-03 17:56:49 +02:00
enum class EVictoryConditionType : uint8_t
2013-12-29 14:27:38 +03:00
{
2023-04-03 17:56:49 +02:00
ARTIFACT = 0 ,
GATHERTROOP = 1 ,
GATHERRESOURCE = 2 ,
BUILDCITY = 3 ,
BUILDGRAIL = 4 ,
BEATHERO = 5 ,
CAPTURECITY = 6 ,
BEATMONSTER = 7 ,
TAKEDWELLINGS = 8 ,
TAKEMINES = 9 ,
TRANSPORTITEM = 10 ,
HOTA_ELIMINATE_ALL_MONSTERS = 11 ,
HOTA_SURVIVE_FOR_DAYS = 12 ,
WINSTANDARD = 255
} ;
enum class ELossConditionType : uint8_t
2013-12-29 14:27:38 +03:00
{
2023-04-05 13:08:41 +02:00
LOSSCASTLE = 0 ,
LOSSHERO = 1 ,
TIMEEXPIRES = 2 ,
2023-04-03 17:56:49 +02:00
LOSSSTANDARD = 255
} ;
2013-12-29 14:27:38 +03:00
2013-02-04 13:29:59 +03:00
void CMapLoaderH3M : : readVictoryLossConditions ( )
{
2013-12-29 14:27:38 +03:00
mapHeader - > triggeredEvents . clear ( ) ;
2023-04-03 17:56:49 +02:00
auto vicCondition = static_cast < EVictoryConditionType > ( reader - > readUInt8 ( ) ) ;
2013-12-29 14:27:38 +03:00
EventCondition victoryCondition ( EventCondition : : STANDARD_WIN ) ;
EventCondition defeatCondition ( EventCondition : : DAYS_WITHOUT_TOWN ) ;
defeatCondition . value = 7 ;
TriggeredEvent standardVictory ;
standardVictory . effect . type = EventEffect : : VICTORY ;
standardVictory . effect . toOtherMessage = VLC - > generaltexth - > allTexts [ 5 ] ;
standardVictory . identifier = " standardVictory " ;
2022-11-15 02:20:55 +02:00
standardVictory . description . clear ( ) ; // TODO: display in quest window
2013-12-29 14:27:38 +03:00
standardVictory . onFulfill = VLC - > generaltexth - > allTexts [ 659 ] ;
standardVictory . trigger = EventExpression ( victoryCondition ) ;
TriggeredEvent standardDefeat ;
standardDefeat . effect . type = EventEffect : : DEFEAT ;
standardDefeat . effect . toOtherMessage = VLC - > generaltexth - > allTexts [ 8 ] ;
standardDefeat . identifier = " standardDefeat " ;
2022-11-15 02:20:55 +02:00
standardDefeat . description . clear ( ) ; // TODO: display in quest window
2013-12-29 14:27:38 +03:00
standardDefeat . onFulfill = VLC - > generaltexth - > allTexts [ 7 ] ;
standardDefeat . trigger = EventExpression ( defeatCondition ) ;
2013-02-04 13:29:59 +03:00
// Specific victory conditions
2013-12-29 14:27:38 +03:00
if ( vicCondition = = EVictoryConditionType : : WINSTANDARD )
{
// create normal condition
mapHeader - > triggeredEvents . push_back ( standardVictory ) ;
mapHeader - > victoryIconIndex = 11 ;
mapHeader - > victoryMessage = VLC - > generaltexth - > victoryConditions [ 0 ] ;
}
else
2013-02-04 13:29:59 +03:00
{
2013-12-29 14:27:38 +03:00
TriggeredEvent specialVictory ;
specialVictory . effect . type = EventEffect : : VICTORY ;
specialVictory . identifier = " specialVictory " ;
2022-11-15 02:20:55 +02:00
specialVictory . description . clear ( ) ; // TODO: display in quest window
2013-12-29 14:27:38 +03:00
2023-02-11 18:30:06 +02:00
mapHeader - > victoryIconIndex = static_cast < ui16 > ( vicCondition ) ;
mapHeader - > victoryMessage = VLC - > generaltexth - > victoryConditions [ static_cast < size_t > ( vicCondition ) + 1 ] ;
2013-12-29 14:27:38 +03:00
2023-02-24 16:15:45 +02:00
bool allowNormalVictory = reader - > readBool ( ) ;
bool appliesToAI = reader - > readBool ( ) ;
2013-02-04 13:29:59 +03:00
2023-04-05 13:08:41 +02:00
if ( allowNormalVictory )
2014-02-09 15:10:02 +03:00
{
2023-04-05 13:08:41 +02:00
size_t playersOnMap = boost : : range : : count_if (
mapHeader - > players ,
[ ] ( const PlayerInfo & info )
{
return info . canAnyonePlay ( ) ;
}
) ;
2014-02-09 15:10:02 +03:00
2023-04-05 13:08:41 +02:00
if ( playersOnMap = = 1 )
2014-02-09 15:10:02 +03:00
{
2023-04-11 16:46:08 +02:00
logGlobal - > warn ( " Map %s: Only one player exists, but normal victory allowed! " , mapName ) ;
2014-02-09 15:10:02 +03:00
allowNormalVictory = false ; // makes sense? Not much. Works as H3? Yes!
}
}
2013-12-29 14:27:38 +03:00
switch ( vicCondition )
2013-02-04 13:29:59 +03:00
{
2023-04-05 13:08:41 +02:00
case EVictoryConditionType : : ARTIFACT :
2013-02-04 13:29:59 +03:00
{
2013-12-29 14:27:38 +03:00
EventCondition cond ( EventCondition : : HAVE_ARTIFACT ) ;
2023-04-02 18:56:10 +02:00
cond . objectType = reader - > readArtifact ( ) ;
2013-12-29 14:27:38 +03:00
specialVictory . effect . toOtherMessage = VLC - > generaltexth - > allTexts [ 281 ] ;
specialVictory . onFulfill = VLC - > generaltexth - > allTexts [ 280 ] ;
specialVictory . trigger = EventExpression ( cond ) ;
2013-02-04 13:29:59 +03:00
break ;
}
2023-04-05 13:08:41 +02:00
case EVictoryConditionType : : GATHERTROOP :
2013-02-04 13:29:59 +03:00
{
2013-12-29 14:27:38 +03:00
EventCondition cond ( EventCondition : : HAVE_CREATURES ) ;
2023-04-02 18:56:10 +02:00
cond . objectType = reader - > readCreature ( ) ;
2023-04-03 14:02:15 +02:00
cond . value = reader - > readInt32 ( ) ;
2013-12-29 14:27:38 +03:00
specialVictory . effect . toOtherMessage = VLC - > generaltexth - > allTexts [ 277 ] ;
specialVictory . onFulfill = VLC - > generaltexth - > allTexts [ 276 ] ;
specialVictory . trigger = EventExpression ( cond ) ;
2013-02-04 13:29:59 +03:00
break ;
}
2023-04-05 13:08:41 +02:00
case EVictoryConditionType : : GATHERRESOURCE :
2013-02-04 13:29:59 +03:00
{
2013-12-29 14:27:38 +03:00
EventCondition cond ( EventCondition : : HAVE_RESOURCES ) ;
2023-02-24 16:15:45 +02:00
cond . objectType = reader - > readUInt8 ( ) ;
2023-04-03 14:02:15 +02:00
cond . value = reader - > readInt32 ( ) ;
2013-12-29 14:27:38 +03:00
specialVictory . effect . toOtherMessage = VLC - > generaltexth - > allTexts [ 279 ] ;
specialVictory . onFulfill = VLC - > generaltexth - > allTexts [ 278 ] ;
specialVictory . trigger = EventExpression ( cond ) ;
2013-02-04 13:29:59 +03:00
break ;
}
2023-04-05 13:08:41 +02:00
case EVictoryConditionType : : BUILDCITY :
2013-02-04 13:29:59 +03:00
{
2013-12-29 14:27:38 +03:00
EventExpression : : OperatorAll oper ;
EventCondition cond ( EventCondition : : HAVE_BUILDING ) ;
2023-04-02 18:56:10 +02:00
cond . position = reader - > readInt3 ( ) ;
2023-02-24 16:15:45 +02:00
cond . objectType = BuildingID : : TOWN_HALL + reader - > readUInt8 ( ) ;
2023-02-11 18:30:06 +02:00
oper . expressions . emplace_back ( cond ) ;
2023-02-24 16:15:45 +02:00
cond . objectType = BuildingID : : FORT + reader - > readUInt8 ( ) ;
2023-02-11 18:30:06 +02:00
oper . expressions . emplace_back ( cond ) ;
2013-12-29 14:27:38 +03:00
specialVictory . effect . toOtherMessage = VLC - > generaltexth - > allTexts [ 283 ] ;
specialVictory . onFulfill = VLC - > generaltexth - > allTexts [ 282 ] ;
specialVictory . trigger = EventExpression ( oper ) ;
2013-02-04 13:29:59 +03:00
break ;
}
2023-04-05 13:08:41 +02:00
case EVictoryConditionType : : BUILDGRAIL :
2013-02-04 13:29:59 +03:00
{
2013-12-29 14:27:38 +03:00
EventCondition cond ( EventCondition : : HAVE_BUILDING ) ;
cond . objectType = BuildingID : : GRAIL ;
2023-04-02 18:56:10 +02:00
cond . position = reader - > readInt3 ( ) ;
2013-12-29 14:27:38 +03:00
if ( cond . position . z > 2 )
2023-04-05 13:08:41 +02:00
cond . position = int3 ( - 1 , - 1 , - 1 ) ;
2013-12-29 14:27:38 +03:00
specialVictory . effect . toOtherMessage = VLC - > generaltexth - > allTexts [ 285 ] ;
specialVictory . onFulfill = VLC - > generaltexth - > allTexts [ 284 ] ;
specialVictory . trigger = EventExpression ( cond ) ;
2013-02-04 13:29:59 +03:00
break ;
}
2023-04-05 13:08:41 +02:00
case EVictoryConditionType : : BEATHERO :
2013-12-29 14:27:38 +03:00
{
EventCondition cond ( EventCondition : : DESTROY ) ;
cond . objectType = Obj : : HERO ;
2023-04-02 18:56:10 +02:00
cond . position = reader - > readInt3 ( ) ;
2013-12-29 14:27:38 +03:00
specialVictory . effect . toOtherMessage = VLC - > generaltexth - > allTexts [ 253 ] ;
specialVictory . onFulfill = VLC - > generaltexth - > allTexts [ 252 ] ;
specialVictory . trigger = EventExpression ( cond ) ;
break ;
}
2023-04-05 13:08:41 +02:00
case EVictoryConditionType : : CAPTURECITY :
2013-12-29 14:27:38 +03:00
{
EventCondition cond ( EventCondition : : CONTROL ) ;
cond . objectType = Obj : : TOWN ;
2023-04-02 18:56:10 +02:00
cond . position = reader - > readInt3 ( ) ;
2013-12-29 14:27:38 +03:00
specialVictory . effect . toOtherMessage = VLC - > generaltexth - > allTexts [ 250 ] ;
specialVictory . onFulfill = VLC - > generaltexth - > allTexts [ 249 ] ;
specialVictory . trigger = EventExpression ( cond ) ;
break ;
}
2023-04-05 13:08:41 +02:00
case EVictoryConditionType : : BEATMONSTER :
2013-02-04 13:29:59 +03:00
{
2013-12-29 14:27:38 +03:00
EventCondition cond ( EventCondition : : DESTROY ) ;
cond . objectType = Obj : : MONSTER ;
2023-04-02 18:56:10 +02:00
cond . position = reader - > readInt3 ( ) ;
2013-12-29 14:27:38 +03:00
specialVictory . effect . toOtherMessage = VLC - > generaltexth - > allTexts [ 287 ] ;
specialVictory . onFulfill = VLC - > generaltexth - > allTexts [ 286 ] ;
specialVictory . trigger = EventExpression ( cond ) ;
2013-02-04 13:29:59 +03:00
break ;
}
2023-04-05 13:08:41 +02:00
case EVictoryConditionType : : TAKEDWELLINGS :
2013-12-29 14:27:38 +03:00
{
2014-02-09 22:47:23 +03:00
EventExpression : : OperatorAll oper ;
2023-02-11 18:30:06 +02:00
oper . expressions . emplace_back ( EventCondition ( EventCondition : : CONTROL , 0 , Obj : : CREATURE_GENERATOR1 ) ) ;
oper . expressions . emplace_back ( EventCondition ( EventCondition : : CONTROL , 0 , Obj : : CREATURE_GENERATOR4 ) ) ;
2013-12-29 14:27:38 +03:00
specialVictory . effect . toOtherMessage = VLC - > generaltexth - > allTexts [ 289 ] ;
specialVictory . onFulfill = VLC - > generaltexth - > allTexts [ 288 ] ;
2014-02-09 22:47:23 +03:00
specialVictory . trigger = EventExpression ( oper ) ;
2013-12-29 14:27:38 +03:00
break ;
}
2023-04-05 13:08:41 +02:00
case EVictoryConditionType : : TAKEMINES :
2013-02-04 13:29:59 +03:00
{
2013-12-29 14:27:38 +03:00
EventCondition cond ( EventCondition : : CONTROL ) ;
cond . objectType = Obj : : MINE ;
specialVictory . effect . toOtherMessage = VLC - > generaltexth - > allTexts [ 291 ] ;
specialVictory . onFulfill = VLC - > generaltexth - > allTexts [ 290 ] ;
specialVictory . trigger = EventExpression ( cond ) ;
2013-02-04 13:29:59 +03:00
break ;
}
2023-04-05 13:08:41 +02:00
case EVictoryConditionType : : TRANSPORTITEM :
2013-02-04 13:29:59 +03:00
{
2013-12-29 14:27:38 +03:00
EventCondition cond ( EventCondition : : TRANSPORT ) ;
2023-02-24 16:15:45 +02:00
cond . objectType = reader - > readUInt8 ( ) ;
2023-04-02 18:56:10 +02:00
cond . position = reader - > readInt3 ( ) ;
2013-12-29 14:27:38 +03:00
specialVictory . effect . toOtherMessage = VLC - > generaltexth - > allTexts [ 293 ] ;
specialVictory . onFulfill = VLC - > generaltexth - > allTexts [ 292 ] ;
specialVictory . trigger = EventExpression ( cond ) ;
2013-02-04 13:29:59 +03:00
break ;
}
2023-04-05 13:08:41 +02:00
case EVictoryConditionType : : HOTA_ELIMINATE_ALL_MONSTERS :
2023-04-03 17:56:49 +02:00
//TODO: HOTA
2023-04-05 00:08:24 +02:00
logGlobal - > warn ( " Map '%s': Victory condition 'Eliminate all monsters' is not implemented! " , mapName ) ;
2023-04-03 17:56:49 +02:00
break ;
2023-04-05 13:08:41 +02:00
case EVictoryConditionType : : HOTA_SURVIVE_FOR_DAYS :
2023-04-04 14:40:16 +02:00
{
2023-04-03 17:56:49 +02:00
//TODO: HOTA
2023-04-04 14:40:16 +02:00
uint32_t daysToSurvive = reader - > readUInt32 ( ) ; // Number of days
2023-04-05 12:40:13 +02:00
logGlobal - > warn ( " Map '%s': Victory condition 'Survive for %d days' is not implemented! " , mapName , daysToSurvive ) ;
2023-04-03 17:56:49 +02:00
break ;
2023-04-04 14:40:16 +02:00
}
2023-04-05 13:08:41 +02:00
default :
2023-04-04 14:40:16 +02:00
assert ( 0 ) ;
2013-02-04 13:29:59 +03:00
}
2014-01-30 21:56:31 +03:00
2013-12-29 14:27:38 +03:00
// if condition is human-only turn it into following construction: AllOf(human, condition)
2023-04-05 13:08:41 +02:00
if ( ! appliesToAI )
2013-12-29 14:27:38 +03:00
{
EventExpression : : OperatorAll oper ;
EventCondition notAI ( EventCondition : : IS_HUMAN ) ;
notAI . value = 1 ;
2023-02-11 18:30:06 +02:00
oper . expressions . emplace_back ( notAI ) ;
2013-12-29 14:27:38 +03:00
oper . expressions . push_back ( specialVictory . trigger . get ( ) ) ;
specialVictory . trigger = EventExpression ( oper ) ;
}
// if normal victory allowed - add one more quest
2023-04-05 13:08:41 +02:00
if ( allowNormalVictory )
2013-12-29 14:27:38 +03:00
{
mapHeader - > victoryMessage + = " / " ;
mapHeader - > victoryMessage + = VLC - > generaltexth - > victoryConditions [ 0 ] ;
mapHeader - > triggeredEvents . push_back ( standardVictory ) ;
}
mapHeader - > triggeredEvents . push_back ( specialVictory ) ;
2013-02-04 13:29:59 +03:00
}
// Read loss conditions
2023-04-03 17:56:49 +02:00
auto lossCond = static_cast < ELossConditionType > ( reader - > readUInt8 ( ) ) ;
2023-04-05 13:08:41 +02:00
if ( lossCond = = ELossConditionType : : LOSSSTANDARD )
2013-02-04 13:29:59 +03:00
{
2013-12-29 14:27:38 +03:00
mapHeader - > defeatIconIndex = 3 ;
mapHeader - > defeatMessage = VLC - > generaltexth - > lossCondtions [ 0 ] ;
}
else
{
TriggeredEvent specialDefeat ;
specialDefeat . effect . type = EventEffect : : DEFEAT ;
specialDefeat . effect . toOtherMessage = VLC - > generaltexth - > allTexts [ 5 ] ;
specialDefeat . identifier = " specialDefeat " ;
2022-11-15 02:20:55 +02:00
specialDefeat . description . clear ( ) ; // TODO: display in quest window
2013-12-29 14:27:38 +03:00
2023-02-11 18:30:06 +02:00
mapHeader - > defeatIconIndex = static_cast < ui16 > ( lossCond ) ;
mapHeader - > defeatMessage = VLC - > generaltexth - > lossCondtions [ static_cast < size_t > ( lossCond ) + 1 ] ;
2013-12-29 14:27:38 +03:00
switch ( lossCond )
2013-02-04 13:29:59 +03:00
{
2023-04-05 13:08:41 +02:00
case ELossConditionType : : LOSSCASTLE :
2013-12-29 14:27:38 +03:00
{
EventExpression : : OperatorNone noneOf ;
EventCondition cond ( EventCondition : : CONTROL ) ;
cond . objectType = Obj : : TOWN ;
2023-04-02 18:56:10 +02:00
cond . position = reader - > readInt3 ( ) ;
2013-12-29 14:27:38 +03:00
2023-02-11 18:30:06 +02:00
noneOf . expressions . emplace_back ( cond ) ;
2013-12-29 14:27:38 +03:00
specialDefeat . onFulfill = VLC - > generaltexth - > allTexts [ 251 ] ;
specialDefeat . trigger = EventExpression ( noneOf ) ;
break ;
}
2023-04-05 13:08:41 +02:00
case ELossConditionType : : LOSSHERO :
2013-12-29 14:27:38 +03:00
{
EventExpression : : OperatorNone noneOf ;
EventCondition cond ( EventCondition : : CONTROL ) ;
cond . objectType = Obj : : HERO ;
2023-04-02 18:56:10 +02:00
cond . position = reader - > readInt3 ( ) ;
2013-12-29 14:27:38 +03:00
2023-02-11 18:30:06 +02:00
noneOf . expressions . emplace_back ( cond ) ;
2013-12-29 14:27:38 +03:00
specialDefeat . onFulfill = VLC - > generaltexth - > allTexts [ 253 ] ;
specialDefeat . trigger = EventExpression ( noneOf ) ;
break ;
}
2023-04-05 13:08:41 +02:00
case ELossConditionType : : TIMEEXPIRES :
2013-12-29 14:27:38 +03:00
{
EventCondition cond ( EventCondition : : DAYS_PASSED ) ;
2023-02-24 16:15:45 +02:00
cond . value = reader - > readUInt16 ( ) ;
2013-12-29 14:27:38 +03:00
specialDefeat . onFulfill = VLC - > generaltexth - > allTexts [ 254 ] ;
specialDefeat . trigger = EventExpression ( cond ) ;
break ;
}
2013-02-04 13:29:59 +03:00
}
2013-12-29 14:27:38 +03:00
// turn simple loss condition into complete one that can be evaluated later:
// - any of :
// - days without town: 7
// - all of:
// - is human
// - (expression)
EventExpression : : OperatorAll allOf ;
EventCondition isHuman ( EventCondition : : IS_HUMAN ) ;
isHuman . value = 1 ;
2023-02-11 18:30:06 +02:00
allOf . expressions . emplace_back ( isHuman ) ;
2013-12-29 14:27:38 +03:00
allOf . expressions . push_back ( specialDefeat . trigger . get ( ) ) ;
specialDefeat . trigger = EventExpression ( allOf ) ;
mapHeader - > triggeredEvents . push_back ( specialDefeat ) ;
2013-02-04 13:29:59 +03:00
}
2013-12-29 14:27:38 +03:00
mapHeader - > triggeredEvents . push_back ( standardDefeat ) ;
2013-02-04 13:29:59 +03:00
}
void CMapLoaderH3M : : readTeamInfo ( )
{
2023-02-24 16:15:45 +02:00
mapHeader - > howManyTeams = reader - > readUInt8 ( ) ;
2013-02-04 13:29:59 +03:00
if ( mapHeader - > howManyTeams > 0 )
{
// Teams
2013-03-03 20:06:03 +03:00
for ( int i = 0 ; i < PlayerColor : : PLAYER_LIMIT_I ; + + i )
2023-02-24 16:15:45 +02:00
mapHeader - > players [ i ] . team = TeamID ( reader - > readUInt8 ( ) ) ;
2013-02-04 13:29:59 +03:00
}
else
{
// No alliances
2013-03-03 20:06:03 +03:00
for ( int i = 0 ; i < PlayerColor : : PLAYER_LIMIT_I ; i + + )
2013-02-04 13:29:59 +03:00
if ( mapHeader - > players [ i ] . canComputerPlay | | mapHeader - > players [ i ] . canHumanPlay )
2013-03-03 20:06:03 +03:00
mapHeader - > players [ i ] . team = TeamID ( mapHeader - > howManyTeams + + ) ;
2013-02-04 13:29:59 +03:00
}
}
void CMapLoaderH3M : : readAllowedHeroes ( )
{
2023-04-03 14:02:15 +02:00
mapHeader - > allowedHeroes = VLC - > heroh - > getDefaultAllowed ( ) ;
2013-02-04 13:29:59 +03:00
2023-04-03 17:56:49 +02:00
uint32_t heroesCount = features . heroesCount ;
2023-04-04 12:45:31 +02:00
uint32_t heroesBytes = features . heroesBytes ;
2013-02-05 00:58:42 +03:00
2023-04-09 00:03:43 +02:00
if ( features . levelHOTA0 )
2013-02-04 13:29:59 +03:00
{
2023-04-03 17:56:49 +02:00
heroesCount = reader - > readUInt32 ( ) ;
2023-04-04 12:45:31 +02:00
heroesBytes = ( heroesCount + 7 ) / 8 ;
}
2016-02-21 21:12:58 +02:00
2023-04-03 17:56:49 +02:00
assert ( heroesCount < = features . heroesCount ) ;
2016-02-21 21:12:58 +02:00
2023-04-04 12:45:31 +02:00
reader - > readBitmask ( mapHeader - > allowedHeroes , heroesBytes , heroesCount , false ) ;
2013-02-04 13:29:59 +03:00
2023-04-02 18:56:10 +02:00
//TODO: unknown value. Check meaning? Only present in campaign maps.
if ( features . levelAB )
2013-02-04 13:29:59 +03:00
{
2023-04-03 14:02:15 +02:00
uint32_t placeholdersQty = reader - > readUInt32 ( ) ;
2023-04-02 18:56:10 +02:00
reader - > skipUnused ( placeholdersQty * 1 ) ;
2013-02-04 13:29:59 +03:00
}
}
void CMapLoaderH3M : : readDisposedHeroes ( )
{
// Reading disposed heroes (20 bytes)
2023-04-02 18:56:10 +02:00
if ( features . levelSOD )
2013-02-04 13:29:59 +03:00
{
2023-02-24 16:15:45 +02:00
ui8 disp = reader - > readUInt8 ( ) ;
2013-02-04 13:29:59 +03:00
map - > disposedHeroes . resize ( disp ) ;
for ( int g = 0 ; g < disp ; + + g )
{
2023-04-02 18:56:10 +02:00
map - > disposedHeroes [ g ] . heroId = reader - > readHero ( ) . getNum ( ) ;
map - > disposedHeroes [ g ] . portrait = reader - > readHero ( ) . getNum ( ) ;
2023-02-25 17:44:15 +02:00
map - > disposedHeroes [ g ] . name = readLocalizedString ( TextIdentifier ( " header " , " heroes " , map - > disposedHeroes [ g ] . heroId ) ) ;
2023-02-24 16:15:45 +02:00
map - > disposedHeroes [ g ] . players = reader - > readUInt8 ( ) ;
2013-02-04 13:29:59 +03:00
}
}
2023-04-03 19:51:02 +02:00
}
2013-02-04 13:29:59 +03:00
2023-04-03 19:51:02 +02:00
void CMapLoaderH3M : : readMapOptions ( )
{
2013-02-04 13:29:59 +03:00
//omitting NULLS
2023-04-02 18:56:10 +02:00
reader - > skipZero ( 31 ) ;
2023-04-03 19:51:02 +02:00
2023-04-09 00:03:43 +02:00
if ( features . levelHOTA0 )
2023-04-03 19:51:02 +02:00
{
2023-04-04 14:40:16 +02:00
//TODO: HotA
bool allowSpecialMonths = reader - > readBool ( ) ;
2023-04-05 13:08:41 +02:00
if ( ! allowSpecialMonths )
2023-04-04 14:40:16 +02:00
logGlobal - > warn ( " Map '%s': Option 'allow special months' is not implemented! " , mapName ) ;
reader - > skipZero ( 3 ) ;
2023-04-09 00:03:43 +02:00
}
if ( features . levelHOTA1 )
{
2023-04-15 16:20:19 +02:00
// Unknown, may be another "sized bitmap", e.g
// 4 bytes - size of bitmap (16)
// 2 bytes - bitmap data (16 bits / 2 bytes)
[[maybe_unused]] uint8_t unknownConstant = reader - > readUInt8 ( ) ;
2023-04-04 14:40:16 +02:00
assert ( unknownConstant = = 16 ) ;
reader - > skipZero ( 5 ) ;
2023-04-03 19:51:02 +02:00
}
2023-04-05 13:08:41 +02:00
if ( features . levelHOTA3 )
2023-04-03 19:51:02 +02:00
{
2023-04-04 14:40:16 +02:00
//TODO: HotA
int32_t roundLimit = reader - > readInt32 ( ) ;
2023-04-05 13:08:41 +02:00
if ( roundLimit ! = - 1 )
2023-04-04 14:40:16 +02:00
logGlobal - > warn ( " Map '%s': roundLimit of %d is not implemented! " , mapName , roundLimit ) ;
2023-04-03 19:51:02 +02:00
}
2013-02-04 13:29:59 +03:00
}
void CMapLoaderH3M : : readAllowedArtifacts ( )
{
2023-04-03 14:02:15 +02:00
map - > allowedArtifact = VLC - > arth - > getDefaultAllowed ( ) ;
2013-02-05 00:58:42 +03:00
2023-04-04 12:45:31 +02:00
uint32_t artifactsCount = features . artifactsCount ;
uint32_t artifactsBytes = features . artifactsBytes ;
2013-02-04 13:29:59 +03:00
2023-04-09 00:03:43 +02:00
if ( features . levelHOTA0 )
2023-04-04 12:45:31 +02:00
{
artifactsCount = reader - > readUInt32 ( ) ;
artifactsBytes = ( artifactsCount + 7 ) / 8 ;
2013-02-04 13:29:59 +03:00
}
2023-04-04 12:45:31 +02:00
assert ( artifactsCount < = features . artifactsCount ) ;
2023-04-02 18:56:10 +02:00
if ( features . levelAB )
2023-04-04 12:45:31 +02:00
reader - > readBitmask ( map - > allowedArtifact , artifactsBytes , artifactsCount , true ) ;
2013-02-04 13:29:59 +03:00
// ban combo artifacts
2023-04-05 13:08:41 +02:00
if ( ! features . levelSOD )
2013-02-04 13:29:59 +03:00
{
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
for ( CArtifact * artifact : VLC - > arth - > objects )
2023-04-05 13:08:41 +02:00
if ( artifact - > constituents )
2023-01-02 15:58:56 +02:00
map - > allowedArtifact [ artifact - > getId ( ) ] = false ;
2023-04-02 18:56:10 +02:00
}
2023-04-05 13:08:41 +02:00
if ( ! features . levelAB )
2023-04-02 18:56:10 +02:00
{
map - > allowedArtifact [ ArtifactID : : VIAL_OF_DRAGON_BLOOD ] = false ;
map - > allowedArtifact [ ArtifactID : : ARMAGEDDONS_BLADE ] = false ;
2013-02-04 13:29:59 +03:00
}
// Messy, but needed
2023-04-05 13:08:41 +02:00
for ( TriggeredEvent & event : map - > triggeredEvents )
2013-02-04 13:29:59 +03:00
{
2014-08-07 19:40:22 +03:00
auto patcher = [ & ] ( EventCondition cond ) - > EventExpression : : Variant
2013-12-29 14:27:38 +03:00
{
2023-04-05 13:08:41 +02:00
if ( cond . condition = = EventCondition : : HAVE_ARTIFACT | | cond . condition = = EventCondition : : TRANSPORT )
2013-12-29 14:27:38 +03:00
{
map - > allowedArtifact [ cond . objectType ] = false ;
}
2014-08-07 19:40:22 +03:00
return cond ;
2013-12-29 14:27:38 +03:00
} ;
2014-08-07 19:40:22 +03:00
event . trigger = event . trigger . morph ( patcher ) ;
2013-02-04 13:29:59 +03:00
}
}
void CMapLoaderH3M : : readAllowedSpellsAbilities ( )
{
2023-04-03 14:02:15 +02:00
map - > allowedSpell = VLC - > spellh - > getDefaultAllowed ( ) ;
map - > allowedAbilities = VLC - > skillh - > getDefaultAllowed ( ) ;
2013-02-04 13:29:59 +03:00
2023-04-02 18:56:10 +02:00
if ( features . levelSOD )
2013-02-04 13:29:59 +03:00
{
2023-04-03 14:02:15 +02:00
reader - > readBitmask ( map - > allowedSpell , features . spellsBytes , features . spellsCount , true ) ;
reader - > readBitmask ( map - > allowedAbilities , features . skillsBytes , features . skillsCount , true ) ;
2013-02-04 13:29:59 +03:00
}
}
void CMapLoaderH3M : : readRumors ( )
{
2023-04-03 19:51:02 +02:00
uint32_t rumorsCount = reader - > readUInt32 ( ) ;
2023-04-05 13:08:41 +02:00
assert ( rumorsCount < 1000 ) ; // sanity check
2013-02-04 13:29:59 +03:00
2023-04-03 19:51:02 +02:00
for ( int it = 0 ; it < rumorsCount ; it + + )
2013-02-04 13:29:59 +03:00
{
Rumor ourRumor ;
2023-02-25 18:11:17 +02:00
ourRumor . name = readBasicString ( ) ;
2023-02-25 17:44:15 +02:00
ourRumor . text = readLocalizedString ( TextIdentifier ( " header " , " rumor " , it , " text " ) ) ;
2013-02-04 13:29:59 +03:00
map - > rumors . push_back ( ourRumor ) ;
}
}
void CMapLoaderH3M : : readPredefinedHeroes ( )
{
2023-04-05 13:08:41 +02:00
if ( ! features . levelSOD )
2023-04-02 18:56:10 +02:00
return ;
2023-04-03 19:51:02 +02:00
uint32_t heroesCount = features . heroesCount ;
2023-04-09 00:03:43 +02:00
if ( features . levelHOTA0 )
2023-04-03 19:51:02 +02:00
heroesCount = reader - > readUInt32 ( ) ;
assert ( heroesCount < = features . heroesCount ) ;
2023-04-05 13:08:41 +02:00
for ( int heroID = 0 ; heroID < heroesCount ; heroID + + )
2013-02-04 13:29:59 +03:00
{
2023-04-05 13:08:41 +02:00
bool custom = reader - > readBool ( ) ;
2023-04-02 18:56:10 +02:00
if ( ! custom )
continue ;
2013-02-04 13:29:59 +03:00
2023-04-02 18:56:10 +02:00
auto * hero = new CGHeroInstance ( ) ;
hero - > ID = Obj : : HERO ;
2023-04-05 13:08:41 +02:00
hero - > subID = heroID ;
2013-02-04 13:29:59 +03:00
2023-04-02 18:56:10 +02:00
bool hasExp = reader - > readBool ( ) ;
if ( hasExp )
{
hero - > exp = reader - > readUInt32 ( ) ;
}
else
{
hero - > exp = 0 ;
}
2013-02-04 13:29:59 +03:00
2023-04-02 18:56:10 +02:00
bool hasSecSkills = reader - > readBool ( ) ;
if ( hasSecSkills )
{
2023-04-03 14:02:15 +02:00
uint32_t howMany = reader - > readUInt32 ( ) ;
2023-04-02 18:56:10 +02:00
hero - > secSkills . resize ( howMany ) ;
for ( int yy = 0 ; yy < howMany ; + + yy )
{
hero - > secSkills [ yy ] . first = reader - > readSkill ( ) ;
hero - > secSkills [ yy ] . second = reader - > readUInt8 ( ) ;
}
}
2013-02-04 13:29:59 +03:00
2023-04-02 18:56:10 +02:00
loadArtifactsOfHero ( hero ) ;
2013-02-04 13:29:59 +03:00
2023-04-02 18:56:10 +02:00
bool hasCustomBio = reader - > readBool ( ) ;
if ( hasCustomBio )
2023-04-05 13:08:41 +02:00
hero - > biographyCustom = readLocalizedString ( TextIdentifier ( " heroes " , heroID , " biography " ) ) ;
2013-02-04 13:29:59 +03:00
2023-04-02 18:56:10 +02:00
// 0xFF is default, 00 male, 01 female
hero - > gender = static_cast < EHeroGender > ( reader - > readUInt8 ( ) ) ;
assert ( hero - > gender = = EHeroGender : : MALE | | hero - > gender = = EHeroGender : : FEMALE | | hero - > gender = = EHeroGender : : DEFAULT ) ;
2013-02-04 13:29:59 +03:00
2023-04-02 18:56:10 +02:00
bool hasCustomSpells = reader - > readBool ( ) ;
if ( hasCustomSpells )
readSpells ( hero - > spells ) ;
2013-02-04 13:29:59 +03:00
2023-04-02 18:56:10 +02:00
bool hasCustomPrimSkills = reader - > readBool ( ) ;
if ( hasCustomPrimSkills )
{
2023-04-05 13:08:41 +02:00
for ( int skillID = 0 ; skillID < GameConstants : : PRIMARY_SKILLS ; skillID + + )
2023-04-02 18:56:10 +02:00
{
2023-04-05 13:08:41 +02:00
hero - > pushPrimSkill ( static_cast < PrimarySkill : : PrimarySkill > ( skillID ) , reader - > readUInt8 ( ) ) ;
2013-02-04 13:29:59 +03:00
}
}
2023-04-02 18:56:10 +02:00
map - > predefinedHeroes . emplace_back ( hero ) ;
2013-02-04 13:29:59 +03:00
}
}
void CMapLoaderH3M : : loadArtifactsOfHero ( CGHeroInstance * hero )
{
2023-04-02 18:56:10 +02:00
bool hasArtSet = reader - > readBool ( ) ;
2013-02-04 13:29:59 +03:00
// True if artifact set is not default (hero has some artifacts)
2023-04-02 18:56:10 +02:00
if ( ! hasArtSet )
return ;
2023-02-11 18:30:06 +02:00
if ( ! hero - > artifactsWorn . empty ( ) | | ! hero - > artifactsInBackpack . empty ( ) )
{
2023-04-05 13:08:41 +02:00
logGlobal - > warn ( " Hero %s at %s has set artifacts twice (in map properties and on adventure map instance). Using the latter set... " , hero - > getNameTranslated ( ) , hero - > pos . toString ( ) ) ;
2023-02-11 18:30:06 +02:00
hero - > artifactsInBackpack . clear ( ) ;
while ( ! hero - > artifactsWorn . empty ( ) )
2023-04-05 13:08:41 +02:00
hero - > eraseArtSlot ( hero - > artifactsWorn . begin ( ) - > first ) ;
2023-02-11 18:30:06 +02:00
}
2013-09-03 16:18:09 +03:00
2023-04-05 13:08:41 +02:00
for ( int i = 0 ; i < features . artifactSlotsCount ; i + + )
loadArtifactToSlot ( hero , i ) ;
2013-02-04 13:29:59 +03:00
2023-04-02 18:56:10 +02:00
// bag artifacts
// number of artifacts in hero's bag
int amount = reader - > readUInt16 ( ) ;
2023-04-05 13:08:41 +02:00
for ( int i = 0 ; i < amount ; + + i )
2023-04-02 18:56:10 +02:00
{
loadArtifactToSlot ( hero , GameConstants : : BACKPACK_START + static_cast < int > ( hero - > artifactsInBackpack . size ( ) ) ) ;
2013-02-04 13:29:59 +03:00
}
}
bool CMapLoaderH3M : : loadArtifactToSlot ( CGHeroInstance * hero , int slot )
{
2023-04-05 13:08:41 +02:00
ArtifactID artifactID = reader - > readArtifact ( ) ;
2013-02-04 13:29:59 +03:00
2023-04-05 13:08:41 +02:00
if ( artifactID = = ArtifactID : : NONE )
2023-04-02 18:56:10 +02:00
return false ;
2013-02-04 13:29:59 +03:00
2023-04-05 13:08:41 +02:00
const Artifact * art = artifactID . toArtifact ( VLC - > artifacts ( ) ) ;
2023-04-02 18:56:10 +02:00
2023-04-05 13:08:41 +02:00
if ( ! art )
2013-02-04 13:29:59 +03:00
{
2023-04-11 16:46:08 +02:00
logGlobal - > warn ( " Map '%s': Invalid artifact in hero's backpack, ignoring... " , mapName ) ;
2023-04-02 18:56:10 +02:00
return false ;
2013-02-04 13:29:59 +03:00
}
2023-04-02 18:56:10 +02:00
if ( art - > isBig ( ) & & slot > = GameConstants : : BACKPACK_START )
2013-02-04 13:29:59 +03:00
{
2023-04-11 16:46:08 +02:00
logGlobal - > warn ( " Map '%s': A big artifact (war machine) in hero's backpack, ignoring... " , mapName ) ;
2023-04-02 18:56:10 +02:00
return false ;
2013-02-04 13:29:59 +03:00
}
2023-04-02 18:56:10 +02:00
// H3 bug workaround - Enemy hero on 3rd scenario of Good1.h3c campaign ("Long Live The Queen")
// He has Shackles of War (normally - MISC slot artifact) in LEFT_HAND slot set in editor
// Artifact seems to be missing in game, so skip artifacts that don't fit target slot
2023-04-05 13:08:41 +02:00
auto * artifact = CArtifactInstance : : createArtifact ( map , artifactID ) ;
2023-04-02 18:56:10 +02:00
auto artifactPos = ArtifactPosition ( slot ) ;
2023-04-05 13:08:41 +02:00
if ( artifact - > canBePutAt ( ArtifactLocation ( hero , artifactPos ) ) )
2013-02-04 13:29:59 +03:00
{
2023-04-02 18:56:10 +02:00
hero - > putArtifact ( artifactPos , artifact ) ;
}
else
{
2023-04-11 16:46:08 +02:00
logGlobal - > warn ( " Map '%s': Artifact '%s' can't be put at the slot %d " , mapName , artifact - > artType - > getNameTranslated ( ) , slot ) ;
2023-04-02 18:56:10 +02:00
return false ;
2013-02-04 13:29:59 +03:00
}
2023-04-02 18:56:10 +02:00
return true ;
2013-02-04 13:29:59 +03:00
}
void CMapLoaderH3M : : readTerrain ( )
{
map - > initTerrain ( ) ;
// Read terrain
2022-09-18 16:39:10 +02:00
int3 pos ;
2022-09-23 16:24:01 +02:00
for ( pos . z = 0 ; pos . z < map - > levels ( ) ; + + pos . z )
2013-02-04 13:29:59 +03:00
{
2022-09-18 16:39:10 +02:00
//OH3 format is [z][y][x]
for ( pos . y = 0 ; pos . y < map - > height ; pos . y + + )
2013-02-04 13:29:59 +03:00
{
2022-09-18 16:39:10 +02:00
for ( pos . x = 0 ; pos . x < map - > width ; pos . x + + )
2013-02-04 13:29:59 +03:00
{
2022-09-18 16:39:10 +02:00
auto & tile = map - > getTile ( pos ) ;
2023-04-02 18:56:10 +02:00
tile . terType = VLC - > terrainTypeHandler - > getById ( reader - > readTerrain ( ) ) ;
2023-02-24 16:15:45 +02:00
tile . terView = reader - > readUInt8 ( ) ;
2023-04-02 18:56:10 +02:00
tile . riverType = VLC - > riverTypeHandler - > getById ( reader - > readRiver ( ) ) ;
2023-02-24 16:15:45 +02:00
tile . riverDir = reader - > readUInt8 ( ) ;
2023-04-02 18:56:10 +02:00
tile . roadType = VLC - > roadTypeHandler - > getById ( reader - > readRoad ( ) ) ;
2023-02-24 16:15:45 +02:00
tile . roadDir = reader - > readUInt8 ( ) ;
tile . extTileFlags = reader - > readUInt8 ( ) ;
2023-01-10 20:09:09 +02:00
tile . blocked = ! tile . terType - > isPassable ( ) ;
2023-02-11 18:30:06 +02:00
tile . visitable = false ;
2023-01-10 20:09:09 +02:00
assert ( tile . terType - > getId ( ) ! = ETerrainId : : NONE ) ;
2013-02-04 13:29:59 +03:00
}
}
}
}
void CMapLoaderH3M : : readDefInfo ( )
{
2023-04-03 14:02:15 +02:00
uint32_t defAmount = reader - > readUInt32 ( ) ;
2013-02-04 13:29:59 +03:00
2014-01-03 02:48:38 +03:00
templates . reserve ( defAmount ) ;
2013-02-04 13:29:59 +03:00
// Read custom defs
2023-04-05 13:08:41 +02:00
for ( int defID = 0 ; defID < defAmount ; + + defID )
2013-02-04 13:29:59 +03:00
{
2023-02-11 18:30:06 +02:00
auto * tmpl = new ObjectTemplate ;
2023-04-02 18:56:10 +02:00
tmpl - > readMap ( reader - > getInternalReader ( ) ) ;
2022-09-11 15:12:35 +02:00
templates . push_back ( std : : shared_ptr < const ObjectTemplate > ( tmpl ) ) ;
2013-02-04 13:29:59 +03:00
}
}
2023-04-05 13:08:41 +02:00
CGObjectInstance * CMapLoaderH3M : : readEvent ( const int3 & mapPosition )
2013-02-04 13:29:59 +03:00
{
2023-04-03 21:41:05 +02:00
auto * object = new CGEvent ( ) ;
2013-02-04 13:29:59 +03:00
2023-04-05 13:08:41 +02:00
readBoxContent ( object , mapPosition ) ;
2013-02-04 13:29:59 +03:00
2023-04-03 21:41:05 +02:00
object - > availableFor = reader - > readUInt8 ( ) ;
object - > computerActivate = reader - > readBool ( ) ;
object - > removeAfterVisit = reader - > readBool ( ) ;
2013-02-04 13:29:59 +03:00
2023-04-03 21:41:05 +02:00
reader - > skipZero ( 4 ) ;
2013-02-04 13:29:59 +03:00
2023-04-05 13:08:41 +02:00
if ( features . levelHOTA3 )
2023-04-04 12:45:31 +02:00
object - > humanActivate = reader - > readBool ( ) ;
else
object - > humanActivate = true ;
2013-02-04 13:29:59 +03:00
2023-04-03 21:41:05 +02:00
return object ;
}
2013-02-04 13:29:59 +03:00
2023-04-05 13:08:41 +02:00
CGObjectInstance * CMapLoaderH3M : : readPandora ( const int3 & mapPosition )
2023-04-03 21:41:05 +02:00
{
auto * object = new CGPandoraBox ( ) ;
2023-04-05 13:08:41 +02:00
readBoxContent ( object , mapPosition ) ;
2023-04-03 21:41:05 +02:00
return object ;
}
2013-02-04 13:29:59 +03:00
2023-04-05 13:08:41 +02:00
void CMapLoaderH3M : : readBoxContent ( CGPandoraBox * object , const int3 & mapPosition )
2023-04-03 21:41:05 +02:00
{
2023-04-05 13:08:41 +02:00
readMessageAndGuards ( object - > message , object , mapPosition ) ;
2013-02-04 13:29:59 +03:00
2023-04-03 21:41:05 +02:00
object - > gainedExp = reader - > readUInt32 ( ) ;
object - > manaDiff = reader - > readInt32 ( ) ;
object - > moraleDiff = reader - > readInt8 ( ) ;
object - > luckDiff = reader - > readInt8 ( ) ;
2013-02-04 13:29:59 +03:00
2023-04-03 21:41:05 +02:00
reader - > readResourses ( object - > resources ) ;
2013-02-04 13:29:59 +03:00
2023-04-03 21:41:05 +02:00
object - > primskills . resize ( GameConstants : : PRIMARY_SKILLS ) ;
for ( int x = 0 ; x < GameConstants : : PRIMARY_SKILLS ; + + x )
object - > primskills [ x ] = static_cast < PrimarySkill : : PrimarySkill > ( reader - > readUInt8 ( ) ) ;
2013-02-04 13:29:59 +03:00
2023-04-05 13:08:41 +02:00
int gabn = reader - > readUInt8 ( ) ; //number of gained abilities
2023-04-03 21:41:05 +02:00
for ( int oo = 0 ; oo < gabn ; + + oo )
2013-02-04 13:29:59 +03:00
{
2023-04-03 21:41:05 +02:00
object - > abilities . emplace_back ( reader - > readSkill ( ) ) ;
object - > abilityLevels . push_back ( reader - > readUInt8 ( ) ) ;
}
int gart = reader - > readUInt8 ( ) ; //number of gained artifacts
for ( int oo = 0 ; oo < gart ; + + oo )
object - > artifacts . emplace_back ( reader - > readArtifact ( ) ) ;
2013-02-04 13:29:59 +03:00
2023-04-03 21:41:05 +02:00
int gspel = reader - > readUInt8 ( ) ; //number of gained spells
for ( int oo = 0 ; oo < gspel ; + + oo )
object - > spells . emplace_back ( reader - > readSpell ( ) ) ;
2013-02-04 13:29:59 +03:00
2023-04-03 21:41:05 +02:00
int gcre = reader - > readUInt8 ( ) ; //number of gained creatures
readCreatureSet ( & object - > creatures , gcre ) ;
reader - > skipZero ( 8 ) ;
}
2013-02-04 13:29:59 +03:00
2023-04-05 13:08:41 +02:00
CGObjectInstance * CMapLoaderH3M : : readMonster ( const int3 & mapPosition , const ObjectInstanceID & objectInstanceID )
2023-04-03 21:41:05 +02:00
{
auto * object = new CGCreature ( ) ;
2013-02-04 13:29:59 +03:00
2023-04-03 21:41:05 +02:00
if ( features . levelAB )
{
object - > identifier = reader - > readUInt32 ( ) ;
2023-04-05 13:08:41 +02:00
map - > questIdentifierToId [ object - > identifier ] = objectInstanceID ;
2023-04-03 21:41:05 +02:00
}
2013-02-04 13:29:59 +03:00
2023-04-03 21:41:05 +02:00
auto * hlp = new CStackInstance ( ) ;
hlp - > count = reader - > readUInt16 ( ) ;
2013-02-04 13:29:59 +03:00
2023-04-03 21:41:05 +02:00
//type will be set during initialization
object - > putStack ( SlotID ( 0 ) , hlp ) ;
2013-02-04 13:29:59 +03:00
2023-04-03 21:41:05 +02:00
object - > character = reader - > readInt8 ( ) ;
2013-02-04 13:29:59 +03:00
2023-04-03 21:41:05 +02:00
bool hasMessage = reader - > readBool ( ) ;
if ( hasMessage )
{
2023-04-05 13:08:41 +02:00
object - > message = readLocalizedString ( TextIdentifier ( " monster " , mapPosition . x , mapPosition . y , mapPosition . z , " message " ) ) ;
2023-04-03 21:41:05 +02:00
reader - > readResourses ( object - > resources ) ;
object - > gainedArtifact = reader - > readArtifact ( ) ;
}
object - > neverFlees = reader - > readBool ( ) ;
object - > notGrowingTeam = reader - > readBool ( ) ;
reader - > skipZero ( 2 ) ;
2023-04-04 12:45:31 +02:00
2023-04-05 13:08:41 +02:00
if ( features . levelHOTA3 )
2023-04-04 12:45:31 +02:00
{
2023-04-04 14:40:16 +02:00
//TODO: HotA
2023-04-05 12:40:13 +02:00
int32_t agressionExact = reader - > readInt32 ( ) ; // -1 = default, 1-10 = possible values range
bool joinOnlyForMoney = reader - > readBool ( ) ; // if true, monsters will only join for money
int32_t joinPercent = reader - > readInt32 ( ) ; // 100 = default, percent of monsters that will join on succesfull agression check
int32_t upgradedStack = reader - > readInt32 ( ) ; // Presence of upgraded stack, -1 = random, 0 = never, 1 = always
int32_t stacksCount = reader - > readInt32 ( ) ; // TODO: check possible values. How many creature stacks will be present on battlefield, -1 = default
2023-04-05 13:08:41 +02:00
if ( agressionExact ! = - 1 | | joinOnlyForMoney | | joinPercent ! = 100 | | upgradedStack ! = - 1 | | stacksCount ! = - 1 )
logGlobal - > warn (
" Map '%s': Wandering monsters %s settings %d %d %d %d %d are not implemeted! " ,
mapName ,
mapPosition . toString ( ) ,
agressionExact ,
int ( joinOnlyForMoney ) ,
joinPercent ,
upgradedStack ,
stacksCount
) ;
2023-04-04 12:45:31 +02:00
}
2023-04-03 21:41:05 +02:00
return object ;
}
2013-02-04 13:29:59 +03:00
2023-04-05 13:08:41 +02:00
CGObjectInstance * CMapLoaderH3M : : readSign ( const int3 & mapPosition )
2023-04-03 21:41:05 +02:00
{
auto * object = new CGSignBottle ( ) ;
2023-04-05 13:08:41 +02:00
object - > message = readLocalizedString ( TextIdentifier ( " sign " , mapPosition . x , mapPosition . y , mapPosition . z , " message " ) ) ;
2023-04-03 21:41:05 +02:00
reader - > skipZero ( 4 ) ;
return object ;
}
2023-04-05 13:08:41 +02:00
CGObjectInstance * CMapLoaderH3M : : readWitchHut ( )
2023-04-03 21:41:05 +02:00
{
auto * object = new CGWitchHut ( ) ;
// AB and later maps have allowed abilities defined in H3M
if ( features . levelAB )
{
reader - > readBitmask ( object - > allowedAbilities , features . skillsBytes , features . skillsCount , false ) ;
if ( object - > allowedAbilities . size ( ) ! = 1 )
2013-02-04 13:29:59 +03:00
{
2023-04-03 21:41:05 +02:00
auto defaultAllowed = VLC - > skillh - > getDefaultAllowed ( ) ;
for ( int skillID = 0 ; skillID < VLC - > skillh - > size ( ) ; + + skillID )
2023-04-05 13:08:41 +02:00
if ( defaultAllowed [ skillID ] )
2023-04-03 21:41:05 +02:00
object - > allowedAbilities . insert ( skillID ) ;
}
}
return object ;
}
2023-04-05 13:08:41 +02:00
CGObjectInstance * CMapLoaderH3M : : readScholar ( )
2023-04-03 21:41:05 +02:00
{
auto * object = new CGScholar ( ) ;
object - > bonusType = static_cast < CGScholar : : EBonusType > ( reader - > readUInt8 ( ) ) ;
object - > bonusID = reader - > readUInt8 ( ) ;
reader - > skipZero ( 6 ) ;
2023-04-11 16:46:08 +02:00
return object ;
}
CGObjectInstance * CMapLoaderH3M : : readGarrison ( const int3 & mapPosition )
{
auto * object = new CGGarrison ( ) ;
setOwnerAndValidate ( mapPosition , object , reader - > readPlayer32 ( ) ) ;
readCreatureSet ( object , 7 ) ;
if ( features . levelAB )
object - > removableUnits = reader - > readBool ( ) ;
2023-04-03 21:41:05 +02:00
else
object - > removableUnits = true ;
reader - > skipZero ( 8 ) ;
return object ;
}
2023-04-05 13:08:41 +02:00
CGObjectInstance * CMapLoaderH3M : : readArtifact ( const int3 & mapPosition , std : : shared_ptr < const ObjectTemplate > objectTemplate )
2023-04-03 21:41:05 +02:00
{
auto artID = ArtifactID : : NONE ; //random, set later
int spellID = - 1 ;
auto * object = new CGArtifact ( ) ;
2023-04-05 13:08:41 +02:00
readMessageAndGuards ( object - > message , object , mapPosition ) ;
2023-04-03 21:41:05 +02:00
2023-04-05 13:08:41 +02:00
if ( objectTemplate - > id = = Obj : : SPELL_SCROLL )
2023-04-03 21:41:05 +02:00
{
spellID = reader - > readSpell32 ( ) ;
artID = ArtifactID : : SPELL_SCROLL ;
}
2023-04-05 13:08:41 +02:00
else if ( objectTemplate - > id = = Obj : : ARTIFACT )
2023-04-03 21:41:05 +02:00
{
//specific artifact
2023-04-05 13:08:41 +02:00
artID = ArtifactID ( objectTemplate - > subid ) ;
2023-04-03 21:41:05 +02:00
}
object - > storedArtifact = CArtifactInstance : : createArtifact ( map , artID , spellID ) ;
return object ;
}
2023-04-05 13:08:41 +02:00
CGObjectInstance * CMapLoaderH3M : : readResource ( const int3 & mapPosition , std : : shared_ptr < const ObjectTemplate > objectTemplate )
2023-04-03 21:41:05 +02:00
{
auto * object = new CGResource ( ) ;
2023-04-05 13:08:41 +02:00
readMessageAndGuards ( object - > message , object , mapPosition ) ;
2023-04-03 21:41:05 +02:00
object - > amount = reader - > readUInt32 ( ) ;
2023-04-05 13:08:41 +02:00
if ( objectTemplate - > subid = = GameResID ( EGameResID : : GOLD ) )
2023-04-03 21:41:05 +02:00
{
// Gold is multiplied by 100.
object - > amount * = 100 ;
}
reader - > skipZero ( 4 ) ;
return object ;
}
2023-04-05 13:08:41 +02:00
CGObjectInstance * CMapLoaderH3M : : readMine ( const int3 & mapPosition , std : : shared_ptr < const ObjectTemplate > objectTemplate )
2023-04-03 21:41:05 +02:00
{
2023-04-11 16:46:08 +02:00
auto * object = new CGMine ( ) ;
if ( objectTemplate - > subid < 7 )
{
setOwnerAndValidate ( mapPosition , object , reader - > readPlayer32 ( ) ) ;
}
else
{
2023-04-03 21:41:05 +02:00
object - > setOwner ( PlayerColor : : NEUTRAL ) ;
reader - > readBitmask ( object - > abandonedMineResources , features . resourcesBytes , features . resourcesCount , false ) ;
}
return object ;
}
2023-04-11 16:46:08 +02:00
CGObjectInstance * CMapLoaderH3M : : readDwelling ( const int3 & position )
{
auto * object = new CGDwelling ( ) ;
setOwnerAndValidate ( position , object , reader - > readPlayer32 ( ) ) ;
return object ;
}
2023-04-05 13:08:41 +02:00
CGObjectInstance * CMapLoaderH3M : : readDwellingRandom ( const int3 & mapPosition , std : : shared_ptr < const ObjectTemplate > objectTemplate )
2023-04-03 21:41:05 +02:00
{
auto * object = new CGDwelling ( ) ;
CSpecObjInfo * spec = nullptr ;
2023-04-05 13:08:41 +02:00
switch ( objectTemplate - > id )
2023-04-03 21:41:05 +02:00
{
2023-04-05 13:08:41 +02:00
case Obj : : RANDOM_DWELLING :
spec = new CCreGenLeveledCastleInfo ( ) ;
break ;
case Obj : : RANDOM_DWELLING_LVL :
spec = new CCreGenAsCastleInfo ( ) ;
break ;
case Obj : : RANDOM_DWELLING_FACTION :
spec = new CCreGenLeveledInfo ( ) ;
break ;
default :
throw std : : runtime_error ( " Invalid random dwelling format " ) ;
2023-04-11 16:46:08 +02:00
}
spec - > owner = object ;
setOwnerAndValidate ( mapPosition , object , reader - > readPlayer32 ( ) ) ;
//216 and 217
if ( auto * castleSpec = dynamic_cast < CCreGenAsCastleInfo * > ( spec ) )
2023-04-03 21:41:05 +02:00
{
castleSpec - > instanceId = " " ;
castleSpec - > identifier = reader - > readUInt32 ( ) ;
if ( ! castleSpec - > identifier )
{
castleSpec - > asCastle = false ;
const int MASK_SIZE = 8 ;
ui8 mask [ 2 ] ;
mask [ 0 ] = reader - > readUInt8 ( ) ;
mask [ 1 ] = reader - > readUInt8 ( ) ;
castleSpec - > allowedFactions . clear ( ) ;
castleSpec - > allowedFactions . resize ( VLC - > townh - > size ( ) , false ) ;
for ( int i = 0 ; i < MASK_SIZE ; i + + )
2023-04-05 13:08:41 +02:00
castleSpec - > allowedFactions [ i ] = ( ( mask [ 0 ] & ( 1 < < i ) ) > 0 ) ;
2023-04-03 21:41:05 +02:00
2023-04-05 13:08:41 +02:00
for ( int i = 0 ; i < ( GameConstants : : F_NUMBER - MASK_SIZE ) ; i + + )
castleSpec - > allowedFactions [ i + MASK_SIZE ] = ( ( mask [ 1 ] & ( 1 < < i ) ) > 0 ) ;
2023-04-03 21:41:05 +02:00
}
else
{
castleSpec - > asCastle = true ;
}
}
//216 and 218
if ( auto * lvlSpec = dynamic_cast < CCreGenLeveledInfo * > ( spec ) )
{
lvlSpec - > minLevel = std : : max ( reader - > readUInt8 ( ) , static_cast < ui8 > ( 1 ) ) ;
lvlSpec - > maxLevel = std : : min ( reader - > readUInt8 ( ) , static_cast < ui8 > ( 7 ) ) ;
}
object - > info = spec ;
return object ;
}
2023-04-05 13:08:41 +02:00
CGObjectInstance * CMapLoaderH3M : : readShrine ( )
2023-04-03 21:41:05 +02:00
{
auto * object = new CGShrine ( ) ;
object - > spell = reader - > readSpell32 ( ) ;
return object ;
}
2023-04-05 13:08:41 +02:00
CGObjectInstance * CMapLoaderH3M : : readHeroPlaceholder ( const int3 & mapPosition )
2023-04-11 16:46:08 +02:00
{
auto * object = new CGHeroPlaceholder ( ) ;
setOwnerAndValidate ( mapPosition , object , reader - > readPlayer ( ) ) ;
HeroTypeID htid = reader - > readHero ( ) ; //hero type id
object - > subID = htid . getNum ( ) ;
2023-04-03 21:41:05 +02:00
if ( htid . getNum ( ) = = - 1 )
{
object - > power = reader - > readUInt8 ( ) ;
2023-04-05 13:08:41 +02:00
logGlobal - > debug ( " Hero placeholder: by power at %s " , mapPosition . toString ( ) ) ;
2023-04-03 21:41:05 +02:00
}
else
{
object - > power = 0 ;
2023-04-05 13:08:41 +02:00
logGlobal - > debug ( " Hero placeholder: %s at %s " , VLC - > heroh - > getById ( htid ) - > getNameTranslated ( ) , mapPosition . toString ( ) ) ;
2023-04-03 21:41:05 +02:00
}
return object ;
}
2023-04-12 00:02:01 +02:00
CGObjectInstance * CMapLoaderH3M : : readGrail ( const int3 & mapPosition , std : : shared_ptr < const ObjectTemplate > objectTemplate )
2023-04-03 21:41:05 +02:00
{
2023-04-12 00:02:01 +02:00
if ( objectTemplate - > subid < 1000 )
{
map - > grailPos = mapPosition ;
map - > grailRadius = reader - > readInt32 ( ) ;
}
else
{
// Battle location for arena mode in HotA
logGlobal - > warn ( " Map '%s': Arena mode is not supported! " , mapName ) ;
}
2023-04-03 21:41:05 +02:00
return nullptr ;
}
2023-04-05 13:08:41 +02:00
CGObjectInstance * CMapLoaderH3M : : readGeneric ( const int3 & mapPosition , std : : shared_ptr < const ObjectTemplate > objectTemplate )
2023-04-03 21:41:05 +02:00
{
2023-04-05 13:08:41 +02:00
if ( VLC - > objtypeh - > knownSubObjects ( objectTemplate - > id ) . count ( objectTemplate - > subid ) )
return VLC - > objtypeh - > getHandlerFor ( objectTemplate - > id , objectTemplate - > subid ) - > create ( objectTemplate ) ;
2023-04-03 21:41:05 +02:00
2023-04-11 16:46:08 +02:00
logGlobal - > warn ( " Map '%s': Unrecognized object %d:%d ('%s') at %s found! " , mapName , objectTemplate - > id . toEnum ( ) , objectTemplate - > subid , objectTemplate - > animationFile , mapPosition . toString ( ) ) ;
2023-04-03 21:41:05 +02:00
return new CGObjectInstance ( ) ;
}
2023-04-05 13:08:41 +02:00
CGObjectInstance * CMapLoaderH3M : : readPyramid ( const int3 & mapPosition , std : : shared_ptr < const ObjectTemplate > objectTemplate )
2023-04-03 21:41:05 +02:00
{
2023-04-05 13:08:41 +02:00
if ( objectTemplate - > subid = = 0 )
2023-04-03 21:41:05 +02:00
return new CBank ( ) ;
return new CGObjectInstance ( ) ;
}
2023-04-05 13:08:41 +02:00
CGObjectInstance * CMapLoaderH3M : : readQuestGuard ( const int3 & mapPosition )
2023-04-03 21:41:05 +02:00
{
auto * guard = new CGQuestGuard ( ) ;
2023-04-05 13:08:41 +02:00
readQuest ( guard , mapPosition ) ;
2023-04-11 16:46:08 +02:00
return guard ;
}
CGObjectInstance * CMapLoaderH3M : : readShipyard ( const int3 & mapPosition )
{
auto * object = new CGShipyard ( ) ;
setOwnerAndValidate ( mapPosition , object , reader - > readPlayer32 ( ) ) ;
return object ;
}
2023-04-05 13:08:41 +02:00
CGObjectInstance * CMapLoaderH3M : : readBorderGuard ( )
2023-04-03 21:41:05 +02:00
{
return new CGBorderGuard ( ) ;
}
2023-04-05 13:08:41 +02:00
CGObjectInstance * CMapLoaderH3M : : readBorderGate ( const int3 & mapPosition , std : : shared_ptr < const ObjectTemplate > objectTemplate )
2023-04-03 21:41:05 +02:00
{
2023-04-05 13:08:41 +02:00
if ( objectTemplate - > subid < 1000 )
2023-04-05 00:08:24 +02:00
return new CGBorderGate ( ) ;
//TODO: HotA - grave has same ID as border gate? WTF?
2023-04-05 13:08:41 +02:00
if ( objectTemplate - > subid = = 1001 )
2023-04-05 00:08:24 +02:00
return new CGObjectInstance ( ) ;
2023-04-05 13:08:41 +02:00
logGlobal - > warn ( " Map '%s: Quest gates at %s are not implemented! " , mapName , mapPosition . toString ( ) ) ;
2023-04-11 16:46:08 +02:00
return readQuestGuard ( mapPosition ) ;
}
CGObjectInstance * CMapLoaderH3M : : readLighthouse ( const int3 & mapPosition )
{
auto * object = new CGLighthouse ( ) ;
setOwnerAndValidate ( mapPosition , object , reader - > readPlayer32 ( ) ) ;
return object ;
}
2023-04-05 13:08:41 +02:00
CGObjectInstance * CMapLoaderH3M : : readBank ( const int3 & mapPosition , std : : shared_ptr < const ObjectTemplate > objectTemplate )
2023-04-04 12:45:31 +02:00
{
2023-04-05 13:08:41 +02:00
if ( features . levelHOTA3 )
2023-04-04 12:45:31 +02:00
{
2023-04-04 14:40:16 +02:00
//TODO: HotA
2023-04-05 12:40:13 +02:00
// index of guards preset. -1 = random, 0-4 = index of possible guards settings
int32_t guardsPresetIndex = reader - > readInt32 ( ) ;
// presence of upgraded stack: -1 = random, 0 = never, 1 = always
int8_t upgradedStackPresence = reader - > readInt8 ( ) ;
2023-04-04 12:45:31 +02:00
2023-04-05 12:40:13 +02:00
assert ( vstd : : iswithin ( guardsPresetIndex , - 1 , 4 ) ) ;
assert ( vstd : : iswithin ( upgradedStackPresence , - 1 , 1 ) ) ;
// list of possible artifacts in reward
// - if list is empty, artifacts are either not present in reward or random
// - if non-empty, then list always have same number of elements as number of artifacts in bank
// - ArtifactID::NONE indictates random artifact, other values indicate artifact that should be used as reward
2023-04-04 12:45:31 +02:00
std : : vector < ArtifactID > artifacts ;
int artNumber = reader - > readUInt32 ( ) ;
for ( int yy = 0 ; yy < artNumber ; + + yy )
{
2023-04-05 12:40:13 +02:00
artifacts . push_back ( reader - > readArtifact32 ( ) ) ;
2023-04-04 12:45:31 +02:00
}
2023-04-05 12:40:13 +02:00
2023-04-05 13:08:41 +02:00
if ( guardsPresetIndex ! = - 1 | | upgradedStackPresence ! = - 1 | | ! artifacts . empty ( ) )
logGlobal - > warn (
" Map '%s: creature bank at %s settings %d %d %d are not implemented! " ,
mapName ,
mapPosition . toString ( ) ,
guardsPresetIndex ,
int ( upgradedStackPresence ) ,
artifacts . size ( )
) ;
2023-04-04 12:45:31 +02:00
}
2023-04-05 13:08:41 +02:00
return readGeneric ( mapPosition , objectTemplate ) ;
2023-04-04 12:45:31 +02:00
}
2023-04-05 13:08:41 +02:00
CGObjectInstance * CMapLoaderH3M : : readObject ( std : : shared_ptr < const ObjectTemplate > objectTemplate , const int3 & mapPosition , const ObjectInstanceID & objectInstanceID )
2023-04-03 21:41:05 +02:00
{
switch ( objectTemplate - > id )
{
2013-02-04 13:29:59 +03:00
case Obj : : EVENT :
2023-04-05 13:08:41 +02:00
return readEvent ( mapPosition ) ;
2023-04-03 21:41:05 +02:00
2013-02-04 13:29:59 +03:00
case Obj : : HERO :
case Obj : : RANDOM_HERO :
case Obj : : PRISON :
2023-04-05 13:08:41 +02:00
return readHero ( mapPosition , objectInstanceID ) ;
2023-04-03 21:41:05 +02:00
case Obj : : MONSTER :
2013-02-04 13:29:59 +03:00
case Obj : : RANDOM_MONSTER :
case Obj : : RANDOM_MONSTER_L1 :
case Obj : : RANDOM_MONSTER_L2 :
case Obj : : RANDOM_MONSTER_L3 :
case Obj : : RANDOM_MONSTER_L4 :
case Obj : : RANDOM_MONSTER_L5 :
case Obj : : RANDOM_MONSTER_L6 :
case Obj : : RANDOM_MONSTER_L7 :
2023-04-05 13:08:41 +02:00
return readMonster ( mapPosition , objectInstanceID ) ;
2013-02-07 20:02:15 +03:00
2013-02-04 13:29:59 +03:00
case Obj : : OCEAN_BOTTLE :
case Obj : : SIGN :
2023-04-05 13:08:41 +02:00
return readSign ( mapPosition ) ;
2023-04-03 21:41:05 +02:00
2013-02-04 13:29:59 +03:00
case Obj : : SEER_HUT :
2023-04-05 13:08:41 +02:00
return readSeerHut ( mapPosition ) ;
2013-02-04 13:29:59 +03:00
case Obj : : WITCH_HUT :
2023-04-05 13:08:41 +02:00
return readWitchHut ( ) ;
2013-02-04 13:29:59 +03:00
case Obj : : SCHOLAR :
2023-04-05 13:08:41 +02:00
return readScholar ( ) ;
2023-04-11 16:46:08 +02:00
2013-02-04 13:29:59 +03:00
case Obj : : GARRISON :
case Obj : : GARRISON2 :
2023-04-11 16:46:08 +02:00
return readGarrison ( mapPosition ) ;
2013-02-04 13:29:59 +03:00
case Obj : : ARTIFACT :
case Obj : : RANDOM_ART :
case Obj : : RANDOM_TREASURE_ART :
case Obj : : RANDOM_MINOR_ART :
case Obj : : RANDOM_MAJOR_ART :
case Obj : : RANDOM_RELIC_ART :
case Obj : : SPELL_SCROLL :
2023-04-05 13:08:41 +02:00
return readArtifact ( mapPosition , objectTemplate ) ;
2013-02-04 13:29:59 +03:00
case Obj : : RANDOM_RESOURCE :
case Obj : : RESOURCE :
2023-04-05 13:08:41 +02:00
return readResource ( mapPosition , objectTemplate ) ;
2013-02-04 13:29:59 +03:00
case Obj : : RANDOM_TOWN :
case Obj : : TOWN :
2023-04-05 13:08:41 +02:00
return readTown ( mapPosition , objectTemplate ) ;
2023-04-03 21:41:05 +02:00
2013-02-04 13:29:59 +03:00
case Obj : : MINE :
case Obj : : ABANDONED_MINE :
2023-04-05 13:08:41 +02:00
return readMine ( mapPosition , objectTemplate ) ;
2023-04-03 21:41:05 +02:00
2013-02-04 13:29:59 +03:00
case Obj : : CREATURE_GENERATOR1 :
case Obj : : CREATURE_GENERATOR2 :
case Obj : : CREATURE_GENERATOR3 :
case Obj : : CREATURE_GENERATOR4 :
2023-04-05 13:08:41 +02:00
return readDwelling ( mapPosition ) ;
2023-04-03 21:41:05 +02:00
2013-02-04 13:29:59 +03:00
case Obj : : SHRINE_OF_MAGIC_INCANTATION :
case Obj : : SHRINE_OF_MAGIC_GESTURE :
case Obj : : SHRINE_OF_MAGIC_THOUGHT :
2023-04-05 13:08:41 +02:00
return readShrine ( ) ;
2013-02-13 22:35:43 +03:00
2013-02-04 13:29:59 +03:00
case Obj : : PANDORAS_BOX :
2023-04-05 13:08:41 +02:00
return readPandora ( mapPosition ) ;
2013-02-04 13:29:59 +03:00
case Obj : : GRAIL :
2023-04-12 00:02:01 +02:00
return readGrail ( mapPosition , objectTemplate ) ;
2013-02-04 13:29:59 +03:00
2023-04-03 21:41:05 +02:00
case Obj : : RANDOM_DWELLING :
case Obj : : RANDOM_DWELLING_LVL :
case Obj : : RANDOM_DWELLING_FACTION :
2023-04-05 13:08:41 +02:00
return readDwellingRandom ( mapPosition , objectTemplate ) ;
2013-02-04 13:29:59 +03:00
2023-04-03 21:41:05 +02:00
case Obj : : QUEST_GUARD :
2023-04-11 16:46:08 +02:00
return readQuestGuard ( mapPosition ) ;
2013-02-04 13:29:59 +03:00
2023-04-11 16:46:08 +02:00
case Obj : : SHIPYARD :
return readShipyard ( mapPosition ) ;
2013-02-04 13:29:59 +03:00
2023-04-11 16:46:08 +02:00
case Obj : : HERO_PLACEHOLDER :
return readHeroPlaceholder ( mapPosition ) ;
2013-02-04 13:29:59 +03:00
case Obj : : BORDERGUARD :
2023-04-05 13:08:41 +02:00
return readBorderGuard ( ) ;
2013-02-04 13:29:59 +03:00
case Obj : : BORDER_GATE :
2023-04-05 13:08:41 +02:00
return readBorderGate ( mapPosition , objectTemplate ) ;
2013-02-04 13:29:59 +03:00
2023-04-03 21:41:05 +02:00
case Obj : : PYRAMID :
2023-04-11 16:46:08 +02:00
return readPyramid ( mapPosition , objectTemplate ) ;
2013-02-04 13:29:59 +03:00
2023-04-11 16:46:08 +02:00
case Obj : : LIGHTHOUSE :
return readLighthouse ( mapPosition ) ;
2013-02-04 13:29:59 +03:00
2023-04-11 16:46:08 +02:00
case Obj : : CREATURE_BANK :
case Obj : : DERELICT_SHIP :
2023-04-04 12:45:31 +02:00
case Obj : : DRAGON_UTOPIA :
case Obj : : CRYPT :
case Obj : : SHIPWRECK :
2023-04-05 13:08:41 +02:00
return readBank ( mapPosition , objectTemplate ) ;
2013-02-04 13:29:59 +03:00
default : //any other object
2023-04-05 13:08:41 +02:00
return readGeneric ( mapPosition , objectTemplate ) ;
2023-04-03 21:41:05 +02:00
}
}
void CMapLoaderH3M : : readObjects ( )
{
2023-04-05 13:08:41 +02:00
uint32_t objectsCount = reader - > readUInt32 ( ) ;
2023-04-03 21:41:05 +02:00
2023-04-05 13:08:41 +02:00
for ( uint32_t i = 0 ; i < objectsCount ; + + i )
2023-04-03 21:41:05 +02:00
{
2023-04-05 13:08:41 +02:00
int3 mapPosition = reader - > readInt3 ( ) ;
2023-04-03 21:41:05 +02:00
2023-04-05 13:08:41 +02:00
uint32_t defIndex = reader - > readUInt32 ( ) ;
ObjectInstanceID objectInstanceID = ObjectInstanceID ( static_cast < si32 > ( map - > objects . size ( ) ) ) ;
2023-04-03 21:41:05 +02:00
2023-04-05 13:08:41 +02:00
std : : shared_ptr < const ObjectTemplate > objectTemplate = templates . at ( defIndex ) ;
2023-04-03 21:41:05 +02:00
reader - > skipZero ( 5 ) ;
2023-04-05 13:08:41 +02:00
CGObjectInstance * newObject = readObject ( objectTemplate , mapPosition , objectInstanceID ) ;
2023-04-03 21:41:05 +02:00
2023-04-05 13:08:41 +02:00
if ( ! newObject )
2023-04-03 21:41:05 +02:00
continue ;
2013-02-04 13:29:59 +03:00
2023-04-05 13:08:41 +02:00
newObject - > pos = mapPosition ;
newObject - > ID = objectTemplate - > id ;
newObject - > id = objectInstanceID ;
if ( newObject - > ID ! = Obj : : HERO & & newObject - > ID ! = Obj : : HERO_PLACEHOLDER & & newObject - > ID ! = Obj : : PRISON )
2013-02-04 13:29:59 +03:00
{
2023-04-05 13:08:41 +02:00
newObject - > subID = objectTemplate - > subid ;
2013-02-04 13:29:59 +03:00
}
2023-04-05 13:08:41 +02:00
newObject - > appearance = objectTemplate ;
assert ( objectInstanceID = = ObjectInstanceID ( ( si32 ) map - > objects . size ( ) ) ) ;
2016-02-22 18:26:42 +02:00
2013-02-04 13:29:59 +03:00
{
2023-04-02 18:56:10 +02:00
//TODO: define valid typeName and subtypeName for H3M maps
2016-02-22 18:26:42 +02:00
//boost::format fmt("%s_%d");
//fmt % nobj->typeName % nobj->id.getNum();
boost : : format fmt ( " obj_%d " ) ;
2023-04-05 13:08:41 +02:00
fmt % newObject - > id . getNum ( ) ;
newObject - > instanceName = fmt . str ( ) ;
2013-02-04 13:29:59 +03:00
}
2023-04-05 13:08:41 +02:00
map - > addNewObject ( newObject ) ;
2013-02-04 13:29:59 +03:00
}
2023-04-05 13:08:41 +02:00
std : : sort (
map - > heroesOnMap . begin ( ) ,
map - > heroesOnMap . end ( ) ,
[ ] ( const ConstTransitivePtr < CGHeroInstance > & a , const ConstTransitivePtr < CGHeroInstance > & b )
{
return a - > subID < b - > subID ;
}
) ;
2013-02-04 13:29:59 +03:00
}
2013-02-05 00:58:42 +03:00
void CMapLoaderH3M : : readCreatureSet ( CCreatureSet * out , int number )
2013-02-04 13:29:59 +03:00
{
2023-04-05 13:08:41 +02:00
for ( int index = 0 ; index < number ; + + index )
2013-02-04 13:29:59 +03:00
{
2023-04-05 13:08:41 +02:00
CreatureID creatureID = reader - > readCreature ( ) ;
2023-04-02 18:56:10 +02:00
int count = reader - > readUInt16 ( ) ;
2013-02-04 13:29:59 +03:00
// Empty slot
2023-04-05 13:08:41 +02:00
if ( creatureID = = CreatureID : : NONE )
2014-02-09 15:10:02 +03:00
continue ;
2013-02-04 13:29:59 +03:00
2023-04-05 13:08:41 +02:00
auto * result = new CStackInstance ( ) ;
result - > count = count ;
2013-02-04 13:29:59 +03:00
2023-04-05 13:08:41 +02:00
if ( creatureID < CreatureID : : NONE )
2013-02-04 13:29:59 +03:00
{
2023-04-05 13:08:41 +02:00
int value = - creatureID . getNum ( ) - 2 ;
2023-04-02 18:56:10 +02:00
assert ( value > = 0 & & value < 14 ) ;
uint8_t level = value / 2 ;
uint8_t upgrade = value % 2 ;
2013-02-04 13:29:59 +03:00
//this will happen when random object has random army
2023-04-05 13:08:41 +02:00
result - > randomStack = CStackInstance : : RandomStackInfo { level , upgrade } ;
2013-02-04 13:29:59 +03:00
}
else
{
2023-04-05 13:08:41 +02:00
result - > setType ( creatureID ) ;
2013-02-04 13:29:59 +03:00
}
2023-04-05 13:08:41 +02:00
out - > putStack ( SlotID ( index ) , result ) ;
2013-02-04 13:29:59 +03:00
}
out - > validTypes ( true ) ;
}
2023-04-11 16:46:08 +02:00
void CMapLoaderH3M : : setOwnerAndValidate ( const int3 & mapPosition , CGObjectInstance * object , const PlayerColor & owner )
2013-02-04 13:29:59 +03:00
{
2023-04-11 16:46:08 +02:00
assert ( owner . isValidPlayer ( ) | | owner = = PlayerColor : : NEUTRAL ) ;
2013-02-04 13:29:59 +03:00
2023-04-11 16:46:08 +02:00
if ( owner = = PlayerColor : : NEUTRAL )
2013-02-04 13:29:59 +03:00
{
2023-04-11 16:46:08 +02:00
object - > setOwner ( PlayerColor : : NEUTRAL ) ;
return ;
}
if ( ! owner . isValidPlayer ( ) )
{
object - > setOwner ( PlayerColor : : NEUTRAL ) ;
logGlobal - > warn ( " Map '%s': Object at %s - owned by invalid player %d! Will be set to neutral! " , mapName , mapPosition . toString ( ) , int ( owner . getNum ( ) ) ) ;
return ;
}
if ( ! mapHeader - > players [ owner . getNum ( ) ] . canAnyonePlay ( ) )
{
object - > setOwner ( PlayerColor : : NEUTRAL ) ;
logGlobal - > warn ( " Map '%s': Object at %s - owned by non-existing player %d! Will be set to neutral! " , mapName , mapPosition . toString ( ) , int ( owner . getNum ( ) )
) ;
return ;
2013-02-04 13:29:59 +03:00
}
2023-04-11 16:46:08 +02:00
object - > setOwner ( owner ) ;
}
CGObjectInstance * CMapLoaderH3M : : readHero ( const int3 & mapPosition , const ObjectInstanceID & objectInstanceID )
{
auto * object = new CGHeroInstance ( ) ;
2013-02-04 13:29:59 +03:00
2023-04-02 18:56:10 +02:00
if ( features . levelAB )
2013-02-04 13:29:59 +03:00
{
2023-02-24 16:15:45 +02:00
unsigned int identifier = reader - > readUInt32 ( ) ;
2023-04-05 13:08:41 +02:00
map - > questIdentifierToId [ identifier ] = objectInstanceID ;
2013-02-04 13:29:59 +03:00
}
2023-04-02 18:56:10 +02:00
PlayerColor owner = reader - > readPlayer ( ) ;
2023-04-05 13:08:41 +02:00
object - > subID = reader - > readUInt8 ( ) ;
2013-09-03 16:18:09 +03:00
2013-05-19 18:14:23 +03:00
//If hero of this type has been predefined, use that as a base.
//Instance data will overwrite the predefined values where appropriate.
2013-06-29 16:05:48 +03:00
for ( auto & elem : map - > predefinedHeroes )
2013-02-04 13:29:59 +03:00
{
2023-04-05 13:08:41 +02:00
if ( elem - > subID = = object - > subID )
2013-02-04 13:29:59 +03:00
{
2023-04-05 13:08:41 +02:00
logGlobal - > debug ( " Hero %d will be taken from the predefined heroes list. " , object - > subID ) ;
delete object ;
object = elem ;
2013-02-04 13:29:59 +03:00
break ;
}
}
2023-04-11 16:46:08 +02:00
setOwnerAndValidate ( mapPosition , object , owner ) ;
object - > portrait = object - > subID ;
2013-02-04 13:29:59 +03:00
2013-06-29 16:05:48 +03:00
for ( auto & elem : map - > disposedHeroes )
2013-02-04 13:29:59 +03:00
{
2023-04-05 13:08:41 +02:00
if ( elem . heroId = = object - > subID )
2013-02-04 13:29:59 +03:00
{
2023-04-05 13:08:41 +02:00
object - > nameCustom = elem . name ;
object - > portrait = elem . portrait ;
2013-02-04 13:29:59 +03:00
break ;
}
}
2023-02-24 16:15:45 +02:00
bool hasName = reader - > readBool ( ) ;
2013-02-07 20:02:15 +03:00
if ( hasName )
2023-04-05 13:08:41 +02:00
object - > nameCustom = readLocalizedString ( TextIdentifier ( " heroes " , object - > subID , " name " ) ) ;
2023-04-02 18:56:10 +02:00
if ( features . levelSOD )
2013-02-04 13:29:59 +03:00
{
2023-04-05 13:08:41 +02:00
bool hasCustomExperience = reader - > readBool ( ) ;
if ( hasCustomExperience )
object - > exp = reader - > readUInt32 ( ) ;
2013-02-04 13:29:59 +03:00
else
2023-04-05 13:08:41 +02:00
object - > exp = CGHeroInstance : : UNINITIALIZED_EXPERIENCE ;
2013-02-04 13:29:59 +03:00
}
else
{
2023-04-05 13:08:41 +02:00
object - > exp = reader - > readUInt32 ( ) ;
2013-02-04 13:29:59 +03:00
//0 means "not set" in <=AB maps
2023-04-05 13:08:41 +02:00
if ( ! object - > exp )
object - > exp = CGHeroInstance : : UNINITIALIZED_EXPERIENCE ;
2013-02-04 13:29:59 +03:00
}
2023-02-24 16:15:45 +02:00
bool hasPortrait = reader - > readBool ( ) ;
2013-02-07 20:02:15 +03:00
if ( hasPortrait )
2023-04-05 13:08:41 +02:00
object - > portrait = reader - > readHero ( ) . getNum ( ) ;
2013-02-04 13:29:59 +03:00
2023-02-24 16:15:45 +02:00
bool hasSecSkills = reader - > readBool ( ) ;
2013-02-07 20:02:15 +03:00
if ( hasSecSkills )
2013-02-04 13:29:59 +03:00
{
2023-04-05 13:08:41 +02:00
if ( ! object - > secSkills . empty ( ) )
2013-09-03 16:18:09 +03:00
{
2023-04-05 13:08:41 +02:00
if ( object - > secSkills [ 0 ] . first ! = SecondarySkill : : DEFAULT )
logGlobal - > warn ( " Hero %s subID=%d has set secondary skills twice (in map properties and on adventure map instance). Using the latter set... " , object - > getNameTextID ( ) , object - > subID ) ;
object - > secSkills . clear ( ) ;
2013-09-03 16:18:09 +03:00
}
2023-04-05 13:08:41 +02:00
uint32_t skillsCount = reader - > readUInt32 ( ) ;
object - > secSkills . resize ( skillsCount ) ;
for ( int i = 0 ; i < skillsCount ; + + i )
2013-02-04 13:29:59 +03:00
{
2023-04-05 13:08:41 +02:00
object - > secSkills [ i ] . first = reader - > readSkill ( ) ;
object - > secSkills [ i ] . second = reader - > readUInt8 ( ) ;
2013-02-04 13:29:59 +03:00
}
}
2023-02-24 16:15:45 +02:00
bool hasGarison = reader - > readBool ( ) ;
2013-02-07 20:02:15 +03:00
if ( hasGarison )
2023-04-10 13:01:09 +02:00
readCreatureSet ( object , 7 ) ;
object - > formation = static_cast < EArmyFormation > ( reader - > readUInt8 ( ) ) ;
assert ( object - > formation = = EArmyFormation : : LOOSE | | object - > formation = = EArmyFormation : : TIGHT ) ;
2013-02-04 13:29:59 +03:00
2023-04-10 13:01:09 +02:00
loadArtifactsOfHero ( object ) ;
object - > patrol . patrolRadius = reader - > readUInt8 ( ) ;
2023-04-05 13:08:41 +02:00
object - > patrol . patrolling = ( object - > patrol . patrolRadius ! = 0xff ) ;
2013-02-04 13:29:59 +03:00
2023-04-02 18:56:10 +02:00
if ( features . levelAB )
2013-02-04 13:29:59 +03:00
{
2023-02-24 16:15:45 +02:00
bool hasCustomBiography = reader - > readBool ( ) ;
2013-02-07 20:02:15 +03:00
if ( hasCustomBiography )
2023-04-05 13:08:41 +02:00
object - > biographyCustom = readLocalizedString ( TextIdentifier ( " heroes " , object - > subID , " biography " ) ) ;
2013-02-04 13:29:59 +03:00
2023-04-05 13:08:41 +02:00
object - > gender = static_cast < EHeroGender > ( reader - > readUInt8 ( ) ) ;
assert ( object - > gender = = EHeroGender : : MALE | | object - > gender = = EHeroGender : : FEMALE | | object - > gender = = EHeroGender : : DEFAULT ) ;
2013-02-04 13:29:59 +03:00
}
else
{
2023-04-05 13:08:41 +02:00
object - > gender = EHeroGender : : DEFAULT ;
2013-02-04 13:29:59 +03:00
}
// Spells
2023-04-02 18:56:10 +02:00
if ( features . levelSOD )
2013-02-04 13:29:59 +03:00
{
2023-02-24 16:15:45 +02:00
bool hasCustomSpells = reader - > readBool ( ) ;
2013-02-07 20:02:15 +03:00
if ( hasCustomSpells )
2013-02-04 13:29:59 +03:00
{
2023-04-05 13:08:41 +02:00
if ( ! object - > spells . empty ( ) )
2023-04-02 18:56:10 +02:00
{
2023-04-05 13:08:41 +02:00
object - > clear ( ) ;
logGlobal - > warn ( " Hero %s subID=%d has spells set twice (in map properties and on adventure map instance). Using the latter set... " , object - > getNameTextID ( ) , object - > subID ) ;
2023-04-02 18:56:10 +02:00
}
2023-04-05 13:08:41 +02:00
object - > spells . insert ( SpellID : : PRESET ) ; //placeholder "preset spells"
2013-02-05 00:58:42 +03:00
2023-04-05 13:08:41 +02:00
readSpells ( object - > spells ) ;
2013-02-04 13:29:59 +03:00
}
}
2023-04-02 18:56:10 +02:00
else if ( features . levelAB )
2013-02-04 13:29:59 +03:00
{
//we can read one spell
2023-04-02 18:56:10 +02:00
SpellID spell = reader - > readSpell ( ) ;
if ( spell ! = SpellID : : NONE )
2023-04-05 13:08:41 +02:00
object - > spells . insert ( spell ) ;
2013-02-04 13:29:59 +03:00
}
2023-04-02 18:56:10 +02:00
if ( features . levelSOD )
2013-02-04 13:29:59 +03:00
{
2023-02-24 16:15:45 +02:00
bool hasCustomPrimSkills = reader - > readBool ( ) ;
2013-02-07 20:02:15 +03:00
if ( hasCustomPrimSkills )
2013-02-04 13:29:59 +03:00
{
2023-04-05 13:08:41 +02:00
auto ps = object - > getAllBonuses ( Selector : : type ( ) ( Bonus : : PRIMARY_SKILL ) . And ( Selector : : sourceType ( ) ( Bonus : : HERO_BASE_SKILL ) ) , nullptr ) ;
2013-09-03 16:18:09 +03:00
if ( ps - > size ( ) )
{
2023-04-05 13:08:41 +02:00
logGlobal - > warn ( " Hero %s subID=%d has set primary skills twice (in map properties and on adventure map instance). Using the latter set... " , object - > getNameTranslated ( ) , object - > subID ) ;
2023-02-11 18:30:06 +02:00
for ( const auto & b : * ps )
2023-04-05 13:08:41 +02:00
object - > removeBonus ( b ) ;
2013-09-03 16:18:09 +03:00
}
2013-02-04 13:29:59 +03:00
for ( int xx = 0 ; xx < GameConstants : : PRIMARY_SKILLS ; + + xx )
{
2023-04-05 13:08:41 +02:00
object - > pushPrimSkill ( static_cast < PrimarySkill : : PrimarySkill > ( xx ) , reader - > readUInt8 ( ) ) ;
2013-02-04 13:29:59 +03:00
}
}
}
2023-04-02 18:56:10 +02:00
reader - > skipZero ( 16 ) ;
2023-04-05 13:08:41 +02:00
return object ;
2013-02-04 13:29:59 +03:00
}
2023-04-03 21:41:05 +02:00
CGObjectInstance * CMapLoaderH3M : : readSeerHut ( const int3 & position )
2013-02-04 13:29:59 +03:00
{
2023-02-11 18:30:06 +02:00
auto * hut = new CGSeerHut ( ) ;
2013-02-04 13:29:59 +03:00
2023-04-05 00:08:24 +02:00
uint32_t questsCount = 1 ;
2023-04-05 13:08:41 +02:00
if ( features . levelHOTA3 )
2023-04-05 00:08:24 +02:00
questsCount = reader - > readUInt32 ( ) ;
//TODO: HotA
2023-04-05 13:08:41 +02:00
if ( questsCount > 1 )
2023-04-05 12:40:13 +02:00
logGlobal - > warn ( " Map '%s': Seer Hut at %s - %d quests are not implemented! " , mapName , position . toString ( ) , questsCount ) ;
2023-04-05 00:08:24 +02:00
2023-04-05 13:08:41 +02:00
for ( size_t i = 0 ; i < questsCount ; + + i )
2023-04-05 00:08:24 +02:00
readSeerHutQuest ( hut , position ) ;
2023-04-05 13:08:41 +02:00
if ( features . levelHOTA3 )
2013-02-04 13:29:59 +03:00
{
2023-04-05 00:08:24 +02:00
uint32_t repeateableQuestsCount = reader - > readUInt32 ( ) ;
2023-04-04 12:45:31 +02:00
2023-04-05 13:08:41 +02:00
if ( repeateableQuestsCount ! = 0 )
2023-04-05 00:08:24 +02:00
logGlobal - > warn ( " Map '%s': Seer Hut at %s - %d repeatable quests are not implemented! " , mapName , position . toString ( ) , repeateableQuestsCount ) ;
2023-04-05 13:08:41 +02:00
for ( size_t i = 0 ; i < repeateableQuestsCount ; + + i )
2023-04-05 00:08:24 +02:00
readSeerHutQuest ( hut , position ) ;
}
reader - > skipZero ( 2 ) ;
return hut ;
}
void CMapLoaderH3M : : readSeerHutQuest ( CGSeerHut * hut , const int3 & position )
{
if ( features . levelAB )
2013-02-04 13:29:59 +03:00
{
2023-02-25 17:44:15 +02:00
readQuest ( hut , position ) ;
2013-02-04 13:29:59 +03:00
}
else
{
//RoE
2023-04-02 18:56:10 +02:00
auto artID = reader - > readArtifact ( ) ;
2023-04-05 13:08:41 +02:00
if ( artID ! = ArtifactID : : NONE )
2013-02-04 13:29:59 +03:00
{
//not none quest
2022-10-01 08:28:32 +02:00
hut - > quest - > addArtifactID ( artID ) ;
2013-02-04 13:29:59 +03:00
hut - > quest - > missionType = CQuest : : MISSION_ART ;
}
else
{
hut - > quest - > missionType = CQuest : : MISSION_NONE ;
}
hut - > quest - > lastDay = - 1 ; //no timeout
2023-04-02 18:56:10 +02:00
hut - > quest - > isCustomFirst = false ;
hut - > quest - > isCustomNext = false ;
hut - > quest - > isCustomComplete = false ;
2013-02-04 13:29:59 +03:00
}
2023-04-05 13:08:41 +02:00
if ( hut - > quest - > missionType )
2013-02-04 13:29:59 +03:00
{
2023-02-24 16:15:45 +02:00
auto rewardType = static_cast < CGSeerHut : : ERewardType > ( reader - > readUInt8 ( ) ) ;
2013-02-07 20:02:15 +03:00
hut - > rewardType = rewardType ;
2013-02-04 13:29:59 +03:00
switch ( rewardType )
{
2023-04-05 13:08:41 +02:00
case CGSeerHut : : EXPERIENCE :
2013-02-04 13:29:59 +03:00
{
2023-02-24 16:15:45 +02:00
hut - > rVal = reader - > readUInt32 ( ) ;
2013-02-04 13:29:59 +03:00
break ;
}
2023-04-05 13:08:41 +02:00
case CGSeerHut : : MANA_POINTS :
2013-02-04 13:29:59 +03:00
{
2023-02-24 16:15:45 +02:00
hut - > rVal = reader - > readUInt32 ( ) ;
2013-02-04 13:29:59 +03:00
break ;
}
2023-04-05 13:08:41 +02:00
case CGSeerHut : : MORALE_BONUS :
2013-02-04 13:29:59 +03:00
{
2023-02-24 16:15:45 +02:00
hut - > rVal = reader - > readUInt8 ( ) ;
2013-02-04 13:29:59 +03:00
break ;
}
2023-04-05 13:08:41 +02:00
case CGSeerHut : : LUCK_BONUS :
2013-02-04 13:29:59 +03:00
{
2023-02-24 16:15:45 +02:00
hut - > rVal = reader - > readUInt8 ( ) ;
2013-02-04 13:29:59 +03:00
break ;
}
2023-04-05 13:08:41 +02:00
case CGSeerHut : : RESOURCES :
2013-02-04 13:29:59 +03:00
{
2023-02-24 16:15:45 +02:00
hut - > rID = reader - > readUInt8 ( ) ;
2023-04-02 18:56:10 +02:00
hut - > rVal = reader - > readUInt32 ( ) ;
assert ( hut - > rID < features . resourcesCount ) ;
assert ( ( hut - > rVal & 0x00ffffff ) = = hut - > rVal ) ;
2013-02-04 13:29:59 +03:00
break ;
}
2023-04-05 13:08:41 +02:00
case CGSeerHut : : PRIMARY_SKILL :
2013-02-04 13:29:59 +03:00
{
2023-02-24 16:15:45 +02:00
hut - > rID = reader - > readUInt8 ( ) ;
hut - > rVal = reader - > readUInt8 ( ) ;
2013-02-04 13:29:59 +03:00
break ;
}
2023-04-05 13:08:41 +02:00
case CGSeerHut : : SECONDARY_SKILL :
2013-02-04 13:29:59 +03:00
{
2023-04-02 18:56:10 +02:00
hut - > rID = reader - > readSkill ( ) ;
2023-02-24 16:15:45 +02:00
hut - > rVal = reader - > readUInt8 ( ) ;
2013-02-04 13:29:59 +03:00
break ;
}
2023-04-05 13:08:41 +02:00
case CGSeerHut : : ARTIFACT :
2013-02-04 13:29:59 +03:00
{
2023-04-02 18:56:10 +02:00
hut - > rID = reader - > readArtifact ( ) ;
2013-02-04 13:29:59 +03:00
break ;
}
2023-04-05 13:08:41 +02:00
case CGSeerHut : : SPELL :
2013-02-04 13:29:59 +03:00
{
2023-04-02 18:56:10 +02:00
hut - > rID = reader - > readSpell ( ) ;
2013-02-04 13:29:59 +03:00
break ;
}
2023-04-05 13:08:41 +02:00
case CGSeerHut : : CREATURE :
2013-02-04 13:29:59 +03:00
{
2023-04-02 18:56:10 +02:00
hut - > rID = reader - > readCreature ( ) ;
hut - > rVal = reader - > readUInt16 ( ) ;
2013-02-04 13:29:59 +03:00
break ;
}
2023-04-05 13:08:41 +02:00
case CGSeerHut : : NOTHING :
2023-04-02 18:56:10 +02:00
{
// no-op
2013-02-04 13:29:59 +03:00
break ;
}
2023-04-05 13:08:41 +02:00
default :
2023-04-02 18:56:10 +02:00
{
assert ( 0 ) ;
}
2013-02-04 13:29:59 +03:00
}
}
else
{
// missionType==255
2023-04-05 00:08:24 +02:00
reader - > skipZero ( 1 ) ;
2013-02-04 13:29:59 +03:00
}
}
2023-02-25 17:44:15 +02:00
void CMapLoaderH3M : : readQuest ( IQuestObject * guard , const int3 & position )
2013-02-04 13:29:59 +03:00
{
2023-02-24 16:15:45 +02:00
guard - > quest - > missionType = static_cast < CQuest : : Emission > ( reader - > readUInt8 ( ) ) ;
2013-02-04 13:29:59 +03:00
switch ( guard - > quest - > missionType )
{
2023-04-05 13:08:41 +02:00
case CQuest : : MISSION_NONE :
return ;
case CQuest : : MISSION_PRIMARY_STAT :
2013-02-04 13:29:59 +03:00
{
guard - > quest - > m2stats . resize ( 4 ) ;
for ( int x = 0 ; x < 4 ; + + x )
{
2023-02-24 16:15:45 +02:00
guard - > quest - > m2stats [ x ] = reader - > readUInt8 ( ) ;
2013-02-04 13:29:59 +03:00
}
}
break ;
2023-04-05 13:08:41 +02:00
case CQuest : : MISSION_LEVEL :
case CQuest : : MISSION_KILL_HERO :
case CQuest : : MISSION_KILL_CREATURE :
2013-02-04 13:29:59 +03:00
{
2023-02-24 16:15:45 +02:00
guard - > quest - > m13489val = reader - > readUInt32 ( ) ;
2013-02-04 13:29:59 +03:00
break ;
}
2023-04-05 13:08:41 +02:00
case CQuest : : MISSION_ART :
2013-02-04 13:29:59 +03:00
{
2023-02-24 16:15:45 +02:00
int artNumber = reader - > readUInt8 ( ) ;
2013-02-04 13:29:59 +03:00
for ( int yy = 0 ; yy < artNumber ; + + yy )
{
2023-04-02 18:56:10 +02:00
auto artid = reader - > readArtifact ( ) ;
2022-10-01 08:28:32 +02:00
guard - > quest - > addArtifactID ( artid ) ;
2013-02-04 13:29:59 +03:00
map - > allowedArtifact [ artid ] = false ; //these are unavailable for random generation
}
break ;
}
2023-04-05 13:08:41 +02:00
case CQuest : : MISSION_ARMY :
2013-02-04 13:29:59 +03:00
{
2023-02-24 16:15:45 +02:00
int typeNumber = reader - > readUInt8 ( ) ;
2013-02-04 13:29:59 +03:00
guard - > quest - > m6creatures . resize ( typeNumber ) ;
for ( int hh = 0 ; hh < typeNumber ; + + hh )
{
2023-04-02 18:56:10 +02:00
guard - > quest - > m6creatures [ hh ] . type = VLC - > creh - > objects [ reader - > readCreature ( ) ] ;
2023-02-24 16:15:45 +02:00
guard - > quest - > m6creatures [ hh ] . count = reader - > readUInt16 ( ) ;
2013-02-04 13:29:59 +03:00
}
break ;
}
2023-04-05 13:08:41 +02:00
case CQuest : : MISSION_RESOURCES :
2013-02-04 13:29:59 +03:00
{
for ( int x = 0 ; x < 7 ; + + x )
2023-02-24 16:15:45 +02:00
guard - > quest - > m7resources [ x ] = reader - > readUInt32 ( ) ;
2023-04-05 02:26:29 +02:00
2013-02-04 13:29:59 +03:00
break ;
}
2023-04-05 13:08:41 +02:00
case CQuest : : MISSION_HERO :
2013-02-04 13:29:59 +03:00
{
2023-04-02 18:56:10 +02:00
guard - > quest - > m13489val = reader - > readHero ( ) . getNum ( ) ;
2013-02-04 13:29:59 +03:00
break ;
}
2023-04-05 13:08:41 +02:00
case CQuest : : MISSION_PLAYER :
2013-02-04 13:29:59 +03:00
{
2023-04-02 18:56:10 +02:00
guard - > quest - > m13489val = reader - > readPlayer ( ) . getNum ( ) ;
2013-02-04 13:29:59 +03:00
break ;
}
2023-04-05 13:08:41 +02:00
case CQuest : : MISSION_HOTA_MULTI :
2023-04-05 00:08:24 +02:00
{
uint32_t missionSubID = reader - > readUInt32 ( ) ;
2013-02-04 13:29:59 +03:00
2023-04-05 13:08:41 +02:00
if ( missionSubID = = 0 )
2023-04-05 00:08:24 +02:00
{
guard - > quest - > missionType = CQuest : : MISSION_HOTA_HERO_CLASS ;
std : : set < HeroClassID > heroClasses ;
uint32_t classesCount = reader - > readUInt32 ( ) ;
uint32_t classesBytes = ( classesCount + 7 ) / 8 ;
reader - > readBitmask ( heroClasses , classesBytes , classesCount , false ) ;
logGlobal - > warn ( " Map '%s': Quest at %s 'Belong to one of %d classes' is not implemented! " , mapName , position . toString ( ) , heroClasses . size ( ) ) ;
break ;
}
2023-04-05 13:08:41 +02:00
if ( missionSubID = = 1 )
2023-04-05 00:08:24 +02:00
{
guard - > quest - > missionType = CQuest : : MISSION_HOTA_REACH_DATE ;
uint32_t daysPassed = reader - > readUInt32 ( ) ;
logGlobal - > warn ( " Map '%s': Quest at %s 'Wait till %d days passed' is not implemented! " , mapName , position . toString ( ) , daysPassed ) ;
break ;
}
assert ( 0 ) ;
break ;
}
2023-04-05 13:08:41 +02:00
default :
2023-04-02 18:56:10 +02:00
{
assert ( 0 ) ;
}
2013-02-04 13:29:59 +03:00
}
2023-04-02 18:56:10 +02:00
guard - > quest - > lastDay = reader - > readInt32 ( ) ;
2023-02-25 17:44:15 +02:00
guard - > quest - > firstVisitText = readLocalizedString ( TextIdentifier ( " quest " , position . x , position . y , position . z , " firstVisit " ) ) ;
guard - > quest - > nextVisitText = readLocalizedString ( TextIdentifier ( " quest " , position . x , position . y , position . z , " nextVisit " ) ) ;
guard - > quest - > completedText = readLocalizedString ( TextIdentifier ( " quest " , position . x , position . y , position . z , " completed " ) ) ;
2023-02-11 18:30:06 +02:00
guard - > quest - > isCustomFirst = ! guard - > quest - > firstVisitText . empty ( ) ;
guard - > quest - > isCustomNext = ! guard - > quest - > nextVisitText . empty ( ) ;
guard - > quest - > isCustomComplete = ! guard - > quest - > completedText . empty ( ) ;
2013-02-04 13:29:59 +03:00
}
2023-04-05 13:08:41 +02:00
CGObjectInstance * CMapLoaderH3M : : readTown ( const int3 & position , std : : shared_ptr < const ObjectTemplate > objectTemplate )
2013-02-04 13:29:59 +03:00
{
2023-04-05 13:08:41 +02:00
auto * object = new CGTownInstance ( ) ;
2023-04-11 16:46:08 +02:00
if ( features . levelAB )
object - > identifier = reader - > readUInt32 ( ) ;
setOwnerAndValidate ( position , object , reader - > readPlayer ( ) ) ;
2023-02-24 16:15:45 +02:00
bool hasName = reader - > readBool ( ) ;
2013-02-07 20:02:15 +03:00
if ( hasName )
2023-04-11 16:46:08 +02:00
object - > setNameTranslated ( readLocalizedString ( TextIdentifier ( " town " , position . x , position . y , position . z , " name " ) ) ) ;
2013-02-04 13:29:59 +03:00
2023-02-24 16:15:45 +02:00
bool hasGarrison = reader - > readBool ( ) ;
2013-02-07 20:02:15 +03:00
if ( hasGarrison )
2023-04-10 13:01:09 +02:00
readCreatureSet ( object , 7 ) ;
object - > formation = static_cast < EArmyFormation > ( reader - > readUInt8 ( ) ) ;
assert ( object - > formation = = EArmyFormation : : LOOSE | | object - > formation = = EArmyFormation : : TIGHT ) ;
2013-02-04 13:29:59 +03:00
2023-02-24 16:15:45 +02:00
bool hasCustomBuildings = reader - > readBool ( ) ;
2013-02-07 20:02:15 +03:00
if ( hasCustomBuildings )
2013-02-04 13:29:59 +03:00
{
2023-04-05 13:08:41 +02:00
reader - > readBitmask ( object - > builtBuildings , features . buildingsBytes , features . buildingsCount , false ) ;
reader - > readBitmask ( object - > forbiddenBuildings , features . buildingsBytes , features . buildingsCount , false ) ;
2013-02-04 13:29:59 +03:00
2023-04-05 13:08:41 +02:00
object - > builtBuildings = convertBuildings ( object - > builtBuildings , objectTemplate - > subid ) ;
object - > forbiddenBuildings = convertBuildings ( object - > forbiddenBuildings , objectTemplate - > subid ) ;
2013-02-04 13:29:59 +03:00
}
// Standard buildings
else
{
2023-02-24 16:15:45 +02:00
bool hasFort = reader - > readBool ( ) ;
2013-02-07 20:02:15 +03:00
if ( hasFort )
2023-04-05 13:08:41 +02:00
object - > builtBuildings . insert ( BuildingID : : FORT ) ;
2013-02-04 13:29:59 +03:00
//means that set of standard building should be included
2023-04-05 13:08:41 +02:00
object - > builtBuildings . insert ( BuildingID : : DEFAULT ) ;
2013-02-04 13:29:59 +03:00
}
2023-04-02 18:56:10 +02:00
if ( features . levelAB )
2013-02-04 13:29:59 +03:00
{
2023-04-02 18:56:10 +02:00
std : : set < SpellID > spellsMask ;
2023-04-05 13:08:41 +02:00
reader - > readBitmask ( spellsMask , features . spellsBytes , features . spellsCount , false ) ;
std : : copy ( spellsMask . begin ( ) , spellsMask . end ( ) , std : : back_inserter ( object - > obligatorySpells ) ) ;
2013-02-04 13:29:59 +03:00
}
{
2023-04-02 18:56:10 +02:00
std : : set < SpellID > spellsMask ;
2023-04-05 13:08:41 +02:00
reader - > readBitmask ( spellsMask , features . spellsBytes , features . spellsCount , true ) ;
std : : copy ( spellsMask . begin ( ) , spellsMask . end ( ) , std : : back_inserter ( object - > possibleSpells ) ) ;
2023-04-02 18:56:10 +02:00
2023-04-03 14:02:15 +02:00
auto defaultAllowed = VLC - > spellh - > getDefaultAllowed ( ) ;
//add all spells from mods
2023-04-05 13:08:41 +02:00
for ( int i = features . spellsCount ; i < defaultAllowed . size ( ) ; + + i )
if ( defaultAllowed [ i ] )
object - > possibleSpells . emplace_back ( i ) ;
2013-02-04 13:29:59 +03:00
}
2023-04-09 00:03:43 +02:00
if ( features . levelHOTA1 )
2014-03-09 12:37:20 +03:00
{
2023-04-04 14:40:16 +02:00
// TODO: HOTA support
2023-04-15 16:20:19 +02:00
[[maybe_unused]] bool spellResearchAvailable = reader - > readBool ( ) ;
2014-03-09 12:37:20 +03:00
}
2013-02-04 13:29:59 +03:00
// Read castle events
2023-04-05 13:08:41 +02:00
uint32_t eventsCount = reader - > readUInt32 ( ) ;
2013-02-04 13:29:59 +03:00
2023-04-05 13:08:41 +02:00
for ( int eventID = 0 ; eventID < eventsCount ; + + eventID )
2013-02-04 13:29:59 +03:00
{
2023-04-05 13:08:41 +02:00
CCastleEvent event ;
event . town = object ;
event . name = readBasicString ( ) ;
event . message = readLocalizedString ( TextIdentifier ( " town " , position . x , position . y , position . z , " event " , eventID , " description " ) ) ;
2013-02-05 00:58:42 +03:00
2023-04-05 13:08:41 +02:00
reader - > readResourses ( event . resources ) ;
2013-02-05 00:58:42 +03:00
2023-04-05 13:08:41 +02:00
event . players = reader - > readUInt8 ( ) ;
2023-04-02 18:56:10 +02:00
if ( features . levelSOD )
2023-04-05 13:08:41 +02:00
event . humanAffected = reader - > readBool ( ) ;
2013-02-04 13:29:59 +03:00
else
2023-04-05 13:08:41 +02:00
event . humanAffected = true ;
2013-02-04 13:29:59 +03:00
2023-04-05 13:08:41 +02:00
event . computerAffected = reader - > readUInt8 ( ) ;
event . firstOccurence = reader - > readUInt16 ( ) ;
event . nextOccurence = reader - > readUInt8 ( ) ;
2013-02-04 13:29:59 +03:00
2023-04-02 18:56:10 +02:00
reader - > skipZero ( 17 ) ;
2013-02-04 13:29:59 +03:00
// New buildings
2013-02-07 20:02:15 +03:00
2023-04-05 13:08:41 +02:00
reader - > readBitmask ( event . buildings , features . buildingsBytes , features . buildingsCount , false ) ;
2013-02-07 20:02:15 +03:00
2023-04-05 13:08:41 +02:00
event . buildings = convertBuildings ( event . buildings , objectTemplate - > subid , false ) ;
2013-02-04 13:29:59 +03:00
2023-04-05 13:08:41 +02:00
event . creatures . resize ( 7 ) ;
for ( int i = 0 ; i < 7 ; + + i )
event . creatures [ i ] = reader - > readUInt16 ( ) ;
2023-04-02 18:56:10 +02:00
reader - > skipZero ( 4 ) ;
2023-04-05 13:08:41 +02:00
object - > events . push_back ( event ) ;
2013-02-04 13:29:59 +03:00
}
2023-04-11 23:36:01 +02:00
if ( features . levelSOD )
2013-02-04 13:29:59 +03:00
{
2023-04-11 23:36:01 +02:00
object - > alignmentToPlayer = PlayerColor : : NEUTRAL ; // "same as owner or random"
uint8_t alignment = reader - > readUInt8 ( ) ;
if ( alignment ! = PlayerColor : : NEUTRAL . getNum ( ) )
{
if ( alignment < PlayerColor : : PLAYER_LIMIT . getNum ( ) )
{
if ( mapHeader - > players [ alignment ] . canAnyonePlay ( ) )
object - > alignmentToPlayer = PlayerColor ( alignment ) ;
else
logGlobal - > warn ( " %s - Aligment of town at %s is invalid! Player %d is not present on map! " , mapName , position . toString ( ) , int ( alignment ) ) ;
}
else
{
// TODO: HOTA support
uint8_t invertedAlignment = alignment - PlayerColor : : PLAYER_LIMIT . getNum ( ) ;
if ( invertedAlignment < PlayerColor : : PLAYER_LIMIT . getNum ( ) )
{
logGlobal - > warn ( " %s - Aligment of town at %s 'not as player %d' is not implemented! " , mapName , position . toString ( ) , alignment - PlayerColor : : PLAYER_LIMIT . getNum ( ) ) ;
}
else
{
logGlobal - > warn ( " %s - Aligment of town at %s is corrupted!! " , mapName , position . toString ( ) ) ;
}
}
}
2013-02-04 13:29:59 +03:00
}
2023-04-02 18:56:10 +02:00
reader - > skipZero ( 3 ) ;
2013-02-04 13:29:59 +03:00
2023-04-05 13:08:41 +02:00
return object ;
2013-02-04 13:29:59 +03:00
}
2023-02-11 18:30:06 +02:00
std : : set < BuildingID > CMapLoaderH3M : : convertBuildings ( const std : : set < BuildingID > & h3m , int castleID , bool addAuxiliary ) const
2013-02-04 13:29:59 +03:00
{
2023-03-25 12:17:49 +02:00
std : : map < int , BuildingID > helperMap ;
2013-02-11 22:11:34 +03:00
std : : set < BuildingID > ret ;
2013-02-04 13:29:59 +03:00
// Note: this file is parsed many times.
2023-04-02 18:56:10 +02:00
static const JsonNode config ( ResourceID ( " config/buildings5.json " ) ) ;
2013-02-04 13:29:59 +03:00
2013-06-29 16:05:48 +03:00
for ( const JsonNode & entry : config [ " table " ] . Vector ( ) )
2013-02-04 13:29:59 +03:00
{
2020-10-01 10:38:06 +02:00
int town = static_cast < int > ( entry [ " town " ] . Float ( ) ) ;
2013-02-04 13:29:59 +03:00
2023-04-05 13:08:41 +02:00
if ( town = = castleID | | town = = - 1 )
2013-02-04 13:29:59 +03:00
{
2023-03-25 12:17:49 +02:00
helperMap [ static_cast < int > ( entry [ " h3 " ] . Float ( ) ) ] = BuildingID ( static_cast < si32 > ( entry [ " vcmi " ] . Float ( ) ) ) ;
2013-02-04 13:29:59 +03:00
}
}
2023-02-11 18:30:06 +02:00
for ( const auto & elem : h3m )
2013-02-04 13:29:59 +03:00
{
2023-03-25 12:17:49 +02:00
if ( helperMap [ elem ] > = BuildingID : : FIRST_REGULAR_ID )
2013-02-04 13:29:59 +03:00
{
2023-03-25 12:17:49 +02:00
ret . insert ( helperMap [ elem ] ) ;
2013-02-04 13:29:59 +03:00
}
2023-03-25 12:17:49 +02:00
// horde buildings use indexes from -1 to -5, where creature level is 1 to 5
else if ( helperMap [ elem ] > = ( - GameConstants : : CREATURES_PER_TOWN ) )
2013-02-04 13:29:59 +03:00
{
2023-03-25 12:17:49 +02:00
int level = ( helperMap [ elem ] ) ;
2013-02-04 13:29:59 +03:00
2023-03-25 12:17:49 +02:00
//(-30)..(-36) - horde buildings (for game loading only)
//They will be replaced in CGameState::initTowns()
ret . insert ( BuildingID ( level + BuildingID : : HORDE_BUILDING_CONVERTER ) ) ; //-1 => -30
2013-02-04 13:29:59 +03:00
}
else
{
2017-08-11 19:03:05 +02:00
logGlobal - > warn ( " Conversion warning: unknown building %d in castle %d " , elem . num , castleID ) ;
2013-02-04 13:29:59 +03:00
}
}
if ( addAuxiliary )
{
//village hall is always present
2013-02-11 02:24:57 +03:00
ret . insert ( BuildingID : : VILLAGE_HALL ) ;
2013-02-04 13:29:59 +03:00
2019-06-04 22:29:07 +02:00
if ( ret . find ( BuildingID : : CITY_HALL ) ! = ret . end ( ) )
{
ret . insert ( BuildingID : : EXTRA_CITY_HALL ) ;
}
if ( ret . find ( BuildingID : : TOWN_HALL ) ! = ret . end ( ) )
{
ret . insert ( BuildingID : : EXTRA_TOWN_HALL ) ;
}
if ( ret . find ( BuildingID : : CAPITOL ) ! = ret . end ( ) )
{
ret . insert ( BuildingID : : EXTRA_CAPITOL ) ;
}
2013-02-04 13:29:59 +03:00
}
return ret ;
}
void CMapLoaderH3M : : readEvents ( )
{
2023-04-05 13:08:41 +02:00
uint32_t eventsCount = reader - > readUInt32 ( ) ;
for ( int eventID = 0 ; eventID < eventsCount ; + + eventID )
2013-02-04 13:29:59 +03:00
{
2023-04-05 13:08:41 +02:00
CMapEvent event ;
event . name = readBasicString ( ) ;
event . message = readLocalizedString ( TextIdentifier ( " event " , eventID , " description " ) ) ;
2013-02-04 13:29:59 +03:00
2023-04-05 13:08:41 +02:00
reader - > readResourses ( event . resources ) ;
event . players = reader - > readUInt8 ( ) ;
2023-04-02 18:56:10 +02:00
if ( features . levelSOD )
2013-02-04 13:29:59 +03:00
{
2023-04-05 13:08:41 +02:00
event . humanAffected = reader - > readBool ( ) ;
2013-02-04 13:29:59 +03:00
}
else
{
2023-04-05 13:08:41 +02:00
event . humanAffected = true ;
2013-02-04 13:29:59 +03:00
}
2023-04-05 13:08:41 +02:00
event . computerAffected = reader - > readBool ( ) ;
event . firstOccurence = reader - > readUInt16 ( ) ;
event . nextOccurence = reader - > readUInt8 ( ) ;
2013-02-04 13:29:59 +03:00
2023-04-02 18:56:10 +02:00
reader - > skipZero ( 17 ) ;
2013-02-04 13:29:59 +03:00
2023-04-05 13:08:41 +02:00
map - > events . push_back ( event ) ;
2013-02-04 13:29:59 +03:00
}
}
2023-04-05 13:08:41 +02:00
void CMapLoaderH3M : : readMessageAndGuards ( std : : string & message , CCreatureSet * guards , const int3 & position )
2013-02-05 00:58:42 +03:00
{
2023-02-24 16:15:45 +02:00
bool hasMessage = reader - > readBool ( ) ;
2013-02-07 20:02:15 +03:00
if ( hasMessage )
2013-02-05 00:58:42 +03:00
{
2023-02-25 17:44:15 +02:00
message = readLocalizedString ( TextIdentifier ( " guards " , position . x , position . y , position . z , " message " ) ) ;
2023-02-24 16:15:45 +02:00
bool hasGuards = reader - > readBool ( ) ;
2013-02-07 20:02:15 +03:00
if ( hasGuards )
readCreatureSet ( guards , 7 ) ;
2013-02-05 00:58:42 +03:00
2023-04-02 18:56:10 +02:00
reader - > skipZero ( 4 ) ;
2013-02-04 13:29:59 +03:00
}
}
2016-11-13 12:38:42 +02:00
2023-04-05 13:08:41 +02:00
void CMapLoaderH3M : : readSpells ( std : : set < SpellID > & dest )
2023-02-24 16:15:45 +02:00
{
2023-04-05 13:08:41 +02:00
reader - > readBitmask ( dest , features . spellsBytes , features . spellsCount , false ) ;
2023-02-24 16:15:45 +02:00
}
2023-02-25 17:44:15 +02:00
std : : string CMapLoaderH3M : : readBasicString ( )
2023-02-24 16:15:45 +02:00
{
2023-02-25 01:18:15 +02:00
return TextOperations : : toUnicode ( reader - > readBaseString ( ) , fileEncoding ) ;
2023-02-24 16:15:45 +02:00
}
2023-02-25 17:44:15 +02:00
std : : string CMapLoaderH3M : : readLocalizedString ( const TextIdentifier & stringIdentifier )
{
std : : string mapString = TextOperations : : toUnicode ( reader - > readBaseString ( ) , fileEncoding ) ;
2023-03-14 17:54:37 +02:00
TextIdentifier fullIdentifier ( " map " , mapName , stringIdentifier . get ( ) ) ;
2023-02-25 17:44:15 +02:00
2023-04-05 13:08:41 +02:00
if ( mapString . empty ( ) )
2023-02-25 17:44:15 +02:00
return " " ;
VLC - > generaltexth - > registerString ( modName , fullIdentifier , mapString ) ;
return VLC - > generaltexth - > translate ( fullIdentifier . get ( ) ) ;
}
2016-11-13 12:38:42 +02:00
void CMapLoaderH3M : : afterRead ( )
{
2023-02-24 16:15:45 +02:00
//convert main town positions for all players to actual object position, in H3M it is position of active tile
2016-11-13 12:38:42 +02:00
2023-02-24 16:15:45 +02:00
for ( auto & p : map - > players )
2016-11-13 12:38:42 +02:00
{
int3 posOfMainTown = p . posOfMainTown ;
if ( posOfMainTown . valid ( ) & & map - > isInTheMap ( posOfMainTown ) )
{
const TerrainTile & t = map - > getTile ( posOfMainTown ) ;
const CGObjectInstance * mainTown = nullptr ;
2023-02-11 18:30:06 +02:00
for ( auto * obj : t . visitableObjects )
2016-11-13 12:38:42 +02:00
{
2017-05-29 11:39:08 +02:00
if ( obj - > ID = = Obj : : TOWN | | obj - > ID = = Obj : : RANDOM_TOWN )
2016-11-13 12:38:42 +02:00
{
mainTown = obj ;
break ;
}
}
if ( mainTown = = nullptr )
continue ;
p . posOfMainTown = posOfMainTown + mainTown - > getVisitableOffset ( ) ;
}
}
}
2022-07-26 15:07:42 +02:00
VCMI_LIB_NAMESPACE_END