41 protected int $maxStackSize = Inventory::MAX_STACK;
50 public function __construct(){
56 return $this->maxStackSize;
60 $this->maxStackSize = $size;
63 abstract protected function internalSetItem(
int $index,
Item $item) : void;
67 $item = VanillaItems::AIR();
72 $oldItem = $this->getItem($index);
74 $this->internalSetItem($index, $item);
75 $this->onSlotChange($index, $oldItem);
89 Utils::validateArrayValueType($items, function(
Item $item) : void{});
90 if(count($items) > $this->getSize()){
91 $items = array_slice($items, 0, $this->getSize(),
true);
94 $oldContents = $this->getContents(
true);
96 $listeners = $this->listeners->
toArray();
97 $this->listeners->clear();
99 $this->internalSetContents($items);
101 $this->listeners->add(...$listeners);
103 $this->onContentChange($oldContents);
107 $count = max(1, $item->getCount());
109 for($i = 0, $size = $this->getSize(); $i < $size; $i++){
110 $slotCount = $this->getMatchingItemCount($i, $item, $checkTags);
112 $count -= $slotCount;
125 for($i = 0, $size = $this->getSize(); $i < $size; $i++){
126 if($this->getMatchingItemCount($i, $item, $checkTags) > 0){
127 $slots[$i] = $this->getItem($i);
134 public function first(
Item $item,
bool $exact =
false) : int{
135 $count = $exact ? $item->getCount() : max(1, $item->getCount());
138 for($i = 0, $size = $this->getSize(); $i < $size; $i++){
139 $slotCount = $this->getMatchingItemCount($i, $item, $checkTags);
140 if($slotCount > 0 && ($slotCount === $count || (!$exact && $slotCount > $count))){
149 for($i = 0, $size = $this->getSize(); $i < $size; $i++){
150 if($this->isSlotEmpty($i)){
163 return $this->getItem($index)->isNull();
167 return $this->getAddableItemQuantity($item) === $item->getCount();
171 $count = $item->getCount();
172 $maxStackSize = min($this->getMaxStackSize(), $item->
getMaxStackSize());
174 for($i = 0, $size = $this->getSize(); $i < $size; ++$i){
175 if($this->isSlotEmpty($i)){
176 $count -= $maxStackSize;
178 $slotCount = $this->getMatchingItemCount($i, $item,
true);
179 if($slotCount > 0 && ($diff = $maxStackSize - $slotCount) > 0){
185 return $item->getCount();
189 return $item->getCount() - $count;
196 foreach($slots as $slot){
197 if(!$slot->isNull()){
198 $itemSlots[] = clone $slot;
205 foreach($itemSlots as $item){
206 $leftover = $this->internalAddItem($item);
207 if(!$leftover->isNull()){
208 $returnSlots[] = $leftover;
215 private function internalAddItem(Item $newItem) : Item{
218 $maxStackSize = min($this->getMaxStackSize(), $newItem->getMaxStackSize());
220 for($i = 0, $size = $this->getSize(); $i < $size; ++$i){
221 if($this->isSlotEmpty($i)){
225 $slotCount = $this->getMatchingItemCount($i, $newItem,
true);
226 if($slotCount === 0){
230 if($slotCount < $maxStackSize){
231 $amount = min($maxStackSize - $slotCount, $newItem->getCount());
233 $newItem->setCount($newItem->getCount() - $amount);
234 $slotItem = $this->getItem($i);
235 $slotItem->setCount($slotItem->getCount() + $amount);
236 $this->setItem($i, $slotItem);
237 if($newItem->getCount() <= 0){
244 if(count($emptySlots) > 0){
245 foreach($emptySlots as $slotIndex){
246 $amount = min($maxStackSize, $newItem->getCount());
247 $newItem->setCount($newItem->getCount() - $amount);
248 $slotItem = clone $newItem;
249 $slotItem->setCount($amount);
250 $this->setItem($slotIndex, $slotItem);
251 if($newItem->getCount() <= 0){
260 public function remove(
Item $item) : void{
261 $checkTags = $item->hasNamedTag();
263 for($i = 0, $size = $this->getSize(); $i < $size; $i++){
264 if($this->getMatchingItemCount($i, $item, $checkTags) > 0){
272 foreach($slots as $slot){
273 if(!$slot->isNull()){
274 $searchItems[] = clone $slot;
278 for($i = 0, $size = $this->getSize(); $i < $size; ++$i){
279 if($this->isSlotEmpty($i)){
283 foreach($searchItems as $index => $search){
284 $slotCount = $this->getMatchingItemCount($i, $search, $search->hasNamedTag());
286 $amount = min($slotCount, $search->getCount());
287 $search->setCount($search->getCount() - $amount);
289 $slotItem = $this->getItem($i);
290 $slotItem->setCount($slotItem->getCount() - $amount);
291 $this->setItem($i, $slotItem);
292 if($search->getCount() <= 0){
293 unset($searchItems[$index]);
298 if(count($searchItems) === 0){
306 public function clear(
int $index) : void{
311 $this->setContents([]);
314 public function swap(
int $slot1,
int $slot2) : void{
315 $i1 = $this->getItem($slot1);
316 $i2 = $this->getItem($slot2);
317 $this->setItem($slot1, $i2);
318 $this->setItem($slot2, $i1);
321 protected function onSlotChange(
int $index,
Item $before) : void{
322 foreach($this->listeners as $listener){
323 $listener->onSlotChange($this, $index, $before);
332 foreach($this->listeners as $listener){
333 $listener->onContentChange($this, $itemsBefore);
338 return $slot >= 0 && $slot < $this->getSize();
342 return $this->listeners;
346 return $this->validators;