rendered paste bodyIndex: src/server/psserverchar.cpp
===================================================================
--- src/server/psserverchar.cpp (revision 4983)
+++ src/server/psserverchar.cpp (working copy)
@@ -24,6 +24,7 @@
#include <iutil/databuff.h>
#include <iutil/object.h>
#include <csutil/csstring.h>
+#include <csutil/list.h>
//=============================================================================
// Project Includes
@@ -743,94 +744,165 @@
if (count <= 0) return;
}
+ csList<psItem *> newitem;
+ bool error = false;
// if item is to be personalised, then duplicate the item_stats and personalise
// by given it a unique name for the purchaser.
- psItem* newitem;
if (item->GetBuyPersonalise())
{
- // only 1 personalised thing can be bought at a time
- if (count != 1)
+ for (int i = 0; i < count; i++)
{
- psserver->SendSystemError(client->GetClientNum(),"You can only purchase 1 Personalised thing at a time!");
- return;
- }
+ // copy 'item' item_stats to create unique item...
+ // personalised name is "<item> of <purchaser>"
+ // If "<item> of <purchaser>" already exists, add " (<number>)" (being the row count+1 of item_stats)
+ // if item is 'public', then name is "<item> (<number>)"
+ psItem * newpersonaliseditem;
+ PSITEMSTATS_CREATORSTATUS creatorStatus;
+ item->GetBaseStats()->GetCreator(creatorStatus);
+ csString itemName(item->GetName());
+ if (creatorStatus != PSITEMSTATS_CREATOR_PUBLIC)
+ itemName.AppendFmt(" of %s", client->GetName());
- // copy 'item' item_stats to create unique item...
- // personalised name is "<item> of <purchaser>"
- // If "<item> of <purchaser>" already exists, add " (<number>)" (being the row count+1 of item_stats)
- // if item is 'public', then name is "<item> (<number>)"
- csString personalisedName(item->GetName());
- PSITEMSTATS_CREATORSTATUS creatorStatus;
- item->GetBaseStats()->GetCreator(creatorStatus);
- if (creatorStatus == PSITEMSTATS_CREATOR_PUBLIC)
- {
- personalisedName.AppendFmt(" (%zu)", CacheManager::GetSingleton().ItemStatsSize()+1);
- }
- else
- {
- personalisedName.AppendFmt(" of %s", client->GetName());
- if (CacheManager::GetSingleton().BasicItemStatsByNameExist(personalisedName) > 0)
- personalisedName.AppendFmt(" (%zu)", CacheManager::GetSingleton().ItemStatsSize()+1);
- }
+ csString personalisedName(itemName);
- psItemStats* newCreation = CacheManager::GetSingleton().CopyItemStats(item->GetBaseStats()->GetUID(),
+ for(int i = 1; CacheManager::GetSingleton().BasicItemStatsByNameExist(personalisedName) > 0; i++)
+ {
+ personalisedName = itemName;
+ personalisedName.AppendFmt(" (%zu)", CacheManager::GetSingleton().ItemStatsSize()+i);
+ }
+
+ psItemStats * newCreation = CacheManager::GetSingleton().CopyItemStats(item->GetBaseStats()->GetUID(),
personalisedName);
- if (!newCreation)
- return;
+ if (!newCreation)
+ {
+ error = true;
+ break;
+ }
- newCreation->SetUnique();
+ newCreation->SetUnique();
- newCreation->Save();
+ newCreation->Save();
- // instantiate it
- newitem = newCreation->InstantiateBasicItem();
- if (newitem)
- newitem->SetLoaded();
+ // instantiate it
+ newpersonaliseditem = newCreation->InstantiateBasicItem();
+
+ if (!newpersonaliseditem)
+ {
+ error = true;
+ break;
+ }
+
+ newpersonaliseditem->SetLoaded();
+ newitem.PushBack(newpersonaliseditem);
+ }
}
else // normal purchase
{
- newitem = item->Copy(count);
+ int maxcount = MAX_STACK_COUNT;
+ if (item->GetIsContainer())
+ maxcount = 1;
+
+ // split it into several stacks if it doesn't fit into a single one
+ for (int i = 0; i < count/maxcount && !error; i++)
+ if(!*newitem.PushBack(item->Copy(maxcount)))
+ error = true;
+
+ if(error || (count % maxcount && !*newitem.PushBack(item->Copy(count % maxcount))))
+ error = true;
}
- if (newitem == NULL)
+ if (error)
{
Error2("Error: failed to create item %s.", itemName.GetData());
psserver->SendSystemError(client->GetClientNum(), "Error: failed to create item %s.", itemName.GetData());
+
+ // destroy the created items
+ for (csList<psItem *>::Iterator newitemitr(newitem); newitemitr.HasNext(); )
+ {
+ psItem * currentitem = newitemitr.Next();
+ if (!currentitem)
+ continue;
+
+ CacheManager::GetSingleton().RemoveInstance(currentitem);
+ }
+
return;
}
- // If we managed to buy some items, we pay some money
- bool stackable = newitem->GetBaseStats()->GetIsStackable();
+ csList<psBuyEvent> buyevents;
+ psMoney cost;
+ int newcount = 0; // count what we really got
- if (character->Inventory().Add(newitem, false, stackable))
+ for (csList<psItem *>::Iterator newitemitr(newitem); newitemitr.HasNext(); )
{
- psMoney cost;
- cost = (price * count).Normalized();
+ psItem *& currentitem = newitemitr.Next();
+ if (!currentitem)
+ continue;
- character->SetMoney(money - cost);
+ bool stackable = currentitem->GetIsStackable();
+ int partcount = 1;
- psserver->SendSystemOK( client->GetClientNum(), "You bought %d %s for %s a total of %d Trias.",
- count, itemName.GetData(), cost.ToUserString().GetDataSafe(),cost.GetTotal());
+ if (stackable) // if it's stackable, try to add in on existing stacks, first
+ {
+ for (psItem * newstack; newstack = character->Inventory().AddStacked(currentitem, partcount); )
+ {
+ newcount += partcount;
- psBuyEvent evt(
- character->GetPID(),
- merchant->GetPID(),
- item->GetUID(),
- count,
- (int)item->GetCurrentStats()->GetQuality(),
- cost.GetTotal()
- );
- evt.FireEvent();
+ psMoney partcost((price * partcount).Normalized());
+ cost += partcost;
+
+ psBuyEvent evt(
+ character->GetPID(),
+ merchant->GetPID(),
+ newstack->GetUID(),
+ partcount,
+ (int)newstack->GetCurrentStats()->GetQuality(),
+ partcost.GetTotal()
+ );
+ buyevents.PushBack(evt);
+ }
+
+ partcount = currentitem->GetStackCount();
+ }
+
+ if (character->Inventory().Add(currentitem, false, stackable))
+ {
+ newcount += partcount;
+ psMoney partcost((price * partcount).Normalized());
+ cost += partcost;
+
+ psBuyEvent evt(
+ character->GetPID(),
+ merchant->GetPID(),
+ currentitem->GetUID(),
+ partcount,
+ (int)currentitem->GetCurrentStats()->GetQuality(),
+ partcost.GetTotal()
+ );
+ buyevents.PushBack(evt);
+ }
+ else
+ {
+ CacheManager::GetSingleton().RemoveInstance(currentitem);
+ }
}
- else
- {
- // No empty or stackable slot in bulk or any container
- psserver->SendSystemError(client->GetClientNum(),"You're carrying too many items");
- CacheManager::GetSingleton().RemoveInstance(newitem);
+
+ if (newcount != count) // not enough empty or stackable slots
+ psserver->SendSystemError(client->GetClientNum(),"You're carrying too many items [%d/%d]", newcount, count);
+
+ if (!newcount)
return;
- }
+ // If we managed to buy some items, we pay some money
+ character->SetMoney(money - cost);
+
+ psserver->SendSystemOK( client->GetClientNum(), "You bought %d %s for %s a total of %d Trias.",
+ newcount, itemName.GetData(), cost.ToUserString().GetDataSafe(),cost.GetTotal());
+
+ for (csList<psBuyEvent>::Iterator buyeventsitr(buyevents); buyeventsitr.HasNext(); )
+ buyeventsitr.Next().FireEvent();
+
// Update client views
SendPlayerMoney( client );
SendMerchantItems( client, merchant, item->GetCategory() );
Index: src/server/bulkobjects/psitem.cpp
===================================================================
--- src/server/bulkobjects/psitem.cpp (revision 4983)
+++ src/server/bulkobjects/psitem.cpp (working copy)
@@ -1267,7 +1267,7 @@
}
}
-bool psItem::CheckStackableWith(const psItem *otheritem, bool precise) const
+bool psItem::CheckStackableWith(const psItem *otheritem, bool precise, bool checkStackCount) const
{
int i;
@@ -1300,7 +1300,7 @@
if (!GetIsStackable() || !otheritem->GetIsStackable())
return false;
- if (otheritem->GetStackCount() > MAX_STACK_COUNT - stack_count)
+ if (checkStackCount && otheritem->GetStackCount() > MAX_STACK_COUNT - stack_count)
return false;
if (strcmp(GetName(), otheritem->GetName()) || strcmp(GetDescription(), otheritem->GetDescription()))
Index: src/server/bulkobjects/pscharinventory.h
===================================================================
--- src/server/bulkobjects/pscharinventory.h (revision 4983)
+++ src/server/bulkobjects/pscharinventory.h (working copy)
@@ -204,6 +204,13 @@
bool Add(psItem *& item, bool test = false, bool stack = true,
INVENTORY_SLOT_NUMBER slot = PSCHARACTER_SLOT_NONE, gemContainer* container = NULL);
+ /** Attempt to stack an item on an existing one if Add failed.
+ * @param item The item we want to place into the slot.
+ * @param added number of items that have been taken off the stack
+ * @return The item if the item could be placed partially in the slot. else NULL.
+ */
+ psItem * AddStacked(psItem *& item, int & added);
+
/// Add an item to the inventory, or drop if inventory is full.
bool AddOrDrop(psItem *&item, bool stack = true);
@@ -442,12 +449,12 @@
/** Returns the array index of the item that matches the specified
* ones, so they can be combined.
*/
- size_t FindCompatibleStackedItem(psItem *item);
+ size_t FindCompatibleStackedItem(psItem *item, bool checkStackCount = true);
/** Returns an array of array indices for items that match the specified
* ones, so they can be combined.
*/
- csArray<size_t> FindCompatibleStackedItems(psItem *item);
+ csArray<size_t> FindCompatibleStackedItems(psItem *item, bool checkStackCount = true);
/// Allocate either a psItem or a psGlyph
psItem *GetItemFactory(psItemStats *stats);
Index: src/server/bulkobjects/pscharinventory.cpp
===================================================================
--- src/server/bulkobjects/pscharinventory.cpp (revision 4983)
+++ src/server/bulkobjects/pscharinventory.cpp (working copy)
@@ -494,11 +494,11 @@
return SIZET_NOT_FOUND;
}
-size_t psCharacterInventory::FindCompatibleStackedItem(psItem *item)
+size_t psCharacterInventory::FindCompatibleStackedItem(psItem *item, bool checkStackCount)
{
for (size_t i=1; i<inventory.GetSize(); i++)
{
- if (inventory[i].item->CheckStackableWith(item, true))
+ if (inventory[i].item->CheckStackableWith(item, true, checkStackCount))
{
return i;
}
@@ -506,12 +506,12 @@
return SIZET_NOT_FOUND;
}
-csArray<size_t> psCharacterInventory::FindCompatibleStackedItems(psItem *item)
+csArray<size_t> psCharacterInventory::FindCompatibleStackedItems(psItem *item, bool checkStackCount)
{
csArray<size_t> compatibleItems;
for (size_t i=1; i<inventory.GetSize(); i++)
{
- if (inventory[i].item->CheckStackableWith(item, true))
+ if (inventory[i].item->CheckStackableWith(item, true, checkStackCount))
{
compatibleItems.Push(i);
}
@@ -580,6 +580,56 @@
}
}
+psItem * psCharacterInventory::AddStacked(psItem *& item, int & added)
+{
+ if (!item->GetIsStackable())
+ return NULL;
+
+ csArray<size_t> itemIndices(FindCompatibleStackedItems(item, false));
+ size_t max = 0;
+
+ // find the maximum amount we can stack in a single try
+ for (size_t i = 0; i < itemIndices.GetSize() && max < item->GetStackCount(); i++)
+ {
+ psItem * tocheck = inventory[itemIndices[i]].item;
+ int fits = MAX_STACK_COUNT - tocheck->GetStackCount();
+
+ if (tocheck->GetContainerID())
+ {
+ int size = GetContainedSize(FindItemID(tocheck->GetContainerID())) - FindItemID(tocheck->GetContainerID())->GetContainerMaxSize();
+ if(size/item->GetItemSize() > fits)
+ fits = size/item->GetItemSize();
+ }
+
+ if (fits > max)
+ max = fits;
+ }
+
+ if(max > item->GetStackCount())
+ max = item->GetStackCount();
+
+ if(max && max < item->GetStackCount())
+ {
+ psItem * newstack = item->SplitStack(max);
+ newstack->SetOwningCharacter(item->GetOwningCharacter());
+ newstack->ForceSaveIfNew();
+
+ if(Add(newstack, false, true))
+ {
+ added = max;
+ return newstack;
+ }
+ else // this really should never happen, but just in case
+ {
+ item->SetStackCount(item->GetStackCount() + newstack->GetStackCount());
+ CacheManager::GetSingleton().RemoveInstance(newstack);
+ }
+ }
+
+ added = 0;
+ return NULL;
+}
+
bool psCharacterInventory::Add(psItem *&item, bool test, bool stack, INVENTORY_SLOT_NUMBER slot, gemContainer* container)
{
if (slot%100<ANY_EMPTY_BULK_SLOT || slot%100>=PSCHARACTER_SLOT_BULK_END)
@@ -641,8 +691,6 @@
}
}
- itemIndex = SIZET_NOT_FOUND; // We need to reset this in case a stack was found, but the container it was in was full
-
// Next check the main bulk slots
if (itemIndex == SIZET_NOT_FOUND && slot < 0)
{
Index: src/server/bulkobjects/psitem.h
===================================================================
--- src/server/bulkobjects/psitem.h (revision 4983)
+++ src/server/bulkobjects/psitem.h (working copy)
@@ -804,7 +804,7 @@
void RunEquipScript(gemActor *actor);
void CancelEquipScript();
- bool CheckStackableWith(const psItem *otheritem, bool precise) const;
+ bool CheckStackableWith(const psItem *otheritem, bool precise, bool checkStackCount = true) const;
const char *GetSound();