uox3/source/cPlayerAction.cpp
2023-04-07 11:17:51 -04:00

3881 lines
116 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"
#include "strutil.hpp"
using namespace std::string_literals;
void SendTradeStatus( CItem *cont1, CItem *cont2 );
CItem * StartTrade( CSocket *mSock, CChar *i );
bool CheckItemRange( CChar *mChar, CItem *i );
void DoHouseTarget( CSocket *mSock, UI16 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 - Bounce()
//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 - PickupBounce()
//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 - DoStacking()
//| 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, [[maybe_unused]] 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 - AutoStack()
//| 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
auto AutoStack( CSocket *mSock, CItem *iToStack, CItem *iPack ) ->CItem *
{
CChar *mChar = nullptr;
if( mSock )
{
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();
auto ipCont = iPack->GetContainsList();
for( const auto &stack :ipCont->collection() )
{
if( ValidateObject( stack ))
{
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 )
{
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 - CPIGetItem::Handle()
//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->SendTargetCursor( 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 ))
{
if( iOwner->CanBeObjType( OT_CHAR ) && static_cast<CChar *>( iOwner )->GetNpcAiType() == AI_PLAYERVENDOR )
{
tSock->SysMessage( 9175 ); // If you'd like to purchase an item, just say so.
}
else
{
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
MakeCriminal( 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 )
{
// Remove item from mapregion it was in before being picked up
MapRegion->RemoveItem( i );
// Add item to mapregion of character who picked up item instead
MapRegion->AddItem( 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( iCont != nullptr )
{
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 - CPIEquipItem::Handle()
//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->SendTargetCursor( 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 - DropOnPC()
//| 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 - IsOnFoodList()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Check if a particular item is on an NPC's list of accepted foods
//o------------------------------------------------------------------------------------------------o
auto IsOnFoodList( const std::string& sFoodList, const UI16 sItemId ) -> bool
{
auto doesEat = false;
const std::string sect = "FOODLIST "s + sFoodList;
auto FoodList = FileLookup->FindEntry( sect, items_def );
if( FoodList )
{
for( const auto &sec : FoodList->collection() )
{
auto tag = sec->tag;
if( !tag.empty() )
{
if( util::upper( tag ) == "FOODLIST" )
{
doesEat = IsOnFoodList( sec->data, sItemId );
}
else if( sItemId == static_cast<UI16>( std::stoul( tag, nullptr, 0 )))
{
doesEat = true;
}
}
if( doesEat ) // We found a valid food, let's stop looking
break;
}
}
return doesEat;
}
//o------------------------------------------------------------------------------------------------o
//| Function - DropOnNPC()
//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, ACT_ANIMAL_EAT, 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 ); // That pet is already stuffed, it can't eat more.
}
}
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 ); // Thank thee kindly, but I have done nothing to warrant a gift.
}
}
else if( i->GetId() == 0x0EED ) // They gave the NPC gold
{
targNPC->TextMessage( mSock, 1198, TALK, false ); // I thank thee for thy payment. That should give thee a good start on thy way. Farewell!
UI08 trainedIn = targNPC->GetTrainingPlayerIn();
UI16 oldskill = mChar->GetBaseSkill( trainedIn );
mChar->SetBaseSkill( static_cast<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 ); // I am sorry, but I can only accept gold.
}
}
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 - DropOnChar()
//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 ); // That person can not possibly hold more weight
if( mSock->PickupSpot() == PL_OTHERPACK || mSock->PickupSpot() == PL_GROUND )
{
Weight->SubtractItemWeight( mChar, i );
}
Bounce( mSock, i );
return false;
}
if( !targChar->IsNpc() )
{
// Drop on player character
return DropOnPC( mSock, mChar, targChar, i );
}
// Drop on NPC
return DropOnNPC( mSock, mChar, targChar, i );
}
return false;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CheckForValidDropLocation()
//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 for a valid surface to drop item on first - then check for blocking tiles after
// If done the other way, the valid surface will override the blocking tiles!
// First, check for a static surface to drop item on
if( !Map->CheckStaticFlag( x, y, z, nChar->WorldNumber(), TF_SURFACE, false ))
{
// Nowhere static to put item? Check dynamic tiles for surface!
if( !Map->CheckDynamicFlag( x, y, z, nChar->WorldNumber(), nChar->GetInstanceId(), TF_SURFACE ))
{
// No static OR dynamic surface was found to place item? Check if map itself blocks the placement
dropLocationBlocked = Map->DoesMapBlock( x, y, z, nChar->WorldNumber(), true, false, false, false );
}
}
if( !dropLocationBlocked )
{
// Some kind of valid surface was found. But is it blocked by...
if( Map->CheckStaticFlag( x, y, z, nChar->WorldNumber(), TF_BLOCKING, false ) || Map->CheckStaticFlag( x, y, z, nChar->WorldNumber(), TF_ROOF, false ))
{ // ...static items?
dropLocationBlocked = true;
}
else if( Map->CheckDynamicFlag( x, y, z, nChar->WorldNumber(), nChar->GetInstanceId(), TF_BLOCKING ) || Map->CheckDynamicFlag( x, y, z, nChar->WorldNumber(), nChar->GetInstanceId(), TF_ROOF ) )
{ // No? What about dynamic items?
dropLocationBlocked = true;
}
}
}
// 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 - Drop()
//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(), 14 );
if( newZ == ILLEGAL_Z || newZ < z || newZ > z + 14 )
{
// No valid static elevation found, use dynamic elevation instead
newZ = Map->DynamicElevation( x, y, z, nChar->WorldNumber(), nChar->GetInstanceId(), 14 );
if( newZ < z || newZ > z + 14 )
{
// 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(), nChar->GetInstanceId(), 14 );
newZ = (( dynZ >= z && dynZ <= z + 14 ) ? dynZ : newZ );
}
if( newZ + tile.Height() > ( z + 14 ) )
{
// Item is too tall - can't drop it this high up
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;
}
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 - DropOnTradeWindow()
//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 - ValidateLockdownAccess()
//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 - DropOnSpellBook()
//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 - DropOnStack()
//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 - HandleAutoStack()
//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 - DropOnContainer()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Called when item is dropped on a container
//o------------------------------------------------------------------------------------------------o
bool DropOnContainer( CSocket& mSock, CChar& mChar, CItem& droppedOn, CItem& iDropped, [[maybe_unused]] 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 )
{
// Clear old values, if item have 'em
iDropped.SetDesc( "" );
iDropped.SetVendorPrice( 0 );
// Ask player to enter price for item, or 0 to mark it as 'Not for Sale'
mChar.SetSpeechMode( 3 );
mChar.SetSpeechItem( &iDropped );
mSock.SysMessage( 1207 ); // Set a price for this item.
}
else
{
if( mSock.PickupSpot() == PL_OTHERPACK || mSock.PickupSpot() == PL_GROUND )
{
Weight->SubtractItemWeight( &mChar, &iDropped );
}
contOwner->TextMessage( &mSock, 9176, TALK, false ); // I can only accept items from the shop owner.
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 - DropOnItem()
//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 - CPIDropItem::Handle()
//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 - BestSkill()
//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 - GetSkillProwessTitle()
//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 - GetFameTitle()
//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( cwmWorldState->ServerData()->ShowRaceWithName() && 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 = util::format( Dictionary->GetEntry( 1177 ), Races->Name( p->GetRace() ).c_str() ) + std::string( " " );
}
else
{
fameTitle = util::format( Dictionary->GetEntry( 1178 ), Races->Name( p->GetRace() ).c_str() ) + std::string( " " );
}
}
else if( p->GetId( 2 ) == 0x91 )
{
fameTitle = util::format( Dictionary->GetEntry( 1179 ), theTitle.c_str() ) + std::string( " " );
}
else
{
fameTitle = util::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 = util::strip( theTitle, "//" )).empty() )
{
fameTitle = util::format( Dictionary->GetEntry( 1182 ), theTitle.c_str() );
}
}
}
}
//o------------------------------------------------------------------------------------------------o
//| Function - PaperDoll()
//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 race, if enabled
if( cwmWorldState->ServerData()->ShowRaceInPaperdoll() && pdoll->GetRace() != 0 && pdoll->GetRace() != 65535 )
{
tempstr += " ("s + Races->Name( pdoll->GetRace() ) + ")";
}
// 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 = util::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 = util::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
// Append race, if enabled
if( cwmWorldState->ServerData()->ShowRaceInPaperdoll() && pdoll->GetRace() != 0 && pdoll->GetRace() != 65535 )
{
tempstr += " ("s + Races->Name( pdoll->GetRace() ) + ")";
}
if( pdoll->GetTownTitle() || pdoll->GetTownPriv() == 2 ) // TownTitle
{
if( pdoll->GetTownPriv() == 2 ) // is Mayor
{
tempstr = util::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 - handleCharDoubleClick()
//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 ); // You are dead and cannot do that.
}
else if( mChar->GetId() != mChar->GetOrgId() )
{
mSock->SysMessage( 380 ); // You cannot ride anything under polymorph effect.
}
else
{
MountCreature( mSock, mChar, c );
}
}
else
{
mSock->SysMessage( 382 ); // You need to get closer.
}
}
else
{
mSock->SysMessage( 1214 ); // You don't own that creature.
}
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 ); // You are dead and cannot do that.
}
else if( !ObjInRange( mChar, c, DIST_NEARBY ))
{
mSock->SysMessage( 382 ); // You need to get closer.
}
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 ); // That is not your beast of burden!
}
}
}
return;
}
else if( c->GetNpcAiType() == AI_PLAYERVENDOR ) // PlayerVendors
{
c->TextMessage( mSock, 385, TALK, false ); // Take a look at my goods.
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 - HandleDoubleClickTypes()
//| 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 ); // You are too far away!
}
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 ); // This item is locked.
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 ); // If you wish to open a spellbook, it must be equipped or in your main backpack.
}
}
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 ); // That rune is not yet marked!
}
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 ); // Enter new rune name.
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 ))
{
auto plankStatus = iUsed->GetTag( "plankLocked" );
if( plankStatus.m_IntValue == 1 ) // Is plank locked?
{
auto iMulti = iUsed->GetMultiObj();
auto mMulti = mChar->GetMultiObj();
if( ValidateObject( iMulti ) && ValidateObject( mMulti ) && iMulti == mMulti )
{
// Allow opening plank for disembarking purposes if player is onboard boat
switch( iUsed->GetId() )
{
// Open the plank visually
case 0x3EE9: iUsed->SetId( 0x3E84 ); break;
case 0x3EB1: iUsed->SetId( 0x3ED5 ); break;
case 0x3EB2: iUsed->SetId( 0x3ED4 ); break;
case 0x3E8A: iUsed->SetId( 0x3E89 ); break;
case 0x3E85: iUsed->SetId( 0x3E84 ); break;
// Exit from the open (but locked) plank
case 0x3E84: [[fallthrough]];
case 0x3ED5: [[fallthrough]];
case 0x3ED4: [[fallthrough]];
case 0x3E89: PlankStuff( mSock, iUsed ); break;
default: return true;
}
// Start a timer to auto-close the plank
mSock->SysMessage( 9144 ); // You open the plank, though it's still locked.
iUsed->SetTempTimer( BuildTimeValue( static_cast<R32>( 5 )));
}
else
{
// Plank is locked! Tough luck
mSock->SysMessage( 398 ); // Locked
}
}
else
{
// Plank is unlocked! Open the plank, or use it to disembark
switch( iUsed->GetId() )
{
case 0x3EE9: iUsed->SetId( 0x3E84 ); break;
case 0x3EB1: iUsed->SetId( 0x3ED5 ); break;
case 0x3EB2: iUsed->SetId( 0x3ED4 ); break;
case 0x3E8A: iUsed->SetId( 0x3E89 ); break;
case 0x3E85: iUsed->SetId( 0x3E84 ); break;
default: PlankStuff( mSock, iUsed ); return true;;
}
mSock->SysMessage( 2031 ); // You open the plank
}
}
else
{
mSock->SysMessage( 399 ); // You can't reach that!
}
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 ); // That is out of charges!
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 ); // Enter your new name.
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 )))
{
mSock->SysMessage( 1984 ); // You are not a member of this guild!
}
else if( 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 ); // You already belong to a guild.
}
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->SendTargetCursor( 0, TARGET_REPAIRMETAL, 0, 485 ); // What do we wish to repair?
return true;
case IT_FORGE: // Forges
mSock->TempObj( iUsed );
mSock->SendTargetCursor( 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->SendTargetCursor( 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->SendTargetCursor( 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<UI16>( 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->SendTargetCursor( 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->SendTargetCursor( 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->SendTargetCursor( 0, TARGET_VIAL, 0, 447 );
}
else
{
mSock->SysMessage( 448 ); // The vial is not in your pack!
}
}
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->SendTargetCursor( 0, TARGET_SMELTORE, 0, 460 ); // Select the forge on which to smelt the ore.
}
else
{
mSock->SendTargetCursor( 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 ); // You are too far away.
}
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 ); // You failed to use this statue.
}
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 ); // You failed to use this.
}
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( util::upper( strToFind ));
if( toFind != tagToItemType.end() )
return toFind->second;
return IT_COUNT;
}
std::map<UI16, ItemTypes> idToItemType;
//o------------------------------------------------------------------------------------------------o
//| Function - InitIdToItemType()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Load in and map IDs to item types based on entries in itemtypes.dfn
//o------------------------------------------------------------------------------------------------o
auto InitIdToItemType() -> void
{
auto Itemtypes = FileLookup->FindEntry( "ITEMTYPES", items_def );
if( Itemtypes )
{
SI32 sectionCount;
ItemTypes iType = IT_COUNT;
for( const auto &sec : Itemtypes->collection() )
{
auto tag = sec->tag;
auto data = sec->data;
auto comma_secs = util::parse( 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[ util::ston<UI16>( oldstrutil::extractSection( data, ",", i, i ), 16 )] = iType;
}
}
else
{
idToItemType[static_cast<UI16>( std::stoul( data, nullptr, 16 ))] = iType;
}
}
}
}
}
//o------------------------------------------------------------------------------------------------o
//| Function - FindItemTypeFromId()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Find an item's type based on its ID
//o------------------------------------------------------------------------------------------------o
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;
}
//o------------------------------------------------------------------------------------------------o
//| Function - FindItemType()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Find an item's type, or try to find it's type from its ID if no type is specified
//o------------------------------------------------------------------------------------------------o
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 - ItemIsUsable()
//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 )
{
MakeCriminal( 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 - CPIDblClick::Handle()
//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 ); // You can't think of a way to use that item.
return true;
}
//o------------------------------------------------------------------------------------------------o
//| Function - AppendData()
//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( " (" ) + util::ntos( static_cast<SI32>( i->GetContainsList()->Num() )) + std::string( " items, " );
dataToAdd += util::ntos(( i->GetWeight() / 100 )) + std::string( " stones)" );
break;
case IT_LOCKEDCONTAINER: // containers
case IT_LOCKEDSPAWNCONT: // spawn containers
dataToAdd = std::string( " (" ) + util::ntos( static_cast<SI32>( i->GetContainsList()->Num() )) + std::string( " items, " );
dataToAdd += util::ntos(( 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->IsLockedDown() )
{
auto iMultiObj = i->GetMultiObj();
if( ValidateObject( iMultiObj ) && iMultiObj->IsSecureContainer( i ))
{
dataToAdd += Dictionary->GetEntry( 9052, s->Language() ); // [locked down & secure]
}
else
{
dataToAdd += Dictionary->GetEntry( 9053, s->Language() ); // [locked down]
}
}
if( i->IsGuarded() )
{
CTownRegion *itemTownRegion = i->GetRegion();
if( !itemTownRegion->IsGuarded() && !itemTownRegion->IsSafeZone() )
{
dataToAdd += " " + Dictionary->GetEntry( 9051, s->Language() ); // [Guarded]
}
}
if( i->IsNewbie() )
{
dataToAdd += " " + Dictionary->GetEntry( 9055, s->Language() ); // [Blessed]
}
// Is the item a magical item, and unidentified?
if( !i->IsCorpse() && i->GetName2() != "#" && i->GetName2() != "" )
{
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 - CPISingleClick::Handle()
//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;
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;
temp = "Desc: " + i->GetDesc() + std::string( " at " ) + std::to_string( i->GetBuyValue() ) + std::string( "gp" );
tSock->ObjMessage( temp, i );
}
}
}
#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 = util::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 = util::format( "%s (%u)", i->GetNameRequest( tSock->CurrcharObj() ).c_str(), getAmount );
}
else
{
realname = i->GetNameRequest( tSock->CurrcharObj() );
}
}
else
{
realname = util::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 )
{
if( i->GetName2() == "#" || i->GetName2() == "" )
{
realname += util::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 += util::format( ", (%u items, %u stones)", iCount, ( i->GetWeight() / 100 ));
}
// Use item's real name as starting point for final string
temp2 = realname;
if( i->GetCreator() != INVALIDSERIAL )
{
CChar *mCreator = CalcCharObjFromSer( i->GetCreator() );
if( ValidateObject( mCreator ))
{
if( cwmWorldState->ServerData()->RankSystemStatus() && i->GetRank() == 10 )
{
std::string creatorName = GetNpcDictName( mCreator, tSock );
temp2 += util::format( " %s", Dictionary->GetEntry( 9140, tSock->Language() ).c_str() ); // of exceptional quality
if( cwmWorldState->ServerData()->DisplayMakersMark() && i->IsMarkedByMaker() )
{
temp2 += util::format( " %s by %s", cwmWorldState->skill[i->GetMadeWith()-1].madeWord.c_str(), creatorName.c_str() ); // [crafted] by %s
}
}
}
}
tSock->ObjMessage( temp2, i );
std::string itemTags = "";
tSock->ObjMessage( AppendData( tSock, i, itemTags ), i );
return true;
}