Advertisement
bram0101

MC Bukkit/Spigot Armor Stand Animator Class

Jun 4th, 2016
2,293
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 14.07 KB | None | 0 0
  1. package me.dutchanimations;
  2.  
  3. import java.io.BufferedReader;
  4. import java.io.File;
  5. import java.io.FileReader;
  6. import java.util.HashMap;
  7. import java.util.HashSet;
  8. import java.util.Map;
  9. import java.util.Set;
  10.  
  11. import org.bukkit.Location;
  12. import org.bukkit.entity.ArmorStand;
  13. import org.bukkit.util.EulerAngle;
  14.  
  15. /**
  16. * The original thread that this code belongs to can be found here:
  17. * https://www.spigotmc.org/threads/armor-stand-animator-class.152863/
  18. * MIT License
  19.  
  20. Copyright (c) 2016 Bram Stout
  21.  
  22. Permission is hereby granted, free of charge, to any person obtaining a copy
  23. of this software and associated documentation files (the "Software"), to deal
  24. in the Software without restriction, including without limitation the rights
  25. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  26. copies of the Software, and to permit persons to whom the Software is
  27. furnished to do so, subject to the following conditions:
  28.  
  29. The above copyright notice and this permission notice shall be included in all
  30. copies or substantial portions of the Software.
  31.  
  32. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  33. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  34. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  35. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  36. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  37. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  38. SOFTWARE.
  39. * @author Bram
  40. *
  41. */
  42. public class ArmorStandAnimator {
  43.  
  44. /**
  45. * This is a map containing the already loaded frames. This way we don't have to parse the same animation over and over.
  46. */
  47. private static Map<String, Frame[]> animCache = new HashMap<String, Frame[]>();
  48. /**
  49. * This is a list with all the animator instances. This makes it easy to update all the instances at one.
  50. */
  51. private static Set<ArmorStandAnimator> animators = new HashSet<ArmorStandAnimator>();
  52.  
  53. /** This void updates all the animator instances at once */
  54. public static void updateAll() {
  55. for (ArmorStandAnimator ani : animators) {
  56. ani.update();
  57. }
  58. }
  59.  
  60. /** Returns all the animator instances */
  61. public static Set<ArmorStandAnimator> getAnimators() {
  62. return animators;
  63. }
  64.  
  65. /** Clears the animation cache in case you want to update an animation */
  66. public static void clearCache() {
  67. animCache.clear();
  68. }
  69.  
  70. /** The armor stand to animate */
  71. private ArmorStand armorStand;
  72. /** The amount of frames this animation has */
  73. private int length;
  74. /** All the frames of the animation */
  75. private Frame[] frames;
  76. /** Says when the animation is paused */
  77. private boolean paused = false;
  78. /** The current frame we're on */
  79. private int currentFrame;
  80. /** The start location of the animation */
  81. private Location startLocation;
  82. /** If this is true. The animator is going to guess the frames that aren't specified */
  83. private boolean interpolate = false;
  84.  
  85. /**
  86. * Constructor of the animator. Takes in the path to the file with the animation and the armor stand to animate.
  87. *
  88. * @param aniFile
  89. * @param armorStand
  90. */
  91. public ArmorStandAnimator(File aniFile, ArmorStand armorStand) {
  92. // set all the stuff
  93. this.armorStand = armorStand;
  94. startLocation = armorStand.getLocation();
  95. // checks if the file has been loaded before. If so return the cached version
  96. if (animCache.containsKey(aniFile.getAbsolutePath())) {
  97. frames = animCache.get(aniFile.getAbsolutePath());
  98. } else {
  99. // File has not been loaded before so load it.
  100. BufferedReader br = null;
  101. try {
  102. br = new BufferedReader(new FileReader(aniFile));
  103. String line = "";
  104. // create the current frame variable
  105. Frame currentFrame = null;
  106. while ((line = br.readLine()) != null) {
  107. // set the length
  108. if (line.startsWith("length")) {
  109. length = (int) Float.parseFloat(line.split(" ")[1]);
  110. frames = new Frame[length];
  111. }
  112. // sets the current frame
  113. else if (line.startsWith("frame")) {
  114. if (currentFrame != null) {
  115. frames[currentFrame.frameID] = currentFrame;
  116. }
  117. int frameID = Integer.parseInt(line.split(" ")[1]);
  118. currentFrame = new Frame();
  119. currentFrame.frameID = frameID;
  120. }
  121. // check if we need to interpolate
  122. else if (line.contains("interpolate")) {
  123. interpolate = true;
  124. }
  125. // sets the position and rotation or the main armor stand
  126. else if (line.contains("Armorstand_Position")) {
  127. currentFrame.x = Float.parseFloat(line.split(" ")[1]);
  128. currentFrame.y = Float.parseFloat(line.split(" ")[2]);
  129. currentFrame.z = Float.parseFloat(line.split(" ")[3]);
  130. currentFrame.r = Float.parseFloat(line.split(" ")[4]);
  131. }
  132. // sets the rotation for the middle
  133. else if (line.contains("Armorstand_Middle")) {
  134. float x = (float) Math.toRadians(Float.parseFloat(line.split(" ")[1]));
  135. float y = (float) Math.toRadians(Float.parseFloat(line.split(" ")[2]));
  136. float z = (float) Math.toRadians(Float.parseFloat(line.split(" ")[3]));
  137. currentFrame.middle = new EulerAngle(x, y, z);
  138. }
  139. // sets the rotation for the right leg
  140. else if (line.contains("Armorstand_Right_Leg")) {
  141. float x = (float) Math.toRadians(Float.parseFloat(line.split(" ")[1]));
  142. float y = (float) Math.toRadians(Float.parseFloat(line.split(" ")[2]));
  143. float z = (float) Math.toRadians(Float.parseFloat(line.split(" ")[3]));
  144. currentFrame.rightLeg = new EulerAngle(x, y, z);
  145. }
  146. // sets the rotation for the left leg
  147. else if (line.contains("Armorstand_Left_Leg")) {
  148. float x = (float) Math.toRadians(Float.parseFloat(line.split(" ")[1]));
  149. float y = (float) Math.toRadians(Float.parseFloat(line.split(" ")[2]));
  150. float z = (float) Math.toRadians(Float.parseFloat(line.split(" ")[3]));
  151. currentFrame.leftLeg = new EulerAngle(x, y, z);
  152. }
  153. // sets the rotation for the left arm
  154. else if (line.contains("Armorstand_Left_Arm")) {
  155. float x = (float) Math.toRadians(Float.parseFloat(line.split(" ")[1]));
  156. float y = (float) Math.toRadians(Float.parseFloat(line.split(" ")[2]));
  157. float z = (float) Math.toRadians(Float.parseFloat(line.split(" ")[3]));
  158. currentFrame.leftArm = new EulerAngle(x, y, z);
  159. }
  160. // sets the rotation for the right arm
  161. else if (line.contains("Armorstand_Right_Arm")) {
  162. float x = (float) Math.toRadians(Float.parseFloat(line.split(" ")[1]));
  163. float y = (float) Math.toRadians(Float.parseFloat(line.split(" ")[2]));
  164. float z = (float) Math.toRadians(Float.parseFloat(line.split(" ")[3]));
  165. currentFrame.rightArm = new EulerAngle(x, y, z);
  166. }
  167. // sets the rotation for the head
  168. else if (line.contains("Armorstand_Head")) {
  169. float x = (float) Math.toRadians(Float.parseFloat(line.split(" ")[1]));
  170. float y = (float) Math.toRadians(Float.parseFloat(line.split(" ")[2]));
  171. float z = (float) Math.toRadians(Float.parseFloat(line.split(" ")[3]));
  172. currentFrame.head = new EulerAngle(x, y, z);
  173. }
  174. }
  175. if (currentFrame != null) {
  176. frames[currentFrame.frameID] = currentFrame;
  177. }
  178. } catch (Exception ex) {
  179. ex.printStackTrace();
  180. } finally {
  181. // make sure to close the stream!
  182. if (br != null) {
  183. try {
  184. br.close();
  185. } catch (Exception ex) {
  186. ex.printStackTrace();
  187. }
  188. }
  189. }
  190. // add the animation to the cache, else adding the whole cache thing has no point.
  191. animCache.put(aniFile.getAbsolutePath(), frames);
  192. }
  193. // register this instance of the animator
  194. animators.add(this);
  195. }
  196.  
  197. /**
  198. * 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.
  199. */
  200. public void remove() {
  201. animators.remove(this);
  202. }
  203.  
  204. /** Pauses the animation */
  205. public void pause() {
  206. paused = true;
  207. }
  208.  
  209. /**
  210. * 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.
  211. */
  212. public void stop() {
  213. // set the current frame to 0 and update the frame and set it to 0 again
  214. currentFrame = 0;
  215. update();
  216. currentFrame = 0;
  217. paused = true;
  218. }
  219.  
  220. /** Plays the animation */
  221. public void play() {
  222. paused = false;
  223. }
  224.  
  225. /** Updates the animation and goes to the next frame */
  226. public void update() {
  227. // make sure that the animation isn't paused
  228. if (!paused) {
  229. // makes sure that the frame is in bounds
  230. if (currentFrame >= (length - 1) || currentFrame < 0) {
  231. currentFrame = 0;
  232. }
  233. // get the frame
  234. Frame f = frames[currentFrame];
  235. //checks if we need to interpolate. If so interpolate.
  236. if(interpolate) {
  237. if(f == null) {
  238. f = interpolate(currentFrame);
  239. }
  240. }
  241. // make sure it's not null
  242. if (f != null) {
  243. // get the new location
  244. Location newLoc = startLocation.clone().add(f.x, f.y, f.z);
  245. newLoc.setYaw(f.r + newLoc.getYaw());
  246. // set all the values
  247. armorStand.teleport(newLoc);
  248. armorStand.setBodyPose(f.middle);
  249. armorStand.setLeftLegPose(f.leftLeg);
  250. armorStand.setRightLegPose(f.rightLeg);
  251. armorStand.setLeftArmPose(f.leftArm);
  252. armorStand.setRightArmPose(f.rightArm);
  253. armorStand.setHeadPose(f.head);
  254. }
  255. // go one frame higher
  256. currentFrame++;
  257. }
  258. }
  259.  
  260. /** Returns the current frame */
  261. public int getCurrentFrame() {
  262. return currentFrame;
  263. }
  264.  
  265. /** Sets the current frame */
  266. public void setCurrentFrame(int currentFrame) {
  267. this.currentFrame = currentFrame;
  268. }
  269.  
  270. /** Returns the armor stand this instance animates */
  271. public ArmorStand getArmorStand() {
  272. return armorStand;
  273. }
  274.  
  275. /** Returns the amount of frame this animation has */
  276. public int getLength() {
  277. return length;
  278. }
  279.  
  280. /** Returns the list of frames */
  281. public Frame[] getFrames() {
  282. return frames;
  283. }
  284.  
  285. /** Returns if the animation is paused */
  286. public boolean isPaused() {
  287. return paused;
  288. }
  289.  
  290. /** Gets the start location */
  291. public Location getStartLocation() {
  292. return startLocation;
  293. }
  294.  
  295. /**
  296. * Sets the start location. If you want to teleport the armor stand this is the recommended function
  297. *
  298. * @param location
  299. */
  300. public void setStartLocation(Location location) {
  301. startLocation = location;
  302. }
  303.  
  304. /** Returns interpolate */
  305. public boolean isInterpolated() {
  306. return interpolate;
  307. }
  308.  
  309. /** Sets interpolate */
  310. public void setInterpolated(boolean interpolate) {
  311. this.interpolate = interpolate;
  312. }
  313.  
  314. /**Returns an interpolated frame*/
  315. private Frame interpolate(int frameID) {
  316. //get the minimum and maximum frames that are the closest
  317. Frame minFrame = null;
  318. for (int i = frameID; i >= 0; i--) {
  319. if (frames[i] != null) {
  320. minFrame = frames[i];
  321. break;
  322. }
  323. }
  324. Frame maxFrame = null;
  325. for (int i = frameID; i < frames.length; i++) {
  326. if (frames[i] != null) {
  327. maxFrame = frames[i];
  328. break;
  329. }
  330. }
  331. //make sure that those frame weren't the last one
  332. Frame res = null;
  333.  
  334. if(maxFrame == null || minFrame == null) {
  335. if(maxFrame == null && minFrame != null) {
  336. return minFrame;
  337. }
  338. if(minFrame == null && maxFrame != null) {
  339. return maxFrame;
  340. }
  341. res = new Frame();
  342. res.frameID = frameID;
  343. return res;
  344. }
  345. //create the frame and interpolate
  346. res = new Frame();
  347. res.frameID = frameID;
  348.  
  349. //this part calculates the distance the current frame is from the minimum and maximum frame and this allows for an easy linear interpolation
  350. float Dmin = frameID - minFrame.frameID;
  351. float D = maxFrame.frameID - minFrame.frameID;
  352. float D0 = Dmin / D;
  353.  
  354. res = minFrame.mult(1 - D0, frameID).add(maxFrame.mult(D0, frameID), frameID);
  355.  
  356. return res;
  357. }
  358.  
  359. /**
  360. * The frame class. This class holds all the information of one frame.
  361. */
  362. public static class Frame {
  363. /**The Frame ID*/
  364. int frameID;
  365. /**the location and rotation*/
  366. float x, y, z, r;
  367. /**The rotation of the body parts*/
  368. EulerAngle middle;
  369. EulerAngle rightLeg;
  370. EulerAngle leftLeg;
  371. EulerAngle rightArm;
  372. EulerAngle leftArm;
  373. EulerAngle head;
  374. /**This multiplies every value with another value.
  375. * Used for interpolation
  376. * @param a
  377. * @param frameID
  378. * @return
  379. */
  380. public Frame mult(float a, int frameID) {
  381. Frame f = new Frame();
  382. f.frameID = frameID;
  383. f.x = f.x * a;
  384. f.y = f.y * a;
  385. f.z = f.z * a;
  386. f.r = f.r * a;
  387. f.middle = new EulerAngle(middle.getX() * a, middle.getY() * a, middle.getZ() * a);
  388. f.rightLeg = new EulerAngle(rightLeg.getX() * a, rightLeg.getY() * a, rightLeg.getZ() * a);
  389. f.leftLeg = new EulerAngle(leftLeg.getX() * a, leftLeg.getY() * a, leftLeg.getZ() * a);
  390. f.rightArm = new EulerAngle(rightArm.getX() * a, rightArm.getY() * a, rightArm.getZ() * a);
  391. f.leftArm = new EulerAngle(leftArm.getX() * a, leftArm.getY() * a, leftArm.getZ() * a);
  392. f.head = new EulerAngle(head.getX() * a, head.getY() * a, head.getZ() * a);
  393. return f;
  394. }
  395. /**This adds a value to every value.
  396. * Used for interpolation
  397. * @param a
  398. * @param frameID
  399. * @return
  400. */
  401. public Frame add(Frame a, int frameID) {
  402. Frame f = new Frame();
  403. f.frameID = frameID;
  404. f.x = f.x + a.x;
  405. f.y = f.y + a.y;
  406. f.z = f.z + a.z;
  407. f.r = f.r + a.r;
  408. f.middle = new EulerAngle(middle.getX() + a.middle.getX(), middle.getY() + a.middle.getY(), middle.getZ() + a.middle.getZ());
  409. f.rightLeg = new EulerAngle(rightLeg.getX() + a.rightLeg.getX(), rightLeg.getY() + a.rightLeg.getY(), rightLeg.getZ() + a.rightLeg.getZ());
  410. f.leftLeg = new EulerAngle(leftLeg.getX() + a.leftLeg.getX(), leftLeg.getY() + a.leftLeg.getY(), leftLeg.getZ() + a.leftLeg.getZ());
  411. f.rightArm = new EulerAngle(rightArm.getX() + a.rightArm.getX(), rightArm.getY() + a.rightArm.getY(), rightArm.getZ() + a.rightArm.getZ());
  412. f.leftArm = new EulerAngle(leftArm.getX() + a.leftArm.getX(), leftArm.getY() + a.leftArm.getY(), leftArm.getZ() + a.leftArm.getZ());
  413. f.head = new EulerAngle(head.getX() + a.head.getX(), head.getY() + a.head.getY(), head.getZ() + a.head.getZ());
  414. return f;
  415. }
  416. }
  417.  
  418. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement