diff --git a/src/main/java/carpet/script/CarpetExpression.java b/src/main/java/carpet/script/CarpetExpression.java index 38600480..8d8f14b2 100644 --- a/src/main/java/carpet/script/CarpetExpression.java +++ b/src/main/java/carpet/script/CarpetExpression.java @@ -19,11 +19,14 @@ import carpet.script.value.StringValue; import carpet.script.value.Value; import carpet.utils.BlockInfo; import carpet.utils.Messenger; +import com.google.common.collect.Sets; import com.mojang.brigadier.StringReader; import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.block.entity.BlockEntity; +import net.minecraft.command.arguments.ItemStackArgument; +import net.minecraft.command.arguments.ItemStringReader; import net.minecraft.enchantment.Enchantments; import net.minecraft.entity.EntityType; import net.minecraft.entity.EquipmentSlot; @@ -50,10 +53,7 @@ import net.minecraft.predicate.entity.EntityPredicates; import net.minecraft.text.LiteralText; import net.minecraft.util.Identifier; import net.minecraft.sound.SoundCategory; -import net.minecraft.util.math.Box; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.EulerRotation; -import net.minecraft.util.math.Vec3d; +import net.minecraft.util.math.*; import net.minecraft.util.registry.Registry; import net.minecraft.world.LightType; import net.minecraft.world.World; @@ -899,19 +899,125 @@ public class CarpetExpression }); //inventory_find(, or null (first empty slot), ) -> or null + this.expr.addLazyFunction("inventory_find", -1, (c, t, lv) -> { + //long stime = System.nanoTime(); + CarpetContext cc = (CarpetContext) c; + NBTSerializableValue.InventoryLocator inventoryLocator = NBTSerializableValue.locateInventory(cc, lv, 0); + ItemStackArgument itemArg = null; + if (lv.size() > inventoryLocator.offset) + { + Value secondArg = lv.get(inventoryLocator.offset+0).evalValue(c); + if (!(secondArg instanceof NullValue)) + { + itemArg = NBTSerializableValue.parseItem(secondArg.getString()); + if (itemArg == null) + throw new InternalExpressionException("Incorrect item format: "+secondArg.getString()); + } + } + int startIndex = 0; + if (lv.size() > inventoryLocator.offset+1) + { + startIndex = (int) NumericValue.asNumber(lv.get(inventoryLocator.offset+1).evalValue(c)).getLong(); + } + startIndex = NBTSerializableValue.validateSlot(startIndex, inventoryLocator.inventory); + for (int i = startIndex, maxi = inventoryLocator.inventory.getInvSize(); i < maxi; i++) + { + ItemStack stack = inventoryLocator.inventory.getInvStack(i); + if ( (itemArg == null && stack.isEmpty()) || (itemArg != null && itemArg.getItem().equals(stack.getItem())) ) + { + Value res = new NumericValue(i); + return (_c, _t) -> res; + } + } + //long etime = System.nanoTime(); + //CarpetSettings.LOG.error("took "+(etime-stime)/1000); + return (_c, _t) -> Value.NULL; + }); + //inventory_set(, , , ) + this.expr.addLazyFunction("inventory_set", -1, (c, t, lv) -> { + //long stime = System.nanoTime(); + CarpetContext cc = (CarpetContext) c; + NBTSerializableValue.InventoryLocator inventoryLocator = NBTSerializableValue.locateInventory(cc, lv, 0); + + if (lv.size() <= inventoryLocator.offset+2) + throw new InternalExpressionException("inventory_set requires at least slot number and new stack size, and optional new item"); + int slot = (int) NumericValue.asNumber(lv.get(inventoryLocator.offset+0).evalValue(c)).getLong(); + slot = NBTSerializableValue.validateSlot(slot, inventoryLocator.inventory); + int count = (int) NumericValue.asNumber(lv.get(inventoryLocator.offset+1).evalValue(c)).getLong(); + if (count == 0) + { + // clear slot + ItemStack removedStack = inventoryLocator.inventory.removeInvStack(slot); + //Value res = ListValue.fromItemStack(removedStack); + return (_c, _t) -> ListValue.fromItemStack(removedStack); // that tuple will be read only but cheaper if noone cares + } + if (lv.size() <= inventoryLocator.offset+3) + { + ItemStack previousStack = inventoryLocator.inventory.getInvStack(slot); + ItemStack newStack = previousStack.copy(); + newStack.setCount(count); + inventoryLocator.inventory.setInvStack(slot, newStack); + return (_c, _t) -> ListValue.fromItemStack(previousStack); + } + ItemStackArgument newitem = NBTSerializableValue.parseItem(lv.get(inventoryLocator.offset+2).evalValue(c).getString()); + ItemStack previousStack = inventoryLocator.inventory.getInvStack(slot); + try + { + inventoryLocator.inventory.setInvStack(slot, newitem.createStack(count, false)); + } + catch (CommandSyntaxException e) + { + throw new InternalExpressionException(e.getMessage()); + } + return (_c, _t) -> ListValue.fromItemStack(previousStack); + }); + //inventory_remove(, , ) -> bool + this.expr.addLazyFunction("inventory_remove", -1, (c, t, lv) -> { + //long stime = System.nanoTime(); + CarpetContext cc = (CarpetContext) c; + NBTSerializableValue.InventoryLocator inventoryLocator = NBTSerializableValue.locateInventory(cc, lv, 0); - - /* - if (slot > -1) + if (lv.size() <= inventoryLocator.offset+1) + throw new InternalExpressionException("inventory_set requires at least an item to be removed"); + ItemStackArgument searchItem = NBTSerializableValue.parseItem(lv.get(inventoryLocator.offset).evalValue(c).getString()); + int amount = 1; + if (lv.size() > inventoryLocator.offset+2) { - if (slot >= inv.getInvSize()) - throw new InternalExpressionException("Inventory of "+e+" has "+inv.getInvSize()+" slots only"); - itemstack = inv.getInvStack(slot); + amount = (int)NumericValue.asNumber(lv.get(inventoryLocator.offset+2).evalValue(c)).getLong(); } - */ + // not enough + if ( (amount == 1) && (inventoryLocator.inventory.containsAnyInInv(Sets.newHashSet(searchItem.getItem()))) || + inventoryLocator.inventory.getInvAmountOf(searchItem.getItem()) >= amount ) + return (_c, _t) -> Value.FALSE; + for (int i = 0, maxi = inventoryLocator.inventory.getInvSize(); i < maxi; i++) + { + ItemStack stack = inventoryLocator.inventory.getInvStack(i); + if (stack.isEmpty()) + continue; + if (!stack.getItem().equals(searchItem.getItem())) + continue; + int left = stack.getCount()-amount; + if (left > 0) + { + stack.setCount(left); + inventoryLocator.inventory.setInvStack(i, stack); + return (_c, _t) -> Value.TRUE; + } + else + { + inventoryLocator.inventory.removeInvStack(i); + amount -= stack.getCount(); + } + } + if (amount > 0) + throw new InternalExpressionException("Something bad happened - cannot pull all items from inventory"); + return (_c, _t) -> Value.TRUE; + + }); + } /** diff --git a/src/main/java/carpet/script/value/NBTSerializableValue.java b/src/main/java/carpet/script/value/NBTSerializableValue.java index 1feac122..8e3a0efa 100644 --- a/src/main/java/carpet/script/value/NBTSerializableValue.java +++ b/src/main/java/carpet/script/value/NBTSerializableValue.java @@ -3,7 +3,12 @@ package carpet.script.value; import carpet.script.CarpetContext; import carpet.script.LazyValue; import carpet.script.exception.InternalExpressionException; +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.minecraft.block.entity.HopperBlockEntity; +import net.minecraft.command.arguments.ItemStackArgument; +import net.minecraft.command.arguments.ItemStackArgumentType; +import net.minecraft.command.arguments.ItemStringReader; import net.minecraft.entity.Entity; import net.minecraft.entity.passive.AbstractTraderEntity; import net.minecraft.entity.passive.VillagerEntity; @@ -12,8 +17,11 @@ import net.minecraft.inventory.Inventory; import net.minecraft.item.ItemStack; import net.minecraft.nbt.CompoundTag; import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.function.Supplier; public class NBTSerializableValue extends Value @@ -26,7 +34,7 @@ public class NBTSerializableValue extends Value public NBTSerializableValue(ItemStack stack) { what = stack; - nbtSupplier = () -> ((ItemStack)what).toTag(new CompoundTag()); + nbtSupplier = () -> ((ItemStack) what).toTag(new CompoundTag()); } public static InventoryLocator locateInventory(CarpetContext c, List params, int offset) @@ -39,13 +47,13 @@ public class NBTSerializableValue extends Value { Entity e = ((EntityValue) v1).getEntity(); if (e instanceof PlayerEntity) inv = ((PlayerEntity) e).inventory; - else if (e instanceof Inventory) inv = (Inventory)e; + else if (e instanceof Inventory) inv = (Inventory) e; else if (e instanceof VillagerEntity) inv = ((VillagerEntity) e).getInventory(); if (inv == null) - throw new InternalExpressionException("Entity "+e+" has no inventory"); + throw new InternalExpressionException("Entity " + e + " has no inventory"); - return new InventoryLocator(e, e.getBlockPos(), inv, offset+1); + return new InventoryLocator(e, e.getBlockPos(), inv, offset + 1); } else if (v1 instanceof BlockValue) { @@ -54,8 +62,8 @@ public class NBTSerializableValue extends Value throw new InternalExpressionException("Block to acess inventory needs to be positioned in the world"); inv = HopperBlockEntity.getInventoryAt(c.s.getWorld(), pos); if (inv == null) - throw new InternalExpressionException("Block at "+pos+" has no inventory"); - return new InventoryLocator(pos, pos, inv, offset+1); + throw new InternalExpressionException("Block at " + pos + " has no inventory"); + return new InventoryLocator(pos, pos, inv, offset + 1); } else if (v1 instanceof ListValue) { @@ -66,8 +74,8 @@ public class NBTSerializableValue extends Value NumericValue.asNumber(args.get(2)).getDouble()); inv = HopperBlockEntity.getInventoryAt(c.s.getWorld(), pos); if (inv == null) - throw new InternalExpressionException("Block at "+pos+" has no inventory"); - return new InventoryLocator(pos, pos, inv, offset+1); + throw new InternalExpressionException("Block at " + pos + " has no inventory"); + return new InventoryLocator(pos, pos, inv, offset + 1); } BlockPos pos = new BlockPos( NumericValue.asNumber(v1).getDouble(), @@ -75,8 +83,8 @@ public class NBTSerializableValue extends Value NumericValue.asNumber(params.get(2 + offset).evalValue(c)).getDouble()); inv = HopperBlockEntity.getInventoryAt(c.s.getWorld(), pos); if (inv == null) - throw new InternalExpressionException("Block at "+pos+" has no inventory"); - return new InventoryLocator(pos, pos, inv, offset+3); + throw new InternalExpressionException("Block at " + pos + " has no inventory"); + return new InventoryLocator(pos, pos, inv, offset + 3); } catch (IndexOutOfBoundsException e) { @@ -84,6 +92,37 @@ public class NBTSerializableValue extends Value } } + private static Map itemCache = new HashMap<>(); + public static ItemStackArgument parseItem(String itemString) + { + try + { + ItemStackArgument res = itemCache.get(itemString); + if (res != null) + return res; + ItemStringReader parser = (new ItemStringReader(new StringReader(itemString), false)).consume(); + res = new ItemStackArgument(parser.getItem(), parser.getTag()); + itemCache.put(itemString, res); + if (itemCache.size()>64000) + itemCache.clear(); + return res; + } + catch (CommandSyntaxException e) + { + return null; + } + } + + public static int validateSlot(int slot, Inventory inv) + { + int invSize = inv.getInvSize(); + if (slot < 0) + slot = invSize + slot; + if (slot < 0 || slot >= invSize) + throw new InternalExpressionException("Slot for inventory should be between 0 and "+(invSize-1)); + return slot; + } + private CompoundTag getTag() { if (nbtTag == null)