1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-20 20:23:03 +02:00

PlayerState now stores all objects owned by player

This commit is contained in:
Ivan Savenko 2024-08-24 22:21:26 +00:00
parent 0fd9dbf240
commit a481f07daf
21 changed files with 184 additions and 113 deletions

View File

@ -237,7 +237,7 @@ void CGameInfoCallback::getThievesGuildInfo(SThievesGuildInfo & thi, const CGObj
if(obj->ID == Obj::TOWN || obj->ID == Obj::TAVERN) if(obj->ID == Obj::TOWN || obj->ID == Obj::TAVERN)
{ {
int taverns = 0; int taverns = 0;
for(auto town : gs->players[*getPlayerID()].towns) for(auto town : gs->players[*getPlayerID()].getTowns())
{ {
if(town->hasBuilt(BuildingID::TAVERN)) if(town->hasBuilt(BuildingID::TAVERN))
taverns++; taverns++;
@ -256,7 +256,7 @@ void CGameInfoCallback::getThievesGuildInfo(SThievesGuildInfo & thi, const CGObj
int CGameInfoCallback::howManyTowns(PlayerColor Player) const int CGameInfoCallback::howManyTowns(PlayerColor Player) const
{ {
ERROR_RET_VAL_IF(!hasAccess(Player), "Access forbidden!", -1); ERROR_RET_VAL_IF(!hasAccess(Player), "Access forbidden!", -1);
return static_cast<int>(gs->players[Player].towns.size()); return static_cast<int>(gs->players[Player].getTowns().size());
} }
bool CGameInfoCallback::getTownInfo(const CGObjectInstance * town, InfoAboutTown & dest, const CGObjectInstance * selectedObject) const bool CGameInfoCallback::getTownInfo(const CGObjectInstance * town, InfoAboutTown & dest, const CGObjectInstance * selectedObject) const
@ -609,7 +609,7 @@ EBuildingState CGameInfoCallback::canBuildStructure( const CGTownInstance *t, Bu
const PlayerState *ps = getPlayerState(t->tempOwner, false); const PlayerState *ps = getPlayerState(t->tempOwner, false);
if(ps) if(ps)
{ {
for(const CGTownInstance *town : ps->towns) for(const CGTownInstance *town : ps->getTowns())
{ {
if(town->hasBuilt(BuildingID::CAPITOL)) if(town->hasBuilt(BuildingID::CAPITOL))
{ {
@ -711,9 +711,9 @@ int CGameInfoCallback::getHeroCount( PlayerColor player, bool includeGarrisoned
ERROR_RET_VAL_IF(!p, "No such player!", -1); ERROR_RET_VAL_IF(!p, "No such player!", -1);
if(includeGarrisoned) if(includeGarrisoned)
return static_cast<int>(p->heroes.size()); return static_cast<int>(p->getHeroes().size());
else else
for(const auto & elem : p->heroes) for(const auto & elem : p->getHeroes())
if(!elem->inTownGarrison) if(!elem->inTownGarrison)
ret++; ret++;
return ret; return ret;
@ -757,7 +757,7 @@ std::vector < const CGTownInstance *> CPlayerSpecificInfoCallback::getTownsInfo(
auto ret = std::vector < const CGTownInstance *>(); auto ret = std::vector < const CGTownInstance *>();
for(const auto & i : gs->players) for(const auto & i : gs->players)
{ {
for(const auto & town : i.second.towns) for(const auto & town : i.second.getTowns())
{ {
if(i.first == getPlayerID() || (!onlyOur && isVisible(town, getPlayerID()))) if(i.first == getPlayerID() || (!onlyOur && isVisible(town, getPlayerID())))
{ {
@ -789,7 +789,7 @@ int CPlayerSpecificInfoCallback::getHeroSerial(const CGHeroInstance * hero, bool
return -1; return -1;
size_t index = 0; size_t index = 0;
auto & heroes = gs->players[*getPlayerID()].heroes; const auto & heroes = gs->players[*getPlayerID()].getHeroes();
for (auto & possibleHero : heroes) for (auto & possibleHero : heroes)
{ {
@ -835,7 +835,7 @@ std::vector < const CGDwelling * > CPlayerSpecificInfoCallback::getMyDwellings()
{ {
ASSERT_IF_CALLED_WITH_PLAYER ASSERT_IF_CALLED_WITH_PLAYER
std::vector < const CGDwelling * > ret; std::vector < const CGDwelling * > ret;
for(CGDwelling * dw : gs->getPlayerState(*getPlayerID())->dwellings) for(const CGDwelling * dw : gs->getPlayerState(*getPlayerID())->getDwellings())
{ {
ret.push_back(dw); ret.push_back(dw);
} }
@ -867,12 +867,12 @@ const CGHeroInstance* CPlayerSpecificInfoCallback::getHeroBySerial(int serialId,
if (!includeGarrisoned) if (!includeGarrisoned)
{ {
for(ui32 i = 0; i < p->heroes.size() && static_cast<int>(i) <= serialId; i++) for(ui32 i = 0; i < p->getHeroes().size() && static_cast<int>(i) <= serialId; i++)
if(p->heroes[i]->inTownGarrison) if(p->getHeroes()[i]->inTownGarrison)
serialId++; serialId++;
} }
ERROR_RET_VAL_IF(serialId < 0 || serialId >= p->heroes.size(), "No player info", nullptr); ERROR_RET_VAL_IF(serialId < 0 || serialId >= p->getHeroes().size(), "No player info", nullptr);
return p->heroes[serialId]; return p->getHeroes()[serialId];
} }
const CGTownInstance* CPlayerSpecificInfoCallback::getTownBySerial(int serialId) const const CGTownInstance* CPlayerSpecificInfoCallback::getTownBySerial(int serialId) const
@ -880,8 +880,8 @@ const CGTownInstance* CPlayerSpecificInfoCallback::getTownBySerial(int serialId)
ASSERT_IF_CALLED_WITH_PLAYER ASSERT_IF_CALLED_WITH_PLAYER
const PlayerState *p = getPlayerState(*getPlayerID()); const PlayerState *p = getPlayerState(*getPlayerID());
ERROR_RET_VAL_IF(!p, "No player info", nullptr); ERROR_RET_VAL_IF(!p, "No player info", nullptr);
ERROR_RET_VAL_IF(serialId < 0 || serialId >= p->towns.size(), "No player info", nullptr); ERROR_RET_VAL_IF(serialId < 0 || serialId >= p->getTowns().size(), "No player info", nullptr);
return p->towns[serialId]; return p->getTowns()[serialId];
} }
int CPlayerSpecificInfoCallback::getResourceAmount(GameResID type) const int CPlayerSpecificInfoCallback::getResourceAmount(GameResID type) const

View File

@ -23,7 +23,7 @@ struct InfoWindow;
struct PlayerSettings; struct PlayerSettings;
struct CPackForClient; struct CPackForClient;
struct TerrainTile; struct TerrainTile;
struct PlayerState; class PlayerState;
class CTown; class CTown;
struct StartInfo; struct StartInfo;
struct CPathsInfo; struct CPathsInfo;

View File

@ -10,6 +10,9 @@
#include "StdInc.h" #include "StdInc.h"
#include "CPlayerState.h" #include "CPlayerState.h"
#include "mapObjects/CGDwelling.h"
#include "mapObjects/CGTownInstance.h"
#include "mapObjects/CGHeroInstance.h"
#include "gameState/QuestInfo.h" #include "gameState/QuestInfo.h"
#include "texts/CGeneralTextHandler.h" #include "texts/CGeneralTextHandler.h"
#include "VCMI_Lib.h" #include "VCMI_Lib.h"
@ -90,4 +93,66 @@ int PlayerState::getResourceAmount(int type) const
return vstd::atOrDefault(resources, static_cast<size_t>(type), 0); return vstd::atOrDefault(resources, static_cast<size_t>(type), 0);
} }
std::vector<const CGDwelling* > PlayerState::getDwellings() const
{
std::vector<const CGDwelling* > result;
for (auto const & object : ownedObjects)
{
auto dwelling = dynamic_cast<const CGDwelling *>(object);
auto town = dynamic_cast<const CGTownInstance *>(object);
if (dwelling && !town)
result.push_back(dwelling);
}
return result;
}
template<typename T>
std::vector<T> PlayerState::getObjectsOfType() const
{
std::vector<T> result;
for (auto const & object : ownedObjects)
{
auto casted = dynamic_cast<T>(object);
if (casted)
result.push_back(casted);
}
return result;
}
std::vector<const CGHeroInstance *> PlayerState::getHeroes() const
{
return getObjectsOfType<const CGHeroInstance *>();
}
std::vector<const CGTownInstance *> PlayerState::getTowns() const
{
return getObjectsOfType<const CGTownInstance *>();
}
std::vector<CGHeroInstance *> PlayerState::getHeroes()
{
return getObjectsOfType<CGHeroInstance *>();
}
std::vector<CGTownInstance *> PlayerState::getTowns()
{
return getObjectsOfType<CGTownInstance *>();
}
std::vector<const CGObjectInstance *> PlayerState::getOwnedObjects() const
{
return {ownedObjects.begin(), ownedObjects.end()};
}
void PlayerState::addOwnedObject(CGObjectInstance * object)
{
ownedObjects.push_back(object);
}
void PlayerState::removeOwnedObject(CGObjectInstance * object)
{
vstd::erase(ownedObjects, object);
}
VCMI_LIB_NAMESPACE_END VCMI_LIB_NAMESPACE_END

View File

@ -20,12 +20,13 @@
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
class CGObjectInstance;
class CGHeroInstance; class CGHeroInstance;
class CGTownInstance; class CGTownInstance;
class CGDwelling; class CGDwelling;
struct QuestInfo; struct QuestInfo;
struct DLL_LINKAGE PlayerState : public CBonusSystemNode, public Player class DLL_LINKAGE PlayerState : public CBonusSystemNode, public Player
{ {
struct VisitedObjectGlobal struct VisitedObjectGlobal
{ {
@ -47,6 +48,11 @@ struct DLL_LINKAGE PlayerState : public CBonusSystemNode, public Player
} }
}; };
std::vector<CGObjectInstance*> ownedObjects;
template<typename T>
std::vector<T> getObjectsOfType() const;
public: public:
PlayerColor color; PlayerColor color;
bool human; //true if human controlled player, false for AI bool human; //true if human controlled player, false for AI
@ -55,12 +61,8 @@ public:
/// list of objects that were "destroyed" by player, either via simple pick-up (e.g. resources) or defeated heroes or wandering monsters /// list of objects that were "destroyed" by player, either via simple pick-up (e.g. resources) or defeated heroes or wandering monsters
std::set<ObjectInstanceID> destroyedObjects; std::set<ObjectInstanceID> destroyedObjects;
std::set<ObjectInstanceID> visitedObjects; // as a std::set, since most accesses here will be from visited status checks std::set<ObjectInstanceID> visitedObjects; // as a std::set, since most accesses here will be from visited status checks
std::set<VisitedObjectGlobal> visitedObjectsGlobal; std::set<VisitedObjectGlobal> visitedObjectsGlobal;
std::vector<ConstTransitivePtr<CGHeroInstance> > heroes;
std::vector<ConstTransitivePtr<CGTownInstance> > towns;
std::vector<ConstTransitivePtr<CGDwelling> > dwellings; //used for town growth
std::vector<QuestInfo> quests; //store info about all received quests std::vector<QuestInfo> quests; //store info about all received quests
std::vector<Bonus> battleBonuses; //additional bonuses to be added during battle with neutrals std::vector<Bonus> battleBonuses; //additional bonuses to be added during battle with neutrals
std::map<uint32_t, std::map<ArtifactPosition, ArtifactID>> costumesArtifacts; std::map<uint32_t, std::map<ArtifactPosition, ArtifactID>> costumesArtifacts;
@ -90,9 +92,20 @@ public:
std::string getNameTextID() const override; std::string getNameTextID() const override;
void registerIcons(const IconRegistar & cb) const override; void registerIcons(const IconRegistar & cb) const override;
std::vector<const CGHeroInstance* > getHeroes() const;
std::vector<const CGTownInstance* > getTowns() const;
std::vector<CGHeroInstance* > getHeroes();
std::vector<CGTownInstance* > getTowns();
std::vector<const CGDwelling* > getDwellings() const;
std::vector<const CGObjectInstance* > getOwnedObjects() const;
void addOwnedObject(CGObjectInstance * object);
void removeOwnedObject(CGObjectInstance * object);
bool checkVanquished() const bool checkVanquished() const
{ {
return heroes.empty() && towns.empty(); return ownedObjects.empty();
} }
template <typename Handler> void serialize(Handler &h) template <typename Handler> void serialize(Handler &h)
@ -103,9 +116,10 @@ public:
h & resources; h & resources;
h & status; h & status;
h & turnTimer; h & turnTimer;
h & heroes; h & ownedObjects;
h & towns; //h & heroes;
h & dwellings; //h & towns;
//h & dwellings;
h & quests; h & quests;
h & visitedObjects; h & visitedObjects;
h & visitedObjectsGlobal; h & visitedObjectsGlobal;

View File

@ -574,7 +574,6 @@ void CGameState::initHeroes()
} }
hero->initHero(getRandomGenerator()); hero->initHero(getRandomGenerator());
getPlayerState(hero->getOwner())->heroes.push_back(hero);
map->allHeroes[hero->getHeroType().getNum()] = hero; map->allHeroes[hero->getHeroType().getNum()] = hero;
} }
@ -699,14 +698,14 @@ void CGameState::initStartingBonus()
} }
case PlayerStartingBonus::ARTIFACT: case PlayerStartingBonus::ARTIFACT:
{ {
if(elem.second.heroes.empty()) if(elem.second.getHeroes().empty())
{ {
logGlobal->error("Cannot give starting artifact - no heroes!"); logGlobal->error("Cannot give starting artifact - no heroes!");
break; break;
} }
const Artifact * toGive = pickRandomArtifact(getRandomGenerator(), CArtifact::ART_TREASURE).toEntity(VLC); const Artifact * toGive = pickRandomArtifact(getRandomGenerator(), CArtifact::ART_TREASURE).toEntity(VLC);
CGHeroInstance *hero = elem.second.heroes[0]; CGHeroInstance *hero = elem.second.getHeroes()[0];
if(!giveHeroArtifact(hero, toGive->getId())) if(!giveHeroArtifact(hero, toGive->getId()))
logGlobal->error("Cannot give starting artifact - no free slots!"); logGlobal->error("Cannot give starting artifact - no free slots!");
} }
@ -893,8 +892,6 @@ void CGameState::initTowns()
vti->possibleSpells -= s->id; vti->possibleSpells -= s->id;
} }
vti->possibleSpells.clear(); vti->possibleSpells.clear();
if(vti->getOwner() != PlayerColor::NEUTRAL)
getPlayerState(vti->getOwner())->towns.emplace_back(vti);
} }
} }
@ -902,6 +899,12 @@ void CGameState::initMapObjects()
{ {
logGlobal->debug("\tObject initialization"); logGlobal->debug("\tObject initialization");
for(CGObjectInstance *obj : map->objects)
{
if (obj && obj->getOwner().isValidPlayer())
getPlayerState(obj->getOwner())->addOwnedObject(obj);
}
// objCaller->preInit(); // objCaller->preInit();
for(CGObjectInstance *obj : map->objects) for(CGObjectInstance *obj : map->objects)
{ {
@ -937,9 +940,9 @@ void CGameState::placeHeroesInTowns()
if(player.first == PlayerColor::NEUTRAL) if(player.first == PlayerColor::NEUTRAL)
continue; continue;
for(CGHeroInstance * h : player.second.heroes) for(CGHeroInstance * h : player.second.getHeroes())
{ {
for(CGTownInstance * t : player.second.towns) for(CGTownInstance * t : player.second.getTowns())
{ {
if(h->visitablePos().z != t->visitablePos().z) if(h->visitablePos().z != t->visitablePos().z)
continue; continue;
@ -971,9 +974,9 @@ void CGameState::initVisitingAndGarrisonedHeroes()
continue; continue;
//init visiting and garrisoned heroes //init visiting and garrisoned heroes
for(CGHeroInstance * h : player.second.heroes) for(CGHeroInstance * h : player.second.getHeroes())
{ {
for(CGTownInstance * t : player.second.towns) for(CGTownInstance * t : player.second.getTowns())
{ {
if(h->visitablePos().z != t->visitablePos().z) if(h->visitablePos().z != t->visitablePos().z)
continue; continue;
@ -1371,7 +1374,7 @@ bool CGameState::checkForVictory(const PlayerColor & player, const EventConditio
} }
case EventCondition::HAVE_ARTIFACT: //check if any hero has winning artifact case EventCondition::HAVE_ARTIFACT: //check if any hero has winning artifact
{ {
for(const auto & elem : p->heroes) for(const auto & elem : p->getHeroes())
if(elem->hasArt(condition.objectType.as<ArtifactID>())) if(elem->hasArt(condition.objectType.as<ArtifactID>()))
return true; return true;
return false; return false;
@ -1405,7 +1408,7 @@ bool CGameState::checkForVictory(const PlayerColor & player, const EventConditio
} }
else // any town else // any town
{ {
for (const CGTownInstance * t : p->towns) for (const CGTownInstance * t : p->getTowns())
{ {
if (t->hasBuilt(condition.objectType.as<BuildingID>())) if (t->hasBuilt(condition.objectType.as<BuildingID>()))
return true; return true;
@ -1550,9 +1553,9 @@ void CGameState::obtainPlayersStats(SThievesGuildInfo & tgi, int level)
if(level >= 0) //num of towns & num of heroes if(level >= 0) //num of towns & num of heroes
{ {
//num of towns //num of towns
FILL_FIELD(numOfTowns, g->second.towns.size()) FILL_FIELD(numOfTowns, g->second.getTowns().size())
//num of heroes //num of heroes
FILL_FIELD(numOfHeroes, g->second.heroes.size()) FILL_FIELD(numOfHeroes, g->second.getHeroes().size())
} }
if(level >= 1) //best hero's portrait if(level >= 1) //best hero's portrait
{ {
@ -1624,7 +1627,7 @@ void CGameState::obtainPlayersStats(SThievesGuildInfo & tgi, int level)
if(playerInactive(player.second.color)) //do nothing for neutral player if(playerInactive(player.second.color)) //do nothing for neutral player
continue; continue;
CreatureID bestCre; //best creature's ID CreatureID bestCre; //best creature's ID
for(const auto & elem : player.second.heroes) for(const auto & elem : player.second.getHeroes())
{ {
for(const auto & it : elem->Slots()) for(const auto & it : elem->Slots())
{ {

View File

@ -536,7 +536,7 @@ void CGameStateCampaign::initHeroes()
} }
assert(humanPlayer != PlayerColor::NEUTRAL); assert(humanPlayer != PlayerColor::NEUTRAL);
std::vector<ConstTransitivePtr<CGHeroInstance> > & heroes = gameState->players[humanPlayer].heroes; const auto & heroes = gameState->players[humanPlayer].getHeroes();
if (chosenBonus->info1 == 0xFFFD) //most powerful if (chosenBonus->info1 == 0xFFFD) //most powerful
{ {

View File

@ -50,10 +50,10 @@ StatisticDataSetEntry StatisticDataSet::createEntry(const PlayerState * ps, cons
data.isHuman = ps->isHuman(); data.isHuman = ps->isHuman();
data.status = ps->status; data.status = ps->status;
data.resources = ps->resources; data.resources = ps->resources;
data.numberHeroes = ps->heroes.size(); data.numberHeroes = ps->getHeroes().size();
data.numberTowns = gs->howManyTowns(ps->color); data.numberTowns = gs->howManyTowns(ps->color);
data.numberArtifacts = Statistic::getNumberOfArts(ps); data.numberArtifacts = Statistic::getNumberOfArts(ps);
data.numberDwellings = gs->getPlayerState(ps->color)->dwellings.size(); data.numberDwellings = gs->getPlayerState(ps->color)->getDwellings().size();
data.armyStrength = Statistic::getArmyStrength(ps, true); data.armyStrength = Statistic::getArmyStrength(ps, true);
data.totalExperience = Statistic::getTotalExperience(ps); data.totalExperience = Statistic::getTotalExperience(ps);
data.income = Statistic::getIncome(gs, ps); data.income = Statistic::getIncome(gs, ps);
@ -221,7 +221,7 @@ std::vector<const CGMine *> Statistic::getMines(const CGameState * gs, const Pla
int Statistic::getNumberOfArts(const PlayerState * ps) int Statistic::getNumberOfArts(const PlayerState * ps)
{ {
int ret = 0; int ret = 0;
for(auto h : ps->heroes) for(auto h : ps->getHeroes())
{ {
ret += h->artifactsInBackpack.size() + h->artifactsWorn.size(); ret += h->artifactsInBackpack.size() + h->artifactsWorn.size();
} }
@ -233,7 +233,7 @@ si64 Statistic::getArmyStrength(const PlayerState * ps, bool withTownGarrison)
{ {
si64 str = 0; si64 str = 0;
for(auto h : ps->heroes) for(auto h : ps->getHeroes())
{ {
if(!h->inTownGarrison || withTownGarrison) //original h3 behavior if(!h->inTownGarrison || withTownGarrison) //original h3 behavior
str += h->getArmyStrength(); str += h->getArmyStrength();
@ -246,7 +246,7 @@ si64 Statistic::getTotalExperience(const PlayerState * ps)
{ {
si64 tmp = 0; si64 tmp = 0;
for(auto h : ps->heroes) for(auto h : ps->getHeroes())
tmp += h->exp; tmp += h->exp;
return tmp; return tmp;
@ -258,11 +258,11 @@ int Statistic::getIncome(const CGameState * gs, const PlayerState * ps)
int totalIncome = 0; int totalIncome = 0;
//Heroes can produce gold as well - skill, specialty or arts //Heroes can produce gold as well - skill, specialty or arts
for(const auto & h : ps->heroes) for(const auto & h : ps->getHeroes())
totalIncome += h->dailyIncome()[EGameResID::GOLD]; totalIncome += h->dailyIncome()[EGameResID::GOLD];
//Add town income of all towns //Add town income of all towns
for(const auto & t : ps->towns) for(const auto & t : ps->getTowns())
totalIncome += t->dailyIncome()[EGameResID::GOLD]; totalIncome += t->dailyIncome()[EGameResID::GOLD];
for(const CGMine * mine : getMines(gs, ps)) for(const CGMine * mine : getMines(gs, ps))
@ -295,7 +295,7 @@ float Statistic::getMapExploredRatio(const CGameState * gs, PlayerColor player)
const CGHeroInstance * Statistic::findBestHero(const CGameState * gs, const PlayerColor & color) const CGHeroInstance * Statistic::findBestHero(const CGameState * gs, const PlayerColor & color)
{ {
auto &h = gs->players.at(color).heroes; const auto &h = gs->players.at(color).getHeroes();
if(h.empty()) if(h.empty())
return nullptr; return nullptr;
//best hero will be that with highest exp //best hero will be that with highest exp
@ -368,7 +368,7 @@ float Statistic::getTownBuiltRatio(const PlayerState * ps)
float built = 0.0; float built = 0.0;
float total = 0.0; float total = 0.0;
for(const auto & t : ps->towns) for(const auto & t : ps->getTowns())
{ {
built += t->getBuildings().size(); built += t->getBuildings().size();
for(const auto & b : t->town->buildings) for(const auto & b : t->town->buildings)

View File

@ -14,7 +14,7 @@
VCMI_LIB_NAMESPACE_BEGIN VCMI_LIB_NAMESPACE_BEGIN
struct PlayerState; class PlayerState;
class CGameState; class CGameState;
class CGHeroInstance; class CGHeroInstance;
class CGMine; class CGMine;

View File

@ -29,10 +29,10 @@ HighScoreParameter HighScore::prepareHighScores(const CGameState * gs, PlayerCol
param.townAmount = gs->howManyTowns(player); param.townAmount = gs->howManyTowns(player);
param.usedCheat = gs->getPlayerState(player)->cheated; param.usedCheat = gs->getPlayerState(player)->cheated;
param.hasGrail = false; param.hasGrail = false;
for(const CGHeroInstance * h : playerState->heroes) for(const CGHeroInstance * h : playerState->getHeroes())
if(h->hasArt(ArtifactID::GRAIL)) if(h->hasArt(ArtifactID::GRAIL))
param.hasGrail = true; param.hasGrail = true;
for(const CGTownInstance * t : playerState->towns) for(const CGTownInstance * t : playerState->getTowns())
if(t->hasBuilt(BuildingID::GRAIL)) if(t->hasBuilt(BuildingID::GRAIL))
param.hasGrail = true; param.hasGrail = true;
param.allEnemiesDefeated = true; param.allEnemiesDefeated = true;

View File

@ -182,10 +182,6 @@ void CGDwelling::initObj(vstd::RNG & rand)
case Obj::CREATURE_GENERATOR4: case Obj::CREATURE_GENERATOR4:
{ {
getObjectHandler()->configureObject(this, rand); getObjectHandler()->configureObject(this, rand);
if (getOwner() != PlayerColor::NEUTRAL)
cb->gameState()->players[getOwner()].dwellings.emplace_back(this);
assert(!creatures.empty()); assert(!creatures.empty());
assert(!creatures[0].second.empty()); assert(!creatures[0].second.empty());
break; break;
@ -211,19 +207,6 @@ void CGDwelling::setPropertyDer(ObjProperty what, ObjPropertyID identifier)
{ {
switch (what) switch (what)
{ {
case ObjProperty::OWNER: //change owner
if (ID == Obj::CREATURE_GENERATOR1 || ID == Obj::CREATURE_GENERATOR2
|| ID == Obj::CREATURE_GENERATOR3 || ID == Obj::CREATURE_GENERATOR4)
{
if (tempOwner != PlayerColor::NEUTRAL)
{
std::vector<ConstTransitivePtr<CGDwelling> >* dwellings = &cb->gameState()->players[tempOwner].dwellings;
dwellings->erase (std::find(dwellings->begin(), dwellings->end(), this));
}
if (identifier.as<PlayerColor>().isValidPlayer())
cb->gameState()->players[identifier.as<PlayerColor>()].dwellings.emplace_back(this);
}
break;
case ObjProperty::AVAILABLE_CREATURE: case ObjProperty::AVAILABLE_CREATURE:
creatures.resize(1); creatures.resize(1);
creatures[0].second.resize(1); creatures[0].second.resize(1);

View File

@ -181,7 +181,7 @@ GrowthInfo CGTownInstance::getGrowthInfo(int level) const
int dwellingBonus = 0; int dwellingBonus = 0;
if(const PlayerState *p = cb->getPlayerState(tempOwner, false)) if(const PlayerState *p = cb->getPlayerState(tempOwner, false))
{ {
dwellingBonus = getDwellingBonus(creatures[level].second, p->dwellings); dwellingBonus = getDwellingBonus(creatures[level].second, p->getDwellings());
} }
if(dwellingBonus) if(dwellingBonus)
ret.entries.emplace_back(VLC->generaltexth->allTexts[591], dwellingBonus); // \nExternal dwellings %+d ret.entries.emplace_back(VLC->generaltexth->allTexts[591], dwellingBonus); // \nExternal dwellings %+d
@ -192,7 +192,7 @@ GrowthInfo CGTownInstance::getGrowthInfo(int level) const
return ret; return ret;
} }
int CGTownInstance::getDwellingBonus(const std::vector<CreatureID>& creatureIds, const std::vector<ConstTransitivePtr<CGDwelling> >& dwellings) const int CGTownInstance::getDwellingBonus(const std::vector<CreatureID>& creatureIds, const std::vector<const CGDwelling * >& dwellings) const
{ {
int totalBonus = 0; int totalBonus = 0;
for (const auto& dwelling : dwellings) for (const auto& dwelling : dwellings)
@ -635,9 +635,9 @@ void CGTownInstance::removeCapitols(const PlayerColor & owner) const
if (hasCapitol()) // search if there's an older capitol if (hasCapitol()) // search if there's an older capitol
{ {
PlayerState* state = cb->gameState()->getPlayerState(owner); //get all towns owned by player PlayerState* state = cb->gameState()->getPlayerState(owner); //get all towns owned by player
for (auto i = state->towns.cbegin(); i < state->towns.cend(); ++i) for (const auto & town : state->getTowns())
{ {
if (*i != this && (*i)->hasCapitol()) if (town != this && town->hasCapitol())
{ {
RazeStructures rs; RazeStructures rs;
rs.tid = id; rs.tid = id;
@ -672,7 +672,7 @@ int CGTownInstance::getMarketEfficiency() const
assert(p); assert(p);
int marketCount = 0; int marketCount = 0;
for(const CGTownInstance *t : p->towns) for(const CGTownInstance *t : p->getTowns())
if(t->hasBuiltSomeTradeBuilding()) if(t->hasBuiltSomeTradeBuilding())
marketCount++; marketCount++;

View File

@ -239,7 +239,7 @@ private:
FactionID randomizeFaction(vstd::RNG & rand); FactionID randomizeFaction(vstd::RNG & rand);
void setOwner(const PlayerColor & owner) const; void setOwner(const PlayerColor & owner) const;
void onTownCaptured(const PlayerColor & winner) const; void onTownCaptured(const PlayerColor & winner) const;
int getDwellingBonus(const std::vector<CreatureID>& creatureIds, const std::vector<ConstTransitivePtr<CGDwelling> >& dwellings) const; int getDwellingBonus(const std::vector<CreatureID>& creatureIds, const std::vector<const CGDwelling* >& dwellings) const;
bool townEnvisagesBuilding(BuildingSubID::EBuildingSubID bid) const; bool townEnvisagesBuilding(BuildingSubID::EBuildingSubID bid) const;
void initializeConfigurableBuildings(vstd::RNG & rand); void initializeConfigurableBuildings(vstd::RNG & rand);
}; };

View File

@ -1171,7 +1171,7 @@ void RemoveObject::applyGs(CGameState *gs)
assert(beatenHero); assert(beatenHero);
PlayerState * p = gs->getPlayerState(beatenHero->tempOwner); PlayerState * p = gs->getPlayerState(beatenHero->tempOwner);
gs->map->heroesOnMap -= beatenHero; gs->map->heroesOnMap -= beatenHero;
p->heroes -= beatenHero; p->removeOwnedObject(beatenHero);
auto * siegeNode = beatenHero->whereShouldBeAttachedOnSiege(gs); auto * siegeNode = beatenHero->whereShouldBeAttachedOnSiege(gs);
@ -1417,7 +1417,7 @@ void HeroRecruited::applyGs(CGameState *gs)
gs->map->objects[h->id.getNum()] = h; gs->map->objects[h->id.getNum()] = h;
gs->map->heroesOnMap.emplace_back(h); gs->map->heroesOnMap.emplace_back(h);
p->heroes.emplace_back(h); p->addOwnedObject(h);
h->attachTo(*p); h->attachTo(*p);
gs->map->addBlockVisTiles(h); gs->map->addBlockVisTiles(h);
@ -1452,7 +1452,7 @@ void GiveHero::applyGs(CGameState *gs)
h->setMovementPoints(h->movementPointsLimit(true)); h->setMovementPoints(h->movementPointsLimit(true));
h->pos = h->convertFromVisitablePos(oldVisitablePos); h->pos = h->convertFromVisitablePos(oldVisitablePos);
gs->map->heroesOnMap.emplace_back(h); gs->map->heroesOnMap.emplace_back(h);
gs->getPlayerState(h->getOwner())->heroes.emplace_back(h); gs->getPlayerState(h->getOwner())->addOwnedObject(h);
gs->map->addBlockVisTiles(h); gs->map->addBlockVisTiles(h);
h->inTownGarrison = false; h->inTownGarrison = false;
@ -1942,6 +1942,18 @@ void SetObjectProperty::applyGs(CGameState *gs)
} }
auto * cai = dynamic_cast<CArmedInstance *>(obj); auto * cai = dynamic_cast<CArmedInstance *>(obj);
if(what == ObjProperty::OWNER)
{
PlayerColor oldOwner = obj->getOwner();
PlayerColor newOwner = identifier.as<PlayerColor>();
if(oldOwner.isValidPlayer())
gs->getPlayerState(oldOwner)->removeOwnedObject(obj);;
if(newOwner.isValidPlayer())
gs->getPlayerState(newOwner)->addOwnedObject(obj);;
}
if(what == ObjProperty::OWNER && cai) if(what == ObjProperty::OWNER && cai)
{ {
if(obj->ID == Obj::TOWN) if(obj->ID == Obj::TOWN)
@ -1953,17 +1965,13 @@ void SetObjectProperty::applyGs(CGameState *gs)
if(oldOwner.isValidPlayer()) if(oldOwner.isValidPlayer())
{ {
auto * state = gs->getPlayerState(oldOwner); auto * state = gs->getPlayerState(oldOwner);
state->towns -= t; if(state->getTowns().empty())
if(state->towns.empty())
state->daysWithoutCastle = 0; state->daysWithoutCastle = 0;
} }
if(identifier.as<PlayerColor>().isValidPlayer()) if(identifier.as<PlayerColor>().isValidPlayer())
{ {
PlayerState * p = gs->getPlayerState(identifier.as<PlayerColor>());
p->towns.emplace_back(t);
//reset counter before NewTurn to avoid no town message if game loaded at turn when one already captured //reset counter before NewTurn to avoid no town message if game loaded at turn when one already captured
PlayerState * p = gs->getPlayerState(identifier.as<PlayerColor>());
if(p->daysWithoutCastle) if(p->daysWithoutCastle)
p->daysWithoutCastle = std::nullopt; p->daysWithoutCastle = std::nullopt;
} }

View File

@ -264,8 +264,7 @@ TeleporterTilesVector CPathfinderHelper::getCastleGates(const PathNodeInfo & sou
{ {
TeleporterTilesVector allowedExits; TeleporterTilesVector allowedExits;
auto towns = getPlayerState(hero->tempOwner)->towns; for(const auto & town : getPlayerState(hero->tempOwner)->getTowns())
for(const auto & town : towns)
{ {
if(town->id != source.nodeObject->id && town->visitingHero == nullptr if(town->id != source.nodeObject->id && town->visitingHero == nullptr
&& town->hasBuilt(BuildingID::CASTLE_GATE, ETownType::INFERNO)) && town->hasBuilt(BuildingID::CASTLE_GATE, ETownType::INFERNO))

View File

@ -692,9 +692,9 @@ std::vector <const CGTownInstance*> TownPortalMechanics::getPossibleTowns(SpellC
for(const auto & color : team->players) for(const auto & color : team->players)
{ {
for(auto currTown : env->getCb()->getPlayerState(color)->towns) for(auto currTown : env->getCb()->getPlayerState(color)->getTowns())
{ {
ret.push_back(currTown.get()); ret.push_back(currTown);
} }
} }
return ret; return ret;

View File

@ -555,7 +555,7 @@ void CGameHandler::setPortalDwelling(const CGTownInstance * town, bool forced=fa
ssi.creatures = town->creatures; ssi.creatures = town->creatures;
ssi.creatures[town->town->creatures.size()].second.clear();//remove old one ssi.creatures[town->town->creatures.size()].second.clear();//remove old one
const std::vector<ConstTransitivePtr<CGDwelling> > &dwellings = p->dwellings; const auto &dwellings = p->getDwellings();
if (dwellings.empty())//no dwellings - just remove if (dwellings.empty())//no dwellings - just remove
{ {
sendAndApply(&ssi); sendAndApply(&ssi);
@ -639,7 +639,7 @@ void CGameHandler::onNewTurn()
if (player.second.status != EPlayerStatus::INGAME) if (player.second.status != EPlayerStatus::INGAME)
continue; continue;
if (player.second.heroes.empty() && player.second.towns.empty()) if (player.second.getHeroes().empty() && player.second.getTowns().empty())
throw std::runtime_error("Invalid player in player state! Player " + std::to_string(player.first.getNum()) + ", map name: " + gs->map->name.toString() + ", map description: " + gs->map->description.toString()); throw std::runtime_error("Invalid player in player state! Player " + std::to_string(player.first.getNum()) + ", map name: " + gs->map->name.toString() + ", map description: " + gs->map->description.toString());
} }
@ -724,7 +724,7 @@ void CGameHandler::onNewTurn()
if (firstTurn) if (firstTurn)
heroPool->onNewWeek(elem.first); heroPool->onNewWeek(elem.first);
for (CGHeroInstance *h : (elem).second.heroes) for (CGHeroInstance *h : (elem).second.getHeroes())
{ {
if (h->visitedTown) if (h->visitedTown)
giveSpells(h->visitedTown, h); giveSpells(h->visitedTown, h);
@ -1286,9 +1286,9 @@ void CGameHandler::setOwner(const CGObjectInstance * obj, const PlayerColor owne
const PlayerState * p = getPlayerState(owner); const PlayerState * p = getPlayerState(owner);
if ((obj->ID == Obj::CREATURE_GENERATOR1 || obj->ID == Obj::CREATURE_GENERATOR4) && p && p->dwellings.size()==1)//first dwelling captured if ((obj->ID == Obj::CREATURE_GENERATOR1 || obj->ID == Obj::CREATURE_GENERATOR4) && p && p->getDwellings().size()==1)//first dwelling captured
{ {
for (const CGTownInstance * t : getPlayerState(owner)->towns) for (const CGTownInstance * t : getPlayerState(owner)->getTowns())
{ {
if (t->hasBuilt(BuildingSubID::PORTAL_OF_SUMMONING)) if (t->hasBuilt(BuildingSubID::PORTAL_OF_SUMMONING))
setPortalDwelling(t);//set initial creatures for all portals of summoning setPortalDwelling(t);//set initial creatures for all portals of summoning
@ -3297,7 +3297,7 @@ void CGameHandler::handleTimeEvents(PlayerColor color)
} }
} }
void CGameHandler::handleTownEvents(CGTownInstance * town) void CGameHandler::handleTownEvents(const CGTownInstance * town)
{ {
for (auto const & event : town->events) for (auto const & event : town->events)
{ {
@ -3631,10 +3631,10 @@ void CGameHandler::checkVictoryLossConditionsForPlayer(PlayerColor player)
else else
{ {
//copy heroes vector to avoid iterator invalidation as removal change PlayerState //copy heroes vector to avoid iterator invalidation as removal change PlayerState
auto hlp = p->heroes; auto hlp = p->getHeroes();
for (auto h : hlp) //eliminate heroes for (auto h : hlp) //eliminate heroes
{ {
if (h.get()) if (h)
removeObject(h, player); removeObject(h, player);
} }
@ -4159,11 +4159,11 @@ void CGameHandler::changeFogOfWar(int3 center, ui32 radius, PlayerColor player,
std::unordered_set<int3> observedTiles; //do not hide tiles observed by heroes. May lead to disastrous AI problems std::unordered_set<int3> observedTiles; //do not hide tiles observed by heroes. May lead to disastrous AI problems
auto p = getPlayerState(player); auto p = getPlayerState(player);
for (auto h : p->heroes) for (auto h : p->getHeroes())
{ {
getTilesInRange(observedTiles, h->getSightCenter(), h->getSightRadius(), ETileVisibility::REVEALED, h->tempOwner); getTilesInRange(observedTiles, h->getSightCenter(), h->getSightRadius(), ETileVisibility::REVEALED, h->tempOwner);
} }
for (auto t : p->towns) for (auto t : p->getTowns())
{ {
getTilesInRange(observedTiles, t->getSightCenter(), t->getSightRadius(), ETileVisibility::REVEALED, t->tempOwner); getTilesInRange(observedTiles, t->getSightCenter(), t->getSightRadius(), ETileVisibility::REVEALED, t->tempOwner);
} }

View File

@ -230,7 +230,7 @@ public:
void addStatistics(StatisticDataSet &stat) const; void addStatistics(StatisticDataSet &stat) const;
void handleTimeEvents(PlayerColor player); void handleTimeEvents(PlayerColor player);
void handleTownEvents(CGTownInstance *town); void handleTownEvents(const CGTownInstance *town);
bool complain(const std::string &problem); //sends message to all clients, prints on the logs and return true bool complain(const std::string &problem); //sends message to all clients, prints on the logs and return true
void objectVisited( const CGObjectInstance * obj, const CGHeroInstance * h ); void objectVisited( const CGObjectInstance * obj, const CGHeroInstance * h );
void objectVisitEnded(const CObjectVisitQuery &query); void objectVisitEnded(const CObjectVisitQuery &query);

View File

@ -484,7 +484,7 @@ void BattleResultProcessor::endBattleConfirm(const CBattleInfoCallback & battle)
if(!finishingBattle->isDraw()) if(!finishingBattle->isDraw())
{ {
ConstTransitivePtr<CGHeroInstance> strongestHero = nullptr; ConstTransitivePtr<CGHeroInstance> strongestHero = nullptr;
for(auto & hero : gameHandler->gameState()->getPlayerState(finishingBattle->loser)->heroes) for(auto & hero : gameHandler->gameState()->getPlayerState(finishingBattle->loser)->getHeroes())
if(!strongestHero || hero->exp > strongestHero->exp) if(!strongestHero || hero->exp > strongestHero->exp)
strongestHero = hero; strongestHero = hero;
if(strongestHero->id == finishingBattle->loserHero->id && strongestHero->level > 5) if(strongestHero->id == finishingBattle->loserHero->id && strongestHero->level > 5)

View File

@ -40,10 +40,10 @@ void NewTurnProcessor::onPlayerTurnStarted(PlayerColor which)
const auto * playerState = gameHandler->gameState()->getPlayerState(which); const auto * playerState = gameHandler->gameState()->getPlayerState(which);
gameHandler->handleTimeEvents(which); gameHandler->handleTimeEvents(which);
for (auto t : playerState->towns) for (const auto * t : playerState->getTowns())
gameHandler->handleTownEvents(t); gameHandler->handleTownEvents(t);
for (auto t : playerState->towns) for (const auto * t : playerState->getTowns())
{ {
//garrison hero first - consistent with original H3 Mana Vortex and Battle Scholar Academy levelup windows order //garrison hero first - consistent with original H3 Mana Vortex and Battle Scholar Academy levelup windows order
if (t->garrisonHero != nullptr) if (t->garrisonHero != nullptr)
@ -59,7 +59,7 @@ void NewTurnProcessor::onPlayerTurnEnded(PlayerColor which)
const auto * playerState = gameHandler->gameState()->getPlayerState(which); const auto * playerState = gameHandler->gameState()->getPlayerState(which);
assert(playerState->status == EPlayerStatus::INGAME); assert(playerState->status == EPlayerStatus::INGAME);
if (playerState->towns.empty()) if (playerState->getTowns().empty())
{ {
DaysWithoutTown pack; DaysWithoutTown pack;
pack.player = which; pack.player = which;
@ -92,7 +92,7 @@ ResourceSet NewTurnProcessor::generatePlayerIncome(PlayerColor playerID, bool ne
const PlayerState & state = gameHandler->gameState()->players.at(playerID); const PlayerState & state = gameHandler->gameState()->players.at(playerID);
ResourceSet income; ResourceSet income;
for (const auto & town : state.towns) for (const auto & town : state.getTowns())
{ {
if (newWeek && town->hasBuilt(BuildingSubID::TREASURY)) if (newWeek && town->hasBuilt(BuildingSubID::TREASURY))
{ {
@ -123,18 +123,18 @@ ResourceSet NewTurnProcessor::generatePlayerIncome(PlayerColor playerID, bool ne
for (GameResID k = GameResID::WOOD; k < GameResID::COUNT; k++) for (GameResID k = GameResID::WOOD; k < GameResID::COUNT; k++)
{ {
income += state.valOfBonuses(BonusType::RESOURCES_CONSTANT_BOOST, BonusSubtypeID(k)); income += state.valOfBonuses(BonusType::RESOURCES_CONSTANT_BOOST, BonusSubtypeID(k));
income += state.valOfBonuses(BonusType::RESOURCES_TOWN_MULTIPLYING_BOOST, BonusSubtypeID(k)) * state.towns.size(); income += state.valOfBonuses(BonusType::RESOURCES_TOWN_MULTIPLYING_BOOST, BonusSubtypeID(k)) * state.getTowns().size();
} }
if(newWeek) //weekly crystal generation if 1 or more crystal dragons in any hero army or town garrison if(newWeek) //weekly crystal generation if 1 or more crystal dragons in any hero army or town garrison
{ {
bool hasCrystalGenCreature = false; bool hasCrystalGenCreature = false;
for (const auto & hero : state.heroes) for (const auto & hero : state.getHeroes())
for(auto stack : hero->stacks) for(auto stack : hero->stacks)
if(stack.second->hasBonusOfType(BonusType::SPECIAL_CRYSTAL_GENERATION)) if(stack.second->hasBonusOfType(BonusType::SPECIAL_CRYSTAL_GENERATION))
hasCrystalGenCreature = true; hasCrystalGenCreature = true;
for(const auto & town : state.towns) for(const auto & town : state.getTowns())
for(auto stack : town->stacks) for(auto stack : town->stacks)
if(stack.second->hasBonusOfType(BonusType::SPECIAL_CRYSTAL_GENERATION)) if(stack.second->hasBonusOfType(BonusType::SPECIAL_CRYSTAL_GENERATION))
hasCrystalGenCreature = true; hasCrystalGenCreature = true;
@ -146,10 +146,9 @@ ResourceSet NewTurnProcessor::generatePlayerIncome(PlayerColor playerID, bool ne
TResources incomeHandicapped = income; TResources incomeHandicapped = income;
incomeHandicapped.applyHandicap(playerSettings->handicap.percentIncome); incomeHandicapped.applyHandicap(playerSettings->handicap.percentIncome);
// FIXME: store pre-filtered, all owned objects in PlayerState for (auto obj : state.getOwnedObjects())
for (auto obj : gameHandler->gameState()->map->objects)
{ {
if (obj && obj->asOwnable() && obj->getOwner() == playerID) if (obj->asOwnable())
incomeHandicapped += obj->asOwnable()->dailyIncome(); incomeHandicapped += obj->asOwnable()->dailyIncome();
} }

View File

@ -710,11 +710,11 @@ bool PlayerMessageProcessor::handleCheatCode(const std::string & cheat, PlayerCo
executeCheatCode(cheatName, i.first, ObjectInstanceID::NONE, parameters); executeCheatCode(cheatName, i.first, ObjectInstanceID::NONE, parameters);
if (vstd::contains(townTargetedCheats, cheatName)) if (vstd::contains(townTargetedCheats, cheatName))
for (const auto & t : i.second.towns) for (const auto & t : i.second.getTowns())
executeCheatCode(cheatName, i.first, t->id, parameters); executeCheatCode(cheatName, i.first, t->id, parameters);
if (vstd::contains(heroTargetedCheats, cheatName)) if (vstd::contains(heroTargetedCheats, cheatName))
for (const auto & h : i.second.heroes) for (const auto & h : i.second.getHeroes())
executeCheatCode(cheatName, i.first, h->id, parameters); executeCheatCode(cheatName, i.first, h->id, parameters);
} }

View File

@ -121,7 +121,7 @@ bool TurnOrderProcessor::playersInContact(PlayerColor left, PlayerColor right) c
} }
} }
for(const auto & hero : leftInfo->heroes) for(const auto & hero : leftInfo->getHeroes())
{ {
CPathsInfo out(mapSize, hero); CPathsInfo out(mapSize, hero);
auto config = std::make_shared<SingleHeroPathfinderConfig>(out, gameHandler->gameState(), hero); auto config = std::make_shared<SingleHeroPathfinderConfig>(out, gameHandler->gameState(), hero);
@ -137,7 +137,7 @@ bool TurnOrderProcessor::playersInContact(PlayerColor left, PlayerColor right) c
leftReachability[z][x][y] = true; leftReachability[z][x][y] = true;
} }
for(const auto & hero : rightInfo->heroes) for(const auto & hero : rightInfo->getHeroes())
{ {
CPathsInfo out(mapSize, hero); CPathsInfo out(mapSize, hero);
auto config = std::make_shared<SingleHeroPathfinderConfig>(out, gameHandler->gameState(), hero); auto config = std::make_shared<SingleHeroPathfinderConfig>(out, gameHandler->gameState(), hero);