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",