浏览代码

getSourcesForSpell

SoundSSGood 4 月之前
父节点
当前提交
7f9936cf10

+ 6 - 0
lib/bonuses/Bonus.cpp

@@ -236,6 +236,12 @@ Bonus::Bonus(BonusDuration::Type Duration, BonusType Type, BonusSource Src, si32
 	targetSourceType = BonusSource::OTHER;
 }
 
+Bonus::Bonus(const Bonus & inst, const BonusSourceID & sourceId)
+	: Bonus(inst)
+{
+	sid = sourceId;
+}
+
 std::shared_ptr<Bonus> Bonus::addPropagator(const TPropagatorPtr & Propagator)
 {
 	propagator = Propagator;

+ 1 - 0
lib/bonuses/Bonus.h

@@ -86,6 +86,7 @@ struct DLL_LINKAGE Bonus : public std::enable_shared_from_this<Bonus>, public Se
 	Bonus(BonusDuration::Type Duration, BonusType Type, BonusSource Src, si32 Val, BonusSourceID sourceID);
 	Bonus(BonusDuration::Type Duration, BonusType Type, BonusSource Src, si32 Val, BonusSourceID sourceID, BonusSubtypeID subtype);
 	Bonus(BonusDuration::Type Duration, BonusType Type, BonusSource Src, si32 Val, BonusSourceID sourceID, BonusSubtypeID subtype, BonusValueType ValType);
+	Bonus(const Bonus & inst, const BonusSourceID & sourceId);
 	Bonus() = default;
 
 	template <typename Handler> void serialize(Handler &h)

+ 0 - 1
lib/bonuses/CBonusSystemNode.h

@@ -120,7 +120,6 @@ public:
 	virtual std::string bonusToString(const std::shared_ptr<Bonus>& bonus) const {return "";}; //description or bonus name
 	virtual std::string nodeName() const;
 	bool isHypothetic() const { return isHypotheticNode; }
-	void setHypothetic(const bool hypothetic);
 
 	BonusList & getExportedBonusList();
 	const BonusList & getExportedBonusList() const;

+ 3 - 0
lib/entities/artifact/CArtHandler.cpp

@@ -181,6 +181,7 @@ std::shared_ptr<CArtifact> CArtHandler::loadFromJson(const std::string & scope,
 		for(const auto & b : node["bonuses"].Vector())
 		{
 			auto bonus = JsonUtils::parseBonus(b);
+			bonus->sid = art->getId();
 			art->addNewBonus(bonus);
 		}
 	}
@@ -191,6 +192,7 @@ std::shared_ptr<CArtifact> CArtHandler::loadFromJson(const std::string & scope,
 			if (b.second.isNull())
 				continue;
 			auto bonus = JsonUtils::parseBonus(b.second, art->getBonusTextID(b.first));
+			bonus->sid = art->getId();
 			art->addNewBonus(bonus);
 		}
 	}
@@ -200,6 +202,7 @@ std::shared_ptr<CArtifact> CArtHandler::loadFromJson(const std::string & scope,
 		if (b.second.isNull())
 			continue;
 		auto bonus = JsonUtils::parseBonus(b.second, art->getBonusTextID(b.first));
+		bonus->sid = art->getId();
 		bonus->source = BonusSource::ARTIFACT;
 		bonus->duration = BonusDuration::PERMANENT;
 		bonus->description.appendTextID(art->getNameTextID());

+ 27 - 23
lib/mapObjects/CGHeroInstance.cpp

@@ -902,22 +902,7 @@ void CGHeroInstance::spendMana(ServerCallback * server, const int spellCost) con
 
 bool CGHeroInstance::canCastThisSpell(const spells::Spell * spell) const
 {
-	const bool isAllowed = cb->isAllowed(spell->getId());
-
-	const bool inSpellBook = vstd::contains(spells, spell->getId()) && hasSpellbook();
-	const bool specificBonus = hasBonusOfType(BonusType::SPELL, BonusSubtypeID(spell->getId()));
-
-	bool schoolBonus = false;
-
-	spell->forEachSchool([this, &schoolBonus](const SpellSchool & cnf, bool & stop)
-	{
-		if(hasBonusOfType(BonusType::SPELLS_OF_SCHOOL, BonusSubtypeID(cnf)))
-		{
-			schoolBonus = stop = true;
-		}
-	});
-
-	const bool levelBonus = hasBonusOfType(BonusType::SPELLS_OF_LEVEL, BonusCustomSubtype::spellLevel(spell->getLevel()));
+	const bool inSpellBook = spellbookContainsSpell(spell->getId()) && hasSpellbook();
 
 	if(spell->isSpecial())
 	{
@@ -925,9 +910,9 @@ bool CGHeroInstance::canCastThisSpell(const spells::Spell * spell) const
 		{//hero has this spell in spellbook
 			logGlobal->error("Special spell %s in spellbook.", spell->getNameTranslated());
 		}
-		return specificBonus;
+		return hasBonusOfType(BonusType::SPELL, BonusSubtypeID(spell->getId()));
 	}
-	else if(!isAllowed)
+	else if(!cb->isAllowed(spell->getId()))
 	{
 		if(inSpellBook)
 		{
@@ -935,12 +920,8 @@ bool CGHeroInstance::canCastThisSpell(const spells::Spell * spell) const
 			//it is normal if set in map editor, but trace it to possible debug of magic guild
 			logGlobal->trace("Banned spell %s in spellbook.", spell->getNameTranslated());
 		}
-		return inSpellBook || specificBonus || schoolBonus || levelBonus;
-	}
-	else
-	{
-		return inSpellBook || schoolBonus || specificBonus || levelBonus;
 	}
+	return !getSourcesForSpell(spell->getId()).empty();
 }
 
 bool CGHeroInstance::canLearnSpell(const spells::Spell * spell, bool allowBanned) const
@@ -1254,6 +1235,29 @@ bool CGHeroInstance::spellbookContainsSpell(const SpellID & spell) const
 	return vstd::contains(spells, spell);
 }
 
+std::vector<BonusSourceID> CGHeroInstance::getSourcesForSpell(const SpellID & spellId) const
+{
+	std::vector<BonusSourceID> sources;
+
+	if(hasSpellbook() && spellbookContainsSpell(spellId))
+		sources.emplace_back(getArt(ArtifactPosition::SPELLBOOK)->getId());
+
+	for(const auto & bonus : *getBonusesOfType(BonusType::SPELL, spellId))
+		sources.emplace_back(bonus->sid);
+
+	const auto spell = spellId.toSpell();
+	spell->forEachSchool([this, &sources](const SpellSchool & cnf, bool & stop)
+	{
+		for(const auto & bonus : *getBonusesOfType(BonusType::SPELLS_OF_SCHOOL, cnf))
+			sources.emplace_back(bonus->sid);
+	});
+
+	for(const auto & bonus : *getBonusesOfType(BonusType::SPELLS_OF_LEVEL, BonusCustomSubtype::spellLevel(spell->getLevel())))
+		sources.emplace_back(bonus->sid);
+
+	return sources;
+}
+
 void CGHeroInstance::removeSpellbook()
 {
 	spells.clear();

+ 1 - 0
lib/mapObjects/CGHeroInstance.h

@@ -153,6 +153,7 @@ public:
 	void addSpellToSpellbook(const SpellID & spell);
 	void removeSpellFromSpellbook(const SpellID & spell);
 	bool spellbookContainsSpell(const SpellID & spell) const;
+	std::vector<BonusSourceID> getSourcesForSpell(const SpellID & spell) const;
 	void removeSpellbook();
 	const std::set<SpellID> & getSpellsInSpellbook() const;
 	EAlignment getAlignment() const;

+ 1 - 1
lib/mapping/CMap.cpp

@@ -872,7 +872,7 @@ CArtifactInstance * CMap::createArtifact(const ArtifactID & artID, const SpellID
 
 	for (const auto & bonus : art->instanceBonuses)
 	{
-		auto instBonus = std::make_shared<Bonus>(*bonus);
+		auto instBonus = std::make_shared<Bonus>(*bonus, artInst->getId());
 		if(const auto srcLimiter = std::static_pointer_cast<const HasChargesLimiter>(bonus->limiter))
 			instBonus->limiter = std::make_shared<HasChargesLimiter>(*srcLimiter, artInst->getId());
 		artInst->addNewBonus(instBonus);

+ 17 - 22
server/CGameHandler.cpp

@@ -4337,34 +4337,29 @@ void CGameHandler::useChargeBasedSpell(const ObjectInstanceID & heroObjectID, co
 	assert(hero);
 	assert(hero->canCastThisSpell(spellID.toSpell()));
 
-	std::optional<std::tuple<ArtifactPosition, ArtifactInstanceID, uint16_t>> chargedArt;
-
 	// Check if hero used charge based spell
-	// To do this, we create a local copy of the hero with the necessary magical bonuses, except for the bonuses of charged artifacts
-	CGHeroInstance caster(&gameInfo());
-	caster.setHypothetic(true);
-	for(const auto & b : *hero->getAllBonuses(Selector::type()(BonusType::SPELLS_OF_LEVEL), nullptr))
-		caster.addNewBonus(b);
-	for(const auto & b : *hero->getAllBonuses(Selector::type()(BonusType::SPELLS_OF_SCHOOL), nullptr))
-		caster.addNewBonus(b);
-	for(const auto & spell : hero->getSpellsInSpellbook())
-		caster.addSpellToSpellbook(spell);
-	for(const auto & [slot, slotInfo] : hero->artifactsWorn)
-	{
-		const auto * artInst = slotInfo.getArt();
-		const auto * artType = artInst->getType();
-		if(artType->getDischargeCondition() == DischargeArtifactCondition::SPELLCAST)
+	// Try to find other sources of the spell besides the charged artifacts. If there are any, we use them.
+	std::optional<std::tuple<ArtifactPosition, ArtifactInstanceID, uint16_t>> chargedArt;
+	for(const auto & source : hero->getSourcesForSpell(spellID))
+	{
+		if(const auto * artInst = hero->getArtByInstanceId(source.as<ArtifactInstanceID>()))
 		{
-			if(const auto spellCost = artType->getChargeCost(spellID))
+			const auto * artType = artInst->getType();
+			const auto spellCost = artType->getChargeCost(spellID);
+			if(spellCost.has_value() && artType->getDischargeCondition()== DischargeArtifactCondition::SPELLCAST)
 			{
-				chargedArt.emplace(slot, artInst->getId(), spellCost.value());
-				continue;
+				chargedArt.emplace(hero->getArtPos(artInst), artInst->getId(), spellCost.value());
 			}
+			else
+			{
+				return;
+			}
+		}
+		else
+		{
+			return;
 		}
-		caster.putArtifact(slot, artInst);
 	}
-	if(caster.canCastThisSpell(spellID.toSpell()))
-		return;
 
 	assert(chargedArt.has_value());
 	DischargeArtifact msg(std::get<1>(chargedArt.value()), std::get<2>(chargedArt.value()));