gdle/Source/WeenieObject_Generate.cpp
2020-07-18 01:04:04 +00:00

491 lines
13 KiB
C++

#include <StdAfx.h>
#include "WeenieObject.h"
#include "PhysicsObj.h"
#include "ObjectMsgs.h"
#include "World.h"
#include "Lifestone.h"
#include "UseManager.h"
#include "SpellcastingManager.h"
#include "AttackManager.h"
#include "WeenieFactory.h"
#include "BinaryWriter.h"
#include "ChatMsgs.h"
#include "HouseManager.h"
#include "Player.h"
#include "Client.h"
#include "ClientCommands.h"
#include "MonsterAI.h"
#include "AllegianceManager.h"
#include "SpellcastingManager.h"
#include "GameEventManager.h"
#include "EmoteManager.h"
#include "WorldLandBlock.h"
#include "InferredPortalData.h"
#include "MovementManager.h"
#include "Movement.h"
#include "WClassID.h"
#include "InferredPortalData.h"
#include "Config.h"
#include "House.h"
#include "Ammunition.h"
void CWeenieObject::EnsureLink(CWeenieObject *source)
{
if (GeneratorTable *table = m_Qualities._generator_table)
{
source->m_Qualities.SetInstanceID(GENERATOR_IID, GetID());
// linkable gens have a placeholder item that defines how long to wait
// the regen interval on these is faster than the placeholder delay
// we either need to have the placeholder stored on load or find/copy that node
// in the table to have a proper delay on linked items
// the assumption is linkable gens have the placeholder as the first item
uint32_t length = (uint32_t)table->_profile_list.size();
GeneratorProfile prof;
if (length > 0)
{
prof = *table->_profile_list.begin();
}
prof.slot = length;
prof.type = source->m_Qualities.id;
prof.ptid = source->m_Qualities.GetInt(PALETTE_TEMPLATE_INT, 0);
prof.shade = source->m_Qualities.GetFloat(SHADE_FLOAT, 0.0);
prof.pos_val = source->GetPosition();
table->_profile_list.push_back(prof);
// some of these have defined init/max values that don't make sense
// so update the max-gen with the length of the table
m_Qualities.SetInt(MAX_GENERATED_OBJECTS_INT, length + 1);
GeneratorAddToRegistry(source, prof);
}
else
{
// probably should check if it's zero or not
source->m_Qualities.SetInstanceID(ACTIVATION_TARGET_IID, GetID());
}
}
void CWeenieObject::NotifyGeneratedFailure(CWeenieObject *weenie)
{
// Don't erase entry from m_GeneratorSpawns here as NotifyGeneratedDeath still runs after this.
OnGeneratedFailure(weenie);
}
void CWeenieObject::OnGeneratedFailure(CWeenieObject *weenie)
{
if (!weenie)
return;
uint32_t weenie_id = weenie->GetID();
if (!m_Qualities._generator_registry)
return;
GeneratorRegistryNode *node = m_Qualities._generator_registry->_registry.lookup(weenie_id);
if (node)
{
// Remove from the registry here as we don't want OnGeneratedDeath to trigger the respawn delay.
m_Qualities._generator_registry->_registry.remove(weenie->GetID());
if (!m_Qualities._generator_queue)
m_Qualities._generator_queue = new GeneratorQueue();
// Ensure this node is not stuck in queue
GeneratorQueueNode queueNode;
queueNode.slot = node->slot;
queueNode.when = Timer::cur_time;
m_Qualities._generator_queue->_queue.push_back(queueNode);
}
// Set next regen to try again.
_nextRegen = Timer::cur_time;
}
void CWeenieObject::NotifyGeneratedDeath(CWeenieObject *weenie)
{
OnGeneratedDestruction(weenie, (RegenerationType)(Death_RegenerationType | Destruction_RegenerationType));
m_GeneratorSpawns.erase(weenie->GetID());
}
void CWeenieObject::NotifyGeneratedPickedUp(CWeenieObject *weenie)
{
OnGeneratedDestruction(weenie, (RegenerationType)(PickUp_RegenerationType | Destruction_RegenerationType));
weenie->m_Qualities.RemoveInstanceID(GENERATOR_IID);
m_GeneratorSpawns.erase(weenie->GetID());
}
void CWeenieObject::OnGeneratedDestruction(CWeenieObject *weenie, RegenerationType flags)
{
if (!weenie || !m_Qualities._generator_registry)
return;
GeneratorRemoveNode(weenie->GetID(), flags);
if (InqIIDQuality(GENERATOR_IID, 0)) // we have a generator
{
std::vector<CWeenieObject *> rotList;
bool hasValidChildren = false;
if (m_Qualities._generator_registry->_registry.size() > 0)
{
for (auto entry : m_Qualities._generator_registry->_registry)
{
CWeenieObject *weenie = g_pWorld->FindObject(entry.second.m_objectId);
if (weenie && (weenie->IsCreature() || !weenie->IsStuck())) //stuck objects(chests and decorations usually) do not prevent the generator from being finished.
hasValidChildren = true;
else if (weenie)
rotList.push_back(weenie);
}
}
if (!hasValidChildren)
{
//make the leftovers rot.
for (auto entry : rotList)
{
entry->m_Qualities.SetInstanceID(GENERATOR_IID, 0);
entry->_timeToRot = Timer::cur_time + 300.0; //in 5 minutes
entry->_beganRot = false;
entry->m_Qualities.SetFloat(TIME_TO_ROT_FLOAT, _timeToRot);
}
//we're the child of a generator and all our children have been destroyed/picked up.
//so we're done and should cease to exist.
if (!IsCreature())
{
MarkForDestroy();
}
}
}
}
void CWeenieObject::GeneratorRemoveNode(uint32_t weenie_id, RegenerationType flags)
{
if (!weenie_id || !m_Qualities._generator_registry)
return;
GeneratorRegistryNode *node = m_Qualities._generator_registry->_registry.lookup(weenie_id);
if (node)
{
GeneratorRegistryNode oldNode = *node;
m_Qualities._generator_registry->_registry.remove(weenie_id);
double delay = -1.0;
if (m_Qualities._generator_table)
{
// find this profile slot
for (auto &profile : m_Qualities._generator_table->_profile_list)
{
if (profile.slot == oldNode.slot && (profile.whenCreate & flags) != 0)
{
delay = profile.delay * g_pConfig->RespawnTimeMultiplier();
break;
}
}
}
if (delay >= 0)
{
if (!m_Qualities._generator_queue)
m_Qualities._generator_queue = new GeneratorQueue();
GeneratorQueueNode queueNode;
queueNode.slot = oldNode.slot;
queueNode.when = Timer::cur_time + delay;
m_Qualities._generator_queue->_queue.push_back(queueNode);
if (_nextRegen < 0.0)
_nextRegen = Timer::cur_time + (InqFloatQuality(REGENERATION_INTERVAL_FLOAT, 0.0, TRUE) * g_pConfig->RespawnTimeMultiplier());
}
}
}
void CWeenieObject::GeneratorAddToRegistry(CWeenieObject *source, const GeneratorProfile &profile)
{
if (!m_Qualities._generator_registry)
m_Qualities._generator_registry = new GeneratorRegistry();
GeneratorRegistryNode node;
node.m_wcidOrTtype = profile.type;
node.slot = profile.slot;
node.ts = Timer::cur_time;
node.amount = source->InqIntQuality(STACK_SIZE_INT, 1);
node.checkpointed = 0;
node.m_bTreasureType = profile.IsTreasureType();
node.shop = 0;
node.m_objectId = source->GetID();
m_Qualities._generator_registry->_registry.add(source->GetID(), &node);
m_GeneratorSpawns[source->GetID()] = source->m_Qualities.id;
source->ChanceExecuteEmoteSet(GetID(), Generation_EmoteCategory);
//Linkable generators do not have a _nextRegen time when the generator_queue is empty
//and they have spawns in the world. Regular generators should not have a _nextRegen
//time if they have more spawns than MAX_GENERATED_OBJECTS_INT.
int numSpawned = m_Qualities._generator_registry->_registry.size();
int maxSpawn = InqIntQuality(MAX_GENERATED_OBJECTS_INT, 0, TRUE);
bool queueEmpty = (!m_Qualities._generator_queue || m_Qualities._generator_queue->_queue.empty());
if (numSpawned >= maxSpawn && (maxSpawn > 0 || queueEmpty))
_nextRegen = -1.0;
else
_nextRegen = Timer::cur_time + (InqFloatQuality(REGENERATION_INTERVAL_FLOAT, -1.0, TRUE) * g_pConfig->RespawnTimeMultiplier());
}
bool CWeenieObject::IsGeneratorSlotReady(int slot)
{
if (m_Qualities._generator_registry)
{
for (auto &entry : m_Qualities._generator_registry->_registry)
{
if (entry.second.slot == slot)
{
return false;
}
}
}
if (m_Qualities._generator_queue)
{
for (auto &entry : m_Qualities._generator_queue->_queue)
{
if (entry.slot == slot && entry.when > Timer::cur_time)
{
return false;
}
}
}
return true;
}
void CWeenieObject::InitCreateGenerator(double adjust)
{
double delay = 0.0;
if (m_Qualities.InqFloat(GENERATOR_INITIAL_DELAY_FLOAT, delay, TRUE))
{
// if this event started in the past, we need to adjust the delay to account for
// the event start time, not this object becoming active
delay += adjust;
_nextRegen = Timer::cur_time + (delay * g_pConfig->RespawnTimeMultiplier());
}
else
{
UpdateGenerator(true);
}
//{
// if (g_pGameEventManager->IsEventStarted(eventString.c_str()))
// g_pWeenieFactory->AddFromGeneratorTable(this, true);
//}
//else
// g_pWeenieFactory->AddFromGeneratorTable(this, true);
}
void CWeenieObject::UpdateGenerator(bool init)
{
if (ShouldDestroy()) return;
if (!m_Qualities._generator_table) return;
if (InqBoolQuality(GENERATOR_DISABLED_BOOL, FALSE) == TRUE) return;
uint32_t timeType = InqIntQuality(GENERATOR_TIME_TYPE_INT, 0, TRUE);
// REGENERATION_TIMESTAMP_FLOAT
// GENERATOR_UPDATE_TIMESTAMP_FLOAT
bool reset = false;
switch (timeType)
{
case 4: // night
case 5: // day
//reset = !GameTime::IsNight();
reset = GameTime::IsNight();
if (timeType == 4) reset = !reset;
double lastUpdate = InqFloatQuality(GENERATOR_UPDATE_TIMESTAMP_FLOAT, 0, TRUE);
if (lastUpdate < GameTime::LastCycleChange())
{
// set our nextRegen time a short time in the future
_nextRegen = Timer::cur_time + Random::GenFloat(0.001, 1.0);
m_Qualities.SetFloat(GENERATOR_UPDATE_TIMESTAMP_FLOAT, GameTime::LastCycleChange());
}
break;
}
if (reset)
{
DestroyGenerated();
_nextRegen = -1;
return;
}
if (init)
{
g_pWeenieFactory->AddFromGeneratorTable(this, true);
return;
}
// not time to spawn more
if (/*_nextRegen < 0 || */_nextRegen > Timer::cur_time)
return;
double regenInterval = 0.0;
if (!m_Qualities.InqFloat(REGENERATION_INTERVAL_FLOAT, regenInterval) || !(regenInterval > 0.0))
{
// we don't regen, so don't do any more checks
return;
}
int maxSpawn = InqIntQuality(MAX_GENERATED_OBJECTS_INT, 0, TRUE);
int numSpawned = m_GeneratorSpawns.size();
//check if the number spawned is higher than the max_generated_objects. Linkable generators have a max of 0, so check if there is an existing _generator_queue instead.
if (numSpawned < maxSpawn || maxSpawn == 0)
{
bool emptyQueue = !m_Qualities._generator_queue || m_Qualities._generator_queue->_queue.empty();
if (!emptyQueue)
{
PackableList<GeneratorQueueNode> &queue = m_Qualities._generator_queue->_queue;
for (auto entry = queue.begin(); entry != queue.end();)
{
if (entry->when <= Timer::cur_time)
{
entry = queue.erase(entry);
continue;
}
entry++;
}
}
numSpawned = g_pWeenieFactory->AddFromGeneratorTable(this, false);
}
}
void CWeenieObject::GenerateOnDemand(int amount) //Called by Emote_Type 72 Generator
{
if (amount != 0)
{
for (int i = 0; i < amount; i++)
g_pWeenieFactory->AddFromGeneratorTable(this, false);
}
else if (m_Qualities._generator_registry)
{
while (!m_Qualities._generator_registry->_registry.empty())
{
uint32_t weenie_id = m_Qualities._generator_registry->_registry.begin()->first;
if (CWeenieObject *spawned_weenie = g_pWorld->FindObject(weenie_id))
{
//erase weenie from generator registry to stop spawning new weenies.
m_Qualities._generator_registry->_registry.erase(weenie_id);
}
}
if (m_Qualities._generator_queue)
{
m_Qualities._generator_queue->_queue.clear();
}
}
}
void CWeenieObject::CheckEventState(std::string eventName, GameEventDef *event)
{
std::string eventString;
if (!m_Qualities.InqString(GENERATOR_EVENT_STRING, eventString))
return;
if (event->IsStarted())
{
double start = event->GetStarted();
TaskSchedule(Random::GenFloat(0.001, 1.0), [&]()
{
m_Qualities.SetBool(GENERATOR_DISABLED_BOOL, FALSE);
InitCreateGenerator(start - g_pGlobals->Time());
});
}
else
{
m_Qualities.SetBool(GENERATOR_DISABLED_BOOL, TRUE);
TaskSchedule(Random::GenFloat(0.001, 1.0), [&]()
{
DestroyGenerated();
});
}
}
void CWeenieObject::DestroyGenerated()
{
int destroy = 0;
m_Qualities.InqInt(GENERATOR_END_DESTRUCTION_TYPE_INT, destroy);
if (m_Qualities._generator_registry)
{
auto reg = m_Qualities._generator_registry->_registry;
auto itr = reg.begin();
while (itr != reg.end())
{
uint32_t id = itr->first;
CWeenieObject * weenie = g_pWorld->FindObject(id);
if (weenie && ((GeneratorDestruct)destroy == Kill_GeneratorDestruct || (GeneratorDestruct)destroy == Destroy_GeneratorDestruct))
{
weenie->MarkForDestroy();
}
reg.remove(id);
m_GeneratorSpawns.erase(id);
itr = reg.begin();
}
}
if (m_Qualities._generator_queue)
{
m_Qualities._generator_queue->_queue.clear();
}
}
void CWeenieObject::HandleEventActive()
{
// if we have any spawns, we shouldn't be needing activation
if (m_Qualities._generator_registry && !m_Qualities._generator_registry->_registry.empty())
return;
if (m_Qualities._generator_queue && !m_Qualities._generator_queue->_queue.empty())
return;
g_pWeenieFactory->AddFromGeneratorTable(this, false);
}
void CWeenieObject::HandleEventInactive()
{
if (m_Qualities._generator_registry)
{
while (!m_Qualities._generator_registry->_registry.empty())
{
uint32_t weenie_id = m_Qualities._generator_registry->_registry.begin()->first;
if (CWeenieObject *spawned_weenie = g_pWorld->FindObject(weenie_id))
{
spawned_weenie->MarkForDestroy();
}
// make sure it's gone (it should be already.)
m_Qualities._generator_registry->_registry.erase(weenie_id);
}
}
if (m_Qualities._generator_queue)
{
m_Qualities._generator_queue->_queue.clear();
}
}