All pastes #1719447 Raw Edit

Unnamed

public text v1 · immutable
#1719447 ·published 2009-12-18 13:58 UTC
rendered paste body
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);
 
Index: src/server/bulkobjects/pscharinventory.cpp
===================================================================
--- src/server/bulkobjects/pscharinventory.cpp	(revision 4983)
+++ src/server/bulkobjects/pscharinventory.cpp	(working copy)
@@ -580,6 +580,60 @@
     }
 }
 
+psItem * psCharacterInventory::AddStacked(psItem *& item, int & added)
+{
+    if (!item->GetIsStackable())
+        return NULL;
+
+    // dirty workaround to find all compatible item stacks, even if our one wouldn't fit completely
+    psItem * singleitem = item->Copy(1);
+    csArray<size_t> itemIndices(FindCompatibleStackedItems(singleitem));
+    CacheManager::GetSingleton().RemoveInstance(singleitem);
+
+    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 +695,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/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() );