From c1c13cfafbae674aa3f09fae8b7a844ce5332f71 Mon Sep 17 00:00:00 2001 From: nordsoft Date: Fri, 15 Sep 2023 15:29:41 +0200 Subject: [PATCH] Pandora and events work as rewardable object --- lib/mapObjects/CGPandoraBox.cpp | 520 +++++++----------------- lib/mapObjects/CGPandoraBox.h | 42 +- lib/mapObjects/CQuest.cpp | 30 +- lib/mapObjects/CQuest.h | 6 +- lib/mapObjects/CRewardableObject.cpp | 1 - lib/mapping/MapFormatH3M.cpp | 48 ++- lib/rmg/modificators/TreasurePlacer.cpp | 64 ++- 7 files changed, 236 insertions(+), 475 deletions(-) diff --git a/lib/mapObjects/CGPandoraBox.cpp b/lib/mapObjects/CGPandoraBox.cpp index 9a4dd89e8..d4787f306 100644 --- a/lib/mapObjects/CGPandoraBox.cpp +++ b/lib/mapObjects/CGPandoraBox.cpp @@ -25,297 +25,117 @@ VCMI_LIB_NAMESPACE_BEGIN +void CGPandoraBox::init() +{ + blockVisit = true; + + for(auto & i : configuration.info) + i.reward.removeObject = true; +} + void CGPandoraBox::initObj(CRandomGenerator & rand) { - blockVisit = (ID==Obj::PANDORAS_BOX); //block only if it's really pandora's box (events also derive from that class) - hasGuardians = stacks.size(); + init(); + + CRewardableObject::initObj(rand); } void CGPandoraBox::onHeroVisit(const CGHeroInstance * h) const { - BlockingDialog bd (true, false); - bd.player = h->getOwner(); - bd.text.appendLocalString (EMetaText::ADVOB_TXT, 14); - cb->showBlockingDialog (&bd); -} - -void CGPandoraBox::giveContentsUpToExp(const CGHeroInstance *h) const -{ - afterSuccessfulVisit(); - - InfoWindow iw; - iw.type = EInfoWindowMode::AUTO; - iw.player = h->getOwner(); - - bool changesPrimSkill = false; - for(const auto & elem : primskills) + auto setText = [](MetaString & text, int tId, const CGHeroInstance * h) { - if(elem) + text.appendLocalString(EMetaText::ADVOB_TXT, tId); + text.replaceRawString(h->getNameTranslated()); + }; + + for(auto i : getAvailableRewards(h, Rewardable::EEventType::EVENT_FIRST_VISIT)) + { + MetaString txt; + auto & r = configuration.info[i]; + + if(r.reward.spells.size() == 1) + setText(txt, 184, h); + else if(!r.reward.spells.empty()) + setText(txt, 188, h); + + if(r.reward.heroExperience || !r.reward.secondary.empty()) + setText(txt, 175, h); + + for(int ps : r.reward.primary) { - changesPrimSkill = true; - break; - } - } - - std::vector> unpossessedAbilities; //ability + ability level - int abilitiesRequiringSlot = 0; - - //filter out unnecessary secondary skills - for (int i = 0; i < abilities.size(); i++) - { - int curLev = h->getSecSkillLevel(abilities[i]); - bool abilityCanUseSlot = !curLev && ((h->secSkills.size() + abilitiesRequiringSlot) < GameConstants::SKILL_PER_HERO); //limit new abilities to number of slots - - if (abilityCanUseSlot) - abilitiesRequiringSlot++; - - if ((curLev && curLev < abilityLevels[i]) || abilityCanUseSlot) - { - unpossessedAbilities.emplace_back(abilities[i], abilityLevels[i]); - } - } - - if(gainedExp || changesPrimSkill || !unpossessedAbilities.empty()) - { - TExpType expVal = h->calculateXp(gainedExp); - //getText(iw,afterBattle,175,h); //wtf? - iw.text.appendLocalString(EMetaText::ADVOB_TXT, 175); //%s learns something - iw.text.replaceRawString(h->getNameTranslated()); - - if(expVal) - iw.components.emplace_back(Component::EComponentType::EXPERIENCE, 0, static_cast(expVal), 0); - - for(int i=0; ishowInfoDialog(&iw); - - //give sec skills - for(const auto & abilityData : unpossessedAbilities) - cb->changeSecSkill(h, abilityData.first, abilityData.second, true); - - assert(h->secSkills.size() <= GameConstants::SKILL_PER_HERO); - - //give prim skills - for(int i=0; ichangePrimSkill(h,static_cast(i),primskills[i],false); - - assert(!cb->isVisitCoveredByAnotherQuery(this, h)); - - //give exp - if(expVal) - cb->changePrimSkill(h, PrimarySkill::EXPERIENCE, expVal, false); - } - //else { } //TODO:Create information that box was empty for now, and deliver to CGPandoraBox::giveContentsAfterExp or refactor - - if(!cb->isVisitCoveredByAnotherQuery(this, h)) - giveContentsAfterExp(h); - //Otherwise continuation occurs via post-level-up callback. -} - -void CGPandoraBox::giveContentsAfterExp(const CGHeroInstance *h) const -{ - bool hadGuardians = hasGuardians; //copy, because flag will be emptied after issuing first post-battle message - - std::string msg = message; //in case box is removed in the meantime - InfoWindow iw; - iw.type = EInfoWindowMode::AUTO; - iw.player = h->getOwner(); - - //TODO: reuse this code for Scholar skill - if(!spells.empty()) - { - std::set spellsToGive; - - auto i = spells.cbegin(); - while (i != spells.cend()) - { - iw.components.clear(); - iw.text.clear(); - spellsToGive.clear(); - - for (; i != spells.cend(); i++) + if(ps) { - const auto * spell = (*i).toSpell(VLC->spells()); - if(h->canLearnSpell(spell)) - { - iw.components.emplace_back(Component::EComponentType::SPELL, *i, 0, 0); - spellsToGive.insert(*i); - } - if(spellsToGive.size() == 8) //display up to 8 spells at once - { - break; - } - } - if (!spellsToGive.empty()) - { - if (spellsToGive.size() > 1) - { - iw.text.appendLocalString(EMetaText::ADVOB_TXT, 188); //%s learns spells - } - else - { - iw.text.appendLocalString(EMetaText::ADVOB_TXT, 184); //%s learns a spell - } - iw.text.replaceRawString(h->getNameTranslated()); - cb->changeSpells(h, true, spellsToGive); - cb->showInfoDialog(&iw); + setText(txt, 175, h); + break; } } - } - - if(manaDiff) - { - getText(iw,hadGuardians,manaDiff,176,177,h); - iw.components.emplace_back(Component::EComponentType::PRIM_SKILL, 5, manaDiff, 0); - cb->showInfoDialog(&iw); - cb->setManaPoints(h->id, h->mana + manaDiff); - } - - if(moraleDiff) - { - getText(iw,hadGuardians,moraleDiff,178,179,h); - iw.components.emplace_back(Component::EComponentType::MORALE, 0, moraleDiff, 0); - cb->showInfoDialog(&iw); - GiveBonus gb; - gb.bonus = Bonus(BonusDuration::ONE_BATTLE,BonusType::MORALE,BonusSource::OBJECT,moraleDiff,id.getNum(),""); - gb.id = h->id.getNum(); - cb->giveHeroBonus(&gb); - } - - if(luckDiff) - { - getText(iw,hadGuardians,luckDiff,180,181,h); - iw.components.emplace_back(Component::EComponentType::LUCK, 0, luckDiff, 0); - cb->showInfoDialog(&iw); - GiveBonus gb; - gb.bonus = Bonus(BonusDuration::ONE_BATTLE,BonusType::LUCK,BonusSource::OBJECT,luckDiff,id.getNum(),""); - gb.id = h->id.getNum(); - cb->giveHeroBonus(&gb); - } - - iw.components.clear(); - iw.text.clear(); - for(int i=0; ishowInfoDialog(&iw); - } - - iw.components.clear(); - iw.text.clear(); - for(int i=0; i 0) - iw.components.emplace_back(Component::EComponentType::RESOURCE, i, resources[i], 0); - } - if(!iw.components.empty()) - { - getText(iw,hadGuardians,183,h); - cb->showInfoDialog(&iw); - } - - iw.components.clear(); - // getText(iw,afterBattle,183,h); - iw.text.appendLocalString(EMetaText::ADVOB_TXT, 183); //% has found treasure - iw.text.replaceRawString(h->getNameTranslated()); - for(const auto & elem : artifacts) - { - iw.components.emplace_back(Component::EComponentType::ARTIFACT, elem, 0, 0); - if(iw.components.size() >= 14) + + if(r.reward.manaDiff < 0) + setText(txt, 176, h); + if(r.reward.manaDiff > 0) + setText(txt, 177, h); + + for(auto b : r.reward.bonuses) { - cb->showInfoDialog(&iw); - iw.components.clear(); - iw.text.appendLocalString(EMetaText::ADVOB_TXT, 183); //% has found treasure - once more? - iw.text.replaceRawString(h->getNameTranslated()); + if(b.type == BonusType::MORALE) + { + if(b.val < 0) + setText(txt, 178, h); + if(b.val > 0) + setText(txt, 179, h); + } + if(b.type == BonusType::LUCK) + { + if(b.val < 0) + setText(txt, 180, h); + if(b.val > 0) + setText(txt, 181, h); + } } - } - if(!iw.components.empty()) - { - cb->showInfoDialog(&iw); - } - - cb->giveResources(h->getOwner(), resources); - - for(const auto & elem : artifacts) - cb->giveHeroNewArtifact(h, VLC->arth->objects[elem],ArtifactPosition::FIRST_AVAILABLE); - - iw.components.clear(); - iw.text.clear(); - - if(creatures.stacksCount()) - { //this part is taken straight from creature bank - MetaString loot; - for(const auto & elem : creatures.Slots()) - { //build list of joined creatures - iw.components.emplace_back(*elem.second); - loot.appendRawString("%s"); - loot.replaceCreatureName(*elem.second); + + for(auto res : r.reward.resources) + { + if(res < 0) + setText(txt, 182, h); + if(res > 0) + setText(txt, 183, h); } - - if(creatures.stacksCount() == 1 && creatures.Slots().begin()->second->count == 1) - iw.text.appendLocalString(EMetaText::ADVOB_TXT, 185); - else - iw.text.appendLocalString(EMetaText::ADVOB_TXT, 186); - - iw.text.replaceRawString(loot.buildList()); - iw.text.replaceRawString(h->getNameTranslated()); - - cb->showInfoDialog(&iw); - cb->giveCreatures(this, h, creatures, false); - } - if(!hasGuardians && !msg.empty()) - { - iw.text.appendRawString(msg); - cb->showInfoDialog(&iw); - } -} - -void CGPandoraBox::getText( InfoWindow &iw, bool &afterBattle, int text, const CGHeroInstance * h ) const -{ - if(afterBattle || message.empty()) - { - iw.text.appendLocalString(EMetaText::ADVOB_TXT,text);//%s has lost treasure. - iw.text.replaceRawString(h->getNameTranslated()); - } - else - { - iw.text.appendRawString(message); - afterBattle = true; - } -} - -void CGPandoraBox::getText( InfoWindow &iw, bool &afterBattle, int val, int negative, int positive, const CGHeroInstance * h ) const -{ - iw.components.clear(); - iw.text.clear(); - if(afterBattle || message.empty()) - { - iw.text.appendLocalString(EMetaText::ADVOB_TXT,val < 0 ? negative : positive); //%s's luck takes a turn for the worse / %s's luck increases - iw.text.replaceRawString(h->getNameTranslated()); - } - else - { - iw.text.appendRawString(message); - afterBattle = true; + + if(!r.reward.artifacts.empty()) + setText(txt, 183, h); + + if(!r.reward.creatures.empty()) + { + MetaString loot; + for(auto c : r.reward.creatures) + { + loot.appendRawString("%s"); + loot.replaceCreatureName(c); + } + + if(r.reward.creatures.size() == 1 && r.reward.creatures[0].count == 1) + txt.appendLocalString(EMetaText::ADVOB_TXT, 185); + else + txt.appendLocalString(EMetaText::ADVOB_TXT, 186); + + txt.replaceRawString(loot.buildList()); + txt.replaceRawString(h->getNameTranslated()); + } + + const_cast(r.message) = txt; } + + BlockingDialog bd (true, false); + bd.player = h->getOwner(); + bd.text.appendLocalString(EMetaText::ADVOB_TXT, 14); + cb->showBlockingDialog(&bd); } void CGPandoraBox::battleFinished(const CGHeroInstance *hero, const BattleResult &result) const { if(result.winner == 0) { - giveContentsUpToExp(hero); + CRewardableObject::onHeroVisit(hero); } } @@ -328,117 +148,87 @@ void CGPandoraBox::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answe hero->showInfoDialog(16, 0, EInfoWindowMode::MODAL); cb->startBattleI(hero, this); //grants things after battle } - else if(message.empty() && resources.empty() - && primskills.empty() && abilities.empty() - && abilityLevels.empty() && artifacts.empty() - && spells.empty() && creatures.stacksCount() == 0 - && gainedExp == 0 && manaDiff == 0 && moraleDiff == 0 && luckDiff == 0) //if it gives nothing without battle + else if(getAvailableRewards(hero, Rewardable::EEventType::EVENT_FIRST_VISIT).empty()) { hero->showInfoDialog(15); cb->removeObject(this); } else //if it gives something without battle { - giveContentsUpToExp(hero); + CRewardableObject::onHeroVisit(hero); } } } -void CGPandoraBox::heroLevelUpDone(const CGHeroInstance *hero) const -{ - giveContentsAfterExp(hero); -} - -void CGPandoraBox::afterSuccessfulVisit() const -{ - cb->removeAfterVisit(this); -} - void CGPandoraBox::serializeJsonOptions(JsonSerializeFormat & handler) { - CCreatureSet::serializeJson(handler, "guards", 7); + CRewardableObject::serializeJsonOptions(handler); handler.serializeString("guardMessage", message); - - handler.serializeInt("experience", gainedExp, 0); - handler.serializeInt("mana", manaDiff, 0); - handler.serializeInt("morale", moraleDiff, 0); - handler.serializeInt("luck", luckDiff, 0); - - resources.serializeJson(handler, "resources"); - + + if(!handler.saving) { - bool haveSkills = false; - - if(handler.saving) - { - for(int primskill : primskills) - if(primskill != 0) - haveSkills = true; - } - else - { - primskills.resize(GameConstants::PRIMARY_SKILLS,0); - haveSkills = true; - } - - if(haveSkills) + //backward compatibility + CCreatureSet::serializeJson(handler, "guards", 7); + + Rewardable::VisitInfo vinfo; + vinfo.visitType = Rewardable::EEventType::EVENT_FIRST_VISIT; + auto & reward = vinfo.reward; + int val; + handler.serializeInt("experience", reward.heroExperience, 0); + handler.serializeInt("mana", reward.manaDiff, 0); + handler.serializeInt("morale", val, 0); + if(val) + reward.bonuses.emplace_back(BonusDuration::ONE_BATTLE, BonusType::MORALE, BonusSource::OBJECT, val, id); + handler.serializeInt("luck", val, 0); + if(val) + reward.bonuses.emplace_back(BonusDuration::ONE_BATTLE, BonusType::LUCK, BonusSource::OBJECT, val, id); + + reward.resources.serializeJson(handler, "resources"); { auto s = handler.enterStruct("primarySkills"); - for(int idx = 0; idx < primskills.size(); idx ++) - handler.serializeInt(NPrimarySkill::names[idx], primskills[idx], 0); + for(int idx = 0; idx < reward.primary.size(); idx ++) + handler.serializeInt(NPrimarySkill::names[idx], reward.primary[idx], 0); } - } + + handler.serializeIdArray("artifacts", reward.artifacts); + handler.serializeIdArray("spells", reward.spells); - if(handler.saving) - { - if(!abilities.empty()) + handler.enterArray("creatures").serializeStruct(reward.creatures); + { auto s = handler.enterStruct("secondarySkills"); - - for(size_t idx = 0; idx < abilities.size(); idx++) + for(const auto & p : handler.getCurrent().Struct()) { - handler.serializeEnum(CSkillHandler::encodeSkill(abilities[idx]), abilityLevels[idx], NSecondarySkill::levels); + const std::string skillName = p.first; + const std::string levelId = p.second.String(); + + const int rawId = CSkillHandler::decodeSkill(skillName); + if(rawId < 0) + { + logGlobal->error("Invalid secondary skill %s", skillName); + continue; + } + + const int level = vstd::find_pos(NSecondarySkill::levels, levelId); + if(level < 0) + { + logGlobal->error("Invalid secondary skill level %s", levelId); + continue; + } + + reward.secondary[rawId] = level; } } + configuration.info.push_back(vinfo); } - else - { - auto s = handler.enterStruct("secondarySkills"); +} - const JsonNode & skillMap = handler.getCurrent(); - - abilities.clear(); - abilityLevels.clear(); - - for(const auto & p : skillMap.Struct()) - { - const std::string skillName = p.first; - const std::string levelId = p.second.String(); - - const int rawId = CSkillHandler::decodeSkill(skillName); - if(rawId < 0) - { - logGlobal->error("Invalid secondary skill %s", skillName); - continue; - } - - const int level = vstd::find_pos(NSecondarySkill::levels, levelId); - if(level < 0) - { - logGlobal->error("Invalid secondary skill level %s", levelId); - continue; - } - - abilities.emplace_back(rawId); - abilityLevels.push_back(level); - } - } - - - handler.serializeIdArray("artifacts", artifacts); - handler.serializeIdArray("spells", spells); - - creatures.serializeJson(handler, "creatures"); +void CGEvent::init() +{ + blockVisit = false; + + for(auto & i : configuration.info) + i.reward.removeObject = removeAfterVisit; } void CGEvent::onHeroVisit( const CGHeroInstance * h ) const @@ -470,27 +260,17 @@ void CGEvent::activated( const CGHeroInstance * h ) const } else { - giveContentsUpToExp(h); + CRewardableObject::onHeroVisit(h); } } -void CGEvent::afterSuccessfulVisit() const -{ - if(removeAfterVisit) - { - cb->removeAfterVisit(this); - } - else if(hasGuardians) - hasGuardians = false; -} - void CGEvent::serializeJsonOptions(JsonSerializeFormat & handler) { CGPandoraBox::serializeJsonOptions(handler); - handler.serializeBool("aIActivable", computerActivate, true, false, false); - handler.serializeBool("humanActivable", humanActivate, true, false, true); - handler.serializeBool("removeAfterVisit", removeAfterVisit, true, false, false); + handler.serializeBool("aIActivable", computerActivate, false); + handler.serializeBool("humanActivable", humanActivate, true); + handler.serializeBool("removeAfterVisit", removeAfterVisit, false); handler.serializeIdArray("availableFor", availableFor); } diff --git a/lib/mapObjects/CGPandoraBox.h b/lib/mapObjects/CGPandoraBox.h index 6cef83266..240e9e68d 100644 --- a/lib/mapObjects/CGPandoraBox.h +++ b/lib/mapObjects/CGPandoraBox.h @@ -9,63 +9,31 @@ */ #pragma once -#include "CArmedInstance.h" +#include "CRewardableObject.h" #include "../ResourceSet.h" VCMI_LIB_NAMESPACE_BEGIN struct InfoWindow; -class DLL_LINKAGE CGPandoraBox : public CArmedInstance +class DLL_LINKAGE CGPandoraBox : public CRewardableObject { public: std::string message; - mutable bool hasGuardians = false; //helper - after battle even though we have no stacks, allows us to know that there was battle - - //gained things: - ui32 gainedExp = 0; - si32 manaDiff = 0; //amount of gained / lost mana - si32 moraleDiff = 0; //morale modifier - si32 luckDiff = 0; //luck modifier - TResources resources;//gained / lost resources - std::vector primskills;//gained / lost prim skills - std::vector abilities; //gained abilities - std::vector abilityLevels; //levels of gained abilities - std::vector artifacts; //gained artifacts - std::vector spells; //gained spells - CCreatureSet creatures; //gained creatures void initObj(CRandomGenerator & rand) override; void onHeroVisit(const CGHeroInstance * h) const override; void battleFinished(const CGHeroInstance *hero, const BattleResult &result) const override; void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override; - void heroLevelUpDone(const CGHeroInstance *hero) const override; template void serialize(Handler &h, const int version) { - h & static_cast(*this); + h & static_cast(*this); h & message; - h & hasGuardians; - h & gainedExp; - h & manaDiff; - h & moraleDiff; - h & luckDiff; - h & resources; - h & primskills; - h & abilities; - h & abilityLevels; - h & artifacts; - h & spells; - h & creatures; } protected: - void giveContentsUpToExp(const CGHeroInstance *h) const; - void giveContentsAfterExp(const CGHeroInstance *h) const; + virtual void init(); void serializeJsonOptions(JsonSerializeFormat & handler) override; -private: - void getText( InfoWindow &iw, bool &afterBattle, int val, int negative, int positive, const CGHeroInstance * h ) const; - void getText( InfoWindow &iw, bool &afterBattle, int text, const CGHeroInstance * h ) const; - virtual void afterSuccessfulVisit() const; }; class DLL_LINKAGE CGEvent : public CGPandoraBox //event objects @@ -87,10 +55,10 @@ public: void onHeroVisit(const CGHeroInstance * h) const override; protected: + void init() override; void serializeJsonOptions(JsonSerializeFormat & handler) override; private: void activated(const CGHeroInstance * h) const; - void afterSuccessfulVisit() const override; }; VCMI_LIB_NAMESPACE_END diff --git a/lib/mapObjects/CQuest.cpp b/lib/mapObjects/CQuest.cpp index 01d71bf19..3dae6194b 100644 --- a/lib/mapObjects/CQuest.cpp +++ b/lib/mapObjects/CQuest.cpp @@ -794,20 +794,11 @@ void CGSeerHut::afterAddToMap(CMap* map) void CGSeerHut::serializeJsonOptions(JsonSerializeFormat & handler) { //quest and reward + CRewardableObject::serializeJsonOptions(handler); quest->serializeJson(handler, "quest"); - - if(handler.saving) + + if(!handler.saving) { - CRewardableObject::serializeJsonOptions(handler); - } - else - { - if(handler.getCurrent()["reward"].isNull() || handler.getCurrent()["reward"].Struct().empty()) - { - CRewardableObject::serializeJsonOptions(handler); - return; - } - //backward compatibility auto s = handler.enterStruct("reward"); const JsonNode & rewardsJson = handler.getCurrent(); @@ -827,7 +818,8 @@ void CGSeerHut::serializeJsonOptions(JsonSerializeFormat & handler) int val = 0; handler.serializeInt(fullIdentifier, val); - Rewardable::Reward reward; + Rewardable::VisitInfo vinfo; + auto & reward = vinfo.reward; if(metaTypeName == "experience") reward.heroExperience = val; if(metaTypeName == "mana") @@ -867,9 +859,8 @@ void CGSeerHut::serializeJsonOptions(JsonSerializeFormat & handler) reward.creatures.emplace_back(rawId, val); } - configuration.info.push_back({}); - configuration.info.back().reward = reward; - configuration.info.back().visitType = Rewardable::EEventType::EVENT_FIRST_VISIT; + vinfo.visitType = Rewardable::EEventType::EVENT_FIRST_VISIT; + configuration.info.push_back(vinfo); } } @@ -881,14 +872,10 @@ void CGQuestGuard::init(CRandomGenerator & rand) configuration.info.push_back({}); configuration.info.back().visitType = Rewardable::EEventType::EVENT_FIRST_VISIT; + configuration.info.back().reward.removeObject = true; configuration.canRefuse = true; } -void CGQuestGuard::completeQuest() const -{ - cb->removeObject(this); -} - void CGQuestGuard::serializeJsonOptions(JsonSerializeFormat & handler) { //quest only, do not call base class @@ -946,7 +933,6 @@ void CGKeymasterTent::onHeroVisit( const CGHeroInstance * h ) const void CGBorderGuard::initObj(CRandomGenerator & rand) { - //ui32 m13489val = subID; //store color as quest info blockVisit = true; } diff --git a/lib/mapObjects/CQuest.h b/lib/mapObjects/CQuest.h index 963f29499..7cb0df2bc 100644 --- a/lib/mapObjects/CQuest.h +++ b/lib/mapObjects/CQuest.h @@ -162,11 +162,8 @@ public: template void serialize(Handler &h, const int version) { - h & static_cast(*this); + h & static_cast(*this); h & static_cast(*this); - //h & rewardType; - //h & rID; - //h & rVal; h & seerName; } protected: @@ -181,7 +178,6 @@ class DLL_LINKAGE CGQuestGuard : public CGSeerHut { public: void init(CRandomGenerator & rand) override; - void completeQuest() const override; template void serialize(Handler &h, const int version) { diff --git a/lib/mapObjects/CRewardableObject.cpp b/lib/mapObjects/CRewardableObject.cpp index 31c17e6e7..c07b6da44 100644 --- a/lib/mapObjects/CRewardableObject.cpp +++ b/lib/mapObjects/CRewardableObject.cpp @@ -271,7 +271,6 @@ void CRewardableObject::newTurn(CRandomGenerator & rand) const void CRewardableObject::initObj(CRandomGenerator & rand) { VLC->objtypeh->getHandlerFor(ID, subID)->configureObject(this, rand); - assert(!configuration.info.empty()); } CRewardableObject::CRewardableObject() diff --git a/lib/mapping/MapFormatH3M.cpp b/lib/mapping/MapFormatH3M.cpp index a3b4b16df..4d062659b 100644 --- a/lib/mapping/MapFormatH3M.cpp +++ b/lib/mapping/MapFormatH3M.cpp @@ -1019,34 +1019,46 @@ CGObjectInstance * CMapLoaderH3M::readPandora(const int3 & mapPosition) void CMapLoaderH3M::readBoxContent(CGPandoraBox * object, const int3 & mapPosition) { readMessageAndGuards(object->message, object, mapPosition); + Rewardable::VisitInfo vinfo; + auto & reward = vinfo.reward; + + reward.heroExperience = reader->readUInt32(); + reward.manaDiff = reader->readInt32(); + reward.bonuses.emplace_back(BonusDuration::ONE_BATTLE, BonusType::MORALE, BonusSource::OBJECT, reader->readUInt8(), object->id); + reward.bonuses.emplace_back(BonusDuration::ONE_BATTLE, BonusType::LUCK, BonusSource::OBJECT, reader->readUInt8(), object->id); - object->gainedExp = reader->readUInt32(); - object->manaDiff = reader->readInt32(); - object->moraleDiff = reader->readInt8(); - object->luckDiff = reader->readInt8(); - - reader->readResourses(object->resources); - - object->primskills.resize(GameConstants::PRIMARY_SKILLS); + reader->readResourses(reward.resources); for(int x = 0; x < GameConstants::PRIMARY_SKILLS; ++x) - object->primskills[x] = reader->readUInt8(); + reward.primary.at(x) = reader->readUInt8(); int gabn = reader->readUInt8(); //number of gained abilities for(int oo = 0; oo < gabn; ++oo) { - object->abilities.emplace_back(reader->readSkill()); - object->abilityLevels.push_back(reader->readUInt8()); + auto rId = reader->readSkill(); + auto rVal = reader->readUInt8(); + + reward.secondary[rId] = rVal; } int gart = reader->readUInt8(); //number of gained artifacts for(int oo = 0; oo < gart; ++oo) - object->artifacts.emplace_back(reader->readArtifact()); + reward.artifacts.push_back(reader->readArtifact()); int gspel = reader->readUInt8(); //number of gained spells for(int oo = 0; oo < gspel; ++oo) - object->spells.emplace_back(reader->readSpell()); + reward.spells.push_back(reader->readSpell()); int gcre = reader->readUInt8(); //number of gained creatures - readCreatureSet(&object->creatures, gcre); + for(int oo = 0; oo < gcre; ++oo) + { + auto rId = reader->readCreature(); + auto rVal = reader->readUInt16(); + + reward.creatures.emplace_back(rId, rVal); + } + + vinfo.visitType = Rewardable::EEventType::EVENT_FIRST_VISIT; + object->configuration.info.push_back(vinfo); + reader->skipZero(8); } @@ -1843,7 +1855,8 @@ void CMapLoaderH3M::readSeerHutQuest(CGSeerHut * hut, const int3 & position) if(hut->quest->missionType) { auto rewardType = reader->readUInt8(); - Rewardable::Reward reward; + Rewardable::VisitInfo vinfo; + auto & reward = vinfo.reward; switch(rewardType) { case 0: //NOTHING @@ -1922,9 +1935,8 @@ void CMapLoaderH3M::readSeerHutQuest(CGSeerHut * hut, const int3 & position) } } - hut->configuration.info.push_back({}); - hut->configuration.info.back().reward = reward; - hut->configuration.info.back().visitType = Rewardable::EEventType::EVENT_FIRST_VISIT; + vinfo.visitType = Rewardable::EEventType::EVENT_FIRST_VISIT; + hut->configuration.info.push_back(vinfo); } else { diff --git a/lib/rmg/modificators/TreasurePlacer.cpp b/lib/rmg/modificators/TreasurePlacer.cpp index 0addaa481..c92637aea 100644 --- a/lib/rmg/modificators/TreasurePlacer.cpp +++ b/lib/rmg/modificators/TreasurePlacer.cpp @@ -235,7 +235,12 @@ void TreasurePlacer::addAllPossibleObjects() { auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0); auto * obj = dynamic_cast(factory->create()); - obj->resources[EGameResID::GOLD] = i * 5000; + + Rewardable::VisitInfo reward; + reward.reward.resources[EGameResID::GOLD] = i * 5000; + reward.visitType = Rewardable::EEventType::EVENT_FIRST_VISIT; + obj->configuration.info.push_back(reward); + return obj; }; oi.setTemplate(Obj::PANDORAS_BOX, 0, zone.getTerrainType()); @@ -251,7 +256,12 @@ void TreasurePlacer::addAllPossibleObjects() { auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0); auto * obj = dynamic_cast(factory->create()); - obj->gainedExp = i * 5000; + + Rewardable::VisitInfo reward; + reward.reward.heroExperience = i * 5000; + reward.visitType = Rewardable::EEventType::EVENT_FIRST_VISIT; + obj->configuration.info.push_back(reward); + return obj; }; oi.setTemplate(Obj::PANDORAS_BOX, 0, zone.getTerrainType()); @@ -307,8 +317,12 @@ void TreasurePlacer::addAllPossibleObjects() { auto factory = VLC->objtypeh->getHandlerFor(Obj::PANDORAS_BOX, 0); auto * obj = dynamic_cast(factory->create()); - auto * stack = new CStackInstance(creature, creaturesAmount); - obj->creatures.putStack(SlotID(0), stack); + + Rewardable::VisitInfo reward; + reward.reward.creatures.emplace_back(creature, creaturesAmount); + reward.visitType = Rewardable::EEventType::EVENT_FIRST_VISIT; + obj->configuration.info.push_back(reward); + return obj; }; oi.setTemplate(Obj::PANDORAS_BOX, 0, zone.getTerrainType()); @@ -333,10 +347,13 @@ void TreasurePlacer::addAllPossibleObjects() } RandomGeneratorUtil::randomShuffle(spells, zone.getRand()); + Rewardable::VisitInfo reward; for(int j = 0; j < std::min(12, static_cast(spells.size())); j++) { - obj->spells.push_back(spells[j]->id); + reward.reward.spells.push_back(spells[j]->id); } + reward.visitType = Rewardable::EEventType::EVENT_FIRST_VISIT; + obj->configuration.info.push_back(reward); return obj; }; @@ -362,10 +379,13 @@ void TreasurePlacer::addAllPossibleObjects() } RandomGeneratorUtil::randomShuffle(spells, zone.getRand()); + Rewardable::VisitInfo reward; for(int j = 0; j < std::min(15, static_cast(spells.size())); j++) { - obj->spells.push_back(spells[j]->id); + reward.reward.spells.push_back(spells[j]->id); } + reward.visitType = Rewardable::EEventType::EVENT_FIRST_VISIT; + obj->configuration.info.push_back(reward); return obj; }; @@ -390,10 +410,13 @@ void TreasurePlacer::addAllPossibleObjects() } RandomGeneratorUtil::randomShuffle(spells, zone.getRand()); + Rewardable::VisitInfo reward; for(int j = 0; j < std::min(60, static_cast(spells.size())); j++) { - obj->spells.push_back(spells[j]->id); + reward.reward.spells.push_back(spells[j]->id); } + reward.visitType = Rewardable::EEventType::EVENT_FIRST_VISIT; + obj->configuration.info.push_back(reward); return obj; }; @@ -442,11 +465,10 @@ void TreasurePlacer::addAllPossibleObjects() auto factory = VLC->objtypeh->getHandlerFor(Obj::SEER_HUT, randomAppearance); auto * obj = dynamic_cast(factory->create()); - Rewardable::Reward reward; - reward.creatures.emplace_back(creature->getId(), creaturesAmount); - obj->configuration.info.push_back({}); - obj->configuration.info.back().reward = reward; - obj->configuration.info.back().visitType = Rewardable::EEventType::EVENT_FIRST_VISIT; + Rewardable::VisitInfo reward; + reward.reward.creatures.emplace_back(creature->getId(), creaturesAmount); + reward.visitType = Rewardable::EEventType::EVENT_FIRST_VISIT; + obj->configuration.info.push_back(reward); obj->quest->missionType = CQuest::MISSION_ART; @@ -494,11 +516,10 @@ void TreasurePlacer::addAllPossibleObjects() auto factory = VLC->objtypeh->getHandlerFor(Obj::SEER_HUT, randomAppearance); auto * obj = dynamic_cast(factory->create()); - Rewardable::Reward reward; - reward.heroExperience = generator.getConfig().questRewardValues[i]; - obj->configuration.info.push_back({}); - obj->configuration.info.back().reward = reward; - obj->configuration.info.back().visitType = Rewardable::EEventType::EVENT_FIRST_VISIT; + Rewardable::VisitInfo reward; + reward.reward.heroExperience = generator.getConfig().questRewardValues[i]; + reward.visitType = Rewardable::EEventType::EVENT_FIRST_VISIT; + obj->configuration.info.push_back(reward); obj->quest->missionType = CQuest::MISSION_ART; ArtifactID artid = qap->drawRandomArtifact(); @@ -519,11 +540,10 @@ void TreasurePlacer::addAllPossibleObjects() auto factory = VLC->objtypeh->getHandlerFor(Obj::SEER_HUT, randomAppearance); auto * obj = dynamic_cast(factory->create()); - Rewardable::Reward reward; - reward.resources[EGameResID::GOLD] = generator.getConfig().questRewardValues[i]; - obj->configuration.info.push_back({}); - obj->configuration.info.back().reward = reward; - obj->configuration.info.back().visitType = Rewardable::EEventType::EVENT_FIRST_VISIT; + Rewardable::VisitInfo reward; + reward.reward.resources[EGameResID::GOLD] = generator.getConfig().questRewardValues[i]; + reward.visitType = Rewardable::EEventType::EVENT_FIRST_VISIT; + obj->configuration.info.push_back(reward); obj->quest->missionType = CQuest::MISSION_ART; ArtifactID artid = qap->drawRandomArtifact();