uox3/source/npcs.cpp
2022-06-06 08:52:19 -04:00

1957 lines
67 KiB
C++

#include "uox3.h"
#include "msgboard.h"
#include "movement.h"
#include "cServerDefinitions.h"
#include "cSpawnRegion.h"
#include "skills.h"
#include "ssection.h"
#include "CJSMapping.h"
#include "cScript.h"
#include "teffect.h"
#include "CPacketSend.h"
#include "cEffects.h"
#include "classes.h"
#include "regions.h"
#include "townregion.h"
#include "Dictionary.h"
#include "ObjectFactory.h"
cCharStuff *Npcs = nullptr;
GenericList< CChar * > alwaysAwakeNPCs;
//o-----------------------------------------------------------------------------------------------o
//| Function - GenericList< CChar * > * GetAlwaysAwakeNPCs( void )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Returns list of NPCs that are always awake
//o-----------------------------------------------------------------------------------------------o
GenericList< CChar * > * cCharStuff::GetAlwaysAwakeNPCs( void )
{
return &alwaysAwakeNPCs;
}
//o-----------------------------------------------------------------------------------------------o
//| Function - CItem *addRandomLoot( CItem *s, std::string lootlist )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Add loot to monsters packs
//o-----------------------------------------------------------------------------------------------o
CItem *cCharStuff::addRandomLoot( CItem *s, const std::string& lootlist, bool shouldSave )
{
CItem *retItem = nullptr;
std::string sect = std::string("LOOTLIST ") + lootlist;
ScriptSection *LootList = FileLookup->FindEntry( sect, items_def );
if( LootList == nullptr )
return nullptr;
size_t i = LootList->NumEntries();
if( i > 0 )
{
i = RandomNum( static_cast< size_t >(0), i - 1 );
std::string tag = LootList->MoveTo( i );
std::string tagData = LootList->GrabData();
auto csecs = oldstrutil::sections( oldstrutil::trim( oldstrutil::removeTrailing( tagData, "//" )), "," );
auto tcsecs = oldstrutil::sections( oldstrutil::trim( oldstrutil::removeTrailing( tag, "//" )), "," );
if( tag.empty() )
return nullptr;
UI16 iAmount = 0;
if( oldstrutil::upper( tag ) == "LOOTLIST" )
{
if( csecs.size() > 1 ) // Amount specified behind lootlist entry?
{
iAmount = static_cast<UI16>(std::stoul(oldstrutil::trim( oldstrutil::removeTrailing( csecs[1], "//" )), nullptr, 0));
CItem *retItemNested = nullptr;
for( UI16 iCount = 0; iCount < iAmount; ++iCount )
{
retItemNested = addRandomLoot( s, oldstrutil::trim( oldstrutil::removeTrailing( csecs[0], "//" )), shouldSave );
}
}
else
{
retItem = addRandomLoot( s, LootList->GrabData(), shouldSave );
}
}
else
{
if( tcsecs.size() > 1 ) // Amount specified behind lootlist entry?
{
iAmount = static_cast<UI16>(std::stoul(oldstrutil::trim( oldstrutil::removeTrailing( tcsecs[1], "//" )), nullptr, 0));
retItem = Items->CreateBaseScriptItem( s, oldstrutil::trim( oldstrutil::removeTrailing( tcsecs[0], "//" )), s->WorldNumber(), iAmount, s->GetInstanceID(), OT_ITEM, 0xFFFF, shouldSave );
if( retItem != nullptr )
{
retItem->SetCont( s );
retItem->PlaceInPack();
}
}
else
{
retItem = Items->CreateBaseScriptItem( s, tag, s->WorldNumber(), 1, s->GetInstanceID(), OT_ITEM, 0xFFFF, shouldSave );
if( retItem != nullptr )
{
retItem->SetCont( s );
retItem->PlaceInPack();
}
}
}
}
return retItem;
}
//o-----------------------------------------------------------------------------------------------o
//| Function - CChar *CreateBaseNPC( std::string ourNPC )
//| Date - 10/12/2003
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Creates a basic npc from the scripts
//o-----------------------------------------------------------------------------------------------o
CChar *cCharStuff::CreateBaseNPC( std::string ourNPC, bool shouldSave )
{
ourNPC = oldstrutil::trim( oldstrutil::removeTrailing( ourNPC, "//" ));
ScriptSection *npcCreate = FileLookup->FindEntry( ourNPC, npc_def );
if( npcCreate == nullptr )
{
Console.error(oldstrutil::format( "CreateBaseNPC(): Bad script npc %s (NPC Not Found).", ourNPC.c_str()) );
return nullptr;
}
CChar *cCreated = nullptr;
if( npcCreate->NpcListExist() )
cCreated = CreateRandomNPC( npcCreate->NpcListData() );
else
{
cCreated = static_cast< CChar * >(ObjectFactory::getSingleton().CreateObject( OT_CHAR ));
if( cCreated == nullptr )
return nullptr;
if( !shouldSave )
cCreated->ShouldSave( false );
cCreated->SetSkillTitles( true );
cCreated->SetNpc( true );
cCreated->SetLoDamage( 1 );
cCreated->SetHiDamage( 1 );
cCreated->SetResist( 1, PHYSICAL );
cCreated->SetSpawn( INVALIDSERIAL );
if( !ApplyNpcSection( cCreated, npcCreate, ourNPC ) )
Console.error( "Trying to apply an npc section failed" );
std::vector<UI16> scriptTriggers = cCreated->GetScriptTriggers();
for( auto scriptTrig : scriptTriggers )
{
cScript *toExecute = JSMapping->GetScript( scriptTrig );
if( toExecute != nullptr )
{
toExecute->OnCreate( cCreated, true );
}
}
}
return cCreated;
}
//o-----------------------------------------------------------------------------------------------o
//| Function - CChar *CreateRandomNPC( const std::string& npcList )
//| Date - 10/12/2003
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Creates a random npc from an npclist in specified dfn file
//o-----------------------------------------------------------------------------------------------o
CChar *cCharStuff::CreateRandomNPC( const std::string& npcList )
{
CChar *cCreated = nullptr;
std::string sect = std::string("NPCLIST ") + npcList;
sect = oldstrutil::trim( oldstrutil::removeTrailing( sect, "//" ));
ScriptSection *NPCList = FileLookup->FindEntry( sect, npc_def );
if( NPCList != nullptr )
{
const size_t i = NPCList->NumEntries();
if( i > 0 )
{
std::string k = NPCList->MoveTo( RandomNum( static_cast<size_t>(0), i - 1 ));
if( !k.empty() )
{
if( oldstrutil::upper( k ) == "NPCLIST" ) {
cCreated = CreateRandomNPC( NPCList->GrabData() );
}
else
{
cCreated = CreateBaseNPC( k );
}
}
}
}
return cCreated;
}
//o-----------------------------------------------------------------------------------------------o
//| Function - CChar *CreateNPC( CSpawnItem *iSpawner, const std::string &npc )
//| Date - 10/12/2003
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Creates an npc spawned from an item spawner
//o-----------------------------------------------------------------------------------------------o
CChar *cCharStuff::CreateNPC( CSpawnItem *iSpawner, const std::string &npc )
{
const ItemTypes iType = iSpawner->GetType();
// If the spawner type is 125 and escort quests are not active then abort
if( iType == IT_ESCORTNPCSPAWNER && !cwmWorldState->ServerData()->EscortsEnabled() )
return nullptr;
CChar *cCreated = nullptr;
if( iSpawner->IsSectionAList() )
cCreated = CreateRandomNPC( npc );
else
cCreated = CreateBaseNPC( npc );
if( cCreated == nullptr )
return nullptr;
cCreated->SetSpawn( iSpawner->GetSerial() );
SI16 awayX = 0, awayY = 0;
if(( iType == IT_AREASPAWNER || iType == IT_ESCORTNPCSPAWNER ) && iSpawner->GetCont() == nullptr )
{
awayX = iSpawner->GetTempVar( CITV_MORE, 3 );
awayY = iSpawner->GetTempVar( CITV_MORE, 4 );
}
FindSpotForNPC( cCreated, iSpawner->GetX(), iSpawner->GetY(), awayX, awayY, iSpawner->GetZ(), iSpawner->WorldNumber(), iSpawner->GetInstanceID() );
PostSpawnUpdate( cCreated );
if( iType == IT_ESCORTNPCSPAWNER )
MsgBoardQuestEscortCreate( cCreated );
cCreated->SetWipeable( true );
return cCreated;
}
//o-----------------------------------------------------------------------------------------------o
//| Function - CChar *CreateNPCxyz( const std::string &npc, SI16 x, SI16 y, SI08 z, UI08 worldNumber, UI16 instanceID, bool useNpcList )
//| Date - 10/12/2003
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Creates an npc at location xyz
//o-----------------------------------------------------------------------------------------------o
CChar *cCharStuff::CreateNPCxyz( const std::string &npc, SI16 x, SI16 y, SI08 z, UI08 worldNumber, UI16 instanceID, bool useNpcList )
{
CChar *cCreated = nullptr;
if( useNpcList )
cCreated = CreateRandomNPC( npc );
else
cCreated = CreateBaseNPC( npc );
if( cCreated == nullptr )
return nullptr;
cCreated->SetLocation( x, y, z, worldNumber, instanceID );
// Update "old location" for new NPCs straight away
cCreated->SetOldLocation( x, y, z );
PostSpawnUpdate( cCreated );
return cCreated;
}
//o-----------------------------------------------------------------------------------------------o
//| Function - void PostSpawnUpdate( CChar *cCreated )
//| Date - 10/12/2003
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Updates created npc
//o-----------------------------------------------------------------------------------------------o
void cCharStuff::PostSpawnUpdate( CChar *cCreated )
{
CTownRegion *tReg = calcRegionFromXY( cCreated->GetX(), cCreated->GetY(), cCreated->WorldNumber(), cCreated->GetInstanceID(), cCreated );
cCreated->SetRegion( tReg->GetRegionNum() );
for( UI08 z = 0; z < ALLSKILLS; ++z )
Skills->updateSkillLevel( cCreated, z );
UpdateFlag( cCreated );
cCreated->Update();
}
//o-----------------------------------------------------------------------------------------------o
//| Function - SI16 getRadius( CChar *c )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Generate a sensible radius given the values from the NPC DFNs
//o-----------------------------------------------------------------------------------------------o
SI16 getRadius( CChar *c )
{
// see if they supplied a 'radius'
if( c->GetFx( 1 ) > 0 )
{
// if they were supplying a bounding area, use the radius from that
if( c->GetFx( 0 ) > 0 )
c->SetFx( abs(c->GetFx( 0 ) - c->GetFx( 1 )), 1 );
}
// ensure its not something bogus
if( c->GetFx( 1 ) <= 0 || c->GetFx( 1 ) > 100)
c->SetFx( 10, 1 );
return c->GetFx( 1 );
}
//o-----------------------------------------------------------------------------------------------o
//| Function - bool checkBoundingBox( const SI16 xPos, const SI16 yPos, const SI16 fx1, const SI16 fy1,
//| const SI08 fz1, const SI16 fx2, const SI16 fy2, const UI08 worldNumber, const UI16 instanceID )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Check bounding box
//o-----------------------------------------------------------------------------------------------o
bool checkBoundingBox( const SI16 xPos, const SI16 yPos, const SI16 fx1, const SI16 fy1, const SI08 fz1,
const SI16 fx2, const SI16 fy2, const UI08 worldNumber, const UI16 instanceID )
{
if( xPos >= ( ( fx1 < fx2 ) ? fx1 : fx2 ) && xPos <= ( ( fx1 < fx2 ) ? fx2 : fx1 ) )
{
if( yPos >= ( ( fy1 < fy2 ) ? fy1 : fy2 ) && yPos <= ( ( fy1 < fy2 ) ? fy2 : fy1 ) )
{
if( fz1 == -1 || abs( fz1 - Map->Height( xPos, yPos, fz1, worldNumber, instanceID ) ) <= 5 )
return true;
}
}
return false;
}
//o-----------------------------------------------------------------------------------------------o
//| Function - bool checkBoundingCircle( const SI16 xPos, const SI16 yPos, const SI16 fx1, const SI16 fy1,
//| const SI08 fz1, const SI16 radius, const UI08 worldNumber, const UI16 instanceID )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Check bounding circle
//o-----------------------------------------------------------------------------------------------o
bool checkBoundingCircle( const SI16 xPos, const SI16 yPos, const SI16 fx1, const SI16 fy1, const SI08 fz1,
const SI16 radius, const UI08 worldNumber, const UI16 instanceID )
{
if( ( xPos - fx1 ) * ( xPos - fx1 ) + ( yPos - fy1 ) * ( yPos - fy1 ) <= radius * radius )
{
if( fz1 == -1 || abs( fz1 - Map->Height( xPos, yPos, fz1, worldNumber, instanceID ) ) <= 5 )
return true;
}
return false;
}
//o-----------------------------------------------------------------------------------------------o
//| Function - void InitializeWanderArea( CChar *c, SI16 xAway, SI16 yAway )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Setup the wander area if the npcwander is rect or circle
//o-----------------------------------------------------------------------------------------------o
void InitializeWanderArea( CChar *c, SI16 xAway, SI16 yAway )
{
// compute the rectangular bounding area
if( WT_BOX == c->GetNpcWander() )
{
// if they provided a legal rectangle and
// ensure the bounding rect contains the current location
// if it doesn't the monster will never move!
if( c->GetFx( 0 ) >= 0 && c->GetFy( 0 ) >= 0 && c->GetFy( 1 ) >= 0 && c->GetFx( 1 ) >= 0 &&
checkBoundingBox( c->GetX(), c->GetY(), c->GetFx( 0 ), c->GetFy( 0 ), c->GetFz(), c->GetFx( 1 ), c->GetFy( 1 ), c->WorldNumber(), c->GetInstanceID() ))
{
return;// don't do anything to use what they specified in the NPC DFNs
}
else
{
// if they provided a 'radius' in the NPC DFNs, use that
if( c->GetFx( 1 ) > 0 )
xAway = yAway = getRadius( c );
// setup info for rectangular areas
c->SetFx( c->GetX() - xAway, 0 );
c->SetFy( c->GetY() - yAway, 0 );
c->SetFx( c->GetX() + xAway, 1 );
c->SetFy( c->GetY() + yAway, 1 );
}
}
else if( WT_CIRCLE == c->GetNpcWander() )
{
// if they provided a legal circle and
// ensure the bounding circle contains the current location
// if it doesn't the monster will never move!
if( c->GetFx( 0 ) >= 0 && c->GetFy( 0 ) >= 0 && c->GetFx( 1 ) >= 0 &&
checkBoundingCircle( c->GetX(), c->GetY(), c->GetFx( 0 ), c->GetFy( 0 ), c->GetFz(), c->GetFx( 1 ), c->WorldNumber(), c->GetInstanceID() ))
{
return;// don't do anything to use what they specified in NPC DFNs
}
else
{
// if they provided a 'radius' in the NPC DFNs use that
if( c->GetFx( 1 ) > 0 )
xAway = yAway = getRadius( c );
// setup info for circular areas
c->SetFx( c->GetX(), 0 );
c->SetFy( c->GetY(), 0 );
c->SetFx( xAway, 1 );
c->SetFy( -1, 1 );
}
}
// setting fz1 actually makes it check against the height and slows the system down a lot
// does anyone really need to constrain the height at which a monster can move??
}
//o-----------------------------------------------------------------------------------------------o
//| Function - void cCharStuff::FindSpotForNPC( CChar *cCreated, const SI16 originX, const SI16 originY,
//| const SI16 xAway, const SI16 yAway, const SI08 z, const UI08 worldNumber, const UI16 instanceID )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Find a valid spot to drop an NPC near the spawners location
//|
//| Changes - 06/26/2020 - Added support for instanceID
//|
//| Changes - 04/20/2002 - (Type 69) xos and yos (X OffSet, Y OffSet)
//| are used to find a random number that is then added to the spawner's
//| x and y (Using the spawner's z) and then place the NPC anywhere in
//| a square around the spawner. This square is random anywhere from -10
//| to +10 from the spawner's location (for x and y) If the place chosen
//| is not a valid position (the NPC can't walk there) then a new place
//| will be chosen, if a valid place cannot be found in a certain # of
//| tries (50),the NPC will be placed directly on the spawner and the
//| server op will be warned.
//o-----------------------------------------------------------------------------------------------o
void cCharStuff::FindSpotForNPC( CChar *cCreated, const SI16 originX, const SI16 originY, const SI16 xAway,
const SI16 yAway, const SI08 z, const UI08 worldNumber, const UI16 instanceID )
{
#ifdef DEBUG_SPAWN
Console.print( oldstrutil::format( "Going to spawn at (%d,%d) within %d by %d\n", originX, originY, xAway, yAway ));
#endif
if( !ValidateObject( cCreated ) )
return;
SI32 k = xAway * yAway / 2;
SI16 xos = 0, yos = 0;
SI08 targZ = 0;
bool foundSpot = false;
if( k > 50 )
k = 50;
while( !foundSpot )
{
targZ = z;
if( --k < 0 ) //this CAN be a bit laggy. adjust as nessicary
{
if( xAway > 0 && yAway > 0 )
Console << "Problem area spawner found, NPC placed at default location" << myendl;
xos = originX;
yos = originY;
foundSpot = true;
break;
}
xos = originX + RandomNum( static_cast< SI16 >(-xAway), xAway );
yos = originY + RandomNum( static_cast< SI16 >(-yAway), yAway );
if( xos >= 1 && yos >= 1 )
{
targZ = Map->Height( xos, yos, z, worldNumber, instanceID );
if( !cwmWorldState->creatures[cCreated->GetID()].IsWater() )
foundSpot = Map->ValidSpawnLocation( xos, yos, targZ, worldNumber, instanceID );
else if( cwmWorldState->creatures[cCreated->GetID()].IsWater() || ( !foundSpot && cwmWorldState->creatures[cCreated->GetID()].IsAmphibian() ) )
foundSpot = Map->ValidSpawnLocation( xos, yos, targZ, worldNumber, instanceID, false );
}
}
cCreated->SetLocation( xos, yos, targZ, worldNumber, instanceID );
InitializeWanderArea( cCreated, xAway, yAway );
}
//o-----------------------------------------------------------------------------------------------o
//| Function - void LoadShopList( const std::string& list, CChar *c )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Loads the shopping list pointed to by data in ITEM DFNs
//o-----------------------------------------------------------------------------------------------o
void cCharStuff::LoadShopList( const std::string& list, CChar *c )
{
CItem *buyLayer = c->GetItemAtLayer( IL_BUYCONTAINER ); //Contains items the NPC is willing to buy
CItem *boughtLayer = c->GetItemAtLayer( IL_BOUGHTCONTAINER ); //Contains items the NPC has already bought
CItem *sellLayer = c->GetItemAtLayer( IL_SELLCONTAINER ); //Contains items the NPC will sell
std::string sect = std::string("SHOPLIST ") + list;
sect = oldstrutil::trim( oldstrutil::removeTrailing( sect, "//" ));
ScriptSection *ShoppingList = FileLookup->FindEntry( sect, items_def );
if( ShoppingList == nullptr )
return;
bool shouldSave = c->ShouldSave();
std::string cdata;
SI32 ndata = -1, odata = -1;
CItem *retItem = nullptr;
for( DFNTAGS tag = ShoppingList->FirstTag(); !ShoppingList->AtEndTags(); tag = ShoppingList->NextTag() )
{
cdata = ShoppingList->GrabData( ndata, odata );
switch( tag )
{
case DFNTAG_RSHOPITEM:
if( ValidateObject( buyLayer ) )
{
retItem = Items->CreateBaseScriptItem( nullptr, cdata, c->WorldNumber(), 1, c->GetInstanceID(), OT_ITEM, 0xFFFF, shouldSave );
if( retItem != nullptr )
{
retItem->SetCont( buyLayer );
retItem->PlaceInPack();
if( retItem->GetName2()[0] && ( strcmp( retItem->GetName2(), "#" ) ) )
retItem->SetName( retItem->GetName2() ); // Item identified!
}
}
else
Console << "Warning: Bad Shopping List " << list << " with no Vendor Buy Pack for NPC " << c << " (serial: " << c->GetSerial() << myendl;
break;
case DFNTAG_SELLITEM:
if( ValidateObject( sellLayer ) )
{
retItem = Items->CreateBaseScriptItem( nullptr, cdata, c->WorldNumber(), 1, c->GetInstanceID(), OT_ITEM, 0xFFFF, shouldSave );
if( retItem != nullptr )
{
retItem->SetCont( sellLayer );
//retitem->SetSellValue( retitem->GetBuyValue() / 2 );
retItem->PlaceInPack();
if( retItem->GetName2()[0] && ( strcmp( retItem->GetName2(), "#" ) ) )
retItem->SetName( retItem->GetName2() );
// Let's start out with the shops fully restocked after spawning
retItem->IncAmount( retItem->GetRestock() );
retItem->SetRestock( 0 );
}
}
else
Console << "Warning: Bad Shopping List " << list << " with no Vendor Sell Pack for NPC " << c << " (serial: " << c->GetSerial() << myendl;
break;
case DFNTAG_SHOPITEM:
if( ValidateObject( boughtLayer ) )
{
retItem = Items->CreateBaseScriptItem( nullptr, cdata, c->WorldNumber(), 1, c->GetInstanceID(), OT_ITEM, 0xFFFF, shouldSave );
if( retItem != nullptr )
{
retItem->SetCont( boughtLayer );
retItem->PlaceInPack();
if( retItem->GetName2()[0] && ( strcmp( retItem->GetName2(), "#" ) ) )
retItem->SetName( retItem->GetName2() );
}
}
else
Console << "Warning: Bad Shopping List " << list << " with no Vendor Bought Pack for NPC " << c << " (serial: " << c->GetSerial() << myendl;
break;
case DFNTAG_VALUE:
if( retItem != nullptr )
{
if( !cdata.empty() )
{
auto ssecs = oldstrutil::sections( cdata, " " );
if( ssecs.size() > 1 )
{
retItem->SetBuyValue( static_cast<UI32>(std::stoul(oldstrutil::trim( oldstrutil::removeTrailing( ssecs[0], "//" )), nullptr, 0)) );
retItem->SetSellValue( static_cast<UI32>(std::stoul(oldstrutil::trim( oldstrutil::removeTrailing( ssecs[1], "//" )), nullptr, 0)) );
break;
}
}
retItem->SetBuyValue( ndata );
retItem->SetSellValue( (ndata / 2) );
}
break;
default:
break;
}
}
}
//o-----------------------------------------------------------------------------------------------o
//| Function - void setRandomName( CChar *s, const std::string& namelist )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Sets a character with a random name from NPC namelists in namelists.dfn
//o-----------------------------------------------------------------------------------------------o
void setRandomName( CChar *s, const std::string& namelist )
{
std::string sect = std::string("RANDOMNAME ") + namelist;
sect = oldstrutil::trim( oldstrutil::removeTrailing( sect, "//" ));
std::string tempName;
ScriptSection *RandomName = FileLookup->FindEntry( sect, npc_def );
if( RandomName == nullptr )
{
tempName = std::string("Error Namelist ") + namelist+std::string(" Not Found");
}
else
{
size_t i = RandomName->NumEntries();
if( i > 0 )
{
i = RandomNum( static_cast<size_t>(0), i - 1 );
tempName = RandomName->MoveTo( static_cast<SI16>(i) );
}
}
s->SetName( tempName );
}
//o-----------------------------------------------------------------------------------------------o
//| Function - UI16 addRandomColor( const std::string& colorlist )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Randomly colors character
//o-----------------------------------------------------------------------------------------------o
UI16 addRandomColor( const std::string& colorlist )
{
std::string sect = std::string("RANDOMCOLOR ") + colorlist;
sect = oldstrutil::trim( oldstrutil::removeTrailing( sect, "//" ));
ScriptSection *RandomColours = FileLookup->FindEntry( sect, colors_def );
if( RandomColours == nullptr )
{
Console.warning( oldstrutil::format("Error Colorlist %s Not Found", colorlist.c_str() ));
return 0;
}
size_t i = RandomColours->NumEntries();
if( i > 0 )
{
i = RandomNum( static_cast<size_t>(0), i - 1 );
std::string tag = RandomColours->MoveTo( static_cast<SI16>(i) );
return static_cast<UI16>(std::stoul(oldstrutil::trim( oldstrutil::removeTrailing( tag, "//" )), nullptr, 0));
}
return 0;
}
//o-----------------------------------------------------------------------------------------------o
//| Function - void MakeShop( CChar *c )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Turn an NPC into a shopkeeper
//o-----------------------------------------------------------------------------------------------o
void MakeShop( CChar *c )
{
if( !ValidateObject( c ) )
return;
c->SetShop( true );
CItem *tPack = nullptr;
for( UI08 i = IL_SELLCONTAINER; i <= IL_BUYCONTAINER; ++i )
{
tPack = c->GetItemAtLayer( static_cast<ItemLayers>(i) );
if( !ValidateObject( tPack ) )
{
bool shouldSave = c->ShouldSave();
tPack = Items->CreateItem( nullptr, c, 0x2AF8, 1, 0, OT_ITEM, false, shouldSave );
if( ValidateObject( tPack ) )
{
tPack->SetDecayable( false );
tPack->SetLayer( static_cast<ItemLayers>(i) );
if( !tPack->SetCont( c ) )
tPack->Delete();
else
{
tPack->SetType( IT_CONTAINER );
tPack->SetNewbie( true );
}
}
}
}
c->Update();
}
//o-----------------------------------------------------------------------------------------------o
//| Function - bool cCharStuff::ApplyNpcSection( CChar *applyTo, ScriptSection *NpcCreation, bool isGate )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Apply NPC DFN sections to an NPC
//o-----------------------------------------------------------------------------------------------o
bool cCharStuff::ApplyNpcSection( CChar *applyTo, ScriptSection *NpcCreation, std::string sectionID, bool isGate )
{
if( NpcCreation == nullptr || !ValidateObject( applyTo ) )
return false;
UI16 haircolor = INVALIDCOLOUR;
CItem *buyPack = nullptr, *boughtPack = nullptr, *sellPack = nullptr;
CItem *retitem = nullptr, *mypack = nullptr;
std::string cdata;
SI32 ndata = -1, odata = -1;
UI08 skillToSet = 0xFF;
TAGMAPOBJECT customTag;
std::string customTagName;
std::string customTagStringValue;
for( DFNTAGS tag = NpcCreation->FirstTag(); !NpcCreation->AtEndTags(); tag = NpcCreation->NextTag() )
{
cdata = NpcCreation->GrabData( ndata, odata );
cdata = oldstrutil::trim( oldstrutil::removeTrailing( cdata, "//" ));
auto ssects = oldstrutil::sections( cdata, " " );
auto csects = oldstrutil::sections( cdata, "," );
switch( tag )
{
case DFNTAG_ALCHEMY: skillToSet = ALCHEMY; break;
case DFNTAG_ANATOMY: skillToSet = ANATOMY; break;
case DFNTAG_ANIMALLORE: skillToSet = ANIMALLORE; break;
case DFNTAG_ARMSLORE: skillToSet = ARMSLORE; break;
case DFNTAG_ARCHERY: skillToSet = ARCHERY; break;
case DFNTAG_DAMAGE:
case DFNTAG_ATT:
if( ndata >= 0 )
{
if( odata && odata > ndata )
{
applyTo->SetLoDamage( static_cast<SI16>( ndata ) );
applyTo->SetHiDamage( static_cast<SI16>( odata ) );
}
else
{
applyTo->SetLoDamage( static_cast<SI16>( ndata ) );
applyTo->SetHiDamage( static_cast<SI16>( ndata ) );
}
}
else
Console.warning( oldstrutil::format("Invalid data found in ATT/DAMAGE tag inside NPC script [%s]", sectionID.c_str() ));
break;
case DFNTAG_AWAKE:
if( !isGate )
applyTo->SetAwake(ndata != 0);
break;
case DFNTAG_BACKPACK:
if( !isGate )
{
if( mypack == nullptr )
mypack = applyTo->GetPackItem();
if( mypack == nullptr )
{
bool shouldSave = applyTo->ShouldSave();
mypack = Items->CreateItem( nullptr, applyTo, 0x0E75, 1, 0, OT_ITEM, false, shouldSave );
if( ValidateObject( mypack ) )
{
mypack->SetDecayable( false );
applyTo->SetPackItem( mypack );
mypack->SetName( "Backpack" );
mypack->SetLayer( IL_PACKITEM );
if( !mypack->SetCont( applyTo ) )
mypack = nullptr;
else
{
mypack->SetX( 0 );
mypack->SetY( 0 );
mypack->SetZ( 0 );
mypack->SetType( IT_CONTAINER );
mypack->SetDye( true );
mypack->SetMaxItems( cwmWorldState->ServerData()->MaxPlayerPackItems() );
}
}
}
}
break;
case DFNTAG_BEGGING: skillToSet = BEGGING; break;
case DFNTAG_BLACKSMITHING: skillToSet = BLACKSMITHING; break;
case DFNTAG_BOWCRAFT: skillToSet = BOWCRAFT; break;
case DFNTAG_BUSHIDO: skillToSet = BUSHIDO; break;
case DFNTAG_CAMPING: skillToSet = CAMPING; break;
case DFNTAG_CARPENTRY: skillToSet = CARPENTRY; break;
case DFNTAG_CARTOGRAPHY: skillToSet = CARTOGRAPHY; break;
case DFNTAG_CARVE:
if( !isGate )
applyTo->SetCarve( static_cast<SI16>(ndata) );
break;
case DFNTAG_CHIVALRY: skillToSet = CHIVALRY; break;
case DFNTAG_COOKING: skillToSet = COOKING; break;
case DFNTAG_COLOUR:
if( retitem != nullptr )
retitem->SetColour( static_cast<UI16>(ndata) );
break;
case DFNTAG_COLOURLIST:
if( retitem != nullptr )
retitem->SetColour( addRandomColor( cdata ) );
break;
case DFNTAG_COLOURMATCHHAIR:
if( retitem != nullptr )
retitem->SetColour( static_cast<UI16>(haircolor) );
break;
case DFNTAG_CONTROLSLOTS:
if( !isGate )
applyTo->SetControlSlots( static_cast<UI16>( ndata ) );
break;
case DFNTAG_DEX:
if( ndata > 0 )
{
if( odata && odata > ndata )
{
applyTo->SetDexterity( static_cast<SI16>(RandomNum( ndata, odata )) );
}
else
{
applyTo->SetDexterity( ndata );
}
applyTo->SetStamina( applyTo->GetMaxStam() );
}
else
Console.warning( oldstrutil::format("Invalid data found in DEX tag inside NPC script [%s]", sectionID.c_str() ));
break;
case DFNTAG_DETECTINGHIDDEN: skillToSet = DETECTINGHIDDEN; break;
case DFNTAG_DEF:
if( ndata >= 0 )
{
if( odata && odata > ndata )
{
applyTo->SetResist( static_cast<UI16>(RandomNum( ndata, odata )), PHYSICAL );
}
else
{
applyTo->SetResist( static_cast<UI16>(ndata), PHYSICAL );
}
}
else
Console.warning( oldstrutil::format("Invalid data found in DEF tag inside NPC script [%s]", sectionID.c_str() ));
break;
case DFNTAG_DIR:
if( !isGate )
{
std::string cupper = oldstrutil::upper( cdata );
if( cupper == "NE" ) {
applyTo->SetDir( NORTHEAST );
}
else if( cupper == "E" ) {
applyTo->SetDir( EAST );
}
else if( cupper == "SE" ) {
applyTo->SetDir( SOUTHEAST );
}
else if( cupper == "S" ) {
applyTo->SetDir( SOUTH );
}
else if( cupper == "SW" ) {
applyTo->SetDir( SOUTHWEST );
}
else if( cupper == "W" ) {
applyTo->SetDir( WEST );
}
else if( cupper == "NW" ) {
applyTo->SetDir( NORTHWEST );
}
else if( cupper == "N" ){
applyTo->SetDir( NORTH );
}
else if( cupper == "RND" )
{
UI08 rndDir = RandomNum( 0, 7 );
switch( rndDir )
{
case 0:
applyTo->SetDir( NORTHEAST );
break;
case 1:
applyTo->SetDir( EAST );
break;
case 2:
applyTo->SetDir( SOUTHEAST );
break;
case 3:
applyTo->SetDir( SOUTH );
break;
case 4:
applyTo->SetDir( SOUTHWEST );
break;
case 5:
applyTo->SetDir( WEST );
break;
case 6:
applyTo->SetDir( NORTHWEST );
break;
case 7:
applyTo->SetDir( NORTH );
break;
}
}
}
break;
case DFNTAG_ELEMENTRESIST:
if( ssects.size() >= 4 )
{
applyTo->SetResist( ( static_cast<UI16>(std::stoul(oldstrutil::trim( oldstrutil::removeTrailing( ssects[0], "//" )), nullptr, 0)) ), HEAT );
applyTo->SetResist( ( static_cast<UI16>(std::stoul(oldstrutil::trim( oldstrutil::removeTrailing( ssects[1], "//" )), nullptr, 0)) ), COLD );
applyTo->SetResist( ( static_cast<UI16>(std::stoul(oldstrutil::trim( oldstrutil::removeTrailing( ssects[2], "//" )), nullptr, 0)) ), LIGHTNING );
applyTo->SetResist( ( static_cast<UI16>(std::stoul(oldstrutil::trim( oldstrutil::removeTrailing( ssects[3], "//" )), nullptr, 0)) ), POISON );
}
break;
case DFNTAG_EMOTECOLOUR:
if( !isGate )
applyTo->SetEmoteColour( static_cast<UI16>(ndata) );
break;
case DFNTAG_ENTICEMENT: skillToSet = ENTICEMENT; break;
case DFNTAG_EQUIPITEM:
if( !isGate )
{
bool shouldSave = applyTo->ShouldSave();
retitem = Items->CreateBaseScriptItem( nullptr, cdata, applyTo->WorldNumber(), 1, applyTo->GetInstanceID(), OT_ITEM, 0xFFFF, shouldSave );
if( retitem != nullptr )
{
if( retitem->GetLayer() == IL_NONE )
Console << "Warning: Bad NPC Script ([" << sectionID.c_str() << "]) with problem item " << cdata << " executed!" << myendl;
else if( !retitem->SetCont( applyTo ) )
{
if( !retitem->SetCont( applyTo->GetPackItem() ) )
retitem = nullptr;
}
if( retitem->GetLayer() == 25 )
{
// Item equipped on mount layer. Set NPC as mounted!
applyTo->SetMounted( true );
}
}
}
break;
case DFNTAG_EVALUATINGINTEL: skillToSet = EVALUATINGINTEL; break;
case DFNTAG_FAME: applyTo->SetFame( static_cast<SI16>(ndata) ); break;
case DFNTAG_FENCING: skillToSet = FENCING; break;
case DFNTAG_FISHING: skillToSet = FISHING; break;
case DFNTAG_FLEEAT:
if( !isGate )
applyTo->SetFleeAt( static_cast<SI16>(ndata) );
break;
case DFNTAG_FLEEINGSPEED:
applyTo->SetFleeingSpeed( std::stof(cdata) );
break;
case DFNTAG_FLEEINGSPEEDMOUNTED:
applyTo->SetMountedFleeingSpeed( std::stof(cdata) );
break;
case DFNTAG_FLAG:
if( !isGate )
{
if( !cdata.empty() )
{
std::string upperData = oldstrutil::upper( cdata );
if( upperData == "NEUTRAL" )
{
applyTo->SetNPCFlag( fNPC_NEUTRAL );
}
else if( upperData == "INNOCENT" )
{
applyTo->SetNPCFlag( fNPC_INNOCENT );
}
else if( upperData == "EVIL" )
{
applyTo->SetNPCFlag( fNPC_EVIL );
}
}
}
break;
case DFNTAG_FORENSICS: skillToSet = FORENSICS; break;
case DFNTAG_FOCUS: skillToSet = FOCUS; break;
case DFNTAG_FX1:
if( !isGate )
applyTo->SetFx( static_cast<SI16>(ndata), 0 );
break;
case DFNTAG_FX2:
if( !isGate )
applyTo->SetFx( static_cast<SI16>(ndata), 1 );
break;
case DFNTAG_FY1:
if( !isGate )
applyTo->SetFy( static_cast<SI16>(ndata), 0 );
break;
case DFNTAG_FY2:
if( !isGate )
applyTo->SetFy( static_cast<SI16>(ndata), 1 );
break;
case DFNTAG_FZ1:
if( !isGate )
applyTo->SetFz( static_cast<SI08>(ndata) );
break;
case DFNTAG_FOOD: applyTo->SetFood( cdata ); break;
case DFNTAG_GET:
{
std::string scriptEntry = "";
if( ssects.size() == 1 )
{
scriptEntry = cdata;
}
else
{
SI32 rndEntry = RandomNum( 0, static_cast<SI32>(ssects.size()-1));
scriptEntry = oldstrutil::trim( oldstrutil::removeTrailing( ssects[rndEntry], "//" ));
}
ScriptSection *toFind = FileLookup->FindEntry( scriptEntry, npc_def );
if( toFind == nullptr )
Console.warning( oldstrutil::format( "Invalid script entry ([%s]) called with GET tag, NPC serial 0x%X", scriptEntry.c_str(), applyTo->GetSerial() ));
else if( toFind == NpcCreation )
Console.warning( oldstrutil::format( "Infinite loop avoided with GET tag inside NPC script [%s]", scriptEntry.c_str() ));
else
ApplyNpcSection( applyTo, toFind, scriptEntry, isGate );
break;
}
break;
case DFNTAG_GOLD:
if( !isGate )
{
if( !ValidateObject( mypack ) )
mypack = applyTo->GetPackItem();
if( ValidateObject( mypack ) )
{
if( ndata >= 0 )
{
bool shouldSave = applyTo->ShouldSave();
if( odata && odata > ndata )
{
retitem = Items->CreateScriptItem( nullptr, applyTo, "0x0EED", static_cast<UI16>(RandomNum( ndata, odata )), OT_ITEM, true, 0xFFFF, shouldSave );
}
else
{
retitem = Items->CreateScriptItem( nullptr, applyTo, "0x0EED", ndata, OT_ITEM, true, 0xFFFF, shouldSave );
}
}
else
Console.warning( oldstrutil::format("Invalid data found in GOLD tag inside NPC script [%s]", sectionID.c_str() ));
}
else
Console.warning( oldstrutil::format("Bad NPC Script ([%s]) with problem no backpack for gold", sectionID.c_str() ));
}
break;
case DFNTAG_HAIRCOLOUR:
haircolor = addRandomColor( cdata );
if( retitem != nullptr )
retitem->SetColour( haircolor );
break;
case DFNTAG_HEALING: skillToSet = HEALING; break;
case DFNTAG_HERDING: skillToSet = HERDING; break;
case DFNTAG_HIDING: skillToSet = HIDING; break;
case DFNTAG_HIDAMAGE: applyTo->SetHiDamage( static_cast<SI16>(ndata) ); break;
case DFNTAG_HIRELING:
if( !isGate )
applyTo->SetCanHire( true );
break;
case DFNTAG_HP:
if( ndata >= 0 )
{
if( odata && odata > ndata )
{
applyTo->SetHP( static_cast<SI16>(RandomNum( ndata, odata )) );
}
else
{
applyTo->SetHP( ndata );
}
}
else
Console.warning( oldstrutil::format("Invalid data found in HP tag inside NPC script [%s]", sectionID.c_str() ));
break;
case DFNTAG_HPMAX:
if( ndata >= 0 )
{
if( odata && odata > ndata )
{
applyTo->SetFixedMaxHP( static_cast<SI16>(RandomNum( ndata, odata )) );
}
else
{
applyTo->SetFixedMaxHP( ndata );
}
// Update current HP
applyTo->SetHP( applyTo->GetMaxHP() );
}
else
Console.warning( oldstrutil::format("Invalid data found in HPMAX tag inside NPC script [%s]", sectionID.c_str() ));
break;
case DFNTAG_ID:
/*applyTo->SetID( static_cast<UI16>(ndata) );
applyTo->SetOrgID( static_cast<UI16>(ndata) );
break;*/
if( ssects.size() == 1 )
{
applyTo->SetID( static_cast<UI16>(std::stoul(oldstrutil::trim( oldstrutil::removeTrailing( ssects[0], "//" )), nullptr, 0)) );
applyTo->SetOrgID( static_cast<UI16>(std::stoul(oldstrutil::trim( oldstrutil::removeTrailing( ssects[0], "//" )), nullptr, 0)) );
}
else
{
SI32 rndEntry = RandomNum( 0, static_cast<SI32>(ssects.size()-1));
applyTo->SetID( static_cast<UI16>(std::stoul(oldstrutil::trim( oldstrutil::removeTrailing( ssects[rndEntry], "//" )), nullptr, 0)) );
applyTo->SetOrgID( static_cast<UI16>(std::stoul(oldstrutil::trim( oldstrutil::removeTrailing( ssects[rndEntry], "//" )), nullptr, 0)) );
}
break;
case DFNTAG_IMBUING: skillToSet = IMBUING; break;
case DFNTAG_INSCRIPTION: skillToSet = INSCRIPTION; break;
case DFNTAG_INTELLIGENCE:
if( ndata >= 0 )
{
if( odata && odata > ndata )
{
applyTo->SetIntelligence( static_cast<SI16>(RandomNum( ndata, odata )) );
}
else
{
applyTo->SetIntelligence( ndata );
}
applyTo->SetMana( applyTo->GetMaxMana() );
}
else
Console.warning( oldstrutil::format("Invalid data found in INTELLIGENCE tag inside NPC script [%s]", sectionID.c_str() ));
break;
case DFNTAG_ITEMID: skillToSet = ITEMID; break;
case DFNTAG_KARMA: applyTo->SetKarma( static_cast<SI16>(ndata) ); break;
case DFNTAG_PACKITEM:
[[fallthrough]];
case DFNTAG_LOOT:
if( !isGate )
{
if( !ValidateObject( mypack ) )
mypack = applyTo->GetPackItem();
if( ValidateObject( mypack ) )
{
if( !cdata.empty() )
{
auto csecs = oldstrutil::sections( oldstrutil::trim( oldstrutil::removeTrailing( cdata, "//" )), "," );
if( csecs.size() > 1 )
{
UI16 iAmount = 0;
std::string amountData = oldstrutil::trim( oldstrutil::removeTrailing( csecs[1], "//" ));
auto tsects = oldstrutil::sections( amountData, " " );
if( tsects.size() > 1 ) // check if the second part of the tag-data contains two sections separated by a space
{
auto first = static_cast<UI16>(std::stoul(oldstrutil::trim( oldstrutil::removeTrailing( tsects[0], "//" )), nullptr, 0));
auto second = static_cast<UI16>(std::stoul(oldstrutil::trim( oldstrutil::removeTrailing( tsects[1], "//" )), nullptr, 0));
// Tag contained a minimum and maximum value for amount! Let's randomize!
iAmount = static_cast<UI16>(RandomNum( first, second ));
}
else
{
iAmount = static_cast<UI16>(std::stoul(amountData, nullptr, 0));
}
auto tdata = oldstrutil::trim( oldstrutil::removeTrailing( csecs[0], "//" ));
if( tag == DFNTAG_LOOT )
Items->AddRespawnItem( mypack, tdata, true, true, iAmount, true );
else
Items->AddRespawnItem( mypack, tdata, true, false, iAmount, false );
}
else
{
if( tag == DFNTAG_LOOT )
Items->AddRespawnItem( mypack, cdata, true, true, 1, true );
else
Items->AddRespawnItem( mypack, cdata, true, false, 1, false );
}
}
}
else
Console.warning( oldstrutil::format("Bad NPC Script ([%s]) with problem no backpack for loot", sectionID.c_str() ) );
}
break;
case DFNTAG_LOCKPICKING: skillToSet = LOCKPICKING; break;
case DFNTAG_LODAMAGE: applyTo->SetLoDamage( static_cast<SI16>(ndata) ); break;
case DFNTAG_LUMBERJACKING: skillToSet = LUMBERJACKING; break;
case DFNTAG_LOYALTY:
applyTo->SetLoyalty( static_cast<UI16>( ndata ) );
break;
case DFNTAG_MACEFIGHTING: skillToSet = MACEFIGHTING; break;
case DFNTAG_MAGERY: skillToSet = MAGERY; break;
case DFNTAG_MAGICRESISTANCE: skillToSet = MAGICRESISTANCE; break;
case DFNTAG_MANA:
if( ndata >= 0 )
{
if( odata && odata > ndata )
{
applyTo->SetMana( static_cast<SI16>(RandomNum( ndata, odata )) );
}
else
{
applyTo->SetMana( ndata );
}
}
else
Console.warning( oldstrutil::format("Invalid data found in MANA tag inside NPC script [%s]", sectionID.c_str() ));
break;
case DFNTAG_MANAMAX:
if( ndata >= 0 )
{
if( odata && odata > ndata )
{
applyTo->SetFixedMaxMana( static_cast<SI16>(RandomNum( ndata, odata )) );
}
else
{
applyTo->SetFixedMaxMana( ndata );
}
// Update current Mana
applyTo->SetMana( applyTo->GetMaxMana() );
}
else
Console.warning( oldstrutil::format("Invalid data found in MANAMAX tag inside NPC script [%s]", sectionID.c_str() ));
break;
case DFNTAG_MAXLOYALTY:
applyTo->SetMaxLoyalty( static_cast<UI16>( ndata ) );
break;
case DFNTAG_MEDITATION: skillToSet = MEDITATION; break;
case DFNTAG_MINING: skillToSet = MINING; break;
case DFNTAG_MUSICIANSHIP: skillToSet = MUSICIANSHIP; break;
case DFNTAG_MYSTICISM: skillToSet = MYSTICISM; break;
case DFNTAG_NAME: applyTo->SetName( cdata ); break;
case DFNTAG_NAMELIST: setRandomName( applyTo, cdata ); break;
case DFNTAG_NECROMANCY: skillToSet = NECROMANCY; break;
case DFNTAG_NINJITSU: skillToSet = NINJITSU; break;
case DFNTAG_NPCWANDER:
if( !isGate )
applyTo->SetNpcWander( static_cast<SI08>(ndata) );
break;
case DFNTAG_NPCAI:
if( !isGate )
applyTo->SetNPCAiType( static_cast<SI16>(ndata) );
break;
case DFNTAG_NOHIRELING:
if( !isGate )
applyTo->SetCanHire( false );
break;
case DFNTAG_NOTRAIN:
if( !isGate )
applyTo->SetCanTrain( false );
break;
case DFNTAG_POISONSTRENGTH: applyTo->SetPoisonStrength( static_cast<UI08>(ndata) ); break;
case DFNTAG_PRIV:
if( !isGate )
applyTo->SetPriv( static_cast<UI16>(ndata) );
break;
case DFNTAG_PARRYING: skillToSet = PARRYING; break;
case DFNTAG_PEACEMAKING: skillToSet = PEACEMAKING; break;
case DFNTAG_PROVOCATION: skillToSet = PROVOCATION; break;
case DFNTAG_POISONING: skillToSet = POISONING; break;
case DFNTAG_RESISTFIRE:
if( ndata >= 0 )
{
if( odata && odata > ndata )
{
applyTo->SetResist( static_cast<UI16>(RandomNum( ndata, odata )), HEAT );
}
else
{
applyTo->SetResist( ndata, HEAT );
}
}
else
Console.warning( oldstrutil::format("Invalid data found in RESISTFIRE tag inside NPC script [%s]", sectionID.c_str() ));
break;
case DFNTAG_RESISTCOLD:
if( ndata >= 0 )
{
if( odata && odata > ndata )
{
applyTo->SetResist( static_cast<UI16>(RandomNum( ndata, odata )), COLD );
}
else
{
applyTo->SetResist( ndata, COLD );
}
}
else
Console.warning( oldstrutil::format("Invalid data found in RESISTCOLD tag inside NPC script [%s]", sectionID.c_str() ));
break;
case DFNTAG_RESISTLIGHTNING:
if( ndata >= 0 )
{
if( odata && odata > ndata )
{
applyTo->SetResist( static_cast<UI16>(RandomNum( ndata, odata )), LIGHTNING );
}
else
{
applyTo->SetResist( ndata, LIGHTNING );
}
}
else
Console.warning( oldstrutil::format("Invalid data found in RESISTLIGHTNING tag inside NPC script [%s]", sectionID.c_str() ));
break;
case DFNTAG_RESISTPOISON:
if( ndata >= 0 )
{
if( odata && odata > ndata )
{
applyTo->SetResist( static_cast<UI16>(RandomNum( ndata, odata )), POISON );
}
else
{
applyTo->SetResist( ndata, POISON );
}
}
else
Console.warning( oldstrutil::format("Invalid data found in RESISTPOISON tag inside NPC script [%s]", sectionID.c_str() ));
break;
case DFNTAG_RSHOPITEM:
if( !isGate )
{
if( !ValidateObject( buyPack ) )
buyPack = applyTo->GetItemAtLayer( IL_BUYCONTAINER );
if( ValidateObject( buyPack ) )
{
retitem = Items->CreateBaseScriptItem( nullptr, cdata, applyTo->WorldNumber(), 1 );
if( retitem != nullptr )
{
retitem->SetCont( buyPack );
retitem->PlaceInPack();
if( retitem->GetName2()[0] && ( strcmp( retitem->GetName2(), "#" )))
retitem->SetName( retitem->GetName2() ); // Item identified!
}
}
else
Console.warning( oldstrutil::format("Bad NPC Script ([%s]) with no Vendor Buy Pack for item", sectionID.c_str() ) );
}
break;
case DFNTAG_REATTACKAT:
if( !isGate )
applyTo->SetReattackAt( static_cast<SI16>(ndata) );
break;
case DFNTAG_REMOVETRAP: skillToSet = REMOVETRAP; break;
case DFNTAG_RACE: applyTo->SetRace( static_cast<UI16>(ndata)); break;
case DFNTAG_RUNS:
if( !isGate )
applyTo->SetRun( true );
break;
case DFNTAG_RUNNINGSPEED:
applyTo->SetRunningSpeed( std::stof(cdata) );
break;
case DFNTAG_RUNNINGSPEEDMOUNTED:
applyTo->SetMountedRunningSpeed( std::stof(cdata) );
break;
case DFNTAG_SKIN: applyTo->SetSkin( static_cast<UI16>(ndata) ); break;
case DFNTAG_SHOPKEEPER:
if( !isGate )
MakeShop( applyTo );
break;
case DFNTAG_SHOPLIST:
if( !isGate )
LoadShopList( cdata, applyTo );
break;
case DFNTAG_SELLITEM:
if( !isGate )
{
if( !ValidateObject( sellPack ) )
sellPack = applyTo->GetItemAtLayer( IL_SELLCONTAINER );
if( ValidateObject( sellPack ) )
{
bool shouldSave = applyTo->ShouldSave();
retitem = Items->CreateBaseScriptItem( nullptr, cdata, applyTo->WorldNumber(), 1, applyTo->GetInstanceID(), OT_ITEM, 0xFFFF, shouldSave );
if( retitem != nullptr )
{
retitem->SetCont( sellPack );
retitem->PlaceInPack();
if( retitem->GetName2()[0] && ( strcmp( retitem->GetName2(), "#" ) ) )
retitem->SetName( retitem->GetName2() );
}
}
else
Console.warning( oldstrutil::format("Bad NPC Script ([%s]) with no Vendor SellPack for item", sectionID.c_str() ));
}
break;
case DFNTAG_SHOPITEM:
if( !isGate )
{
if( !ValidateObject( boughtPack ) )
boughtPack = applyTo->GetItemAtLayer( IL_BOUGHTCONTAINER );
if( ValidateObject( boughtPack ) )
{
bool shouldSave = applyTo->ShouldSave();
retitem = Items->CreateBaseScriptItem( nullptr, cdata, applyTo->WorldNumber(), 1, applyTo->GetInstanceID(), OT_ITEM, 0xFFFF, shouldSave );
if( retitem != nullptr )
{
retitem->SetCont( boughtPack );
retitem->PlaceInPack();
if( retitem->GetName2()[0] && ( strcmp( retitem->GetName2(), "#" ) ) )
retitem->SetName( retitem->GetName2() );
}
}
else
Console.warning(oldstrutil::format( "Bad NPC Script ([%s]) with no Vendor Bought Pack for item", sectionID.c_str() ) );
}
break;
case DFNTAG_SAYCOLOUR:
if( !isGate )
applyTo->SetSayColour( static_cast<UI16>(ndata) );
break;
case DFNTAG_SPATTACK:
if( !isGate )
applyTo->SetSpAttack( static_cast<SI16>(ndata) );
break;
case DFNTAG_SPADELAY:
if( !isGate )
applyTo->SetSpDelay( static_cast<SI08>(ndata) );
break;
case DFNTAG_SPELLWEAVING: skillToSet = SPELLWEAVING; break;
case DFNTAG_SPLIT:
if( !isGate )
applyTo->SetSplit( static_cast<UI08>(ndata) );
break;
case DFNTAG_SPLITCHANCE:
if( !isGate )
applyTo->SetSplitChance( static_cast<UI08>(ndata) );
break;
case DFNTAG_SNOOPING: skillToSet = SNOOPING; break;
case DFNTAG_SPIRITSPEAK: skillToSet = SPIRITSPEAK; break;
case DFNTAG_STEALING: skillToSet = STEALING; break;
case DFNTAG_STRENGTH:
if( ndata >= 0 )
{
if( odata && odata > ndata )
{
applyTo->SetStrength( static_cast<SI16>(RandomNum( ndata, odata )) );
}
else
{
applyTo->SetStrength( ndata );
}
applyTo->SetHP( applyTo->GetMaxHP() );
}
else
Console.warning( oldstrutil::format("Invalid data found in STRENGTH tag inside NPC script [%s]", sectionID.c_str() ));
break;
case DFNTAG_SWORDSMANSHIP: skillToSet = SWORDSMANSHIP; break;
case DFNTAG_STAMINA:
if( ndata >= 0 )
{
if( odata && odata > ndata )
{
applyTo->SetStamina( static_cast<SI16>(RandomNum( ndata, odata )) );
}
else
{
applyTo->SetStamina( ndata );
}
}
else
Console.warning( oldstrutil::format("Invalid data found in STAMINA tag inside NPC script [%s]", sectionID.c_str() ));
break;
case DFNTAG_STAMINAMAX:
if( ndata >= 0 )
{
if( odata && odata > ndata )
{
applyTo->SetFixedMaxStam( static_cast<SI16>(RandomNum( ndata, odata )) );
}
else
{
applyTo->SetFixedMaxStam( ndata );
}
// Update current Stamina
applyTo->SetStamina( applyTo->GetMaxStam() );
}
else
Console.warning( oldstrutil::format("Invalid data found in STAMINA tag inside NPC script [%s]", sectionID.c_str() ));
break;
case DFNTAG_STEALTH: skillToSet = STEALTH; break;
case DFNTAG_SKINLIST: applyTo->SetSkin( addRandomColor( cdata ) ); break;
case DFNTAG_SKILL: applyTo->SetBaseSkill( static_cast<UI16>(odata), static_cast<UI08>(ndata) ); break;
case DFNTAG_SCRIPT:
if( !isGate )
{
applyTo->AddScriptTrigger( static_cast<UI16>(ndata) );
}
break;
case DFNTAG_THROWING: skillToSet = THROWING; break;
case DFNTAG_TITLE: applyTo->SetTitle( cdata ); break;
case DFNTAG_TOTAME:
if( !isGate )
{
applyTo->SetTaming( static_cast<SI16>(ndata) );
applyTo->SetOrneriness( static_cast<UI16>(ndata) ); // Set default difficulty to control equal to skill required to tame
}
break;
case DFNTAG_TOPROV:
if( !isGate )
applyTo->SetProvoing( static_cast<SI16>(ndata) );
break;
case DFNTAG_TOPEACE:
if( !isGate )
applyTo->SetPeaceing( static_cast<SI16>(ndata) );
applyTo->SetBrkPeaceChanceGain( static_cast<SI16>(odata) );
break;
case DFNTAG_TAMEDHUNGER:
if( !isGate )
{
applyTo->SetTamedHungerRate( static_cast<UI16>(ndata) );
applyTo->SetTamedHungerWildChance( static_cast<UI08>(odata) );
}
break;
case DFNTAG_TAMEDTHIRST:
if( !isGate )
{
applyTo->SetTamedThirstRate( static_cast<UI16>( ndata ) );
applyTo->SetTamedThirstWildChance( static_cast<UI08>( odata ) );
}
break;
case DFNTAG_WILLHUNGER:
if( !isGate )
{
if( ndata > 0 )
applyTo->SetHungerStatus( true );
else
applyTo->SetHungerStatus( false );
}
break;
case DFNTAG_WALKINGSPEED:
applyTo->SetWalkingSpeed( std::stof(cdata) );
break;
case DFNTAG_WALKINGSPEEDMOUNTED:
applyTo->SetMountedWalkingSpeed( std::stof(cdata) );
break;
case DFNTAG_TACTICS: skillToSet = TACTICS; break;
case DFNTAG_TAILORING: skillToSet = TAILORING; break;
case DFNTAG_TAMING: skillToSet = TAMING; break;
case DFNTAG_TASTEID: skillToSet = TASTEID; break;
case DFNTAG_TINKERING: skillToSet = TINKERING; break;
case DFNTAG_TRACKING: skillToSet = TRACKING; break;
case DFNTAG_VETERINARY: skillToSet = VETERINARY; break;
case DFNTAG_WRESTLING: skillToSet = WRESTLING; break;
case DFNTAG_CUSTOMSTRINGTAG:
{
auto count = 0;
std::string result;
for( auto &sec : ssects )
{
if( count > 0 )
{
if( count == 1 )
{
result = oldstrutil::trim( oldstrutil::removeTrailing( sec, "//" ));
}
else
{
result = result + " " + oldstrutil::trim( oldstrutil::removeTrailing( sec, "//" ));
}
}
count++;
}
customTagName = oldstrutil::trim( oldstrutil::removeTrailing( ssects[0], "//" ));
customTagStringValue = result;
if( !customTagName.empty() && !customTagStringValue.empty() )
{
customTag.m_Destroy = false;
customTag.m_StringValue = customTagStringValue;
customTag.m_IntValue = static_cast<SI32>(customTag.m_StringValue.size());
customTag.m_ObjectType = TAGMAP_TYPE_STRING;
applyTo->SetTag( customTagName, customTag);
}
else
{
Console.warning( oldstrutil::format( "Invalid data found in CUSTOMSTRINGTAG tag inside NPC script [%s] - Supported data format: <tagName> <text>", sectionID.c_str() ) );
}
break;
}
case DFNTAG_CUSTOMINTTAG:
{
auto count = 0;
std::string result;
for( auto &sec : ssects )
{
if( count > 0 )
{
if( count == 1 )
{
result = oldstrutil::trim( oldstrutil::removeTrailing( sec, "//" ));
}
}
count++;
}
customTagName = oldstrutil::trim( oldstrutil::removeTrailing( ssects[0], "//" ));
customTagStringValue = result;
if( !customTagName.empty() && !customTagStringValue.empty() )
{
customTag.m_Destroy = false;
customTag.m_IntValue = std::stoi( customTagStringValue );
customTag.m_ObjectType = TAGMAP_TYPE_INT;
customTag.m_StringValue = "";
applyTo->SetTag( customTagName, customTag );
if( count > 2 )
{
Console.warning( oldstrutil::format( "Multiple values detected for CUSTOMINTTAG in NPC script [%s] - only first value will be used! Supported data format: <tagName> <value>", sectionID.c_str() ) );
}
}
else
{
Console.warning( oldstrutil::format( "Invalid data found in CUSTOMINTTAG tag in NPC script [%s] - Supported data format: <tagName> <value>", sectionID.c_str() ) );
}
break;
}
default:
Console << "Unknown tag in ApplyNpcSection(): " << (SI32)tag << myendl; break;
}
if( skillToSet != 0xFF )
{
applyTo->SetBaseSkill( static_cast<UI16>(RandomNum( ndata, odata )), skillToSet );
skillToSet = 0xFF;
}
}
return true;
}
bool cCharStuff::canControlPet( CChar *mChar, CChar *Npc, bool isRestricted, bool checkDifficulty, bool ignoreOwnerCheck, bool ignoreLoyaltyChanges )
{
if( ValidateObject( Npc->GetOwnerObj() ) && Npc->GetNPCAiType() != AI_PLAYERVENDOR && Npc->GetQuestType() == 0 )
{
if( !ignoreOwnerCheck && Npc->GetOwnerObj() != mChar && ( isRestricted || !Npcs->checkPetFriend( mChar, Npc ) ) )
return false;
if( checkDifficulty && Npc->IsTamed() && cwmWorldState->ServerData()->CheckPetControlDifficulty() )
{
// Let's base this on how difficult it is to control the pet, as well
UI16 chanceToControl = Skills->CalculatePetControlChance( mChar, Npc );
UI16 loyaltyGainOnSuccess = cwmWorldState->ServerData()->GetPetLoyaltyGainOnSuccess();
UI16 loyaltyLossOnFailure = cwmWorldState->ServerData()->GetPetLoyaltyLossOnFailure();
// Easy to control pets/summoned creatures don't gain/lose loyalty from commands
if( chanceToControl == 1000 )
return true;
if( chanceToControl >= RandomNum( 0, 1000 ) )
{
// Succeeded in controlling pet
if( !ignoreLoyaltyChanges )
{
Npc->SetLoyalty( std::min( 100, Npc->GetLoyalty() + loyaltyGainOnSuccess ) );
}
return true;
}
else
{
// Failed to control pet
if( !ignoreLoyaltyChanges )
{
Npc->SetLoyalty( std::max( 0, Npc->GetLoyalty() - loyaltyLossOnFailure ) );
UI16 soundToPlay = cwmWorldState->creatures[Npc->GetID()].GetSound( SND_STARTATTACK );
Effects->PlaySound( Npc, soundToPlay );
if( Npc->GetLoyalty() == 0 )
{
// Reduce player's control slot usage by the amount of control slots taken up by the pet
mChar->SetControlSlotsUsed( std::max( 0, mChar->GetControlSlotsUsed() - Npc->GetControlSlots() ) );
// Pet goes wild for lack of loyalty
Npcs->releasePet( Npc );
return false;
}
CSocket *mSock = mChar->GetSocket();
if( mSock != nullptr )
{
std::string npcName = getNpcDictName( Npc, mSock );
mSock->sysmessage( 2412, npcName.c_str() ); // %s disobeys your command
}
}
return false;
}
}
// No difficulty checking needed, just allow it
return true;
}
return false;
}
//o-----------------------------------------------------------------------------------------------o
//| Function - void finalizeTransfer( CChar *npc )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Finalize transfer of pet to new owner
//o-----------------------------------------------------------------------------------------------o
void cCharStuff::finalizeTransfer( CChar *petChar, CChar *srcChar, CChar *targChar )
{
// If the pet is guarding something, stop guarding that something
Npcs->stopPetGuarding( petChar );
// Clear pet's existing friend-list
petChar->ClearFriendList();
std::string petName = getNpcDictName( petChar );
petChar->TextMessage( nullptr, 1074, TALK, 0, petName.c_str(), targChar->GetName().c_str() ); // * %s will now take %s as his master *
// Transfer ownership
petChar->SetOwner( targChar );
// Make pet follow their new owner
petChar->SetFTarg( static_cast<CChar*>( targChar ) );
petChar->FlushPath();
petChar->SetNpcWander( WT_FOLLOW );
// Update control slots used for both old and new owners
srcChar->SetControlSlotsUsed( std::max(0, srcChar->GetControlSlotsUsed() - petChar->GetControlSlots()) );
targChar->SetControlSlotsUsed( std::clamp(targChar->GetControlSlotsUsed() + petChar->GetControlSlots(), 0, 255 ));
}
//o-----------------------------------------------------------------------------------------------o
//| Function - void releasePet( CChar *npc )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Release the pet from its owner
//o-----------------------------------------------------------------------------------------------o
void cCharStuff::releasePet( CChar *pet )
{
// Stop guarding
Npcs->stopPetGuarding( pet );
// Stop following
pet->SetFTarg( nullptr );
pet->SetNpcWander( WT_FREE );
// Remove owner
pet->SetOwner( nullptr );
std::string petName = getNpcDictName( pet );
pet->TextMessage( nullptr, 1325, TALK, 0, petName.c_str() ); // *%s appears to have decided that it is better off without a master *
// If a summoned creature, unsummon it
if( pet->GetTimer( tNPC_SUMMONTIME ) )
{
Effects->PlaySound( pet, 0x01FE );
pet->Delete();
}
// Reset hunger
pet->SetHunger( 6 );
// Increase pet orneriness, to make it more difficult to control for its next owner
auto ownerCount = pet->GetOwnerCount();
switch( ownerCount )
{
case 1:
pet->SetOrneriness( pet->GetTaming() + 48 );
break;
case 2:
pet->SetOrneriness( pet->GetTaming() + 192 );
break;
case 3:
pet->SetOrneriness( pet->GetTaming() + 432 );
break;
case 4:
pet->SetOrneriness( pet->GetTaming() + 768 );
break;
default:
break;
}
}
//o-----------------------------------------------------------------------------------------------o
//| Function - CChar * getGuardingPet( CChar *mChar, CBaseObject *guarded )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Get the pet guarding an item / character
//o-----------------------------------------------------------------------------------------------o
CChar * cCharStuff::getGuardingPet( CChar *mChar, CBaseObject *guarded )
{
if( !ValidateObject( mChar ) || !ValidateObject( guarded ) )
return nullptr;
GenericList< CChar * > *myPets = mChar->GetPetList();
for( CChar *pet = myPets->First(); !myPets->Finished(); pet = myPets->Next() )
{
if( ValidateObject( pet ) )
{
//if( !pet->GetMounted() && pet->GetNPCAiType() == AI_PET_GUARD &&
if( !pet->GetMounted() &&
pet->GetGuarding() == guarded && pet->GetOwnerObj() == mChar )
return pet;
}
}
return nullptr;
}
//o-----------------------------------------------------------------------------------------------o
//| Function - bool checkPetFriend( CChar *mChar, CChar *pet )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Search a pets friends list to check if a character is a friend
//o-----------------------------------------------------------------------------------------------o
bool cCharStuff::checkPetFriend( CChar *mChar, CChar *pet )
{
auto petFriends = pet->GetFriendList();
if( petFriends != nullptr )
{
for (auto &getFriend : *petFriends){
if( ValidateObject( getFriend ) && getFriend == mChar )
return true;
}
}
return false;
}
//o-----------------------------------------------------------------------------------------------o
//| Function - void stopPetGuarding( CChar *pet )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Find the item/char pet is guarding and set it to not guarded
//o-----------------------------------------------------------------------------------------------o
void cCharStuff::stopPetGuarding( CChar *pet )
{
CBaseObject *petGuarding = pet->GetGuarding();
if( !ValidateObject( petGuarding ) )
return;
if( petGuarding->GetObjType() == OT_ITEM )
{
CItem *itemGuard = static_cast<CItem *>(petGuarding);
if( ValidateObject( itemGuard ) )
itemGuard->SetGuarded( false );
}
else
{
CChar *charGuard = static_cast<CChar *>(petGuarding);
if( ValidateObject( charGuard ) )
charGuard->SetGuarded( false );
}
pet->SetGuarding( nullptr );
}
//o-----------------------------------------------------------------------------------------------o
//| Function - void MonsterGate( CChar *s, const std::string& scriptEntry )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Handle monster gates (polymorphs players into monster bodies)
//o-----------------------------------------------------------------------------------------------o
void MonsterGate( CChar *s, const std::string& scriptEntry )
{
CItem *mypack = nullptr;
if( s->IsNpc() )
return;
auto entry = scriptEntry;
entry = oldstrutil::trim( oldstrutil::removeTrailing( entry, "//" ));
ScriptSection *Monster = FileLookup->FindEntry( entry, npc_def );
if( Monster == nullptr )
return;
s->SetTitle( "\0" );
CItem *n;
for( CItem *z = s->FirstItem(); !s->FinishedItems(); z = s->NextItem() )
{
if( ValidateObject( z ) )
{
if( z->isFree() )
continue;
if( z->GetLayer() == IL_HAIR || z->GetLayer() == IL_FACIALHAIR )
z->Delete();
else if( z->GetLayer() != IL_PACKITEM && z->GetLayer() != IL_BANKBOX )
{
if( mypack == nullptr )
mypack = s->GetPackItem();
if( mypack == nullptr )
{
n = Items->CreateItem( nullptr, s, 0x0E75, 1, 0, OT_ITEM );
if( !ValidateObject( n ) )
return;
s->SetPackItem( n );
n->SetDecayable( false );
n->SetLayer( IL_PACKITEM );
if( n->SetCont( s ) )
{
n->SetType( IT_CONTAINER );
n->SetDye( true );
mypack = n;
}
}
z->SetCont( mypack );
z->PlaceInPack();
}
}
}
Npcs->ApplyNpcSection( s, Monster, scriptEntry, true );
//Now find real 'skill' based on 'baseskill' (stat modifiers)
for( UI08 j = 0; j < ALLSKILLS; ++j )
Skills->updateSkillLevel( s, j );
s->Update();
Effects->PlayStaticAnimation( s, 0x373A, 0, 15 );
Effects->PlaySound( s, 0x01E9 );
}
//o-----------------------------------------------------------------------------------------------o
//| Function - void Karma( CChar *nCharID, CChar *nKilledID, const SI16 nKarma )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Handle karma addition/subtraction when character kills
//| another Character / NPC
//o-----------------------------------------------------------------------------------------------o
void Karma( CChar *nCharID, CChar *nKilledID, const SI16 nKarma )
{
SI16 nChange = 0;
bool nEffect = false;
SI16 nCurKarma = nCharID->GetKarma();
if( nCurKarma > 10000 )
{
nCharID->SetKarma( 10000 );
nCurKarma = 10000;
}
else if( nCurKarma < -10000 )
{
nCharID->SetKarma( -10000 );
nCurKarma = -10000;
}
if( nCurKarma < nKarma && nKarma > 0 )
{
nChange = ( ( nKarma - nCurKarma ) / 75 );
nCharID->SetKarma( (SI16)( nCurKarma + nChange ) );
nEffect = true;
}
if( nCurKarma > nKarma && ( !ValidateObject( nKilledID ) || nKilledID->GetKarma() > 0 ) )
{
nChange = ( ( nCurKarma - nKarma ) / 50 );
nCharID->SetKarma( (SI16)( nCurKarma - nChange ) );
nEffect = false;
}
if( nChange == 0 ) // NPCs CAN gain/lose karma
return;
CSocket *mSock = nCharID->GetSocket();
if( nCharID->IsNpc() || mSock == nullptr )
return;
if( nChange <= 25 )
{
if( nEffect )
mSock->sysmessage( 1367 );
else
mSock->sysmessage( 1368 );
return;
}
if( nChange <= 50 )
{
if( nEffect )
mSock->sysmessage( 1369 );
else
mSock->sysmessage( 1370 );
return;
}
if( nEffect )
mSock->sysmessage( 1371 );
else
mSock->sysmessage( 1372 );
}
//o-----------------------------------------------------------------------------------------------o
//| Function - void Fame( CChar *nCharID, const SI16 nFame )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Handle fame addition when character kills another Character / NPC
//o-----------------------------------------------------------------------------------------------o
void Fame( CChar *nCharID, const SI16 nFame )
{
SI16 nChange = 0;
bool nEffect = false;
const SI16 nCurFame = nCharID->GetFame();
if( nCharID->IsDead() )
{
if( nCurFame <= 0 )
nCharID->SetFame( 0 );
else
{
nChange = ( nCurFame - 0 ) / 25;
nCharID->SetFame( (SI16)( nCurFame - nChange ) );
}
nCharID->SetDeaths( (UI16)( nCharID->GetDeaths() + 1 ) );
nEffect = false;
}
else if( nCurFame <= nFame )
{
nChange = ( nFame - nCurFame ) / 75;
nCharID->SetFame( (SI16)( nCurFame + nChange ) );
nEffect = true;
if( nCharID->GetFame() > 10000 )
nCharID->SetFame( 10000 );
}
else
return; // current fame is greater than target fame, and we're not dead
if( nChange == 0 )
return;
CSocket *mSock = nCharID->GetSocket();
if( mSock == nullptr || nCharID->IsNpc() )
return;
if( nChange <= 25 )
{
if( nEffect )
mSock->sysmessage( 1373 );
else
mSock->sysmessage( 1374 );
}
else if( nChange <= 50 )
{
if( nEffect )
mSock->sysmessage( 1375 );
else
mSock->sysmessage( 1376 );
}
else
{
if( nEffect )
mSock->sysmessage( 1377 );
else
mSock->sysmessage( 1378 );
}
}