Skip to content

Commit

Permalink
fix: ward spell not disappearing
Browse files Browse the repository at this point in the history
  • Loading branch information
RobbeBryssinck committed May 24, 2022
1 parent cb248b3 commit ef86bde
Show file tree
Hide file tree
Showing 12 changed files with 103 additions and 13 deletions.
1 change: 1 addition & 0 deletions Code/client/Games/Overrides.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ thread_local uint32_t ScopedActivateOverride::s_refCount = 0;
thread_local uint32_t ScopedInventoryOverride::s_refCount = 0;
thread_local uint32_t ScopedExtraDataOverride::s_refCount = 0;
thread_local uint32_t ScopedQuestOverride::s_refCount = 0;
thread_local uint32_t ScopedSpellCastOverride::s_refCount = 0;
2 changes: 2 additions & 0 deletions Code/client/Games/Overrides.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ namespace details
struct Inventory {};
struct ExtraData {};
struct Quest {};
struct SpellCast {};
}

using ScopedEquipOverride = ScopedOverride<details::Equip>;
Expand All @@ -45,3 +46,4 @@ using ScopedActivateOverride = ScopedOverride<details::Activate>;
using ScopedInventoryOverride = ScopedOverride<details::Inventory>;
using ScopedExtraDataOverride = ScopedOverride<details::ExtraData>;
using ScopedQuestOverride = ScopedOverride<details::Quest>;
using ScopedSpellCastOverride = ScopedOverride<details::SpellCast>;
15 changes: 15 additions & 0 deletions Code/client/Games/Skyrim/Forms/MagicItem.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#include "MagicItem.h"

bool MagicItem::IsWardSpell() noexcept
{
BGSKeyword* pMagicWard = Cast<BGSKeyword>(TESForm::GetById(0x1ea69));
return keyword.Contains(pMagicWard);
}

bool MagicItem::IsInvisibilitySpell() noexcept
{
BGSKeyword* pMagicInvisibility = Cast<BGSKeyword>(TESForm::GetById(0x1ea6f));
bool result = keyword.Contains(pMagicInvisibility);
spdlog::warn("IsInvisib {}", result);
return result;
}
5 changes: 4 additions & 1 deletion Code/client/Games/Skyrim/Forms/MagicItem.h
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
#pragma once


#include <Forms/TESBoundObject.h>
#include <Forms/EffectSetting.h>
#include <Forms/BGSKeyword.h>
#include <Magic/EffectItem.h>
#include <Components/BGSKeywordForm.h>
#include <Components/TESFullName.h>

struct MagicItem : TESBoundObject
{
bool IsWardSpell() noexcept;
bool IsInvisibilitySpell() noexcept;

TESFullName fullName;
BGSKeywordForm keyword;
GameArray<EffectItem*> listOfEffects;
Expand Down
4 changes: 2 additions & 2 deletions Code/client/Games/Skyrim/Magic/ActorMagicCaster.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ static TInterruptCast* RealInterruptCast = nullptr;

void TP_MAKE_THISCALL(HookSpellCast, ActorMagicCaster, bool abSuccess, int32_t auiTargetCount, MagicItem* apSpell)
{
spdlog::info("HookSpellCast, abSuccess: {}, auiTargetCount: {}, apSpell: {:X}", abSuccess, auiTargetCount, (uint64_t)apSpell);

// TODO(cosideci): why is this here?
if (!abSuccess)
return;
Expand All @@ -39,8 +41,6 @@ void TP_MAKE_THISCALL(HookSpellCast, ActorMagicCaster, bool abSuccess, int32_t a
if (apSpell)
World::Get().GetRunner().Trigger(SpellCastEvent(apThis, apSpell->formID, targetFormId));

spdlog::debug("HookSpellCast, abSuccess: {}, auiTargetCount: {}, apSpell: {:X}, desired target: {:X}", abSuccess, auiTargetCount, (uint64_t)apSpell, targetFormId);

ThisCall(RealSpellCast, apThis, abSuccess, auiTargetCount, apSpell);
}

Expand Down
5 changes: 5 additions & 0 deletions Code/client/Games/Skyrim/Magic/EffectItem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ bool EffectItem::IsSlowEffect() const noexcept
return pEffectSetting->eArchetype == EffectArchetypes::ArchetypeID::kSlowTime;
}

bool EffectItem::IsInivisibilityEffect() const noexcept
{
return pEffectSetting->eArchetype == EffectArchetypes::ArchetypeID::kInvisibility;
}

bool EffectItem::IsWerewolfEffect() const noexcept
{
return pEffectSetting->eArchetype == EffectArchetypes::ArchetypeID::kWerewolf;
Expand Down
1 change: 1 addition & 0 deletions Code/client/Games/Skyrim/Magic/EffectItem.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ struct EffectItem
bool IsHealingEffect() const noexcept;
bool IsSummonEffect() const noexcept;
bool IsSlowEffect() const noexcept;
bool IsInivisibilityEffect() const noexcept;
bool IsWerewolfEffect() const noexcept;
bool IsVampireLordEffect() const noexcept;

Expand Down
21 changes: 18 additions & 3 deletions Code/client/Games/Skyrim/Magic/MagicTarget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@

#include <Events/AddTargetEvent.h>

#include <Games/Overrides.h>

TP_THIS_FUNCTION(TAddTarget, bool, MagicTarget, MagicTarget::AddTargetData& arData);
TP_THIS_FUNCTION(TCheckAddEffectTargetData, bool, MagicTarget::AddTargetData, void* arArgs, float afResistance);
TP_THIS_FUNCTION(TFindTargets, bool, MagicCaster, float afEffectivenessMult, int32_t* aruiTargetCount, TESBoundObject* apSource, char abLoadCast, char abAdjust);
Expand All @@ -29,9 +31,18 @@ bool MagicTarget::AddTarget(AddTargetData& arData) noexcept
return result;
}

bool MagicTarget::AddTargetData::ShouldSync()
{
return !pEffectItem->IsSummonEffect() &&
!pSpell->IsInvisibilitySpell() &&
!pSpell->IsWardSpell();
}

// If you want a detailed flowchart of what's happening here, ask cosi
bool TP_MAKE_THISCALL(HookAddTarget, MagicTarget, MagicTarget::AddTargetData& arData)
{
spdlog::info("HookAddTarget");

// TODO: this can be fixed by properly implementing multiple inheritance
Actor* pTargetActor = (Actor*)((uint8_t*)apThis - 0x98);
ActorExtension* pTargetActorEx = pTargetActor->GetExtension();
Expand All @@ -45,6 +56,10 @@ bool TP_MAKE_THISCALL(HookAddTarget, MagicTarget, MagicTarget::AddTargetData& ar
if (arData.pEffectItem->IsVampireLordEffect())
pTargetActorEx->GraphDescriptorHash = AnimationGraphDescriptor_VampireLordBehavior::m_key;

// TODO(cosideci): maybe call MagicTarget::AddTarget()?
if (ScopedSpellCastOverride::IsOverriden())
return ThisCall(RealAddTarget, apThis, arData);

AddTargetEvent addTargetEvent{};
addTargetEvent.TargetID = pTargetActor->formID;
addTargetEvent.SpellID = arData.pSpell->formID;
Expand Down Expand Up @@ -79,7 +94,7 @@ bool TP_MAKE_THISCALL(HookAddTarget, MagicTarget, MagicTarget::AddTargetData& ar
}

bool result = ThisCall(RealAddTarget, apThis, arData);
if (result && !arData.pEffectItem->IsSummonEffect())
if (result && arData.ShouldSync())
World::Get().GetRunner().Trigger(addTargetEvent);
return result;
}
Expand All @@ -90,7 +105,7 @@ bool TP_MAKE_THISCALL(HookAddTarget, MagicTarget, MagicTarget::AddTargetData& ar
if (pCasterExtension->IsLocalPlayer())
{
bool result = ThisCall(RealAddTarget, apThis, arData);
if (result && !arData.pEffectItem->IsSummonEffect())
if (result && arData.ShouldSync())
World::Get().GetRunner().Trigger(addTargetEvent);
return result;
}
Expand All @@ -103,7 +118,7 @@ bool TP_MAKE_THISCALL(HookAddTarget, MagicTarget, MagicTarget::AddTargetData& ar
if (pTargetActorEx->IsLocal())
{
bool result = ThisCall(RealAddTarget, apThis, arData);
if (result && !arData.pEffectItem->IsSummonEffect())
if (result && arData.ShouldSync())
World::Get().GetRunner().Trigger(addTargetEvent);
return result;
}
Expand Down
2 changes: 2 additions & 0 deletions Code/client/Games/Skyrim/Magic/MagicTarget.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ struct MagicTarget
{
bool CheckAddEffect(void* arArgs, float afResistance);

bool ShouldSync();

Actor* pCaster;
MagicItem* pSpell;
EffectItem* pEffectItem;
Expand Down
13 changes: 13 additions & 0 deletions Code/client/Services/Debug/DebugService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
#include <Interface/IMenu.h>

#include <Games/Misc/SubtitleManager.h>
#include <Games/Overrides.h>

#if TP_SKYRIM64
#include <EquipManager.h>
Expand Down Expand Up @@ -81,6 +82,8 @@ void __declspec(noinline) DebugService::PlaceActorInWorld() noexcept
Inventory inventory = PlayerCharacter::Get()->GetActorInventory();
pActor->SetActorInventory(inventory);

pActor->GetExtension()->SetPlayer(true);

m_actors.emplace_back(pActor);
}

Expand Down Expand Up @@ -161,6 +164,16 @@ void DebugService::OnUpdate(const UpdateEvent& acUpdateEvent) noexcept
if (!s_f8Pressed)
{
s_f8Pressed = true;
if (m_actors.empty())
PlaceActorInWorld();
else
{
ScopedSpellCastOverride _;
auto pActor = m_actors[0];
pActor->GenerateMagicCasters();
pActor->leftHandCaster->CastSpellImmediate(Cast<MagicItem>(TESForm::GetById(0x27EB6)),
false, nullptr, 1.f, false, 0.f);
}
}
}
else
Expand Down
40 changes: 33 additions & 7 deletions Code/client/Services/Generic/MagicService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
#include <Structs/Skyrim/AnimationGraphDescriptor_VampireLordBehavior.h>
#include <Structs/Skyrim/AnimationGraphDescriptor_WerewolfBehavior.h>

#include <Games/Overrides.h>

#if TP_SKYRIM64
#include <Forms/SpellItem.h>
#include <PlayerCharacter.h>
Expand Down Expand Up @@ -69,7 +71,9 @@ void MagicService::OnSpellCastEvent(const SpellCastEvent& acEvent) const noexcep
// only sync concentration spells through spell cast sync, the rest through projectile sync for accuracy
if (SpellItem* pSpell = Cast<SpellItem>(TESForm::GetById(acEvent.SpellId)))
{
if (pSpell->eCastingType != MagicSystem::CastingType::CONCENTRATION)
if (pSpell->eCastingType != MagicSystem::CastingType::CONCENTRATION &&
!pSpell->IsWardSpell() &&
!pSpell->IsInvisibilitySpell())
{
spdlog::debug("Canceled magic spell");
return;
Expand Down Expand Up @@ -119,7 +123,7 @@ void MagicService::OnSpellCastEvent(const SpellCastEvent& acEvent) const noexcep
}
}

spdlog::debug("Spell cast event sent, ID: {:X}, Source: {}, IsDualCasting: {}, desired target: {:X}", request.CasterId,
spdlog::info("Spell cast event sent, ID: {:X}, Source: {}, IsDualCasting: {}, desired target: {:X}", request.CasterId,
request.CastingSource, request.IsDualCasting, request.DesiredTarget);

m_transport.Send(request);
Expand Down Expand Up @@ -181,7 +185,7 @@ void MagicService::OnNotifySpellCast(const NotifySpellCast& acMessage) const noe
return;
}

TESForm* pDesiredTargetForm = nullptr;
TESObjectREFR* pDesiredTarget = nullptr;

if (acMessage.DesiredTarget != 0)
{
Expand All @@ -200,12 +204,12 @@ void MagicService::OnNotifySpellCast(const NotifySpellCast& acMessage) const noe
if (serverId == acMessage.DesiredTarget)
{
const auto& formIdComponent = view.get<FormIdComponent>(entity);
pDesiredTargetForm = TESForm::GetById(formIdComponent.Id);
pDesiredTarget = Cast<TESObjectREFR>(TESForm::GetById(formIdComponent.Id));
}
}
}

TESObjectREFR* pDesiredTarget = Cast<TESObjectREFR>(pDesiredTargetForm);
ScopedSpellCastOverride _;

switch (acMessage.CastingSource)
{
Expand All @@ -221,7 +225,11 @@ void MagicService::OnNotifySpellCast(const NotifySpellCast& acMessage) const noe
case MagicSystem::CastingSource::INSTANT:
pActor->instantCaster->CastSpellImmediate(pSpell, false, pDesiredTarget, 1.0f, false, 0.0f);
break;
default:
spdlog::warn("{}: could not find casting source {}", __FUNCTION__, acMessage.CastingSource);
}

spdlog::info("Successfully casted remote spell");
#endif
}

Expand All @@ -240,14 +248,17 @@ void MagicService::OnInterruptCastEvent(const InterruptCastEvent& acEvent) const
});

if (casterEntityIt == std::end(view))
{
spdlog::warn("{}: could not find caster, form id {:X}", __FUNCTION__, formId);
return;
}

auto& localComponent = view.get<LocalComponent>(*casterEntityIt);

InterruptCastRequest request;
request.CasterId = localComponent.Id;

spdlog::debug("Sending out interrupt cast");
spdlog::info("Sending out interrupt cast");

m_transport.Send(request);
#endif
Expand Down Expand Up @@ -275,7 +286,7 @@ void MagicService::OnNotifyInterruptCast(const NotifyInterruptCast& acMessage) c

pActor->InterruptCast(false);

spdlog::debug("Interrupt remote cast successful");
spdlog::info("Interrupt remote cast successful");
#endif
}

Expand All @@ -285,6 +296,17 @@ void MagicService::OnAddTargetEvent(const AddTargetEvent& acEvent) noexcept
if (!m_transport.IsConnected())
return;

// These effects are applied through spell cast sync
if (SpellItem* pSpellItem = Cast<SpellItem>(TESForm::GetById(acEvent.SpellID)))
{
if (pSpellItem->eCastingType == MagicSystem::CastingType::CONCENTRATION ||
pSpellItem->IsWardSpell() ||
pSpellItem->IsInvisibilitySpell())
{
return;
}
}

AddTargetRequest request;

if (!m_world.GetModSystem().GetServerModId(acEvent.SpellID, request.SpellId.ModId, request.SpellId.BaseId))
Expand Down Expand Up @@ -324,6 +346,8 @@ void MagicService::OnAddTargetEvent(const AddTargetEvent& acEvent) noexcept

request.TargetId = serverIdRes.value();
m_transport.Send(request);

spdlog::info("Sending effect sync request");
#endif
}

Expand Down Expand Up @@ -395,6 +419,8 @@ void MagicService::OnNotifyAddTarget(const NotifyAddTarget& acMessage) const noe
pActor = PlayerCharacter::Get();

pActor->magicTarget.AddTarget(data);

spdlog::info("Applied remote magic effect");
#endif
}

Expand Down
7 changes: 7 additions & 0 deletions Code/client/Services/MagicService.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ struct NotifyAddTarget;

/**
* @brief Handles magic spell casting and magic effects.
*
* To the poor sod that thinks of venturing into this part of the code base..
* Yes, the magic sync code is a mess. It is easily the ugliest part of the codebase.
* I did not get around to refactoring this before going open source, but it definitely should be.
* I'm sorry, and good luck.
*
* Contact cosideci for more info.
*/
struct MagicService : BSTEventSink<TESMagicEffectApplyEvent>, BSTEventSink<TESActiveEffectApplyRemove>
{
Expand Down

0 comments on commit ef86bde

Please sign in to comment.