1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-02-03 13:01:33 +02:00

Added loading of reserved heroes from H3M

This commit is contained in:
Ivan Savenko 2023-06-25 17:47:45 +03:00
parent 2b7e554807
commit 2882e2d248
4 changed files with 39 additions and 9 deletions

View File

@ -123,7 +123,7 @@ CrossoverHeroesList CGameStateCampaign::getCrossoverHeroesFromPreviousScenarios(
return crossoverHeroes;
}
void CGameStateCampaign::prepareCrossoverHeroes(std::vector<CampaignHeroReplacement> & campaignHeroReplacements, const CScenarioTravel & travelOptions)
void CGameStateCampaign::trimCrossoverHeroesParameters(std::vector<CampaignHeroReplacement> & campaignHeroReplacements, const CScenarioTravel & travelOptions)
{
// create heroes list for convenience iterating
std::vector<CGHeroInstance *> crossoverHeroes;
@ -133,7 +133,6 @@ void CGameStateCampaign::prepareCrossoverHeroes(std::vector<CampaignHeroReplacem
crossoverHeroes.push_back(campaignHeroReplacement.hero);
}
// TODO replace magic numbers with named constants
// TODO this logic (what should be kept) should be part of CScenarioTravel and be exposed via some clean set of methods
if(!travelOptions.whatHeroKeeps.experience)
{
@ -235,6 +234,23 @@ void CGameStateCampaign::prepareCrossoverHeroes(std::vector<CampaignHeroReplacem
void CGameStateCampaign::placeCampaignHeroes()
{
// WARNING: CURRENT CODE IS LIKELY INCORRECT AND LEADS TO MULTIPLE ISSUES WITH HERO TRANSFER
// Approximate behavior according to testing H3 game logic.
// 1) definitions:
// - 'reserved heroes' are heroes that have fixed placeholder in unfinished maps. See CMapHeader::reservedCampaignHeroes
// - 'campaign pool' are serialized heroes and is unique to a campaign
// - 'scenario pool' are serialized heroes and is unique to a scenario
//
// 2) scenario end logic:
// - at end of scenario, all 'reserved heroes' of a player go to 'campaign pool'
// - at end of scenario, rest of player's heroes go to 'scenario pool'
//
// 3) scenario start logic
// - at scenario start, all heroes that are placed on map but already exist in 'campaign pool' and are still 'reserved heroes' are replaced with other, randomly selected heroes (and probably marked as unavailable in map)
// - at scenario start, all fixed placeholders are replaced with heroes from 'campaign pool', if exist
// - at scenario start, all power placeholders owned by player are replaced by heroes from 'scenario pool' of last complete map, if exist
// - exception: if starting bonus is 'select player' then power placeholders are taken from 'scenario pool' of linked map
// place bonus hero
auto campaignBonus = gameState->scenarioOps->campState->getBonusForCurrentMap();
bool campaignGiveHero = campaignBonus && campaignBonus->type == CScenarioTravel::STravelBonus::HERO;
@ -264,7 +280,7 @@ void CGameStateCampaign::placeCampaignHeroes()
auto campaignHeroReplacements = generateCampaignHeroesToReplace(crossoverHeroes);
logGlobal->debug("\tPrepare crossover heroes");
prepareCrossoverHeroes(campaignHeroReplacements, gameState->scenarioOps->campState->getCurrentScenario().travelOptions);
trimCrossoverHeroesParameters(campaignHeroReplacements, gameState->scenarioOps->campState->getCurrentScenario().travelOptions);
// remove same heroes on the map which will be added through crossover heroes
// INFO: we will remove heroes because later it may be possible that the API doesn't allow having heroes

View File

@ -42,8 +42,8 @@ class CGameStateCampaign
/// returns heroes and placeholders in where heroes will be put
std::vector<CampaignHeroReplacement> generateCampaignHeroesToReplace(CrossoverHeroesList & crossoverHeroes);
/// gets prepared and copied hero instances with crossover heroes from prev. scenario and travel options from current scenario
void prepareCrossoverHeroes(std::vector<CampaignHeroReplacement> & campaignHeroReplacements, const CScenarioTravel & travelOptions);
/// Trims hero parameters that should not transfer between scenarios according to travelOptions flags
void trimCrossoverHeroesParameters(std::vector<CampaignHeroReplacement> & campaignHeroReplacements, const CScenarioTravel & travelOptions);
void replaceHeroesPlaceholders(const std::vector<CampaignHeroReplacement> & campaignHeroReplacements);

View File

@ -238,6 +238,7 @@ public:
std::vector<PlayerInfo> players; /// The default size of the vector is PlayerColor::PLAYER_LIMIT.
ui8 howManyTeams;
std::vector<bool> allowedHeroes;
std::vector<HeroTypeID> reservedCampaignHeroes; /// Heroes that have placeholders in this map and are reserverd for campaign
bool areAnyPlayers; /// Unused. True if there are any playable players on the map.
@ -261,6 +262,7 @@ public:
h & players;
h & howManyTeams;
h & allowedHeroes;
h & reservedCampaignHeroes;
//Do not serialize triggeredEvents in header as they can contain information about heroes and armies
h & victoryMessage;
h & victoryIconIndex;

View File

@ -668,11 +668,15 @@ void CMapLoaderH3M::readAllowedHeroes()
else
reader->readBitmaskHeroes(mapHeader->allowedHeroes, false);
//TODO: unknown value. Check meaning? Only present in campaign maps.
if(features.levelAB)
{
uint32_t placeholdersQty = reader->readUInt32();
reader->skipUnused(placeholdersQty * 1);
for (uint32_t i = 0; i < placeholdersQty; ++i)
{
auto heroID = reader->readHero();
mapHeader->reservedCampaignHeroes.push_back(heroID);
}
}
}
@ -861,6 +865,8 @@ void CMapLoaderH3M::readPredefinedHeroes()
}
}
map->predefinedHeroes.emplace_back(hero);
logGlobal->debug("Map '%s': Hero predefined in map: %s", mapName, VLC->heroh->getByIndex(hero->subID)->getJsonKey());
}
}
@ -1288,12 +1294,12 @@ CGObjectInstance * CMapLoaderH3M::readHeroPlaceholder(const int3 & mapPosition)
if(htid.getNum() == -1)
{
object->power = reader->readUInt8();
logGlobal->debug("Hero placeholder: by power at %s", mapPosition.toString());
logGlobal->debug("Map '%s': Hero placeholder: by power at %s, owned by %s", mapName, mapPosition.toString(), object->getOwner().getStr());
}
else
{
object->power = 0;
logGlobal->debug("Hero placeholder: %s at %s", VLC->heroh->getById(htid)->getNameTranslated(), mapPosition.toString());
logGlobal->debug("Map '%s': Hero placeholder: %s at %s, owned by %s", mapName, VLC->heroh->getById(htid)->getJsonKey(), mapPosition.toString(), object->getOwner().getStr());
}
return object;
@ -1765,6 +1771,12 @@ CGObjectInstance * CMapLoaderH3M::readHero(const int3 & mapPosition, const Objec
}
}
}
if (object->subID != -1)
logGlobal->debug("Map '%s': Hero on map: %s at %s, owned by %s", mapName, VLC->heroh->getByIndex(object->subID)->getJsonKey(), mapPosition.toString(), object->getOwner().getStr());
else
logGlobal->debug("Map '%s': Hero on map: (random) at %s, owned by %s", mapName, mapPosition.toString(), object->getOwner().getStr());
reader->skipZero(16);
return object;
}