소스 검색

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