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;