diff --git a/skymp5-server/cpp/addon/ScampServer.cpp b/skymp5-server/cpp/addon/ScampServer.cpp index 3fd67bd007..d5e715c782 100644 --- a/skymp5-server/cpp/addon/ScampServer.cpp +++ b/skymp5-server/cpp/addon/ScampServer.cpp @@ -11,6 +11,7 @@ #include "database_drivers/DatabaseFactory.h" #include "formulas/DamageMultFormula.h" #include "formulas/SweetPieDamageFormula.h" +#include "formulas/SweetPieSpellDamageFormula.h" #include "formulas/TES5DamageFormula.h" #include "libespm/IterateFields.h" #include "papyrus-vm/Utils.h" @@ -328,6 +329,9 @@ ScampServer::ScampServer(const Napi::CallbackInfo& info) auto sweetPieDamageFormulaSettings = serverSettings["sweetPieDamageFormulaSettings"]; + auto sweetPieSpellDamageFormulaSettings = + serverSettings["sweetPieSpellDamageFormulaSettings"]; + auto damageMultFormulaSettings = serverSettings["damageMultFormulaSettings"]; @@ -337,6 +341,8 @@ ScampServer::ScampServer(const Napi::CallbackInfo& info) damageMultFormulaSettings); formula = std::make_unique( std::move(formula), sweetPieDamageFormulaSettings); + formula = std::make_unique( + std::move(formula), sweetPieSpellDamageFormulaSettings); partOne->SetDamageFormula(std::move(formula)); partOne->worldState.AttachScriptStorage( diff --git a/skymp5-server/cpp/server_guest_lib/formulas/SweetPieDamageFormula.h b/skymp5-server/cpp/server_guest_lib/formulas/SweetPieDamageFormula.h index 5760d8ec85..fe436f8e75 100644 --- a/skymp5-server/cpp/server_guest_lib/formulas/SweetPieDamageFormula.h +++ b/skymp5-server/cpp/server_guest_lib/formulas/SweetPieDamageFormula.h @@ -9,7 +9,7 @@ #include -// Modifies vanilla damage +// Modifies vanilla damage for weapons struct SweetPieDamageFormulaSettings { diff --git a/skymp5-server/cpp/server_guest_lib/formulas/SweetPieSpellDamageFormula.cpp b/skymp5-server/cpp/server_guest_lib/formulas/SweetPieSpellDamageFormula.cpp new file mode 100644 index 0000000000..3b983bd8e9 --- /dev/null +++ b/skymp5-server/cpp/server_guest_lib/formulas/SweetPieSpellDamageFormula.cpp @@ -0,0 +1,123 @@ + +#include "SweetPieSpellDamageFormula.h" + +#include "archives/JsonInputArchive.h" +#include +#include + +namespace SweetPieSpellDamageFormulaPrivate { + +bool IsPlayerBaseId(const MpActor& actor) +{ + return actor.GetBaseId() == 0x7; +} + +float SelectRespectiveMult( + const MpActor& aggressor, const MpActor& target, + const SweetPieSpellDamageFormulaSettingsEntry& entry) +{ + const bool isPlayerAggressor = IsPlayerBaseId(aggressor); + const bool isPlayerTarget = IsPlayerBaseId(target); + + if (isPlayerAggressor && isPlayerTarget) { + return entry.multPlayerHitsPlayer.has_value() ? *entry.multPlayerHitsPlayer + : 1.f; + } + + if (isPlayerAggressor && !isPlayerTarget) { + return entry.multPlayerHitsNpc.has_value() ? *entry.multPlayerHitsNpc + : 1.f; + } + + return 1.f; +} + +template +T Clamp(T value, T minValue, T maxValue) +{ + if (value < minValue) { + return minValue; + } else if (value > maxValue) { + return maxValue; + } else { + return value; + } +} +} + +SweetPieSpellDamageFormulaSettings +SweetPieSpellDamageFormulaSettings::FromJson(const nlohmann::json& j) +{ + JsonInputArchive ar(j); + SweetPieSpellDamageFormulaSettings res; + res.Serialize(ar); + return res; +} + +SweetPieSpellDamageFormula::SweetPieSpellDamageFormula( + std::unique_ptr baseFormula_, const nlohmann::json& config) + : baseFormula(std::move(baseFormula_)) + , settings(std::nullopt) +{ + if (config.is_object()) { + settings = ParseConfig(config); + } +} + +float SweetPieSpellDamageFormula::CalculateDamage(const MpActor& aggressor, + const MpActor& target, + const HitData& hitData) const +{ + return baseFormula->CalculateDamage(aggressor, target, hitData); +} + +float SweetPieSpellDamageFormula::CalculateDamage( + const MpActor& aggressor, const MpActor& target, + const SpellCastData& spellCastData) const +{ + const float baseDamage = + baseFormula->CalculateDamage(aggressor, target, spellCastData); + + if (!settings) { + return baseDamage; + } + + float biggestMult = -1; + float defaultMult = 1.f; + + for (auto& entry : settings->entries) { + const std::string& itemId = entry.itemId; + const float mult = SweetPieSpellDamageFormulaPrivate::Clamp( + SweetPieSpellDamageFormulaPrivate::SelectRespectiveMult(aggressor, + target, entry), + 0.f, std::numeric_limits::infinity()); + + uint32_t itemIdParsed = 0; + + if (itemId.find("0x") == 0 || itemId.find("0X") == 0) { + std::stringstream ss; + ss << std::hex << itemId.substr(2); // Skip "0x" + ss >> itemIdParsed; + } else { + std::stringstream ss(itemId); + ss >> itemIdParsed; + } + + if (itemIdParsed == 0) { + defaultMult = mult; + } + + if (aggressor.GetInventory().GetItemCount(itemIdParsed) > 0) { + biggestMult = std::max(biggestMult, mult); + } + } + + return biggestMult >= 0 ? biggestMult * baseDamage + : defaultMult * baseDamage; +} + +SweetPieSpellDamageFormulaSettings SweetPieSpellDamageFormula::ParseConfig( + const nlohmann::json& config) const +{ + return SweetPieSpellDamageFormulaSettings::FromJson(config); +} diff --git a/skymp5-server/cpp/server_guest_lib/formulas/SweetPieSpellDamageFormula.h b/skymp5-server/cpp/server_guest_lib/formulas/SweetPieSpellDamageFormula.h new file mode 100644 index 0000000000..f751c62226 --- /dev/null +++ b/skymp5-server/cpp/server_guest_lib/formulas/SweetPieSpellDamageFormula.h @@ -0,0 +1,64 @@ +#pragma once + +#include "IDamageFormula.h" +#include "MpActor.h" + +#include +#include +#include + +#include + +// Modifies vanilla damage for spells + +struct SweetPieSpellDamageFormulaSettingsEntry +{ + template + void Serialize(Archive& archive) + { + archive.Serialize("itemId", itemId) + .Serialize("multPlayerHitsPlayer", multPlayerHitsPlayer) + .Serialize("multPlayerHitsNpc", multPlayerHitsNpc); + } + + std::string itemId; // supports hex and decimal ids: "0x000feef1", "0" + std::optional multPlayerHitsPlayer; + std::optional multPlayerHitsNpc; +}; + +struct SweetPieSpellDamageFormulaSettings +{ + + template + void Serialize(Archive& archive) + { + archive.Serialize("entries", entries); + } + + static SweetPieSpellDamageFormulaSettings FromJson(const nlohmann::json& j); + + std::vector entries; +}; + +class SweetPieSpellDamageFormula : public IDamageFormula +{ +public: + SweetPieSpellDamageFormula(std::unique_ptr baseFormula_, + const nlohmann::json& config); + + [[nodiscard]] float CalculateDamage(const MpActor& aggressor, + const MpActor& target, + const HitData& hitData) const override; + + [[nodiscard]] float CalculateDamage( + const MpActor& aggressor, const MpActor& target, + const SpellCastData& spellCastData) const override; + +private: + SweetPieSpellDamageFormulaSettings ParseConfig( + const nlohmann::json& config) const; + +private: + std::unique_ptr baseFormula; + std::optional settings; +};