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(skymp5-server): prevent blocking marksman attacks #1740

Merged
merged 11 commits into from
Nov 15, 2023
Next Next commit
feat(skymp5-server): implement CureDiseases MGEF archetype
  • Loading branch information
Pospelove committed Nov 12, 2023
commit baabea1f0bd59bcb6c7214f3cc3be673b522eb55
1 change: 1 addition & 0 deletions libespm/include/libespm/Records.h
Original file line number Diff line number Diff line change
@@ -27,3 +27,4 @@
#include "TREE.h"
#include "WEAP.h"
#include "WRLD.h"
#include "SPEL.h"
37 changes: 37 additions & 0 deletions libespm/include/libespm/SPEL.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#pragma once
#include "RecordHeader.h"

#pragma pack(push, 1)

namespace espm {

class SPEL final : public RecordHeader
{
public:
static constexpr auto kType = "SPEL";

enum class SpellType
{
Spell = 0x00,
Disease = 0x01,
Power = 0x02,
LesserPower = 0x03,
Ability = 0x04,
Poison = 0x05,
Addiction = 0x0A,
Voice = 0x0B
};

struct Data
{
SpellType type = SpellType::Spell;
};

Data GetData(CompressedFieldsCache& compressedFieldsCache) const noexcept;
};

static_assert(sizeof(SPEL) == sizeof(RecordHeader));

}

#pragma pack(pop)
22 changes: 22 additions & 0 deletions libespm/src/SPEL.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#include "libespm/SPEL.h"
#include "libespm/RecordHeaderAccess.h"
#include <cstring>

namespace espm {

SPEL::Data SPEL::GetData(
CompressedFieldsCache& compressedFieldsCache) const noexcept
{
Data result;
RecordHeaderAccess::IterateFields(
this,
[&](const char* type, uint32_t dataSize, const char* data) {
if (!std::memcmp(type, "SPIT", 4)) {
result.type = *reinterpret_cast<const SpellType*>(data + 8);
}
},
compressedFieldsCache);
return result;
}

}
40 changes: 39 additions & 1 deletion skymp5-server/cpp/server_guest_lib/MpActor.cpp
Original file line number Diff line number Diff line change
@@ -27,6 +27,9 @@
#include "TeleportMessage.h"
#include "UpdateEquipmentMessage.h"

#include "SpSnippet.h"
#include "SpSnippetFunctionGen.h"

struct MpActor::Impl
{
std::map<uint32_t, Viet::Promise<VarValue>> snippetPromises;
@@ -148,18 +151,39 @@ void MpActor::EquipBestWeapon()
}
}

void MpActor::AddSpell(const uint32_t spellId)
void MpActor::AddSpell(const uint32_t spellId, const bool verbose)
{
EditChangeForm([&](MpChangeForm& changeForm) {
changeForm.learnedSpells.LearnSpell(spellId);
});

auto spell = GetParent()->GetEspm().GetBrowser().LookupById(spellId);

std::vector<VarValue> arguments(2);
arguments[0] = VarValue(std::make_shared<EspmGameObject>(spell));
arguments[1] = VarValue(verbose);

SpSnippet spSnippet(
"Actor", "AddSpell",
SpSnippetFunctionGen::SerializeArguments(arguments).data(), GetFormId());
spSnippet.Execute(this);
}

void MpActor::RemoveSpell(const uint32_t spellId)
{
EditChangeForm([&](MpChangeForm& changeForm) {
changeForm.learnedSpells.ForgetSpell(spellId);
});

auto spell = GetParent()->GetEspm().GetBrowser().LookupById(spellId);

std::vector<VarValue> arguments(1);
arguments[0] = VarValue(std::make_shared<EspmGameObject>(spell));

SpSnippet spSnippet(
"Actor", "RemoveSpell",
SpSnippetFunctionGen::SerializeArguments(arguments).data(), GetFormId());
spSnippet.Execute(this);
}

void MpActor::SetRaceMenuOpen(bool isOpen)
@@ -1028,6 +1052,20 @@ void MpActor::ApplyMagicEffect(espm::Effects::Effect& effect, bool hasSweetpie,
{
WorldState* worldState = GetParent();
auto data = espm::GetData<espm::MGEF>(effect.effectId, worldState).data;

if (data.effectType == espm::MGEF::EffectType::CureDisease) {
spdlog::trace("Curing all diseases");
auto spells = ChangeForm().learnedSpells.GetLearnedSpells();
for (auto spellId : spells) {
auto spellData = espm::GetData<espm::SPEL>(spellId);
if (spellData.type == espm::SPEL::SpellType::Disease) {
spdlog::trace("Curing disease {:x}", spellId);
RemoveSpell(spellId);
}
}
return;
}

const espm::ActorValue av = data.primaryAV;
const espm::MGEF::EffectType type = data.effectType;
spdlog::trace("Actor value in ApplyMagicEffect(): {}",
2 changes: 1 addition & 1 deletion skymp5-server/cpp/server_guest_lib/MpActor.h
Original file line number Diff line number Diff line change
@@ -134,7 +134,7 @@ class MpActor : public MpObjectReference
bool MpApiCraft(uint32_t craftedItemBaseId, uint32_t count,
uint32_t recipeId);

void AddSpell(uint32_t spellId);
void AddSpell(uint32_t spellId, bool verbose);
void RemoveSpell(uint32_t spellId);

private:
16 changes: 3 additions & 13 deletions skymp5-server/cpp/server_guest_lib/script_classes/PapyrusActor.cpp
Original file line number Diff line number Diff line change
@@ -267,6 +267,8 @@ VarValue PapyrusActor::AddSpell(VarValue self,
"Actor.AddSpell requires at least two arguments");
}

const bool verbose = static_cast<bool>(arguments[1]);

const auto& spell = GetRecordPtr(arguments[0]);
if (!spell.rec) {
spdlog::error("Actor.AddSpell - invalid spell form");
@@ -282,13 +284,7 @@ VarValue PapyrusActor::AddSpell(VarValue self,
uint32_t spellId = spell.ToGlobalId(spell.rec->GetId());

if (!actor->IsSpellLearned(spellId)) {
actor->AddSpell(spellId);

SpSnippet(GetName(), "AddSpell",
SpSnippetFunctionGen::SerializeArguments(arguments).data(),
actor->GetFormId())
.Execute(actor);

actor->AddSpell(spellId, verbose);
return VarValue(true);
}
}
@@ -327,12 +323,6 @@ VarValue PapyrusActor::RemoveSpell(VarValue self,
spdlog::warn("Actor.RemoveSpell - spell already removed/not learned");
} else {
actor->RemoveSpell(spellId);

SpSnippet(GetName(), "RemoveSpell",
SpSnippetFunctionGen::SerializeArguments(arguments).data(),
actor->GetFormId())
.Execute(actor);

return VarValue(true);
}
}