From 2f7fbb55b3006544f0664285c655ab7424b1f2f0 Mon Sep 17 00:00:00 2001 From: Leonid Pospelov Date: Mon, 11 Nov 2024 01:17:01 +0500 Subject: [PATCH] feat(skymp5-server): add onReadBook event (#2212) --- .../cpp/server_guest_lib/MpActor.cpp | 35 ++-------- .../gamemode_events/ReadBookEvent.cpp | 70 +++++++++++++++++++ .../gamemode_events/ReadBookEvent.h | 29 ++++++++ 3 files changed, 103 insertions(+), 31 deletions(-) create mode 100644 skymp5-server/cpp/server_guest_lib/gamemode_events/ReadBookEvent.cpp create mode 100644 skymp5-server/cpp/server_guest_lib/gamemode_events/ReadBookEvent.h diff --git a/skymp5-server/cpp/server_guest_lib/MpActor.cpp b/skymp5-server/cpp/server_guest_lib/MpActor.cpp index c5bcebb5c7..3de00e8bfe 100644 --- a/skymp5-server/cpp/server_guest_lib/MpActor.cpp +++ b/skymp5-server/cpp/server_guest_lib/MpActor.cpp @@ -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" @@ -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(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, diff --git a/skymp5-server/cpp/server_guest_lib/gamemode_events/ReadBookEvent.cpp b/skymp5-server/cpp/server_guest_lib/gamemode_events/ReadBookEvent.cpp new file mode 100644 index 0000000000..298791221e --- /dev/null +++ b/skymp5-server/cpp/server_guest_lib/gamemode_events/ReadBookEvent.cpp @@ -0,0 +1,70 @@ +#include "ReadBookEvent.h" + +#include "MpActor.h" +#include "WorldState.h" +#include +#include +#include + +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(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; + } +} diff --git a/skymp5-server/cpp/server_guest_lib/gamemode_events/ReadBookEvent.h b/skymp5-server/cpp/server_guest_lib/gamemode_events/ReadBookEvent.h new file mode 100644 index 0000000000..d7e92f9734 --- /dev/null +++ b/skymp5-server/cpp/server_guest_lib/gamemode_events/ReadBookEvent.h @@ -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; +};