diff --git a/dGame/Character.cpp b/dGame/Character.cpp index 2e62dae8..0e838ba8 100644 --- a/dGame/Character.cpp +++ b/dGame/Character.cpp @@ -296,6 +296,12 @@ void Character::SaveXMLToDatabase() { flags->LinkEndChild(s); } + if (GetPlayerFlag(ePlayerFlag::EQUPPED_TRIAL_FACTION_GEAR)) { + auto* s = m_Doc.NewElement("s"); + s->SetAttribute("si", ePlayerFlag::EQUPPED_TRIAL_FACTION_GEAR); + flags->LinkEndChild(s); + } + SaveXmlRespawnCheckpoints(); //Call upon the entity to update our xmlDoc: @@ -357,49 +363,62 @@ void Character::SetPlayerFlag(const uint32_t flagId, const bool value) { } } - // Calculate the index first - auto flagIndex = uint32_t(std::floor(flagId / 64)); - - const auto shiftedValue = 1ULL << flagId % 64; - - auto it = m_PlayerFlags.find(flagIndex); - - // Check if flag index exists - if (it != m_PlayerFlags.end()) { - // Update the value - if (value) { - it->second |= shiftedValue; - } else { - it->second &= ~shiftedValue; - } + if (flagId == EQUPPED_TRIAL_FACTION_GEAR || flagId == IS_NEWS_SCREEN_VISIBLE) { + if (value) m_SessionFlags.insert(flagId); + else m_SessionFlags.erase(flagId); } else { - if (value) { - // Otherwise, insert the value - uint64_t flagValue = 0; + // Calculate the index first + auto flagIndex = uint32_t(std::floor(flagId / 64)); - flagValue |= shiftedValue; + const auto shiftedValue = 1ULL << flagId % 64; - m_PlayerFlags.insert(std::make_pair(flagIndex, flagValue)); + auto it = m_PlayerFlags.find(flagIndex); + + // Check if flag index exists + if (it != m_PlayerFlags.end()) { + // Update the value + if (value) { + it->second |= shiftedValue; + } else { + it->second &= ~shiftedValue; + } + } else { + if (value) { + // Otherwise, insert the value + uint64_t flagValue = 0; + + flagValue |= shiftedValue; + + m_PlayerFlags.insert(std::make_pair(flagIndex, flagValue)); + } } } - // Notify the client that a flag has changed server-side GameMessages::SendNotifyClientFlagChange(m_ObjectID, flagId, value, m_ParentUser->GetSystemAddress()); } bool Character::GetPlayerFlag(const uint32_t flagId) const { - // Calculate the index first - const auto flagIndex = uint32_t(std::floor(flagId / 64)); + using enum ePlayerFlag; - const auto shiftedValue = 1ULL << flagId % 64; + bool toReturn = false; //by def, return false. - auto it = m_PlayerFlags.find(flagIndex); - if (it != m_PlayerFlags.end()) { - // Don't set the data if we don't have to - return (it->second & shiftedValue) != 0; + // TODO make actual session flag checker using flags table in database. + if (flagId == EQUPPED_TRIAL_FACTION_GEAR || flagId == IS_NEWS_SCREEN_VISIBLE) { + toReturn = m_SessionFlags.contains(flagId); + } else { + // Calculate the index first + const auto flagIndex = uint32_t(std::floor(flagId / 64)); + + const auto shiftedValue = 1ULL << flagId % 64; + + auto it = m_PlayerFlags.find(flagIndex); + if (it != m_PlayerFlags.end()) { + // Don't set the data if we don't have to + toReturn = (it->second & shiftedValue) != 0; + } } - return false; //by def, return false. + return toReturn; } void Character::SetRetroactiveFlags() { diff --git a/dGame/Character.h b/dGame/Character.h index 0f41c634..99ed9855 100644 --- a/dGame/Character.h +++ b/dGame/Character.h @@ -620,6 +620,12 @@ private: */ uint64_t m_LastLogin{}; + /** + * Flags only set for the duration of a session + * + */ + std::set m_SessionFlags; + /** * The gameplay flags this character has (not just true values) */ diff --git a/dGame/Entity.cpp b/dGame/Entity.cpp index 5a0dab77..5184b378 100644 --- a/dGame/Entity.cpp +++ b/dGame/Entity.cpp @@ -860,6 +860,9 @@ void Entity::Subscribe(LWOOBJID scriptObjId, CppScripts::Script* scriptToAdd, co auto* destroyableComponent = GetComponent(); if (!destroyableComponent) return; destroyableComponent->Subscribe(scriptObjId, scriptToAdd); + } else if (notificationName == "PlayerResurrectionFinished") { + LOG("Subscribing to PlayerResurrectionFinished"); + m_Subscriptions[scriptObjId][notificationName] = scriptToAdd; } } @@ -868,6 +871,9 @@ void Entity::Unsubscribe(LWOOBJID scriptObjId, const std::string& notificationNa auto* destroyableComponent = GetComponent(); if (!destroyableComponent) return; destroyableComponent->Unsubscribe(scriptObjId); + } else if (notificationName == "PlayerResurrectionFinished") { + LOG("Unsubscribing from PlayerResurrectionFinished"); + m_Subscriptions[scriptObjId].erase(notificationName); } } @@ -1511,6 +1517,15 @@ void Entity::OnChildLoaded(GameMessages::ChildLoaded& childLoaded) { GetScript()->OnChildLoaded(*this, childLoaded); } +void Entity::NotifyPlayerResurrectionFinished(GameMessages::PlayerResurrectionFinished& msg) { + for (const auto& [id, scriptList] : m_Subscriptions) { + auto it = scriptList.find("PlayerResurrectionFinished"); + if (it == scriptList.end()) continue; + + it->second->NotifyPlayerResurrectionFinished(*this, msg); + } +} + void Entity::RequestActivityExit(Entity* sender, LWOOBJID player, bool canceled) { GetScript()->OnRequestActivityExit(sender, player, canceled); } diff --git a/dGame/Entity.h b/dGame/Entity.h index 7bb53c2d..853aa207 100644 --- a/dGame/Entity.h +++ b/dGame/Entity.h @@ -17,6 +17,7 @@ namespace GameMessages { struct ActivityNotify; struct ShootingGalleryFire; struct ChildLoaded; + struct PlayerResurrectionFinished; }; namespace Loot { @@ -219,6 +220,7 @@ public: void OnActivityNotify(GameMessages::ActivityNotify& notify); void OnShootingGalleryFire(GameMessages::ShootingGalleryFire& notify); void OnChildLoaded(GameMessages::ChildLoaded& childLoaded); + void NotifyPlayerResurrectionFinished(GameMessages::PlayerResurrectionFinished& msg); void OnMessageBoxResponse(Entity* sender, int32_t button, const std::u16string& identifier, const std::u16string& userData); void OnChoiceBoxResponse(Entity* sender, int32_t button, const std::u16string& buttonIdentifier, const std::u16string& identifier); @@ -372,6 +374,9 @@ protected: * Collision */ std::vector m_TargetsInPhantom; + + // objectID of receiver and map of notification name to script + std::map> m_Subscriptions; }; /** diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index 10259890..7e833a58 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -968,6 +968,8 @@ void GameMessages::SendResurrect(Entity* entity) { // and just make sure the client has time to be ready. constexpr float respawnTime = 3.66700005531311f + 0.5f; entity->AddCallbackTimer(respawnTime, [=]() { + GameMessages::PlayerResurrectionFinished msg; + entity->NotifyPlayerResurrectionFinished(msg); auto* destroyableComponent = entity->GetComponent(); if (destroyableComponent != nullptr && entity->GetLOT() == 1) { diff --git a/dGame/dGameMessages/GameMessages.h b/dGame/dGameMessages/GameMessages.h index b5ef62d9..4dde6285 100644 --- a/dGame/dGameMessages/GameMessages.h +++ b/dGame/dGameMessages/GameMessages.h @@ -765,6 +765,10 @@ namespace GameMessages { LOT templateID{}; LWOOBJID childID{}; }; + + struct PlayerResurrectionFinished : public GameMsg { + PlayerResurrectionFinished() : GameMsg(MessageType::Game::PLAYER_RESURRECTION_FINISHED) {} + }; }; #endif // GAMEMESSAGES_H diff --git a/dScripts/CppScripts.cpp b/dScripts/CppScripts.cpp index c8087c94..bd483e73 100644 --- a/dScripts/CppScripts.cpp +++ b/dScripts/CppScripts.cpp @@ -334,6 +334,8 @@ #include "AgSpiderBossMessage.h" #include "GfRaceInstancer.h" #include "NsRaceServer.h" +#include "TrialFactionArmorServer.h" +#include "ImaginationBackPack.h" #include #include @@ -700,6 +702,8 @@ namespace { {"scripts\\ai\\RACING\\TRACK_GF\\GF_RACE_SERVER.lua", []() {return new GfRaceServer();}}, {"scripts\\ai\\RACING\\TRACK_FV\\FV_RACE_SERVER.lua", []() {return new FvRaceServer();}}, {"scripts\\ai\\RACING\\OBJECTS\\VEHICLE_DEATH_TRIGGER_WATER_SERVER.lua", []() {return new VehicleDeathTriggerWaterServer();}}, + {"scripts\\equipmenttriggers\\L_TRIAL_FACTION_ARMOR_SERVER.lua", []() {return new TrialFactionArmorServer();}}, + {"scripts\\equipmenttriggers\\ImaginationBackPack.lua", []() {return new ImaginationBackPack();}}, }; diff --git a/dScripts/CppScripts.h b/dScripts/CppScripts.h index 18e0ec10..eb483a11 100644 --- a/dScripts/CppScripts.h +++ b/dScripts/CppScripts.h @@ -186,6 +186,8 @@ namespace CppScripts { */ virtual void NotifyHitOrHealResult(Entity* self, Entity* attacker, int32_t damage) {}; + virtual void NotifyPlayerResurrectionFinished(Entity& self, GameMessages::PlayerResurrectionFinished& msg) {}; + /** * Invoked when a player has responsed to a mission. * diff --git a/dScripts/EquipmentTriggers/CMakeLists.txt b/dScripts/EquipmentTriggers/CMakeLists.txt index af0a1425..6cb12313 100644 --- a/dScripts/EquipmentTriggers/CMakeLists.txt +++ b/dScripts/EquipmentTriggers/CMakeLists.txt @@ -1,5 +1,7 @@ set(DSCRIPTS_SOURCES_EQUIPMENTTRIGGERSSCRIPTS - "CoilBackpackBase.cpp") + "CoilBackpackBase.cpp" + "ImaginationBackPack.cpp" + "TrialFactionArmorServer.cpp") add_library(dScriptsEquipmentTriggers OBJECT ${DSCRIPTS_SOURCES_EQUIPMENTTRIGGERSSCRIPTS}) target_include_directories(dScriptsEquipmentTriggers PUBLIC ".") diff --git a/dScripts/EquipmentTriggers/ImaginationBackPack.cpp b/dScripts/EquipmentTriggers/ImaginationBackPack.cpp new file mode 100644 index 00000000..96b99449 --- /dev/null +++ b/dScripts/EquipmentTriggers/ImaginationBackPack.cpp @@ -0,0 +1,22 @@ +#include "ImaginationBackPack.h" + +#include "SkillComponent.h" + +void ImaginationBackPack::OnFactionTriggerItemEquipped(Entity* itemOwner, LWOOBJID itemObjId) { + LOG("Subscribing to PlayerResurrectionFinished"); + itemOwner->Subscribe(itemObjId, this, "PlayerResurrectionFinished"); +} + +void ImaginationBackPack::NotifyPlayerResurrectionFinished(Entity& self, GameMessages::PlayerResurrectionFinished& msg) { + LOG("PlayerResurrectionFinished"); + auto* skillComponent = self.GetComponent(); + if (!skillComponent) return; + LOG("Casting skill 1334"); + skillComponent->CastSkill(1334); +} + +void ImaginationBackPack::OnFactionTriggerItemUnequipped(Entity* itemOwner, LWOOBJID itemObjId) { + LOG("Unsubscribing from PlayerResurrectionFinished"); + itemOwner->Unsubscribe(itemObjId, "PlayerResurrectionFinished"); +} + diff --git a/dScripts/EquipmentTriggers/ImaginationBackPack.h b/dScripts/EquipmentTriggers/ImaginationBackPack.h new file mode 100644 index 00000000..2b1fb1d4 --- /dev/null +++ b/dScripts/EquipmentTriggers/ImaginationBackPack.h @@ -0,0 +1,13 @@ +#ifndef IMAGINATIONBACKPACK_H +#define IMAGINATIONBACKPACK_H + +#include "CppScripts.h" + +class ImaginationBackPack : public CppScripts::Script { +public: + void OnFactionTriggerItemEquipped(Entity* itemOwner, LWOOBJID itemObjId) override; + void NotifyPlayerResurrectionFinished(Entity& self, GameMessages::PlayerResurrectionFinished& msg) override; + void OnFactionTriggerItemUnequipped(Entity* itemOwner, LWOOBJID itemObjId) override; +}; + +#endif //!IMAGINATIONBACKPACK_H diff --git a/dScripts/EquipmentTriggers/ObyxShardPack.h b/dScripts/EquipmentTriggers/ObyxShardPack.h new file mode 100644 index 00000000..a13ab120 --- /dev/null +++ b/dScripts/EquipmentTriggers/ObyxShardPack.h @@ -0,0 +1,13 @@ +#ifndef __OBYXSHARDPACK__H__ +#define __OBYXSHARDPACK__H__ + +#include "CoilBackpackBase.h" + +class ObyxShardPack : public CoilBackpackBase { +public: + ObyxShardPack() : CoilBackpackBase(skillId) {}; +private: + static const uint32_t skillId = 1751; +}; + +#endif //!__OBYXSHARDPACK__H__ diff --git a/dScripts/EquipmentTriggers/RubyShardPack.h b/dScripts/EquipmentTriggers/RubyShardPack.h new file mode 100644 index 00000000..38b7904a --- /dev/null +++ b/dScripts/EquipmentTriggers/RubyShardPack.h @@ -0,0 +1,13 @@ +#ifndef __RUBYSHARDPACK__H__ +#define __RUBYSHARDPACK__H__ + +#include "CoilBackpackBase.h" + +class RubyShardPack : public CoilBackpackBase { +public: + RubyShardPack() : CoilBackpackBase(skillId) {}; +private: + static const uint32_t skillId = 1750; +}; + +#endif //!__RUBYSHARDPACK__H__ diff --git a/dScripts/EquipmentTriggers/TrialFactionArmorServer.cpp b/dScripts/EquipmentTriggers/TrialFactionArmorServer.cpp new file mode 100644 index 00000000..b59b24aa --- /dev/null +++ b/dScripts/EquipmentTriggers/TrialFactionArmorServer.cpp @@ -0,0 +1,27 @@ +#include "TrialFactionArmorServer.h" + +#include "Character.h" +#include "ePlayerFlag.h" +#include "DestroyableComponent.h" + +void TrialFactionArmorServer::OnFactionTriggerItemEquipped(Entity* itemOwner, LWOOBJID itemObjId) { + auto* character = itemOwner->GetCharacter(); + if (!character) return; + + auto flag = character->GetPlayerFlag(ePlayerFlag::EQUPPED_TRIAL_FACTION_GEAR); + if (!flag) { + character->SetPlayerFlag(ePlayerFlag::EQUPPED_TRIAL_FACTION_GEAR, true); + + // technically a TimerWithCancel but our current implementation doesnt support this. + itemOwner->AddCallbackTimer(1.0f, [itemOwner]() { + auto* destroyableComponent = itemOwner->GetComponent(); + if (!destroyableComponent) return; + + destroyableComponent->SetHealth(destroyableComponent->GetMaxHealth()); + destroyableComponent->SetArmor(destroyableComponent->GetMaxArmor()); + destroyableComponent->SetImagination(destroyableComponent->GetMaxImagination()); + + Game::entityManager->SerializeEntity(itemOwner); + }); + } +} diff --git a/dScripts/EquipmentTriggers/TrialFactionArmorServer.h b/dScripts/EquipmentTriggers/TrialFactionArmorServer.h new file mode 100644 index 00000000..6728729b --- /dev/null +++ b/dScripts/EquipmentTriggers/TrialFactionArmorServer.h @@ -0,0 +1,11 @@ +#ifndef TRIALFACTIONARMORSERVER_ +#define TRIALFACTIONARMORSERVER_ + +#include "CppScripts.h" + +class TrialFactionArmorServer : public CppScripts::Script { +public: + void OnFactionTriggerItemEquipped(Entity* itemOwner, LWOOBJID itemObjId) override; +}; + +#endif //!TRIALFACTIONARMORSERVER_