فهرست منبع

Extend Bonus.addInfo to integer vector (#427)

* changed Bonus::additionalInfo to integer vector

* fixed deserialization for old savegames

* removed newline from JsonNode::toJson()

* updated bonus schema; SPELL_AFTER_ATTACK and SPELL_BEFORE_ATTACK use new addInfo format

* removed unnecessary init in Bonus constructor
Henning Koehler 7 سال پیش
والد
کامیت
7f76648a7c

+ 2 - 2
config/creatures/dungeon.json

@@ -183,7 +183,7 @@
 				"type" : "SPELL_AFTER_ATTACK",
 				"subtype" : "spell.stoneGaze",
 				"val" : 20,
-				"addInfo" : 2000 // FIXME: replace with range field?
+				"addInfo" : [0,2]
 			}
 		},
 		"upgrades": ["medusaQueen"],
@@ -217,7 +217,7 @@
 				"type" : "SPELL_AFTER_ATTACK",
 				"subtype" : "spell.stoneGaze",
 				"val" : 20,
-				"addInfo" : 2000 // FIXME: replace with range?
+				"addInfo" : [0,2]
 			}
 		},
 		"graphics" :

+ 10 - 1
config/schemas/bonus.json

@@ -10,7 +10,16 @@
 		"addInfo": {
 			"anyOf" : [
 				{ "type" : "string" },
-				{ "type" : "number" }
+				{ "type" : "number" },
+				{
+					"type" : "array",
+					"items" : {
+						"anyof" : [
+							{ "type" : "string" },
+							{ "type" : "number" }
+						]
+					}
+				}
 			],
 			"description": "addInfo"
 		},

+ 1 - 1
lib/CGameState.cpp

@@ -1890,7 +1890,7 @@ UpgradeInfo CGameState::getUpgradeInfo(const CStackInstance &stack)
 		TBonusListPtr lista = h->getBonuses(Selector::typeSubtype(Bonus::SPECIAL_UPGRADE, base->idNumber));
 		for(const std::shared_ptr<Bonus> it : *lista)
 		{
-			auto nid = CreatureID(it->additionalInfo);
+			auto nid = CreatureID(it->additionalInfo[0]);
 			if (nid != base->idNumber) //in very specific case the upgrade is available by default (?)
 			{
 				ret.newID.push_back(nid);

+ 73 - 12
lib/HeroBonus.cpp

@@ -150,6 +150,59 @@ const BonusList * CBonusProxy::operator->() const
 	return get().get();
 }
 
+CAddInfo::CAddInfo()
+{
+}
+
+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);
+}
+
+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;
+	}
+}
+
 std::atomic<int32_t> CBonusSystemNode::treeChanged(1);
 const bool CBonusSystemNode::cachingEnabled = true;
 
@@ -1189,14 +1242,24 @@ JsonNode subtypeToJson(Bonus::BonusType type, int subtype)
 	}
 }
 
-JsonNode additionalInfoToJson(Bonus::BonusType type, int addInfo)
+JsonNode additionalInfoToJson(Bonus::BonusType type, CAddInfo addInfo)
 {
 	switch(type)
 	{
 	case Bonus::SPECIAL_UPGRADE:
-		return JsonUtils::stringNode("creature." + CreatureID::encode(addInfo));
+		return JsonUtils::stringNode("creature." + CreatureID::encode(addInfo[0]));
 	default:
-		return JsonUtils::intNode(addInfo);
+		if(addInfo.size() <= 1)
+		{
+			return JsonUtils::intNode(addInfo[0]);
+		}
+		else
+		{
+			JsonNode vecNode(JsonNode::JsonType::DATA_VECTOR);
+			for(si32 value : addInfo)
+				vecNode.Vector().push_back(JsonUtils::intNode(value));
+			return vecNode;
+		}
 	}
 }
 
@@ -1207,7 +1270,7 @@ JsonNode Bonus::toJsonNode() const
 	root["type"].String() = vstd::findKey(bonusNameMap, type);
 	if(subtype != -1)
 		root["subtype"] = subtypeToJson(type, subtype);
-	if(additionalInfo != -1)
+	if(additionalInfo != CAddInfo::NONE)
 		root["addInfo"] = additionalInfoToJson(type, additionalInfo);
 	if(val != 0)
 		root["val"].Integer() = val;
@@ -1235,7 +1298,7 @@ std::string Bonus::nameForBonus() const
 	case Bonus::SPECIAL_PECULIAR_ENCHANT:
 		return (*VLC->spellh)[SpellID::ESpellID(subtype)]->identifier;
 	case Bonus::SPECIAL_UPGRADE:
-		return CreatureID::encode(subtype) + "2" + CreatureID::encode(additionalInfo);
+		return CreatureID::encode(subtype) + "2" + CreatureID::encode(additionalInfo[0]);
 	case Bonus::GENERATE_RESOURCE:
 		return GameConstants::RESOURCE_NAMES[subtype];
 	case Bonus::STACKS_SPEED:
@@ -1248,7 +1311,6 @@ std::string Bonus::nameForBonus() const
 Bonus::Bonus(ui16 Dur, BonusType Type, BonusSource Src, si32 Val, ui32 ID, std::string Desc, si32 Subtype)
 	: duration(Dur), type(Type), subtype(Subtype), source(Src), val(Val), sid(ID), description(Desc)
 {
-	additionalInfo = -1;
 	turnsRemain = 0;
 	valType = ADDITIVE_VALUE;
 	effectRange = NO_LIMIT;
@@ -1258,7 +1320,6 @@ Bonus::Bonus(ui16 Dur, BonusType Type, BonusSource Src, si32 Val, ui32 ID, std::
 Bonus::Bonus(ui16 Dur, BonusType Type, BonusSource Src, si32 Val, ui32 ID, si32 Subtype, ValueType ValType)
 	: duration(Dur), type(Type), subtype(Subtype), source(Src), val(Val), sid(ID), valType(ValType)
 {
-	additionalInfo = -1;
 	turnsRemain = 0;
 	effectRange = NO_LIMIT;
 }
@@ -1269,7 +1330,6 @@ Bonus::Bonus()
 	turnsRemain = 0;
 	type = NONE;
 	subtype = -1;
-	additionalInfo = -1;
 
 	valType = ADDITIVE_VALUE;
 	effectRange = NO_LIMIT;
@@ -1288,7 +1348,7 @@ namespace Selector
 {
 	DLL_LINKAGE CSelectFieldEqual<Bonus::BonusType> type(&Bonus::type);
 	DLL_LINKAGE CSelectFieldEqual<TBonusSubtype> subtype(&Bonus::subtype);
-	DLL_LINKAGE CSelectFieldEqual<si32> info(&Bonus::additionalInfo);
+	DLL_LINKAGE CSelectFieldEqual<CAddInfo> info(&Bonus::additionalInfo);
 	DLL_LINKAGE CSelectFieldEqual<Bonus::BonusSource> sourceType(&Bonus::source);
 	DLL_LINKAGE CSelectFieldEqual<Bonus::LimitEffect> effectRange(&Bonus::effectRange);
 	DLL_LINKAGE CWillLastTurns turns;
@@ -1299,11 +1359,11 @@ namespace Selector
 		return type(Type).And(subtype(Subtype));
 	}
 
-	CSelector DLL_LINKAGE typeSubtypeInfo(Bonus::BonusType type, TBonusSubtype subtype, si32 info)
+	CSelector DLL_LINKAGE typeSubtypeInfo(Bonus::BonusType type, TBonusSubtype subtype, CAddInfo info)
 	{
 		return CSelectFieldEqual<Bonus::BonusType>(&Bonus::type)(type)
 			.And(CSelectFieldEqual<TBonusSubtype>(&Bonus::subtype)(subtype))
-			.And(CSelectFieldEqual<si32>(&Bonus::additionalInfo)(info));
+			.And(CSelectFieldEqual<CAddInfo>(&Bonus::additionalInfo)(info));
 	}
 
 	CSelector DLL_LINKAGE source(Bonus::BonusSource source, ui32 sourceID)
@@ -1403,7 +1463,8 @@ DLL_LINKAGE std::ostream & operator<<(std::ostream &out, const Bonus &bonus)
 	printField(duration);
 	printField(source);
 	printField(sid);
-	printField(additionalInfo);
+	if(bonus.additionalInfo != CAddInfo::NONE)
+		out << "\taddInfo: " << bonus.additionalInfo.toString() << "\n";
 	printField(turnsRemain);
 	printField(valType);
 	printField(effectRange);

+ 32 - 6
lib/HeroBonus.h

@@ -87,6 +87,24 @@ private:
 	mutable TBonusListPtr data;
 };
 
+class DLL_LINKAGE CAddInfo : public std::vector<si32>
+{
+public:
+	static const si32 NONE = -1;
+
+	CAddInfo();
+	CAddInfo(si32 value);
+
+	bool operator==(si32 value) const;
+	bool operator!=(si32 value) const;
+
+	using std::vector<si32>::operator[];
+	si32 operator[](size_type pos) const;
+
+	std::string toString() const;
+	JsonNode toJsonNode() const;
+};
+
 #define BONUS_TREE_DESERIALIZATION_FIX if(!h.saving && h.smartPointerSerialization) deserializationFix();
 
 #define BONUS_LIST										\
@@ -147,8 +165,8 @@ private:
 	BONUS_NAME(MAGIC_RESISTANCE) /*in % (value)*/		\
 	BONUS_NAME(CHANGES_SPELL_COST_FOR_ALLY) /*in mana points (value) , eg. mage*/ \
 	BONUS_NAME(CHANGES_SPELL_COST_FOR_ENEMY) /*in mana points (value) , eg. pegasus */ \
-	BONUS_NAME(SPELL_AFTER_ATTACK) /* subtype - spell id, value - chance %, additional info % 1000 - level, (additional info)/1000 -> [0 - all attacks, 1 - shot only, 2 - melee only*/ \
-	BONUS_NAME(SPELL_BEFORE_ATTACK) /* subtype - spell id, value - chance %, additional info % 1000 - level, (additional info)/1000 -> [0 - all attacks, 1 - shot only, 2 - melee only*/ \
+	BONUS_NAME(SPELL_AFTER_ATTACK) /* subtype - spell id, value - chance %, addInfo[0] - level, addInfo[1] -> [0 - all attacks, 1 - shot only, 2 - melee only] */ \
+	BONUS_NAME(SPELL_BEFORE_ATTACK) /* subtype - spell id, value - chance %, addInfo[0] - level, addInfo[1] -> [0 - all attacks, 1 - shot only, 2 - melee only] */ \
 	BONUS_NAME(SPELL_RESISTANCE_AURA) /*eg. unicorns, value - resistance bonus in % for adjacent creatures*/ \
 	BONUS_NAME(LEVEL_SPELL_IMMUNITY) /*creature is immune to all spell with level below or equal to value of this bonus */ \
 	BONUS_NAME(BLOCK_MAGIC_ABOVE) /*blocks casting spells of the level > value */ \
@@ -338,7 +356,7 @@ struct DLL_LINKAGE Bonus : public std::enable_shared_from_this<Bonus>
 	ui32 sid; //source id: id of object/artifact/spell
 	ValueType valType;
 
-	si32 additionalInfo;
+	CAddInfo additionalInfo;
 	LimitEffect effectRange; //if not NO_LIMIT, bonus will be omitted by default
 
 	TLimiterPtr limiter;
@@ -360,7 +378,15 @@ struct DLL_LINKAGE Bonus : public std::enable_shared_from_this<Bonus>
 		h & val;
 		h & sid;
 		h & description;
-		h & additionalInfo;
+		if(version >= 783)
+		{
+			h & additionalInfo;
+		}
+		else
+		{
+			additionalInfo.resize(1, -1);
+			h & additionalInfo[0];
+		}
 		h & turnsRemain;
 		h & valType;
 		h & effectRange;
@@ -980,14 +1006,14 @@ namespace Selector
 {
 	extern DLL_LINKAGE CSelectFieldEqual<Bonus::BonusType> type;
 	extern DLL_LINKAGE CSelectFieldEqual<TBonusSubtype> subtype;
-	extern DLL_LINKAGE CSelectFieldEqual<si32> info;
+	extern DLL_LINKAGE CSelectFieldEqual<CAddInfo> info;
 	extern DLL_LINKAGE CSelectFieldEqual<Bonus::BonusSource> sourceType;
 	extern DLL_LINKAGE CSelectFieldEqual<Bonus::LimitEffect> effectRange;
 	extern DLL_LINKAGE CWillLastTurns turns;
 	extern DLL_LINKAGE CWillLastDays days;
 
 	CSelector DLL_LINKAGE typeSubtype(Bonus::BonusType Type, TBonusSubtype Subtype);
-	CSelector DLL_LINKAGE typeSubtypeInfo(Bonus::BonusType type, TBonusSubtype subtype, si32 info);
+	CSelector DLL_LINKAGE typeSubtypeInfo(Bonus::BonusType type, TBonusSubtype subtype, CAddInfo info);
 	CSelector DLL_LINKAGE source(Bonus::BonusSource source, ui32 sourceID);
 	CSelector DLL_LINKAGE sourceTypeSel(Bonus::BonusSource source);
 	CSelector DLL_LINKAGE valueType(Bonus::ValueType valType);

+ 53 - 3
lib/JsonNode.cpp

@@ -418,7 +418,6 @@ std::string JsonNode::toJson(bool compact) const
 	std::ostringstream out;
 	JsonWriter writer(out, compact);
 	writer.writeNode(*this);
-	out << "\n";
 	return out.str();
 }
 
@@ -496,6 +495,57 @@ void JsonUtils::resolveIdentifier(si32 &var, const JsonNode &node, std::string n
 	}
 }
 
+void JsonUtils::resolveAddInfo(CAddInfo & var, const JsonNode & node)
+{
+	const JsonNode & value = node["addInfo"];
+	if (!value.isNull())
+	{
+		switch (value.getType())
+		{
+		case JsonNode::JsonType::DATA_INTEGER:
+			var = value.Integer();
+			break;
+		case JsonNode::JsonType::DATA_FLOAT:
+			var = value.Float();
+			break;
+		case JsonNode::JsonType::DATA_STRING:
+			VLC->modh->identifiers.requestIdentifier(value, [&](si32 identifier)
+			{
+				var = identifier;
+			});
+			break;
+		case JsonNode::JsonType::DATA_VECTOR:
+			{
+				const JsonVector & vec = value.Vector();
+				var.resize(vec.size());
+				for(int i = 0; i < vec.size(); i++)
+				{
+					switch(vec[i].getType())
+					{
+						case JsonNode::JsonType::DATA_INTEGER:
+							var[i] = vec[i].Integer();
+							break;
+						case JsonNode::JsonType::DATA_FLOAT:
+							var[i] = vec[i].Float();
+							break;
+						case JsonNode::JsonType::DATA_STRING:
+							VLC->modh->identifiers.requestIdentifier(vec[i], [&var,i](si32 identifier)
+							{
+								var[i] = identifier;
+							});
+							break;
+						default:
+							logMod->error("Error! Wrong identifier used for value of addInfo[%d].", i);
+					}
+				}
+				break;
+			}
+		default:
+			logMod->error("Error! Wrong identifier used for value of addInfo.");
+		}
+	}
+}
+
 void JsonUtils::resolveIdentifier(const JsonNode &node, si32 &var)
 {
 	switch (node.getType())
@@ -548,7 +598,7 @@ bool JsonUtils::parseBonus(const JsonNode &ability, Bonus *b)
 	if (!value->isNull())
 		b->valType = static_cast<Bonus::ValueType>(parseByMap(bonusValueMap, value, "value type "));
 
-	resolveIdentifier(b->additionalInfo, ability, "addInfo");
+	resolveAddInfo(b->additionalInfo, ability);
 
 	b->turnsRemain = ability["turns"].Float();
 
@@ -698,7 +748,7 @@ void JsonUtils::unparseBonus( JsonNode &node, const std::shared_ptr<Bonus>& bonu
 	node["subtype"].Float() = bonus->subtype;
 	node["val"].Float() = bonus->val;
 	node["valueType"].String() = reverseMapFirst<std::string, Bonus::ValueType>(bonus->valType, bonusValueMap);
-	node["additionalInfo"].Float() = bonus->additionalInfo;
+	node["additionalInfo"] = bonus->additionalInfo.toJsonNode();
 	node["turns"].Float() = bonus->turnsRemain;
 	node["sourceID"].Float() = bonus->source;
 	node["description"].String() = bonus->description;

+ 2 - 0
lib/JsonNode.h

@@ -15,6 +15,7 @@ typedef std::vector <JsonNode> JsonVector;
 
 struct Bonus;
 class ResourceID;
+class CAddInfo;
 
 class DLL_LINKAGE JsonNode
 {
@@ -170,6 +171,7 @@ namespace JsonUtils
 	DLL_LINKAGE void unparseBonus (JsonNode &node, const std::shared_ptr<Bonus>& bonus);
 	DLL_LINKAGE void resolveIdentifier(si32 &var, const JsonNode &node, std::string name);
 	DLL_LINKAGE void resolveIdentifier(const JsonNode &node, si32 &var);
+	DLL_LINKAGE void resolveAddInfo(CAddInfo & var, const JsonNode & node);
 
 	/**
 	 * @brief recursively merges source into dest, replacing identical fields

+ 3 - 3
lib/battle/CBattleInfoCallback.cpp

@@ -797,7 +797,7 @@ TDmgRange CBattleInfoCallback::calculateDmgRange(const BattleAttackInfo & info)
 	TBonusListPtr blessEffects = attackerBonuses->getBonuses(selectorForcedMaxDamage, cachingStrForcedMaxDamage);
 
 	int curseBlessAdditiveModifier = blessEffects->totalValue() - curseEffects->totalValue();
-	double curseMultiplicativePenalty = curseEffects->size() ? (*std::max_element(curseEffects->begin(), curseEffects->end(), &Bonus::compareByAdditionalInfo<std::shared_ptr<Bonus>>))->additionalInfo : 0;
+	double curseMultiplicativePenalty = curseEffects->size() ? (*std::max_element(curseEffects->begin(), curseEffects->end(), &Bonus::compareByAdditionalInfo<std::shared_ptr<Bonus>>))->additionalInfo[0] : 0;
 
 	if(curseMultiplicativePenalty) //curse handling (partial, the rest is below)
 	{
@@ -1738,12 +1738,12 @@ SpellID CBattleInfoCallback::getRandomCastedSpell(CRandomGenerator & rand,const
 	int totalWeight = 0;
 	for(auto b : *bl)
 	{
-		totalWeight += std::max(b->additionalInfo, 1); //minimal chance to cast is 1
+		totalWeight += std::max(b->additionalInfo[0], 1); //minimal chance to cast is 1
 	}
 	int randomPos = rand.nextInt(totalWeight - 1);
 	for(auto b : *bl)
 	{
-		randomPos -= std::max(b->additionalInfo, 1);
+		randomPos -= std::max(b->additionalInfo[0], 1);
 		if(randomPos < 0)
 		{
 			return SpellID(b->subtype);

+ 1 - 1
lib/serializer/CSerializer.h

@@ -12,7 +12,7 @@
 #include "../ConstTransitivePtr.h"
 #include "../GameConstants.h"
 
-const ui32 SERIALIZATION_VERSION = 782;
+const ui32 SERIALIZATION_VERSION = 783;
 const ui32 MINIMAL_SERIALIZATION_VERSION = 753;
 const std::string SAVEGAME_MAGIC = "VCMISVG";
 

+ 1 - 1
lib/spells/effects/Timed.cpp

@@ -181,7 +181,7 @@ void Timed::prepareEffects(SetStackEffect & sse, const Mechanics * m, const Effe
 		const auto tier = std::max(affected->creatureLevel(), 1); //don't divide by 0 for certain creatures (commanders, war machines)
 		if(bonus)
 		{
-			switch(bonus->additionalInfo)
+			switch(bonus->additionalInfo[0])
 			{
 			case 0: //normal
 				switch(tier)

+ 21 - 11
server/CGameHandler.cpp

@@ -4731,9 +4731,9 @@ void CGameHandler::stackTurnTrigger(const CStack *st)
 
 			for (auto b : bl)
 			{
-				if(b->additionalInfo >= 0)
+				if(b->additionalInfo != CAddInfo::NONE)
 				{
-					const CStack * stack = gs->curB->battleGetStackByID(b->additionalInfo); //binding stack must be alive and adjacent
+					const CStack * stack = gs->curB->battleGetStackByID(b->additionalInfo[0]); //binding stack must be alive and adjacent
 					if(stack)
 					{
 						if(vstd::contains(adjacent, stack)) //binding stack is still present
@@ -4840,7 +4840,7 @@ void CGameHandler::stackTurnTrigger(const CStack *st)
 				{
 					cast = true;
 
-					int cooldown = bonus->additionalInfo;
+					int cooldown = bonus->additionalInfo[0];
 					BattleSetStackProperty ssp;
 					ssp.which = BattleSetStackProperty::ENCHANTER_COUNTER;
 					ssp.absolute = false;
@@ -5483,8 +5483,18 @@ void CGameHandler::attackCasting(bool ranged, Bonus::BonusType attackMode, const
 			TBonusListPtr spellsByType = attacker->getBonuses(Selector::typeSubtype(attackMode, spellID));
 			for(const std::shared_ptr<Bonus> sf : *spellsByType)
 			{
-				vstd::amax(spellLevel, sf->additionalInfo % 1000); //pick highest level
-				int meleeRanged = sf->additionalInfo / 1000;
+				int meleeRanged;
+				if(sf->additionalInfo.size() < 2)
+				{
+					// legacy format
+					vstd::amax(spellLevel, sf->additionalInfo[0] % 1000);
+					meleeRanged = sf->additionalInfo[0] / 1000;
+				}
+				else
+				{
+					vstd::amax(spellLevel, sf->additionalInfo[0]);
+					meleeRanged = sf->additionalInfo[1];
+				}
 				if (meleeRanged == 0 || (meleeRanged == 1 && ranged) || (meleeRanged == 2 && !ranged))
 					castMe = true;
 			}
@@ -5569,7 +5579,7 @@ void CGameHandler::handleAfterAttackCasting(bool ranged, const CStack * attacker
 	TBonusListPtr acidBreath = attacker->getBonuses(Selector::type(Bonus::ACID_BREATH));
 	for(const std::shared_ptr<Bonus> b : *acidBreath)
 	{
-		if(b->additionalInfo > getRandomGenerator().nextInt(99))
+		if(b->additionalInfo[0] > getRandomGenerator().nextInt(99))
 			acidDamage += b->val;
 	}
 
@@ -5597,10 +5607,10 @@ void CGameHandler::handleAfterAttackCasting(bool ranged, const CStack * attacker
 		if(getRandomGenerator().getDoubleRange(0, 1)() > chanceToTrigger)
 			return;
 
-		int bonusAdditionalInfo = attacker->getBonus(Selector::type(Bonus::TRANSMUTATION))->additionalInfo;
+		int bonusAdditionalInfo = attacker->getBonus(Selector::type(Bonus::TRANSMUTATION))->additionalInfo[0];
 
 		if(defender->getCreature()->idNumber == bonusAdditionalInfo ||
-			(bonusAdditionalInfo == -1 && defender->getCreature()->idNumber == attacker->getCreature()->idNumber))
+			(bonusAdditionalInfo == CAddInfo::NONE && defender->getCreature()->idNumber == attacker->getCreature()->idNumber))
 			return;
 
 		battle::UnitInfo resurrectInfo;
@@ -5609,7 +5619,7 @@ void CGameHandler::handleAfterAttackCasting(bool ranged, const CStack * attacker
 		resurrectInfo.position = defender->getPosition();
 		resurrectInfo.side = defender->unitSide();
 
-		if(bonusAdditionalInfo != -1)
+		if(bonusAdditionalInfo != CAddInfo::NONE)
 			resurrectInfo.type = CreatureID(bonusAdditionalInfo);
 		else
 			resurrectInfo.type = attacker->creatureId();
@@ -5639,13 +5649,13 @@ void CGameHandler::handleAfterAttackCasting(bool ranged, const CStack * attacker
 		if(attacker->hasBonusOfType(Bonus::DESTRUCTION, 0)) //killing by percentage
 		{
 			chanceToTrigger = attacker->valOfBonuses(Bonus::DESTRUCTION, 0) / 100.0f;
-			int percentageToDie = attacker->getBonus(Selector::type(Bonus::DESTRUCTION).And(Selector::subtype(0)))->additionalInfo;
+			int percentageToDie = attacker->getBonus(Selector::type(Bonus::DESTRUCTION).And(Selector::subtype(0)))->additionalInfo[0];
 			amountToDie = defender->getCount() * percentageToDie * 0.01f;
 		}
 		else if(attacker->hasBonusOfType(Bonus::DESTRUCTION, 1)) //killing by count
 		{
 			chanceToTrigger = attacker->valOfBonuses(Bonus::DESTRUCTION, 1) / 100.0f;
-			amountToDie = attacker->getBonus(Selector::type(Bonus::DESTRUCTION).And(Selector::subtype(1)))->additionalInfo;
+			amountToDie = attacker->getBonus(Selector::type(Bonus::DESTRUCTION).And(Selector::subtype(1)))->additionalInfo[0];
 		}
 
 		vstd::amin(chanceToTrigger, 1); //cap trigger chance at 100%