Inventory improvements: (expand commit for details).

- Fixed a bug with Inventory#add(Item) not handling integer overflow properly.
- Fixed a bug with Inventory#add(Item) returning an Item object with an amount of 0 instead of null.
- Inventory#add(Item) now returns an Optional<Item> object and cleaned up portions of the method.
- code clean-up to Inventory#remove(int, int).
- Inventory#remove(int, int) now supports stopping and refiring of listeners while the inventory is being updated (prevents flickering and unnecessary updating of the inventory for batch updates).
This commit is contained in:
Ryley Kimmel
2015-02-25 15:17:41 -05:00
parent 4bcb7914a2
commit 87ff14c4d7
+74 -55
View File
@@ -3,6 +3,7 @@ package org.apollo.game.model.inv;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Optional;
import org.apollo.game.model.Item; import org.apollo.game.model.Item;
import org.apollo.game.model.def.ItemDefinition; import org.apollo.game.model.def.ItemDefinition;
@@ -116,84 +117,92 @@ public final class Inventory {
* @return The amount that remains. * @return The amount that remains.
*/ */
public int add(int id, int amount) { public int add(int id, int amount) {
Item item = add(new Item(id, amount)); Optional<Item> optionalItem = add(new Item(id, amount));
return (item != null) ? item.getAmount() : 0; return optionalItem.map(item -> optionalItem.isPresent() ? item.getAmount() : 0).get();
} }
/** /**
* Adds an item to this inventory. This will attempt to add as much of the item that is possible. If the item * Attempts to add as much of the specified {@code item} to this inventory
* remains, it will be returned (in the case of stackable items, any quantity that remains in the stack is * as possible. If any of the item remains, an {@link Item item with the
* returned). If nothing remains, the method will return {@code null}. If something remains, the listener will also * remainder} will be returned (in the case of stack-able items, any
* be notified which could be used, for example, to send a message to the player. * quantity that remains in the stack is returned). If nothing remains, the
* * method will return {@link Optional#empty an empty Optional}.
*
* <p>
* If anything remains at all, the listener will be notified which could be
* used for notifying a player that their inventory is full, for example.
*
* @param item The item to add to this inventory. * @param item The item to add to this inventory.
* @return The item that remains if there is not enough room in the inventory. If nothing remains, {@code null}. * @return The item that may remain, if nothing remains,
* {@link Optional#empty an empty Optional} is returned.
*/ */
public Item add(Item item) { public Optional<Item> add(Item item) {
int id = item.getId(); int id = item.getId();
boolean stackable = isStackable(item.getDefinition()); boolean stackable = isStackable(item.getDefinition());
if (stackable) { if (stackable) {
for (int slot = 0; slot < capacity; slot++) { int slot = slotOf(id);
if (slot != -1) {
Item other = items[slot]; Item other = items[slot];
if (other != null && other.getId() == id) { long total = (long) item.getAmount() + other.getAmount();
long total = item.getAmount() + other.getAmount(); int amount, remaining;
int amount, remaining;
if (total > Integer.MAX_VALUE) { if (total > Integer.MAX_VALUE) {
amount = (int) (total - Integer.MAX_VALUE); amount = (int) (total - Integer.MAX_VALUE);
remaining = (int) (total - amount); remaining = (int) (total - amount);
notifyCapacityExceeded(); notifyCapacityExceeded();
} else { } else {
amount = (int) total; amount = (int) total;
remaining = 0; remaining = 0;
}
set(slot, new Item(id, amount));
return remaining > 0 ? new Item(id, remaining) : null;
} }
set(slot, new Item(id, amount));
return remaining > 0 ? Optional.of(new Item(id, remaining)) : Optional.empty();
} }
for (int slot = 0; slot < capacity; slot++) { for (slot = 0; slot < capacity; slot++) {
Item other = items[slot]; if (items[slot] == null) {
if (other == null) {
set(slot, item); set(slot, item);
return null; return Optional.empty();
} }
} }
notifyCapacityExceeded(); notifyCapacityExceeded();
return item; return Optional.of(item);
} }
int remaining = item.getAmount(); int remaining = item.getAmount();
if (remaining == 0) {
return Optional.empty();
}
stopFiringEvents(); stopFiringEvents();
try { try {
Item single = new Item(item.getId(), 1); Item single = new Item(item.getId(), 1);
for (int slot = 0; slot < capacity; slot++) { for (int slot = 0; slot < capacity; slot++) {
if (items[slot] == null) { if (items[slot] == null) {
remaining--;
set(slot, single); // share the instances set(slot, single); // share the instances
if (remaining <= 0) { if (--remaining <= 0) {
break; return Optional.empty();
} }
} }
} }
} finally { } finally {
startFiringEvents(); startFiringEvents();
if (remaining != item.getAmount()) {
notifyItemsUpdated();
}
} }
if (remaining != item.getAmount()) { notifyCapacityExceeded();
notifyItemsUpdated();
}
if (remaining > 0) {
notifyCapacityExceeded();
}
return new Item(item.getId(), remaining); return Optional.of(new Item(item.getId(), remaining));
} }
/** /**
@@ -442,19 +451,19 @@ public final class Inventory {
* @return The amount that was removed. * @return The amount that was removed.
*/ */
public int remove(int id, int amount) { public int remove(int id, int amount) {
ItemDefinition definition = ItemDefinition.lookup(id); ItemDefinition def = ItemDefinition.lookup(id);
boolean stackable = isStackable(definition); boolean stackable = isStackable(def);
if (stackable) { if (stackable) {
for (int slot = 0; slot < capacity; slot++) { int slot = slotOf(id);
if (slot != -1) {
Item item = items[slot]; Item item = items[slot];
if (item != null && item.getId() == id) { if (amount >= item.getAmount()) {
if (amount >= item.getAmount()) { set(slot, null);
set(slot, null); return item.getAmount();
return item.getAmount(); } else {
}
set(slot, new Item(item.getId(), item.getAmount() - amount)); set(slot, new Item(item.getId(), item.getAmount() - amount));
return amount; return amount;
} }
@@ -464,16 +473,26 @@ public final class Inventory {
} }
int removed = 0; int removed = 0;
for (int slot = 0; slot < capacity; slot++) {
Item item = items[slot];
if (item != null && item.getId() == id) { stopFiringEvents();
set(slot, null);
removed++; try {
for (int slot = 0; slot < capacity; slot++) {
Item item = items[slot];
if (item != null && item.getId() == id) {
set(slot, null);
if (++removed >= amount) {
break;
}
}
} }
if (removed >= amount) { } finally {
break; startFiringEvents();
if (removed > 0) {
notifyItemsUpdated();
} }
} }