diff --git a/README.md b/README.md index 09ec93a..d687ac2 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,31 @@ This game engine was developed to create and play role-playing games with jump-n The engine is currently still at an early stage, which means that essential functions may still be missing. A rough overview of the available features can be found in the table below. -To download and try out the editor/engine, you can download it from this website: [Download](https://sunnix.de/downloads). +To download and try out the editor/engine, you can download it from this [repo](https://github.com/Sunnnix/Sunnixs_RPG_Engine/releases/tag/V0.6) or from this website: [Download](https://sunnix.de/downloads). > [!NOTE] > The engine does not contain any graphics, music, or sounds; you have to add them yourself! ## Patch Notes +
+ V0.6 + +- Objects with events and components + - Events + - Move + - Wait + - Message + - Play Sound + - Components + - Render +- Event controlled textbox +- Object animation V1 +- Audio System for playing Sounds +- Object states + +
+
V0.5 @@ -62,23 +80,23 @@ To download and try out the editor/engine, you can download it from this website ## Supported Features -| Description | Since | -|---------------------------------|---------| -| Set BGM from Map | 0.4 | -| Editor multi-language support | 0.4 | -| Load custom tilesets | 0.3 | -| Create 3D maps with walls | 0.2 | -| Start the game from the editor | 0.1 | +| Description | Since | +|------------------------------------------|-------| +| Audio System V2 (playable Sounds) | 0.6 | +| Sprite animation | 0.6 | +| Dynamic Object properties via Components | 0.6 | +| Object control via Events | 0.6 | +| Objects | 0.6 | +| Audio System V1 (only BGM) | 0.4 | +| Editor multi-language support | 0.4 | +| Load custom tilesets | 0.3 | +| Create 3D maps with walls | 0.2 | +| Start the game from the editor | 0.1 | ## Upcoming Features | Description | Planned | Progress | Priority | |-------------------------------------------------------|---------|----------|----------| -| Objects with events and components | 0.6 | 90% | High | -| Event controlled textbox | 0.6 | 100% | High | -| Object animation V1 | 0.6 | 100% | High | -| Audio System for playing Sounds | 0.6 | 100% | High | -| Object states | 0.6 | 100% | High | | Tile animation | 0.7 | 0% | Medium | | Physics System | 0.7 | 0% | High | | Map Transition System / Teleporter objects and events | 0.7 | 0% | High | diff --git a/editor/res/de/sunnix/srpge/editor/lang/en.lang b/editor/res/de/sunnix/srpge/editor/lang/en.lang index 756ab8b..cf30b3a 100644 --- a/editor/res/de/sunnix/srpge/editor/lang/en.lang +++ b/editor/res/de/sunnix/srpge/editor/lang/en.lang @@ -254,6 +254,8 @@ view.dialog_resources.audio.default_value=Default Volume: # States View view.dialog_resources.variables.states.id=ID view.dialog_resources.variables.states.prio=Priority +view.dialog_resources.variables.states.add_state=Add State +view.dialog_resources.variables.states.remove_state=Remove State # Dialog Language dialog.language.reload_editor.text=The new Language Pack has been loaded and is now being used. However, in order to update all texts, you need to restart the editor. @@ -268,6 +270,9 @@ dialog.language.no_lang_selected=No Language Pack selected! dialog.language.lang_not_found=Language Pack not found! dialog.language.en_fallback=Use English if translation is missing +# Dialog Player +dialog.player.sprite.title=Edit player sprites + # Dialog Objects dialog_object.title=Edit Object dialog_object.add_event=Add Event @@ -275,6 +280,20 @@ dialog_object.edit_event=Edit Event dialog_object.remove_event=Remove Event dialog_object.remove_component.text=Are you sure you want to delete this component?\nAll settings for this component will be lost dialog_object.remove_component.title=Delete component +dialog_object.add_component=+ Add Component + +# Components +component.render =Renderer +component.render.set_sprite=Set Sprite +component.render.set_state_sprite=Set State Sprites +component.render.state_dialog=State Sprite Editor +component.render.state_dialog.add=Add State +component.render.state_dialog.remove=Remove State +component.render.state_dialog.no_state=No state availible! +component.render.state_dialog.title=States +component.render.state_dialog.select=Select state +component.render.state_dialog.select_sprite.title=Select Sprite +component.render.state_dialog.select_sprite.text=Select Sprite for State: # Events event_dialog.edit=Edit %s diff --git a/editor/src/de/sunnix/srpge/editor/window/Window.java b/editor/src/de/sunnix/srpge/editor/window/Window.java index 84fb47a..914af86 100644 --- a/editor/src/de/sunnix/srpge/editor/window/Window.java +++ b/editor/src/de/sunnix/srpge/editor/window/Window.java @@ -87,6 +87,7 @@ public class Window extends JFrame { private boolean showGrid = true; @Getter + @Setter private GameObject player; public Window(){ diff --git a/editor/src/de/sunnix/srpge/editor/window/menubar/PlayerSpriteManager.java b/editor/src/de/sunnix/srpge/editor/window/menubar/PlayerSpriteManager.java index 56a68b9..87d1b97 100644 --- a/editor/src/de/sunnix/srpge/editor/window/menubar/PlayerSpriteManager.java +++ b/editor/src/de/sunnix/srpge/editor/window/menubar/PlayerSpriteManager.java @@ -10,20 +10,19 @@ import java.awt.event.WindowListener; import static de.sunnix.srpge.editor.lang.Language.getString; -import static de.sunnix.srpge.editor.lang.Language.removeLanguagePack; public class PlayerSpriteManager extends JDialog { private boolean loopShouldStop; public PlayerSpriteManager(Window window){ - super(window, "Edit player sprites", true); - var mainPanel = new JPanel(new BorderLayout(0, 5)); + super(window, getString("dialog.player.sprite.title"), true); + var mainPanel = new JPanel(new BorderLayout(0, 10)); mainPanel.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); setContentPane(mainPanel); var player = window.getPlayer(); - var component = player.getComponent(RenderComponent.class); + var component = player.getComponent(RenderComponent.class).clone(); var centerPanel = new JPanel(); centerPanel.setLayout(new BoxLayout(centerPanel, BoxLayout.Y_AXIS)); centerPanel.setPreferredSize(new Dimension(180, 250)); @@ -33,7 +32,8 @@ public PlayerSpriteManager(Window window){ var loop = new Thread(() -> { while(!loopShouldStop){ try { - loopFunction.run(); + if(loopFunction != null) + loopFunction.run(); Thread.sleep(16, 666666); } catch (InterruptedException e) { throw new RuntimeException(e); @@ -43,6 +43,24 @@ public PlayerSpriteManager(Window window){ loop.setDaemon(true); loop.start(); + + var buttonsPanel = new JPanel(new FlowLayout(FlowLayout.CENTER, 10, 0)); + + var btnApply = new JButton(getString("button.apply")); + btnApply.addActionListener(l -> { + player.getComponents().removeIf(c -> c.ID.equals(component.ID)); + player.getComponents().add(component); + window.setProjectChanged(); + dispose(); + }); + var btnCancel = new JButton(getString("button.cancel")); + btnCancel.addActionListener(l -> dispose()); + + buttonsPanel.add(btnApply); + buttonsPanel.add(btnCancel); + + mainPanel.add(buttonsPanel, BorderLayout.SOUTH); + addWindowListener(creatwWindowListener()); setResizable(false); diff --git a/editor/src/de/sunnix/srpge/editor/window/menubar/resource/SpriteView.java b/editor/src/de/sunnix/srpge/editor/window/menubar/resource/SpriteView.java index d9ad919..cbf921e 100644 --- a/editor/src/de/sunnix/srpge/editor/window/menubar/resource/SpriteView.java +++ b/editor/src/de/sunnix/srpge/editor/window/menubar/resource/SpriteView.java @@ -84,7 +84,7 @@ private JPanel genTexturePanel(){ setBorder(BorderFactory.createBevelBorder(BevelBorder.LOWERED)); addMouseListener(new MouseAdapter() { @Override - public void mouseClicked(MouseEvent e) { + public void mousePressed(MouseEvent e) { var sprite = getCurrentSprite(); if(sprite == null) return; @@ -115,6 +115,7 @@ public void mouseClicked(MouseEvent e) { mY /= spriteHeight; sprite.getPattern(direction.getSelectedIndex()).add(mX + mY * texture.getWidth()); + window.setProjectChanged(); direction.setSelectedIndex(direction.getSelectedIndex()); // reload list } }); @@ -185,6 +186,7 @@ private JPanel genProperties(){ var split = name.split("/"); c.setSelectedItem(split[0]); }); + addAL(imageCategory, l -> window.setProjectChanged()); pPanel.add(imageCategory, gbc); gbc.gridy++; imageResource = createPropertieComp(boxes[1], (c, s) -> { @@ -238,7 +240,10 @@ private JPanel genProperties(){ var sprite = getCurrentSprite(); if (sprite == null) return; - sprite.setDirectionType((Sprite.DirectionType) directionType.getSelectedItem()); + if(!sprite.getDirectionType().equals(directionType.getSelectedItem())) { + sprite.setDirectionType((Sprite.DirectionType) directionType.getSelectedItem()); + window.setProjectChanged(); + } reloadSprite(); }); animPanel.add(directionType, gbc2); @@ -256,7 +261,10 @@ private JPanel genProperties(){ var sprite = getCurrentSprite(); if (sprite == null) return; - sprite.setAnimationType((Sprite.AnimationType) animType.getSelectedItem()); + if(!sprite.getAnimationType().equals(animType.getSelectedItem())) { + sprite.setAnimationType((Sprite.AnimationType) animType.getSelectedItem()); + window.setProjectChanged(); + } reloadSprite(); }); animPanel.add(animType, gbc2); @@ -296,6 +304,7 @@ public void keyPressed(KeyEvent e) { return; sprite.getPattern(direction.getSelectedIndex()).remove(index); spriteListModel.remove(index); + window.setProjectChanged(); spriteList.setSelectedIndex(index - (spriteListModel.getSize() > index ? 0 : 1)); } case KeyEvent.VK_UP -> { @@ -309,6 +318,7 @@ public void keyPressed(KeyEvent e) { pattern.add(index - 1, tex); spriteListModel.remove(index); spriteListModel.add(index - 1, tex); + window.setProjectChanged(); spriteList.setSelectedIndex(index - 1); e.consume(); } @@ -323,6 +333,7 @@ public void keyPressed(KeyEvent e) { pattern.add(index + 1, tex); spriteListModel.remove(index); spriteListModel.add(index + 1, tex); + window.setProjectChanged(); spriteList.setSelectedIndex(index + 1); e.consume(); } @@ -354,8 +365,6 @@ public void keyPressed(KeyEvent e) { pPanel.add(showIndex, gbc); gbc.gridy++; -// pPanel.add(new JLabel(getString("view.dialog_resources.sprite.animation_delay")), gbc); -// gbc.gridy++; animSpeed = createPropertieComp(new NumberPicker(getString("view.dialog_resources.sprite.animation_speed"), 1, 0, 1, Integer.MAX_VALUE), (c, s) -> { if(s == null) { animSpeed.setValue(1, true); @@ -368,13 +377,14 @@ public void keyPressed(KeyEvent e) { if(sprite == null) return; sprite.setAnimationSpeed(animSpeed.getValue()); + window.setProjectChanged(); }); pPanel.add(animSpeed, gbc); gbc.gridy++; - var mPanel = new JPanel(new BorderLayout()); + var mPanel = new JPanel(new FlowLayout()); mPanel.setPreferredSize(new Dimension(200, 0)); - mPanel.add(pPanel, BorderLayout.NORTH); + mPanel.add(pPanel); var scroll = new JScrollPane(mPanel); scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); diff --git a/editor/src/de/sunnix/srpge/editor/window/menubar/resource/StatesView.java b/editor/src/de/sunnix/srpge/editor/window/menubar/resource/StatesView.java index a4d7903..87b3537 100644 --- a/editor/src/de/sunnix/srpge/editor/window/menubar/resource/StatesView.java +++ b/editor/src/de/sunnix/srpge/editor/window/menubar/resource/StatesView.java @@ -19,7 +19,10 @@ public class StatesView extends JPanel { + private Window window; + public StatesView(Window window, JPanel parent) { + this.window = window; setLayout(new BorderLayout()); add(new JScrollPane(createTable()), BorderLayout.CENTER); @@ -63,14 +66,18 @@ public Component getTableCellRendererComponent(JTable table, Object value, boole // sorter.toggleSortOrder(1); var popupMenu = new JPopupMenu(); - var addItem = new JMenuItem("Add State"); - var removeItem = new JMenuItem("Remove State"); + var addItem = new JMenuItem(getString("view.dialog_resources.variables.states.add_state")); + var removeItem = new JMenuItem(getString("view.dialog_resources.variables.states.remove_state")); - addItem.addActionListener(e -> ((TableModel) table.getModel()).addNewState()); + addItem.addActionListener(e -> { + ((TableModel) table.getModel()).addNewState(); + window.setProjectChanged(); + }); removeItem.addActionListener(e -> { int row = table.getSelectedRow(); if (row != -1) { ((TableModel) table.getModel()).removeState(row); + window.setProjectChanged(); } }); @@ -148,9 +155,9 @@ public void setValueAt(Object aValue, int rowIndex, int columnIndex) { var id = (String) aValue; if(id.equals(state.id()) || id.isBlank() || states.stream().anyMatch(x -> id.equals(x.id()))) yield state; - yield States.changeStateId(state, id); + yield States.changeStateId(window, state, id); } - case 1 -> States.changeStatePrio(state, Integer.parseInt((String) aValue)); + case 1 -> States.changeStatePrio(window, state, Integer.parseInt((String) aValue)); default -> state; }); fireTableDataChanged(); @@ -167,7 +174,7 @@ public void addNewState() { public void removeState(int row){ var value = (String) getValueAt(row, 0); - states.remove(States.removeState(value)); + states.remove(States.removeState(window, value)); fireTableDataChanged(); } diff --git a/editor/src/de/sunnix/srpge/editor/window/object/ObjectEditDialog.java b/editor/src/de/sunnix/srpge/editor/window/object/ObjectEditDialog.java index 9b45894..7142697 100644 --- a/editor/src/de/sunnix/srpge/editor/window/object/ObjectEditDialog.java +++ b/editor/src/de/sunnix/srpge/editor/window/object/ObjectEditDialog.java @@ -133,7 +133,7 @@ private JComponent createComponentPanel(){ componentList = new ArrayList<>(object.getComponents().stream().map(Component::clone).toList()); componentsView = panel; - var addbtn = new JButton("+ Add Component"); + var addbtn = new JButton(getString("dialog_object.add_component")); addbtn.setMaximumSize(new Dimension(Integer.MAX_VALUE, addbtn.getMinimumSize().height)); addbtn.addActionListener(a -> { var component = ComponentCreateDialog.show(this, object); diff --git a/editor/src/de/sunnix/srpge/editor/window/object/States.java b/editor/src/de/sunnix/srpge/editor/window/object/States.java index a972960..8222096 100644 --- a/editor/src/de/sunnix/srpge/editor/window/object/States.java +++ b/editor/src/de/sunnix/srpge/editor/window/object/States.java @@ -1,6 +1,7 @@ package de.sunnix.srpge.editor.window.object; import de.sunnix.sdso.DataSaveObject; +import de.sunnix.srpge.editor.window.Window; import de.sunnix.srpge.engine.ecs.State; import java.util.ArrayList; @@ -53,17 +54,26 @@ public static DataSaveObject save(DataSaveObject dso){ return dso; } - public static State changeStateId(State state, String newID) { + public static State changeStateId(Window window, State state, String newID) { newID = newID.toLowerCase(); + if(state.id().equals(newID)) + return state; states.remove(state.id()); var newState = new State(newID, state.priority()); states.put(newID, newState); + if(window != null) + window.setProjectChanged(); return newState; } - public static State changeStatePrio(State state, int newPrio) { + public static State changeStatePrio(Window window, State state, int newPrio) { + var curState = states.get(state.id()); + if(curState != null && curState.priority() == newPrio) + return state; var newState = new State(state.id(), newPrio); states.put(state.id(), newState); + if(window != null) + window.setProjectChanged(); return newState; } @@ -72,10 +82,12 @@ public static boolean hasStateID(String id) { return states.containsKey(id); } - public static State removeState(String id) { + public static State removeState(Window window, String id) { id = id.toLowerCase(); if(!isRemovable(id)) throw new RuntimeException("State " + id + " can't be removed!"); + if(window != null) + window.setProjectChanged(); return states.remove(id); } } diff --git a/editor/src/de/sunnix/srpge/editor/window/object/components/RenderComponent.java b/editor/src/de/sunnix/srpge/editor/window/object/components/RenderComponent.java index 2e31ed1..7382692 100644 --- a/editor/src/de/sunnix/srpge/editor/window/object/components/RenderComponent.java +++ b/editor/src/de/sunnix/srpge/editor/window/object/components/RenderComponent.java @@ -31,7 +31,7 @@ public RenderComponent() { @Override public String genName() { - return "Renderer"; + return getString("component.render"); } @Override @@ -103,7 +103,7 @@ public void paint(Graphics g) { }, 250); setSpriteBtn.addActionListener(a -> { - var newSprite = window.getSingleton(Resources.class).sprites.showSelectDialogSinglePath(parent, "Select sprite", null, "Sprite", defaultSprite); + var newSprite = window.getSingleton(Resources.class).sprites.showSelectDialogSinglePath(parent, getString("component.render.set_sprite"), null, getString("name.sprite"), defaultSprite); if(newSprite == null) return; defaultSprite = newSprite; @@ -111,7 +111,7 @@ public void paint(Graphics g) { parent.repaint(); }); - var setStateSpritesBtn = addView(parent, new JButton("Set State Sprites")); + var setStateSpritesBtn = addView(parent, new JButton(getString("component.render.set_state_sprite"))); setStateSpritesBtn.addActionListener(l -> new StateSpriteEditDialog(window, parent, stateSprites)); return () -> { @@ -144,7 +144,7 @@ private static class StateSpriteEditDialog extends JDialog { private final Map stateSprites; public StateSpriteEditDialog(Window window, JPanel parent, HashMap stateSprites){ - super(DialogUtils.getWindowForComponent(parent), "State Sprite Editor", ModalityType.APPLICATION_MODAL); + super(DialogUtils.getWindowForComponent(parent), getString("component.render.state_dialog"), ModalityType.APPLICATION_MODAL); this.window = window; this.parent = parent; var content = new JPanel(new BorderLayout(5, 5)); @@ -206,17 +206,17 @@ else if(index % 2 == 0) { private MouseAdapter genMouseListener(DefaultListModel> model, JList> list){ var popup = new JPopupMenu(); - var addMenu = new JMenuItem("Add State"); - var removeMenu = new JMenuItem("Remove State"); + var addMenu = new JMenuItem(getString("component.render.state_dialog.add")); + var removeMenu = new JMenuItem(getString("component.render.state_dialog.remove")); addMenu.addActionListener(a -> { var states = States.getStates().stream().filter(x -> !stateSprites.containsKey(x.id())).toList(); if(states.isEmpty()) { - JOptionPane.showMessageDialog(parent, "No state availible!", "States", JOptionPane.ERROR_MESSAGE); + JOptionPane.showMessageDialog(parent, getString("component.render.state_dialog.no_state"), getString("component.render.state_dialog.title"), JOptionPane.ERROR_MESSAGE); return; } var statestrings = states.stream().map(x -> String.format("(%s) %s", x.priority(), x.id())).toArray(String[]::new); - var selection = (String) JOptionPane.showInputDialog(parent, "Select state", "States", JOptionPane.PLAIN_MESSAGE, null, statestrings, statestrings[0]); + var selection = (String) JOptionPane.showInputDialog(parent, getString("component.render.state_dialog.select"), getString("component.render.state_dialog.title"), JOptionPane.PLAIN_MESSAGE, null, statestrings, statestrings[0]); if(selection == null) return; var state = states.get(Arrays.stream(statestrings).toList().indexOf(selection)); @@ -246,9 +246,9 @@ public void mousePressed(MouseEvent e) { var selection = window.getSingleton(Resources.class).sprites .showSelectDialog( StateSpriteEditDialog.this.rootPane, - "Select Sprite", - "Select Sprite for State:", - "Sprite", + getString("component.render.state_dialog.select_sprite.title"), + getString("component.render.state_dialog.select_sprite.text"), + getString("name.sprite"), stateSprites.get(list.getSelectedValue().getValue() ) ); @@ -290,4 +290,12 @@ private JPanel createButtons(HashMap originalMap) { } } + + @Override + public Component clone() { + var comp = (RenderComponent) super.clone(); + comp.stateSprites = new HashMap<>(comp.stateSprites); + return comp; + } + }