Skip to content

Commit

Permalink
feat(skymp5-server): add onReadBook event (#2212)
Browse files Browse the repository at this point in the history
  • Loading branch information
Pospelove authored Nov 10, 2024
1 parent 3fc4084 commit 2f7fbb5
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 31 deletions.
35 changes: 4 additions & 31 deletions skymp5-server/cpp/server_guest_lib/MpActor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
#include "gamemode_events/DeathEvent.h"
#include "gamemode_events/DropItemEvent.h"
#include "gamemode_events/EatItemEvent.h"
#include "gamemode_events/ReadBookEvent.h"
#include "gamemode_events/RespawnEvent.h"
#include "libespm/espm.h"
#include "papyrus-vm/Utils.h"
Expand Down Expand Up @@ -866,37 +867,9 @@ void MpActor::EatItem(uint32_t baseId, espm::Type t)

bool MpActor::ReadBook(const uint32_t baseId)
{
auto& loader = GetParent()->GetEspm();
auto bookLookupResult = loader.GetBrowser().LookupById(baseId);

if (!bookLookupResult.rec) {
spdlog::error("ReadBook {:x} - No book form {:x}", GetFormId(), baseId);
return false;
}

const auto bookData = espm::GetData<espm::BOOK>(baseId, GetParent());
const auto spellOrSkillFormId =
bookLookupResult.ToGlobalId(bookData.spellOrSkillFormId);

if (bookData.IsFlagSet(espm::BOOK::Flags::TeachesSpell)) {
if (ChangeForm().learnedSpells.IsSpellLearned(spellOrSkillFormId)) {
spdlog::info(
"ReadBook {:x} - Spell already learned {:x}, not spending the book",
GetFormId(), spellOrSkillFormId);
return false;
}

EditChangeForm([&](MpChangeForm& changeForm) {
changeForm.learnedSpells.LearnSpell(spellOrSkillFormId);
});
return true;
} else if (bookData.IsFlagSet(espm::BOOK::Flags::TeachesSkill)) {
spdlog::info("ReadBook {:x} - Skill book {:x} detected, not implemented",
GetFormId(), baseId);
return false;
}

return false;
ReadBookEvent readBookEvent(this, baseId);
readBookEvent.Fire(GetParent());
return readBookEvent.SpellLearned();
}

void MpActor::EnsureTemplateChainEvaluated(espm::Loader& loader,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#include "ReadBookEvent.h"

#include "MpActor.h"
#include "WorldState.h"
#include <string>
#include <unordered_set>
#include <vector>

ReadBookEvent::ReadBookEvent(MpActor* actor_, uint32_t baseId_)
: actor(actor_)
, baseId(baseId_)
{
}

const char* ReadBookEvent::GetName() const
{
return "onReadBook";
}

std::string ReadBookEvent::GetArgumentsJsonArray() const
{
std::string result;
result += "[";
result += std::to_string(actor->GetFormId());
result += ",";
result += std::to_string(baseId);
result += "]";
return result;
}

bool ReadBookEvent::SpellLearned() const
{
return spellLearned;
}

void ReadBookEvent::OnFireSuccess(WorldState* worldState)
{
auto& loader = actor->GetParent()->GetEspm();
auto bookLookupResult = loader.GetBrowser().LookupById(baseId);

if (!bookLookupResult.rec) {
spdlog::error("ReadBookEvent::OnFireSuccess - Actor {:x} reading book: No "
"book form {:x}",
actor->GetFormId(), baseId);
return;
}

const auto bookData = espm::GetData<espm::BOOK>(baseId, actor->GetParent());
const auto spellOrSkillFormId =
bookLookupResult.ToGlobalId(bookData.spellOrSkillFormId);

if (bookData.IsFlagSet(espm::BOOK::Flags::TeachesSpell)) {
if (actor->IsSpellLearned(spellOrSkillFormId)) {
spdlog::info("ReadBookEvent::OnFireSuccess - Actor {:x} reading book: "
"Spell already learned "
"{:x}, not spending the book",
actor->GetFormId(), spellOrSkillFormId);
return;
}
actor->AddSpell(spellOrSkillFormId);
spellLearned = true;
return;
} else if (bookData.IsFlagSet(espm::BOOK::Flags::TeachesSkill)) {
spdlog::info(
"ReadBookEvent::OnFireSuccess - Actor {:x} reading skill book {:x} "
"detected, not implemented",
actor->GetFormId(), baseId);
return;
}
}
29 changes: 29 additions & 0 deletions skymp5-server/cpp/server_guest_lib/gamemode_events/ReadBookEvent.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#pragma once
#include "GameModeEvent.h"

class MpActor;

class ReadBookEvent : public GameModeEvent
{
public:
ReadBookEvent(MpActor* actor_, uint32_t baseId_);

const char* GetName() const override;

std::string GetArgumentsJsonArray() const override;

bool SpellLearned() const;

private:
void OnFireSuccess(WorldState* worldState) override;

// event arguments
MpActor* actor = nullptr;
uint32_t baseId = 0;

// OnFireSuccess-specific arguments
// ...

// Results
bool spellLearned = false;
};

0 comments on commit 2f7fbb5

Please sign in to comment.