From 201654ff4444b1e1b0abca8afaa7444bf3164530 Mon Sep 17 00:00:00 2001 From: gnembon Date: Wed, 25 Nov 2020 17:52:17 -0400 Subject: [PATCH 1/2] adding support for equipment inventories as well as horse and pillager item inventories. --- docs/scarpet/api/Inventories.md | 13 +++ .../fakes/InventoryBearerInterface.java | 8 ++ .../mixins/HorseBaseEntity_scarpetMixin.java | 21 ++++ .../mixins/PillagerEntity_scarpetMixin.java | 21 ++++ .../carpet/script/utils/LivingInventory.java | 96 +++++++++++++++++++ .../script/value/NBTSerializableValue.java | 15 ++- src/main/resources/carpet.mixins.json | 2 + 7 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 src/main/java/carpet/fakes/InventoryBearerInterface.java create mode 100644 src/main/java/carpet/mixins/HorseBaseEntity_scarpetMixin.java create mode 100644 src/main/java/carpet/mixins/PillagerEntity_scarpetMixin.java create mode 100644 src/main/java/carpet/script/utils/LivingInventory.java diff --git a/docs/scarpet/api/Inventories.md b/docs/scarpet/api/Inventories.md index f72b130a..e2a68f18 100644 --- a/docs/scarpet/api/Inventories.md +++ b/docs/scarpet/api/Inventories.md @@ -8,6 +8,18 @@ two arguments, keyword `'enderchest'`, followed by the player entity argument, o form: `'enderchest_steve'`. If your player name starts with enderchest, it can be always accessed by passing a player entity value. If all else fails, it will try to identify first three arguments as coordinates of a block position of a block inventory. Player inventories can also be called by their name. + +You can also use `'equipment'` as a keyword preceeding an entity indicating the need of access that entity as a mob. +Equipment inventory +consists of its armour and hand items, and a few living entites can have both, their regular inventory, and their equipment inventory. For some +entities (players) their regular inventory already contains the equipment. For entity types that only have +their equipment inventory, the equipment is returned by default. + +
+inventory_size(player()) => 41  // items
+inventory_size('enderchest', player()) => 27 // equipment
+inventory_size('equipment', player()) => 6 // equipment
+
If the entity or a block doesn't have an inventory, they typically do nothing and return null. @@ -91,6 +103,7 @@ have an inventory
 inventory_size(player()) => 41
 inventory_size('enderchest', player()) => 27 // enderchest
+inventory_size('equipment', player()) => 6 // equipment
 inventory_size(x,y,z) => 27 // chest
 inventory_size(block(pos)) => 5 // hopper
 
diff --git a/src/main/java/carpet/fakes/InventoryBearerInterface.java b/src/main/java/carpet/fakes/InventoryBearerInterface.java new file mode 100644 index 00000000..aa282382 --- /dev/null +++ b/src/main/java/carpet/fakes/InventoryBearerInterface.java @@ -0,0 +1,8 @@ +package carpet.fakes; + +import net.minecraft.inventory.Inventory; + +public interface InventoryBearerInterface +{ + Inventory getCMInventory(); +} diff --git a/src/main/java/carpet/mixins/HorseBaseEntity_scarpetMixin.java b/src/main/java/carpet/mixins/HorseBaseEntity_scarpetMixin.java new file mode 100644 index 00000000..dc08d54d --- /dev/null +++ b/src/main/java/carpet/mixins/HorseBaseEntity_scarpetMixin.java @@ -0,0 +1,21 @@ +package carpet.mixins; + +import carpet.fakes.InventoryBearerInterface; +import net.minecraft.entity.passive.HorseBaseEntity; +import net.minecraft.inventory.Inventory; +import net.minecraft.inventory.SimpleInventory; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(HorseBaseEntity.class) +public class HorseBaseEntity_scarpetMixin implements InventoryBearerInterface +{ + + @Shadow protected SimpleInventory items; + + @Override + public Inventory getCMInventory() + { + return items; + } +} diff --git a/src/main/java/carpet/mixins/PillagerEntity_scarpetMixin.java b/src/main/java/carpet/mixins/PillagerEntity_scarpetMixin.java new file mode 100644 index 00000000..66db248f --- /dev/null +++ b/src/main/java/carpet/mixins/PillagerEntity_scarpetMixin.java @@ -0,0 +1,21 @@ +package carpet.mixins; + +import carpet.fakes.InventoryBearerInterface; +import net.minecraft.entity.mob.PillagerEntity; +import net.minecraft.inventory.Inventory; +import net.minecraft.inventory.SimpleInventory; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(PillagerEntity.class) +public class PillagerEntity_scarpetMixin implements InventoryBearerInterface +{ + @Shadow @Final private SimpleInventory inventory; + + @Override + public Inventory getCMInventory() + { + return inventory; + } +} diff --git a/src/main/java/carpet/script/utils/LivingInventory.java b/src/main/java/carpet/script/utils/LivingInventory.java new file mode 100644 index 00000000..34c563cd --- /dev/null +++ b/src/main/java/carpet/script/utils/LivingInventory.java @@ -0,0 +1,96 @@ +package carpet.script.utils; + +import it.unimi.dsi.fastutil.ints.Int2ObjectMap; +import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; +import net.minecraft.entity.EquipmentSlot; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.inventory.Inventory; +import net.minecraft.item.ItemStack; + +public class LivingInventory implements Inventory +{ + private static final Int2ObjectMap slotToSlot = new Int2ObjectOpenHashMap(){{ + put(0, EquipmentSlot.MAINHAND); + put(1, EquipmentSlot.FEET); + put(2, EquipmentSlot.LEGS); + put(3, EquipmentSlot.CHEST); + put(4, EquipmentSlot.HEAD); + put(5, EquipmentSlot.OFFHAND); + }}; + + LivingEntity mob; + public LivingInventory(LivingEntity mob) + { + this.mob = mob; + } + + @Override + public int size() + { + return 6; + } + + @Override + public boolean isEmpty() + { + for (EquipmentSlot slot: EquipmentSlot.values()) + if (!mob.getEquippedStack(slot).isEmpty()) return false; + return true; + } + + @Override + public ItemStack getStack(int slot) + { + EquipmentSlot slotSlot = slotToSlot.getOrDefault(slot, null); + if (slotSlot == null) return null; + return mob.getEquippedStack(slotToSlot.get(slot)); + } + + @Override + public ItemStack removeStack(int slot, int amount) + { + EquipmentSlot slotSlot = slotToSlot.getOrDefault(slot, null); + if (slotSlot == null) return null; + ItemStack stack = mob.getEquippedStack(slotSlot); + stack.decrement(amount); + return stack; + } + + @Override + public ItemStack removeStack(int slot) + { + EquipmentSlot slotSlot = slotToSlot.getOrDefault(slot, null); + if (slotSlot == null) return null; + ItemStack previous = mob.getEquippedStack(slotSlot); + mob.equipStack(slotSlot, ItemStack.EMPTY); + return previous; + } + + @Override + public void setStack(int slot, ItemStack stack) + { + EquipmentSlot slotSlot = slotToSlot.getOrDefault(slot, null); + if (slotSlot == null) return; + mob.equipStack(slotSlot, stack); + } + + @Override + public void markDirty() + { + + } + + @Override + public boolean canPlayerUse(PlayerEntity player) + { + return false; + } + + @Override + public void clear() + { + for (EquipmentSlot slot: EquipmentSlot.values()) + mob.equipStack(slot, ItemStack.EMPTY); + } +} diff --git a/src/main/java/carpet/script/value/NBTSerializableValue.java b/src/main/java/carpet/script/value/NBTSerializableValue.java index 7d41eec1..c8309bec 100644 --- a/src/main/java/carpet/script/value/NBTSerializableValue.java +++ b/src/main/java/carpet/script/value/NBTSerializableValue.java @@ -1,8 +1,10 @@ package carpet.script.value; +import carpet.fakes.InventoryBearerInterface; import carpet.script.CarpetContext; import carpet.script.LazyValue; import carpet.script.exception.InternalExpressionException; +import carpet.script.utils.LivingInventory; import com.mojang.brigadier.StringReader; import com.mojang.brigadier.exceptions.CommandSyntaxException; import net.minecraft.block.Block; @@ -15,6 +17,8 @@ import net.minecraft.command.argument.ItemStackArgument; import net.minecraft.command.argument.ItemStringReader; import net.minecraft.command.argument.NbtPathArgumentType; import net.minecraft.entity.Entity; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.mob.MobEntity; import net.minecraft.entity.passive.VillagerEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.inventory.Inventory; @@ -184,7 +188,8 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa if (e instanceof PlayerEntity) inv = ((PlayerEntity) e).inventory; else if (e instanceof Inventory) inv = (Inventory) e; else if (e instanceof VillagerEntity) inv = ((VillagerEntity) e).getInventory(); - + else if (e instanceof InventoryBearerInterface) inv = ((InventoryBearerInterface)e).getCMInventory(); + else if (e instanceof LivingEntity) inv = new LivingInventory((MobEntity) e); if (inv == null) return null; @@ -222,6 +227,14 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa if (player == null) throw new InternalExpressionException("enderchest inventory requires player argument"); return new InventoryLocator(player, player.getBlockPos(), player.getEnderChestInventory(), offset + 2, true); } + else if (strVal.equals("equipment")) + { + Value v2 = params.get(1 + offset).evalValue(c); + if (!(v2 instanceof EntityValue)) throw new InternalExpressionException("mob inventory requires a mob argument"); + Entity e = ((EntityValue) v2).getEntity(); + if (!(e instanceof LivingEntity)) throw new InternalExpressionException("mob inventory requires a mob argument"); + return new InventoryLocator(e, e.getBlockPos(), new LivingInventory((LivingEntity) e), offset + 2, false); + } else { boolean isEnder = strVal.startsWith("enderchest_"); diff --git a/src/main/resources/carpet.mixins.json b/src/main/resources/carpet.mixins.json index 4d5cfb6a..9806f587 100644 --- a/src/main/resources/carpet.mixins.json +++ b/src/main/resources/carpet.mixins.json @@ -174,6 +174,8 @@ "RecipeManager_scarpetMixin", "Ingredient_scarpetMixin", "BlockStateArgument_scarpetMixin", + "HorseBaseEntity_scarpetMixin", + "PillagerEntity_scarpetMixin", "PistonBlock_movableTEMixin", "PistonBlockEntity_movableTEMixin", From 81b14fd57fcfcce0e492727300ddd9d3ac0d0b66 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 25 Nov 2020 17:52:43 +0000 Subject: [PATCH 2/2] Merge docs for 'adding support for equipment inventories as well as horse and pillager item inventories.' --- docs/scarpet/Full.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/scarpet/Full.md b/docs/scarpet/Full.md index 01d76d85..f20de214 100644 --- a/docs/scarpet/Full.md +++ b/docs/scarpet/Full.md @@ -3591,6 +3591,18 @@ two arguments, keyword `'enderchest'`, followed by the player entity argument, o form: `'enderchest_steve'`. If your player name starts with enderchest, it can be always accessed by passing a player entity value. If all else fails, it will try to identify first three arguments as coordinates of a block position of a block inventory. Player inventories can also be called by their name. + +You can also use `'equipment'` as a keyword preceeding an entity indicating the need of access that entity as a mob. +Equipment inventory +consists of its armour and hand items, and a few living entites can have both, their regular inventory, and their equipment inventory. For some +entities (players) their regular inventory already contains the equipment. For entity types that only have +their equipment inventory, the equipment is returned by default. + +
+inventory_size(player()) => 41  // items
+inventory_size('enderchest', player()) => 27 // equipment
+inventory_size('equipment', player()) => 6 // equipment
+
If the entity or a block doesn't have an inventory, they typically do nothing and return null. @@ -3674,6 +3686,7 @@ have an inventory
 inventory_size(player()) => 41
 inventory_size('enderchest', player()) => 27 // enderchest
+inventory_size('equipment', player()) => 6 // equipment
 inventory_size(x,y,z) => 27 // chest
 inventory_size(block(pos)) => 5 // hopper