parent
6b8891666a
commit
1af5f01520
@ -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); |
||||
} |
||||
} |
@ -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(); |
||||
} |
||||
} |
@ -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(); |
||||
} |
||||
} |
@ -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(); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -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); |
||||
} |
||||
} |
@ -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 + " ]"; |
||||
} |
||||
|
||||
} |
@ -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; |
||||
} |
||||
}; |
||||
} |
@ -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); |
||||
} |
@ -0,0 +1,50 @@ |
||||
package mightypork.utils.time; |
||||
|
||||
|
||||
import mightypork.utils.math.Calc; |
||||
import mightypork.utils.math.Calc.Rad; |
||||
import mightypork.utils.math.easing.Easing; |
||||
|
||||
|
||||
/** |
||||
* Radians animator |
||||
* |
||||
* @author MightyPork |
||||
*/ |
||||
public class AnimDoubleRad extends AnimDouble { |
||||
|
||||
public AnimDoubleRad(AnimDouble other) { |
||||
super(other); |
||||
} |
||||
|
||||
|
||||
public AnimDoubleRad(double value) { |
||||
super(value); |
||||
} |
||||
|
||||
|
||||
public AnimDoubleRad(double value, Easing easing) { |
||||
super(value, easing); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
public double getCurrentValue() |
||||
{ |
||||
if (duration == 0) return Rad.norm(to); |
||||
return Calc.interpolateRad(from, to, (elapsedTime / duration), easing); |
||||
} |
||||
|
||||
|
||||
@Override |
||||
protected double getProgressFromValue(double value) |
||||
{ |
||||
double whole = Rad.diff(from, to); |
||||
if (Rad.diff(value, from) < whole && Rad.diff(value, to) < whole) { |
||||
double partial = Rad.diff(from, value); |
||||
return partial / whole; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
} |
Loading…
Reference in new issue