mirror of
https://github.com/vcmi/vcmi.git
synced 2025-08-13 19:54:17 +02:00
@@ -37,42 +37,29 @@ TGoalVec CompleteQuest::decompose() const
|
||||
}
|
||||
|
||||
logAi->debug("Trying to realize quest: %s", questToString());
|
||||
|
||||
switch(q.quest->missionType)
|
||||
{
|
||||
case CQuest::MISSION_ART:
|
||||
|
||||
if(!q.quest->mission.artifacts.empty())
|
||||
return missionArt();
|
||||
|
||||
case CQuest::MISSION_HERO:
|
||||
if(!q.quest->mission.heroes.empty())
|
||||
return missionHero();
|
||||
|
||||
case CQuest::MISSION_ARMY:
|
||||
if(!q.quest->mission.creatures.empty())
|
||||
return missionArmy();
|
||||
|
||||
case CQuest::MISSION_RESOURCES:
|
||||
if(q.quest->mission.resources.nonZero())
|
||||
return missionResources();
|
||||
|
||||
case CQuest::MISSION_KILL_HERO:
|
||||
case CQuest::MISSION_KILL_CREATURE:
|
||||
if(q.quest->killTarget != ObjectInstanceID::NONE)
|
||||
return missionDestroyObj();
|
||||
|
||||
case CQuest::MISSION_PRIMARY_STAT:
|
||||
return missionIncreasePrimaryStat();
|
||||
for(auto & s : q.quest->mission.primary)
|
||||
if(s)
|
||||
return missionIncreasePrimaryStat();
|
||||
|
||||
case CQuest::MISSION_LEVEL:
|
||||
if(q.quest->mission.heroLevel > 0)
|
||||
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:
|
||||
return missionKeymaster();
|
||||
|
||||
} //end of switch
|
||||
|
||||
return TGoalVec();
|
||||
}
|
||||
|
||||
@@ -107,7 +94,7 @@ std::string CompleteQuest::questToString() const
|
||||
return "find " + VLC->generaltexth->tentColors[q.obj->subID] + " keymaster tent";
|
||||
}
|
||||
|
||||
if(q.quest->missionType == CQuest::MISSION_NONE)
|
||||
if(q.quest->questName == CQuest::missionName(0))
|
||||
return "inactive quest";
|
||||
|
||||
MetaString ms;
|
||||
@@ -137,7 +124,7 @@ TGoalVec CompleteQuest::missionArt() const
|
||||
|
||||
CaptureObjectsBehavior findArts;
|
||||
|
||||
for(auto art : q.quest->m5arts)
|
||||
for(auto art : q.quest->mission.artifacts)
|
||||
{
|
||||
solutions.push_back(sptr(CaptureObjectsBehavior().ofType(Obj::ARTIFACT, art)));
|
||||
}
|
||||
@@ -223,7 +210,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();
|
||||
|
@@ -25,7 +25,7 @@ namespace AIPathfinding
|
||||
return dynamic_cast<const IQuestObject *>(questInfo.obj)->checkQuest(node->actor->hero);
|
||||
}
|
||||
|
||||
return questInfo.quest->progress == CQuest::NOT_ACTIVE
|
||||
return questInfo.quest->activeForPlayers.count(node->actor->hero->getOwner())
|
||||
|| questInfo.quest->checkQuest(node->actor->hero);
|
||||
}
|
||||
|
||||
|
@@ -130,7 +130,9 @@ namespace AIPathfinding
|
||||
auto questInfo = QuestInfo(questObj->quest, destination.nodeObject, destination.coord);
|
||||
QuestAction questAction(questInfo);
|
||||
|
||||
if(destination.nodeObject->ID == Obj::QUEST_GUARD && questObj->quest->missionType == CQuest::MISSION_NONE)
|
||||
if(destination.nodeObject->ID == Obj::QUEST_GUARD
|
||||
&& questObj->quest->mission == Rewardable::Limiter{}
|
||||
&& questObj->quest->killTarget == ObjectInstanceID::NONE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
@@ -21,48 +21,43 @@ bool CompleteQuest::operator==(const CompleteQuest & other) const
|
||||
return q.quest->qid == other.q.quest->qid;
|
||||
}
|
||||
|
||||
bool isKeyMaster(const QuestInfo & q)
|
||||
{
|
||||
return q.obj && (q.obj->ID == Obj::BORDER_GATE || q.obj->ID == Obj::BORDERGUARD);
|
||||
}
|
||||
|
||||
TGoalVec CompleteQuest::getAllPossibleSubgoals()
|
||||
{
|
||||
TGoalVec solutions;
|
||||
|
||||
if(q.quest->missionType && q.quest->progress != CQuest::COMPLETE)
|
||||
if(!q.quest->isCompleted)
|
||||
{
|
||||
logAi->debug("Trying to realize quest: %s", questToString());
|
||||
|
||||
switch(q.quest->missionType)
|
||||
{
|
||||
case CQuest::MISSION_ART:
|
||||
return missionArt();
|
||||
|
||||
case CQuest::MISSION_HERO:
|
||||
return missionHero();
|
||||
|
||||
case CQuest::MISSION_ARMY:
|
||||
return missionArmy();
|
||||
|
||||
case CQuest::MISSION_RESOURCES:
|
||||
return missionResources();
|
||||
|
||||
case CQuest::MISSION_KILL_HERO:
|
||||
case CQuest::MISSION_KILL_CREATURE:
|
||||
return missionDestroyObj();
|
||||
|
||||
case CQuest::MISSION_PRIMARY_STAT:
|
||||
return missionIncreasePrimaryStat();
|
||||
|
||||
case CQuest::MISSION_LEVEL:
|
||||
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:
|
||||
if(isKeyMaster(q))
|
||||
return missionKeymaster();
|
||||
|
||||
} //end of switch
|
||||
if(!q.quest->mission.artifacts.empty())
|
||||
return missionArt();
|
||||
|
||||
if(!q.quest->mission.heroes.empty())
|
||||
return missionHero();
|
||||
|
||||
if(!q.quest->mission.creatures.empty())
|
||||
return missionArmy();
|
||||
|
||||
if(q.quest->mission.resources.nonZero())
|
||||
return missionResources();
|
||||
|
||||
if(q.quest->killTarget != ObjectInstanceID::NONE)
|
||||
return missionDestroyObj();
|
||||
|
||||
for(auto & s : q.quest->mission.primary)
|
||||
if(s)
|
||||
return missionIncreasePrimaryStat();
|
||||
|
||||
if(q.quest->mission.heroLevel > 0)
|
||||
return missionLevel();
|
||||
}
|
||||
|
||||
return TGoalVec();
|
||||
@@ -70,7 +65,7 @@ TGoalVec CompleteQuest::getAllPossibleSubgoals()
|
||||
|
||||
TSubgoal CompleteQuest::whatToDoToAchieve()
|
||||
{
|
||||
if(q.quest->missionType == CQuest::MISSION_NONE)
|
||||
if(q.quest->mission == Rewardable::Limiter{})
|
||||
{
|
||||
throw cannotFulfillGoalException("Can not complete inactive quest");
|
||||
}
|
||||
@@ -104,7 +99,7 @@ std::string CompleteQuest::completeMessage() const
|
||||
|
||||
std::string CompleteQuest::questToString() const
|
||||
{
|
||||
if(q.quest->missionType == CQuest::MISSION_NONE)
|
||||
if(q.quest->questName == CQuest::missionName(0))
|
||||
return "inactive quest";
|
||||
|
||||
MetaString ms;
|
||||
@@ -137,7 +132,7 @@ TGoalVec CompleteQuest::missionArt() const
|
||||
if(!solutions.empty())
|
||||
return solutions;
|
||||
|
||||
for(auto art : q.quest->m5arts)
|
||||
for(auto art : q.quest->mission.artifacts)
|
||||
{
|
||||
solutions.push_back(sptr(GetArtOfType(art))); //TODO: transport?
|
||||
}
|
||||
@@ -165,7 +160,7 @@ TGoalVec CompleteQuest::missionArmy() const
|
||||
if(!solutions.empty())
|
||||
return solutions;
|
||||
|
||||
for(auto creature : q.quest->m6creatures)
|
||||
for(auto creature : q.quest->mission.creatures)
|
||||
{
|
||||
solutions.push_back(sptr(GatherTroops(creature.type->getId(), creature.count)));
|
||||
}
|
||||
@@ -179,7 +174,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->mission.primary.size(); ++i)
|
||||
{
|
||||
// TODO: library, school and other boost objects
|
||||
logAi->debug("Don't know how to increase primary stat %d", i);
|
||||
@@ -195,7 +190,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->mission.heroLevel);
|
||||
}
|
||||
|
||||
return solutions;
|
||||
@@ -227,10 +222,10 @@ TGoalVec CompleteQuest::missionResources() const
|
||||
}
|
||||
else
|
||||
{
|
||||
for(int i = 0; i < q.quest->m7resources.size(); ++i)
|
||||
for(int i = 0; i < q.quest->mission.resources.size(); ++i)
|
||||
{
|
||||
if(q.quest->m7resources[i])
|
||||
solutions.push_back(sptr(CollectRes(static_cast<EGameResID>(i), q.quest->m7resources[i])));
|
||||
if(q.quest->mission.resources[i])
|
||||
solutions.push_back(sptr(CollectRes(static_cast<EGameResID>(i), q.quest->mission.resources[i])));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -246,7 +241,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);
|
||||
|
@@ -148,11 +148,11 @@ void CQuestLog::recreateLabelList()
|
||||
int currentLabel = 0;
|
||||
for (int i = 0; i < quests.size(); ++i)
|
||||
{
|
||||
// Quests with MISSION_NONE type don't have text for them and can't be displayed
|
||||
if (quests[i].quest->missionType == CQuest::MISSION_NONE)
|
||||
// Quests without mision don't have text for them and can't be displayed
|
||||
if (quests[i].quest->mission == Rewardable::Limiter{})
|
||||
continue;
|
||||
|
||||
if (quests[i].quest->progress == CQuest::COMPLETE)
|
||||
if (quests[i].quest->isCompleted)
|
||||
{
|
||||
completeMissing = false;
|
||||
if (hideComplete)
|
||||
@@ -180,7 +180,7 @@ void CQuestLog::recreateLabelList()
|
||||
labels.push_back(label);
|
||||
|
||||
// Select latest active quest
|
||||
if (quests[i].quest->progress != CQuest::COMPLETE)
|
||||
if(!quests[i].quest->isCompleted)
|
||||
selectQuest(i, currentLabel);
|
||||
|
||||
currentLabel = static_cast<int>(labels.size());
|
||||
@@ -236,7 +236,7 @@ void CQuestLog::selectQuest(int which, int labelId)
|
||||
|
||||
MetaString text;
|
||||
std::vector<Component> components;
|
||||
currentQuest->quest->getVisitText (text, components, currentQuest->quest->isCustomFirst, true);
|
||||
currentQuest->quest->getVisitText(text, components, true);
|
||||
if(description->slider)
|
||||
description->slider->scrollToMin(); // scroll text to start position
|
||||
description->setText(text.toString()); //TODO: use special log entry text
|
||||
@@ -247,9 +247,15 @@ void CQuestLog::selectQuest(int which, int labelId)
|
||||
int descriptionHeight = DESCRIPTION_HEIGHT_MAX;
|
||||
if(componentsSize)
|
||||
{
|
||||
descriptionHeight -= 15;
|
||||
CComponent::ESize imageSize = CComponent::large;
|
||||
switch (currentQuest->quest->missionType)
|
||||
if (componentsSize > 4)
|
||||
{
|
||||
imageSize = CComponent::small; // Only small icons can be used for resources as 4+ icons take too much space
|
||||
descriptionHeight -= 155;
|
||||
}
|
||||
else
|
||||
descriptionHeight -= 130;
|
||||
/*switch (currentQuest->quest->missionType)
|
||||
{
|
||||
case CQuest::MISSION_ARMY:
|
||||
{
|
||||
@@ -285,7 +291,7 @@ void CQuestLog::selectQuest(int which, int labelId)
|
||||
default:
|
||||
descriptionHeight -= 115;
|
||||
break;
|
||||
}
|
||||
}*/
|
||||
|
||||
OBJECT_CONSTRUCTION_CUSTOM_CAPTURING(255-DISPOSE);
|
||||
|
||||
|
@@ -72,7 +72,7 @@ Rewardable object is defined similarly to other objects, with key difference bei
|
||||
// additional list of conditions. Limiter will be valid if any of these conditions are true
|
||||
"anyOf" : [
|
||||
{
|
||||
// See "Configurable Properties" section for additiona parameters
|
||||
// See "Configurable Properties" section for additional parameters
|
||||
<additional properties>
|
||||
}
|
||||
]
|
||||
@@ -80,12 +80,12 @@ Rewardable object is defined similarly to other objects, with key difference bei
|
||||
// additional list of conditions. Limiter will be valid only if none of these conditions are true
|
||||
"noneOf" : [
|
||||
{
|
||||
// See "Configurable Properties" section for additiona parameters
|
||||
// See "Configurable Properties" section for additional parameters
|
||||
<additional properties>
|
||||
}
|
||||
]
|
||||
|
||||
// See "Configurable Properties" section for additiona parameters
|
||||
// See "Configurable Properties" section for additional parameters
|
||||
<additional properties>
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ Rewardable object is defined similarly to other objects, with key difference bei
|
||||
// object will be disappeared after taking reward is set to true
|
||||
"removeObject": false
|
||||
|
||||
// See "Configurable Properties" section for additiona parameters
|
||||
// See "Configurable Properties" section for additional parameters
|
||||
<additional properties>
|
||||
}
|
||||
],
|
||||
@@ -450,4 +450,31 @@ Keep in mind, that all randomization is performed on map load and on object rese
|
||||
"spell" : "townPortal",
|
||||
"schoolLevel": 3
|
||||
}
|
||||
```
|
||||
|
||||
### Player color
|
||||
- Can be used as limiter
|
||||
- Can NOT be used as reward
|
||||
- Only players with specific color can pass the limiter
|
||||
|
||||
```jsonc
|
||||
"colors" : [ "red", "blue", "tan", "green", "orange", "purple", "teal", "pink" ]
|
||||
```
|
||||
|
||||
### Hero types
|
||||
- Can be used as limiter
|
||||
- Can NOT be used as reward
|
||||
- Only specific heroes can pass the limiter
|
||||
|
||||
```jsonc
|
||||
"heroes" : [ "orrin" ]
|
||||
```
|
||||
|
||||
### Hero classes
|
||||
- Can be used as limiter
|
||||
- Can NOT be used as reward
|
||||
- Only heroes belonging to specific classes can pass the limiter
|
||||
|
||||
```jsonc
|
||||
"heroClasses" : [ "battlemage" ]
|
||||
```
|
@@ -1029,6 +1029,14 @@ void CStackBasicDescriptor::setType(const CCreature * c)
|
||||
type = c;
|
||||
}
|
||||
|
||||
bool operator== (const CStackBasicDescriptor & l, const CStackBasicDescriptor & r)
|
||||
{
|
||||
return (!l.type && !r.type)
|
||||
|| (l.type && r.type
|
||||
&& l.type->getId() == r.type->getId()
|
||||
&& l.count == r.count);
|
||||
}
|
||||
|
||||
void CStackBasicDescriptor::serializeJson(JsonSerializeFormat & handler)
|
||||
{
|
||||
handler.serializeInt("amount", count);
|
||||
|
@@ -42,6 +42,8 @@ public:
|
||||
TQuantity getCount() const;
|
||||
|
||||
virtual void setType(const CCreature * c);
|
||||
|
||||
friend bool operator== (const CStackBasicDescriptor & l, const CStackBasicDescriptor & r);
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
|
@@ -662,9 +662,9 @@ std::string CGameInfoCallback::getTavernRumor(const CGObjectInstance * townOrTav
|
||||
case RumorState::TYPE_SPECIAL:
|
||||
text.replaceLocalString(EMetaText::GENERAL_TXT, rumor.first);
|
||||
if(rumor.first == RumorState::RUMOR_GRAIL)
|
||||
text.replaceTextID(TextIdentifier("core", "genrltxt", "arraytxt", 158 + rumor.second).get());
|
||||
text.replaceTextID(TextIdentifier("core", "arraytxt", 158 + rumor.second).get());
|
||||
else
|
||||
text.replaceTextID(TextIdentifier("core", "genrltxt", "capitalColors", rumor.second).get());
|
||||
text.replaceTextID(TextIdentifier("core", "plcolors", rumor.second).get());
|
||||
|
||||
break;
|
||||
case RumorState::TYPE_MAP:
|
||||
@@ -672,7 +672,7 @@ std::string CGameInfoCallback::getTavernRumor(const CGObjectInstance * townOrTav
|
||||
break;
|
||||
|
||||
case RumorState::TYPE_RAND:
|
||||
text.replaceTextID(TextIdentifier("core", "genrltxt", "randtvrn", rumor.first).get());
|
||||
text.replaceTextID(TextIdentifier("core", "randtvrn", rumor.first).get());
|
||||
break;
|
||||
}
|
||||
|
||||
|
@@ -264,21 +264,21 @@ void TextLocalizationContainer::registerStringOverride(const std::string & modCo
|
||||
|
||||
void TextLocalizationContainer::addSubContainer(const TextLocalizationContainer & container)
|
||||
{
|
||||
subContainers.insert(&container);
|
||||
subContainers.push_back(&container);
|
||||
}
|
||||
|
||||
void TextLocalizationContainer::removeSubContainer(const TextLocalizationContainer & container)
|
||||
{
|
||||
subContainers.erase(&container);
|
||||
subContainers.erase(std::remove(subContainers.begin(), subContainers.end(), &container), subContainers.end());
|
||||
}
|
||||
|
||||
const std::string & TextLocalizationContainer::deserialize(const TextIdentifier & identifier) const
|
||||
{
|
||||
if(stringsLocalizations.count(identifier.get()) == 0)
|
||||
{
|
||||
for(const auto * container : subContainers)
|
||||
if(container->identifierExists(identifier))
|
||||
return container->deserialize(identifier);
|
||||
for(auto containerIter = subContainers.rbegin(); containerIter != subContainers.rend(); ++containerIter)
|
||||
if((*containerIter)->identifierExists(identifier))
|
||||
return (*containerIter)->deserialize(identifier);
|
||||
|
||||
logGlobal->error("Unable to find localization for string '%s'", identifier.get());
|
||||
return identifier.get();
|
||||
@@ -547,7 +547,7 @@ CGeneralTextHandler::CGeneralTextHandler():
|
||||
|
||||
for (size_t i = 0; i < 9; ++i) //9 types of quests
|
||||
{
|
||||
std::string questName = CQuest::missionName(static_cast<CQuest::Emission>(1+i));
|
||||
std::string questName = CQuest::missionName(1+i);
|
||||
|
||||
for (size_t j = 0; j < 5; ++j)
|
||||
{
|
||||
|
@@ -146,7 +146,7 @@ protected:
|
||||
/// map identifier -> localization
|
||||
std::unordered_map<std::string, StringState> stringsLocalizations;
|
||||
|
||||
std::set<const TextLocalizationContainer *> subContainers;
|
||||
std::vector<const TextLocalizationContainer *> subContainers;
|
||||
|
||||
/// add selected string to internal storage as high-priority strings
|
||||
void registerStringOverride(const std::string & modContext, const std::string & language, const TextIdentifier & UID, const std::string & localized);
|
||||
|
@@ -22,6 +22,7 @@
|
||||
#include "CCreatureSet.h"
|
||||
#include "spells/CSpellHandler.h"
|
||||
#include "CSkillHandler.h"
|
||||
#include "CHeroHandler.h"
|
||||
#include "IGameCallback.h"
|
||||
#include "mapObjects/IObjectInterface.h"
|
||||
#include "modding/IdentifierStorage.h"
|
||||
@@ -282,6 +283,46 @@ namespace JsonRandom
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<PlayerColor> loadColors(const JsonNode & value, CRandomGenerator & rng)
|
||||
{
|
||||
std::vector<PlayerColor> ret;
|
||||
std::set<std::string> def;
|
||||
|
||||
for(auto & color : GameConstants::PLAYER_COLOR_NAMES)
|
||||
def.insert(color);
|
||||
|
||||
for(auto & entry : value.Vector())
|
||||
{
|
||||
auto key = loadKey(entry, rng, def);
|
||||
auto pos = vstd::find_pos(GameConstants::PLAYER_COLOR_NAMES, key);
|
||||
if(pos < 0)
|
||||
logMod->warn("Unable to determine player color %s", key);
|
||||
else
|
||||
ret.emplace_back(pos);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<HeroTypeID> loadHeroes(const JsonNode & value, CRandomGenerator & rng)
|
||||
{
|
||||
std::vector<HeroTypeID> ret;
|
||||
for(auto & entry : value.Vector())
|
||||
{
|
||||
ret.push_back(VLC->heroTypes()->getByIndex(VLC->identifiers()->getIdentifier("hero", entry.String()).value())->getId());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<HeroClassID> loadHeroClasses(const JsonNode & value, CRandomGenerator & rng)
|
||||
{
|
||||
std::vector<HeroClassID> ret;
|
||||
for(auto & entry : value.Vector())
|
||||
{
|
||||
ret.push_back(VLC->heroClasses()->getByIndex(VLC->identifiers()->getIdentifier("heroClass", entry.String()).value())->getId());
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
CStackBasicDescriptor loadCreature(const JsonNode & value, CRandomGenerator & rng)
|
||||
{
|
||||
CStackBasicDescriptor stack;
|
||||
|
@@ -48,6 +48,10 @@ namespace JsonRandom
|
||||
DLL_LINKAGE std::vector<CStackBasicDescriptor> loadCreatures(const JsonNode & value, CRandomGenerator & rng);
|
||||
DLL_LINKAGE std::vector<RandomStackInfo> evaluateCreatures(const JsonNode & value);
|
||||
|
||||
DLL_LINKAGE std::vector<PlayerColor> loadColors(const JsonNode & value, CRandomGenerator & rng);
|
||||
DLL_LINKAGE std::vector<HeroTypeID> loadHeroes(const JsonNode & value, CRandomGenerator & rng);
|
||||
DLL_LINKAGE std::vector<HeroClassID> loadHeroClasses(const JsonNode & value, CRandomGenerator & rng);
|
||||
|
||||
DLL_LINKAGE std::vector<Bonus> loadBonuses(const JsonNode & value);
|
||||
//DLL_LINKAGE std::vector<Component> loadComponents(const JsonNode & value);
|
||||
}
|
||||
|
@@ -19,6 +19,8 @@
|
||||
#include <vcmi/FactionService.h>
|
||||
#include <vcmi/HeroType.h>
|
||||
#include <vcmi/HeroTypeService.h>
|
||||
#include <vcmi/HeroClass.h>
|
||||
#include <vcmi/HeroClassService.h>
|
||||
|
||||
#include <vcmi/spells/Spell.h>
|
||||
#include <vcmi/spells/Service.h>
|
||||
@@ -103,6 +105,25 @@ namespace GameConstants
|
||||
#endif
|
||||
}
|
||||
|
||||
si32 HeroClassID::decode(const std::string & identifier)
|
||||
{
|
||||
auto rawId = VLC->identifiers()->getIdentifier(ModScope::scopeMap(), "heroClass", identifier);
|
||||
if(rawId)
|
||||
return rawId.value();
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::string HeroClassID::encode(const si32 index)
|
||||
{
|
||||
return VLC->heroClasses()->getByIndex(index)->getJsonKey();
|
||||
}
|
||||
|
||||
std::string HeroClassID::entityType()
|
||||
{
|
||||
return "heroClass";
|
||||
}
|
||||
|
||||
si32 HeroTypeID::decode(const std::string & identifier)
|
||||
{
|
||||
auto rawId = VLC->identifiers()->getIdentifier(ModScope::scopeMap(), "hero", identifier);
|
||||
|
@@ -223,6 +223,10 @@ class HeroClassID : public Identifier<HeroClassID>
|
||||
{
|
||||
public:
|
||||
using Identifier<HeroClassID>::Identifier;
|
||||
///json serialization helpers
|
||||
DLL_LINKAGE static si32 decode(const std::string & identifier);
|
||||
DLL_LINKAGE static std::string encode(const si32 index);
|
||||
static std::string entityType();
|
||||
};
|
||||
|
||||
class HeroTypeID : public Identifier<HeroTypeID>
|
||||
|
@@ -28,6 +28,7 @@
|
||||
#include "../mapping/CMap.h"
|
||||
#include "../modding/ModScope.h"
|
||||
#include "../modding/ModUtility.h"
|
||||
#include "../spells/CSpellHandler.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
@@ -37,16 +38,17 @@ std::map <PlayerColor, std::set <ui8> > CGKeys::playerKeyMap;
|
||||
//TODO: Remove constructor
|
||||
CQuest::CQuest():
|
||||
qid(-1),
|
||||
missionType(MISSION_NONE),
|
||||
progress(NOT_ACTIVE),
|
||||
isCompleted(false),
|
||||
lastDay(-1),
|
||||
m13489val(0),
|
||||
killTarget(ObjectInstanceID::NONE),
|
||||
textOption(0),
|
||||
completedOption(0),
|
||||
stackDirection(0),
|
||||
isCustomFirst(false),
|
||||
isCustomNext(false),
|
||||
isCustomComplete(false)
|
||||
isCustomComplete(false),
|
||||
repeatedQuest(false),
|
||||
questName(CQuest::missionName(0))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -56,9 +58,9 @@ static std::string visitedTxt(const bool visited)
|
||||
return VLC->generaltexth->allTexts[id];
|
||||
}
|
||||
|
||||
const std::string & CQuest::missionName(CQuest::Emission mission)
|
||||
const std::string & CQuest::missionName(int mission)
|
||||
{
|
||||
static const std::array<std::string, 11> names = {
|
||||
static const std::array<std::string, 13> names = {
|
||||
"empty",
|
||||
"heroLevel",
|
||||
"primarySkill",
|
||||
@@ -69,7 +71,9 @@ const std::string & CQuest::missionName(CQuest::Emission mission)
|
||||
"bringResources",
|
||||
"bringHero",
|
||||
"bringPlayer",
|
||||
"keymaster"
|
||||
"keymaster",
|
||||
"hota",
|
||||
"other"
|
||||
};
|
||||
|
||||
if(static_cast<size_t>(mission) < names.size())
|
||||
@@ -99,7 +103,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->mission.creatures.begin(); cre != q->mission.creatures.end(); ++cre)
|
||||
{
|
||||
for(count = 0, it = army->Slots().begin(); it != army->Slots().end(); ++it)
|
||||
{
|
||||
@@ -121,348 +125,212 @@ bool CQuest::checkMissionArmy(const CQuest * q, const CCreatureSet * army)
|
||||
|
||||
bool CQuest::checkQuest(const CGHeroInstance * h) const
|
||||
{
|
||||
switch (missionType)
|
||||
if(!mission.heroAllowed(h))
|
||||
return false;
|
||||
|
||||
if(killTarget != ObjectInstanceID::NONE)
|
||||
{
|
||||
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::getVisitText(MetaString &iwText, std::vector<Component> &components, bool isCustom, bool firstVisit, const CGHeroInstance * h) const
|
||||
void CQuest::completeQuest(IGameCallback * cb, const CGHeroInstance *h) const
|
||||
{
|
||||
for(auto & elem : mission.artifacts)
|
||||
{
|
||||
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)));
|
||||
|
||||
// Disassemble this backpack artifact
|
||||
for(const auto & ci : parts)
|
||||
{
|
||||
if(ci.art->getTypeId() != elem)
|
||||
cb->giveHeroNewArtifact(h, ci.art->artType, ArtifactPosition::BACKPACK_START);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
cb->takeCreatures(h->id, mission.creatures);
|
||||
cb->giveResources(h->getOwner(), mission.resources);
|
||||
}
|
||||
|
||||
void CQuest::addTextReplacements(MetaString & text, std::vector<Component> & components) const
|
||||
{
|
||||
if(mission.heroLevel > 0)
|
||||
text.replaceNumber(mission.heroLevel);
|
||||
|
||||
if(mission.heroExperience > 0)
|
||||
text.replaceNumber(mission.heroExperience);
|
||||
|
||||
{ //primary skills
|
||||
MetaString loot;
|
||||
for(int i = 0; i < 4; ++i)
|
||||
{
|
||||
if(mission.primary[i])
|
||||
{
|
||||
loot.appendRawString("%d %s");
|
||||
loot.replaceNumber(mission.primary[i]);
|
||||
loot.replaceRawString(VLC->generaltexth->primarySkillNames[i]);
|
||||
}
|
||||
}
|
||||
|
||||
for(auto & skill : mission.secondary)
|
||||
{
|
||||
loot.appendTextID(VLC->skillh->getById(skill.first)->getNameTextID());
|
||||
}
|
||||
|
||||
for(auto & spell : mission.spells)
|
||||
{
|
||||
loot.appendTextID(VLC->spellh->getById(spell)->getNameTextID());
|
||||
}
|
||||
|
||||
if(!loot.empty())
|
||||
text.replaceRawString(loot.buildList());
|
||||
}
|
||||
|
||||
if(killTarget != ObjectInstanceID::NONE && !heroName.empty())
|
||||
{
|
||||
components.emplace_back(Component::EComponentType::HERO_PORTRAIT, heroPortrait, 0, 0);
|
||||
addKillTargetReplacements(text);
|
||||
}
|
||||
|
||||
if(killTarget != ObjectInstanceID::NONE && stackToKill.type)
|
||||
{
|
||||
components.emplace_back(stackToKill);
|
||||
addKillTargetReplacements(text);
|
||||
}
|
||||
|
||||
if(!mission.heroes.empty())
|
||||
text.replaceRawString(VLC->heroh->getById(mission.heroes.front())->getNameTranslated());
|
||||
|
||||
if(!mission.artifacts.empty())
|
||||
{
|
||||
MetaString loot;
|
||||
for(const auto & elem : mission.artifacts)
|
||||
{
|
||||
loot.appendRawString("%s");
|
||||
loot.replaceLocalString(EMetaText::ART_NAMES, elem);
|
||||
}
|
||||
text.replaceRawString(loot.buildList());
|
||||
}
|
||||
|
||||
if(!mission.creatures.empty())
|
||||
{
|
||||
MetaString loot;
|
||||
for(const auto & elem : mission.creatures)
|
||||
{
|
||||
loot.appendRawString("%s");
|
||||
loot.replaceCreatureName(elem);
|
||||
}
|
||||
text.replaceRawString(loot.buildList());
|
||||
}
|
||||
|
||||
if(mission.resources.nonZero())
|
||||
{
|
||||
MetaString loot;
|
||||
for(int i = 0; i < 7; ++i)
|
||||
{
|
||||
if(mission.resources[i])
|
||||
{
|
||||
loot.appendRawString("%d %s");
|
||||
loot.replaceNumber(mission.resources[i]);
|
||||
loot.replaceLocalString(EMetaText::RES_NAMES, i);
|
||||
}
|
||||
}
|
||||
text.replaceRawString(loot.buildList());
|
||||
}
|
||||
|
||||
if(!mission.players.empty())
|
||||
{
|
||||
MetaString loot;
|
||||
for(auto & p : mission.players)
|
||||
loot.appendLocalString(EMetaText::COLOR, p);
|
||||
|
||||
text.replaceRawString(loot.buildList());
|
||||
}
|
||||
|
||||
if(lastDay >= 0)
|
||||
text.replaceNumber(lastDay - IObjectInterface::cb->getDate(Date::DAY));
|
||||
}
|
||||
|
||||
void CQuest::getVisitText(MetaString &iwText, std::vector<Component> &components, bool firstVisit, const CGHeroInstance * h) const
|
||||
{
|
||||
MetaString text;
|
||||
bool failRequirements = (h ? !checkQuest(h) : true);
|
||||
mission.loadComponents(components, h);
|
||||
|
||||
if(firstVisit)
|
||||
{
|
||||
isCustom = isCustomFirst;
|
||||
text = firstVisitText;
|
||||
iwText.appendRawString(text.toString());
|
||||
}
|
||||
iwText.appendRawString(firstVisitText.toString());
|
||||
else if(failRequirements)
|
||||
{
|
||||
isCustom = isCustomNext;
|
||||
text = nextVisitText;
|
||||
iwText.appendRawString(text.toString());
|
||||
}
|
||||
switch (missionType)
|
||||
{
|
||||
case MISSION_LEVEL:
|
||||
components.emplace_back(Component::EComponentType::EXPERIENCE, 0, m13489val, 0);
|
||||
if(!isCustom)
|
||||
iwText.replaceNumber(m13489val);
|
||||
break;
|
||||
case MISSION_PRIMARY_STAT:
|
||||
{
|
||||
MetaString loot;
|
||||
for(int i = 0; i < 4; ++i)
|
||||
{
|
||||
if(m2stats[i])
|
||||
{
|
||||
components.emplace_back(Component::EComponentType::PRIM_SKILL, i, m2stats[i], 0);
|
||||
loot.appendRawString("%d %s");
|
||||
loot.replaceNumber(m2stats[i]);
|
||||
loot.replaceRawString(VLC->generaltexth->primarySkillNames[i]);
|
||||
}
|
||||
}
|
||||
if (!isCustom)
|
||||
iwText.replaceRawString(loot.buildList());
|
||||
}
|
||||
break;
|
||||
case MISSION_KILL_HERO:
|
||||
components.emplace_back(Component::EComponentType::HERO_PORTRAIT, heroPortrait, 0, 0);
|
||||
if(!isCustom)
|
||||
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());
|
||||
break;
|
||||
case MISSION_KILL_CREATURE:
|
||||
{
|
||||
components.emplace_back(stackToKill);
|
||||
if(!isCustom)
|
||||
{
|
||||
addReplacements(iwText, text.toString());
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MISSION_ART:
|
||||
{
|
||||
MetaString loot;
|
||||
for(const auto & elem : m5arts)
|
||||
{
|
||||
components.emplace_back(Component::EComponentType::ARTIFACT, elem, 0, 0);
|
||||
loot.appendRawString("%s");
|
||||
loot.replaceLocalString(EMetaText::ART_NAMES, elem);
|
||||
}
|
||||
if(!isCustom)
|
||||
iwText.replaceRawString(loot.buildList());
|
||||
}
|
||||
break;
|
||||
case MISSION_ARMY:
|
||||
{
|
||||
MetaString loot;
|
||||
for(const auto & elem : m6creatures)
|
||||
{
|
||||
components.emplace_back(elem);
|
||||
loot.appendRawString("%s");
|
||||
loot.replaceCreatureName(elem);
|
||||
}
|
||||
if(!isCustom)
|
||||
iwText.replaceRawString(loot.buildList());
|
||||
}
|
||||
break;
|
||||
case MISSION_RESOURCES:
|
||||
{
|
||||
MetaString loot;
|
||||
for(int i = 0; i < 7; ++i)
|
||||
{
|
||||
if(m7resources[i])
|
||||
{
|
||||
components.emplace_back(Component::EComponentType::RESOURCE, i, m7resources[i], 0);
|
||||
loot.appendRawString("%d %s");
|
||||
loot.replaceNumber(m7resources[i]);
|
||||
loot.replaceLocalString(EMetaText::RES_NAMES, i);
|
||||
}
|
||||
}
|
||||
if(!isCustom)
|
||||
iwText.replaceRawString(loot.buildList());
|
||||
}
|
||||
break;
|
||||
case MISSION_PLAYER:
|
||||
components.emplace_back(Component::EComponentType::FLAG, m13489val, 0, 0);
|
||||
if(!isCustom)
|
||||
iwText.replaceLocalString(EMetaText::COLOR, m13489val);
|
||||
break;
|
||||
}
|
||||
iwText.appendRawString(nextVisitText.toString());
|
||||
|
||||
if(lastDay >= 0)
|
||||
iwText.appendTextID(TextIdentifier("core", "seerhut", "time", textOption).get());
|
||||
|
||||
addTextReplacements(iwText, components);
|
||||
}
|
||||
|
||||
void CQuest::getRolloverText(MetaString &ms, bool onHover) const
|
||||
{
|
||||
// Quests with MISSION_NONE type don't have a text for them
|
||||
assert(missionType != MISSION_NONE);
|
||||
|
||||
if(onHover)
|
||||
ms.appendRawString("\n\n");
|
||||
|
||||
std::string questName = missionName(missionType);
|
||||
std::string questState = missionState(onHover ? 3 : 4);
|
||||
|
||||
ms.appendRawString(VLC->generaltexth->translate("core.seerhut.quest", questName, questState,textOption));
|
||||
ms.appendTextID(TextIdentifier("core", "seerhut", "quest", questName, questState, textOption).get());
|
||||
|
||||
switch(missionType)
|
||||
{
|
||||
case MISSION_LEVEL:
|
||||
ms.replaceNumber(m13489val);
|
||||
break;
|
||||
case MISSION_PRIMARY_STAT:
|
||||
{
|
||||
MetaString loot;
|
||||
for (int i = 0; i < 4; ++i)
|
||||
{
|
||||
if (m2stats[i])
|
||||
{
|
||||
loot.appendRawString("%d %s");
|
||||
loot.replaceNumber(m2stats[i]);
|
||||
loot.replaceRawString(VLC->generaltexth->primarySkillNames[i]);
|
||||
}
|
||||
}
|
||||
ms.replaceRawString(loot.buildList());
|
||||
}
|
||||
break;
|
||||
case MISSION_KILL_HERO:
|
||||
ms.replaceRawString(heroName);
|
||||
break;
|
||||
case MISSION_KILL_CREATURE:
|
||||
ms.replaceCreatureName(stackToKill);
|
||||
break;
|
||||
case MISSION_ART:
|
||||
{
|
||||
MetaString loot;
|
||||
for(const auto & elem : m5arts)
|
||||
{
|
||||
loot.appendRawString("%s");
|
||||
loot.replaceLocalString(EMetaText::ART_NAMES, elem);
|
||||
}
|
||||
ms.replaceRawString(loot.buildList());
|
||||
}
|
||||
break;
|
||||
case MISSION_ARMY:
|
||||
{
|
||||
MetaString loot;
|
||||
for(const auto & elem : m6creatures)
|
||||
{
|
||||
loot.appendRawString("%s");
|
||||
loot.replaceCreatureName(elem);
|
||||
}
|
||||
ms.replaceRawString(loot.buildList());
|
||||
}
|
||||
break;
|
||||
case MISSION_RESOURCES:
|
||||
{
|
||||
MetaString loot;
|
||||
for (int i = 0; i < 7; ++i)
|
||||
{
|
||||
if (m7resources[i])
|
||||
{
|
||||
loot.appendRawString("%d %s");
|
||||
loot.replaceNumber(m7resources[i]);
|
||||
loot.replaceLocalString(EMetaText::RES_NAMES, i);
|
||||
}
|
||||
}
|
||||
ms.replaceRawString(loot.buildList());
|
||||
}
|
||||
break;
|
||||
case MISSION_HERO:
|
||||
ms.replaceRawString(VLC->heroh->objects[m13489val]->getNameTranslated());
|
||||
break;
|
||||
case MISSION_PLAYER:
|
||||
ms.replaceRawString(VLC->generaltexth->colors[m13489val]);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
std::vector<Component> components;
|
||||
addTextReplacements(ms, components);
|
||||
}
|
||||
|
||||
void CQuest::getCompletionText(MetaString &iwText) const
|
||||
{
|
||||
iwText.appendRawString(completedText.toString());
|
||||
switch(missionType)
|
||||
{
|
||||
case CQuest::MISSION_LEVEL:
|
||||
if (!isCustomComplete)
|
||||
iwText.replaceNumber(m13489val);
|
||||
break;
|
||||
case CQuest::MISSION_PRIMARY_STAT:
|
||||
{
|
||||
MetaString loot;
|
||||
assert(m2stats.size() <= 4);
|
||||
for (int i = 0; i < m2stats.size(); ++i)
|
||||
{
|
||||
if (m2stats[i])
|
||||
{
|
||||
loot.appendRawString("%d %s");
|
||||
loot.replaceNumber(m2stats[i]);
|
||||
loot.replaceRawString(VLC->generaltexth->primarySkillNames[i]);
|
||||
}
|
||||
}
|
||||
if (!isCustomComplete)
|
||||
iwText.replaceRawString(loot.buildList());
|
||||
break;
|
||||
}
|
||||
case CQuest::MISSION_ART:
|
||||
{
|
||||
MetaString loot;
|
||||
for(const auto & elem : m5arts)
|
||||
{
|
||||
loot.appendRawString("%s");
|
||||
loot.replaceLocalString(EMetaText::ART_NAMES, elem);
|
||||
}
|
||||
if (!isCustomComplete)
|
||||
iwText.replaceRawString(loot.buildList());
|
||||
}
|
||||
break;
|
||||
case CQuest::MISSION_ARMY:
|
||||
{
|
||||
MetaString loot;
|
||||
for(const auto & elem : m6creatures)
|
||||
{
|
||||
loot.appendRawString("%s");
|
||||
loot.replaceCreatureName(elem);
|
||||
}
|
||||
if (!isCustomComplete)
|
||||
iwText.replaceRawString(loot.buildList());
|
||||
}
|
||||
break;
|
||||
case CQuest::MISSION_RESOURCES:
|
||||
{
|
||||
MetaString loot;
|
||||
for (int i = 0; i < 7; ++i)
|
||||
{
|
||||
if (m7resources[i])
|
||||
{
|
||||
loot.appendRawString("%d %s");
|
||||
loot.replaceNumber(m7resources[i]);
|
||||
loot.replaceLocalString(EMetaText::RES_NAMES, i);
|
||||
}
|
||||
}
|
||||
if (!isCustomComplete)
|
||||
iwText.replaceRawString(loot.buildList());
|
||||
}
|
||||
break;
|
||||
case MISSION_KILL_HERO:
|
||||
case MISSION_KILL_CREATURE:
|
||||
if (!isCustomComplete)
|
||||
addReplacements(iwText, completedText.toString());
|
||||
break;
|
||||
case MISSION_HERO:
|
||||
if (!isCustomComplete)
|
||||
iwText.replaceRawString(VLC->heroh->objects[m13489val]->getNameTranslated());
|
||||
break;
|
||||
case MISSION_PLAYER:
|
||||
if (!isCustomComplete)
|
||||
iwText.replaceRawString(VLC->generaltexth->colors[m13489val]);
|
||||
break;
|
||||
}
|
||||
|
||||
std::vector<Component> components;
|
||||
addTextReplacements(iwText, components);
|
||||
}
|
||||
|
||||
void CQuest::addArtifactID(const ArtifactID & id)
|
||||
void CQuest::defineQuestName()
|
||||
{
|
||||
m5arts.push_back(id);
|
||||
++artifactsRequirements[id];
|
||||
//standard quests
|
||||
questName = CQuest::missionName(0);
|
||||
if(mission != Rewardable::Limiter{}) questName = CQuest::missionName(12);
|
||||
if(mission.heroLevel > 0) questName = CQuest::missionName(1);
|
||||
for(auto & s : mission.primary) if(s) questName = CQuest::missionName(2);
|
||||
if(!mission.spells.empty()) questName = CQuest::missionName(2);
|
||||
if(!mission.secondary.empty()) questName = CQuest::missionName(2);
|
||||
if(killTarget != ObjectInstanceID::NONE && !heroName.empty()) questName = CQuest::missionName(3);
|
||||
if(killTarget != ObjectInstanceID::NONE && stackToKill.getType()) questName = CQuest::missionName(4);
|
||||
if(!mission.artifacts.empty()) questName = CQuest::missionName(5);
|
||||
if(!mission.creatures.empty()) questName = CQuest::missionName(6);
|
||||
if(mission.resources.nonZero()) questName = CQuest::missionName(7);
|
||||
if(!mission.heroes.empty()) questName = CQuest::missionName(8);
|
||||
if(!mission.players.empty()) questName = CQuest::missionName(9);
|
||||
if(mission.daysPassed > 0 || !mission.heroClasses.empty()) questName = CQuest::missionName(11);
|
||||
}
|
||||
|
||||
void CQuest::addKillTargetReplacements(MetaString &out) const
|
||||
{
|
||||
if(!heroName.empty())
|
||||
out.replaceTextID(heroName);
|
||||
if(stackToKill.type)
|
||||
{
|
||||
out.replaceCreatureName(stackToKill);
|
||||
out.replaceRawString(VLC->generaltexth->arraytxt[147+stackDirection]);
|
||||
}
|
||||
}
|
||||
|
||||
void CQuest::serializeJson(JsonSerializeFormat & handler, const std::string & fieldName)
|
||||
@@ -472,6 +340,7 @@ void CQuest::serializeJson(JsonSerializeFormat & handler, const std::string & fi
|
||||
handler.serializeStruct("firstVisitText", firstVisitText);
|
||||
handler.serializeStruct("nextVisitText", nextVisitText);
|
||||
handler.serializeStruct("completedText", completedText);
|
||||
handler.serializeBool("repeatedQuest", repeatedQuest, false);
|
||||
|
||||
if(!handler.saving)
|
||||
{
|
||||
@@ -479,87 +348,93 @@ void CQuest::serializeJson(JsonSerializeFormat & handler, const std::string & fi
|
||||
isCustomNext = !nextVisitText.empty();
|
||||
isCustomComplete = !completedText.empty();
|
||||
}
|
||||
|
||||
static const std::vector<std::string> MISSION_TYPE_JSON =
|
||||
{
|
||||
"None", "Level", "PrimaryStat", "KillHero", "KillCreature", "Artifact", "Army", "Resources", "Hero", "Player"
|
||||
};
|
||||
|
||||
handler.serializeEnum("missionType", missionType, Emission::MISSION_NONE, MISSION_TYPE_JSON);
|
||||
|
||||
handler.serializeInt("timeLimit", lastDay, -1);
|
||||
handler.serializeStruct("limiter", mission);
|
||||
handler.serializeInstance("killTarget", killTarget, ObjectInstanceID::NONE);
|
||||
|
||||
switch (missionType)
|
||||
if(!handler.saving) //compatibility with legacy vmaps
|
||||
{
|
||||
case MISSION_NONE:
|
||||
break;
|
||||
case MISSION_LEVEL:
|
||||
handler.serializeInt("heroLevel", m13489val, -1);
|
||||
break;
|
||||
case MISSION_PRIMARY_STAT:
|
||||
std::string missionType = "None";
|
||||
handler.serializeString("missionType", missionType);
|
||||
if(missionType == "None")
|
||||
return;
|
||||
|
||||
if(missionType == "Level")
|
||||
handler.serializeInt("heroLevel", mission.heroLevel);
|
||||
|
||||
if(missionType == "PrimaryStat")
|
||||
{
|
||||
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);
|
||||
handler.serializeInt(NPrimarySkill::names[i], mission.primary[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:
|
||||
|
||||
if(missionType == "Artifact")
|
||||
handler.serializeIdArray<ArtifactID>("artifacts", mission.artifacts);
|
||||
|
||||
if(missionType == "Army")
|
||||
{
|
||||
auto a = handler.enterArray("creatures");
|
||||
a.serializeStruct(m6creatures);
|
||||
a.serializeStruct(mission.creatures);
|
||||
}
|
||||
break;
|
||||
case MISSION_RESOURCES:
|
||||
|
||||
if(missionType == "Resources")
|
||||
{
|
||||
auto r = handler.enterStruct("resources");
|
||||
|
||||
for(size_t idx = 0; idx < (GameConstants::RESOURCE_QUANTITY - 1); idx++)
|
||||
{
|
||||
handler.serializeInt(GameConstants::RESOURCE_NAMES[idx], m7resources[idx], 0);
|
||||
handler.serializeInt(GameConstants::RESOURCE_NAMES[idx], mission.resources[idx], 0);
|
||||
}
|
||||
}
|
||||
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;
|
||||
|
||||
if(missionType == "Hero")
|
||||
{
|
||||
ui32 temp;
|
||||
handler.serializeId<ui32, ui32, HeroTypeID>("hero", temp, 0);
|
||||
mission.heroes.emplace_back(temp);
|
||||
}
|
||||
|
||||
if(missionType == "Player")
|
||||
{
|
||||
ui32 temp;
|
||||
handler.serializeId<ui32, ui32, PlayerColor>("player", temp, PlayerColor::NEUTRAL);
|
||||
mission.players.emplace_back(temp);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool IQuestObject::checkQuest(const CGHeroInstance* h) const
|
||||
{
|
||||
return quest->checkQuest(h);
|
||||
}
|
||||
|
||||
void IQuestObject::getVisitText(MetaString &text, std::vector<Component> &components, bool FirstVisit, const CGHeroInstance * h) const
|
||||
{
|
||||
quest->getVisitText(text, components, FirstVisit, h);
|
||||
}
|
||||
|
||||
void IQuestObject::afterAddToMapCommon(CMap * map) const
|
||||
{
|
||||
map->addNewQuestInstance(quest);
|
||||
}
|
||||
|
||||
void CGSeerHut::setObjToKill()
|
||||
{
|
||||
if(quest->missionType == CQuest::MISSION_KILL_CREATURE)
|
||||
if(getCreatureToKill(true))
|
||||
{
|
||||
quest->stackToKill = getCreatureToKill(false)->getStack(SlotID(0)); //FIXME: stacks tend to disappear (desync?) on server :?
|
||||
assert(quest->stackToKill.type);
|
||||
quest->stackToKill.count = 0; //no count in info window
|
||||
quest->stackDirection = checkDirection();
|
||||
}
|
||||
else if(quest->missionType == CQuest::MISSION_KILL_HERO)
|
||||
else if(getHeroToKill(true))
|
||||
{
|
||||
quest->heroName = getHeroToKill(false)->getNameTranslated();
|
||||
quest->heroPortrait = getHeroToKill(false)->getPortraitSource();
|
||||
}
|
||||
|
||||
quest->getCompletionText(configuration.onSelect);
|
||||
for(auto & i : configuration.info)
|
||||
quest->getCompletionText(i.message);
|
||||
}
|
||||
|
||||
void CGSeerHut::init(CRandomGenerator & rand)
|
||||
@@ -581,29 +456,35 @@ void CGSeerHut::initObj(CRandomGenerator & rand)
|
||||
init(rand);
|
||||
|
||||
CRewardableObject::initObj(rand);
|
||||
|
||||
quest->progress = CQuest::NOT_ACTIVE;
|
||||
if(quest->missionType)
|
||||
|
||||
setObjToKill();
|
||||
quest->defineQuestName();
|
||||
|
||||
if(quest->mission == Rewardable::Limiter{} && quest->killTarget == ObjectInstanceID::NONE)
|
||||
quest->isCompleted = true;
|
||||
|
||||
if(quest->questName == quest->missionName(0))
|
||||
{
|
||||
std::string questName = quest->missionName(quest->missionType);
|
||||
|
||||
if(!quest->isCustomFirst)
|
||||
quest->firstVisitText.appendTextID(TextIdentifier("core", "seerhut", "quest", questName, quest->missionState(0), quest->textOption).get());
|
||||
if(!quest->isCustomNext)
|
||||
quest->firstVisitText.appendTextID(TextIdentifier("core", "seerhut", "quest", questName, quest->missionState(1), quest->textOption).get());
|
||||
if(!quest->isCustomComplete)
|
||||
quest->firstVisitText.appendTextID(TextIdentifier("core", "seerhut", "quest", questName, quest->missionState(2), quest->textOption).get());
|
||||
quest->firstVisitText.appendTextID(TextIdentifier("core", "seehut", "empty", quest->completedOption).get());
|
||||
}
|
||||
else
|
||||
{
|
||||
quest->progress = CQuest::COMPLETE;
|
||||
quest->firstVisitText.appendTextID(TextIdentifier("core", "seehut", "empty", quest->completedOption).get());
|
||||
if(!quest->isCustomFirst)
|
||||
quest->firstVisitText.appendTextID(TextIdentifier("core", "seerhut", "quest", quest->questName, quest->missionState(0), quest->textOption).get());
|
||||
if(!quest->isCustomNext)
|
||||
quest->nextVisitText.appendTextID(TextIdentifier("core", "seerhut", "quest", quest->questName, quest->missionState(1), quest->textOption).get());
|
||||
if(!quest->isCustomComplete)
|
||||
quest->completedText.appendTextID(TextIdentifier("core", "seerhut", "quest", quest-> questName, quest->missionState(2), quest->textOption).get());
|
||||
}
|
||||
|
||||
quest->getCompletionText(configuration.onSelect);
|
||||
for(auto & i : configuration.info)
|
||||
quest->getCompletionText(i.message);
|
||||
}
|
||||
|
||||
void CGSeerHut::getRolloverText(MetaString &text, bool onHover) const
|
||||
{
|
||||
quest->getRolloverText (text, onHover);//TODO: simplify?
|
||||
quest->getRolloverText(text, onHover);//TODO: simplify?
|
||||
if(!onHover)
|
||||
text.replaceRawString(seerName);
|
||||
}
|
||||
@@ -611,13 +492,15 @@ void CGSeerHut::getRolloverText(MetaString &text, bool onHover) const
|
||||
std::string CGSeerHut::getHoverText(PlayerColor player) const
|
||||
{
|
||||
std::string hoverName = getObjectName();
|
||||
if(ID == Obj::SEER_HUT && quest->progress != CQuest::NOT_ACTIVE)
|
||||
if(ID == Obj::SEER_HUT && quest->activeForPlayers.count(player))
|
||||
{
|
||||
hoverName = VLC->generaltexth->allTexts[347];
|
||||
boost::algorithm::replace_first(hoverName, "%s", seerName);
|
||||
}
|
||||
|
||||
if(quest->progress & quest->missionType) //rollover when the quest is active
|
||||
if(quest->activeForPlayers.count(player)
|
||||
&& (quest->mission != Rewardable::Limiter{}
|
||||
|| quest->killTarget != ObjectInstanceID::NONE)) //rollover when the quest is active
|
||||
{
|
||||
MetaString ms;
|
||||
getRolloverText (ms, true);
|
||||
@@ -626,48 +509,21 @@ std::string CGSeerHut::getHoverText(PlayerColor player) const
|
||||
return hoverName;
|
||||
}
|
||||
|
||||
void CQuest::addReplacements(MetaString &out, const std::string &base) const
|
||||
{
|
||||
switch(missionType)
|
||||
{
|
||||
case MISSION_KILL_CREATURE:
|
||||
if(stackToKill.type)
|
||||
{
|
||||
out.replaceCreatureName(stackToKill);
|
||||
if (std::count(base.begin(), base.end(), '%') == 2) //say where is placed monster
|
||||
{
|
||||
out.replaceRawString(VLC->generaltexth->arraytxt[147+stackDirection]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MISSION_KILL_HERO:
|
||||
out.replaceTextID(heroName);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool IQuestObject::checkQuest(const CGHeroInstance* h) const
|
||||
{
|
||||
return quest->checkQuest(h);
|
||||
}
|
||||
|
||||
void IQuestObject::getVisitText (MetaString &text, std::vector<Component> &components, bool isCustom, bool FirstVisit, const CGHeroInstance * h) const
|
||||
{
|
||||
quest->getVisitText (text,components, isCustom, FirstVisit, h);
|
||||
}
|
||||
|
||||
void IQuestObject::afterAddToMapCommon(CMap * map) const
|
||||
{
|
||||
map->addNewQuestInstance(quest);
|
||||
}
|
||||
|
||||
void CGSeerHut::setPropertyDer (ui8 what, ui32 val)
|
||||
void CGSeerHut::setPropertyDer(ui8 what, ui32 val)
|
||||
{
|
||||
switch(what)
|
||||
{
|
||||
case 10:
|
||||
quest->progress = static_cast<CQuest::Eprogress>(val);
|
||||
case CGSeerHut::SEERHUT_VISITED:
|
||||
{
|
||||
quest->activeForPlayers.emplace(val);
|
||||
break;
|
||||
}
|
||||
case CGSeerHut::SEERHUT_COMPLETE:
|
||||
{
|
||||
quest->isCompleted = val;
|
||||
quest->activeForPlayers.clear();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -676,7 +532,7 @@ void CGSeerHut::newTurn(CRandomGenerator & rand) const
|
||||
CRewardableObject::newTurn(rand);
|
||||
if(quest->lastDay >= 0 && quest->lastDay <= cb->getDate() - 1) //time is up
|
||||
{
|
||||
cb->setObjProperty (id, CGSeerHut::OBJPROP_VISITED, CQuest::COMPLETE);
|
||||
cb->setObjProperty (id, CGSeerHut::SEERHUT_COMPLETE, true);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -684,30 +540,24 @@ void CGSeerHut::onHeroVisit(const CGHeroInstance * h) const
|
||||
{
|
||||
InfoWindow iw;
|
||||
iw.player = h->getOwner();
|
||||
if(quest->progress < CQuest::COMPLETE)
|
||||
if(!quest->isCompleted)
|
||||
{
|
||||
bool firstVisit = !quest->progress;
|
||||
bool firstVisit = !quest->activeForPlayers.count(h->getOwner());
|
||||
bool failRequirements = !checkQuest(h);
|
||||
bool isCustom = false;
|
||||
|
||||
if(firstVisit)
|
||||
{
|
||||
isCustom = quest->isCustomFirst;
|
||||
cb->setObjProperty(id, CGSeerHut::OBJPROP_VISITED, CQuest::IN_PROGRESS);
|
||||
cb->setObjProperty(id, CGSeerHut::SEERHUT_VISITED, h->getOwner());
|
||||
|
||||
AddQuest aq;
|
||||
aq.quest = QuestInfo (quest, this, visitablePos());
|
||||
aq.player = h->tempOwner;
|
||||
cb->sendAndApply(&aq); //TODO: merge with setObjProperty?
|
||||
}
|
||||
else if(failRequirements)
|
||||
{
|
||||
isCustom = quest->isCustomNext;
|
||||
}
|
||||
|
||||
if(firstVisit || failRequirements)
|
||||
{
|
||||
getVisitText (iw.text, iw.components, isCustom, firstVisit, h);
|
||||
getVisitText (iw.text, iw.components, firstVisit, h);
|
||||
|
||||
cb->showInfoDialog(&iw);
|
||||
}
|
||||
@@ -758,26 +608,19 @@ int CGSeerHut::checkDirection() const
|
||||
}
|
||||
}
|
||||
|
||||
void CGSeerHut::completeQuest() const //reward
|
||||
{
|
||||
cb->setObjProperty(id, CGSeerHut::OBJPROP_VISITED, CQuest::COMPLETE); //mission complete
|
||||
}
|
||||
|
||||
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));
|
||||
return dynamic_cast<const CGHeroInstance *>(o);
|
||||
}
|
||||
|
||||
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);
|
||||
return dynamic_cast<const CGCreature *>(o);
|
||||
}
|
||||
|
||||
@@ -785,7 +628,10 @@ void CGSeerHut::blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer)
|
||||
{
|
||||
CRewardableObject::blockingDialogAnswered(hero, answer);
|
||||
if(answer)
|
||||
completeQuest();
|
||||
{
|
||||
quest->completeQuest(cb, hero);
|
||||
cb->setObjProperty(id, CGSeerHut::SEERHUT_COMPLETE, !quest->repeatedQuest); //mission complete
|
||||
}
|
||||
}
|
||||
|
||||
void CGSeerHut::afterAddToMap(CMap* map)
|
||||
@@ -874,10 +720,23 @@ 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.info.back().reward.removeObject = subID == 0 ? true : false;
|
||||
configuration.canRefuse = true;
|
||||
}
|
||||
|
||||
void CGQuestGuard::onHeroVisit(const CGHeroInstance * h) const
|
||||
{
|
||||
if(!quest->isCompleted)
|
||||
CGSeerHut::onHeroVisit(h);
|
||||
else
|
||||
cb->setObjProperty(id, CGSeerHut::SEERHUT_COMPLETE, false);
|
||||
}
|
||||
|
||||
bool CGQuestGuard::passableFor(PlayerColor color) const
|
||||
{
|
||||
return quest->isCompleted;
|
||||
}
|
||||
|
||||
void CGQuestGuard::serializeJsonOptions(JsonSerializeFormat & handler)
|
||||
{
|
||||
//quest only, do not call base class
|
||||
@@ -938,12 +797,12 @@ void CGBorderGuard::initObj(CRandomGenerator & rand)
|
||||
blockVisit = true;
|
||||
}
|
||||
|
||||
void CGBorderGuard::getVisitText (MetaString &text, std::vector<Component> &components, bool isCustom, bool FirstVisit, const CGHeroInstance * h) const
|
||||
void CGBorderGuard::getVisitText(MetaString &text, std::vector<Component> &components, bool FirstVisit, const CGHeroInstance * h) const
|
||||
{
|
||||
text.appendLocalString(EMetaText::ADVOB_TXT,18);
|
||||
text.appendLocalString(EMetaText::ADVOB_TXT, 18);
|
||||
}
|
||||
|
||||
void CGBorderGuard::getRolloverText (MetaString &text, bool onHover) const
|
||||
void CGBorderGuard::getRolloverText(MetaString &text, bool onHover) const
|
||||
{
|
||||
if (!onHover)
|
||||
{
|
||||
|
@@ -19,47 +19,21 @@ class CGCreature;
|
||||
|
||||
class DLL_LINKAGE CQuest final
|
||||
{
|
||||
mutable std::unordered_map<ArtifactID, unsigned, ArtifactID::hash> artifactsRequirements; // artifact ID -> required count
|
||||
|
||||
public:
|
||||
enum Emission {
|
||||
MISSION_NONE = 0,
|
||||
MISSION_LEVEL = 1,
|
||||
MISSION_PRIMARY_STAT = 2,
|
||||
MISSION_KILL_HERO = 3,
|
||||
MISSION_KILL_CREATURE = 4,
|
||||
MISSION_ART = 5,
|
||||
MISSION_ARMY = 6,
|
||||
MISSION_RESOURCES = 7,
|
||||
MISSION_HERO = 8,
|
||||
MISSION_PLAYER = 9,
|
||||
MISSION_HOTA_MULTI = 10,
|
||||
// end of H3 missions
|
||||
MISSION_KEYMASTER = 100,
|
||||
MISSION_HOTA_HERO_CLASS = 101,
|
||||
MISSION_HOTA_REACH_DATE = 102
|
||||
};
|
||||
|
||||
enum Eprogress {
|
||||
NOT_ACTIVE,
|
||||
IN_PROGRESS,
|
||||
COMPLETE
|
||||
};
|
||||
|
||||
static const std::string & missionName(Emission mission);
|
||||
static const std::string & missionState(int index);
|
||||
static const std::string & missionName(int index);
|
||||
static const std::string & missionState(int index);
|
||||
|
||||
std::string questName;
|
||||
|
||||
si32 qid; //unique quest id for serialization / identification
|
||||
|
||||
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;
|
||||
ObjectInstanceID killTarget;
|
||||
Rewardable::Limiter mission;
|
||||
bool repeatedQuest;
|
||||
bool isCompleted;
|
||||
std::set<PlayerColor> activeForPlayers;
|
||||
|
||||
// following fields are used only for kill creature/hero missions, the original
|
||||
// objects became inaccessible after their removal, so we need to store info
|
||||
@@ -79,13 +53,14 @@ public:
|
||||
CQuest(); //TODO: Remove constructor
|
||||
|
||||
static bool checkMissionArmy(const CQuest * q, const CCreatureSet * army);
|
||||
virtual bool checkQuest (const CGHeroInstance * h) const; //determines whether the quest is complete or not
|
||||
virtual void getVisitText (MetaString &text, std::vector<Component> &components, bool isCustom, bool FirstVisit, const CGHeroInstance * h = nullptr) const;
|
||||
virtual bool checkQuest(const CGHeroInstance * h) const; //determines whether the quest is complete or not
|
||||
virtual void getVisitText(MetaString &text, std::vector<Component> & components, bool FirstVisit, const CGHeroInstance * h = nullptr) const;
|
||||
virtual void getCompletionText(MetaString &text) const;
|
||||
virtual void getRolloverText (MetaString &text, bool onHover) const; //hover or quest log entry
|
||||
virtual void completeQuest (const CGHeroInstance * h) const {};
|
||||
virtual void addReplacements(MetaString &out, const std::string &base) const;
|
||||
void addArtifactID(const ArtifactID & id);
|
||||
virtual void completeQuest(IGameCallback *, const CGHeroInstance * h) const;
|
||||
virtual void addTextReplacements(MetaString &out, std::vector<Component> & components) const;
|
||||
virtual void addKillTargetReplacements(MetaString &out) const;
|
||||
void defineQuestName();
|
||||
|
||||
bool operator== (const CQuest & quest) const
|
||||
{
|
||||
@@ -95,14 +70,9 @@ public:
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
h & qid;
|
||||
h & missionType;
|
||||
h & progress;
|
||||
h & isCompleted;
|
||||
h & activeForPlayers;
|
||||
h & lastDay;
|
||||
h & m13489val;
|
||||
h & m2stats;
|
||||
h & m5arts;
|
||||
h & m6creatures;
|
||||
h & m7resources;
|
||||
h & textOption;
|
||||
h & stackToKill;
|
||||
h & stackDirection;
|
||||
@@ -115,6 +85,9 @@ public:
|
||||
h & isCustomNext;
|
||||
h & isCustomComplete;
|
||||
h & completedOption;
|
||||
h & questName;
|
||||
h & mission;
|
||||
h & killTarget;
|
||||
}
|
||||
|
||||
void serializeJson(JsonSerializeFormat & handler, const std::string & fieldName);
|
||||
@@ -128,7 +101,7 @@ public:
|
||||
///Information about quest should remain accessible even if IQuestObject removed from map
|
||||
///All CQuest objects are freed in CMap destructor
|
||||
virtual ~IQuestObject() = default;
|
||||
virtual void getVisitText (MetaString &text, std::vector<Component> &components, bool isCustom, bool FirstVisit, const CGHeroInstance * h = nullptr) const;
|
||||
virtual void getVisitText (MetaString &text, std::vector<Component> &components, bool FirstVisit, const CGHeroInstance * h = nullptr) const;
|
||||
virtual bool checkQuest (const CGHeroInstance * h) const;
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
@@ -156,8 +129,6 @@ public:
|
||||
const CGHeroInstance *getHeroToKill(bool allowNull = false) const;
|
||||
const CGCreature *getCreatureToKill(bool allowNull = false) const;
|
||||
void getRolloverText (MetaString &text, bool onHover) const;
|
||||
void finishQuest (const CGHeroInstance * h, ui32 accept) const; //common for both objects
|
||||
virtual void completeQuest() const;
|
||||
|
||||
void afterAddToMap(CMap * map) override;
|
||||
|
||||
@@ -168,7 +139,8 @@ public:
|
||||
h & seerName;
|
||||
}
|
||||
protected:
|
||||
static constexpr int OBJPROP_VISITED = 10;
|
||||
static constexpr int SEERHUT_VISITED = 10;
|
||||
static constexpr int SEERHUT_COMPLETE = 11;
|
||||
|
||||
void setPropertyDer(ui8 what, ui32 val) override;
|
||||
|
||||
@@ -179,6 +151,9 @@ class DLL_LINKAGE CGQuestGuard : public CGSeerHut
|
||||
{
|
||||
public:
|
||||
void init(CRandomGenerator & rand) override;
|
||||
|
||||
void onHeroVisit(const CGHeroInstance * h) const override;
|
||||
bool passableFor(PlayerColor color) const override;
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
@@ -228,7 +203,7 @@ public:
|
||||
void onHeroVisit(const CGHeroInstance * h) const override;
|
||||
void blockingDialogAnswered(const CGHeroInstance *hero, ui32 answer) const override;
|
||||
|
||||
void getVisitText (MetaString &text, std::vector<Component> &components, bool isCustom, bool FirstVisit, const CGHeroInstance * h = nullptr) const override;
|
||||
void getVisitText (MetaString &text, std::vector<Component> &components, bool FirstVisit, const CGHeroInstance * h = nullptr) const override;
|
||||
void getRolloverText (MetaString &text, bool onHover) const;
|
||||
bool checkQuest (const CGHeroInstance * h) const override;
|
||||
|
||||
|
@@ -144,6 +144,9 @@ ui8 CMapHeader::levels() const
|
||||
|
||||
void CMapHeader::registerMapStrings()
|
||||
{
|
||||
VLC->generaltexth->removeSubContainer(*this);
|
||||
VLC->generaltexth->addSubContainer(*this);
|
||||
|
||||
//get supported languages. Assuming that translation containing most strings is the base language
|
||||
std::set<std::string> mapLanguages, mapBaseLanguages;
|
||||
int maxStrings = 0;
|
||||
|
@@ -105,7 +105,10 @@ std::string CMapInfo::getNameTranslated() const
|
||||
if(campaign && !campaign->getNameTranslated().empty())
|
||||
return campaign->getNameTranslated();
|
||||
else if(mapHeader && !mapHeader->name.empty())
|
||||
{
|
||||
mapHeader->registerMapStrings();
|
||||
return mapHeader->name.toString();
|
||||
}
|
||||
else
|
||||
return VLC->generaltexth->allTexts[508];
|
||||
}
|
||||
|
@@ -1830,6 +1830,7 @@ CGObjectInstance * CMapLoaderH3M::readSeerHut(const int3 & position, const Objec
|
||||
if(features.levelHOTA3)
|
||||
{
|
||||
uint32_t repeateableQuestsCount = reader->readUInt32();
|
||||
hut->quest->repeatedQuest = repeateableQuestsCount != 0;
|
||||
|
||||
if(repeateableQuestsCount != 0)
|
||||
logGlobal->warn("Map '%s': Seer Hut at %s - %d repeatable quests are not implemented!", mapName, position.toString(), repeateableQuestsCount);
|
||||
@@ -1858,11 +1859,30 @@ enum class ESeerHutRewardType : uint8_t
|
||||
CREATURE = 10,
|
||||
};
|
||||
|
||||
enum class EQuestMission {
|
||||
NONE = 0,
|
||||
LEVEL = 1,
|
||||
PRIMARY_SKILL = 2,
|
||||
KILL_HERO = 3,
|
||||
KILL_CREATURE = 4,
|
||||
ARTIFACT = 5,
|
||||
ARMY = 6,
|
||||
RESOURCES = 7,
|
||||
HERO = 8,
|
||||
PLAYER = 9,
|
||||
HOTA_MULTI = 10,
|
||||
// end of H3 missions
|
||||
KEYMASTER = 100,
|
||||
HOTA_HERO_CLASS = 101,
|
||||
HOTA_REACH_DATE = 102
|
||||
};
|
||||
|
||||
void CMapLoaderH3M::readSeerHutQuest(CGSeerHut * hut, const int3 & position, const ObjectInstanceID & idToBeGiven)
|
||||
{
|
||||
EQuestMission missionType = EQuestMission::NONE;
|
||||
if(features.levelAB)
|
||||
{
|
||||
readQuest(hut, position);
|
||||
missionType = static_cast<EQuestMission>(readQuest(hut, position));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1871,12 +1891,8 @@ void CMapLoaderH3M::readSeerHutQuest(CGSeerHut * hut, const int3 & position, con
|
||||
if(artID != ArtifactID::NONE)
|
||||
{
|
||||
//not none quest
|
||||
hut->quest->addArtifactID(artID);
|
||||
hut->quest->missionType = CQuest::MISSION_ART;
|
||||
}
|
||||
else
|
||||
{
|
||||
hut->quest->missionType = CQuest::MISSION_NONE;
|
||||
hut->quest->mission.artifacts.push_back(artID);
|
||||
missionType = EQuestMission::ARTIFACT;
|
||||
}
|
||||
hut->quest->lastDay = -1; //no timeout
|
||||
hut->quest->isCustomFirst = false;
|
||||
@@ -1884,7 +1900,7 @@ void CMapLoaderH3M::readSeerHutQuest(CGSeerHut * hut, const int3 & position, con
|
||||
hut->quest->isCustomComplete = false;
|
||||
}
|
||||
|
||||
if(hut->quest->missionType)
|
||||
if(missionType != EQuestMission::NONE)
|
||||
{
|
||||
auto rewardType = static_cast<ESeerHutRewardType>(reader->readUInt8());
|
||||
Rewardable::VisitInfo vinfo;
|
||||
@@ -1976,91 +1992,91 @@ void CMapLoaderH3M::readSeerHutQuest(CGSeerHut * hut, const int3 & position, con
|
||||
}
|
||||
}
|
||||
|
||||
void CMapLoaderH3M::readQuest(IQuestObject * guard, const int3 & position)
|
||||
int CMapLoaderH3M::readQuest(IQuestObject * guard, const int3 & position)
|
||||
{
|
||||
guard->quest->missionType = static_cast<CQuest::Emission>(reader->readUInt8());
|
||||
auto missionId = reader->readUInt8();
|
||||
|
||||
switch(guard->quest->missionType)
|
||||
switch(static_cast<EQuestMission>(missionId))
|
||||
{
|
||||
case CQuest::MISSION_NONE:
|
||||
return;
|
||||
case CQuest::MISSION_PRIMARY_STAT:
|
||||
case EQuestMission::NONE:
|
||||
return missionId;
|
||||
case EQuestMission::PRIMARY_SKILL:
|
||||
{
|
||||
guard->quest->m2stats.resize(4);
|
||||
for(int x = 0; x < 4; ++x)
|
||||
{
|
||||
guard->quest->m2stats[x] = reader->readUInt8();
|
||||
guard->quest->mission.primary[x] = reader->readUInt8();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CQuest::MISSION_LEVEL:
|
||||
case CQuest::MISSION_KILL_HERO:
|
||||
case CQuest::MISSION_KILL_CREATURE:
|
||||
{
|
||||
guard->quest->m13489val = reader->readUInt32();
|
||||
break;
|
||||
}
|
||||
case CQuest::MISSION_ART:
|
||||
case EQuestMission::LEVEL:
|
||||
{
|
||||
guard->quest->mission.heroLevel = reader->readUInt32();
|
||||
break;
|
||||
}
|
||||
case EQuestMission::KILL_HERO:
|
||||
case EQuestMission::KILL_CREATURE:
|
||||
{
|
||||
guard->quest->killTarget = ObjectInstanceID(reader->readUInt32());
|
||||
break;
|
||||
}
|
||||
case EQuestMission::ARTIFACT:
|
||||
{
|
||||
int artNumber = reader->readUInt8();
|
||||
for(int yy = 0; yy < artNumber; ++yy)
|
||||
{
|
||||
auto artid = reader->readArtifact();
|
||||
guard->quest->addArtifactID(artid);
|
||||
guard->quest->mission.artifacts.push_back(artid);
|
||||
map->allowedArtifact[artid] = false; //these are unavailable for random generation
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CQuest::MISSION_ARMY:
|
||||
case EQuestMission::ARMY:
|
||||
{
|
||||
int typeNumber = reader->readUInt8();
|
||||
guard->quest->m6creatures.resize(typeNumber);
|
||||
guard->quest->mission.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->mission.creatures[hh].type = VLC->creh->objects[reader->readCreature()];
|
||||
guard->quest->mission.creatures[hh].count = reader->readUInt16();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CQuest::MISSION_RESOURCES:
|
||||
case EQuestMission::RESOURCES:
|
||||
{
|
||||
for(int x = 0; x < 7; ++x)
|
||||
guard->quest->m7resources[x] = reader->readUInt32();
|
||||
guard->quest->mission.resources[x] = reader->readUInt32();
|
||||
|
||||
break;
|
||||
}
|
||||
case CQuest::MISSION_HERO:
|
||||
case EQuestMission::HERO:
|
||||
{
|
||||
guard->quest->m13489val = reader->readHero().getNum();
|
||||
guard->quest->mission.heroes.push_back(reader->readHero());
|
||||
break;
|
||||
}
|
||||
case CQuest::MISSION_PLAYER:
|
||||
case EQuestMission::PLAYER:
|
||||
{
|
||||
guard->quest->m13489val = reader->readPlayer().getNum();
|
||||
guard->quest->mission.players.push_back(reader->readPlayer());
|
||||
break;
|
||||
}
|
||||
case CQuest::MISSION_HOTA_MULTI:
|
||||
case EQuestMission::HOTA_MULTI:
|
||||
{
|
||||
uint32_t missionSubID = reader->readUInt32();
|
||||
|
||||
if(missionSubID == 0)
|
||||
{
|
||||
guard->quest->missionType = CQuest::MISSION_NONE; //TODO: CQuest::MISSION_HOTA_HERO_CLASS;
|
||||
missionId = int(EQuestMission::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->mission.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);
|
||||
missionId = int(EQuestMission::HOTA_REACH_DATE);
|
||||
guard->quest->mission.daysPassed = reader->readUInt32() + 1;
|
||||
break;
|
||||
}
|
||||
assert(0);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@@ -2076,6 +2092,7 @@ void CMapLoaderH3M::readQuest(IQuestObject * guard, const int3 & position)
|
||||
guard->quest->isCustomFirst = !guard->quest->firstVisitText.empty();
|
||||
guard->quest->isCustomNext = !guard->quest->nextVisitText.empty();
|
||||
guard->quest->isCustomComplete = !guard->quest->completedText.empty();
|
||||
return missionId;
|
||||
}
|
||||
|
||||
CGObjectInstance * CMapLoaderH3M::readTown(const int3 & position, std::shared_ptr<const ObjectTemplate> objectTemplate)
|
||||
|
@@ -204,7 +204,7 @@ private:
|
||||
*
|
||||
* @param guard the quest guard where that quest should be applied to
|
||||
*/
|
||||
void readQuest(IQuestObject * guard, const int3 & position);
|
||||
int readQuest(IQuestObject * guard, const int3 & position);
|
||||
|
||||
void readSeerHutQuest(CGSeerHut * hut, const int3 & position, const ObjectInstanceID & idToBeGiven);
|
||||
|
||||
|
@@ -122,6 +122,10 @@ void Rewardable::Info::configureLimiter(Rewardable::Configuration & object, CRan
|
||||
limiter.artifacts = JsonRandom::loadArtifacts(source["artifacts"], rng);
|
||||
limiter.spells = JsonRandom::loadSpells(source["spells"], rng, spells);
|
||||
limiter.creatures = JsonRandom::loadCreatures(source["creatures"], rng);
|
||||
|
||||
limiter.players = JsonRandom::loadColors(source["colors"], rng);
|
||||
limiter.heroes = JsonRandom::loadHeroes(source["heroes"], rng);
|
||||
limiter.heroClasses = JsonRandom::loadHeroClasses(source["heroClasses"], rng);
|
||||
|
||||
limiter.allOf = configureSublimiters(object, rng, source["allOf"] );
|
||||
limiter.anyOf = configureSublimiters(object, rng, source["anyOf"] );
|
||||
|
@@ -16,7 +16,9 @@
|
||||
#include "../mapObjects/CGHeroInstance.h"
|
||||
#include "../serializer/JsonSerializeFormat.h"
|
||||
#include "../constants/StringConstants.h"
|
||||
#include "../CHeroHandler.h"
|
||||
#include "../CSkillHandler.h"
|
||||
#include "../ArtifactUtils.h"
|
||||
|
||||
VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
@@ -24,7 +26,7 @@ Rewardable::Limiter::Limiter()
|
||||
: dayOfWeek(0)
|
||||
, daysPassed(0)
|
||||
, heroExperience(0)
|
||||
, heroLevel(0)
|
||||
, heroLevel(-1)
|
||||
, manaPercentage(0)
|
||||
, manaPoints(0)
|
||||
, primary(GameConstants::PRIMARY_SKILLS, 0)
|
||||
@@ -33,6 +35,33 @@ Rewardable::Limiter::Limiter()
|
||||
|
||||
Rewardable::Limiter::~Limiter() = default;
|
||||
|
||||
bool operator==(const Rewardable::Limiter & l, const Rewardable::Limiter & r)
|
||||
{
|
||||
return l.dayOfWeek == r.dayOfWeek
|
||||
&& l.daysPassed == r.daysPassed
|
||||
&& l.heroLevel == r.heroLevel
|
||||
&& l.heroExperience == r.heroExperience
|
||||
&& l.manaPoints == r.manaPoints
|
||||
&& l.manaPercentage == r.manaPercentage
|
||||
&& l.secondary == r.secondary
|
||||
&& l.creatures == r.creatures
|
||||
&& l.spells == r.spells
|
||||
&& l.artifacts == r.artifacts
|
||||
&& l.players == r.players
|
||||
&& l.heroes == r.heroes
|
||||
&& l.heroClasses == r.heroClasses
|
||||
&& l.resources == r.resources
|
||||
&& l.primary == r.primary
|
||||
&& l.noneOf == r.noneOf
|
||||
&& l.allOf == r.allOf
|
||||
&& l.anyOf == r.anyOf;
|
||||
}
|
||||
|
||||
bool operator!=(const Rewardable::Limiter & l, const Rewardable::Limiter & r)
|
||||
{
|
||||
return !(l == r);
|
||||
}
|
||||
|
||||
bool Rewardable::Limiter::heroAllowed(const CGHeroInstance * hero) const
|
||||
{
|
||||
if(dayOfWeek != 0)
|
||||
@@ -93,12 +122,34 @@ bool Rewardable::Limiter::heroAllowed(const CGHeroInstance * hero) const
|
||||
return false;
|
||||
}
|
||||
|
||||
for(const auto & art : artifacts)
|
||||
{
|
||||
if (!hero->hasArt(art))
|
||||
std::unordered_map<ArtifactID, unsigned int, ArtifactID::hash> artifactsRequirements; // artifact ID -> required count
|
||||
for(const auto & art : artifacts)
|
||||
++artifactsRequirements[art];
|
||||
|
||||
size_t reqSlots = 0;
|
||||
for(const auto & elem : artifactsRequirements)
|
||||
{
|
||||
// check required amount of artifacts
|
||||
if(hero->getArtPosCount(elem.first, false, true, true) < elem.second)
|
||||
return false;
|
||||
if(!hero->hasArt(elem.first))
|
||||
reqSlots += hero->getAssemblyByConstituent(elem.first)->getPartsInfo().size() - 2;
|
||||
}
|
||||
if(!ArtifactUtils::isBackpackFreeSlots(hero, reqSlots))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
if(!players.empty() && !vstd::contains(players, hero->getOwner()))
|
||||
return false;
|
||||
|
||||
if(!heroes.empty() && !vstd::contains(heroes, hero->type->getId()))
|
||||
return false;
|
||||
|
||||
if(!heroClasses.empty() && !vstd::contains(heroClasses, hero->type->heroClass->getId()))
|
||||
return false;
|
||||
|
||||
|
||||
for(const auto & sublimiter : noneOf)
|
||||
{
|
||||
if (sublimiter->heroAllowed(hero))
|
||||
@@ -122,6 +173,56 @@ 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 > 0)
|
||||
comps.emplace_back(Component::EComponentType::EXPERIENCE, 1, heroLevel, 0);
|
||||
|
||||
if (manaPoints || manaPercentage > 0)
|
||||
{
|
||||
int absoluteMana = h->manaLimit() ? (manaPercentage * h->mana / h->manaLimit() / 100) : 0;
|
||||
comps.emplace_back(Component::EComponentType::PRIM_SKILL, 5, absoluteMana + manaPoints, 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);
|
||||
@@ -130,6 +231,9 @@ void Rewardable::Limiter::serializeJson(JsonSerializeFormat & handler)
|
||||
handler.serializeInt("manaPercentage", manaPercentage);
|
||||
handler.serializeInt("heroExperience", heroExperience);
|
||||
handler.serializeInt("heroLevel", heroLevel);
|
||||
handler.serializeIdArray("heroes", heroes);
|
||||
handler.serializeIdArray("heroClasses", heroClasses);
|
||||
handler.serializeIdArray("colors", players);
|
||||
handler.serializeInt("manaPoints", manaPoints);
|
||||
handler.serializeIdArray("artifacts", artifacts);
|
||||
handler.enterArray("creatures").serializeStruct(creatures);
|
||||
|
@@ -17,6 +17,7 @@ VCMI_LIB_NAMESPACE_BEGIN
|
||||
|
||||
class CGHeroInstance;
|
||||
class CStackBasicDescriptor;
|
||||
struct Component;
|
||||
|
||||
namespace Rewardable {
|
||||
|
||||
@@ -25,8 +26,7 @@ using LimitersList = std::vector<std::shared_ptr<Rewardable::Limiter>>;
|
||||
|
||||
/// Limiters of rewards. Rewards will be granted to hero only if he satisfies requirements
|
||||
/// Note: for this is only a test - it won't remove anything from hero (e.g. artifacts or creatures)
|
||||
/// NOTE: in future should (partially) replace seer hut/quest guard quests checks
|
||||
struct DLL_LINKAGE Limiter
|
||||
struct DLL_LINKAGE Limiter final
|
||||
{
|
||||
/// day of week, unused if 0, 1-7 will test for current day of week
|
||||
si32 dayOfWeek;
|
||||
@@ -52,7 +52,7 @@ struct DLL_LINKAGE Limiter
|
||||
std::map<SecondarySkill, si32> secondary;
|
||||
|
||||
/// artifacts that hero needs to have (equipped or in backpack) to trigger this
|
||||
/// Note: does not checks for multiple copies of the same arts
|
||||
/// checks for artifacts copies if same artifact id is included multiple times
|
||||
std::vector<ArtifactID> artifacts;
|
||||
|
||||
/// Spells that hero must have in the spellbook
|
||||
@@ -60,6 +60,13 @@ struct DLL_LINKAGE Limiter
|
||||
|
||||
/// creatures that hero needs to have
|
||||
std::vector<CStackBasicDescriptor> creatures;
|
||||
|
||||
/// only heroes/hero classes from list could pass limiter
|
||||
std::vector<HeroTypeID> heroes;
|
||||
std::vector<HeroClassID> heroClasses;
|
||||
|
||||
/// only player colors can pass limiter
|
||||
std::vector<PlayerColor> players;
|
||||
|
||||
/// sub-limiters, all must pass for this limiter to pass
|
||||
LimitersList allOf;
|
||||
@@ -74,6 +81,10 @@ struct DLL_LINKAGE Limiter
|
||||
~Limiter();
|
||||
|
||||
bool heroAllowed(const CGHeroInstance * hero) const;
|
||||
|
||||
/// Generates list of components that describes reward for a specific hero
|
||||
void loadComponents(std::vector<Component> & comps,
|
||||
const CGHeroInstance * h) const;
|
||||
|
||||
template <typename Handler> void serialize(Handler &h, const int version)
|
||||
{
|
||||
@@ -88,6 +99,9 @@ struct DLL_LINKAGE Limiter
|
||||
h & secondary;
|
||||
h & artifacts;
|
||||
h & creatures;
|
||||
h & heroes;
|
||||
h & heroClasses;
|
||||
h & players;
|
||||
h & allOf;
|
||||
h & anyOf;
|
||||
h & noneOf;
|
||||
@@ -98,4 +112,7 @@ struct DLL_LINKAGE Limiter
|
||||
|
||||
}
|
||||
|
||||
bool DLL_LINKAGE operator== (const Rewardable::Limiter & l, const Rewardable::Limiter & r);
|
||||
bool DLL_LINKAGE operator!= (const Rewardable::Limiter & l, const Rewardable::Limiter & r);
|
||||
|
||||
VCMI_LIB_NAMESPACE_END
|
||||
|
@@ -29,7 +29,7 @@ using RewardsList = std::vector<std::shared_ptr<Rewardable::Reward>>;
|
||||
|
||||
/// Reward that can be granted to a hero
|
||||
/// NOTE: eventually should replace seer hut rewards and events/pandoras
|
||||
struct DLL_LINKAGE Reward
|
||||
struct DLL_LINKAGE Reward final
|
||||
{
|
||||
/// resources that will be given to player
|
||||
TResources resources;
|
||||
@@ -78,7 +78,7 @@ struct DLL_LINKAGE Reward
|
||||
bool removeObject;
|
||||
|
||||
/// Generates list of components that describes reward for a specific hero
|
||||
virtual void loadComponents(std::vector<Component> & comps,
|
||||
void loadComponents(std::vector<Component> & comps,
|
||||
const CGHeroInstance * h) const;
|
||||
|
||||
Component getDisplayedComponent(const CGHeroInstance * h) const;
|
||||
|
@@ -460,13 +460,9 @@ void TreasurePlacer::addAllPossibleObjects()
|
||||
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;
|
||||
|
||||
|
||||
ArtifactID artid = qap->drawRandomArtifact();
|
||||
obj->quest->addArtifactID(artid);
|
||||
obj->quest->lastDay = -1;
|
||||
obj->quest->isCustomFirst = obj->quest->isCustomNext = obj->quest->isCustomComplete = false;
|
||||
obj->quest->mission.artifacts.push_back(artid);
|
||||
|
||||
generator.banQuestArt(artid);
|
||||
zone.getModificator<QuestArtifactPlacer>()->addQuestArtifact(artid);
|
||||
@@ -513,11 +509,8 @@ void TreasurePlacer::addAllPossibleObjects()
|
||||
reward.visitType = Rewardable::EEventType::EVENT_FIRST_VISIT;
|
||||
obj->configuration.info.push_back(reward);
|
||||
|
||||
obj->quest->missionType = CQuest::MISSION_ART;
|
||||
ArtifactID artid = qap->drawRandomArtifact();
|
||||
obj->quest->addArtifactID(artid);
|
||||
obj->quest->lastDay = -1;
|
||||
obj->quest->isCustomFirst = obj->quest->isCustomNext = obj->quest->isCustomComplete = false;
|
||||
obj->quest->mission.artifacts.push_back(artid);
|
||||
|
||||
generator.banQuestArt(artid);
|
||||
zone.getModificator<QuestArtifactPlacer>()->addQuestArtifact(artid);
|
||||
@@ -538,11 +531,8 @@ void TreasurePlacer::addAllPossibleObjects()
|
||||
reward.visitType = Rewardable::EEventType::EVENT_FIRST_VISIT;
|
||||
obj->configuration.info.push_back(reward);
|
||||
|
||||
obj->quest->missionType = CQuest::MISSION_ART;
|
||||
ArtifactID artid = qap->drawRandomArtifact();
|
||||
obj->quest->addArtifactID(artid);
|
||||
obj->quest->lastDay = -1;
|
||||
obj->quest->isCustomFirst = obj->quest->isCustomNext = obj->quest->isCustomComplete = false;
|
||||
obj->quest->mission.artifacts.push_back(artid);
|
||||
|
||||
generator.banQuestArt(artid);
|
||||
zone.getModificator<QuestArtifactPlacer>()->addQuestArtifact(artid);
|
||||
|
@@ -29,20 +29,6 @@
|
||||
#include "PickObjectDelegate.h"
|
||||
#include "../mapcontroller.h"
|
||||
|
||||
static QList<std::pair<QString, QVariant>> MissionIdentifiers
|
||||
{
|
||||
{QObject::tr("None"), QVariant::fromValue(int(CQuest::Emission::MISSION_NONE))},
|
||||
{QObject::tr("Reach level"), QVariant::fromValue(int(CQuest::Emission::MISSION_LEVEL))},
|
||||
{QObject::tr("Stats"), QVariant::fromValue(int(CQuest::Emission::MISSION_PRIMARY_STAT))},
|
||||
{QObject::tr("Kill hero"), QVariant::fromValue(int(CQuest::Emission::MISSION_KILL_HERO))},
|
||||
{QObject::tr("Kill monster"), QVariant::fromValue(int(CQuest::Emission::MISSION_KILL_CREATURE))},
|
||||
{QObject::tr("Artifact"), QVariant::fromValue(int(CQuest::Emission::MISSION_ART))},
|
||||
{QObject::tr("Army"), QVariant::fromValue(int(CQuest::Emission::MISSION_ARMY))},
|
||||
{QObject::tr("Resources"), QVariant::fromValue(int(CQuest::Emission::MISSION_RESOURCES))},
|
||||
{QObject::tr("Hero"), QVariant::fromValue(int(CQuest::Emission::MISSION_HERO))},
|
||||
{QObject::tr("Player"), QVariant::fromValue(int(CQuest::Emission::MISSION_PLAYER))},
|
||||
};
|
||||
|
||||
static QList<std::pair<QString, QVariant>> CharacterIdentifiers
|
||||
{
|
||||
{QObject::tr("Compliant"), QVariant::fromValue(int(CGCreature::Character::COMPLIANT))},
|
||||
@@ -410,24 +396,28 @@ void Inspector::updateProperties(CGEvent * o)
|
||||
|
||||
void Inspector::updateProperties(CGSeerHut * o)
|
||||
{
|
||||
if(!o) return;
|
||||
|
||||
{ //Mission type
|
||||
auto * delegate = new InspectorDelegate;
|
||||
delegate->options = MissionIdentifiers;
|
||||
addProperty<CQuest::Emission>("Mission type", o->quest->missionType, delegate, false);
|
||||
}
|
||||
if(!o || !o->quest) return;
|
||||
|
||||
addProperty("First visit text", o->quest->firstVisitText, new MessageDelegate, false);
|
||||
addProperty("Next visit text", o->quest->nextVisitText, new MessageDelegate, false);
|
||||
addProperty("Completed text", o->quest->completedText, new MessageDelegate, false);
|
||||
addProperty("Repeat quest", o->quest->repeatedQuest, false);
|
||||
addProperty("Time limit", o->quest->lastDay, false);
|
||||
|
||||
{ //Quest
|
||||
auto * delegate = new QuestDelegate(*controller.map(), *o);
|
||||
auto * delegate = new QuestDelegate(controller, *o->quest);
|
||||
addProperty("Quest", PropertyEditorPlaceholder(), delegate, false);
|
||||
}
|
||||
}
|
||||
|
||||
void Inspector::updateProperties(CGQuestGuard * o)
|
||||
{
|
||||
if(!o || !o->quest) return;
|
||||
|
||||
addProperty("Reward", PropertyEditorPlaceholder(), nullptr, true);
|
||||
addProperty("Repeat quest", o->quest->repeatedQuest, true);
|
||||
}
|
||||
|
||||
void Inspector::updateProperties()
|
||||
{
|
||||
if(!obj)
|
||||
@@ -470,6 +460,7 @@ void Inspector::updateProperties()
|
||||
UPDATE_OBJ_PROPERTIES(CGPandoraBox);
|
||||
UPDATE_OBJ_PROPERTIES(CGEvent);
|
||||
UPDATE_OBJ_PROPERTIES(CGSeerHut);
|
||||
UPDATE_OBJ_PROPERTIES(CGQuestGuard);
|
||||
|
||||
table->show();
|
||||
}
|
||||
@@ -516,6 +507,7 @@ void Inspector::setProperty(const QString & key, const QVariant & value)
|
||||
SET_PROPERTIES(CGPandoraBox);
|
||||
SET_PROPERTIES(CGEvent);
|
||||
SET_PROPERTIES(CGSeerHut);
|
||||
SET_PROPERTIES(CGQuestGuard);
|
||||
}
|
||||
|
||||
void Inspector::setProperty(CArmedInstance * o, const QString & key, const QVariant & value)
|
||||
@@ -538,7 +530,8 @@ void Inspector::setProperty(CGPandoraBox * o, const QString & key, const QVarian
|
||||
if(!o) return;
|
||||
|
||||
if(key == "Message")
|
||||
o->message = MetaString::createFromTextID(mapRegisterLocalizedString("map", *controller.map(), TextIdentifier("guards", o->instanceName, "message"), value.toString().toStdString()));
|
||||
o->message = MetaString::createFromTextID(mapRegisterLocalizedString("map", *controller.map(),
|
||||
TextIdentifier("guards", o->instanceName, "message"), value.toString().toStdString()));
|
||||
}
|
||||
|
||||
void Inspector::setProperty(CGEvent * o, const QString & key, const QVariant & value)
|
||||
@@ -560,7 +553,8 @@ void Inspector::setProperty(CGTownInstance * o, const QString & key, const QVari
|
||||
if(!o) return;
|
||||
|
||||
if(key == "Town name")
|
||||
o->setNameTextId(mapRegisterLocalizedString("map", *controller.map(), TextIdentifier("town", o->instanceName, "name"), value.toString().toStdString()));
|
||||
o->setNameTextId(mapRegisterLocalizedString("map", *controller.map(),
|
||||
TextIdentifier("town", o->instanceName, "name"), value.toString().toStdString()));
|
||||
}
|
||||
|
||||
void Inspector::setProperty(CGSignBottle * o, const QString & key, const QVariant & value)
|
||||
@@ -568,7 +562,8 @@ void Inspector::setProperty(CGSignBottle * o, const QString & key, const QVarian
|
||||
if(!o) return;
|
||||
|
||||
if(key == "Message")
|
||||
o->message = MetaString::createFromTextID(mapRegisterLocalizedString("map", *controller.map(), TextIdentifier("sign", o->instanceName, "message"), value.toString().toStdString()));
|
||||
o->message = MetaString::createFromTextID(mapRegisterLocalizedString("map", *controller.map(),
|
||||
TextIdentifier("sign", o->instanceName, "message"), value.toString().toStdString()));
|
||||
}
|
||||
|
||||
void Inspector::setProperty(CGMine * o, const QString & key, const QVariant & value)
|
||||
@@ -584,7 +579,8 @@ void Inspector::setProperty(CGArtifact * o, const QString & key, const QVariant
|
||||
if(!o) return;
|
||||
|
||||
if(key == "Message")
|
||||
o->message = MetaString::createFromTextID(mapRegisterLocalizedString("map", *controller.map(), TextIdentifier("guards", o->instanceName, "message"), value.toString().toStdString()));
|
||||
o->message = MetaString::createFromTextID(mapRegisterLocalizedString("map", *controller.map(),
|
||||
TextIdentifier("guards", o->instanceName, "message"), value.toString().toStdString()));
|
||||
|
||||
if(o->storedArtifact && key == "Spell")
|
||||
{
|
||||
@@ -623,10 +619,12 @@ void Inspector::setProperty(CGHeroInstance * o, const QString & key, const QVari
|
||||
o->gender = EHeroGender(value.toInt());
|
||||
|
||||
if(key == "Name")
|
||||
o->nameCustomTextId = mapRegisterLocalizedString("map", *controller.map(), TextIdentifier("hero", o->instanceName, "name"), value.toString().toStdString());
|
||||
o->nameCustomTextId = mapRegisterLocalizedString("map", *controller.map(),
|
||||
TextIdentifier("hero", o->instanceName, "name"), value.toString().toStdString());
|
||||
|
||||
if(key == "Biography")
|
||||
o->biographyCustomTextId = mapRegisterLocalizedString("map", *controller.map(), TextIdentifier("hero", o->instanceName, "biography"), value.toString().toStdString());
|
||||
o->biographyCustomTextId = mapRegisterLocalizedString("map", *controller.map(),
|
||||
TextIdentifier("hero", o->instanceName, "biography"), value.toString().toStdString());
|
||||
|
||||
if(key == "Experience")
|
||||
o->exp = value.toString().toInt();
|
||||
@@ -662,7 +660,8 @@ void Inspector::setProperty(CGCreature * o, const QString & key, const QVariant
|
||||
if(!o) return;
|
||||
|
||||
if(key == "Message")
|
||||
o->message = MetaString::createFromTextID(mapRegisterLocalizedString("map", *controller.map(), TextIdentifier("monster", o->instanceName, "message"), value.toString().toStdString()));
|
||||
o->message = MetaString::createFromTextID(mapRegisterLocalizedString("map", *controller.map(),
|
||||
TextIdentifier("monster", o->instanceName, "message"), value.toString().toStdString()));
|
||||
if(key == "Character")
|
||||
o->character = CGCreature::Character(value.toInt());
|
||||
if(key == "Never flees")
|
||||
@@ -677,14 +676,24 @@ void Inspector::setProperty(CGSeerHut * o, const QString & key, const QVariant &
|
||||
{
|
||||
if(!o) return;
|
||||
|
||||
if(key == "Mission type")
|
||||
o->quest->missionType = CQuest::Emission(value.toInt());
|
||||
if(key == "First visit text")
|
||||
o->quest->firstVisitText = MetaString::createFromTextID(mapRegisterLocalizedString("map", *controller.map(), TextIdentifier("quest", o->instanceName, "firstVisit"), value.toString().toStdString()));
|
||||
o->quest->firstVisitText = MetaString::createFromTextID(mapRegisterLocalizedString("map", *controller.map(),
|
||||
TextIdentifier("quest", o->instanceName, "firstVisit"), value.toString().toStdString()));
|
||||
if(key == "Next visit text")
|
||||
o->quest->nextVisitText = MetaString::createFromTextID(mapRegisterLocalizedString("map", *controller.map(), TextIdentifier("quest", o->instanceName, "nextVisit"), value.toString().toStdString()));
|
||||
o->quest->nextVisitText = MetaString::createFromTextID(mapRegisterLocalizedString("map", *controller.map(),
|
||||
TextIdentifier("quest", o->instanceName, "nextVisit"), value.toString().toStdString()));
|
||||
if(key == "Completed text")
|
||||
o->quest->completedText = MetaString::createFromTextID(mapRegisterLocalizedString("map", *controller.map(), TextIdentifier("quest", o->instanceName, "completed"), value.toString().toStdString()));
|
||||
o->quest->completedText = MetaString::createFromTextID(mapRegisterLocalizedString("map", *controller.map(),
|
||||
TextIdentifier("quest", o->instanceName, "completed"), value.toString().toStdString()));
|
||||
if(key == "Repeat quest")
|
||||
o->quest->repeatedQuest = value.toBool();
|
||||
if(key == "Time limit")
|
||||
o->quest->lastDay = value.toString().toInt();
|
||||
}
|
||||
|
||||
void Inspector::setProperty(CGQuestGuard * o, const QString & key, const QVariant & value)
|
||||
{
|
||||
if(!o) return;
|
||||
}
|
||||
|
||||
|
||||
@@ -797,24 +806,6 @@ QTableWidgetItem * Inspector::addProperty(CGCreature::Character value)
|
||||
return item;
|
||||
}
|
||||
|
||||
QTableWidgetItem * Inspector::addProperty(CQuest::Emission value)
|
||||
{
|
||||
auto * item = new QTableWidgetItem;
|
||||
item->setFlags(Qt::NoItemFlags);
|
||||
item->setData(Qt::UserRole, QVariant::fromValue(int(value)));
|
||||
|
||||
for(auto & i : MissionIdentifiers)
|
||||
{
|
||||
if(i.second.toInt() == value)
|
||||
{
|
||||
item->setText(i.first);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
//========================================================================
|
||||
|
||||
Inspector::Inspector(MapController & c, CGObjectInstance * o, QTableWidget * t): obj(o), table(t), controller(c)
|
||||
|
@@ -82,6 +82,7 @@ protected:
|
||||
DECLARE_OBJ_PROPERTY_METHODS(CGPandoraBox);
|
||||
DECLARE_OBJ_PROPERTY_METHODS(CGEvent);
|
||||
DECLARE_OBJ_PROPERTY_METHODS(CGSeerHut);
|
||||
DECLARE_OBJ_PROPERTY_METHODS(CGQuestGuard);
|
||||
|
||||
//===============DECLARE PROPERTY VALUE TYPE==============================
|
||||
QTableWidgetItem * addProperty(unsigned int value);
|
||||
@@ -96,7 +97,6 @@ protected:
|
||||
QTableWidgetItem * addProperty(bool value);
|
||||
QTableWidgetItem * addProperty(CGObjectInstance * value);
|
||||
QTableWidgetItem * addProperty(CGCreature::Character value);
|
||||
QTableWidgetItem * addProperty(CQuest::Emission value);
|
||||
QTableWidgetItem * addProperty(PropertyEditorPlaceholder value);
|
||||
|
||||
//===============END OF DECLARATION=======================================
|
||||
|
@@ -10,21 +10,124 @@
|
||||
#include "StdInc.h"
|
||||
#include "questwidget.h"
|
||||
#include "ui_questwidget.h"
|
||||
#include "../mapcontroller.h"
|
||||
#include "../lib/VCMI_Lib.h"
|
||||
#include "../lib/CSkillHandler.h"
|
||||
#include "../lib/spells/CSpellHandler.h"
|
||||
#include "../lib/CArtHandler.h"
|
||||
#include "../lib/CCreatureHandler.h"
|
||||
#include "../lib/CHeroHandler.h"
|
||||
#include "../lib/constants/StringConstants.h"
|
||||
#include "../lib/mapping/CMap.h"
|
||||
#include "../lib/mapObjects/CGHeroInstance.h"
|
||||
#include "../lib/mapObjects/CGCreature.h"
|
||||
|
||||
QuestWidget::QuestWidget(const CMap & _map, CGSeerHut & _sh, QWidget *parent) :
|
||||
QuestWidget::QuestWidget(MapController & _controller, CQuest & _sh, QWidget *parent) :
|
||||
QDialog(parent),
|
||||
map(_map),
|
||||
seerhut(_sh),
|
||||
controller(_controller),
|
||||
quest(_sh),
|
||||
ui(new Ui::QuestWidget)
|
||||
{
|
||||
setAttribute(Qt::WA_DeleteOnClose, true);
|
||||
ui->setupUi(this);
|
||||
|
||||
ui->lDayOfWeek->addItem(tr("None"));
|
||||
for(int i = 1; i <= 7; ++i)
|
||||
ui->lDayOfWeek->addItem(tr("Day %1").arg(i));
|
||||
|
||||
//fill resources
|
||||
ui->lResources->setRowCount(GameConstants::RESOURCE_QUANTITY - 1);
|
||||
for(int i = 0; i < GameConstants::RESOURCE_QUANTITY - 1; ++i)
|
||||
{
|
||||
auto * item = new QTableWidgetItem(QString::fromStdString(GameConstants::RESOURCE_NAMES[i]));
|
||||
item->setData(Qt::UserRole, QVariant::fromValue(i));
|
||||
ui->lResources->setItem(i, 0, item);
|
||||
auto * spinBox = new QSpinBox;
|
||||
spinBox->setMaximum(i == GameResID::GOLD ? 999999 : 999);
|
||||
ui->lResources->setCellWidget(i, 1, spinBox);
|
||||
}
|
||||
|
||||
//fill artifacts
|
||||
for(int i = 0; i < controller.map()->allowedArtifact.size(); ++i)
|
||||
{
|
||||
auto * item = new QListWidgetItem(QString::fromStdString(VLC->artifacts()->getByIndex(i)->getNameTranslated()));
|
||||
item->setData(Qt::UserRole, QVariant::fromValue(i));
|
||||
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
||||
item->setCheckState(Qt::Unchecked);
|
||||
if(!controller.map()->allowedArtifact[i])
|
||||
item->setFlags(item->flags() & ~Qt::ItemIsEnabled);
|
||||
ui->lArtifacts->addItem(item);
|
||||
}
|
||||
|
||||
//fill spells
|
||||
for(int i = 0; i < controller.map()->allowedSpells.size(); ++i)
|
||||
{
|
||||
auto * item = new QListWidgetItem(QString::fromStdString(VLC->spells()->getByIndex(i)->getNameTranslated()));
|
||||
item->setData(Qt::UserRole, QVariant::fromValue(i));
|
||||
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
||||
item->setCheckState(Qt::Unchecked);
|
||||
if(!controller.map()->allowedSpells[i])
|
||||
item->setFlags(item->flags() & ~Qt::ItemIsEnabled);
|
||||
ui->lSpells->addItem(item);
|
||||
}
|
||||
|
||||
//fill skills
|
||||
ui->lSkills->setRowCount(controller.map()->allowedAbilities.size());
|
||||
for(int i = 0; i < controller.map()->allowedAbilities.size(); ++i)
|
||||
{
|
||||
auto * item = new QTableWidgetItem(QString::fromStdString(VLC->skills()->getByIndex(i)->getNameTranslated()));
|
||||
item->setData(Qt::UserRole, QVariant::fromValue(i));
|
||||
|
||||
auto * widget = new QComboBox;
|
||||
for(auto & s : NSecondarySkill::levels)
|
||||
widget->addItem(QString::fromStdString(s));
|
||||
|
||||
if(!controller.map()->allowedAbilities[i])
|
||||
{
|
||||
item->setFlags(item->flags() & ~Qt::ItemIsEnabled);
|
||||
widget->setEnabled(false);
|
||||
}
|
||||
|
||||
ui->lSkills->setItem(i, 0, item);
|
||||
ui->lSkills->setCellWidget(i, 1, widget);
|
||||
}
|
||||
|
||||
//fill creatures
|
||||
for(auto & creature : VLC->creh->objects)
|
||||
{
|
||||
ui->lCreatureId->addItem(QString::fromStdString(creature->getNameSingularTranslated()));
|
||||
ui->lCreatureId->setItemData(ui->lCreatureId->count() - 1, creature->getIndex());
|
||||
}
|
||||
|
||||
//fill heroes
|
||||
VLC->heroTypes()->forEach([this](const HeroType * hero, bool &)
|
||||
{
|
||||
auto * item = new QListWidgetItem(QString::fromStdString(hero->getNameTranslated()));
|
||||
item->setData(Qt::UserRole, QVariant::fromValue(hero->getId().getNum()));
|
||||
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
||||
item->setCheckState(Qt::Unchecked);
|
||||
ui->lHeroes->addItem(item);
|
||||
});
|
||||
|
||||
//fill hero classes
|
||||
VLC->heroClasses()->forEach([this](const HeroClass * heroClass, bool &)
|
||||
{
|
||||
auto * item = new QListWidgetItem(QString::fromStdString(heroClass->getNameTranslated()));
|
||||
item->setData(Qt::UserRole, QVariant::fromValue(heroClass->getId().getNum()));
|
||||
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
||||
item->setCheckState(Qt::Unchecked);
|
||||
ui->lHeroClasses->addItem(item);
|
||||
});
|
||||
|
||||
//fill players
|
||||
for(auto color = PlayerColor(0); color < PlayerColor::PLAYER_LIMIT; ++color)
|
||||
{
|
||||
auto * item = new QListWidgetItem(QString::fromStdString(GameConstants::PLAYER_COLOR_NAMES[color.getNum()]));
|
||||
item->setData(Qt::UserRole, QVariant::fromValue(color.getNum()));
|
||||
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
||||
item->setCheckState(Qt::Unchecked);
|
||||
ui->lPlayers->addItem(item);
|
||||
}
|
||||
}
|
||||
|
||||
QuestWidget::~QuestWidget()
|
||||
@@ -34,137 +137,259 @@ QuestWidget::~QuestWidget()
|
||||
|
||||
void QuestWidget::obtainData()
|
||||
{
|
||||
assert(seerhut.quest);
|
||||
bool activeId = false;
|
||||
bool activeAmount = false;
|
||||
switch(seerhut.quest->missionType) {
|
||||
case CQuest::Emission::MISSION_LEVEL:
|
||||
activeAmount = true;
|
||||
ui->targetId->addItem("Reach level");
|
||||
ui->targetAmount->setText(QString::number(seerhut.quest->m13489val));
|
||||
break;
|
||||
case CQuest::Emission::MISSION_PRIMARY_STAT:
|
||||
activeId = true;
|
||||
activeAmount = true;
|
||||
for(auto s : NPrimarySkill::names)
|
||||
ui->targetId->addItem(QString::fromStdString(s));
|
||||
for(int i = 0; i < seerhut.quest->m2stats.size(); ++i)
|
||||
{
|
||||
if(seerhut.quest->m2stats[i] > 0)
|
||||
{
|
||||
ui->targetId->setCurrentIndex(i);
|
||||
ui->targetAmount->setText(QString::number(seerhut.quest->m2stats[i]));
|
||||
break; //TODO: support multiple stats
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CQuest::Emission::MISSION_KILL_HERO:
|
||||
activeId = true;
|
||||
//TODO: implement
|
||||
break;
|
||||
case CQuest::Emission::MISSION_KILL_CREATURE:
|
||||
activeId = true;
|
||||
//TODO: implement
|
||||
break;
|
||||
case CQuest::Emission::MISSION_ART:
|
||||
activeId = true;
|
||||
for(int i = 0; i < map.allowedArtifact.size(); ++i)
|
||||
ui->targetId->addItem(QString::fromStdString(VLC->arth->objects.at(i)->getNameTranslated()));
|
||||
if(!seerhut.quest->m5arts.empty())
|
||||
ui->targetId->setCurrentIndex(seerhut.quest->m5arts.front());
|
||||
//TODO: support multiple artifacts
|
||||
break;
|
||||
case CQuest::Emission::MISSION_ARMY:
|
||||
activeId = true;
|
||||
activeAmount = true;
|
||||
break;
|
||||
case CQuest::Emission::MISSION_RESOURCES:
|
||||
activeId = true;
|
||||
activeAmount = true;
|
||||
for(auto s : GameConstants::RESOURCE_NAMES)
|
||||
ui->targetId->addItem(QString::fromStdString(s));
|
||||
for(int i = 0; i < seerhut.quest->m7resources.size(); ++i)
|
||||
{
|
||||
if(seerhut.quest->m7resources[i] > 0)
|
||||
{
|
||||
ui->targetId->setCurrentIndex(i);
|
||||
ui->targetAmount->setText(QString::number(seerhut.quest->m7resources[i]));
|
||||
break; //TODO: support multiple resources
|
||||
}
|
||||
}
|
||||
break;
|
||||
case CQuest::Emission::MISSION_HERO:
|
||||
activeId = true;
|
||||
for(int i = 0; i < map.allowedHeroes.size(); ++i)
|
||||
ui->targetId->addItem(QString::fromStdString(VLC->heroh->objects.at(i)->getNameTranslated()));
|
||||
ui->targetId->setCurrentIndex(seerhut.quest->m13489val);
|
||||
break;
|
||||
case CQuest::Emission::MISSION_PLAYER:
|
||||
activeId = true;
|
||||
for(auto s : GameConstants::PLAYER_COLOR_NAMES)
|
||||
ui->targetId->addItem(QString::fromStdString(s));
|
||||
ui->targetId->setCurrentIndex(seerhut.quest->m13489val);
|
||||
break;
|
||||
case CQuest::Emission::MISSION_KEYMASTER:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
ui->lDayOfWeek->setCurrentIndex(quest.mission.dayOfWeek);
|
||||
ui->lDaysPassed->setValue(quest.mission.daysPassed);
|
||||
ui->lHeroLevel->setValue(quest.mission.heroLevel);
|
||||
ui->lHeroExperience->setValue(quest.mission.heroExperience);
|
||||
ui->lManaPoints->setValue(quest.mission.manaPoints);
|
||||
ui->lManaPercentage->setValue(quest.mission.manaPercentage);
|
||||
ui->lAttack->setValue(quest.mission.primary[0]);
|
||||
ui->lDefence->setValue(quest.mission.primary[1]);
|
||||
ui->lPower->setValue(quest.mission.primary[2]);
|
||||
ui->lKnowledge->setValue(quest.mission.primary[3]);
|
||||
for(int i = 0; i < ui->lResources->rowCount(); ++i)
|
||||
{
|
||||
if(auto * widget = qobject_cast<QSpinBox*>(ui->lResources->cellWidget(i, 1)))
|
||||
widget->setValue(quest.mission.resources[i]);
|
||||
}
|
||||
|
||||
ui->targetId->setEnabled(activeId);
|
||||
ui->targetAmount->setEnabled(activeAmount);
|
||||
}
|
||||
|
||||
QString QuestWidget::commitChanges()
|
||||
{
|
||||
assert(seerhut.quest);
|
||||
switch(seerhut.quest->missionType) {
|
||||
case CQuest::Emission::MISSION_LEVEL:
|
||||
seerhut.quest->m13489val = ui->targetAmount->text().toInt();
|
||||
return QString("Reach lvl ").append(ui->targetAmount->text());
|
||||
case CQuest::Emission::MISSION_PRIMARY_STAT:
|
||||
seerhut.quest->m2stats.resize(sizeof(NPrimarySkill::names), 0);
|
||||
seerhut.quest->m2stats[ui->targetId->currentIndex()] = ui->targetAmount->text().toInt();
|
||||
//TODO: support multiple stats
|
||||
return ui->targetId->currentText().append(ui->targetAmount->text());
|
||||
case CQuest::Emission::MISSION_KILL_HERO:
|
||||
//TODO: implement
|
||||
return QString("N/A");
|
||||
case CQuest::Emission::MISSION_KILL_CREATURE:
|
||||
//TODO: implement
|
||||
return QString("N/A");
|
||||
case CQuest::Emission::MISSION_ART:
|
||||
seerhut.quest->m5arts.clear();
|
||||
seerhut.quest->m5arts.push_back(ArtifactID(ui->targetId->currentIndex()));
|
||||
//TODO: support multiple artifacts
|
||||
return ui->targetId->currentText();
|
||||
case CQuest::Emission::MISSION_ARMY:
|
||||
//TODO: implement
|
||||
return QString("N/A");
|
||||
case CQuest::Emission::MISSION_RESOURCES:
|
||||
seerhut.quest->m7resources[ui->targetId->currentIndex()] = ui->targetAmount->text().toInt();
|
||||
//TODO: support resources
|
||||
return ui->targetId->currentText().append(ui->targetAmount->text());
|
||||
case CQuest::Emission::MISSION_HERO:
|
||||
seerhut.quest->m13489val = ui->targetId->currentIndex();
|
||||
return ui->targetId->currentText();
|
||||
case CQuest::Emission::MISSION_PLAYER:
|
||||
seerhut.quest->m13489val = ui->targetId->currentIndex();
|
||||
return ui->targetId->currentText();
|
||||
case CQuest::Emission::MISSION_KEYMASTER:
|
||||
return QString("N/A");
|
||||
default:
|
||||
return QString("N/A");
|
||||
for(auto i : quest.mission.artifacts)
|
||||
ui->lArtifacts->item(VLC->artifacts()->getById(i)->getIndex())->setCheckState(Qt::Checked);
|
||||
for(auto i : quest.mission.spells)
|
||||
ui->lArtifacts->item(VLC->spells()->getById(i)->getIndex())->setCheckState(Qt::Checked);
|
||||
for(auto & i : quest.mission.secondary)
|
||||
{
|
||||
int index = VLC->skills()->getById(i.first)->getIndex();
|
||||
if(auto * widget = qobject_cast<QComboBox*>(ui->lSkills->cellWidget(index, 1)))
|
||||
widget->setCurrentIndex(i.second);
|
||||
}
|
||||
for(auto & i : quest.mission.creatures)
|
||||
{
|
||||
int index = i.type->getIndex();
|
||||
ui->lCreatureId->setCurrentIndex(index);
|
||||
ui->lCreatureAmount->setValue(i.count);
|
||||
onCreatureAdd(ui->lCreatures, ui->lCreatureId, ui->lCreatureAmount);
|
||||
}
|
||||
for(auto & i : quest.mission.heroes)
|
||||
{
|
||||
for(int e = 0; e < ui->lHeroes->count(); ++e)
|
||||
{
|
||||
if(ui->lHeroes->item(e)->data(Qt::UserRole).toInt() == i.getNum())
|
||||
{
|
||||
ui->lHeroes->item(e)->setCheckState(Qt::Checked);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for(auto & i : quest.mission.heroClasses)
|
||||
{
|
||||
for(int e = 0; e < ui->lHeroClasses->count(); ++e)
|
||||
{
|
||||
if(ui->lHeroClasses->item(e)->data(Qt::UserRole).toInt() == i.getNum())
|
||||
{
|
||||
ui->lHeroClasses->item(e)->setCheckState(Qt::Checked);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for(auto & i : quest.mission.players)
|
||||
{
|
||||
for(int e = 0; e < ui->lPlayers->count(); ++e)
|
||||
{
|
||||
if(ui->lPlayers->item(e)->data(Qt::UserRole).toInt() == i.getNum())
|
||||
{
|
||||
ui->lPlayers->item(e)->setCheckState(Qt::Checked);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(quest.killTarget != ObjectInstanceID::NONE && quest.killTarget < controller.map()->objects.size())
|
||||
ui->lKillTarget->setText(QString::fromStdString(controller.map()->objects[quest.killTarget]->instanceName));
|
||||
else
|
||||
quest.killTarget = ObjectInstanceID::NONE;
|
||||
}
|
||||
|
||||
QuestDelegate::QuestDelegate(const CMap & m, CGSeerHut & t): map(m), seerhut(t), QStyledItemDelegate()
|
||||
bool QuestWidget::commitChanges()
|
||||
{
|
||||
quest.mission.dayOfWeek = ui->lDayOfWeek->currentIndex();
|
||||
quest.mission.daysPassed = ui->lDaysPassed->value();
|
||||
quest.mission.heroLevel = ui->lHeroLevel->value();
|
||||
quest.mission.heroExperience = ui->lHeroExperience->value();
|
||||
quest.mission.manaPoints = ui->lManaPoints->value();
|
||||
quest.mission.manaPercentage = ui->lManaPercentage->value();
|
||||
quest.mission.primary[0] = ui->lAttack->value();
|
||||
quest.mission.primary[1] = ui->lDefence->value();
|
||||
quest.mission.primary[2] = ui->lPower->value();
|
||||
quest.mission.primary[3] = ui->lKnowledge->value();
|
||||
for(int i = 0; i < ui->lResources->rowCount(); ++i)
|
||||
{
|
||||
if(auto * widget = qobject_cast<QSpinBox*>(ui->lResources->cellWidget(i, 1)))
|
||||
quest.mission.resources[i] = widget->value();
|
||||
}
|
||||
|
||||
quest.mission.artifacts.clear();
|
||||
for(int i = 0; i < ui->lArtifacts->count(); ++i)
|
||||
{
|
||||
if(ui->lArtifacts->item(i)->checkState() == Qt::Checked)
|
||||
quest.mission.artifacts.push_back(VLC->artifacts()->getByIndex(i)->getId());
|
||||
}
|
||||
quest.mission.spells.clear();
|
||||
for(int i = 0; i < ui->lSpells->count(); ++i)
|
||||
{
|
||||
if(ui->lSpells->item(i)->checkState() == Qt::Checked)
|
||||
quest.mission.spells.push_back(VLC->spells()->getByIndex(i)->getId());
|
||||
}
|
||||
|
||||
quest.mission.secondary.clear();
|
||||
for(int i = 0; i < ui->lSkills->rowCount(); ++i)
|
||||
{
|
||||
if(auto * widget = qobject_cast<QComboBox*>(ui->lSkills->cellWidget(i, 1)))
|
||||
{
|
||||
if(widget->currentIndex() > 0)
|
||||
quest.mission.secondary[VLC->skills()->getByIndex(i)->getId()] = widget->currentIndex();
|
||||
}
|
||||
}
|
||||
|
||||
quest.mission.creatures.clear();
|
||||
for(int i = 0; i < ui->lCreatures->rowCount(); ++i)
|
||||
{
|
||||
int index = ui->lCreatures->item(i, 0)->data(Qt::UserRole).toInt();
|
||||
if(auto * widget = qobject_cast<QSpinBox*>(ui->lCreatures->cellWidget(i, 1)))
|
||||
if(widget->value())
|
||||
quest.mission.creatures.emplace_back(VLC->creatures()->getByIndex(index)->getId(), widget->value());
|
||||
}
|
||||
|
||||
quest.mission.heroes.clear();
|
||||
for(int i = 0; i < ui->lHeroes->count(); ++i)
|
||||
{
|
||||
if(ui->lHeroes->item(i)->checkState() == Qt::Checked)
|
||||
quest.mission.heroes.emplace_back(ui->lHeroes->item(i)->data(Qt::UserRole).toInt());
|
||||
}
|
||||
|
||||
quest.mission.heroClasses.clear();
|
||||
for(int i = 0; i < ui->lHeroClasses->count(); ++i)
|
||||
{
|
||||
if(ui->lHeroClasses->item(i)->checkState() == Qt::Checked)
|
||||
quest.mission.heroClasses.emplace_back(ui->lHeroClasses->item(i)->data(Qt::UserRole).toInt());
|
||||
}
|
||||
|
||||
quest.mission.players.clear();
|
||||
for(int i = 0; i < ui->lPlayers->count(); ++i)
|
||||
{
|
||||
if(ui->lPlayers->item(i)->checkState() == Qt::Checked)
|
||||
quest.mission.players.emplace_back(ui->lPlayers->item(i)->data(Qt::UserRole).toInt());
|
||||
}
|
||||
|
||||
//quest.killTarget is set directly in object picking
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void QuestWidget::onCreatureAdd(QTableWidget * listWidget, QComboBox * comboWidget, QSpinBox * spinWidget)
|
||||
{
|
||||
QTableWidgetItem * item = nullptr;
|
||||
QSpinBox * widget = nullptr;
|
||||
for(int i = 0; i < listWidget->rowCount(); ++i)
|
||||
{
|
||||
if(auto * cname = listWidget->item(i, 0))
|
||||
{
|
||||
if(cname->data(Qt::UserRole).toInt() == comboWidget->currentData().toInt())
|
||||
{
|
||||
item = cname;
|
||||
widget = qobject_cast<QSpinBox*>(listWidget->cellWidget(i, 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!item)
|
||||
{
|
||||
listWidget->setRowCount(listWidget->rowCount() + 1);
|
||||
item = new QTableWidgetItem(comboWidget->currentText());
|
||||
listWidget->setItem(listWidget->rowCount() - 1, 0, item);
|
||||
}
|
||||
|
||||
item->setData(Qt::UserRole, comboWidget->currentData());
|
||||
|
||||
if(!widget)
|
||||
{
|
||||
widget = new QSpinBox;
|
||||
widget->setRange(spinWidget->minimum(), spinWidget->maximum());
|
||||
listWidget->setCellWidget(listWidget->rowCount() - 1, 1, widget);
|
||||
}
|
||||
|
||||
widget->setValue(spinWidget->value());
|
||||
}
|
||||
|
||||
void QuestWidget::on_lKillTargetSelect_clicked()
|
||||
{
|
||||
auto pred = [](const CGObjectInstance * obj) -> bool
|
||||
{
|
||||
if(auto * o = dynamic_cast<const CGHeroInstance*>(obj))
|
||||
return o->ID != Obj::PRISON;
|
||||
if(dynamic_cast<const CGCreature*>(obj))
|
||||
return true;
|
||||
return false;
|
||||
};
|
||||
|
||||
for(int lvl : {0, 1})
|
||||
{
|
||||
auto & l = controller.scene(lvl)->objectPickerView;
|
||||
l.highlight(pred);
|
||||
l.update();
|
||||
QObject::connect(&l, &ObjectPickerLayer::selectionMade, this, &QuestWidget::onTargetPicked);
|
||||
}
|
||||
|
||||
hide();
|
||||
}
|
||||
|
||||
void QuestWidget::onTargetPicked(const CGObjectInstance * obj)
|
||||
{
|
||||
show();
|
||||
|
||||
for(int lvl : {0, 1})
|
||||
{
|
||||
auto & l = controller.scene(lvl)->objectPickerView;
|
||||
l.clear();
|
||||
l.update();
|
||||
QObject::disconnect(&l, &ObjectPickerLayer::selectionMade, this, &QuestWidget::onTargetPicked);
|
||||
}
|
||||
|
||||
if(!obj) //discarded
|
||||
{
|
||||
quest.killTarget = ObjectInstanceID::NONE;
|
||||
ui->lKillTarget->setText("");
|
||||
return;
|
||||
}
|
||||
|
||||
ui->lKillTarget->setText(QString::fromStdString(obj->instanceName));
|
||||
quest.killTarget = obj->id;
|
||||
}
|
||||
|
||||
void QuestWidget::on_lCreatureAdd_clicked()
|
||||
{
|
||||
onCreatureAdd(ui->lCreatures, ui->lCreatureId, ui->lCreatureAmount);
|
||||
}
|
||||
|
||||
|
||||
void QuestWidget::on_lCreatureRemove_clicked()
|
||||
{
|
||||
std::set<int, std::greater<int>> rowsToRemove;
|
||||
for(auto * i : ui->lCreatures->selectedItems())
|
||||
rowsToRemove.insert(i->row());
|
||||
|
||||
for(auto i : rowsToRemove)
|
||||
ui->lCreatures->removeRow(i);
|
||||
}
|
||||
|
||||
QuestDelegate::QuestDelegate(MapController & c, CQuest & t): controller(c), quest(t), QStyledItemDelegate()
|
||||
{
|
||||
}
|
||||
|
||||
QWidget * QuestDelegate::createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const
|
||||
{
|
||||
return new QuestWidget(map, seerhut, parent);
|
||||
return new QuestWidget(controller, quest, parent);
|
||||
}
|
||||
|
||||
void QuestDelegate::setEditorData(QWidget * editor, const QModelIndex & index) const
|
||||
@@ -183,11 +408,26 @@ void QuestDelegate::setModelData(QWidget * editor, QAbstractItemModel * model, c
|
||||
{
|
||||
if(auto *ed = qobject_cast<QuestWidget *>(editor))
|
||||
{
|
||||
auto quest = ed->commitChanges();
|
||||
model->setData(index, quest);
|
||||
ed->commitChanges();
|
||||
}
|
||||
else
|
||||
{
|
||||
QStyledItemDelegate::setModelData(editor, model, index);
|
||||
}
|
||||
}
|
||||
|
||||
bool QuestDelegate::eventFilter(QObject * object, QEvent * event)
|
||||
{
|
||||
if(auto * ed = qobject_cast<QuestWidget *>(object))
|
||||
{
|
||||
if(event->type() == QEvent::Hide || event->type() == QEvent::FocusOut)
|
||||
return false;
|
||||
if(event->type() == QEvent::Close)
|
||||
{
|
||||
emit commitData(ed);
|
||||
emit closeEditor(ed);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return QStyledItemDelegate::eventFilter(object, event);
|
||||
}
|
||||
|
@@ -16,20 +16,33 @@ namespace Ui {
|
||||
class QuestWidget;
|
||||
}
|
||||
|
||||
class MapController;
|
||||
|
||||
class QuestWidget : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit QuestWidget(const CMap &, CGSeerHut &, QWidget *parent = nullptr);
|
||||
explicit QuestWidget(MapController &, CQuest &, QWidget *parent = nullptr);
|
||||
~QuestWidget();
|
||||
|
||||
void obtainData();
|
||||
QString commitChanges();
|
||||
bool commitChanges();
|
||||
|
||||
private slots:
|
||||
void onTargetPicked(const CGObjectInstance *);
|
||||
|
||||
void on_lKillTargetSelect_clicked();
|
||||
|
||||
void on_lCreatureAdd_clicked();
|
||||
|
||||
void on_lCreatureRemove_clicked();
|
||||
|
||||
private:
|
||||
CGSeerHut & seerhut;
|
||||
const CMap & map;
|
||||
void onCreatureAdd(QTableWidget * listWidget, QComboBox * comboWidget, QSpinBox * spinWidget);
|
||||
|
||||
CQuest & quest;
|
||||
MapController & controller;
|
||||
Ui::QuestWidget *ui;
|
||||
};
|
||||
|
||||
@@ -39,13 +52,16 @@ class QuestDelegate : public QStyledItemDelegate
|
||||
public:
|
||||
using QStyledItemDelegate::QStyledItemDelegate;
|
||||
|
||||
QuestDelegate(const CMap &, CGSeerHut &);
|
||||
QuestDelegate(MapController &, CQuest &);
|
||||
|
||||
QWidget * createEditor(QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index) const override;
|
||||
void setEditorData(QWidget * editor, const QModelIndex & index) const override;
|
||||
void setModelData(QWidget * editor, QAbstractItemModel * model, const QModelIndex & index) const override;
|
||||
|
||||
protected:
|
||||
bool eventFilter(QObject * object, QEvent * event) override;
|
||||
|
||||
private:
|
||||
CGSeerHut & seerhut;
|
||||
const CMap & map;
|
||||
CQuest & quest;
|
||||
MapController & controller;
|
||||
};
|
||||
|
@@ -9,8 +9,8 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>429</width>
|
||||
<height>89</height>
|
||||
<width>531</width>
|
||||
<height>495</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
@@ -19,34 +19,616 @@
|
||||
<property name="modal">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QComboBox" name="targetId">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_8">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_7">
|
||||
<property name="text">
|
||||
<string>Day of week</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QComboBox" name="lDayOfWeek">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>120</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="text">
|
||||
<string>Days passed</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="lDaysPassed">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_3">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_10">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_14">
|
||||
<property name="text">
|
||||
<string>Hero level</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="lHeroLevel">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_15">
|
||||
<property name="text">
|
||||
<string>Hero experience</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="lHeroExperience">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>80</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>100000</number>
|
||||
</property>
|
||||
<property name="singleStep">
|
||||
<number>100</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_9">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="text">
|
||||
<string>Spell points</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="lManaPoints">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>60</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>999</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="lManaPercentage">
|
||||
<property name="suffix">
|
||||
<string>%</string>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>100</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>40</width>
|
||||
<height>20</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_14">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Kill hero/monster</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="lKillTarget">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QToolButton" name="lKillTargetSelect">
|
||||
<property name="text">
|
||||
<string>...</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_3">
|
||||
<property name="title">
|
||||
<string>Primary skills</string>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<property name="leftMargin">
|
||||
<number>12</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_10">
|
||||
<property name="text">
|
||||
<string>Attack</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="lAttack"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_11">
|
||||
<property name="text">
|
||||
<string>Defence</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="lDefence"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_12">
|
||||
<property name="text">
|
||||
<string>Spell power</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="lPower"/>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_13">
|
||||
<property name="text">
|
||||
<string>Knowledge</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="lKnowledge"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="targetAmount">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
<widget class="QTabWidget" name="tabWidget_2">
|
||||
<property name="layoutDirection">
|
||||
<enum>Qt::LeftToRight</enum>
|
||||
</property>
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>60</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
<property name="tabPosition">
|
||||
<enum>QTabWidget::North</enum>
|
||||
</property>
|
||||
<property name="inputMethodHints">
|
||||
<set>Qt::ImhDigitsOnly</set>
|
||||
<property name="tabShape">
|
||||
<enum>QTabWidget::Rounded</enum>
|
||||
</property>
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="elideMode">
|
||||
<enum>Qt::ElideNone</enum>
|
||||
</property>
|
||||
<property name="usesScrollButtons">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="documentMode">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="tabBarAutoHide">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<widget class="QWidget" name="tab_3">
|
||||
<attribute name="title">
|
||||
<string>Resources</string>
|
||||
</attribute>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTableWidget" name="lResources">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::NoSelection</enum>
|
||||
</property>
|
||||
<property name="columnCount">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderDefaultSectionSize">
|
||||
<number>180</number>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderStretchLastSection">
|
||||
<bool>true</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderDefaultSectionSize">
|
||||
<number>24</number>
|
||||
</attribute>
|
||||
<column/>
|
||||
<column/>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_4">
|
||||
<attribute name="title">
|
||||
<string>Artifacts</string>
|
||||
</attribute>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_3">
|
||||
<property name="leftMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QListWidget" name="lArtifacts">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::NoSelection</enum>
|
||||
</property>
|
||||
<property name="isWrapping" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_5">
|
||||
<attribute name="title">
|
||||
<string>Spells</string>
|
||||
</attribute>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_4">
|
||||
<property name="leftMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QListWidget" name="lSpells">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::NoSelection</enum>
|
||||
</property>
|
||||
<property name="isWrapping" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_7">
|
||||
<attribute name="title">
|
||||
<string>Skills</string>
|
||||
</attribute>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_5">
|
||||
<property name="leftMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QTableWidget" name="lSkills">
|
||||
<property name="columnCount">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderDefaultSectionSize">
|
||||
<number>180</number>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderDefaultSectionSize">
|
||||
<number>24</number>
|
||||
</attribute>
|
||||
<column/>
|
||||
<column/>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_6">
|
||||
<attribute name="title">
|
||||
<string>Creatures</string>
|
||||
</attribute>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<property name="spacing">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_6">
|
||||
<item>
|
||||
<widget class="QComboBox" name="lCreatureId">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="lCreatureAmount">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>60</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>9999</number>
|
||||
</property>
|
||||
<property name="stepType">
|
||||
<enum>QAbstractSpinBox::AdaptiveDecimalStepType</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="lCreatureAdd">
|
||||
<property name="text">
|
||||
<string>Add</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="lCreatureRemove">
|
||||
<property name="text">
|
||||
<string>Remove</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTableWidget" name="lCreatures">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::MultiSelection</enum>
|
||||
</property>
|
||||
<property name="selectionBehavior">
|
||||
<enum>QAbstractItemView::SelectRows</enum>
|
||||
</property>
|
||||
<property name="columnCount">
|
||||
<number>2</number>
|
||||
</property>
|
||||
<attribute name="horizontalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="horizontalHeaderDefaultSectionSize">
|
||||
<number>180</number>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderVisible">
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderDefaultSectionSize">
|
||||
<number>24</number>
|
||||
</attribute>
|
||||
<column/>
|
||||
<column/>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab">
|
||||
<attribute name="title">
|
||||
<string>Heroes</string>
|
||||
</attribute>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_12">
|
||||
<property name="leftMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QListWidget" name="lHeroes">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::NoSelection</enum>
|
||||
</property>
|
||||
<property name="isWrapping" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_2">
|
||||
<attribute name="title">
|
||||
<string>Hero classes</string>
|
||||
</attribute>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_11">
|
||||
<property name="leftMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QListWidget" name="lHeroClasses">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::NoSelection</enum>
|
||||
</property>
|
||||
<property name="isWrapping" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_8">
|
||||
<attribute name="title">
|
||||
<string>Players</string>
|
||||
</attribute>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_13">
|
||||
<property name="leftMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QListWidget" name="lPlayers">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::NoSelection</enum>
|
||||
</property>
|
||||
<property name="isWrapping" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
@@ -13,6 +13,7 @@
|
||||
#include "../lib/VCMI_Lib.h"
|
||||
#include "../lib/CSkillHandler.h"
|
||||
#include "../lib/spells/CSpellHandler.h"
|
||||
#include "../lib/CHeroHandler.h"
|
||||
#include "../lib/CArtHandler.h"
|
||||
#include "../lib/CCreatureHandler.h"
|
||||
#include "../lib/constants/StringConstants.h"
|
||||
@@ -55,7 +56,11 @@ RewardsWidget::RewardsWidget(CMap & m, CRewardableObject & p, QWidget *parent) :
|
||||
auto * item = new QTableWidgetItem(QString::fromStdString(GameConstants::RESOURCE_NAMES[i]));
|
||||
item->setData(Qt::UserRole, QVariant::fromValue(i));
|
||||
w->setItem(i, 0, item);
|
||||
w->setCellWidget(i, 1, new QSpinBox);
|
||||
auto * spinBox = new QSpinBox;
|
||||
spinBox->setMaximum(i == GameResID::GOLD ? 999999 : 999);
|
||||
if(w == ui->rResources)
|
||||
spinBox->setMinimum(i == GameResID::GOLD ? -999999 : -999);
|
||||
w->setCellWidget(i, 1, spinBox);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,6 +136,36 @@ RewardsWidget::RewardsWidget(CMap & m, CRewardableObject & p, QWidget *parent) :
|
||||
}
|
||||
}
|
||||
|
||||
//fill heroes
|
||||
VLC->heroTypes()->forEach([this](const HeroType * hero, bool &)
|
||||
{
|
||||
auto * item = new QListWidgetItem(QString::fromStdString(hero->getNameTranslated()));
|
||||
item->setData(Qt::UserRole, QVariant::fromValue(hero->getId().getNum()));
|
||||
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
||||
item->setCheckState(Qt::Unchecked);
|
||||
ui->lHeroes->addItem(item);
|
||||
});
|
||||
|
||||
//fill hero classes
|
||||
VLC->heroClasses()->forEach([this](const HeroClass * heroClass, bool &)
|
||||
{
|
||||
auto * item = new QListWidgetItem(QString::fromStdString(heroClass->getNameTranslated()));
|
||||
item->setData(Qt::UserRole, QVariant::fromValue(heroClass->getId().getNum()));
|
||||
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
||||
item->setCheckState(Qt::Unchecked);
|
||||
ui->lHeroClasses->addItem(item);
|
||||
});
|
||||
|
||||
//fill players
|
||||
for(auto color = PlayerColor(0); color < PlayerColor::PLAYER_LIMIT; ++color)
|
||||
{
|
||||
auto * item = new QListWidgetItem(QString::fromStdString(GameConstants::PLAYER_COLOR_NAMES[color.getNum()]));
|
||||
item->setData(Qt::UserRole, QVariant::fromValue(color.getNum()));
|
||||
item->setFlags(item->flags() | Qt::ItemIsUserCheckable);
|
||||
item->setCheckState(Qt::Unchecked);
|
||||
ui->lPlayers->addItem(item);
|
||||
}
|
||||
|
||||
//fill spell cast
|
||||
for(auto & s : NSecondarySkill::levels)
|
||||
ui->castLevel->addItem(QString::fromStdString(s));
|
||||
@@ -347,7 +382,28 @@ void RewardsWidget::saveCurrentVisitInfo(int index)
|
||||
int index = ui->lCreatures->item(i, 0)->data(Qt::UserRole).toInt();
|
||||
if(auto * widget = qobject_cast<QSpinBox*>(ui->lCreatures->cellWidget(i, 1)))
|
||||
if(widget->value())
|
||||
vinfo.reward.creatures.emplace_back(VLC->creatures()->getByIndex(index)->getId(), widget->value());
|
||||
vinfo.limiter.creatures.emplace_back(VLC->creatures()->getByIndex(index)->getId(), widget->value());
|
||||
}
|
||||
|
||||
vinfo.limiter.heroes.clear();
|
||||
for(int i = 0; i < ui->lHeroes->count(); ++i)
|
||||
{
|
||||
if(ui->lHeroes->item(i)->checkState() == Qt::Checked)
|
||||
vinfo.limiter.heroes.emplace_back(ui->lHeroes->item(i)->data(Qt::UserRole).toInt());
|
||||
}
|
||||
|
||||
vinfo.limiter.heroClasses.clear();
|
||||
for(int i = 0; i < ui->lHeroClasses->count(); ++i)
|
||||
{
|
||||
if(ui->lHeroClasses->item(i)->checkState() == Qt::Checked)
|
||||
vinfo.limiter.heroClasses.emplace_back(ui->lHeroClasses->item(i)->data(Qt::UserRole).toInt());
|
||||
}
|
||||
|
||||
vinfo.limiter.players.clear();
|
||||
for(int i = 0; i < ui->lPlayers->count(); ++i)
|
||||
{
|
||||
if(ui->lPlayers->item(i)->checkState() == Qt::Checked)
|
||||
vinfo.limiter.players.emplace_back(ui->lPlayers->item(i)->data(Qt::UserRole).toInt());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -445,10 +501,10 @@ void RewardsWidget::loadCurrentVisitInfo(int index)
|
||||
ui->lHeroExperience->setValue(vinfo.limiter.heroExperience);
|
||||
ui->lManaPoints->setValue(vinfo.limiter.manaPoints);
|
||||
ui->lManaPercentage->setValue(vinfo.limiter.manaPercentage);
|
||||
ui->lAttack->setValue(vinfo.reward.primary[0]);
|
||||
ui->lDefence->setValue(vinfo.reward.primary[1]);
|
||||
ui->lPower->setValue(vinfo.reward.primary[2]);
|
||||
ui->lKnowledge->setValue(vinfo.reward.primary[3]);
|
||||
ui->lAttack->setValue(vinfo.limiter.primary[0]);
|
||||
ui->lDefence->setValue(vinfo.limiter.primary[1]);
|
||||
ui->lPower->setValue(vinfo.limiter.primary[2]);
|
||||
ui->lKnowledge->setValue(vinfo.limiter.primary[3]);
|
||||
for(int i = 0; i < ui->lResources->rowCount(); ++i)
|
||||
{
|
||||
if(auto * widget = qobject_cast<QSpinBox*>(ui->lResources->cellWidget(i, 1)))
|
||||
@@ -472,6 +528,40 @@ void RewardsWidget::loadCurrentVisitInfo(int index)
|
||||
ui->lCreatureAmount->setValue(i.count);
|
||||
onCreatureAdd(ui->lCreatures, ui->lCreatureId, ui->lCreatureAmount);
|
||||
}
|
||||
|
||||
for(auto & i : vinfo.limiter.heroes)
|
||||
{
|
||||
for(int e = 0; e < ui->lHeroes->count(); ++e)
|
||||
{
|
||||
if(ui->lHeroes->item(e)->data(Qt::UserRole).toInt() == i.getNum())
|
||||
{
|
||||
ui->lHeroes->item(e)->setCheckState(Qt::Checked);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for(auto & i : vinfo.limiter.heroClasses)
|
||||
{
|
||||
for(int e = 0; e < ui->lHeroClasses->count(); ++e)
|
||||
{
|
||||
if(ui->lHeroClasses->item(e)->data(Qt::UserRole).toInt() == i.getNum())
|
||||
{
|
||||
ui->lHeroClasses->item(e)->setCheckState(Qt::Checked);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for(auto & i : vinfo.limiter.players)
|
||||
{
|
||||
for(int e = 0; e < ui->lPlayers->count(); ++e)
|
||||
{
|
||||
if(ui->lPlayers->item(e)->data(Qt::UserRole).toInt() == i.getNum())
|
||||
{
|
||||
ui->lPlayers->item(e)->setCheckState(Qt::Checked);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RewardsWidget::onCreatureAdd(QTableWidget * listWidget, QComboBox * comboWidget, QSpinBox * spinWidget)
|
||||
|
@@ -487,6 +487,12 @@
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="elideMode">
|
||||
<enum>Qt::ElideNone</enum>
|
||||
</property>
|
||||
<property name="usesScrollButtons">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="tabBarAutoHide">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
@@ -843,7 +849,7 @@
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderDefaultSectionSize">
|
||||
<number>21</number>
|
||||
<number>24</number>
|
||||
</attribute>
|
||||
<column>
|
||||
<property name="text">
|
||||
@@ -1185,6 +1191,12 @@
|
||||
<property name="currentIndex">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="elideMode">
|
||||
<enum>Qt::ElideNone</enum>
|
||||
</property>
|
||||
<property name="usesScrollButtons">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="tabBarAutoHide">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
@@ -1229,7 +1241,7 @@
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderDefaultSectionSize">
|
||||
<number>21</number>
|
||||
<number>24</number>
|
||||
</attribute>
|
||||
<column/>
|
||||
<column/>
|
||||
@@ -1333,7 +1345,7 @@
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderDefaultSectionSize">
|
||||
<number>21</number>
|
||||
<number>24</number>
|
||||
</attribute>
|
||||
<column/>
|
||||
<column/>
|
||||
@@ -1429,7 +1441,7 @@
|
||||
<bool>false</bool>
|
||||
</attribute>
|
||||
<attribute name="verticalHeaderDefaultSectionSize">
|
||||
<number>21</number>
|
||||
<number>24</number>
|
||||
</attribute>
|
||||
<column/>
|
||||
<column/>
|
||||
@@ -1437,6 +1449,102 @@
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab">
|
||||
<attribute name="title">
|
||||
<string>Heroes</string>
|
||||
</attribute>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_29">
|
||||
<property name="leftMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QListWidget" name="lHeroes">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::NoSelection</enum>
|
||||
</property>
|
||||
<property name="isWrapping" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_2">
|
||||
<attribute name="title">
|
||||
<string>Hero classes</string>
|
||||
</attribute>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_30">
|
||||
<property name="leftMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QListWidget" name="lHeroClasses">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::NoSelection</enum>
|
||||
</property>
|
||||
<property name="isWrapping" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<widget class="QWidget" name="tab_15">
|
||||
<attribute name="title">
|
||||
<string>Players</string>
|
||||
</attribute>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_31">
|
||||
<property name="leftMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>3</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QListWidget" name="lPlayers">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
<property name="selectionMode">
|
||||
<enum>QAbstractItemView::NoSelection</enum>
|
||||
</property>
|
||||
<property name="isWrapping" stdset="0">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
|
@@ -1095,8 +1095,6 @@ bool CGameHandler::moveHero(ObjectInstanceID hid, int3 dst, ui8 teleporting, boo
|
||||
if (isInTheMap(guardPos))
|
||||
guardian = getTile(guardPos)->visitableObjects.back();
|
||||
|
||||
assert(guardian == nullptr || dynamic_cast<CGCreature*>(guardian) != nullptr);
|
||||
|
||||
const bool embarking = !h->boat && objectToVisit && objectToVisit->ID == Obj::BOAT;
|
||||
const bool disembarking = h->boat
|
||||
&& t.terType->isLand()
|
||||
|
Reference in New Issue
Block a user