2023-01-25 23:18:27 -06:00
|
|
|
package noppes.npcs.controllers;
|
|
|
|
|
|
2026-02-16 20:42:57 -03:00
|
|
|
import kamkeel.npcs.util.Register;
|
2023-11-08 14:16:01 -05:00
|
|
|
import net.minecraft.nbt.CompressedStreamTools;
|
|
|
|
|
import net.minecraft.nbt.NBTTagCompound;
|
|
|
|
|
import net.minecraft.nbt.NBTTagList;
|
2023-01-25 23:18:27 -06:00
|
|
|
import noppes.npcs.CustomNpcs;
|
|
|
|
|
import noppes.npcs.LogWriter;
|
2023-01-26 14:14:13 -06:00
|
|
|
import noppes.npcs.api.handler.IAnimationHandler;
|
2023-01-25 23:18:27 -06:00
|
|
|
import noppes.npcs.api.handler.data.IAnimation;
|
|
|
|
|
import noppes.npcs.controllers.data.Animation;
|
2026-01-28 16:31:26 -05:00
|
|
|
import noppes.npcs.controllers.data.BuiltInAnimation;
|
2023-01-25 23:18:27 -06:00
|
|
|
import noppes.npcs.util.NBTJsonUtil;
|
|
|
|
|
|
2025-07-23 14:22:52 -04:00
|
|
|
import java.io.BufferedInputStream;
|
2026-01-28 16:31:26 -05:00
|
|
|
import java.io.BufferedReader;
|
2025-07-23 14:22:52 -04:00
|
|
|
import java.io.DataInputStream;
|
|
|
|
|
import java.io.File;
|
|
|
|
|
import java.io.FileInputStream;
|
|
|
|
|
import java.io.FileOutputStream;
|
|
|
|
|
import java.io.IOException;
|
2026-01-28 16:31:26 -05:00
|
|
|
import java.io.InputStream;
|
|
|
|
|
import java.io.InputStreamReader;
|
|
|
|
|
import java.net.URI;
|
|
|
|
|
import java.net.URISyntaxException;
|
|
|
|
|
import java.net.URL;
|
|
|
|
|
import java.nio.file.FileSystem;
|
|
|
|
|
import java.nio.file.FileSystems;
|
|
|
|
|
import java.nio.file.Files;
|
|
|
|
|
import java.nio.file.Path;
|
|
|
|
|
import java.nio.file.Paths;
|
2026-02-16 20:42:57 -03:00
|
|
|
import java.util.*;
|
2026-01-28 16:31:26 -05:00
|
|
|
import java.util.stream.Stream;
|
2023-11-08 14:16:01 -05:00
|
|
|
import java.util.zip.GZIPInputStream;
|
2023-01-25 23:18:27 -06:00
|
|
|
|
2023-01-26 14:14:13 -06:00
|
|
|
public class AnimationController implements IAnimationHandler {
|
2023-11-01 21:39:08 -04:00
|
|
|
public HashMap<Integer, Animation> animations;
|
2023-11-08 14:16:01 -05:00
|
|
|
private HashMap<Integer, String> bootOrder;
|
|
|
|
|
|
2026-01-28 16:31:26 -05:00
|
|
|
// Built-in animations loaded from assets folder (read-only, keyed by name)
|
|
|
|
|
public HashMap<String, BuiltInAnimation> builtInAnimations = new HashMap<>();
|
|
|
|
|
private static final String BUILTIN_ANIMATIONS_PATH = "/assets/customnpcs/animations";
|
|
|
|
|
private static final String BUILTIN_ANIMATIONS_RESOURCE = "assets/customnpcs/animations";
|
|
|
|
|
|
2026-02-16 12:40:02 -03:00
|
|
|
public static AnimationController Instance = new AnimationController();
|
2023-11-01 21:39:08 -04:00
|
|
|
private int lastUsedID = 0;
|
2023-01-25 23:18:27 -06:00
|
|
|
|
2026-02-16 20:42:57 -03:00
|
|
|
public LinkedHashMap<String, Register.Animations> registeredAnimations = new LinkedHashMap<>();
|
|
|
|
|
|
2023-01-25 23:18:27 -06:00
|
|
|
public AnimationController() {
|
2023-06-05 22:12:48 -04:00
|
|
|
Instance = this;
|
2023-11-08 14:16:01 -05:00
|
|
|
bootOrder = new HashMap<>();
|
2023-01-26 14:14:13 -06:00
|
|
|
animations = new HashMap<>();
|
2023-01-25 23:18:27 -06:00
|
|
|
}
|
|
|
|
|
|
2025-03-12 20:12:01 -04:00
|
|
|
public static AnimationController getInstance() {
|
2023-06-05 22:12:48 -04:00
|
|
|
return Instance;
|
2023-03-21 19:34:24 -04:00
|
|
|
}
|
|
|
|
|
|
2025-03-12 20:12:01 -04:00
|
|
|
public void load() {
|
2024-04-20 22:06:06 -04:00
|
|
|
bootOrder = new HashMap<>();
|
|
|
|
|
animations = new HashMap<>();
|
2026-01-28 16:31:26 -05:00
|
|
|
builtInAnimations = new HashMap<>();
|
2023-01-25 23:18:27 -06:00
|
|
|
LogWriter.info("Loading animations...");
|
2026-01-28 16:31:26 -05:00
|
|
|
loadBuiltInAnimations();
|
2026-02-16 20:42:57 -03:00
|
|
|
loadRegisteredBuiltIns();
|
2023-11-08 14:16:01 -05:00
|
|
|
readAnimationMap();
|
2023-01-25 23:18:27 -06:00
|
|
|
loadAnimations();
|
|
|
|
|
LogWriter.info("Done loading animations.");
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-28 16:31:26 -05:00
|
|
|
/**
|
|
|
|
|
* Load built-in animations from assets/customnpcs/animations/ folder.
|
|
|
|
|
* These animations are read-only and accessed by name only (no IDs).
|
|
|
|
|
* Scans the folder directly instead of using a manifest.
|
|
|
|
|
*/
|
|
|
|
|
private void loadBuiltInAnimations() {
|
|
|
|
|
try {
|
|
|
|
|
URL resourceUrl = CustomNpcs.class.getResource(BUILTIN_ANIMATIONS_PATH);
|
|
|
|
|
if (resourceUrl == null) {
|
|
|
|
|
LogWriter.info("Built-in animations folder not found: " + BUILTIN_ANIMATIONS_PATH);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
URI uri = resourceUrl.toURI();
|
|
|
|
|
Path animationsPath;
|
|
|
|
|
|
|
|
|
|
if (uri.getScheme().equals("jar")) {
|
|
|
|
|
// Running from JAR - need to create a FileSystem to access it
|
|
|
|
|
FileSystem fileSystem = null;
|
|
|
|
|
try {
|
|
|
|
|
fileSystem = FileSystems.getFileSystem(uri);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
fileSystem = FileSystems.newFileSystem(uri, Collections.emptyMap());
|
|
|
|
|
}
|
|
|
|
|
animationsPath = fileSystem.getPath(BUILTIN_ANIMATIONS_PATH);
|
|
|
|
|
} else {
|
|
|
|
|
// Running from file system (dev environment)
|
|
|
|
|
animationsPath = Paths.get(uri);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Scan the directory for .json files
|
|
|
|
|
try (Stream<Path> paths = Files.walk(animationsPath, 1)) {
|
|
|
|
|
paths.filter(path -> path.toString().endsWith(".json"))
|
|
|
|
|
.forEach(path -> {
|
|
|
|
|
String fileName = path.getFileName().toString();
|
|
|
|
|
String animName = fileName.substring(0, fileName.length() - 5); // Remove .json
|
|
|
|
|
try {
|
|
|
|
|
loadBuiltInAnimation(animName);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
LogWriter.error("Error loading built-in animation: " + animName, e);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LogWriter.info("Loaded " + builtInAnimations.size() + " built-in animations.");
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
LogWriter.error("Error scanning built-in animations folder", e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-01 20:09:12 -03:00
|
|
|
public void loadBuiltInAnimation(Class<?> modClass, String path, String animName) throws Exception {
|
|
|
|
|
String resourcePath = path + "/" + animName + ".json";
|
|
|
|
|
try (InputStream stream = modClass.getResourceAsStream(resourcePath)) {
|
2026-01-28 16:31:26 -05:00
|
|
|
if (stream == null) {
|
2026-02-16 20:21:32 -03:00
|
|
|
LogWriter.error("Animation not found: " + animName.toLowerCase());
|
2026-01-28 16:31:26 -05:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Read JSON content
|
|
|
|
|
StringBuilder content = new StringBuilder();
|
|
|
|
|
BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
|
|
|
|
|
String line;
|
|
|
|
|
while ((line = reader.readLine()) != null) {
|
|
|
|
|
content.append(line);
|
|
|
|
|
}
|
|
|
|
|
reader.close();
|
|
|
|
|
|
|
|
|
|
// Parse NBT from JSON
|
|
|
|
|
NBTTagCompound nbt = NBTJsonUtil.Convert(content.toString());
|
|
|
|
|
|
|
|
|
|
BuiltInAnimation animation = new BuiltInAnimation(animName);
|
|
|
|
|
animation.readFromNBT(nbt);
|
|
|
|
|
|
|
|
|
|
// Store with lowercase key for case-insensitive lookup
|
|
|
|
|
builtInAnimations.put(animName.toLowerCase(), animation);
|
2026-02-16 20:21:32 -03:00
|
|
|
LogWriter.info("Registered animation: " + animName.toLowerCase());
|
2026-01-28 16:31:26 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-01 20:09:12 -03:00
|
|
|
/**
|
|
|
|
|
* Load a single built-in animation by name from assets folder.
|
|
|
|
|
*/
|
|
|
|
|
private void loadBuiltInAnimation(String animName) throws Exception {
|
|
|
|
|
loadBuiltInAnimation(CustomNpcs.class, BUILTIN_ANIMATIONS_PATH, animName);
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-12 20:12:01 -04:00
|
|
|
private void loadAnimations() {
|
2023-01-25 23:18:27 -06:00
|
|
|
animations.clear();
|
|
|
|
|
|
|
|
|
|
File dir = getDir();
|
|
|
|
|
if (!dir.exists()) {
|
|
|
|
|
dir.mkdir();
|
|
|
|
|
} else {
|
|
|
|
|
for (File file : dir.listFiles()) {
|
|
|
|
|
if (!file.isFile() || !file.getName().endsWith(".json"))
|
|
|
|
|
continue;
|
|
|
|
|
try {
|
|
|
|
|
Animation animation = new Animation();
|
|
|
|
|
animation.readFromNBT(NBTJsonUtil.LoadFile(file));
|
2023-11-08 14:16:01 -05:00
|
|
|
animation.name = file.getName().substring(0, file.getName().length() - 5);
|
|
|
|
|
|
2025-03-12 20:12:01 -04:00
|
|
|
if (animation.id == -1) {
|
2023-11-08 14:16:01 -05:00
|
|
|
animation.id = getUnusedId();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int originalID = animation.id;
|
|
|
|
|
int setID = animation.id;
|
2025-03-12 20:12:01 -04:00
|
|
|
while (bootOrder.containsKey(setID) || animations.containsKey(setID)) {
|
|
|
|
|
if (bootOrder.containsKey(setID))
|
|
|
|
|
if (bootOrder.get(setID).equals(animation.name))
|
2023-11-08 14:16:01 -05:00
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
setID++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
animation.id = setID;
|
2025-03-12 20:12:01 -04:00
|
|
|
if (originalID != setID) {
|
2023-11-08 14:16:01 -05:00
|
|
|
LogWriter.info("Found Animation ID Mismatch: " + animation.name + ", New ID: " + setID);
|
|
|
|
|
animation.save();
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-01 21:39:08 -04:00
|
|
|
animations.put(animation.id, animation);
|
2025-03-12 20:12:01 -04:00
|
|
|
} catch (Exception e) {
|
2023-01-25 23:18:27 -06:00
|
|
|
LogWriter.error("Error loading: " + file.getAbsolutePath(), e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-11-08 14:16:01 -05:00
|
|
|
|
|
|
|
|
saveAnimationMap();
|
2023-01-25 23:18:27 -06:00
|
|
|
}
|
|
|
|
|
|
2026-02-16 20:42:57 -03:00
|
|
|
public void addAnimationRegister(String namespace, Register.Animations register) {
|
|
|
|
|
registeredAnimations.put(namespace, register);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void loadRegisteredBuiltIns() {
|
|
|
|
|
for (Register.Animations register : registeredAnimations.values()) {
|
|
|
|
|
register.register();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-26 14:14:13 -06:00
|
|
|
private File getDir() {
|
|
|
|
|
return new File(CustomNpcs.getWorldSaveDirectory(), "animations");
|
2023-01-25 23:18:27 -06:00
|
|
|
}
|
|
|
|
|
|
2025-03-12 20:12:01 -04:00
|
|
|
public int getUnusedId() {
|
|
|
|
|
if (lastUsedID == 0) {
|
|
|
|
|
for (int catid : animations.keySet()) {
|
|
|
|
|
if (catid > lastUsedID)
|
2023-11-01 21:39:08 -04:00
|
|
|
lastUsedID = catid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
lastUsedID++;
|
|
|
|
|
return lastUsedID;
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-12 20:12:01 -04:00
|
|
|
public IAnimation saveAnimation(IAnimation animation) {
|
2026-01-28 16:31:26 -05:00
|
|
|
// Reject saving built-in animations
|
|
|
|
|
if (animation instanceof BuiltInAnimation) {
|
|
|
|
|
LogWriter.info("Cannot save built-in animation: " + animation.getName());
|
|
|
|
|
return animation;
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-12 20:12:01 -04:00
|
|
|
if (animation.getID() < 0) {
|
2023-11-01 21:39:08 -04:00
|
|
|
animation.setID(getUnusedId());
|
2025-03-12 20:12:01 -04:00
|
|
|
while (hasName(animation.getName()))
|
2023-11-01 21:39:08 -04:00
|
|
|
animation.setName(animation.getName() + "_");
|
2025-03-12 20:12:01 -04:00
|
|
|
} else {
|
2023-11-01 21:39:08 -04:00
|
|
|
Animation existing = animations.get(animation.getID());
|
2025-03-12 20:12:01 -04:00
|
|
|
if (existing != null && !existing.name.equals(animation.getName()))
|
|
|
|
|
while (hasName(animation.getName()))
|
2023-11-01 21:39:08 -04:00
|
|
|
animation.setName(animation.getName() + "_");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
animations.remove(animation.getID());
|
|
|
|
|
animations.put(animation.getID(), (Animation) animation);
|
2023-01-25 23:18:27 -06:00
|
|
|
|
2023-11-08 14:16:01 -05:00
|
|
|
saveAnimationMap();
|
|
|
|
|
|
2023-11-01 21:39:08 -04:00
|
|
|
// Save Animation File
|
2023-01-25 23:18:27 -06:00
|
|
|
File dir = this.getDir();
|
2025-03-12 20:12:01 -04:00
|
|
|
if (!dir.exists())
|
2023-01-25 23:18:27 -06:00
|
|
|
dir.mkdirs();
|
|
|
|
|
|
2023-01-26 14:14:13 -06:00
|
|
|
File file = new File(dir, animation.getName() + ".json_new");
|
2025-03-12 20:12:01 -04:00
|
|
|
File file2 = new File(dir, animation.getName() + ".json");
|
2023-01-25 23:18:27 -06:00
|
|
|
|
|
|
|
|
try {
|
2025-03-12 20:12:01 -04:00
|
|
|
NBTJsonUtil.SaveFile(file, ((Animation) animation).writeToNBT());
|
|
|
|
|
if (file2.exists())
|
2023-01-25 23:18:27 -06:00
|
|
|
file2.delete();
|
|
|
|
|
file.renameTo(file2);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
LogWriter.except(e);
|
|
|
|
|
}
|
2023-11-01 21:39:08 -04:00
|
|
|
return animations.get(animation.getID());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public boolean hasName(String newName) {
|
2025-03-12 20:12:01 -04:00
|
|
|
if (newName.trim().isEmpty())
|
2023-11-01 21:39:08 -04:00
|
|
|
return true;
|
2025-03-12 20:12:01 -04:00
|
|
|
for (Animation animation : animations.values())
|
|
|
|
|
if (animation.name.equals(newName))
|
2023-11-01 21:39:08 -04:00
|
|
|
return true;
|
|
|
|
|
return false;
|
2023-01-25 23:18:27 -06:00
|
|
|
}
|
|
|
|
|
|
2023-01-26 14:14:13 -06:00
|
|
|
public void delete(String name) {
|
2025-03-12 20:12:01 -04:00
|
|
|
Animation delete = getAnimationFromName(name);
|
|
|
|
|
if (delete != null) {
|
2026-01-28 16:31:26 -05:00
|
|
|
// Reject deleting built-in animations
|
|
|
|
|
if (delete instanceof BuiltInAnimation) {
|
|
|
|
|
LogWriter.info("Cannot delete built-in animation: " + name);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-01 21:39:08 -04:00
|
|
|
Animation foundAnimation = this.animations.remove(delete.getID());
|
2025-03-12 20:12:01 -04:00
|
|
|
if (foundAnimation != null && foundAnimation.name != null) {
|
2023-11-01 21:39:08 -04:00
|
|
|
File dir = this.getDir();
|
|
|
|
|
for (File file : dir.listFiles()) {
|
|
|
|
|
if (!file.isFile() || !file.getName().endsWith(".json"))
|
|
|
|
|
continue;
|
2025-03-12 20:12:01 -04:00
|
|
|
if (file.getName().equals(foundAnimation.name + ".json")) {
|
2023-11-01 21:39:08 -04:00
|
|
|
file.delete();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-11-08 14:16:01 -05:00
|
|
|
|
|
|
|
|
saveAnimationMap();
|
2023-11-01 21:39:08 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void delete(int id) {
|
2026-01-28 16:31:26 -05:00
|
|
|
// Only user animations have valid IDs (>= 0)
|
|
|
|
|
if (id < 0 || !this.animations.containsKey(id))
|
2023-01-25 23:18:27 -06:00
|
|
|
return;
|
2023-11-01 21:39:08 -04:00
|
|
|
|
|
|
|
|
Animation foundAnimation = this.animations.remove(id);
|
2025-03-12 20:12:01 -04:00
|
|
|
if (foundAnimation != null && foundAnimation.name != null) {
|
2023-11-01 21:39:08 -04:00
|
|
|
File dir = this.getDir();
|
|
|
|
|
for (File file : dir.listFiles()) {
|
|
|
|
|
if (!file.isFile() || !file.getName().endsWith(".json"))
|
|
|
|
|
continue;
|
2025-03-12 20:12:01 -04:00
|
|
|
if (file.getName().equals(foundAnimation.name + ".json")) {
|
2023-11-01 21:39:08 -04:00
|
|
|
file.delete();
|
|
|
|
|
break;
|
|
|
|
|
}
|
2023-01-25 23:18:27 -06:00
|
|
|
}
|
2023-11-08 14:16:01 -05:00
|
|
|
|
|
|
|
|
saveAnimationMap();
|
2023-01-25 23:18:27 -06:00
|
|
|
}
|
2023-01-26 14:14:13 -06:00
|
|
|
}
|
|
|
|
|
|
2023-01-28 01:19:57 -06:00
|
|
|
public boolean has(String name) {
|
2023-11-01 21:39:08 -04:00
|
|
|
return getAnimationFromName(name) != null;
|
2023-01-28 01:19:57 -06:00
|
|
|
}
|
|
|
|
|
|
2023-01-26 14:14:13 -06:00
|
|
|
public IAnimation get(String name) {
|
2026-02-01 20:09:12 -03:00
|
|
|
return get(name, false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public IAnimation get(String name, boolean builtIn) {
|
2026-01-28 16:57:49 -05:00
|
|
|
// Check user/custom animations first (allows overriding built-in)
|
|
|
|
|
for (Map.Entry<Integer, Animation> entry : animations.entrySet()) {
|
|
|
|
|
if (entry.getValue().name.equalsIgnoreCase(name)) {
|
|
|
|
|
return entry.getValue();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Then check built-in animations
|
2026-02-01 20:09:12 -03:00
|
|
|
if (builtIn) {
|
|
|
|
|
BuiltInAnimation builtInAnim = builtInAnimations.get(name.toLowerCase());
|
|
|
|
|
if (builtInAnim != null) {
|
|
|
|
|
return builtInAnim;
|
|
|
|
|
}
|
2026-01-28 16:31:26 -05:00
|
|
|
}
|
2026-02-01 20:09:12 -03:00
|
|
|
|
2026-01-28 16:57:49 -05:00
|
|
|
return null;
|
2023-11-01 21:39:08 -04:00
|
|
|
}
|
|
|
|
|
|
2026-01-28 16:31:26 -05:00
|
|
|
/**
|
|
|
|
|
* Get a user animation by ID.
|
|
|
|
|
* Built-in animations have no IDs and cannot be retrieved this way.
|
|
|
|
|
*/
|
2023-11-01 21:39:08 -04:00
|
|
|
public IAnimation get(int id) {
|
2026-01-28 16:31:26 -05:00
|
|
|
// Only user animations have valid IDs
|
|
|
|
|
if (id < 0) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
2023-11-01 21:39:08 -04:00
|
|
|
return this.animations.get(id);
|
2023-01-25 23:18:27 -06:00
|
|
|
}
|
|
|
|
|
|
2026-01-28 16:31:26 -05:00
|
|
|
/**
|
|
|
|
|
* Get a built-in animation by name.
|
|
|
|
|
* @param name The animation name (case-insensitive)
|
|
|
|
|
* @return The built-in animation, or null if not found
|
|
|
|
|
*/
|
|
|
|
|
public BuiltInAnimation getBuiltInAnimation(String name) {
|
|
|
|
|
return builtInAnimations.get(name.toLowerCase());
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-26 14:14:13 -06:00
|
|
|
public IAnimation[] getAnimations() {
|
|
|
|
|
ArrayList<IAnimation> animations = new ArrayList<>(this.animations.values());
|
|
|
|
|
return animations.toArray(new IAnimation[0]);
|
2023-01-25 23:18:27 -06:00
|
|
|
}
|
2023-03-21 19:34:24 -04:00
|
|
|
|
2026-01-28 16:31:26 -05:00
|
|
|
/**
|
|
|
|
|
* Get all built-in animations.
|
|
|
|
|
*/
|
|
|
|
|
public IAnimation[] getBuiltInAnimations() {
|
|
|
|
|
return builtInAnimations.values().toArray(new IAnimation[0]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get all animations (both built-in and user-created).
|
|
|
|
|
*/
|
|
|
|
|
public IAnimation[] getAllAnimations() {
|
|
|
|
|
ArrayList<IAnimation> all = new ArrayList<>();
|
|
|
|
|
all.addAll(builtInAnimations.values());
|
|
|
|
|
all.addAll(animations.values());
|
|
|
|
|
return all.toArray(new IAnimation[0]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Check if an animation name is a built-in animation.
|
|
|
|
|
* @param name The animation name to check
|
|
|
|
|
* @return true if this is a built-in animation
|
|
|
|
|
*/
|
|
|
|
|
public boolean isBuiltIn(String name) {
|
|
|
|
|
return builtInAnimations.containsKey(name.toLowerCase());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @deprecated Built-in animations no longer use IDs. Use isBuiltIn(String name) instead.
|
|
|
|
|
*/
|
|
|
|
|
@Deprecated
|
|
|
|
|
public boolean isBuiltIn(int id) {
|
|
|
|
|
return false; // Built-in animations have no IDs
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-12 20:12:01 -04:00
|
|
|
public Animation getAnimationFromName(String animation) {
|
2026-01-28 16:57:49 -05:00
|
|
|
// Check user/custom animations first (allows overriding built-in)
|
2026-01-28 16:31:26 -05:00
|
|
|
for (Map.Entry<Integer, Animation> entryAnimation : animations.entrySet()) {
|
2025-03-12 20:12:01 -04:00
|
|
|
if (entryAnimation.getValue().name.equalsIgnoreCase(animation)) {
|
2023-03-21 19:34:24 -04:00
|
|
|
return entryAnimation.getValue();
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-01-28 16:57:49 -05:00
|
|
|
// Then check built-in animations
|
|
|
|
|
BuiltInAnimation builtIn = builtInAnimations.get(animation.toLowerCase());
|
|
|
|
|
if (builtIn != null) {
|
|
|
|
|
return builtIn;
|
|
|
|
|
}
|
2023-03-21 19:34:24 -04:00
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-28 16:31:26 -05:00
|
|
|
/**
|
|
|
|
|
* Get names of all built-in animations.
|
|
|
|
|
*/
|
|
|
|
|
public String[] getBuiltInAnimationNames() {
|
|
|
|
|
return builtInAnimations.keySet().toArray(new String[0]);
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-21 19:34:24 -04:00
|
|
|
public String[] getNames() {
|
|
|
|
|
String[] names = new String[animations.size()];
|
|
|
|
|
int i = 0;
|
2025-03-12 20:12:01 -04:00
|
|
|
for (Animation animation : animations.values()) {
|
2023-03-21 19:34:24 -04:00
|
|
|
names[i] = animation.name.toLowerCase();
|
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
return names;
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-08 14:16:01 -05:00
|
|
|
|
2026-01-22 23:07:38 -05:00
|
|
|
/// /////////////////////////////////////////////////////
|
|
|
|
|
/// /////////////////////////////////////////////////////
|
2023-11-08 14:16:01 -05:00
|
|
|
// ANIMATION MAP
|
2025-03-12 20:12:01 -04:00
|
|
|
public File getMapDir() {
|
2023-11-08 14:16:01 -05:00
|
|
|
File dir = CustomNpcs.getWorldSaveDirectory();
|
2025-03-12 20:12:01 -04:00
|
|
|
if (!dir.exists())
|
2023-11-08 14:16:01 -05:00
|
|
|
dir.mkdir();
|
|
|
|
|
return dir;
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-12 20:12:01 -04:00
|
|
|
public void readAnimationMap() {
|
2023-11-08 14:16:01 -05:00
|
|
|
bootOrder.clear();
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
File file = new File(getMapDir(), "animations.dat");
|
2025-03-12 20:12:01 -04:00
|
|
|
if (file.exists()) {
|
2023-11-08 14:16:01 -05:00
|
|
|
loadAnimationMapFile(file);
|
|
|
|
|
}
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
try {
|
|
|
|
|
File file = new File(getMapDir(), "animations.dat_old");
|
2025-03-12 20:12:01 -04:00
|
|
|
if (file.exists()) {
|
2023-11-08 14:16:01 -05:00
|
|
|
loadAnimationMapFile(file);
|
|
|
|
|
}
|
2025-03-12 20:12:01 -04:00
|
|
|
} catch (Exception ignored) {
|
|
|
|
|
}
|
2023-11-08 14:16:01 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-12 20:12:01 -04:00
|
|
|
public NBTTagCompound writeMapNBT() {
|
2023-11-08 14:16:01 -05:00
|
|
|
NBTTagCompound nbt = new NBTTagCompound();
|
|
|
|
|
NBTTagList animationList = new NBTTagList();
|
2025-03-12 20:12:01 -04:00
|
|
|
for (Integer key : animations.keySet()) {
|
2023-11-08 14:16:01 -05:00
|
|
|
Animation animation = animations.get(key);
|
2025-03-12 20:12:01 -04:00
|
|
|
if (!animation.getName().isEmpty()) {
|
2023-11-08 14:16:01 -05:00
|
|
|
NBTTagCompound animationCompound = new NBTTagCompound();
|
|
|
|
|
animationCompound.setString("Name", animation.getName());
|
|
|
|
|
animationCompound.setInteger("ID", key);
|
|
|
|
|
|
|
|
|
|
animationList.appendTag(animationCompound);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
nbt.setTag("Animations", animationList);
|
|
|
|
|
return nbt;
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-12 20:12:01 -04:00
|
|
|
public void readMapNBT(NBTTagCompound compound) {
|
2023-11-08 14:16:01 -05:00
|
|
|
NBTTagList list = compound.getTagList("Animations", 10);
|
2025-03-12 20:12:01 -04:00
|
|
|
if (list != null) {
|
|
|
|
|
for (int i = 0; i < list.tagCount(); i++) {
|
2023-11-08 14:16:01 -05:00
|
|
|
NBTTagCompound nbttagcompound = list.getCompoundTagAt(i);
|
|
|
|
|
String animationName = nbttagcompound.getString("Name");
|
|
|
|
|
Integer key = nbttagcompound.getInteger("ID");
|
|
|
|
|
bootOrder.put(key, animationName);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void loadAnimationMapFile(File file) throws IOException {
|
|
|
|
|
DataInputStream var1 = new DataInputStream(new BufferedInputStream(new GZIPInputStream(new FileInputStream(file))));
|
|
|
|
|
readAnimationMap(var1);
|
|
|
|
|
var1.close();
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-12 20:12:01 -04:00
|
|
|
public void readAnimationMap(DataInputStream stream) throws IOException {
|
2023-11-08 14:16:01 -05:00
|
|
|
NBTTagCompound nbtCompound = CompressedStreamTools.read(stream);
|
|
|
|
|
this.readMapNBT(nbtCompound);
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-12 20:12:01 -04:00
|
|
|
public void saveAnimationMap() {
|
2023-11-08 14:16:01 -05:00
|
|
|
try {
|
|
|
|
|
File saveDir = getMapDir();
|
|
|
|
|
File file = new File(saveDir, "animations.dat_new");
|
|
|
|
|
File file1 = new File(saveDir, "animations.dat_old");
|
|
|
|
|
File file2 = new File(saveDir, "animations.dat");
|
|
|
|
|
CompressedStreamTools.writeCompressed(this.writeMapNBT(), new FileOutputStream(file));
|
2025-03-12 20:12:01 -04:00
|
|
|
if (file1.exists()) {
|
2023-11-08 14:16:01 -05:00
|
|
|
file1.delete();
|
|
|
|
|
}
|
|
|
|
|
file2.renameTo(file1);
|
2025-03-12 20:12:01 -04:00
|
|
|
if (file2.exists()) {
|
2023-11-08 14:16:01 -05:00
|
|
|
file2.delete();
|
|
|
|
|
}
|
|
|
|
|
file.renameTo(file2);
|
2025-03-12 20:12:01 -04:00
|
|
|
if (file.exists()) {
|
2023-11-08 14:16:01 -05:00
|
|
|
file.delete();
|
|
|
|
|
}
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
LogWriter.except(e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////
|
|
|
|
|
////////////////////////////////////////////////////////
|
2023-01-25 23:18:27 -06:00
|
|
|
}
|