From 051381d4db45d6b60e32c1dc23f0d8cdc20078a4 Mon Sep 17 00:00:00 2001 From: SoundSSGood <87084363+SoundSSGood@users.noreply.github.com> Date: Fri, 16 May 2025 22:01:07 +0200 Subject: [PATCH] Docs --- config/schemas/artifact.json | 2 +- .../Entities_Format/Artifact_Format.md | 13 +++++++ lib/entities/artifact/CArtHandler.cpp | 33 +++++++++-------- lib/entities/artifact/CArtHandler.h | 2 +- lib/entities/artifact/CArtifact.cpp | 4 +-- lib/entities/artifact/CArtifact.h | 2 +- lib/entities/artifact/CArtifactInstance.cpp | 4 +-- server/CGameHandler.cpp | 35 +++++++++++++++++++ server/CGameHandler.h | 1 + server/battles/BattleActionProcessor.cpp | 1 + 10 files changed, 75 insertions(+), 22 deletions(-) diff --git a/config/schemas/artifact.json b/config/schemas/artifact.json index 2a7f3a600..cfd8f26fb 100644 --- a/config/schemas/artifact.json +++ b/config/schemas/artifact.json @@ -144,7 +144,7 @@ }, "val" : { "type" : "number", - "description" : "Default number of charges" + "description" : "Default starting charge amount" } } } diff --git a/docs/modders/Entities_Format/Artifact_Format.md b/docs/modders/Entities_Format/Artifact_Format.md index 23ab28e8c..5ac89571a 100644 --- a/docs/modders/Entities_Format/Artifact_Format.md +++ b/docs/modders/Entities_Format/Artifact_Format.md @@ -87,5 +87,18 @@ In order to make functional artifact you also need: "bonusesPerLevel" : {}, "thresholdBonuses" : {}, } + + // Optional, used for artifacts with charges. + "charged" : { + // Artifact discharging action + // SPELLCAST - Consumes a charge for each spellcast. Applies to every spell added through the "bonuses" section. + // BATTLE - Consumes one charge per battle. + // BUILDING (not implemented) + "usageType": "BATTLE", + // Optional, by default is false. Remove when fully discharged + "removeOnDepletion" : true, + // Optional, by default is 0. Default starting charge amount. + "val" : 2, + } } ``` diff --git a/lib/entities/artifact/CArtHandler.cpp b/lib/entities/artifact/CArtHandler.cpp index 8880503ae..e0837ec3f 100644 --- a/lib/entities/artifact/CArtHandler.cpp +++ b/lib/entities/artifact/CArtHandler.cpp @@ -152,20 +152,6 @@ std::shared_ptr CArtHandler::loadFromJson(const std::string & scope, JsonUtils::parseBonus(bonus["bonus"], &art->thresholdBonuses.back().second); } } - if(!node["charged"].isNull()) - { - art->setCondition(stringToDischargeCond(node["charged"]["usageType"].String())); - if(!node["charged"]["removeOnDepletion"].isNull()) - art->setRemoveOnDepletion(node["charged"]["removeOnDepletion"].Bool()); - if(!node["charged"]["val"].isNull()) - { - const auto charges = node["charged"]["val"].Integer(); - if(charges < 0) - logMod->warn("Warning! Charged artifact number of charges cannot be less than zero %d!", charges); - else - art->setDefaultStartCharges(charges); - } - } art->id = ArtifactID(index); art->identifier = identifier; @@ -235,6 +221,23 @@ std::shared_ptr CArtHandler::loadFromJson(const std::string & scope, if(art->isTradable()) art->possibleSlots.at(ArtBearer::ALTAR).push_back(ArtifactPosition::ALTAR); + if(!node["charged"].isNull()) + { + art->setCondition(stringToDischargeCond(node["charged"]["usageType"].String())); + if(!node["charged"]["removeOnDepletion"].isNull()) + art->setRemoveOnDepletion(node["charged"]["removeOnDepletion"].Bool()); + if(!node["charged"]["val"].isNull()) + { + const auto charges = node["charged"]["val"].Integer(); + if(charges < 0) + logMod->warn("Warning! Charged artifact %s number of charges cannot be less than zero %d!", art->getNameTranslated(), charges); + else + art->setDefaultStartCharges(charges); + } + if(art->getDischargeCondition() == DischargeArtifactCondition::SPELLCAST && art->getBonusesOfType(BonusType::SPELL)->size() == 0) + logMod->warn("Warning! %s condition of discharge is \"SPELLCAST\", but there is not a single spell.", art->getNameTranslated()); + } + return art; } @@ -326,7 +329,7 @@ EArtifactClass CArtHandler::stringToClass(const std::string & className) return EArtifactClass::ART_SPECIAL; } -DischargeArtifactCondition CArtHandler::stringToDischargeCond(const std::string & cond) +DischargeArtifactCondition CArtHandler::stringToDischargeCond(const std::string & cond) const { const std::unordered_map growingConditionsMap = { diff --git a/lib/entities/artifact/CArtHandler.h b/lib/entities/artifact/CArtHandler.h index b633826c3..0689a3483 100644 --- a/lib/entities/artifact/CArtHandler.h +++ b/lib/entities/artifact/CArtHandler.h @@ -24,7 +24,7 @@ public: void addBonuses(CArtifact * art, const JsonNode & bonusList); static EArtifactClass stringToClass(const std::string & className); //TODO: rework EartClass to make this a constructor - DischargeArtifactCondition stringToDischargeCond(const std::string & cond); + DischargeArtifactCondition stringToDischargeCond(const std::string & cond) const; bool legalArtifact(const ArtifactID & id) const; static void makeItCreatureArt(CArtifact * a, bool onlyCreature = true); diff --git a/lib/entities/artifact/CArtifact.cpp b/lib/entities/artifact/CArtifact.cpp index 37379c0d4..67cde1238 100644 --- a/lib/entities/artifact/CArtifact.cpp +++ b/lib/entities/artifact/CArtifact.cpp @@ -257,9 +257,9 @@ bool CChargedArtifact::isCharged() const return condition.has_value(); } -void CChargedArtifact::setCondition(const DischargeArtifactCondition & condition) +void CChargedArtifact::setCondition(const DischargeArtifactCondition & dischargeCondition) { - this->condition = condition; + condition = dischargeCondition; } void CChargedArtifact::setRemoveOnDepletion(const bool remove) diff --git a/lib/entities/artifact/CArtifact.h b/lib/entities/artifact/CArtifact.h index 8e9a1df60..7754cf9aa 100644 --- a/lib/entities/artifact/CArtifact.h +++ b/lib/entities/artifact/CArtifact.h @@ -79,7 +79,7 @@ protected: public: bool isCharged() const; - void setCondition(const DischargeArtifactCondition & condition); + void setCondition(const DischargeArtifactCondition & dischargeCondition); void setRemoveOnDepletion(const bool remove); void setDefaultStartCharges(const uint16_t charges); uint16_t getDefaultStartCharges() const; diff --git a/lib/entities/artifact/CArtifactInstance.cpp b/lib/entities/artifact/CArtifactInstance.cpp index 99ac38d14..2177ae693 100644 --- a/lib/entities/artifact/CArtifactInstance.cpp +++ b/lib/entities/artifact/CArtifactInstance.cpp @@ -101,7 +101,7 @@ void CGrowingArtifactInstance::growingUp() if(artType->isGrowing()) { const auto growingBonus = artInst->getBonusesOfType(BonusType::ARTIFACT_GROWING); - assert(!growingBonus.empty()); + assert(!growingBonus->empty()); growingBonus->front()->val++; for(const auto & bonus : artType->getBonusesPerLevel()) @@ -179,7 +179,7 @@ void CChargedArtifactInstance::addCharges(const uint16_t charges) if(artInst->getType()->isCharged()) { const auto chargedBonus = artInst->getBonusesOfType(BonusType::ARTIFACT_CHARGE); - assert(!chargedBonus.empty()); + assert(!chargedBonus->empty()); chargedBonus->front()->val += charges; onChargesChanged(); } diff --git a/server/CGameHandler.cpp b/server/CGameHandler.cpp index 38f0c0122..ed2e16834 100644 --- a/server/CGameHandler.cpp +++ b/server/CGameHandler.cpp @@ -3931,6 +3931,9 @@ void CGameHandler::castSpell(const spells::Caster * caster, SpellID spellID, con const CSpell * s = spellID.toSpell(); s->adventureCast(spellEnv, p); + + if(const auto hero = caster->getHeroCaster()) + verifyChargedArtifactUsed(hero->id, spellID); } bool CGameHandler::swapStacks(const StackLocation & sl1, const StackLocation & sl2) @@ -4337,3 +4340,35 @@ void CGameHandler::startBattle(const CArmedInstance *army1, const CArmedInstance { battles->startBattle(army1, army2); } + +void CGameHandler::verifyChargedArtifactUsed(const ObjectInstanceID & heroObjectID, const SpellID & spellID) +{ + if(const auto hero = getHero(heroObjectID)) + { + assert(hero->canCastThisSpell(spellID.toSpell())); + + if(vstd::contains(hero->getSpellsInSpellbook(), spellID)) + return; + + std::vector> chargedArts; + for(const auto & [slot, slotInfo] : hero->artifactsWorn) + { + const auto artInst = slotInfo.getArt(); + const auto artType = artInst->getType(); + if(artType->getDischargeCondition() == DischargeArtifactCondition::SPELLCAST) + { + chargedArts.emplace_back(slot, artInst->getId()); + } + else + { + if(const auto bonuses = artInst->getBonusesOfType(BonusType::SPELL, spellID); !bonuses->empty()) + return; + } + } + + assert(!chargedArts.empty()); + DischargeArtifact msg(chargedArts.front().second, 1); + msg.artLoc.emplace(hero->id, chargedArts.front().first); + sendAndApply(msg); + } +} diff --git a/server/CGameHandler.h b/server/CGameHandler.h index fb720b209..a808fe8f9 100644 --- a/server/CGameHandler.h +++ b/server/CGameHandler.h @@ -169,6 +169,7 @@ public: void changeFogOfWar(const std::unordered_set &tiles, PlayerColor player,ETileVisibility mode) override; void castSpell(const spells::Caster * caster, SpellID spellID, const int3 &pos) override; + void verifyChargedArtifactUsed(const ObjectInstanceID & heroObjectID, const SpellID & spellID); /// Returns hero that is currently visiting this object, or nullptr if no visit is active const CGHeroInstance * getVisitingHero(const CGObjectInstance *obj); diff --git a/server/battles/BattleActionProcessor.cpp b/server/battles/BattleActionProcessor.cpp index d42a4654b..a9170949a 100644 --- a/server/battles/BattleActionProcessor.cpp +++ b/server/battles/BattleActionProcessor.cpp @@ -124,6 +124,7 @@ bool BattleActionProcessor::doHeroSpellAction(const CBattleInfoCallback & battle } parameters.cast(gameHandler->spellEnv, ba.getTarget(&battle)); + gameHandler->verifyChargedArtifactUsed(h->id, ba.spell); return true; }