From: <wel...@us...> - 2010-01-17 08:17:16
|
Revision: 5322 http://planeshift.svn.sourceforge.net/planeshift/?rev=5322&view=rev Author: weltall2 Date: 2010-01-17 08:17:10 +0000 (Sun, 17 Jan 2010) Log Message: ----------- fixed PS#3149 - Unable to buy a stack of books at Jayose and PS#701 - Stack items in sacks before moving them to the main inventory. patch by rlydontknow Modified Paths: -------------- trunk/src/server/bulkobjects/pscharinventory.cpp trunk/src/server/bulkobjects/pscharinventory.h trunk/src/server/bulkobjects/psitem.cpp trunk/src/server/bulkobjects/psitem.h trunk/src/server/psserverchar.cpp Modified: trunk/src/server/bulkobjects/pscharinventory.cpp =================================================================== --- trunk/src/server/bulkobjects/pscharinventory.cpp 2010-01-17 08:12:05 UTC (rev 5321) +++ trunk/src/server/bulkobjects/pscharinventory.cpp 2010-01-17 08:17:10 UTC (rev 5322) @@ -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) { Modified: trunk/src/server/bulkobjects/pscharinventory.h =================================================================== --- trunk/src/server/bulkobjects/pscharinventory.h 2010-01-17 08:12:05 UTC (rev 5321) +++ trunk/src/server/bulkobjects/pscharinventory.h 2010-01-17 08:17:10 UTC (rev 5322) @@ -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); Modified: trunk/src/server/bulkobjects/psitem.cpp =================================================================== --- trunk/src/server/bulkobjects/psitem.cpp 2010-01-17 08:12:05 UTC (rev 5321) +++ trunk/src/server/bulkobjects/psitem.cpp 2010-01-17 08:17:10 UTC (rev 5322) @@ -1268,7 +1268,7 @@ } } -bool psItem::CheckStackableWith(const psItem *otheritem, bool precise) const +bool psItem::CheckStackableWith(const psItem *otheritem, bool precise, bool checkStackCount) const { int i; @@ -1301,7 +1301,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())) Modified: trunk/src/server/bulkobjects/psitem.h =================================================================== --- trunk/src/server/bulkobjects/psitem.h 2010-01-17 08:12:05 UTC (rev 5321) +++ trunk/src/server/bulkobjects/psitem.h 2010-01-17 08:17:10 UTC (rev 5322) @@ -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(); Modified: trunk/src/server/psserverchar.cpp =================================================================== --- trunk/src/server/psserverchar.cpp 2010-01-17 08:12:05 UTC (rev 5321) +++ trunk/src/server/psserverchar.cpp 2010-01-17 08:17:10 UTC (rev 5322) @@ -24,6 +24,7 @@ #include <iutil/databuff.h> #include <iutil/object.h> #include <csutil/csstring.h> +#include <csutil/list.h> //============================================================================= // Project Includes @@ -770,94 +771,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() ); This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site. |