uox3/source/cPlayerAction.cpp
2022-06-08 10:38:16 -04:00

3344 lines
111 KiB
C++

#include "uox3.h"
#include "weight.h"
#include "combat.h"
#include "cRaces.h"
#include "skills.h"
#include "cMagic.h"
#include "CJSMapping.h"
#include "mapstuff.h"
#include "cScript.h"
#include "cEffects.h"
#include "CPacketSend.h"
#include "classes.h"
#include "regions.h"
#include "books.h"
#include "magic.h"
#include "townregion.h"
#include "gump.h"
#include "cGuild.h"
#include "ssection.h"
#include "cServerDefinitions.h"
#include "cSkillClass.h"
#include "Dictionary.h"
void sendTradeStatus( CItem *cont1, CItem *cont2 );
CItem * startTrade( CSocket *mSock, CChar *i );
bool checkItemRange( CChar *mChar, CItem *i );
void DoHouseTarget( CSocket *mSock, UI08 houseEntry );
void SocketMapChange( CSocket *sock, CChar *charMoving, CItem *gate );
void BuildGumpFromScripts( CSocket *s, UI16 m );
void PlankStuff( CSocket *s, CItem *p );
CBoatObj * GetBoat( CSocket *s );
void ModelBoat( CSocket *s, CBoatObj *i );
//o-----------------------------------------------------------------------------------------------o
//| Function - void Bounce( CSocket *bouncer, CItem *bouncing, UI08 mode )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Bounce items back from where they came
//o-----------------------------------------------------------------------------------------------o
void Bounce( CSocket *bouncer, CItem *bouncing, UI08 mode = 5 )
{
if( bouncer == nullptr || !ValidateObject( bouncing ) )
return;
CPBounce bounce( mode );
PickupLocations from = bouncer->PickupSpot();
SERIAL spot = bouncer->PickupSerial();
switch( from )
{
default:
case PL_NOWHERE: break;
case PL_GROUND:
{
SI16 x = bouncer->PickupX();
SI16 y = bouncer->PickupY();
SI08 z = bouncer->PickupZ();
bouncing->SetLocation( x, y, z );
//If item bounces out of range, bounce it to character's feet instead
if( !objInRange( bouncer, bouncing, DIST_INRANGE ))
{
x = bouncer->CurrcharObj()->GetX();
y = bouncer->CurrcharObj()->GetY();
z = bouncer->CurrcharObj()->GetZ();
bouncing->SetLocation( x, y, z );
}
}
break;
case PL_OWNPACK:
case PL_OTHERPACK:
case PL_PAPERDOLL:
bouncing->SetContSerial( spot );
break;
}
bouncing->SetHeldOnCursor( false );
bouncing->Dirty( UT_UPDATE );
bouncer->Send( &bounce );
bouncer->PickupSpot( PL_NOWHERE );
bouncer->SetCursorItem( nullptr );
}
//o-----------------------------------------------------------------------------------------------o
//| Function - void PickupBounce( CSocket *bouncer )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Bounce items back if pickup is illegal. Doesn't require updating item.
//o-----------------------------------------------------------------------------------------------o
void PickupBounce( CSocket *bouncer, UI08 mode = 0 )
{
if( bouncer == nullptr )
return;
CPBounce bounce( mode );
bouncer->Send( &bounce );
bouncer->PickupSpot( PL_NOWHERE );
bouncer->SetCursorItem( nullptr );
}
//o-----------------------------------------------------------------------------------------------o
//| Function - CItem *doStacking( CSocket *mSock, CChar *mChar, CItem *i, CItem *stack )
//| Date - 8/14/01
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Searches pack for pileable items that match the item being
//| dropped into said pack (only if it's pileable), if found
//| ensures the amount won't go over 65535 (the limit how large
//| an item can stack) then stacks it. If the item is not stackable
//| or it cannot stack the item with a pile and have an amount that
//| is <= 65355 then it creates a new pile.
//|
//| Changes - 09/09/2001 - returns true if item deleted
//| 09/25/2002 - Weight fixes
//o-----------------------------------------------------------------------------------------------o
CItem *doStacking( CSocket *mSock, CChar *mChar, CItem *i, CItem *stack )
{
UI32 newAmt = stack->GetAmount() + i->GetAmount();
if( newAmt > MAX_STACK )
{
i->SetAmount( ( newAmt - MAX_STACK ) );
stack->SetAmount( MAX_STACK );
}
else
{
stack->SetAmount( newAmt );
i->Delete();
if( mSock != nullptr )
Effects->itemSound( mSock, stack, false );
return stack;
}
return i;
}
//o-----------------------------------------------------------------------------------------------o
//| Function - CItem *autoStack( CSocket *mSock, CItem *iToStack, CItem *iPack )
//| Status - Modified to v2
//| v2 - accepts a possible nullptr socket to deal with the JSE
//| v3 - returns a CItem * (stack if stacked, item otherwise)
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Searches through the pack to see if an item can be stacked
//| stacking them automatically
//o-----------------------------------------------------------------------------------------------o
CItem *autoStack( CSocket *mSock, CItem *iToStack, CItem *iPack )
{
CChar *mChar = nullptr;
if( mSock != nullptr )
mChar = mSock->CurrcharObj();
if( !ValidateObject( iToStack ) || !ValidateObject( iPack ) )
return nullptr;
iToStack->SetCont( iPack );
if( iToStack->isPileable() )
{
if( mSock != nullptr && ( mSock->PickupSpot() == PL_OTHERPACK || mSock->PickupSpot() == PL_GROUND ) )
Weight->subtractItemWeight( mChar, iToStack );
const UI16 itID = iToStack->GetID();
const SERIAL itSer = iToStack->GetSerial();
const UI16 itCol = iToStack->GetColour();
const UI32 itMore = iToStack->GetTempVar( CITV_MORE );
const UI32 itMoreX = iToStack->GetTempVar( CITV_MOREX );
const UI32 itMoreY = iToStack->GetTempVar( CITV_MOREY );
const UI32 itMoreZ = iToStack->GetTempVar( CITV_MOREZ );
const UI32 itBuyValue = iToStack->GetBuyValue();
const UI32 itSellValue = iToStack->GetSellValue();
GenericList< CItem * > *ipCont = iPack->GetContainsList();
for( CItem *stack = ipCont->First(); !ipCont->Finished(); stack = ipCont->Next() )
{
if( !ValidateObject( stack ) )
continue;
if( stack->isPileable() && stack->GetAmount() < MAX_STACK &&
stack->GetSerial() != itSer && stack->GetID() == itID && stack->GetColour() == itCol && stack->GetTempVar( CITV_MORE ) == itMore && stack->GetTempVar( CITV_MOREX ) == itMoreX
&& stack->GetTempVar( CITV_MOREY ) == itMoreY && stack->GetTempVar( CITV_MOREZ ) == itMoreZ && stack->GetBuyValue() == itBuyValue && stack->GetSellValue() == itSellValue )
{ // Autostack
if( doStacking( mSock, mChar, iToStack, stack ) == stack ) // compare to stack, if doStacking returned the stack, then the raw object was deleted
return stack; // return the stack
}
}
if( mSock != nullptr && ( mSock->PickupSpot() == PL_OTHERPACK || mSock->PickupSpot() == PL_GROUND ) )
Weight->addItemWeight( mChar, iToStack );
}
iToStack->PlaceInPack();
if( mSock != nullptr )
Effects->itemSound( mSock, iToStack, false );
return iToStack;
}
auto findNearbyChars( SI16 x, SI16 y, UI08 worldNumber, UI16 instanceID, UI16 distance ) ->std::vector< CChar* >;
//o-----------------------------------------------------------------------------------------------o
//| Function - bool CPIGetItem::Handle( void )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Called when a player picks up an item
//o-----------------------------------------------------------------------------------------------o
bool CPIGetItem::Handle( void )
{
CChar *ourChar = tSock->CurrcharObj();
SERIAL serial = tSock->GetDWord( 1 );
if( serial == INVALIDSERIAL )
return true;
// Break their meditation/concentration
ourChar->BreakConcentration( tSock );
// In case player is casting a spell, cancel their targeting cursor (if enabled in INI)
if(( ourChar->IsCasting() || ourChar->GetSpellCast() != -1 ) && cwmWorldState->ServerData()->ItemsInterruptCasting() )
{
tSock->target( 0, 0x00000000, Dictionary->GetEntry( 2862, tSock->Language() ), 3 ); // Your hands must be free to cast spells or meditate.
ourChar->StopSpell();
if( ourChar->IsFrozen() )
ourChar->SetFrozen( false );
}
CItem *i = calcItemObjFromSer( serial );
if( !ValidateObject( i ) )
return true;
CPBounce bounce( 0 );
ObjectType oType = OT_CBO;
CBaseObject *iOwner = nullptr;
CItem *x = i;
CBaseObject *iCont = i->GetCont();
if( iCont != nullptr ) //Find character owning item
{
iOwner = FindItemOwner( i, oType );
tSock->PickupSerial( i->GetContSerial() );
if( oType == OT_CHAR )
{
if( iCont->GetObjType() == OT_CHAR )
tSock->PickupSpot( PL_PAPERDOLL );
else
{
if( iOwner != ourChar )
tSock->PickupSpot( PL_OTHERPACK );
else
tSock->PickupSpot( PL_OWNPACK );
}
bool otherPackCheck = (iOwner != ourChar);
if( otherPackCheck ) // okay, not our owner!
{
otherPackCheck = !ourChar->IsGM();
if( otherPackCheck ) // not a GM either
{
if( iOwner->GetOwnerObj() == ourChar )
otherPackCheck = false;
else if( Npcs->checkPetFriend( ourChar, static_cast< CChar * >(iOwner) ) )
otherPackCheck = false;
}
}
if( otherPackCheck || !objInRange( ourChar, iOwner, DIST_NEARBY ) )
{
tSock->sysmessage( 9065 ); // That item does not belong to you. You'll have to steal it.
Bounce( tSock, i );
return true;
}
if( iCont->CanBeObjType( OT_ITEM ) )
{
CItem *contItem = static_cast<CItem *>(iCont);
if( contItem->GetType() == IT_TRADEWINDOW ) // Trade Window
{
tSock->sysmessage( 9064 ); // You cannot pick that up.
Bounce( tSock, i );
return true;
}
CItem *recurseItem = FindRootContainer( contItem );
if( recurseItem->GetType() == IT_TRADEWINDOW )
{
CItem *z = calcItemObjFromSer( recurseItem->GetTempVar( CITV_MOREX ) );
if( ValidateObject( z ) )
{
if( z->GetTempVar( CITV_MOREZ ) || recurseItem->GetTempVar( CITV_MOREZ ) )
{
z->SetTempVar( CITV_MOREZ, 0 );
recurseItem->SetTempVar( CITV_MOREZ, 0 );
sendTradeStatus( z, recurseItem );
}
CChar *zChar = FindItemOwner( z );
if( ValidateObject( zChar ) )
{
CSocket *zSock = zChar->GetSocket();
if( zSock != nullptr )
Effects->PlaySound( zSock, 0x0057, true );
}
}
}
else if( recurseItem->GetLayer() == IL_BANKBOX )
tSock->PickupSpot( PL_OTHERPACK );
}
}
else if( oType == OT_ITEM )
{
// Picking up item from non-character container
tSock->PickupSpot( PL_OTHERPACK );
CItem *x = static_cast<CItem *>(iOwner);
if( !objInRange( ourChar, x, DIST_NEARBY ) )
{
Bounce( tSock, i );
tSock->sysmessage( 393 ); // That is too far away.
// Resend items in container after bounce, or the bounced item will vanish from player's screen
CPItemsInContainer itemsIn( tSock, static_cast<CItem *>(iCont), 0x01 );
tSock->Send( &itemsIn );
return true;
}
if( x->isCorpse() )
{
CChar *corpseTargChar = x->GetOwnerObj();
if( ValidateObject( corpseTargChar ) )
{
if( corpseTargChar->IsGuarded() ) // Is the corpse being guarded?
Combat->petGuardAttack( ourChar, corpseTargChar, corpseTargChar );
else if( x->isGuarded() )
{
CTownRegion *itemTownRegion = x->GetRegion();
if( !itemTownRegion->IsGuarded() && !itemTownRegion->IsSafeZone() )
{
Combat->petGuardAttack( ourChar, corpseTargChar, x );
}
}
}
}
}
}
else
{
// Picking up item from ground - check that character is in range and can see the item!
tSock->PickupSpot( PL_GROUND );
tSock->PickupLocation( i->GetX(), i->GetY(), i->GetZ() );
if( !ourChar->IsGM() && ( !objInRange( ourChar, i, DIST_NEARBY ) || !LineOfSight( tSock, ourChar, i->GetX(), i->GetY(), i->GetZ(), WALLS_CHIMNEYS + DOORS, true )))
{
tSock->sysmessage( 683 ); // There seems to be something in the way
Bounce( tSock, i );
return true;
}
}
if( i->isCorpse() || !checkItemRange( ourChar, x ) )
{
tSock->sysmessage( 9064 ); // You cannot pick that up.
Bounce( tSock, i );
return true;
}
if( x->GetMultiObj() != nullptr )
{
if( ( tSock->PickupSpot() == PL_OTHERPACK || tSock->PickupSpot() == PL_GROUND ) && ( x->GetMultiObj() != ourChar->GetMultiObj() || x->IsLockedDown() ))
{
tSock->sysmessage( 9064 ); // You cannot pick that up.
Bounce( tSock, i );
return true;
}
i->SetMulti( INVALIDSERIAL );
}
if( i->isDecayable() )
i->SetDecayTime( cwmWorldState->ServerData()->BuildSystemTimeValue( tSERVER_DECAY ) );
if( i->isGuarded() )
{
// Only care about guarded state if outside of a guarded/safezone region
CTownRegion *itemTownRegion = x->GetRegion();
if( !itemTownRegion->IsGuarded() && !itemTownRegion->IsSafeZone() )
{
// Let's loop through a list of nearby characters to see if anyone is guarding the object
for (auto &nearbyChar : findNearbyChars(i->GetX(), i->GetY(), i->WorldNumber(), i->GetInstanceID(), DIST_INRANGE)) {
// Only proceed if the character is guarding the object
if( nearbyChar->GetGuarding() == i )
{
CChar *nearbyCharOwner = nearbyChar->GetOwnerObj();
if( ValidateObject( nearbyCharOwner ) )
{
// Turn the player who picked up the guarded item criminal
criminal( ourChar );
// Have pet/follower attack the player who picked up the item
Combat->petGuardAttack( ourChar, nearbyCharOwner, i, nearbyChar );
// Stop guarding item, it is now in someone else's possession
nearbyChar->SetGuarding( nullptr );
i->SetGuarded( false );
}
}
}
}
}
CTile& tile = Map->SeekTile( i->GetID() );
if( !ourChar->AllMove() && ( i->GetMovable() == 2 || i->IsLockedDown() ||
( tile.Weight() == 255 && i->GetMovable() != 1 ) ) )
{
if( ourChar->GetCommandLevel() < 2 || tSock->PickupSpot() != PL_OWNPACK )
{
tSock->sysmessage( 9064 ); // You cannot pick that up.
if( ourChar->GetCommandLevel() >= 2 )
tSock->sysmessage( 9099 ); // Tip: Try 'ALLMOVE ON command or modify item's movable property with 'TWEAK command!
Bounce( tSock, i );
return true;
}
else
{
tSock->sysmessage( 9098 ); // Item immovable to normal players, but can be dragged out of backpack by GM characters.
}
}
std::vector<UI16> scriptTriggers = i->GetScriptTriggers();
for( auto scriptTrig : scriptTriggers )
{
cScript *toExecute = JSMapping->GetScript( scriptTrig );
if( toExecute != nullptr )
{
SI08 retStatus = toExecute->OnPickup( i, ourChar );
// -1 == script doesn't exist, or returned -1
// 0 == script returned false, 0, or nothing - don't execute hard code
// 1 == script returned true or 1
if( retStatus == 0 )
{
Bounce( tSock, i );
return true;
}
}
}
if( i->GetAmount() > 1 && i->GetObjType() != OT_SPAWNER )
{
UI16 amount = tSock->GetWord( 5 );
if( amount > i->GetAmount() )
amount = i->GetAmount();
if( tSock->PickupSpot() == PL_OTHERPACK || tSock->PickupSpot() == PL_GROUND )
{
if( !Weight->checkCharWeight( nullptr, ourChar, i, amount ))
{
tSock->sysmessage( 1743 ); // That person can not possibly hold more weight
Bounce( tSock, i );
return true;
}
}
if( amount < i->GetAmount() )
{
CItem *c = i->Dupe();
if( c != nullptr )
{
c->IncAmount( -amount );
c->SetCont( i->GetCont() );
c->SetLocation( c->GetX(), c->GetY(), c->GetZ(), 0, c->WorldNumber(), c->GetInstanceID() );
}
i->SetAmount( amount );
}
if( i->GetID() == 0x0EED )
{
if( tSock->PickupSpot() == PL_OWNPACK )
ourChar->Dirty( UT_STATWINDOW );
}
}
Effects->PlaySound( tSock, 0x0057, true );
if( iCont == nullptr )
MapRegion->RemoveItem( i );
i->RemoveFromSight();
tSock->SetCursorItem( i );
i->SetHeldOnCursor( true );
// Update region of item to match that of the character holding it
i->SetRegion( ourChar->GetRegionNum() );
if( tSock->PickupSpot() == PL_OTHERPACK || tSock->PickupSpot() == PL_GROUND )
Weight->addItemWeight( ourChar, i );
if( ValidateObject( iCont ) && iCont->CanBeObjType( OT_ITEM ))
{
std::vector<UI16> contScriptTriggers = iCont->GetScriptTriggers();
for( auto scriptTrig : contScriptTriggers )
{
cScript *toExecute = JSMapping->GetScript( scriptTrig );
if( toExecute != nullptr )
{
CItem *contItem = static_cast<CItem *>(iCont);
toExecute->OnContRemoveItem( contItem, i, ourChar );
}
}
}
return true;
}
//o-----------------------------------------------------------------------------------------------o
//| Function - bool CPIEquipItem::Handle( void )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Called when an item is dropped on a players paperdoll
//o-----------------------------------------------------------------------------------------------o
bool CPIEquipItem::Handle( void )
{
CChar *ourChar = tSock->CurrcharObj();
SERIAL cserial = tSock->GetDWord( 6 );
SERIAL iserial = tSock->GetDWord( 1 );
if( cserial == INVALIDSERIAL || iserial == INVALIDSERIAL )
return true;
ourChar->BreakConcentration( tSock );
CItem *i = calcItemObjFromSer( iserial );
CChar *k = calcCharObjFromSer( cserial );
if( !ValidateObject( i ) )
return true;
if( tSock->PickupSpot() == PL_OTHERPACK || tSock->PickupSpot() == PL_PAPERDOLL )
{
ObjectType pOType;
CBaseObject *pOwner = FindItemOwner( i, pOType );
if( pOType == OT_CHAR )
{
CChar *pOChar = static_cast< CChar * >( pOwner );
if( ( ( pOChar != ourChar ) && ( ( !ourChar->IsGM() && ( pOChar->GetOwnerObj() != ourChar ) ) ) ) || !objInRange( ourChar, pOwner, DIST_NEARBY ) )
{
tSock->sysmessage( 9066 ); // That belongs to someone else.
Bounce( tSock, i );
return true;
}
}
else if( pOType == OT_ITEM )
{
if( !objInRange( ourChar, pOwner, DIST_NEARBY ) )
{
tSock->sysmessage( 393 ); // That is too far away
Bounce( tSock, i );
return true;
}
}
}
else if( tSock->PickupSpot() == PL_GROUND )
{
if( !objInRange( ourChar, i, DIST_NEARBY ) )
{
tSock->sysmessage( 393 ); // That is too far away
Bounce( tSock, i );
return true;
}
}
if( tSock->PickupSpot() == PL_OTHERPACK || tSock->PickupSpot() == PL_GROUND )
Weight->subtractItemWeight( ourChar, i ); // SetCont() adds the weight for us (But we also had to add the weight in grabItem() since it sets cont as INVALIDSERIAL
if( !ourChar->IsGM() && k != ourChar ) // players cant equip items on other players or npc`s paperdolls. // GM PRIVS
{
Bounce( tSock, i );
tSock->sysmessage( 1186 ); // You can't put items on other players!
return true;
}
if( ourChar->IsDead() )
{
Bounce( tSock, i );
tSock->sysmessage( 1185 ); // You can't do much in your current state.
return true;
}
if( !ValidateObject( k ) )
return true;
RACEID raceID = k->GetRace();
CRace *race = Races->Race( raceID );
if( !race->CanEquipItem( i->GetID() ))
{
tSock->sysmessage( 1981, race->Name().c_str() ); // Members of the %s race cannot wear this
Bounce( tSock, i );
return true;
}
ARMORCLASS ac1 = Races->ArmorRestrict( raceID );
ARMORCLASS ac2 = i->GetArmourClass();
if( ac1 != 0 && ( (ac1&ac2) == 0 ) ) // bit comparison, if they have ANYTHING in common, they can wear it
{
tSock->sysmessage( 1187 ); // You are unable to equip that due to your race.
Bounce( tSock, i );
return true;
}
if( k == ourChar )
{
bool canWear = false;
if( i->GetStrength() > k->GetStrength() )
tSock->sysmessage( 1188 ); // You are not strong enough to use that. (NOTE: Should these messages use color 0x096a to stand out and replicate hard coded client message?)
else if( i->GetDexterity() > k->GetDexterity() )
tSock->sysmessage( 1189 ); // You are not agile enough to use that.
else if( i->GetIntelligence() > k->GetIntelligence() )
tSock->sysmessage( 1190 ); // You are not smart enough to use that.
else
canWear = true;
if( !canWear )
{
Bounce( tSock, i );
if( tSock->PickupSpot() == PL_OTHERPACK || tSock->PickupSpot() == PL_GROUND )
Effects->itemSound( tSock, i, true );
else
Effects->itemSound( tSock, i, false );
return true;
}
}
CTile& tile = Map->SeekTile( i->GetID() );
if( !ourChar->AllMove() && ( i->GetMovable() == 2 || ( i->IsLockedDown() && i->GetOwnerObj() != ourChar ) ||
( tile.Weight() == 255 && i->GetMovable() != 1 ) ) )
{
tSock->sysmessage( 9064 ); // You cannot pick that up.
Bounce( tSock, i );
return true;
}
if( i->GetLayer() == IL_NONE )
i->SetLayer( static_cast<ItemLayers>(tSock->GetByte( 5 )) );
// 1/13/2003 - Fix for equipping an item to more than one hand, or multiple equipping.
if( i->GetCont() != k )
{
bool conflictItem = true;
CItem *j = k->GetItemAtLayer( i->GetLayer() );
if( !ValidateObject( j ))
{
if( i->GetLayer() == IL_RIGHTHAND )
j = k->GetItemAtLayer( IL_LEFTHAND );
else if( i->GetLayer() == IL_LEFTHAND )
j = k->GetItemAtLayer( IL_RIGHTHAND );
// GetDir-check is to allow for torches and lanterns,
// which use left-hand layer but are not 2-handers or shields
if( ValidateObject( j ) && !i->IsShieldType() && i->GetDir() == 0 )
{
if( j->IsShieldType() || j->GetDir() != 0 )
conflictItem = false;
}
else
conflictItem = false;
}
if( conflictItem == true )
{
tSock->sysmessage( 1744 ); // You can't equip two items in the same slot
Bounce( tSock, i );
return true;
}
}
if( !Weight->checkCharWeight( ourChar, k, i ) )
{
tSock->sysmessage( 1743 ); // That person can not possibly hold more weight
Bounce( tSock, i );
return true;
}
i->SetCont( k );
i->SetHeldOnCursor( false );
//Reset PickupSpot after dropping the item
tSock->PickupSpot( PL_NOWHERE );
tSock->SetCursorItem( nullptr );
// In case player is casting a spell, cancel their targeting cursor (if enabled in INI)
if(( ourChar->IsCasting() || ourChar->GetSpellCast() != -1 ) && cwmWorldState->ServerData()->ItemsInterruptCasting() )
{
tSock->target( 0, 0x00000000, Dictionary->GetEntry( 2862, tSock->Language() ), 3 ); // Your hands must be free to cast spells or meditate.
ourChar->StopSpell();
if( ourChar->IsFrozen() )
ourChar->SetFrozen( false );
}
CPDropItemApproved lc;
tSock->Send( &lc );
auto weaponType = Combat->getCombatSkill( i );
if( weaponType == SWORDSMANSHIP || weaponType == MACEFIGHTING || weaponType == FENCING )
Effects->PlaySound( tSock, 0x0056, false ); // Play unsheath sound if
else
Effects->PlaySound( tSock, 0x0057, false );
return true;
}
//o-----------------------------------------------------------------------------------------------o
//| Function - bool DropOnPC( CSocket *mSock, CChar *mChar, CChar *targPlayer, CItem *i )
//| Changes - September 14th, 2001, returns true if item deleted
//| - September 25, 2002, Weight fixes
//| - September 21st, 2003, moved into seperate file and few other minor tweaks
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Called when an item is dropped on a character
//o-----------------------------------------------------------------------------------------------o
bool DropOnPC( CSocket *mSock, CChar *mChar, CChar *targPlayer, CItem *i )
{
bool stackDeleted = false;
if( targPlayer == mChar )
{
CItem *pack = mChar->GetPackItem();
if( !ValidateObject( pack ) ) // if player has no pack, put it at its feet
{
i->SetCont( nullptr );
i->SetLocation( mChar );
}
else
stackDeleted = ( autoStack( mSock, i, pack ) != i );
}
else // Trade stuff
{
if( isOnline( (*targPlayer) ) )
{
CItem *j = startTrade( mSock, targPlayer );
if( ValidateObject( j ) )
{
i->SetCont( j );
i->SetX( 30 );
i->SetY( 30 );
i->SetZ( 9 );
}
}
else
{
if( mSock->PickupSpot() == PL_OTHERPACK || mSock->PickupSpot() == PL_GROUND )
Weight->subtractItemWeight( mChar, i );
mSock->sysmessage( 1754 ); // That character is not online.
Bounce( mSock, i );
return true;
}
}
return stackDeleted;
}
//o-----------------------------------------------------------------------------------------------o
//| Function - bool IsOnFoodList( const std::string& sFoodList, const UI16 sItemID )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Check if a particular item is on an NPC's list of accepted foods
//o-----------------------------------------------------------------------------------------------o
bool IsOnFoodList( const std::string& sFoodList, const UI16 sItemID )
{
bool doesEat = false;
const std::string sect = "FOODLIST " + sFoodList;
ScriptSection *FoodList = FileLookup->FindEntry( sect, items_def );
if( FoodList != nullptr )
{
for( std::string tag = FoodList->First(); !FoodList->AtEnd() && !doesEat; tag = FoodList->Next() )
{
if( !tag.empty() )
{
if( oldstrutil::upper( tag ) == "FOODLIST" )
{
doesEat = IsOnFoodList( FoodList->GrabData(), sItemID );
}
else if( sItemID == static_cast<UI16>(std::stoul(tag, nullptr, 0)) )
{
doesEat = true;
}
}
}
}
return doesEat;
}
//o-----------------------------------------------------------------------------------------------o
//| Function - bool DropOnNPC( CSocket *mSock, CChar *mChar, CChar *targNPC, CItem *i )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Called when an item is dropped on an NPC
//o-----------------------------------------------------------------------------------------------o
bool DropOnNPC( CSocket *mSock, CChar *mChar, CChar *targNPC, CItem *i )
{
UI08 dropResult = 0;
const bool isGM = (mChar->GetCommandLevel() >= CL_CNS);
bool stackDeleted = false;
bool executeNpc = true;
SI08 rVal = 0;
std::vector<UI16> scriptTriggers = i->GetScriptTriggers();
for( auto scriptTrig : scriptTriggers )
{
cScript *toExecute = JSMapping->GetScript( scriptTrig );
if( toExecute != nullptr )
{
rVal = toExecute->OnDropItemOnNpc( mChar, targNPC, i ); // returns 1 if we should bounce it
switch( rVal )
{
case -1: // no such event
default:
executeNpc = true;
break;
case 0: // bounce, no code, error message handled in script
if( mSock->PickupSpot() == PL_OTHERPACK || mSock->PickupSpot() == PL_GROUND )
Weight->subtractItemWeight( mChar, i );
Bounce( mSock, i );
return false; // stack not deleted, as we're bouncing
case 1: // don't bounce, use code
executeNpc = false;
break;
case 2: // don't bounce, don't use code
executeNpc = true;
break;
}
}
}
if( executeNpc )
{
std::vector<UI16> targScriptTriggers = targNPC->GetScriptTriggers();
for( auto scriptTrig : targScriptTriggers )
{
cScript *toExecute = JSMapping->GetScript( scriptTrig );
if( toExecute != nullptr )
{
rVal = toExecute->OnDropItemOnNpc( mChar, targNPC, i ); // returns 1 if we should bounce it
switch( rVal )
{
case -1: // no such event
case 1: // we don't want to bounce, use code
default:
break;
case 0: // we want to bounce and return. Handle error message in script
// If we want to bounce, then it's safe to assume the item still exists!
if( mSock->PickupSpot() == PL_OTHERPACK || mSock->PickupSpot() == PL_GROUND )
Weight->subtractItemWeight( mChar, i );
Bounce( mSock, i );
return false; // bouncing, so item exists
case 2: // we don't want to bounce, don't use code
return true; // don't let the code assume the item exists, so we never reference it
}
}
}
}
if( targNPC->IsTamed() && ( isGM || targNPC->GetOwnerObj() == mChar || Npcs->checkPetFriend( mChar, targNPC ) ) ) // do food stuff
{
if( targNPC->WillHunger() && IsOnFoodList( targNPC->GetFood(), i->GetID() ) )
{
if( targNPC->GetHunger() < 6 )
{
Effects->PlaySound( mSock, static_cast<UI16>(0x003A + RandomNum( 0, 2 )), true );
if( cwmWorldState->creatures[targNPC->GetID()].IsAnimal() )
{
Effects->PlayCharacterAnimation( targNPC, 3, 0, 5 );
// Restore loyalty to max upon feeding pet
if( targNPC->GetLoyalty() < targNPC->GetMaxLoyalty() )
{
targNPC->SetLoyalty( targNPC->GetMaxLoyalty() );
mSock->sysmessage( 2416 ); // Your pet looks happier.
}
}
UI08 poisonStrength = i->GetPoisoned();
if( poisonStrength && targNPC->GetPoisoned() < poisonStrength )
{
Effects->PlaySound( mSock, 0x0246, true ); // poison sound - SpaceDog
// Apply poison on target
targNPC->SetPoisoned( poisonStrength );
// Set time until next time poison "ticks"
targNPC->SetTimer( tCHAR_POISONTIME, BuildTimeValue(static_cast<R32>(getPoisonTickTime( poisonStrength ))));
// Set time until poison wears off completely
targNPC->SetTimer( tCHAR_POISONWEAROFF, targNPC->GetTimer( tCHAR_POISONTIME ) + ( 1000 * getPoisonDuration( poisonStrength ))); //wear off starts after poison takes effect
// Cut loyalty in half if pet was fed poisoned food
if( targNPC->GetLoyalty() > 0 )
{
targNPC->SetLoyalty( std::clamp( static_cast<UI16>(targNPC->GetLoyalty() / 2), static_cast<UI16>(0), static_cast<UI16>(100)) );
}
}
//Remove a food item
bool iDeleted = i->IncAmount( -1 );
targNPC->SetHunger( static_cast<SI08>(targNPC->GetHunger() + 1) );
mSock->sysmessage( 1781 ); // That pet accepts your food and happily eats it.
if( iDeleted )
return true; //stackdeleted
}
else
mSock->sysmessage( 1780 );
}
else if( isGM || targNPC->GetID() == 0x0123 || targNPC->GetID() == 0x0124 || targNPC->GetID() == 0x0317 ) // It's a pack animal
dropResult = 2;
}
else if( cwmWorldState->creatures[targNPC->GetID()].IsHuman() )
{
if( static_cast<CChar *>(mSock->TempObj()) != targNPC )
{
if( isGM )
dropResult = 2;
else
{
dropResult = 1;
targNPC->TextMessage( mSock, 1197, TALK, false );
}
}
else if( i->GetID() == 0x0EED ) // They gave the NPC gold
{
UI08 trainedIn = targNPC->GetTrainingPlayerIn();
targNPC->TextMessage( mSock, 1198, TALK, false );
UI16 oldskill = mChar->GetBaseSkill( trainedIn );
mChar->SetBaseSkill( (UI16)( mChar->GetBaseSkill( trainedIn ) + i->GetAmount() ), trainedIn );
if( mChar->GetBaseSkill( trainedIn ) > 250 )
mChar->SetBaseSkill( 250, trainedIn );
Skills->updateSkillLevel( mChar, trainedIn );
mSock->updateskill( trainedIn );
UI16 getAmount = i->GetAmount();
if( i->GetAmount() > 250 ) // Paid too much
{
i->IncAmount( -(250 - oldskill) );
dropResult = 1;
}
else // Gave exact change
{
if( mSock->PickupSpot() == PL_OTHERPACK || mSock->PickupSpot() == PL_GROUND )
Weight->subtractItemWeight( mChar, i );
i->Delete();
stackDeleted = true;
}
mSock->TempObj( nullptr );
targNPC->SetTrainingPlayerIn( 0xFF );
Effects->goldSound( mSock, getAmount, false );
}
else // Did not give gold
{
dropResult = 1;
targNPC->TextMessage( mSock, 1199, TALK, false );
}
}
else
dropResult = ( isGM ? 2 : 1 );
switch( dropResult )
{
default:
case 0: // Do nothing;
break;
case 1: // Bounce
if( mSock->PickupSpot() == PL_OTHERPACK || mSock->PickupSpot() == PL_GROUND )
Weight->subtractItemWeight( mChar, i );
Bounce( mSock, i );
break;
case 2: // Stack
CItem *pack;
pack = targNPC->GetPackItem();
if( ValidateObject( pack ) )
stackDeleted = ( autoStack( mSock, i, pack ) != i );
break;
}
return stackDeleted;
}
//o-----------------------------------------------------------------------------------------------o
//| Function - bool DropOnChar( CSocket *mSock, CChar *targChar, CItem *i )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Called when an item is dropped on an a character
//o-----------------------------------------------------------------------------------------------o
bool DropOnChar( CSocket *mSock, CChar *targChar, CItem *i )
{
CChar *mChar = mSock->CurrcharObj();
if( !ValidateObject( mChar ) )
return false;
CItem *packItem = targChar->GetPackItem();
if( ValidateObject( packItem ) )
{
// Can character's backpack hold any more items?
if( mSock->PickupSpot() != PL_OWNPACK && ( GetTotalItemCount( packItem ) >= packItem->GetMaxItems() ||
GetTotalItemCount( packItem ) + std::max( static_cast<UI32>(1), 1 + GetTotalItemCount( i )) > packItem->GetMaxItems() ))
{
mSock->sysmessage( 1818 ); // The container is already at capacity.
if( mSock->PickupSpot() == PL_OTHERPACK || mSock->PickupSpot() == PL_GROUND )
Weight->subtractItemWeight( mChar, i );
Bounce( mSock, i );
return false;
}
// Can character's backpack hold any more weight?
if( !Weight->checkCharWeight( mChar, targChar, i ) )
{
mSock->sysmessage( 1743 );
if( mSock->PickupSpot() == PL_OTHERPACK || mSock->PickupSpot() == PL_GROUND )
Weight->subtractItemWeight( mChar, i );
Bounce( mSock, i );
return false;
}
if( !targChar->IsNpc() )
return DropOnPC( mSock, mChar, targChar, i );
return DropOnNPC( mSock, mChar, targChar, i );
}
return false;
}
//o-----------------------------------------------------------------------------------------------o
//| Function - bool CheckForValidDropLocation( CSocket *mSock, CChar *nChar, UI16 x, UI16 y, SI08 z )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Check for a valid drop location at the location where client attempts to drop an item
//o-----------------------------------------------------------------------------------------------o
bool CheckForValidDropLocation( CSocket *mSock, CChar *nChar, UI16 x, UI16 y, SI08 z )
{
bool dropLocationBlocked = false;
// Don't allow dropping item at a location far below or far above character
if( z < nChar->GetZ() - 5 || z > nChar->GetZ() + 16 )
dropLocationBlocked = true;
if( !dropLocationBlocked )
{
// Check static items for TF_BLOCKING flag, or if map tile blocks dropping item
dropLocationBlocked = ( Map->CheckStaticFlag( x, y, z, nChar->WorldNumber(), TF_BLOCKING, false )
|| Map->DoesMapBlock( x, y, z, nChar->WorldNumber(), true, false, false, false ));
if( !dropLocationBlocked )
{
// No? Well, then check static flags for TF_ROOF flag
dropLocationBlocked = Map->CheckStaticFlag( x, y, z, nChar->WorldNumber(), TF_ROOF, false );
if( !dropLocationBlocked )
{
// Still no? Time to check dynamic items for TF_BLOCKING flag...
dropLocationBlocked = ( Map->CheckDynamicFlag( x, y, z, nChar->WorldNumber(), nChar->GetInstanceID(), TF_BLOCKING ));
if( !dropLocationBlocked )
{
// ...and then for TF_ROOF flag
dropLocationBlocked = Map->CheckDynamicFlag( x, y, z, nChar->WorldNumber(), nChar->GetInstanceID(), TF_ROOF );
}
else
{
// Location blocked! But wait, there might be a valid dynamic surface to place the item on...
dropLocationBlocked = !Map->CheckDynamicFlag( x, y, z, nChar->WorldNumber(), nChar->GetInstanceID(), TF_SURFACE );
}
}
}
else
{
// Location blocked! But wait, there might be a valid static surface to place the item on...
dropLocationBlocked = !Map->CheckStaticFlag( x, y, z, nChar->WorldNumber(), TF_SURFACE, false );
}
}
// Let's check line of sight as well, for good measure
if( !nChar->IsGM() && !dropLocationBlocked && !LineOfSight( mSock, nChar, x, y, z, WALLS_CHIMNEYS + DOORS + ROOFING_SLANTED, true ) )
{
dropLocationBlocked = true;
}
return !dropLocationBlocked;
}
//o-----------------------------------------------------------------------------------------------o
//| Function - void Drop( CSocket *mSock, SERIAL item, SERIAL dest, SI16 x, SI16 y, SI08 z, SI08 gridLoc )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Item is dropped on the ground or on a character
//o-----------------------------------------------------------------------------------------------o
void Drop( CSocket *mSock, SERIAL item, SERIAL dest, SI16 x, SI16 y, SI08 z, SI08 gridLoc )
{
CChar *nChar = mSock->CurrcharObj();
CItem *i = calcItemObjFromSer( item );
bool stackDeleted = false;
if( !ValidateObject( i ) )
{
nChar->Teleport();
return;
}
//Bounce if no valid pickupspot exists
if( mSock->PickupSpot() == PL_NOWHERE )
{
mSock->sysmessage( 9067 ); // Unable to drop item... where was it picked up from?
Bounce( mSock, i );
return;
}
std::vector<UI16> scriptTriggers = i->GetScriptTriggers();
for( auto scriptTrig : scriptTriggers )
{
cScript *toExecute = JSMapping->GetScript( scriptTrig );
if( toExecute != nullptr )
{
SI08 rVal = toExecute->OnDrop( i, nChar ); // returns 1 if we should bounce it
switch( rVal )
{
case 1: // don't bounce, use code
case -1: // no such event
default:
break;
case 0: // bounce, no code, handle error message in client
Bounce( mSock, i );
return; // stack not deleted, as we're bouncing
case 2: // don't bounce, don't use code
return;
}
}
}
CTile& tile = Map->SeekTile( i->GetID() );
if( !nChar->AllMove() && ( i->GetMovable() == 2 || ( i->IsLockedDown() && i->GetOwnerObj() != nChar ) ||
( tile.Weight() == 255 && i->GetMovable() != 1 ) ) )
{
if( nChar->GetCommandLevel() < 2 || mSock->PickupSpot() != PL_OWNPACK )
{
if( mSock->PickupSpot() == PL_OTHERPACK || mSock->PickupSpot() == PL_GROUND )
Weight->subtractItemWeight( nChar, i );
mSock->sysmessage( 9064 ); // You cannot pick that up.
Bounce( mSock, i );
return;
}
}
if( mSock->GetByte( 5 ) != 0xFF ) // Dropped in a specific location or on an item
{
// Let's check for a valid drop location for the item, and bounce it if none was found
if( !CheckForValidDropLocation( mSock, nChar, x, y, z ))
{
if( mSock->PickupSpot() == PL_OTHERPACK || mSock->PickupSpot() == PL_GROUND )
Weight->subtractItemWeight( nChar, i );
Bounce( mSock, i );
mSock->sysmessage( 683 ); // There seems to be something in the way
return;
}
// New location either is not blocking, or has a surface we can put the item on, so let's find the exact Z of where to put it
// auto nCharZ = nChar->GetZ();
auto newZ = Map->StaticTop( x, y, z, nChar->WorldNumber(), 16 );
if( newZ == ILLEGAL_Z || newZ < z || newZ > z + 16 )
{
// No valid static elevation found, use dynamic elevation instead
newZ = Map->DynamicElevation( x, y, z, nChar->WorldNumber(), 16, nChar->GetInstanceID() );
if( newZ < z || newZ > z + 16 )
{
// No valid dynamic elevation found. Use map elevation instead (don't implicitly trust Z from client)
newZ = Map->MapElevation( x, y, nChar->WorldNumber() );
}
}
else
{
auto dynZ = Map->DynamicElevation( x, y, z, nChar->WorldNumber(), 16, nChar->GetInstanceID() );
newZ = (( dynZ >= z && dynZ <= z + 16 ) ? dynZ : newZ );
}
i->SetCont( nullptr );
i->SetLocation( x, y, newZ, gridLoc, nChar->WorldNumber(), nChar->GetInstanceID() );
// If item was picked up from a container and dropped on ground, update old location to match new!
if( mSock->PickupSpot() != PL_GROUND )
i->SetOldLocation( x, y, z );
}
else
{
// Dropped on a character
CChar *t = calcCharObjFromSer( dest );
if( ValidateObject( t ) )
stackDeleted = DropOnChar( mSock, t, i );
else
{
//Bounces items dropped in illegal locations in 3D UO client!!!
if( mSock->PickupSpot() == PL_OTHERPACK || mSock->PickupSpot() == PL_GROUND )
Weight->subtractItemWeight( nChar, i );
mSock->sysmessage( 683 ); // There seems to be something in the way
Bounce( mSock, i );
return;
}
}
if( !stackDeleted )
{
if( mSock->PickupSpot() == PL_OTHERPACK || mSock->PickupSpot() == PL_GROUND )
Weight->subtractItemWeight( nChar, i );
if( nChar->GetMultiObj() != nullptr )
{
CMultiObj *multi = findMulti( i );
if( ValidateObject( multi ) )
{
if( i->isDecayable() )
i->SetDecayTime( cwmWorldState->ServerData()->BuildSystemTimeValue( tSERVER_DECAYINHOUSE ) );
i->SetMulti( multi );
}
else if( i->isDecayable() )
i->SetDecayTime( cwmWorldState->ServerData()->BuildSystemTimeValue( tSERVER_DECAY ) );
}
else if( i->isDecayable() )
i->SetDecayTime( cwmWorldState->ServerData()->BuildSystemTimeValue( tSERVER_DECAY ) );
i->SetHeldOnCursor( false );
Effects->itemSound( mSock, i, ( mSock->PickupSpot() == PL_OTHERPACK || mSock->PickupSpot() == PL_GROUND ) );
}
// IF client-version is above 6.0.1.7, send approval packet for dropping item. If not, don't.
CPDropItemApproved lc;
mSock->Send( &lc );
}
//o-----------------------------------------------------------------------------------------------o
//| Function - void DropOnTradeWindow( CSocket& mSock, CChar& mChar, CItem& tradeWindowOne, CItem& iDropped, SI16 x, SI16 y, SI08 gridLoc )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Called when item is dropped in the secure trade window
//o-----------------------------------------------------------------------------------------------o
void DropOnTradeWindow( CSocket& mSock, CChar& mChar, CItem& tradeWindowOne, CItem& iDropped, SI16 x, SI16 y, SI08 gridLoc )
{
if( mSock.PickupSpot() == PL_OTHERPACK || mSock.PickupSpot() == PL_GROUND )
Weight->subtractItemWeight( &mChar, &iDropped );
iDropped.SetCont( &tradeWindowOne );
iDropped.SetX( x );
iDropped.SetY( y );
iDropped.SetZ( 9 );
iDropped.SetGridLocation( gridLoc );
CItem *tradeWindowTwo = calcItemObjFromSer( tradeWindowOne.GetTempVar( CITV_MOREX ) );
if( ValidateObject( tradeWindowTwo ) )
{
if( tradeWindowTwo->GetTempVar( CITV_MOREZ ) || tradeWindowOne.GetTempVar( CITV_MOREZ ) )
{
tradeWindowTwo->SetTempVar( CITV_MOREZ, 0 );
tradeWindowOne.SetTempVar( CITV_MOREZ, 0 );
sendTradeStatus( tradeWindowTwo, &tradeWindowOne );
}
CChar *tw2Owner = FindItemOwner( tradeWindowTwo );
if( ValidateObject( tw2Owner ) )
{
CSocket *tw2Sock = tw2Owner->GetSocket();
if( tw2Sock != nullptr )
Effects->itemSound( tw2Sock, &tradeWindowOne, ( mSock.PickupSpot() == PL_OTHERPACK || mSock.PickupSpot() == PL_GROUND ) );
}
}
}
//o-----------------------------------------------------------------------------------------------o
//| Function - bool ValidateLockdownAccess( CChar *mChar, CSocket *mSock, CItem *itemToCheck, bool checkFriend )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Check if player has access to a locked down item as an owner, co-owner or friend
//o-----------------------------------------------------------------------------------------------o
bool ValidateLockdownAccess( CChar *mChar, CSocket *mSock, CItem *itemToCheck, bool checkFriend )
{
CMultiObj *iMulti = itemToCheck->GetMultiObj();
if( ValidateObject( iMulti ) && !iMulti->IsOwner( mChar ) && !iMulti->IsOnOwnerList( mChar )
&& ( checkFriend && !iMulti->IsOnFriendList( mChar )))
{
mSock->sysmessage( 1032 ); // This is not yours!
return false;
}
return true;
}
//o-----------------------------------------------------------------------------------------------o
//| Function - void DropOnSpellBook( CSocket& mSock, CChar& mChar, CItem& spellBook, CItem& iDropped )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Called when item is dropped on a spellbook
//o-----------------------------------------------------------------------------------------------o
void DropOnSpellBook( CSocket& mSock, CChar& mChar, CItem& spellBook, CItem& iDropped )
{
if( mSock.PickupSpot() == PL_OTHERPACK || mSock.PickupSpot() == PL_GROUND )
Weight->subtractItemWeight( &mChar, &iDropped );
if( iDropped.GetID( 1 ) != 0x1F || iDropped.GetID( 2 ) < 0x2D || iDropped.GetID( 2 ) > 0x72 )
{
Bounce( &mSock, &iDropped );
mSock.sysmessage( 1202 ); // You can only place spell scrolls in a spellbook!
return;
}
CChar *sbOwner = FindItemOwner( &spellBook );
if( ValidateObject( sbOwner ) && sbOwner != &mChar && !mChar.CanSnoop() )
{
Bounce( &mSock, &iDropped );
mSock.sysmessage( 1203 ); // You cannot place spells in other peoples spellbooks.
return;
}
std::string name;
name.reserve( MAX_NAME );
if( iDropped.GetName()[0] == '#' )
getTileName( iDropped, name );
else
name = iDropped.GetName();
if( spellBook.GetTempVar( CITV_MORE, 1 ) == 1 ) // using more1 to "lock" a spellbook for RP purposes
{
mSock.sysmessage( 1204 ); // There are no empty pages left in your book.
Bounce( &mSock, &iDropped );
return;
}
if( name == Dictionary->GetEntry( 1605 ) ) // All-Spell Scroll
{
if( spellBook.GetSpell( 0 ) == INVALIDSERIAL && spellBook.GetSpell( 1 ) == INVALIDSERIAL && spellBook.GetSpell( 2 ) == INVALIDSERIAL )
{
mSock.sysmessage( 1205 ); // You already have a full book!
Bounce( &mSock, &iDropped );
return;
}
spellBook.SetSpell( 0, INVALIDSERIAL );
spellBook.SetSpell( 1, INVALIDSERIAL );
spellBook.SetSpell( 2, INVALIDSERIAL );
}
else
{
SI32 targSpellNum = 0;
UI16 scrollID = iDropped.GetID();
if( scrollID == 0x1F2D )
targSpellNum = 6;
else if( scrollID > 0x1F2D && scrollID < 0x1F34 )
targSpellNum = (scrollID - 0x1F2E);
else if( scrollID >= 0x1F34 && scrollID < 0x1F6D )
targSpellNum = (scrollID - 0x1F2D);
if( Magic->HasSpell( &spellBook, targSpellNum ) )
{
mSock.sysmessage( 1206 ); // You already have that spell.
Bounce( &mSock, &iDropped );
return;
}
else
Magic->AddSpell( &spellBook, targSpellNum );
}
Effects->PlaySound( &mSock, 0x0042, false );
if( iDropped.GetAmount() > 1 )
{
iDropped.IncAmount( -1 );
Bounce( &mSock, &iDropped );
}
else
iDropped.Delete();
}
//o-----------------------------------------------------------------------------------------------o
//| Function - bool DropOnStack( CSocket& mSock, CChar& mChar, CItem& droppedOn, CItem& iDropped, bool &stackDeleted )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Called when item is dropped on a stack of items
//o-----------------------------------------------------------------------------------------------o
bool DropOnStack( CSocket& mSock, CChar& mChar, CItem& droppedOn, CItem& iDropped, bool &stackDeleted )
{
// Check if player is allowed to drop item on a locked down stack of items
if( droppedOn.IsLockedDown() && !ValidateLockdownAccess( &mChar, &mSock, &droppedOn, true ) )
{
if( mSock.PickupSpot() == PL_OTHERPACK || mSock.PickupSpot() == PL_GROUND )
Weight->subtractItemWeight( &mChar, &iDropped );
mSock.sysmessage( 774 ); // That is locked down and you cannot use it.
Bounce( &mSock, &iDropped );
return false;
}
bool canHold = true;
if( droppedOn.GetCont() != nullptr )
{
if( droppedOn.GetContSerial() >= BASEITEMSERIAL )
canHold = Weight->checkPackWeight( &mChar, static_cast<CItem *>(droppedOn.GetCont()), &iDropped );
else
canHold = Weight->checkCharWeight( &mChar, static_cast<CChar *>(droppedOn.GetCont()), &iDropped );
}
if( !canHold )
{
if( droppedOn.GetContSerial() >= BASEITEMSERIAL )
mSock.sysmessage( 1385 ); // That pack cannot hold any more weight
else
mSock.sysmessage( 1743 ); // That person can not possibly hold more weight
if( mSock.PickupSpot() == PL_OTHERPACK || mSock.PickupSpot() == PL_GROUND )
Weight->subtractItemWeight( &mChar, &iDropped );
Bounce( &mSock, &iDropped );
return false;
}
if( mSock.PickupSpot() == PL_OTHERPACK || mSock.PickupSpot() == PL_GROUND )
Weight->subtractItemWeight( &mChar, &iDropped );
stackDeleted = ( doStacking( &mSock, &mChar, &iDropped, &droppedOn ) != &iDropped );
if( !stackDeleted ) // if the item didn't stack or the stack was full
Bounce( &mSock, &iDropped );
return true;
}
//o-----------------------------------------------------------------------------------------------o
//| Function - UI16 HandleAutoStack( CItem *mItem, CItem *mCont )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Automatically stack an item with existing items in a container
//o-----------------------------------------------------------------------------------------------o
UI16 HandleAutoStack( CItem *mItem, CItem *mCont, CSocket *mSock, CChar *mChar )
{
// Check that container can hold more weight
if( mItem != mCont->GetCont() && !Weight->checkPackWeight( mChar, mCont, mItem ))
{
if( mSock != nullptr && ValidateObject( mChar ) )
{
if( mSock->PickupSpot() == PL_OTHERPACK || mSock->PickupSpot() == PL_GROUND )
Weight->subtractItemWeight( mChar, mItem );
mSock->sysmessage( 1385 ); // That pack cannot hold any more weight
Bounce( mSock, mItem );
return mItem->GetAmount();
}
}
// Attempt to autostack item in container
bool stackDeleted = ( autoStack( mSock, mItem, mCont ) != mItem );
if( !stackDeleted )
{
if( mSock != nullptr && ( mSock->PickupSpot() == PL_OTHERPACK || mSock->PickupSpot() == PL_GROUND ) )
{
Weight->subtractItemWeight( mChar, mItem );
}
return mItem->GetAmount();
}
return 0;
}
//o-----------------------------------------------------------------------------------------------o
//| Function - bool DropOnContainer( CSocket& mSock, CChar& mChar, CItem& droppedOn, CItem& iDropped, bool &stackDeleted, SI16 x, SI16 y, SI08 gridLoc )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Called when item is dropped on a container
//o-----------------------------------------------------------------------------------------------o
bool DropOnContainer( CSocket& mSock, CChar& mChar, CItem& droppedOn, CItem& iDropped, bool &stackDeleted, SI16 x, SI16 y, SI08 gridLoc )
{
CChar *contOwner = FindItemOwner( &droppedOn );
if( ValidateObject( contOwner ) )
{
if( contOwner == &mChar )
{
CBaseObject *recurseCont = droppedOn.GetCont();
while( ValidateObject( recurseCont ) )
{
if( recurseCont->CanBeObjType( OT_ITEM ) )
{
CItem *recurseItem = static_cast<CItem *>(recurseCont);
if( recurseItem->GetType() == IT_TRADEWINDOW )
{
CItem *tradeWindowTwo = calcItemObjFromSer( recurseItem->GetTempVar( CITV_MOREX ) );
if( ValidateObject( tradeWindowTwo ) )
{
if( tradeWindowTwo->GetTempVar( CITV_MOREZ ) || recurseItem->GetTempVar( CITV_MOREZ ) )
{
tradeWindowTwo->SetTempVar( CITV_MOREZ, 0 );
recurseItem->SetTempVar( CITV_MOREZ, 0 );
sendTradeStatus( tradeWindowTwo, recurseItem );
}
CChar *tw2Char = FindItemOwner( tradeWindowTwo );
if( ValidateObject( tw2Char ) )
{
CSocket *tw2Sock = tw2Char->GetSocket();
if( tw2Sock != nullptr )
Effects->itemSound( tw2Sock, recurseItem, ( mSock.PickupSpot() == PL_OTHERPACK || mSock.PickupSpot() == PL_GROUND ) );
}
}
break;
}
else
recurseCont = recurseItem->GetCont();
}
else
break;
}
}
else if( contOwner->IsNpc() && contOwner->GetNPCAiType() == AI_PLAYERVENDOR )
{
if( contOwner->GetOwnerObj() == &mChar || mChar.GetCommandLevel() > CL_PLAYER )
{
mChar.SetSpeechMode( 3 );
mChar.SetSpeechItem( &iDropped );
mSock.sysmessage( 1207 );
}
else
{
if( mSock.PickupSpot() == PL_OTHERPACK || mSock.PickupSpot() == PL_GROUND )
Weight->subtractItemWeight( &mChar, &iDropped );
mSock.sysmessage( 1630 ); // You cannot put items in other players packs!
Bounce( &mSock, &iDropped );
return false;
}
}
else if( mChar.GetCommandLevel() < CL_CNS && ( !contOwner->IsNpc() || !contOwner->IsTamed() ||
( contOwner->GetID() != 0x0123 && contOwner->GetID() != 0x0124 && contOwner->GetID() != 0x0317 ) ||
( contOwner->GetOwnerObj() != &mChar && !Npcs->checkPetFriend( &mChar, contOwner ) ) ) )
{
if( mSock.PickupSpot() == PL_OTHERPACK || mSock.PickupSpot() == PL_GROUND )
Weight->subtractItemWeight( &mChar, &iDropped );
mSock.sysmessage( 1630 ); // You cannot put items in other players packs!
Bounce( &mSock, &iDropped );
return false;
}
}
// Check if player is allowed to drop item on container
if( droppedOn.IsLockedDown() )
{
auto iMultiObj = droppedOn.GetMultiObj();
if( ValidateObject( iMultiObj ) && iMultiObj->IsSecureContainer( &droppedOn ) )
{
if( !ValidateLockdownAccess( &mChar, &mSock, &droppedOn, true ) )
{
if( mSock.PickupSpot() == PL_OTHERPACK || mSock.PickupSpot() == PL_GROUND )
Weight->subtractItemWeight( &mChar, &iDropped );
mSock.sysmessage( 774 ); // That is locked down and you cannot use it.
Bounce( &mSock, &iDropped );
return false;
}
}
}
// Check if container can hold more items
if( iDropped.GetContSerial() != droppedOn.GetSerial() && ( mSock.PickupSpot() != PL_OWNPACK && ( GetTotalItemCount( &droppedOn ) >= droppedOn.GetMaxItems() ||
GetTotalItemCount( &droppedOn ) + std::max(static_cast<UI32>(1), 1 + GetTotalItemCount( &iDropped )) > droppedOn.GetMaxItems() )))
{
if( mSock.PickupSpot() == PL_OTHERPACK || mSock.PickupSpot() == PL_GROUND )
Weight->subtractItemWeight( &mChar, &iDropped );
mSock.sysmessage( 1818 ); // The container is already at capacity.
Bounce( &mSock, &iDropped );
return false;
}
if( mSock.GetByte( 5 ) != 0xFF ) // In a specific spot in a container
{
if( mSock.PickupSpot() == PL_OTHERPACK || mSock.PickupSpot() == PL_GROUND )
Weight->subtractItemWeight( &mChar, &iDropped );
if( &droppedOn != iDropped.GetCont() && !Weight->checkPackWeight( &mChar, &droppedOn, &iDropped ) )
{
mSock.sysmessage( 1385 ); // That pack cannot hold any more weight
Bounce( &mSock, &iDropped );
return false;
}
iDropped.SetCont( &droppedOn );
iDropped.SetX( x );
iDropped.SetY( y );
iDropped.SetZ( 9 );
iDropped.SetGridLocation( gridLoc );
// Only send tooltip if server feature for tooltips is enabled
if( cwmWorldState->ServerData()->GetServerFeature( SF_BIT_AOS ) )
{
// Refresh target container tooltip
CPToolTip pSend( droppedOn.GetSerial(), &mSock );
mSock.Send(&pSend);
}
}
else // Drop directly on a container, placing it randomly inside
{
[[maybe_unused]] UI16 amountLeft = HandleAutoStack( &iDropped, &droppedOn, &mSock, &mChar );
// Only send tooltip if server feature for tooltips is enabled
if( cwmWorldState->ServerData()->GetServerFeature( SF_BIT_AOS ) )
{
// Refresh target container tooltip
CPToolTip pSend( droppedOn.GetSerial(), &mSock);
mSock.Send(&pSend);
}
}
return true;
}
//o-----------------------------------------------------------------------------------------------o
//| Function - void DropOnItem( CSocket *mSock, SERIAL item, SERIAL dest, SI16 x, SI16 y, SI08 z, SI08 gridLoc )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Called when an item is dropped in a container or on another item
//o-----------------------------------------------------------------------------------------------o
void DropOnItem( CSocket *mSock, SERIAL item, SERIAL dest, SI16 x, SI16 y, SI08 z, SI08 gridLoc )
{
CChar *mChar = mSock->CurrcharObj();
CItem *nCont = calcItemObjFromSer( dest );
if( !ValidateObject( nCont ) || !ValidateObject( mChar ) )
return;
CItem *nItem = calcItemObjFromSer( item );
if( !ValidateObject( nItem ) )
return;
nItem->SetHeldOnCursor( false );
bool executeCont = true;
std::vector<UI16> scriptTriggers = nItem->GetScriptTriggers();
for( auto scriptTrig : scriptTriggers )
{
cScript *toExecute = JSMapping->GetScript( scriptTrig );
if( toExecute != nullptr )
{
SI08 rVal = toExecute->OnDropItemOnItem( nItem, mChar, nCont ); // returns 1 if we should bounce it
switch( rVal )
{
case -1: // no such event
default:
executeCont = true;
break;
case 0: // bounce, no code, handle player feedback in script
if( mSock->PickupSpot() == PL_OTHERPACK || mSock->PickupSpot() == PL_GROUND )
Weight->subtractItemWeight( mChar, nItem );
Bounce( mSock, nItem );
return; // stack not deleted, as we're bouncing
case 1: // don't bounce, use code
executeCont = false;
break;
case 2: // don't bounce, don't use code
executeCont = true;
break;
}
}
}
if( executeCont )
{
std::vector<UI16> contScriptTriggers = nCont->GetScriptTriggers();
for( auto scriptTrig : contScriptTriggers )
{
cScript *toExecute = JSMapping->GetScript( scriptTrig );
if( toExecute != nullptr )
{
SI08 rVal = toExecute->OnDropItemOnItem( nItem, mChar, nCont ); // returns 1 if we should bounce it
switch( rVal )
{
case 1: // don't bounce, use code
case -1: // no such event
default:
break;
case 0: // bounce, no code, handle player feedback in script
if( mSock->PickupSpot() == PL_OTHERPACK || mSock->PickupSpot() == PL_GROUND )
Weight->subtractItemWeight( mChar, nItem );
Bounce( mSock, nItem );
return; // stack not deleted, as we're bouncing
case 2: // don't bounce, don't use code
return;
}
}
}
}
bool stackDeleted = false;
CTile& tile = Map->SeekTile( nItem->GetID() );
if( !mChar->AllMove() && ( nItem->GetMovable() == 2 || ( nItem->IsLockedDown() && nItem->GetOwnerObj() != mChar ) ||
( tile.Weight() == 255 && nItem->GetMovable() != 1 ) ) )
{
if( mChar->GetCommandLevel() < 2 || mSock->PickupSpot() != PL_OWNPACK )
{
if( mSock->PickupSpot() == PL_OTHERPACK || mSock->PickupSpot() == PL_GROUND )
Weight->subtractItemWeight( mChar, nItem );
mSock->sysmessage( 9064 ); // You cannot pick that up.
Bounce( mSock, nItem );
return;
}
}
if( nCont->GetType() == IT_TRADEWINDOW && FindItemOwner( nCont ) == mChar ) // Trade window
DropOnTradeWindow( (*mSock), (*mChar), (*nCont), (*nItem), x, y, gridLoc );
else if( nCont->GetType() == IT_TRASHCONT ) // Trash container
{
Effects->PlaySound( mSock, 0x0042, false );
if( mSock->PickupSpot() == PL_OTHERPACK || mSock->PickupSpot() == PL_GROUND )
Weight->subtractItemWeight( mChar, nItem );
nItem->Delete();
mSock->sysmessage( 1201 ); // As you let go of the item it disappears.
return;
}
else if( nCont->GetType() == IT_SPELLBOOK ) // Spell Book
{
DropOnSpellBook( (*mSock), (*mChar), (*nCont), (*nItem) );
return;
}
else if( nCont->GetType() == IT_CONTAINER )
{
if( !DropOnContainer( (*mSock), (*mChar), (*nCont), (*nItem), stackDeleted, x, y, gridLoc ) )
return;
}
else if( nCont->isPileable() && nItem->isPileable() && nCont->GetID() == nItem->GetID() && nCont->GetColour() == nItem->GetColour() && nCont->GetTempVar( CITV_MORE ) == nItem->GetTempVar( CITV_MORE )
&& nCont->GetTempVar( CITV_MOREX ) == nItem->GetTempVar( CITV_MOREX ) && nCont->GetTempVar( CITV_MOREY ) == nItem->GetTempVar( CITV_MOREY ) && nCont->GetTempVar( CITV_MOREZ ) == nItem->GetTempVar( CITV_MOREZ )
&& nCont->GetBuyValue() == nItem->GetBuyValue() && nCont->GetSellValue() == nItem->GetSellValue() )
{ // Stacking
if( !DropOnStack( (*mSock), (*mChar), (*nCont), (*nItem), stackDeleted ) )
return;
}
else
{
if( nCont->GetType() == IT_SPAWNCONT || nCont->GetType() == IT_UNLOCKABLESPAWNCONT ) // - Unlocked item spawner or unlockable item spawner
nItem->SetCont( nCont );
else
nItem->SetCont( nCont->GetCont() );
nItem->SetX( x );
nItem->SetY( y );
nItem->SetZ( z );
nItem->SetGridLocation( gridLoc );
if( mSock->PickupSpot() == PL_OTHERPACK || mSock->PickupSpot() == PL_GROUND )
Weight->subtractItemWeight( mChar, nItem );
}
if( !stackDeleted )
Effects->itemSound( mSock, nItem, ( mSock->PickupSpot() == PL_OTHERPACK || mSock->PickupSpot() == PL_GROUND ) );
//Would be nice to do this to instantly update the stats & weight on the container the item is dropped on
// but unfortunately it seems like updating the container will also force-close it :/
// nCont->Dirty( UT_UPDATE );
}
//o-----------------------------------------------------------------------------------------------o
//| Function - bool CPIDropItem::Handle( void )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Called when an item is dropped
//o-----------------------------------------------------------------------------------------------o
bool CPIDropItem::Handle( void )
{
CChar *ourChar = tSock->CurrcharObj();
ourChar->BreakConcentration( tSock );
if( dest >= BASEITEMSERIAL && dest != INVALIDSERIAL )
DropOnItem( tSock, item, dest, x, y, z, gridLoc );
else
Drop( tSock, item, dest, x, y, z, gridLoc );
// Display overloaded message if character is overloaded as a result of the above
if( Weight->isOverloaded( ourChar ) )
{
SI32 maxWeight = ourChar->GetStrength() * cwmWorldState->ServerData()->WeightPerStr() + 40;
SI32 currentWeight = ourChar->GetWeight() / 100;
tSock->sysmessage( 1784, currentWeight, maxWeight ); //You are overloaded. Current / Max: %i / %i
}
// Only send tooltip if server feature for tooltips is enabled
if( cwmWorldState->ServerData()->GetServerFeature( SF_BIT_AOS ) )
{
// Refresh source container tooltip
CPToolTip pSend( tSock->PickupSerial(), tSock );
tSock->Send(&pSend);
}
//Reset PickupSpot after dropping the item
tSock->PickupSpot( PL_NOWHERE );
tSock->SetCursorItem( nullptr );
return true;
}
//o-----------------------------------------------------------------------------------------------o
//| Function - UI08 BestSkill( CChar *mChar, SKILLVAL &skillLevel )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Return characters highest skill
//o-----------------------------------------------------------------------------------------------o
UI08 BestSkill( CChar *mChar, SKILLVAL &skillLevel )
{
UI08 retSkill = 0;
std::vector< cSkillClass > vecSkills;
for( UI08 sCtr = 0 ; sCtr < ALLSKILLS; ++sCtr )
vecSkills.push_back( cSkillClass( sCtr, mChar->GetBaseSkill( sCtr ) ) );
std::sort( vecSkills.rbegin(), vecSkills.rend() );
if( vecSkills[0].value > 0 )
{
retSkill = vecSkills[0].skill;
skillLevel = vecSkills[0].value;
}
return retSkill;
}
//o-----------------------------------------------------------------------------------------------o
//| Function - void getSkillProwessTitle( CChar *mChar, std::string &SkillProwessTitle )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Fetches the characters "prowess" and "skill" title based upon titles.dfn
//| entries and characters best skill
//o-----------------------------------------------------------------------------------------------o
void getSkillProwessTitle( CChar *mChar, std::string &SkillProwessTitle )
{
if( cwmWorldState->prowessTitles.empty() )
return;
SKILLVAL skillLevel = 0;
UI08 bestSkill = BestSkill( mChar, skillLevel );
if( skillLevel <= cwmWorldState->prowessTitles[0].lowBound )
SkillProwessTitle = cwmWorldState->prowessTitles[0].toDisplay;
else
{
size_t pEntry = 0;
for( pEntry = 0; pEntry < cwmWorldState->prowessTitles.size() - 1; ++pEntry )
{
if( skillLevel >= cwmWorldState->prowessTitles[pEntry].lowBound && skillLevel < cwmWorldState->prowessTitles[pEntry+1].lowBound )
break;
}
SkillProwessTitle = cwmWorldState->prowessTitles[pEntry].toDisplay;
}
SkillProwessTitle += " " + cwmWorldState->title[static_cast<UI08>(bestSkill + 1)].skill;
}
//o-----------------------------------------------------------------------------------------------o
//| Function - void getFameTitle( CChar *p, std::string& FameTitle )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Returns players reputation title based on their Fame and Karma
//o-----------------------------------------------------------------------------------------------o
void getFameTitle( CChar *p, std::string& FameTitle )
{
if( ValidateObject( p ) )
{
std::string thetitle;
UI08 titlenum = 0xFF;
SI16 k = p->GetKarma();
SI16 f = p->GetFame();
if( k >= 10000 )
{
if( f >= 5000 )
titlenum = 0;
else if( f >= 2500 )
titlenum = 1;
else if( f >= 1250 )
titlenum = 2;
else
titlenum = 3;
}
else if( k >= 5000 )
{
if( f >= 5000 )
titlenum = 4;
else if( f >= 2500 )
titlenum = 5;
else if( f >= 1250 )
titlenum = 6;
else
titlenum = 7;
}
else if( k >= 2500 )
{
if( f >= 5000 )
titlenum = 8;
else if( f >= 2500 )
titlenum = 9;
else if( f >= 1250 )
titlenum = 10;
else
titlenum = 11;
}
else if( k >= 1250 )
{
if( f >= 5000 )
titlenum = 12;
else if( f >= 2500 )
titlenum = 13;
else if( f >= 1250 )
titlenum = 14;
else
titlenum = 15;
}
else if( k >= 625 )
{
if( f >= 5000 )
titlenum = 16;
else if( f >= 1000 )
titlenum = 17;
else if( f >= 500 )
titlenum = 18;
else
titlenum = 19;
}
else if( k > -625 )
{
if( f >= 5000 )
titlenum = 20;
else if( f >= 2500 )
titlenum = 21;
else if( f >= 1250 )
titlenum = 22;
else
titlenum = 23;
}
else if( k > -1250 )
{
if( f >= 10000 )
titlenum = 28;
else if( f >= 5000 )
titlenum = 27;
else if( f >= 2500 )
titlenum = 26;
else if( f >= 1250 )
titlenum = 25;
else
titlenum = 24;
}
else if( k > -2500 )
{
if( f >= 5000 )
titlenum = 32;
else if( f >= 2500 )
titlenum = 31;
else if( f >= 1250 )
titlenum = 30;
else
titlenum = 29;
}
else if( k > -5000 )
{
if( f >= 10000 )
titlenum = 37;
else if( f >= 5000 )
titlenum = 36;
else if( f >= 2500 )
titlenum = 35;
else if( f >= 1250 )
titlenum = 34;
else
titlenum = 33;
}
else if( k > -10000 )
{
if( f >= 5000 )
titlenum = 41;
else if( f >= 2500 )
titlenum = 40;
else if( f >= 1250 )
titlenum = 39;
else
titlenum = 38;
}
else if( k <= -10000 )
{
if( f >= 5000 )
titlenum = 45;
else if( f >= 2500 )
titlenum = 44;
else if( f >= 1250 )
titlenum = 43;
else
titlenum = 42;
}
if( !cwmWorldState->title[titlenum].fame.empty() )
thetitle = cwmWorldState->title[titlenum].fame + " ";
if( p->GetRace() != 0 && p->GetRace() != 65535 )
thetitle = thetitle + Races->Name( p->GetRace() ) + " ";
if( f >= 10000 ) // bugfix for repsys
{
if( p->GetKills() > cwmWorldState->ServerData()->RepMaxKills() )
{
if( p->GetID( 2 ) == 0x91 )
{
FameTitle = oldstrutil::format( Dictionary->GetEntry( 1177 ), Races->Name( p->GetRace() ).c_str() ) + std::string(" ");
}
else
{
FameTitle = oldstrutil::format( Dictionary->GetEntry( 1178 ), Races->Name( p->GetRace() ).c_str() ) + std::string(" ");
}
}
else if( p->GetID( 2 ) == 0x91 )
{
FameTitle = oldstrutil::format( Dictionary->GetEntry( 1179 ), thetitle.c_str() ) + std::string(" ");
}
else
{
FameTitle = oldstrutil::format( Dictionary->GetEntry( 1180 ), thetitle.c_str() ) + std::string(" ");
}
}
else
{
if( p->GetKills() > cwmWorldState->ServerData()->RepMaxKills() )
{
FameTitle = Dictionary->GetEntry( 1181 ) + std::string(" ");
}
else if( !( thetitle = oldstrutil::removeTrailing( thetitle, "//" )).empty() )
{
FameTitle = oldstrutil::format( Dictionary->GetEntry( 1182 ), thetitle.c_str() );
}
}
}
}
//o-----------------------------------------------------------------------------------------------o
//| Function - void PaperDoll( CSocket *s, CChar *pdoll )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Open a characters paperdoll and show titles based on skill,
//| reputation, murder counts, race, ect.
//o-----------------------------------------------------------------------------------------------o
void PaperDoll( CSocket *s, CChar *pdoll )
{
CChar *myChar = s->CurrcharObj();
std::string tempstr;
CPPaperdoll pd = (*pdoll);
UnicodeTypes sLang = s->Language();
cScript *toExecute;
std::vector<UI16> scriptTriggers = pdoll->GetScriptTriggers();
for( auto scriptTrig : scriptTriggers )
{
toExecute = JSMapping->GetScript( scriptTrig );
if( toExecute != nullptr )
{
// -1 == script doesn't exist, or returned -1
// 0 == script returned false, 0, or nothing - don't execute hard code
// 1 == script returned true or 1
if( toExecute->OnCharDoubleClick( myChar, pdoll ) == 0 )
{
// if it exists and we don't want hard code, return
return;
}
}
}
// Also check global script!
toExecute = JSMapping->GetScript( static_cast<UI16>(0) );
if( toExecute != nullptr )
{
if( toExecute->OnCharDoubleClick( myChar, pdoll ) == 0 )
{
// if it exists and we don't want hard code, return
return;
}
}
std::string SkillProwessTitle;
std::string FameTitle;
getSkillProwessTitle( pdoll, SkillProwessTitle );
getFameTitle( pdoll, FameTitle );
bool bContinue = false;
if( pdoll->IsGM() )
tempstr = pdoll->GetName() + " " + pdoll->GetTitle();
else if( pdoll->IsNpc() )
{
// Prefix name with fame title, if it exists
if( FameTitle != "" )
tempstr = FameTitle;
// Then add actual name
tempstr += pdoll->GetNameRequest( myChar );
// Append title, if one exists
if( pdoll->GetTitle() != "" )
{
std::string tempTitle = getNpcDictTitle( pdoll, s );
tempstr += " " + tempTitle;
}
}
else if( pdoll->IsDead() )
tempstr = pdoll->GetNameRequest( myChar );
// Murder tags now scriptable in SECTION MURDERER - Titles.dfn
else if( pdoll->GetKills() > cwmWorldState->ServerData()->RepMaxKills() )
{
if( cwmWorldState->murdererTags.empty() )
tempstr = oldstrutil::format( Dictionary->GetEntry( 374, sLang ), pdoll->GetNameRequest( myChar ).c_str(), pdoll->GetTitle().c_str(), SkillProwessTitle.c_str() );
else if( pdoll->GetKills() < cwmWorldState->murdererTags[0].lowBound ) // not a real murderer
bContinue = true;
else
{
SI16 mKills = pdoll->GetKills();
size_t kCtr;
for( kCtr = 0; kCtr < cwmWorldState->murdererTags.size() - 1; ++kCtr )
{
if( mKills >= cwmWorldState->murdererTags[kCtr].lowBound && mKills < cwmWorldState->murdererTags[kCtr+1].lowBound )
break;
}
if( kCtr >= cwmWorldState->murdererTags.size() )
bContinue = true;
else
tempstr = cwmWorldState->murdererTags[kCtr].toDisplay + " " + pdoll->GetNameRequest( myChar ) + ", " + pdoll->GetTitle() + SkillProwessTitle;
}
}
else if( pdoll->IsCriminal() )
tempstr = oldstrutil::format( Dictionary->GetEntry( 373, sLang ), pdoll->GetNameRequest( myChar ).c_str(), pdoll->GetTitle().c_str(), SkillProwessTitle.c_str() );
else
bContinue = true;
if( bContinue )
{
tempstr = FameTitle + pdoll->GetNameRequest( myChar ); //Repuation + Name
if( pdoll->GetTownTitle() || pdoll->GetTownPriv() == 2 ) // TownTitle
{
if( pdoll->GetTownPriv() == 2 ) // is Mayor
tempstr = oldstrutil::format( Dictionary->GetEntry( 379, sLang ), pdoll->GetNameRequest( myChar ).c_str(), cwmWorldState->townRegions[pdoll->GetTown()]->GetName().c_str(), SkillProwessTitle.c_str() );
else // is Resident
tempstr = pdoll->GetNameRequest( myChar ) + " of " + cwmWorldState->townRegions[pdoll->GetTown()]->GetName() + ", " + SkillProwessTitle;
}
else // No Town Title
{
if( !pdoll->IsIncognito() && !( pdoll->GetTitle().empty() ) ) // Titled & Skill
tempstr += " " + pdoll->GetTitle() + ", " + SkillProwessTitle;
else // Just skilled
tempstr += ", " + SkillProwessTitle;
}
}
pd.Text( tempstr );
CChar *mChar = s->CurrcharObj();
if( ValidateObject( mChar ) && ( mChar == pdoll ))
pd.FlagByte( 0x02 | ( mChar->IsAtWar() ? 0x01 : 0x00 ));
else if( ValidateObject( mChar ) && mChar->IsGM() )
pd.FlagByte( 0x02 );
s->Send( &pd );
// Only send tooltip if server feature for tooltips is enabled
if( cwmWorldState->ServerData()->GetServerFeature( SF_BIT_AOS ) )
{
for( CItem *wearItem = pdoll->FirstItem(); !pdoll->FinishedItems(); wearItem = pdoll->NextItem() )
{
if( ValidateObject( wearItem ) )
{
CPToolTip pSend( wearItem->GetSerial(), s );
s->Send( &pSend );
}
}
}
}
void MountCreature( CSocket *mSock, CChar *s, CChar *x );
//o-----------------------------------------------------------------------------------------------o
//| Function - void handleCharDoubleClick( CSocket *mSock, SERIAL serial, bool keyboard )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Handles double-clicks on a character
//o-----------------------------------------------------------------------------------------------o
void handleCharDoubleClick( CSocket *mSock, SERIAL serial, bool keyboard )
{
CChar *mChar = mSock->CurrcharObj();
CChar *c = calcCharObjFromSer( serial );
if( !ValidateObject( c ) )
return;
std::vector<UI16> scriptTriggers = c->GetScriptTriggers();
for( auto scriptTrig : scriptTriggers )
{
cScript *toExecute = JSMapping->GetScript( scriptTrig );
if( toExecute != nullptr )
{
// -1 == script doesn't exist, or returned -1
// 0 == script returned false, 0, or nothing - don't execute hard code
// 1 == script returned true or 1
if( toExecute->OnCharDoubleClick( mChar, c ) == 0 ) // if it exists and we don't want hard code, return
return;
}
}
if( c->IsNpc() )
{
CItem *pack = nullptr;
if( cwmWorldState->creatures[c->GetID()].MountID() != 0 ) // Is a mount
{
if( ( c->IsTamed() && ( c->GetOwnerObj() == mChar || Npcs->checkPetFriend( mChar, c ) ) ) || mChar->GetCommandLevel() >= CL_GM )
{
if( objInRange( mChar, c, DIST_NEXTTILE ) )
{
if( mChar->IsDead() )
mSock->sysmessage( 381 );
else if( mChar->GetID() != mChar->GetOrgID() )
mSock->sysmessage( 380 );
else
MountCreature( mSock, mChar, c );
}
else
mSock->sysmessage( 382 );
}
else
mSock->sysmessage( 1214 );
return;
}
else if( !cwmWorldState->creatures[c->GetID()].IsHuman() && !c->IsDead() )
{
if( c->GetID() == 0x0123 || c->GetID() == 0x0124 || c->GetID() == 0x0317 ) // Is a pack animal
{
if( mChar->IsDead() )
mSock->sysmessage( 392 );
else if( !objInRange( mChar, c, DIST_NEARBY ) )
mSock->sysmessage( 382 );
else
{
pack = c->GetPackItem();
if( mChar->GetCommandLevel() >= CL_CNS || c->GetOwnerObj() == mChar || Npcs->checkPetFriend( mChar, c ) )
{
if( ValidateObject( pack ) )
mSock->openPack( pack );
else
Console << "Pack animal " << c->GetSerial() << " has no backpack!" << myendl;
}
else
{
Skills->Snooping( mSock, c, pack );
mSock->sysmessage( 383 );
}
}
return;
}
return;
}
else if( c->GetNPCAiType() == AI_PLAYERVENDOR ) // PlayerVendors
{
c->TextMessage( mSock, 385, TALK, false );
pack = c->GetPackItem();
if( ValidateObject( pack ) )
mSock->openPack( pack, true );
return;
}
}
else
{
if( mChar->GetSerial() == serial ) // Double-clicked yourself
{
if( mChar->IsOnHorse() && !keyboard )
{
DismountCreature( mChar ); // If on horse, dismount
return;
}
}
}
PaperDoll( mSock, c );
}
//o-----------------------------------------------------------------------------------------------o
//| Function - bool handleDoubleClickTypes( CSocket *mSock, CChar *mChar, CItem *iUsed, ItemTypes iType )
//| Date - 2/11/2003
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Runs a switch to match an item type to a function
//o-----------------------------------------------------------------------------------------------o
bool handleDoubleClickTypes( CSocket *mSock, CChar *mChar, CItem *iUsed, ItemTypes iType )
{
CChar *iChar = nullptr;
CItem *i = nullptr;
UI16 itemID = iUsed->GetID();
bool canTrap = false;
// Begin Check items by type
switch( iType )
{
case IT_NOTYPE:
case IT_COUNT:
case IT_HOUSEADDON:
return false;
case IT_CONTAINER: // Container, Backpack
case IT_SPAWNCONT: // Item spawn container
canTrap = true;
[[fallthrough]]; // Indicate to compiler that fallthrough is valid to suppress warning
case IT_UNLOCKABLESPAWNCONT: // Unlockable item spawn container
case IT_TRASHCONT: // Trash container
bool packOpened;
packOpened = false;
CBaseObject *baseCont;
ObjectType objType;
baseCont = FindItemOwner( iUsed, objType );
if( !ValidateObject( baseCont ) )
baseCont = iUsed;
if( ValidateObject( baseCont ) )
{
if( baseCont->CanBeObjType( OT_ITEM ) )
{
CMultiObj * baseContMultiObj = baseCont->GetMultiObj();
if( baseContMultiObj == nullptr || mChar->GetMultiObj() == baseContMultiObj )
{
if( baseContMultiObj && baseContMultiObj->IsSecureContainer( static_cast<CItem *>( baseCont ) ) && !mChar->GetMultiObj()->IsOnOwnerList( mChar ) )
{
mSock->sysmessage( 9011 ); // That container is secure. You cannot use this unless you are the owner.
return true;
}
else
{
mSock->openPack( iUsed );
packOpened = true;
}
}
}
else
{
iChar = static_cast<CChar *>(baseCont);
if( ValidateObject( iChar ) )
{
if( mChar == iChar || mChar == iChar->GetOwnerObj() )
{
if( iChar->GetNPCAiType() == AI_PLAYERVENDOR ) // PlayerVendors
mSock->openPack( iUsed, true );
else
{
if( mChar->GetPackItem() == iUsed )
mChar->SetWeight( Weight->calcCharWeight( mChar ));
mSock->openPack( iUsed );
}
}
else if( iChar->GetNPCAiType() == AI_PLAYERVENDOR ) // PlayerVendors
mSock->openPack( iUsed, true );
else
{
CItem *rootItem = FindRootContainer( iUsed );
if( ValidateObject( rootItem ) && rootItem->GetType() == IT_TRADEWINDOW )
mSock->openPack( iUsed, true );
else
Skills->Snooping( mSock, iChar, iUsed );
}
packOpened = true;
}
}
}
if( packOpened )
{
if( canTrap && iUsed->GetTempVar( CITV_MOREZ, 1 ))// Is trapped
Magic->MagicTrap( mChar, iUsed );
}
else
mSock->sysmessage( 400 );
return true;
case IT_CASTLEGATEOPENER: // Order gate opener
i = calcItemObjFromSer( iUsed->GetTempVar( CITV_MORE ) );
if( ValidateObject( i ) )
{
if( i->GetType() == IT_CASTLEGATE )
{
if( i->GetTempVar( CITV_MOREZ ) == 1 )
{
i->SetTempVar( CITV_MOREZ, 2 );
i->SetZ( i->GetZ() + 17 );
}
else if( i->GetTempVar( CITV_MOREZ ) == 2 )
{
i->SetTempVar( CITV_MOREZ, 1 );
i->SetZ( i->GetZ() - 17 );
}
}
}
return true;
case IT_LOCKEDCONTAINER: // Locked container
case IT_LOCKEDSPAWNCONT: // locked spawn container
mSock->sysmessage( 1428 );
return true;
case IT_SPELLBOOK: // Spellbook
i = mChar->GetPackItem();
if( ValidateObject( i ) )
{
if( ( iUsed->GetCont() == i || iUsed->GetCont() == mChar ) || iUsed->GetLayer() == IL_RIGHTHAND )
Magic->SpellBook( mSock );
else
mSock->sysmessage( 403 );
}
return true;
case IT_MAP: // Map
{
CPMapMessage m1;
CPMapRelated m2;
m1.KeyUsed( iUsed->GetSerial() );
m1.GumpArt( 0x139D );
m1.UpperLeft( 0, 0 );
m1.LowerRight( 0x13FF, 0x0FA0 );
m1.Dimensions( 0x0190, 0x0190 );
mSock->Send( &m1 );
m2.ID( iUsed->GetSerial() );
m2.Command( 5 );
m2.Location( 0, 0 );
m2.PlotState( 0 );
mSock->Send( &m2 );
return true;
}
case IT_BOOK: // Readable book
if( iUsed->GetTempVar( CITV_MOREX ) != 666 && iUsed->GetTempVar( CITV_MOREX ) != 999 )
Books->OpenPreDefBook( mSock, iUsed );
else
Books->OpenBook( mSock, iUsed, ( iUsed->GetTempVar( CITV_MOREX ) == 666 ) );
return true;
case IT_MAGICWAND: // Magic Wands
// If item is locked down, check if player has access to use it
if( iUsed->IsLockedDown() && !ValidateLockdownAccess( mChar, mSock, iUsed, true ))
return true;
if( iUsed->GetTempVar( CITV_MOREZ ) != 0 )
{
mSock->CurrentSpellType( 2 );
if( Magic->SelectSpell( mSock, iUsed->GetTempVar( CITV_MOREY )))
{
iUsed->SetTempVar( CITV_MOREZ, iUsed->GetTempVar( CITV_MOREZ ) - 1 );
if( iUsed->GetTempVar( CITV_MOREZ ) == 0 ) // Number of charges have dropped to zero
{
iUsed->SetType( IT_NOTYPE );
iUsed->SetTempVar( CITV_MOREX, 0 );
iUsed->SetTempVar( CITV_MOREY, 0 );
iUsed->SetOffSpell( 0 );
}
//Update item so player will see how many charges remain (if any)
iUsed->Dirty( UT_UPDATE );
}
}
return true;
case IT_TOWNSTONE: // Townstone and Townstone Deed
if( itemID == 0x14F0 ) // Check for Deed
{
// If item is locked down, check if player has access to use it
if( iUsed->IsLockedDown() && !ValidateLockdownAccess( mChar, mSock, iUsed, false ))
return true;
Items->CreateScriptItem( nullptr, mChar, "townstone", 1, OT_ITEM );
iUsed->Delete();
}
else // Display Townstone gump
{
CTownRegion *useRegion = iUsed->GetRegion();
if( useRegion != nullptr )
useRegion->DisplayTownMenu( iUsed, mSock );
}
return true;
case IT_RECALLRUNE: // Recall Rune
if( iUsed->GetTempVar( CITV_MOREX ) == 0 && iUsed->GetTempVar( CITV_MOREY ) == 0 && iUsed->GetTempVar( CITV_MOREZ ) == 0 ) // changed, to fix, Lord Vader
mSock->sysmessage( 431 );
else
{
// If item is locked down, check if player has access to use it
if( iUsed->IsLockedDown() && !ValidateLockdownAccess( mChar, mSock, iUsed, false ))
return true;
mSock->sysmessage( 432 );
mChar->SetSpeechItem( iUsed );
mChar->SetSpeechMode( 7 );
}
return true;
case IT_OBJTELEPORTER: // Object Teleporter
if( objInRange( mChar, iUsed, DIST_NEARBY ) )
mChar->SetLocation( static_cast<SI16>(iUsed->GetTempVar( CITV_MOREX )), static_cast<SI16>(iUsed->GetTempVar( CITV_MOREY )), static_cast<SI08>(iUsed->GetTempVar( CITV_MOREZ ) ));
return true;
case IT_MAPCHANGEOBJECT:
SocketMapChange( mSock, mChar, iUsed );
return true;
case IT_MORPHOBJECT: // Morph Object
mChar->SetID( static_cast<UI16>(iUsed->GetTempVar( CITV_MOREX )) );
iUsed->SetType( IT_UNMORPHOBJECT );
return true;
case IT_UNMORPHOBJECT: // Unmorph Object
mChar->SetID( mChar->GetOrgID() );
iUsed->SetType( IT_MORPHOBJECT );
return true;
case IT_PLANK: // Planks
if( objInRange( mChar, iUsed, DIST_INRANGE ) )
{
if( iUsed->GetID( 2 ) == 0x84 || iUsed->GetID( 2 ) == 0xD5 || iUsed->GetID( 2 ) == 0xD4 || iUsed->GetID( 2 ) == 0x89 )
PlankStuff( mSock, iUsed );
else
mSock->sysmessage( 398 );//Locked
}
else
mSock->sysmessage( 399 );
return true;
case IT_FIREWORKSWAND: //Fireworks wands
// If item is locked down, check if player has access to use it
if( iUsed->IsLockedDown() && !ValidateLockdownAccess( mChar, mSock, iUsed, true ))
return true;
if( iUsed->GetTempVar( CITV_MOREX ) == 0 )
{
mSock->sysmessage( 396 );
return true;
}
iUsed->SetTempVar( CITV_MOREX, iUsed->GetTempVar( CITV_MOREX ) - 1 );
mSock->sysmessage( 397, iUsed->GetTempVar( CITV_MOREX ) );
UI08 j;
for( j = 0; j < static_cast< UI08 >( RandomNum( 0, 3 ) + 2 ); ++j )
{
SI16 wx = ( mChar->GetX() + RandomNum( 0, 5 ) - 5 );
SI16 wy = ( mChar->GetY() - RandomNum( 0, 7 ) );
Effects->PlayMovingAnimation( mChar, wx, wy, mChar->GetZ() + 10, 0x36E4, 17, 0, ( RandomNum( 0, 1 ) == 1 ) );
UI16 animID;
switch( RandomNum( 0, 4 ) )
{
default:
case 0: animID = 0x373A; break;
case 1: animID = 0x374A; break;
case 2: animID = 0x375A; break;
case 3: animID = 0x376A; break;
case 4: animID = 0x377A; break;
}
Effects->PlayStaticAnimation( wx, wy, mChar->GetZ() + 10, animID, RandomNum( 0x04, 0x09 ), 30, 0 );
}
return true;
case IT_RENAMEDEED: // Rename Deed
// If item is locked down, check if player has access to use it
if( iUsed->IsLockedDown() && !ValidateLockdownAccess( mChar, mSock, iUsed, false ))
return true;
// Don't allow using GM-locked down tools
if( iUsed->GetMovable() == 2 )
{
mSock->sysmessage( 1032 ); // This is not yours!
return true;
}
mChar->SetSpeechItem( iUsed );
mChar->SetSpeechMode( 6 );
mSock->sysmessage( 434 );
return true;
case IT_TILLER: // Tillerman
{
CBoatObj *boat = static_cast<CBoatObj *>( iUsed->GetMultiObj() );
if( ValidateObject( boat ))
{
if( boat->GetMoveType() != BOAT_ANCHORED )
{
mSock->sysmessage( 2029 ); // You must lower the anchor to dock the boat.
}
else
{
if( objInRange( mChar, boat, DIST_INRANGE ) )
{
ModelBoat( mSock, boat );
}
else
{
mSock->sysmessage( 461 ); // You are too far away.
}
}
}
return true;
}
case IT_GUILDSTONE: // Guildstone Deed
if( itemID == 0x14F0 || itemID == 0x1869 ) // Check for Deed/Teleporter + Guild Type
{
// If item is locked down, check if player has access to use it
if( iUsed->IsLockedDown() && !ValidateLockdownAccess( mChar, mSock, iUsed, false ))
return true;
mChar->SetSpeechItem( iUsed ); // we're using this as a generic item
GuildSys->PlaceStone( mSock, iUsed );
return true;
}
else if( itemID == 0x0ED5 ) // Check for Guildstone + Guild Type
{
mSock->TempInt( iUsed->GetTempVar( CITV_MORE ) ); // track things properly
if( mChar->GetGuildNumber() == -1 || mChar->GetGuildNumber() == static_cast<SI16>(iUsed->GetTempVar( CITV_MORE )) )
GuildSys->Menu( mSock, BasePage + 1, static_cast<GUILDID>(iUsed->GetTempVar( CITV_MORE )) ); // more of the stone is the guild number
else
mSock->sysmessage( 438 );
return true;
}
else
Console << "Unhandled guild item type named: " << iUsed->GetName() << " with ID of: " << itemID << myendl;
return true;
case IT_METALREPAIRTOOL:
// If item is locked down, check if player has access to use it
if( iUsed->IsLockedDown() && !ValidateLockdownAccess( mChar, mSock, iUsed, true ))
return true;
mSock->TempObj( iUsed );
mSock->target( 0, TARGET_REPAIRMETAL, 0, 485 ); // What do we wish to repair?
return true;
case IT_FORGE: // Forges
mSock->TempObj( iUsed );
mSock->target( 0, TARGET_SMELT, 0, 440 );
return true;
case IT_DYE: // Dye
// If item is locked down, check if player has access to use it
if( iUsed->IsLockedDown() && !ValidateLockdownAccess( mChar, mSock, iUsed, false ))
return true;
// Don't allow using GM-locked down tools
if( iUsed->GetMovable() == 2 )
{
mSock->sysmessage( 1032 ); // This is not yours!
return true;
}
mSock->TempObj( iUsed );
mSock->DyeAll( 0 );
mSock->target( 0, TARGET_DYEALL, 0, 441 );
return true;
case IT_DYEVAT: // Dye vat
// If item is locked down, check if player has access to use it
if( iUsed->IsLockedDown() && !ValidateLockdownAccess( mChar, mSock, iUsed, true ))
return true;
// Don't allow using GM-locked down tools
if( iUsed->GetMovable() == 2 )
{
mSock->sysmessage( 1032 ); // This is not yours!
return true;
}
mSock->TempObj( iUsed );
mSock->AddID1( iUsed->GetColour( 1 ) );
mSock->AddID2( iUsed->GetColour( 2 ) );
mSock->target( 0, TARGET_DVAT, 0, 442 );
return true;
case IT_MODELMULTI: // Model boat/Houses
if( iType != IT_TOWNSTONE && iType != IT_GUILDSTONE )
{
// If item is locked down, check if player has access to use it
if( iUsed->IsLockedDown() && !ValidateLockdownAccess( mChar, mSock, iUsed, false ))
return true;
if( iUsed->GetTempVar( CITV_MOREX ) == 0 )
break;
mChar->SetSpeechItem( iUsed );
DoHouseTarget( mSock, static_cast<UI08>(iUsed->GetTempVar( CITV_MOREX )) );
}
return true;
case IT_SMITHYTOOL:
// If item is locked down, check if player has access to use it
if( iUsed->IsLockedDown() && !ValidateLockdownAccess( mChar, mSock, iUsed, true ))
return true;
// Don't allow using GM-locked down tools
if( iUsed->GetMovable() == 2 )
{
mSock->sysmessage( 1032 ); // This is not yours!
return true;
}
mSock->TempObj( iUsed );
mSock->target( 0, TARGET_SMITH, 0, 444 );
return true;
case IT_MININGTOOL: // Mining
// If item is locked down, check if player has access to use it
if( iUsed->IsLockedDown() && !ValidateLockdownAccess( mChar, mSock, iUsed, true ))
return true;
// Don't allow using GM-locked down tools
if( iUsed->GetMovable() == 2 )
{
mSock->sysmessage( 1032 ); // This is not yours!
return true;
}
// Don't allow using tools with no usage left, if usage limits are enabled in ini
if( cwmWorldState->ServerData()->ToolUseLimit() && iUsed->GetUsesLeft() == 0 )
{
mSock->sysmessage( 9262 ); // This has no more charges
return true;
}
mSock->TempObj( iUsed );
mSock->target( 0, TARGET_MINE, 0, 446 );
return true;
case IT_EMPTYVIAL: // empty vial
// If item is locked down, check if player has access to use it
if( iUsed->IsLockedDown() && !ValidateLockdownAccess( mChar, mSock, iUsed, true ))
return true;
// Don't allow using GM-locked down tools
if( iUsed->GetMovable() == 2 )
{
mSock->sysmessage( 1032 ); // This is not yours!
return true;
}
i = mChar->GetPackItem();
if( ValidateObject( i ) )
{
if( iUsed->GetCont() == i )
{
mSock->TempObj( iUsed );
mSock->target( 0, TARGET_VIAL, 0, 447 );
}
else
mSock->sysmessage( 448 );
}
return true;
case IT_ORE: // smelt ore
// If item is locked down, check if player has access to use it
if( iUsed->IsLockedDown() && !ValidateLockdownAccess( mChar, mSock, iUsed, false ))
return true;
// Don't allow using GM-locked down tools
if( iUsed->GetMovable() == 2 )
{
mSock->sysmessage( 1032 ); // This is not yours!
return true;
}
if( iUsed->GetID() == 0x19B7 && iUsed->GetAmount() < 2 )
{
mSock->sysmessage( 1814 ); // Too little ore to smelt!
}
else
{
mSock->TempObj( iUsed );
if( iUsed->GetID() == 0x19B7 )
mSock->target( 0, TARGET_SMELTORE, 0, 460 ); // Select the forge on which to smelt the ore.
else
mSock->target( 0, TARGET_SMELTORE, 0, 1815 ); // Select the forge on which to smelt the ore, or another pile of ore with which to combine it.
}
return true;
case IT_MESSAGEBOARD: // Message board opening
if( objInRange( mChar, iUsed, DIST_NEARBY ) )
{
CPIMsgBoardEvent *mbEvent = new CPIMsgBoardEvent( mSock, false );
mbEvent->Handle();
}
else
mSock->sysmessage( 461 );
return true;
case IT_MAGICSTATUE:
if( Skills->CheckSkill( mChar, ITEMID, 0, 10 ) )
{
if( itemID == 0x1508 )
iUsed->SetID( 0x1509 );
else
iUsed->SetID( 0x1508 );
}
else
mSock->sysmessage( 465 );
return true;
case IT_GUILLOTINE:
if( Skills->CheckSkill( mChar, ITEMID, 0, 10 ) )
{
if( itemID == 0x1245 )
iUsed->SetID( 0x1230 );
else
iUsed->SetID( 0x1245 );
}
else
mSock->sysmessage( 466 );
return true;
case IT_HAIRDYE: // Hair Dye
// If item is locked down, check if player has access to use it
if( iUsed->IsLockedDown() && !ValidateLockdownAccess( mChar, mSock, iUsed, false ))
return true;
// Don't allow using GM-locked down tools
if( iUsed->GetMovable() == 2 )
{
mSock->sysmessage( 1032 ); // This is not yours!
return true;
}
mSock->TempObj( iUsed );
BuildGumpFromScripts( mSock, 6 );
return true;
default:
if( iType )
Console << "Unhandled item type for item: " << iUsed->GetName() << "[" << iUsed->GetSerial() << "] of type: " << static_cast<UI16>(iType) << myendl;
break;
}
return false;
}
std::map< std::string, ItemTypes > tagToItemType;
void InitTagToItemType( void )
{
tagToItemType["CONTAINER"] = IT_CONTAINER;
tagToItemType["CASTLEGATEOPENER"] = IT_CASTLEGATEOPENER;
tagToItemType["CASTLEGATE"] = IT_CASTLEGATE;
tagToItemType["TELEPORTITEM"] = IT_TELEPORTITEM;
tagToItemType["KEY"] = IT_KEY;
tagToItemType["LOCKEDCONTAINER"] = IT_LOCKEDCONTAINER;
tagToItemType["SPELLBOOK"] = IT_SPELLBOOK;
tagToItemType["MAP"] = IT_MAP;
tagToItemType["BOOK"] = IT_BOOK;
tagToItemType["DOOR"] = IT_DOOR;
tagToItemType["LOCKEDDOOR"] = IT_LOCKEDDOOR;
tagToItemType["FOOD"] = IT_FOOD;
tagToItemType["MAGICWAND"] = IT_MAGICWAND;
tagToItemType["RESURRECTOBJECT"] = IT_RESURRECTOBJECT;
tagToItemType["CRYSTALBALL"] = IT_CRYSTALBALL;
tagToItemType["POTION"] = IT_POTION;
tagToItemType["TOWNSTONE"] = IT_TOWNSTONE;
tagToItemType["RECALLRUNE"] = IT_RECALLRUNE;
tagToItemType["GATE"] = IT_GATE;
tagToItemType["OBJTELEPORTER"] = IT_OBJTELEPORTER;
tagToItemType["ITEMSPAWNER"] = IT_ITEMSPAWNER;
tagToItemType["NPCSPAWNER"] = IT_NPCSPAWNER;
tagToItemType["SPAWNCONT"] = IT_SPAWNCONT;
tagToItemType["LOCKEDSPAWNCONT"] = IT_LOCKEDSPAWNCONT;
tagToItemType["UNLOCKABLESPAWNCONT"] = IT_UNLOCKABLESPAWNCONT;
tagToItemType["AREASPAWNER"] = IT_AREASPAWNER;
tagToItemType["ADVANCEGATE"] = IT_ADVANCEGATE;
tagToItemType["MULTIADVANCEGATE"] = IT_MULTIADVANCEGATE;
tagToItemType["MONSTERGATE"] = IT_MONSTERGATE;
tagToItemType["RACEGATE"] = IT_RACEGATE;
tagToItemType["DAMAGEOBJECT"] = IT_DAMAGEOBJECT;
tagToItemType["TRASHCONT"] = IT_TRASHCONT;
tagToItemType["SOUNDOBJECT"] = IT_SOUNDOBJECT;
tagToItemType["MAPCHANGEOBJECT"] = IT_MAPCHANGEOBJECT;
tagToItemType["WORLDCHANGEGATE"] = IT_WORLDCHANGEGATE;
tagToItemType["MORPHOBJECT"] = IT_MORPHOBJECT;
tagToItemType["UNMORPHOBJECT"] = IT_UNMORPHOBJECT;
tagToItemType["ZEROKILLSGATE"] = IT_ZEROKILLSGATE;
tagToItemType["PLANK"] = IT_PLANK;
tagToItemType["FIREWORKSWAND"] = IT_FIREWORKSWAND;
tagToItemType["ESCORTNPCSPAWNER"] = IT_ESCORTNPCSPAWNER;
tagToItemType["RENAMEDEED"] = IT_RENAMEDEED;
tagToItemType["TILLER"] = IT_TILLER;
tagToItemType["GUILDSTONE"] = IT_GUILDSTONE;
tagToItemType["HOUSESIGN"] = IT_HOUSESIGN;
tagToItemType["METALREPAIRTOOL"] = IT_METALREPAIRTOOL;
tagToItemType["FORGE"] = IT_FORGE;
tagToItemType["DYE"] = IT_DYE;
tagToItemType["DYEVAT"] = IT_DYEVAT;
tagToItemType["MODELMULTI"] = IT_MODELMULTI;
tagToItemType["PLAYERVENDORDEED"] = IT_PLAYERVENDORDEED;
tagToItemType["SMITHYTOOL"] = IT_SMITHYTOOL;
tagToItemType["MININGTOOL"] = IT_MININGTOOL;
tagToItemType["EMPTYVIAL"] = IT_EMPTYVIAL;
tagToItemType["CANNONBALL"] = IT_CANNONBALL;
tagToItemType["ORE"] = IT_ORE;
tagToItemType["MESSAGEBOARD"] = IT_MESSAGEBOARD;
tagToItemType["MAGICSTATUE"] = IT_MAGICSTATUE;
tagToItemType["GUILLOTINE"] = IT_GUILLOTINE;
tagToItemType["FISHINGPOLE"] = IT_FISHINGPOLE;
tagToItemType["CLOCK"] = IT_CLOCK;
tagToItemType["SEXTANT"] = IT_SEXTANT;
tagToItemType["HAIRDYE"] = IT_HAIRDYE;
}
ItemTypes FindItemTypeFromTag( const std::string &strToFind )
{
if( tagToItemType.empty() ) // if we haven't built our array yet
InitTagToItemType();
std::map< std::string, ItemTypes >::const_iterator toFind = tagToItemType.find( oldstrutil::upper(strToFind) );
if( toFind != tagToItemType.end() )
return toFind->second;
return IT_COUNT;
}
std::map< UI16, ItemTypes > idToItemType;
void InitIDToItemType( void )
{
ScriptSection *Itemtypes = FileLookup->FindEntry( "ITEMTYPES", items_def );
if( Itemtypes == nullptr )
return;
SI32 sectionCount;
std::string data;
ItemTypes iType = IT_COUNT;
for( std::string tag = Itemtypes->First(); !Itemtypes->AtEnd(); tag = Itemtypes->Next() )
{
data = Itemtypes->GrabData();
auto comma_secs = oldstrutil::sections( data, "," );
iType = FindItemTypeFromTag( tag );
if( iType != IT_COUNT )
{
sectionCount = static_cast<SI32>(comma_secs.size() - 1);
if( sectionCount != 0 )
{
for( SI32 i = 0; i <= sectionCount; i++ )
{
idToItemType[ oldstrutil::value<std::uint16_t>(oldstrutil::extractSection(data, ",", i, i ), 16 )] = iType;
}
}
else
{
idToItemType[static_cast<UI16>(std::stoul(data, nullptr, 16))] = iType;
}
}
}
}
ItemTypes FindItemTypeFromID( UI16 idToFind )
{
if( idToItemType.empty() ) // if we haven't built our array yet
InitIDToItemType();
std::map< UI16, ItemTypes >::const_iterator toFind = idToItemType.find( idToFind );
if( toFind != idToItemType.end() )
return toFind->second;
return IT_COUNT;
}
ItemTypes findItemType( CItem *i )
{
ItemTypes iType = i->GetType();
switch( iType )
{
case IT_NOTYPE:
case IT_COUNT:
iType = FindItemTypeFromID( i->GetID() ); break;
default: break;
}
return iType;
}
//o-----------------------------------------------------------------------------------------------o
//| Function - bool ItemIsUsable( CSocket *tSock, CChar *ourChar, CItem *iUsed, ItemTypes iType )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Checks if item is usable and if player can actually use it
//o-----------------------------------------------------------------------------------------------o
bool ItemIsUsable( CSocket *tSock, CChar *ourChar, CItem *iUsed, ItemTypes iType )
{
if( ourChar->IsDead() && iType != IT_PLANK && iType != IT_HOUSESIGN )
{
if( iType == IT_RESURRECTOBJECT ) // Check for a resurrect item type
{
NpcResurrectTarget( ourChar );
tSock->sysmessage( 390 ); // You have been resurrected.
}
else // If it's not a ressurect item, and you're dead, forget it
tSock->sysmessage( 392 ); // You are dead and cannot do that.
return false;
}
// Range check for double clicking on items
if( iType != IT_PLANK && iType != IT_TILLER && iType != IT_HOUSESIGN )
{
bool canUse = checkItemRange( ourChar, iUsed );
if( canUse )
canUse = (iType == IT_DOOR || iType == IT_LOCKEDDOOR || checkItemLineOfSight( ourChar, iUsed ) );
if( !canUse )
{
tSock->sysmessage( 389 ); // That is too far away and you cannot reach it.
return false;
}
}
if( ourChar->GetCommandLevel() < CL_CNS )
{
if( ( tSock->GetTimer( tPC_OBJDELAY ) >= cwmWorldState->GetUICurrentTime() || cwmWorldState->GetOverflow() ) )
{
if( !tSock->ObjDelayMsgShown() )
{
tSock->sysmessage( 386 ); // You must wait to perform another action.
tSock->ObjDelayMsgShown( true );
}
return false;
}
tSock->SetTimer( tPC_OBJDELAY, cwmWorldState->ServerData()->BuildSystemTimeValue( tSERVER_OBJECTUSAGE ) );
tSock->ObjDelayMsgShown( false );
CChar *iChar = nullptr;
if( iUsed->GetCont() != nullptr )
{
iChar = FindItemOwner( iUsed );
if( ValidateObject( iChar ) && iChar != ourChar &&
!( iUsed->IsContType() && cwmWorldState->ServerData()->RogueStatus() ) )
{
tSock->sysmessage( 387 ); // You cannot use items equipped by other players.
return false;
}
}
// Check if item is in a house?
else if( iType != IT_DOOR && iType != IT_LOCKEDDOOR && iType != IT_PLANK && iType != IT_HOUSESIGN && iType != IT_TILLER )
{
if( iUsed->GetMultiObj() != nullptr && iUsed->GetMultiObj() != ourChar->GetMultiObj() )
{
tSock->sysmessage( 389 ); // That is too far away and you cannot reach it.
return false;
}
}
iChar = iUsed->GetOwnerObj();
if( iUsed->isCorpse() )
{
if( cwmWorldState->ServerData()->LootingIsCrime() )
{
bool willCrim = false;
if( ValidateObject( iChar ) )
{
// if the corpse is from an innocent player, and is not our own corpse if( otherCheck
// and if the corpse is not from an enemy/allied guild && guildCheck
// and if the races are not allied/enemy && raceCheck )
willCrim = WillResultInCriminal( ourChar, iChar );
}
else
willCrim = ( (iUsed->GetTempVar( CITV_MOREZ )&0x04) == 0x04 );
if( willCrim )
criminal( ourChar );
}
if( ValidateObject( iChar ) )
{
CTownRegion *itemTownRegion = iUsed->GetRegion();
if( !itemTownRegion->IsGuarded() && !itemTownRegion->IsSafeZone() )
{
if( iChar->IsGuarded() ) // Is the corpse being guarded?
Combat->petGuardAttack( ourChar, iChar, iChar );
else if( iUsed->isGuarded() )
Combat->petGuardAttack( ourChar, iChar, iUsed );
}
}
}
else if( iUsed->isGuarded() )
{
CMultiObj *multi = iUsed->GetMultiObj();
if( ValidateObject( multi ) )
{
CChar *multiOwner = multi->GetOwnerObj();
if( ValidateObject( multiOwner ) && multiOwner != ourChar )
Combat->petGuardAttack( ourChar, multiOwner, iUsed );
}
else
{
CTownRegion *itemTownRegion = iUsed->GetRegion();
if( !itemTownRegion->IsGuarded() && !itemTownRegion->IsSafeZone() )
{
if( ValidateObject( iChar ))
Combat->petGuardAttack( ourChar, iChar, iUsed );
}
}
}
}
return true;
}
//o-----------------------------------------------------------------------------------------------o
//| Function - bool CPIDblClick::Handle( void )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Player double clicks on a character or item
//| Changes - 09/22/2002 - Removed piece of code which is not needed. Cooking of raw
//| fish shouldn't produce cooked ribs, people can cook the fish after filleting with a knife.
//o-----------------------------------------------------------------------------------------------o
bool CPIDblClick::Handle( void )
{
CChar *ourChar = tSock->CurrcharObj();
ourChar->BreakConcentration( tSock );
UI08 a1 = tSock->GetByte( 1 );
bool keyboard = ( (a1 & 0x80) == 0x80 );
if( keyboard )
objectID -= 0x80000000;
if( objectID < BASEITEMSERIAL )
{
handleCharDoubleClick( tSock, objectID, keyboard );
return true;
}
// Begin Items / Guildstones Section
CItem *iUsed = calcItemObjFromSer( objectID );
if( !ValidateObject( iUsed ) )
return true;
ItemTypes iType = findItemType( iUsed );
bool scriptExecuted = false;
std::vector<UI16> scriptTriggers = iUsed->GetScriptTriggers();
for( auto scriptTrig : scriptTriggers )
{
// First loop through all scripts, checking for OnUseUnChecked events, which should run before
// item usage check is performed
cScript *toExecute = JSMapping->GetScript( scriptTrig );
if( toExecute != nullptr ) // Was a script found?
{
// If retVal is -1, event doesn't exist in script
// If retVal is 0, event exists, but returned false/0, and handles item usage. Don't proceed with hard code (or other scripts!)
// If retVal is 1, event exists, proceed with hard code/other scripts
SI08 retVal = toExecute->OnUseUnChecked( ourChar, iUsed );
if( retVal == 1 )
{
// Event exists, and returned 1 - proceed with other scripts/hard code
scriptExecuted = true;
}
else if( retVal == 0 )
{
// Event exists, and returned 0 - script handles usage, don't continue!
return true;
}
}
}
bool itemUsageCheckComplete = false;
// Then loop through all scripts again, checking for OnUseChecked event - but first run item usage check
// once to make sure player can actually use item!
// If it hasn't been done at least once already, run the usual item-usage checks
if( !itemUsageCheckComplete )
{
if( !ItemIsUsable( tSock, ourChar, iUsed, iType ) )
{
return true;
}
// If item is disabled, don't continue
if( iUsed->isDisabled() )
{
tSock->sysmessage( 394 ); // You cannot do that at the moment.
return true;
}
// Item-usage check completed!
itemUsageCheckComplete = true;
}
for( auto scriptTrig : scriptTriggers )
{
cScript *toExecute = JSMapping->GetScript( scriptTrig );
if( toExecute != nullptr )
{
// Check if OnUseChecked event is present in script
SI08 retVal = toExecute->OnUseChecked( ourChar, iUsed );
if( retVal == 1 )
{
// Event exists, and returned 1 - proceed with other scripts/hard code
scriptExecuted = true;
}
else if( retVal == 0 )
{
// Script returned 0 - don't continue, usage is handled in script!
return true;
}
}
}
// If no scripts were found, or if no script contained onUse events, proceed with envoke stuff
if( scriptTriggers.size() == 0 || !scriptExecuted )
{
UI16 itemID = iUsed->GetID();
cScript *toExecute = nullptr;
UI16 envTrig = 0;
if( JSMapping->GetEnvokeByType()->Check( static_cast<UI16>(iType) ) )
{
// Get script to run by item type
envTrig = JSMapping->GetEnvokeByType()->GetScript( static_cast<UI16>(iType) );
toExecute = JSMapping->GetScript( envTrig );
}
else if( JSMapping->GetEnvokeByID()->Check( itemID ) )
{
// Get script to run by item ID
envTrig = JSMapping->GetEnvokeByID()->GetScript( itemID );
toExecute = JSMapping->GetScript( envTrig );
}
// Check for the onUse events in envoke scripts!
if( toExecute != nullptr )
{
if( toExecute->OnUseUnChecked( ourChar, iUsed ) == 0 )
return true;
if( toExecute->OnUseChecked( ourChar, iUsed ) == 0 )
return true;
}
}
if( handleDoubleClickTypes( tSock, ourChar, iUsed, iType ) )
return true;
tSock->sysmessage( 486 );
return true;
}
//o-----------------------------------------------------------------------------------------------o
//| Function - const char *AppendData( CItem *i, std::string currentName )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Add data onto the end of the string in singleclick() based on an items type
//| This is used by legacy singleclick names that don't show tooltips
//o-----------------------------------------------------------------------------------------------o
const char *AppendData( CSocket *s, CItem *i, std::string &currentName )
{
std::string dataToAdd;
switch( i->GetType() )
{
case IT_CONTAINER:
case IT_SPAWNCONT:
case IT_UNLOCKABLESPAWNCONT:
dataToAdd = std::string(" (") + oldstrutil::number( (SI32)i->GetContainsList()->Num() ) + std::string(" items, ");
dataToAdd += oldstrutil::number( ( i->GetWeight() / 100 ) ) + std::string(" stones)");
break;
case IT_LOCKEDCONTAINER: // containers
case IT_LOCKEDSPAWNCONT: // spawn containers
dataToAdd = std::string(" (") + oldstrutil::number( (SI32)i->GetContainsList()->Num() ) + std::string(" items, ");
dataToAdd += oldstrutil::number( ( i->GetWeight() / 100 ) ) + std::string(" stones) " + Dictionary->GetEntry( 9050, s->Language() )); // [Locked]
break;
case IT_LOCKEDDOOR:
dataToAdd = " " + Dictionary->GetEntry( 9050 ); // [Locked]
break;
case IT_RECALLRUNE:
case IT_GATE:
case IT_OBJTELEPORTER:
{
CTownRegion *newRegion = calcRegionFromXY( static_cast<SI16>(i->GetTempVar( CITV_MOREX )), static_cast<SI16>(i->GetTempVar( CITV_MOREY )), i->WorldNumber(), i->GetInstanceID() );
dataToAdd = std::string(" (") + newRegion->GetName() + std::string(")");
break;
}
default:
break;
}
if( i->isGuarded() )
{
CTownRegion *itemTownRegion = i->GetRegion();
if( !itemTownRegion->IsGuarded() && !itemTownRegion->IsSafeZone() )
{
dataToAdd += " " + Dictionary->GetEntry( 9051, s->Language() ); // [Guarded]
}
}
if( i->isNewbie() )
{
s->objMessage( Dictionary->GetEntry( 9055, s->Language() ), i ); // [Blessed]
}
std::string name2( i->GetName2() );
if( name2 != "#" && name2 != "" )
{
s->objMessage( Dictionary->GetEntry( 9402, s->Language() ), i ); // [Unidentified]
}
currentName += dataToAdd;
// Question: Do we put the creator thing here, saves some redundancy a bit later
return currentName.c_str();
}
//o-----------------------------------------------------------------------------------------------o
//| Function - bool CPISingleClick::Handle( void )
//o-----------------------------------------------------------------------------------------------o
//| Purpose - Called when an item or character is single-clicked (also used for AllNames macro)
//o-----------------------------------------------------------------------------------------------o
bool CPISingleClick::Handle( void )
{
if( objectID == INVALIDSERIAL ) // invalid
return true;
else if( objectID < BASEITEMSERIAL )
{
// Begin chars/npcs section
CChar *c = calcCharObjFromSer( objectID );
if( ValidateObject( c ) )
{
std::vector<UI16> scriptTriggers = c->GetScriptTriggers();
for( auto scriptTrig : scriptTriggers )
{
cScript *toExecute = JSMapping->GetScript( scriptTrig );
if( toExecute != nullptr )
{
// If script returns true/1, don't show hard-coded name, and don't run additional onClick events
if( toExecute->OnClick( tSock, c ) == 1 )
{
return true;
}
}
}
tSock->ShowCharName( c, false );
}
//End chars/npcs section
return true;
}
std::string temp2 ;
std::string realname;
CChar *mChar = tSock->CurrcharObj();
//Begin items section
UI08 a1 = tSock->GetByte( 1 );
UI08 a2 = tSock->GetByte( 2 );
UI08 a3 = tSock->GetByte( 3 );
UI08 a4 = tSock->GetByte( 4 );
CItem *i = calcItemObjFromSer( objectID );
if( !ValidateObject( i ) ) // invalid item
return true;
// October 6, 2002 - Added support for the onClick event
std::vector<UI16> scriptTriggers = i->GetScriptTriggers();
for( auto scriptTrig : scriptTriggers )
{
cScript *toExecute = JSMapping->GetScript( scriptTrig );
if( toExecute != nullptr )
{
// If script returns true/0, don't show hard-coded name, and don't run additional onClick events
if( toExecute->OnClick( tSock, i ) == 1 )
{
return true;
}
}
}
if( mChar->GetSingClickSer() )
tSock->objMessage( 1737, i, 0.0f, 0x03B2, a1, a2, a3, a4 );
UI16 getAmount = i->GetAmount();
if( i->GetCont() != nullptr && i->GetContSerial() >= BASEITEMSERIAL )
{
CChar *w = FindItemOwner( static_cast<CItem *>( i->GetCont() ));
if( ValidateObject( w ) )
{
if( w->GetNPCAiType() == AI_PLAYERVENDOR )
{
std::string temp ;
if( i->GetCreator() != INVALIDSERIAL && i->GetMadeWith() > 0 )
{
CChar *mCreator = calcCharObjFromSer( i->GetCreator() );
if( ValidateObject( mCreator ) )
{
std::string creatorName = getNpcDictName( mCreator, tSock );
temp2 = oldstrutil::format( "%s %s by %s", i->GetDesc().c_str(), cwmWorldState->skill[i->GetMadeWith()-1].madeword.c_str(), creatorName.c_str() );
}
else
{
temp2 = i->GetDesc();
}
}
else{
temp2= i->GetDesc();
}
temp = temp2 + std::string(" at ")+std::to_string(i->GetBuyValue())+std::string("gp");
tSock->objMessage( AppendData( tSock, i, temp ), i );
return true;
}
}
}
#if defined( _MSC_VER )
#pragma todo( "We need to update this to use getTileName almost exclusively, for plurality" )
#endif
if( i->GetNameRequest( tSock->CurrcharObj() )[0] != '#' )
{
if( i->GetID() == 0x0ED5 )//guildstone
realname = oldstrutil::format( Dictionary->GetEntry( 101, tSock->Language() ).c_str(), i->GetNameRequest( tSock->CurrcharObj() ).c_str() );
if( !i->isPileable() || getAmount == 1 )
{
if( mChar->IsGM() && !i->isCorpse() && getAmount > 1 )
realname = oldstrutil::format( "%s (%u)", i->GetNameRequest( tSock->CurrcharObj() ).c_str(), getAmount );
else
realname = i->GetNameRequest( tSock->CurrcharObj() );
}
else
realname = oldstrutil::format( "%u %ss", getAmount, i->GetNameRequest( tSock->CurrcharObj() ).c_str() );
}
else
{
getTileName( (*i), realname );
if( i->GetAmount() > 1 )
realname = std::to_string( getAmount ) + std::string(" ") + realname;
}
if( i->GetType() == IT_MAGICWAND )
{
std::string name2( i->GetName2() );
if( name2 == "#" || name2 == "" )
{
realname += oldstrutil::format( Dictionary->GetEntry( 9404, tSock->Language() ), i->GetTempVar( CITV_MOREZ )); // with %d charges
}
else
{
realname += " (unidentified)";
}
}
else if( i->IsContType() )
{
SI32 iCount = 0;
if( i->isCorpse() )
{
iCount = static_cast<SI32>(GetTotalItemCount( i ));
}
else
{
iCount = static_cast<SI32>(i->GetContainsList()->Num());
}
realname += oldstrutil::format( ", (%u items, %u stones)", iCount, (i->GetWeight()/100) );
}
if( i->GetCreator() != INVALIDSERIAL && i->GetMadeWith() > 0 )
{
CChar *mCreator = calcCharObjFromSer( i->GetCreator() );
if( ValidateObject( mCreator ) )
{
std::string creatorName = getNpcDictName( mCreator, tSock );
temp2 = oldstrutil::format( "%s %s by %s", realname.c_str(), cwmWorldState->skill[i->GetMadeWith()-1].madeword.c_str(), creatorName.c_str() );
}
else
{
temp2 = realname;
}
}
else{
temp2 = realname;
}
tSock->objMessage( temp2, i );
if( i->IsLockedDown() )
{
auto iMultiObj = i->GetMultiObj();
if( ValidateObject( iMultiObj ) && iMultiObj->IsSecureContainer( i ) )
tSock->objMessage( Dictionary->GetEntry( 9052, tSock->Language() ), i ); // [locked down & secure]
else
tSock->objMessage( Dictionary->GetEntry( 9053, tSock->Language() ), i ); // [locked down]
}
if( i->isGuarded() )
{
CTownRegion *itemTownRegion = i->GetRegion();
if( !itemTownRegion->IsGuarded() && !itemTownRegion->IsSafeZone() )
{
tSock->objMessage( Dictionary->GetEntry( 9051, tSock->Language() ), i ); // [Guarded]
}
}
if( i->isNewbie() )
{
tSock->objMessage( Dictionary->GetEntry( 9055, tSock->Language() ), i ); // [Blessed]
}
return true;
}