Skip to content

Commit d7797cd

Browse files
authored
Support Quest Item Synchronization and Party Member Progression (#769)
* 1. sync quest items 2. support party member quest progression * Optimize code * Optimize code.
1 parent 84e7461 commit d7797cd

File tree

8 files changed

+83
-7
lines changed

8 files changed

+83
-7
lines changed

Code/client/Games/Skyrim/Actor.cpp

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -680,16 +680,22 @@ int32_t Actor::GetGoldAmount() const noexcept
680680
return TiltedPhoques::ThisCall(s_getGoldAmount, this);
681681
}
682682

683-
void Actor::SetActorInventory(const Inventory& aInventory) noexcept
683+
void Actor::SetActorInventory(const Inventory& acInventory) noexcept
684684
{
685685
spdlog::info("Setting inventory for actor {:X}", formID);
686686

687687
// The UnEquipAll() that used to be here is redundant,
688688
// as RemoveAllItems() unequips every item if needed.
689689
// Placing this UnEquipAll() here seems to trigger a Skyrim bug/race.
690690

691-
SetInventory(aInventory);
692-
SetMagicEquipment(aInventory.CurrentMagicEquipment);
691+
Inventory currentInventory = GetActorInventory();
692+
693+
if (!this->GetExtension()->IsPlayer() && currentInventory.ContainsQuestItems())
694+
SetInventoryRetainingQuestItems(currentInventory, acInventory);
695+
else
696+
SetInventory(acInventory);
697+
698+
SetMagicEquipment(acInventory.CurrentMagicEquipment);
693699
}
694700

695701
void Actor::SetMagicEquipment(const MagicEquipment& acEquipment) noexcept

Code/client/Games/Skyrim/TESObjectREFR.cpp

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -781,6 +781,56 @@ void TESObjectREFR::SetInventory(const Inventory& aInventory) noexcept
781781
}
782782
}
783783

784+
Vector<uint32_t> TESObjectREFR::RemoveNonQuestItems(Inventory& aCurrentInventory) noexcept
785+
{
786+
ScopedEquipOverride equipOverride_;
787+
788+
Vector<uint32_t> questEntries{};
789+
790+
auto& modSystem = World::Get().GetModSystem();
791+
792+
for (auto& entry : aCurrentInventory.Entries)
793+
{
794+
if (entry.IsQuestItem)
795+
{
796+
uint32_t gameId = modSystem.GetGameId(entry.BaseId);
797+
questEntries.emplace_back(gameId);
798+
continue;
799+
}
800+
801+
if (entry.Count <= 0)
802+
continue;
803+
804+
entry.Count = -entry.Count;
805+
AddOrRemoveItem(entry, true);
806+
}
807+
808+
return questEntries;
809+
}
810+
811+
void TESObjectREFR::SetInventoryRetainingQuestItems(Inventory& aCurrentInventory, const Inventory& acSourceInventory) noexcept
812+
{
813+
spdlog::debug("Setting inventory for {:X}", formID);
814+
815+
ScopedInventoryOverride _;
816+
817+
// Remove all non-quest items first
818+
Vector<uint32_t> questItemIds = RemoveNonQuestItems(aCurrentInventory);
819+
auto& modSystem = World::Get().GetModSystem();
820+
821+
for (const auto& entry : acSourceInventory.Entries)
822+
{
823+
uint32_t gameId = modSystem.GetGameId(entry.BaseId);
824+
825+
// If the item is not in the list of quest items, add it
826+
if (std::find(questItemIds.begin(), questItemIds.end(), gameId) == questItemIds.end())
827+
{
828+
if (entry.Count != 0)
829+
AddOrRemoveItem(entry, true);
830+
}
831+
}
832+
}
833+
784834
void TESObjectREFR::AddOrRemoveItem(const Inventory::Entry& arEntry, bool aIsSettingInventory) noexcept
785835
{
786836
ModSystem& modSystem = World::Get().GetModSystem();

Code/client/Games/Skyrim/TESObjectREFR.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ struct TESObjectREFR : TESForm
178178
uint32_t GetAnimationVariableInt(BSFixedString* apVariableName) noexcept;
179179

180180
void RemoveAllItems() noexcept;
181+
Vector<uint32_t> RemoveNonQuestItems(Inventory& aCurrentInventory) noexcept;
181182
void Delete() const noexcept;
182183
void Disable() const noexcept;
183184
void Enable() const noexcept;
@@ -206,6 +207,7 @@ struct TESObjectREFR : TESForm
206207
bool IsItemInInventory(uint32_t aFormID) const noexcept;
207208

208209
void SetInventory(const Inventory& acContainer) noexcept;
210+
void SetInventoryRetainingQuestItems(Inventory& aCurrentInventory, const Inventory& acSourceInventory) noexcept;
209211
void AddOrRemoveItem(const Inventory::Entry& arEntry, bool aIsSettingInventory = false) noexcept;
210212
void UpdateItemList(TESForm* pUnkForm) noexcept;
211213

Code/client/Services/Generic/ObjectService.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,14 @@ void ObjectService::OnAssignObjectsResponse(const AssignObjectsResponse& acMessa
185185
}
186186

187187
if (pObject->baseForm->formType == FormType::Container)
188-
pObject->SetInventory(objectData.CurrentInventory);
188+
{
189+
Inventory currentInventory = pObject->GetInventory();
190+
191+
if (currentInventory.ContainsQuestItems())
192+
pObject->SetInventoryRetainingQuestItems(currentInventory, objectData.CurrentInventory);
193+
else
194+
pObject->SetInventory(objectData.CurrentInventory);
195+
}
189196
}
190197
}
191198

Code/client/Services/Generic/PlayerService.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ void PlayerService::OnPlayerDialogueEvent(const PlayerDialogueEvent& acEvent) co
153153
return;
154154

155155
const auto& partyService = m_world.GetPartyService();
156-
if (!partyService.IsInParty() || !partyService.IsLeader())
156+
if (!partyService.IsInParty())
157157
return;
158158

159159
PlayerDialogueRequest request{};

Code/client/Services/Generic/QuestService.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ void QuestService::OnConnected(const ConnectedEvent&) noexcept
5454

5555
BSTEventResult QuestService::OnEvent(const TESQuestStartStopEvent* apEvent, const EventDispatcher<TESQuestStartStopEvent>*)
5656
{
57-
if (ScopedQuestOverride::IsOverriden() || !m_world.Get().GetPartyService().IsLeader())
57+
if (ScopedQuestOverride::IsOverriden() || !m_world.Get().GetPartyService().IsInParty())
5858
return BSTEventResult::kOk;
5959

6060
spdlog::info("Quest start/stop event: {:X}", apEvent->formId);
@@ -86,7 +86,7 @@ BSTEventResult QuestService::OnEvent(const TESQuestStartStopEvent* apEvent, cons
8686

8787
BSTEventResult QuestService::OnEvent(const TESQuestStageEvent* apEvent, const EventDispatcher<TESQuestStageEvent>*)
8888
{
89-
if (ScopedQuestOverride::IsOverriden() || !m_world.Get().GetPartyService().IsLeader())
89+
if (ScopedQuestOverride::IsOverriden() || !m_world.Get().GetPartyService().IsInParty())
9090
return BSTEventResult::kOk;
9191

9292
spdlog::info("Quest stage event: {:X}, stage: {}", apEvent->formId, apEvent->stageId);

Code/encoding/Structs/Inventory.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,3 +193,13 @@ void Inventory::RemoveByFilter(std::function<bool(const Entry&)> aFilter) noexce
193193
{
194194
Entries.erase(std::remove_if(Entries.begin(), Entries.end(), aFilter), Entries.end());
195195
}
196+
197+
bool Inventory::ContainsQuestItems() const noexcept
198+
{
199+
for (const auto& entry : Entries)
200+
{
201+
if (entry.IsQuestItem)
202+
return true;
203+
}
204+
return false;
205+
}

Code/encoding/Structs/Inventory.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ struct Inventory
8383
void RemoveByFilter(std::function<bool(const Entry&)> aFilter) noexcept;
8484
void AddOrRemoveEntry(const Entry& acEntry) noexcept;
8585
void UpdateEquipment(const Inventory& acNewInventory) noexcept;
86+
bool ContainsQuestItems() const noexcept;
8687

8788
Vector<Entry> Entries{};
8889
MagicEquipment CurrentMagicEquipment{};

0 commit comments

Comments
 (0)