1
0
Fork 0

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
This commit is contained in:
madmaxoft@gmail.com 2013-05-24 07:30:39 +00:00
parent 3b429a9bdf
commit cf87169737
25 changed files with 1581 additions and 534 deletions

View File

@ -1,36 +1,39 @@
function HandleItemCommand( Split, Player )
if( #Split ~= 2 and #Split ~=3 ) then
Player:SendMessage( cChatColor.Green .. "Usage: /item [ItemType/Name:Dmg] <Amount>" )
return true
function HandleItemCommand(Split, Player)
if ((#Split ~= 2) and (#Split ~=3)) then
Player:SendMessage(cChatColor.Green .. "Usage: /item [ItemType/Name:Dmg] <Amount>");
return true;
end
local Item = cItem(E_ITEM_EMPTY, 1)
local FoundItem = StringToItem( Split[2], Item )
local Item = cItem(E_ITEM_EMPTY, 1);
local FoundItem = StringToItem(Split[2], Item);
if( IsValidItem( Item.m_ItemType ) == false ) then -- StringToItem does not check if item is valid
if not(IsValidItem(Item.m_ItemType)) then -- StringToItem does not check if item is valid
FoundItem = false
end
if( FoundItem == false ) then
if not(FoundItem) then
Player:SendMessage( cChatColor.Green .. "Invalid Item type / name !" )
return true
end
if( #Split == 3 ) then
ItemAmount = tonumber( Split[3] )
if( ItemAmount == nil or ItemAmount < 1 or ItemAmount > 512 ) then
Player:SendMessage( cChatColor.Green .. "Invalid Amount !" )
return true
else
Item.m_ItemCount = ItemAmount
local ItemAmount = 1;
if (#Split == 3) then
ItemAmount = tonumber(Split[3]);
if ((ItemAmount == nil) or (ItemAmount < 1) or (ItemAmount > 512)) then
Player:SendMessage(cChatColor.Green .. "Invalid Amount!");
return true;
end
end
if( Player:GetInventory():AddItem( Item ) == true ) then
Player:SendMessage( cChatColor.Green .. "There you go !" )
LOG("Gave " .. Player:GetName() .. " " .. Item.m_ItemCount .. " times " .. Item.m_ItemType .. ":" .. Item.m_ItemDamage)
Item.m_ItemCount = ItemAmount;
local ItemsGiven = Player:GetInventory():AddItem(Item);
if (ItemsGiven == ItemAmount) then
Player:SendMessage( cChatColor.Green .. "There you go !");
LOG("Gave " .. Player:GetName() .. " " .. Item.m_ItemCount .. " times " .. Item.m_ItemType .. ":" .. Item.m_ItemDamage);
else
Player:SendMessage( cChatColor.Green .. "Not enough space in inventory !" )
Player:SendMessage(cChatColor.Green .. "Not enough space in inventory, only gave " .. ItemsGiven);
LOG("Player " .. Player:GetName() .. " asked for " .. Item.m_ItemCount .. " times " .. Item.m_ItemType .. ":" .. Item.m_ItemDamage ..", but only could fit " .. ItemsGiven);
end
return true
return true;
end

View File

@ -456,10 +456,10 @@ end
function HandleWoolCmd(Split, Player)
local Wool = cItem(E_BLOCK_WOOL, 1, E_META_WOOL_BLUE);
Player:GetInventory():SetSlot(5, Wool);
Player:GetInventory():SetSlot(6, Wool);
Player:GetInventory():SetSlot(7, Wool);
Player:GetInventory():SetSlot(8, Wool);
Player:GetInventory():SetArmorSlot(0, Wool);
Player:GetInventory():SetArmorSlot(1, Wool);
Player:GetInventory():SetArmorSlot(2, Wool);
Player:GetInventory():SetArmorSlot(3, Wool);
Player:SendMessage("You have been bluewooled :)");
return true;
end

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
/*
** Lua binding: AllToLua
** Generated automatically by tolua++-1.0.92 on 05/21/13 14:54:11.
** Generated automatically by tolua++-1.0.92 on 05/24/13 09:11:00.
*/
/* Exported function */

View File

@ -29,8 +29,7 @@ public:
case E_ITEM_WATER_BUCKET:
{
a_World->SetBlockMeta( a_BlockX, a_BlockY, a_BlockZ, 3 );
cItem Item(a_Player->GetEquippedItem().m_ItemType, 1);
a_Player->GetInventory().RemoveItem(Item);
a_Player->GetInventory().RemoveOneEquippedItem();
cItem NewItem(E_ITEM_BUCKET, 1);
a_Player->GetInventory().AddItem(NewItem);
break;
@ -39,9 +38,8 @@ public:
{
if( Meta > 0 )
{
a_World->SetBlockMeta( a_BlockX, a_BlockY, a_BlockZ, --Meta );
cItem Item(a_Player->GetEquippedItem().m_ItemType, 1);
a_Player->GetInventory().RemoveItem(Item);
a_World->SetBlockMeta( a_BlockX, a_BlockY, a_BlockZ, --Meta);
a_Player->GetInventory().RemoveOneEquippedItem();
cItem NewItem(E_ITEM_POTIONS, 1, 0);
a_Player->GetInventory().AddItem(NewItem);
}

View File

@ -86,10 +86,9 @@ public:
}
}
if (a_Player->GetGameMode() != eGameMode_Creative)
if (a_Player->GetGameMode() != gmCreative)
{
cItem Item(a_Player->GetEquippedItem().m_ItemType, 1);
a_Player->GetInventory().RemoveItem(Item);
a_Player->GetInventory().RemoveOneEquippedItem();
}
a_World->SetBlockMeta(a_BlockX, a_BlockY, a_BlockZ, Meta);
}

View File

@ -791,7 +791,7 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, c
if (ItemHandler->EatItem(m_Player, &Item))
{
ItemHandler->OnFoodEaten(World, m_Player, &Item);
m_Player->GetInventory().RemoveItem(Item);
m_Player->GetInventory().RemoveOneEquippedItem();
return;
}
}
@ -877,10 +877,9 @@ void cClientHandle::HandlePlaceBlock(int a_BlockX, int a_BlockY, int a_BlockZ, c
// The actual block placement:
World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, BlockType, BlockMeta);
if (m_Player->GetGameMode() == eGameMode_Survival)
if (m_Player->GetGameMode() != gmCreative)
{
cItem Item(m_Player->GetEquippedItem().m_ItemType, 1);
m_Player->GetInventory().RemoveItem(Item);
m_Player->GetInventory().RemoveOneEquippedItem();
}
NewBlock->OnPlacedByPlayer(World, m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ, BlockType, BlockMeta);

View File

@ -18,13 +18,16 @@
cInventory::cInventory(cPlayer & a_Owner) :
m_ArmorSlots (1, 4), // 1 x 4 slots
m_InventorySlots(9, 3), // 9 x 3 slots
m_HotbarSlots (9, 1), // 9 x 1 slots
m_Owner(a_Owner)
{
m_CraftSlots = m_Slots + c_CraftOffset;
m_ArmorSlots = m_Slots + c_ArmorOffset;
m_MainSlots = m_Slots + c_MainOffset;
m_HotSlots = m_Slots + c_HotOffset;
// Ask each ItemGrid to report changes to us:
m_ArmorSlots.AddListener(*this);
m_InventorySlots.AddListener(*this);
m_HotbarSlots.AddListener(*this);
SetEquippedSlotNum(0);
}
@ -32,82 +35,130 @@ cInventory::cInventory(cPlayer & a_Owner) :
cInventory::~cInventory()
void cInventory::Clear(void)
{
/*
// TODO
cWindow wnd = GetWindow();
if (wnd != NULL)
{
wnd->Close(*m_Owner);
}
CloseWindow();
*/
m_ArmorSlots.Clear();
m_InventorySlots.Clear();
m_HotbarSlots.Clear();
}
bool cInventory::AddItem( cItem & a_Item )
int cInventory::HowManyCanFit(const cItem & a_ItemStack, bool a_ConsiderEmptySlots)
{
cItem BackupSlots[c_NumSlots];
memcpy( BackupSlots, m_Slots, c_NumSlots * sizeof( cItem ) );
return HowManyCanFit(a_ItemStack, 0, invNumSlots - 1, a_ConsiderEmptySlots);
}
bool ChangedSlots[c_NumSlots];
memset( ChangedSlots, false, c_NumSlots * sizeof( bool ) );
if( a_Item.m_ItemCount > 0 ) AddToBar( a_Item, c_HotOffset, c_HotSlots, ChangedSlots, 0 );
if( a_Item.m_ItemCount > 0 ) AddToBar( a_Item, c_MainOffset, c_MainSlots, ChangedSlots, 0 );
if( a_Item.m_ItemCount > 0 ) AddToBar( a_Item, c_HotOffset, c_HotSlots, ChangedSlots, 2 );
if( a_Item.m_ItemCount > 0 ) AddToBar( a_Item, c_MainOffset, c_MainSlots, ChangedSlots, 2 );
if( a_Item.m_ItemCount > 0 ) // Could not add all items
int cInventory::HowManyCanFit(const cItem & a_ItemStack, int a_BeginSlotNum, int a_EndSlotNum, bool a_ConsiderEmptySlots)
{
if ((a_BeginSlotNum < 0) || (a_BeginSlotNum >= invNumSlots))
{
// retore backup
memcpy( m_Slots, BackupSlots, c_NumSlots * sizeof( cItem ) );
return false;
LOGWARNING("%s: Bad BeginSlotNum, got %d, there are %d slots; correcting to 0.", __FUNCTION__, a_BeginSlotNum, invNumSlots - 1);
a_BeginSlotNum = 0;
}
if ((a_EndSlotNum < 0) || (a_EndSlotNum >= invNumSlots))
{
LOGWARNING("%s: Bad EndSlotNum, got %d, there are %d slots; correcting to %d.", __FUNCTION__, a_BeginSlotNum, invNumSlots, invNumSlots - 1);
a_EndSlotNum = invNumSlots - 1;
}
if (a_BeginSlotNum > a_EndSlotNum)
{
std::swap(a_BeginSlotNum, a_EndSlotNum);
}
for (unsigned int i = 0; i < c_NumSlots; i++)
char NumLeft = a_ItemStack.m_ItemCount;
int MaxStack = ItemHandler(a_ItemStack.m_ItemType)->GetMaxStackSize();
for (int i = a_BeginSlotNum; i <= a_EndSlotNum; i++)
{
if (ChangedSlots[i])
const cItem & Slot = GetSlot(i);
if (Slot.IsEmpty())
{
LOGD("cInventory::AddItem(): Item was added to %i ID:%i Count:%i", i, m_Slots[i].m_ItemType, m_Slots[i].m_ItemCount);
SendSlot(i);
NumLeft -= MaxStack;
}
else if (Slot.IsStackableWith(a_ItemStack))
{
NumLeft -= MaxStack - Slot.m_ItemCount;
}
if (NumLeft <= 0)
{
// All items fit
return a_ItemStack.m_ItemCount;
}
} // for i - m_Slots[]
return a_ItemStack.m_ItemCount - NumLeft;
}
int cInventory::AddItem(const cItem & a_Item, bool a_AllowNewStacks)
{
cItem ToAdd(a_Item);
int res = 0;
if (ItemCategory::IsArmor(a_Item.m_ItemType))
{
res = m_ArmorSlots.AddItem(ToAdd, a_AllowNewStacks);
ToAdd.m_ItemCount -= res;
if (ToAdd.m_ItemCount == 0)
{
return res;
}
}
return (a_Item.m_ItemCount == 0);
}
bool cInventory::AddItemAnyAmount( cItem & a_Item )
{
bool ChangedSlots[c_NumSlots];
memset( ChangedSlots, false, c_NumSlots * sizeof( bool ) );
char StartCount = a_Item.m_ItemCount;
if( a_Item.m_ItemCount > 0 ) AddToBar( a_Item, c_HotOffset, c_HotSlots, ChangedSlots, 0 );
if( a_Item.m_ItemCount > 0 ) AddToBar( a_Item, c_MainOffset, c_MainSlots, ChangedSlots, 0 );
if( a_Item.m_ItemCount > 0 ) AddToBar( a_Item, c_HotOffset, c_HotSlots, ChangedSlots, 2 );
if( a_Item.m_ItemCount > 0 ) AddToBar( a_Item, c_MainOffset, c_MainSlots, ChangedSlots, 2 );
res += m_HotbarSlots.AddItem(ToAdd, a_AllowNewStacks);
ToAdd.m_ItemCount = a_Item.m_ItemCount - res;
if (ToAdd.m_ItemCount == 0)
{
return res;
}
if (a_Item.m_ItemCount == StartCount)
return false;
res += m_InventorySlots.AddItem(ToAdd, a_AllowNewStacks);
return res;
}
for (unsigned int i = 0; i < c_NumSlots; i++)
int cInventory::AddItems(cItems & a_ItemStackList, bool a_AllowNewStacks)
{
int TotalAdded = 0;
for (cItems::iterator itr = a_ItemStackList.begin(); itr != a_ItemStackList.end();)
{
if (ChangedSlots[i])
int NumAdded = AddItem(*itr, a_AllowNewStacks);
if (itr->m_ItemCount == NumAdded)
{
LOGD("cInventory::AddItemAnyAmount(): Item was added to %i ID:%i Count:%i", i, m_Slots[i].m_ItemType, m_Slots[i].m_ItemCount);
SendSlot(i);
itr = a_ItemStackList.erase(itr);
}
else
{
itr->m_ItemCount -= NumAdded;
++itr;
}
TotalAdded += NumAdded;
}
return TotalAdded;
}
bool cInventory::RemoveOneEquippedItem(void)
{
if (m_HotbarSlots.GetSlot(m_EquippedSlotNum).IsEmpty())
{
return false;
}
m_HotbarSlots.ChangeSlotCount(m_EquippedSlotNum, -1);
return true;
}
@ -115,68 +166,22 @@ bool cInventory::AddItemAnyAmount( cItem & a_Item )
// TODO: Right now if you dont have enough items, the items you did have are removed, and the function returns false anyway
bool cInventory::RemoveItem(cItem & a_Item)
int cInventory::HowManyItems(const cItem & a_Item)
{
// First check equipped slot
if ((m_EquippedSlotNum >= 0) && (m_EquippedSlotNum < 9))
{
if (m_HotSlots[m_EquippedSlotNum].m_ItemType == a_Item.m_ItemType)
{
cItem & Item = m_HotSlots[m_EquippedSlotNum];
if (Item.m_ItemCount > a_Item.m_ItemCount)
{
Item.m_ItemCount -= a_Item.m_ItemCount;
SendSlot(m_EquippedSlotNum + c_HotOffset);
return true;
}
else if (Item.m_ItemCount > 0)
{
a_Item.m_ItemCount -= Item.m_ItemCount;
Item.Empty();
SendSlot(m_EquippedSlotNum + c_HotOffset);
}
}
}
// Then check other slotz
if (a_Item.m_ItemCount > 0)
{
for (int i = 0; i < c_MainSlots; i++)
{
cItem & Item = m_MainSlots[i];
if (Item.m_ItemType == a_Item.m_ItemType)
{
if (Item.m_ItemCount > a_Item.m_ItemCount)
{
Item.m_ItemCount -= a_Item.m_ItemCount;
SendSlot(i + c_MainOffset);
return true;
}
else if (Item.m_ItemCount > 0)
{
a_Item.m_ItemCount -= Item.m_ItemCount;
Item.Empty();
SendSlot(i + c_MainOffset);
}
}
}
}
return (a_Item.m_ItemCount == 0);
return
m_ArmorSlots.HowManyItems(a_Item) +
m_InventorySlots.HowManyItems(a_Item) +
m_HotbarSlots.HowManyItems(a_Item);
}
void cInventory::Clear()
bool cInventory::HasItems(const cItem & a_ItemStack)
{
for (unsigned int i = 0; i < ARRAYCOUNT(m_Slots); i++)
{
m_Slots[i].Empty();
}
// TODO: Broadcast / send the changes to wherever needed
int CurrentlyHave = HowManyItems(a_ItemStack);
return (CurrentlyHave >= a_ItemStack.m_ItemCount);
}
@ -185,29 +190,47 @@ void cInventory::Clear()
void cInventory::SetSlot(int a_SlotNum, const cItem & a_Item)
{
if ((a_SlotNum < 0) || (a_SlotNum >= ARRAYCOUNT(m_Slots)))
if ((a_SlotNum < 0) || (a_SlotNum >= invNumSlots))
{
LOGWARNING("%s requesting an invalid slot index: %d out of %d. Ignoring.", __FUNCTION__, a_SlotNum, ARRAYCOUNT(m_Slots));
LOGWARNING("%s: requesting an invalid slot index: %d out of %d. Ignoring.", __FUNCTION__, a_SlotNum, invNumSlots - 1);
return;
}
m_Slots[a_SlotNum] = a_Item;
// If an armor slot was touched, broadcast an EntityEquipment packet
if ((a_SlotNum >= c_ArmorOffset) && (a_SlotNum < c_MainOffset))
int GridSlotNum = 0;
cItemGrid * Grid = GetGridForSlotNum(a_SlotNum, GridSlotNum);
if (Grid == NULL)
{
m_Owner.GetWorld()->BroadcastEntityEquipment(m_Owner, SlotNumToEntityEquipmentID(a_SlotNum), a_Item, m_Owner.GetClientHandle());
LOGWARNING("%s(%d): requesting an invalid itemgrid. Ignoring.", __FUNCTION__, a_SlotNum);
return;
}
SendSlot(a_SlotNum);
Grid->SetSlot(GridSlotNum, a_Item);
}
void cInventory::SetHotBarSlot(int a_HotBarSlotNum, const cItem & a_Item)
void cInventory::SetArmorSlot(int a_ArmorSlotNum, const cItem & a_Item)
{
SetSlot(a_HotBarSlotNum + c_HotSlots, a_Item);
m_ArmorSlots.SetSlot(a_ArmorSlotNum, a_Item);
}
void cInventory::SetInventorySlot(int a_InventorySlotNum, const cItem & a_Item)
{
m_InventorySlots.SetSlot(a_InventorySlotNum, a_Item);
}
void cInventory::SetHotbarSlot(int a_HotBarSlotNum, const cItem & a_Item)
{
m_HotbarSlots.SetSlot(a_HotBarSlotNum, a_Item);
}
@ -216,27 +239,62 @@ void cInventory::SetHotBarSlot(int a_HotBarSlotNum, const cItem & a_Item)
const cItem & cInventory::GetSlot(int a_SlotNum) const
{
if ((a_SlotNum < 0) || (a_SlotNum >= ARRAYCOUNT(m_Slots)))
if ((a_SlotNum < 0) || (a_SlotNum >= invNumSlots))
{
LOGWARNING("%s requesting an invalid slot index: %d out of %d. Returning the first one instead.", __FUNCTION__, a_SlotNum, ARRAYCOUNT(m_Slots));
return m_Slots[0];
LOGWARNING("%s: requesting an invalid slot index: %d out of %d. Returning the first inventory slot instead.", __FUNCTION__, a_SlotNum, invNumSlots - 1);
return m_InventorySlots.GetSlot(0);
}
return m_Slots[a_SlotNum];
int GridSlotNum = 0;
const cItemGrid * Grid = GetGridForSlotNum(a_SlotNum, GridSlotNum);
if (Grid == NULL)
{
// Something went wrong, but we don't know what. We must return a value, so return the first inventory slot
LOGWARNING("%s(%d): requesting an invalid ItemGrid, returning the first inventory slot instead.", __FUNCTION__, a_SlotNum);
return m_InventorySlots.GetSlot(0);
}
return Grid->GetSlot(GridSlotNum);
}
const cItem & cInventory::GetHotBarSlot(int a_SlotNum) const
const cItem & cInventory::GetArmorSlot(int a_ArmorSlotNum) const
{
if ((a_SlotNum < 0) || (a_SlotNum >= 9))
if ((a_ArmorSlotNum < 0) || (a_ArmorSlotNum >= invArmorCount))
{
LOGWARNING("%s requesting an invalid slot index: %d out of 9. Returning the first one instead", __FUNCTION__, a_SlotNum);
return m_HotSlots[0];
LOGWARNING("%s: requesting an invalid slot index: %d out of %d. Returning the first one instead", __FUNCTION__, a_ArmorSlotNum, invArmorCount - 1);
return m_ArmorSlots.GetSlot(0);
}
return m_HotSlots[a_SlotNum];
return m_ArmorSlots.GetSlot(a_ArmorSlotNum);
}
const cItem & cInventory::GetInventorySlot(int a_InventorySlotNum) const
{
if ((a_InventorySlotNum < 0) || (a_InventorySlotNum >= invInventoryCount))
{
LOGWARNING("%s: requesting an invalid slot index: %d out of %d. Returning the first one instead", __FUNCTION__, a_InventorySlotNum, invInventoryCount - 1);
return m_InventorySlots.GetSlot(0);
}
return m_InventorySlots.GetSlot(a_InventorySlotNum);
}
const cItem & cInventory::GetHotbarSlot(int a_SlotNum) const
{
if ((a_SlotNum < 0) || (a_SlotNum >= invHotbarCount))
{
LOGWARNING("%s: requesting an invalid slot index: %d out of %d. Returning the first one instead", __FUNCTION__, a_SlotNum, invHotbarCount - 1);
return m_HotbarSlots.GetSlot(0);
}
return m_HotbarSlots.GetSlot(a_SlotNum);
}
@ -245,7 +303,7 @@ const cItem & cInventory::GetHotBarSlot(int a_SlotNum) const
const cItem & cInventory::GetEquippedItem(void) const
{
return GetHotBarSlot(m_EquippedSlotNum);
return GetHotbarSlot(m_EquippedSlotNum);
}
@ -254,14 +312,14 @@ const cItem & cInventory::GetEquippedItem(void) const
void cInventory::SetEquippedSlotNum(int a_SlotNum)
{
if ((a_SlotNum < 0) || (a_SlotNum >= 9))
if ((a_SlotNum < 0) || (a_SlotNum >= invHotbarCount))
{
LOGWARNING("%s requesting invalid slot index: %d out of 9. Setting 0 instead.", __FUNCTION__, a_SlotNum);
LOGWARNING("%s: requesting invalid slot index: %d out of %d. Setting 0 instead.", __FUNCTION__, a_SlotNum, invHotbarCount - 1);
m_EquippedSlotNum = 0;
}
else
{
m_EquippedSlotNum = (short)a_SlotNum;
m_EquippedSlotNum = a_SlotNum;
}
}
@ -271,7 +329,7 @@ void cInventory::SetEquippedSlotNum(int a_SlotNum)
bool cInventory::DamageEquippedItem(short a_Amount)
{
return DamageItem(c_HotOffset + m_EquippedSlotNum, a_Amount);
return DamageItem(invHotbarOffset + m_EquippedSlotNum, a_Amount);
}
@ -280,22 +338,27 @@ bool cInventory::DamageEquippedItem(short a_Amount)
bool cInventory::DamageItem(int a_SlotNum, short a_Amount)
{
if ((a_SlotNum < 0) || (a_SlotNum >= ARRAYCOUNT(m_Slots)))
if ((a_SlotNum < 0) || (a_SlotNum >= invNumSlots))
{
LOGWARNING("%s requesting an invalid slot index: %d out of %d", __FUNCTION__, a_SlotNum, ARRAYCOUNT(m_Slots));
LOGWARNING("%s: requesting an invalid slot index: %d out of %d", __FUNCTION__, a_SlotNum, invNumSlots - 1);
return false;
}
if (!m_Slots[a_SlotNum].DamageItem(a_Amount))
int GridSlotNum = 0;
cItemGrid * Grid = GetGridForSlotNum(a_SlotNum, GridSlotNum);
if (Grid == NULL)
{
LOGWARNING("%s(%d, %d): requesting an invalid grid, ignoring.", __FUNCTION__, a_SlotNum, a_Amount);
return false;
}
if (!Grid->DamageItem(GridSlotNum, a_Amount))
{
// The item has been damaged, but did not break yet
return false;
}
// The item has broken, remove it:
m_Slots[a_SlotNum].Empty();
SendSlot(a_SlotNum);
// TODO: If it was a special slot (armor / equipped), broadcast the change
Grid->EmptySlot(a_SlotNum);
return true;
}
@ -303,6 +366,17 @@ bool cInventory::DamageItem(int a_SlotNum, short a_Amount)
void cInventory::CopyToItems(cItems & a_Items)
{
m_ArmorSlots.CopyToItems(a_Items);
m_InventorySlots.CopyToItems(a_Items);
m_HotbarSlots.CopyToItems(a_Items);
}
void cInventory::SendWholeInventory(cClientHandle & a_Client)
{
a_Client.SendWholeInventory(*this);
@ -320,35 +394,14 @@ void cInventory::SendSlot(int a_SlotNum)
// Sanitize items that are not completely empty (ie. count == 0, but type != empty)
Item.Empty();
}
m_Owner.GetClientHandle()->SendInventorySlot(0, a_SlotNum, Item);
}
int cInventory::HowManyCanFit(short a_ItemType, short a_ItemDamage, int a_BeginSlot, int a_EndSlot)
{
int res = 0;
for (int i = a_BeginSlot; i <= a_EndSlot; i++)
{
if (
m_Slots[i].IsEmpty() ||
((m_Slots[i].m_ItemType == a_ItemType) && (m_Slots[i].m_ItemDamage == a_ItemDamage))
)
{
int MaxCount = ItemHandler(a_ItemType)->GetMaxStackSize();
ASSERT(m_Slots[i].m_ItemCount <= MaxCount);
res += MaxCount - m_Slots[i].m_ItemCount;
}
} // for i - m_Slots[]
return res;
m_Owner.GetClientHandle()->SendInventorySlot(0, a_SlotNum + 5, Item); // Slots in the client are numbered "+ 5" because of crafting grid and result
}
/*
int cInventory::MoveItem(short a_ItemType, short a_ItemDamage, int a_Count, int a_BeginSlot, int a_EndSlot)
{
int res = 0;
@ -378,21 +431,22 @@ int cInventory::MoveItem(short a_ItemType, short a_ItemDamage, int a_Count, int
// No more space to distribute to
return res;
}
*/
int cInventory::SlotNumToEntityEquipmentID(short a_SlotNum)
int cInventory::ArmorSlotNumToEntityEquipmentID(short a_ArmorSlotNum)
{
switch (a_SlotNum)
switch (a_ArmorSlotNum)
{
case 5: return 4; // Helmet
case 6: return 3; // Chestplate
case 7: return 2; // Leggings
case 8: return 1; // Boots
case 0: return 4; // Helmet
case 1: return 3; // Chestplate
case 2: return 2; // Leggings
case 3: return 1; // Boots
}
LOGWARN("%s: invalid slot number: %d", __FUNCTION__, a_SlotNum);
LOGWARN("%s: invalid armor slot number: %d", __FUNCTION__, a_ArmorSlotNum);
return 0;
}
@ -400,6 +454,7 @@ int cInventory::SlotNumToEntityEquipmentID(short a_SlotNum)
#if 0
bool cInventory::AddToBar( cItem & a_Item, const int a_Offset, const int a_Size, bool* a_bChangedSlots, int a_Mode /* = 0 */ )
{
// Fill already present stacks
@ -447,6 +502,7 @@ bool cInventory::AddToBar( cItem & a_Item, const int a_Offset, const int a_Size,
return true;
}
#endif
@ -454,11 +510,37 @@ bool cInventory::AddToBar( cItem & a_Item, const int a_Offset, const int a_Size,
void cInventory::SaveToJson(Json::Value & a_Value)
{
for(unsigned int i = 0; i < c_NumSlots; i++)
// The JSON originally included the 4 crafting slots and the result, so we have to put empty items there, too:
cItem EmptyItem;
Json::Value EmptyItemJson;
EmptyItem.GetJson(EmptyItemJson);
for (int i = 0; i < 5; i++)
{
a_Value.append(EmptyItemJson);
}
// The 4 armor slots follow:
for (int i = 0; i < invArmorCount; i++)
{
Json::Value JSON_Item;
m_Slots[i].GetJson( JSON_Item );
a_Value.append( JSON_Item );
m_ArmorSlots.GetSlot(i).GetJson(JSON_Item);
a_Value.append(JSON_Item);
}
// Next comes the main inventory:
for (int i = 0; i < invInventoryCount; i++)
{
Json::Value JSON_Item;
m_InventorySlots.GetSlot(i).GetJson(JSON_Item);
a_Value.append(JSON_Item);
}
// The hotbar is the last:
for (int i = 0; i < invHotbarCount; i++)
{
Json::Value JSON_Item;
m_HotbarSlots.GetSlot(i).GetJson(JSON_Item);
a_Value.append(JSON_Item);
}
}
@ -470,18 +552,124 @@ bool cInventory::LoadFromJson(Json::Value & a_Value)
{
int SlotIdx = 0;
for( Json::Value::iterator itr = a_Value.begin(); itr != a_Value.end(); ++itr )
for (Json::Value::iterator itr = a_Value.begin(); itr != a_Value.end(); ++itr, SlotIdx++)
{
m_Slots[SlotIdx].FromJson( *itr );
SlotIdx++;
if (SlotIdx >= ARRAYCOUNT(m_Slots))
cItem Item;
Item.FromJson(*itr);
// The JSON originally included the 4 crafting slots and the result slot, so we need to skip the first 5 items:
if (SlotIdx < 5)
{
continue;
}
// If we loaded all the slots, stop now, even if the JSON has more:
if (SlotIdx - 5 >= invNumSlots)
{
break;
}
}
int GridSlotNum = 0;
cItemGrid * Grid = GetGridForSlotNum(SlotIdx - 5, GridSlotNum);
ASSERT(Grid != NULL);
Grid->SetSlot(GridSlotNum, Item);
} // for itr - a_Value[]
return true;
}
const cItemGrid * cInventory::GetGridForSlotNum(int a_SlotNum, int & a_GridSlotNum) const
{
ASSERT(a_SlotNum >= 0);
if (a_SlotNum < invArmorCount)
{
a_GridSlotNum = a_SlotNum;
return &m_ArmorSlots;
}
a_SlotNum -= invArmorCount;
if (a_SlotNum < invInventoryCount)
{
a_GridSlotNum = a_SlotNum;
return &m_InventorySlots;
}
a_GridSlotNum = a_SlotNum - invInventoryCount;
return &m_HotbarSlots;
}
cItemGrid * cInventory::GetGridForSlotNum(int a_SlotNum, int & a_GridSlotNum)
{
ASSERT(a_SlotNum >= 0);
if (a_SlotNum < invArmorCount)
{
a_GridSlotNum = a_SlotNum;
return &m_ArmorSlots;
}
a_SlotNum -= invArmorCount;
if (a_SlotNum < invInventoryCount)
{
a_GridSlotNum = a_SlotNum;
return &m_InventorySlots;
}
a_GridSlotNum = a_SlotNum - invInventoryCount;
return &m_HotbarSlots;
}
void cInventory::OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum)
{
// Send the neccessary updates to whoever needs them
if (m_Owner.IsDestroyed())
{
// Owner is not (yet) valid, skip for now
return;
}
// Armor update needs broadcast to other players:
cWorld * World = m_Owner.GetWorld();
if ((a_ItemGrid == &m_ArmorSlots) && (World != NULL))
{
World->BroadcastEntityEquipment(
m_Owner, ArmorSlotNumToEntityEquipmentID(a_SlotNum),
m_ArmorSlots.GetSlot(a_SlotNum), m_Owner.GetClientHandle()
);
}
// Convert the grid-local a_SlotNum to our global SlotNum:
int Base = 0;
if (a_ItemGrid == &m_ArmorSlots)
{
Base = invArmorOffset;
}
else if (a_ItemGrid == &m_InventorySlots)
{
Base = invInventoryOffset;
}
else if (a_ItemGrid == &m_HotbarSlots)
{
Base = invHotbarOffset;
}
else
{
ASSERT(!"Unknown ItemGrid calling OnSlotChanged()");
return;
}
SendSlot(Base + a_SlotNum);
}

View File

@ -1,7 +1,7 @@
#pragma once
#include "Item.h"
#include "ItemGrid.h"
@ -18,36 +18,108 @@ class cPlayer;
// tolua_begin
class cInventory // tolua_export
{ // tolua_export
/** This class represents the player's inventory
The slots are divided into three areas:
- armor slots (1 x 4)
- inventory slots (9 x 3)
- hotbar slots (9 x 1)
The generic GetSlot(), SetSlot() and HowManyCanFit() functions take the index of the slots,
as if armor slots, inventory slots and then hotbar slots were put one after another.
You can use the invArmorOffset, invInventoryOffset and invHotbarOffset constants.
*/
class cInventory :
public cItemGrid::cListener
{
public:
// Counts and offsets to individual parts of the inventory, as used by GetSlot() / SetSlot() / HowManyCanFit():
enum
{
invArmorCount = 4,
invInventoryCount = 9 * 3,
invHotbarCount = 9,
invArmorOffset = 0,
invInventoryOffset = invArmorOffset + invArmorCount,
invHotbarOffset = invInventoryOffset + invInventoryCount,
invNumSlots = invHotbarOffset + invHotbarCount
} ;
// tolua_end
cInventory(cPlayer & a_Owner);
~cInventory();
// tolua_begin
void Clear(); // tolua_export
/// Removes all items from the entire inventory
void Clear(void);
// cItem * GetSlotsForType( int a_Type );
// int GetSlotCountForType( int a_Type );
/// Returns number of items out of a_ItemStack that can fit in the storage
int HowManyCanFit(const cItem & a_ItemStack, bool a_ConsiderEmptySlots);
bool AddItem( cItem & a_Item ); // tolua_export
bool AddItemAnyAmount( cItem & a_Item ); // tolua_export
bool RemoveItem( cItem & a_Item ); // tolua_export
/// Returns how many items of the specified type would fit into the slot range specified
int HowManyCanFit(const cItem & a_ItemStack, int a_BeginSlotNum, int a_EndSlotNum, bool a_ConsiderEmptySlots);
/** Adds as many items out of a_ItemStack as can fit.
If a_AllowNewStacks is set to false, only existing stacks can be topped up;
if a_AllowNewStacks is set to true, empty slots can be used for the rest.
Returns the number of items that fit.
*/
int AddItem(const cItem & a_ItemStack, bool a_AllowNewStacks = true);
void SaveToJson(Json::Value & a_Value);
bool LoadFromJson(Json::Value & a_Value);
/** Same as AddItem, but works on an entire list of item stacks.
The a_ItemStackList is modified to reflect the leftover items.
If a_AllowNewStacks is set to false, only existing stacks can be topped up;
if a_AllowNewStacks is set to true, empty slots can be used for the rest
Returns the total number of items that fit.
*/
int AddItems(cItems & a_ItemStackList, bool a_AllowNewStacks);
/// Removes one item out of the currently equipped item stack, returns true if successful, false if empty-handed
bool RemoveOneEquippedItem(void);
/// Returns the number of items of type a_Item that are stored
int HowManyItems(const cItem & a_Item);
/// Returns true if there are at least as many items of type a_ItemStack as in a_ItemStack
bool HasItems(const cItem & a_ItemStack);
/// Returns the cItemGrid object representing the armor slots
cItemGrid & GetArmorGrid(void) { return m_ArmorSlots; }
/// Returns the cItemGrid object representing the main inventory slots
cItemGrid & GetInventoryGrid(void) { return m_InventorySlots; }
/// Returns the cItemGrid object representing the hotbar slots
cItemGrid & GetHotbarGrid(void) { return m_HotbarSlots; }
/// Returns the player associated with this inventory
cPlayer & GetOwner(void) { return m_Owner; }
/// Copies the non-empty slots into a_ItemStacks; preserves the original a_Items contents
void CopyToItems(cItems & a_Items);
// tolua_end
void SendWholeInventory(cClientHandle & a_Client);
const cItem * GetSlots(void) const { return m_Slots; }
/// Returns the player associated with this inventory (const version)
const cPlayer & GetOwner(void) const { return m_Owner; }
// tolua_begin
const cItem & GetSlot(int a_SlotNum) const;
const cItem & GetHotBarSlot(int a_HotBarSlotNum) const;
const cItem & GetArmorSlot(int a_ArmorSlotNum) const;
const cItem & GetInventorySlot(int a_InventorySlotNum) const;
const cItem & GetHotbarSlot(int a_HotBarSlotNum) const;
const cItem & GetEquippedItem(void) const;
void SetSlot(int a_SlotNum, const cItem & a_Item);
void SetHotBarSlot(int a_HotBarSlotNum, const cItem & a_Item);
void SetArmorSlot(int a_ArmorSlotNum, const cItem & a_Item);
void SetInventorySlot(int a_InventorySlotNum, const cItem & a_Item);
void SetHotbarSlot(int a_HotBarSlotNum, const cItem & a_Item);
void SetEquippedSlotNum(int a_SlotNum);
int GetEquippedSlotNum(void) { return m_EquippedSlotNum; }
@ -58,48 +130,41 @@ public:
/// Adds the specified damage to the currently held item; deletes the item and returns true if the item broke.
bool DamageEquippedItem(short a_Amount = 1);
const cItem & GetEquippedHelmet (void) const { return m_Slots[c_ArmorOffset]; }
const cItem & GetEquippedChestplate(void) const { return m_Slots[c_ArmorOffset + 1]; }
const cItem & GetEquippedLeggings (void) const { return m_Slots[c_ArmorOffset + 2]; }
const cItem & GetEquippedBoots (void) const { return m_Slots[c_ArmorOffset + 3]; }
const cItem & GetEquippedHelmet (void) const { return m_ArmorSlots.GetSlot(0); }
const cItem & GetEquippedChestplate(void) const { return m_ArmorSlots.GetSlot(1); }
const cItem & GetEquippedLeggings (void) const { return m_ArmorSlots.GetSlot(2); }
const cItem & GetEquippedBoots (void) const { return m_ArmorSlots.GetSlot(3); }
/// Sends the slot contents to the owner
void SendSlot(int a_SlotNum);
// tolua_end
void SendSlot( int a_SlotNum ); // tolua_export
/// Returns how many items of the specified type would fit into the slot range specified
int HowManyCanFit(short a_ItemType, short a_ItemDamage, int a_BeginSlot, int a_EndSlot);
/// Moves items, fitting them into the slot range specified, up to a_Count items. Returns the number of items moved
int MoveItem(short a_ItemType, short a_ItemDamage, int a_Count, int a_BeginSlot, int a_EndSlot);
/// Converts an armor slot number into the ID for the EntityEquipment packet
static int ArmorSlotNumToEntityEquipmentID(short a_ArmorSlotNum);
static const unsigned int c_NumSlots = 45;
static const unsigned int c_MainSlots = 27;
static const unsigned int c_HotSlots = 9;
static const unsigned int c_CraftSlots = 4;
static const unsigned int c_ArmorSlots = 4;
static const unsigned int c_CraftOffset = 0;
static const unsigned int c_ArmorOffset = 5;
static const unsigned int c_MainOffset = 9;
static const unsigned int c_HotOffset = 36;
/// Converts a slot number into the ID for the EntityEquipment packet
static int SlotNumToEntityEquipmentID(short a_SlotNum);
void SaveToJson(Json::Value & a_Value);
bool LoadFromJson(Json::Value & a_Value);
protected:
bool AddToBar( cItem & a_Item, const int a_Offset, const int a_Size, bool* a_bChangedSlots, int a_Mode = 0 );
cItem m_Slots[c_NumSlots];
cItem * m_MainSlots;
cItem * m_CraftSlots;
cItem * m_ArmorSlots;
cItem * m_HotSlots;
cItemGrid m_ArmorSlots;
cItemGrid m_InventorySlots;
cItemGrid m_HotbarSlots;
int m_EquippedSlotNum;
cPlayer & m_Owner;
/// Returns the ItemGrid and the (grid-local) slot number for a (global) slot number; return NULL for invalid SlotNum
const cItemGrid * GetGridForSlotNum(int a_SlotNum, int & a_GridSlotNum) const;
/// Returns the ItemGrid and the (grid-local) slot number for a (global) slot number; return NULL for invalid SlotNum
cItemGrid * GetGridForSlotNum(int a_SlotNum, int & a_GridSlotNum);
// cItemGrid::cListener override:
virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) override;
}; // tolua_export

View File

@ -75,7 +75,7 @@ bool cItem::DamageItem(short a_Amount)
bool cItem::IsStackableWith(const cItem & a_OtherStack)
bool cItem::IsStackableWith(const cItem & a_OtherStack) const
{
if (a_OtherStack.m_ItemType != m_ItemType)
{

View File

@ -74,7 +74,7 @@ public:
inline bool IsDamageable(void) const { return (GetMaxDamage() > 0); }
/// Returns true if this itemstack can stack with the specified stack (types match, enchantments etc.) ItemCounts are ignored!
bool IsStackableWith(const cItem & a_OtherStack);
bool IsStackableWith(const cItem & a_OtherStack) const;
// tolua_end
void GetJson( Json::Value & a_OutValue ) const;

View File

@ -16,7 +16,8 @@ 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_Slots(new cItem[a_Width * a_Height]),
m_IsInTriggerListeners(false)
{
}
@ -149,6 +150,7 @@ void cItemGrid::SetSlot(int a_SlotNum, const cItem & a_Item)
return;
}
m_Slots[a_SlotNum] = a_Item;
TriggerListeners(a_SlotNum);
}
@ -164,11 +166,46 @@ void cItemGrid::SetSlot(int a_SlotNum, short a_ItemType, char a_ItemCount, short
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);
}
}
@ -203,10 +240,33 @@ int cItemGrid::HowManyCanFit(const cItem & a_ItemStack)
bool cItemGrid::AddItem(cItem & a_ItemStack)
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())
@ -214,40 +274,87 @@ bool cItemGrid::AddItem(cItem & a_ItemStack)
m_Slots[i] = a_ItemStack;
m_Slots[i].m_ItemCount = std::min(MaxStack, NumLeft);
NumLeft -= m_Slots[i].m_ItemCount;
}
else 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 true;
return a_ItemStack.m_ItemCount;
}
} // for i - m_Slots[]
return (a_ItemStack.m_ItemCount > NumLeft);
return (a_ItemStack.m_ItemCount - NumLeft);
}
bool cItemGrid::AddItems(cItems & a_ItemStackList)
int cItemGrid::AddItems(cItems & a_ItemStackList, bool a_AllowNewStacks)
{
bool res;
int TotalAdded = 0;
for (cItems::iterator itr = a_ItemStackList.begin(); itr != a_ItemStackList.end();)
{
res = AddItem(*itr) | res;
if (itr->IsEmpty())
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;
}
@ -256,6 +363,16 @@ bool cItemGrid::AddItems(cItems & a_ItemStackList)
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);
@ -312,6 +429,20 @@ void cItemGrid::CopyToItems(cItems & a_Items) const
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:
@ -347,3 +478,47 @@ void cItemGrid::GenerateRandomLootWithBooks(const cLootProbab * a_LootProbabs, i
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;
}

View File

@ -19,6 +19,16 @@ class cItemGrid
{
public:
// tolua_end
/// This class is used as a callback for when a slot changes
class cListener
{
public:
/// Called whenever a slot changes
virtual void OnSlotChanged(cItemGrid * a_ItemGrid, int a_SlotNum) = 0;
} ;
typedef std::vector<cListener *> cListeners;
cItemGrid(int a_Width, int a_Height);
~cItemGrid();
@ -48,17 +58,42 @@ public:
void SetSlot(int a_SlotNum, const cItem & a_Item);
void SetSlot(int a_SlotNum, short a_ItemType, char a_ItemCount, short a_ItemDamage);
// Empty the specified slot; Logs warning and doesn't set on invalid coords / slotnum
void EmptySlot(int a_X, int a_Y);
void EmptySlot(int a_SlotNum);
/// Sets all items as empty
void Clear(void);
/// Returns number of items out of a_ItemStack that can fit in the storage
int HowManyCanFit(const cItem & a_ItemStack);
/// Adds as many items out of a_ItemStack as can fit; the rest is left in a_ItemStack; returns true if any items fit.
bool AddItem(cItem & a_ItemStack);
/** Adds as many items out of a_ItemStack as can fit.
If a_AllowNewStacks is set to false, only existing stacks can be topped up;
if a_AllowNewStacks is set to true, empty slots can be used for the rest
Returns the number of items that fit.
*/
int AddItem(cItem & a_ItemStack, bool a_AllowNewStacks);
/// Same as AddItem, but works on an entire list of item stacks
bool AddItems(cItems & a_ItemStackList);
/** Same as AddItem, but works on an entire list of item stacks.
The a_ItemStackList is modified to reflect the leftover items.
If a_AllowNewStacks is set to false, only existing stacks can be topped up;
if a_AllowNewStacks is set to true, empty slots can be used for the rest
Returns the total number of items that fit.
*/
int AddItems(cItems & a_ItemStackList, bool a_AllowNewStacks);
/** Adds (or subtracts, if a_AddToCount is negative) to the count of items in the specified slot.
If the slot is empty, ignores the call.
Returns the new count.
*/
int ChangeSlotCount(int a_SlotNum, int a_AddToCount);
/// Returns the number of items of type a_Item that are stored
int HowManyItems(const cItem & a_Item);
/// Returns true if there are at least as many items of type a_ItemStack as in a_ItemStack
bool HasItems(const cItem & a_ItemStack);
/// Returns the index of the first empty slot; -1 if all full
int GetFirstEmptySlot(void) const;
@ -69,16 +104,26 @@ public:
/// Returns the index of the first empty slot following a_StartFrom (a_StartFrom is not checked)
int GetNextEmptySlot(int a_StartFrom) const;
/// Copies the contents into a cItems object
/// Copies the contents into a cItems object; preserves the original a_Items contents
void CopyToItems(cItems & a_Items) const;
/// Adds the specified damage to the specified item; returns true if the item broke (but the item is left intact)
bool DamageItem(int a_SlotNum, short a_Amount);
// tolua_end
/** Generates random loot from the specified loot probability table, with a chance of enchanted books added.
A total of a_NumSlots are taken by the loot.
Cannot export to Lua due to raw array a_LootProbabs.
Cannot export to Lua due to raw array a_LootProbabs. TODO: Make this exportable / export through ManualBindings.cpp with a Lua table as LootProbabs
*/
void GenerateRandomLootWithBooks(const cLootProbab * a_LootProbabs, int a_CountLootProbabs, int a_NumSlots, int a_Seed);
/// Adds a callback that gets called whenever a slot changes. Must not be called from within the listener callback!
void AddListener(cListener & a_Listener);
/// Removes a slot-change-callback. Must not be called from within the listener callback!
void RemoveListener(cListener & a_Listener);
// tolua_begin
@ -86,7 +131,14 @@ protected:
int m_Width;
int m_Height;
int m_NumSlots; // m_Width * m_Height, for easier validity checking in the access functions
cItem * m_Slots; // x + m_Width * y
cItem * m_Slots; // x + m_Width * y
cListeners m_Listeners; ///< Listeners which should be notified on slot changes; the pointers are not owned by this object
cCriticalSection m_CSListeners; ///< CS that guards the m_Listeners against multi-thread access
bool m_IsInTriggerListeners; ///< Set to true while TriggerListeners is running, to detect attempts to manipulate listener list while triggerring
/// Calls all m_Listeners for the specified slot number
void TriggerListeners(int a_SlotNum);
} ;
// tolua_end

View File

@ -81,8 +81,7 @@ public:
}
// Remove the bucket from the inventory
cItem Item(a_Item.m_ItemType, 1);
if (!a_Player->GetInventory().RemoveItem(Item))
if (!a_Player->GetInventory().RemoveOneEquippedItem())
{
LOG("Clicked with an empty bucket, but cannot remove one from the inventory? WTF?");
ASSERT(!"Inventory bucket mismatch");
@ -90,8 +89,7 @@ public:
}
// Give new bucket, filled with fluid:
Item.m_ItemType = NewItem;
Item.m_ItemCount = 1;
cItem Item(NewItem, 1);
a_Player->GetInventory().AddItem(Item);
// Remove water / lava block
@ -131,15 +129,13 @@ public:
if (a_Player->GetGameMode() != gmCreative)
{
// Remove fluid bucket, add empty bucket:
cItem Item(a_Item.m_ItemType, 1);
if (!a_Player->GetInventory().RemoveItem(Item))
if (!a_Player->GetInventory().RemoveOneEquippedItem())
{
LOG("Clicked with a full bucket, but cannot remove one from the inventory? WTF?");
ASSERT(!"Inventory bucket mismatch");
return false;
}
Item.m_ItemType = E_ITEM_BUCKET;
Item.m_ItemCount = 1;
cItem Item(E_ITEM_BUCKET, 1);
if (!a_Player->GetInventory().AddItem(Item))
{
return false;

View File

@ -22,15 +22,15 @@ public:
virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Dir) override
{
// TODO: Handle coloring the sheep, too (OnItemUseOnEntity maybe)
// Handle growing the plants:
if (a_Item.m_ItemDamage == E_META_DYE_WHITE)
{
if (a_World->GrowRipePlant(a_BlockX, a_BlockY, a_BlockZ, true))
{
if (a_Player->GetGameMode() != eGameMode_Creative)
if (a_Player->GetGameMode() != gmCreative)
{
cItem Item(a_Item.m_ItemType, 1, a_Item.m_ItemDamage);
a_Player->GetInventory().RemoveItem(Item);
a_Player->GetInventory().RemoveOneEquippedItem();
return true;
}
}

View File

@ -36,8 +36,7 @@ public:
}
else
{
cItem Item(a_Item.m_ItemType, 1);
if (a_Player->GetInventory().RemoveItem(Item))
if (a_Player->GetInventory().RemoveOneEquippedItem())
{
a_World->SetBlock(a_BlockX, a_BlockY, a_BlockZ, Block - 1, Meta); // Block - 1 simple hack to save one if statement
return true;

View File

@ -38,8 +38,7 @@ public:
if (a_Player->GetGameMode() != 1)
{
// The mob was spawned, "use" the item:
cItem Equipped(a_Player->GetEquippedItem());
a_Player->GetInventory().RemoveItem(Equipped);
a_Player->GetInventory().RemoveOneEquippedItem();
}
return true;
}

View File

@ -40,8 +40,7 @@ void cJukeboxEntity::UsedBy(cPlayer * a_Player)
if (HeldItem.m_ItemType >= 2256 && HeldItem.m_ItemType <= 2267)
{
m_Record = HeldItem.m_ItemType;
cItem Equipped(a_Player->GetInventory().GetEquippedItem());
a_Player->GetInventory().RemoveItem(Equipped);
a_Player->GetInventory().RemoveOneEquippedItem();
PlayRecord();
}
}

View File

@ -149,17 +149,17 @@ bool cPickup::CollectedBy(cPlayer * a_Dest)
return false;
}
if (a_Dest->GetInventory().AddItemAnyAmount(m_Item))
int NumAdded = a_Dest->GetInventory().AddItem(m_Item);
if (NumAdded > 0)
{
m_Item.m_ItemCount -= NumAdded;
m_World->BroadcastCollectPickup(*this, *a_Dest);
m_bCollected = true;
m_Timer = 0;
if (m_Item.m_ItemCount != 0)
if (m_Item.m_ItemCount == 0)
{
cItems Pickup;
Pickup.push_back(cItem(m_Item));
m_World->SpawnItemPickups(Pickup, GetPosX(), GetPosY(), GetPosZ());
// All of the pickup has been collected, schedule the pickup for destroying
m_bCollected = true;
}
m_Timer = 0;
return true;
}

View File

@ -360,25 +360,18 @@ void cPlayer::KilledBy(cPawn * a_Killer)
m_bVisible = false; // So new clients don't see the player
// Puke out all the items
const cItem * Items = m_Inventory.GetSlots();
cItems Pickups;
for (unsigned int i = 1; i < m_Inventory.c_NumSlots; ++i)
{
if (!Items[i].IsEmpty())
{
Pickups.push_back(Items[i]);
}
}
m_Inventory.CopyToItems(Pickups);
m_Inventory.Clear();
m_World->SpawnItemPickups(Pickups, GetPosX(), GetPosY(), GetPosZ(), 10);
SaveToDisk(); // Save it, yeah the world is a tough place !
SaveToDisk(); // Save it, yeah the world is a tough place !
}
void cPlayer::Respawn()
void cPlayer::Respawn(void)
{
m_Health = GetMaxHealth();
@ -768,7 +761,7 @@ void cPlayer::TossItem(
)
{
cItems Drops;
if (a_CreateType)
if (a_CreateType != 0)
{
// Just create item without touching the inventory (used in creative mode)
Drops.push_back(cItem(a_CreateType, a_Amount, a_CreateHealth));
@ -800,8 +793,7 @@ void cPlayer::TossItem(
cItem DroppedItem(GetInventory().GetEquippedItem());
if (!DroppedItem.IsEmpty())
{
DroppedItem.m_ItemCount = 1;
if (GetInventory().RemoveItem(DroppedItem))
if (GetInventory().RemoveOneEquippedItem())
{
DroppedItem.m_ItemCount = 1; // RemoveItem decreases the count, so set it to 1 again
Drops.push_back(DroppedItem);

View File

@ -81,10 +81,12 @@ public:
void LoginSetGameMode( eGameMode a_GameMode );
void SetIP(const AString & a_IP);
// Tries to move to a new position, with collision checks and stuff
/// Tries to move to a new position, with collision checks and stuff
virtual void MoveTo( const Vector3d & a_NewPos ); // tolua_export
cWindow* GetWindow() { return m_CurrentWindow; }
cWindow * GetWindow(void) { return m_CurrentWindow; }
const cWindow * GetWindow(void) const { return m_CurrentWindow; }
void OpenWindow( cWindow* a_Window );
void CloseWindow(char a_WindowType);

View File

@ -874,8 +874,7 @@ void cProtocol125::SendWeather(eWeather a_Weather)
void cProtocol125::SendWholeInventory(const cInventory & a_Inventory)
{
cCSLock Lock(m_CSPacket);
SendWindowSlots(0, a_Inventory.c_NumSlots, a_Inventory.GetSlots());
SendWholeInventory(*(a_Inventory.GetOwner().GetWindow()));
}

View File

@ -438,14 +438,24 @@ void cProtocol132::SendWholeInventory(const cWindow & a_Window)
{
// 1.3.2 requires player inventory slots to be sent as SetSlot packets,
// otherwise it sometimes fails to update the window
// Send the entire window:
super::SendWholeInventory(a_Window);
const cItem * Slots = m_Client->GetPlayer()->GetInventory().GetSlots();
int BaseOffset = a_Window.GetNumSlots() - cInventory::c_NumSlots + cInventory::c_MainOffset; // the number of non-inventory slots the window has; inventory follows
// Send the player inventory and hotbar:
const cInventory & Inventory = m_Client->GetPlayer()->GetInventory();
int BaseOffset = a_Window.GetNumSlots() - (cInventory::invNumSlots - cInventory::invInventoryOffset); // Number of non-inventory slots
char WindowID = a_Window.GetWindowID();
for (int i = 0; i < cInventory::c_NumSlots - cInventory::c_MainOffset; i++)
for (int i = 0; i < cInventory::invInventoryCount; i++)
{
SendInventorySlot(WindowID, BaseOffset + i, Slots[i + cInventory::c_MainOffset]);
} // for i - Slots[]
SendInventorySlot(WindowID, BaseOffset + i, Inventory.GetInventorySlot(i));
} // for i - Inventory[]
BaseOffset += cInventory::invInventoryCount;
for (int i = 0; i < cInventory::invHotbarCount; i++)
{
SendInventorySlot(WindowID, BaseOffset + i, Inventory.GetHotbarSlot(i));
} // for i - Hotbar[]
// Send even the item being dragged:
SendInventorySlot(-1, -1, m_Client->GetPlayer()->GetDraggingItem());
}

View File

@ -613,7 +613,7 @@ void cSlotAreaInventoryBase::Clicked(cPlayer & a_Player, int a_SlotNum, eClickAc
const cItem * cSlotAreaInventoryBase::GetSlot(int a_SlotNum, cPlayer & a_Player)
{
// a_SlotNum ranges from 0 to 35, map that to the player's inventory slots 9 to 44
// a_SlotNum ranges from 0 to 35, map that to the player's inventory slots according to the internal offset
return &a_Player.GetInventory().GetSlot(a_SlotNum + m_SlotOffset);
}

View File

@ -8,7 +8,7 @@
#pragma once
#include "../Item.h"
#include "../Inventory.h"
@ -97,7 +97,7 @@ class cSlotAreaInventory :
public:
cSlotAreaInventory(cWindow & a_ParentWindow) :
cSlotAreaInventoryBase(27, 9, a_ParentWindow) // 27 slots, starting at inventory index 9
cSlotAreaInventoryBase(cInventory::invInventoryCount, cInventory::invInventoryOffset, a_ParentWindow)
{
}
} ;
@ -114,7 +114,7 @@ class cSlotAreaHotBar :
public:
cSlotAreaHotBar(cWindow & a_ParentWindow) :
cSlotAreaInventoryBase(9, 36, a_ParentWindow)
cSlotAreaInventoryBase(cInventory::invHotbarCount, cInventory::invHotbarOffset, a_ParentWindow)
{
}
} ;
@ -129,7 +129,7 @@ class cSlotAreaArmor :
{
public:
cSlotAreaArmor(cWindow & a_ParentWindow) :
cSlotAreaInventoryBase(4, 5, a_ParentWindow)
cSlotAreaInventoryBase(cInventory::invArmorCount, cInventory::invArmorOffset, a_ParentWindow)
{
}