uox3/source/vendor.cpp
Xoduz 98596b9f78 Some minor fixes and updates
Updated UOX3 & JS documentation
Added new JS command: 'seeryell [text] // broadcast message to all with seer privs or higher privs
Added new privilege flag for characters:
	0x20000 - Seer (Character is a Seer)
Fixed an issue where Young players could not direct hirelings to attack valid targets (js/npc/ai/hireling.js)
When buying items from NPC shopkeepers worth more than the BUYTHRESHOLD in uox.ini, the player's backpack is first checked for the required amount of gold, before their bankbox is checked. A new message is displayed about any gold withdrawn from player's bank.
When selling items to NPC shopkeepers that are worth more than the BUYTHRESHOLD in uox.ini, the gold is now deposited directly into the player's bankbox, instead of to their backpack. A new message is displayed about any gold deposited to the player's bank.
Fixed an issue with Shopkeeper AI (js/npc/ai/shopkeeper.js) where the NPC would incorrectly assume player was member of the same NPC guild as them, and provide them with an unwarranted bonus to trade prices as a result
Fixed an issue that prevented players from being able to apply poison to weapons (js/skill/poisoning.js)
Fixed incorrect NPC spawn entry in Khaldun spawn region DFNs: spectralarmour -> spectralarmor
Added entries for Orc Caves/Khaldun to GM travel menu (dfndata/items/travelmenu.dfn and travelmenu.bulk.dfn)
Fixed an issue that sometimes prevented admin characters from seeing offline GMs
Added support for reading SKILLCAPSINGLE ini setting with GetServerSetting() JS function
Fixed issue where animal trainers would give players a random ostard when buying a desert ostard (js/npc/pets/static_ostard.js and dfndata/items/misc/mount-statues.dfn)
Added DFN entries for additional ostard-mount-statues (not sold by animal trainer) that can give user a random ostard, forest ostard or frenzied ostard respectively (dfndata/items/misc/mount-statues.dfn)
Player pets are no longer safe from being attacked from monsters. This also addresses an issue with tamed pets being able to freely attack monsters if player gets the monster's attention first and then kites the monster around while pet kills it. Now monster will properly re-focus to nearest attacker (including pets) if original target is too far away
Fixed incorrect command-levels for GMs for hard-coded commands listed in commands.dfn
Added support for specifying minEra,maxEra value pair for teleport locations in js/teleport.scp. These can be used to define the minimum and maximum era a given teleport entry should be active for. The values for these default to -1, unless otherwise specified. Updated teleport entry format:
	sourceX, sourceY, sourceZ, targX, targY, targZ, [sourceWorld, targWorld[, minEra, maxEra]]
Updated teleport entries for Yew <-> Heartwood to only be active for ML era shards and above (js/teleport.scp)
Updated regions for Moonglow and Skara Brae to properly cover dock areas (dfndata/regions/regions.dfn)
Added missing dictionary entries related to poison/corrosion update
Added some missing details for poisoning skill (js/skill/poisoning.js) and arms lore skill (js/skill/armslore.js)
Added missing details for 'tweak (js/commands/targeting/tweak.js), 'get and 'set (js/commands/targeting/get.js and set.js) commands related to poison charges and poison strength on items
Updated poison strength of poison elementals [poisonele] from 4 to 5 (dfndata/npc/elementals.dfn)
Fixed an issue with Spawner Object Editor where state of "Use Spawnlist" checkbox would not be read correctly if no "Spawner Type" radio buttons were selected (js/server/misc/spawner_editor.js)
2025-07-02 03:11:29 +08:00

743 lines
21 KiB
C++

#include "uox3.h"
#include "commands.h"
#include "cEffects.h"
#include "CPacketSend.h"
#include "classes.h"
#include "townregion.h"
#include "CJSMapping.h"
#include "cScript.h"
#include "Dictionary.h"
#include "cServerDefinitions.h"
#include "ObjectFactory.h"
#include <algorithm>
//o------------------------------------------------------------------------------------------------o
//| Function - CalcValue()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Calculate the value of an item
//o------------------------------------------------------------------------------------------------o
UI32 CalcValue( CItem *i, UI32 value )
{
if( i->GetType() == IT_POTION )
{
UI32 mod = 10;
if( i->GetTempVar( CITV_MOREX ) > 500 )
{
++mod;
}
if( i->GetTempVar( CITV_MOREX ) > 900 )
{
++mod;
}
if( i->GetTempVar( CITV_MOREX ) > 1000 )
{
++mod;
}
if( i->GetTempVar( CITV_MOREZ ) > 1 )
{
mod += ( 3 * ( i->GetTempVar( CITV_MOREZ ) - 1 ));
}
value = ( value * mod ) / 10;
}
if( i->GetRank() > 0 && i->GetRank() < 10 && cwmWorldState->ServerData()->RankSystemStatus() )
{
value = static_cast<UI32>( i->GetRank() * value ) / 10;
}
if( value < 1 )
{
value = 1;
}
if( !i->GetRndValueRate() )
{
i->SetRndValueRate( 0 );
}
if( i->GetRndValueRate() != 0 && cwmWorldState->ServerData()->TradeSystemStatus() )
{
value += static_cast<UI32>( value * i->GetRndValueRate() ) / 1000;
}
if( value < 1 )
{
value = 1;
}
return value;
}
//o------------------------------------------------------------------------------------------------o
//| Function - CalcGoodValue()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Calculate the value of a good
//o------------------------------------------------------------------------------------------------o
UI32 CalcGoodValue( CTownRegion *tReg, CItem *i, UI32 value, bool isSelling )
{
if( tReg == nullptr )
return value;
SI16 good = i->GetGood();
SI32 regvalue = 0;
if( good <= -1 )
{
return value;
}
if( isSelling )
{
regvalue = tReg->GetGoodSell( static_cast<UI08>( i->GetGood() ));
}
else
{
regvalue = tReg->GetGoodBuy( static_cast<UI08>( i->GetGood() ));
}
UI32 x = static_cast<UI32>( value * abs( regvalue )) / 1000;
if( regvalue < 0 )
{
value -= x;
}
else
{
value += x;
}
if( value < 1 )
{
value = 1;
}
return value;
}
bool ApplyItemSection( CItem *applyTo, CScriptSection *toApply, std::string sectionId );
//o------------------------------------------------------------------------------------------------o
//| Function - CPIBuyItem::Handle()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Called when player buys an item from a vendor
//o------------------------------------------------------------------------------------------------o
bool CPIBuyItem::Handle( void )
{
UI16 i;
UI32 totalPlayerGold = 0, totalGoldCost = 0;
bool soldout = false, clear = false;
CChar *mChar = tSock->CurrcharObj();
CItem *p = mChar->GetPackItem();
if( !ValidateObject( p ))
return true;
std::vector<CItem*> bItems;
std::vector<UI08> layer;
std::vector<UI16> amount;
// vector for storing all objects that actually end up in user backpack
std::vector<CItem *> boughtItems;
std::vector<UI16> boughtItemAmounts;
CChar *npc = CalcCharObjFromSer( tSock->GetDWord( 3 ));
UI16 itemTotal = static_cast<UI16>(( tSock->GetWord( 1 ) - 8) / 7 );
if( itemTotal > 511 )
return true;
boughtItems.reserve( itemTotal );
boughtItemAmounts.reserve( itemTotal );
bItems.resize( itemTotal );
amount.resize( itemTotal );
layer.resize( itemTotal );
SI32 baseOffset = 0;
for( i = 0; i < itemTotal; ++i )
{
baseOffset = 7 * i;
layer[i] = tSock->GetByte( 8 + static_cast<size_t>( baseOffset ));
bItems[i] = CalcItemObjFromSer( tSock->GetDWord( 9 + static_cast<size_t>( baseOffset )));
amount[i] = tSock->GetWord( 13 + static_cast<size_t>( baseOffset ));
totalGoldCost += ( amount[i] * ( bItems[i]->GetBuyValue() ));
}
bool takeGoldFromBank = false;
// First look for the required gold in player's backpack
totalPlayerGold = GetItemAmount( mChar, 0x0EED );
if( totalPlayerGold < totalGoldCost )
{
// Not enough gold in backpack, check bank if amount is over threshold
if( totalGoldCost >= static_cast<UI32>( cwmWorldState->ServerData()->BuyThreshold() ))
{
totalPlayerGold = GetBankCount( mChar, 0x0EED );
takeGoldFromBank = true;
}
}
if( mChar->IsGM() || ( totalPlayerGold >= totalGoldCost ))
{
for( i = 0; i < itemTotal; ++i )
{
if( bItems[i]->GetAmount() < amount[i] )
{
soldout = true;
}
// Check if onBuyFromVendor JS event is present for each item being purchased
// If return false or 0 has been returned from the script, halt the purchase
for( auto scriptTrig : bItems[i]->GetScriptTriggers() )
{
cScript *toExecute = JSMapping->GetScript( scriptTrig );
if( toExecute != nullptr )
{
if( toExecute->OnBuyFromVendor( tSock, npc, bItems[i], amount[i] ) == 0 )
{
bItems.clear();
bItems.shrink_to_fit();
return true;
}
}
}
// Also run the event on the vendor itself. If purchase didn't get blocked on the item side, maybe
// vendor has a script with something to say about it instead
for( auto scriptTrig : npc->GetScriptTriggers() )
{
cScript *toExecute = JSMapping->GetScript( scriptTrig );
if( toExecute != nullptr )
{
if( toExecute->OnBuyFromVendor( tSock, npc, bItems[i], amount[i] ) == 0 )
{
bItems.clear();
bItems.shrink_to_fit();
return true;
}
}
}
}
if( soldout )
{
npc->TextMessage( tSock, 1336, TALK, false ); // Alas, I no longer have all those goods in stock. Let me know if there is something else thou wouldst buy.
clear = true;
}
else
{
if( mChar->IsGM() )
{
npc->TextMessage( nullptr, 1337, TALK, 0, mChar->GetName().c_str() ); // Here you are, %s. Someone as special as thee will receive my wares for free of course.
}
else
{
if( totalGoldCost == 1 )
{
npc->TextMessage( nullptr, 1338, TALK, 0, mChar->GetName().c_str(), totalGoldCost ); // Here you are, %s. That will be %d gold coin. I thank thee for thy business.
}
else
{
npc->TextMessage( nullptr, 1339, TALK, 0, mChar->GetName().c_str(), totalGoldCost ); // Here you are, %s. That will be %d gold coins. I thank thee for thy business.
}
Effects->GoldSound( tSock, totalGoldCost );
}
clear = true;
if( !mChar->IsGM() )
{
if( takeGoldFromBank )
{
// Remove total amount of gold spent, from player's bankbox
DeleteBankItem( mChar, totalGoldCost, 0x0EED );
tSock->SysMessage( oldstrutil::format( Dictionary->GetEntry( 2124, tSock->Language() ), totalGoldCost )); // %i gold has been withdrawn from your bank box.
}
else
{
// Remove total amount of gold spent, from player's backpack
DeleteItemAmount( mChar, totalGoldCost, 0x0EED );
}
}
CItem *biTemp;
CItem *iMade = nullptr;
UI16 j;
for( i = 0; i < itemTotal; ++i )
{
biTemp = bItems[i];
iMade = nullptr;
if( biTemp->GetAmount() > amount[i] )
{
if( biTemp->IsPileable() )
{
iMade = Items->DupeItem( tSock, biTemp, amount[i] );
if( iMade != nullptr )
{
iMade->SetCont( p );
iMade->PlaceInPack();
boughtItems.push_back( iMade );
boughtItemAmounts.push_back( amount[i] );
}
}
else
{
for( j = 0; j < amount[i]; ++j )
{
iMade = Items->DupeItem( tSock, biTemp, 1 );
if( iMade != nullptr )
{
CItem * biTempCont = static_cast<CItem *>( biTemp->GetCont() );
if( biTempCont->GetLayer() == 0x1A )
{
// Only do this for new items sold from vendor's sell container
SI16 iMadeHP = iMade->GetHP();
SI16 iMadeMaxHP = static_cast<SI16>( iMade->GetMaxHP() );
if( iMadeMaxHP > iMadeHP )
{
// Randomize the item's HP from HP to MaxHP
iMade->SetHP( static_cast<SI16>( RandomNum( iMadeHP, iMadeMaxHP )));
}
}
iMade->SetCont( p );
iMade->PlaceInPack();
boughtItems.push_back( iMade );
boughtItemAmounts.push_back( 1 );
}
}
}
biTemp->IncAmount( -amount[i], true );
biTemp->SetRestock( biTemp->GetRestock() + amount[i] );
}
else
{
CItem * biTempCont = static_cast<CItem *>( biTemp->GetCont() );
switch( biTempCont->GetLayer() )
{
case 0x1A: // Sell Container
if( biTemp->IsPileable() )
{
iMade = Items->DupeItem( tSock, biTemp, amount[i] );
if( iMade != nullptr )
{
iMade->SetCont( p );
iMade->PlaceInPack();
boughtItems.push_back( iMade );
boughtItemAmounts.push_back( amount[i] );
}
}
else
{
for( j = 0; j < amount[i]; ++j )
{
iMade = Items->DupeItem( tSock, biTemp, 1 );
if( iMade != nullptr )
{
SI16 iMadeHP = iMade->GetHP();
SI16 iMadeMaxHP = static_cast<SI16>( iMade->GetMaxHP() );
if( iMadeMaxHP > iMadeHP )
{
// Randomize the item's HP from HP to MaxHP
iMade->SetHP( static_cast<SI16>( RandomNum( iMadeHP, iMadeMaxHP )));
}
iMade->SetCont( p );
iMade->PlaceInPack();
boughtItems.push_back( iMade );
boughtItemAmounts.push_back( 1 );
}
}
}
biTemp->IncAmount( -amount[i], true );
biTemp->SetRestock( biTemp->GetRestock() + amount[i] );
break;
case 0x1B: // Bought Container
if( biTemp->IsPileable() )
{
boughtItemAmounts.push_back( biTemp->GetAmount() );
biTemp->SetCont( p );
boughtItems.push_back( biTemp );
}
else
{
for( j = 0; j < amount[i]-1; ++j )
{
iMade = Items->DupeItem( tSock, biTemp, 1 );
if( iMade != nullptr )
{
iMade->SetCont( p );
iMade->PlaceInPack();
boughtItems.push_back( iMade );
boughtItemAmounts.push_back( 1 );
}
}
biTemp->SetCont( p );
biTemp->SetAmount( 1 );
boughtItems.push_back( biTemp );
boughtItemAmounts.push_back( 1 );
}
break;
default:
Console.Error( " Fallout of switch statement without default. vendor.cpp, buyItem()" );
break;
}
}
}
for( i = 0; i < boughtItems.size(); ++i )
{
if( boughtItems[i] )
{
for( auto scriptTrig : boughtItems[i]->GetScriptTriggers() )
{
cScript *toExecute = JSMapping->GetScript( scriptTrig );
if( toExecute != nullptr )
{
// If script returns 1, prevent other scripts with event from running
if( toExecute->OnBoughtFromVendor( tSock, npc, boughtItems[i], boughtItemAmounts[i] ) == 1 )
{
break;
}
}
}
for( auto scriptTrig : npc->GetScriptTriggers() )
{
cScript *toExecute = JSMapping->GetScript( scriptTrig );
if( toExecute != nullptr )
{
// If script returns 1, prevent other scripts with event from running
if( toExecute->OnBoughtFromVendor( tSock, npc, boughtItems[i], boughtItemAmounts[i] ) == 1 )
{
break;
}
}
}
}
}
}
}
else
{
npc->TextMessage( nullptr, 1340, TALK, false ); // Alas, thou dost not possess sufficient gold for this purchase!
}
if( clear )
{
CPBuyItem clrSend;
clrSend.Serial( tSock->GetDWord( 3 ));
tSock->Send( &clrSend );
}
return true;
}
UI16 HandleAutoStack( CItem *mItem, CItem *mCont, CSocket *mSock, CChar *mChar );
//o------------------------------------------------------------------------------------------------o
//| Function - CPISellItem::Handle()
//o------------------------------------------------------------------------------------------------o
//| Purpose - Player sells an item to the vendor
//o------------------------------------------------------------------------------------------------o
bool CPISellItem::Handle( void )
{
if( tSock->GetByte( 8 ) != 0 )
{
CChar *mChar = tSock->CurrcharObj();
CChar *n = CalcCharObjFromSer( tSock->GetDWord( 3 ));
if( !ValidateObject( n ) || !ValidateObject( mChar ))
return true;
CItem *buyPack = n->GetItemAtLayer( IL_BUYCONTAINER );
CItem *boughtPack = n->GetItemAtLayer( IL_BOUGHTCONTAINER );
CItem *sellPack = n->GetItemAtLayer( IL_SELLCONTAINER );
if( !ValidateObject( buyPack ) || !ValidateObject( sellPack ) || !ValidateObject( boughtPack ))
return true;
CItem *j = nullptr, *k = nullptr, *l = nullptr;
UI16 amt = 0, maxsell = 0;
UI08 i = 0;
UI32 totgold = 0, value = 0;
for( i = 0; i < tSock->GetByte( 8 ); ++i )
{
//j = CalcItemObjFromSer( tSock->GetDWord( 9 + (6 * static_cast<size_t>( i ))));
amt = tSock->GetWord( 13 + ( 6 * static_cast<size_t>( i )));
maxsell += amt;
}
if( maxsell > cwmWorldState->ServerData()->SellMaxItemsStatus() )
{
n->TextMessage( nullptr, 1342, TALK, 0, mChar->GetName().c_str(), cwmWorldState->ServerData()->SellMaxItemsStatus() ); // Sorry %s, but I can only buy %i items at a time!
return true;
}
// Loop through each item being sold
for( i = 0; i < tSock->GetByte( 8 ); ++i )
{
j = CalcItemObjFromSer( tSock->GetDWord( 9 + ( 6 * static_cast<size_t>( i ))));
amt = tSock->GetWord( 13 + ( 6 * static_cast<size_t>( i )));
if( ValidateObject( j ))
{
if( j->GetAmount() < amt || FindItemOwner( j ) != mChar )
{
n->TextMessage( nullptr, 1343, TALK, false ); // Cheating scum! Leave now, before I call the guards!
return true;
}
// Check if onSellToVendor JS event is present for item being sold
// If present and a value of "false" or 0 was returned from the script, halt the sale
bool saleHalted = false;
std::vector<UI16> jScriptTriggers = j->GetScriptTriggers();
for( auto scriptTrig : jScriptTriggers )
{
cScript *toExecute = JSMapping->GetScript( scriptTrig );
if( toExecute != nullptr )
{
if( toExecute->OnSellToVendor( tSock, n, j, maxsell ) == 0 )
{
// Halt sale!
saleHalted = true;
break;
}
}
}
// Also check for same event on vendor itself, for broader usecases that doesn't require scripts on every item sold
// Only run this check if the sale was not halted by a script on the item itself, though
if( !saleHalted )
{
std::vector<UI16> nScriptTriggers = n->GetScriptTriggers();
for( auto scriptTrig : nScriptTriggers )
{
cScript *toExecute = JSMapping->GetScript( scriptTrig );
if( toExecute != nullptr )
{
if( toExecute->OnSellToVendor( tSock, n, j, maxsell ) == 0 )
{
// Halt sale!
saleHalted = true;
break;
}
}
}
}
// If sale was halted by onSellToVendor event, continue to next item
if( saleHalted )
{
continue;
}
CItem *join = nullptr;
GenericList<CItem *> *pCont = boughtPack->GetContainsList();
for( k = pCont->First(); !pCont->Finished(); k = pCont->Next() )
{
if( ValidateObject( k ))
{
if( k->GetId() == j->GetId() && j->GetType() == k->GetType() )
{
if( j->GetId() != 0x14f0 || ( j->GetTempVar( CITV_MOREX ) == k->GetTempVar( CITV_MOREX )))
{
join = k;
break; // we found the item we're looking for already
}
}
}
}
pCont = buyPack->GetContainsList();
for( k = pCont->First(); !pCont->Finished(); k = pCont->Next() )
{
if( ValidateObject( k ))
{
if(( k->GetId() == j->GetId() || oldstrutil::lower( k->GetSectionId() ) == oldstrutil::lower( j->GetSectionId() )) && j->GetType() == k->GetType() )
{
if( j->GetId() != 0x14f0 || ( j->GetTempVar( CITV_MOREX ) == k->GetTempVar( CITV_MOREX )))
{
value = CalcValue( j, k->GetSellValue() );
break; // we found the value we're looking for already
}
}
}
}
// If an object already exist in the boughtPack that this one can be joined to...
if( ValidateObject( join ))
{
join->IncAmount( amt );
join->SetRestock( join->GetRestock() - amt );
l = join;
totgold += ( amt * value );
if( j->GetAmount() == amt )
{
j->Delete();
}
else
{
j->IncAmount( -amt );
}
}
else
{
//Otherwise, move this item to the vendor's boughtPack
totgold += ( amt * value );
if( j->GetAmount() != amt )
{
l = Items->DupeItem( tSock, j, amt );
j->SetAmount( j->GetAmount() - amt );
}
else
{
l = j;
}
if( ValidateObject( l ))
{
l->SetCont( boughtPack );
l->RemoveFromSight();
}
}
if( l )
{
// Check for onSoldToVendor event on item
std::vector<UI16> lScriptTriggers = l->GetScriptTriggers();
for( auto scriptTrig : lScriptTriggers )
{
cScript *toExecute = JSMapping->GetScript( scriptTrig );
if( toExecute != nullptr )
{
// If script returns true/1, prevent other scripts with event from running
if( toExecute->OnSoldToVendor( tSock, n, l, amt ) == 1 )
{
break;
}
}
}
// Check for onSoldToVendor event on vendor
std::vector<UI16> nScriptTriggers = n->GetScriptTriggers();
for( auto scriptTrig : nScriptTriggers )
{
cScript *toExecute = JSMapping->GetScript( scriptTrig );
if( toExecute != nullptr )
{
// If script returns true/1, prevent other scripts with event from running
if( toExecute->OnSoldToVendor( tSock, n, l, amt ) == 1 )
{
break;
}
}
}
}
}
}
Effects->GoldSound( tSock, totgold );
bool shouldUseBank = false;
CItem *bankBox = nullptr;
if( totgold >= static_cast<UI32>( cwmWorldState->ServerData()->BuyThreshold() ))
{
bankBox = mChar->GetItemAtLayer( IL_BANKBOX );
if( ValidateObject( bankBox ))
{
shouldUseBank = true;
}
}
while( totgold > MAX_STACK )
{
auto newGoldPile = Items->CreateScriptItem( nullptr, mChar, "0x0EED", MAX_STACK, OT_ITEM, false );
if( shouldUseBank )
{
newGoldPile->SetCont( bankBox );
HandleAutoStack( newGoldPile, bankBox, nullptr, nullptr );
tSock->SysMessage( oldstrutil::format( Dictionary->GetEntry( 2703, tSock->Language() ) + " %u", newGoldPile->GetAmount() )); // Gold was deposited in your account:
}
else
{
newGoldPile->SetCont( mChar->GetPackItem() );
HandleAutoStack( newGoldPile, mChar->GetPackItem(), nullptr, nullptr );
}
if( ValidateObject( newGoldPile ))
{
newGoldPile->PlaceInPack();
}
totgold -= MAX_STACK;
}
if( totgold > 0 )
{
auto newGoldPile = Items->CreateScriptItem( nullptr, mChar, "0x0EED", totgold, OT_ITEM, false );
if( shouldUseBank )
{
newGoldPile->SetCont( bankBox );
HandleAutoStack( newGoldPile, bankBox, nullptr, nullptr );
tSock->SysMessage( oldstrutil::format( Dictionary->GetEntry( 2703, tSock->Language() ) + " %u", newGoldPile->GetAmount() )); // Gold was deposited in your account:
}
else
{
newGoldPile->SetCont( mChar->GetPackItem() );
HandleAutoStack( newGoldPile, mChar->GetPackItem(), nullptr, nullptr );
}
if( ValidateObject( newGoldPile ))
{
newGoldPile->PlaceInPack();
}
}
}
CPBuyItem clrSend;
clrSend.Serial( tSock->GetDWord( 3 ));
tSock->Send( &clrSend );
return true;
}
//o------------------------------------------------------------------------------------------------o
//| Function - RestockNPC()
///o------------------------------------------------------------------------------------------------o
//| Purpose - Restock NPC Vendors
//o------------------------------------------------------------------------------------------------o
void RestockNPC( CChar& i, bool stockAll )
{
if( !i.IsShop() )
return; // if we aren't a shopkeeper, why bother?
CItem *ci = i.GetItemAtLayer( IL_SELLCONTAINER );
if( ValidateObject( ci ))
{
GenericList<CItem *> *ciCont = ci->GetContainsList();
for( CItem *c = ciCont->First(); !ciCont->Finished(); c = ciCont->Next() )
{
if( ValidateObject( c ))
{
if( stockAll )
{
c->IncAmount( c->GetRestock() );
c->SetRestock( 0 );
}
else if( c->GetRestock() )
{
UI16 stockAmt = std::min( c->GetRestock(), static_cast<UI16>(( c->GetRestock() / 2 ) + 1 ));
c->IncAmount( stockAmt );
c->SetRestock( c->GetRestock() - stockAmt );
}
if( cwmWorldState->ServerData()->TradeSystemStatus() )
{
CTownRegion *tReg = CalcRegionFromXY( i.GetX(), i.GetY(), i.WorldNumber(), i.GetInstanceId() );
Items->StoreItemRandomValue( c, tReg );
}
}
}
}
}
bool RestockFunctor( CBaseObject *a, UI32 &b, [[maybe_unused]] void *extraData )
{
bool retVal = true;
CChar *c = static_cast<CChar *>( a );
if( ValidateObject( c ))
{
RestockNPC(( *c ), ( b == 1 ));
}
return retVal;
}
//o------------------------------------------------------------------------------------------------o
//| Function - Restock()
//| Date - 3/15/2003
//o------------------------------------------------------------------------------------------------o
//| Purpose - Restock all NPC Vendors
//o------------------------------------------------------------------------------------------------o
void Restock( bool stockAll )
{
UI32 b = ( stockAll ? 1 : 0 );
ObjectFactory::GetSingleton().IterateOver( OT_CHAR, b, nullptr, &RestockFunctor );
}