PocketMine-MP 5.33.2 git-1133d49c924b4358c79d44eeb97dcbf56cb4d1eb
Loading...
Searching...
No Matches
BaseInventory.php
1<?php
2
3/*
4 *
5 * ____ _ _ __ __ _ __ __ ____
6 * | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
7 * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
8 * | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
9 * |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
10 *
11 * This program is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License as published by
13 * the Free Software Foundation, either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * @author PocketMine Team
17 * @link http://www.pocketmine.net/
18 *
19 *
20 */
21
22declare(strict_types=1);
23
24namespace pocketmine\inventory;
25
30use function array_slice;
31use function count;
32use function max;
33use function min;
34
41 protected int $maxStackSize = Inventory::MAX_STACK;
46 protected ObjectSet $listeners;
49
50 public function __construct(){
51 $this->listeners = new ObjectSet();
52 $this->validators = new ObjectSet();
53 }
54
55 public function getMaxStackSize() : int{
56 return $this->maxStackSize;
57 }
58
59 public function setMaxStackSize(int $size) : void{
60 $this->maxStackSize = $size;
61 }
62
63 abstract protected function internalSetItem(int $index, Item $item) : void;
64
65 public function setItem(int $index, Item $item) : void{
66 if($item->isNull()){
67 $item = VanillaItems::AIR();
68 }else{
69 $item = clone $item;
70 }
71
72 $oldItem = $this->getItem($index);
73
74 $this->internalSetItem($index, $item);
75 $this->onSlotChange($index, $oldItem);
76 }
77
82 abstract protected function internalSetContents(array $items) : void;
83
88 public function setContents(array $items) : void{
89 Utils::validateArrayValueType($items, function(Item $item) : void{});
90 if(count($items) > $this->getSize()){
91 $items = array_slice($items, 0, $this->getSize(), true);
92 }
93
94 $oldContents = $this->getContents(true);
95
96 $listeners = $this->listeners->toArray();
97 $this->listeners->clear();
98
99 $this->internalSetContents($items);
100
101 $this->listeners->add(...$listeners); //don't directly write, in case listeners were added while operation was in progress
102
103 $this->onContentChange($oldContents);
104 }
105
106 public function contains(Item $item) : bool{
107 $count = max(1, $item->getCount());
108 $checkTags = $item->hasNamedTag();
109 for($i = 0, $size = $this->getSize(); $i < $size; $i++){
110 $slotCount = $this->getMatchingItemCount($i, $item, $checkTags);
111 if($slotCount > 0){
112 $count -= $slotCount;
113 if($count <= 0){
114 return true;
115 }
116 }
117 }
118
119 return false;
120 }
121
122 public function all(Item $item) : array{
123 $slots = [];
124 $checkTags = $item->hasNamedTag();
125 for($i = 0, $size = $this->getSize(); $i < $size; $i++){
126 if($this->getMatchingItemCount($i, $item, $checkTags) > 0){
127 $slots[$i] = $this->getItem($i);
128 }
129 }
130
131 return $slots;
132 }
133
134 public function first(Item $item, bool $exact = false) : int{
135 $count = $exact ? $item->getCount() : max(1, $item->getCount());
136 $checkTags = $exact || $item->hasNamedTag();
137
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))){
141 return $i;
142 }
143 }
144
145 return -1;
146 }
147
148 public function firstEmpty() : int{
149 for($i = 0, $size = $this->getSize(); $i < $size; $i++){
150 if($this->isSlotEmpty($i)){
151 return $i;
152 }
153 }
154
155 return -1;
156 }
157
162 public function isSlotEmpty(int $index) : bool{
163 return $this->getItem($index)->isNull();
164 }
165
166 public function canAddItem(Item $item) : bool{
167 return $this->getAddableItemQuantity($item) === $item->getCount();
168 }
169
170 public function getAddableItemQuantity(Item $item) : int{
171 $count = $item->getCount();
172 $maxStackSize = min($this->getMaxStackSize(), $item->getMaxStackSize());
173
174 for($i = 0, $size = $this->getSize(); $i < $size; ++$i){
175 if($this->isSlotEmpty($i)){
176 $count -= $maxStackSize;
177 }else{
178 $slotCount = $this->getMatchingItemCount($i, $item, true);
179 if($slotCount > 0 && ($diff = $maxStackSize - $slotCount) > 0){
180 $count -= $diff;
181 }
182 }
183
184 if($count <= 0){
185 return $item->getCount();
186 }
187 }
188
189 return $item->getCount() - $count;
190 }
191
192 public function addItem(Item ...$slots) : array{
195 $itemSlots = [];
196 foreach($slots as $slot){
197 if(!$slot->isNull()){
198 $itemSlots[] = clone $slot;
199 }
200 }
201
203 $returnSlots = [];
204
205 foreach($itemSlots as $item){
206 $leftover = $this->internalAddItem($item);
207 if(!$leftover->isNull()){
208 $returnSlots[] = $leftover;
209 }
210 }
211
212 return $returnSlots;
213 }
214
215 private function internalAddItem(Item $newItem) : Item{
216 $emptySlots = [];
217
218 $maxStackSize = min($this->getMaxStackSize(), $newItem->getMaxStackSize());
219
220 for($i = 0, $size = $this->getSize(); $i < $size; ++$i){
221 if($this->isSlotEmpty($i)){
222 $emptySlots[] = $i;
223 continue;
224 }
225 $slotCount = $this->getMatchingItemCount($i, $newItem, true);
226 if($slotCount === 0){
227 continue;
228 }
229
230 if($slotCount < $maxStackSize){
231 $amount = min($maxStackSize - $slotCount, $newItem->getCount());
232 if($amount > 0){
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){
238 return $newItem;
239 }
240 }
241 }
242 }
243
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){
252 return $newItem;
253 }
254 }
255 }
256
257 return $newItem;
258 }
259
260 public function remove(Item $item) : void{
261 $checkTags = $item->hasNamedTag();
262
263 for($i = 0, $size = $this->getSize(); $i < $size; $i++){
264 if($this->getMatchingItemCount($i, $item, $checkTags) > 0){
265 $this->clear($i);
266 }
267 }
268 }
269
270 public function removeItem(Item ...$slots) : array{
271 $searchItems = [];
272 foreach($slots as $slot){
273 if(!$slot->isNull()){
274 $searchItems[] = clone $slot;
275 }
276 }
277
278 for($i = 0, $size = $this->getSize(); $i < $size; ++$i){
279 if($this->isSlotEmpty($i)){
280 continue;
281 }
282
283 foreach($searchItems as $index => $search){
284 $slotCount = $this->getMatchingItemCount($i, $search, $search->hasNamedTag());
285 if($slotCount > 0){
286 $amount = min($slotCount, $search->getCount());
287 $search->setCount($search->getCount() - $amount);
288
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]);
294 }
295 }
296 }
297
298 if(count($searchItems) === 0){
299 break;
300 }
301 }
302
303 return $searchItems;
304 }
305
306 public function clear(int $index) : void{
307 $this->setItem($index, VanillaItems::AIR());
308 }
309
310 public function clearAll() : void{
311 $this->setContents([]);
312 }
313
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);
319 }
320
321 protected function onSlotChange(int $index, Item $before) : void{
322 foreach($this->listeners as $listener){
323 $listener->onSlotChange($this, $index, $before);
324 }
325 }
326
331 protected function onContentChange(array $itemsBefore) : void{
332 foreach($this->listeners as $listener){
333 $listener->onContentChange($this, $itemsBefore);
334 }
335 }
336
337 public function slotExists(int $slot) : bool{
338 return $slot >= 0 && $slot < $this->getSize();
339 }
340
341 public function getListeners() : ObjectSet{
342 return $this->listeners;
343 }
344
345 public function getSlotValidators() : ObjectSet{
346 return $this->validators;
347 }
348}
first(Item $item, bool $exact=false)
setItem(int $index, Item $item)