Browse Source

Nahezu vollständige Portierung von Snuviscript

Kajetan Johannes Hammerle 7 years ago
parent
commit
23da01780f
50 changed files with 6450 additions and 168 deletions
  1. 9 19
      src/main/java/me/km/KajetansMod.java
  2. 18 1
      src/main/java/me/km/api/Location.java
  3. 52 2
      src/main/java/me/km/api/SimpleConfig.java
  4. 2 3
      src/main/java/me/km/api/Utils.java
  5. 6 5
      src/main/java/me/km/blockprotections/SemiProtections.java
  6. 0 1
      src/main/java/me/km/commands/CommandBan.java
  7. 6 6
      src/main/java/me/km/commands/CommandExp.java
  8. 1 3
      src/main/java/me/km/commands/CommandFeed.java
  9. 1 4
      src/main/java/me/km/commands/CommandHeal.java
  10. 10 7
      src/main/java/me/km/commands/CommandTpPos.java
  11. 10 7
      src/main/java/me/km/commands/CommandUser.java
  12. 183 0
      src/main/java/me/km/dimensions/ChangeWorldEvent.java
  13. 8 2
      src/main/java/me/km/inventory/CustomContainer.java
  14. 27 0
      src/main/java/me/km/inventory/InventoryUtils.java
  15. 5 0
      src/main/java/me/km/items/ModItems.java
  16. 7 1
      src/main/java/me/km/permissions/Permissions.java
  17. 28 0
      src/main/java/me/km/playerbank/GodMode.java
  18. 0 1
      src/main/java/me/km/playerbank/PlayerBank.java
  19. 81 0
      src/main/java/me/km/playerbank/PlayerLogInOut.java
  20. 13 0
      src/main/java/me/km/playerbank/PlayerManager.java
  21. 45 0
      src/main/java/me/km/playerbank/PvpEvent.java
  22. 9 21
      src/main/java/me/km/plots/ProtectionCommand.java
  23. 10 24
      src/main/java/me/km/plots/ProtectionExplosion.java
  24. 0 48
      src/main/java/me/km/plots/ProtectionFire.java
  25. 76 0
      src/main/java/me/km/scrolls/CommandScroll.java
  26. 72 0
      src/main/java/me/km/scrolls/ItemScroll.java
  27. 46 0
      src/main/java/me/km/snuviscript/BenchmarkClock.java
  28. 126 0
      src/main/java/me/km/snuviscript/CodeFunction.java
  29. 55 0
      src/main/java/me/km/snuviscript/CommandGiveUp.java
  30. 254 0
      src/main/java/me/km/snuviscript/CommandQuest.java
  31. 40 0
      src/main/java/me/km/snuviscript/CommandQuestInfo.java
  32. 255 0
      src/main/java/me/km/snuviscript/CommandScript.java
  33. 256 0
      src/main/java/me/km/snuviscript/QuestAPI.java
  34. 159 0
      src/main/java/me/km/snuviscript/QuestBank.java
  35. 833 0
      src/main/java/me/km/snuviscript/QuestData.java
  36. 22 0
      src/main/java/me/km/snuviscript/QuestInventoryHolder.java
  37. 1912 0
      src/main/java/me/km/snuviscript/QuestParser.java
  38. 329 0
      src/main/java/me/km/snuviscript/QuestUtils.java
  39. 53 0
      src/main/java/me/km/snuviscript/QuestVars.java
  40. 1004 0
      src/main/java/me/km/snuviscript/QuestsEvents.java
  41. 20 0
      src/main/java/me/km/snuviscript/SnuviInventory.java
  42. 174 0
      src/main/java/me/km/snuviscript/Tree.java
  43. 27 0
      src/main/java/me/km/snuviscript/Variable.java
  44. 10 0
      src/main/java/me/km/table/Table.java
  45. 27 0
      src/main/java/me/km/table/TableAPI.java
  46. 4 8
      src/main/java/me/km/utils/ItemStackBuilder.java
  47. 13 0
      src/main/java/me/km/utils/ItemStackUtils.java
  48. 144 5
      src/main/java/me/km/utils/ReflectionUtils.java
  49. 2 0
      src/main/resources/assets/km/lang/en_US.lang
  50. 6 0
      src/main/resources/assets/km/models/item/scroll.json

+ 9 - 19
src/main/java/me/km/KajetansMod.java

@@ -19,6 +19,8 @@ import me.km.plots.ProtectionBank;
 import me.km.scheduler.SnuviScheduler;
 import me.km.scoreboard.ScoreboardAPI;
 import me.km.skills.SkillManager;
+import me.km.snuviscript.QuestAPI;
+import me.km.snuviscript.QuestBank;
 import net.minecraft.server.MinecraftServer;
 import net.minecraft.util.text.TextFormatting;
 import net.minecraftforge.fml.common.FMLCommonHandler;
@@ -49,13 +51,11 @@ public class KajetansMod
     public static Module datatools;
     public static WorldData worldManager;
     public static Module environment;
-    //public static QuestAPI quest;
+    public static QuestAPI quest;
     public static JobAPI jobs;
     public static EffectUtils effects;
     public static SkillManager skills;
-    //public static Module scrolls;
-    //public static Customs customs;
-    //public static PiercingAPI piercing;
+    public static Module scrolls;
     public static ScoreboardAPI scoreboard;
     
     @SidedProxy(serverSide = "me.km.CommonProxy", clientSide = "me.km.ClientProxy")
@@ -141,11 +141,11 @@ public class KajetansMod
         worldManager.registerEvents("me.km.dimensions");
         
         // Questsystem
-        /*quest = new QuestAPI("Quests", "Quests", TextFormatting.LIGHT_PURPLE);
+        quest = new QuestAPI("Quests", "Quests", TextFormatting.LIGHT_PURPLE);
         quest.registerPrefix("Scripts", TextFormatting.LIGHT_PURPLE);
         quest.setDataBank(new QuestBank(quest, databank.getConnection()));
-        quest.registerCommands(e, "me.kt.quests");          
-        quest.registerEvents("me.kt.quests");*/
+        quest.registerCommands(e, "me.km.quests");          
+        quest.registerEvents("me.km.quests");
         
         // Jobsystem
         jobs = new JobAPI("JobSystem", "Jobs", TextFormatting.GREEN);
@@ -164,9 +164,8 @@ public class KajetansMod
         skills.registerEvents("me.km.skills");
         
         // Scrollsystem
-        /*scrolls = new Module("Scrolls", "Scrolls", TextFormatting.BLUE);
-        scrolls.registerCommands(e, "me.kt.scrolls"); 
-        scrolls.registerEvents("me.kt.scrolls");    */ 
+        scrolls = new Module("Scrolls", "Scrolls", TextFormatting.BLUE);
+        scrolls.registerCommands(e, "me.km.scrolls"); 
         
         // Environment
         environment = new Module("Environment", "Environment", TextFormatting.GREEN);
@@ -176,15 +175,6 @@ public class KajetansMod
         // Scoreboard
         scoreboard = new ScoreboardAPI("Scoreboard", "Scoreboard", TextFormatting.GOLD);
         scoreboard.registerEvents("me.km.scoreboard");
-        
-        // Customs
-        /*customs = new Customs("Customs", "Customs", TextFormatting.GRAY);
-        customs.registerCommands(e, "me.kt.custom");
-        customs.registerEvents("me.kt.custom");*/
-        
-        // Piercing
-        /*piercing = new PiercingAPI("Piercing", "Piercing", TextFormatting.DARK_PURPLE);
-        piercing.registerEvents("me.kt.piercing.events");*/
           
         //quest.startScript(this.getServer().getConsoleSender(), "startscript");
     }

+ 18 - 1
src/main/java/me/km/api/Location.java

@@ -1,6 +1,7 @@
 package me.km.api;
 
 import net.minecraft.block.state.IBlockState;
+import net.minecraft.entity.Entity;
 import net.minecraft.util.math.BlockPos;
 import net.minecraft.util.math.Vec3d;
 import net.minecraft.world.World;
@@ -8,7 +9,7 @@ import net.minecraft.world.World;
 public class Location 
 {
     private final World w;
-    private final Vec3d pos;
+    private Vec3d pos;
     private float yaw;
     private float pitch;
     
@@ -34,6 +35,17 @@ public class Location
     {
         this(w, pos, 0, 0);
     }
+    
+    public Location(Entity ent)
+    {
+        this(ent.world, ent.getPositionVector(), ent.rotationYaw, ent.rotationPitch);
+    }
+    
+    public Location add(double x, double y, double z)
+    {
+        pos = pos.addVector(x, y, z);
+        return this;
+    }
 
     public World getWorld() 
     {
@@ -44,6 +56,11 @@ public class Location
     {
         return pos;
     }
+    
+    public BlockPos getBlockPos() 
+    {
+        return new BlockPos(pos.xCoord, pos.yCoord, pos.zCoord);
+    }
 
     public float getYaw() 
     {

+ 52 - 2
src/main/java/me/km/api/SimpleConfig.java

@@ -11,6 +11,10 @@ import java.util.List;
 import java.util.TreeMap;
 import java.util.stream.Collectors;
 import me.km.dimensions.ModDimensions;
+import me.km.exception.IllegalItemStackStringException;
+import me.km.nms.NmsUtilities;
+import me.km.utils.ItemStackUtils;
+import net.minecraft.item.ItemStack;
 import net.minecraft.util.math.Vec3d;
 
 public class SimpleConfig
@@ -66,17 +70,19 @@ public class SimpleConfig
         return file.delete();
     }
     
-    public void save()
+    public boolean save()
     {
         try
         {
             Files.write(Paths.get(file.toURI()), conf.entrySet().stream()
                             .map(e -> e.getKey() + "=" + e.getValue())
                             .collect(Collectors.toList()), Charset.forName("UTF-8"));
+            return true;
         }
         catch(IOException ex)
         {
             m.sendWarningToConsole("Can't write to '" + file.getPath() + "'");
+            return false;
         }
     }
     
@@ -133,6 +139,23 @@ public class SimpleConfig
         }
     }
     
+    public int getInt(String key, int error)
+    {
+        String s = conf.get(key);
+        if(s == null)
+        {
+            return error;
+        }
+        try
+        {
+            return Integer.parseInt(s);
+        }
+        catch(NumberFormatException ex)
+        {
+            return error;
+        }
+    }
+    
     public Location getLocation(String key)
     {
         return new Location(ModDimensions.getWorldFromName(getString(key + ".world")), 
@@ -140,6 +163,23 @@ public class SimpleConfig
                 getFloat(key + ".yaw", 0), getFloat(key + ".pitch", 0));
     }
     
+    public ItemStack getItemStack(String key)
+    {
+        String s = conf.get(key);
+        if(s == null)
+        {
+            return ItemStack.EMPTY;
+        }
+        try
+        {
+            return ItemStackUtils.getStackFromNbtString(s);
+        }
+        catch(IllegalItemStackStringException ex)
+        {
+            return ItemStack.EMPTY;
+        }
+    }
+    
     // -----------------------------------------------------------------------------------
     // Add Data
     // -----------------------------------------------------------------------------------
@@ -154,11 +194,16 @@ public class SimpleConfig
         setString(key, String.valueOf(d));
     }
     
-    public void setDouble(String key, float d)
+    public void setFloat(String key, float d)
     {
         setString(key, String.valueOf(d));
     }
     
+    public void setInt(String key, int i)
+    {
+        setString(key, String.valueOf(i));
+    }
+    
     public void setLocation(String key, Location l)
     {
         setString(key + ".world", l.getWorld().getWorldInfo().getWorldName());
@@ -168,4 +213,9 @@ public class SimpleConfig
         setDouble(key + ".yaw", l.getYaw());
         setDouble(key + ".pitch", l.getPitch());
     }
+    
+    public void setItemStack(String key, ItemStack stack)
+    {
+        setString(key, ItemStackUtils.getNbtString(stack));
+    }
 }

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

@@ -24,7 +24,6 @@ import net.minecraft.entity.player.EntityPlayerMP;
 import net.minecraft.init.Blocks;
 import net.minecraft.init.Items;
 import net.minecraft.init.MobEffects;
-import net.minecraft.inventory.IInventory;
 import net.minecraft.item.Item;
 import net.minecraft.item.ItemAxe;
 import net.minecraft.item.ItemHoe;
@@ -473,9 +472,9 @@ public class Utils
     // Player-Tools
     // -------------------------------------------------------------------------
     
-    public static EntityPlayer getNearestPlayer(World w, BlockPos l)
+    public static EntityPlayer getNearestPlayer(World w, Vec3d v)
     {
-        return w.getPlayers(EntityPlayer.class, p -> true).stream().min((p1, p2) -> Double.compare(p1.getDistanceSq(l), p2.getDistanceSq(l))).orElse(null);
+        return w.getPlayers(EntityPlayer.class, p -> true).stream().min((p1, p2) -> Double.compare(p1.getDistanceSq(v.xCoord, v.yCoord, v.zCoord), p2.getDistanceSq(v.xCoord, v.yCoord, v.zCoord))).orElse(null);
     }
     
     public static BlockPos getPlayerTarget(EntityPlayer p, double range)

+ 6 - 5
src/main/java/me/km/blockprotections/SemiProtections.java

@@ -1,9 +1,11 @@
 package me.km.blockprotections;
 
+import me.km.KajetansMod;
 import me.km.api.Module;
 import me.km.api.ModuleListener;
 import me.km.permissions.Permission;
 import me.km.permissions.Permissions;
+import me.km.plots.ProtectionBank;
 import net.minecraft.block.BlockCrops;
 import net.minecraft.block.state.IBlockState;
 import net.minecraft.entity.player.EntityPlayer;
@@ -12,12 +14,12 @@ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
 
 public class SemiProtections extends ModuleListener
 {    
-    //private final BlockProtectionBank bank;
+    private final ProtectionBank bank;
     
     public SemiProtections(Module m)
     {
         super(m);
-        //bank = KajetansMod.blocks.getDataBank(BlockProtectionBank.class);
+        bank = KajetansMod.plots.getDataBank(ProtectionBank.class);
     }
     
     @SubscribeEvent
@@ -31,11 +33,10 @@ public class SemiProtections extends ModuleListener
         IBlockState b = e.getState();
         if(b.getBlock() instanceof BlockCrops)
         {
-            // TODO
-            /*if(!bank.hasTag(e., "farm"))
+            if(!bank.hasTag(e.getWorld(), e.getPos(), "farm"))
             {
                 return;
-            }*/
+            }
             e.setCanceled(true); 
             if(b.getValue(BlockCrops.AGE) == 7)
             {

+ 0 - 1
src/main/java/me/km/commands/CommandBan.java

@@ -5,7 +5,6 @@ import me.km.api.Utils;
 import me.km.api.GlobalText;
 import me.km.api.Module;
 import me.km.api.ModuleCommand;
-import java.util.Date;
 import me.km.KajetansMod;
 import me.km.permissions.Permissions;
 import net.minecraft.command.ICommandSender;

+ 6 - 6
src/main/java/me/km/commands/CommandExp.java

@@ -96,7 +96,7 @@ public class CommandExp extends ModuleCommand
                             try
                             {         
                                 int i = Integer.parseInt(arg[1]);
-                                if(i <= 0)
+                                if(i < 0)
                                 {
                                     throw new NumberFormatException();
                                 }
@@ -108,7 +108,7 @@ public class CommandExp extends ModuleCommand
                             }
                             catch(NumberFormatException ex2)
                             {
-                                m.send(cs, GlobalText.noPositiveNaturalNumber());
+                                m.send(cs, GlobalText.noNaturalNumber());
                                 return true;
                             }
                         }
@@ -126,7 +126,7 @@ public class CommandExp extends ModuleCommand
                             try
                             {         
                                 int i = Integer.parseInt(arg[1]);
-                                if(i <= 0)
+                                if(i < 0)
                                 {
                                     throw new NumberFormatException();
                                 }
@@ -136,7 +136,7 @@ public class CommandExp extends ModuleCommand
                             }
                             catch(NumberFormatException ex)
                             {
-                                m.send(cs, GlobalText.noPositiveNaturalNumber());
+                                m.send(cs, GlobalText.noNaturalNumber());
                                 return true;
                             } 
                         }
@@ -156,7 +156,7 @@ public class CommandExp extends ModuleCommand
                             try
                             {         
                                 int i = Integer.parseInt(arg[1]);
-                                if(i <= 0)
+                                if(i < 0)
                                 {
                                     throw new NumberFormatException();
                                 }
@@ -165,7 +165,7 @@ public class CommandExp extends ModuleCommand
                             }
                             catch(NumberFormatException ex2)
                             {
-                                m.send(cs, GlobalText.noPositiveNaturalNumber());
+                                m.send(cs, GlobalText.noNaturalNumber());
                                 return true;
                             }
                         }

+ 1 - 3
src/main/java/me/km/commands/CommandFeed.java

@@ -45,9 +45,7 @@ public class CommandFeed extends ModuleCommand
             affectedPlayer = (EntityPlayer) cs;
             m.send(affectedPlayer, "Dein Hunger wurde gesättigt.");
         }
-        affectedPlayer.getFoodStats().setFoodLevel(20);
-        affectedPlayer.getFoodStats().addExhaustion(16);
-        affectedPlayer.getFoodStats().setFoodSaturationLevel(16);
+        affectedPlayer.getFoodStats().addStats(50, 1);
         return true;
     }
 }

+ 1 - 4
src/main/java/me/km/commands/CommandHeal.java

@@ -47,10 +47,7 @@ public class CommandHeal extends ModuleCommand
             m.send(affectedPlayer, "Du wurdest geheilt.");
         }
         affectedPlayer.setHealth(affectedPlayer.getMaxHealth());
-        for(PotionEffect eff : affectedPlayer.getActivePotionEffects())
-        {
-            affectedPlayer.removePotionEffect(eff.getPotion());
-        }
+        affectedPlayer.clearActivePotions();
         return true;
     }
 }

+ 10 - 7
src/main/java/me/km/commands/CommandTpPos.java

@@ -6,6 +6,7 @@ import me.km.api.GlobalText;
 import me.km.api.Location;
 import me.km.api.Module;
 import me.km.api.ModuleCommand;
+import me.km.dimensions.ModDimensions;
 import me.km.exception.PlayerNotFoundException;
 import net.minecraft.command.ICommandSender;
 import net.minecraft.entity.player.EntityPlayer;
@@ -52,18 +53,20 @@ public class CommandTpPos extends ModuleCommand
         try
         {
             World w;
-            // TODO
-            /*if(arg.length >= 4 && Bukkit.getWorld(arg[3]) != null)
+            if(arg.length >= 4)
             {
-                w = Bukkit.getWorld(arg[3]);
+                w = ModDimensions.getWorldFromName(arg[3]);
+                if(w == null)
+                {
+                    this.getModule().send(cs, "Die Welt '" + arg[3] + "' wurde nicht gefunden.");
+                }
             }
             else
-            {*/
+            {
                 w = affectedPlayer.getEntityWorld();
-            //}
+            }
             Vec3d v = new Vec3d(Double.parseDouble(arg[0]), Double.parseDouble(arg[1]), Double.parseDouble(arg[2]));
-            Location l = new Location(w, v);
-            Utils.teleportEntity(affectedPlayer, l);
+            Utils.teleportEntity(affectedPlayer, new Location(w, v));
             String s = " zu " + v.xCoord + ", " + v.yCoord + ", " + v.zCoord + " teleportiert.";
             this.getModule().send(affectedPlayer, "Du wurdest" + s);
             if(!cs.equals(affectedPlayer))

+ 10 - 7
src/main/java/me/km/commands/CommandUser.java

@@ -5,8 +5,11 @@ import me.km.KajetansMod;
 import me.km.api.GlobalText;
 import me.km.api.Module;
 import me.km.api.ModuleCommand;
+import me.km.api.Utils;
 import me.km.permissions.Permissions;
 import net.minecraft.command.ICommandSender;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.server.management.PlayerList;
 import net.minecraft.util.text.TextComponentString;
 
 public class CommandUser extends ModuleCommand
@@ -38,14 +41,14 @@ public class CommandUser extends ModuleCommand
         cs.sendMessage(new TextComponentString(GlobalText.Spacer()));
         m.send(cs, op.getName());
         m.sendHelpListElement(cs, "UUID", op.getId().toString());
-        // TODO
-        /*m.sendHelpListElement(cs, "Gebannt", String.valueOf(op.));
-        m.sendHelpListElement(cs, "Whitelisted", String.valueOf(op.isWhitelisted()));
-        if(op.isOnline())
+        PlayerList list = KajetansMod.server.getPlayerList();
+        m.sendHelpListElement(cs, "Gebannt", String.valueOf(list.getBannedPlayers().isBanned(op)));
+        m.sendHelpListElement(cs, "Whitelisted", String.valueOf(list.getWhitelistedPlayers().isWhitelisted(op)));
+        EntityPlayerMP p = list.getPlayerByUUID(op.getId());
+        if(p != null)
         {
-            Player p = op.getPlayer();
-            m.sendHelpListElement(cs, "IP", p.getAddress().getAddress().toString().substring(1));
-        }*/
+            m.sendHelpListElement(cs, "IP", p.connection.netManager.getRemoteAddress().toString());
+        }
         return true;
     }
 }

+ 183 - 0
src/main/java/me/km/dimensions/ChangeWorldEvent.java

@@ -0,0 +1,183 @@
+package me.km.dimensions;
+
+import me.km.KajetansMod;
+import me.km.api.Location;
+import me.km.api.Module;
+import me.km.api.ModuleListener;
+import me.km.api.SimpleConfig;
+import me.km.api.Utils;
+import me.km.utils.ReflectionUtils;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.inventory.InventoryEnderChest;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.FoodStats;
+import net.minecraft.util.NonNullList;
+import net.minecraftforge.common.DimensionManager;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+import net.minecraftforge.event.entity.EntityTravelToDimensionEvent;
+
+public class ChangeWorldEvent extends ModuleListener 
+{
+    public ChangeWorldEvent(Module m)
+    {
+        super(m);
+    }
+
+    @SubscribeEvent
+    public void changeWorldEvent(EntityTravelToDimensionEvent e)
+    { 
+        if(!(e.getEntity() instanceof EntityPlayer))
+        {
+            return;
+        }
+        EntityPlayer p = (EntityPlayer) e.getEntity();
+        int first = p.dimension;
+        int second = e.getDimension();
+        if(first >= 2 || second >= 2)
+        {
+            savePlayerInventory(p, first);
+            loadPlayerInventory(p, second);
+        }
+    }   
+    
+    public void savePlayerInventory(EntityPlayer p, int dimension)
+    {
+        String s;
+        if(dimension <= 1)
+        {
+            s = DimensionManager.getWorld(0).getWorldInfo().getWorldName();
+        }
+        else
+        {
+            s = ModDimensions.getWorldName(DimensionManager.getWorld(dimension));
+        }
+        SimpleConfig sp = new SimpleConfig(KajetansMod.worldManager, "world/" + p.getUniqueID() + "/" + s + ".yml", false);  
+        
+        // general data
+        sp.setInt("exp", p.experienceTotal);     
+        FoodStats stats = p.getFoodStats();
+        sp.setFloat("exhaustion", ReflectionUtils.getExhaustion(stats));
+        sp.setFloat("saturation", ReflectionUtils.getSaturation(stats));
+        sp.setInt("foodlevel", stats.getFoodLevel());
+        sp.setFloat("currenthealth", p.getHealth());
+        sp.setFloat("maxhealth", 20);
+        sp.setInt("maxair", p.getAir());
+        
+        
+        // position in world
+        sp.setLocation("location", new Location(p.world, p.getPositionVector()));
+        
+        // main inventory
+        int counter = 0;
+        for(ItemStack stack : p.inventory.mainInventory)
+        {
+            sp.setItemStack("inv." + counter, stack);      
+            counter++;
+        }
+        
+        // armor inventory
+        counter = 0;
+        for(ItemStack stack : p.inventory.armorInventory)
+        {
+            sp.setItemStack("armor." + counter, stack);      
+            counter++;
+        }
+        
+        // offhand inventory
+        counter = 0;
+        for(ItemStack stack : p.inventory.offHandInventory)
+        {
+            sp.setItemStack("offhand." + counter, stack);      
+            counter++;
+        }
+        
+        // enderchest
+        InventoryEnderChest chest = p.getInventoryEnderChest();
+        for(counter = 0; counter <  chest.getSizeInventory(); counter++)
+        {
+            sp.setItemStack("enderchest." + counter, chest.getStackInSlot(counter));   
+        }
+        
+        if(sp.save())
+        {
+            this.getModule().sendToConsole("Der Spieler '" + p.getName() + "' wurde gespeichert.");
+            return;
+        }
+        this.getModule().sendWarningToConsole("Der Spieler '" + p.getName() + "' konnte nicht gespeichert werden.");
+    }
+    
+    public void loadPlayerInventory(EntityPlayer p, int dimension)
+    {
+        String s;
+        if(dimension <= 1)
+        {
+            s = DimensionManager.getWorld(0).getWorldInfo().getWorldName();
+        }
+        else
+        {
+            s = ModDimensions.getWorldName(DimensionManager.getWorld(dimension));
+        }
+        SimpleConfig sp = new SimpleConfig(KajetansMod.worldManager, "world/" + p.getUniqueID() + "/" + s + ".yml", true); 
+        if(!sp.exists())
+        {
+            this.getModule().sendToConsole("Der Spieler '" + p.getName() + "' hat keine Daten in der Welt '" + s + "'.");
+            p.inventory.clear();
+            p.experienceTotal = 0;   
+            FoodStats stats = p.getFoodStats();
+            stats.setFoodLevel(20);
+            ReflectionUtils.setExhaustion(stats, 1);
+            ReflectionUtils.setSaturation(stats, 5);
+            p.setHealth(20);
+            p.getInventoryEnderChest().clear();
+            return;
+        }
+
+        // general data  
+        p.experience = 0;
+        p.experienceTotal = 0;
+        p.experienceLevel = 0;
+        p.addExperience(sp.getInt("exp", 0));  
+        FoodStats stats = p.getFoodStats();
+        ReflectionUtils.setExhaustion(stats, sp.getFloat("exhaustion", 1));
+        ReflectionUtils.setSaturation(stats, sp.getFloat("saturation", 5));   
+        stats.setFoodLevel(sp.getInt("foodlevel", 20));    
+        p.setHealth(sp.getFloat("currenthealth", 20));    
+
+        // position in world
+        Utils.teleportEntity(p, sp.getLocation("location"));
+        
+        // main inventory
+        int counter;
+        NonNullList<ItemStack> inv = p.inventory.mainInventory;
+        for(counter = 0; counter < inv.size(); counter++)
+        {
+            inv.set(counter, sp.getItemStack("inv." + counter)); 
+            counter++;
+        }
+        
+        // armor inventory
+        inv = p.inventory.armorInventory;
+        for(counter = 0; counter < inv.size(); counter++)
+        {
+            inv.set(counter, sp.getItemStack("armor." + counter)); 
+            counter++;
+        }
+        
+        // offhand inventory
+        inv = p.inventory.offHandInventory;
+        for(counter = 0; counter < inv.size(); counter++)
+        {
+            inv.set(counter, sp.getItemStack("offhand." + counter)); 
+            counter++;
+        }
+        
+        // enderchest
+        InventoryEnderChest chest = p.getInventoryEnderChest();
+        for(counter = 0; counter <  chest.getSizeInventory(); counter++)
+        {
+            chest.setInventorySlotContents(counter, sp.getItemStack("enderchest." + counter));  
+        }
+
+        this.getModule().sendToConsole("Der Spieler '" + p.getName() + "' wurde geladen.");
+    }
+}

+ 8 - 2
src/main/java/me/km/inventory/CustomContainer.java

@@ -1,5 +1,6 @@
 package me.km.inventory;
 
+import me.km.snuviscript.SnuviInventory;
 import net.minecraft.entity.player.EntityPlayer;
 import net.minecraft.entity.player.EntityPlayerMP;
 import net.minecraft.inventory.ClickType;
@@ -15,6 +16,11 @@ public class CustomContainer extends ContainerChest
         super(p.inventory, new EntityInventory(title, slotCount, p), p);
     }
     
+    public CustomContainer(SnuviInventory inv, EntityPlayer p) 
+    {
+        super(p.inventory, inv, p);
+    }
+    
     public EntityInventory getShownInventory()
     {
         return (EntityInventory) this.getLowerChestInventory();
@@ -34,7 +40,7 @@ public class CustomContainer extends ContainerChest
         p.openContainer.addListener(p);
     }
     
-    public boolean noClicking()
+    public boolean noClicking(int slotId, int dragType, ClickType clickTypeIn, EntityPlayer player)
     {
         return true;
     }
@@ -42,7 +48,7 @@ public class CustomContainer extends ContainerChest
     @Override
     public final ItemStack slotClick(int slotId, int dragType, ClickType clickTypeIn, EntityPlayer player) 
     {
-        if(noClicking())
+        if(noClicking(slotId, dragType, clickTypeIn, player))
         {
             onCanceledClick(slotId, dragType, clickTypeIn, player);
             return ItemStack.EMPTY;

+ 27 - 0
src/main/java/me/km/inventory/InventoryUtils.java

@@ -104,4 +104,31 @@ public class InventoryUtils
         }
         return amount;
     }
+    
+    public static int addToInventory(IInventory inv, ItemStack add)
+    {
+        ItemStack stack;
+        int sub;
+        int amount = add.getCount();
+        int max = Math.min(add.getMaxStackSize(), inv.getInventoryStackLimit());
+        for(int i = 0; i < inv.getSizeInventory(); i++)
+        {
+            stack = inv.getStackInSlot(i);
+            if(doItemStacksMatch(stack, add))
+            {
+                sub = stack.getCount();
+                if(max - sub >= amount)
+                {
+                    stack.setCount(amount + stack.getCount());
+                }
+                else if(sub < max)
+                {
+                    amount -= max - sub;
+                    stack.setCount(max);
+                    return 0;
+                }
+            }
+        }
+        return amount;
+    }
 }

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

@@ -1,6 +1,7 @@
 package me.km.items;
 
 import me.km.KajetansMod;
+import me.km.scrolls.ItemScroll;
 import net.minecraft.creativetab.CreativeTabs;
 import net.minecraft.init.SoundEvents;
 import net.minecraft.inventory.EntityEquipmentSlot;
@@ -53,6 +54,8 @@ public class ModItems
     public static ItemArmor copperLeggings;
     public static ItemArmor copperBoots;
     
+    public static ItemScroll scroll;
+    
     public static void init() 
     {
 	copperIngot = register(new ItemBase("copper_ingot", "ingotCopper").setCreativeTab(CreativeTabs.MATERIALS));
@@ -68,6 +71,8 @@ public class ModItems
         copperChestplate = register(new ItemArmor(ARMOR_COPPER, EntityEquipmentSlot.CHEST, "copper_chestplate", "chestplateCopper"));
         copperLeggings = register(new ItemArmor(ARMOR_COPPER, EntityEquipmentSlot.LEGS, "copper_leggings", "leggingsCopper"));
         copperBoots = register(new ItemArmor(ARMOR_COPPER, EntityEquipmentSlot.FEET, "copper_boots", "bootsCopper"));
+        
+        scroll = register((ItemScroll) new ItemScroll("scroll", "scroll").setCreativeTab(CreativeTabs.MISC));
     }
     
     public static void lateInit() 

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

@@ -33,7 +33,13 @@ public enum Permissions
     JOB, RECIPE, ACTIVE_SKILLS, SKILLS,
     
     // Plots
-    PLOT, PLOT_CREATE, PLOT_INFO, PLOT_TAG, PLOT_SHARE, PLOT_SIGN, PLOT_BYPASS, PLOT_MARK
+    PLOT, PLOT_CREATE, PLOT_INFO, PLOT_TAG, PLOT_SHARE, PLOT_SIGN, PLOT_BYPASS, PLOT_MARK,
+    
+    // Scrolls
+    SCROLL,
+    
+    // Quests / Scripts
+    QUEST, SCRIPT, QUESTINFO, GIVEUP, SCRIPT_ERROR
     
     // Special
 }

+ 28 - 0
src/main/java/me/km/playerbank/GodMode.java

@@ -0,0 +1,28 @@
+package me.km.playerbank;
+
+import me.km.KajetansMod;
+import me.km.api.Module;
+import me.km.api.ModuleListener;
+import net.minecraft.entity.EntityLivingBase;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraftforge.event.entity.living.LivingHurtEvent;
+import net.minecraftforge.fml.common.eventhandler.EventPriority;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+
+public class GodMode extends ModuleListener
+{
+    public GodMode(Module m) 
+    {
+        super(m);
+    }
+    
+    @SubscribeEvent(priority = EventPriority.LOW)
+    public void GodGetsDamageEvent(LivingHurtEvent e)
+    {
+        EntityLivingBase ent = e.getEntityLiving();
+        if(ent instanceof EntityPlayer && KajetansMod.playerbank.getDataBank().getTag((EntityPlayer) ent, "god") == 1)
+        {
+            e.setCanceled(true);
+        }
+    }
+}

+ 0 - 1
src/main/java/me/km/playerbank/PlayerBank.java

@@ -2,7 +2,6 @@ package me.km.playerbank;
 
 import com.mojang.authlib.GameProfile;
 import java.sql.Connection;
-import java.util.UUID;
 import me.km.KajetansMod;
 import me.km.api.Module;
 import me.km.databank.SimpleDataBank;

+ 81 - 0
src/main/java/me/km/playerbank/PlayerLogInOut.java

@@ -0,0 +1,81 @@
+package me.km.playerbank;
+
+import java.io.File;
+import me.km.KajetansMod;
+import me.km.api.Module;
+import me.km.api.ModuleListener;
+import me.km.api.Utils;
+import me.km.commands.CommandSilent;
+import me.km.commands.CommandTeleportAccept;
+import me.km.permissions.Permission;
+import me.km.permissions.Permissions;
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.server.management.PlayerList;
+import net.minecraftforge.event.entity.EntityJoinWorldEvent;
+import net.minecraftforge.fml.common.eventhandler.EventPriority;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+import net.minecraftforge.fml.common.gameevent.PlayerEvent;
+import net.minecraftforge.fml.common.network.FMLNetworkEvent.ServerConnectionFromClientEvent;
+
+public class PlayerLogInOut extends ModuleListener
+{
+    public PlayerLogInOut(Module m) 
+    {
+        super(m);
+    }
+    
+    @SubscribeEvent(priority = EventPriority.HIGH)
+    public void onPlayerJoin(PlayerEvent.PlayerLoggedInEvent e)
+    {
+        EntityPlayer p = e.player;
+        
+        File file = new File("./saves/world/playerdata/" + p.getUniqueID() + ".dat");
+        if(!file.exists()) // first join
+        {
+            Utils.teleportEntity(p, Utils.getSpawn());
+        }
+        
+        PlayerBank pb = KajetansMod.playerbank.getDataBank();
+        if(!pb.contains(p))
+        {          
+            pb.add(p.getGameProfile());
+            this.getModule().sendToConsole(p.getName() + " wurde hinzugefügt.");
+        }
+        else
+        {
+            this.getModule().sendToConsole(p.getName() + " ist bereits vorhanden.");
+            if(pb.getName(p.getUniqueID().toString()).equals(p.getName()))
+            {
+                this.getModule().sendToConsole(p.getName() + " hat noch den gleichen Namen.");
+            }
+            else
+            {
+                this.getModule().sendToConsole(p.getName() + " hat seinen Namen geändert.");
+                pb.changeName(p);
+            }
+        }      
+    }
+    
+    /*@SubscribeEvent(priority = EventPriority.LOW)
+    public void silentJoin(PlayerLoginEvent e)
+    {
+        // does not work currently, needs overwriting of PlayerList in DedicatedServer
+        
+        Entity p = e.getEntity();
+        if(p instanceof EntityPlayerMP && Permission.hasPermission(p, Permissions.SILENT) && 
+            KajetansMod.generalCommands.getCommand(CommandSilent.class).silentjoin)
+        {
+            ((EntityPlayerMP) p).add
+        }
+    }*/
+    
+    @SubscribeEvent(priority = EventPriority.LOW)
+    public void onPlayerLeaveCleanup(PlayerEvent.PlayerLoggedOutEvent e)
+    {      
+        EntityPlayer p = e.player;
+        KajetansMod.generalCommands.getCommand(CommandTeleportAccept.class).tpaccept.remove(p.getUniqueID());
+        KajetansMod.playerbank.removeData(p);
+    }
+}

+ 13 - 0
src/main/java/me/km/playerbank/PlayerManager.java

@@ -2,6 +2,7 @@ package me.km.playerbank;
 
 import java.util.HashMap;
 import java.util.UUID;
+import me.km.api.Location;
 import me.km.api.Module;
 import net.minecraft.entity.player.EntityPlayer;
 import net.minecraft.util.text.TextFormatting;
@@ -9,11 +10,13 @@ import net.minecraft.util.text.TextFormatting;
 public class PlayerManager extends Module
 {
     private final HashMap<UUID, PlayerData> data;
+    private final HashMap<UUID, Location> back;
     
     public PlayerManager(String mname, String prefix, TextFormatting color) 
     {
         super(mname, prefix, color);
         data = new HashMap<>();
+        back = new HashMap<>();
     }
 
     public PlayerBank getDataBank() 
@@ -21,6 +24,16 @@ public class PlayerManager extends Module
         return super.getDataBank(PlayerBank.class);
     }
     
+    public Location saveLocation(EntityPlayer p)
+    {
+        return back.put(p.getUniqueID(), new Location(p.world, p.getPositionVector()));
+    }
+    
+    public Location getLastLocation(EntityPlayer p)
+    {
+        return back.get(p.getUniqueID());
+    }
+    
     public PlayerData getData(EntityPlayer p)
     {
         PlayerData pd = data.get(p.getUniqueID());

+ 45 - 0
src/main/java/me/km/playerbank/PvpEvent.java

@@ -0,0 +1,45 @@
+package me.km.playerbank;
+
+import me.km.KajetansMod;
+import me.km.api.Module;
+import me.km.api.ModuleListener;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraftforge.event.entity.living.LivingAttackEvent;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+
+public class PvpEvent extends ModuleListener
+{
+    public PvpEvent(Module m) 
+    {
+        super(m);
+    }
+    
+    @SubscribeEvent
+    public void PlayerVSPlayer(LivingAttackEvent e)
+    {      
+        if(!(e.getEntityLiving() instanceof EntityPlayer) ||
+            !KajetansMod.worldManager.getWorldPreferences(e.getEntity().world).pvpProtection || 
+            !(e.getSource().getSourceOfDamage() instanceof EntityPlayer))
+        {
+            return;
+        }
+        EntityPlayer affectedPlayer = (EntityPlayer) e.getEntityLiving();
+        EntityPlayer p = (EntityPlayer) e.getSource().getSourceOfDamage();    
+        if(affectedPlayer.equals(p))
+        {
+            return;
+        }
+        
+        PlayerBank pb = KajetansMod.playerbank.getDataBank();
+        if(pb.getTag(p, "pvp") != 1)
+        {
+            this.getModule().send(affectedPlayer, p.getName() + " hat PVP ausgeschalten!");
+            e.setCanceled(true);
+        }
+        if(pb.getTag(affectedPlayer, "pvp") != 1)
+        {
+            this.getModule().send(affectedPlayer, "Du hast PVP ausgeschalten!");
+            e.setCanceled(true);
+        }
+    }
+}

+ 9 - 21
src/main/java/me/km/plots/ProtectionCommand.java

@@ -1,10 +1,9 @@
 package me.km.plots;
 
-import me.kt.api.Module;
-import org.bukkit.entity.Player;
-import org.bukkit.event.EventHandler;
-import org.bukkit.event.EventPriority;
-import org.bukkit.event.player.PlayerCommandPreprocessEvent;
+import me.km.api.Module;
+import net.minecraftforge.fml.common.eventhandler.EventPriority;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
+import net.minecraftforge.event.CommandEvent;
 
 public class ProtectionCommand extends Protection
 {
@@ -13,24 +12,13 @@ public class ProtectionCommand extends Protection
         super(m);
     }
 
-    @EventHandler(priority = EventPriority.LOWEST)
-    public void CommandProtection(PlayerCommandPreprocessEvent e)
+    @SubscribeEvent(priority = EventPriority.HIGHEST)
+    public void CommandProtection(CommandEvent e)
     {
-        if(e.getMessage().startsWith("/op") || e.getMessage().startsWith("/minecraft:op"))
+        String name = e.getCommand().getName();
+        if(name.equals("op"))
         {
-            e.setCancelled(true);   
-        }
-        else if(e.getMessage().startsWith("/fly"))
-        {
-            Player p = e.getPlayer();
-            if(p.hasPermission("kt.plot.bypass"))
-            {
-                return;
-            }
-            if(this.getProtectionBank().hasTag(p.getLocation(), "fly"))
-            {
-                e.setCancelled(true);
-            }       
+            e.setCanceled(true);   
         }
     }
 }

+ 10 - 24
src/main/java/me/km/plots/ProtectionExplosion.java

@@ -1,11 +1,10 @@
 package me.km.plots;
 
-import me.kt.api.Module;
-import org.bukkit.World;
-import org.bukkit.event.EventHandler;
-import org.bukkit.event.EventPriority;
-import org.bukkit.event.block.BlockExplodeEvent;
-import org.bukkit.event.entity.EntityExplodeEvent;
+import me.km.api.Module;
+import net.minecraft.util.math.BlockPos;
+import net.minecraftforge.event.world.ExplosionEvent;
+import net.minecraftforge.fml.common.eventhandler.EventPriority;
+import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
 
 public class ProtectionExplosion extends Protection
 {
@@ -14,25 +13,12 @@ public class ProtectionExplosion extends Protection
         super(m);
     }
     
-    @EventHandler(priority = EventPriority.LOWEST)
-    public void BlockExplosionProtection(BlockExplodeEvent e)
+    @SubscribeEvent(priority = EventPriority.HIGHEST)
+    public void BlockExplosionProtection(ExplosionEvent.Start e)
     {
-        if(e.getBlock().getWorld().getEnvironment() != World.Environment.NETHER)
+        if(!this.getProtectionBank().hasTag(e.getWorld(), new BlockPos(e.getExplosion().getPosition()), "TNT"))
         {
-            e.setCancelled(true);
-        } 
-    }
-    
-    @EventHandler(priority = EventPriority.LOWEST)
-    public void EntityExplosionProtection(EntityExplodeEvent e)
-    {
-        if(e.getLocation().getWorld().getEnvironment() != World.Environment.NETHER)
-        {
-            if(this.getProtectionBank().hasTag(e.getLocation(), "TNT"))
-            {
-                return;
-            } 
-            e.setCancelled(true);
-        }   
+            e.setCanceled(true);
+        }
     }
 }

+ 0 - 48
src/main/java/me/km/plots/ProtectionFire.java

@@ -1,48 +0,0 @@
-package me.km.plots;
-
-import me.km.api.Module;
-import net.minecraftforge.event.world.BlockEvent;
-import net.minecraftforge.fml.common.eventhandler.EventPriority;
-import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
-
-public class ProtectionFire extends Protection
-{
-    public ProtectionFire(Module m) 
-    {
-        super(m);
-    }
-    
-    @SubscribeEvent(priority = EventPriority.HIGHEST)
-    public void FireDestroyProtection(BlockEvent.BreakEvent e)
-    {
-        if(e.getBlock().getWorld().getEnvironment() != World.Environment.NETHER)
-        {
-            e.setCancelled(true);
-        }    
-    }
-    
-    @SubscribeEvent(priority = EventPriority.HIGHEST)
-    public void FireIgniteProtection(BlockIgniteEvent e)
-    {
-        if(e.getCause() == BlockIgniteEvent.IgniteCause.FLINT_AND_STEEL)
-        {
-            return;
-        }
-        if(e.getBlock().getWorld().getEnvironment() != World.Environment.NETHER)
-        {
-            e.setCancelled(true);
-        }   
-    }
-    
-    @SubscribeEvent(priority = EventPriority.HIGHEST)
-    public void FireSpreadProtection(BlockSpreadEvent e)
-    {
-        if(e.getSource().getType() == Material.FIRE)
-        {
-            if(e.getBlock().getWorld().getEnvironment() != World.Environment.NETHER)
-            {
-                e.setCancelled(true);
-            } 
-        }  
-    }
-}

+ 76 - 0
src/main/java/me/km/scrolls/CommandScroll.java

@@ -0,0 +1,76 @@
+package me.km.scrolls;
+
+import java.util.Arrays;
+import java.util.stream.Collectors;
+import me.km.api.GlobalText;
+import me.km.api.Module;
+import me.km.api.ModuleTabCommand;
+import me.km.api.Utils;
+import me.km.effects.ActiveEffectBase;
+import me.km.effects.Effect;
+import me.km.effects.EffectUtils;
+import me.km.items.ModItems;
+import me.km.permissions.Permissions;
+import me.km.utils.ItemStackBuilder;
+import net.minecraft.command.ICommandSender;
+import net.minecraft.entity.player.EntityPlayer;
+
+public class CommandScroll extends ModuleTabCommand
+{
+    public CommandScroll(Module m) 
+    {
+        super("scroll", m, Arrays.stream(Effect.values()).map(n -> n.getEffectString()).filter(o -> o != null).collect(Collectors.toList()), 1);
+        super.setDescription("Erstellt Schriftrollen");
+        super.setUsage("/scroll <name> [level] [amount]");
+        super.setPermission(Permissions.SCROLL);
+    }
+
+    @Override
+    public boolean execute(ICommandSender cs, String[] arg) 
+    {
+        if(arg.length < 1)
+        {
+            return false;
+        }
+        if(!(cs instanceof EntityPlayer))
+        {
+            this.getModule().send(cs, GlobalText.onlyPlayer());
+            return true;
+        }
+        EntityPlayer p = (EntityPlayer) cs;
+        Class<? extends ActiveEffectBase> c = EffectUtils.getEffectClass(arg[0]);
+        if(c == null)
+        { 
+            this.getModule().send(cs, "Dieser aktive Effekt existiert nicht.");
+            return true;
+        }
+        int level = 1;
+        try
+        {               
+            level = Integer.parseInt(arg[1]);
+            if(level <= 0)
+            {
+                this.getModule().send(cs, GlobalText.noPositiveNaturalNumber());
+                return true;
+            }
+        }
+        catch(NumberFormatException | ArrayIndexOutOfBoundsException ex)
+        {
+        }
+        int amount = 1;
+        try
+        {               
+            amount = Integer.parseInt(arg[2]);
+            if(amount <= 0)
+            {
+                this.getModule().send(cs, GlobalText.noPositiveNaturalNumber());
+                return true;
+            }
+        }
+        catch(NumberFormatException | ArrayIndexOutOfBoundsException ex)
+        {
+        }     
+        new ItemStackBuilder(ModItems.scroll, amount).setName(arg[0] + " " + Utils.intToRoman(level)).drop(p.world, p.getPosition());
+        return true;
+    }
+}

+ 72 - 0
src/main/java/me/km/scrolls/ItemScroll.java

@@ -0,0 +1,72 @@
+package me.km.scrolls;
+
+import me.km.KajetansMod;
+import me.km.api.GlobalText;
+import me.km.api.Utils;
+import me.km.effects.ActiveEffectBase;
+import me.km.effects.EffectCause;
+import me.km.effects.EffectUtils;
+import me.km.items.ItemBase;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.stats.StatList;
+import net.minecraft.util.ActionResult;
+import net.minecraft.util.EnumActionResult;
+import net.minecraft.util.EnumHand;
+import net.minecraft.world.World;
+
+public class ItemScroll extends ItemBase
+{
+    public ItemScroll(String name, String local) 
+    {
+        super(name, local);
+    }
+
+    @Override
+    public ActionResult<ItemStack> onItemRightClick(World w, EntityPlayer p, EnumHand hand) 
+    {
+        if(!KajetansMod.worldManager.getWorldPreferences(w).scrolls)
+        {
+            KajetansMod.scrolls.send(p, GlobalText.noScrolls());
+            return new ActionResult(EnumActionResult.FAIL, p.getHeldItem(hand));
+        }
+        
+        ItemStack stack = p.getHeldItem(hand);
+        if (!w.isRemote)
+        {
+            String s = stack.getDisplayName();
+            if(s.contains(" "))
+            {
+                try
+                {
+                    String[] parts = s.split(" ");
+                    if(parts.length < 2)
+                    {
+                        throw new Exception();
+                    }
+                    Class<? extends ActiveEffectBase> c = EffectUtils.getEffectClass(parts[0]);
+                    if(c == null)
+                    { 
+                        throw new Exception();
+                    }
+                    int power = Utils.romanToInt(parts[1]);
+                    if(ActiveEffectBase.executeEffect(c, p, power, 0, EffectCause.SCROLL))
+                    {
+                        if(!p.isCreative())
+                        {
+                            stack.shrink(1);
+                        }
+                    }
+                }
+                catch(Exception ex)
+                {
+                    KajetansMod.scrolls.send(p, "Die Schriftrolle ist korrupt.");
+                    return new ActionResult(EnumActionResult.FAIL, stack);
+                }
+            }
+        }
+
+        p.addStat(StatList.getObjectUseStats(this));
+        return new ActionResult(EnumActionResult.SUCCESS, stack);
+    }
+}

+ 46 - 0
src/main/java/me/km/snuviscript/BenchmarkClock.java

@@ -0,0 +1,46 @@
+package me.km.snuviscript;
+
+public class BenchmarkClock 
+{
+    private long sum;
+    private long l;
+    private boolean b;
+    
+    public BenchmarkClock()
+    {
+        sum = 0;
+        l = 0;
+        b = false;
+    }
+    
+    public void pushTime(boolean bo)
+    {
+        if(!bo)
+        {
+            return;
+        }
+        if(b)
+        {
+            sum += (System.nanoTime() - l) / 1000;
+            b = false;
+            return;
+        }
+        b = true;
+        l = System.nanoTime();
+    }
+    
+    public long getSummedTime()
+    {
+        return sum / 1000;
+    }
+    
+    public long getSummedTimeExact()
+    {
+        return sum;
+    }
+    
+    public void clear()
+    {
+        l = 0;
+    }
+}

+ 126 - 0
src/main/java/me/km/snuviscript/CodeFunction.java

@@ -0,0 +1,126 @@
+package me.km.snuviscript;
+
+import me.km.KajetansMod;
+import me.km.exception.PrescriptException;
+import me.km.exception.NoSuchMethodException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.stream.Collectors;
+
+public class CodeFunction 
+{
+    private int function;
+    private Object[] pars;
+    
+    public CodeFunction(String code)
+    {
+        function = 0;
+        findFunction(code);
+    }
+    
+    private void findFunction(String c)
+    {
+        //System.out.println(c);
+        if(c.equals("")) // Springt in { } ohne Kopf
+        {
+            function = KajetansMod.quest.getQuestParser().translateMethod("godeeper");
+            pars = new Object[0];
+            return;
+        }
+        int pos = c.indexOf("(");
+        if(pos == -1)
+        {
+            throw new PrescriptException("Falsche Syntax bei: "  + c);
+        }
+        try
+        {
+            function = KajetansMod.quest.getQuestParser().translateMethod(c.substring(0, pos).trim().toLowerCase());
+        }
+        catch(NullPointerException ex)
+        {
+            throw new NoSuchMethodException(c.substring(0, pos).trim());
+        }
+        pos++;
+        
+        ArrayList<Object> pars = new ArrayList<>();
+        int last = pos;
+        int counter = 1;
+        boolean string = false;
+        while(pos < c.length())
+        {
+            if(string)
+            {              
+                if(c.charAt(pos) == '"')
+                {
+                    string = !string;
+                }
+                pos++;
+                continue;
+            }
+            switch(c.charAt(pos))
+            {
+                case '"':
+                    string = !string;
+                    break;
+                case ',':
+                    if(last != pos)
+                    {
+                        pars.add(QuestUtils.convertInput(c.substring(last, pos).trim()));
+                    }
+                    last = pos + 1;
+                    break;
+                case ')':
+                    if(counter <= 0)
+                    {
+                        throw new PrescriptException(") ohne (");
+                    }
+                    if(last != pos)
+                    {
+                        pars.add(QuestUtils.convertInput(c.substring(last, pos).trim()));
+                    }
+                    break;
+                case '(':
+                    counter++;
+                    pos++;
+                    while(counter != 1 && pos < c.length())
+                    {
+                        switch(c.charAt(pos))
+                        {
+                            case '(':
+                                counter++;
+                                break;
+                            case ')':
+                                counter--;
+                                break;    
+                        }
+                        pos++;
+                    }
+                    pars.add(new CodeFunction(c.substring(last, pos).trim())); 
+                    while(pos < c.length() && c.charAt(pos) != ',' && c.charAt(pos) != ')')
+                    {
+                        pos++;
+                    }
+                    last = pos + 1;
+                    break;
+            }  
+            pos++;
+        }   
+        this.pars = pars.toArray(new Object[pars.size()]);
+    }
+    
+    public int getFunction()
+    {
+        return function;
+    }
+    
+    public Object[] getParameters()
+    {
+        return pars;
+    }
+
+    @Override
+    public String toString() 
+    {
+        return function + "(" + Arrays.asList(pars).stream().map(o -> String.valueOf(o)).collect(Collectors.joining(", ")) + ")";
+    }
+}

+ 55 - 0
src/main/java/me/km/snuviscript/CommandGiveUp.java

@@ -0,0 +1,55 @@
+package me.km.snuviscript;
+
+import me.km.KajetansMod;
+import me.km.api.GlobalText;
+import me.km.api.Module;
+import me.km.api.ModuleCommand;
+import me.km.exception.GotoLabelNotFoundException;
+import me.km.exception.NoChildTreeException;
+import me.km.permissions.Permissions;
+import net.minecraft.command.ICommandSender;
+import net.minecraft.entity.player.EntityPlayer;
+
+public class CommandGiveUp extends ModuleCommand
+{
+    public CommandGiveUp(Module m) 
+    {
+        super("giveup", m);
+        super.setDescription("Gibt deine Quest auf");
+        super.setUsage("/giveup");
+        super.setPermission(Permissions.GIVEUP);
+    }
+
+    @Override
+    public boolean execute(ICommandSender cs, String[] arg) 
+    {
+        if(!(cs instanceof EntityPlayer))
+        {
+            this.getModule().send(cs, GlobalText.onlyPlayer());
+            return true;
+        }
+        EntityPlayer p = (EntityPlayer) cs;
+        QuestData qd = KajetansMod.quest.getQuestData(p);
+        if(qd == null)
+        {
+            this.getModule().send(cs, "Du hast gerade keine Quest.");
+            return true;
+        }
+        try
+        {
+            qd.gotoLabel("giveup");
+            qd.setVar("event", "player-leave");
+            QuestVars.setPlayerVars(qd, p); 
+            qd.runCode();
+            this.getModule().send(cs, "Du hast deine Quest aufgegeben.");
+            return true;
+        }
+        catch(GotoLabelNotFoundException | NoChildTreeException ex)
+        {
+            KajetansMod.quest.removePlayerFromQuest(p, qd);
+            this.getModule().send(cs, "Du hast deine Quest aufgegeben.");
+            return true;
+        }
+    }
+}
+

+ 254 - 0
src/main/java/me/km/snuviscript/CommandQuest.java

@@ -0,0 +1,254 @@
+package me.km.snuviscript;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import me.km.KajetansMod;
+import me.km.api.GlobalText;
+import me.km.api.Module;
+import me.km.api.ModuleCommand;
+import me.km.api.Utils;
+import me.km.chatmanager.ChatManager;
+import me.km.exception.PlayerNotFoundException;
+import me.km.permissions.Permissions;
+import net.minecraft.command.ICommandSender;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.util.text.TextComponentString;
+
+public class CommandQuest extends ModuleCommand
+{
+    public CommandQuest(Module m) 
+    {
+        super("quest", m);
+        super.setDescription("Zeigt alles über Quests");
+        super.setUsage("/quest für die Hilfe");
+        super.setPermission(Permissions.QUEST);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public boolean execute(ICommandSender cs, String[] arg) 
+    {
+        Module m = this.getModule();
+        if(arg.length >= 1)
+        {
+            final EntityPlayer p;
+            if(cs instanceof EntityPlayer)
+            {
+                p = (EntityPlayer) cs;
+            }
+            else
+            {
+                p = null;
+            }
+            
+            // Abkürzungen
+            switch(arg[0].toLowerCase())
+            {
+                case "s":
+                case "start":
+                {
+                    if(arg.length >= 3)
+                    {
+                        EntityPlayer affectedPlayer;
+                        try
+                        {
+                            affectedPlayer = Utils.getPlayerByName(arg[1]);                   
+                        }
+                        catch(PlayerNotFoundException ex)
+                        {
+                            m.send(cs, GlobalText.cantFindPlayer(arg[1]));
+                            return true;
+                        }                           
+                        KajetansMod.quest.startQuest(cs, arg[Utils.randomInt(2, arg.length-1)], affectedPlayer, null, null);
+                        return true;
+                    }
+                    break;
+                }
+                case "v":
+                case "variable":    
+                {
+                    if(arg.length >= 2 && p != null)
+                    {
+                        QuestData qd = KajetansMod.quest.getQuestData(p);
+                        if(qd == null)
+                        {
+                            m.send(cs, "Du hast gerade keine Quest.");
+                            return true;
+                        }
+                        m.send(cs, arg[1] + " = " + qd.getVar(arg[1]));
+                        return true; 
+                    }
+                    break;
+                }
+                case "n":
+                case "next":
+                {
+                    if(p != null)
+                    {
+                        QuestData qd = KajetansMod.quest.getQuestData(p);
+                        if(qd == null)
+                        {
+                            m.send(cs, "Du hast gerade keine Quest.");
+                            return true;
+                        }
+                        qd.setIsWaiting(false);
+                        qd.runCode();
+                        return true;
+                    }
+                    break;
+                }
+                case "c":
+                case "code":
+                {
+                    if(arg.length >= 2 && p != null)
+                    {
+                        QuestData qd = KajetansMod.quest.getQuestData(p);
+                        if(qd == null)
+                        {
+                            m.send(cs, "Du hast gerade keine Quest.");
+                            return true;
+                        }
+                        qd.runCodeLine(ChatManager.colorMessage(Utils.connectSpaces(arg, 1), p), cs);
+                        return true;
+                    }
+                    break;
+                }
+                case "p":
+                case "printvars":
+                {
+                    if(p != null)
+                    {
+                        QuestData qd = KajetansMod.quest.getQuestData(p);
+                        if(qd == null)
+                        {
+                            m.send(cs, "Du hast gerade keine Quest.");
+                            return true;
+                        }
+                        cs.sendMessage(new TextComponentString(GlobalText.Spacer()));
+                        qd.getVars().entrySet().stream().filter(e -> arg.length < 2 || 
+                                (e.getKey().startsWith(arg[1]))).forEach(e -> 
+                        {
+                            m.send(cs, e.getKey() + " = " + e.getValue());
+                        });                   
+                        return true; 
+                    }
+                    break;
+                }
+                case "pl":
+                case "printlists":
+                {
+                    if(p != null)
+                    {
+                        QuestData qd = KajetansMod.quest.getQuestData(p);
+                        if(qd == null)
+                        {
+                            m.send(cs, "Du hast gerade keine Quest.");
+                            return true;
+                        }
+                        cs.sendMessage(new TextComponentString(GlobalText.Spacer()));
+                        qd.getVars().entrySet().stream()
+                                .filter(e -> e.getValue() instanceof List)
+                                .filter(e -> arg.length < 2 || (e.getKey().startsWith(arg[1])))
+                                .forEach(e -> 
+                        {
+                            m.send(cs, e.getKey());
+                            ((List<Object>) e.getValue()).forEach(l -> m.sendListElement(cs, l.toString()));
+                        });                   
+                        return true; 
+                    }
+                    break;
+                }
+                case "pm":
+                case "printmaps":
+                {
+                    if(p != null)
+                    {
+                        QuestData qd = KajetansMod.quest.getQuestData(p);
+                        if(qd == null)
+                        {
+                            m.send(cs, "Du hast gerade keine Quest.");
+                            return true;
+                        }
+                        cs.sendMessage(new TextComponentString(GlobalText.Spacer()));
+                        qd.getVars().entrySet().stream()
+                                .filter(e -> e.getValue() instanceof Map)
+                                .filter(e -> arg.length < 2 || (e.getKey().startsWith(arg[1]))).forEach(e -> 
+                        {
+                            m.send(cs, e.getKey());
+                            ((Map) e.getValue()).forEach((k, v) -> m.sendListElement(cs, k + " | " + v));
+                        });                   
+                        return true; 
+                    }
+                    break;
+                }
+                case "see":
+                {
+                    Collection<QuestData> quests = KajetansMod.quest.getQuests();
+                    if(quests.isEmpty())
+                    {
+                        m.send(cs, "Momentan sind keine Quests aktiv.");
+                        return true;
+                    }
+                    m.send(cs, "Folgende Quests sind aktiv:");
+                    quests.forEach(data ->
+                    {
+                        m.sendHelpListElement(cs, data.getName() + " (" + data.getId() + ")", String.join(", ", data.getPlayerNames()));
+                    });
+                    return true;
+                }
+                case "get":
+                {
+                    if(p != null && arg.length >= 2)
+                    {
+                        EntityPlayer affectedPlayer;
+                        try
+                        {
+                            affectedPlayer = Utils.getPlayerByName(arg[1]);                   
+                        }
+                        catch(PlayerNotFoundException ex)
+                        {
+                            m.send(cs, GlobalText.cantFindPlayer(arg[1]));
+                            return true;
+                        }
+                        KajetansMod.quest.addPlayerToPlayer(p, affectedPlayer); 
+                        return true;
+                    }
+                    break;
+                }
+                case "term":
+                {
+                    KajetansMod.quest.removeQuests();
+                    m.send(cs, "Alle aktiven Quests wurden beendet.");
+                    return true;
+                }
+                case "stack":
+                {
+                    KajetansMod.quest.getQuestParser().printStack = !KajetansMod.quest.getQuestParser().printStack;
+                    if(KajetansMod.quest.getQuestParser().printStack)
+                    {
+                        m.send(cs, "Exceptions werden nun angezeigt.");
+                        return true;
+                    }
+                    m.send(cs, "Exceptions werden nicht mehr angezeigt.");
+                    return true;
+                }
+            }
+        }
+        
+        m.send(cs, "/quest ...");
+        m.sendHelpListElement(cs, "start <pname> <qn1> [qn2] ...", "Startet eine Quest");
+        m.sendHelpListElement(cs, "variable <name>", "Zeigt den Wert einer Variable");
+        m.sendHelpListElement(cs, "printvars [starts]", "Zeigt alle Variablen");
+        m.sendHelpListElement(cs, "printlists [starts]", "Zeigt alle Listen");
+        m.sendHelpListElement(cs, "printmaps [starts]", "Zeigt alle Maps");
+        m.sendHelpListElement(cs, "next", "Springt in die nächste Codezeile");
+        m.sendHelpListElement(cs, "code <code>", "Führt den angegebenen Code aus");
+        m.sendHelpListElement(cs, "see", "Gibt alle aktiven Quests aus");
+        m.sendHelpListElement(cs, "get <pname>", "Fügt dich der Quest hinzu");
+        m.sendHelpListElement(cs, "term", "Entfernt alle Quests aus dem RAM");
+        m.sendHelpListElement(cs, "stack", "Zeigt Exceptions auf der Konsole");
+        return true;
+    }
+}
+

+ 40 - 0
src/main/java/me/km/snuviscript/CommandQuestInfo.java

@@ -0,0 +1,40 @@
+package me.km.snuviscript;
+
+import me.km.KajetansMod;
+import me.km.api.GlobalText;
+import me.km.api.Module;
+import me.km.api.ModuleCommand;
+import me.km.permissions.Permissions;
+import net.minecraft.command.ICommandSender;
+import net.minecraft.entity.player.EntityPlayer;
+
+public class CommandQuestInfo extends ModuleCommand
+{
+    public CommandQuestInfo(Module m) 
+    {
+        super("questinfo", m);
+        super.setDescription("Gibt dir Information über deine Quest");
+        super.setUsage("/questinfo");
+        super.setPermission(Permissions.QUESTINFO);
+    }
+
+    @Override
+    public boolean execute(ICommandSender cs, String[] arg) 
+    {
+        if(!(cs instanceof EntityPlayer))
+        {
+            this.getModule().send(cs, GlobalText.onlyPlayer());
+            return true;
+        }
+        EntityPlayer p = (EntityPlayer) cs;
+        QuestData data = KajetansMod.quest.getQuestData(p);
+        if(data == null)
+        {
+            this.getModule().send(cs, "Du hast gerade keine Quest.");
+            return true;
+        }
+        this.getModule().send(cs, data.getInfo());
+        return true;
+    }
+}
+

+ 255 - 0
src/main/java/me/km/snuviscript/CommandScript.java

@@ -0,0 +1,255 @@
+package me.km.snuviscript;
+
+import me.km.KajetansMod;
+import me.km.api.GlobalText;
+import me.km.api.Module;
+import me.km.api.ModuleCommand;
+import me.km.api.Utils;
+import me.km.chatmanager.ChatManager;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import me.km.permissions.Permissions;
+import net.minecraft.command.ICommandSender;
+import net.minecraft.util.text.TextComponentString;
+
+public class CommandScript extends ModuleCommand
+{
+    public CommandScript(Module m) 
+    {
+        super("script", m);
+        super.setDescription("Zeigt alles über Scripts");
+        super.setUsage("/script für die Hilfe");
+        super.setPermission(Permissions.SCRIPT);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public boolean execute(ICommandSender cs, String[] arg) 
+    {
+        Module m = this.getModule();
+        if(arg.length >= 1)
+        {
+            switch(arg[0].toLowerCase())
+            {
+                case "s":
+                case "start":
+                {
+                    if(arg.length >= 2)
+                    {
+                        KajetansMod.quest.startScript(cs, arg[1]);
+                        return true;
+                    }
+                    break;
+                }
+                case "v":
+                case "variable":    
+                {
+                    if(arg.length >= 3)
+                    {
+                        try
+                        {
+                            QuestData qd = KajetansMod.quest.getScript(Integer.parseInt(arg[1]));
+                            if(qd == null)
+                            {
+                                throw new NumberFormatException();
+                            }
+                            m.send(cs, arg[2] + " = " + qd.getVar(arg[2]), 1);
+                        }
+                        catch(NumberFormatException ex)
+                        {
+                            m.send(cs, "'" + arg[1] + "' ist keine gültige ID.", 1);
+                        }
+                        return true;
+                    }
+                    break;
+                }
+                case "n":
+                case "next":
+                {
+                    if(arg.length >= 2)
+                    {
+                        try
+                        {
+                            QuestData qd = KajetansMod.quest.getScript(Integer.parseInt(arg[1]));
+                            if(qd == null)
+                            {
+                                throw new NumberFormatException();
+                            }
+                            qd.setIsWaiting(false);
+                            qd.runCode();
+                        }
+                        catch(NumberFormatException ex)
+                        {
+                            m.send(cs, "'" + arg[1] + "' ist keine gültige ID.", 1);
+                        } 
+                        return true;
+                    }
+                    break;
+                }
+                case "c":
+                case "code":
+                {
+                    if(arg.length >= 3)
+                    {
+                        try
+                        {
+                            QuestData qd = KajetansMod.quest.getScript(Integer.parseInt(arg[1]));
+                            if(qd == null)
+                            {
+                                throw new NumberFormatException();
+                            }
+                            qd.runCodeLine(ChatManager.colorMessage(Utils.connectSpaces(arg, 2), cs), cs);
+                        }
+                        catch(NumberFormatException ex)
+                        {
+                            m.send(cs, "'" + arg[1] + "' ist keine gültige ID.", 1);
+                        } 
+                        return true;
+                    }
+                    break;
+                }
+                case "p":
+                case "printvars":
+                {
+                    if(arg.length >= 2)
+                    {
+                        try
+                        {
+                            QuestData qd = KajetansMod.quest.getScript(Integer.parseInt(arg[1]));
+                            if(qd == null)
+                            {
+                                throw new NumberFormatException();
+                            }
+                            cs.sendMessage(new TextComponentString(GlobalText.Spacer()));
+                            qd.getVars().entrySet().stream().filter(e -> arg.length < 3 || 
+                                (e.getKey().startsWith(arg[2]))).forEach(e -> 
+                            {
+                                m.send(cs, e.getKey() + " = " + e.getValue(), 1);
+                            }); 
+                        }
+                        catch(NumberFormatException ex)
+                        {
+                            m.send(cs, "'" + arg[1] + "' ist keine gültige ID.", 1);
+                        } 
+                        return true;
+                    }
+                    break;
+                }
+                case "pl":
+                case "printlists":
+                {
+                    if(arg.length >= 2)
+                    {
+                        try
+                        {
+                            QuestData qd = KajetansMod.quest.getScript(Integer.parseInt(arg[1]));
+                            if(qd == null)
+                            {
+                                throw new NumberFormatException();
+                            }
+                            cs.sendMessage(new TextComponentString(GlobalText.Spacer()));
+                            qd.getVars().entrySet().stream()
+                                    .filter(e -> e.getValue() instanceof List)
+                                    .filter(e -> arg.length < 3 || (e.getKey().startsWith(arg[2]))).forEach(e -> 
+                            {
+                                m.send(cs, e.getKey());
+                                ((List<Object>) e.getValue()).forEach(l -> m.sendListElement(cs, l.toString()));
+                            });   
+                        }
+                        catch(NumberFormatException ex)
+                        {
+                            m.send(cs, "'" + arg[1] + "' ist keine gültige ID.", 1);
+                        }
+                        return true;
+                    }
+                    break;
+                }
+                case "pm":
+                case "printmaps":
+                {
+                    if(arg.length >= 2)
+                    {
+                        try
+                        {
+                            QuestData qd = KajetansMod.quest.getScript(Integer.parseInt(arg[1]));
+                            if(qd == null)
+                            {
+                                throw new NumberFormatException();
+                            }
+                            cs.sendMessage(new TextComponentString(GlobalText.Spacer()));
+                            qd.getVars().entrySet().stream()
+                                    .filter(e -> e.getValue() instanceof Map)
+                                    .filter(e -> arg.length < 3 || (e.getKey().startsWith(arg[2]))).forEach(e -> 
+                            {
+                                m.send(cs, e.getKey());
+                                ((Map) e.getValue()).forEach((k, v) -> m.sendListElement(cs, k + " | " + v));
+                            });                   
+                        }
+                        catch(NumberFormatException ex)
+                        {
+                            m.send(cs, "'" + arg[1] + "' ist keine gültige ID.", 1);
+                        }
+                        return true;
+                    }
+                    break;
+                }
+                case "see":
+                {
+                    Collection<QuestData> scripts = KajetansMod.quest.getScripts();
+                    if(scripts.isEmpty())
+                    {
+                        m.send(cs, "Momentan sind keine Scripts aktiv.", 1);
+                        return true;
+                    }
+                    m.send(cs, "Folgende Scripts sind aktiv:", 1);
+                    scripts.forEach(data ->
+                    {
+                        m.sendHelpListElement(cs, data.getName(), "(" + data.getId() + ")", 1);
+                    });
+                    return true;
+                }
+                case "term":
+                {
+                    if(arg.length >= 2)
+                    {
+                        try
+                        {
+                            if(arg[1].equals("all"))
+                            {
+                                KajetansMod.quest.removeScripts();
+                                m.send(cs, "Alle aktiven Scripts wurden beendet.", 1);
+                                return true;
+                            }
+                            QuestData qd = KajetansMod.quest.getScript(Integer.parseInt(arg[1]));
+                            if(!KajetansMod.quest.term(qd))
+                            {
+                                throw new NumberFormatException();
+                            }
+                            m.send(cs, "Das Script '" + qd.getName() + "' wurde beendet.", 1);
+                        }
+                        catch(NumberFormatException ex)
+                        {
+                            m.send(cs, "'" + arg[1] + "' ist keine gültige ID.", 1);
+                        } 
+                        return true;
+                    }
+                    break;
+                }
+            }
+        }
+        
+        m.send(cs, "/script ...", 1);
+        m.sendHelpListElement(cs, "start <script>", "Startet ein Script", 1);
+        m.sendHelpListElement(cs, "variable <id> <name>", "Zeigt den Wert einer Variable", 1);       
+        m.sendHelpListElement(cs, "printvars <id> [starts]", "Zeigt alle Variablen", 1);
+        m.sendHelpListElement(cs, "printlists <id> [starts]", "Zeigt alle Listen");
+        m.sendHelpListElement(cs, "printmaps <id> [starts]", "Zeigt alle Maps");
+        m.sendHelpListElement(cs, "next <id>", "Springt in die nächste Codezeile", 1);
+        m.sendHelpListElement(cs, "code <id> <code", "Führt den angegebenen Code aus", 1);
+        m.sendHelpListElement(cs, "see", "Gibt alle aktiven Scripts aus", 1);
+        m.sendHelpListElement(cs, "term <id/all>", "Entfernt Scripts aus dem RAM", 1);
+        return true;
+    }
+}
+

+ 256 - 0
src/main/java/me/km/snuviscript/QuestAPI.java

@@ -0,0 +1,256 @@
+package me.km.snuviscript;
+
+import me.km.KajetansMod;
+import me.km.api.Module;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.UUID;
+import net.minecraft.command.ICommandSender;
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.util.text.TextFormatting;
+
+public class QuestAPI extends Module
+{
+    private final QuestParser qparser;
+    
+    private int idCounter;
+    
+    private final HashMap<Integer, QuestData> questData;
+    private final HashMap<UUID, Integer> playerQuestID;
+    
+    private final HashMap<Integer, QuestData> scripts;
+    
+    public QuestAPI(String mname, String prefix, TextFormatting color) 
+    {
+        super(mname, prefix, color);
+        
+        questData = new HashMap<>();
+        playerQuestID = new HashMap<>();
+        scripts = new HashMap<>();
+        idCounter = 0;
+        qparser = new QuestParser();
+    }
+       
+    // -------------------------------------------------------------------------
+    // QuestParser
+    // -------------------------------------------------------------------------
+    
+    public QuestParser getQuestParser()
+    {
+        return qparser;
+    }
+    
+    // -------------------------------------------------------------------------
+    // Quest - Control
+    // -------------------------------------------------------------------------
+
+    private void addPlayerToQuest(EntityPlayer p, EntityPlayer p2, QuestData qd)
+    {             
+        if(hasQuest(p))
+        {
+            this.send(p, "Du hast bereits eine Quest.");;
+            return;
+        }       
+        if(qd == null)
+        {
+            if(p2 != null)
+            {
+                this.send(p, p2.getName() + " hat keine aktive Quest.");
+            }
+            return;
+        }
+        if(qd.getBoolean("no-join"))
+        {
+            this.send(p, "Du darfst dieser Quest nicht beitreten.");
+            return;
+        }
+        playerQuestID.put(p.getUniqueID(), qd.getId());
+        qd.addPlayer(p);
+        if(!qd.getBoolean("no-quest-msg"))
+        {
+            qparser.sendMessageWithSuffix(qd, p.getName() + " ist deiner Quest beigetreten.");
+            if(p2 != null)
+            {
+                this.send(p, "Du bist nun in der Quest von " + p2.getName() + ".");
+            }
+            else
+            {
+                this.send(p, "Du bist einer bereits laufenden Quest beigetreten.");
+            }
+        }
+        qd.setVar("event", "player-join");
+        QuestVars.setPlayerVars(qd, p); 
+        qd.runCode();
+    } 
+    
+    public void addPlayerToPlayer(EntityPlayer p, EntityPlayer p2)
+    {             
+        addPlayerToQuest(p, p2, getQuestData(p2)); 
+    } 
+    
+    public void addPlayerToQuest(EntityPlayer p, QuestData qd)
+    {             
+        addPlayerToQuest(p, null, qd);  
+    } 
+    
+    public Collection<QuestData> getQuests()
+    {
+        return questData.values();
+    }
+    
+    public boolean hasQuest(EntityPlayer p)
+    {
+        return playerQuestID.containsKey(p.getUniqueID());
+    }
+    
+    public QuestData getQuestData(EntityPlayer p)
+    {
+        Integer id = playerQuestID.get(p.getUniqueID());
+        if(id == null)
+        {
+            return null;
+        }
+        QuestData qd = questData.get(id);
+        if(qd == null)
+        {
+            qparser.sendWarningToAllDevs("Die ungültige Quest von '" + p.getName() + "' wurde entfernt.");
+            return null;
+        }
+        return qd;
+    }
+    
+    public void removePlayerFromQuest(EntityPlayer p, QuestData qd)
+    {
+        if(qd == null)
+        {
+            return;
+        }
+        if(qd.removePlayer(p))
+        {
+            qd.setIsValid(false);
+            questData.remove(qd.getId());
+        }       
+        playerQuestID.remove(p.getUniqueID());
+    }
+    
+    public void removeQuests()
+    {
+        questData.values().forEach(qd -> qd.setIsValid(false));
+        questData.clear();
+        playerQuestID.clear();
+    }
+    
+    // -------------------------------------------------------------------------
+    // Script - Control
+    // -------------------------------------------------------------------------
+
+    public QuestData getScript(int id)
+    {
+        return scripts.get(id);
+    }
+    
+    public void removeScripts()
+    {
+        scripts.values().forEach(qd -> qd.setIsValid(false));
+        scripts.clear();
+    }
+    
+    public Collection<QuestData> getScripts()
+    {
+        return scripts.values();
+    }
+    
+    // -------------------------------------------------------------------------
+    // Quest / Script - Control
+    // -------------------------------------------------------------------------
+    
+    public void add(QuestData qd, EntityPlayer p)
+    {
+        if(p != null)
+        {
+            questData.put(idCounter, qd);
+            playerQuestID.put(p.getUniqueID(), qd.getId());
+        }
+        else
+        {
+            scripts.put(idCounter, qd);
+        }
+        idCounter++;
+    }
+    
+    public boolean term(QuestData qd)
+    {
+        if(qd == null)
+        {
+            return false;
+        }
+        qd.setIsValid(false);
+        int id = qd.getId();
+        if(qd.isScript())
+        {
+            KajetansMod.scheduler.scheduleTask(() -> scripts.remove(id), 0);
+            return scripts.containsKey(id);
+        }
+        qd.getPlayers().stream().forEach((player) -> 
+        {
+            playerQuestID.remove(player.getUniqueID());          
+        });
+        questData.remove(id);
+        return true;
+    }
+
+    // -----------------------
+    // Quests / Scripts loading and starting
+    // -----------------------
+    
+    public void startQuest(ICommandSender sender, String chosenQuest, EntityPlayer p, Entity executor, String forcedQuest)
+    {
+        /*List<String> contents = getQuestCode(sender, chosenQuest, false);
+        if(contents == null)
+        {                       
+            return;
+        }         */   
+        if(hasQuest(p))
+        {
+            this.send(sender, "Du hast bereits eine Quest.");
+            return;
+        }
+        QuestData other = getQuests().stream()
+                .filter(qd -> qd.getVar("add-if-name") != null)
+                .filter(qd -> qd.getVar("add-if-name").equals(chosenQuest))
+                .findAny().orElse(null);
+        if(other != null)
+        {            
+            addPlayerToQuest(p, other);
+            return;
+        }
+        QuestData qd = new QuestData(idCounter, p, chosenQuest, sender);
+        QuestVars.setPlayerVars(qd, p); 
+        if(executor != null)
+        {
+            QuestVars.setEntityVars(qd, executor);
+        }
+        if(forcedQuest != null)
+        {
+            qd.setVar("force", forcedQuest);
+        }
+        add(qd, p);
+        qd.runCode();
+    }
+    
+    public void startScript(ICommandSender sender, String chosenQuest)
+    {
+        /*List<String> contents = getQuestCode(sender, chosenQuest, true);
+        if(contents == null)
+        {                       
+            return;
+        }          */  
+        QuestData qd = new QuestData(idCounter, null, chosenQuest, sender);
+        add(qd, null);
+        if(qd.isValid())
+        {
+            qd.runCode();
+        }
+    }
+}

+ 159 - 0
src/main/java/me/km/snuviscript/QuestBank.java

@@ -0,0 +1,159 @@
+package me.km.snuviscript;
+
+import me.km.api.Module;
+import me.km.databank.SimpleDataBank;
+import java.sql.Connection;
+import me.km.table.Table;
+
+public class QuestBank extends SimpleDataBank
+{   
+    public QuestBank(Module m, Connection c) 
+    {
+        super(m, c);
+    }
+    
+    @Override
+    protected void init() 
+    {
+        // Globale Variablen
+        if(!this.doesTableExist("questdata"))
+        {
+            this.getModule().sendToConsole("Die Quest-Var-Datenbank wurde nicht gefunden, erstelle ...");
+            if(this.update("CREATE TABLE IF NOT EXISTS questdata ("
+                + "id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, "
+                + "player_id INT NOT NULL, "
+                + "var VARCHAR(20) NOT NULL, "
+                + "value VARCHAR(256) NOT NULL, "
+                + "UNIQUE INDEX (player_id,var), "
+                + "FOREIGN KEY (player_id) REFERENCES minecraft.players(id) ON DELETE RESTRICT);", false))
+            {
+                this.getModule().sendToConsole("Die Quest-Var-Datenbank wurde erstellt.");
+            }
+        }
+        else
+        {
+            this.getModule().sendToConsole("Die Quest-Var-Datenbank wurde gefunden.");
+        }
+        
+        // Globale Maps
+        if(!this.doesTableExist("questmaps"))
+        {
+            this.getModule().sendToConsole("Die Quest-Map-Datenbank wurde nicht gefunden, erstelle ...");
+            if(this.update("CREATE TABLE IF NOT EXISTS questmaps ("
+                + "id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, "
+                + "map VARCHAR(255) NOT NULL, "
+                + "keyname VARCHAR(255) NOT NULL, "
+                + "value VARCHAR(255) NOT NULL, "
+                + "INDEX (map), "
+                + "UNIQUE INDEX (map,keyname));", false))
+            {
+                this.getModule().sendToConsole("Die Quest-Map-Datenbank wurde erstellt.");
+            }
+        }
+        else
+        {
+            this.getModule().sendToConsole("Die Quest-Map-Datenbank wurde gefunden.");
+        }
+        
+        // Doppelte Globale Maps
+        if(!this.doesTableExist("questdualmaps"))
+        {
+            this.getModule().sendToConsole("Die Quest-Dual-Map-Datenbank wurde nicht gefunden, erstelle ...");
+            if(this.update("CREATE TABLE IF NOT EXISTS questdualmaps ("
+                + "id INT NOT NULL AUTO_INCREMENT PRIMARY KEY, "
+                + "map VARCHAR(255) NOT NULL, "
+                + "keyname VARCHAR(255) NOT NULL, "
+                + "seckeyname VARCHAR(255) NOT NULL, "
+                + "value VARCHAR(255) NOT NULL, "
+                + "INDEX (map), "
+                + "UNIQUE INDEX (map,keyname,seckeyname));", false))
+            {
+                this.getModule().sendToConsole("Die Quest-Dual-Map-Datenbank wurde erstellt.");
+            }
+        }
+        else
+        {
+            this.getModule().sendToConsole("Die Quest-Dual-Map-Datenbank wurde gefunden.");
+        }
+    }
+
+    public void setVar(String value, String var, String uuid)
+    {
+        this.update("INSERT INTO questdata (player_id,var,value) "
+                + "SELECT id, '" + var + "', '" + value + "' "
+                + "FROM players WHERE uuid='" + uuid + "' "
+                + "ON DUPLICATE KEY UPDATE questdata.value='" + value + "';", false);
+    }
+
+    public Object getVar(String var, String uuid)
+    {
+        return QuestUtils.convertInput(this.getFirst("SELECT value from questdata "
+                + "LEFT JOIN players ON players.id = questdata.player_id "
+                + "WHERE players.uuid = '" + uuid + "' AND "
+                + "questdata.var='" + var + "';", String.class));
+    }    
+    
+    public void addMapElement(String map, String key, String value)
+    {
+        this.update("INSERT INTO questmaps (map,keyname,value) "
+                + "SELECT '" + map + "', '" + key + "', '" + value + "' "
+                + "ON DUPLICATE KEY UPDATE value='" + value + "';", false);
+    }
+    
+    public void removeMapElement(String map, String key)
+    {
+        this.update("DELETE FROM questmaps WHERE "
+                + "map = '" + map + "' AND "
+                + "keyname = '" + key + "';", false);
+    }
+    
+    public void removeMap(String map)
+    {
+        this.update("DELETE FROM questmaps WHERE map = '" + map + "';", false);
+    }
+    
+    public Table getGlobalMapAsTable(String map)
+    {
+        return this.getTable("SELECT keyname,value from questmaps WHERE map = '" + map + "'");
+    }
+
+    public Object getMapValue(String map, String key)
+    {
+        return QuestUtils.convertInput(this.getFirst("SELECT value from questmaps "
+                + "WHERE map = '" + map + "' AND "
+                + "keyname='" + key + "';", String.class));
+    }  
+    
+    public void addDualMapElement(String map, String key, String key2, String value)
+    {
+        this.update("INSERT INTO questdualmaps (map,keyname,seckeyname,value) "
+                + "SELECT '" + map + "', '" + key + "', '" + key2 + "', '" + value + "' "
+                + "ON DUPLICATE KEY UPDATE value='" + value + "';", false);
+    }
+    
+    public void removeDualMapElement(String map, String key, String key2)
+    {
+        this.update("DELETE FROM questdualmaps WHERE "
+                + "map = '" + map + "' AND "
+                + "seckeyname= '" + key + "' AND "
+                + "keyname = '" + key2 + "';", false);
+    }
+    
+    public void removeDualMap(String map)
+    {
+        this.update("DELETE FROM questdualmaps WHERE map = '" + map + "';", false);
+    }
+    
+    public Table getGlobalDualMapAsTable(String map, String key)
+    {
+        return this.getTable("SELECT seckeyname,value from questdualmaps WHERE map = '" + map + "' AND keyname = '" + key + "';");
+    }
+
+    public Object getDualMapValue(String map, String key, String key2)
+    {
+        return QuestUtils.convertInput(this.getFirst("SELECT value from questdualmaps "
+                + "WHERE map = '" + map + "' AND "
+                + "keyname = '" + key + "' AND "
+                + "seckeyname='" + key2 + "';", String.class));
+    } 
+}

+ 833 - 0
src/main/java/me/km/snuviscript/QuestData.java

@@ -0,0 +1,833 @@
+package me.km.snuviscript;
+
+import me.km.KajetansMod;
+import me.km.api.GlobalText;
+import me.km.api.Module;
+import me.km.exception.CodeTooLongException;
+import me.km.exception.GoHigherAtRootTreeException;
+import me.km.exception.GotoLabelNotFoundException;
+import me.km.exception.HoldCodeException;
+import me.km.exception.IllegalStringException;
+import me.km.exception.NoChildTreeException;
+import me.km.exception.PrescriptException;
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.EmptyStackException;
+import java.util.List;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Stack;
+import java.util.stream.Collectors;
+import me.km.api.Location;
+import net.minecraft.command.ICommandSender;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.util.text.TextComponentString;
+
+public class QuestData 
+{
+    private final QuestParser parser;
+    
+    private final BenchmarkClock treeClock;
+    public final BenchmarkClock exeClock;
+    private boolean benchmark;
+    
+    private final Tree<CodeFunction> code;
+    private CodeFunction currentCode;
+
+    private final HashMap<String, Object> variables;    
+    private final HashMap<String, Integer[]> gotos;
+    private final Stack<Integer[]> stack;
+    private final HashSet<String> events;
+    private final HashSet<Location> loadedLocations;
+    
+    private boolean isWaiting;
+    private boolean isTrying;
+    private boolean tryFailed;
+    private boolean shouldDoElse;
+    private boolean isValid;
+    private final Stack<Boolean> whileStack;
+    private int overflowProtection;
+    
+    private String info;  
+    private String name; 
+    private final int id;
+    private final boolean script;
+    private int idCounter;
+ 
+    private final ArrayList<EntityPlayer> quester;
+    private final ArrayList<EntityPlayer> offquester;      
+    
+    public QuestData(int id, EntityPlayer p, String filename, ICommandSender cs)
+    {       
+        parser = KajetansMod.quest.getQuestParser();
+        treeClock = new BenchmarkClock();
+        exeClock = new BenchmarkClock();
+        benchmark = false;
+        
+        this.id = id;     
+        
+        code = new Tree<>();
+        idCounter = 0;
+        
+        variables = new HashMap<>();
+        gotos = new HashMap<>();
+        stack = new Stack<>();
+        events = new HashSet<>();
+        loadedLocations = new HashSet<>();
+        whileStack = new Stack<>();
+        
+        name = filename;
+
+        isWaiting = false;
+        isValid = true;
+        overflowProtection = 0;
+        isTrying = false;
+        
+        quester = new ArrayList<>();
+        offquester = new ArrayList<>();
+        
+        info = "Keine Info wurde gesetzt.";
+        
+        if(p != null)
+        {
+            quester.add(p);
+            script = false;
+        }
+        else
+        {
+            script = true;
+        }
+        
+        readCode(filename, cs);
+        setVar("quote", "\"");
+    }
+    
+    // -------------------------------------------------------------------------
+    // Überladen
+    // -------------------------------------------------------------------------
+    
+    public void loadNewCode(String filename, ICommandSender cs)
+    {
+        code.clear();
+        
+        gotos.clear();
+        stack.clear();
+        whileStack.clear();
+        
+        name = filename;
+        
+        readCode(filename, cs);
+
+        isWaiting = false;
+        isValid = true;
+    }
+    
+    // -------------------------------------------------------------------------
+    // Flow-Control
+    // -------------------------------------------------------------------------
+    
+    public void goDeeper(boolean b) throws NoChildTreeException
+    {
+        code.goDeeper();
+        addWhileMode(b);
+    }
+    
+    public void goHigher()
+    {
+        try
+        {
+            code.goHigher();
+        }
+        catch(GoHigherAtRootTreeException ex)
+        {
+        }
+    }
+    
+    public String getTreePath()
+    {
+        return Arrays.asList(code.getCurrentPosition()).stream().map(i -> i.toString()).collect(Collectors.joining(", "));
+    }
+    
+    private void readCode(String filename, ICommandSender cs)
+    {
+        int line = 0;
+        StringBuilder codeLine = new StringBuilder();
+        try
+        {
+            File file = new File("./quests/" + filename + ".txt");   
+            List<String> lines;
+            if(file.exists())
+            {
+                try 
+                {
+                    lines = Files.readAllLines(file.toPath());
+                } 
+                catch (IOException ex) 
+                {
+                    throw new PrescriptException("Die Datei '" + filename + "' kann nicht gelesen werden.");
+                }
+            }
+            else
+            {
+                //System.out.println("Die Datei '" + filename + "' wurde nicht gefunden.");
+                throw new PrescriptException("Die Datei '" + filename + "' wurde nicht gefunden.");
+            }
+
+            int i = 0;
+            int old = 0;
+            int bracket = 0;
+            boolean text = false;
+            boolean bracketEnd = false;
+            int bracketEndPos = 0;
+            boolean oneLineChild = false;
+            boolean comment = false;       
+            int commentPos = 0;
+            while(line < lines.size())
+            {
+                if(old >= codeLine.length() && !comment)
+                {
+                    codeLine = new StringBuilder(lines.get(line).trim());
+                    i = 0;
+                    old = 0;
+                    //System.out.println("NEU");
+                }
+                else
+                {
+                    codeLine.append(lines.get(line).trim());
+                    //System.out.println("APPEND");
+                }
+
+                //System.out.println(codeLine);
+
+                while(i < codeLine.length())
+                {
+                    switch(codeLine.charAt(i))
+                    {
+                        case '"':
+                            if(!comment)
+                            {
+                                text = !text;
+                            }
+                            break;
+                        case '@':
+                            if(comment || text)
+                            {
+                                break;
+                            }
+                            old = i;
+                            while(i < codeLine.length() && codeLine.charAt(i) != ' ')
+                            {
+                                i++;
+                            }
+                            try
+                            {
+                                code.selectLastChild(); // Nur für richtige Position
+                            }
+                            catch(NoChildTreeException ex)
+                            {
+                            }
+                            String s = codeLine.substring(old + 1, i);
+                            if(gotos.put(s, code.getCurrentPosition()) != null)
+                            {
+                                throw new PrescriptException("Doppeltes Goto: " + s + " - " + (line + 1));
+                            }
+                            codeLine.delete(old, i + 1);
+                            i = old - 1;
+                            break;
+                        case '/':
+                            if(text)
+                            {
+                                break;
+                            }
+                            if(!comment && i + 1 < codeLine.length() && codeLine.charAt(i + 1) == '*')
+                            {
+                                comment = true;
+                                commentPos = i;
+                            }
+                            if(!comment && i + 1 < codeLine.length() && codeLine.charAt(i + 1) == '/')
+                            {
+                                codeLine.delete(i, codeLine.length());
+                                old = codeLine.length() + 1;
+                            }
+                            break;
+                        case '*':
+                            if(text)
+                            {
+                                break;
+                            }
+                            if(comment && i + 1 < codeLine.length() && codeLine.charAt(i + 1) == '/')
+                            {
+                                comment = false;
+                                codeLine.delete(commentPos, i + 2);
+                                i = commentPos;
+                            }
+                            break;
+                        case ';':
+                            if(comment || text)
+                            {
+                                break;
+                            }
+                            if(bracket != 0)
+                            {
+                                //System.out.println(bracket);
+                                throw new PrescriptException("; aber Klammern ungültig - " + (line + 1));
+                            }
+                            bracketEnd = false;
+                            code.addChild(new CodeFunction(codeLine.substring(old, i)), line);
+                            old = i + 1;
+                            if(oneLineChild)
+                            {
+                                oneLineChild = false;
+                                try
+                                {
+                                    code.goHigher();
+                                }
+                                catch(GoHigherAtRootTreeException ex)
+                                {
+                                    throw new PrescriptException("Das sollte nicht passieren - " + (line + 1));
+                                }
+                            }
+                            bracket = 0;
+                            break;
+                        case '(':
+                            if(!comment && !text)
+                            {
+                                bracket++;
+                                if(bracketEnd && !oneLineChild)
+                                {
+                                    bracketEnd = false;
+                                    oneLineChild = true;
+                                    code.addChild(new CodeFunction(codeLine.substring(old, bracketEndPos)), line);
+                                    old = bracketEndPos;
+                                    try
+                                    {
+                                        code.selectLastChild();
+                                        code.goDeeper();
+                                    }
+                                    catch(NoChildTreeException ex)
+                                    {
+                                        throw new PrescriptException("Das sollte nicht passieren - " + (line + 1));
+                                    }
+                                }
+                            }
+                            break;
+                        case ')':
+                            if(comment || text)
+                            {
+                                break;
+                            }
+                            bracket--;
+                            if(oneLineChild)
+                            {
+                                break;
+                            }
+                            if(bracket < 0)
+                            {
+                                throw new PrescriptException(") ohne ( - " + (line + 1));
+                            }
+                            if(bracket == 0)
+                            {
+                                bracketEnd = true;   
+                                bracketEndPos = i + 1;
+                            }
+                            break;
+                        case '{':
+                            if(comment || text)
+                            {
+                                break;
+                            }
+                            bracketEnd = false;
+                            code.addChild(new CodeFunction(codeLine.substring(old, i)), line);
+                            old = i + 1;
+                            try
+                            {
+                                code.selectLastChild();
+                                code.goDeeper();
+                            }
+                            catch(NoChildTreeException ex)
+                            {
+                                throw new PrescriptException("Das sollte nicht passieren - " + (line + 1));
+                            }
+                            break;
+                        case '}':
+                            if(comment || text)
+                            {
+                                break;
+                            }
+                            old = i + 1;
+                            try
+                            {
+                                code.goHigher();
+                            }
+                            catch(GoHigherAtRootTreeException ex)
+                            {
+                                throw new PrescriptException("} ohne { - " + (line + 1));
+                            }
+                            break;
+                    }
+                    i++;
+                } 
+                line++;
+            }
+            code.goToRoot();
+        }
+        catch(Exception ex)
+        {
+            if(parser.printStack)
+            {
+                ex.printStackTrace();
+            }
+            resetOverflowProtection();
+            Module m = KajetansMod.quest;
+            int id = script ? 1 : 0;
+            m.send(cs, "§cError in", id);
+            m.sendHelpListElement(cs, "§cScript", getName(), id);
+            m.sendHelpListElement(cs, "§cZeile", codeLine.toString(), id);
+            m.sendHelpListElement(cs, "§cZeilennummer", String.valueOf(line + 1), id);
+            if(ex.getLocalizedMessage() == null)
+            {
+                m.sendHelpListElement(cs, "§cException", ex.getClass().getSimpleName(), id);
+            }
+            else
+            {
+                m.sendHelpListElement(cs, "§cException", ex.getClass().getSimpleName() + " - " + ex.getLocalizedMessage(), id);
+            }
+            if(ex instanceof IllegalStringException)
+            {
+                m.sendHelpListElement(cs, "§cUngültiger Wert", ((IllegalStringException) ex).getBadString(), id);
+            }
+            KajetansMod.quest.term(this);
+        }
+    }
+    
+    public void runCode()
+    {
+        try
+        { 
+            while(!isWaiting)
+            {
+                treeClock.pushTime(benchmark);
+                try
+                {
+                    //System.out.println("NEXT");
+                    code.selectNextChild();
+                }
+                catch(NoChildTreeException ex)
+                {
+                    //System.out.println("NACH OBEN!");
+                    code.goHigher();
+                    setTryMode(false);
+                    setElseMode(false);
+                    if(removeWhileMode())
+                    {
+                        increaseOverflowProtection();
+                        code.selectPreviousChild();
+                    }
+                    treeClock.pushTime(benchmark);
+                    continue;
+                }
+                currentCode = code.getCurrentChildData();
+                treeClock.pushTime(benchmark);
+                //System.out.println(currentCode.getFunction());
+                //System.out.println(currentCode);
+                //System.out.println("AUFRUF");
+                //findFunction(currentCode, 0, currentCode.length() - 1);
+                doFunction(currentCode);
+            }
+        }
+        catch(NoChildTreeException | GoHigherAtRootTreeException ex)
+        {
+            KajetansMod.quest.term(this);
+        }
+        catch(HoldCodeException ex2)
+        {
+        }
+        catch(CodeTooLongException | PrescriptException ex3)
+        {
+            parser.printQuestException(this, ex3, currentCode.toString());
+        }
+    }
+    
+    public void runCodeLine(String c, ICommandSender cs)
+    {
+        try
+        {
+            doFunction(new CodeFunction(c));
+        }
+        catch(HoldCodeException ex)
+        {
+        }
+        catch(Exception ex)
+        {
+            if(ex instanceof IllegalStringException)
+            {
+                KajetansMod.quest.send(cs, ((IllegalStringException) ex).getBadString(), script ? 1 : 0);
+            }
+            else
+            {
+                KajetansMod.quest.send(cs, ex.getClass().getSimpleName(), script ? 1 : 0);
+            }
+        }
+    }   
+    
+    private Object doFunction(CodeFunction cf)
+    {
+        Object[] old = cf.getParameters();
+        Object[] newPars = new Object[old.length];
+        for(int i = 0; i < old.length; i++)
+        {
+            if(old[i] != null && old[i].getClass() == CodeFunction.class)
+            {
+                newPars[i] = readParameter(doFunction((CodeFunction) old[i]));
+                continue;
+            }
+            newPars[i] = readParameter(old[i]);
+        }
+        return parser.parseFunction(this, cf.getFunction(), newPars);
+    }  
+    
+    private Object readParameter(Object o)
+    {
+        if(o != null && o.getClass() == Variable.class)
+        {
+            return getVar(o.toString());
+        }
+        return o;
+    }
+    
+    public int getActualCodeLine()
+    {
+        return code.getActualCodeLine();
+    }
+
+    // -------------------------------------------------------------------------
+    // Quest / Scriptdaten
+    // -------------------------------------------------------------------------
+    
+    public boolean isScript() 
+    {
+        return script; 
+    }
+    
+    public int getId() 
+    {
+        return id;
+    }
+    
+    public String getInfo() 
+    {
+        return info;
+    }
+    
+    public void setInfo(String t) 
+    {
+        info = t;
+    }
+
+    public String getName() 
+    {
+        return name;
+    }
+    
+    // -------------------------------------------------------------------------
+    // Wait für Zeitutils
+    // -------------------------------------------------------------------------
+    
+    public boolean isWaiting()
+    {
+        return isWaiting;
+    }
+    
+    public void setIsWaiting(boolean b) 
+    {
+        isWaiting = b;
+    }
+    
+    // -------------------------------------------------------------------------
+    // Valid
+    // -------------------------------------------------------------------------
+    
+    public boolean isValid() 
+    {
+        return isValid;
+    }
+    
+    public void setIsValid(Boolean bool) 
+    {
+        isValid = bool;
+    }
+    
+    // -------------------------------------------------------------------------
+    // Variablen
+    // -------------------------------------------------------------------------
+
+    public void setVar(String var, Object value)
+    {
+        variables.put(var, value);   
+    }
+    
+    public void setVar(Object var, Object value)
+    {
+        setVar(var.toString(), value);
+    }
+    
+    public HashMap<String, Object> getVars()
+    {
+        return variables;
+    }
+    
+    public Object getVar(String var)
+    {
+        return variables.get(var);
+    }
+    
+    public Object getVar(Object var)
+    {
+        return getVar(var.toString());
+    }
+    
+    public boolean getBoolean(String var)
+    {
+        try
+        {
+            return (boolean) getVar(var);
+        }
+        catch(ClassCastException | NullPointerException ex)
+        {
+            return false;
+        }
+    }
+    
+    public void removeVar(String var)
+    {
+        variables.remove(var);
+    }
+    
+    // -------------------------------------------------------------------------
+    // Goto
+    // -------------------------------------------------------------------------
+    
+    public void gotoLabel(String label) throws NoChildTreeException, CodeTooLongException
+    {
+        Integer[] i = gotos.get(label);
+        if(i == null)
+        {
+            throw new GotoLabelNotFoundException(label);
+        }
+        increaseOverflowProtection();
+        resetWhileMode();
+        setTryMode(false);
+        code.goToPosition(i);
+    }  
+    
+    public void gotoLabelWithReturn(String label) throws NoChildTreeException, CodeTooLongException
+    {
+        stack.push(code.getCurrentPosition());
+        gotoLabel(label);
+    }
+    
+    public void doReturn() throws NoChildTreeException
+    {
+        code.goToPosition(stack.pop()); 
+        /*try
+        {
+            code.selectNextChild();
+        }
+        catch(NoChildTreeException ex)
+        {
+            code.goHigher();
+        }*/
+    }
+    
+    public void resetOverflowProtection()
+    {
+        overflowProtection = 0;
+    }
+    
+    public void increaseOverflowProtection() throws CodeTooLongException
+    {
+        overflowProtection++;
+        if(overflowProtection > 1000)
+        {
+            overflowProtection = 0;
+            throw new CodeTooLongException();
+        }
+    }
+    
+    // -------------------------------------------------------------------------
+    // Event Handling
+    // -------------------------------------------------------------------------
+    
+    public void loadEvent(String event)
+    {
+        events.add(event);
+    }
+    
+    public void unloadEvent(String event)
+    {
+        events.remove(event);
+    }
+    
+    public boolean isEventLoaded(String event)
+    {
+        return events.contains(event);
+    }
+    
+    // -------------------------------------------------------------------------
+    // Player Handling
+    // -------------------------------------------------------------------------
+    
+    public void addPlayer(EntityPlayer p)
+    {
+        quester.add(p);
+    }
+    
+    public boolean removePlayer(EntityPlayer p)
+    {
+        quester.remove(p);
+        offquester.remove(p);
+        return quester.isEmpty();
+    }
+    
+    public List<EntityPlayer> getPlayers()
+    {      
+        return quester;
+    }
+    
+    public List<String> getPlayerNames()
+    {
+        return getPlayers().stream().map(p -> p.getName()).collect(Collectors.toList());
+    }
+    
+    public void addToOffQuesters(EntityPlayer p)
+    {
+        if(p == null)
+        {
+            return;
+        }
+        offquester.add(p);
+    }
+    
+    // -------------------------------------------------------------------------
+    // Location Handling für Wait-For-Location
+    // -------------------------------------------------------------------------
+    
+    public void addLocation(Location l)
+    {
+        loadedLocations.add(l);
+    }
+    
+    public boolean removeLocation(Location l)
+    {
+        return loadedLocations.remove(l);
+    }
+    
+    public void clearLocations()
+    {
+        loadedLocations.clear();
+    }
+    
+    // -------------------------------------------------------------------------
+    // Try-Catch Handler
+    // -------------------------------------------------------------------------
+    
+    public void setTryMode(boolean b)
+    {
+        isTrying = b;
+    }
+    
+    public boolean getTryMode()
+    {
+        return isTrying;
+    }
+    
+    public void setTryFail(boolean b)
+    {
+        tryFailed = b;
+    }
+    
+    public boolean getTryFail()
+    {
+        return tryFailed;
+    }
+    
+    // -------------------------------------------------------------------------
+    // While Handler
+    // -------------------------------------------------------------------------
+    
+    private void addWhileMode(boolean b)
+    {
+        whileStack.push(b);
+    }
+    
+    public boolean removeWhileMode()
+    {
+        try
+        {
+            return whileStack.pop();
+        }
+        catch(EmptyStackException ex)
+        {
+            return false;
+        }
+    }
+    
+    public void resetWhileMode()
+    {
+        whileStack.clear();
+    }
+    
+    // -------------------------------------------------------------------------
+    // Else Handler
+    // -------------------------------------------------------------------------
+    
+    public void setElseMode(boolean b)
+    {
+        shouldDoElse = b;
+    }
+    
+    public boolean getElseMode()
+    {
+        return shouldDoElse;
+    }
+    
+    // -------------------------------------------------------------------------
+    // Für Inventar-IDs
+    // -------------------------------------------------------------------------
+    
+    public int getNewId()
+    {
+        idCounter++;
+        return idCounter;
+    }
+    
+    // -------------------------------------------------------------------------
+    // Für Benchmarks
+    // -------------------------------------------------------------------------
+    
+    public void clearBenchmark()
+    {
+        exeClock.clear();
+        treeClock.clear();
+    }
+    
+    public boolean isBenchmarking()
+    {
+        return benchmark;
+    }
+    
+    public void toggleBenchmark()
+    {
+        benchmark = !benchmark;
+    }
+    
+    public void printBenchmarks(ICommandSender cs)
+    {
+        cs.sendMessage(new TextComponentString(GlobalText.Spacer()));
+        cs.sendMessage(new TextComponentString("Ausführung: " + exeClock.getSummedTime() + " ms"));
+        cs.sendMessage(new TextComponentString("Baum: " + treeClock.getSummedTime() + " ms"));
+    }
+}

+ 22 - 0
src/main/java/me/km/snuviscript/QuestInventoryHolder.java

@@ -0,0 +1,22 @@
+package me.km.snuviscript;
+
+import me.km.inventory.CustomContainer;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.inventory.ClickType;
+
+public class QuestInventoryHolder extends CustomContainer
+{
+    private final QuestData qd;
+
+    public QuestInventoryHolder(SnuviInventory inv, EntityPlayer p, QuestData qd) 
+    {
+        super(inv, p);
+        this.qd = qd;
+    }
+
+    @Override
+    public boolean noClicking(int slotId, int dragType, ClickType clickTypeIn, EntityPlayer player) 
+    {
+        return false;
+    }
+}

+ 1912 - 0
src/main/java/me/km/snuviscript/QuestParser.java

@@ -0,0 +1,1912 @@
+package me.km.snuviscript;
+
+import com.google.common.collect.HashBiMap;
+import me.km.KajetansMod;
+import me.km.afk.AfkListener;
+import me.km.api.Module;
+import me.km.api.Utils;
+import me.km.environment.EnvironmentAPI;
+import me.km.exception.CodeTooLongException;
+import me.km.exception.EntityNotFoundException;
+import me.km.exception.GotoLabelNotFoundException;
+import me.km.exception.HoldCodeException;
+import me.km.exception.IfWithoutStatementException;
+import me.km.exception.IllegalItemStackStringException;
+import me.km.exception.IllegalStringException;
+import me.km.exception.IllegalStringLocationException;
+import me.km.exception.NoChildTreeException;
+import me.km.exception.PlayerNotFoundException;
+import me.km.nms.NmsUtilities;
+import me.km.plots.ProtectionBank;
+import me.km.table.TableAPI;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.time.ZonedDateTime;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiFunction;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.BiConsumer;
+import java.util.stream.Collectors;
+import me.km.api.Location;
+import me.km.api.TitleAPI;
+import me.km.dimensions.ModDimensions;
+import me.km.effects.EffectUtils;
+import me.km.inventory.InventoryUtils;
+import me.km.permissions.Permission;
+import me.km.permissions.Permissions;
+import me.km.table.Table;
+import me.km.utils.ItemStackUtils;
+import me.km.utils.ReflectionUtils;
+import net.minecraft.block.Block;
+import net.minecraft.block.state.IBlockState;
+import net.minecraft.command.ICommandSender;
+import net.minecraft.enchantment.EnchantmentHelper;
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.EntityHanging;
+import net.minecraft.entity.EntityLivingBase;
+import net.minecraft.entity.item.EntityArmorStand;
+import net.minecraft.entity.item.EntityItemFrame;
+import net.minecraft.entity.monster.EntityCreeper;
+import net.minecraft.entity.passive.EntityVillager;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.entity.player.InventoryPlayer;
+import net.minecraft.inventory.EntityEquipmentSlot;
+import net.minecraft.inventory.IInventory;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.network.play.server.SPacketSpawnPosition;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.tileentity.TileEntityChest;
+import net.minecraft.tileentity.TileEntitySign;
+import net.minecraft.util.EnumFacing;
+import net.minecraft.util.EnumParticleTypes;
+import net.minecraft.util.SoundCategory;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.Vec3d;
+import net.minecraft.util.text.TextComponentString;
+import net.minecraft.world.World;
+import net.minecraft.world.WorldServer;
+
+public class QuestParser 
+{
+    private final BiFunction<Object[], QuestData, Object>[] methods;
+    private final HashBiMap<String, Integer> translator;
+    
+    private void nothing()
+    {
+    }
+    
+    public Integer translateMethod(String s)
+    {
+        return translator.get(s);
+    }
+    
+    private void registerFunction(List<BiFunction<Object[], QuestData, Object>> list, String s, BiFunction<Object[], QuestData, Object> f)
+    {
+        translator.put(s, list.size());
+        list.add(f);
+    }
+    
+    private void registerConsumer(List<BiFunction<Object[], QuestData, Object>> list, String s, BiConsumer<Object[], QuestData> f)
+    {
+        translator.put(s, list.size());
+        list.add((BiFunction<Object[], QuestData, Object>) (args, qd) -> 
+        {
+            f.accept(args, qd);
+            return 0;
+        });
+    }
+    
+    private void registerAlias(List<BiFunction<Object[], QuestData, Object>> list, String s, String original)
+    {
+        registerFunction(list, s, list.get(translator.get(original))); 
+    }
+    
+    @SuppressWarnings("unchecked")
+    public QuestParser()
+    {
+        ArrayList<BiFunction<Object[], QuestData, Object>> list = new ArrayList<>();
+        translator = HashBiMap.create(300);
+        
+        registerFunction(list, "nothing", (args, qd) -> 
+                0);
+        registerConsumer(list, "godeeper", (args, qd) -> 
+                qd.goDeeper(false));
+        registerConsumer(list, "benchmark", (args, qd) -> 
+                qd.printBenchmarks((ICommandSender) args[0]));
+        registerConsumer(list, "togglebenchmark", (args, qd) -> 
+                qd.toggleBenchmark());
+        registerConsumer(list, "clearbenchmark", (args, qd) -> 
+                qd.clearBenchmark());
+
+        // -------------------------------------------------------------
+        // Title-Bibliothek  
+        // -------------------------------------------------------------
+        registerConsumer(list, "title.settime", (args, qd) -> 
+                TitleAPI.setTitleTime((EntityPlayerMP) args[0], QuestUtils.getInt(args[1]), QuestUtils.getInt(args[2]), QuestUtils.getInt(args[3])));
+        registerConsumer(list, "title.clear", (args, qd) -> 
+                TitleAPI.clearTitle((EntityPlayerMP) args[0]));
+        registerConsumer(list, "title.reset", (args, qd) -> 
+                TitleAPI.resetTitle((EntityPlayerMP) args[0]));
+        registerConsumer(list, "title.send", (args, qd) -> 
+                TitleAPI.setTitle((EntityPlayerMP) args[0], QuestUtils.connect(args, 1)));
+        registerConsumer(list, "title.setsub", (args, qd) -> 
+                TitleAPI.setSubTitle((EntityPlayerMP) args[0], QuestUtils.connect(args, 1)));
+        
+        // -------------------------------------------------------------
+        // Villager-Bibliothek  
+        // -------------------------------------------------------------
+        registerConsumer(list, "villager.showtrades", (args, qd) -> 
+                ((EntityPlayer) args[0]).displayVillagerTradeGui(((EntityVillager) args[1])));
+        
+        // -------------------------------------------------------------    
+        // Player-Bibliothek
+        // -------------------------------------------------------------  
+        registerFunction(list, "player.getitemamount", (args, qd) ->       
+                InventoryUtils.searchInventoryFor(((EntityPlayer) args[0]).inventory, (ItemStack) args[2], (boolean) args[1]));                
+        registerFunction(list, "player.removeitem", (args, qd) -> 
+                removeItem(args));                 
+        registerFunction(list, "player.giveitem", (args, qd) ->       
+                giveItem(args));
+        registerConsumer(list, "player.shootitem", (args, qd) -> nothing());
+                // TODO Utils.shootItemWithEvent((EntityPlayer) args[0], ((ItemStack) args[5]).clone(), QuestUtils.getDouble(args[1]), QuestUtils.getDouble(args[2]), QuestUtils.getDouble(args[3]), QuestUtils.getDouble(args[4])));
+        registerConsumer(list, "player.shootprojectile", (args, qd) -> nothing());
+                // TODO Utils.shootProjectile(((EntityPlayer) args[0]), (Class<? extends Projectile>) getClass("org.bukkit.entity." + args[1]), QuestUtils.getDouble(args[2])));
+        registerConsumer(list, "player.respawn", (args, qd) ->     
+                ((EntityPlayer) args[0]).respawnPlayer());
+        registerConsumer(list, "player.inventorytolist", (args, qd) -> 
+                qd.setVar(args[0], ((EntityPlayer) args[1]).inventory.mainInventory));    
+        registerFunction(list, "player.getamount", (args, qd) ->                            
+                KajetansMod.server.getCurrentPlayerCount());
+        registerFunction(list, "player.getnearest", (args, qd) ->                            
+                Utils.getNearestPlayer(((Location) args[0]).getWorld(), ((Location) args[0]).getPos())); 
+        registerConsumer(list, "player.say", (args, qd) -> nothing());
+                // TODO ((EntityPlayerMP) args[0]).connection.chat(QuestUtils.connect(args, 1)));
+        registerFunction(list, "player.isafk", (args, qd) -> 
+                KajetansMod.afkManager.getEvent(AfkListener.class).isAfk(((EntityPlayer) args[0])));
+        registerConsumer(list, "player.speak", (args, qd) -> 
+                sendMessageToGroup(args[0], qd, "§7[§r" + args[1] + "§7] " + QuestUtils.connect(args, 2)));
+        registerFunction(list, "player.getquestleader", (args, qd) -> 
+                getQuestLeader(qd));
+        registerConsumer(list, "player.setcompass", (args, qd) -> 
+                ((EntityPlayerMP) args[0]).connection.sendPacket(new SPacketSpawnPosition(((Location) args[1]).getBlockPos()))); 
+        registerFunction(list, "player.gethunger", (args, qd) -> 
+                ((EntityPlayer) args[0]).getFoodStats().getFoodLevel());
+        registerConsumer(list, "player.sethunger", (args, qd) -> 
+                ((EntityPlayer) args[0]).getFoodStats().setFoodLevel(QuestUtils.getInt(args[1])));
+        registerFunction(list, "player.getsaturation", (args, qd) -> 
+                ReflectionUtils.getSaturation(((EntityPlayer) args[0]).getFoodStats()));
+        registerConsumer(list, "player.setsaturation", (args, qd) -> 
+                ReflectionUtils.setSaturation(((EntityPlayer) args[0]).getFoodStats(), QuestUtils.getFloat(args[1])));
+        registerFunction(list, "player.getname", (args, qd) -> 
+                getName(args));
+        registerFunction(list, "player.getfullname", (args, qd) -> 
+                KajetansMod.chatManager.getFullName((EntityPlayer) args[0]));
+        registerFunction(list, "player.getuuid", (args, qd) -> 
+                getUuid(args));
+        registerFunction(list, "player.getip", (args, qd) -> 
+                ((EntityPlayerMP) args[0]).connection.netManager.getRemoteAddress().toString());
+        registerFunction(list, "player.iscreative", (args, qd) -> 
+                ((EntityPlayer) args[0]).isCreative());
+        registerFunction(list, "player.isspectator", (args, qd) -> 
+                ((EntityPlayer) args[0]).isSpectator());
+        registerFunction(list, "player.hasfly", (args, qd) -> 
+                ((EntityPlayer) args[0]).capabilities.allowFlying);
+        registerFunction(list, "player.getlastdamager", (args, qd) -> 
+                ((EntityPlayer) args[0]).getLastAttacker());
+        registerConsumer(list, "player.settag", (args, qd) -> 
+                setTag(args));
+        registerFunction(list, "player.gettag", (args, qd) -> 
+                getTag(args));
+        registerConsumer(list, "player.dropinventory", (args, qd) -> 
+                dropInventory(args));
+        registerFunction(list, "player.gettarget", (args, qd) -> 
+                Utils.getPlayerTarget((EntityPlayer) args[0], QuestUtils.getInt(args[1])));
+        registerFunction(list, "player.hasquest", (args, qd) ->     
+                KajetansMod.quest.hasQuest((EntityPlayer) args[0]));
+        registerConsumer(list, "player.action", (args, qd) ->     
+                sendToActionBar(args, qd)); 
+        registerConsumer(list, "player.kick", (args, qd) -> 
+                kickPlayerFromQuest(args, qd));  
+        registerConsumer(list, "player.playsound", (args, qd) -> 
+                playSoundPlayer(args));
+        registerFunction(list, "player.getbedspawn", (args, qd) ->     
+                ((EntityPlayer) args[0]).getBedLocation(QuestUtils.getInt(args[1])));
+        registerConsumer(list, "player.setbedspawn", (args, qd) ->     
+                ((EntityPlayer) args[0]).bedLocation = ((Location) args[1]).getBlockPos());
+        registerConsumer(list, "player.damageitem", (args, qd) ->   
+                ((EntityPlayer) args[0]).getHeldItemMainhand().damageItem(QuestUtils.getInt(args[1]), (EntityPlayer) args[0]));
+
+        // -------------------------------------------------------------    
+        // Players-Bibliothek
+        // -------------------------------------------------------------   
+        registerConsumer(list, "players.tolist", (args, qd) ->     
+                qd.setVar(args[0].toString(), Arrays.asList(KajetansMod.server.getOnlinePlayerNames())));    
+        registerConsumer(list, "players.toworldlist", (args, qd) ->     
+                qd.setVar(args[0].toString(), new ArrayList(ModDimensions.getWorldFromName(args[1].toString()).playerEntities)));
+        registerConsumer(list, "players.near", (args, qd) ->     
+                qd.setVar(args[0].toString(), Utils.getNearbyEntities(((Location) args[1]).getWorld(), ((Location) args[1]).getPos(), QuestUtils.getDouble(args[2]), EntityPlayer.class).stream().map(p -> p.getName()).collect(Collectors.toList()))); 
+
+        // -------------------------------------------------------------    
+        // Rank-Bibliothek
+        // -------------------------------------------------------------  
+        registerConsumer(list, "rank.get", (args, qd) -> 
+                qd.setVar(args[0], KajetansMod.chatManager.getRanks((EntityPlayer) args[1])));
+        registerConsumer(list, "rank.register", (args, qd) -> 
+                KajetansMod.chatManager.registerRank(args[0].toString(), QuestUtils.getInt(args[1]), args[2].toString()));
+        registerConsumer(list, "rank.clear", (args, qd) -> 
+                KajetansMod.chatManager.clearRanks());
+        
+        // -------------------------------------------------------------    
+        // Custom-Bibliothek
+        // -------------------------------------------------------------
+        registerConsumer(list, "custom.registerenchantmentrecipe", (args, qd) -> 
+                registerEnchantmentRecipe(args));
+        registerConsumer(list, "custom.registershapelessrecipe", (args, qd) -> 
+                registerShapelessRecipe(args));
+        registerConsumer(list, "custom.registershapedrecipe", (args, qd) -> 
+                registerShapedRecipe(args));
+        registerConsumer(list, "custom.registerfurnacerecipe", (args, qd) -> nothing()
+                /*KajetansMod.customs.registerFurnaceRecipe((ItemStack) args[0], (ItemStack) args[1])*/);
+        registerConsumer(list, "custom.clearrecipes", (args, qd) -> nothing()
+                /*KajetansMod.customs.clearRecipes()*/);
+        registerConsumer(list, "custom.clearenchantments", (args, qd) -> nothing()
+                /*KajetansMod.customs.clearEnchantments()*/);
+        
+        
+        // -------------------------------------------------------------    
+        // World-Bibliothek
+        // -------------------------------------------------------------
+        
+        registerAlias(list, "world.getplayers", "players.toworldlist");
+        registerConsumer(list, "world.setskills", (args, qd) ->     
+                KajetansMod.worldManager.getWorldPreferences(ModDimensions.getWorldFromName(args[0].toString())).skills = (boolean) args[1]);
+        registerConsumer(list, "world.setblockprotections", (args, qd) ->     
+                KajetansMod.worldManager.getWorldPreferences(ModDimensions.getWorldFromName(args[0].toString())).blockProtection = (boolean) args[1]);
+        registerConsumer(list, "world.setdefaultenchanting", (args, qd) ->     
+                KajetansMod.worldManager.getWorldPreferences(ModDimensions.getWorldFromName(args[0].toString())).defaultEnchanting = (boolean) args[1]);
+        registerConsumer(list, "world.setpvpprotection", (args, qd) ->     
+                KajetansMod.worldManager.getWorldPreferences(ModDimensions.getWorldFromName(args[0].toString())).pvpProtection = (boolean) args[1]);
+        registerConsumer(list, "world.setstatuseffects", (args, qd) ->     
+                KajetansMod.worldManager.getWorldPreferences(ModDimensions.getWorldFromName(args[0].toString())).statusEffects = (boolean) args[1]);
+        registerConsumer(list, "world.setmanabar", (args, qd) ->     
+                KajetansMod.worldManager.getWorldPreferences(ModDimensions.getWorldFromName(args[0].toString())).manabar = (boolean) args[1]);
+        registerConsumer(list, "world.setscrolls", (args, qd) ->     
+                KajetansMod.worldManager.getWorldPreferences(ModDimensions.getWorldFromName(args[0].toString())).scrolls = (boolean) args[1]);
+        registerConsumer(list, "world.setdefaultproducing", (args, qd) ->     
+                KajetansMod.worldManager.getWorldPreferences(ModDimensions.getWorldFromName(args[0].toString())).defaultProducing = (boolean) args[1]);
+      
+        // -------------------------------------------------------------    
+        // Item-Bibliothek   
+        // -------------------------------------------------------------   
+        registerConsumer(list, "item.drop", (args, qd) ->       
+                dropItem(args));                              
+        registerFunction(list, "item.gettype", (args, qd) ->    
+                ((ItemStack) args[0]).getItem().getRegistryName());
+        registerFunction(list, "item.getdata", (args, qd) ->    
+                ((ItemStack) args[0]).getMetadata());           
+        registerConsumer(list, "item.setdata", (args, qd) ->    
+                ((ItemStack) args[0]).setItemDamage((short) QuestUtils.getInt(args[1])));
+        registerFunction(list, "item.getamount", (args, qd) ->    
+                ((ItemStack) args[0]).getCount()); 
+        registerConsumer(list, "item.setamount", (args, qd) ->   
+                ((ItemStack) args[0]).setCount(QuestUtils.getInt(args[1])));   
+        registerFunction(list, "item.getname", (args, qd) ->    
+                ((ItemStack) args[0]).getDisplayName());
+        registerConsumer(list, "item.setname", (args, qd) ->   
+                ((ItemStack) args[0]).setStackDisplayName(QuestUtils.connect(args, 1)));
+        registerConsumer(list, "item.getlore", (args, qd) ->    
+                qd.setVar(args[0], Utils.getLore((ItemStack) args[1])));
+        registerConsumer(list, "item.setlore", (args, qd) ->   
+                Utils.setLore((ItemStack) args[0], QuestUtils.connect(args, 2), QuestUtils.getInt(args[1])));
+        registerFunction(list, "item.getenchantlevel", (args, qd) ->   
+                EnchantmentHelper.getEnchantmentLevel(ReflectionUtils.getEnchantment(args[1].toString()), (ItemStack) args[0]));
+        registerConsumer(list, "item.setcooldown", (args, qd) ->   
+                ((EntityPlayer) args[0]).getCooldownTracker().setCooldown(((ItemStack) args[1]).getItem(), QuestUtils.getInt(args[2])));
+        registerFunction(list, "item.gettooltype", (args, qd) ->   
+                Utils.getToolType((ItemStack) args[0]));
+
+        // -------------------------------------------------------------    
+        // Location-Bibliothek
+        // -------------------------------------------------------------  
+        registerFunction(list, "loc.distance", (args, qd) -> 
+                ((Location) args[0]).getPos().distanceTo(((Location) args[1]).getPos()));   
+        registerFunction(list, "loc.mod", (args, qd) -> 
+                ((Location) args[0]).add(QuestUtils.getDouble(args[1]), QuestUtils.getDouble(args[2]), QuestUtils.getDouble(args[3])));
+        registerFunction(list, "loc.getcoord", (args, qd) -> 
+                getCoordOfLocation(args));
+        registerFunction(list, "loc.gettime", (args, qd) -> 
+                ((Long) ((Location) args[0]).getWorld().getWorldTime()).intValue());
+        registerFunction(list, "loc.hasstorm", (args, qd) -> 
+                ((Location) args[0]).getWorld().isRaining());
+        registerFunction(list, "loc.isbetween", (args, qd) -> 
+                isBetween(args));
+
+        // -------------------------------------------------------------    
+        // Block-Bibliothek
+        // -------------------------------------------------------------    
+        registerFunction(list, "block.gettype", (args, qd) -> 
+                ((Location) args[0]).getWorld().getBlockState(((Location) args[0]).getBlockPos()).getBlock().getRegistryName());
+        registerFunction(list, "block.getdata", (args, qd) -> 
+                getBlockData((Location) args[0]));
+        registerConsumer(list, "block.clone", (args, qd) -> 
+                cloneBlock(args));
+        registerConsumer(list, "block.set", (args, qd) -> 
+                setBlock(args, qd));
+        registerConsumer(list, "block.set2", (args, qd) -> 
+                setBlockWithData(args, qd));
+        registerConsumer(list, "block.setsign", (args, qd) -> 
+                setSign(args));
+        registerFunction(list, "block.getsign", (args, qd) -> 
+                getSign(args));
+        registerFunction(list, "block.issolid", (args, qd) -> 
+                getBlockState((Location) args[0]).isFullCube());
+        registerFunction(list, "block.tostack", (args, qd) -> 
+                getStackFromBlock((Location) args[0]));
+        registerFunction(list, "block.getitemamount", (args, qd) -> 
+                getItemAmountChest(args));
+        registerFunction(list, "block.additem", (args, qd) -> 
+                addItemAmountChest(args));
+        registerFunction(list, "block.subitem", (args, qd) -> 
+                removeItemAmountChest(args));  
+
+        // -------------------------------------------------------------    
+        // Event-Bibliothek 
+        // -------------------------------------------------------------  
+        registerConsumer(list, "event.addloc", (args, qd) -> 
+                qd.addLocation(QuestUtils.roundLocation((Location) args[0])));
+        registerConsumer(list, "event.removeloc", (args, qd) -> 
+                qd.removeLocation(QuestUtils.roundLocation((Location) args[0])));
+        registerConsumer(list, "event.clearlocs", (args, qd) -> 
+                qd.clearLocations());
+        registerConsumer(list, "event.load", (args, qd) -> 
+                qd.loadEvent(args[0].toString())); 
+        registerConsumer(list, "event.unload", (args, qd) -> 
+                qd.unloadEvent(args[0].toString())); 
+
+        // -------------------------------------------------------------    
+        // Skill-Bibliothek 
+        // ------------------------------------------------------------- 
+        registerConsumer(list, "skill.register", (args, qd) -> 
+                KajetansMod.skills.registerSkill(QuestUtils.getInt(args[0]), (ItemStack) args[1], me.km.effects.Effect.valueOf(args[2].toString()), args[3].toString(), args[4].toString()));
+        registerConsumer(list, "skill.clear", (args, qd) -> 
+                KajetansMod.skills.clearSkills());
+        
+        // -------------------------------------------------------------    
+        // Job-Bibliothek 
+        // -------------------------------------------------------------  
+        registerFunction(list, "job.getlevel", (args, qd) -> 
+                KajetansMod.jobs.getLevel((EntityPlayer) args[0], QuestUtils.getByte(args[1])));
+        registerConsumer(list, "job.addlevel", (args, qd) -> 
+                KajetansMod.jobs.addLevel((EntityPlayer) args[0], QuestUtils.getByte(args[1]), QuestUtils.getByte(args[2])));
+        registerConsumer(list, "job.setlevel", (args, qd) -> 
+                KajetansMod.jobs.setLevel((EntityPlayer) args[0], QuestUtils.getByte(args[1]), QuestUtils.getByte(args[2])));
+        registerFunction(list, "job.getxp", (args, qd) -> 
+                KajetansMod.jobs.getXP((EntityPlayer) args[0], QuestUtils.getByte(args[1])));
+        registerConsumer(list, "job.addxp", (args, qd) -> 
+                KajetansMod.jobs.addXP((EntityPlayer) args[0], QuestUtils.getByte(args[1]), QuestUtils.getByte(args[2])));
+        registerConsumer(list, "job.setxp", (args, qd) -> 
+                KajetansMod.jobs.setXP((EntityPlayer) args[0], QuestUtils.getByte(args[1]), QuestUtils.getByte(args[2])));
+        registerConsumer(list, "job.registerjob", (args, qd) -> 
+                KajetansMod.jobs.registerJob(QuestUtils.getByte(args[0]), QuestUtils.connect(args, 1)));
+        registerConsumer(list, "job.registerrecipe", (args, qd) -> 
+                KajetansMod.jobs.registerRecipe(QuestUtils.getByte(args[0]), Item.getByNameOrId(args[1].toString()), QuestUtils.getByte(args[2])));
+        registerConsumer(list, "job.registermaterial", (args, qd) -> 
+                KajetansMod.jobs.registerPreferedBlock(QuestUtils.getByte(args[0]), Block.getBlockFromName(args[1].toString())));
+        registerConsumer(list, "job.registerskill", (args, qd) -> 
+                KajetansMod.jobs.registerSkill(QuestUtils.getByte(args[0]), KajetansMod.skills.getSkill(QuestUtils.getInt(args[1])), QuestUtils.getByte(args[2]), QuestUtils.getByte(args[3])));
+        registerFunction(list, "job.getamount", (args, qd) -> 
+                KajetansMod.jobs.getNumberOfJobs());
+        registerFunction(list, "job.getname", (args, qd) -> 
+                KajetansMod.jobs.getJobName(QuestUtils.getByte(args[0])));
+        registerFunction(list, "job.geteffectlevel", (args, qd) -> 
+                EffectUtils.getEffectLevel((EntityPlayer) args[0], me.km.effects.Effect.valueOf(args[1].toString())));
+        registerFunction(list, "job.hasjob", (args, qd) -> 
+                KajetansMod.jobs.hasJob((EntityPlayer) args[0], QuestUtils.getByte(args[1])));
+        registerFunction(list, "job.hasrecipe", (args, qd) -> 
+                KajetansMod.jobs.hasRecipe((EntityPlayer) args[0], Item.getByNameOrId(args[1].toString())));
+        registerConsumer(list, "job.getjobs", (args, qd) -> 
+                qd.setVar(args[0], KajetansMod.jobs.getJobs((EntityPlayer) args[1])));
+        registerFunction(list, "job.isregmaterial", (args, qd) -> 
+                KajetansMod.jobs.isPreferedMaterial((EntityPlayer) args[0],Block.getBlockFromName(args[1].toString())));
+        registerConsumer(list, "job.setjob", (args, qd) -> 
+                KajetansMod.jobs.setJob((EntityPlayer) args[0], QuestUtils.getByte(args[1]), (boolean) args[2])); 
+        registerConsumer(list, "job.reset", (args, qd) -> 
+                KajetansMod.jobs.resetAll());  
+
+        // -------------------------------------------------------------    
+        // Bit-Bibliothek 
+        // -------------------------------------------------------------   
+        registerFunction(list, "bit.rightshift", (args, qd) -> 
+                QuestUtils.getInt(args[0]) >> QuestUtils.getInt(args[1]));
+        registerFunction(list, "bit.leftshift", (args, qd) -> 
+                QuestUtils.getInt(args[0]) << QuestUtils.getInt(args[1]));
+        registerFunction(list, "bit.and", (args, qd) -> 
+                QuestUtils.getInt(args[0]) & QuestUtils.getInt(args[1]));
+        registerFunction(list, "bit.or", (args, qd) -> 
+                QuestUtils.getInt(args[0]) | QuestUtils.getInt(args[1]));
+        registerFunction(list, "bit.xor", (args, qd) -> 
+                QuestUtils.getInt(args[0]) ^ QuestUtils.getInt(args[1]));
+        registerFunction(list, "bit.invert", (args, qd) -> 
+                ~QuestUtils.getInt(args[0]));
+        registerFunction(list, "bit.set", (args, qd) -> 
+                QuestUtils.getInt(args[0]) | (1 << QuestUtils.getInt(args[1])));
+        registerFunction(list, "bit.get", (args, qd) -> 
+                (QuestUtils.getInt(args[0]) & (1 << QuestUtils.getInt(args[1]))) != 0);
+
+        // -------------------------------------------------------------    
+        // Mathe-Bibliothek 
+        // -------------------------------------------------------------    
+        registerFunction(list, "math.mod", (args, qd) -> 
+                numberHandler(args[0], args[1], (a, b) -> a % b, (a, b) -> a % b, (a, b) -> a % b));
+        registerFunction(list, "math.abs", (args, qd) -> 
+                numberHandler(args[0], (a) -> Math.abs(a), (a) -> Math.abs(a), (a) -> Math.abs(a)));
+        registerFunction(list, "math.pow", (args, qd) -> 
+                Math.pow(QuestUtils.getDouble(args[0]), QuestUtils.getDouble(args[1])));
+        registerFunction(list, "math.root", (args, qd) -> 
+                Math.pow(QuestUtils.getDouble(args[0]), 1d / QuestUtils.getDouble(args[1])));
+        registerFunction(list, "math.sin", (args, qd) -> 
+                Math.sin(QuestUtils.getDouble(args[0])));
+        registerFunction(list, "math.cos", (args, qd) -> 
+                Math.cos(QuestUtils.getDouble(args[0])));
+        registerFunction(list, "math.e", (args, qd) -> 
+                Math.E);
+        registerFunction(list, "math.pi", (args, qd) -> 
+                Math.PI);
+        registerFunction(list, "math.ln", (args, qd) -> 
+                Math.log(QuestUtils.getDouble(args[0])));
+        registerFunction(list, "math.log", (args, qd) -> 
+                Math.log10(QuestUtils.getDouble(args[0])));
+        registerFunction(list, "math.random", (args, qd) -> 
+                Utils.randomInt(QuestUtils.getInt(args[0]), QuestUtils.getInt(args[1])));
+        registerFunction(list, "math.round", (args, qd) -> 
+                round(args[0]));
+        registerFunction(list, "math.rounddown", (args, qd) -> 
+                roundDown(args[0]));
+        registerFunction(list, "math.roundup", (args, qd) -> 
+                roundUp(args[0]));
+        registerFunction(list, "math.roundcomma", (args, qd) -> 
+                new BigDecimal(QuestUtils.getDouble(args[0])).setScale(QuestUtils.getInt(args[1]), RoundingMode.HALF_UP).doubleValue());
+
+        // -------------------------------------------------------------    
+        // Entity - Befehle
+        // -------------------------------------------------------------    
+        registerFunction(list, "entity.getlocation", (args, qd) -> 
+                new Location(((Entity) args[0])));   
+        registerConsumer(list, "entity.damage", (args, qd) -> 
+                damageEntity(args));  
+        registerFunction(list, "entity.gethealth", (args, qd) -> 
+                ((EntityLivingBase) args[0]).getHealth());     
+        registerConsumer(list, "entity.sethealth", (args, qd) -> 
+                ((EntityLivingBase) args[0]).setHealth(QuestUtils.getFloat(args[1])));  
+        registerConsumer(list, "entity.setname", (args, qd) -> 
+                nameEntity(args)); 
+        registerConsumer(list, "entity.throw", (args, qd) -> 
+                ((Entity) args[0]).setVelocity(QuestUtils.getDouble(args[1]), QuestUtils.getDouble(args[2]), QuestUtils.getDouble(args[3])));
+        registerConsumer(list, "entity.teleport", (args, qd) -> 
+                Utils.teleportEntity((Entity) args[0], (Location) args[1]));  
+        registerConsumer(list, "entity.setequip", (args, qd) -> 
+                setEntityEquip(args)); 
+        registerFunction(list, "entity.getequip", (args, qd) -> 
+                getEntityEquip(args));
+        registerConsumer(list, "entity.removeall", (args, qd) -> 
+                removeEntities(args));
+        registerConsumer(list, "entity.remove", (args, qd) -> 
+                ((Entity) args[0]).setDead());
+        registerConsumer(list, "entity.setinvulnerable", (args, qd) -> 
+                ((Entity) args[0]).setEntityInvulnerable((boolean) args[1]));
+        registerConsumer(list, "entity.setsilent", (args, qd) -> 
+                ((Entity) args[0]).setSilent((boolean) args[1]));
+        registerConsumer(list, "entity.hide", (args, qd) -> 
+                ((Entity) args[0]).setInvisible(true));
+        registerConsumer(list, "entity.show", (args, qd) -> 
+                ((Entity) args[0]).setInvisible(false));
+        registerConsumer(list, "entity.ride", (args, qd) -> 
+                ((Entity) args[0]).startRiding(((Entity) args[1])));
+        registerConsumer(list, "entity.setvars", (args, qd) -> 
+                QuestVars.setEntityVars(qd, Utils.getNearestEntity(((Location) args[0]).getWorld(), ((Location) args[0]).getPos(), 3, (Class<? extends Entity>) getClass("net.minecraft.entity." + args[1]))));
+        registerConsumer(list, "entity.addeffect", (args, qd) -> 
+                EffectUtils.addPotionTo((EntityLivingBase) args[0], Utils.getPotion(args[1].toString()), QuestUtils.getInt(args[2]), QuestUtils.getInt(args[3])));
+        registerFunction(list, "entity.haseffect", (args, qd) -> 
+                ((EntityLivingBase) args[0]).isPotionActive(Utils.getPotion(args[1].toString())));
+        registerConsumer(list, "entity.goto", (args, qd) -> nothing()
+                /*TODO NmsUtilities.walkTo((Entity) args[0], (Location) args[1], QuestUtils.getDouble(args[2]), QuestUtils.getDouble(args[3]))*/);
+        registerConsumer(list, "entity.explode", (args, qd) -> 
+                ((EntityCreeper) args[0]).ignite());
+        registerConsumer(list, "entity.spawnitemframe", (args, qd) -> 
+                spawnItemFrame(args));
+        registerFunction(list, "entity.getitemframe", (args, qd) -> 
+                Utils.getNearestEntity(((Location) args[0]).getWorld(), ((Location) args[0]).getPos().addVector(0.5, 0, 0.5), 1, EntityItemFrame.class).getDisplayedItem());
+        registerFunction(list, "entity.getpotiontype", (args, qd) -> 
+                getPotionType(args));
+        registerConsumer(list, "entity.setgravity", (args, qd) -> 
+                ((Entity) args[0]).setNoGravity(!(boolean) args[1]));
+
+        // -------------------------------------------------------------
+        // Status-Bibliothek
+        // -------------------------------------------------------------  
+        registerFunction(list, "status.getmana", (args, qd) -> 
+                EnvironmentAPI.getMana(((EntityPlayer) args[0])));
+        registerFunction(list, "status.getcold", (args, qd) -> 
+                EnvironmentAPI.getCold(((EntityPlayer) args[0])));
+        registerFunction(list, "status.getenergy", (args, qd) -> 
+                EnvironmentAPI.getEnergy(((EntityPlayer) args[0])));
+        registerFunction(list, "status.getthirst", (args, qd) -> 
+                EnvironmentAPI.getThirst(((EntityPlayer) args[0])));
+        registerConsumer(list, "status.changemange", (args, qd) -> 
+                EnvironmentAPI.changeMana(((EntityPlayer) args[0]), QuestUtils.getInt(args[1])));
+        registerConsumer(list, "status.changecold", (args, qd) -> 
+                EnvironmentAPI.changeCold(((EntityPlayer) args[0]), QuestUtils.getInt(args[1])));
+        registerConsumer(list, "status.changeenergie", (args, qd) -> 
+                EnvironmentAPI.changeEnergy(((EntityPlayer) args[0]), QuestUtils.getInt(args[1])));
+        registerConsumer(list, "status.changethirst", (args, qd) -> 
+                EnvironmentAPI.changeThirst(((EntityPlayer) args[0]), QuestUtils.getInt(args[1])));
+        registerConsumer(list, "status.resetmana", (args, qd) -> 
+                EnvironmentAPI.resetMana(((EntityPlayer) args[0])));
+        registerConsumer(list, "status.resetcold", (args, qd) -> 
+                EnvironmentAPI.resetCold(((EntityPlayer) args[0])));
+        registerConsumer(list, "status.resetenergy", (args, qd) -> 
+                EnvironmentAPI.resetEnergy(((EntityPlayer) args[0])));
+        registerConsumer(list, "status.resetthirst", (args, qd) -> 
+                EnvironmentAPI.resetThirst(((EntityPlayer) args[0])));
+        registerConsumer(list, "status.gettemperature", (args, qd) -> 
+                EnvironmentAPI.getTemperature(((Location) args[0]).getWorld(), ((Location) args[0]).getBlockPos()));    
+
+        // -------------------------------------------------------------  
+        // Listen-Bibliothek   
+        // -------------------------------------------------------------    
+        registerConsumer(list, "list.new", (args, qd) ->                                                
+                qd.setVar(args[0], new ArrayList<>()));
+        registerFunction(list, "list.exists", (args, qd) ->                                                
+                args[0] instanceof List); 
+        registerConsumer(list, "list.add", (args, qd) ->                            
+                ((List) args[0]).add(args[1]));
+        registerConsumer(list, "list.remove", (args, qd) ->                            
+                ((List) args[0]).remove(args[1]));
+        registerConsumer(list, "list.removeindex", (args, qd) ->                            
+                ((List) args[0]).remove(QuestUtils.getInt(args[1])));                
+        registerFunction(list, "list.contains", (args, qd) ->                            
+                ((List) args[0]).contains(args[1]));
+        registerFunction(list, "list.getsize", (args, qd) ->                            
+                ((List) args[0]).size());
+        registerFunction(list, "list.getindex", (args, qd) ->                            
+                ((List) args[0]).get(QuestUtils.getInt(args[1])));
+        registerConsumer(list, "list.setindex", (args, qd) ->                            
+                ((List) args[0]).set(QuestUtils.getInt(args[1]), args[2]));
+        registerConsumer(list, "list.clear", (args, qd) ->                            
+                ((List) args[0]).clear());
+        registerFunction(list, "list.getindexof", (args, qd) ->                            
+                ((List) args[0]).indexOf(args[1]));  
+        registerConsumer(list, "list.sort", (args, qd) ->  
+                sortList((List<Object>) args[0], qd));
+        registerConsumer(list, "list.reverse", (args, qd) ->                            
+                Collections.reverse((List<Object>) args[0])); 
+        registerConsumer(list, "list.shuffle", (args, qd) ->                            
+                Collections.shuffle((List<Object>) args[0]));
+
+        // -------------------------------------------------------------  
+        // Map-Bibliothek   
+        // ------------------------------------------------------------- 
+        registerConsumer(list, "map.new", (args, qd) ->                                                
+                qd.setVar(args[0], new HashMap<>())); 
+        registerFunction(list, "map.exists", (args, qd) ->                                                
+                args[0] instanceof HashMap);  
+        registerConsumer(list, "map.add", (args, qd) ->                            
+                ((HashMap) args[0]).put(args[1], args[2]));
+        registerConsumer(list, "map.remove", (args, qd) ->                            
+                ((HashMap) args[0]).remove(args[1]));
+        registerFunction(list, "map.contains", (args, qd) ->                            
+                ((HashMap) args[0]).containsKey(args[1]));
+        registerFunction(list, "map.getsize", (args, qd) ->                            
+                ((HashMap) args[0]).size());
+        registerFunction(list, "map.get", (args, qd) ->                            
+                ((HashMap) args[0]).get(args[1]));
+        registerConsumer(list, "map.clear", (args, qd) ->                            
+                ((HashMap) args[0]).clear());
+        
+        // -------------------------------------------------------------  
+        // Set-Bibliothek   
+        // ------------------------------------------------------------- 
+        registerConsumer(list, "set.new", (args, qd) ->                                                
+                qd.setVar(args[0], new HashSet<>())); 
+        registerFunction(list, "set.exists", (args, qd) ->                                                
+                args[0] instanceof HashSet);  
+        registerConsumer(list, "set.add", (args, qd) ->                            
+                ((HashSet) args[0]).add(args[1]));
+        registerConsumer(list, "set.remove", (args, qd) ->                            
+                ((HashSet) args[0]).remove(args[1]));
+        registerFunction(list, "set.contains", (args, qd) ->                            
+                ((HashSet) args[0]).contains(args[1]));
+        registerFunction(list, "set.getsize", (args, qd) ->                            
+                ((HashSet) args[0]).size());
+
+        // -------------------------------------------------------------  
+        // GMap-Bibliothek   
+        // -------------------------------------------------------------  
+        registerConsumer(list, "gmap.removeall", (args, qd) ->    
+                KajetansMod.quest.getDataBank(QuestBank.class).removeMap(args[0].toString()));
+        registerConsumer(list, "gmap.add", (args, qd) ->    
+                KajetansMod.quest.getDataBank(QuestBank.class).addMapElement(args[0].toString(), args[1].toString(), args[2].toString()));
+        registerConsumer(list, "gmap.remove", (args, qd) ->    
+                KajetansMod.quest.getDataBank(QuestBank.class).removeMapElement(args[0].toString(), args[1].toString()));
+        registerConsumer(list, "gmap.totable", (args, qd) ->    
+                qd.setVar(args[0], KajetansMod.quest.getDataBank(QuestBank.class).getGlobalMapAsTable(args[1].toString())));
+        registerFunction(list, "gmap.get", (args, qd) ->    
+                KajetansMod.quest.getDataBank(QuestBank.class).getMapValue(args[0].toString(), args[1].toString()));
+        
+        // -------------------------------------------------------------  
+        // GDMap-Bibliothek   
+        // -------------------------------------------------------------  
+        registerConsumer(list, "gdmap.removeall", (args, qd) ->    
+                KajetansMod.quest.getDataBank(QuestBank.class).removeDualMap(args[0].toString()));
+        registerConsumer(list, "gdmap.add", (args, qd) ->    
+                KajetansMod.quest.getDataBank(QuestBank.class).addDualMapElement(args[0].toString(), args[1].toString(), args[2].toString(), args[3].toString()));
+        registerConsumer(list, "gdmap.remove", (args, qd) ->    
+                KajetansMod.quest.getDataBank(QuestBank.class).removeDualMapElement(args[0].toString(), args[1].toString(), args[2].toString()));
+        registerConsumer(list, "gdmap.totable", (args, qd) ->    
+                qd.setVar(args[0], KajetansMod.quest.getDataBank(QuestBank.class).getGlobalDualMapAsTable(args[1].toString(), args[2].toString())));
+        registerFunction(list, "gdmap.get", (args, qd) ->    
+                KajetansMod.quest.getDataBank(QuestBank.class).getDualMapValue(args[0].toString(), args[1].toString(), args[2].toString()));
+
+        // -------------------------------------------------------------  
+        // Table-Bibliothek   
+        // -------------------------------------------------------------  
+        registerConsumer(list, "table.printstart", (args, qd) ->   
+                sendMessageToGroup(args[0], qd, TableAPI.getTableStart(QuestUtils.getInt(args[1]), QuestUtils.getInt(args[2]), args[3].toString())));
+        registerConsumer(list, "table.printmiddle", (args, qd) ->   
+                sendMessageToGroup(args[0], qd, TableAPI.getTableMiddle(QuestUtils.getInt(args[1]), QuestUtils.getInt(args[2]), args[3].toString())));  
+        registerConsumer(list, "table.print", (args, qd) ->     
+                sendMessageToGroup(args[0], qd, TableAPI.getTable(QuestUtils.getInt(args[1]), args[2].toString(), Arrays.stream(args, 3, args.length).map(o -> o.toString()).collect(Collectors.toList()))));  
+        registerConsumer(list, "table.printend", (args, qd) ->    
+                sendMessageToGroup(args[0], qd, TableAPI.getTableEnd(QuestUtils.getInt(args[1]), QuestUtils.getInt(args[2]), args[3].toString())));  
+        registerConsumer(list, "table.new", (args, qd) -> 
+                qd.setVar(args[0], new Table(QuestUtils.getInt(args[1]))));
+        registerConsumer(list, "table.frommap", (args, qd) -> 
+                qd.setVar(args[0], new Table((Map<Object, Object>) args[1])));
+        registerConsumer(list, "table.sort", (args, qd) -> 
+                ((Table) args[0]).sort());
+        registerConsumer(list, "table.reverse", (args, qd) -> 
+                ((Table) args[0]).reverse());
+        registerConsumer(list, "table.shuffle", (args, qd) -> 
+                ((Table) args[0]).shuffle());
+        registerConsumer(list, "table.clear", (args, qd) -> 
+                ((Table) args[0]).clear());
+        registerConsumer(list, "table.addrow", (args, qd) -> 
+                ((Table) args[0]).addRow(Arrays.copyOfRange(args, 1, args.length)));
+        registerConsumer(list, "table.removerow", (args, qd) -> 
+                ((Table) args[0]).removeRow(QuestUtils.getInt(args[1])));
+        registerFunction(list, "table.get", (args, qd) -> 
+                ((Table) args[0]).getElement(QuestUtils.getInt(args[1]), QuestUtils.getInt(args[2])));
+        registerFunction(list, "table.getindexof", (args, qd) -> 
+                ((Table) args[0]).getIndexOf(args[1]));
+        registerConsumer(list, "table.setsortcolumn", (args, qd) -> 
+                ((Table) args[0]).setSortColumn(QuestUtils.getInt(args[1])));
+        registerFunction(list, "table.getsize", (args, qd) -> 
+                ((Table) args[0]).getSize());
+
+        // -------------------------------------------------------------  
+        // Plot-Bibliothek   
+        // -------------------------------------------------------------  
+        registerFunction(list, "plot.hastag", (args, qd) ->    
+                KajetansMod.plots.getDataBank(ProtectionBank.class).hasTag(((Location) args[0]).getWorld(), ((Location) args[0]).getBlockPos(), args[1].toString())); 
+        registerConsumer(list, "plot.add", (args, qd) ->    
+                addPlot(args));
+        registerFunction(list, "plot.getid", (args, qd) ->    
+                KajetansMod.plots.getDataBank(ProtectionBank.class).getFirstRegionId(((Location) args[0]).getWorld(), ((Location) args[0]).getBlockPos()));
+        registerFunction(list, "plot.canbuild", (args, qd) ->    
+                KajetansMod.plots.getDataBank(ProtectionBank.class).canBuild(((Location) args[0]).getWorld(), ((Location) args[0]).getBlockPos(), (EntityPlayer) args[1]));
+        registerFunction(list, "plot.getname", (args, qd) ->    
+                KajetansMod.plots.getDataBank(ProtectionBank.class).getFirstRegionName(((Location) args[0]).getWorld(), ((Location) args[0]).getBlockPos()));
+
+        // -------------------------------------------------------------  
+        // Quest-Bibliothek   
+        // -------------------------------------------------------------    
+        registerConsumer(list, "quest.setinfo", (args, qd) -> 
+                qd.setInfo(QuestUtils.connect(args, 0)));
+        registerConsumer(list, "quest.end", (args, qd) -> 
+                endQuest(args, qd));
+        registerConsumer(list, "quest.playerstolist", (args, qd) ->  
+                questersToList(args, qd)); 
+        registerFunction(list, "quest.getplayeramount", (args, qd) ->      
+                getNumberOfQuesters(qd));
+        registerConsumer(list, "quest.give", (args, qd) ->  
+                giveQuest(args, qd));
+        registerConsumer(list, "quest.force", (args, qd) ->  
+                forceQuest(qd)); 
+        registerFunction(list, "quest.isactive", (args, qd) ->  
+                KajetansMod.quest.getQuests().stream().anyMatch(q -> q.getName().equals(args[0])));
+
+        // -------------------------------------------------------------  
+        // Scoreboard-Bibliothek   
+        // -------------------------------------------------------------  
+        registerConsumer(list, "sb.add", (args, qd) ->  
+                addToScoreBoard(args, qd));
+        registerConsumer(list, "sb.remove", (args, qd) ->  
+                removeFromScoreBoard(args, qd));
+        registerConsumer(list, "sb.reset", (args, qd) ->  
+                doForGroup(args[0], qd, p -> KajetansMod.scoreboard.resetScoreboard((EntityPlayer) p)));
+
+        // -------------------------------------------------------------  
+        // Effect-Bibliothek   
+        // ------------------------------------------------------------- 
+        registerConsumer(list, "effect.playsound", (args, qd) -> 
+                playSound(args));
+        registerConsumer(list, "effect.play", (args, qd) -> 
+                playParticle(args));
+
+        // -------------------------------------------------------------  
+        // Inventory-Bibliothek   
+        // -------------------------------------------------------------
+        registerConsumer(list, "inv.new", (args, qd) -> 
+                qd.setVar(args[0], new SnuviInventory(args[2].toString(), QuestUtils.getInt(args[1]), qd.getNewId())));   
+        registerConsumer(list, "inv.loadblock", (args, qd) -> 
+                qd.setVar(args[0], newInventory((Location) args[1], qd, args[2].toString())));
+        registerConsumer(list, "inv.setitem", (args, qd) -> 
+                ((IInventory) args[0]).setInventorySlotContents(QuestUtils.getInt(args[1]), ((ItemStack) args[2]).copy()));
+        registerFunction(list, "inv.getitem", (args, qd) -> 
+                ((IInventory) args[0]).getStackInSlot(QuestUtils.getInt(args[1])));
+        registerConsumer(list, "inv.open", (args, qd) -> 
+                new QuestInventoryHolder((SnuviInventory) args[0], (EntityPlayer) args[1], qd).openForPlayer((EntityPlayerMP) args[1]));
+        registerConsumer(list, "inv.close", (args, qd) -> 
+                ((EntityPlayer) args[0]).closeScreen()); 
+
+        // -------------------------------------------------------------  
+        // Time-Bibliothek   
+        // -------------------------------------------------------------
+        registerFunction(list, "time.get", (args, qd) ->                            
+                System.currentTimeMillis());
+        registerFunction(list, "time.nextday", (args, qd) ->         
+                getNextDay(args));   
+        registerFunction(list, "time.getyear", (args, qd) ->         
+                getYear(args));   
+        registerFunction(list, "time.getmonth", (args, qd) ->         
+                getMonth(args));   
+        registerFunction(list, "time.getday", (args, qd) ->         
+                getDay(args));   
+        registerFunction(list, "time.gethour", (args, qd) ->         
+                getHour(args));   
+        registerFunction(list, "time.getminute", (args, qd) ->         
+                getMinute(args));   
+        registerFunction(list, "time.getsecond", (args, qd) ->         
+                getSecond(args));                   
+
+        // -------------------------------------------------------------  
+        // Read-Bibliothek   
+        // -------------------------------------------------------------
+        registerFunction(list, "read.player", (args, qd) -> 
+                Utils.getPlayerByName(args[0].toString()));
+        registerFunction(list, "read.location", (args, qd) -> 
+                QuestUtils.getLocation(args[0].toString()));
+        registerFunction(list, "read.item", (args, qd) -> 
+                QuestUtils.getItemStack(args, 0));
+        registerFunction(list, "read.spawnmob", (args, qd) -> 
+                NmsUtilities.getEntityFromNbtString(QuestUtils.connect(args," ", 1).replace("'", "\""), (Location) args[0]));
+
+        // -------------------------------------------------------------  
+        // Text-Bibliothek   
+        // -------------------------------------------------------------
+        registerFunction(list, "text.location", (args, qd) -> 
+                QuestUtils.getLocationString((Location) args[0]));
+        registerFunction(list, "text.item", (args, qd) -> 
+                QuestUtils.getItemStackString((ItemStack) args[0]));
+
+        // -------------------------------------------------------------    
+        // Ohne Bibliothek
+        // -------------------------------------------------------------    
+        registerFunction(list, "add", (args, qd) -> 
+                convertDouble(Arrays.stream(args).mapToDouble(s -> QuestUtils.getDouble(s)).sum()));
+        registerFunction(list, "sub", (args, qd) -> 
+                numberHandler(args[0], args[1], (a, b) -> a - b, (a, b) -> a - b, (a, b) -> a - b));
+        registerFunction(list, "inc", (args, qd) -> 
+                increaseVar(args[0], qd, 1)); 
+        registerFunction(list, "dec", (args, qd) -> 
+                increaseVar(args[0], qd, -1));
+        registerFunction(list, "mul", (args, qd) -> 
+                numberHandler(args[0], args[1], (a, b) -> a * b, (a, b) -> a * b, (a, b) -> a * b));
+        registerFunction(list, "div", (args, qd) -> 
+                QuestUtils.getDouble(args[0]) / QuestUtils.getDouble(args[1]));
+        registerFunction(list, "getvar", (args, qd) -> 
+                qd.getVar(args[0].toString()));
+        registerConsumer(list, "setvar", (args, qd) -> 
+                qd.setVar(args[0].toString(), args[1]));
+        registerConsumer(list, "removevar", (args, qd) -> 
+                qd.removeVar(args[0].toString()));
+        registerFunction(list, "getglobalvar", (args, qd) ->                    
+                getGlobalVar(args));
+        registerAlias(list, "ggv", "getglobalvar");
+        registerConsumer(list, "setglobalvar", (args, qd) -> 
+                setGlobalVar(args));                          
+        registerAlias(list, "sgv", "setglobalvar");
+        registerConsumer(list, "msg", (args, qd) -> 
+                sendMessageToGroup(args[0], qd, QuestUtils.connect(args, 1)));
+        registerConsumer(list, "reset", (args, qd) -> 
+                qd.resetOverflowProtection());
+        registerConsumer(list, "wait", (args, qd) -> 
+                { qd.resetOverflowProtection(); throw new HoldCodeException(); });
+        registerConsumer(list, "term", (args, qd) -> 
+                { KajetansMod.quest.term(qd); throw new HoldCodeException(); });
+        registerConsumer(list, "goto", (args, qd) -> 
+                qd.gotoLabel(args[0].toString()));
+        registerConsumer(list, "sgoto", (args, qd) -> 
+                scheduleGoto(args, qd));
+        registerConsumer(list, "gosub", (args, qd) -> 
+                qd.gotoLabelWithReturn(args[0].toString()));
+        registerConsumer(list, "return", (args, qd) -> 
+                qd.doReturn());
+        registerConsumer(list, "try", (args, qd) -> 
+                tryFunction(args, qd));                   
+        registerConsumer(list, "catch", (args, qd) -> 
+                catchFunction(qd));
+        registerConsumer(list, "if", (args, qd) -> 
+                ifFunction(args, qd));   
+        registerConsumer(list, "else", (args, qd) -> 
+                elseFunction(args, qd));   
+        registerConsumer(list, "while", (args, qd) -> 
+                whileFunction(args, qd)); 
+        registerFunction(list, "equal", (args, qd) -> 
+                isEqual(args));
+        registerAlias(list, "equals", "equal");
+        registerFunction(list, "less", (args, qd) -> 
+                QuestUtils.getDouble(args[0]) < QuestUtils.getDouble(args[1]));
+        registerFunction(list, "greater", (args, qd) -> 
+                QuestUtils.getDouble(args[0]) > QuestUtils.getDouble(args[1]));
+        registerFunction(list, "notequal", (args, qd) -> 
+                !isEqual(args));
+        registerFunction(list, "lessequal", (args, qd) -> 
+                QuestUtils.getDouble(args[0]) <= QuestUtils.getDouble(args[1]));
+        registerFunction(list, "greaterequal", (args, qd) -> 
+                QuestUtils.getDouble(args[0]) >= QuestUtils.getDouble(args[1]));
+        registerFunction(list, "invert", (args, qd) -> 
+                !((boolean) args[0]));
+        registerFunction(list, "and", (args, qd) -> 
+                Arrays.stream(args).allMatch(s -> s.equals(true)));
+        registerFunction(list, "or", (args, qd) -> 
+                Arrays.stream(args).anyMatch(s -> s.equals(true)));
+        registerFunction(list, "removeformat", (args, qd) -> 
+                QuestUtils.connect(args, 0).replaceAll("§.", ""));
+        registerFunction(list, "concatlist", (args, qd) ->      
+                ((List<Object>) args[0]).stream().limit(QuestUtils.getInt(args[3]) + 1).skip(QuestUtils.getInt(args[2])).map(o -> o.toString()).collect(Collectors.joining(args[1].toString())));
+        registerConsumer(list, "split", (args, qd) ->      
+                split(args, qd));             
+        registerFunction(list, "concat", (args, qd) ->                            
+                QuestUtils.connect(args, 0));
+        registerFunction(list, "tolowercase", (args, qd) ->                            
+                QuestUtils.connect(args, 0).toLowerCase());
+        registerFunction(list, "touppercase", (args, qd) ->                            
+                QuestUtils.connect(args, 0).toUpperCase());
+        registerFunction(list, "concatspace", (args, qd) ->                            
+                QuestUtils.connect(args, " ", 0));     
+        registerFunction(list, "onlyletters", (args, qd) ->                            
+                onlyLetters(QuestUtils.connect(args, 0)));
+        registerConsumer(list, "command", (args, qd) -> 
+                KajetansMod.server.commandManager.executeCommand(KajetansMod.server, QuestUtils.connect(args, "", 0)));
+        registerConsumer(list, "waitfor", (args, qd) ->     
+                waitFor(args, qd));  
+        
+        methods = list.toArray(new BiFunction[list.size()]);
+    }
+    
+    public boolean printStack = false;
+
+    @SuppressWarnings("unchecked")
+    public Object parseFunction(QuestData qd, int function, Object[] args)
+    {
+        try
+        {
+            qd.exeClock.pushTime(qd.isBenchmarking());
+            Object o = methods[function].apply(args, qd);
+            qd.exeClock.pushTime(qd.isBenchmarking());
+            return o;
+        }
+        catch(Exception ex)
+        {
+            if(ex instanceof HoldCodeException)
+            {
+                throw new HoldCodeException();
+            }
+            if(printStack)
+            {
+                ex.printStackTrace();
+            }
+            if(qd.getTryMode())
+            {
+                qd.setTryMode(false);
+                qd.setTryFail(true);
+                qd.setVar("error", ex.getClass().getSimpleName());
+                qd.goHigher();
+                return 0;
+            }
+            printQuestException(qd, ex, translator.inverse().get(function) + "(" + Arrays.stream(args).map(o -> String.valueOf(o)).collect(Collectors.joining(", ")) + ")");     
+            throw new HoldCodeException();
+        }
+    }
+    
+    public void printQuestException(QuestData qd, Exception ex, String line)
+    {
+        qd.resetOverflowProtection();
+        sendToDevsWithSuffix(qd, "§cError in");
+        if(qd.isScript())
+        {
+            sendToDevsWithHelpList(qd, "§cScript", qd.getName());
+        }
+        else
+        {
+            sendToDevsWithHelpList(qd, "§cQuest", qd.getName());
+        }
+        sendToDevsWithHelpList(qd, "§cZeile", line);
+        sendToDevsWithHelpList(qd, "§cZeilennummer", String.valueOf(qd.getActualCodeLine() + 1));
+        sendToDevsWithHelpList(qd, "§cZeilenpfad", qd.getTreePath());
+        if(ex.getLocalizedMessage() == null)
+        {
+            sendToDevsWithHelpList(qd, "§cException", ex.getClass().getSimpleName());
+        }
+        else
+        {
+            sendToDevsWithHelpList(qd, "§cException", ex.getClass().getSimpleName() + " - " + ex.getLocalizedMessage());
+        }
+        if(ex instanceof IllegalStringException)
+        {
+            sendToDevsWithHelpList(qd, "§cUngültiger Wert", ((IllegalStringException) ex).getBadString());
+        }
+    }
+    
+    // -----------------------
+    // Quest Befehle
+    // -----------------------
+    
+    private Class getClass(String s)
+    {
+        try
+        {
+            return Class.forName(s);
+        }
+        catch(ClassNotFoundException ex)
+        {
+            throw new IllegalStringException(s);
+        }
+    }
+    
+    private int getItemAmountChest(Object[] args) throws IllegalStringLocationException, IllegalItemStackStringException
+    {       
+        Location l = (Location) args[0];
+        TileEntity te = l.getWorld().getTileEntity(l.getBlockPos());
+        if(te == null || !(te instanceof TileEntityChest))
+        {
+            return 0;
+        }        
+        return InventoryUtils.searchInventoryFor(((TileEntityChest) te), (ItemStack) args[2], (boolean) args[1]);
+    }
+    
+    private ItemStack addItemAmountChest(Object[] args) throws IllegalStringLocationException, IllegalItemStackStringException
+    {       
+        Location l = (Location) args[0];
+        ItemStack stack = ((ItemStack) args[1]).copy();
+        TileEntity te = l.getWorld().getTileEntity(l.getBlockPos());
+        if(te == null || !(te instanceof TileEntityChest))
+        {
+            return stack;
+        }        
+        stack.setCount(InventoryUtils.addToInventory((TileEntityChest) te, stack));  
+        return stack;
+    }
+    
+    private ItemStack removeItemAmountChest(Object[] args) throws IllegalStringLocationException, IllegalItemStackStringException
+    {       
+        Location l = (Location) args[0];
+        ItemStack stack = ((ItemStack) args[1]).copy();
+        TileEntity te = l.getWorld().getTileEntity(l.getBlockPos());
+        if(te == null || !(te instanceof TileEntityChest))
+        {
+            return stack;
+        }        
+        stack.setCount(InventoryUtils.removeFromInventory((TileEntityChest) te, stack));  
+        return stack;
+    }
+    
+    private Object getCoordOfLocation(Object[] args) throws IllegalStringLocationException
+    {
+        Location l = (Location) args[0];
+        switch(args[1].toString())
+        {
+            case "x":
+                return l.getPos().xCoord;
+            case "y":
+                return l.getPos().yCoord;
+            case "z":
+                return l.getPos().zCoord;
+            case "bx":
+                return (int) l.getPos().xCoord;
+            case "by":
+                return (int) l.getPos().yCoord;
+            case "bz":
+                return (int) l.getPos().zCoord;
+            case "w":
+                return ModDimensions.getWorldName(l.getWorld());
+            default:
+                return null;
+        }
+    }
+    
+    private void cloneBlock(Object[] args) throws IllegalStringLocationException
+    {
+        Location l = (Location) args[1];
+        IBlockState state = getBlockState((Location) args[0]);
+        l.getWorld().setBlockState(l.getBlockPos(), state);
+    }
+    
+    private ItemStack removeItem(Object[] args) throws PlayerNotFoundException, IllegalItemStackStringException
+    {       
+        ItemStack stack = ((ItemStack) args[1]).copy();
+        stack.setCount(InventoryUtils.removeFromInventory(((EntityPlayer) args[0]).inventory, stack));
+        return stack;
+    }
+    
+    private void dropItem(Object[] args) throws IllegalStringLocationException, IllegalItemStackStringException
+    {
+        Location l = (Location) args[0];
+        World w = l.getWorld();
+        BlockPos pos = l.getBlockPos();
+        ItemStack stack = ((ItemStack) args[1]).copy();
+        int amount = stack.getCount();
+        while(amount > 64)
+        {            
+            stack.setCount(64);
+            amount -= 64;
+            ItemStackUtils.drop(w, pos, stack);
+        }
+        if(amount > 0)
+        {
+            stack.setCount(amount);
+            ItemStackUtils.drop(w, pos, stack);
+        }
+    }
+    
+    private ItemStack giveItem(Object[] args) throws PlayerNotFoundException, IllegalItemStackStringException
+    {
+        EntityPlayer affectedPlayer = ((EntityPlayer) args[0]);                   
+        ItemStack stack = ((ItemStack) args[1]).copy();
+        int amount = stack.getCount();
+        InventoryPlayer inv = affectedPlayer.inventory;
+        int left = 0;
+        while(amount > 64)
+        {            
+            stack.setCount(64);
+            amount -= 64;
+            left += InventoryUtils.addToInventory(inv, stack);
+        }
+        stack.setCount(amount);
+        left += InventoryUtils.addToInventory(inv, stack);
+        stack.setCount(left);
+        return stack;
+    }
+    
+    private void endQuest(Object[] args, QuestData qd) throws NumberFormatException, UnsupportedOperationException
+    {           
+        if(qd.isScript())
+        {
+            throw new UnsupportedOperationException();
+        }
+        qd.getPlayers().stream().forEach((p) -> 
+        {
+            KajetansMod.quest.send(p, "Du hast die Quest geschafft!");
+        });
+        KajetansMod.quest.term(qd);
+        throw new HoldCodeException();
+    }
+    
+    private void setBlock(Object[] args, QuestData qd) throws IllegalStringLocationException, IllegalItemStackStringException
+    {           
+        Location l = (Location) args[0];
+        ItemStack stack = (ItemStack) args[1];
+        l.getWorld().setBlockState(l.getBlockPos(), Block.getBlockFromItem(stack.getItem()).getStateFromMeta(stack.getMetadata()));
+        qd.increaseOverflowProtection();
+    }
+    
+    private void setBlockWithData(Object[] args, QuestData qd) throws IllegalStringLocationException
+    {           
+        Location l = (Location) args[0];
+        if(args.length >= 4)
+        {
+            NmsUtilities.setBlockWithData(l.getWorld(), l.getBlockPos(), QuestUtils.getInt(args[2]), Block.getBlockFromName(args[1].toString()), QuestUtils.connect(args, 3));
+        }
+        else
+        {
+            NmsUtilities.setBlockWithData(l.getWorld(), l.getBlockPos(), QuestUtils.getInt(args[2]), Block.getBlockFromName(args[1].toString()), null);
+        }
+        qd.increaseOverflowProtection();
+    }
+    
+    private void dropInventory(Object[] args)
+    {
+        ((EntityPlayer) args[0]).inventory.dropAllItems();
+    }
+    
+    private void playSound(Object[] args) throws IllegalStringLocationException
+    {
+        Location l = ((Location) args[0]);
+        Vec3d v = l.getPos();
+        EffectUtils.playSound((WorldServer) l.getWorld(), ReflectionUtils.getSoundEvent(args[1].toString()), SoundCategory.valueOf(args[2].toString()), v.xCoord, v.yCoord, v.zCoord);
+    }
+    
+    private void playSoundPlayer(Object[] args) throws IllegalStringLocationException
+    {
+        EffectUtils.playSound((EntityPlayerMP) args[0], ReflectionUtils.getSoundEvent(args[1].toString()), SoundCategory.valueOf(args[2].toString()));
+    }
+    
+    private void playParticle(Object[] args) throws IllegalStringLocationException
+    {
+        Location l = ((Location) args[0]).add(0.5, 0.5, 0.5);
+        EffectUtils.spawnParticle((WorldServer) l.getWorld(), EnumParticleTypes.getByName(args[1].toString()), l.getPos(), QuestUtils.getInt(args[2]));
+    }
+    
+    private void setEntityEquip(Object[] args) throws IllegalStringLocationException, EntityNotFoundException, IllegalItemStackStringException
+    {
+        EntityLivingBase liv = (EntityLivingBase) args[0];  
+        ItemStack stack = ((ItemStack) args[2]).copy();
+        switch(args[1].toString())
+        {
+            case "hand":
+                liv.setItemStackToSlot(EntityEquipmentSlot.MAINHAND, stack);
+                return;
+            case "head":
+                liv.setItemStackToSlot(EntityEquipmentSlot.HEAD, stack);
+                return;
+            case "chest":
+                liv.setItemStackToSlot(EntityEquipmentSlot.CHEST, stack);
+                return;
+            case "legs":
+                liv.setItemStackToSlot(EntityEquipmentSlot.LEGS, stack);
+                return;
+            case "feet":
+                liv.setItemStackToSlot(EntityEquipmentSlot.FEET, stack);
+                return;
+            case "offhand":
+                liv.setItemStackToSlot(EntityEquipmentSlot.OFFHAND, stack);
+        }
+    }  
+    
+    private ItemStack getEntityEquip(Object[] args) throws IllegalStringLocationException, EntityNotFoundException
+    {
+        EntityLivingBase liv = (EntityLivingBase) args[0];        
+        switch(args[1].toString())
+        {
+            case "hand":
+                return liv.getItemStackFromSlot(EntityEquipmentSlot.MAINHAND);
+            case "head":
+                return liv.getItemStackFromSlot(EntityEquipmentSlot.HEAD);
+            case "chest":
+                return liv.getItemStackFromSlot(EntityEquipmentSlot.CHEST);
+            case "legs":
+                return liv.getItemStackFromSlot(EntityEquipmentSlot.LEGS);
+            case "feet":
+                return liv.getItemStackFromSlot(EntityEquipmentSlot.FEET);
+            case "offhand":
+                return liv.getItemStackFromSlot(EntityEquipmentSlot.OFFHAND);
+        }
+        return ItemStack.EMPTY;
+    } 
+    
+    @SuppressWarnings("unchecked")
+    private void removeEntities(Object[] args) throws IllegalStringLocationException
+    {
+        Class<? extends Entity> c = (Class<? extends Entity>) getClass("org.bukkit.entity." + args[0]);
+        if(c == Entity.class || c == EntityVillager.class || c == EntityArmorStand.class || c == EntityItemFrame.class || c == EntityHanging.class)
+        {
+            return;
+        }
+        Location l = (Location) args[1];
+        Utils.getNearbyEntities(l.getWorld(), l.getPos(), QuestUtils.getDouble(args[2]), c).stream().forEach(ent -> 
+        {
+            ent.setDead();
+        });
+    }
+    
+    private void ifFunction(Object[] args, QuestData qd) throws IfWithoutStatementException, NoChildTreeException
+    {
+        if(Arrays.stream(args).allMatch(s -> s.equals(true)))
+        {
+            qd.goDeeper(false);
+            qd.setElseMode(false);
+            return;
+        }
+        //System.out.println("ELSE MODE AN .... ZEILE: " + qd.getActualCodeLine());
+        qd.setElseMode(true);
+    }  
+    
+    private void elseFunction(Object[] args, QuestData qd) throws IfWithoutStatementException, NoChildTreeException
+    {
+        //System.out.println("ZEILE ... vll tiefer ..." + qd.getActualCodeLine());
+        if(qd.getElseMode())
+        {
+            //System.out.println("TIEFER");
+            qd.setElseMode(false);
+            qd.goDeeper(false);
+        }
+    } 
+    
+    private void whileFunction(Object[] args, QuestData qd) throws IfWithoutStatementException, NoChildTreeException
+    {
+        if(Arrays.stream(args).allMatch(s -> s.equals(true)))
+        {
+            qd.goDeeper(true);
+        }
+    } 
+    
+    private void setSign(Object[] args) throws IllegalStringLocationException
+    {
+        Location l = (Location) args[0];
+        TileEntitySign sign = (TileEntitySign) l.getWorld().getTileEntity(l.getBlockPos());
+        sign.signText[QuestUtils.getInt(args[1])] = new TextComponentString(QuestUtils.connect(args, 2));
+    }
+    
+    private String getSign(Object[] args) throws IllegalStringLocationException
+    {
+        Location l = (Location) args[0];
+        TileEntitySign sign = (TileEntitySign) l.getWorld().getTileEntity(l.getBlockPos());
+        return sign.signText[QuestUtils.getInt(args[1])].getUnformattedText();
+    }
+
+    private void spawnItemFrame(Object[] args) throws IllegalStringLocationException, IllegalItemStackStringException
+    {
+        Location l = ((Location) args[0]);
+        EntityItemFrame frame = new EntityItemFrame(l.getWorld(), l.getBlockPos(), EnumFacing.byName(args[1].toString()));
+        frame.setDisplayedItem(((ItemStack) args[2]).copy());
+    }
+                   
+    private void nameEntity(Object[] args) throws EntityNotFoundException
+    {
+        Entity ent = (Entity) args[0];
+        ent.setCustomNameTag(QuestUtils.connect(args, 1));
+        if(args.length >= 3)
+        {
+            ent.setAlwaysRenderNameTag((boolean) args[2]);   
+            return;
+        }
+        ent.setAlwaysRenderNameTag(true);  
+    }
+    
+    private EntityPlayer getQuestLeader(QuestData qd) throws PlayerNotFoundException
+    {
+        if(qd.isScript())
+        {
+            throw new UnsupportedOperationException();
+        }
+        return qd.getPlayers().get(0);
+    }
+                  
+    @SuppressWarnings(value = "unchecked")
+    private void sortList(List<Object> args, QuestData qd) 
+    {
+        Collections.sort(args, (Object o1, Object o2) -> ((Comparable) o1).compareTo(o2));
+    }
+    
+    private void questersToList(Object[] args, QuestData qd) throws UnsupportedOperationException 
+    {
+        if(qd.isScript())
+        {
+            throw new UnsupportedOperationException();
+        }
+        qd.setVar(args[0], qd.getPlayers());
+    }
+    
+    private int getNumberOfQuesters(QuestData qd) throws UnsupportedOperationException
+    {
+        if(qd.isScript())
+        {
+            throw new UnsupportedOperationException();
+        }
+        return qd.getPlayers().size();
+    }
+                   
+    private void giveQuest(Object[] args, QuestData qd) throws PlayerNotFoundException
+    {
+        if(qd.isScript())
+        {
+            EntityPlayer p = ((EntityPlayer) args[0]);
+            if(!KajetansMod.quest.hasQuest(p))
+            {
+                KajetansMod.quest.startQuest(KajetansMod.server, args[1].toString(), p, null, null);
+            }
+            return;
+        }
+        EntityPlayer p = qd.getPlayers().get(0);
+        if(Permission.hasPermission(p, Permissions.SCRIPT_ERROR))
+        {
+            qd.loadNewCode(args[0].toString(), p);
+            return;
+        }
+        qd.loadNewCode(args[0].toString(), KajetansMod.server);
+    }
+    
+    private void forceQuest(QuestData qd) throws UnsupportedOperationException
+    {
+        if(qd.isScript())
+        {
+            throw new UnsupportedOperationException();
+        }
+        Object name = qd.getVar("force");
+        if(name != null)
+        {
+            qd.loadNewCode(name.toString(), KajetansMod.server);
+        }
+    }
+               
+    private void addToScoreBoard(Object[] args, QuestData qd) throws PlayerNotFoundException
+    {
+        int id = QuestUtils.getInt(args[1]);
+        String message = QuestUtils.connect(args, 2);
+        doForGroup(args[0], qd, p -> KajetansMod.scoreboard.getScoreboard((EntityPlayer) p).addText(id, message));
+    }
+    
+    private void removeFromScoreBoard(Object[] args, QuestData qd) throws PlayerNotFoundException
+    {
+        int id = QuestUtils.getInt(args[1]);
+        doForGroup(args[0], qd, p -> KajetansMod.scoreboard.getScoreboard((EntityPlayer) p).removeText(id));
+    }
+
+    private void sendToActionBar(Object[] args, QuestData qd) throws PlayerNotFoundException
+    {
+        String text = QuestUtils.connect(args, 1);
+        doForGroup(args[0], qd, p -> NmsUtilities.sendActionBar((EntityPlayerMP) p, text));
+    }
+
+    private void kickPlayerFromQuest(Object[] args, QuestData qd) throws PlayerNotFoundException, UnsupportedOperationException
+    {
+        if(qd.isScript())
+        {
+            throw new UnsupportedOperationException();
+        }
+        KajetansMod.quest.removePlayerFromQuest((EntityPlayer) args[0], qd);
+    }
+                   
+    private void scheduleGoto(Object[] args, QuestData qd)
+    {
+        KajetansMod.scheduler.scheduleTask(() -> 
+        {
+            if(!qd.isValid())
+            {
+                return;
+            }
+            try
+            {
+                qd.gotoLabel(args[1].toString());
+                qd.setIsWaiting(false);
+                qd.runCode();
+            }
+            catch(NoChildTreeException | CodeTooLongException | GotoLabelNotFoundException ex)
+            {
+                printQuestException(qd, ex, "(Scheduled Goto)");
+            }
+        }, QuestUtils.getInt(args[0]));
+    }
+    
+    private void waitFor(Object[] args, QuestData qd) throws UnsupportedOperationException
+    {           
+        qd.resetOverflowProtection();
+        int i = QuestUtils.getInt(args[0]);
+        if(i < 1)
+        {
+            throw new UnsupportedOperationException();
+        }
+        qd.setIsWaiting(true);
+        KajetansMod.scheduler.scheduleTask(() -> 
+        {                   
+            if(qd == null || !qd.isValid())
+            {
+                return;
+            }
+            qd.setIsWaiting(false);
+            qd.runCode();
+        }, i); 
+        throw new HoldCodeException();
+    }   
+    
+    private void damageEntity(Object[] args)
+    {
+        if(args.length >= 4)
+        {
+            ((EntityLivingBase) args[0]).damage(QuestUtils.getDouble(args[1]), (Entity) args[2]);
+            return;
+        }
+        ((EntityLivingBase) args[0]).damage(QuestUtils.getDouble(args[1]));
+    }
+    
+    private boolean isBetween(Object[] args)
+    {
+        Location l1 = (Location) args[0];
+        Location l2 = (Location) args[1];
+        Location l3 = (Location) args[2];
+        return l1.getX() >= Math.min(l2.getX(), l3.getX()) &&
+                l1.getX() <= Math.max(l2.getX(), l3.getX()) &&
+                l1.getY() >= Math.min(l2.getY(), l3.getY()) &&
+                l1.getY() <= Math.max(l2.getY(), l3.getY()) &&
+                l1.getZ() >= Math.min(l2.getZ(), l3.getZ()) &&
+                l1.getZ() <= Math.max(l2.getZ(), l3.getZ());
+    }
+    
+    private Number increaseVar(Object var, QuestData qd, int value)
+    {
+        Number n = numberHandler(qd.getVar(var.toString()), (a) -> a + value, (a) -> a + value, (a) -> a + value);
+        qd.setVar(var.toString(), n);
+        return n;
+    }
+    
+    private void addPlot(Object[] args)
+    {
+        Location l1 = (Location) args[0];
+        Location l2 = (Location) args[1];
+        KajetansMod.plots.getDataBank(ProtectionBank.class).addPlot(Math.min(l1.getBlockX(), l2.getBlockX()),
+                    Math.min(l1.getBlockY(), l2.getBlockY()),
+                    Math.min(l1.getBlockZ(), l2.getBlockZ()),
+                    Math.max(l1.getBlockX(), l2.getBlockX()),
+                    Math.max(l1.getBlockY(), l2.getBlockY()),
+                    Math.max(l1.getBlockZ(), l2.getBlockZ()),
+                    l1.getWorld().getName(), null, args[2].toString()); 
+    }
+    
+    private boolean isEqual(Object[] args)
+    {
+        if(args[0] == null)
+        {
+            return args[1] == null;
+        }
+        else if(args[1] == null)
+        {
+            return args[0] == null;
+        }
+        else if(args[1] instanceof ItemStack && args[0] instanceof ItemStack)
+        {
+            return ((ItemStack) args[0]).isSimilar((ItemStack) args[0]);
+        }
+        else if(args[1] instanceof Number && args[0] instanceof Number)
+        {
+            return ((Number) args[0]).doubleValue() == ((Number) args[1]).doubleValue();
+        }
+        else if(args[0] instanceof Location && args[1] instanceof Location)
+        {
+            Location l = (Location) args[0];
+            Location l2 = (Location) args[1];
+            return l.getBlockX() == l2.getBlockX() &&
+                    l.getBlockY() == l2.getBlockY() &&
+                    l.getBlockZ() == l2.getBlockZ();
+        }
+        return args[0].equals(args[1]);
+    }
+    
+    private void tryFunction(Object[] args, QuestData qd) throws NoChildTreeException
+    {
+        qd.goDeeper(false); 
+        qd.setTryMode(true);
+    }
+    
+    private void catchFunction(QuestData qd) throws NoChildTreeException
+    {
+        if(qd.getTryFail())
+        {
+            qd.setTryFail(false);
+            qd.goDeeper(false);
+        }
+    }
+    
+    private Inventory newInventory(Location l, QuestData qd, String s)
+    {
+        Inventory inv = ((InventoryHolder) l.getBlock().getState()).getInventory();
+        int size = inv.getSize();
+        if(size % 9 != 0)
+        {
+            size /= 9;
+            size++;
+            size *= 9;
+        }
+        Inventory inv2 = Bukkit.createInventory(new QuestInventoryHolder(qd.getId(), qd.getNewId()), size, s); 
+        inv2.setContents(inv.getContents());
+        return inv2;
+    }
+    
+    private void split(Object[] args, QuestData qd)
+    {
+        String[] parts = QuestUtils.connect(args, 2).split(args[1].toString());
+        ArrayList<Object> list = new ArrayList<>();
+        for(String s : parts)
+        {
+            list.add(QuestUtils.convertInput(s));
+        }
+        qd.setVar(args[0], list);
+    }
+    
+    private boolean onlyLetters(String s)
+    {
+        for(char c : s.toCharArray())
+        {
+            if(!Character.isLetter(c))
+            {
+                return false;
+            }
+        }
+        return true;
+    }
+    
+    private String getName(Object[] args)
+    {
+        Object o = args[0];
+        if(o instanceof Player)
+        {
+            return ((EntityPlayer) o).getName();
+        }
+        return KajetansMod.playerbank.getDataBank().getName(o.toString());
+    }
+    
+    private String getUuid(Object[] args)
+    {
+        Object o = args[0];
+        if(o instanceof Player)
+        {
+            return ((EntityPlayer) o).getUniqueId().toString();
+        }
+        return KajetansMod.playerbank.getDataBank().getUUID(o.toString());
+    }
+    
+    private String getPotionType(Object[] args)
+    {
+        PotionMeta meta = (PotionMeta) ((SplashPotion) args[0]).getItem().getItemMeta();
+        return meta.getBasePotionData().getType().toString();
+    }
+    
+    private void setTag(Object[] args)
+    {
+        if(args[0] instanceof Player)
+        {
+            KajetansMod.playerbank.getDataBank().setTag((EntityPlayer) args[0], args[1].toString(), QuestUtils.getInt(args[2]));
+            return;
+        }
+        KajetansMod.playerbank.getDataBank().setTag(KajetansMod.playerbank.getDataBank().getUUID(args[0].toString()), args[1].toString(), QuestUtils.getInt(args[2]));
+    }
+    
+    private int getTag(Object[] args)
+    {
+        if(args[0] instanceof Player)
+        {
+            return KajetansMod.playerbank.getDataBank().getTag((EntityPlayer) args[0], args[1].toString());
+        }
+        return KajetansMod.playerbank.getDataBank().getTag(KajetansMod.playerbank.getDataBank().getUUID(args[0].toString()), args[1].toString());
+    }
+    
+    private void setGlobalVar(Object[] args)
+    {
+        if(args[0] instanceof Player)
+        {
+            KajetansMod.quest.getDataBank(QuestBank.class).setVar(args[2].toString(), args[1].toString(), ((EntityPlayer) args[0]).getUniqueId().toString());
+            return;
+        }
+        KajetansMod.quest.getDataBank(QuestBank.class).setVar(args[2].toString(), args[1].toString(), KajetansMod.playerbank.getDataBank().getUUID(args[0].toString()));
+    }
+    
+    private Object getGlobalVar(Object[] args)
+    {
+        if(args[0] instanceof Player)
+        {
+            return KajetansMod.quest.getDataBank(QuestBank.class).getVar(args[1].toString(), ((EntityPlayer) args[0]).getUniqueId().toString());
+        }
+        return KajetansMod.quest.getDataBank(QuestBank.class).getVar(args[1].toString(), KajetansMod.playerbank.getDataBank().getUUID(args[0].toString()));
+    }
+
+    // -------------------------------------------------------------------------    
+    // Block
+    // ------------------------------------------------------------------------- 
+    
+    private int getBlockData(Location l)
+    {
+        IBlockState state = l.getWorld().getBlockState(l.getBlockPos());
+        return state.getBlock().getMetaFromState(state);
+    }
+    
+    private IBlockState getBlockState(Location l)
+    {
+        return l.getWorld().getBlockState(l.getBlockPos());
+    }
+    
+    private ItemStack getStackFromBlock(Location l)
+    {
+        World w = l.getWorld();
+        BlockPos pos = l.getBlockPos();
+        IBlockState state = w.getBlockState(pos);
+        return state.getBlock().getItem(w, pos, state);
+    }
+    
+    // -------------------------------------------------------------------------    
+    // Custom-Handler
+    // ------------------------------------------------------------------------- 
+    
+    private void registerEnchantmentRecipe(Object[] args)
+    {
+        Enchantment e = Enchantment.getByName(args[0].toString());
+        if(e == null)
+        {
+            throw new IllegalStringException(args[0].toString());
+        }
+        ItemStack[] stacks = new ItemStack[args.length - 3];
+        for(int i = 0; i < stacks.length; i++)
+        {
+            stacks[i] = (ItemStack) args[i + 3];
+        }
+        KajetansMod.customs.registerEnchantmentRecipe(e, QuestUtils.getInt(args[1]), QuestUtils.getInt(args[2]), stacks);
+    }
+    
+    private void registerShapelessRecipe(Object[] args)
+    {
+        ItemStack[] stacks = new ItemStack[args.length - 1];
+        for(int i = 0; i < stacks.length; i++)
+        {
+            stacks[i] = (ItemStack) args[i + 1];
+        }
+        KajetansMod.customs.registerShapelessRecipe((ItemStack) args[0], stacks);
+    }
+    
+    private void registerShapedRecipe(Object[] args)
+    {
+        int counter = 0;
+        while(args[counter + 1].getClass() == String.class)
+        {
+            counter++;
+        }
+        String[] s = new String[counter];
+        for(int i = 0; i < s.length; i++)
+        {
+            s[i] = args[i + 1].toString();
+        }
+        
+        ItemStack[] stacks = new ItemStack[args.length - 1 - s.length];
+        for(int i = 0; i < stacks.length; i++)
+        {
+            stacks[i] = (ItemStack) args[i + 1 + counter];
+        }
+        KajetansMod.customs.registerShapedRecipe((ItemStack) args[0], s, stacks);
+    }
+    
+    // -------------------------------------------------------------------------    
+    // Zeit-Handler
+    // ------------------------------------------------------------------------- 
+    
+    private long getNextDay(Object[] args)       
+    {
+        GregorianCalendar cal = GregorianCalendar.from(ZonedDateTime.now());
+        cal.setTimeInMillis((long) args[0]);
+        cal.add(Calendar.DAY_OF_YEAR, 1);
+        cal.set(Calendar.HOUR, 0);
+        cal.set(Calendar.SECOND, 0);
+        cal.set(Calendar.MINUTE, 0);
+        cal.set(Calendar.MILLISECOND, 0);
+        return cal.getTimeInMillis();   
+    } 
+    
+    private int getYear(Object[] args)       
+    {
+        GregorianCalendar cal = GregorianCalendar.from(ZonedDateTime.now());
+        cal.setTimeInMillis((long) args[0]);
+        return cal.get(Calendar.YEAR);   
+    }
+    
+    private int getMonth(Object[] args)       
+    {
+        GregorianCalendar cal = GregorianCalendar.from(ZonedDateTime.now());
+        cal.setTimeInMillis((long) args[0]);
+        return cal.get(Calendar.MONTH) + 1;   
+    }
+    
+    private int getDay(Object[] args)       
+    {
+        GregorianCalendar cal = GregorianCalendar.from(ZonedDateTime.now());
+        cal.setTimeInMillis((long) args[0]);
+        return cal.get(Calendar.DAY_OF_MONTH);   
+    }
+    
+    private int getHour(Object[] args)       
+    {
+        GregorianCalendar cal = GregorianCalendar.from(ZonedDateTime.now());
+        cal.setTimeInMillis((long) args[0]);
+        return cal.get(Calendar.HOUR_OF_DAY);   
+    }
+    
+    private int getMinute(Object[] args)       
+    {
+        GregorianCalendar cal = GregorianCalendar.from(ZonedDateTime.now());
+        cal.setTimeInMillis((long) args[0]);
+        return cal.get(Calendar.MINUTE);   
+    }
+    
+    private int getSecond(Object[] args)       
+    {
+        GregorianCalendar cal = GregorianCalendar.from(ZonedDateTime.now());
+        cal.setTimeInMillis((long) args[0]);
+        return cal.get(Calendar.SECOND);   
+    }
+    
+    // -------------------------------------------------------------------------    
+    // Gruppen-Handler
+    // ------------------------------------------------------------------------- 
+    
+    private void doForGroup(Object group, QuestData qd, Consumer<ICommandSender> c) throws UnsupportedOperationException, PlayerNotFoundException
+    {
+        if(group instanceof String)
+        {
+            switch(group.toString()) 
+            {
+                case "all":
+                    if(qd.isScript())
+                    {
+                        throw new UnsupportedOperationException();
+                    }
+                    qd.getPlayers().forEach(p -> c.accept(p));
+                    break;
+                case "online":
+                    Bukkit.getOnlinePlayers().forEach(p -> c.accept(p));
+                    break;
+                case "dev":
+                    if(qd.isScript())
+                    {
+                        Bukkit.getOnlinePlayers().stream().filter(p -> p.hasPermission("kt.quest.error")).forEach(p -> c.accept(p));
+                        return;
+                    }
+                    qd.getPlayers().stream().filter(p -> p.hasPermission("kt.quest.error")).forEach(p -> c.accept(p));
+                    break;
+                case "server":
+                    c.accept(Bukkit.getConsoleSender());
+                    break;
+                default:
+                    c.accept(Utils.getPlayerByName(group.toString()));
+                    break;
+            }
+            return;
+        }
+        c.accept((EntityPlayer) group);
+    } 
+    
+    private void sendMessageToGroup(Object group, QuestData qd, String message)
+    {
+        doForGroup(group, qd, p -> p.sendMessage(message));
+    }
+    
+    public void sendMessageWithSuffix(QuestData qd, String message)
+    {
+        Module m = KajetansMod.quest;
+        int id = qd.isScript() ? 1 : 0;
+        doForGroup("all", qd, p -> m.send(p, message, id));
+    }
+    
+    private void sendToDevsWithSuffix(QuestData qd, String message)
+    {
+        Module m = KajetansMod.quest;
+        int id = qd.isScript() ? 1 : 0;
+        doForGroup("dev", qd, p -> m.send(p, message, id));
+    }
+    
+    public void sendWarningToAllDevs(String message)
+    {
+        Module m = KajetansMod.quest;
+        String warnMessage = "§4" + message;
+        Bukkit.getOnlinePlayers().stream().filter(p -> p.hasPermission("kt.quest.error")).forEach(p -> m.send(p, warnMessage));
+    }
+    
+    private void sendToDevsWithList(QuestData qd, String message)
+    {
+        Module m = KajetansMod.quest;
+        int id = qd.isScript() ? 1 : 0;
+        doForGroup("dev", qd, p -> m.sendListElement(p, message, id));
+    }
+    
+    private void sendToDevsWithHelpList(QuestData qd, String message, String message2)
+    {
+        Module m = KajetansMod.quest;
+        int id = qd.isScript() ? 1 : 0;
+        doForGroup("dev", qd, p -> m.sendHelpListElement(p, message, message2, id));
+    }   
+        
+    // -------------------------------------------------------------------------    
+    // Integer-Double-Long-Handler
+    // ------------------------------------------------------------------------- 
+    
+    private Number numberHandler(Number a,
+            Function<Integer, Integer> f,
+            Function<Long, Long> f2,
+            Function<Double, Double> f3)
+    {
+        if(a instanceof Integer)
+        {
+            return f.apply(a.intValue());
+        }
+        else if(a instanceof Long)
+        {
+            return f2.apply(a.longValue());
+        }
+        return f3.apply(a.doubleValue());
+    }
+    
+    private Number numberHandler(Object a,
+            Function<Integer, Integer> f,
+            Function<Long, Long> f2,
+            Function<Double, Double> f3)
+    {
+        return numberHandler((Number) a, f, f2, f3);
+    }
+    
+    private Number numberHandler(Number a, Number b, 
+            BiFunction<Integer, Integer, Integer> f,
+            BiFunction<Long, Long, Long> f2,
+            BiFunction<Double, Double, Double> f3)
+    {
+        if(a instanceof Integer && b instanceof Integer)
+        {
+            return f.apply(a.intValue(), b.intValue());
+        }
+        else if((a instanceof Long || a instanceof Integer) && (b instanceof Long || b instanceof Integer))
+        {
+            return f2.apply(a.longValue(), b.longValue());
+        }
+        return f3.apply(a.doubleValue(), b.doubleValue());
+    }
+    
+    private Number numberHandler(Object a, Object b, 
+            BiFunction<Integer, Integer, Integer> f,
+            BiFunction<Long, Long, Long> f2,
+            BiFunction<Double, Double, Double> f3)
+    {
+        return numberHandler((Number) a, (Number) b, f, f2, f3);
+    }
+    
+    private Number convertDouble(Double d)
+    {
+        if(d == d.intValue())
+        {
+            return d.intValue();
+        }
+        else if(d == d.longValue())
+        {
+            return d.longValue();
+        }
+        return d;
+    }
+    
+    // -------------------------------------------------------------------------    
+    // Rundungs-Handler
+    // ------------------------------------------------------------------------- 
+    
+    private Number round(Object o)
+    {
+        Long l = Math.round(QuestUtils.getDouble(o));
+        if(l == l.intValue())
+        {
+            return l.intValue();
+        }
+        return l;
+    }
+    
+    private Number roundDown(Object o)
+    {
+        Double d = Math.floor(QuestUtils.getDouble(o));
+        if(d == d.intValue())
+        {
+            return d.intValue();
+        }
+        return d.longValue();
+    }
+    
+    private Number roundUp(Object o)
+    {
+        Double d = Math.ceil(QuestUtils.getDouble(o));
+        if(d == d.intValue())
+        {
+            return d.intValue();
+        }
+        return d.longValue();
+    }
+}

+ 329 - 0
src/main/java/me/km/snuviscript/QuestUtils.java

@@ -0,0 +1,329 @@
+package me.km.snuviscript;
+
+import me.km.exception.IllegalStringLocationException;
+import me.km.exception.IllegalItemStackStringException;
+import me.km.exception.EntityNotFoundException;
+import me.km.api.Utils;
+import me.km.api.Location;
+import me.km.nms.NmsUtilities;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.stream.Collectors;
+import me.km.dimensions.ModDimensions;
+import me.km.utils.ItemStackUtils;
+import net.minecraft.item.Item;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.util.math.Vec3d;
+
+public class QuestUtils 
+{ 
+    public static Number getNumber(String s)
+    {
+        Double d = Double.parseDouble(s);
+        if(d.intValue() == d)
+        {
+            return d.intValue();
+        }
+        else if(d.longValue() == d)
+        {
+            return d.longValue();
+        }
+        return d;
+    }
+    
+    public static int getInt(Object o)
+    {
+        return ((Number) o).intValue();
+    }
+    
+    public static byte getByte(Object o)
+    {
+        return ((Number) o).byteValue();
+    }
+    
+    public static double getDouble(Object o)
+    {
+        return ((Number) o).doubleValue();
+    }
+    
+    public static float getFloat(Object o)
+    {
+        return ((Number) o).floatValue();
+    }
+    
+    public static String connect(Collection<Object> c, String s, int skip)
+    {
+        return c.stream().skip(skip).map(o -> o.toString()).collect(Collectors.joining(s));
+    }
+    
+    public static String connect(Object[] c, String s, int skip)
+    {
+        return Arrays.stream(c, skip, c.length).map(o -> o.toString()).collect(Collectors.joining(s));
+    }
+    
+    public static String connect(Collection<Object> c, int skip)
+    {
+        return c.stream().skip(skip).map(o -> String.valueOf(o)).collect(Collectors.joining());
+    }
+    
+    public static String connect(Object[] c, int skip)
+    {
+        return Arrays.stream(c, skip, c.length).map(o -> String.valueOf(o)).collect(Collectors.joining());
+    }
+    
+    /** Generiert einen String aus dem gegebenen Double, ein etwaiges <code>.0</code> wird entfernt.
+    * @param d ein Double
+    * @return der generierte String
+    */    
+    public static String doubleToString(double d)
+    {
+        if((int) d == d)
+        {
+            return String.valueOf((int) d);
+        }
+        return String.valueOf(d);
+    }
+    
+    /** Generiert eine Location aus dem gegebenen String.
+    * @param location String-Location
+    * @throws IllegalStringLocationException wenn der String nicht das Format "world-name:x:y:z" oder "..:yaw:pitch" besitzt.
+    * @return die generierte Location
+    */    
+    public static Location getLocation(String location) throws IllegalStringLocationException
+    {
+        String[] parts = location.split(":");
+        try
+        {
+            if(parts.length > 4)
+            {
+                return new Location(ModDimensions.getWorldFromName(parts[0]), new Vec3d(Double.parseDouble(parts[1]), 
+                        Double.parseDouble(parts[2]), Double.parseDouble(parts[3])),
+                        Float.parseFloat(parts[4]), Float.parseFloat(parts[5]));
+            }
+            return new Location(ModDimensions.getWorldFromName(parts[0]), new Vec3d(Double.parseDouble(parts[1]), Double.parseDouble(parts[2]), Double.parseDouble(parts[3])));
+        }
+        catch(Exception ex)
+        {
+            throw new IllegalStringLocationException(location);
+        }
+    }
+    
+    /** Rundet eine Location zu einer Block-Location
+    * @param l Location
+    * @return die generierte Location
+    */    
+    public static Location roundLocation(Location l)
+    {
+        return new Location(l.getWorld(), new BlockPos(l.getPos()));
+    }
+    
+    /** Generiert einen Location-String aus der gegebenen Location.
+    * @param l eine Location
+    * @return Location-String im Format "world-name:x:y:z"
+    */ 
+    public static String getLocationString(Location l)
+    {
+        Vec3d v = l.getPos();
+        return ModDimensions.getWorldName(l.getWorld()) + ":" + v.xCoord + ":" + v.yCoord + ":" + v.zCoord + ":" + l.getYaw()+ ":" + l.getPitch();
+    }
+    
+    /** Generiert einen Block-Location-String aus der gegebenen Location.
+    * @param l eine Location
+    * @return Location-String im Format "world-name:x:y:z"
+    */ 
+    public static String getBlockLocationString(Location l)
+    {
+        Vec3d v = l.getPos();
+        return ModDimensions.getWorldName(l.getWorld()) + ":" + ((int) v.xCoord) + ":" + ((int) v.yCoord) + ":" + ((int) v.zCoord);
+    }
+    
+    /** Gibt das Material eines ItemStack-Strings zurück
+    * @param stack ein ItemStack-String im Format "Material:..." oder "Material"
+    * @throws IllegalItemStackStringException wenn das Material ungültig ist
+    * @return das Material
+    */ 
+    public static Item getMaterial(String stack) throws IllegalItemStackStringException
+    {
+        Item item = Item.getByNameOrId(stack);
+        if(item == null)
+        {
+            throw new IllegalItemStackStringException(stack);
+        }
+        return item;
+    }
+    
+    /** Gibt die DamageValue eines ItemStack-Strings zurück
+    * @param stack ein ItemStack-String im Format "...:DV:..." oder "...:DV"
+    * @throws IllegalItemStackStringException wenn das Format nicht erfüllt ist
+    * @return die DamageValue
+    */
+    public static short getData(String stack) throws IllegalItemStackStringException
+    {
+        String[] parts = stack.split(":");
+        if(parts.length < 2)
+        {
+            throw new IllegalItemStackStringException(stack);
+        }
+        try
+        {
+            short data = Short.parseShort(parts[1]);
+            if(data < 0)
+            {
+                return 0;
+            }
+            return data;
+        }
+        catch(NumberFormatException ex)
+        {
+            throw new IllegalItemStackStringException(stack);
+        }
+    }
+    
+    /** Gibt die Anzahl eines ItemStack-Strings zurück
+    * @param stack ein ItemStack-String im Format "...:..:Amount"
+    * @throws IllegalItemStackStringException wenn das Format nicht erfüllt ist
+    * @return die Anzahl
+    */
+    public static int getAmount(String stack) throws IllegalItemStackStringException
+    {
+        String[] parts = stack.split(":");
+        if(parts.length < 3)
+        {
+            throw new IllegalItemStackStringException(stack);
+        }
+        try
+        {
+            int amount = Integer.parseInt(parts[2]);
+            if(amount < 0)
+            {
+                return 0;
+            }
+            return amount;
+        }
+        catch(NumberFormatException ex)
+        {
+            throw new IllegalItemStackStringException(stack);
+        }
+    }
+    
+    /** Gibt einen ItemStack zurück
+    * @param stack ein ItemStack-String im Format "Material", "Material:DV" oder "Material:DV:Amount"
+    * @throws IllegalItemStackStringException wenn das Format nicht erfüllt ist
+    * @return den ItemStack
+    */
+    public static ItemStack getItemStack(String stack) throws IllegalItemStackStringException
+    {
+        if(stack.startsWith("{"))
+        {
+            return ItemStackUtils.getStackFromNbtString(stack.replace("'", "\""));
+        }
+        Item m = getMaterial(stack);
+        int amount = 1;
+        try
+        {   
+            amount = getAmount(stack);
+        }
+        catch(IllegalItemStackStringException ex)
+        {
+        }
+        short data = 0;
+        try
+        {   
+            data = getData(stack);
+        }
+        catch(IllegalItemStackStringException ex)
+        {
+        }
+        return new ItemStack(m, amount, data);
+    }
+    
+    /** Gibt einen ItemStack zurück
+    * @param args ein Array der mindestens einen gültigen ItemStack-String enthalten muss.
+    * Weiters kann auch ein Name und eine Lore mitgegeben werden, andernfalls muss der
+    * Array vorher an seiner Grenze sein oder null verwendet werden.
+    * @param start die Stelle, an der der ItemStack beginnt
+    * @throws IllegalItemStackStringException wenn das Format des ItemStack-Strings nicht erfüllt ist
+    * @return den ItemStack
+    */
+    public static ItemStack getItemStack(Object[] args, int start) throws IllegalItemStackStringException
+    {
+        if(args[start].toString().startsWith("{"))
+        {
+            return ItemStackUtils.getStackFromNbtString(connect(args, " ", start).replace("'", "\""));
+        }
+        ItemStack stack = getItemStack(args[start].toString());
+        if(args.length >= start + 2)
+        {
+            if(!(args[start + 1] == null))
+            {
+                stack.setStackDisplayName(args[start + 1].toString());
+            }
+        }
+        if(args.length >= start + 3)
+        {
+            if(!(args[start + 2] == null))
+            { 
+                for(int i = start + 2; i < args.length; i++)
+                {
+                    ItemStackUtils.addLore(stack, args[i].toString());
+                }
+            } 
+        }
+        return stack;
+    }
+    
+    /** Gibt einen ItemStack-String zurück
+    * @param stack ein ItemStack
+    * @return den ItemStack-String im Format "Material:DV:Amount"
+    */
+    public static String getItemStackString(ItemStack stack)
+    {
+        return ItemStackUtils.getNbtString(stack);
+    }   
+    
+    /** Konvertiert einen String in die gewünschten Formate (true, false, null, Zahl, ...)
+    * @param s ein String
+    * @return ein konvertiertes Object
+    */
+    public static Object convertInput(String s)
+    {
+        if(s == null)
+        {
+            return null;
+        }
+        else if(s.startsWith("\"") && s.endsWith("\""))
+        {
+            if(s.length() <= 1)
+            {
+                return "\"";
+            }
+            return s.substring(1, s.length() - 1);
+        }
+        else if(s.startsWith("$"))
+        {
+            return new Variable(s);
+        }
+        else if(s.equals("true"))
+        {
+            return true;
+        }
+        else if(s.equals("false"))
+        {
+            return false;
+        }
+        else if(s.equals("null"))
+        {
+            return null;
+        }
+        try
+        {
+            return getNumber(s);
+        }
+        catch(NumberFormatException ex)
+        {
+            return s;
+        }
+    }
+}

+ 53 - 0
src/main/java/me/km/snuviscript/QuestVars.java

@@ -0,0 +1,53 @@
+package me.km.snuviscript;
+
+import me.km.api.Location;
+import net.minecraft.block.state.IBlockState;
+import net.minecraft.entity.Entity;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.world.World;
+
+public class QuestVars 
+{  
+    public static void setBlockVars(QuestData qd, World w, BlockPos pos)
+    {               
+        IBlockState state = w.getBlockState(pos);
+        qd.setVar("block-loc", new Location(w, pos));
+        qd.setVar("block-type", state.getBlock().getRegistryName());
+        qd.setVar("block-data", state.getBlock().getMetaFromState(state));
+    }
+    
+    public static void setPlayerVars(QuestData qd, EntityPlayer p)
+    {
+        qd.setVar("player", p);
+        qd.setVar("player-name", p.getName());
+        qd.setVar("player-loc", new Location(p.world, p.getPositionVector()));
+    }
+    
+    public static void setSecPlayer(QuestData qd, EntityPlayer p)
+    {
+        if(p != null)
+        {
+            qd.setVar("sec-player", p);
+            qd.setVar("sec-player-name", p.getName());
+            qd.setVar("sec-player-loc", new Location(p.world, p.getPositionVector()));
+        }
+    }
+
+    public static void setEntityVars(QuestData qd, Entity ent)
+    {
+        qd.setVar("entity", ent);
+        qd.setVar("entity-name", ent.getDisplayName().getUnformattedText());
+        qd.setVar("entity-loc", new Location(ent.world, ent.getPositionVector()));
+        qd.setVar("entity-type", ent.getClass().getSimpleName());             
+    }
+
+    public static void setItemVars(QuestData qd, ItemStack stack)
+    {
+        qd.setVar("item-type", stack.getItem().getRegistryName());
+        qd.setVar("item-data", stack.getMetadata());
+        qd.setVar("item-amount", stack.getCount());
+        qd.setVar("item", stack);
+    }   
+}

+ 1004 - 0
src/main/java/me/km/snuviscript/QuestsEvents.java

@@ -0,0 +1,1004 @@
+package me.km.snuviscript;
+
+import me.kt.exceptions.IllegalStringLocationException;
+import me.kt.exceptions.EntityNotFoundException;
+import me.kt.KajetansTools;
+import me.kt.api.Utils;
+import me.kt.api.Module;
+import me.kt.api.ModuleListener;
+import me.kt.events.EntityHitByItemEvent;
+import me.kt.events.ItemHitGroundEvent;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.UUID;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+import me.kt.events.PlayerUsesEffectEvent;
+import org.bukkit.Bukkit;
+import org.bukkit.Location;
+import org.bukkit.Material;
+import org.bukkit.entity.Item;
+import org.bukkit.entity.LivingEntity;
+import org.bukkit.entity.Player;
+import org.bukkit.entity.Projectile;
+import org.bukkit.entity.Sheep;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.block.Action;
+import org.bukkit.event.block.BlockBreakEvent;
+import org.bukkit.event.block.BlockPlaceEvent;
+import org.bukkit.event.entity.EntityDamageByEntityEvent;
+import org.bukkit.event.entity.EntityDamageEvent;
+import org.bukkit.event.entity.EntityDeathEvent;
+import org.bukkit.event.entity.EntityTameEvent;
+import org.bukkit.event.entity.PlayerDeathEvent;
+import org.bukkit.event.entity.ProjectileLaunchEvent;
+import org.bukkit.event.entity.ProjectileHitEvent;
+import org.bukkit.event.inventory.ClickType;
+import org.bukkit.event.inventory.CraftItemEvent;
+import org.bukkit.event.inventory.InventoryClickEvent;
+import org.bukkit.event.inventory.InventoryCloseEvent;
+import org.bukkit.event.player.PlayerBucketFillEvent;
+import org.bukkit.event.player.PlayerDropItemEvent;
+import org.bukkit.event.player.PlayerPickupItemEvent;
+import org.bukkit.event.player.PlayerMoveEvent;
+import org.bukkit.event.player.PlayerFishEvent;
+import org.bukkit.event.player.PlayerInteractEntityEvent;
+import org.bukkit.event.player.PlayerInteractEvent;
+import org.bukkit.event.player.PlayerItemConsumeEvent;
+import org.bukkit.event.player.PlayerQuitEvent;
+import org.bukkit.event.player.PlayerRespawnEvent;
+import org.bukkit.inventory.CraftingInventory;
+import org.bukkit.inventory.EquipmentSlot;
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.material.Colorable;
+import org.bukkit.event.player.AsyncPlayerChatEvent;
+import org.bukkit.event.player.PlayerCommandPreprocessEvent;
+import org.bukkit.event.player.PlayerJoinEvent;
+import org.bukkit.event.player.PlayerLoginEvent;
+import org.bukkit.event.player.PlayerPortalEvent;
+import org.bukkit.event.player.PlayerToggleSneakEvent;
+import org.bukkit.event.vehicle.VehicleEnterEvent;
+import org.bukkit.event.vehicle.VehicleExitEvent;
+
+public class QuestsEvents extends ModuleListener
+{               
+    private final HashMap<UUID, UUID> questJoin;
+                 
+    public QuestsEvents(Module m)
+    {    
+        super(m);
+        questJoin = new HashMap<>();
+    }  
+    
+    // -------------------------------------------------------------------------
+    // Basics
+    // -------------------------------------------------------------------------
+        
+    private void doInEvent(QuestData data, Player p, String event, Consumer<QuestData> before, Consumer<QuestData> after)
+    {
+        data.setVar("event", event);
+        if(p != null)
+        {
+            QuestVars.setPlayerVars(data, p); 
+        }
+        if(before != null)
+        {
+            before.accept(data);
+        }
+        data.runCode();
+        if(after != null)
+        {
+            after.accept(data);
+        }
+    }
+    
+    private void handleEvent(Player p, String event, Predicate<? super QuestData> pre, Consumer<QuestData> before, Consumer<QuestData> after)
+    {      
+        KajetansTools.quest.getScripts().stream().filter(pre).filter(qd -> qd.isEventLoaded(event)).forEach(data -> 
+        {
+            doInEvent(data, p, event, before, after);
+        });
+        
+        if(p != null && KajetansTools.quest.hasQuest(p))
+        {
+            QuestData data = KajetansTools.quest.getQuestData(p);
+            if(data != null && data.isEventLoaded(event) && pre.test(data))
+            {
+                doInEvent(data, p, event, before, after);
+            }
+        }      
+    }
+    
+    private void handleEvent(Player p, String event, Consumer<QuestData> before, Consumer<QuestData> after)
+    {      
+        handleEvent(p, event, (qd) -> true, before, after);    
+    }
+    
+    private void handleEvent(Player p, String event, Consumer<QuestData> before)
+    {      
+        handleEvent(p, event, before, null);
+    }
+         
+    // -------------------------------------------------------------------------
+    // Questevents
+    // -------------------------------------------------------------------------
+    
+    @EventHandler
+    public void joinOtherPlayersQuests(PlayerInteractEntityEvent e)
+    {              
+        Player p = e.getPlayer();   
+        if(e.getHand() == EquipmentSlot.OFF_HAND || !(e.getRightClicked() instanceof Player))
+        {
+            return;
+        }
+        Player affectedPlayer = (Player) e.getRightClicked();
+        if(KajetansTools.quest.hasQuest(p) && !KajetansTools.quest.hasQuest(affectedPlayer))
+        {
+            if(questJoin.get(affectedPlayer.getUniqueId()) != null)
+            {
+                if(questJoin.get(affectedPlayer.getUniqueId()).equals(p.getUniqueId()))
+                {
+                    return;
+                }
+            }           
+            questJoin.put(affectedPlayer.getUniqueId(), p.getUniqueId());
+            this.getModule().send(affectedPlayer, p.getName() + " fragt dich, ob du seiner Quest beitreten willst.");
+            this.getModule().send(p, affectedPlayer.getName() + " wurde eine Anfrage gesendet.");
+            return;
+        }
+        if(!KajetansTools.quest.hasQuest(p) && questJoin.get(p.getUniqueId()) != null && KajetansTools.quest.hasQuest(affectedPlayer))
+        {
+            if(questJoin.get(p.getUniqueId()).equals(affectedPlayer.getUniqueId()))
+            {
+                questJoin.remove(p.getUniqueId());
+                KajetansTools.quest.addPlayerToPlayer(p, affectedPlayer);
+            }
+        }
+    }
+
+    @EventHandler
+    public void onPlayerMove(PlayerMoveEvent e)
+    {      
+        Player p = e.getPlayer();
+        
+        KajetansTools.quest.getScripts().stream().filter(qd -> qd.isEventLoaded("player-move")).forEach(data -> 
+        {
+            if(data.removeLocation(QuestUtils.roundLocation(p.getLocation())))
+            {
+                doInEvent(data, p, "player-move", null, null);
+            }
+        });
+        
+        if(KajetansTools.quest.hasQuest(p))
+        {
+            QuestData data = KajetansTools.quest.getQuestData(p);
+            if(data != null && data.isEventLoaded("player-move") && data.removeLocation(QuestUtils.roundLocation(p.getLocation())))
+            {
+                doInEvent(data, p, "player-move", null, null);
+            }
+        }      
+    } 
+    
+    @EventHandler
+    public void QuestClickInventory(InventoryClickEvent e)
+    {
+        Inventory inv = e.getView().getTopInventory();
+        if(inv == null || !(inv.getHolder() instanceof QuestInventoryHolder) ||
+            !(e.getWhoClicked() instanceof Player))            
+        {
+            return;
+        }
+        
+        Player p = (Player) e.getWhoClicked();
+        QuestInventoryHolder qh = (QuestInventoryHolder) inv.getHolder(); 
+        
+        handleEvent(p, "inv-click", qd -> qd.getId() == qh.getQuestId(), (qd) -> 
+        {
+            qd.setVar("inv-id", qh.getInventoryId());
+            qd.setVar("inv-name", inv.getName());
+            qd.setVar("inv-slot", e.getSlot());
+            if(e.getClickedInventory() != null)
+            {
+                qd.setVar("inv-type", e.getClickedInventory().getType().toString());
+            }
+            else
+            {
+                qd.setVar("inv-type", "AIR");
+            }
+            QuestVars.setItemVars(qd, e.getCurrentItem());
+            qd.setVar("cancel", e.isCancelled()); 
+        }, (qd) -> 
+        {
+            e.setCancelled(qd.getBoolean("cancel"));
+        });
+    }
+
+    @EventHandler
+    public void QuestCloseInventory(InventoryCloseEvent e)
+    {
+        Inventory inv = e.getInventory();
+        if(inv == null || !(inv.getHolder() instanceof QuestInventoryHolder) ||
+            !(e.getPlayer()instanceof Player))            
+        {
+            return;
+        }
+        
+        Player p = (Player) e.getPlayer();
+        QuestInventoryHolder qh = (QuestInventoryHolder) inv.getHolder();    
+        
+        handleEvent(p, "inv-close", qd -> qd.getId() == qh.getQuestId(), (qd) -> 
+        {
+            qd.setVar("inv-id", qh.getInventoryId());
+            qd.setVar("inv-name", inv.getName());
+        }, null);
+    }
+    
+    @EventHandler
+    public void QuestOnPlayerRespawn(PlayerRespawnEvent e)
+    {
+        Player p = e.getPlayer();             
+        if(p.getBedSpawnLocation() != null)
+        {           
+            p.setCompassTarget(p.getBedSpawnLocation());
+        }        
+         
+        handleEvent(p, "player-respawn", (qd) -> 
+        {
+            qd.setVar("respawn-loc", e.getRespawnLocation());
+        }, (qd) -> 
+        {
+            try 
+            {
+                e.setRespawnLocation((Location) qd.getVar("respawn-loc"));
+            } 
+            catch (Exception ex) 
+            {
+                KajetansTools.quest.getQuestParser().printQuestException(qd, ex, "(respawn-loc)");
+            }
+        });
+    }
+    
+    @EventHandler
+    public void QuestPlayerDamage(EntityDamageEvent e)
+    {        
+        if(!(e.getEntity() instanceof Player))
+        {
+            return;
+        }
+        Player p = (Player) e.getEntity();   
+
+        handleEvent(p, "player-hurt", (qd) -> 
+        {
+            if(p.getHealth() <= e.getFinalDamage())
+            {
+                qd.setVar("player-killed", true);   
+            }  
+            else
+            {
+                qd.setVar("player-killed", false); 
+            }
+            qd.setVar("player-damage", e.getFinalDamage());   
+            qd.setVar("player-damage-cause", e.getCause().toString());
+            if(e instanceof EntityDamageByEntityEvent)
+            {
+                EntityDamageByEntityEvent byEnt = (EntityDamageByEntityEvent) e;
+                if(byEnt.getDamager() instanceof Player)
+                {
+                    QuestVars.setSecPlayer(qd, (Player) byEnt.getDamager());
+                }
+                else if(byEnt.getDamager() instanceof Projectile)
+                {
+                    qd.setVar("projectile-type", byEnt.getDamager().getType().toString());  
+                    if(((Projectile) byEnt.getDamager()).getShooter() instanceof Player)
+                    {
+                        QuestVars.setSecPlayer(qd, (Player) ((Projectile) byEnt.getDamager()).getShooter());
+                    }
+                }
+            }
+            qd.setVar("cancel", e.isCancelled()); 
+        }, (qd) -> 
+        {
+            e.setCancelled(qd.getBoolean("cancel")); 
+        });
+    } 
+    
+    @EventHandler
+    public void QuestPlayerDeath(PlayerDeathEvent e)
+    {        
+        Player p = e.getEntity();    
+        handleEvent(p, "player-death", (qd) -> 
+        {
+            qd.setVar("message", e.getDeathMessage());
+            qd.setVar("keep-inventory", e.getKeepInventory());
+            qd.setVar("clear", false);
+            QuestVars.setSecPlayer(qd, p.getKiller());
+        }, (qd) -> 
+        {
+            e.setDeathMessage(String.valueOf(qd.getVar("message")));
+            e.setKeepInventory(qd.getBoolean("keep-inventory"));
+            if(qd.getBoolean("clear"))
+            {
+                p.getInventory().clear();
+            } 
+        });
+    } 
+       
+    @EventHandler
+    public void QuestEntityDamage(EntityDamageByEntityEvent e)
+    {        
+        Player p = Utils.getDamager(e);
+        if(p == null)
+        {
+            return;
+        }
+        
+        handleEvent(p, "entity-hurt", (qd) -> 
+        {
+            if(e.getEntity() instanceof LivingEntity)
+            {
+                if(((LivingEntity) e.getEntity()).getHealth() <= e.getFinalDamage())
+                {
+                    qd.setVar("entity-killed", true); 
+                }
+                else
+                {
+                    qd.setVar("entity-killed", false); 
+                }
+            }            
+            QuestVars.setEntityVars(qd, e.getEntity()); 
+            qd.setVar("entity-damage", QuestUtils.doubleToString(e.getFinalDamage()));   
+            qd.setVar("entity-damage-cause", e.getCause().toString());
+            qd.setVar("cancel", e.isCancelled());   
+        }, (qd) -> 
+        {
+            e.setCancelled(qd.getBoolean("cancel"));
+        });
+    }
+    
+    @EventHandler
+    public void QuestEntityDeath(EntityDamageByEntityEvent e)
+    {             
+        if(!(e.getEntity() instanceof LivingEntity))
+        {
+            return;
+        }
+        Player p = Utils.getDamager(e);
+        if(p == null)
+        {
+            return;
+        }       
+
+        LivingEntity ent = (LivingEntity) e.getEntity();  
+        if(ent.getHealth() > e.getFinalDamage())
+        {
+            return;
+        }
+        
+        handleEvent(p, "entity-kill", (qd) -> 
+        {
+            QuestVars.setEntityVars(qd, ent);
+            qd.setVar("entity-damage-cause", e.getCause().toString());
+            qd.setVar("cancel", e.isCancelled());
+        }, (qd) -> 
+        {
+            e.setCancelled(qd.getBoolean("cancel"));
+        });
+    }
+    
+    @EventHandler
+    public void QuestEntityRealDeath(EntityDeathEvent e)
+    {             
+        try
+        {
+            handleEvent(null, "entity-death", (qd) -> 
+            {
+                qd.setVar("entity-xp", e.getDroppedExp());
+                qd.setVar("entity-drops", e.getDrops());
+                LivingEntity ent = e.getEntity();
+                QuestVars.setEntityVars(qd, ent);
+                Player p = ent.getKiller();
+                if(p != null)
+                {
+                    qd.setVar("player-involved", true);
+                    QuestVars.setPlayerVars(qd, p);
+                }  
+                else
+                {
+                    qd.setVar("player-involved", false);
+                }
+            }, (qd) -> 
+            {
+                try
+                {
+                    e.setDroppedExp((int) qd.getVar("entity-xp"));
+                }
+                catch(Exception ex)
+                {
+                    KajetansTools.quest.getQuestParser().printQuestException(qd, ex, "(entity-xp)");           
+                }
+            });
+        }
+        catch(NullPointerException ex)
+        {
+            KajetansTools.quest.sendWarningToConsole(ex.toString() + "  " + ex.getMessage());
+        }
+    }
+    
+    @EventHandler
+    public void QuestProjectileLaunch(ProjectileLaunchEvent e)
+    {        
+        if(e.getEntity().getShooter() instanceof Player == false)
+        {
+            return;
+        }
+        Player p = (Player) e.getEntity().getShooter();             
+        
+        handleEvent(p, "throw", (qd) -> 
+        {
+            qd.setVar("factor", 1);
+            QuestVars.setEntityVars(qd, e.getEntity()); 
+            qd.setVar("cancel", e.isCancelled());  
+        }, (qd) -> 
+        {
+            if(qd.getBoolean("cancel"))
+            {
+                e.setCancelled(true);
+                return;
+            }
+            try
+            {
+                e.getEntity().setVelocity(e.getEntity().getVelocity().multiply((double) qd.getVar("factor")));
+            }
+            catch(Exception ex)
+            {
+                KajetansTools.quest.getQuestParser().printQuestException(qd, ex, "(factor)");           
+            }
+        });
+    }
+    
+    @EventHandler
+    public void QuestProjectileHit(ProjectileHitEvent e)
+    {        
+        if(e.getEntity().getShooter() instanceof Player == false)
+        {
+            return;
+        }           
+        handleEvent((Player) e.getEntity().getShooter(), "throw-hit", (qd) -> QuestVars.setEntityVars(qd, e.getEntity()));
+    }
+    
+    @EventHandler
+    public void QuestEntityTame(EntityTameEvent e)
+    {        
+        if(e.getOwner() instanceof Player == false)
+        {
+            return;
+        }
+        Player p = (Player) e.getOwner();             
+        if(p == null)
+        {
+            return;
+        }
+        
+        handleEvent(p, "entity-tame", (qd) -> 
+        {
+            QuestVars.setEntityVars(qd, e.getEntity()); 
+            qd.setVar("cancel", e.isCancelled());  
+        }, (qd) -> 
+        {
+            e.setCancelled(qd.getBoolean("cancel")); 
+        });
+    }
+    
+    @EventHandler
+    public void QuestEntityShear(PlayerInteractEntityEvent e)
+    {              
+        Player p = e.getPlayer();   
+        if(e.getHand() == EquipmentSlot.OFF_HAND)
+        {
+            return;
+        }
+        if(!(e.getRightClicked() instanceof Sheep))
+        {
+            return;
+        }
+        if(p.getInventory().getItemInMainHand().getType() != Material.SHEARS)
+        {
+            return;
+        }
+        Sheep sheep = (Sheep) e.getRightClicked();
+        handleEvent(p, "entity-shear", (qd) -> 
+        {
+            QuestVars.setEntityVars(qd, sheep);
+            qd.setVar("entity-sheared", sheep.isSheared());
+            qd.setVar("entity-color", ((Colorable) sheep).getColor().name());
+            qd.setVar("cancel", e.isCancelled()); 
+        }, (qd) -> 
+        {
+            e.setCancelled(qd.getBoolean("cancel")); 
+            sheep.setSheared(qd.getBoolean("entity-sheared"));
+        });
+    }
+       
+    @EventHandler
+    public void QuestBlockBreak(BlockBreakEvent e)
+    {        
+        handleEvent(e.getPlayer(), "block-break", (qd) -> 
+        {
+            QuestVars.setBlockVars(qd, e.getBlock());
+            qd.setVar("cancel", e.isCancelled()); 
+        }, (qd) -> 
+        {
+            e.setCancelled(qd.getBoolean("cancel")); 
+        });
+    }
+    
+    @EventHandler
+    public void QuestPlayerLogin(PlayerLoginEvent e)
+    {    
+        Player p = e.getPlayer();
+        handleEvent(p, "player-login", (qd) -> 
+        {       
+            qd.setVar("first-join", p.hasPlayedBefore());
+            qd.setVar("is-banned", p.isBanned());
+            qd.setVar("is-whitelisted", p.isWhitelisted());
+        });
+    }
+    
+    @EventHandler
+    public void QuestPlayerJoin(PlayerJoinEvent e)
+    {    
+        Player p = e.getPlayer();
+        handleEvent(p, "player-join-server", (qd) -> 
+        {       
+            qd.setVar("message", e.getJoinMessage());
+        }, (qd) -> 
+        {
+            e.setJoinMessage(String.valueOf(qd.getVar("message"))); 
+        });
+    }
+    
+    @EventHandler
+    public void onPlayerLeave(PlayerQuitEvent e)
+    {      
+        Player p = e.getPlayer();
+        handleEvent(p, "player-leave", (qd) -> 
+        {       
+            qd.setVar("message", e.getQuitMessage());
+        }, (qd) -> 
+        {
+            e.setQuitMessage(String.valueOf(qd.getVar("message"))); 
+            qd.addToOffQuesters(p);
+        });
+    }
+    
+    @EventHandler
+    public void QuestBucketFill(PlayerBucketFillEvent e)
+    {      
+        handleEvent(e.getPlayer(), "bucket-fill", (qd) -> 
+        {
+            QuestVars.setBlockVars(qd, e.getBlockClicked());
+            qd.setVar("cancel", e.isCancelled()); 
+        }, (qd) -> 
+        {
+            e.setCancelled(qd.getBoolean("cancel")); 
+        });
+    } 
+       
+    @EventHandler
+    public void QuestBlockPlace(BlockPlaceEvent e)
+    {
+        handleEvent(e.getPlayer(), "block-place", (qd) -> 
+        {
+            qd.setVar("block-type-before", e.getBlockReplacedState().getType().toString());
+            QuestVars.setBlockVars(qd, e.getBlock());
+            qd.setVar("cancel", e.isCancelled()); 
+        }, (qd) -> 
+        {
+            e.setCancelled(qd.getBoolean("cancel")); 
+        });
+    }    
+    
+    @EventHandler
+    public void QuestClickAir(PlayerInteractEvent e)
+    {
+        if(e.getClickedBlock() != null || e.getHand() == EquipmentSlot.OFF_HAND)
+        {
+            return;
+        }
+        handleEvent(e.getPlayer(), "air-click", (qd) -> 
+        {
+            qd.setVar("action", e.getAction().toString());
+            qd.setVar("cancel", e.isCancelled()); 
+        }, (qd) -> 
+        {
+            e.setCancelled(qd.getBoolean("cancel")); 
+        });
+    }
+    
+    @EventHandler
+    public void QuestClickBlock(PlayerInteractEvent e)
+    {
+        if(e.getClickedBlock() == null || e.getAction() == Action.PHYSICAL || e.getHand() == EquipmentSlot.OFF_HAND)
+        {
+            return;
+        }       
+        handleEvent(e.getPlayer(), "block-click", (qd) -> 
+        {
+            qd.setVar("action", e.getAction().toString());
+            QuestVars.setBlockVars(qd, e.getClickedBlock());
+            qd.setVar("cancel", e.isCancelled()); 
+        }, (qd) -> 
+        {
+            e.setCancelled(qd.getBoolean("cancel")); 
+        });       
+    }
+    
+    @EventHandler
+    public void QuestInteract(PlayerInteractEvent e)
+    {
+        Player p = e.getPlayer(); 
+        if(e.getAction() != Action.PHYSICAL || e.getClickedBlock() == null ||
+                e.getClickedBlock().getType() == Material.REDSTONE_ORE)
+        {
+            return;
+        }
+        handleEvent(e.getPlayer(), "physical-interact", (qd) -> 
+        {
+            qd.setVar("action", e.getAction().toString());
+            QuestVars.setBlockVars(qd, e.getClickedBlock());
+            qd.setVar("cancel", e.isCancelled()); 
+        }, (qd) -> 
+        {
+            e.setCancelled(qd.getBoolean("cancel")); 
+        });
+    }
+    
+    @EventHandler
+    public void QuestClickEntity(PlayerInteractEntityEvent e)
+    {
+        if(e.getHand() != EquipmentSlot.OFF_HAND)
+        {
+            handleEvent(e.getPlayer(), "entity-click", (qd) -> 
+            {
+                QuestVars.setEntityVars(qd, e.getRightClicked());
+                qd.setVar("cancel", e.isCancelled()); 
+            }, (qd) -> 
+            {
+                e.setCancelled(qd.getBoolean("cancel")); 
+            });
+        }
+    }
+    
+    @EventHandler
+    public void QuestFishing(PlayerFishEvent e)
+    {
+        if(e.getCaught() instanceof Item)
+        {
+            handleEvent(e.getPlayer(), "fishing", (qd) -> 
+            {
+                QuestVars.setItemVars(qd, ((Item) e.getCaught()).getItemStack()); 
+                qd.setVar("cancel", e.isCancelled()); 
+            }, (qd) -> 
+            {
+                e.setCancelled(qd.getBoolean("cancel")); 
+            });
+        }
+    }
+    
+    @EventHandler
+    public void QuestConsuming(PlayerItemConsumeEvent e)
+    {
+        handleEvent(e.getPlayer(), "consume", (qd) -> 
+        {
+            QuestVars.setItemVars(qd, e.getItem());
+            qd.setVar("cancel", e.isCancelled()); 
+        }, (qd) -> 
+        {
+            e.setCancelled(qd.getBoolean("cancel")); 
+        });
+    }
+    
+    @EventHandler
+    public void QuestCrafting(CraftItemEvent e)
+    {  
+        CraftingInventory inv = e.getInventory();
+        if(inv.getResult() != null && e.getWhoClicked() instanceof Player)
+        {
+            handleEvent((Player) e.getWhoClicked(), "craft", (qd) -> 
+            {
+                int amount = inv.getResult().getAmount();
+                if(e.getClick() == ClickType.SHIFT_LEFT || e.getClick() == ClickType.SHIFT_RIGHT)
+                {
+                    amount = amount * Utils.getLowestStackAmount(inv);
+                } 
+                QuestVars.setItemVars(qd, inv.getResult());
+                qd.setVar("item-amount", amount);
+                qd.setVar("cancel", e.isCancelled()); 
+            }, (qd) -> 
+            {
+                e.setCancelled(qd.getBoolean("cancel")); 
+            });
+        }     
+    } 
+    
+    @EventHandler
+    public void QuestDropItem(PlayerDropItemEvent e)
+    {
+        handleEvent(e.getPlayer(), "player-drop", (qd) -> 
+        {
+            QuestVars.setItemVars(qd, e.getItemDrop().getItemStack());   
+            qd.setVar("cancel", e.isCancelled()); 
+        }, (qd) -> 
+        {
+            e.setCancelled(qd.getBoolean("cancel")); 
+        });
+    }
+    
+    @EventHandler
+    public void QuestPickupItem(PlayerPickupItemEvent e)
+    {
+        handleEvent(e.getPlayer(), "player-pickup", (qd) -> 
+        {
+            QuestVars.setItemVars(qd, e.getItem().getItemStack());  
+            qd.setVar("cancel", e.isCancelled()); 
+        }, (qd) -> 
+        {
+            e.setCancelled(qd.getBoolean("cancel")); 
+        });
+    }
+    
+    @EventHandler
+    public void QuestHitByItem(EntityHitByItemEvent e)
+    {
+        handleEvent(e.getPlayer(), "item-hit", (qd) -> 
+        {
+            QuestVars.setItemVars(qd, e.getItem().getItemStack()); 
+            qd.setVar("item-loc", e.getItem().getLocation());
+            qd.setVar("ents", e.getAffectedEntities());
+        });       
+    }
+    
+    @EventHandler
+    public void QuestItemHitGround(ItemHitGroundEvent e)
+    {
+        handleEvent(e.getPlayer(), "item-hit-ground", (qd) -> 
+        {
+            QuestVars.setItemVars(qd, e.getItem().getItemStack());  
+            qd.setVar("item-loc", e.getItem().getLocation());
+        });
+    }
+    
+    @EventHandler
+    public void VehicleEnterEvent(VehicleEnterEvent e)
+    {
+        if(e.getEntered() instanceof Player)
+        {
+            handleEvent((Player) e.getEntered(), "vehicle-enter", (qd) -> 
+            {
+                QuestVars.setEntityVars(qd, e.getVehicle()); 
+                qd.setVar("cancel", e.isCancelled()); 
+            }, (qd) -> 
+            {
+                e.setCancelled(qd.getBoolean("cancel")); 
+            });
+        }
+    }
+    
+    @EventHandler
+    public void VehicleEnterEvent(VehicleExitEvent e)
+    {
+        if(e.getExited() instanceof Player)
+        {
+            handleEvent((Player) e.getExited(), "vehicle-exit", (qd) -> 
+            {
+                QuestVars.setEntityVars(qd, e.getVehicle()); 
+                qd.setVar("cancel", e.isCancelled()); 
+            }, (qd) -> 
+            {
+                e.setCancelled(qd.getBoolean("cancel")); 
+            });
+        }
+    }
+    
+    @EventHandler
+    public void PlayerPortalEvent(PlayerPortalEvent e)
+    {
+        handleEvent(e.getPlayer(), "portal", (qd) -> 
+        {
+            qd.setVar("from", e.getFrom());
+            qd.setVar("to", e.getTo());
+            qd.setVar("cancel", e.isCancelled()); 
+        }, (qd) -> 
+        {
+            e.setCancelled(qd.getBoolean("cancel")); 
+        });
+    }
+    
+    @EventHandler
+    public void PlayerCommandEvent(PlayerCommandPreprocessEvent e)
+    {
+        handleEvent(e.getPlayer(), "command", (qd) -> 
+        {
+            qd.setVar("args", Arrays.asList(e.getMessage().split(" ")));
+            qd.setVar("cancel", e.isCancelled()); 
+        }, (qd) -> 
+        {
+            e.setCancelled(qd.getBoolean("cancel")); 
+        });
+    }
+    
+    @EventHandler
+    public void PlayerToggleSneakEvent(PlayerToggleSneakEvent e)
+    {
+        handleEvent(e.getPlayer(), "player-sneak", (qd) -> 
+        {
+            qd.setVar("sneak", e.isSneaking());
+            qd.setVar("cancel", e.isCancelled()); 
+        }, (qd) -> 
+        {
+            e.setCancelled(qd.getBoolean("cancel")); 
+        });
+    }
+    
+    @EventHandler
+    public void onChatEvent(AsyncPlayerChatEvent e)
+    {
+        if(e.isAsynchronous() && e.getMessage().startsWith("§r%"))
+        {
+            e.setCancelled(true);
+            Bukkit.getScheduler().scheduleSyncDelayedTask(KajetansTools.plugin, () -> 
+            {
+                handleEvent(e.getPlayer(), "chat", (qd) -> 
+                {
+                    qd.setVar("args", Arrays.stream(e.getMessage().substring(3).split(" ")).map(s -> QuestUtils.convertInput(s)).collect(Collectors.toList()));
+                });
+            });
+        }
+    }
+      
+    @EventHandler
+    public void useEffectEvent(PlayerUsesEffectEvent e)
+    {    
+        handleEvent(e.getPlayer(), "player-use-effect", (qd) -> 
+        {       
+            qd.setVar("power", e.getPower());
+            qd.setVar("mana-cost", e.getMana());
+            qd.setVar("cause", e.getCause().toString());
+            qd.setVar("effect", e.getEffect());
+            qd.setVar("cancel", e.isCancelled());
+        }, (qd) -> 
+        {
+            try
+            {
+                int power = (int) qd.getVar("power");
+                if(power < 1 || power > 20)
+                {
+                    throw new IllegalArgumentException();
+                }
+                e.setPower(power);
+            }
+            catch(Exception ex)
+            {
+                KajetansTools.quest.getQuestParser().printQuestException(qd, ex, "(power)");           
+            }
+            try
+            {
+                int mana = (int) qd.getVar("mana-cost");
+                if(mana < 0)
+                {
+                    throw new IllegalArgumentException();
+                }
+                e.setMana(mana);
+            }
+            catch(Exception ex)
+            {
+                KajetansTools.quest.getQuestParser().printQuestException(qd, ex, "(mana-cost)");           
+            }
+            e.setCancelled(qd.getBoolean("cancel")); 
+        });
+    }
+    
+    @EventHandler
+    public void QuestVillagerPickUpItem(PlayerDropItemEvent e)
+    {             
+        Player p = e.getPlayer();
+        if(KajetansTools.quest.hasQuest(p))
+        {           
+            QuestData qd = KajetansTools.quest.getQuestData(p);
+            if(qd == null || !qd.isEventLoaded("villager-give"))
+            {
+                return;
+            }
+            final Item itemEnt = e.getItemDrop();
+            final ItemStack oldStack = itemEnt.getItemStack();
+            final Material m = oldStack.getType();
+            final int amount = oldStack.getAmount();
+            final short dur = oldStack.getDurability();
+            try
+            {
+                final QuestVillager vil = QuestUtils.getVillagerNearLocation(itemEnt.getLocation(), 3.5);
+                Bukkit.getScheduler().runTaskLater(KajetansTools.plugin, () -> 
+                {
+                    ItemStack stack = new ItemStack(m, amount, dur);
+                    QuestData data = KajetansTools.quest.getQuestData(p);
+                    if(data == null || !data.isEventLoaded("villager-give"))
+                    {
+                        return;
+                    }
+                    data.setVar("event", "villager-give");
+                    QuestVars.setPlayerVars(data, p); 
+                    QuestVars.setItemVars(data, stack);
+                    data.setVar("villager-loc", vil.getLocation());                    
+                    data.setVar("villager-prof", vil.getProfession().toString());
+                    data.setVar("cancel", e.isCancelled()); 
+                    data.runCode();
+                    if(data.getBoolean("cancel"))
+                    {
+                        return;
+                    }                  
+                    itemEnt.remove();                
+                }, 40); 
+            }
+            catch(EntityNotFoundException ex)
+            {
+            }          
+        }               
+    }
+    
+    @EventHandler
+    public void QuestVillagerPickUpItemStore(PlayerDropItemEvent e)
+    {             
+        Player p = e.getPlayer();
+        if(KajetansTools.quest.hasQuest(p))
+        {           
+            QuestData qd = KajetansTools.quest.getQuestData(p);
+            if(qd == null || !qd.isEventLoaded("villager-give"))
+            {
+                return;
+            }
+            final Item itemEnt = e.getItemDrop();
+            final ItemStack oldStack = itemEnt.getItemStack();
+            final Material m = oldStack.getType();
+            final int amount = oldStack.getAmount();
+            final short dur = oldStack.getDurability();
+            try
+            {
+                final QuestVillager vil = QuestUtils.getVillagerNearLocation(itemEnt.getLocation(), 3.5);
+                Bukkit.getScheduler().runTaskLater(KajetansTools.plugin, () -> 
+                {
+                    ItemStack stack = new ItemStack(m, amount, dur);
+                    QuestData data = KajetansTools.quest.getQuestData(p);
+                    if(data == null || !data.isEventLoaded("villager-give-store"))
+                    {
+                        return;
+                    }
+                    data.setVar("event", "villager-give-store");
+                     QuestVars.setPlayerVars(data, p); 
+                    QuestVars.setItemVars(data, stack);
+                    data.setVar("villager-loc", vil.getLocation());                    
+                    data.setVar("villager-prof", vil.getProfession().toString());
+                    data.setVar("cancel", e.isCancelled()); 
+                    data.runCode();
+                    if(data.getBoolean("cancel"))
+                    {
+                        return;
+                    }                  
+                    try 
+                    {
+                        vil.addItemAndChest(stack);
+                    } 
+                    catch (IllegalStringLocationException ex) 
+                    {
+                        this.getModule().send(p, "Das Location-Papier des Villagers ist ungültig.");
+                    }
+                    itemEnt.remove();                
+                }, 40); 
+            }
+            catch(EntityNotFoundException ex)
+            {
+            }   
+        }               
+    }
+}

+ 20 - 0
src/main/java/me/km/snuviscript/SnuviInventory.java

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

+ 174 - 0
src/main/java/me/km/snuviscript/Tree.java

@@ -0,0 +1,174 @@
+package me.km.snuviscript;
+
+import me.km.exception.GoHigherAtRootTreeException;
+import me.km.exception.NoChildTreeException;
+import it.unimi.dsi.fastutil.objects.ObjectArrayList;
+import java.util.Stack;
+
+public class Tree<T> 
+{
+    private final Node<T> root;
+    private Node<T> currentNode;
+    private final Stack<Integer> position;
+
+    public Tree() 
+    {
+        root = new Node<>();
+        root.parent = null;
+        root.data = null;
+        root.children = new ObjectArrayList<>();
+        root.selectedChild = -1;
+        
+        currentNode = root;
+        
+        position = new Stack<>();
+    }
+    
+    public void clear()
+    {
+        root.children.clear();
+        currentNode = root;
+        position.clear();
+        root.selectedChild = -1;
+    }
+
+    private static class Node<T> 
+    {
+        private int actualCodeLine;
+        private T data;
+        private Node<T> parent;
+        private ObjectArrayList<Node<T>> children;
+        private int selectedChild;
+    }
+    
+    public void addChild(T child, int actualCodeLine)
+    {
+        Node<T> node = new Node<>();
+        node.data = child;
+        node.parent = currentNode;
+        node.children = new ObjectArrayList<>();    
+        node.selectedChild = -1;
+        node.actualCodeLine = actualCodeLine;
+        currentNode.children.add(node);
+    }
+    
+    public int getActualCodeLine()
+    {
+        return currentNode.children.get(currentNode.selectedChild).actualCodeLine;
+    }
+    
+    public void goHigher() throws GoHigherAtRootTreeException
+    {
+        currentNode.selectedChild = 0;
+        currentNode = currentNode.parent;
+        if(currentNode == null)
+        {
+            currentNode = root;
+            throw new GoHigherAtRootTreeException();
+        }
+        position.pop();
+    }
+    
+    public void goDeeper() throws NoChildTreeException
+    {
+        try
+        {   
+            int i = currentNode.selectedChild;
+            currentNode = currentNode.children.get(i);
+            position.add(i);
+            currentNode.selectedChild = -1;
+        }
+        catch(IndexOutOfBoundsException ex)
+        {
+            throw new NoChildTreeException();
+        }
+    }
+    
+    public void selectChild(int index) throws NoChildTreeException
+    {
+        if(index >= currentNode.children.size())
+        {
+            throw new NoChildTreeException();
+        }
+        currentNode.selectedChild = index;
+    }
+    
+    public void selectNextChild() throws NoChildTreeException
+    {
+        currentNode.selectedChild++;
+        if(currentNode.selectedChild >= currentNode.children.size())
+        {
+            currentNode.selectedChild--;
+            throw new NoChildTreeException();
+        }
+    }
+    
+    public void selectPreviousChild()
+    {
+        currentNode.selectedChild--;
+    }
+    
+    public void selectLastChild() throws NoChildTreeException
+    {
+        currentNode.selectedChild = currentNode.children.size() - 1;
+        if(currentNode.selectedChild == -1)
+        {
+            throw new NoChildTreeException();
+        }
+    }
+    
+    public T getCurrentData()
+    {
+        return currentNode.data;
+    }
+    
+    public T getCurrentChildData() throws NoChildTreeException
+    {
+        try
+        {   
+            return currentNode.children.get(currentNode.selectedChild).data;
+        }
+        catch(IndexOutOfBoundsException ex)
+        {
+            throw new NoChildTreeException();
+        }
+    }
+    
+    public void goToRoot()
+    {
+        try
+        {
+            while(true)
+            {
+                goHigher();
+            }
+        }
+        catch(GoHigherAtRootTreeException ex)
+        {
+            currentNode.selectedChild = -1;
+        }     
+    }
+    
+    public void goToPosition(Integer... i) throws NoChildTreeException
+    {
+        goToRoot();
+        for(int a = 0; a < i.length - 1; a++)
+        {
+            selectChild(i[a]);
+            goDeeper();
+        }
+        selectChild(i[i.length - 1]);
+    }
+    
+    public boolean isRootNodeSelected()
+    {
+        return currentNode.data == null;
+    }
+    
+    public Integer[] getCurrentPosition()
+    {
+        Integer[] i = position.toArray(new Integer[position.size() + 1]);
+        i[position.size()] = currentNode.selectedChild;
+        return i;
+    }
+}

+ 27 - 0
src/main/java/me/km/snuviscript/Variable.java

@@ -0,0 +1,27 @@
+package me.km.snuviscript;
+
+public class Variable 
+{
+    private final String name;
+  
+    public Variable(String name)
+    {
+        if(name.startsWith("$"))
+        {
+            this.name = name.substring(1);
+            return;
+        }
+        this.name = name;
+    }
+    
+    public String getName()
+    {
+        return name;
+    }
+
+    @Override
+    public String toString() 
+    {
+        return name;
+    }  
+}

+ 10 - 0
src/main/java/me/km/table/Table.java

@@ -28,6 +28,16 @@ public class Table
         });
     }
     
+    public ArrayList<Object[]> getRows()
+    {
+        return list;
+    }
+
+    public int getColumns() 
+    {
+        return columns;
+    }
+
     public void addRow(Object[] o)
     {
         list.add(o);

+ 27 - 0
src/main/java/me/km/table/TableAPI.java

@@ -1,6 +1,9 @@
 package me.km.table;
 
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.List;
+import java.util.stream.Collectors;
 
 public class TableAPI 
 {
@@ -174,4 +177,28 @@ public class TableAPI
         }
         return r;
     }
+    
+    public static ArrayList<String> getTable(Table table, int width, String color, boolean middle)
+    {
+        ArrayList<String> list = new ArrayList<>();
+        int columns = table.getColumns();
+        list.add(getTableStart(width, columns, color));
+        if(middle)
+        {
+            table.getRows().forEach(o -> 
+            {
+                list.add(getTable(width, color, Arrays.stream(o).map(ob -> String.valueOf(ob)).collect(Collectors.toList())));
+                list.add(getTableMiddle(width, columns, color));
+            });
+        }
+        else
+        {
+            table.getRows().forEach(o -> 
+            {
+                list.add(getTable(width, color, Arrays.stream(o).map(ob -> String.valueOf(ob)).collect(Collectors.toList())));
+            });
+        }
+        list.add(getTableEnd(width, columns, color));
+        return list;
+    }
 }

+ 4 - 8
src/main/java/me/km/utils/ItemStackBuilder.java

@@ -1,5 +1,7 @@
 package me.km.utils;
 
+import me.km.api.Utils;
+import me.km.utils.ItemStackUtils.ItemFlag;
 import net.minecraft.block.Block;
 import net.minecraft.item.Item;
 import net.minecraft.item.ItemStack;
@@ -60,7 +62,7 @@ public class ItemStackBuilder
     
     public ItemStackBuilder addLore(String line)
     {
-        //TODO
+        Utils.setLore(stack, line, -1);
         return this;
     }
     
@@ -80,13 +82,7 @@ public class ItemStackBuilder
     
     public ItemStackBuilder hideTags()
     {
-        //TODO
-        /*stack.setTagCompound(nbt);
-        ItemMeta meta = stack.getItemMeta();
-        meta.addItemFlags(ItemFlag.HIDE_ATTRIBUTES);
-        meta.addItemFlags(ItemFlag.HIDE_POTION_EFFECTS);
-        meta.addItemFlags(ItemFlag.HIDE_ENCHANTS);
-        stack.setItemMeta(meta);*/
+        ItemStackUtils.addItemFlag(stack, ItemFlag.HIDE_ATTRIBUTES, ItemFlag.HIDE_POTION_EFFECTS, ItemFlag.HIDE_ENCHANTS);
         return this;
     }
     

+ 13 - 0
src/main/java/me/km/utils/ItemStackUtils.java

@@ -2,12 +2,15 @@ package me.km.utils;
 
 import java.util.ArrayList;
 import me.km.exception.IllegalItemStackStringException;
+import net.minecraft.block.Block;
 import net.minecraft.entity.ai.attributes.AttributeModifier;
 import net.minecraft.inventory.EntityEquipmentSlot;
 import net.minecraft.item.ItemStack;
 import net.minecraft.nbt.JsonToNBT;
 import net.minecraft.nbt.NBTException;
 import net.minecraft.nbt.NBTTagCompound;
+import net.minecraft.util.math.BlockPos;
+import net.minecraft.world.World;
 
 public class ItemStackUtils 
 {
@@ -192,4 +195,14 @@ public class ItemStackUtils
     {
         // TODO
     }
+    
+    public static void addLore(ItemStack stack, String s) 
+    {
+        // TODO
+    }
+    
+    public static void drop(World w, BlockPos pos, ItemStack stack)
+    {
+        Block.spawnAsEntity(w, pos, stack);
+    }
 }

+ 144 - 5
src/main/java/me/km/utils/ReflectionUtils.java

@@ -1,30 +1,169 @@
 package me.km.utils;
 
 import java.lang.reflect.Field;
+import net.minecraft.enchantment.Enchantment;
 import net.minecraft.entity.passive.EntityVillager;
+import net.minecraft.init.Enchantments;
+import net.minecraft.init.SoundEvents;
+import net.minecraft.util.FoodStats;
+import net.minecraft.util.SoundEvent;
 
 public class ReflectionUtils 
 {    
-    public static void setCareerLevel(EntityVillager v, int level)
+    // -----------------------------------------------------------------------------------
+    // helper stuff
+    // -----------------------------------------------------------------------------------
+    
+    private static <T> void setInt(Class<T> c, T t, String field, int i)
+    {
+        try
+        {
+            Field f = c.getDeclaredField(field);
+            {
+                try
+                {
+                    f.setAccessible(true);
+                    f.setInt(t, i);
+                }
+                catch(SecurityException | IllegalArgumentException | IllegalAccessException ex)
+                {
+                    System.out.println(field + " - " + ex);
+                }
+            }
+        }
+        catch(NoSuchFieldException | SecurityException ex)
+        {
+            System.out.println(field + " - " + ex);
+        }
+    }
+    
+    private static <T> int getInt(Class<T> c, T t, String field, int error)
+    {
+        try
+        {
+            Field f = c.getDeclaredField(field);
+            {
+                try
+                {
+                    f.setAccessible(true);
+                    return f.getInt(t);
+                }
+                catch(SecurityException | IllegalArgumentException | IllegalAccessException ex)
+                {
+                    System.out.println(field + " - " + ex);
+                }
+            }
+        }
+        catch(NoSuchFieldException | SecurityException ex)
+        {
+            System.out.println(field + " - " + ex);
+        }
+        return error;
+    }
+    
+    private static <T> void setFloat(Class<T> c, T t, String field, float fl)
+    {
+        try
+        {
+            Field f = c.getDeclaredField(field);
+            {
+                try
+                {
+                    f.setAccessible(true);
+                    f.setFloat(t, fl);
+                }
+                catch(SecurityException | IllegalArgumentException | IllegalAccessException ex)
+                {
+                    System.out.println(field + " - " + ex);
+                }
+            }
+        }
+        catch(NoSuchFieldException | SecurityException ex)
+        {
+            System.out.println(field + " - " + ex);
+        }
+    }
+    
+    private static <T> float getFloat(Class<T> c, T t, String field, float error)
     {
         try
         {
-            Field f = EntityVillager.class.getDeclaredField("careerLevel");
+            Field f = c.getDeclaredField(field);
             {
                 try
                 {
                     f.setAccessible(true);
-                    f.setInt(v, level);
+                    return f.getFloat(t);
                 }
                 catch(SecurityException | IllegalArgumentException | IllegalAccessException ex)
                 {
-                    System.out.println("setCareerLevel - " + ex);
+                    System.out.println(field + " - " + ex);
                 }
             }
         }
         catch(NoSuchFieldException | SecurityException ex)
         {
-            System.out.println("setCareerLevel - " + ex);
+            System.out.println(field + " - " + ex);
         }
+        return error;
+    }
+    
+    private static <T> T getField(Class<T> cast, Class c, String field)
+    {
+        try
+        {
+            return (T) c.getField(field).get(null);
+        }
+        catch(NoSuchFieldException | SecurityException | IllegalAccessException | IllegalArgumentException ex)
+        {
+            return null;
+        }
+    }
+    
+    // -----------------------------------------------------------------------------------
+    // Villager stuff
+    // -----------------------------------------------------------------------------------
+    
+    public static void setCareerLevel(EntityVillager v, int level)
+    {
+        setInt(EntityVillager.class, v, "careerLevel", level);
+    }
+    
+    // -----------------------------------------------------------------------------------
+    // FoodStats
+    // -----------------------------------------------------------------------------------
+    
+    public static void setExhaustion(FoodStats stats, float f)
+    {
+        setFloat(FoodStats.class, stats, "foodExhaustionLevel", f);
+    }
+    
+    public static float getExhaustion(FoodStats stats)
+    {
+        return getFloat(FoodStats.class, stats, "foodExhaustionLevel", 0);
+    }
+    
+    public static void setSaturation(FoodStats stats, float f)
+    {
+        setFloat(FoodStats.class, stats, "foodSaturationLevel", f);
+    }
+    
+    public static float getSaturation(FoodStats stats)
+    {
+        return getFloat(FoodStats.class, stats, "foodSaturationLevel", 0);
+    }
+    
+    // -----------------------------------------------------------------------------------
+    // field list gets
+    // -----------------------------------------------------------------------------------
+    
+    public static Enchantment getEnchantment(String name)
+    {
+        return getField(Enchantment.class, Enchantments.class, name);
+    }
+    
+    public static SoundEvent getSoundEvent(String name)
+    {
+        return getField(SoundEvent.class, SoundEvents.class, name);
     }
 }

+ 2 - 0
src/main/resources/assets/km/lang/en_US.lang

@@ -12,5 +12,7 @@ item.chestplateCopper.name=Copper Chestplate
 item.leggingsCopper.name=Copper Leggings
 item.bootsCopper.name=Copper Boots
 
+item.scroll.name=Magic Scroll
+
 tile.oreCopper.name=Copper Ore
 tile.blockCopper.name=Copper Block

+ 6 - 0
src/main/resources/assets/km/models/item/scroll.json

@@ -0,0 +1,6 @@
+{
+    "parent": "item/generated",
+    "textures": {
+        "layer0": "items/paper"
+    }
+}