Implement the Imaginite Backpack and Shard armor scripts (#886)

* Imaginite Pack now works

* Remove unused params

* Address issues

* Add TeslaPack script

Co-authored-by: aronwk-aaron <aronwk.aaron@gmail.com>
This commit is contained in:
David Markowitz 2022-12-21 14:33:41 -08:00 committed by GitHub
parent 51dd56f0a0
commit bd7f532a28
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 249 additions and 1 deletions

View File

@ -176,6 +176,7 @@ set(INCLUDED_DIRECTORIES
"dScripts/ai"
"dScripts/client"
"dScripts/EquipmentScripts"
"dScripts/EquipmentTriggers"
"dScripts/zone"
"dScripts/02_server/DLU"
"dScripts/02_server/Enemy"

View File

@ -824,6 +824,22 @@ std::vector<ScriptComponent*> Entity::GetScriptComponents() {
return comps;
}
void Entity::Subscribe(LWOOBJID scriptObjId, CppScripts::Script* scriptToAdd, const std::string& notificationName) {
if (notificationName == "HitOrHealResult" || notificationName == "Hit") {
auto* destroyableComponent = GetComponent<DestroyableComponent>();
if (!destroyableComponent) return;
destroyableComponent->Subscribe(scriptObjId, scriptToAdd);
}
}
void Entity::Unsubscribe(LWOOBJID scriptObjId, const std::string& notificationName) {
if (notificationName == "HitOrHealResult" || notificationName == "Hit") {
auto* destroyableComponent = GetComponent<DestroyableComponent>();
if (!destroyableComponent) return;
destroyableComponent->Unsubscribe(scriptObjId);
}
}
void Entity::SetProximityRadius(float proxRadius, std::string name) {
ProximityMonitorComponent* proxMon = GetComponent<ProximityMonitorComponent>();
if (!proxMon) {

View File

@ -26,8 +26,13 @@ class Spawner;
class ScriptComponent;
class dpEntity;
class Component;
class Item;
class Character;
namespace CppScripts {
class Script;
};
/**
* An entity in the world. Has multiple components.
*/
@ -139,6 +144,9 @@ public:
std::vector<ScriptComponent*> GetScriptComponents();
void Subscribe(LWOOBJID scriptObjId, CppScripts::Script* scriptToAdd, const std::string& notificationName);
void Unsubscribe(LWOOBJID scriptObjId, const std::string& notificationName);
void SetProximityRadius(float proxRadius, std::string name);
void SetProximityRadius(dpEntity* entity, std::string name);

View File

@ -631,6 +631,7 @@ void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, uint32
auto* attacker = EntityManager::Instance()->GetEntity(source);
m_Parent->OnHit(attacker);
m_Parent->OnHitOrHealResult(attacker, sourceDamage);
NotifySubscribers(attacker, sourceDamage);
for (const auto& cb : m_OnHitCallbacks) {
cb(attacker);
@ -648,6 +649,29 @@ void DestroyableComponent::Damage(uint32_t damage, const LWOOBJID source, uint32
Smash(source, eKillType::VIOLENT, u"", skillID);
}
void DestroyableComponent::Subscribe(LWOOBJID scriptObjId, CppScripts::Script* scriptToAdd) {
m_SubscribedScripts.insert(std::make_pair(scriptObjId, scriptToAdd));
Game::logger->LogDebug("DestroyableComponent", "Added script %llu to entity %llu", scriptObjId, m_Parent->GetObjectID());
Game::logger->LogDebug("DestroyableComponent", "Number of subscribed scripts %i", m_SubscribedScripts.size());
}
void DestroyableComponent::Unsubscribe(LWOOBJID scriptObjId) {
auto foundScript = m_SubscribedScripts.find(scriptObjId);
if (foundScript != m_SubscribedScripts.end()) {
m_SubscribedScripts.erase(foundScript);
Game::logger->LogDebug("DestroyableComponent", "Removed script %llu from entity %llu", scriptObjId, m_Parent->GetObjectID());
} else {
Game::logger->LogDebug("DestroyableComponent", "Tried to remove a script for Entity %llu but script %llu didnt exist", m_Parent->GetObjectID(), scriptObjId);
}
Game::logger->LogDebug("DestroyableComponent", "Number of subscribed scripts %i", m_SubscribedScripts.size());
}
void DestroyableComponent::NotifySubscribers(Entity* attacker, uint32_t damage) {
for (auto script : m_SubscribedScripts) {
script.second->NotifyHitOrHealResult(m_Parent, attacker, damage);
}
}
void DestroyableComponent::Smash(const LWOOBJID source, const eKillType killType, const std::u16string& deathType, uint32_t skillID) {
if (m_iHealth > 0) {
SetArmor(0);

View File

@ -7,6 +7,10 @@
#include "Entity.h"
#include "Component.h"
namespace CppScripts {
class Script;
}; //! namespace CppScripts
/**
* Represents the stats of an entity, for example its health, imagination and armor. Also handles factions, which
* indicate which enemies this entity has.
@ -422,6 +426,17 @@ public:
*/
void AddFactionNoLookup(int32_t faction) { m_FactionIDs.push_back(faction); };
/**
* Notify subscribed scripts of Damage actions.
*
* @param attacker The attacking Entity
* @param damage The amount of damage that was done
*/
void NotifySubscribers(Entity* attacker, uint32_t damage);
void Subscribe(LWOOBJID scriptObjId, CppScripts::Script* scriptToAdd);
void Unsubscribe(LWOOBJID scriptObjId);
private:
/**
* Whether or not the health should be serialized
@ -557,6 +572,11 @@ private:
* The list of callbacks that will be called when this entity gets hit
*/
std::vector<std::function<void(Entity*)>> m_OnHitCallbacks;
/**
* The list of scripts subscribed to this components actions
*/
std::map<LWOOBJID, CppScripts::Script*> m_SubscribedScripts;
};
#endif // DESTROYABLECOMPONENT_H

View File

@ -27,8 +27,9 @@
#include "dConfig.h"
#include "eItemType.h"
#include "eUnequippableActiveType.h"
#include "CppScripts.h"
InventoryComponent::InventoryComponent(Entity* parent, tinyxml2::XMLDocument* document) : Component(parent) {
InventoryComponent::InventoryComponent(Entity* parent, tinyxml2::XMLDocument* document): Component(parent) {
this->m_Dirty = true;
this->m_Equipped = {};
this->m_Pushed = {};
@ -867,6 +868,8 @@ void InventoryComponent::EquipItem(Item* item, const bool skipChecks) {
AddItemSkills(item->GetLot());
EquipScripts(item);
EntityManager::Instance()->SerializeEntity(m_Parent);
}
@ -895,6 +898,8 @@ void InventoryComponent::UnEquipItem(Item* item) {
PurgeProxies(item);
UnequipScripts(item);
EntityManager::Instance()->SerializeEntity(m_Parent);
// Trigger property event
@ -904,6 +909,37 @@ void InventoryComponent::UnEquipItem(Item* item) {
}
}
void InventoryComponent::EquipScripts(Item* equippedItem) {
CDComponentsRegistryTable* compRegistryTable = CDClientManager::Instance()->GetTable<CDComponentsRegistryTable>("ComponentsRegistry");
if (!compRegistryTable) return;
int32_t scriptComponentID = compRegistryTable->GetByIDAndType(equippedItem->GetLot(), COMPONENT_TYPE_SCRIPT, -1);
if (scriptComponentID > -1) {
CDScriptComponentTable* scriptCompTable = CDClientManager::Instance()->GetTable<CDScriptComponentTable>("ScriptComponent");
CDScriptComponent scriptCompData = scriptCompTable->GetByID(scriptComponentID);
auto* itemScript = CppScripts::GetScript(m_Parent, scriptCompData.script_name);
if (!itemScript) {
Game::logger->Log("InventoryComponent", "null script?");
}
itemScript->OnFactionTriggerItemEquipped(m_Parent, equippedItem->GetId());
}
}
void InventoryComponent::UnequipScripts(Item* unequippedItem) {
CDComponentsRegistryTable* compRegistryTable = CDClientManager::Instance()->GetTable<CDComponentsRegistryTable>("ComponentsRegistry");
if (!compRegistryTable) return;
int32_t scriptComponentID = compRegistryTable->GetByIDAndType(unequippedItem->GetLot(), COMPONENT_TYPE_SCRIPT, -1);
if (scriptComponentID > -1) {
CDScriptComponentTable* scriptCompTable = CDClientManager::Instance()->GetTable<CDScriptComponentTable>("ScriptComponent");
CDScriptComponent scriptCompData = scriptCompTable->GetByID(scriptComponentID);
auto* itemScript = CppScripts::GetScript(m_Parent, scriptCompData.script_name);
if (!itemScript) {
Game::logger->Log("InventoryComponent", "null script?");
}
itemScript->OnFactionTriggerItemUnequipped(m_Parent, unequippedItem->GetId());
}
}
void InventoryComponent::HandlePossession(Item* item) {
auto* characterComponent = m_Parent->GetComponent<CharacterComponent>();
if (!characterComponent) return;

View File

@ -352,6 +352,20 @@ public:
*/
static uint32_t FindSkill(LOT lot);
/**
* Call this when you equip an item. This calls OnFactionTriggerItemEquipped for any scripts found on the items.
*
* @param equippedItem The item script to lookup and call equip on
*/
void EquipScripts(Item* equippedItem);
/**
* Call this when you unequip an item. This calls OnFactionTriggerItemUnequipped for any scripts found on the items.
*
* @param unequippedItem The item script to lookup and call unequip on
*/
void UnequipScripts(Item* unequippedItem);
~InventoryComponent() override;
private:

View File

@ -39,6 +39,12 @@ foreach(file ${DSCRIPTS_SOURCES_EQUIPMENTSCRIPTS})
set(DSCRIPTS_SOURCES ${DSCRIPTS_SOURCES} "EquipmentScripts/${file}")
endforeach()
add_subdirectory(EquipmentTriggers)
foreach(file ${DSCRIPTS_SOURCES_EQUIPMENTTRIGGERSSCRIPTS})
set(DSCRIPTS_SOURCES ${DSCRIPTS_SOURCES} "EquipmentTriggers/${file}")
endforeach()
add_subdirectory(zone)
foreach(file ${DSCRIPTS_SOURCES_ZONE})

View File

@ -278,6 +278,9 @@
#include "ImaginationBackpackHealServer.h"
#include "LegoDieRoll.h"
#include "BuccaneerValiantShip.h"
#include "GemPack.h"
#include "ShardArmor.h"
#include "TeslaPack.h"
// Survival scripts
#include "AgSurvivalStromling.h"
@ -837,6 +840,12 @@ CppScripts::Script* CppScripts::GetScript(Entity* parent, const std::string& scr
script = new BuccaneerValiantShip();
else if (scriptName == "scripts\\EquipmentScripts\\FireFirstSkillonStartup.lua")
script = new FireFirstSkillonStartup();
else if (scriptName == "scripts\\equipmenttriggers\\gempack.lua")
script = new GemPack();
else if (scriptName == "scripts\\equipmenttriggers\\shardarmor.lua")
script = new ShardArmor();
else if (scriptName == "scripts\\equipmenttriggers\\coilbackpack.lua")
script = new TeslaPack();
// FB
else if (scriptName == "scripts\\ai\\NS\\WH\\L_ROCKHYDRANT_BROKEN.lua")

View File

@ -172,6 +172,13 @@ namespace CppScripts {
*/
virtual void OnHitOrHealResult(Entity* self, Entity* attacker, int32_t damage) {};
/**
* Invoked when self has received either a hit or heal. Only used for scripts subscribed to an entity.
*
* Equivalent to 'function notifyHitOrHealResult(self, msg)'
*/
virtual void NotifyHitOrHealResult(Entity* self, Entity* attacker, int32_t damage) {};
/**
* Invoked when a player has responsed to a mission.
*
@ -316,6 +323,22 @@ namespace CppScripts {
virtual void OnCinematicUpdate(Entity* self, Entity* sender, eCinematicEvent event, const std::u16string& pathName,
float_t pathTime, float_t totalTime, int32_t waypoint) {
};
/**
* Used by items to tell their owner that they were equipped.
*
* @param itemOwner The owner of the item
* @param itemObjId The items Object ID
*/
virtual void OnFactionTriggerItemEquipped(Entity* itemOwner, LWOOBJID itemObjId) {};
/**
* Used by items to tell their owner that they were unequipped.
*
* @param itemOwner The owner of the item
* @param itemObjId The items Object ID
*/
virtual void OnFactionTriggerItemUnequipped(Entity* itemOwner, LWOOBJID itemObjId) {};
};
Script* GetScript(Entity* parent, const std::string& scriptName);

View File

@ -0,0 +1,3 @@
set(DSCRIPTS_SOURCES_EQUIPMENTTRIGGERSSCRIPTS
"CoilBackpackBase.cpp"
PARENT_SCOPE)

View File

@ -0,0 +1,25 @@
#include "CoilBackpackBase.h"
#include "Entity.h"
#include "SkillComponent.h"
void CoilBackpackBase::OnFactionTriggerItemEquipped(Entity* itemOwner, LWOOBJID itemObjId) {
itemOwner->Subscribe(itemObjId, this, "HitOrHealResult");
itemOwner->SetVar<uint8_t>(u"coilCount", 0);
}
void CoilBackpackBase::NotifyHitOrHealResult(Entity* self, Entity* attacker, int32_t damage) {
if (damage > 0) {
self->SetVar<uint8_t>(u"coilCount", self->GetVar<uint8_t>(u"coilCount") + 1);
if (self->GetVar<uint8_t>(u"coilCount") > 4) {
auto* skillComponent = self->GetComponent<SkillComponent>();
if (!skillComponent) return;
skillComponent->CalculateBehavior(m_SkillId, m_BehaviorId, self->GetObjectID());
self->SetVar<uint8_t>(u"coilCount", 0);
}
}
}
void CoilBackpackBase::OnFactionTriggerItemUnequipped(Entity* itemOwner, LWOOBJID itemObjId) {
itemOwner->Unsubscribe(itemObjId, "HitOrHealResult");
}

View File

@ -0,0 +1,21 @@
#ifndef __GemPackBase__H__
#define __GemPackBase__H__
#include "CppScripts.h"
class CoilBackpackBase: public CppScripts::Script {
public:
CoilBackpackBase(uint32_t skillId, uint32_t behaviorId) {
m_SkillId = skillId;
m_BehaviorId = behaviorId;
};
void OnFactionTriggerItemEquipped(Entity* itemOwner, LWOOBJID itemObjId) override;
void NotifyHitOrHealResult(Entity* self, Entity* attacker, int32_t damage) override;
void OnFactionTriggerItemUnequipped(Entity* itemOwner, LWOOBJID itemObjId) override;
private:
uint32_t m_SkillId = 0;
uint32_t m_BehaviorId = 0;
};
#endif //!__GemPackBase__H__

View File

@ -0,0 +1,14 @@
#ifndef __GEMPACK__H__
#define __GEMPACK__H__
#include "CoilBackpackBase.h"
class GemPack : public CoilBackpackBase {
public:
GemPack() : CoilBackpackBase(skillId, behaviorId) {};
private:
static const uint32_t skillId = 1488;
static const uint32_t behaviorId = 36779;
};
#endif //!__GEMPACK__H__

View File

@ -0,0 +1,14 @@
#ifndef __SHARDARMOR__H__
#define __SHARDARMOR__H__
#include "CoilBackpackBase.h"
class ShardArmor : public CoilBackpackBase {
public:
ShardArmor() : CoilBackpackBase(skillId, behaviorId) {};
private:
static const uint32_t skillId = 1249;
static const uint32_t behaviorId = 29086;
};
#endif //!__SHARDARMOR__H__

View File

@ -0,0 +1,14 @@
#ifndef __TESLAPACK__H__
#define __TESLAPACK__H__
#include "CoilBackpackBase.h"
class TeslaPack : public CoilBackpackBase {
public:
TeslaPack() : CoilBackpackBase(skillId, behaviorId) {};
private:
static const uint32_t skillId = 1001;
static const uint32_t behaviorId = 20917;
};
#endif //!__TESLAPACK__H__