RuneHive-Game
Loading...
Searching...
No Matches
ItemContainer.java
Go to the documentation of this file.
1package com.runehive.game.world.items.containers;
2
3import com.google.common.base.Preconditions;
4import com.runehive.game.world.entity.mob.player.Player;
5import com.runehive.game.world.items.Item;
6import com.runehive.game.world.items.ItemDefinition;
7import com.runehive.game.world.items.containers.pricechecker.PriceType;
8import com.runehive.net.packet.out.SendItemOnInterface;
9
10import java.util.*;
11import java.util.function.Consumer;
12import java.util.stream.Stream;
13import java.util.stream.StreamSupport;
14
15import static com.google.common.base.Preconditions.checkArgument;
16import static com.google.common.base.Preconditions.checkState;
17
18
19/**
20 * An abstraction game representing a group of {@link Item}s.
21 *
22 * @author lare96 <http://github.com/lare96>
23 */
24public class ItemContainer implements Iterable<Item> {
25
26 // TODO: Unit tests for various functions
27
28 /** An {@link Iterator} implementation for this container. */
29 private static final class ItemContainerIterator implements Iterator<Item> {
30
31 /** The container instance to iterate over. */
32 private final ItemContainer container;
33
34 /** The current index being iterated over. */
35 private int index;
36
37 /** The last index that was iterated over. */
38 private int lastIndex = -1;
39
40 /** Creates a new {@link ItemContainerIterator}. */
44
45 @Override
46 public boolean hasNext() {
47 return (index + 1) <= container.capacity;
48 }
49
50 @Override
51 public Item next() {
52 checkState(index < container.capacity, "no more elements left to iterate");
53
55 index++;
56 return container.items[lastIndex];
57 }
58
59 @Override
60 public void remove() {
61 checkState(lastIndex != -1, "can only be called once after 'next'");
62
63 Item oldItem = container.items[lastIndex];
64 container.items[lastIndex] = null;
65 container.fireItemUpdatedEvent(oldItem, null, lastIndex, true);
66
68 lastIndex = -1;
69 }
70 }
71
72 /** An enumerated type defining policies for stackable {@link Item}s. */
73 public enum StackPolicy {
74
75 /**
76 * The {@code STANDARD} policy, items are only stacked if they are
77 * defined as stackable in their {@link ItemDefinition} table.
78 */
80
81 /**
82 * The {@code ALWAYS} policy, items are always stacked regardless of
83 * their {@link ItemDefinition} table.
84 */
86
87 /**
88 * The {@code NEVER} policy, items are never stacked regardless of their
89 * {@link ItemDefinition} table.
90 */
92 }
93
94 /**
95 * An {@link ArrayList} of {@link ItemContainerListener}s listening for
96 * various events.
97 */
98 private final List<ItemContainerListener> listeners = new ArrayList<>();
99
100 /** The capacity of this container. */
101 private final int capacity;
102
103 /** The policy of this container. */
104 private final StackPolicy policy;
105
106 /** The {@link Item}s within this container. */
107 private Item[] items;
108
109 /** If events are currently being fired. */
110 private boolean firingEvents = true;
111
112 /** Creates a new {@link ItemContainer}. */
114 this.capacity = capacity;
115 this.policy = policy;
116 this.items = items;
117 }
118
119 /** Creates a new {@link ItemContainer}. */
121 this(capacity, policy, new Item[capacity]);
122 }
123
124 /**
125 * Iterates through all of the {@link Item}s within this container and
126 * performs {@code action} on them, skipping empty indexes ({@code null}
127 * values) as they are encountered.
128 */
129 @Override
130 public final void forEach(Consumer<? super Item> action) {
131 Objects.requireNonNull(action);
132 for (Item item : items) {
133 if (item != null) {
134 action.accept(item);
135 }
136 }
137 }
138
139 @Override
140 public final Spliterator<Item> spliterator() {
141 return Spliterators.spliterator(items, Spliterator.ORDERED);
142 }
143
144 @Override
145 public final Iterator<Item> iterator() {
146 return new ItemContainerIterator(this);
147 }
148
149 /**
150 * @return A stream associated with the elements in this container, built
151 * using the {@code spliterator()} implementation.
152 */
153 public final Stream<Item> stream() {
154 return StreamSupport.stream(spliterator(), false);
155 }
156
157 /**
158 * Attempts to deposit {@code item} into this container.
159 *
160 * @param item The {@link Item} to deposit.
161 * @return {@code true} the {@code Item} was added, {@code false} if there
162 * was not enough space left.
163 */
164 public boolean add(Item item) {
165 return add(item, -1, true);
166 }
167
168 /**
169 * Attempts to deposit {@code item} into this container.
170 *
171 * @param item The {@link Item} to deposit.
172 * @param slot The slot to deposit the item too.
173 * @return {@code true} the {@code Item} was added, {@code false} if there
174 * was not enough space left.
175 */
176 public boolean add(Item item, int slot) {
177 return add(item, slot, true);
178 }
179
180 /**
181 * Attempts to deposit {@code item} into this container.
182 *
183 * @param id the id of the item.
184 * @param amount the amount of the item.
185 * @return {@code true} the item was added, {@code false} if there was not
186 * enough space left.
187 */
188 public boolean add(int id, int amount) {
189 return add(new Item(id, amount));
190 }
191
192 public boolean add(Item item, boolean refresh, boolean stack) {
193 return add(item, -1, refresh, stack);
194 }
195
196 public boolean add(Item item, int preferredIndex, boolean refresh) {
197 return add(item, preferredIndex, refresh, false);
198 }
199
200 /**
201 * Attempts to deposit {@code item} into this container, preferably at
202 * {@code preferredIndex}.
203 *
204 * @param item The {@link Item} to deposit.
205 * @param preferredIndex The preferable index to deposit {@code item} to.
206 * @param refresh The condition if we will be refreshing our
207 * container.
208 * @return {@code true} if the {@code Item} was added, {@code false} if
209 * there was not enough space left.
210 */
211 public boolean add(Item item, int preferredIndex, boolean refresh, boolean stack) {
212 checkArgument(preferredIndex >= -1, "invalid index identifier");
213
214 item = item.copy();
215
216 boolean stackable = stack || (policy.equals(StackPolicy.STANDARD) && item.isStackable()) || policy.equals(StackPolicy.ALWAYS);
217
218 if (stackable && !stack) {
219 preferredIndex = computeIndexForId(item.getId());
220 } else if (preferredIndex != -1) {
221 preferredIndex = items[preferredIndex] != null ? -1 : preferredIndex;
222 }
223
224 preferredIndex = preferredIndex == -1 ? computeFreeIndex() : preferredIndex;
225
226 if (preferredIndex == -1) { // Not enough space in container.
228 return false;
229 }
230
231 if (stackable) {
232 Item current = items[preferredIndex];
233 items[preferredIndex] = (current == null) ? item : current.createAndIncrement(item.getAmount());
234 fireItemUpdatedEvent(current, items[preferredIndex], preferredIndex, refresh);
235 } else {
236 int remaining = remaining();
237 int until = (remaining > item.getAmount()) ? item.getAmount() : remaining;
238
239 for (int index = 0; index < until; index++) {
240 preferredIndex = (preferredIndex > capacity || preferredIndex < 0 || items[preferredIndex] == null) ? preferredIndex : computeFreeIndex();
241 if (preferredIndex == -1) {//Couldn't find an empty spot.
243 return false;
244 }
245 item.setAmount(1);
246 items[preferredIndex] = item;
247
248 fireItemUpdatedEvent(null, item, preferredIndex++, refresh);
249 }
250 }
251 return true;
252 }
253
254 /**
255 * Attempts to deposit {@code items} in bulk into this container.
256 *
257 * @param items The {@link Item}s to deposit.
258 * @return {@code true} if at least {@code 1} of the {@code Item}s were
259 * added, {@code false} if none could be added.
260 */
261 public boolean addAll(Collection<? extends Item> items) {
262 if (items.size() == 1) { // Bulk operation on singleton list? No thanks..
263 Optional<? extends Item> item = items.stream().
264 filter(Objects::nonNull).
265 findFirst();
266 return item.isPresent() && add(item.get());
267 }
268
269 firingEvents = false;
270
271 boolean added = false;
272 try {
273 for (Item item : items) {
274 if (item == null) {
275 continue;
276 }
277 if (add(item, -1, false)) {
278 added = true;
279 }
280 }
281 } finally {
282 firingEvents = true;
283 }
285 return added;
286 }
287
288 /**
289 * Attempts to deposit {@code items} in bulk into this container.
290 *
291 * @param items The {@link Item}s to deposit.
292 * @return {@code true} if at least {@code 1} of the {@code Item}s were
293 * added, {@code false} if none could be added.
294 */
295 public boolean addAll(Item... items) {
296 return addAll(Arrays.asList(items));
297 }
298
299 /**
300 * Attempts to deposit {@code items} in bulk into this container.
301 *
302 * @param items The {@link Item}s to deposit.
303 * @return {@code true} if at least {@code 1} of the {@code Item}s were
304 * added, {@code false} if none could be added.
305 */
306 public boolean addAll(ItemContainer items) {
307 return addAll(items.items);
308 }
309
310 /**
311 * Attempts to withdraw {@code item} from this container.
312 *
313 * @param item The {@link Item} to withdraw.
314 * @return {@code true} if the {@code Item} was removed, {@code false} if it
315 * isn't present in this container.
316 */
317 public boolean remove(Item item) {
318 return remove(item, -1, true);
319 }
320
321 /**
322 * Attempts to withdraw {@code item} from this container, preferably from
323 * {@code preferredIndex}.
324 *
325 * @param item The {@link Item} to withdraw.
326 * @param preferredIndex The preferable index to withdraw {@code item}
327 * from.
328 * @return {@code true} if the {@code Item} was removed, {@code false} if it
329 * isn't present in this container.
330 */
331 public boolean remove(Item item, int preferredIndex) {
332 return remove(item, preferredIndex, true);
333 }
334
335 public boolean remove(int id) {
336 return remove(new Item(id, 1));
337 }
338
339 public boolean remove(int id, int amount) {
340 return remove(new Item(id, amount));
341 }
342
343 public boolean remove(Item item, int preferredIndex, boolean refresh) {
344 return remove(item, preferredIndex, refresh, true);
345 }
346
347 /**
348 * Attempts to withdraw {@code item} from this container, preferably from
349 * {@code preferredIndex}.
350 *
351 * @param item The {@link Item} to withdraw.
352 * @param preferredIndex The preferable index to withdraw {@code item}
353 * from.
354 * @param refresh The condition if we will be refreshing our
355 * container.
356 * @param removeAll Determines if the items should be removed from all
357 * slots or only the specified s
358 * @return {@code true} if the {@code Item} was removed, {@code false} if it
359 * isn't present in this container.
360 */
361 public boolean remove(Item item, int preferredIndex, boolean refresh, boolean removeAll) {
362 checkArgument(preferredIndex >= -1, "invalid index identifier");
363
364 item = item.copy();
365
366 boolean stackable = (policy.equals(StackPolicy.STANDARD) && item.isStackable()) || policy.equals(StackPolicy.ALWAYS);
367
368 if (stackable) {
369 //todo fix dupe
370 /*
371 because stackable is true (due to the coins being stackable). it will compute the first index.
372 being the 1 coin, so when you drop. amount is 260. removes 1coin but drops 260k.
373 */
374 preferredIndex = computeIndexForId(item.getId());
375 } else {
376 preferredIndex = preferredIndex == -1 ? computeIndexForId(item.getId()) : preferredIndex;
377
378 if (preferredIndex != -1 && items[preferredIndex] == null) {
379 preferredIndex = -1;
380 }
381
382 }
383
384 if (preferredIndex == -1) { // Item isn't present within this container.
385 return false;
386 }
387
388 if (stackable) {
389 Item current = items[preferredIndex];
390 if (current.getAmount() > item.getAmount()) {
391 items[preferredIndex] = current.createAndDecrement(item.getAmount());
392 } else {
393 if (allowZero()) {
394 items[preferredIndex] = current.createWithAmount(0);
395 } else {
396 items[preferredIndex] = null;
397 }
398 }
399
400 fireItemUpdatedEvent(current, items[preferredIndex], preferredIndex, refresh);
401 } else {
402 int until = removeAll ? computeAmountForId(item.getId()) : items[preferredIndex].getAmount();
403 until = (item.getAmount() > until) ? until : item.getAmount();
404
405 for (int index = 0; index < until && index < capacity; index++) {
406 if (removeAll) {
407 if (preferredIndex < 0 || preferredIndex >= capacity) {
408 preferredIndex = computeIndexForId(item.getId());
409 } else if (items[preferredIndex] == null) {
410 preferredIndex = computeIndexForId(item.getId());
411 } else if (items[preferredIndex].getId() != item.getId()) {
412 preferredIndex = computeIndexForId(item.getId());
413 }
414 if (preferredIndex == -1) {
415 break;
416 }
417 }
418 Item oldItem = items[preferredIndex];
419 if (allowZero()) {
420 items[preferredIndex] = oldItem.createWithAmount(0);
421 } else {
422 items[preferredIndex] = null;
423 }
424 fireItemUpdatedEvent(oldItem, null, preferredIndex++, refresh);
425 }
426 }
427 return true;
428 }
429
430 /**
431 * Attempts to withdraw {@code items} in bulk from this container.
432 *
433 * @param items The {@link Item}s to withdraw.
434 * @return {@code true} if at least {@code 1} of the {@code Item}s were
435 * withdraw, {@code false} if none could be removed.
436 */
437 public boolean removeAll(Collection<? extends Item> items) {
438 if (items.size() == 1) { // Bulk operation on singleton list? No thanks..
439 Optional<? extends Item> item = items.stream().
440 filter(Objects::nonNull).
441 findFirst();
442 return item.isPresent() && remove(item.get());
443 }
444
445 firingEvents = false;
446 boolean removed = false;
447 try {
448 for (Item item : items) {
449 if (item == null) {
450 continue;
451 }
452 if (remove(item, -1, false)) {
453 removed = true;
454 }
455 }
456 } finally {
457 firingEvents = true;
458 }
460 return removed;
461 }
462
463 /**
464 * Attempts to withdraw {@code items} in bulk from this container.
465 *
466 * @param items The {@link Item}s to withdraw.
467 * @return {@code true} if at least {@code 1} of the {@code Item}s were
468 * withdraw, {@code false} if none could be removed.
469 */
470 public boolean removeAll(Item... items) {
471 return removeAll(Arrays.asList(items));
472 }
473
474 /**
475 * Attempts to withdraw {@code items} in bulk from this container.
476 *
477 * @param items The {@link Item}s to withdraw.
478 * @return {@code true} if at least {@code 1} of the {@code Item}s were
479 * withdraw, {@code false} if none could be removed.
480 */
481 public boolean removeAll(ItemContainer items) {
482 return removeAll(items.items);
483 }
484
485 /**
486 * Gets the total worth of the container using the item's values.
487 *
488 * @return The total container worth.
489 */
490 public long containerValue(PriceType type) {
491 long value = 0;
492
493 for (final Item item : items) {
494 if (item == null) {
495 continue;
496 }
497
498 int price = item.getValue(type);
499
500 if (value >= Long.MAX_VALUE - price * item.getAmount()) {
501 return Long.MAX_VALUE;
502 }
503
504 value += price * item.getAmount();
505 }
506
507 return value;
508 }
509
510 /**
511 * Computes the next free ({@code null}) index in this container.
512 *
513 * @return The free index, {@code -1} if no free indexes could be found.
514 */
515 public final int computeFreeIndex() {
516 for (int index = 0; index < capacity; index++) {
517 if (items[index] == null) {
518 return index;
519 }
520 }
521 return -1;
522 }
523
524 /**
525 * Computes the first index found that {@code id} is in.
526 *
527 * @param id The identifier to compute for.
528 * @return The first index found, {@code -1} if no {@link Item} with {@code
529 * id} is in this container.
530 */
531 public final int computeIndexForId(int id) {
532 for (int index = 0; index < capacity; index++) {
533 if (items[index] != null && items[index].getId() == id) {
534 return index;
535 }
536 }
537 return -1;
538 }
539
540 /**
541 * Computes the total quantity of the {@link Item}s in this container with
542 * {@code id}.
543 *
544 * @param id The identifier of the {@code Item} to determine the total
545 * quantity of.
546 * @return The total quantity.
547 */
548 public final int computeAmountForId(int id) {
549 int amount = 0;
550 for (Item item : items) {
551 if (item == null || item.getId() != id)
552 continue;
553 amount += item.getAmount();
554 }
555 return amount;
556 }
557
558 /**
559 * Computes the identifier of the {@link Item} on {@code index}.
560 *
561 * @param index The index to compute the identifier for.
562 * @return The identifier wrapped in an optional.
563 */
564 public final Optional<Integer> computeIdForIndex(int index) {
565 return retrieve(index).map(Item::getId);
566 }
567
568 /**
569 * Replaces the first occurrence of the {@link Item} having the identifier
570 * {@code oldId} with {@code newId}.
571 *
572 * @param oldId The old identifier to replace.
573 * @param newId The new identifier to replace.
574 * @param refresh The condition if the coontainer will be refreshed.
575 * @return {@code true} if the replace operation was successful, {@code
576 * false otherwise}.
577 */
578 public final boolean replace(int oldId, int newId, int index, boolean refresh) {
579 Item oldItem = items[index];
580
581 if (oldItem == null || !oldItem.matchesId(oldId))
582 return false;
583
584 Item newItem = oldItem.createWithId(newId);
585 return remove(oldItem, index, refresh) && add(newItem, index, refresh);
586 }
587
588 /**
589 * Replaces the first occurrence of the {@link Item} having the identifier
590 * {@code oldId} with {@code newId}.
591 *
592 * @param oldId The old identifier to replace.
593 * @param newId The new identifier to replace.
594 * @param refresh The condition if the coontainer will be refreshed.
595 * @return {@code true} if the replace operation was successful, {@code
596 * false otherwise}.
597 */
598 public final boolean replace(int oldId, int newId, boolean refresh) {
599 int index = computeIndexForId(oldId);
600 if (index == -1) {
601 return false;
602 }
603
604 Item oldItem = items[index];
605
606 if (oldItem == null || !oldItem.matchesId(oldId))
607 return false;
608
609 Item newItem = oldItem.createWithId(newId);
610 return remove(oldItem, index, refresh) && add(newItem, index, refresh);
611 }
612
613 /**
614 * Replaces the first occurrence of the {@link Item} having the identifier
615 * {@code oldId} with {@code newId}.
616 *
617 * @param refresh The condition if the coontainer will be refreshed.
618 * @return {@code true} if the replace operation was successful, {@code
619 * false otherwise}.
620 */
621 public final boolean replace(Item first, Item second, boolean refresh) {
622 int index = computeIndexForId(first.getId());
623
624 if (index == -1) {
625 return false;
626 }
627
628 Item oldItem = items[index];
629
630 if (oldItem == null || !oldItem.matchesId(first.getId()))
631 return false;
632
633 return remove(oldItem, index, refresh) && add(second, index, refresh);
634 }
635
636 /**
637 * Replaces all occurrences of {@link Item}s having the identifier {@code
638 * oldId} with {@code newId}.
639 *
640 * @param oldId The old identifier to replace.
641 * @param newId The new identifier to replace.
642 * @return {@code true} if the replace operation was successful at least
643 * once, {@code false otherwise}.
644 */
645 public final boolean replaceAll(int oldId, int newId) {
646 boolean replaced = false;
647
648 firingEvents = false;
649 try {
650 while (replace(oldId, newId, false)) {
651 replaced = true;
652 }
653 } finally {
654 firingEvents = true;
655 }
657 return replaced;
658 }
659
660 /**
661 * Computes the amount of indexes required to hold {@code items} in this
662 * container.
663 *
664 * @param forItems The items to compute the index count for.
665 * @return The index count.
666 */
667 public final int computeIndexCount(Item... forItems) {
668 int indexCount = 0;
669 for (Item item : forItems) {
670 if (item == null)
671 continue;
672 boolean stackable = (policy.equals(StackPolicy.STANDARD) && item.isStackable()) || policy.equals(StackPolicy.ALWAYS);
673
674 if (stackable) {
675 int index = computeIndexForId(item.getId());
676 if (index == -1) {
677 indexCount++;
678 continue;
679 }
680
681 Item existing = items[index];
682 if ((existing.getAmount() + item.getAmount()) <= 0) {
683 indexCount++;
684 }
685 } else {
686 indexCount += item.getAmount();
687 }
688 }
689 return indexCount;
690 }
691
692 /**
693 * Determines if this container has the capacity for {@code item}.
694 *
695 * @param item The {@link Item} to determine this for.
696 * @return {@code true} if {@code item} can be added, {@code false}
697 * otherwise.
698 */
699 public final boolean hasCapacityFor(Item... item) {
700 int indexCount = computeIndexCount(item);
701 return remaining() >= indexCount;
702 }
703
704 /**
705 * Creates a copy of the underlying container and removes the items
706 * specified from it and after tries to deposit the specified items to it.
707 *
708 * @param add the items to deposit to this container.
709 * @param remove the items that should be removed before adding.
710 * @return {@code true} if {@code item} can be added, {@code false}
711 * otherwise.
712 */
713 public final boolean hasCapacityAfter(Item[] add, Item... remove) {
715 container.removeAll(Arrays.copyOf(remove, remove.length));
716 return container.hasCapacityFor(add);
717 }
718
719 /**
720 * Determines if this container contains {@code id}.
721 *
722 * @param id The identifier to check this container for.
723 * @return {@code true} if this container has {@code id}, {@code false}
724 * otherwise.
725 */
726 public boolean contains(int id) {
727 for (Item item : items) {
728 if (item != null && id == item.getId()) {
729 return true;
730 }
731 }
732 return false;
733 }
734
735 /**
736 * Determines if this container contains all {@code identifiers}.
737 *
738 * @param identifiers The identifiers to check this container for.
739 * @return {@code true} if this container has all {@code identifiers},
740 * {@code false} otherwise.
741 */
742 public final boolean containsAll(int... identifiers) {
743 for (int id : identifiers) {
744 if (!contains(id)) {
745 return false;
746 }
747 }
748 return true;
749 }
750
751 /**
752 * Determines if this container contains any {@code identifiers}.
753 *
754 * @param identifiers The identifiers to check this container for.
755 * @return {@code true} if this container has any {@code identifiers},
756 * {@code false} otherwise.
757 */
758 public final boolean containsAny(int... identifiers) {
759 for (int id : identifiers) {
760 if (contains(id)) {
761 return true;
762 }
763 }
764 return false;
765 }
766
767 /** @return {@code true} if this container has the {@code item} */
768 public final boolean contains(Item item) {
769 return item != null && contains(item.getId(), item.getAmount());
770 }
771
772 /** @return {@code true} if this container has id with amount */
773 public final boolean contains(int id, int amount) {
774 for (Item item : items) {
775 if (item != null && id == item.getId()) {
776 amount -= item.getAmount();
777 if (amount <= 0) return true;
778 }
779 }
780 return false;
781 }
782
783 /** @return {@code true} if this container has all {@code items} */
784 public final boolean containsAll(Item... items) {
785 for (Item item : items) {
786 if (!contains(item)) {
787 return false;
788 }
789 }
790 return true;
791 }
792
793 /** @return {@code true} if this container has all {@code items} */
794 public final boolean containsAll(Collection<Item> items) {
795 for (Item item : items) {
796 if (!contains(item)) {
797 return false;
798 }
799 }
800 return true;
801 }
802
803 /** @return {@code true} if this container has all {@code items} */
804 public final boolean containsAny(Item... items) {
805 for (Item item : items) {
806 if (contains(item)) {
807 return true;
808 }
809 }
810 return false;
811 }
812
813 /** @return {@code true} if this container has all {@code items} */
814 public final boolean containsAny(Collection<Item> items) {
815 for (Item item : items) {
816 if (contains(item)) {
817 return true;
818 }
819 }
820 return false;
821 }
822
823 /**
824 * Sends the items on the itemcontainer.
825 *
826 * @param widget The widget to send the {@code Item}s on.
827 */
828 public void refresh(Player player, int widget) {
829 player.send(new SendItemOnInterface(widget, items));
830 onRefresh();
831 }
832
833 /** Any functionality that should occur when refreshed. */
834 public void onRefresh() {
835 /* can be overriden */
836 }
837
838 /**
839 * Swaps the {@link Item}s on {@code oldIndex} and {@code newIndex}.
840 *
841 * @param oldIndex The old index.
842 * @param newIndex The new index.
843 */
844 public final void swap(int oldIndex, int newIndex) {
845 swap(false, oldIndex, newIndex, true);
846 }
847
848 /**
849 * Swaps the {@link Item}s on {@code oldIndex} and {@code newIndex}.
850 *
851 * @param insert If the {@code Item} should be inserted.
852 * @param oldIndex The old index.
853 * @param newIndex The new index.
854 * @param refresh The condition that determines if we will refresh the
855 * container.
856 */
857 public final void swap(boolean insert, int oldIndex, int newIndex, boolean refresh) {
858 if (insert) {
859 insert(oldIndex, newIndex, refresh);
860 } else {
861 swap(oldIndex, newIndex, refresh);
862 }
863 }
864
865 public final void swap(int oldIndex, int newIndex, boolean refresh) {
866 checkArgument(oldIndex >= 0 && oldIndex < capacity, "[swap] oldIndex out of range - [old=" + oldIndex + ", new=" + newIndex + ", refresh=" + refresh + ", size=" + size() + ", capacity=" + capacity + "]");
867 checkArgument(newIndex >= 0 && newIndex < capacity, "[swap] newIndex out of range - [old=" + oldIndex + ", new=" + newIndex + ", refresh=" + refresh + ", size=" + size() + ", capacity=" + capacity + "]");
868
869 Item itemOld = items[oldIndex];
870 Item itemNew = items[newIndex];
871
872 items[oldIndex] = itemNew;
873 items[newIndex] = itemOld;
874
875 fireItemUpdatedEvent(itemOld, items[oldIndex], oldIndex, refresh);
876 fireItemUpdatedEvent(itemNew, items[newIndex], newIndex, refresh);
877 }
878
879 public final void insert(int oldIndex, int newIndex, boolean refresh) {
880 checkArgument(oldIndex >= 0 && oldIndex < capacity, "[insert] oldIndex out of range - [old=" + oldIndex + ", new=" + newIndex + ", refresh=" + refresh + ", size=" + size() + ", capacity=" + capacity + "]");
881 checkArgument(newIndex >= 0 && newIndex < capacity, "[insert] newIndex out of range - [old=" + oldIndex + ", new=" + newIndex + ", refresh=" + refresh + ", size=" + size() + ", capacity=" + capacity + "]");
882
883 if (newIndex > oldIndex) {
884 for (int index = oldIndex; index < newIndex; index++) {
885 swap(index, index + 1, refresh);
886 }
887 } else if (oldIndex > newIndex) {
888 for (int index = oldIndex; index > newIndex; index--) {
889 swap(index, index - 1, refresh);
890 }
891 }
892 }
893
894 public final void transfer(int firstIndex, int secondIndex, ItemContainer other, boolean refresh) {
895 checkArgument(firstIndex >= 0 && firstIndex < capacity, "[transfer] firstIndex out of range - [first=" + firstIndex + ", second=" + secondIndex + ", refresh=" + refresh + "]");
896 checkArgument(secondIndex >= 0 && secondIndex < other.capacity, "[transfer] secondIndex out of range - [first=" + firstIndex + ", second=" + secondIndex + ", refresh=" + refresh + "]");
897 Item first = get(firstIndex);
898 Item second = other.get(secondIndex);
899 set(firstIndex, second, true);
900 other.set(secondIndex, first, true);
901 }
902
903 /**
904 * Percolates the null indices to the end of the stack.
905 */
906 public void shift() {
907 Item[] newItems = new Item[capacity];
908 int newIndex = 0;
909
910 for (Item item : items) {
911 if (item == null) {
912 continue;
913 }
914 newItems[newIndex++] = item;
915 }
916
917 setItems(newItems);
918 }
919
920 /**
921 * Sets the container of items to {@code items}. The container will not hold
922 * any references to the array, nor the item instances in the array.
923 *
924 * @param items the new array of items, the capacities of this must be equal
925 * to or lesser than the container.
926 */
927 public final void setItems(Item[] items, boolean copy) {
928 Preconditions.checkArgument(items.length <= capacity);
929 clear();
930 for (int i = 0; i < items.length; i++) {
931 this.items[i] = items[i] == null ? null : copy ? items[i].copy() : items[i];
932 }
934 }
935
936 public final void setItems(Item[] items) {
937 setItems(items, true);
938 }
939
940 public final void set(Item[] toSet) {
941 items = toSet;
942 }
943
944 /**
945 * Returns a <strong>shallow copy</strong> of the backing array. Changes
946 * made to the returned array will not be reflected on the backing array.
947 *
948 * @return A shallow copy of the backing array.
949 */
950 public final Item[] toArray() {
951 return Arrays.copyOf(items, items.length);
952 }
953
954 public final Item[] toNonNullArray() {
955 if (size() == 0) {
956 return new Item[0];
957 }
958
959 final List<Item> items = new ArrayList<>(size());
960
961 for (Item item : getItems()) {
962 if (item == null) {
963 continue;
964 }
965
966 items.add(item);
967 }
968
969 return items.toArray(new Item[items.size()]);
970 }
971
972 /**
973 * Gets the weight of the container.
974 *
975 * @return The weight of the container.
976 */
977 public double getWeight() {
978 double weight = 0;
979 for (Item item : toArray()) {
980 if (item == null)
981 continue;
982 weight += item.getWeight();
983 }
984 return weight;
985 }
986
987 /**
988 * Sets the {@code index} to {@code item}.
989 *
990 * @param index The index to set.
991 * @param item The {@link Item} to set on the index.
992 * @param refresh The condition if the container must be refreshed.
993 */
994 public void set(int index, Item item, boolean refresh) {
995 Item oldItem = items[index];
996 items[index] = item;
997 fireItemUpdatedEvent(oldItem, items[index], index, refresh);
998 }
999
1000 /**
1001 * Retrieves the item located on {@code index}.
1002 *
1003 * @param index the index to get the item on.
1004 * @return the item on the index, or {@code null} if no item exists on the
1005 * index.
1006 */
1007 public final Optional<Item> retrieve(int index) {
1008 if (index >= 0 && index < items.length)
1009 return Optional.ofNullable(items[index]);
1010 return Optional.empty();
1011 }
1012
1013 /**
1014 * Consumes an action if the {@code index} is a valid item index in this
1015 * container.
1016 *
1017 * @param index the index to get the item on.
1018 */
1019 public final void ifPresent(int index, Consumer<Item> action) {
1020 if (index >= 0 && index < items.length)
1021 action.accept(items[index]);
1022 }
1023
1024 /**
1025 * Gets the {@link Item} located on {@code index}.
1026 *
1027 * @param index The index to get the {@code Item} on.
1028 * @return The {@code Item} instance, {@code null} if the index is empty.
1029 */
1030 public final Item get(int index) {
1031 if (index >= 0 && index < items.length)
1032 return items[index];
1033 return null;
1034 }
1035
1036 /**
1037 * Gets the item id located on {@code index}.
1038 *
1039 * @param index The index to get the {@code Item} on.
1040 * @return The {@code Item} instance, {@code null} if the index is empty.
1041 */
1042 public final int getId(int index) {
1043 if (items[index] == null) {
1044 return -1;
1045 }
1046 return items[index].getId();
1047 }
1048
1049 /**
1050 * Searches and returns the first item found with {@code id}.
1051 *
1052 * @param id the identifier to search this container for.
1053 * @return the item wrapped within an optional, or an empty optional if no
1054 * item was found.
1055 */
1056 public Optional<Item> search(int id) {
1057 return stream().filter(i -> i != null && id == i.getId()).findFirst();
1058 }
1059
1060 /**
1061 * Searches and returns the first item found with {@code id} and {@code
1062 * amount}.
1063 *
1064 * @param item the item to search this container for.
1065 * @return the item wrapped within an optional, or an empty optional if no
1066 * item was found.
1067 */
1068 public Optional<Item> search(Item item) {
1069 return stream().filter(i -> i != null && item.getId() == i.getId() && item.getAmount() == i.getAmount()).findFirst();
1070 }
1071
1072 /**
1073 * Returns {@code true} if {@code index} is occupied (non-{@code null}).
1074 */
1075 public final boolean indexOccupied(int index) {
1076 return retrieve(index).isPresent();
1077 }
1078
1079 /**
1080 * Returns {@code true} if {@code index} is not occupied ({@code null}).
1081 */
1082 public final boolean indexFree(int index) {
1083 return !indexOccupied(index);
1084 }
1085
1086 /**
1087 * Creates a copy of the underlying item container.
1088 *
1089 * @return a copy of the unterlying item container.
1090 */
1091 public final ItemContainer copy() {
1092 ItemContainer container = new ItemContainer(this.capacity, this.policy, this.toArray());
1093 this.listeners.forEach(container::addListener);
1094 return container;
1095 }
1096
1097 /** Removes all of the items from this container. */
1098 public void clear() {
1099 clear(true);
1100 }
1101
1102 /** Removes all of the items from this container. */
1103 public final void clear(boolean refresh) {
1104 Arrays.fill(items, null);
1105 if (refresh)
1107 }
1108
1109 /** @return {@code true} if this container is empty */
1110 public final boolean isEmpty() {
1111 return size() == 0;
1112 }
1113
1114 public final boolean isFull() {
1115 return getFreeSlots() == 0;
1116 }
1117
1118 /**
1119 * Adds an {@link ItemContainerListener} to this container.
1120 *
1121 * @param listener The listener to deposit to this container.
1122 * @return {@code true} if the listener was added, {@code false} otherwise.
1123 */
1124 public final boolean addListener(ItemContainerListener listener) {
1125 return listeners.add(listener);
1126 }
1127
1128 /**
1129 * Removes an {@link ItemContainerListener} from this container.
1130 *
1131 * @param listener The listener to withdraw from this container.
1132 * @return {@code true} if the listener was removed, {@code false}
1133 * otherwise.
1134 */
1135 public final boolean removeListener(ItemContainerListener listener) {
1136 return listeners.remove(listener);
1137 }
1138
1139 /**
1140 * Fires the {@code ItemContainerListener.itemUpdated(ItemContainer, int)}
1141 * event.
1142 */
1143
1144 public final void fireItemUpdatedEvent(Item oldItem, Item newItem, int index, boolean refresh) {
1145 fireItemUpdatedEvent(oldItem, newItem, index, refresh, false);
1146 }
1147
1148 public final void fireItemUpdatedEvent(Item oldItem, Item newItem, int index, boolean refresh, boolean login) {
1149 if (firingEvents) {
1150 listeners.forEach(evt -> evt.itemUpdated(this, Optional.ofNullable(oldItem), Optional.ofNullable(newItem), index, refresh, login));
1151 }
1152 }
1153
1154 /**
1155 * Fires the {@code ItemContainerListener.bulkItemsUpdated(ItemContainer)}
1156 * event.
1157 */
1158 public final void fireBulkItemsUpdatedEvent() {
1159 if (firingEvents) {
1160 listeners.forEach(evt -> evt.bulkItemsUpdated(this));
1161 }
1162 }
1163
1164 /**
1165 * Fires the {@code ItemContainerListener.capacityExceeded(ItemContainer)}
1166 * event.
1167 */
1168 public final void fireCapacityExceededEvent() {
1169 if (firingEvents) {
1170 listeners.forEach(evt -> evt.capacityExceeded(this));
1171 }
1172 }
1173
1174 /** @return The item array in this container. */
1175 public Item[] getItems() {
1176 return items;
1177 }
1178
1179 /** Sets the value for {@link #firingEvents}. */
1180 public void setFiringEvents(boolean firingEvents) {
1181 this.firingEvents = firingEvents;
1182 }
1183
1184 /** @return the amount of remaining free indices */
1185 public final int remaining() {
1186 return capacity - size();
1187 }
1188
1189 /** @return the amount of free slots available in the container */
1190 public int getFreeSlots() {
1191 return capacity() - size();
1192 }
1193
1194 /** @return the amount of used indices */
1195 public final int size() {
1196 return (int) Arrays.stream(items).filter(Objects::nonNull).count();
1197 }
1198
1199 /** @return the total amount of used and free indices */
1200 public final int capacity() {
1201 return capacity;
1202 }
1203
1204 /** @return the policy this container follows */
1205 public final StackPolicy policy() {
1206 return policy;
1207 }
1208
1209 /** @return this policy checks if the container will allow empty items */
1210 public boolean allowZero() {
1211 return false;
1212 }
1213
1214 /**
1215 * Gets a slot by id.
1216 *
1217 * @param id
1218 * The id.
1219 * @return The slot, or <code>-1</code> if it could not be found.
1220 */
1221 public int getSlotById(int id) {
1222 for (int i = 0; i < items.length; i++) {
1223 if (items[i] == null) {
1224 continue;
1225 }
1226 if (items[i].getId() == id) {
1227 return i;
1228 }
1229 }
1230 return -1;
1231 }
1232
1233// public boolean isFull() {
1234// return size() == capacity();
1235// }
1236}
This class represents a character controlled by a player.
Definition Player.java:125
The container class that represents an item that can be interacted with.
Definition Item.java:21
final void setAmount(int amount)
Sets the quantity of this item.
Definition Item.java:351
final int getId()
Gets the identification of this item.
Definition Item.java:324
Item createWithId(int newId)
Creates a new item with newId and the same amount as this instance.
Definition Item.java:142
final int getAmount()
Gets the quantity of this item.
Definition Item.java:342
Item copy()
A substitute for Object#clone() that creates another 'copy' of this instance.
Definition Item.java:270
Item createAndIncrement(int addAmount)
Creates a new item with amount + addAmount and the same identifier.
Definition Item.java:174
Item createWithAmount(int newAmount)
Creates a new item with newAmount and the same identifier as this instance.
Definition Item.java:158
Item createAndDecrement(int removeAmount)
Creates a new item with amount - removeAmount and the same identifier.
Definition Item.java:198
ItemContainerIterator(ItemContainer container)
Creates a new ItemContainerIterator.
final ItemContainer container
The container instance to iterate over.
boolean addAll(ItemContainer items)
Attempts to deposit items in bulk into this container.
final void swap(boolean insert, int oldIndex, int newIndex, boolean refresh)
Swaps the Items on oldIndex and newIndex.
final void fireItemUpdatedEvent(Item oldItem, Item newItem, int index, boolean refresh)
Fires the ItemContainerListener.itemUpdated(ItemContainer, int) event.
double getWeight()
Gets the weight of the container.
ItemContainer(int capacity, StackPolicy policy, Item[] items)
Creates a new ItemContainer.
void setFiringEvents(boolean firingEvents)
Sets the value for firingEvents.
final int computeAmountForId(int id)
Computes the total quantity of the Items in this container with id.
final void forEach(Consumer<? super Item > action)
Iterates through all of the Items within this container and performs action on them,...
final boolean replace(int oldId, int newId, boolean refresh)
Replaces the first occurrence of the Item having the identifier oldId with newId.
final Item get(int index)
Gets the Item located on index.
Item[] items
The Items within this container.
final void fireBulkItemsUpdatedEvent()
Fires the ItemContainerListener.bulkItemsUpdated(ItemContainer) event.
boolean add(Item item)
Attempts to deposit item into this container.
final boolean replace(int oldId, int newId, int index, boolean refresh)
Replaces the first occurrence of the Item having the identifier oldId with newId.
boolean add(Item item, int preferredIndex, boolean refresh)
final int capacity
The capacity of this container.
boolean add(int id, int amount)
Attempts to deposit item into this container.
void refresh(Player player, int widget)
Sends the items on the itemcontainer.
final void transfer(int firstIndex, int secondIndex, ItemContainer other, boolean refresh)
boolean firingEvents
If events are currently being fired.
final int getId(int index)
Gets the item id located on index.
final int computeIndexForId(int id)
Computes the first index found that id is in.
final void setItems(Item[] items, boolean copy)
Sets the container of items to items.
final void insert(int oldIndex, int newIndex, boolean refresh)
boolean add(Item item, boolean refresh, boolean stack)
final boolean hasCapacityFor(Item... item)
Determines if this container has the capacity for item.
boolean addAll(Item... items)
Attempts to deposit items in bulk into this container.
final Optional< Integer > computeIdForIndex(int index)
Computes the identifier of the Item on index.
ItemContainer(int capacity, StackPolicy policy)
Creates a new ItemContainer.
final void fireCapacityExceededEvent()
Fires the ItemContainerListener.capacityExceeded(ItemContainer) event.
final boolean addListener(ItemContainerListener listener)
Adds an ItemContainerListener to this container.
final List< ItemContainerListener > listeners
An ArrayList of ItemContainerListeners listening for various events.
final Optional< Item > retrieve(int index)
Retrieves the item located on index.
final int computeIndexCount(Item... forItems)
Computes the amount of indexes required to hold items in this container.
boolean addAll(Collection<? extends Item > items)
Attempts to deposit items in bulk into this container.
final void swap(int oldIndex, int newIndex, boolean refresh)
final void fireItemUpdatedEvent(Item oldItem, Item newItem, int index, boolean refresh, boolean login)
Optional< Item > search(Item item)
Searches and returns the first item found with id and amount.
boolean contains(int id)
Determines if this container contains id.
boolean removeAll(ItemContainer items)
Attempts to withdraw items in bulk from this container.
final boolean indexOccupied(int index)
Returns true if index is occupied (non-null).
final boolean removeListener(ItemContainerListener listener)
Removes an ItemContainerListener from this container.
final boolean replaceAll(int oldId, int newId)
Replaces all occurrences of Items having the identifier oldId with newId.
boolean add(Item item, int slot)
Attempts to deposit item into this container.
Optional< Item > search(int id)
Searches and returns the first item found with id.
boolean add(Item item, int preferredIndex, boolean refresh, boolean stack)
Attempts to deposit item into this container, preferably at preferredIndex.
void shift()
Percolates the null indices to the end of the stack.
void clear()
Removes all of the items from this container.
final boolean replace(Item first, Item second, boolean refresh)
Replaces the first occurrence of the Item having the identifier oldId with newId.
final boolean containsAny(int... identifiers)
Determines if this container contains any identifiers.
final boolean indexFree(int index)
Returns true if index is not occupied (null).
final void ifPresent(int index, Consumer< Item > action)
Consumes an action if the index is a valid item index in this container.
void onRefresh()
Any functionality that should occur when refreshed.
final StackPolicy policy
The policy of this container.
final int computeFreeIndex()
Computes the next free (null) index in this container.
boolean removeAll(Item... items)
Attempts to withdraw items in bulk from this container.
boolean removeAll(Collection<? extends Item > items)
Attempts to withdraw items in bulk from this container.
final ItemContainer copy()
Creates a copy of the underlying item container.
final boolean hasCapacityAfter(Item[] add, Item... remove)
Creates a copy of the underlying container and removes the items specified from it and after tries to...
final boolean containsAny(Collection< Item > items)
final boolean containsAll(Collection< Item > items)
long containerValue(PriceType type)
Gets the total worth of the container using the item's values.
final void clear(boolean refresh)
Removes all of the items from this container.
final boolean containsAll(int... identifiers)
Determines if this container contains all identifiers.
final Item[] toArray()
Returns a shallow copy of the backing array.
final void swap(int oldIndex, int newIndex)
Swaps the Items on oldIndex and newIndex.
An enumerated type defining policies for stackable Items.
ALWAYS
The ALWAYS policy, items are always stacked regardless of their ItemDefinition table.
NEVER
The NEVER policy, items are never stacked regardless of their ItemDefinition table.
STANDARD
The STANDARD policy, items are only stacked if they are defined as stackable in their ItemDefinition ...