diff --git a/.gitignore b/.gitignore
index ae3c172..a1050df 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,3 @@
/bin/
+/target
+Core.iml
\ No newline at end of file
diff --git a/Core.iml b/Core.iml
index bf8bd26..b23cecb 100644
--- a/Core.iml
+++ b/Core.iml
@@ -6,6 +6,7 @@
+
@@ -23,18 +24,24 @@
+
+
-
+
+
+
-
+
+
+
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index c5d432d..4deeaea 100644
--- a/pom.xml
+++ b/pom.xml
@@ -10,102 +10,40 @@
jar
Univento.eu Core System
- https://development.univento.eu/Plugins/Core
-
- Plugin used on all Spigot servers to add basic functions and APIs.
-
-
-
-
- joethei
- Johannes Theiner
- info@joethei.de
- Europe/Berlin
-
- architect
- project leader
- senior developer
-
-
- https://de.gravatar.com/userimage/65052389/84435a829d76e6b6c48d67cdd463c6ab.png?size=50
-
-
-
-
-
-
- Daniel Planötscher
- planiel@univento.eu
- Europe/Berlin
-
- designer
-
-
- https://pbs.twimg.com/profile_images/706847659416494080/in1AJ9rc.jpg
-
-
-
-
- 2015
-
- univento
- http://univento.eu
-
-
- org.apache.maven.plugins
- maven-compiler-plugin
-
-
- 1.8
-
-
-
- org.apache.maven.plugins
- maven-assembly-plugin
- 2.2.1
-
-
-
- eu.univento.core.Core
-
-
-
- jar-with-dependencies
-
-
-
-
- make-assembly
- package
-
- single
-
-
-
-
-
- org.apache.maven.plugins
- maven-site-plugin
- 3.4
-
- en
-
-
-
- org.apache.maven.plugins
- maven-javadoc-plugin
- 2.10.4
-
-
- attach-javadocs
-
- javadoc
-
-
-
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ 1.8
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+ 2.2.1
+
+
+
+ eu.univento.core.Core
+
+
+
+ jar-with-dependencies
+
+
+
+
+ make-assembly
+ package
+
+ single
+
+
+
@@ -138,7 +76,7 @@
eu.univento
- Commons
+ CloudCommons
1.0-SNAPSHOT
diff --git a/src/main/java/eu/univento/core/Core.java b/src/main/java/eu/univento/core/Core.java
index 3a7d5c0..76439cc 100644
--- a/src/main/java/eu/univento/core/Core.java
+++ b/src/main/java/eu/univento/core/Core.java
@@ -6,6 +6,7 @@ import eu.univento.core.antihack.AntiHack;
import eu.univento.core.api.Config;
import eu.univento.core.api.effects.Blackscreen;
import eu.univento.core.api.events.MoveEventFilter;
+import eu.univento.core.api.items.InventoryManager;
import eu.univento.core.api.player.CustomPlayer;
import eu.univento.core.api.server.NetworkData;
import eu.univento.core.api.server.ServerSettings;
@@ -150,10 +151,7 @@ public class Core extends JavaPlugin implements NettyInjection.PacketHandler {
pm.registerEvents(new WeaponEvents(), this);
pm.registerEvents(new SpectatorEvents(), this);
pm.registerEvents(new MoveEventFilter(getServer()), this);
-
- if (ServerSettings.isBuild()) {
- new Build(this, "build", "enables/disables the build mode", "b");
- }
+ pm.registerEvents(new InventoryManager(), this);
if (ServerSettings.isGame()) {
new Fix(this, "fix", "fix your self or other players");
diff --git a/src/main/java/eu/univento/core/antihack/AntiHack.java b/src/main/java/eu/univento/core/antihack/AntiHack.java
index 12e00a2..646cf80 100644
--- a/src/main/java/eu/univento/core/antihack/AntiHack.java
+++ b/src/main/java/eu/univento/core/antihack/AntiHack.java
@@ -20,8 +20,7 @@ public class AntiHack implements Listener{
public static void registerListeners() {
PluginManager pm = Bukkit.getPluginManager();
pm.registerEvents(new AntiHack(), Core.getInstance());
- pm.registerEvents(new Glide(), Core.getInstance());
- pm.registerEvents(new Fly(), Core.getInstance());
+ pm.registerEvents(new Movement(), Core.getInstance());
pm.registerEvents(new AutoClicker(), Core.getInstance());
pm.registerEvents(new Criticals(), Core.getInstance());
pm.registerEvents(new Reach(), Core.getInstance());
@@ -31,13 +30,15 @@ public class AntiHack implements Listener{
pm.registerEvents(new FastPlace(), Core.getInstance());
//pm.registerEvents(new KillAura(), Core.getInstance());
- for(Map.Entry entry : AutoClicker.clicks.entrySet()) {
- if(entry.getValue() > 16) {
- //TODO: change to real warn reason
- entry.getKey().warn(WarnReason.SPAM, null, "https://players.univento.eu/" + entry.getKey().getUniqueId().toString() + "/hacks");
+ Bukkit.getScheduler().scheduleSyncRepeatingTask(Core.getInstance(), () -> {
+ for(Map.Entry entry : AutoClicker.clicks.entrySet()) {
+ if(entry.getValue() > 16) {
+ //TODO: change to real warn reason
+ entry.getKey().warn(WarnReason.SPAM, null, "https://players.univento.eu/" + entry.getKey().getUniqueId().toString() + "/hacks");
+ }
+ AutoClicker.clicks.remove(entry.getKey());
}
- AutoClicker.clicks.remove(entry.getKey());
- }
+ }, 20L, 20L);
}
@EventHandler
diff --git a/src/main/java/eu/univento/core/antihack/modules/Fly.java b/src/main/java/eu/univento/core/antihack/modules/Fly.java
deleted file mode 100644
index caf47f9..0000000
--- a/src/main/java/eu/univento/core/antihack/modules/Fly.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package eu.univento.core.antihack.modules;
-
-import eu.univento.core.api.player.CustomPlayer;
-import org.bukkit.GameMode;
-import org.bukkit.event.EventHandler;
-import org.bukkit.event.Listener;
-import org.bukkit.event.player.PlayerMoveEvent;
-
-public class Fly implements Listener{
-
- @EventHandler
- public void onFly(PlayerMoveEvent e) {
- CustomPlayer p = CustomPlayer.getPlayer(e.getPlayer());
- if(p.getGameMode() != GameMode.CREATIVE && e.getTo().getY() > e.getFrom().getY() + 1.5) {
- //p.warn(Hack.FLY);
- }
- }
-}
\ No newline at end of file
diff --git a/src/main/java/eu/univento/core/antihack/modules/Glide.java b/src/main/java/eu/univento/core/antihack/modules/Glide.java
deleted file mode 100644
index 4d53fd7..0000000
--- a/src/main/java/eu/univento/core/antihack/modules/Glide.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package eu.univento.core.antihack.modules;
-
-import eu.univento.core.api.player.CustomPlayer;
-import org.bukkit.Material;
-import org.bukkit.event.EventHandler;
-import org.bukkit.event.Listener;
-import org.bukkit.event.player.PlayerMoveEvent;
-
-public class Glide implements Listener{
-
- @EventHandler
- public void onGlide(PlayerMoveEvent e) {
- CustomPlayer p = CustomPlayer.getPlayer(e.getPlayer());
- if(e.getTo().getY() - e.getFrom().getY() == -0.125 && e.getTo().clone().subtract(0.0, 1.0, 0.0).getBlock().getType().equals(Material.AIR)) {
- //p.warn(Hack.GLIDE);
- }
- }
-}
\ No newline at end of file
diff --git a/src/main/java/eu/univento/core/antihack/modules/Movement.java b/src/main/java/eu/univento/core/antihack/modules/Movement.java
new file mode 100644
index 0000000..eaf51a7
--- /dev/null
+++ b/src/main/java/eu/univento/core/antihack/modules/Movement.java
@@ -0,0 +1,46 @@
+package eu.univento.core.antihack.modules;
+
+import eu.univento.core.api.player.CustomPlayer;
+import org.bukkit.GameMode;
+import org.bukkit.Material;
+import org.bukkit.block.BlockFace;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerMoveEvent;
+
+public class Movement implements Listener{
+
+ @EventHandler
+ public void onMove(PlayerMoveEvent e) {
+ CustomPlayer p = CustomPlayer.getPlayer(e.getPlayer());
+ if(p.getGameMode() == GameMode.CREATIVE) return;
+ if(p.getVehicle() != null) return;
+ if(p.getAllowFlight()) return;
+ double distance = e.getTo().distance(e.getFrom());
+ if(p.getFallDistance() == 0.0F && p.getLocation().getBlock().getRelative(BlockFace.UP).getType() == Material.AIR) {
+ if(distance > 0.6D && !p.isOnGround()) {
+ e.setCancelled(true);
+ p.teleport(e.getFrom());
+ p.sendMessage("Du hast Fly an");
+ }
+ }
+ if(distance > 0.2D && distance < 0.29D) {
+ if(p.getLocation().getBlock().getRelative(BlockFace.DOWN).getType() == Material.WATER) return;
+ if(p.getLocation().getBlock().getRelative(BlockFace.DOWN).isLiquid()) {
+ e.setCancelled(true);
+ p.teleport(e.getFrom());
+ p.sendMessage("Du hast Jesus an");
+ }
+ }
+ if(p.getFallDistance() == 0.0F && distance > 0.79D && p.isOnGround()) {
+ e.setCancelled(true);
+ p.setHealth(0.0D);
+ p.sendMessage("Du hast NoFall an");
+ }
+ if(e.getTo().getY() - e.getFrom().getY() == -0.125 && e.getTo().clone().subtract(0.0, 1.0, 0.0).getBlock().getType().equals(Material.AIR)) {
+ e.setCancelled(true);
+ p.teleport(e.getFrom());
+ p.sendMessage("Du hast Glide an");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/eu/univento/core/antihack/modules/NoSlowDown.java b/src/main/java/eu/univento/core/antihack/modules/NoSlowDown.java
index bf08d31..dc0c291 100644
--- a/src/main/java/eu/univento/core/antihack/modules/NoSlowDown.java
+++ b/src/main/java/eu/univento/core/antihack/modules/NoSlowDown.java
@@ -14,7 +14,10 @@ public class NoSlowDown implements Listener{
if(e.getEntity() instanceof Player) {
CustomPlayer p = CustomPlayer.getPlayer((Player) e.getEntity());
if(p.isSprinting()) {
- //p.warn(Hack.NOSLOWDOWN);
+ e.setCancelled(true);
+ p.damage(2.0D);
+ p.setArrowsInBody(p.getArrowsInBody() + 50);
+ p.sendMessage("Du hast NoSlowDown an");
}
}
}
@@ -24,7 +27,9 @@ public class NoSlowDown implements Listener{
if(e.getEntity() instanceof Player) {
CustomPlayer p = CustomPlayer.getPlayer((Player) e.getEntity());
if(e.getFoodLevel() > p.getFoodLevel() && p.isSprinting()) {
- //p.warn(Hack.NOSLOWDOWN);
+ e.setCancelled(true);
+ e.setFoodLevel(0);
+ p.sendMessage("Du hast NoSlowDown an");
}
}
}
diff --git a/src/main/java/eu/univento/core/api/entity/ArmorStandAnimator.java b/src/main/java/eu/univento/core/api/entity/ArmorStandAnimator.java
new file mode 100644
index 0000000..90feac1
--- /dev/null
+++ b/src/main/java/eu/univento/core/api/entity/ArmorStandAnimator.java
@@ -0,0 +1,418 @@
+package eu.univento.core.api.entity;
+
+import org.bukkit.Location;
+import org.bukkit.entity.ArmorStand;
+import org.bukkit.util.EulerAngle;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileReader;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * The original thread that this code belongs to can be found here:
+ * https://www.spigotmc.org/threads/armor-stand-animator-class.152863/
+ * MIT License
+
+ Copyright (c) 2016 Bram Stout
+
+ Permission is hereby granted, free of charge, to any person obtaining a copy
+ of this software and associated documentation files (the "Software"), to deal
+ in the Software without restriction, including without limitation the rights
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ copies of the Software, and to permit persons to whom the Software is
+ furnished to do so, subject to the following conditions:
+
+ The above copyright notice and this permission notice shall be included in all
+ copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ SOFTWARE.
+ * @author Bram
+ *
+ */
+public class ArmorStandAnimator {
+
+ /**
+ * This is a map containing the already loaded frames. This way we don't have to parse the same animation over and over.
+ */
+ private static Map animCache = new HashMap<>();
+ /**
+ * This is a list with all the animator instances. This makes it easy to update all the instances at one.
+ */
+ private static Set animators = new HashSet<>();
+
+ /** This void updates all the animator instances at once */
+ public static void updateAll() {
+ for (ArmorStandAnimator ani : animators) {
+ ani.update();
+ }
+ }
+
+ /** Returns all the animator instances */
+ public static Set getAnimators() {
+ return animators;
+ }
+
+ /** Clears the animation cache in case you want to update an animation */
+ public static void clearCache() {
+ animCache.clear();
+ }
+
+ /** The armor stand to animate */
+ private ArmorStand armorStand;
+ /** The amount of frames this animation has */
+ private int length;
+ /** All the frames of the animation */
+ private Frame[] frames;
+ /** Says when the animation is paused */
+ private boolean paused = false;
+ /** The current frame we're on */
+ private int currentFrame;
+ /** The start location of the animation */
+ private Location startLocation;
+ /** If this is true. The animator is going to guess the frames that aren't specified */
+ private boolean interpolate = false;
+
+ /**
+ * Constructor of the animator. Takes in the path to the file with the animation and the armor stand to animate.
+ *
+ * @param aniFile
+ * @param armorStand
+ */
+ public ArmorStandAnimator(File aniFile, ArmorStand armorStand) {
+ // set all the stuff
+ this.armorStand = armorStand;
+ startLocation = armorStand.getLocation();
+ // checks if the file has been loaded before. If so return the cached version
+ if (animCache.containsKey(aniFile.getAbsolutePath())) {
+ frames = animCache.get(aniFile.getAbsolutePath());
+ } else {
+ // File has not been loaded before so load it.
+ BufferedReader br = null;
+ try {
+ br = new BufferedReader(new FileReader(aniFile));
+ String line = "";
+ // create the current frame variable
+ Frame currentFrame = null;
+ while ((line = br.readLine()) != null) {
+ // set the length
+ if (line.startsWith("length")) {
+ length = (int) Float.parseFloat(line.split(" ")[1]);
+ frames = new Frame[length];
+ }
+ // sets the current frame
+ else if (line.startsWith("frame")) {
+ if (currentFrame != null) {
+ frames[currentFrame.frameID] = currentFrame;
+ }
+ int frameID = Integer.parseInt(line.split(" ")[1]);
+ currentFrame = new Frame();
+ currentFrame.frameID = frameID;
+ }
+ // check if we need to interpolate
+ else if (line.contains("interpolate")) {
+ interpolate = true;
+ }
+ // sets the position and rotation or the main armor stand
+ else if (line.contains("Armorstand_Position")) {
+ currentFrame.x = Float.parseFloat(line.split(" ")[1]);
+ currentFrame.y = Float.parseFloat(line.split(" ")[2]);
+ currentFrame.z = Float.parseFloat(line.split(" ")[3]);
+ currentFrame.r = Float.parseFloat(line.split(" ")[4]);
+ }
+ // sets the rotation for the middle
+ else if (line.contains("Armorstand_Middle")) {
+ float x = (float) Math.toRadians(Float.parseFloat(line.split(" ")[1]));
+ float y = (float) Math.toRadians(Float.parseFloat(line.split(" ")[2]));
+ float z = (float) Math.toRadians(Float.parseFloat(line.split(" ")[3]));
+ currentFrame.middle = new EulerAngle(x, y, z);
+ }
+ // sets the rotation for the right leg
+ else if (line.contains("Armorstand_Right_Leg")) {
+ float x = (float) Math.toRadians(Float.parseFloat(line.split(" ")[1]));
+ float y = (float) Math.toRadians(Float.parseFloat(line.split(" ")[2]));
+ float z = (float) Math.toRadians(Float.parseFloat(line.split(" ")[3]));
+ currentFrame.rightLeg = new EulerAngle(x, y, z);
+ }
+ // sets the rotation for the left leg
+ else if (line.contains("Armorstand_Left_Leg")) {
+ float x = (float) Math.toRadians(Float.parseFloat(line.split(" ")[1]));
+ float y = (float) Math.toRadians(Float.parseFloat(line.split(" ")[2]));
+ float z = (float) Math.toRadians(Float.parseFloat(line.split(" ")[3]));
+ currentFrame.leftLeg = new EulerAngle(x, y, z);
+ }
+ // sets the rotation for the left arm
+ else if (line.contains("Armorstand_Left_Arm")) {
+ float x = (float) Math.toRadians(Float.parseFloat(line.split(" ")[1]));
+ float y = (float) Math.toRadians(Float.parseFloat(line.split(" ")[2]));
+ float z = (float) Math.toRadians(Float.parseFloat(line.split(" ")[3]));
+ currentFrame.leftArm = new EulerAngle(x, y, z);
+ }
+ // sets the rotation for the right arm
+ else if (line.contains("Armorstand_Right_Arm")) {
+ float x = (float) Math.toRadians(Float.parseFloat(line.split(" ")[1]));
+ float y = (float) Math.toRadians(Float.parseFloat(line.split(" ")[2]));
+ float z = (float) Math.toRadians(Float.parseFloat(line.split(" ")[3]));
+ currentFrame.rightArm = new EulerAngle(x, y, z);
+ }
+ // sets the rotation for the head
+ else if (line.contains("Armorstand_Head")) {
+ float x = (float) Math.toRadians(Float.parseFloat(line.split(" ")[1]));
+ float y = (float) Math.toRadians(Float.parseFloat(line.split(" ")[2]));
+ float z = (float) Math.toRadians(Float.parseFloat(line.split(" ")[3]));
+ currentFrame.head = new EulerAngle(x, y, z);
+ }
+ }
+ if (currentFrame != null) {
+ frames[currentFrame.frameID] = currentFrame;
+ }
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ } finally {
+ // make sure to close the stream!
+ if (br != null) {
+ try {
+ br.close();
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+ }
+ // add the animation to the cache, else adding the whole cache thing has no point.
+ animCache.put(aniFile.getAbsolutePath(), frames);
+ }
+ // register this instance of the animator
+ animators.add(this);
+ }
+
+ /**
+ * This method removes this instance from the animator instances list. When you don't want to use this instance any more, you can call this method.
+ */
+ public void remove() {
+ animators.remove(this);
+ }
+
+ /** Pauses the animation */
+ public void pause() {
+ paused = true;
+ }
+
+ /**
+ * Pauses the animation and sets the current frame to 0. It also updates the animation one more time to set the armor stand to the first frame.
+ */
+ public void stop() {
+ // set the current frame to 0 and update the frame and set it to 0 again
+ currentFrame = 0;
+ update();
+ currentFrame = 0;
+ paused = true;
+ }
+
+ /** Plays the animation */
+ public void play() {
+ paused = false;
+ }
+
+ /** Updates the animation and goes to the next frame */
+ public void update() {
+ // make sure that the animation isn't paused
+ if (!paused) {
+ // makes sure that the frame is in bounds
+ if (currentFrame >= (length - 1) || currentFrame < 0) {
+ currentFrame = 0;
+ }
+ // get the frame
+ Frame f = frames[currentFrame];
+ //checks if we need to interpolate. If so interpolate.
+ if(interpolate) {
+ if(f == null) {
+ f = interpolate(currentFrame);
+ }
+ }
+ // make sure it's not null
+ if (f != null) {
+ // get the new location
+ Location newLoc = startLocation.clone().add(f.x, f.y, f.z);
+ newLoc.setYaw(f.r + newLoc.getYaw());
+ // set all the values
+ armorStand.teleport(newLoc);
+ armorStand.setBodyPose(f.middle);
+ armorStand.setLeftLegPose(f.leftLeg);
+ armorStand.setRightLegPose(f.rightLeg);
+ armorStand.setLeftArmPose(f.leftArm);
+ armorStand.setRightArmPose(f.rightArm);
+ armorStand.setHeadPose(f.head);
+ }
+ // go one frame higher
+ currentFrame++;
+ }
+ }
+
+ /** Returns the current frame */
+ public int getCurrentFrame() {
+ return currentFrame;
+ }
+
+ /** Sets the current frame */
+ public void setCurrentFrame(int currentFrame) {
+ this.currentFrame = currentFrame;
+ }
+
+ /** Returns the armor stand this instance animates */
+ public ArmorStand getArmorStand() {
+ return armorStand;
+ }
+
+ /** Returns the amount of frame this animation has */
+ public int getLength() {
+ return length;
+ }
+
+ /** Returns the list of frames */
+ public Frame[] getFrames() {
+ return frames;
+ }
+
+ /** Returns if the animation is paused */
+ public boolean isPaused() {
+ return paused;
+ }
+
+ /** Gets the start location */
+ public Location getStartLocation() {
+ return startLocation;
+ }
+
+ /**
+ * Sets the start location. If you want to teleport the armor stand this is the recommended function
+ *
+ * @param location
+ */
+ public void setStartLocation(Location location) {
+ startLocation = location;
+ }
+
+ /** Returns interpolate */
+ public boolean isInterpolated() {
+ return interpolate;
+ }
+
+ /** Sets interpolate */
+ public void setInterpolated(boolean interpolate) {
+ this.interpolate = interpolate;
+ }
+
+ /**Returns an interpolated frame*/
+ private Frame interpolate(int frameID) {
+ //get the minimum and maximum frames that are the closest
+ Frame minFrame = null;
+ for (int i = frameID; i >= 0; i--) {
+ if (frames[i] != null) {
+ minFrame = frames[i];
+ break;
+ }
+ }
+ Frame maxFrame = null;
+ for (int i = frameID; i < frames.length; i++) {
+ if (frames[i] != null) {
+ maxFrame = frames[i];
+ break;
+ }
+ }
+ //make sure that those frame weren't the last one
+ Frame res = null;
+
+ if(maxFrame == null || minFrame == null) {
+ if(maxFrame == null && minFrame != null) {
+ return minFrame;
+ }
+ if(minFrame == null && maxFrame != null) {
+ return maxFrame;
+ }
+ res = new Frame();
+ res.frameID = frameID;
+ return res;
+ }
+ //create the frame and interpolate
+ res = new Frame();
+ res.frameID = frameID;
+
+ //this part calculates the distance the current frame is from the minimum and maximum frame and this allows for an easy linear interpolation
+ float Dmin = frameID - minFrame.frameID;
+ float D = maxFrame.frameID - minFrame.frameID;
+ float D0 = Dmin / D;
+
+ res = minFrame.mult(1 - D0, frameID).add(maxFrame.mult(D0, frameID), frameID);
+
+ return res;
+ }
+
+ /**
+ * The frame class. This class holds all the information of one frame.
+ */
+ public static class Frame {
+ /**The Frame ID*/
+ int frameID;
+ /**the location and rotation*/
+ float x, y, z, r;
+ /**The rotation of the body parts*/
+ EulerAngle middle;
+ EulerAngle rightLeg;
+ EulerAngle leftLeg;
+ EulerAngle rightArm;
+ EulerAngle leftArm;
+ EulerAngle head;
+ /**This multiplies every value with another value.
+ * Used for interpolation
+ * @param a
+ * @param frameID
+ * @return
+ */
+ public Frame mult(float a, int frameID) {
+ Frame f = new Frame();
+ f.frameID = frameID;
+ f.x = f.x * a;
+ f.y = f.y * a;
+ f.z = f.z * a;
+ f.r = f.r * a;
+ f.middle = new EulerAngle(middle.getX() * a, middle.getY() * a, middle.getZ() * a);
+ f.rightLeg = new EulerAngle(rightLeg.getX() * a, rightLeg.getY() * a, rightLeg.getZ() * a);
+ f.leftLeg = new EulerAngle(leftLeg.getX() * a, leftLeg.getY() * a, leftLeg.getZ() * a);
+ f.rightArm = new EulerAngle(rightArm.getX() * a, rightArm.getY() * a, rightArm.getZ() * a);
+ f.leftArm = new EulerAngle(leftArm.getX() * a, leftArm.getY() * a, leftArm.getZ() * a);
+ f.head = new EulerAngle(head.getX() * a, head.getY() * a, head.getZ() * a);
+ return f;
+ }
+ /**This adds a value to every value.
+ * Used for interpolation
+ * @param a
+ * @param frameID
+ * @return
+ */
+ public Frame add(Frame a, int frameID) {
+ Frame f = new Frame();
+ f.frameID = frameID;
+ f.x = f.x + a.x;
+ f.y = f.y + a.y;
+ f.z = f.z + a.z;
+ f.r = f.r + a.r;
+ f.middle = new EulerAngle(middle.getX() + a.middle.getX(), middle.getY() + a.middle.getY(), middle.getZ() + a.middle.getZ());
+ f.rightLeg = new EulerAngle(rightLeg.getX() + a.rightLeg.getX(), rightLeg.getY() + a.rightLeg.getY(), rightLeg.getZ() + a.rightLeg.getZ());
+ f.leftLeg = new EulerAngle(leftLeg.getX() + a.leftLeg.getX(), leftLeg.getY() + a.leftLeg.getY(), leftLeg.getZ() + a.leftLeg.getZ());
+ f.rightArm = new EulerAngle(rightArm.getX() + a.rightArm.getX(), rightArm.getY() + a.rightArm.getY(), rightArm.getZ() + a.rightArm.getZ());
+ f.leftArm = new EulerAngle(leftArm.getX() + a.leftArm.getX(), leftArm.getY() + a.leftArm.getY(), leftArm.getZ() + a.leftArm.getZ());
+ f.head = new EulerAngle(head.getX() + a.head.getX(), head.getY() + a.head.getY(), head.getZ() + a.head.getZ());
+ return f;
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/src/main/java/eu/univento/core/api/items/ClickInventory.java b/src/main/java/eu/univento/core/api/items/ClickInventory.java
new file mode 100644
index 0000000..e995d65
--- /dev/null
+++ b/src/main/java/eu/univento/core/api/items/ClickInventory.java
@@ -0,0 +1,238 @@
+package eu.univento.core.api.items;
+
+import eu.univento.core.api.items.events.NamedCloseEvent;
+import eu.univento.core.api.items.events.PageCloseEvent;
+import eu.univento.core.api.player.CustomPlayer;
+import org.bukkit.Bukkit;
+import org.bukkit.Material;
+import org.bukkit.event.inventory.InventoryClickEvent;
+import org.bukkit.event.inventory.InventoryDragEvent;
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.metadata.FixedMetadataValue;
+import org.bukkit.plugin.java.JavaPlugin;
+
+import java.lang.reflect.Array;
+import java.util.HashMap;
+
+/**
+ * @author joethei
+ * @version 0.1
+ */
+public abstract class ClickInventory{
+
+ protected static JavaPlugin plugin;
+ protected Inventory currentInventory;
+ protected boolean inventoryInUse;
+ private boolean modifiable;
+ private CustomPlayer player;
+ private boolean playerInventoryUsed;
+ private ItemStack[] previousContents;
+ private String inventoryName;
+ private HashMap