瀏覽代碼

Discharging conditions

SoundSSGood 5 月之前
父節點
當前提交
f614a8a7f5

+ 1 - 1
docs/modders/Bonus/Bonus_Types.md

@@ -1037,7 +1037,7 @@ Internal bonus, do not use
 
 ### ARTIFACT_CHARGE
 
-Consumable bonus. Used to perform actions specified by a specific artifact.
+Consumable bonus. Used to perform actions specified by a charged artifact.
 
 - val: number of charges
 

+ 5 - 0
lib/entities/artifact/CArtifact.cpp

@@ -282,6 +282,11 @@ std::optional<DischargeArtifactCondition> CChargedArtifact::getDischargeConditio
 	return condition;
 }
 
+bool CChargedArtifact::getRemoveOnDepletion() const
+{
+	return removeOnDepletion;
+}
+
 CArtifact::CArtifact()
 	: iconIndex(ArtifactID::NONE),
 	price(0)

+ 1 - 0
lib/entities/artifact/CArtifact.h

@@ -84,6 +84,7 @@ public:
 	void setDefaultStartCharges(const uint16_t charges);
 	uint16_t getDefaultStartCharges() const;
 	std::optional<DischargeArtifactCondition> getDischargeCondition() const;
+	bool getRemoveOnDepletion() const;
 };
 
 // Container for artifacts. Not for instances.

+ 74 - 25
lib/entities/artifact/CArtifactInstance.cpp

@@ -96,16 +96,15 @@ SpellID CScrollArtifactInstance::getScrollSpellID() const
 void CGrowingArtifactInstance::growingUp()
 {
 	auto artInst = static_cast<CArtifactInstance*>(this);
+	const auto artType = artInst->getType();
 	
-	if(artInst->getType()->isGrowing())
+	if(artType->isGrowing())
 	{
-		auto growingBonus = std::make_shared<Bonus>();
-		growingBonus->type = BonusType::ARTIFACT_GROWING;
-		growingBonus->val = 1;
-		growingBonus->duration = BonusDuration::PERMANENT;
-		artInst->accumulateBonus(growingBonus);
+		const auto growingBonus = artInst->getBonusesOfType(BonusType::ARTIFACT_GROWING);
+		assert(!growingBonus.empty());
+		growingBonus->front()->val++;
 
-		for(const auto & bonus : artInst->getType()->getBonusesPerLevel())
+		for(const auto & bonus : artType->getBonusesPerLevel())
 		{
 			// Every n levels
 			if(artInst->valOfBonuses(BonusType::ARTIFACT_GROWING) % bonus.first == 0)
@@ -113,7 +112,7 @@ void CGrowingArtifactInstance::growingUp()
 				artInst->accumulateBonus(std::make_shared<Bonus>(bonus.second));
 			}
 		}
-		for(const auto & bonus : artInst->getType()->getThresholdBonuses())
+		for(const auto & bonus : artType->getThresholdBonuses())
 		{
 			// At n level
 			if(artInst->valOfBonuses(BonusType::ARTIFACT_GROWING) == bonus.first)
@@ -121,6 +120,41 @@ void CGrowingArtifactInstance::growingUp()
 				artInst->addNewBonus(std::make_shared<Bonus>(bonus.second));
 			}
 		}
+
+		if(artType->isCharged())
+			artInst->onChargesChanged();
+	}
+}
+
+void CChargedArtifactInstance::onChargesChanged()
+{
+	auto artInst = static_cast<CArtifactInstance*>(this);
+	const auto artType = artInst->getType();
+
+	const auto bonusSelector = artType->getDischargeCondition() == DischargeArtifactCondition::SPELLCAST ?
+		Selector::type()(BonusType::SPELL) : Selector::all;
+
+	auto instBonuses = artInst->getAllBonuses(bonusSelector, nullptr);
+
+	if(artInst->getCharges() == 0)
+	{
+		for(const auto & bonus : *instBonuses)
+			if(bonus->type != BonusType::ARTIFACT_GROWING && bonus->type != BonusType::ARTIFACT_CHARGE)
+				artInst->removeBonus(bonus);
+	}
+	else
+	{
+		for(const auto & refBonus : *artType->getAllBonuses(bonusSelector, nullptr))
+		{
+			if(const auto bonusFound = std::find_if(instBonuses->begin(), instBonuses->end(),
+				[refBonus](const auto & instBonus)
+				{
+					return refBonus->type == instBonus->type;
+				}); bonusFound == instBonuses->end())
+			{
+				artInst->accumulateBonus(refBonus);
+			}
+		}
 	}
 }
 
@@ -128,12 +162,13 @@ void CChargedArtifactInstance::discharge(const uint16_t charges)
 {
 	auto artInst = static_cast<CArtifactInstance*>(this);
 
-	if(const auto chargeBonuses = artInst->getAllBonuses(Selector::type()(BonusType::ARTIFACT_CHARGE), nullptr))
+	if(const auto chargedBonus = artInst->getBonusesOfType(BonusType::ARTIFACT_CHARGE); !chargedBonus->empty())
 	{
-		if(chargeBonuses->front()->val > charges)
-			chargeBonuses->front()->val -= charges;
+		if(chargedBonus->front()->val > charges)
+			chargedBonus->front()->val -= charges;
 		else
-			chargeBonuses->front()->val = 0;
+			chargedBonus->front()->val = 0;
+		onChargesChanged();
 	}
 }
 
@@ -143,11 +178,10 @@ void CChargedArtifactInstance::addCharges(const uint16_t charges)
 
 	if(artInst->getType()->isCharged())
 	{
-		auto bonus = std::make_shared<Bonus>();
-		bonus->type = BonusType::ARTIFACT_CHARGE;
-		bonus->val = charges;
-		bonus->duration = BonusDuration::PERMANENT;
-		artInst->accumulateBonus(bonus);
+		const auto chargedBonus = artInst->getBonusesOfType(BonusType::ARTIFACT_CHARGE);
+		assert(!chargedBonus.empty());
+		chargedBonus->front()->val += charges;
+		onChargesChanged();
 	}
 }
 
@@ -158,11 +192,32 @@ uint16_t CChargedArtifactInstance::getCharges() const
 	return artInst->valOfBonuses(BonusType::ARTIFACT_CHARGE);
 }
 
+void CArtifactInstance::init()
+{
+	const auto art = artTypeID.toArtifact();
+	assert(art);
+
+	if(art->isCharged())
+	{
+		// Charged artifacts contain all bonuses inside instance bonus node
+		if(art->getDischargeCondition() == DischargeArtifactCondition::SPELLCAST)
+		{
+			for(const auto & bonus : *art->getAllBonuses(Selector::all, nullptr))
+				if(bonus->type != BonusType::SPELL)
+					accumulateBonus(bonus);
+		}
+	}
+	else
+	{
+		attachToSource(*art);
+	}
+}
+
 CArtifactInstance::CArtifactInstance(IGameInfoCallback *cb, const CArtifact * art)
 	:CArtifactInstance(cb)
 {
-	setType(art);
-	addCharges(getType()->getDefaultStartCharges());
+	artTypeID = art->getId();
+	init();
 }
 
 CArtifactInstance::CArtifactInstance(IGameInfoCallback *cb)
@@ -171,12 +226,6 @@ CArtifactInstance::CArtifactInstance(IGameInfoCallback *cb)
 {
 }
 
-void CArtifactInstance::setType(const CArtifact * art)
-{
-	artTypeID = art->getId();
-	attachToSource(*art);
-}
-
 std::string CArtifactInstance::nodeName() const
 {
 	return "Artifact instance of " + (getType() ? getType()->getJsonKey() : std::string("uninitialized")) + " type";

+ 7 - 3
lib/entities/artifact/CArtifactInstance.h

@@ -80,6 +80,7 @@ class DLL_LINKAGE CChargedArtifactInstance
 protected:
 	CChargedArtifactInstance() = default;
 public:
+	void onChargesChanged();
 	void discharge(const uint16_t charges);
 	void addCharges(const uint16_t charges);
 	uint16_t getCharges() const;
@@ -91,10 +92,11 @@ class DLL_LINKAGE CArtifactInstance final
 	ArtifactInstanceID id;
 	ArtifactID artTypeID;
 
+	void init();
+
 public:
 	CArtifactInstance(IGameInfoCallback *cb, const CArtifact * art);
 	CArtifactInstance(IGameInfoCallback *cb);
-	void setType(const CArtifact * art);
 	std::string nodeName() const override;
 	ArtifactID getTypeId() const;
 	const CArtifact * getType() const;
@@ -118,8 +120,10 @@ public:
 		h & id;
 
 		if(!h.saving && h.loadingGamestate)
-			setType(artTypeID.toArtifact());
-
+		{
+			init();
+			onChargesChanged();
+		}
 	}
 };
 

+ 12 - 4
lib/gameState/GameStatePackVisitor.cpp

@@ -926,6 +926,14 @@ void GameStatePackVisitor::visitDischargeArtifact(DischargeArtifact & pack)
 	auto artInst = gs.getArtInstance(pack.id);
 	assert(artInst);
 	artInst->discharge(pack.charges);
+	if(artInst->getType()->getRemoveOnDepletion() && artInst->getCharges() == 0 && pack.artLoc.has_value())
+	{
+		BulkEraseArtifacts ePack;
+		ePack.artHolder = pack.artLoc.value().artHolder;
+		ePack.creature = pack.artLoc.value().creature;
+		ePack.posPack.push_back(pack.artLoc.value().slot);
+		ePack.visit(*this);
+	}
 }
 
 void GameStatePackVisitor::visitAssembledArtifact(AssembledArtifact & pack)
@@ -1341,14 +1349,14 @@ void GameStatePackVisitor::visitBattleResultsApplied(BattleResultsApplied & pack
 {
 	pack.learnedSpells.visit(*this);
 
-	for(auto & movingPack : pack.movingArtifacts)
-		movingPack.visit(*this);
+	for(auto & discharging : pack.dischargingArtifacts)
+		discharging.visit(*this);
 
 	for(auto & growing : pack.growingArtifacts)
 		growing.visit(*this);
 
-	for(auto & discharging : pack.dischargingArtifacts)
-		discharging.visit(*this);
+	for(auto & movingPack : pack.movingArtifacts)
+		movingPack.visit(*this);
 
 	const auto currentBattle = std::find_if(gs.currentBattles.begin(), gs.currentBattles.end(),
 											[&](const auto & battle)

+ 8 - 0
lib/mapping/CMap.cpp

@@ -847,6 +847,14 @@ CArtifactInstance * CMap::createArtifact(const ArtifactID & artID, const SpellID
 		artInst->addNewBonus(std::make_shared<Bonus>(BonusDuration::PERMANENT, BonusType::SPELL,
 													 BonusSource::ARTIFACT_INSTANCE, -1, BonusSourceID(ArtifactID(ArtifactID::SPELL_SCROLL)), BonusSubtypeID(spellId)));
 	}
+	if(art->isCharged())
+	{
+		auto bonus = std::make_shared<Bonus>();
+		bonus->type = BonusType::ARTIFACT_CHARGE;
+		bonus->val = 0;
+		artInst->addNewBonus(bonus);
+		artInst->addCharges(art->getDefaultStartCharges());
+	}
 	return artInst;
 }
 

+ 3 - 3
lib/networkPacks/ArtifactLocation.h

@@ -81,9 +81,9 @@ struct MoveArtifactInfo
 
 enum class DischargeArtifactCondition
 {
-		SPELLCAST,
-		BATTLE,
-		BUILDING
+	SPELLCAST,
+	BATTLE,
+	BUILDING	// not implemented
 };
 
 VCMI_LIB_NAMESPACE_END

+ 2 - 0
lib/networkPacks/PacksForClient.h

@@ -1027,6 +1027,7 @@ struct DLL_LINKAGE DischargeArtifact : CArtifactOperationPack
 {
 	ArtifactInstanceID id;
 	uint16_t charges;
+	std::optional<ArtifactLocation> artLoc;
 
 	DischargeArtifact() = default;
 	DischargeArtifact(const ArtifactInstanceID & id, const uint16_t charges)
@@ -1041,6 +1042,7 @@ struct DLL_LINKAGE DischargeArtifact : CArtifactOperationPack
 	{
 		h & id;
 		h & charges;
+		h & artLoc;
 	}
 };
 

+ 10 - 6
server/battles/BattleResultProcessor.cpp

@@ -501,27 +501,31 @@ void BattleResultProcessor::battleFinalize(const BattleID & battleID, const Batt
 	}
 
 	// Charged artifacts handling
-	const auto addArtifactToDischarging = [&resultsApplied](const std::map<ArtifactPosition, ArtSlotInfo> & artMap)
+	const auto addArtifactToDischarging = [&resultsApplied](const std::map<ArtifactPosition, ArtSlotInfo> & artMap,
+			const ObjectInstanceID & id, const std::optional<SlotID> & creature = std::nullopt)
 	{
 		for(const auto & [slot, slotInfo] : artMap)
 		{
 			auto artInst = slotInfo.getArt();
 			assert(artInst);
 			if(const auto condition = artInst->getType()->getDischargeCondition(); condition && condition.value() == DischargeArtifactCondition::BATTLE)
-				resultsApplied.dischargingArtifacts.emplace_back(artInst->getId(), 1);
+			{
+				auto & discharging = resultsApplied.dischargingArtifacts.emplace_back(artInst->getId(), 1);
+				discharging.artLoc.emplace(id, creature, slot);
+			}
 		}
 	};
 	if(winnerHero)
 	{
-		addArtifactToDischarging(winnerHero->artifactsWorn);
+		addArtifactToDischarging(winnerHero->artifactsWorn, winnerHero->id);
 		if(const auto commander = winnerHero->getCommander())
-			addArtifactToDischarging(commander->artifactsWorn);
+			addArtifactToDischarging(commander->artifactsWorn, winnerHero->id, winnerHero->findStack(winnerHero->getCommander()));
 	}
 	if(loserHero)
 	{
-		addArtifactToDischarging(loserHero->artifactsWorn);
+		addArtifactToDischarging(loserHero->artifactsWorn, loserHero->id);
 		if(const auto commander = loserHero->getCommander())
-			addArtifactToDischarging(commander->artifactsWorn);
+			addArtifactToDischarging(commander->artifactsWorn, loserHero->id, loserHero->findStack(loserHero->getCommander()));
 	}
 
 	// Necromancy handling