mirror of
https://github.com/vcmi/vcmi.git
synced 2025-11-06 09:09:40 +02:00
Implemented option to emulate H3 seer hut full-army quest bug
This commit is contained in:
@@ -188,7 +188,7 @@ public:
|
||||
void giveResources(PlayerColor player, TResources resources) override {};
|
||||
|
||||
void giveCreatures(const CArmedInstance * objid, const CGHeroInstance * h, const CCreatureSet & creatures, bool remove) override {};
|
||||
void takeCreatures(ObjectInstanceID objid, const std::vector<CStackBasicDescriptor> & creatures) override {};
|
||||
void takeCreatures(ObjectInstanceID objid, const std::vector<CStackBasicDescriptor> & creatures, bool forceRemoval) override {};
|
||||
bool changeStackType(const StackLocation & sl, const CCreature * c) override {return false;};
|
||||
bool changeStackCount(const StackLocation & sl, TQuantity count, bool absoluteValue = false) override {return false;};
|
||||
bool insertNewStack(const StackLocation & sl, const CCreature * c, TQuantity count) override {return false;};
|
||||
|
||||
@@ -463,6 +463,14 @@
|
||||
"mergeOnRecruit" : true
|
||||
},
|
||||
|
||||
"mapObjects" :
|
||||
{
|
||||
// Allow behavior that emulates h3 bug where quest from Seer Hut or Border Guard can take entire army from hero
|
||||
// WARNING: handling of heroes without armies is not tested and may lead to bugs or crashes! Use at own risk!
|
||||
// If this option is off, quests will only allow taking entire army if quest reward also gives creatures
|
||||
"h3BugQuestTakesEntireArmy" : false
|
||||
},
|
||||
|
||||
"markets" :
|
||||
{
|
||||
// period between restocking of "Black Market" object found on adventure map
|
||||
|
||||
@@ -90,6 +90,7 @@ const std::vector<GameSettings::SettingOption> GameSettings::settingProperties =
|
||||
{EGameSettings::MAP_FORMAT_JSON_VCMI, "mapFormat", "jsonVCMI" },
|
||||
{EGameSettings::MAP_FORMAT_RESTORATION_OF_ERATHIA, "mapFormat", "restorationOfErathia" },
|
||||
{EGameSettings::MAP_FORMAT_SHADOW_OF_DEATH, "mapFormat", "shadowOfDeath" },
|
||||
{EGameSettings::MAP_OBJECTS_H3_BUG_QUEST_TAKES_ENTIRE_ARMY, "mapObjects","h3BugQuestTakesEntireArmy" },
|
||||
{EGameSettings::MARKETS_BLACK_MARKET_RESTOCK_PERIOD, "markets", "blackMarketRestockPeriod" },
|
||||
{EGameSettings::MODULE_COMMANDERS, "modules", "commanders" },
|
||||
{EGameSettings::MODULE_STACK_ARTIFACT, "modules", "stackArtifact" },
|
||||
|
||||
@@ -103,7 +103,7 @@ public:
|
||||
virtual void giveResources(PlayerColor player, TResources resources)=0;
|
||||
|
||||
virtual void giveCreatures(const CArmedInstance *objid, const CGHeroInstance * h, const CCreatureSet &creatures, bool remove) =0;
|
||||
virtual void takeCreatures(ObjectInstanceID objid, const std::vector<CStackBasicDescriptor> &creatures) =0;
|
||||
virtual void takeCreatures(ObjectInstanceID objid, const std::vector<CStackBasicDescriptor> &creatures, bool forceRemoval = false) =0;
|
||||
virtual bool changeStackCount(const StackLocation &sl, TQuantity count, bool absoluteValue = false) =0;
|
||||
virtual bool changeStackType(const StackLocation &sl, const CCreature *c) =0;
|
||||
virtual bool insertNewStack(const StackLocation &sl, const CCreature *c, TQuantity count = -1) =0; //count -1 => moves whole stack
|
||||
|
||||
@@ -63,6 +63,7 @@ enum class EGameSettings
|
||||
MAP_FORMAT_JSON_VCMI,
|
||||
MAP_FORMAT_RESTORATION_OF_ERATHIA,
|
||||
MAP_FORMAT_SHADOW_OF_DEATH,
|
||||
MAP_OBJECTS_H3_BUG_QUEST_TAKES_ENTIRE_ARMY,
|
||||
MARKETS_BLACK_MARKET_RESTOCK_PERIOD,
|
||||
MODULE_COMMANDERS,
|
||||
MODULE_STACK_ARTIFACT,
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "../texts/CGeneralTextHandler.h"
|
||||
#include "CGCreature.h"
|
||||
#include "../IGameCallback.h"
|
||||
#include "../IGameSettings.h"
|
||||
#include "../entities/artifact/CArtifact.h"
|
||||
#include "../entities/hero/CHeroHandler.h"
|
||||
#include "../mapObjectConstructors/CObjectClassesHandler.h"
|
||||
@@ -118,7 +119,7 @@ bool CQuest::checkQuest(const CGHeroInstance * h) const
|
||||
return true;
|
||||
}
|
||||
|
||||
void CQuest::completeQuest(IGameCallback * cb, const CGHeroInstance *h) const
|
||||
void CQuest::completeQuest(IGameCallback * cb, const CGHeroInstance *h, bool allowFullArmyRemoval) const
|
||||
{
|
||||
// FIXME: this should be part of 'reward', and not hacking into limiter state that should only limit access to such reward
|
||||
|
||||
@@ -152,7 +153,7 @@ void CQuest::completeQuest(IGameCallback * cb, const CGHeroInstance *h) const
|
||||
logGlobal->error("Failed to find artifact %s in inventory of hero %s", elem.toEntity(LIBRARY)->getJsonKey(), h->getHeroTypeID());
|
||||
}
|
||||
|
||||
cb->takeCreatures(h->id, mission.creatures);
|
||||
cb->takeCreatures(h->id, mission.creatures, allowFullArmyRemoval);
|
||||
cb->giveResources(h->getOwner(), -mission.resources);
|
||||
}
|
||||
|
||||
@@ -435,7 +436,8 @@ void CGSeerHut::init(vstd::RNG & rand)
|
||||
seerName = LIBRARY->generaltexth->translate(seerNameID);
|
||||
getQuest().textOption = rand.nextInt(2);
|
||||
getQuest().completedOption = rand.nextInt(1, 3);
|
||||
|
||||
getQuest().mission.hasExtraCreatures = !allowsFullArmyRemoval();
|
||||
|
||||
configuration.canRefuse = true;
|
||||
configuration.visitMode = Rewardable::EVisitMode::VISIT_ONCE;
|
||||
configuration.selectMode = Rewardable::ESelectMode::SELECT_PLAYER;
|
||||
@@ -645,14 +647,21 @@ const CGCreature * CGSeerHut::getCreatureToKill(bool allowNull) const
|
||||
return dynamic_cast<const CGCreature *>(o);
|
||||
}
|
||||
|
||||
bool CGSeerHut::allowsFullArmyRemoval() const
|
||||
{
|
||||
bool seerGivesUnits = !configuration.info.empty() && !configuration.info.back().reward.creatures.empty();
|
||||
bool h3BugSettingEnabled = cb->getSettings().getBoolean(EGameSettings::MAP_OBJECTS_H3_BUG_QUEST_TAKES_ENTIRE_ARMY);
|
||||
return seerGivesUnits || h3BugSettingEnabled;
|
||||
}
|
||||
|
||||
void CGSeerHut::blockingDialogAnswered(const CGHeroInstance *hero, int32_t answer) const
|
||||
{
|
||||
CRewardableObject::blockingDialogAnswered(hero, answer);
|
||||
if(answer)
|
||||
{
|
||||
getQuest().completeQuest(cb, hero);
|
||||
getQuest().completeQuest(cb, hero, allowsFullArmyRemoval());
|
||||
cb->setObjPropertyValue(id, ObjProperty::SEERHUT_COMPLETE, !getQuest().repeatedQuest); //mission complete
|
||||
}
|
||||
CRewardableObject::blockingDialogAnswered(hero, answer);
|
||||
}
|
||||
|
||||
void CGSeerHut::serializeJsonOptions(JsonSerializeFormat & handler)
|
||||
@@ -736,6 +745,7 @@ void CGQuestGuard::init(vstd::RNG & rand)
|
||||
blockVisit = true;
|
||||
getQuest().textOption = rand.nextInt(3, 5);
|
||||
getQuest().completedOption = rand.nextInt(4, 5);
|
||||
getQuest().mission.hasExtraCreatures = !allowsFullArmyRemoval();
|
||||
|
||||
configuration.info.push_back({});
|
||||
configuration.info.back().visitType = Rewardable::EEventType::EVENT_FIRST_VISIT;
|
||||
|
||||
@@ -80,7 +80,7 @@ public:
|
||||
void getVisitText(const CGameInfoCallback * cb, MetaString &text, std::vector<Component> & components, bool FirstVisit, const CGHeroInstance * h = nullptr) const;
|
||||
void getCompletionText(const CGameInfoCallback * cb, MetaString &text) const;
|
||||
void getRolloverText (const CGameInfoCallback * cb, MetaString &text, bool onHover) const; //hover or quest log entry
|
||||
void completeQuest(IGameCallback *, const CGHeroInstance * h) const;
|
||||
void completeQuest(IGameCallback *, const CGHeroInstance * h, bool allowFullArmyRemoval) const;
|
||||
void addTextReplacements(const CGameInfoCallback * cb, MetaString &out, std::vector<Component> & components) const;
|
||||
void addKillTargetReplacements(MetaString &out) const;
|
||||
void defineQuestName();
|
||||
@@ -166,6 +166,7 @@ public:
|
||||
h & seerName;
|
||||
}
|
||||
protected:
|
||||
bool allowsFullArmyRemoval() const;
|
||||
void setPropertyDer(ObjProperty what, ObjPropertyID identifier) override;
|
||||
|
||||
void serializeJsonOptions(JsonSerializeFormat & handler) override;
|
||||
|
||||
@@ -2429,7 +2429,6 @@ EQuestMission CMapLoaderH3M::readQuest(IQuestObject * guard, const int3 & positi
|
||||
{
|
||||
size_t typeNumber = reader->readUInt8();
|
||||
guard->getQuest().mission.creatures.resize(typeNumber);
|
||||
guard->getQuest().mission.hasExtraCreatures = true;
|
||||
for(size_t hh = 0; hh < typeNumber; ++hh)
|
||||
{
|
||||
guard->getQuest().mission.creatures[hh].setType(reader->readCreature().toCreature());
|
||||
|
||||
@@ -218,7 +218,7 @@ void Rewardable::Interface::grantRewardAfterLevelup(const Rewardable::VisitInfo
|
||||
|
||||
if (!info.reward.takenCreatures.empty())
|
||||
{
|
||||
cb->takeCreatures(hero->id, info.reward.takenCreatures);
|
||||
cb->takeCreatures(hero->id, info.reward.takenCreatures, !info.reward.creatures.empty());
|
||||
}
|
||||
|
||||
if(!info.reward.creaturesChange.empty())
|
||||
|
||||
@@ -1157,26 +1157,36 @@ void CGameHandler::giveCreatures(const CArmedInstance *obj, const CGHeroInstance
|
||||
tryJoiningArmy(obj, h, remove, true);
|
||||
}
|
||||
|
||||
void CGameHandler::takeCreatures(ObjectInstanceID objid, const std::vector<CStackBasicDescriptor> &creatures)
|
||||
void CGameHandler::takeCreatures(ObjectInstanceID objid, const std::vector<CStackBasicDescriptor> &creatures, bool forceRemoval)
|
||||
{
|
||||
std::vector<CStackBasicDescriptor> cres = creatures;
|
||||
if (cres.size() <= 0)
|
||||
std::vector<CStackBasicDescriptor> remainerForTaking = creatures;
|
||||
if (remainerForTaking.empty())
|
||||
return;
|
||||
const CArmedInstance* obj = static_cast<const CArmedInstance*>(getObj(objid));
|
||||
|
||||
for (CStackBasicDescriptor &sbd : cres)
|
||||
const CArmedInstance* army = static_cast<const CArmedInstance*>(getObj(objid));
|
||||
|
||||
for (const CStackBasicDescriptor &stackToTake : remainerForTaking)
|
||||
{
|
||||
TQuantity collected = 0;
|
||||
while(collected < sbd.getCount())
|
||||
while(collected < stackToTake.getCount())
|
||||
{
|
||||
bool foundSth = false;
|
||||
for (auto i = obj->Slots().begin(); i != obj->Slots().end(); i++)
|
||||
for (const auto & armySlot : army->Slots())
|
||||
{
|
||||
if (i->second->getType() == sbd.getType())
|
||||
if (armySlot.second->getType() == stackToTake.getType())
|
||||
{
|
||||
TQuantity take = std::min(sbd.getCount() - collected, i->second->getCount()); //collect as much cres as we can
|
||||
changeStackCount(StackLocation(obj->id, i->first), -take, false);
|
||||
collected += take;
|
||||
if (stackToTake.getCount() - collected >= armySlot.second->getCount())
|
||||
{
|
||||
// take entire stack
|
||||
collected += armySlot.second->getCount();
|
||||
eraseStack(StackLocation(army->id, armySlot.first), forceRemoval);
|
||||
}
|
||||
else
|
||||
{
|
||||
// take part of the stack
|
||||
collected = stackToTake.getCount();
|
||||
changeStackCount(StackLocation(army->id, armySlot.first), collected - stackToTake.getCount(), false);
|
||||
}
|
||||
foundSth = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ public:
|
||||
void giveResources(PlayerColor player, TResources resources) override;
|
||||
|
||||
void giveCreatures(const CArmedInstance *objid, const CGHeroInstance * h, const CCreatureSet &creatures, bool remove) override;
|
||||
void takeCreatures(ObjectInstanceID objid, const std::vector<CStackBasicDescriptor> &creatures) override;
|
||||
void takeCreatures(ObjectInstanceID objid, const std::vector<CStackBasicDescriptor> &creatures, bool forceRemoval) override;
|
||||
bool changeStackType(const StackLocation &sl, const CCreature *c) override;
|
||||
bool changeStackCount(const StackLocation &sl, TQuantity count, bool absoluteValue = false) override;
|
||||
bool insertNewStack(const StackLocation &sl, const CCreature *c, TQuantity count) override;
|
||||
|
||||
@@ -62,7 +62,7 @@ public:
|
||||
void giveResources(PlayerColor player, TResources resources) override {}
|
||||
|
||||
void giveCreatures(const CArmedInstance *objid, const CGHeroInstance * h, const CCreatureSet &creatures, bool remove) override {}
|
||||
void takeCreatures(ObjectInstanceID objid, const std::vector<CStackBasicDescriptor> &creatures) override {}
|
||||
void takeCreatures(ObjectInstanceID objid, const std::vector<CStackBasicDescriptor> &creatures, bool forceRemoval) override {}
|
||||
bool changeStackCount(const StackLocation &sl, TQuantity count, bool absoluteValue = false) override {return false;}
|
||||
bool changeStackType(const StackLocation &sl, const CCreature *c) override {return false;}
|
||||
bool insertNewStack(const StackLocation &sl, const CCreature *c, TQuantity count = -1) override {return false;} //count -1 => moves whole stack
|
||||
|
||||
Reference in New Issue
Block a user