diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index b42e7d010..eea76fba3 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -6338,6 +6338,40 @@ void GameMessages::SendUpdateInventoryUi(LWOOBJID objectId, const SystemAddress& bitStream.Write(objectId); bitStream.Write(MessageType::Game::UPDATE_INVENTORY_UI); - + + SEND_PACKET; +} + +void GameMessages::DisplayTooltip::Send() const { + CBITSTREAM; + CMSGHEADER; + + bitStream.Write(target); + bitStream.Write(msgId); + + bitStream.Write(doOrDie); + bitStream.Write(noRepeat); + bitStream.Write(noRevive); + bitStream.Write(isPropertyTooltip); + bitStream.Write(show); + bitStream.Write(translate); + bitStream.Write(time); + bitStream.Write(id.size()); + bitStream.Write(id); + + std::string toWrite; + for (const auto* item : localizeParams) { + toWrite += item->GetString() + "\n"; + } + if (!toWrite.empty()) toWrite.pop_back(); + bitStream.Write(toWrite.size()); + bitStream.Write(GeneralUtils::ASCIIToUTF16(toWrite)); + if (!toWrite.empty()) bitStream.Write(0x00); // Null Terminator + + bitStream.Write(imageName.size()); + bitStream.Write(imageName); + bitStream.Write(text.size()); + bitStream.Write(text); + SEND_PACKET; } diff --git a/dGame/dGameMessages/GameMessages.h b/dGame/dGameMessages/GameMessages.h index 1dbe5d815..62d59a1de 100644 --- a/dGame/dGameMessages/GameMessages.h +++ b/dGame/dGameMessages/GameMessages.h @@ -11,6 +11,7 @@ #include "eCyclingMode.h" #include "eLootSourceType.h" #include "Brick.h" +#include "MessageType/Game.h" class AMFBaseValue; class Entity; @@ -20,6 +21,7 @@ class User; class Leaderboard; class PropertySelectQueryProperty; class TradeItem; +class LDFBaseData; enum class eAnimationFlags : uint32_t; @@ -47,6 +49,15 @@ enum class eCameraTargetCyclingMode : int32_t { }; namespace GameMessages { + struct GameMsg { + GameMsg(MessageType::Game gmId) : msgId{ gmId } {} + virtual ~GameMsg() = default; + virtual void Send() const {} + MessageType::Game msgId; + LWOOBJID target{ LWOOBJID_EMPTY }; + SystemAddress sysAddr{ UNASSIGNED_SYSTEM_ADDRESS }; + }; + class PropertyDataMessage; void SendFireEventClientSide(const LWOOBJID& objectID, const SystemAddress& sysAddr, std::u16string args, const LWOOBJID& object, int64_t param1, int param2, const LWOOBJID& sender); void SendTeleport(const LWOOBJID& objectID, const NiPoint3& pos, const NiQuaternion& rot, const SystemAddress& sysAddr, bool bSetRotation = false); @@ -680,6 +691,22 @@ namespace GameMessages { // This is a client gm however its default values are exactly what we need to get around the invisible inventory item issues. void SendUpdateInventoryUi(LWOOBJID objectId, const SystemAddress& sysAddr); + + struct DisplayTooltip : public GameMsg { + DisplayTooltip() : GameMsg(MessageType::Game::DISPLAY_TOOLTIP) {} + bool doOrDie{}; + bool noRepeat{}; + bool noRevive{}; + bool isPropertyTooltip{}; + bool show{}; + bool translate{}; + int32_t time{}; + std::u16string id{}; + std::vector localizeParams{}; + std::u16string imageName{}; + std::u16string text{}; + void Send() const override; + }; }; #endif // GAMEMESSAGES_H diff --git a/dScripts/CppScripts.cpp b/dScripts/CppScripts.cpp index e3b3acdbd..543284516 100644 --- a/dScripts/CppScripts.cpp +++ b/dScripts/CppScripts.cpp @@ -328,6 +328,7 @@ #include "LupGenericInteract.h" #include "WblRobotCitizen.h" #include "EnemyClearThreat.h" +#include "AgSpiderBossMessage.h" #include #include @@ -688,6 +689,7 @@ namespace { {"scripts\\zone\\LUPs\\RobotCity Intro\\WBL_RCIntro_RobotCitizenRed.lua", []() {return new WblRobotCitizen();}}, {"scripts\\zone\\LUPs\\RobotCity Intro\\WBL_RCIntro_RobotCitizenYellow.lua", []() {return new WblRobotCitizen();}}, {"scripts\\02_server\\Map\\General\\L_ENEMY_CLEAR_THREAT.lua", []() {return new EnemyClearThreat();}}, + {"scripts\\ai\\AG\\L_AG_SPIDER_BOSS_MESSAGE.lua", []() {return new AgSpiderBossMessage();}}, }; diff --git a/dScripts/ai/AG/AgSpiderBossMessage.cpp b/dScripts/ai/AG/AgSpiderBossMessage.cpp new file mode 100644 index 000000000..6e6cc1b9d --- /dev/null +++ b/dScripts/ai/AG/AgSpiderBossMessage.cpp @@ -0,0 +1,81 @@ +#include "AgSpiderBossMessage.h" + +#include "Entity.h" +#include "GameMessages.h" + +#include "RenderComponent.h" + +Box AgSpiderBossMessage::GetBox(Entity* self) const { + return self->GetVar(u"box"); +} + +void AgSpiderBossMessage::SetBox(Entity* self, const Box& box) const { + self->SetVar(u"box", box); +} + +void AgSpiderBossMessage::MakeBox(Entity* self) const { + auto box = GetBox(self); + if (box.boxTarget == LWOOBJID_EMPTY || box.isDisplayed || box.boxSelf == LWOOBJID_EMPTY) return; + + box.isDisplayed = true; + SetBox(self, box); + self->AddTimer("BoxTimer", box.boxTime); + + const auto* const tgt = Game::entityManager->GetEntity(box.boxTarget); + if (!tgt) return; + GameMessages::DisplayTooltip tooltip; + tooltip.target = tgt->GetObjectID(); + tooltip.sysAddr = tgt->GetSystemAddress(); + tooltip.show = true; + tooltip.text = box.boxText; + tooltip.time = box.boxTime * 1000; // to ms + tooltip.Send(); +} + +void AgSpiderBossMessage::OnCollisionPhantom(Entity* self, Entity* target) { + if (!target || !target->IsPlayer()) return; + + auto box = GetBox(self); + // knockback the target + auto forward = target->GetRotation().GetForwardVector(); + box.boxTarget = target->GetObjectID(); + GameMessages::SendPlayFXEffect(target->GetObjectID(), 1378, u"create", "pushBack"); + RenderComponent::PlayAnimation(target, "knockback-recovery"); + forward.y += 15; + forward.x *= 100; + forward.z *= 100; + GameMessages::SendKnockback(target->GetObjectID(), self->GetObjectID(), self->GetObjectID(), 0, forward); + + if (box.isTouch || box.isDisplayed) return; + box.boxSelf = self->GetObjectID(); + box.isTouch = true; + box.boxText = u"%[SPIDER_CAVE_MESSAGE]"; + SetBox(self, box); + self->AddTimer("EventTimer", 0.1f); +} + +void AgSpiderBossMessage::OnOffCollisionPhantom(Entity* self, Entity* target) { + if (!target) return; + auto box = GetBox(self); + box.isTouch = false; + box.Reset(); + SetBox(self, box); +} + +void AgSpiderBossMessage::OnTimerDone(Entity* self, std::string timerName) { + if (timerName == "BoxTimer") { + auto box = GetBox(self); + box.isDisplayed = false; + SetBox(self, box); + ResetBox(self); + } else if (timerName == "EventTimer") { + auto box = GetBox(self); + MakeBox(self); + } +} + +void AgSpiderBossMessage::ResetBox(Entity* self) const { + auto box = GetBox(self); + box.Reset(); + SetBox(self, box); +} diff --git a/dScripts/ai/AG/AgSpiderBossMessage.h b/dScripts/ai/AG/AgSpiderBossMessage.h new file mode 100644 index 000000000..70a2fbee3 --- /dev/null +++ b/dScripts/ai/AG/AgSpiderBossMessage.h @@ -0,0 +1,37 @@ +#ifndef AGSPIDERBOSSMESSAGE_H +#define AGSPIDERBOSSMESSAGE_H + +#include "CppScripts.h" + +struct Box { + LWOOBJID boxTarget{}; + bool isDisplayed{}; + bool isTouch{}; + bool isFirst{}; + LWOOBJID boxSelf{}; + std::u16string boxText{}; + int32_t boxTime{ 1 }; + + void Reset() { + boxTarget = LWOOBJID_EMPTY; + isDisplayed = false; + isTouch = false; + isFirst = false; + boxSelf = LWOOBJID_EMPTY; + boxText.clear(); + boxTime = 1; + } +}; + +class AgSpiderBossMessage : public CppScripts::Script { +public: + Box GetBox(Entity* self) const; + void SetBox(Entity* self, const Box& box) const; + void MakeBox(Entity* self) const; + void OnCollisionPhantom(Entity* self, Entity* target) override; + void OnOffCollisionPhantom(Entity* self, Entity* target) override; + void OnTimerDone(Entity* self, std::string timerName) override; + void ResetBox(Entity* self) const; +}; + +#endif //!AGSPIDERBOSSMESSAGE_H diff --git a/dScripts/ai/AG/CMakeLists.txt b/dScripts/ai/AG/CMakeLists.txt index 101f86fd5..237e266a5 100644 --- a/dScripts/ai/AG/CMakeLists.txt +++ b/dScripts/ai/AG/CMakeLists.txt @@ -1,6 +1,7 @@ set(DSCRIPTS_SOURCES_AI_AG "AgShipPlayerDeathTrigger.cpp" "AgSpaceStuff.cpp" + "AgSpiderBossMessage.cpp" "AgShipShake.cpp" "AgShipPlayerShockServer.cpp" "AgImagSmashable.cpp"