uox3/source/cSpawnRegion.cpp
Xoduz 9f768a467c Fixed issue with NPCs returning to bounding box, added new Character JS properties
- [ADD] The original spawn location is now stored for NPCs when they spawn
- [ADD] Added new character JS properties: .spawnX, .spawnY and .spawnZ. These are primarily used to store original spawn location of NPCs.
- [FIX] Fixed an issue where NPCs would fail in many cases to pathfind back to their bounding box after leaving it. They now pathfind back to original spawn location instead.
2026-04-27 04:12:54 +08:00

1556 lines
52 KiB
C++

#include "uox3.h"
#include "cSpawnRegion.h"
#include "cServerDefinitions.h"
#include "ssection.h"
#include "cScript.h"
#include "mapstuff.h"
#include "classes.h"
#include "Dictionary.h"
#include "StringUtility.hpp"
#include <sstream>
#include <iostream>
#include <regex>
#include <CJSMapping.h>
using namespace std::string_literals;
const TIMERVAL DEFSPAWN_NEXTTIME = 0;
const UI16 DEFSPAWN_CALL = 1;
const SI16 DEFSPAWN_X1 = 0;
const SI16 DEFSPAWN_X2 = 0;
const SI16 DEFSPAWN_Y1 = 0;
const SI16 DEFSPAWN_Y2 = 0;
const size_t DEFSPAWN_MAXCSPAWN = 0;
const size_t DEFSPAWN_MAXISPAWN = 0;
const UI08 DEFSPAWN_MAXTIME = 0;
const UI08 DEFSPAWN_MINTIME = 0;
const SI32 DEFSPAWN_CURCSPAWN = 0;
const SI32 DEFSPAWN_CURISPAWN = 0;
const UI08 DEFSPAWN_WORLDNUM = 0;
const SI08 DEFSPAWN_PREFZ = 18;
const SI08 DEFSPAWN_DEFZ = ILLEGAL_Z;
const bool DEFSPAWN_ONLYOUTSIDE = false;
const bool DEFSPAWN_ISSPAWNER = false;
const bool DEFSPAWN_FORCESPAWN = false;
//o------------------------------------------------------------------------------------------------o
//| Class - CSpawnRegion()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Constructor for CSpawnRegion class
//o------------------------------------------------------------------------------------------------o
CSpawnRegion::CSpawnRegion( UI16 spawnregion ) : regionNum( spawnregion ), maxCharSpawn( DEFSPAWN_MAXCSPAWN ), maxItemSpawn( DEFSPAWN_MAXISPAWN ),
curCharSpawn( DEFSPAWN_CURCSPAWN ), curItemSpawn( DEFSPAWN_CURISPAWN ), minTime( DEFSPAWN_MINTIME ), maxTime( DEFSPAWN_MAXTIME ),
nextTime( DEFSPAWN_NEXTTIME ), x1( DEFSPAWN_X1 ), x2( DEFSPAWN_X2 ), y1( DEFSPAWN_Y1 ), y2( DEFSPAWN_Y2 ),
prefZ( DEFSPAWN_PREFZ ), defZ( DEFSPAWN_DEFZ ), onlyOutside( DEFSPAWN_ONLYOUTSIDE ), isSpawner( DEFSPAWN_ISSPAWNER ),
call( DEFSPAWN_CALL ), worldNumber( DEFSPAWN_WORLDNUM ), forceSpawn( DEFSPAWN_FORCESPAWN )
{
sItems.resize( 0 );
sNpcs.resize( 0 );
exclusionAreas.resize( 0 );
name = Dictionary->GetEntry( 1117 );
// note: doesn't go here, but i'll see it here. when an item is spawned, as soon as it's moved it needs to lose it's
// spawn setting. If not, then when people pick up spawned items, they will disappear (on region spawns)
}
//o------------------------------------------------------------------------------------------------o
//| Function - CSpawnRegion::~CSpawnRegion()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Deconstructor for CSpawnRegion class
//o------------------------------------------------------------------------------------------------o
CSpawnRegion::~CSpawnRegion()
{
sItems.resize( 0 );
sNpcs.resize( 0 );
exclusionAreas.resize( 0 );
// Wipe out all items and npcs
}
//o------------------------------------------------------------------------------------------------o
//| Function - CSpawnRegion::GetName()
//| CSpawnRegion::SetName()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Gets/Sets spawn region's name
//o------------------------------------------------------------------------------------------------o
const std::string CSpawnRegion::GetName( void ) const
{
return name;
}
void CSpawnRegion::SetName( const std::string& newName )
{
name = newName;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CSpawnRegion::MaxSpawn()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Maximum amount of objects a region can spawn
//o------------------------------------------------------------------------------------------------o
size_t CSpawnRegion::GetMaxSpawn( void ) const
{
return ( maxCharSpawn + maxItemSpawn );
}
//o------------------------------------------------------------------------------------------------o
//| Function - CSpawnRegion::GetMaxCharSpawn()
//| CSpawnRegion::SetMaxCharSpawn()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Gets/Sets maximum amount of characters to spawn
//o------------------------------------------------------------------------------------------------o
size_t CSpawnRegion::GetMaxCharSpawn( void ) const
{
return maxCharSpawn;
}
void CSpawnRegion::SetMaxCharSpawn( size_t newVal )
{
maxCharSpawn = newVal;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CSpawnRegion::GetMaxItemSpawn()
//| CSpawnRegion::SetMaxItemSpawn()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Gets/Sets maximum amount of items to spawn
//o------------------------------------------------------------------------------------------------o
size_t CSpawnRegion::GetMaxItemSpawn( void ) const
{
return maxItemSpawn;
}
void CSpawnRegion::SetMaxItemSpawn( size_t newVal )
{
maxItemSpawn = newVal;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CSpawnRegion::Current()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Current amount of objects spawned
//o------------------------------------------------------------------------------------------------o
SI32 CSpawnRegion::GetCurrent( void ) const
{
return ( curCharSpawn + curItemSpawn );
}
//o------------------------------------------------------------------------------------------------o
//| Function - CSpawnRegion::GetCurrentCharAmt()
//| CSpawnRegion::IncCurrentCharAmt()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Gets/Sets current amount of characters spawned
//o------------------------------------------------------------------------------------------------o
SI32 CSpawnRegion::GetCurrentCharAmt( void ) const
{
return curCharSpawn;
}
void CSpawnRegion::IncCurrentCharAmt( SI16 incAmt )
{
curCharSpawn += incAmt;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CSpawnRegion::GetCurrentItemAmt()
//| CSpawnRegion::IncCurrentItemAmt()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Gets/Sets current amount of items spawned
//o------------------------------------------------------------------------------------------------o
SI32 CSpawnRegion::GetCurrentItemAmt( void ) const
{
return curItemSpawn;
}
void CSpawnRegion::IncCurrentItemAmt( SI16 incAmt )
{
curItemSpawn += incAmt;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CSpawnRegion::GetRegionNum()
//| CSpawnRegion::SetRegionNum()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Gets/Sets spawn region number
//o------------------------------------------------------------------------------------------------o
UI16 CSpawnRegion::GetRegionNum( void ) const
{
return regionNum;
}
void CSpawnRegion::SetRegionNum( UI16 newVal )
{
regionNum = newVal;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CSpawnRegion::GetMinTime()
//| CSpawnRegion::SetMinTime()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Gets/Sets minimum amount of time to pass before a spawnregion
//| spawns a new object
//o------------------------------------------------------------------------------------------------o
UI08 CSpawnRegion::GetMinTime( void ) const
{
return minTime;
}
void CSpawnRegion::SetMinTime( UI08 newVal )
{
minTime = newVal;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CSpawnRegion::GetMaxTime()
//| CSpawnRegion::SetMaxTime()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Gets/Sets maximum amount of time to pass before a spawnregion
//| spawns a new object
//o------------------------------------------------------------------------------------------------o
UI08 CSpawnRegion::GetMaxTime( void ) const
{
return maxTime;
}
void CSpawnRegion::SetMaxTime( UI08 newVal )
{
maxTime = newVal;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CSpawnRegion::GetNextTime()
//| CSpawnRegion::SetNextTime()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Gets/Sets time for when a spawnregion will next spawn a new object
//o------------------------------------------------------------------------------------------------o
TIMERVAL CSpawnRegion::GetNextTime( void ) const
{
return nextTime;
}
void CSpawnRegion::SetNextTime( TIMERVAL newVal )
{
nextTime = newVal;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CSpawnRegion::SetPrefZ()
//| CSpawnRegion::GetPrefZ()
//| Date - 04/22/2002
//o------------------------------------------------------------------------------------------------o
//| Purpose - Gets/Sets Z Level of the Spawn Region
//o------------------------------------------------------------------------------------------------o
void CSpawnRegion::SetPrefZ( SI08 newVal )
{
prefZ = newVal;
}
SI08 CSpawnRegion::GetPrefZ( void ) const
{
return prefZ;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CSpawnRegion::SetDefZ()
//| CSpawnRegion::GetDefZ()
//| Date - 04/22/2002
//o------------------------------------------------------------------------------------------------o
//| Purpose - Gets/Sets definitive Z Level of the Spawn Region at which to attempt spawning objects
//o------------------------------------------------------------------------------------------------o
void CSpawnRegion::SetDefZ( SI08 newVal )
{
defZ = newVal;
}
SI08 CSpawnRegion::GetDefZ( void ) const
{
return defZ;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CSpawnRegion::GetY1()
//| CSpawnRegion::SetY1()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Gets/Sets the y pos of the top corner of the spawnregion
//o------------------------------------------------------------------------------------------------o
SI16 CSpawnRegion::GetY1( void ) const
{
return y1;
}
void CSpawnRegion::SetY1( SI16 newVal )
{
y1 = newVal;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CSpawnRegion::GetX1()
//| CSpawnRegion::SetX1()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Gets/Sets the x pos of the top corner of the spawnregion
//o------------------------------------------------------------------------------------------------o
SI16 CSpawnRegion::GetX1( void ) const
{
return x1;
}
void CSpawnRegion::SetX1( SI16 newVal )
{
x1 = newVal;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CSpawnRegion::GetY2()
//| CSpawnRegion::SetY2()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Gets/Sets the y pos of the bottom corner of the spawn region
//o------------------------------------------------------------------------------------------------o
SI16 CSpawnRegion::GetY2( void ) const
{
return y2;
}
void CSpawnRegion::SetY2( SI16 newVal )
{
y2 = newVal;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CSpawnRegion::GetX2()
//| CSpawnRegion::SetX2()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Gets/Sets the x pos of the bottom corner of the spawn region
//o------------------------------------------------------------------------------------------------o
SI16 CSpawnRegion::GetX2( void ) const
{
return x2;
}
void CSpawnRegion::SetX2( SI16 newVal )
{
x2 = newVal;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CSpawnRegion::WorldNumber()
//| CSpawnRegion::WorldNumber()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Gets/Sets the world number of the spawnregion
//o------------------------------------------------------------------------------------------------o
UI08 CSpawnRegion::WorldNumber( void ) const
{
return worldNumber;
}
void CSpawnRegion::WorldNumber( UI08 newVal )
{
worldNumber = newVal;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CSpawnRegion::GetInstanceId()
//| CSpawnRegion::SetInstanceId()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Gets/Sets the instanceId of the spawnregion
//o------------------------------------------------------------------------------------------------o
UI16 CSpawnRegion::GetInstanceId( void ) const
{
return instanceId;
}
void CSpawnRegion::SetInstanceId( UI16 newVal )
{
instanceId = newVal;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CSpawnRegion::GetOnlyOutside()
//| CSpawnRegion::SetOnlyOutside()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Gets/Sets whether NPCs should only spawn outside buildings
//o------------------------------------------------------------------------------------------------o
bool CSpawnRegion::GetOnlyOutside( void ) const
{
return onlyOutside;
}
void CSpawnRegion::SetOnlyOutside( bool newVal )
{
onlyOutside = newVal;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CSpawnRegion::IsSpawner()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Gets/Sets whether items spawned from spawnregion is a spawner or not
//o------------------------------------------------------------------------------------------------o
bool CSpawnRegion::IsSpawner( void ) const
{
return isSpawner;
}
void CSpawnRegion::IsSpawner( bool newVal )
{
isSpawner = newVal;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CSpawnRegion::GetForceSpawn()
//| CSpawnRegion::SetForceSpawn()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Gets/Sets whether spawn region should force spawn NPCs, without validating location
//o------------------------------------------------------------------------------------------------o
bool CSpawnRegion::GetForceSpawn( void ) const
{
return forceSpawn;
}
void CSpawnRegion::SetForceSpawn( bool newVal )
{
forceSpawn = newVal;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CSpawnRegion::GetCall()
//| CSpawnRegion::SetCall()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Gets/Sets number of objects that should be spawned in each call for a spawnregion
//o------------------------------------------------------------------------------------------------o
UI16 CSpawnRegion::GetCall( void ) const
{
return call;
}
void CSpawnRegion::SetCall( UI16 newVal )
{
call = newVal;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CSpawnRegion::GetNPC()
//| CSpawnRegion::SetNPC()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Gets/Sets stringlist of individual NPCs to spawn in a spawnregion
//| Only gets set during loading in of spawnregions from DFN
//o------------------------------------------------------------------------------------------------o
// WHy isn't this returning a const reference?
auto CSpawnRegion::GetNPC( void ) const -> std::vector<std::string>
{
return sNpcs;
}
void CSpawnRegion::SetNPC( const std::string &newVal )
{
// Clear old entries to make room for new ones
sNpcs.clear();
sNpcs.push_back( newVal );
}
void CSpawnRegion::SetNPCList( std::string newVal )
{
// Clear old entries to make room for new ones
sNpcs.clear();
// Check if a comma delimited string was provided
if( newVal.find( "," ) != std::string::npos )
{
// Strip whitespace from string
std::regex r( "\\s+" );
newVal = std::regex_replace( newVal, r, "" );
// Add section of the string to the sNpcs list with the help of a stringstream
std::stringstream s_stream( newVal );
while( s_stream.good() )
{
std::string substr;
getline( s_stream, substr, ',' );
sNpcs.push_back( substr );
}
}
else
{
// Assume an actual NPClist was provided
LoadNPCList( newVal );
}
}
//o------------------------------------------------------------------------------------------------o
//| Function - CSpawnRegion::GetItem()
//| CSpawnRegion::SetItem()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Gets/Sets stringlist of individual Items to spawn in a spawnregion
//o------------------------------------------------------------------------------------------------o
// THIS SHOULD BE A reference return! If you need to handle const/non const, include both types
// Just changing all this wojuld prevent copys being made each time it is called!
// Need to change the header as well of course
// example
/*
auto CSpawnRegion::GetItem() const -> const std::vector<std::string> &
{
return sItems;
}
auto CSpawnRegion::GetItem() -> std::vector<std::string> &
{
return sItems;
}
*/
auto CSpawnRegion::GetItem() const -> std::vector<std::string>
{
return sItems;
}
void CSpawnRegion::SetItem( const std::string &newVal )
{
// Clear old entries to make room for new ones
sItems.clear();
sItems.push_back( newVal );
}
void CSpawnRegion::SetItemList( std::string newVal )
{
// Clear old entries to make room for new ones
sItems.clear();
// Check if a comma delimited string was provided
if( newVal.find( "," ) != std::string::npos )
{
// Strip whitespace from string
std::regex r( "\\s+" );
newVal = std::regex_replace( newVal, r, "" );
// Add section of the string to the sItems list with the help of a stringstream
std::stringstream s_stream( newVal );
while( s_stream.good() )
{
std::string substr;
getline( s_stream, substr, ',' );
sItems.push_back( substr );
}
}
else
{
// Assume an actual ItemList was provided
LoadItemList( newVal );
}
}
//o------------------------------------------------------------------------------------------------o
//| Function - CSpawnRegion::LoadNPCList()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Loads NPCLIST from DFNs with list of NPCs to spawn for a spawnregion
//o------------------------------------------------------------------------------------------------o
void CSpawnRegion::LoadNPCList( const std::string &npcList )
{
std::string sect = "NPCLIST " + npcList;
CScriptSection *CharList = FileLookup->FindEntry( sect, npc_def );
if( CharList != nullptr )
{
for( std::string npc = CharList->First(); !CharList->AtEnd(); npc = CharList->Next() )
{
if( oldstrutil::upper( npc ) == "NPCLIST" )
{
LoadNPCList( CharList->GrabData() );
}
else
{
auto charData = CharList->GrabData();
if( !charData.empty() )
{
// Entry in npclist contains data, likely another npclist with weight in front
sNpcs.push_back( npc + "=" + CharList->GrabData() );
}
else
{
sNpcs.push_back( npc );
}
}
}
}
}
//o------------------------------------------------------------------------------------------------o
//| Function - CSpawnRegion::LoadItemList()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Loads ITEMLIST from DFNs with list of Items to spawn for a spawnregion
//o------------------------------------------------------------------------------------------------o
void CSpawnRegion::LoadItemList( const std::string &itemList )
{
std::string sect = "ITEMLIST " + itemList;
CScriptSection *ItemList = FileLookup->FindEntry( sect, items_def );
if( ItemList != nullptr )
{
for( std::string itm = ItemList->First(); !ItemList->AtEnd(); itm = ItemList->Next() )
{
if( oldstrutil::upper( itm ) == "ITEMLIST" )
{
LoadItemList( ItemList->GrabData() );
}
else
{
sItems.push_back( itm );
}
}
}
}
//o------------------------------------------------------------------------------------------------o
//| Function - CSpawnRegion::SpawnRegionScripts()
//| CSpawnRegion::ClearSpawnRegionScripts()
//| CSpawnRegion::AddSpawnRegionScripts()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Manage extra scripts applied to objects spawned from this spawn region
//o------------------------------------------------------------------------------------------------o
const std::vector<UI16> &CSpawnRegion::SpawnRegionScripts( void ) const
{
return spawnRegionScripts;
}
void CSpawnRegion::ClearSpawnRegionScripts( void )
{
spawnRegionScripts.clear();
}
void CSpawnRegion::AddSpawnRegionScripts( UI16 scriptId )
{
spawnRegionScripts.push_back( scriptId );
}
//o------------------------------------------------------------------------------------------------o
//| Function - CSpawnRegion::Load()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Loads the spawnregion from spawn.dfn script entry
//o------------------------------------------------------------------------------------------------o
bool CSpawnRegion::Load( CScriptSection *toScan, bool isParentSpawnRegion )
{
std::string sect;
std::string data;
std::string UTag;
auto coreShardEra = cwmWorldState->ServerData()->ExpansionCoreShardEra();
// Default to instanceId 0, in case nothing else is specified in DFN
SetInstanceId( 0 );
SpawnRegionExclusionAreas_st excludeArea;
for( std::string tag = toScan->First(); !toScan->AtEnd(); tag = toScan->Next() )
{
if( !tag.empty() )
{
UTag = oldstrutil::upper( tag );
data = toScan->GrabData();
switch(( UTag.data()[0] ))
{
case 'A':
{
if( UTag == "ADDSCRIPT" )
{
// Example: ADDSCRIPT=7500,7501,7502
data = oldstrutil::simplify( data );
auto csecs = oldstrutil::sections( data, "," );
for( auto& sect : csecs )
{
auto value = oldstrutil::trim( oldstrutil::removeTrailing( sect, "//" ) );
if( value.empty() )
continue;
// Script IDs are always decimal
UI16 scriptId = static_cast< UI16 >( std::stoul( value, nullptr, 10 ) );
AddSpawnRegionScripts( scriptId );
}
}
break;
}
case 'C':
{
if( UTag == "CALL" )
{
call = static_cast<UI16>( std::stoul( data, nullptr, 0 ));
}
break;
}
case 'D':
{
if( UTag == "DEFZ" )
{
defZ = static_cast<SI08>( std::stoi( data, nullptr, 0 ));
}
break;
}
case 'E':
{
if( UTag == "EXCLUDEAREA" )
{
// For every "EXCLUDEAREA" tag that contains 4 comma-separated coordinates (EXCLUDEAREA=X1,Y1,X2,Y2), push them back into a list of exclusion areas that will be avoided during spawning
data = oldstrutil::simplify( data );
auto csecs = oldstrutil::sections( data, "," );
if( csecs.size() == 4 )
{
excludeArea.x1 = static_cast<UI16>( std::stoul( oldstrutil::trim( oldstrutil::removeTrailing( csecs[0], "//" )), nullptr, 0 ));
excludeArea.y1 = static_cast<UI16>( std::stoul( oldstrutil::trim( oldstrutil::removeTrailing( csecs[1], "//" )), nullptr, 0 ));
excludeArea.x2 = static_cast<UI16>( std::stoul( oldstrutil::trim( oldstrutil::removeTrailing( csecs[2], "//" )), nullptr, 0 ));
excludeArea.y2 = static_cast<UI16>( std::stoul( oldstrutil::trim( oldstrutil::removeTrailing( csecs[3], "//" )), nullptr, 0 ));
exclusionAreas.push_back( excludeArea );
}
}
else if( UTag == "ERAS" && !isParentSpawnRegion )
{
auto ssecs = oldstrutil::sections( oldstrutil::trim( oldstrutil::removeTrailing( data, "//" )), "," );
if( ssecs.size() == 1 )
{
// Only one era specified
if( cwmWorldState->ServerData()->EraStringToEnum( ssecs[0], false, false ) != cwmWorldState->ServerData()->ExpansionCoreShardEra() )
{
// SpawnRegion not intended for current shard era, abort!
return false;
}
}
else
{
// Multiple eras specified
bool eraFound = false;
for( int i = 0; i < ssecs.size(); i++ )
{
if( cwmWorldState->ServerData()->EraStringToEnum( oldstrutil::simplify( ssecs[i] ), false, false ) == cwmWorldState->ServerData()->ExpansionCoreShardEra() )
{
eraFound = true;
}
}
if( !eraFound )
{
// SpawnRegion not intended for current shard era, abort!
return false;
}
}
}
break;
}
case 'F':
{
if( UTag == "FORCESPAWN" )
{
forceSpawn = (static_cast<SI08>( std::stoi( data, nullptr, 0 )) == 1 );
}
break;
}
case 'G':
{
if( UTag == "GET" )
{
data = "REGIONSPAWN " + oldstrutil::trim( oldstrutil::removeTrailing( data, "//" ));
CScriptSection *spawnRegionParent = FileLookup->FindEntry( data, spawn_def );
if( spawnRegionParent == nullptr )
{
Console.Warning( oldstrutil::format( "Invalid script entry (%s) called with GET tag in REGIONSPAWN section.", data.c_str() ));
}
else if( spawnRegionParent == toScan )
{
Console.Warning( oldstrutil::format( "Infinite loop avoided with GET tag in REGIONSPAWN [%s]", data.c_str() ));
}
else
{
// Load in data from "parent" spawn region
auto loadParentResult = Load( spawnRegionParent, true );
if( !loadParentResult )
{
Console.Warning( oldstrutil::format( "Unable to load parent spawn region (%s), aborting!", data.c_str() ));
return false;
}
else
{
// Parent loaded! But we don't want the spawn entries from the parent - we want to use our own!
sNpcs.clear();
sItems.clear();
}
}
}
break;
}
case 'I':
{
if( UTag == "INSTANCEID" )
{
instanceId = static_cast<UI16>( std::stoul( data, nullptr, 0 ));
}
else if( UTag == "ITEMLIST" && !isParentSpawnRegion )
{
LoadItemList( data );
}
else if( UTag == "ITEM" && !isParentSpawnRegion )
{
sItems.push_back( data );
}
else if( UTag == "ISSPAWNER" )
{
isSpawner = ( static_cast<SI08>( std::stoi( data, nullptr, 0 )) == 1 );
}
break;
}
case 'M':
{
if( UTag == "MAXITEMS" )
{
maxItemSpawn = static_cast<UI32>( std::stoul( data, nullptr, 0 ));
}
else if( UTag == "MAXNPCS" )
{
maxCharSpawn = static_cast<UI32>( std::stoul( data, nullptr, 0 ));
}
else if( UTag == "MINTIME" )
{
minTime = static_cast<UI08>( std::stoul( data, nullptr, 0 ));
}
else if( UTag == "MAXTIME" )
{
maxTime = static_cast<UI08>( std::stoul( data, nullptr, 0 ));
}
break;
}
case 'N':
{
if( UTag == "NAME" )
{
name = data;
}
else if( UTag == "NPCLIST" && !isParentSpawnRegion )
{
LoadNPCList( data );
}
else if( UTag == "NPC" && !isParentSpawnRegion )
{
sNpcs.push_back( data );
}
break;
}
case 'O':
{
if( UTag == "ONLYOUTSIDE" )
{
onlyOutside = (static_cast<SI08>( std::stoi( data, nullptr, 0 )) == 1 );
}
break;
}
case 'P':
{
if( UTag == "PREFZ" )
{
prefZ = static_cast<SI08>( std::stoi( data, nullptr, 0 ));
}
break;
}
case 'V':
{
if( UTag == "VALIDLANDPOS" )
{
data = oldstrutil::simplify( data );
auto csecs = oldstrutil::sections( data, "," );
if( csecs.size() == 3 )
{
validLandPos.push_back( Point3_st( static_cast<UI16>( std::stoul( oldstrutil::trim( oldstrutil::removeTrailing( csecs[0], "//" )), nullptr, 0)), static_cast<UI16>( std::stoul( oldstrutil::trim( oldstrutil::removeTrailing( csecs[1], "//") ), nullptr, 0 )), static_cast<UI08>( std::stoul( oldstrutil::trim( oldstrutil::removeTrailing( csecs[0], "//" )), nullptr, 0 ))));
validLandPosCheck[ static_cast<UI16>( std::stoul( oldstrutil::trim( oldstrutil::removeTrailing( csecs[1], "//" )), nullptr, 0 )) + ( static_cast<UI16>(std::stoul( oldstrutil::trim( oldstrutil::removeTrailing( csecs[0], "//" )), nullptr, 0 )) << 16 ) ] = static_cast<UI08>( std::stoul( oldstrutil::trim( oldstrutil::removeTrailing( csecs[2], "//" )), nullptr, 0 ));
}
}
else if( UTag == "VALIDWATERPOS" )
{
data = oldstrutil::simplify( data );
auto csecs = oldstrutil::sections( data, "," );
if( csecs.size() == 3 )
{
validWaterPos.push_back( Point3_st( static_cast<UI16>( std::stoul( oldstrutil::trim( oldstrutil::removeTrailing( csecs[0], "//" )), nullptr, 0 )), static_cast<UI16>( std::stoul( oldstrutil::trim( oldstrutil::removeTrailing( csecs[1], "//" )), nullptr, 0 )), static_cast<UI08>( std::stoul( oldstrutil::trim( oldstrutil::removeTrailing( csecs[0], "//" )), nullptr, 0 ))));
validWaterPosCheck[ static_cast<UI16>( std::stoul( oldstrutil::trim( oldstrutil::removeTrailing( csecs[1], "//" )), nullptr, 0 )) + ( static_cast<UI16>( std::stoul( oldstrutil::trim( oldstrutil::removeTrailing( csecs[0], "//" )), nullptr, 0 )) << 16 ) ] = static_cast<UI08>( std::stoul( oldstrutil::trim( oldstrutil::removeTrailing( csecs[2], "//" )), nullptr, 0 ));
}
}
break;
}
case 'W':
{
if( UTag == "WORLD" )
{
worldNumber = static_cast<UI16>( std::stoul( data, nullptr, 0 ));
}
break;
}
case 'X':
{
if( UTag == "X1" )
{
x1 = static_cast<SI16>( std::stoi( data, nullptr, 0 ));
}
else if( UTag == "X2" )
{
x2 = static_cast<SI16>( std::stoi( data, nullptr, 0 ));
}
break;
}
case 'Y':
{
if( UTag == "Y1" )
{
y1 = static_cast<SI16>( std::stoi( data, nullptr, 0 ));
}
else if( UTag == "Y2" )
{
y2 = static_cast<SI16>( std::stoi( data, nullptr, 0 ));
}
break;
}
default:
{
Console.Warning( oldstrutil::format( "Unknown DFN tag found in REGIONSPAWN section: %s", UTag.c_str() ));
break;
}
}
}
}
return true;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CSpawnRegion::DoRegionSpawn()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Do spawn stuff for spawnregion
//o------------------------------------------------------------------------------------------------o
void CSpawnRegion::DoRegionSpawn( UI32& itemsSpawned, UI32& npcsSpawned )
{
// Only perform the region spawn if the spawn region in question is active
if( !cwmWorldState->ServerData()->GetSpawnRegionsFacetStatus( static_cast<UI32>( WorldNumber() )))
return;
if( sNpcs.empty() )
{
maxCharSpawn = 0;
}
if( sItems.empty() )
{
maxItemSpawn = 0;
}
bool shouldSpawnChars = ( !sNpcs.empty() && maxCharSpawn > spawnedChars.Num() );
bool shouldSpawnItems = ( !sItems.empty() && maxItemSpawn > spawnedItems.Num() );
if( shouldSpawnChars || shouldSpawnItems )
{
CChar *spawnChar = nullptr;
CItem *spawnItem = nullptr;
const UI08 spawnChars = ( shouldSpawnChars ? 0 : 50 );
const UI08 spawnItems = ( shouldSpawnItems ? 100 : 49 );
for( UI16 i = 0; i < call && ( shouldSpawnItems || shouldSpawnChars ); ++i )
{
if( RandomNum( static_cast<UI16>( spawnChars ), static_cast<UI16>( spawnItems )) > 49 )
{
if( shouldSpawnItems )
{
spawnItem = RegionSpawnItem();
if( ValidateObject( spawnItem ))
{
spawnedItems.Add( spawnItem );
++itemsSpawned;
shouldSpawnItems = ( spawnedItems.Num() < maxItemSpawn );
}
}
}
else
{
if( shouldSpawnChars )
{
spawnChar = RegionSpawnChar();
if( ValidateObject( spawnChar ))
{
spawnedChars.Add( spawnChar );
++npcsSpawned;
shouldSpawnChars = ( spawnedChars.Num() < maxCharSpawn );
}
}
}
}
}
SetNextTime( BuildTimeValue( static_cast<R64>( RandomNum( static_cast<UI16>( GetMinTime() * 60 ), static_cast<UI16>( GetMaxTime() * 60 )))));
}
//o------------------------------------------------------------------------------------------------o
//| Function - CSpawnRegion::RegionSpawnChar()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Do a char spawn
//o------------------------------------------------------------------------------------------------o
auto CSpawnRegion::RegionSpawnChar() -> CChar *
{
// Stuff each NPC entry into a vector
std::vector<std::pair<std::string, UI16>> npcListVector;
for( size_t i = 0; i < sNpcs.size(); i++ )
{
// Split string for entry into a stringlist based on | as separator
auto csecs = oldstrutil::sections( oldstrutil::trim( oldstrutil::removeTrailing( sNpcs[i], "//" )), "|" );
UI16 sectionWeight = 1;
if( csecs.size() > 1 )
{
sectionWeight = static_cast<UI16>( std::stoul( oldstrutil::trim( oldstrutil::removeTrailing( csecs[0], "//" )), nullptr, 0 ));
}
auto npcSection = ( csecs.size() > 1 ? csecs[1] : csecs[0] );
npcListVector.emplace_back( npcSection, sectionWeight );
}
auto ourNPC = Npcs->ChooseNpcToCreate( npcListVector );
if( ourNPC.empty() )
return nullptr;
auto csecs = oldstrutil::sections( oldstrutil::trim( oldstrutil::removeTrailing( ourNPC, "//" )), "=" );
if( oldstrutil::upper( csecs[0] ) == "NPCLIST" )
{
// Chosen entry contained another NPCLIST! Let's dive back into it...
ourNPC = Npcs->NpcListLookup( csecs[1] );
}
CScriptSection *npcCreate = FileLookup->FindEntry( ourNPC, npc_def );
if( npcCreate == nullptr )
return nullptr;
std::string cdata;
SI32 ndata = -1, odata = -1;
UI16 npcId = 0;
for( DFNTAGS tag = npcCreate->FirstTag(); !npcCreate->AtEndTags(); tag = npcCreate->NextTag() )
{
cdata = npcCreate->GrabData( ndata, odata );
switch( tag )
{
case DFNTAG_ID:
npcId = oldstrutil::value<UI16>( cdata ); // static_cast<UI16>( ndata );
goto foundNpcId;
default:
break;
}
}
foundNpcId:
bool waterCreature = false;
bool amphiCreature = false;
if( npcId > 0 )
{
waterCreature = cwmWorldState->creatures[npcId].IsWater();
amphiCreature = cwmWorldState->creatures[npcId].IsAmphibian();
}
SI16 x, y;
SI08 z;
if( FindCharSpotToSpawn( x, y, z, waterCreature, amphiCreature ))
{
CChar *CSpawn = nullptr;
CSpawn = Npcs->CreateBaseNPC( ourNPC, false );
if( CSpawn != nullptr )
{
// NPCs should always wander the whole spawnregion
CSpawn->SetNpcWander( WT_BOX );
CSpawn->SetFx( x1, 0 );
CSpawn->SetFx( x2, 1 );
CSpawn->SetFy( y1, 0 );
CSpawn->SetFy( y2, 1 );
CSpawn->SetLocation( x, y, z, worldNumber, instanceId );
CSpawn->SetSpawnX( x );
CSpawn->SetSpawnY( y );
CSpawn->SetSpawnZ( z );
CSpawn->SetSpawned( true );
CSpawn->ShouldSave( false );
CSpawn->SetSpawn( static_cast<UI32>( regionNum ));
Npcs->PostSpawnUpdate( CSpawn );
IncCurrentCharAmt();
TAGMAPOBJECT tagvalObject;
tagvalObject.m_ObjectType = TAGMAP_TYPE_INT;
tagvalObject.m_IntValue = this->GetRegionNum();
tagvalObject.m_Destroy = false;
tagvalObject.m_StringValue = "";
CSpawn->SetTag( "spawnRegion", tagvalObject );
// Extra scripts applied from REGIONSPAWN ADDSCRIPT tag
for( auto scriptId : spawnRegionScripts )
{
CSpawn->AddScriptTrigger( scriptId );
}
// Get all script triggers and run onSpawn on each
std::vector<UI16> scriptTriggers = CSpawn->GetScriptTriggers();
for( auto scriptTrig : scriptTriggers )
{
cScript* toExecute = JSMapping->GetScript( scriptTrig );
if( toExecute != nullptr )
{
toExecute->OnSpawn( CSpawn, regionNum );
}
}
return CSpawn;
}
}
else
{
Console.Warning( oldstrutil::format( "Unable to find valid location to spawn NPC in region %i", this->GetRegionNum() ));
}
return nullptr;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CSpawnRegion::RegionSpawnItem()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Do an item spawn
//o------------------------------------------------------------------------------------------------o
auto CSpawnRegion::RegionSpawnItem() -> CItem *
{
CItem *ISpawn = nullptr;
SI16 x, y;
SI08 z;
auto idToSpawn = sItems[RandomNum( static_cast<size_t>( 0 ), sItems.size() - 1 )];
if( FindItemSpotToSpawn( x, y, z, idToSpawn ))
{
auto objType = OT_ITEM;
if( isSpawner )
{
objType = OT_SPAWNER;
}
ISpawn = Items->CreateBaseScriptItem( nullptr, idToSpawn, worldNumber, 1, instanceId, objType, 0xFFFF, false );
if( ISpawn != nullptr )
{
ISpawn->SetLocation( x, y, z );
ISpawn->SetSpawn( static_cast<UI32>( regionNum ));
ISpawn->SetSpawned( true );
ISpawn->ShouldSave( false );
IncCurrentItemAmt();
// Extra scripts applied from REGIONSPAWN ADDSCRIPT tag
for( auto scriptId : spawnRegionScripts )
{
ISpawn->AddScriptTrigger( scriptId );
}
// Get all script triggers and run onSpawn on each
std::vector<UI16> scriptTriggers = ISpawn->GetScriptTriggers();
for( auto scriptTrig : scriptTriggers )
{
cScript* toExecute = JSMapping->GetScript( scriptTrig );
if( toExecute != nullptr )
{
// Assumes cScript has OnSpawn( CBaseObject* ), mirroring your OnCreate usage
toExecute->OnSpawn( ISpawn, regionNum );
}
}
}
}
return ISpawn;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CSpawnRegion::FindCharSpotToSpawn()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Find a random spot within a region valid for dropping an item
//o------------------------------------------------------------------------------------------------o
bool CSpawnRegion::FindCharSpotToSpawn( SI16 &x, SI16 &y, SI08 &z, bool &waterCreature, bool &amphiCreature )
{
bool rValue = false;
Point3_st currLoc;
//std::map<UI32, SI08>::const_iterator checkValid;
SI08 z2 = ILLEGAL_Z;
const size_t landPosSize = validLandPos.size();
const size_t waterPosSize = validWaterPos.size();
// By default, let's attempt - at most - this many times to find a valid spawn point for the NPC
UI08 maxSpawnAttempts = 100;
// However, if we have found some valid spawn points already, reduce the amount of attempts to find MORE
// valid spawn points, and increase the chance of using those valid spots instead! Always make some attempts to
// find new spots, though
if( !waterCreature && landPosSize > 0 ) // land creature
{
maxSpawnAttempts = std::max( static_cast<SI08>( 25 ), static_cast<SI08>( maxSpawnAttempts - landPosSize ));
}
else if(( waterCreature || amphiCreature ) && waterPosSize > 0 ) // water or amphibian creature
{
maxSpawnAttempts = std::max( static_cast<SI08>( 25 ), static_cast<SI08>( maxSpawnAttempts - waterPosSize ));
}
for( UI08 a = 0; a < maxSpawnAttempts; ++a )
{
x = RandomNum( x1, x2 );
y = RandomNum( y1, y2 );
z = Map->MapElevation( x, y, worldNumber );
if( defZ != ILLEGAL_Z )
{
// Use the definite Z level defined in the spawn region
// Trust that the spawnregion scripter knows what they are doing...
z = defZ;
}
else
{
// No definite Z has been defined, look for valid dynamic Z based on prefZ
const SI08 dynZ = Map->DynamicElevation( x, y, z, worldNumber, instanceId, prefZ );
if( ILLEGAL_Z != dynZ )
{
z = dynZ;
}
// Even if we got a valid dynamic Z, there might be a better match with statics, based on prefZ
const SI08 staticZ = Map->StaticTop( x, y, z, worldNumber, prefZ );
if( ILLEGAL_Z != staticZ && staticZ > z )
{
z = staticZ;
}
}
// Continue with the next spawn attempt if z is illegal
if( z == ILLEGAL_Z )
continue;
if( forceSpawn )
{
// Don't care about validation, force spawn at own risk!
return true;
}
// First go through the lists of already valid spawn locations on land,
// and see if the chosen location has already been validated
if( !waterCreature )
{
if( !validLandPosCheck.empty() && landPosSize > 0 )
{
auto checkValid = validLandPosCheck.find( y + ( x << 16 ));
if( checkValid != validLandPosCheck.end() )
{
z2 = ( *checkValid ).second;
}
if( z2 == z && z != ILLEGAL_Z )
{
rValue = true;
break;
}
}
}
// No luck, so let's try the list of already valid water tiles instead (if NPC can move on water)
if( waterCreature || amphiCreature )
{
if( !validWaterPosCheck.empty() && waterPosSize > 0 )
{
auto checkValid = validWaterPosCheck.find( y + ( x << 16 ));
if( checkValid != validWaterPosCheck.end() )
{
z2 = ( *checkValid ).second;
}
if( z2 == z && z != ILLEGAL_Z )
{
rValue = true;
break;
}
}
}
// Make sure the chosen spawn location is not in an exclusion area
auto illegalLoc = false;
if( !rValue )
{
for( auto &exclusionArea : exclusionAreas )
{
if( x >= exclusionArea.x1 && x <= exclusionArea.x2 && y >= exclusionArea.y1 && y <= exclusionArea.y2 )
{
// Chosen location is in an exclusion area. Disallow!
illegalLoc = true;
break;
}
}
}
// Since our chosen location has not already been validated, lets validate it with a land-based creature in mind
if( !illegalLoc && !waterCreature && Map->ValidSpawnLocation( x, y, z, worldNumber, instanceId ))
{
if( onlyOutside == false || !Map->InBuilding( x, y, z, worldNumber, instanceId ))
{
if( z != ILLEGAL_Z )
{
rValue = true;
validLandPos.push_back( Point3_st( x, y, z ));
validLandPosCheck[ y + ( x << 16) ] = z;
break;
}
}
}
// Otherwise, validate it with a water-based creature in mind instead
if( !illegalLoc && ( waterCreature || amphiCreature ) && Map->ValidSpawnLocation( x, y, z, worldNumber, instanceId, false ))
{
if( onlyOutside == false || !Map->InBuilding( x, y, z, worldNumber, instanceId ))
{
rValue = true;
validWaterPos.push_back( Point3_st( x, y, z ));
validWaterPosCheck[ y + ( x << 16) ] = z;
break;
}
}
}
// If we haven't found a valid location pick a random location from the stored ones
if( !rValue && !waterCreature && !validLandPos.empty() && landPosSize > 0 )
{
currLoc = validLandPos[RandomNum( static_cast<size_t>( 0 ), ( landPosSize - 1 ))];
x = static_cast<SI16>( currLoc.x );
y = static_cast<SI16>( currLoc.y );
z = static_cast<SI08>( currLoc.z );
// Recalculate the z coordinate to see whether something has changed
z2 = Map->MapElevation( x, y, worldNumber );
const SI08 dynz = Map->DynamicElevation( x, y, z, worldNumber, instanceId, prefZ );
if( ILLEGAL_Z != dynz )
{
z2 = dynz;
}
else
{
const SI08 staticz = Map->StaticTop( x, y, z, worldNumber, prefZ );
if( ILLEGAL_Z != staticz )
{
z2 = staticz;
}
}
if( z2 == z )
{
rValue = true;
}
}
else if( !rValue && ( waterCreature || amphiCreature) && !validWaterPos.empty() && waterPosSize > 0 )
{
currLoc = validWaterPos[RandomNum( static_cast<size_t>( 0 ), ( waterPosSize - 1 ))];
x = static_cast<SI16>( currLoc.x );
y = static_cast<SI16>( currLoc.y );
z = static_cast<SI08>( currLoc.z );
// Recalculate the z coordinate to see whether something has changed
z2 = Map->MapElevation( x, y, worldNumber );
const SI08 dynz = Map->DynamicElevation( x, y, z, worldNumber, instanceId, prefZ );
if( ILLEGAL_Z != dynz )
{
z2 = dynz;
}
else
{
const SI08 staticz = Map->StaticTop( x, y, z, worldNumber, prefZ );
if( ILLEGAL_Z != staticz )
{
z2 = staticz;
}
}
if( z2 == z )
{
rValue = true;
}
}
return rValue;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CSpawnRegion::FindItemSpotToSpawn()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Find a random spot within a region valid for dropping an item
//o------------------------------------------------------------------------------------------------o
bool CSpawnRegion::FindItemSpotToSpawn( SI16 &x, SI16 &y, SI08 &z, std::string idToSpawn )
{
bool rValue = false;
Point3_st currLoc;
SI08 z2 = ILLEGAL_Z;
std::map<UI32, SI08>::const_iterator checkValid;
const size_t landPosSize = validLandPos.size();
// By default, let's try - at most - this many times to find a valid spawn point for the item
UI08 maxSpawnAttempts = 100;
// However, if we have found some valid spawn points already, reduce the amount of attempts to find MORE
// valid spawn points, and increase the chance of using those valid spots instead!
if( landPosSize > 0 )
{
maxSpawnAttempts = std::max( static_cast<SI16>( 25 ), static_cast<SI16>( maxSpawnAttempts - landPosSize ));
}
for( UI08 a = 0; a < maxSpawnAttempts; ++a )
{
x = RandomNum( x1, x2 );
y = RandomNum( y1, y2 );
z = Map->MapElevation( x, y, worldNumber );
if( defZ != ILLEGAL_Z )
{
// Use the definite Z level defined in the spawn region
// Trust that the spawnregion scripter knows what they are doing...
z = defZ;
}
else
{
// No definite Z has been defined, look for valid dynamic Z based on prefZ
const SI08 dynZ = Map->DynamicElevation( x, y, z, worldNumber, instanceId, prefZ );
if( ILLEGAL_Z != dynZ )
{
z = dynZ;
}
// Even if we got a valid dynamic Z, there might be a better match with statics, based on prefZ
const SI08 staticZ = Map->StaticTop( x, y, z, worldNumber, prefZ );
if( ILLEGAL_Z != staticZ && staticZ > z )
{
z = staticZ;
}
}
// First go through the lists of already stored good locations
if( !validLandPosCheck.empty() && landPosSize > 0 )
{
auto checkValid = validLandPosCheck.find( y + ( x << 16 ));
if( checkValid != validLandPosCheck.end() )
{
z2 = (*checkValid).second;
}
if( z2 == z && z != ILLEGAL_Z )
{
rValue = true;
//break;
}
}
// Make sure the chosen spawn location is not in an exclusion area
auto illegalLoc = false;
if( !rValue )
{
for( auto &exclusionArea : exclusionAreas )
{
if( x >= exclusionArea.x1 && x <= exclusionArea.x2 && y >= exclusionArea.y1 && y <= exclusionArea.y2 )
{
// Chosen location is in an exclusion area. Disallow!
illegalLoc = true;
break;
}
}
}
// Don't spawn an item in a location where same item has already spawned before
auto itemAtXYZ = GetItemAtXYZ( x, y, z, worldNumber, instanceId );
if( ValidateObject( itemAtXYZ ) && itemAtXYZ->GetSectionId() == idToSpawn )
{
illegalLoc = true;
rValue = false;
break;
}
if( !illegalLoc && !rValue && Map->ValidSpawnLocation( x, y, z, worldNumber, instanceId ))
{
if( onlyOutside == false || !Map->InBuilding( x, y, z, worldNumber, instanceId ))
{
rValue = true;
validLandPos.push_back( Point3_st( x, y, z ));
validLandPosCheck[ y + ( x << 16) ] = z;
break;
}
}
}
// If we haven't found a valid location pick a random location from the stored ones
if( !rValue && !validLandPos.empty() && landPosSize > 0 )
{
currLoc = validLandPos[RandomNum( static_cast<size_t>( 0 ), ( landPosSize - 1 ))];
x = static_cast<SI16>( currLoc.x );
y = static_cast<SI16>( currLoc.y );
z = static_cast<SI08>( currLoc.z );
// Recalculate the z coordinate to see whether something has changed
z2 = Map->MapElevation( x, y, worldNumber );
const SI08 dynz = Map->DynamicElevation( x, y, z, worldNumber, instanceId, prefZ );
if( ILLEGAL_Z != dynz )
{
z2 = dynz;
}
else
{
const SI08 staticz = Map->StaticTop( x, y, z, worldNumber, prefZ );
if( ILLEGAL_Z != staticz )
{
z2 = staticz;
}
}
if( z2 == z )
{
// Nevertheless, don't spawn an item in a location where same item has already spawned before
auto itemAtXYZ = GetItemAtXYZ( x, y, z, worldNumber, instanceId );
if( ValidateObject( itemAtXYZ ) && itemAtXYZ->GetSectionId() == idToSpawn )
{
rValue = false;
}
else
{
rValue = true;
}
}
}
return rValue;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CSpawnRegion::CheckSpawned()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Check if an item/npc should be removed from spawnlist and saved
//o------------------------------------------------------------------------------------------------o
void CSpawnRegion::CheckSpawned( void )
{
for( CChar *cCheck = spawnedChars.First(); !spawnedChars.Finished(); cCheck = spawnedChars.Next() )
{
if( ValidateObject( cCheck ))
{
if( ValidateObject( cCheck->GetOwnerObj() ))
{
cCheck->ShouldSave( true );
spawnedChars.Remove( cCheck );
}
}
else
{
Console.Warning( "Invalid Object found in CSpawnRegion character list, AutoCorrecting." );
spawnedChars.Remove( cCheck );
}
}
for( CItem *iCheck = spawnedItems.First(); !spawnedItems.Finished(); iCheck = spawnedItems.Next() )
{
if( ValidateObject( iCheck ))
{
if( ValidateObject( iCheck->GetCont() ))
{
iCheck->ShouldSave( true );
spawnedItems.Remove( iCheck );
}
}
else
{
Console.Warning( "Invalid Object found in CSpawnRegion item list, AutoCorrecting." );
spawnedItems.Remove( iCheck );
}
}
}
//o------------------------------------------------------------------------------------------------o
//| Function - CSpawnRegion::DeleteSpawnedChar()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Delete an npc from spawnlist
//o------------------------------------------------------------------------------------------------o
void CSpawnRegion::DeleteSpawnedChar( CChar *toDelete )
{
if( spawnedChars.Remove( toDelete ))
{
IncCurrentCharAmt( -1 );
}
}
//o------------------------------------------------------------------------------------------------o
//| Function - CSpawnRegion::DeleteSpawnedItem()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Delete an item from spawnlist
//o------------------------------------------------------------------------------------------------o
void CSpawnRegion::DeleteSpawnedItem( CItem *toDelete )
{
if( spawnedItems.Remove( toDelete ))
{
IncCurrentItemAmt( -1 );
}
}
//o------------------------------------------------------------------------------------------------o
//| Function - CSpawnRegion::GetSpawnedItemsList()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Gets list of spawned Items for spawnregion
//o------------------------------------------------------------------------------------------------o
GenericList<CItem *> * CSpawnRegion::GetSpawnedItemsList( void )
{
return &spawnedItems;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CSpawnRegion::GetSpawnedCharsList()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Gets list of spawned NPCs for spawnregion
//o------------------------------------------------------------------------------------------------o
GenericList<CChar *> * CSpawnRegion::GetSpawnedCharsList( void )
{
return &spawnedChars;
}