Removed junk & added Easing, Event system and others

This commit is contained in:
Ondřej Hruška
2014-03-30 11:10:43 +02:00
parent 6b8891666a
commit 1af5f01520
57 changed files with 3618 additions and 2535 deletions
+212 -247
View File
@@ -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;
}
}
+2 -7
View File
@@ -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;
}
+1
View File
@@ -9,6 +9,7 @@ public class CrashHandler implements UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread t, Throwable e)
{
e.printStackTrace();
App.onCrash(e);
}
-200
View File
@@ -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);
}
}
+214
View File
@@ -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);
}
}
+91 -91
View File
@@ -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();
}
+126
View File
@@ -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));
}
}
+21
View File
@@ -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);
}
}
}
+114
View File
@@ -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;
}
}
-122
View File
@@ -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();
}
}
+144 -89
View File
@@ -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);
}
}
+11 -11
View File
@@ -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);
}
+3 -3
View File
@@ -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();
}
}
}
}
+62 -32
View File
@@ -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);
}
}
+5
View File
@@ -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;
}
}