2012-12-19 17:54:10 +03:00
|
|
|
/*
|
2023-05-01 19:29:53 +02:00
|
|
|
* Bonus.cpp, part of VCMI engine
|
2012-12-19 17:54:10 +03:00
|
|
|
*
|
|
|
|
* Authors: listed in file AUTHORS in main folder
|
|
|
|
*
|
|
|
|
* License: GNU General Public License v2.0 or later
|
|
|
|
* Full text of license available in license.txt file, in main folder
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2011-12-14 00:23:17 +03:00
|
|
|
#include "StdInc.h"
|
2023-05-01 19:29:53 +02:00
|
|
|
#include "Bonus.h"
|
2023-04-30 16:35:15 +02:00
|
|
|
#include "CBonusSystemNode.h"
|
2023-04-30 15:52:48 +02:00
|
|
|
#include "Limiters.h"
|
|
|
|
#include "Updaters.h"
|
2023-04-30 16:35:15 +02:00
|
|
|
#include "Propagators.h"
|
2023-04-27 23:47:29 +02:00
|
|
|
|
|
|
|
#include "../VCMI_Lib.h"
|
|
|
|
#include "../spells/CSpellHandler.h"
|
|
|
|
#include "../CCreatureHandler.h"
|
|
|
|
#include "../CCreatureSet.h"
|
|
|
|
#include "../CHeroHandler.h"
|
|
|
|
#include "../CTownHandler.h"
|
|
|
|
#include "../CGeneralTextHandler.h"
|
|
|
|
#include "../CSkillHandler.h"
|
|
|
|
#include "../CArtHandler.h"
|
|
|
|
#include "../TerrainHandler.h"
|
2023-08-19 23:22:31 +02:00
|
|
|
#include "../constants/StringConstants.h"
|
2023-04-27 23:47:29 +02:00
|
|
|
#include "../battle/BattleInfo.h"
|
2023-07-30 19:12:25 +02:00
|
|
|
#include "../modding/ModUtility.h"
|
2010-04-03 06:33:46 +03:00
|
|
|
|
2022-07-26 15:07:42 +02:00
|
|
|
VCMI_LIB_NAMESPACE_BEGIN
|
|
|
|
|
2023-03-13 23:26:44 +02:00
|
|
|
//This constructor should be placed here to avoid side effects
|
|
|
|
CAddInfo::CAddInfo() = default;
|
2018-03-12 07:20:18 +02:00
|
|
|
|
|
|
|
CAddInfo::CAddInfo(si32 value)
|
|
|
|
{
|
|
|
|
if(value != CAddInfo::NONE)
|
|
|
|
push_back(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CAddInfo::operator==(si32 value) const
|
|
|
|
{
|
|
|
|
switch(size())
|
|
|
|
{
|
|
|
|
case 0:
|
|
|
|
return value == CAddInfo::NONE;
|
|
|
|
case 1:
|
|
|
|
return operator[](0) == value;
|
|
|
|
default:
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CAddInfo::operator!=(si32 value) const
|
|
|
|
{
|
|
|
|
return !operator==(value);
|
|
|
|
}
|
|
|
|
|
2018-03-17 00:03:02 +02:00
|
|
|
si32 & CAddInfo::operator[](size_type pos)
|
|
|
|
{
|
2018-03-20 01:58:02 +02:00
|
|
|
if(pos >= size())
|
2018-03-17 00:03:02 +02:00
|
|
|
resize(pos + 1, CAddInfo::NONE);
|
|
|
|
return vector::operator[](pos);
|
|
|
|
}
|
|
|
|
|
2018-03-12 07:20:18 +02:00
|
|
|
si32 CAddInfo::operator[](size_type pos) const
|
|
|
|
{
|
|
|
|
return pos < size() ? vector::operator[](pos) : CAddInfo::NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string CAddInfo::toString() const
|
|
|
|
{
|
|
|
|
return toJsonNode().toJson(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
JsonNode CAddInfo::toJsonNode() const
|
|
|
|
{
|
|
|
|
if(size() < 2)
|
|
|
|
{
|
|
|
|
return JsonUtils::intNode(operator[](0));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
JsonNode node(JsonNode::JsonType::DATA_VECTOR);
|
|
|
|
for(si32 value : *this)
|
|
|
|
node.Vector().push_back(JsonUtils::intNode(value));
|
|
|
|
return node;
|
|
|
|
}
|
|
|
|
}
|
2023-04-16 19:42:56 +02:00
|
|
|
std::string Bonus::Description(std::optional<si32> customValue) const
|
2010-05-02 21:20:26 +03:00
|
|
|
{
|
|
|
|
std::ostringstream str;
|
2016-08-18 14:03:20 +02:00
|
|
|
|
2015-09-04 18:38:42 +02:00
|
|
|
if(description.empty())
|
2018-03-27 09:54:58 +02:00
|
|
|
{
|
|
|
|
if(stacking.empty() || stacking == "ALWAYS")
|
2015-09-04 18:38:42 +02:00
|
|
|
{
|
2018-03-27 09:54:58 +02:00
|
|
|
switch(source)
|
|
|
|
{
|
2023-05-01 00:20:01 +02:00
|
|
|
case BonusSource::ARTIFACT:
|
2023-01-02 15:58:56 +02:00
|
|
|
str << ArtifactID(sid).toArtifact(VLC->artifacts())->getNameTranslated();
|
2018-03-27 09:54:58 +02:00
|
|
|
break;
|
2023-05-01 00:20:01 +02:00
|
|
|
case BonusSource::SPELL_EFFECT:
|
2023-01-02 00:06:42 +02:00
|
|
|
str << SpellID(sid).toSpell(VLC->spells())->getNameTranslated();
|
2018-03-27 09:54:58 +02:00
|
|
|
break;
|
2023-05-01 00:20:01 +02:00
|
|
|
case BonusSource::CREATURE_ABILITY:
|
|
|
|
str << CreatureID(sid).toCreature(VLC->creatures())->getNamePluralTranslated();
|
2018-03-27 09:54:58 +02:00
|
|
|
break;
|
2023-05-01 00:20:01 +02:00
|
|
|
case BonusSource::SECONDARY_SKILL:
|
|
|
|
str << VLC->skills()->getByIndex(sid)->getNameTranslated();
|
2018-03-27 09:54:58 +02:00
|
|
|
break;
|
2023-05-01 00:20:01 +02:00
|
|
|
case BonusSource::HERO_SPECIAL:
|
|
|
|
str << VLC->heroTypes()->getByIndex(sid)->getNameTranslated();
|
2018-10-29 22:33:13 +02:00
|
|
|
break;
|
2018-03-27 09:54:58 +02:00
|
|
|
default:
|
|
|
|
//todo: handle all possible sources
|
|
|
|
str << "Unknown";
|
|
|
|
break;
|
|
|
|
}
|
2015-09-04 18:38:42 +02:00
|
|
|
}
|
2018-03-27 09:54:58 +02:00
|
|
|
else
|
|
|
|
str << stacking;
|
|
|
|
}
|
2015-09-04 18:38:42 +02:00
|
|
|
else
|
2018-03-27 09:54:58 +02:00
|
|
|
{
|
2015-09-04 18:38:42 +02:00
|
|
|
str << description;
|
2018-03-27 09:54:58 +02:00
|
|
|
}
|
2016-08-18 14:03:20 +02:00
|
|
|
|
2023-01-24 17:35:30 +02:00
|
|
|
if(auto value = customValue.value_or(val))
|
|
|
|
str << " " << std::showpos << value;
|
2016-08-18 14:03:20 +02:00
|
|
|
|
2010-05-02 21:20:26 +03:00
|
|
|
return str.str();
|
|
|
|
}
|
|
|
|
|
2023-05-05 11:56:53 +02:00
|
|
|
static JsonNode subtypeToJson(BonusType type, int subtype)
|
2017-09-12 08:15:13 +02:00
|
|
|
{
|
|
|
|
switch(type)
|
|
|
|
{
|
2023-05-01 00:20:01 +02:00
|
|
|
case BonusType::PRIMARY_SKILL:
|
2023-08-19 20:43:50 +02:00
|
|
|
return JsonUtils::stringNode("primSkill." + NPrimarySkill::names[subtype]);
|
2023-05-01 00:20:01 +02:00
|
|
|
case BonusType::SPECIAL_SPELL_LEV:
|
|
|
|
case BonusType::SPECIFIC_SPELL_DAMAGE:
|
|
|
|
case BonusType::SPELL:
|
|
|
|
case BonusType::SPECIAL_PECULIAR_ENCHANT:
|
|
|
|
case BonusType::SPECIAL_ADD_VALUE_ENCHANT:
|
|
|
|
case BonusType::SPECIAL_FIXED_VALUE_ENCHANT:
|
2023-07-30 19:12:25 +02:00
|
|
|
return JsonUtils::stringNode(ModUtility::makeFullIdentifier("", "spell", SpellID::encode(subtype)));
|
2023-05-01 00:20:01 +02:00
|
|
|
case BonusType::IMPROVED_NECROMANCY:
|
|
|
|
case BonusType::SPECIAL_UPGRADE:
|
2023-07-30 19:12:25 +02:00
|
|
|
return JsonUtils::stringNode(ModUtility::makeFullIdentifier("", "creature", CreatureID::encode(subtype)));
|
2023-05-01 00:20:01 +02:00
|
|
|
case BonusType::GENERATE_RESOURCE:
|
2017-09-17 00:02:04 +02:00
|
|
|
return JsonUtils::stringNode("resource." + GameConstants::RESOURCE_NAMES[subtype]);
|
2017-09-12 08:15:13 +02:00
|
|
|
default:
|
2017-09-12 10:56:59 +02:00
|
|
|
return JsonUtils::intNode(subtype);
|
2017-09-12 08:15:13 +02:00
|
|
|
}
|
2017-09-12 10:56:59 +02:00
|
|
|
}
|
2017-09-12 08:15:13 +02:00
|
|
|
|
2023-05-05 11:56:53 +02:00
|
|
|
static JsonNode additionalInfoToJson(BonusType type, CAddInfo addInfo)
|
2017-09-12 10:56:59 +02:00
|
|
|
{
|
|
|
|
switch(type)
|
|
|
|
{
|
2023-05-01 00:20:01 +02:00
|
|
|
case BonusType::SPECIAL_UPGRADE:
|
2023-07-30 19:12:25 +02:00
|
|
|
return JsonUtils::stringNode(ModUtility::makeFullIdentifier("", "creature", CreatureID::encode(addInfo[0])));
|
2017-09-12 10:56:59 +02:00
|
|
|
default:
|
2018-03-27 09:54:58 +02:00
|
|
|
return addInfo.toJsonNode();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-11 08:21:24 +02:00
|
|
|
JsonNode Bonus::toJsonNode() const
|
|
|
|
{
|
2017-09-12 10:56:59 +02:00
|
|
|
JsonNode root(JsonNode::JsonType::DATA_STRUCT);
|
2018-03-27 09:54:58 +02:00
|
|
|
// only add values that might reasonably be found in config files
|
2017-09-12 08:15:13 +02:00
|
|
|
root["type"].String() = vstd::findKey(bonusNameMap, type);
|
2017-09-11 08:21:24 +02:00
|
|
|
if(subtype != -1)
|
2017-09-12 08:15:13 +02:00
|
|
|
root["subtype"] = subtypeToJson(type, subtype);
|
2018-03-12 07:20:18 +02:00
|
|
|
if(additionalInfo != CAddInfo::NONE)
|
2017-09-12 10:56:59 +02:00
|
|
|
root["addInfo"] = additionalInfoToJson(type, additionalInfo);
|
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
|
|
|
if(turnsRemain != 0)
|
|
|
|
root["turns"].Integer() = turnsRemain;
|
2023-05-01 00:20:01 +02:00
|
|
|
if(source != BonusSource::OTHER)
|
2023-02-13 10:52:11 +02:00
|
|
|
root["sourceType"].String() = vstd::findKey(bonusSourceMap, source);
|
2023-05-01 00:20:01 +02:00
|
|
|
if(targetSourceType != BonusSource::OTHER)
|
2023-02-15 21:19:35 +02:00
|
|
|
root["targetSourceType"].String() = vstd::findKey(bonusSourceMap, targetSourceType);
|
Entities redesign and a few ERM features
* Made most Handlers derived from CHandlerBase and moved service API there.
* Declared existing Entity APIs.
* Added basic script context caching
* Started Lua script module
* Started Lua spell effect API
* Started script state persistence
* Started battle info callback binding
* CommitPackage removed
* Extracted spells::Caster to own header; Expanded Spell API.
* implemented !!MC:S, !!FU:E, !!FU:P, !!MA, !!VR:H, !!VR:C
* !!BU:C, !!BU:E, !!BU:G, !!BU:M implemented
* Allow use of "MC:S@varName@" to declare normal variable (technically v-variable with string key)
* Re-enabled VERM macros.
* !?GM0 added
* !?TM implemented
* Added !!MF:N
* Started !?OB, !!BM, !!HE, !!OW, !!UN
* Added basic support of w-variables
* Added support for ERM indirect variables
* Made !?FU regular trigger
* !!re (ERA loop receiver) implemented
* Fixed ERM receivers with zero args.
2018-03-17 16:58:30 +02:00
|
|
|
if(sid != 0)
|
|
|
|
root["sourceID"].Integer() = sid;
|
2017-09-11 08:21:24 +02:00
|
|
|
if(val != 0)
|
2017-09-12 08:15:13 +02:00
|
|
|
root["val"].Integer() = val;
|
2023-05-01 00:20:01 +02:00
|
|
|
if(valType != BonusValueType::ADDITIVE_VALUE)
|
2017-09-17 00:02:04 +02:00
|
|
|
root["valueType"].String() = vstd::findKey(bonusValueMap, valType);
|
2022-11-15 02:20:55 +02:00
|
|
|
if(!stacking.empty())
|
2018-03-27 09:54:58 +02:00
|
|
|
root["stacking"].String() = stacking;
|
2022-11-15 02:20:55 +02:00
|
|
|
if(!description.empty())
|
2018-03-27 09:54:58 +02:00
|
|
|
root["description"].String() = description;
|
2023-05-01 00:20:01 +02:00
|
|
|
if(effectRange != BonusLimitEffect::NO_LIMIT)
|
2018-03-27 09:54:58 +02:00
|
|
|
root["effectRange"].String() = vstd::findKey(bonusLimitEffect, effectRange);
|
2023-05-01 00:20:01 +02:00
|
|
|
if(duration != BonusDuration::PERMANENT)
|
2023-05-05 11:56:53 +02:00
|
|
|
root["duration"] = BonusDuration::toJson(duration);
|
2018-03-27 09:54:58 +02:00
|
|
|
if(turnsRemain)
|
|
|
|
root["turns"].Integer() = turnsRemain;
|
2017-09-12 01:56:38 +02:00
|
|
|
if(limiter)
|
2018-04-01 13:17:34 +02:00
|
|
|
root["limiters"] = limiter->toJsonNode();
|
2017-09-11 08:21:24 +02:00
|
|
|
if(updater)
|
|
|
|
root["updater"] = updater->toJsonNode();
|
2018-03-27 09:54:58 +02:00
|
|
|
if(propagator)
|
|
|
|
root["propagator"].String() = vstd::findKey(bonusPropagatorMap, propagator);
|
2017-09-11 08:21:24 +02:00
|
|
|
return root;
|
|
|
|
}
|
|
|
|
|
2023-05-05 11:56:53 +02:00
|
|
|
Bonus::Bonus(BonusDuration::Type Duration, BonusType Type, BonusSource Src, si32 Val, ui32 ID, std::string Desc, si32 Subtype):
|
2023-05-01 00:20:01 +02:00
|
|
|
duration(Duration),
|
2023-03-13 23:26:44 +02:00
|
|
|
type(Type),
|
|
|
|
subtype(Subtype),
|
|
|
|
source(Src),
|
|
|
|
val(Val),
|
|
|
|
sid(ID),
|
|
|
|
description(std::move(Desc))
|
2010-07-13 08:25:40 +03:00
|
|
|
{
|
|
|
|
boost::algorithm::trim(description);
|
2023-05-01 00:20:01 +02:00
|
|
|
targetSourceType = BonusSource::OTHER;
|
2010-07-13 08:25:40 +03:00
|
|
|
}
|
|
|
|
|
2023-05-05 11:56:53 +02:00
|
|
|
Bonus::Bonus(BonusDuration::Type Duration, BonusType Type, BonusSource Src, si32 Val, ui32 ID, si32 Subtype, BonusValueType ValType):
|
2023-05-01 00:20:01 +02:00
|
|
|
duration(Duration),
|
2023-03-13 23:26:44 +02:00
|
|
|
type(Type),
|
|
|
|
subtype(Subtype),
|
|
|
|
source(Src),
|
|
|
|
val(Val),
|
|
|
|
sid(ID),
|
|
|
|
valType(ValType)
|
2010-07-13 08:25:40 +03:00
|
|
|
{
|
|
|
|
turnsRemain = 0;
|
2023-05-01 00:20:01 +02:00
|
|
|
effectRange = BonusLimitEffect::NO_LIMIT;
|
|
|
|
targetSourceType = BonusSource::OTHER;
|
2010-07-13 08:25:40 +03:00
|
|
|
}
|
|
|
|
|
2023-03-13 23:26:44 +02:00
|
|
|
std::shared_ptr<Bonus> Bonus::addPropagator(const TPropagatorPtr & Propagator)
|
2011-02-21 06:13:00 +02:00
|
|
|
{
|
|
|
|
propagator = Propagator;
|
2016-09-19 23:36:35 +02:00
|
|
|
return this->shared_from_this();
|
2010-07-13 08:25:40 +03:00
|
|
|
}
|
|
|
|
|
2011-12-14 00:23:17 +03:00
|
|
|
DLL_LINKAGE std::ostream & operator<<(std::ostream &out, const Bonus &bonus)
|
2010-07-12 13:20:25 +03:00
|
|
|
{
|
2023-03-13 23:26:44 +02:00
|
|
|
for(const auto & i : bonusNameMap)
|
|
|
|
if(i.second == bonus.type)
|
|
|
|
out << "\tType: " << i.first << " \t";
|
2010-07-12 13:20:25 +03:00
|
|
|
|
|
|
|
#define printField(field) out << "\t" #field ": " << (int)bonus.field << "\n"
|
|
|
|
printField(val);
|
|
|
|
printField(subtype);
|
2023-05-05 11:56:53 +02:00
|
|
|
printField(duration.to_ulong());
|
2010-07-12 13:20:25 +03:00
|
|
|
printField(source);
|
2011-02-20 20:32:39 +02:00
|
|
|
printField(sid);
|
2018-03-12 07:20:18 +02:00
|
|
|
if(bonus.additionalInfo != CAddInfo::NONE)
|
|
|
|
out << "\taddInfo: " << bonus.additionalInfo.toString() << "\n";
|
2010-07-12 13:20:25 +03:00
|
|
|
printField(turnsRemain);
|
|
|
|
printField(valType);
|
2018-03-27 09:54:58 +02:00
|
|
|
if(!bonus.stacking.empty())
|
|
|
|
out << "\tstacking: \"" << bonus.stacking << "\"\n";
|
2010-07-12 13:20:25 +03:00
|
|
|
printField(effectRange);
|
|
|
|
#undef printField
|
|
|
|
|
2017-09-12 01:56:38 +02:00
|
|
|
if(bonus.limiter)
|
|
|
|
out << "\tLimiter: " << bonus.limiter->toString() << "\n";
|
2017-09-11 02:36:02 +02:00
|
|
|
if(bonus.updater)
|
|
|
|
out << "\tUpdater: " << bonus.updater->toString() << "\n";
|
|
|
|
|
2010-07-12 13:20:25 +03:00
|
|
|
return out;
|
|
|
|
}
|
2013-01-20 15:06:18 +03:00
|
|
|
|
2023-03-13 23:26:44 +02:00
|
|
|
std::shared_ptr<Bonus> Bonus::addLimiter(const TLimiterPtr & Limiter)
|
2013-01-16 16:02:01 +03:00
|
|
|
{
|
2013-01-20 15:06:18 +03:00
|
|
|
if (limiter)
|
2013-01-16 16:02:01 +03:00
|
|
|
{
|
2014-03-23 15:59:03 +03:00
|
|
|
//If we already have limiter list, retrieve it
|
2018-04-01 13:17:34 +02:00
|
|
|
auto limiterList = std::dynamic_pointer_cast<AllOfLimiter>(limiter);
|
2013-01-20 15:06:18 +03:00
|
|
|
if(!limiterList)
|
|
|
|
{
|
|
|
|
//Create a new limiter list with old limiter and the new one will be pushed later
|
2018-04-01 13:17:34 +02:00
|
|
|
limiterList = std::make_shared<AllOfLimiter>();
|
2013-01-20 15:06:18 +03:00
|
|
|
limiterList->add(limiter);
|
|
|
|
limiter = limiterList;
|
|
|
|
}
|
|
|
|
|
|
|
|
limiterList->add(Limiter);
|
2013-01-16 16:02:01 +03:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-01-19 20:38:37 +03:00
|
|
|
limiter = Limiter;
|
2013-01-16 16:02:01 +03:00
|
|
|
}
|
2016-09-19 23:36:35 +02:00
|
|
|
return this->shared_from_this();
|
2012-12-13 13:49:12 +03:00
|
|
|
}
|
|
|
|
|
2017-09-10 05:20:47 +02:00
|
|
|
// Updaters
|
|
|
|
|
2023-03-13 23:26:44 +02:00
|
|
|
std::shared_ptr<Bonus> Bonus::addUpdater(const TUpdaterPtr & Updater)
|
2017-09-12 01:56:38 +02:00
|
|
|
{
|
|
|
|
updater = Updater;
|
|
|
|
return this->shared_from_this();
|
|
|
|
}
|
|
|
|
|
2022-07-26 15:07:42 +02:00
|
|
|
VCMI_LIB_NAMESPACE_END
|