Skip to content
Closed
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
3 changes: 3 additions & 0 deletions Code/client/Games/Animation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ static TPerformAction* RealPerformAction;
// TODO: make scoped override
thread_local bool g_forceAnimation = false;

// This is where the Actors AI is enabled/disabled: almost all of NPC AI/behavior is
// determined by Actions that are run on them.

uint8_t TP_MAKE_THISCALL(HookPerformAction, ActorMediator, TESActionData* apAction)
{
auto pActor = apAction->actor;
Expand Down
7 changes: 7 additions & 0 deletions Code/client/Games/Skyrim/AI/Movement/PlayerControls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ void PlayerControls::SetCamSwitch(bool aSet) noexcept
Data.remapMode = aSet;
}

bool PlayerControls::IsMovementControlsEnabled() noexcept
{
using TIsMovementControlsEnabled = bool();
POINTER_SKYRIMSE(TIsMovementControlsEnabled, s_isMovementControlsEnabled, 55485);
return s_isMovementControlsEnabled.Get()();
}

BSInputEnableManager* BSInputEnableManager::Get()
{
POINTER_SKYRIMSE(BSInputEnableManager*, s_instance, 400863);
Expand Down
2 changes: 2 additions & 0 deletions Code/client/Games/Skyrim/AI/Movement/PlayerControls.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ struct PlayerControls

void SetCamSwitch(bool aSet) noexcept;

static bool IsMovementControlsEnabled() noexcept;

public:
char pad0[0x20];
PlayerControlsData Data;
Expand Down
2 changes: 0 additions & 2 deletions Code/client/Games/Skyrim/Actor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1215,8 +1215,6 @@ static TSpeakSoundFunction* RealSpeakSoundFunction = nullptr;

bool TP_MAKE_THISCALL(HookSpeakSoundFunction, Actor, const char* apName, uint32_t* a3, uint32_t a4, uint32_t a5, uint32_t a6, uint64_t a7, uint64_t a8, uint64_t a9, bool a10, uint64_t a11, bool a12, bool a13, bool a14)
{
spdlog::debug("a3: {:X}, a4: {}, a5: {}, a6: {}, a7: {}, a8: {:X}, a9: {:X}, a10: {}, a11: {:X}, a12: {}, a13: {}, a14: {}", (uint64_t)a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14);

if (apThis->GetExtension()->IsLocal())
World::Get().GetRunner().Trigger(DialogueEvent(apThis->formID, apName));

Expand Down
15 changes: 15 additions & 0 deletions Code/client/Games/Skyrim/Events/EventDispatcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,14 +190,29 @@ struct TESResolveNPCTemplatesEvent

struct TESSceneEvent
{
void* ref;
uint32_t sceneFormId;
uint32_t sceneType; // BEGIN (0) or END (1)
};

// RE'd fields in TESSceneActionEvent and TESScenePhaseEvent are probably incorrect

struct TESSceneActionEvent
{
void* ref;
uint32_t sceneFormId;
uint32_t actionIndex;
uint32_t questFormId;
uint32_t actorAliasId;
};

struct TESScenePhaseEvent
{
uint32_t sceneFormId;
uint32_t phaseIndex;
uint32_t sceneType; // BEGIN (0) or END (1)
uint16_t questStageId;
void* callback;
};

struct TESSellEvent
Expand Down
37 changes: 28 additions & 9 deletions Code/client/Games/Skyrim/Forms/TESQuest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,13 +54,9 @@ void TESQuest::SetActive(bool toggle)

bool TESQuest::IsStageDone(uint16_t stageIndex)
{
for (Stage* it : stages)
{
if (it->stageIndex == stageIndex)
return it->IsDone();
}

return false;
TP_THIS_FUNCTION(TIsStageDone, bool, TESQuest, uint16_t);
POINTER_SKYRIMSE(TIsStageDone, IsStageDone, 25011);
return IsStageDone(this, stageIndex);
}

bool TESQuest::Kill()
Expand Down Expand Up @@ -88,23 +84,28 @@ bool TESQuest::EnsureQuestStarted(bool& success, bool force)
return SetRunning(this, &success, force);
}

bool TESQuest::SetStage(uint16_t newStage)
bool TESQuest::SetStage(uint16_t stageIndex)
{
ScopedQuestOverride _;

TP_THIS_FUNCTION(TSetStage, bool, TESQuest, uint16_t);
POINTER_SKYRIMSE(TSetStage, SetStage, 25004);
return SetStage(this, newStage);
return SetStage(this, stageIndex);
}

void TESQuest::ScriptSetStage(uint16_t stageIndex)
{
spdlog::debug(__FUNCTION__ ": called with a value of {}", stageIndex);
if (currentStage == stageIndex || IsStageDone(stageIndex))
{
spdlog::debug("Stage {} is already done, not calling SetCurrentStageID", stageIndex);
return;
}

using Quest = TESQuest;
PAPYRUS_FUNCTION(void, Quest, SetCurrentStageID, int);
s_pSetCurrentStageID(this, stageIndex);
spdlog::debug(__FUNCTION__ ": stage has been set to {}", stageIndex);
}

void TESQuest::SetStopped()
Expand All @@ -113,6 +114,24 @@ void TESQuest::SetStopped()
MarkChanged(2);
}

bool TESQuest::IsAnyCutscenePlaying()
{
for (const auto& scene : scenes)
{
if (scene->isPlaying)
return true;
}
return false;
}

void BGSScene::ScriptForceStart()
{
using Scene = BGSScene;
PAPYRUS_FUNCTION(void, Scene, ForceStart);
s_pForceStart(this);
spdlog::debug(__FUNCTION__ ": force started scene {:X}, isPlaying? {}", formID, isPlaying);
}

static TiltedPhoques::Initializer s_questInitHooks(
[]()
{
Expand Down
45 changes: 38 additions & 7 deletions Code/client/Games/Skyrim/Forms/TESQuest.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,42 @@
#include <Games/Primitives.h>
#include <Misc/BSString.h>
#include <Components/TESFullName.h>
#include <Components/TESCondition.h>
#include <Forms/BGSStoryManagerTree.h>

struct BGSSceneAction
{
virtual ~BGSSceneAction();

uint32_t actorID;
uint16_t startPhase;
uint16_t endPhase;
uint32_t flags;
uint8_t status;

void Start() { this->status |= 1u; }
};

static_assert(offsetof(BGSSceneAction, flags) == 0x10);

struct BGSScene : TESForm
{
GameArray<void*> phases;
GameArray<uint32_t> actorIds;
GameArray<uint32_t> actorFlags;
GameArray<uint32_t> actorProgressionFlags;
GameArray<BGSSceneAction*> actions;
TESQuest* owningQuest;
uint32_t flags;
uint32_t padA4;
TESCondition conditions;
bool isPlaying;

void ScriptForceStart();
};

static_assert(offsetof(BGSScene, isPlaying) == 0xB0);

struct TESQuest : BGSStoryManagerTreeForm
{
enum class State : uint8_t
Expand Down Expand Up @@ -76,6 +104,10 @@ struct TESQuest : BGSStoryManagerTreeForm
uint16_t stageIndex;
uint8_t flags;

operator bool() const
{
return *reinterpret_cast<const std::uintptr_t*>(this) != 0;
}
inline bool IsDone() { return flags & 1; }
};

Expand All @@ -90,11 +122,9 @@ struct TESQuest : BGSStoryManagerTreeForm
Type type; // 0x00DF
int32_t scopedStatus; // 0x00E0 default init: -1, if not -1 outside of story manager scope
uint32_t padE4;
GameList<Stage> stages;
/*
GameList<Stage>* pExecutedStages; // 0x00E8
GameList<Stage>* pWaitingStages; // 0x00F0
*/
//GameList<Stage> stages;
GameValueList<Stage>* pExecutedStages; // 0x00E8
GameValueList<Stage*>* pWaitingStages; // 0x00F0
GameList<Objective> objectives; // 0x00F8
char pad108[0x100]; // 0x0108
GameArray<BGSScene*> scenes; // 0x0208
Expand Down Expand Up @@ -126,15 +156,16 @@ struct TESQuest : BGSStoryManagerTreeForm

bool EnsureQuestStarted(bool& succeded, bool force);

bool SetStage(uint16_t stage);
bool SetStage(uint16_t stageIndex);
void ScriptSetStage(uint16_t stage);
void SetStopped();
bool IsAnyCutscenePlaying();
};

static_assert(sizeof(TESQuest) == 0x268);
static_assert(offsetof(TESQuest, fullName) == 0x28);
static_assert(offsetof(TESQuest, flags) == 0xDC);
static_assert(offsetof(TESQuest, stages) == 0xE8);
static_assert(offsetof(TESQuest, pExecutedStages) == 0xE8);
static_assert(offsetof(TESQuest, objectives) == 0xF8);
static_assert(offsetof(TESQuest, currentStage) == 0x228);
static_assert(offsetof(TESQuest, unkFlags) == 0x248);
2 changes: 1 addition & 1 deletion Code/client/Games/Skyrim/Magic/MagicTarget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ bool MagicTarget::AddTargetData::IsForbiddenEffect(Actor* apTarget)
if (apTarget != PlayerCharacter::Get())
return false;

return pEffectItem->IsNightVisionEffect();
return pEffectItem->IsNightVisionEffect() || pEffectItem->IsSlowEffect();
}

Actor* MagicTarget::GetTargetAsActor()
Expand Down
33 changes: 32 additions & 1 deletion Code/client/Services/Debug/Views/QuestDebugView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,9 @@ void DebugService::DrawQuestDebugView()

if (ImGui::CollapsingHeader("Stages"))
{
for (auto* pStage : pQuest->stages)
for (auto& stage : *pQuest->pExecutedStages)
{
auto pStage = &stage;
ImGui::TextColored({0.f, 255.f, 255.f, 255.f}, "Stage: %d, is done? %s", pStage->stageIndex, pStage->IsDone() ? "true" : "false");

char setStage[64];
Expand All @@ -64,6 +65,36 @@ void DebugService::DrawQuestDebugView()
pQuest->ScriptSetStage(pStage->stageIndex);
}
}
if (ImGui::CollapsingHeader("Waiting Stages"))
{
for (auto& pStage : *pQuest->pWaitingStages)
{
ImGui::TextColored({0.f, 255.f, 255.f, 255.f}, "Stage: %d, is done? %s", pStage->stageIndex, pStage->IsDone() ? "true" : "false");

char setStage[64];
sprintf_s(setStage, std::size(setStage), "Set stage (%d)", pStage->stageIndex);

if (ImGui::Button(setStage))
pQuest->ScriptSetStage(pStage->stageIndex);
}
}

if (ImGui::CollapsingHeader("Scenes"))
{
for (auto& pScene : pQuest->scenes)
{
ImGui::TextColored({0.f, 255.f, 255.f, 255.f}, "Scene Form ID: %x, is playing? %s", pScene->formID, pScene->isPlaying ? "true" : "false");

ImGui::Text("Scene actions:");
for (int i = 0; i < pScene->actions.length; ++i)
{
char startAction[64];
sprintf_s(startAction, std::size(startAction), "Start action %d", i);
if (ImGui::Button(startAction))
pScene->actions[i]->Start();
}
}
}

if (ImGui::CollapsingHeader("Actors"))
{
Expand Down
2 changes: 0 additions & 2 deletions Code/client/Services/Generic/InputService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,8 +189,6 @@ void ProcessKeyboard(uint16_t aKey, uint16_t aScanCode, cef_key_event_type_t aTy

const auto active = overlay.GetActive();

spdlog::debug("ProcessKey, type: {}, key: {}, active: {}", aType, aKey, active);

if (aType != KEYEVENT_CHAR && (IsToggleKey(aKey) || (IsDisableKey(aKey) && active)))
{
if (!overlay.GetInGame())
Expand Down
4 changes: 3 additions & 1 deletion Code/client/Services/Generic/MagicService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,9 @@ void MagicService::OnAddTargetEvent(const AddTargetEvent& acEvent) noexcept

m_transport.Send(request);

spdlog::debug("Sending effect sync request");
spdlog::debug("Sending effect sync request. SpellId={:X} ({:X} / {:X}), EffectId={:X} ({:X} / {:X})",
request.SpellId.LogFormat(), request.SpellId.BaseId, request.SpellId.ModId,
request.EffectId.LogFormat(), request.EffectId.BaseId, request.EffectId.ModId);
}

void MagicService::OnNotifyAddTarget(const NotifyAddTarget& acMessage) noexcept
Expand Down
Loading
Loading