diff --git a/build.gradle b/build.gradle index ef23d02..ef50236 100644 --- a/build.gradle +++ b/build.gradle @@ -4,7 +4,7 @@ plugins { } group = 'fr.openmc.riftengine' -version = '0.4-SNAPSHOT' +version = '0.5-SNAPSHOT' repositories { mavenCentral() diff --git a/libs/OpenMC.jar b/libs/OpenMC.jar index 84c45a1..1821607 100644 Binary files a/libs/OpenMC.jar and b/libs/OpenMC.jar differ diff --git a/src/main/java/fr/openmc/riftengine/core/RiftLog.java b/src/main/java/fr/openmc/riftengine/core/RiftLog.java index af7cb33..610f10c 100644 --- a/src/main/java/fr/openmc/riftengine/core/RiftLog.java +++ b/src/main/java/fr/openmc/riftengine/core/RiftLog.java @@ -18,7 +18,13 @@ public enum RiftLog { NUMBER_GLYPH_REGISTERED("{}/{} glyphs enregistré", LogType.INFO), ITEM_NOT_RESOURCE("Item {} : n'a pas de best resources", LogType.WARN), - RESOURCE_NOT_FOUND("Item {} : a aucune resource", LogType.WARN) + RESOURCE_NOT_FOUND("Item {} : a aucune resource", LogType.WARN), + NUMBER_TOOLTIP_REGISTERED("{}/{} tooltip enregistré", LogType.INFO), + GLYPH_NOT_FOUND("Glyph {} introuvable", LogType.WARN), + FLIPBOOK_NULL("Flipbook est null pour ce tooltip {}", LogType.WARN), + NULL("{} est null", LogType.WARN), + + PATH_CANT_REDUCED_TO_TEXTURES("resource {} ne peut pas etre réduite à un chemin de texture", LogType.WARN), ; public static void logBanner(String pluginVersion, String server, String javaVersion) { diff --git a/src/main/java/fr/openmc/riftengine/core/converter/ConverterManager.java b/src/main/java/fr/openmc/riftengine/core/converter/ConverterManager.java index 6cd001f..6443402 100644 --- a/src/main/java/fr/openmc/riftengine/core/converter/ConverterManager.java +++ b/src/main/java/fr/openmc/riftengine/core/converter/ConverterManager.java @@ -20,9 +20,18 @@ import fr.openmc.riftengine.core.converter.writers.manifest.IconWriter; import fr.openmc.riftengine.core.converter.writers.manifest.ManifestWriter; import fr.openmc.riftengine.core.converter.writers.manifest.PackIdentity; +import fr.openmc.riftengine.core.converter.writers.sounds.SoundsDefinitionsJsonWriter; +import fr.openmc.riftengine.core.converter.writers.sounds.SoundsFileWriter; +import fr.openmc.riftengine.core.converter.writers.tooltip.UiCommonTooltipWriter; +import fr.openmc.riftengine.core.converter.writers.tooltip.glyph.TooltipGlyphApplicatorWriter; +import fr.openmc.riftengine.core.converter.writers.tooltip.glyph.TooltipGlyphAssignatorWriter; +import fr.openmc.riftengine.core.converter.writers.tooltip.textures.TooltipTextureWriter; import fr.openmc.riftengine.core.converter.writers.translations.TranslationInjector; import fr.openmc.riftengine.core.converter.writers.ui.ScoreboardUiWriter; import fr.openmc.riftengine.core.resolvers.items.ItemResourceResolver; +import fr.openmc.riftengine.core.resolvers.mcmeta.animation.AnimationMcMetaResolver; +import fr.openmc.riftengine.core.resolvers.mcmeta.gui.GuiMcMetaResolver; +import fr.openmc.riftengine.core.resolvers.tooltip.TooltipResourceResolver; import org.bukkit.plugin.java.JavaPlugin; import java.io.File; @@ -45,7 +54,11 @@ public ConverterManager(RiftPlugin plugin) { try { PackIdentity identity = PackIdentity.loadOrCreate(plugin); List items = OMCRegistry.SCANNERS.ITEMS.scan(itemsAdderContents); - ItemResourceResolver resolver = RiftRegistry.RESOLVERS.ITEM_RESOURCE_RESOLVER; + + ItemResourceResolver itemResolver = RiftRegistry.RESOLVERS.ITEM_RESOURCE_RESOLVER; + TooltipResourceResolver tooltipResolver = RiftRegistry.RESOLVERS.TOOLTIP_RESOURCE_RESOLVER; + GuiMcMetaResolver guiResolver = RiftRegistry.RESOLVERS.GUI_MCMETA_RESOLVER; + AnimationMcMetaResolver animResolver = RiftRegistry.RESOLVERS.ANIMATION_MCMETA_RESOLVER; register("Icônes", new IconWriter()); register("Manifest", new ManifestWriter(identity)); @@ -55,16 +68,27 @@ public ConverterManager(RiftPlugin plugin) { register("Scoreboard", new ScoreboardUiWriter(config.isHideScoreboardNumberBedrock())); + register("Sounds Definitions Json", new SoundsDefinitionsJsonWriter()); + register("Sounds File", new SoundsFileWriter()); + register("Icônes ItemsAdder", new IconsWriter(itemsAdderContents)); register("Mappings des symboles", new SymbolWriter()); - register("Textures des items", new ItemsTextureJsonWriter(items, resolver)); - register("Fichiers de textures", new ItemsTextureWriter(items, resolver)); - register("Mappings des items", new ItemsMappingWriter(items, resolver)); + register("Textures des items", new ItemsTextureJsonWriter(items, itemResolver)); + register("Fichiers de textures", new ItemsTextureWriter(items, itemResolver)); + register("Mappings des items", new ItemsMappingWriter(items, itemResolver)); + + register("Textures des tooltips", new TooltipTextureWriter(items, guiResolver, tooltipResolver)); + register("Assignations des glyphs pour les tooltips", + new TooltipGlyphAssignatorWriter(items, tooltipResolver)); + register("Application des glyphs sur les items", + new TooltipGlyphApplicatorWriter(items, itemsAdderContents)); + register("Génération du JSON UI pour les tooltips", + new UiCommonTooltipWriter(items, tooltipResolver, guiResolver, animResolver)); register("Textures des blocs", - new TerrainTextureJsonWriter(identity, items, resolver)); - register("Mappings des blocs", new BlocksMappingWriter(items, resolver)); + new TerrainTextureJsonWriter(identity, items, itemResolver)); + register("Mappings des blocs", new BlocksMappingWriter(items, itemResolver)); } catch (Exception e) { throw new RuntimeException("Erreur lors d'initialisation du ConverterManager", e); @@ -108,9 +132,13 @@ public static Path getJavaPackPath(JavaPlugin plugin) { File pluginsDir = plugin.getDataFolder().getParentFile(); // * root/plugins/ File itemsAdderDir = new File(pluginsDir, "ItemsAdder"); // * root/plugins/ItemsAdder File outputDir = new File(itemsAdderDir, "output"); // * root/plugins/ItemsAdder/output - File generatedDir = new File(outputDir, "generated.zip"); // * root/plugins/ItemsAdder/output/generated.zip + File clientPacksDir = new File(outputDir, "client_packs"); // * root/plugins/ItemsAdder/output/client_packs/ - return generatedDir.toPath(); + return FilesUtils.listFileNames(clientPacksDir.toPath().toAbsolutePath().toString()).stream() + .filter(name -> name.contains(".zip") && name.contains("26_3") && name.contains("fast_compression")) + .findFirst() + .map(name -> clientPacksDir.toPath().resolve(name)) + .orElse(null); } public static Path getItemsAdderContents(JavaPlugin plugin) { diff --git a/src/main/java/fr/openmc/riftengine/core/converter/writers/PackWriter.java b/src/main/java/fr/openmc/riftengine/core/converter/writers/PackWriter.java index 09195d1..10ec7ce 100644 --- a/src/main/java/fr/openmc/riftengine/core/converter/writers/PackWriter.java +++ b/src/main/java/fr/openmc/riftengine/core/converter/writers/PackWriter.java @@ -19,7 +19,11 @@ public interface PackWriter { void write(Path bedrockRootPath, Path javaRootPath) throws Exception; default void writeJsonFile(Path outputFile, JsonObject root) throws IOException { - this.writeFile(outputFile, GSON.toJson(root)); + this.writeJsonFile(outputFile, root, GSON); + } + + default void writeJsonFile(Path outputFile, JsonObject root, Gson gson) throws IOException { + this.writeFile(outputFile, gson.toJson(root)); } default void writeFile(Path outputFile, String content) throws IOException { diff --git a/src/main/java/fr/openmc/riftengine/core/converter/writers/glyph/icons/SymbolWriter.java b/src/main/java/fr/openmc/riftengine/core/converter/writers/glyph/icons/SymbolWriter.java index 0ef3826..15494e1 100644 --- a/src/main/java/fr/openmc/riftengine/core/converter/writers/glyph/icons/SymbolWriter.java +++ b/src/main/java/fr/openmc/riftengine/core/converter/writers/glyph/icons/SymbolWriter.java @@ -2,13 +2,11 @@ import fr.openmc.core.utils.YmlUtils; import fr.openmc.riftengine.core.RiftLog; -import fr.openmc.riftengine.core.RiftPlugin; import fr.openmc.riftengine.core.RiftRegistry; import fr.openmc.riftengine.core.converter.writers.PackWriter; import fr.openmc.riftengine.core.listeners.LoadAfterItemsAdderListener; import fr.openmc.riftengine.core.registry.glyphs.Glyph; import fr.openmc.riftengine.core.registry.glyphs.types.IconGlyph; -import org.bukkit.Bukkit; import org.yaml.snakeyaml.Yaml; import java.io.IOException; @@ -67,12 +65,6 @@ public void write(Path bedrockRootPath, Path javaRootPath) throws IOException { } } - LoadAfterItemsAdderListener.actionToLaunch.add(this::reloadItemsAdder); - } - - private void reloadItemsAdder() { - Bukkit.getScheduler().runTask(RiftPlugin.getInstance(), () -> - Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "iazip") - ); + LoadAfterItemsAdderListener.reloadToLaunch = true; } } diff --git a/src/main/java/fr/openmc/riftengine/core/converter/writers/manifest/IconWriter.java b/src/main/java/fr/openmc/riftengine/core/converter/writers/manifest/IconWriter.java index 2f26dd5..47ae12f 100644 --- a/src/main/java/fr/openmc/riftengine/core/converter/writers/manifest/IconWriter.java +++ b/src/main/java/fr/openmc/riftengine/core/converter/writers/manifest/IconWriter.java @@ -18,6 +18,5 @@ public void write(Path bedrockRootPath, Path javaRootPath) throws IOException { Files.copy(is, bedrockRootPath.resolve(OUTPUT_FILE), StandardCopyOption.REPLACE_EXISTING); } - } } diff --git a/src/main/java/fr/openmc/riftengine/core/converter/writers/sounds/SoundsDefinitionsJsonWriter.java b/src/main/java/fr/openmc/riftengine/core/converter/writers/sounds/SoundsDefinitionsJsonWriter.java new file mode 100644 index 0000000..e2abc75 --- /dev/null +++ b/src/main/java/fr/openmc/riftengine/core/converter/writers/sounds/SoundsDefinitionsJsonWriter.java @@ -0,0 +1,45 @@ +package fr.openmc.riftengine.core.converter.writers.sounds; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import fr.openmc.core.OMCRegistry; +import fr.openmc.riftengine.api.scanner.sounds.SoundEntry; +import fr.openmc.riftengine.core.converter.writers.PackWriter; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.List; + +/** + * Ecrit le sounds_definitions.json qui sert grosso modo a relier les textures aux items (une ref = une resource) + */ +public class SoundsDefinitionsJsonWriter implements PackWriter { + private static final String OUTPUT_FILE = "sounds/sound_definitions.json"; + + @Override + public void write(Path bedrockRootPath, Path javaRootPath) throws IOException { + List sounds = OMCRegistry.SCANNERS.SOUNDS.scan(javaRootPath); + JsonObject definitions = new JsonObject(); + + for (SoundEntry entry : sounds) { + JsonArray array = new JsonArray(); + for (String resourceId : entry.soundResourcesId()) { + if (resourceId == null) continue; + array.add(entry.toBedrockPath("minecraft", resourceId)); + } + if (array.isEmpty()) continue; + + JsonObject def = new JsonObject(); + def.addProperty("category", "player"); + def.add("sounds", array); + definitions.add(entry.toBedrockEventId(), def); + } + + JsonObject root = new JsonObject(); + root.addProperty("format_version", "1.21.50"); + root.add("sound_definitions", definitions); + + Path outputFile = bedrockRootPath.resolve(OUTPUT_FILE); + this.writeJsonFile(outputFile, root); + } +} diff --git a/src/main/java/fr/openmc/riftengine/core/converter/writers/sounds/SoundsFileWriter.java b/src/main/java/fr/openmc/riftengine/core/converter/writers/sounds/SoundsFileWriter.java new file mode 100644 index 0000000..344661b --- /dev/null +++ b/src/main/java/fr/openmc/riftengine/core/converter/writers/sounds/SoundsFileWriter.java @@ -0,0 +1,25 @@ +package fr.openmc.riftengine.core.converter.writers.sounds; + +import fr.openmc.core.OMCRegistry; +import fr.openmc.riftengine.api.scanner.sounds.SoundEntry; +import fr.openmc.riftengine.core.converter.writers.PackWriter; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.List; + +public class SoundsFileWriter implements PackWriter { + @Override + public void write(Path bedrockRootPath, Path javaRootPath) throws IOException { + List sounds = OMCRegistry.SCANNERS.SOUNDS.scan(javaRootPath); + + for (SoundEntry soundEntry : sounds) { + for (String soundResourceId : soundEntry.soundResourcesId()) { + Path soundsPath = javaRootPath.resolve(soundEntry.toJavaPath("minecraft", soundResourceId) + ".ogg"); + Path dest = bedrockRootPath.resolve(soundEntry.toBedrockPath("minecraft", soundResourceId) + ".ogg"); + + this.copyFile(soundsPath, dest); + } + } + } +} diff --git a/src/main/java/fr/openmc/riftengine/core/converter/writers/tooltip/UiCommonTooltipWriter.java b/src/main/java/fr/openmc/riftengine/core/converter/writers/tooltip/UiCommonTooltipWriter.java new file mode 100644 index 0000000..6dafc88 --- /dev/null +++ b/src/main/java/fr/openmc/riftengine/core/converter/writers/tooltip/UiCommonTooltipWriter.java @@ -0,0 +1,444 @@ +package fr.openmc.riftengine.core.converter.writers.tooltip; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; +import fr.openmc.core.utils.JsonUtils; +import fr.openmc.riftengine.api.scanner.items.ItemEntry; +import fr.openmc.riftengine.core.RiftLog; +import fr.openmc.riftengine.core.RiftRegistry; +import fr.openmc.riftengine.core.converter.writers.PackWriter; +import fr.openmc.riftengine.core.registry.glyphs.Glyph; +import fr.openmc.riftengine.core.resolvers.mcmeta.animation.AnimationMcMetaResolver; +import fr.openmc.riftengine.core.resolvers.mcmeta.gui.GuiMcMetaResolver; +import fr.openmc.riftengine.core.resolvers.mcmeta.gui.ResolvedGuiMcMeta; +import fr.openmc.riftengine.core.resolvers.tooltip.ResolvedTooltipResource; +import fr.openmc.riftengine.core.resolvers.tooltip.TooltipResourceResolver; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +public class UiCommonTooltipWriter implements PackWriter { + private final List items; + private final TooltipResourceResolver tooltipResolver; + private final GuiMcMetaResolver guiMcMetaResolver; + private final AnimationMcMetaResolver animationMcMetaResolver; + private static final String OUTPUT_FILE = "ui/ui_common.json"; + + public UiCommonTooltipWriter(List items, + TooltipResourceResolver tooltipResolver, + GuiMcMetaResolver guiMcMetaResolver, + AnimationMcMetaResolver animationMcMetaResolver) { + this.items = items; + this.tooltipResolver = tooltipResolver; + this.animationMcMetaResolver = animationMcMetaResolver; + this.guiMcMetaResolver = guiMcMetaResolver; + } + + @Override + public void write(Path bedrockRootPath, Path javaRootPath) throws IOException { + // todo: faire un TooltipScanner quand OpenMC sera divisé en module + Map tooltipResources = new HashMap<>(); + for (ItemEntry itemEntry : items) { + ResolvedTooltipResource resource = tooltipResolver.resolve(javaRootPath, itemEntry); + if (resource == null) continue; + + tooltipResources.put(resource.resourceId(), resource); + } + + String namespace = "common"; + String element = "omc_hover_text"; + + JsonObject root = new JsonObject(); + root.addProperty("namespace", namespace); + + // * génération des animations des frames des tooltips + handleTooltipAnimation(javaRootPath, root, tooltipResources); + + tooltipResources.put("default", new ResolvedTooltipResource("default")); + + // * génération des tooltips coté JSON UI + handleTooltipDetection(javaRootPath, tooltipResources, root, namespace, element); + + // * on mets la template pour "injecter" notre tooltip + handleTooltipInjection(root, namespace, element); + + + Path outputFile = bedrockRootPath.resolve(OUTPUT_FILE); + this.writeJsonFile(outputFile, root); + } + + private void handleTooltipAnimation(Path javaRootPath, JsonObject root, Map tooltipResources) throws IOException { + for (ResolvedTooltipResource resource : tooltipResources.values()) { + String animationKey = resource.getKey() + "_frame" + "_flipbook"; + ResolvedTooltipResource.FlipbookAnimation flipbookAnimation = resource.getFrameFlipbookAnimation( + animationMcMetaResolver, guiMcMetaResolver, javaRootPath); + + // * écrit le jsonobject pour les animations + ResolvedGuiMcMeta guiMeta = guiMcMetaResolver.resolve(javaRootPath, resource.frameMCMetaPath(javaRootPath)); + if (guiMeta != null && flipbookAnimation != null) { + JsonObject animation = writeAnimation(flipbookAnimation, animationKey, guiMeta); + root.add(animationKey, animation); + } + } + } + + private final String TEMPLATE_TOOLTIP = """ + { + "$hover_text_binding_name|default": "#hover_text", + "type": "panel", + "size": ["100%c", "100%c"], + "layer": 5, + "follows_cursor": true, + "offset": [12, -12], + "bindings": [ + { + "binding_name": "$hover_text_binding_name", + "binding_name_override": "#hover_text", + "binding_type": "collection", + "binding_collection_name": "$item_collection_name" + }, + { + "binding_type": "view", + "source_property_name": "(not (#hover_text = ''))", + "target_property_name": "#visible" + } + ] + }"""; + + private final String TEMPLATE_BASE_TOOLTIP = """ + { + "type": "panel", + "size": ["100%c", "100%c"], + "anchor_from": "top_left", + "anchor_to": "top_left" + }"""; + + private final String TEMPLATE_BINDINGS = """ + [ + { + "binding_name": "$hover_text_binding_name", + "binding_name_override": "#hover_text", + "binding_type": "collection", + "binding_collection_name": "$item_collection_name" + }, + { + "binding_type": "view", + "source_property_name": "", + "target_property_name": "#visible" + } + ] + """; + + private final String TEMPLATE_DEFAULT_CONTROLS = """ + [ + { + "border": { + "type": "image", + "texture": "textures/ui/tooltip_default_background", + "color": [0.3, 0.0, 1.0], + "layer": 0, + "anchor_from": "top_left", + "anchor_to": "top_left", + "size": ["100%sm", "100%sm"], + "controls": [ + { + "fill": { + "type": "image", + "texture": "textures/ui/tooltip_default_background", + "color": [0.235, 0.0, 0.235], + "alpha": 0.94, + "layer": 1, + "anchor_from": "center", + "anchor_to": "center", + "size": ["100% - 6px", "100% - 6px"] + } + } + ] + } + }, + { + "content": { + "type": "panel", + "layer": 3, + "size": ["100%c + 12px", "100%c + 8px"], + "anchor_from": "top_left", + "anchor_to": "top_left", + "controls": [ + { + "tooltip_text": { + "type": "label", + "text": "#text", + "size": ["default", "default"], + "anchor_from": "top_left", + "anchor_to": "top_left", + "offset": [6, 4], + "bindings": [ + { + "binding_name": "$hover_text_binding_name", + "binding_name_override": "#hover_text", + "binding_type": "collection", + "binding_collection_name": "$item_collection_name" + }, + { + "binding_type": "view", + "source_property_name": "", + "target_property_name": "#text" + } + ] + } + } + ] + } + } + ] + """; + + private final String TEMPLATE_CONTROLS_BACKGROUND_TOOLTIP = """ + { + "type": "image", + "texture": "", + "nineslice_size": [9, 23, 9, 18], + "layer": 0, + "anchor_from": "top_left", + "anchor_to": "top_left", + "size": ["100%sm + 10px", "100%sm + 14px"] + } + """; + + private final String TEMPLATE_CONTROLS_FRAME_TOOLTIP = """ + { + "type": "image", + "texture": "", + "uv": [0, 0], + "uv_size": [, ], + "nineslice_size": 20, + "layer": 1, + "anchor_from": "center", + "anchor_to": "center", + "size": ["100%", "100%"] + } + """; + + private final String TEMPLATE_CONTROLS_CONTENT = """ + { + "type": "panel", + "layer": 3, + "size": ["100%c + 12px", "100%c + 8px"], + "offset": [5, 7], + "anchor_from": "top_left", + "anchor_to": "top_left", + "controls": [ + { + "tooltip_text": { + "type": "label", + "text": "#text", + "size": ["default", "default"], + "anchor_from": "top_left", + "anchor_to": "top_left", + "offset": [6, 4], + "bindings": [ + { + "binding_name": "$hover_text_binding_name", + "binding_name_override": "#hover_text", + "binding_type": "collection", + "binding_collection_name": "$item_collection_name" + }, + { + "binding_type": "view", + "source_property_name": "(#hover_text - '')", + "target_property_name": "#text" + } + ] + } + } + ] + } + """; + + private void handleTooltipDetection(Path javaRootPath, + Map tooltipResources, + JsonObject root, + String namespace, + String element) throws IOException { + JsonObject elementTooltip = JsonUtils.parseJson(TEMPLATE_TOOLTIP); + JsonArray controls = new JsonArray(); + + for (ResolvedTooltipResource resource : tooltipResources.values()) { + Optional glyph = RiftRegistry.GLYPHS.get(resource.resourceId()); + + if (glyph.isEmpty() && !resource.resourceId().equals("default")) { + RiftLog.GLYPH_NOT_FOUND.send(resource.resourceId()); + continue; + } + + JsonObject tooltip = JsonUtils.parseJson(TEMPLATE_BASE_TOOLTIP); + + // * création de l'object binding + if (resource.getKey().equals("default")) { + String condition = "(#hover_text = #hover_text)"; + + for (ResolvedTooltipResource resourceCondition : tooltipResources.values()) { + Optional glyphCondition = RiftRegistry.GLYPHS.get(resourceCondition.resourceId()); + + if (glyphCondition.isEmpty()) { + RiftLog.GLYPH_NOT_FOUND.send(resourceCondition.resourceId()); + continue; + } + + String character = glyphCondition.get().getBedrockCharString(); + String marker = "'" + character + "'"; + condition = "(" + condition + + " and ((#hover_text - " + marker + + ") = #hover_text))"; + } + + tooltip.add("bindings", JsonUtils.parseJsonArray( + TEMPLATE_BINDINGS.replace("", condition))); + } else { + tooltip.add("bindings", JsonUtils.parseJsonArray(TEMPLATE_BINDINGS.replace("", + "(not ((#hover_text - '" + glyph.get().getBedrockCharString() + "') = #hover_text))"))); + } + + // * création de l'object controls + if (resource.getKey().equals("default")) { + String expression = "#hover_text"; + + for (ResolvedTooltipResource resourceCondition : tooltipResources.values()) { + Optional glyphCondition = RiftRegistry.GLYPHS.get(resourceCondition.resourceId()); + + if (glyphCondition.isEmpty()) { + RiftLog.GLYPH_NOT_FOUND.send(resourceCondition.resourceId()); + continue; + } + + String character = glyphCondition.get().getBedrockCharString(); + + expression = "(" + expression + " - '" + + character + "')"; + + tooltip.add("controls", JsonUtils.parseJsonArray(TEMPLATE_DEFAULT_CONTROLS.replace("", + expression))); + } + } else { + ResolvedTooltipResource.FlipbookAnimation animation = resource.getFrameFlipbookAnimation(animationMcMetaResolver, guiMcMetaResolver, javaRootPath); + JsonArray controlsTooltip = new JsonArray(); + + // * écriture du background + JsonObject backgroundResource = new JsonObject(); + JsonObject inBackgroundResource = JsonUtils.parseJson(TEMPLATE_CONTROLS_BACKGROUND_TOOLTIP + .replace("", resource.backgroundImageBedrockPath(javaRootPath).toString())); + + String width = "0"; + String height = "0"; + if (animation != null) { + width = String.valueOf(animation.widthFrame()); + height = String.valueOf(animation.heightFrame()); + } else { + RiftLog.FLIPBOOK_NULL.send(resource.resourceId()); + } + + JsonArray backgroundControls = new JsonArray(); + JsonObject frame = JsonUtils.parseJson(TEMPLATE_CONTROLS_FRAME_TOOLTIP + .replace("", resource.frameImageBedrockPath(javaRootPath).toString()) + .replace("", width) + .replace("", height)); + + if (animation != null) { + frame.add("anims", JsonUtils.parseJsonArray("[@"+ namespace +"." + resource.getKey() + "_frame_flipbook]")); + } + + JsonObject frameObj = new JsonObject(); + frameObj.add(resource.getKey() + "_frame", frame); + backgroundControls.add(frameObj); + inBackgroundResource.add("controls", backgroundControls); + backgroundResource.add("bg_" + resource.getKey(), inBackgroundResource); + controlsTooltip.add(backgroundResource); + + // * écriture du content + JsonObject contentResource = new JsonObject(); + JsonObject inContentResource = JsonUtils.parseJson(TEMPLATE_CONTROLS_CONTENT + .replace("", glyph.get().getBedrockCharString())); + + contentResource.add("content", inContentResource); + + controlsTooltip.add(contentResource); + + tooltip.add("controls", controlsTooltip); + } + + JsonObject control = new JsonObject(); + control.add("varient_" + resource.getKey(), tooltip); + controls.add(control); + } + + elementTooltip.add("controls", controls); + + root.add(element, elementTooltip); + } + + private final String TEMPLATE_HIGHLIGHT_SLOT_PANEL = """ + { + "type": "panel", + "controls": [ + { + "highlight@common.highlight_slot": { + "controls": [ + { + "hover_text@%s": { + "layer": 29 + } + } + ], + "bindings": [ + { + "binding_name": "#show_persistent_bundle_hover_text", + "binding_name_override": "#visible" + } + ] + } + }, + { + "white_border@common.white_border_slot": { + "bindings": [ + { + "binding_name": "#show_persistent_bundle_hover_text", + "binding_name_override": "#visible" + } + ] + } + } + ] + } + """; + + private void handleTooltipInjection(JsonObject root, String namespace, String element) { + JsonObject highlightSlotPanel = JsonUtils.parseJson( + TEMPLATE_HIGHLIGHT_SLOT_PANEL.formatted(namespace + "." + element)); + root.add("highlight_slot_panel", highlightSlotPanel); + } + + /** + * "anim_type": "flip_book", + * "frame_count": 15, + * "frame_step": 100, + * "fps": 6.67, + * "reversible": false, + * "easing": "linear", + * "next": "@common.legendary_frame_flipbook" + */ + private JsonObject writeAnimation(ResolvedTooltipResource.FlipbookAnimation flipbookAnim, String animationKey, ResolvedGuiMcMeta guiMeta) { + JsonObject flipbook = new JsonObject(); + flipbook.addProperty("anim_type", "flip_book"); + flipbook.addProperty("frame_count", flipbookAnim.frameCount()); + flipbook.addProperty("frame_step", guiMeta.widthFrame()); + flipbook.addProperty("fps", flipbookAnim.fps()); + flipbook.addProperty("reversible", false); + flipbook.addProperty("easing", "linear"); + flipbook.addProperty("next", "@common." + animationKey); + return flipbook; + } +} diff --git a/src/main/java/fr/openmc/riftengine/core/converter/writers/tooltip/glyph/TooltipGlyphApplicatorWriter.java b/src/main/java/fr/openmc/riftengine/core/converter/writers/tooltip/glyph/TooltipGlyphApplicatorWriter.java new file mode 100644 index 0000000..1c558db --- /dev/null +++ b/src/main/java/fr/openmc/riftengine/core/converter/writers/tooltip/glyph/TooltipGlyphApplicatorWriter.java @@ -0,0 +1,113 @@ +package fr.openmc.riftengine.core.converter.writers.tooltip.glyph; + +import fr.openmc.core.utils.YmlUtils; +import fr.openmc.riftengine.api.scanner.items.ItemEntry; +import fr.openmc.riftengine.core.RiftRegistry; +import fr.openmc.riftengine.core.converter.writers.PackWriter; +import fr.openmc.riftengine.core.registry.glyphs.Glyph; +import fr.openmc.riftengine.core.registry.glyphs.types.TooltipGlyph; +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.Yaml; + +import java.io.IOException; +import java.io.Writer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; + +/** + /** + * Suite logique de TooltipGlyphAssignatorWriter. + * On applique le glyph sur l'item qui doit avoir un tooltip. + */ +public class TooltipGlyphApplicatorWriter implements PackWriter { + + private final List items; + private final Path itemsAdderContents; + + public TooltipGlyphApplicatorWriter(List items, Path itemsAdderContents) { + this.items = items; + this.itemsAdderContents = itemsAdderContents; + } + + @Override + public void write(Path bedrockRootPath, Path javaRootPath) throws IOException { + DumperOptions options = new DumperOptions(); + options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); + options.setDefaultScalarStyle(DumperOptions.ScalarStyle.DOUBLE_QUOTED); + options.setSplitLines(false); + options.setWidth(Integer.MAX_VALUE); + + Yaml yaml = new Yaml(options); + + Map> itemsByFile = new LinkedHashMap<>(); + + for (ItemEntry itemEntry : items) { + String sourceYml = itemEntry.getSourceYml().toString() + .replaceFirst("^plugins/ItemsAdder/contents/", ""); + + Path ymlPath = itemsAdderContents.resolve(sourceYml).normalize(); + itemsByFile.computeIfAbsent(ymlPath, _ -> new ArrayList<>()).add(itemEntry); + } + + for (Map.Entry> fileEntry : itemsByFile.entrySet()) { + Path ymlPath = fileEntry.getKey(); + + if (!Files.isRegularFile(ymlPath)) continue; + + Map root = YmlUtils.loadYml(ymlPath); + if (root == null) continue; + + Map itemsSection = YmlUtils.getMap(root, "items"); + if (itemsSection == null) continue; + + boolean modified = false; + + for (ItemEntry itemEntry : fileEntry.getValue()) { + Optional glyph = RiftRegistry.GLYPHS.get( + itemEntry.getTooltipResourceId() + ); + + if (glyph.isEmpty() || !(glyph.get() instanceof TooltipGlyph tooltipGlyph)) continue; + + Object itemObject = itemsSection.get(itemEntry.getKey()); + if (!(itemObject instanceof Map)) continue; + + Map itemMap = (Map) itemObject; + + String key = itemMap.containsKey("display_name") ? "display_name" : "name"; + + String name = YmlUtils.getString(itemMap, key, null); + if (name == null) continue; + + String glyphStr = tooltipGlyph.getBedrockCharString(); + + if (name.contains(glyphStr) || name.contains(String.valueOf(tooltipGlyph.getBedrockChar()))) continue; + + itemMap.put(key, name + glyphStr); + modified = true; + } + + if (!modified) continue; + + try (Writer writer = Files.newBufferedWriter(ymlPath)) { + yaml.dump(root, writer); + } + + String content = Files.readString(ymlPath); + + for (ItemEntry itemEntry : fileEntry.getValue()) { + Optional glyph = RiftRegistry.GLYPHS.get( + itemEntry.getTooltipResourceId() + ); + + if (glyph.isEmpty() + || !(glyph.get() instanceof TooltipGlyph)) continue; + + content = content.replaceAll("\\\\\\\\(uF[0-9A-Fa-f]{3})", "\\\\$1"); + } + + Files.writeString(ymlPath, content); + } + } +} \ No newline at end of file diff --git a/src/main/java/fr/openmc/riftengine/core/converter/writers/tooltip/glyph/TooltipGlyphAssignatorWriter.java b/src/main/java/fr/openmc/riftengine/core/converter/writers/tooltip/glyph/TooltipGlyphAssignatorWriter.java new file mode 100644 index 0000000..5dfeb45 --- /dev/null +++ b/src/main/java/fr/openmc/riftengine/core/converter/writers/tooltip/glyph/TooltipGlyphAssignatorWriter.java @@ -0,0 +1,52 @@ +package fr.openmc.riftengine.core.converter.writers.tooltip.glyph; + +import fr.openmc.riftengine.api.scanner.items.ItemEntry; +import fr.openmc.riftengine.core.RiftLog; +import fr.openmc.riftengine.core.RiftRegistry; +import fr.openmc.riftengine.core.converter.writers.PackWriter; +import fr.openmc.riftengine.core.registry.glyphs.types.TooltipGlyph; +import fr.openmc.riftengine.core.resolvers.tooltip.ResolvedTooltipResource; +import fr.openmc.riftengine.core.resolvers.tooltip.TooltipResourceResolver; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Cette classe doit être justifiée. + * Afin de detecter le tooltip sur un item dans JSON UI, il faut impérativement mettre un glyph dans l'item name + * de l'item qui doit avoir un tooltip. + */ +public class TooltipGlyphAssignatorWriter implements PackWriter { + private final List items; + private final TooltipResourceResolver resolver; + + public TooltipGlyphAssignatorWriter(List items, TooltipResourceResolver resolver) { + this.items = items; + this.resolver = resolver; + } + + @Override + public void write(Path bedrockRootPath, Path javaRootPath) throws IOException { + // todo: faire un TooltipScanner quand OpenMC sera divisé en module + Map tooltipResources = new HashMap<>(); + for (ItemEntry itemEntry : items) { + ResolvedTooltipResource resource = resolver.resolve(javaRootPath, itemEntry); + if (resource == null) continue; + + tooltipResources.put(resource.resourceId(), resource); + } + + int indexPage = 0; + for (ResolvedTooltipResource resource : tooltipResources.values()) { + int row = indexPage / 16; + int col = indexPage % 16; + RiftRegistry.GLYPHS.register(new TooltipGlyph(resource.resourceId(), row, col, resource)); + indexPage++; + } + + RiftLog.NUMBER_GLYPH_REGISTERED.send(indexPage + 1, 16*16); + } +} \ No newline at end of file diff --git a/src/main/java/fr/openmc/riftengine/core/converter/writers/tooltip/textures/FrameTextureConverter.java b/src/main/java/fr/openmc/riftengine/core/converter/writers/tooltip/textures/FrameTextureConverter.java new file mode 100644 index 0000000..c81e5f1 --- /dev/null +++ b/src/main/java/fr/openmc/riftengine/core/converter/writers/tooltip/textures/FrameTextureConverter.java @@ -0,0 +1,54 @@ +package fr.openmc.riftengine.core.converter.writers.tooltip.textures; + +import fr.openmc.riftengine.core.resolvers.mcmeta.gui.ResolvedGuiMcMeta; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +/** + * Compense un problème, les animations de frames, doit etre en horizontal au lieu de vertical, on change d'orientation puis + * on copie la texture ou elle doit etre + */ +public class FrameTextureConverter { + public static void convert(ResolvedGuiMcMeta gui, + Path source, + Path destination) throws IOException { + BufferedImage src = ImageIO.read(source.toFile()); + + int width = src.getWidth(); + int height = src.getHeight(); + + int realWidth = gui.widthFrame() != null ? gui.widthFrame() : + (gui.heightFrame() != null ? gui.heightFrame() : Math.min(width, height)); + int realHeight = gui.heightFrame() != null ? gui.heightFrame() : + (gui.widthFrame() != null ? gui.widthFrame() : Math.min(width, height)); + + int cols = width / realWidth; + int numberFrame = cols * (height / realHeight); + + BufferedImage out = new BufferedImage(realWidth * numberFrame, realHeight, BufferedImage.TYPE_INT_ARGB); + Graphics2D graphics = out.createGraphics(); + try { + for (int i = 0; i < numberFrame; i++) { + graphics.drawImage(src.getSubimage( + (i % cols) * realWidth, + (i / cols) * realHeight, + realWidth, + realHeight), + i * realWidth, + 0, + null); + } + } finally { + graphics.dispose(); + } + + if (destination.getParent() != null) + Files.createDirectories(destination.getParent()); + ImageIO.write(out, "png", destination.toFile()); + } +} diff --git a/src/main/java/fr/openmc/riftengine/core/converter/writers/tooltip/textures/TooltipTextureWriter.java b/src/main/java/fr/openmc/riftengine/core/converter/writers/tooltip/textures/TooltipTextureWriter.java new file mode 100644 index 0000000..b53c515 --- /dev/null +++ b/src/main/java/fr/openmc/riftengine/core/converter/writers/tooltip/textures/TooltipTextureWriter.java @@ -0,0 +1,81 @@ +package fr.openmc.riftengine.core.converter.writers.tooltip.textures; + +import fr.openmc.riftengine.api.scanner.items.ItemEntry; +import fr.openmc.riftengine.core.RiftLog; +import fr.openmc.riftengine.core.converter.writers.PackWriter; +import fr.openmc.riftengine.core.resolvers.mcmeta.gui.GuiMcMetaResolver; +import fr.openmc.riftengine.core.resolvers.mcmeta.gui.ResolvedGuiMcMeta; +import fr.openmc.riftengine.core.resolvers.tooltip.ResolvedTooltipResource; +import fr.openmc.riftengine.core.resolvers.tooltip.TooltipResourceResolver; +import fr.openmc.riftengine.core.utils.IdentifierUtils; + +import java.io.IOException; +import java.nio.file.Path; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + + +// - ecrire les textures dans le resourcepack bedrock +// * faire un scanner pour savoir les frames, fps, et tt chose qui sont relié entre les textures et ui_common.json +// - allouer un glyph pour chaque "type" de tooltip + le mettre dans l'item name + demander reload ia +// - ecrire le ui/ui_common.json (une giga template que j'adapte en fonction des textures) +public class TooltipTextureWriter implements PackWriter { + private final List items; + private final GuiMcMetaResolver guiResolver; + private final TooltipResourceResolver resolver; + + public TooltipTextureWriter(List items, + GuiMcMetaResolver guiResolver, + TooltipResourceResolver resolver) { + this.items = items; + this.guiResolver = guiResolver; + this.resolver = resolver; + } + + @Override + public void write(Path bedrockRootPath, Path javaRootPath) throws IOException { + // todo: faire un TooltipScanner quand OpenMC sera divisé en module + Map tooltipResources = new HashMap<>(); + for (ItemEntry itemEntry : items) { + ResolvedTooltipResource resource = resolver.resolve(javaRootPath, itemEntry); + if (resource == null) continue; + + tooltipResources.put(resource.resourceId(), resource); + } + + for (ResolvedTooltipResource resource : tooltipResources.values()) { + Path backgroundImagePath = resource.backgroundImagePath(javaRootPath); + Path frameImagePath = resource.frameImagePath(javaRootPath); + + Path reducedBackgroundImagePath = resource.backgroundImageBedrockPath(javaRootPath); + Path reducedFrameImagePath = resource.frameImageBedrockPath(javaRootPath); + + if (reducedBackgroundImagePath == null) { + RiftLog.PATH_CANT_REDUCED_TO_TEXTURES.send(backgroundImagePath.toString()); + return; + } + + this.handleTextureUnanimated(bedrockRootPath, backgroundImagePath, reducedBackgroundImagePath); + + ResolvedGuiMcMeta resolvedGuiMcMeta = guiResolver.resolve(javaRootPath, resource.frameMCMetaPath(javaRootPath)); + if (resolvedGuiMcMeta == null) { + this.handleTextureUnanimated(bedrockRootPath, frameImagePath, reducedFrameImagePath); + } else { + this.handleTextureAnimated(bedrockRootPath, resolvedGuiMcMeta, frameImagePath, reducedFrameImagePath); + } + } + } + + private void handleTextureUnanimated(Path bedrockRootPath, Path imagePath, Path reducedImagePath) throws IOException { + this.copyFile(imagePath, bedrockRootPath.resolve(IdentifierUtils.toBedrockTexturePath(reducedImagePath.toString()))); + } + + private void handleTextureAnimated(Path bedrockRootPath, ResolvedGuiMcMeta resolvedGuiMcMeta, Path imagePath, Path reducedImagePath) throws IOException { + FrameTextureConverter.convert( + resolvedGuiMcMeta, + imagePath, + bedrockRootPath.resolve( IdentifierUtils.toBedrockTexturePath(reducedImagePath.toString())) + ); + } +} \ No newline at end of file diff --git a/src/main/java/fr/openmc/riftengine/core/listeners/LoadAfterItemsAdderListener.java b/src/main/java/fr/openmc/riftengine/core/listeners/LoadAfterItemsAdderListener.java index 5e762a1..ee7d4b0 100644 --- a/src/main/java/fr/openmc/riftengine/core/listeners/LoadAfterItemsAdderListener.java +++ b/src/main/java/fr/openmc/riftengine/core/listeners/LoadAfterItemsAdderListener.java @@ -1,19 +1,22 @@ package fr.openmc.riftengine.core.listeners; import dev.lone.itemsadder.api.Events.ItemsAdderLoadDataEvent; +import fr.openmc.riftengine.core.RiftPlugin; +import org.bukkit.Bukkit; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; -import java.util.ArrayList; -import java.util.List; - public class LoadAfterItemsAdderListener implements Listener { - public static List actionToLaunch = new ArrayList<>(); + public static boolean reloadToLaunch = false; @EventHandler public void onItemsAdderLoadFinish(ItemsAdderLoadDataEvent event) { - actionToLaunch.forEach(Runnable::run); - actionToLaunch.clear(); + if (reloadToLaunch) { + Bukkit.getScheduler().runTask(RiftPlugin.getInstance(), () -> + Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "iazip") + ); + reloadToLaunch = false; + } } } diff --git a/src/main/java/fr/openmc/riftengine/core/registry/glyphs/Glyph.java b/src/main/java/fr/openmc/riftengine/core/registry/glyphs/Glyph.java index e4fd784..10cdfcf 100644 --- a/src/main/java/fr/openmc/riftengine/core/registry/glyphs/Glyph.java +++ b/src/main/java/fr/openmc/riftengine/core/registry/glyphs/Glyph.java @@ -19,6 +19,11 @@ public Glyph(String namespacedId, String page, int row, int col) { this.bedrockChar = buildBedrockChar(); } + public String getBedrockCharString() { + return String.format("\\u%04X", + (int) this.getBedrockChar()); + } + private char buildBedrockChar() { int page = Integer.parseInt(this.page, 16); return (char) ((page << 8) | (row * 16 + col)); diff --git a/src/main/java/fr/openmc/riftengine/core/registry/glyphs/GlyphsRegistry.java b/src/main/java/fr/openmc/riftengine/core/registry/glyphs/GlyphsRegistry.java index f6de6d5..56e82fb 100644 --- a/src/main/java/fr/openmc/riftengine/core/registry/glyphs/GlyphsRegistry.java +++ b/src/main/java/fr/openmc/riftengine/core/registry/glyphs/GlyphsRegistry.java @@ -13,7 +13,8 @@ public class GlyphsRegistry extends Registry private final List SLOT_GLYPH = new ArrayList<>(List.of( "E2", "E3", "E4", "E5", "E6", "E7", "E8", "E9", "EA", "EB", "EC", "ED", "EE", "EF", - "F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7", "F8")); + "F0", "F1", "F2", "F3", "F4", "F5", "F6", "F7")); + public static final String TOOLTIP_PAGE = "F8"; private int nextPageIndex = 0; public String nextGlyphPage() { diff --git a/src/main/java/fr/openmc/riftengine/core/registry/glyphs/types/TooltipGlyph.java b/src/main/java/fr/openmc/riftengine/core/registry/glyphs/types/TooltipGlyph.java new file mode 100644 index 0000000..25b12e8 --- /dev/null +++ b/src/main/java/fr/openmc/riftengine/core/registry/glyphs/types/TooltipGlyph.java @@ -0,0 +1,16 @@ +package fr.openmc.riftengine.core.registry.glyphs.types; + +import fr.openmc.riftengine.core.registry.glyphs.Glyph; +import fr.openmc.riftengine.core.registry.glyphs.GlyphsRegistry; +import fr.openmc.riftengine.core.resolvers.tooltip.ResolvedTooltipResource; +import lombok.Getter; + +@Getter +public class TooltipGlyph extends Glyph { + private final ResolvedTooltipResource tooltipResource; + + public TooltipGlyph(String namespacedId, int row, int col, ResolvedTooltipResource tooltipResource) { + super(namespacedId, GlyphsRegistry.TOOLTIP_PAGE, row, col); + this.tooltipResource = tooltipResource; + } +} \ No newline at end of file diff --git a/src/main/java/fr/openmc/riftengine/core/registry/resolver/ResolverRegistry.java b/src/main/java/fr/openmc/riftengine/core/registry/resolver/ResolverRegistry.java index 9fe82af..218e814 100644 --- a/src/main/java/fr/openmc/riftengine/core/registry/resolver/ResolverRegistry.java +++ b/src/main/java/fr/openmc/riftengine/core/registry/resolver/ResolverRegistry.java @@ -3,12 +3,20 @@ import fr.openmc.core.bootstrap.registries.KeyedRegistry; import fr.openmc.core.bootstrap.registries.Registry; import fr.openmc.riftengine.core.resolvers.items.ItemResourceResolver; +import fr.openmc.riftengine.core.resolvers.mcmeta.animation.AnimationMcMetaResolver; +import fr.openmc.riftengine.core.resolvers.mcmeta.gui.GuiMcMetaResolver; +import fr.openmc.riftengine.core.resolvers.tooltip.TooltipResourceResolver; public class ResolverRegistry extends Registry> implements KeyedRegistry> { public final ItemResourceResolver ITEM_RESOURCE_RESOLVER = register(new ItemResourceResolver()); + public final TooltipResourceResolver TOOLTIP_RESOURCE_RESOLVER = register(new TooltipResourceResolver()); + // todo: https://minecraft.wiki/w/Mcmeta faire les autres types de mcmeta si nécessaire + public final AnimationMcMetaResolver ANIMATION_MCMETA_RESOLVER = register(new AnimationMcMetaResolver()); + public final GuiMcMetaResolver GUI_MCMETA_RESOLVER = register(new GuiMcMetaResolver()); + @Override public String key(AbstractResolver resolver) { return resolver.getClass().getSimpleName(); diff --git a/src/main/java/fr/openmc/riftengine/core/resolvers/items/ItemResourceResolver.java b/src/main/java/fr/openmc/riftengine/core/resolvers/items/ItemResourceResolver.java index c6138a9..01078be 100644 --- a/src/main/java/fr/openmc/riftengine/core/resolvers/items/ItemResourceResolver.java +++ b/src/main/java/fr/openmc/riftengine/core/resolvers/items/ItemResourceResolver.java @@ -53,7 +53,7 @@ public List resolve(Path javaRootPath, ItemEntry item) { Path reducedPath = PathUtils.getPathFromRoot(resourcePath, "textures"); if (reducedPath != null) { - bedrockTextureValue = IdentifierUtils.toBedrockTexturePath(reducedPath.toString()) + bedrockTextureValue = IdentifierUtils.toBedrockTexturePath(reducedPath.toString()).toString() .replaceFirst("\\.[^/.]+$", ""); } } diff --git a/src/main/java/fr/openmc/riftengine/core/resolvers/mcmeta/animation/AnimationMcMetaResolver.java b/src/main/java/fr/openmc/riftengine/core/resolvers/mcmeta/animation/AnimationMcMetaResolver.java new file mode 100644 index 0000000..3b0660a --- /dev/null +++ b/src/main/java/fr/openmc/riftengine/core/resolvers/mcmeta/animation/AnimationMcMetaResolver.java @@ -0,0 +1,13 @@ +package fr.openmc.riftengine.core.resolvers.mcmeta.animation; + +import fr.openmc.riftengine.core.registry.resolver.AbstractResolver; + +import java.nio.file.Path; + + +public class AnimationMcMetaResolver extends AbstractResolver { + @Override + public ResolvedAnimationMcMeta resolve(Path javaRootPath, Path mcmeta) { + return ResolvedAnimationMcMeta.read(mcmeta); + } +} \ No newline at end of file diff --git a/src/main/java/fr/openmc/riftengine/core/resolvers/mcmeta/animation/ResolvedAnimationMcMeta.java b/src/main/java/fr/openmc/riftengine/core/resolvers/mcmeta/animation/ResolvedAnimationMcMeta.java new file mode 100644 index 0000000..56f4cab --- /dev/null +++ b/src/main/java/fr/openmc/riftengine/core/resolvers/mcmeta/animation/ResolvedAnimationMcMeta.java @@ -0,0 +1,61 @@ +package fr.openmc.riftengine.core.resolvers.mcmeta.animation; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import fr.openmc.core.utils.JsonUtils; +import fr.openmc.riftengine.core.RiftLog; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.List; + +/** + * basé sur ... + */ +public record ResolvedAnimationMcMeta( + boolean interpolate, + Integer widthFrame, // taille en px d'une frame + Integer heightFrame, // hauteur en px d'une frame + int frameTime, // en ticks + List frames, + List frameTimes +) { + public static ResolvedAnimationMcMeta read(Path mcmetaPath) { + JsonObject root = JsonUtils.loadJson(mcmetaPath); + + if (root == null) { + RiftLog.ERROR_READING.send(mcmetaPath); + return null; + } + + JsonElement animation = root.get("animation"); + if (animation == null || !animation.isJsonObject()) { + RiftLog.NULL.send("animation"); + return null; + } + + JsonObject anim = root.getAsJsonObject("animation"); + + int frametime = Math.max(1, JsonUtils.getInt(anim, "frametime", 1)); + + List frames = new ArrayList<>(); + List frameTimes = new ArrayList<>(); + if (anim.has("frames") && anim.get("frames").isJsonArray()) { + for (JsonElement element : anim.getAsJsonArray("frames")) { + if (element.isJsonObject()) { + JsonObject f = element.getAsJsonObject(); + frames.add(JsonUtils.getInt(f, "index", 0)); + frameTimes.add(Math.max(1, JsonUtils.getInt(f, "time", frametime))); + } + } + } + + return new ResolvedAnimationMcMeta( + anim.has("interpolate") && anim.get("interpolate").getAsBoolean(), + JsonUtils.getInt(anim, "width", 0), + JsonUtils.getInt(anim, "height", 0), + frametime, + frames, + frameTimes); + } +} diff --git a/src/main/java/fr/openmc/riftengine/core/resolvers/mcmeta/gui/GuiMcMetaResolver.java b/src/main/java/fr/openmc/riftengine/core/resolvers/mcmeta/gui/GuiMcMetaResolver.java new file mode 100644 index 0000000..74f7eba --- /dev/null +++ b/src/main/java/fr/openmc/riftengine/core/resolvers/mcmeta/gui/GuiMcMetaResolver.java @@ -0,0 +1,12 @@ +package fr.openmc.riftengine.core.resolvers.mcmeta.gui; + +import fr.openmc.riftengine.core.registry.resolver.AbstractResolver; + +import java.nio.file.Path; + +public class GuiMcMetaResolver extends AbstractResolver { + @Override + public ResolvedGuiMcMeta resolve(Path javaRootPath, Path mcmeta) { + return ResolvedGuiMcMeta.read(mcmeta); + } +} \ No newline at end of file diff --git a/src/main/java/fr/openmc/riftengine/core/resolvers/mcmeta/gui/ResolvedGuiMcMeta.java b/src/main/java/fr/openmc/riftengine/core/resolvers/mcmeta/gui/ResolvedGuiMcMeta.java new file mode 100644 index 0000000..dfa2585 --- /dev/null +++ b/src/main/java/fr/openmc/riftengine/core/resolvers/mcmeta/gui/ResolvedGuiMcMeta.java @@ -0,0 +1,70 @@ +package fr.openmc.riftengine.core.resolvers.mcmeta.gui; + +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import fr.openmc.core.utils.JsonUtils; +import fr.openmc.riftengine.core.RiftLog; + +import java.nio.file.Path; + +/** + * basé sur ... + */ +public record ResolvedGuiMcMeta( + String scallingType, // entre stretch (default), tile, ou nine_slice + Integer widthFrame, // taille en px d'une frame + Integer heightFrame, // hauteur en px d'une frame + boolean stretchInner, + Border border +) { + private record Border(Integer left, Integer right, Integer top, Integer bottom) { + public Border(Integer border) { + this(border, border, border, border); + } + } + public static ResolvedGuiMcMeta read(Path mcmetaPath) { + JsonObject root = JsonUtils.loadJson(mcmetaPath); + + if (root == null) { + RiftLog.ERROR_READING.send(mcmetaPath); + return null; + } + + JsonElement gui = root.get("gui"); + if (gui == null || !gui.isJsonObject()) { + RiftLog.NULL.send("gui"); + return null; + } + + JsonElement scaling = gui.getAsJsonObject().get("scaling"); + if (scaling == null || !scaling.isJsonObject()) { + RiftLog.NULL.send("scaling"); + return null; + } + + JsonObject scalingObj = scaling.getAsJsonObject(); + + Border border = null; + + JsonElement borderElt = scalingObj.get("border"); + if (borderElt.isJsonObject()) { + JsonObject borderObj = borderElt.getAsJsonObject(); + border = new Border( + JsonUtils.getInt(borderObj, "left", null), + JsonUtils.getInt(borderObj, "right", null), + JsonUtils.getInt(borderObj, "top", null), + JsonUtils.getInt(borderObj, "bottom", null) + ); + } else if (borderElt.isJsonPrimitive()) { + border = new Border(JsonUtils.getInt(scalingObj, "border", null)); + } + + return new ResolvedGuiMcMeta( + JsonUtils.getString(scalingObj, "type", "stretch"), + JsonUtils.getInt(scalingObj, "width", null), + JsonUtils.getInt(scalingObj, "height", null), + JsonUtils.getBool(scalingObj, "stretch_inner", false), + border + ); + } +} diff --git a/src/main/java/fr/openmc/riftengine/core/resolvers/tooltip/ResolvedTooltipResource.java b/src/main/java/fr/openmc/riftengine/core/resolvers/tooltip/ResolvedTooltipResource.java new file mode 100644 index 0000000..eae388a --- /dev/null +++ b/src/main/java/fr/openmc/riftengine/core/resolvers/tooltip/ResolvedTooltipResource.java @@ -0,0 +1,117 @@ +package fr.openmc.riftengine.core.resolvers.tooltip; + +import fr.openmc.riftengine.api.utils.IdentifierUtils; +import fr.openmc.riftengine.core.resolvers.mcmeta.animation.AnimationMcMetaResolver; +import fr.openmc.riftengine.core.resolvers.mcmeta.animation.ResolvedAnimationMcMeta; +import fr.openmc.riftengine.core.resolvers.mcmeta.gui.GuiMcMetaResolver; +import fr.openmc.riftengine.core.resolvers.mcmeta.gui.ResolvedGuiMcMeta; +import fr.openmc.riftengine.core.utils.PathUtils; + +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.nio.file.Path; + +public record ResolvedTooltipResource( + String resourceId // ex omc_tooltips:tooltip/legendary +) { + public String getKey() { + String keySplit = IdentifierUtils.normalizeId(resourceId).split(":")[1]; + String[] keySplit2 = keySplit.split("/"); + return keySplit2[keySplit2.length - 1]; + } + + public String backgroundResourceId() { + return resourceId + "_background"; + } + + public String frameResourceId() { + return resourceId + "_frame"; + } + + private String normalizeTooltipId(String id) { + String normalized = IdentifierUtils.normalizeId(id); + String[] keySplit = normalized.split(":"); + String key = "gui/sprites/tooltip/" + keySplit[1]; + + return keySplit[0] + ":" + key; + } + + public Path backgroundImagePath(Path javaRoot) { + Path path = IdentifierUtils.resolveTextureId(javaRoot, IdentifierUtils.normalizeId(backgroundResourceId())); + + if (path.toFile().exists()) + return path; + + return IdentifierUtils.resolveTextureId(javaRoot, normalizeTooltipId(backgroundResourceId())); + } + + public Path frameImagePath(Path javaRoot) { + Path path = IdentifierUtils.resolveTextureId(javaRoot, IdentifierUtils.normalizeId(frameResourceId())); + + if (path.toFile().exists()) + return path; + + return IdentifierUtils.resolveTextureId(javaRoot, normalizeTooltipId(frameResourceId())); + } + + public Path backgroundMCMetaPath(Path javaRoot) { + Path path = IdentifierUtils.resolveMetaId(javaRoot, IdentifierUtils.normalizeId(backgroundResourceId()), ".png"); + + if (path.toFile().exists()) + return path; + + return IdentifierUtils.resolveMetaId(javaRoot, normalizeTooltipId(backgroundResourceId()), ".png"); + } + + public Path frameMCMetaPath(Path javaRoot) { + Path path = IdentifierUtils.resolveMetaId(javaRoot, IdentifierUtils.normalizeId(frameResourceId()), ".png"); + + if (path.toFile().exists()) + return path; + + return IdentifierUtils.resolveMetaId(javaRoot, normalizeTooltipId(frameResourceId()), ".png"); + } + + public Path backgroundImageBedrockPath(Path javaRootPath) { + return PathUtils.getPathFromRoot(backgroundImagePath(javaRootPath), "textures"); + } + + public Path frameImageBedrockPath(Path javaRootPath) { + return PathUtils.getPathFromRoot(frameImagePath(javaRootPath), "textures"); + } + + + // todo: sacré bordel, surement faier un record Animationinfo nn? + public int numberFrame(Path javaRootPath, ResolvedGuiMcMeta gui) throws IOException { + Path frameImagePath = this.frameImagePath(javaRootPath); + BufferedImage src = ImageIO.read(frameImagePath.toFile()); + + int width = src.getWidth(); + int height = src.getHeight(); + + int realWidth = gui.widthFrame() != null ? gui.widthFrame() : + (gui.heightFrame() != null ? gui.heightFrame() : Math.min(width, height)); + int realHeight = gui.heightFrame() != null ? gui.heightFrame() : + (gui.widthFrame() != null ? gui.widthFrame() : Math.min(width, height)); + + int cols = width / realWidth; + return cols * (height / realHeight); + } + + public FlipbookAnimation getFrameFlipbookAnimation(AnimationMcMetaResolver animationMcMetaResolver, GuiMcMetaResolver guiMcMetaResolver, Path javaRootPath) throws IOException { + ResolvedGuiMcMeta guiMeta = guiMcMetaResolver.resolve(javaRootPath, frameMCMetaPath(javaRootPath)); + ResolvedAnimationMcMeta animationMeta = animationMcMetaResolver.resolve(javaRootPath, frameMCMetaPath(javaRootPath)); + + if (guiMeta == null) + return null; + + Double fps = animationMeta == null ? null : (20d / animationMeta.frameTime()); + return new FlipbookAnimation(this.numberFrame(javaRootPath, guiMeta), + guiMeta.heightFrame(), + guiMeta.widthFrame(), + fps); + } + + public record FlipbookAnimation(int frameCount, int heightFrame, int widthFrame, Double fps) {} +} diff --git a/src/main/java/fr/openmc/riftengine/core/resolvers/tooltip/TooltipResourceResolver.java b/src/main/java/fr/openmc/riftengine/core/resolvers/tooltip/TooltipResourceResolver.java new file mode 100644 index 0000000..3ff0178 --- /dev/null +++ b/src/main/java/fr/openmc/riftengine/core/resolvers/tooltip/TooltipResourceResolver.java @@ -0,0 +1,17 @@ +package fr.openmc.riftengine.core.resolvers.tooltip; + +import fr.openmc.riftengine.api.scanner.items.ItemEntry; +import fr.openmc.riftengine.core.registry.resolver.AbstractResolver; + +import java.nio.file.Path; + +public class TooltipResourceResolver extends AbstractResolver { + @Override + public ResolvedTooltipResource resolve(Path javaRootPath, ItemEntry item) { + String tooltipStyle = item.getTooltipResourceId(); + + if (tooltipStyle == null) return null; + + return new ResolvedTooltipResource(tooltipStyle); + } +}