1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-06-04 23:17:41 +02:00

Merge pull request #5422 from IvanSavenko/disposed_heroes_fix

[1.7] Do not allow heroes banned for player as starting heroes
This commit is contained in:
Ivan Savenko 2025-02-20 16:33:04 +02:00 committed by GitHub
commit b2642bb7d7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 67 additions and 43 deletions

View File

@ -432,6 +432,12 @@ OptionsTab::SelectionWindow::SelectionWindow(const PlayerColor & color, SelType
unusableHeroes.insert(player.second.hero); unusableHeroes.insert(player.second.hero);
} }
for (const auto & disposedHero : SEL->getMapInfo()->mapHeader->disposedHeroes)
{
if (!disposedHero.players.count(color))
allowedHeroes.erase(disposedHero.heroId);
}
allowedBonus.push_back(PlayerStartingBonus::RANDOM); allowedBonus.push_back(PlayerStartingBonus::RANDOM);
if(initialHero != HeroTypeID::NONE|| SEL->getPlayerInfo(color).heroesNames.size() > 0) if(initialHero != HeroTypeID::NONE|| SEL->getPlayerInfo(color).heroesNames.size() > 0)
@ -459,8 +465,10 @@ std::tuple<int, int> OptionsTab::SelectionWindow::calcLines(FactionID faction)
for(auto & elemh : allowedHeroes) for(auto & elemh : allowedHeroes)
{ {
const CHero * type = elemh.toHeroType(); const CHero * type = elemh.toHeroType();
if(type->heroClass->faction == faction) if(type->heroClass->faction != faction)
count++; continue;
count++;
} }
return std::make_tuple( return std::make_tuple(

View File

@ -39,11 +39,6 @@ void Rumor::serializeJson(JsonSerializeFormat & handler)
handler.serializeStruct("text", text); handler.serializeStruct("text", text);
} }
DisposedHero::DisposedHero() : heroId(0), portrait(255)
{
}
CMapEvent::CMapEvent() CMapEvent::CMapEvent()
: humanAffected(false) : humanAffected(false)
, computerAffected(false) , computerAffected(false)

View File

@ -56,26 +56,6 @@ struct DLL_LINKAGE Rumor
void serializeJson(JsonSerializeFormat & handler); void serializeJson(JsonSerializeFormat & handler);
}; };
/// The disposed hero struct describes which hero can be hired from which player.
struct DLL_LINKAGE DisposedHero
{
DisposedHero();
HeroTypeID heroId;
HeroTypeID portrait; /// The portrait id of the hero, -1 is default.
std::string name;
std::set<PlayerColor> players; /// Who can hire this hero (bitfield).
template <typename Handler>
void serialize(Handler & h)
{
h & heroId;
h & portrait;
h & name;
h & players;
}
};
/// The map contains the map header, the tiles of the terrain, objects, heroes, towns, rumors... /// The map contains the map header, the tiles of the terrain, objects, heroes, towns, rumors...
class DLL_LINKAGE CMap : public CMapHeader, public GameCallbackHolder class DLL_LINKAGE CMap : public CMapHeader, public GameCallbackHolder
{ {
@ -138,7 +118,6 @@ public:
void reindexObjects(); void reindexObjects();
std::vector<Rumor> rumors; std::vector<Rumor> rumors;
std::vector<DisposedHero> disposedHeroes;
std::vector<ConstTransitivePtr<CGHeroInstance> > predefinedHeroes; std::vector<ConstTransitivePtr<CGHeroInstance> > predefinedHeroes;
std::set<SpellID> allowedSpells; std::set<SpellID> allowedSpells;
std::set<ArtifactID> allowedArtifact; std::set<ArtifactID> allowedArtifact;

View File

@ -202,6 +202,24 @@ enum class EMapDifficulty : uint8_t
IMPOSSIBLE = 4 IMPOSSIBLE = 4
}; };
/// The disposed hero struct describes which hero can be hired from which player.
struct DLL_LINKAGE DisposedHero
{
HeroTypeID heroId;
HeroTypeID portrait; /// The portrait id of the hero, -1 is default.
std::string name;
std::set<PlayerColor> players; /// Who can hire this hero (bitfield).
template <typename Handler>
void serialize(Handler & h)
{
h & heroId;
h & portrait;
h & name;
h & players;
}
};
/// The map header holds information about loss/victory condition,map format, version, players, height, width,... /// The map header holds information about loss/victory condition,map format, version, players, height, width,...
class DLL_LINKAGE CMapHeader: public Serializeable class DLL_LINKAGE CMapHeader: public Serializeable
{ {
@ -248,6 +266,8 @@ public:
std::set<HeroTypeID> allowedHeroes; std::set<HeroTypeID> allowedHeroes;
std::set<HeroTypeID> reservedCampaignHeroes; /// Heroes that have placeholders in this map and are reserved for campaign std::set<HeroTypeID> reservedCampaignHeroes; /// Heroes that have placeholders in this map and are reserved for campaign
std::vector<DisposedHero> disposedHeroes;
bool areAnyPlayers; /// Unused. True if there are any playable players on the map. bool areAnyPlayers; /// Unused. True if there are any playable players on the map.
/// "main quests" of the map that describe victory and loss conditions /// "main quests" of the map that describe victory and loss conditions
@ -298,6 +318,8 @@ public:
h & victoryIconIndex; h & victoryIconIndex;
h & defeatMessage; h & defeatMessage;
h & defeatIconIndex; h & defeatIconIndex;
if (h.version >= Handler::Version::MAP_HEADER_DISPOSED_HEROES)
h & disposedHeroes;
h & translations; h & translations;
if(!h.saving) if(!h.saving)
registerMapStrings(); registerMapStrings();

View File

@ -95,7 +95,6 @@ void CMapLoaderH3M::init()
inputStream->seek(0); inputStream->seek(0);
readHeader(); readHeader();
readDisposedHeroes();
readMapOptions(); readMapOptions();
readAllowedArtifacts(); readAllowedArtifacts();
readAllowedSpellsAbilities(); readAllowedSpellsAbilities();
@ -248,6 +247,7 @@ void CMapLoaderH3M::readHeader()
readVictoryLossConditions(); readVictoryLossConditions();
readTeamInfo(); readTeamInfo();
readAllowedHeroes(); readAllowedHeroes();
readDisposedHeroes();
} }
void CMapLoaderH3M::readPlayerInfo() void CMapLoaderH3M::readPlayerInfo()
@ -728,13 +728,13 @@ void CMapLoaderH3M::readDisposedHeroes()
if(features.levelSOD) if(features.levelSOD)
{ {
size_t disp = reader->readUInt8(); size_t disp = reader->readUInt8();
map->disposedHeroes.resize(disp); mapHeader->disposedHeroes.resize(disp);
for(size_t g = 0; g < disp; ++g) for(size_t g = 0; g < disp; ++g)
{ {
map->disposedHeroes[g].heroId = reader->readHero(); mapHeader->disposedHeroes[g].heroId = reader->readHero();
map->disposedHeroes[g].portrait = reader->readHeroPortrait(); mapHeader->disposedHeroes[g].portrait = reader->readHeroPortrait();
map->disposedHeroes[g].name = readLocalizedString(TextIdentifier("header", "heroes", map->disposedHeroes[g].heroId.getNum())); mapHeader->disposedHeroes[g].name = readLocalizedString(TextIdentifier("header", "heroes", mapHeader->disposedHeroes[g].heroId.getNum()));
reader->readBitmaskPlayers(map->disposedHeroes[g].players, false); reader->readBitmaskPlayers(mapHeader->disposedHeroes[g].players, false);
} }
} }
} }

View File

@ -640,19 +640,19 @@ void CMapFormatJson::readDisposedHeroes(JsonSerializeFormat & handler)
hero.players = mask; hero.players = mask;
//name and portrait are not used //name and portrait are not used
map->disposedHeroes.push_back(hero); mapHeader->disposedHeroes.push_back(hero);
} }
} }
} }
void CMapFormatJson::writeDisposedHeroes(JsonSerializeFormat & handler) void CMapFormatJson::writeDisposedHeroes(JsonSerializeFormat & handler)
{ {
if(map->disposedHeroes.empty()) if(mapHeader->disposedHeroes.empty())
return; return;
auto definitions = handler.enterStruct("predefinedHeroes");//DisposedHeroes are part of predefinedHeroes in VCMI map format auto definitions = handler.enterStruct("predefinedHeroes");//DisposedHeroes are part of predefinedHeroes in VCMI map format
for(DisposedHero & hero : map->disposedHeroes) for(DisposedHero & hero : mapHeader->disposedHeroes)
{ {
std::string type = HeroTypeID::encode(hero.heroId.getNum()); std::string type = HeroTypeID::encode(hero.heroId.getNum());

View File

@ -70,6 +70,10 @@ enum class ESerializationVersion : int32_t
REWARDABLE_GUARDS, // 871 - fix missing serialization of guards in rewardable objects REWARDABLE_GUARDS, // 871 - fix missing serialization of guards in rewardable objects
MARKET_TRANSLATION_FIX, // 872 - remove serialization of markets translateable strings MARKET_TRANSLATION_FIX, // 872 - remove serialization of markets translateable strings
EVENT_OBJECTS_DELETION, //873 - allow events to remove map objects EVENT_OBJECTS_DELETION, //873 - allow events to remove map objects
RELEASE_160 = 873,
MAP_HEADER_DISPOSED_HEROES, // map header contains disposed heroes list
CURRENT = EVENT_OBJECTS_DELETION CURRENT = MAP_HEADER_DISPOSED_HEROES
}; };

View File

@ -974,10 +974,26 @@ void CVCMIServer::optionSetBonus(PlayerColor player, PlayerStartingBonus id)
bool CVCMIServer::canUseThisHero(PlayerColor player, HeroTypeID ID) bool CVCMIServer::canUseThisHero(PlayerColor player, HeroTypeID ID)
{ {
return VLC->heroh->size() > ID if (!ID.hasValue())
&& si->playerInfos[player].castle == VLC->heroh->objects[ID]->heroClass->faction return false;
&& !vstd::contains(getUsedHeroes(), ID)
&& mi->mapHeader->allowedHeroes.count(ID); if (ID >= VLC->heroh->size())
return false;
if (si->playerInfos[player].castle != VLC->heroh->objects[ID]->heroClass->faction)
return false;
if (vstd::contains(getUsedHeroes(), ID))
return false;
if (!mi->mapHeader->allowedHeroes.count(ID))
return false;
for (const auto & disposedHero : mi->mapHeader->disposedHeroes)
if (disposedHero.heroId == ID && !disposedHero.players.count(player))
return false;
return true;
} }
std::vector<HeroTypeID> CVCMIServer::getUsedHeroes() std::vector<HeroTypeID> CVCMIServer::getUsedHeroes()

View File

@ -180,12 +180,12 @@ void MapComparer::compareHeader()
boost::sort (expectedEvents, sortByIdentifier); boost::sort (expectedEvents, sortByIdentifier);
checkEqual(actualEvents, expectedEvents); checkEqual(actualEvents, expectedEvents);
checkEqual(actual->disposedHeroes, expected->disposedHeroes);
} }
void MapComparer::compareOptions() void MapComparer::compareOptions()
{ {
checkEqual(actual->rumors, expected->rumors); checkEqual(actual->rumors, expected->rumors);
checkEqual(actual->disposedHeroes, expected->disposedHeroes);
//todo: compareOptions predefinedHeroes //todo: compareOptions predefinedHeroes
checkEqual(actual->allowedAbilities, expected->allowedAbilities); checkEqual(actual->allowedAbilities, expected->allowedAbilities);