diff --git a/src/mightypork/rogue/App.java b/src/mightypork/rogue/App.java
index b3f18c6..98203cb 100644
--- a/src/mightypork/rogue/App.java
+++ b/src/mightypork/rogue/App.java
@@ -1,93 +1,57 @@
package mightypork.rogue;
-import static org.lwjgl.opengl.GL11.*;
-
import java.io.File;
import java.io.RandomAccessFile;
-import java.nio.ByteBuffer;
import java.nio.channels.FileLock;
import javax.swing.JOptionPane;
-import mightypork.rogue.input.Keys;
-import mightypork.rogue.screens.ScreenSplash;
-import mightypork.rogue.sounds.SoundManager;
-import mightypork.rogue.threads.ThreadSaveScreenshot;
-import mightypork.rogue.threads.ThreadScreenshotTrigger;
+import mightypork.rogue.display.DisplaySystem;
+import mightypork.rogue.display.Screen;
+import mightypork.rogue.display.ScreenSplash;
+import mightypork.rogue.input.InputSystem;
+import mightypork.rogue.input.KeyStroke;
+import mightypork.rogue.input.events.MouseMotionEvent;
+import mightypork.rogue.sounds.SoundSystem;
+import mightypork.rogue.tasks.TaskTakeScreenshot;
+import mightypork.rogue.util.Utils;
import mightypork.utils.logging.Log;
import mightypork.utils.logging.LogInstance;
-import mightypork.utils.math.coord.Coord;
+import mightypork.utils.patterns.Destroyable;
+import mightypork.utils.patterns.subscription.MessageBus;
import mightypork.utils.time.TimerDelta;
import mightypork.utils.time.TimerInterpolating;
-import org.lwjgl.BufferUtils;
-import org.lwjgl.LWJGLException;
import org.lwjgl.input.Keyboard;
-import org.lwjgl.input.Mouse;
-import org.lwjgl.openal.AL;
import org.lwjgl.opengl.Display;
-import org.lwjgl.opengl.DisplayMode;
-public class App {
+public class App implements Destroyable {
- /** instance */
- public static App inst;
- /** Current delta time (secs since last render) */
- public static double currentDelta = 0;
+ /** instance pointer */
+ private static App inst;
- private static DisplayMode windowDisplayMode = null;
+ private InputSystem input;
+ private SoundSystem sounds;
+ private DisplaySystem display;
+ private MessageBus events;
/** current screen */
- public static Screen screen = null;
+ private Screen screen;
/** Flag that screenshot is scheduled to be taken next loop */
- public static boolean scheduledScreenshot = false;
-
-
- private static boolean lockInstance()
- {
- if (Config.SINGLE_INSTANCE == false) return true; // bypass lock
-
- final File lockFile = new File(Paths.WORKDIR, ".lock");
- try {
- final RandomAccessFile randomAccessFile = new RandomAccessFile(lockFile, "rw");
- final FileLock fileLock = randomAccessFile.getChannel().tryLock();
- if (fileLock != null) {
- Runtime.getRuntime().addShutdownHook(new Thread() {
-
- @Override
- public void run()
- {
- try {
- fileLock.release();
- randomAccessFile.close();
- lockFile.delete();
- } catch (Exception e) {
- System.out.println("Unable to remove lock file.");
- e.printStackTrace();
- }
- }
- });
- return true;
- }
- } catch (Exception e) {
- System.out.println("Unable to create and/or lock file.");
- e.printStackTrace();
- }
- return false;
- }
+ private boolean scheduledScreenshot = false;
/**
- * Is if FS
+ * Get the instance
*
- * @return is in fs
+ * @return instance of App
*/
- public static boolean isFullscreen()
+ public static App inst()
{
- return Display.isFullscreen();
+ return inst;
}
@@ -116,24 +80,19 @@ public class App {
*/
public static void onCrash(Throwable error)
{
- Log.e("The game has crashed.");
-
- Log.e(error);
+ Log.e("The game has crashed.", error);
- try {
- inst.deinit();
- } catch (Throwable t) {
- // ignore
- }
+ inst.exit();
}
/**
- * Quit to OS
+ * Quit to OS
+ * Destroy app & exit VM
*/
public void exit()
{
- deinit();
+ destroy();
System.exit(0);
}
@@ -143,58 +102,36 @@ public class App {
*
* @return screen
*/
- public Screen getScreen()
+ public Screen getCurrentScreen()
{
return screen;
}
- /**
- * Get screen size
- *
- * @return size
- */
- public Coord getSize()
+ public void initialize()
{
- return new Coord(Display.getWidth(), Display.getHeight());
+ Log.i("Initializing subsystems");
+ initLock();
+ initBus();
+ initLogger();
+ initDisplay();
+ initSound();
+ initInput();
}
- private void init() throws LWJGLException
+ @Override
+ public void destroy()
{
- // initialize main logger
- LogInstance li = Log.create("runtime", Paths.LOGS, 10);
- li.enable(Config.LOGGING_ENABLED);
- li.enableSysout(Config.LOG_TO_STDOUT);
-
- // initialize display
- Display.setDisplayMode(windowDisplayMode = new DisplayMode(Const.WINDOW_SIZE_X, Const.WINDOW_SIZE_Y));
- Display.setResizable(true);
- Display.setVSyncEnabled(true);
- Display.setTitle(Const.TITLEBAR);
- Display.create();
-
- if (Config.START_IN_FS) {
- switchFullscreen();
- Display.update();
- }
-
- // initialize inputs
- Mouse.create();
- Keyboard.create();
- Keyboard.enableRepeatEvents(false);
-
- // initialize sound system
- SoundManager.get().setListener(Const.LISTENER_POS);
- SoundManager.get().setMasterVolume(1F);
-
- // start async screenshot trigger listener
- (new ThreadScreenshotTrigger()).start();
+ if (sounds != null) sounds.destroy();
+ if (input != null) input.destroy();
+ if (display != null) display.destroy();
}
- private void start() throws LWJGLException
+ private void initLock()
{
+ if (!Config.SINGLE_INSTANCE) return;
if (!lockInstance()) {
System.out.println("Working directory is locked.\nOnly one instance can run at a time.");
@@ -211,207 +148,235 @@ public class App {
exit();
return;
}
+ }
- init();
- mainLoop();
- deinit();
+
+ private boolean lockInstance()
+ {
+ final File lockFile = new File(Paths.WORKDIR, ".lock");
+ try {
+ final RandomAccessFile randomAccessFile = new RandomAccessFile(lockFile, "rw");
+ final FileLock fileLock = randomAccessFile.getChannel().tryLock();
+ if (fileLock != null) {
+ Runtime.getRuntime().addShutdownHook(new Thread() {
+
+ @Override
+ public void run()
+ {
+ try {
+ fileLock.release();
+ randomAccessFile.close();
+ lockFile.delete();
+ } catch (Exception e) {
+ System.err.println("Unable to remove lock file.");
+ e.printStackTrace();
+ }
+ }
+ });
+ return true;
+ }
+ } catch (Exception e) {
+ System.err.println("Unable to create and/or lock file.");
+ e.printStackTrace();
+ }
+ return false;
}
- private void deinit()
+ /**
+ * initialize inputs
+ */
+ private void initBus()
{
- Display.destroy();
- Mouse.destroy();
- Keyboard.destroy();
- SoundManager.get().destroy();
- AL.destroy();
+ events = new MessageBus();
+ events.addSubscriber(this);
+ }
+
+
+ /**
+ * initialize sound system
+ */
+ private void initSound()
+ {
+ sounds = new SoundSystem();
+ sounds.setMasterVolume(1);
+ }
+
+
+ /**
+ * initialize inputs
+ */
+ private void initInput()
+ {
+ input = new InputSystem();
+
+ input.bindKeyStroke(new KeyStroke(Keyboard.KEY_F2), new Runnable() {
+
+ @Override
+ public void run()
+ {
+ Log.f3("F2, taking screenshot.");
+ scheduledScreenshot = true;
+ }
+ });
+
+ input.bindKeyStroke(new KeyStroke(false, Keyboard.KEY_F11), new Runnable() {
+
+ @Override
+ public void run()
+ {
+ Log.f3("F11, toggling fullscreen.");
+ display.switchFullscreen();
+ }
+ });
+
+ input.bindKeyStroke(new KeyStroke(Keyboard.KEY_LCONTROL, Keyboard.KEY_Q), new Runnable() {
+
+ @Override
+ public void run()
+ {
+ Log.f3("CTRL+Q, shutting down.");
+ exit();
+ }
+ });
+ }
+
+
+ /**
+ * initialize display
+ */
+ private void initDisplay()
+ {
+ display = new DisplaySystem();
+ display.createMainWindow(Const.WINDOW_W, Const.WINDOW_H, true, Config.START_IN_FS, Const.TITLEBAR);
+ display.setTargetFps(Const.FPS_RENDER);
+ }
+
+
+ /**
+ * initialize main logger
+ */
+ private void initLogger()
+ {
+ LogInstance li = Log.create("runtime", Paths.LOGS, 10);
+ li.enable(Config.LOGGING_ENABLED);
+ li.enableSysout(Config.LOG_TO_STDOUT);
+ }
+
+
+ private void start()
+ {
+ initialize();
+ mainLoop();
+ exit();
}
/** timer */
private TimerDelta timerRender;
private TimerInterpolating timerGui;
- private int timerAfterResize = 0;
-
private void mainLoop()
{
screen = new ScreenSplash();
- screen.init();
+ screen.setActive(true);
timerRender = new TimerDelta();
timerGui = new TimerInterpolating(Const.FPS_GUI_UPDATE);
- while (!Display.isCloseRequested()) {
- glLoadIdentity();
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ while (!display.isCloseRequested()) {
+ display.beginFrame();
+ // gui update
timerGui.sync();
-
int ticks = timerGui.getSkipped();
-
if (ticks >= 1) {
- screen.updateGui();
+ input.poll();
timerGui.startNewFrame();
}
- currentDelta = timerRender.getDelta();
+ double delta = timerRender.getDelta();
- // RENDER
- screen.render(currentDelta);
- SoundManager.get().update(currentDelta);
+ sounds.update(delta);
- Display.update();
+ // Screen
+ screen.update(delta);
if (scheduledScreenshot) {
takeScreenshot();
scheduledScreenshot = false;
}
- if (Keys.justPressed(Keyboard.KEY_F11)) {
- Log.f2("F11, toggle fullscreen.");
- switchFullscreen();
- screen.onFullscreenChange();
- Keys.destroyChangeState(Keyboard.KEY_F11);
- }
-
- if (Keyboard.isKeyDown(Keyboard.KEY_LCONTROL)) {
- if (Keyboard.isKeyDown(Keyboard.KEY_Q)) {
- Log.f2("Ctrl+Q, force quit.");
- Keys.destroyChangeState(Keyboard.KEY_Q);
- exit();
- return;
- }
-
-// if (Keyboard.isKeyDown(Keyboard.KEY_M)) {
-// Log.f2("Ctrl+M, go to main menu.");
-// Keys.destroyChangeState(Keyboard.KEY_M);
-// replaceScreen(new ScreenMenuMain());
-// }
-
- if (Keyboard.isKeyDown(Keyboard.KEY_F)) {
- Log.f2("Ctrl+F, switch fullscreen.");
- Keys.destroyChangeState(Keyboard.KEY_F);
- switchFullscreen();
- screen.onFullscreenChange();
- }
- }
-
- if (Display.wasResized()) {
- screen.onWindowResize();
- timerAfterResize = 0;
- } else { // ensure screen has even size
- timerAfterResize++;
- if (timerAfterResize > Const.FPS_GUI_UPDATE * 0.3) {
- timerAfterResize = 0;
- int x = Display.getX();
- int y = Display.getY();
-
- int w = Display.getWidth();
- int h = Display.getHeight();
- if (w % 2 != 0 || h % 2 != 0) {
- try {
- Display.setDisplayMode(windowDisplayMode = new DisplayMode(w - w % 2, h - h % 2));
- screen.onWindowResize();
- Display.setLocation(x, y);
- } catch (LWJGLException e) {
- e.printStackTrace();
- }
- }
- }
- }
-
- try {
- Display.sync(Const.FPS_RENDER);
- } catch (Throwable t) {
- Log.e("Your graphics card driver does not support fullscreen properly.", t);
-
- try {
- Display.setDisplayMode(windowDisplayMode);
- } catch (LWJGLException e) {
- Log.e("Error going back from corrupted fullscreen.");
- onCrash(e);
- }
- }
+ display.endFrame();
}
}
-// UPDATE LOOP END
-
/**
* Do take a screenshot
*/
public void takeScreenshot()
{
- //Effects.play("gui.screenshot");
+ sounds.getEffect("gui.shutter").play(1);
+ Utils.runAsThread(new TaskTakeScreenshot());
+ }
+
- glReadBuffer(GL_FRONT);
- int width = Display.getDisplayMode().getWidth();
- int height = Display.getDisplayMode().getHeight();
- int bpp = 4; // Assuming a 32-bit display with a byte each for red, green, blue, and alpha.
- ByteBuffer buffer = BufferUtils.createByteBuffer(width * height * bpp);
- glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
+ //
+ // static accessors
+ //
- (new ThreadSaveScreenshot(buffer, width, height, bpp)).start();
+ /**
+ * @return sound system of the running instance
+ */
+ public static SoundSystem soundsys()
+ {
+ return inst.sounds;
}
/**
- * Replace screen
- *
- * @param newScreen new screen
+ * @return input system of the running instance
*/
- public void replaceScreen(Screen newScreen)
+ public static InputSystem input()
{
- screen = newScreen;
- screen.init();
+ return inst.input;
}
/**
- * Replace screen, don't init it
- *
- * @param newScreen new screen
+ * @return display system of the running instance
*/
- public void replaceScreenNoInit(Screen newScreen)
+ public static DisplaySystem disp()
{
- screen = newScreen;
+ return inst.display;
}
/**
- * Toggle FS if possible
+ * @return event bus of the running instance
*/
- public void switchFullscreen()
+ public static MessageBus msgbus()
{
- try {
- if (!Display.isFullscreen()) {
- Log.f3("Entering fullscreen.");
- // save window resize
- windowDisplayMode = new DisplayMode(Display.getWidth(), Display.getHeight());
-
- Display.setDisplayMode(Display.getDesktopDisplayMode());
- Display.setFullscreen(true);
- Display.update();
-//
-//
-// DisplayMode mode = Display.getDesktopDisplayMode(); //findDisplayMode(WIDTH, HEIGHT);
-// Display.setDisplayModeAndFullscreen(mode);
- } else {
- Log.f3("Leaving fullscreen.");
- Display.setDisplayMode(windowDisplayMode);
- Display.update();
- }
- } catch (Throwable t) {
- Log.e("Failed to toggle fullscreen mode.", t);
- try {
- Display.setDisplayMode(windowDisplayMode);
- Display.update();
- } catch (Throwable t1) {
- onCrash(t1);
- }
- }
+ return inst.events;
}
+
+
+ /**
+ * @return screen of the running instance
+ */
+ public static Screen screen()
+ {
+ return inst.getCurrentScreen();
+ }
+
+
+ public static boolean broadcast(Object message)
+ {
+ boolean was = msgbus().broadcast(message);
+ if (!was) Log.w("Message not accepted by any channel: " + message);
+ return was;
+ }
+
}
diff --git a/src/mightypork/rogue/Const.java b/src/mightypork/rogue/Const.java
index ca7da92..1500e1b 100644
--- a/src/mightypork/rogue/Const.java
+++ b/src/mightypork/rogue/Const.java
@@ -1,9 +1,6 @@
package mightypork.rogue;
-import mightypork.utils.math.coord.Coord;
-
-
/**
* Application constants
*
@@ -18,12 +15,10 @@ public class Const {
public static final String TITLEBAR = APP_NAME + " v." + VERSION;
// AUDIO
- public static final Coord LISTENER_POS = Coord.ZERO;
-
public static final int FPS_RENDER = 200; // max
public static final long FPS_GUI_UPDATE = 60;
// INITIAL WINDOW SIZE
- public static final int WINDOW_SIZE_X = 1024;
- public static final int WINDOW_SIZE_Y = 768;
+ public static final int WINDOW_W = 1024;
+ public static final int WINDOW_H = 768;
}
diff --git a/src/mightypork/rogue/CrashHandler.java b/src/mightypork/rogue/CrashHandler.java
index fe9aa55..147a50c 100644
--- a/src/mightypork/rogue/CrashHandler.java
+++ b/src/mightypork/rogue/CrashHandler.java
@@ -9,6 +9,7 @@ public class CrashHandler implements UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread t, Throwable e)
{
+ e.printStackTrace();
App.onCrash(e);
}
diff --git a/src/mightypork/rogue/Screen.java b/src/mightypork/rogue/Screen.java
deleted file mode 100644
index e726810..0000000
--- a/src/mightypork/rogue/Screen.java
+++ /dev/null
@@ -1,200 +0,0 @@
-package mightypork.rogue;
-
-
-import static org.lwjgl.opengl.GL11.*;
-
-import java.util.Random;
-
-import mightypork.rogue.animations.GUIRenderer;
-import mightypork.rogue.input.InputHandler;
-import mightypork.rogue.input.Keys;
-import mightypork.utils.math.coord.Coord;
-import mightypork.utils.math.coord.Vec;
-
-import org.lwjgl.input.Keyboard;
-import org.lwjgl.input.Mouse;
-
-
-/**
- * Screen class.
- * Screen animates 3D world, while contained panels render 2D overlays, process
- * inputs and run the game logic.
- *
- * @author MightyPork
- */
-public abstract class Screen implements GUIRenderer, InputHandler {
-
- /** RNG */
- protected static Random rand = new Random();
-
-
- /**
- * handle fullscreen change
- */
- @Override
- public final void onFullscreenChange()
- {
- onWindowResize();
- onViewportChanged();
- }
-
-
- protected abstract void onViewportChanged();
-
-
- /**
- * handle window resize.
- */
- public final void onWindowResize()
- {
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
-
- Coord s = App.inst.getSize();
-
- glViewport(0, 0, s.xi(), s.yi());
-
- glOrtho(0, s.x, 0, s.y, -1000, 1000);
-
- glMatrixMode(GL_MODELVIEW);
-
- glLoadIdentity();
-
- glEnable(GL_BLEND);
- //glDisable(GL_DEPTH_TEST);
- glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
- glDisable(GL_TEXTURE_2D);
- }
-
-
- /**
- * Initialize screen
- */
- public void init()
- {
- onWindowResize();
-
- initScreen();
-
- // SETUP LIGHTS
- glDisable(GL_LIGHTING);
-
- // OTHER SETTINGS
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
-
- glClearDepth(1f);
- glEnable(GL_DEPTH_TEST);
- glDepthFunc(GL_LEQUAL);
-
- glEnable(GL_NORMALIZE);
-
- glShadeModel(GL_SMOOTH);
- glDisable(GL_TEXTURE_2D);
- }
-
-
- /**
- * Here you can initialize the screen.
- */
- public abstract void initScreen();
-
-
- /**
- * Update tick
- */
- @Override
- public final void updateGui()
- {
- Mouse.poll();
- Keyboard.poll();
- checkInputEvents();
-
- onGuiUpdate();
- }
-
-
- protected abstract void onGuiUpdate();
-
-
- /**
- * Render screen
- *
- * @param delta delta time (position between two update ticks, to allow
- * super-smooth animations)
- */
- @Override
- public final void render(double delta)
- {
- glPushAttrib(GL_ENABLE_BIT);
-
- // draw the directly rendered 3D stuff
- render3D();
-
- glPopAttrib();
- }
-
-
- protected abstract void render3D();
-
-
- /**
- * Check input events and process them.
- */
- private final void checkInputEvents()
- {
- while (Keyboard.next()) {
- int key = Keyboard.getEventKey();
- boolean down = Keyboard.getEventKeyState();
- char c = Keyboard.getEventCharacter();
- Keys.onKey(key, down);
- onKey(key, c, down);
- }
- while (Mouse.next()) {
- int button = Mouse.getEventButton();
- boolean down = Mouse.getEventButtonState();
- Coord delta = new Coord(Mouse.getEventDX(), Mouse.getEventDY());
- Coord pos = new Coord(Mouse.getEventX(), Mouse.getEventY());
- int wheeld = Mouse.getEventDWheel();
-
- onMouseButton(button, down, wheeld, pos, delta);
- }
-
- int xc = Mouse.getX();
- int yc = Mouse.getY();
- int xd = Mouse.getDX();
- int yd = Mouse.getDY();
- int wd = Mouse.getDWheel();
-
- if (Math.abs(xd) > 0 || Math.abs(yd) > 0 || Math.abs(wd) > 0) {
- onMouseMove(new Coord(xc, yc), new Vec(xd, yd), wd);
- }
-
- handleKeyStates();
- }
-
-
- @Override
- public abstract void onKey(int key, char c, boolean down);
-
-
- @Override
- public abstract void onMouseButton(int button, boolean down, int wheeld, Coord pos, Coord delta);
-
-
- @Override
- public abstract void handleKeyStates();
-
-
- @Override
- public abstract void onMouseMove(Coord coord, Vec vec, int wd);
-
-
- /**
- * Render background 2D (all is ready for rendering)
- *
- * @param delta delta time
- */
- protected abstract void render2D(double delta);
-
-}
diff --git a/src/mightypork/rogue/animations/EmptyAnimator.java b/src/mightypork/rogue/animations/EmptyAnimator.java
deleted file mode 100644
index feb9f75..0000000
--- a/src/mightypork/rogue/animations/EmptyAnimator.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package mightypork.rogue.animations;
-
-
-/**
- * Empty animation (no effect)
- *
- * @author MightyPork
- */
-public class EmptyAnimator implements GUIRenderer {
-
- /**
- * New empty animation
- */
- public EmptyAnimator() {}
-
-
- @Override
- public void updateGui()
- {}
-
-
- @Override
- public void render(double delta)
- {}
-
-
- @Override
- public void onFullscreenChange()
- {}
-}
diff --git a/src/mightypork/rogue/animations/GUIRenderer.java b/src/mightypork/rogue/animations/GUIRenderer.java
deleted file mode 100644
index 112c5cd..0000000
--- a/src/mightypork/rogue/animations/GUIRenderer.java
+++ /dev/null
@@ -1,13 +0,0 @@
-package mightypork.rogue.animations;
-
-
-public interface GUIRenderer {
-
- public void updateGui();
-
-
- public void render(double delta);
-
-
- public void onFullscreenChange();
-}
diff --git a/src/mightypork/rogue/display/DisplaySystem.java b/src/mightypork/rogue/display/DisplaySystem.java
new file mode 100644
index 0000000..5c07564
--- /dev/null
+++ b/src/mightypork/rogue/display/DisplaySystem.java
@@ -0,0 +1,200 @@
+package mightypork.rogue.display;
+
+
+import static org.lwjgl.opengl.GL11.*;
+
+import java.awt.image.BufferedImage;
+import java.nio.ByteBuffer;
+
+import org.lwjgl.BufferUtils;
+import org.lwjgl.LWJGLException;
+import org.lwjgl.opengl.Display;
+import org.lwjgl.opengl.DisplayMode;
+
+import mightypork.rogue.App;
+import mightypork.rogue.Const;
+import mightypork.rogue.display.events.ScreenChangeEvent;
+import mightypork.utils.logging.Log;
+import mightypork.utils.math.coord.Coord;
+import mightypork.utils.patterns.Destroyable;
+import mightypork.utils.patterns.Initializable;
+import mightypork.utils.time.Updateable;
+
+
+public class DisplaySystem implements Initializable, Destroyable {
+
+ private boolean initialized;
+
+ private DisplayMode windowDisplayMode;
+ private int targetFps;
+
+
+ public DisplaySystem() {
+ initialize();
+ }
+
+ @Override
+ public void initialize()
+ {
+ if(initialized) return;
+
+ initChannels();
+
+ initialized = true;
+ }
+
+ /**
+ * Initialize event channels
+ */
+ private void initChannels()
+ {
+ App.msgbus().registerMessageType(ScreenChangeEvent.class, ScreenChangeEvent.Listener.class);
+ }
+
+ @Override
+ public void destroy()
+ {
+ Display.destroy();
+ }
+
+
+ public void setTargetFps(int fps)
+ {
+ this.targetFps = fps;
+ }
+
+
+ public void createMainWindow(int width, int height, boolean resizable, boolean fullscreen, String title)
+ {
+ try {
+ Display.setDisplayMode(windowDisplayMode = new DisplayMode(width, height));
+ Display.setResizable(resizable);
+ Display.setVSyncEnabled(true);
+ Display.setTitle(title);
+ Display.create();
+
+ if (fullscreen) {
+ switchFullscreen();
+ Display.update();
+ }
+ } catch (LWJGLException e) {
+ throw new RuntimeException("Could not initialize screen", e);
+ }
+ }
+
+
+ /**
+ * Toggle FS if possible
+ */
+ public void switchFullscreen()
+ {
+ try {
+
+ if (!Display.isFullscreen()) {
+ Log.f3("Entering fullscreen.");
+ // save window resize
+ windowDisplayMode = new DisplayMode(Display.getWidth(), Display.getHeight());
+
+ Display.setDisplayMode(Display.getDesktopDisplayMode());
+ Display.setFullscreen(true);
+ Display.update();
+ } else {
+ Log.f3("Leaving fullscreen.");
+ Display.setDisplayMode(windowDisplayMode);
+ Display.update();
+ }
+
+ App.broadcast(new ScreenChangeEvent(true, Display.isFullscreen(), getSize()));
+
+ } catch (Throwable t) {
+ Log.e("Failed to toggle fullscreen mode.", t);
+ try {
+ Display.setDisplayMode(windowDisplayMode);
+ Display.update();
+ } catch (Throwable t1) {
+ throw new RuntimeException("Failed to revert failed fullscreen toggle.", t1);
+ }
+ }
+ }
+
+
+ public BufferedImage takeScreenshot()
+ {
+ glReadBuffer(GL_FRONT);
+ int width = Display.getDisplayMode().getWidth();
+ int height = Display.getDisplayMode().getHeight();
+ int bpp = 4; // Assuming a 32-bit display with a byte each for red, green, blue, and alpha.
+ ByteBuffer buffer = BufferUtils.createByteBuffer(width * height * bpp);
+ glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
+
+ BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
+
+ // convert to a buffered image
+ for (int x = 0; x < width; x++) {
+ for (int y = 0; y < height; y++) {
+ int i = (x + (width * y)) * bpp;
+ int r = buffer.get(i) & 0xFF;
+ int g = buffer.get(i + 1) & 0xFF;
+ int b = buffer.get(i + 2) & 0xFF;
+ image.setRGB(x, height - (y + 1), (0xFF << 24) | (r << 16) | (g << 8) | b);
+ }
+ }
+
+ return image;
+ }
+
+
+ /**
+ * @return true if close was requested (i.e. click on cross)
+ */
+ public boolean isCloseRequested()
+ {
+ return Display.isCloseRequested();
+ }
+
+
+ /**
+ * Get fullscreen state
+ *
+ * @return is fullscreen
+ */
+ public boolean isFullscreen()
+ {
+ return Display.isFullscreen();
+ }
+
+
+ /**
+ * Get screen size
+ *
+ * @return size
+ */
+ public Coord getSize()
+ {
+ return new Coord(Display.getWidth(), Display.getHeight());
+ }
+
+
+ /**
+ * Start a OpenGL frame
+ */
+ public void beginFrame()
+ {
+ if(Display.wasResized()) {
+ App.broadcast(new ScreenChangeEvent(false, Display.isFullscreen(), getSize()));
+ }
+
+ glLoadIdentity();
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ }
+
+
+ /**
+ * End an OpenGL frame, flip buffers, sync to fps.
+ */
+ public void endFrame()
+ {
+ Display.update(false); // don't poll input devices
+ Display.sync(targetFps);
+ }
+}
diff --git a/src/mightypork/rogue/display/Screen.java b/src/mightypork/rogue/display/Screen.java
new file mode 100644
index 0000000..5d93e24
--- /dev/null
+++ b/src/mightypork/rogue/display/Screen.java
@@ -0,0 +1,214 @@
+package mightypork.rogue.display;
+
+
+import static org.lwjgl.opengl.GL11.*;
+
+import java.util.Random;
+
+import mightypork.rogue.App;
+import mightypork.rogue.display.events.ScreenChangeEvent;
+import mightypork.rogue.input.KeyBinder;
+import mightypork.rogue.input.KeyBindingPool;
+import mightypork.rogue.input.KeyStroke;
+import mightypork.rogue.input.events.KeyboardEvent;
+import mightypork.rogue.input.events.MouseMotionEvent;
+import mightypork.rogue.input.events.MouseButtonEvent;
+import mightypork.utils.math.coord.Coord;
+import mightypork.utils.patterns.Destroyable;
+import mightypork.utils.patterns.Initializable;
+import mightypork.utils.time.Updateable;
+
+
+/**
+ * Screen class.
+ * Screen animates 3D world, while contained panels render 2D overlays, process
+ * inputs and run the game logic.
+ *
+ * @author MightyPork
+ */
+public abstract class Screen implements KeyBinder, Updateable, Initializable, KeyboardEvent.Listener, MouseMotionEvent.Listener, MouseButtonEvent.Listener, ScreenChangeEvent.Listener {
+
+ private KeyBindingPool keybindings = new KeyBindingPool();
+
+ private boolean active;
+
+
+ public Screen() {
+ initialize();
+ }
+
+
+ @Override
+ public void bindKeyStroke(KeyStroke stroke, Runnable task)
+ {
+ keybindings.bindKeyStroke(stroke, task);
+ }
+
+
+ @Override
+ public void unbindKeyStroke(KeyStroke stroke)
+ {
+ keybindings.unbindKeyStroke(stroke);
+ }
+
+
+ /**
+ * Prepare for being shown
+ * @param shown true to show, false to hide
+ */
+ public final void setActive(boolean shown)
+ {
+ if (shown) {
+ active = true;
+ setupGraphics();
+ setupViewport();
+ onSizeChanged(App.disp().getSize());
+ onEnter();
+ App.msgbus().addSubscriber(this);
+ } else {
+ active = false;
+ onLeave();
+ App.msgbus().removeSubscriber(this);
+ }
+ }
+
+
+ private void setupGraphics()
+ {
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+
+ glDisable(GL_LIGHTING);
+
+ glClearDepth(1f);
+ glEnable(GL_DEPTH_TEST);
+ glDepthFunc(GL_LEQUAL);
+
+ glEnable(GL_NORMALIZE);
+
+ glShadeModel(GL_SMOOTH);
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ glDisable(GL_TEXTURE_2D);
+
+ setupViewport();
+ }
+
+
+ private void setupViewport()
+ {
+ // fix projection for changed size
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ Coord s = App.disp().getSize();
+ glViewport(0, 0, s.xi(), s.yi());
+ glOrtho(0, s.x, 0, s.y, -1000, 1000);
+
+ // back to modelview
+ glMatrixMode(GL_MODELVIEW);
+ }
+
+
+ /**
+ * Initialize screen layout and key bindings.
+ * Called when the screen is created, not when it comes to front. For that, use onEnter().
+ */
+ @Override
+ public abstract void initialize();
+
+ /**
+ * Called when the screen becomes active
+ */
+ protected abstract void onEnter();
+
+
+ /**
+ * Called when the screen is no longer active
+ */
+ protected abstract void onLeave();
+
+
+ /**
+ * Update GUI for new screen size
+ *
+ * @param size screen size
+ */
+ protected abstract void onSizeChanged(Coord size);
+
+
+ /**
+ * Render screen contents (context is ready for 2D rendering)
+ */
+ protected abstract void renderScreen();
+
+
+ /**
+ * Update animations and timing
+ *
+ * @param delta time elapsed
+ */
+ protected abstract void updateScreen(double delta);
+
+
+ /**
+ * Render screen
+ */
+ private final void renderBegin()
+ {
+ glPushAttrib(GL_ENABLE_BIT);
+ glPushMatrix();
+ }
+
+
+ /**
+ * Render screen
+ */
+ private final void renderEnd()
+ {
+ glPopAttrib();
+ glPopMatrix();
+ }
+
+
+ @Override
+ public final void update(double delta)
+ {
+ if (!isActive()) return;
+
+ updateScreen(delta);
+ renderBegin();
+ renderScreen();
+ renderEnd();
+ };
+
+
+ /**
+ * @return true if screen is the curretn screen
+ */
+ protected final boolean isActive()
+ {
+ return active;
+ }
+
+
+ @Override
+ public final void receive(ScreenChangeEvent event)
+ {
+ if (!isActive()) return;
+
+ setupViewport();
+
+ onSizeChanged(event.getScreenSize());
+ }
+
+
+ @Override
+ public final void receive(KeyboardEvent event)
+ {
+ if (!isActive()) return;
+ keybindings.receive(event);
+ }
+
+}
diff --git a/src/mightypork/rogue/display/ScreenSplash.java b/src/mightypork/rogue/display/ScreenSplash.java
new file mode 100644
index 0000000..c93f1b5
--- /dev/null
+++ b/src/mightypork/rogue/display/ScreenSplash.java
@@ -0,0 +1,168 @@
+package mightypork.rogue.display;
+
+
+import org.lwjgl.input.Keyboard;
+import org.lwjgl.input.Mouse;
+import org.lwjgl.opengl.Display;
+
+import mightypork.rogue.App;
+import mightypork.rogue.input.KeyStroke;
+import mightypork.rogue.input.events.MouseButtonEvent;
+import mightypork.rogue.input.events.MouseMotionEvent;
+import mightypork.rogue.util.RenderUtils;
+import mightypork.utils.math.Polar;
+import mightypork.utils.math.color.RGB;
+import mightypork.utils.math.coord.Coord;
+import mightypork.utils.math.easing.Easing;
+import mightypork.utils.time.AnimDouble;
+import mightypork.utils.time.AnimDoubleDeg;
+
+
+public class ScreenSplash extends Screen {
+
+ private AnimDoubleDeg degAnim = new AnimDoubleDeg(0, Easing.SINE);
+
+ //@formatter:off
+ private AnimDouble[] anims = new AnimDouble[] {
+ new AnimDouble(0, Easing.NONE),
+ new AnimDouble(0, Easing.LINEAR),
+
+ new AnimDouble(0, Easing.QUADRATIC_IN),
+ new AnimDouble(0, Easing.QUADRATIC_OUT),
+ new AnimDouble(0, Easing.QUADRATIC),
+
+ new AnimDouble(0, Easing.CUBIC_IN),
+ new AnimDouble(0, Easing.CUBIC_OUT),
+ new AnimDouble(0, Easing.CUBIC),
+
+ new AnimDouble(0, Easing.QUADRATIC_IN),
+ new AnimDouble(0, Easing.QUADRATIC_OUT),
+ new AnimDouble(0, Easing.QUADRATIC),
+
+ new AnimDouble(0, Easing.QUINTIC_IN),
+ new AnimDouble(0, Easing.QUINTIC_OUT),
+ new AnimDouble(0, Easing.QUINTIC_IN_OUT),
+
+ new AnimDouble(0, Easing.EXPO_IN),
+ new AnimDouble(0, Easing.EXPO_OUT),
+ new AnimDouble(0, Easing.EXPO),
+
+ new AnimDouble(0, Easing.SINE_IN),
+ new AnimDouble(0, Easing.SINE_OUT),
+ new AnimDouble(0, Easing.SINE),
+
+ new AnimDouble(0, Easing.CIRC_IN),
+ new AnimDouble(0, Easing.CIRC_OUT),
+ new AnimDouble(0, Easing.CIRC),
+ };
+ //@formatter:on
+
+ @Override
+ public void initialize()
+ {
+ bindKeyStroke(new KeyStroke(Keyboard.KEY_RIGHT), new Runnable() {
+
+ @Override
+ public void run()
+ {
+ for (AnimDouble a : anims) {
+ a.animate(0, 1, 3);
+ }
+ }
+ });
+
+ bindKeyStroke(new KeyStroke(Keyboard.KEY_LEFT), new Runnable() {
+
+ @Override
+ public void run()
+ {
+ for (AnimDouble a : anims) {
+ a.animate(1, 0, 3);
+ }
+ }
+ });
+ }
+
+
+ @Override
+ protected void renderScreen()
+ {
+ double screenH = Display.getHeight();
+ double screenW = Display.getWidth();
+ double perBoxH = screenH / anims.length;
+ double padding = perBoxH*0.1;
+ double boxSide = perBoxH-padding*2;
+
+ for (int i = 0; i < anims.length; i++) {
+ AnimDouble a = anims[i];
+
+ RenderUtils.setColor(i%3==0?RGB.GREEN:RGB.BLUE);
+ RenderUtils.quadSize(
+ padding + a.getCurrentValue() * (screenW - perBoxH - padding*2),
+ screenH - perBoxH * i - perBoxH + padding,
+ boxSide,
+ boxSide
+ );
+ }
+
+ RenderUtils.setColor(RGB.YELLOW);
+ RenderUtils.translate(new Coord(Display.getWidth() / 2, Display.getHeight() / 2));
+ RenderUtils.rotateZ(degAnim.getCurrentValue());
+ RenderUtils.quadSize(-10, -10, 20, 200);
+ }
+
+
+ @Override
+ public void receive(MouseMotionEvent event)
+ {
+ }
+
+
+ @Override
+ public void receive(MouseButtonEvent event)
+ {
+ if(event.isDown()) {
+ Coord vec = App.disp().getSize().half().vecTo(event.getPos());
+
+ Polar p = Polar.fromCoord(vec);
+
+ degAnim.fadeTo(p.getAngleDeg() - 90, 0.2);
+ }
+ }
+
+
+ @Override
+ protected void onEnter()
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+
+ @Override
+ protected void onLeave()
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+
+ @Override
+ protected void onSizeChanged(Coord size)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+
+ @Override
+ protected void updateScreen(double delta)
+ {
+ degAnim.update(delta);
+
+ for (AnimDouble a : anims) {
+ a.update(delta);
+ }
+ }
+
+}
diff --git a/src/mightypork/rogue/display/events/ScreenChangeEvent.java b/src/mightypork/rogue/display/events/ScreenChangeEvent.java
new file mode 100644
index 0000000..b3e881e
--- /dev/null
+++ b/src/mightypork/rogue/display/events/ScreenChangeEvent.java
@@ -0,0 +1,50 @@
+package mightypork.rogue.display.events;
+
+
+import mightypork.utils.math.coord.Coord;
+import mightypork.utils.patterns.subscription.Handleable;
+
+
+public class ScreenChangeEvent implements Handleable {
+
+ private boolean fullscreen;
+ private Coord screenSize;
+ private boolean fsChanged;
+
+
+ public ScreenChangeEvent(boolean fsChanged, boolean fullscreen, Coord size) {
+ this.fullscreen = fullscreen;
+ this.screenSize = size;
+ this.fsChanged = fsChanged;
+ }
+
+
+ public boolean isFullscreen()
+ {
+ return fullscreen;
+ }
+
+
+ public boolean fullscreenChanged()
+ {
+ return fsChanged;
+ }
+
+
+ public Coord getScreenSize()
+ {
+ return screenSize;
+ }
+
+
+ @Override
+ public void handleBy(Listener handler)
+ {
+ handler.receive(this);
+ }
+
+ public interface Listener {
+
+ public void receive(ScreenChangeEvent event);
+ }
+}
diff --git a/src/mightypork/rogue/fonts/FontManager.java b/src/mightypork/rogue/fonts/FontManager.java
index 7185479..77bf1f3 100644
--- a/src/mightypork/rogue/fonts/FontManager.java
+++ b/src/mightypork/rogue/fonts/FontManager.java
@@ -112,96 +112,96 @@ public class FontManager {
OUTLINE;
}
- /**
- * Preloaded font identifier [name, size, style]
- *
- * @author MightyPork
- */
- public static class FontId {
-
- /** font size (pt) */
- public float size = 24;
- /** font name, registered with registerFile */
- public String name = "";
- /** font style. The given style must be in a file. */
- public Style style;
-
- /** Set of glyphs in this ID */
- public String glyphs = "";
-
- /** Index for faster comparision of glyph ids. */
- public int glyphset_id = 0;
-
-
- /**
- * Preloaded font identifier
- *
- * @param name font name (registerFile)
- * @param size font size (pt)
- * @param style font style
- * @param glyphs glyphs to load
- */
- public FontId(String name, double size, Style style, String glyphs) {
- this.name = name;
- this.size = (float) size;
- this.style = style;
-
- if (glyphs.equals(Glyphs.basic)) {
- glyphset_id = 1;
- } else if (glyphs.equals(Glyphs.alnum)) {
- glyphset_id = 2;
- } else if (glyphs.equals(Glyphs.basic_text)) {
- glyphset_id = 3;
- } else if (glyphs.equals(Glyphs.numbers)) {
- glyphset_id = 4;
- } else if (glyphs.equals(Glyphs.alpha)) {
- glyphset_id = 5;
- } else if (glyphs.equals(Glyphs.all)) {
- glyphset_id = 6;
- } else if (glyphs.equals(Glyphs.alnum_extra)) {
- glyphset_id = 7;
- } else if (glyphs.equals(Glyphs.signs)) {
- glyphset_id = 8;
- } else if (glyphs.equals(Glyphs.signs_extra)) {
- glyphset_id = 9;
- } else {
- this.glyphs = glyphs;
- }
- }
-
-
- @Override
- public boolean equals(Object obj)
- {
- if (obj == null) return false;
- if (!(obj.getClass().isAssignableFrom(getClass()))) return false;
- if (obj instanceof FontId) {
- if (obj == this) return true;
- FontId id2 = ((FontId) obj);
- boolean flag = true;
- flag &= id2.size == size;
- flag &= id2.name.equals(name);
- flag &= id2.style == style;
- flag &= ((id2.glyphset_id != -1 && id2.glyphset_id == glyphset_id) || id2.glyphs.equals(glyphs));
- return flag;
- }
- return false;
- }
-
-
- @Override
- public int hashCode()
- {
- return (new Float(size).hashCode()) ^ name.hashCode() ^ style.hashCode() ^ glyphset_id;
- }
-
-
- @Override
- public String toString()
- {
- return "[" + name + ", " + size + ", " + style + (glyphset_id > 0 ? ", g=" + glyphset_id : ", g=custom") + "]";
- }
- }
+// /**
+// * Preloaded font identifier [name, size, style]
+// *
+// * @author MightyPork
+// */
+// public static class FontId {
+//
+// /** font size (pt) */
+// public float size = 24;
+// /** font name, registered with registerFile */
+// public String name = "";
+// /** font style. The given style must be in a file. */
+// public Style style;
+//
+// /** Set of glyphs in this ID */
+// public String glyphs = "";
+//
+// /** Index for faster comparision of glyph ids. */
+// public int glyphset_id = 0;
+//
+//
+// /**
+// * Preloaded font identifier
+// *
+// * @param name font name (registerFile)
+// * @param size font size (pt)
+// * @param style font style
+// * @param glyphs glyphs to load
+// */
+// public FontId(String name, double size, Style style, String glyphs) {
+// this.name = name;
+// this.size = (float) size;
+// this.style = style;
+//
+// if (glyphs.equals(Glyphs.basic)) {
+// glyphset_id = 1;
+// } else if (glyphs.equals(Glyphs.alnum)) {
+// glyphset_id = 2;
+// } else if (glyphs.equals(Glyphs.basic_text)) {
+// glyphset_id = 3;
+// } else if (glyphs.equals(Glyphs.numbers)) {
+// glyphset_id = 4;
+// } else if (glyphs.equals(Glyphs.alpha)) {
+// glyphset_id = 5;
+// } else if (glyphs.equals(Glyphs.all)) {
+// glyphset_id = 6;
+// } else if (glyphs.equals(Glyphs.alnum_extra)) {
+// glyphset_id = 7;
+// } else if (glyphs.equals(Glyphs.signs)) {
+// glyphset_id = 8;
+// } else if (glyphs.equals(Glyphs.signs_extra)) {
+// glyphset_id = 9;
+// } else {
+// this.glyphs = glyphs;
+// }
+// }
+//
+//
+// @Override
+// public boolean equals(Object obj)
+// {
+// if (obj == null) return false;
+// if (!(obj.getClass().isAssignableFrom(getClass()))) return false;
+// if (obj instanceof FontId) {
+// if (obj == this) return true;
+// FontId id2 = ((FontId) obj);
+// boolean flag = true;
+// flag &= id2.size == size;
+// flag &= id2.name.equals(name);
+// flag &= id2.style == style;
+// flag &= ((id2.glyphset_id != -1 && id2.glyphset_id == glyphset_id) || id2.glyphs.equals(glyphs));
+// return flag;
+// }
+// return false;
+// }
+//
+//
+// @Override
+// public int hashCode()
+// {
+// return (new Float(size).hashCode()) ^ name.hashCode() ^ style.hashCode() ^ glyphset_id;
+// }
+//
+//
+// @Override
+// public String toString()
+// {
+// return "[" + name + ", " + size + ", " + style + (glyphset_id > 0 ? ", g=" + glyphset_id : ", g=custom") + "]";
+// }
+// }
/**
* Group of styles of one font.
@@ -255,7 +255,7 @@ public class FontManager {
*/
public static LoadedFont loadFont(String name, double size, Style style, String glyphs)
{
- return loadFont(name, size, style, glyphs, 9, 8, Coord.ONE, 0, 0);
+ return loadFont(name, size, style, glyphs, 9, 8, Coord.one(), 0, 0);
}
diff --git a/src/mightypork/rogue/fonts/LoadedFont.java b/src/mightypork/rogue/fonts/LoadedFont.java
index fc79201..f459836 100644
--- a/src/mightypork/rogue/fonts/LoadedFont.java
+++ b/src/mightypork/rogue/fonts/LoadedFont.java
@@ -21,7 +21,6 @@ import mightypork.rogue.Config;
import mightypork.utils.logging.Log;
import mightypork.utils.math.color.RGB;
import mightypork.utils.math.coord.Coord;
-import mightypork.utils.math.coord.CoordI;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
@@ -598,38 +597,12 @@ public class LoadedFont {
}
- /**
- * Draw string with font.
- *
- * @param pos coord
- * @param text text to draw
- * @param color render color
- * @param align (-1,0,1)
- */
- public void draw(CoordI pos, String text, RGB color, int align)
- {
- drawString(pos.x, pos.y, text, 1, 1, color, align);
- }
-
-
public void drawFuzzy(Coord pos, String text, int align, RGB textColor, RGB blurColor, int blurSize)
{
drawFuzzy(pos, text, align, textColor, blurColor, blurSize, true);
}
- public void drawFuzzy(CoordI pos, String text, int align, RGB textColor, RGB blurColor, int blurSize)
- {
- drawFuzzy(pos.toCoord(), text, align, textColor, blurColor, blurSize, true);
- }
-
-
- public void drawFuzzy(CoordI pos, String text, int align, RGB textColor, RGB blurColor, int blurSize, boolean smooth)
- {
- drawFuzzy(pos.toCoord(), text, align, textColor, blurColor, blurSize, smooth);
- }
-
-
public void drawFuzzy(Coord pos, String text, int align, RGB textColor, RGB blurColor, int blurSize, boolean smooth)
{
glPushMatrix();
diff --git a/src/mightypork/rogue/input/InputHandler.java b/src/mightypork/rogue/input/InputHandler.java
deleted file mode 100644
index 8e9154c..0000000
--- a/src/mightypork/rogue/input/InputHandler.java
+++ /dev/null
@@ -1,52 +0,0 @@
-package mightypork.rogue.input;
-
-
-import mightypork.utils.math.coord.Coord;
-import mightypork.utils.math.coord.Vec;
-
-
-/**
- * Input event handler
- *
- * @author MightyPork
- */
-public interface InputHandler {
-
- /**
- * Called each update tick, if the mouse position was changed.
- *
- * @param pos mouse position
- * @param move mouse motion
- * @param wheelDelta mouse wheel delta
- */
- public void onMouseMove(Coord pos, Vec move, int wheelDelta);
-
-
- /**
- * Mouse event handler.
- *
- * @param button button which caused this event
- * @param down true = down, false = up
- * @param wheelDelta number of steps the wheel turned since last event
- * @param pos mouse position
- * @param deltaPos delta mouse position
- */
- public void onMouseButton(int button, boolean down, int wheelDelta, Coord pos, Coord deltaPos);
-
-
- /**
- * Key event handler.
- *
- * @param key key index, constant Keyboard.KEY_???
- * @param c character typed, if any
- * @param down true = down, false = up
- */
- public void onKey(int key, char c, boolean down);
-
-
- /**
- * In this method screen can handle static inputs, that is:
- * Keyboard.isKeyDown, Mouse.isButtonDown etc.
- */
- public void handleKeyStates();
-}
diff --git a/src/mightypork/rogue/input/InputSystem.java b/src/mightypork/rogue/input/InputSystem.java
new file mode 100644
index 0000000..0bd3d6a
--- /dev/null
+++ b/src/mightypork/rogue/input/InputSystem.java
@@ -0,0 +1,126 @@
+package mightypork.rogue.input;
+
+
+import mightypork.rogue.App;
+import mightypork.rogue.input.events.KeyboardEvent;
+import mightypork.rogue.input.events.MouseButtonEvent;
+import mightypork.rogue.input.events.MouseMotionEvent;
+import mightypork.utils.math.coord.Coord;
+import mightypork.utils.patterns.Destroyable;
+import mightypork.utils.patterns.Initializable;
+import org.lwjgl.LWJGLException;
+import org.lwjgl.input.Keyboard;
+import org.lwjgl.input.Mouse;
+import org.lwjgl.opengl.Display;
+
+
+public class InputSystem implements KeyBinder, Destroyable, Initializable {
+
+ private boolean initialized;
+
+ // listeners
+ private KeyBindingPool keybindings;
+
+
+ public InputSystem() {
+ initialize();
+ }
+
+
+ @Override
+ public void initialize()
+ {
+ if (initialized) return;
+
+ initDevices();
+
+ initChannels();
+
+ keybindings = new KeyBindingPool();
+
+ App.msgbus().addSubscriber(keybindings);
+ }
+
+
+ private void initDevices()
+ {
+ try {
+ Mouse.create();
+ Keyboard.create();
+ Keyboard.enableRepeatEvents(false);
+ } catch (LWJGLException e) {
+ throw new RuntimeException("Failed to initialize input devices.", e);
+ }
+ }
+
+
+ private void initChannels()
+ {
+ App.msgbus().registerMessageType(KeyboardEvent.class, KeyboardEvent.Listener.class);
+ App.msgbus().registerMessageType(MouseMotionEvent.class, MouseMotionEvent.Listener.class);
+ App.msgbus().registerMessageType(MouseButtonEvent.class, MouseButtonEvent.Listener.class);
+ }
+
+
+ @Override
+ public void destroy()
+ {
+ Mouse.destroy();
+ Keyboard.destroy();
+ }
+
+
+ @Override
+ public void bindKeyStroke(KeyStroke stroke, Runnable task)
+ {
+ keybindings.bindKeyStroke(stroke, task);
+ }
+
+
+ @Override
+ public void unbindKeyStroke(KeyStroke stroke)
+ {
+ keybindings.unbindKeyStroke(stroke);
+ }
+
+
+ /**
+ * Update inputs
+ */
+ public final void poll()
+ {
+ Display.processMessages(); // redundant if Display.update() is called in main loop
+ Mouse.poll();
+ Keyboard.poll();
+
+ while (Mouse.next()) {
+ onMouseEvent();
+ }
+
+ while (Keyboard.next()) {
+ onKeyEvent();
+ }
+ }
+
+
+ private void onMouseEvent()
+ {
+ int button = Mouse.getEventButton();
+ boolean down = Mouse.getEventButtonState();
+ Coord pos = new Coord(Mouse.getEventX(), Mouse.getEventY());
+ Coord move = new Coord(Mouse.getEventDX(), Mouse.getEventDY());
+ int wheeld = Mouse.getEventDWheel();
+
+ if (button != -1 || wheeld != 0) App.broadcast(new MouseButtonEvent(pos, button, down, wheeld));
+ if(!move.isZero()) App.broadcast(new MouseMotionEvent(pos, move));
+ }
+
+
+ private void onKeyEvent()
+ {
+ int key = Keyboard.getEventKey();
+ boolean down = Keyboard.getEventKeyState();
+ char c = Keyboard.getEventCharacter();
+ App.broadcast(new KeyboardEvent(key, c, down));
+ }
+}
diff --git a/src/mightypork/rogue/input/KeyBinder.java b/src/mightypork/rogue/input/KeyBinder.java
new file mode 100644
index 0000000..e1edb61
--- /dev/null
+++ b/src/mightypork/rogue/input/KeyBinder.java
@@ -0,0 +1,21 @@
+package mightypork.rogue.input;
+
+
+public interface KeyBinder {
+
+ /**
+ * Bind handler to a keystroke, replace current handler if any
+ *
+ * @param stroke trigger keystroke
+ * @param task handler
+ */
+ abstract void bindKeyStroke(KeyStroke stroke, Runnable task);
+
+
+ /**
+ * Remove handler from a keystroke (id any)
+ * @param stroke stroke
+ */
+ abstract void unbindKeyStroke(KeyStroke stroke);
+
+}
diff --git a/src/mightypork/rogue/input/KeyBinding.java b/src/mightypork/rogue/input/KeyBinding.java
new file mode 100644
index 0000000..8f31420
--- /dev/null
+++ b/src/mightypork/rogue/input/KeyBinding.java
@@ -0,0 +1,48 @@
+package mightypork.rogue.input;
+
+
+import mightypork.rogue.input.events.KeyboardEvent;
+
+
+public class KeyBinding implements KeyboardEvent.Listener {
+
+ private KeyStroke keystroke;
+ private Runnable handler;
+ private boolean wasActive = false;
+
+
+ public KeyBinding(KeyStroke stroke, Runnable handler) {
+ this.keystroke = stroke;
+ this.handler = handler;
+
+ wasActive = keystroke.isActive();
+ }
+
+
+ public boolean matches(KeyStroke stroke)
+ {
+ return this.keystroke.equals(stroke);
+ }
+
+
+ public void setHandler(Runnable handler)
+ {
+ this.handler = handler;
+ }
+
+
+ @Override
+ public void receive(KeyboardEvent event)
+ {
+ // ignore unrelated events
+ if(!keystroke.getKeys().contains(event.getKey())) return;
+
+ // run handler when event was met
+ if (keystroke.isActive() && !wasActive) {
+ handler.run();
+ }
+
+ wasActive = keystroke.isActive();
+ }
+
+}
diff --git a/src/mightypork/rogue/input/KeyBindingPool.java b/src/mightypork/rogue/input/KeyBindingPool.java
new file mode 100644
index 0000000..9b71d0c
--- /dev/null
+++ b/src/mightypork/rogue/input/KeyBindingPool.java
@@ -0,0 +1,68 @@
+package mightypork.rogue.input;
+
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import mightypork.rogue.input.events.KeyboardEvent;
+import mightypork.utils.logging.Log;
+
+
+/**
+ * Key binding pool
+ *
+ * @author MightyPork
+ */
+public class KeyBindingPool implements KeyBinder, KeyboardEvent.Listener {
+
+ private Set bindings = new HashSet();
+
+
+ /**
+ * Bind handler to a keystroke, replace current handler if any
+ *
+ * @param stroke trigger keystroke
+ * @param task handler
+ */
+ @Override
+ public void bindKeyStroke(KeyStroke stroke, Runnable task)
+ {
+ for (KeyBinding kb : bindings) {
+ if (kb.matches(stroke)) {
+ Log.w("Duplicate KeyBinding ("+stroke+"), using newest handler.");
+ kb.setHandler(task);
+ return;
+ }
+ }
+
+ bindings.add(new KeyBinding(stroke, task));
+ }
+
+
+ /**
+ * Remove handler from keystroke (id any)
+ * @param stroke stroke
+ */
+ @Override
+ public void unbindKeyStroke(KeyStroke stroke)
+ {
+ Iterator iter = bindings.iterator();
+
+ while (iter.hasNext()) {
+ KeyBinding kb = iter.next();
+ if (kb.matches(stroke)) {
+ iter.remove();
+ return;
+ }
+ }
+ }
+
+ @Override
+ public void receive(KeyboardEvent event)
+ {
+ for(KeyBinding kb: bindings) {
+ kb.receive(event);
+ }
+ }
+}
diff --git a/src/mightypork/rogue/input/KeyStroke.java b/src/mightypork/rogue/input/KeyStroke.java
new file mode 100644
index 0000000..4faea8e
--- /dev/null
+++ b/src/mightypork/rogue/input/KeyStroke.java
@@ -0,0 +1,114 @@
+package mightypork.rogue.input;
+
+
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import org.lwjgl.input.Keyboard;
+
+
+public class KeyStroke {
+
+ private Set keys = new LinkedHashSet();
+ private boolean down = true;
+
+
+ /**
+ * KeyStroke
+ *
+ * @param down true for falling edge, up for rising edge
+ * @param keys keys that must be pressed
+ */
+ public KeyStroke(boolean down, int... keys) {
+ this.down = down;
+ for (int k : keys) {
+ this.keys.add(k);
+ }
+ }
+
+
+ /**
+ * Falling edge keystroke
+ *
+ * @param keys
+ */
+ public KeyStroke(int... keys) {
+ for (int k : keys) {
+ this.keys.add(k);
+ }
+ }
+
+
+ public boolean isActive()
+ {
+ boolean st = true;
+ for (int k : keys) {
+ st &= Keyboard.isKeyDown(k);
+ }
+
+ return down ? st : !st;
+ }
+
+
+ public void setKeys(Set keys)
+ {
+ this.keys = keys;
+ }
+
+
+ @Override
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((keys == null) ? 0 : keys.hashCode());
+ return result;
+ }
+
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (!(obj instanceof KeyStroke)) return false;
+ KeyStroke other = (KeyStroke) obj;
+
+ if (keys == null) {
+ if (other.keys != null) return false;
+ } else if (!keys.equals(other.keys)) {
+ return false;
+ }
+
+ if (down != other.down) return false;
+
+ return true;
+ }
+
+
+ @Override
+ public String toString()
+ {
+ String s = "(";
+
+ int cnt = 0;
+ Iterator i = keys.iterator();
+ for (; i.hasNext(); cnt++) {
+ if (cnt > 0) s += "+";
+ s += Keyboard.getKeyName(i.next());
+ }
+
+ s += down ? ",DOWN" : "UP";
+
+ s += ")";
+
+ return s;
+ }
+
+
+ public Set getKeys()
+ {
+ return keys;
+ }
+}
diff --git a/src/mightypork/rogue/input/Keys.java b/src/mightypork/rogue/input/Keys.java
deleted file mode 100644
index b734af3..0000000
--- a/src/mightypork/rogue/input/Keys.java
+++ /dev/null
@@ -1,122 +0,0 @@
-package mightypork.rogue.input;
-
-
-import org.lwjgl.input.Keyboard;
-
-
-/**
- * Key state handler
- */
-public class Keys {
-
- private static boolean[] prevKeys;
- private static boolean[] keys;
-
-
- /**
- * initialize key state handler
- */
- private static void init()
- {
-
- if (keys == null) {
- keys = new boolean[Keyboard.KEYBOARD_SIZE];
- }
-
- if (prevKeys == null) {
- prevKeys = new boolean[Keyboard.KEYBOARD_SIZE];
- }
- }
-
-
- /**
- * method called when key event was detected in Screen class.
- *
- * @param key
- * @param down
- */
- public static void onKey(int key, boolean down)
- {
-
- init();
-
- prevKeys[key] = keys[key];
- keys[key] = down;
- }
-
-
- /**
- * Check if key is down
- *
- * @param key key index
- * @return is down
- */
- public static boolean isDown(int key)
- {
-
- init();
-
- return keys[key];
- }
-
-
- /**
- * Check if key is up
- *
- * @param key key index
- * @return is up
- */
- public static boolean isUp(int key)
- {
-
- init();
-
- return !keys[key];
- }
-
-
- /**
- * Check if key was just pressed (changed state since last event on this
- * key)
- *
- * @param key key index
- * @return true if changed state to DOWN
- */
- public static boolean justPressed(int key)
- {
-
- init();
-
- return !prevKeys[key] && keys[key];
- }
-
-
- /**
- * Check if key was just released (changed state since last event on this
- * key)
- *
- * @param key key index
- * @return true if changed state to UP
- */
- public static boolean justReleased(int key)
- {
-
- init();
-
- return prevKeys[key] && !keys[key];
- }
-
-
- /**
- * Destroy "just" flag for a key.
- *
- * @param key key index
- */
- public static void destroyChangeState(int key)
- {
-
- init();
-
- prevKeys[key] = keys[key];
- }
-}
diff --git a/src/mightypork/rogue/input/events/KeyboardEvent.java b/src/mightypork/rogue/input/events/KeyboardEvent.java
new file mode 100644
index 0000000..cc478da
--- /dev/null
+++ b/src/mightypork/rogue/input/events/KeyboardEvent.java
@@ -0,0 +1,85 @@
+package mightypork.rogue.input.events;
+
+
+import org.lwjgl.input.Keyboard;
+
+import mightypork.utils.patterns.subscription.Handleable;
+
+
+/**
+ * A keyboard event
+ *
+ * @author MightyPork
+ */
+public class KeyboardEvent implements Handleable {
+
+ private int key;
+ private boolean down;
+ private char c;
+
+
+ public KeyboardEvent(int key, char c, boolean down) {
+ this.key = key;
+ this.c = c;
+ this.down = down;
+ }
+
+
+ /**
+ * @return key code (see {@link org.lwjgl.input.Keyboard})
+ */
+ public int getKey()
+ {
+ return key;
+ }
+
+
+ /**
+ * @return true if key was just pressed
+ */
+ public boolean isDown()
+ {
+ return down;
+ }
+
+ /**
+ * @return true if key was just released
+ */
+ public boolean isUp()
+ {
+ return !down;
+ }
+
+
+ /**
+ * @return event character (if any)
+ */
+ public char getChar()
+ {
+ return c;
+ }
+
+
+ @Override
+ public void handleBy(Listener keh)
+ {
+ keh.receive(this);
+ }
+
+ public interface Listener {
+
+ /**
+ * Handle an event
+ *
+ * @param event event
+ */
+ public void receive(KeyboardEvent event);
+ }
+
+ @Override
+ public String toString()
+ {
+ return Keyboard.getKeyName(key)+":"+(down?"DOWN":"UP");
+ }
+
+}
diff --git a/src/mightypork/rogue/input/events/MouseButtonEvent.java b/src/mightypork/rogue/input/events/MouseButtonEvent.java
new file mode 100644
index 0000000..8aa7225
--- /dev/null
+++ b/src/mightypork/rogue/input/events/MouseButtonEvent.java
@@ -0,0 +1,119 @@
+package mightypork.rogue.input.events;
+
+
+import mightypork.utils.math.coord.Coord;
+import mightypork.utils.patterns.subscription.Handleable;
+
+
+/**
+ * Mouse button / wheel event
+ *
+ * @author MightyPork
+ */
+public class MouseButtonEvent implements Handleable {
+
+ public static final int BUTTON_LEFT = 0;
+ public static final int BUTTON_MIDDLE = 1;
+ public static final int BUTTON_RIGHT = 2;
+
+ private int button;
+ private int wheeld;
+ private Coord pos;
+ private boolean down;
+
+
+ /**
+ * Mouse button event
+ *
+ * @param pos event position
+ * @param button button id
+ * @param down button pressed
+ * @param wheeld wheel change
+ */
+ public MouseButtonEvent(Coord pos, int button, boolean down, int wheeld) {
+ this.button = button;
+ this.down = down;
+ this.pos = pos;
+ this.wheeld = wheeld;
+ }
+
+
+ /**
+ * @return true if the event was caused by a button state change
+ */
+ public boolean isButtonEvent()
+ {
+ return button != -1;
+ }
+
+
+ /**
+ * @return true if the event was caused by a wheel change
+ */
+ public boolean isWheelEvent()
+ {
+ return wheeld != 0;
+ }
+
+
+ /**
+ * @return button id or -1 if none was pressed
+ */
+ public int getButton()
+ {
+ return button;
+ }
+
+
+ /**
+ * @return number of steps the wheel changed since last event
+ */
+ public int getWheelDelta()
+ {
+ return wheeld;
+ }
+
+
+ /**
+ * @return mouse position when the event occurred
+ */
+ public Coord getPos()
+ {
+ return pos;
+ }
+
+
+ /**
+ * @return true if button was just pressed
+ */
+ public boolean isDown()
+ {
+ return button != -1 && down;
+ }
+
+
+ /**
+ * @return true if button was just released
+ */
+ public boolean isUp()
+ {
+ return button != -1 && !down;
+ }
+
+
+ @Override
+ public void handleBy(Listener handler)
+ {
+ handler.receive(this);
+ }
+
+ public interface Listener {
+
+ /**
+ * Handle an event
+ *
+ * @param event event
+ */
+ public void receive(MouseButtonEvent event);
+ }
+}
diff --git a/src/mightypork/rogue/input/events/MouseMotionEvent.java b/src/mightypork/rogue/input/events/MouseMotionEvent.java
new file mode 100644
index 0000000..0d36624
--- /dev/null
+++ b/src/mightypork/rogue/input/events/MouseMotionEvent.java
@@ -0,0 +1,54 @@
+package mightypork.rogue.input.events;
+
+
+import mightypork.utils.math.coord.Coord;
+import mightypork.utils.patterns.subscription.Handleable;
+
+
+public class MouseMotionEvent implements Handleable {
+
+ private Coord move;
+ private Coord pos;
+
+
+ public MouseMotionEvent(Coord pos, Coord move) {
+ this.move = move;
+ this.pos = pos;
+ }
+
+
+ /**
+ * @return movement since last {@link MouseMotionEvent}
+ */
+ public Coord getPosDelta()
+ {
+ return move;
+ }
+
+
+ /**
+ * @return current mouse position
+ */
+ public Coord getPos()
+ {
+ return pos;
+ }
+
+
+ @Override
+ public void handleBy(Listener keh)
+ {
+ keh.receive(this);
+ }
+
+ public interface Listener {
+
+ /**
+ * Handle an event
+ *
+ * @param event event
+ */
+ public void receive(MouseMotionEvent event);
+ }
+
+}
diff --git a/src/mightypork/rogue/screens/ScreenSplash.java b/src/mightypork/rogue/screens/ScreenSplash.java
deleted file mode 100644
index be4967f..0000000
--- a/src/mightypork/rogue/screens/ScreenSplash.java
+++ /dev/null
@@ -1,82 +0,0 @@
-package mightypork.rogue.screens;
-
-
-import mightypork.rogue.Screen;
-import mightypork.utils.math.coord.Coord;
-import mightypork.utils.math.coord.Vec;
-
-
-public class ScreenSplash extends Screen {
-
- @Override
- protected void onViewportChanged()
- {
- // TODO Auto-generated method stub
-
- }
-
-
- @Override
- public void initScreen()
- {
- // TODO Auto-generated method stub
-
- }
-
-
- @Override
- protected void onGuiUpdate()
- {
- // TODO Auto-generated method stub
-
- }
-
-
- @Override
- protected void render3D()
- {
- // TODO Auto-generated method stub
-
- }
-
-
- @Override
- public void onKey(int key, char c, boolean down)
- {
- // TODO Auto-generated method stub
-
- }
-
-
- @Override
- public void onMouseButton(int button, boolean down, int wheeld, Coord pos, Coord delta)
- {
- // TODO Auto-generated method stub
-
- }
-
-
- @Override
- public void handleKeyStates()
- {
- // TODO Auto-generated method stub
-
- }
-
-
- @Override
- public void onMouseMove(Coord coord, Vec vec, int wd)
- {
- // TODO Auto-generated method stub
-
- }
-
-
- @Override
- protected void render2D(double delta)
- {
- // TODO Auto-generated method stub
-
- }
-
-}
diff --git a/src/mightypork/rogue/sounds/AudioPlayer.java b/src/mightypork/rogue/sounds/AudioPlayer.java
deleted file mode 100644
index b2070ae..0000000
--- a/src/mightypork/rogue/sounds/AudioPlayer.java
+++ /dev/null
@@ -1,77 +0,0 @@
-package mightypork.rogue.sounds;
-
-
-import mightypork.utils.objects.Mutable;
-
-
-public abstract class AudioPlayer {
-
- /** base gain for sfx */
- private double baseGain = 1;
-
- /** the track */
- private AudioX track;
-
- /** base pitch for sfx */
- private double basePitch = 1;
-
- /** dedicated volume control */
- private Mutable gainMultiplier = null;
-
-
- public AudioPlayer(AudioX track, double baseGain, Mutable gainMultiplier) {
- this(track, 1, baseGain, gainMultiplier);
- }
-
-
- public AudioPlayer(AudioX track, double basePitch, double baseGain, Mutable gainMultiplier) {
- this.track = track;
-
- this.baseGain = baseGain;
- this.basePitch = basePitch;
-
- this.gainMultiplier = gainMultiplier;
- }
-
-
- public void destroy()
- {
- track.release();
- track = null;
- }
-
-
- protected AudioX getAudio()
- {
- return track;
- }
-
-
- protected float getGain(double multiplier)
- {
- return (float) (baseGain * gainMultiplier.get() * multiplier);
- }
-
-
- protected float getPitch(double multiplier)
- {
- return (float) (basePitch * multiplier);
- }
-
-
- /**
- * Get if audio is valid
- *
- * @return is valid
- */
- protected boolean canPlay()
- {
- return (track != null);
- }
-
-
- public void load()
- {
- if (canPlay()) track.load();
- }
-}
diff --git a/src/mightypork/rogue/sounds/AudioX.java b/src/mightypork/rogue/sounds/AudioX.java
index f7de478..5cf31c7 100644
--- a/src/mightypork/rogue/sounds/AudioX.java
+++ b/src/mightypork/rogue/sounds/AudioX.java
@@ -4,6 +4,7 @@ package mightypork.rogue.sounds;
import mightypork.utils.files.FileUtils;
import mightypork.utils.logging.Log;
import mightypork.utils.math.coord.Coord;
+import mightypork.utils.patterns.Destroyable;
import org.newdawn.slick.openal.Audio;
import org.newdawn.slick.openal.SoundStore;
@@ -14,7 +15,7 @@ import org.newdawn.slick.openal.SoundStore;
*
* @author MightyPork
*/
-public class AudioX implements Audio {
+public class AudioX implements Destroyable {
private enum PlayMode
{
@@ -22,14 +23,26 @@ public class AudioX implements Audio {
};
private Audio audio = null;
- private float pauseLoopPosition = 0;
+ private double pauseLoopPosition = 0;
private boolean looping = false;
private boolean paused = false;
private PlayMode mode = PlayMode.EFFECT;
- private float pitch = 1;
- private float gain = 1;
+ private double lastPlayPitch = 1;
+ private double lastPlayGain = 1;
- private String resourcePath;
+ private final String resourcePath;
+ private boolean loadFailed = false;
+
+
+ /**
+ * Create deferred primitive audio player
+ *
+ * @param resourceName resource to load when needed
+ */
+ public AudioX(String resourceName) {
+ this.audio = null;
+ this.resourcePath = resourceName;
+ }
/**
@@ -37,7 +50,7 @@ public class AudioX implements Audio {
*/
public void pauseLoop()
{
- if (!ensureLoaded()) return;
+ if (!load()) return;
if (isPlaying() && looping) {
pauseLoopPosition = audio.getPosition();
@@ -54,16 +67,16 @@ public class AudioX implements Audio {
*/
public int resumeLoop()
{
- if (!ensureLoaded()) return -1;
+ if (!load()) return -1;
int source = -1;
if (looping && paused) {
if (mode == PlayMode.MUSIC) {
- source = audio.playAsMusic(pitch, gain, true);
+ source = audio.playAsMusic((float) lastPlayPitch, (float) lastPlayGain, true);
} else {
- source = audio.playAsSoundEffect(pitch, gain, true);
+ source = audio.playAsSoundEffect((float) lastPlayPitch, (float) lastPlayGain, true);
}
- audio.setPosition(pauseLoopPosition);
+ audio.setPosition((float) pauseLoopPosition);
paused = false;
}
return source;
@@ -71,34 +84,27 @@ public class AudioX implements Audio {
/**
- * Create deferred primitive audio player
+ * Check if resource is loaded
*
- * @param resourceName resource to load when needed
+ * @return resource is loaded
*/
- public AudioX(String resourceName) {
- this.audio = null;
- this.resourcePath = resourceName;
+ private boolean isLoaded()
+ {
+ return audio != null;
}
/**
- * Check if can play, if not, try to load sound.
+ * Try to load if not loaded already
*
- * @return can now play
+ * @return is loaded
*/
- private boolean ensureLoaded()
- {
- load();
-
- return audio != null;
- }
-
-
- public void load()
+ public boolean load()
{
- if (audio != null) return; // already loaded
- if (resourcePath == null) return; // not loaded, but can't load anyway
+ if (isLoaded()) return true; // already loaded
+ if (loadFailed || resourcePath == null) return false; // not loaded, but can't load anyway
+ loadFailed = false;
try {
String ext = FileUtils.getExtension(resourcePath);
@@ -112,119 +118,168 @@ public class AudioX implements Audio {
} else if (ext.equalsIgnoreCase("mod")) {
audio = SoundStore.get().getMOD(resourcePath);
} else {
- resourcePath = null; // don't try next time
Log.e("Invalid audio file extension: " + resourcePath);
+ loadFailed = true; // don't try next time
}
} catch (Exception e) {
Log.e("Could not load " + resourcePath, e);
- resourcePath = null; // don't try next time
+ loadFailed = true; // don't try next time
}
+
+ return isLoaded();
}
- @Override
public void stop()
{
- if (!ensureLoaded()) return;
+ if (!isLoaded()) return;
audio.stop();
paused = false;
}
- @Override
- public int getBufferID()
- {
- if (!ensureLoaded()) return -1;
-
- return audio.getBufferID();
- }
-
-
- @Override
public boolean isPlaying()
{
- if (!ensureLoaded()) return false;
+ if (!isLoaded()) return false;
return audio.isPlaying();
}
- @Override
public boolean isPaused()
{
- if (!ensureLoaded()) return false;
+ if (!isLoaded()) return false;
return audio.isPaused();
}
- @Override
- public int playAsSoundEffect(float pitch, float gain, boolean loop)
+ /**
+ * Play as sound effect at listener position
+ *
+ * @param pitch pitch (1 = default)
+ * @param gain gain (0-1)
+ * @param loop looping
+ * @return source id
+ */
+ public int playAsEffect(double pitch, double gain, boolean loop)
{
- return playAsSoundEffect(pitch, gain, loop, SoundManager.get().listener);
+ return playAsEffect(pitch, gain, loop, SoundSystem.getListener());
}
- @Override
- public int playAsSoundEffect(float pitch, float gain, boolean loop, float x, float y, float z)
+ /**
+ * Play as sound effect at given X-Y position
+ *
+ * @param pitch pitch (1 = default)
+ * @param gain gain (0-1)
+ * @param loop looping
+ * @param x
+ * @param y
+ * @return source id
+ */
+ public int playAsEffect(double pitch, double gain, boolean loop, double x, double y)
+ {
+ return playAsEffect(pitch, gain, loop, x, y, SoundSystem.getListener().z);
+ }
+
+
+ /**
+ * Play as sound effect at given position
+ *
+ * @param pitch pitch (1 = default)
+ * @param gain gain (0-1)
+ * @param loop looping
+ * @param x
+ * @param y
+ * @param z
+ * @return source id
+ */
+ public int playAsEffect(double pitch, double gain, boolean loop, double x, double y, double z)
{
- if (!ensureLoaded()) return -1;
+ if (!load()) return -1;
- this.pitch = pitch;
- this.gain = gain;
+ this.lastPlayPitch = pitch;
+ this.lastPlayGain = gain;
looping = loop;
mode = PlayMode.EFFECT;
- return audio.playAsSoundEffect(pitch, gain, loop, x, y, z);
+ return audio.playAsSoundEffect((float) pitch, (float) gain, loop, (float) x, (float) y, (float) z);
}
/**
- * Play this sound as a sound effect
+ * Play as sound effect at given position
*
- * @param pitch The pitch of the play back
- * @param gain The gain of the play back
- * @param loop True if we should loop
- * @param pos The position of the sound
- * @return The ID of the source playing the sound
+ * @param pitch pitch (1 = default)
+ * @param gain gain (0-1)
+ * @param loop looping
+ * @param pos coord
+ * @return source id
*/
- public int playAsSoundEffect(float pitch, float gain, boolean loop, Coord pos)
+ public int playAsEffect(double pitch, double gain, boolean loop, Coord pos)
{
- return playAsSoundEffect(pitch, gain, loop, (float) pos.x, (float) pos.y, (float) pos.z);
+ if (!load()) return -1;
+
+ return playAsEffect(pitch, gain, loop, pos.x, pos.y, pos.z);
}
- @Override
- public int playAsMusic(float pitch, float gain, boolean loop)
+ /**
+ * Play as music using source 0.
+ * Discouraged, since this does not allow cross-fading.
+ *
+ * @param pitch play pitch
+ * @param gain play gain
+ * @param loop looping
+ * @return source
+ */
+ public int playAsMusic(double pitch, double gain, boolean loop)
{
- this.pitch = pitch;
- this.gain = gain;
+ if (!load()) return -1;
+
+ this.lastPlayPitch = (float) pitch;
+ this.lastPlayGain = (float) gain;
looping = loop;
mode = PlayMode.MUSIC;
- return audio.playAsMusic(pitch, gain, loop);
+ return audio.playAsMusic((float) pitch, (float) gain, loop);
}
@Override
- public boolean setPosition(float position)
+ public void destroy()
{
- return audio.setPosition(position);
+ if (!isLoaded()) return;
+
+ audio.release();
+ audio = null;
}
@Override
- public float getPosition()
+ public int hashCode()
{
- return audio.getPosition();
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((resourcePath == null) ? 0 : resourcePath.hashCode());
+ return result;
}
@Override
- public void release()
+ public boolean equals(Object obj)
{
- audio.release();
- audio = null;
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (!(obj instanceof AudioX)) return false;
+ AudioX other = (AudioX) obj;
+ if (resourcePath == null) {
+ if (other.resourcePath != null) return false;
+ } else if (!resourcePath.equals(other.resourcePath)) {
+ return false;
+ }
+ return true;
}
}
diff --git a/src/mightypork/rogue/sounds/BaseAudioPlayer.java b/src/mightypork/rogue/sounds/BaseAudioPlayer.java
new file mode 100644
index 0000000..ac4a152
--- /dev/null
+++ b/src/mightypork/rogue/sounds/BaseAudioPlayer.java
@@ -0,0 +1,77 @@
+package mightypork.rogue.sounds;
+
+
+import mightypork.utils.objects.Mutable;
+
+
+public abstract class BaseAudioPlayer {
+
+ /** the track */
+ private AudioX audio;
+
+ /** base gain for sfx */
+ private double baseGain = 1;
+
+ /** base pitch for sfx */
+ private double basePitch = 1;
+
+ /** dedicated volume control */
+ private Mutable gainMultiplier = null;
+
+
+ public BaseAudioPlayer(AudioX track, double baseGain, Mutable gainMultiplier) {
+ this(track, 1, baseGain, gainMultiplier);
+ }
+
+
+ public BaseAudioPlayer(AudioX track, double basePitch, double baseGain, Mutable gainMultiplier) {
+ this.audio = track;
+
+ this.baseGain = baseGain;
+ this.basePitch = basePitch;
+
+ this.gainMultiplier = gainMultiplier;
+ }
+
+
+ public void destroy()
+ {
+ audio.destroy();
+ audio = null;
+ }
+
+
+ protected AudioX getAudio()
+ {
+ return audio;
+ }
+
+
+ protected double getGain(double multiplier)
+ {
+ return baseGain * gainMultiplier.get() * multiplier;
+ }
+
+
+ protected double getPitch(double multiplier)
+ {
+ return basePitch * multiplier;
+ }
+
+
+ /**
+ * Get if audio is valid
+ *
+ * @return is valid
+ */
+ protected boolean canPlay()
+ {
+ return (audio != null);
+ }
+
+
+ public void load()
+ {
+ if (canPlay()) audio.load();
+ }
+}
diff --git a/src/mightypork/rogue/sounds/EffectPlayer.java b/src/mightypork/rogue/sounds/EffectPlayer.java
index dfb4edf..ee26718 100644
--- a/src/mightypork/rogue/sounds/EffectPlayer.java
+++ b/src/mightypork/rogue/sounds/EffectPlayer.java
@@ -5,9 +5,9 @@ import mightypork.utils.math.coord.Coord;
import mightypork.utils.objects.Mutable;
-public class EffectPlayer extends AudioPlayer {
+public class EffectPlayer extends BaseAudioPlayer {
- public EffectPlayer(AudioX track, double basePitch, double baseGain, Mutable gainMultiplier) {
+ public EffectPlayer(AudioX track, double basePitch, double baseGain, Mutable gainMultiplier) {
super(track, (float) basePitch, (float) baseGain, gainMultiplier);
}
@@ -16,7 +16,7 @@ public class EffectPlayer extends AudioPlayer {
{
if (!canPlay()) return -1;
- return getAudio().playAsSoundEffect(getPitch(pitch), getGain(gain), false);
+ return getAudio().playAsEffect(getPitch(pitch), getGain(gain), false);
}
@@ -30,7 +30,7 @@ public class EffectPlayer extends AudioPlayer {
{
if (!canPlay()) return -1;
- return getAudio().playAsSoundEffect(getPitch(pitch), getGain(gain), false, pos);
+ return getAudio().playAsEffect(getPitch(pitch), getGain(gain), false, pos);
}
}
diff --git a/src/mightypork/rogue/sounds/JointVolume.java b/src/mightypork/rogue/sounds/JointVolume.java
index 7cb47bf..a314b3f 100644
--- a/src/mightypork/rogue/sounds/JointVolume.java
+++ b/src/mightypork/rogue/sounds/JointVolume.java
@@ -10,18 +10,18 @@ import mightypork.utils.objects.Mutable;
*
* @author MightyPork
*/
-public class JointVolume extends Mutable {
+public class JointVolume extends Mutable {
- private Mutable[] volumes;
+ private Mutable[] volumes;
/**
- * CReate joint volume with master gain of 1
+ * Create joint volume with master gain of 1
*
* @param volumes individual volumes to join
*/
- public JointVolume(Mutable... volumes) {
- super(1F);
+ public JointVolume(Mutable... volumes) {
+ super(1D);
this.volumes = volumes;
}
@@ -30,13 +30,13 @@ public class JointVolume extends Mutable {
* Get combined gain (multiplied)
*/
@Override
- public Float get()
+ public Double get()
{
- float f = super.get();
- for (Mutable v : volumes)
- f *= v.get();
+ double d = super.get();
+ for (Mutable v : volumes)
+ d *= v.get();
- return Calc.clampf(f, 0, 1);
+ return Calc.clampd(d, 0, 1);
}
@@ -44,7 +44,7 @@ public class JointVolume extends Mutable {
* Set master gain
*/
@Override
- public void set(Float o)
+ public void set(Double o)
{
super.set(o);
}
diff --git a/src/mightypork/rogue/sounds/LoopPlayer.java b/src/mightypork/rogue/sounds/LoopPlayer.java
index d894df3..858ae49 100644
--- a/src/mightypork/rogue/sounds/LoopPlayer.java
+++ b/src/mightypork/rogue/sounds/LoopPlayer.java
@@ -9,7 +9,7 @@ import mightypork.utils.time.Updateable;
import org.lwjgl.openal.AL10;
-public class LoopPlayer extends AudioPlayer implements Updateable, Pauseable {
+public class LoopPlayer extends BaseAudioPlayer implements Updateable, Pauseable {
private int sourceID = -1;
@@ -28,7 +28,7 @@ public class LoopPlayer extends AudioPlayer implements Updateable, Pauseable {
private double outTime = 1;
- public LoopPlayer(AudioX track, double pitch, double baseGain, Mutable gainMultiplier) {
+ public LoopPlayer(AudioX track, double pitch, double baseGain, Mutable gainMultiplier) {
super(track, (float) pitch, (float) baseGain, gainMultiplier);
paused = true;
@@ -45,7 +45,7 @@ public class LoopPlayer extends AudioPlayer implements Updateable, Pauseable {
private void initLoop()
{
if (!canPlay() && sourceID == -1) {
- sourceID = getAudio().playAsSoundEffect(getPitch(1), getGain(1), true);
+ sourceID = getAudio().playAsEffect(getPitch(1), getGain(1), true);
getAudio().pauseLoop();
}
}
diff --git a/src/mightypork/rogue/sounds/SoundManager.java b/src/mightypork/rogue/sounds/SoundManager.java
deleted file mode 100644
index 72b9c36..0000000
--- a/src/mightypork/rogue/sounds/SoundManager.java
+++ /dev/null
@@ -1,179 +0,0 @@
-package mightypork.rogue.sounds;
-
-
-import java.nio.FloatBuffer;
-import java.util.HashMap;
-import java.util.Map;
-
-import mightypork.utils.math.Calc.Buffers;
-import mightypork.utils.math.coord.Coord;
-import mightypork.utils.objects.Mutable;
-import mightypork.utils.time.Updateable;
-
-import org.lwjgl.openal.AL10;
-import org.newdawn.slick.openal.SoundStore;
-
-
-/**
- * Preloaded sounds.
- *
- * @author MightyPork
- */
-public class SoundManager implements Updateable {
-
- private static SoundManager inst = new SoundManager();
- public Mutable masterVolume = new Mutable(1F);
- @SuppressWarnings("unchecked")
- public Mutable effectsVolume = new JointVolume(masterVolume);
- @SuppressWarnings("unchecked")
- public Mutable loopsVolume = new JointVolume(masterVolume);
- public Coord listener = new Coord(Coord.ZERO);
- private Map effects = new HashMap();
- private Map loops = new HashMap();
-
-
- /**
- * Singleton constructor
- */
- private SoundManager() {
- SoundStore.get().setMaxSources(256);
- SoundStore.get().init();
- }
-
-
- public static SoundManager get()
- {
- return inst;
- }
-
-
- public void addEffect(String key, String resource, float pitch, float gain)
- {
- EffectPlayer p = new EffectPlayer(new AudioX(resource), pitch, gain, effectsVolume);
- effects.put(key, p);
- }
-
-
- public void addLoop(String key, String resource, float pitch, float gain, double fadeIn, double fadeOut)
- {
- LoopPlayer p = new LoopPlayer(new AudioX(resource), pitch, gain, loopsVolume);
- p.setFadeTimes(fadeIn, fadeOut);
- loops.put(key, p);
- }
-
-
- public LoopPlayer getLoop(String key)
- {
- LoopPlayer p = loops.get(key);
- if (p == null) {
- throw new IllegalArgumentException("Requesting unknown sound loop \"" + key + "\".");
- }
- return p;
- }
-
-
- public EffectPlayer getEffect(String key)
- {
- EffectPlayer p = effects.get(key);
- if (p == null) {
- throw new IllegalArgumentException("Requesting unknown sound effect \"" + key + "\".");
- }
- return p;
- }
-
-
- public void fadeOutAllLoops()
- {
- for (LoopPlayer p : loops.values()) {
- p.fadeOut();
- }
- }
-
-
- public void fadeInLoop(String key)
- {
- getLoop(key).fadeIn();
- }
-
-
- public void fadeInLoop(String key, double seconds)
- {
- getLoop(key).fadeIn(seconds);
- }
-
-
- public void pauseLoop(String key)
- {
- getLoop(key).pause();
- }
-
-
- public void pauseAllLoops()
- {
- for (LoopPlayer p : loops.values()) {
- p.pause();
- }
- }
-
-
- public void resumeLoop(String key)
- {
- getLoop(key).resume();
- }
-
-
- /**
- * Set listener pos
- *
- * @param pos
- */
- public void setListener(Coord pos)
- {
- listener.setTo(pos);
- FloatBuffer buf3 = Buffers.alloc(3);
- FloatBuffer buf6 = Buffers.alloc(6);
- buf3.clear();
- Buffers.fill(buf3, (float) pos.x, (float) pos.y, (float) pos.z);
- AL10.alListener(AL10.AL_POSITION, buf3);
- buf3.clear();
- Buffers.fill(buf3, 0, 0, 0);
- AL10.alListener(AL10.AL_VELOCITY, buf3);
- buf6.clear();
- Buffers.fill(buf6, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f);
- AL10.alListener(AL10.AL_ORIENTATION, buf6);
- buf3 = buf6 = null;
- }
-
-
- @Override
- public void update(double delta)
- {
- for (LoopPlayer lp : loops.values()) {
- lp.update(delta);
- }
- }
-
-
- public void setMasterVolume(float f)
- {
- masterVolume.set(f);
- }
-
-
- public void setEffectsVolume(float f)
- {
- effectsVolume.set(f);
- }
-
-
- public void setMusicVolume(float f)
- {
- loopsVolume.set(f);
- }
-
-
- public void destroy()
- {
- SoundStore.get().clear();
- }
-}
diff --git a/src/mightypork/rogue/sounds/SoundSystem.java b/src/mightypork/rogue/sounds/SoundSystem.java
new file mode 100644
index 0000000..f828271
--- /dev/null
+++ b/src/mightypork/rogue/sounds/SoundSystem.java
@@ -0,0 +1,309 @@
+package mightypork.rogue.sounds;
+
+
+import java.nio.FloatBuffer;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import mightypork.utils.math.Calc.Buffers;
+import mightypork.utils.math.coord.Coord;
+import mightypork.utils.objects.Mutable;
+import mightypork.utils.patterns.Destroyable;
+import mightypork.utils.time.Updateable;
+
+import org.lwjgl.openal.AL;
+import org.lwjgl.openal.AL10;
+import org.newdawn.slick.openal.SoundStore;
+
+
+/**
+ * Sound system class (only one instance should be made per application)
+ *
+ * @author MightyPork
+ */
+@SuppressWarnings("unchecked")
+// needed for JointVolume
+public class SoundSystem implements Updateable, Destroyable {
+
+ // static
+
+ private static final Coord INITIAL_LISTENER_POS = new Coord(0, 0, 0);
+ private static final int MAX_SOURCES = 256;
+
+ private static Coord listener = new Coord();
+
+
+ static {
+ // initialize sound system
+ SoundStore.get().setMaxSources(MAX_SOURCES);
+ SoundStore.get().init();
+
+ setListener(INITIAL_LISTENER_POS);
+ }
+
+
+ /**
+ * Set listener pos
+ *
+ * @param pos
+ */
+ public static void setListener(Coord pos)
+ {
+ listener.setTo(pos);
+ FloatBuffer buf3 = Buffers.alloc(3);
+ FloatBuffer buf6 = Buffers.alloc(6);
+ buf3.clear();
+ Buffers.fill(buf3, (float) pos.x, (float) pos.y, (float) pos.z);
+ AL10.alListener(AL10.AL_POSITION, buf3);
+ buf3.clear();
+ Buffers.fill(buf3, 0, 0, 0);
+ AL10.alListener(AL10.AL_VELOCITY, buf3);
+ buf6.clear();
+ Buffers.fill(buf6, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f);
+ AL10.alListener(AL10.AL_ORIENTATION, buf6);
+ buf3 = buf6 = null;
+ }
+
+
+ public static Coord getListener()
+ {
+ return listener;
+ }
+
+ // instance
+
+ public Mutable masterVolume = new Mutable(1D);
+ public Mutable effectsVolume = new JointVolume(masterVolume);
+ public Mutable loopsVolume = new JointVolume(masterVolume);
+
+ private Map effects = new HashMap();
+ private Map loops = new HashMap();
+ private Set resources = new HashSet();
+
+
+ /**
+ * Register effect resource
+ *
+ * @param key sound key
+ * @param resource resource path
+ * @param pitch default pitch (1 = unchanged)
+ * @param gain default gain (0-1)
+ */
+ public void addEffect(String key, String resource, double pitch, double gain)
+ {
+ EffectPlayer p = new EffectPlayer(getResource(resource), pitch, gain, effectsVolume);
+ effects.put(key, p);
+ }
+
+
+ /**
+ * Register loop resource (music / effect loop)
+ *
+ * @param key sound key
+ * @param resource resource path
+ * @param pitch default pitch (1 = unchanged)
+ * @param gain default gain (0-1)
+ * @param fadeIn default time for fadeIn
+ * @param fadeOut default time for fadeOut
+ */
+ public void addLoop(String key, String resource, double pitch, double gain, double fadeIn, double fadeOut)
+ {
+ LoopPlayer p = new LoopPlayer(getResource(resource), pitch, gain, loopsVolume);
+ p.setFadeTimes(fadeIn, fadeOut);
+ loops.put(key, p);
+ }
+
+
+ /**
+ * Create {@link AudioX} for a resource
+ * @param res a resource name
+ * @return the resource
+ *
+ * @throws IllegalArgumentException if resource is already registered
+ */
+ private AudioX getResource(String res) {
+ AudioX a = new AudioX(res);
+ if(resources.contains(a)) throw new IllegalArgumentException("Sound resource "+res+" is already registered.");
+ resources.add(a);
+ return a;
+ }
+
+
+ /**
+ * Get a loop player for key
+ *
+ * @param key sound key
+ * @return loop player
+ */
+ public LoopPlayer getLoop(String key)
+ {
+ LoopPlayer p = loops.get(key);
+ if (p == null) {
+ throw new IllegalArgumentException("Requesting unknown sound loop \"" + key + "\".");
+ }
+ return p;
+ }
+
+
+ /**
+ * Get a effect player for key
+ *
+ * @param key sound key
+ * @return effect player
+ */
+ public EffectPlayer getEffect(String key)
+ {
+ EffectPlayer p = effects.get(key);
+ if (p == null) {
+ throw new IllegalArgumentException("Requesting unknown sound effect \"" + key + "\".");
+ }
+ return p;
+ }
+
+
+ /**
+ * Fade out all loops (ie. for screen transitions)
+ */
+ public void fadeOutAllLoops()
+ {
+ for (LoopPlayer p : loops.values()) {
+ p.fadeOut();
+ }
+ }
+
+
+ /**
+ * Fade in a loop (with default time)
+ *
+ * @param key sound key
+ */
+ public void fadeInLoop(String key)
+ {
+ getLoop(key).fadeIn();
+ }
+
+
+ /**
+ * Fade in a loop
+ *
+ * @param key sound key
+ * @param seconds fade-in duration
+ */
+ public void fadeInLoop(String key, double seconds)
+ {
+ getLoop(key).fadeIn(seconds);
+ }
+
+
+ /**
+ * Fade out a loop (with default time)
+ *
+ * @param key sound key
+ */
+ public void fadeOutLoop(String key)
+ {
+ getLoop(key).fadeOut();
+ }
+
+
+ /**
+ * Fade out a loop
+ *
+ * @param key sound key
+ * @param seconds fade-out duration
+ */
+ public void fadeOutLoop(String key, double seconds)
+ {
+ getLoop(key).fadeOut(seconds);
+ }
+
+
+ /**
+ * Pause a loop
+ *
+ * @param key sound key
+ */
+ public void pauseLoop(String key)
+ {
+ getLoop(key).pause();
+ }
+
+
+ /**
+ * Pause all loops (leave volume unchanged)
+ */
+ public void pauseAllLoops()
+ {
+ for (LoopPlayer p : loops.values()) {
+ p.pause();
+ }
+ }
+
+
+ /**
+ * Resume a loop
+ *
+ * @param key sound key
+ */
+ public void resumeLoop(String key)
+ {
+ getLoop(key).resume();
+ }
+
+
+ @Override
+ public void update(double delta)
+ {
+ for (LoopPlayer lp : loops.values()) {
+ lp.update(delta);
+ }
+ }
+
+
+ /**
+ * Set level of master volume
+ *
+ * @param d level
+ */
+ public void setMasterVolume(double d)
+ {
+ masterVolume.set(d);
+ }
+
+
+ /**
+ * Set level of effects volume
+ *
+ * @param d level
+ */
+ public void setEffectsVolume(double d)
+ {
+ effectsVolume.set(d);
+ }
+
+
+ /**
+ * Set level of music volume
+ *
+ * @param d level
+ */
+ public void setMusicVolume(double d)
+ {
+ loopsVolume.set(d);
+ }
+
+
+ @Override
+ public void destroy()
+ {
+ for(AudioX r: resources) {
+ r.destroy();
+ }
+
+ SoundStore.get().clear();
+ AL.destroy();
+ }
+
+}
diff --git a/src/mightypork/rogue/tasks/TaskTakeScreenshot.java b/src/mightypork/rogue/tasks/TaskTakeScreenshot.java
new file mode 100644
index 0000000..dde9961
--- /dev/null
+++ b/src/mightypork/rogue/tasks/TaskTakeScreenshot.java
@@ -0,0 +1,61 @@
+package mightypork.rogue.tasks;
+
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import javax.imageio.ImageIO;
+
+import mightypork.rogue.App;
+import mightypork.rogue.Paths;
+import mightypork.utils.logging.Log;
+
+
+public class TaskTakeScreenshot implements Runnable {
+
+ private BufferedImage image;
+
+
+ public TaskTakeScreenshot() {
+ this.image = App.disp().takeScreenshot();
+ }
+
+
+ @Override
+ public void run()
+ {
+ String fname = getUniqueScreenshotName();
+
+ // generate unique filename
+ File file;
+ int index = 0;
+ while (true) {
+ file = new File(Paths.SCREENSHOTS, fname + (index > 0 ? "-" + index : "") + ".png");
+ if (!file.exists()) break;
+ index++;
+ }
+
+ Log.f3("Saving screenshot to file: " + file);
+
+ String format = "PNG";
+
+ // save to disk
+ try {
+ ImageIO.write(image, format, file);
+ } catch (IOException e) {
+ Log.e("Failed to save screenshot.", e);
+ }
+ }
+
+
+ private String getUniqueScreenshotName()
+ {
+ DateFormat df = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss");
+ return df.format(new Date());
+ }
+
+}
diff --git a/src/mightypork/rogue/threads/ThreadSaveScreenshot.java b/src/mightypork/rogue/threads/ThreadSaveScreenshot.java
deleted file mode 100644
index 0d04c57..0000000
--- a/src/mightypork/rogue/threads/ThreadSaveScreenshot.java
+++ /dev/null
@@ -1,91 +0,0 @@
-package mightypork.rogue.threads;
-
-
-import java.awt.image.BufferedImage;
-import java.io.File;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-
-import javax.imageio.ImageIO;
-
-import mightypork.rogue.Paths;
-import mightypork.utils.logging.Log;
-
-
-/**
- * Thread for saving screenshot
- *
- * @author MightyPork
- */
-public class ThreadSaveScreenshot extends Thread {
-
- private ByteBuffer buffer;
- private int width;
- private int height;
- private int bpp;
-
-
- /**
- * Save screenshot thread
- *
- * @param buffer byte buffer with image data
- * @param width screen width
- * @param height screen height
- * @param bpp bits per pixel
- */
- public ThreadSaveScreenshot(ByteBuffer buffer, int width, int height, int bpp) {
- this.buffer = buffer;
- this.width = width;
- this.height = height;
- this.bpp = bpp;
- }
-
-
- private String getUniqueScreenshotName()
- {
- DateFormat df = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss");
- return df.format(new Date());
- }
-
-
- @Override
- public void run()
- {
- String fname = getUniqueScreenshotName();
-
- // generate unique filename
- File file;
- int index = 0;
- while (true) {
- file = new File(Paths.SCREENSHOTS, fname + (index > 0 ? "-" + index : "") + ".png");
- if (!file.exists()) break;
- index++;
- }
-
- Log.f3("Saving screenshot to file: " + file);
-
- String format = "PNG";
- BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
-
- // convert to a buffered image
- for (int x = 0; x < width; x++) {
- for (int y = 0; y < height; y++) {
- int i = (x + (width * y)) * bpp;
- int r = buffer.get(i) & 0xFF;
- int g = buffer.get(i + 1) & 0xFF;
- int b = buffer.get(i + 2) & 0xFF;
- image.setRGB(x, height - (y + 1), (0xFF << 24) | (r << 16) | (g << 8) | b);
- }
- }
-
- // save to disk
- try {
- ImageIO.write(image, format, file);
- } catch (IOException e) {
- Log.e("Failed to save screenshot.", e);
- }
- }
-}
diff --git a/src/mightypork/rogue/threads/ThreadScreenshotTrigger.java b/src/mightypork/rogue/threads/ThreadScreenshotTrigger.java
deleted file mode 100644
index 8b1bc88..0000000
--- a/src/mightypork/rogue/threads/ThreadScreenshotTrigger.java
+++ /dev/null
@@ -1,32 +0,0 @@
-package mightypork.rogue.threads;
-
-
-import mightypork.rogue.App;
-import mightypork.rogue.input.Keys;
-import mightypork.utils.logging.Log;
-
-import org.lwjgl.input.Keyboard;
-
-
-/**
- * @author MightyPork
- */
-public class ThreadScreenshotTrigger extends Thread {
-
- @Override
- public void run()
- {
- while (true) {
- if (Keys.justPressed(Keyboard.KEY_F2)) {
- Log.f2("F2, taking screenshot.");
- App.scheduledScreenshot = true;
- Keys.destroyChangeState(Keyboard.KEY_F2);
- }
- try {
- sleep(2);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
-}
diff --git a/src/mightypork/rogue/util/RenderUtils.java b/src/mightypork/rogue/util/RenderUtils.java
index 8747797..0643515 100644
--- a/src/mightypork/rogue/util/RenderUtils.java
+++ b/src/mightypork/rogue/util/RenderUtils.java
@@ -4,6 +4,7 @@ package mightypork.rogue.util;
import static org.lwjgl.opengl.GL11.*;
import mightypork.rogue.textures.TextureManager;
import mightypork.rogue.textures.TxQuad;
+import mightypork.utils.math.Calc.Deg;
import mightypork.utils.math.color.HSV;
import mightypork.utils.math.color.RGB;
import mightypork.utils.math.coord.Coord;
@@ -18,6 +19,10 @@ import org.newdawn.slick.opengl.Texture;
* @author MightyPork
*/
public class RenderUtils {
+
+ private static final Coord AXIS_X = new Coord(1, 0, 0);
+ private static final Coord AXIS_Y = new Coord(0, 1, 0);
+ private static final Coord AXIS_Z = new Coord(0, 0, 1);
/**
* Render quad 2D
@@ -471,15 +476,15 @@ public class RenderUtils {
*/
public static void quadTexturedAbs(Rect quad, Rect textureCoords)
{
- double left = quad.x1();
- double bottom = quad.y2();
- double right = quad.x2();
- double top = quad.y1();
+ double left = quad.xMin();
+ double bottom = quad.yMax();
+ double right = quad.xMax();
+ double top = quad.yMin();
- double tleft = textureCoords.x1();
- double tbottom = textureCoords.y1();
- double tright = textureCoords.x2();
- double ttop = textureCoords.y2();
+ double tleft = textureCoords.xMin();
+ double tbottom = textureCoords.yMin();
+ double tright = textureCoords.xMax();
+ double ttop = textureCoords.yMax();
glBegin(GL_QUADS);
glTexCoord2d(tleft, ttop);
@@ -584,18 +589,18 @@ public class RenderUtils {
int yOffset = yOffsetTimes * frameHeight;
- double x1 = quadRect.x1();
- double y1 = quadRect.y1();
- double x2 = quadRect.x2();
- double y2 = quadRect.y2();
+ double x1 = quadRect.xMin();
+ double y1 = quadRect.yMin();
+ double x2 = quadRect.xMax();
+ double y2 = quadRect.yMax();
double w = x2 - x1;
double h = y2 - y1;
- double tx1 = textureRect.x1();
- double ty1 = textureRect.y1();
- double tx2 = textureRect.x2();
- double ty2 = textureRect.y2();
+ double tx1 = textureRect.xMin();
+ double ty1 = textureRect.yMin();
+ double tx2 = textureRect.xMax();
+ double ty2 = textureRect.yMax();
double halfY = h / 2D;
double halfX = w / 2D;
@@ -713,15 +718,15 @@ public class RenderUtils {
{
Texture tx = texture;
- double x1 = quadRect.x1();
- double y1 = quadRect.y1();
- double x2 = quadRect.x2();
- double y2 = quadRect.y2();
+ double x1 = quadRect.xMin();
+ double y1 = quadRect.yMin();
+ double x2 = quadRect.xMax();
+ double y2 = quadRect.yMax();
- double tx1 = textureRect.x1();
- double ty1 = textureRect.y1();
- double tx2 = textureRect.x2();
- double ty2 = textureRect.y2();
+ double tx1 = textureRect.xMin();
+ double ty1 = textureRect.yMin();
+ double tx2 = textureRect.xMax();
+ double ty2 = textureRect.yMax();
// lt, mi, rt
@@ -802,15 +807,15 @@ public class RenderUtils {
{
Texture tx = texture;
- double x1 = quadRect.x1();
- double y1 = quadRect.y1();
- double x2 = quadRect.x2();
- double y2 = quadRect.y2();
+ double x1 = quadRect.xMin();
+ double y1 = quadRect.yMin();
+ double x2 = quadRect.xMax();
+ double y2 = quadRect.yMax();
- double tx1 = textureRect.x1();
- double ty1 = textureRect.y1();
- double tx2 = textureRect.x2();
- double ty2 = textureRect.y2();
+ double tx1 = textureRect.xMin();
+ double ty1 = textureRect.yMin();
+ double tx2 = textureRect.xMax();
+ double ty2 = textureRect.yMax();
// tp
// mi
@@ -916,4 +921,29 @@ public class RenderUtils {
{
glTranslated(coord.x, coord.y, coord.z);
}
+
+
+ public static void rotateX(double angle)
+ {
+ rotate(angle, AXIS_X);
+ }
+
+
+ public static void rotateY(double angle)
+ {
+ rotate(angle, AXIS_Y);
+ }
+
+
+ public static void rotateZ(double angle)
+ {
+ rotate(angle, AXIS_Z);
+ }
+
+
+ public static void rotate(double angle, Coord axis)
+ {
+ Coord vec = axis.norm(1);
+ glRotated(angle, vec.x, vec.y, vec.z);
+ }
}
diff --git a/src/mightypork/rogue/util/Utils.java b/src/mightypork/rogue/util/Utils.java
index 3ac307a..eadec42 100644
--- a/src/mightypork/rogue/util/Utils.java
+++ b/src/mightypork/rogue/util/Utils.java
@@ -7,4 +7,9 @@ package mightypork.rogue.util;
* @author MightyPork
*/
public class Utils {
+ public static Thread runAsThread(Runnable r) {
+ Thread t = new Thread(r);
+ t.start();
+ return t;
+ }
}
diff --git a/src/mightypork/utils/logging/Log.java b/src/mightypork/utils/logging/Log.java
index 6b9657c..ba7c858 100644
--- a/src/mightypork/utils/logging/Log.java
+++ b/src/mightypork/utils/logging/Log.java
@@ -8,7 +8,7 @@ import java.util.HashMap;
public class Log {
/** enable static logging */
- private static boolean esl;
+ private static boolean esl = true;
/**
diff --git a/src/mightypork/utils/math/Calc.java b/src/mightypork/utils/math/Calc.java
index e3fbd66..c8ff741 100644
--- a/src/mightypork/utils/math/Calc.java
+++ b/src/mightypork/utils/math/Calc.java
@@ -7,7 +7,7 @@ import java.util.List;
import java.util.Random;
import mightypork.utils.math.coord.Coord;
-import mightypork.utils.math.coord.Vec;
+import mightypork.utils.math.easing.Easing;
import org.lwjgl.BufferUtils;
@@ -31,7 +31,7 @@ public class Calc {
* @param point point coordinate
* @return distance
*/
- public static double linePointDist(Vec lineDirVec, Coord linePoint, Coord point)
+ public static double linePointDist(Coord lineDirVec, Coord linePoint, Coord point)
{
// line point L[lx,ly]
double lx = linePoint.x;
@@ -59,9 +59,9 @@ public class Calc {
* @param point point coordinate
* @return distance
*/
- public static double linePointDistXZ(Vec lineDirVec, Coord linePoint, Coord point)
+ public static double linePointDistXZ(Coord lineDirVec, Coord linePoint, Coord point)
{
- return linePointDist(new Vec(lineDirVec.x, lineDirVec.z), new Coord(linePoint.x, linePoint.z), new Coord(point.x, point.z));
+ return linePointDist(new Coord(lineDirVec.x, lineDirVec.z), new Coord(linePoint.x, linePoint.z), new Coord(point.x, point.z));
}
@@ -739,28 +739,45 @@ public class Calc {
/**
* Get number from A to B at delta time (tween A to B)
*
- * @param last last number
- * @param now new number
- * @param dtime delta time
+ * @param from last number
+ * @param to new number
+ * @param time time 0..1
+ * @param easing easing function
* @return current number to render
*/
- public static double interpolate(double last, double now, double dtime)
- {
- return last + (now - last) * dtime;
+ public static double interpolate(double from, double to, double time, Easing easing)
+ {
+ return from + (to - from) * easing.get(time);
}
/**
* Get angle [degrees] from A to B at delta time (tween A to B)
*
- * @param last last angle
- * @param now new angle
- * @param delta delta time
+ * @param from last angle
+ * @param to new angle
+ * @param time time 0..1
+ * @param easing easing function
+ * @return current angle to render
+ */
+ public static double interpolateDeg(double from, double to, double time, Easing easing)
+ {
+ return Deg.norm(from - Deg.delta(to, from) * easing.get(time));
+ }
+
+
+ /**
+ * Get angle [radians] from A to B at delta time (tween A to B)
+ *
+ * @param from last angle
+ * @param to new angle
+ * @param time time 0..1
+ * @param easing easing function
* @return current angle to render
*/
- public static double interpolateDeg(double last, double now, double delta)
+ public static double interpolateRad(double from, double to, double time, Easing easing)
{
- return Deg.norm(last + Deg.delta(now, last) * delta);
+ return Rad.norm(from - Rad.delta(to, from) * easing.get(time));
}
diff --git a/src/mightypork/utils/math/Polar.java b/src/mightypork/utils/math/Polar.java
index d7739d1..65f307c 100644
--- a/src/mightypork/utils/math/Polar.java
+++ b/src/mightypork/utils/math/Polar.java
@@ -1,6 +1,8 @@
package mightypork.utils.math;
+import mightypork.utils.math.Calc.Deg;
+import mightypork.utils.math.Calc.Rad;
import mightypork.utils.math.coord.Coord;
@@ -12,18 +14,87 @@ import mightypork.utils.math.coord.Coord;
public class Polar {
/** angle in radians */
- public double angle = 0;
+ private double angle = 0;
+
/** distance in units */
- public double distance = 0;
+ private double radius = 0;
/**
- * @param angle angle in radians
+ * Create a polar
+ *
+ * @param angle angle in RAD
* @param distance distance from origin
*/
public Polar(double angle, double distance) {
+ this(angle, false, distance);
+ }
+
+
+ /**
+ * Create a polar
+ *
+ * @param angle angle
+ * @param deg angle is in DEG
+ * @param distance radius
+ */
+ public Polar(double angle, boolean deg, double distance) {
+ this.radius = distance;
+ this.angle = deg ? Deg.toRad(angle) : angle;
+ }
+
+
+ /**
+ * @return angle in RAD
+ */
+ public double getAngle()
+ {
+ return angle;
+ }
+
+
+ /**
+ * @return angle in DEG
+ */
+ public double getAngleDeg()
+ {
+ return Rad.toDeg(angle);
+ }
+
+
+ /**
+ * @param angle angle in RAD
+ */
+ public void setAngle(double angle)
+ {
this.angle = angle;
- this.distance = distance;
+ }
+
+
+ /**
+ * @param angle angle in DEG
+ */
+ public void setAngleDeg(double angle)
+ {
+ this.angle = Deg.toRad(angle);
+ }
+
+
+ /**
+ * @return radius
+ */
+ public double getRadius()
+ {
+ return radius;
+ }
+
+
+ /**
+ * @param r radius
+ */
+ public void setRadius(double r)
+ {
+ this.radius = r;
}
@@ -52,19 +123,6 @@ public class Polar {
}
- /**
- * Make polar from coords
- *
- * @param x x coord
- * @param z z coord
- * @return polar
- */
- public static Polar fromCoordXZ(double x, double z)
- {
- return Polar.fromCoordXZ(new Coord(x, 0, z));
- }
-
-
/**
* Get coord from polar
*
@@ -72,36 +130,40 @@ public class Polar {
*/
public Coord toCoord()
{
- return new Coord(distance * Math.cos(angle), distance * Math.sin(angle));
+ return new Coord(radius * Math.cos(angle), radius * Math.sin(angle));
}
- /**
- * Get X,0,Y coord from polar
- *
- * @return coord
- */
- public Coord toCoordXZ()
+ @Override
+ public String toString()
{
- return new Coord(distance * Math.cos(angle), 0, distance * Math.sin(angle));
+ return "Polar(" + angle + "rad, " + radius + ")";
}
@Override
- public String toString()
+ public int hashCode()
{
- return "Polar(theta=" + angle + ", r=" + distance + ")";
+ final int prime = 31;
+ int result = 1;
+ long temp;
+ temp = Double.doubleToLongBits(angle);
+ result = prime * result + (int) (temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(radius);
+ result = prime * result + (int) (temp ^ (temp >>> 32));
+ return result;
}
- /**
- * Build polar from X,Z instead of X,Y
- *
- * @param coord cpprd with X,Z
- * @return polar
- */
- public static Polar fromCoordXZ(Coord coord)
+ @Override
+ public boolean equals(Object obj)
{
- return fromCoord(coord.x, coord.z);
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (!(obj instanceof Polar)) return false;
+ Polar other = (Polar) obj;
+ if (Double.doubleToLongBits(angle) != Double.doubleToLongBits(other.angle)) return false;
+ if (Double.doubleToLongBits(radius) != Double.doubleToLongBits(other.radius)) return false;
+ return true;
}
}
diff --git a/src/mightypork/utils/math/PolarDeg.java b/src/mightypork/utils/math/PolarDeg.java
deleted file mode 100644
index 8a244c7..0000000
--- a/src/mightypork/utils/math/PolarDeg.java
+++ /dev/null
@@ -1,111 +0,0 @@
-package mightypork.utils.math;
-
-
-import mightypork.utils.math.Calc.Deg;
-import mightypork.utils.math.Calc.Rad;
-import mightypork.utils.math.coord.Coord;
-
-
-/**
- * Polar coordinate in degrees
- *
- * @author MightyPork
- */
-public class PolarDeg {
-
- /** angle in degrees */
- public double angle = 0;
- /** distance in units */
- public double distance = 0;
-
-
- /**
- * Polar coordinate in degrees
- *
- * @param angle angle in degrees
- * @param distance distance from origin
- */
- public PolarDeg(double angle, double distance) {
- this.angle = angle;
- this.distance = distance;
- }
-
-
- /**
- * Make polar from coord
- *
- * @param coord coord
- * @return polar
- */
- public static PolarDeg fromCoord(Coord coord)
- {
- return new PolarDeg(Rad.toDeg(Math.atan2(coord.y, coord.x)), Math.sqrt(Calc.square(coord.x) + Calc.square(coord.y)));
- }
-
-
- /**
- * Make polar from coords
- *
- * @param x x coord
- * @param y y coord
- * @return polar
- */
- public static PolarDeg fromCoord(double x, double y)
- {
- return PolarDeg.fromCoord(new Coord(x, y));
- }
-
-
- /**
- * Make polar from coords
- *
- * @param x x coord
- * @param z y coord
- * @return polar
- */
- public static PolarDeg fromCoordXZ(double x, double z)
- {
- return PolarDeg.fromCoordXZ(new Coord(x, 0, z));
- }
-
-
- /**
- * Get coord from polar
- *
- * @return coord
- */
- public Coord toCoord()
- {
- return new Coord(distance * Math.cos(Deg.toRad(angle)), distance * Math.sin(Deg.toRad(angle)));
- }
-
-
- /**
- * Get X,0,Y coord from polar
- *
- * @return coord
- */
- public Coord toCoordXZ()
- {
- return new Coord(distance * Math.cos(Deg.toRad(angle)), 0, distance * Math.sin(Deg.toRad(angle)));
- }
-
-
- @Override
- public String toString()
- {
- return "Polar(theta=" + angle + ", r=" + distance + ")";
- }
-
-
- /**
- * Build polar from X,Z instead of X,Y
- *
- * @param coord cpprd with X,Z
- * @return polar
- */
- public static PolarDeg fromCoordXZ(Coord coord)
- {
- return fromCoord(coord.x, coord.z);
- }
-}
diff --git a/src/mightypork/utils/math/coord/Coord.java b/src/mightypork/utils/math/coord/Coord.java
index 21e5172..7e40402 100644
--- a/src/mightypork/utils/math/coord/Coord.java
+++ b/src/mightypork/utils/math/coord/Coord.java
@@ -4,7 +4,6 @@ package mightypork.utils.math.coord;
import java.util.Random;
import mightypork.utils.math.Calc;
-import mightypork.utils.time.Updateable;
/**
@@ -12,13 +11,7 @@ import mightypork.utils.time.Updateable;
*
* @author MightyPork
*/
-public class Coord implements Updateable {
-
- /** Coord [1;1;1] */
- public static final Coord ONE = new Coord(1, 1, 1);
-
- /** Zero Coord */
- public static final Coord ZERO = new Coord(0, 0);
+public class Coord {
/** RNG */
protected static Random rand = new Random();
@@ -63,14 +56,6 @@ public class Coord implements Updateable {
(rand.nextBoolean() ? -1 : 1) * (min + rand.nextDouble() * (max - min)));
}
- private double animTime = 0;
-
- private Vec offs;
-
- private Coord start;
-
- private double time = 0;
-
/** X coordinate */
public double x = 0;
@@ -130,7 +115,7 @@ public class Coord implements Updateable {
*/
public Coord add(Coord vec)
{
- return copy().add_ip(vec);
+ return getCopy().add_ip(vec);
}
@@ -143,7 +128,7 @@ public class Coord implements Updateable {
*/
public Coord add(Number x, Number y)
{
- return copy().add_ip(x, y);
+ return getCopy().add_ip(x, y);
}
@@ -157,7 +142,7 @@ public class Coord implements Updateable {
*/
public Coord add(Number x, Number y, Number z)
{
- return copy().add_ip(x, y, z);
+ return getCopy().add_ip(x, y, z);
}
@@ -208,25 +193,10 @@ public class Coord implements Updateable {
}
- /**
- * Start animation
- *
- * @param time anim length
- */
- public void animate(double time)
- {
- if (start == null) start = new Coord();
- if (offs == null) offs = new Vec();
- this.time = time;
- animTime = 0;
- offs = start.vecTo(this);
- }
-
-
/**
* @return copy of this vector
*/
- public Coord copy()
+ public Coord getCopy()
{
return new Coord(x, y, z);
}
@@ -252,7 +222,7 @@ public class Coord implements Updateable {
*/
public Coord div(double d)
{
- return copy().div_ip(d);
+ return getCopy().div_ip(d);
}
@@ -272,48 +242,6 @@ public class Coord implements Updateable {
}
- @Override
- public boolean equals(Object obj)
- {
- if (obj == null) return false;
- if (!obj.getClass().isAssignableFrom(Coord.class)) return false;
- Coord other = (Coord) obj;
- return x == other.x && y == other.y && z == other.z;
- }
-
-
- /**
- * Get current value (animated)
- *
- * @return curent value
- */
- public Coord getDelta()
- {
- if (start == null) start = new Coord();
- if (offs == null) offs = new Vec();
- if (isFinished()) return this;
- return new Coord(start.add(offs.scale(animTime / time)));
- }
-
-
- @Override
- public int hashCode()
- {
- return Double.valueOf(x).hashCode() ^ Double.valueOf(y).hashCode() ^ Double.valueOf(z).hashCode();
- }
-
-
- /**
- * Get if animation is finished
- *
- * @return is finished
- */
- public boolean isFinished()
- {
- return animTime >= time;
- }
-
-
/**
* Check if this rectangle in inside a rectangular zone
*
@@ -347,7 +275,7 @@ public class Coord implements Updateable {
*/
public Coord midTo(Coord other)
{
- return add(vecTo(other).scale(0.5));
+ return add(vecTo(other).mul_ip(0.5));
}
@@ -359,7 +287,7 @@ public class Coord implements Updateable {
*/
public Coord mul(double d)
{
- return copy().mul_ip(d);
+ return getCopy().mul_ip(d);
}
@@ -373,7 +301,7 @@ public class Coord implements Updateable {
*/
public Coord mul(double xd, double yd, double zd)
{
- return copy().mul_ip(xd, yd, zd);
+ return getCopy().mul_ip(xd, yd, zd);
}
@@ -417,10 +345,9 @@ public class Coord implements Updateable {
*/
public Coord random_offset(double max)
{
- Coord r = random(1);
- Vec v = new Vec(r);
- v.norm_ip(0.00001 + rand.nextDouble() * max);
- return copy().add_ip(v);
+ Coord v = random(1).norm_ip(0.00001 + rand.nextDouble() * max);
+
+ return getCopy().add_ip(v);
}
@@ -433,7 +360,7 @@ public class Coord implements Updateable {
*/
public Coord random_offset(double min, double max)
{
- return copy().add_ip(random(min, max));
+ return getCopy().add_ip(random(min, max));
}
@@ -463,17 +390,6 @@ public class Coord implements Updateable {
}
- /**
- * Remember position (other changes will be for animation)
- */
- public void remember()
- {
- if (start == null) start = new Coord();
- if (offs == null) offs = new Vec();
- start.setTo(this);
- }
-
-
/**
* Get a copy with rounded coords
*
@@ -481,7 +397,7 @@ public class Coord implements Updateable {
*/
public Coord round()
{
- return copy().round_ip();
+ return getCopy().round_ip();
}
@@ -577,7 +493,7 @@ public class Coord implements Updateable {
*/
public Coord setX(Number x)
{
- return copy().setX_ip(x);
+ return getCopy().setX_ip(x);
}
@@ -602,7 +518,7 @@ public class Coord implements Updateable {
*/
public Coord setY(Number y)
{
- return copy().setY_ip(y);
+ return getCopy().setY_ip(y);
}
@@ -627,7 +543,7 @@ public class Coord implements Updateable {
*/
public Coord setZ(Number z)
{
- return copy().setZ_ip(z);
+ return getCopy().setZ_ip(z);
}
@@ -644,17 +560,6 @@ public class Coord implements Updateable {
}
- /**
- * Get size
- *
- * @return size
- */
- public double size()
- {
- return new Vec(this).size();
- }
-
-
/**
* Get a copy subtracted by vector
*
@@ -663,7 +568,7 @@ public class Coord implements Updateable {
*/
public Coord sub(Coord vec)
{
- return copy().sub_ip(vec);
+ return getCopy().sub_ip(vec);
}
@@ -676,7 +581,7 @@ public class Coord implements Updateable {
*/
public Coord sub(Number x, Number y)
{
- return copy().sub_ip(x, y);
+ return getCopy().sub_ip(x, y);
}
@@ -690,7 +595,7 @@ public class Coord implements Updateable {
*/
public Coord sub(Number x, Number y, Number z)
{
- return copy().sub_ip(x, y, z);
+ return getCopy().sub_ip(x, y, z);
}
@@ -741,52 +646,15 @@ public class Coord implements Updateable {
}
- /**
- * Convert X and Y coordinates of this coord to a new CoordI.
- *
- * @return the new CoordI
- */
- public CoordI toCoordI()
- {
- return new CoordI((int) Math.round(x), (int) Math.round(y));
- }
-
-
- @Override
- public String toString()
- {
- return "[ " + x + " ; " + y + " ; " + z + " ]";
- }
-
-
- /**
- * Update delta timing
- *
- * @param delta delta time to add
- */
- @Override
- public void update(double delta)
- {
- if (start == null) start = new Coord();
- if (offs == null) offs = new Vec();
- animTime = Calc.clampd(animTime + delta, 0, time);
- if (isFinished()) {
- time = 0;
- animTime = 0;
- start.setTo(this);
- }
- }
-
-
/**
* Create vector from this point to other point
*
* @param point second point
* @return vector
*/
- public Vec vecTo(Coord point)
+ public Coord vecTo(Coord point)
{
- return (Vec) (new Vec(point)).add(new Vec(this).neg());
+ return point.sub(this);
}
@@ -896,4 +764,213 @@ public class Coord implements Updateable {
{
return (int) Math.round(z);
}
+
+
+ /**
+ * Get cross product of two vectors
+ *
+ * @param a 1st vector
+ * @param b 2nd vector
+ * @return cross product
+ */
+ public static Coord cross(Coord a, Coord b)
+ {
+ return a.cross(b);
+ }
+
+
+ /**
+ * Get dot product of two vectors
+ *
+ * @param a 1st vector
+ * @param b 2nd vector
+ * @return dot product
+ */
+ public static double dot(Coord a, Coord b)
+ {
+ return a.dot(b);
+ }
+
+
+ /**
+ * Multiply by other vector, vector multiplication
+ *
+ * @param vec other vector
+ * @return copy multiplied
+ */
+ public Coord cross(Coord vec)
+ {
+ return getCopy().cross_ip(vec);
+ }
+
+
+ /**
+ * Multiply by other vector, vector multiplication; in place
+ *
+ * @param vec other vector
+ * @return this
+ */
+ public Coord cross_ip(Coord vec)
+ {
+ setTo(y * vec.z - z * vec.y, z * vec.x - x * vec.z, x * vec.y - y * vec.x);
+ return this;
+ }
+
+
+ /**
+ * Get dot product
+ *
+ * @param vec other vector
+ * @return dot product
+ */
+ public double dot(Coord vec)
+ {
+ return x * vec.x + y * vec.y + z * vec.z;
+ }
+
+
+ /**
+ * Negate all coordinates (* -1)
+ *
+ * @return negated coordinate
+ */
+ public Coord neg()
+ {
+ return getCopy().neg_ip();
+ }
+
+
+ /**
+ * Negate all coordinates (* -1), in place
+ *
+ * @return this
+ */
+ public Coord neg_ip()
+ {
+ mul_ip(-1);
+ return this;
+ }
+
+
+ /**
+ * Scale vector to given size
+ *
+ * @param size size we need
+ * @return scaled vector
+ */
+ public Coord norm(double size)
+ {
+ return getCopy().norm_ip(size);
+ }
+
+
+ /**
+ * Scale vector to given size, in place
+ *
+ * @param size size we need
+ * @return scaled vector
+ */
+ public Coord norm_ip(double size)
+ {
+ if (size() == 0) {
+ z = -1;
+ }
+ if (size == 0) {
+ setTo(0, 0, 0);
+ return this;
+ }
+ double k = size / size();
+ mul_ip(k);
+ return this;
+ }
+
+
+ /**
+ * Get vector size
+ *
+ * @return vector size in units
+ */
+ public double size()
+ {
+ return Math.sqrt(x * x + y * y + z * z);
+ }
+
+
+ /**
+ * Get copy divided by two
+ * @return copy halved
+ */
+ public Coord half()
+ {
+ return getCopy().half_ip();
+ }
+
+
+ /**
+ * Divide in place by two
+ * @return this
+ */
+ public Coord half_ip()
+ {
+ mul_ip(0.5);
+ return this;
+ }
+
+
+ @Override
+ public String toString()
+ {
+ return "[" + x + ", " + y + ", " + z + "]";
+ }
+
+
+ @Override
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ long temp;
+ temp = Double.doubleToLongBits(x);
+ result = prime * result + (int) (temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(y);
+ result = prime * result + (int) (temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(z);
+ result = prime * result + (int) (temp ^ (temp >>> 32));
+ return result;
+ }
+
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (!(obj instanceof Coord)) return false;
+ Coord other = (Coord) obj;
+ if (Double.doubleToLongBits(x) != Double.doubleToLongBits(other.x)) return false;
+ if (Double.doubleToLongBits(y) != Double.doubleToLongBits(other.y)) return false;
+ if (Double.doubleToLongBits(z) != Double.doubleToLongBits(other.z)) return false;
+ return true;
+ }
+
+
+ /**
+ * @return true if this coord is a zero coord
+ */
+ public boolean isZero()
+ {
+ return x == 0 && y == 0 && z == 0;
+ }
+
+
+ public static Coord zero()
+ {
+ return new Coord(0, 0, 0);
+ }
+
+
+ public static Coord one()
+ {
+ return new Coord(1, 1, 1);
+ }
}
diff --git a/src/mightypork/utils/math/coord/CoordAnimated.java b/src/mightypork/utils/math/coord/CoordAnimated.java
new file mode 100644
index 0000000..5605fad
--- /dev/null
+++ b/src/mightypork/utils/math/coord/CoordAnimated.java
@@ -0,0 +1,94 @@
+package mightypork.utils.math.coord;
+
+import mightypork.utils.math.Calc;
+import mightypork.utils.time.Updateable;
+
+
+public class CoordAnimated extends Coord implements Updateable {
+
+ private double animTime = 0;
+ private Coord offs;
+ private Coord start;
+ private double time = 0;
+
+ /**
+ * Update delta timing
+ *
+ * @param delta delta time to add
+ */
+ @Override
+ public void update(double delta)
+ {
+ if (start == null) start = new Coord();
+ if (offs == null) offs = new Coord();
+ animTime = Calc.clampd(animTime + delta, 0, time);
+ if (animIsFinished()) {
+ time = 0;
+ animTime = 0;
+ start.setTo(this);
+ }
+ }
+
+ /**
+ * Remember position (other changes will be for animation)
+ */
+ public void animRemember()
+ {
+ if (start == null) start = new Coord();
+ if (offs == null) offs = new Coord();
+ start.setTo(this);
+ offs = Coord.zero();
+ }
+
+ /**
+ * Start animation
+ *
+ * @param time anim length
+ */
+ public void animStart(double time)
+ {
+ if (start == null) start = new Coord();
+ if (offs == null) offs = new Coord();
+ this.time = time;
+ animTime = 0;
+ offs = start.vecTo(this);
+ }
+
+ /**
+ * Stop animation, assign to current value
+ */
+ public void animStop()
+ {
+ setTo(animGetCurrent());
+ animRemember();
+ animTime = 0;
+ }
+
+ /**
+ * Get if animation is finished
+ *
+ * @return is finished
+ */
+ public boolean animIsFinished()
+ {
+ return animTime >= time;
+ }
+
+ /**
+ * Get current value (animated)
+ *
+ * @return curent value
+ */
+ public Coord animGetCurrent()
+ {
+ if (time == 0) return getCopy(); // avoid zero division
+
+ if (start == null) start = new Coord();
+ if (offs == null) offs = new Coord();
+
+ if (animIsFinished()) return this;
+
+ return start.add(offs.mul(animTime / time));
+ }
+
+}
diff --git a/src/mightypork/utils/math/coord/CoordI.java b/src/mightypork/utils/math/coord/CoordI.java
deleted file mode 100644
index 16fa958..0000000
--- a/src/mightypork/utils/math/coord/CoordI.java
+++ /dev/null
@@ -1,398 +0,0 @@
-package mightypork.utils.math.coord;
-
-
-/**
- * Simple integer coordinate class
- * Unlike Coord, this is suitable for using in array indices etc.
- *
- * @author MightyPork
- */
-public class CoordI {
-
- /** X coordinate */
- public int x = 0;
- /** Y coordinate */
- public int y = 0;
- /** Z coordinate */
- public int z = 0;
-
-
- /**
- * Create CoordI as copy of other
- *
- * @param other coord to copy
- */
- public CoordI(CoordI other) {
- setTo(other);
- }
-
-
- /**
- * Integer 2D Coord
- *
- * @param x x coord
- * @param y y coord
- */
- public CoordI(int x, int y) {
- this.x = x;
- this.y = y;
- }
-
-
- /**
- * Integer 3D Coord
- *
- * @param x x coord
- * @param y y coord
- * @param z z coord
- */
- public CoordI(int x, int y, int z) {
- this.x = x;
- this.y = y;
- this.z = z;
- }
-
-
- /**
- * Empty cobnstructor 0,0,0
- */
- public CoordI() {
- x = 0;
- y = 0;
- z = 0;
- }
-
-
- /**
- * Add other coordI coordinates in a copy
- *
- * @param other coordI to add
- * @return copy modified
- */
- public CoordI add(CoordI other)
- {
- return copy().add_ip(other);
- }
-
-
- /**
- * Add coords in copy
- *
- * @param x x coord
- * @param y y coord
- * @return the copy
- */
- public CoordI add(int x, int y)
- {
- return copy().add_ip(x, y, 0);
- }
-
-
- /**
- * Add coords in copy
- *
- * @param x x coord
- * @param y y coord
- * @param z z coord
- * @return the copy
- */
- public CoordI add(int x, int y, int z)
- {
- return copy().add_ip(x, y, z);
- }
-
-
- /**
- * Add other coordI coordinates in place
- *
- * @param move coordI to add
- * @return this
- */
- public CoordI add_ip(CoordI move)
- {
- x += move.x;
- y += move.y;
- z += move.z;
- return this;
- }
-
-
- /**
- * Add coords in place
- *
- * @param x x coord
- * @param y y coord
- * @return this
- */
- public CoordI add_ip(int x, int y)
- {
- this.x += x;
- this.y += y;
- return this;
- }
-
-
- /**
- * Add coords in place
- *
- * @param x x coord
- * @param y y coord
- * @param z z coord
- * @return this
- */
- public CoordI add_ip(int x, int y, int z)
- {
- this.x += x;
- this.y += y;
- this.z += z;
- return this;
- }
-
-
- /**
- * Get copy
- *
- * @return copy
- */
- public CoordI copy()
- {
- return new CoordI(x, y, z);
- }
-
-
- @Override
- public boolean equals(Object obj)
- {
- if (obj == null) return false;
- if (obj instanceof CoordI) return ((CoordI) obj).x == x && ((CoordI) obj).y == y && ((CoordI) obj).z == z;
- return false;
- }
-
-
- @Override
- public int hashCode()
- {
- return x ^ y ^ z;
- }
-
-
- /**
- * Middle of this and other coordinate, rounded to CoordI - integers
- *
- * @param other other coordI
- * @return middle CoordI
- */
- public CoordI midTo(CoordI other)
- {
- return new CoordI((x + other.x) / 2, (y + other.y) / 2, (z + other.z) / 2);
- }
-
-
- /**
- * Multiply in copy 2D
- *
- * @param x x coord
- * @param y y coord
- * @return the copy
- */
- public CoordI mul(double x, double y)
- {
- return copy().mul_ip(x, y);
- }
-
-
- /**
- * Multiply in copy
- *
- * @param x x coord
- * @param y y coord
- * @param z z coord
- * @return the copy
- */
- public CoordI mul(double x, double y, double z)
- {
- return copy().mul_ip(x, y, z);
- }
-
-
- /**
- * Multiply in place 2D
- *
- * @param x x coord
- * @param y y coord
- * @return this
- */
- public CoordI mul_ip(double x, double y)
- {
- this.x *= x;
- this.y *= y;
- return this;
- }
-
-
- /**
- * Multiply in place
- *
- * @param x x coord
- * @param y y coord
- * @param z z coord
- * @return this
- */
- public CoordI mul_ip(double x, double y, double z)
- {
- this.x *= x;
- this.y *= y;
- this.z *= z;
- return this;
- }
-
-
- /**
- * Set to coords from other coord
- *
- * @param other source coord
- */
- public void setTo(CoordI other)
- {
- if (other == null) {
- setTo(0, 0, 0);
- return;
- }
- this.x = other.x;
- this.y = other.y;
- this.z = other.z;
- }
-
-
- /**
- * Set coords to
- *
- * @param x x coord to set
- * @param y y coord to set
- */
- public void setTo(int x, int y)
- {
- this.x = x;
- this.y = y;
- }
-
-
- /**
- * Set coords to
- *
- * @param x x coord to set
- * @param y y coord to set
- * @param z z coord to set
- */
- public void setTo(int x, int y, int z)
- {
- this.x = x;
- this.y = y;
- this.z = z;
- }
-
-
- /**
- * Subtract other coordI coordinates in a copy
- *
- * @param other coordI to subtract
- * @return copy subtracted
- */
- public CoordI sub(CoordI other)
- {
- return copy().sub_ip(other);
- }
-
-
- /**
- * Subtract x,y in a copy
- *
- * @param x x to subtract
- * @param y y to subtract
- * @return copy subtracted
- */
- public CoordI sub(int x, int y)
- {
- return copy().sub_ip(new CoordI(x, y));
- }
-
-
- /**
- * Subtract x,y,z in a copy
- *
- * @param x x to subtract
- * @param y y to subtract
- * @param z z to subtract
- * @return copy subtracted
- */
- public CoordI sub(int x, int y, int z)
- {
- return copy().sub_ip(new CoordI(x, y, z));
- }
-
-
- /**
- * Subtract other coordI coordinates in place
- *
- * @param move coordI to subtract
- * @return this
- */
- public CoordI sub_ip(CoordI move)
- {
- x -= move.x;
- y -= move.y;
- z -= move.z;
- return this;
- }
-
-
- /**
- * Sub coords in place
- *
- * @param x x coord
- * @param y y coord
- * @return this
- */
- public CoordI sub_ip(int x, int y)
- {
- this.x -= x;
- this.y -= y;
- return this;
- }
-
-
- /**
- * Sub coords in place
- *
- * @param x x coord
- * @param y y coord
- * @param z z coord
- * @return this
- */
- public CoordI sub_ip(int x, int y, int z)
- {
- this.x -= x;
- this.y -= y;
- this.z -= z;
- return this;
- }
-
-
- /**
- * Convert to double Coord
- *
- * @return coord with X and y from this CoordI
- */
- public Coord toCoord()
- {
- return new Coord(x, y);
- }
-
-
- @Override
- public String toString()
- {
- return "[ " + x + " ; " + y + " ; " + z + " ]";
- }
-
-}
diff --git a/src/mightypork/utils/math/coord/Rect.java b/src/mightypork/utils/math/coord/Rect.java
index 8eed249..cabfe9a 100644
--- a/src/mightypork/utils/math/coord/Rect.java
+++ b/src/mightypork/utils/math/coord/Rect.java
@@ -38,7 +38,7 @@ public class Rect {
* @param height size y
* @return the new rect
*/
- public static Rect fromSize(Coord min, int width, int height)
+ public static Rect fromSize(Coord min, double width, double height)
{
return new Rect(min, min.add(width, height));
}
@@ -47,37 +47,37 @@ public class Rect {
/**
* Rectangle from size
*
- * @param i min X
- * @param j min Y
+ * @param x min X
+ * @param y min Y
* @param size rect size
* @return the rect
*/
- public static Rect fromSize(int i, int j, CoordI size)
+ public static Rect fromSize(int x, int y, Coord size)
{
- return fromSize(i, j, size.x, size.y);
+ return fromSize(x, y, size.x, size.y);
}
/**
* Make rect from min coord and size
*
- * @param x1 min x
- * @param y1 min y
+ * @param xMin min x
+ * @param yMin min y
* @param width size x
* @param height size y
* @return the new rect
*/
- public static Rect fromSize(int x1, int y1, int width, int height)
+ public static Rect fromSize(double xMin, double yMin, double width, double height)
{
- return new Rect(x1, y1, x1 + width, y1 + height);
+ return new Rect(xMin, yMin, xMin + width, yMin + height);
}
- /** Highest coordinates xy */
- protected Coord max = new Coord();
-
/** Lowest coordinates xy */
protected Coord min = new Coord();
+ /** Highest coordinates xy */
+ protected Coord max = new Coord();
+
/**
* New Rect [0, 0, 0, 0]
@@ -127,7 +127,7 @@ public class Rect {
* @param x width
* @param y height
*/
- public Rect(int x, int y) {
+ public Rect(double x, double y) {
this(0, 0, x, y);
}
@@ -163,7 +163,7 @@ public class Rect {
*/
public Rect add(double x, double y)
{
- return add(new Vec(x, y));
+ return add(new Coord(x, y));
}
@@ -190,7 +190,7 @@ public class Rect {
*/
public Rect add_ip(double x, double y)
{
- return add_ip(new Vec(x, y));
+ return add_ip(new Coord(x, y));
}
@@ -718,7 +718,7 @@ public class Rect {
*/
public Rect sub(double x, double y)
{
- return sub(new Vec(x, y));
+ return sub(new Coord(x, y));
}
@@ -728,7 +728,7 @@ public class Rect {
* @param move offset vector
* @return offset copy
*/
- public Rect sub(Vec move)
+ public Rect sub(Coord move)
{
return copy().sub_ip(move);
}
@@ -743,7 +743,7 @@ public class Rect {
*/
public Rect sub_ip(double x, double y)
{
- return sub_ip(new Vec(x, y));
+ return sub_ip(new Coord(x, y));
}
@@ -753,7 +753,7 @@ public class Rect {
* @param move offset vector
* @return this
*/
- public Rect sub_ip(Vec move)
+ public Rect sub_ip(Coord move)
{
min.sub_ip(move);
max.sub_ip(move);
@@ -771,7 +771,7 @@ public class Rect {
/**
* @return lower x
*/
- public double x1()
+ public double xMin()
{
return min.x;
}
@@ -780,7 +780,7 @@ public class Rect {
/**
* @return upper x
*/
- public double x2()
+ public double xMax()
{
return max.x;
}
@@ -789,7 +789,7 @@ public class Rect {
/**
* @return lower y
*/
- public double y1()
+ public double yMin()
{
return min.y;
}
@@ -798,7 +798,7 @@ public class Rect {
/**
* @return upper y
*/
- public double y2()
+ public double yMax()
{
return max.y;
}
diff --git a/src/mightypork/utils/math/coord/Vec.java b/src/mightypork/utils/math/coord/Vec.java
deleted file mode 100644
index e6f8e51..0000000
--- a/src/mightypork/utils/math/coord/Vec.java
+++ /dev/null
@@ -1,326 +0,0 @@
-package mightypork.utils.math.coord;
-
-
-/**
- * Vector in 2D/3D space.
- *
- * @author MightyPork
- */
-public class Vec extends Coord {
-
- /** Vec [1;1;1] */
- @SuppressWarnings("hiding")
- public static final Vec ONE = new Vec(1, 1, 1);
- /** Zero vector */
- @SuppressWarnings("hiding")
- public static final Vec ZERO = new Vec(0, 0, 0);
-
-
- /**
- * Get cross product of two vectors
- *
- * @param a 1st vector
- * @param b 2nd vector
- * @return cross product
- */
- public static Vec cross(Vec a, Vec b)
- {
- return a.cross(b);
- }
-
-
- /**
- * Get dot product of two vectors
- *
- * @param a 1st vector
- * @param b 2nd vector
- * @return dot product
- */
- public static double dot(Vec a, Vec b)
- {
- return a.dot(b);
- }
-
-
- /**
- * Generate random coord (gaussian)
- *
- * @param max max distance from 0
- * @return new coord
- */
- public static Vec random(double max)
- {
- return new Vec(Coord.random(max));
- }
-
-
- /**
- * Generate random coord (min-max)
- *
- * @param max max distance from 0
- * @return new coord
- */
- public static Vec random(double min, double max)
- {
- return new Vec(Coord.random(min, max));
- }
-
-
- /**
- * Scale vector
- *
- * @param a vector
- * @param scale
- * @return scaled copy
- */
- public static Vec scale(Vec a, double scale)
- {
- return a.scale(scale);
- }
-
-
- /**
- * Get vector size
- *
- * @param vec vector to get size of
- * @return size in units
- */
- public static double size(Vec vec)
- {
- return vec.size();
- }
-
-
- /**
- * Create zero vector
- */
- public Vec() {
- super();
- }
-
-
- /**
- * Create vector as a copy of another
- *
- * @param copied copied vector
- */
- public Vec(Coord copied) {
- super(copied);
- }
-
-
- /**
- * Create 2D vector
- *
- * @param x x coordinate
- * @param y y coordinate
- */
- public Vec(Number x, Number y) {
- super(x, y);
- }
-
-
- /**
- * Create 3D vector
- *
- * @param x x coordinate
- * @param y y coordinate
- * @param z z coordinate
- */
- public Vec(Number x, Number y, Number z) {
- super(x, y, z);
- }
-
-
- @Override
- public Vec copy()
- {
- return new Vec(this);
- }
-
-
- /**
- * Multiply by other vector, vector multiplication
- *
- * @param vec other vector
- * @return copy multiplied
- */
- public Vec cross(Vec vec)
- {
- return copy().cross_ip(vec);
- }
-
-
- /**
- * Multiply by other vector, vector multiplication; in place
- *
- * @param vec other vector
- * @return this
- */
- public Vec cross_ip(Vec vec)
- {
- setTo(y * vec.z - z * vec.y, z * vec.x - x * vec.z, x * vec.y - y * vec.x);
- return this;
- }
-
-
- /**
- * Get dot product
- *
- * @param vec other vector
- * @return dot product
- */
- public double dot(Vec vec)
- {
- return x * vec.x + y * vec.y + z * vec.z;
- }
-
-
- // STATIC
-
- /**
- * Negate all coordinates (* -1)
- *
- * @return negated coordinate
- */
- public Vec neg()
- {
- return copy().neg_ip();
- }
-
-
- /**
- * Negate all coordinates (* -1), in place
- *
- * @return this
- */
- public Vec neg_ip()
- {
- scale_ip(-1);
- return this;
- }
-
-
- /**
- * Scale vector to given size
- *
- * @param size size we need
- * @return scaled vector
- */
- public Vec norm(double size)
- {
- return copy().norm_ip(size);
- }
-
-
- /**
- * Scale vector to given size, in place
- *
- * @param size size we need
- * @return scaled vector
- */
- public Vec norm_ip(double size)
- {
- if (size() == 0) {
- z = -1;
- }
- if (size == 0) {
- setTo(0, 0, 0);
- return this;
- }
- double k = size / size();
- scale_ip(k);
- return this;
- }
-
-
- /**
- * offset randomly
- *
- * @param max max +- offset
- * @return offset coord
- */
- @Override
- public Vec random_offset(double max)
- {
- return (Vec) super.random_offset(max);
- }
-
-
- /**
- * offset randomly
- *
- * @param min min offset
- * @param max max offset
- * @return offset coord
- */
- @Override
- public Vec random_offset(double min, double max)
- {
- return (Vec) super.random_offset(min, max);
- }
-
-
- /**
- * offset randomly in place
- *
- * @param max max +- offset
- * @return this
- */
- @Override
- public Vec random_offset_ip(double max)
- {
- return (Vec) super.random_offset_ip(max);
- }
-
-
- /**
- * offset randomly in place
- *
- * @param min min offset
- * @param max max offset
- * @return this
- */
- @Override
- public Vec random_offset_ip(double min, double max)
- {
- return (Vec) super.random_offset_ip(min, max);
- }
-
-
- /**
- * Multiply all coordinates by factor; scalar multiplication
- *
- * @param factor multiplier
- * @return copy multiplied
- */
- public Vec scale(double factor)
- {
- return copy().scale_ip(factor);
- }
-
-
- /**
- * Multiply all coordinates by factor, in place
- *
- * @param factor multiplier
- * @return this
- */
- public Vec scale_ip(double factor)
- {
- return (Vec) mul_ip(factor);
- }
-
-
- /**
- * Get vector size
- *
- * @return vector size in units
- */
- @Override
- public double size()
- {
- return Math.sqrt(x * x + y * y + z * z);
- }
-
-}
diff --git a/src/mightypork/utils/math/easing/Easing.java b/src/mightypork/utils/math/easing/Easing.java
new file mode 100644
index 0000000..cf5b51e
--- /dev/null
+++ b/src/mightypork/utils/math/easing/Easing.java
@@ -0,0 +1,322 @@
+package mightypork.utils.math.easing;
+
+
+import mightypork.utils.math.Calc;
+
+
+/**
+ * Easing function.
+ * The easing function must be time-invariant and it's domain is [0-1].
+ *
+ * @author MightyPork
+ */
+public interface Easing {
+
+ /**
+ * Get value of the easing function at given time.
+ *
+ * @param time number in range 0..1
+ * @return value at given time
+ */
+ public double get(double time);
+
+ public static final Easing NONE = new Easing() {
+
+ @Override
+ public double get(double time)
+ {
+ double t = Calc.clampd(time, 0, 1);
+
+ return (t < 0.5 ? 0 : 1);
+ }
+ };
+
+ public static final Easing LINEAR = new Easing() {
+
+ @Override
+ public double get(double time)
+ {
+ double t = Calc.clampd(time, 0, 1);
+
+ return t;
+ }
+ };
+
+ public static final Easing QUADRATIC_IN = new Easing() {
+
+ @Override
+ public double get(double time)
+ {
+ double t = Calc.clampd(time, 0, 1);
+
+ return t * t;
+ }
+ };
+
+ public static final Easing QUADRATIC_OUT = new Easing() {
+
+ @Override
+ public double get(double time)
+ {
+ double t = Calc.clampd(time, 0, 1);
+
+ return 1 - (t - 1) * (t - 1);
+ }
+ };
+
+ public static final Easing QUADRATIC = new Easing() {
+
+ @Override
+ public double get(double time)
+ {
+ double t = Calc.clampd(time, 0, 1);
+
+ if (t < 0.5) return QUADRATIC_IN.get(2 * t) * 0.5;
+ return 0.5 + QUADRATIC_OUT.get(2 * t - 1) * 0.5;
+ }
+ };
+
+ public static final Easing CUBIC_IN = new Easing() {
+
+ @Override
+ public double get(double time)
+ {
+ double t = Calc.clampd(time, 0, 1);
+
+ return t * t * t;
+ }
+ };
+ public static final Easing CUBIC_OUT = new Easing() {
+
+ @Override
+ public double get(double time)
+ {
+ double t = Calc.clampd(time, 0, 1);
+
+ return (t - 1) * (t - 1) * (t - 1) + 1;
+ }
+ };
+ public static final Easing CUBIC = new Easing() {
+
+ @Override
+ public double get(double time)
+ {
+ double d = 1;
+ double t = time;
+ double b = 0;
+ double c = (1 - 0);
+ t /= d / 2;
+ if (t < 1) return c / 2 * t * t * t + b;
+ t -= 2;
+ return c / 2 * (t * t * t + 2) + b;
+ }
+ };
+ public static final Easing QUARTIC_IN = new Easing() {
+
+ @Override
+ public double get(double time)
+ {
+ double d = 1;
+ double t = time;
+ double b = 0;
+ double c = (1 - 0);
+ t /= d;
+ return c * t * t * t * t + b;
+ }
+ };
+ public static final Easing QUARTIC_OUT = new Easing() {
+
+ @Override
+ public double get(double time)
+ {
+ double d = 1;
+ double t = time;
+ double b = 0;
+ double c = (1 - 0);
+
+ t /= d;
+ t--;
+ return -c * (t * t * t * t - 1) + b;
+ }
+ };
+ public static final Easing QUARTIC = new Easing() {
+
+ @Override
+ public double get(double time)
+ {
+ double d = 1;
+ double t = time;
+ double b = 0;
+ double c = (1 - 0);
+
+ t /= d / 2;
+ if (t < 1) return c / 2 * t * t * t * t + b;
+ t -= 2;
+ return -c / 2 * (t * t * t * t - 2) + b;
+ }
+ };
+ public static final Easing QUINTIC_IN = new Easing() {
+
+ @Override
+ public double get(double time)
+ {
+ double d = 1;
+ double t = time;
+ double b = 0;
+ double c = (1 - 0);
+ t /= d;
+ return c * t * t * t * t * t + b;
+ }
+ };
+ public static final Easing QUINTIC_OUT = new Easing() {
+
+ @Override
+ public double get(double time)
+ {
+ double d = 1;
+ double t = time;
+ double b = 0;
+ double c = (1 - 0);
+ t /= d;
+ t--;
+ return c * (t * t * t * t * t + 1) + b;
+ }
+ };
+ public static final Easing QUINTIC_IN_OUT = new Easing() {
+
+ @Override
+ public double get(double time)
+ {
+ double d = 1;
+ double t = time;
+ double b = 0;
+ double c = (1 - 0);
+ t /= d / 2;
+ if (t < 1) return c / 2 * t * t * t * t * t + b;
+ t -= 2;
+ return c / 2 * (t * t * t * t * t + 2) + b;
+ }
+ };
+ public static final Easing SINE_IN = new Easing() {
+
+ @Override
+ public double get(double time)
+ {
+ double d = 1;
+ double t = time;
+ double b = 0;
+ double c = (1 - 0);
+ return -c * Math.cos(t / d * (Math.PI / 2)) + c + b;
+ }
+ };
+ public static final Easing SINE_OUT = new Easing() {
+
+ @Override
+ public double get(double time)
+ {
+ double d = 1;
+ double t = time;
+ double b = 0;
+ double c = (1 - 0);
+
+ return c * Math.sin(t / d * (Math.PI / 2)) + b;
+ }
+ };
+ public static final Easing SINE = new Easing() {
+
+ @Override
+ public double get(double time)
+ {
+ double d = 1;
+ double t = time;
+ double b = 0;
+ double c = (1 - 0);
+ return -c / 2 * (Math.cos(Math.PI * t / d) - 1) + b;
+ }
+ };
+ public static final Easing EXPO_IN = new Easing() {
+
+ @Override
+ public double get(double time)
+ {
+ double d = 1;
+ double t = time;
+ double b = 0;
+ double c = (1 - 0);
+
+ return c * Math.pow(2, 10 * (t / d - 1)) + b;
+ }
+ };
+ public static final Easing EXPO_OUT = new Easing() {
+
+ @Override
+ public double get(double time)
+ {
+ double d = 1;
+ double t = time;
+ double b = 0;
+ double c = (1 - 0);
+
+ return c * (-Math.pow(2, -10 * t / d) + 1) + b;
+ }
+ };
+ public static final Easing EXPO = new Easing() {
+
+ @Override
+ public double get(double time)
+ {
+ double d = 1;
+ double t = time;
+ double b = 0;
+ double c = (1 - 0);
+
+ t /= d / 2;
+ if (t < 1) return c / 2 * Math.pow(2, 10 * (t - 1)) + b;
+ t--;
+ return c / 2 * (-Math.pow(2, -10 * t) + 2) + b;
+ }
+ };
+ public static final Easing CIRC_IN = new Easing() {
+
+ @Override
+ public double get(double time)
+ {
+ double d = 1;
+ double t = time;
+ double b = 0;
+ double c = (1 - 0);
+ t /= d;
+ return -c * (Math.sqrt(1 - t * t) - 1) + b;
+ }
+ };
+ public static final Easing CIRC_OUT = new Easing() {
+
+ @Override
+ public double get(double time)
+ {
+ double d = 1;
+ double t = time;
+ double b = 0;
+ double c = (1 - 0);
+
+ t--;
+ return c * Math.sqrt(1 - t * t) + b;
+ }
+ };
+ public static final Easing CIRC = new Easing() {
+
+ @Override
+ public double get(double time)
+ {
+ double d = 1;
+ double t = time;
+ double b = 0;
+ double c = (1 - 0);
+
+ t /= d / 2;
+ if (t < 1) return -c / 2 * (Math.sqrt(1 - t * t) - 1) + b;
+ t -= 2;
+ return c / 2 * (Math.sqrt(1 - t * t) + 1) + b;
+ }
+ };
+}
diff --git a/src/mightypork/utils/objects/Convertor.java b/src/mightypork/utils/objects/Convertor.java
index 3b1f8db..c8c36f5 100644
--- a/src/mightypork/utils/objects/Convertor.java
+++ b/src/mightypork/utils/objects/Convertor.java
@@ -4,7 +4,6 @@ package mightypork.utils.objects;
import mightypork.utils.logging.Log;
import mightypork.utils.math.Range;
import mightypork.utils.math.coord.Coord;
-import mightypork.utils.math.coord.CoordI;
/**
@@ -154,7 +153,6 @@ public class Convertor {
return new Coord(Double.parseDouble(parts[0].trim()), Double.parseDouble(parts[1].trim()));
}
if (o instanceof Coord) return new Coord((Coord) o);
- if (o instanceof CoordI) return ((CoordI) o).toCoord();
} catch (NumberFormatException e) {
// ignore
}
@@ -267,7 +265,7 @@ public class Convertor {
*/
public static Coord getCoord(Object o)
{
- return getCoord(o, Coord.ZERO.copy());
+ return getCoord(o, Coord.zero());
}
diff --git a/src/mightypork/utils/objects/Mutable.java b/src/mightypork/utils/objects/Mutable.java
index 0440cbe..5cc59c5 100644
--- a/src/mightypork/utils/objects/Mutable.java
+++ b/src/mightypork/utils/objects/Mutable.java
@@ -10,17 +10,11 @@ package mightypork.utils.objects;
public class Mutable {
/** The wrapped value */
- public T o = null;
+ private T o = null;
/**
- * Implicint constructor
- */
- public Mutable() {}
-
-
- /**
- * new mutable object
+ * New mutable object
*
* @param o value
*/
@@ -49,4 +43,38 @@ public class Mutable {
{
this.o = o;
}
+
+
+ @Override
+ public int hashCode()
+ {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((o == null) ? 0 : o.hashCode());
+ return result;
+ }
+
+
+ @Override
+ public boolean equals(Object obj)
+ {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (!(obj instanceof Mutable)) return false;
+
+ Mutable> other = (Mutable>) obj;
+ if (o == null) {
+ if (other.o != null) return false;
+ } else if (!o.equals(other.o)) {
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ public String toString()
+ {
+ if(o == null) return "";
+ return o.toString();
+ }
}
diff --git a/src/mightypork/utils/patterns/Destroyable.java b/src/mightypork/utils/patterns/Destroyable.java
new file mode 100644
index 0000000..17569be
--- /dev/null
+++ b/src/mightypork/utils/patterns/Destroyable.java
@@ -0,0 +1,14 @@
+package mightypork.utils.patterns;
+
+
+/**
+ * Object that can be destroyed (free resources etc)
+ *
+ * @author MightyPork
+ */
+public interface Destroyable {
+ /**
+ * Destroy this object
+ */
+ public void destroy();
+}
diff --git a/src/mightypork/utils/patterns/Initializable.java b/src/mightypork/utils/patterns/Initializable.java
new file mode 100644
index 0000000..12d50cc
--- /dev/null
+++ b/src/mightypork/utils/patterns/Initializable.java
@@ -0,0 +1,14 @@
+package mightypork.utils.patterns;
+
+
+/**
+ * Object that can be initialized
+ *
+ * @author MightyPork
+ */
+public interface Initializable {
+ /**
+ * Initialize if not initialized yet
+ */
+ public void initialize();
+}
diff --git a/src/mightypork/utils/patterns/subscription/Handleable.java b/src/mightypork/utils/patterns/subscription/Handleable.java
new file mode 100644
index 0000000..815b323
--- /dev/null
+++ b/src/mightypork/utils/patterns/subscription/Handleable.java
@@ -0,0 +1,18 @@
+package mightypork.utils.patterns.subscription;
+
+
+/**
+ * Something that can be handled by HANDLER.
+ *
+ * @author MightyPork
+ * @param handler type
+ */
+public interface Handleable {
+
+ /**
+ * Ask handler to handle this message.
+ *
+ * @param handler handler instance
+ */
+ public void handleBy(HANDLER handler);
+}
diff --git a/src/mightypork/utils/patterns/subscription/MessageBus.java b/src/mightypork/utils/patterns/subscription/MessageBus.java
new file mode 100644
index 0000000..1d6afe7
--- /dev/null
+++ b/src/mightypork/utils/patterns/subscription/MessageBus.java
@@ -0,0 +1,121 @@
+package mightypork.utils.patterns.subscription;
+
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import mightypork.utils.logging.Log;
+
+
+/**
+ * An event bus, accommodating multiple {@link MessageChannel}s.
+ *
+ * @author MightyPork
+ */
+public class MessageBus implements Subscribable {
+
+ private Set> channels = new LinkedHashSet>();
+ private Set