summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/subconscious/Game.java33
-rw-r--r--src/subconscious/graphics/BattleScene.java4
-rw-r--r--src/subconscious/graphics/GameWindow.java147
-rw-r--r--src/subconscious/graphics/MapEditorScene.java2
-rw-r--r--src/subconscious/graphics/MapScene.java16
-rw-r--r--src/subconscious/graphics/MenuScene.java70
-rw-r--r--src/subconscious/graphics/Palette.java1
-rw-r--r--src/subconscious/graphics/PauseScene.java42
-rw-r--r--src/subconscious/graphics/Scene.java97
9 files changed, 275 insertions, 137 deletions
diff --git a/src/subconscious/Game.java b/src/subconscious/Game.java
index 8e51a32..a6d42be 100644
--- a/src/subconscious/Game.java
+++ b/src/subconscious/Game.java
@@ -14,15 +14,17 @@ public class Game {
public enum State {
// main menu
MENU,
- // state in which the player is awake (real world)
- REALITY,
- // state in which the player is dreaming (imaginary world)
- DREAM,
+ // where actors can move freely
+ WORLD,
+ // where actors are constrained in the tiles
+ BATTLE,
// intermediate states
PAUSE, CLOSING,
}
private State state = State.MENU;
+ private State lastState;
+
private final Lock stateLock = new ReentrantLock();
private Condition stateChanged;
@@ -49,7 +51,8 @@ public class Game {
}
public void start() {
- this.setState(State.DREAM);
+ // TODO: change
+ this.setState(State.BATTLE);
}
public void update() {
@@ -82,6 +85,26 @@ public class Game {
return this.state;
}
+ public void togglePause() {
+ if (this.isPaused())
+ this.resume();
+ else
+ this.pause();
+ }
+
+ public void pause() {
+ this.lastState = this.state;
+ this.setState(State.PAUSE);
+ }
+
+ public void resume() {
+ this.setState(lastState);
+ }
+
+ public boolean isPaused() {
+ return this.state == State.PAUSE;
+ }
+
/* methods to manage map and map loading */
public Map getMap() {
return currentMap;
diff --git a/src/subconscious/graphics/BattleScene.java b/src/subconscious/graphics/BattleScene.java
index 0252dec..6cc38c9 100644
--- a/src/subconscious/graphics/BattleScene.java
+++ b/src/subconscious/graphics/BattleScene.java
@@ -56,8 +56,8 @@ public class BattleScene extends MapScene implements ActionListener {
private int realX = 0;
private int realY = 0;
- public BattleScene(Game g) {
- super(g);
+ public BattleScene(Game g, String uniqueName) {
+ super(g, uniqueName);
// TODO: this should be handled in MapScene
// MapLoader mapLoader = new MapLoader("../testmap.json");
diff --git a/src/subconscious/graphics/GameWindow.java b/src/subconscious/graphics/GameWindow.java
index b0f48fd..da69d40 100644
--- a/src/subconscious/graphics/GameWindow.java
+++ b/src/subconscious/graphics/GameWindow.java
@@ -2,8 +2,10 @@ package subconscious.graphics;
import subconscious.Game;
+import java.util.Stack;
+import java.util.AbstractMap.SimpleEntry;
+
import java.awt.Dimension;
-import java.awt.GridLayout;
import java.awt.CardLayout;
import java.awt.BorderLayout;
@@ -11,24 +13,24 @@ import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JButton;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
+import java.awt.event.WindowListener;
+import java.awt.event.WindowEvent;
/* GameWindow
* This class manages the graphical part of the game, such as loading and
* unloading scenes, and the window itself
*/
@SuppressWarnings("serial")
-public class GameWindow extends JFrame implements ActionListener {
+public class GameWindow extends JFrame implements WindowListener {
public static final Dimension WINDOW_SIZE = new Dimension(600, 400);
- public final String MENU_CARD = "menu";
- public final String SCENE_CARD = "scene";
-
private JPanel root;
+ // reference to the current scene which is NOT on the stack
private Scene scene = null;
private Thread sceneThread = null;
+ // stack of loaded scenes in the background that are paused
+ private Stack<SimpleEntry<Scene, Thread>> loadedScenes = new Stack<>();
private Game game;
@@ -44,8 +46,11 @@ public class GameWindow extends JFrame implements ActionListener {
this.setPreferredSize(WINDOW_SIZE);
this.setLocationRelativeTo(null);
+ this.addWindowListener(this);
+
// set up components
- this.buildUi();
+ // set up a cardlayout
+ this.root = new JPanel(new CardLayout());
this.add(this.root, BorderLayout.CENTER);
this.pack();
@@ -55,47 +60,33 @@ public class GameWindow extends JFrame implements ActionListener {
this.loop();
}
- private void buildUi() {
- // set up a cardlayout
- this.root = new JPanel(new CardLayout());
-
- // TODO: menu should be a Scene
- // build Menu card
- JPanel menu = new JPanel();
- menu.setLayout(new GridLayout(3, 1));
-
- JButton editor = new JButton("Editor");
- editor.setActionCommand("btn-editor");
- editor.addActionListener(this);
-
- JButton battle = new JButton("Battle");
- battle.setActionCommand("btn-battle");
- battle.addActionListener(this);
-
- JButton exit = new JButton("Exit");
- exit.setActionCommand("btn-exit");
- exit.addActionListener(this);
-
- menu.add(editor);
- menu.add(battle);
- menu.add(exit);
-
- // add cards
- this.root.add(menu, MENU_CARD);
- }
-
// ovserver of this.game
private void loop() {
+ // load the first scene
+ this.loadScene(new MenuScene(this.game, "mainmenu"));
+
while (this.game.isRunning()) {
Game.State newState = this.game.waitStateChange();
switch (newState) {
case MENU:
- this.loadMenu();
+ // unload all scenes to get the first one => menu
+ this.unloadAllScenes();
break;
- case DREAM:
- this.loadScene(this.scene);
+ // TODO: rewrite the following part
+ case BATTLE:
+ this.loadScene(new BattleScene(this.game, "battle1"));
+ break;
+
+ case PAUSE:
+ this.loadScene(new PauseScene(this.game, "pause"));
+ try {
+ this.sceneThread.join();
+ } catch (InterruptedException ex) {
+ ex.printStackTrace();
+ }
+ this.unloadScene();
break;
}
@@ -106,29 +97,40 @@ public class GameWindow extends JFrame implements ActionListener {
private void loadScene(Scene scene) {
// check to not reload the same scene
- // TODO: this may not be necessary, ex reaload scene?
if (this.scene == scene)
return;
+ // if a scene is loaded push it onto the loadedScenes stack
if (this.scene != null){
- this.unloadScene();
+ this.scene.pauseThread();
+ this.loadedScenes.push(new SimpleEntry<Scene, Thread>(scene, sceneThread));
}
// build thread
this.scene = scene;
// add to UI
- this.root.add(this.scene, SCENE_CARD);
- ((CardLayout) this.root.getLayout()).show(this.root, SCENE_CARD);
+ this.root.add(this.scene, this.scene.UNIQUE_NAME);
+ ((CardLayout) this.root.getLayout()).show(this.root, this.scene.UNIQUE_NAME);
// start scene
this.sceneThread = new Thread(this.scene);
// for debugging
- this.sceneThread.setName("Scene rendering Thread");
+ this.sceneThread.setName("Scene [" + scene.UNIQUE_NAME + "]");
this.sceneThread.start();
this.scene.updateCanvasSize(this.getSize());
}
+ private void unloadAllScenes() {
+ unloadScenes(this.loadedScenes.size() -1);
+ }
+
+ private void unloadScenes(int count) {
+ for (int i = 0; i < count; i++) {
+ this.unloadScene();
+ }
+ }
+
private void unloadScene() {
// close old scene
this.scene.stop();
@@ -142,44 +144,41 @@ public class GameWindow extends JFrame implements ActionListener {
// remove card from UI
this.root.remove(scene);
- this.scene = null;
- this.sceneThread = null;
- }
+ // TODO: this should probably be an error
+ if (this.loadedScenes.empty()) {
+ this.scene = null;
+ this.sceneThread = null;
+ return;
+ }
- // TODO: menu should become a Scene
- private void loadMenu() {
- if (this.scene != null)
- this.scene.pause();
+ // load the last scene
+ SimpleEntry<Scene, Thread> sceneEntry = this.loadedScenes.pop();
+ this.scene = sceneEntry.getKey();
+ this.sceneThread = sceneEntry.getValue();
- ((CardLayout) this.root.getLayout()).show(this.root, MENU_CARD);
+ ((CardLayout) this.root.getLayout()).show(this.root, this.scene.UNIQUE_NAME);
+ this.scene.resumeThread();
}
public void quit() {
+ this.unloadAllScenes();
this.setVisible(false);
-
- if (this.scene != null) {
- this.unloadScene();
- }
-
this.dispose();
}
- // Action Listener for menu
- @Override
- public void actionPerformed(ActionEvent e) {
- if (e.getActionCommand().startsWith("btn")) {
- if (e.getActionCommand().equals("btn-exit")) {
- this.game.quit();
- return;
- }
-
- if (e.getActionCommand().equals("btn-editor")) {
- this.loadScene(new MapEditorScene());
- } else if (e.getActionCommand().equals("btn-battle")) {
- this.loadScene(new BattleScene(this.game));
- this.game.start();
- }
+ /* window listener */
+ @Override public void windowActivated(WindowEvent e) {}
+ @Override public void windowClosed(WindowEvent e) {}
+ @Override public void windowClosing(WindowEvent e) {
+ this.quit();
+ }
- }
+ @Override
+ public void windowDeactivated(WindowEvent e) {
+ this.game.pause();
}
+
+ @Override public void windowDeiconified(WindowEvent e) {}
+ @Override public void windowIconified(WindowEvent e) {}
+ @Override public void windowOpened(WindowEvent e) {}
}
diff --git a/src/subconscious/graphics/MapEditorScene.java b/src/subconscious/graphics/MapEditorScene.java
index edf9323..0b6d8a8 100644
--- a/src/subconscious/graphics/MapEditorScene.java
+++ b/src/subconscious/graphics/MapEditorScene.java
@@ -46,7 +46,7 @@ public class MapEditorScene extends MapScene implements ActionListener {
private AffineTransform tx = new AffineTransform();
public MapEditorScene() {
- super();
+ super(null, "map-editor");
this.setLayout(new BorderLayout());
JPanel bottomPanel = new JPanel();
diff --git a/src/subconscious/graphics/MapScene.java b/src/subconscious/graphics/MapScene.java
index a16fcf3..87a6012 100644
--- a/src/subconscious/graphics/MapScene.java
+++ b/src/subconscious/graphics/MapScene.java
@@ -26,8 +26,6 @@ import java.awt.geom.NoninvertibleTransformException;
*/
@SuppressWarnings("serial")
public abstract class MapScene extends Scene {
-
- protected Game game;
// TODO: remove
// @Deprecated
@@ -52,8 +50,8 @@ public abstract class MapScene extends Scene {
protected double zoom = 1;
- public MapScene(Game g) {
- super(g);
+ public MapScene(Game g, String uniqueName) {
+ super(g, uniqueName);
this.game = g;
this.map = this.game.getMap();
@@ -66,12 +64,6 @@ public abstract class MapScene extends Scene {
this.tileSize = this.shorterCanvasLenght / 10;
}
-
- // This constructor is used ONLY for a MapEditorScene which does not use a
- // game object, and when the MapEditor will be removed this constructor
- // will also be deleted
- @Deprecated
- public MapScene() { }
protected void renderTiles(Graphics2D g) {
for (Tile tile : this.game.getMap().getGrid()) {
@@ -248,6 +240,10 @@ public abstract class MapScene extends Scene {
case KeyEvent.VK_RIGHT:
this.pan.x -= 10;
break;
+
+ case KeyEvent.VK_ESCAPE:
+ this.game.pause();
+ break;
}
}
diff --git a/src/subconscious/graphics/MenuScene.java b/src/subconscious/graphics/MenuScene.java
new file mode 100644
index 0000000..9c57ca6
--- /dev/null
+++ b/src/subconscious/graphics/MenuScene.java
@@ -0,0 +1,70 @@
+package subconscious.graphics;
+
+import subconscious.Game;
+
+import java.awt.Graphics2D;
+import java.awt.GridLayout;
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+
+import java.awt.event.ActionListener;
+import java.awt.event.ActionEvent;
+
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+import javax.swing.JButton;
+
+
+@SuppressWarnings("serial")
+public class MenuScene extends Scene implements ActionListener {
+ public MenuScene(Game g, String uniqueName) {
+ super(g, uniqueName);
+
+ this.game = g;
+
+ // build Menu card
+ this.setLayout(new GridLayout(3, 1));
+
+ JButton editor = new JButton("Editor");
+ editor.setActionCommand("btn-editor");
+ editor.addActionListener(this);
+
+ JButton battle = new JButton("Battle");
+ battle.setActionCommand("btn-battle");
+ battle.addActionListener(this);
+
+ JButton exit = new JButton("Exit");
+ exit.setActionCommand("btn-exit");
+ exit.addActionListener(this);
+
+ this.add(editor);
+ this.add(battle);
+ this.add(exit);
+ }
+
+ @Override
+ public void build() {}
+ @Override
+ public void update(long deltaNanoTime) {}
+ @Override
+ public void render(Graphics2D g) {}
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ if (e.getActionCommand().startsWith("btn")) {
+ if (e.getActionCommand().equals("btn-exit")) {
+ this.game.quit();
+ this.stop();
+ return;
+ }
+
+ if (e.getActionCommand().equals("btn-editor")) {
+ // TODO: fix editor
+ // this.loadScene(new MapEditorScene());
+ } else if (e.getActionCommand().equals("btn-battle")) {
+ this.game.start();
+ }
+
+ }
+ }
+} \ No newline at end of file
diff --git a/src/subconscious/graphics/Palette.java b/src/subconscious/graphics/Palette.java
index 75937a0..50e8372 100644
--- a/src/subconscious/graphics/Palette.java
+++ b/src/subconscious/graphics/Palette.java
@@ -4,6 +4,7 @@ import java.awt.Color;
public class Palette {
public final static Color BLACK = new Color(29, 31, 33);
+ public final static Color BLACK_T = new Color(29, 31, 33, 100);
public final static Color WHITE = new Color(197, 200, 198);
public final static Color WHITE_T = new Color(197, 200, 198, 200);
public final static Color RED = new Color(165, 66, 66);
diff --git a/src/subconscious/graphics/PauseScene.java b/src/subconscious/graphics/PauseScene.java
new file mode 100644
index 0000000..6365514
--- /dev/null
+++ b/src/subconscious/graphics/PauseScene.java
@@ -0,0 +1,42 @@
+package subconscious.graphics;
+
+import subconscious.Game;
+
+import java.awt.Graphics2D;
+import java.awt.BorderLayout;
+
+import java.awt.event.KeyEvent;
+
+
+@SuppressWarnings("serial")
+public class PauseScene extends Scene {
+ public PauseScene(Game g, String uniqueName) {
+ super(g, uniqueName);
+
+ // TODO: build on upper level
+ this.add(this.canvas, BorderLayout.CENTER);
+ }
+
+ protected void build() {
+
+ }
+
+ protected void render(Graphics2D g) {
+ g.setColor(Palette.BLACK);
+ g.fillRect(0, 0, this.canvasSize.width, this.canvasSize.height);
+ }
+
+ protected void update(long deltaNanoTime) {
+ }
+
+
+ @Override
+ public void keyReleased(KeyEvent e) {
+ super.keyReleased(e);
+
+ if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
+ this.game.resume();
+ this.stop();
+ }
+ }
+} \ No newline at end of file
diff --git a/src/subconscious/graphics/Scene.java b/src/subconscious/graphics/Scene.java
index bbc98eb..4132ded 100644
--- a/src/subconscious/graphics/Scene.java
+++ b/src/subconscious/graphics/Scene.java
@@ -35,14 +35,18 @@ import javax.swing.JPanel;
public abstract class Scene extends JPanel
implements Runnable, KeyListener, MouseListener, MouseMotionListener,
MouseWheelListener, ComponentListener {
-
+
+ // this is required to manage multiple scenes
+ public final String UNIQUE_NAME;
+
protected final long DESIRED_FPS = 60;
protected final long DESIRED_DELTA_LOOP = (1000*1000*1000)/DESIRED_FPS;
- protected Game game;
+ // Game is never be cached in the local thread
+ protected volatile Game game;
- protected boolean running;
- protected boolean paused;
+ protected volatile boolean running;
+ protected volatile boolean threadPaused;
protected Object pauseLock;
protected BufferStrategy buffer;
@@ -52,12 +56,14 @@ public abstract class Scene extends JPanel
// TODO: make accessible from user settings
protected int guiSize = 2;
- protected Scene() {
- this.game = null;
-
+ public Scene(Game game, String uniqueName) {
+
+ this.UNIQUE_NAME = uniqueName;
+ this.game = game;
+
// thread control
this.running = false;
- this.paused = false;
+ this.threadPaused = false;
this.pauseLock = new Object();
// watch for resize
@@ -75,22 +81,17 @@ public abstract class Scene extends JPanel
this.canvas.addMouseMotionListener(this);
this.canvas.addMouseWheelListener(this);
+ // TODO: solve canvas
+ // this.add(this.canvas, BorderLayout.CENTER);
this.build();
}
- protected Scene(Game g) {
- // call default contructor
- this();
-
- this.game = g;
- }
-
/* abstract methods */
// runs when the the scene thread starts
protected abstract void build();
// runs on each tick
- protected abstract void render(Graphics2D g);
protected abstract void update(long deltaNanoTime);
+ protected abstract void render(Graphics2D g);
public void run() {
long beginLoopTime;
@@ -99,28 +100,22 @@ public abstract class Scene extends JPanel
long lastUpdateTime;
long deltaLoop;
+ this.running = true;
+ this.threadPaused = false;
+
+ // wait for the canvas to be ready
+ // this is required otherwise createBufferStrategy()
+ // throws an IllegalStateException
+ while (!this.canvas.isDisplayable() && running);
+
// initialize canvas buffer
this.canvas.createBufferStrategy(2);
this.buffer = this.canvas.getBufferStrategy();
this.canvas.requestFocus();
- this.running = true;
- this.paused = false;
-
while (running) {
beginLoopTime = System.nanoTime();
- synchronized (this.pauseLock) {
- if (this.paused) {
- try {
- this.pauseLock.wait();
- } catch (InterruptedException ex) {
- ex.printStackTrace();
- break;
- }
- }
- }
-
do {
do {
Graphics2D g = (Graphics2D) this.buffer.getDrawGraphics();
@@ -131,7 +126,13 @@ public abstract class Scene extends JPanel
// repeat if the drawing buffer contents were lost
} while (this.buffer.contentsLost());
- this.buffer.show();
+ try {
+ this.buffer.show();
+ } catch (IllegalStateException ex) {
+ // this happens when the scene is hidden or the frame is disposed
+ // for example then the thread is stopped, and so this exception
+ // can be ignored
+ }
lastUpdateTime = currentUpdateTime;
currentUpdateTime = System.nanoTime();
@@ -151,25 +152,35 @@ public abstract class Scene extends JPanel
}
}
+
+ synchronized (this.pauseLock) {
+ if (this.threadPaused) {
+ try {
+ this.pauseLock.wait();
+ } catch (InterruptedException ex) {
+ ex.printStackTrace();
+ }
+ }
+ }
}
}
- public synchronized void stop() {
- this.running = false;
-
+ /* game pause controls */
+ public void stop() {
// stop thread even if paused
- this.resume();
+ this.resumeThread();
+ this.running = false;
}
- public synchronized void pause() {
- this.paused = true;
+ public void pauseThread() {
+ this.threadPaused = true;
}
- public synchronized void resume() {
- if (!paused)
+ public void resumeThread() {
+ if (!this.threadPaused)
return;
- this.paused = false;
+ this.threadPaused = false;
synchronized (this.pauseLock) {
this.pauseLock.notifyAll();
@@ -192,12 +203,8 @@ public abstract class Scene extends JPanel
/* key listener */
@Override public void keyTyped(KeyEvent e) {}
@Override public void keyPressed(KeyEvent e) {}
+ @Override public void keyReleased(KeyEvent e) {}
- @Override
- public void keyReleased(KeyEvent e) {
- // TODO: Escape pauses the game
- }
-
/* mouse listener */
@Override public void mouseClicked(MouseEvent e) {}
@Override public void mouseEntered(MouseEvent e) {}