Removed junk & added Easing, Event system and others

v5stable
Ondřej Hruška 11 years ago
parent 6b8891666a
commit 1af5f01520
  1. 449
      src/mightypork/rogue/App.java
  2. 9
      src/mightypork/rogue/Const.java
  3. 1
      src/mightypork/rogue/CrashHandler.java
  4. 200
      src/mightypork/rogue/Screen.java
  5. 30
      src/mightypork/rogue/animations/EmptyAnimator.java
  6. 13
      src/mightypork/rogue/animations/GUIRenderer.java
  7. 200
      src/mightypork/rogue/display/DisplaySystem.java
  8. 214
      src/mightypork/rogue/display/Screen.java
  9. 168
      src/mightypork/rogue/display/ScreenSplash.java
  10. 50
      src/mightypork/rogue/display/events/ScreenChangeEvent.java
  11. 182
      src/mightypork/rogue/fonts/FontManager.java
  12. 27
      src/mightypork/rogue/fonts/LoadedFont.java
  13. 52
      src/mightypork/rogue/input/InputHandler.java
  14. 126
      src/mightypork/rogue/input/InputSystem.java
  15. 21
      src/mightypork/rogue/input/KeyBinder.java
  16. 48
      src/mightypork/rogue/input/KeyBinding.java
  17. 68
      src/mightypork/rogue/input/KeyBindingPool.java
  18. 114
      src/mightypork/rogue/input/KeyStroke.java
  19. 122
      src/mightypork/rogue/input/Keys.java
  20. 85
      src/mightypork/rogue/input/events/KeyboardEvent.java
  21. 119
      src/mightypork/rogue/input/events/MouseButtonEvent.java
  22. 54
      src/mightypork/rogue/input/events/MouseMotionEvent.java
  23. 82
      src/mightypork/rogue/screens/ScreenSplash.java
  24. 77
      src/mightypork/rogue/sounds/AudioPlayer.java
  25. 203
      src/mightypork/rogue/sounds/AudioX.java
  26. 77
      src/mightypork/rogue/sounds/BaseAudioPlayer.java
  27. 8
      src/mightypork/rogue/sounds/EffectPlayer.java
  28. 22
      src/mightypork/rogue/sounds/JointVolume.java
  29. 6
      src/mightypork/rogue/sounds/LoopPlayer.java
  30. 179
      src/mightypork/rogue/sounds/SoundManager.java
  31. 309
      src/mightypork/rogue/sounds/SoundSystem.java
  32. 61
      src/mightypork/rogue/tasks/TaskTakeScreenshot.java
  33. 91
      src/mightypork/rogue/threads/ThreadSaveScreenshot.java
  34. 32
      src/mightypork/rogue/threads/ThreadScreenshotTrigger.java
  35. 94
      src/mightypork/rogue/util/RenderUtils.java
  36. 5
      src/mightypork/rogue/util/Utils.java
  37. 2
      src/mightypork/utils/logging/Log.java
  38. 45
      src/mightypork/utils/math/Calc.java
  39. 132
      src/mightypork/utils/math/Polar.java
  40. 111
      src/mightypork/utils/math/PolarDeg.java
  41. 385
      src/mightypork/utils/math/coord/Coord.java
  42. 94
      src/mightypork/utils/math/coord/CoordAnimated.java
  43. 398
      src/mightypork/utils/math/coord/CoordI.java
  44. 46
      src/mightypork/utils/math/coord/Rect.java
  45. 326
      src/mightypork/utils/math/coord/Vec.java
  46. 322
      src/mightypork/utils/math/easing/Easing.java
  47. 4
      src/mightypork/utils/objects/Convertor.java
  48. 44
      src/mightypork/utils/objects/Mutable.java
  49. 14
      src/mightypork/utils/patterns/Destroyable.java
  50. 14
      src/mightypork/utils/patterns/Initializable.java
  51. 18
      src/mightypork/utils/patterns/subscription/Handleable.java
  52. 121
      src/mightypork/utils/patterns/subscription/MessageBus.java
  53. 156
      src/mightypork/utils/patterns/subscription/MessageChannel.java
  54. 26
      src/mightypork/utils/patterns/subscription/Subscribable.java
  55. 138
      src/mightypork/utils/time/AnimDouble.java
  56. 41
      src/mightypork/utils/time/AnimDoubleDeg.java
  57. 50
      src/mightypork/utils/time/AnimDoubleRad.java

@ -1,93 +1,57 @@
package mightypork.rogue; package mightypork.rogue;
import static org.lwjgl.opengl.GL11.*;
import java.io.File; import java.io.File;
import java.io.RandomAccessFile; import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileLock; import java.nio.channels.FileLock;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import mightypork.rogue.input.Keys; import mightypork.rogue.display.DisplaySystem;
import mightypork.rogue.screens.ScreenSplash; import mightypork.rogue.display.Screen;
import mightypork.rogue.sounds.SoundManager; import mightypork.rogue.display.ScreenSplash;
import mightypork.rogue.threads.ThreadSaveScreenshot; import mightypork.rogue.input.InputSystem;
import mightypork.rogue.threads.ThreadScreenshotTrigger; 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.Log;
import mightypork.utils.logging.LogInstance; 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.TimerDelta;
import mightypork.utils.time.TimerInterpolating; import mightypork.utils.time.TimerInterpolating;
import org.lwjgl.BufferUtils;
import org.lwjgl.LWJGLException;
import org.lwjgl.input.Keyboard; import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.lwjgl.openal.AL;
import org.lwjgl.opengl.Display; import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
public class App { public class App implements Destroyable {
/** instance */ /** instance pointer */
public static App inst; private static App inst;
/** Current delta time (secs since last render) */
public static double currentDelta = 0;
private static DisplayMode windowDisplayMode = null; private InputSystem input;
private SoundSystem sounds;
private DisplaySystem display;
private MessageBus events;
/** current screen */ /** current screen */
public static Screen screen = null; private Screen screen;
/** Flag that screenshot is scheduled to be taken next loop */ /** Flag that screenshot is scheduled to be taken next loop */
public static boolean scheduledScreenshot = false; private 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;
}
/** /**
* 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) public static void onCrash(Throwable error)
{ {
Log.e("The game has crashed."); Log.e("The game has crashed.", error);
Log.e(error);
try { inst.exit();
inst.deinit();
} catch (Throwable t) {
// ignore
}
} }
/** /**
* Quit to OS * Quit to OS<br>
* Destroy app & exit VM
*/ */
public void exit() public void exit()
{ {
deinit(); destroy();
System.exit(0); System.exit(0);
} }
@ -143,58 +102,36 @@ public class App {
* *
* @return screen * @return screen
*/ */
public Screen getScreen() public Screen getCurrentScreen()
{ {
return screen; return screen;
} }
/** public void initialize()
* Get screen size
*
* @return size
*/
public Coord getSize()
{ {
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 if (sounds != null) sounds.destroy();
LogInstance li = Log.create("runtime", Paths.LOGS, 10); if (input != null) input.destroy();
li.enable(Config.LOGGING_ENABLED); if (display != null) display.destroy();
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 private void initLock()
SoundManager.get().setListener(Const.LISTENER_POS);
SoundManager.get().setMasterVolume(1F);
// start async screenshot trigger listener
(new ThreadScreenshotTrigger()).start();
}
private void start() throws LWJGLException
{ {
if (!Config.SINGLE_INSTANCE) return;
if (!lockInstance()) { if (!lockInstance()) {
System.out.println("Working directory is locked.\nOnly one instance can run at a time."); System.out.println("Working directory is locked.\nOnly one instance can run at a time.");
@ -211,207 +148,235 @@ public class App {
exit(); exit();
return; return;
} }
}
init();
mainLoop(); private boolean lockInstance()
deinit(); {
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;
}
/**
* initialize inputs
*/
private void initBus()
{
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();
}
});
} }
private void deinit() /**
* initialize display
*/
private void initDisplay()
{ {
Display.destroy(); display = new DisplaySystem();
Mouse.destroy(); display.createMainWindow(Const.WINDOW_W, Const.WINDOW_H, true, Config.START_IN_FS, Const.TITLEBAR);
Keyboard.destroy(); display.setTargetFps(Const.FPS_RENDER);
SoundManager.get().destroy(); }
AL.destroy();
/**
* 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 */ /** timer */
private TimerDelta timerRender; private TimerDelta timerRender;
private TimerInterpolating timerGui; private TimerInterpolating timerGui;
private int timerAfterResize = 0;
private void mainLoop() private void mainLoop()
{ {
screen = new ScreenSplash(); screen = new ScreenSplash();
screen.init(); screen.setActive(true);
timerRender = new TimerDelta(); timerRender = new TimerDelta();
timerGui = new TimerInterpolating(Const.FPS_GUI_UPDATE); timerGui = new TimerInterpolating(Const.FPS_GUI_UPDATE);
while (!Display.isCloseRequested()) { while (!display.isCloseRequested()) {
glLoadIdentity(); display.beginFrame();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// gui update
timerGui.sync(); timerGui.sync();
int ticks = timerGui.getSkipped(); int ticks = timerGui.getSkipped();
if (ticks >= 1) { if (ticks >= 1) {
screen.updateGui(); input.poll();
timerGui.startNewFrame(); timerGui.startNewFrame();
} }
currentDelta = timerRender.getDelta(); double delta = timerRender.getDelta();
// RENDER sounds.update(delta);
screen.render(currentDelta);
SoundManager.get().update(currentDelta);
Display.update(); // Screen
screen.update(delta);
if (scheduledScreenshot) { if (scheduledScreenshot) {
takeScreenshot(); takeScreenshot();
scheduledScreenshot = false; scheduledScreenshot = false;
} }
if (Keys.justPressed(Keyboard.KEY_F11)) { display.endFrame();
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."); * Do take a screenshot
Keys.destroyChangeState(Keyboard.KEY_F); */
switchFullscreen(); public void takeScreenshot()
screen.onFullscreenChange(); {
} sounds.getEffect("gui.shutter").play(1);
Utils.runAsThread(new TaskTakeScreenshot());
} }
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(); // static accessors
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); * @return sound system of the running instance
} catch (LWJGLException e) { */
Log.e("Error going back from corrupted fullscreen."); public static SoundSystem soundsys()
onCrash(e); {
} return inst.sounds;
}
}
} }
// UPDATE LOOP END
/** /**
* Do take a screenshot * @return input system of the running instance
*/ */
public void takeScreenshot() public static InputSystem input()
{ {
//Effects.play("gui.screenshot"); return inst.input;
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);
(new ThreadSaveScreenshot(buffer, width, height, bpp)).start();
} }
/** /**
* Replace screen * @return display system of the running instance
*
* @param newScreen new screen
*/ */
public void replaceScreen(Screen newScreen) public static DisplaySystem disp()
{ {
screen = newScreen; return inst.display;
screen.init();
} }
/** /**
* Replace screen, don't init it * @return event bus of the running instance
*
* @param newScreen new screen
*/ */
public void replaceScreenNoInit(Screen newScreen) public static MessageBus msgbus()
{ {
screen = newScreen; return inst.events;
} }
/** /**
* Toggle FS if possible * @return screen of the running instance
*/ */
public void switchFullscreen() public static Screen screen()
{ {
try { return inst.getCurrentScreen();
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);
}
} }
public static boolean broadcast(Object message)
{
boolean was = msgbus().broadcast(message);
if (!was) Log.w("Message not accepted by any channel: " + message);
return was;
} }
} }

@ -1,9 +1,6 @@
package mightypork.rogue; package mightypork.rogue;
import mightypork.utils.math.coord.Coord;
/** /**
* Application constants * Application constants
* *
@ -18,12 +15,10 @@ public class Const {
public static final String TITLEBAR = APP_NAME + " v." + VERSION; public static final String TITLEBAR = APP_NAME + " v." + VERSION;
// AUDIO // AUDIO
public static final Coord LISTENER_POS = Coord.ZERO;
public static final int FPS_RENDER = 200; // max public static final int FPS_RENDER = 200; // max
public static final long FPS_GUI_UPDATE = 60; public static final long FPS_GUI_UPDATE = 60;
// INITIAL WINDOW SIZE // INITIAL WINDOW SIZE
public static final int WINDOW_SIZE_X = 1024; public static final int WINDOW_W = 1024;
public static final int WINDOW_SIZE_Y = 768; public static final int WINDOW_H = 768;
} }

@ -9,6 +9,7 @@ public class CrashHandler implements UncaughtExceptionHandler {
@Override @Override
public void uncaughtException(Thread t, Throwable e) public void uncaughtException(Thread t, Throwable e)
{ {
e.printStackTrace();
App.onCrash(e); App.onCrash(e);
} }

@ -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.<br>
* 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);
}

@ -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()
{}
}

@ -1,13 +0,0 @@
package mightypork.rogue.animations;
public interface GUIRenderer {
public void updateGui();
public void render(double delta);
public void onFullscreenChange();
}

@ -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);
}
}

@ -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.<br>
* 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.<br>
* 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);
}
}

@ -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);
}
}
}

@ -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<ScreenChangeEvent.Listener> {
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);
}
}

@ -112,96 +112,96 @@ public class FontManager {
OUTLINE; OUTLINE;
} }
/** // /**
* Preloaded font identifier [name, size, style] // * Preloaded font identifier [name, size, style]
* // *
* @author MightyPork // * @author MightyPork
*/ // */
public static class FontId { // public static class FontId {
//
/** font size (pt) */ // /** font size (pt) */
public float size = 24; // public float size = 24;
/** font name, registered with registerFile */ // /** font name, registered with registerFile */
public String name = ""; // public String name = "";
/** font style. The given style must be in a file. */ // /** font style. The given style must be in a file. */
public Style style; // public Style style;
//
/** Set of glyphs in this ID */ // /** Set of glyphs in this ID */
public String glyphs = ""; // public String glyphs = "";
//
/** Index for faster comparision of glyph ids. */ // /** Index for faster comparision of glyph ids. */
public int glyphset_id = 0; // public int glyphset_id = 0;
//
//
/** // /**
* Preloaded font identifier // * Preloaded font identifier
* // *
* @param name font name (registerFile) // * @param name font name (registerFile)
* @param size font size (pt) // * @param size font size (pt)
* @param style font style // * @param style font style
* @param glyphs glyphs to load // * @param glyphs glyphs to load
*/ // */
public FontId(String name, double size, Style style, String glyphs) { // public FontId(String name, double size, Style style, String glyphs) {
this.name = name; // this.name = name;
this.size = (float) size; // this.size = (float) size;
this.style = style; // this.style = style;
//
if (glyphs.equals(Glyphs.basic)) { // if (glyphs.equals(Glyphs.basic)) {
glyphset_id = 1; // glyphset_id = 1;
} else if (glyphs.equals(Glyphs.alnum)) { // } else if (glyphs.equals(Glyphs.alnum)) {
glyphset_id = 2; // glyphset_id = 2;
} else if (glyphs.equals(Glyphs.basic_text)) { // } else if (glyphs.equals(Glyphs.basic_text)) {
glyphset_id = 3; // glyphset_id = 3;
} else if (glyphs.equals(Glyphs.numbers)) { // } else if (glyphs.equals(Glyphs.numbers)) {
glyphset_id = 4; // glyphset_id = 4;
} else if (glyphs.equals(Glyphs.alpha)) { // } else if (glyphs.equals(Glyphs.alpha)) {
glyphset_id = 5; // glyphset_id = 5;
} else if (glyphs.equals(Glyphs.all)) { // } else if (glyphs.equals(Glyphs.all)) {
glyphset_id = 6; // glyphset_id = 6;
} else if (glyphs.equals(Glyphs.alnum_extra)) { // } else if (glyphs.equals(Glyphs.alnum_extra)) {
glyphset_id = 7; // glyphset_id = 7;
} else if (glyphs.equals(Glyphs.signs)) { // } else if (glyphs.equals(Glyphs.signs)) {
glyphset_id = 8; // glyphset_id = 8;
} else if (glyphs.equals(Glyphs.signs_extra)) { // } else if (glyphs.equals(Glyphs.signs_extra)) {
glyphset_id = 9; // glyphset_id = 9;
} else { // } else {
this.glyphs = glyphs; // this.glyphs = glyphs;
} // }
} // }
//
//
@Override // @Override
public boolean equals(Object obj) // public boolean equals(Object obj)
{ // {
if (obj == null) return false; // if (obj == null) return false;
if (!(obj.getClass().isAssignableFrom(getClass()))) return false; // if (!(obj.getClass().isAssignableFrom(getClass()))) return false;
if (obj instanceof FontId) { // if (obj instanceof FontId) {
if (obj == this) return true; // if (obj == this) return true;
FontId id2 = ((FontId) obj); // FontId id2 = ((FontId) obj);
boolean flag = true; // boolean flag = true;
flag &= id2.size == size; // flag &= id2.size == size;
flag &= id2.name.equals(name); // flag &= id2.name.equals(name);
flag &= id2.style == style; // flag &= id2.style == style;
flag &= ((id2.glyphset_id != -1 && id2.glyphset_id == glyphset_id) || id2.glyphs.equals(glyphs)); // flag &= ((id2.glyphset_id != -1 && id2.glyphset_id == glyphset_id) || id2.glyphs.equals(glyphs));
return flag; // return flag;
} // }
return false; // return false;
} // }
//
//
@Override // @Override
public int hashCode() // public int hashCode()
{ // {
return (new Float(size).hashCode()) ^ name.hashCode() ^ style.hashCode() ^ glyphset_id; // return (new Float(size).hashCode()) ^ name.hashCode() ^ style.hashCode() ^ glyphset_id;
} // }
//
//
@Override // @Override
public String toString() // public String toString()
{ // {
return "[" + name + ", " + size + ", " + style + (glyphset_id > 0 ? ", g=" + glyphset_id : ", g=custom") + "]"; // return "[" + name + ", " + size + ", " + style + (glyphset_id > 0 ? ", g=" + glyphset_id : ", g=custom") + "]";
} // }
} // }
/** /**
* Group of styles of one font. * 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) 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);
} }

@ -21,7 +21,6 @@ import mightypork.rogue.Config;
import mightypork.utils.logging.Log; import mightypork.utils.logging.Log;
import mightypork.utils.math.color.RGB; import mightypork.utils.math.color.RGB;
import mightypork.utils.math.coord.Coord; import mightypork.utils.math.coord.Coord;
import mightypork.utils.math.coord.CoordI;
import org.lwjgl.BufferUtils; import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11; 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) public void drawFuzzy(Coord pos, String text, int align, RGB textColor, RGB blurColor, int blurSize)
{ {
drawFuzzy(pos, text, align, textColor, blurColor, blurSize, true); 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) public void drawFuzzy(Coord pos, String text, int align, RGB textColor, RGB blurColor, int blurSize, boolean smooth)
{ {
glPushMatrix(); glPushMatrix();

@ -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();
}

@ -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));
}
}

@ -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);
}

@ -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();
}
}

@ -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<KeyBinding> bindings = new HashSet<KeyBinding>();
/**
* 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<KeyBinding> 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);
}
}
}

@ -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<Integer> keys = new LinkedHashSet<Integer>();
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<Integer> 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<Integer> 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<Integer> getKeys()
{
return keys;
}
}

@ -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];
}
}

@ -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<KeyboardEvent.Listener> {
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");
}
}

@ -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<MouseButtonEvent.Listener> {
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);
}
}

@ -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<MouseMotionEvent.Listener> {
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);
}
}

@ -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
}
}

@ -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<Float> gainMultiplier = null;
public AudioPlayer(AudioX track, double baseGain, Mutable<Float> gainMultiplier) {
this(track, 1, baseGain, gainMultiplier);
}
public AudioPlayer(AudioX track, double basePitch, double baseGain, Mutable<Float> 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();
}
}

@ -4,6 +4,7 @@ package mightypork.rogue.sounds;
import mightypork.utils.files.FileUtils; import mightypork.utils.files.FileUtils;
import mightypork.utils.logging.Log; import mightypork.utils.logging.Log;
import mightypork.utils.math.coord.Coord; import mightypork.utils.math.coord.Coord;
import mightypork.utils.patterns.Destroyable;
import org.newdawn.slick.openal.Audio; import org.newdawn.slick.openal.Audio;
import org.newdawn.slick.openal.SoundStore; import org.newdawn.slick.openal.SoundStore;
@ -14,7 +15,7 @@ import org.newdawn.slick.openal.SoundStore;
* *
* @author MightyPork * @author MightyPork
*/ */
public class AudioX implements Audio { public class AudioX implements Destroyable {
private enum PlayMode private enum PlayMode
{ {
@ -22,14 +23,26 @@ public class AudioX implements Audio {
}; };
private Audio audio = null; private Audio audio = null;
private float pauseLoopPosition = 0; private double pauseLoopPosition = 0;
private boolean looping = false; private boolean looping = false;
private boolean paused = false; private boolean paused = false;
private PlayMode mode = PlayMode.EFFECT; private PlayMode mode = PlayMode.EFFECT;
private float pitch = 1; private double lastPlayPitch = 1;
private float gain = 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() public void pauseLoop()
{ {
if (!ensureLoaded()) return; if (!load()) return;
if (isPlaying() && looping) { if (isPlaying() && looping) {
pauseLoopPosition = audio.getPosition(); pauseLoopPosition = audio.getPosition();
@ -54,16 +67,16 @@ public class AudioX implements Audio {
*/ */
public int resumeLoop() public int resumeLoop()
{ {
if (!ensureLoaded()) return -1; if (!load()) return -1;
int source = -1; int source = -1;
if (looping && paused) { if (looping && paused) {
if (mode == PlayMode.MUSIC) { if (mode == PlayMode.MUSIC) {
source = audio.playAsMusic(pitch, gain, true); source = audio.playAsMusic((float) lastPlayPitch, (float) lastPlayGain, true);
} else { } else {
source = audio.playAsSoundEffect(pitch, gain, true); source = audio.playAsSoundEffect((float) lastPlayPitch, (float) lastPlayGain, true);
} }
audio.setPosition(pauseLoopPosition); audio.setPosition((float) pauseLoopPosition);
paused = false; paused = false;
} }
return source; 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) { private boolean isLoaded()
this.audio = null; {
this.resourcePath = resourceName; 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() public boolean load()
{
load();
return audio != null;
}
public void load()
{ {
if (audio != null) return; // already loaded if (isLoaded()) return true; // already loaded
if (resourcePath == null) return; // not loaded, but can't load anyway if (loadFailed || resourcePath == null) return false; // not loaded, but can't load anyway
loadFailed = false;
try { try {
String ext = FileUtils.getExtension(resourcePath); String ext = FileUtils.getExtension(resourcePath);
@ -112,119 +118,168 @@ public class AudioX implements Audio {
} else if (ext.equalsIgnoreCase("mod")) { } else if (ext.equalsIgnoreCase("mod")) {
audio = SoundStore.get().getMOD(resourcePath); audio = SoundStore.get().getMOD(resourcePath);
} else { } else {
resourcePath = null; // don't try next time
Log.e("Invalid audio file extension: " + resourcePath); Log.e("Invalid audio file extension: " + resourcePath);
loadFailed = true; // don't try next time
} }
} catch (Exception e) { } catch (Exception e) {
Log.e("Could not load " + resourcePath, 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() public void stop()
{ {
if (!ensureLoaded()) return; if (!isLoaded()) return;
audio.stop(); audio.stop();
paused = false; paused = false;
} }
@Override
public int getBufferID()
{
if (!ensureLoaded()) return -1;
return audio.getBufferID();
}
@Override
public boolean isPlaying() public boolean isPlaying()
{ {
if (!ensureLoaded()) return false; if (!isLoaded()) return false;
return audio.isPlaying(); return audio.isPlaying();
} }
@Override
public boolean isPaused() public boolean isPaused()
{ {
if (!ensureLoaded()) return false; if (!isLoaded()) return false;
return audio.isPaused(); 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.lastPlayPitch = pitch;
this.gain = gain; this.lastPlayGain = gain;
looping = loop; looping = loop;
mode = PlayMode.EFFECT; 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 pitch pitch (1 = default)
* @param gain The gain of the play back * @param gain gain (0-1)
* @param loop True if we should loop * @param loop looping
* @param pos The position of the sound * @param pos coord
* @return The ID of the source playing the sound * @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.<br>
* 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; if (!load()) return -1;
this.gain = gain;
this.lastPlayPitch = (float) pitch;
this.lastPlayGain = (float) gain;
looping = loop; looping = loop;
mode = PlayMode.MUSIC; mode = PlayMode.MUSIC;
return audio.playAsMusic(pitch, gain, loop); return audio.playAsMusic((float) pitch, (float) gain, loop);
} }
@Override @Override
public boolean setPosition(float position) public void destroy()
{ {
return audio.setPosition(position); if (!isLoaded()) return;
audio.release();
audio = null;
} }
@Override @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 @Override
public void release() public boolean equals(Object obj)
{ {
audio.release(); if (this == obj) return true;
audio = null; 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;
} }
} }

@ -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<Double> gainMultiplier = null;
public BaseAudioPlayer(AudioX track, double baseGain, Mutable<Double> gainMultiplier) {
this(track, 1, baseGain, gainMultiplier);
}
public BaseAudioPlayer(AudioX track, double basePitch, double baseGain, Mutable<Double> 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();
}
}

@ -5,9 +5,9 @@ import mightypork.utils.math.coord.Coord;
import mightypork.utils.objects.Mutable; import mightypork.utils.objects.Mutable;
public class EffectPlayer extends AudioPlayer { public class EffectPlayer extends BaseAudioPlayer {
public EffectPlayer(AudioX track, double basePitch, double baseGain, Mutable<Float> gainMultiplier) { public EffectPlayer(AudioX track, double basePitch, double baseGain, Mutable<Double> gainMultiplier) {
super(track, (float) basePitch, (float) baseGain, gainMultiplier); super(track, (float) basePitch, (float) baseGain, gainMultiplier);
} }
@ -16,7 +16,7 @@ public class EffectPlayer extends AudioPlayer {
{ {
if (!canPlay()) return -1; 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; if (!canPlay()) return -1;
return getAudio().playAsSoundEffect(getPitch(pitch), getGain(gain), false, pos); return getAudio().playAsEffect(getPitch(pitch), getGain(gain), false, pos);
} }
} }

@ -10,18 +10,18 @@ import mightypork.utils.objects.Mutable;
* *
* @author MightyPork * @author MightyPork
*/ */
public class JointVolume extends Mutable<Float> { public class JointVolume extends Mutable<Double> {
private Mutable<Float>[] volumes; private Mutable<Double>[] volumes;
/** /**
* CReate joint volume with master gain of 1 * Create joint volume with master gain of 1
* *
* @param volumes individual volumes to join * @param volumes individual volumes to join
*/ */
public JointVolume(Mutable<Float>... volumes) { public JointVolume(Mutable<Double>... volumes) {
super(1F); super(1D);
this.volumes = volumes; this.volumes = volumes;
} }
@ -30,13 +30,13 @@ public class JointVolume extends Mutable<Float> {
* Get combined gain (multiplied) * Get combined gain (multiplied)
*/ */
@Override @Override
public Float get() public Double get()
{ {
float f = super.get(); double d = super.get();
for (Mutable<Float> v : volumes) for (Mutable<Double> v : volumes)
f *= v.get(); d *= v.get();
return Calc.clampf(f, 0, 1); return Calc.clampd(d, 0, 1);
} }
@ -44,7 +44,7 @@ public class JointVolume extends Mutable<Float> {
* Set master gain * Set master gain
*/ */
@Override @Override
public void set(Float o) public void set(Double o)
{ {
super.set(o); super.set(o);
} }

@ -9,7 +9,7 @@ import mightypork.utils.time.Updateable;
import org.lwjgl.openal.AL10; 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; private int sourceID = -1;
@ -28,7 +28,7 @@ public class LoopPlayer extends AudioPlayer implements Updateable, Pauseable {
private double outTime = 1; private double outTime = 1;
public LoopPlayer(AudioX track, double pitch, double baseGain, Mutable<Float> gainMultiplier) { public LoopPlayer(AudioX track, double pitch, double baseGain, Mutable<Double> gainMultiplier) {
super(track, (float) pitch, (float) baseGain, gainMultiplier); super(track, (float) pitch, (float) baseGain, gainMultiplier);
paused = true; paused = true;
@ -45,7 +45,7 @@ public class LoopPlayer extends AudioPlayer implements Updateable, Pauseable {
private void initLoop() private void initLoop()
{ {
if (!canPlay() && sourceID == -1) { if (!canPlay() && sourceID == -1) {
sourceID = getAudio().playAsSoundEffect(getPitch(1), getGain(1), true); sourceID = getAudio().playAsEffect(getPitch(1), getGain(1), true);
getAudio().pauseLoop(); getAudio().pauseLoop();
} }
} }

@ -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<Float> masterVolume = new Mutable<Float>(1F);
@SuppressWarnings("unchecked")
public Mutable<Float> effectsVolume = new JointVolume(masterVolume);
@SuppressWarnings("unchecked")
public Mutable<Float> loopsVolume = new JointVolume(masterVolume);
public Coord listener = new Coord(Coord.ZERO);
private Map<String, EffectPlayer> effects = new HashMap<String, EffectPlayer>();
private Map<String, LoopPlayer> loops = new HashMap<String, LoopPlayer>();
/**
* 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();
}
}

@ -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<Double> masterVolume = new Mutable<Double>(1D);
public Mutable<Double> effectsVolume = new JointVolume(masterVolume);
public Mutable<Double> loopsVolume = new JointVolume(masterVolume);
private Map<String, EffectPlayer> effects = new HashMap<String, EffectPlayer>();
private Map<String, LoopPlayer> loops = new HashMap<String, LoopPlayer>();
private Set<AudioX> resources = new HashSet<AudioX>();
/**
* 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();
}
}

@ -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());
}
}

@ -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);
}
}
}

@ -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();
}
}
}
}

@ -4,6 +4,7 @@ package mightypork.rogue.util;
import static org.lwjgl.opengl.GL11.*; import static org.lwjgl.opengl.GL11.*;
import mightypork.rogue.textures.TextureManager; import mightypork.rogue.textures.TextureManager;
import mightypork.rogue.textures.TxQuad; import mightypork.rogue.textures.TxQuad;
import mightypork.utils.math.Calc.Deg;
import mightypork.utils.math.color.HSV; import mightypork.utils.math.color.HSV;
import mightypork.utils.math.color.RGB; import mightypork.utils.math.color.RGB;
import mightypork.utils.math.coord.Coord; import mightypork.utils.math.coord.Coord;
@ -19,6 +20,10 @@ import org.newdawn.slick.opengl.Texture;
*/ */
public class RenderUtils { 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 * Render quad 2D
* *
@ -471,15 +476,15 @@ public class RenderUtils {
*/ */
public static void quadTexturedAbs(Rect quad, Rect textureCoords) public static void quadTexturedAbs(Rect quad, Rect textureCoords)
{ {
double left = quad.x1(); double left = quad.xMin();
double bottom = quad.y2(); double bottom = quad.yMax();
double right = quad.x2(); double right = quad.xMax();
double top = quad.y1(); double top = quad.yMin();
double tleft = textureCoords.x1(); double tleft = textureCoords.xMin();
double tbottom = textureCoords.y1(); double tbottom = textureCoords.yMin();
double tright = textureCoords.x2(); double tright = textureCoords.xMax();
double ttop = textureCoords.y2(); double ttop = textureCoords.yMax();
glBegin(GL_QUADS); glBegin(GL_QUADS);
glTexCoord2d(tleft, ttop); glTexCoord2d(tleft, ttop);
@ -584,18 +589,18 @@ public class RenderUtils {
int yOffset = yOffsetTimes * frameHeight; int yOffset = yOffsetTimes * frameHeight;
double x1 = quadRect.x1(); double x1 = quadRect.xMin();
double y1 = quadRect.y1(); double y1 = quadRect.yMin();
double x2 = quadRect.x2(); double x2 = quadRect.xMax();
double y2 = quadRect.y2(); double y2 = quadRect.yMax();
double w = x2 - x1; double w = x2 - x1;
double h = y2 - y1; double h = y2 - y1;
double tx1 = textureRect.x1(); double tx1 = textureRect.xMin();
double ty1 = textureRect.y1(); double ty1 = textureRect.yMin();
double tx2 = textureRect.x2(); double tx2 = textureRect.xMax();
double ty2 = textureRect.y2(); double ty2 = textureRect.yMax();
double halfY = h / 2D; double halfY = h / 2D;
double halfX = w / 2D; double halfX = w / 2D;
@ -713,15 +718,15 @@ public class RenderUtils {
{ {
Texture tx = texture; Texture tx = texture;
double x1 = quadRect.x1(); double x1 = quadRect.xMin();
double y1 = quadRect.y1(); double y1 = quadRect.yMin();
double x2 = quadRect.x2(); double x2 = quadRect.xMax();
double y2 = quadRect.y2(); double y2 = quadRect.yMax();
double tx1 = textureRect.x1(); double tx1 = textureRect.xMin();
double ty1 = textureRect.y1(); double ty1 = textureRect.yMin();
double tx2 = textureRect.x2(); double tx2 = textureRect.xMax();
double ty2 = textureRect.y2(); double ty2 = textureRect.yMax();
// lt, mi, rt // lt, mi, rt
@ -802,15 +807,15 @@ public class RenderUtils {
{ {
Texture tx = texture; Texture tx = texture;
double x1 = quadRect.x1(); double x1 = quadRect.xMin();
double y1 = quadRect.y1(); double y1 = quadRect.yMin();
double x2 = quadRect.x2(); double x2 = quadRect.xMax();
double y2 = quadRect.y2(); double y2 = quadRect.yMax();
double tx1 = textureRect.x1(); double tx1 = textureRect.xMin();
double ty1 = textureRect.y1(); double ty1 = textureRect.yMin();
double tx2 = textureRect.x2(); double tx2 = textureRect.xMax();
double ty2 = textureRect.y2(); double ty2 = textureRect.yMax();
// tp // tp
// mi // mi
@ -916,4 +921,29 @@ public class RenderUtils {
{ {
glTranslated(coord.x, coord.y, coord.z); 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);
}
} }

@ -7,4 +7,9 @@ package mightypork.rogue.util;
* @author MightyPork * @author MightyPork
*/ */
public class Utils { public class Utils {
public static Thread runAsThread(Runnable r) {
Thread t = new Thread(r);
t.start();
return t;
}
} }

@ -8,7 +8,7 @@ import java.util.HashMap;
public class Log { public class Log {
/** enable static logging */ /** enable static logging */
private static boolean esl; private static boolean esl = true;
/** /**

@ -7,7 +7,7 @@ import java.util.List;
import java.util.Random; import java.util.Random;
import mightypork.utils.math.coord.Coord; import mightypork.utils.math.coord.Coord;
import mightypork.utils.math.coord.Vec; import mightypork.utils.math.easing.Easing;
import org.lwjgl.BufferUtils; import org.lwjgl.BufferUtils;
@ -31,7 +31,7 @@ public class Calc {
* @param point point coordinate * @param point point coordinate
* @return distance * @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] // line point L[lx,ly]
double lx = linePoint.x; double lx = linePoint.x;
@ -59,9 +59,9 @@ public class Calc {
* @param point point coordinate * @param point point coordinate
* @return distance * @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) * Get number from A to B at delta time (tween A to B)
* *
* @param last last number * @param from last number
* @param now new number * @param to new number
* @param dtime delta time * @param time time 0..1
* @param easing easing function
* @return current number to render * @return current number to render
*/ */
public static double interpolate(double last, double now, double dtime) public static double interpolate(double from, double to, double time, Easing easing)
{ {
return last + (now - last) * dtime; return from + (to - from) * easing.get(time);
} }
/** /**
* Get angle [degrees] from A to B at delta time (tween A to B) * Get angle [degrees] from A to B at delta time (tween A to B)
* *
* @param last last angle * @param from last angle
* @param now new angle * @param to new angle
* @param delta delta time * @param time time 0..1
* @param easing easing function
* @return current angle to render * @return current angle to render
*/ */
public static double interpolateDeg(double last, double now, double delta) public static double interpolateDeg(double from, double to, double time, Easing easing)
{ {
return Deg.norm(last + Deg.delta(now, last) * delta); 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 interpolateRad(double from, double to, double time, Easing easing)
{
return Rad.norm(from - Rad.delta(to, from) * easing.get(time));
} }

@ -1,6 +1,8 @@
package mightypork.utils.math; package mightypork.utils.math;
import mightypork.utils.math.Calc.Deg;
import mightypork.utils.math.Calc.Rad;
import mightypork.utils.math.coord.Coord; import mightypork.utils.math.coord.Coord;
@ -12,18 +14,87 @@ import mightypork.utils.math.coord.Coord;
public class Polar { public class Polar {
/** angle in radians */ /** angle in radians */
public double angle = 0; private double angle = 0;
/** distance in units */ /** 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 * @param distance distance from origin
*/ */
public Polar(double angle, double distance) { 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.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 * Get coord from polar
* *
@ -72,36 +130,40 @@ public class Polar {
*/ */
public Coord toCoord() 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));
} }
/** @Override
* Get X,0,Y coord from polar public String toString()
*
* @return coord
*/
public Coord toCoordXZ()
{ {
return new Coord(distance * Math.cos(angle), 0, distance * Math.sin(angle)); return "Polar(" + angle + "rad, " + radius + ")";
} }
@Override @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;
} }
/** @Override
* Build polar from X,Z instead of X,Y public boolean equals(Object obj)
*
* @param coord cpprd with X,Z
* @return polar
*/
public static Polar fromCoordXZ(Coord coord)
{ {
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;
} }
} }

@ -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);
}
}

@ -4,7 +4,6 @@ package mightypork.utils.math.coord;
import java.util.Random; import java.util.Random;
import mightypork.utils.math.Calc; import mightypork.utils.math.Calc;
import mightypork.utils.time.Updateable;
/** /**
@ -12,13 +11,7 @@ import mightypork.utils.time.Updateable;
* *
* @author MightyPork * @author MightyPork
*/ */
public class Coord implements Updateable { public class Coord {
/** 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);
/** RNG */ /** RNG */
protected static Random rand = new Random(); protected static Random rand = new Random();
@ -63,14 +56,6 @@ public class Coord implements Updateable {
(rand.nextBoolean() ? -1 : 1) * (min + rand.nextDouble() * (max - min))); (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 */ /** X coordinate */
public double x = 0; public double x = 0;
@ -130,7 +115,7 @@ public class Coord implements Updateable {
*/ */
public Coord add(Coord vec) 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) 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) 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 * @return copy of this vector
*/ */
public Coord copy() public Coord getCopy()
{ {
return new Coord(x, y, z); return new Coord(x, y, z);
} }
@ -252,7 +222,7 @@ public class Coord implements Updateable {
*/ */
public Coord div(double d) 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 * Check if this rectangle in inside a rectangular zone
* *
@ -347,7 +275,7 @@ public class Coord implements Updateable {
*/ */
public Coord midTo(Coord other) 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) 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) 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) public Coord random_offset(double max)
{ {
Coord r = random(1); Coord v = random(1).norm_ip(0.00001 + rand.nextDouble() * max);
Vec v = new Vec(r);
v.norm_ip(0.00001 + rand.nextDouble() * max); return getCopy().add_ip(v);
return copy().add_ip(v);
} }
@ -433,7 +360,7 @@ public class Coord implements Updateable {
*/ */
public Coord random_offset(double min, double max) 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 * Get a copy with rounded coords
* *
@ -481,7 +397,7 @@ public class Coord implements Updateable {
*/ */
public Coord round() 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) 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) 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) 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 * Get a copy subtracted by vector
* *
@ -663,7 +568,7 @@ public class Coord implements Updateable {
*/ */
public Coord sub(Coord vec) 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) 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) 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 * Create vector from this point to other point
* *
* @param point second point * @param point second point
* @return vector * @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); 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);
}
} }

@ -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));
}
}

@ -1,398 +0,0 @@
package mightypork.utils.math.coord;
/**
* Simple integer coordinate class<br>
* 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 + " ]";
}
}

@ -38,7 +38,7 @@ public class Rect {
* @param height size y * @param height size y
* @return the new rect * @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)); return new Rect(min, min.add(width, height));
} }
@ -47,37 +47,37 @@ public class Rect {
/** /**
* Rectangle from size * Rectangle from size
* *
* @param i min X * @param x min X
* @param j min Y * @param y min Y
* @param size rect size * @param size rect size
* @return the rect * @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 * Make rect from min coord and size
* *
* @param x1 min x * @param xMin min x
* @param y1 min y * @param yMin min y
* @param width size x * @param width size x
* @param height size y * @param height size y
* @return the new rect * @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 */ /** Lowest coordinates xy */
protected Coord min = new Coord(); protected Coord min = new Coord();
/** Highest coordinates xy */
protected Coord max = new Coord();
/** /**
* New Rect [0, 0, 0, 0] * New Rect [0, 0, 0, 0]
@ -127,7 +127,7 @@ public class Rect {
* @param x width * @param x width
* @param y height * @param y height
*/ */
public Rect(int x, int y) { public Rect(double x, double y) {
this(0, 0, x, y); this(0, 0, x, y);
} }
@ -163,7 +163,7 @@ public class Rect {
*/ */
public Rect add(double x, double y) 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) 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) 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 * @param move offset vector
* @return offset copy * @return offset copy
*/ */
public Rect sub(Vec move) public Rect sub(Coord move)
{ {
return copy().sub_ip(move); return copy().sub_ip(move);
} }
@ -743,7 +743,7 @@ public class Rect {
*/ */
public Rect sub_ip(double x, double y) 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 * @param move offset vector
* @return this * @return this
*/ */
public Rect sub_ip(Vec move) public Rect sub_ip(Coord move)
{ {
min.sub_ip(move); min.sub_ip(move);
max.sub_ip(move); max.sub_ip(move);
@ -771,7 +771,7 @@ public class Rect {
/** /**
* @return lower x * @return lower x
*/ */
public double x1() public double xMin()
{ {
return min.x; return min.x;
} }
@ -780,7 +780,7 @@ public class Rect {
/** /**
* @return upper x * @return upper x
*/ */
public double x2() public double xMax()
{ {
return max.x; return max.x;
} }
@ -789,7 +789,7 @@ public class Rect {
/** /**
* @return lower y * @return lower y
*/ */
public double y1() public double yMin()
{ {
return min.y; return min.y;
} }
@ -798,7 +798,7 @@ public class Rect {
/** /**
* @return upper y * @return upper y
*/ */
public double y2() public double yMax()
{ {
return max.y; return max.y;
} }

@ -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);
}
}

@ -0,0 +1,322 @@
package mightypork.utils.math.easing;
import mightypork.utils.math.Calc;
/**
* Easing function.<br>
* 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;
}
};
}

@ -4,7 +4,6 @@ package mightypork.utils.objects;
import mightypork.utils.logging.Log; import mightypork.utils.logging.Log;
import mightypork.utils.math.Range; import mightypork.utils.math.Range;
import mightypork.utils.math.coord.Coord; 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())); 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 Coord) return new Coord((Coord) o);
if (o instanceof CoordI) return ((CoordI) o).toCoord();
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
// ignore // ignore
} }
@ -267,7 +265,7 @@ public class Convertor {
*/ */
public static Coord getCoord(Object o) public static Coord getCoord(Object o)
{ {
return getCoord(o, Coord.ZERO.copy()); return getCoord(o, Coord.zero());
} }

@ -10,17 +10,11 @@ package mightypork.utils.objects;
public class Mutable<T> { public class Mutable<T> {
/** The wrapped value */ /** The wrapped value */
public T o = null; private T o = null;
/** /**
* Implicint constructor * New mutable object
*/
public Mutable() {}
/**
* new mutable object
* *
* @param o value * @param o value
*/ */
@ -49,4 +43,38 @@ public class Mutable<T> {
{ {
this.o = o; 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 "<null>";
return o.toString();
}
} }

@ -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();
}

@ -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();
}

@ -0,0 +1,18 @@
package mightypork.utils.patterns.subscription;
/**
* Something that can be handled by HANDLER.
*
* @author MightyPork
* @param <HANDLER> handler type
*/
public interface Handleable<HANDLER> {
/**
* Ask handler to handle this message.
*
* @param handler handler instance
*/
public void handleBy(HANDLER handler);
}

@ -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<MessageChannel<?, ?>> channels = new LinkedHashSet<MessageChannel<?, ?>>();
private Set<Object> clients = new LinkedHashSet<Object>();
/**
* Add a {@link MessageChannel} to this bus.<br>
* If a channel of matching types is already added, it is returned instead.
*
* @param channel channel to be added
* @return the channel that's now in the bus
*/
public MessageChannel<?, ?> addChannel(MessageChannel<?, ?> channel)
{
// if the channel already exists, return this instance instead.
for (MessageChannel<?, ?> ch : channels) {
if (ch.equals(channel)) {
Log.w("Channel of type "+channel+" already registered.");
return ch;
}
}
channels.add(channel);
for (Object c : clients) {
channel.addSubscriber(c);
}
return channel;
}
/**
* Remove a {@link MessageChannel} from this bus
*
* @param channel true if channel was removed
*/
public void removeChannel(MessageChannel<?, ?> channel)
{
channels.remove(channel);
for (Object c : clients) {
channel.removeSubscriber(c);
}
}
/**
* Broadcast a message
*
* @param message message
* @return true if message was accepted by at least one channel
*/
public boolean broadcast(Object message)
{
boolean sent = false;
for (MessageChannel<?, ?> b : channels) {
sent |= b.broadcast(message);
}
return sent;
}
/**
* Subscribe a client to the bus. The client will be connected to all
* current and future channels, until removed from the bus.
*
* @return <code>true</code>
*/
@Override
public boolean addSubscriber(Object client)
{
for (MessageChannel<?, ?> b : channels) {
b.addSubscriber(client);
}
clients.add(client);
return true;
}
@Override
public void removeSubscriber(Object client)
{
for (MessageChannel<?, ?> b : channels) {
b.removeSubscriber(client);
}
clients.remove(client);
}
/**
* Add a channel for given message and client type.
*
* @param messageClass message type
* @param clientClass client type
* @return the created channel instance
*/
public <F_MESSAGE extends Handleable<F_CLIENT>, F_CLIENT> MessageChannel<?, ?> registerMessageType(Class<F_MESSAGE> messageClass, Class<F_CLIENT> clientClass)
{
MessageChannel<F_MESSAGE, F_CLIENT> bc = new MessageChannel<F_MESSAGE, F_CLIENT>(messageClass, clientClass);
return addChannel(bc);
}
}

@ -0,0 +1,156 @@
package mightypork.utils.patterns.subscription;
import java.util.HashSet;
import java.util.Set;
/**
* Message subsystem (broadcaster with clients) to which clients can subscribe.<br>
* If more than one type of message is needed, {@link MessageBus} is a better
* choice.
*
* @author MightyPork
* @param <MESSAGE> message type
* @param <CLIENT> client (subscriber) type
*/
public final class MessageChannel<MESSAGE extends Handleable<CLIENT>, CLIENT> implements Subscribable {
private Set<CLIENT> clients = new HashSet<CLIENT>();
private Class<CLIENT> clientClass;
private Class<MESSAGE> messageClass;
public MessageChannel(Class<MESSAGE> messageClass, Class<CLIENT> clientClass) {
if(messageClass == null || clientClass == null) throw new IllegalArgumentException("Null Message or Client class.");
this.clientClass = clientClass;
this.messageClass = messageClass;
}
@Override
public boolean addSubscriber(Object client)
{
if (!canSubscribe(client)) return false;
clients.add(clientClass.cast(client));
return true;
}
@Override
public void removeSubscriber(Object client)
{
clients.remove(client);
}
/**
* Try to broadcast a message.<br>
* If message is of wrong type, <code>false</code> is returned.
*
* @param message a message to send
* @return true if message was sent
*/
public boolean broadcast(Object message)
{
if (!canBroadcast(message)) return false;
MESSAGE evt = messageClass.cast(message);
for (CLIENT client : clients) {
sendTo(client, evt);
}
return true;
}
/**
* Send a message to a client
*
* @param client target client
* @param message message to send
*/
private void sendTo(CLIENT client, MESSAGE message)
{
((Handleable<CLIENT>) message).handleBy(client);
}
/**
* Check if the given message can be broadcasted by this
* {@link MessageChannel}
*
* @param maybeMessage event object
* @return can be broadcasted
*/
private boolean canBroadcast(Object maybeMessage)
{
return messageClass.isInstance(maybeMessage);
}
/**
* Check if a client can subscribe to this {@link MessageChannel}
*
* @param maybeClient client asking for subscription
* @return can subscribe
*/
public boolean canSubscribe(Object maybeClient)
{
return clientClass.isInstance(maybeClient);
}
@Override
public int hashCode()
{
final int prime = 31;
int result = 1;
result = prime * result + clientClass.getName().hashCode();
result = prime * result + messageClass.getName().hashCode();
return result;
}
@Override
public boolean equals(Object obj)
{
if (this == obj) return true;
if (obj == null) return false;
if (!(obj instanceof MessageChannel)) return false;
MessageChannel<?, ?> other = (MessageChannel<?, ?>) obj;
if (!clientClass.getName().equals(other.clientClass.getName())) return false;
if (!messageClass.getName().equals(other.messageClass.getName())) return false;
return true;
}
@Override
public String toString()
{
return "CHANNEL( "+messageClass.getSimpleName()+" -> "+clientClass.getSimpleName()+" )";
}
/**
* Create an instance for given types
*
* @param messageClass event class
* @param clientClass client class
* @return the broadcaster
*/
public static <F_MESSAGE extends Handleable<F_CLIENT>, F_CLIENT> MessageChannel<F_MESSAGE, F_CLIENT> create(Class<F_MESSAGE> messageClass, Class<F_CLIENT> clientClass)
{
return new MessageChannel<F_MESSAGE, F_CLIENT>(messageClass, clientClass);
}
}

@ -0,0 +1,26 @@
package mightypork.utils.patterns.subscription;
/**
* Subscribable object
*
* @author MightyPork
*/
public interface Subscribable {
/**
* Subscribe a client to messages from this object
*
* @param client a subscribing client
* @return true if client is now subscribed
*/
public boolean addSubscriber(Object client);
/**
* Unsubscribe a client from from this object
*
* @param client a clientto unsubscribe
*/
public void removeSubscriber(Object client);
}

@ -2,6 +2,7 @@ package mightypork.utils.time;
import mightypork.utils.math.Calc; import mightypork.utils.math.Calc;
import mightypork.utils.math.easing.Easing;
/** /**
@ -12,10 +13,10 @@ import mightypork.utils.math.Calc;
public class AnimDouble implements Updateable, Pauseable { public class AnimDouble implements Updateable, Pauseable {
/** target double */ /** target double */
protected double endValue = 0; protected double to = 0;
/** last tick double */ /** last tick double */
protected double startValue = 0; protected double from = 0;
/** how long the transition should last */ /** how long the transition should last */
protected double duration = 0; protected double duration = 0;
@ -26,51 +27,91 @@ public class AnimDouble implements Updateable, Pauseable {
/** True if this animator is paused */ /** True if this animator is paused */
protected boolean paused = false; protected boolean paused = false;
/** Easing fn */
protected Easing easing = Easing.LINEAR;
/** /**
* @param value value * Create linear animator
*
* @param value initial value
*/ */
public AnimDouble(double value) { public AnimDouble(double value) {
setTo(value); setTo(value);
} }
/**
* Create animator with easing
*
* @param value initial value
* @param easing easing function
*/
public AnimDouble(double value, Easing easing) {
setTo(value);
setEasing(easing);
}
/**
* Create as copy of another
*
* @param other other animator
*/
public AnimDouble(AnimDouble other) { public AnimDouble(AnimDouble other) {
setTo(other); setTo(other);
} }
/**
* @return easing function
*/
public Easing getEasing()
{
return easing;
}
/**
* @param easing easing function
*/
public void setEasing(Easing easing)
{
this.easing = easing;
}
/** /**
* Get start value * Get start value
* *
* @return number * @return number
*/ */
public double getStartValue() public double getFrom()
{ {
return startValue; return from;
} }
/** /**
* Get value at delta time * Get end value
* *
* @return the value * @return number
*/ */
public double getCurrentValue() public double getTo()
{ {
if (duration == 0) return endValue; return to;
return Calc.interpolate(startValue, endValue, elapsedTime / duration);
} }
/** /**
* Get end value * Get value at delta time
* *
* @return number * @return the value
*/ */
public double getEndValue() public double getCurrentValue()
{ {
return endValue; if (duration == 0) return to;
return Calc.interpolate(from, to, (elapsedTime / duration), easing);
} }
@ -89,11 +130,13 @@ public class AnimDouble implements Updateable, Pauseable {
@Override @Override
public void update(double delta) public void update(double delta)
{ {
if (paused) return;
elapsedTime = Calc.clampd(elapsedTime + delta, 0, duration); elapsedTime = Calc.clampd(elapsedTime + delta, 0, duration);
if (isFinished()) { if (isFinished()) {
duration = 0; duration = 0;
elapsedTime = 0; elapsedTime = 0;
startValue = endValue; from = to;
} }
} }
@ -116,7 +159,7 @@ public class AnimDouble implements Updateable, Pauseable {
*/ */
public void setTo(double value) public void setTo(double value)
{ {
startValue = endValue = value; from = to = value;
elapsedTime = 0; elapsedTime = 0;
duration = 0; duration = 0;
} }
@ -129,52 +172,61 @@ public class AnimDouble implements Updateable, Pauseable {
*/ */
public void setTo(AnimDouble other) public void setTo(AnimDouble other)
{ {
this.startValue = other.startValue; this.from = other.from;
this.endValue = other.endValue; this.to = other.to;
this.duration = other.duration; this.duration = other.duration;
this.elapsedTime = other.elapsedTime; this.elapsedTime = other.elapsedTime;
this.paused = other.paused; this.paused = other.paused;
this.easing = other.easing;
} }
/** /**
* Animate between two states, discard current state * Animate between two states, start from current value (if it's in between)
* *
* @param from initial state * @param from start value
* @param to target state * @param to target state
* @param time animation time (secs) * @param time animation time (secs)
*/ */
public void animate(double from, double to, double time) public void animate(double from, double to, double time)
{ {
startValue = from; double current = getCurrentValue();
endValue = to;
duration = time; this.from = from;
elapsedTime = 0; this.to = to;
this.duration = time * (1 - getProgressFromValue(current));
this.elapsedTime = 0;
}
protected double getProgressFromValue(double value)
{
double p = 0;
if (value >= from && value <= to) { // up
p = ((value - from) / (to - from));
} else if (value >= to && value <= from) { // down
p = ((from - value) / (from - to));
}
return p;
} }
/** /**
* Animate between two states, start from current value (if it's in between) * Animate to a value from curretn value
* *
* @param from start value
* @param to target state * @param to target state
* @param time animation time (secs) * @param time animation time (secs)
*/ */
public void fadeTo(double from, double to, double time) public void fadeTo(double to, double time)
{ {
double current = getCurrentValue(); double current = getCurrentValue();
startValue = from; this.from = current;
endValue = to; this.to = to;
duration = time; this.duration = time;
elapsedTime = 0; this.elapsedTime = 0;
// if in between, pick up from where it is
if (current >= from && current <= to) { // up
elapsedTime = ((current - from) / (to - from)) * time;
} else if (current >= to && current <= from) { // down
elapsedTime = ((from - current) / (from - to)) * time;
}
} }
@ -185,7 +237,7 @@ public class AnimDouble implements Updateable, Pauseable {
*/ */
public void fadeIn(double time) public void fadeIn(double time)
{ {
fadeTo(0, 1, time); animate(0, 1, time);
} }
@ -196,7 +248,7 @@ public class AnimDouble implements Updateable, Pauseable {
*/ */
public void fadeOut(double time) public void fadeOut(double time)
{ {
fadeTo(1, 0, time); animate(1, 0, time);
} }
@ -214,7 +266,7 @@ public class AnimDouble implements Updateable, Pauseable {
@Override @Override
public String toString() public String toString()
{ {
return "Animation(" + startValue + " -> " + endValue + ", t=" + duration + "s, elapsed=" + elapsedTime + "s)"; return "Animation(" + from + " -> " + to + ", t=" + duration + "s, elapsed=" + elapsedTime + "s)";
} }
@ -223,7 +275,7 @@ public class AnimDouble implements Updateable, Pauseable {
*/ */
public void clear() public void clear()
{ {
startValue = endValue = 0; from = to = 0;
elapsedTime = 0; elapsedTime = 0;
duration = 0; duration = 0;
paused = false; paused = false;
@ -235,7 +287,7 @@ public class AnimDouble implements Updateable, Pauseable {
*/ */
public void stop() public void stop()
{ {
startValue = endValue = getCurrentValue(); from = to = getCurrentValue();
elapsedTime = 0; elapsedTime = 0;
duration = 0; duration = 0;
} }

@ -2,40 +2,49 @@ package mightypork.utils.time;
import mightypork.utils.math.Calc; import mightypork.utils.math.Calc;
import mightypork.utils.math.Calc.Deg;
import mightypork.utils.math.easing.Easing;
/** /**
* Double which supports delta timing * Degree animator
* *
* @author MightyPork * @author MightyPork
*/ */
public class AnimDoubleDeg extends AnimDouble { public class AnimDoubleDeg extends AnimDouble {
/** public AnimDoubleDeg(AnimDouble other) {
* new AnimDoubleDeg super(other);
* }
* @param d value
*/
public AnimDoubleDeg(double d) { public AnimDoubleDeg(double value) {
super(d); super(value);
}
public AnimDoubleDeg(double value, Easing easing) {
super(value, easing);
} }
/**
* Get value at delta time
*
* @return the value
*/
@Override @Override
public double getCurrentValue() public double getCurrentValue()
{ {
return Calc.interpolateDeg(startValue, endValue, elapsedTime / duration); if (duration == 0) return Deg.norm(to);
return Calc.interpolateDeg(from, to, (elapsedTime / duration), easing);
} }
@Override @Override
public void fadeTo(double from, double to, double time) protected double getProgressFromValue(double value)
{ {
throw new UnsupportedOperationException("Cannot fadeTo in AnimDoubleDeg. Use animate() instead."); double whole = Deg.diff(from, to);
if (Deg.diff(value, from) < whole && Deg.diff(value, to) < whole) {
double partial = Deg.diff(from, value);
return partial / whole;
}
return 0;
} }
} }

@ -0,0 +1,50 @@
package mightypork.utils.time;
import mightypork.utils.math.Calc;
import mightypork.utils.math.Calc.Rad;
import mightypork.utils.math.easing.Easing;
/**
* Radians animator
*
* @author MightyPork
*/
public class AnimDoubleRad extends AnimDouble {
public AnimDoubleRad(AnimDouble other) {
super(other);
}
public AnimDoubleRad(double value) {
super(value);
}
public AnimDoubleRad(double value, Easing easing) {
super(value, easing);
}
@Override
public double getCurrentValue()
{
if (duration == 0) return Rad.norm(to);
return Calc.interpolateRad(from, to, (elapsedTime / duration), easing);
}
@Override
protected double getProgressFromValue(double value)
{
double whole = Rad.diff(from, to);
if (Rad.diff(value, from) < whole && Rad.diff(value, to) < whole) {
double partial = Rad.diff(from, value);
return partial / whole;
}
return 0;
}
}
Loading…
Cancel
Save