Removed junk & added Easing, Event system and others
This commit is contained in:
+212
-247
@@ -1,93 +1,57 @@
|
||||
package mightypork.rogue;
|
||||
|
||||
|
||||
import static org.lwjgl.opengl.GL11.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.FileLock;
|
||||
|
||||
import javax.swing.JOptionPane;
|
||||
|
||||
import mightypork.rogue.input.Keys;
|
||||
import mightypork.rogue.screens.ScreenSplash;
|
||||
import mightypork.rogue.sounds.SoundManager;
|
||||
import mightypork.rogue.threads.ThreadSaveScreenshot;
|
||||
import mightypork.rogue.threads.ThreadScreenshotTrigger;
|
||||
import mightypork.rogue.display.DisplaySystem;
|
||||
import mightypork.rogue.display.Screen;
|
||||
import mightypork.rogue.display.ScreenSplash;
|
||||
import mightypork.rogue.input.InputSystem;
|
||||
import mightypork.rogue.input.KeyStroke;
|
||||
import mightypork.rogue.input.events.MouseMotionEvent;
|
||||
import mightypork.rogue.sounds.SoundSystem;
|
||||
import mightypork.rogue.tasks.TaskTakeScreenshot;
|
||||
import mightypork.rogue.util.Utils;
|
||||
import mightypork.utils.logging.Log;
|
||||
import mightypork.utils.logging.LogInstance;
|
||||
import mightypork.utils.math.coord.Coord;
|
||||
import mightypork.utils.patterns.Destroyable;
|
||||
import mightypork.utils.patterns.subscription.MessageBus;
|
||||
import mightypork.utils.time.TimerDelta;
|
||||
import mightypork.utils.time.TimerInterpolating;
|
||||
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.LWJGLException;
|
||||
import org.lwjgl.input.Keyboard;
|
||||
import org.lwjgl.input.Mouse;
|
||||
import org.lwjgl.openal.AL;
|
||||
import org.lwjgl.opengl.Display;
|
||||
import org.lwjgl.opengl.DisplayMode;
|
||||
|
||||
|
||||
public class App {
|
||||
public class App implements Destroyable {
|
||||
|
||||
/** instance */
|
||||
public static App inst;
|
||||
/** Current delta time (secs since last render) */
|
||||
public static double currentDelta = 0;
|
||||
/** instance pointer */
|
||||
private static App inst;
|
||||
|
||||
private static DisplayMode windowDisplayMode = null;
|
||||
private InputSystem input;
|
||||
private SoundSystem sounds;
|
||||
private DisplaySystem display;
|
||||
private MessageBus events;
|
||||
|
||||
/** current screen */
|
||||
public static Screen screen = null;
|
||||
private Screen screen;
|
||||
|
||||
/** Flag that screenshot is scheduled to be taken next loop */
|
||||
public static boolean scheduledScreenshot = false;
|
||||
|
||||
|
||||
private static boolean lockInstance()
|
||||
{
|
||||
if (Config.SINGLE_INSTANCE == false) return true; // bypass lock
|
||||
|
||||
final File lockFile = new File(Paths.WORKDIR, ".lock");
|
||||
try {
|
||||
final RandomAccessFile randomAccessFile = new RandomAccessFile(lockFile, "rw");
|
||||
final FileLock fileLock = randomAccessFile.getChannel().tryLock();
|
||||
if (fileLock != null) {
|
||||
Runtime.getRuntime().addShutdownHook(new Thread() {
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try {
|
||||
fileLock.release();
|
||||
randomAccessFile.close();
|
||||
lockFile.delete();
|
||||
} catch (Exception e) {
|
||||
System.out.println("Unable to remove lock file.");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
System.out.println("Unable to create and/or lock file.");
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
private boolean scheduledScreenshot = false;
|
||||
|
||||
|
||||
/**
|
||||
* Is if FS
|
||||
* Get the instance
|
||||
*
|
||||
* @return is in fs
|
||||
* @return instance of App
|
||||
*/
|
||||
public static boolean isFullscreen()
|
||||
public static App inst()
|
||||
{
|
||||
return Display.isFullscreen();
|
||||
return inst;
|
||||
}
|
||||
|
||||
|
||||
@@ -116,24 +80,19 @@ public class App {
|
||||
*/
|
||||
public static void onCrash(Throwable error)
|
||||
{
|
||||
Log.e("The game has crashed.");
|
||||
Log.e("The game has crashed.", error);
|
||||
|
||||
Log.e(error);
|
||||
|
||||
try {
|
||||
inst.deinit();
|
||||
} catch (Throwable t) {
|
||||
// ignore
|
||||
}
|
||||
inst.exit();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Quit to OS
|
||||
* Quit to OS<br>
|
||||
* Destroy app & exit VM
|
||||
*/
|
||||
public void exit()
|
||||
{
|
||||
deinit();
|
||||
destroy();
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
@@ -143,58 +102,36 @@ public class App {
|
||||
*
|
||||
* @return screen
|
||||
*/
|
||||
public Screen getScreen()
|
||||
public Screen getCurrentScreen()
|
||||
{
|
||||
return screen;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get screen size
|
||||
*
|
||||
* @return size
|
||||
*/
|
||||
public Coord getSize()
|
||||
public void initialize()
|
||||
{
|
||||
return new Coord(Display.getWidth(), Display.getHeight());
|
||||
Log.i("Initializing subsystems");
|
||||
initLock();
|
||||
initBus();
|
||||
initLogger();
|
||||
initDisplay();
|
||||
initSound();
|
||||
initInput();
|
||||
}
|
||||
|
||||
|
||||
private void init() throws LWJGLException
|
||||
@Override
|
||||
public void destroy()
|
||||
{
|
||||
// initialize main logger
|
||||
LogInstance li = Log.create("runtime", Paths.LOGS, 10);
|
||||
li.enable(Config.LOGGING_ENABLED);
|
||||
li.enableSysout(Config.LOG_TO_STDOUT);
|
||||
|
||||
// initialize display
|
||||
Display.setDisplayMode(windowDisplayMode = new DisplayMode(Const.WINDOW_SIZE_X, Const.WINDOW_SIZE_Y));
|
||||
Display.setResizable(true);
|
||||
Display.setVSyncEnabled(true);
|
||||
Display.setTitle(Const.TITLEBAR);
|
||||
Display.create();
|
||||
|
||||
if (Config.START_IN_FS) {
|
||||
switchFullscreen();
|
||||
Display.update();
|
||||
}
|
||||
|
||||
// initialize inputs
|
||||
Mouse.create();
|
||||
Keyboard.create();
|
||||
Keyboard.enableRepeatEvents(false);
|
||||
|
||||
// initialize sound system
|
||||
SoundManager.get().setListener(Const.LISTENER_POS);
|
||||
SoundManager.get().setMasterVolume(1F);
|
||||
|
||||
// start async screenshot trigger listener
|
||||
(new ThreadScreenshotTrigger()).start();
|
||||
if (sounds != null) sounds.destroy();
|
||||
if (input != null) input.destroy();
|
||||
if (display != null) display.destroy();
|
||||
}
|
||||
|
||||
|
||||
private void start() throws LWJGLException
|
||||
private void initLock()
|
||||
{
|
||||
if (!Config.SINGLE_INSTANCE) return;
|
||||
|
||||
if (!lockInstance()) {
|
||||
System.out.println("Working directory is locked.\nOnly one instance can run at a time.");
|
||||
@@ -211,207 +148,235 @@ public class App {
|
||||
exit();
|
||||
return;
|
||||
}
|
||||
|
||||
init();
|
||||
mainLoop();
|
||||
deinit();
|
||||
}
|
||||
|
||||
|
||||
private void deinit()
|
||||
private boolean lockInstance()
|
||||
{
|
||||
Display.destroy();
|
||||
Mouse.destroy();
|
||||
Keyboard.destroy();
|
||||
SoundManager.get().destroy();
|
||||
AL.destroy();
|
||||
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();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* initialize display
|
||||
*/
|
||||
private void initDisplay()
|
||||
{
|
||||
display = new DisplaySystem();
|
||||
display.createMainWindow(Const.WINDOW_W, Const.WINDOW_H, true, Config.START_IN_FS, Const.TITLEBAR);
|
||||
display.setTargetFps(Const.FPS_RENDER);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* initialize main logger
|
||||
*/
|
||||
private void initLogger()
|
||||
{
|
||||
LogInstance li = Log.create("runtime", Paths.LOGS, 10);
|
||||
li.enable(Config.LOGGING_ENABLED);
|
||||
li.enableSysout(Config.LOG_TO_STDOUT);
|
||||
}
|
||||
|
||||
|
||||
private void start()
|
||||
{
|
||||
initialize();
|
||||
mainLoop();
|
||||
exit();
|
||||
}
|
||||
|
||||
/** timer */
|
||||
private TimerDelta timerRender;
|
||||
private TimerInterpolating timerGui;
|
||||
|
||||
private int timerAfterResize = 0;
|
||||
|
||||
|
||||
private void mainLoop()
|
||||
{
|
||||
screen = new ScreenSplash();
|
||||
|
||||
screen.init();
|
||||
screen.setActive(true);
|
||||
|
||||
timerRender = new TimerDelta();
|
||||
timerGui = new TimerInterpolating(Const.FPS_GUI_UPDATE);
|
||||
|
||||
while (!Display.isCloseRequested()) {
|
||||
glLoadIdentity();
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
while (!display.isCloseRequested()) {
|
||||
display.beginFrame();
|
||||
|
||||
// gui update
|
||||
timerGui.sync();
|
||||
|
||||
int ticks = timerGui.getSkipped();
|
||||
|
||||
if (ticks >= 1) {
|
||||
screen.updateGui();
|
||||
input.poll();
|
||||
timerGui.startNewFrame();
|
||||
}
|
||||
|
||||
currentDelta = timerRender.getDelta();
|
||||
double delta = timerRender.getDelta();
|
||||
|
||||
// RENDER
|
||||
screen.render(currentDelta);
|
||||
SoundManager.get().update(currentDelta);
|
||||
sounds.update(delta);
|
||||
|
||||
Display.update();
|
||||
// Screen
|
||||
screen.update(delta);
|
||||
|
||||
if (scheduledScreenshot) {
|
||||
takeScreenshot();
|
||||
scheduledScreenshot = false;
|
||||
}
|
||||
|
||||
if (Keys.justPressed(Keyboard.KEY_F11)) {
|
||||
Log.f2("F11, toggle fullscreen.");
|
||||
switchFullscreen();
|
||||
screen.onFullscreenChange();
|
||||
Keys.destroyChangeState(Keyboard.KEY_F11);
|
||||
}
|
||||
|
||||
if (Keyboard.isKeyDown(Keyboard.KEY_LCONTROL)) {
|
||||
if (Keyboard.isKeyDown(Keyboard.KEY_Q)) {
|
||||
Log.f2("Ctrl+Q, force quit.");
|
||||
Keys.destroyChangeState(Keyboard.KEY_Q);
|
||||
exit();
|
||||
return;
|
||||
}
|
||||
|
||||
// if (Keyboard.isKeyDown(Keyboard.KEY_M)) {
|
||||
// Log.f2("Ctrl+M, go to main menu.");
|
||||
// Keys.destroyChangeState(Keyboard.KEY_M);
|
||||
// replaceScreen(new ScreenMenuMain());
|
||||
// }
|
||||
|
||||
if (Keyboard.isKeyDown(Keyboard.KEY_F)) {
|
||||
Log.f2("Ctrl+F, switch fullscreen.");
|
||||
Keys.destroyChangeState(Keyboard.KEY_F);
|
||||
switchFullscreen();
|
||||
screen.onFullscreenChange();
|
||||
}
|
||||
}
|
||||
|
||||
if (Display.wasResized()) {
|
||||
screen.onWindowResize();
|
||||
timerAfterResize = 0;
|
||||
} else { // ensure screen has even size
|
||||
timerAfterResize++;
|
||||
if (timerAfterResize > Const.FPS_GUI_UPDATE * 0.3) {
|
||||
timerAfterResize = 0;
|
||||
int x = Display.getX();
|
||||
int y = Display.getY();
|
||||
|
||||
int w = Display.getWidth();
|
||||
int h = Display.getHeight();
|
||||
if (w % 2 != 0 || h % 2 != 0) {
|
||||
try {
|
||||
Display.setDisplayMode(windowDisplayMode = new DisplayMode(w - w % 2, h - h % 2));
|
||||
screen.onWindowResize();
|
||||
Display.setLocation(x, y);
|
||||
} catch (LWJGLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
Display.sync(Const.FPS_RENDER);
|
||||
} catch (Throwable t) {
|
||||
Log.e("Your graphics card driver does not support fullscreen properly.", t);
|
||||
|
||||
try {
|
||||
Display.setDisplayMode(windowDisplayMode);
|
||||
} catch (LWJGLException e) {
|
||||
Log.e("Error going back from corrupted fullscreen.");
|
||||
onCrash(e);
|
||||
}
|
||||
}
|
||||
display.endFrame();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// UPDATE LOOP END
|
||||
|
||||
/**
|
||||
* Do take a screenshot
|
||||
*/
|
||||
public void takeScreenshot()
|
||||
{
|
||||
//Effects.play("gui.screenshot");
|
||||
sounds.getEffect("gui.shutter").play(1);
|
||||
Utils.runAsThread(new TaskTakeScreenshot());
|
||||
}
|
||||
|
||||
glReadBuffer(GL_FRONT);
|
||||
int width = Display.getDisplayMode().getWidth();
|
||||
int height = Display.getDisplayMode().getHeight();
|
||||
int bpp = 4; // Assuming a 32-bit display with a byte each for red, green, blue, and alpha.
|
||||
ByteBuffer buffer = BufferUtils.createByteBuffer(width * height * bpp);
|
||||
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
|
||||
|
||||
(new ThreadSaveScreenshot(buffer, width, height, bpp)).start();
|
||||
//
|
||||
// static accessors
|
||||
//
|
||||
|
||||
/**
|
||||
* @return sound system of the running instance
|
||||
*/
|
||||
public static SoundSystem soundsys()
|
||||
{
|
||||
return inst.sounds;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Replace screen
|
||||
*
|
||||
* @param newScreen new screen
|
||||
* @return input system of the running instance
|
||||
*/
|
||||
public void replaceScreen(Screen newScreen)
|
||||
public static InputSystem input()
|
||||
{
|
||||
screen = newScreen;
|
||||
screen.init();
|
||||
return inst.input;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Replace screen, don't init it
|
||||
*
|
||||
* @param newScreen new screen
|
||||
* @return display system of the running instance
|
||||
*/
|
||||
public void replaceScreenNoInit(Screen newScreen)
|
||||
public static DisplaySystem disp()
|
||||
{
|
||||
screen = newScreen;
|
||||
return inst.display;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Toggle FS if possible
|
||||
* @return event bus of the running instance
|
||||
*/
|
||||
public void switchFullscreen()
|
||||
public static MessageBus msgbus()
|
||||
{
|
||||
try {
|
||||
if (!Display.isFullscreen()) {
|
||||
Log.f3("Entering fullscreen.");
|
||||
// save window resize
|
||||
windowDisplayMode = new DisplayMode(Display.getWidth(), Display.getHeight());
|
||||
|
||||
Display.setDisplayMode(Display.getDesktopDisplayMode());
|
||||
Display.setFullscreen(true);
|
||||
Display.update();
|
||||
//
|
||||
//
|
||||
// DisplayMode mode = Display.getDesktopDisplayMode(); //findDisplayMode(WIDTH, HEIGHT);
|
||||
// Display.setDisplayModeAndFullscreen(mode);
|
||||
} else {
|
||||
Log.f3("Leaving fullscreen.");
|
||||
Display.setDisplayMode(windowDisplayMode);
|
||||
Display.update();
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
Log.e("Failed to toggle fullscreen mode.", t);
|
||||
try {
|
||||
Display.setDisplayMode(windowDisplayMode);
|
||||
Display.update();
|
||||
} catch (Throwable t1) {
|
||||
onCrash(t1);
|
||||
}
|
||||
}
|
||||
return inst.events;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return screen of the running instance
|
||||
*/
|
||||
public static Screen screen()
|
||||
{
|
||||
return inst.getCurrentScreen();
|
||||
}
|
||||
|
||||
|
||||
public static boolean broadcast(Object message)
|
||||
{
|
||||
boolean was = msgbus().broadcast(message);
|
||||
if (!was) Log.w("Message not accepted by any channel: " + message);
|
||||
return was;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
package mightypork.rogue;
|
||||
|
||||
|
||||
import mightypork.utils.math.coord.Coord;
|
||||
|
||||
|
||||
/**
|
||||
* Application constants
|
||||
*
|
||||
@@ -18,12 +15,10 @@ public class Const {
|
||||
public static final String TITLEBAR = APP_NAME + " v." + VERSION;
|
||||
|
||||
// AUDIO
|
||||
public static final Coord LISTENER_POS = Coord.ZERO;
|
||||
|
||||
public static final int FPS_RENDER = 200; // max
|
||||
public static final long FPS_GUI_UPDATE = 60;
|
||||
|
||||
// INITIAL WINDOW SIZE
|
||||
public static final int WINDOW_SIZE_X = 1024;
|
||||
public static final int WINDOW_SIZE_Y = 768;
|
||||
public static final int WINDOW_W = 1024;
|
||||
public static final int WINDOW_H = 768;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ public class CrashHandler implements UncaughtExceptionHandler {
|
||||
@Override
|
||||
public void uncaughtException(Thread t, Throwable e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Preloaded font identifier [name, size, style]
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public static class FontId {
|
||||
|
||||
/** font size (pt) */
|
||||
public float size = 24;
|
||||
/** font name, registered with registerFile */
|
||||
public String name = "";
|
||||
/** font style. The given style must be in a file. */
|
||||
public Style style;
|
||||
|
||||
/** Set of glyphs in this ID */
|
||||
public String glyphs = "";
|
||||
|
||||
/** Index for faster comparision of glyph ids. */
|
||||
public int glyphset_id = 0;
|
||||
|
||||
|
||||
/**
|
||||
* Preloaded font identifier
|
||||
*
|
||||
* @param name font name (registerFile)
|
||||
* @param size font size (pt)
|
||||
* @param style font style
|
||||
* @param glyphs glyphs to load
|
||||
*/
|
||||
public FontId(String name, double size, Style style, String glyphs) {
|
||||
this.name = name;
|
||||
this.size = (float) size;
|
||||
this.style = style;
|
||||
|
||||
if (glyphs.equals(Glyphs.basic)) {
|
||||
glyphset_id = 1;
|
||||
} else if (glyphs.equals(Glyphs.alnum)) {
|
||||
glyphset_id = 2;
|
||||
} else if (glyphs.equals(Glyphs.basic_text)) {
|
||||
glyphset_id = 3;
|
||||
} else if (glyphs.equals(Glyphs.numbers)) {
|
||||
glyphset_id = 4;
|
||||
} else if (glyphs.equals(Glyphs.alpha)) {
|
||||
glyphset_id = 5;
|
||||
} else if (glyphs.equals(Glyphs.all)) {
|
||||
glyphset_id = 6;
|
||||
} else if (glyphs.equals(Glyphs.alnum_extra)) {
|
||||
glyphset_id = 7;
|
||||
} else if (glyphs.equals(Glyphs.signs)) {
|
||||
glyphset_id = 8;
|
||||
} else if (glyphs.equals(Glyphs.signs_extra)) {
|
||||
glyphset_id = 9;
|
||||
} else {
|
||||
this.glyphs = glyphs;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (obj == null) return false;
|
||||
if (!(obj.getClass().isAssignableFrom(getClass()))) return false;
|
||||
if (obj instanceof FontId) {
|
||||
if (obj == this) return true;
|
||||
FontId id2 = ((FontId) obj);
|
||||
boolean flag = true;
|
||||
flag &= id2.size == size;
|
||||
flag &= id2.name.equals(name);
|
||||
flag &= id2.style == style;
|
||||
flag &= ((id2.glyphset_id != -1 && id2.glyphset_id == glyphset_id) || id2.glyphs.equals(glyphs));
|
||||
return flag;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return (new Float(size).hashCode()) ^ name.hashCode() ^ style.hashCode() ^ glyphset_id;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "[" + name + ", " + size + ", " + style + (glyphset_id > 0 ? ", g=" + glyphset_id : ", g=custom") + "]";
|
||||
}
|
||||
}
|
||||
// /**
|
||||
// * Preloaded font identifier [name, size, style]
|
||||
// *
|
||||
// * @author MightyPork
|
||||
// */
|
||||
// public static class FontId {
|
||||
//
|
||||
// /** font size (pt) */
|
||||
// public float size = 24;
|
||||
// /** font name, registered with registerFile */
|
||||
// public String name = "";
|
||||
// /** font style. The given style must be in a file. */
|
||||
// public Style style;
|
||||
//
|
||||
// /** Set of glyphs in this ID */
|
||||
// public String glyphs = "";
|
||||
//
|
||||
// /** Index for faster comparision of glyph ids. */
|
||||
// public int glyphset_id = 0;
|
||||
//
|
||||
//
|
||||
// /**
|
||||
// * Preloaded font identifier
|
||||
// *
|
||||
// * @param name font name (registerFile)
|
||||
// * @param size font size (pt)
|
||||
// * @param style font style
|
||||
// * @param glyphs glyphs to load
|
||||
// */
|
||||
// public FontId(String name, double size, Style style, String glyphs) {
|
||||
// this.name = name;
|
||||
// this.size = (float) size;
|
||||
// this.style = style;
|
||||
//
|
||||
// if (glyphs.equals(Glyphs.basic)) {
|
||||
// glyphset_id = 1;
|
||||
// } else if (glyphs.equals(Glyphs.alnum)) {
|
||||
// glyphset_id = 2;
|
||||
// } else if (glyphs.equals(Glyphs.basic_text)) {
|
||||
// glyphset_id = 3;
|
||||
// } else if (glyphs.equals(Glyphs.numbers)) {
|
||||
// glyphset_id = 4;
|
||||
// } else if (glyphs.equals(Glyphs.alpha)) {
|
||||
// glyphset_id = 5;
|
||||
// } else if (glyphs.equals(Glyphs.all)) {
|
||||
// glyphset_id = 6;
|
||||
// } else if (glyphs.equals(Glyphs.alnum_extra)) {
|
||||
// glyphset_id = 7;
|
||||
// } else if (glyphs.equals(Glyphs.signs)) {
|
||||
// glyphset_id = 8;
|
||||
// } else if (glyphs.equals(Glyphs.signs_extra)) {
|
||||
// glyphset_id = 9;
|
||||
// } else {
|
||||
// this.glyphs = glyphs;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
//
|
||||
// @Override
|
||||
// public boolean equals(Object obj)
|
||||
// {
|
||||
// if (obj == null) return false;
|
||||
// if (!(obj.getClass().isAssignableFrom(getClass()))) return false;
|
||||
// if (obj instanceof FontId) {
|
||||
// if (obj == this) return true;
|
||||
// FontId id2 = ((FontId) obj);
|
||||
// boolean flag = true;
|
||||
// flag &= id2.size == size;
|
||||
// flag &= id2.name.equals(name);
|
||||
// flag &= id2.style == style;
|
||||
// flag &= ((id2.glyphset_id != -1 && id2.glyphset_id == glyphset_id) || id2.glyphs.equals(glyphs));
|
||||
// return flag;
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// @Override
|
||||
// public int hashCode()
|
||||
// {
|
||||
// return (new Float(size).hashCode()) ^ name.hashCode() ^ style.hashCode() ^ glyphset_id;
|
||||
// }
|
||||
//
|
||||
//
|
||||
// @Override
|
||||
// public String toString()
|
||||
// {
|
||||
// return "[" + name + ", " + size + ", " + style + (glyphset_id > 0 ? ", g=" + glyphset_id : ", g=custom") + "]";
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* Group of styles of one font.
|
||||
@@ -255,7 +255,7 @@ public class FontManager {
|
||||
*/
|
||||
public static LoadedFont loadFont(String name, double size, Style style, String glyphs)
|
||||
{
|
||||
return loadFont(name, size, style, glyphs, 9, 8, Coord.ONE, 0, 0);
|
||||
return loadFont(name, size, style, glyphs, 9, 8, Coord.one(), 0, 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ import mightypork.rogue.Config;
|
||||
import mightypork.utils.logging.Log;
|
||||
import mightypork.utils.math.color.RGB;
|
||||
import mightypork.utils.math.coord.Coord;
|
||||
import mightypork.utils.math.coord.CoordI;
|
||||
|
||||
import org.lwjgl.BufferUtils;
|
||||
import org.lwjgl.opengl.GL11;
|
||||
@@ -598,38 +597,12 @@ public class LoadedFont {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Draw string with font.
|
||||
*
|
||||
* @param pos coord
|
||||
* @param text text to draw
|
||||
* @param color render color
|
||||
* @param align (-1,0,1)
|
||||
*/
|
||||
public void draw(CoordI pos, String text, RGB color, int align)
|
||||
{
|
||||
drawString(pos.x, pos.y, text, 1, 1, color, align);
|
||||
}
|
||||
|
||||
|
||||
public void drawFuzzy(Coord pos, String text, int align, RGB textColor, RGB blurColor, int blurSize)
|
||||
{
|
||||
drawFuzzy(pos, text, align, textColor, blurColor, blurSize, true);
|
||||
}
|
||||
|
||||
|
||||
public void drawFuzzy(CoordI pos, String text, int align, RGB textColor, RGB blurColor, int blurSize)
|
||||
{
|
||||
drawFuzzy(pos.toCoord(), text, align, textColor, blurColor, blurSize, true);
|
||||
}
|
||||
|
||||
|
||||
public void drawFuzzy(CoordI pos, String text, int align, RGB textColor, RGB blurColor, int blurSize, boolean smooth)
|
||||
{
|
||||
drawFuzzy(pos.toCoord(), text, align, textColor, blurColor, blurSize, smooth);
|
||||
}
|
||||
|
||||
|
||||
public void drawFuzzy(Coord pos, String text, int align, RGB textColor, RGB blurColor, int blurSize, boolean smooth)
|
||||
{
|
||||
glPushMatrix();
|
||||
|
||||
@@ -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.logging.Log;
|
||||
import mightypork.utils.math.coord.Coord;
|
||||
import mightypork.utils.patterns.Destroyable;
|
||||
|
||||
import org.newdawn.slick.openal.Audio;
|
||||
import org.newdawn.slick.openal.SoundStore;
|
||||
@@ -14,7 +15,7 @@ import org.newdawn.slick.openal.SoundStore;
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class AudioX implements Audio {
|
||||
public class AudioX implements Destroyable {
|
||||
|
||||
private enum PlayMode
|
||||
{
|
||||
@@ -22,14 +23,26 @@ public class AudioX implements Audio {
|
||||
};
|
||||
|
||||
private Audio audio = null;
|
||||
private float pauseLoopPosition = 0;
|
||||
private double pauseLoopPosition = 0;
|
||||
private boolean looping = false;
|
||||
private boolean paused = false;
|
||||
private PlayMode mode = PlayMode.EFFECT;
|
||||
private float pitch = 1;
|
||||
private float gain = 1;
|
||||
private double lastPlayPitch = 1;
|
||||
private double lastPlayGain = 1;
|
||||
|
||||
private String resourcePath;
|
||||
private final String resourcePath;
|
||||
private boolean loadFailed = false;
|
||||
|
||||
|
||||
/**
|
||||
* Create deferred primitive audio player
|
||||
*
|
||||
* @param resourceName resource to load when needed
|
||||
*/
|
||||
public AudioX(String resourceName) {
|
||||
this.audio = null;
|
||||
this.resourcePath = resourceName;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@@ -37,7 +50,7 @@ public class AudioX implements Audio {
|
||||
*/
|
||||
public void pauseLoop()
|
||||
{
|
||||
if (!ensureLoaded()) return;
|
||||
if (!load()) return;
|
||||
|
||||
if (isPlaying() && looping) {
|
||||
pauseLoopPosition = audio.getPosition();
|
||||
@@ -54,16 +67,16 @@ public class AudioX implements Audio {
|
||||
*/
|
||||
public int resumeLoop()
|
||||
{
|
||||
if (!ensureLoaded()) return -1;
|
||||
if (!load()) return -1;
|
||||
|
||||
int source = -1;
|
||||
if (looping && paused) {
|
||||
if (mode == PlayMode.MUSIC) {
|
||||
source = audio.playAsMusic(pitch, gain, true);
|
||||
source = audio.playAsMusic((float) lastPlayPitch, (float) lastPlayGain, true);
|
||||
} else {
|
||||
source = audio.playAsSoundEffect(pitch, gain, true);
|
||||
source = audio.playAsSoundEffect((float) lastPlayPitch, (float) lastPlayGain, true);
|
||||
}
|
||||
audio.setPosition(pauseLoopPosition);
|
||||
audio.setPosition((float) pauseLoopPosition);
|
||||
paused = false;
|
||||
}
|
||||
return source;
|
||||
@@ -71,34 +84,27 @@ public class AudioX implements Audio {
|
||||
|
||||
|
||||
/**
|
||||
* Create deferred primitive audio player
|
||||
* Check if resource is loaded
|
||||
*
|
||||
* @param resourceName resource to load when needed
|
||||
* @return resource is loaded
|
||||
*/
|
||||
public AudioX(String resourceName) {
|
||||
this.audio = null;
|
||||
this.resourcePath = resourceName;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if can play, if not, try to load sound.
|
||||
*
|
||||
* @return can now play
|
||||
*/
|
||||
private boolean ensureLoaded()
|
||||
private boolean isLoaded()
|
||||
{
|
||||
load();
|
||||
|
||||
return audio != null;
|
||||
}
|
||||
|
||||
|
||||
public void load()
|
||||
/**
|
||||
* Try to load if not loaded already
|
||||
*
|
||||
* @return is loaded
|
||||
*/
|
||||
public boolean load()
|
||||
{
|
||||
if (audio != null) return; // already loaded
|
||||
if (resourcePath == null) return; // not loaded, but can't load anyway
|
||||
if (isLoaded()) return true; // already loaded
|
||||
if (loadFailed || resourcePath == null) return false; // not loaded, but can't load anyway
|
||||
|
||||
loadFailed = false;
|
||||
try {
|
||||
String ext = FileUtils.getExtension(resourcePath);
|
||||
|
||||
@@ -112,119 +118,168 @@ public class AudioX implements Audio {
|
||||
} else if (ext.equalsIgnoreCase("mod")) {
|
||||
audio = SoundStore.get().getMOD(resourcePath);
|
||||
} else {
|
||||
resourcePath = null; // don't try next time
|
||||
Log.e("Invalid audio file extension: " + resourcePath);
|
||||
loadFailed = true; // don't try next time
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.e("Could not load " + resourcePath, e);
|
||||
resourcePath = null; // don't try next time
|
||||
loadFailed = true; // don't try next time
|
||||
}
|
||||
|
||||
return isLoaded();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void stop()
|
||||
{
|
||||
if (!ensureLoaded()) return;
|
||||
if (!isLoaded()) return;
|
||||
|
||||
audio.stop();
|
||||
paused = false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getBufferID()
|
||||
{
|
||||
if (!ensureLoaded()) return -1;
|
||||
|
||||
return audio.getBufferID();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isPlaying()
|
||||
{
|
||||
if (!ensureLoaded()) return false;
|
||||
if (!isLoaded()) return false;
|
||||
|
||||
return audio.isPlaying();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isPaused()
|
||||
{
|
||||
if (!ensureLoaded()) return false;
|
||||
if (!isLoaded()) return false;
|
||||
|
||||
return audio.isPaused();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int playAsSoundEffect(float pitch, float gain, boolean loop)
|
||||
/**
|
||||
* Play as sound effect at listener position
|
||||
*
|
||||
* @param pitch pitch (1 = default)
|
||||
* @param gain gain (0-1)
|
||||
* @param loop looping
|
||||
* @return source id
|
||||
*/
|
||||
public int playAsEffect(double pitch, double gain, boolean loop)
|
||||
{
|
||||
return playAsSoundEffect(pitch, gain, loop, SoundManager.get().listener);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int playAsSoundEffect(float pitch, float gain, boolean loop, float x, float y, float z)
|
||||
{
|
||||
if (!ensureLoaded()) return -1;
|
||||
|
||||
this.pitch = pitch;
|
||||
this.gain = gain;
|
||||
looping = loop;
|
||||
mode = PlayMode.EFFECT;
|
||||
return audio.playAsSoundEffect(pitch, gain, loop, x, y, z);
|
||||
return playAsEffect(pitch, gain, loop, SoundSystem.getListener());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Play this sound as a sound effect
|
||||
* Play as sound effect at given X-Y position
|
||||
*
|
||||
* @param pitch The pitch of the play back
|
||||
* @param gain The gain of the play back
|
||||
* @param loop True if we should loop
|
||||
* @param pos The position of the sound
|
||||
* @return The ID of the source playing the sound
|
||||
* @param pitch pitch (1 = default)
|
||||
* @param gain gain (0-1)
|
||||
* @param loop looping
|
||||
* @param x
|
||||
* @param y
|
||||
* @return source id
|
||||
*/
|
||||
public int playAsSoundEffect(float pitch, float gain, boolean loop, Coord pos)
|
||||
public int playAsEffect(double pitch, double gain, boolean loop, double x, double y)
|
||||
{
|
||||
return playAsSoundEffect(pitch, gain, loop, (float) pos.x, (float) pos.y, (float) pos.z);
|
||||
return playAsEffect(pitch, gain, loop, x, y, SoundSystem.getListener().z);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int playAsMusic(float pitch, float gain, boolean loop)
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
this.pitch = pitch;
|
||||
this.gain = gain;
|
||||
if (!load()) return -1;
|
||||
|
||||
this.lastPlayPitch = pitch;
|
||||
this.lastPlayGain = gain;
|
||||
looping = loop;
|
||||
mode = PlayMode.EFFECT;
|
||||
return audio.playAsSoundEffect((float) pitch, (float) gain, loop, (float) x, (float) y, (float) z);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Play as sound effect at given position
|
||||
*
|
||||
* @param pitch pitch (1 = default)
|
||||
* @param gain gain (0-1)
|
||||
* @param loop looping
|
||||
* @param pos coord
|
||||
* @return source id
|
||||
*/
|
||||
public int playAsEffect(double pitch, double gain, boolean loop, Coord pos)
|
||||
{
|
||||
if (!load()) return -1;
|
||||
|
||||
return playAsEffect(pitch, gain, loop, pos.x, pos.y, pos.z);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
if (!load()) return -1;
|
||||
|
||||
this.lastPlayPitch = (float) pitch;
|
||||
this.lastPlayGain = (float) gain;
|
||||
looping = loop;
|
||||
mode = PlayMode.MUSIC;
|
||||
return audio.playAsMusic(pitch, gain, loop);
|
||||
return audio.playAsMusic((float) pitch, (float) gain, loop);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean setPosition(float position)
|
||||
public void destroy()
|
||||
{
|
||||
return audio.setPosition(position);
|
||||
}
|
||||
if (!isLoaded()) return;
|
||||
|
||||
|
||||
@Override
|
||||
public float getPosition()
|
||||
{
|
||||
return audio.getPosition();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void release()
|
||||
{
|
||||
audio.release();
|
||||
audio = null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + ((resourcePath == null) ? 0 : resourcePath.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (this == obj) return true;
|
||||
if (obj == null) return false;
|
||||
if (!(obj instanceof AudioX)) return false;
|
||||
AudioX other = (AudioX) obj;
|
||||
if (resourcePath == null) {
|
||||
if (other.resourcePath != null) return false;
|
||||
} else if (!resourcePath.equals(other.resourcePath)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ public class EffectPlayer extends AudioPlayer {
|
||||
{
|
||||
if (!canPlay()) return -1;
|
||||
|
||||
return getAudio().playAsSoundEffect(getPitch(pitch), getGain(gain), false);
|
||||
return getAudio().playAsEffect(getPitch(pitch), getGain(gain), false);
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ public class EffectPlayer extends AudioPlayer {
|
||||
{
|
||||
if (!canPlay()) return -1;
|
||||
|
||||
return getAudio().playAsSoundEffect(getPitch(pitch), getGain(gain), false, pos);
|
||||
return getAudio().playAsEffect(getPitch(pitch), getGain(gain), false, pos);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,18 +10,18 @@ import mightypork.utils.objects.Mutable;
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
public JointVolume(Mutable<Float>... volumes) {
|
||||
super(1F);
|
||||
public JointVolume(Mutable<Double>... volumes) {
|
||||
super(1D);
|
||||
this.volumes = volumes;
|
||||
}
|
||||
|
||||
@@ -30,13 +30,13 @@ public class JointVolume extends Mutable<Float> {
|
||||
* Get combined gain (multiplied)
|
||||
*/
|
||||
@Override
|
||||
public Float get()
|
||||
public Double get()
|
||||
{
|
||||
float f = super.get();
|
||||
for (Mutable<Float> v : volumes)
|
||||
f *= v.get();
|
||||
double d = super.get();
|
||||
for (Mutable<Double> v : volumes)
|
||||
d *= v.get();
|
||||
|
||||
return Calc.clampf(f, 0, 1);
|
||||
return Calc.clampd(d, 0, 1);
|
||||
}
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ public class JointVolume extends Mutable<Float> {
|
||||
* Set master gain
|
||||
*/
|
||||
@Override
|
||||
public void set(Float o)
|
||||
public void set(Double o)
|
||||
{
|
||||
super.set(o);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ import mightypork.utils.time.Updateable;
|
||||
import org.lwjgl.openal.AL10;
|
||||
|
||||
|
||||
public class LoopPlayer extends AudioPlayer implements Updateable, Pauseable {
|
||||
public class LoopPlayer extends BaseAudioPlayer implements Updateable, Pauseable {
|
||||
|
||||
private int sourceID = -1;
|
||||
|
||||
@@ -28,7 +28,7 @@ public class LoopPlayer extends AudioPlayer implements Updateable, Pauseable {
|
||||
private double outTime = 1;
|
||||
|
||||
|
||||
public LoopPlayer(AudioX track, double pitch, double baseGain, Mutable<Float> gainMultiplier) {
|
||||
public LoopPlayer(AudioX track, double pitch, double baseGain, Mutable<Double> gainMultiplier) {
|
||||
super(track, (float) pitch, (float) baseGain, gainMultiplier);
|
||||
|
||||
paused = true;
|
||||
@@ -45,7 +45,7 @@ public class LoopPlayer extends AudioPlayer implements Updateable, Pauseable {
|
||||
private void initLoop()
|
||||
{
|
||||
if (!canPlay() && sourceID == -1) {
|
||||
sourceID = getAudio().playAsSoundEffect(getPitch(1), getGain(1), true);
|
||||
sourceID = getAudio().playAsEffect(getPitch(1), getGain(1), true);
|
||||
getAudio().pauseLoop();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 mightypork.rogue.textures.TextureManager;
|
||||
import mightypork.rogue.textures.TxQuad;
|
||||
import mightypork.utils.math.Calc.Deg;
|
||||
import mightypork.utils.math.color.HSV;
|
||||
import mightypork.utils.math.color.RGB;
|
||||
import mightypork.utils.math.coord.Coord;
|
||||
@@ -18,6 +19,10 @@ import org.newdawn.slick.opengl.Texture;
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class RenderUtils {
|
||||
|
||||
private static final Coord AXIS_X = new Coord(1, 0, 0);
|
||||
private static final Coord AXIS_Y = new Coord(0, 1, 0);
|
||||
private static final Coord AXIS_Z = new Coord(0, 0, 1);
|
||||
|
||||
/**
|
||||
* Render quad 2D
|
||||
@@ -471,15 +476,15 @@ public class RenderUtils {
|
||||
*/
|
||||
public static void quadTexturedAbs(Rect quad, Rect textureCoords)
|
||||
{
|
||||
double left = quad.x1();
|
||||
double bottom = quad.y2();
|
||||
double right = quad.x2();
|
||||
double top = quad.y1();
|
||||
double left = quad.xMin();
|
||||
double bottom = quad.yMax();
|
||||
double right = quad.xMax();
|
||||
double top = quad.yMin();
|
||||
|
||||
double tleft = textureCoords.x1();
|
||||
double tbottom = textureCoords.y1();
|
||||
double tright = textureCoords.x2();
|
||||
double ttop = textureCoords.y2();
|
||||
double tleft = textureCoords.xMin();
|
||||
double tbottom = textureCoords.yMin();
|
||||
double tright = textureCoords.xMax();
|
||||
double ttop = textureCoords.yMax();
|
||||
|
||||
glBegin(GL_QUADS);
|
||||
glTexCoord2d(tleft, ttop);
|
||||
@@ -584,18 +589,18 @@ public class RenderUtils {
|
||||
|
||||
int yOffset = yOffsetTimes * frameHeight;
|
||||
|
||||
double x1 = quadRect.x1();
|
||||
double y1 = quadRect.y1();
|
||||
double x2 = quadRect.x2();
|
||||
double y2 = quadRect.y2();
|
||||
double x1 = quadRect.xMin();
|
||||
double y1 = quadRect.yMin();
|
||||
double x2 = quadRect.xMax();
|
||||
double y2 = quadRect.yMax();
|
||||
|
||||
double w = x2 - x1;
|
||||
double h = y2 - y1;
|
||||
|
||||
double tx1 = textureRect.x1();
|
||||
double ty1 = textureRect.y1();
|
||||
double tx2 = textureRect.x2();
|
||||
double ty2 = textureRect.y2();
|
||||
double tx1 = textureRect.xMin();
|
||||
double ty1 = textureRect.yMin();
|
||||
double tx2 = textureRect.xMax();
|
||||
double ty2 = textureRect.yMax();
|
||||
|
||||
double halfY = h / 2D;
|
||||
double halfX = w / 2D;
|
||||
@@ -713,15 +718,15 @@ public class RenderUtils {
|
||||
{
|
||||
Texture tx = texture;
|
||||
|
||||
double x1 = quadRect.x1();
|
||||
double y1 = quadRect.y1();
|
||||
double x2 = quadRect.x2();
|
||||
double y2 = quadRect.y2();
|
||||
double x1 = quadRect.xMin();
|
||||
double y1 = quadRect.yMin();
|
||||
double x2 = quadRect.xMax();
|
||||
double y2 = quadRect.yMax();
|
||||
|
||||
double tx1 = textureRect.x1();
|
||||
double ty1 = textureRect.y1();
|
||||
double tx2 = textureRect.x2();
|
||||
double ty2 = textureRect.y2();
|
||||
double tx1 = textureRect.xMin();
|
||||
double ty1 = textureRect.yMin();
|
||||
double tx2 = textureRect.xMax();
|
||||
double ty2 = textureRect.yMax();
|
||||
|
||||
// lt, mi, rt
|
||||
|
||||
@@ -802,15 +807,15 @@ public class RenderUtils {
|
||||
{
|
||||
Texture tx = texture;
|
||||
|
||||
double x1 = quadRect.x1();
|
||||
double y1 = quadRect.y1();
|
||||
double x2 = quadRect.x2();
|
||||
double y2 = quadRect.y2();
|
||||
double x1 = quadRect.xMin();
|
||||
double y1 = quadRect.yMin();
|
||||
double x2 = quadRect.xMax();
|
||||
double y2 = quadRect.yMax();
|
||||
|
||||
double tx1 = textureRect.x1();
|
||||
double ty1 = textureRect.y1();
|
||||
double tx2 = textureRect.x2();
|
||||
double ty2 = textureRect.y2();
|
||||
double tx1 = textureRect.xMin();
|
||||
double ty1 = textureRect.yMin();
|
||||
double tx2 = textureRect.xMax();
|
||||
double ty2 = textureRect.yMax();
|
||||
|
||||
// tp
|
||||
// mi
|
||||
@@ -916,4 +921,29 @@ public class RenderUtils {
|
||||
{
|
||||
glTranslated(coord.x, coord.y, coord.z);
|
||||
}
|
||||
|
||||
|
||||
public static void rotateX(double angle)
|
||||
{
|
||||
rotate(angle, AXIS_X);
|
||||
}
|
||||
|
||||
|
||||
public static void rotateY(double angle)
|
||||
{
|
||||
rotate(angle, AXIS_Y);
|
||||
}
|
||||
|
||||
|
||||
public static void rotateZ(double angle)
|
||||
{
|
||||
rotate(angle, AXIS_Z);
|
||||
}
|
||||
|
||||
|
||||
public static void rotate(double angle, Coord axis)
|
||||
{
|
||||
Coord vec = axis.norm(1);
|
||||
glRotated(angle, vec.x, vec.y, vec.z);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,4 +7,9 @@ package mightypork.rogue.util;
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class Utils {
|
||||
public static Thread runAsThread(Runnable r) {
|
||||
Thread t = new Thread(r);
|
||||
t.start();
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import java.util.HashMap;
|
||||
public class Log {
|
||||
|
||||
/** 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 mightypork.utils.math.coord.Coord;
|
||||
import mightypork.utils.math.coord.Vec;
|
||||
import mightypork.utils.math.easing.Easing;
|
||||
|
||||
import org.lwjgl.BufferUtils;
|
||||
|
||||
@@ -31,7 +31,7 @@ public class Calc {
|
||||
* @param point point coordinate
|
||||
* @return distance
|
||||
*/
|
||||
public static double linePointDist(Vec lineDirVec, Coord linePoint, Coord point)
|
||||
public static double linePointDist(Coord lineDirVec, Coord linePoint, Coord point)
|
||||
{
|
||||
// line point L[lx,ly]
|
||||
double lx = linePoint.x;
|
||||
@@ -59,9 +59,9 @@ public class Calc {
|
||||
* @param point point coordinate
|
||||
* @return distance
|
||||
*/
|
||||
public static double linePointDistXZ(Vec lineDirVec, Coord linePoint, Coord point)
|
||||
public static double linePointDistXZ(Coord lineDirVec, Coord linePoint, Coord point)
|
||||
{
|
||||
return linePointDist(new Vec(lineDirVec.x, lineDirVec.z), new Coord(linePoint.x, linePoint.z), new Coord(point.x, point.z));
|
||||
return linePointDist(new Coord(lineDirVec.x, lineDirVec.z), new Coord(linePoint.x, linePoint.z), new Coord(point.x, point.z));
|
||||
}
|
||||
|
||||
|
||||
@@ -739,28 +739,45 @@ public class Calc {
|
||||
/**
|
||||
* Get number from A to B at delta time (tween A to B)
|
||||
*
|
||||
* @param last last number
|
||||
* @param now new number
|
||||
* @param dtime delta time
|
||||
* @param from last number
|
||||
* @param to new number
|
||||
* @param time time 0..1
|
||||
* @param easing easing function
|
||||
* @return current number to render
|
||||
*/
|
||||
public static double interpolate(double last, double now, double dtime)
|
||||
{
|
||||
return last + (now - last) * dtime;
|
||||
public static double interpolate(double from, double to, double time, Easing easing)
|
||||
{
|
||||
return from + (to - from) * easing.get(time);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get angle [degrees] from A to B at delta time (tween A to B)
|
||||
*
|
||||
* @param last last angle
|
||||
* @param now new angle
|
||||
* @param delta delta time
|
||||
* @param from last angle
|
||||
* @param to new angle
|
||||
* @param time time 0..1
|
||||
* @param easing easing function
|
||||
* @return current angle to render
|
||||
*/
|
||||
public static double interpolateDeg(double 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;
|
||||
|
||||
|
||||
import mightypork.utils.math.Calc.Deg;
|
||||
import mightypork.utils.math.Calc.Rad;
|
||||
import mightypork.utils.math.coord.Coord;
|
||||
|
||||
|
||||
@@ -12,18 +14,87 @@ import mightypork.utils.math.coord.Coord;
|
||||
public class Polar {
|
||||
|
||||
/** angle in radians */
|
||||
public double angle = 0;
|
||||
private double angle = 0;
|
||||
|
||||
/** distance in units */
|
||||
public double distance = 0;
|
||||
private double radius = 0;
|
||||
|
||||
|
||||
/**
|
||||
* @param angle angle in radians
|
||||
* Create a polar
|
||||
*
|
||||
* @param angle angle in RAD
|
||||
* @param distance distance from origin
|
||||
*/
|
||||
public Polar(double angle, double distance) {
|
||||
this(angle, false, distance);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create a polar
|
||||
*
|
||||
* @param angle angle
|
||||
* @param deg angle is in DEG
|
||||
* @param distance radius
|
||||
*/
|
||||
public Polar(double angle, boolean deg, double distance) {
|
||||
this.radius = distance;
|
||||
this.angle = deg ? Deg.toRad(angle) : angle;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return angle in RAD
|
||||
*/
|
||||
public double getAngle()
|
||||
{
|
||||
return angle;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return angle in DEG
|
||||
*/
|
||||
public double getAngleDeg()
|
||||
{
|
||||
return Rad.toDeg(angle);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param angle angle in RAD
|
||||
*/
|
||||
public void setAngle(double angle)
|
||||
{
|
||||
this.angle = angle;
|
||||
this.distance = distance;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param angle angle in DEG
|
||||
*/
|
||||
public void setAngleDeg(double angle)
|
||||
{
|
||||
this.angle = Deg.toRad(angle);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return radius
|
||||
*/
|
||||
public double getRadius()
|
||||
{
|
||||
return radius;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param r radius
|
||||
*/
|
||||
public void setRadius(double r)
|
||||
{
|
||||
this.radius = r;
|
||||
}
|
||||
|
||||
|
||||
@@ -52,19 +123,6 @@ public class Polar {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make polar from coords
|
||||
*
|
||||
* @param x x coord
|
||||
* @param z z coord
|
||||
* @return polar
|
||||
*/
|
||||
public static Polar fromCoordXZ(double x, double z)
|
||||
{
|
||||
return Polar.fromCoordXZ(new Coord(x, 0, z));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get coord from polar
|
||||
*
|
||||
@@ -72,36 +130,40 @@ public class Polar {
|
||||
*/
|
||||
public Coord toCoord()
|
||||
{
|
||||
return new Coord(distance * Math.cos(angle), distance * Math.sin(angle));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get X,0,Y coord from polar
|
||||
*
|
||||
* @return coord
|
||||
*/
|
||||
public Coord toCoordXZ()
|
||||
{
|
||||
return new Coord(distance * Math.cos(angle), 0, distance * Math.sin(angle));
|
||||
return new Coord(radius * Math.cos(angle), radius * Math.sin(angle));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "Polar(theta=" + angle + ", r=" + distance + ")";
|
||||
return "Polar(" + angle + "rad, " + radius + ")";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Build polar from X,Z instead of X,Y
|
||||
*
|
||||
* @param coord cpprd with X,Z
|
||||
* @return polar
|
||||
*/
|
||||
public static Polar fromCoordXZ(Coord coord)
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return fromCoord(coord.x, coord.z);
|
||||
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
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
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 mightypork.utils.math.Calc;
|
||||
import mightypork.utils.time.Updateable;
|
||||
|
||||
|
||||
/**
|
||||
@@ -12,13 +11,7 @@ import mightypork.utils.time.Updateable;
|
||||
*
|
||||
* @author MightyPork
|
||||
*/
|
||||
public class Coord implements Updateable {
|
||||
|
||||
/** Coord [1;1;1] */
|
||||
public static final Coord ONE = new Coord(1, 1, 1);
|
||||
|
||||
/** Zero Coord */
|
||||
public static final Coord ZERO = new Coord(0, 0);
|
||||
public class Coord {
|
||||
|
||||
/** RNG */
|
||||
protected static Random rand = new Random();
|
||||
@@ -63,14 +56,6 @@ public class Coord implements Updateable {
|
||||
(rand.nextBoolean() ? -1 : 1) * (min + rand.nextDouble() * (max - min)));
|
||||
}
|
||||
|
||||
private double animTime = 0;
|
||||
|
||||
private Vec offs;
|
||||
|
||||
private Coord start;
|
||||
|
||||
private double time = 0;
|
||||
|
||||
/** X coordinate */
|
||||
public double x = 0;
|
||||
|
||||
@@ -130,7 +115,7 @@ public class Coord implements Updateable {
|
||||
*/
|
||||
public Coord add(Coord vec)
|
||||
{
|
||||
return copy().add_ip(vec);
|
||||
return getCopy().add_ip(vec);
|
||||
}
|
||||
|
||||
|
||||
@@ -143,7 +128,7 @@ public class Coord implements Updateable {
|
||||
*/
|
||||
public Coord add(Number x, Number y)
|
||||
{
|
||||
return copy().add_ip(x, y);
|
||||
return getCopy().add_ip(x, y);
|
||||
}
|
||||
|
||||
|
||||
@@ -157,7 +142,7 @@ public class Coord implements Updateable {
|
||||
*/
|
||||
public Coord add(Number x, Number y, Number z)
|
||||
{
|
||||
return copy().add_ip(x, y, z);
|
||||
return getCopy().add_ip(x, y, z);
|
||||
}
|
||||
|
||||
|
||||
@@ -208,25 +193,10 @@ public class Coord implements Updateable {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Start animation
|
||||
*
|
||||
* @param time anim length
|
||||
*/
|
||||
public void animate(double time)
|
||||
{
|
||||
if (start == null) start = new Coord();
|
||||
if (offs == null) offs = new Vec();
|
||||
this.time = time;
|
||||
animTime = 0;
|
||||
offs = start.vecTo(this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return copy of this vector
|
||||
*/
|
||||
public Coord copy()
|
||||
public Coord getCopy()
|
||||
{
|
||||
return new Coord(x, y, z);
|
||||
}
|
||||
@@ -252,7 +222,7 @@ public class Coord implements Updateable {
|
||||
*/
|
||||
public Coord div(double d)
|
||||
{
|
||||
return copy().div_ip(d);
|
||||
return getCopy().div_ip(d);
|
||||
}
|
||||
|
||||
|
||||
@@ -272,48 +242,6 @@ public class Coord implements Updateable {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (obj == null) return false;
|
||||
if (!obj.getClass().isAssignableFrom(Coord.class)) return false;
|
||||
Coord other = (Coord) obj;
|
||||
return x == other.x && y == other.y && z == other.z;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get current value (animated)
|
||||
*
|
||||
* @return curent value
|
||||
*/
|
||||
public Coord getDelta()
|
||||
{
|
||||
if (start == null) start = new Coord();
|
||||
if (offs == null) offs = new Vec();
|
||||
if (isFinished()) return this;
|
||||
return new Coord(start.add(offs.scale(animTime / time)));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Double.valueOf(x).hashCode() ^ Double.valueOf(y).hashCode() ^ Double.valueOf(z).hashCode();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get if animation is finished
|
||||
*
|
||||
* @return is finished
|
||||
*/
|
||||
public boolean isFinished()
|
||||
{
|
||||
return animTime >= time;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if this rectangle in inside a rectangular zone
|
||||
*
|
||||
@@ -347,7 +275,7 @@ public class Coord implements Updateable {
|
||||
*/
|
||||
public Coord midTo(Coord other)
|
||||
{
|
||||
return add(vecTo(other).scale(0.5));
|
||||
return add(vecTo(other).mul_ip(0.5));
|
||||
}
|
||||
|
||||
|
||||
@@ -359,7 +287,7 @@ public class Coord implements Updateable {
|
||||
*/
|
||||
public Coord mul(double d)
|
||||
{
|
||||
return copy().mul_ip(d);
|
||||
return getCopy().mul_ip(d);
|
||||
}
|
||||
|
||||
|
||||
@@ -373,7 +301,7 @@ public class Coord implements Updateable {
|
||||
*/
|
||||
public Coord mul(double xd, double yd, double zd)
|
||||
{
|
||||
return copy().mul_ip(xd, yd, zd);
|
||||
return getCopy().mul_ip(xd, yd, zd);
|
||||
}
|
||||
|
||||
|
||||
@@ -417,10 +345,9 @@ public class Coord implements Updateable {
|
||||
*/
|
||||
public Coord random_offset(double max)
|
||||
{
|
||||
Coord r = random(1);
|
||||
Vec v = new Vec(r);
|
||||
v.norm_ip(0.00001 + rand.nextDouble() * max);
|
||||
return copy().add_ip(v);
|
||||
Coord v = random(1).norm_ip(0.00001 + rand.nextDouble() * max);
|
||||
|
||||
return getCopy().add_ip(v);
|
||||
}
|
||||
|
||||
|
||||
@@ -433,7 +360,7 @@ public class Coord implements Updateable {
|
||||
*/
|
||||
public Coord random_offset(double min, double max)
|
||||
{
|
||||
return copy().add_ip(random(min, max));
|
||||
return getCopy().add_ip(random(min, max));
|
||||
}
|
||||
|
||||
|
||||
@@ -463,17 +390,6 @@ public class Coord implements Updateable {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Remember position (other changes will be for animation)
|
||||
*/
|
||||
public void remember()
|
||||
{
|
||||
if (start == null) start = new Coord();
|
||||
if (offs == null) offs = new Vec();
|
||||
start.setTo(this);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a copy with rounded coords
|
||||
*
|
||||
@@ -481,7 +397,7 @@ public class Coord implements Updateable {
|
||||
*/
|
||||
public Coord round()
|
||||
{
|
||||
return copy().round_ip();
|
||||
return getCopy().round_ip();
|
||||
}
|
||||
|
||||
|
||||
@@ -577,7 +493,7 @@ public class Coord implements Updateable {
|
||||
*/
|
||||
public Coord setX(Number x)
|
||||
{
|
||||
return copy().setX_ip(x);
|
||||
return getCopy().setX_ip(x);
|
||||
}
|
||||
|
||||
|
||||
@@ -602,7 +518,7 @@ public class Coord implements Updateable {
|
||||
*/
|
||||
public Coord setY(Number y)
|
||||
{
|
||||
return copy().setY_ip(y);
|
||||
return getCopy().setY_ip(y);
|
||||
}
|
||||
|
||||
|
||||
@@ -627,7 +543,7 @@ public class Coord implements Updateable {
|
||||
*/
|
||||
public Coord setZ(Number z)
|
||||
{
|
||||
return copy().setZ_ip(z);
|
||||
return getCopy().setZ_ip(z);
|
||||
}
|
||||
|
||||
|
||||
@@ -644,17 +560,6 @@ public class Coord implements Updateable {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get size
|
||||
*
|
||||
* @return size
|
||||
*/
|
||||
public double size()
|
||||
{
|
||||
return new Vec(this).size();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get a copy subtracted by vector
|
||||
*
|
||||
@@ -663,7 +568,7 @@ public class Coord implements Updateable {
|
||||
*/
|
||||
public Coord sub(Coord vec)
|
||||
{
|
||||
return copy().sub_ip(vec);
|
||||
return getCopy().sub_ip(vec);
|
||||
}
|
||||
|
||||
|
||||
@@ -676,7 +581,7 @@ public class Coord implements Updateable {
|
||||
*/
|
||||
public Coord sub(Number x, Number y)
|
||||
{
|
||||
return copy().sub_ip(x, y);
|
||||
return getCopy().sub_ip(x, y);
|
||||
}
|
||||
|
||||
|
||||
@@ -690,7 +595,7 @@ public class Coord implements Updateable {
|
||||
*/
|
||||
public Coord sub(Number x, Number y, Number z)
|
||||
{
|
||||
return copy().sub_ip(x, y, z);
|
||||
return getCopy().sub_ip(x, y, z);
|
||||
}
|
||||
|
||||
|
||||
@@ -741,52 +646,15 @@ public class Coord implements Updateable {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Convert X and Y coordinates of this coord to a new CoordI.
|
||||
*
|
||||
* @return the new CoordI
|
||||
*/
|
||||
public CoordI toCoordI()
|
||||
{
|
||||
return new CoordI((int) Math.round(x), (int) Math.round(y));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "[ " + x + " ; " + y + " ; " + z + " ]";
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update delta timing
|
||||
*
|
||||
* @param delta delta time to add
|
||||
*/
|
||||
@Override
|
||||
public void update(double delta)
|
||||
{
|
||||
if (start == null) start = new Coord();
|
||||
if (offs == null) offs = new Vec();
|
||||
animTime = Calc.clampd(animTime + delta, 0, time);
|
||||
if (isFinished()) {
|
||||
time = 0;
|
||||
animTime = 0;
|
||||
start.setTo(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create vector from this point to other point
|
||||
*
|
||||
* @param point second point
|
||||
* @return vector
|
||||
*/
|
||||
public Vec vecTo(Coord point)
|
||||
public Coord vecTo(Coord point)
|
||||
{
|
||||
return (Vec) (new Vec(point)).add(new Vec(this).neg());
|
||||
return point.sub(this);
|
||||
}
|
||||
|
||||
|
||||
@@ -896,4 +764,213 @@ public class Coord implements Updateable {
|
||||
{
|
||||
return (int) Math.round(z);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get cross product of two vectors
|
||||
*
|
||||
* @param a 1st vector
|
||||
* @param b 2nd vector
|
||||
* @return cross product
|
||||
*/
|
||||
public static Coord cross(Coord a, Coord b)
|
||||
{
|
||||
return a.cross(b);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get dot product of two vectors
|
||||
*
|
||||
* @param a 1st vector
|
||||
* @param b 2nd vector
|
||||
* @return dot product
|
||||
*/
|
||||
public static double dot(Coord a, Coord b)
|
||||
{
|
||||
return a.dot(b);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Multiply by other vector, vector multiplication
|
||||
*
|
||||
* @param vec other vector
|
||||
* @return copy multiplied
|
||||
*/
|
||||
public Coord cross(Coord vec)
|
||||
{
|
||||
return getCopy().cross_ip(vec);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Multiply by other vector, vector multiplication; in place
|
||||
*
|
||||
* @param vec other vector
|
||||
* @return this
|
||||
*/
|
||||
public Coord cross_ip(Coord vec)
|
||||
{
|
||||
setTo(y * vec.z - z * vec.y, z * vec.x - x * vec.z, x * vec.y - y * vec.x);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get dot product
|
||||
*
|
||||
* @param vec other vector
|
||||
* @return dot product
|
||||
*/
|
||||
public double dot(Coord vec)
|
||||
{
|
||||
return x * vec.x + y * vec.y + z * vec.z;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Negate all coordinates (* -1)
|
||||
*
|
||||
* @return negated coordinate
|
||||
*/
|
||||
public Coord neg()
|
||||
{
|
||||
return getCopy().neg_ip();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Negate all coordinates (* -1), in place
|
||||
*
|
||||
* @return this
|
||||
*/
|
||||
public Coord neg_ip()
|
||||
{
|
||||
mul_ip(-1);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Scale vector to given size
|
||||
*
|
||||
* @param size size we need
|
||||
* @return scaled vector
|
||||
*/
|
||||
public Coord norm(double size)
|
||||
{
|
||||
return getCopy().norm_ip(size);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Scale vector to given size, in place
|
||||
*
|
||||
* @param size size we need
|
||||
* @return scaled vector
|
||||
*/
|
||||
public Coord norm_ip(double size)
|
||||
{
|
||||
if (size() == 0) {
|
||||
z = -1;
|
||||
}
|
||||
if (size == 0) {
|
||||
setTo(0, 0, 0);
|
||||
return this;
|
||||
}
|
||||
double k = size / size();
|
||||
mul_ip(k);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get vector size
|
||||
*
|
||||
* @return vector size in units
|
||||
*/
|
||||
public double size()
|
||||
{
|
||||
return Math.sqrt(x * x + y * y + z * z);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get copy divided by two
|
||||
* @return copy halved
|
||||
*/
|
||||
public Coord half()
|
||||
{
|
||||
return getCopy().half_ip();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Divide in place by two
|
||||
* @return this
|
||||
*/
|
||||
public Coord half_ip()
|
||||
{
|
||||
mul_ip(0.5);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "[" + x + ", " + y + ", " + z + "]";
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
long temp;
|
||||
temp = Double.doubleToLongBits(x);
|
||||
result = prime * result + (int) (temp ^ (temp >>> 32));
|
||||
temp = Double.doubleToLongBits(y);
|
||||
result = prime * result + (int) (temp ^ (temp >>> 32));
|
||||
temp = Double.doubleToLongBits(z);
|
||||
result = prime * result + (int) (temp ^ (temp >>> 32));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
if (this == obj) return true;
|
||||
if (obj == null) return false;
|
||||
if (!(obj instanceof Coord)) return false;
|
||||
Coord other = (Coord) obj;
|
||||
if (Double.doubleToLongBits(x) != Double.doubleToLongBits(other.x)) return false;
|
||||
if (Double.doubleToLongBits(y) != Double.doubleToLongBits(other.y)) return false;
|
||||
if (Double.doubleToLongBits(z) != Double.doubleToLongBits(other.z)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return true if this coord is a zero coord
|
||||
*/
|
||||
public boolean isZero()
|
||||
{
|
||||
return x == 0 && y == 0 && z == 0;
|
||||
}
|
||||
|
||||
|
||||
public static Coord zero()
|
||||
{
|
||||
return new Coord(0, 0, 0);
|
||||
}
|
||||
|
||||
|
||||
public static Coord one()
|
||||
{
|
||||
return new Coord(1, 1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
* @return the new rect
|
||||
*/
|
||||
public static Rect fromSize(Coord min, int width, int height)
|
||||
public static Rect fromSize(Coord min, double width, double height)
|
||||
{
|
||||
return new Rect(min, min.add(width, height));
|
||||
}
|
||||
@@ -47,37 +47,37 @@ public class Rect {
|
||||
/**
|
||||
* Rectangle from size
|
||||
*
|
||||
* @param i min X
|
||||
* @param j min Y
|
||||
* @param x min X
|
||||
* @param y min Y
|
||||
* @param size rect size
|
||||
* @return the rect
|
||||
*/
|
||||
public static Rect fromSize(int i, int j, CoordI size)
|
||||
public static Rect fromSize(int x, int y, Coord size)
|
||||
{
|
||||
return fromSize(i, j, size.x, size.y);
|
||||
return fromSize(x, y, size.x, size.y);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make rect from min coord and size
|
||||
*
|
||||
* @param x1 min x
|
||||
* @param y1 min y
|
||||
* @param xMin min x
|
||||
* @param yMin min y
|
||||
* @param width size x
|
||||
* @param height size y
|
||||
* @return the new rect
|
||||
*/
|
||||
public static Rect fromSize(int x1, int y1, int width, int height)
|
||||
public static Rect fromSize(double xMin, double yMin, double width, double height)
|
||||
{
|
||||
return new Rect(x1, y1, x1 + width, y1 + height);
|
||||
return new Rect(xMin, yMin, xMin + width, yMin + height);
|
||||
}
|
||||
|
||||
/** Highest coordinates xy */
|
||||
protected Coord max = new Coord();
|
||||
|
||||
/** Lowest coordinates xy */
|
||||
protected Coord min = new Coord();
|
||||
|
||||
/** Highest coordinates xy */
|
||||
protected Coord max = new Coord();
|
||||
|
||||
|
||||
/**
|
||||
* New Rect [0, 0, 0, 0]
|
||||
@@ -127,7 +127,7 @@ public class Rect {
|
||||
* @param x width
|
||||
* @param y height
|
||||
*/
|
||||
public Rect(int x, int y) {
|
||||
public Rect(double x, double y) {
|
||||
this(0, 0, x, y);
|
||||
}
|
||||
|
||||
@@ -163,7 +163,7 @@ public class Rect {
|
||||
*/
|
||||
public Rect add(double x, double y)
|
||||
{
|
||||
return add(new Vec(x, y));
|
||||
return add(new Coord(x, y));
|
||||
}
|
||||
|
||||
|
||||
@@ -190,7 +190,7 @@ public class Rect {
|
||||
*/
|
||||
public Rect add_ip(double x, double y)
|
||||
{
|
||||
return add_ip(new Vec(x, y));
|
||||
return add_ip(new Coord(x, y));
|
||||
}
|
||||
|
||||
|
||||
@@ -718,7 +718,7 @@ public class Rect {
|
||||
*/
|
||||
public Rect sub(double x, double y)
|
||||
{
|
||||
return sub(new Vec(x, y));
|
||||
return sub(new Coord(x, y));
|
||||
}
|
||||
|
||||
|
||||
@@ -728,7 +728,7 @@ public class Rect {
|
||||
* @param move offset vector
|
||||
* @return offset copy
|
||||
*/
|
||||
public Rect sub(Vec move)
|
||||
public Rect sub(Coord move)
|
||||
{
|
||||
return copy().sub_ip(move);
|
||||
}
|
||||
@@ -743,7 +743,7 @@ public class Rect {
|
||||
*/
|
||||
public Rect sub_ip(double x, double y)
|
||||
{
|
||||
return sub_ip(new Vec(x, y));
|
||||
return sub_ip(new Coord(x, y));
|
||||
}
|
||||
|
||||
|
||||
@@ -753,7 +753,7 @@ public class Rect {
|
||||
* @param move offset vector
|
||||
* @return this
|
||||
*/
|
||||
public Rect sub_ip(Vec move)
|
||||
public Rect sub_ip(Coord move)
|
||||
{
|
||||
min.sub_ip(move);
|
||||
max.sub_ip(move);
|
||||
@@ -771,7 +771,7 @@ public class Rect {
|
||||
/**
|
||||
* @return lower x
|
||||
*/
|
||||
public double x1()
|
||||
public double xMin()
|
||||
{
|
||||
return min.x;
|
||||
}
|
||||
@@ -780,7 +780,7 @@ public class Rect {
|
||||
/**
|
||||
* @return upper x
|
||||
*/
|
||||
public double x2()
|
||||
public double xMax()
|
||||
{
|
||||
return max.x;
|
||||
}
|
||||
@@ -789,7 +789,7 @@ public class Rect {
|
||||
/**
|
||||
* @return lower y
|
||||
*/
|
||||
public double y1()
|
||||
public double yMin()
|
||||
{
|
||||
return min.y;
|
||||
}
|
||||
@@ -798,7 +798,7 @@ public class Rect {
|
||||
/**
|
||||
* @return upper y
|
||||
*/
|
||||
public double y2()
|
||||
public double yMax()
|
||||
{
|
||||
return max.y;
|
||||
}
|
||||
|
||||
@@ -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.math.Range;
|
||||
import mightypork.utils.math.coord.Coord;
|
||||
import mightypork.utils.math.coord.CoordI;
|
||||
|
||||
|
||||
/**
|
||||
@@ -154,7 +153,6 @@ public class Convertor {
|
||||
return new Coord(Double.parseDouble(parts[0].trim()), Double.parseDouble(parts[1].trim()));
|
||||
}
|
||||
if (o instanceof Coord) return new Coord((Coord) o);
|
||||
if (o instanceof CoordI) return ((CoordI) o).toCoord();
|
||||
} catch (NumberFormatException e) {
|
||||
// ignore
|
||||
}
|
||||
@@ -267,7 +265,7 @@ public class Convertor {
|
||||
*/
|
||||
public static Coord getCoord(Object o)
|
||||
{
|
||||
return getCoord(o, Coord.ZERO.copy());
|
||||
return getCoord(o, Coord.zero());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -10,17 +10,11 @@ package mightypork.utils.objects;
|
||||
public class Mutable<T> {
|
||||
|
||||
/** The wrapped value */
|
||||
public T o = null;
|
||||
private T o = null;
|
||||
|
||||
|
||||
/**
|
||||
* Implicint constructor
|
||||
*/
|
||||
public Mutable() {}
|
||||
|
||||
|
||||
/**
|
||||
* new mutable object
|
||||
* New mutable object
|
||||
*
|
||||
* @param o value
|
||||
*/
|
||||
@@ -49,4 +43,38 @@ public class Mutable<T> {
|
||||
{
|
||||
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.easing.Easing;
|
||||
|
||||
|
||||
/**
|
||||
@@ -12,10 +13,10 @@ import mightypork.utils.math.Calc;
|
||||
public class AnimDouble implements Updateable, Pauseable {
|
||||
|
||||
/** target double */
|
||||
protected double endValue = 0;
|
||||
protected double to = 0;
|
||||
|
||||
/** last tick double */
|
||||
protected double startValue = 0;
|
||||
protected double from = 0;
|
||||
|
||||
/** how long the transition should last */
|
||||
protected double duration = 0;
|
||||
@@ -26,28 +27,79 @@ public class AnimDouble implements Updateable, Pauseable {
|
||||
/** True if this animator is paused */
|
||||
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) {
|
||||
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) {
|
||||
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
|
||||
*
|
||||
* @return number
|
||||
*/
|
||||
public double getStartValue()
|
||||
public double getFrom()
|
||||
{
|
||||
return startValue;
|
||||
return from;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get end value
|
||||
*
|
||||
* @return number
|
||||
*/
|
||||
public double getTo()
|
||||
{
|
||||
return to;
|
||||
}
|
||||
|
||||
|
||||
@@ -58,19 +110,8 @@ public class AnimDouble implements Updateable, Pauseable {
|
||||
*/
|
||||
public double getCurrentValue()
|
||||
{
|
||||
if (duration == 0) return endValue;
|
||||
return Calc.interpolate(startValue, endValue, elapsedTime / duration);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get end value
|
||||
*
|
||||
* @return number
|
||||
*/
|
||||
public double getEndValue()
|
||||
{
|
||||
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
|
||||
public void update(double delta)
|
||||
{
|
||||
if (paused) return;
|
||||
|
||||
elapsedTime = Calc.clampd(elapsedTime + delta, 0, duration);
|
||||
if (isFinished()) {
|
||||
duration = 0;
|
||||
elapsedTime = 0;
|
||||
startValue = endValue;
|
||||
from = to;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,7 +159,7 @@ public class AnimDouble implements Updateable, Pauseable {
|
||||
*/
|
||||
public void setTo(double value)
|
||||
{
|
||||
startValue = endValue = value;
|
||||
from = to = value;
|
||||
elapsedTime = 0;
|
||||
duration = 0;
|
||||
}
|
||||
@@ -129,27 +172,12 @@ public class AnimDouble implements Updateable, Pauseable {
|
||||
*/
|
||||
public void setTo(AnimDouble other)
|
||||
{
|
||||
this.startValue = other.startValue;
|
||||
this.endValue = other.endValue;
|
||||
this.from = other.from;
|
||||
this.to = other.to;
|
||||
this.duration = other.duration;
|
||||
this.elapsedTime = other.elapsedTime;
|
||||
this.paused = other.paused;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Animate between two states, discard current state
|
||||
*
|
||||
* @param from initial state
|
||||
* @param to target state
|
||||
* @param time animation time (secs)
|
||||
*/
|
||||
public void animate(double from, double to, double time)
|
||||
{
|
||||
startValue = from;
|
||||
endValue = to;
|
||||
duration = time;
|
||||
elapsedTime = 0;
|
||||
this.easing = other.easing;
|
||||
}
|
||||
|
||||
|
||||
@@ -160,21 +188,45 @@ public class AnimDouble implements Updateable, Pauseable {
|
||||
* @param to target state
|
||||
* @param time animation time (secs)
|
||||
*/
|
||||
public void fadeTo(double from, double to, double time)
|
||||
public void animate(double from, double to, double time)
|
||||
{
|
||||
double current = getCurrentValue();
|
||||
|
||||
startValue = from;
|
||||
endValue = to;
|
||||
duration = time;
|
||||
elapsedTime = 0;
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
this.duration = time * (1 - getProgressFromValue(current));
|
||||
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;
|
||||
|
||||
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 to a value from curretn value
|
||||
*
|
||||
* @param to target state
|
||||
* @param time animation time (secs)
|
||||
*/
|
||||
public void fadeTo(double to, double time)
|
||||
{
|
||||
double current = getCurrentValue();
|
||||
|
||||
this.from = current;
|
||||
this.to = to;
|
||||
this.duration = time;
|
||||
this.elapsedTime = 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -185,7 +237,7 @@ public class AnimDouble implements Updateable, Pauseable {
|
||||
*/
|
||||
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)
|
||||
{
|
||||
fadeTo(1, 0, time);
|
||||
animate(1, 0, time);
|
||||
}
|
||||
|
||||
|
||||
@@ -214,7 +266,7 @@ public class AnimDouble implements Updateable, Pauseable {
|
||||
@Override
|
||||
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()
|
||||
{
|
||||
startValue = endValue = 0;
|
||||
from = to = 0;
|
||||
elapsedTime = 0;
|
||||
duration = 0;
|
||||
paused = false;
|
||||
@@ -235,7 +287,7 @@ public class AnimDouble implements Updateable, Pauseable {
|
||||
*/
|
||||
public void stop()
|
||||
{
|
||||
startValue = endValue = getCurrentValue();
|
||||
from = to = getCurrentValue();
|
||||
elapsedTime = 0;
|
||||
duration = 0;
|
||||
}
|
||||
|
||||
@@ -2,40 +2,49 @@ package mightypork.utils.time;
|
||||
|
||||
|
||||
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
|
||||
*/
|
||||
public class AnimDoubleDeg extends AnimDouble {
|
||||
|
||||
/**
|
||||
* new AnimDoubleDeg
|
||||
*
|
||||
* @param d value
|
||||
*/
|
||||
public AnimDoubleDeg(double d) {
|
||||
super(d);
|
||||
public AnimDoubleDeg(AnimDouble other) {
|
||||
super(other);
|
||||
}
|
||||
|
||||
|
||||
public AnimDoubleDeg(double value) {
|
||||
super(value);
|
||||
}
|
||||
|
||||
|
||||
public AnimDoubleDeg(double value, Easing easing) {
|
||||
super(value, easing);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get value at delta time
|
||||
*
|
||||
* @return the value
|
||||
*/
|
||||
@Override
|
||||
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
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user