1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-11-25 22:42:04 +02:00

Improvements to type safety of Identifier class

- Constructor of Identifier from integer is now explicit
- Lobby hero/town selection now uses Identifiers instead of int's
- Removed serialization workaround for hero portraits
- Added dummy objects for custom heroes portraits for ID resolver to use
- HeroInstance now stores portrait ID only in case of custom portrait
- Fixed loading of campaign heroes portraits on RoE maps
This commit is contained in:
Ivan Savenko
2023-09-28 19:43:04 +03:00
parent e322d0a084
commit 037efdf5fc
53 changed files with 693 additions and 464 deletions

View File

@@ -729,7 +729,7 @@ void CVCMIServer::updateStartInfoOnMapChange(std::shared_ptr<CMapInfo> mapInfo,
pset.castle = pinfo.defaultCastle();
pset.hero = pinfo.defaultHero();
if(pset.hero.getNum() != PlayerSettings::RANDOM && pinfo.hasCustomMainHero())
if(pset.hero != HeroTypeID::RANDOM && pinfo.hasCustomMainHero())
{
pset.hero = pinfo.mainCustomHeroId;
pset.heroNameTextId = pinfo.mainCustomHeroNameTextId;
@@ -839,13 +839,13 @@ void CVCMIServer::optionNextCastle(PlayerColor player, int dir)
{
PlayerSettings & s = si->playerInfos[player];
FactionID & cur = s.castle;
auto & allowed = getPlayerInfo(player.getNum()).allowedFactions;
const bool allowRandomTown = getPlayerInfo(player.getNum()).isFactionRandom;
auto & allowed = getPlayerInfo(player).allowedFactions;
const bool allowRandomTown = getPlayerInfo(player).isFactionRandom;
if(cur.getNum() == PlayerSettings::NONE) //no change
if(cur == FactionID::NONE) //no change
return;
if(cur.getNum() == PlayerSettings::RANDOM) //first/last available
if(cur == FactionID::RANDOM) //first/last available
{
if(dir > 0)
cur = *allowed.begin(); //id of first town
@@ -859,7 +859,7 @@ void CVCMIServer::optionNextCastle(PlayerColor player, int dir)
{
if(allowRandomTown)
{
cur = PlayerSettings::RANDOM;
cur = FactionID::RANDOM;
}
else
{
@@ -878,34 +878,34 @@ void CVCMIServer::optionNextCastle(PlayerColor player, int dir)
}
}
if(s.hero.getNum() >= 0 && !getPlayerInfo(player.getNum()).hasCustomMainHero()) // remove hero unless it set to fixed one in map editor
if(s.hero.isValid() && !getPlayerInfo(player).hasCustomMainHero()) // remove hero unless it set to fixed one in map editor
{
s.hero = PlayerSettings::RANDOM;
s.hero = HeroTypeID::RANDOM;
}
if(cur.getNum() < 0 && s.bonus == PlayerSettings::RESOURCE)
s.bonus = PlayerSettings::RANDOM;
if(!cur.isValid() && s.bonus == PlayerStartingBonus::RESOURCE)
s.bonus = PlayerStartingBonus::RANDOM;
}
void CVCMIServer::optionSetCastle(PlayerColor player, int id)
void CVCMIServer::optionSetCastle(PlayerColor player, FactionID id)
{
PlayerSettings & s = si->playerInfos[player];
FactionID & cur = s.castle;
auto & allowed = getPlayerInfo(player.getNum()).allowedFactions;
auto & allowed = getPlayerInfo(player).allowedFactions;
if(cur.getNum() == PlayerSettings::NONE) //no change
if(cur == FactionID::NONE) //no change
return;
if(allowed.find(static_cast<FactionID>(id)) == allowed.end() && id != PlayerSettings::RANDOM) // valid id
if(allowed.find(id) == allowed.end() && id != FactionID::RANDOM) // valid id
return;
cur = static_cast<FactionID>(id);
if(s.hero.getNum() >= 0 && !getPlayerInfo(player.getNum()).hasCustomMainHero()) // remove hero unless it set to fixed one in map editor
if(s.hero.isValid() && !getPlayerInfo(player).hasCustomMainHero()) // remove hero unless it set to fixed one in map editor
{
s.hero = PlayerSettings::RANDOM;
s.hero = HeroTypeID::RANDOM;
}
if(cur.getNum() < 0 && s.bonus == PlayerSettings::RESOURCE)
s.bonus = PlayerSettings::RANDOM;
if(!cur.isValid() && s.bonus == PlayerStartingBonus::RESOURCE)
s.bonus = PlayerStartingBonus::RANDOM;
}
void CVCMIServer::setCampaignMap(CampaignScenarioID mapId)
@@ -937,105 +937,105 @@ void CVCMIServer::setCampaignBonus(int bonusId)
void CVCMIServer::optionNextHero(PlayerColor player, int dir)
{
PlayerSettings & s = si->playerInfos[player];
if(s.castle.getNum() < 0 || s.hero.getNum() == PlayerSettings::NONE)
if(!s.castle.isValid() || s.hero == HeroTypeID::NONE)
return;
if(s.hero.getNum() == PlayerSettings::RANDOM) // first/last available
if(s.hero == HeroTypeID::RANDOM) // first/last available
{
int max = static_cast<int>(VLC->heroh->size()),
min = 0;
s.hero = nextAllowedHero(player, min, max, 0, dir);
if (dir > 0)
s.hero = nextAllowedHero(player, HeroTypeID(-1), dir);
else
s.hero = nextAllowedHero(player, HeroTypeID(VLC->heroh->size()), dir);
}
else
{
if(dir > 0)
s.hero = nextAllowedHero(player, s.hero, (int)VLC->heroh->size(), 1, dir);
else
s.hero = nextAllowedHero(player, -1, s.hero, 1, dir); // min needs to be -1 -- hero at index 0 would be skipped otherwise
s.hero = nextAllowedHero(player, s.hero, dir);
}
}
void CVCMIServer::optionSetHero(PlayerColor player, int id)
void CVCMIServer::optionSetHero(PlayerColor player, HeroTypeID id)
{
PlayerSettings & s = si->playerInfos[player];
if(s.castle.getNum() < 0 || s.hero.getNum() == PlayerSettings::NONE)
if(!s.castle.isValid() || s.hero == HeroTypeID::NONE)
return;
if(id == PlayerSettings::RANDOM)
if(id == HeroTypeID::RANDOM)
{
s.hero = PlayerSettings::RANDOM;
s.hero = HeroTypeID::RANDOM;
}
if(canUseThisHero(player, id))
s.hero = static_cast<HeroTypeID>(id);
}
HeroTypeID CVCMIServer::nextAllowedHero(PlayerColor player, int min, int max, int incl, int dir)
HeroTypeID CVCMIServer::nextAllowedHero(PlayerColor player, HeroTypeID initial, int direction)
{
if(dir > 0)
HeroTypeID first(initial.getNum() + direction);
if(direction > 0)
{
for(int i = min + incl; i <= max - incl; i++)
for (auto i = first; i.getNum() < VLC->heroh->size(); ++i)
if(canUseThisHero(player, i))
return HeroTypeID(i);
return i;
}
else
{
for(int i = max - incl; i >= min + incl; i--)
for (auto i = first; i.getNum() >= 0; --i)
if(canUseThisHero(player, i))
return HeroTypeID(i);
return i;
}
return -1;
return HeroTypeID::RANDOM;
}
void CVCMIServer::optionNextBonus(PlayerColor player, int dir)
{
PlayerSettings & s = si->playerInfos[player];
PlayerSettings::Ebonus & ret = s.bonus = static_cast<PlayerSettings::Ebonus>(static_cast<int>(s.bonus) + dir);
PlayerStartingBonus & ret = s.bonus = static_cast<PlayerStartingBonus>(static_cast<int>(s.bonus) + dir);
if(s.hero.getNum() == PlayerSettings::NONE &&
!getPlayerInfo(player.getNum()).heroesNames.size() &&
ret == PlayerSettings::ARTIFACT) //no hero - can't be artifact
if(s.hero == HeroTypeID::NONE &&
!getPlayerInfo(player).heroesNames.size() &&
ret == PlayerStartingBonus::ARTIFACT) //no hero - can't be artifact
{
if(dir < 0)
ret = PlayerSettings::RANDOM;
ret = PlayerStartingBonus::RANDOM;
else
ret = PlayerSettings::GOLD;
ret = PlayerStartingBonus::GOLD;
}
if(ret > PlayerSettings::RESOURCE)
ret = PlayerSettings::RANDOM;
if(ret < PlayerSettings::RANDOM)
ret = PlayerSettings::RESOURCE;
if(ret > PlayerStartingBonus::RESOURCE)
ret = PlayerStartingBonus::RANDOM;
if(ret < PlayerStartingBonus::RANDOM)
ret = PlayerStartingBonus::RESOURCE;
if(s.castle.getNum() == PlayerSettings::RANDOM && ret == PlayerSettings::RESOURCE) //random castle - can't be resource
if(s.castle == FactionID::RANDOM && ret == PlayerStartingBonus::RESOURCE) //random castle - can't be resource
{
if(dir < 0)
ret = PlayerSettings::GOLD;
ret = PlayerStartingBonus::GOLD;
else
ret = PlayerSettings::RANDOM;
ret = PlayerStartingBonus::RANDOM;
}
}
void CVCMIServer::optionSetBonus(PlayerColor player, int id)
void CVCMIServer::optionSetBonus(PlayerColor player, PlayerStartingBonus id)
{
PlayerSettings & s = si->playerInfos[player];
if(s.hero.getNum() == PlayerSettings::NONE &&
!getPlayerInfo(player.getNum()).heroesNames.size() &&
id == PlayerSettings::ARTIFACT) //no hero - can't be artifact
if(s.hero == HeroTypeID::NONE &&
!getPlayerInfo(player).heroesNames.size() &&
id == PlayerStartingBonus::ARTIFACT) //no hero - can't be artifact
return;
if(id > PlayerSettings::RESOURCE)
if(id > PlayerStartingBonus::RESOURCE)
return;
if(id < PlayerSettings::RANDOM)
if(id < PlayerStartingBonus::RANDOM)
return;
if(s.castle.getNum() == PlayerSettings::RANDOM && id == PlayerSettings::RESOURCE) //random castle - can't be resource
if(s.castle == FactionID::RANDOM && id == PlayerStartingBonus::RESOURCE) //random castle - can't be resource
return;
s.bonus = static_cast<PlayerSettings::Ebonus>(static_cast<int>(id));
s.bonus = id;;
}
bool CVCMIServer::canUseThisHero(PlayerColor player, int ID)
bool CVCMIServer::canUseThisHero(PlayerColor player, HeroTypeID ID)
{
return VLC->heroh->size() > ID
&& si->playerInfos[player].castle == VLC->heroh->objects[ID]->heroClass->faction
@@ -1043,17 +1043,17 @@ bool CVCMIServer::canUseThisHero(PlayerColor player, int ID)
&& mi->mapHeader->allowedHeroes[ID];
}
std::vector<int> CVCMIServer::getUsedHeroes()
std::vector<HeroTypeID> CVCMIServer::getUsedHeroes()
{
std::vector<int> heroIds;
std::vector<HeroTypeID> heroIds;
for(auto & p : si->playerInfos)
{
const auto & heroes = getPlayerInfo(p.first.getNum()).heroesNames;
const auto & heroes = getPlayerInfo(p.first).heroesNames;
for(auto & hero : heroes)
if(hero.heroId >= 0) //in VCMI map format heroId = -1 means random hero
heroIds.push_back(hero.heroId);
if(p.second.hero.getNum() != PlayerSettings::RANDOM)
if(p.second.hero != HeroTypeID::RANDOM)
heroIds.push_back(p.second.hero);
}
return heroIds;