1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-07-13 01:20:34 +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

@ -78,7 +78,7 @@ void CArmedInstance::updateMoraleBonusFromArmy()
const CStackInstance * inst = slot.second;
const auto * creature = inst->getCreatureID().toEntity(VLC);
factions.insert(creature->getFaction());
factions.insert(creature->getFactionID());
// Check for undead flag instead of faction (undead mummies are neutral)
if (!hasUndead)
{

View File

@ -45,7 +45,7 @@ std::string CGCreature::getHoverText(PlayerColor player) const
else
ms.appendLocalString(EMetaText::ARRAY_TXT, quantityTextIndex);
ms.appendRawString(" ");
ms.appendNamePlural(getCreature());
ms.appendNamePlural(getCreatureID());
return ms.toString();
}
@ -57,7 +57,7 @@ std::string CGCreature::getHoverText(const CGHeroInstance * hero) const
MetaString ms;
ms.appendNumber(stacks.begin()->second->count);
ms.appendRawString(" ");
ms.appendName(getCreature(), stacks.begin()->second->count);
ms.appendName(getCreatureID(), stacks.begin()->second->count);
return ms.toString();
}
else
@ -69,11 +69,11 @@ std::string CGCreature::getHoverText(const CGHeroInstance * hero) const
std::string CGCreature::getMonsterLevelText() const
{
std::string monsterLevel = VLC->generaltexth->translate("vcmi.adventureMap.monsterLevel");
bool isRanged = VLC->creatures()->getById(getCreature())->getBonusBearer()->hasBonusOfType(BonusType::SHOOTER);
bool isRanged = getCreature()->getBonusBearer()->hasBonusOfType(BonusType::SHOOTER);
std::string attackTypeKey = isRanged ? "vcmi.adventureMap.monsterRangedType" : "vcmi.adventureMap.monsterMeleeType";
std::string attackType = VLC->generaltexth->translate(attackTypeKey);
boost::replace_first(monsterLevel, "%TOWN", (*VLC->townh)[VLC->creatures()->getById(getCreature())->getFaction()]->getNameTranslated());
boost::replace_first(monsterLevel, "%LEVEL", std::to_string(VLC->creatures()->getById(getCreature())->getLevel()));
boost::replace_first(monsterLevel, "%TOWN", getCreature()->getFactionID().toEntity(VLC)->getNameTranslated());
boost::replace_first(monsterLevel, "%LEVEL", std::to_string(getCreature()->getLevel()));
boost::replace_first(monsterLevel, "%ATTACK_TYPE", attackType);
return monsterLevel;
}
@ -150,7 +150,7 @@ std::string CGCreature::getPopupText(PlayerColor player) const
std::vector<Component> CGCreature::getPopupComponents(PlayerColor player) const
{
return {
Component(ComponentType::CREATURE, getCreature())
Component(ComponentType::CREATURE, getCreatureID())
};
}
@ -182,7 +182,7 @@ void CGCreature::onHeroVisit( const CGHeroInstance * h ) const
BlockingDialog ynd(true,false);
ynd.player = h->tempOwner;
ynd.text.appendLocalString(EMetaText::ADVOB_TXT, 86);
ynd.text.replaceName(getCreature(), getStackCount(SlotID(0)));
ynd.text.replaceName(getCreatureID(), getStackCount(SlotID(0)));
cb->showBlockingDialog(this, &ynd);
break;
}
@ -197,7 +197,7 @@ void CGCreature::onHeroVisit( const CGHeroInstance * h ) const
std::string tmp = VLC->generaltexth->advobtxt[90];
boost::algorithm::replace_first(tmp, "%d", std::to_string(getStackCount(SlotID(0))));
boost::algorithm::replace_first(tmp, "%d", std::to_string(action));
boost::algorithm::replace_first(tmp,"%s",VLC->creatures()->getById(getCreature())->getNamePluralTranslated());
boost::algorithm::replace_first(tmp,"%s",getCreature()->getNamePluralTranslated());
ynd.text.appendRawString(tmp);
cb->showBlockingDialog(this, &ynd);
break;
@ -205,11 +205,16 @@ void CGCreature::onHeroVisit( const CGHeroInstance * h ) const
}
}
CreatureID CGCreature::getCreature() const
CreatureID CGCreature::getCreatureID() const
{
return CreatureID(getObjTypeIndex().getNum());
}
const CCreature * CGCreature::getCreature() const
{
return getCreatureID().toCreature();
}
void CGCreature::pickRandomObject(vstd::RNG & rand)
{
switch(ID.toEnum())
@ -279,7 +284,7 @@ void CGCreature::initObj(vstd::RNG & rand)
stacks[SlotID(0)]->setType(getCreature());
TQuantity &amount = stacks[SlotID(0)]->count;
const Creature * c = VLC->creatures()->getById(getCreature());
const Creature * c = getCreature();
if(amount == 0)
{
amount = rand.nextInt(c->getAdvMapAmountMin(), c->getAdvMapAmountMax());
@ -353,8 +358,8 @@ int CGCreature::takenAction(const CGHeroInstance *h, bool allowJoin) const
for(const auto & elem : h->Slots())
{
bool isOurUpgrade = vstd::contains(getCreature().toCreature()->upgrades, elem.second->getCreatureID());
bool isOurDowngrade = vstd::contains(elem.second->type->upgrades, getCreature());
bool isOurUpgrade = vstd::contains(getCreature()->upgrades, elem.second->getCreatureID());
bool isOurDowngrade = vstd::contains(elem.second->type->upgrades, getCreatureID());
if(isOurUpgrade || isOurDowngrade)
count += elem.second->count;
@ -380,7 +385,7 @@ int CGCreature::takenAction(const CGHeroInstance *h, bool allowJoin) const
if(diplomacy * 2 + sympathy + 1 >= character)
{
int32_t recruitCost = VLC->creatures()->getById(getCreature())->getRecruitCost(EGameResID::GOLD);
int32_t recruitCost = getCreature()->getRecruitCost(EGameResID::GOLD);
int32_t stackCount = getStackCount(SlotID(0));
return recruitCost * stackCount; //join for gold
}
@ -493,7 +498,7 @@ void CGCreature::flee( const CGHeroInstance * h ) const
BlockingDialog ynd(true,false);
ynd.player = h->tempOwner;
ynd.text.appendLocalString(EMetaText::ADVOB_TXT,91);
ynd.text.replaceName(getCreature(), getStackCount(SlotID(0)));
ynd.text.replaceName(getCreatureID(), getStackCount(SlotID(0)));
cb->showBlockingDialog(this, &ynd);
}
@ -513,7 +518,7 @@ void CGCreature::battleFinished(const CGHeroInstance *hero, const BattleResult &
{
//merge stacks into one
TSlots::const_iterator i;
const CCreature * cre = getCreature().toCreature();
const CCreature * cre = getCreature();
for(i = stacks.begin(); i != stacks.end(); i++)
{
if(cre->isMyUpgrade(i->second->type))

View File

@ -49,7 +49,8 @@ public:
void newTurn(vstd::RNG & rand) const override;
void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override;
void blockingDialogAnswered(const CGHeroInstance *hero, int32_t answer) const override;
CreatureID getCreature() const;
CreatureID getCreatureID() const;
const CCreature * getCreature() const;
//stack formation depends on position,
bool containsUpgradedStack() const;

View File

@ -93,7 +93,7 @@ FactionID CGDwelling::randomizeFaction(vstd::RNG & rand)
assert(linkedTown->ID == Obj::TOWN);
if(linkedTown->ID==Obj::TOWN)
return linkedTown->getFaction();
return linkedTown->getFactionID();
}
if(!randomizationInfo->allowedFactions.empty())

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;

View File

@ -72,7 +72,6 @@ public:
//////////////////////////////////////////////////////////////////////////
const CHero * type;
TExpType exp; //experience points
ui32 level; //current level of hero
@ -171,7 +170,7 @@ public:
const IOwnableObject * asOwnable() const final;
//INativeTerrainProvider
FactionID getFaction() const override;
FactionID getFactionID() const override;
TerrainId getNativeTerrain() const override;
int getLowestCreatureSpeed() const;
si32 manaRegain() const; //how many points of mana can hero regain "naturally" in one day
@ -235,7 +234,11 @@ public:
//////////////////////////////////////////////////////////////////////////
HeroTypeID getHeroType() const;
const CHeroClass * getHeroClass() const;
HeroClassID getHeroClassID() const;
const CHero * getHeroType() const;
HeroTypeID getHeroTypeID() const;
void setHeroType(HeroTypeID type);
void initHero(vstd::RNG & rand);
@ -352,7 +355,11 @@ public:
h & skillsInfo;
h & visitedTown;
h & boat;
h & type;
if (h.version < Handler::Version::REMOVE_TOWN_PTR)
{
CHero * type = nullptr;
h & type;
}
h & commander;
h & visitedObjects;
BONUS_TREE_DESERIALIZATION_FIX

View File

@ -45,7 +45,7 @@ int CGTownInstance::getSightRadius() const //returns sight distance
for(const auto & bid : builtBuildings)
{
auto height = town->buildings.at(bid)->height;
auto height = getTown()->buildings.at(bid)->height;
if(ret < height)
ret = height;
}
@ -115,7 +115,7 @@ int CGTownInstance::mageGuildLevel() const
int CGTownInstance::getHordeLevel(const int & HID) const//HID - 0 or 1; returns creature level or -1 if that horde structure is not present
{
return town->hordeLvl.at(HID);
return getTown()->hordeLvl.at(HID);
}
int CGTownInstance::creatureGrowth(const int & level) const
@ -127,7 +127,7 @@ GrowthInfo CGTownInstance::getGrowthInfo(int level) const
{
GrowthInfo ret;
if (level<0 || level >=town->creatures.size())
if (level<0 || level >=getTown()->creatures.size())
return ret;
if (creatures[level].second.empty())
return ret; //no dwelling
@ -151,11 +151,11 @@ GrowthInfo CGTownInstance::getGrowthInfo(int level) const
else if (hasBuilt(BuildingID::CITADEL))
ret.entries.emplace_back(subID, BuildingID::CITADEL, castleBonus = base / 2);
if(town->hordeLvl.at(0) == level)//horde 1
if(getTown()->hordeLvl.at(0) == level)//horde 1
if(hasBuilt(BuildingID::HORDE_1))
ret.entries.emplace_back(subID, BuildingID::HORDE_1, creature->getHorde());
if(town->hordeLvl.at(1) == level)//horde 2
if(getTown()->hordeLvl.at(1) == level)//horde 2
if(hasBuilt(BuildingID::HORDE_2))
ret.entries.emplace_back(subID, BuildingID::HORDE_2, creature->getHorde());
@ -209,11 +209,11 @@ int CGTownInstance::getDwellingBonus(const std::vector<CreatureID>& creatureIds,
TResources CGTownInstance::dailyIncome() const
{
TResources ret;
for(const auto & p : town->buildings)
for(const auto & p : getTown()->buildings)
{
BuildingID buildingUpgrade;
for(const auto & p2 : town->buildings)
for(const auto & p2 : getTown()->buildings)
{
if (p2.second->upgrade == p.first)
{
@ -251,10 +251,10 @@ bool CGTownInstance::hasCapitol() const
TownFortifications CGTownInstance::fortificationsLevel() const
{
auto result = town->fortifications;
auto result = getTown()->fortifications;
for (auto const & buildingID : builtBuildings)
result += town->buildings.at(buildingID)->fortifications;
result += getTown()->buildings.at(buildingID)->fortifications;
if (result.wallsHealth == 0)
return TownFortifications();
@ -264,7 +264,6 @@ TownFortifications CGTownInstance::fortificationsLevel() const
CGTownInstance::CGTownInstance(IGameCallback *cb):
CGDwelling(cb),
town(nullptr),
built(0),
destroyed(0),
identifier(0),
@ -379,17 +378,17 @@ void CGTownInstance::onHeroLeave(const CGHeroInstance * h) const
std::string CGTownInstance::getObjectName() const
{
return getNameTranslated() + ", " + town->faction->getNameTranslated();
return getNameTranslated() + ", " + getTown()->faction->getNameTranslated();
}
bool CGTownInstance::townEnvisagesBuilding(BuildingSubID::EBuildingSubID subId) const
{
return town->getBuildingType(subId) != BuildingID::NONE;
return getTown()->getBuildingType(subId) != BuildingID::NONE;
}
void CGTownInstance::initializeConfigurableBuildings(vstd::RNG & rand)
{
for(const auto & kvp : town->buildings)
for(const auto & kvp : getTown()->buildings)
{
if(!kvp.second->rewardableObjectInfo.getParameters().isNull())
rewardableBuildings[kvp.first] = new TownRewardableBuildingInstance(this, kvp.second->bid, rand);
@ -457,8 +456,7 @@ void CGTownInstance::pickRandomObject(vstd::RNG & rand)
assert(ID == Obj::TOWN); // just in case
setType(ID, subID);
town = (*VLC->townh)[getFaction()]->town;
randomizeArmy(getFaction());
randomizeArmy(getFactionID());
updateAppearance();
}
@ -467,19 +465,19 @@ void CGTownInstance::initObj(vstd::RNG & rand) ///initialize town structures
blockVisit = true;
if(townEnvisagesBuilding(BuildingSubID::PORTAL_OF_SUMMONING)) //Dungeon for example
creatures.resize(town->creatures.size() + 1);
creatures.resize(getTown()->creatures.size() + 1);
else
creatures.resize(town->creatures.size());
creatures.resize(getTown()->creatures.size());
for (int level = 0; level < town->creatures.size(); level++)
for (int level = 0; level < getTown()->creatures.size(); level++)
{
BuildingID buildID = BuildingID(BuildingID::getDwellingFromLevel(level, 0));
int upgradeNum = 0;
for (; town->buildings.count(buildID); upgradeNum++, BuildingID::advanceDwelling(buildID))
for (; getTown()->buildings.count(buildID); upgradeNum++, BuildingID::advanceDwelling(buildID))
{
if (hasBuilt(buildID) && town->creatures.at(level).size() > upgradeNum)
creatures[level].second.push_back(town->creatures[level][upgradeNum]);
if (hasBuilt(buildID) && getTown()->creatures.at(level).size() > upgradeNum)
creatures[level].second.push_back(getTown()->creatures[level][upgradeNum]);
}
}
initializeConfigurableBuildings(rand);
@ -623,9 +621,9 @@ void CGTownInstance::removeCapitols(const PlayerColor & owner) const
if (hasCapitol()) // search if there's an older capitol
{
PlayerState* state = cb->gameState()->getPlayerState(owner); //get all towns owned by player
for (const auto & town : state->getTowns())
for (const auto & otherTown : state->getTowns())
{
if (town != this && town->hasCapitol())
if (otherTown != this && otherTown->hasCapitol())
{
RazeStructures rs;
rs.tid = id;
@ -648,7 +646,7 @@ void CGTownInstance::clearArmy() const
BoatId CGTownInstance::getBoatType() const
{
return town->faction->boatType;
return getTown()->faction->boatType;
}
int CGTownInstance::getMarketEfficiency() const
@ -703,7 +701,7 @@ void CGTownInstance::updateAppearance()
std::string CGTownInstance::nodeName() const
{
return "Town (" + (town ? town->faction->getNameTranslated() : "unknown") + ") of " + getNameTranslated();
return "Town (" + getTown()->faction->getNameTranslated() + ") of " + getNameTranslated();
}
void CGTownInstance::deserializationFix()
@ -752,7 +750,7 @@ void CGTownInstance::recreateBuildingsBonuses()
for(const auto & upgradeID : builtBuildings)
{
const auto & upgrade = town->buildings.at(upgradeID);
const auto & upgrade = getTown()->buildings.at(upgradeID);
if (upgrade->getBase() == bid && upgrade->upgradeReplacesBonuses)
bonusesReplacedByUpgrade = true;
}
@ -761,7 +759,7 @@ void CGTownInstance::recreateBuildingsBonuses()
if (bonusesReplacedByUpgrade)
continue;
auto building = town->buildings.at(bid);
auto building = getTown()->buildings.at(bid);
if(building->buildingBonuses.empty())
continue;
@ -828,21 +826,6 @@ bool CGTownInstance::armedGarrison() const
return !stacks.empty() || garrisonHero;
}
const CTown * CGTownInstance::getTown() const
{
if(ID == Obj::RANDOM_TOWN)
return VLC->townh->randomTown;
else
{
if(nullptr == town)
{
return (*VLC->townh)[getFaction()]->town;
}
else
return town;
}
}
int CGTownInstance::getTownLevel() const
{
// count all buildings that are not upgrades
@ -850,7 +833,7 @@ int CGTownInstance::getTownLevel() const
for(const auto & bid : builtBuildings)
{
if(town->buildings.at(bid)->upgrade == BuildingID::NONE)
if(getTown()->buildings.at(bid)->upgrade == BuildingID::NONE)
level++;
}
return level;
@ -892,7 +875,7 @@ bool CGTownInstance::hasBuilt(BuildingSubID::EBuildingSubID buildingID) const
{
for(const auto & bid : builtBuildings)
{
if(town->buildings.at(bid)->subId == buildingID)
if(getTown()->buildings.at(bid)->subId == buildingID)
return true;
}
return false;
@ -905,7 +888,7 @@ bool CGTownInstance::hasBuilt(const BuildingID & buildingID) const
bool CGTownInstance::hasBuilt(const BuildingID & buildingID, FactionID townID) const
{
if (townID == town->faction->getId() || townID == FactionID::ANY)
if (townID == getTown()->faction->getId() || townID == FactionID::ANY)
return hasBuilt(buildingID);
return false;
}
@ -923,7 +906,7 @@ std::set<EMarketMode> CGTownInstance::availableModes() const
std::set<EMarketMode> result;
for (const auto & buildingID : builtBuildings)
{
const auto * buildingPtr = town->buildings.at(buildingID).get();
const auto * buildingPtr = getTown()->buildings.at(buildingID).get();
result.insert(buildingPtr->marketModes.begin(), buildingPtr->marketModes.end());
}
@ -950,8 +933,8 @@ std::set<BuildingID> CGTownInstance::getBuildings() const
TResources CGTownInstance::getBuildingCost(const BuildingID & buildingID) const
{
if (vstd::contains(town->buildings, buildingID))
return town->buildings.at(buildingID)->resources;
if (vstd::contains(getTown()->buildings, buildingID))
return getTown()->buildings.at(buildingID)->resources;
else
{
logGlobal->error("Town %s at %s has no possible building %d!", getNameTranslated(), anchorPos().toString(), buildingID.toEnum());
@ -962,7 +945,7 @@ TResources CGTownInstance::getBuildingCost(const BuildingID & buildingID) const
CBuilding::TRequired CGTownInstance::genBuildingRequirements(const BuildingID & buildID, bool deep) const
{
const CBuilding * building = town->buildings.at(buildID);
const CBuilding * building = getTown()->buildings.at(buildID);
//TODO: find better solution to prevent infinite loops
std::set<BuildingID> processed;
@ -970,13 +953,13 @@ CBuilding::TRequired CGTownInstance::genBuildingRequirements(const BuildingID &
std::function<CBuilding::TRequired::Variant(const BuildingID &)> dependTest =
[&](const BuildingID & id) -> CBuilding::TRequired::Variant
{
if (town->buildings.count(id) == 0)
if (getTown()->buildings.count(id) == 0)
{
logMod->error("Invalid building ID %d in building dependencies!", id.getNum());
return CBuilding::TRequired::OperatorAll();
}
const CBuilding * build = town->buildings.at(id);
const CBuilding * build = getTown()->buildings.at(id);
CBuilding::TRequired::OperatorAll requirements;
if (!hasBuilt(id))
@ -1001,7 +984,7 @@ CBuilding::TRequired CGTownInstance::genBuildingRequirements(const BuildingID &
CBuilding::TRequired::OperatorAll requirements;
if (building->upgrade != BuildingID::NONE)
{
const CBuilding * upgr = town->buildings.at(building->upgrade);
const CBuilding * upgr = getTown()->buildings.at(building->upgrade);
requirements.expressions.push_back(dependTest(upgr->bid));
processed.clear();
@ -1151,14 +1134,27 @@ void CGTownInstance::serializeJsonOptions(JsonSerializeFormat & handler)
}
}
FactionID CGTownInstance::getFaction() const
const CFaction * CGTownInstance::getFaction() const
{
return FactionID(subID.getNum());
return getFactionID().toFaction();
}
const CTown * CGTownInstance::getTown() const
{
if(ID == Obj::RANDOM_TOWN)
return VLC->townh->randomTown;
return getFaction()->town;
}
FactionID CGTownInstance::getFactionID() const
{
return FactionID(subID.getNum());
}
TerrainId CGTownInstance::getNativeTerrain() const
{
return town->faction->getNativeTerrain();
return getTown()->faction->getNativeTerrain();
}
ArtifactID CGTownInstance::getWarMachineInBuilding(BuildingID building) const
@ -1166,21 +1162,21 @@ ArtifactID CGTownInstance::getWarMachineInBuilding(BuildingID building) const
if (builtBuildings.count(building) == 0)
return ArtifactID::NONE;
if (building == BuildingID::BLACKSMITH && town->warMachineDeprecated.hasValue())
return town->warMachineDeprecated.toCreature()->warMachine;
if (building == BuildingID::BLACKSMITH && getTown()->warMachineDeprecated.hasValue())
return getTown()->warMachineDeprecated.toCreature()->warMachine;
return town->buildings.at(building)->warMachine;
return getTown()->buildings.at(building)->warMachine;
}
bool CGTownInstance::isWarMachineAvailable(ArtifactID warMachine) const
{
for (auto const & buildingID : builtBuildings)
if (town->buildings.at(buildingID)->warMachine == warMachine)
if (getTown()->buildings.at(buildingID)->warMachine == warMachine)
return true;
if (builtBuildings.count(BuildingID::BLACKSMITH) &&
town->warMachineDeprecated.hasValue() &&
town->warMachineDeprecated.toCreature()->warMachine == warMachine)
getTown()->warMachineDeprecated.hasValue() &&
getTown()->warMachineDeprecated.toCreature()->warMachine == warMachine)
return true;
return false;
@ -1200,7 +1196,7 @@ GrowthInfo::Entry::Entry(int subID, const BuildingID & building, int _count): co
{
MetaString formatter;
formatter.appendRawString("%s %+d");
formatter.replaceRawString((*VLC->townh)[subID]->town->buildings.at(building)->getNameTranslated());
formatter.replaceRawString(FactionID(subID).toFaction()->town->buildings.at(building)->getNameTranslated());
formatter.replacePositiveNumber(count);
description = formatter.toString();

View File

@ -50,6 +50,7 @@ struct DLL_LINKAGE GrowthInfo
class DLL_LINKAGE CGTownInstance : public CGDwelling, public IShipyard, public IMarket, public INativeTerrainProvider, public ICreatureUpgrader
{
friend class CTownInstanceConstructor;
std::string nameTextId; // name of town
std::map<BuildingID, TownRewardableBuildingInstance*> convertOldBuildings(std::vector<TownRewardableBuildingInstance*> oldVector);
@ -59,7 +60,6 @@ public:
enum EFortLevel {NONE = 0, FORT = 1, CITADEL = 2, CASTLE = 3};
CTownAndVisitingHero townAndVis;
const CTown * town;
si32 built; //how many buildings has been built this turn
si32 destroyed; //how many buildings has been destroyed this turn
ConstTransitivePtr<CGHeroInstance> garrisonHero, visitingHero;
@ -112,16 +112,21 @@ public:
rewardableBuildings = convertOldBuildings(oldVector);
}
if (h.saving)
if (h.version < Handler::Version::REMOVE_TOWN_PTR)
{
CFaction * faction = town ? town->faction : nullptr;
h & faction;
}
else
{
CFaction * faction = nullptr;
h & faction;
town = faction ? faction->town : nullptr;
CTown * town = nullptr;
if (h.saving)
{
CFaction * faction = town ? town->faction : nullptr;
h & faction;
}
else
{
CFaction * faction = nullptr;
h & faction;
town = faction ? faction->town : nullptr;
}
}
h & townAndVis;
@ -213,9 +218,10 @@ public:
DamageRange getKeepDamageRange() const;
const CTown * getTown() const;
const CFaction * getFaction() const;
/// INativeTerrainProvider
FactionID getFaction() const override;
FactionID getFactionID() const override;
TerrainId getNativeTerrain() const override;
/// Returns ID of war machine that is produced by specified building or NONE if this is not built or if building does not produce war machines

View File

@ -431,7 +431,7 @@ void CGSeerHut::setObjToKill()
if(getCreatureToKill(true))
{
quest->stackToKill = getCreatureToKill(false)->getCreature();
quest->stackToKill = getCreatureToKill(false)->getCreatureID();
assert(quest->stackToKill != CreatureID::NONE);
quest->stackDirection = checkDirection();
}

View File

@ -73,14 +73,14 @@ TownRewardableBuildingInstance::TownRewardableBuildingInstance(CGTownInstance *
void TownRewardableBuildingInstance::initObj(vstd::RNG & rand)
{
assert(town && town->town);
assert(town && town->getTown());
configuration = generateConfiguration(rand);
}
Rewardable::Configuration TownRewardableBuildingInstance::generateConfiguration(vstd::RNG & rand) const
{
Rewardable::Configuration result;
auto building = town->town->buildings.at(getBuildingType());
auto building = town->getTown()->buildings.at(getBuildingType());
building->rewardableObjectInfo.configureObject(result, rand, cb);
for(auto & rewardInfo : result.info)