1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-06-15 00:05:02 +02:00

Reduce usage of pointers to VLC entities

Final goal (of multiple PR's) is to remove all remaining pointers from
serializeable game state, and replace them with either identifiers or
with shared/unique pointers.

CGTownInstance::town and CGHeroInstance::type members have been removed.
Now this data is computed dynamically using subID member.

VLC entity of a town can now be accessed via following methods:
- getFactionID() returns ID of a faction
- getFaction() returns pointer to a faction
- getTown() returns pointer to a town

VLC entity of a hero can now be accessed via following methods:
- getHeroTypeID() returns ID of a hero
- getHeroClassID() returns ID of a hero class
- getHeroType() returns pointer to a hero
- getHeroClass() returns pointer to a hero class
This commit is contained in:
Ivan Savenko
2024-10-05 19:37:52 +00:00
parent 81f0222c68
commit 3dd4fa2528
83 changed files with 445 additions and 468 deletions

View File

@ -116,9 +116,9 @@ ui32 CGHeroInstance::getTileMovementCost(const TerrainTile & dest, const Terrain
return static_cast<ui32>(ret);
}
FactionID CGHeroInstance::getFaction() const
FactionID CGHeroInstance::getFactionID() const
{
return FactionID(type->heroClass->faction);
return getHeroClass()->faction;
}
const IBonusBearer* CGHeroInstance::getBonusBearer() const
@ -229,10 +229,10 @@ bool CGHeroInstance::canLearnSkill(const SecondarySkill & which) const
if (getSecSkillLevel(which) > 0)
return false;
if (type->heroClass->secSkillProbability.count(which) == 0)
if (getHeroClass()->secSkillProbability.count(which) == 0)
return false;
if (type->heroClass->secSkillProbability.at(which) == 0)
if (getHeroClass()->secSkillProbability.at(which) == 0)
return false;
return true;
@ -282,7 +282,6 @@ int CGHeroInstance::movementPointsLimitCached(bool onLand, const TurnInfo * ti)
CGHeroInstance::CGHeroInstance(IGameCallback * cb)
: CArmedInstance(cb),
type(nullptr),
tacticFormationEnabled(false),
inTownGarrison(false),
moveDir(4),
@ -303,14 +302,30 @@ PlayerColor CGHeroInstance::getOwner() const
return tempOwner;
}
HeroTypeID CGHeroInstance::getHeroType() const
const CHeroClass * CGHeroInstance::getHeroClass() const
{
return getHeroType()->heroClass;
}
HeroClassID CGHeroInstance::getHeroClassID() const
{
return getHeroType()->heroClass->getId();
}
const CHero * CGHeroInstance::getHeroType() const
{
return getHeroTypeID().toHeroType();
}
HeroTypeID CGHeroInstance::getHeroTypeID() const
{
if (ID == Obj::RANDOM_HERO)
return HeroTypeID::NONE;
return HeroTypeID(getObjTypeIndex().getNum());
}
void CGHeroInstance::setHeroType(HeroTypeID heroType)
{
assert(type == nullptr);
subID = heroType;
}
@ -323,16 +338,14 @@ void CGHeroInstance::initHero(vstd::RNG & rand, const HeroTypeID & SUBID)
void CGHeroInstance::initHero(vstd::RNG & rand)
{
assert(validTypes(true));
if(!type)
type = getHeroType().toHeroType();
if (ID == Obj::HERO)
appearance = VLC->objtypeh->getHandlerFor(Obj::HERO, type->heroClass->getIndex())->getTemplates().front();
appearance = VLC->objtypeh->getHandlerFor(Obj::HERO, getHeroClass()->getIndex())->getTemplates().front();
if(!vstd::contains(spells, SpellID::PRESET))
{
// hero starts with default spells
for(const auto & spellID : type->spells)
for(const auto & spellID : getHeroType()->spells)
spells.insert(spellID);
}
else //remove placeholder
@ -341,7 +354,7 @@ void CGHeroInstance::initHero(vstd::RNG & rand)
if(!vstd::contains(spells, SpellID::SPELLBOOK_PRESET))
{
// hero starts with default spellbook presence status
if(!getArt(ArtifactPosition::SPELLBOOK) && type->haveSpellBook)
if(!getArt(ArtifactPosition::SPELLBOOK) && getHeroType()->haveSpellBook)
{
auto artifact = ArtifactUtils::createArtifact(ArtifactID::SPELLBOOK);
putArtifact(ArtifactPosition::SPELLBOOK, artifact);
@ -360,14 +373,14 @@ void CGHeroInstance::initHero(vstd::RNG & rand)
{
for(int g=0; g<GameConstants::PRIMARY_SKILLS; ++g)
{
pushPrimSkill(static_cast<PrimarySkill>(g), type->heroClass->primarySkillInitial[g]);
pushPrimSkill(static_cast<PrimarySkill>(g), getHeroClass()->primarySkillInitial[g]);
}
}
if(secSkills.size() == 1 && secSkills[0] == std::pair<SecondarySkill,ui8>(SecondarySkill::NONE, -1)) //set secondary skills to default
secSkills = type->secSkillsInit;
secSkills = getHeroType()->secSkillsInit;
if (gender == EHeroGender::DEFAULT)
gender = type->gender;
gender = getHeroType()->gender;
setFormation(EArmyFormation::LOOSE);
if (!stacksCount()) //standard army//initial army
@ -403,9 +416,9 @@ void CGHeroInstance::initHero(vstd::RNG & rand)
addNewBonus(bonus);
}
if (cb->getSettings().getBoolean(EGameSettings::MODULE_COMMANDERS) && !commander && type->heroClass->commander.hasValue())
if (cb->getSettings().getBoolean(EGameSettings::MODULE_COMMANDERS) && !commander && getHeroClass()->commander.hasValue())
{
commander = new CCommanderInstance(type->heroClass->commander);
commander = new CCommanderInstance(getHeroClass()->commander);
commander->setArmyObj (castToArmyObj()); //TODO: separate function for setting commanders
commander->giveStackExp (exp); //after our exp is set
}
@ -413,7 +426,7 @@ void CGHeroInstance::initHero(vstd::RNG & rand)
skillsInfo = SecondarySkillsInfo();
//copy active (probably growing) bonuses from hero prototype to hero object
for(const std::shared_ptr<Bonus> & b : type->specialty)
for(const std::shared_ptr<Bonus> & b : getHeroType()->specialty)
addNewBonus(b);
//initialize bonuses
@ -433,14 +446,14 @@ void CGHeroInstance::initArmy(vstd::RNG & rand, IArmyDescriptor * dst)
auto stacksCountChances = cb->getSettings().getVector(EGameSettings::HEROES_STARTING_STACKS_CHANCES);
int stacksCountInitRandomNumber = rand.nextInt(1, 100);
size_t maxStacksCount = std::min(stacksCountChances.size(), type->initialArmy.size());
size_t maxStacksCount = std::min(stacksCountChances.size(), getHeroType()->initialArmy.size());
for(int stackNo=0; stackNo < maxStacksCount; stackNo++)
{
if (stacksCountInitRandomNumber > stacksCountChances[stackNo])
continue;
auto & stack = type->initialArmy[stackNo];
auto & stack = getHeroType()->initialArmy[stackNo];
int count = rand.nextInt(stack.minAmount, stack.maxAmount);
@ -588,11 +601,11 @@ std::string CGHeroInstance::getMovementPointsTextIfOwner(PlayerColor player) con
ui8 CGHeroInstance::maxlevelsToMagicSchool() const
{
return type->heroClass->isMagicHero() ? 3 : 4;
return getHeroClass()->isMagicHero() ? 3 : 4;
}
ui8 CGHeroInstance::maxlevelsToWisdom() const
{
return type->heroClass->isMagicHero() ? 3 : 6;
return getHeroClass()->isMagicHero() ? 3 : 6;
}
CGHeroInstance::SecondarySkillsInfo::SecondarySkillsInfo():
@ -617,11 +630,8 @@ void CGHeroInstance::pickRandomObject(vstd::RNG & rand)
{
ID = Obj::HERO;
subID = cb->gameState()->pickNextHeroType(getOwner());
type = getHeroType().toHeroType();
randomizeArmy(type->heroClass->faction);
randomizeArmy(getHeroClass()->faction);
}
else
type = getHeroType().toHeroType();
auto oldSubID = subID;
@ -629,7 +639,7 @@ void CGHeroInstance::pickRandomObject(vstd::RNG & rand)
// after setType subID used to store unique hero identify id. Check issue 2277 for details
// exclude prisons which should use appearance as set in map, via map editor or RMG
if (ID != Obj::PRISON)
setType(ID, type->heroClass->getIndex());
setType(ID, getHeroClass()->getIndex());
this->subID = oldSubID;
}
@ -1041,7 +1051,7 @@ si32 CGHeroInstance::getManaNewTurn() const
BoatId CGHeroInstance::getBoatType() const
{
return BoatId(VLC->townh->getById(type->heroClass->faction)->getBoatType());
return BoatId(VLC->townh->getById(getHeroClass()->faction)->getBoatType());
}
void CGHeroInstance::getOutOffsets(std::vector<int3> &offsets) const
@ -1080,7 +1090,7 @@ void CGHeroInstance::pushPrimSkill( PrimarySkill which, int val )
EAlignment CGHeroInstance::getAlignment() const
{
return type->heroClass->getAlignment();
return getHeroClass()->getAlignment();
}
void CGHeroInstance::initExp(vstd::RNG & rand)
@ -1104,12 +1114,12 @@ HeroTypeID CGHeroInstance::getPortraitSource() const
if (customPortraitSource.isValid())
return customPortraitSource;
else
return getHeroType();
return getHeroTypeID();
}
int32_t CGHeroInstance::getIconIndex() const
{
return VLC->heroTypes()->getById(getPortraitSource())->getIconIndex();
return getPortraitSource().toEntity(VLC)->getIconIndex();
}
std::string CGHeroInstance::getNameTranslated() const
@ -1126,15 +1136,15 @@ std::string CGHeroInstance::getClassNameTextID() const
{
if (isCampaignGem())
return "core.genrltxt.735";
return type->heroClass->getNameTextID();
return getHeroClass()->getNameTextID();
}
std::string CGHeroInstance::getNameTextID() const
{
if (!nameCustomTextId.empty())
return nameCustomTextId;
if (type)
return type->getNameTextID();
if (getHeroTypeID().hasValue())
return getHeroType()->getNameTextID();
// FIXME: called by logging from some specialties (mods?) before type is set on deserialization
// assert(0);
@ -1150,8 +1160,8 @@ std::string CGHeroInstance::getBiographyTextID() const
{
if (!biographyCustomTextId.empty())
return biographyCustomTextId;
if (type)
return type->getBiographyTextID();
if (getHeroTypeID().hasValue())
return getHeroType()->getBiographyTextID();
return ""; //for random hero
}
@ -1349,11 +1359,11 @@ std::vector<SecondarySkill> CGHeroInstance::getLevelUpProposedSecondarySkills(vs
SecondarySkill selection;
if (selectWisdom)
selection = type->heroClass->chooseSecSkill(intersect(options, wisdomList), rand);
selection = getHeroClass()->chooseSecSkill(intersect(options, wisdomList), rand);
else if (selectSchool)
selection = type->heroClass->chooseSecSkill(intersect(options, schoolList), rand);
selection = getHeroClass()->chooseSecSkill(intersect(options, schoolList), rand);
else
selection = type->heroClass->chooseSecSkill(options, rand);
selection = getHeroClass()->chooseSecSkill(options, rand);
skills.push_back(selection);
options.erase(selection);
@ -1384,7 +1394,7 @@ PrimarySkill CGHeroInstance::nextPrimarySkill(vstd::RNG & rand) const
{
assert(gainsLevel());
const auto isLowLevelHero = level < GameConstants::HERO_HIGH_LEVEL;
const auto & skillChances = isLowLevelHero ? type->heroClass->primarySkillLowLevel : type->heroClass->primarySkillHighLevel;
const auto & skillChances = isLowLevelHero ? getHeroClass()->primarySkillLowLevel : getHeroClass()->primarySkillHighLevel;
if (isCampaignYog())
{
@ -1524,25 +1534,15 @@ bool CGHeroInstance::hasVisions(const CGObjectInstance * target, BonusSubtypeID
std::string CGHeroInstance::getHeroTypeName() const
{
if(ID == Obj::HERO || ID == Obj::PRISON)
{
if(type)
{
return type->getJsonKey();
}
else
{
return getHeroType().toEntity(VLC)->getJsonKey();
}
}
return getHeroType()->getJsonKey();
return "";
}
void CGHeroInstance::afterAddToMap(CMap * map)
{
if(ID != Obj::PRISON)
{
map->heroesOnMap.emplace_back(this);
}
}
void CGHeroInstance::afterRemoveFromMap(CMap* map)
{
@ -1729,8 +1729,7 @@ void CGHeroInstance::serializeJsonOptions(JsonSerializeFormat & handler)
if(!appearance)
{
// crossoverDeserialize
type = getHeroType().toHeroType();
appearance = VLC->objtypeh->getHandlerFor(Obj::HERO, type->heroClass->getIndex())->getTemplates().front();
appearance = VLC->objtypeh->getHandlerFor(Obj::HERO, getHeroClassID())->getTemplates().front();
}
}
@ -1817,7 +1816,7 @@ bool CGHeroInstance::isCampaignYog() const
if (!boost::starts_with(campaign, "DATA/YOG")) // "Birth of a Barbarian"
return false;
if (getHeroType() != HeroTypeID::SOLMYR) // Yog (based on Solmyr)
if (getHeroTypeID() != HeroTypeID::SOLMYR) // Yog (based on Solmyr)
return false;
return true;
@ -1835,7 +1834,7 @@ bool CGHeroInstance::isCampaignGem() const
if (!boost::starts_with(campaign, "DATA/GEM") && !boost::starts_with(campaign, "DATA/FINAL")) // "New Beginning" and "Unholy Alliance"
return false;
if (getHeroType() != HeroTypeID::GEM) // Yog (based on Solmyr)
if (getHeroTypeID() != HeroTypeID::GEM) // Yog (based on Solmyr)
return false;
return true;