Преглед на файлове

dynamic custom inventories, moved effect cooldown and mana to snuviscript, campfire, nearly completion of the coresponding tile entity, humans with skins, scale and more, snuviscript commands

Kajetan Johannes Hammerle преди 7 години
родител
ревизия
c6a5922c3d
променени са 70 файла, в които са добавени 2932 реда и са изтрити 219 реда
  1. 3 0
      src/main/java/me/km/ClientEvents.java
  2. 47 11
      src/main/java/me/km/KajetansMod.java
  3. 2 9
      src/main/java/me/km/api/Utils.java
  4. 106 0
      src/main/java/me/km/blocks/BlockCampFire.java
  5. 113 0
      src/main/java/me/km/blocks/BlockCampFireBurning.java
  6. 51 0
      src/main/java/me/km/blocks/BlockCampFireBurnt.java
  7. 13 1
      src/main/java/me/km/blocks/ModBlocks.java
  8. 295 0
      src/main/java/me/km/blocks/TileEntityCampFire.java
  9. 110 0
      src/main/java/me/km/datatools/CommandHuman.java
  10. 5 3
      src/main/java/me/km/datatools/CommandVillager.java
  11. 8 11
      src/main/java/me/km/datatools/DataToolsEvents.java
  12. 1 1
      src/main/java/me/km/effects/ActiveEffectBase.java
  13. 1 1
      src/main/java/me/km/effects/active/AreaDamage.java
  14. 1 1
      src/main/java/me/km/effects/active/BoneBreaker.java
  15. 1 1
      src/main/java/me/km/effects/active/Doomed.java
  16. 1 1
      src/main/java/me/km/effects/active/Earthquake.java
  17. 1 1
      src/main/java/me/km/effects/active/Harm.java
  18. 1 1
      src/main/java/me/km/effects/active/HeartSeeker.java
  19. 1 1
      src/main/java/me/km/effects/active/HungerPunch.java
  20. 1 1
      src/main/java/me/km/effects/active/LifeBreaker.java
  21. 1 1
      src/main/java/me/km/effects/active/LifeSteal.java
  22. 1 1
      src/main/java/me/km/effects/active/Smash.java
  23. 1 1
      src/main/java/me/km/effects/active/TeleportPlayer.java
  24. 2 3
      src/main/java/me/km/effects/passive/ArrowEffects.java
  25. 314 0
      src/main/java/me/km/entities/EntityHuman.java
  26. 116 0
      src/main/java/me/km/entities/HumanSkinLoader.java
  27. 3 0
      src/main/java/me/km/entities/ModEntities.java
  28. 43 0
      src/main/java/me/km/entities/ModelHuman.java
  29. 156 0
      src/main/java/me/km/entities/RenderHuman.java
  30. 153 0
      src/main/java/me/km/inventory/ContainerCampFire.java
  31. 39 56
      src/main/java/me/km/inventory/CustomContainer.java
  32. 164 0
      src/main/java/me/km/inventory/CustomContainerBase.java
  33. 0 36
      src/main/java/me/km/inventory/EntityInventory.java
  34. 20 0
      src/main/java/me/km/inventory/IdSlot.java
  35. 155 0
      src/main/java/me/km/inventory/InventoryBase.java
  36. 5 5
      src/main/java/me/km/inventory/InventoryUtils.java
  37. 3 5
      src/main/java/me/km/inventory/TeleportContainer.java
  38. 1 1
      src/main/java/me/km/items/ItemGun.java
  39. 0 5
      src/main/java/me/km/items/ModItems.java
  40. 8 0
      src/main/java/me/km/jobsystem/JobAPI.java
  41. 64 0
      src/main/java/me/km/networking/CampFireInventory.java
  42. 66 0
      src/main/java/me/km/networking/CampFireInventoryGui.java
  43. 74 0
      src/main/java/me/km/networking/CustomInventory.java
  44. 72 0
      src/main/java/me/km/networking/CustomInventoryGui.java
  45. 63 0
      src/main/java/me/km/networking/HumanScaleUpdate.java
  46. 63 0
      src/main/java/me/km/networking/HumanUpdate.java
  47. 33 0
      src/main/java/me/km/networking/ModPacketHandler.java
  48. 29 1
      src/main/java/me/km/permissions/PermissionManager.java
  49. 1 1
      src/main/java/me/km/permissions/Permissions.java
  50. 2 2
      src/main/java/me/km/skills/ActiveSkillContainer.java
  51. 32 0
      src/main/java/me/km/skills/LeveledSkill.java
  52. 33 1
      src/main/java/me/km/skills/Skill.java
  53. 5 9
      src/main/java/me/km/skills/SkillContainer.java
  54. 1 1
      src/main/java/me/km/skills/SkillManager.java
  55. 7 6
      src/main/java/me/km/skills/SkillMenuUtilities.java
  56. 94 5
      src/main/java/me/km/snuviscript/MinecraftFunctions.java
  57. 33 27
      src/main/java/me/km/snuviscript/ScriptEvents.java
  58. 3 5
      src/main/java/me/km/snuviscript/ScriptInventoryHolder.java
  59. 3 3
      src/main/java/me/km/snuviscript/SnuviInventory.java
  60. 5 0
      src/main/resources/assets/km/blockstates/camp_fire.json
  61. 5 0
      src/main/resources/assets/km/blockstates/camp_fire_burning.json
  62. 5 0
      src/main/resources/assets/km/blockstates/camp_fire_burnt.json
  63. 94 0
      src/main/resources/assets/km/models/block/camp_fire.json
  64. 130 0
      src/main/resources/assets/km/models/block/camp_fire_burning.json
  65. 68 0
      src/main/resources/assets/km/models/block/camp_fire_burnt.json
  66. BIN
      src/main/resources/assets/km/textures/blocks/campfire/burnt_oak_tip.png
  67. BIN
      src/main/resources/assets/km/textures/blocks/campfire/burnt_plank_oak.png
  68. BIN
      src/main/resources/assets/km/textures/blocks/campfire/planks_oak.png
  69. BIN
      src/main/resources/assets/km/textures/gui/container/campfire.png
  70. BIN
      src/main/resources/assets/km/textures/gui/container/empty_tile.png

+ 3 - 0
src/main/java/me/km/ClientEvents.java

@@ -2,6 +2,8 @@ package me.km;
 
 import java.util.List;
 import me.km.api.GlobalText;
+import me.km.entities.EntityHuman;
+import me.km.networking.ModPacketHandler;
 import me.km.networking.PlayerDisplayGui;
 import net.minecraft.item.Item;
 import net.minecraft.item.ItemArmor;
@@ -12,6 +14,7 @@ import net.minecraft.util.text.TextComponentTranslation;
 import net.minecraft.util.text.TextFormatting;
 import net.minecraftforge.client.event.ClientChatReceivedEvent;
 import net.minecraftforge.client.event.RenderGameOverlayEvent;
+import net.minecraftforge.event.entity.EntityJoinWorldEvent;
 import net.minecraftforge.event.entity.player.ItemTooltipEvent;
 import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
 import net.minecraftforge.fml.relauncher.Side;

+ 47 - 11
src/main/java/me/km/KajetansMod.java

@@ -4,6 +4,7 @@ import me.km.capabilities.CapabilitiesEvents;
 import me.km.api.Module;
 import me.km.api.SimpleConfig;
 import me.km.api.CommandOverloader;
+import me.km.api.Utils;
 import me.km.blockprotections.BlockProtectionBank;
 import me.km.capabilities.DamageUtils;
 import me.km.chatmanager.ChatManager;
@@ -11,6 +12,7 @@ import me.km.databank.DataBank;
 import me.km.dimensions.ModWorldGeneration;
 import me.km.dimensions.WorldData;
 import me.km.effects.EffectUtils;
+import me.km.entities.EntityHuman;
 import me.km.fluids.ModFluids;
 import me.km.jobsystem.JobAPI;
 import me.km.networking.ModPacketHandler;
@@ -69,7 +71,7 @@ public class KajetansMod
 
     public static final String MODID = "km";
     public static final String NAME = "Kajetans Mod";
-    public static final String VERSION = "0.0.13";
+    public static final String VERSION = "0.0.15";
 
     @Mod.Instance(MODID)
     public static KajetansMod instance;
@@ -94,26 +96,60 @@ public class KajetansMod
         
         DamageUtils.init();
         CapabilitiesEvents.init();
-        /*MinecraftForge.EVENT_BUS.register(new Object()
+        
+        net.minecraftforge.common.MinecraftForge.EVENT_BUS.register(new Object()
             {
-                @SubscribeEvent
-                public void wusi(ServerChatEvent e) 
+                public me.km.inventory.InventoryBase inv = null;
+                
+                @net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+                public void wusi(net.minecraftforge.client.event.ClientChatEvent e) 
+                {
+                    net.minecraft.client.entity.EntityPlayerSP p = net.minecraft.client.Minecraft.getMinecraft().player;
+                }
+                
+                @net.minecraftforge.fml.common.eventhandler.SubscribeEvent
+                public void wusi2(net.minecraftforge.event.ServerChatEvent e) 
                 {
-                    System.out.println(e.getMessage());
+                    net.minecraft.entity.player.EntityPlayerMP p = e.getPlayer();
+                    /*if(inv == null)
+                    {
+                        System.out.println("NEU");
+                        inv = new me.km.inventory.InventoryBase("Test Titel", "21102111", p);
+                    }
+                    new me.km.inventory.CustomContainer(inv, p).openForPlayer();*/
                     try
                     {
                         String[] parts = e.getMessage().split(" ");
-                        int action = Integer.parseInt(parts[0]);
-                        int index = Integer.parseInt(parts[1]);
-                        String text = Arrays.stream(parts, 2, parts.length).collect(Collectors.joining(" ")).replace('&', '§');
-                        System.out.println("action '" + action + "' with index '" + index + "' and text '" + text + "'");
-                        ModPacketHandler.sendStats(e.getPlayer(), action, index, text);
+                        switch(parts[0])
+                        {
+                            case "spawn": 
+                            {
+                                EntityHuman h = new EntityHuman(p.world);
+                                h.setPosition(p.posX, p.posY, p.posZ);
+                                p.world.spawnEntity(h);
+                                break;
+                            }
+                            case "name": 
+                            {
+                                Utils.getEntities(p.world, p.posX, p.posY, p.posZ, 2, EntityHuman.class).forEach(h -> h.setName(parts[1]));
+                                break;
+                            }
+                            case "slim": 
+                            {
+                                Utils.getEntities(p.world, p.posX, p.posY, p.posZ, 2, EntityHuman.class).forEach(h -> h.setSlim(!h.isSlim()));
+                                break;
+                            }
+                            default:
+                                float f = Float.parseFloat(e.getMessage());
+                                Utils.getEntities(p.world, p.posX, p.posY, p.posZ, 5, EntityHuman.class).forEach(h -> h.setScale(f));
+                        }
+                            
                     }
                     catch(Exception ex)
                     {
                     }
                 }
-            });*/
+            });
     }
     
     @Mod.EventHandler

+ 2 - 9
src/main/java/me/km/api/Utils.java

@@ -662,19 +662,12 @@ public class Utils
     
     public static EntityPlayerMP getPlayerByName(String name) throws PlayerNotFoundException
     {
+        String nameLower = name.toLowerCase();
         return KajetansMod.server.getPlayerList().getPlayers().stream()
-                    .filter(pl -> pl.getName().contains(name))
+                    .filter(pl -> pl.getName().toLowerCase().contains(nameLower))
                     .findFirst().orElseThrow(() -> new PlayerNotFoundException(name));
     }
     
-    public static EntityPlayerMP getPlayerByDisplayName(ITextComponent name) throws PlayerNotFoundException
-    {
-        String s = name.getUnformattedText();
-        return KajetansMod.server.getPlayerList().getPlayers().stream()
-                    .filter(pl -> pl.getDisplayName().getUnformattedText().equals(s))
-                    .findFirst().orElseThrow(() -> new PlayerNotFoundException(s));
-    }
-        
     // -------------------------------------------------------------------------
     // Spawn
     // -------------------------------------------------------------------------

+ 106 - 0
src/main/java/me/km/blocks/BlockCampFire.java

@@ -0,0 +1,106 @@
+package me.km.blocks;
+
+import net.minecraft.block.Block;
+import net.minecraft.block.material.Material;
+import net.minecraft.block.state.IBlockState;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.BlockRenderLayer;
+import net.minecraft.util.EnumFacing;
+import net.minecraft.util.EnumHand;
+import net.minecraft.util.math.AxisAlignedBB;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.world.IBlockAccess;
+import net.minecraft.world.World;
+import net.minecraftforge.fml.relauncher.Side;
+import net.minecraftforge.fml.relauncher.SideOnly;
+
+public class BlockCampFire extends BlockBase
+{
+    public static final AxisAlignedBB BOX = new AxisAlignedBB(0.25D, 0.0D, 0.25D, 0.75D, 0.4D, 0.75D);
+    
+    public BlockCampFire(String name, String local) 
+    {
+        super(Material.FIRE, name, local);
+        super.setLightOpacity(0);
+        super.setHardness(1.5f);
+        super.setResistance(4);
+    }
+    
+    @Override
+    public boolean onBlockActivated(World w, BlockPos pos, IBlockState state, EntityPlayer p, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ)
+    {
+        if (w.isRemote)
+        {
+            return true;
+        }
+        else
+        {
+            w.setBlockState(pos, ModBlocks.campFireBurning.getDefaultState());
+            return true;
+        }
+    }
+
+    @Override
+    public AxisAlignedBB getBoundingBox(IBlockState state, IBlockAccess source, BlockPos pos)
+    {
+        return BOX;
+    }
+
+    @Override
+    public boolean isOpaqueCube(IBlockState state)
+    {
+        return false;
+    }
+
+    @Override
+    public boolean isFullCube(IBlockState state)
+    {
+        return false;
+    }
+
+    @Override
+    public ItemStack getItem(World worldIn, BlockPos pos, IBlockState state) 
+    {
+        return new ItemStack(ModBlocks.campFire);
+    }
+    
+    @SideOnly(Side.CLIENT)
+    @Override
+    public boolean shouldSideBeRendered(IBlockState blockState, IBlockAccess blockAccess, BlockPos pos, EnumFacing side)
+    {
+        return true;
+    }
+    
+    @SideOnly(Side.CLIENT)
+    @Override
+    public BlockRenderLayer getBlockLayer()
+    {
+        return BlockRenderLayer.CUTOUT;
+    }
+    
+    @Override
+    public boolean canPlaceBlockAt(World w, BlockPos pos)
+    {
+        return canBlockStay(w, pos) ? super.canPlaceBlockAt(w, pos) : false;
+    }
+
+    public boolean canBlockStay(World w, BlockPos pos)
+    {
+        return w.getBlockState(pos.down()).isTopSolid();
+    }
+
+    @Override
+    public void neighborChanged(IBlockState state, World w, BlockPos pos, Block b, BlockPos fromPos)
+    {
+        if(!this.canBlockStay(w, pos))
+        {
+            this.dropBlockAsItem(w, pos, state, 0);
+            w.setBlockToAir(pos);
+            for(EnumFacing enumfacing : EnumFacing.values())
+            {
+                w.notifyNeighborsOfStateChange(pos.offset(enumfacing), this, false);
+            }
+        }
+    }
+}

+ 113 - 0
src/main/java/me/km/blocks/BlockCampFireBurning.java

@@ -0,0 +1,113 @@
+package me.km.blocks;
+
+import java.util.Random;
+import me.km.inventory.ContainerCampFire;
+import me.km.networking.ModPacketHandler;
+import net.minecraft.block.ITileEntityProvider;
+import net.minecraft.block.state.IBlockState;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.init.Items;
+import net.minecraft.inventory.InventoryHelper;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.stats.StatList;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.EnumFacing;
+import net.minecraft.util.EnumHand;
+import net.minecraft.util.NonNullList;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.world.IBlockAccess;
+import net.minecraft.world.World;
+
+public class BlockCampFireBurning extends BlockCampFire implements ITileEntityProvider
+{
+    public BlockCampFireBurning(String name, String local) 
+    {
+        super(name, local);
+        this.isBlockContainer = true;
+    }
+
+    @Override
+    public boolean eventReceived(IBlockState state, World w, BlockPos pos, int id, int param)
+    {
+        TileEntity tile = w.getTileEntity(pos);
+        return tile == null ? false : tile.receiveClientEvent(id, param);
+    }
+    
+    @Override
+    public boolean isBurning(IBlockAccess world, BlockPos pos) 
+    {
+        return true;
+    }
+    
+    @Override
+    public void onBlockClicked(World w, BlockPos pos, EntityPlayer p)
+    {
+        if(!w.isRemote)
+        {
+            w.setBlockState(pos, ModBlocks.campFireBurnt.getDefaultState());
+        }
+    }
+    
+    @Override
+    public Item getItemDropped(IBlockState state, Random rand, int fortune)
+    {
+        return Items.STICK;
+    }
+    
+    @Override
+    public int quantityDropped(Random r) 
+    {
+        return r.nextInt(3) + 1;
+    }
+
+    @Override
+    public void getDrops(NonNullList<ItemStack> drops, IBlockAccess w, BlockPos pos, IBlockState state, int fortune)
+    {
+        super.getDrops(drops, w, pos, state, 0);
+        Random rand = w instanceof World ? ((World) w).rand : new Random();
+        if(rand.nextBoolean())
+        {
+            drops.add(new ItemStack(Items.COAL, 1, 1));
+        }
+    }
+    
+    @Override
+    public boolean onBlockActivated(World w, BlockPos pos, IBlockState state, EntityPlayer p, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ)
+    {
+        if(w.isRemote)
+        {
+            return true;
+        }
+        else
+        {
+            TileEntity tile = w.getTileEntity(pos);
+            if(tile != null && tile instanceof TileEntityCampFire)
+            {
+                ContainerCampFire container = new ContainerCampFire(p.inventory, (TileEntityCampFire) tile);
+                container.openForPlayer((EntityPlayerMP) p);
+                p.addStat(StatList.FURNACE_INTERACTION);
+            }
+            return true;
+        }
+    }
+
+    @Override
+    public TileEntity createNewTileEntity(World w, int meta)
+    {
+        return new TileEntityCampFire();
+    }
+
+    @Override
+    public void breakBlock(World w, BlockPos pos, IBlockState state)
+    {
+        TileEntity tile = w.getTileEntity(pos);
+        if(tile instanceof TileEntityCampFire)
+        {
+            InventoryHelper.dropInventoryItems(w, pos, (TileEntityCampFire) tile);
+        }
+        System.out.println("REMOVING");
+        w.removeTileEntity(pos);
+    }
+}

+ 51 - 0
src/main/java/me/km/blocks/BlockCampFireBurnt.java

@@ -0,0 +1,51 @@
+package me.km.blocks;
+
+import java.util.Random;
+import net.minecraft.block.state.IBlockState;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.init.Items;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.EnumFacing;
+import net.minecraft.util.EnumHand;
+import net.minecraft.util.NonNullList;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.world.IBlockAccess;
+import net.minecraft.world.World;
+
+public class BlockCampFireBurnt extends BlockCampFire
+{
+    public BlockCampFireBurnt(String name, String local) 
+    {
+        super(name, local);
+    }
+    
+    @Override
+    public boolean onBlockActivated(World w, BlockPos pos, IBlockState state, EntityPlayer p, EnumHand hand, EnumFacing facing, float hitX, float hitY, float hitZ)
+    {
+        return false;
+    }
+    
+    @Override
+    public Item getItemDropped(IBlockState state, Random rand, int fortune)
+    {
+        return Items.STICK;
+    }
+    
+    @Override
+    public int quantityDropped(Random r) 
+    {
+        return r.nextInt(3) + 1;
+    }
+
+    @Override
+    public void getDrops(NonNullList<ItemStack> drops, IBlockAccess w, BlockPos pos, IBlockState state, int fortune)
+    {
+        super.getDrops(drops, w, pos, state, 0);
+        Random rand = w instanceof World ? ((World) w).rand : new Random();
+        if(rand.nextBoolean())
+        {
+            drops.add(new ItemStack(Items.COAL, 1, 1));
+        }
+    }
+}

+ 13 - 1
src/main/java/me/km/blocks/ModBlocks.java

@@ -11,6 +11,7 @@ import net.minecraft.block.material.Material;
 import net.minecraft.init.Blocks;
 import net.minecraft.item.Item;
 import net.minecraft.item.ItemBlock;
+import net.minecraftforge.fml.common.registry.GameRegistry;
 import net.minecraftforge.registries.IForgeRegistry;
 
 public class ModBlocks 
@@ -26,13 +27,17 @@ public class ModBlocks
     
     public static BlockOre silverOre;
     public static BlockBase silverBlock;
-    
+        
     // misc
     public static BlockBase guildblock;
     public static BlockBase artefact;
     public static BlockHay realHayBlock;
     public static BlockHayBed realHayBed;
     
+    public static BlockCampFire campFire;
+    public static BlockCampFireBurning campFireBurning;
+    public static BlockCampFireBurnt campFireBurnt;
+    
     // traps
     public static BlockSpikeTrap spikes;
     
@@ -63,6 +68,11 @@ public class ModBlocks
         realHayBlock = register(r, new BlockHay("real_hay_block", "realHayBlock"));
         realHayBed = register(r, new BlockHayBed("real_hay_bed", "realHayBlock"));
         
+        campFire = register(r, new BlockCampFire("camp_fire", "campFire"));
+        campFireBurning = (BlockCampFireBurning) register(r, new BlockCampFireBurning("camp_fire_burning", "campFireBurning")).setLightLevel(1);
+        campFireBurnt = register(r, new BlockCampFireBurnt("camp_fire_burnt", "campFireBurnt"));
+        GameRegistry.registerTileEntity(TileEntityCampFire.class, campFireBurning.getRegistryName().toString());
+        
         // traps
         spikes = register(r, new BlockSpikeTrap(Material.IRON, "spikes", "spikes"));
     
@@ -100,6 +110,8 @@ public class ModBlocks
         
         register(r, realHayBlock, getItemBlock(realHayBlock));
         
+        register(r, campFire, getItemBlock(campFire));
+        
         // traps
         register(r, spikes, getItemMetal(spikes));
         

+ 295 - 0
src/main/java/me/km/blocks/TileEntityCampFire.java

@@ -0,0 +1,295 @@
+package me.km.blocks;
+
+import java.util.Arrays;
+import me.km.inventory.ContainerCampFire;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.InventoryPlayer;
+import net.minecraft.init.Items;
+import net.minecraft.inventory.Container;
+import net.minecraft.inventory.ItemStackHelper;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.tileentity.TileEntityLockable;
+import net.minecraft.util.ITickable;
+import net.minecraft.util.NonNullList;
+
+public class TileEntityCampFire extends TileEntityLockable implements ITickable
+{
+    private NonNullList<ItemStack> stacks = NonNullList.<ItemStack>withSize(6, ItemStack.EMPTY);
+    
+    private int burnTime;
+    
+    /** The number of ticks that a fresh copy of the currently-burning item would keep the furnace burning for */
+    private int[] cookTime;
+    private String furnaceCustomName;
+
+    public TileEntityCampFire()
+    {
+        this.cookTime = new int[3];
+        Arrays.fill(this.cookTime, 0);
+        this.furnaceCustomName = null;
+        this.burnTime = 960;
+    }
+    
+    @Override
+    public int getSizeInventory()
+    {
+        return this.stacks.size();
+    }
+
+    @Override
+    public boolean isEmpty()
+    {
+        return !this.stacks.stream().anyMatch(stack -> !stack.isEmpty());
+    }
+    
+    @Override
+    public ItemStack getStackInSlot(int index)
+    {
+        return this.stacks.get(index);
+    }
+
+    @Override
+    public ItemStack decrStackSize(int index, int count)
+    {
+        return ItemStackHelper.getAndSplit(this.stacks, index, count);
+    }
+
+    @Override
+    public ItemStack removeStackFromSlot(int index)
+    {
+        return ItemStackHelper.getAndRemove(this.stacks, index);
+    }
+
+    @Override
+    public void setInventorySlotContents(int index, ItemStack stack)
+    {
+        ItemStack itemstack = this.stacks.get(index);
+        boolean flag = !stack.isEmpty() && stack.isItemEqual(itemstack) && ItemStack.areItemStackTagsEqual(stack, itemstack);
+        this.stacks.set(index, stack);
+        if(stack.getCount() > this.getInventoryStackLimit())
+        {
+            stack.setCount(this.getInventoryStackLimit());
+        }
+        if(index >= 0 && index <= 2 && !flag)
+        {
+            this.cookTime[index] = 0;
+            this.markDirty();
+        }
+    }
+
+    @Override
+    public String getName()
+    {
+        return this.hasCustomName() ? this.furnaceCustomName : "container.campfire";
+    }
+
+    @Override
+    public boolean hasCustomName()
+    {
+        return this.furnaceCustomName != null && !this.furnaceCustomName.isEmpty();
+    }
+
+    public void setCustomInventoryName(String name)
+    {
+        this.furnaceCustomName = name;
+    }
+
+    @Override
+    public void readFromNBT(NBTTagCompound com)
+    {
+        super.readFromNBT(com);
+        this.stacks = NonNullList.<ItemStack>withSize(this.getSizeInventory(), ItemStack.EMPTY);
+        ItemStackHelper.loadAllItems(com, this.stacks);
+        this.burnTime = com.getInteger("BurnTime");
+        this.cookTime = com.getIntArray("CookTime");
+        if(com.hasKey("CustomName", 8))
+        {
+            this.furnaceCustomName = com.getString("CustomName");
+        }
+    }
+
+    @Override
+    public NBTTagCompound writeToNBT(NBTTagCompound compound)
+    {
+        super.writeToNBT(compound);
+        compound.setInteger("BurnTime", this.burnTime);
+        compound.setIntArray("CookTime", this.cookTime);
+        ItemStackHelper.saveAllItems(compound, this.stacks);
+        if(this.hasCustomName())
+        {
+            compound.setString("CustomName", this.furnaceCustomName);
+        }
+        return compound;
+    }
+
+    @Override
+    public int getInventoryStackLimit()
+    {
+        return 64;
+    }
+
+    public boolean isBurning()
+    {
+        return this.burnTime > 0;
+    }
+
+    @Override
+    public void update()
+    {       
+        if(!this.world.isRemote)
+        {
+            boolean update = false;
+            for(int i = 0; i < 3; i++)
+            {
+                ItemStack input = stacks.get(i);
+                if(!input.isEmpty())
+                {
+                    ItemStack output = stacks.get(i + 3);
+                    if(output.getCount() >= output.getMaxStackSize())
+                    {
+                        continue;
+                    }
+                    ItemStack result = getResult(input);
+                    if(result == ItemStack.EMPTY)
+                    {
+                        continue;
+                    }
+                    boolean empty = output.isEmpty();
+                    if(empty || output.isItemEqual(result))
+                    {
+                        this.cookTime[i]++;
+                        this.burnTime--;
+                        if(this.cookTime[i] >= 200)
+                        {
+                            this.cookTime[i] = 0;
+                            input.shrink(1);
+                            if(empty)
+                            {
+                                output = result;
+                                stacks.set(i + 3, output);
+                            }
+                            else
+                            {
+                                output.grow(1);
+                            }
+                            update = true;
+                        }
+                    }
+                }
+            }
+            if(update)
+            {
+                this.markDirty();
+            }
+        }
+    }
+    
+    public ItemStack getResult(ItemStack stack)
+    {
+        Item item = stack.getItem();
+        Item result = null;
+        int dv = 0;
+        if(item == Items.PORKCHOP)
+        {
+            result = Items.COOKED_PORKCHOP;
+        }
+        else if(item == Items.BEEF)
+        {
+            result = Items.COOKED_BEEF;
+        }
+        else if(item == Items.CHICKEN)
+        {
+            result = Items.COOKED_CHICKEN;
+        }
+        else if(item == Items.FISH)
+        {
+            result = Items.COOKED_FISH;
+            dv = stack.getItemDamage();
+        }
+        else if(item == Items.POTATO)
+        {
+            result = Items.BAKED_POTATO;
+        }
+        else if(item == Items.MUTTON)
+        {
+            result = Items.COOKED_MUTTON;
+        }
+        else if(item == Items.RABBIT)
+        {
+            result = Items.COOKED_RABBIT;
+        }
+
+        if(result != null)
+        {
+            return new ItemStack(result, 1, dv);
+        }
+        return ItemStack.EMPTY;
+    }
+
+    @Override
+    public boolean isUsableByPlayer(EntityPlayer p)
+    {
+        if(this.world.getTileEntity(this.pos) != this)
+        {
+            return false;
+        }
+        else
+        {
+            return p.getDistanceSq(pos.getX() + 0.5d, pos.getY() + 0.5d, pos.getZ() + 0.5d) <= 64.0D;
+        }
+    }
+
+    @Override
+    public void openInventory(EntityPlayer player)
+    {
+    }
+
+    @Override
+    public void closeInventory(EntityPlayer player)
+    {
+    }
+
+    @Override
+    public boolean isItemValidForSlot(int index, ItemStack stack)
+    {
+        return false;
+    }
+
+    @Override
+    public String getGuiID()
+    {
+        return "km:campfire";
+    }
+
+    @Override
+    public Container createContainer(InventoryPlayer pInv, EntityPlayer p)
+    {
+        return new ContainerCampFire(pInv, this);
+    }
+
+    @Override
+    public int getField(int id)
+    {
+        return this.cookTime[id];
+    }
+
+    @Override
+    public void setField(int id, int value)
+    {
+        this.cookTime[id] = value;
+    }
+
+    @Override
+    public int getFieldCount()
+    {
+        return 3;
+    }
+
+    @Override
+    public void clear()
+    {
+        this.stacks.clear();
+    }
+}

+ 110 - 0
src/main/java/me/km/datatools/CommandHuman.java

@@ -0,0 +1,110 @@
+package me.km.datatools;
+
+import me.km.api.GlobalText;
+import me.km.api.Module;
+import me.km.api.ModuleCommand;
+import me.km.api.Utils;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import me.km.entities.EntityHuman;
+import me.km.networking.ModPacketHandler;
+import me.km.permissions.Permissions;
+import net.minecraft.command.ICommandSender;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.world.World;
+
+public class CommandHuman extends ModuleCommand
+{
+    private final ArrayList<String> first;
+    
+    public CommandHuman(Module m) 
+    {
+        super("human", m);
+        super.setDescription("Spezielle Commands für Humans");
+        super.setUsage("/human für die Hilfe");
+        super.setPermission(Permissions.HUMAN);  
+        super.addAlias("h");
+        
+        first = new ArrayList<>(Arrays.asList(new String[]
+                {"spawn", "set", "kill"})); 
+    }
+
+    @Override
+    public List<String> getTabCompletions(MinecraftServer server, ICommandSender sender, String[] args, BlockPos targetPos) 
+    {
+        List<String> names = new ArrayList<>();
+        switch (args.length) 
+        {
+            case 1:
+                first.stream().filter(e -> e.toLowerCase().startsWith(args[0].toLowerCase())).forEach(e ->
+                {
+                    names.add(e);
+                }); 
+                break;  
+        }
+        return names;  
+    }
+
+    @Override
+    public boolean execute(ICommandSender cs, String[] arg) 
+    {
+        if(!(cs instanceof EntityPlayerMP))
+        {
+            this.getModule().send(cs, GlobalText.onlyPlayer());
+            return true;
+        }
+        EntityPlayerMP p = (EntityPlayerMP) cs;
+        Module m = this.getModule();
+        if(arg.length >= 1)
+        {         
+            switch (arg[0]) 
+            {
+                case "spawn":
+                {                                            
+                    World w = p.getEntityWorld();
+                    EntityHuman h = new EntityHuman(w);
+                    h.setPosition(p.posX, p.posY, p.posZ);       
+                    w.spawnEntity(h);
+                    return true;
+                }
+                case "set":
+                {
+                    if(arg.length < 2)
+                    {
+                        break;
+                    }
+                    EntityHuman h = Utils.getTargetedEntity(p, 3, EntityHuman.class);
+                    if(h == null)
+                    {
+                        m.send(cs, "Du musst auf einen Human gerichtet sein.");
+                        return true;
+                    }       
+                    h.setName(arg[1]);
+                    m.send(cs, "Die Daten wurden geändert.");
+                    return true;
+                }
+                case "kill":
+                {
+                    EntityHuman h = Utils.getTargetedEntity(p, 3, EntityHuman.class);
+                    if(h == null)
+                    {
+                        m.send(cs, "Du musst auf einen Human gerichtet sein.");
+                        return true;
+                    }       
+                    h.setDead();
+                    m.send(cs, "Der Human wurde getötet.");
+                    return true;
+                }
+            }
+        }
+                    
+        m.send(cs, "/human ...");
+        m.sendHelpListElement(cs, "spawn", "Spawnt einen Human");
+        m.sendHelpListElement(cs, "set <name>", "Setzt den Namen");
+        m.sendHelpListElement(cs, "kill", "Tötet einen Human");
+        return true;  
+    }
+}

+ 5 - 3
src/main/java/me/km/datatools/CommandVillager.java

@@ -7,8 +7,10 @@ import me.km.api.Utils;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.List;
+import me.km.inventory.CustomContainer;
+import me.km.inventory.CustomContainerBase;
 import me.km.permissions.Permissions;
-import me.km.inventory.EntityInventory;
+import me.km.inventory.InventoryBase;
 import me.km.inventory.InventoryUtils;
 import net.minecraft.command.ICommandSender;
 import net.minecraft.entity.SharedMonsterAttributes;
@@ -83,7 +85,7 @@ public class CommandVillager extends ModuleCommand
                         m.send(cs, "Du musst auf einen Villager gerichtet sein.");
                         return true;
                     }         
-                    EntityInventory inv = new EntityInventory("ChangeTrades - " + v.getEntityId(), 54, v);
+                    InventoryBase inv = new InventoryBase("ChangeTrades - " + v.getEntityId(), 54, v);
                     int i = 0;
                     MerchantRecipeList list = v.getRecipes(null);
                     if(list != null)
@@ -104,7 +106,7 @@ public class CommandVillager extends ModuleCommand
                             }
                         }
                     }
-                    p.displayGUIChest(inv);
+                    new CustomContainer(inv, p).openForPlayer();
                     return true;
                 }
                 case "spawn":   

+ 8 - 11
src/main/java/me/km/datatools/DataToolsEvents.java

@@ -2,11 +2,11 @@ package me.km.datatools;
 
 import me.km.api.Module;
 import me.km.api.ModuleListener;
-import me.km.inventory.EntityInventory;
+import me.km.inventory.CustomContainer;
+import me.km.inventory.InventoryBase;
 import me.km.inventory.InventoryUtils;
 import me.km.utils.ReflectionUtils;
 import net.minecraft.entity.passive.EntityVillager;
-import net.minecraft.inventory.ContainerChest;
 import net.minecraft.item.ItemStack;
 import net.minecraft.village.MerchantRecipe;
 import net.minecraft.village.MerchantRecipeList;
@@ -23,18 +23,15 @@ public class DataToolsEvents extends ModuleListener
     }
     
     @SubscribeEvent
-    public void VillagerClose(PlayerContainerEvent.Close e)
+    public void onVillagerMenuClose(PlayerContainerEvent.Close e)
     {             
-        if(!(e.getContainer() instanceof ContainerChest))
+        if(!(e.getContainer() instanceof CustomContainer))
         {
             return;
         }
-        ContainerChest c = (ContainerChest) e.getContainer();
-        if(!(c.getLowerChestInventory() instanceof EntityInventory))
-        {
-            return;
-        }
-        EntityInventory inv = (EntityInventory) c.getLowerChestInventory();
+        CustomContainer c = (CustomContainer) e.getContainer();
+        InventoryBase inv = c.getInventoryBase();
+        
         if(inv.getName().startsWith("ChangeInventory"))
         {
             InventoryUtils.setInventory(inv);
@@ -42,7 +39,7 @@ public class DataToolsEvents extends ModuleListener
         }
         else if(inv.getName().startsWith("ChangeTrades"))
         {
-            EntityVillager v = (EntityVillager) inv.getEntity();
+            EntityVillager v = (EntityVillager) inv.getOwner();
             MerchantRecipeList list = v.getRecipes(null);
             if(list == null)
             {

+ 1 - 1
src/main/java/me/km/effects/ActiveEffectBase.java

@@ -25,7 +25,7 @@ public abstract class ActiveEffectBase
         // Effect-Event-Start
         String name = this.getClass().getSimpleName();
         PlayerUsesEffectEvent e = new PlayerUsesEffectEvent(p, power, mana, cooldown, cause, name);
-        KajetansMod.scripts.getEvent(ScriptEvents.class).useEffectEvent(e);
+        KajetansMod.scripts.getEvent(ScriptEvents.class).onEffectUse(e);
         if(e.isCanceled())
         {
             return false;

+ 1 - 1
src/main/java/me/km/effects/active/AreaDamage.java

@@ -36,7 +36,7 @@ public class AreaDamage extends ActiveEffectBase
             EffectUtils.spawnParticleCircle(w, p, EnumParticleTypes.FLAME, 3, 12);
             EffectUtils.spawnParticleCircle(w, p, EnumParticleTypes.FLAME, 2, 12);
             EffectUtils.spawnParticleCircle(w, p, EnumParticleTypes.FLAME, 1, 12);
-            DamageSource ds = DamageSource.causeMobDamage(p);
+            DamageSource ds = DamageSource.causePlayerDamage(p);
             EffectUtils.getEntsOfNotGuild(p, 5).stream().forEach((m) -> 
             {
                 m.attackEntityFrom(ds, 2);   

+ 1 - 1
src/main/java/me/km/effects/active/BoneBreaker.java

@@ -18,7 +18,7 @@ public class BoneBreaker extends ActiveEffectBase
         {
             return false;
         }
-        DamageSource ds = DamageSource.causeMobDamage(p);
+        DamageSource ds = DamageSource.causePlayerDamage(p);
         ds.setDamageIsAbsolute();
         ds.setDamageBypassesArmor();
         liv.attackEntityFrom(ds, liv.getMaxHealth() / 7f);

+ 1 - 1
src/main/java/me/km/effects/active/Doomed.java

@@ -22,7 +22,7 @@ public class Doomed extends ActiveEffectBase
         KajetansMod.playerbank.getData(p2).addBadTimedData("doomed", p, duration, "Todgeweiht", 52);
         KajetansMod.scheduler.scheduleTask(() -> 
         {
-            p2.attackEntityFrom(DamageSource.causeMobDamage(p), power);
+            p2.attackEntityFrom(DamageSource.causePlayerDamage(p), power);
         }, duration);
         return true;
     }

+ 1 - 1
src/main/java/me/km/effects/active/Earthquake.java

@@ -20,7 +20,7 @@ public class Earthquake extends ActiveEffectBase
         EffectUtils.spawnParticleCircle(w, p, EnumParticleTypes.BLOCK_CRACK, 3, 12, data);
         EffectUtils.spawnParticleCircle(w, p, EnumParticleTypes.BLOCK_CRACK, 2, 12, data);
         EffectUtils.spawnParticleCircle(w, p, EnumParticleTypes.BLOCK_CRACK, 1, 12, data);
-        DamageSource ds = DamageSource.causeMobDamage(p);
+        DamageSource ds = DamageSource.causePlayerDamage(p);
         EffectUtils.getEntsOfNotGuild(p, 5).stream().forEach((m) -> 
         {
             m.attackEntityFrom(ds, power * 4);                           

+ 1 - 1
src/main/java/me/km/effects/active/Harm.java

@@ -10,7 +10,7 @@ public class Harm extends ActiveEffectBase
     @Override
     protected boolean executeEffect(EntityPlayerMP p, int power) 
     {
-        DamageSource ds = DamageSource.causeMobDamage(p);
+        DamageSource ds = DamageSource.causePlayerDamage(p);
         EffectUtils.getEntsOfNotGuild(p, 5).forEach(ent -> ent.attackEntityFrom(ds, power));
         return true;
     }

+ 1 - 1
src/main/java/me/km/effects/active/HeartSeeker.java

@@ -21,7 +21,7 @@ public class HeartSeeker extends ActiveEffectBase
         {
             damage *= 2;
         }
-        liv.attackEntityFrom(DamageSource.causeMobDamage(p), damage);
+        liv.attackEntityFrom(DamageSource.causePlayerDamage(p), damage);
         return true;
     }
 }

+ 1 - 1
src/main/java/me/km/effects/active/HungerPunch.java

@@ -18,7 +18,7 @@ public class HungerPunch extends ActiveEffectBase
         {
             return false;
         }
-        DamageSource ds = DamageSource.causeMobDamage(p);
+        DamageSource ds = DamageSource.causePlayerDamage(p);
         ds.setDamageIsAbsolute();
         ds.setDamageBypassesArmor();
         liv.attackEntityFrom(ds, p.getMaxHealth() / 7f);

+ 1 - 1
src/main/java/me/km/effects/active/LifeBreaker.java

@@ -20,7 +20,7 @@ public class LifeBreaker extends ActiveEffectBase
         }
         Utils.teleportEntity(p, liv);
         
-        DamageSource ds = DamageSource.causeMobDamage(p);
+        DamageSource ds = DamageSource.causePlayerDamage(p);
         ds.setDamageIsAbsolute();
         ds.setDamageBypassesArmor();
         liv.attackEntityFrom(ds, p.getMaxHealth() / 5f);

+ 1 - 1
src/main/java/me/km/effects/active/LifeSteal.java

@@ -17,7 +17,7 @@ public class LifeSteal extends ActiveEffectBase
         {
             return false;
         }
-        DamageSource ds = DamageSource.causeMobDamage(p);
+        DamageSource ds = DamageSource.causePlayerDamage(p);
         float real = Utils.getRealDamage(liv, ds, power);
         liv.attackEntityFrom(ds, power);
         p.heal(real);

+ 1 - 1
src/main/java/me/km/effects/active/Smash.java

@@ -16,7 +16,7 @@ public class Smash extends ActiveEffectBase
         {
             return false;
         }
-        DamageSource ds = DamageSource.causeMobDamage(p);
+        DamageSource ds = DamageSource.causePlayerDamage(p);
         ds.setDamageBypassesArmor();
         ds.setDamageIsAbsolute();
         liv.attackEntityFrom(ds, power);

+ 1 - 1
src/main/java/me/km/effects/active/TeleportPlayer.java

@@ -19,7 +19,7 @@ public class TeleportPlayer extends ActiveEffectBase
             return false;
         } 
         TeleportContainer inv = new TeleportContainer(p, players);
-        inv.openForPlayer(p);
+        inv.openForPlayer();
         return true;
     }
 }

+ 2 - 3
src/main/java/me/km/effects/passive/ArrowEffects.java

@@ -21,7 +21,6 @@ import net.minecraft.init.MobEffects;
 import net.minecraft.util.DamageSource;
 import net.minecraft.util.EnumParticleTypes;
 import net.minecraft.util.math.BlockPos;
-import net.minecraft.util.math.Vec3d;
 import net.minecraft.world.WorldServer;
 import net.minecraftforge.event.entity.ThrowableImpactEvent;
 import net.minecraftforge.event.entity.living.LivingHurtEvent;
@@ -97,7 +96,7 @@ public class ArrowEffects extends ModuleListener
             int damage = fromList * 3;
             EffectUtils.spawnParticle(w, EnumParticleTypes.EXPLOSION_LARGE, arrow, 1);
             EffectUtils.spawnParticleCircle(w, arrow, EnumParticleTypes.EXPLOSION_LARGE, 3, 9);
-            DamageSource ds = DamageSource.causeMobDamage(p);
+            DamageSource ds = DamageSource.causePlayerDamage(p);
             EffectUtils.getEntsOfNotGuild(p, w, arrow.posX, arrow.posY, arrow.posZ, 4).forEach(e -> 
             {
                 e.attackEntityFrom(ds, damage);
@@ -156,7 +155,7 @@ public class ArrowEffects extends ModuleListener
         {
             return;
         }
-        DamageSource ds = DamageSource.causeMobDamage(p);
+        DamageSource ds = DamageSource.causePlayerDamage(p);
         EffectUtils.spawnParticle(p.getServerWorld(), EnumParticleTypes.EXPLOSION_LARGE, pro, 1);
         EffectUtils.getEntsOfNotGuild(p, p.getServerWorld(), pro.posX, pro.posY, pro.posZ, 3).forEach(ent -> 
         {

+ 314 - 0
src/main/java/me/km/entities/EntityHuman.java

@@ -0,0 +1,314 @@
+package me.km.entities;
+
+import io.netty.buffer.ByteBuf;
+import java.nio.charset.StandardCharsets;
+import me.km.KajetansMod;
+import me.km.networking.ModPacketHandler;
+import me.km.snuviscript.ScriptEvents;
+import net.minecraft.entity.EntityAgeable;
+import net.minecraft.client.renderer.entity.RenderZombie;
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.EntityCreature;
+import net.minecraft.entity.EntityLiving;
+import net.minecraft.entity.SharedMonsterAttributes;
+import net.minecraft.entity.ai.EntityAISwimming;
+import net.minecraft.entity.ai.EntityAIWatchClosest;
+import net.minecraft.entity.ai.EntityAIWatchClosest2;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.init.SoundEvents;
+import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.util.DamageSource;
+import net.minecraft.util.ResourceLocation;
+import net.minecraft.util.SoundCategory;
+import net.minecraft.util.SoundEvent;
+import net.minecraft.util.math.AxisAlignedBB;
+import net.minecraft.util.text.ITextComponent;
+import net.minecraft.util.text.TextComponentString;
+import net.minecraft.world.World;
+import net.minecraftforge.fml.common.registry.IEntityAdditionalSpawnData;
+import net.minecraftforge.fml.relauncher.Side;
+import net.minecraftforge.fml.relauncher.SideOnly;
+
+public class EntityHuman extends EntityCreature implements IEntityAdditionalSpawnData
+{
+    private final static String STEVE = "Steve";
+    
+    @SideOnly(Side.CLIENT)
+    private ResourceLocation texture;
+    
+    private String name;
+    private float scale;
+    private byte slim;
+    private float originalWidth;
+    private float originalHeight;
+    
+    public EntityHuman(World w)
+    {
+        super(w);
+        this.name = STEVE;
+        this.scale = 1;
+        this.originalWidth = width;
+        this.originalHeight = height;
+        this.slim = 0;
+    }
+
+    // -------------------------------------------------------------------------
+    // size modifier
+    // -------------------------------------------------------------------------
+
+    public byte getSlim()
+    {
+        return slim;
+    }
+    
+    public void setSlim(byte slim)
+    {
+        this.slim = slim;
+    }
+    
+    public void setSlim(boolean b)
+    {
+        this.slim = (byte) (b ? 1 : 0);
+        if(!this.world.isRemote)
+        {
+            ModPacketHandler.sendHumanScaleUpdate(this);
+        }
+    }
+    
+    public boolean isSlim()
+    {
+        return this.slim == 1;
+    }
+    
+    public final float getScale()
+    {
+        return scale;
+    }
+    
+    public final void setScale(float scale)
+    {
+        this.scale = scale;
+        this.setSize(originalWidth * scale, originalHeight * scale);
+        if(!this.world.isRemote)
+        {
+            ModPacketHandler.sendHumanScaleUpdate(this);
+        }
+    }
+    
+    // -------------------------------------------------------------------------
+    // texture stuff
+    // -------------------------------------------------------------------------
+    
+    @SideOnly(Side.CLIENT)
+    public ResourceLocation getTexture()
+    {
+        return texture;
+    }
+    
+    @SideOnly(Side.CLIENT)
+    public void setTexture(ResourceLocation texture)
+    {
+        this.texture = texture;
+    }
+    
+    @Override
+    public void readEntityFromNBT(NBTTagCompound com)
+    {
+        super.readEntityFromNBT(com);
+        if(com.hasKey("HumanName"))
+        {
+            this.name = com.getString("HumanName");
+        }
+        if(com.hasKey("Scale"))
+        {
+            this.scale = com.getFloat("Scale");
+            this.setSize(originalWidth * scale, originalHeight * scale);
+        }
+        if(com.hasKey("Slim"))
+        {
+            this.slim = com.getByte("Slim");
+        }
+    }
+
+    @Override
+    public void writeEntityToNBT(NBTTagCompound com)
+    {
+        super.writeEntityToNBT(com);
+        com.setString("HumanName", name);
+        com.setFloat("Scale", scale);
+        com.setByte("Slim", slim);
+    }
+    
+    @Override
+    public void writeSpawnData(ByteBuf buf) 
+    {
+        byte[] b = name.getBytes(StandardCharsets.UTF_8);
+        buf.writeInt(b.length);
+        buf.writeBytes(b);
+        buf.writeFloat(scale);
+        buf.writeByte(slim);
+    }
+
+    @Override
+    public void readSpawnData(ByteBuf buf) 
+    {
+        int length = buf.readInt();
+        name = buf.readBytes(length).toString(StandardCharsets.UTF_8);
+        setTexture(HumanSkinLoader.INSTANCE.getTexture(name, loc -> setTexture(loc)));
+        scale = buf.readFloat();
+        this.setSize(originalWidth * scale, originalHeight * scale);
+        slim = buf.readByte();
+    }
+    
+    // -------------------------------------------------------------------------
+
+    @Override
+    public void addVelocity(double x, double y, double z)
+    {
+        
+    }
+    
+    @Override
+    public void applyEntityCollision(Entity entityIn) 
+    {
+    }
+
+    @Override
+    public boolean attackEntityFrom(DamageSource source, float amount) 
+    {
+        if(source == DamageSource.OUT_OF_WORLD)
+        {
+            this.setDead();
+            return false;
+        }
+        if(!KajetansMod.singlePlayer)
+        {
+            Entity ent = source.getTrueSource();
+            if(ent instanceof EntityPlayer)
+            {
+                KajetansMod.scripts.getEvent(ScriptEvents.class).onHumanHurt((EntityPlayer) ent, this);
+            }
+        }
+        else
+        {
+            this.setDead();
+        }
+        return false;
+    }
+
+    @Override
+    protected void initEntityAI()
+    {
+        this.tasks.addTask(0, new EntityAISwimming(this));
+        this.tasks.addTask(1, new EntityAIWatchClosest2(this, EntityPlayer.class, 3.0F, 1.0F));
+        this.tasks.addTask(2, new EntityAIWatchClosest(this, EntityLiving.class, 8.0F));
+    }
+    
+    public void setName(String name)
+    {
+        this.name = name;
+        if(!this.world.isRemote)
+        {
+            ModPacketHandler.sendHumanUpdate(this);
+        }
+    }   
+
+    @Override
+    protected void applyEntityAttributes()
+    {
+        super.applyEntityAttributes();
+        this.getAttributeMap().registerAttribute(SharedMonsterAttributes.ATTACK_DAMAGE).setBaseValue(1);
+        this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).setBaseValue(0.1d);
+        this.getAttributeMap().registerAttribute(SharedMonsterAttributes.ATTACK_SPEED);
+    }
+
+    @Override
+    protected SoundEvent getSwimSound()
+    {
+        return SoundEvents.ENTITY_PLAYER_SWIM;
+    }
+
+    @Override
+    protected SoundEvent getSplashSound()
+    {
+        return SoundEvents.ENTITY_PLAYER_SPLASH;
+    }
+
+    @Override
+    public SoundCategory getSoundCategory()
+    {
+        return SoundCategory.NEUTRAL;
+    }
+
+    @Override
+    protected SoundEvent getHurtSound(DamageSource ds)
+    {
+        if (ds == DamageSource.ON_FIRE)
+        {
+            return SoundEvents.ENTITY_PLAYER_HURT_ON_FIRE;
+        }
+        else
+        {
+            return ds == DamageSource.DROWN ? SoundEvents.ENTITY_PLAYER_HURT_DROWN : SoundEvents.ENTITY_PLAYER_HURT;
+        }
+    }
+
+    @Override
+    protected SoundEvent getDeathSound()
+    {
+        return SoundEvents.ENTITY_PLAYER_DEATH;
+    }
+
+    @Override
+    public double getYOffset()
+    {
+        return -0.35D;
+    }
+
+    @Override
+    public float getAIMoveSpeed()
+    {
+        return (float) this.getEntityAttribute(SharedMonsterAttributes.MOVEMENT_SPEED).getAttributeValue();
+    }
+
+    @Override
+    protected SoundEvent getFallSound(int heightIn)
+    {
+        return heightIn > 4 ? SoundEvents.ENTITY_PLAYER_BIG_FALL : SoundEvents.ENTITY_PLAYER_SMALL_FALL;
+    }
+
+    @SideOnly(Side.CLIENT)
+    @Override
+    public boolean getAlwaysRenderNameTagForRender()
+    {
+        return true;
+    }
+    
+    @Override
+    public String getName()
+    {
+        return name;
+    }
+
+    @Override
+    public ITextComponent getDisplayName()
+    {
+        return new TextComponentString(getName());
+    }
+    
+    @Override
+    public float getEyeHeight()
+    {
+        float f = getDefaultEyeHeight();
+        if(this.height == 1.65F)
+        {
+            f -= 0.08F;
+        }
+        return f;
+    }
+
+    public float getDefaultEyeHeight()
+    {
+        return 1.62F;
+    }
+}

+ 116 - 0
src/main/java/me/km/entities/HumanSkinLoader.java

@@ -0,0 +1,116 @@
+package me.km.entities;
+
+import java.awt.image.BufferedImage;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.function.Consumer;
+import javax.imageio.ImageIO;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.renderer.ImageBufferDownload;
+import net.minecraft.client.renderer.texture.DynamicTexture;
+import net.minecraft.client.renderer.texture.TextureManager;
+import net.minecraft.util.ResourceLocation;
+import net.minecraftforge.fml.relauncher.Side;
+import net.minecraftforge.fml.relauncher.SideOnly;
+
+@SideOnly(Side.CLIENT)
+public class HumanSkinLoader
+{
+    public final static HumanSkinLoader INSTANCE = new HumanSkinLoader();
+    
+    private static final ResourceLocation TEXTURE_STEVE = new ResourceLocation("textures/entity/steve.png");
+    
+    private final HashMap<String, ResourceLocation> skins;
+    private final HashSet<String> loading;
+    
+    private final HashMap<String, ArrayList<Consumer<ResourceLocation>>> queue;
+    
+    private TextureManager manager;
+    private ImageBufferDownload converter;
+    
+    public HumanSkinLoader()
+    {
+        this.skins = new HashMap<>();
+        this.loading = new HashSet<>();
+        this.queue = new HashMap<>();
+        this.manager = Minecraft.getMinecraft().getRenderManager().renderEngine;
+        this.converter = new ImageBufferDownload();
+    }
+    
+    public ResourceLocation getTexture(String name, Consumer<ResourceLocation> delayed)
+    {
+        ResourceLocation loc = skins.get(name);
+        if(loc == null)
+        {
+            if(loading.add(name))
+            {
+                downloadSkin(name, image -> 
+                {
+                    ResourceLocation rloc = manager.getDynamicTextureLocation("skin_" + name, new DynamicTexture(image));
+                    skins.put(name, rloc);
+                    loading.remove(name);
+                    delayed.accept(rloc);
+                    
+                     ArrayList<Consumer<ResourceLocation>> list = queue.get(name);
+                     if(list != null)
+                     {
+                         list.forEach(c -> c.accept(rloc));
+                         list.clear();
+                         queue.remove(name);
+                     }
+                });
+            }
+            else
+            {
+                ArrayList<Consumer<ResourceLocation>> list = queue.get(name);
+                if(list == null)
+                {
+                    list = new ArrayList<>();
+                    queue.put(name, list);
+                }
+                list.add(delayed);
+            }
+            return TEXTURE_STEVE;
+        }
+        return loc;
+    }
+    
+    private void downloadSkin(String name, Consumer<BufferedImage> delayed)
+    {
+        new Thread(() -> 
+        {
+            HttpURLConnection httpurlconnection = null;
+            try
+            {
+                httpurlconnection = (HttpURLConnection)(new URL("http://skins.minecraft.net/MinecraftSkins/" + name + ".png"))
+                        .openConnection(Minecraft.getMinecraft().getProxy());
+                httpurlconnection.setDoInput(true);
+                httpurlconnection.setDoOutput(false);
+                httpurlconnection.connect();
+
+                if(httpurlconnection.getResponseCode() != 200)
+                {
+                    System.out.println("Server response code did not return 200, skin servers might be down.");
+                    return;
+                }
+                
+                BufferedImage image = converter.parseUserSkin(ImageIO.read(httpurlconnection.getInputStream()));
+                Minecraft.getMinecraft().addScheduledTask(() -> delayed.accept(image));
+            }
+            catch (Exception ex)
+            {
+                System.out.println("Error occurred when downloading skin, however, skin servers seem to be up.");
+            }
+            finally
+            {
+                if (httpurlconnection != null)
+                {
+                    httpurlconnection.disconnect();
+                }
+            }
+        }).start();
+    }
+}

+ 3 - 0
src/main/java/me/km/entities/ModEntities.java

@@ -1,6 +1,7 @@
 package me.km.entities;
 
 import me.km.KajetansMod;
+import net.minecraft.client.Minecraft;
 import net.minecraft.client.renderer.entity.Render;
 import net.minecraft.entity.Entity;
 import net.minecraft.util.ResourceLocation;
@@ -14,6 +15,7 @@ public class ModEntities
     {
         register(1, EntityBrownBear.class, "BrownBear", "bear/brownbear");
         register(2, EntityBlackBear.class, "BlackBear", "bear/blackbear");
+        register(3, EntityHuman.class, "Human", "human");
     }
     
     @SideOnly(Side.CLIENT)
@@ -21,6 +23,7 @@ public class ModEntities
     {
         register(1, EntityBrownBear.class, "BrownBear", "bear/brownbear", new RenderBrownBear());
         register(2, EntityBlackBear.class, "BlackBear", "bear/blackbear", new RenderBlackBear());
+        register(3, EntityHuman.class, "Human", "human", new RenderHuman());
     }
 
     @SideOnly(Side.CLIENT)

+ 43 - 0
src/main/java/me/km/entities/ModelHuman.java

@@ -0,0 +1,43 @@
+package me.km.entities;
+
+import net.minecraft.client.model.ModelPlayer;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.entity.Entity;
+
+public class ModelHuman extends ModelPlayer
+{
+    public ModelHuman(float modelSize, boolean smallArmsIn) 
+    {
+        super(modelSize, smallArmsIn);
+    }
+    
+    @Override
+    public void render(Entity ent, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch, float scale)
+    {
+        this.setRotationAngles(limbSwing, limbSwingAmount, ageInTicks, netHeadYaw, headPitch, scale, ent);
+        GlStateManager.pushMatrix();
+        
+        if (ent.isSneaking())
+        {
+            GlStateManager.translate(0.0F, 0.2F, 0.0F);
+        }
+
+        float factor = ((EntityHuman) ent).getScale();
+        GlStateManager.translate(0.0F, -1.5f * factor + 1.5f, 0.0F);
+        GlStateManager.scale(factor, factor, factor);
+        this.bipedHead.render(scale);
+        this.bipedBody.render(scale);
+        this.bipedRightArm.render(scale);
+        this.bipedLeftArm.render(scale);
+        this.bipedRightLeg.render(scale);
+        this.bipedLeftLeg.render(scale);
+        this.bipedHeadwear.render(scale);
+        this.bipedLeftLegwear.render(scale);
+        this.bipedRightLegwear.render(scale);
+        this.bipedLeftArmwear.render(scale);
+        this.bipedRightArmwear.render(scale);
+        this.bipedBodyWear.render(scale);
+
+        GlStateManager.popMatrix();
+    }
+}

+ 156 - 0
src/main/java/me/km/entities/RenderHuman.java

@@ -0,0 +1,156 @@
+package me.km.entities;
+
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.model.ModelBiped;
+import net.minecraft.client.model.ModelPlayer;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.client.renderer.entity.RenderLivingBase;
+import net.minecraft.client.renderer.entity.layers.LayerArrow;
+import net.minecraft.client.renderer.entity.layers.LayerBipedArmor;
+import net.minecraft.client.renderer.entity.layers.LayerCustomHead;
+import net.minecraft.client.renderer.entity.layers.LayerElytra;
+import net.minecraft.client.renderer.entity.layers.LayerHeldItem;
+import net.minecraft.item.EnumAction;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.EnumHandSide;
+import net.minecraft.util.ResourceLocation;
+import net.minecraftforge.fml.relauncher.Side;
+import net.minecraftforge.fml.relauncher.SideOnly;
+
+@SideOnly(Side.CLIENT)
+public class RenderHuman extends RenderLivingBase<EntityHuman>
+{ 
+    /** this field is used to indicate the 3-pixel wide arms */
+    private final ModelHuman steve;
+    private final ModelHuman alex;
+
+    public RenderHuman()
+    {
+        super(Minecraft.getMinecraft().getRenderManager(), new ModelHuman(0, false), 0.5f);
+        this.addLayer(new LayerBipedArmor(this));
+        this.addLayer(new LayerHeldItem(this));
+        this.addLayer(new LayerArrow(this));
+        this.addLayer(new LayerCustomHead(this.getMainModel().bipedHead));
+        this.addLayer(new LayerElytra(this));
+        
+        this.steve = this.getMainModel();
+        this.alex = new ModelHuman(0, true);
+    }
+
+    @Override
+    public final ModelHuman getMainModel()
+    {
+        return (ModelHuman) super.getMainModel();
+    }
+
+    @Override
+    public void doRender(EntityHuman h, double x, double y, double z, float entityYaw, float partialTicks)
+    {
+        double d0 = y;
+        if (h.isSneaking())
+        {
+            d0 = y - 0.125D;
+        }
+        this.setModelVisibilities(h);
+        GlStateManager.enableBlendProfile(GlStateManager.Profile.PLAYER_SKIN);
+        if(h.isSlim())
+        {
+            this.mainModel = alex;
+            super.doRender(h, x, d0, z, entityYaw, partialTicks);
+            this.mainModel = steve;
+        }
+        else
+        {
+            super.doRender(h, x, d0, z, entityYaw, partialTicks);
+        }
+        GlStateManager.disableBlendProfile(GlStateManager.Profile.PLAYER_SKIN);
+    }
+
+    private void setModelVisibilities(EntityHuman h)
+    {
+        ModelPlayer modelplayer = this.getMainModel();
+        
+        modelplayer.setVisible(true);
+        modelplayer.bipedHeadwear.showModel = true;
+        modelplayer.bipedBodyWear.showModel = true;
+        modelplayer.bipedLeftLegwear.showModel = true;
+        modelplayer.bipedRightLegwear.showModel = true;
+        modelplayer.bipedLeftArmwear.showModel = true;
+        modelplayer.bipedRightArmwear.showModel = true;
+        modelplayer.isSneak = false;
+        ModelBiped.ArmPose modelbiped$armpose = ModelBiped.ArmPose.EMPTY;
+        ModelBiped.ArmPose modelbiped$armpose1 = ModelBiped.ArmPose.EMPTY;
+
+        ItemStack mainHand = h.getHeldItemMainhand();
+        
+        if (!mainHand.isEmpty())
+        {
+            modelbiped$armpose = ModelBiped.ArmPose.ITEM;
+
+            if (h.getItemInUseCount() > 0)
+            {
+                EnumAction enumaction = mainHand.getItemUseAction();
+
+                if (enumaction == EnumAction.BLOCK)
+                {
+                    modelbiped$armpose = ModelBiped.ArmPose.BLOCK;
+                }
+                else if (enumaction == EnumAction.BOW)
+                {
+                    modelbiped$armpose = ModelBiped.ArmPose.BOW_AND_ARROW;
+                }
+            }
+        }
+        
+        ItemStack offHand = h.getHeldItemMainhand();
+
+        if (!offHand.isEmpty())
+        {
+            modelbiped$armpose1 = ModelBiped.ArmPose.ITEM;
+
+            if (h.getItemInUseCount() > 0)
+            {
+                EnumAction enumaction1 = offHand.getItemUseAction();
+
+                if (enumaction1 == EnumAction.BLOCK)
+                {
+                    modelbiped$armpose1 = ModelBiped.ArmPose.BLOCK;
+                }
+                // FORGE: fix MC-88356 allow offhand to use bow and arrow animation
+                else if (enumaction1 == EnumAction.BOW)
+                {
+                    modelbiped$armpose1 = ModelBiped.ArmPose.BOW_AND_ARROW;
+                }
+            }
+        }
+
+        if (h.getPrimaryHand() == EnumHandSide.RIGHT)
+        {
+            modelplayer.rightArmPose = modelbiped$armpose;
+            modelplayer.leftArmPose = modelbiped$armpose1;
+        }
+        else
+        {
+            modelplayer.rightArmPose = modelbiped$armpose1;
+            modelplayer.leftArmPose = modelbiped$armpose;
+        }
+    }
+    
+    @Override
+    public ResourceLocation getEntityTexture(EntityHuman h)
+    {
+        return h.getTexture();
+    }
+
+    @Override
+    public void transformHeldFull3DItemLayer()
+    {
+        GlStateManager.translate(0.0F, 0.1875F, 0.0F);
+    }
+
+    @Override
+    protected void preRenderCallback(EntityHuman entitylivingbaseIn, float partialTickTime)
+    {
+        GlStateManager.scale(0.9375F, 0.9375F, 0.9375F);
+    }
+}

+ 153 - 0
src/main/java/me/km/inventory/ContainerCampFire.java

@@ -0,0 +1,153 @@
+package me.km.inventory;
+
+import me.km.networking.ModPacketHandler;
+import net.minecraft.advancements.CriteriaTriggers;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.entity.player.InventoryPlayer;
+import net.minecraft.inventory.Container;
+import net.minecraft.inventory.IContainerListener;
+import net.minecraft.inventory.IInventory;
+import net.minecraft.inventory.Slot;
+import net.minecraft.inventory.SlotCrafting;
+import net.minecraft.item.ItemStack;
+import net.minecraft.network.play.server.SPacketSetSlot;
+import net.minecraft.network.play.server.SPacketWindowItems;
+import net.minecraft.network.play.server.SPacketWindowProperty;
+import net.minecraft.util.NonNullList;
+import net.minecraftforge.fml.relauncher.Side;
+import net.minecraftforge.fml.relauncher.SideOnly;
+
+public class ContainerCampFire extends Container
+{
+    private final IInventory fireInv;
+    private int[] cookTime;
+
+    public ContainerCampFire(InventoryPlayer pInv, IInventory fireInv)
+    {
+        this.fireInv = fireInv;
+        this.cookTime = new int[3];
+        super.addSlotToContainer(new Slot(fireInv, 0, 52, 17));
+        super.addSlotToContainer(new Slot(fireInv, 1, 52, 35));
+        super.addSlotToContainer(new Slot(fireInv, 2, 52, 53));
+        super.addSlotToContainer(new Slot(fireInv, 3, 108, 17));
+        super.addSlotToContainer(new Slot(fireInv, 4, 108, 35));
+        super.addSlotToContainer(new Slot(fireInv, 5, 108, 53));
+
+        for(int i = 0; i < 3; i++)
+        {
+            for(int j = 0; j < 9; j++)
+            {
+                super.addSlotToContainer(new Slot(pInv, j + i * 9 + 9, 8 + j * 18, 84 + i * 18));
+            }
+        }
+
+        for(int k = 0; k < 9; k++)
+        {
+            super.addSlotToContainer(new Slot(pInv, k, 8 + k * 18, 142));
+        }
+    }
+
+    @Override
+    public void addListener(IContainerListener listener)
+    {
+        super.addListener(listener);
+        listener.sendAllWindowProperties(this, this.fireInv);
+    }
+
+    @Override
+    public void detectAndSendChanges()
+    {
+        super.detectAndSendChanges();
+        for(int i = 0; i < this.listeners.size(); ++i)
+        {
+            IContainerListener icontainerlistener = this.listeners.get(i);
+            if(this.cookTime[0] != this.fireInv.getField(0))
+            {
+                icontainerlistener.sendWindowProperty(this, 0, this.fireInv.getField(0));
+            }
+            if(this.cookTime[1] != this.fireInv.getField(1))
+            {
+                icontainerlistener.sendWindowProperty(this, 1, this.fireInv.getField(1));
+            }
+            if(this.cookTime[2] != this.fireInv.getField(2))
+            {
+                icontainerlistener.sendWindowProperty(this, 2, this.fireInv.getField(2));
+            }
+        }
+        this.cookTime[0] = this.fireInv.getField(0);
+        this.cookTime[1] = this.fireInv.getField(1);
+        this.cookTime[2] = this.fireInv.getField(2);
+    }
+
+    @SideOnly(Side.CLIENT)
+    @Override
+    public void updateProgressBar(int id, int data)
+    {
+        this.fireInv.setField(id, data);
+    }
+
+    @Override
+    public boolean canInteractWith(EntityPlayer p)
+    {
+        return this.fireInv.isUsableByPlayer(p);
+    }
+
+    @Override
+    public ItemStack transferStackInSlot(EntityPlayer p, int index)
+    {
+        return ItemStack.EMPTY;
+    }
+    
+    public void openForPlayer(EntityPlayerMP p)
+    {
+        if(p.openContainer != p.inventoryContainer)
+        {
+            p.closeScreen();
+        }
+        p.getNextWindowId();
+        p.openContainer = this;
+        p.openContainer.windowId = p.currentWindowId;
+        ModPacketHandler.sendCampFireInventory(p, windowId, fireInv.getName());
+        p.openContainer.addListener(new IContainerListener() 
+        {
+            @Override
+            public void sendAllContents(Container containerToSend, NonNullList<ItemStack> itemsList) 
+            {
+                p.connection.sendPacket(new SPacketWindowItems(containerToSend.windowId, itemsList));
+                p.connection.sendPacket(new SPacketSetSlot(-1, -1, p.inventory.getItemStack()));
+            }
+
+            @Override
+            public void sendSlotContents(Container containerToSend, int slotInd, ItemStack stack) 
+            {
+                if(!(containerToSend.getSlot(slotInd) instanceof SlotCrafting))
+                {
+                    if(containerToSend == p.inventoryContainer)
+                    {
+                        CriteriaTriggers.INVENTORY_CHANGED.trigger(p, p.inventory);
+                    }
+                    if(!p.isChangingQuantityOnly)
+                    {
+                        p.connection.sendPacket(new SPacketSetSlot(containerToSend.windowId, slotInd, stack));
+                    }
+                }
+            }
+
+            @Override
+            public void sendWindowProperty(Container containerIn, int varToUpdate, int newValue) 
+            {
+                p.connection.sendPacket(new SPacketWindowProperty(containerIn.windowId, varToUpdate, newValue));
+            }
+
+            @Override
+            public void sendAllWindowProperties(Container containerIn, IInventory inventory) 
+            {
+                for(int i = 0; i < inventory.getFieldCount(); i++)
+                {
+                    p.connection.sendPacket(new SPacketWindowProperty(containerIn.windowId, i, inventory.getField(i)));
+                }
+            }
+        });
+    }
+}

+ 39 - 56
src/main/java/me/km/inventory/CustomContainer.java

@@ -1,103 +1,86 @@
 package me.km.inventory;
 
 import me.km.KajetansMod;
-import me.km.snuviscript.SnuviInventory;
 import net.minecraft.entity.player.EntityPlayer;
 import net.minecraft.entity.player.EntityPlayerMP;
 import net.minecraft.inventory.ClickType;
-import net.minecraft.inventory.ContainerChest;
-import net.minecraft.inventory.IInventory;
-import net.minecraft.inventory.InventoryBasic;
 import net.minecraft.item.ItemStack;
-import net.minecraft.network.play.server.SPacketOpenWindow;
 
-public class CustomContainer extends ContainerChest
+public class CustomContainer extends CustomContainerBase
 {
     private boolean canBeClosed;
     
-    public CustomContainer(String title, int slotCount, EntityPlayerMP p) 
+    public CustomContainer(InventoryBase inv, EntityPlayer p) 
     {
-        super(p.inventory, new EntityInventory(title, slotCount, p), p);
+        super(inv, p);
         canBeClosed = true;
     }
     
-    public void closeSafe(EntityPlayer p)
+    @Override
+    public void closeSafe()
     {
         if(canBeClosed)
         {
-            p.closeScreen();
+            super.closeSafe();
         }
-        else
+        else if(isServerSide)
         {
             KajetansMod.scheduler.scheduleTask(() -> 
             {
-                if(p.openContainer.windowId == this.windowId)
+                if(viewer.openContainer.windowId == this.windowId)
                 {
-                    p.closeScreen();
+                    super.closeSafe();
                 }
             });
         }
     }
     
-    public CustomContainer(SnuviInventory inv, EntityPlayerMP p) 
-    {
-        super(p.inventory, inv, p);
-        canBeClosed = true;
-    }
-    
-    public InventoryBasic getShownInventory()
-    {
-        return (InventoryBasic) this.getLowerChestInventory();
-    }
-    
-    public void openForPlayer(EntityPlayerMP p)
+    @Override
+    public void openForPlayer()
     {
         // opening container on next tick to prevent closing during noClicking or onCanceledClick
-        if(p.openContainer instanceof CustomContainer)
+        if(isServerSide)
         {
-            CustomContainer cc = (CustomContainer) p.openContainer;
-            if(!cc.canBeClosed)
+            if(viewer.openContainer instanceof CustomContainer)
             {
-                KajetansMod.scheduler.scheduleTask(() -> openForPlayer(p));
-                return;
+                CustomContainer cc = (CustomContainer) viewer.openContainer;
+                if(!cc.canBeClosed)
+                {
+                    KajetansMod.scheduler.scheduleTask(() -> openForPlayer());
+                    return;
+                }
             }
+            super.openForPlayer();
         }
-        if(p.openContainer != p.inventoryContainer)
-        {
-            p.closeScreen();
-        }
-        p.getNextWindowId();
-        IInventory inv = this.getLowerChestInventory();
-        p.connection.sendPacket(new SPacketOpenWindow(p.currentWindowId, "minecraft:container", inv.getDisplayName(), inv.getSizeInventory()));
-        p.openContainer = this;
-        p.openContainer.windowId = p.currentWindowId;
-        p.openContainer.addListener(p);
-    }
-    
-    public boolean noClicking(int slotId, int dragType, ClickType clickTypeIn, EntityPlayerMP player)
-    {
-        return true;
     }
     
     @Override
     public final ItemStack slotClick(int slotId, int dragType, ClickType clickTypeIn, EntityPlayer p) 
     {
-        if(p instanceof EntityPlayerMP)
+        if(slotId < 0 || slotId >= inv.getSizeInventory())
         {
-            EntityPlayerMP player = (EntityPlayerMP) p;
-            canBeClosed = false;
-            if(noClicking(slotId, dragType, clickTypeIn, player))
-            {
-                onCanceledClick(slotId, dragType, clickTypeIn, player);
-                canBeClosed = true;
+            return super.slotClick(slotId, dragType, clickTypeIn, p);
+        }
+        switch(inv.getSlotStatus(((IdSlot) this.getSlot(slotId)).getRealId()))
+        {
+            case 0: return ItemStack.EMPTY;
+            case 1: return super.slotClick(slotId, dragType, clickTypeIn, p);
+            case 2: 
+                if(isServerSide)
+                {
+                    System.out.println("Button was pressed");
+                    EntityPlayerMP player = (EntityPlayerMP) p;
+                    canBeClosed = false;
+                    onButtonClick(slotId, dragType, clickTypeIn, player);
+                    canBeClosed = true;
+                }
                 return ItemStack.EMPTY;
-            }
-            canBeClosed = true;
+            case 3: return ItemStack.EMPTY; 
         }
-        return super.slotClick(slotId, dragType, clickTypeIn, p);
+        return ItemStack.EMPTY;
     }  
     
-    public void onCanceledClick(int slotId, int dragType, ClickType clickTypeIn, EntityPlayerMP player)
+    public void onButtonClick(int slotId, int dragType, ClickType clickTypeIn, EntityPlayerMP player)
     {
     }
 }

+ 164 - 0
src/main/java/me/km/inventory/CustomContainerBase.java

@@ -0,0 +1,164 @@
+package me.km.inventory;
+
+import me.km.networking.ModPacketHandler;
+import net.minecraft.advancements.CriteriaTriggers;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.entity.player.InventoryPlayer;
+import net.minecraft.inventory.Container;
+import net.minecraft.inventory.IContainerListener;
+import net.minecraft.inventory.IInventory;
+import net.minecraft.inventory.Slot;
+import net.minecraft.inventory.SlotCrafting;
+import net.minecraft.item.ItemStack;
+import net.minecraft.network.play.server.SPacketSetSlot;
+import net.minecraft.network.play.server.SPacketWindowItems;
+import net.minecraft.network.play.server.SPacketWindowProperty;
+import net.minecraft.util.NonNullList;
+
+public abstract class CustomContainerBase extends Container
+{
+    protected final EntityPlayer viewer;
+    protected final boolean isServerSide;
+    protected final InventoryBase inv;
+    private final int numRows;
+
+    protected CustomContainerBase(InventoryBase inv, EntityPlayer viewer)
+    {
+        // basic stuff
+        this.viewer = viewer;
+        this.isServerSide = viewer instanceof EntityPlayerMP;
+        this.inv = inv;
+        this.numRows = inv.getRows();
+        int i = (this.numRows - 4) * 18;
+        
+        // inventory slots
+        int counter = 0;
+        for(int y = 0; y < this.numRows; y++)
+        {
+            for(int x = 0; x < 9; x++)
+            {
+                if(inv.isSlotValid(x, y))
+                {
+                    super.addSlotToContainer(new IdSlot(inv, counter, 8 + x * 18, 18 + y * 18, x + y * 9));
+                    counter++;
+                }
+            }
+        }
+
+        InventoryPlayer pInv = this.viewer.inventory;
+        // plaver inventory slots
+        for(int y = 0; y < 3; y++)
+        {
+            for(int x = 0; x < 9; x++)
+            {
+                super.addSlotToContainer(new Slot(pInv, x + y * 9 + 9, 8 + x * 18, 103 + y * 18 + i));
+            }
+        }
+        for(int x = 0; x < 9; x++)
+        {
+            super.addSlotToContainer(new Slot(pInv, x, 8 + x * 18, 161 + i));
+        }
+    }
+
+    public InventoryBase getInventoryBase() 
+    {
+        return inv;
+    }
+    
+    public void closeSafe()
+    {
+        viewer.closeScreen();
+    }
+    
+    public void openForPlayer()
+    {
+        if(isServerSide)
+        {
+            EntityPlayerMP p = (EntityPlayerMP) viewer;
+            if(p.openContainer != p.inventoryContainer)
+            {
+                p.closeScreen();
+            }
+            p.getNextWindowId();
+            p.openContainer = this;
+            p.openContainer.windowId = p.currentWindowId;
+            ModPacketHandler.sendCustomInventory(p, windowId, inv);
+            p.openContainer.addListener(new IContainerListener() 
+            {
+                @Override
+                public void sendAllContents(Container containerToSend, NonNullList<ItemStack> itemsList) 
+                {
+                    p.connection.sendPacket(new SPacketWindowItems(containerToSend.windowId, itemsList));
+                    p.connection.sendPacket(new SPacketSetSlot(-1, -1, p.inventory.getItemStack()));
+                }
+
+                @Override
+                public void sendSlotContents(Container containerToSend, int slotInd, ItemStack stack) 
+                {
+                    if(!(containerToSend.getSlot(slotInd) instanceof SlotCrafting))
+                    {
+                        if(containerToSend == p.inventoryContainer)
+                        {
+                            CriteriaTriggers.INVENTORY_CHANGED.trigger(p, p.inventory);
+                        }
+                        if(!p.isChangingQuantityOnly)
+                        {
+                            p.connection.sendPacket(new SPacketSetSlot(containerToSend.windowId, slotInd, stack));
+                        }
+                    }
+                }
+
+                @Override
+                public void sendWindowProperty(Container containerIn, int varToUpdate, int newValue) 
+                {
+                }
+
+                @Override
+                public void sendAllWindowProperties(Container containerIn, IInventory inventory) 
+                {
+                }
+            });
+        }
+    }
+
+    @Override
+    public boolean canInteractWith(EntityPlayer p)
+    {
+        return this.inv.isUsableByPlayer(p);
+    }
+
+    @Override
+    public ItemStack transferStackInSlot(EntityPlayer p, int index)
+    {
+        ItemStack stack = ItemStack.EMPTY;
+        Slot slot = this.inventorySlots.get(index);
+        int size = inv.getSizeInventory();
+        
+        if(slot != null && slot.getHasStack())
+        {
+            ItemStack slotStack = slot.getStack();
+            stack = slotStack.copy();
+            if(index < size)
+            {
+                if(!this.mergeItemStack(slotStack, size, this.inventorySlots.size(), true))
+                {
+                    return ItemStack.EMPTY;
+                }
+            }
+            else if(!this.mergeItemStack(slotStack, 0, size, false))
+            {
+                return ItemStack.EMPTY;
+            }
+            if(slotStack.isEmpty())
+            {
+                slot.putStack(ItemStack.EMPTY);
+            }
+            else
+            {
+                slot.onSlotChanged();
+            }
+        }
+        return stack;
+    }
+}

+ 0 - 36
src/main/java/me/km/inventory/EntityInventory.java

@@ -1,36 +0,0 @@
-package me.km.inventory;
-
-import net.minecraft.entity.Entity;
-import net.minecraft.inventory.InventoryBasic;
-import net.minecraft.item.ItemStack;
-
-public class EntityInventory extends InventoryBasic
-{
-    private final Entity ent;
-    
-    public EntityInventory(String title, int slotCount, Entity ent) 
-    {
-        super(title, true, slotCount);
-        this.ent = ent;
-    }
-    
-    public Entity getEntity()
-    {
-        return ent;
-    }
-    
-    public void setContents(Iterable<? extends ItemStack> stacks)
-    {
-        int counter = 0;
-        int size = super.getSizeInventory();
-        for(ItemStack stack : stacks)
-        {
-            this.setInventorySlotContents(counter, stack);
-            counter++;
-            if(counter >= size)
-            {
-                return;
-            }
-        }
-    }
-}

+ 20 - 0
src/main/java/me/km/inventory/IdSlot.java

@@ -0,0 +1,20 @@
+package me.km.inventory;
+
+import net.minecraft.inventory.IInventory;
+import net.minecraft.inventory.Slot;
+
+public class IdSlot extends Slot
+{
+    private int realId;
+    
+    public IdSlot(IInventory inventoryIn, int index, int xPosition, int yPosition, int realId) 
+    {
+        super(inventoryIn, index, xPosition, yPosition);
+        this.realId = realId;
+    }
+
+    public int getRealId() 
+    {
+        return realId;
+    }
+}

+ 155 - 0
src/main/java/me/km/inventory/InventoryBase.java

@@ -0,0 +1,155 @@
+package me.km.inventory;
+
+import net.minecraft.entity.Entity;
+import net.minecraft.inventory.InventoryBasic;
+import net.minecraft.item.ItemStack;
+
+public class InventoryBase extends InventoryBasic
+{
+    private final Entity owner;
+    private final int rows;
+    private final byte[] data;
+    
+    public InventoryBase(String title, byte[] data, int slots, Entity owner) 
+    {
+        super(title, true, slots);
+        this.owner = owner;
+        this.rows = slots / 9 + (slots % 9 == 0 ? 0 : 1);
+        this.data = data;
+    }
+    
+    private static int countZeros(String slots)
+    {
+        int i = 0;
+        for(char c : slots.toCharArray())
+        {
+            if(c == '0')
+            {
+                i++;
+            }
+        }
+        return i;
+    }
+    
+    public InventoryBase(String title, String slots, Entity owner) 
+    {
+        super(title, true, Math.min(54, slots.length() - countZeros(slots)));
+        this.owner = owner;
+        
+        int slotAmount = Math.min(54, slots.length());
+        this.rows = slotAmount / 9 + (slotAmount % 9 == 0 ? 0 : 1);
+        
+        this.data = new byte[14];
+        
+        int id;
+        for(int i = 0; i < slotAmount; i++)
+        {
+            id = Character.getNumericValue(slots.charAt(i));
+            if(id > 3 || id < 0)
+            {
+                id = 0;
+            }
+            switch(i % 4)
+            {
+                case 0:
+                    data[i / 4] += id;
+                    break;
+                case 1:
+                    data[i / 4] += id << 2;
+                    break;
+                case 2:
+                    data[i / 4] += id << 4;
+                    break;
+                case 3:
+                    data[i / 4] += id << 6;
+                    break;
+            }
+        }
+    }
+    
+    public InventoryBase(String title, int slots, Entity owner) 
+    {
+        super(title, true, Math.max(9, Math.min(54, ((slots / 9) + ((slots % 9) == 0 ? 0 : 1)) * 9)));
+        this.owner = owner;
+        this.rows = super.getSizeInventory() / 9;
+        this.data = new byte[14];
+        
+        int end = super.getSizeInventory();
+        for(int i = 0; i < end; i++)
+        {
+            switch(i % 4)
+            {
+                case 0:
+                    data[i / 4] += 1;
+                    break;
+                case 1:
+                    data[i / 4] += 4;
+                    break;
+                case 2:
+                    data[i / 4] += 16;
+                    break;
+                case 3:
+                    data[i / 4] += 64;
+                    break;
+            }
+        }
+    }
+
+    public byte[] getData() 
+    {
+        return data;
+    }
+
+    public Entity getOwner() 
+    {
+        return owner;
+    }
+    
+    public void setContents(Iterable<? extends ItemStack> stacks)
+    {
+        int counter = 0;
+        int size = super.getSizeInventory();
+        for(ItemStack stack : stacks)
+        {
+            this.setInventorySlotContents(counter, stack);
+            counter++;
+            if(counter >= size)
+            {
+                return;
+            }
+        }
+    }
+    
+    public int getRows()
+    {
+        return rows;
+    }
+    
+    public int getSlotStatus(int slot)
+    {
+        int index = Math.max(0, Math.min(13, slot / 4));
+        switch(slot % 4)
+        {
+            case 0: return data[index] & 3;
+            case 1: return (data[index] >> 2) & 3;
+            case 2: return (data[index] >> 4) & 3;
+            case 3: return (data[index] >> 6) & 3;
+        }
+        return 0;
+    }
+    
+    public int getSlotStatus(int x, int y)
+    {
+        return getSlotStatus(y * 9 + x);
+    }
+    
+    public boolean isSlotValid(int slot)
+    {
+        return getSlotStatus(slot) != 0;
+    }
+    
+    public boolean isSlotValid(int x, int y)
+    {
+        return getSlotStatus(x, y) != 0;
+    }
+}

+ 5 - 5
src/main/java/me/km/inventory/InventoryUtils.java

@@ -10,7 +10,7 @@ public class InventoryUtils
 {       
     public static InventoryBasic getInventory(EntityVillager v)
     {
-        EntityInventory real = new EntityInventory("Villager", 9, v);
+        InventoryBase real = new InventoryBase("Villager", 9, v);
         InventoryBasic old = v.getVillagerInventory();
         for(int i = 0; i < 9; i++)
         {
@@ -19,13 +19,13 @@ public class InventoryUtils
         return real;
     }
     
-    public static void setInventory(EntityInventory inv)
+    public static void setInventory(InventoryBase inv)
     {
-        if(inv.getEntity() instanceof EntityVillager)
+        if(inv.getOwner() instanceof EntityVillager)
         {
-            EntityVillager v = (EntityVillager) inv.getEntity();
+            EntityVillager v = (EntityVillager) inv.getOwner();
             InventoryBasic vInv = v.getVillagerInventory();
-            for(int i = 0; i < 9; i++)
+            for(int i = 0; i < 8; i++)
             {
                 vInv.setInventorySlotContents(i, inv.getStackInSlot(i).copy());
             }

+ 3 - 5
src/main/java/me/km/inventory/TeleportContainer.java

@@ -10,7 +10,6 @@ import me.km.commands.CommandTeleportAccept;
 import net.minecraft.entity.player.EntityPlayer;
 import net.minecraft.entity.player.EntityPlayerMP;
 import net.minecraft.inventory.ClickType;
-import net.minecraft.inventory.InventoryBasic;
 
 public class TeleportContainer extends CustomContainer
 {
@@ -18,10 +17,9 @@ public class TeleportContainer extends CustomContainer
     
     public TeleportContainer(EntityPlayerMP p, List<EntityPlayer> list) 
     {
-        super("Teleport to Player ...", list.size() % 9 == 0 ? list.size() : ((list.size() / 9) + 1) * 9, p);
+        super(new InventoryBase("Teleport to Player ...", list.size(), p), p);
         this.players = list;
         
-        InventoryBasic inv = super.getShownInventory();
         int counter = 0;
         for(EntityPlayer player : players) 
         {
@@ -35,7 +33,7 @@ public class TeleportContainer extends CustomContainer
     }
 
     @Override
-    public void onCanceledClick(int slotId, int dragType, ClickType clickTypeIn, EntityPlayerMP p) 
+    public void onButtonClick(int slotId, int dragType, ClickType clickTypeIn, EntityPlayerMP p) 
     {
         if(slotId >= players.size())
         {
@@ -48,6 +46,6 @@ public class TeleportContainer extends CustomContainer
         m.send(p2, "Du kannst mit /tpa akzeptieren.");
         HashMap<UUID, UUID> tpaccept = KajetansMod.generalCommands.getCommand(CommandTeleportAccept.class).tpaccept;
         tpaccept.put(p2.getUniqueID(), p.getUniqueID());
-        this.closeSafe(p);
+        this.closeSafe();
     }
 }

+ 1 - 1
src/main/java/me/km/items/ItemGun.java

@@ -147,7 +147,7 @@ public class ItemGun extends ItemWeapon
                     EntityLivingBase target = Utils.getTargetedEntity(p, 128, d, d, d, EntityLivingBase.class);
                     if(target != null)
                     {
-                        target.attackEntityFrom(DamageSource.causeMobDamage(p), damage);
+                        target.attackEntityFrom(DamageSource.causePlayerDamage(p), damage);
                     }   
                 }
                 

+ 0 - 5
src/main/java/me/km/items/ModItems.java

@@ -1,8 +1,5 @@
 package me.km.items;
 
-import com.google.common.collect.Multimap;
-import java.util.Iterator;
-import java.util.UUID;
 import me.km.items.noglint.ItemNoGlintPotion;
 import me.km.KajetansMod;
 import me.km.blocks.EnumMetals;
@@ -10,8 +7,6 @@ import me.km.items.noglint.ItemNoGlintLingeringPotion;
 import me.km.items.noglint.ItemNoGlintSplashPotion;
 import me.km.sounds.Sounds;
 import net.minecraft.creativetab.CreativeTabs;
-import net.minecraft.entity.SharedMonsterAttributes;
-import net.minecraft.entity.ai.attributes.AttributeModifier;
 import net.minecraft.init.Items;
 import net.minecraft.init.SoundEvents;
 import net.minecraft.inventory.EntityEquipmentSlot;

+ 8 - 0
src/main/java/me/km/jobsystem/JobAPI.java

@@ -6,6 +6,7 @@ import java.util.HashSet;
 import java.util.Set;
 import me.km.KajetansMod;
 import me.km.api.Module;
+import me.km.skills.LeveledSkill;
 import me.km.skills.Skill;
 import net.minecraft.block.Block;
 import net.minecraft.entity.player.EntityPlayer;
@@ -140,6 +141,13 @@ public class JobAPI extends Module
         return map;
     }
     
+    public HashMap<Integer, LeveledSkill> getActiveSkillMap(EntityPlayer p)
+    {
+        HashMap<Integer, LeveledSkill> map = new HashMap<>();
+        getSkillMap(p).forEach((k, v) -> map.put(k.getId(), new LeveledSkill(k, v)));
+        return map;
+    }
+    
     // -------------------------------------------------------------------------
     // jobs
     // -------------------------------------------------------------------------

+ 64 - 0
src/main/java/me/km/networking/CampFireInventory.java

@@ -0,0 +1,64 @@
+package me.km.networking;
+
+import io.netty.buffer.ByteBuf;
+import java.nio.charset.StandardCharsets;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.entity.EntityPlayerSP;
+import net.minecraft.client.player.inventory.ContainerLocalMenu;
+import net.minecraft.util.text.TextComponentString;
+import net.minecraft.util.text.TextComponentTranslation;
+import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
+import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler;
+import net.minecraftforge.fml.common.network.simpleimpl.MessageContext;
+
+public class CampFireInventory implements IMessage
+{
+    private int windowId;
+    private String windowTitle;
+    
+    public CampFireInventory() 
+    {
+        windowId = -1;
+        windowTitle = "";
+    }
+    
+    public CampFireInventory(int id, String name) 
+    {
+        windowId = id;
+        windowTitle = name;
+    }
+    
+    @Override
+    public void fromBytes(ByteBuf buf) 
+    {
+        windowId = buf.readInt();
+        int length = buf.readInt();
+        windowTitle = buf.readBytes(length).toString(StandardCharsets.UTF_8);
+    }
+
+    @Override
+    public void toBytes(ByteBuf buf) 
+    {
+        buf.writeInt(windowId);
+        byte[] b = windowTitle.getBytes(StandardCharsets.UTF_8);
+        buf.writeInt(b.length);
+        buf.writeBytes(b);
+    }
+
+    public static class Handler implements IMessageHandler<CampFireInventory, IMessage>
+    {
+        @Override
+        public IMessage onMessage(CampFireInventory m, MessageContext ctx) 
+        {
+            Minecraft mc = Minecraft.getMinecraft();
+            final EntityPlayerSP p = mc.player;
+            mc.addScheduledTask(() -> 
+            {
+                mc.displayGuiScreen(new CampFireInventoryGui(p.inventory, 
+                        new ContainerLocalMenu("km:campfire", new TextComponentTranslation(m.windowTitle), 6)));
+                p.openContainer.windowId = m.windowId;
+            });
+            return null;
+        }
+    }
+}

+ 66 - 0
src/main/java/me/km/networking/CampFireInventoryGui.java

@@ -0,0 +1,66 @@
+package me.km.networking;
+
+import me.km.KajetansMod;
+import me.km.inventory.ContainerCampFire;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.inventory.IInventory;
+import net.minecraft.util.ResourceLocation;
+import net.minecraftforge.fml.relauncher.Side;
+import net.minecraftforge.fml.relauncher.SideOnly;
+import net.minecraft.client.gui.inventory.GuiContainer;
+import net.minecraft.entity.player.InventoryPlayer;
+
+@SideOnly(Side.CLIENT)
+public class CampFireInventoryGui extends GuiContainer
+{
+    private static final ResourceLocation FURNACE_GUI_TEXTURES = new ResourceLocation(KajetansMod.MODID, "textures/gui/container/campfire.png");
+    private final InventoryPlayer pInv;
+    private final IInventory tileFurnace;
+
+    public CampFireInventoryGui(InventoryPlayer pInv, IInventory fireInv)
+    {
+        super(new ContainerCampFire(pInv, fireInv));
+        this.pInv = pInv;
+        this.tileFurnace = fireInv;
+    }
+
+    @Override
+    public void drawScreen(int mouseX, int mouseY, float partialTicks)
+    {
+        this.drawDefaultBackground();
+        super.drawScreen(mouseX, mouseY, partialTicks);
+        this.renderHoveredToolTip(mouseX, mouseY);
+    }
+
+    @Override
+    protected void drawGuiContainerForegroundLayer(int mouseX, int mouseY)
+    {
+        String s = this.tileFurnace.getDisplayName().getUnformattedText();
+        this.fontRenderer.drawString(s, this.xSize / 2 - this.fontRenderer.getStringWidth(s) / 2, 6, 4210752);
+        this.fontRenderer.drawString(this.pInv.getDisplayName().getUnformattedText(), 8, this.ySize - 96 + 2, 4210752);
+    }
+
+    @Override
+    protected void drawGuiContainerBackgroundLayer(float partialTicks, int mouseX, int mouseY)
+    {
+        GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
+        this.mc.getTextureManager().bindTexture(FURNACE_GUI_TEXTURES);
+        int i = (this.width - this.xSize) / 2;
+        int j = (this.height - this.ySize) / 2;
+        this.drawTexturedModalRect(i, j, 0, 0, this.xSize, this.ySize);
+
+        int l = this.getCookProgressScaled(0);
+        this.drawTexturedModalRect(i + 75, j + 16, 176, 14, l + 1, 16);
+        l = this.getCookProgressScaled(1);
+        this.drawTexturedModalRect(i + 75, j + 34, 176, 14, l + 1, 16);
+        l = this.getCookProgressScaled(2);
+        this.drawTexturedModalRect(i + 75, j + 52, 176, 14, l + 1, 16);
+    }
+
+    private int getCookProgressScaled(int id)
+    {
+        int i = this.tileFurnace.getField(id);
+        int j = 200;
+        return j != 0 && i != 0 ? i * 24 / j : 0;
+    }
+}

+ 74 - 0
src/main/java/me/km/networking/CustomInventory.java

@@ -0,0 +1,74 @@
+package me.km.networking;
+
+import io.netty.buffer.ByteBuf;
+import java.nio.charset.StandardCharsets;
+import me.km.inventory.InventoryBase;
+import net.minecraft.client.Minecraft;
+import net.minecraft.client.entity.EntityPlayerSP;
+import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
+import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler;
+import net.minecraftforge.fml.common.network.simpleimpl.MessageContext;
+
+public class CustomInventory implements IMessage
+{
+    private int windowId;
+    private String windowTitle;
+    private byte slotCount;
+    private byte[] data;
+    
+    public CustomInventory() 
+    {
+        windowId = -1;
+        windowTitle = "";
+        slotCount = 9;
+        data = new byte[14];
+    }
+    
+    public CustomInventory(int id, InventoryBase inv) 
+    {
+        windowId = id;
+        windowTitle = inv.getName();
+        slotCount = (byte) inv.getSizeInventory();
+        data = inv.getData();
+    }
+    
+    @Override
+    public void fromBytes(ByteBuf buf) 
+    {
+        windowId = buf.readInt();
+        int length = buf.readInt();
+        windowTitle = buf.readBytes(length).toString(StandardCharsets.UTF_8);
+        slotCount = buf.readByte();
+        for(int i = 0; i < 14; i++)
+        {
+            data[i] = buf.readByte();
+        }
+    }
+
+    @Override
+    public void toBytes(ByteBuf buf) 
+    {
+        buf.writeInt(windowId);
+        byte[] b = windowTitle.getBytes(StandardCharsets.UTF_8);
+        buf.writeInt(b.length);
+        buf.writeBytes(b);
+        buf.writeByte(slotCount);
+        buf.writeBytes(data);
+    }
+
+    public static class Handler implements IMessageHandler<CustomInventory, IMessage>
+    {
+        @Override
+        public IMessage onMessage(CustomInventory m, MessageContext ctx) 
+        {
+            Minecraft mc = Minecraft.getMinecraft();
+            final EntityPlayerSP p = mc.player;
+            mc.addScheduledTask(() -> 
+            {
+                mc.displayGuiScreen(new CustomInventoryGui(p.inventory, new InventoryBase(m.windowTitle, m.data, m.slotCount, p)));
+                p.openContainer.windowId = m.windowId;
+            });
+            return null;
+        }
+    }
+}

+ 72 - 0
src/main/java/me/km/networking/CustomInventoryGui.java

@@ -0,0 +1,72 @@
+package me.km.networking;
+
+import me.km.KajetansMod;
+import me.km.inventory.CustomContainer;
+import me.km.inventory.InventoryBase;
+import net.minecraft.client.renderer.GlStateManager;
+import net.minecraft.client.Minecraft;
+import net.minecraft.inventory.IInventory;
+import net.minecraft.util.ResourceLocation;
+import net.minecraftforge.fml.relauncher.Side;
+import net.minecraftforge.fml.relauncher.SideOnly;
+import net.minecraft.client.gui.inventory.GuiContainer;
+
+@SideOnly(Side.CLIENT)
+public class CustomInventoryGui extends GuiContainer
+{
+    /** The ResourceLocation containing the chest GUI texture. */
+    private static final ResourceLocation CHEST_GUI_TEXTURE = new ResourceLocation("textures/gui/container/generic_54.png");
+    private static final ResourceLocation EMPTY_TILE = new ResourceLocation(KajetansMod.MODID, "textures/gui/container/empty_tile.png");
+    private final IInventory pInv;
+    private final InventoryBase inv;
+    private final int inventoryRows;
+
+    public CustomInventoryGui(IInventory pInv, InventoryBase inv)
+    {
+        super(new CustomContainer(inv, Minecraft.getMinecraft().player));
+        this.pInv = pInv;
+        this.inv = inv;
+        this.allowUserInput = false;
+        this.inventoryRows = inv.getRows();
+        this.ySize = 114 + this.inventoryRows * 18;
+    }
+
+    @Override
+    public void drawScreen(int mouseX, int mouseY, float partialTicks)
+    {
+        this.drawDefaultBackground();
+        super.drawScreen(mouseX, mouseY, partialTicks);
+        this.renderHoveredToolTip(mouseX, mouseY);
+    }
+
+    @Override
+    protected void drawGuiContainerForegroundLayer(int mouseX, int mouseY)
+    {
+        this.fontRenderer.drawString(this.inv.getDisplayName().getUnformattedText(), 8, 6, 4210752);
+        this.fontRenderer.drawString(this.pInv.getDisplayName().getUnformattedText(), 8, this.ySize - 96 + 2, 4210752);
+    }
+
+    @Override
+    protected void drawGuiContainerBackgroundLayer(float partialTicks, int mouseX, int mouseY)
+    {
+        GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F);
+        this.mc.getTextureManager().bindTexture(CHEST_GUI_TEXTURE);
+        int i = (this.width - this.xSize) / 2;
+        int j = (this.height - this.ySize) / 2;
+        this.drawTexturedModalRect(i, j, 0, 0, this.xSize, this.inventoryRows * 18 + 17);
+        this.drawTexturedModalRect(i, j + this.inventoryRows * 18 + 17, 0, 126, this.xSize, 96);
+        i += 7;
+        j += 17;
+        this.mc.getTextureManager().bindTexture(EMPTY_TILE);
+        for(int x = 0; x < 9; x++)
+        {
+            for(int y = 0; y < inventoryRows; y++)
+            {
+                if(!inv.isSlotValid(x, y))
+                {
+                    this.drawTexturedModalRect(i + 18 * x, j + 18 * y, 0, 0, 18, 18);
+                }
+            }
+        }
+    }
+}

+ 63 - 0
src/main/java/me/km/networking/HumanScaleUpdate.java

@@ -0,0 +1,63 @@
+package me.km.networking;
+
+import io.netty.buffer.ByteBuf;
+import me.km.entities.EntityHuman;
+import net.minecraft.client.Minecraft;
+import net.minecraft.entity.Entity;
+import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
+import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler;
+import net.minecraftforge.fml.common.network.simpleimpl.MessageContext;
+
+public class HumanScaleUpdate implements IMessage
+{
+    private int humanId;
+    private float scale;
+    private byte slim;
+    
+    public HumanScaleUpdate() 
+    {
+        humanId = -1;
+        scale = 1;
+        slim = 0;
+    }
+    
+    public HumanScaleUpdate(EntityHuman h) 
+    {
+        humanId = h.getEntityId();
+        scale = h.getScale();
+        slim = h.getSlim();
+    }
+    
+    @Override
+    public void fromBytes(ByteBuf buf) 
+    {
+        humanId = buf.readInt();
+        scale = buf.readFloat();
+        slim = buf.readByte();
+    }
+
+    @Override
+    public void toBytes(ByteBuf buf) 
+    {
+        buf.writeInt(humanId);
+        buf.writeFloat(scale);
+        buf.writeByte(slim);
+    }
+
+    public static class Handler implements IMessageHandler<HumanScaleUpdate, IMessage>
+    {
+        @Override
+        public IMessage onMessage(HumanScaleUpdate message, MessageContext ctx) 
+        {
+            Entity ent = Minecraft.getMinecraft().world.getEntityByID(message.humanId);
+            if(ent == null || ent.getClass() != EntityHuman.class)
+            {
+                return null;
+            }
+            EntityHuman h = (EntityHuman) ent;
+            h.setScale(message.scale);
+            h.setSlim(message.slim);
+            return null;
+        }
+    }
+}

+ 63 - 0
src/main/java/me/km/networking/HumanUpdate.java

@@ -0,0 +1,63 @@
+package me.km.networking;
+
+import io.netty.buffer.ByteBuf;
+import java.nio.charset.StandardCharsets;
+import me.km.entities.EntityHuman;
+import me.km.entities.HumanSkinLoader;
+import net.minecraft.client.Minecraft;
+import net.minecraft.entity.Entity;
+import net.minecraftforge.fml.common.network.simpleimpl.IMessage;
+import net.minecraftforge.fml.common.network.simpleimpl.IMessageHandler;
+import net.minecraftforge.fml.common.network.simpleimpl.MessageContext;
+
+public class HumanUpdate implements IMessage
+{
+    private int humanId;
+    private String name;
+    
+    public HumanUpdate() 
+    {
+        humanId = -1;
+        name = null;
+    }
+    
+    public HumanUpdate(EntityHuman h) 
+    {
+        humanId = h.getEntityId();
+        name = h.getName();
+    }
+    
+    @Override
+    public void fromBytes(ByteBuf buf) 
+    {
+        humanId = buf.readInt();
+        int length = buf.readInt();
+        name = buf.readBytes(length).toString(StandardCharsets.UTF_8);
+    }
+
+    @Override
+    public void toBytes(ByteBuf buf) 
+    {
+        buf.writeInt(humanId);
+        byte[] b = name.getBytes(StandardCharsets.UTF_8);
+        buf.writeInt(b.length);
+        buf.writeBytes(b);
+    }
+
+    public static class Handler implements IMessageHandler<HumanUpdate, IMessage>
+    {
+        @Override
+        public IMessage onMessage(HumanUpdate message, MessageContext ctx) 
+        {
+            Entity ent = Minecraft.getMinecraft().world.getEntityByID(message.humanId);
+            if(ent == null || ent.getClass() != EntityHuman.class)
+            {
+                return null;
+            }
+            EntityHuman h = (EntityHuman) ent;
+            h.setName(message.name);
+            h.setTexture(HumanSkinLoader.INSTANCE.getTexture(message.name, loc -> h.setTexture(loc)));
+            return null;
+        }
+    }
+}

+ 33 - 0
src/main/java/me/km/networking/ModPacketHandler.java

@@ -1,6 +1,8 @@
 package me.km.networking;
 
 import me.km.KajetansMod;
+import me.km.entities.EntityHuman;
+import me.km.inventory.InventoryBase;
 import net.minecraft.entity.player.EntityPlayerMP;
 import net.minecraftforge.fml.common.network.NetworkRegistry;
 import net.minecraftforge.fml.common.network.simpleimpl.SimpleNetworkWrapper;
@@ -16,6 +18,10 @@ public class ModPacketHandler
     {
         INSTANCE.registerMessage(PlayerDisplay.Handler.class, PlayerDisplay.class, id++, Side.CLIENT);
         INSTANCE.registerMessage(FunctionKey.Handler.class, FunctionKey.class, id++, Side.SERVER);
+        INSTANCE.registerMessage(HumanUpdate.Handler.class, HumanUpdate.class, id++, Side.CLIENT);
+        INSTANCE.registerMessage(HumanScaleUpdate.Handler.class, HumanScaleUpdate.class, id++, Side.CLIENT);
+        INSTANCE.registerMessage(CustomInventory.Handler.class, CustomInventory.class, id++, Side.CLIENT);
+        INSTANCE.registerMessage(CampFireInventory.Handler.class, CampFireInventory.class, id++, Side.CLIENT);
     }
     
     public static void sendStats(EntityPlayerMP p, int action, int index, String text)
@@ -27,4 +33,31 @@ public class ModPacketHandler
     {
         INSTANCE.sendToServer(new FunctionKey(key));
     }
+    
+    public static void sendHumanUpdate(EntityPlayerMP p, EntityHuman human)
+    {
+        INSTANCE.sendTo(new HumanUpdate(human), p);
+    }
+    
+    public static void sendHumanUpdate(EntityHuman human)
+    {
+        HumanUpdate update = new HumanUpdate(human);
+        KajetansMod.server.getPlayerList().getPlayers().forEach(p -> INSTANCE.sendTo(update, p));
+    }
+    
+    public static void sendHumanScaleUpdate(EntityHuman human)
+    {
+        HumanScaleUpdate update = new HumanScaleUpdate(human);
+        KajetansMod.server.getPlayerList().getPlayers().forEach(p -> INSTANCE.sendTo(update, p));
+    }
+    
+    public static void sendCustomInventory(EntityPlayerMP p, int id, InventoryBase inv)
+    {
+        INSTANCE.sendTo(new CustomInventory(id, inv), p);
+    }
+    
+    public static void sendCampFireInventory(EntityPlayerMP p, int id, String name)
+    {
+        INSTANCE.sendTo(new CampFireInventory(id, name), p);
+    }
 }

+ 29 - 1
src/main/java/me/km/permissions/PermissionManager.java

@@ -91,6 +91,22 @@ public class PermissionManager extends Module
         return cs instanceof MinecraftServer;
     }
     
+    public boolean has(ICommandSender cs, Object perm)
+    {
+        if(perm == null)
+        {
+            return false;
+        }
+        else if(perm.getClass() == Permissions.class)
+        {
+            return has(cs, (Permissions) perm);
+        }
+        else
+        {
+            return has(cs, perm.toString());
+        }
+    }
+    
     // -----------------------------------------------------------------------------------
     // Permission-Registry
     // -----------------------------------------------------------------------------------
@@ -118,7 +134,7 @@ public class PermissionManager extends Module
         {
             throw new IllegalArgumentException("empty permission string");
         }
-        else if(Character.isUpperCase(perm.charAt(0)))
+        else if(isUppercase(perm))
         {
             // guess it's an enum ...
             Permissions permission = Permissions.valueOf(perm);
@@ -147,4 +163,16 @@ public class PermissionManager extends Module
             stringGroupPerms.get(id).add(perm);
         }
     }
+    
+    private boolean isUppercase(String s)
+    {
+        for(char c : s.toCharArray())
+        {
+            if(Character.isLowerCase(c))
+            {
+                return false;
+            }
+        }
+        return true;
+    }
 }

+ 1 - 1
src/main/java/me/km/permissions/Permissions.java

@@ -21,7 +21,7 @@ public enum Permissions
     AFK, AFK_OTHER,
     
     // Data-Tools
-    DATATOOLS, VILLAGER,
+    DATATOOLS, VILLAGER, HUMAN,
     
     // Effects
     WAND,

+ 2 - 2
src/main/java/me/km/skills/ActiveSkillContainer.java

@@ -11,7 +11,7 @@ public class ActiveSkillContainer extends SkillContainer
 { 
     public ActiveSkillContainer(String title, EntityPlayerMP p, List<ItemStack> stacks, List<Map.Entry<Skill, Byte>> list) 
     {
-        super(title, p, stacks, list);
+        super(title, p, p, stacks, list);
     }
 
     @Override
@@ -26,7 +26,7 @@ public class ActiveSkillContainer extends SkillContainer
         if(s.getEffect().getEffectInstance().run(p, level, s.getMana(level), s.getCooldown(level), EffectCause.SKILL))
         {
             KajetansMod.skills.sendToPlayers(p.world, p.posX, p.posY, p.posZ, 20, "§3" + p.getName() + "§r hat §3" + s.getName() + "§r benutzt.");
-            this.closeSafe(p);
+            this.closeSafe();
         }
     }
 }

+ 32 - 0
src/main/java/me/km/skills/LeveledSkill.java

@@ -0,0 +1,32 @@
+package me.km.skills;
+
+import me.km.effects.EffectCause;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.ItemStack;
+
+public class LeveledSkill 
+{
+    private byte level;
+    private Skill skill;
+    
+    public LeveledSkill(Skill skill, byte level)
+    {
+        this.level = level;
+        this.skill = skill;
+    }
+    
+    public ItemStack getItemStack()
+    {
+        return skill.getItemStack(level);
+    }
+    
+    public boolean useEffect(EntityPlayerMP p)
+    {
+        return skill.getEffect().getEffectInstance().run(p, level, skill.getMana(level), skill.getCooldown(level), EffectCause.SKILL);
+    }
+    
+    public int getId()
+    {
+        return skill.getId();
+    }
+}

+ 33 - 1
src/main/java/me/km/skills/Skill.java

@@ -8,6 +8,8 @@ import net.minecraft.item.ItemStack;
 
 public class Skill 
 { 
+    private final int id;
+    
     private final ItemStack stack;
     private final Effect effect;
     private final String name;
@@ -17,9 +19,10 @@ public class Skill
     private final int cooldownConstant;
     private final int cooldownFactor;
     
-    public Skill(ItemStack stack, Effect eff, String name, String explanation, 
+    public Skill(int id, ItemStack stack, Effect eff, String name, String explanation, 
             int manaConstant, int manaFactor, int cooldownConstant, int cooldownFactor)
     {
+        this.id = id;
         ItemStackUtils.addItemFlag(stack, ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_ENCHANTS, ItemFlag.HIDE_POTION_EFFECTS, ItemFlag.HIDE_UNBREAKABLE);
         stack.setStackDisplayName("§6" + name);
         ItemStackUtils.setLore(stack, ItemStackBuilder.buildLimitedLore(explanation, 25, "§7"));
@@ -67,4 +70,33 @@ public class Skill
     {
         return name;
     }
+
+    public int getId() 
+    {
+        return id;
+    }
+
+    @Override
+    public int hashCode() 
+    {
+        return id;
+    }
+
+    @Override
+    public boolean equals(Object o) 
+    {
+        if(this == o) 
+        {
+            return true;
+        }
+        if (o == null) 
+        {
+            return false;
+        }
+        if(Skill.class != o.getClass()) 
+        {
+            return false;
+        }
+        return this.id == ((Skill) o).id;
+    }
 }

+ 5 - 9
src/main/java/me/km/skills/SkillContainer.java

@@ -4,7 +4,8 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import me.km.inventory.CustomContainer;
-import me.km.inventory.EntityInventory;
+import me.km.inventory.InventoryBase;
+import net.minecraft.entity.player.EntityPlayer;
 import net.minecraft.entity.player.EntityPlayerMP;
 import net.minecraft.init.Items;
 import net.minecraft.inventory.ClickType;
@@ -17,13 +18,10 @@ public class SkillContainer extends CustomContainer
     private final ArrayList<List<ItemStack>> pages;
     private int page;
     
-    public SkillContainer(String title, EntityPlayerMP p, List<ItemStack> stacks, List<Map.Entry<Skill, Byte>> list) 
+    public SkillContainer(String title, EntityPlayer owner, EntityPlayerMP viewer, List<ItemStack> stacks, List<Map.Entry<Skill, Byte>> list) 
     {
-        super(title, stacks.size() % 9 != 0 ? ((stacks.size() / 9) + 1) + 9 : stacks.size(), p);
+        super(new InventoryBase(title, stacks.size(), owner), viewer);
         
-        //System.out.println("ANZAHL DER SKILLS: " + stacks.size());
-        
-        EntityInventory inv = (EntityInventory) super.getShownInventory();
         if(inv.getSizeInventory() > 45)
         {
             inv.setContents(stacks.subList(0, 45));
@@ -102,7 +100,7 @@ public class SkillContainer extends CustomContainer
     }
     
     @Override
-    public void onCanceledClick(int slot, int dragType, ClickType clickTypeIn, EntityPlayerMP p) 
+    public void onButtonClick(int slot, int dragType, ClickType clickTypeIn, EntityPlayerMP p) 
     {
         if(slot >= 45)
         {
@@ -113,7 +111,6 @@ public class SkillContainer extends CustomContainer
                 {
                     return;
                 }
-                EntityInventory inv = (EntityInventory) getShownInventory();
                 inv.setContents(list);
                 placeButtons(inv);
             }
@@ -124,7 +121,6 @@ public class SkillContainer extends CustomContainer
                 {
                     return;
                 }
-                EntityInventory inv = (EntityInventory) getShownInventory();
                 inv.setContents(list);
                 placeButtons(inv);
             }

+ 1 - 1
src/main/java/me/km/skills/SkillManager.java

@@ -19,7 +19,7 @@ public class SkillManager extends Module
     public void registerSkill(int id, ItemStack icon, Effect eff, String name, String explanation,
             int manaConstant, int manaFactor, int cooldownConstant, int cooldownFactor)
     {
-        Skill skill = new Skill(icon.copy(), eff, name, explanation, manaConstant, manaFactor, cooldownConstant, cooldownFactor);
+        Skill skill = new Skill(id, icon.copy(), eff, name, explanation, manaConstant, manaFactor, cooldownConstant, cooldownFactor);
         if(skills.put(id, skill) == null)
         {
             eff.addSkill(skill);

+ 7 - 6
src/main/java/me/km/skills/SkillMenuUtilities.java

@@ -11,23 +11,24 @@ public class SkillMenuUtilities
 {
     public static void openActiveSkills(EntityPlayerMP p)
     {
-        List<Map.Entry<Skill, Byte>> list = KajetansMod.jobs.getSkillMap(p).entrySet().stream().filter(e -> e.getKey().isActive()).collect(Collectors.toList());
+        List<Map.Entry<Skill, Byte>> list = KajetansMod.jobs.getSkillMap(p).entrySet()
+                .stream().filter(e -> e.getKey().isActive()).collect(Collectors.toList());
         List<ItemStack> stacks = list.stream().map(e -> e.getKey().getItemStack(e.getValue())).collect(Collectors.toList());
         ActiveSkillContainer active = new ActiveSkillContainer("Aktive Skills", p, stacks, list);
-        active.openForPlayer(p);
+        active.openForPlayer();
     }
     
     public static void openSkills(EntityPlayerMP p, EntityPlayerMP show)
     {
         List<ItemStack> stacks = KajetansMod.jobs.getSkillMap(p).entrySet().stream().map(e -> e.getKey().getItemStack(e.getValue())).collect(Collectors.toList());
-        SkillContainer skills = new SkillContainer("Skills", p, stacks, null);
-        skills.openForPlayer(show);
+        SkillContainer skills = new SkillContainer("Skills", p, show, stacks, null);
+        skills.openForPlayer();
     }
     
     public static void openRecipes(EntityPlayerMP p, EntityPlayerMP show)
     {
         List<ItemStack> stacks = KajetansMod.jobs.getRecipes(p).stream().map(m -> new ItemStack(m)).collect(Collectors.toList());
-        SkillContainer skills = new SkillContainer("Crafting-Rezepte", p, stacks, null);
-        skills.openForPlayer(show);
+        SkillContainer skills = new SkillContainer("Crafting-Rezepte", p, show, stacks, null);
+        skills.openForPlayer();
     }
 }

+ 94 - 5
src/main/java/me/km/snuviscript/MinecraftFunctions.java

@@ -79,6 +79,7 @@ import me.km.effects.Effect;
 import me.km.inventory.CustomContainer;
 import me.km.networking.ModPacketHandler;
 import me.km.pathfinder.PathfinderUtils;
+import me.km.skills.LeveledSkill;
 import net.minecraft.entity.item.EntityItem;
 import net.minecraft.util.math.MathHelper;
 import net.minecraft.util.text.ITextComponent;
@@ -109,6 +110,10 @@ public class MinecraftFunctions implements ISnuviLogger
                 KajetansMod.perms.registerGroupPermission(ScriptUtils.getInt(args[0]), args[1].toString()));
         parser.registerConsumer("perm.registerplayer", (args, qd) -> 
                 KajetansMod.perms.registerPlayerGroup(UUID.fromString(args[0].toString()), ScriptUtils.getInt(args[1])));
+        parser.registerFunction("perm.get", (args, qd) -> 
+                Permissions.valueOf(args[0].toString()));
+        parser.registerFunction("perm.has", (args, qd) -> 
+                KajetansMod.perms.has((ICommandSender) args[0], args[1]));
         
         // -------------------------------------------------------------
         // Title-Bibliothek  
@@ -370,6 +375,14 @@ public class MinecraftFunctions implements ISnuviLogger
                 registerSkill(args));              
         parser.registerConsumer("skill.clear", (args, qd) -> 
                 KajetansMod.skills.clearSkills());
+        parser.registerConsumer("skill.getactive", (args, qd) -> 
+                qd.setVar(args[0].toString(), KajetansMod.jobs.getActiveSkillMap((EntityPlayer) args[1]))); 
+        parser.registerFunction("skill.getid", (args, qd) -> 
+                ((LeveledSkill) args[0]).getId());
+        parser.registerFunction("skill.getstack", (args, qd) -> 
+                ((LeveledSkill) args[0]).getItemStack());
+        parser.registerFunction("skill.use", (args, qd) -> 
+                ((LeveledSkill) args[0]).useEffect((EntityPlayerMP) args[1]));
         
         // -------------------------------------------------------------    
         // Job-Bibliothek 
@@ -419,7 +432,9 @@ public class MinecraftFunctions implements ISnuviLogger
         parser.registerFunction("entity.getlocation", (args, qd) -> 
                 new Location(((Entity) args[0])));   
         parser.registerConsumer("entity.damage", (args, qd) -> 
-                damageEntity(args));  
+                damageEntity(args)); 
+        parser.registerConsumer("entity.getdamagesource", (args, qd) -> 
+                getDamageSource(args));
         parser.registerFunction("entity.gethealth", (args, qd) -> 
                 Fraction.fromDouble(((EntityLivingBase) args[0]).getHealth()));     
         parser.registerConsumer("entity.sethealth", (args, qd) -> 
@@ -635,7 +650,7 @@ public class MinecraftFunctions implements ISnuviLogger
         parser.registerFunction("inv.getitem", (args, qd) -> 
                 ((IInventory) args[0]).getStackInSlot(ScriptUtils.getInt(args[1])));
         parser.registerConsumer("inv.open", (args, qd) -> 
-                new ScriptInventoryHolder((SnuviInventory) args[0], (EntityPlayerMP) args[1], (MinecraftScript) qd).openForPlayer((EntityPlayerMP) args[1]));
+                new ScriptInventoryHolder((SnuviInventory) args[0], (EntityPlayerMP) args[1], (MinecraftScript) qd).openForPlayer());
         parser.registerConsumer("inv.close", (args, qd) -> 
                 invClose(args));                 
 
@@ -1032,7 +1047,7 @@ public class MinecraftFunctions implements ISnuviLogger
         EntityPlayer p = (EntityPlayer) args[0];
         if(p.openContainer instanceof CustomContainer)
         {
-            ((CustomContainer) p.openContainer).closeSafe(p);
+            ((CustomContainer) p.openContainer).closeSafe();
         }
         else
         {
@@ -1162,14 +1177,88 @@ public class MinecraftFunctions implements ISnuviLogger
 
     private static void damageEntity(Object[] args)
     {
-        if(args.length >= 4)
+        // entity.damage(entity, damagefloat, DamageSource);
+        if(args.length >= 3)
         {
-            ((EntityLivingBase) args[0]).attackEntityFrom(DamageSource.causeMobDamage((EntityLivingBase) args[2]), ScriptUtils.getFloat(args[1]));
+            ((EntityLivingBase) args[0]).attackEntityFrom((DamageSource) args[2], ScriptUtils.getFloat(args[1]));
             return;
         }
         ((EntityLivingBase) args[0]).attackEntityFrom(DamageSource.GENERIC, ScriptUtils.getFloat(args[1]));
     }
     
+    private static DamageSource getDamageSource(Object[] args)
+    {
+        if(args[0] instanceof EntityLivingBase)
+        {
+            EntityLivingBase ent = (EntityLivingBase) args[0];
+            DamageSource ds;
+            if(ent instanceof EntityPlayer)
+            {
+                ds = DamageSource.causePlayerDamage((EntityPlayer) ent);
+            }
+            else
+            {
+                ds = DamageSource.causeMobDamage(ent);
+            }
+            
+            if((boolean) args[1])
+            {
+                ds.setDamageAllowedInCreativeMode();
+            }
+            if((boolean) args[2])
+            {
+                ds.setDamageBypassesArmor();
+            }
+            if((boolean) args[3])
+            {
+                ds.setDamageIsAbsolute();
+            }
+            if((boolean) args[4])
+            {
+                ds.setExplosion();
+            }
+            if((boolean) args[5])
+            {
+                ds.setFireDamage();
+            }
+            if((boolean) args[6])
+            {
+                ds.setMagicDamage();
+            }
+            if((boolean) args[7])
+            {
+               ds.setProjectile();
+            } 
+            return ds;
+        }
+        
+        switch(args[0].toString())
+        {
+            case "IN_FIRE": return DamageSource.IN_FIRE;
+            case "LIGHTNING_BOLT": return DamageSource.LIGHTNING_BOLT;
+            case "ON_FIRE": return DamageSource.ON_FIRE;
+            case "LAVA": return DamageSource.LAVA;
+            case "HOT_FLOOR": return DamageSource.HOT_FLOOR;
+            case "IN_WALL": return DamageSource.IN_WALL;
+            case "CRAMMING": return DamageSource.CRAMMING;
+            case "DROWN": return DamageSource.DROWN;
+            case "STARVE": return DamageSource.STARVE;
+            case "CACTUS": return DamageSource.CACTUS;
+            case "FALL": return DamageSource.FALL;
+            case "FLY_INTO_WALL": return DamageSource.FLY_INTO_WALL;
+            case "OUT_OF_WORLD": return DamageSource.OUT_OF_WORLD;
+            case "GENERIC": return DamageSource.GENERIC;
+            case "MAGIC": return DamageSource.MAGIC;
+            case "WITHER": return DamageSource.WITHER;
+            case "ANVIL": return DamageSource.ANVIL;
+            case "FALLING_BLOCK": return DamageSource.FALLING_BLOCK;
+            case "DRAGON_BREATH": return DamageSource.DRAGON_BREATH;
+            case "FIREWORKS": return DamageSource.FIREWORKS;
+            case "THORNS": return DamageSource.causeThornsDamage((Entity) args[1]);
+        }
+        return DamageSource.GENERIC;
+    }
+    
     private static boolean doesIntersect(Object[] args)
     {
         Location l1 = (Location) args[0];

+ 33 - 27
src/main/java/me/km/snuviscript/ScriptEvents.java

@@ -19,6 +19,7 @@ import me.km.api.Module;
 import me.km.api.Utils;
 import me.km.dimensions.ModDimensions;
 import me.km.effects.PlayerUsesEffectEvent;
+import me.km.entities.EntityHuman;
 import me.km.entities.EntityItemProjectile;
 import me.km.events.PlayerHurtEvent;
 import me.km.events.PlayerJoinMessageEvent;
@@ -103,7 +104,7 @@ public class ScriptEvents extends ModuleListener
     // -------------------------------------------------------------------------
     
     @SubscribeEvent
-    public void joinOtherPlayersQuests(PlayerInteractEvent.EntityInteract e)
+    public void onOtherScriptJoin(PlayerInteractEvent.EntityInteract e)
     {              
         if(e.getHand() == EnumHand.OFF_HAND || !(e.getTarget() instanceof EntityPlayer))
         {
@@ -154,7 +155,7 @@ public class ScriptEvents extends ModuleListener
         }
     } 
     
-    public boolean QuestClickInventory(Script qd, SnuviInventory inv, int slot, ClickType click, EntityPlayer p)
+    public boolean onInventoryClick(Script qd, SnuviInventory inv, int slot, ClickType click, EntityPlayer p)
     {
         parser.callEvent("inv_click", qd, sc -> 
         {
@@ -168,7 +169,7 @@ public class ScriptEvents extends ModuleListener
         return qd.getBooleanVar("cancel");
     }
 
-    public void QuestCloseInventory(Script qd, SnuviInventory inv, EntityPlayer p)
+    public void onInventoryClose(Script qd, SnuviInventory inv, EntityPlayer p)
     {
         parser.callEvent("inv_close", qd, sc -> 
         {
@@ -178,8 +179,13 @@ public class ScriptEvents extends ModuleListener
         }, null);
     }
     
+    public void onHumanHurt(EntityPlayer p, EntityHuman h)
+    {
+        handleEvent(p, "human_hurt", sc -> ScriptVars.setEntityVars(sc, h));
+    }
+    
     @SubscribeEvent
-    public void QuestOnPlayerRespawn(PlayerRespawnAtEvent e)
+    public void onPlayerRespawn(PlayerRespawnAtEvent e)
     {
         handleEvent(e.getEntityPlayer(), "player_respawn", (qd) -> 
         {
@@ -202,7 +208,7 @@ public class ScriptEvents extends ModuleListener
     }
     
     @SubscribeEvent
-    public void QuestPlayerDamage(PlayerHurtEvent e)
+    public void onPlayerDamage(PlayerHurtEvent e)
     {        
         EntityPlayer p = e.getEntityPlayer();  
         handleEvent(p, "player_hurt", (qd) -> 
@@ -235,7 +241,7 @@ public class ScriptEvents extends ModuleListener
     } 
     
     @SubscribeEvent
-    public void QuestDeath(LivingDeathEvent e)
+    public void onLivingDeath(LivingDeathEvent e)
     {        
         if(!(e.getEntityLiving() instanceof EntityPlayer))
         {
@@ -281,7 +287,7 @@ public class ScriptEvents extends ModuleListener
     } 
        
     @SubscribeEvent
-    public void QuestEntityDamage(LivingHurtEvent e)
+    public void onEntityDamage(LivingHurtEvent e)
     {        
         EntityPlayer p = Utils.getDamager(e.getSource());
         if(p == null)
@@ -302,7 +308,7 @@ public class ScriptEvents extends ModuleListener
     }
     
     @SubscribeEvent
-    public void QuestEntityDrop(LivingDropsEvent e)
+    public void onEntityDrop(LivingDropsEvent e)
     {             
         try
         {
@@ -333,7 +339,7 @@ public class ScriptEvents extends ModuleListener
     } 
             
     @SubscribeEvent
-    public void QuestPlayerDrop(PlayerDropsEvent e)
+    public void onPlayerDrop(PlayerDropsEvent e)
     {             
         handleEvent(e.getEntityPlayer(), "player_drop", (qd) -> 
         {
@@ -345,7 +351,7 @@ public class ScriptEvents extends ModuleListener
     } 
     
     @SubscribeEvent
-    public void QuestProjectileHit(ThrowableImpactEvent e)
+    public void onProjectileHit(ThrowableImpactEvent e)
     {        
         EntityPlayer p = Utils.getPlayerFromProjectile(e.getEntityThrowable());
         if(p != null)
@@ -377,7 +383,7 @@ public class ScriptEvents extends ModuleListener
     }
     
     @SubscribeEvent
-    public void QuestEntityShear(PlayerInteractEvent.EntityInteract e)
+    public void onEntityShear(PlayerInteractEvent.EntityInteract e)
     {              
         if(e.getHand() == EnumHand.OFF_HAND || !(e.getTarget() instanceof EntitySheep))
         {
@@ -403,7 +409,7 @@ public class ScriptEvents extends ModuleListener
     }
        
     @SubscribeEvent
-    public void QuestBlockBreak(BlockEvent.BreakEvent e)
+    public void onBlockBreak(BlockEvent.BreakEvent e)
     {        
         handleEvent(e.getPlayer(), "block_break", (qd) -> 
         {
@@ -416,7 +422,7 @@ public class ScriptEvents extends ModuleListener
     }
     
     @SubscribeEvent
-    public void QuestPlayerLogin(PlayerEvent.PlayerLoggedInEvent e)
+    public void onPlayerLogin(PlayerEvent.PlayerLoggedInEvent e)
     {    
         EntityPlayer p = e.player;
         if(p == null)
@@ -433,7 +439,7 @@ public class ScriptEvents extends ModuleListener
     }
     
     @SubscribeEvent
-    public void QuestPlayerJoin(PlayerJoinMessageEvent e)
+    public void onPlayerJoin(PlayerJoinMessageEvent e)
     {    
         EntityPlayer p = e.getEntityPlayer();
         handleEvent(p, "player_join_server", (qd) -> 
@@ -464,7 +470,7 @@ public class ScriptEvents extends ModuleListener
     }
     
     @SubscribeEvent
-    public void QuestBucketFill(FillBucketEvent e)
+    public void onBucketFill(FillBucketEvent e)
     {      
         handleEvent(e.getEntityPlayer(), "bucket_fill", (qd) -> 
         {
@@ -494,7 +500,7 @@ public class ScriptEvents extends ModuleListener
     } 
        
     @SubscribeEvent
-    public void QuestBlockPlace(BlockEvent.PlaceEvent e)
+    public void onBlockPlace(BlockEvent.PlaceEvent e)
     {
         handleEvent(e.getPlayer(), "block_place", (qd) -> 
         {
@@ -508,7 +514,7 @@ public class ScriptEvents extends ModuleListener
     }    
     
     @SubscribeEvent
-    public void QuestClickBlockRight(PlayerInteractEvent.RightClickBlock e)
+    public void onRightClickBlock(PlayerInteractEvent.RightClickBlock e)
     {
         if(e.getHand() == EnumHand.OFF_HAND)
         {
@@ -526,7 +532,7 @@ public class ScriptEvents extends ModuleListener
     }
     
     @SubscribeEvent
-    public void QuestClickBlockLeft(PlayerInteractEvent.LeftClickBlock e)
+    public void onLeftClickBlock(PlayerInteractEvent.LeftClickBlock e)
     {
         if(e.getHand() == EnumHand.OFF_HAND)
         {
@@ -544,7 +550,7 @@ public class ScriptEvents extends ModuleListener
     }
     
     @SubscribeEvent
-    public void QuestClickEntity(PlayerInteractEvent.EntityInteract e)
+    public void onEntityClick(PlayerInteractEvent.EntityInteract e)
     {
         if(e.getHand() != EnumHand.OFF_HAND)
         {
@@ -560,7 +566,7 @@ public class ScriptEvents extends ModuleListener
     }
     
     @SubscribeEvent
-    public void QuestFishing(ItemFishedEvent e)
+    public void onFishing(ItemFishedEvent e)
     {
         handleEvent(e.getEntityPlayer(), "fishing", (qd) -> 
         {
@@ -620,7 +626,7 @@ public class ScriptEvents extends ModuleListener
     }
     
     @SubscribeEvent
-    public void QuestConsuming(LivingEntityUseItemEvent.Finish e)
+    public void onConsuming(LivingEntityUseItemEvent.Finish e)
     {
         if(!(e.getEntityLiving() instanceof EntityPlayer))
         {
@@ -633,7 +639,7 @@ public class ScriptEvents extends ModuleListener
     }
     
     @SubscribeEvent
-    public void QuestCrafting(PlayerEvent.ItemCraftedEvent e)
+    public void onCrafting(PlayerEvent.ItemCraftedEvent e)
     {  
         handleEvent(e.player, "craft", (qd) -> 
         {
@@ -642,7 +648,7 @@ public class ScriptEvents extends ModuleListener
     } 
     
     @SubscribeEvent
-    public void QuestDropItem(ItemTossEvent e)
+    public void onItemDrop(ItemTossEvent e)
     {
         handleEvent(e.getPlayer(), "player_toss", (qd) -> 
         {
@@ -655,7 +661,7 @@ public class ScriptEvents extends ModuleListener
     }
     
     @SubscribeEvent
-    public void QuestPickupItem(EntityItemPickupEvent e)
+    public void onItemPickup(EntityItemPickupEvent e)
     {
         handleEvent(e.getEntityPlayer(), "player_pickup", (qd) -> 
         {
@@ -668,7 +674,7 @@ public class ScriptEvents extends ModuleListener
     }
     
     @SubscribeEvent
-    public void VehicleEnterEvent(EntityMountEvent e)
+    public void onVehicleEnter(EntityMountEvent e)
     {
         if(!(e.getEntityMounting() instanceof EntityPlayer))
         {
@@ -687,7 +693,7 @@ public class ScriptEvents extends ModuleListener
     }
     
     @SubscribeEvent
-    public void PlayerPortalEvent(PlayerEvent.PlayerChangedDimensionEvent e)
+    public void onPlayerUsePortal(PlayerEvent.PlayerChangedDimensionEvent e)
     {
         handleEvent(e.player, "portal", (qd) -> 
         {
@@ -727,7 +733,7 @@ public class ScriptEvents extends ModuleListener
         });
     }
       
-    public void useEffectEvent(PlayerUsesEffectEvent e)
+    public void onEffectUse(PlayerUsesEffectEvent e)
     {    
         handleEvent(e.getPlayer(), "player_use_effect", (qd) -> 
         {       

+ 3 - 5
src/main/java/me/km/snuviscript/ScriptInventoryHolder.java

@@ -10,26 +10,24 @@ public class ScriptInventoryHolder extends CustomContainer
 {
     private final MinecraftScript qd;
     private final ScriptEvents e;
-    private final SnuviInventory inv;
 
     public ScriptInventoryHolder(SnuviInventory inv, EntityPlayerMP p, MinecraftScript qd) 
     {
         super(inv, p);
         this.qd = qd;
-        this.inv = inv;
         this.e = KajetansMod.scripts.getEvent(ScriptEvents.class);
     }
 
     @Override
-    public boolean noClicking(int slot, int dragType, ClickType click, EntityPlayerMP p) 
+    public void onButtonClick(int slot, int dragType, ClickType click, EntityPlayerMP p)
     {
-        return e.QuestClickInventory(qd, inv, slot, click, p);
+        e.onInventoryClick(qd, (SnuviInventory) inv, slot, click, p);
     }
 
     @Override
     public void onContainerClosed(EntityPlayer p) 
     {
-        e.QuestCloseInventory(qd, inv, p);
+        e.onInventoryClose(qd, (SnuviInventory) inv, p);
         super.onContainerClosed(p);
     }
 }

+ 3 - 3
src/main/java/me/km/snuviscript/SnuviInventory.java

@@ -1,14 +1,14 @@
 package me.km.snuviscript;
 
-import net.minecraft.inventory.InventoryBasic;
+import me.km.inventory.InventoryBase;
 
-public class SnuviInventory extends InventoryBasic
+public class SnuviInventory extends InventoryBase
 {
     private final int id;
     
     public SnuviInventory(String title, int slotCount, int id) 
     {
-        super(title, true, slotCount);
+        super(title, slotCount, null);
         this.id = id;
     }
     

+ 5 - 0
src/main/resources/assets/km/blockstates/camp_fire.json

@@ -0,0 +1,5 @@
+{
+    "variants": {
+        "normal": { "model": "km:camp_fire" }
+    }
+}

+ 5 - 0
src/main/resources/assets/km/blockstates/camp_fire_burning.json

@@ -0,0 +1,5 @@
+{
+    "variants": {
+        "normal": { "model": "km:camp_fire_burning" }
+    }
+}

+ 5 - 0
src/main/resources/assets/km/blockstates/camp_fire_burnt.json

@@ -0,0 +1,5 @@
+{
+    "variants": {
+        "normal": { "model": "km:camp_fire_burnt" }
+    }
+}

+ 94 - 0
src/main/resources/assets/km/models/block/camp_fire.json

@@ -0,0 +1,94 @@
+{
+    "__comment": "Model generated using MrCrayfish's Model Creator (http://mrcrayfish.com/modelcreator/)",
+    "textures": {
+        "0": "blocks/planks_oak",
+        "particle": "blocks/planks_oak"
+    },
+    "ambientocclusion": false,
+    "elements": [
+        {
+            "name": "Cube",
+            "from": [ 6.0, 6.0, 7.0 ], 
+            "to": [ 19.0, 8.0, 9.0 ], 
+            "rotation": { "origin": [ 8.0, 8.0, 8.0 ], "axis": "z", "angle": -45.0 },
+            "faces": {
+                "north": { "texture": "#0", "uv": [ 0.0, 0.0, 13.0, 2.0 ] },
+                "east": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 2.0 ] },
+                "south": { "texture": "#0", "uv": [ 0.0, 0.0, 13.0, 2.0 ] },
+                "west": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 2.0 ] },
+                "up": { "texture": "#0", "uv": [ 0.0, 0.0, 13.0, 2.0 ] },
+                "down": { "texture": "#0", "uv": [ 0.0, 0.0, 13.0, 2.0 ] }
+            }
+        },
+        {
+            "name": "Cube",
+            "from": [ -3.0, 6.0, 7.0 ], 
+            "to": [ 10.0, 8.0, 9.0 ], 
+            "rotation": { "origin": [ 8.0, 8.0, 8.0 ], "axis": "z", "angle": 45.0 },
+            "faces": {
+                "north": { "texture": "#0", "uv": [ 0.0, 0.0, 13.0, 2.0 ] },
+                "east": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 2.0 ] },
+                "south": { "texture": "#0", "uv": [ 0.0, 0.0, 13.0, 2.0 ] },
+                "west": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 2.0 ] },
+                "up": { "texture": "#0", "uv": [ 0.0, 0.0, 13.0, 2.0 ] },
+                "down": { "texture": "#0", "uv": [ 0.0, 0.0, 13.0, 2.0 ] }
+            }
+        },
+        {
+            "name": "Cube",
+            "from": [ 7.0, 6.0, -3.0 ], 
+            "to": [ 9.0, 8.0, 10.0 ], 
+            "rotation": { "origin": [ 8.0, 8.0, 8.0 ], "axis": "x", "angle": -45.0 },
+            "faces": {
+                "north": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 2.0 ] },
+                "east": { "texture": "#0", "uv": [ 0.0, 0.0, 13.0, 2.0 ] },
+                "south": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 2.0 ] },
+                "west": { "texture": "#0", "uv": [ 0.0, 0.0, 13.0, 2.0 ] },
+                "up": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 13.0 ] },
+                "down": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 13.0 ] }
+            }
+        },
+        {
+            "name": "Cube",
+            "from": [ 7.0, 6.0, 6.0 ], 
+            "to": [ 9.0, 8.0, 19.0 ], 
+            "rotation": { "origin": [ 8.0, 8.0, 8.0 ], "axis": "x", "angle": 45.0 },
+            "faces": {
+                "north": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 2.0 ] },
+                "east": { "texture": "#0", "uv": [ 0.0, 0.0, 13.0, 2.0 ] },
+                "south": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 2.0 ] },
+                "west": { "texture": "#0", "uv": [ 0.0, 0.0, 13.0, 2.0 ] },
+                "up": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 13.0 ] },
+                "down": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 13.0 ] }
+            }
+        },
+        {
+            "name": "Cube",
+            "from": [ 7.0, 0.0, 0.0 ], 
+            "to": [ 9.0, 2.0, 16.0 ], 
+            "rotation": { "origin": [ 8.0, 6.0, 8.0 ], "axis": "y", "angle": 45.0 },
+            "faces": {
+                "north": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 2.0 ] },
+                "east": { "texture": "#0", "uv": [ 0.0, 0.0, 16.0, 2.0 ] },
+                "south": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 2.0 ] },
+                "west": { "texture": "#0", "uv": [ 0.0, 0.0, 16.0, 2.0 ] },
+                "up": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 16.0 ] },
+                "down": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 16.0 ] }
+            }
+        },
+        {
+            "name": "Cube",
+            "from": [ 7.0, 0.0, 0.0 ], 
+            "to": [ 9.0, 2.0, 16.0 ], 
+            "rotation": { "origin": [ 8.0, 6.0, 8.0 ], "axis": "y", "angle": -45.0 },
+            "faces": {
+                "north": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 2.0 ] },
+                "east": { "texture": "#0", "uv": [ 0.0, 0.0, 16.0, 2.0 ] },
+                "south": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 2.0 ] },
+                "west": { "texture": "#0", "uv": [ 0.0, 0.0, 16.0, 2.0 ] },
+                "up": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 16.0 ] },
+                "down": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 16.0 ] }
+            }
+        }
+    ]
+}

+ 130 - 0
src/main/resources/assets/km/models/block/camp_fire_burning.json

@@ -0,0 +1,130 @@
+{
+    "__comment": "Model generated using MrCrayfish's Model Creator (http://mrcrayfish.com/modelcreator/)",
+    "textures": {
+        "0": "blocks/planks_oak",
+        "particle": "blocks/planks_oak",
+        "fire": "blocks/fire_layer_0"
+    },
+    "ambientocclusion": false,
+    "elements": [
+        {
+            "name": "Cube",
+            "from": [ 6.0, 6.0, 7.0 ], 
+            "to": [ 19.0, 8.0, 9.0 ], 
+            "rotation": { "origin": [ 8.0, 8.0, 8.0 ], "axis": "z", "angle": -45.0 },
+            "faces": {
+                "north": { "texture": "#0", "uv": [ 0.0, 0.0, 13.0, 2.0 ] },
+                "east": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 2.0 ] },
+                "south": { "texture": "#0", "uv": [ 0.0, 0.0, 13.0, 2.0 ] },
+                "west": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 2.0 ] },
+                "up": { "texture": "#0", "uv": [ 0.0, 0.0, 13.0, 2.0 ] },
+                "down": { "texture": "#0", "uv": [ 0.0, 0.0, 13.0, 2.0 ] }
+            }
+        },
+        {
+            "name": "Cube",
+            "from": [ -3.0, 6.0, 7.0 ], 
+            "to": [ 10.0, 8.0, 9.0 ], 
+            "rotation": { "origin": [ 8.0, 8.0, 8.0 ], "axis": "z", "angle": 45.0 },
+            "faces": {
+                "north": { "texture": "#0", "uv": [ 0.0, 0.0, 13.0, 2.0 ] },
+                "east": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 2.0 ] },
+                "south": { "texture": "#0", "uv": [ 0.0, 0.0, 13.0, 2.0 ] },
+                "west": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 2.0 ] },
+                "up": { "texture": "#0", "uv": [ 0.0, 0.0, 13.0, 2.0 ] },
+                "down": { "texture": "#0", "uv": [ 0.0, 0.0, 13.0, 2.0 ] }
+            }
+        },
+        {
+            "name": "Cube",
+            "from": [ 7.0, 6.0, -3.0 ], 
+            "to": [ 9.0, 8.0, 10.0 ], 
+            "rotation": { "origin": [ 8.0, 8.0, 8.0 ], "axis": "x", "angle": -45.0 },
+            "faces": {
+                "north": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 2.0 ] },
+                "east": { "texture": "#0", "uv": [ 0.0, 0.0, 13.0, 2.0 ] },
+                "south": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 2.0 ] },
+                "west": { "texture": "#0", "uv": [ 0.0, 0.0, 13.0, 2.0 ] },
+                "up": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 13.0 ] },
+                "down": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 13.0 ] }
+            }
+        },
+        {
+            "name": "Cube",
+            "from": [ 7.0, 6.0, 6.0 ], 
+            "to": [ 9.0, 8.0, 19.0 ], 
+            "rotation": { "origin": [ 8.0, 8.0, 8.0 ], "axis": "x", "angle": 45.0 },
+            "faces": {
+                "north": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 2.0 ] },
+                "east": { "texture": "#0", "uv": [ 0.0, 0.0, 13.0, 2.0 ] },
+                "south": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 2.0 ] },
+                "west": { "texture": "#0", "uv": [ 0.0, 0.0, 13.0, 2.0 ] },
+                "up": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 13.0 ] },
+                "down": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 13.0 ] }
+            }
+        },
+        {
+            "name": "Cube",
+            "from": [ 7.0, 0.0, 0.0 ], 
+            "to": [ 9.0, 2.0, 16.0 ], 
+            "rotation": { "origin": [ 8.0, 6.0, 8.0 ], "axis": "y", "angle": 45.0 },
+            "faces": {
+                "north": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 2.0 ] },
+                "east": { "texture": "#0", "uv": [ 0.0, 0.0, 16.0, 2.0 ] },
+                "south": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 2.0 ] },
+                "west": { "texture": "#0", "uv": [ 0.0, 0.0, 16.0, 2.0 ] },
+                "up": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 16.0 ] },
+                "down": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 16.0 ] }
+            }
+        },
+        {
+            "name": "Cube",
+            "from": [ 7.0, 0.0, 0.0 ], 
+            "to": [ 9.0, 2.0, 16.0 ], 
+            "rotation": { "origin": [ 8.0, 6.0, 8.0 ], "axis": "y", "angle": -45.0 },
+            "faces": {
+                "north": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 2.0 ] },
+                "east": { "texture": "#0", "uv": [ 0.0, 0.0, 16.0, 2.0 ] },
+                "south": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 2.0 ] },
+                "west": { "texture": "#0", "uv": [ 0.0, 0.0, 16.0, 2.0 ] },
+                "up": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 16.0 ] },
+                "down": { "texture": "#0", "uv": [ 0.0, 0.0, 2.0, 16.0 ] }
+            }
+        },
+
+        {   "from": [ 2.8, 1.5, 8 ],
+            "to": [ 13.2, 17.5, 8 ],
+            "rotation": { "origin": [ 8, 8, 8 ], "axis": "y", "angle": 45, "rescale": true },
+            "shade": false,
+            "faces": {
+                "north": { "uv": [ 0, 0, 16, 16 ], "texture": "#fire"},
+                "south": { "uv": [ 0, 0, 16, 16 ], "texture": "#fire"}
+            }
+        },
+        {   "from": [ 2.8, 1.5, 8 ],
+            "to": [ 13.2, 17.5, 8 ],
+            "shade": false,
+            "faces": {
+                "north": { "uv": [ 0, 0, 16, 16 ], "texture": "#fire"},
+                "south": { "uv": [ 0, 0, 16, 16 ], "texture": "#fire"}
+            }
+        },
+        {   "from": [ 8, 1.5, 2.8 ],
+            "to": [ 8, 17.5, 13.2 ],
+            "shade": false,
+            "faces": {
+                "west": { "uv": [ 0, 0, 16, 16 ], "texture": "#fire"},
+                "east": { "uv": [ 0, 0, 16, 16 ], "texture": "#fire"}
+            }
+        },
+        {   "from": [ 8, 1.5, 2.8 ],
+            "to": [ 8, 17.5, 13.2 ],
+            "rotation": { "origin": [ 8, 8, 8 ], "axis": "y", "angle": 45, "rescale": true },
+            "shade": false,
+            "faces": {
+                "west": { "uv": [ 0, 0, 16, 16 ], "texture": "#fire"},
+                "east": { "uv": [ 0, 0, 16, 16 ], "texture": "#fire"}
+            }
+        }
+    ]
+}

+ 68 - 0
src/main/resources/assets/km/models/block/camp_fire_burnt.json

@@ -0,0 +1,68 @@
+{
+    "__comment": "Model generated using MrCrayfish's Model Creator (http://mrcrayfish.com/modelcreator/)",
+    "textures": {
+        "0": "km:blocks/campfire/burnt_oak_tip",
+        "1": "km:blocks/campfire/burnt_plank_oak",
+        "2": "blocks/planks_oak",
+        "fire": "blocks/fire_layer_0",
+        "particle": "blocks/planks_oak"
+    },
+    "elements": [
+        {
+            "name": "Cube",
+            "from": [ -3.0, 6.0, 7.0 ], 
+            "to": [ 10.0, 8.0, 9.0 ], 
+            "rotation": { "origin": [ 8.0, 8.0, 8.0 ], "axis": "z", "angle": 45.0 },
+            "faces": {
+                "north": { "texture": "#0", "uv": [ 3.0, 7.0, 16.0, 9.0 ] },
+                "east": { "texture": "#1", "uv": [ 7.0, 6.0, 9.0, 8.0 ] },
+                "south": { "texture": "#0", "uv": [ 0.0, 12.0, 13.0, 14.0 ], "rotation": 180 },
+                "west": { "texture": "#2", "uv": [ 0.0, 0.0, 2.0, 2.0 ] },
+                "up": { "texture": "#0", "uv": [ 0.0, 6.0, 13.0, 8.0 ], "rotation": 180 },
+                "down": { "texture": "#0", "uv": [ 0.0, 5.0, 6.0, 7.0 ], "rotation": 180 }
+            }
+        },
+        {
+            "name": "Cube",
+            "from": [ 7.0, 3.0, -3.0 ], 
+            "to": [ 9.0, 5.0, 10.0 ], 
+            "rotation": { "origin": [ 8.0, 8.0, 8.0 ], "axis": "x", "angle": -22.5 },
+            "faces": {
+                "north": { "texture": "#2", "uv": [ 1.0, 1.0, 3.0, 3.0 ] },
+                "east": { "texture": "#0", "uv": [ 0.0, 6.0, 13.0, 8.0 ] },
+                "south": { "texture": "#0", "uv": [ 0.0, 5.0, 2.0, 7.0 ] },
+                "west": { "texture": "#0", "uv": [ 0.0, 5.0, 13.0, 7.0 ], "rotation": 180 },
+                "up": { "texture": "#0", "uv": [ 5.0, 3.0, 7.0, 16.0 ], "rotation": 180 },
+                "down": { "texture": "#0", "uv": [ 3.0, 3.0, 5.0, 16.0 ] }
+            }
+        },
+        {
+            "name": "Cube",
+            "from": [ 7.0, 0.0, 0.0 ], 
+            "to": [ 9.0, 2.0, 16.0 ], 
+            "rotation": { "origin": [ 8.0, 6.0, 8.0 ], "axis": "y", "angle": 45.0 },
+            "faces": {
+                "north": { "texture": "#2", "uv": [ 0.0, 0.0, 2.0, 2.0 ] },
+                "east": { "texture": "#1", "uv": [ 0.0, 12.0, 16.0, 14.0 ] },
+                "south": { "texture": "#2", "uv": [ 0.0, 0.0, 2.0, 2.0 ] },
+                "west": { "texture": "#1", "uv": [ 0.0, 11.0, 16.0, 13.0 ] },
+                "up": { "texture": "#1", "uv": [ 7.0, 0.0, 9.0, 16.0 ] },
+                "down": { "texture": "#1", "uv": [ 12.0, 0.0, 14.0, 16.0 ] }
+            }
+        },
+        {
+            "name": "Cube",
+            "from": [ 7.0, 0.0, 0.0 ], 
+            "to": [ 9.0, 2.0, 16.0 ], 
+            "rotation": { "origin": [ 8.0, 6.0, 8.0 ], "axis": "y", "angle": -45.0 },
+            "faces": {
+                "north": { "texture": "#2", "uv": [ 0.0, 0.0, 2.0, 2.0 ] },
+                "east": { "texture": "#1", "uv": [ 0.0, 12.0, 16.0, 14.0 ] },
+                "south": { "texture": "#2", "uv": [ 0.0, 0.0, 2.0, 2.0 ] },
+                "west": { "texture": "#1", "uv": [ 0.0, 11.0, 16.0, 13.0 ] },
+                "up": { "texture": "#1", "uv": [ 7.0, 0.0, 9.0, 16.0 ] },
+                "down": { "texture": "#1", "uv": [ 3.0, 0.0, 5.0, 16.0 ] }
+            }
+        }
+    ]
+}

BIN
src/main/resources/assets/km/textures/blocks/campfire/burnt_oak_tip.png


BIN
src/main/resources/assets/km/textures/blocks/campfire/burnt_plank_oak.png


BIN
src/main/resources/assets/km/textures/blocks/campfire/planks_oak.png


BIN
src/main/resources/assets/km/textures/gui/container/campfire.png


BIN
src/main/resources/assets/km/textures/gui/container/empty_tile.png