EventBus now using thread; +Textures; +Audio; Cleaner structure

v5stable
Ondřej Hruška 10 years ago
parent 7f35d1d316
commit 53b7b02609
  1. 171
      eclipse-config.epf
  2. BIN
      org.eclipse.core.runtime.zip
  3. BIN
      res/audio/shutter.ogg
  4. BIN
      res/audio/wilderness.ogg
  5. BIN
      res/font/PolygonPixel5x7Standard.ttf
  6. BIN
      res/font/PressStart2P.ttf
  7. BIN
      res/img/kitten.png
  8. 236
      src/mightypork/rogue/App.java
  9. 11
      src/mightypork/rogue/AppAccess.java
  10. 12
      src/mightypork/rogue/AppAdapter.java
  11. 1
      src/mightypork/rogue/Config.java
  12. 3
      src/mightypork/rogue/Const.java
  13. 21
      src/mightypork/rogue/Deferred.java
  14. 112
      src/mightypork/rogue/MainLoop.java
  15. 87
      src/mightypork/rogue/Res.java
  16. 33
      src/mightypork/rogue/Resources.java
  17. 14
      src/mightypork/rogue/audio/BaseAudioPlayer.java
  18. 43
      src/mightypork/rogue/audio/DeferredAudio.java
  19. 8
      src/mightypork/rogue/audio/EffectPlayer.java
  20. 2
      src/mightypork/rogue/audio/JointVolume.java
  21. 18
      src/mightypork/rogue/audio/LoopPlayer.java
  22. 30
      src/mightypork/rogue/audio/NullAudio.java
  23. 90
      src/mightypork/rogue/audio/SoundBank.java
  24. 157
      src/mightypork/rogue/audio/SoundSystem.java
  25. 5
      src/mightypork/rogue/bus/ChildClient.java
  26. 33
      src/mightypork/rogue/bus/events/ActionRequest.java
  27. 4
      src/mightypork/rogue/bus/events/KeyboardEvent.java
  28. 4
      src/mightypork/rogue/bus/events/MouseButtonEvent.java
  29. 6
      src/mightypork/rogue/bus/events/MouseMotionEvent.java
  30. 7
      src/mightypork/rogue/bus/events/RequestType.java
  31. 4
      src/mightypork/rogue/bus/events/ScreenChangeEvent.java
  32. 28
      src/mightypork/rogue/bus/events/ScreenRequestEvent.java
  33. 22
      src/mightypork/rogue/display/screens/ScreenRegistry.java
  34. 6
      src/mightypork/rogue/display/screens/screenTextures/ScreenTextureTest.java
  35. 2
      src/mightypork/rogue/gui/constraints/ElementHolder.java
  36. 2
      src/mightypork/rogue/gui/constraints/Renderable.java
  37. 2
      src/mightypork/rogue/gui/constraints/RenderableWithContext.java
  38. 2
      src/mightypork/rogue/gui/screens/LayeredScreen.java
  39. 119
      src/mightypork/rogue/gui/screens/Screen.java
  40. 4
      src/mightypork/rogue/gui/screens/ScreenLayer.java
  41. 55
      src/mightypork/rogue/gui/screens/ScreenRegistry.java
  42. 8
      src/mightypork/rogue/gui/screens/screenBouncy/BouncyBox.java
  43. 8
      src/mightypork/rogue/gui/screens/screenBouncy/LayerBouncyBoxes.java
  44. 8
      src/mightypork/rogue/gui/screens/screenBouncy/ScreenTestAnim.java
  45. 104
      src/mightypork/rogue/gui/screens/screenTextures/ScreenTextureTest.java
  46. 56
      src/mightypork/rogue/input/InputSystem.java
  47. 17
      src/mightypork/rogue/render/DisplaySystem.java
  48. 349
      src/mightypork/rogue/render/Render.java
  49. 21
      src/mightypork/rogue/render/textures/MultiTexture.java
  50. 23
      src/mightypork/rogue/render/textures/TextureBank.java
  51. 91
      src/mightypork/rogue/render/textures/TxQuad.java
  52. 17
      src/mightypork/rogue/sounds/NullAudio.java
  53. 4
      src/mightypork/rogue/tasks/TaskTakeScreenshot.java
  54. 1032
      src/mightypork/rogue/textures/Render.java
  55. 70
      src/mightypork/rogue/textures/TxQuad.java
  56. 130
      src/mightypork/utils/control/bus/BufferedHashSet.java
  57. 2
      src/mightypork/utils/control/bus/Event.java
  58. 236
      src/mightypork/utils/control/bus/EventBus.java
  59. 101
      src/mightypork/utils/control/bus/EventChannel.java
  60. 4
      src/mightypork/utils/control/bus/events/DestroyEvent.java
  61. 4
      src/mightypork/utils/control/bus/events/UpdateEvent.java
  62. 1
      src/mightypork/utils/control/timing/TimerFps.java
  63. 31
      src/mightypork/utils/logging/Log.java
  64. 10
      src/mightypork/utils/math/animation/AnimDouble.java
  65. 2
      src/mightypork/utils/math/animation/AnimDoubleDeg.java
  66. 2
      src/mightypork/utils/math/animation/AnimDoubleRad.java
  67. 21
      src/mightypork/utils/math/constraints/ConstraintFactory.java
  68. 26
      src/mightypork/utils/math/coord/Rect.java

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

@ -4,22 +4,21 @@ package mightypork.rogue;
import java.io.File;
import java.io.RandomAccessFile;
import java.nio.channels.FileLock;
import java.util.ArrayList;
import javax.swing.JOptionPane;
import mightypork.rogue.display.DisplaySystem;
import mightypork.rogue.display.Screen;
import mightypork.rogue.display.screens.screenBouncy.TestLayeredScreen;
import mightypork.rogue.audio.SoundSystem;
import mightypork.rogue.bus.events.*;
import mightypork.rogue.gui.screens.ScreenRegistry;
import mightypork.rogue.input.InputSystem;
import mightypork.rogue.input.KeyStroke;
import mightypork.rogue.sounds.SoundSystem;
import mightypork.rogue.tasks.TaskTakeScreenshot;
import mightypork.rogue.textures.TextureRegistry;
import mightypork.rogue.util.Utils;
import mightypork.rogue.render.DisplaySystem;
import mightypork.utils.control.bus.EventBus;
import mightypork.utils.control.bus.events.DestroyEvent;
import mightypork.utils.control.bus.events.UpdateEvent;
import mightypork.utils.control.timing.TimerDelta;
import mightypork.utils.control.interf.Destroyable;
import mightypork.utils.control.interf.Updateable;
import mightypork.utils.logging.Log;
import mightypork.utils.logging.LogInstance;
@ -38,22 +37,12 @@ public class App implements AppAccess {
// modules
private InputSystem inputSystem;
private SoundSystem soundSystem;
private DisplaySystem displaySystem;
private static SoundSystem soundSystem;
private EventBus eventBus;
private TextureRegistry textureRegistry;
private MainLoop mainLoop;
/** current screen */
private Screen screen;
/** Flag that screenshot is scheduled to be taken next loop */
private boolean scheduledScreenshot = false;
/** Log instance; accessible as static via Log. */
private LogInstance log;
/** timer */
private TimerDelta timerRender;
public ScreenRegistry screens;
/**
@ -82,34 +71,9 @@ public class App implements AppAccess {
private void start()
{
initialize();
mainLoop();
shutdown();
}
/**
* App main loop
*/
private void mainLoop()
{
screen = new TestLayeredScreen(this);
screen.setActive(true);
timerRender = new TimerDelta();
while (!displaySystem.isCloseRequested()) {
displaySystem.beginFrame();
eventBus.broadcast(new UpdateEvent(timerRender.getDelta()));
if (scheduledScreenshot) {
takeScreenshot();
scheduledScreenshot = false;
}
displaySystem.endFrame();
}
Log.i("Starting main loop...");
mainLoop.start();
}
@ -120,7 +84,7 @@ public class App implements AppAccess {
*/
public static void onCrash(Throwable error)
{
Log.e("The game has crashed.", error);
Log.e("The game has crashed!", error);
if (inst != null) inst.shutdown();
}
@ -129,7 +93,11 @@ public class App implements AppAccess {
@Override
public void shutdown()
{
bus().broadcast(new DestroyEvent());
bus().send(new DestroyEvent());
bus().destroy();
Log.i("Shutting down...");
System.exit(0);
}
@ -137,34 +105,124 @@ public class App implements AppAccess {
public void initialize()
{
Log.i("Initializing subsystems");
// lock working directory
/*
* Lock working directory
*/
initLock();
// setup logging
log = Log.create("runtime", Paths.LOGS, 10);
/*
* Setup logging
*/
LogInstance log = Log.create("runtime", Paths.LOGS, 10);
log.enable(Config.LOGGING_ENABLED);
log.enableSysout(Config.LOG_TO_STDOUT);
// event bus
eventBus = new EventBus();
Log.f1("Initializing subsystems...");
// Subsystems
textureRegistry = new TextureRegistry(this);
/*
* Event bus
*/
Log.f2("Initializing Event Bus...");
eventBus = new EventBus();
eventBus.enableLogging(Config.LOG_BUS);
initChannels();
/*
* Display
*/
Log.f2("Initializing Display System...");
displaySystem = new DisplaySystem(this);
displaySystem.createMainWindow(Const.WINDOW_W, Const.WINDOW_H, true, Config.START_IN_FS, Const.TITLEBAR);
displaySystem.setTargetFps(Const.FPS_RENDER);
/*
* Audio
*/
Log.f2("Initializing Sound System...");
soundSystem = new SoundSystem(this);
soundSystem.setMasterVolume(1);
/*
* Input
*/
Log.f2("Initializing Input System...");
inputSystem = new InputSystem(this);
setupGlobalKeystrokes();
// load resources
Resources.load(this);
/*
* Screen registry
*/
Log.f2("Initializing screen registry...");
screens = new ScreenRegistry(this);
/*
* Load resources
*/
Log.f1("Registering resources...");
Res.load(this);
/*
* Prepare main loop
*/
Log.f1("Preparing main loop...");
ArrayList<Runnable> loopTasks = new ArrayList<Runnable>();
loopTasks.add(new Runnable() {
@Override
public void run()
{
screens.render();
}
});
mainLoop = new MainLoop(this, loopTasks);
}
private void initChannels()
{
Log.f3("Registering channels...");
bus().addChannel(DestroyEvent.class, Destroyable.class);
bus().addChannel(UpdateEvent.class, Updateable.class);
bus().addChannel(ScreenChangeEvent.class, ScreenChangeEvent.Listener.class);
bus().addChannel(KeyboardEvent.class, KeyboardEvent.Listener.class);
bus().addChannel(MouseMotionEvent.class, MouseMotionEvent.Listener.class);
bus().addChannel(MouseButtonEvent.class, MouseButtonEvent.Listener.class);
bus().addChannel(ScreenRequestEvent.class, ScreenRequestEvent.Listener.class);
bus().addChannel(ActionRequest.class, ActionRequest.Listener.class);
}
private void setupGlobalKeystrokes()
{
input().bindKeyStroke(new KeyStroke(Keyboard.KEY_F11), new Runnable() {
@Override
public void run()
{
bus().queue(new ActionRequest(RequestType.FULLSCREEN));
}
});
input().bindKeyStroke(new KeyStroke(Keyboard.KEY_F2), new Runnable() {
@Override
public void run()
{
bus().queue(new ActionRequest(RequestType.SCREENSHOT));
}
});
input().bindKeyStroke(new KeyStroke(Keyboard.KEY_LCONTROL, Keyboard.KEY_Q), new Runnable() {
@Override
public void run()
{
bus().queue(new ActionRequest(RequestType.SHUTDOWN));
}
});
}
@ -173,7 +231,7 @@ public class App implements AppAccess {
if (!Config.SINGLE_INSTANCE) return;
if (!lockInstance()) {
System.out.println("Working directory is locked.\nOnly one instance can run at a time.");
System.err.println("Working directory is locked.\nOnly one instance can run at a time.");
//@formatter:off
JOptionPane.showMessageDialog(
@ -222,53 +280,6 @@ public class App implements AppAccess {
}
/**
* initialize inputs
*/
private void setupGlobalKeystrokes()
{
inputSystem.bindKeyStroke(new KeyStroke(Keyboard.KEY_F2), new Runnable() {
@Override
public void run()
{
Log.f3("F2, taking screenshot.");
scheduledScreenshot = true;
}
});
inputSystem.bindKeyStroke(new KeyStroke(false, Keyboard.KEY_F11), new Runnable() {
@Override
public void run()
{
Log.f3("F11, toggling fullscreen.");
displaySystem.switchFullscreen();
}
});
inputSystem.bindKeyStroke(new KeyStroke(Keyboard.KEY_LCONTROL, Keyboard.KEY_Q), new Runnable() {
@Override
public void run()
{
Log.f3("CTRL+Q, shutting down.");
shutdown();
}
});
}
/**
* Do take a screenshot
*/
private void takeScreenshot()
{
soundSystem.getEffect("gui.shutter").play(1);
Utils.runAsThread(new TaskTakeScreenshot(displaySystem));
}
/**
* @return sound system of the running instance
*/
@ -308,11 +319,4 @@ public class App implements AppAccess {
return eventBus;
}
@Override
public TextureRegistry tx()
{
return textureRegistry;
}
}

@ -1,10 +1,9 @@
package mightypork.rogue;
import mightypork.rogue.display.DisplaySystem;
import mightypork.rogue.audio.SoundSystem;
import mightypork.rogue.input.InputSystem;
import mightypork.rogue.sounds.SoundSystem;
import mightypork.rogue.textures.TextureRegistry;
import mightypork.rogue.render.DisplaySystem;
import mightypork.utils.control.bus.EventBus;
@ -39,12 +38,6 @@ public interface AppAccess {
abstract EventBus bus();
/**
* @return texture registry
*/
abstract TextureRegistry tx();
/**
* Quit to OS<br>
* Destroy app & exit VM

@ -1,10 +1,9 @@
package mightypork.rogue;
import mightypork.rogue.display.DisplaySystem;
import mightypork.rogue.audio.SoundSystem;
import mightypork.rogue.input.InputSystem;
import mightypork.rogue.sounds.SoundSystem;
import mightypork.rogue.textures.TextureRegistry;
import mightypork.rogue.render.DisplaySystem;
import mightypork.utils.control.bus.EventBus;
@ -59,11 +58,4 @@ public class AppAdapter implements AppAccess {
app.shutdown();
}
@Override
public TextureRegistry tx()
{
return app.tx();
}
}

@ -77,5 +77,6 @@ public class Config {
public static boolean SINGLE_INSTANCE = true;
public static boolean LOG_FONTS = false;
public static boolean LOG_BUS = false;
}

@ -15,8 +15,7 @@ public class Const {
public static final String TITLEBAR = APP_NAME + " v." + VERSION;
// AUDIO
public static final int FPS_RENDER = 200; // max
public static final long FPS_GUI_UPDATE = 60;
public static final int FPS_RENDER = 80; // max
// INITIAL WINDOW SIZE
public static final int WINDOW_W = 1024;

@ -0,0 +1,21 @@
package mightypork.rogue;
/**
* Deferred resource
*
* @author MightyPork
*/
public interface Deferred {
/**
* Load the actual resource if not loaded yet
*/
public void load();
/**
* @return true if already loaded
*/
public boolean isLoaded();
}

@ -0,0 +1,112 @@
package mightypork.rogue;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import mightypork.rogue.bus.Subsystem;
import mightypork.rogue.bus.events.ActionRequest;
import mightypork.rogue.bus.events.RequestType;
import mightypork.rogue.bus.events.ScreenRequestEvent;
import mightypork.rogue.tasks.TaskTakeScreenshot;
import mightypork.rogue.util.Utils;
import mightypork.utils.control.bus.events.UpdateEvent;
import mightypork.utils.control.timing.TimerDelta;
public class MainLoop extends Subsystem implements ActionRequest.Listener {
private Queue<Runnable> taskQueue = new ConcurrentLinkedQueue<Runnable>();
private List<Runnable> regularTasks;
public MainLoop(App app, ArrayList<Runnable> loopTasks) {
super(app);
this.regularTasks = loopTasks;
}
/** timer */
private TimerDelta timer;
private boolean running = true;
public void start()
{
bus().queue(new ScreenRequestEvent("test.texture"));
timer = new TimerDelta();
while (running) {
disp().beginFrame();
bus().send(new UpdateEvent(timer.getDelta()));
for (Runnable r : regularTasks) {
r.run();
}
Runnable r;
while ((r = taskQueue.poll()) != null) {
r.run();
}
disp().endFrame();
}
}
@Override
protected void deinit()
{
running = false;
}
@Override
public void requestAction(RequestType request)
{
switch (request) {
case FULLSCREEN:
taskQueue.add(taskFullscreen);
break;
case SCREENSHOT:
taskQueue.add(taskScreenshot);
break;
case SHUTDOWN:
taskQueue.add(taskShutdown);
}
}
private final Runnable taskScreenshot = new Runnable() {
@Override
public void run()
{
Res.getEffect("gui.shutter").play(1);
Utils.runAsThread(new TaskTakeScreenshot(disp()));
}
};
private final Runnable taskShutdown = new Runnable() {
@Override
public void run()
{
shutdown();
}
};
private final Runnable taskFullscreen = new Runnable() {
@Override
public void run()
{
disp().switchFullscreen();
}
};
}

@ -0,0 +1,87 @@
package mightypork.rogue;
import mightypork.rogue.audio.EffectPlayer;
import mightypork.rogue.audio.LoopPlayer;
import mightypork.rogue.audio.SoundBank;
import mightypork.rogue.gui.screens.screenBouncy.ScreenTestAnim;
import mightypork.rogue.gui.screens.screenTextures.ScreenTextureTest;
import mightypork.rogue.render.textures.TextureBank;
import mightypork.rogue.render.textures.TxQuad;
import org.newdawn.slick.opengl.Texture;
public class Res {
private static TextureBank textures;
private static SoundBank sounds;
private static boolean initialized = false;
public static void load(App app)
{
if (initialized) return;
initialized = true;
textures = new TextureBank(app);
sounds = new SoundBank(app);
loadSounds(app);
loadTextures(app);
loadFonts(app);
loadScreens(app);
}
private static void loadFonts(App app)
{
}
private static void loadTextures(App app)
{
textures.loadTexture("test.kitten", "/res/img/kitten.png");
}
private static void loadSounds(App app)
{
sounds.addEffect("gui.shutter", "/res/audio/shutter.ogg", 1, 1);
sounds.addLoop("test.wilderness", "/res/audio/wilderness.ogg", 1, 1, 3, 3);
}
private static void loadScreens(App app)
{
app.screens.add("test.anim", new ScreenTestAnim(app));
app.screens.add("test.texture", new ScreenTextureTest(app));
}
public static TxQuad getTxQuad(String key)
{
return textures.getTxQuad(key);
}
public static Texture getTexture(String key)
{
return textures.getTexture(key);
}
public static LoopPlayer getLoop(String key)
{
return sounds.getLoop(key);
}
public static EffectPlayer getEffect(String key)
{
return sounds.getEffect(key);
}
}

@ -1,33 +0,0 @@
package mightypork.rogue;
public class Resources {
public static void load(AppAccess app)
{
loadSounds(app);
loadTextures(app);
loadFonts(app);
}
private static void loadFonts(AppAccess app)
{
}
private static void loadTextures(AppAccess app)
{
}
private static void loadSounds(AppAccess app)
{
}
}

@ -1,4 +1,4 @@
package mightypork.rogue.sounds;
package mightypork.rogue.audio;
import mightypork.utils.objects.Mutable;
@ -7,7 +7,7 @@ import mightypork.utils.objects.Mutable;
public abstract class BaseAudioPlayer {
/** the track */
private AudioX audio;
private DeferredAudio audio;
/** base gain for sfx */
private double baseGain = 1;
@ -19,12 +19,12 @@ public abstract class BaseAudioPlayer {
private Mutable<Double> gainMultiplier = null;
public BaseAudioPlayer(AudioX track, double baseGain, Mutable<Double> gainMultiplier) {
public BaseAudioPlayer(DeferredAudio track, double baseGain, Mutable<Double> gainMultiplier) {
this(track, 1, baseGain, gainMultiplier);
}
public BaseAudioPlayer(AudioX track, double basePitch, double baseGain, Mutable<Double> gainMultiplier) {
public BaseAudioPlayer(DeferredAudio track, double basePitch, double baseGain, Mutable<Double> gainMultiplier) {
this.audio = track;
this.baseGain = baseGain;
@ -43,7 +43,7 @@ public abstract class BaseAudioPlayer {
}
protected AudioX getAudio()
protected DeferredAudio getAudio()
{
return audio;
}
@ -66,7 +66,7 @@ public abstract class BaseAudioPlayer {
*
* @return is valid
*/
protected boolean canPlay()
protected boolean hasAudio()
{
return (audio != null);
}
@ -74,6 +74,6 @@ public abstract class BaseAudioPlayer {
public void load()
{
if (canPlay()) audio.load();
if (hasAudio()) audio.load();
}
}

@ -1,6 +1,7 @@
package mightypork.rogue.sounds;
package mightypork.rogue.audio;
import mightypork.rogue.Deferred;
import mightypork.utils.control.interf.Destroyable;
import mightypork.utils.files.FileUtils;
import mightypork.utils.logging.Log;
@ -15,7 +16,7 @@ import org.newdawn.slick.openal.SoundStore;
*
* @author MightyPork
*/
public class AudioX implements Destroyable {
public class DeferredAudio implements Destroyable, Deferred {
private enum PlayMode
{
@ -39,7 +40,7 @@ public class AudioX implements Destroyable {
*
* @param resourceName resource to load when needed
*/
public AudioX(String resourceName) {
public DeferredAudio(String resourceName) {
this.audio = null;
this.resourcePath = resourceName;
}
@ -50,7 +51,7 @@ public class AudioX implements Destroyable {
*/
public void pauseLoop()
{
if (!load()) return;
if (!ensureLoaded()) return;
if (isPlaying() && looping) {
pauseLoopPosition = audio.getPosition();
@ -67,7 +68,7 @@ public class AudioX implements Destroyable {
*/
public int resumeLoop()
{
if (!load()) return -1;
if (!ensureLoaded()) return -1;
int source = -1;
if (looping && paused) {
@ -88,24 +89,32 @@ public class AudioX implements Destroyable {
*
* @return resource is loaded
*/
private boolean isLoaded()
@Override
public boolean isLoaded()
{
return audio != null;
}
@Override
public void load()
{
ensureLoaded();
}
/**
* Try to load if not loaded already
*
* @return is loaded
*/
public boolean load()
protected boolean ensureLoaded()
{
if (isLoaded()) return true; // already loaded
if (loadFailed || resourcePath == null) return false; // not loaded, but
// can't load
// anyway
if (loadFailed || resourcePath == null) return false;
Log.f3("Trying to load: " + resourcePath);
loadFailed = false;
try {
String ext = FileUtils.getExtension(resourcePath);
@ -124,6 +133,8 @@ public class AudioX implements Destroyable {
loadFailed = true; // don't try next time
}
if (!loadFailed) Log.f3("Audio loaded: " + resourcePath);
} catch (Exception e) {
Log.e("Could not load " + resourcePath, e);
loadFailed = true; // don't try next time
@ -201,7 +212,7 @@ public class AudioX implements Destroyable {
*/
public int playAsEffect(double pitch, double gain, boolean loop, double x, double y, double z)
{
if (!load()) return -1;
if (!ensureLoaded()) return -1;
this.lastPlayPitch = pitch;
this.lastPlayGain = gain;
@ -222,7 +233,7 @@ public class AudioX implements Destroyable {
*/
public int playAsEffect(double pitch, double gain, boolean loop, Coord pos)
{
if (!load()) return -1;
if (!ensureLoaded()) return -1;
return playAsEffect(pitch, gain, loop, pos.x, pos.y, pos.z);
}
@ -239,7 +250,7 @@ public class AudioX implements Destroyable {
*/
public int playAsMusic(double pitch, double gain, boolean loop)
{
if (!load()) return -1;
if (!ensureLoaded()) return -1;
this.lastPlayPitch = (float) pitch;
this.lastPlayGain = (float) gain;
@ -274,8 +285,8 @@ public class AudioX implements Destroyable {
{
if (this == obj) return true;
if (obj == null) return false;
if (!(obj instanceof AudioX)) return false;
AudioX other = (AudioX) obj;
if (!(obj instanceof DeferredAudio)) return false;
DeferredAudio other = (DeferredAudio) obj;
if (resourcePath == null) {
if (other.resourcePath != null) return false;
} else if (!resourcePath.equals(other.resourcePath)) {

@ -1,4 +1,4 @@
package mightypork.rogue.sounds;
package mightypork.rogue.audio;
import mightypork.utils.math.coord.Coord;
@ -7,14 +7,14 @@ import mightypork.utils.objects.Mutable;
public class EffectPlayer extends BaseAudioPlayer {
public EffectPlayer(AudioX track, double basePitch, double baseGain, Mutable<Double> gainMultiplier) {
public EffectPlayer(DeferredAudio track, double basePitch, double baseGain, Mutable<Double> gainMultiplier) {
super(track, (float) basePitch, (float) baseGain, gainMultiplier);
}
public int play(double pitch, double gain)
{
if (!canPlay()) return -1;
if (!hasAudio()) return -1;
return getAudio().playAsEffect(getPitch(pitch), getGain(gain), false);
}
@ -28,7 +28,7 @@ public class EffectPlayer extends BaseAudioPlayer {
public int play(double pitch, double gain, Coord pos)
{
if (!canPlay()) return -1;
if (!hasAudio()) return -1;
return getAudio().playAsEffect(getPitch(pitch), getGain(gain), false, pos);
}

@ -1,4 +1,4 @@
package mightypork.rogue.sounds;
package mightypork.rogue.audio;
import mightypork.utils.math.Calc;

@ -1,4 +1,4 @@
package mightypork.rogue.sounds;
package mightypork.rogue.audio;
import mightypork.utils.control.interf.Updateable;
@ -28,7 +28,7 @@ public class LoopPlayer extends BaseAudioPlayer implements Updateable, Pauseable
private double outTime = 1;
public LoopPlayer(AudioX track, double pitch, double baseGain, Mutable<Double> gainMultiplier) {
public LoopPlayer(DeferredAudio track, double pitch, double baseGain, Mutable<Double> gainMultiplier) {
super(track, (float) pitch, (float) baseGain, gainMultiplier);
paused = true;
@ -44,7 +44,7 @@ public class LoopPlayer extends BaseAudioPlayer implements Updateable, Pauseable
private void initLoop()
{
if (!canPlay() && sourceID == -1) {
if (hasAudio() && sourceID == -1) {
sourceID = getAudio().playAsEffect(getPitch(1), getGain(1), true);
getAudio().pauseLoop();
}
@ -54,7 +54,7 @@ public class LoopPlayer extends BaseAudioPlayer implements Updateable, Pauseable
@Override
public void pause()
{
if (!canPlay() || paused) return;
if (!hasAudio() || paused) return;
initLoop();
@ -73,7 +73,7 @@ public class LoopPlayer extends BaseAudioPlayer implements Updateable, Pauseable
@Override
public void resume()
{
if (!canPlay() || paused) return;
if (!hasAudio() || !paused) return;
initLoop();
@ -85,13 +85,13 @@ public class LoopPlayer extends BaseAudioPlayer implements Updateable, Pauseable
@Override
public void update(double delta)
{
if (!canPlay() || paused) return;
if (!hasAudio() || paused) return;
initLoop();
fadeAnim.update(delta);
double gain = getGain(fadeAnim.getCurrentValue());
double gain = getGain(fadeAnim.now());
if (!paused && gain != lastUpdateGain) {
AL10.alSourcef(sourceID, AL10.AL_GAIN, (float) gain);
lastUpdateGain = gain;
@ -103,7 +103,7 @@ public class LoopPlayer extends BaseAudioPlayer implements Updateable, Pauseable
public void fadeIn(double secs)
{
if (!canPlay()) return;
if (!hasAudio()) return;
resume();
fadeAnim.fadeIn(secs);
@ -112,7 +112,7 @@ public class LoopPlayer extends BaseAudioPlayer implements Updateable, Pauseable
public void fadeOut(double secs)
{
if (!canPlay()) return;
if (!hasAudio()) return;
fadeAnim.fadeOut(secs);
}

@ -0,0 +1,30 @@
package mightypork.rogue.audio;
public class NullAudio extends DeferredAudio {
public NullAudio() {
super("");
}
@Override
public void load()
{
}
@Override
public boolean isLoaded()
{
return true;
}
@Override
protected boolean ensureLoaded()
{
return false;
}
}

@ -0,0 +1,90 @@
package mightypork.rogue.audio;
import java.util.HashMap;
import java.util.Map;
import mightypork.rogue.AppAccess;
import mightypork.rogue.AppAdapter;
import mightypork.utils.logging.Log;
public class SoundBank extends AppAdapter {
private static final DeferredAudio NO_SOUND = new NullAudio();
private static final LoopPlayer NULL_LOOP = new LoopPlayer(NO_SOUND, 0, 0, null);
private static final EffectPlayer NULL_EFFECT = new EffectPlayer(NO_SOUND, 0, 0, null);
private Map<String, EffectPlayer> effects = new HashMap<String, EffectPlayer>();
private Map<String, LoopPlayer> loops = new HashMap<String, LoopPlayer>();
public SoundBank(AppAccess app) {
super(app);
if (snd() == null) throw new NullPointerException("SoundSystem cannot be null.");
}
/**
* 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)
{
effects.put(key, snd().createEffect(resource, pitch, gain));
}
/**
* 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)
{
loops.put(key, snd().createLoop(resource, pitch, gain, fadeIn, fadeOut));
}
/**
* 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) {
Log.w("Requesting unknown sound loop \"" + key + "\".");
return NULL_LOOP;
}
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) {
Log.w("Requesting unknown sound effect \"" + key + "\".");
return NULL_EFFECT;
}
return p;
}
}

@ -1,16 +1,13 @@
package mightypork.rogue.sounds;
package mightypork.rogue.audio;
import java.nio.FloatBuffer;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import mightypork.rogue.AppAccess;
import mightypork.rogue.bus.Subsystem;
import mightypork.utils.control.interf.Updateable;
import mightypork.utils.logging.Log;
import mightypork.utils.math.Calc.Buffers;
import mightypork.utils.math.coord.Coord;
import mightypork.utils.objects.Mutable;
@ -30,9 +27,6 @@ public class SoundSystem extends Subsystem implements Updateable {
private static final Coord INITIAL_LISTENER_POS = new Coord(0, 0, 0);
private static final int MAX_SOURCES = 256;
private static final AudioX NO_SOUND = new NullAudio();
private static final LoopPlayer NULL_LOOP = new LoopPlayer(NO_SOUND, 0, 0, null);
private static final EffectPlayer NULL_EFFECT = new EffectPlayer(NO_SOUND, 0, 0, null);
private static Coord listener = new Coord();
@ -67,15 +61,20 @@ public class SoundSystem extends Subsystem implements Updateable {
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>();
private Set<LoopPlayer> loopPlayers = new HashSet<LoopPlayer>();
private Set<DeferredAudio> resources = new HashSet<DeferredAudio>();
public SoundSystem(AppAccess app) {
@ -86,7 +85,7 @@ public class SoundSystem extends Subsystem implements Updateable {
@Override
public final void deinit()
{
for (AudioX r : resources) {
for (DeferredAudio r : resources) {
r.destroy();
}
@ -98,191 +97,83 @@ public class SoundSystem extends Subsystem implements Updateable {
@Override
public void update(double delta)
{
for (LoopPlayer lp : loops.values()) {
for (Updateable lp : loopPlayers) {
lp.update(delta);
}
}
public static Coord getListener()
{
return listener;
}
/**
* Register effect resource
* Create effect resource
*
* @param key sound key
* @param resource resource path
* @param pitch default pitch (1 = unchanged)
* @param gain default gain (0-1)
* @return player
*/
public void addEffect(String key, String resource, double pitch, double gain)
public EffectPlayer createEffect(String resource, double pitch, double gain)
{
EffectPlayer p = new EffectPlayer(getResource(resource), pitch, gain, effectsVolume);
effects.put(key, p);
return new EffectPlayer(getResource(resource), pitch, gain, effectsVolume);
}
/**
* 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
* @return player
*/
public void addLoop(String key, String resource, double pitch, double gain, double fadeIn, double fadeOut)
public LoopPlayer createLoop(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);
loopPlayers.add(p);
return p;
}
/**
* Create {@link AudioX} for a resource
* Create {@link DeferredAudio} for a resource
*
* @param res a resource name
* @return the resource
* @throws IllegalArgumentException if resource is already registered
*/
private AudioX getResource(String res)
private DeferredAudio getResource(String res)
{
AudioX a = new AudioX(res);
DeferredAudio a = new DeferredAudio(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) {
Log.w("Requesting unknown sound loop \"" + key + "\".");
return NULL_LOOP;
}
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) {
Log.w("Requesting unknown sound effect \"" + key + "\".");
return NULL_EFFECT;
}
return p;
}
/**
* Fade out all loops (ie. for screen transitions)
*/
public void fadeOutAllLoops()
{
for (LoopPlayer p : loops.values()) {
for (LoopPlayer p : loopPlayers) {
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()) {
for (LoopPlayer p : loopPlayers) {
p.pause();
}
}
/**
* Resume a loop
*
* @param key sound key
*/
public void resumeLoop(String key)
{
getLoop(key).resume();
}
/**
* Set level of master volume
*

@ -10,6 +10,7 @@ import mightypork.rogue.AppAdapter;
import mightypork.utils.control.bus.EventBus;
import mightypork.utils.control.bus.clients.DelegatingClient;
import mightypork.utils.control.bus.clients.ToggleableClient;
import mightypork.utils.logging.Log;
/**
@ -57,8 +58,10 @@ public class ChildClient extends AppAdapter implements DelegatingClient, Togglea
*/
public final void addChildClient(Object client)
{
if (client != null) {
if (bus().isClientValid(client)) {
clients.add(client);
} else {
Log.w("Client rejected by bus: " + client.getClass().getSimpleName());
}
}

@ -0,0 +1,33 @@
package mightypork.rogue.bus.events;
import mightypork.utils.control.bus.Event;
/**
* Request for action that should be performed in the main thread.
*
* @author MightyPork
*/
public class ActionRequest implements Event<ActionRequest.Listener> {
private RequestType type;
public ActionRequest(RequestType request) {
type = request;
}
@Override
public void handleBy(Listener handler)
{
handler.requestAction(type);
}
public interface Listener {
public void requestAction(RequestType request);
}
}

@ -1,7 +1,7 @@
package mightypork.rogue.bus.events;
import mightypork.utils.control.bus.Handleable;
import mightypork.utils.control.bus.Event;
import org.lwjgl.input.Keyboard;
@ -11,7 +11,7 @@ import org.lwjgl.input.Keyboard;
*
* @author MightyPork
*/
public class KeyboardEvent implements Handleable<KeyboardEvent.Listener> {
public class KeyboardEvent implements Event<KeyboardEvent.Listener> {
private int key;
private boolean down;

@ -1,7 +1,7 @@
package mightypork.rogue.bus.events;
import mightypork.utils.control.bus.Handleable;
import mightypork.utils.control.bus.Event;
import mightypork.utils.math.coord.Coord;
@ -10,7 +10,7 @@ import mightypork.utils.math.coord.Coord;
*
* @author MightyPork
*/
public class MouseButtonEvent implements Handleable<MouseButtonEvent.Listener> {
public class MouseButtonEvent implements Event<MouseButtonEvent.Listener> {
public static final int BUTTON_LEFT = 0;
public static final int BUTTON_MIDDLE = 1;

@ -1,11 +1,11 @@
package mightypork.rogue.bus.events;
import mightypork.utils.control.bus.Handleable;
import mightypork.utils.control.bus.Event;
import mightypork.utils.math.coord.Coord;
public class MouseMotionEvent implements Handleable<MouseMotionEvent.Listener> {
public class MouseMotionEvent implements Event<MouseMotionEvent.Listener> {
private Coord move;
private Coord pos;
@ -20,7 +20,7 @@ public class MouseMotionEvent implements Handleable<MouseMotionEvent.Listener> {
/**
* @return movement since last {@link MouseMotionEvent}
*/
public Coord getPosDelta()
public Coord getMove()
{
return move;
}

@ -0,0 +1,7 @@
package mightypork.rogue.bus.events;
public enum RequestType
{
FULLSCREEN, SCREENSHOT, SHUTDOWN;
}

@ -1,11 +1,11 @@
package mightypork.rogue.bus.events;
import mightypork.utils.control.bus.Handleable;
import mightypork.utils.control.bus.Event;
import mightypork.utils.math.coord.Coord;
public class ScreenChangeEvent implements Handleable<ScreenChangeEvent.Listener> {
public class ScreenChangeEvent implements Event<ScreenChangeEvent.Listener> {
private boolean fullscreen;
private Coord screenSize;

@ -0,0 +1,28 @@
package mightypork.rogue.bus.events;
import mightypork.utils.control.bus.Event;
public class ScreenRequestEvent implements Event<ScreenRequestEvent.Listener> {
private String scrName;
public ScreenRequestEvent(String screenKey) {
scrName = screenKey;
}
@Override
public void handleBy(Listener handler)
{
handler.showScreen(scrName);
}
public interface Listener {
public void showScreen(String key);
}
}

@ -1,22 +0,0 @@
package mightypork.rogue.display.screens;
import mightypork.rogue.AppAccess;
import mightypork.rogue.bus.Subsystem;
public class ScreenRegistry extends Subsystem {
public ScreenRegistry(AppAccess app) {
super(app);
}
@Override
protected void deinit()
{
// TODO Auto-generated method stub
}
}

@ -1,6 +0,0 @@
package mightypork.rogue.display.screens.screenTextures;
public class ScreenTextureTest {
}

@ -1,4 +1,4 @@
package mightypork.rogue.display.constraints;
package mightypork.rogue.gui.constraints;
import java.util.LinkedList;

@ -1,4 +1,4 @@
package mightypork.rogue.display.constraints;
package mightypork.rogue.gui.constraints;
/**

@ -1,4 +1,4 @@
package mightypork.rogue.display.constraints;
package mightypork.rogue.gui.constraints;
import mightypork.utils.math.constraints.ConstraintContext;

@ -1,4 +1,4 @@
package mightypork.rogue.display;
package mightypork.rogue.gui.screens;
import java.util.LinkedList;

@ -1,13 +1,15 @@
package mightypork.rogue.display;
package mightypork.rogue.gui.screens;
import static org.lwjgl.opengl.GL11.*;
import mightypork.rogue.AppAccess;
import mightypork.rogue.bus.Subsystem;
import mightypork.rogue.bus.ChildClient;
import mightypork.rogue.bus.events.ScreenChangeEvent;
import mightypork.rogue.input.KeyBinder;
import mightypork.rogue.input.KeyBindingPool;
import mightypork.rogue.input.KeyStroke;
import mightypork.rogue.render.Render;
import mightypork.utils.control.interf.Destroyable;
import mightypork.utils.control.interf.Updateable;
import mightypork.utils.math.constraints.ConstraintContext;
import mightypork.utils.math.coord.Coord;
@ -21,11 +23,13 @@ import mightypork.utils.math.coord.Rect;
*
* @author MightyPork
*/
public abstract class Screen extends Subsystem implements Updateable, KeyBinder, ConstraintContext, ScreenChangeEvent.Listener {
public abstract class Screen extends ChildClient implements Destroyable, Updateable, KeyBinder, ConstraintContext, ScreenChangeEvent.Listener {
private final KeyBindingPool keybindings = new KeyBindingPool();
private boolean active;
private boolean needSetupGraphics = false;
private boolean needSetupViewport = false;
public Screen(AppAccess app) {
@ -53,7 +57,7 @@ public abstract class Screen extends Subsystem implements Updateable, KeyBinder,
@Override
protected final void deinit()
public final void destroy()
{
deinitScreen();
}
@ -68,8 +72,9 @@ public abstract class Screen extends Subsystem implements Updateable, KeyBinder,
{
if (shown) {
active = true;
setupGraphics();
setupViewport();
needSetupGraphics = true;
needSetupViewport = true;
onSizeChanged(getRect().getSize());
onScreenEnter();
@ -130,51 +135,12 @@ public abstract class Screen extends Subsystem implements Updateable, KeyBinder,
protected abstract void updateScreen(double delta);
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 = disp().getSize();
glViewport(0, 0, s.xi(), s.yi());
glOrtho(0, s.x, 0, s.y, -1000, 1000);
// back to modelview
glMatrixMode(GL_MODELVIEW);
}
/**
* Render screen
*/
private void renderBegin()
{
glPushAttrib(GL_ENABLE_BIT);
glPushMatrix();
Render.pushState();
}
@ -183,8 +149,7 @@ public abstract class Screen extends Subsystem implements Updateable, KeyBinder,
*/
private void renderEnd()
{
glPopAttrib();
glPopMatrix();
Render.popState();
}
@ -202,9 +167,9 @@ public abstract class Screen extends Subsystem implements Updateable, KeyBinder,
{
if (!isActive()) return;
setupViewport();
onSizeChanged(event.getScreenSize());
needSetupViewport = true;
}
@ -215,6 +180,27 @@ public abstract class Screen extends Subsystem implements Updateable, KeyBinder,
public final void update(double delta)
{
updateScreen(delta);
}
@Override
public final Rect getRect()
{
return disp().getRect();
}
public final void render()
{
if (!isActive()) return;
if (needSetupGraphics) {
setupGraphics();
}
if (needSetupViewport) {
setupViewport();
}
renderBegin();
renderScreen();
@ -222,10 +208,37 @@ public abstract class Screen extends Subsystem implements Updateable, KeyBinder,
}
@Override
public final Rect getRect()
private void setupGraphics()
{
return disp().getRect();
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);
}
private void setupViewport()
{
// fix projection for changed size
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
Coord s = disp().getSize();
glViewport(0, 0, s.xi(), s.yi());
glOrtho(0, s.x, s.y, 0, -1000, 1000);
// back to modelview
glMatrixMode(GL_MODELVIEW);
}
}

@ -1,8 +1,8 @@
package mightypork.rogue.display;
package mightypork.rogue.gui.screens;
import mightypork.rogue.bus.ChildClient;
import mightypork.rogue.display.constraints.Renderable;
import mightypork.rogue.gui.constraints.Renderable;
import mightypork.utils.control.interf.Updateable;
import mightypork.utils.math.constraints.ConstraintContext;
import mightypork.utils.math.coord.Rect;

@ -0,0 +1,55 @@
package mightypork.rogue.gui.screens;
import java.util.HashMap;
import mightypork.rogue.AppAccess;
import mightypork.rogue.bus.Subsystem;
import mightypork.rogue.bus.events.ScreenRequestEvent;
public class ScreenRegistry extends Subsystem implements ScreenRequestEvent.Listener {
private HashMap<String, Screen> screens = new HashMap<String, Screen>();
private Screen active = null;
public ScreenRegistry(AppAccess app) {
super(app);
}
public void add(String key, Screen screen)
{
screens.put(key, screen);
addChildClient(screen);
}
@Override
public void showScreen(String key)
{
Screen toshow = screens.get(key);
if (toshow == null) throw new RuntimeException("Screen " + key + " not defined.");
if (active != null) active.setActive(false);
toshow.setActive(true);
active = toshow;
}
public void render()
{
if (active != null) active.render();
}
@Override
protected void deinit()
{
// no impl
}
}

@ -1,12 +1,12 @@
package mightypork.rogue.display.screens.screenBouncy;
package mightypork.rogue.gui.screens.screenBouncy;
import static mightypork.utils.math.constraints.ConstraintFactory.*;
import java.util.Random;
import mightypork.rogue.display.constraints.RenderableWithContext;
import mightypork.rogue.textures.Render;
import mightypork.rogue.gui.constraints.RenderableWithContext;
import mightypork.rogue.render.Render;
import mightypork.utils.control.interf.Updateable;
import mightypork.utils.math.animation.AnimDouble;
import mightypork.utils.math.animation.Easing;
@ -55,7 +55,7 @@ public class BouncyBox implements RenderableWithContext, Updateable, ConstraintC
@Override
public void render()
{
Render.quadRect(box.getRect(), RGB.GREEN);
Render.quad(box.getRect(), RGB.GREEN);
}

@ -1,4 +1,4 @@
package mightypork.rogue.display.screens.screenBouncy;
package mightypork.rogue.gui.screens.screenBouncy;
import static mightypork.utils.math.constraints.ConstraintFactory.*;
@ -6,9 +6,9 @@ import static mightypork.utils.math.constraints.ConstraintFactory.*;
import java.util.ArrayList;
import java.util.List;
import mightypork.rogue.display.Screen;
import mightypork.rogue.display.ScreenLayer;
import mightypork.rogue.display.constraints.ElementHolder;
import mightypork.rogue.gui.constraints.ElementHolder;
import mightypork.rogue.gui.screens.Screen;
import mightypork.rogue.gui.screens.ScreenLayer;
import mightypork.utils.math.constraints.RectConstraint;

@ -1,19 +1,19 @@
package mightypork.rogue.display.screens.screenBouncy;
package mightypork.rogue.gui.screens.screenBouncy;
import mightypork.rogue.AppAccess;
import mightypork.rogue.display.LayeredScreen;
import mightypork.rogue.gui.screens.LayeredScreen;
import mightypork.rogue.input.KeyStroke;
import org.lwjgl.input.Keyboard;
public class TestLayeredScreen extends LayeredScreen {
public class ScreenTestAnim extends LayeredScreen {
private LayerBouncyBoxes layer;
public TestLayeredScreen(AppAccess app) {
public ScreenTestAnim(AppAccess app) {
super(app);
layer = new LayerBouncyBoxes(this);

@ -0,0 +1,104 @@
package mightypork.rogue.gui.screens.screenTextures;
import static mightypork.utils.math.constraints.ConstraintFactory.*;
import java.util.Random;
import mightypork.rogue.AppAccess;
import mightypork.rogue.Res;
import mightypork.rogue.bus.events.ActionRequest;
import mightypork.rogue.bus.events.MouseButtonEvent;
import mightypork.rogue.bus.events.RequestType;
import mightypork.rogue.gui.screens.Screen;
import mightypork.rogue.input.KeyStroke;
import mightypork.rogue.render.Render;
import mightypork.utils.math.animation.AnimDouble;
import mightypork.utils.math.animation.Easing;
import mightypork.utils.math.constraints.RectConstraint;
import mightypork.utils.math.coord.Coord;
import org.lwjgl.input.Keyboard;
public class ScreenTextureTest extends Screen implements MouseButtonEvent.Listener {
private RectConstraint kittenbox;
private AnimDouble s = new AnimDouble(400, Easing.SINE_BOTH);
private AnimDouble x = new AnimDouble(200, Easing.ELASTIC_OUT);
private AnimDouble y = new AnimDouble(200, Easing.ELASTIC_OUT);
private Random rand = new Random();
public ScreenTextureTest(AppAccess app) {
super(app);
kittenbox = c_move(c_box_sized(this, c_n(s), c_n(s)), c_n(x), c_n(y));
bindKeyStroke(new KeyStroke(Keyboard.KEY_ESCAPE), new Runnable() {
@Override
public void run()
{
snd().fadeOutAllLoops();
bus().schedule(new ActionRequest(RequestType.SHUTDOWN), 3);
}
});
}
@Override
protected void deinitScreen()
{
}
@Override
protected void onScreenEnter()
{
System.out.println("YOLO");
Res.getLoop("test.wilderness").fadeIn();
}
@Override
protected void onScreenLeave()
{
}
@Override
protected void renderScreen()
{
Render.quadTextured(kittenbox.getRect(), Res.getTexture("test.kitten"));
}
@Override
protected void updateScreen(double delta)
{
s.update(delta);
x.update(delta);
y.update(delta);
}
@Override
public void receive(MouseButtonEvent event)
{
if (!event.isDown()) return;
Coord pos = event.getPos();
double newSize = 200 + rand.nextInt(600);
double t = 2;
s.fadeTo(newSize, t / 2D);
x.fadeTo(pos.x - newSize / 2D, t);
y.fadeTo(pos.y - newSize / 2D, t);
}
}

@ -3,9 +3,11 @@ package mightypork.rogue.input;
import mightypork.rogue.AppAccess;
import mightypork.rogue.bus.Subsystem;
import mightypork.rogue.bus.events.ActionRequest;
import mightypork.rogue.bus.events.KeyboardEvent;
import mightypork.rogue.bus.events.MouseButtonEvent;
import mightypork.rogue.bus.events.MouseMotionEvent;
import mightypork.rogue.bus.events.RequestType;
import mightypork.utils.control.interf.Updateable;
import mightypork.utils.math.coord.Coord;
@ -19,6 +21,7 @@ public class InputSystem extends Subsystem implements Updateable, KeyBinder {
// listeners
private KeyBindingPool keybindings;
private boolean yAxisDown = true;
public InputSystem(AppAccess app) {
@ -26,8 +29,6 @@ public class InputSystem extends Subsystem implements Updateable, KeyBinder {
initDevices();
initChannels();
// global keybindings
keybindings = new KeyBindingPool();
addChildClient(keybindings);
@ -54,14 +55,6 @@ public class InputSystem extends Subsystem implements Updateable, KeyBinder {
}
private void initChannels()
{
bus().createChannel(KeyboardEvent.class, KeyboardEvent.Listener.class);
bus().createChannel(MouseMotionEvent.class, MouseMotionEvent.Listener.class);
bus().createChannel(MouseButtonEvent.class, MouseButtonEvent.Listener.class);
}
@Override
public final void bindKeyStroke(KeyStroke stroke, Runnable task)
{
@ -79,19 +72,35 @@ public class InputSystem extends Subsystem implements Updateable, KeyBinder {
@Override
public void update(double delta)
{
// was destroyed
if (!Display.isCreated()) return;
if (!Mouse.isCreated()) return;
if (!Keyboard.isCreated()) return;
Display.processMessages();
Coord moveSum = Coord.zero();
Coord lastPos = Coord.zero();
boolean wasMouse = false;
while (Mouse.next()) {
onMouseEvent();
onMouseEvent(moveSum, lastPos);
wasMouse = true;
}
if (wasMouse && !moveSum.isZero()) bus().queue(new MouseMotionEvent(lastPos, moveSum));
while (Keyboard.next()) {
onKeyEvent();
}
if (Display.isCloseRequested()) {
bus().queue(new ActionRequest(RequestType.SHUTDOWN));
}
}
private void onMouseEvent()
private void onMouseEvent(Coord moveSum, Coord lastPos)
{
int button = Mouse.getEventButton();
boolean down = Mouse.getEventButtonState();
@ -99,8 +108,17 @@ public class InputSystem extends Subsystem implements Updateable, KeyBinder {
Coord move = new Coord(Mouse.getEventDX(), Mouse.getEventDY());
int wheeld = Mouse.getEventDWheel();
if (button != -1 || wheeld != 0) bus().broadcast(new MouseButtonEvent(pos, button, down, wheeld));
if (!move.isZero()) bus().broadcast(new MouseMotionEvent(pos, move));
if (yAxisDown) {
flipScrY(pos);
move.mul_ip(1, -1, 1);
}
if (button != -1 || wheeld != 0) {
bus().queue(new MouseButtonEvent(pos, button, down, wheeld));
}
moveSum.add_ip(move);
lastPos.setTo(pos);
}
@ -109,6 +127,14 @@ public class InputSystem extends Subsystem implements Updateable, KeyBinder {
int key = Keyboard.getEventKey();
boolean down = Keyboard.getEventKeyState();
char c = Keyboard.getEventCharacter();
bus().broadcast(new KeyboardEvent(key, c, down));
bus().queue(new KeyboardEvent(key, c, down));
}
private void flipScrY(Coord c)
{
if (disp() != null) {
c.setY_ip(disp().getSize().y - c.y);
}
}
}

@ -1,4 +1,4 @@
package mightypork.rogue.display;
package mightypork.rogue.render;
import static org.lwjgl.opengl.GL11.*;
@ -27,8 +27,6 @@ public class DisplaySystem extends Subsystem implements ConstraintContext {
public DisplaySystem(AppAccess app) {
super(app);
initChannels();
}
@ -39,15 +37,6 @@ public class DisplaySystem extends Subsystem implements ConstraintContext {
}
/**
* Initialize event channels
*/
private void initChannels()
{
bus().createChannel(ScreenChangeEvent.class, ScreenChangeEvent.Listener.class);
}
public void setTargetFps(int fps)
{
this.targetFps = fps;
@ -94,7 +83,7 @@ public class DisplaySystem extends Subsystem implements ConstraintContext {
Display.update();
}
bus().broadcast(new ScreenChangeEvent(true, Display.isFullscreen(), getSize()));
bus().queue(new ScreenChangeEvent(true, Display.isFullscreen(), getSize()));
} catch (Throwable t) {
Log.e("Failed to toggle fullscreen mode.", t);
@ -162,7 +151,7 @@ public class DisplaySystem extends Subsystem implements ConstraintContext {
{
// handle resize
if (Display.wasResized()) {
bus().broadcast(new ScreenChangeEvent(false, Display.isFullscreen(), getSize()));
bus().queue(new ScreenChangeEvent(false, Display.isFullscreen(), getSize()));
}
glLoadIdentity();

@ -0,0 +1,349 @@
package mightypork.rogue.render;
import static org.lwjgl.opengl.GL11.*;
import java.io.IOException;
import mightypork.rogue.render.textures.TxQuad;
import mightypork.utils.files.FileUtils;
import mightypork.utils.logging.Log;
import mightypork.utils.math.color.RGB;
import mightypork.utils.math.coord.Coord;
import mightypork.utils.math.coord.Rect;
import org.newdawn.slick.opengl.SlickCallable;
import org.newdawn.slick.opengl.Texture;
import org.newdawn.slick.opengl.TextureImpl;
import org.newdawn.slick.opengl.TextureLoader;
import org.newdawn.slick.util.ResourceLoader;
/**
* Render utilities
*
* @author MightyPork
*/
public class Render {
private static final Coord AXIS_X = new Coord(1, 0, 0);
private static final Coord AXIS_Y = new Coord(0, 1, 0);
private static final Coord AXIS_Z = new Coord(0, 0, 1);
private static boolean inited = false;
/**
* Bind GL color
*
* @param color RGB color
*/
public static void setColor(RGB color)
{
if (color != null) glColor4d(color.r, color.g, color.b, color.a);
}
/**
* Bind GL color
*
* @param color RGB color
* @param alpha alpha multiplier
*/
public static void setColor(RGB color, double alpha)
{
if (color != null) glColor4d(color.r, color.g, color.b, color.a * alpha);
}
/**
* Translate with coord
*
* @param coord coord
*/
public static void translate(Coord coord)
{
glTranslated(coord.x, coord.y, coord.z);
}
public static void rotateX(double angle)
{
rotate(angle, AXIS_X);
}
public static void rotateY(double angle)
{
rotate(angle, AXIS_Y);
}
public static void rotateZ(double angle)
{
rotate(angle, AXIS_Z);
}
public static void rotate(double angle, Coord axis)
{
Coord vec = axis.norm(1);
glRotated(angle, vec.x, vec.y, vec.z);
}
public static void pushState()
{
SlickCallable.enterSafeBlock();
}
public static void popState()
{
SlickCallable.leaveSafeBlock();
}
/**
* Load texture
*
* @param resourcePath
* @return the loaded texture
*/
public static Texture loadTexture(String resourcePath)
{
if (!inited) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
inited = true;
}
try {
String ext = FileUtils.getExtension(resourcePath).toUpperCase();
Log.f3("Loading texture " + ext + " at " + resourcePath);
Texture texture = TextureLoader.getTexture(ext, ResourceLoader.getResourceAsStream(resourcePath));
if (texture == null) {
Log.w("Texture " + resourcePath + " could not be loaded.");
}
return texture;
} catch (IOException e) {
Log.e("Loading of texture " + resourcePath + " failed.", e);
throw new RuntimeException("Could not load texture " + resourcePath + ".", e);
}
}
/**
* Bind texture
*
* @param texture the texture
* @throws RuntimeException if not loaded yet
*/
private static void bindTexture(Texture texture) throws RuntimeException
{
texture.bind();
}
/**
* Unbind all
*/
private static void unbindTexture()
{
if (TextureImpl.getLastBind() != null) {
TextureImpl.bindNone();
}
}
/**
* Render quad 2D
*
* @param rect rectangle
* @param color draw color
*/
public static void quad(Rect rect, RGB color)
{
setColor(color);
quad(rect);
}
/**
* Render quad
*
* @param quad the quad to draw (px)
*/
public static void quad(Rect quad)
{
double left = quad.xMin();
double bottom = quad.yMin();
double right = quad.yMax();
double top = quad.yMax();
// draw with color
unbindTexture();
// quad
glBegin(GL_QUADS);
glVertex2d(left, top);
glVertex2d(right, top);
glVertex2d(right, bottom);
glVertex2d(left, bottom);
glEnd();
}
/**
* Render textured rect (texture must be binded already)
*
* @param quad rectangle (px)
* @param uvs texture coords (0-1)
*/
private static void quadUV(Rect quad, Rect uvs)
{
double left = quad.xMin();
double bottom = quad.yMin();
double right = quad.xMax();
double top = quad.yMax();
double tleft = uvs.xMin();
double tbottom = uvs.yMin();
double tright = uvs.xMax();
double ttop = uvs.yMax();
// quad with texture
glBegin(GL_QUADS);
glTexCoord2d(tleft, ttop);
glVertex2d(left, top);
glTexCoord2d(tright, ttop);
glVertex2d(right, top);
glTexCoord2d(tright, tbottom);
glVertex2d(right, bottom);
glTexCoord2d(tleft, tbottom);
glVertex2d(left, bottom);
glEnd();
}
public static void quadGradH(Rect quad, RGB colorLeft, RGB colorRight)
{
double left = quad.xMin();
double bottom = quad.yMin();
double right = quad.yMax();
double top = quad.yMax();
// draw with color
unbindTexture();
glBegin(GL_QUADS);
setColor(colorLeft);
glVertex2d(left, top);
setColor(colorRight);
glVertex2d(right, top);
setColor(colorRight);
glVertex2d(right, bottom);
setColor(colorLeft);
glVertex2d(left, bottom);
glEnd();
}
public static void quadGradV(Rect quad, RGB colorTop, RGB colorBottom)
{
double left = quad.xMin();
double bottom = quad.yMin();
double right = quad.yMax();
double top = quad.yMax();
// draw with color
unbindTexture();
glBegin(GL_QUADS);
setColor(colorTop);
glVertex2d(left, top);
glVertex2d(right, top);
setColor(colorBottom);
glVertex2d(right, bottom);
glVertex2d(left, bottom);
glEnd();
}
/**
* Render textured rect
*
* @param quad rectangle (px)
* @param uvs texture coords rectangle (0-1)
* @param texture texture instance
* @param tint color tint
*/
public static void quadTextured(Rect quad, Rect uvs, Texture texture, RGB tint)
{
bindTexture(texture);
setColor(tint);
quadUV(quad, uvs);
}
/**
* Render textured rect
*
* @param quad rectangle (px)
* @param uvs texture coords rectangle (px)
* @param texture texture instance
*/
public static void quadTextured(Rect quad, Rect uvs, Texture texture)
{
quadTextured(quad, uvs, texture, RGB.WHITE);
}
/**
* Render textured rect
*
* @param quad rectangle (px)
* @param texture texture instance
*/
public static void quadTextured(Rect quad, Texture texture)
{
quadTextured(quad, Rect.one(), texture, RGB.WHITE);
}
/**
* Render textured rect
*
* @param quad rectangle (px)
* @param txquad texture quad
*/
public static void quadTextured(Rect quad, TxQuad txquad)
{
quadTextured(quad, txquad, RGB.WHITE);
}
/**
* Render textured rect
*
* @param quad rectangle (px)
* @param txquad texture instance
* @param tint color tint
*/
public static void quadTextured(Rect quad, TxQuad txquad, RGB tint)
{
quadTextured(quad, txquad.uvs, txquad.tx, tint);
}
}

@ -1,12 +1,14 @@
package mightypork.rogue.textures;
package mightypork.rogue.render.textures;
import mightypork.rogue.Deferred;
import mightypork.rogue.render.Render;
import mightypork.utils.math.coord.Rect;
import org.newdawn.slick.opengl.Texture;
public class MultiTexture implements Texture {
public class MultiTexture implements Texture, Deferred {
private Texture backingTexture;
private String resourcePath;
@ -23,17 +25,22 @@ public class MultiTexture implements Texture {
}
/**
* Attempt to load the texture.
*/
private void load()
@Override
public void load()
{
if (backingTexture == null) {
if (!isLoaded()) {
backingTexture = Render.loadTexture(resourcePath);
}
}
@Override
public boolean isLoaded()
{
return backingTexture != null;
}
@Override
public boolean hasAlpha()
{

@ -1,11 +1,10 @@
package mightypork.rogue.textures;
package mightypork.rogue.render.textures;
import java.util.HashMap;
import mightypork.rogue.AppAccess;
import mightypork.rogue.bus.Subsystem;
import mightypork.utils.control.interf.Destroyable;
import mightypork.rogue.AppAdapter;
import mightypork.utils.math.coord.Rect;
import org.newdawn.slick.opengl.Texture;
@ -16,9 +15,9 @@ import org.newdawn.slick.opengl.Texture;
*
* @author MightyPork
*/
public class TextureRegistry extends Subsystem implements Destroyable {
public class TextureBank extends AppAdapter {
public TextureRegistry(AppAccess app) {
public TextureBank(AppAccess app) {
super(app);
}
@ -84,7 +83,7 @@ public class TextureRegistry extends Subsystem implements Destroyable {
* @param key quad key
* @return the quad
*/
public TxQuad getQuad(String key)
public TxQuad getTxQuad(String key)
{
TxQuad q = quads.get(key);
@ -109,16 +108,4 @@ public class TextureRegistry extends Subsystem implements Destroyable {
return t;
}
@Override
protected void deinit()
{
for (Texture tx : textures.values()) {
tx.release();
}
textures.clear();
quads.clear();
}
}

@ -0,0 +1,91 @@
package mightypork.rogue.render.textures;
import mightypork.utils.math.coord.Rect;
import org.newdawn.slick.opengl.Texture;
/**
* Texture Quad (describing a part of a texture)
*
* @author MightyPork
*/
public class TxQuad {
/** The texture */
public Texture tx;
/** Coords in texture (0-1) */
public Rect uvs;
/**
* TxQuad from origin and size in pixels
*
* @param tx texture
* @param xPx left top X (0-1)
* @param yPx left top Y (0-1)
* @param widthPx area width (0-1)
* @param heightPx area height (0-1)
* @return new TxQuad
*/
public static TxQuad fromSizePx(Texture tx, double xPx, double yPx, double widthPx, double heightPx)
{
double w = tx.getImageWidth();
double h = tx.getImageHeight();
return fromSize(tx, xPx / w, yPx / h, widthPx / w, heightPx / h);
}
/**
* TxQuad from origin and size 0-1
*
* @param tx texture
* @param x1 left top X (0-1)
* @param y1 left top Y (0-1)
* @param width area width (0-1)
* @param height area height (0-1)
* @return new TxQuad
*/
public static TxQuad fromSize(Texture tx, double x1, double y1, double width, double height)
{
return new TxQuad(tx, x1, y1, x1 + width, y1 + height);
}
/**
* Make of coords
*
* @param tx texture
* @param x1 left top X (0-1)
* @param y1 left top Y (0-1)
* @param x2 right bottom X (0-1)
* @param y2 right bottom Y (0-1)
*/
public TxQuad(Texture tx, double x1, double y1, double x2, double y2) {
this(tx, new Rect(x1, y1, x2, y2));
}
/**
* @param tx Texture
* @param uvs Rect of texturwe UVs (pixels - from left top)
*/
public TxQuad(Texture tx, Rect uvs) {
this.tx = tx;
this.uvs = uvs.copy();
}
public TxQuad(TxQuad txQuad) {
this.tx = txQuad.tx;
this.uvs = txQuad.uvs.copy();
}
public TxQuad copy()
{
return new TxQuad(this);
}
}

@ -1,17 +0,0 @@
package mightypork.rogue.sounds;
public class NullAudio extends AudioX {
public NullAudio() {
super("");
}
@Override
public boolean load()
{
return false;
}
}

@ -11,8 +11,8 @@ import java.util.Date;
import javax.imageio.ImageIO;
import mightypork.rogue.Paths;
import mightypork.rogue.display.DisplaySystem;
import mightypork.rogue.display.DisplaySystem.Screenshot;
import mightypork.rogue.render.DisplaySystem;
import mightypork.rogue.render.DisplaySystem.Screenshot;
import mightypork.utils.logging.Log;

File diff suppressed because it is too large Load Diff

@ -1,70 +0,0 @@
package mightypork.rogue.textures;
import mightypork.utils.math.coord.Coord;
import mightypork.utils.math.coord.Rect;
import org.newdawn.slick.opengl.Texture;
/**
* Texture Quad (describing a part of a texture)
*
* @author MightyPork
*/
public class TxQuad {
/** The texture */
public Texture tx;
/** Coords in texture (pixels) */
public Rect uvs;
/** Quad size */
public Coord size;
/**
* Create TxQuad from left top coord and rect size
*
* @param tx texture
* @param x1 left top X
* @param y1 left top Y
* @param width area width
* @param height area height
* @return new TxQuad
*/
public static TxQuad fromSize(Texture tx, int x1, int y1, int width, int height)
{
return new TxQuad(tx, x1, y1, x1 + width, y1 + height);
}
/**
* Make of coords
*
* @param tx texture
* @param x1 x1
* @param y1 y1
* @param x2 x2
* @param y2 y2
*/
public TxQuad(Texture tx, int x1, int y1, int x2, int y2) {
this(tx, new Rect(x1, y1, x2, y2));
}
/**
* @param tx Texture
* @param uvs Rect of texturwe UVs (pixels - from left top)
*/
public TxQuad(Texture tx, Rect uvs) {
this.tx = tx;
this.uvs = uvs.copy();
this.size = uvs.getSize();
}
public TxQuad copy()
{
return new TxQuad(tx, uvs);
}
}

@ -0,0 +1,130 @@
package mightypork.utils.control.bus;
import java.util.Collection;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
/**
* HashSet that buffers add and remove calls and performs them all at once when
* a flush() method is called.
*
* @author MightyPork
* @param <E> element type
*/
public class BufferedHashSet<E> extends HashSet<E> {
private List<E> toAdd = new LinkedList<E>();
private List<Object> toRemove = new LinkedList<Object>();
private boolean buffering = false;
public BufferedHashSet() {
super();
}
public BufferedHashSet(Collection<? extends E> c) {
super(c);
}
public BufferedHashSet(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
}
public BufferedHashSet(int initialCapacity) {
super(initialCapacity);
}
@Override
public boolean add(E e)
{
if (buffering) {
toAdd.add(e);
} else {
super.add(e);
}
return true;
}
@Override
public boolean remove(Object e)
{
if (buffering) {
toRemove.add(e);
} else {
super.remove(e);
}
return true;
}
/**
* Flush all
*/
private void flush()
{
for (E e : toAdd) {
super.add(e);
}
for (Object e : toRemove) {
super.remove(e);
}
toAdd.clear();
toRemove.clear();
}
@Override
public boolean removeAll(Collection<?> c)
{
throw new UnsupportedOperationException();
}
@Override
public boolean containsAll(Collection<?> c)
{
throw new UnsupportedOperationException();
}
@Override
public boolean addAll(Collection<? extends E> c)
{
throw new UnsupportedOperationException();
}
@Override
public boolean retainAll(Collection<?> c)
{
throw new UnsupportedOperationException();
}
/**
* Toggle buffering
*
* @param enable enable buffering
*/
public void setBuffering(boolean enable)
{
if (this.buffering && !enable) {
flush();
}
this.buffering = enable;
}
}

@ -7,7 +7,7 @@ package mightypork.utils.control.bus;
* @author MightyPork
* @param <HANDLER> handler type
*/
public interface Handleable<HANDLER> {
public interface Event<HANDLER> {
/**
* Ask handler to handle this message.

@ -1,13 +1,11 @@
package mightypork.utils.control.bus;
import java.util.Collection;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
import mightypork.utils.control.bus.events.DestroyEvent;
import mightypork.utils.control.bus.events.UpdateEvent;
import mightypork.utils.control.interf.Destroyable;
import mightypork.utils.control.interf.Updateable;
import mightypork.utils.logging.Log;
@ -16,25 +14,37 @@ import mightypork.utils.logging.Log;
*
* @author MightyPork
*/
final public class EventBus {
final public class EventBus implements Destroyable {
private Collection<EventChannel<?, ?>> channels = new CopyOnWriteArraySet<EventChannel<?, ?>>();
private Collection<Object> clients = new CopyOnWriteArraySet<Object>();
private BufferedHashSet<EventChannel<?, ?>> channels = new BufferedHashSet<EventChannel<?, ?>>();
private BufferedHashSet<Object> clients = new BufferedHashSet<Object>();
private DelayQueue<DelayedMessage> sendQueue = new DelayQueue<DelayedMessage>();
private BusThread busThread;
private boolean logging = false;
private boolean dead = false;
public EventBus() {
// default channels
createChannel(DestroyEvent.class, Destroyable.class);
createChannel(UpdateEvent.class, Updateable.class);
busThread = new BusThread();
busThread.start();
}
public void enableLogging(boolean enable)
/**
* Enable a level of logging.
*
* @param level 0 none, 1 warning only, 2 all
*/
public void enableLogging(boolean level)
{
logging = enable;
assertLive();
logging = level;
for (EventChannel<?, ?> ch : channels) {
ch.enableLogging(logging);
}
}
@ -47,20 +57,40 @@ final public class EventBus {
*/
public EventChannel<?, ?> addChannel(EventChannel<?, ?> channel)
{
assertLive();
// if the channel already exists, return this instance instead.
for (EventChannel<?, ?> ch : channels) {
if (ch.equals(channel)) {
if (logging) Log.w("Channel of type " + channel + " already registered.");
Log.w("<bus> Channel of type " + Log.str(channel) + " already registered.");
return ch;
}
}
channels.add(channel);
channel.enableLogging(logging);
if (logging) Log.f3("<bus> Added chanel: " + Log.str(channel));
return channel;
}
/**
* Add a channel for given message and client type.
*
* @param messageClass message type
* @param clientClass client type
* @return the created channel instance
*/
public <F_EVENT extends Event<F_CLIENT>, F_CLIENT> EventChannel<?, ?> addChannel(Class<F_EVENT> messageClass, Class<F_CLIENT> clientClass)
{
assertLive();
EventChannel<F_EVENT, F_CLIENT> channel = EventChannel.create(messageClass, clientClass);
return addChannel(channel);
}
/**
* Remove a {@link EventChannel} from this bus
*
@ -68,29 +98,71 @@ final public class EventBus {
*/
public void removeChannel(EventChannel<?, ?> channel)
{
assertLive();
channels.remove(channel);
}
/**
* Broadcast a message
* Add message to a queue
*
* @param message message
* @return true if message was accepted by at least one channel
*/
public boolean broadcast(Object message)
public void queue(Event<?> message)
{
if (logging) Log.f3("<bus> Broadcasting: " + message);
assertLive();
boolean sent = false;
schedule(message, 0);
}
/**
* Add message to a queue, scheduled for given time.
*
* @param message message
* @param delay delay before message is dispatched
*/
public void schedule(Event<?> message, double delay)
{
assertLive();
for (EventChannel<?, ?> b : channels) {
sent |= b.broadcast(message, clients);
}
DelayedMessage dm = new DelayedMessage(delay, message);
if (!sent && logging) Log.w("Message not accepted by any channel: " + message);
if (logging) Log.f3("<bus> + [ Queuing: " + Log.str(message) + " ]");
return sent;
sendQueue.add(dm);
}
/**
* Send immediately.<br>
* Should be used for real-time events that require immediate response, such
* as timing events.
*
* @param message message
*/
public void send(Event<?> message)
{
assertLive();
synchronized (this) {
channels.setBuffering(true);
clients.setBuffering(true);
if (logging) Log.f3("<bus> - [ Sending: " + Log.str(message) + " ]");
boolean sent = false;
for (EventChannel<?, ?> b : channels) {
sent |= b.broadcast(message, clients);
}
if (!sent) Log.w("<bus> Not accepted by any channel: " + Log.str(message));
channels.setBuffering(false);
clients.setBuffering(false);
}
}
@ -99,15 +171,16 @@ final public class EventBus {
* and future channels, until removed from the bus.
*
* @param client the client
* @return true on success
*/
public boolean subscribe(Object client)
public void subscribe(Object client)
{
if (client == null) return false;
assertLive();
if (client == null) return;
clients.add(client);
return true;
if (logging) Log.f3("<bus> ADDING CLIENT " + client);
}
@ -118,22 +191,111 @@ final public class EventBus {
*/
public void unsubscribe(Object client)
{
assertLive();
clients.remove(client);
if (logging) Log.f3("<bus> REMOVING CLIENT " + client);
}
public boolean isClientValid(Object client)
{
assertLive();
if (client == null) return false;
for (EventChannel<?, ?> ch : channels) {
if (ch.isClientValid(client)) {
return true;
}
}
return false;
}
private class DelayedMessage implements Delayed {
private long due;
private Event<?> theMessage = null;
public DelayedMessage(double seconds, Event<?> theMessage) {
super();
this.due = System.currentTimeMillis() + (long) (seconds * 1000);
this.theMessage = theMessage;
}
@Override
public int compareTo(Delayed o)
{
return -Long.valueOf(o.getDelay(TimeUnit.MILLISECONDS)).compareTo(getDelay(TimeUnit.MILLISECONDS));
}
@Override
public long getDelay(TimeUnit unit)
{
return unit.convert(due - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
}
public Event<?> getMessage()
{
return theMessage;
}
}
private class BusThread extends Thread {
public boolean stopped;
@Override
public void run()
{
while (!stopped) {
DelayedMessage dm = null;
try {
dm = sendQueue.take();
} catch (InterruptedException ignored) {
//
}
if (dm != null) {
send(dm.getMessage());
}
}
}
}
/**
* Add a channel for given message and client type.
* Halt bus thread and reject any future events.
*/
@Override
public void destroy()
{
assertLive();
busThread.stopped = true;
dead = true;
}
/**
* Make sure the bus is not destroyed.
*
* @param messageClass message type
* @param clientClass client type
* @return the created channel instance
* @throws IllegalStateException if the bus is dead.
*/
public <F_EVENT extends Handleable<F_CLIENT>, F_CLIENT> EventChannel<?, ?> createChannel(Class<F_EVENT> messageClass, Class<F_CLIENT> clientClass)
private void assertLive() throws IllegalStateException
{
EventChannel<F_EVENT, F_CLIENT> channel = EventChannel.create(messageClass, clientClass);
channel.enableLogging(logging);
return addChannel(channel);
if (dead) throw new IllegalStateException("EventBus is dead.");
}
}

@ -16,13 +16,19 @@ import mightypork.utils.logging.Log;
* @param <EVENT> message type
* @param <CLIENT> client (subscriber) type
*/
final public class EventChannel<EVENT extends Handleable<CLIENT>, CLIENT> {
final public class EventChannel<EVENT extends Event<CLIENT>, CLIENT> {
private Class<CLIENT> clientClass;
private Class<EVENT> messageClass;
private boolean logging = false;
/**
* Create a channel
*
* @param messageClass event class
* @param clientClass client class
*/
public EventChannel(Class<EVENT> messageClass, Class<CLIENT> clientClass) {
if (messageClass == null || clientClass == null) throw new NullPointerException("Null Message or Client class.");
@ -32,9 +38,14 @@ final public class EventChannel<EVENT extends Handleable<CLIENT>, CLIENT> {
}
public void enableLogging(boolean enable)
/**
* Enable logging of non-warning debug messages.
*
* @param logging enable logging
*/
public void enableLogging(boolean logging)
{
logging = enable;
this.logging = logging;
}
@ -46,7 +57,7 @@ final public class EventChannel<EVENT extends Handleable<CLIENT>, CLIENT> {
* @param clients collection of clients
* @return true if message was accepted by this channel
*/
public boolean broadcast(Object message, Collection<Object> clients)
public boolean broadcast(Event<?> message, Collection<Object> clients)
{
if (!canBroadcast(message)) return false;
@ -58,13 +69,22 @@ final public class EventChannel<EVENT extends Handleable<CLIENT>, CLIENT> {
}
private void doBroadcast(EVENT message, Collection<Object> clients, Collection<Object> processed)
/**
* Send the message
*
* @param message sent message
* @param clients subscribing clients
* @param processed clients already processed
*/
private void doBroadcast(final EVENT message, final Collection<Object> clients, final Collection<Object> processed)
{
for (Object client : clients) {
if (!isClientValid(client)) {
continue;
}
// circular reference check
if (processed.contains(client)) {
if (logging) Log.w("Client already served (subscribing twice?)");
Log.w("<bus> Client already served: " + Log.str(client));
continue;
}
processed.add(client);
@ -72,6 +92,7 @@ final public class EventChannel<EVENT extends Handleable<CLIENT>, CLIENT> {
// opt-out
if (client instanceof ToggleableClient) {
if (!((ToggleableClient) client).isListening()) {
if (logging) Log.f3("<bus> Client disabled: " + Log.str(client));
continue;
}
}
@ -80,7 +101,10 @@ final public class EventChannel<EVENT extends Handleable<CLIENT>, CLIENT> {
if (client instanceof DelegatingClient) {
if (((DelegatingClient) client).doesDelegate()) {
doBroadcast(message, ((DelegatingClient) client).getChildClients(), processed);
Collection<Object> children = ((DelegatingClient) client).getChildClients();
if (children != null && children.size() > 0) doBroadcast(message, children, processed);
} else {
if (logging) Log.f3("<bus> Client not delegating: " + Log.str(client));
}
}
}
@ -96,9 +120,9 @@ final public class EventChannel<EVENT extends Handleable<CLIENT>, CLIENT> {
@SuppressWarnings("unchecked")
private void sendTo(Object client, EVENT message)
{
if (clientClass.isInstance(client)) {
((Handleable<CLIENT>) message).handleBy((CLIENT) client);
if (logging) Log.f3("<bus> Received by: " + client);
if (isClientOfType(client)) {
if (logging) Log.f3("<bus> Delivered " + Log.str(message) + " to " + Log.str(client));
((Event<CLIENT>) message).handleBy((CLIENT) client);
}
}
@ -110,12 +134,49 @@ final public class EventChannel<EVENT extends Handleable<CLIENT>, CLIENT> {
* @param message event object
* @return can be broadcasted
*/
public boolean canBroadcast(Object message)
public boolean canBroadcast(Event<?> message)
{
return message != null && messageClass.isInstance(message);
}
/**
* Create an instance for given types
*
* @param messageClass event class
* @param clientClass client class
* @return the broadcaster
*/
public static <F_EVENT extends Event<F_CLIENT>, F_CLIENT> EventChannel<F_EVENT, F_CLIENT> create(Class<F_EVENT> messageClass, Class<F_CLIENT> clientClass)
{
return new EventChannel<F_EVENT, F_CLIENT>(messageClass, clientClass);
}
/**
* Check if client is of channel type
*
* @param client client
* @return is of type
*/
private boolean isClientOfType(Object client)
{
return clientClass.isInstance(client);
}
/**
* Check if the channel is compatible with given
*
* @param client client
* @return is supported
*/
public boolean isClientValid(Object client)
{
return isClientOfType(client) || (client instanceof DelegatingClient);
}
@Override
public int hashCode()
{
@ -147,20 +208,6 @@ final public class EventChannel<EVENT extends Handleable<CLIENT>, CLIENT> {
@Override
public String toString()
{
return "CHANNEL( " + messageClass.getSimpleName() + " -> " + clientClass.getSimpleName() + " )";
return "{ " + Log.str(messageClass) + " => " + Log.str(clientClass) + " }";
}
/**
* Create an instance for given types
*
* @param messageClass event class
* @param clientClass client class
* @return the broadcaster
*/
public static <F_EVENT extends Handleable<F_CLIENT>, F_CLIENT> EventChannel<F_EVENT, F_CLIENT> create(Class<F_EVENT> messageClass, Class<F_CLIENT> clientClass)
{
return new EventChannel<F_EVENT, F_CLIENT>(messageClass, clientClass);
}
}

@ -1,11 +1,11 @@
package mightypork.utils.control.bus.events;
import mightypork.utils.control.bus.Handleable;
import mightypork.utils.control.bus.Event;
import mightypork.utils.control.interf.Destroyable;
public class DestroyEvent implements Handleable<Destroyable> {
public class DestroyEvent implements Event<Destroyable> {
@Override
public void handleBy(Destroyable handler)

@ -1,7 +1,7 @@
package mightypork.utils.control.bus.events;
import mightypork.utils.control.bus.Handleable;
import mightypork.utils.control.bus.Event;
import mightypork.utils.control.interf.Updateable;
@ -10,7 +10,7 @@ import mightypork.utils.control.interf.Updateable;
*
* @author MightyPork
*/
public class UpdateEvent implements Handleable<Updateable> {
public class UpdateEvent implements Event<Updateable> {
private final double deltaTime;

@ -38,7 +38,6 @@ public class TimerFps {
long time = getTime();
if (time >= nextFrame) {
long skippedNow = (long) Math.floor((time - nextFrame) / (double) FRAME) + 1;
// System.out.println("Skipping: "+skippedNow);
skipped += skippedNow;
lastFrame = nextFrame + (1 - skippedNow) * FRAME;
nextFrame += skippedNow * FRAME;

@ -176,4 +176,35 @@ public class Log {
main.removeMonitor(id);
}
public static String str(Object o)
{
boolean hasToString = false;
try {
hasToString = (o.getClass().getMethod("toString").getDeclaringClass() != Object.class);
} catch (Exception e) {
// oh well..
}
if (hasToString) {
return o.toString();
} else {
Class<?> cls = o.getClass();
Class<?> enclosing = cls.getEnclosingClass();
return (enclosing == null ? "" : enclosing.getSimpleName() + ".") + cls.getSimpleName();
}
}
public static String str(Class<?> cls)
{
Class<?> enclosing = cls.getEnclosingClass();
return (enclosing == null ? "" : enclosing.getSimpleName() + ".") + cls.getSimpleName();
}
}

@ -109,7 +109,7 @@ public class AnimDouble implements Updateable, Pauseable {
*
* @return the value
*/
public double getCurrentValue()
public double now()
{
if (duration == 0) return to;
return Calc.interpolate(from, to, (elapsedTime / duration), easing);
@ -191,7 +191,7 @@ public class AnimDouble implements Updateable, Pauseable {
*/
public void animate(double from, double to, double time)
{
double current = getCurrentValue();
double current = now();
this.from = from;
this.to = to;
@ -229,9 +229,7 @@ public class AnimDouble implements Updateable, Pauseable {
*/
public void fadeTo(double to, double time)
{
double current = getCurrentValue();
this.from = current;
this.from = now();
this.to = to;
this.duration = time;
this.elapsedTime = 0;
@ -295,7 +293,7 @@ public class AnimDouble implements Updateable, Pauseable {
*/
public void stop()
{
from = to = getCurrentValue();
from = to = now();
elapsedTime = 0;
duration = 0;
}

@ -28,7 +28,7 @@ public class AnimDoubleDeg extends AnimDouble {
@Override
public double getCurrentValue()
public double now()
{
if (duration == 0) return Deg.norm(to);
return Calc.interpolateDeg(from, to, (elapsedTime / duration), easing);

@ -28,7 +28,7 @@ public class AnimDoubleRad extends AnimDouble {
@Override
public double getCurrentValue()
public double now()
{
if (duration == 0) return Rad.norm(to);
return Calc.interpolateRad(from, to, (elapsedTime / duration), easing);

@ -203,7 +203,7 @@ public class ConstraintFactory {
@Override
public double getValue()
{
return a.getCurrentValue();
return a.now();
}
};
}
@ -292,6 +292,19 @@ public class ConstraintFactory {
}
public static RectConstraint c_center(ConstraintContext context)
{
return new RectConstraint(context) {
@Override
public Rect getRect()
{
return Rect.fromSize(getContext().getRect().getCenter(), 0, 0);
}
};
}
public static RectConstraint c_grow(ConstraintContext context, NumConstraint grow)
{
return c_grow(context, grow, grow, grow, grow);
@ -335,12 +348,6 @@ public class ConstraintFactory {
}
/**
* @param context
* @param width
* @param height
* @return
*/
public static RectConstraint c_box_sized(ConstraintContext context, final NumConstraint width, final NumConstraint height)
{
return new RectConstraint(context) {

@ -11,12 +11,6 @@ import mightypork.utils.math.Calc;
*/
public class Rect {
/** Rect [0, 0, 1, 1] */
public static final Rect ONE = new Rect(0, 0, 1, 1);
/** Rect all zeros */
public static final Rect ZERO = new Rect(0, 0, 0, 0);
/**
* Rectangle from size
*
@ -751,10 +745,28 @@ public class Rect {
}
/**
* @return rect [0,0-1,1]
*/
public static Rect one()
{
return new Rect(0, 0, 1, 1);
}
/**
* @return rect [0,0-0,0]
*/
public static Rect zero()
{
return new Rect(0, 0, 0, 0);
}
@Override
public String toString()
{
return String.format("[( %4d, %4d )-( %4d, %4d )]", (int) min.x, (int) min.y, (int) max.x, (int) max.y);
return String.format("[( %4.1f; %4.1f )-( %4.1f; %4.1f )]", min.x, min.y, max.x, max.y);
}

Loading…
Cancel
Save