2020-03-16 18:15:40 +00:00
# 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 ) ;
2020-07-08 15:23:16 -07:00
// 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 ) ;
2020-03-16 18:15:40 +00:00
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 ;
}
2020-04-20 23:56:09 -07:00
void CWeenieObject : : InitCreateGenerator ( double adjust )
2020-03-16 18:15:40 +00:00
{
double delay = 0.0 ;
if ( m_Qualities . InqFloat ( GENERATOR_INITIAL_DELAY_FLOAT , delay , TRUE ) )
{
2020-04-20 23:56:09 -07:00
// 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 ;
2020-03-16 18:15:40 +00:00
_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 ) ;
2020-07-07 20:02:06 +00:00
// REGENERATION_TIMESTAMP_FLOAT
// GENERATOR_UPDATE_TIMESTAMP_FLOAT
2020-03-16 18:15:40 +00:00
bool reset = false ;
switch ( timeType )
{
case 4 : // night
case 5 : // day
2020-07-07 20:02:06 +00:00
//reset = !GameTime::IsNight();
2020-03-16 18:15:40 +00:00
reset = GameTime : : IsNight ( ) ;
2020-07-07 20:02:06 +00:00
if ( timeType = = 4 ) reset = ! reset ;
2020-03-16 18:15:40 +00:00
2020-07-07 20:02:06 +00:00
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 ( ) ) ;
}
2020-03-16 18:15:40 +00:00
break ;
}
if ( reset )
{
2020-04-20 23:56:09 -07:00
DestroyGenerated ( ) ;
2020-07-07 20:02:06 +00:00
_nextRegen = - 1 ;
2020-03-16 18:15:40 +00:00
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 + + ;
}
}
2020-07-07 20:02:06 +00:00
numSpawned = g_pWeenieFactory - > AddFromGeneratorTable ( this , false ) ;
2020-03-16 18:15:40 +00:00
}
}
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 ( ) ;
}
}
}
2020-04-20 23:56:09 -07:00
void CWeenieObject : : CheckEventState ( std : : string eventName , GameEventDef * event )
2020-03-16 18:15:40 +00:00
{
2020-04-20 23:56:09 -07:00
std : : string eventString ;
if ( ! m_Qualities . InqString ( GENERATOR_EVENT_STRING , eventString ) )
return ;
if ( event - > IsStarted ( ) )
{
2020-07-07 20:02:06 +00:00
double start = event - > GetStarted ( ) ;
TaskSchedule ( Random : : GenFloat ( 0.001 , 1.0 ) , [ & ] ( )
{
m_Qualities . SetBool ( GENERATOR_DISABLED_BOOL , FALSE ) ;
InitCreateGenerator ( start - g_pGlobals - > Time ( ) ) ;
} ) ;
2020-04-20 23:56:09 -07:00
}
else
{
m_Qualities . SetBool ( GENERATOR_DISABLED_BOOL , TRUE ) ;
2020-07-07 20:02:06 +00:00
TaskSchedule ( Random : : GenFloat ( 0.001 , 1.0 ) , [ & ] ( )
{
DestroyGenerated ( ) ;
} ) ;
2020-04-20 23:56:09 -07:00
}
2020-03-16 18:15:40 +00:00
}
2020-04-20 23:56:09 -07:00
void CWeenieObject : : DestroyGenerated ( )
{
2020-05-14 17:24:45 -07:00
int destroy = 0 ;
m_Qualities . InqInt ( GENERATOR_END_DESTRUCTION_TYPE_INT , destroy ) ;
2020-05-24 22:25:54 -07:00
2020-04-20 23:56:09 -07:00
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 ) ;
2020-05-24 22:25:54 -07:00
if ( weenie & & ( ( GeneratorDestruct ) destroy = = Kill_GeneratorDestruct | | ( GeneratorDestruct ) destroy = = Destroy_GeneratorDestruct ) )
2020-04-20 23:56:09 -07:00
{
2020-05-14 17:24:45 -07:00
weenie - > MarkForDestroy ( ) ;
2020-04-20 23:56:09 -07:00
}
reg . remove ( id ) ;
m_GeneratorSpawns . erase ( id ) ;
itr = reg . begin ( ) ;
}
}
if ( m_Qualities . _generator_queue )
{
m_Qualities . _generator_queue - > _queue . clear ( ) ;
}
}
2020-03-16 18:15:40 +00:00
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 ( ) ;
}
}