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

This commit is contained in:
Ondřej Hruška
2014-04-04 23:06:56 +02:00
parent 7f35d1d316
commit 53b7b02609
68 changed files with 1887 additions and 1929 deletions
+120 -116
View File
@@ -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
Log.f1("Initializing subsystems...");
/*
* Event bus
*/
Log.f2("Initializing Event Bus...");
eventBus = new EventBus();
eventBus.enableLogging(Config.LOG_BUS);
initChannels();
// Subsystems
textureRegistry = new TextureRegistry(this);
/*
* 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;
}
}
+2 -9
View File
@@ -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
+2 -10
View File
@@ -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();
}
}
+1
View File
@@ -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;
}
+1 -2
View File
@@ -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;
+21
View File
@@ -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();
}
+112
View File
@@ -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();
}
};
}
+87
View File
@@ -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);
}
}
-33
View File
@@ -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);
}
+30
View File
@@ -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;
}
}
+90
View File
@@ -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
*
+4 -1
View File
@@ -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,10 +180,6 @@ public abstract class Screen extends Subsystem implements Updateable, KeyBinder,
public final void update(double delta)
{
updateScreen(delta);
renderBegin();
renderScreen();
renderEnd();
}
@@ -228,4 +189,56 @@ public abstract class Screen extends Subsystem implements Updateable, KeyBinder,
return disp().getRect();
}
public final void render()
{
if (!isActive()) return;
if (needSetupGraphics) {
setupGraphics();
}
if (needSetupViewport) {
setupViewport();
}
renderBegin();
renderScreen();
renderEnd();
}
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);
}
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);
}
}
+41 -15
View File
@@ -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();
+349
View File
@@ -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
-70
View File
@@ -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.
+240 -78
View File
@@ -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,81 +57,24 @@ 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;
}
/**
* Remove a {@link EventChannel} from this bus
*
* @param channel true if channel was removed
*/
public void removeChannel(EventChannel<?, ?> channel)
{
channels.remove(channel);
}
/**
* Broadcast a message
*
* @param message message
* @return true if message was accepted by at least one channel
*/
public boolean broadcast(Object message)
{
if (logging) Log.f3("<bus> Broadcasting: " + message);
boolean sent = false;
for (EventChannel<?, ?> b : channels) {
sent |= b.broadcast(message, clients);
}
if (!sent && logging) Log.w("Message not accepted by any channel: " + message);
return sent;
}
/**
* Connect a client to the bus. The client will be connected to all current
* and future channels, until removed from the bus.
*
* @param client the client
* @return true on success
*/
public boolean subscribe(Object client)
{
if (client == null) return false;
clients.add(client);
return true;
}
/**
* Disconnect a client from the bus.
*
* @param client the client
*/
public void unsubscribe(Object client)
{
clients.remove(client);
}
/**
* Add a channel for given message and client type.
*
@@ -129,11 +82,220 @@ final public class EventBus {
* @param clientClass client type
* @return the created channel instance
*/
public <F_EVENT extends Handleable<F_CLIENT>, F_CLIENT> EventChannel<?, ?> createChannel(Class<F_EVENT> messageClass, Class<F_CLIENT> clientClass)
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);
channel.enableLogging(logging);
return addChannel(channel);
}
/**
* Remove a {@link EventChannel} from this bus
*
* @param channel true if channel was removed
*/
public void removeChannel(EventChannel<?, ?> channel)
{
assertLive();
channels.remove(channel);
}
/**
* Add message to a queue
*
* @param message message
*/
public void queue(Event<?> message)
{
assertLive();
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();
DelayedMessage dm = new DelayedMessage(delay, message);
if (logging) Log.f3("<bus> + [ Queuing: " + Log.str(message) + " ]");
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);
}
}
/**
* Connect a client to the bus. The client will be connected to all current
* and future channels, until removed from the bus.
*
* @param client the client
*/
public void subscribe(Object client)
{
assertLive();
if (client == null) return;
clients.add(client);
if (logging) Log.f3("<bus> ADDING CLIENT " + client);
}
/**
* Disconnect a client from the bus.
*
* @param client the client
*/
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());
}
}
}
}
/**
* 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.
*
* @throws IllegalStateException if the bus is dead.
*/
private void assertLive() throws IllegalStateException
{
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;
+31
View File
@@ -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) {
+19 -7
View File
@@ -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);
}