better vanity checks (#1666)

tested that vanity npcs now chat when close, and then on a cooldown
This commit is contained in:
David Markowitz 2024-12-08 14:27:04 -08:00 committed by GitHub
parent 1644d9448d
commit aa7c3b9061
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 47 additions and 27 deletions

View File

@ -83,6 +83,7 @@
#include "ItemComponent.h" #include "ItemComponent.h"
#include "GhostComponent.h" #include "GhostComponent.h"
#include "AchievementVendorComponent.h" #include "AchievementVendorComponent.h"
#include "VanityUtilities.h"
// Table includes // Table includes
#include "CDComponentsRegistryTable.h" #include "CDComponentsRegistryTable.h"
@ -1271,6 +1272,7 @@ void Entity::Update(const float deltaTime) {
auto timerName = timer.GetName(); auto timerName = timer.GetName();
m_Timers.erase(m_Timers.begin() + timerPosition); m_Timers.erase(m_Timers.begin() + timerPosition);
GetScript()->OnTimerDone(this, timerName); GetScript()->OnTimerDone(this, timerName);
VanityUtilities::OnTimerDone(this, timerName);
TriggerEvent(eTriggerEventType::TIMER_DONE, this); TriggerEvent(eTriggerEventType::TIMER_DONE, this);
} else { } else {
@ -1334,6 +1336,7 @@ void Entity::OnCollisionProximity(LWOOBJID otherEntity, const std::string& proxN
if (!other) return; if (!other) return;
GetScript()->OnProximityUpdate(this, other, proxName, status); GetScript()->OnProximityUpdate(this, other, proxName, status);
VanityUtilities::OnProximityUpdate(this, other, proxName, status);
RocketLaunchpadControlComponent* rocketComp = GetComponent<RocketLaunchpadControlComponent>(); RocketLaunchpadControlComponent* rocketComp = GetComponent<RocketLaunchpadControlComponent>();
if (!rocketComp) return; if (!rocketComp) return;

View File

@ -38,7 +38,7 @@ void ProximityMonitorComponent::SetProximityRadius(dpEntity* entity, const std::
m_ProximitiesData.insert(std::make_pair(name, entity)); m_ProximitiesData.insert(std::make_pair(name, entity));
} }
const std::unordered_set<LWOOBJID>& ProximityMonitorComponent::GetProximityObjects(const std::string& name) { const std::unordered_set<LWOOBJID>& ProximityMonitorComponent::GetProximityObjects(const std::string& name) const {
const auto iter = m_ProximitiesData.find(name); const auto iter = m_ProximitiesData.find(name);
if (iter == m_ProximitiesData.cend()) { if (iter == m_ProximitiesData.cend()) {

View File

@ -46,7 +46,7 @@ public:
* @param name the proximity name to retrieve physics objects for * @param name the proximity name to retrieve physics objects for
* @return a set of physics entity object IDs for this name * @return a set of physics entity object IDs for this name
*/ */
const std::unordered_set<LWOOBJID>& GetProximityObjects(const std::string& name); const std::unordered_set<LWOOBJID>& GetProximityObjects(const std::string& name) const;
/** /**
* Checks if the passed object is in proximity of the named proximity sensor * Checks if the passed object is in proximity of the named proximity sensor

View File

@ -59,9 +59,9 @@ void VanityUtilities::SpawnVanity() {
for (const auto& npc : objects) { for (const auto& npc : objects) {
if (npc.m_ID == LWOOBJID_EMPTY) continue; if (npc.m_ID == LWOOBJID_EMPTY) continue;
if (npc.m_LOT == 176){ if (npc.m_LOT == 176) {
Game::zoneManager->RemoveSpawner(npc.m_ID); Game::zoneManager->RemoveSpawner(npc.m_ID);
} else{ } else {
auto* entity = Game::entityManager->GetEntity(npc.m_ID); auto* entity = Game::entityManager->GetEntity(npc.m_ID);
if (!entity) continue; if (!entity) continue;
entity->Smash(LWOOBJID_EMPTY, eKillType::VIOLENT); entity->Smash(LWOOBJID_EMPTY, eKillType::VIOLENT);
@ -86,14 +86,14 @@ void VanityUtilities::SpawnVanity() {
float rate = GeneralUtils::GenerateRandomNumber<float>(0, 1); float rate = GeneralUtils::GenerateRandomNumber<float>(0, 1);
if (location.m_Chance < rate) continue; if (location.m_Chance < rate) continue;
if (object.m_LOT == 176){ if (object.m_LOT == 176) {
object.m_ID = SpawnSpawner(object, location); object.m_ID = SpawnSpawner(object, location);
} else { } else {
// Spawn the NPC // Spawn the NPC
auto* objectEntity = SpawnObject(object, location); auto* objectEntity = SpawnObject(object, location);
if (!objectEntity) continue; if (!objectEntity) continue;
object.m_ID = objectEntity->GetObjectID(); object.m_ID = objectEntity->GetObjectID();
if (!object.m_Phrases.empty()){ if (!object.m_Phrases.empty()) {
objectEntity->SetVar<std::vector<std::string>>(u"chats", object.m_Phrases); objectEntity->SetVar<std::vector<std::string>>(u"chats", object.m_Phrases);
SetupNPCTalk(objectEntity); SetupNPCTalk(objectEntity);
} }
@ -107,7 +107,7 @@ LWOOBJID SpawnSpawner(const VanityObject& object, const VanityObjectLocation& lo
// guratantee we have no collisions // guratantee we have no collisions
do { do {
obj.id = ObjectIDManager::GenerateObjectID(); obj.id = ObjectIDManager::GenerateObjectID();
} while(Game::zoneManager->GetSpawner(obj.id)); } while (Game::zoneManager->GetSpawner(obj.id));
obj.position = location.m_Position; obj.position = location.m_Position;
obj.rotation = location.m_Rotation; obj.rotation = location.m_Rotation;
obj.settings = object.m_Config; obj.settings = object.m_Config;
@ -146,7 +146,7 @@ Entity* SpawnObject(const VanityObject& object, const VanityObjectLocation& loca
} }
void ParseXml(const std::string& file) { void ParseXml(const std::string& file) {
if (loadedFiles.contains(file)){ if (loadedFiles.contains(file)) {
LOG("Trying to load vanity file %s twice!!!", file.c_str()); LOG("Trying to load vanity file %s twice!!!", file.c_str());
return; return;
} }
@ -232,7 +232,7 @@ void ParseXml(const std::string& file) {
auto* configElement = object->FirstChildElement("config"); auto* configElement = object->FirstChildElement("config");
std::vector<std::u16string> keys = {}; std::vector<std::u16string> keys = {};
std::vector<LDFBaseData*> config = {}; std::vector<LDFBaseData*> config = {};
if(configElement) { if (configElement) {
for (auto* key = configElement->FirstChildElement("key"); key != nullptr; for (auto* key = configElement->FirstChildElement("key"); key != nullptr;
key = key->NextSiblingElement("key")) { key = key->NextSiblingElement("key")) {
// Get the config data // Get the config data
@ -240,7 +240,7 @@ void ParseXml(const std::string& file) {
if (!data) continue; if (!data) continue;
LDFBaseData* configData = LDFBaseData::DataFromString(data); LDFBaseData* configData = LDFBaseData::DataFromString(data);
if (configData->GetKey() == u"useLocationsAsRandomSpawnPoint" && configData->GetValueType() == eLDFType::LDF_TYPE_BOOLEAN){ if (configData->GetKey() == u"useLocationsAsRandomSpawnPoint" && configData->GetValueType() == eLDFType::LDF_TYPE_BOOLEAN) {
useLocationsAsRandomSpawnPoint = static_cast<bool>(configData); useLocationsAsRandomSpawnPoint = static_cast<bool>(configData);
continue; continue;
} }
@ -250,7 +250,7 @@ void ParseXml(const std::string& file) {
} }
if (!keys.empty()) config.push_back(new LDFData<std::vector<std::u16string>>(u"syncLDF", keys)); if (!keys.empty()) config.push_back(new LDFData<std::vector<std::u16string>>(u"syncLDF", keys));
VanityObject objectData { VanityObject objectData{
.m_Name = name, .m_Name = name,
.m_LOT = lot, .m_LOT = lot,
.m_Equipment = inventory, .m_Equipment = inventory,
@ -288,7 +288,7 @@ void ParseXml(const std::string& file) {
continue; continue;
} }
VanityObjectLocation locationData { VanityObjectLocation locationData{
.m_Position = { x.value(), y.value(), z.value() }, .m_Position = { x.value(), y.value(), z.value() },
.m_Rotation = { rw.value(), rx.value(), ry.value(), rz.value() }, .m_Rotation = { rw.value(), rx.value(), ry.value(), rz.value() },
}; };
@ -403,26 +403,39 @@ void SetupNPCTalk(Entity* npc) {
npc->SetProximityRadius(20.0f, "talk"); npc->SetProximityRadius(20.0f, "talk");
} }
void NPCTalk(Entity* npc) { void VanityUtilities::OnProximityUpdate(Entity* entity, Entity* other, const std::string& proxName, const std::string& name) {
auto* proximityMonitorComponent = npc->GetComponent<ProximityMonitorComponent>(); if (proxName != "talk") return;
const auto* const proximityMonitorComponent = entity->GetComponent<ProximityMonitorComponent>();
if (!proximityMonitorComponent) return;
if (!proximityMonitorComponent->GetProximityObjects("talk").empty()) { if (name == "ENTER" && !entity->HasTimer("talk")) {
NPCTalk(entity);
}
}
void VanityUtilities::OnTimerDone(Entity* npc, const std::string& name) {
if (name == "talk") {
const auto* const proximityMonitorComponent = npc->GetComponent<ProximityMonitorComponent>();
if (!proximityMonitorComponent || proximityMonitorComponent->GetProximityObjects("talk").empty()) return;
NPCTalk(npc);
}
}
void NPCTalk(Entity* npc) {
const auto& chats = npc->GetVar<std::vector<std::string>>(u"chats"); const auto& chats = npc->GetVar<std::vector<std::string>>(u"chats");
if (chats.empty()) { if (chats.empty()) return;
return;
}
const auto& selected const auto& selected
= chats[GeneralUtils::GenerateRandomNumber<int32_t>(0, static_cast<int32_t>(chats.size() - 1))]; = chats[GeneralUtils::GenerateRandomNumber<int32_t>(0, static_cast<int32_t>(chats.size() - 1))];
GameMessages::SendNotifyClientZoneObject( GameMessages::SendNotifyClientZoneObject(
npc->GetObjectID(), u"sendToclient_bubble", 0, 0, npc->GetObjectID(), selected, UNASSIGNED_SYSTEM_ADDRESS); npc->GetObjectID(), u"sendToclient_bubble", 0, 0, npc->GetObjectID(), selected, UNASSIGNED_SYSTEM_ADDRESS);
}
Game::entityManager->SerializeEntity(npc); Game::entityManager->SerializeEntity(npc);
const float nextTime = GeneralUtils::GenerateRandomNumber<float>(15, 60); const float nextTime = GeneralUtils::GenerateRandomNumber<float>(15, 60);
npc->AddCallbackTimer(nextTime, [npc]() { NPCTalk(npc); }); npc->AddTimer("talk", nextTime);
} }

View File

@ -31,4 +31,8 @@ namespace VanityUtilities {
std::string ParseMarkdown( std::string ParseMarkdown(
const std::string& file const std::string& file
); );
void OnProximityUpdate(Entity* entity, Entity* other, const std::string& proxName, const std::string& name);
void OnTimerDone(Entity* entity, const std::string& name);
}; };