RuneHive-Tarnish
Neural OSRS Enhancement Framework
Loading...
Searching...
No Matches
ItemContainer.java
1package com.osroyale.game.world.items.containers;
2
3import com.google.common.base.Preconditions;
4import com.osroyale.game.world.entity.mob.player.Player;
5import com.osroyale.game.world.items.Item;
6import com.osroyale.game.world.items.ItemDefinition;
7import com.osroyale.game.world.items.containers.pricechecker.PriceType;
8import com.osroyale.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
54
55public class ItemContainer implements Iterable<Item> {
56
57 // TODO: Unit tests for various functions
58
60 private static final class ItemContainerIterator implements Iterator<Item> {
61
63 private final ItemContainer container;
64
66 private int index;
67
69 private int lastIndex = -1;
70
72 ItemContainerIterator(ItemContainer container) {
73 this.container = container;
74 }
75
76 @Override
77 public boolean hasNext() {
78 return (index + 1) <= container.capacity;
79 }
80
81 @Override
82 public Item next() {
83 checkState(index < container.capacity, "no more elements left to iterate");
84
85 lastIndex = index;
86 index++;
87 return container.items[lastIndex];
88 }
89
90 @Override
91 public void remove() {
92 checkState(lastIndex != -1, "can only be called once after 'next'");
93
94 Item oldItem = container.items[lastIndex];
95 container.items[lastIndex] = null;
96 container.fireItemUpdatedEvent(oldItem, null, lastIndex, true);
97
98 index = lastIndex;
99 lastIndex = -1;
100 }
101 }
102
104public enum StackPolicy {
105
111
117
123 }
124
129 private final List<ItemContainerListener> listeners = new ArrayList<>();
130
132 private final int capacity;
133
135 private final StackPolicy policy;
136
138 private Item[] items;
139
141 private boolean firingEvents = true;
142
144 public ItemContainer(int capacity, StackPolicy policy, Item[] items) {
145 this.capacity = capacity;
146 this.policy = policy;
147 this.items = items;
148 }
149
151 public ItemContainer(int capacity, StackPolicy policy) {
152 this(capacity, policy, new Item[capacity]);
153 }
154
160 @Override
161 public final void forEach(Consumer<? super Item> action) {
162 Objects.requireNonNull(action);
163 for (Item item : items) {
164 if (item != null) {
165 action.accept(item);
166 }
167 }
168 }
169
170 @Override
171 public final Spliterator<Item> spliterator() {
172 return Spliterators.spliterator(items, Spliterator.ORDERED);
173 }
174
175 @Override
176 public final Iterator<Item> iterator() {
177 return new ItemContainerIterator(this);
178 }
179
184 public final Stream<Item> stream() {
185 return StreamSupport.stream(spliterator(), false);
186 }
187
195 public boolean add(Item item) {
196 return add(item, -1, true);
197 }
198
207 public boolean add(Item item, int slot) {
208 return add(item, slot, true);
209 }
210
219 public boolean add(int id, int amount) {
220 return add(new Item(id, amount));
221 }
222
223 public boolean add(Item item, boolean refresh, boolean stack) {
224 return add(item, -1, refresh, stack);
225 }
226
227 public boolean add(Item item, int preferredIndex, boolean refresh) {
228 return add(item, preferredIndex, refresh, false);
229 }
230
242 public boolean add(Item item, int preferredIndex, boolean refresh, boolean stack) {
243 checkArgument(preferredIndex >= -1, "invalid index identifier");
244
245 item = item.copy();
246
247 boolean stackable = stack || (policy.equals(StackPolicy.STANDARD) && item.isStackable()) || policy.equals(StackPolicy.ALWAYS);
248
249 if (stackable && !stack) {
250 preferredIndex = computeIndexForId(item.getId());
251 } else if (preferredIndex != -1) {
252 preferredIndex = items[preferredIndex] != null ? -1 : preferredIndex;
253 }
254
255 preferredIndex = preferredIndex == -1 ? computeFreeIndex() : preferredIndex;
256
257 if (preferredIndex == -1) { // Not enough space in container.
259 return false;
260 }
261
262 if (stackable) {
263 Item current = items[preferredIndex];
264 items[preferredIndex] = (current == null) ? item : current.createAndIncrement(item.getAmount());
265 fireItemUpdatedEvent(current, items[preferredIndex], preferredIndex, refresh);
266 } else {
267 int remaining = remaining();
268 int until = (remaining > item.getAmount()) ? item.getAmount() : remaining;
269
270 for (int index = 0; index < until; index++) {
271 preferredIndex = (preferredIndex > capacity || preferredIndex < 0 || items[preferredIndex] == null) ? preferredIndex : computeFreeIndex();
272 if (preferredIndex == -1) {//Couldn't find an empty spot.
274 return false;
275 }
276 item.setAmount(1);
277 items[preferredIndex] = item;
278
279 fireItemUpdatedEvent(null, item, preferredIndex++, refresh);
280 }
281 }
282 return true;
283 }
284
292 public boolean addAll(Collection<? extends Item> items) {
293 if (items.size() == 1) { // Bulk operation on singleton list? No thanks..
294 Optional<? extends Item> item = items.stream().
295 filter(Objects::nonNull).
296 findFirst();
297 return item.isPresent() && add(item.get());
298 }
299
300 firingEvents = false;
301
302 boolean added = false;
303 try {
304 for (Item item : items) {
305 if (item == null) {
306 continue;
307 }
308 if (add(item, -1, false)) {
309 added = true;
310 }
311 }
312 } finally {
313 firingEvents = true;
314 }
316 return added;
317 }
318
326 public boolean addAll(Item... items) {
327 return addAll(Arrays.asList(items));
328 }
329
337 public boolean addAll(ItemContainer items) {
338 return addAll(items.items);
339 }
340
348 public boolean remove(Item item) {
349 return remove(item, -1, true);
350 }
351
362 public boolean remove(Item item, int preferredIndex) {
363 return remove(item, preferredIndex, true);
364 }
365
366 public boolean remove(int id) {
367 return remove(new Item(id, 1));
368 }
369
370 public boolean remove(int id, int amount) {
371 return remove(new Item(id, amount));
372 }
373
374 public boolean remove(Item item, int preferredIndex, boolean refresh) {
375 return remove(item, preferredIndex, refresh, true);
376 }
377
392 public boolean remove(Item item, int preferredIndex, boolean refresh, boolean removeAll) {
393 checkArgument(preferredIndex >= -1, "invalid index identifier");
394
395 item = item.copy();
396
397 boolean stackable = (policy.equals(StackPolicy.STANDARD) && item.isStackable()) || policy.equals(StackPolicy.ALWAYS);
398
399 if (stackable) {
400 //todo fix dupe
401 /*
402 because stackable is true (due to the coins being stackable). it will compute the first index.
403 being the 1 coin, so when you drop. amount is 260. removes 1coin but drops 260k.
404 */
405 preferredIndex = computeIndexForId(item.getId());
406 } else {
407 preferredIndex = preferredIndex == -1 ? computeIndexForId(item.getId()) : preferredIndex;
408
409 if (preferredIndex != -1 && items[preferredIndex] == null) {
410 preferredIndex = -1;
411 }
412
413 }
414
415 if (preferredIndex == -1) { // Item isn't present within this container.
416 return false;
417 }
418
419 if (stackable) {
420 Item current = items[preferredIndex];
421 if (current.getAmount() > item.getAmount()) {
422 items[preferredIndex] = current.createAndDecrement(item.getAmount());
423 } else {
424 if (allowZero()) {
425 items[preferredIndex] = current.createWithAmount(0);
426 } else {
427 items[preferredIndex] = null;
428 }
429 }
430
431 fireItemUpdatedEvent(current, items[preferredIndex], preferredIndex, refresh);
432 } else {
433 int until = removeAll ? computeAmountForId(item.getId()) : items[preferredIndex].getAmount();
434 until = (item.getAmount() > until) ? until : item.getAmount();
435
436 for (int index = 0; index < until && index < capacity; index++) {
437 if (removeAll) {
438 if (preferredIndex < 0 || preferredIndex >= capacity) {
439 preferredIndex = computeIndexForId(item.getId());
440 } else if (items[preferredIndex] == null) {
441 preferredIndex = computeIndexForId(item.getId());
442 } else if (items[preferredIndex].getId() != item.getId()) {
443 preferredIndex = computeIndexForId(item.getId());
444 }
445 if (preferredIndex == -1) {
446 break;
447 }
448 }
449 Item oldItem = items[preferredIndex];
450 if (allowZero()) {
451 items[preferredIndex] = oldItem.createWithAmount(0);
452 } else {
453 items[preferredIndex] = null;
454 }
455 fireItemUpdatedEvent(oldItem, null, preferredIndex++, refresh);
456 }
457 }
458 return true;
459 }
460
468 public boolean removeAll(Collection<? extends Item> items) {
469 if (items.size() == 1) { // Bulk operation on singleton list? No thanks..
470 Optional<? extends Item> item = items.stream().
471 filter(Objects::nonNull).
472 findFirst();
473 return item.isPresent() && remove(item.get());
474 }
475
476 firingEvents = false;
477 boolean removed = false;
478 try {
479 for (Item item : items) {
480 if (item == null) {
481 continue;
482 }
483 if (remove(item, -1, false)) {
484 removed = true;
485 }
486 }
487 } finally {
488 firingEvents = true;
489 }
491 return removed;
492 }
493
501 public boolean removeAll(Item... items) {
502 return removeAll(Arrays.asList(items));
503 }
504
512 public boolean removeAll(ItemContainer items) {
513 return removeAll(items.items);
514 }
515
521 public long containerValue(PriceType type) {
522 long value = 0;
523
524 for (final Item item : items) {
525 if (item == null) {
526 continue;
527 }
528
529 int price = item.getValue(type);
530
531 if (value >= Long.MAX_VALUE - price * item.getAmount()) {
532 return Long.MAX_VALUE;
533 }
534
535 value += price * item.getAmount();
536 }
537
538 return value;
539 }
540
546 public final int computeFreeIndex() {
547 for (int index = 0; index < capacity; index++) {
548 if (items[index] == null) {
549 return index;
550 }
551 }
552 return -1;
553 }
554
562 public final int computeIndexForId(int id) {
563 for (int index = 0; index < capacity; index++) {
564 if (items[index] != null && items[index].getId() == id) {
565 return index;
566 }
567 }
568 return -1;
569 }
570
579 public final int computeAmountForId(int id) {
580 int amount = 0;
581 for (Item item : items) {
582 if (item == null || item.getId() != id)
583 continue;
584 amount += item.getAmount();
585 }
586 return amount;
587 }
588
595 public final Optional<Integer> computeIdForIndex(int index) {
596 return retrieve(index).map(Item::getId);
597 }
598
609 public final boolean replace(int oldId, int newId, int index, boolean refresh) {
610 Item oldItem = items[index];
611
612 if (oldItem == null || !oldItem.matchesId(oldId))
613 return false;
614
615 Item newItem = oldItem.createWithId(newId);
616 return remove(oldItem, index, refresh) && add(newItem, index, refresh);
617 }
618
629 public final boolean replace(int oldId, int newId, boolean refresh) {
630 int index = computeIndexForId(oldId);
631 if (index == -1) {
632 return false;
633 }
634
635 Item oldItem = items[index];
636
637 if (oldItem == null || !oldItem.matchesId(oldId))
638 return false;
639
640 Item newItem = oldItem.createWithId(newId);
641 return remove(oldItem, index, refresh) && add(newItem, index, refresh);
642 }
643
652 public final boolean replace(Item first, Item second, boolean refresh) {
653 int index = computeIndexForId(first.getId());
654
655 if (index == -1) {
656 return false;
657 }
658
659 Item oldItem = items[index];
660
661 if (oldItem == null || !oldItem.matchesId(first.getId()))
662 return false;
663
664 return remove(oldItem, index, refresh) && add(second, index, refresh);
665 }
666
676 public final boolean replaceAll(int oldId, int newId) {
677 boolean replaced = false;
678
679 firingEvents = false;
680 try {
681 while (replace(oldId, newId, false)) {
682 replaced = true;
683 }
684 } finally {
685 firingEvents = true;
686 }
688 return replaced;
689 }
690
698 public final int computeIndexCount(Item... forItems) {
699 int indexCount = 0;
700 for (Item item : forItems) {
701 if (item == null)
702 continue;
703 boolean stackable = (policy.equals(StackPolicy.STANDARD) && item.isStackable()) || policy.equals(StackPolicy.ALWAYS);
704
705 if (stackable) {
706 int index = computeIndexForId(item.getId());
707 if (index == -1) {
708 indexCount++;
709 continue;
710 }
711
712 Item existing = items[index];
713 if ((existing.getAmount() + item.getAmount()) <= 0) {
714 indexCount++;
715 }
716 } else {
717 indexCount += item.getAmount();
718 }
719 }
720 return indexCount;
721 }
722
730 public final boolean hasCapacityFor(Item... item) {
731 int indexCount = computeIndexCount(item);
732 return remaining() >= indexCount;
733 }
734
744 public final boolean hasCapacityAfter(Item[] add, Item... remove) {
745 ItemContainer container = new ItemContainer(capacity, policy, toArray());
746 container.removeAll(Arrays.copyOf(remove, remove.length));
747 return container.hasCapacityFor(add);
748 }
749
757 public boolean contains(int id) {
758 for (Item item : items) {
759 if (item != null && id == item.getId()) {
760 return true;
761 }
762 }
763 return false;
764 }
765
773 public final boolean containsAll(int... identifiers) {
774 for (int id : identifiers) {
775 if (!contains(id)) {
776 return false;
777 }
778 }
779 return true;
780 }
781
789 public final boolean containsAny(int... identifiers) {
790 for (int id : identifiers) {
791 if (contains(id)) {
792 return true;
793 }
794 }
795 return false;
796 }
797
799 public final boolean contains(Item item) {
800 return item != null && contains(item.getId(), item.getAmount());
801 }
802
804 public final boolean contains(int id, int amount) {
805 for (Item item : items) {
806 if (item != null && id == item.getId()) {
807 amount -= item.getAmount();
808 if (amount <= 0) return true;
809 }
810 }
811 return false;
812 }
813
815 public final boolean containsAll(Item... items) {
816 for (Item item : items) {
817 if (!contains(item)) {
818 return false;
819 }
820 }
821 return true;
822 }
823
825 public final boolean containsAll(Collection<Item> items) {
826 for (Item item : items) {
827 if (!contains(item)) {
828 return false;
829 }
830 }
831 return true;
832 }
833
835 public final boolean containsAny(Item... items) {
836 for (Item item : items) {
837 if (contains(item)) {
838 return true;
839 }
840 }
841 return false;
842 }
843
845 public final boolean containsAny(Collection<Item> items) {
846 for (Item item : items) {
847 if (contains(item)) {
848 return true;
849 }
850 }
851 return false;
852 }
853
859 public void refresh(Player player, int widget) {
860 player.send(new SendItemOnInterface(widget, items));
861 onRefresh();
862 }
863
865 public void onRefresh() {
866 /* can be overriden */
867 }
868
875 public final void swap(int oldIndex, int newIndex) {
876 swap(false, oldIndex, newIndex, true);
877 }
878
888 public final void swap(boolean insert, int oldIndex, int newIndex, boolean refresh) {
889 if (insert) {
890 insert(oldIndex, newIndex, refresh);
891 } else {
892 swap(oldIndex, newIndex, refresh);
893 }
894 }
895
896 public final void swap(int oldIndex, int newIndex, boolean refresh) {
897 checkArgument(oldIndex >= 0 && oldIndex < capacity, "[swap] oldIndex out of range - [old=" + oldIndex + ", new=" + newIndex + ", refresh=" + refresh + ", size=" + size() + ", capacity=" + capacity + "]");
898 checkArgument(newIndex >= 0 && newIndex < capacity, "[swap] newIndex out of range - [old=" + oldIndex + ", new=" + newIndex + ", refresh=" + refresh + ", size=" + size() + ", capacity=" + capacity + "]");
899
900 Item itemOld = items[oldIndex];
901 Item itemNew = items[newIndex];
902
903 items[oldIndex] = itemNew;
904 items[newIndex] = itemOld;
905
906 fireItemUpdatedEvent(itemOld, items[oldIndex], oldIndex, refresh);
907 fireItemUpdatedEvent(itemNew, items[newIndex], newIndex, refresh);
908 }
909
910 public final void insert(int oldIndex, int newIndex, boolean refresh) {
911 checkArgument(oldIndex >= 0 && oldIndex < capacity, "[insert] oldIndex out of range - [old=" + oldIndex + ", new=" + newIndex + ", refresh=" + refresh + ", size=" + size() + ", capacity=" + capacity + "]");
912 checkArgument(newIndex >= 0 && newIndex < capacity, "[insert] newIndex out of range - [old=" + oldIndex + ", new=" + newIndex + ", refresh=" + refresh + ", size=" + size() + ", capacity=" + capacity + "]");
913
914 if (newIndex > oldIndex) {
915 for (int index = oldIndex; index < newIndex; index++) {
916 swap(index, index + 1, refresh);
917 }
918 } else if (oldIndex > newIndex) {
919 for (int index = oldIndex; index > newIndex; index--) {
920 swap(index, index - 1, refresh);
921 }
922 }
923 }
924
925 public final void transfer(int firstIndex, int secondIndex, ItemContainer other, boolean refresh) {
926 checkArgument(firstIndex >= 0 && firstIndex < capacity, "[transfer] firstIndex out of range - [first=" + firstIndex + ", second=" + secondIndex + ", refresh=" + refresh + "]");
927 checkArgument(secondIndex >= 0 && secondIndex < other.capacity, "[transfer] secondIndex out of range - [first=" + firstIndex + ", second=" + secondIndex + ", refresh=" + refresh + "]");
928 Item first = get(firstIndex);
929 Item second = other.get(secondIndex);
930 set(firstIndex, second, true);
931 other.set(secondIndex, first, true);
932 }
933
937 public void shift() {
938 Item[] newItems = new Item[capacity];
939 int newIndex = 0;
940
941 for (Item item : items) {
942 if (item == null) {
943 continue;
944 }
945 newItems[newIndex++] = item;
946 }
947
948 setItems(newItems);
949 }
950
958 public final void setItems(Item[] items, boolean copy) {
959 Preconditions.checkArgument(items.length <= capacity);
960 clear();
961 for (int i = 0; i < items.length; i++) {
962 this.items[i] = items[i] == null ? null : copy ? items[i].copy() : items[i];
963 }
965 }
966
967 public final void setItems(Item[] items) {
968 setItems(items, true);
969 }
970
971 public final void set(Item[] toSet) {
972 items = toSet;
973 }
974
981 public final Item[] toArray() {
982 return Arrays.copyOf(items, items.length);
983 }
984
985 public final Item[] toNonNullArray() {
986 if (size() == 0) {
987 return new Item[0];
988 }
989
990 final List<Item> items = new ArrayList<>(size());
991
992 for (Item item : getItems()) {
993 if (item == null) {
994 continue;
995 }
996
997 items.add(item);
998 }
999
1000 return items.toArray(new Item[items.size()]);
1001 }
1002
1008 public double getWeight() {
1009 double weight = 0;
1010 for (Item item : toArray()) {
1011 if (item == null)
1012 continue;
1013 weight += item.getWeight();
1014 }
1015 return weight;
1016 }
1017
1025 public void set(int index, Item item, boolean refresh) {
1026 Item oldItem = items[index];
1027 items[index] = item;
1028 fireItemUpdatedEvent(oldItem, items[index], index, refresh);
1029 }
1030
1038 public final Optional<Item> retrieve(int index) {
1039 if (index >= 0 && index < items.length)
1040 return Optional.ofNullable(items[index]);
1041 return Optional.empty();
1042 }
1043
1050 public final void ifPresent(int index, Consumer<Item> action) {
1051 if (index >= 0 && index < items.length)
1052 action.accept(items[index]);
1053 }
1054
1061 public final Item get(int index) {
1062 if (index >= 0 && index < items.length)
1063 return items[index];
1064 return null;
1065 }
1066
1073 public final int getId(int index) {
1074 if (items[index] == null) {
1075 return -1;
1076 }
1077 return items[index].getId();
1078 }
1079
1087 public Optional<Item> search(int id) {
1088 return stream().filter(i -> i != null && id == i.getId()).findFirst();
1089 }
1090
1099 public Optional<Item> search(Item item) {
1100 return stream().filter(i -> i != null && item.getId() == i.getId() && item.getAmount() == i.getAmount()).findFirst();
1101 }
1102
1106 public final boolean indexOccupied(int index) {
1107 return retrieve(index).isPresent();
1108 }
1109
1113 public final boolean indexFree(int index) {
1114 return !indexOccupied(index);
1115 }
1116
1122 public final ItemContainer copy() {
1123 ItemContainer container = new ItemContainer(this.capacity, this.policy, this.toArray());
1124 this.listeners.forEach(container::addListener);
1125 return container;
1126 }
1127
1129 public void clear() {
1130 clear(true);
1131 }
1132
1134 public final void clear(boolean refresh) {
1135 Arrays.fill(items, null);
1136 if (refresh)
1138 }
1139
1141 public final boolean isEmpty() {
1142 return size() == 0;
1143 }
1144
1145 public final boolean isFull() {
1146 return getFreeSlots() == 0;
1147 }
1148
1155 public final boolean addListener(ItemContainerListener listener) {
1156 return listeners.add(listener);
1157 }
1158
1166 public final boolean removeListener(ItemContainerListener listener) {
1167 return listeners.remove(listener);
1168 }
1169
1174
1175 public final void fireItemUpdatedEvent(Item oldItem, Item newItem, int index, boolean refresh) {
1176 fireItemUpdatedEvent(oldItem, newItem, index, refresh, false);
1177 }
1178
1179 public final void fireItemUpdatedEvent(Item oldItem, Item newItem, int index, boolean refresh, boolean login) {
1180 if (firingEvents) {
1181 listeners.forEach(evt -> evt.itemUpdated(this, Optional.ofNullable(oldItem), Optional.ofNullable(newItem), index, refresh, login));
1182 }
1183 }
1184
1189 public final void fireBulkItemsUpdatedEvent() {
1190 if (firingEvents) {
1191 listeners.forEach(evt -> evt.bulkItemsUpdated(this));
1192 }
1193 }
1194
1199 public final void fireCapacityExceededEvent() {
1200 if (firingEvents) {
1201 listeners.forEach(evt -> evt.capacityExceeded(this));
1202 }
1203 }
1204
1206 public Item[] getItems() {
1207 return items;
1208 }
1209
1211 public void setFiringEvents(boolean firingEvents) {
1212 this.firingEvents = firingEvents;
1213 }
1214
1216 public final int remaining() {
1217 return capacity - size();
1218 }
1219
1221 public int getFreeSlots() {
1222 return capacity() - size();
1223 }
1224
1226 public final int size() {
1227 return (int) Arrays.stream(items).filter(Objects::nonNull).count();
1228 }
1229
1231 public final int capacity() {
1232 return capacity;
1233 }
1234
1236 public final StackPolicy policy() {
1237 return policy;
1238 }
1239
1241 public boolean allowZero() {
1242 return false;
1243 }
1244
1252 public int getSlotById(int id) {
1253 for (int i = 0; i < items.length; i++) {
1254 if (items[i] == null) {
1255 continue;
1256 }
1257 if (items[i].getId() == id) {
1258 return i;
1259 }
1260 }
1261 return -1;
1262 }
1263
1264// public boolean isFull() {
1265// return size() == capacity();
1266// }
1267}
final Optional< Integer > computeIdForIndex(int index)
final boolean replace(int oldId, int newId, int index, boolean refresh)
ItemContainer(int capacity, StackPolicy policy, Item[] items)
final boolean replace(Item first, Item second, boolean refresh)
final boolean replace(int oldId, int newId, boolean refresh)
final boolean containsAny(Collection< Item > items)
final boolean hasCapacityAfter(Item[] add, Item... remove)
final void fireItemUpdatedEvent(Item oldItem, Item newItem, int index, boolean refresh)
final void forEach(Consumer<? super Item > action)
final void swap(int oldIndex, int newIndex)
final boolean addListener(ItemContainerListener listener)
boolean add(Item item, int preferredIndex, boolean refresh, boolean stack)
final boolean removeListener(ItemContainerListener listener)
final void setItems(Item[] items, boolean copy)
final boolean containsAll(Collection< Item > items)
boolean addAll(Collection<? extends Item > items)
final void ifPresent(int index, Consumer< Item > action)
boolean removeAll(Collection<? extends Item > items)
final void swap(boolean insert, int oldIndex, int newIndex, boolean refresh)