mirror of
https://github.com/garbagemule/MobArena.git
synced 2024-11-25 12:06:14 +01:00
Add InventoryThing collection.
Adds three new Thing types that can be used to reference items in chests (or any block-based InventoryHolder): - InventoryIndexThing looks up an item by index/slot in an inventory. - InventoryGroupThing groups all non-null/non-air items in an inventory into a ThingGroup. - InventoryRangeThing groups all non-null/non-air items in a given range of an inventory into a ThingGroup. The new Thing types aim to bridge a gap between the class chests and the rest of the Thing-based parts of the config-file. The goal is two-fold: allow for more in-game configuration so access to the config-file isn't _quite_ as crucial, and propagate the item-wise feature completeness of class chests to other parts of the plugin. While class chests are low configuration and a bit "all or nothing", the inventory Thing types require manually punching in the coords for chests and possibly indices/ranges for items. This means that the initial setup could be a bit unwieldy, and highly volatile wave setups are definitely not a good fit. If the wave setup is mostly pre-defined, it is fairly easy to tweak upgrade waves and rewards in the same way class chests are tweaked. As for item-wise feature completeness, the inventory Thing types share the same "if Bukkit can copy it, it will work" rule of thumb as class chests do, which means items with metadata such as custom names, lore, or even NBTs, should just work. This could remove the need to employ other plugins. By no means can this solution be considered "optimal", but it it _does_ enable some long-requested features. Closes #456
This commit is contained in:
parent
1a7109a1d4
commit
d7336526e1
@ -11,6 +11,9 @@ These changes will (most likely) be included in the next version.
|
||||
|
||||
|
||||
## [Unreleased]
|
||||
### Added
|
||||
- Support for chest references in item syntax. The new `inv` syntax allows for referencing container indices in the config-file. This should help bridge the gap between class chests and various other parts of the config-file, such as rewards and upgrade waves.
|
||||
|
||||
### Fixed
|
||||
- Explosion damage caused by Exploding Sheep now correctly counts as monster damage. This means that the explosions only affect other mobs if the per-arena setting `monster-infight` is set to `true`.
|
||||
- Explosion damage caused by the boss ability `obsidian-bomb` now correctly counts as monster damage. This means that the explosions only affect other mobs if the per-arena setting `monster-infight` is set to `true`.
|
||||
|
@ -0,0 +1,42 @@
|
||||
package com.garbagemule.MobArena.things;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
class InventoryGroupThing extends InventoryThing {
|
||||
|
||||
InventoryGroupThing(Supplier<Location> location) {
|
||||
super(location);
|
||||
}
|
||||
|
||||
@Override
|
||||
Thing load() {
|
||||
Inventory inventory = super.getInventory();
|
||||
if (inventory == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<Thing> things = new ArrayList<>();
|
||||
for (ItemStack stack : inventory) {
|
||||
if (stack != null && stack.getType() != Material.AIR) {
|
||||
things.add(new ItemStackThing(stack));
|
||||
}
|
||||
}
|
||||
|
||||
if (things.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
if (things.size() == 1) {
|
||||
return things.get(0);
|
||||
}
|
||||
|
||||
return new ThingGroup(things);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,40 @@
|
||||
package com.garbagemule.MobArena.things;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
class InventoryIndexThing extends InventoryThing {
|
||||
|
||||
private final int index;
|
||||
|
||||
InventoryIndexThing(
|
||||
Supplier<Location> location,
|
||||
int index
|
||||
) {
|
||||
super(location);
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
@Override
|
||||
Thing load() {
|
||||
Inventory inventory = super.getInventory();
|
||||
if (inventory == null) {
|
||||
return null;
|
||||
}
|
||||
if (inventory.getSize() <= index) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ItemStack stack = inventory.getItem(index);
|
||||
if (stack == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ItemStack clone = stack.clone();
|
||||
return new ItemStackThing(clone);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
package com.garbagemule.MobArena.things;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
class InventoryRangeThing extends InventoryThing {
|
||||
|
||||
private final int first;
|
||||
private final int last;
|
||||
|
||||
InventoryRangeThing(
|
||||
Supplier<Location> location,
|
||||
int first,
|
||||
int last
|
||||
) {
|
||||
super(location);
|
||||
this.first = first;
|
||||
this.last = last;
|
||||
}
|
||||
|
||||
@Override
|
||||
Thing load() {
|
||||
Inventory inventory = super.getInventory();
|
||||
if (inventory == null) {
|
||||
return null;
|
||||
}
|
||||
if (inventory.getSize() <= last) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<Thing> things = new ArrayList<>();
|
||||
ItemStack[] content = inventory.getContents();
|
||||
for (int i = first; i <= last; i++) {
|
||||
ItemStack stack = content[i];
|
||||
if (stack != null && stack.getType() != Material.AIR) {
|
||||
things.add(new ItemStackThing(stack));
|
||||
}
|
||||
}
|
||||
|
||||
if (things.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
if (things.size() == 1) {
|
||||
return things.get(0);
|
||||
}
|
||||
|
||||
return new ThingGroup(things);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,68 @@
|
||||
package com.garbagemule.MobArena.things;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.block.BlockState;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.inventory.Inventory;
|
||||
import org.bukkit.inventory.InventoryHolder;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
abstract class InventoryThing implements Thing {
|
||||
|
||||
private final Supplier<Location> location;
|
||||
|
||||
protected InventoryThing(Supplier<Location> location) {
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean giveTo(Player player) {
|
||||
Thing thing = load();
|
||||
if (thing == null) {
|
||||
return false;
|
||||
}
|
||||
return thing.giveTo(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean takeFrom(Player player) {
|
||||
Thing thing = load();
|
||||
if (thing == null) {
|
||||
return false;
|
||||
}
|
||||
return thing.takeFrom(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean heldBy(Player player) {
|
||||
Thing thing = load();
|
||||
if (thing == null) {
|
||||
return false;
|
||||
}
|
||||
return thing.heldBy(player);
|
||||
}
|
||||
|
||||
abstract Thing load();
|
||||
|
||||
Inventory getInventory() {
|
||||
Location location = this.location.get();
|
||||
if (location != null) {
|
||||
Block block = location.getBlock();
|
||||
BlockState state = block.getState();
|
||||
if (state instanceof InventoryHolder) {
|
||||
InventoryHolder holder = (InventoryHolder) state;
|
||||
return holder.getInventory();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
Thing thing = load();
|
||||
return (thing != null) ? thing.toString() : "nothing";
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,80 @@
|
||||
package com.garbagemule.MobArena.things;
|
||||
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.World;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class InventoryThingParser implements ThingParser {
|
||||
|
||||
private static final String PREFIX = "inv(";
|
||||
private static final String SUFFIX = ")";
|
||||
|
||||
private final Server server;
|
||||
|
||||
InventoryThingParser(Server server) {
|
||||
this.server = server;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InventoryThing parse(String s) {
|
||||
if (!s.startsWith(PREFIX) || !s.endsWith(SUFFIX)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Trim prefix and suffix
|
||||
int start = PREFIX.length();
|
||||
int end = s.length() - SUFFIX.length();
|
||||
String inner = s.substring(start, end);
|
||||
|
||||
// Split by whitespace to get all the parts
|
||||
String[] parts = inner.split("\\s+");
|
||||
if (parts.length != 5) {
|
||||
throw new IllegalArgumentException("Expected format " + PREFIX + "world x y z slot" + SUFFIX + ", got: " + s);
|
||||
}
|
||||
|
||||
// Extract location
|
||||
String name = parts[0];
|
||||
int x = Integer.parseInt(parts[1]);
|
||||
int y = Integer.parseInt(parts[2]);
|
||||
int z = Integer.parseInt(parts[3]);
|
||||
Supplier<Location> location = () -> {
|
||||
World world = server.getWorld(name);
|
||||
return new Location(world, x, y ,z);
|
||||
};
|
||||
|
||||
// Determine type by slot value
|
||||
String slot = parts[4];
|
||||
if (slot.equals("all")) {
|
||||
return group(location);
|
||||
}
|
||||
if (slot.contains("-")) {
|
||||
return range(location, slot);
|
||||
}
|
||||
return index(location, slot);
|
||||
}
|
||||
|
||||
private InventoryThing group(Supplier<Location> location) {
|
||||
return new InventoryGroupThing(location);
|
||||
}
|
||||
|
||||
private InventoryThing range(Supplier<Location> location, String slot) {
|
||||
String[] indices = slot.split("-");
|
||||
if (indices.length != 2) {
|
||||
throw new IllegalArgumentException("Expected range format (e.g. 0-8), got: " + slot);
|
||||
}
|
||||
int first = Integer.parseInt(indices[0]);
|
||||
int last = Integer.parseInt(indices[1]);
|
||||
if (last < first) {
|
||||
throw new IllegalArgumentException("Range end is less than range start: " + slot);
|
||||
}
|
||||
return new InventoryRangeThing(location, first, last);
|
||||
}
|
||||
|
||||
private InventoryThing index(Supplier<Location> location, String slot) {
|
||||
int index = Integer.parseInt(slot);
|
||||
return new InventoryIndexThing(location, index);
|
||||
}
|
||||
|
||||
}
|
@ -16,6 +16,7 @@ public class ThingManager implements ThingParser {
|
||||
parsers.add(new MoneyThingParser(plugin));
|
||||
parsers.add(new PermissionThingParser(plugin));
|
||||
parsers.add(new PotionEffectThingParser());
|
||||
parsers.add(new InventoryThingParser(plugin.getServer()));
|
||||
items = parser;
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,124 @@
|
||||
package com.garbagemule.MobArena.things;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.MatcherAssert.*;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
|
||||
public class InventoryThingParserTest {
|
||||
|
||||
private InventoryThingParser subject;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
subject = new InventoryThingParser(null);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void returnsNullOnWrongType() {
|
||||
String input = "dirt";
|
||||
|
||||
Thing result = subject.parse(input);
|
||||
|
||||
assertThat(result, nullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void returnsNullOnMismatchedParenthesis() {
|
||||
String input = "inv(world -10 20 130 15";
|
||||
|
||||
Thing result = subject.parse(input);
|
||||
|
||||
assertThat(result, nullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void throwsOnTooFewArguments() {
|
||||
String input = "inv(world -10 20 130)";
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> subject.parse(input)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void throwsOnTooManyArguments() {
|
||||
String input = "inv(world -10 20 130 15 16)";
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> subject.parse(input)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void throwsOnUnknownSlotType() {
|
||||
String input = "inv(world -10 20 130 potato)";
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> subject.parse(input)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parsesIndexThing() {
|
||||
String input = "inv(world -10 20 130 15)";
|
||||
|
||||
Thing result = subject.parse(input);
|
||||
|
||||
assertThat(result, instanceOf(InventoryIndexThing.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void throwsIfIndexIsNegative() {
|
||||
String input = "inv(world -10 20 130 -1)";
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> subject.parse(input)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parsesRangeThing() {
|
||||
String input = "inv(world -10 20 130 0-8)";
|
||||
|
||||
Thing result = subject.parse(input);
|
||||
|
||||
assertThat(result, instanceOf(InventoryRangeThing.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void throwsIfRangeStartIsLessThanZero() {
|
||||
String input = "inv(world -10 20 130 -1-8)";
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> subject.parse(input)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void throwsIfRangeEndIsLessThanRangeStart() {
|
||||
String input = "inv(world -10 20 130 5-2)";
|
||||
|
||||
assertThrows(
|
||||
IllegalArgumentException.class,
|
||||
() -> subject.parse(input)
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parsesAllGroup() {
|
||||
String input = "inv(world -10 20 130 all)";
|
||||
|
||||
Thing result = subject.parse(input);
|
||||
|
||||
assertThat(result, instanceOf(InventoryGroupThing.class));
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user