From 8764765dcf2f42c857856685758a0f2794b2356c Mon Sep 17 00:00:00 2001 From: Konstantin P Date: Fri, 5 May 2023 12:56:53 +0300 Subject: [PATCH] Bonus: complex duration as bitset Fixes #2125 --- lib/JsonNode.cpp | 10 ++++++++- lib/bonuses/Bonus.cpp | 12 +++++----- lib/bonuses/Bonus.h | 37 +++++++++++++++++++------------ lib/bonuses/BonusEnum.cpp | 26 +++++++++++++++++++++- lib/bonuses/BonusEnum.h | 28 +++++++++++++---------- lib/mapObjects/CObjectHandler.cpp | 2 +- lib/mapObjects/CObjectHandler.h | 2 +- scripting/lua/LuaStack.h | 8 +++++++ scripting/lua/api/BonusSystem.cpp | 18 +++++++++++++-- 9 files changed, 105 insertions(+), 38 deletions(-) diff --git a/lib/JsonNode.cpp b/lib/JsonNode.cpp index 31b7ad297..c49125a4f 100644 --- a/lib/JsonNode.cpp +++ b/lib/JsonNode.cpp @@ -975,7 +975,15 @@ bool JsonUtils::parseBonus(const JsonNode &ability, Bonus *b) switch (value->getType()) { case JsonNode::JsonType::DATA_STRING: - b->duration = static_cast(parseByMap(bonusDurationMap, value, "duration type ")); + b->duration = parseByMap(bonusDurationMap, value, "duration type "); + break; + case JsonNode::JsonType::DATA_VECTOR: + { + BonusDuration::Type dur = 0; + for (const JsonNode & d : value->Vector()) + dur |= parseByMapN(bonusDurationMap, &d, "duration type "); + b->duration = dur; + } break; default: logMod->error("Error! Wrong bonus duration format."); diff --git a/lib/bonuses/Bonus.cpp b/lib/bonuses/Bonus.cpp index 7a3be976f..da6f08362 100644 --- a/lib/bonuses/Bonus.cpp +++ b/lib/bonuses/Bonus.cpp @@ -154,7 +154,7 @@ std::string Bonus::Description(std::optional customValue) const return str.str(); } -JsonNode subtypeToJson(BonusType type, int subtype) +static JsonNode subtypeToJson(BonusType type, int subtype) { switch(type) { @@ -177,7 +177,7 @@ JsonNode subtypeToJson(BonusType type, int subtype) } } -JsonNode additionalInfoToJson(BonusType type, CAddInfo addInfo) +static JsonNode additionalInfoToJson(BonusType type, CAddInfo addInfo) { switch(type) { @@ -216,7 +216,7 @@ JsonNode Bonus::toJsonNode() const if(effectRange != BonusLimitEffect::NO_LIMIT) root["effectRange"].String() = vstd::findKey(bonusLimitEffect, effectRange); if(duration != BonusDuration::PERMANENT) - root["duration"].String() = vstd::findKey(bonusDurationMap, duration); + root["duration"] = BonusDuration::toJson(duration); if(turnsRemain) root["turns"].Integer() = turnsRemain; if(limiter) @@ -228,7 +228,7 @@ JsonNode Bonus::toJsonNode() const return root; } -Bonus::Bonus(BonusDuration Duration, BonusType Type, BonusSource Src, si32 Val, ui32 ID, std::string Desc, si32 Subtype): +Bonus::Bonus(BonusDuration::Type Duration, BonusType Type, BonusSource Src, si32 Val, ui32 ID, std::string Desc, si32 Subtype): duration(Duration), type(Type), subtype(Subtype), @@ -241,7 +241,7 @@ Bonus::Bonus(BonusDuration Duration, BonusType Type, BonusSource Src, si32 Val, targetSourceType = BonusSource::OTHER; } -Bonus::Bonus(BonusDuration Duration, BonusType Type, BonusSource Src, si32 Val, ui32 ID, si32 Subtype, BonusValueType ValType): +Bonus::Bonus(BonusDuration::Type Duration, BonusType Type, BonusSource Src, si32 Val, ui32 ID, si32 Subtype, BonusValueType ValType): duration(Duration), type(Type), subtype(Subtype), @@ -270,7 +270,7 @@ DLL_LINKAGE std::ostream & operator<<(std::ostream &out, const Bonus &bonus) #define printField(field) out << "\t" #field ": " << (int)bonus.field << "\n" printField(val); printField(subtype); - printField(duration); + printField(duration.to_ulong()); printField(source); printField(sid); if(bonus.additionalInfo != CAddInfo::NONE) diff --git a/lib/bonuses/Bonus.h b/lib/bonuses/Bonus.h index fbf41f25f..58837c910 100644 --- a/lib/bonuses/Bonus.h +++ b/lib/bonuses/Bonus.h @@ -10,7 +10,6 @@ #pragma once #include "BonusEnum.h" -#include "../JsonNode.h" VCMI_LIB_NAMESPACE_BEGIN @@ -52,7 +51,7 @@ public: /// Struct for handling bonuses of several types. Can be transferred to any hero struct DLL_LINKAGE Bonus : public std::enable_shared_from_this { - BonusDuration duration = BonusDuration::PERMANENT; //uses BonusDuration values + BonusDuration::Type duration = BonusDuration::PERMANENT; //uses BonusDuration values si16 turnsRemain = 0; //used if duration is N_TURNS, N_DAYS or ONE_WEEK BonusType type = BonusType::NONE; //uses BonusType values - says to what is this bonus - 1 byte @@ -75,8 +74,8 @@ struct DLL_LINKAGE Bonus : public std::enable_shared_from_this std::string description; - Bonus(BonusDuration Duration, BonusType Type, BonusSource Src, si32 Val, ui32 ID, std::string Desc, si32 Subtype=-1); - Bonus(BonusDuration Duration, BonusType Type, BonusSource Src, si32 Val, ui32 ID, si32 Subtype=-1, BonusValueType ValType = BonusValueType::ADDITIVE_VALUE); + Bonus(BonusDuration::Type Duration, BonusType Type, BonusSource Src, si32 Val, ui32 ID, std::string Desc, si32 Subtype=-1); + Bonus(BonusDuration::Type Duration, BonusType Type, BonusSource Src, si32 Val, ui32 ID, si32 Subtype=-1, BonusValueType ValType = BonusValueType::ADDITIVE_VALUE); Bonus() = default; template void serialize(Handler &h, const int version) @@ -107,43 +106,53 @@ struct DLL_LINKAGE Bonus : public std::enable_shared_from_this } static bool NDays(const Bonus *hb) { - return hb->duration == BonusDuration::N_DAYS; + auto set = hb->duration & BonusDuration::N_DAYS; + return set.any(); } static bool NTurns(const Bonus *hb) { - return hb->duration == BonusDuration::N_TURNS; + auto set = hb->duration & BonusDuration::N_TURNS; + return set.any(); } static bool OneDay(const Bonus *hb) { - return hb->duration == BonusDuration::ONE_DAY; + auto set = hb->duration & BonusDuration::ONE_DAY; + return set.any(); } static bool OneWeek(const Bonus *hb) { - return hb->duration == BonusDuration::ONE_WEEK; + auto set = hb->duration & BonusDuration::ONE_WEEK; + return set.any(); } static bool OneBattle(const Bonus *hb) { - return hb->duration == BonusDuration::ONE_BATTLE; + auto set = hb->duration & BonusDuration::ONE_BATTLE; + return set.any(); } static bool Permanent(const Bonus *hb) { - return hb->duration == BonusDuration::PERMANENT; + auto set = hb->duration & BonusDuration::PERMANENT; + return set.any(); } static bool UntilGetsTurn(const Bonus *hb) { - return hb->duration == BonusDuration::STACK_GETS_TURN; + auto set = hb->duration & BonusDuration::STACK_GETS_TURN; + return set.any(); } static bool UntilAttack(const Bonus *hb) { - return hb->duration == BonusDuration::UNTIL_ATTACK; + auto set = hb->duration & BonusDuration::UNTIL_ATTACK; + return set.any(); } static bool UntilBeingAttacked(const Bonus *hb) { - return hb->duration == BonusDuration::UNTIL_BEING_ATTACKED; + auto set = hb->duration & BonusDuration::UNTIL_BEING_ATTACKED; + return set.any(); } static bool UntilCommanderKilled(const Bonus *hb) { - return hb->duration == BonusDuration::COMMANDER_KILLED; + auto set = hb->duration & BonusDuration::COMMANDER_KILLED; + return set.any(); } inline bool operator == (const BonusType & cf) const { diff --git a/lib/bonuses/BonusEnum.cpp b/lib/bonuses/BonusEnum.cpp index 83ab8c58c..efd7428c3 100644 --- a/lib/bonuses/BonusEnum.cpp +++ b/lib/bonuses/BonusEnum.cpp @@ -30,7 +30,7 @@ VCMI_LIB_NAMESPACE_BEGIN #undef BONUS_SOURCE #define BONUS_ITEM(x) { #x, BonusDuration::x }, -const std::map bonusDurationMap = +const std::map bonusDurationMap = { BONUS_ITEM(PERMANENT) BONUS_ITEM(ONE_BATTLE) @@ -55,4 +55,28 @@ const std::map bonusLimitEffect = }; #undef BONUS_ITEM +namespace BonusDuration +{ + JsonNode toJson(const Type & duration) + { + std::vector durationNames; + for(auto durBit = 0; durBit < duration.size(); durBit++) + { + if(duration[durBit]) + durationNames.push_back(vstd::findKey(bonusDurationMap, duration & Type().set(durBit))); + } + if(durationNames.size() == 1) + { + return JsonUtils::stringNode(durationNames[0]); + } + else + { + JsonNode node(JsonNode::JsonType::DATA_VECTOR); + for(const std::string & dur : durationNames) + node.Vector().push_back(JsonUtils::stringNode(dur)); + return node; + } + } +} + VCMI_LIB_NAMESPACE_END \ No newline at end of file diff --git a/lib/bonuses/BonusEnum.h b/lib/bonuses/BonusEnum.h index a386baf65..8ab5d9f72 100644 --- a/lib/bonuses/BonusEnum.h +++ b/lib/bonuses/BonusEnum.h @@ -12,6 +12,8 @@ VCMI_LIB_NAMESPACE_BEGIN +#include "../JsonNode.h" + #define BONUS_LIST \ BONUS_NAME(NONE) \ BONUS_NAME(LEVEL_COUNTER) /* for commander artifacts*/ \ @@ -220,18 +222,20 @@ enum class BonusType BONUS_LIST #undef BONUS_NAME }; -enum class BonusDuration : uint16_t //when bonus is automatically removed +namespace BonusDuration //when bonus is automatically removed { - PERMANENT = 1, - ONE_BATTLE = 2, //at the end of battle - ONE_DAY = 4, //at the end of day - ONE_WEEK = 8, //at the end of week (bonus lasts till the end of week, thats NOT 7 days - N_TURNS = 16, //used during battles, after battle bonus is always removed - N_DAYS = 32, - UNTIL_BEING_ATTACKED = 64, /*removed after attack and counterattacks are performed*/ - UNTIL_ATTACK = 128, /*removed after attack and counterattacks are performed*/ - STACK_GETS_TURN = 256, /*removed when stack gets its turn - used for defensive stance*/ - COMMANDER_KILLED = 512 + using Type = std::bitset<10>; + extern JsonNode toJson(const Type & duration); + constexpr Type PERMANENT = 1 << 0; + constexpr Type ONE_BATTLE = 1 << 1; //at the end of battle + constexpr Type ONE_DAY = 1 << 2; //at the end of day + constexpr Type ONE_WEEK = 1 << 3; //at the end of week (bonus lasts till the end of week, thats NOT 7 days + constexpr Type N_TURNS = 1 << 4; //used during battles, after battle bonus is always removed + constexpr Type N_DAYS = 1 << 5; + constexpr Type UNTIL_BEING_ATTACKED = 1 << 6; /*removed after attack and counterattacks are performed*/ + constexpr Type UNTIL_ATTACK = 1 << 7; /*removed after attack and counterattacks are performed*/ + constexpr Type STACK_GETS_TURN = 1 << 8; /*removed when stack gets its turn - used for defensive stance*/ + constexpr Type COMMANDER_KILLED = 1 << 9; }; enum class BonusSource { @@ -257,7 +261,7 @@ enum class BonusValueType extern DLL_LINKAGE const std::map bonusNameMap; extern DLL_LINKAGE const std::map bonusValueMap; extern DLL_LINKAGE const std::map bonusSourceMap; -extern DLL_LINKAGE const std::map bonusDurationMap; +extern DLL_LINKAGE const std::map bonusDurationMap; extern DLL_LINKAGE const std::map bonusLimitEffect; VCMI_LIB_NAMESPACE_END \ No newline at end of file diff --git a/lib/mapObjects/CObjectHandler.cpp b/lib/mapObjects/CObjectHandler.cpp index 1ebd47ecd..7771ffa01 100644 --- a/lib/mapObjects/CObjectHandler.cpp +++ b/lib/mapObjects/CObjectHandler.cpp @@ -272,7 +272,7 @@ int3 CGObjectInstance::getVisitableOffset() const return appearance->getVisitableOffset(); } -void CGObjectInstance::giveDummyBonus(const ObjectInstanceID & heroID, BonusDuration duration) const +void CGObjectInstance::giveDummyBonus(const ObjectInstanceID & heroID, BonusDuration::Type duration) const { GiveBonus gbonus; gbonus.bonus.type = BonusType::NONE; diff --git a/lib/mapObjects/CObjectHandler.h b/lib/mapObjects/CObjectHandler.h index 21ed0b3d3..5e537af21 100644 --- a/lib/mapObjects/CObjectHandler.h +++ b/lib/mapObjects/CObjectHandler.h @@ -232,7 +232,7 @@ protected: virtual void setPropertyDer(ui8 what, ui32 val); /// Gives dummy bonus from this object to hero. Can be used to track visited state - void giveDummyBonus(const ObjectInstanceID & heroID, BonusDuration duration = BonusDuration::ONE_DAY) const; + void giveDummyBonus(const ObjectInstanceID & heroID, BonusDuration::Type duration = BonusDuration::ONE_DAY) const; ///Serialize object-type specific options virtual void serializeJsonOptions(JsonSerializeFormat & handler); diff --git a/scripting/lua/LuaStack.h b/scripting/lua/LuaStack.h index fd273eada..880536e6b 100644 --- a/scripting/lua/LuaStack.h +++ b/scripting/lua/LuaStack.h @@ -351,6 +351,14 @@ public: return 1; } + template + static int quickRetInt(lua_State * L, const std::bitset & value) + { + lua_settop(L, 0); + lua_pushinteger(L, static_cast(value.to_ulong())); + return 1; + } + static int quickRetStr(lua_State * L, const std::string & value) { lua_settop(L, 0); diff --git a/scripting/lua/api/BonusSystem.cpp b/scripting/lua/api/BonusSystem.cpp index 611748a2b..37f2147d6 100644 --- a/scripting/lua/api/BonusSystem.cpp +++ b/scripting/lua/api/BonusSystem.cpp @@ -155,8 +155,8 @@ int BonusProxy::toJsonNode(lua_State * L) return 1; } -template -static void publishMap(lua_State * L, const T & map) +template +static void publishMap(lua_State * L, const std::map & map) { for(auto & p : map) { @@ -169,6 +169,20 @@ static void publishMap(lua_State * L, const T & map) } } +template +static void publishMap(lua_State * L, const std::map> & map) +{ + for(auto & p : map) + { + const std::string & name = p.first; + auto id = static_cast(p.second.to_ulong()); + + lua_pushstring(L, name.c_str()); + lua_pushinteger(L, id); + lua_rawset(L, -3); + } +} + void BonusProxy::adjustStaticTable(lua_State * L) const { publishMap(L, bonusNameMap);