PocketMine-MP 5.35.1 git-e32e836dad793a3a3c8ddd8927c00e112b1e576a
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
158 public function canAddItem(Item $item) : bool{
159 return $this->getAddableItemQuantity($item) === $item->getCount();
160 }
161
162 public function getAddableItemQuantity(Item $item) : int{
163 $count = $item->getCount();
164 $maxStackSize = min($this->getMaxStackSize(), $item->getMaxStackSize());
165
166 for($i = 0, $size = $this->getSize(); $i < $size; ++$i){
167 if($this->isSlotEmpty($i)){
168 $count -= $maxStackSize;
169 }else{
170 $slotCount = $this->getMatchingItemCount($i, $item, true);
171 if($slotCount > 0 && ($diff = $maxStackSize - $slotCount) > 0){
172 $count -= $diff;
173 }
174 }
175
176 if($count <= 0){
177 return $item->getCount();
178 }
179 }
180
181 return $item->getCount() - $count;
182 }
183
184 public function addItem(Item ...$slots) : array{
187 $itemSlots = [];
188 foreach($slots as $slot){
189 if(!$slot->isNull()){
190 $itemSlots[] = clone $slot;
191 }
192 }
193
195 $returnSlots = [];
196
197 foreach($itemSlots as $item){
198 $leftover = $this->internalAddItem($item);
199 if(!$leftover->isNull()){
200 $returnSlots[] = $leftover;
201 }
202 }
203
204 return $returnSlots;
205 }
206
207 private function internalAddItem(Item $newItem) : Item{
208 $emptySlots = [];
209
210 $maxStackSize = min($this->getMaxStackSize(), $newItem->getMaxStackSize());
211
212 for($i = 0, $size = $this->getSize(); $i < $size; ++$i){
213 if($this->isSlotEmpty($i)){
214 $emptySlots[] = $i;
215 continue;
216 }
217 $slotCount = $this->getMatchingItemCount($i, $newItem, true);
218 if($slotCount === 0){
219 continue;
220 }
221
222 if($slotCount < $maxStackSize){
223 $amount = min($maxStackSize - $slotCount, $newItem->getCount());
224 if($amount > 0){
225 $newItem->setCount($newItem->getCount() - $amount);
226 $slotItem = $this->getItem($i);
227 $slotItem->setCount($slotItem->getCount() + $amount);
228 $this->setItem($i, $slotItem);
229 if($newItem->getCount() <= 0){
230 return $newItem;
231 }
232 }
233 }
234 }
235
236 if(count($emptySlots) > 0){
237 foreach($emptySlots as $slotIndex){
238 $amount = min($maxStackSize, $newItem->getCount());
239 $newItem->setCount($newItem->getCount() - $amount);
240 $slotItem = clone $newItem;
241 $slotItem->setCount($amount);
242 $this->setItem($slotIndex, $slotItem);
243 if($newItem->getCount() <= 0){
244 return $newItem;
245 }
246 }
247 }
248
249 return $newItem;
250 }
251
252 public function remove(Item $item) : void{
253 $checkTags = $item->hasNamedTag();
254
255 for($i = 0, $size = $this->getSize(); $i < $size; $i++){
256 if($this->getMatchingItemCount($i, $item, $checkTags) > 0){
257 $this->clear($i);
258 }
259 }
260 }
261
262 public function removeItem(Item ...$slots) : array{
263 $searchItems = [];
264 foreach($slots as $slot){
265 if(!$slot->isNull()){
266 $searchItems[] = clone $slot;
267 }
268 }
269
270 for($i = 0, $size = $this->getSize(); $i < $size; ++$i){
271 if($this->isSlotEmpty($i)){
272 continue;
273 }
274
275 foreach($searchItems as $index => $search){
276 $slotCount = $this->getMatchingItemCount($i, $search, $search->hasNamedTag());
277 if($slotCount > 0){
278 $amount = min($slotCount, $search->getCount());
279 $search->setCount($search->getCount() - $amount);
280
281 $slotItem = $this->getItem($i);
282 $slotItem->setCount($slotItem->getCount() - $amount);
283 $this->setItem($i, $slotItem);
284 if($search->getCount() <= 0){
285 unset($searchItems[$index]);
286 }
287 }
288 }
289
290 if(count($searchItems) === 0){
291 break;
292 }
293 }
294
295 return $searchItems;
296 }
297
298 public function clear(int $index) : void{
299 $this->setItem($index, VanillaItems::AIR());
300 }
301
302 public function clearAll() : void{
303 $this->setContents([]);
304 }
305
306 public function swap(int $slot1, int $slot2) : void{
307 $i1 = $this->getItem($slot1);
308 $i2 = $this->getItem($slot2);
309 $this->setItem($slot1, $i2);
310 $this->setItem($slot2, $i1);
311 }
312
313 protected function onSlotChange(int $index, Item $before) : void{
314 foreach($this->listeners as $listener){
315 $listener->onSlotChange($this, $index, $before);
316 }
317 }
318
323 protected function onContentChange(array $itemsBefore) : void{
324 foreach($this->listeners as $listener){
325 $listener->onContentChange($this, $itemsBefore);
326 }
327 }
328
329 public function slotExists(int $slot) : bool{
330 return $slot >= 0 && $slot < $this->getSize();
331 }
332
333 public function getListeners() : ObjectSet{
334 return $this->listeners;
335 }
336
337 public function getSlotValidators() : ObjectSet{
338 return $this->validators;
339 }
340}
first(Item $item, bool $exact=false)
setItem(int $index, Item $item)