├── assets ├── textfile.txt ├── close.png ├── gfx │ ├── redy.png │ └── white.png └── tilesets │ ├── dev_tileset.png │ ├── tileset_red.png │ ├── tileset_blue.png │ ├── tileset_test.png │ └── tileset_blue_small.png ├── docs └── index.html ├── settings.gradle ├── ui ├── close.png ├── folder.png ├── plus-box.png ├── arrow_left.png └── cursor-default.png ├── .gitignore ├── raw └── dev_tileset.xcf ├── .travis.yml ├── editor ├── assets │ ├── tinted.png │ ├── icons-large_0.png │ ├── icons-small_0.png │ ├── graphics │ │ ├── editor.png │ │ └── editor.atlas │ └── tinted.atlas ├── src │ ├── main │ │ └── java │ │ │ └── me │ │ │ └── manabreak │ │ │ └── ratio │ │ │ ├── utils │ │ │ ├── Action1.java │ │ │ ├── TextUtils.java │ │ │ └── LineUtils.java │ │ │ ├── editor │ │ │ ├── LoopListener.java │ │ │ ├── PluginHost.java │ │ │ ├── EditorPanel.java │ │ │ ├── EditorTab.java │ │ │ ├── TilesetTab.java │ │ │ ├── menu │ │ │ │ ├── ConfirmMenuItem.java │ │ │ │ └── EditorMenuBar.java │ │ │ ├── EditorView.java │ │ │ ├── PluginManager.java │ │ │ ├── TabbedContainer.java │ │ │ ├── EditorPlugin.java │ │ │ ├── EditorGameScreen.java │ │ │ ├── TilesetTabContainer.java │ │ │ ├── ShortcutProcessor.java │ │ │ ├── EditorController.java │ │ │ └── EditorStage.java │ │ │ ├── plugins │ │ │ ├── camera │ │ │ │ ├── CameraSnapMode.java │ │ │ │ └── EditorCamera.java │ │ │ ├── level │ │ │ │ ├── Command.java │ │ │ │ ├── Face.java │ │ │ │ ├── ToolPresenter.java │ │ │ │ ├── Coord.java │ │ │ │ ├── DrawFloorCommand.java │ │ │ │ ├── DrawBlockCommand.java │ │ │ │ ├── EraseBlockCommand.java │ │ │ │ ├── PaintCommand.java │ │ │ │ ├── Cell.java │ │ │ │ ├── WireframeRenderer.java │ │ │ │ ├── LevelShader.java │ │ │ │ ├── Level.java │ │ │ │ ├── ToolView.java │ │ │ │ ├── LayerListAdapter.java │ │ │ │ ├── LayerUi.java │ │ │ │ ├── LayerPresenter.java │ │ │ │ ├── Tile.java │ │ │ │ ├── TileLayer.java │ │ │ │ ├── ObjectRenderer.java │ │ │ │ ├── Octree.java │ │ │ │ └── ToolRenderer.java │ │ │ ├── objects │ │ │ │ ├── ObjectRenderMode.java │ │ │ │ ├── ObjectShader.java │ │ │ │ ├── ObjectListAdapter.java │ │ │ │ ├── ObjectEditorPlugin.java │ │ │ │ ├── GameObject.java │ │ │ │ ├── ObjectEditorPresenter.java │ │ │ │ └── ObjectEditorUi.java │ │ │ ├── importers │ │ │ │ ├── MissingImporterException.java │ │ │ │ ├── ImporterException.java │ │ │ │ ├── Importer.java │ │ │ │ └── ImportManager.java │ │ │ ├── properties │ │ │ │ ├── PropertyHandler.java │ │ │ │ ├── LevelPropertiesPlugin.java │ │ │ │ ├── LevelPropertiesUi.java │ │ │ │ ├── LevelPropertiesPresenter.java │ │ │ │ ├── PropertyPrompt.java │ │ │ │ └── PropertyAdapter.java │ │ │ ├── tilesets │ │ │ │ ├── ImageTileset.java │ │ │ │ ├── TilesetPlugin.java │ │ │ │ ├── Tileset.java │ │ │ │ ├── TilesetManager.java │ │ │ │ ├── PaletteTileset.java │ │ │ │ ├── TilesetPresenter.java │ │ │ │ └── TilesetLoaderDialog.java │ │ │ ├── toolbar │ │ │ │ ├── Tool.java │ │ │ │ ├── ToolbarPlugin.java │ │ │ │ ├── ToolbarPresenter.java │ │ │ │ └── ToolbarUi.java │ │ │ ├── exporters │ │ │ │ ├── ExportManager.java │ │ │ │ └── Exporter.java │ │ │ └── scene │ │ │ │ └── EditorGrid.java │ │ │ ├── common │ │ │ ├── mvp │ │ │ │ ├── MvpPresenter.java │ │ │ │ └── MvpView.java │ │ │ └── Properties.java │ │ │ ├── ui │ │ │ ├── DefTable.java │ │ │ ├── NumericTextFieldFilter.java │ │ │ ├── IntegerTextFieldFilter.java │ │ │ ├── UiUtils.java │ │ │ └── Res.java │ │ │ ├── EditorLauncher.java │ │ │ └── EditorGame.java │ └── test │ │ └── java │ │ └── me │ │ └── manabreak │ │ └── ratio │ │ ├── ui │ │ └── DefTable.java │ │ ├── utils │ │ └── LineUtilsTest.java │ │ ├── plugins │ │ ├── tilesets │ │ │ ├── TilesetTest.java │ │ │ └── TilesetPresenterTest.java │ │ ├── level │ │ │ ├── OctreeTest.java │ │ │ └── LevelTest.java │ │ ├── objects │ │ │ └── ObjectEditorTest.java │ │ └── exporters │ │ │ └── GdxJsonExporterTest.java │ │ └── test │ │ └── GdxTest.java └── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── README.md ├── gradlew.bat └── gradlew /assets/textfile.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | Hello, Ratio! 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include 'editor' 2 | 3 | -------------------------------------------------------------------------------- /ui/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manabreak/ratio/HEAD/ui/close.png -------------------------------------------------------------------------------- /ui/folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manabreak/ratio/HEAD/ui/folder.png -------------------------------------------------------------------------------- /assets/close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manabreak/ratio/HEAD/assets/close.png -------------------------------------------------------------------------------- /ui/plus-box.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manabreak/ratio/HEAD/ui/plus-box.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | *.iml 3 | .gradle/ 4 | out/ 5 | local.properties 6 | build/ 7 | -------------------------------------------------------------------------------- /ui/arrow_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manabreak/ratio/HEAD/ui/arrow_left.png -------------------------------------------------------------------------------- /assets/gfx/redy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manabreak/ratio/HEAD/assets/gfx/redy.png -------------------------------------------------------------------------------- /assets/gfx/white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manabreak/ratio/HEAD/assets/gfx/white.png -------------------------------------------------------------------------------- /raw/dev_tileset.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manabreak/ratio/HEAD/raw/dev_tileset.xcf -------------------------------------------------------------------------------- /ui/cursor-default.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manabreak/ratio/HEAD/ui/cursor-default.png -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | after_success: 3 | - bash <(curl -s https://codecov.io/bash) 4 | -------------------------------------------------------------------------------- /editor/assets/tinted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manabreak/ratio/HEAD/editor/assets/tinted.png -------------------------------------------------------------------------------- /assets/tilesets/dev_tileset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manabreak/ratio/HEAD/assets/tilesets/dev_tileset.png -------------------------------------------------------------------------------- /assets/tilesets/tileset_red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manabreak/ratio/HEAD/assets/tilesets/tileset_red.png -------------------------------------------------------------------------------- /editor/assets/icons-large_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manabreak/ratio/HEAD/editor/assets/icons-large_0.png -------------------------------------------------------------------------------- /editor/assets/icons-small_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manabreak/ratio/HEAD/editor/assets/icons-small_0.png -------------------------------------------------------------------------------- /assets/tilesets/tileset_blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manabreak/ratio/HEAD/assets/tilesets/tileset_blue.png -------------------------------------------------------------------------------- /assets/tilesets/tileset_test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manabreak/ratio/HEAD/assets/tilesets/tileset_test.png -------------------------------------------------------------------------------- /editor/assets/graphics/editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manabreak/ratio/HEAD/editor/assets/graphics/editor.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manabreak/ratio/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /assets/tilesets/tileset_blue_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/manabreak/ratio/HEAD/assets/tilesets/tileset_blue_small.png -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/utils/Action1.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.utils; 2 | 3 | public interface Action1 { 4 | void call(T item); 5 | } 6 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/editor/LoopListener.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.editor; 2 | 3 | public interface LoopListener { 4 | void onUpdate(float dt); 5 | } 6 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/camera/CameraSnapMode.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.camera; 2 | 3 | public enum CameraSnapMode { 4 | NONE, 5 | TOPDOWN 6 | } 7 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/level/Command.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.level; 2 | 3 | public interface Command { 4 | void execute(); 5 | 6 | void undo(); 7 | } 8 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/objects/ObjectRenderMode.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.objects; 2 | 3 | public enum ObjectRenderMode { 4 | None, 5 | Line, 6 | Filled 7 | } 8 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/utils/TextUtils.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.utils; 2 | 3 | public class TextUtils { 4 | public static boolean isNullOrEmpty(String type) { 5 | return type == null || type.length() == 0; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /editor/src/test/java/me/manabreak/ratio/ui/DefTable.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.ui; 2 | 3 | import com.kotcrab.vis.ui.widget.VisTable; 4 | 5 | public class DefTable extends VisTable { 6 | 7 | public DefTable() { 8 | super(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/editor/PluginHost.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.editor; 2 | 3 | import java.util.Collection; 4 | 5 | public interface PluginHost { 6 | void addPlugin(EditorPlugin plugin); 7 | 8 | Collection getPlugins(); 9 | } 10 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Apr 14 13:27:09 BST 2018 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4.1-all.zip 7 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/common/mvp/MvpPresenter.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.common.mvp; 2 | 3 | public abstract class MvpPresenter { 4 | 5 | protected V view; 6 | 7 | public void attachView(V view) { 8 | this.view = view; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/ui/DefTable.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.ui; 2 | 3 | import com.kotcrab.vis.ui.widget.VisTable; 4 | 5 | public class DefTable extends VisTable { 6 | 7 | public DefTable() { 8 | super(); 9 | setBackground("default-table"); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/importers/MissingImporterException.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.importers; 2 | 3 | import com.badlogic.gdx.files.FileHandle; 4 | 5 | public class MissingImporterException extends Exception { 6 | public MissingImporterException(FileHandle fh) { 7 | super("Missing importer for file format " + fh.extension()); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/properties/PropertyHandler.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.properties; 2 | 3 | public interface PropertyHandler { 4 | void createBooleanProperty(String key); 5 | 6 | void createStringProperty(String key); 7 | 8 | void createIntProperty(String key); 9 | 10 | void createDoubleProperty(String key); 11 | 12 | void createVec3Property(String key); 13 | } 14 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/editor/EditorPanel.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.editor; 2 | 3 | /** 4 | * Denotes the panel in the editor 5 | */ 6 | public enum EditorPanel { 7 | LEFT, // Left portion of the main content 8 | RIGHT, // Right portion of the main content 9 | TOP, // Toolbar above the main content 10 | NONE, // Not a panel, but a floating window 11 | BOTTOM // Bottom bar below the main content 12 | } 13 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/ui/NumericTextFieldFilter.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.ui; 2 | 3 | import com.kotcrab.vis.ui.widget.VisTextField; 4 | 5 | public class NumericTextFieldFilter implements VisTextField.TextFieldFilter { 6 | @Override 7 | public boolean acceptChar(VisTextField textField, char c) { 8 | return Character.isDigit(c) || c == '.' || (textField.getText().length() == 0 && c == '-'); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/tilesets/ImageTileset.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.tilesets; 2 | 3 | import com.badlogic.gdx.graphics.Texture; 4 | 5 | public class ImageTileset extends Tileset { 6 | private final String path; 7 | 8 | public ImageTileset(String name, String path, Texture texture) { 9 | super(name, texture); 10 | this.path = path; 11 | } 12 | 13 | public String getPath() { 14 | return path; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/common/mvp/MvpView.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.common.mvp; 2 | 3 | import me.manabreak.ratio.ui.DefTable; 4 | 5 | public abstract class MvpView

extends DefTable { 6 | 7 | protected final P presenter; 8 | 9 | protected MvpView(P presenter) { 10 | super(); 11 | this.presenter = presenter; 12 | presenter.attachView(this); 13 | } 14 | 15 | public P getPresenter() { 16 | return presenter; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/level/Face.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.level; 2 | 3 | import com.badlogic.gdx.math.Vector3; 4 | 5 | public enum Face { 6 | FRONT(0f, 0f, 1f), 7 | BACK(0f, 0f, -1f), 8 | LEFT(1f, 0f, 0f), 9 | RIGHT(-1f, 0f, 0f), 10 | TOP(0f, 1f, 0f), 11 | BOTTOM(0f, -1f, 0f); 12 | 13 | private final Vector3 normal; 14 | 15 | Face(float nx, float ny, float nz) { 16 | normal = new Vector3(nx, ny, nz); 17 | } 18 | 19 | public Vector3 getNormal() { 20 | return normal; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/importers/ImporterException.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.importers; 2 | 3 | import com.badlogic.gdx.files.FileHandle; 4 | 5 | public class ImporterException extends Exception { 6 | public static ImporterException create(Importer importer, FileHandle fh, String reason) { 7 | return new ImporterException("Failed to load level file " + fh.toString() + " using importer " + importer.getClass().getSimpleName() + ", reason: " + reason); 8 | } 9 | 10 | private ImporterException(String message) { 11 | super(message); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/properties/LevelPropertiesPlugin.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.properties; 2 | 3 | import me.manabreak.ratio.editor.EditorPlugin; 4 | 5 | public class LevelPropertiesPlugin extends EditorPlugin { 6 | 7 | private LevelPropertiesUi ui; 8 | 9 | @Override 10 | public void initialize() { 11 | ui = new LevelPropertiesUi(new LevelPropertiesPresenter()); 12 | editorView.addLeftPanelTab("Properties", ui); 13 | } 14 | 15 | public LevelPropertiesPresenter getPresenter() { 16 | return ui.getPresenter(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/importers/Importer.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.importers; 2 | 3 | import com.badlogic.gdx.files.FileHandle; 4 | import me.manabreak.ratio.common.Properties; 5 | import me.manabreak.ratio.plugins.level.Level; 6 | import me.manabreak.ratio.plugins.objects.GameObject; 7 | import me.manabreak.ratio.utils.Action1; 8 | 9 | import java.util.List; 10 | 11 | public interface Importer { 12 | String getFileExtension(); 13 | 14 | String getFormatName(); 15 | 16 | Level load(FileHandle fh, Action1> objectCallback, Action1 propertiesCallback) throws ImporterException; 17 | } 18 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/toolbar/Tool.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.toolbar; 2 | 3 | public enum Tool { 4 | /** 5 | * Denotes the state when no tool is chosen 6 | */ 7 | NONE, 8 | 9 | /** 10 | * Draw voxel blocks 11 | */ 12 | BLOCK, 13 | 14 | /** 15 | * Draw horizontal quads 16 | */ 17 | FLOOR, 18 | 19 | /** 20 | * Paint existing voxel faces 21 | */ 22 | PAINT, 23 | 24 | /** 25 | * Erase blocks 26 | */ 27 | ERASE, 28 | 29 | /** 30 | * Select objects 31 | */ 32 | SELECT, 33 | 34 | /** 35 | * Create objects 36 | */ 37 | CREATE 38 | } 39 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/level/ToolPresenter.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.level; 2 | 3 | import io.reactivex.Observable; 4 | import io.reactivex.subjects.PublishSubject; 5 | import me.manabreak.ratio.common.mvp.MvpPresenter; 6 | 7 | public class ToolPresenter extends MvpPresenter { 8 | 9 | private final PublishSubject toolSizeSubject = PublishSubject.create(); 10 | 11 | public void toolSizeChanged(int s) { 12 | int toolSize = 1 << s; 13 | view.toolSizeChanged(toolSize); 14 | toolSizeSubject.onNext(toolSize); 15 | } 16 | 17 | public Observable getToolSizeObservable() { 18 | return toolSizeSubject; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/ui/IntegerTextFieldFilter.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.ui; 2 | 3 | import com.kotcrab.vis.ui.widget.VisTextField; 4 | 5 | public class IntegerTextFieldFilter implements VisTextField.TextFieldFilter { 6 | 7 | private boolean allowNegative = true; 8 | 9 | public IntegerTextFieldFilter() { 10 | allowNegative = true; 11 | } 12 | 13 | public IntegerTextFieldFilter(boolean allowNegative) { 14 | this.allowNegative = allowNegative; 15 | } 16 | 17 | @Override 18 | public boolean acceptChar(VisTextField textField, char c) { 19 | return Character.isDigit(c) || (allowNegative && textField.getText().length() == 0 && c == '-'); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/editor/EditorTab.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.editor; 2 | 3 | import com.badlogic.gdx.scenes.scene2d.ui.Table; 4 | import com.kotcrab.vis.ui.widget.VisTable; 5 | import com.kotcrab.vis.ui.widget.tabbedpane.Tab; 6 | 7 | public class EditorTab extends Tab { 8 | 9 | private final String title; 10 | private final VisTable content; 11 | 12 | public EditorTab(String title, VisTable content) { 13 | super(false, false); 14 | this.title = title; 15 | this.content = content; 16 | } 17 | 18 | @Override 19 | public String getTabTitle() { 20 | return title; 21 | } 22 | 23 | @Override 24 | public Table getContentTable() { 25 | return content; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /editor/assets/graphics/editor.atlas: -------------------------------------------------------------------------------- 1 | 2 | editor.png 3 | size: 512,128 4 | format: RGBA8888 5 | filter: Linear,Linear 6 | repeat: none 7 | arrow_left 8 | rotate: false 9 | xy: 1, 1 10 | size: 96, 96 11 | orig: 96, 96 12 | offset: 0, 0 13 | index: -1 14 | close 15 | rotate: false 16 | xy: 100, 1 17 | size: 96, 96 18 | orig: 96, 96 19 | offset: 0, 0 20 | index: -1 21 | cursor-default 22 | rotate: false 23 | xy: 298, 49 24 | size: 48, 48 25 | orig: 48, 48 26 | offset: 0, 0 27 | index: -1 28 | folder 29 | rotate: false 30 | xy: 199, 1 31 | size: 96, 96 32 | orig: 96, 96 33 | offset: 0, 0 34 | index: -1 35 | plus-box 36 | rotate: false 37 | xy: 349, 49 38 | size: 48, 48 39 | orig: 48, 48 40 | offset: 0, 0 41 | index: -1 42 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/ui/UiUtils.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.ui; 2 | 3 | import com.badlogic.gdx.scenes.scene2d.Stage; 4 | import com.kotcrab.vis.ui.util.dialog.Dialogs; 5 | import com.kotcrab.vis.ui.util.dialog.InputDialogAdapter; 6 | import me.manabreak.ratio.utils.Action1; 7 | 8 | public class UiUtils { 9 | public static void promptProperty(Stage stage, String type, Action1 key) { 10 | Dialogs.InputDialog dialog = new Dialogs.InputDialog("New " + type, "Name", true, input -> !input.isEmpty(), new InputDialogAdapter() { 11 | @Override 12 | public void finished(String input) { 13 | key.call(input); 14 | } 15 | }); 16 | stage.addActor(dialog); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/editor/TilesetTab.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.editor; 2 | 3 | import com.badlogic.gdx.scenes.scene2d.ui.Table; 4 | import com.kotcrab.vis.ui.widget.VisTable; 5 | import com.kotcrab.vis.ui.widget.tabbedpane.Tab; 6 | import me.manabreak.ratio.plugins.tilesets.Tileset; 7 | 8 | public class TilesetTab extends Tab { 9 | 10 | private final Tileset tileset; 11 | private final VisTable content; 12 | 13 | public TilesetTab(Tileset tileset, VisTable content) { 14 | super(false, false); 15 | this.tileset = tileset; 16 | this.content = content; 17 | } 18 | 19 | @Override 20 | public String getTabTitle() { 21 | return tileset.getName(); 22 | } 23 | 24 | @Override 25 | public Table getContentTable() { 26 | return content; 27 | } 28 | 29 | public Tileset getTileset() { 30 | return tileset; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/editor/menu/ConfirmMenuItem.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.editor.menu; 2 | 3 | import com.badlogic.gdx.scenes.scene2d.Actor; 4 | import com.badlogic.gdx.scenes.scene2d.Stage; 5 | import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; 6 | import com.kotcrab.vis.ui.util.dialog.Dialogs; 7 | import com.kotcrab.vis.ui.widget.MenuItem; 8 | 9 | import me.manabreak.ratio.utils.Action1; 10 | 11 | public class ConfirmMenuItem extends MenuItem { 12 | public ConfirmMenuItem(Stage stage, String text, String title, String message, Action1 result) { 13 | super(text, new ChangeListener() { 14 | @Override 15 | public void changed(ChangeEvent event, Actor actor) { 16 | Dialogs.showConfirmDialog(stage, title, message, new String[] {"Cancel", "OK"}, new Boolean[] {false, true}, result::call); 17 | } 18 | }); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/editor/EditorView.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.editor; 2 | 3 | import com.badlogic.gdx.InputProcessor; 4 | import com.badlogic.gdx.scenes.scene2d.Actor; 5 | import com.badlogic.gdx.scenes.scene2d.ui.Cell; 6 | import com.badlogic.gdx.scenes.scene2d.ui.Table; 7 | import com.kotcrab.vis.ui.widget.VisTable; 8 | import me.manabreak.ratio.plugins.exporters.Exporter; 9 | import me.manabreak.ratio.plugins.importers.Importer; 10 | 11 | import java.util.List; 12 | 13 | public interface EditorView extends InputProcessor { 14 | 15 | void setToolView(VisTable content); 16 | 17 | void addLeftPanelTab(String title, VisTable content); 18 | 19 | void showSaveAsDialog(List exporters); 20 | 21 | void showOpenDialog(List importers); 22 | 23 | void registerToolbar(Table table); 24 | 25 | void toggleLeftPanel(); 26 | 27 | void toggleRightPanel(); 28 | 29 | void setRightPanelTop(VisTable content); 30 | 31 | void setRightPanelBottom(VisTable content); 32 | } 33 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/tilesets/TilesetPlugin.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.tilesets; 2 | 3 | import com.badlogic.gdx.graphics.g2d.TextureRegion; 4 | import me.manabreak.ratio.editor.EditorPlugin; 5 | import me.manabreak.ratio.plugins.level.Level; 6 | 7 | public class TilesetPlugin extends EditorPlugin { 8 | private TilesetUi ui; 9 | 10 | @Override 11 | public void initialize() { 12 | ui = new TilesetUi(new TilesetPresenter(this, editorController.getTilesetManager())); 13 | editorView.setRightPanelBottom(ui); 14 | } 15 | 16 | public TextureRegion getCurrentRegion() { 17 | return ui.getPresenter().getCurrentRegion(); 18 | } 19 | 20 | public void levelLoaded(Level level) { 21 | // TODO 22 | for (Tileset tileset : level.getTilesets()) { 23 | ui.createTab(tileset); 24 | final boolean palette = tileset instanceof PaletteTileset; 25 | ui.createPicker(tileset, palette ? 1 : 16, palette ? 1 : 16); 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/exporters/ExportManager.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.exporters; 2 | 3 | import com.badlogic.gdx.files.FileHandle; 4 | import me.manabreak.ratio.common.Properties; 5 | import me.manabreak.ratio.plugins.level.Level; 6 | import me.manabreak.ratio.plugins.objects.GameObject; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | public class ExportManager { 12 | 13 | private final List exporters = new ArrayList<>(); 14 | 15 | public void register(Exporter exporter) { 16 | exporters.add(exporter); 17 | } 18 | 19 | public List getExporters() { 20 | return exporters; 21 | } 22 | 23 | public void save(Level level, List objects, Properties properties, FileHandle fh) { 24 | for (Exporter exporter : exporters) { 25 | if (exporter.getFileExtension().equalsIgnoreCase(fh.extension())) { 26 | exporter.save(level, objects, properties, fh); 27 | break; 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/editor/PluginManager.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.editor; 2 | 3 | import java.util.LinkedHashMap; 4 | import java.util.Map; 5 | 6 | public class PluginManager { 7 | 8 | private final Map, EditorPlugin> plugins = new LinkedHashMap<>(); 9 | 10 | public PluginManager() { 11 | 12 | } 13 | 14 | @SafeVarargs 15 | public final void register(T... plugins) { 16 | for (T plugin : plugins) { 17 | if (this.plugins.containsKey(plugin.getClass())) { 18 | throw new IllegalArgumentException("Cannot register same plugin twice!\nPlugin class in question: " + plugin.getClass().getSimpleName()); 19 | } 20 | this.plugins.put(plugin.getClass(), plugin); 21 | } 22 | } 23 | 24 | void bindPlugins(PluginHost host) { 25 | for (Map.Entry, EditorPlugin> entry : plugins.entrySet()) { 26 | host.addPlugin(entry.getValue()); 27 | } 28 | 29 | host.getPlugins().forEach(EditorPlugin::postInitialize); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/level/Coord.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.level; 2 | 3 | import java.util.Objects; 4 | 5 | public class Coord { 6 | public int x; 7 | public int y; 8 | public int z; 9 | 10 | public Coord() { 11 | 12 | } 13 | 14 | public Coord(int x, int y, int z) { 15 | this.x = x; 16 | this.y = y; 17 | this.z = z; 18 | } 19 | 20 | public Coord(Coord other) { 21 | this.x = other.x; 22 | this.y = other.y; 23 | this.z = other.z; 24 | } 25 | 26 | @Override 27 | public String toString() { 28 | return "X: " + x + ", Y: " + y + ", Z: " + z; 29 | } 30 | 31 | @Override 32 | public boolean equals(Object o) { 33 | if (this == o) return true; 34 | if (o == null || getClass() != o.getClass()) return false; 35 | Coord coord = (Coord) o; 36 | return x == coord.x && 37 | y == coord.y && 38 | z == coord.z; 39 | } 40 | 41 | @Override 42 | public int hashCode() { 43 | 44 | return Objects.hash(x, y, z); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /editor/src/test/java/me/manabreak/ratio/utils/LineUtilsTest.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.utils; 2 | 3 | import org.junit.Test; 4 | 5 | import java.util.List; 6 | 7 | import static org.junit.Assert.assertEquals; 8 | 9 | public class LineUtilsTest { 10 | 11 | @Test 12 | public void testBresenham2D_AlongPositiveX() { 13 | List points = LineUtils.bresenham(0, 0, 4, 0); 14 | assertEquals(10, points.size()); 15 | assertEquals(0, points.get(0).intValue()); 16 | assertEquals(1, points.get(2).intValue()); 17 | assertEquals(2, points.get(4).intValue()); 18 | assertEquals(3, points.get(6).intValue()); 19 | assertEquals(4, points.get(8).intValue()); 20 | } 21 | 22 | @Test 23 | public void testBresenham2D_AlongPositiveY() { 24 | List points = LineUtils.bresenham(0, 0, 0, 4); 25 | assertEquals(10, points.size()); 26 | assertEquals(0, points.get(1).intValue()); 27 | assertEquals(1, points.get(3).intValue()); 28 | assertEquals(2, points.get(5).intValue()); 29 | assertEquals(3, points.get(7).intValue()); 30 | assertEquals(4, points.get(9).intValue()); 31 | } 32 | } -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/exporters/Exporter.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.exporters; 2 | 3 | import com.badlogic.gdx.files.FileHandle; 4 | import me.manabreak.ratio.common.Properties; 5 | import me.manabreak.ratio.plugins.level.Level; 6 | import me.manabreak.ratio.plugins.objects.GameObject; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * Interface for all exporters formats. 12 | */ 13 | public interface Exporter { 14 | 15 | /** 16 | * Retrieves the file extension without the leading dot, for example "txt" 17 | * 18 | * @return File extension used for the exporters format 19 | */ 20 | String getFileExtension(); 21 | 22 | /** 23 | * Retrieves the human-readable name of the format, eg. "JSON File" 24 | * 25 | * @return The name of the format 26 | */ 27 | String getFormatName(); 28 | 29 | /** 30 | * Saves the given level to the given file 31 | * @param level to save 32 | * @param objects List of game objects 33 | * @param properties 34 | * @param fh file handle of the target file 35 | */ 36 | void save(Level level, List objects, Properties properties, FileHandle fh); 37 | } 38 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/importers/ImportManager.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.importers; 2 | 3 | import com.badlogic.gdx.files.FileHandle; 4 | import me.manabreak.ratio.common.Properties; 5 | import me.manabreak.ratio.plugins.level.Level; 6 | import me.manabreak.ratio.plugins.objects.GameObject; 7 | import me.manabreak.ratio.utils.Action1; 8 | 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | public class ImportManager { 13 | 14 | private final List importers = new ArrayList<>(); 15 | 16 | public void register(Importer importer) { 17 | importers.add(importer); 18 | } 19 | 20 | public List getImporters() { 21 | return importers; 22 | } 23 | 24 | public Level load(FileHandle fh, Action1> objectCallback, Action1 propertyCallback) throws ImporterException, MissingImporterException { 25 | for (Importer importer : importers) { 26 | if (importer.getFileExtension().equalsIgnoreCase(fh.extension())) { 27 | return importer.load(fh, objectCallback, propertyCallback); 28 | } 29 | } 30 | 31 | throw new MissingImporterException(fh); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/editor/TabbedContainer.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.editor; 2 | 3 | import com.kotcrab.vis.ui.widget.VisTable; 4 | import com.kotcrab.vis.ui.widget.tabbedpane.Tab; 5 | import com.kotcrab.vis.ui.widget.tabbedpane.TabbedPane; 6 | import com.kotcrab.vis.ui.widget.tabbedpane.TabbedPaneListener; 7 | import me.manabreak.ratio.ui.DefTable; 8 | 9 | public class TabbedContainer extends DefTable implements TabbedPaneListener { 10 | private TabbedPane tabs; 11 | private VisTable content; 12 | 13 | public TabbedContainer() { 14 | super(); 15 | 16 | tabs = new TabbedPane(); 17 | tabs.getTable().top(); 18 | add(tabs.getTable()).growX(); 19 | 20 | row(); 21 | tabs.addListener(this); 22 | 23 | content = new VisTable(); 24 | add(content).grow().top(); 25 | } 26 | 27 | public void addTab(Tab tab) { 28 | tabs.add(tab); 29 | } 30 | 31 | @Override 32 | public void switchedTab(Tab tab) { 33 | content.clear(); 34 | content.add(tab.getContentTable()).grow(); 35 | } 36 | 37 | @Override 38 | public void removedTab(Tab tab) { 39 | 40 | } 41 | 42 | @Override 43 | public void removedAllTabs() { 44 | 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/objects/ObjectShader.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.objects; 2 | 3 | import com.badlogic.gdx.graphics.glutils.ShaderProgram; 4 | 5 | public class ObjectShader extends ShaderProgram { 6 | 7 | private static final String VERTEX = "attribute vec4 a_position;\n" + 8 | "attribute vec4 a_color;\n" + 9 | "\n" + 10 | "uniform mat4 u_projTrans;\n" + 11 | "varying vec3 v_normal;\n" + 12 | "varying vec4 v_color;\n" + 13 | "\n" + 14 | "void main() {\n" + 15 | " v_normal = a_normal;\n" + 16 | " v_color = a_color;\n" + 17 | " gl_Position = u_projTrans * a_position;\n" + 18 | "}"; 19 | private static final String FRAGMENT = "varying vec3 v_normal;\n" + 20 | "varying vec4 v_color;\n" + 21 | "\n" + 22 | "void main() {\n" + 23 | " gl_FragColor = v_color;\n" + 24 | " \n" + 25 | " vec3 light = vec3(0.2, -0.8, -0.6);\n" + 26 | " light = normalize(light);\n" + 27 | " float d = max(0.2, dot(v_normal, -light));\n" + 28 | " gl_FragColor *= d;\n" + 29 | "}"; 30 | 31 | public ObjectShader() { 32 | super(VERTEX, FRAGMENT); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/editor/EditorPlugin.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.editor; 2 | 3 | import com.badlogic.gdx.InputAdapter; 4 | 5 | /** 6 | * Base class for all editor plugins 7 | */ 8 | public abstract class EditorPlugin extends InputAdapter { 9 | 10 | public EditorController editorController; 11 | protected EditorView editorView; 12 | private boolean active = false; 13 | 14 | protected EditorPlugin() { 15 | 16 | } 17 | 18 | void setEditorController(EditorController editorController) { 19 | this.editorController = editorController; 20 | } 21 | 22 | void setEditorView(EditorView editorView) { 23 | this.editorView = editorView; 24 | } 25 | 26 | public boolean isActive() { 27 | return active; 28 | } 29 | 30 | void setActive(boolean active) { 31 | this.active = active; 32 | } 33 | 34 | /** 35 | * Called when all the plugin dependencies are set and 36 | * the plugin is ready to initialize itself. 37 | */ 38 | public void initialize() { 39 | // NOP 40 | } 41 | 42 | /** 43 | * Called when all plugins have initialized themselves. 44 | */ 45 | public void postInitialize() { 46 | // NOP 47 | } 48 | 49 | protected T getPlugin(Class aClass) { 50 | return editorController.getPlugin(aClass); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/level/DrawFloorCommand.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.level; 2 | 3 | import com.badlogic.gdx.graphics.g2d.TextureRegion; 4 | import me.manabreak.ratio.plugins.tilesets.Tileset; 5 | 6 | public class DrawFloorCommand implements Command { 7 | private final LevelEditorPlugin plugin; 8 | private final TileLayer layer; 9 | private final Tileset tileset; 10 | private final int x; 11 | private final int y; 12 | private final int z; 13 | private final int size; 14 | private final TextureRegion region; 15 | 16 | public DrawFloorCommand(LevelEditorPlugin plugin, TileLayer layer, TextureRegion region, Tileset tileset, int x, int y, int z, int size) { 17 | this.plugin = plugin; 18 | this.layer = layer; 19 | this.region = region; 20 | this.tileset = tileset; 21 | this.x = x; 22 | this.y = y; 23 | this.z = z; 24 | this.size = size; 25 | } 26 | 27 | @Override 28 | public void execute() { 29 | layer.drawFloor(tileset, x, y, z, size); 30 | final Tile tile = tileset.createTile(region); 31 | layer.paint(tileset, x, y, z, size, Face.TOP, tile); 32 | plugin.recreateMeshes(); 33 | } 34 | 35 | @Override 36 | public void undo() { 37 | layer.erase(tileset, x, y, z, size); 38 | plugin.recreateMeshes(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/ui/Res.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.ui; 2 | 3 | import com.badlogic.gdx.Gdx; 4 | import com.kotcrab.vis.ui.VisUI; 5 | 6 | public class Res { 7 | 8 | public static final String ICONS_LARGE = "icons-large"; 9 | public static final String ICONS_SMALL = "icons-small"; 10 | public static final String ICONS_SMALL_TOGGLE = "icons-small-toggle"; 11 | 12 | public static final String ICON_EYE = "\uf06e"; 13 | public static final String ICON_EYE_OFF = "\uf070"; 14 | public static final String ICON_ADD = "\uf067"; 15 | public static final String ICON_UP = "\uf106"; 16 | public static final String ICON_DOWN = "\uf107"; 17 | public static final String ICON_DELETE = "\uf00d"; 18 | public static final String ICON_BLOCK = "\uf1b2"; 19 | public static final String ICON_FLOOR = "\uf0c8"; 20 | public static final String ICON_ERASE = "\uf12d"; 21 | public static final String ICON_PAINT = "\uf1fc"; 22 | public static final String ICON_SELECT = "\uf245"; 23 | public static final String ICON_BOX_CARET_LEFT = "\uf191"; 24 | public static final String ICON_BOX_CARET_RIGHT = "\uf152"; 25 | public static final String ICON_CAMERA = "\uf030"; 26 | public static final String ICON_LOCK = "\uf023"; 27 | public static final String ICON_CREATE = "\uf0fe"; 28 | 29 | public static void load() { 30 | VisUI.load(Gdx.files.internal("tinted.json")); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ratio Editor [![Build Status](https://travis-ci.org/manabreak/ratio.svg?branch=master)](https://travis-ci.org/manabreak/ratio) 2 | [![codecov](https://codecov.io/gh/manabreak/ratio/branch/master/graph/badge.svg)](https://codecov.io/gh/manabreak/ratio) 3 | 4 | Ratio Editor is a voxel editor with some extra features. Download @ [itch.io](https://manabreak.itch.io/ratio-editor) 5 | 6 | [![Imgur](https://i.imgur.com/NILRZ0P.png)](https://i.imgur.com/NILRZ0P.png) 7 | 8 | ## Building and running 9 | 10 | Clone the repository: 11 | 12 | git clone https://github.com/manabreak/ratio.git 13 | 14 | `cd` into `ratio` directory: 15 | 16 | cd ratio 17 | 18 | Build using gradle `dist` task: 19 | 20 | ./gradlew dist 21 | 22 | Run: 23 | 24 | java -jar editor/build/libs/editor.jar 25 | 26 | ## Executing tests 27 | 28 | You can run all the unit tests by running `./gradlew clean test` 29 | 30 | ## Contributing to Ratio 31 | 32 | All contributions - bug fixes, new features, documentation, increasing test coverage - are highly appreciated! While contribution guidelines are still on my TODO list, here are a few pointers: 33 | 34 | - Follow the general coding style used throughout the code base; use auto-formatting 35 | 36 | - Strive to follow SOLID principles and keep your code DRY 37 | 38 | - When possible, verify the changes with unit tests; missing tests should be an exception 39 | 40 | - Keep your changes as small as possible, but not smaller 41 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/level/DrawBlockCommand.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.level; 2 | 3 | import com.badlogic.gdx.graphics.g2d.TextureRegion; 4 | import me.manabreak.ratio.plugins.tilesets.Tileset; 5 | 6 | public class DrawBlockCommand implements Command { 7 | 8 | private final LevelEditorPlugin plugin; 9 | private final TileLayer layer; 10 | private final Tileset tileset; 11 | private final int x; 12 | private final int y; 13 | private final int z; 14 | private final int size; 15 | private final TextureRegion region; 16 | 17 | public DrawBlockCommand(LevelEditorPlugin plugin, TileLayer layer, TextureRegion region, Tileset tileset, int x, int y, int z, int size) { 18 | this.plugin = plugin; 19 | this.layer = layer; 20 | this.region = region; 21 | this.tileset = tileset; 22 | this.x = x; 23 | this.y = y; 24 | this.z = z; 25 | this.size = size; 26 | } 27 | 28 | @Override 29 | public void execute() { 30 | layer.draw(tileset, x, y, z, size); 31 | final Tile tile = tileset.createTile(region); 32 | for (Face face : Face.values()) { 33 | layer.paint(tileset, x, y, z, size, face, tile); 34 | } 35 | plugin.recreateMeshes(); 36 | } 37 | 38 | @Override 39 | public void undo() { 40 | layer.erase(tileset, x, y, z, size); 41 | plugin.recreateMeshes(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/level/EraseBlockCommand.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.level; 2 | 3 | import com.badlogic.gdx.graphics.g2d.TextureRegion; 4 | import me.manabreak.ratio.plugins.tilesets.Tileset; 5 | 6 | public class EraseBlockCommand implements Command { 7 | 8 | private final LevelEditorPlugin plugin; 9 | private final TileLayer layer; 10 | private final TextureRegion region; 11 | private final Tileset tileset; 12 | private final int x; 13 | private final int y; 14 | private final int z; 15 | private final int size; 16 | 17 | public EraseBlockCommand(LevelEditorPlugin plugin, TileLayer layer, TextureRegion region, Tileset tileset, int x, int y, int z, int size) { 18 | this.plugin = plugin; 19 | this.layer = layer; 20 | this.region = region; 21 | this.tileset = tileset; 22 | this.x = x; 23 | this.y = y; 24 | this.z = z; 25 | this.size = size; 26 | } 27 | 28 | @Override 29 | public void execute() { 30 | layer.erase(tileset, x, y, z, size); 31 | plugin.recreateMeshes(); 32 | } 33 | 34 | @Override 35 | public void undo() { 36 | /* 37 | TextureRegion tmpRegion = layer.getCurrentRegion(); 38 | layer.setCurrentRegion(region); 39 | layer.draw(tileset, x, y, z, size); 40 | layer.setCurrentRegion(tmpRegion); 41 | */ 42 | plugin.recreateMeshes(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/toolbar/ToolbarPlugin.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.toolbar; 2 | 3 | import me.manabreak.ratio.editor.EditorPlugin; 4 | import me.manabreak.ratio.plugins.camera.EditorCameraPlugin; 5 | import me.manabreak.ratio.plugins.level.LevelEditorPlugin; 6 | 7 | public class ToolbarPlugin extends EditorPlugin { 8 | 9 | private ToolbarUi toolbarUi; 10 | 11 | @Override 12 | public void initialize() { 13 | toolbarUi = new ToolbarUi(new ToolbarPresenter(editorView)); 14 | editorView.registerToolbar(toolbarUi); 15 | } 16 | 17 | @Override 18 | public void postInitialize() { 19 | toolbarUi.getPresenter().getToolSubject() 20 | .subscribe(tool -> { 21 | getPlugin(LevelEditorPlugin.class).setTool(tool); 22 | }); 23 | 24 | toolbarUi.getPresenter().getCameraSnapObservable() 25 | .subscribe(snap -> { 26 | getPlugin(EditorCameraPlugin.class).setCameraSnap(snap); 27 | }); 28 | 29 | toolbarUi.getPresenter().getCameraProjectionObservable() 30 | .subscribe(perspective -> { 31 | getPlugin(EditorCameraPlugin.class).setProjection(perspective); 32 | }); 33 | } 34 | 35 | public ToolbarPresenter getPresenter() { 36 | return toolbarUi.getPresenter(); 37 | } 38 | 39 | public ToolbarUi getUi() { 40 | return toolbarUi; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/level/PaintCommand.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.level; 2 | 3 | import com.badlogic.gdx.graphics.g2d.TextureRegion; 4 | import me.manabreak.ratio.plugins.tilesets.Tileset; 5 | 6 | public class PaintCommand implements Command { 7 | private final LevelEditorPlugin plugin; 8 | private final TileLayer layer; 9 | private final TextureRegion region; 10 | private final Tileset tileset; 11 | private final int x; 12 | private final int y; 13 | private final int z; 14 | private final int size; 15 | private final Face face; 16 | 17 | // Stored on execute for undoing 18 | private Tile oldTile; 19 | 20 | public PaintCommand(LevelEditorPlugin plugin, TileLayer layer, TextureRegion region, Tileset tileset, int x, int y, int z, int size, Face face) { 21 | this.plugin = plugin; 22 | this.layer = layer; 23 | this.region = region; 24 | this.tileset = tileset; 25 | this.x = x; 26 | this.y = y; 27 | this.z = z; 28 | this.size = size; 29 | this.face = face; 30 | } 31 | 32 | @Override 33 | public void execute() { 34 | oldTile = layer.getTile(tileset, x, y, z, size, face); 35 | layer.paint(tileset, x, y, z, size, face, tileset.createTile(region)); 36 | plugin.recreateMeshes(); 37 | } 38 | 39 | @Override 40 | public void undo() { 41 | layer.paint(tileset, x, y, z, size, face, oldTile); 42 | plugin.recreateMeshes(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/level/Cell.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.level; 2 | 3 | import java.util.EnumMap; 4 | import java.util.Map; 5 | 6 | public class Cell { 7 | private Map faces = new EnumMap<>(Face.class); 8 | private int x, y, z; 9 | private int size = 1; 10 | private Type type = Type.BLOCK; 11 | 12 | public Cell(int x, int y, int z) { 13 | this.x = x; 14 | this.y = y; 15 | this.z = z; 16 | for (Face face : Face.values()) { 17 | faces.put(face, 0); 18 | } 19 | } 20 | 21 | public int getX() { 22 | return x; 23 | } 24 | 25 | public int getY() { 26 | return y; 27 | } 28 | 29 | public int getZ() { 30 | return z; 31 | } 32 | 33 | public Type getType() { 34 | return type; 35 | } 36 | 37 | public void setType(Type type) { 38 | this.type = type; 39 | } 40 | 41 | public Integer get(Face face) { 42 | return faces.get(face); 43 | } 44 | 45 | public void clear(Face face) { 46 | faces.put(face, 0); 47 | size = 1; 48 | } 49 | 50 | public void set(Face face, Tile tile) { 51 | faces.put(face, tile.getId()); 52 | } 53 | 54 | public boolean has(Face face) { 55 | return get(face) != 0; 56 | } 57 | 58 | public int getSize() { 59 | return size; 60 | } 61 | 62 | public void setSize(int size) { 63 | this.size = size; 64 | } 65 | 66 | public enum Type { 67 | BLOCK, 68 | FLOOR 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/editor/EditorGameScreen.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.editor; 2 | 3 | import com.badlogic.gdx.Gdx; 4 | import com.badlogic.gdx.ScreenAdapter; 5 | import com.badlogic.gdx.graphics.GL20; 6 | 7 | import me.manabreak.ratio.plugins.camera.EditorCamera; 8 | import me.manabreak.ratio.plugins.exporters.ExportManager; 9 | import me.manabreak.ratio.plugins.importers.ImportManager; 10 | import me.manabreak.ratio.plugins.tilesets.TilesetManager; 11 | 12 | public class EditorGameScreen extends ScreenAdapter { 13 | 14 | private final EditorController editorController; 15 | private EditorStage editorStage; 16 | 17 | public EditorGameScreen(PluginManager pluginManager, ExportManager exportManager, ImportManager importManager, TilesetManager tilesetManager) { 18 | editorController = new EditorController(pluginManager, exportManager, importManager, tilesetManager); 19 | editorStage = new EditorStage(editorController); 20 | pluginManager.bindPlugins(editorController); 21 | } 22 | 23 | @Override 24 | public void show() { 25 | 26 | } 27 | 28 | @Override 29 | public void render(float delta) { 30 | Gdx.gl20.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT); 31 | editorController.act(delta); 32 | editorStage.act(delta); 33 | editorStage.draw(); 34 | } 35 | 36 | @Override 37 | public void resize(int width, int height) { 38 | System.out.println("Resize: " + width + "," + height); 39 | editorStage.resize(width, height); 40 | EditorCamera.main.resize(width, height); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/properties/LevelPropertiesUi.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.properties; 2 | 3 | import com.kotcrab.vis.ui.widget.ListView; 4 | import com.kotcrab.vis.ui.widget.Tooltip; 5 | import com.kotcrab.vis.ui.widget.VisTable; 6 | import com.kotcrab.vis.ui.widget.VisTextButton; 7 | import me.manabreak.ratio.common.Properties; 8 | import me.manabreak.ratio.common.mvp.MvpView; 9 | import me.manabreak.ratio.ui.Res; 10 | 11 | public class LevelPropertiesUi extends MvpView { 12 | 13 | private final VisTable content = new VisTable(); 14 | 15 | protected LevelPropertiesUi(LevelPropertiesPresenter presenter) { 16 | super(presenter); 17 | top(); 18 | 19 | VisTable controls = new VisTable(); 20 | VisTextButton btnAddProperty = new VisTextButton(Res.ICON_ADD, Res.ICONS_SMALL); 21 | new Tooltip.Builder("Add Property").target(btnAddProperty).build(); 22 | btnAddProperty.addListener(new PropertyPrompt(btnAddProperty, presenter)); 23 | controls.add(btnAddProperty); 24 | add(controls).expandX(); 25 | 26 | row(); 27 | addSeparator(); 28 | row(); 29 | 30 | add(content).grow(); 31 | } 32 | 33 | public void showProperties(Properties props) { 34 | content.clear(); 35 | final PropertyAdapter adapter = new PropertyAdapter(props); 36 | adapter.setDeleteCallback(entry -> presenter.removeProperty(entry.getKey())); 37 | ListView listView = new ListView<>(adapter); 38 | content.add(listView.getMainTable()).grow().top(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/editor/TilesetTabContainer.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.editor; 2 | 3 | import com.badlogic.gdx.scenes.scene2d.ui.Table; 4 | import com.kotcrab.vis.ui.widget.VisTable; 5 | import com.kotcrab.vis.ui.widget.tabbedpane.Tab; 6 | import com.kotcrab.vis.ui.widget.tabbedpane.TabbedPane; 7 | import com.kotcrab.vis.ui.widget.tabbedpane.TabbedPaneListener; 8 | import me.manabreak.ratio.plugins.tilesets.TilesetPresenter; 9 | 10 | public class TilesetTabContainer extends Table implements TabbedPaneListener { 11 | private final TilesetPresenter presenter; 12 | private TabbedPane tabs; 13 | private Table content; 14 | 15 | public TilesetTabContainer(TilesetPresenter presenter) { 16 | super(); 17 | this.presenter = presenter; 18 | 19 | tabs = new TabbedPane(); 20 | add(tabs.getTable()).left().top().growX(); 21 | row(); 22 | tabs.addListener(this); 23 | 24 | content = new VisTable(); 25 | add(content).grow().top(); 26 | } 27 | 28 | public void addTab(Tab tab) { 29 | tabs.add(tab); 30 | } 31 | 32 | @Override 33 | public void switchedTab(Tab tab) { 34 | content.clear(); 35 | content.add(tab.getContentTable()).grow(); 36 | content.pack(); 37 | 38 | TilesetTab tt = (TilesetTab) tab; 39 | presenter.selectTileset(tt.getTileset()); 40 | } 41 | 42 | @Override 43 | public void removedTab(Tab tab) { 44 | 45 | } 46 | 47 | @Override 48 | public void removedAllTabs() { 49 | 50 | } 51 | 52 | public TabbedPane getTabs() { 53 | return tabs; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/EditorLauncher.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio; 2 | 3 | import com.badlogic.gdx.backends.lwjgl.LwjglApplication; 4 | import com.badlogic.gdx.backends.lwjgl.LwjglApplicationConfiguration; 5 | import com.badlogic.gdx.graphics.Texture; 6 | import com.badlogic.gdx.tools.texturepacker.TexturePacker; 7 | 8 | import java.io.File; 9 | 10 | public class EditorLauncher { 11 | public static void main(String[] arg) { 12 | LwjglApplicationConfiguration config = new LwjglApplicationConfiguration(); 13 | 14 | config.width = 1600; 15 | config.height = 900; 16 | config.title = "Ratio Editor"; 17 | // config.x = 2000; 18 | 19 | File f = new File("ui"); 20 | if (f.exists() && f.isDirectory() && f.listFiles().length > 0) { 21 | TexturePacker.Settings s = new TexturePacker.Settings(); 22 | s.filterMag = Texture.TextureFilter.Linear; 23 | s.filterMin = Texture.TextureFilter.Linear; 24 | s.maxWidth = 1024; 25 | s.maxHeight = 1024; 26 | s.paddingX = 3; 27 | s.paddingY = 3; 28 | s.edgePadding = true; 29 | s.alias = false; 30 | s.bleed = true; 31 | s.duplicatePadding = true; 32 | s.useIndexes = false; 33 | 34 | // TexturePacker.process(s, "images", "android/assets/graphics", "game"); 35 | TexturePacker.process(s, "ui", "editor/assets/graphics", "editor"); 36 | } else { 37 | System.out.println("No images to pack"); 38 | } 39 | 40 | new LwjglApplication(new EditorGame(), config); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/level/WireframeRenderer.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.level; 2 | 3 | import com.badlogic.gdx.graphics.Camera; 4 | import com.badlogic.gdx.graphics.Mesh; 5 | import com.badlogic.gdx.graphics.glutils.ShapeRenderer; 6 | 7 | import java.util.Collection; 8 | 9 | public class WireframeRenderer { 10 | 11 | private final ShapeRenderer renderer = new ShapeRenderer(); 12 | 13 | public void render(Camera camera, Collection meshes) { 14 | renderer.setProjectionMatrix(camera.combined); 15 | renderer.setColor(0.5f, 0.5f, 1f, 1f); 16 | renderer.begin(ShapeRenderer.ShapeType.Line); 17 | for (Mesh mesh : meshes) { 18 | float[] v = new float[mesh.getNumVertices() * 8]; 19 | short[] i = new short[mesh.getNumIndices()]; 20 | mesh.getVertices(v); 21 | mesh.getIndices(i); 22 | 23 | for (int c = 0; c < i.length; c += 3) { 24 | float x0 = v[i[c] * 8]; 25 | float y0 = v[i[c] * 8 + 1]; 26 | float z0 = v[i[c] * 8 + 2]; 27 | 28 | float x1 = v[i[c + 1] * 8]; 29 | float y1 = v[i[c + 1] * 8 + 1]; 30 | float z1 = v[i[c + 1] * 8 + 2]; 31 | 32 | float x2 = v[i[c + 2] * 8]; 33 | float y2 = v[i[c + 2] * 8 + 1]; 34 | float z2 = v[i[c + 2] * 8 + 2]; 35 | 36 | renderer.line(x0, y0, z0, x1, y1, z1); 37 | renderer.line(x1, y1, z1, x2, y2, z2); 38 | renderer.line(x2, y2, z2, x0, y0, z0); 39 | } 40 | } 41 | renderer.end(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/level/LevelShader.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.level; 2 | 3 | import com.badlogic.gdx.graphics.glutils.ShaderProgram; 4 | 5 | public class LevelShader extends ShaderProgram { 6 | 7 | private static final String SHADER_VERTEX = "attribute vec4 a_position;\n" + 8 | "attribute vec2 a_texCoord0;\n" + 9 | "attribute vec3 a_normal;\n" + 10 | "uniform mat4 u_projTrans;\n" + 11 | "varying vec2 v_texCoords;\n" + 12 | "varying vec3 v_normal;\n" + 13 | "\n" + 14 | "void main() {\n" + 15 | " v_texCoords = a_texCoord0;\n" + 16 | " v_normal = a_normal;\n" + 17 | " gl_Position = u_projTrans * a_position;\n" + 18 | "}"; 19 | private static final String SHADER_FRAGMENT = "varying vec2 v_texCoords;\n" + 20 | "varying vec3 v_normal;\n" + 21 | "\n" + 22 | "uniform sampler2D u_texture;\n" + 23 | "uniform vec3 u_lightDirection;\n" + 24 | "uniform float u_ambientIntensity;\n" + 25 | "uniform float u_level;\n" + 26 | "\n" + 27 | "void main() {\n" + 28 | " gl_FragColor = texture2D(u_texture, v_texCoords);\n" + 29 | " \n" + 30 | " vec3 light = u_lightDirection;\n" + 31 | " light = normalize(light);\n" + 32 | " float d = max(u_ambientIntensity, dot(v_normal, -light));\n" + 33 | " gl_FragColor *= d * u_level;\n" + 34 | "}"; 35 | 36 | public LevelShader() { 37 | super(SHADER_VERTEX, SHADER_FRAGMENT); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/level/Level.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.level; 2 | 3 | import me.manabreak.ratio.plugins.tilesets.Tileset; 4 | 5 | import java.util.ArrayList; 6 | import java.util.List; 7 | 8 | public class Level { 9 | private final ArrayList tilesets = new ArrayList<>(); 10 | private final ArrayList layers = new ArrayList<>(); 11 | 12 | public Level() { 13 | 14 | } 15 | 16 | public void addTileset(Tileset tileset) { 17 | this.tilesets.add(tileset); 18 | } 19 | 20 | public Tileset getTileset(int index) { 21 | return tilesets.get(index); 22 | } 23 | 24 | public Tileset getTileset(String name) { 25 | for (Tileset tileset : tilesets) { 26 | if (tileset.getName().equals(name)) return tileset; 27 | } 28 | return null; 29 | } 30 | 31 | public TileLayer createLayer(String name) { 32 | TileLayer layer = new TileLayer(name); 33 | layers.add(layer); 34 | return layer; 35 | } 36 | 37 | public void deleteLayer(TileLayer layer) { 38 | layers.remove(layer); 39 | } 40 | 41 | public TileLayer get(String name) { 42 | for (TileLayer layer : layers) { 43 | if (layer.getName().equals(name)) return layer; 44 | } 45 | throw new IllegalArgumentException("No such layer " + name); 46 | } 47 | 48 | public ArrayList getLayers() { 49 | return layers; 50 | } 51 | 52 | public void addLayer(int i, TileLayer layer) { 53 | this.layers.add(i, layer); 54 | } 55 | 56 | public List getTilesets() { 57 | return tilesets; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /editor/src/test/java/me/manabreak/ratio/plugins/tilesets/TilesetTest.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.tilesets; 2 | 3 | import com.badlogic.gdx.graphics.Color; 4 | import com.badlogic.gdx.graphics.g2d.TextureRegion; 5 | import me.manabreak.ratio.plugins.level.Tile; 6 | import me.manabreak.ratio.test.GdxTest; 7 | import org.junit.Test; 8 | 9 | import java.util.Map; 10 | 11 | import static org.junit.Assert.assertEquals; 12 | import static org.junit.Assert.assertNotNull; 13 | 14 | public class TilesetTest extends GdxTest { 15 | 16 | @Test 17 | public void testCreatePalette() { 18 | PaletteTileset p = new PaletteTileset("Pal"); 19 | assertEquals("Pal", p.getName()); 20 | assertEquals(Color.BLACK, p.getColorAt(0, 0)); 21 | 22 | p.draw(0, 0, Color.TAN); 23 | assertEquals(Color.TAN, p.getColorAt(0, 0)); 24 | 25 | final Map entries = p.getEntries(); 26 | assertEquals(1, entries.size()); 27 | 28 | assertNotNull(p.getTexture()); 29 | final Tile tile = p.createTile(new TextureRegion(p.getTexture())); 30 | assertNotNull(tile); 31 | assertEquals(1, p.getTiles().size); 32 | assertEquals(1, p.size()); 33 | assertEquals(0, tile.getId()); 34 | 35 | // Re-create the same tile; should return the same tile 36 | final Tile tile2 = p.createTile(new TextureRegion(p.getTexture())); 37 | assertEquals(tile, tile2); 38 | assertEquals(1, p.getTiles().size); 39 | assertEquals(1, p.size()); 40 | assertEquals(0, tile2.getId()); 41 | 42 | // Remove the tile 43 | p.removeTile(0); 44 | assertEquals(0, p.getTiles().size); 45 | } 46 | } -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/utils/LineUtils.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.utils; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public class LineUtils { 7 | 8 | /** 9 | * Plots a line in 2D space between two coordinates. 10 | * 11 | * @param x0 start X 12 | * @param y0 start Y 13 | * @param x1 end X 14 | * @param y1 end Y 15 | * @return all the coordinates along the line 16 | */ 17 | public static List bresenham(int x0, int y0, int x1, int y1) { 18 | List list = new ArrayList<>(Math.max(Math.abs(x1 - x0), Math.abs(y1 - y0))); 19 | int d = 0; 20 | int dx = Math.abs(x1 - x0); 21 | int dy = Math.abs(y1 - y0); 22 | int dx2 = 2 * dx; 23 | int dy2 = 2 * dy; 24 | int ix = x0 < x1 ? 1 : -1; 25 | int iy = y0 < y1 ? 1 : -1; 26 | 27 | int x = x0; 28 | int y = y0; 29 | if (dx >= dy) { 30 | while (true) { 31 | list.add(x); 32 | list.add(y); 33 | if (x == x1) break; 34 | x += ix; 35 | d += dy2; 36 | if (d > dx) { 37 | y += iy; 38 | d -= dx2; 39 | } 40 | } 41 | } else { 42 | while (true) { 43 | list.add(x); 44 | list.add(y); 45 | if (y == y1) break; 46 | y += iy; 47 | d += dx2; 48 | if (d > dy) { 49 | x += ix; 50 | d -= dy2; 51 | } 52 | } 53 | } 54 | return list; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/tilesets/Tileset.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.tilesets; 2 | 3 | import com.badlogic.gdx.graphics.Texture; 4 | import com.badlogic.gdx.graphics.g2d.TextureRegion; 5 | import com.badlogic.gdx.utils.IntMap; 6 | import me.manabreak.ratio.plugins.level.Tile; 7 | 8 | public abstract class Tileset { 9 | private final String name; 10 | private final Texture texture; 11 | private final IntMap tiles = new IntMap<>(); 12 | 13 | public Tileset(String name, Texture texture) { 14 | this.name = name; 15 | this.texture = texture; 16 | } 17 | 18 | public String getName() { 19 | return name; 20 | } 21 | 22 | public Texture getTexture() { 23 | return texture; 24 | } 25 | 26 | public IntMap getTiles() { 27 | return tiles; 28 | } 29 | 30 | public Tile getTile(int id) { 31 | return tiles.get(id); 32 | } 33 | 34 | public void putTile(int id, Tile tile) { 35 | tiles.put(id, tile); 36 | } 37 | 38 | public void removeTile(int id) { 39 | tiles.remove(id); 40 | } 41 | 42 | public int size() { 43 | return tiles.size; 44 | } 45 | 46 | public Tile createTile(TextureRegion region) { 47 | for (Tile tile : tiles.values()) { 48 | if (region.getRegionX() == tile.getX() && region.getRegionY() == tile.getY() && 49 | region.getRegionWidth() == tile.getWidth() && region.getRegionHeight() == tile.getHeight()) { 50 | return tile; 51 | } 52 | } 53 | 54 | Tile tile = new Tile(tiles.size, region); 55 | tiles.put(tile.getId(), tile); 56 | return tile; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/properties/LevelPropertiesPresenter.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.properties; 2 | 3 | import com.badlogic.gdx.math.Vector3; 4 | import me.manabreak.ratio.common.Properties; 5 | import me.manabreak.ratio.common.mvp.MvpPresenter; 6 | 7 | public class LevelPropertiesPresenter extends MvpPresenter implements PropertyHandler { 8 | 9 | private Properties properties = new Properties(); 10 | private PropertyAdapter adapter; 11 | 12 | public void createBooleanProperty(String key) { 13 | properties.setProperty(key, true); 14 | view.showProperties(properties); 15 | } 16 | 17 | public void createStringProperty(String key) { 18 | properties.setProperty(key, ""); 19 | view.showProperties(properties); 20 | } 21 | 22 | public void createIntProperty(String key) { 23 | properties.setProperty(key, 0); 24 | view.showProperties(properties); 25 | } 26 | 27 | public void createDoubleProperty(String key) { 28 | properties.setProperty(key, 0.0); 29 | view.showProperties(properties); 30 | } 31 | 32 | @Override 33 | public void createVec3Property(String key) { 34 | properties.setProperty(key, new Vector3()); 35 | view.showProperties(properties); 36 | } 37 | 38 | public void removeProperty(String key) { 39 | properties.removeProperty(key); 40 | view.showProperties(properties); 41 | } 42 | 43 | public void changePropertyValue(String key, Object value) { 44 | properties.setProperty(key, value); 45 | view.showProperties(properties); 46 | } 47 | 48 | public Properties getProperties() { 49 | return properties; 50 | } 51 | 52 | public void loadProperties(Properties properties) { 53 | this.properties = properties; 54 | view.showProperties(properties); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/tilesets/TilesetManager.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.tilesets; 2 | 3 | import com.badlogic.gdx.Gdx; 4 | import com.badlogic.gdx.files.FileHandle; 5 | import com.badlogic.gdx.graphics.Texture; 6 | import io.reactivex.subjects.PublishSubject; 7 | 8 | import java.util.Collection; 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | 12 | public class TilesetManager { 13 | private final Map textures = new HashMap<>(); 14 | private final Map tilesets = new HashMap<>(); 15 | private final PublishSubject tilesetSubject = PublishSubject.create(); 16 | 17 | private Tileset currentTileset = null; 18 | 19 | public PaletteTileset createPalette(String name) { 20 | if (textures.containsKey(name)) { 21 | return (PaletteTileset) tilesets.get(textures.get(name)); 22 | } 23 | 24 | PaletteTileset t = new PaletteTileset(name); 25 | tilesets.put(t.getTexture(), t); 26 | textures.put(name, t.getTexture()); 27 | return t; 28 | } 29 | 30 | public Tileset load(FileHandle handle) { 31 | String path = handle.path(); 32 | if (textures.containsKey(path)) return tilesets.get(textures.get(path)); 33 | 34 | Texture t = new Texture(handle, false); 35 | textures.put(path, t); 36 | 37 | Tileset tileset = new ImageTileset(handle.nameWithoutExtension(), Gdx.files.absolute(path).path(), t); 38 | tilesets.put(t, tileset); 39 | 40 | currentTileset = tileset; 41 | return tileset; 42 | } 43 | 44 | public Collection getTilesets() { 45 | return tilesets.values(); 46 | } 47 | 48 | public Tileset getCurrentTileset() { 49 | return currentTileset; 50 | } 51 | 52 | public void setCurrentTileset(Tileset currentTileset) { 53 | this.currentTileset = currentTileset; 54 | tilesetSubject.onNext(currentTileset); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /editor/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "application" 2 | apply plugin: "idea" 3 | apply plugin: "jacoco" 4 | 5 | mainClassName = "me.manabreak.ratio.EditorLauncher" 6 | project.ext.mainClassName = mainClassName 7 | project.ext.assetsDir = new File("assets") 8 | sourceCompatibility = 1.8 9 | 10 | dependencies { 11 | compile "com.badlogicgames.gdx:gdx:$gdxVersion" 12 | compile "com.badlogicgames.gdx:gdx-box2d:$gdxVersion" 13 | compile "com.badlogicgames.gdx:gdx-freetype:$gdxVersion" 14 | compile "com.badlogicgames.gdx:gdx-backend-lwjgl:$gdxVersion" 15 | compile "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop" 16 | compile "com.badlogicgames.gdx:gdx-box2d-platform:$gdxVersion:natives-desktop" 17 | compile "com.badlogicgames.gdx:gdx-tools:$gdxVersion" 18 | compile "com.badlogicgames.gdx:gdx-freetype-platform:$gdxVersion:natives-desktop" 19 | compile 'com.kotcrab.vis:vis-ui:1.4.1-SNAPSHOT' 20 | 21 | compile "io.reactivex.rxjava2:rxjava:${rxJavaVersion}" 22 | 23 | testCompile 'junit:junit:4.12' 24 | testCompile 'org.mockito:mockito-core:2.11.0' 25 | testCompile "com.badlogicgames.gdx:gdx-backend-headless:$gdxVersion" 26 | testCompile "com.badlogicgames.gdx:gdx:$gdxVersion" 27 | testCompile "com.badlogicgames.gdx:gdx-platform:$gdxVersion:natives-desktop" 28 | testCompile "com.badlogicgames.gdx:gdx-box2d:$gdxVersion" 29 | testCompile "com.badlogicgames.gdx:gdx-box2d-platform:$gdxVersion:natives-desktop" 30 | } 31 | 32 | task dist(type: Jar) { 33 | from files(sourceSets.main.output.classesDirs) 34 | from files(sourceSets.main.output.resourcesDir) 35 | from { configurations.compile.collect { zipTree(it) } } 36 | from files(project.assetsDir) 37 | 38 | manifest { 39 | attributes 'Main-Class': project.mainClassName 40 | } 41 | } 42 | 43 | dist.dependsOn classes 44 | 45 | jacocoTestReport { 46 | reports { 47 | xml.enabled = true 48 | html.enabled = true 49 | } 50 | } 51 | 52 | check.dependsOn jacocoTestReport -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/level/ToolView.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.level; 2 | 3 | import com.badlogic.gdx.scenes.scene2d.Actor; 4 | import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; 5 | import com.badlogic.gdx.utils.Align; 6 | import com.kotcrab.vis.ui.widget.VisLabel; 7 | import com.kotcrab.vis.ui.widget.VisSlider; 8 | import me.manabreak.ratio.common.mvp.MvpView; 9 | 10 | public class ToolView extends MvpView { 11 | 12 | private final ChangeListener toolSizeListener; 13 | private final VisSlider toolSizeSlider; 14 | private final VisLabel toolSizeLabel; 15 | 16 | protected ToolView(ToolPresenter presenter) { 17 | super(presenter); 18 | 19 | add("Tool Settings").padLeft(4f).align(Align.left); 20 | row().padBottom(4f); 21 | 22 | toolSizeSlider = new VisSlider(0f, 5f, 1f, false); 23 | toolSizeSlider.setValue(4f); 24 | toolSizeLabel = new VisLabel("Cell size: 16"); 25 | toolSizeLabel.setAlignment(Align.left); 26 | add(toolSizeLabel).padLeft(4f).padRight(4f); 27 | add(toolSizeSlider).growX().padLeft(4f).padRight(4f); 28 | toolSizeListener = new ChangeListener() { 29 | @Override 30 | public void changed(ChangeEvent event, Actor actor) { 31 | int s = Math.round(toolSizeSlider.getValue()); 32 | presenter.toolSizeChanged(s); 33 | } 34 | }; 35 | toolSizeSlider.addListener(toolSizeListener); 36 | row().pad(4f); 37 | addSeparator().colspan(2); 38 | } 39 | 40 | public void updateToolSize(int size) { 41 | int i = Integer.numberOfTrailingZeros(Integer.highestOneBit(size)); 42 | toolSizeSlider.removeListener(toolSizeListener); 43 | toolSizeSlider.setValue(i); 44 | toolSizeSlider.addListener(toolSizeListener); 45 | toolSizeLabel.setText("Tool size: " + size); 46 | } 47 | 48 | public void toolSizeChanged(int toolSize) { 49 | toolSizeLabel.setText("Cell size: " + toolSize); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/common/Properties.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.common; 2 | 3 | import java.util.ArrayList; 4 | 5 | public class Properties { 6 | private final ArrayList properties = new ArrayList<>(); 7 | 8 | public Properties() { 9 | } 10 | 11 | public boolean hasProperty(String key) { 12 | for (Entry entry : properties) { 13 | if (entry.key.equals(key)) return true; 14 | } 15 | return false; 16 | } 17 | 18 | public void setProperty(String key, Object value) { 19 | for (Entry entry : properties) { 20 | if (entry.key.equals(key)) { 21 | entry.value = value; 22 | return; 23 | } 24 | } 25 | 26 | Entry entry = new Entry(key, value); 27 | properties.add(entry); 28 | } 29 | 30 | public T getProperty(String key, T defaultValue) { 31 | for (Entry entry : properties) { 32 | if (entry.key.equals(key)) { 33 | //noinspection unchecked 34 | return (T) entry.value; 35 | } 36 | } 37 | return defaultValue; 38 | } 39 | 40 | public T removeProperty(String key) { 41 | for (Entry entry : properties) { 42 | if (entry.key.equals(key)) { 43 | properties.remove(entry); 44 | //noinspection unchecked 45 | return (T) entry.value; 46 | } 47 | } 48 | return null; 49 | } 50 | 51 | public ArrayList getProperties() { 52 | return properties; 53 | } 54 | 55 | public static class Entry { 56 | String key; 57 | Object value; 58 | 59 | Entry(String key, Object value) { 60 | this.key = key; 61 | this.value = value; 62 | } 63 | 64 | public String getKey() { 65 | return key; 66 | } 67 | 68 | public Object getValue() { 69 | return value; 70 | } 71 | 72 | public void setValue(Object value) { 73 | this.value = value; 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/objects/ObjectListAdapter.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.objects; 2 | 3 | import com.badlogic.gdx.scenes.scene2d.Actor; 4 | import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; 5 | import com.badlogic.gdx.scenes.scene2d.utils.Drawable; 6 | import com.badlogic.gdx.utils.Align; 7 | import com.kotcrab.vis.ui.VisUI; 8 | import com.kotcrab.vis.ui.util.adapter.ArrayListAdapter; 9 | import com.kotcrab.vis.ui.widget.VisTable; 10 | import com.kotcrab.vis.ui.widget.VisTextButton; 11 | import com.kotcrab.vis.ui.widget.VisTextField; 12 | 13 | import java.util.ArrayList; 14 | 15 | public class ObjectListAdapter extends ArrayListAdapter { 16 | 17 | private final Drawable bg = VisUI.getSkin().getDrawable("window-bg"); 18 | private final Drawable selection = VisUI.getSkin().getDrawable("list-selection"); 19 | private final ObjectEditorPresenter presenter; 20 | 21 | public ObjectListAdapter(ObjectEditorPresenter presenter, ArrayList array) { 22 | super(array); 23 | this.presenter = presenter; 24 | setSelectionMode(SelectionMode.SINGLE); 25 | } 26 | 27 | @Override 28 | protected VisTable createView(GameObject item) { 29 | VisTable t = new VisTable(true); 30 | 31 | VisTextButton btnRemove = new VisTextButton("-", new ChangeListener() { 32 | @Override 33 | public void changed(ChangeEvent event, Actor actor) { 34 | presenter.removeObject(item); 35 | } 36 | }); 37 | t.add(btnRemove).padRight(4f).minWidth(32f); 38 | 39 | VisTextField name = new VisTextField(item.getName()); 40 | name.setTextFieldListener((textField, c) -> { 41 | item.setName(textField.getText()); 42 | }); 43 | name.setEnterKeyFocusTraversal(true); 44 | name.setAlignment(Align.left); 45 | t.add(name).pad(2f).left(); 46 | t.add().growX(); 47 | return t; 48 | } 49 | 50 | @Override 51 | protected void selectView(VisTable view) { 52 | view.setBackground(selection); 53 | } 54 | 55 | @Override 56 | protected void deselectView(VisTable view) { 57 | view.setBackground(bg); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /editor/src/test/java/me/manabreak/ratio/test/GdxTest.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.test; 2 | 3 | import com.badlogic.gdx.Application; 4 | import com.badlogic.gdx.ApplicationListener; 5 | import com.badlogic.gdx.Gdx; 6 | import com.badlogic.gdx.backends.headless.HeadlessApplication; 7 | import com.badlogic.gdx.graphics.GL20; 8 | import com.badlogic.gdx.scenes.scene2d.ui.Skin; 9 | import com.kotcrab.vis.ui.VisUI; 10 | import com.kotcrab.vis.ui.widget.VisTextButton; 11 | import org.junit.AfterClass; 12 | import org.junit.BeforeClass; 13 | 14 | import static org.mockito.Mockito.mock; 15 | 16 | public abstract class GdxTest { 17 | // This is our "test" application 18 | private static Application application; 19 | 20 | // Before running any tests, initialize the application with the headless backend 21 | @BeforeClass 22 | public static void init() { 23 | // Note that we don't need to implement any of the listener's methods 24 | application = new HeadlessApplication(new ApplicationListener() { 25 | @Override 26 | public void create() { 27 | } 28 | 29 | @Override 30 | public void resize(int width, int height) { 31 | } 32 | 33 | @Override 34 | public void render() { 35 | } 36 | 37 | @Override 38 | public void pause() { 39 | } 40 | 41 | @Override 42 | public void resume() { 43 | } 44 | 45 | @Override 46 | public void dispose() { 47 | } 48 | }); 49 | 50 | // Use Mockito to mock the OpenGL methods since we are running headlessly 51 | Gdx.gl20 = mock(GL20.class); 52 | Gdx.gl = Gdx.gl20; 53 | VisUI.load(); 54 | final Skin skin = VisUI.getSkin(); 55 | 56 | // Fake the icons style for tests 57 | final VisTextButton.VisTextButtonStyle style = skin.get("default", VisTextButton.VisTextButtonStyle.class); 58 | skin.add("icons-small", style); 59 | } 60 | 61 | // After we are done, clean up the application 62 | @AfterClass 63 | public static void cleanUp() { 64 | // Exit the application first 65 | application.exit(); 66 | VisUI.dispose(true); 67 | application = null; 68 | } 69 | } -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/tilesets/PaletteTileset.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.tilesets; 2 | 3 | import com.badlogic.gdx.graphics.Color; 4 | import com.badlogic.gdx.graphics.Pixmap; 5 | import com.badlogic.gdx.graphics.Texture; 6 | import com.badlogic.gdx.graphics.TextureData; 7 | import com.badlogic.gdx.graphics.glutils.PixmapTextureData; 8 | 9 | import java.util.HashMap; 10 | import java.util.Map; 11 | import java.util.Objects; 12 | 13 | public class PaletteTileset extends Tileset { 14 | 15 | private final Map indexes = new HashMap<>(); 16 | private TextureData data; 17 | private Pixmap pixmap; 18 | 19 | public PaletteTileset(String name) { 20 | super(name, new Texture(64, 64, Pixmap.Format.RGB888)); 21 | pixmap = new Pixmap(64, 64, Pixmap.Format.RGB888); 22 | pixmap.setFilter(Pixmap.Filter.NearestNeighbour); 23 | data = new PixmapTextureData(pixmap, Pixmap.Format.RGB888, false, false); 24 | getTexture().load(data); 25 | } 26 | 27 | public void draw(int x, int y, Color color) { 28 | System.out.println("Drawing color " + color.toString() + " to " + x + ", " + y); 29 | int col = Color.rgba8888(color); 30 | pixmap.drawPixel(x, y, col); 31 | getTexture().load(data); 32 | indexes.put(new Tuple(x, y), color); 33 | } 34 | 35 | public Color getColorAt(int x, int y) { 36 | System.out.println("Fetching color from " + x + ", " + y); 37 | int i = pixmap.getPixel(x, y); 38 | Color c = new Color(i); 39 | System.out.println(" --> " + c.toString()); 40 | return c; 41 | } 42 | 43 | public Map getEntries() { 44 | return indexes; 45 | } 46 | 47 | public static class Tuple { 48 | public final int x, y; 49 | 50 | public Tuple(int x, int y) { 51 | this.x = x; 52 | this.y = y; 53 | } 54 | 55 | @Override 56 | public boolean equals(Object o) { 57 | if (this == o) return true; 58 | if (o == null || getClass() != o.getClass()) return false; 59 | Tuple tuple = (Tuple) o; 60 | return x == tuple.x && 61 | y == tuple.y; 62 | } 63 | 64 | @Override 65 | public int hashCode() { 66 | 67 | return Objects.hash(x, y); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /editor/src/test/java/me/manabreak/ratio/plugins/level/OctreeTest.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.level; 2 | 3 | import org.junit.Test; 4 | 5 | import java.util.List; 6 | 7 | import static org.junit.Assert.assertEquals; 8 | import static org.junit.Assert.assertNull; 9 | import static org.junit.Assert.assertTrue; 10 | 11 | public class OctreeTest { 12 | 13 | @Test 14 | public void testAddNode() { 15 | Octree o = new Octree<>(6); 16 | assertEquals(64, o.getLevel()); 17 | assertEquals(0, o.getItemCount()); 18 | 19 | o.insert(0, 0, 0, 8, 42); 20 | assertEquals(1, o.getItemCount()); 21 | 22 | Octree o1 = o.get(1); 23 | assertEquals(32, o1.getLevel()); 24 | 25 | Octree o2 = o1.get(1); 26 | assertEquals(16, o2.getLevel()); 27 | 28 | Octree o3 = o2.get(1); 29 | assertEquals(8, o3.getLevel()); 30 | assertEquals(42, o3.getItem()); 31 | 32 | o.insert(4, 0, 0, 4, 66); 33 | assertEquals(1, o.getItemCount()); 34 | assertNull(o3.getItem()); 35 | 36 | Octree o4 = o3.get(0); 37 | assertEquals(66, o4.getItem()); 38 | 39 | o.insert(0, 0, 0, 4, 69); 40 | assertEquals(2, o.getItemCount()); 41 | 42 | Octree o5 = o3.get(1); 43 | assertEquals(69, o5.getItem()); 44 | assertEquals(66, o4.getItem()); 45 | 46 | o5.clear(); 47 | assertEquals(1, o.getItemCount()); 48 | assertNull(o5.getItem()); 49 | assertEquals(66, o4.getItem()); 50 | 51 | o.clear(); 52 | assertEquals(0, o.getItemCount()); 53 | assertNull(o.getItem()); 54 | for (int i = 0; i < 8; ++i) { 55 | assertNull(o.get(i)); 56 | } 57 | } 58 | 59 | @Test 60 | public void testRemove() { 61 | Octree o = new Octree<>(6); 62 | o.insert(0, 0, 0, 8, 42); 63 | assertEquals(1, o.getItemCount()); 64 | 65 | o.remove(0, 0, 0, 8); 66 | assertEquals(0, o.getItemCount()); 67 | } 68 | 69 | @Test 70 | public void testFlatten() { 71 | Octree o = new Octree<>(); 72 | o.insert(0, 0, 0, 8, "Foo"); 73 | o.insert(32, 16, 8, 4, "Bar"); 74 | 75 | List f = o.flatten(); 76 | assertEquals(2, f.size()); 77 | assertTrue(f.contains("Foo")); 78 | assertTrue(f.contains("Bar")); 79 | } 80 | } -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/camera/EditorCamera.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.camera; 2 | 3 | import com.badlogic.gdx.Gdx; 4 | import com.badlogic.gdx.graphics.Camera; 5 | import com.badlogic.gdx.graphics.OrthographicCamera; 6 | import com.badlogic.gdx.graphics.PerspectiveCamera; 7 | import com.badlogic.gdx.utils.viewport.ExtendViewport; 8 | import com.badlogic.gdx.utils.viewport.Viewport; 9 | 10 | public class EditorCamera { 11 | public static final EditorCamera main = new EditorCamera(); 12 | private Viewport viewport; 13 | 14 | private EditorCamera() { 15 | this.viewport = new ExtendViewport(48f, 27f); 16 | viewport.getCamera().far = 1000f; 17 | viewport.getCamera().near = 0.1f; 18 | viewport.apply(); 19 | 20 | viewport.getCamera().position.set(0f, 50f, 100f); 21 | viewport.getCamera().lookAt(0f, 0f, 0f); 22 | 23 | viewport.update(Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); 24 | viewport.getCamera().update(); 25 | } 26 | 27 | public Camera getCamera() { 28 | return viewport.getCamera(); 29 | } 30 | 31 | public void resize(int width, int height) { 32 | viewport.update(width, height); 33 | } 34 | 35 | public void setToPerspective() { 36 | final Camera old = viewport.getCamera(); 37 | PerspectiveCamera newCamera = new PerspectiveCamera(45f, Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); 38 | newCamera.near = 0.1f; 39 | newCamera.far = 1000f; 40 | newCamera.direction.set(old.direction); 41 | newCamera.position.set(old.position).add(old.direction.scl(40f)); 42 | newCamera.up.set(old.up); 43 | newCamera.update(); 44 | viewport.setCamera(newCamera); 45 | viewport.update(Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); 46 | } 47 | 48 | public void setToOrthogonal() { 49 | final Camera old = viewport.getCamera(); 50 | OrthographicCamera newCamera = new OrthographicCamera(48f, 27f); 51 | newCamera.near = 0.1f; 52 | newCamera.far = 1000f; 53 | newCamera.direction.set(old.direction); 54 | newCamera.position.set(old.position).add(old.direction.scl(-40f)); 55 | newCamera.up.set(old.up); 56 | newCamera.update(); 57 | viewport.setCamera(newCamera); 58 | viewport.update(Gdx.graphics.getWidth(), Gdx.graphics.getHeight()); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/toolbar/ToolbarPresenter.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.toolbar; 2 | 3 | import io.reactivex.Observable; 4 | import io.reactivex.subjects.PublishSubject; 5 | import me.manabreak.ratio.common.mvp.MvpPresenter; 6 | import me.manabreak.ratio.editor.EditorView; 7 | import me.manabreak.ratio.plugins.camera.CameraSnapMode; 8 | 9 | public class ToolbarPresenter extends MvpPresenter { 10 | 11 | private final PublishSubject toolSubject = PublishSubject.create(); 12 | private final PublishSubject cameraSnapSubject = PublishSubject.create(); 13 | private final PublishSubject cameraProjectionSubject = PublishSubject.create(); 14 | private final EditorView mainView; 15 | private Tool tool = Tool.NONE; 16 | 17 | public ToolbarPresenter(EditorView mainView) { 18 | this.mainView = mainView; 19 | } 20 | 21 | private void changeTool(Tool tool) { 22 | if (this.tool == tool) { 23 | this.tool = Tool.NONE; 24 | } else { 25 | this.tool = tool; 26 | } 27 | toolSubject.onNext(tool); 28 | } 29 | 30 | public void blockToolSelected() { 31 | changeTool(Tool.BLOCK); 32 | } 33 | 34 | public void floorToolSelected() { 35 | changeTool(Tool.FLOOR); 36 | } 37 | 38 | public void paintToolSelected() { 39 | changeTool(Tool.PAINT); 40 | } 41 | 42 | public void eraseToolSelected() { 43 | changeTool(Tool.ERASE); 44 | } 45 | 46 | public void selectToolSelected() { 47 | changeTool(Tool.SELECT); 48 | } 49 | 50 | public void createObjectClicked() { 51 | changeTool(Tool.CREATE); 52 | } 53 | 54 | public PublishSubject getToolSubject() { 55 | return toolSubject; 56 | } 57 | 58 | public void toggleLeftPanel() { 59 | mainView.toggleLeftPanel(); 60 | } 61 | 62 | public void toggleRightPanel() { 63 | mainView.toggleRightPanel(); 64 | } 65 | 66 | public void setCameraSnap(boolean checked) { 67 | cameraSnapSubject.onNext(checked ? CameraSnapMode.TOPDOWN : CameraSnapMode.NONE); 68 | } 69 | 70 | public Observable getCameraSnapObservable() { 71 | return cameraSnapSubject; 72 | } 73 | 74 | public void setCameraMode(boolean checked) { 75 | cameraProjectionSubject.onNext(checked); 76 | } 77 | 78 | public Observable getCameraProjectionObservable() { 79 | return cameraProjectionSubject; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/level/LayerListAdapter.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.level; 2 | 3 | import com.badlogic.gdx.scenes.scene2d.Actor; 4 | import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; 5 | import com.badlogic.gdx.scenes.scene2d.utils.Drawable; 6 | import com.badlogic.gdx.utils.Align; 7 | import com.kotcrab.vis.ui.VisUI; 8 | import com.kotcrab.vis.ui.util.adapter.ArrayListAdapter; 9 | import com.kotcrab.vis.ui.widget.VisTable; 10 | import com.kotcrab.vis.ui.widget.VisTextButton; 11 | import com.kotcrab.vis.ui.widget.VisTextField; 12 | import me.manabreak.ratio.ui.Res; 13 | 14 | import java.util.ArrayList; 15 | 16 | public class LayerListAdapter extends ArrayListAdapter { 17 | 18 | private final Drawable bg = VisUI.getSkin().getDrawable("window-bg"); 19 | private final Drawable selection = VisUI.getSkin().getDrawable("list-selection"); 20 | private final LayerPresenter presenter; 21 | 22 | public LayerListAdapter(LayerPresenter presenter, ArrayList array) { 23 | super(array); 24 | this.presenter = presenter; 25 | setSelectionMode(SelectionMode.SINGLE); 26 | } 27 | 28 | @Override 29 | protected VisTable createView(TileLayer item) { 30 | VisTable t = new VisTable(true); 31 | 32 | VisTextButton btnShow = new VisTextButton(item.isVisible() ? Res.ICON_EYE : Res.ICON_EYE_OFF, Res.ICONS_SMALL, new ChangeListener() { 33 | @Override 34 | public void changed(ChangeEvent event, Actor actor) { 35 | item.setVisible(!item.isVisible()); 36 | if (item.isVisible()) { 37 | ((VisTextButton) actor).setText(Res.ICON_EYE); 38 | } else { 39 | ((VisTextButton) actor).setText(Res.ICON_EYE_OFF); 40 | } 41 | } 42 | }); 43 | t.add(btnShow).padLeft(2f).minWidth(16f); 44 | 45 | VisTextField name = new VisTextField(item.getName()); 46 | name.setTextFieldListener((textField, c) -> { 47 | item.setName(textField.getText()); 48 | }); 49 | name.setEnterKeyFocusTraversal(true); 50 | name.setAlignment(Align.left); 51 | t.add(name).pad(2f).left(); 52 | t.add().grow(); 53 | 54 | return t; 55 | } 56 | 57 | @Override 58 | protected void selectView(VisTable view) { 59 | view.setBackground(selection); 60 | } 61 | 62 | @Override 63 | protected void deselectView(VisTable view) { 64 | view.setBackground(bg); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/level/LayerUi.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.level; 2 | 3 | import com.badlogic.gdx.scenes.scene2d.Actor; 4 | import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; 5 | import com.kotcrab.vis.ui.widget.ListView; 6 | import com.kotcrab.vis.ui.widget.VisTable; 7 | import com.kotcrab.vis.ui.widget.VisTextButton; 8 | import me.manabreak.ratio.common.mvp.MvpView; 9 | import me.manabreak.ratio.ui.Res; 10 | 11 | public class LayerUi extends MvpView { 12 | 13 | private VisTable content; 14 | private VisTable actions; 15 | 16 | protected LayerUi(LayerPresenter presenter) { 17 | super(presenter); 18 | 19 | actions = new VisTable(true); 20 | actions.add("Layers").left().padLeft(8f).expandX(); 21 | 22 | actions.defaults().right().width(32f).height(32f).pad(2f); 23 | VisTextButton btnNew = new VisTextButton(Res.ICON_ADD, Res.ICONS_SMALL, new ChangeListener() { 24 | @Override 25 | public void changed(ChangeEvent event, Actor actor) { 26 | presenter.createLayer(); 27 | } 28 | }); 29 | actions.add(btnNew); 30 | 31 | VisTextButton btnMoveUp = new VisTextButton(Res.ICON_UP, Res.ICONS_SMALL, new ChangeListener() { 32 | @Override 33 | public void changed(ChangeEvent event, Actor actor) { 34 | presenter.moveLayerUp(); 35 | } 36 | }); 37 | actions.add(btnMoveUp); 38 | 39 | VisTextButton btnMoveDown = new VisTextButton(Res.ICON_DOWN, Res.ICONS_SMALL, new ChangeListener() { 40 | @Override 41 | public void changed(ChangeEvent event, Actor actor) { 42 | presenter.moveLayerDown(); 43 | } 44 | }); 45 | actions.add(btnMoveDown); 46 | 47 | VisTextButton btnDelete = new VisTextButton(Res.ICON_DELETE, Res.ICONS_SMALL, new ChangeListener() { 48 | @Override 49 | public void changed(ChangeEvent event, Actor actor) { 50 | presenter.deleteLayer(); 51 | } 52 | }); 53 | actions.add(btnDelete); 54 | 55 | add(actions).growX(); 56 | 57 | row(); 58 | 59 | content = new VisTable(true); 60 | add(content).grow().maxHeight(300f); 61 | 62 | 63 | presenter.viewCreated(); 64 | } 65 | 66 | public void createListView(LayerListAdapter adapter) { 67 | ListView listView = new ListView<>(adapter); 68 | content.add(listView.getMainTable()).grow().top().pad(2f); 69 | listView.setItemClickListener(presenter::layerClicked); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/properties/PropertyPrompt.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.properties; 2 | 3 | import com.badlogic.gdx.scenes.scene2d.Actor; 4 | import com.badlogic.gdx.scenes.scene2d.ui.Table; 5 | import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; 6 | import com.kotcrab.vis.ui.widget.MenuItem; 7 | import com.kotcrab.vis.ui.widget.PopupMenu; 8 | import me.manabreak.ratio.ui.UiUtils; 9 | 10 | public class PropertyPrompt extends ChangeListener { 11 | private final Table parent; 12 | private final PropertyHandler propertyHandler; 13 | 14 | public PropertyPrompt(Table parent, PropertyHandler propertyHandler) { 15 | this.parent = parent; 16 | this.propertyHandler = propertyHandler; 17 | } 18 | 19 | @Override 20 | public void changed(ChangeEvent event, Actor actor) { 21 | PopupMenu menu = new PopupMenu(); 22 | MenuItem booleanItem = new MenuItem("Boolean", new ChangeListener() { 23 | @Override 24 | public void changed(ChangeEvent event, Actor actor) { 25 | UiUtils.promptProperty(parent.getStage(), "Boolean", propertyHandler::createBooleanProperty); 26 | } 27 | }); 28 | menu.addItem(booleanItem); 29 | 30 | MenuItem stringItem = new MenuItem("String", new ChangeListener() { 31 | @Override 32 | public void changed(ChangeEvent event, Actor actor) { 33 | UiUtils.promptProperty(parent.getStage(), "String", propertyHandler::createStringProperty); 34 | } 35 | }); 36 | menu.addItem(stringItem); 37 | 38 | MenuItem intItem = new MenuItem("Integer", new ChangeListener() { 39 | @Override 40 | public void changed(ChangeEvent event, Actor actor) { 41 | UiUtils.promptProperty(parent.getStage(), "Integer", propertyHandler::createIntProperty); 42 | } 43 | }); 44 | menu.addItem(intItem); 45 | 46 | MenuItem doubleItem = new MenuItem("Double", new ChangeListener() { 47 | @Override 48 | public void changed(ChangeEvent event, Actor actor) { 49 | UiUtils.promptProperty(parent.getStage(), "Double", propertyHandler::createDoubleProperty); 50 | } 51 | }); 52 | menu.addItem(doubleItem); 53 | 54 | MenuItem vec3Item = new MenuItem("Vector3", new ChangeListener() { 55 | @Override 56 | public void changed(ChangeEvent event, Actor actor) { 57 | UiUtils.promptProperty(parent.getStage(), "Vector3", propertyHandler::createVec3Property); 58 | } 59 | }); 60 | menu.addItem(vec3Item); 61 | 62 | menu.showMenu(parent.getStage(), parent); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/EditorGame.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio; 2 | 3 | import com.badlogic.gdx.Game; 4 | import com.badlogic.gdx.Screen; 5 | import com.badlogic.gdx.graphics.g2d.BitmapFont; 6 | import com.badlogic.gdx.graphics.g2d.SpriteBatch; 7 | import com.badlogic.gdx.graphics.glutils.ShapeRenderer; 8 | import com.kotcrab.vis.ui.widget.file.FileChooser; 9 | import me.manabreak.ratio.editor.EditorGameScreen; 10 | import me.manabreak.ratio.editor.PluginManager; 11 | import me.manabreak.ratio.plugins.camera.EditorCameraPlugin; 12 | import me.manabreak.ratio.plugins.exporters.ExportManager; 13 | import me.manabreak.ratio.plugins.exporters.GdxJsonExporter; 14 | import me.manabreak.ratio.plugins.importers.GdxJsonImporter; 15 | import me.manabreak.ratio.plugins.importers.ImportManager; 16 | import me.manabreak.ratio.plugins.level.*; 17 | import me.manabreak.ratio.plugins.objects.ObjectEditorPlugin; 18 | import me.manabreak.ratio.plugins.properties.LevelPropertiesPlugin; 19 | import me.manabreak.ratio.plugins.scene.EditorGrid; 20 | import me.manabreak.ratio.plugins.tilesets.TilesetManager; 21 | import me.manabreak.ratio.plugins.tilesets.TilesetPlugin; 22 | import me.manabreak.ratio.plugins.toolbar.ToolbarPlugin; 23 | import me.manabreak.ratio.ui.Res; 24 | 25 | public class EditorGame extends Game { 26 | 27 | private Screen gameScreen; 28 | private TilesetManager tilesetManager; 29 | 30 | @Override 31 | public void create() { 32 | Res.load(); 33 | FileChooser.setDefaultPrefsName("me.manabreak.ratio.filechooser"); 34 | FileChooser.setSaveLastDirectory(true); 35 | tilesetManager = new TilesetManager(); 36 | gameScreen = createGameScreen(); 37 | setScreen(gameScreen); 38 | } 39 | 40 | private Screen createGameScreen() { 41 | PluginManager pluginManager = new PluginManager(); 42 | pluginManager.register( 43 | new ToolbarPlugin(), 44 | new EditorCameraPlugin(), 45 | new EditorGrid(), 46 | new LevelEditorPlugin(new LevelShader(), new WireframeRenderer(), new ObjectRenderer(new ShapeRenderer(), new SpriteBatch(), new BitmapFont()), new ToolRenderer()), 47 | new ObjectEditorPlugin(), 48 | new LevelPropertiesPlugin(), 49 | new TilesetPlugin() 50 | ); 51 | 52 | ExportManager exportManager = new ExportManager(); 53 | exportManager.register( 54 | new GdxJsonExporter() 55 | ); 56 | 57 | ImportManager importManager = new ImportManager(); 58 | importManager.register( 59 | new GdxJsonImporter(tilesetManager) 60 | ); 61 | 62 | return new EditorGameScreen(pluginManager, exportManager, importManager, tilesetManager); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/scene/EditorGrid.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.scene; 2 | 3 | import com.badlogic.gdx.Gdx; 4 | import com.badlogic.gdx.Input; 5 | import com.badlogic.gdx.graphics.Camera; 6 | import com.badlogic.gdx.graphics.GL20; 7 | import com.badlogic.gdx.graphics.glutils.ShapeRenderer; 8 | import me.manabreak.ratio.plugins.camera.EditorCamera; 9 | import me.manabreak.ratio.editor.EditorPlugin; 10 | import me.manabreak.ratio.editor.LoopListener; 11 | 12 | public class EditorGrid extends EditorPlugin implements LoopListener { 13 | 14 | private static final int GRID_DISTANCE = 100; 15 | private static final float MAIN_LINE_ALPHA = 0.5f; 16 | private static final float Y = 1f; 17 | private static final float UNIT_SIZE = 16f; 18 | private ShapeRenderer renderer; 19 | private boolean enabled = true; 20 | private float y = Y; 21 | 22 | public EditorGrid() { 23 | renderer = new ShapeRenderer(); 24 | renderer.setAutoShapeType(true); 25 | } 26 | 27 | @Override 28 | public void initialize() { 29 | editorController.addLoopListener(this); 30 | editorController.registerInputProcessor(this); 31 | } 32 | 33 | @Override 34 | public boolean keyUp(int keycode) { 35 | if (keycode == Input.Keys.G) { 36 | enabled = !enabled; 37 | return true; 38 | } 39 | return super.keyUp(keycode); 40 | } 41 | 42 | private void render() { 43 | if (!enabled) return; 44 | 45 | Camera camera = EditorCamera.main.getCamera(); 46 | Gdx.gl20.glEnable(GL20.GL_BLEND); 47 | Gdx.gl20.glEnable(GL20.GL_DEPTH_TEST); 48 | Gdx.gl20.glDepthFunc(GL20.GL_LEQUAL); 49 | renderer.begin(ShapeRenderer.ShapeType.Line); 50 | renderer.setProjectionMatrix(camera.combined); 51 | 52 | int x = 0; 53 | int z = 0; 54 | 55 | for (int i = 0; i < GRID_DISTANCE; ++i) { 56 | float a = 1f - Math.abs((float) i / ((float) GRID_DISTANCE)); 57 | renderer.setColor(1f, 1f, 1f, MAIN_LINE_ALPHA * a); 58 | renderer.line(x + i, y, 0f, x + i, y, GRID_DISTANCE); 59 | renderer.line(0f, y, z + i, GRID_DISTANCE, y, z + i); 60 | } 61 | 62 | renderer.set(ShapeRenderer.ShapeType.Filled); 63 | renderer.setColor(1f, 1f, 1f, 0.07f); 64 | renderer.box(0f, y, 0f, GRID_DISTANCE, -0.1f, -GRID_DISTANCE); 65 | renderer.end(); 66 | 67 | Gdx.gl20.glClear(GL20.GL_DEPTH_BUFFER_BIT); 68 | } 69 | 70 | @Override 71 | public void onUpdate(float dt) { 72 | render(); 73 | } 74 | 75 | public void raiseBy(float amount) { 76 | y += amount; 77 | y = Math.round(y * UNIT_SIZE) / UNIT_SIZE - 0.001f; 78 | } 79 | 80 | public void lowerBy(float amount) { 81 | y -= amount; 82 | y = Math.round(y * UNIT_SIZE) / UNIT_SIZE - 0.001f; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/objects/ObjectEditorPlugin.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.objects; 2 | 3 | import com.badlogic.gdx.graphics.Camera; 4 | import com.badlogic.gdx.math.Intersector; 5 | import com.badlogic.gdx.math.Vector3; 6 | import com.badlogic.gdx.math.collision.BoundingBox; 7 | import com.badlogic.gdx.math.collision.Ray; 8 | import com.kotcrab.vis.ui.widget.VisTable; 9 | import me.manabreak.ratio.editor.EditorPlugin; 10 | import me.manabreak.ratio.plugins.camera.EditorCamera; 11 | import me.manabreak.ratio.plugins.level.Coord; 12 | 13 | import java.util.List; 14 | 15 | public class ObjectEditorPlugin extends EditorPlugin { 16 | 17 | private ObjectEditorUi ui; 18 | private Coord startCoord = null; 19 | private Coord endCoord = null; 20 | 21 | @Override 22 | public void initialize() { 23 | VisTable table = new VisTable(true); 24 | ui = new ObjectEditorUi(new ObjectEditorPresenter()); 25 | table.add(ui).grow(); 26 | editorView.addLeftPanelTab("Objects", table); 27 | editorController.registerInputProcessor(this); 28 | } 29 | 30 | public ObjectEditorPresenter getPresenter() { 31 | return ui.getPresenter(); 32 | } 33 | 34 | public void loadObjects(List objects) { 35 | ui.getPresenter().loadObjects(objects); 36 | } 37 | 38 | public void selectAt(int x, int y) { 39 | Camera cam = EditorCamera.main.getCamera(); 40 | final Ray ray = cam.getPickRay(x, y); 41 | Vector3 intersection = new Vector3(); 42 | 43 | System.out.println("Clicking..."); 44 | for (GameObject obj : getPresenter().getObjects()) { 45 | BoundingBox bb = new BoundingBox( 46 | new Vector3(obj.x, obj.y, obj.z), 47 | new Vector3(obj.x + obj.sizeX, obj.y + obj.sizeY, obj.z + obj.sizeZ) 48 | ); 49 | if (Intersector.intersectRayBounds(ray, bb, intersection)) { 50 | System.out.println("Clicked object " + obj.getName()); 51 | getPresenter().setSelection(obj); 52 | return; 53 | } 54 | } 55 | 56 | getPresenter().setSelection(null); 57 | } 58 | 59 | @Override 60 | public boolean keyUp(int keycode) { 61 | ui.getPresenter().onKeyEvent(keycode); 62 | return super.keyUp(keycode); 63 | } 64 | 65 | public void createAt(int createClicks, Coord coord) { 66 | switch (createClicks) { 67 | case 1: 68 | System.out.println("Click 0 at " + coord); 69 | startCoord = new Coord(coord.x, coord.y, coord.z); 70 | System.out.println("Set start coord at " + startCoord); 71 | break; 72 | case 2: 73 | System.out.println("Click 1 at " + coord); 74 | endCoord = new Coord(coord.x, coord.y, coord.z); 75 | System.out.println("Set end coord at " + endCoord); 76 | break; 77 | case 3: 78 | getPresenter().createObject(startCoord, coord); 79 | break; 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/level/LayerPresenter.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.level; 2 | 3 | import com.badlogic.gdx.utils.Array; 4 | import io.reactivex.subjects.PublishSubject; 5 | import me.manabreak.ratio.common.mvp.MvpPresenter; 6 | 7 | public class LayerPresenter extends MvpPresenter { 8 | 9 | private final PublishSubject layerCreatedSubject = PublishSubject.create(); 10 | private final PublishSubject layerSelectedSubject = PublishSubject.create(); 11 | private LayerListAdapter adapter; 12 | private Level level; 13 | private TileLayer selectedLayer; 14 | 15 | public LayerPresenter(Level level) { 16 | this.level = level; 17 | this.adapter = new LayerListAdapter(this, level.getLayers()); 18 | } 19 | 20 | public void viewCreated() { 21 | view.createListView(adapter); 22 | } 23 | 24 | public void createLayer() { 25 | TileLayer layer = this.level.createLayer("Layer"); 26 | adapter.itemsChanged(); 27 | adapter.getSelectionManager().select(layer); 28 | layerCreatedSubject.onNext(layer); 29 | layerClicked(layer); 30 | } 31 | 32 | public void layerClicked(TileLayer layer) { 33 | System.out.println("Clicked layer " + layer.getName()); 34 | this.selectedLayer = layer; 35 | layerSelectedSubject.onNext(layer); 36 | } 37 | 38 | public void moveLayerUp() { 39 | final Array selection = adapter.getSelection(); 40 | if (selection.size == 0) return; 41 | 42 | final TileLayer layer = selection.get(0); 43 | int i = level.getLayers().indexOf(layer); 44 | if (i == 0) return; 45 | level.deleteLayer(layer); 46 | i--; 47 | level.addLayer(i, layer); 48 | adapter.itemsChanged(); 49 | adapter.getSelectionManager().select(layer); 50 | } 51 | 52 | public void moveLayerDown() { 53 | final Array selection = adapter.getSelection(); 54 | if (selection.size == 0) return; 55 | 56 | final TileLayer layer = selection.get(0); 57 | int i = level.getLayers().indexOf(layer); 58 | if (i == level.getLayers().size() - 1) return; 59 | level.deleteLayer(layer); 60 | i++; 61 | level.addLayer(i, layer); 62 | adapter.itemsChanged(); 63 | adapter.getSelectionManager().select(layer); 64 | } 65 | 66 | public void deleteLayer() { 67 | final Array selection = adapter.getSelection(); 68 | if (selection.size == 0) return; 69 | 70 | final TileLayer layer = selection.get(0); 71 | final int i = level.getLayers().indexOf(layer); 72 | level.deleteLayer(layer); 73 | adapter.itemsChanged(); 74 | 75 | if (level.getLayers().size() > 0) { 76 | adapter.getSelectionManager().select(level.getLayers().get(Math.max(Math.min(i, level.getLayers().size() - 1), 0))); 77 | } 78 | } 79 | 80 | public TileLayer getSelectedLayer() { 81 | return selectedLayer; 82 | } 83 | 84 | public void levelLoaded(Level level) { 85 | this.level = level; 86 | this.adapter = new LayerListAdapter(this, level.getLayers()); 87 | view.createListView(adapter); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/editor/ShortcutProcessor.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.editor; 2 | 3 | import com.badlogic.gdx.InputAdapter; 4 | import me.manabreak.ratio.plugins.level.LevelEditorPlugin; 5 | import me.manabreak.ratio.plugins.toolbar.ToolbarPlugin; 6 | 7 | import static com.badlogic.gdx.Input.Keys.*; 8 | 9 | public class ShortcutProcessor extends InputAdapter { 10 | private final EditorController controller; 11 | private final EditorStage stage; 12 | 13 | private boolean controlDown; 14 | private boolean shiftDown; 15 | 16 | public ShortcutProcessor(EditorController controller, EditorStage stage) { 17 | this.controller = controller; 18 | this.stage = stage; 19 | } 20 | 21 | @Override 22 | public boolean keyDown(int keycode) { 23 | if (stage.getKeyboardFocus() != null) return false; 24 | 25 | switch (keycode) { 26 | case CONTROL_LEFT: 27 | case CONTROL_RIGHT: 28 | controlDown = true; 29 | break; 30 | case SHIFT_LEFT: 31 | case SHIFT_RIGHT: 32 | shiftDown = true; 33 | controller.getPlugin(LevelEditorPlugin.class).engageLineTool(); 34 | break; 35 | case B: 36 | controller.getPlugin(ToolbarPlugin.class).getUi().onPaintToolClicked(); 37 | break; 38 | case D: 39 | controller.getPlugin(ToolbarPlugin.class).getUi().onBlockToolClicked(); 40 | break; 41 | case E: 42 | controller.getPlugin(ToolbarPlugin.class).getUi().onEraseToolClicked(); 43 | break; 44 | case F: 45 | controller.getPlugin(ToolbarPlugin.class).getUi().onFloorToolClicked(); 46 | break; 47 | case H: 48 | controller.getPlugin(LevelEditorPlugin.class).toggleLayerHighlighting(); 49 | break; 50 | case O: 51 | if (controlDown) { 52 | controller.onOpenClicked(); 53 | } else { 54 | controller.getPlugin(LevelEditorPlugin.class).getObjectRenderer().rotateDrawMode(); 55 | } 56 | break; 57 | case P: 58 | if (!controlDown) { 59 | controller.getPlugin(LevelEditorPlugin.class).getObjectRenderer().toggleSeeThrough(); 60 | } 61 | break; 62 | case S: 63 | if (controlDown) { 64 | if (shiftDown) controller.onSaveAsClicked(); 65 | else controller.onSaveClicked(); 66 | } else { 67 | controller.getPlugin(ToolbarPlugin.class).getUi().onSelectToolClicked(); 68 | } 69 | break; 70 | } 71 | 72 | return false; 73 | } 74 | 75 | @Override 76 | public boolean keyUp(int keycode) { 77 | switch (keycode) { 78 | case CONTROL_LEFT: 79 | case CONTROL_RIGHT: 80 | controlDown = false; 81 | break; 82 | case SHIFT_LEFT: 83 | case SHIFT_RIGHT: 84 | shiftDown = false; 85 | controller.getPlugin(LevelEditorPlugin.class).unengageLineTool(); 86 | } 87 | 88 | return false; 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/tilesets/TilesetPresenter.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.tilesets; 2 | 3 | import com.badlogic.gdx.files.FileHandle; 4 | import com.badlogic.gdx.graphics.Color; 5 | import com.badlogic.gdx.graphics.g2d.TextureRegion; 6 | import io.reactivex.subjects.PublishSubject; 7 | import me.manabreak.ratio.common.mvp.MvpPresenter; 8 | 9 | import java.util.Collection; 10 | 11 | public class TilesetPresenter extends MvpPresenter { 12 | 13 | private final TilesetPlugin plugin; 14 | private final TilesetManager manager; 15 | private final PublishSubject automapSubject = PublishSubject.create(); 16 | private final PublishSubject regionSubject = PublishSubject.create(); 17 | 18 | private int toolSize = 16; 19 | private int tileWidth = 16, tileHeight = 16; 20 | private TextureRegion currentRegion; 21 | 22 | public TilesetPresenter(TilesetPlugin plugin, TilesetManager manager) { 23 | this.plugin = plugin; 24 | this.manager = manager; 25 | } 26 | 27 | 28 | public void loadTileset(FileHandle file) { 29 | Tileset tileset = manager.load(file); 30 | view.createTab(tileset); 31 | boolean palette = tileset instanceof PaletteTileset; 32 | view.createPicker(tileset, palette ? 1 : tileWidth, palette ? 1 : tileHeight); 33 | } 34 | 35 | public void createPalette() { 36 | view.promptPaletteName(); 37 | } 38 | 39 | public void createPalette(String name) { 40 | PaletteTileset tileset = manager.createPalette(name); 41 | tileset.draw(0, 0, Color.RED); 42 | tileset.draw(0, 1, Color.GREEN); 43 | tileset.draw(1, 0, Color.BLUE); 44 | tileset.draw(1, 1, Color.YELLOW); 45 | view.createTab(tileset); 46 | view.createPicker(tileset, 1, 1); 47 | } 48 | 49 | public void selectTileset(Tileset tileset) { 50 | manager.setCurrentTileset(tileset); 51 | } 52 | 53 | public void tileWidthChanged(int width) { 54 | this.tileWidth = width; 55 | boolean palette = manager.getCurrentTileset() instanceof PaletteTileset; 56 | view.createPicker(manager.getCurrentTileset(), palette ? 1 : tileWidth, palette ? 1 : tileHeight); 57 | } 58 | 59 | public void tileHeightChanged(int height) { 60 | this.tileHeight = height; 61 | boolean palette = manager.getCurrentTileset() instanceof PaletteTileset; 62 | view.createPicker(manager.getCurrentTileset(), palette ? 1 : tileWidth, palette ? 1 : tileHeight); 63 | } 64 | 65 | public void automapChanged(boolean checked) { 66 | automapSubject.onNext(checked); 67 | } 68 | 69 | public void selectRegion(TextureRegion region) { 70 | this.currentRegion = region; 71 | regionSubject.onNext(region); 72 | } 73 | 74 | public PublishSubject getAutomapSubject() { 75 | return automapSubject; 76 | } 77 | 78 | public PublishSubject getRegionSubject() { 79 | return regionSubject; 80 | } 81 | 82 | public void levelLoaded() { 83 | Collection tilesets = manager.getTilesets(); 84 | for (Tileset tileset : tilesets) { 85 | view.createTab(tileset); 86 | boolean palette = tileset instanceof PaletteTileset; 87 | view.createPicker(tileset, palette ? 1 : tileWidth, palette ? 1 : tileHeight); 88 | } 89 | } 90 | 91 | public TextureRegion getCurrentRegion() { 92 | return this.currentRegion; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/objects/GameObject.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.objects; 2 | 3 | import com.badlogic.gdx.graphics.Color; 4 | import me.manabreak.ratio.common.Properties; 5 | 6 | public class GameObject { 7 | 8 | private final Properties properties = new Properties(); 9 | 10 | public float x, y = 1f, z; 11 | public float sizeX = 1f, sizeY = 1f, sizeZ = 1f; 12 | public Color color = new Color(1f, 1f, 1f, 1f); 13 | public boolean visible = true; 14 | private String name = ""; 15 | private String type = ""; 16 | private boolean selected = false; 17 | 18 | public GameObject() { 19 | 20 | } 21 | 22 | public void setProperty(String key, Object value) { 23 | properties.setProperty(key, value); 24 | } 25 | 26 | public T getProperty(String key, T defaultValue) { 27 | //noinspection unchecked 28 | return properties.getProperty(key, defaultValue); 29 | } 30 | 31 | public T removeProperty(String key) { 32 | //noinspection unchecked 33 | return properties.removeProperty(key); 34 | } 35 | 36 | public Properties getProperties() { 37 | return properties; 38 | } 39 | 40 | public void setPosition(float x, float y, float z) { 41 | this.x = x; 42 | this.y = y; 43 | this.z = z; 44 | } 45 | 46 | public String getName() { 47 | return name; 48 | } 49 | 50 | public void setName(String name) { 51 | this.name = name; 52 | if (this.name == null) this.name = ""; 53 | } 54 | 55 | public float getX() { 56 | return x; 57 | } 58 | 59 | public void setX(float x) { 60 | this.x = x; 61 | } 62 | 63 | public float getY() { 64 | return y; 65 | } 66 | 67 | public void setY(float y) { 68 | this.y = y; 69 | } 70 | 71 | public float getZ() { 72 | return z; 73 | } 74 | 75 | public void setZ(float z) { 76 | this.z = z; 77 | } 78 | 79 | public float getSizeX() { 80 | return sizeX; 81 | } 82 | 83 | public void setSizeX(float sizeX) { 84 | this.sizeX = sizeX; 85 | } 86 | 87 | public float getSizeY() { 88 | return sizeY; 89 | } 90 | 91 | public void setSizeY(float sizeY) { 92 | this.sizeY = sizeY; 93 | } 94 | 95 | public float getSizeZ() { 96 | return sizeZ; 97 | } 98 | 99 | public void setSizeZ(float sizeZ) { 100 | this.sizeZ = sizeZ; 101 | } 102 | 103 | public Color getColor() { 104 | return color; 105 | } 106 | 107 | public void setColor(Color color) { 108 | this.color = color; 109 | } 110 | 111 | public boolean isVisible() { 112 | return visible; 113 | } 114 | 115 | public void setVisible(boolean visible) { 116 | this.visible = visible; 117 | } 118 | 119 | public void setSize(float sx, float sy, float sz) { 120 | this.sizeX = sx; 121 | this.sizeY = sy; 122 | this.sizeZ = sz; 123 | } 124 | 125 | public String getType() { 126 | return type; 127 | } 128 | 129 | public void setType(String type) { 130 | this.type = type; 131 | if (this.type == null) this.type = ""; 132 | } 133 | 134 | public boolean isSelected() { 135 | return selected; 136 | } 137 | 138 | public void setSelected(boolean selected) { 139 | this.selected = selected; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/tilesets/TilesetLoaderDialog.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.tilesets; 2 | 3 | import com.badlogic.gdx.Gdx; 4 | import com.badlogic.gdx.files.FileHandle; 5 | import com.badlogic.gdx.scenes.scene2d.Actor; 6 | import com.badlogic.gdx.scenes.scene2d.Stage; 7 | import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; 8 | import com.badlogic.gdx.utils.Array; 9 | import com.kotcrab.vis.ui.widget.VisTable; 10 | import com.kotcrab.vis.ui.widget.VisTextButton; 11 | import com.kotcrab.vis.ui.widget.VisTextField; 12 | import com.kotcrab.vis.ui.widget.VisWindow; 13 | import com.kotcrab.vis.ui.widget.file.FileChooser; 14 | import com.kotcrab.vis.ui.widget.file.FileChooserAdapter; 15 | import com.kotcrab.vis.ui.widget.file.FileTypeFilter; 16 | import me.manabreak.ratio.utils.Action1; 17 | 18 | public class TilesetLoaderDialog { 19 | 20 | public TilesetLoaderDialog(Stage stage, Action1 callback) { 21 | VisWindow root = new VisWindow("New Tileset", true); 22 | root.setKeepWithinParent(true); 23 | root.addCloseButton(); 24 | root.setSize(500f, 300f); 25 | root.setMovable(true); 26 | root.centerWindow(); 27 | root.setModal(true); 28 | root.closeOnEscape(); 29 | stage.addActor(root); 30 | 31 | VisTable pathTable = new VisTable(true); 32 | 33 | VisTextField tfPath = new VisTextField(""); 34 | tfPath.setTextFieldListener((tf, c) -> { 35 | FileHandle fh = Gdx.files.internal(tf.getText()); 36 | tfPath.setInputValid(fh.exists() && !fh.isDirectory()); 37 | }); 38 | pathTable.add(tfPath).pad(4f).growX(); 39 | 40 | VisTextButton btnBrowse = new VisTextButton("Browse", new ChangeListener() { 41 | @Override 42 | public void changed(ChangeEvent event, Actor actor) { 43 | FileChooser fileChooser = new FileChooser(FileChooser.Mode.OPEN); 44 | fileChooser.setMultiSelectionEnabled(false); 45 | fileChooser.setSelectionMode(FileChooser.SelectionMode.FILES); 46 | FileTypeFilter filter = new FileTypeFilter(false); 47 | filter.addRule("PNG", "png"); 48 | fileChooser.setFileTypeFilter(filter); 49 | fileChooser.setDirectory(Gdx.files.internal("assets/tilesets")); 50 | 51 | fileChooser.setListener(new FileChooserAdapter() { 52 | @Override 53 | public void selected(Array files) { 54 | tfPath.setText(files.get(0).path()); 55 | } 56 | }); 57 | 58 | stage.addActor(fileChooser); 59 | } 60 | }); 61 | pathTable.add(btnBrowse).pad(4f); 62 | root.add(pathTable).growX().row(); 63 | 64 | VisTable mainButtonTable = new VisTable(true); 65 | VisTextButton btnCancel = new VisTextButton("Cancel", new ChangeListener() { 66 | @Override 67 | public void changed(ChangeEvent event, Actor actor) { 68 | root.remove(); 69 | } 70 | }); 71 | mainButtonTable.add(btnCancel).pad(4f); 72 | 73 | VisTextButton btnOk = new VisTextButton("OK", new ChangeListener() { 74 | @Override 75 | public void changed(ChangeEvent event, Actor actor) { 76 | callback.call(Gdx.files.absolute(tfPath.getText())); 77 | root.remove(); 78 | } 79 | }); 80 | mainButtonTable.add(btnOk).pad(4f); 81 | root.add(mainButtonTable).grow(); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/properties/PropertyAdapter.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.properties; 2 | 3 | import com.badlogic.gdx.scenes.scene2d.Actor; 4 | import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; 5 | import com.kotcrab.vis.ui.util.adapter.ArrayListAdapter; 6 | import com.kotcrab.vis.ui.widget.VisCheckBox; 7 | import com.kotcrab.vis.ui.widget.VisTable; 8 | import com.kotcrab.vis.ui.widget.VisTextButton; 9 | import com.kotcrab.vis.ui.widget.VisTextField; 10 | import me.manabreak.ratio.common.Properties; 11 | import me.manabreak.ratio.ui.IntegerTextFieldFilter; 12 | import me.manabreak.ratio.ui.NumericTextFieldFilter; 13 | import me.manabreak.ratio.ui.Res; 14 | import me.manabreak.ratio.utils.Action1; 15 | 16 | public class PropertyAdapter extends ArrayListAdapter { 17 | 18 | private Action1 deleteCallback; 19 | 20 | public PropertyAdapter(Properties properties) { 21 | super(properties.getProperties()); 22 | } 23 | 24 | @Override 25 | protected VisTable createView(Properties.Entry item) { 26 | VisTable t = new VisTable(); 27 | 28 | final VisTextButton btnDelete = new VisTextButton(Res.ICON_DELETE, Res.ICONS_SMALL, new ChangeListener() { 29 | @Override 30 | public void changed(ChangeEvent event, Actor actor) { 31 | if (deleteCallback != null) deleteCallback.call(item); 32 | } 33 | }); 34 | t.add(btnDelete).size(20f).padRight(2f); 35 | 36 | t.add(item.getKey()).width(140f); 37 | 38 | if (item.getValue() instanceof Boolean) { 39 | VisCheckBox cb = new VisCheckBox("", (Boolean) item.getValue()); 40 | cb.addListener(new ChangeListener() { 41 | @Override 42 | public void changed(ChangeEvent event, Actor actor) { 43 | item.setValue(cb.isChecked()); 44 | } 45 | }); 46 | t.add(cb).growX(); 47 | } else if (item.getValue() instanceof String) { 48 | VisTextField tf = new VisTextField((String) item.getValue()); 49 | tf.setTextFieldListener((textField, c) -> item.setValue(textField.getText())); 50 | t.add(tf).growX(); 51 | } else if (item.getValue() instanceof Double) { 52 | VisTextField tf = new VisTextField("" + item.getValue()); 53 | tf.setTextFieldFilter(new NumericTextFieldFilter()); 54 | tf.setTextFieldListener(((textField, c) -> { 55 | final String text = textField.getText(); 56 | try { 57 | final double v = Double.parseDouble(text); 58 | item.setValue(v); 59 | } catch (NumberFormatException ignored) { 60 | 61 | } 62 | })); 63 | t.add(tf).growX(); 64 | } else if (item.getValue() instanceof Integer) { 65 | VisTextField tf = new VisTextField("" + item.getValue()); 66 | tf.setTextFieldFilter(new IntegerTextFieldFilter()); 67 | tf.setTextFieldListener(((textField, c) -> { 68 | final String text = textField.getText(); 69 | try { 70 | final int v = Integer.parseInt(text); 71 | item.setValue(v); 72 | } catch (NumberFormatException ignored) { 73 | 74 | } 75 | })); 76 | t.add(tf).growX(); 77 | } 78 | return t; 79 | } 80 | 81 | public void setDeleteCallback(Action1 deleteCallback) { 82 | this.deleteCallback = deleteCallback; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/level/Tile.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.level; 2 | 3 | import com.badlogic.gdx.graphics.Texture; 4 | import com.badlogic.gdx.graphics.g2d.TextureRegion; 5 | 6 | public class Tile { 7 | 8 | private final int id; 9 | private final TextureRegion region; 10 | private final int x, y, width, height; 11 | private Form form; 12 | 13 | public Tile(int id, TextureRegion region) { 14 | this.id = id; 15 | this.region = region; 16 | this.form = Form.OTHER; 17 | this.x = region.getRegionX(); 18 | this.y = region.getRegionY(); 19 | this.width = region.getRegionWidth(); 20 | this.height = region.getRegionHeight(); 21 | } 22 | 23 | public static Tile from(Texture texture, int id, int x, int y, int width, int height) { 24 | return new Tile(id, new TextureRegion(texture, x, y, width, height)); 25 | } 26 | 27 | public int getId() { 28 | return id; 29 | } 30 | 31 | public TextureRegion getRegion() { 32 | return region; 33 | } 34 | 35 | public Form getForm() { 36 | return form; 37 | } 38 | 39 | public void setForm(Form form) { 40 | this.form = form; 41 | } 42 | 43 | @Override 44 | public boolean equals(Object o) { 45 | if (this == o) return true; 46 | if (o == null || getClass() != o.getClass()) return false; 47 | 48 | Tile tile = (Tile) o; 49 | 50 | if (x != tile.x) return false; 51 | if (y != tile.y) return false; 52 | if (width != tile.width) return false; 53 | return height == tile.height; 54 | } 55 | 56 | @Override 57 | public int hashCode() { 58 | int result = x; 59 | result = 31 * result + y; 60 | result = 31 * result + width; 61 | result = 31 * result + height; 62 | return result; 63 | } 64 | 65 | public int getX() { 66 | return x; 67 | } 68 | 69 | public int getY() { 70 | return y; 71 | } 72 | 73 | public int getWidth() { 74 | return width; 75 | } 76 | 77 | public int getHeight() { 78 | return height; 79 | } 80 | 81 | // TODO Refactor and remove Form enum; make it possible to create new forms during runtime 82 | public enum Form { 83 | TOP_SINGLE("Top / Single"), 84 | TOP_HORIZONTAL_END_WEST("Top / West End"), 85 | TOP_VERTICAL_END_SOUTH("Top / South End"), 86 | TOP_CORNER_SW("Top / Corner SW"), 87 | TOP_HORIZONTAL_END_EAST("Top / East End"), 88 | TOP_HORIZONTAL("Top / Horizontal"), 89 | TOP_CORNER_SE("Top / Corner SE"), 90 | TOP_TRI_N("Top / T-Junction North"), // Tri-connector "pointing" north 91 | TOP_VERTICAL_END_NORTH("Top / North End"), 92 | TOP_CORNER_NW("Top / Corner NW"), 93 | TOP_VERTICAL("Top / Vertical"), 94 | TOP_TRI_E("Top / T-Junction East"), 95 | TOP_CORNER_NE("Top / Corner NE"), 96 | TOP_TRI_S("Top / T-Junction South"), 97 | TOP_TRI_W("Top / T-Junction West"), 98 | TOP_CROSS("Top / Cross"), // Four-way crossing 99 | 100 | SIDE_SINGLE("Side / Single"), 101 | SIDE_END_WEST("Side / West End"), 102 | SIDE_END_EAST("Side / East End"), 103 | SIDE("Side"), 104 | 105 | TOP_FILL("Top / Fill"), // Fill; when forming bigger top areas or floors 106 | OTHER("Other"); 107 | 108 | private final String text; 109 | 110 | Form(String text) { 111 | this.text = text; 112 | } 113 | 114 | public String getText() { 115 | return text; 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/level/TileLayer.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.level; 2 | 3 | import me.manabreak.ratio.plugins.tilesets.Tileset; 4 | 5 | import java.util.LinkedHashMap; 6 | import java.util.Map; 7 | 8 | public class TileLayer { 9 | private final Map> parts = new LinkedHashMap<>(); 10 | private String name; 11 | private boolean visible = true; 12 | 13 | public TileLayer(String name) { 14 | this.name = name; 15 | } 16 | 17 | private Octree getPart(Tileset tileset) { 18 | if (parts.containsKey(tileset)) { 19 | return parts.get(tileset); 20 | } 21 | 22 | Octree tree = new Octree<>(); 23 | parts.put(tileset, tree); 24 | return tree; 25 | } 26 | 27 | public void drawFloor(Tileset tileset, int x, int y, int z, int size) { 28 | Octree part = getPart(tileset); 29 | if (part.getItem() != null) { 30 | System.out.println("Warning: Re-drawing on same tile?"); 31 | return; 32 | } 33 | x = x - x % size; 34 | y = y - y % size; 35 | z = z - z % size; 36 | 37 | if (part.get(x, y, z, size) != null) return; 38 | 39 | Cell cell = new Cell(x, y, z); 40 | cell.setType(Cell.Type.FLOOR); 41 | cell.setSize(size); 42 | part.insert(x, y, z, size, cell); 43 | } 44 | 45 | public void draw(Tileset tileset, int x, int y, int z, int size) { 46 | Octree part = getPart(tileset); 47 | if (part.getItem() != null) { 48 | System.out.println("Warning: Re-drawing on same tile?"); 49 | return; 50 | } 51 | x = x - x % size; 52 | y = y - y % size; 53 | z = z - z % size; 54 | 55 | addCell(x, y, z, size, part); 56 | } 57 | 58 | private void addCell(int x, int y, int z, int size, Octree part) { 59 | Cell cell = new Cell(x, y, z); 60 | cell.setSize(size); 61 | part.insert(x, y, z, size, cell); 62 | } 63 | 64 | public void paint(Tileset tileset, int x, int y, int z, int size, Face face, Tile tile) { 65 | Octree part = getPart(tileset); 66 | x = x - x % size; 67 | y = y - y % size; 68 | z = z - z % size; 69 | Cell cell = part.get(x, y, z, size); 70 | if (cell != null) { 71 | cell.set(face, tile); 72 | } else { 73 | System.out.println("Warning: No cell at (" + x + ", " + y + ", " + z + ") when trying to paint; tileset: " + tileset.getName()); 74 | } 75 | } 76 | 77 | public void erase(Tileset tileset, int x, int y, int z, int size) { 78 | Octree part = getPart(tileset); 79 | x = x - x % size; 80 | y = y - y % size; 81 | z = z - z % size; 82 | part.remove(x, y, z, size); 83 | } 84 | 85 | public Map> getParts() { 86 | return parts; 87 | } 88 | 89 | public String getName() { 90 | return name; 91 | } 92 | 93 | public void setName(String name) { 94 | this.name = name; 95 | } 96 | 97 | 98 | public boolean isVisible() { 99 | return visible; 100 | } 101 | 102 | public void setVisible(boolean visible) { 103 | this.visible = visible; 104 | } 105 | 106 | public Tile getTile(Tileset tileset, int x, int y, int z, int size, Face face) { 107 | final Octree part = getPart(tileset); 108 | if (part == null) return null; 109 | 110 | final Cell cell = part.get(x, y, z, size); 111 | if (cell == null) return null; 112 | 113 | final Integer i = cell.get(face); 114 | return tileset.getTile(i); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/editor/menu/EditorMenuBar.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.editor.menu; 2 | 3 | import com.badlogic.gdx.Gdx; 4 | import com.badlogic.gdx.Input; 5 | import com.badlogic.gdx.scenes.scene2d.Actor; 6 | import com.badlogic.gdx.scenes.scene2d.Stage; 7 | import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; 8 | import com.kotcrab.vis.ui.util.dialog.Dialogs; 9 | import com.kotcrab.vis.ui.widget.Menu; 10 | import com.kotcrab.vis.ui.widget.MenuBar; 11 | import com.kotcrab.vis.ui.widget.MenuItem; 12 | import me.manabreak.ratio.editor.EditorController; 13 | import me.manabreak.ratio.plugins.level.LevelEditorPlugin; 14 | 15 | public class EditorMenuBar extends MenuBar { 16 | 17 | private final Stage stage; 18 | private final EditorController controller; 19 | 20 | public EditorMenuBar(Stage stage, EditorController controller) { 21 | this.stage = stage; 22 | this.controller = controller; 23 | addMenu(createFileMenu()); 24 | addMenu(createOptionsMenu()); 25 | } 26 | 27 | private Menu createOptionsMenu() { 28 | Menu optMenu = new Menu("Options"); 29 | 30 | MenuItem itemHighlight = new MenuItem("Toggle layer highlighting", new ChangeListener() { 31 | @Override 32 | public void changed(ChangeEvent event, Actor actor) { 33 | controller.getPlugin(LevelEditorPlugin.class).toggleLayerHighlighting(); 34 | } 35 | }); 36 | itemHighlight.setShortcut(Input.Keys.H); 37 | optMenu.addItem(itemHighlight); 38 | 39 | return optMenu; 40 | } 41 | 42 | private Menu createFileMenu() { 43 | Menu fileMenu = new Menu("File"); 44 | 45 | MenuItem itemNew = new MenuItem("New scene", new ChangeListener() { 46 | @Override 47 | public void changed(ChangeEvent event, Actor actor) { 48 | Dialogs.showConfirmDialog(stage, 49 | "Discard Changes?", 50 | "Unsaved changes will be lost!\nAre you sure you want to continue?", 51 | new String[]{"Cancel", "OK"}, 52 | new Boolean[]{false, true}, 53 | result -> { 54 | if (!result) return; 55 | // TODO Handle reset 56 | }); 57 | } 58 | }); 59 | fileMenu.addItem(itemNew); 60 | 61 | MenuItem itemOpen = new MenuItem("Open", new ChangeListener() { 62 | @Override 63 | public void changed(ChangeEvent event, Actor actor) { 64 | controller.onOpenClicked(); 65 | } 66 | }); 67 | itemOpen.setShortcut(Input.Keys.CONTROL_LEFT, Input.Keys.O); 68 | fileMenu.addItem(itemOpen); 69 | 70 | MenuItem itemSave = new MenuItem("Save", new ChangeListener() { 71 | @Override 72 | public void changed(ChangeEvent event, Actor actor) { 73 | controller.onSaveClicked(); 74 | } 75 | }); 76 | itemSave.setShortcut(Input.Keys.CONTROL_LEFT, Input.Keys.S); 77 | fileMenu.addItem(itemSave); 78 | 79 | MenuItem itemSaveAs = new MenuItem("Save As...", new ChangeListener() { 80 | @Override 81 | public void changed(ChangeEvent event, Actor actor) { 82 | controller.onSaveAsClicked(); 83 | } 84 | }); 85 | itemSaveAs.setShortcut(Input.Keys.CONTROL_LEFT, Input.Keys.SHIFT_LEFT, Input.Keys.S); 86 | fileMenu.addItem(itemSaveAs); 87 | fileMenu.addSeparator(); 88 | 89 | fileMenu.addItem(new ConfirmMenuItem(stage, "Exit", "Confirm exit", "Are you sure you want to quit? Unsaved changes will be lost.", result -> { 90 | if (result) { 91 | Gdx.app.exit(); 92 | } 93 | })); 94 | 95 | return fileMenu; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/level/ObjectRenderer.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.level; 2 | 3 | import com.badlogic.gdx.Gdx; 4 | import com.badlogic.gdx.graphics.Camera; 5 | import com.badlogic.gdx.graphics.Color; 6 | import com.badlogic.gdx.graphics.GL20; 7 | import com.badlogic.gdx.graphics.g2d.BitmapFont; 8 | import com.badlogic.gdx.graphics.g2d.GlyphLayout; 9 | import com.badlogic.gdx.graphics.g2d.SpriteBatch; 10 | import com.badlogic.gdx.graphics.glutils.ShapeRenderer; 11 | import com.badlogic.gdx.math.Vector3; 12 | import me.manabreak.ratio.plugins.objects.GameObject; 13 | import me.manabreak.ratio.plugins.objects.ObjectRenderMode; 14 | import me.manabreak.ratio.utils.TextUtils; 15 | 16 | import java.util.List; 17 | 18 | public class ObjectRenderer { 19 | 20 | private static final Color SELECTION_COLOR = Color.CHARTREUSE; 21 | private final ShapeRenderer renderer; 22 | private final SpriteBatch batch; 23 | private final BitmapFont font; 24 | private Vector3 tmp = new Vector3(); 25 | private ObjectRenderMode mode = ObjectRenderMode.Line; 26 | private boolean seeThrough = true; 27 | 28 | public ObjectRenderer(ShapeRenderer renderer, SpriteBatch batch, BitmapFont font) { 29 | this.renderer = renderer; 30 | this.batch = batch; 31 | this.font = font; 32 | } 33 | 34 | public void render(Camera camera, List objects) { 35 | renderObjects(camera, objects); 36 | renderNames(camera, objects); 37 | } 38 | 39 | public void rotateDrawMode() { 40 | int i = mode.ordinal(); 41 | i++; 42 | i %= ObjectRenderMode.values().length; 43 | mode = ObjectRenderMode.values()[i]; 44 | } 45 | 46 | private void renderNames(Camera camera, List objects) { 47 | batch.begin(); 48 | for (GameObject obj : objects) { 49 | if (obj.isSelected()) { 50 | font.setColor(SELECTION_COLOR); 51 | } else { 52 | font.setColor(Color.WHITE); 53 | } 54 | 55 | float x = obj.getX() + (obj.getSizeX()) / 2f; 56 | float y = obj.getY() + (obj.getSizeY()) + 1.5f; 57 | float z = obj.getZ() + (obj.getSizeZ()) / 2f; 58 | tmp.set(x, y, z); 59 | tmp = camera.project(tmp); 60 | 61 | String text = obj.getName(); 62 | if (!TextUtils.isNullOrEmpty(obj.getType())) { 63 | text += " (" + obj.getType() + ")"; 64 | } 65 | GlyphLayout layout = new GlyphLayout(font, text); 66 | final float fontX = tmp.x - (layout.width) / 2f; 67 | final float fontY = tmp.y - (layout.height) / 2f; 68 | 69 | font.draw(batch, layout, fontX, fontY); 70 | } 71 | batch.end(); 72 | } 73 | 74 | private void renderObjects(Camera camera, List objects) { 75 | if (mode == ObjectRenderMode.None) return; 76 | 77 | Gdx.gl20.glEnable(GL20.GL_DEPTH_TEST); 78 | Gdx.gl20.glDepthFunc(GL20.GL_LEQUAL); 79 | 80 | if (seeThrough) { 81 | Gdx.gl20.glClear(GL20.GL_DEPTH_BUFFER_BIT); 82 | } 83 | 84 | renderer.setProjectionMatrix(camera.combined); 85 | switch (mode) { 86 | case Line: 87 | renderer.begin(ShapeRenderer.ShapeType.Line); 88 | break; 89 | case Filled: 90 | renderer.begin(ShapeRenderer.ShapeType.Filled); 91 | break; 92 | } 93 | for (GameObject obj : objects) { 94 | float sx = obj.getSizeX(); 95 | float sy = obj.getSizeY(); 96 | float sz = obj.getSizeZ(); 97 | 98 | float x = obj.getX(); 99 | float y = obj.getY(); 100 | float z = obj.getZ() + sz; 101 | 102 | renderer.setColor(obj.isSelected() ? SELECTION_COLOR : obj.getColor()); 103 | renderer.box(x, y, z, sx, sy, sz); 104 | } 105 | renderer.end(); 106 | } 107 | 108 | public void toggleSeeThrough() { 109 | seeThrough = !seeThrough; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /editor/src/test/java/me/manabreak/ratio/plugins/tilesets/TilesetPresenterTest.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.tilesets; 2 | 3 | import com.badlogic.gdx.files.FileHandle; 4 | import com.badlogic.gdx.graphics.g2d.TextureRegion; 5 | import io.reactivex.observers.TestObserver; 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | import org.mockito.Mock; 10 | import org.mockito.junit.MockitoJUnitRunner; 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | import static org.mockito.ArgumentMatchers.any; 16 | import static org.mockito.ArgumentMatchers.eq; 17 | import static org.mockito.Mockito.*; 18 | 19 | @RunWith(MockitoJUnitRunner.class) 20 | public class TilesetPresenterTest { 21 | 22 | @Mock 23 | private TilesetPlugin plugin; 24 | 25 | @Mock 26 | private TilesetManager manager; 27 | 28 | @Mock 29 | private TilesetUi view; 30 | 31 | private TilesetPresenter presenter; 32 | 33 | @Before 34 | public void setUp() { 35 | presenter = new TilesetPresenter(plugin, manager); 36 | presenter.attachView(view); 37 | } 38 | 39 | @Test 40 | public void testLoadTileset() { 41 | ImageTileset tileset = mock(ImageTileset.class); 42 | when(manager.load(any())).thenReturn(tileset); 43 | 44 | FileHandle fh = mock(FileHandle.class); 45 | presenter.loadTileset(fh); 46 | 47 | verify(manager).load(eq(fh)); 48 | verify(view).createTab(eq(tileset)); 49 | verify(view).createPicker(eq(tileset), eq(16), eq(16)); 50 | } 51 | 52 | @Test 53 | public void testCreatePalette() { 54 | PaletteTileset tileset = mock(PaletteTileset.class); 55 | when(manager.createPalette(anyString())).thenReturn(tileset); 56 | 57 | presenter.createPalette(); 58 | verify(view).promptPaletteName(); 59 | 60 | presenter.createPalette("Test"); 61 | verify(manager).createPalette(eq("Test")); 62 | verify(view).createTab(eq(tileset)); 63 | verify(view).createPicker(eq(tileset), eq(1), eq(1)); 64 | } 65 | 66 | @Test 67 | public void testSelectTileset() { 68 | Tileset tileset = mock(Tileset.class); 69 | presenter.selectTileset(tileset); 70 | verify(manager).setCurrentTileset(eq(tileset)); 71 | } 72 | 73 | @Test 74 | public void testTileSizeChanged() { 75 | Tileset tileset = mock(Tileset.class); 76 | FileHandle fh = mock(FileHandle.class); 77 | when(manager.load(any())).thenReturn(tileset); 78 | when(manager.getCurrentTileset()).thenReturn(tileset); 79 | presenter.loadTileset(fh); 80 | verify(view).createPicker(eq(tileset), eq(16), eq(16)); 81 | 82 | presenter.tileWidthChanged(8); 83 | verify(view).createPicker(eq(tileset), eq(8), eq(16)); 84 | 85 | presenter.tileHeightChanged(4); 86 | verify(view).createPicker(eq(tileset), eq(8), eq(4)); 87 | } 88 | 89 | @Test 90 | public void testSelectRegion() { 91 | TextureRegion region = mock(TextureRegion.class); 92 | TestObserver test = presenter.getRegionSubject().test(); 93 | presenter.selectRegion(region); 94 | test.assertValue(region); 95 | } 96 | 97 | @Test 98 | public void testAutomapChanged() { 99 | TestObserver test = presenter.getAutomapSubject().test(); 100 | presenter.automapChanged(true); 101 | presenter.automapChanged(false); 102 | test.assertValues(true, false); 103 | } 104 | 105 | @Test 106 | public void testLevelLoaded() { 107 | List tilesets = new ArrayList<>(); 108 | ImageTileset imageTileset = mock(ImageTileset.class); 109 | PaletteTileset paletteTileset = mock(PaletteTileset.class); 110 | tilesets.add(imageTileset); 111 | tilesets.add(paletteTileset); 112 | when(manager.getTilesets()).thenReturn(tilesets); 113 | presenter.levelLoaded(); 114 | 115 | verify(view).createTab(eq(imageTileset)); 116 | verify(view).createPicker(eq(imageTileset), eq(16), eq(16)); 117 | 118 | verify(view).createTab(eq(paletteTileset)); 119 | verify(view).createPicker(eq(paletteTileset), eq(1), eq(1)); 120 | } 121 | } -------------------------------------------------------------------------------- /editor/src/test/java/me/manabreak/ratio/plugins/objects/ObjectEditorTest.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.objects; 2 | 3 | import com.badlogic.gdx.Input; 4 | import com.badlogic.gdx.graphics.Color; 5 | import me.manabreak.ratio.test.GdxTest; 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | import org.mockito.junit.MockitoJUnitRunner; 10 | 11 | import static org.junit.Assert.*; 12 | 13 | @RunWith(MockitoJUnitRunner.class) 14 | public class ObjectEditorTest extends GdxTest { 15 | 16 | private ObjectEditorUi view; 17 | private ObjectEditorPresenter p; 18 | 19 | @Before 20 | public void setUp() { 21 | p = new ObjectEditorPresenter(); 22 | view = new ObjectEditorUi(p); 23 | p.attachView(view); 24 | } 25 | 26 | @Test 27 | public void testClickToSelect() { 28 | p.createObject(); 29 | p.createObject(); 30 | 31 | for (GameObject object : p.getObjects()) { 32 | assertFalse(object.isSelected()); 33 | } 34 | 35 | p.objectClicked(p.getObjects().get(0)); 36 | assertTrue(p.getObjects().get(0).isSelected()); 37 | assertFalse(p.getObjects().get(1).isSelected()); 38 | } 39 | 40 | @Test 41 | public void testObjectCreationDeletion() { 42 | assertEquals(0, p.getObjects().size()); 43 | p.createObject(); 44 | assertEquals(1, p.getObjects().size()); 45 | 46 | p.removeObject(p.getObjects().get(0)); 47 | assertEquals(0, p.getObjects().size()); 48 | 49 | p.createObject(); 50 | p.setSelection(p.getObjects().get(0)); 51 | 52 | p.onKeyEvent(Input.Keys.FORWARD_DEL); 53 | assertEquals(0, p.getObjects().size()); 54 | } 55 | 56 | @Test 57 | public void testBasicPropertyEditing() { 58 | p.createObject(); 59 | 60 | final GameObject obj = p.getObjects().get(0); 61 | p.setSelection(obj); 62 | 63 | p.typeChanged(obj, "Spawn"); 64 | assertEquals("Spawn", obj.getType()); 65 | 66 | p.xChanged(obj, "5.0"); 67 | assertEquals(5.0f, obj.getX(), 0.001f); 68 | 69 | p.yChanged(obj, "6.0"); 70 | assertEquals(6.0f, obj.getY(), 0.001f); 71 | 72 | p.zChanged(obj, "7.0"); 73 | assertEquals(7.0f, obj.getZ(), 0.001f); 74 | 75 | p.widthChanged(obj, "8.0"); 76 | assertEquals(8.0f, obj.getSizeX(), 0.001f); 77 | 78 | p.heightChanged(obj, "9.0"); 79 | assertEquals(9.0f, obj.getSizeY(), 0.001f); 80 | 81 | p.depthChanged(obj, "10.0"); 82 | assertEquals(10.0f, obj.getSizeZ(), 0.001f); 83 | 84 | p.colorChanged(obj, Color.CHARTREUSE); 85 | assertEquals(Color.CHARTREUSE, obj.getColor()); 86 | } 87 | 88 | @Test 89 | public void testCreateCustomProperties() { 90 | p.createObject(); 91 | final GameObject obj = p.getObjects().get(0); 92 | 93 | assertFalse(obj.getProperties().hasProperty("somebool")); 94 | assertFalse(obj.getProperty("somebool", false)); 95 | 96 | // No object selected; shouldn't affect the obj 97 | p.createBooleanProperty("somebool"); 98 | assertFalse(obj.getProperties().hasProperty("somebool")); 99 | assertFalse(obj.getProperty("somebool", false)); 100 | 101 | p.objectClicked(obj); 102 | 103 | // Boolean 104 | p.createBooleanProperty("somebool"); 105 | assertTrue(obj.getProperties().hasProperty("somebool")); 106 | assertTrue(obj.getProperty("somebool", false)); 107 | p.changePropertyValue("somebool", false); 108 | assertFalse(obj.getProperty("somebool", true)); 109 | 110 | // Integer 111 | p.createIntProperty("someint"); 112 | assertTrue(obj.getProperties().hasProperty("someint")); 113 | assertEquals(0, (int) obj.getProperty("someint", 42)); 114 | p.changePropertyValue("someint", 11); 115 | assertEquals(11, (int) obj.getProperty("someint", 42)); 116 | 117 | // Double 118 | p.createDoubleProperty("somedouble"); 119 | assertTrue(obj.getProperties().hasProperty("somedouble")); 120 | assertEquals(0.0, obj.getProperty("somedouble", 3.14), 0.0001); 121 | p.changePropertyValue("somedouble", 0.707); 122 | assertEquals(0.707, obj.getProperty("somedouble", 3.14), 0.0001); 123 | 124 | // String 125 | p.createStringProperty("somestr"); 126 | assertTrue(obj.getProperties().hasProperty("somestr")); 127 | assertEquals("", obj.getProperty("somestr", "foo")); 128 | p.changePropertyValue("somestr", "bar"); 129 | assertEquals("bar", obj.getProperty("somestr", "foo")); 130 | } 131 | } -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/level/Octree.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.level; 2 | 3 | import java.util.ArrayDeque; 4 | import java.util.ArrayList; 5 | import java.util.Deque; 6 | import java.util.List; 7 | 8 | /** 9 | * Quadtree implementation which has just one item per leaf cell 10 | */ 11 | public class Octree { 12 | 13 | private final int level; 14 | private final int x; 15 | private final int y; 16 | private final int z; 17 | private T item = null; 18 | private Octree[] octants = new Octree[8]; 19 | 20 | public Octree() { 21 | level = 1 << 30; 22 | x = 0; 23 | y = 0; 24 | z = 0; 25 | } 26 | 27 | public Octree(int rootLevel) { 28 | level = 1 << rootLevel; 29 | x = 0; 30 | y = 0; 31 | z = 0; 32 | } 33 | 34 | private Octree(int level, int x, int y, int z) { 35 | this.level = level; 36 | this.x = x; 37 | this.y = y; 38 | this.z = z; 39 | } 40 | 41 | public void clear() { 42 | item = null; 43 | for (int i = 0; i < 8; ++i) { 44 | if (octants[i] != null) { 45 | octants[i].clear(); 46 | octants[i] = null; 47 | } 48 | } 49 | } 50 | 51 | public void remove(int x, int y, int z, int size) { 52 | if (size == level && this.item != null) { 53 | this.item = null; 54 | return; 55 | } 56 | 57 | for (int i = 0; i < 8; ++i) { 58 | if (octants[i] != null && octants[i].containsPoint(x, y, z)) { 59 | octants[i].remove(x, y, z, size); 60 | break; 61 | } 62 | } 63 | } 64 | 65 | private void split() { 66 | if (level == 1) throw new IllegalStateException("Cannot split level 1!"); 67 | int subs = level >> 1; 68 | octants[0] = new Octree(subs, x + subs, y, z); 69 | octants[1] = new Octree(subs, x, y, z); 70 | octants[2] = new Octree(subs, x, y + subs, z); 71 | octants[3] = new Octree(subs, x + subs, y + subs, z); 72 | octants[4] = new Octree(subs, x + subs, y, z + subs); 73 | octants[5] = new Octree(subs, x, y, z + subs); 74 | octants[6] = new Octree(subs, x, y + subs, z + subs); 75 | octants[7] = new Octree(subs, x + subs, y + subs, z + subs); 76 | } 77 | 78 | private boolean containsPoint(int x, int y, int z) { 79 | return x >= this.x && y >= this.y && z >= this.z && 80 | x < this.x + level && y < this.y + level && z < this.z + level; 81 | } 82 | 83 | public void insert(int x, int y, int z, int size, T item) { 84 | if (size == level && this.item == null) { 85 | this.item = item; 86 | return; 87 | } else if (size < level && this.item != null) { 88 | this.item = null; 89 | } 90 | 91 | if (octants[0] == null) { 92 | split(); 93 | } 94 | 95 | for (int i = 0; i < 8; ++i) { 96 | if (octants[i].containsPoint(x, y, z)) { 97 | //noinspection unchecked 98 | octants[i].insert(x, y, z, size, item); 99 | break; 100 | } 101 | } 102 | } 103 | 104 | public Octree get(int octant) { 105 | return octants[octant]; 106 | } 107 | 108 | public int getLevel() { 109 | return level; 110 | } 111 | 112 | public T getItem() { 113 | return item; 114 | } 115 | 116 | public int getItemCount() { 117 | int c = 0; 118 | if (item != null) c++; 119 | for (int i = 0; i < 8; ++i) { 120 | if (octants[i] != null) { 121 | c += octants[i].getItemCount(); 122 | } 123 | } 124 | return c; 125 | } 126 | 127 | public T get(int x, int y, int z, int size) { 128 | if (size == level) { 129 | return item; 130 | } 131 | 132 | if (octants[0] != null) { 133 | for (int i = 0; i < 8; ++i) { 134 | if (octants[i].containsPoint(x, y, z)) { 135 | //noinspection unchecked 136 | T t = (T) octants[i].get(x, y, z, size); 137 | if (t != null) return t; 138 | } 139 | } 140 | } 141 | 142 | return null; 143 | } 144 | 145 | public List flatten() { 146 | List list = new ArrayList(); 147 | 148 | Deque> queue = new ArrayDeque<>(); 149 | queue.push(this); 150 | 151 | while (queue.size() > 0) { 152 | Octree tree = queue.pop(); 153 | if (tree.item != null) { 154 | list.add(tree.item); 155 | } 156 | 157 | for (int oct = 0; oct < 8; ++oct) { 158 | //noinspection unchecked 159 | Octree o = tree.get(oct); 160 | if (o != null) queue.push(o); 161 | } 162 | } 163 | 164 | return list; 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/level/ToolRenderer.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.level; 2 | 3 | import com.badlogic.gdx.graphics.Camera; 4 | import com.badlogic.gdx.graphics.Color; 5 | import com.badlogic.gdx.graphics.glutils.ShapeRenderer; 6 | 7 | public class ToolRenderer { 8 | 9 | private ShapeRenderer renderer = new ShapeRenderer(); 10 | 11 | public ToolRenderer() { 12 | 13 | } 14 | 15 | public void renderWireCube(Camera camera, Coord coord, int size, Color color) { 16 | renderCube(camera, coord, size, color, ShapeRenderer.ShapeType.Line); 17 | } 18 | 19 | public void renderWireCube(Camera camera, Coord start, Coord end, int size, Color color) { 20 | float cf = size / 16f; 21 | renderer.setProjectionMatrix(camera.combined); 22 | renderer.begin(ShapeRenderer.ShapeType.Line); 23 | renderer.setColor(color); 24 | 25 | int minX = Math.min(start.x, end.x); 26 | int minY = Math.min(start.y, end.y); 27 | int minZ = Math.min(start.z, end.z); 28 | 29 | int maxX = Math.max(start.x, end.x); 30 | int maxY = Math.max(start.y, end.y); 31 | int maxZ = Math.max(start.z, end.z); 32 | 33 | float sx = minX * cf / size; 34 | float sy = minY * cf / size; 35 | float sz = minZ * cf / size; 36 | 37 | float ex = maxX * cf / size; 38 | float ey = maxY * cf / size; 39 | float ez = maxZ * cf / size; 40 | 41 | float s0 = ex - sx + 1; 42 | float s1 = ey - sy + 1; 43 | float s2 = ez - sz + 1; 44 | 45 | renderer.box(sx, sy, sz, s0, s1, -s2); 46 | renderer.end(); 47 | } 48 | 49 | void renderCube(Camera camera, Coord coord, int cellSize, Color color) { 50 | renderCube(camera, coord, cellSize, color, ShapeRenderer.ShapeType.Filled); 51 | } 52 | 53 | private void renderCube(Camera camera, Coord coord, int cellSize, Color color, ShapeRenderer.ShapeType type) { 54 | float cf = cellSize / 16f; 55 | renderer.setProjectionMatrix(camera.combined); 56 | if (coord.x >= 0 && coord.y >= 0 && coord.z >= 0) { 57 | renderer.begin(type); 58 | renderer.setColor(color); 59 | 60 | float cx = coord.x * cf / cellSize; 61 | float cy = coord.y * cf / cellSize; 62 | float cz = coord.z * cf / cellSize; 63 | renderer.box(cx, cy, cz + cf, cf, cf, cf); 64 | renderer.end(); 65 | } 66 | } 67 | 68 | public void renderFace(Camera camera, Coord coord, int cellSize, Face face) { 69 | float cf = cellSize / 16f; 70 | renderer.setProjectionMatrix(camera.combined); 71 | if (coord.x >= 0 && coord.y >= 0 && coord.z >= 0) { 72 | renderer.begin(ShapeRenderer.ShapeType.Line); 73 | renderer.setColor(1f, 0f, 1f, 1f); 74 | 75 | float cx = coord.x * cf / cellSize; 76 | float cy = coord.y * cf / cellSize; 77 | float cz = coord.z * cf / cellSize; 78 | 79 | switch (face) { 80 | case FRONT: 81 | renderer.line(cx, cy, cz + cf, cx + cf, cy, cz + cf); 82 | renderer.line(cx, cy, cz + cf, cx, cy + cf, cz + cf); 83 | renderer.line(cx + cf, cy + cf, cz + cf, cx, cy + cf, cz + cf); 84 | renderer.line(cx + cf, cy + cf, cz + cf, cx + cf, cy, cz + cf); 85 | break; 86 | case BACK: 87 | renderer.line(cx + cf, cy, cz, cx, cy, cz); 88 | renderer.line(cx + cf, cy, cz, cx + cf, cy + cf, cz); 89 | renderer.line(cx, cy + cf, cz, cx + cf, cy + cf, cz); 90 | renderer.line(cx, cy + cf, cz, cx, cy, cz); 91 | break; 92 | case LEFT: 93 | renderer.line(cx, cy, cz, cx, cy + cf, cz); 94 | renderer.line(cx, cy, cz + cf, cx, cy + cf, cz + cf); 95 | renderer.line(cx, cy + cf, cz, cx, cy + cf, cz + cf); 96 | renderer.line(cx, cy, cz, cx, cy, cz + cf); 97 | break; 98 | case RIGHT: 99 | renderer.line(cx + cf, cy, cz, cx + cf, cy + cf, cz); 100 | renderer.line(cx + cf, cy, cz + cf, cx + cf, cy + cf, cz + cf); 101 | renderer.line(cx + cf, cy + cf, cz, cx + cf, cy + cf, cz + cf); 102 | renderer.line(cx + cf, cy, cz, cx + cf, cy, cz + cf); 103 | break; 104 | case TOP: 105 | renderer.line(cx, cy + cf, cz, cx + cf, cy + cf, cz); 106 | renderer.line(cx, cy + cf, cz + cf, cx + cf, cy + cf, cz + cf); 107 | renderer.line(cx, cy + cf, cz, cx, cy + cf, cz + cf); 108 | renderer.line(cx + cf, cy + cf, cz, cx + cf, cy + cf, cz + cf); 109 | break; 110 | case BOTTOM: 111 | cy += 0.01f; 112 | renderer.line(cx, cy, cz, cx + cf, cy, cz); 113 | renderer.line(cx, cy, cz + cf, cx + cf, cy, cz + cf); 114 | renderer.line(cx, cy, cz, cx, cy, cz + cf); 115 | renderer.line(cx + cf, cy, cz, cx + cf, cy, cz + cf); 116 | break; 117 | } 118 | 119 | renderer.end(); 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /editor/src/test/java/me/manabreak/ratio/plugins/level/LevelTest.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.level; 2 | 3 | import com.badlogic.gdx.graphics.Texture; 4 | import com.badlogic.gdx.graphics.g2d.TextureRegion; 5 | import me.manabreak.ratio.plugins.tilesets.ImageTileset; 6 | import me.manabreak.ratio.plugins.tilesets.Tileset; 7 | import org.junit.Before; 8 | import org.junit.Test; 9 | import org.junit.runner.RunWith; 10 | import org.mockito.Mock; 11 | import org.mockito.junit.MockitoJUnitRunner; 12 | 13 | import java.util.Map; 14 | 15 | import static org.junit.Assert.*; 16 | import static org.mockito.ArgumentMatchers.anyInt; 17 | import static org.mockito.Mockito.mock; 18 | import static org.mockito.Mockito.when; 19 | 20 | @RunWith(MockitoJUnitRunner.class) 21 | public class LevelTest { 22 | 23 | @Mock 24 | private ImageTileset tileset; 25 | 26 | private Level level; 27 | 28 | @Before 29 | public void setUp() { 30 | level = new Level(); 31 | } 32 | 33 | @Test 34 | public void testDrawEraseBlock() { 35 | assertEquals(0, level.getLayers().size()); 36 | final TileLayer layer = level.createLayer("Test"); 37 | assertEquals(0, layer.getParts().size()); 38 | 39 | layer.draw(tileset, 0, 0, 0, 16); 40 | assertEquals(1, layer.getParts().size()); 41 | 42 | Map> parts = layer.getParts(); 43 | assertTrue(parts.containsKey(tileset)); 44 | 45 | Octree tree = parts.get(tileset); 46 | assertEquals(1, tree.getItemCount()); 47 | Cell cell = tree.get(0, 0, 0, 16); 48 | assertNotNull(cell); 49 | for (Face face : Face.values()) { 50 | assertEquals(0, cell.get(face).intValue()); 51 | assertEquals(Cell.Type.BLOCK, cell.getType()); 52 | } 53 | 54 | layer.erase(tileset, 0, 0, 0, 16); 55 | assertEquals(0, tree.getItemCount()); 56 | } 57 | 58 | @Test 59 | public void testDrawFloor() { 60 | final TileLayer layer = level.createLayer("Test"); 61 | layer.drawFloor(tileset, 0, 0, 0, 16); 62 | 63 | Map> parts = layer.getParts(); 64 | assertEquals(1, parts.size()); 65 | assertTrue(parts.containsKey(tileset)); 66 | Octree tree = parts.get(tileset); 67 | assertEquals(1, tree.getItemCount()); 68 | Cell cell = tree.get(0, 0, 0, 16); 69 | assertNotNull(cell); 70 | assertEquals(Cell.Type.FLOOR, cell.getType()); 71 | 72 | layer.erase(tileset, 0, 0, 0, 16); 73 | assertEquals(0, tree.getItemCount()); 74 | } 75 | 76 | @Test 77 | public void testCreateDeleteLayer() { 78 | assertEquals(0, level.getLayers().size()); 79 | 80 | final TileLayer a = level.createLayer("A"); 81 | assertEquals(1, level.getLayers().size()); 82 | 83 | final TileLayer b = level.createLayer("B"); 84 | assertEquals(2, level.getLayers().size()); 85 | 86 | final TileLayer otherA = level.get("A"); 87 | assertEquals(a, otherA); 88 | 89 | TileLayer manuallyCreated = new TileLayer("Manual"); 90 | level.addLayer(0, manuallyCreated); 91 | assertEquals(3, level.getLayers().size()); 92 | assertEquals(manuallyCreated, level.getLayers().get(0)); 93 | 94 | level.deleteLayer(a); 95 | assertEquals(2, level.getLayers().size()); 96 | level.deleteLayer(b); 97 | level.deleteLayer(manuallyCreated); 98 | assertEquals(0, level.getLayers().size()); 99 | } 100 | 101 | @Test(expected = IllegalArgumentException.class) 102 | public void testGetLayer_noSuchLayer() { 103 | level.createLayer("bar"); 104 | level.get("foo"); 105 | } 106 | 107 | @Test 108 | public void testVisibility() { 109 | final TileLayer a = level.createLayer("A"); 110 | final TileLayer b = level.createLayer("B"); 111 | assertTrue(a.isVisible()); 112 | assertTrue(b.isVisible()); 113 | a.setVisible(false); 114 | assertFalse(a.isVisible()); 115 | assertTrue(b.isVisible()); 116 | } 117 | 118 | @Test 119 | public void testSetName() { 120 | final TileLayer a = level.createLayer("A"); 121 | assertEquals("A", a.getName()); 122 | a.setName("B"); 123 | assertEquals("B", a.getName()); 124 | assertEquals(a, level.get("B")); 125 | } 126 | 127 | @Test 128 | public void testGetTile() { 129 | Tile mockTile = mock(Tile.class); 130 | when(tileset.getTile(anyInt())).thenReturn(mockTile); 131 | final TileLayer a = level.createLayer("A"); 132 | 133 | // Empty layer, no tiles 134 | assertNull(a.getTile(tileset, 0, 0, 0, 16, Face.FRONT)); 135 | 136 | a.draw(tileset, 0, 0, 0, 16); 137 | final Tile tile = a.getTile(tileset, 0, 0, 0, 16, Face.FRONT); 138 | assertNotNull(tile); 139 | } 140 | 141 | @Test 142 | public void testPaint() { 143 | Texture t = mock(Texture.class); 144 | Tileset tileset = new ImageTileset("tileset", "tileset", t); 145 | final Tile tile = tileset.createTile(new TextureRegion(t)); 146 | final TileLayer a = level.createLayer("A"); 147 | 148 | // No cell to paint; should fail silently 149 | a.paint(tileset, 0, 0, 0, 16, Face.FRONT, tile); 150 | 151 | a.draw(tileset, 0, 0, 0, 16); 152 | a.paint(tileset, 0, 0, 0, 16, Face.FRONT, tile); 153 | final Tile otherTile = a.getTile(tileset, 0, 0, 0, 16, Face.FRONT); 154 | assertEquals(tile, otherTile); 155 | } 156 | } -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/editor/EditorController.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.editor; 2 | 3 | import com.badlogic.gdx.Gdx; 4 | import com.badlogic.gdx.InputMultiplexer; 5 | import com.badlogic.gdx.InputProcessor; 6 | import com.badlogic.gdx.files.FileHandle; 7 | import me.manabreak.ratio.plugins.exporters.ExportManager; 8 | import me.manabreak.ratio.plugins.importers.ImportManager; 9 | import me.manabreak.ratio.plugins.importers.ImporterException; 10 | import me.manabreak.ratio.plugins.importers.MissingImporterException; 11 | import me.manabreak.ratio.plugins.level.Level; 12 | import me.manabreak.ratio.plugins.level.LevelEditorPlugin; 13 | import me.manabreak.ratio.plugins.objects.ObjectEditorPlugin; 14 | import me.manabreak.ratio.plugins.properties.LevelPropertiesPlugin; 15 | import me.manabreak.ratio.plugins.tilesets.TilesetManager; 16 | 17 | import java.util.ArrayList; 18 | import java.util.Collection; 19 | import java.util.List; 20 | 21 | public class EditorController implements PluginHost { 22 | 23 | private final PluginManager pluginManager; 24 | private final ExportManager exportManager; 25 | private final ImportManager importManager; 26 | private final TilesetManager tilesetManager; 27 | private InputMultiplexer multiplexer; 28 | private EditorView view; 29 | private FileHandle savedHandle; 30 | private List activePlugins = new ArrayList<>(); 31 | private List loopListeners = new ArrayList<>(); 32 | 33 | EditorController(PluginManager pluginManager, ExportManager exportManager, ImportManager importManager, TilesetManager tilesetManager) { 34 | this.pluginManager = pluginManager; 35 | this.exportManager = exportManager; 36 | this.importManager = importManager; 37 | this.tilesetManager = tilesetManager; 38 | } 39 | 40 | @Override 41 | public void addPlugin(EditorPlugin plugin) { 42 | System.out.println("addPlugin: " + plugin.getClass().getSimpleName()); 43 | plugin.setEditorView(view); 44 | plugin.setEditorController(this); 45 | plugin.initialize(); 46 | activePlugins.add(plugin); 47 | } 48 | 49 | public T getPlugin(Class tClass) { 50 | for (EditorPlugin plugin : activePlugins) { 51 | if (plugin.getClass().equals(tClass)) { 52 | //noinspection unchecked 53 | return (T) plugin; 54 | } 55 | } 56 | throw new IllegalArgumentException("No plugin " + tClass.getSimpleName() + " registered!"); 57 | } 58 | 59 | @Override 60 | public Collection getPlugins() { 61 | return activePlugins; 62 | } 63 | 64 | public void addLoopListener(LoopListener listener) { 65 | if (!loopListeners.contains(listener)) { 66 | loopListeners.add(listener); 67 | } 68 | } 69 | 70 | public void registerInputProcessor(InputProcessor inputProcessor) { 71 | multiplexer.addProcessor(inputProcessor); 72 | } 73 | 74 | void setView(me.manabreak.ratio.editor.EditorView view) { 75 | this.view = view; 76 | multiplexer = new InputMultiplexer(); 77 | multiplexer.addProcessor(view); 78 | multiplexer.addProcessor(new ShortcutProcessor(this, (EditorStage) view)); 79 | Gdx.input.setInputProcessor(multiplexer); 80 | } 81 | 82 | void act(float dt) { 83 | for (LoopListener listener : loopListeners) { 84 | listener.onUpdate(dt); 85 | } 86 | } 87 | 88 | public void onSaveClicked() { 89 | System.out.println("onSaveClicked()"); 90 | if (savedHandle != null) { 91 | onSaveFileSelected(savedHandle); 92 | } else { 93 | onSaveAsClicked(); 94 | } 95 | } 96 | 97 | public void onSaveAsClicked() { 98 | System.out.println("onSaveAsClicked()"); 99 | view.showSaveAsDialog(exportManager.getExporters()); 100 | } 101 | 102 | public void onSaveFileSelected(FileHandle fh) { 103 | System.out.println("Should save to file " + fh.toString()); 104 | LevelEditorPlugin levelEditorPlugin = getPlugin(LevelEditorPlugin.class); 105 | ObjectEditorPlugin objectEditorPlugin = getPlugin(ObjectEditorPlugin.class); 106 | LevelPropertiesPlugin levelPropertiesPlugin = getPlugin(LevelPropertiesPlugin.class); 107 | exportManager.save(levelEditorPlugin.getLevel(), objectEditorPlugin.getPresenter().getObjects(), levelPropertiesPlugin.getPresenter().getProperties(), fh); 108 | savedHandle = fh; 109 | } 110 | 111 | public TilesetManager getTilesetManager() { 112 | return tilesetManager; 113 | } 114 | 115 | public void onOpenClicked() { 116 | System.out.println("onOpenClicked()"); 117 | view.showOpenDialog(importManager.getImporters()); 118 | } 119 | 120 | public void onOpenFileSelected(FileHandle fh) { 121 | System.out.println("Should open file " + fh.toString()); 122 | // TODO If unsaved changes, prompt user before clearing 123 | try { 124 | Level level = importManager.load(fh, objects -> { 125 | getPlugin(ObjectEditorPlugin.class).loadObjects(objects); 126 | }, properties -> { 127 | getPlugin(LevelPropertiesPlugin.class).getPresenter().loadProperties(properties); 128 | }); 129 | getPlugin(LevelEditorPlugin.class).setLevel(level); 130 | savedHandle = fh; 131 | } catch (ImporterException | MissingImporterException e) { 132 | // TODO Show error notification on UI 133 | e.printStackTrace(); 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/toolbar/ToolbarUi.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.toolbar; 2 | 3 | import com.badlogic.gdx.scenes.scene2d.Actor; 4 | import com.badlogic.gdx.scenes.scene2d.ui.ButtonGroup; 5 | import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; 6 | import com.badlogic.gdx.utils.Align; 7 | import com.kotcrab.vis.ui.widget.Tooltip; 8 | import com.kotcrab.vis.ui.widget.VisTextButton; 9 | import me.manabreak.ratio.common.mvp.MvpView; 10 | import me.manabreak.ratio.ui.Res; 11 | 12 | public class ToolbarUi extends MvpView { 13 | 14 | private final ButtonGroup group; 15 | private final VisTextButton btnBlock; 16 | private final VisTextButton btnFloor; 17 | private final VisTextButton btnPaint; 18 | private final VisTextButton btnErase; 19 | private final VisTextButton btnSelect; 20 | 21 | protected ToolbarUi(ToolbarPresenter presenter) { 22 | super(presenter); 23 | 24 | defaults().width(32f).height(32f).pad(2f).padBottom(4f).padTop(4f).align(Align.center); 25 | 26 | VisTextButton btnHideLeftPanel = new VisTextButton(Res.ICON_BOX_CARET_LEFT, Res.ICONS_SMALL, new ChangeListener() { 27 | @Override 28 | public void changed(ChangeEvent event, Actor actor) { 29 | presenter.toggleLeftPanel(); 30 | } 31 | }); 32 | new Tooltip.Builder("Hide / show properties panel").target(btnHideLeftPanel).build(); 33 | add(btnHideLeftPanel); 34 | 35 | add().expandX(); 36 | 37 | btnBlock = new VisTextButton(Res.ICON_BLOCK, Res.ICONS_SMALL_TOGGLE, new ChangeListener() { 38 | @Override 39 | public void changed(ChangeEvent event, Actor actor) { 40 | presenter.blockToolSelected(); 41 | } 42 | }); 43 | new Tooltip.Builder("Draw blocks").target(btnBlock).build(); 44 | add(btnBlock); 45 | 46 | btnFloor = new VisTextButton(Res.ICON_FLOOR, Res.ICONS_SMALL_TOGGLE, new ChangeListener() { 47 | @Override 48 | public void changed(ChangeEvent event, Actor actor) { 49 | presenter.floorToolSelected(); 50 | } 51 | }); 52 | new Tooltip.Builder("Draw horizontal quads").target(btnFloor).build(); 53 | add(btnFloor); 54 | 55 | btnPaint = new VisTextButton(Res.ICON_PAINT, Res.ICONS_SMALL_TOGGLE, new ChangeListener() { 56 | @Override 57 | public void changed(ChangeEvent event, Actor actor) { 58 | presenter.paintToolSelected(); 59 | } 60 | }); 61 | new Tooltip.Builder("Paint faces").target(btnPaint).build(); 62 | add(btnPaint); 63 | 64 | btnErase = new VisTextButton(Res.ICON_ERASE, Res.ICONS_SMALL_TOGGLE, new ChangeListener() { 65 | @Override 66 | public void changed(ChangeEvent event, Actor actor) { 67 | presenter.eraseToolSelected(); 68 | } 69 | }); 70 | new Tooltip.Builder("Erase blocks").target(btnErase).build(); 71 | add(btnErase); 72 | 73 | addSeparator(true).width(2f); 74 | 75 | btnSelect = new VisTextButton(Res.ICON_SELECT, Res.ICONS_SMALL_TOGGLE, new ChangeListener() { 76 | @Override 77 | public void changed(ChangeEvent event, Actor actor) { 78 | presenter.selectToolSelected(); 79 | } 80 | }); 81 | new Tooltip.Builder("Select objects").target(btnSelect).build(); 82 | add(btnSelect); 83 | 84 | VisTextButton btnCreate = new VisTextButton(Res.ICON_CREATE, Res.ICONS_SMALL_TOGGLE, new ChangeListener() { 85 | @Override 86 | public void changed(ChangeEvent event, Actor actor) { 87 | presenter.createObjectClicked(); 88 | } 89 | }); 90 | new Tooltip.Builder("Create an object").target(btnCreate).build(); 91 | add(btnCreate); 92 | 93 | addSeparator(true).width(2f); 94 | 95 | VisTextButton btnCameraSnap = new VisTextButton(Res.ICON_LOCK, Res.ICONS_SMALL_TOGGLE, new ChangeListener() { 96 | @Override 97 | public void changed(ChangeEvent event, Actor actor) { 98 | presenter.setCameraSnap(((VisTextButton) actor).isChecked()); 99 | } 100 | }); 101 | btnCameraSnap.setChecked(true); 102 | new Tooltip.Builder("Toggle camera snap").target(btnCameraSnap).build(); 103 | add(btnCameraSnap); 104 | 105 | VisTextButton btnCameraMode = new VisTextButton(Res.ICON_CAMERA, Res.ICONS_SMALL_TOGGLE, new ChangeListener() { 106 | @Override 107 | public void changed(ChangeEvent event, Actor actor) { 108 | presenter.setCameraMode(((VisTextButton) actor).isChecked()); 109 | } 110 | }); 111 | new Tooltip.Builder("Toggle perspective projection").target(btnCameraMode).build(); 112 | add(btnCameraMode); 113 | 114 | add().expandX(); 115 | 116 | VisTextButton btnHideRightPanel = new VisTextButton(Res.ICON_BOX_CARET_RIGHT, Res.ICONS_SMALL, new ChangeListener() { 117 | @Override 118 | public void changed(ChangeEvent event, Actor actor) { 119 | presenter.toggleRightPanel(); 120 | } 121 | }); 122 | new Tooltip.Builder("Hide / show panel").target(btnHideRightPanel).build(); 123 | add(btnHideRightPanel); 124 | 125 | group = new ButtonGroup<>(btnBlock, btnFloor, btnPaint, btnErase, btnSelect); 126 | group.setMinCheckCount(0); 127 | group.setMaxCheckCount(1); 128 | } 129 | 130 | public void onBlockToolClicked() { 131 | group.uncheckAll(); 132 | btnBlock.setChecked(true); 133 | } 134 | 135 | public void onPaintToolClicked() { 136 | group.uncheckAll(); 137 | btnPaint.setChecked(true); 138 | } 139 | 140 | public void onEraseToolClicked() { 141 | group.uncheckAll(); 142 | btnErase.setChecked(true); 143 | } 144 | 145 | public void onSelectToolClicked() { 146 | group.uncheckAll(); 147 | btnSelect.setChecked(true); 148 | } 149 | 150 | public void onFloorToolClicked() { 151 | group.uncheckAll(); 152 | btnFloor.setChecked(true); 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /editor/src/test/java/me/manabreak/ratio/plugins/exporters/GdxJsonExporterTest.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.exporters; 2 | 3 | import com.badlogic.gdx.files.FileHandle; 4 | import com.badlogic.gdx.graphics.Color; 5 | import com.badlogic.gdx.graphics.g2d.TextureRegion; 6 | import com.badlogic.gdx.utils.IntMap; 7 | import com.badlogic.gdx.utils.JsonReader; 8 | import com.badlogic.gdx.utils.JsonValue; 9 | import me.manabreak.ratio.common.Properties; 10 | import me.manabreak.ratio.plugins.level.Level; 11 | import me.manabreak.ratio.plugins.level.Tile; 12 | import me.manabreak.ratio.plugins.level.TileLayer; 13 | import me.manabreak.ratio.plugins.objects.GameObject; 14 | import me.manabreak.ratio.plugins.tilesets.ImageTileset; 15 | import me.manabreak.ratio.plugins.tilesets.PaletteTileset; 16 | import org.junit.Before; 17 | import org.junit.Test; 18 | import org.junit.runner.RunWith; 19 | import org.mockito.ArgumentCaptor; 20 | import org.mockito.Captor; 21 | import org.mockito.Mock; 22 | import org.mockito.junit.MockitoJUnitRunner; 23 | 24 | import java.util.ArrayList; 25 | import java.util.HashMap; 26 | import java.util.List; 27 | import java.util.Map; 28 | 29 | import static org.junit.Assert.assertEquals; 30 | import static org.junit.Assert.assertTrue; 31 | import static org.mockito.ArgumentMatchers.eq; 32 | import static org.mockito.Mockito.verify; 33 | import static org.mockito.Mockito.when; 34 | 35 | @RunWith(MockitoJUnitRunner.class) 36 | public class GdxJsonExporterTest { 37 | 38 | @Mock 39 | private FileHandle fh; 40 | 41 | @Mock 42 | private ImageTileset imageTileset; 43 | 44 | @Mock 45 | private PaletteTileset paletteTileset; 46 | 47 | @Mock 48 | private TextureRegion imageRegion; 49 | 50 | @Mock 51 | private TextureRegion paletteRegion; 52 | 53 | @Captor 54 | private ArgumentCaptor captor; 55 | 56 | private GdxJsonExporter exporter; 57 | 58 | @Before 59 | public void setUp() throws Exception { 60 | exporter = new GdxJsonExporter(); 61 | 62 | IntMap tilemap = new IntMap<>(); 63 | tilemap.put(1, new Tile(1, imageRegion)); 64 | tilemap.put(2, new Tile(2, imageRegion)); 65 | when(imageTileset.getTiles()).thenReturn(tilemap); 66 | 67 | Map paletteEntries = new HashMap<>(); 68 | paletteEntries.put(new PaletteTileset.Tuple(0, 0), new Color(1f, 0f, 0f, 1f)); 69 | paletteEntries.put(new PaletteTileset.Tuple(1, 0), new Color(0f, 1f, 0f, 1f)); 70 | paletteEntries.put(new PaletteTileset.Tuple(0, 1), new Color(0f, 0f, 1f, 1f)); 71 | when(paletteTileset.getEntries()).thenReturn(paletteEntries); 72 | 73 | IntMap palettemap = new IntMap<>(); 74 | palettemap.put(1, new Tile(1, paletteRegion)); 75 | palettemap.put(2, new Tile(2, paletteRegion)); 76 | palettemap.put(3, new Tile(3, paletteRegion)); 77 | when(paletteTileset.getTiles()).thenReturn(palettemap); 78 | } 79 | 80 | @Test 81 | public void testExporting() { 82 | Level level = new Level(); 83 | final TileLayer layer = level.createLayer("Test"); 84 | 85 | // Draw few tiles with image tileset 86 | layer.draw(imageTileset, 0, 0, 0, 16); 87 | layer.draw(imageTileset, 16, 0, 0, 16); 88 | 89 | // ...and a few with palette 90 | layer.draw(paletteTileset, 0, 16, 0, 8); 91 | layer.draw(paletteTileset, 8, 16, 0, 8); 92 | 93 | List objectList = new ArrayList<>(); 94 | GameObject obj1 = new GameObject(); 95 | obj1.setName("Test1"); 96 | obj1.setPosition(1f, 2f, 3f); 97 | obj1.setSize(5f, 6f, 7f); 98 | obj1.setVisible(true); 99 | obj1.setColor(new Color(0.4f, 0.6f, 0.8f, 0.1f)); 100 | obj1.setProperty("TestStr", "Foobar"); 101 | obj1.setProperty("TestInt", 42); 102 | obj1.setProperty("TestBoolTrue", true); 103 | obj1.setProperty("TestBoolFalse", false); 104 | obj1.setProperty("TestDouble", 3.14); 105 | objectList.add(obj1); 106 | 107 | Properties properties = new Properties(); 108 | exporter.save(level, objectList, properties, fh); 109 | verify(fh).writeString(captor.capture(), eq(false)); 110 | String jsonString = captor.getValue(); 111 | System.out.println(jsonString); 112 | 113 | JsonValue val = new JsonReader().parse(jsonString); 114 | 115 | assertTrue(val.has("tilesets")); 116 | JsonValue tilesets = val.get("tilesets"); 117 | assertEquals(2, tilesets.size); 118 | 119 | JsonValue imageTilesetPart = tilesets.get(0); 120 | assertEquals("normal", imageTilesetPart.getString("type")); 121 | assertEquals(2, imageTilesetPart.get("tiles").size); 122 | 123 | JsonValue paletteTilesetPart = tilesets.get(1); 124 | assertEquals("palette", paletteTilesetPart.getString("type")); 125 | assertEquals(3, paletteTilesetPart.get("tiles").size); 126 | 127 | assertTrue(val.has("layers")); 128 | JsonValue layers = val.get("layers"); 129 | assertEquals(1, layers.size); 130 | 131 | JsonValue layerJson = layers.get(0); 132 | assertTrue(layerJson.has("parts")); 133 | assertEquals(2, layerJson.get("parts").size); 134 | 135 | assertEquals(0, layerJson.get("parts").get(0).getInt("tileset")); 136 | assertEquals(1, layerJson.get("parts").get(1).getInt("tileset")); 137 | 138 | assertTrue(val.has("objects")); 139 | JsonValue objects = val.get("objects"); 140 | assertEquals(1, objects.size); 141 | JsonValue o = objects.get(0); 142 | assertEquals("Test1", o.getString("name")); 143 | float[] location = o.get("location").asFloatArray(); 144 | assertEquals(1f, location[0], 0.001f); 145 | assertEquals(2f, location[1], 0.001f); 146 | assertEquals(3f, location[2], 0.001f); 147 | 148 | float[] size = o.get("size").asFloatArray(); 149 | assertEquals(5f, size[0], 0.001f); 150 | assertEquals(6f, size[1], 0.001f); 151 | assertEquals(7f, size[2], 0.001f); 152 | 153 | float[] color = o.get("color").asFloatArray(); 154 | assertEquals(0.4f, color[0], 0.0001f); 155 | assertEquals(0.6f, color[1], 0.0001f); 156 | assertEquals(0.8f, color[2], 0.0001f); 157 | assertEquals(0.1f, color[3], 0.0001f); 158 | 159 | assertTrue(o.getBoolean("visible")); 160 | } 161 | } -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/objects/ObjectEditorPresenter.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.objects; 2 | 3 | import com.badlogic.gdx.Input; 4 | import com.badlogic.gdx.graphics.Color; 5 | import com.badlogic.gdx.math.Vector3; 6 | import me.manabreak.ratio.common.mvp.MvpPresenter; 7 | import me.manabreak.ratio.plugins.level.Coord; 8 | import me.manabreak.ratio.plugins.properties.PropertyHandler; 9 | 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | public class ObjectEditorPresenter extends MvpPresenter implements PropertyHandler { 14 | 15 | private final ArrayList objects = new ArrayList<>(); 16 | private final ObjectListAdapter adapter = new ObjectListAdapter(this, objects); 17 | 18 | private GameObject detailedItem; 19 | private GameObject selectedObject = null; 20 | 21 | public ObjectEditorPresenter() { 22 | 23 | } 24 | 25 | public void viewCreated() { 26 | view.createListView(adapter); 27 | } 28 | 29 | public void createObject() { 30 | System.out.println("Add object clicked"); 31 | GameObject obj = new GameObject(); 32 | obj.setName("GameObject 1"); 33 | objects.add(obj); 34 | adapter.itemsChanged(); 35 | } 36 | 37 | public void createObject(Coord startCoord, Coord endCoord) { 38 | GameObject obj = new GameObject(); 39 | int minX = Math.min(startCoord.x, endCoord.x) / 16; 40 | int maxX = Math.max(startCoord.x, endCoord.x) / 16; 41 | int minY = Math.min(startCoord.y, endCoord.y) / 16; 42 | int maxY = Math.max(startCoord.y, endCoord.y) / 16; 43 | int minZ = Math.min(startCoord.z, endCoord.z) / 16; 44 | int maxZ = Math.max(startCoord.z, endCoord.z) / 16; 45 | 46 | System.out.println("Min: " + minX + ", " + minY + ", " + minZ); 47 | System.out.println("Max: " + maxX + ", " + maxY + ", " + maxZ); 48 | 49 | obj.setPosition(minX, minY, minZ); 50 | obj.setSize(maxX - minX + 1, maxY - minY + 1, maxZ - minZ + 1); 51 | obj.setName("GameObject"); 52 | objects.add(obj); 53 | adapter.itemsChanged(); 54 | } 55 | 56 | public void removeObject(GameObject item) { 57 | objects.remove(item); 58 | adapter.itemsChanged(); 59 | } 60 | 61 | public void objectClicked(GameObject item) { 62 | detailedItem = item; 63 | view.showDetails(item); 64 | if (selectedObject != null) { 65 | selectedObject.setSelected(false); 66 | selectedObject = null; 67 | } 68 | this.selectedObject = item; 69 | item.setSelected(true); 70 | } 71 | 72 | public void changePropertyValue(String key, Object value) { 73 | detailedItem.setProperty(key, value); 74 | } 75 | 76 | public void removeProperty(String key) { 77 | detailedItem.removeProperty(key); 78 | view.showDetails(detailedItem); 79 | } 80 | 81 | public void createBooleanProperty(String key) { 82 | if (detailedItem == null) return; 83 | detailedItem.setProperty(key, true); 84 | view.showDetails(detailedItem); 85 | } 86 | 87 | public void createStringProperty(String key) { 88 | detailedItem.setProperty(key, ""); 89 | view.showDetails(detailedItem); 90 | } 91 | 92 | public void createDoubleProperty(String key) { 93 | detailedItem.setProperty(key, 0.0); 94 | view.showDetails(detailedItem); 95 | } 96 | 97 | @Override 98 | public void createVec3Property(String key) { 99 | detailedItem.setProperty(key, new Vector3()); 100 | view.showDetails(detailedItem); 101 | } 102 | 103 | public void createIntProperty(String key) { 104 | detailedItem.setProperty(key, 0); 105 | view.showDetails(detailedItem); 106 | } 107 | 108 | public void typeChanged(GameObject item, String text) { 109 | item.setType(text); 110 | } 111 | 112 | public void xChanged(GameObject item, String text) { 113 | try { 114 | float x = Float.parseFloat(text); 115 | item.setX(x); 116 | } catch (NumberFormatException ignored) { 117 | 118 | } 119 | } 120 | 121 | public void yChanged(GameObject item, String text) { 122 | try { 123 | float y = Float.parseFloat(text); 124 | item.setY(y); 125 | } catch (NumberFormatException ignored) { 126 | 127 | } 128 | } 129 | 130 | public void zChanged(GameObject item, String text) { 131 | try { 132 | float z = Float.parseFloat(text); 133 | item.setZ(z); 134 | } catch (NumberFormatException ignored) { 135 | 136 | } 137 | } 138 | 139 | public void colorChanged(GameObject item, Color newColor) { 140 | item.setColor(newColor); 141 | } 142 | 143 | public List getObjects() { 144 | return objects; 145 | } 146 | 147 | public void loadObjects(List objects) { 148 | this.objects.clear(); 149 | this.objects.addAll(objects); 150 | adapter.itemsChanged(); 151 | } 152 | 153 | public void widthChanged(GameObject item, String text) { 154 | try { 155 | float width = Float.parseFloat(text); 156 | item.setSizeX(width); 157 | } catch (NumberFormatException ignored) { 158 | 159 | } 160 | } 161 | 162 | public void heightChanged(GameObject item, String text) { 163 | try { 164 | float height = Float.parseFloat(text); 165 | item.setSizeY(height); 166 | } catch (NumberFormatException ignored) { 167 | 168 | } 169 | } 170 | 171 | public void depthChanged(GameObject item, String text) { 172 | try { 173 | float depth = Float.parseFloat(text); 174 | item.setSizeZ(depth); 175 | } catch (NumberFormatException ignored) { 176 | 177 | } 178 | } 179 | 180 | public void setSelection(GameObject obj) { 181 | if (this.selectedObject != null) { 182 | this.selectedObject.setSelected(false); 183 | this.selectedObject = null; 184 | } 185 | this.selectedObject = obj; 186 | if (obj != null) { 187 | obj.setSelected(true); 188 | adapter.getSelectionManager().select(obj); 189 | objectClicked(obj); 190 | } else { 191 | adapter.getSelectionManager().deselectAll(); 192 | view.hideDetails(); 193 | } 194 | } 195 | 196 | public void onKeyEvent(int keycode) { 197 | if (keycode == Input.Keys.FORWARD_DEL && selectedObject != null) { 198 | removeObject(selectedObject); 199 | setSelection(null); 200 | } 201 | } 202 | } 203 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/plugins/objects/ObjectEditorUi.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.plugins.objects; 2 | 3 | import com.badlogic.gdx.graphics.Color; 4 | import com.badlogic.gdx.scenes.scene2d.Actor; 5 | import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener; 6 | import com.kotcrab.vis.ui.widget.*; 7 | import com.kotcrab.vis.ui.widget.color.ColorPicker; 8 | import com.kotcrab.vis.ui.widget.color.ColorPickerAdapter; 9 | import me.manabreak.ratio.common.Properties; 10 | import me.manabreak.ratio.common.mvp.MvpView; 11 | import me.manabreak.ratio.plugins.properties.PropertyAdapter; 12 | import me.manabreak.ratio.plugins.properties.PropertyPrompt; 13 | import me.manabreak.ratio.ui.NumericTextFieldFilter; 14 | 15 | public class ObjectEditorUi extends MvpView { 16 | 17 | private final VisTable toolRegion; 18 | private final VisTable objectListRegion; 19 | private final VisTable objectDetailsRegion; 20 | private final VisTable objectPropertyActionsRegion; 21 | private final VisTable objectPropertyListRegion; 22 | private final VisTable objectBasicDetailsRegion; 23 | 24 | protected ObjectEditorUi(ObjectEditorPresenter presenter) { 25 | super(presenter); 26 | 27 | toolRegion = new VisTable(); 28 | add(toolRegion).growX().pad(4f); 29 | 30 | VisTextButton btnAdd = new VisTextButton("New Object", new ChangeListener() { 31 | @Override 32 | public void changed(ChangeEvent event, Actor actor) { 33 | presenter.createObject(); 34 | } 35 | }); 36 | toolRegion.add(btnAdd).pad(4f); 37 | 38 | row().pad(2f); 39 | 40 | objectListRegion = new VisTable(); 41 | add(objectListRegion).pad(4f).growX().maxHeight(200f); 42 | 43 | row().pad(2f); 44 | 45 | objectDetailsRegion = new VisTable(); 46 | add(objectDetailsRegion).grow().minHeight(300f); 47 | objectDetailsRegion.setVisible(false); 48 | 49 | objectBasicDetailsRegion = new VisTable(); 50 | objectDetailsRegion.add(objectBasicDetailsRegion).growX(); 51 | objectDetailsRegion.row(); 52 | 53 | objectPropertyActionsRegion = new VisTable(); 54 | VisTextButton btnAddProperty = new VisTextButton("Add Property"); 55 | btnAddProperty.addListener(new PropertyPrompt(btnAddProperty, presenter)); 56 | objectPropertyActionsRegion.add(btnAddProperty); 57 | objectDetailsRegion.add(objectPropertyActionsRegion).growX(); 58 | objectDetailsRegion.row(); 59 | 60 | objectPropertyListRegion = new VisTable(); 61 | objectDetailsRegion.add(objectPropertyListRegion).grow(); 62 | 63 | presenter.viewCreated(); 64 | } 65 | 66 | public void hideDetails() { 67 | objectDetailsRegion.setVisible(false); 68 | } 69 | 70 | void showDetails(GameObject item) { 71 | System.out.println("Showing details for item " + item.getName()); 72 | objectDetailsRegion.setVisible(true); 73 | objectBasicDetailsRegion.clear(); 74 | objectBasicDetailsRegion.defaults() 75 | .height(20f) 76 | .pad(1f); 77 | objectBasicDetailsRegion.columnDefaults(0) 78 | .width(140f).padRight(4f); 79 | objectBasicDetailsRegion.columnDefaults(1) 80 | .growX(); 81 | 82 | VisLabel lblType = new VisLabel("Type"); 83 | objectBasicDetailsRegion.add(lblType); 84 | VisTextField tfType = new VisTextField(item.getType()); 85 | tfType.setTextFieldListener((textField, c) -> { 86 | presenter.typeChanged(item, textField.getText()); 87 | }); 88 | objectBasicDetailsRegion.add(tfType); 89 | objectBasicDetailsRegion.row(); 90 | 91 | createNumberField(item.getX(), "X", (textField, c) -> presenter.xChanged(item, textField.getText())); 92 | createNumberField(item.getY(), "Y", (textField, c) -> presenter.yChanged(item, textField.getText())); 93 | createNumberField(item.getZ(), "Z", (textField, c) -> presenter.zChanged(item, textField.getText())); 94 | 95 | createNumberField(item.getSizeX(), "Width", ((textField, c) -> presenter.widthChanged(item, textField.getText()))); 96 | createNumberField(item.getSizeY(), "Height", ((textField, c) -> presenter.heightChanged(item, textField.getText()))); 97 | createNumberField(item.getSizeZ(), "Depth", ((textField, c) -> presenter.depthChanged(item, textField.getText()))); 98 | 99 | VisLabel lblColor = new VisLabel("Color"); 100 | objectBasicDetailsRegion.add(lblColor); 101 | VisTextButton btnColor = new VisTextButton("Choose"); 102 | btnColor.addListener(new ChangeListener() { 103 | @Override 104 | public void changed(ChangeEvent event, Actor actor) { 105 | ColorPicker cp = new ColorPicker("Choose Color", new ColorPickerAdapter() { 106 | @Override 107 | public void changed(Color newColor) { 108 | btnColor.setColor(newColor); 109 | presenter.colorChanged(item, newColor); 110 | } 111 | 112 | @Override 113 | public void finished(Color newColor) { 114 | presenter.colorChanged(item, newColor); 115 | } 116 | }); 117 | getStage().addActor(cp); 118 | } 119 | }); 120 | objectBasicDetailsRegion.add(btnColor); 121 | objectBasicDetailsRegion.row(); 122 | 123 | objectPropertyListRegion.clear(); 124 | final PropertyAdapter adapter = new PropertyAdapter(item.getProperties()); 125 | adapter.setDeleteCallback(entry -> presenter.removeProperty(entry.getKey())); 126 | ListView propertyListView = new ListView<>(adapter); 127 | propertyListView.getScrollPane().setScrollingDisabled(false, false); 128 | objectPropertyListRegion.add(propertyListView.getMainTable()).grow().top(); 129 | } 130 | 131 | private void createNumberField(float initialValue, String labelText, VisTextField.TextFieldListener listener) { 132 | VisLabel label = new VisLabel(labelText); 133 | objectBasicDetailsRegion.add(label); 134 | VisTextField textField = new VisTextField("" + initialValue); 135 | textField.setTextFieldFilter(new NumericTextFieldFilter()); 136 | textField.setTextFieldListener(listener); 137 | objectBasicDetailsRegion.add(textField); 138 | objectBasicDetailsRegion.row(); 139 | } 140 | 141 | void createListView(ObjectListAdapter adapter) { 142 | ListView listView = new ListView<>(adapter); 143 | objectListRegion.add(listView.getMainTable()).growX().top(); 144 | listView.setItemClickListener(presenter::objectClicked); 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /editor/src/main/java/me/manabreak/ratio/editor/EditorStage.java: -------------------------------------------------------------------------------- 1 | package me.manabreak.ratio.editor; 2 | 3 | import com.badlogic.gdx.Input; 4 | import com.badlogic.gdx.files.FileHandle; 5 | import com.badlogic.gdx.math.Vector2; 6 | import com.badlogic.gdx.scenes.scene2d.Actor; 7 | import com.badlogic.gdx.scenes.scene2d.Stage; 8 | import com.badlogic.gdx.scenes.scene2d.ui.Table; 9 | import com.badlogic.gdx.utils.Array; 10 | import com.badlogic.gdx.utils.viewport.ScreenViewport; 11 | import com.kotcrab.vis.ui.widget.MenuBar; 12 | import com.kotcrab.vis.ui.widget.VisTable; 13 | import com.kotcrab.vis.ui.widget.file.FileChooser; 14 | import com.kotcrab.vis.ui.widget.file.FileChooserAdapter; 15 | import com.kotcrab.vis.ui.widget.file.FileTypeFilter; 16 | import me.manabreak.ratio.editor.menu.EditorMenuBar; 17 | import me.manabreak.ratio.plugins.exporters.Exporter; 18 | import me.manabreak.ratio.plugins.importers.Importer; 19 | 20 | import java.util.List; 21 | 22 | public class EditorStage extends Stage implements EditorView { 23 | 24 | private final VisTable root; 25 | private final VisTable topPanel; 26 | private final TabbedContainer leftTabs; 27 | private final VisTable rightPanel; 28 | private final VisTable rightPanelTop, rightPanelBottom; 29 | // private final VisTable bottomPanel; 30 | private final EditorController controller; 31 | private final VisTable leftPanel; 32 | private final VisTable toolContainer; 33 | 34 | EditorStage(EditorController controller) { 35 | super(new ScreenViewport()); 36 | this.controller = controller; 37 | controller.setView(this); 38 | 39 | root = new VisTable(); 40 | root.setFillParent(true); 41 | addActor(root); 42 | 43 | MenuBar menuBar = new EditorMenuBar(this, controller); 44 | root.add(menuBar.getTable()).growX(); 45 | 46 | root.row(); 47 | 48 | VisTable mainContent = new VisTable(); 49 | root.add(mainContent).grow(); 50 | 51 | topPanel = new VisTable(); 52 | mainContent.add(topPanel).colspan(3).growX(); 53 | 54 | mainContent.row(); 55 | 56 | VisTable editorCameraTable = new VisTable(true); 57 | editorCameraTable.add("Editor Camera tab"); 58 | 59 | leftPanel = new VisTable(); 60 | toolContainer = new VisTable(); 61 | leftTabs = new TabbedContainer(); 62 | mainContent.add(leftPanel).top().growY().width(300f); 63 | leftPanel.add(toolContainer).growX().padBottom(16f); 64 | leftPanel.setBackground("default-table"); 65 | leftPanel.row(); 66 | leftPanel.add(leftTabs).grow().width(300f); 67 | 68 | VisTable centerPanel = new VisTable(true); 69 | mainContent.add(centerPanel).grow(); 70 | 71 | rightPanel = new VisTable(); 72 | mainContent.add(rightPanel).growY().width(300f); 73 | rightPanelTop = new VisTable(); 74 | rightPanelBottom = new VisTable(); 75 | rightPanel.add(rightPanelTop).grow().uniform(); 76 | rightPanel.row(); 77 | rightPanel.add(rightPanelBottom).grow().uniform(); 78 | mainContent.row(); 79 | 80 | // bottomPanel = new VisTable(); 81 | // mainContent.add(bottomPanel).colspan(3).growX().height(32f); 82 | 83 | root.pack(); 84 | } 85 | 86 | @Override 87 | public void setToolView(VisTable content) { 88 | toolContainer.add(content).grow(); 89 | } 90 | 91 | @Override 92 | public void addLeftPanelTab(String tabTitle, VisTable content) { 93 | EditorTab tab = new EditorTab(tabTitle, content); 94 | leftTabs.addTab(tab); 95 | } 96 | 97 | @Override 98 | public void showSaveAsDialog(List exporters) { 99 | FileChooser fileChooser = new FileChooser(FileChooser.Mode.SAVE); 100 | fileChooser.setMultiSelectionEnabled(false); 101 | fileChooser.setSelectionMode(FileChooser.SelectionMode.FILES); 102 | FileTypeFilter filter = new FileTypeFilter(false); 103 | for (Exporter exporter : exporters) { 104 | filter.addRule(exporter.getFormatName(), exporter.getFileExtension()); 105 | } 106 | fileChooser.setFileTypeFilter(filter); 107 | 108 | fileChooser.setListener(new FileChooserAdapter() { 109 | @Override 110 | public void selected(Array files) { 111 | controller.onSaveFileSelected(files.get(0)); 112 | } 113 | }); 114 | 115 | addActor(fileChooser); 116 | } 117 | 118 | @Override 119 | public void showOpenDialog(List importers) { 120 | FileChooser fileChooser = new FileChooser(FileChooser.Mode.OPEN); 121 | fileChooser.setMultiSelectionEnabled(false); 122 | fileChooser.setSelectionMode(FileChooser.SelectionMode.FILES); 123 | FileTypeFilter filter = new FileTypeFilter(false); 124 | for (Importer importer : importers) { 125 | filter.addRule(importer.getFormatName(), importer.getFileExtension()); 126 | } 127 | fileChooser.setFileTypeFilter(filter); 128 | 129 | fileChooser.setListener(new FileChooserAdapter() { 130 | @Override 131 | public void selected(Array files) { 132 | controller.onOpenFileSelected(files.get(0)); 133 | } 134 | }); 135 | 136 | addActor(fileChooser); 137 | } 138 | 139 | @Override 140 | public void registerToolbar(Table table) { 141 | topPanel.add(table).center().growX(); 142 | } 143 | 144 | @Override 145 | public void toggleLeftPanel() { 146 | leftPanel.setVisible(!leftPanel.isVisible()); 147 | } 148 | 149 | @Override 150 | public void toggleRightPanel() { 151 | rightPanel.setVisible(!rightPanel.isVisible()); 152 | } 153 | 154 | @Override 155 | public void setRightPanelTop(VisTable content) { 156 | rightPanelTop.add(content).grow(); 157 | } 158 | 159 | @Override 160 | public void setRightPanelBottom(VisTable content) { 161 | rightPanelBottom.add(content).grow(); 162 | } 163 | 164 | 165 | void resize(int width, int height) { 166 | super.getViewport().update(width, height, true); 167 | root.setPosition(0f, 0f); 168 | } 169 | 170 | @Override 171 | public boolean keyDown(int keyCode) { 172 | if (keyCode == Input.Keys.ENTER || keyCode == Input.Keys.ESCAPE) { 173 | if (getKeyboardFocus() != null) { 174 | setKeyboardFocus(null); 175 | } 176 | } 177 | return super.keyDown(keyCode); 178 | } 179 | 180 | @Override 181 | public boolean mouseMoved(int screenX, int screenY) { 182 | final Vector2 coords = screenToStageCoordinates(new Vector2(screenX, screenY)); 183 | final Actor actor = hit(coords.x, coords.y, true); 184 | setScrollFocus(actor); 185 | return super.mouseMoved(screenX, screenY); 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /editor/assets/tinted.atlas: -------------------------------------------------------------------------------- 1 | 2 | tinted.png 3 | size: 512,256 4 | format: RGBA8888 5 | filter: Nearest,Nearest 6 | repeat: none 7 | color-picker-bar-selector 8 | rotate: false 9 | xy: 292, 216 10 | size: 7, 14 11 | orig: 7, 14 12 | offset: 0, 0 13 | index: -1 14 | color-picker-cross 15 | rotate: false 16 | xy: 316, 231 17 | size: 5, 5 18 | orig: 5, 5 19 | offset: 0, 0 20 | index: -1 21 | color-picker-selector-horizontal 22 | rotate: false 23 | xy: 343, 238 24 | size: 3, 1 25 | orig: 3, 1 26 | offset: 0, 0 27 | index: -1 28 | color-picker-selector-vertical 29 | rotate: false 30 | xy: 25, 1 31 | size: 1, 3 32 | orig: 1, 3 33 | offset: 0, 0 34 | index: -1 35 | cursor 36 | rotate: false 37 | xy: 17, 1 38 | size: 3, 3 39 | split: 1, 1, 1, 1 40 | orig: 3, 3 41 | offset: 0, 0 42 | index: -1 43 | default 44 | rotate: false 45 | xy: 1, 116 46 | size: 254, 139 47 | orig: 256, 256 48 | offset: 0, 117 49 | index: -1 50 | font-small 51 | rotate: false 52 | xy: 1, 10 53 | size: 254, 105 54 | orig: 256, 128 55 | offset: 0, 22 56 | index: -1 57 | icon-arrow-left 58 | rotate: false 59 | xy: 270, 215 60 | size: 21, 15 61 | orig: 22, 22 62 | offset: 1, 4 63 | index: -1 64 | icon-arrow-right 65 | rotate: false 66 | xy: 318, 240 67 | size: 21, 15 68 | orig: 22, 22 69 | offset: 1, 4 70 | index: -1 71 | icon-close 72 | rotate: false 73 | xy: 300, 220 74 | size: 10, 10 75 | orig: 22, 22 76 | offset: 6, 6 77 | index: -1 78 | icon-close-titlebar 79 | rotate: false 80 | xy: 311, 220 81 | size: 10, 10 82 | orig: 22, 22 83 | offset: 6, 6 84 | index: -1 85 | icon-drive 86 | rotate: false 87 | xy: 297, 231 88 | size: 18, 5 89 | orig: 22, 22 90 | offset: 2, 8 91 | index: -1 92 | icon-file-audio 93 | rotate: false 94 | xy: 256, 100 95 | size: 14, 18 96 | orig: 22, 22 97 | offset: 4, 2 98 | index: -1 99 | icon-file-image 100 | rotate: false 101 | xy: 256, 64 102 | size: 14, 16 103 | orig: 22, 22 104 | offset: 4, 3 105 | index: -1 106 | icon-file-pdf 107 | rotate: false 108 | xy: 256, 81 109 | size: 14, 18 110 | orig: 22, 22 111 | offset: 4, 2 112 | index: -1 113 | icon-file-text 114 | rotate: false 115 | xy: 256, 47 116 | size: 14, 16 117 | orig: 22, 22 118 | offset: 4, 3 119 | index: -1 120 | icon-folder 121 | rotate: false 122 | xy: 416, 242 123 | size: 18, 13 124 | orig: 22, 22 125 | offset: 2, 6 126 | index: -1 127 | icon-folder-new 128 | rotate: false 129 | xy: 340, 240 130 | size: 18, 15 131 | orig: 22, 22 132 | offset: 2, 3 133 | index: -1 134 | icon-folder-parent 135 | rotate: false 136 | xy: 378, 241 137 | size: 18, 14 138 | orig: 22, 22 139 | offset: 2, 5 140 | index: -1 141 | icon-folder-star 142 | rotate: false 143 | xy: 397, 241 144 | size: 18, 14 145 | orig: 22, 22 146 | offset: 2, 4 147 | index: -1 148 | icon-list-settings 149 | rotate: false 150 | xy: 297, 237 151 | size: 20, 18 152 | orig: 22, 22 153 | offset: 2, 1 154 | index: -1 155 | icon-maximize 156 | rotate: false 157 | xy: 256, 1 158 | size: 12, 12 159 | orig: 22, 22 160 | offset: 5, 5 161 | index: -1 162 | icon-minimize 163 | rotate: false 164 | xy: 17, 5 165 | size: 10, 4 166 | orig: 22, 22 167 | offset: 6, 5 168 | index: -1 169 | icon-refresh 170 | rotate: false 171 | xy: 256, 30 172 | size: 14, 16 173 | orig: 22, 22 174 | offset: 4, 3 175 | index: -1 176 | icon-restore 177 | rotate: false 178 | xy: 256, 14 179 | size: 13, 15 180 | orig: 22, 22 181 | offset: 5, 3 182 | index: -1 183 | icon-star 184 | rotate: false 185 | xy: 435, 241 186 | size: 16, 14 187 | orig: 22, 22 188 | offset: 3, 4 189 | index: -1 190 | icon-star-outline 191 | rotate: false 192 | xy: 359, 239 193 | size: 18, 16 194 | orig: 22, 22 195 | offset: 2, 3 196 | index: -1 197 | icon-trash 198 | rotate: false 199 | xy: 497, 239 200 | size: 12, 16 201 | orig: 22, 22 202 | offset: 5, 3 203 | index: -1 204 | t-base 205 | rotate: false 206 | xy: 256, 119 207 | size: 12, 24 208 | split: 4, 4, 4, 4 209 | orig: 12, 24 210 | offset: 0, 0 211 | index: -1 212 | t-border 213 | rotate: false 214 | xy: 21, 1 215 | size: 3, 3 216 | split: 1, 1, 1, 1 217 | orig: 3, 3 218 | offset: 0, 0 219 | index: -1 220 | t-border-circle 221 | rotate: false 222 | xy: 452, 241 223 | size: 14, 14 224 | orig: 14, 14 225 | offset: 0, 0 226 | index: -1 227 | t-check 228 | rotate: false 229 | xy: 467, 241 230 | size: 14, 14 231 | orig: 14, 14 232 | offset: 0, 0 233 | index: -1 234 | t-check-tick 235 | rotate: false 236 | xy: 300, 209 237 | size: 10, 10 238 | orig: 14, 14 239 | offset: 2, 2 240 | index: -1 241 | t-dot 242 | rotate: false 243 | xy: 311, 211 244 | size: 1, 1 245 | orig: 1, 1 246 | offset: 0, 0 247 | index: -1 248 | t-hknob 249 | rotate: false 250 | xy: 10, 1 251 | size: 6, 8 252 | orig: 6, 24 253 | offset: 0, 8 254 | index: -1 255 | t-hline 256 | rotate: false 257 | xy: 318, 238 258 | size: 24, 1 259 | orig: 24, 1 260 | offset: 0, 0 261 | index: -1 262 | t-hslider 263 | rotate: false 264 | xy: 320, 215 265 | size: 1, 4 266 | orig: 1, 4 267 | offset: 0, 0 268 | index: -1 269 | t-radio 270 | rotate: false 271 | xy: 482, 241 272 | size: 14, 14 273 | orig: 14, 14 274 | offset: 0, 0 275 | index: -1 276 | t-radio-tick 277 | rotate: false 278 | xy: 1, 1 279 | size: 8, 8 280 | orig: 14, 14 281 | offset: 3, 3 282 | index: -1 283 | t-select 284 | rotate: false 285 | xy: 270, 231 286 | size: 26, 24 287 | split: 4, 15, 0, 24 288 | orig: 26, 24 289 | offset: 0, 0 290 | index: -1 291 | t-select-down 292 | rotate: false 293 | xy: 292, 211 294 | size: 7, 4 295 | orig: 7, 4 296 | offset: 0, 0 297 | index: -1 298 | t-select-up 299 | rotate: false 300 | xy: 28, 5 301 | size: 7, 4 302 | orig: 7, 4 303 | offset: 0, 0 304 | index: -1 305 | t-tree-minus 306 | rotate: false 307 | xy: 327, 232 308 | size: 5, 5 309 | orig: 8, 8 310 | offset: 1, 2 311 | index: -1 312 | t-tree-plus 313 | rotate: false 314 | xy: 322, 230 315 | size: 4, 7 316 | orig: 8, 8 317 | offset: 3, 0 318 | index: -1 319 | t-vknob 320 | rotate: false 321 | xy: 311, 213 322 | size: 8, 6 323 | orig: 24, 6 324 | offset: 8, 0 325 | index: -1 326 | t-vline 327 | rotate: false 328 | xy: 510, 231 329 | size: 1, 24 330 | orig: 1, 24 331 | offset: 0, 0 332 | index: -1 333 | t-vslider 334 | rotate: false 335 | xy: 378, 239 336 | size: 4, 1 337 | orig: 4, 1 338 | offset: 0, 0 339 | index: -1 340 | t-window 341 | rotate: false 342 | xy: 256, 144 343 | size: 10, 34 344 | split: 5, 4, 30, 3 345 | orig: 10, 34 346 | offset: 0, 0 347 | index: -1 348 | t-window-border 349 | rotate: false 350 | xy: 256, 179 351 | size: 10, 34 352 | split: 5, 4, 30, 3 353 | orig: 10, 34 354 | offset: 0, 0 355 | index: -1 356 | t-window-resize 357 | rotate: false 358 | xy: 256, 214 359 | size: 13, 41 360 | split: 2, 10, 30, 10 361 | orig: 13, 41 362 | offset: 0, 0 363 | index: -1 364 | --------------------------------------------------------------------------------