1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

New limiter based quests

This commit is contained in:
nordsoft 2023-10-09 05:24:40 +02:00
parent b7568a160c
commit 1460541ee5
9 changed files with 205 additions and 236 deletions

View File

@ -63,9 +63,6 @@ TGoalVec CompleteQuest::decompose() const
return missionLevel();
case CQuest::MISSION_PLAYER:
if(ai->playerID.getNum() != q.quest->m13489val)
logAi->debug("Can't be player of color %d", q.quest->m13489val);
break;
case CQuest::MISSION_KEYMASTER:
@ -137,7 +134,7 @@ TGoalVec CompleteQuest::missionArt() const
CaptureObjectsBehavior findArts;
for(auto art : q.quest->m5arts)
for(auto art : q.quest->artifacts)
{
solutions.push_back(sptr(CaptureObjectsBehavior().ofType(Obj::ARTIFACT, art)));
}
@ -223,7 +220,7 @@ TGoalVec CompleteQuest::missionResources() const
TGoalVec CompleteQuest::missionDestroyObj() const
{
auto obj = cb->getObjByQuestIdentifier(q.quest->m13489val);
auto obj = cb->getObjByQuestIdentifier(q.quest->killTarget);
if(!obj)
return CaptureObjectsBehavior(q.obj).decompose();

View File

@ -54,9 +54,6 @@ TGoalVec CompleteQuest::getAllPossibleSubgoals()
return missionLevel();
case CQuest::MISSION_PLAYER:
if(ai->playerID.getNum() != q.quest->m13489val)
logAi->debug("Can't be player of color %d", q.quest->m13489val);
break;
case CQuest::MISSION_KEYMASTER:
@ -137,7 +134,7 @@ TGoalVec CompleteQuest::missionArt() const
if(!solutions.empty())
return solutions;
for(auto art : q.quest->m5arts)
for(auto art : q.quest->artifacts)
{
solutions.push_back(sptr(GetArtOfType(art))); //TODO: transport?
}
@ -165,7 +162,7 @@ TGoalVec CompleteQuest::missionArmy() const
if(!solutions.empty())
return solutions;
for(auto creature : q.quest->m6creatures)
for(auto creature : q.quest->creatures)
{
solutions.push_back(sptr(GatherTroops(creature.type->getId(), creature.count)));
}
@ -179,7 +176,7 @@ TGoalVec CompleteQuest::missionIncreasePrimaryStat() const
if(solutions.empty())
{
for(int i = 0; i < q.quest->m2stats.size(); ++i)
for(int i = 0; i < q.quest->primary.size(); ++i)
{
// TODO: library, school and other boost objects
logAi->debug("Don't know how to increase primary stat %d", i);
@ -195,7 +192,7 @@ TGoalVec CompleteQuest::missionLevel() const
if(solutions.empty())
{
logAi->debug("Don't know how to reach hero level %d", q.quest->m13489val);
logAi->debug("Don't know how to reach hero level %d", q.quest->heroLevel);
}
return solutions;
@ -227,10 +224,10 @@ TGoalVec CompleteQuest::missionResources() const
}
else
{
for(int i = 0; i < q.quest->m7resources.size(); ++i)
for(int i = 0; i < q.quest->resources.size(); ++i)
{
if(q.quest->m7resources[i])
solutions.push_back(sptr(CollectRes(static_cast<EGameResID>(i), q.quest->m7resources[i])));
if(q.quest->resources[i])
solutions.push_back(sptr(CollectRes(static_cast<EGameResID>(i), q.quest->resources[i])));
}
}
}
@ -246,7 +243,7 @@ TGoalVec CompleteQuest::missionDestroyObj() const
{
TGoalVec solutions;
auto obj = cb->getObjByQuestIdentifier(q.quest->m13489val);
auto obj = cb->getObjByQuestIdentifier(q.quest->killTarget);
if(!obj)
return ai->ah->howToVisitObj(q.obj);

View File

@ -40,7 +40,7 @@ CQuest::CQuest():
missionType(MISSION_NONE),
progress(NOT_ACTIVE),
lastDay(-1),
m13489val(0),
killTarget(-1),
textOption(0),
completedOption(0),
stackDirection(0),
@ -99,7 +99,7 @@ bool CQuest::checkMissionArmy(const CQuest * q, const CCreatureSet * army)
ui32 count = 0;
ui32 slotsCount = 0;
bool hasExtraCreatures = false;
for(cre = q->m6creatures.begin(); cre != q->m6creatures.end(); ++cre)
for(cre = q->creatures.begin(); cre != q->creatures.end(); ++cre)
{
for(count = 0, it = army->Slots().begin(); it != army->Slots().end(); ++it)
{
@ -121,110 +121,53 @@ bool CQuest::checkMissionArmy(const CQuest * q, const CCreatureSet * army)
bool CQuest::checkQuest(const CGHeroInstance * h) const
{
switch (missionType)
if(!heroAllowed(h))
return false;
if(killTarget >= 0)
{
case MISSION_NONE:
return true;
case MISSION_LEVEL:
return m13489val <= h->level;
case MISSION_PRIMARY_STAT:
for(int i = 0; i < GameConstants::PRIMARY_SKILLS; ++i)
{
if(h->getPrimSkillLevel(static_cast<PrimarySkill>(i)) < static_cast<int>(m2stats[i]))
return false;
}
return true;
case MISSION_KILL_HERO:
case MISSION_KILL_CREATURE:
if(!CGHeroInstance::cb->getObjByQuestIdentifier(m13489val))
return true;
return false;
case MISSION_ART:
{
// if the object was deserialized
if(artifactsRequirements.empty())
for(const auto & id : m5arts)
++artifactsRequirements[id];
size_t reqSlots = 0;
for(const auto & elem : artifactsRequirements)
{
// check required amount of artifacts
if(h->getArtPosCount(elem.first, false, true, true) < elem.second)
return false;
if(!h->hasArt(elem.first))
reqSlots += h->getAssemblyByConstituent(elem.first)->getPartsInfo().size() - 2;
}
if(ArtifactUtils::isBackpackFreeSlots(h, reqSlots))
return true;
else
return false;
}
case MISSION_ARMY:
return checkMissionArmy(this, h);
case MISSION_RESOURCES:
for(GameResID i = EGameResID::WOOD; i <= EGameResID::GOLD; ++i) //including Mithril ?
{ //Quest has no direct access to callback
if(CGHeroInstance::cb->getResource(h->tempOwner, i) < static_cast<int>(m7resources[i]))
return false;
}
return true;
case MISSION_HERO:
return m13489val == h->type->getIndex();
case MISSION_PLAYER:
return m13489val == h->getOwner().getNum();
default:
if(!CGHeroInstance::cb->getObjByQuestIdentifier(killTarget))
return false;
}
return true;
}
void CQuest::completeQuest(IGameCallback * cb, const CGHeroInstance *h) const
{
switch (missionType)
for(auto & elem : artifacts)
{
case CQuest::MISSION_ART:
for(auto & elem : m5arts)
{
if(h->hasArt(elem))
{
cb->removeArtifact(ArtifactLocation(h, h->getArtPos(elem, false)));
}
else
{
const auto * assembly = h->getAssemblyByConstituent(elem);
assert(assembly);
auto parts = assembly->getPartsInfo();
if(h->hasArt(elem))
{
cb->removeArtifact(ArtifactLocation(h, h->getArtPos(elem, false)));
}
else
{
const auto * assembly = h->getAssemblyByConstituent(elem);
assert(assembly);
auto parts = assembly->getPartsInfo();
// Remove the assembly
cb->removeArtifact(ArtifactLocation(h, h->getArtPos(assembly)));
// Remove the assembly
cb->removeArtifact(ArtifactLocation(h, h->getArtPos(assembly)));
// Disassemble this backpack artifact
for(const auto & ci : parts)
{
if(ci.art->getTypeId() != elem)
cb->giveHeroNewArtifact(h, ci.art->artType, ArtifactPosition::BACKPACK_START);
}
}
}
break;
case CQuest::MISSION_ARMY:
cb->takeCreatures(h->id, m6creatures);
break;
case CQuest::MISSION_RESOURCES:
for (int i = 0; i < 7; ++i)
// Disassemble this backpack artifact
for(const auto & ci : parts)
{
cb->giveResource(h->getOwner(), static_cast<EGameResID>(i), -static_cast<int>(m7resources[i]));
if(ci.art->getTypeId() != elem)
cb->giveHeroNewArtifact(h, ci.art->artType, ArtifactPosition::BACKPACK_START);
}
break;
default:
break;
}
}
cb->takeCreatures(h->id, creatures);
cb->giveResources(h->getOwner(), resources);
}
void CQuest::getVisitText(MetaString &iwText, std::vector<Component> &components, bool isCustom, bool firstVisit, const CGHeroInstance * h) const
{
MetaString text;
bool failRequirements = (h ? !checkQuest(h) : true);
loadComponents(components, h);
if(firstVisit)
{
@ -241,20 +184,18 @@ void CQuest::getVisitText(MetaString &iwText, std::vector<Component> &components
switch (missionType)
{
case MISSION_LEVEL:
components.emplace_back(Component::EComponentType::EXPERIENCE, 0, m13489val, 0);
if(!isCustom)
iwText.replaceNumber(m13489val);
iwText.replaceNumber(heroLevel); //TODO: heroLevel
break;
case MISSION_PRIMARY_STAT:
{
MetaString loot;
for(int i = 0; i < 4; ++i)
{
if(m2stats[i])
if(primary[i])
{
components.emplace_back(Component::EComponentType::PRIM_SKILL, i, m2stats[i], 0);
loot.appendRawString("%d %s");
loot.replaceNumber(m2stats[i]);
loot.replaceNumber(primary[i]);
loot.replaceRawString(VLC->generaltexth->primarySkillNames[i]);
}
}
@ -268,10 +209,8 @@ void CQuest::getVisitText(MetaString &iwText, std::vector<Component> &components
addReplacements(iwText, text.toString());
break;
case MISSION_HERO:
//FIXME: portrait may not match hero, if custom portrait was set in map editor
components.emplace_back(Component::EComponentType::HERO_PORTRAIT, VLC->heroh->objects[m13489val]->imageIndex, 0, 0);
if(!isCustom)
iwText.replaceRawString(VLC->heroh->objects[m13489val]->getNameTranslated());
if(!isCustom && !heroes.empty())
iwText.replaceRawString(VLC->heroh->getById(heroes.front())->getNameTranslated());
break;
case MISSION_KILL_CREATURE:
{
@ -285,9 +224,8 @@ void CQuest::getVisitText(MetaString &iwText, std::vector<Component> &components
case MISSION_ART:
{
MetaString loot;
for(const auto & elem : m5arts)
for(const auto & elem : artifacts)
{
components.emplace_back(Component::EComponentType::ARTIFACT, elem, 0, 0);
loot.appendRawString("%s");
loot.replaceLocalString(EMetaText::ART_NAMES, elem);
}
@ -298,9 +236,8 @@ void CQuest::getVisitText(MetaString &iwText, std::vector<Component> &components
case MISSION_ARMY:
{
MetaString loot;
for(const auto & elem : m6creatures)
for(const auto & elem : creatures)
{
components.emplace_back(elem);
loot.appendRawString("%s");
loot.replaceCreatureName(elem);
}
@ -313,11 +250,10 @@ void CQuest::getVisitText(MetaString &iwText, std::vector<Component> &components
MetaString loot;
for(int i = 0; i < 7; ++i)
{
if(m7resources[i])
if(resources[i])
{
components.emplace_back(Component::EComponentType::RESOURCE, i, m7resources[i], 0);
loot.appendRawString("%d %s");
loot.replaceNumber(m7resources[i]);
loot.replaceNumber(resources[i]);
loot.replaceLocalString(EMetaText::RES_NAMES, i);
}
}
@ -326,9 +262,8 @@ void CQuest::getVisitText(MetaString &iwText, std::vector<Component> &components
}
break;
case MISSION_PLAYER:
components.emplace_back(Component::EComponentType::FLAG, m13489val, 0, 0);
if(!isCustom)
iwText.replaceLocalString(EMetaText::COLOR, m13489val);
if(!isCustom && !players.empty())
iwText.replaceLocalString(EMetaText::COLOR, players.front());
break;
}
}
@ -349,17 +284,17 @@ void CQuest::getRolloverText(MetaString &ms, bool onHover) const
switch(missionType)
{
case MISSION_LEVEL:
ms.replaceNumber(m13489val);
ms.replaceNumber(heroLevel);
break;
case MISSION_PRIMARY_STAT:
{
MetaString loot;
for (int i = 0; i < 4; ++i)
{
if (m2stats[i])
if (primary[i])
{
loot.appendRawString("%d %s");
loot.replaceNumber(m2stats[i]);
loot.replaceNumber(primary[i]);
loot.replaceRawString(VLC->generaltexth->primarySkillNames[i]);
}
}
@ -375,7 +310,7 @@ void CQuest::getRolloverText(MetaString &ms, bool onHover) const
case MISSION_ART:
{
MetaString loot;
for(const auto & elem : m5arts)
for(const auto & elem : artifacts)
{
loot.appendRawString("%s");
loot.replaceLocalString(EMetaText::ART_NAMES, elem);
@ -386,7 +321,7 @@ void CQuest::getRolloverText(MetaString &ms, bool onHover) const
case MISSION_ARMY:
{
MetaString loot;
for(const auto & elem : m6creatures)
for(const auto & elem : creatures)
{
loot.appendRawString("%s");
loot.replaceCreatureName(elem);
@ -399,10 +334,10 @@ void CQuest::getRolloverText(MetaString &ms, bool onHover) const
MetaString loot;
for (int i = 0; i < 7; ++i)
{
if (m7resources[i])
if (resources[i])
{
loot.appendRawString("%d %s");
loot.replaceNumber(m7resources[i]);
loot.replaceNumber(resources[i]);
loot.replaceLocalString(EMetaText::RES_NAMES, i);
}
}
@ -410,10 +345,10 @@ void CQuest::getRolloverText(MetaString &ms, bool onHover) const
}
break;
case MISSION_HERO:
ms.replaceRawString(VLC->heroh->objects[m13489val]->getNameTranslated());
ms.replaceRawString(VLC->heroh->getById(heroes.front())->getNameTranslated());
break;
case MISSION_PLAYER:
ms.replaceRawString(VLC->generaltexth->colors[m13489val]);
ms.replaceRawString(VLC->generaltexth->colors[players.front()]);
break;
default:
break;
@ -427,18 +362,18 @@ void CQuest::getCompletionText(MetaString &iwText) const
{
case CQuest::MISSION_LEVEL:
if (!isCustomComplete)
iwText.replaceNumber(m13489val);
iwText.replaceNumber(heroLevel);
break;
case CQuest::MISSION_PRIMARY_STAT:
{
MetaString loot;
assert(m2stats.size() <= 4);
for (int i = 0; i < m2stats.size(); ++i)
assert(primary.size() <= 4);
for (int i = 0; i < primary.size(); ++i)
{
if (m2stats[i])
if (primary[i])
{
loot.appendRawString("%d %s");
loot.replaceNumber(m2stats[i]);
loot.replaceNumber(primary[i]);
loot.replaceRawString(VLC->generaltexth->primarySkillNames[i]);
}
}
@ -449,7 +384,7 @@ void CQuest::getCompletionText(MetaString &iwText) const
case CQuest::MISSION_ART:
{
MetaString loot;
for(const auto & elem : m5arts)
for(const auto & elem : artifacts)
{
loot.appendRawString("%s");
loot.replaceLocalString(EMetaText::ART_NAMES, elem);
@ -461,7 +396,7 @@ void CQuest::getCompletionText(MetaString &iwText) const
case CQuest::MISSION_ARMY:
{
MetaString loot;
for(const auto & elem : m6creatures)
for(const auto & elem : creatures)
{
loot.appendRawString("%s");
loot.replaceCreatureName(elem);
@ -475,10 +410,10 @@ void CQuest::getCompletionText(MetaString &iwText) const
MetaString loot;
for (int i = 0; i < 7; ++i)
{
if (m7resources[i])
if (resources[i])
{
loot.appendRawString("%d %s");
loot.replaceNumber(m7resources[i]);
loot.replaceNumber(resources[i]);
loot.replaceLocalString(EMetaText::RES_NAMES, i);
}
}
@ -492,22 +427,16 @@ void CQuest::getCompletionText(MetaString &iwText) const
addReplacements(iwText, completedText.toString());
break;
case MISSION_HERO:
if (!isCustomComplete)
iwText.replaceRawString(VLC->heroh->objects[m13489val]->getNameTranslated());
if (!isCustomComplete && !heroes.empty())
iwText.replaceRawString(VLC->heroh->getById(heroes.front())->getNameTranslated());
break;
case MISSION_PLAYER:
if (!isCustomComplete)
iwText.replaceRawString(VLC->generaltexth->colors[m13489val]);
if (!isCustomComplete && !players.empty())
iwText.replaceRawString(VLC->generaltexth->colors[players.front()]);
break;
}
}
void CQuest::addArtifactID(const ArtifactID & id)
{
m5arts.push_back(id);
++artifactsRequirements[id];
}
void CQuest::serializeJson(JsonSerializeFormat & handler, const std::string & fieldName)
{
auto q = handler.enterStruct(fieldName);
@ -522,6 +451,8 @@ void CQuest::serializeJson(JsonSerializeFormat & handler, const std::string & fi
isCustomNext = !nextVisitText.empty();
isCustomComplete = !completedText.empty();
}
Rewardable::Limiter::serializeJson(handler);
static const std::vector<std::string> MISSION_TYPE_JSON =
{
@ -530,57 +461,64 @@ void CQuest::serializeJson(JsonSerializeFormat & handler, const std::string & fi
handler.serializeEnum("missionType", missionType, Emission::MISSION_NONE, MISSION_TYPE_JSON);
handler.serializeInt("timeLimit", lastDay, -1);
handler.serializeInstance<int>("killTarget", killTarget, -1);
switch (missionType)
if(!handler.saving)
{
case MISSION_NONE:
break;
case MISSION_LEVEL:
handler.serializeInt("heroLevel", m13489val, -1);
break;
case MISSION_PRIMARY_STAT:
switch (missionType)
{
auto primarySkills = handler.enterStruct("primarySkills");
if(!handler.saving)
m2stats.resize(GameConstants::PRIMARY_SKILLS);
for(int i = 0; i < GameConstants::PRIMARY_SKILLS; ++i)
handler.serializeInt(NPrimarySkill::names[i], m2stats[i], 0);
}
break;
case MISSION_KILL_HERO:
case MISSION_KILL_CREATURE:
handler.serializeInstance<ui32>("killTarget", m13489val, static_cast<ui32>(-1));
break;
case MISSION_ART:
//todo: ban artifacts
handler.serializeIdArray<ArtifactID>("artifacts", m5arts);
break;
case MISSION_ARMY:
{
auto a = handler.enterArray("creatures");
a.serializeStruct(m6creatures);
}
break;
case MISSION_RESOURCES:
{
auto r = handler.enterStruct("resources");
for(size_t idx = 0; idx < (GameConstants::RESOURCE_QUANTITY - 1); idx++)
case MISSION_NONE:
break;
case MISSION_LEVEL:
handler.serializeInt("heroLevel", heroLevel, -1);
break;
case MISSION_PRIMARY_STAT:
{
auto primarySkills = handler.enterStruct("primarySkills");
for(int i = 0; i < GameConstants::PRIMARY_SKILLS; ++i)
handler.serializeInt(NPrimarySkill::names[i], primary[i], 0);
}
break;
case MISSION_KILL_HERO:
case MISSION_KILL_CREATURE:
break;
case MISSION_ART:
handler.serializeIdArray<ArtifactID>("artifacts", artifacts);
break;
case MISSION_ARMY:
{
handler.serializeInt(GameConstants::RESOURCE_NAMES[idx], m7resources[idx], 0);
auto a = handler.enterArray("creatures");
a.serializeStruct(creatures);
}
break;
case MISSION_RESOURCES:
{
auto r = handler.enterStruct("resources");
for(size_t idx = 0; idx < (GameConstants::RESOURCE_QUANTITY - 1); idx++)
{
handler.serializeInt(GameConstants::RESOURCE_NAMES[idx], resources[idx], 0);
}
}
break;
case MISSION_HERO:
{
ui32 temp;
handler.serializeId<ui32, ui32, HeroTypeID>("hero", temp, 0);
heroes.emplace_back(temp);
}
break;
case MISSION_PLAYER:
{
ui32 temp;
handler.serializeId<ui32, ui32, PlayerColor>("player", temp, PlayerColor::NEUTRAL);
players.emplace_back(temp);
}
break;
default:
logGlobal->error("Invalid quest mission type");
break;
}
break;
case MISSION_HERO:
handler.serializeId<ui32, ui32, HeroTypeID>("hero", m13489val, 0);
break;
case MISSION_PLAYER:
handler.serializeId<ui32, ui32, PlayerColor>("player", m13489val, PlayerColor::NEUTRAL);
break;
default:
logGlobal->error("Invalid quest mission type");
break;
}
}
@ -803,7 +741,7 @@ int CGSeerHut::checkDirection() const
const CGHeroInstance * CGSeerHut::getHeroToKill(bool allowNull) const
{
const CGObjectInstance *o = cb->getObjByQuestIdentifier(quest->m13489val);
const CGObjectInstance *o = cb->getObjByQuestIdentifier(quest->killTarget);
if(allowNull && !o)
return nullptr;
assert(o && (o->ID == Obj::HERO || o->ID == Obj::PRISON));
@ -812,7 +750,7 @@ const CGHeroInstance * CGSeerHut::getHeroToKill(bool allowNull) const
const CGCreature * CGSeerHut::getCreatureToKill(bool allowNull) const
{
const CGObjectInstance *o = cb->getObjByQuestIdentifier(quest->m13489val);
const CGObjectInstance *o = cb->getObjByQuestIdentifier(quest->killTarget);
if(allowNull && !o)
return nullptr;
assert(o && o->ID == Obj::MONSTER);

View File

@ -17,10 +17,8 @@ VCMI_LIB_NAMESPACE_BEGIN
class CGCreature;
class DLL_LINKAGE CQuest final
class DLL_LINKAGE CQuest: public Rewardable::Limiter
{
mutable std::unordered_map<ArtifactID, unsigned, ArtifactID::hash> artifactsRequirements; // artifact ID -> required count
public:
enum Emission {
MISSION_NONE = 0,
@ -54,12 +52,7 @@ public:
Emission missionType;
Eprogress progress;
si32 lastDay; //after this day (first day is 0) mission cannot be completed; if -1 - no limit
ui32 m13489val;
std::vector<ui32> m2stats;
std::vector<ArtifactID> m5arts; // artifact IDs. Add IDs through addArtifactID(), not directly to the field.
std::vector<CStackBasicDescriptor> m6creatures; //pair[cre id, cre count], CreatureSet info irrelevant
TResources m7resources;
int killTarget;
// following fields are used only for kill creature/hero missions, the original
// objects became inaccessible after their removal, so we need to store info
@ -85,7 +78,6 @@ public:
virtual void getRolloverText (MetaString &text, bool onHover) const; //hover or quest log entry
virtual void completeQuest(IGameCallback *, const CGHeroInstance * h) const;
virtual void addReplacements(MetaString &out, const std::string &base) const;
void addArtifactID(const ArtifactID & id);
bool operator== (const CQuest & quest) const
{
@ -98,11 +90,6 @@ public:
h & missionType;
h & progress;
h & lastDay;
h & m13489val;
h & m2stats;
h & m5arts;
h & m6creatures;
h & m7resources;
h & textOption;
h & stackToKill;
h & stackDirection;
@ -115,6 +102,7 @@ public:
h & isCustomNext;
h & isCustomComplete;
h & completedOption;
h & static_cast<Rewardable::Limiter&>(*this);
}
void serializeJson(JsonSerializeFormat & handler, const std::string & fieldName);

View File

@ -1871,7 +1871,7 @@ void CMapLoaderH3M::readSeerHutQuest(CGSeerHut * hut, const int3 & position, con
if(artID != ArtifactID::NONE)
{
//not none quest
hut->quest->addArtifactID(artID);
hut->quest->artifacts.push_back(artID);
hut->quest->missionType = CQuest::MISSION_ART;
}
else
@ -1986,10 +1986,9 @@ void CMapLoaderH3M::readQuest(IQuestObject * guard, const int3 & position)
return;
case CQuest::MISSION_PRIMARY_STAT:
{
guard->quest->m2stats.resize(4);
for(int x = 0; x < 4; ++x)
{
guard->quest->m2stats[x] = reader->readUInt8();
guard->quest->primary[x] = reader->readUInt8();
}
}
break;
@ -1997,7 +1996,7 @@ void CMapLoaderH3M::readQuest(IQuestObject * guard, const int3 & position)
case CQuest::MISSION_KILL_HERO:
case CQuest::MISSION_KILL_CREATURE:
{
guard->quest->m13489val = reader->readUInt32();
guard->quest->killTarget = reader->readUInt32();
break;
}
case CQuest::MISSION_ART:
@ -2006,7 +2005,7 @@ void CMapLoaderH3M::readQuest(IQuestObject * guard, const int3 & position)
for(int yy = 0; yy < artNumber; ++yy)
{
auto artid = reader->readArtifact();
guard->quest->addArtifactID(artid);
guard->quest->artifacts.push_back(artid);
map->allowedArtifact[artid] = false; //these are unavailable for random generation
}
break;
@ -2014,29 +2013,29 @@ void CMapLoaderH3M::readQuest(IQuestObject * guard, const int3 & position)
case CQuest::MISSION_ARMY:
{
int typeNumber = reader->readUInt8();
guard->quest->m6creatures.resize(typeNumber);
guard->quest->creatures.resize(typeNumber);
for(int hh = 0; hh < typeNumber; ++hh)
{
guard->quest->m6creatures[hh].type = VLC->creh->objects[reader->readCreature()];
guard->quest->m6creatures[hh].count = reader->readUInt16();
guard->quest->creatures[hh].type = VLC->creh->objects[reader->readCreature()];
guard->quest->creatures[hh].count = reader->readUInt16();
}
break;
}
case CQuest::MISSION_RESOURCES:
{
for(int x = 0; x < 7; ++x)
guard->quest->m7resources[x] = reader->readUInt32();
guard->quest->resources[x] = reader->readUInt32();
break;
}
case CQuest::MISSION_HERO:
{
guard->quest->m13489val = reader->readHero().getNum();
guard->quest->heroes.push_back(reader->readHero());
break;
}
case CQuest::MISSION_PLAYER:
{
guard->quest->m13489val = reader->readPlayer().getNum();
guard->quest->players.push_back(reader->readPlayer());
break;
}
case CQuest::MISSION_HOTA_MULTI:
@ -2045,22 +2044,19 @@ void CMapLoaderH3M::readQuest(IQuestObject * guard, const int3 & position)
if(missionSubID == 0)
{
guard->quest->missionType = CQuest::MISSION_NONE; //TODO: CQuest::MISSION_HOTA_HERO_CLASS;
guard->quest->missionType = CQuest::MISSION_HOTA_HERO_CLASS;
std::set<HeroClassID> heroClasses;
reader->readBitmaskHeroClassesSized(heroClasses, false);
logGlobal->warn("Map '%s': Quest at %s 'Belong to one of %d classes' is not implemented!", mapName, position.toString(), heroClasses.size());
for(auto & hc : heroClasses)
guard->quest->heroClasses.push_back(hc);
break;
}
if(missionSubID == 1)
{
guard->quest->missionType = CQuest::MISSION_NONE; //TODO: CQuest::MISSION_HOTA_REACH_DATE;
uint32_t daysPassed = reader->readUInt32();
logGlobal->warn("Map '%s': Quest at %s 'Wait till %d days passed' is not implemented!", mapName, position.toString(), daysPassed);
guard->quest->missionType = CQuest::MISSION_HOTA_REACH_DATE;
guard->quest->daysPassed = reader->readUInt32();
break;
}
assert(0);
break;
}
default:

View File

@ -146,6 +146,54 @@ bool Rewardable::Limiter::heroAllowed(const CGHeroInstance * hero) const
return false;
}
void Rewardable::Limiter::loadComponents(std::vector<Component> & comps,
const CGHeroInstance * h) const
{
if (heroExperience)
{
comps.emplace_back(Component::EComponentType::EXPERIENCE, 0, static_cast<si32>(h->calculateXp(heroExperience)), 0);
}
if (heroLevel)
comps.emplace_back(Component::EComponentType::EXPERIENCE, 1, heroLevel, 0);
if (manaPoints || manaPercentage > 0)
comps.emplace_back(Component::EComponentType::PRIM_SKILL, 5, 0, 0);
for (size_t i=0; i<primary.size(); i++)
{
if (primary[i] != 0)
comps.emplace_back(Component::EComponentType::PRIM_SKILL, static_cast<ui16>(i), primary[i], 0);
}
for(const auto & entry : secondary)
comps.emplace_back(Component::EComponentType::SEC_SKILL, entry.first, entry.second, 0);
for(const auto & entry : artifacts)
comps.emplace_back(Component::EComponentType::ARTIFACT, entry, 1, 0);
for(const auto & entry : spells)
comps.emplace_back(Component::EComponentType::SPELL, entry, 1, 0);
for(const auto & entry : creatures)
comps.emplace_back(Component::EComponentType::CREATURE, entry.type->getId(), entry.count, 0);
for(const auto & entry : players)
comps.emplace_back(Component::EComponentType::FLAG, entry, 0, 0);
//FIXME: portrait may not match hero, if custom portrait was set in map editor
for(const auto & entry : heroes)
comps.emplace_back(Component::EComponentType::HERO_PORTRAIT, VLC->heroTypes()->getById(entry)->getIconIndex(), 0, 0);
for(const auto & entry : heroClasses)
comps.emplace_back(Component::EComponentType::HERO_PORTRAIT, VLC->heroClasses()->getById(entry)->getIconIndex(), 0, 0);
for (size_t i=0; i<resources.size(); i++)
{
if (resources[i] !=0)
comps.emplace_back(Component::EComponentType::RESOURCE, static_cast<ui16>(i), resources[i], 0);
}
}
void Rewardable::Limiter::serializeJson(JsonSerializeFormat & handler)
{
handler.serializeInt("dayOfWeek", dayOfWeek);

View File

@ -17,6 +17,7 @@ VCMI_LIB_NAMESPACE_BEGIN
class CGHeroInstance;
class CStackBasicDescriptor;
struct Component;
namespace Rewardable {
@ -78,9 +79,13 @@ struct DLL_LINKAGE Limiter
LimitersList noneOf;
Limiter();
~Limiter();
virtual ~Limiter();
bool heroAllowed(const CGHeroInstance * hero) const;
/// Generates list of components that describes reward for a specific hero
virtual void loadComponents(std::vector<Component> & comps,
const CGHeroInstance * h) const;
template <typename Handler> void serialize(Handler &h, const int version)
{

View File

@ -86,7 +86,7 @@ struct DLL_LINKAGE Reward
si32 calculateManaPoints(const CGHeroInstance * h) const;
Reward();
~Reward();
virtual ~Reward();
template <typename Handler> void serialize(Handler &h, const int version)
{

View File

@ -464,7 +464,7 @@ void TreasurePlacer::addAllPossibleObjects()
obj->quest->missionType = CQuest::MISSION_ART;
ArtifactID artid = qap->drawRandomArtifact();
obj->quest->addArtifactID(artid);
obj->quest->artifacts.push_back(artid);
obj->quest->lastDay = -1;
obj->quest->isCustomFirst = obj->quest->isCustomNext = obj->quest->isCustomComplete = false;
@ -515,7 +515,7 @@ void TreasurePlacer::addAllPossibleObjects()
obj->quest->missionType = CQuest::MISSION_ART;
ArtifactID artid = qap->drawRandomArtifact();
obj->quest->addArtifactID(artid);
obj->quest->artifacts.push_back(artid);
obj->quest->lastDay = -1;
obj->quest->isCustomFirst = obj->quest->isCustomNext = obj->quest->isCustomComplete = false;
@ -540,7 +540,7 @@ void TreasurePlacer::addAllPossibleObjects()
obj->quest->missionType = CQuest::MISSION_ART;
ArtifactID artid = qap->drawRandomArtifact();
obj->quest->addArtifactID(artid);
obj->quest->artifacts.push_back(artid);
obj->quest->lastDay = -1;
obj->quest->isCustomFirst = obj->quest->isCustomNext = obj->quest->isCustomComplete = false;