diff --git a/README.md b/README.md index 11d18767..0e70b6af 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,7 @@ If you want to download the jar file to run this, just download in [releases](ht or alternatively in [actions](https://github.com/oryxel1/Base/actions) sections. ## Usage and examples -Download **Java 21** or above, and then run `java -jar Base.jar` (or whatever the jar file is called). +Download **Java 25** or above, and then run `java -jar Base.jar` (or whatever the jar file is called).

For example of how to use the program, you can look at the [wiki](https://github.com/oryxel1/Base/wiki). ## Contact diff --git a/build.gradle.kts b/build.gradle.kts index 6e6c897a..4b13acd4 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -27,15 +27,4 @@ subprojects { implementation(rootProject.libs.google.gson) implementation(rootProject.libs.configurate.yaml) } -} -// -//dependencies { -// implementation(project(":editor")) -// implementation(project(":core")) -// implementation(project(":api")) -// -// implementation(libs.gdx.base) -// implementation(libs.commons.core) -// -// implementation(libs.imgui.binding) -//} \ No newline at end of file +} \ No newline at end of file diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 2e5f52bf..0ac5c464 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -7,20 +7,18 @@ var lwjgl = "3.3.6" var lwjglStd = "3.4.0" var libGdx = "1.13.1" +repositories { + maven("https://jitpack.io") +} + dependencies { api(project(":api")) api(project(":serializer")) - api(variantOf(libs.raphimc.thingl) { classifier("java17") }) + api(libs.raphimc.thingl) api(libs.commons.animations) api(libs.unnamed.mocha) - api(libs.imgui.lwjgl3) - api(libs.imgui.binding) - api(libs.imgui.natives.windows) - api(libs.imgui.natives.linux) - api(libs.imgui.natives.macos) - api(libs.lwjgl.base) api(libs.lwjgl.opengl) api(libs.lwjgl.stb) @@ -42,6 +40,8 @@ dependencies { api(libs.jcraft.jorbis) api(libs.javazoom.jlayer) + api(libs.weisj.jsvg) + listOf("natives-windows", "natives-windows-arm64", "natives-linux", "natives-linux-arm64").forEach { api("org.lwjgl:lwjgl:$lwjgl:$it") api("org.lwjgl:lwjgl-glfw:$lwjgl:$it") @@ -57,4 +57,9 @@ dependencies { } api("com.badlogicgames.gdx:gdx-platform:$libGdx:natives-desktop") -} \ No newline at end of file + + api(libs.rivet.core) + api(libs.rivet.thingl.backend) { + exclude("org.lwjgl") + } +} diff --git a/core/src/main/java/oxy/bascenario/EngineRenderer.java b/core/src/main/java/oxy/bascenario/EngineRenderer.java index 1cea0eab..aeb25497 100644 --- a/core/src/main/java/oxy/bascenario/EngineRenderer.java +++ b/core/src/main/java/oxy/bascenario/EngineRenderer.java @@ -6,37 +6,51 @@ import com.badlogic.gdx.Screen; import com.badlogic.gdx.backends.lwjgl3.Lwjgl3Graphics; import com.badlogic.gdx.utils.ScreenUtils; -import imgui.ImGui; -import imgui.ImGuiIO; -import imgui.extension.implot.ImPlot; -import imgui.flag.ImGuiConfigFlags; -import imgui.flag.ImGuiKey; -import imgui.gl3.ImGuiImplGl3; -import imgui.glfw.ImGuiImplGlfw; import lombok.RequiredArgsConstructor; +import lombok.SneakyThrows; +import net.lenni0451.commons.collections.Maps; +import net.lenni0451.rivet.Rivet; +import net.lenni0451.rivet.backend.render.deferred.DeferredRenderer; +import net.lenni0451.rivet.backend.render.deferred.RenderListExecutor; +import net.lenni0451.rivet.backend.thingl.ThinGLBackend; +import net.lenni0451.rivet.backend.thingl.render.ThinGLRenderer; +import net.lenni0451.rivet.backend.thingl.text.ThinGLFont; +import net.lenni0451.rivet.backend.thingl.util.GLFWMapper; +import net.lenni0451.rivet.input.keyboard.CharEvent; +import net.lenni0451.rivet.input.keyboard.KeyEvent; +import net.lenni0451.rivet.input.mouse.MouseButton; +import net.lenni0451.rivet.input.mouse.MouseButtonEvent; +import net.lenni0451.rivet.input.mouse.MouseMoveEvent; +import net.lenni0451.rivet.input.mouse.MouseScrollEvent; +import net.lenni0451.rivet.layout.fullsize.FullSizeLayout; +import net.lenni0451.rivet.math.Size; import net.raphimc.thingl.ThinGL; +import net.raphimc.thingl.resource.font.instance.FontInstance; +import net.raphimc.thingl.resource.font.instance.FontInstanceSet; +import net.raphimc.thingl.text.util.GlyphPredicate; import org.joml.Matrix4fStack; import org.lwjgl.glfw.GLFW; +import org.lwjgl.glfw.GLFWFramebufferSizeCallback; import oxy.bascenario.managers.AudioManager; -import oxy.bascenario.screens.ScenarioScreen; import oxy.bascenario.utils.*; import oxy.bascenario.utils.font.FontUtils; import oxy.bascenario.utils.thingl.ThinGLExtended; import oxy.bascenario.utils.thingl.ThinGLUtils; -import java.io.File; -import java.io.IOException; +import java.util.EnumSet; +import java.util.Set; @RequiredArgsConstructor public final class EngineRenderer extends Game { - private final ImGuiImplGlfw imGuiGlfw = new ImGuiImplGlfw(); - private final ImGuiImplGl3 imGuiGl3 = new ImGuiImplGl3(); + private ThinGLBackend backend; + private Rivet rivet; private final Screen initialScreen; private final boolean initialFullScreen; public double mouseX, mouseY; + @SneakyThrows @Override public void create() { long windowHandle = ((Lwjgl3Graphics) Gdx.graphics).getWindow().getWindowHandle(); @@ -49,14 +63,29 @@ public void create() { // GLFW.glfwSetWindowAspectRatio(windowHandle, 16, 9); GLFW.glfwSetCursorPosCallback(windowHandle, (window, x, y) -> { - if (window != windowHandle) { - return; - } - this.mouseX = x; this.mouseY = y; + + float[] mouseScale = this.getMouseScale(); + this.rivet.onMouseMove(new MouseMoveEvent((float) x * mouseScale[0], (float) y * mouseScale[1], this.heldMouseButtons)); }); GLFW.glfwSetMouseButtonCallback(windowHandle, (window, button, action, mode) -> { + float[] mouseScale = this.getMouseScale(); + final double[] xpos = new double[1]; + final double[] ypos = new double[1]; + GLFW.glfwGetCursorPos(window, xpos, ypos); + + MouseButtonEvent event = GLFWMapper.mapMouseButton((float) xpos[0] * mouseScale[0], (float) ypos[0] * mouseScale[1], button, mode); + if (event != null) { + if (action == GLFW.GLFW_PRESS) { + this.heldMouseButtons.add(event.button()); + this.rivet.onMouseDown(event); + } else if (action == GLFW.GLFW_RELEASE) { + this.rivet.onMouseUp(event); + this.heldMouseButtons.remove(event.button()); + } + } + if (window != windowHandle || !(this.screen instanceof ExtendableScreen extendableScreen)) { return; } @@ -71,25 +100,14 @@ public void create() { } }); - ImGui.createContext(); - ImPlot.createContext(); - final ImGuiIO data = ImGui.getIO(); - data.setIniFilename("base.imgui"); - data.setFontGlobalScale(1F); - data.setConfigFlags(ImGuiConfigFlags.DockingEnable); - imGuiGlfw.init(windowHandle, true); - imGuiGl3.init(); - - if (!new File("base.imgui").exists()) { - try { - ImGui.loadIniSettingsFromMemory(new String(EngineRenderer.class.getResourceAsStream("/assets/base/base.imgui").readAllBytes())); - } catch (IOException ignored) { - } - } + FontUtils.loadFonts(); - ImGuiUtils.setupTheme(); + final FontInstance rivetFont = FontUtils.font("SFUIRegular"); + this.backend = new ThinGLBackend(windowHandle, new ThinGLFont(new FontInstanceSet(Maps.linkedHashMap(rivetFont, GlyphPredicate.all())))); + this.rivet = new Rivet(this.backend, FullSizeLayout.INSTANCE, new Size(ThinGL.windowInterface().getFramebufferWidth(), ThinGL.windowInterface().getFramebufferHeight())); - FontUtils.loadFonts(); + setupRivetCallbacks(); + RivetUtil.setupTheme(rivet); this.setScreen(this.initialScreen); @@ -100,14 +118,20 @@ public void create() { @Override public void setScreen(Screen screen) { - ScenarioScreen.RENDER_WITHIN_IMGUI = !(screen instanceof ScenarioScreen); TimeUtils.fakeTimeMillis = null; super.setScreen(screen); + + this.rivet.root().clearChildren(); + if (screen instanceof ExtendableScreen extendableScreen) { + extendableScreen.init(this.rivet); + } } private boolean someTempHackyBool; private boolean fullScreen; + + private final ThinGLRenderer renderer = new ThinGLRenderer(); @Override public void render() { ScreenUtils.clear(0, 0, 0, 1, true); @@ -115,40 +139,26 @@ public void render() { AudioManager.getInstance().tick(); ThinGLUtils.GLOBAL_RENDER_STACK = new Matrix4fStack(8); - float x = ThinGL.windowInterface().getFramebufferWidth() / 1920F; - ThinGLUtils.GLOBAL_RENDER_STACK.scale(x, ThinGL.windowInterface().getFramebufferHeight() / 1080F, x); - - ImGuiUtils.COUNTER = 0; - imGuiGl3.newFrame(); - imGuiGlfw.newFrame(); - ImGui.newFrame(); - if (ImGui.getStyle().getFontScaleDpi() != 1) { - ImGui.pushFont(FontUtils.IM_FONT_REGULAR, 17); + ThinGLUtils.start(); + if (screen != null && screen instanceof ExtendableScreen extendableScreen) { + extendableScreen.renderBehindRivet(); } - super.render(); - if (ImGui.getStyle().getFontScaleDpi() != 1) { - ImGui.popFont(); - } - - ImGui.render(); + ThinGL.programs().getMsaa().bindInput(); + RenderListExecutor.INSTANCE.renderList(this.renderer, this.rivet.render(new DeferredRenderer()).complete()); + ThinGL.programs().getMsaa().unbindInput(); + ThinGL.programs().getMsaa().renderFullscreen(); + ThinGL.programs().getMsaa().clearInput(); + ThinGLUtils.end(); - imGuiGl3.renderDrawData(ImGui.getDrawData()); + ThinGLUtils.GLOBAL_RENDER_STACK = new Matrix4fStack(8); + float x = ThinGL.windowInterface().getFramebufferWidth() / 1920F; + ThinGLUtils.GLOBAL_RENDER_STACK.scale(x, ThinGL.windowInterface().getFramebufferHeight() / 1080F, x); -// if (GLFW.glfwGetWindowAttrib(((Lwjgl3Graphics) Gdx.graphics).getWindow().getWindowHandle(), GLFW.GLFW_MAXIMIZED) == 1) { -// int width = ThinGL.windowInterface().getFramebufferWidth(), height = ThinGL.windowInterface().getFramebufferHeight(); -//// System.out.println(width/(float)height); -// if (width/(float)height != 16/9f) { -// boolean widthLargerHeight = ThinGL.windowInterface().getFramebufferWidth() > ThinGL.windowInterface().getFramebufferHeight(); -// int fixedWidth = !widthLargerHeight ? width : (int) (height * 16/9f); -// int fixedHeight = !widthLargerHeight ? (int) (width * (9/16f)) : height; -// -// Gdx.graphics.setWindowedMode(fixedWidth, fixedHeight); -// } -// } + super.render(); - if (ImGui.isKeyReleased(ImGuiKey.F11) || someTempHackyBool) { + if (/*ImGui.isKeyReleased(ImGuiKey.F11) || */ someTempHackyBool) { if (this.fullScreen) { Gdx.graphics.setUndecorated(false); Gdx.graphics.setWindowedMode(1280, 720); @@ -162,17 +172,71 @@ public void render() { } } - @Override - public void resize(int width, int height) { - super.resize(width, height); + private final Set heldMouseButtons = EnumSet.noneOf(MouseButton.class); + private void setupRivetCallbacks() { + long windowHandle = ((Lwjgl3Graphics) Gdx.graphics).getWindow().getWindowHandle(); + + GLFW.glfwSetScrollCallback(windowHandle, (window, xoffset, yoffset) -> { + float[] mouseScale = this.getMouseScale(); + final double[] xpos = new double[1]; + final double[] ypos = new double[1]; + GLFW.glfwGetCursorPos(window, xpos, ypos); + this.rivet.onMouseScroll(new MouseScrollEvent((float) xpos[0] * mouseScale[0], (float) ypos[0] * mouseScale[1], (float) xoffset, (float) yoffset)); + }); + GLFW.glfwSetKeyCallback(windowHandle, (_, key, _, action, mods) -> { + KeyEvent event = GLFWMapper.mapKey(key, mods); + if (event != null) { + if (action == GLFW.GLFW_PRESS) { + this.rivet.onKeyDown(event); + } else if (action == GLFW.GLFW_RELEASE) { + this.rivet.onKeyUp(event); + } else if (action == GLFW.GLFW_REPEAT) { + this.rivet.onKeyDown(event); + } + } + }); + GLFW.glfwSetCharCallback(windowHandle, (_, codepoint) -> { + if (Character.isBmpCodePoint(codepoint)) { + this.rivet.onCharTyped(new CharEvent((char) codepoint)); + } else if (Character.isValidCodePoint(codepoint)) { + this.rivet.onCharTyped(new CharEvent(Character.highSurrogate(codepoint))); + this.rivet.onCharTyped(new CharEvent(Character.lowSurrogate(codepoint))); + } + }); + GLFWFramebufferSizeCallback[] oldCallback = new GLFWFramebufferSizeCallback[1]; + oldCallback[0] = GLFW.glfwSetFramebufferSizeCallback(windowHandle, (window, width, height) -> { + if (oldCallback[0] != null) oldCallback[0].invoke(window, width, height); + this.rivet.size(new Size(width, height)); + }); + GLFW.glfwSetWindowFocusCallback(windowHandle, (window, focused) -> { + if (!focused) { + this.heldMouseButtons.clear(); + this.rivet.unfocus(); + } + }); + } + + private float[] getMouseScale() { + long windowHandle = ((Lwjgl3Graphics) Gdx.graphics).getWindow().getWindowHandle(); + + int[] windowSizeX = new int[1]; + int[] windowSizeY = new int[1]; + GLFW.glfwGetWindowSize(windowHandle, windowSizeX, windowSizeY); + int[] framebufferSizeX = new int[1]; + int[] framebufferSizeY = new int[1]; + GLFW.glfwGetFramebufferSize(windowHandle, framebufferSizeX, framebufferSizeY); + return new float[]{(float) framebufferSizeX[0] / windowSizeX[0], (float) framebufferSizeY[0] / windowSizeY[0]}; } @Override - public void dispose() { - super.dispose(); - imGuiGlfw.shutdown(); - imGuiGl3.shutdown(); - ImPlot.destroyContext(); - ImGui.destroyContext(); + public void resize(int width, int height) { + if (width <= 1920 && height <= 1080) { + this.rivet.scale(1); + } else { + float scale = (width + height) / (1920 + 1080f); + this.rivet.scale(scale); + } + + super.resize(width, height); } } diff --git a/core/src/main/java/oxy/bascenario/managers/AssetsManager.java b/core/src/main/java/oxy/bascenario/managers/AssetsManager.java index a8eea2b4..6f1838a3 100644 --- a/core/src/main/java/oxy/bascenario/managers/AssetsManager.java +++ b/core/src/main/java/oxy/bascenario/managers/AssetsManager.java @@ -91,7 +91,7 @@ public Texture2D texture(String scenario, FileInfo info) { public Texture2D texture(String scenario, FileInfo info, boolean filter) { try { - return ((TextureAsset)assets(scenario, info, AssetType.TEXTURE).asset()).get(filter); + return ((TextureAsset)assets(scenario, info, AssetType.TEXTURE).asset()).get(info.path().endsWith(".svg"), filter); } catch (Exception ignored) { if (INVALID_TEXTURE_KEY == Integer.MIN_VALUE) { FileInfo invalidTexture = new FileInfo("assets/base/invalid.png", false, true); diff --git a/core/src/main/java/oxy/bascenario/managers/other/GifAsset.java b/core/src/main/java/oxy/bascenario/managers/other/GifAsset.java index 360195e9..1196c233 100644 --- a/core/src/main/java/oxy/bascenario/managers/other/GifAsset.java +++ b/core/src/main/java/oxy/bascenario/managers/other/GifAsset.java @@ -3,7 +3,7 @@ import lombok.RequiredArgsConstructor; import lombok.SneakyThrows; import net.raphimc.thingl.gl.texture.animated.SequencedTexture; -import net.raphimc.thingl.image.animated.impl.AwtGifImage; +import net.raphimc.thingl.resource.image.animated.impl.AwtGifImage; @RequiredArgsConstructor public class GifAsset { diff --git a/core/src/main/java/oxy/bascenario/managers/other/TextureAsset.java b/core/src/main/java/oxy/bascenario/managers/other/TextureAsset.java index ba91766b..59868667 100644 --- a/core/src/main/java/oxy/bascenario/managers/other/TextureAsset.java +++ b/core/src/main/java/oxy/bascenario/managers/other/TextureAsset.java @@ -2,7 +2,9 @@ import lombok.RequiredArgsConstructor; import net.raphimc.thingl.gl.resource.image.texture.impl.Texture2D; +import net.raphimc.thingl.resource.image.impl.AwtSvgByteImage2D; import org.lwjgl.opengl.GL20; +import oxy.bascenario.api.managers.other.AssetType; @RequiredArgsConstructor public class TextureAsset { @@ -10,8 +12,12 @@ public class TextureAsset { private Texture2D texture2D; public Texture2D get(boolean filter) { + return get(false, filter); + } + + public Texture2D get(boolean svg, boolean filter) { if (this.texture2D == null) { - this.texture2D = Texture2D.fromImage(bytes); + this.texture2D = svg ? Texture2D.fromImage(new AwtSvgByteImage2D(bytes, 1)) : Texture2D.fromImage(bytes); if (filter) { this.texture2D.setFilter(GL20.GL_NEAREST); } diff --git a/core/src/main/java/oxy/bascenario/screens/ScenarioScreen.java b/core/src/main/java/oxy/bascenario/screens/ScenarioScreen.java index 8b0efc2c..488b1c91 100644 --- a/core/src/main/java/oxy/bascenario/screens/ScenarioScreen.java +++ b/core/src/main/java/oxy/bascenario/screens/ScenarioScreen.java @@ -1,7 +1,5 @@ package oxy.bascenario.screens; -import imgui.ImGui; -import imgui.flag.ImGuiKey; import lombok.Getter; import lombok.Setter; import net.lenni0451.commons.animation.easing.EasingFunction; @@ -49,7 +47,6 @@ public class ScenarioScreen extends ScreenEffectScreen { @Setter private boolean playing = true; - public static boolean RENDER_WITHIN_IMGUI = false; private final boolean preload; public ScenarioScreen(Scenario scenario, boolean preload) { this.timestamps.addAll(scenario.getTimestamps()); @@ -321,13 +318,13 @@ public void render(float delta) { elements.removeIf(ElementRenderer::selfDestruct); elements.forEach(element -> element.getSubElements().values().removeIf(ElementRenderer::selfDestruct)); - if (ImGui.isKeyReleased(ImGuiKey.Space)) { - if (!this.dialogueRenderer.isBusy()) { - this.busyDialogue = false; - } else { - this.dialogueRenderer.finishAll(); - } - } +// if (ImGui.isKeyReleased(ImGuiKey.Space)) { +// if (!this.dialogueRenderer.isBusy()) { +// this.busyDialogue = false; +// } else { +// this.dialogueRenderer.finishAll(); +// } +// } } @Setter diff --git a/core/src/main/java/oxy/bascenario/screens/renderer/dialogue/DialogueRenderer.java b/core/src/main/java/oxy/bascenario/screens/renderer/dialogue/DialogueRenderer.java index ab0852fc..b28a033c 100644 --- a/core/src/main/java/oxy/bascenario/screens/renderer/dialogue/DialogueRenderer.java +++ b/core/src/main/java/oxy/bascenario/screens/renderer/dialogue/DialogueRenderer.java @@ -3,7 +3,7 @@ import lombok.RequiredArgsConstructor; import net.lenni0451.commons.color.Color; import net.raphimc.thingl.gl.renderer.impl.RendererText; -import net.raphimc.thingl.resource.font.Font; +import net.raphimc.thingl.resource.font.instance.FontInstance; import net.raphimc.thingl.text.TextLine; import net.raphimc.thingl.text.TextRun; import oxy.bascenario.api.Scenario; @@ -46,7 +46,7 @@ public boolean add(int index, boolean newLine, Dialogue... dialogues) { final Text text = dialogue.getDialogue(); for (final TextSegment segment : text.segments()) { - Font font = FontUtils.toFont(scenario, segment); + FontInstance font = FontUtils.toFont(scenario, segment); for (char c : segment.text().toCharArray()) { final String next = texts.get(texts.size() - 1).builder().toString() + c; @@ -83,13 +83,13 @@ public boolean add(int index, boolean newLine, Dialogue... dialogues) { return true; } - private record TextBuilder(float offset, StringBuilder builder, List> segments) { + private record TextBuilder(float offset, StringBuilder builder, List> segments) { } @RequiredArgsConstructor private static final class DialogueText { private final float offset; - private final List> allSegments; + private final List> allSegments; private final float speed; private final long time; private final long prev; @@ -137,7 +137,7 @@ public void renderDialogues() { if (lines.isEmpty() || text.newLine()) { break; } else { - for (Pair pair : text.allSegments) { + for (Pair pair : text.allSegments) { lines.getLast().allSegments.add(new TextRun(pair.right(), pair.left())); } } @@ -155,7 +155,7 @@ public void renderDialogues() { final List segments = new ArrayList<>(); int length = -1; - for (Pair pair : text.allSegments) { + for (Pair pair : text.allSegments) { if (length == words) { break; } @@ -167,7 +167,7 @@ public void renderDialogues() { } else { final TextRun last = segments.getLast(); // Probably not the best idea to do this... But who cares! - boolean same = pair.right().getFamilyName().equals(last.font().getFamilyName()) && pair.right().getSubFamilyName().equals(last.font().getSubFamilyName()); + boolean same = pair.right().getFace().getFamilyName().equals(last.font().getFace().getFamilyName()) && pair.right().getFace().getSubFamilyName().equals(last.font().getFace().getSubFamilyName()); if (same) { last.add(pair.left()); } else { @@ -177,7 +177,7 @@ public void renderDialogues() { } final List all = new ArrayList<>(); - for (Pair pair : text.allSegments) { + for (Pair pair : text.allSegments) { all.add(new TextRun(pair.right(), pair.left())); } diff --git a/core/src/main/java/oxy/bascenario/screens/renderer/element/SpriteRenderer.java b/core/src/main/java/oxy/bascenario/screens/renderer/element/SpriteRenderer.java index 60cd2c1a..ce625477 100644 --- a/core/src/main/java/oxy/bascenario/screens/renderer/element/SpriteRenderer.java +++ b/core/src/main/java/oxy/bascenario/screens/renderer/element/SpriteRenderer.java @@ -4,7 +4,6 @@ import com.badlogic.gdx.graphics.OrthographicCamera; import com.badlogic.gdx.graphics.g2d.PolygonSpriteBatch; import com.esotericsoftware.spine.*; -import imgui.ImGui; import net.raphimc.thingl.ThinGL; import net.raphimc.thingl.implementation.window.WindowInterface; import oxy.bascenario.Base; @@ -88,14 +87,14 @@ protected void render(ScenarioScreen screen) { return; } - this.queueAnimations.forEach(animation -> { - try { - state.setAnimation(animation.index, animation.animation, animation.loop); + try { + this.queueAnimations.forEach(animation -> { stateData.setDefaultMix(animation.duration); - } catch (Exception ignored) { - } - }); - this.queueAnimations.clear(); + state.setAnimation(animation.index, animation.animation, animation.loop); + }); + this.queueAnimations.clear(); + } catch (Exception ignored) { + } ThinGLUtils.end(); // Hacky, but we need to stop thingl rendering then start again later to avoid conflicts... @@ -132,7 +131,7 @@ protected void render(ScenarioScreen screen) { } if (this.overlayColor.alpha() != 0 && this.color.alpha() != 0) { - ThinGLUtils.overlayColor(() -> { + ThinGLUtils.colorTweak(() -> { this.batch.begin(); this.renderer.draw(this.batch, this.skeleton); this.batch.end(); @@ -173,20 +172,20 @@ protected void render(ScenarioScreen screen) { private void updateSkeleton(Skeleton skeleton) { final WindowInterface window = ThinGL.windowInterface(); - float width = ScenarioScreen.RENDER_WITHIN_IMGUI ? ImGui.getWindowSizeX() : window.getFramebufferWidth(), height = ScenarioScreen.RENDER_WITHIN_IMGUI ? (ImGui.getWindowSizeY() - 46) : window.getFramebufferHeight(); + float width = window.getFramebufferWidth(), height = window.getFramebufferHeight(); float x = this.position.x() + this.offset.x() + this.animationOffset.x(), y = this.position.y() + this.offset.y() + this.animationOffset.y(); float posX = (x / 1920) * width, posY = (y / 1080) * -height; - if (ScenarioScreen.RENDER_WITHIN_IMGUI) { - posX += ImGui.getWindowPosX(); - - float ratio = (0.00064814813f * height) / (0.00064814813f * window.getFramebufferHeight()); - posY /= ratio; - - posY += window.getFramebufferHeight() - height; - posY -= ImGui.getWindowPosY() + 46; - } +// if (ScenarioScreen.RENDER_WITHIN_IMGUI) { +// posX += ImGui.getWindowPosX(); +// +// float ratio = (0.00064814813f * height) / (0.00064814813f * window.getFramebufferHeight()); +// posY /= ratio; +// +// posY += window.getFramebufferHeight() - height; +// posY -= ImGui.getWindowPosY() + 46; +// } skeleton.setPosition(posX, posY); diff --git a/core/src/main/java/oxy/bascenario/screens/renderer/element/thingl/text/TextRenderer.java b/core/src/main/java/oxy/bascenario/screens/renderer/element/thingl/text/TextRenderer.java index 1139c1fd..87fad83b 100644 --- a/core/src/main/java/oxy/bascenario/screens/renderer/element/thingl/text/TextRenderer.java +++ b/core/src/main/java/oxy/bascenario/screens/renderer/element/thingl/text/TextRenderer.java @@ -3,13 +3,12 @@ import net.lenni0451.commons.color.Color; import net.raphimc.thingl.ThinGL; import net.raphimc.thingl.gl.renderer.impl.RendererText; -import net.raphimc.thingl.resource.font.Font; +import net.raphimc.thingl.resource.font.instance.FontInstance; import net.raphimc.thingl.text.TextLine; import net.raphimc.thingl.text.TextRun; import net.raphimc.thingl.text.TextSegment; import oxy.bascenario.api.Scenario; import oxy.bascenario.api.render.RenderLayer; -import oxy.bascenario.api.render.elements.text.AnimatedText; import oxy.bascenario.api.render.elements.text.Text; import oxy.bascenario.screens.renderer.element.base.ThinGLElementRenderer; import oxy.bascenario.utils.font.FontUtils; @@ -28,7 +27,7 @@ public TextRenderer(Text element, RenderLayer layer, Scenario scenario) { size = element.size(); for (oxy.bascenario.api.render.elements.text.TextSegment segment : element.segments()) { - Font font = FontUtils.toFont(scenario, segment); + FontInstance font = FontUtils.toFont(scenario, segment); line.add(new TextRun(font, new TextSegment(segment.text(), segment.color(), segment.toFlags(), segment.outline().isPresent() ? segment.outline().get() : Color.TRANSPARENT))); } } diff --git a/core/src/main/java/oxy/bascenario/utils/ExtendableScreen.java b/core/src/main/java/oxy/bascenario/utils/ExtendableScreen.java index 2ee4f67f..60f9a0fa 100644 --- a/core/src/main/java/oxy/bascenario/utils/ExtendableScreen.java +++ b/core/src/main/java/oxy/bascenario/utils/ExtendableScreen.java @@ -1,12 +1,19 @@ package oxy.bascenario.utils; import com.badlogic.gdx.Screen; +import net.lenni0451.rivet.Rivet; public class ExtendableScreen implements Screen { @Override public void show() { } + public void init(Rivet rivet) { + } + + public void renderBehindRivet() { + } + @Override public void render(float delta) { } diff --git a/core/src/main/java/oxy/bascenario/utils/ImGuiUtils.java b/core/src/main/java/oxy/bascenario/utils/ImGuiUtils.java deleted file mode 100644 index 39840d4c..00000000 --- a/core/src/main/java/oxy/bascenario/utils/ImGuiUtils.java +++ /dev/null @@ -1,234 +0,0 @@ -package oxy.bascenario.utils; - -import imgui.ImGui; -import imgui.ImGuiStyle; -import imgui.flag.ImGuiCol; -import imgui.flag.ImGuiInputTextFlags; -import imgui.type.ImFloat; -import imgui.type.ImInt; -import imgui.type.ImString; -import net.lenni0451.commons.color.Color; -import oxy.bascenario.Base; -import oxy.bascenario.api.Scenario; -import oxy.bascenario.api.utils.FileInfo; -import oxy.bascenario.utils.files.NFDUtils; -import oxy.bascenario.utils.font.FontUtils; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.List; -import java.util.function.Consumer; -import java.util.stream.Stream; - -// Just so the code can look cleaner! -public class ImGuiUtils { - public static int COUNTER = 0; - - public static void pickFolder(Consumer consumer, Scenario.Builder scenario, final String name) { - if (!ImGui.button(name + "##" + COUNTER++)) { - return; - } - - final String pick = NFDUtils.pickFolder(); - if (pick.isEmpty()) { - return; - } - - File folder = new File(pick); - try (final Stream stream = Files.walk(folder.toPath())) { - List files = stream.filter(Files::isRegularFile).map(Path::toFile).toList(); - - new Thread(() -> { - try { - for (final File file : files) { - String strip = file.getAbsolutePath().replace(folder.getAbsolutePath() + "\\", ""); - FileInfo info = new FileInfo(folder.getName() + "\\" + strip, false, false); - - File save = Base.instance().scenarioManager().file(scenario.name(), info); - if (save.isDirectory() && !save.delete()) { - continue; - } - - Files.write(save.toPath(), Files.readAllBytes(file.toPath())); - consumer.accept(info); - } - } catch (Exception ignored) { - } - }).start(); - } catch (Exception ignored) { - } - } - - public static void pick(Consumer consumer, Scenario.Builder scenario, final String name, String filter) { - if (!ImGui.button(name + "##" + COUNTER++)) { - return; - } - - final String pick = NFDUtils.pickFile(filter); - if (pick.isEmpty()) { - return; - } - - final FileInfo fileInfo = new FileInfo(new File(pick).getName(), false, false); - final File file = Base.instance().scenarioManager().file(scenario.name(), fileInfo); - - try { - if (file.isDirectory() && !file.delete()) { - return; - } - - Files.write(file.toPath(), Files.readAllBytes(Path.of(pick))); - } catch (IOException e) { - return; - } - - consumer.accept(fileInfo); - } - - public static Color color(final String name, Color color) { - final float[] rgba = new float[] { color.getRedF(), color.getGreenF(), color.getBlueF(), color.getAlphaF() }; - ImGui.colorPicker4(name + "##" + COUNTER++, rgba); - return Color.fromRGBAF(rgba); - } - - public static int combo(final String label, int index, final String[] items) { - final ImInt currentItem = new ImInt(index); - ImGui.combo(label + "##" + COUNTER++, currentItem, items); - return currentItem.get(); - } - - public static int inputInt(String name, int value) { - final ImInt imInt = new ImInt(value); - ImGui.inputInt(name + "##" + COUNTER++, imInt); - return imInt.get(); - } - - public static float inputFloat(String name, float value) { - final ImFloat imFloat = new ImFloat(value); - ImGui.inputFloat(name + "##" + COUNTER++, imFloat); - return imFloat.get(); - } - - public static void inputInt4(String name, int[] value) { - ImGui.inputInt4(name + "##" + COUNTER++, value); - } - - public static String inputText(String name, String value) { -// ImGui.pushFont(FontUtils.CHILLGOTHIC_17, 17); - final ImString imString = new ImString(value); - ImGui.inputText(name + "##" + COUNTER++, imString, ImGuiInputTextFlags.NoHorizontalScroll | ImGuiInputTextFlags.CallbackResize); -// ImGui.popFont(); - return imString.get(); - } - - public static String inputMultiLineText(String name, String value) { -// ImGui.pushFont(FontUtils.CHILLGOTHIC_17, 17); - final ImString imString = new ImString(value); - ImGui.inputTextMultiline(name + "##" + COUNTER++, imString, ImGuiInputTextFlags.CallbackResize); -// ImGui.popFont(); - return imString.get(); - } - - public static boolean checkbox(String name, boolean value) { - return ImGui.checkbox(name + "##" + COUNTER++, value) != value; - } - - public static float sliderFloat(String name, float value, float min, float max) { - float[] newValue = new float[] {value}; - ImGui.sliderFloat(name + "##" + COUNTER++, newValue, min, max); - ImGui.sameLine(); - return inputFloat("", newValue[0]); - } - - public static int sliderInt(String name, int value, int min, int max) { - int[] newValue = new int[] {value}; - ImGui.sliderInt(name + "##" + COUNTER++, newValue, min, max); - ImGui.sameLine(); - return inputInt("", newValue[0]); - } - - // https://github.com/ocornut/imgui/issues/707#issuecomment-2732535348 - public static void setupTheme() { - ImGuiStyle style = ImGui.getStyle(); - - // Base colors for a pleasant and modern dark theme with dark accents - style.setColor(ImGuiCol.Text, 0.92f, 0.93f, 0.94f, 1.00f); // Light grey text for readability - style.setColor(ImGuiCol.TextDisabled, 0.50f, 0.52f, 0.54f, 1.00f); // Subtle grey for disabled text - style.setColor(ImGuiCol.WindowBg, 0.14f, 0.14f, 0.16f, 1.00f); // Dark background with a hint of blue - style.setColor(ImGuiCol.ChildBg, 0.16f, 0.16f, 0.18f, 1.00f); // Slightly lighter for child elements - style.setColor(ImGuiCol.PopupBg, 0.18f, 0.18f, 0.20f, 1.00f); // Popup background - style.setColor(ImGuiCol.Border, 0.28f, 0.29f, 0.30f, 0.60f); // Soft border color - style.setColor(ImGuiCol.BorderShadow, 0.00f, 0.00f, 0.00f, 0.00f); // No border shadow - style.setColor(ImGuiCol.FrameBg, 0.20f, 0.22f, 0.24f, 1.00f); // Frame background - style.setColor(ImGuiCol.FrameBgHovered, 0.22f, 0.24f, 0.26f, 1.00f); // Frame hover effect - style.setColor(ImGuiCol.FrameBgActive, 0.24f, 0.26f, 0.28f, 1.00f); // Active frame background - style.setColor(ImGuiCol.TitleBg, 0.14f, 0.14f, 0.16f, 1.00f); // Title background - style.setColor(ImGuiCol.TitleBgActive, 0.16f, 0.16f, 0.18f, 1.00f); // Active title background - style.setColor(ImGuiCol.TitleBgCollapsed, 0.14f, 0.14f, 0.16f, 1.00f); // Collapsed title background - style.setColor(ImGuiCol.MenuBarBg, 0.20f, 0.20f, 0.22f, 1.00f); // Menu bar background - style.setColor(ImGuiCol.ScrollbarBg, 0.16f, 0.16f, 0.18f, 1.00f); // Scrollbar background - style.setColor(ImGuiCol.ScrollbarGrab, 0.24f, 0.26f, 0.28f, 1.00f); // Dark accent for scrollbar grab - style.setColor(ImGuiCol.ScrollbarGrabHovered, 0.28f, 0.30f, 0.32f, 1.00f); // Scrollbar grab hover - style.setColor(ImGuiCol.ScrollbarGrabActive, 0.32f, 0.34f, 0.36f, 1.00f); // Scrollbar grab active - style.setColor(ImGuiCol.CheckMark, 0.46f, 0.56f, 0.66f, 1.00f); // Dark blue checkmark - style.setColor(ImGuiCol.SliderGrab, 0.36f, 0.46f, 0.56f, 1.00f); // Dark blue slider grab - style.setColor(ImGuiCol.SliderGrabActive, 0.40f, 0.50f, 0.60f, 1.00f); // Active slider grab - style.setColor(ImGuiCol.Button, 0.24f, 0.34f, 0.44f, 1.00f); // Dark blue button - style.setColor(ImGuiCol.ButtonHovered, 0.28f, 0.38f, 0.48f, 1.00f); // Button hover effect - style.setColor(ImGuiCol.ButtonActive, 0.32f, 0.42f, 0.52f, 1.00f); // Active button - style.setColor(ImGuiCol.Header, 0.24f, 0.34f, 0.44f, 1.00f); // Header color similar to button - style.setColor(ImGuiCol.HeaderHovered, 0.28f, 0.38f, 0.48f, 1.00f); // Header hover effect - style.setColor(ImGuiCol.HeaderActive, 0.32f, 0.42f, 0.52f, 1.00f); // Active header - style.setColor(ImGuiCol.Separator, 0.28f, 0.29f, 0.30f, 1.00f); // Separator color - style.setColor(ImGuiCol.SeparatorHovered, 0.46f, 0.56f, 0.66f, 1.00f); // Hover effect for separator - style.setColor(ImGuiCol.SeparatorActive, 0.46f, 0.56f, 0.66f, 1.00f); // Active separator - style.setColor(ImGuiCol.ResizeGrip, 0.36f, 0.46f, 0.56f, 1.00f); // Resize grip - style.setColor(ImGuiCol.ResizeGripHovered, 0.40f, 0.50f, 0.60f, 1.00f); // Hover effect for resize grip - style.setColor(ImGuiCol.ResizeGripActive, 0.44f, 0.54f, 0.64f, 1.00f); // Active resize grip - style.setColor(ImGuiCol.Tab, 0.20f, 0.22f, 0.24f, 1.00f); // Inactive tab - style.setColor(ImGuiCol.TabHovered, 0.28f, 0.38f, 0.48f, 1.00f); // Hover effect for tab - style.setColor(ImGuiCol.TabActive, 0.24f, 0.34f, 0.44f, 1.00f); // Active tab color - style.setColor(ImGuiCol.TabUnfocused, 0.20f, 0.22f, 0.24f, 1.00f); // Unfocused tab - style.setColor(ImGuiCol.TabUnfocusedActive, 0.24f, 0.34f, 0.44f, 1.00f); // Active but unfocused tab - style.setColor(ImGuiCol.PlotLines, 0.46f, 0.56f, 0.66f, 1.00f); // Plot lines - style.setColor(ImGuiCol.PlotLinesHovered, 0.46f, 0.56f, 0.66f, 1.00f); // Hover effect for plot lines - style.setColor(ImGuiCol.PlotHistogram, 0.36f, 0.46f, 0.56f, 1.00f); // Histogram color - style.setColor(ImGuiCol.PlotHistogramHovered, 0.40f, 0.50f, 0.60f, 1.00f); // Hover effect for histogram - style.setColor(ImGuiCol.TableHeaderBg, 0.20f, 0.22f, 0.24f, 1.00f); // Table header background - style.setColor(ImGuiCol.TableBorderStrong, 0.28f, 0.29f, 0.30f, 1.00f); // Strong border for tables - style.setColor(ImGuiCol.TableBorderLight, 0.24f, 0.25f, 0.26f, 1.00f); // Light border for tables - style.setColor(ImGuiCol.TableRowBg, 0.20f, 0.22f, 0.24f, 1.00f); // Table row background - style.setColor(ImGuiCol.TableRowBgAlt, 0.22f, 0.24f, 0.26f, 1.00f); // Alternate row background - style.setColor(ImGuiCol.TextSelectedBg, 0.24f, 0.34f, 0.44f, 0.35f); // Selected text background - style.setColor(ImGuiCol.DragDropTarget, 0.46f, 0.56f, 0.66f, 0.90f); // Drag and drop target - style.setColor(ImGuiCol.NavHighlight, 0.46f, 0.56f, 0.66f, 1.00f); // Navigation highlight - style.setColor(ImGuiCol.NavWindowingHighlight, 1.00f, 1.00f, 1.00f, 0.70f); // Windowing highlight - style.setColor(ImGuiCol.NavWindowingDimBg, 0.80f, 0.80f, 0.80f, 0.20f); // Dim background for windowing - style.setColor(ImGuiCol.ModalWindowDimBg, 0.80f, 0.80f, 0.80f, 0.35f); // Dim background for modal windows - - // Style adjustments - style.setWindowPadding(8.00f, 8.00f); - style.setFramePadding(5.00f, 2.00f); - style.setCellPadding(6.00f, 6.00f); - style.setItemSpacing(6.00f, 6.00f); - style.setItemInnerSpacing(6.00f, 6.00f); - style.setTouchExtraPadding(0.00f, 0.00f); - style.setIndentSpacing(25); - style.setScrollbarSize(11); - style.setGrabMinSize(10); - style.setChildBorderSize(1); - style.setPopupBorderSize(1); - style.setFrameBorderSize(1); - style.setTabBorderSize(1); - style.setWindowRounding(7); - style.setChildRounding(4); - style.setFrameRounding(3); - style.setPopupRounding(4); - style.setScrollbarRounding(8); - style.setGrabRounding(3); - style.setLogSliderDeadzone(4); - style.setTabRounding(4); - } -} \ No newline at end of file diff --git a/core/src/main/java/oxy/bascenario/utils/Launcher.java b/core/src/main/java/oxy/bascenario/utils/Launcher.java index 1126b338..9d2e37c5 100644 --- a/core/src/main/java/oxy/bascenario/utils/Launcher.java +++ b/core/src/main/java/oxy/bascenario/utils/Launcher.java @@ -23,9 +23,9 @@ public static void launch(Screen screen, boolean fullScreen, boolean bypass) { } Lwjgl3ApplicationConfiguration configuration = new Lwjgl3ApplicationConfiguration(); - configuration.setTitle("Blue Archive Scenario Engine."); + configuration.setTitle("Blue Archive Scenario Engine"); configuration.setWindowedMode(1280, 720); - configuration.setWindowSizeLimits(1280, 720, -1, -1); +// configuration.setWindowSizeLimits(1280, 720, -1, -1); WINDOW = new EngineRenderer(screen, fullScreen); new Lwjgl3Application(WINDOW, configuration); diff --git a/core/src/main/java/oxy/bascenario/utils/RivetUtil.java b/core/src/main/java/oxy/bascenario/utils/RivetUtil.java new file mode 100644 index 00000000..02e9fc17 --- /dev/null +++ b/core/src/main/java/oxy/bascenario/utils/RivetUtil.java @@ -0,0 +1,68 @@ +package oxy.bascenario.utils; + +import net.lenni0451.commons.color.Color; +import net.lenni0451.rivet.Rivet; +import net.lenni0451.rivet.component.container.Button; +import net.lenni0451.rivet.component.container.ComboBox; +import net.lenni0451.rivet.component.container.Container; +import net.lenni0451.rivet.component.container.DecoratedContainer; +import net.lenni0451.rivet.component.impl.Label; +import net.lenni0451.rivet.component.impl.SolidColor; +import net.lenni0451.rivet.layout.grid.GridAnchor; +import net.lenni0451.rivet.layout.grid.GridFill; +import net.lenni0451.rivet.layout.grid.GridOptions; +import net.lenni0451.rivet.layout.list.VerticalListLayout; +import net.lenni0451.rivet.math.Padding; +import net.lenni0451.rivet.text.model.TextOrigin; +import net.lenni0451.rivet.theme.Theme; +import oxy.bascenario.utils.math.Pair; + +import java.util.List; + +import static net.lenni0451.rivet.utils.MathUtils.roundMin; + +public class RivetUtil { + public static void setupTheme(Rivet rivet) { + rivet.theme(new Theme() { + @Override + protected void addValues(final Rivet rivet, final Values values) { + values.put(Theme.SLIDER_THUMB_COLOR, Color.WHITE); + } + }); + } + + public static ComboBox dropdown(String name, float width, List> options) { + Container container = new Container(new VerticalListLayout(5, true)); + final DecoratedContainer decoratedContainer = new DecoratedContainer( + new SolidColor(Color.fromRGB(24, 24, 24)).cornerRadius(5).outlineColor(Color.fromRGB(36, 36, 36)).outlineWidth(1f), container); + final ComboBox box = new ComboBox(name, decoratedContainer); + decoratedContainer.minSize(width, 0f); + + Button button = box.button(); + ((Label)button.child()).scale(0.3f); + button.inactiveOutlineColor().set(Color.TRANSPARENT); + button.activeOutlineColor().set(Color.TRANSPARENT); + button.inactiveColor().set(Color.TRANSPARENT); + + box.arrowSize().set(0f); + box.layoutOptions(new GridOptions(0, 6).withAnchor(GridAnchor.LEFT).withWeightX(1).withFill(GridFill.HORIZONTAL).withColumnSpan(2)); + + float textHeight = 11f; + Padding padding = new Padding(roundMin(textHeight / 3F, 0), roundMin(textHeight / 10F, 0), roundMin(textHeight / 3F, 0), roundMin(textHeight / 10F, 0)); + button.innerPadding().set(padding.withTop(3f)); + + for (Pair option : options) { + container.addChild(new Button(option.left(), c -> option.right().run()), b -> { + ((Label)b.child()).scale(0.31f); + ((Label)b.child()).horizontalOrigin(TextOrigin.Horizontal.VISUAL_LEFT); + b.inactiveOutlineColor().set(Color.TRANSPARENT); + b.activeOutlineColor().set(Color.TRANSPARENT); + b.inactiveColor().set(Color.TRANSPARENT); + }); + } +// container.addChild(new Button("None", c -> {})); +// container.addChild(new Button("Invert", c -> {})); + + return box; + } +} diff --git a/core/src/main/java/oxy/bascenario/utils/font/FontUtils.java b/core/src/main/java/oxy/bascenario/utils/font/FontUtils.java index 187fd01b..3c32c80b 100644 --- a/core/src/main/java/oxy/bascenario/utils/font/FontUtils.java +++ b/core/src/main/java/oxy/bascenario/utils/font/FontUtils.java @@ -2,10 +2,9 @@ import java.util.*; -import imgui.*; -import lombok.SneakyThrows; -import net.raphimc.thingl.resource.font.Font; -import net.raphimc.thingl.resource.font.impl.FreeTypeFont; +import net.raphimc.thingl.resource.font.face.impl.FreeTypeFontFace; +import net.raphimc.thingl.resource.font.instance.FontInstance; +import net.raphimc.thingl.resource.font.instance.impl.FreeTypeFontInstance; import oxy.bascenario.Base; import oxy.bascenario.api.Scenario; import oxy.bascenario.api.render.elements.text.font.FontStyle; @@ -14,21 +13,19 @@ import oxy.bascenario.api.utils.FileInfo; public class FontUtils { - public static Font DEFAULT, SEMI_BOLD; - private static final Map NAME_TO_FONTS = new HashMap<>(); + public static FontInstance DEFAULT, SEMI_BOLD; + private static final Map NAME_TO_FONTS = new HashMap<>(); - public static ImFont IM_FONT_SEMI_BOLD, IM_FONT_REGULAR, CHILLGOTHIC_17; - - public static Font font(FontStyle style, FontType type) { + public static FontInstance font(FontStyle style, FontType type) { return NAME_TO_FONTS.get(type.toName(style)); } - public static Font font(String name) { + public static FontInstance font(String name) { return NAME_TO_FONTS.get(name); } - public static Font toFont(Scenario scenario, TextSegment segment) { - Font font; + public static FontInstance toFont(Scenario scenario, TextSegment segment) { + FontInstance font; if (segment.font().file() != null) { font = NAME_TO_FONTS.get(String.valueOf(segment.font().file().hashCode(scenario.getName()))); if (font == null) { @@ -42,6 +39,9 @@ public static Font toFont(Scenario scenario, TextSegment segment) { } public static void loadFonts() { + // UI (Rivet) + loadFont("SFUIRegular", "/assets/base/fonts/rivet/SFUIText-Regular.ttf", 40); + // Global loadFont("NotoSansRegular", "/assets/base/fonts/global/NotoSans-Regular.ttf"); loadFont("NotoSansSemiBold", "/assets/base/fonts/global/NotoSans-SemiBold.ttf"); @@ -69,50 +69,23 @@ public static void loadFonts() { DEFAULT = NAME_TO_FONTS.get("ChillRoundRegular"); SEMI_BOLD = NAME_TO_FONTS.get("ChillRoundSemiBold"); - - ImGui.getIO().setFontDefault(loadImFont("/assets/base/fonts/global/NotoSans-Regular.ttf", 18, false)); - IM_FONT_REGULAR = loadImFont("/assets/base/fonts/global/NotoSans-Regular.ttf", 50, false); - IM_FONT_SEMI_BOLD = loadImFont("/assets/base/fonts/global/NotoSans-SemiBold.ttf", 50, false); - CHILLGOTHIC_17 = loadImFont("/assets/base/fonts/chinese/simplified/ChillRoundGothic_Regular.otf", 50, true); - - ImGui.getIO().setConfigDpiScaleFonts(true); - ImGui.getIO().setConfigDpiScaleViewports(true); } - public static Font loadSpecificFont(Scenario scenario, FileInfo font) { + public static FontInstance loadSpecificFont(Scenario scenario, FileInfo font) { final byte[] fontData = (byte[]) Base.instance().assetsManager().assets(scenario.getName(), font).asset(); - return new FreeTypeFont(fontData, 65); + return new FreeTypeFontInstance(new FreeTypeFontFace(fontData), 65); } private static void loadFont(String name, String font) { + loadFont(name, font, 65); + } + + private static void loadFont(String name, String font, int size) { try { final byte[] fontData = FontUtils.class.getResourceAsStream(font).readAllBytes(); - NAME_TO_FONTS.put(name, new FreeTypeFont(fontData, 65)); + NAME_TO_FONTS.put(name, new FreeTypeFontInstance(new FreeTypeFontFace(fontData), size)); } catch (Exception e) { throw new RuntimeException(e); } } - - @SneakyThrows - private static ImFont loadImFont(String font, int size, boolean full) { - final byte[] fontData = FontUtils.class.getResourceAsStream(font).readAllBytes(); - - final ImFontGlyphRangesBuilder rangesBuilder = new ImFontGlyphRangesBuilder(); - rangesBuilder.addRanges(ImGui.getIO().getFonts().getGlyphRangesDefault()); - if (full) { - rangesBuilder.addRanges(ImGui.getIO().getFonts().getGlyphRangesJapanese()); - rangesBuilder.addRanges(ImGui.getIO().getFonts().getGlyphRangesCyrillic()); - rangesBuilder.addRanges(ImGui.getIO().getFonts().getGlyphRangesChineseFull()); - rangesBuilder.addRanges(ImGui.getIO().getFonts().getGlyphRangesChineseSimplifiedCommon()); - rangesBuilder.addRanges(ImGui.getIO().getFonts().getGlyphRangesKorean()); - - // Thanks ImGui :D, memory goin to be nice! and well imgui-java still on 1.90.0 so no dynamic render. - for (char c = '\u4e00'; c <= '\u9fff'; c++) { - rangesBuilder.addChar(c); - } - } - - final ImGuiIO data = ImGui.getIO(); - return data.getFonts().addFontFromMemoryTTF(fontData, size, new ImFontConfig(), rangesBuilder.buildRanges()); - } } \ No newline at end of file diff --git a/core/src/main/java/oxy/bascenario/utils/font/TextUtils.java b/core/src/main/java/oxy/bascenario/utils/font/TextUtils.java index a4b3b16d..f8eb3d2f 100644 --- a/core/src/main/java/oxy/bascenario/utils/font/TextUtils.java +++ b/core/src/main/java/oxy/bascenario/utils/font/TextUtils.java @@ -35,26 +35,30 @@ public static float getLogicalHeight(float size, final ShapedTextRun textRun) { } public static void textRun(float size, final TextRun run, float x, float y) { - ThinGL.rendererText().pushGlobalScale(size / 65f); - ThinGL.rendererText().textRun(GLOBAL_RENDER_STACK, run, x, y); - ThinGL.rendererText().popGlobalScale(); + GLOBAL_RENDER_STACK.pushMatrix(); + GLOBAL_RENDER_STACK.scale(size / 65f); + ThinGL.rendererText().textRun(GLOBAL_RENDER_STACK, run, x / (size / 65f), y / (size / 65f)); + GLOBAL_RENDER_STACK.popMatrix(); } public static void textRun(float size, final TextRun run, float x, float y, RendererText.VerticalOrigin verticalOrigin, RendererText.HorizontalOrigin horizontalOrigin) { - ThinGL.rendererText().pushGlobalScale(size / 65f); - ThinGL.rendererText().textRun(GLOBAL_RENDER_STACK, run, x, y, verticalOrigin, horizontalOrigin); - ThinGL.rendererText().popGlobalScale(); + GLOBAL_RENDER_STACK.pushMatrix(); + GLOBAL_RENDER_STACK.scale(size / 65f); + ThinGL.rendererText().textRun(GLOBAL_RENDER_STACK, run, x / (size / 65f), y / (size / 65f), verticalOrigin, horizontalOrigin); + GLOBAL_RENDER_STACK.popMatrix(); } public static void textLine(float size, final TextLine textLine, final float x, final float y) { - ThinGL.rendererText().pushGlobalScale(size / 65f); - ThinGL.rendererText().textLine(GLOBAL_RENDER_STACK, textLine.shape(), x, y); - ThinGL.rendererText().popGlobalScale(); + GLOBAL_RENDER_STACK.pushMatrix(); + GLOBAL_RENDER_STACK.scale(size / 65f); + ThinGL.rendererText().textLine(GLOBAL_RENDER_STACK, textLine.shape(), x / (size / 65f), y / (size / 65f)); + GLOBAL_RENDER_STACK.popMatrix(); } public static void textLine(float size, final TextLine textLine, final float x, final float y, final RendererText.VerticalOrigin verticalOrigin, final RendererText.HorizontalOrigin horizontalOrigin) { - ThinGL.rendererText().pushGlobalScale(size / 65f); - ThinGL.rendererText().textLine(GLOBAL_RENDER_STACK, textLine.shape(), x, y, verticalOrigin, horizontalOrigin); - ThinGL.rendererText().popGlobalScale(); + GLOBAL_RENDER_STACK.pushMatrix(); + GLOBAL_RENDER_STACK.scale(size / 65f); + ThinGL.rendererText().textLine(GLOBAL_RENDER_STACK, textLine.shape(), x / (size / 65f), y / (size / 65f), verticalOrigin, horizontalOrigin); + GLOBAL_RENDER_STACK.popMatrix(); } } diff --git a/core/src/main/java/oxy/bascenario/utils/math/MathUtils.java b/core/src/main/java/oxy/bascenario/utils/math/MathUtils.java index 9f5eb379..ac9e1004 100644 --- a/core/src/main/java/oxy/bascenario/utils/math/MathUtils.java +++ b/core/src/main/java/oxy/bascenario/utils/math/MathUtils.java @@ -5,6 +5,13 @@ public class MathUtils { public static Vector4f findBackgroundRender(Vector2f minSize) { +// if (minSize.x > 1920 && minSize.y > 1080) { +// int centerX = (int) (1920f / 2F - (minSize.x / 2)); +// int centerY = (int) (1080f / 2F - (minSize.y / 2)); +// +// return new Vector4f(centerX, centerY, minSize.x, minSize.y); +// } + float bestRatio = Math.max(1920f / minSize.x, 1080f / minSize.y); float newSizeX = minSize.x * bestRatio, newSizeY = minSize.y * bestRatio; diff --git a/core/src/main/resources/assets/base/base.imgui b/core/src/main/resources/assets/base/base.imgui deleted file mode 100644 index 9964c90c..00000000 --- a/core/src/main/resources/assets/base/base.imgui +++ /dev/null @@ -1,73 +0,0 @@ -[Window][Debug##Default] -Pos=60,60 -Size=400,400 -Collapsed=0 - -[Window][WindowOverViewport_11111111] -Pos=0,23 -Size=1920,1004 -Collapsed=0 - -[Window][Actions] -Pos=1356,23 -Size=564,563 -Collapsed=0 -DockId=0x00000004,0 - -[Window][Timeline] -Pos=0,588 -Size=1920,439 -Collapsed=0 -DockId=0x00000002,0 - -[Window][Objects] -Pos=1356,23 -Size=564,563 -Collapsed=0 -DockId=0x00000004,1 - -[Window][Inspector] -Pos=0,23 -Size=375,563 -Collapsed=0 -DockId=0x00000005,0 - -[Window][Scenario View] -Pos=377,23 -Size=977,563 -Collapsed=0 -DockId=0x00000006,0 - -[Window][Assets] -Pos=0,23 -Size=375,563 -Collapsed=0 -DockId=0x00000005,1 - -[Window][Select asset] -Pos=775,362 -Size=370,303 -Collapsed=0 - -[Window][Dear ImGui Demo] -Pos=276,54 -Size=550,680 -Collapsed=0 - -[Table][0xF666A32D,2] -Column 0 Weight=1.3118 -Column 1 Weight=0.6882 - -[Table][0x412292FB,2] -Column 0 Weight=1.0000 -Column 1 Weight=1.0000 - -[Docking][Data] -DockSpace ID=0x7C6B3D9B Window=0xA87D555D Pos=0,23 Size=1920,1004 Split=Y - DockNode ID=0x00000001 Parent=0x7C6B3D9B SizeRef=1920,563 Split=X - DockNode ID=0x00000003 Parent=0x00000001 SizeRef=1354,538 Split=X Selected=0xE7039252 - DockNode ID=0x00000005 Parent=0x00000003 SizeRef=375,587 Selected=0xE7039252 - DockNode ID=0x00000006 Parent=0x00000003 SizeRef=977,587 CentralNode=1 Selected=0x52AAAE52 - DockNode ID=0x00000004 Parent=0x00000001 SizeRef=564,538 Selected=0x7D8AF184 - DockNode ID=0x00000002 Parent=0x7C6B3D9B SizeRef=1920,439 Selected=0x0F18B61B - diff --git a/core/src/main/resources/assets/base/fonts/rivet/SFUIText-Regular.ttf b/core/src/main/resources/assets/base/fonts/rivet/SFUIText-Regular.ttf new file mode 100644 index 00000000..13b1295f Binary files /dev/null and b/core/src/main/resources/assets/base/fonts/rivet/SFUIText-Regular.ttf differ diff --git a/core/src/test/java/DefaultRivetTest.java b/core/src/test/java/DefaultRivetTest.java new file mode 100644 index 00000000..a726c2e0 --- /dev/null +++ b/core/src/test/java/DefaultRivetTest.java @@ -0,0 +1,77 @@ +import net.lenni0451.commons.color.Color; +import net.lenni0451.rivet.Rivet; +import net.lenni0451.rivet.backend.render.Renderer; +import net.lenni0451.rivet.backend.thingl.RivetThinGLApplication; +import net.lenni0451.rivet.component.Component; +import net.lenni0451.rivet.component.container.Container; +import net.lenni0451.rivet.component.container.ScrollContainer; +import net.lenni0451.rivet.component.impl.SolidColor; +import net.lenni0451.rivet.layout.border.BorderLayout; +import net.lenni0451.rivet.layout.border.BorderPosition; +import net.lenni0451.rivet.math.Size; +import net.raphimc.thingl.resource.font.face.impl.FreeTypeFontFace; +import net.raphimc.thingl.resource.font.instance.FontInstance; +import net.raphimc.thingl.resource.font.instance.FontInstanceSet; +import net.raphimc.thingl.text.util.GlyphPredicate; +import org.lwjgl.glfw.GLFW; + +import java.io.IOException; +import java.io.InputStream; +import java.util.LinkedHashMap; +import java.util.SequencedMap; + +public class DefaultRivetTest extends RivetThinGLApplication { + + public static void main(String[] args) { + new DefaultRivetTest().run(); + } + + private static final int ONE_SECOND = 200; + + @Override + protected void init(final Rivet rivet) { + Container everything = new Container(BorderLayout.DEFAULT); + everything.addChild(new SolidColor(Color.RED) { + @Override + public Size computeIdealSize(Size constraints) { + return new Size(100, 20); + } + }.layoutOptions(BorderPosition.LEFT)); + everything.addChild(new Timeline().layoutOptions(BorderPosition.TOP)); + + rivet.root().addChild(new ScrollContainer(everything, true, true)); + } + + private static class Timeline extends Component { + @Override + public void render(final Renderer renderer, final Size size) { + renderer.fillRect(0, 0, size.width(), size.height(), Color.LIGHT_GRAY); + } + + @Override + public Size computeIdealSize(final Size constraints) { + return new Size(0, 50); + } + } + + static { + if (System.getProperty("os.name").contains("Linux")) { + GLFW.glfwInitHint(GLFW.GLFW_PLATFORM, GLFW.GLFW_PLATFORM_X11); + } + } + + public static FontInstanceSet createFont(final int size, final InputStream... streams) throws IOException { + SequencedMap fonts = new LinkedHashMap<>(); + for (InputStream is : streams) { + FontInstance font = new FreeTypeFontFace(is.readAllBytes()).getInstance(size); + fonts.put(font, GlyphPredicate.all()); + } + return new FontInstanceSet(fonts); + } + + + @Override + protected FontInstanceSet createFont() throws Exception { + return createFont(40, DefaultRivetTest.class.getResourceAsStream("assets/base/fonts/global/NotoSans-Regular.ttf")); + } +} \ No newline at end of file diff --git a/core/src/test/java/ImGuiTest.java b/core/src/test/java/ImGuiTest.java index d026e820..067666b5 100644 --- a/core/src/test/java/ImGuiTest.java +++ b/core/src/test/java/ImGuiTest.java @@ -1,4 +1,4 @@ -import imgui.ImGui; +//import imgui.ImGui; import oxy.bascenario.utils.ExtendableScreen; import oxy.bascenario.utils.Launcher; @@ -7,7 +7,7 @@ public static void main(String[] args) { Launcher.launch(new ExtendableScreen() { @Override public void render(float delta) { - ImGui.showDemoWindow(); +// ImGui.showDemoWindow(); } }, false); } diff --git a/editor/build.gradle.kts b/editor/build.gradle.kts index c917e1dd..19c71ddd 100644 --- a/editor/build.gradle.kts +++ b/editor/build.gradle.kts @@ -2,6 +2,10 @@ plugins { id("java") } +repositories { + maven("https://jitpack.io") +} + dependencies { implementation(project(":api")) implementation(project(":core")) diff --git a/editor/src/main/java/oxy/bascenario/editor/EditorValues.java b/editor/src/main/java/oxy/bascenario/editor/EditorValues.java new file mode 100644 index 00000000..8bd62189 --- /dev/null +++ b/editor/src/main/java/oxy/bascenario/editor/EditorValues.java @@ -0,0 +1,50 @@ +package oxy.bascenario.editor; + +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; + +@Accessors(fluent = true) +@Getter +@Setter +public class EditorValues { + @Getter + @Setter + private static EditorValues instance; + + private TimelineType type = TimelineType.Sequencer; + + private boolean playing; + private long timestamp = 1000L; + public void timestamp(long timestamp) { + this.timestamp = timestamp; + this.playing = false; + } + + private float scale = 1; + private float scroll; + + private static final float ONE_SECOND_WIDTH = 128; + + public float oneSecondWidth() { + return ONE_SECOND_WIDTH * scale; + } + public float oneMilSecondWidth() { + return (ONE_SECOND_WIDTH / 1000f) * scale; + } + + private long since = 0; + public void tick() { + if (since == 0) { + since = System.currentTimeMillis(); + } + if (playing) { + timestamp += System.currentTimeMillis() - since; + } + since = System.currentTimeMillis(); + } + + public enum TimelineType { + Dope_Sheet, Sequencer + } +} diff --git a/editor/src/main/java/oxy/bascenario/editor/ScenarioEditorScreen.java b/editor/src/main/java/oxy/bascenario/editor/ScenarioEditorScreen.java new file mode 100644 index 00000000..29ceb49a --- /dev/null +++ b/editor/src/main/java/oxy/bascenario/editor/ScenarioEditorScreen.java @@ -0,0 +1,47 @@ +package oxy.bascenario.editor; + +import net.lenni0451.commons.color.Color; +import net.lenni0451.rivet.Rivet; +import net.lenni0451.rivet.component.container.Container; +import net.lenni0451.rivet.component.container.DecoratedContainer; +import net.lenni0451.rivet.component.impl.SolidColor; +import net.lenni0451.rivet.layout.dock.DockLayout; +import net.lenni0451.rivet.layout.dock.DockPosition; +import net.lenni0451.rivet.math.Padding; +import net.raphimc.thingl.ThinGL; +import oxy.bascenario.api.Scenario; +import oxy.bascenario.editor.containers.DockBarContainer; +import oxy.bascenario.editor.containers.GlobalContainer; +import oxy.bascenario.utils.ExtendableScreen; +import oxy.bascenario.utils.thingl.ThinGLUtils; + +public class ScenarioEditorScreen extends ExtendableScreen { + private final Scenario.Builder scenario; + + public ScenarioEditorScreen(Scenario.Builder builder) { + EditorValues.instance(new EditorValues()); + + this.scenario = builder; + } + + @Override + public void renderBehindRivet() { + EditorValues.instance().tick(); + + ThinGL.renderer2D().filledRectangle(ThinGLUtils.GLOBAL_RENDER_STACK, 0, 0, ThinGL.windowInterface().getFramebufferWidth(), ThinGL.windowInterface().getFramebufferHeight(), Color.fromRGB(24, 24, 24)); + } + + @Override + public void init(Rivet rivet) { + Container mainContainer = new Container(new DockLayout()); + rivet.root().addChild(mainContainer); + + // Dock bar (File, Edit, ...) + mainContainer.addChild(new DockBarContainer(), c -> c.layoutOptions(DockPosition.TOP)); + + mainContainer.addChild(new DecoratedContainer(new SolidColor(), new GlobalContainer()), c -> { + c.layoutOptions(DockPosition.CENTER); + c.innerPadding(new Padding(2, 8, 2, 10)); + }); + } +} diff --git a/editor/src/main/java/oxy/bascenario/editor/containers/DockBarContainer.java b/editor/src/main/java/oxy/bascenario/editor/containers/DockBarContainer.java new file mode 100644 index 00000000..d4e34ceb --- /dev/null +++ b/editor/src/main/java/oxy/bascenario/editor/containers/DockBarContainer.java @@ -0,0 +1,16 @@ +package oxy.bascenario.editor.containers; + +import net.lenni0451.rivet.component.container.Container; +import net.lenni0451.rivet.layout.list.HorizontalListLayout; +import net.lenni0451.rivet.math.Size; + +public class DockBarContainer extends Container { + public DockBarContainer() { + super(new HorizontalListLayout(15, true)); + } + + @Override + public Size computeIdealSize(Size constraints) { + return constraints.withHeight(25); + } +} diff --git a/editor/src/main/java/oxy/bascenario/editor/containers/GlobalContainer.java b/editor/src/main/java/oxy/bascenario/editor/containers/GlobalContainer.java new file mode 100644 index 00000000..29ec804b --- /dev/null +++ b/editor/src/main/java/oxy/bascenario/editor/containers/GlobalContainer.java @@ -0,0 +1,161 @@ +package oxy.bascenario.editor.containers; + +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import lombok.experimental.Accessors; +import net.lenni0451.commons.color.Color; +import net.lenni0451.rivet.backend.render.Renderer; +import net.lenni0451.rivet.component.Component; +import net.lenni0451.rivet.component.container.Container; +import net.lenni0451.rivet.input.keyboard.Key; +import net.lenni0451.rivet.input.keyboard.KeyEvent; +import net.lenni0451.rivet.input.mouse.MouseButton; +import net.lenni0451.rivet.input.mouse.MouseButtonEvent; +import net.lenni0451.rivet.input.mouse.MouseMoveEvent; +import net.lenni0451.rivet.layout.Layout; +import net.lenni0451.rivet.layout.dock.DockLayout; +import net.lenni0451.rivet.layout.dock.DockPosition; +import net.lenni0451.rivet.math.Rectangle; +import net.lenni0451.rivet.math.Size; +import net.raphimc.thingl.ThinGL; +import net.raphimc.thingl.implementation.window.GLFWWindowInterface; +import org.lwjgl.glfw.GLFW; +import oxy.bascenario.editor.EditorValues; +import oxy.bascenario.editor.containers.assets.AssetsContainer; +import oxy.bascenario.editor.containers.timeline.TimelineContainer; + +import java.util.HashMap; +import java.util.Map; + +public class GlobalContainer extends Container { + public GlobalContainer() { + super(new DockLayout(3)); + + addChild(new TimelineContainer(), c -> c.layoutOptions(DockPosition.BOTTOM)); + addChild(new AssetsContainer(), c -> c.layoutOptions(DockPosition.LEFT)); + + for (Component component : children()) { + resizableComponents.put((ResizeableContainer) component, new ResizeComponent( + component.layoutOptions() == DockPosition.BOTTOM || component.layoutOptions() == DockPosition.TOP ? + ResizeComponent.ResizeType.Vertical : ResizeComponent.ResizeType.Horizontal, + (DockPosition) component.layoutOptions() + )); + } + } + + final Map resizableComponents = new HashMap<>(); + + @Override + public void render(Renderer renderer, Size size) { + super.render(renderer, size); + + for (Map.Entry entry : resizableComponents.entrySet()) { + if (!entry.getValue().nearAndMouseDown) { + continue; + } + final ResizeComponent component = entry.getValue(); + + Rectangle bounds = this.childBounds(entry.getKey()); + + if (component.type == ResizeComponent.ResizeType.Horizontal) { + if (component.position == DockPosition.LEFT) { + renderer.fillRect(bounds.maxX() + 5, bounds.y(), 2, bounds.height(), Color.fromRGB(115, 115, 115)); + } else { + renderer.fillRect(bounds.x() + 5, bounds.y(), 2, bounds.height(), Color.fromRGB(115, 115, 115)); + } + } else { + renderer.fillRect(bounds.x(), bounds.y() - 5, bounds.width(), 2, Color.fromRGB(115, 115, 115)); + } + } + } + + @Override + protected boolean onComponentMouseMove(MouseMoveEvent event, Size size) { + super.onComponentMouseMove(event, size); + if (rivet().dragAndDropManager().isDragging()) { + return true; + } + + for (Map.Entry entry : resizableComponents.entrySet()) { + final ResizeComponent component = entry.getValue(); + Rectangle bounds = this.childBounds(entry.getKey()); + + boolean near; + if (component.type == ResizeComponent.ResizeType.Vertical) { + near = Math.abs(event.y() - bounds.y()) < 4 && bounds.contains(event.x(), bounds.y()); + } else { + if (component.position == DockPosition.LEFT) { + near = Math.abs(event.x() - bounds.maxX()) < 4; + } else { + near = Math.abs(event.x() - bounds.x()) < 4; + } + + near = near && bounds.contains(bounds.x(), event.y()); + } + + if (near && event.isHeld(MouseButton.LEFT)) { + int mouse = component.type == ResizeComponent.ResizeType.Vertical ? GLFW.GLFW_VRESIZE_CURSOR : GLFW.GLFW_HRESIZE_CURSOR; + GLFW.glfwSetCursor(((GLFWWindowInterface) ThinGL.windowInterface()).getWindowHandle(), GLFW.glfwCreateStandardCursor(mouse)); + } + + component.near = near; + if (!component.nearAndMouseDown) { + component.nearAndMouseDown = event.isHeld(MouseButton.LEFT) && near; + } + + if (component.nearAndMouseDown) { + if (component.type == ResizeComponent.ResizeType.Vertical) { + entry.getKey().relativeScale = ((size.height() - event.y()) - 7) / size.height(); + entry.getKey().requestLayoutRecalculation(); + } else { + entry.getKey().relativeScale = (event.x() - 7) / size.width(); + entry.getKey().requestLayoutRecalculation(); + } + } + } + + return true; + } + + @Override + protected boolean onComponentMouseUp(MouseButtonEvent event, Size size) { + this.resizableComponents.values().forEach(c -> c.nearAndMouseDown = false); + GLFW.glfwSetCursor(((GLFWWindowInterface) ThinGL.windowInterface()).getWindowHandle(), GLFW.glfwCreateStandardCursor(GLFW.GLFW_ARROW_CURSOR)); + return super.onComponentMouseUp(event, size); + } + + @Override + protected boolean onComponentMouseDown(MouseButtonEvent event, Size size) { + this.resizableComponents.values().forEach(c -> c.nearAndMouseDown = event.button() == MouseButton.LEFT && c.near); + return super.onComponentMouseDown(event, size); + } + + @Override + protected boolean onComponentKeyUp(KeyEvent event) { + if (event.key() == Key.SPACE) { + EditorValues.instance().playing(!EditorValues.instance().playing()); + } + + return super.onComponentKeyUp(event); + } + + @RequiredArgsConstructor + private static class ResizeComponent { + private final ResizeType type; + private final DockPosition position; + private boolean near, nearAndMouseDown; + + public enum ResizeType { + Horizontal, Vertical + } + } + + @Accessors(fluent = true) + public static class ResizeableContainer extends Container { + @Setter + protected float relativeScale = 0.2f; + public ResizeableContainer(Layout layout) { + super(layout); + } + } +} diff --git a/editor/src/main/java/oxy/bascenario/editor/containers/assets/AssetsContainer.java b/editor/src/main/java/oxy/bascenario/editor/containers/assets/AssetsContainer.java new file mode 100644 index 00000000..56b14140 --- /dev/null +++ b/editor/src/main/java/oxy/bascenario/editor/containers/assets/AssetsContainer.java @@ -0,0 +1,37 @@ +package oxy.bascenario.editor.containers.assets; + +import lombok.experimental.Accessors; +import net.lenni0451.commons.color.Color; +import net.lenni0451.rivet.backend.render.Renderer; +import net.lenni0451.rivet.component.container.DecoratedContainer; +import net.lenni0451.rivet.component.impl.SolidColor; +import net.lenni0451.rivet.layout.dock.DockLayout; +import net.lenni0451.rivet.layout.dock.DockPosition; +import net.lenni0451.rivet.math.Padding; +import net.lenni0451.rivet.math.Size; +import oxy.bascenario.editor.containers.GlobalContainer; +import oxy.bascenario.editor.containers.assets.object.ObjectContainer; + +@Accessors(fluent = true) +public class AssetsContainer extends GlobalContainer.ResizeableContainer { + public AssetsContainer() { + super(new DockLayout(0)); + + addChild(new DecoratedContainer(new SolidColor(), new ObjectContainer()), c -> { + c.layoutOptions(DockPosition.CENTER); + c.innerPadding(new Padding(10, 10, 0, 0)); + }); + } + + @Override + public void render(Renderer renderer, Size size) { + renderer.fillRoundedRect(0, 0, size.width(), size.height(), 5, Color.fromRGB(40, 40, 40)); + super.render(renderer, size); + renderer.outlineRoundedRect(0, 0, size.width(), size.height(), 5, 1, Color.fromRGB(53, 53, 53)); + } + + @Override + public Size computeIdealSize(Size constraints) { + return constraints.withWidth(constraints.width() * relativeScale); + } +} diff --git a/editor/src/main/java/oxy/bascenario/editor/containers/assets/object/ObjectContainer.java b/editor/src/main/java/oxy/bascenario/editor/containers/assets/object/ObjectContainer.java new file mode 100644 index 00000000..4b42f177 --- /dev/null +++ b/editor/src/main/java/oxy/bascenario/editor/containers/assets/object/ObjectContainer.java @@ -0,0 +1,79 @@ +package oxy.bascenario.editor.containers.assets.object; + +import net.lenni0451.commons.color.Color; +import net.lenni0451.rivet.component.Component; +import net.lenni0451.rivet.component.container.Container; +import net.lenni0451.rivet.component.container.ScrollContainer; +import net.lenni0451.rivet.component.impl.Label; +import net.lenni0451.rivet.layout.flow.HorizontalFlowLayout; +import net.lenni0451.rivet.layout.list.VerticalListLayout; +import oxy.bascenario.api.render.elements.LocationInfo; +import oxy.bascenario.api.render.elements.Preview; +import oxy.bascenario.api.render.elements.emoticon.Emoticon; +import oxy.bascenario.api.render.elements.emoticon.EmoticonType; +import oxy.bascenario.api.render.elements.shape.Circle; +import oxy.bascenario.api.render.elements.shape.Rectangle; +import oxy.bascenario.api.render.elements.text.AnimatedText; +import oxy.bascenario.api.render.elements.text.Text; +import oxy.bascenario.api.render.elements.text.TextSegment; +import oxy.bascenario.api.render.elements.text.font.FontType; +import oxy.bascenario.editor.drag.FakeObjectComponent; +import oxy.bascenario.utils.TimeCompiler; +import oxy.bascenario.utils.components.TextWithName; +import oxy.bascenario.utils.components.TextWithNameNoImage; +import oxy.bascenario.utils.components.TypingLabel; + +import java.util.List; + +public class ObjectContainer extends ScrollContainer { + public ObjectContainer() { + final Container container = new Container(new HorizontalFlowLayout(20, 15)); + super(container); + + add(container, "Preview", "assets/base/uis/editor/objects/preview.png", new Preview(FontType.NotoSans, "Title", "Subtitle", null)); + add(container, "Location Info", "assets/base/uis/editor/objects/location-info.png", new LocationInfo(FontType.NotoSans, "Location A", 1000, 200)); + add(container, "Emoticon", "assets/base/uis/editor/objects/emoticon.png", new Emoticon(100, EmoticonType.ANGRY, true)); + + add(container, "Text", new Label("Text").scale(0.4f), new Text(List.of(TextSegment.builder().text("Text").build()), 42)); + add(container, "Typing Text", new TypingLabel("Typing Text").scale(0.4f), new AnimatedText(1, List.of(TextSegment.builder().text("Typing Text").build()), 42)); + + add(container, "Circle", "assets/base/uis/editor/objects/circle.png", new Circle(5, Color.WHITE, false)); + add(container, "Rectangle", "assets/base/uis/editor/objects/rectangle.png", new Rectangle(500, 500, Color.WHITE, false)); + } + + private void add(Container container, String name, Component c, Object object) { + final TextWithNameNoImage component = new TextWithNameNoImage(name, c); + + long duration = TimeCompiler.compileTime(object); + if (duration == Long.MAX_VALUE) { + duration = 1000L; + } + + long fDuration = duration; + component.mouseDownListener().add((ignored, bounds) -> { + FakeObjectComponent ghost = new FakeObjectComponent(object, fDuration); + rivet().dragAndDropManager().startDrag(ghost, ghost, 0, -60 / 2f); + return true; + }); + + container.addChild(component); + } + + private void add(Container container, String name, String path, Object object) { + final TextWithName component = new TextWithName(name, path); + + long duration = TimeCompiler.compileTime(object); + if (duration == Long.MAX_VALUE) { + duration = 1000L; + } + + long fDuration = duration; + component.mouseDownListener().add((ignored, bounds) -> { + FakeObjectComponent ghost = new FakeObjectComponent(object, fDuration); + rivet().dragAndDropManager().startDrag(ghost, ghost, 0, -60 / 2f); + return true; + }); + + container.addChild(component); + } +} diff --git a/editor/src/main/java/oxy/bascenario/editor/containers/sequencer/SequencerTimeSection.java b/editor/src/main/java/oxy/bascenario/editor/containers/sequencer/SequencerTimeSection.java new file mode 100644 index 00000000..78e7d1da --- /dev/null +++ b/editor/src/main/java/oxy/bascenario/editor/containers/sequencer/SequencerTimeSection.java @@ -0,0 +1,71 @@ +package oxy.bascenario.editor.containers.sequencer; + +import lombok.RequiredArgsConstructor; +import net.lenni0451.commons.color.Color; +import net.lenni0451.commons.math.MathUtils; +import net.lenni0451.rivet.backend.render.Renderer; +import net.lenni0451.rivet.backend.text.ShapedText; +import net.lenni0451.rivet.component.Component; +import net.lenni0451.rivet.input.mouse.MouseButton; +import net.lenni0451.rivet.input.mouse.MouseButtonEvent; +import net.lenni0451.rivet.input.mouse.MouseMoveEvent; +import net.lenni0451.rivet.math.Size; +import net.lenni0451.rivet.text.model.TextOrigin; +import oxy.bascenario.editor.EditorValues; + +@RequiredArgsConstructor +public class SequencerTimeSection extends Component { + @Override + public void render(Renderer renderer, Size size) { + renderer.fillRect(0, size.height() - 4, size.width(), 4, Color.fromRGB(108, 108, 210)); + + float x = -EditorValues.instance().scroll(); + while (!(x >= size.width())) { + float timeX = x + EditorValues.instance().scroll(); + float time = timeX / EditorValues.instance().oneMilSecondWidth(); + + ShapedText text = this.rivet().backend().font().shapeText(format(MathUtils.ceilLong(time)), Color.GRAY); + float currentX = x; + + renderer.scale(0.3f, () -> renderer.text(text, currentX / 0.3f, (5 / 35f) * size.height(), TextOrigin.Horizontal.VISUAL_LEFT, TextOrigin.Vertical.LOGICAL_TOP)); + + x += EditorValues.instance().oneSecondWidth(); + } + } + + @Override + protected boolean onComponentMouseMove(MouseMoveEvent event, Size bounds) { + if (event.x() > 0 && event.x() < bounds.width() && event.y() > 0 && event.y() < bounds.height() && event.buttons().contains(MouseButton.LEFT)) { + float x = EditorValues.instance().scroll() + event.x() - 2.5f; + long newTimestamp = (long) (x / (EditorValues.instance().oneMilSecondWidth())); + + EditorValues.instance().timestamp(Math.max(0, newTimestamp)); + } + + return false; + } + + @Override + protected boolean onComponentMouseDown(MouseButtonEvent event, Size bounds) { + if (event.x() > 0 && event.x() < bounds.width() && event.y() > 0 && event.y() < bounds.height()) { + float x = EditorValues.instance().scroll() + event.x() - 2.5f; + long newTimestamp = (long) (x / (EditorValues.instance().oneMilSecondWidth())); + + EditorValues.instance().timestamp(Math.max(0, newTimestamp)); + } + + return super.onComponentMouseDown(event, bounds); + } + + @Override + public Size computeIdealSize(Size constraints) { + return new Size(constraints.width(), 28); + } + + private static String format(long ms) { + long second = (ms / 1000) % 60; + long minute = (ms / (1000 * 60)) % 60; + long hour = (ms / (1000 * 60 * 60)) % 24; + return (hour < 10 ? "0" + hour : hour) + ":" + (minute < 10 ? "0" + minute : minute) + ":" + (second < 10 ? "0" + second : second); + } +} diff --git a/editor/src/main/java/oxy/bascenario/editor/containers/sequencer/VideoSequencerContainer.java b/editor/src/main/java/oxy/bascenario/editor/containers/sequencer/VideoSequencerContainer.java new file mode 100644 index 00000000..30af177e --- /dev/null +++ b/editor/src/main/java/oxy/bascenario/editor/containers/sequencer/VideoSequencerContainer.java @@ -0,0 +1,90 @@ +package oxy.bascenario.editor.containers.sequencer; + +import lombok.Getter; +import lombok.experimental.Accessors; +import net.lenni0451.commons.color.Color; +import net.lenni0451.rivet.backend.render.Renderer; +import net.lenni0451.rivet.component.Component; +import net.lenni0451.rivet.component.container.Container; +import net.lenni0451.rivet.component.container.DecoratedContainer; +import net.lenni0451.rivet.component.container.ScrollContainer; +import net.lenni0451.rivet.component.impl.SolidColor; +import net.lenni0451.rivet.layout.dock.DockLayout; +import net.lenni0451.rivet.layout.dock.DockPosition; +import net.lenni0451.rivet.layout.list.VerticalListLayout; +import net.lenni0451.rivet.math.Rectangle; +import net.lenni0451.rivet.math.Size; +import oxy.bascenario.editor.EditorValues; +import oxy.bascenario.editor.containers.track.TrackListContainer; +import oxy.bascenario.editor.containers.track.TrackTabListContainer; +import oxy.bascenario.editor.containers.track.component.TrackContainer; +import oxy.bascenario.editor.containers.track.component.TrackTabContainer; + +@Accessors(fluent = true) +public class VideoSequencerContainer extends Container { + final Component sequenceTimeSection; + + final TrackTabListContainer trackTabListContainer; + @Getter + final ScrollContainer trackListContainer; + + public VideoSequencerContainer() { + super(new DockLayout()); + + final Container mouseContainer = new Container(new VerticalListLayout()) { + @Override + public Size computeIdealSize(Size constraints) { + return constraints.withWidth(55f); + } + }; + + this.addChild(new DecoratedContainer(new SolidColor(Color.fromRGB(48, 48, 48)), mouseContainer), c -> c.layoutOptions(DockPosition.LEFT)); + + this.addChild(trackTabListContainer = new TrackTabListContainer(this), c -> c.layoutOptions(DockPosition.LEFT)); + + this.addChild(sequenceTimeSection = new SequencerTimeSection(), c -> c.layoutOptions(DockPosition.TOP)); + + this.addChild(trackListContainer = new TrackListContainer(), c -> c.layoutOptions(DockPosition.CENTER)); + } + + @Override + public void render(Renderer renderer, Size size) { + renderer.fillRect(0, 0, size.width(), size.height(), Color.fromRGB(30, 30, 30)); + + super.render(renderer, size); + drawCursor(renderer, size); + } + + @Override + public Size computeIdealSize(Size constraints) { + return constraints; + } + + private static final Color SEQUENCER_CURSOR_COLOR = Color.fromRGB(71, 114, 179); + + private void drawCursor(Renderer renderer, Size size) { + Rectangle bounds = this.childBounds(trackListContainer); + + final float offsetX = bounds.x(); + final float cursorX = -2.5f + offsetX + EditorValues.instance().timestamp() * (EditorValues.instance().oneSecondWidth() / 1000f) - EditorValues.instance().scroll(); + + renderer.componentBounds(bounds.x(), 0, bounds.width(), this.absoluteBounds().height(), () -> { + renderer.fillRect(cursorX, 0, 5, size.height(), SEQUENCER_CURSOR_COLOR.withAlphaF(0.8f)); + renderer.outlineRect(cursorX, 0, 5, size.height(), 1, Color.BLACK); + + renderer.fillTriangle(cursorX + 2.5f - 10, 0, cursorX + 2.5f, 10, cursorX + 2.5f + 10, 0, SEQUENCER_CURSOR_COLOR); + }); + } + + public TrackContainer createTrack() { + final TrackContainer container = new TrackContainer(); + ((Container) trackListContainer.child()).addChild(container); + trackTabListContainer.container().addChild(new TrackTabContainer(this, container)); + return container; + } + + public void removeTrack(TrackContainer container, TrackTabContainer tabContainer) { + ((Container) trackListContainer.child()).removeChild(container); + trackTabListContainer.container().removeChild(tabContainer); + } +} diff --git a/editor/src/main/java/oxy/bascenario/editor/containers/timeline/TimelineContainer.java b/editor/src/main/java/oxy/bascenario/editor/containers/timeline/TimelineContainer.java new file mode 100644 index 00000000..d88ab7e0 --- /dev/null +++ b/editor/src/main/java/oxy/bascenario/editor/containers/timeline/TimelineContainer.java @@ -0,0 +1,41 @@ +package oxy.bascenario.editor.containers.timeline; + +import lombok.experimental.Accessors; +import net.lenni0451.commons.color.Color; +import net.lenni0451.rivet.backend.render.Renderer; +import net.lenni0451.rivet.component.container.DecoratedContainer; +import net.lenni0451.rivet.component.impl.SolidColor; +import net.lenni0451.rivet.layout.dock.DockLayout; +import net.lenni0451.rivet.layout.dock.DockPosition; +import net.lenni0451.rivet.math.Padding; +import net.lenni0451.rivet.math.Size; +import oxy.bascenario.editor.containers.GlobalContainer; +import oxy.bascenario.editor.containers.sequencer.VideoSequencerContainer; + +@Accessors(fluent = true) +public class TimelineContainer extends GlobalContainer.ResizeableContainer { + public TimelineContainer() { + super(new DockLayout(0)); + + this.addChild(new DecoratedContainer(new SolidColor(), new TimelineDockBar()), c -> { + c.layoutOptions(DockPosition.TOP); + c.innerPadding(new Padding(8, 3, 0, 0)); + }); + this.addChild(new TimelineTimeControl(), c -> c.layoutOptions(DockPosition.BOTTOM)); + this.addChild(new VideoSequencerContainer(), c -> c.layoutOptions(DockPosition.CENTER)); + + relativeScale = 0.34f; + } + + @Override + public void render(Renderer renderer, Size size) { + renderer.fillRoundedRect(0, 0, size.width(), size.height(), 5, Color.fromRGB(48, 48, 48)); + super.render(renderer, size); + renderer.outlineRoundedRect(0, 0, size.width(), size.height(), 5, 1, Color.fromRGB(59, 59, 59)); + } + + @Override + public Size computeIdealSize(Size constraints) { + return constraints.withHeight(constraints.height() * relativeScale); + } +} diff --git a/editor/src/main/java/oxy/bascenario/editor/containers/timeline/TimelineDockBar.java b/editor/src/main/java/oxy/bascenario/editor/containers/timeline/TimelineDockBar.java new file mode 100644 index 00000000..6c30ffc1 --- /dev/null +++ b/editor/src/main/java/oxy/bascenario/editor/containers/timeline/TimelineDockBar.java @@ -0,0 +1,40 @@ +package oxy.bascenario.editor.containers.timeline; + +import net.lenni0451.commons.color.Color; +import net.lenni0451.rivet.component.container.*; +import net.lenni0451.rivet.component.impl.Label; +import net.lenni0451.rivet.component.impl.SolidColor; +import net.lenni0451.rivet.layout.grid.GridAnchor; +import net.lenni0451.rivet.layout.grid.GridFill; +import net.lenni0451.rivet.layout.grid.GridOptions; +import net.lenni0451.rivet.layout.list.HorizontalListLayout; +import net.lenni0451.rivet.layout.list.VerticalListLayout; +import net.lenni0451.rivet.math.Padding; +import net.lenni0451.rivet.math.Size; +import net.lenni0451.rivet.text.model.TextOrigin; +import oxy.bascenario.utils.RivetUtil; +import oxy.bascenario.utils.components.TimelineDockExpandButton; +import oxy.bascenario.utils.math.Pair; + +import java.util.ArrayList; +import java.util.List; + +import static net.lenni0451.rivet.utils.MathUtils.roundMin; + +public class TimelineDockBar extends Container { + public TimelineDockBar() { + super(new HorizontalListLayout(12, false)); + + addChild(new TimelineDockExpandButton()); + + { + final List> options = new ArrayList<>(); + addChild(RivetUtil.dropdown("Add", 250f, options)); + } + } + + @Override + public Size computeIdealSize(Size constraints) { + return constraints.withHeight(24); + } +} diff --git a/editor/src/main/java/oxy/bascenario/editor/containers/timeline/TimelineTimeControl.java b/editor/src/main/java/oxy/bascenario/editor/containers/timeline/TimelineTimeControl.java new file mode 100644 index 00000000..74be8f11 --- /dev/null +++ b/editor/src/main/java/oxy/bascenario/editor/containers/timeline/TimelineTimeControl.java @@ -0,0 +1,42 @@ +package oxy.bascenario.editor.containers.timeline; + +import net.lenni0451.rivet.backend.render.Renderer; +import net.lenni0451.rivet.component.container.Container; +import net.lenni0451.rivet.component.impl.Label; +import net.lenni0451.rivet.layout.anchor.AnchorLayout; +import net.lenni0451.rivet.layout.anchor.AnchorOptions; +import net.lenni0451.rivet.math.Size; +import oxy.bascenario.editor.EditorValues; + +public class TimelineTimeControl extends Container { + private final Label timeLabel; + + public TimelineTimeControl() { + super(AnchorLayout.INSTANCE); + + timeLabel = new Label(""); + timeLabel.layoutOptions(AnchorOptions.EMPTY.withAnchorMinX(0.95f).withAnchorMinY(0.5f).pivot(0.5f, 0.5f)); + timeLabel.scale(.45f); + + this.addChild(timeLabel); + } + + @Override + public void render(Renderer renderer, Size size) { + timeLabel.text(format(EditorValues.instance().timestamp())); + super.render(renderer, size); + } + + @Override + public Size computeIdealSize(Size constraints) { + return constraints.withHeight(25); + } + + private String format(long ms) { + long millis = ms % 1000; + long second = (ms / 1000) % 60; + long minute = (ms / (1000 * 60)) % 60; + long hour = (ms / (1000 * 60 * 60)) % 24; + return (hour < 10 ? "0" + hour : hour) + ":" + (minute < 10 ? "0" + minute : minute) + ":" + (second < 10 ? "0" + second : second) + ":" + (millis < 10 ? "0" + millis : millis); + } +} diff --git a/editor/src/main/java/oxy/bascenario/editor/containers/track/TrackListContainer.java b/editor/src/main/java/oxy/bascenario/editor/containers/track/TrackListContainer.java new file mode 100644 index 00000000..c8c4f61e --- /dev/null +++ b/editor/src/main/java/oxy/bascenario/editor/containers/track/TrackListContainer.java @@ -0,0 +1,154 @@ +package oxy.bascenario.editor.containers.track; + +import lombok.Getter; +import lombok.experimental.Accessors; +import net.lenni0451.commons.color.Color; +import net.lenni0451.rivet.backend.render.Renderer; +import net.lenni0451.rivet.backend.text.ShapedText; +import net.lenni0451.rivet.component.Component; +import net.lenni0451.rivet.component.container.Container; +import net.lenni0451.rivet.component.container.ScrollContainer; +import net.lenni0451.rivet.input.keyboard.Key; +import net.lenni0451.rivet.input.keyboard.KeyEvent; +import net.lenni0451.rivet.input.mouse.MouseButton; +import net.lenni0451.rivet.input.mouse.MouseButtonEvent; +import net.lenni0451.rivet.input.mouse.MouseMoveEvent; +import net.lenni0451.rivet.input.mouse.MouseScrollEvent; +import net.lenni0451.rivet.layout.absolute.AbsoluteOptions; +import net.lenni0451.rivet.layout.list.VerticalListLayout; +import net.lenni0451.rivet.math.Size; +import net.lenni0451.rivet.text.model.TextOrigin; +import oxy.bascenario.editor.EditorValues; +import oxy.bascenario.editor.containers.track.component.ObjectComponent; +import oxy.bascenario.editor.containers.track.component.TrackContainer; + +@Accessors(fluent = true) +public class TrackListContainer extends ScrollContainer { + @Getter + private final Container container; + + @Getter + private final TrackSelectionManager selectionManager = new TrackSelectionManager(); + + public TrackListContainer() { + super(container = new Container(new VerticalListLayout(3, false)), true, true); + } + + private float prevWidth; + + @Override + public void render(Renderer renderer, Size size) { + if (prevWidth != size.width()) { + recalculateObjectPosition(); + this.requestLayoutRecalculation(); + } + prevWidth = size.width(); + + float y = -scrollY(); + int i = 0; + while (!(y >= size.height())) { + if (i % 2 == 0) { + renderer.fillRect(0, y, size.width(), 60, Color.fromRGB(24, 24, 24)); + } + + y += 60; + i++; + } + + drawTimestampLine(renderer, size); + + super.render(renderer, size); + + this.selectionManager.render(renderer); + EditorValues.instance().scroll(scrollX()); + } + + @Override + public Size computeIdealSize(Size constraints) { + return constraints; + } + + public void recalculateObjectPosition() { + for (Component component : container.children()) { + if (!(component instanceof TrackContainer track)) { + continue; + } + + for (Component child : track.children()) { + if (!(child instanceof ObjectComponent object)) { + continue; + } + + float newX = timestampToPosition(object.object().start, 0, EditorValues.instance().oneMilSecondWidth(), EditorValues.instance().scale()); + child.layoutOptions(new AbsoluteOptions(newX, 0)); + } + } + } + + @Override + protected boolean onComponentMouseMove(MouseMoveEvent event, Size bounds) { + if (event.buttons().contains(MouseButton.LEFT)) { + this.selectionManager.x1(event.x()); + this.selectionManager.y1(event.y()); + this.rivet().focusedComponent(this); + } + + return super.onComponentMouseMove(event, bounds); + } + + @Override + protected boolean onComponentMouseDown(MouseButtonEvent event, Size bounds) { + if (!super.onComponentMouseDown(event, bounds) && event.button() == MouseButton.LEFT) { + this.selectionManager.x(event.x()); + this.selectionManager.y(event.y()); + + this.selectionManager.x1(event.x()); + this.selectionManager.y1(event.y()); + + this.selectionManager.objects().clear(); + + this.rivet().focusedComponent(this); + } + return true; + } + + @Override + protected boolean onComponentMouseUp(MouseButtonEvent event, Size bounds) { + if (event.button() == MouseButton.LEFT) { + this.selectionManager.x(0); + this.selectionManager.y(0); + } + + return super.onComponentMouseUp(event, bounds); + } + + @Override + protected boolean onComponentKeyUp(KeyEvent event) { + if (event.key() == Key.DELETE) { + selectionManager.objects().forEach(component -> ((Container)component.parent()).removeChild(component)); + selectionManager().objects().clear(); + } + + return false; + } + + private static float timestampToPosition(long timestamp, float offsetX, float size, float scale) { + return offsetX + (timestamp * size * scale); + } + + @Override + protected boolean onComponentMouseScroll(MouseScrollEvent event, Size size) { + return true; + } + + private void drawTimestampLine(Renderer renderer, Size size) { + float x = -EditorValues.instance().scroll(); + while (!(x >= size.width())) { + float currentX = x - 0.5f; + + renderer.fillRect(currentX, 0, 1, size.height(), Color.fromRGB(48, 48, 48)); + + x += EditorValues.instance().oneSecondWidth(); + } + } +} diff --git a/editor/src/main/java/oxy/bascenario/editor/containers/track/TrackSelectionManager.java b/editor/src/main/java/oxy/bascenario/editor/containers/track/TrackSelectionManager.java new file mode 100644 index 00000000..ad97aba1 --- /dev/null +++ b/editor/src/main/java/oxy/bascenario/editor/containers/track/TrackSelectionManager.java @@ -0,0 +1,54 @@ +package oxy.bascenario.editor.containers.track; + +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; +import net.lenni0451.commons.color.Color; +import net.lenni0451.rivet.backend.render.Renderer; +import net.lenni0451.rivet.component.Component; +import net.lenni0451.rivet.component.container.Container; +import net.lenni0451.rivet.math.Rectangle; +import oxy.bascenario.editor.containers.track.component.ObjectComponent; + +import java.util.HashSet; +import java.util.Set; + +@Accessors(fluent = true) +@Setter +public class TrackSelectionManager { + @Getter + private Set objects = new HashSet<>(); + public boolean isSelected(ObjectComponent component) { + return objects.contains(component); + } + + private float x, y; + private float x1, y1; + + public void render(Renderer renderer) { + if (x == 0 && y == 0) { + return; + } + float minX = Math.min(x, x1), minY = Math.min(y, y1); + float maxX = Math.max(x, x1), maxY = Math.max(y, y1); + + renderer.fillRect(minX, minY, maxX - minX, maxY - minY, Color.WHITE.withAlphaF(0.4f)); + renderer.outlineRect(minX, minY, maxX - minX, maxY - minY, 2, Color.WHITE); + } + + public void addOrRemove(ObjectComponent component, Rectangle rectangle) { + if (x == 0 && y == 0) { + return; + } + + float minX = Math.min(x, x1), minY = Math.min(y, y1); + float maxX = Math.max(x, x1), maxY = Math.max(y, y1); + + if (maxX >= rectangle.x() && minX <= rectangle.maxX() && maxY >= rectangle.y() && minY <= rectangle.maxY() + + ((Container)component.parent()).parent().childBounds((Component) component.parent()).y()) { + objects.add(component); + } else { + objects.remove(component); + } + } +} diff --git a/editor/src/main/java/oxy/bascenario/editor/containers/track/TrackTabListContainer.java b/editor/src/main/java/oxy/bascenario/editor/containers/track/TrackTabListContainer.java new file mode 100644 index 00000000..d544e6e8 --- /dev/null +++ b/editor/src/main/java/oxy/bascenario/editor/containers/track/TrackTabListContainer.java @@ -0,0 +1,68 @@ +package oxy.bascenario.editor.containers.track; + +import lombok.Getter; +import lombok.experimental.Accessors; +import net.lenni0451.commons.color.Color; +import net.lenni0451.rivet.backend.render.Renderer; +import net.lenni0451.rivet.component.container.Container; +import net.lenni0451.rivet.component.container.ScrollContainer; +import net.lenni0451.rivet.input.mouse.MouseButton; +import net.lenni0451.rivet.layout.absolute.AbsoluteLayout; +import net.lenni0451.rivet.layout.absolute.AbsoluteOptions; +import net.lenni0451.rivet.layout.border.BorderLayout; +import net.lenni0451.rivet.layout.border.BorderPosition; +import net.lenni0451.rivet.layout.list.VerticalListLayout; +import net.lenni0451.rivet.math.Size; +import oxy.bascenario.Base; +import oxy.bascenario.editor.containers.sequencer.VideoSequencerContainer; +import oxy.bascenario.utils.components.ButtonImage; + +@Accessors(fluent = true) +public class TrackTabListContainer extends Container { + @Getter + private final Container container; + + public TrackTabListContainer(VideoSequencerContainer sequencerContainer) { + super(BorderLayout.DEFAULT); + + this.addChild(new UpperContainer(sequencerContainer), c -> c.layoutOptions(BorderPosition.TOP)); + + final ScrollContainer scrollContainer = new ScrollContainer(container = new Container(new VerticalListLayout()), false, true) { + @Override + public void render(Renderer renderer, Size size) { + scrollY(sequencerContainer.trackListContainer().scrollY(), true); + super.render(renderer, size); + } + }; + scrollContainer.barWidth().set(0f); + + this.addChild(scrollContainer, c -> c.layoutOptions(BorderPosition.CENTER)); + } + + @Override + public Size computeIdealSize(Size constraints) { + return constraints.withWidth(160); + } + + private static class UpperContainer extends Container { + public UpperContainer(VideoSequencerContainer sequencerContainer) { + super(AbsoluteLayout.INSTANCE); + + final ButtonImage add = new ButtonImage(Base.instance().assetsManager().texture("assets/base/uis/editor/icons/blender_icon_add.svg"), event -> { + if (event.button() != MouseButton.LEFT) { + return; + } + + sequencerContainer.createTrack(); + }); + add.hoverColor().set(Color.fromRGB(145, 218, 255)); + + this.addChild(add, c -> c.layoutOptions(new AbsoluteOptions(5, 5))); + } + + @Override + public Size computeIdealSize(Size constraints) { + return new Size(constraints.width(), 25); + } + } +} diff --git a/editor/src/main/java/oxy/bascenario/editor/containers/track/component/ObjectComponent.java b/editor/src/main/java/oxy/bascenario/editor/containers/track/component/ObjectComponent.java new file mode 100644 index 00000000..0be033b0 --- /dev/null +++ b/editor/src/main/java/oxy/bascenario/editor/containers/track/component/ObjectComponent.java @@ -0,0 +1,114 @@ +package oxy.bascenario.editor.containers.track.component; + +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; +import net.lenni0451.commons.color.Color; +import net.lenni0451.rivet.backend.render.Renderer; +import net.lenni0451.rivet.component.Component; +import net.lenni0451.rivet.input.keyboard.ModifierKey; +import net.lenni0451.rivet.input.mouse.MouseButton; +import net.lenni0451.rivet.input.mouse.MouseButtonEvent; +import net.lenni0451.rivet.input.mouse.MouseMoveEvent; +import net.lenni0451.rivet.math.Rectangle; +import net.lenni0451.rivet.math.Size; +import net.lenni0451.rivet.text.model.TextOrigin; +import oxy.bascenario.editor.EditorValues; +import oxy.bascenario.editor.containers.track.TrackListContainer; +import oxy.bascenario.editor.drag.SecondaryDragComponent; +import oxy.bascenario.editor.object.ObjectOrEvent; +import oxy.bascenario.utils.Launcher; +import oxy.bascenario.utils.NameUtils; + +import java.util.ArrayList; +import java.util.List; + +@Accessors(fluent = true) +public class ObjectComponent extends Component { + public static final Color OBJECT_COLOR = Color.fromRGB(98, 130, 163).darker(); + + private final TrackListContainer parent; + @Getter + private final ObjectOrEvent object; + + @Setter + private boolean dragging; + + public ObjectComponent(TrackListContainer parent, ObjectOrEvent object) { + this.parent = parent; + this.object = object; + } + + @Override + public void render(Renderer renderer, Size bounds) { + if (dragging) { + if (!rivet().dragAndDropManager().isDragging()) { + dragging = false; + } + return; + } + + renderer.fillRoundedRect(0, 0, bounds.width(), bounds.height(), 5, OBJECT_COLOR); + renderer.outlineRoundedRect(0, 0, bounds.width(), bounds.height(), 5, parent.selectionManager().isSelected(this) ? 2 : 1, Color.WHITE); + + renderer.scale(0.4f, () -> renderer.text(this.rivet().backend().font().shapeText(NameUtils.name(object.object), Color.WHITE), 20, 20, TextOrigin.Horizontal.VISUAL_LEFT, TextOrigin.Vertical.LOGICAL_TOP)); + + this.parent.selectionManager().addOrRemove(this, parent().childBounds(this)); + } + + @Override + protected boolean onComponentMouseDown(MouseButtonEvent event, Size bounds) { + if (this.parent.selectionManager().isSelected(this)) { + return true; + } + + if (!event.modifiers().contains(ModifierKey.CONTROL)) { + this.parent.selectionManager().objects().clear(); + } + + this.parent.selectionManager().objects().add(this); + return true; + } + + @Override + protected boolean onComponentMouseMove(MouseMoveEvent event, Size size) { + if (!event.buttons().contains(MouseButton.LEFT)) { + return false; + } + + if (rivet().dragAndDropManager().isDragging()) { + return true; + } + + Rectangle clickedBounds = parent().childBounds(this); + + final List list = new ArrayList<>(); + for (ObjectComponent component : parent.selectionManager().objects()) { + Rectangle bounds = component.parent().childBounds(component); + float x = -event.x() + (bounds.x() - clickedBounds.x()); + float y = -event.y(); + if (component.parent() != parent()) { + y += (component.parent().absoluteBounds().y() - parent().absoluteBounds().y()); + } + + list.add(new SecondaryDragComponent.Data(component, x, y)); + component.dragging(true); + } + + // Ehhh using mouseX and mouseY from EngineRenderer is a bit hacky here... + final SecondaryDragComponent dragComponent = new SecondaryDragComponent((float) Launcher.WINDOW.mouseX, (float) Launcher.WINDOW.mouseY, list); + + rivet().dragAndDropManager().startDrag( + dragComponent, + dragComponent, + (float) -Launcher.WINDOW.mouseX, (float) -Launcher.WINDOW.mouseY + ); + + return true; + } + + @Override + public Size computeIdealSize(Size constraints) { + return new Size(EditorValues.instance().oneMilSecondWidth() * object.duration, constraints.height()); + } +} diff --git a/editor/src/main/java/oxy/bascenario/editor/containers/track/component/TrackContainer.java b/editor/src/main/java/oxy/bascenario/editor/containers/track/component/TrackContainer.java new file mode 100644 index 00000000..0c50bda6 --- /dev/null +++ b/editor/src/main/java/oxy/bascenario/editor/containers/track/component/TrackContainer.java @@ -0,0 +1,56 @@ +package oxy.bascenario.editor.containers.track.component; + +import lombok.Getter; +import lombok.experimental.Accessors; +import net.lenni0451.rivet.component.Component; +import net.lenni0451.rivet.component.container.Container; +import net.lenni0451.rivet.dragdrop.DropEvent; +import net.lenni0451.rivet.layout.absolute.AbsoluteLayout; +import net.lenni0451.rivet.layout.absolute.AbsoluteOptions; +import net.lenni0451.rivet.math.Size; +import oxy.bascenario.editor.EditorValues; +import oxy.bascenario.editor.drag.FakeObjectComponent; +import oxy.bascenario.editor.drag.SecondaryDragComponent; + +@Accessors(fluent = true) +public class TrackContainer extends Container { + @Getter + private float height = 60f; + + public TrackContainer() { + super(AbsoluteLayout.INSTANCE); + + // So the user can scroll more by default. + this.addChild(new Component() { + @Override + public Size computeIdealSize(Size constraints) { + return new Size(1.0e-16f, 1.0e-16f); + } + }, c -> c.layoutOptions(new AbsoluteOptions(3000, 0, 5f, 5f))); + } + + @Override + protected boolean onComponentDrop(DropEvent event, Size bounds) { + EditorValues.instance().playing(false); + + event.dragData().forEach(data -> { + if (data instanceof FakeObjectComponent component) { + component.handle(this, event.x()); + } else if (data instanceof SecondaryDragComponent component) { + component.handle(this, event.x()); + } + }); + + return !event.dragData().isEmpty(); + } + + @Override + public Size computeIdealSize(Size constraints) { + Size idealSize = super.computeIdealSize(new Size(constraints.width(), height)); + return new Size(idealSize.width(), height); + } + + public int index() { + return parent().children().indexOf(this); + } +} diff --git a/editor/src/main/java/oxy/bascenario/editor/containers/track/component/TrackTabContainer.java b/editor/src/main/java/oxy/bascenario/editor/containers/track/component/TrackTabContainer.java new file mode 100644 index 00000000..07805242 --- /dev/null +++ b/editor/src/main/java/oxy/bascenario/editor/containers/track/component/TrackTabContainer.java @@ -0,0 +1,45 @@ +package oxy.bascenario.editor.containers.track.component; + +import net.lenni0451.commons.color.Color; +import net.lenni0451.rivet.backend.render.Renderer; +import net.lenni0451.rivet.component.container.Container; +import net.lenni0451.rivet.input.mouse.MouseButton; +import net.lenni0451.rivet.layout.absolute.AbsoluteLayout; +import net.lenni0451.rivet.layout.absolute.AbsoluteOptions; +import net.lenni0451.rivet.math.Size; +import net.lenni0451.rivet.text.model.TextOrigin; +import oxy.bascenario.Base; +import oxy.bascenario.editor.containers.sequencer.VideoSequencerContainer; +import oxy.bascenario.utils.components.ButtonImage; + +public class TrackTabContainer extends Container { + private final TrackContainer container; + + public TrackTabContainer(VideoSequencerContainer sequencerContainer, TrackContainer container) { + super(AbsoluteLayout.INSTANCE); + this.container = container; + + final ButtonImage trash = new ButtonImage(Base.instance().assetsManager().texture("assets/base/uis/editor/icons/blender_icon_trash.svg"), event -> { + if (event.button() != MouseButton.LEFT) { + return; + } + + sequencerContainer.removeTrack(container, this); + }); + trash.hoverColor().set(Color.fromRGB(145, 218, 255)); + this.addChild(trash, image -> image.layoutOptions(new AbsoluteOptions(5, 35))); + } + + @Override + public void render(Renderer renderer, Size bounds) { +// renderer.fillRect(0, 0, bounds.width(), bounds.height(), Color.RED); + renderer.scale(0.4f, () -> renderer.text(this.rivet().backend().font().shapeText("Track " + container.index(), Color.fromRGB(148, 148, 148)), 15, 25, TextOrigin.Horizontal.VISUAL_LEFT, TextOrigin.Vertical.LOGICAL_TOP)); + + super.render(renderer, bounds); + } + + @Override + public Size computeIdealSize(Size constraints) { + return new Size(constraints.width(), container.height()); + } +} diff --git a/editor/src/main/java/oxy/bascenario/editor/drag/FakeObjectComponent.java b/editor/src/main/java/oxy/bascenario/editor/drag/FakeObjectComponent.java new file mode 100644 index 00000000..89f770e7 --- /dev/null +++ b/editor/src/main/java/oxy/bascenario/editor/drag/FakeObjectComponent.java @@ -0,0 +1,89 @@ +package oxy.bascenario.editor.drag; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.experimental.Accessors; +import net.lenni0451.commons.color.Color; +import net.lenni0451.rivet.backend.render.Renderer; +import net.lenni0451.rivet.component.Component; +import net.lenni0451.rivet.component.container.Container; +import net.lenni0451.rivet.layout.absolute.AbsoluteOptions; +import net.lenni0451.rivet.math.Size; +import net.lenni0451.rivet.text.model.TextOrigin; +import oxy.bascenario.api.render.RenderLayer; +import oxy.bascenario.editor.EditorValues; +import oxy.bascenario.editor.containers.sequencer.VideoSequencerContainer; +import oxy.bascenario.editor.containers.track.TrackListContainer; +import oxy.bascenario.editor.containers.track.component.ObjectComponent; +import oxy.bascenario.editor.containers.track.component.TrackContainer; +import oxy.bascenario.editor.object.ObjectOrEvent; +import oxy.bascenario.utils.NameUtils; +import oxy.bascenario.utils.TimeCompiler; + +import static oxy.bascenario.editor.containers.track.component.ObjectComponent.OBJECT_COLOR; + +@AllArgsConstructor +@Accessors(fluent = true) +public class FakeObjectComponent extends Component { + @Getter + private final Object object; + private final long duration; + + @Override + public void render(Renderer renderer, Size bounds) { + renderer.fillRoundedRect(0, 0, bounds.width(), bounds.height(), 5, OBJECT_COLOR); + renderer.outlineRoundedRect(0, 0, bounds.width(), bounds.height(), 5, 1, Color.WHITE); + + renderer.scale(0.4f, () -> renderer.text(this.rivet().backend().font().shapeText(NameUtils.name(object), Color.WHITE), 20, 20, TextOrigin.Horizontal.VISUAL_LEFT, TextOrigin.Vertical.LOGICAL_TOP)); + } + + @Override + public Size computeIdealSize(Size constraints) { + float widthToAMil = EditorValues.instance().oneSecondWidth() / 1000f; + return new Size(widthToAMil * duration, 60); + } + + public void handle(TrackContainer trackContainer, float x) { + long time = (long) (x / EditorValues.instance().oneMilSecondWidth()); + long duration = TimeCompiler.compileTime(object()); + if (duration == Long.MAX_VALUE) { + duration = 1000L; + } + + final ObjectOrEvent object = new ObjectOrEvent(time, duration, object(), RenderLayer.ABOVE_DIALOGUE, true); + + TrackContainer track = trackContainer; + while (true) { + boolean accepted = true; + for (Component baseComponent : track.children()) { + if (!(baseComponent instanceof ObjectComponent component)) { + continue; + } + + long otherMin = component.object().start; + + long maxTime = component.object().start + component.object().duration; + long otherMaxTime = component.object().start + component.object().duration; + + if (maxTime >= otherMin && time <= otherMaxTime) { + accepted = false; + break; + } + } + + if (!accepted) { + int size = trackContainer.parent().children().size(); + if (track.index() >= size - 1) { + track = ((VideoSequencerContainer)((TrackListContainer)((Container)trackContainer.parent()).parent()).parent()).createTrack(); + break; + } else { + track = (TrackContainer) trackContainer.parent().children().get(track.index() + 1); + } + } else { + break; + } + } + + track.addChild(new ObjectComponent((TrackListContainer) ((Container)trackContainer.parent()).parent(), object), c -> c.layoutOptions(new AbsoluteOptions(x, 0))); + } +} diff --git a/editor/src/main/java/oxy/bascenario/editor/drag/SecondaryDragComponent.java b/editor/src/main/java/oxy/bascenario/editor/drag/SecondaryDragComponent.java new file mode 100644 index 00000000..d977df45 --- /dev/null +++ b/editor/src/main/java/oxy/bascenario/editor/drag/SecondaryDragComponent.java @@ -0,0 +1,123 @@ +package oxy.bascenario.editor.drag; + +import lombok.RequiredArgsConstructor; +import net.lenni0451.commons.color.Color; +import net.lenni0451.rivet.backend.render.Renderer; +import net.lenni0451.rivet.component.Component; +import net.lenni0451.rivet.component.container.Container; +import net.lenni0451.rivet.layout.absolute.AbsoluteOptions; +import net.lenni0451.rivet.math.Size; +import net.lenni0451.rivet.text.model.TextOrigin; +import oxy.bascenario.editor.EditorValues; +import oxy.bascenario.editor.containers.sequencer.VideoSequencerContainer; +import oxy.bascenario.editor.containers.track.TrackListContainer; +import oxy.bascenario.editor.containers.track.component.ObjectComponent; +import oxy.bascenario.editor.containers.track.component.TrackContainer; +import oxy.bascenario.utils.NameUtils; + +import java.util.*; + +import static oxy.bascenario.editor.containers.track.component.ObjectComponent.OBJECT_COLOR; + +@RequiredArgsConstructor +public class SecondaryDragComponent extends Component { + private final float mouseX, mouseY; + private final List list; + + @Override + public void render(Renderer renderer, Size size) { + for (Data data : list) { + renderer.translate(mouseX + data.offsetX, mouseY + data.offsetY, () -> { + float width = EditorValues.instance().oneMilSecondWidth() * data.object.object().duration; + + renderer.fillRoundedRect(0, 0, width, 60, 5, OBJECT_COLOR); + renderer.outlineRoundedRect(0, 0, width, 60, 5, 1, Color.WHITE); + + renderer.scale(0.4f, () -> renderer.text(this.rivet().backend().font().shapeText(NameUtils.name(data.object.object().object), Color.WHITE), 20, 20, TextOrigin.Horizontal.VISUAL_LEFT, TextOrigin.Vertical.LOGICAL_TOP)); + }); + } + } + + + @Override + public Size computeIdealSize(Size size) { + return size; + } + + public record Data(ObjectComponent object, float offsetX, float offsetY) { + public void handle(final TrackContainer container, float x) { + final float dataX = Math.max(0, x + offsetX); + long time = Math.max(0, (long) (dataX / EditorValues.instance().oneMilSecondWidth())); + + TrackContainer track = container; + while (true) { + boolean accepted = true; + for (Component baseComponent : track.children()) { + if (!(baseComponent instanceof ObjectComponent component) || component == object) { + continue; + } + + long otherMin = component.object().start; + + long maxTime = object.object().start + object.object().duration; + long otherMaxTime = component.object().start + component.object().duration; + + if (maxTime >= otherMin && time <= otherMaxTime) { + accepted = false; + break; + } + } + + if (!accepted) { + int size = container.parent().children().size(); + if (track.index() >= size - 1) { + track = ((VideoSequencerContainer)((TrackListContainer)((Container)container.parent()).parent()).parent()).createTrack(); + } else { + track = (TrackContainer) container.parent().children().get(track.index() + 1); + } + } else { + break; + } + } + + object.dragging(false); + + object.layoutOptions(new AbsoluteOptions(dataX, 0)); + object.object().start = time; + track.addChild(object); + } + } + + public void handle(final TrackContainer container, float x) { + if (list.isEmpty()) { + return; + } + + list.sort(Comparator.comparingInt(data -> ((TrackContainer)data.object.parent()).index())); + + int firstIndex = ((TrackContainer)list.getFirst().object.parent()).index(); + + final Map tempMap = new HashMap<>(); + for (Data data : list) { // Remove first. + tempMap.put(data, ((TrackContainer)data.object.parent()).index()); + ((Container)data.object.parent()).removeChild(data.object); + } + + for (Data data : list) { + TrackContainer trackContainer = container; + int offset = tempMap.get(data) - firstIndex; + if (offset > 0) { + int newIndex = trackContainer.index() + offset; + + int size = trackContainer.parent().children().size(); + if (newIndex >= size) { + trackContainer = ((VideoSequencerContainer)((TrackListContainer)((Container)container.parent()).parent()).parent()).createTrack(); + } else { + trackContainer = (TrackContainer) container.parent().children().get(trackContainer.index() + 1); + } + } + + data.handle(trackContainer, x); + } + } +} diff --git a/editor/src/main/java/oxy/bascenario/editor/inspector/Inspector.java b/editor/src/main/java/oxy/bascenario/editor/inspector/Inspector.java deleted file mode 100644 index 9766ceca..00000000 --- a/editor/src/main/java/oxy/bascenario/editor/inspector/Inspector.java +++ /dev/null @@ -1,204 +0,0 @@ -package oxy.bascenario.editor.inspector; - -import imgui.ImGui; -import lombok.RequiredArgsConstructor; -import oxy.bascenario.api.effects.ScreenEffect; -import oxy.bascenario.api.effects.Weather; -import oxy.bascenario.api.event.*; -import oxy.bascenario.api.event.background.ClearBackgroundEvent; -import oxy.bascenario.api.event.background.SetBackgroundEvent; -import oxy.bascenario.api.event.color.ColorOverlayEvent; -import oxy.bascenario.api.event.animation.PlayAnimationEvent; -import oxy.bascenario.api.event.animation.SpriteAnimationEvent; -import oxy.bascenario.api.event.animation.StopAnimationEvent; -import oxy.bascenario.api.event.color.SetColorEvent; -import oxy.bascenario.api.event.dialogue.*; -import oxy.bascenario.api.event.element.ElementEffectEvent; -import oxy.bascenario.api.event.element.focus.FocusElementEvent; -import oxy.bascenario.api.event.element.values.PositionElementEvent; -import oxy.bascenario.api.event.element.values.RotateElementEvent; -import oxy.bascenario.api.event.log.AddLogEvent; -import oxy.bascenario.api.event.sound.SoundVolumeEvent; -import oxy.bascenario.api.render.RenderLayer; -import oxy.bascenario.api.render.elements.LocationInfo; -import oxy.bascenario.api.render.elements.Preview; -import oxy.bascenario.api.render.elements.Sprite; -import oxy.bascenario.api.render.elements.emoticon.Emoticon; -import oxy.bascenario.api.render.elements.image.AnimatedImage; -import oxy.bascenario.api.render.elements.image.Image; -import oxy.bascenario.api.render.elements.shape.Circle; -import oxy.bascenario.api.render.elements.shape.Rectangle; -import oxy.bascenario.api.render.elements.text.AnimatedText; -import oxy.bascenario.api.render.elements.text.Text; -import oxy.bascenario.api.utils.math.Vec2; -import oxy.bascenario.editor.inspector.impl.events.*; -import oxy.bascenario.editor.inspector.impl.objects.*; -import oxy.bascenario.editor.timeline.ObjectOrEvent; -import oxy.bascenario.editor.screen.BaseScenarioEditorScreen; -import oxy.bascenario.editor.timeline.Timeline; -import oxy.bascenario.editor.utils.AudioUtils; -import oxy.bascenario.editor.utils.NameUtils; -import oxy.bascenario.editor.utils.SoundAsElement; -import oxy.bascenario.editor.utils.TimeCompiler; -import oxy.bascenario.utils.ImGuiUtils; - -import java.util.*; - -@RequiredArgsConstructor -public class Inspector { - private final BaseScenarioEditorScreen screen; - private final Timeline timeline; - - @SuppressWarnings("ALL") - public void render() { - ImGui.begin("Inspector"); - final ObjectOrEvent object = timeline.getSelectedObject(); - if (object == null) { - ImGui.end(); - return; - } - - ImGui.text(NameUtils.name(object.object)); - - // I seriously can't think of a proper way to implement this in the editor, so we going with the always wait approch. - boolean requireWait = true; // ImGuiUtils.checkbox("Wait For Dialogue", object.requireWait); - RenderLayer layer = null; - Vec2 vec2 = null; - if (object.layer != null) { - layer = RenderLayer.values()[ImGuiUtils.combo("Render Layer", object.layer.ordinal(), RenderLayer.getAlls())]; - - if (object.vec2 != null) { - float x = ImGuiUtils.sliderFloat("X", object.vec2.x(), -1920, 1920); - float y = ImGuiUtils.sliderFloat("Y", object.vec2.y(), -1080, 1080); - vec2 = new Vec2(x, y); - } - } - - ImGui.separator(); - - final Object old = object.object; - object.object = switch (object.object) { - case Preview preview -> PreviewInspector.render(preview); - case Emoticon emoticon -> EmoticonInspector.render(emoticon); - case LocationInfo info -> LocationInfoInspector.render(info); - case Text text -> TextInspector.render(screen.getScenario(), text); - case AnimatedText text -> TextInspector.render(screen.getScenario(), text); - case AnimatedImage image -> ImageInspector.render(image); - case Image image -> ImageInspector.render(image); - case Circle circle -> ShapeInspector.render(circle); - case Rectangle rectangle -> ShapeInspector.render(rectangle); - case Sprite sprite -> SpriteInspector.render(sprite); - - case StartDialogueEvent event -> DialogueInspector.render(screen.getScenario(), event); - case AddDialogueEvent event -> DialogueInspector.render(screen.getScenario(), event); - case RedirectDialogueEvent event -> DialogueInspector.render(event); - - case AddLogEvent event -> LogInspector.render(screen.getScenario(), event); - - case ShowQuestionSelectionEvent event -> OptionsInspector.render(event); - case ShowOptionsEvent event -> OptionsInspector.render(event); - - case ColorOverlayEvent event -> ColorInspector.render(event); - case SetColorEvent event -> ColorInspector.render(event); - - case PlayAnimationEvent event -> AnimationInspector.render(event); - case StopAnimationEvent event -> AnimationInspector.render(event); - case SpriteAnimationEvent event -> AnimationInspector.render(event); - - case ElementEffectEvent event -> ElementEffectInspector.render(event); - -// case PlaySoundEvent event -> SoundInspector.render(screen.getScenario(), event); - case SoundAsElement sound -> SoundInspector.render(screen.getScenario(), sound); - case SoundVolumeEvent event -> SoundInspector.render(event); -// case StopSoundEvent event -> SoundInspector.render(event); - - case PositionElementEvent event -> PositionInspector.render(event); - case RotateElementEvent event -> PositionInspector.render(event); - - case SetBackgroundEvent event -> BackgroundInspector.render(event); - case ClearBackgroundEvent event -> BackgroundInspector.render(event); - - case FocusElementEvent event -> new FocusElementEvent(ImGuiUtils.inputInt("Track Id", event.id())); - - case ShowButtonsEvent event -> new ShowButtonsEvent(ImGuiUtils.checkbox("Show", event.show())); - - case SetWeatherEvent event -> new SetWeatherEvent(Weather.values()[ImGuiUtils.combo("Weather", event.weather().ordinal(), Weather.getAlls())]); - case ScreenEffectEvent event -> { - ScreenEffectEvent.Type type = ScreenEffectEvent.Type.values()[ImGuiUtils.combo("Type", event.type().ordinal(), ScreenEffectEvent.Type.getAlls())]; - if (type == ScreenEffectEvent.Type.CLEAR_ALL) { - yield new ScreenEffectEvent(ScreenEffectEvent.Type.CLEAR_ALL, ScreenEffect.GRAY_FILTER); // effect here is placeholder - } else { - yield new ScreenEffectEvent(type, - ScreenEffect.values()[ImGuiUtils.combo("Effect", event.effect().ordinal(), ScreenEffect.getAlls())]); - } - } - - case ScreenTransitionEvent event -> TransitionInspector.render(event); - case PopupEvent event -> ImageInspector.render(event); - default -> old; - }; - - boolean objectEquals = !old.equals(object.object); - if (objectEquals || requireWait != object.requireWait || object.layer != layer || !Objects.equals(vec2, object.vec2)) { - final int oldTrack = object.track; - long oldDuration = old instanceof SoundAsElement sound ? AudioUtils.toDuration(screen.getScenario().name(), sound) : TimeCompiler.compileTime(old); - if (objectEquals) { - long duration = object.object instanceof SoundAsElement sound ? AudioUtils.toDuration(screen.getScenario().name(), sound) : TimeCompiler.compileTime(object.object); - if (duration == Long.MAX_VALUE) { - duration = 0; - } - if (oldDuration == Long.MAX_VALUE) { - oldDuration = 0; - } - duration += Math.max(0, object.duration - oldDuration); - object.duration = duration; - - object.track = findNonOccupiedSlot(object.track, object.start, object.duration, object); - } - - final boolean oldWait = object.requireWait; - final RenderLayer oldLayer = object.layer; - final Vec2 oldVec2 = object.vec2; - final long oldTime = oldDuration == Long.MAX_VALUE ? 0 : oldDuration; - timeline.queueUndo(() -> { - object.object = old; - object.duration = oldTime; - object.requireWait = oldWait; - object.layer = oldLayer; - object.vec2 = oldVec2; - object.track = oldTrack; - }); - - object.requireWait = requireWait; - object.layer = layer; - object.vec2 = vec2; - } - - ImGui.end(); - } - - private int findNonOccupiedSlot(int initId, long time, long duration, ObjectOrEvent object1) { - final Map> map = new HashMap<>(); - timeline.getObjects().forEach(object -> map.computeIfAbsent(object.track, n -> new ArrayList<>()).add(object)); - - int i = initId; - List track; - while ((track = map.get(i)) != null) { - boolean occupied = false; - for (ObjectOrEvent object : track) { - final long maxTime = object.start + object.duration, minTime = object.start; - if (maxTime >= time && minTime <= time + duration && object1 != object) { - occupied = true; - break; - } - } - - if (!occupied) { - return i; - } - i++; - } - - return i; - } -} diff --git a/editor/src/main/java/oxy/bascenario/editor/inspector/impl/events/AnimationInspector.java b/editor/src/main/java/oxy/bascenario/editor/inspector/impl/events/AnimationInspector.java deleted file mode 100644 index 564697b1..00000000 --- a/editor/src/main/java/oxy/bascenario/editor/inspector/impl/events/AnimationInspector.java +++ /dev/null @@ -1,40 +0,0 @@ -package oxy.bascenario.editor.inspector.impl.events; - -import oxy.bascenario.Base; -import oxy.bascenario.api.event.animation.PlayAnimationEvent; -import oxy.bascenario.api.event.animation.SpriteAnimationEvent; -import oxy.bascenario.api.event.animation.StopAnimationEvent; -import oxy.bascenario.utils.ImGuiUtils; -import oxy.bascenario.utils.math.Pair; - -public class AnimationInspector { - public static PlayAnimationEvent render(PlayAnimationEvent event) { - PlayAnimationEvent.Builder builder = event.toBuilder(); - builder.id(Math.abs(ImGuiUtils.inputInt("Target Track", event.id()))); - - final Pair alls = Base.instance().animationManager().getAllAnimations(event.name()); - builder.name(alls.right()[ImGuiUtils.combo("Animation Name", alls.left(), alls.right())]); - - builder.loop(ImGuiUtils.checkbox("Loop", event.loop())); - return builder.build(); - } - - public static StopAnimationEvent render(StopAnimationEvent event) { - StopAnimationEvent.Builder builder = event.toBuilder(); - builder.id(Math.abs(ImGuiUtils.inputInt("Target Track", event.id()))); - - final Pair alls = Base.instance().animationManager().getAllAnimations(event.name()); - builder.name(alls.right()[ImGuiUtils.combo("Animation Name", alls.left(), alls.right())]); - return builder.build(); - } - - public static SpriteAnimationEvent render(SpriteAnimationEvent event) { - SpriteAnimationEvent.Builder builder = event.toBuilder(); - builder.id(Math.abs(ImGuiUtils.inputInt("Target Track", event.id()))); - builder.trackIndex(Math.abs(ImGuiUtils.inputInt("Layer", event.trackIndex()))); - builder.mixTime(Math.abs(ImGuiUtils.sliderFloat("Mix/Lerp Time", event.mixTime(), 0, 20))); - builder.animationName(ImGuiUtils.inputText("Animation Name", event.animationName())); - builder.loop(ImGuiUtils.checkbox("Loop", event.loop())); - return builder.build(); - } -} diff --git a/editor/src/main/java/oxy/bascenario/editor/inspector/impl/events/BackgroundInspector.java b/editor/src/main/java/oxy/bascenario/editor/inspector/impl/events/BackgroundInspector.java deleted file mode 100644 index 68fdbadf..00000000 --- a/editor/src/main/java/oxy/bascenario/editor/inspector/impl/events/BackgroundInspector.java +++ /dev/null @@ -1,30 +0,0 @@ -package oxy.bascenario.editor.inspector.impl.events; - -import oxy.bascenario.api.event.background.ClearBackgroundEvent; -import oxy.bascenario.api.event.background.SetBackgroundEvent; -import oxy.bascenario.api.utils.FileInfo; -import oxy.bascenario.editor.miniuis.AssetsUI; -import oxy.bascenario.utils.ImGuiUtils; - -public class BackgroundInspector { - private static FileInfo last; - - public static SetBackgroundEvent render(SetBackgroundEvent event) { - SetBackgroundEvent.Builder builder = event.toBuilder(); - builder.duration(Math.abs(ImGuiUtils.sliderInt("Fade Duration", event.duration(), 0, 10000))); - AssetsUI.pick("Pick Background!", file -> last = file, "png,jpg"); - - if (last != null) { - builder.background(last); - last = null; - } - - return builder.build(); - } - - public static ClearBackgroundEvent render(ClearBackgroundEvent event) { - ClearBackgroundEvent.Builder builder = event.toBuilder(); - builder.duration(Math.abs(ImGuiUtils.sliderInt("Fade Duration", event.duration(), 0, 10000))); - return builder.build(); - } -} diff --git a/editor/src/main/java/oxy/bascenario/editor/inspector/impl/events/ColorInspector.java b/editor/src/main/java/oxy/bascenario/editor/inspector/impl/events/ColorInspector.java deleted file mode 100644 index 2b101aab..00000000 --- a/editor/src/main/java/oxy/bascenario/editor/inspector/impl/events/ColorInspector.java +++ /dev/null @@ -1,36 +0,0 @@ -package oxy.bascenario.editor.inspector.impl.events; - -import oxy.bascenario.api.event.color.ColorOverlayEvent; -import oxy.bascenario.api.event.color.SetColorEvent; -import oxy.bascenario.api.render.RenderLayer; -import oxy.bascenario.utils.ImGuiUtils; - -import java.util.Optional; - -public class ColorInspector { - public static SetColorEvent render(SetColorEvent event) { - SetColorEvent.Builder builder = event.toBuilder(); - builder.id(Math.abs(ImGuiUtils.inputInt("Target Track", event.id()))); - builder.duration(ImGuiUtils.sliderInt("Fade Duration", event.duration(), 0, 60000)); - - builder.color(ImGuiUtils.color("Color", event.color())); - return builder.build(); - } - - public static ColorOverlayEvent render(ColorOverlayEvent event) { - ColorOverlayEvent.Builder builder = event.toBuilder(); - if (event.id().isPresent()) { - builder.id(Optional.of(Math.abs(ImGuiUtils.inputInt("Target Track", event.id().get())))); - } - - builder.duration(ImGuiUtils.sliderInt("Fade Duration", event.duration(), 0, 60000)); - - builder.color(ImGuiUtils.color("Color", event.color())); - - if (event.id().isEmpty()) { - builder.renderLayer(RenderLayer.values()[ImGuiUtils.combo("Render Layer", event.renderLayer().ordinal(), RenderLayer.getAlls())]); - } - - return builder.build(); - } -} diff --git a/editor/src/main/java/oxy/bascenario/editor/inspector/impl/events/DialogueInspector.java b/editor/src/main/java/oxy/bascenario/editor/inspector/impl/events/DialogueInspector.java deleted file mode 100644 index 4d2de83d..00000000 --- a/editor/src/main/java/oxy/bascenario/editor/inspector/impl/events/DialogueInspector.java +++ /dev/null @@ -1,96 +0,0 @@ -package oxy.bascenario.editor.inspector.impl.events; - -import imgui.ImGui; -import imgui.type.ImBoolean; -import oxy.bascenario.api.Scenario; -import oxy.bascenario.api.event.dialogue.AddDialogueEvent; -import oxy.bascenario.api.event.dialogue.RedirectDialogueEvent; -import oxy.bascenario.api.event.dialogue.StartDialogueEvent; -import oxy.bascenario.api.event.dialogue.enums.OffsetType; -import oxy.bascenario.api.event.dialogue.enums.TextOffset; -import oxy.bascenario.api.render.elements.Dialogue; -import oxy.bascenario.api.render.elements.text.font.FontType; -import oxy.bascenario.editor.inspector.impl.objects.TextInspector; -import oxy.bascenario.utils.ImGuiUtils; - -import java.util.ArrayList; -import java.util.List; - -public class DialogueInspector { - public static RedirectDialogueEvent render(RedirectDialogueEvent event) { - return new RedirectDialogueEvent(ImGuiUtils.inputInt("Dialogue Index", event.index())); - } - - public static AddDialogueEvent render(Scenario.Builder scenario, AddDialogueEvent event) { - AddDialogueEvent.Builder builder = event.toBuilder(); - - builder.index(ImGuiUtils.inputInt("Dialogue Index", event.index())); - builder.newLine(ImGuiUtils.checkbox("New Line", event.newLine())); - - final List list = new ArrayList<>(); - if (ImGui.button("New dialogue!##" + ImGuiUtils.COUNTER++)) { - list.add(Dialogue.builder().build()); - } - - for (Dialogue dialogue : event.dialogues()) { - final ImBoolean imBoolean = new ImBoolean(true); - if (ImGui.collapsingHeader("Dialogue##" + ImGuiUtils.COUNTER++, imBoolean)) { - dialogue = render(scenario, dialogue); - } - - if (imBoolean.get()) { - list.add(dialogue); - } - } - builder.dialogues(list.toArray(new Dialogue[0])); - - return builder.build(); - } - - public static StartDialogueEvent render(Scenario.Builder scenario, StartDialogueEvent event) { - StartDialogueEvent.Builder builder = event.toBuilder(); - - OffsetType offsetType = OffsetType.values()[ImGuiUtils.combo("Text Offset", event.offset().type().ordinal(), OffsetType.getAlls())]; - float offset = event.offset().offset(); - if (offsetType == OffsetType.Custom) { - offset = ImGuiUtils.sliderFloat("Offset X", offset, 0, 1920); - } - builder.offset(new TextOffset(offsetType, offset)); - - ImGui.separatorText(""); - - builder.type(FontType.values()[ImGuiUtils.combo("Font Type", event.type().ordinal(), FontType.getAlls())]); - builder.index(ImGuiUtils.inputInt("Dialogue Index", event.index())); - builder.name(ImGuiUtils.inputText("Name", event.name())); - builder.association(ImGuiUtils.inputText("Association", event.association())); - builder.background(ImGuiUtils.checkbox("Background", event.background())); - - final List list = new ArrayList<>(); - boolean add = ImGui.button("New dialogue!##" + ImGuiUtils.COUNTER++); - - for (Dialogue dialogue : event.dialogues()) { - final ImBoolean imBoolean = new ImBoolean(true); - if (ImGui.collapsingHeader("Dialogue##" + ImGuiUtils.COUNTER++, imBoolean)) { - dialogue = render(scenario, dialogue); - } - - if (imBoolean.get()) { - list.add(dialogue); - } - } - if (add) { - list.add(Dialogue.builder().add("This is a dialogue text!").build()); - } - builder.dialogues(list.toArray(new Dialogue[0])); - - return builder.build(); - } - - public static Dialogue render(Scenario.Builder scenario, Dialogue dialogue) { - Dialogue.Builder builder = dialogue.toBuilder(); - builder.offset(ImGuiUtils.sliderFloat("Text Offset", builder.offset(), -1920, 1920)); - builder.playSpeed(ImGuiUtils.sliderFloat("Play Speed", builder.playSpeed(), 0.01f, 50)); - builder.dialogue(TextInspector.render(scenario, dialogue.getDialogue())); - return builder.build(); - } -} diff --git a/editor/src/main/java/oxy/bascenario/editor/inspector/impl/events/ElementEffectInspector.java b/editor/src/main/java/oxy/bascenario/editor/inspector/impl/events/ElementEffectInspector.java deleted file mode 100644 index fc1580e5..00000000 --- a/editor/src/main/java/oxy/bascenario/editor/inspector/impl/events/ElementEffectInspector.java +++ /dev/null @@ -1,68 +0,0 @@ -package oxy.bascenario.editor.inspector.impl.events; - -import oxy.bascenario.api.effects.Effect; -import oxy.bascenario.api.event.element.ElementEffectEvent; -import oxy.bascenario.api.utils.math.Axis; -import oxy.bascenario.utils.ImGuiUtils; - -public class ElementEffectInspector { - public static ElementEffectEvent render(ElementEffectEvent event) { - ElementEffectEvent.Builder builder = event.toBuilder(); - builder.id(Math.abs(ImGuiUtils.inputInt("Target Track", event.id()))); - Effect effect = Effect.values()[ImGuiUtils.combo("Effect Type", event.effect().ordinal(), Effect.getAlls())]; - builder.effect(effect); - Object[] values; - - if (event.type() == ElementEffectEvent.Type.ADD) { - switch (effect) { - case HOLOGRAM -> { - Axis old = Axis.X; - if (event.values().length == 1 && event.values()[0] instanceof Axis axis) { - old = axis; - } - - values = new Object[] {Axis.values()[ImGuiUtils.combo("Axis", old.ordinal(), Axis.getAlls())]}; - } - - case RAINBOW -> { - Axis old = Axis.X; - float oldFloat = 1.5f; - if (event.values().length == 2 && event.values()[0] instanceof Axis axis && event.values()[1] instanceof Float f) { - old = axis; - oldFloat = f; - } - - values = new Object[] { - Axis.values()[ImGuiUtils.combo("Axis", old.ordinal(), Axis.getAlls())], - ImGuiUtils.sliderFloat("Distance", oldFloat, 0.01f, 500) - }; - } - - case BLUR -> { - int old = 5; - if (event.values().length == 1 && event.values()[0] instanceof Integer integer) { - old = integer; - } - - values = new Object[] {ImGuiUtils.sliderInt("Radius", old, 0, 100)}; - } - - case OUTLINE -> { - int old = 1; - if (event.values().length == 2 && event.values()[0] instanceof Integer integer) { - old = integer; - } - - values = new Object[] {ImGuiUtils.sliderInt("Radius", old, 0, 100), 1 << 1}; - } - default -> throw new IllegalStateException("Unexpected value: " + event.effect()); - } - } else { - values = new Object[] {}; - } - - builder.values(values); - - return builder.build(); - } -} diff --git a/editor/src/main/java/oxy/bascenario/editor/inspector/impl/events/LogInspector.java b/editor/src/main/java/oxy/bascenario/editor/inspector/impl/events/LogInspector.java deleted file mode 100644 index 92da02d5..00000000 --- a/editor/src/main/java/oxy/bascenario/editor/inspector/impl/events/LogInspector.java +++ /dev/null @@ -1,58 +0,0 @@ -package oxy.bascenario.editor.inspector.impl.events; - -import imgui.ImGui; -import imgui.type.ImBoolean; -import oxy.bascenario.api.Scenario; -import oxy.bascenario.api.event.dialogue.AddDialogueEvent; -import oxy.bascenario.api.event.dialogue.RedirectDialogueEvent; -import oxy.bascenario.api.event.dialogue.StartDialogueEvent; -import oxy.bascenario.api.event.dialogue.enums.OffsetType; -import oxy.bascenario.api.event.dialogue.enums.TextOffset; -import oxy.bascenario.api.event.log.AddLogEvent; -import oxy.bascenario.api.render.elements.Dialogue; -import oxy.bascenario.api.render.elements.text.font.FontType; -import oxy.bascenario.editor.inspector.impl.objects.TextInspector; -import oxy.bascenario.utils.ImGuiUtils; - -import java.util.ArrayList; -import java.util.List; - -public class LogInspector { - public static AddLogEvent render(Scenario.Builder scenario, AddLogEvent event) { - AddLogEvent.Builder builder = event.toBuilder(); - - AddLogEvent.Type type = AddLogEvent.Type.values()[ImGuiUtils.combo("Action", event.type().ordinal(), AddLogEvent.Type.getAlls())]; - ImGui.separatorText(""); - if (type == AddLogEvent.Type.CLEAR) { - OffsetType offsetType = OffsetType.values()[ImGuiUtils.combo("Text Offset", event.offset().type().ordinal(), OffsetType.getAlls())]; - float offset = event.offset().offset(); - if (offsetType == OffsetType.Custom) { - offset = ImGuiUtils.sliderFloat("Offset X", offset, 0, 1920); - } - builder.offset(new TextOffset(offsetType, offset)); - ImGui.separatorText(""); - } - builder.type(type); - - builder.index(ImGuiUtils.inputInt("Dialogue Index", event.index())); - - final List list = new ArrayList<>(); - if (ImGui.button("New log!##" + ImGuiUtils.COUNTER++)) { - list.add(Dialogue.builder().build()); - } - - for (Dialogue dialogue : event.dialogues()) { - final ImBoolean imBoolean = new ImBoolean(true); - if (ImGui.collapsingHeader("Log##" + ImGuiUtils.COUNTER++, imBoolean)) { - dialogue = DialogueInspector.render(scenario, dialogue); - } - - if (imBoolean.get()) { - list.add(dialogue); - } - } - builder.dialogues(list.toArray(new Dialogue[0])); - - return builder.build(); - } -} diff --git a/editor/src/main/java/oxy/bascenario/editor/inspector/impl/events/OptionsInspector.java b/editor/src/main/java/oxy/bascenario/editor/inspector/impl/events/OptionsInspector.java deleted file mode 100644 index 47354515..00000000 --- a/editor/src/main/java/oxy/bascenario/editor/inspector/impl/events/OptionsInspector.java +++ /dev/null @@ -1,68 +0,0 @@ -package oxy.bascenario.editor.inspector.impl.events; - -import imgui.ImGui; -import imgui.type.ImBoolean; -import oxy.bascenario.api.event.dialogue.ShowOptionsEvent; -import oxy.bascenario.api.event.dialogue.ShowQuestionSelectionEvent; -import oxy.bascenario.api.render.RenderLayer; -import oxy.bascenario.api.render.elements.text.font.FontType; -import oxy.bascenario.utils.ImGuiUtils; - -import java.util.*; - -public class OptionsInspector { - public static ShowQuestionSelectionEvent render(ShowQuestionSelectionEvent event) { - FontType type = FontType.values()[ImGuiUtils.combo("Font Type", event.type().ordinal(), FontType.getAlls())]; - String question = ImGuiUtils.inputText("Question", event.question()); - - boolean add = ImGui.button("New Answer!"); - - final List answers = new ArrayList<>(); - for (ShowQuestionSelectionEvent.Answer answer : event.answers()) { - final ImBoolean imBoolean = new ImBoolean(true); - String option = answer.answer(); - int redirect = answer.dialogueIndex(); - boolean grayedOut = answer.grayedOut(); - if (ImGui.collapsingHeader("Option##" + ImGuiUtils.COUNTER++, imBoolean)) { - option = ImGuiUtils.inputText("Option", option); - redirect = ImGuiUtils.inputInt("Redirect To", redirect); - grayedOut = ImGuiUtils.checkbox("Grayed Out", grayedOut); - } - if (imBoolean.get()) { - answers.add(new ShowQuestionSelectionEvent.Answer(redirect, option, grayedOut)); - } - } - - if (add) { - answers.add(new ShowQuestionSelectionEvent.Answer(0, "", false)); - } - - return new ShowQuestionSelectionEvent(type, question, answers); - } - - public static ShowOptionsEvent render(ShowOptionsEvent event) { - final Map options = new LinkedHashMap<>(); - - FontType type = FontType.values()[ImGuiUtils.combo("Font Type", event.type().ordinal(), FontType.getAlls())]; - boolean add = ImGui.button("New Option!"); - - for (Map.Entry entry : event.options().entrySet()) { - final ImBoolean imBoolean = new ImBoolean(true); - String option = entry.getKey(); - int redirect = entry.getValue(); - if (ImGui.collapsingHeader("Option##" + ImGuiUtils.COUNTER++, imBoolean)) { - option = ImGuiUtils.inputText("Option", entry.getKey()); - redirect = ImGuiUtils.inputInt("Redirect to", entry.getValue()); - } - if (imBoolean.get()) { - options.put(option, redirect); - } - } - - if (add) { - options.put("", 0); - } - - return new ShowOptionsEvent(type, options); - } -} diff --git a/editor/src/main/java/oxy/bascenario/editor/inspector/impl/events/PositionInspector.java b/editor/src/main/java/oxy/bascenario/editor/inspector/impl/events/PositionInspector.java deleted file mode 100644 index 1033f82c..00000000 --- a/editor/src/main/java/oxy/bascenario/editor/inspector/impl/events/PositionInspector.java +++ /dev/null @@ -1,43 +0,0 @@ -package oxy.bascenario.editor.inspector.impl.events; - -import oxy.bascenario.api.effects.Easing; -import oxy.bascenario.api.event.element.values.PositionElementEvent; -import oxy.bascenario.api.event.element.values.RotateElementEvent; -import oxy.bascenario.api.utils.math.Vec2; -import oxy.bascenario.api.utils.math.Vec3; -import oxy.bascenario.utils.ImGuiUtils; - -public class PositionInspector { - public static RotateElementEvent render(RotateElementEvent event) { - RotateElementEvent.Builder builder = event.toBuilder(); - builder.id(Math.abs(ImGuiUtils.inputInt("Target Track", event.id()))); - builder.duration(Math.abs(ImGuiUtils.sliderInt("Duration (ms)", (int) event.duration(), 0, 50000))); - - float x = ImGuiUtils.sliderFloat("X", event.rotation().x(), 0, 360); - float y = ImGuiUtils.sliderFloat("Y", event.rotation().y(), 0, 360); - float z = ImGuiUtils.sliderFloat("Z", event.rotation().z(), 0, 360); - - builder.rotation(new Vec3(x, y, z)); - builder.easing(Easing.values()[ImGuiUtils.combo("Easing Mode", event.easing().ordinal(), Easing.getAlls())]); - return builder.build(); - } - - public static PositionElementEvent render(PositionElementEvent event) { - PositionElementEvent.Builder builder = event.toBuilder(); - builder.id(Math.abs(ImGuiUtils.inputInt("Target Track", event.id()))); - builder.duration(Math.abs(ImGuiUtils.sliderInt("Duration (ms)", event.duration(), 0, 50000))); - - float MIN_X = event.type() == PositionElementEvent.Type.SCALE ? 0 : -1920; - float MAX_X = event.type() == PositionElementEvent.Type.SCALE ? 200 : 1920; - float MIN_Y = event.type() == PositionElementEvent.Type.SCALE ? 0 : -1080; - float MAX_Y = event.type() == PositionElementEvent.Type.SCALE ? 200 : 1080; - - float x = ImGuiUtils.sliderFloat("X", event.vec().x(), MIN_X, MAX_X); - float y = ImGuiUtils.sliderFloat("Y", event.vec().y(), MIN_Y, MAX_Y); - builder.vec(new Vec2(x, y)); - - builder.easing(Easing.values()[ImGuiUtils.combo("Easing Mode", event.easing().ordinal(), Easing.getAlls())]); - - return builder.build(); - } -} diff --git a/editor/src/main/java/oxy/bascenario/editor/inspector/impl/events/SoundInspector.java b/editor/src/main/java/oxy/bascenario/editor/inspector/impl/events/SoundInspector.java deleted file mode 100644 index 7dd1883c..00000000 --- a/editor/src/main/java/oxy/bascenario/editor/inspector/impl/events/SoundInspector.java +++ /dev/null @@ -1,70 +0,0 @@ -package oxy.bascenario.editor.inspector.impl.events; - -import imgui.ImGui; -import oxy.bascenario.api.Scenario; -import oxy.bascenario.api.effects.Easing; -import oxy.bascenario.api.effects.Sound; -import oxy.bascenario.api.event.sound.SoundVolumeEvent; -import oxy.bascenario.api.utils.FileInfo; -import oxy.bascenario.editor.miniuis.AssetsUI; -import oxy.bascenario.editor.utils.AudioUtils; -import oxy.bascenario.editor.utils.SoundAsElement; -import oxy.bascenario.utils.ImGuiUtils; - -public class SoundInspector { - public static SoundAsElement render(Scenario.Builder scenario, SoundAsElement sound) { - SoundAsElement.Builder builder = sound.toBuilder(); - final FileInfo old = sound.sound().file(); - Sound newSound = render(sound.sound()); - builder.sound(newSound); - ImGui.separatorText(""); - builder.in(ImGuiUtils.sliderInt("Fade In (ms)", sound.in(), 0, (int) Math.min(10000, sound.max()))); - builder.out(ImGuiUtils.sliderInt("Fade Out (ms)", sound.out(), 0, (int) Math.min(10000, sound.max()))); - builder.start(ImGuiUtils.sliderFloat("Start Position (seconds)", sound.start(), 0, (float) sound.max() / 1000L)); - if ((old == null || !old.equals(newSound.file())) && newSound.file() != null) { - builder.max(AudioUtils.toDuration(scenario.name(), newSound.file())); - } - return builder.build(); - } -// -// public static StopSoundEvent render(StopSoundEvent event) { -// StopSoundEvent.Builder builder = event.toBuilder(); -// builder.id(Math.abs(ImGuiUtils.inputInt("Sound ID", 0))); -// builder.duration(ImGuiUtils.sliderInt("Fade Duration (ms)", event.getDuration(), 0, 10000)); -// return builder.build(); -// } -// - public static SoundVolumeEvent render(SoundVolumeEvent event) { - SoundVolumeEvent.Builder builder = event.toBuilder(); - builder.id(Math.abs(ImGuiUtils.inputInt("Sound ID", 0))); - builder.duration(ImGuiUtils.sliderInt("Fade Duration (ms)", (int) event.duration(), 0, 10000)); - builder.volume(Math.abs(ImGuiUtils.sliderFloat("Volume", event.volume(), 0, 1))); - builder.easing(Easing.values()[ImGuiUtils.combo("Easing Mode", event.easing().ordinal(), Easing.getAlls())]); - return builder.build(); - } -// -// public static PlaySoundEvent render(Scenario.Builder scenario, PlaySoundEvent event) { -// PlaySoundEvent.Builder builder = event.toBuilder(); -// builder.duration(ImGuiUtils.sliderInt("Fade Duration (ms)", (int) event.getDuration(), 0, 10000)); -// builder.start(ImGuiUtils.inputFloat("Start Position (seconds)", event.getStart())); -// ImGui.separatorText(""); -// builder.sound(render(scenario, event.getSound())); -// return builder.build(); -// } - - private static FileInfo last; - - public static Sound render(Sound sound) { - final Sound.Builder builder = sound.toBuilder(); - builder.id(Math.abs(ImGuiUtils.inputInt("Sound ID", 0))); - AssetsUI.pick("Pick Sound!", file -> last = file, "mp3,wav,ogg"); - builder.maxVolume(Math.abs(ImGuiUtils.sliderFloat("Max Volume", sound.maxVolume(), 0, 1))); -// builder.loop(ImGuiUtils.checkbox("Loop", sound.loop())); - - if (last != null) { - builder.file(last); - last = null; - } - return builder.build(); - } -} diff --git a/editor/src/main/java/oxy/bascenario/editor/inspector/impl/events/TransitionInspector.java b/editor/src/main/java/oxy/bascenario/editor/inspector/impl/events/TransitionInspector.java deleted file mode 100644 index 70d840a1..00000000 --- a/editor/src/main/java/oxy/bascenario/editor/inspector/impl/events/TransitionInspector.java +++ /dev/null @@ -1,31 +0,0 @@ -package oxy.bascenario.editor.inspector.impl.events; - -import oxy.bascenario.api.effects.TransitionType; -import oxy.bascenario.api.event.ScreenTransitionEvent; -import oxy.bascenario.api.utils.FileInfo; -import oxy.bascenario.editor.miniuis.AssetsUI; -import oxy.bascenario.utils.ImGuiUtils; - -public class TransitionInspector { - private static FileInfo last; - - public static ScreenTransitionEvent render(ScreenTransitionEvent event) { - ScreenTransitionEvent.Builder builder = event.toBuilder(); - - AssetsUI.pick("Switch Background To", file -> last = file, "png,jpg"); - - if (last != null) { - builder.background(last); - last = null; - } - - TransitionType type = TransitionType.values()[ImGuiUtils.combo("Transition Type", event.type().ordinal(), TransitionType.getAlls())]; - builder.type(type); - - builder.waitDuration(Math.abs(ImGuiUtils.sliderInt("Wait Duration", event.waitDuration(), 0, 10000))); - builder.inDuration(Math.abs(ImGuiUtils.sliderInt("In Duration", event.inDuration(), 0, 10000))); - builder.outDuration(Math.abs(ImGuiUtils.sliderInt("Out Duration", event.outDuration(), 0, 10000))); - - return builder.build(); - } -} diff --git a/editor/src/main/java/oxy/bascenario/editor/inspector/impl/objects/EmoticonInspector.java b/editor/src/main/java/oxy/bascenario/editor/inspector/impl/objects/EmoticonInspector.java deleted file mode 100644 index be70c558..00000000 --- a/editor/src/main/java/oxy/bascenario/editor/inspector/impl/objects/EmoticonInspector.java +++ /dev/null @@ -1,14 +0,0 @@ -package oxy.bascenario.editor.inspector.impl.objects; - -import oxy.bascenario.api.render.elements.emoticon.Emoticon; -import oxy.bascenario.api.render.elements.emoticon.EmoticonType; -import oxy.bascenario.utils.ImGuiUtils; - -public class EmoticonInspector { - public static Emoticon render(Emoticon emoticon) { - final Emoticon.Builder builder = emoticon.toBuilder(); - builder.type(EmoticonType.values()[ImGuiUtils.combo("Type", emoticon.type().ordinal(), EmoticonType.getAlls())]); - builder.sound(ImGuiUtils.checkbox("Play SFX", emoticon.sound())); - return builder.build(); - } -} diff --git a/editor/src/main/java/oxy/bascenario/editor/inspector/impl/objects/ImageInspector.java b/editor/src/main/java/oxy/bascenario/editor/inspector/impl/objects/ImageInspector.java deleted file mode 100644 index 655c87b4..00000000 --- a/editor/src/main/java/oxy/bascenario/editor/inspector/impl/objects/ImageInspector.java +++ /dev/null @@ -1,63 +0,0 @@ -package oxy.bascenario.editor.inspector.impl.objects; - -import imgui.ImGui; -import oxy.bascenario.api.event.PopupEvent; -import oxy.bascenario.api.render.elements.emoticon.Emoticon; -import oxy.bascenario.api.render.elements.emoticon.EmoticonType; -import oxy.bascenario.api.render.elements.image.AnimatedImage; -import oxy.bascenario.api.render.elements.image.Image; -import oxy.bascenario.api.utils.FileInfo; -import oxy.bascenario.editor.miniuis.AssetsUI; -import oxy.bascenario.utils.ImGuiUtils; - -public class ImageInspector { - private static FileInfo last, last1, last2; - - public static PopupEvent render(PopupEvent event) { - final PopupEvent.Builder builder = event.toBuilder(); - PopupEvent.Type type = PopupEvent.Type.values()[ImGuiUtils.combo("Type", event.type().ordinal(), PopupEvent.Type.getAlls())]; - builder.type(type); - if (type == PopupEvent.Type.SET) { - AssetsUI.pick("Pick Image!", file -> last2 = file, "png,jpg"); - if (last2 != null) { - builder.popup(last2); - last2 = null; - } - } - - return builder.build(); - } - - public static AnimatedImage render(AnimatedImage image) { - AnimatedImage.Builder builder = image.toBuilder(); - AssetsUI.pick("Pick Gif!", file -> last = file, "gif"); - if (last != null) { - builder.file(last); - last = null; - } - - builder.start(ImGuiUtils.sliderInt("Start Position (ms)", (int) image.start(), 0, 20000)); - builder.loop(ImGuiUtils.checkbox("Loop", image.loop())); - - ImGui.separatorText(""); - - builder.color(ImGuiUtils.color("Color", image.color())); - builder.width(ImGuiUtils.sliderInt("Width", image.width(), 0, 1920)); - builder.height(ImGuiUtils.sliderInt("Height", image.height(), 0, 1080)); - return builder.build(); - } - - public static Image render(Image image) { - Image.Builder builder = image.toBuilder(); - AssetsUI.pick("Pick Image!", file -> last1 = file, "png,jpg"); - if (last1 != null) { - builder.file(last1); - last1 = null; - } - - builder.color(ImGuiUtils.color("Color", image.color())); - builder.width(ImGuiUtils.sliderInt("Width", image.width(), 0, 1920)); - builder.height(ImGuiUtils.sliderInt("Height", image.height(), 0, 1080)); - return builder.build(); - } -} diff --git a/editor/src/main/java/oxy/bascenario/editor/inspector/impl/objects/LocationInfoInspector.java b/editor/src/main/java/oxy/bascenario/editor/inspector/impl/objects/LocationInfoInspector.java deleted file mode 100644 index f23402fd..00000000 --- a/editor/src/main/java/oxy/bascenario/editor/inspector/impl/objects/LocationInfoInspector.java +++ /dev/null @@ -1,15 +0,0 @@ -package oxy.bascenario.editor.inspector.impl.objects; - -import oxy.bascenario.api.render.elements.LocationInfo; -import oxy.bascenario.api.render.elements.text.font.FontType; -import oxy.bascenario.utils.ImGuiUtils; - -public class LocationInfoInspector { - public static LocationInfo render(LocationInfo info) { - final LocationInfo.Builder builder = info.toBuilder(); - builder.font(FontType.values()[ImGuiUtils.combo("Font Type", info.font().ordinal(), FontType.getAlls())]); - builder.location(ImGuiUtils.inputText("Location Name", info.location())); - builder.fade(ImGuiUtils.sliderInt("Fade Duration", info.fade(), 0, 5000)); - return builder.build(); - } -} diff --git a/editor/src/main/java/oxy/bascenario/editor/inspector/impl/objects/PreviewInspector.java b/editor/src/main/java/oxy/bascenario/editor/inspector/impl/objects/PreviewInspector.java deleted file mode 100644 index bdcbd9d0..00000000 --- a/editor/src/main/java/oxy/bascenario/editor/inspector/impl/objects/PreviewInspector.java +++ /dev/null @@ -1,35 +0,0 @@ -package oxy.bascenario.editor.inspector.impl.objects; - -import imgui.ImGui; -import oxy.bascenario.api.render.elements.Preview; -import oxy.bascenario.api.render.elements.text.font.FontType; -import oxy.bascenario.api.utils.FileInfo; -import oxy.bascenario.editor.miniuis.AssetsUI; -import oxy.bascenario.utils.ImGuiUtils; - -public class PreviewInspector { - private static FileInfo last; - - public static Preview render(Preview preview) { - final Preview.Builder builder = preview.toBuilder(); - builder.type(FontType.values()[ImGuiUtils.combo("Font Type", preview.type().ordinal(), FontType.getAlls())]); - builder.title(ImGuiUtils.inputText("Title", preview.title())); - builder.subtitle(ImGuiUtils.inputText("Subtitle", preview.subtitle())); - - AssetsUI.pick("Pick Image!", file -> last = file, "png,jpg"); - - if (preview.background() != null) { - ImGui.sameLine(); - if (ImGui.button("Remove background!")) { - builder.background(null); - } - } - - if (last != null) { - builder.background(last); - last = null; - } - - return builder.build(); - } -} diff --git a/editor/src/main/java/oxy/bascenario/editor/inspector/impl/objects/ShapeInspector.java b/editor/src/main/java/oxy/bascenario/editor/inspector/impl/objects/ShapeInspector.java deleted file mode 100644 index f849f205..00000000 --- a/editor/src/main/java/oxy/bascenario/editor/inspector/impl/objects/ShapeInspector.java +++ /dev/null @@ -1,24 +0,0 @@ -package oxy.bascenario.editor.inspector.impl.objects; - -import oxy.bascenario.api.render.elements.shape.Circle; -import oxy.bascenario.api.render.elements.shape.Rectangle; -import oxy.bascenario.utils.ImGuiUtils; - -public class ShapeInspector { - public static Circle render(Circle circle) { - Circle.Builder builder = circle.toBuilder(); - builder.radius(ImGuiUtils.sliderFloat("Radius", circle.radius(), 0, 1000)); - builder.outlineOnly(ImGuiUtils.checkbox("Outline Only", circle.outlineOnly())); - builder.color(ImGuiUtils.color("Color", circle.color())); - return builder.build(); - } - - public static Rectangle render(Rectangle rectangle) { - Rectangle.Builder builder = rectangle.toBuilder(); - builder.width(ImGuiUtils.sliderInt("Width", rectangle.width(), 0, 1920)); - builder.height(ImGuiUtils.sliderInt("Height", rectangle.height(), 0, 1080)); - builder.outlineOnly(ImGuiUtils.checkbox("Outline Only", rectangle.outlineOnly())); - builder.color(ImGuiUtils.color("Color", rectangle.color())); - return builder.build(); - } -} diff --git a/editor/src/main/java/oxy/bascenario/editor/inspector/impl/objects/SpriteInspector.java b/editor/src/main/java/oxy/bascenario/editor/inspector/impl/objects/SpriteInspector.java deleted file mode 100644 index a61d54b4..00000000 --- a/editor/src/main/java/oxy/bascenario/editor/inspector/impl/objects/SpriteInspector.java +++ /dev/null @@ -1,35 +0,0 @@ -package oxy.bascenario.editor.inspector.impl.objects; - -import imgui.ImGui; -import oxy.bascenario.api.render.elements.Sprite; -import oxy.bascenario.api.utils.FileInfo; -import oxy.bascenario.editor.miniuis.AssetsUI; - -public class SpriteInspector { - private static FileInfo skeleton, atlas; - - public static Sprite render(Sprite sprite) { - Sprite.Builder builder = sprite.toBuilder(); - if (skeleton != null) { - builder.skeleton(skeleton); - skeleton = null; - } - - if (atlas != null) { - builder.atlas(atlas); - atlas = null; - } - - AssetsUI.pick("Pick Skeleton", file -> skeleton = file, "skel"); - AssetsUI.pick("Pick Atlas", file -> atlas = file, "atlas"); - - if (sprite.skeleton() != null) { - ImGui.textUnformatted("Skeleton: " + sprite.skeleton()); - } - if (sprite.atlas() != null) { - ImGui.textUnformatted("Atlas: " + sprite.atlas()); - } - - return builder.build(); - } -} diff --git a/editor/src/main/java/oxy/bascenario/editor/inspector/impl/objects/TextInspector.java b/editor/src/main/java/oxy/bascenario/editor/inspector/impl/objects/TextInspector.java deleted file mode 100644 index f5dba903..00000000 --- a/editor/src/main/java/oxy/bascenario/editor/inspector/impl/objects/TextInspector.java +++ /dev/null @@ -1,34 +0,0 @@ -package oxy.bascenario.editor.inspector.impl.objects; - -import imgui.ImGui; -import oxy.bascenario.api.Scenario; -import oxy.bascenario.api.render.elements.text.AnimatedText; -import oxy.bascenario.api.render.elements.text.Text; -import oxy.bascenario.api.render.elements.text.TextSegment; -import oxy.bascenario.editor.inspector.impl.other.TextSegmentInspector; -import oxy.bascenario.utils.ImGuiUtils; - -public class TextInspector { - public static Text render(Scenario.Builder scenario, Text text) { - if (ImGui.button("New segment!")) { - text.segments().add(new TextSegment()); - } - - Text.Builder builder = text.toBuilder(); - builder.size(ImGuiUtils.sliderInt("Font Size", text.size(), 1, 150)); - builder.segments(TextSegmentInspector.render(scenario, text.segments())); - return builder.build(); - } - - public static AnimatedText render(Scenario.Builder scenario, AnimatedText text) { - if (ImGui.button("New segment!")) { - text.segments().add(new TextSegment()); - } - - AnimatedText.Builder builder = text.toBuilder(); - builder.playSpeed(ImGuiUtils.sliderFloat("Play Speed", text.playSpeed(), 0.01f, 50)); - builder.size(ImGuiUtils.sliderInt("Font Size", text.size(), 1, 150)); - builder.segments(TextSegmentInspector.render(scenario, text.segments())); - return builder.build(); - } -} diff --git a/editor/src/main/java/oxy/bascenario/editor/inspector/impl/other/TextSegmentInspector.java b/editor/src/main/java/oxy/bascenario/editor/inspector/impl/other/TextSegmentInspector.java deleted file mode 100644 index d55308ec..00000000 --- a/editor/src/main/java/oxy/bascenario/editor/inspector/impl/other/TextSegmentInspector.java +++ /dev/null @@ -1,90 +0,0 @@ -package oxy.bascenario.editor.inspector.impl.other; - -import imgui.ImGui; -import imgui.type.ImBoolean; -import net.lenni0451.commons.color.Color; -import oxy.bascenario.api.Scenario; -import oxy.bascenario.api.render.elements.text.font.FontStyle; -import oxy.bascenario.api.render.elements.text.TextSegment; -import oxy.bascenario.api.render.elements.text.TextStyle; -import oxy.bascenario.api.render.elements.text.font.FontType; -import oxy.bascenario.api.utils.FileInfo; -import oxy.bascenario.editor.miniuis.AssetsUI; -import oxy.bascenario.utils.ImGuiUtils; - -import java.util.*; - -public class TextSegmentInspector { - public static List render(Scenario.Builder scenario, List segments) { - final List list = new ArrayList<>(); - for (TextSegment segment : segments) { - final ImBoolean imBoolean = new ImBoolean(true); - if (ImGui.collapsingHeader("Text Segment##" + ImGuiUtils.COUNTER++, imBoolean)) { - segment = render(scenario, segment); - } - - if (imBoolean.get()) { - list.add(segment); - } - } - - return list; - } - - private static FileInfo last; - public static TextSegment render(Scenario.Builder scenario, TextSegment segment) { - TextSegment.Builder builder = segment.toBuilder(); - builder.text(ImGuiUtils.inputMultiLineText("Text", segment.text())); - - if (ImGuiUtils.checkbox("Custom Font", segment.font().file() != null)) { - if (segment.font().file() == null) { - // We kinda need to set this to something first. - builder.font(new FileInfo("assets/base/fonts/global/NotoSans-Regular.ttf", false, true)); - } - - AssetsUI.pick("Pick Font!", file -> last = file, "ttf"); - ImGui.sameLine(); - ImGui.textUnformatted(segment.font().file() == null ? "" : segment.font().file().path()); - } else { - builder.font((FileInfo) null); - - int type = segment.font().type() == null ? 0 : segment.font().type().ordinal(); - int style = segment.font().style() == null ? 0 : segment.font().style().ordinal(); - builder.type(FontType.values()[ImGuiUtils.combo("Font Type", type, FontType.getAlls())]); - builder.style(FontStyle.values()[ImGuiUtils.combo("Font Style", style, FontStyle.getAlls())]); - } - - if (last != null) { - builder.font(last); - last = null; - } - - ImGui.separatorText(""); - - builder.color(ImGuiUtils.color("Text Color", segment.color())); - - if (ImGuiUtils.checkbox("Outline", segment.outline().isPresent())) { - builder.outline(ImGuiUtils.color("Outline Color", segment.outline().isEmpty() ? Color.WHITE : segment.outline().get())); - } else { - builder.outline(null); - } - - ImGui.separatorText(""); - - boolean shadow = ImGuiUtils.checkbox("Shadow", segment.styles().contains(TextStyle.SHADOW)); - boolean bold = ImGuiUtils.checkbox("Bold", segment.styles().contains(TextStyle.BOLD)); - boolean italic = ImGuiUtils.checkbox("Italic", segment.styles().contains(TextStyle.ITALIC)); - boolean underline = ImGuiUtils.checkbox("Underline", segment.styles().contains(TextStyle.UNDERLINE)); - boolean strikethrough = ImGuiUtils.checkbox("Strikethrough", segment.styles().contains(TextStyle.STRIKETHROUGH)); - final Set styles = EnumSet.noneOf(TextStyle.class); - if (shadow) styles.add(TextStyle.SHADOW); - if (bold) styles.add(TextStyle.BOLD); - if (italic) styles.add(TextStyle.ITALIC); - if (underline) styles.add(TextStyle.UNDERLINE); - if (strikethrough) styles.add(TextStyle.STRIKETHROUGH); - builder.styles().clear(); - builder.styles().addAll(styles); - - return builder.build(); - } -} diff --git a/editor/src/main/java/oxy/bascenario/editor/miniuis/ActionsUI.java b/editor/src/main/java/oxy/bascenario/editor/miniuis/ActionsUI.java deleted file mode 100644 index 12f07b23..00000000 --- a/editor/src/main/java/oxy/bascenario/editor/miniuis/ActionsUI.java +++ /dev/null @@ -1,298 +0,0 @@ -package oxy.bascenario.editor.miniuis; - -import imgui.ImGui; -import imgui.ImVec2; -import imgui.flag.ImGuiHoveredFlags; -import lombok.RequiredArgsConstructor; -import net.lenni0451.commons.color.Color; -import oxy.bascenario.api.effects.*; -import oxy.bascenario.api.event.*; -import oxy.bascenario.api.event.LockClickEvent; -import oxy.bascenario.api.event.background.ClearBackgroundEvent; -import oxy.bascenario.api.event.background.SetBackgroundEvent; -import oxy.bascenario.api.event.color.ColorOverlayEvent; -import oxy.bascenario.api.event.animation.PlayAnimationEvent; -import oxy.bascenario.api.event.animation.SpriteAnimationEvent; -import oxy.bascenario.api.event.animation.StopAnimationEvent; -import oxy.bascenario.api.event.api.Event; -import oxy.bascenario.api.event.color.SetColorEvent; -import oxy.bascenario.api.event.dialogue.*; -import oxy.bascenario.api.event.dialogue.enums.TextOffset; -import oxy.bascenario.api.event.element.ClearLogEvent; -import oxy.bascenario.api.event.element.ElementEffectEvent; -import oxy.bascenario.api.event.element.focus.FocusElementEvent; -import oxy.bascenario.api.event.element.focus.UnfocusElementEvent; -import oxy.bascenario.api.event.element.values.PositionElementEvent; -import oxy.bascenario.api.event.element.values.RotateElementEvent; -import oxy.bascenario.api.event.log.AddLogEvent; -import oxy.bascenario.api.event.sound.SoundVolumeEvent; -import oxy.bascenario.api.render.RenderLayer; -import oxy.bascenario.api.render.elements.Dialogue; -import oxy.bascenario.api.render.elements.text.font.FontType; -import oxy.bascenario.api.utils.FileInfo; -import oxy.bascenario.api.utils.math.Axis; -import oxy.bascenario.api.utils.math.Vec2; -import oxy.bascenario.api.utils.math.Vec3; -import oxy.bascenario.editor.timeline.ObjectOrEvent; -import oxy.bascenario.editor.timeline.Timeline; -import oxy.bascenario.editor.screen.BaseScenarioEditorScreen; -import oxy.bascenario.editor.utils.TimeCompiler; - -import java.util.*; - -@RequiredArgsConstructor -public class ActionsUI { - private static final Dialogue DUMMY_DIALOGUE = Dialogue.builder().add("Hello World!").build(); - - private final BaseScenarioEditorScreen screen; - private final Timeline timeline; - - // TODO... - public void render() { - ImGui.begin("Actions"); - - ImGui.beginTabBar("actions_tab_bar"); - - if (ImGui.beginTabItem("All")){ - animationTab(); - dialogueTab(); - objectTab(); - backgroundTab(); - soundTab(); - otherTab(); - ImGui.endTabItem(); - } - - if (ImGui.beginTabItem("Animations")){ - animationTab(); - ImGui.endTabItem(); - } - - if (ImGui.beginTabItem("Dialogues")){ - dialogueTab(); - ImGui.endTabItem(); - } - - if (ImGui.beginTabItem("Logs")){ - logTab(); - ImGui.endTabItem(); - } - - if (ImGui.beginTabItem("Objects")){ - objectTab(); - ImGui.endTabItem(); - } - - if (ImGui.beginTabItem("Background")){ - backgroundTab(); - ImGui.endTabItem(); - } - - if (ImGui.beginTabItem("Sounds")){ - soundTab(); - ImGui.endTabItem(); - } - - if (ImGui.beginTabItem("Others")){ - otherTab(); - ImGui.endTabItem(); - } - - ImGui.endTabBar(); - - ImGui.end(); - } - - private void otherTab() { - add("Screen Color", - "This will cover the entire screen with a color you choose on a render layer that you also choose.", - new ColorOverlayEvent(RenderLayer.TOP, 500, Color.WHITE)); - - add("Lock Click", - "This lock or unlock the mouse click button to interact with the options or dialogue.", - new LockClickEvent(false)); - - add("Show Buttons", - "Choose to hide/show the auto/menu button at the top right of the screen.", - new ShowButtonsEvent(true)); - - add("Set Weather", - "Set the current weather", - new SetWeatherEvent(Weather.RAIN)); - - add("Screen Effect", - "Add or remove an effect on the screen.", - new ScreenEffectEvent(ScreenEffectEvent.Type.ADD, ScreenEffect.GRAY_FILTER)); - - add("Screen Transition", - "Transition the screen (to another background if wanted)", - new ScreenTransitionEvent(null, TransitionType.HORIZONTAL_SWIPE_LR, 1000, 700, 700)); - - add("Image Popup", - "Show an image popup on the screen", - new PopupEvent(PopupEvent.Type.SET, null)); - } - - private void soundTab() { - add("Sound Volume", - "Change a playing sound volume.", - new SoundVolumeEvent(0, 1000, 1, Easing.LINEAR)); - } - - private void backgroundTab() { - add("Set Background", - "A simple way to change the background with fade duration.", - new SetBackgroundEvent(new FileInfo("assets/base/black.png", false, true), 500)); - - add("Clear Background", - "Clear the current background and fade it to black!", - new ClearBackgroundEvent(500)); - } - - private void objectTab() { - add("Move Object", - "This move an object.", - new PositionElementEvent(0, 1000, new Vec2(0, 0), Easing.LINEAR, PositionElementEvent.Type.POSITION)); - - add("Offset Object", - "Offset an object by a certain value, so that no matter where the object is the position will always be offset by that value.", - new PositionElementEvent(0, 1000, new Vec2(0, 0), Easing.LINEAR, PositionElementEvent.Type.OFFSET)); - - add("Scale Object", - "This scale an object.", - new PositionElementEvent(0, 1000, new Vec2(1, 1), Easing.LINEAR, PositionElementEvent.Type.SCALE)); - - add("Pivot Object", - "Define a \"pivot\" point, this will be the point used when rotating and the object will be rotate around this point.", - new PositionElementEvent(0, 1000, new Vec2(0, 0), Easing.LINEAR, PositionElementEvent.Type.PIVOT)); - - add("Rotate Object", - "This rotate an object.", - new RotateElementEvent(0, 1000, new Vec3(0, 0, 0), Easing.LINEAR)); - - add("Apply Effect", - "Apply an effect on top of any element on the selected track, eg: hologram, blur, rainbow.", - new ElementEffectEvent(0, Effect.HOLOGRAM, Axis.Y)); - - add("Remove Effect", - "Remove an effect that being applied on top of an element on the selected track.", - new ElementEffectEvent(0, Effect.HOLOGRAM, ElementEffectEvent.Type.REMOVE)); - - add("Color Overlay", - "Apply a color overlay, this will put a color overlay on top of the object.", - new ColorOverlayEvent(0, 500, Color.WHITE)); - - add("Set Color", - "Like color overlay, but instead of overlaying color it change the object color itself.", - new SetColorEvent(0, 500, Color.WHITE)); - - add("Focus Object", - "Focus on a object and un-focus everything else.", - new FocusElementEvent(0)); - - add("Unfocus Objects", - "Un-focus any currently focused object.", - new UnfocusElementEvent()); - } - - private void logTab() { - add("Add Log", - "Start a log which is similar to a dialogue, but will play on the screen instead of bottom of the screen.", - new AddLogEvent(AddLogEvent.Type.CLEAR, 0, TextOffset.LEFT, DUMMY_DIALOGUE)); - - add("Clear Log", - "Clear any kind of log that is on the screen.", - new ClearLogEvent()); - } - - private void dialogueTab() { - add("Start Dialogue", - "Start a dialogue like in game, with values like name, association or should this render background, and of course dialogues.", - new StartDialogueEvent(FontType.NotoSans, 0, "", "", true, DUMMY_DIALOGUE)); - - add("Add Dialogue", - "Add a new dialogue (text) onto any existing playing dialogue, allows for playing a certain dialogue text a bit late.", - new AddDialogueEvent(0, DUMMY_DIALOGUE)); - - add("Redirect Dialogue", - "Set the dialogue index to a whole number you choose.", - new RedirectDialogueEvent(0)); - - add("Close Dialogue", "Close any currently present dialogue", new CloseDialogueEvent()); - - { - final LinkedHashMap map = new LinkedHashMap<>(); - map.put("Yes", 0); - map.put("No", 0); - add("Show Options", - "Show options that the user could choose, result in a dialogue index that determine which dialogue to play.", - new ShowOptionsEvent(map)); - } - - add("Close Options", "Close any currently present options", new CloseOptionsEvent()); - - - { - final List answers = new ArrayList<>(); - answers.add(new ShowQuestionSelectionEvent.Answer(0, "This is grayed out.", true)); - answers.add(new ShowQuestionSelectionEvent.Answer(0, "This is not.", false)); - add("Show Question And Answer", - "Like Show Options but decorated and have a question, allow for grayed out buttons.", - new ShowQuestionSelectionEvent(FontType.NotoSans, "Which one is grayed out", answers)); - } - } - - private void animationTab() { - add("Play Animation", - "Play an defined animation, default or custom with an element on the selected track.", - new PlayAnimationEvent(0, "bascenarioengine:default-shake", false)); - - add("Play Sprite Animation", - "Play an animation that is defined in the spine (.skel) file you imported to a sprite character on the selected track.", - new SpriteAnimationEvent(0, 0.2f, "Idle_01", 1)); - - add("Stop Animation", - "Stop any defined animation, (note) not sprite animation.", - new StopAnimationEvent(0, "bascenarioengine:default-shake")); - } - - private void add(String label, String tooltip, Event event) { - if (!ImGui.button(label, new ImVec2(ImGui.getWindowSize().x - 20, 50))) { - if (ImGui.isItemHovered(ImGuiHoveredFlags.DelayNormal)) { - ImGui.setTooltip(tooltip); - } - return; - } - timeline.setSelectedObject(null); - - long duration = TimeCompiler.compileTime(event); - final int track = findNonOccupiedSlot(timeline.getTimestamp(), duration); - - timeline.put(track, timeline.getTimestamp(), duration, event, null, true, null); - } - - private int findNonOccupiedSlot(long time, long duration) { - final Map> map = new HashMap<>(); - timeline.getObjects().forEach(object -> map.computeIfAbsent(object.track, n -> new ArrayList<>()).add(object)); - - int i = 0; - List track; - while ((track = map.get(i)) != null) { - boolean occupied = false; - for (ObjectOrEvent object : track) { - final long maxTime = object.start + object.duration, minTime = object.start; - if (maxTime >= time && minTime <= time + duration) { - occupied = true; - break; - } - } - - if (!occupied) { - return i; - } - i++; - } - - return i; - } -} diff --git a/editor/src/main/java/oxy/bascenario/editor/miniuis/AssetsUI.java b/editor/src/main/java/oxy/bascenario/editor/miniuis/AssetsUI.java deleted file mode 100644 index 23d95af7..00000000 --- a/editor/src/main/java/oxy/bascenario/editor/miniuis/AssetsUI.java +++ /dev/null @@ -1,152 +0,0 @@ -package oxy.bascenario.editor.miniuis; - -import imgui.ImColor; -import imgui.ImGui; -import imgui.flag.ImGuiTableColumnFlags; -import imgui.flag.ImGuiTableFlags; -import imgui.flag.ImGuiTreeNodeFlags; -import oxy.bascenario.Base; -import oxy.bascenario.api.Scenario; -import oxy.bascenario.api.utils.FileInfo; -import oxy.bascenario.editor.timeline.Timeline; -import oxy.bascenario.managers.ScenarioManager; -import oxy.bascenario.utils.ImGuiUtils; - -import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.*; -import java.util.function.Consumer; -import java.util.stream.Stream; - -public class AssetsUI { - public static boolean popup; - public static Consumer consumer; - public static String filter; - - private static Object object; - public static void pick(String name, Consumer consumer, String filter) { - if (ImGui.button(name + "##" + ImGuiUtils.COUNTER++)) { - AssetsUI.filter = filter; - AssetsUI.consumer = consumer; - AssetsUI.popup = true; - object = null; - } - } - - public static void render(Timeline timeline, Scenario.Builder scenario) { - ImGui.begin("Assets"); - render(timeline, scenario, null); - ImGui.end(); - - if (popup) { - ImGui.openPopup("Select asset"); - popup = false; - } - - if (ImGui.beginPopupModal("Select asset")) { - render(timeline, scenario, consumer); - if (ImGui.button("Nevermine!")) { - ImGui.closeCurrentPopup(); - filter = null; - consumer = null; - } - ImGui.endPopup(); - } - } - - public static void render(Timeline timeline, Scenario.Builder scenario, Consumer consumer) { - if (object == null) { - object = timeline.getSelectedObject(); - } - - ImGuiUtils.pick(f -> {}, scenario, "Load File", filter != null ? filter : "*"); - ImGui.sameLine(); - ImGuiUtils.pickFolder(f -> {}, scenario, "Load Folder"); - - final List noFolders = new ArrayList<>(); - final Map> folders = new HashMap<>(); - - final Path path = new File(new File(ScenarioManager.SAVE_DIR, scenario.name()), "files").toPath(); - try (final Stream stream = Files.walk(path)) { - List files = stream.filter(Files::isRegularFile).map(Path::toFile).toList(); - - for (File file : files) { - FileInfo info = new FileInfo(file.getAbsolutePath().replace(path.toFile().getAbsolutePath() + "\\", ""), false, false); - if (filter != null && !filter.equals("*")) { - boolean valid = false; - for (String ext : filter.replace(" ", "").split(",")) { - if (info.path().toLowerCase(Locale.ROOT).endsWith("." + ext)) { - valid = true; - break; - } - } - - if (!valid) { - continue; - } - } - - if (!info.path().contains("\\")) { - noFolders.add(info); - continue; - } - - String[] split = info.path().split("\\\\"); - if (split.length != 2) { - noFolders.add(info); - continue; // We don't support stacked folder, if they do that, their fault. - } - - folders.computeIfAbsent(split[0], p -> new ArrayList<>()).add(info); - } - } catch (IOException ignored) { - } - - if (ImGui.beginTable("1ways", 1, ImGuiTableFlags.BordersV | ImGuiTableFlags.BordersOuterH | ImGuiTableFlags.Resizable | ImGuiTableFlags.RowBg | ImGuiTableFlags.NoBordersInBody)) { - ImGui.tableSetupColumn("Name", ImGuiTableColumnFlags.NoHide); - ImGui.tableHeadersRow(); - - for (Map.Entry> folder : folders.entrySet()) { - ImGui.tableNextRow(); - ImGui.tableNextColumn(); - if (ImGui.treeNodeEx(folder.getKey(), ImGuiTreeNodeFlags.SpanAllColumns | ImGuiTreeNodeFlags.DefaultOpen)) { - renderAssets(scenario.name(), timeline, folder.getValue(), consumer, true); - ImGui.treePop(); - } - } - - renderAssets(scenario.name(), timeline, noFolders, consumer, false); - - ImGui.endTable(); - } - } - - private static void renderAssets(String scenario, Timeline timeline, List files, Consumer consumer, boolean split) { - for (FileInfo file : files) { - ImGui.tableNextRow(); - ImGui.tableNextColumn(); - - if (consumer == null && filter == null) { - ImGui.text(split ? file.path().split("\\\\")[1] : file.path()); - ImGui.sameLine(); - if (ImGui.smallButton("Delete##" + ImGuiUtils.COUNTER++)) { - Base.instance().scenarioManager().file(scenario, file).delete(); - } - } else { - if (ImGui.selectable(split ? file.path().split("\\\\")[1] : file.path())) { - if (consumer != null) { - if (object == timeline.getSelectedObject()) { - consumer.accept(file); - } - AssetsUI.consumer = null; - } - - ImGui.closeCurrentPopup(); - filter = null; - } - } - } - } -} diff --git a/editor/src/main/java/oxy/bascenario/editor/miniuis/ObjectsUI.java b/editor/src/main/java/oxy/bascenario/editor/miniuis/ObjectsUI.java deleted file mode 100644 index 8f5e38c0..00000000 --- a/editor/src/main/java/oxy/bascenario/editor/miniuis/ObjectsUI.java +++ /dev/null @@ -1,135 +0,0 @@ -package oxy.bascenario.editor.miniuis; - -import imgui.ImGui; -import imgui.ImVec2; -import imgui.flag.ImGuiHoveredFlags; -import lombok.RequiredArgsConstructor; -import net.lenni0451.commons.color.Color; -import oxy.bascenario.api.effects.Sound; -import oxy.bascenario.api.render.RenderLayer; -import oxy.bascenario.api.render.elements.*; -import oxy.bascenario.api.render.elements.emoticon.Emoticon; -import oxy.bascenario.api.render.elements.emoticon.EmoticonType; -import oxy.bascenario.api.render.elements.image.AnimatedImage; -import oxy.bascenario.api.render.elements.image.Image; -import oxy.bascenario.api.render.elements.shape.Circle; -import oxy.bascenario.api.render.elements.shape.Rectangle; -import oxy.bascenario.api.render.elements.text.AnimatedText; -import oxy.bascenario.api.render.elements.text.Text; -import oxy.bascenario.api.render.elements.text.TextSegment; -import oxy.bascenario.api.render.elements.text.font.FontType; -import oxy.bascenario.api.utils.math.Vec2; -import oxy.bascenario.editor.timeline.ObjectOrEvent; -import oxy.bascenario.editor.timeline.Timeline; -import oxy.bascenario.editor.utils.TimeCompiler; -import oxy.bascenario.editor.screen.BaseScenarioEditorScreen; -import oxy.bascenario.editor.utils.SoundAsElement; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -@RequiredArgsConstructor -public class ObjectsUI { - private final BaseScenarioEditorScreen screen; - private final Timeline timeline; - - public void render() { - ImGui.begin("Objects"); - - add("Preview", - "Show up the \"preview\" of the scenario like when you first play the story like in the game.\nWith title and (optional) subtitle and optional background.", - new Preview(FontType.NotoSans, "Title", "Subtitle", null)); - - add("Emoticon", - "Popup an emoji that show emotions like in game, eg: Angry, Thinking, etc.", - new Emoticon(1000, EmoticonType.NOTE, true)); - - add("Sprite", - "A character sprite (spine), in binary format (.skel), that allows for dynamic animations and other niche stuff.", - new Sprite(null, null)); - - add("Image", - "An image... It's an image.", - new Image(null, Color.WHITE, 100, 100)); - - add("Gif", - "A GIF... It's a GIF.", - new AnimatedImage(null, 0, true, Color.WHITE, 100, 100)); - - add("Text", - "A text with defined size with multiple segments, each segments with customizable font, color, etc.", - new Text(new ArrayList<>(List.of(TextSegment.builder().text("Hello World!").build())), 42)); - - add("Typing Text", - "Same as text but with typing animation.", - new AnimatedText(1, new ArrayList<>(List.of(TextSegment.builder().text("Hello World!").build())), 42)); - - add("Circle", - "round round we go.", - new Circle(20, Color.WHITE, false)); - - add("Rectangle", - """ - Why was the rectangle in love with a triangle? - She has acute angle. - """, - new Rectangle(100, 100, Color.WHITE, false)); - -// add("Triangle", null); - add("Location Info", - "Popup a box that show the current location name like in game.", - new LocationInfo(FontType.NotoSans, "Location Name", 2500, 500)); - - add("Sound", - "It's a sound, this will play a sound.", - new SoundAsElement(new Sound(0, null, 1, false), 0, 0, 0, 0)); - - ImGui.end(); - } - - private void add(String label, String tooltip, Object element) { - if (!ImGui.button(label, new ImVec2(ImGui.getWindowSize().x - 20, 50))) { - if (ImGui.isItemHovered(ImGuiHoveredFlags.DelayNormal)) { - ImGui.setTooltip(tooltip); - } - return; - } - - timeline.setSelectedObject(null); - - long duration = TimeCompiler.compileTime(element); - if (duration == Long.MAX_VALUE) { - duration = 1000L; - } - - final int track = findNonOccupiedSlot(timeline.getTimestamp(), duration); - timeline.put(track, timeline.getTimestamp(), duration, element, element instanceof SoundAsElement ? null : RenderLayer.ABOVE_DIALOGUE, true, element instanceof SoundAsElement ? null : new Vec2(0, 0)); - } - - private int findNonOccupiedSlot(long time, long duration) { - final Map> map = new HashMap<>(); - timeline.getObjects().forEach(object -> map.computeIfAbsent(object.track, n -> new ArrayList<>()).add(object)); - - int i = 0; - List track; - while ((track = map.get(i)) != null) { - boolean occupied = false; - for (ObjectOrEvent object : track) { - final long maxTime = object.start + object.duration, minTime = object.start; - if (maxTime >= time && minTime <= time + duration) { - occupied = true; - break; - } - } - - if (!occupied) { - return i; - } - i++; - } - - return i; - } -} diff --git a/editor/src/main/java/oxy/bascenario/editor/object/ObjectOrEvent.java b/editor/src/main/java/oxy/bascenario/editor/object/ObjectOrEvent.java new file mode 100644 index 00000000..7a10e061 --- /dev/null +++ b/editor/src/main/java/oxy/bascenario/editor/object/ObjectOrEvent.java @@ -0,0 +1,13 @@ +package oxy.bascenario.editor.object; + +import lombok.AllArgsConstructor; +import oxy.bascenario.api.render.RenderLayer; + +@AllArgsConstructor +public class ObjectOrEvent { + public long start, duration; + + public Object object; + public RenderLayer layer; + public boolean requireWait; +} diff --git a/editor/src/main/java/oxy/bascenario/editor/screen/BaseScenarioEditorScreen.java b/editor/src/main/java/oxy/bascenario/editor/screen/BaseScenarioEditorScreen.java deleted file mode 100644 index 3a0e7236..00000000 --- a/editor/src/main/java/oxy/bascenario/editor/screen/BaseScenarioEditorScreen.java +++ /dev/null @@ -1,166 +0,0 @@ -package oxy.bascenario.editor.screen; - -import com.badlogic.gdx.Screen; -import imgui.ImGui; -import imgui.ImGuiViewport; -import imgui.flag.ImGuiCol; -import imgui.flag.ImGuiDockNodeFlags; -import imgui.flag.ImGuiKey; -import imgui.flag.ImGuiWindowFlags; -import lombok.Getter; -import net.raphimc.thingl.ThinGL; -import oxy.bascenario.Base; -import oxy.bascenario.api.Scenario; -import oxy.bascenario.editor.miniuis.AssetsUI; -import oxy.bascenario.editor.miniuis.ObjectsUI; -import oxy.bascenario.editor.miniuis.ActionsUI; -import oxy.bascenario.editor.timeline.Timeline; -import oxy.bascenario.editor.inspector.Inspector; - -import oxy.bascenario.editor.utils.TrackParser; -import oxy.bascenario.utils.ExtendableScreen; -import oxy.bascenario.utils.Launcher; -import oxy.bascenario.utils.thingl.ThinGLUtils; - -public class BaseScenarioEditorScreen extends ExtendableScreen { - private final Screen prevScreen; - - @Getter - protected final Scenario.Builder scenario; - protected final Timeline timeline; - protected final ObjectsUI objectsUI; - protected final ActionsUI eventAdder; - protected final Inspector inspector; - - public BaseScenarioEditorScreen(Screen prevScreen, Scenario.Builder scenario) { - this.prevScreen = prevScreen; - this.scenario = scenario; - this.timeline = new Timeline(this, scenario); - this.objectsUI = new ObjectsUI(this, this.timeline); - this.eventAdder = new ActionsUI(this, this.timeline); - this.inspector = new Inspector(this, this.timeline); - } - - @Override - public void render(float delta) { - ImGui.dockSpaceOverViewport(0, new ImGuiViewport(0), ImGuiDockNodeFlags.PassthruCentralNode); - - ImGui.getStyle().setColor(ImGuiCol.WindowBg, 0.098f, 0.098f, 0.098f, 1f); - - renderMenuBar(); - timeline.render(); - objectsUI.render(); - eventAdder.render(); - inspector.render(); - AssetsUI.render(timeline, scenario); - - ImGui.getStyle().setColor(ImGuiCol.WindowBg, 0, 0, 0, 0); - ImGui.begin("Scenario View", ImGuiWindowFlags.NoBackground | ImGuiWindowFlags.MenuBar); - ImGui.beginMenuBar(); - if (ImGui.menuItem("Play", false, !timeline.isPlaying())) { - setPlaying(true); - } - if (ImGui.menuItem("Pause", false, timeline.isPlaying())) { - setPlaying(false); - } - ImGui.endMenuBar(); - renderScenarioWindow(); - ImGui.end(); - - boolean control = ImGui.isKeyDown(ImGuiKey.RightCtrl) || ImGui.isKeyDown(ImGuiKey.LeftCtrl); - if ((control) && ImGui.isKeyReleased(ImGuiKey.Z)) { - timeline.undo(); - } - } - - public void update() { - } - - protected final void renderScenarioWindow() { - float x = ThinGL.windowInterface().getFramebufferWidth() / 1920F; - ThinGLUtils.GLOBAL_RENDER_STACK.pushMatrix(); - ThinGLUtils.GLOBAL_RENDER_STACK.scale(1 / x, 1080F / ThinGL.windowInterface().getFramebufferHeight(), x); - ThinGLUtils.GLOBAL_RENDER_STACK.translate(ImGui.getWindowPosX(), ImGui.getWindowPosY() + 46, 0); - ThinGLUtils.GLOBAL_RENDER_STACK.scale(ImGui.getWindowSizeX() / 1920f, (ImGui.getWindowSizeY() - 46) / 1080f, ImGui.getWindowSizeX() / 1920f); - renderScenario(); - ThinGLUtils.GLOBAL_RENDER_STACK.popMatrix(); - } - - public void renderScenario() { - } - - // TODO. - private void renderMenuBar() { - ImGui.beginMainMenuBar(); - - if (ImGui.beginMenu("Timeline")) { - if (ImGui.menuItem("Play", false, !timeline.isPlaying())) { - setPlaying(true); - } - if (ImGui.menuItem("Pause", false, timeline.isPlaying())) { - setPlaying(false); - } - ImGui.endMenu(); - } - - if (ImGui.beginMenu("Edit")) { - if (ImGui.menuItem("Undo", "Ctrl+Z", false, timeline.canUndo())) { - timeline.undo(); - } - ImGui.endMenu(); - } - - if (ImGui.menuItem("Save")) { - save(); - } - - if (ImGui.beginMenu("Exit")) { - if (ImGui.menuItem("Exit and Save")) { - save(); - Launcher.WINDOW.setScreen(prevScreen); - } - if (ImGui.menuItem("Exit and Discard")) { - confirmExitAndDiscard = true; - } - ImGui.endMenu(); - } - - ImGui.endMainMenuBar(); - - if (confirmExitAndDiscard) { - ImGui.openPopup("AreYouSure"); - confirmExitAndDiscard = false; - } - - if (ImGui.beginPopupModal("AreYouSure", ImGuiWindowFlags.NoResize)) { - ImGui.text("You really want to discard all that hard works? Are you sure?"); - if (ImGui.button("Never mind let's save and quit!")) { - save(); - Launcher.WINDOW.setScreen(prevScreen); - } - ImGui.sameLine(); - if (ImGui.button("Yep!")) { - Launcher.WINDOW.setScreen(prevScreen); - } - ImGui.endPopup(); - } - } - - private boolean confirmExitAndDiscard; - - public void setPlaying(boolean playing) { - timeline.setPlaying(playing); - } - - private void save() { - scenario.timestamps().clear(); - scenario.timestamps().addAll(TrackParser.parse(this.timeline.getObjects())); - - Base.instance().scenarioManager().put(scenario.name(), scenario.build()); - try { - Base.instance().scenarioManager().saveToPath(scenario.build()); - } catch (Exception e) { - throw new RuntimeException(e); - } - } -} diff --git a/editor/src/main/java/oxy/bascenario/editor/screen/ScenarioEditorScreen.java b/editor/src/main/java/oxy/bascenario/editor/screen/ScenarioEditorScreen.java deleted file mode 100644 index a0546ac4..00000000 --- a/editor/src/main/java/oxy/bascenario/editor/screen/ScenarioEditorScreen.java +++ /dev/null @@ -1,146 +0,0 @@ -package oxy.bascenario.editor.screen; - -import com.badlogic.gdx.Screen; -import imgui.ImGui; -import imgui.ImVec2; -import net.raphimc.thingl.ThinGL; -import oxy.bascenario.api.Scenario; -import oxy.bascenario.api.event.dialogue.AddDialogueEvent; -import oxy.bascenario.api.event.dialogue.ShowOptionsEvent; -import oxy.bascenario.api.event.dialogue.StartDialogueEvent; -import oxy.bascenario.editor.timeline.ObjectOrEvent; -import oxy.bascenario.editor.utils.TrackParser; -import oxy.bascenario.screens.ScenarioScreen; -import oxy.bascenario.utils.TimeUtils; - -import java.util.*; - -public final class ScenarioEditorScreen extends BaseScenarioEditorScreen { - private ScenarioScreen screen; - - public ScenarioEditorScreen(Screen screen, Scenario scenario, Scenario.Builder builder) { - super(screen, builder); - this.screen = new ScenarioScreen(scenario, false); - this.screen.setPlaying(false); - } - - @Override - public void show() { - super.show(); - this.screen.show(); - } - - @Override - public void resize(int width, int height) { - super.resize(width, height); - screen.resize(width, height); - } - - @Override - public void dispose() { - super.dispose(); - screen.dispose(); - } - - @Override - public void render(float delta) { - boolean busyDialogue = screen.isBusyDialogue() && !screen.getDialogueRenderer().isBusy(); - boolean busyOptions = screen.isBusyOptions() && - !(screen.getOptionsRenderer().getScale().isRunning() && screen.getOptionsRenderer().getScale().getTarget() == 1); - boolean busyShowQuestions = screen.isBusyAnswerSelection() && !screen.getQuestionSelectionRenderer().getOffset().isRunning(); - timeline.setTickTime(!busyDialogue && !busyOptions && !busyShowQuestions); - super.render(delta); - } - - ImVec2 windowSize = new ImVec2(0, 0), windowPos = new ImVec2(0, 0); - @Override - public void renderScenario() { - windowSize = ImGui.getWindowSize(); - windowPos = ImGui.getWindowPos(); - screen.render(0); - } - - @Override - public void mouseClicked(double mouseX, double mouseY, int button) { - if (!timeline.isPlaying()) { - return; - } - - mouseX *= (ThinGL.windowInterface().getFramebufferWidth() / 1920F); - mouseY *= (ThinGL.windowInterface().getFramebufferHeight() / 1080F); - mouseX -= windowPos.x; - mouseY -= windowPos.y + 46; - - mouseX /= (windowSize.x / 1920f); - mouseY /= ((windowSize.y - 46) / 1080f); - - screen.mouseClicked(mouseX, mouseY, button); - } - - @Override - public void mouseRelease() { - if (!timeline.isPlaying()) { - return; - } - - screen.mouseRelease(); - } - - @Override - public void update() { - screen = new ScenarioScreen(screen.getScenario(), false); - screen.show(); - screen.getTimestamps().clear(); - screen.getTimestamps().addAll(TrackParser.parse(timeline.getObjects())); - screen.setPlaying(false); - - long start = System.currentTimeMillis(); - TimeUtils.fakeTimeMillis = start - timeline.getTimestamp(); - long lastDuration = 0, last = 0, prev = 0; - for (ObjectOrEvent object : this.timeline.getObjects()) { - if (object.start > timeline.getTimestamp()) { - break; - } - - long duration = object.duration; - screen.sinceDialogue = screen.sincePoll = lastDuration + (object.start - last); - - TimeUtils.fakeTimeMillis += object.start - prev; - screen.pollEvents(true); -// renderScenarioWindow(); - - lastDuration = duration; - last = object.start + duration; - prev = object.start; - - if (timeline.getTimestamp() > object.start + duration) { - if (object.object instanceof StartDialogueEvent || object.object instanceof AddDialogueEvent) { - screen.setBusyDialogue(false); - } else if (object.object instanceof ShowOptionsEvent) { - screen.getOptionsRenderer().setOptions(null, null); - screen.setBusyOptions(false); - } - } - } - - TimeUtils.fakeTimeMillis = timeline.isPlaying() ? null : System.currentTimeMillis(); - renderScenarioWindow(); - - screen.setPlaying(timeline.isPlaying()); - screen.sinceDialogue = screen.sincePoll = timeline.getTimestamp() - (last - lastDuration); - } - - @Override - public void setPlaying(boolean playing) { - if (playing) { - if (!timeline.isPlaying()) { - update(); - } - TimeUtils.fakeTimeMillis = null; - } else { - TimeUtils.fakeTimeMillis = System.currentTimeMillis(); - } - super.setPlaying(playing); - this.screen.setPlaying(playing); - } -} diff --git a/editor/src/main/java/oxy/bascenario/editor/timeline/ObjectDragDrop.java b/editor/src/main/java/oxy/bascenario/editor/timeline/ObjectDragDrop.java deleted file mode 100644 index d408a5fb..00000000 --- a/editor/src/main/java/oxy/bascenario/editor/timeline/ObjectDragDrop.java +++ /dev/null @@ -1,36 +0,0 @@ -package oxy.bascenario.editor.timeline; - -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.ToString; - -@ToString -@RequiredArgsConstructor -public abstract class ObjectDragDrop { - public final ObjectOrEvent object; - - public long nearestTime = Long.MAX_VALUE; - public void time(long time, long last) { - if (time < last && Math.abs(time - last) < Math.abs(time - nearestTime)) { - nearestTime = last; - } - } - - @Getter - private boolean waiting; - public boolean loop, second; - public void waitForResult() { - this.waiting = true; - } - - @Getter - private boolean rejected; - public void reject() { - this.rejected = true; - } - public void reset() { - this.rejected = false; - } - - public abstract void accept(); -} diff --git a/editor/src/main/java/oxy/bascenario/editor/timeline/ObjectOrEvent.java b/editor/src/main/java/oxy/bascenario/editor/timeline/ObjectOrEvent.java deleted file mode 100644 index 45953d67..00000000 --- a/editor/src/main/java/oxy/bascenario/editor/timeline/ObjectOrEvent.java +++ /dev/null @@ -1,26 +0,0 @@ -package oxy.bascenario.editor.timeline; - -import oxy.bascenario.api.render.RenderLayer; -import oxy.bascenario.api.utils.math.Vec2; - -public class ObjectOrEvent { - public int track; - public long start, duration; - public ObjectRenderer renderer; - - public Object object; - public RenderLayer layer; - public boolean requireWait; - public Vec2 vec2; - - public ObjectOrEvent(Timeline timeline, int track, long start, long duration, Object object, RenderLayer layer, boolean requireWait, Vec2 vec2) { - this.track = track; - this.start = start; - this.duration = duration; - this.renderer = new ObjectRenderer(timeline, this); - this.object = object; - this.layer = layer; - this.requireWait = requireWait; - this.vec2 = vec2; - } -} diff --git a/editor/src/main/java/oxy/bascenario/editor/timeline/ObjectRenderer.java b/editor/src/main/java/oxy/bascenario/editor/timeline/ObjectRenderer.java deleted file mode 100644 index 5395b1ee..00000000 --- a/editor/src/main/java/oxy/bascenario/editor/timeline/ObjectRenderer.java +++ /dev/null @@ -1,256 +0,0 @@ -package oxy.bascenario.editor.timeline; - -import com.badlogic.gdx.math.MathUtils; -import imgui.ImColor; -import imgui.ImDrawList; -import imgui.ImGui; -import imgui.ImVec2; -import lombok.RequiredArgsConstructor; -import net.raphimc.thingl.ThinGL; -import net.raphimc.thingl.implementation.window.GLFWWindowInterface; -import net.raphimc.thingl.text.TextRun; -import org.lwjgl.glfw.GLFW; -import org.spongepowered.configurate.yaml.internal.snakeyaml.external.com.google.gdata.util.common.base.Escaper; -import oxy.bascenario.editor.utils.NameUtils; -import oxy.bascenario.editor.utils.TimeCompiler; -import oxy.bascenario.utils.font.FontUtils; -import oxy.bascenario.utils.font.TextUtils; - -@RequiredArgsConstructor -public class ObjectRenderer { - private final Timeline timeline; - private final ObjectOrEvent object; - - public float x, y, width; - - public void render() { - if (object.track < timeline.getVerticalScroll()) { - return; - } - - // This part determine render position and allat. - final float maxTime = Timeline.DEFAULT_MAX_TIME * timeline.getScale(); - - final ImVec2 size = ImGui.getWindowSize(), pos = ImGui.getWindowPos(); - float trackX = pos.x + size.x / 4, trackWidth = size.x - (size.x / 4); - float trackY = pos.y + 80 * ImGui.getStyle().getFontScaleDpi() + ((object.track - timeline.getVerticalScroll()) * 50 * ImGui.getStyle().getFontScaleDpi()); - - this.width = (object.duration / maxTime) * trackWidth; - if (!timeline.isDragging(this.object)) { // Fixed the position if not dragging.... - this.x = trackX + (object.start / maxTime) * trackWidth; - this.y = trackY; - - if (timeline.getScroll() > 0) { - final float ratio = (Timeline.DEFAULT_MAX_TIME * timeline.getScroll() * timeline.getScale()) / (Timeline.DEFAULT_MAX_TIME * timeline.getScale()); - this.x -= ratio * (size.x - size.x / 4); - } - - float distance = x - (pos.x + size.x / 4); - x = Math.max(x, pos.x + size.x / 4); - if (distance < 0) { - this.width = Math.max(0, distance + width); - } - } - - if (this.width <= 0) { - return; - } - - handleDraggingResult(); - - if (ImGui.isWindowFocused()) { - handleMouse(); - } - - // This part actually draw the object, nothing crazy here... - final ImDrawList drawList = ImGui.getWindowDrawList(); - drawList.addRectFilled(new ImVec2(x, y), new ImVec2(x + width, y + 50 * ImGui.getStyle().getFontScaleDpi()), ImColor.rgb(0.24f, 0.34f, 0.44f), 5f); - if (timeline.getSelectedObject() == this.object) { - drawList.addRect(new ImVec2(x, y), new ImVec2(x + width, y + 50 * ImGui.getStyle().getFontScaleDpi()), ImColor.rgb(255, 255, 255), 5f); - } - - String s = NameUtils.name(object.object); - float width = TextUtils.getVisualWidth(17 * ImGui.getStyle().getFontScaleDpi(), TextRun.fromString(FontUtils.DEFAULT, s).shape()); - if (width == 0) { - return; - } - s = s.substring(0, MathUtils.floor(Math.min((this.width / width) * s.length(), s.length()))); - - drawList.addText(new ImVec2(x + 5, y + 5), ImColor.rgb(255, 255, 255), s); - } - - private void handleDraggingResult() { - // This handle the current dragging object see if it overlaps and other stuff. - if (timeline.isDragging(this.object) || !timeline.isDragging()) { - return; - } - - final ObjectOrEvent dragging = timeline.getDraggingObject().object; - final ImVec2 size = ImGui.getWindowSize(), pos = ImGui.getWindowPos(); - float x = Math.max(ImGui.getMousePosX() - dragging.renderer.draggingX, pos.x + size.x / 4); - float y = Math.max(ImGui.getMousePosY() - dragging.renderer.draggingY, pos.y + 80 * ImGui.getStyle().getFontScaleDpi()); - float width = dragging.renderer.width; - - // This part check for object snapping. - float endDis = Math.abs((this.x + this.width) - x), startDis = Math.abs(this.x - (x + width)); - if (endDis < 3) { - ImGui.getWindowDrawList().addRectFilled(new ImVec2(this.x + this.width, ImGui.getWindowPosY()), new ImVec2(this.x + this.width + 1, ImGui.getWindowPosY() + ImGui.getWindowSizeY()), ImColor.rgb(255, 255, 255)); - dragging.renderer.draggingX = -((this.x + this.width + 0.01f) - ImGui.getMousePosX()); - } else if (startDis < 3) { - ImGui.getWindowDrawList().addRectFilled(new ImVec2(this.x - 1, ImGui.getWindowPosY()), new ImVec2(this.x, ImGui.getWindowPosY() + ImGui.getWindowSizeY()), ImColor.rgb(255, 255, 255)); - dragging.renderer.draggingX = -((this.x - width - 0.01f) - ImGui.getMousePosX()); - } - - // This code part checks if the drag result is overlapping something else. - if (!timeline.getDraggingObject().isWaiting()) { - return; - } - - int track = trackFromY(timeline, y); - if (track != this.object.track) { - return; // Not the same track, skip! - } - - if (!timeline.getDraggingObject().second) { - final float ratio = (object.renderer.x - pos.x - size.x / 4) / (size.x - size.x / 4); - long time = (long) (Timeline.DEFAULT_MAX_TIME * timeline.getScale() * timeline.getScroll() + ratio * Timeline.DEFAULT_MAX_TIME * timeline.getScale()); - // Determine the close object time to snap to. - timeline.getDraggingObject().time(time, object.start + object.duration); - } else { - long min = timeline.getDraggingObject().nearestTime + 1, max = min + object.duration; - if (max >= object.start && min < object.start + object.duration) { - timeline.getDraggingObject().reject(); - } - return; - } - - // Overlap! This result should not be valid. - if (x + width >= this.x && x <= this.x + this.width) { - timeline.getDraggingObject().reject(); - } - } - - private float draggingX, draggingY; - private void handleMouse() { - if (handleDurationResize()) { - return; - } - - final boolean over = ImGui.isMouseHoveringRect(new ImVec2(x, y), new ImVec2(x + width, y + 50 * ImGui.getStyle().getFontScaleDpi())); - final ImVec2 mouse = ImGui.getMousePos(), pos = ImGui.getWindowPos(), size = ImGui.getWindowSize(); - - boolean dragging = timeline.isDragging(this.object); - if (dragging && !timeline.getDraggingObject().isWaiting()) { - this.x = Math.max(mouse.x - this.draggingX, pos.x + size.x / 4); - this.y = Math.max(mouse.y - this.draggingY, pos.y + 80 * ImGui.getStyle().getFontScaleDpi()); - } - - float length = ImGui.getMouseDragDelta().x * ImGui.getMouseDragDelta().x + ImGui.getMouseDragDelta().y * ImGui.getMouseDragDelta().y; - if (ImGui.isMouseClicked(0) && over) { - timeline.setSelectedObject(this.object); // Selected an element. - } else if (ImGui.isMouseDown(0) && !timeline.isDragging() && over && length > 1) { - // Start dragging an object if we can! - timeline.setDraggingObject(new ObjectDragDrop(this.object) { - @Override - public void accept() { - final ImVec2 pos = ImGui.getWindowPos(), size = ImGui.getWindowSize(); - - final long oldStart = object.start; - final int oldTrack = object.track; - if (second) { - object.start = nearestTime + 1; - } else { - final float ratio = (object.renderer.x - pos.x - size.x / 4) / (size.x - size.x / 4); - object.start = (long) (Timeline.DEFAULT_MAX_TIME * timeline.getScale() * timeline.getScroll() + ratio * Timeline.DEFAULT_MAX_TIME * timeline.getScale()); - } - object.track = trackFromY(timeline, object.renderer.y); - - timeline.queueUndo(() -> { - object.start = oldStart; - object.track = oldTrack; - }); - } - }); - - this.draggingX = mouse.x - this.x; - this.draggingY = mouse.y - this.y; - dragging = true; - } - - // Stop dragging, now we wait for result..... - if (dragging && !ImGui.isMouseDown(0)) { - timeline.getDraggingObject().waitForResult(); - } - } - - private boolean resizing; - private boolean handleDurationResize() { - if (timeline.isDragging()) { - this.resizing = false; - return false; - } - - final ImVec2 mouse = ImGui.getMousePos(), size = ImGui.getWindowSize(); - final boolean yMatch = mouse.y >= y && mouse.y <= y + (50 * ImGui.getStyle().getFontScaleDpi()); - if (resizing && yMatch) { // If we're already resizing. we can't check for x axis xD. - float ratio = (mouse.x - (x + width)) / (size.x - size.x / 4); - long duration = (long) (Timeline.DEFAULT_MAX_TIME * timeline.getScale() * ratio); - - // I wonder how fast this is.... - int next = timeline.getObjects().indexOf(this.object); - if (next != -1 && next + 1 != timeline.getObjects().size()) { - ObjectOrEvent nextObject = timeline.getObjects().get(next + 1); - if (nextObject.track == this.object.track) { - duration = Math.min(duration, nextObject.start - (object.start + object.duration)); - } - } - - if (TimeCompiler.canResize(object.object)) { - final long oldDuration = object.duration; - final Object oldObject = object; - final float oldWidth = this.width; - object.duration = Math.max(0, object.duration + duration); - object.object = TimeCompiler.addTime(object.object, (int) duration); - - this.width = ((float) object.duration / (Timeline.DEFAULT_MAX_TIME * timeline.getScale())) * (size.x - size.x / 4); - timeline.queueUndo(() -> { - this.width = oldWidth; - this.object.duration = oldDuration; - this.object.object = oldObject; - }); - } - - resizing = ImGui.isMouseDown(0); - return true; - } - - resizing = false; - float xDistanceToMax = Math.abs(mouse.x - (x + width)); - if (xDistanceToMax > 5 || !yMatch) { - return false; - } - - if (!ImGui.isMouseDown(0)) { - return false; - } -// GLFW.glfwSetCursor(((GLFWWindowInterface) ThinGL.windowInterface()).getWindowHandle(), GLFW.glfwCreateStandardCursor(GLFW.GLFW_HRESIZE_CURSOR)); - - timeline.setDraggingObject(null); - resizing = true; - return true; - } - - private static int trackFromY(Timeline timeline, float y) { - float approximateTrack = (y - (ImGui.getWindowPosY() + 80)) / (50f * ImGui.getStyle().getFontScaleDpi()); - int ceil = MathUtils.ceil(approximateTrack), floor = MathUtils.floor(approximateTrack); - // See if floor or ceil is closer... - int trackId; - if (Math.abs(y - (ImGui.getWindowPosY() + 80 * ImGui.getStyle().getFontScaleDpi() + (50 * ImGui.getStyle().getFontScaleDpi() * ceil))) > Math.abs(y - (ImGui.getWindowPosY() + 80 * ImGui.getStyle().getFontScaleDpi() + (50 * ImGui.getStyle().getFontScaleDpi() * floor)))) { - trackId = floor; - } else { - trackId = ceil; - } - - return trackId + timeline.getVerticalScroll(); - } -} diff --git a/editor/src/main/java/oxy/bascenario/editor/timeline/Timeline.java b/editor/src/main/java/oxy/bascenario/editor/timeline/Timeline.java deleted file mode 100644 index 00059cb5..00000000 --- a/editor/src/main/java/oxy/bascenario/editor/timeline/Timeline.java +++ /dev/null @@ -1,338 +0,0 @@ -package oxy.bascenario.editor.timeline; - -import imgui.*; -import imgui.flag.ImGuiKey; -import lombok.Getter; -import lombok.Setter; -import oxy.bascenario.api.Scenario; -import oxy.bascenario.api.render.RenderLayer; -import oxy.bascenario.api.utils.math.Vec2; -import oxy.bascenario.editor.screen.BaseScenarioEditorScreen; -import oxy.bascenario.editor.utils.TrackParser; -import oxy.bascenario.utils.font.FontUtils; -import oxy.bascenario.utils.ImGuiUtils; - -import java.util.ArrayList; -import java.util.Comparator; -import java.util.Deque; -import java.util.List; -import java.util.concurrent.ConcurrentLinkedDeque; - -// Shit code but whatever. -public class Timeline { - @Getter - private final BaseScenarioEditorScreen screen; - - public static final long DEFAULT_MAX_TIME = 15000; // 15 seconds - - @Getter - private final List objects; - public void put(int track, long start, long duration, Object object, RenderLayer layer, boolean wait, Vec2 vec2) { - final ObjectOrEvent objectOrEvent = new ObjectOrEvent(this, track, start, duration, object, layer, wait, vec2); - this.objects.add(objectOrEvent); - this.objects.sort(Comparator.comparingInt(o -> o.track)); - this.objects.sort(Comparator.comparingLong(o -> o.start)); - - queueUndo(() -> this.objects.remove(objectOrEvent)); - this.setSelectedObject(objectOrEvent); - } - - // Undo and Redo - private final Deque undo = new ConcurrentLinkedDeque<>(); - public void queueUndo(Runnable run) { - this.queueUpdate = true; - undo.add(run); - if (undo.size() > 30) { - undo.poll(); - } - } - public boolean canUndo() { - return !undo.isEmpty(); - } - public void undo() { - if (undo.isEmpty()) { - return; - } - - undo.pollLast().run(); - this.queueUpdate = true; -// this.redo = - } -// private Runnable redo; - - @Getter @Setter - private ObjectOrEvent selectedObject; - - @Setter @Getter - private ObjectDragDrop draggingObject; - public boolean isDragging() { - return draggingObject != null; - } - public boolean isDragging(ObjectOrEvent object) { - return this.draggingObject != null && object == this.draggingObject.object; - } - - private boolean queueUpdate; - - public Timeline(BaseScenarioEditorScreen screen, Scenario.Builder scenario) { - this.screen = screen; - - if (scenario == null) { - this.objects = new ArrayList<>(); - } else { - this.objects = TrackParser.parse(this, scenario.build()); - } - } - - @Setter @Getter - private boolean playing, tickTime = true; - - @Getter - private float scroll = 0, scale = 1; - @Getter - private int verticalScroll = 0; - @Setter @Getter - private long timestamp; - private long since; - - public void render() { -// GLFW.glfwSetCursor(((GLFWWindowInterface) ThinGL.windowInterface()).getWindowHandle(), GLFW.glfwCreateStandardCursor(GLFW.GLFW_ARROW_CURSOR)); - if (since == 0) { - since = System.currentTimeMillis(); - } - if (playing && tickTime) { - timestamp += System.currentTimeMillis() - since; - } - since = System.currentTimeMillis(); - - scroll = Math.max(0, scroll); - - ImGui.begin("Timeline"); - if (ImGui.getIO().getMouseDown(0) && ImGui.isWindowFocused() && draggingObject == null) { - onMouseDown(ImGui.getIO().getMousePos()); - } - if (playing || ImGui.getIO().getMouseDown(0) && ImGui.isWindowFocused()) { - if (timestamp != 0 && timestamp >= (scroll + 1) * DEFAULT_MAX_TIME * scale) { - long distance = (long) (timestamp - ((scroll + 1) * DEFAULT_MAX_TIME * scale)); - float ratio = (float) distance / DEFAULT_MAX_TIME; - scroll += ratio + 0.2f; - } else if (timestamp != 0 && timestamp <= DEFAULT_MAX_TIME * scroll * scale) { - long distance = (long) (DEFAULT_MAX_TIME * scroll * scale - timestamp); - float ratio = (float) distance / DEFAULT_MAX_TIME; - scroll -= ratio + 0.2f; - } - } - - - final ImVec2 size = ImGui.getWindowSize(), pos = ImGui.getWindowPos(); - final ImVec2 mouse = ImGui.getMousePos(); - if (mouse.x >= pos.x && mouse.x <= pos.x + size.x && mouse.y >= pos.y && mouse.y <= pos.y + size.y && ImGui.isWindowFocused()) { - final float scroll = ImGui.getIO().getMouseWheel(); - if (mouse.x < pos.x + size.x / 4) { - this.verticalScroll = Math.max(0, this.verticalScroll - (int)scroll); - } else if (ImGui.getIO().getKeyCtrl()) { - if (scroll > 0) { - this.scale += 0.05f * this.scale; - } else if (scroll < 0) { - this.scale = Math.max(0.001f, this.scale - 0.05f * this.scale); - } - } else { - this.scroll = Math.max(0, this.scroll + scroll); - } - } - - if (this.queueUpdate) { - this.objects.sort(Comparator.comparingInt(o -> o.track)); - this.objects.sort(Comparator.comparingLong(o -> o.start)); - this.screen.update(); - this.queueUpdate = false; - } - - ImGui.getWindowDrawList().addRectFilled(new ImVec2(pos.x, pos.y), new ImVec2(pos.x + (size.x / 4), pos.y + size.y), ImColor.rgb(25, 25, 25)); - drawElapsedTimeSegments(size.x / 4, pos, size); - drawTimelineSegments(size.x / 4, pos, size); - drawElapsedTime(size.x / 4, pos, size); - drawTimelineCursor(size.x / 4, pos, size); - - if (ImGui.isWindowFocused() && this.selectedObject != null && ImGui.isKeyPressed(ImGuiKey.Delete)) { - final ObjectOrEvent old = this.selectedObject; - queueUndo(() -> this.objects.add(old)); - this.objects.remove(this.selectedObject); - this.selectedObject = null; - } - - if (ImGui.isWindowFocused() && ImGui.isKeyPressed(ImGuiKey.Space)) { - this.screen.setPlaying(!this.isPlaying()); - } - - if (!this.isDragging()) { - ImGui.end(); - return; - } - - // Render dragging separately on top of everything... - if (this.draggingObject != null) { - this.draggingObject.object.renderer.render(); - } - - if (this.draggingObject == null || !this.draggingObject.isWaiting()) { - ImGui.end(); - return; - } else if (!this.draggingObject.loop) { - ImGui.end(); - this.draggingObject.loop = true; - return; - } - - if (!this.draggingObject.isRejected()) { - this.draggingObject.accept(); // Accept the result of dragging if not rejected... - this.draggingObject = null; - } else { - final float ratio = (this.draggingObject.object.renderer.x - pos.x - size.x / 4) / (size.x - size.x / 4); - long time = (long) (Timeline.DEFAULT_MAX_TIME * scale * scroll + ratio * Timeline.DEFAULT_MAX_TIME * scale); - - if (this.draggingObject.second || this.draggingObject.nearestTime - time > 350L) { - this.draggingObject = null; // Done! - } else { - this.draggingObject.reset(); - this.draggingObject.second = true; - } - } - ImGui.end(); - } - - private void drawTimelineSegments(float timelineManagerWidth, ImVec2 pos, ImVec2 size) { - final ImDrawList drawList = ImGui.getWindowDrawList(); - - float yScale = 80 * ImGui.getStyle().getFontScaleDpi(); - float y = pos.y + yScale; - -// GLFW.glfwSetCursor(windowHandle, GLFW.glfwCreateStandardCursor(GLFW.GLFW_VRESIZE_CURSOR)); - - float segmentY = 50 * ImGui.getStyle().getFontScaleDpi(); - for (int i = verticalScroll; i <= ((size.y - yScale) / segmentY) + verticalScroll; i++) { - drawList.addRectFilled(new ImVec2(pos.x, y), new ImVec2(pos.x + timelineManagerWidth, y + segmentY), ImColor.rgb(33, 33, 33)); - - ImGui.pushFont(FontUtils.IM_FONT_REGULAR, 35); - drawList.addText(new ImVec2(pos.x + 10, y), ImColor.rgb(255, 255, 255), "Track " + i); - ImGui.popFont(); - - drawList.addRect(new ImVec2(pos.x, y), new ImVec2(pos.x + size.x, y + 0.5f), ImColor.rgb(50, 50, 50)); - - y += segmentY; - } - - this.objects.forEach(object -> { - if (!this.isDragging(object)) { - object.renderer.render(); - } - }); - } - - private void drawElapsedTimeSegments(float timelineManagerWidth, ImVec2 pos, ImVec2 size) { - final ImDrawList drawList = ImGui.getWindowDrawList(); - - drawList.addRectFilled(new ImVec2(pos.x + timelineManagerWidth, pos.y), new ImVec2(pos.x + size.x, pos.y + size.y), ImColor.rgb(20, 19, 24)); - for (int i = 0; i <= 5; i++) { - ImGui.pushFont(FontUtils.IM_FONT_SEMI_BOLD, 20); - long time = (long) ((DEFAULT_MAX_TIME * scale * scroll) + (DEFAULT_MAX_TIME * scale * (i / 5f))); - float segmentX = timestampToPosition(time, timelineManagerWidth + pos.x, size.x - timelineManagerWidth); - - drawList.addRect(new ImVec2(segmentX, pos.y), new ImVec2(segmentX + 0.5f, pos.y + size.y), ImColor.rgb(50, 50, 50)); - drawList.addText(segmentX + 5, pos.y + 30 * ImGui.getStyle().getFontScaleDpi(), ImColor.rgb(255, 255, 255), format(time)); - - ImGui.popFont(); - } - } - - private void drawElapsedTime(float timelineManagerWidth, ImVec2 pos, ImVec2 size) { - final ImDrawList drawList = ImGui.getWindowDrawList(); - - float yScale = 80 * ImGui.getStyle().getFontScaleDpi(); - - drawList.addRectFilled(new ImVec2(pos.x, pos.y), new ImVec2(pos.x + timelineManagerWidth, - pos.y + yScale), ImColor.rgb(50, 50, 50)); - - drawList.addRect(new ImVec2(pos.x, pos.y), new ImVec2(pos.x + size.x, pos.y + yScale), ImColor.rgb(50, 50, 50)); - - ImGui.pushFont(FontUtils.IM_FONT_SEMI_BOLD, 30); - drawList.addText(pos.x + 20, pos.y + 21 * ImGui.getStyle().getFontScaleDpi(), ImColor.rgb(255, 255, 255), format(timestamp)); - ImGui.popFont(); - - long millis = timestamp % 1000; - long second = (timestamp / 1000) % 60; - long minute = (timestamp / (1000 * 60)) % 60; - long hour = (timestamp / (1000 * 60 * 60)) % 24; - ImGui.pushItemWidth(timelineManagerWidth - 15); - ImGui.setCursorPos(5, 55 * ImGui.getStyle().getFontScaleDpi()); - int[] time = new int[] {Math.toIntExact(hour), Math.toIntExact(minute), Math.toIntExact(second), Math.toIntExact(millis)}; - ImGuiUtils.inputInt4("", time); - ImGui.popItemWidth(); - - long lastTimestamp = timestamp; - timestamp = Math.min(time[0], 99) * (long)3.6e+6 + Math.min(69, time[1]) * 60000L + Math.min(59, time[2]) * 1000L + Math.min(999, time[3]); - if (timestamp != lastTimestamp) { - screen.update(); - } - } - - private void drawTimelineCursor(float timelineManagerWidth, ImVec2 pos, ImVec2 size) { - final ImDrawList drawList = ImGui.getWindowDrawList(); - - final float cursorX = timestampToPosition(timestamp, timelineManagerWidth + pos.x, size.x - timelineManagerWidth); - if (cursorX < timelineManagerWidth + pos.x) { - return; - } - - drawList.addRect(new ImVec2(cursorX, pos.y), new ImVec2(cursorX + ImGui.getStyle().getFontScaleDpi(), pos.y + size.y), ImColor.rgb(255, 255, 255)); - } - - private void onMouseDown(ImVec2 vec2) { - final ImVec2 size = ImGui.getWindowSize(), pos = ImGui.getWindowPos(); - - if (vec2.x < pos.x || vec2.x > pos.x + size.x || vec2.y < pos.y + 20 || vec2.y > pos.y + size.y) { - return; - } - - if (vec2.x > pos.x + size.x - 20) { - scroll += 0.01f * ((pos.x + size.x - vec2.x) / 20) * scale; - } - - if (vec2.x < pos.x + size.x / 4) { - if (vec2.y > pos.y + 80 * ImGui.getStyle().getFontScaleDpi()) { - final float distance = pos.x + size.x / 4 - vec2.x; - final float ratio = distance / (size.x - size.x / 4); - final long backtrackTime = (long) (ratio * (DEFAULT_MAX_TIME * scale * 0.1)); - timestamp = Math.max(0, timestamp - backtrackTime); - screen.update(); - this.selectedObject = null; - } - return; - } - - if (vec2.y > pos.y + 80 * ImGui.getStyle().getFontScaleDpi()) { - return; - } - - final float ratio = (vec2.x - pos.x - size.x / 4) / (size.x - size.x / 4); - long last = timestamp; - timestamp = (long) (DEFAULT_MAX_TIME * scale * scroll + ratio * DEFAULT_MAX_TIME * scale); - this.selectedObject = null; - if (last != timestamp) { - screen.update(); - } - } - - private float timestampToPosition(long timestamp, float offsetX, float size) { - return offsetX + ((timestamp - (Timeline.DEFAULT_MAX_TIME * scale * scroll)) / (Timeline.DEFAULT_MAX_TIME * scale)) * size; - } - - // Don't comment on this. - private static String format(long ms) { - long millis = ms % 1000; - long second = (ms / 1000) % 60; - long minute = (ms / (1000 * 60)) % 60; - long hour = (ms / (1000 * 60 * 60)) % 24; - return (hour < 10 ? "0" + hour : hour) + ":" + (minute < 10 ? "0" + minute : minute) + ":" + (second < 10 ? "0" + second : second) + ":" + (millis < 10 ? "0" + millis : millis); - } -} diff --git a/editor/src/main/java/oxy/bascenario/editor/utils/AudioUtils.java b/editor/src/main/java/oxy/bascenario/editor/utils/AudioUtils.java deleted file mode 100644 index 988ca02a..00000000 --- a/editor/src/main/java/oxy/bascenario/editor/utils/AudioUtils.java +++ /dev/null @@ -1,26 +0,0 @@ -package oxy.bascenario.editor.utils; - -import oxy.bascenario.Base; -import oxy.bascenario.api.managers.other.Asset; -import oxy.bascenario.api.utils.FileInfo; -import oxy.bascenario.managers.other.AudioAsset; - -public class AudioUtils { - public static long toDuration(String scenario, SoundAsElement sound) { - long duration = toDuration(scenario, sound.sound().file()); - duration -= (long) (sound.start() * 1000L); - return duration; - } - - public static long toDuration(String scenario, FileInfo info) { - try { - final Asset asset = Base.instance().assetsManager().assets(scenario, info); - if (asset.asset() instanceof AudioAsset audioAsset) { - return audioAsset.duration(); - } - } catch (Exception ignored) { - } - - return 0; - } -} diff --git a/editor/src/main/java/oxy/bascenario/editor/utils/SelectionUtils.java b/editor/src/main/java/oxy/bascenario/editor/utils/SelectionUtils.java deleted file mode 100644 index 74f5ab70..00000000 --- a/editor/src/main/java/oxy/bascenario/editor/utils/SelectionUtils.java +++ /dev/null @@ -1,14 +0,0 @@ -package oxy.bascenario.editor.utils; - -import imgui.ImVec2; -import oxy.bascenario.api.render.elements.Sprite; -import oxy.bascenario.screens.renderer.element.SpriteRenderer; -import oxy.bascenario.screens.renderer.element.base.ElementRenderer; - -public class SelectionUtils { - public static ImVec2 selectionBox(ElementRenderer base) { - return switch (base) { - default -> null; - }; - } -} diff --git a/editor/src/main/java/oxy/bascenario/editor/utils/SoundAsElement.java b/editor/src/main/java/oxy/bascenario/editor/utils/SoundAsElement.java deleted file mode 100644 index 26b8eb82..00000000 --- a/editor/src/main/java/oxy/bascenario/editor/utils/SoundAsElement.java +++ /dev/null @@ -1,9 +0,0 @@ -package oxy.bascenario.editor.utils; - -import lombok.Builder; -import oxy.bascenario.api.effects.Sound; - -// Just a duplicate of PlaySoundEvent (kinda) to be less confusing. -@Builder(toBuilder = true, builderClassName = "Builder") -public record SoundAsElement(Sound sound, int in, int out, float start, long max) { -} diff --git a/editor/src/main/java/oxy/bascenario/editor/utils/TrackParser.java b/editor/src/main/java/oxy/bascenario/editor/utils/TrackParser.java deleted file mode 100644 index 5861756f..00000000 --- a/editor/src/main/java/oxy/bascenario/editor/utils/TrackParser.java +++ /dev/null @@ -1,175 +0,0 @@ -package oxy.bascenario.editor.utils; - -import oxy.bascenario.api.Scenario; -import oxy.bascenario.api.Timestamp; -import oxy.bascenario.api.effects.Sound; -import oxy.bascenario.api.event.api.Event; -import oxy.bascenario.api.event.dialogue.AddDialogueEvent; -import oxy.bascenario.api.event.dialogue.ShowOptionsEvent; -import oxy.bascenario.api.event.dialogue.ShowQuestionSelectionEvent; -import oxy.bascenario.api.event.dialogue.StartDialogueEvent; -import oxy.bascenario.api.event.element.AddElementEvent; -import oxy.bascenario.api.event.element.RemoveElementEvent; -import oxy.bascenario.api.event.sound.PlaySoundEvent; -import oxy.bascenario.api.event.sound.StopSoundEvent; -import oxy.bascenario.editor.timeline.ObjectOrEvent; -import oxy.bascenario.editor.timeline.Timeline; -import oxy.bascenario.utils.math.Pair; - -import java.util.*; - -public class TrackParser { - // TODO: Sub elements.... - public static List parse(List objects) { - final List timestamps = new ArrayList<>(); - - final Map>> events = new TreeMap<>(); - - objects.forEach((object) -> { - final Pair> list = events.computeIfAbsent(object.start, n -> new Pair<>(object.requireWait, new ArrayList<>())); - if (object.object instanceof Event event) { - list.right().add(event); - } else if (object.object instanceof SoundAsElement(Sound sound, int in, int out, float start, long max)) { - list.right().add(new PlaySoundEvent(sound, in, start)); - if (out != Integer.MIN_VALUE && sound != null) { - events.computeIfAbsent(object.start + object.duration, n -> new Pair<>(object.requireWait, new ArrayList<>())).right().add(new StopSoundEvent(sound.id(), out)); - } - } else { - list.right().add(new AddElementEvent(object.track, object.vec2, object.object, object.layer)); - events.computeIfAbsent(object.start + object.duration, n -> new Pair<>(object.requireWait, new ArrayList<>())).right().add(new RemoveElementEvent(object.track)); - } - }); - - long last = 0; - for (Map.Entry>> entry : events.entrySet()) { - final Long time = entry.getKey(); - long delay = time - last; - last = time; - - long max = 0; - for (Event event : entry.getValue().right()) { - if (event instanceof StartDialogueEvent || event instanceof AddDialogueEvent || event instanceof ShowOptionsEvent || event instanceof ShowQuestionSelectionEvent) { - max = Math.max(max, TimeCompiler.compileTime(event)); - } - } - last += max; - - timestamps.add(new Timestamp(entry.getValue().left(), null, (int) delay, entry.getValue().right())); - } - -// System.out.println(Arrays.toString(timestamps.toArray())); - - return timestamps; - } - - public static List parse(Timeline timeline, Scenario scenario) { - final List result = new ArrayList<>(); - final Map>> occupies = new HashMap<>(); - - final Map previous = new HashMap<>(); - - long time = 0; - for (Timestamp timestamp : scenario.getTimestamps()) { - time += timestamp.time(); - - for (Event other : timestamp.events()) { switch (other) { - case AddElementEvent event -> { - long duration = TimeCompiler.compileTime(event.element()); - - final ObjectOrEvent objectOrEvent = new ObjectOrEvent(timeline, event.id(), time, duration, event.element(), event.layer(), timestamp.waitForDialogue(), event.position()); - previous.put(event.id(), objectOrEvent); - result.add(objectOrEvent); - - occupies.computeIfAbsent(event.id(), i -> new HashMap<>()).put(time, new Pair<>(time, time + duration)); - } - case RemoveElementEvent event -> { - Object object = previous.get(event.id()); - if (!(object instanceof ObjectOrEvent oOE)) { - continue; - } - - oOE.duration = Math.min(time - oOE.start, oOE.duration); - - Map> track = occupies.get(oOE.track); - if (track != null) { - final Pair pair = track.get(oOE.start); - if (pair != null) { - pair.right(pair.left() + oOE.duration); - - } - } - } - case PlaySoundEvent event -> { - long duration = AudioUtils.toDuration(scenario.getName(), event.sound().file()); - int track = findNonOccupiedSlot(time, duration, occupies); - - final SoundAsElement element = new SoundAsElement(event.sound(), (int) event.duration(), 0, event.start(), duration); - final ObjectOrEvent objectOrEvent = new ObjectOrEvent(timeline, track, time, duration, element, null, timestamp.waitForDialogue(), null); - previous.put(track, objectOrEvent); - result.add(objectOrEvent); - - occupies.computeIfAbsent(track, i -> new HashMap<>()).put(time, new Pair<>(time, time + duration)); - } - case StopSoundEvent event -> { - Object object = previous.get(event.id()); - if (!(object instanceof ObjectOrEvent oOE) || !(oOE.object instanceof SoundAsElement sound)) { - continue; - } - - final SoundAsElement.Builder builder = sound.toBuilder(); - builder.out(event.duration()); - - oOE.object = builder.build(); - oOE.duration = Math.min(time - oOE.start, oOE.duration); - - Map> track = occupies.get(oOE.track); - if (track != null) { - final Pair pair = track.get(oOE.start); - if (pair != null) { - pair.right(pair.left() + oOE.duration); - } - } - } - default -> { - long duration = TimeCompiler.compileTime(other); - int track = findNonOccupiedSlot(time, duration, occupies); - - final ObjectOrEvent objectOrEvent = new ObjectOrEvent(timeline, track, time, duration, other, null, timestamp.waitForDialogue(), null); - previous.put(track, objectOrEvent); - result.add(objectOrEvent); - - occupies.computeIfAbsent(track, i -> new HashMap<>()).put(time, new Pair<>(time, time + duration)); - - if (other instanceof StartDialogueEvent || other instanceof AddDialogueEvent || other instanceof ShowOptionsEvent) { - time += TimeCompiler.compileTime(other); - } - } - }} - } - - return result; - } - - private static int findNonOccupiedSlot(long time, long duration, Map>> occupies) { - int i = 0; - - Map> map; - while ((map = occupies.get(i)) != null) { - boolean occupied = false; - for (Pair pair : map.values()) { - final long maxTime = Math.abs(pair.right()), minTime = pair.left(); - if (maxTime >= time && minTime <= time + duration) { - occupied = true; - break; - } - } - - if (!occupied) { - return i; - } - i++; - } - - return i; - } -} diff --git a/editor/src/main/java/oxy/bascenario/editor/utils/NameUtils.java b/editor/src/main/java/oxy/bascenario/utils/NameUtils.java similarity index 97% rename from editor/src/main/java/oxy/bascenario/editor/utils/NameUtils.java rename to editor/src/main/java/oxy/bascenario/utils/NameUtils.java index 881aa6f6..d2885a25 100644 --- a/editor/src/main/java/oxy/bascenario/editor/utils/NameUtils.java +++ b/editor/src/main/java/oxy/bascenario/utils/NameUtils.java @@ -1,4 +1,4 @@ -package oxy.bascenario.editor.utils; +package oxy.bascenario.utils; import oxy.bascenario.api.event.*; import oxy.bascenario.api.event.animation.PlayAnimationEvent; @@ -68,7 +68,7 @@ public static String name(Object object) { case LocationInfo ignored -> "Location Info"; case AnimatedText ignored -> "Typing Text"; - case SoundAsElement ignored -> "Sound"; +// case SoundAsElement ignored -> "Sound"; case AnimatedImage ignored -> "Gif"; case ShowButtonsEvent ignored -> "Show Buttons"; diff --git a/editor/src/main/java/oxy/bascenario/editor/utils/TimeCompiler.java b/editor/src/main/java/oxy/bascenario/utils/TimeCompiler.java similarity index 99% rename from editor/src/main/java/oxy/bascenario/editor/utils/TimeCompiler.java rename to editor/src/main/java/oxy/bascenario/utils/TimeCompiler.java index 2a3d62a0..87764b67 100644 --- a/editor/src/main/java/oxy/bascenario/editor/utils/TimeCompiler.java +++ b/editor/src/main/java/oxy/bascenario/utils/TimeCompiler.java @@ -1,4 +1,4 @@ -package oxy.bascenario.editor.utils; +package oxy.bascenario.utils; import oxy.bascenario.api.event.ScreenTransitionEvent; import oxy.bascenario.api.event.background.ClearBackgroundEvent; diff --git a/editor/src/main/java/oxy/bascenario/utils/components/ButtonImage.java b/editor/src/main/java/oxy/bascenario/utils/components/ButtonImage.java new file mode 100644 index 00000000..a828b790 --- /dev/null +++ b/editor/src/main/java/oxy/bascenario/utils/components/ButtonImage.java @@ -0,0 +1,92 @@ +package oxy.bascenario.utils.components; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.experimental.Accessors; +import net.lenni0451.commons.color.Color; +import net.lenni0451.rivet.backend.render.Renderer; +import net.lenni0451.rivet.backend.thingl.ThinGLTexture; +import net.lenni0451.rivet.component.Component; +import net.lenni0451.rivet.input.mouse.MouseButton; +import net.lenni0451.rivet.input.mouse.MouseButtonEvent; +import net.lenni0451.rivet.math.Size; +import net.lenni0451.rivet.theme.ThemeOption; +import net.raphimc.thingl.gl.resource.image.texture.impl.Texture2D; +import oxy.bascenario.utils.animation.math.ColorAnimations; + +@RequiredArgsConstructor +@Accessors(fluent = true, chain = true, makeFinal = true) +public class ButtonImage extends Component { + private boolean hovered = false; + + @Getter + private final ThinGLTexture texture; + + private final ClickListener clickListener; + + @Getter + private final ThemeOption hoverColor; + + @Getter + private final ThemeOption blendDuration; + + private final ColorAnimations color = new ColorAnimations(Color.WHITE); + + public ButtonImage(final Texture2D texture, final ClickListener clickListener) { + this.texture = new ThinGLTexture(texture); + this.clickListener = clickListener; + + this.hoverColor = new ThemeOption<>(this, null); + this.hoverColor.set(Color.WHITE); + + this.blendDuration = new ThemeOption<>(this, null); + this.blendDuration.set(800); + } + + @Override + protected boolean onComponentMouseUp(MouseButtonEvent event, Size size) { + if (event.button() == MouseButton.LEFT) { + clickListener.onClick(event); + return true; + } + + return false; + } + + @Override + public void render(final Renderer renderer, final Size size) { + renderer.image(this.texture, 0, 0, size.width(), size.height(), color.color()); + } + + @Override + public Size computeIdealSize(final Size constraints) { + return new Size(this.texture.width(), this.texture.height()); + } + + @Override + protected void onComponentRemoved() { + this.hovered = false; + } + + @Override + protected void onComponentDisabled() { + this.hovered = false; + } + + @Override + protected void onComponentMouseEnter() { + this.hovered = true; + color.set(this.hoverColor.value(), this.blendDuration.value()); + } + + @Override + protected void onComponentMouseLeave() { + this.hovered = false; + color.set(Color.WHITE, this.blendDuration.value()); + } + + @FunctionalInterface + public interface ClickListener { + void onClick(final MouseButtonEvent event); + } +} diff --git a/editor/src/main/java/oxy/bascenario/utils/components/PlayButton.java b/editor/src/main/java/oxy/bascenario/utils/components/PlayButton.java new file mode 100644 index 00000000..5b37ef0b --- /dev/null +++ b/editor/src/main/java/oxy/bascenario/utils/components/PlayButton.java @@ -0,0 +1,80 @@ +package oxy.bascenario.utils.components; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import lombok.experimental.Accessors; +import net.lenni0451.commons.color.Color; +import net.lenni0451.rivet.backend.Texture; +import net.lenni0451.rivet.backend.render.Renderer; +import net.lenni0451.rivet.backend.thingl.ThinGLTexture; +import net.lenni0451.rivet.component.Component; +import net.lenni0451.rivet.input.mouse.MouseButton; +import net.lenni0451.rivet.input.mouse.MouseButtonEvent; +import net.lenni0451.rivet.math.Size; +import net.lenni0451.rivet.theme.ThemeOption; +import oxy.bascenario.Base; +import oxy.bascenario.utils.animation.math.ColorAnimations; + +import java.util.function.Consumer; + +@RequiredArgsConstructor +@Accessors(fluent = true, chain = true, makeFinal = true) +public class PlayButton extends Component { + @Setter + private boolean state; + private Consumer onStateChanged; + + @Getter + private final ThemeOption blendDuration; + + private final ColorAnimations color = new ColorAnimations(Color.WHITE); + + private Texture texture1, texture2; + public PlayButton(boolean state, Consumer onStateChanged) { + this.state = state; + this.onStateChanged = onStateChanged; + + this.blendDuration = new ThemeOption<>(this, null); + this.blendDuration.set(800); + + this.texture1 = new ThinGLTexture(Base.instance().assetsManager().texture("assets/base/uis/editor/pause_18.png")); + this.texture2 = new ThinGLTexture(Base.instance().assetsManager().texture("assets/base/uis/editor/play_18.png")); + } + + @Override + protected boolean onComponentMouseUp(MouseButtonEvent event, Size size) { + if (event.button() == MouseButton.LEFT) { + this.state = !this.state; + onStateChanged.accept(this.state); + return true; + } + + return false; + } + + @Override + public void render(final Renderer renderer, final Size size) { + renderer.image(state ? this.texture1 : this.texture2, 0, 0, size.width(), size.height(), color.color()); + } + + @Override + public Size computeIdealSize(final Size constraints) { + return new Size(18, 18); + } + + @Override + protected void onComponentMouseEnter() { + color.set(Color.fromRGB(145, 218, 255), this.blendDuration.value()); + } + + @Override + protected void onComponentMouseLeave() { + color.set(Color.WHITE, this.blendDuration.value()); + } + + @FunctionalInterface + public interface ClickListener { + void onClick(final MouseButtonEvent event); + } +} diff --git a/editor/src/main/java/oxy/bascenario/utils/components/TabButton.java b/editor/src/main/java/oxy/bascenario/utils/components/TabButton.java new file mode 100644 index 00000000..5810dc6d --- /dev/null +++ b/editor/src/main/java/oxy/bascenario/utils/components/TabButton.java @@ -0,0 +1,63 @@ +package oxy.bascenario.utils.components; + +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import lombok.experimental.Accessors; +import net.lenni0451.commons.color.Color; +import net.lenni0451.rivet.backend.render.Renderer; +import net.lenni0451.rivet.backend.text.ShapedText; +import net.lenni0451.rivet.component.Component; +import net.lenni0451.rivet.input.mouse.MouseButton; +import net.lenni0451.rivet.input.mouse.MouseButtonEvent; +import net.lenni0451.rivet.math.Size; +import net.lenni0451.rivet.text.model.TextOrigin; + +@RequiredArgsConstructor +@Accessors(fluent = true, chain = true, makeFinal = true) +public class TabButton extends Component { + @Setter + private boolean line; + private ShapedText shapedText; + + private final String text; + private final ClickListener clickListener; + + @Override + protected boolean onComponentMouseUp(MouseButtonEvent event, Size size) { + if (event.button() == MouseButton.LEFT) { + clickListener.onClick(this); + return true; + } + + return false; + } + + @Override + public void render(final Renderer renderer, final Size size) { + if (shapedText == null) { + shapedText = this.rivet().backend().font().derive(20).shapeText(this.text, Color.WHITE); + } + + float x = TextOrigin.Horizontal.VISUAL_CENTER.position(size.width()); + float y = TextOrigin.Vertical.LOGICAL_TOP.position(size.height()); + renderer.text(shapedText, x, y, TextOrigin.Horizontal.VISUAL_CENTER, TextOrigin.Vertical.LOGICAL_TOP); + + if (line) { + renderer.fillRect(0, shapedText.logicalBounds().height(), size.width(), 1, Color.WHITE); + } + } + + @Override + public Size computeIdealSize(final Size constraints) { + if (shapedText == null) { + shapedText = this.rivet().backend().font().derive(20).shapeText(this.text, Color.WHITE); + } + + return new Size(shapedText.visualBounds().width() + 10f, shapedText.logicalBounds().height() + 10); + } + + @FunctionalInterface + public interface ClickListener { + void onClick(final TabButton button); + } +} diff --git a/editor/src/main/java/oxy/bascenario/utils/components/TextWithName.java b/editor/src/main/java/oxy/bascenario/utils/components/TextWithName.java new file mode 100644 index 00000000..a414965e --- /dev/null +++ b/editor/src/main/java/oxy/bascenario/utils/components/TextWithName.java @@ -0,0 +1,50 @@ +package oxy.bascenario.utils.components; + +import net.lenni0451.commons.color.Color; +import net.lenni0451.rivet.backend.Texture; +import net.lenni0451.rivet.backend.render.Renderer; +import net.lenni0451.rivet.backend.text.ShapedText; +import net.lenni0451.rivet.backend.thingl.ThinGLTexture; +import net.lenni0451.rivet.component.Component; +import net.lenni0451.rivet.math.Size; +import net.lenni0451.rivet.text.model.TextOrigin; +import oxy.bascenario.Base; +import oxy.bascenario.utils.animation.math.ColorAnimations; + +public class TextWithName extends Component { + private final String name; + private final Texture texture; + private final ColorAnimations backgroundColor = new ColorAnimations(Color.fromRGBA(63, 63, 63, 0)); + + public TextWithName(String name, String path) { + this.name = name; + this.texture = new ThinGLTexture(Base.instance().assetsManager().texture(path)); + } + + @Override + public void render(Renderer renderer, Size bounds) { + renderer.fillRoundedRect(0, 0, bounds.width(), bounds.height(), 5, backgroundColor.color()); + renderer.image(this.texture, 5, 5, 96, 96, Color.WHITE); + + ShapedText text = this.rivet().backend().font().shapeText(name, Color.WHITE); + renderer.scale(0.4f, () -> + renderer.text(text, + (102 / 2f - text.visualBounds().width() * .4f / 2f) / .4f, 108 / .4f, TextOrigin.Horizontal.VISUAL_LEFT, TextOrigin.Vertical.VISUAL_TOP + )); + } + + @Override + protected void onComponentMouseEnter() { + backgroundColor.set(Color.fromRGB(63, 63, 63), 200); + } + + @Override + protected void onComponentMouseLeave() { + backgroundColor.set(Color.fromRGBA(63, 63, 63, 0), 200); + } + + @Override + public Size computeIdealSize(Size constraints) { + return new Size(102,128); + } +} diff --git a/editor/src/main/java/oxy/bascenario/utils/components/TextWithNameNoImage.java b/editor/src/main/java/oxy/bascenario/utils/components/TextWithNameNoImage.java new file mode 100644 index 00000000..0b93c5ba --- /dev/null +++ b/editor/src/main/java/oxy/bascenario/utils/components/TextWithNameNoImage.java @@ -0,0 +1,82 @@ +package oxy.bascenario.utils.components; + +import net.lenni0451.commons.color.Color; +import net.lenni0451.rivet.backend.Texture; +import net.lenni0451.rivet.backend.render.Renderer; +import net.lenni0451.rivet.backend.text.ShapedText; +import net.lenni0451.rivet.backend.thingl.ThinGLTexture; +import net.lenni0451.rivet.component.Component; +import net.lenni0451.rivet.component.Parent; +import net.lenni0451.rivet.math.Rectangle; +import net.lenni0451.rivet.math.Size; +import net.lenni0451.rivet.text.model.TextOrigin; +import oxy.bascenario.Base; +import oxy.bascenario.utils.animation.math.ColorAnimations; + +import java.util.List; + +public class TextWithNameNoImage extends Component { + private final String name; + private final Component component; + private final ColorAnimations backgroundColor = new ColorAnimations(Color.fromRGBA(63, 63, 63, 0)); + + public TextWithNameNoImage(String name, Component component) { + this.name = name; + this.component = component; + } + + @Override + public void render(Renderer renderer, Size bounds) { + if (component.rivet() == null) { + component.setRivet(rivet(), new Parent() { + @Override + public void requestLayoutRecalculation() { + } + + @Override + public Size contentSize() { + return Size.EMPTY; + } + + @Override + public List children() { + return List.of(); + } + + @Override + public Rectangle absoluteBounds() { + return new Rectangle(Size.EMPTY); + } + + @Override + public Rectangle childBounds(Component component) { + return new Rectangle(Size.EMPTY); + } + }); + } + + renderer.fillRoundedRect(0, 0, bounds.width(), bounds.height(), 5, backgroundColor.color()); + component.render(renderer, bounds); + + ShapedText text = this.rivet().backend().font().shapeText(name, Color.WHITE); + renderer.scale(0.4f, () -> + renderer.text(text, + (102 / 2f - text.visualBounds().width() * .4f / 2f) / .4f, 108 / .4f, TextOrigin.Horizontal.VISUAL_LEFT, TextOrigin.Vertical.VISUAL_TOP + )); + } + + @Override + protected void onComponentMouseEnter() { + backgroundColor.set(Color.fromRGB(63, 63, 63), 200); + } + + @Override + protected void onComponentMouseLeave() { + backgroundColor.set(Color.fromRGBA(63, 63, 63, 0), 200); + } + + @Override + public Size computeIdealSize(Size constraints) { + return new Size(102,128); + } +} diff --git a/editor/src/main/java/oxy/bascenario/utils/components/TimelineDockExpandButton.java b/editor/src/main/java/oxy/bascenario/utils/components/TimelineDockExpandButton.java new file mode 100644 index 00000000..821233d6 --- /dev/null +++ b/editor/src/main/java/oxy/bascenario/utils/components/TimelineDockExpandButton.java @@ -0,0 +1,51 @@ +package oxy.bascenario.utils.components; + +import net.lenni0451.commons.color.Color; +import net.lenni0451.rivet.backend.render.Renderer; +import net.lenni0451.rivet.backend.thingl.ThinGLTexture; +import net.lenni0451.rivet.component.Component; +import net.lenni0451.rivet.math.Size; +import net.lenni0451.rivet.text.model.TextOrigin; +import oxy.bascenario.Base; +import oxy.bascenario.editor.EditorValues; +import oxy.bascenario.utils.animation.math.ColorAnimations; + +public class TimelineDockExpandButton extends Component { + private final ColorAnimations backgroundColor = new ColorAnimations(Color.fromRGB(40, 40, 40)); + private final ThinGLTexture downTexture = new ThinGLTexture(Base.instance().assetsManager().texture("assets/base/uis/editor/icons/blender_icon_downarrow_hlt.svg")); + + private final ThinGLTexture sequenceTexture = new ThinGLTexture(Base.instance().assetsManager().texture("assets/base/uis/editor/icons/blender_icon_sequence.svg")); + private final ThinGLTexture actionTexture = new ThinGLTexture(Base.instance().assetsManager().texture("assets/base/uis/editor/icons/blender_icon_action.svg")); + + @Override + public void render(Renderer renderer, Size size) { + renderer.fillRoundedRect(0, 0, size.width(), size.height(), 5, backgroundColor.color()); + renderer.outlineRoundedRect(0, 0, size.width(), size.height(), 5, 1, Color.fromRGB(60, 60, 60)); + + renderer.image(downTexture, 112, 6, downTexture.width() * .7f, downTexture.height() * .7f, Color.WHITE); + + if (EditorValues.instance().type() == EditorValues.TimelineType.Sequencer) { + renderer.image(sequenceTexture, 5, 0, sequenceTexture.width() * .9f, sequenceTexture.height() * .9f, Color.WHITE); + } else { + renderer.image(actionTexture, 5, 3, actionTexture.width() * .9f, actionTexture.height() * .9f, Color.WHITE); + } + + String current = EditorValues.instance().type().name().replace("_", " "); + renderer.text(rivet().backend().font().derive(12).shapeText(current, Color.WHITE), 30, 11, TextOrigin.Horizontal.VISUAL_LEFT, TextOrigin.Vertical.VISUAL_CENTER); + } + + @Override + protected void onComponentMouseEnter() { + backgroundColor.set(Color.fromRGB(48, 48, 48), 500); + } + + @Override + protected void onComponentMouseLeave() { + backgroundColor.set(Color.fromRGB(40, 40, 40), 500); + } + + @Override + public Size computeIdealSize(Size size) { + return new Size(130, 20); + } +} diff --git a/editor/src/main/java/oxy/bascenario/utils/components/TypingLabel.java b/editor/src/main/java/oxy/bascenario/utils/components/TypingLabel.java new file mode 100644 index 00000000..c3d236dd --- /dev/null +++ b/editor/src/main/java/oxy/bascenario/utils/components/TypingLabel.java @@ -0,0 +1,92 @@ +package oxy.bascenario.utils.components; + +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; +import net.lenni0451.commons.math.MathUtils; +import net.lenni0451.rivet.backend.render.Renderer; +import net.lenni0451.rivet.backend.text.ShapedText; +import net.lenni0451.rivet.component.Component; +import net.lenni0451.rivet.math.Size; +import net.lenni0451.rivet.text.model.TextOrigin; +import net.lenni0451.rivet.theme.Theme; + +@Accessors(fluent = true, chain = true, makeFinal = true) +public class TypingLabel extends Component { + + @Getter + private String text; + private ShapedText shapedText; + private boolean reshape; + @Getter + @Setter + private TextOrigin.Horizontal horizontalOrigin = TextOrigin.Horizontal.VISUAL_CENTER; + @Getter + @Setter + private TextOrigin.Vertical verticalOrigin = TextOrigin.Vertical.LOGICAL_CENTER; + @Getter + private float scale = 1F; + + public TypingLabel(final String text) { + this.text = text; + } + + public TypingLabel text(final String text) { + if (!this.text.equals(text)) { + this.text = text; + this.reshape = true; + if (this.parent() != null) { + this.parent().requestLayoutRecalculation(); + } + } + return this; + } + + public TypingLabel scale(final float scale) { + if (this.scale != scale) { + this.scale = scale; + if (this.parent() != null) { + this.parent().requestLayoutRecalculation(); + } + } + return this; + } + + private void shapeText() { + if (this.reshape) { + this.shapedText = this.rivet().backend().font().shapeText(this.text, this.rivet().theme().get(Theme.TEXT_COLOR)); + this.reshape = false; + } + } + + @Override + protected void onComponentAdded() { + this.reshape = true; + } + + @Override + protected void onComponentThemeChanged() { + this.reshape = true; + } + + @Override + public void render(final Renderer renderer, final Size bounds) { + this.shapeText(); + float x = this.horizontalOrigin.position(bounds.width() / this.scale); + float y = this.verticalOrigin.position(bounds.height() / this.scale); + + float progress = (System.currentTimeMillis() % 3000) / 3000f; + net.lenni0451.rivet.backend.text.ShapedText text1 = this.rivet().backend().font().shapeText(this.text.substring(0, MathUtils.ceilInt(progress * this.text.length())), this.rivet().theme().get(Theme.TEXT_COLOR)); + renderer.scale(this.scale, () -> renderer.text(text1, x, y, this.horizontalOrigin, this.verticalOrigin)); + } + + @Override + public Size computeIdealSize(final Size constraints) { + this.shapeText(); + return new Size( + this.shapedText.visualBounds().width() * this.scale, + this.shapedText.logicalBounds().height() * this.scale + ); + } + +} diff --git a/editor/src/main/resources/assets/base/uis/editor/icons/blender_icon_action.svg b/editor/src/main/resources/assets/base/uis/editor/icons/blender_icon_action.svg new file mode 100644 index 00000000..d96a38be --- /dev/null +++ b/editor/src/main/resources/assets/base/uis/editor/icons/blender_icon_action.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/editor/src/main/resources/assets/base/uis/editor/icons/blender_icon_add.svg b/editor/src/main/resources/assets/base/uis/editor/icons/blender_icon_add.svg new file mode 100644 index 00000000..86dab65f --- /dev/null +++ b/editor/src/main/resources/assets/base/uis/editor/icons/blender_icon_add.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/editor/src/main/resources/assets/base/uis/editor/icons/blender_icon_downarrow_hlt.svg b/editor/src/main/resources/assets/base/uis/editor/icons/blender_icon_downarrow_hlt.svg new file mode 100644 index 00000000..c2eed90c --- /dev/null +++ b/editor/src/main/resources/assets/base/uis/editor/icons/blender_icon_downarrow_hlt.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/editor/src/main/resources/assets/base/uis/editor/icons/blender_icon_sequence.svg b/editor/src/main/resources/assets/base/uis/editor/icons/blender_icon_sequence.svg new file mode 100644 index 00000000..8530f0c1 --- /dev/null +++ b/editor/src/main/resources/assets/base/uis/editor/icons/blender_icon_sequence.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/editor/src/main/resources/assets/base/uis/editor/icons/blender_icon_trash.svg b/editor/src/main/resources/assets/base/uis/editor/icons/blender_icon_trash.svg new file mode 100644 index 00000000..1ab63b6f --- /dev/null +++ b/editor/src/main/resources/assets/base/uis/editor/icons/blender_icon_trash.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/editor/src/main/resources/assets/base/uis/editor/objects/circle.png b/editor/src/main/resources/assets/base/uis/editor/objects/circle.png new file mode 100644 index 00000000..2d5bb790 Binary files /dev/null and b/editor/src/main/resources/assets/base/uis/editor/objects/circle.png differ diff --git a/editor/src/main/resources/assets/base/uis/editor/objects/emoticon.png b/editor/src/main/resources/assets/base/uis/editor/objects/emoticon.png new file mode 100644 index 00000000..9e4a7f87 Binary files /dev/null and b/editor/src/main/resources/assets/base/uis/editor/objects/emoticon.png differ diff --git a/editor/src/main/resources/assets/base/uis/editor/objects/location-info.png b/editor/src/main/resources/assets/base/uis/editor/objects/location-info.png new file mode 100644 index 00000000..195c6447 Binary files /dev/null and b/editor/src/main/resources/assets/base/uis/editor/objects/location-info.png differ diff --git a/editor/src/main/resources/assets/base/uis/editor/objects/preview.png b/editor/src/main/resources/assets/base/uis/editor/objects/preview.png new file mode 100644 index 00000000..03d88e1a Binary files /dev/null and b/editor/src/main/resources/assets/base/uis/editor/objects/preview.png differ diff --git a/editor/src/main/resources/assets/base/uis/editor/objects/rectangle.png b/editor/src/main/resources/assets/base/uis/editor/objects/rectangle.png new file mode 100644 index 00000000..83e22daa Binary files /dev/null and b/editor/src/main/resources/assets/base/uis/editor/objects/rectangle.png differ diff --git a/editor/src/test/java/ScenarioEditorTest.java b/editor/src/test/java/ScenarioEditorTest.java index 8fca07da..9a36512a 100644 --- a/editor/src/test/java/ScenarioEditorTest.java +++ b/editor/src/test/java/ScenarioEditorTest.java @@ -1,5 +1,5 @@ import oxy.bascenario.api.Scenario; -import oxy.bascenario.editor.screen.ScenarioEditorScreen; +import oxy.bascenario.editor.ScenarioEditorScreen; import oxy.bascenario.utils.Launcher; public class ScenarioEditorTest { @@ -7,6 +7,6 @@ public static void main(String[] args) { final Scenario.Builder scenario = new Scenario.Builder(); scenario.name("Scenario Editor Test"); - Launcher.launch(new ScenarioEditorScreen(null, scenario.build(), scenario), false); + Launcher.launch(new ScenarioEditorScreen(scenario), false); } } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 8428ed1e..1cf6320d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,20 +1,20 @@ [versions] -lombok = "1.18.38" +lombok = "1.18.46" gson = "2.13.1" -lwjgl = "3.4.0" -lwjgl-sdl = "3.4.0" +lwjgl = "3.4.1" +lwjgl-sdl = "3.4.1" libgdx = "1.13.1" spine = "4.2.5" -thingl = "0.1.1-SNAPSHOT" +thingl = "0.2.0-20260630.205754-5" commons = "1.9.2" mocha = "3.0.1" configurate = "4.2.0" -imgui = "1.92.0" audiomixer = "2.3.0-SNAPSHOT" jorbis = "0.0.17" jlayer = "1.0.1" -#netty = "4.2.9.Final" zt-zip = "1.17" +rivet = "b40512f366" +jsvg = "2.1.0" [libraries] zt-zip = { module = "org.zeroturnaround:zt-zip", version.ref = "zt-zip" } @@ -23,12 +23,6 @@ projectlombok-lombok = { module = "org.projectlombok:lombok", version.ref = "lom google-gson = { module = "com.google.code.gson:gson", version.ref = "gson" } configurate-yaml = { module = "org.spongepowered:configurate-yaml", version.ref = "configurate" } -imgui-binding = { module = "io.github.spair:imgui-java-binding", version.ref = "imgui" } -imgui-lwjgl3 = { module = "io.github.spair:imgui-java-lwjgl3", version.ref = "imgui" } -imgui-natives-windows = { module = "io.github.spair:imgui-java-natives-windows", version.ref = "imgui" } -imgui-natives-linux = { module = "io.github.spair:imgui-java-natives-linux", version.ref = "imgui" } -imgui-natives-macos = { module = "io.github.spair:imgui-java-natives-macos", version.ref = "imgui" } - lwjgl-base = { module = "org.lwjgl:lwjgl", version.ref = "lwjgl" } lwjgl-opengl = { module = "org.lwjgl:lwjgl-opengl", version.ref = "lwjgl" } lwjgl-stb = { module = "org.lwjgl:lwjgl-stb", version.ref = "lwjgl" } @@ -49,6 +43,9 @@ gdx-spine = { module = "com.esotericsoftware.spine:spine-libgdx", version.ref = raphimc-thingl = { module = "net.raphimc:thingl", version.ref = "thingl" } raphimc-audiomixer = { module = "net.raphimc:audio-mixer", version.ref = "audiomixer" } +rivet-core = { module = "com.github.Lenni0451.rivet:core", version.ref = "rivet"} +rivet-thingl-backend = { module = "com.github.Lenni0451.rivet:backend-thingl-glfw", version.ref = "rivet" } + commons-animations = { module = "net.lenni0451.commons:animation", version.ref = "commons" } commons-core = { module = "net.lenni0451.commons:core", version.ref = "commons" } @@ -57,5 +54,7 @@ unnamed-mocha = { module = "team.unnamed:mocha", version.ref = "mocha" } jcraft-jorbis = { module = "org.jcraft:jorbis", version.ref = "jorbis" } javazoom-jlayer = { module = "javazoom:jlayer", version.ref = "jlayer" } +weisj-jsvg = { module = "com.github.weisj:jsvg", version.ref = "jsvg" } + #netty-buffer = { module = "io.netty:netty-buffer", version.ref = "netty" } #jthink-jaudiotagger = { module = "net.jthink:jaudiotagger", version.ref = "jaudiotagger" } diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 249e5832..b1b8ef56 100644 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 17dea80b..df6a6ad7 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,9 @@ -#Sun Nov 16 11:40:51 ICT 2025 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.5.1-bin.zip +networkTimeout=10000 +retries=0 +retryBackOffMs=500 +validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index 1b6c7873..b9bb139f 100644 --- a/gradlew +++ b/gradlew @@ -1,7 +1,7 @@ #!/bin/sh # -# Copyright © 2015-2021 the original authors. +# Copyright © 2015 the original authors. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ # See the License for the specific language governing permissions and # limitations under the License. # +# SPDX-License-Identifier: Apache-2.0 +# ############################################################################## # @@ -55,7 +57,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/3d91ce3b8caaf77ad09f381f43615b715b53f72c/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. @@ -80,13 +82,11 @@ do esac done -APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit - -APP_NAME="Gradle" +# This is normally unused +# shellcheck disable=SC2034 APP_BASE_NAME=${0##*/} - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD=maximum @@ -114,7 +114,6 @@ case "$( uname )" in #( NONSTOP* ) nonstop=true ;; esac -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar # Determine the Java command to use to start the JVM. @@ -133,22 +132,29 @@ location of your Java installation." fi else JAVACMD=java - which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. Please set the JAVA_HOME variable in your environment to match the location of your Java installation." + fi fi # Increase the maximum file descriptors if we can. if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then case $MAX_FD in #( max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 MAX_FD=$( ulimit -H -n ) || warn "Could not query maximum file descriptor limit" esac case $MAX_FD in #( '' | soft) :;; #( *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 ulimit -n "$MAX_FD" || warn "Could not set maximum file descriptor limit to $MAX_FD" esac @@ -165,7 +171,6 @@ fi # For Cygwin or MSYS, switch paths to Windows format before running java if "$cygwin" || "$msys" ; then APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) JAVACMD=$( cygpath --unix "$JAVACMD" ) @@ -193,18 +198,27 @@ if "$cygwin" || "$msys" ; then done fi -# Collect all arguments for the java command; -# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of -# shell script including quotes and variable substitutions, so put them in -# double quotes to make sure that they get re-expanded; and -# * put everything else in single quotes, so that it's not re-expanded. + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. set -- \ "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ + -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \ "$@" +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + # Use "xargs" to parse quoted args. # # With -n1 it outputs one arg per line, with the quotes and backslashes removed. diff --git a/gradlew.bat b/gradlew.bat index 107acd32..24c62d56 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -13,19 +13,22 @@ @rem See the License for the specific language governing permissions and @rem limitations under the License. @rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem -@if "%DEBUG%" == "" @echo off +@if "%DEBUG%"=="" @echo off @rem ########################################################################## @rem @rem Gradle startup script for Windows @rem @rem ########################################################################## -@rem Set local scope for the variables with windows NT shell -if "%OS%"=="Windows_NT" setlocal +@rem Set local scope for the variables, and ensure extensions are enabled +setlocal EnableExtensions set DIRNAME=%~dp0 -if "%DIRNAME%" == "" set DIRNAME=. +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @@ -40,15 +43,15 @@ if defined JAVA_HOME goto findJavaFromJavaHome set JAVA_EXE=java.exe %JAVA_EXE% -version >NUL 2>&1 -if "%ERRORLEVEL%" == "0" goto execute +if %ERRORLEVEL% equ 0 goto execute -echo. -echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 -goto fail +"%COMSPEC%" /c exit 1 :findJavaFromJavaHome set JAVA_HOME=%JAVA_HOME:"=% @@ -56,34 +59,24 @@ set JAVA_EXE=%JAVA_HOME%/bin/java.exe if exist "%JAVA_EXE%" goto execute -echo. -echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% -echo. -echo Please set the JAVA_HOME variable in your environment to match the -echo location of your Java installation. +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 -goto fail +"%COMSPEC%" /c exit 1 :execute @rem Setup the command line -set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar @rem Execute Gradle -"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* - -:end -@rem End local scope for the variables with windows NT shell -if "%ERRORLEVEL%"=="0" goto mainEnd - -:fail -rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of -rem the _cmd.exe /c_ return code! -if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 -exit /b 1 - -:mainEnd -if "%OS%"=="Windows_NT" endlocal +@rem endlocal doesn't take effect until after the line is parsed and variables are expanded +@rem which allows us to clear the local environment before executing the java command +endlocal & "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %* & call :exitWithErrorLevel -:omega +:exitWithErrorLevel +@rem Use "%COMSPEC%" /c exit to allow operators to work properly in scripts +"%COMSPEC%" /c exit %ERRORLEVEL% diff --git a/jitpack.yml b/jitpack.yml index a2027925..aaa58906 100644 --- a/jitpack.yml +++ b/jitpack.yml @@ -1,2 +1,2 @@ jdk: - - openjdk21 \ No newline at end of file + - openjdk25 diff --git a/launcher/build.gradle.kts b/launcher/build.gradle.kts index 8b8c1ac7..51e34751 100644 --- a/launcher/build.gradle.kts +++ b/launcher/build.gradle.kts @@ -1,6 +1,10 @@ plugins { id("java") - id("com.gradleup.shadow") version("8.3.0") + id("com.gradleup.shadow") version("9.4.1") +} + +repositories { + maven("https://jitpack.io") } dependencies { @@ -8,7 +12,7 @@ dependencies { implementation(project(":editor")) implementation(project(":serializer")) implementation(project(":core")) - + implementation(libs.zt.zip) } diff --git a/launcher/src/main/java/oxy/bascenario/screen/ScenarioListScreen.java b/launcher/src/main/java/oxy/bascenario/screen/ScenarioListScreen.java index 63f157f7..d54c4555 100644 --- a/launcher/src/main/java/oxy/bascenario/screen/ScenarioListScreen.java +++ b/launcher/src/main/java/oxy/bascenario/screen/ScenarioListScreen.java @@ -2,29 +2,22 @@ import com.google.gson.JsonObject; import com.google.gson.JsonParser; -import imgui.ImColor; -import imgui.ImGui; -import imgui.flag.*; import org.zeroturnaround.zip.ZipUtil; import org.zeroturnaround.zip.commons.FileUtils; import oxy.bascenario.Base; import oxy.bascenario.api.Scenario; -import oxy.bascenario.editor.screen.ScenarioEditorScreen; import oxy.bascenario.managers.ScenarioManager; import oxy.bascenario.screen.title.TitleScreen; import oxy.bascenario.screens.ScenarioScreen; import oxy.bascenario.serializers.Types; import oxy.bascenario.utils.ExtendableScreen; -import oxy.bascenario.utils.ImGuiUtils; import oxy.bascenario.utils.Launcher; import oxy.bascenario.utils.files.NFDUtils; import java.awt.*; import java.io.File; import java.io.IOException; -import java.nio.file.Files; import java.util.Iterator; -import java.util.UUID; public class ScenarioListScreen extends ExtendableScreen { private boolean popupNewScenario, popupImportingFailed; @@ -33,160 +26,160 @@ public class ScenarioListScreen extends ExtendableScreen { @Override public void render(float delta) { - ImGui.getStyle().setColor(ImGuiCol.WindowBg, 0.098f, 0.098f, 0.098f, 1f); - - ImGui.beginMainMenuBar(); - if (ImGui.menuItem("Exit")) { - Launcher.WINDOW.setScreen(TitleScreen.INSTANCE); - } - if (ImGui.menuItem("New Scenario")) { - popupNewScenario = true; - } - if (ImGui.menuItem("Import Scenario")) { - String fileS = NFDUtils.pickFile("zip,rar"); - if (!fileS.isEmpty()) { - final File file = new File(fileS); - if (!ZipUtil.containsEntry(file, "scenario.base")) { - importingFailed = "Import scenario failed, is this the correct format?"; - popupImportingFailed = true; - } else { - Scenario scenario = null; - final byte[] bytes = ZipUtil.unpackEntry(file, "scenario.base"); - try { - JsonObject object = JsonParser.parseString(new String(bytes)).getAsJsonObject(); - scenario = Types.SCENARIO_TYPE.read(object); - } catch (Exception ignored) { - } - if (scenario == null) { - importingFailed = "Import scenario failed, failed to read scenario!"; - popupImportingFailed = true; - } else { - File path = new File(ScenarioManager.SAVE_DIR, scenario.getName()); - if (path.exists()) { - importingFailed = "File/Scenario Folder with this name already exist, please delete/rename it first"; - popupImportingFailed = true; - } else { - path.mkdirs(); - - ZipUtil.unpack(file, path); - Base.instance().scenarioManager().put(scenario.getName(), scenario); - } - } - } - } - } - ImGui.endMainMenuBar(); - - ImGui.setNextWindowPos(0, 23 * ImGui.getStyle().getFontScaleDpi()); - ImGui.setNextWindowSize(ImGui.getIO().getDisplaySize()); - ImGui.begin("Main", ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.NoResize); - - if (ImGui.button("Open Saved Location##" + ImGuiUtils.COUNTER++)) { - try { - Desktop.getDesktop().open(ScenarioManager.SAVE_DIR); - } catch (IOException ignored) { - } - } - - if (ImGui.beginTable("1ways", 1, ImGuiTableFlags.BordersV | ImGuiTableFlags.BordersOuterH |ImGuiTableFlags.RowBg | ImGuiTableFlags.NoBordersInBody)) { - ImGui.tableSetupColumn("Name", ImGuiTableColumnFlags.NoHide); - ImGui.tableHeadersRow(); - - final Iterator iterator = Base.instance().scenarioManager().getScenarios().values().iterator(); - while (iterator.hasNext()) { - Scenario scenario = iterator.next(); - - ImGui.tableNextRow(); - ImGui.tableNextColumn(); - ImGui.text(scenario.getName()); - ImGui.sameLine(); - if (ImGui.smallButton("Edit##" + ImGuiUtils.COUNTER++)) { - Launcher.WINDOW.setScreen(new ScenarioEditorScreen(this, scenario, scenario.toBuilder())); - } - ImGui.sameLine(); - if (ImGui.smallButton("Play##" + ImGuiUtils.COUNTER++)) { - Launcher.WINDOW.setScreen(new ScenarioScreen(scenario) { - @Override - public void render(float delta) { - if (ImGui.isKeyReleased(ImGuiKey.Escape)) { - Launcher.WINDOW.setScreen(TitleScreen.INSTANCE); - } - super.render(delta); - } - }); - } - ImGui.sameLine(); - - // TODO: do me! -// if (ImGui.smallButton("Rename##" + ImGuiUtils.COUNTER++)) { +// ImGui.getStyle().setColor(ImGuiCol.WindowBg, 0.098f, 0.098f, 0.098f, 1f); +// +// ImGui.beginMainMenuBar(); +// if (ImGui.menuItem("Exit")) { +// Launcher.WINDOW.setScreen(TitleScreen.INSTANCE); +// } +// if (ImGui.menuItem("New Scenario")) { +// popupNewScenario = true; +// } +// if (ImGui.menuItem("Import Scenario")) { +// String fileS = NFDUtils.pickFile("zip,rar"); +// if (!fileS.isEmpty()) { +// final File file = new File(fileS); +// if (!ZipUtil.containsEntry(file, "scenario.base")) { +// importingFailed = "Import scenario failed, is this the correct format?"; +// popupImportingFailed = true; +// } else { +// Scenario scenario = null; +// final byte[] bytes = ZipUtil.unpackEntry(file, "scenario.base"); +// try { +// JsonObject object = JsonParser.parseString(new String(bytes)).getAsJsonObject(); +// scenario = Types.SCENARIO_TYPE.read(object); +// } catch (Exception ignored) { +// } +// if (scenario == null) { +// importingFailed = "Import scenario failed, failed to read scenario!"; +// popupImportingFailed = true; +// } else { +// File path = new File(ScenarioManager.SAVE_DIR, scenario.getName()); +// if (path.exists()) { +// importingFailed = "File/Scenario Folder with this name already exist, please delete/rename it first"; +// popupImportingFailed = true; +// } else { +// path.mkdirs(); +// +// ZipUtil.unpack(file, path); +// Base.instance().scenarioManager().put(scenario.getName(), scenario); +// } +// } // } - - if (ImGui.smallButton("Export##" + ImGuiUtils.COUNTER++)) { - String folderS = NFDUtils.pickFolder(); - if (!folderS.isEmpty()) { - final File folder = new File(folderS); - final File path = new File(folder, scenario.getName() + ".zip"); - ZipUtil.pack(new File(ScenarioManager.SAVE_DIR, scenario.getName()), path); - try { - Desktop.getDesktop().open(folder); - } catch (IOException ignored) { - } - } - } - - ImGui.sameLine(); - if (ImGui.smallButton("Delete##" + ImGuiUtils.COUNTER++)) { - final File path = new File(ScenarioManager.SAVE_DIR, scenario.getName()); - try { - FileUtils.deleteDirectory(path); - iterator.remove(); - } catch (IOException ignored) { - } - } - } - - ImGui.endTable(); - } - - ImGui.end(); - - if (popupNewScenario) { - popupNewScenario = false; - cacheName = ""; - ImGui.openPopup("New Scenario"); - } - - if (popupImportingFailed) { - popupImportingFailed = false; - ImGui.openPopup("Import Failed"); - } - - if (ImGui.beginPopupModal("Import Failed", ImGuiWindowFlags.AlwaysAutoResize)) { - ImGui.text(importingFailed); - if (ImGui.button("Ok")) { - ImGui.closeCurrentPopup(); - importingFailed = ""; - } - ImGui.endPopup(); - } - - if (ImGui.beginPopupModal("New Scenario")) { - cacheName = ImGuiUtils.inputText("Name", cacheName); - final File path = new File(ScenarioManager.SAVE_DIR, cacheName); - - if (path.isDirectory() || cacheName.isEmpty()) { - ImGui.text(cacheName.isEmpty() ? "Name can't be empty!" : "A directory with this name already exist!"); - } - if (ImGui.button("Create##" + ImGuiUtils.COUNTER++) && !path.isDirectory() && !cacheName.isEmpty()) { - Base.instance().scenarioManager().put(cacheName, Scenario.builder().name(cacheName).build()); - ImGui.closeCurrentPopup(); - } - ImGui.sameLine(); - if (ImGui.button("Ok nevermind..")) { - ImGui.closeCurrentPopup(); - } - - ImGui.endPopup(); - } +// } +// } +// ImGui.endMainMenuBar(); +// +// ImGui.setNextWindowPos(0, 23 * ImGui.getStyle().getFontScaleDpi()); +// ImGui.setNextWindowSize(ImGui.getIO().getDisplaySize()); +// ImGui.begin("Main", ImGuiWindowFlags.NoDecoration | ImGuiWindowFlags.NoResize); +// +// if (ImGui.button("Open Saved Location##" + ImGuiUtils.COUNTER++)) { +// try { +// Desktop.getDesktop().open(ScenarioManager.SAVE_DIR); +// } catch (IOException ignored) { +// } +// } +// +// if (ImGui.beginTable("1ways", 1, ImGuiTableFlags.BordersV | ImGuiTableFlags.BordersOuterH |ImGuiTableFlags.RowBg | ImGuiTableFlags.NoBordersInBody)) { +// ImGui.tableSetupColumn("Name", ImGuiTableColumnFlags.NoHide); +// ImGui.tableHeadersRow(); +// +// final Iterator iterator = Base.instance().scenarioManager().getScenarios().values().iterator(); +// while (iterator.hasNext()) { +// Scenario scenario = iterator.next(); +// +// ImGui.tableNextRow(); +// ImGui.tableNextColumn(); +// ImGui.text(scenario.getName()); +// ImGui.sameLine(); +// if (ImGui.smallButton("Edit##" + ImGuiUtils.COUNTER++)) { +// Launcher.WINDOW.setScreen(new ScenarioEditorScreen(this, scenario, scenario.toBuilder())); +// } +// ImGui.sameLine(); +// if (ImGui.smallButton("Play##" + ImGuiUtils.COUNTER++)) { +// Launcher.WINDOW.setScreen(new ScenarioScreen(scenario) { +// @Override +// public void render(float delta) { +// if (ImGui.isKeyReleased(ImGuiKey.Escape)) { +// Launcher.WINDOW.setScreen(TitleScreen.INSTANCE); +// } +// super.render(delta); +// } +// }); +// } +// ImGui.sameLine(); +// +// // TODO: do me! +//// if (ImGui.smallButton("Rename##" + ImGuiUtils.COUNTER++)) { +//// } +// +// if (ImGui.smallButton("Export##" + ImGuiUtils.COUNTER++)) { +// String folderS = NFDUtils.pickFolder(); +// if (!folderS.isEmpty()) { +// final File folder = new File(folderS); +// final File path = new File(folder, scenario.getName() + ".zip"); +// ZipUtil.pack(new File(ScenarioManager.SAVE_DIR, scenario.getName()), path); +// try { +// Desktop.getDesktop().open(folder); +// } catch (IOException ignored) { +// } +// } +// } +// +// ImGui.sameLine(); +// if (ImGui.smallButton("Delete##" + ImGuiUtils.COUNTER++)) { +// final File path = new File(ScenarioManager.SAVE_DIR, scenario.getName()); +// try { +// FileUtils.deleteDirectory(path); +// iterator.remove(); +// } catch (IOException ignored) { +// } +// } +// } +// +// ImGui.endTable(); +// } +// +// ImGui.end(); +// +// if (popupNewScenario) { +// popupNewScenario = false; +// cacheName = ""; +// ImGui.openPopup("New Scenario"); +// } +// +// if (popupImportingFailed) { +// popupImportingFailed = false; +// ImGui.openPopup("Import Failed"); +// } +// +// if (ImGui.beginPopupModal("Import Failed", ImGuiWindowFlags.AlwaysAutoResize)) { +// ImGui.text(importingFailed); +// if (ImGui.button("Ok")) { +// ImGui.closeCurrentPopup(); +// importingFailed = ""; +// } +// ImGui.endPopup(); +// } +// +// if (ImGui.beginPopupModal("New Scenario")) { +// cacheName = ImGuiUtils.inputText("Name", cacheName); +// final File path = new File(ScenarioManager.SAVE_DIR, cacheName); +// +// if (path.isDirectory() || cacheName.isEmpty()) { +// ImGui.text(cacheName.isEmpty() ? "Name can't be empty!" : "A directory with this name already exist!"); +// } +// if (ImGui.button("Create##" + ImGuiUtils.COUNTER++) && !path.isDirectory() && !cacheName.isEmpty()) { +// Base.instance().scenarioManager().put(cacheName, Scenario.builder().name(cacheName).build()); +// ImGui.closeCurrentPopup(); +// } +// ImGui.sameLine(); +// if (ImGui.button("Ok nevermind..")) { +// ImGui.closeCurrentPopup(); +// } +// +// ImGui.endPopup(); +// } } } diff --git a/serializer/build.gradle.kts b/serializer/build.gradle.kts index 41551c82..4fe6cbef 100644 --- a/serializer/build.gradle.kts +++ b/serializer/build.gradle.kts @@ -10,6 +10,6 @@ dependencies { api(project(":api")) api(libs.commons.core) - testImplementation(project(":core")) + testImplementation(project(":api")) testImplementation(libs.gdx.base) } \ No newline at end of file diff --git a/serializer/src/test/java/CompilerResultCompareTest.java b/serializer/src/test/java/CompilerResultCompareTest.java index 1d842cbc..83a058be 100644 --- a/serializer/src/test/java/CompilerResultCompareTest.java +++ b/serializer/src/test/java/CompilerResultCompareTest.java @@ -1,11 +1,8 @@ import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import io.netty.buffer.Unpooled; import net.lenni0451.commons.color.Color; import oxy.bascenario.api.Scenario; import oxy.bascenario.api.effects.Easing; -import oxy.bascenario.api.effects.Effect; import oxy.bascenario.api.event.animation.PlayAnimationEvent; import oxy.bascenario.api.event.animation.SpriteAnimationEvent; import oxy.bascenario.api.event.animation.StopAnimationEvent; @@ -15,7 +12,6 @@ import oxy.bascenario.api.event.dialogue.ShowOptionsEvent; import oxy.bascenario.api.event.dialogue.StartDialogueEvent; import oxy.bascenario.api.event.element.AddElementEvent; -import oxy.bascenario.api.event.element.ElementEffectEvent; import oxy.bascenario.api.event.element.values.PositionElementEvent; import oxy.bascenario.api.event.element.values.RotateElementEvent; import oxy.bascenario.api.event.sound.SoundVolumeEvent;