1
0
cuberite-2a/source/ItemGrid.cpp
madmaxoft@gmail.com cf87169737 Refactored cInventory to use cItemGrid for the actual Storage
This makes the API more orthogonal and is easier to use in the plugins. Also changes in the inventory are now propagated to the needed places (armor updates to BroadcastEntityEquipment etc.) even when the inventory is changed by a plugin.

git-svn-id: http://mc-server.googlecode.com/svn/trunk@1503 0a769ca7-a7f5-676a-18bf-c427514a06d6
2013-05-24 07:30:39 +00:00

525 lines
9.5 KiB
C++

// ItemGrid.cpp
// Implements the cItemGrid class representing a storage for items in a XY grid (chests, dispensers, inventory etc.)
#include "Globals.h"
#include "ItemGrid.h"
#include "Items/ItemHandler.h"
#include "Noise.h"
cItemGrid::cItemGrid(int a_Width, int a_Height) :
m_Width(a_Width),
m_Height(a_Height),
m_NumSlots(a_Width * a_Height),
m_Slots(new cItem[a_Width * a_Height]),
m_IsInTriggerListeners(false)
{
}
cItemGrid::~cItemGrid()
{
delete[] m_Slots;
}
int cItemGrid::GetSlotNum(int a_X, int a_Y) const
{
if (
(a_X < 0) || (a_X >= m_Width) ||
(a_Y < 0) || (a_Y >= m_Height)
)
{
LOGWARNING("%s: coords out of range: (%d, %d) in grid of size (%d, %d)",
__FUNCTION__, a_X, a_Y, m_Width, m_Height
);
return -1;
}
return a_X + m_Width * a_Y;
}
void cItemGrid::GetSlotCoords(int a_SlotNum, int & a_X, int & a_Y) const
{
if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots))
{
LOGWARNING("%s: SlotNum out of range: %d in grid of range %d",
__FUNCTION__, a_SlotNum, m_NumSlots
);
a_X = -1;
a_Y = -1;
return;
}
a_X = a_SlotNum % m_Width;
a_Y = a_SlotNum / m_Width;
}
const cItem & cItemGrid::GetSlot(int a_X, int a_Y) const
{
return GetSlot(GetSlotNum(a_X, a_Y));
}
cItem & cItemGrid::GetSlot(int a_X, int a_Y)
{
return GetSlot(GetSlotNum(a_X, a_Y));
}
const cItem & cItemGrid::GetSlot(int a_SlotNum) const
{
if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots))
{
LOGWARNING("%s: Invalid slot number, %d out of %d slots",
__FUNCTION__, a_SlotNum, m_NumSlots
);
return m_Slots[0];
}
return m_Slots[a_SlotNum];
}
cItem & cItemGrid::GetSlot(int a_SlotNum)
{
if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots))
{
LOGWARNING("%s: Invalid slot number, %d out of %d slots",
__FUNCTION__, a_SlotNum, m_NumSlots
);
return m_Slots[0];
}
return m_Slots[a_SlotNum];
}
void cItemGrid::SetSlot(int a_X, int a_Y, const cItem & a_Item)
{
SetSlot(GetSlotNum(a_X, a_Y), a_Item);
}
void cItemGrid::SetSlot(int a_X, int a_Y, short a_ItemType, char a_ItemCount, short a_ItemDamage)
{
SetSlot(GetSlotNum(a_X, a_Y), cItem(a_ItemType, a_ItemCount, a_ItemDamage));
}
void cItemGrid::SetSlot(int a_SlotNum, const cItem & a_Item)
{
if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots))
{
LOGWARNING("%s: Invalid slot number %d out of %d slots",
__FUNCTION__, a_SlotNum, m_NumSlots
);
return;
}
m_Slots[a_SlotNum] = a_Item;
TriggerListeners(a_SlotNum);
}
void cItemGrid::SetSlot(int a_SlotNum, short a_ItemType, char a_ItemCount, short a_ItemDamage)
{
SetSlot(a_SlotNum, cItem(a_ItemType, a_ItemCount, a_ItemDamage));
}
void cItemGrid::EmptySlot(int a_X, int a_Y)
{
EmptySlot(GetSlotNum(a_X, a_Y));
}
void cItemGrid::EmptySlot(int a_SlotNum)
{
if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots))
{
LOGWARNING("%s: Invalid slot number %d out of %d slots",
__FUNCTION__, a_SlotNum, m_NumSlots
);
return;
}
// Check if already empty:
if (m_Slots[a_SlotNum].IsEmpty())
{
return;
}
// Empty and notify
m_Slots[a_SlotNum].Empty();
TriggerListeners(a_SlotNum);
}
void cItemGrid::Clear(void)
{
for (int i = 0; i < m_NumSlots; i++)
{
m_Slots[i].Empty();
TriggerListeners(i);
}
}
int cItemGrid::HowManyCanFit(const cItem & a_ItemStack)
{
char NumLeft = a_ItemStack.m_ItemCount;
int MaxStack = ItemHandler(a_ItemStack.m_ItemType)->GetMaxStackSize();
for (int i = m_NumSlots - 1; i >= 0; i--)
{
if (m_Slots[i].IsEmpty())
{
NumLeft -= MaxStack;
}
else if (m_Slots[i].IsStackableWith(a_ItemStack))
{
NumLeft -= MaxStack - m_Slots[i].m_ItemCount;
}
if (NumLeft <= 0)
{
// All items fit
return a_ItemStack.m_ItemCount;
}
} // for i - m_Slots[]
return a_ItemStack.m_ItemCount - NumLeft;
}
int cItemGrid::AddItem(cItem & a_ItemStack, bool a_AllowNewStacks)
{
int NumLeft = a_ItemStack.m_ItemCount;
int MaxStack = ItemHandler(a_ItemStack.m_ItemType)->GetMaxStackSize();
// Scan existing stacks:
for (int i = m_NumSlots - 1; i >= 0; i--)
{
if (m_Slots[i].IsStackableWith(a_ItemStack))
{
int PrevCount = m_Slots[i].m_ItemCount;
m_Slots[i].m_ItemCount = std::min(MaxStack, PrevCount + NumLeft);
NumLeft -= m_Slots[i].m_ItemCount - PrevCount;
TriggerListeners(i);
}
if (NumLeft <= 0)
{
// All items fit
return a_ItemStack.m_ItemCount;
}
} // for i - m_Slots[]
if (!a_AllowNewStacks)
{
return (a_ItemStack.m_ItemCount - NumLeft);
}
for (int i = m_NumSlots - 1; i >= 0; i--)
{
if (m_Slots[i].IsEmpty())
{
m_Slots[i] = a_ItemStack;
m_Slots[i].m_ItemCount = std::min(MaxStack, NumLeft);
NumLeft -= m_Slots[i].m_ItemCount;
TriggerListeners(i);
}
if (NumLeft <= 0)
{
// All items fit
return a_ItemStack.m_ItemCount;
}
} // for i - m_Slots[]
return (a_ItemStack.m_ItemCount - NumLeft);
}
int cItemGrid::AddItems(cItems & a_ItemStackList, bool a_AllowNewStacks)
{
int TotalAdded = 0;
for (cItems::iterator itr = a_ItemStackList.begin(); itr != a_ItemStackList.end();)
{
int NumAdded = AddItem(*itr, a_AllowNewStacks);
if (itr->m_ItemCount == NumAdded)
{
itr = a_ItemStackList.erase(itr);
}
else
{
itr->m_ItemCount -= NumAdded;
++itr;
}
TotalAdded += NumAdded;
}
return TotalAdded;
}
int cItemGrid::ChangeSlotCount(int a_SlotNum, int a_AddToCount)
{
if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots))
{
LOGWARNING("%s: Invalid slot number %d out of %d slots, ignoring the call, returning empty item",
__FUNCTION__, a_SlotNum, m_NumSlots
);
return 0;
}
if (m_Slots[a_SlotNum].IsEmpty())
{
// The item is empty, it's not gonna change
return 0;
}
if (m_Slots[a_SlotNum].m_ItemCount < -a_AddToCount)
{
// Trying to remove more items than there already are, make the item empty
m_Slots[a_SlotNum].Empty();
TriggerListeners(a_SlotNum);
return 0;
}
m_Slots[a_SlotNum].m_ItemCount += a_AddToCount;
TriggerListeners(a_SlotNum);
return m_Slots[a_SlotNum].m_ItemCount;
}
int cItemGrid::HowManyItems(const cItem & a_Item)
{
int res = 0;
for (int i = 0; i < m_NumSlots; i++)
{
if (m_Slots[i].IsStackableWith(a_Item))
{
res += m_Slots[i].m_ItemCount;
}
}
return res;
}
bool cItemGrid::HasItems(const cItem & a_ItemStack)
{
int CurrentlyHave = HowManyItems(a_ItemStack);
return (CurrentlyHave >= a_ItemStack.m_ItemCount);
}
int cItemGrid::GetFirstEmptySlot(void) const
{
return GetNextEmptySlot(-1);
}
int cItemGrid::GetLastEmptySlot(void) const
{
for (int i = m_NumSlots - 1; i >= 0; i--)
{
if (m_Slots[i].IsEmpty())
{
return i;
}
}
return -1;
}
int cItemGrid::GetNextEmptySlot(int a_StartFrom) const
{
for (int i = a_StartFrom + 1; i < m_NumSlots; i++)
{
if (m_Slots[i].IsEmpty())
{
return i;
}
}
return -1;
}
void cItemGrid::CopyToItems(cItems & a_Items) const
{
for (int i = 0; i < m_NumSlots; i++)
{
if (!m_Slots[i].IsEmpty())
{
a_Items.push_back(m_Slots[i]);
}
} // for i - m_Slots[]
}
bool cItemGrid::DamageItem(int a_SlotNum, short a_Amount)
{
if ((a_SlotNum < 0) || (a_SlotNum >= m_NumSlots))
{
LOGWARNING("%s: invalid slot number %d out of %d slots, ignoring.", __FUNCTION__, a_SlotNum, m_NumSlots);
return false;
}
return m_Slots[a_SlotNum].DamageItem(a_Amount);
}
void cItemGrid::GenerateRandomLootWithBooks(const cLootProbab * a_LootProbabs, int a_CountLootProbabs, int a_NumSlots, int a_Seed)
{
// Calculate the total weight:
int TotalProbab = 1;
for (int i = 0; i < a_CountLootProbabs; i++)
{
TotalProbab += a_LootProbabs[i].m_Weight;
}
// Pick the loot items:
cNoise Noise(a_Seed);
for (int i = 0; i < a_NumSlots; i++)
{
int Rnd = (Noise.IntNoise1DInt(i) / 7);
int LootRnd = Rnd % TotalProbab;
Rnd >>= 8;
cItem CurrentLoot = cItem(E_ITEM_BOOK, 1, 0); // TODO: enchantment
for (int j = 0; j < a_CountLootProbabs; j++)
{
LootRnd -= a_LootProbabs[i].m_Weight;
if (LootRnd < 0)
{
CurrentLoot = a_LootProbabs[i].m_Item;
CurrentLoot.m_ItemCount = a_LootProbabs[i].m_MinAmount + (Rnd % (a_LootProbabs[i].m_MaxAmount - a_LootProbabs[i].m_MinAmount));
Rnd >>= 8;
break;
}
} // for j - a_LootProbabs[]
SetSlot(Rnd % m_NumSlots, CurrentLoot);
} // for i - NumSlots
}
void cItemGrid::AddListener(cListener & a_Listener)
{
cCSLock Lock(m_CSListeners);
ASSERT(!m_IsInTriggerListeners); // Must not call this while in TriggerListeners()
m_Listeners.push_back(&a_Listener);
}
void cItemGrid::RemoveListener(cListener & a_Listener)
{
cCSLock Lock(m_CSListeners);
ASSERT(!m_IsInTriggerListeners); // Must not call this while in TriggerListeners()
for (cListeners::iterator itr = m_Listeners.begin(), end = m_Listeners.end(); itr != end; ++itr)
{
if (*itr == &a_Listener)
{
m_Listeners.erase(itr);
return;
}
} // for itr - m_Listeners[]
}
void cItemGrid::TriggerListeners(int a_SlotNum)
{
cCSLock Lock(m_CSListeners);
m_IsInTriggerListeners = true;
for (cListeners::iterator itr = m_Listeners.begin(), end = m_Listeners.end(); itr != end; ++itr)
{
(*itr)->OnSlotChanged(this, a_SlotNum);
} // for itr - m_Listeners[]
m_IsInTriggerListeners = false;
}