Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add SweetPieSpellDamageFormula for spell damage modification #2247

Merged
merged 10 commits into from
Dec 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions skymp5-server/cpp/addon/ScampServer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -328,6 +329,9 @@ ScampServer::ScampServer(const Napi::CallbackInfo& info)
auto sweetPieDamageFormulaSettings =
serverSettings["sweetPieDamageFormulaSettings"];

auto sweetPieSpellDamageFormulaSettings =
serverSettings["sweetPieSpellDamageFormulaSettings"];

auto damageMultFormulaSettings =
serverSettings["damageMultFormulaSettings"];

Expand All @@ -337,6 +341,8 @@ ScampServer::ScampServer(const Napi::CallbackInfo& info)
damageMultFormulaSettings);
formula = std::make_unique<SweetPieDamageFormula>(
std::move(formula), sweetPieDamageFormulaSettings);
formula = std::make_unique<SweetPieSpellDamageFormula>(
std::move(formula), sweetPieSpellDamageFormulaSettings);
partOne->SetDamageFormula(std::move(formula));

partOne->worldState.AttachScriptStorage(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

#include <nlohmann/json_fwd.hpp>

// Modifies vanilla damage
// Modifies vanilla damage for weapons

struct SweetPieDamageFormulaSettings
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@

#include "SweetPieSpellDamageFormula.h"

#include "archives/JsonInputArchive.h"
#include <limits>
#include <sstream>

namespace SweetPieSpellDamageFormulaPrivate {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anonymous namespace?


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 <class T>
T Clamp(T value, T minValue, T maxValue)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This implements the same value clamping logic as the existing CropValue function. Consider either using that function (if float is sufficient) or making it generic.

{
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<IDamageFormula> 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<float>::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);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#pragma once

#include "IDamageFormula.h"
#include "MpActor.h"

#include <memory>
#include <optional>
#include <vector>

#include <nlohmann/json_fwd.hpp>

// Modifies vanilla damage for spells

struct SweetPieSpellDamageFormulaSettingsEntry
{
template <class Archive>
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<float> multPlayerHitsPlayer;
std::optional<float> multPlayerHitsNpc;
};

struct SweetPieSpellDamageFormulaSettings
{

template <class Archive>
void Serialize(Archive& archive)
{
archive.Serialize("entries", entries);
}

static SweetPieSpellDamageFormulaSettings FromJson(const nlohmann::json& j);

std::vector<SweetPieSpellDamageFormulaSettingsEntry> entries;
};

class SweetPieSpellDamageFormula : public IDamageFormula
{
public:
SweetPieSpellDamageFormula(std::unique_ptr<IDamageFormula> 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<IDamageFormula> baseFormula;
std::optional<SweetPieSpellDamageFormulaSettings> settings;
};
Loading