1
0
mirror of https://github.com/vcmi/vcmi.git synced 2025-03-27 21:49:10 +02:00

Implemented tracking of objects destroyed by players

This commit is contained in:
Ivan Savenko 2024-01-31 01:37:33 +02:00
parent ccea7fc1fb
commit 2e4895766a
13 changed files with 44 additions and 53 deletions

@ -210,7 +210,7 @@ TGoalVec CompleteQuest::missionResources() const
TGoalVec CompleteQuest::missionDestroyObj() const
{
auto obj = cb->getObjByQuestIdentifier(q.quest->killTarget);
auto obj = cb->getObj(q.quest->killTarget);
if(!obj)
return CaptureObjectsBehavior(q.obj).decompose();

@ -241,7 +241,7 @@ TGoalVec CompleteQuest::missionDestroyObj() const
{
TGoalVec solutions;
auto obj = cb->getObjByQuestIdentifier(q.quest->killTarget);
auto obj = cb->getObj(q.quest->killTarget);
if(!obj)
return ai->ah->howToVisitObj(q.obj);

@ -124,20 +124,6 @@ TurnTimerInfo CGameInfoCallback::getPlayerTurnTime(PlayerColor color) const
return TurnTimerInfo{};
}
const CGObjectInstance * CGameInfoCallback::getObjByQuestIdentifier(ObjectInstanceID identifier) const
{
if(gs->map->questIdentifierToId.empty())
{
//assume that it is VCMI map and quest identifier equals instance identifier
return getObj(identifier, true);
}
else
{
ERROR_RET_VAL_IF(!vstd::contains(gs->map->questIdentifierToId, identifier.getNum()), "There is no object with such quest identifier!", nullptr);
return getObj(gs->map->questIdentifierToId[identifier.getNum()]);
}
}
/************************************************************************/
/* */
/************************************************************************/

@ -93,7 +93,6 @@ public:
// std::vector <const CGObjectInstance * > getFlaggableObjects(int3 pos) const;
// const CGObjectInstance * getTopObj (int3 pos) const;
// PlayerColor getOwner(ObjectInstanceID heroID) const;
// const CGObjectInstance *getObjByQuestIdentifier(ObjectInstanceID identifier) const; //nullptr if object has been removed (eg. killed)
//map
// int3 guardingCreaturePosition (int3 pos) const;
@ -190,7 +189,6 @@ public:
virtual std::vector <const CGObjectInstance * > getFlaggableObjects(int3 pos) const;
virtual const CGObjectInstance * getTopObj (int3 pos) const;
virtual PlayerColor getOwner(ObjectInstanceID heroID) const;
virtual const CGObjectInstance *getObjByQuestIdentifier(ObjectInstanceID identifier) const; //nullptr if object has been removed (eg. killed)
//map
virtual int3 guardingCreaturePosition (int3 pos) const;

@ -52,6 +52,10 @@ public:
bool human; //true if human controlled player, false for AI
TeamID team;
TResources resources;
/// 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> visitedObjects; // as a std::set, since most accesses here will be from visited status checks
std::set<VisitedObjectGlobal> visitedObjectsGlobal;
std::vector<ConstTransitivePtr<CGHeroInstance> > heroes;
@ -110,6 +114,8 @@ public:
h & enteredLosingCheatCode;
h & enteredWinningCheatCode;
h & static_cast<CBonusSystemNode&>(*this);
if (h.version >= Handler::Version::DESTROYED_OBJECTS)
h & destroyedObjects;
}
};

@ -1420,10 +1420,7 @@ bool CGameState::checkForVictory(const PlayerColor & player, const EventConditio
{
if (condition.objectID != ObjectInstanceID::NONE) // mode A - destroy specific object of this type
{
if(const auto * hero = getHero(condition.objectID))
return boost::range::find(gs->map->heroesOnMap, hero) == gs->map->heroesOnMap.end();
else
return getObj(condition.objectID) == nullptr;
return p->destroyedObjects.count(condition.objectID);
}
else
{

@ -129,12 +129,12 @@ bool CQuest::checkQuest(const CGHeroInstance * h) const
if(!mission.heroAllowed(h))
return false;
if(killTarget != ObjectInstanceID::NONE)
if(killTarget.hasValue())
{
if(h->cb->getObjByQuestIdentifier(killTarget))
PlayerColor owner = h->getOwner();
if (!h->cb->getPlayerState(owner)->destroyedObjects.count(killTarget))
return false;
}
return true;
}
@ -612,7 +612,7 @@ void CGSeerHut::onHeroVisit(const CGHeroInstance * h) const
int CGSeerHut::checkDirection() const
{
int3 cord = getCreatureToKill()->pos;
int3 cord = getCreatureToKill(false)->pos;
if(static_cast<double>(cord.x) / static_cast<double>(cb->getMapSize().x) < 0.34) //north
{
if(static_cast<double>(cord.y) / static_cast<double>(cb->getMapSize().y) < 0.34) //northwest
@ -644,7 +644,7 @@ int CGSeerHut::checkDirection() const
const CGHeroInstance * CGSeerHut::getHeroToKill(bool allowNull) const
{
const CGObjectInstance *o = cb->getObjByQuestIdentifier(quest->killTarget);
const CGObjectInstance *o = cb->getObj(quest->killTarget);
if(allowNull && !o)
return nullptr;
return dynamic_cast<const CGHeroInstance *>(o);
@ -652,7 +652,7 @@ const CGHeroInstance * CGSeerHut::getHeroToKill(bool allowNull) const
const CGCreature * CGSeerHut::getCreatureToKill(bool allowNull) const
{
const CGObjectInstance *o = cb->getObjByQuestIdentifier(quest->killTarget);
const CGObjectInstance *o = cb->getObj(quest->killTarget);
if(allowNull && !o)
return nullptr;
return dynamic_cast<const CGCreature *>(o);

@ -136,8 +136,8 @@ public:
virtual void init(CRandomGenerator & rand);
int checkDirection() const; //calculates the region of map where monster is placed
void setObjToKill(); //remember creatures / heroes to kill after they are initialized
const CGHeroInstance *getHeroToKill(bool allowNull = false) const;
const CGCreature *getCreatureToKill(bool allowNull = false) const;
const CGHeroInstance *getHeroToKill(bool allowNull) const;
const CGCreature *getCreatureToKill(bool allowNull) const;
void getRolloverText (MetaString &text, bool onHover) const;
void afterAddToMap(CMap * map) override;

@ -684,4 +684,15 @@ void CMap::resetStaticData()
townUniversitySkills.clear();
}
void CMap::resolveQuestIdentifiers()
{
//FIXME: move to CMapLoaderH3M
for (auto & quest : quests)
{
if (quest->killTarget != ObjectInstanceID::NONE)
quest->killTarget = questIdentifierToId[quest->killTarget.getNum()];
}
questIdentifierToId.clear();
}
VCMI_LIB_NAMESPACE_END

@ -126,6 +126,7 @@ public:
void checkForObjectives();
void resetStaticData();
void resolveQuestIdentifiers();
ui32 checksum;
std::vector<Rumor> rumors;
@ -186,7 +187,14 @@ public:
h & artInstances;
h & quests;
h & allHeroes;
h & questIdentifierToId;
if (h.version < Handler::Version::DESTROYED_OBJECTS)
{
// old save compatibility
//FIXME: remove this field after save-breaking change
h & questIdentifierToId;
resolveQuestIdentifiers();
}
//TODO: viccondetails
h & terrain;

@ -2392,6 +2392,8 @@ void CMapLoaderH3M::afterRead()
p.posOfMainTown = posOfMainTown + mainTown->getVisitableOffset();
}
}
map->resolveQuestIdentifiers();
}
VCMI_LIB_NAMESPACE_END

@ -1156,6 +1156,9 @@ void RemoveObject::applyGs(CGameState *gs)
//unblock tiles
gs->map->removeBlockVisTiles(obj);
if (initiator.isValidPlayer())
gs->getPlayerState(initiator)->destroyedObjects.insert(objectID);
if(obj->ID == Obj::HERO) //remove beaten hero
{
auto * beatenHero = dynamic_cast<CGHeroInstance *>(obj);
@ -1221,27 +1224,6 @@ void RemoveObject::applyGs(CGameState *gs)
}
}
for (TriggeredEvent & event : gs->map->triggeredEvents)
{
auto patcher = [&](EventCondition cond) -> EventExpression::Variant
{
if (cond.objectID == obj->id)
{
if (cond.condition == EventCondition::DESTROY)
{
cond.condition = EventCondition::CONST_VALUE;
cond.value = 1; // destroyed object, from now on always fulfilled
}
else if (cond.condition == EventCondition::CONTROL)
{
cond.condition = EventCondition::CONST_VALUE;
cond.value = 0; // destroyed object, from now on can not be fulfilled
}
}
return cond;
};
event.trigger = event.trigger.morph(patcher);
}
gs->map->instanceNames.erase(obj->instanceName);
gs->map->objects[objectID.getNum()].dellNull();
gs->map->calculateGuardingGreaturePositions();

@ -34,6 +34,7 @@ enum class ESerializationVersion : int32_t
MINIMAL = 831,
RELEASE_143, // 832 +text container in campaigns, +starting hero in RMG options
HAS_EXTRA_OPTIONS, // 833 +extra options struct as part of startinfo
DESTROYED_OBJECTS, // 834 +list of objects destroyed by player
CURRENT = HAS_EXTRA_OPTIONS
CURRENT = DESTROYED_OBJECTS
};