Done: Audio, Graphics. Tbd: Input, cleanup Config, cleanup old BaseApp etcmaster
parent
eee9ed14dc
commit
3c0763a4c8
@ -0,0 +1,38 @@ |
|||||||
|
package mightypork.gamecore.backend.lwjgl; |
||||||
|
|
||||||
|
|
||||||
|
import mightypork.gamecore.core.modules.App; |
||||||
|
import mightypork.gamecore.initializers.InitTask; |
||||||
|
import mightypork.gamecore.util.SlickLogRedirector; |
||||||
|
import mightypork.utils.logging.writers.LogWriter; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Initializer that redirects slick logging to main logger. |
||||||
|
* |
||||||
|
* @author Ondřej Hruška (MightyPork) |
||||||
|
*/ |
||||||
|
public class InitTaskRedirectSlickLog extends InitTask { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void run(App app) |
||||||
|
{ |
||||||
|
LogWriter ml = mightypork.utils.logging.Log.getMainLogger(); |
||||||
|
SlickLogRedirector slr = new SlickLogRedirector(ml); |
||||||
|
org.newdawn.slick.util.Log.setLogSystem(slr); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public String getName() |
||||||
|
{ |
||||||
|
return "slick_log"; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public String[] getDependencies() |
||||||
|
{ |
||||||
|
return new String[] { "log" }; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,171 @@ |
|||||||
|
package mightypork.gamecore.backend.lwjgl; |
||||||
|
|
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
import java.io.InputStream; |
||||||
|
|
||||||
|
import mightypork.gamecore.resources.audio.DeferredAudio; |
||||||
|
import mightypork.gamecore.resources.audio.SoundSystem; |
||||||
|
import mightypork.utils.files.FileUtils; |
||||||
|
import mightypork.utils.math.constraints.vect.Vect; |
||||||
|
|
||||||
|
import org.lwjgl.openal.AL10; |
||||||
|
import org.newdawn.slick.openal.Audio; |
||||||
|
import org.newdawn.slick.openal.SoundStore; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* SlickUtil-based deferred audio resource. |
||||||
|
* |
||||||
|
* @author Ondřej Hruška (MightyPork) |
||||||
|
*/ |
||||||
|
public class SlickAudio extends DeferredAudio { |
||||||
|
|
||||||
|
private double pauseLoopPosition = 0; |
||||||
|
private boolean looping = false; |
||||||
|
private boolean paused = false; |
||||||
|
private double lastPlayPitch = 1; |
||||||
|
private double lastPlayGain = 1; |
||||||
|
|
||||||
|
/** Audio resource */ |
||||||
|
private Audio backingAudio = null; |
||||||
|
private int sourceID; |
||||||
|
|
||||||
|
|
||||||
|
public SlickAudio(String resourceName) { |
||||||
|
super(resourceName); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
protected void loadResource(String resource) throws IOException |
||||||
|
{ |
||||||
|
final String ext = FileUtils.getExtension(resource); |
||||||
|
|
||||||
|
try (final InputStream stream = FileUtils.getResource(resource)) { |
||||||
|
|
||||||
|
if (ext.equalsIgnoreCase("ogg")) { |
||||||
|
backingAudio = SoundStore.get().getOgg(resource, stream); |
||||||
|
|
||||||
|
} else if (ext.equalsIgnoreCase("wav")) { |
||||||
|
backingAudio = SoundStore.get().getWAV(resource, stream); |
||||||
|
|
||||||
|
} else if (ext.equalsIgnoreCase("aif")) { |
||||||
|
backingAudio = SoundStore.get().getAIF(resource, stream); |
||||||
|
|
||||||
|
} else if (ext.equalsIgnoreCase("mod")) { |
||||||
|
backingAudio = SoundStore.get().getMOD(resource, stream); |
||||||
|
|
||||||
|
} else { |
||||||
|
throw new RuntimeException("Invalid audio file extension."); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void pauseLoop() |
||||||
|
{ |
||||||
|
if (!ensureLoaded()) return; |
||||||
|
|
||||||
|
if (isPlaying() && looping) { |
||||||
|
pauseLoopPosition = backingAudio.getPosition(); |
||||||
|
stop(); |
||||||
|
paused = true; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void resumeLoop() |
||||||
|
{ |
||||||
|
if (!ensureLoaded()) return; |
||||||
|
|
||||||
|
if (looping && paused) { |
||||||
|
sourceID = backingAudio.playAsSoundEffect((float) lastPlayPitch, (float) lastPlayGain, true); |
||||||
|
backingAudio.setPosition((float) pauseLoopPosition); |
||||||
|
paused = false; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void adjustGain(double gain) |
||||||
|
{ |
||||||
|
AL10.alSourcef(sourceID, AL10.AL_GAIN, (float) gain); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void stop() |
||||||
|
{ |
||||||
|
if (!isLoaded()) return; |
||||||
|
|
||||||
|
backingAudio.stop(); |
||||||
|
paused = false; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isPlaying() |
||||||
|
{ |
||||||
|
if (!isLoaded()) return false; |
||||||
|
|
||||||
|
return backingAudio.isPlaying(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public boolean isPaused() |
||||||
|
{ |
||||||
|
if (!isLoaded()) return false; |
||||||
|
|
||||||
|
return backingAudio.isPaused(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void play(double pitch, double gain, boolean loop) |
||||||
|
{ |
||||||
|
play(pitch, gain, loop, SoundSystem.getListener()); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void play(double pitch, double gain, boolean loop, double x, double y) |
||||||
|
{ |
||||||
|
play(pitch, gain, loop, x, y, SoundSystem.getListener().z()); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void play(double pitch, double gain, boolean loop, double x, double y, double z) |
||||||
|
{ |
||||||
|
if (!ensureLoaded()) return; |
||||||
|
|
||||||
|
this.lastPlayPitch = pitch; |
||||||
|
this.lastPlayGain = gain; |
||||||
|
looping = loop; |
||||||
|
|
||||||
|
sourceID = backingAudio.playAsSoundEffect((float) pitch, (float) gain, loop, (float) x, (float) y, (float) z); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void play(double pitch, double gain, boolean loop, Vect pos) |
||||||
|
{ |
||||||
|
if (!ensureLoaded()) return; |
||||||
|
|
||||||
|
play(pitch, gain, loop, pos.x(), pos.y(), pos.z()); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void destroy() |
||||||
|
{ |
||||||
|
if (!isLoaded() || backingAudio == null) return; |
||||||
|
|
||||||
|
backingAudio.release(); |
||||||
|
backingAudio = null; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,91 @@ |
|||||||
|
package mightypork.gamecore.backend.lwjgl; |
||||||
|
|
||||||
|
|
||||||
|
import java.nio.FloatBuffer; |
||||||
|
|
||||||
|
import org.lwjgl.openal.AL; |
||||||
|
import org.lwjgl.openal.AL10; |
||||||
|
import org.newdawn.slick.openal.SoundStore; |
||||||
|
|
||||||
|
import mightypork.gamecore.core.modules.App; |
||||||
|
import mightypork.gamecore.resources.audio.AudioModule; |
||||||
|
import mightypork.gamecore.resources.audio.AudioReadyEvent; |
||||||
|
import mightypork.gamecore.resources.audio.DeferredAudio; |
||||||
|
import mightypork.gamecore.util.BufferHelper; |
||||||
|
import mightypork.utils.logging.Log; |
||||||
|
import mightypork.utils.math.constraints.vect.Vect; |
||||||
|
import mightypork.utils.math.constraints.vect.var.VectVar; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* SlickUtil-based audio module |
||||||
|
* |
||||||
|
* @author Ondřej Hruška (MightyPork) |
||||||
|
*/ |
||||||
|
public class SlickAudioModule extends AudioModule { |
||||||
|
|
||||||
|
private static final Vect INITIAL_LISTENER_POS = Vect.ZERO; |
||||||
|
private static final int MAX_SOURCES = 256; |
||||||
|
|
||||||
|
private VectVar listener = Vect.makeVar(); |
||||||
|
private static boolean soundSystemInited = false; |
||||||
|
|
||||||
|
|
||||||
|
public SlickAudioModule() { |
||||||
|
|
||||||
|
if (!soundSystemInited) { |
||||||
|
soundSystemInited = true; |
||||||
|
|
||||||
|
try { |
||||||
|
SoundStore.get().setMaxSources(MAX_SOURCES); |
||||||
|
SoundStore.get().init(); |
||||||
|
setListenerPos(INITIAL_LISTENER_POS); |
||||||
|
|
||||||
|
App.bus().send(new AudioReadyEvent()); |
||||||
|
} catch (final Throwable t) { |
||||||
|
Log.e("Error initializing sound system.", t); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void setListenerPos(Vect pos) |
||||||
|
{ |
||||||
|
listener.setTo(pos); |
||||||
|
final FloatBuffer buf3 = BufferHelper.alloc(3); |
||||||
|
final FloatBuffer buf6 = BufferHelper.alloc(6); |
||||||
|
buf3.clear(); |
||||||
|
BufferHelper.fill(buf3, (float) pos.x(), (float) pos.y(), (float) pos.z()); |
||||||
|
AL10.alListener(AL10.AL_POSITION, buf3); |
||||||
|
buf3.clear(); |
||||||
|
BufferHelper.fill(buf3, 0, 0, 0); |
||||||
|
AL10.alListener(AL10.AL_VELOCITY, buf3); |
||||||
|
buf6.clear(); |
||||||
|
BufferHelper.fill(buf6, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f); |
||||||
|
AL10.alListener(AL10.AL_ORIENTATION, buf6); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public Vect getListenerPos() |
||||||
|
{ |
||||||
|
return listener; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
protected void deinitSoundSystem() |
||||||
|
{ |
||||||
|
SoundStore.get().clear(); |
||||||
|
AL.destroy(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
protected DeferredAudio doCreateResource(String res) |
||||||
|
{ |
||||||
|
return new SlickAudio(res); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -1,13 +0,0 @@ |
|||||||
package mightypork.gamecore.core.config; |
|
||||||
|
|
||||||
|
|
||||||
import mightypork.utils.files.config.PropertyManager; |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* Config setup, class used to populate the config file. |
|
||||||
*/ |
|
||||||
public interface ConfigSetup { |
|
||||||
|
|
||||||
void addOptions(PropertyManager prop); |
|
||||||
} |
|
@ -1,30 +0,0 @@ |
|||||||
package mightypork.gamecore.core.config; |
|
||||||
|
|
||||||
|
|
||||||
import mightypork.gamecore.input.KeyStroke; |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* Key options - restricted access to {@link Config} for keys |
|
||||||
*/ |
|
||||||
public class KeyOpts { |
|
||||||
|
|
||||||
public void add(String cfgKey, String dataString) |
|
||||||
{ |
|
||||||
add(cfgKey, dataString, null); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* @param cfgKey key in config file |
|
||||||
* @param dataString string representing the keystroke (format for |
|
||||||
* {@link KeyStroke}) |
|
||||||
* @param comment optional comment |
|
||||||
*/ |
|
||||||
public void add(String cfgKey, String dataString, String comment) |
|
||||||
{ |
|
||||||
final KeyProperty kprop = new KeyProperty(Config.prefixKey(cfgKey), KeyStroke.createFromDataString(dataString), comment); |
|
||||||
Config.strokes.put(Config.prefixKey(cfgKey), kprop); |
|
||||||
Config.cfg.putProperty(kprop); |
|
||||||
} |
|
||||||
} |
|
@ -1,10 +0,0 @@ |
|||||||
package mightypork.gamecore.core.config; |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* Key configurator. Config access restricted to key options. |
|
||||||
*/ |
|
||||||
public interface KeySetup { |
|
||||||
|
|
||||||
public void addKeys(KeyOpts keys); |
|
||||||
} |
|
@ -1,34 +0,0 @@ |
|||||||
package mightypork.gamecore.core.modules; |
|
||||||
|
|
||||||
|
|
||||||
import mightypork.gamecore.input.InputSystem; |
|
||||||
import mightypork.gamecore.resources.audio.SoundSystem; |
|
||||||
import mightypork.utils.eventbus.BusAccess; |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* App interface visible to subsystems |
|
||||||
* |
|
||||||
* @author Ondřej Hruška (MightyPork) |
|
||||||
*/ |
|
||||||
public interface AppAccess extends BusAccess { |
|
||||||
|
|
||||||
/** |
|
||||||
* @return sound system |
|
||||||
*/ |
|
||||||
abstract SoundSystem getSoundSystem(); |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* @return input system |
|
||||||
*/ |
|
||||||
abstract InputSystem getInput(); |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* Quit to OS<br> |
|
||||||
* Destroy app & exit VM |
|
||||||
*/ |
|
||||||
abstract void shutdown(); |
|
||||||
|
|
||||||
} |
|
@ -1,56 +0,0 @@ |
|||||||
package mightypork.gamecore.core.modules; |
|
||||||
|
|
||||||
|
|
||||||
import mightypork.gamecore.input.InputSystem; |
|
||||||
import mightypork.gamecore.resources.audio.SoundSystem; |
|
||||||
import mightypork.utils.eventbus.EventBus; |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* App access adapter (defualt {@link AppAccess} implementation) |
|
||||||
* |
|
||||||
* @author Ondřej Hruška (MightyPork) |
|
||||||
*/ |
|
||||||
public class AppAccessAdapter implements AppAccess { |
|
||||||
|
|
||||||
private final AppAccess app; |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* @param app app access |
|
||||||
*/ |
|
||||||
public AppAccessAdapter(AppAccess app) { |
|
||||||
if (app == null) throw new NullPointerException("AppAccess instance cannot be null."); |
|
||||||
|
|
||||||
this.app = app; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
@Override |
|
||||||
public final SoundSystem getSoundSystem() |
|
||||||
{ |
|
||||||
return app.getSoundSystem(); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
@Override |
|
||||||
public final InputSystem getInput() |
|
||||||
{ |
|
||||||
return app.getInput(); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
@Override |
|
||||||
public final EventBus getEventBus() |
|
||||||
{ |
|
||||||
return app.getEventBus(); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
@Override |
|
||||||
public final void shutdown() |
|
||||||
{ |
|
||||||
app.shutdown(); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,52 +0,0 @@ |
|||||||
package mightypork.gamecore.core.modules; |
|
||||||
|
|
||||||
|
|
||||||
import mightypork.gamecore.input.InputSystem; |
|
||||||
import mightypork.gamecore.resources.audio.SoundSystem; |
|
||||||
import mightypork.utils.eventbus.clients.RootBusNode; |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* App event bus client, to be used for subsystems, screens and anything that |
|
||||||
* needs access to the eventbus and other systems; Attached directly to bus. |
|
||||||
* |
|
||||||
* @author Ondřej Hruška (MightyPork) |
|
||||||
*/ |
|
||||||
public abstract class AppModule extends RootBusNode implements AppAccess { |
|
||||||
|
|
||||||
private final AppAccess app; |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* Create a module |
|
||||||
* |
|
||||||
* @param app access to app systems |
|
||||||
*/ |
|
||||||
public AppModule(AppAccess app) { |
|
||||||
super(app); |
|
||||||
|
|
||||||
this.app = app; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
@Override |
|
||||||
public final SoundSystem getSoundSystem() |
|
||||||
{ |
|
||||||
return app.getSoundSystem(); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
@Override |
|
||||||
public final InputSystem getInput() |
|
||||||
{ |
|
||||||
return app.getInput(); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
@Override |
|
||||||
public final void shutdown() |
|
||||||
{ |
|
||||||
app.shutdown(); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,54 +0,0 @@ |
|||||||
package mightypork.gamecore.core.modules; |
|
||||||
|
|
||||||
|
|
||||||
import mightypork.gamecore.input.InputSystem; |
|
||||||
import mightypork.gamecore.resources.audio.SoundSystem; |
|
||||||
import mightypork.utils.eventbus.clients.BusNode; |
|
||||||
import mightypork.utils.eventbus.clients.DelegatingClient; |
|
||||||
import mightypork.utils.eventbus.clients.RootBusNode; |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* Delegating bus client, to be attached to any {@link DelegatingClient}, such |
|
||||||
* as a {@link RootBusNode}. |
|
||||||
* |
|
||||||
* @author Ondřej Hruška (MightyPork) |
|
||||||
*/ |
|
||||||
public class AppSubModule extends BusNode implements AppAccess { |
|
||||||
|
|
||||||
private final AppAccess app; |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* Create submodule |
|
||||||
* |
|
||||||
* @param app access to app systems |
|
||||||
*/ |
|
||||||
public AppSubModule(AppAccess app) { |
|
||||||
super(app); |
|
||||||
|
|
||||||
this.app = app; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
@Override |
|
||||||
public final SoundSystem getSoundSystem() |
|
||||||
{ |
|
||||||
return app.getSoundSystem(); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
@Override |
|
||||||
public final InputSystem getInput() |
|
||||||
{ |
|
||||||
return app.getInput(); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
@Override |
|
||||||
public final void shutdown() |
|
||||||
{ |
|
||||||
app.shutdown(); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -0,0 +1,47 @@ |
|||||||
|
package mightypork.gamecore.initializers; |
||||||
|
|
||||||
|
|
||||||
|
import mightypork.gamecore.core.modules.App; |
||||||
|
import mightypork.utils.annotations.Stub; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* App initializer. A sequence of initializers is executed once the start() |
||||||
|
* method on App is called. Adding initializers is one way to customize the App |
||||||
|
* behavior and features. |
||||||
|
* |
||||||
|
* @author Ondřej Hruška (MightyPork) |
||||||
|
*/ |
||||||
|
public abstract class InitTask { |
||||||
|
|
||||||
|
/** |
||||||
|
* Run the initalizer on app. |
||||||
|
* |
||||||
|
* @param app the app instance. |
||||||
|
*/ |
||||||
|
public abstract void run(App app); |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Get name of this initializer (for dependency resolver).<br> |
||||||
|
* The name should be short, snake_case and precise. |
||||||
|
* |
||||||
|
* @return name |
||||||
|
*/ |
||||||
|
public abstract String getName(); |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Get what other initializers must be already loaded before this can load.<br> |
||||||
|
* Depending on itself or creating a circular dependency will cause error.<br> |
||||||
|
* If the dependencies cannot be satisfied, the initialization sequence will |
||||||
|
* be aborted. |
||||||
|
* |
||||||
|
* @return array of names of required initializers. |
||||||
|
*/ |
||||||
|
@Stub |
||||||
|
public String[] getDependencies() |
||||||
|
{ |
||||||
|
return new String[] {}; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,79 @@ |
|||||||
|
package mightypork.gamecore.initializers; |
||||||
|
|
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.HashSet; |
||||||
|
import java.util.Iterator; |
||||||
|
import java.util.List; |
||||||
|
import java.util.Set; |
||||||
|
|
||||||
|
import mightypork.utils.logging.Log; |
||||||
|
|
||||||
|
|
||||||
|
public class InitTaskResolver { |
||||||
|
|
||||||
|
/** |
||||||
|
* Order init tasks so that all dependencies are loaded before thye are |
||||||
|
* needed by the tasks. |
||||||
|
* |
||||||
|
* @param tasks task list |
||||||
|
* @return task list ordered |
||||||
|
*/ |
||||||
|
public static List<InitTask> order(List<InitTask> tasks) |
||||||
|
{ |
||||||
|
List<InitTask> remaining = new ArrayList<>(tasks); |
||||||
|
|
||||||
|
List<InitTask> ordered = new ArrayList<>(); |
||||||
|
Set<String> loaded = new HashSet<>(); |
||||||
|
|
||||||
|
// resolve task order
|
||||||
|
int addedThisIteration = 0; |
||||||
|
do { |
||||||
|
for (Iterator<InitTask> i = remaining.iterator(); i.hasNext();) { |
||||||
|
InitTask task = i.next(); |
||||||
|
|
||||||
|
String[] deps = task.getDependencies(); |
||||||
|
if (deps == null) deps = new String[] {}; |
||||||
|
|
||||||
|
int unmetDepsCount = deps.length; |
||||||
|
|
||||||
|
for (String d : deps) { |
||||||
|
if (loaded.contains(d)) unmetDepsCount--; |
||||||
|
} |
||||||
|
|
||||||
|
if (unmetDepsCount == 0) { |
||||||
|
ordered.add(task); |
||||||
|
loaded.add(task.getName()); |
||||||
|
i.remove(); |
||||||
|
addedThisIteration++; |
||||||
|
} |
||||||
|
} |
||||||
|
} while (addedThisIteration > 0); |
||||||
|
|
||||||
|
// check if any tasks are left out
|
||||||
|
if (remaining.size() > 0) { |
||||||
|
|
||||||
|
// build error message for each bad task
|
||||||
|
for (InitTask task : remaining) { |
||||||
|
String notSatisfied = ""; |
||||||
|
|
||||||
|
for (String d : task.getDependencies()) { |
||||||
|
if (!loaded.contains(d)) { |
||||||
|
|
||||||
|
if (!notSatisfied.isEmpty()) { |
||||||
|
notSatisfied += ", "; |
||||||
|
} |
||||||
|
|
||||||
|
notSatisfied += d; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Log.w("InitTask \"" + task.getName() + "\" - missing dependencies: " + notSatisfied); |
||||||
|
} |
||||||
|
|
||||||
|
throw new RuntimeException("Some InitTask dependencies could not be satisfied."); |
||||||
|
} |
||||||
|
|
||||||
|
return ordered; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,74 @@ |
|||||||
|
package mightypork.gamecore.initializers.tasks; |
||||||
|
|
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
|
||||||
|
import mightypork.gamecore.core.modules.App; |
||||||
|
import mightypork.gamecore.initializers.InitTask; |
||||||
|
import mightypork.utils.ion.Ion; |
||||||
|
import mightypork.utils.ion.IonInput; |
||||||
|
import mightypork.utils.ion.IonOutput; |
||||||
|
import mightypork.utils.ion.IonizerBinary; |
||||||
|
import mightypork.utils.math.algo.Coord; |
||||||
|
import mightypork.utils.math.algo.Move; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Register extra ionizables added by the game library (non-native ION types).<br> |
||||||
|
* This initializer can be called anywhere in the initialization sequence. |
||||||
|
* |
||||||
|
* @author Ondřej Hruška (MightyPork) |
||||||
|
*/ |
||||||
|
public class InitTaskRegisterIonizables extends InitTask { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void run(App app) |
||||||
|
{ |
||||||
|
Ion.registerIndirect(255, new IonizerBinary<Coord>() { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void save(Coord object, IonOutput out) throws IOException |
||||||
|
{ |
||||||
|
out.writeInt(object.x); |
||||||
|
out.writeInt(object.y); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public Coord load(IonInput in) throws IOException |
||||||
|
{ |
||||||
|
final int x = in.readInt(); |
||||||
|
final int y = in.readInt(); |
||||||
|
return new Coord(x, y); |
||||||
|
} |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
Ion.registerIndirect(254, new IonizerBinary<Move>() { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void save(Move object, IonOutput out) throws IOException |
||||||
|
{ |
||||||
|
out.writeInt(object.x()); |
||||||
|
out.writeInt(object.y()); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public Move load(IonInput in) throws IOException |
||||||
|
{ |
||||||
|
final int x = in.readInt(); |
||||||
|
final int y = in.readInt(); |
||||||
|
return new Move(x, y); |
||||||
|
} |
||||||
|
|
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public String getName() |
||||||
|
{ |
||||||
|
return "ion"; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,42 @@ |
|||||||
|
package mightypork.gamecore.initializers.tasks; |
||||||
|
|
||||||
|
|
||||||
|
import java.lang.Thread.UncaughtExceptionHandler; |
||||||
|
|
||||||
|
import mightypork.gamecore.core.modules.App; |
||||||
|
import mightypork.gamecore.initializers.InitTask; |
||||||
|
import mightypork.utils.annotations.Stub; |
||||||
|
import mightypork.utils.logging.Log; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Add a crash handler to the app.<br> |
||||||
|
* For customized crash message / crash dialog etc, override the |
||||||
|
* uncaughtException() method. |
||||||
|
* |
||||||
|
* @author Ondřej Hruška (MightyPork) |
||||||
|
*/ |
||||||
|
public class InitTaskSetupCrashHandler extends InitTask implements UncaughtExceptionHandler { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void run(App app) |
||||||
|
{ |
||||||
|
Thread.setDefaultUncaughtExceptionHandler(this); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
@Stub |
||||||
|
public void uncaughtException(Thread thread, Throwable throwable) |
||||||
|
{ |
||||||
|
Log.e("The game has crashed.", throwable); |
||||||
|
App.shutdown(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public String getName() |
||||||
|
{ |
||||||
|
return "crash_handler"; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,100 @@ |
|||||||
|
package mightypork.gamecore.initializers.tasks; |
||||||
|
|
||||||
|
|
||||||
|
import mightypork.gamecore.core.modules.App; |
||||||
|
import mightypork.gamecore.initializers.InitTask; |
||||||
|
import mightypork.gamecore.render.GraphicsModule; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Setup main window. |
||||||
|
* |
||||||
|
* @author Ondřej Hruška (MightyPork) |
||||||
|
*/ |
||||||
|
public class InitTaskSetupDisplay extends InitTask { |
||||||
|
|
||||||
|
private int width = 800, height = 600, fps = 60; |
||||||
|
private boolean resizable, fullscreen; |
||||||
|
private String title = "Game"; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Set initial window size |
||||||
|
* |
||||||
|
* @param width width (px) |
||||||
|
* @param height height (px) |
||||||
|
*/ |
||||||
|
public void setSize(int width, int height) |
||||||
|
{ |
||||||
|
this.width = width; |
||||||
|
this.height = height; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Set whether the window should be resizable |
||||||
|
* |
||||||
|
* @param resizable true for resizable |
||||||
|
*/ |
||||||
|
public void setResizable(boolean resizable) |
||||||
|
{ |
||||||
|
this.resizable = resizable; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Set window title |
||||||
|
* |
||||||
|
* @param title title text |
||||||
|
*/ |
||||||
|
public void setTitle(String title) |
||||||
|
{ |
||||||
|
this.title = title; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Set desired framerate. |
||||||
|
* |
||||||
|
* @param fps FPS |
||||||
|
*/ |
||||||
|
public void setTargetFps(int fps) |
||||||
|
{ |
||||||
|
this.fps = fps; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Set whether the window should start in fullscreen |
||||||
|
* |
||||||
|
* @param fullscreen true for fullscreen |
||||||
|
*/ |
||||||
|
public void setFullscreen(boolean fullscreen) |
||||||
|
{ |
||||||
|
this.fullscreen = fullscreen; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void run(App app) |
||||||
|
{ |
||||||
|
GraphicsModule gfx = app.getBackend().getGraphics(); |
||||||
|
|
||||||
|
gfx.setSize(width, height); |
||||||
|
gfx.setResizable(resizable); |
||||||
|
gfx.setTitle(title); |
||||||
|
gfx.setTargetFps(fps); |
||||||
|
|
||||||
|
if (fullscreen) gfx.setFullscreen(true); |
||||||
|
|
||||||
|
gfx.createDisplay(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public String getName() |
||||||
|
{ |
||||||
|
return "display"; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,105 @@ |
|||||||
|
package mightypork.gamecore.initializers.tasks; |
||||||
|
|
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
import java.util.logging.Level; |
||||||
|
|
||||||
|
import mightypork.gamecore.core.WorkDir; |
||||||
|
import mightypork.gamecore.core.modules.App; |
||||||
|
import mightypork.gamecore.initializers.InitTask; |
||||||
|
import mightypork.utils.logging.Log; |
||||||
|
import mightypork.utils.logging.writers.LogWriter; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Init main logger and console log printing.<br> |
||||||
|
* Must be called after workdir is initialized. |
||||||
|
* |
||||||
|
* @author Ondřej Hruška (MightyPork) |
||||||
|
*/ |
||||||
|
public class InitTaskSetupLog extends InitTask { |
||||||
|
|
||||||
|
private String logDir = "log"; |
||||||
|
private String logName = "runtime"; |
||||||
|
private int archiveCount = 5; |
||||||
|
|
||||||
|
private Level levelWrite = Level.ALL; |
||||||
|
private Level levelPrint = Level.ALL; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Set log directory (relative to workdir).<br> |
||||||
|
* Defaults to "log". |
||||||
|
* |
||||||
|
* @param logDir log directory. |
||||||
|
*/ |
||||||
|
public void setLogDir(String logDir) |
||||||
|
{ |
||||||
|
this.logDir = logDir; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Set log name. This name is used as a prefix for archived log files.<br> |
||||||
|
* Should contain only valid filename characters.<br> |
||||||
|
* Defaults to "runtime". |
||||||
|
* |
||||||
|
* @param logName log name |
||||||
|
*/ |
||||||
|
public void setLogName(String logName) |
||||||
|
{ |
||||||
|
// TODO validate characters
|
||||||
|
this.logName = logName; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Set number of logs to keep in the logs directory.<br> |
||||||
|
* Set to 0 to keep just the last log, -1 to keep unlimited number of logs.<br> |
||||||
|
* Defaults to 5. |
||||||
|
* |
||||||
|
* @param archiveCount logs to keep |
||||||
|
*/ |
||||||
|
public void setArchiveCount(int archiveCount) |
||||||
|
{ |
||||||
|
this.archiveCount = archiveCount; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Set logging levels (minimal level of message to be accepted)<br> |
||||||
|
* Defaults to ALL, ALL. |
||||||
|
* |
||||||
|
* @param levelWrite level for writing to file |
||||||
|
* @param levelPrint level for writing to stdout / stderr |
||||||
|
*/ |
||||||
|
public void setLevels(Level levelWrite, Level levelPrint) |
||||||
|
{ |
||||||
|
this.levelWrite = levelWrite; |
||||||
|
this.levelPrint = levelPrint; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void run(App app) |
||||||
|
{ |
||||||
|
final LogWriter log = Log.create(logName, new File(WorkDir.getDir(logDir), logName + ".log"), archiveCount); |
||||||
|
Log.setMainLogger(log); |
||||||
|
Log.setLevel(levelWrite); |
||||||
|
Log.setSysoutLevel(levelPrint); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public String getName() |
||||||
|
{ |
||||||
|
return "log"; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public String[] getDependencies() |
||||||
|
{ |
||||||
|
return new String[] { "workdir" }; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,113 @@ |
|||||||
|
package mightypork.gamecore.initializers.tasks; |
||||||
|
|
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.Map; |
||||||
|
import java.util.Map.Entry; |
||||||
|
|
||||||
|
import javax.swing.JOptionPane; |
||||||
|
|
||||||
|
import mightypork.gamecore.core.WorkDir; |
||||||
|
import mightypork.gamecore.core.modules.App; |
||||||
|
import mightypork.gamecore.initializers.InitTask; |
||||||
|
import mightypork.utils.annotations.Stub; |
||||||
|
import mightypork.utils.files.InstanceLock; |
||||||
|
import mightypork.utils.logging.Log; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Initializer that takes care of setting up the proper workdir. |
||||||
|
* |
||||||
|
* @author Ondřej Hruška (MightyPork) |
||||||
|
*/ |
||||||
|
public class InitTaskSetupWorkdir extends InitTask { |
||||||
|
|
||||||
|
private final File workdirPath; |
||||||
|
private boolean doLock; |
||||||
|
private String lockFile = ".lock"; |
||||||
|
private Map<String, String> namedPaths = new HashMap<>(); |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* @param workdir path to the working directory |
||||||
|
* @param lock whether to lock the directory (single instance mode) |
||||||
|
*/ |
||||||
|
public InitTaskSetupWorkdir(File workdir, boolean lock) { |
||||||
|
this.workdirPath = workdir; |
||||||
|
this.doLock = lock; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Set name of the lock file. |
||||||
|
* |
||||||
|
* @param lockFile |
||||||
|
*/ |
||||||
|
public void setLockFileName(String lockFile) |
||||||
|
{ |
||||||
|
this.lockFile = lockFile; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Add a named path |
||||||
|
* |
||||||
|
* @param alias path alias (snake_case) |
||||||
|
* @param path path (relative to the workdir) |
||||||
|
*/ |
||||||
|
public void addPath(String alias, String path) |
||||||
|
{ |
||||||
|
namedPaths.put(alias, path); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void run(App app) |
||||||
|
{ |
||||||
|
WorkDir.init(workdirPath); |
||||||
|
|
||||||
|
// lock working directory
|
||||||
|
if (doLock) { |
||||||
|
final File lock = WorkDir.getFile(lockFile); |
||||||
|
if (!InstanceLock.onFile(lock)) { |
||||||
|
onLockError(); |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
for (Entry<String, String> e : namedPaths.entrySet()) { |
||||||
|
WorkDir.addPath(e.getKey(), e.getValue()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Called when the lock file could not be obtained (cannot write or already |
||||||
|
* exists).<br> |
||||||
|
* Feel free to override this method to define custom behavior. |
||||||
|
*/ |
||||||
|
@Stub |
||||||
|
protected void onLockError() |
||||||
|
{ |
||||||
|
Log.e("Could not obtain lock file.\nOnly one instance can run at a time."); |
||||||
|
|
||||||
|
//@formatter:off
|
||||||
|
JOptionPane.showMessageDialog( |
||||||
|
null, |
||||||
|
"Another instance is already running.\n(Delete the "+lockFile +" file in the working directory to override)", |
||||||
|
"Lock Error", |
||||||
|
JOptionPane.ERROR_MESSAGE |
||||||
|
); |
||||||
|
//@formatter:on
|
||||||
|
|
||||||
|
App.shutdown(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public String getName() |
||||||
|
{ |
||||||
|
return "workdir"; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,53 @@ |
|||||||
|
package mightypork.gamecore.initializers.tasks; |
||||||
|
|
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
|
||||||
|
import mightypork.gamecore.core.WorkDir; |
||||||
|
import mightypork.gamecore.core.modules.App; |
||||||
|
import mightypork.gamecore.initializers.InitTask; |
||||||
|
import mightypork.utils.logging.Log; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* initializer task that writes a system info header to the log file.<br> |
||||||
|
* Must be called after log is initialized. |
||||||
|
* |
||||||
|
* @author Ondřej Hruška (MightyPork) |
||||||
|
*/ |
||||||
|
public class InitTaskWriteLogHeader extends InitTask { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void run(App app) |
||||||
|
{ |
||||||
|
String txt = ""; |
||||||
|
|
||||||
|
txt += "\n### SYSTEM INFO ###\n\n"; |
||||||
|
txt += " Platform ...... " + System.getProperty("os.name") + "\n"; |
||||||
|
txt += " Runtime ....... " + System.getProperty("java.runtime.name") + "\n"; |
||||||
|
txt += " Java .......... " + System.getProperty("java.version") + "\n"; |
||||||
|
txt += " Launch path ... " + System.getProperty("user.dir") + "\n"; |
||||||
|
|
||||||
|
try { |
||||||
|
txt += " Workdir ....... " + WorkDir.getWorkDir().getCanonicalPath() + "\n"; |
||||||
|
} catch (final IOException e) { |
||||||
|
Log.e(e); |
||||||
|
} |
||||||
|
|
||||||
|
Log.i(txt); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public String getName() |
||||||
|
{ |
||||||
|
return "log_header"; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public String[] getDependencies() |
||||||
|
{ |
||||||
|
return new String[] { "log", "workdir" }; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,12 @@ |
|||||||
|
package mightypork.gamecore.input; |
||||||
|
|
||||||
|
/** |
||||||
|
* Type of keystroke (falling / rising edge) |
||||||
|
*/ |
||||||
|
public enum Edge |
||||||
|
{ |
||||||
|
/** Activated by falling edge (press) */ |
||||||
|
FALLING, |
||||||
|
/** Activated by rising edge (release) */ |
||||||
|
RISING; |
||||||
|
} |
@ -0,0 +1,28 @@ |
|||||||
|
package mightypork.gamecore.plugins; |
||||||
|
|
||||||
|
|
||||||
|
import mightypork.gamecore.core.modules.App; |
||||||
|
import mightypork.utils.annotations.Stub; |
||||||
|
import mightypork.utils.eventbus.clients.BusNode; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* App plugin. Plugins are an easy way to extend app functionality.<br> |
||||||
|
* Typically, a plugin waits for trigger event(s) and performs some action upon |
||||||
|
* receiving them. |
||||||
|
* |
||||||
|
* @author Ondřej Hruška (MightyPork) |
||||||
|
*/ |
||||||
|
public class AppPlugin extends BusNode { |
||||||
|
|
||||||
|
/** |
||||||
|
* Initialize the plugin for the given App.<br> |
||||||
|
* The plugin is already attached to the event bus. |
||||||
|
* |
||||||
|
* @param app |
||||||
|
*/ |
||||||
|
@Stub |
||||||
|
public void initialize(App app) |
||||||
|
{ |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,34 @@ |
|||||||
|
package mightypork.gamecore.plugins.screenshot; |
||||||
|
|
||||||
|
|
||||||
|
import mightypork.gamecore.core.events.MainLoopRequest; |
||||||
|
import mightypork.gamecore.core.modules.App; |
||||||
|
import mightypork.gamecore.plugins.AppPlugin; |
||||||
|
import mightypork.utils.Support; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* This plugin waits for a {@link ScreenshotRequest} event.<br> |
||||||
|
* Upon receiving it, a screenshot is captured and written to file |
||||||
|
* asynchronously. |
||||||
|
* |
||||||
|
* @author Ondřej Hruška (MightyPork) |
||||||
|
*/ |
||||||
|
public class ScreenshotPlugin extends AppPlugin { |
||||||
|
|
||||||
|
/** |
||||||
|
* Take screenshot. Called by the trigger event. |
||||||
|
*/ |
||||||
|
void takeScreenshot() |
||||||
|
{ |
||||||
|
App.bus().send(new MainLoopRequest(new Runnable() { |
||||||
|
|
||||||
|
@Override |
||||||
|
public void run() |
||||||
|
{ |
||||||
|
Runnable tts = new TaskTakeScreenshot(); |
||||||
|
Support.runAsThread(tts); |
||||||
|
} |
||||||
|
})); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,22 @@ |
|||||||
|
package mightypork.gamecore.plugins.screenshot; |
||||||
|
|
||||||
|
|
||||||
|
import mightypork.utils.eventbus.BusEvent; |
||||||
|
import mightypork.utils.eventbus.events.flags.SingleReceiverEvent; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Event used to request screenshot capture. |
||||||
|
* |
||||||
|
* @author MightyPork |
||||||
|
*/ |
||||||
|
@SingleReceiverEvent |
||||||
|
public class ScreenshotRequest extends BusEvent<ScreenshotPlugin> { |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void handleBy(ScreenshotPlugin handler) |
||||||
|
{ |
||||||
|
handler.takeScreenshot(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,100 @@ |
|||||||
|
package mightypork.gamecore.plugins.screenshot; |
||||||
|
|
||||||
|
|
||||||
|
import java.io.File; |
||||||
|
import java.io.IOException; |
||||||
|
|
||||||
|
import mightypork.gamecore.core.WorkDir; |
||||||
|
import mightypork.gamecore.core.modules.App; |
||||||
|
import mightypork.gamecore.render.Screenshot; |
||||||
|
import mightypork.utils.Support; |
||||||
|
import mightypork.utils.logging.Log; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Task that takes screenshot and asynchronously saves it to a file.<br> |
||||||
|
* Can be run in a separate thread, but must be instantiated in the render |
||||||
|
* thread. |
||||||
|
* |
||||||
|
* @author MightyPork |
||||||
|
*/ |
||||||
|
public class TaskTakeScreenshot implements Runnable { |
||||||
|
|
||||||
|
private final Screenshot scr; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Take screenshot. Must be called in render thread. |
||||||
|
*/ |
||||||
|
public TaskTakeScreenshot() { |
||||||
|
scr = App.gfx().takeScreenshot(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void run() |
||||||
|
{ |
||||||
|
// generate unique filename
|
||||||
|
final File file = getScreenshotFile(); |
||||||
|
|
||||||
|
Log.f3("Saving screenshot to file: " + file); |
||||||
|
|
||||||
|
// save to disk
|
||||||
|
try { |
||||||
|
scr.save(file); |
||||||
|
} catch (final IOException e) { |
||||||
|
Log.e("Failed to save screenshot.", e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* @return File to save the screenshot to. |
||||||
|
*/ |
||||||
|
protected File getScreenshotFile() |
||||||
|
{ |
||||||
|
final String fname = getBaseFilename(); |
||||||
|
return findFreeFile(fname); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* @return directory for screenshots |
||||||
|
*/ |
||||||
|
protected File getScreenshotDirectory() |
||||||
|
{ |
||||||
|
return WorkDir.getDir("_screenshot_dir"); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Get base filename for the screenshot, without extension. |
||||||
|
* |
||||||
|
* @return filename |
||||||
|
*/ |
||||||
|
protected String getBaseFilename() |
||||||
|
{ |
||||||
|
return Support.getTime("yyyy-MM-dd_HH-mm-ss"); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Find first free filename for the screenshot, by adding -NUMBER after the |
||||||
|
* base filename and before extension. |
||||||
|
* |
||||||
|
* @param base_name base filename |
||||||
|
* @return full path to screenshot file |
||||||
|
*/ |
||||||
|
protected File findFreeFile(String base_name) |
||||||
|
{ |
||||||
|
File file; |
||||||
|
int index = 0; |
||||||
|
while (true) { |
||||||
|
file = new File(getScreenshotDirectory(), base_name + (index > 0 ? "-" + index : "") + ".png"); |
||||||
|
if (!file.exists()) break; |
||||||
|
index++; |
||||||
|
} |
||||||
|
return file; |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -1,57 +0,0 @@ |
|||||||
package mightypork.gamecore.render; |
|
||||||
|
|
||||||
|
|
||||||
import java.io.File; |
|
||||||
import java.io.IOException; |
|
||||||
|
|
||||||
import mightypork.gamecore.core.WorkDir; |
|
||||||
import mightypork.gamecore.core.modules.App; |
|
||||||
import mightypork.utils.Support; |
|
||||||
import mightypork.utils.logging.Log; |
|
||||||
|
|
||||||
import org.newdawn.slick.opengl.GLUtils; |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* Task that takes screenshot and asynchronously saves it to a file.<br> |
|
||||||
* Can be run in a separate thread, but must be instantiated in the render |
|
||||||
* thread. |
|
||||||
* |
|
||||||
* @author MightyPork |
|
||||||
*/ |
|
||||||
public class TaskTakeScreenshot implements Runnable { |
|
||||||
|
|
||||||
private final Screenshot scr; |
|
||||||
|
|
||||||
|
|
||||||
public TaskTakeScreenshot() { |
|
||||||
GLUtils.checkGLContext(); |
|
||||||
scr = App.gfx().takeScreenshot(); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
@Override |
|
||||||
public void run() |
|
||||||
{ |
|
||||||
final String fname = Support.getTime("yyyy-MM-dd_HH-mm-ss"); |
|
||||||
|
|
||||||
// generate unique filename
|
|
||||||
File file; |
|
||||||
int index = 0; |
|
||||||
while (true) { |
|
||||||
file = new File(WorkDir.getDir("_screenshot_dir"), fname + (index > 0 ? "-" + index : "") + ".png"); |
|
||||||
if (!file.exists()) break; |
|
||||||
index++; |
|
||||||
} |
|
||||||
|
|
||||||
Log.f3("Saving screenshot to file: " + file); |
|
||||||
|
|
||||||
// save to disk
|
|
||||||
try { |
|
||||||
scr.save(file); |
|
||||||
} catch (final IOException e) { |
|
||||||
Log.e("Failed to save screenshot.", e); |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,15 +0,0 @@ |
|||||||
package mightypork.gamecore.render.events; |
|
||||||
|
|
||||||
|
|
||||||
import mightypork.utils.eventbus.BusEvent; |
|
||||||
|
|
||||||
|
|
||||||
public class ScreenshotRequest extends BusEvent<ScreenshotRequestListener> { |
|
||||||
|
|
||||||
@Override |
|
||||||
protected void handleBy(ScreenshotRequestListener handler) |
|
||||||
{ |
|
||||||
handler.onScreenshotRequest(); |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
@ -1,7 +0,0 @@ |
|||||||
package mightypork.gamecore.render.events; |
|
||||||
|
|
||||||
|
|
||||||
public interface ScreenshotRequestListener { |
|
||||||
|
|
||||||
public void onScreenshotRequest(); |
|
||||||
} |
|
@ -0,0 +1,220 @@ |
|||||||
|
package mightypork.gamecore.resources.audio; |
||||||
|
|
||||||
|
|
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
import org.lwjgl.openal.AL; |
||||||
|
import org.newdawn.slick.openal.SoundStore; |
||||||
|
|
||||||
|
import mightypork.gamecore.backend.BackendModule; |
||||||
|
import mightypork.gamecore.backend.lwjgl.SlickAudio; |
||||||
|
import mightypork.gamecore.core.modules.App; |
||||||
|
import mightypork.gamecore.resources.ResourceLoadRequest; |
||||||
|
import mightypork.gamecore.resources.audio.players.EffectPlayer; |
||||||
|
import mightypork.gamecore.resources.audio.players.LoopPlayer; |
||||||
|
import mightypork.utils.interfaces.Updateable; |
||||||
|
import mightypork.utils.logging.Log; |
||||||
|
import mightypork.utils.math.constraints.vect.Vect; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Abstract audio backend module |
||||||
|
* |
||||||
|
* @author Ondřej Hruška (MightyPork) |
||||||
|
*/ |
||||||
|
public abstract class AudioModule extends BackendModule implements Updateable { |
||||||
|
|
||||||
|
/** |
||||||
|
* Set listener position |
||||||
|
* |
||||||
|
* @param pos listener position |
||||||
|
*/ |
||||||
|
public abstract void setListenerPos(Vect pos); |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Get current listener position |
||||||
|
* |
||||||
|
* @return listener position |
||||||
|
*/ |
||||||
|
public abstract Vect getListenerPos(); |
||||||
|
|
||||||
|
// -- instance --
|
||||||
|
|
||||||
|
private final Volume masterVolume = new Volume(1D); |
||||||
|
private final Volume effectsVolume = new JointVolume(masterVolume); |
||||||
|
private final Volume loopsVolume = new JointVolume(masterVolume); |
||||||
|
|
||||||
|
private final List<LoopPlayer> loopPlayers = new ArrayList<>(); |
||||||
|
private final List<DeferredAudio> resources = new ArrayList<>(); |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void destroy() |
||||||
|
{ |
||||||
|
for (final DeferredAudio r : resources) { |
||||||
|
r.destroy(); |
||||||
|
} |
||||||
|
|
||||||
|
deinitSoundSystem(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
protected abstract void deinitSoundSystem(); |
||||||
|
|
||||||
|
|
||||||
|
@Override |
||||||
|
public void update(double delta) |
||||||
|
{ |
||||||
|
for (final Updateable lp : loopPlayers) { |
||||||
|
lp.update(delta); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Create effect resource |
||||||
|
* |
||||||
|
* @param resource resource path |
||||||
|
* @param pitch default pitch (1 = unchanged) |
||||||
|
* @param gain default gain (0-1) |
||||||
|
* @return player |
||||||
|
*/ |
||||||
|
public EffectPlayer createEffect(String resource, double pitch, double gain) |
||||||
|
{ |
||||||
|
return new EffectPlayer(createResource(resource), pitch, gain, effectsVolume); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Register loop resource (music / effect loop) |
||||||
|
* |
||||||
|
* @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 LoopPlayer createLoop(String resource, double pitch, double gain, double fadeIn, double fadeOut) |
||||||
|
{ |
||||||
|
final LoopPlayer p = new LoopPlayer(createResource(resource), pitch, gain, loopsVolume); |
||||||
|
p.setFadeTimes(fadeIn, fadeOut); |
||||||
|
loopPlayers.add(p); |
||||||
|
return p; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Create {@link DeferredAudio} for a resource |
||||||
|
* |
||||||
|
* @param res a resource name |
||||||
|
* @return the resource |
||||||
|
* @throws IllegalArgumentException if resource is already registered |
||||||
|
*/ |
||||||
|
protected DeferredAudio createResource(String res) |
||||||
|
{ |
||||||
|
final DeferredAudio a = doCreateResource(res);; |
||||||
|
App.bus().send(new ResourceLoadRequest(a)); |
||||||
|
resources.add(a); |
||||||
|
return a; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Create a backend-specific deferred audio resource instance. |
||||||
|
* |
||||||
|
* @param res resource path |
||||||
|
* @return Deferred Audio |
||||||
|
*/ |
||||||
|
protected abstract DeferredAudio doCreateResource(String res); |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Fade out all loops (ie. for screen transitions) |
||||||
|
*/ |
||||||
|
public void fadeOutAllLoops() |
||||||
|
{ |
||||||
|
for (final LoopPlayer p : loopPlayers) { |
||||||
|
p.fadeOut(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Pause all loops (leave volume unchanged) |
||||||
|
*/ |
||||||
|
public void pauseAllLoops() |
||||||
|
{ |
||||||
|
for (final LoopPlayer p : loopPlayers) { |
||||||
|
p.pause(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Set level of master volume |
||||||
|
* |
||||||
|
* @param d level |
||||||
|
*/ |
||||||
|
public void setMasterVolume(double d) |
||||||
|
{ |
||||||
|
masterVolume.set(d); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Set level of effects volume |
||||||
|
* |
||||||
|
* @param d level |
||||||
|
*/ |
||||||
|
public void setEffectsVolume(double d) |
||||||
|
{ |
||||||
|
effectsVolume.set(d); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Set level of music volume |
||||||
|
* |
||||||
|
* @param d level |
||||||
|
*/ |
||||||
|
public void setMusicVolume(double d) |
||||||
|
{ |
||||||
|
loopsVolume.set(d); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Get level of master volume |
||||||
|
* |
||||||
|
* @return level |
||||||
|
*/ |
||||||
|
public double getMasterVolume() |
||||||
|
{ |
||||||
|
return masterVolume.get(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Get level of effects volume |
||||||
|
* |
||||||
|
* @return level |
||||||
|
*/ |
||||||
|
public double getEffectsVolume() |
||||||
|
{ |
||||||
|
return effectsVolume.get(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Get level of music volume |
||||||
|
* |
||||||
|
* @return level |
||||||
|
*/ |
||||||
|
public double getMusicVolume() |
||||||
|
{ |
||||||
|
return loopsVolume.get(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,24 @@ |
|||||||
|
package mightypork.gamecore.resources.audio; |
||||||
|
|
||||||
|
|
||||||
|
import mightypork.gamecore.resources.BaseDeferredResource; |
||||||
|
import mightypork.utils.annotations.Alias; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Base deferred audio |
||||||
|
* |
||||||
|
* @author Ondřej Hruška (MightyPork) |
||||||
|
*/ |
||||||
|
@Alias(name = "Audio") |
||||||
|
public abstract class DeferredAudio extends BaseDeferredResource implements IAudio { |
||||||
|
|
||||||
|
/** |
||||||
|
* Create deferred primitive audio player |
||||||
|
* |
||||||
|
* @param resourceName resource to load when needed |
||||||
|
*/ |
||||||
|
public DeferredAudio(String resourceName) { |
||||||
|
super(resourceName); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,99 @@ |
|||||||
|
package mightypork.gamecore.resources.audio; |
||||||
|
|
||||||
|
|
||||||
|
import mightypork.utils.interfaces.Destroyable; |
||||||
|
import mightypork.utils.math.constraints.vect.Vect; |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Audio resource interface (backend independent) |
||||||
|
* |
||||||
|
* @author Ondřej Hruška (MightyPork) |
||||||
|
*/ |
||||||
|
public interface IAudio extends Destroyable { |
||||||
|
|
||||||
|
/** |
||||||
|
* Pause loop (remember position and stop playing) - if was looping |
||||||
|
*/ |
||||||
|
void pauseLoop(); |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Resume loop (if was paused) |
||||||
|
*/ |
||||||
|
void resumeLoop(); |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Adjust gain for the currently playing effect (can be used for fading |
||||||
|
* music) |
||||||
|
* |
||||||
|
* @param gain gain to set 0..1 |
||||||
|
*/ |
||||||
|
void adjustGain(double gain); |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Stop audio playback |
||||||
|
*/ |
||||||
|
void stop(); |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* @return true if the audio is playing |
||||||
|
*/ |
||||||
|
boolean isPlaying(); |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* @return trie if the audio is paused |
||||||
|
*/ |
||||||
|
boolean isPaused(); |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Play as sound effect at listener position |
||||||
|
* |
||||||
|
* @param pitch pitch (1 = default) |
||||||
|
* @param gain gain (0-1) |
||||||
|
* @param loop looping |
||||||
|
*/ |
||||||
|
void play(double pitch, double gain, boolean loop); |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Play as sound effect at given X-Y position |
||||||
|
* |
||||||
|
* @param pitch pitch (1 = default) |
||||||
|
* @param gain gain (0-1) |
||||||
|
* @param loop looping |
||||||
|
* @param x |
||||||
|
* @param y |
||||||
|
*/ |
||||||
|
void play(double pitch, double gain, boolean loop, double x, double y); |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Play as sound effect at given position |
||||||
|
* |
||||||
|
* @param pitch pitch (1 = default) |
||||||
|
* @param gain gain (0-1) |
||||||
|
* @param loop looping |
||||||
|
* @param x |
||||||
|
* @param y |
||||||
|
* @param z |
||||||
|
*/ |
||||||
|
void play(double pitch, double gain, boolean loop, double x, double y, double z); |
||||||
|
|
||||||
|
|
||||||
|
/** |
||||||
|
* Play as sound effect at given position |
||||||
|
* |
||||||
|
* @param pitch pitch (1 = default) |
||||||
|
* @param gain gain (0-1) |
||||||
|
* @param loop looping |
||||||
|
* @param pos coord |
||||||
|
*/ |
||||||
|
void play(double pitch, double gain, boolean loop, Vect pos); |
||||||
|
|
||||||
|
} |
@ -1,249 +0,0 @@ |
|||||||
package mightypork.gamecore.resources.audio; |
|
||||||
|
|
||||||
|
|
||||||
import java.io.IOException; |
|
||||||
import java.io.InputStream; |
|
||||||
|
|
||||||
import mightypork.gamecore.resources.BaseLazyResource; |
|
||||||
import mightypork.utils.annotations.Alias; |
|
||||||
import mightypork.utils.files.FileUtils; |
|
||||||
import mightypork.utils.math.constraints.vect.Vect; |
|
||||||
|
|
||||||
import org.newdawn.slick.openal.Audio; |
|
||||||
import org.newdawn.slick.openal.SoundStore; |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* Wrapper class for slick audio |
|
||||||
* |
|
||||||
* @author Ondřej Hruška (MightyPork) |
|
||||||
*/ |
|
||||||
@Alias(name = "Audio") |
|
||||||
public class LazyAudio extends BaseLazyResource { |
|
||||||
|
|
||||||
private enum PlayMode |
|
||||||
{ |
|
||||||
EFFECT, MUSIC; |
|
||||||
} |
|
||||||
|
|
||||||
/** Audio resource */ |
|
||||||
private Audio backingAudio = null; |
|
||||||
|
|
||||||
// last play options
|
|
||||||
private PlayMode mode = PlayMode.EFFECT; |
|
||||||
private double pauseLoopPosition = 0; |
|
||||||
private boolean looping = false; |
|
||||||
private boolean paused = false; |
|
||||||
private double lastPlayPitch = 1; |
|
||||||
private double lastPlayGain = 1; |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* Create deferred primitive audio player |
|
||||||
* |
|
||||||
* @param resourceName resource to load when needed |
|
||||||
*/ |
|
||||||
public LazyAudio(String resourceName) { |
|
||||||
super(resourceName); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* Pause loop (remember position and stop playing) - if was looping |
|
||||||
*/ |
|
||||||
public void pauseLoop() |
|
||||||
{ |
|
||||||
if (!ensureLoaded()) return; |
|
||||||
|
|
||||||
if (isPlaying() && looping) { |
|
||||||
pauseLoopPosition = backingAudio.getPosition(); |
|
||||||
stop(); |
|
||||||
paused = true; |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* Resume loop (if was paused) |
|
||||||
* |
|
||||||
* @return source ID |
|
||||||
*/ |
|
||||||
public int resumeLoop() |
|
||||||
{ |
|
||||||
if (!ensureLoaded()) return -1; |
|
||||||
|
|
||||||
int source = -1; |
|
||||||
if (looping && paused) { |
|
||||||
if (mode == PlayMode.MUSIC) { |
|
||||||
source = backingAudio.playAsMusic((float) lastPlayPitch, (float) lastPlayGain, true); |
|
||||||
} else { |
|
||||||
source = backingAudio.playAsSoundEffect((float) lastPlayPitch, (float) lastPlayGain, true); |
|
||||||
} |
|
||||||
backingAudio.setPosition((float) pauseLoopPosition); |
|
||||||
paused = false; |
|
||||||
} |
|
||||||
return source; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
@Override |
|
||||||
protected void loadResource(String resource) throws IOException |
|
||||||
{ |
|
||||||
final String ext = FileUtils.getExtension(resource); |
|
||||||
|
|
||||||
try (final InputStream stream = FileUtils.getResource(resource)) { |
|
||||||
|
|
||||||
if (ext.equalsIgnoreCase("ogg")) { |
|
||||||
backingAudio = SoundStore.get().getOgg(resource, stream); |
|
||||||
|
|
||||||
} else if (ext.equalsIgnoreCase("wav")) { |
|
||||||
backingAudio = SoundStore.get().getWAV(resource, stream); |
|
||||||
|
|
||||||
} else if (ext.equalsIgnoreCase("aif")) { |
|
||||||
backingAudio = SoundStore.get().getAIF(resource, stream); |
|
||||||
|
|
||||||
} else if (ext.equalsIgnoreCase("mod")) { |
|
||||||
backingAudio = SoundStore.get().getMOD(resource, stream); |
|
||||||
|
|
||||||
} else { |
|
||||||
throw new RuntimeException("Invalid audio file extension."); |
|
||||||
} |
|
||||||
} |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* Stop playing |
|
||||||
*/ |
|
||||||
public void stop() |
|
||||||
{ |
|
||||||
if (!isLoaded()) return; |
|
||||||
|
|
||||||
backingAudio.stop(); |
|
||||||
paused = false; |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* @return true if the audio is playing |
|
||||||
*/ |
|
||||||
public boolean isPlaying() |
|
||||||
{ |
|
||||||
if (!isLoaded()) return false; |
|
||||||
|
|
||||||
return backingAudio.isPlaying(); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* @return trie if the audio is paused |
|
||||||
*/ |
|
||||||
public boolean isPaused() |
|
||||||
{ |
|
||||||
if (!isLoaded()) return false; |
|
||||||
|
|
||||||
return backingAudio.isPaused(); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* Play as sound effect at listener position |
|
||||||
* |
|
||||||
* @param pitch pitch (1 = default) |
|
||||||
* @param gain gain (0-1) |
|
||||||
* @param loop looping |
|
||||||
* @return source id |
|
||||||
*/ |
|
||||||
public int playAsEffect(double pitch, double gain, boolean loop) |
|
||||||
{ |
|
||||||
return playAsEffect(pitch, gain, loop, SoundSystem.getListener()); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* Play as sound effect at given X-Y position |
|
||||||
* |
|
||||||
* @param pitch pitch (1 = default) |
|
||||||
* @param gain gain (0-1) |
|
||||||
* @param loop looping |
|
||||||
* @param x |
|
||||||
* @param y |
|
||||||
* @return source id |
|
||||||
*/ |
|
||||||
public int playAsEffect(double pitch, double gain, boolean loop, double x, double y) |
|
||||||
{ |
|
||||||
return playAsEffect(pitch, gain, loop, x, y, SoundSystem.getListener().z()); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* Play as sound effect at given position |
|
||||||
* |
|
||||||
* @param pitch pitch (1 = default) |
|
||||||
* @param gain gain (0-1) |
|
||||||
* @param loop looping |
|
||||||
* @param x |
|
||||||
* @param y |
|
||||||
* @param z |
|
||||||
* @return source id |
|
||||||
*/ |
|
||||||
public int playAsEffect(double pitch, double gain, boolean loop, double x, double y, double z) |
|
||||||
{ |
|
||||||
if (!ensureLoaded()) return -1; |
|
||||||
|
|
||||||
this.lastPlayPitch = pitch; |
|
||||||
this.lastPlayGain = gain; |
|
||||||
looping = loop; |
|
||||||
mode = PlayMode.EFFECT; |
|
||||||
return backingAudio.playAsSoundEffect((float) pitch, (float) gain, loop, (float) x, (float) y, (float) z); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* Play as sound effect at given position |
|
||||||
* |
|
||||||
* @param pitch pitch (1 = default) |
|
||||||
* @param gain gain (0-1) |
|
||||||
* @param loop looping |
|
||||||
* @param pos coord |
|
||||||
* @return source id |
|
||||||
*/ |
|
||||||
public int playAsEffect(double pitch, double gain, boolean loop, Vect pos) |
|
||||||
{ |
|
||||||
if (!ensureLoaded()) return -1; |
|
||||||
|
|
||||||
return playAsEffect(pitch, gain, loop, pos.x(), pos.y(), pos.z()); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
/** |
|
||||||
* Play as music using source 0.<br> |
|
||||||
* Discouraged, since this does not allow cross-fading. |
|
||||||
* |
|
||||||
* @param pitch play pitch |
|
||||||
* @param gain play gain |
|
||||||
* @param loop looping |
|
||||||
* @return source |
|
||||||
*/ |
|
||||||
public int playAsMusic(double pitch, double gain, boolean loop) |
|
||||||
{ |
|
||||||
if (!ensureLoaded()) return -1; |
|
||||||
|
|
||||||
this.lastPlayPitch = (float) pitch; |
|
||||||
this.lastPlayGain = (float) gain; |
|
||||||
looping = loop; |
|
||||||
mode = PlayMode.MUSIC; |
|
||||||
return backingAudio.playAsMusic((float) pitch, (float) gain, loop); |
|
||||||
} |
|
||||||
|
|
||||||
|
|
||||||
@Override |
|
||||||
public void destroy() |
|
||||||
{ |
|
||||||
if (!isLoaded() || backingAudio == null) return; |
|
||||||
|
|
||||||
backingAudio.release(); |
|
||||||
backingAudio = null; |
|
||||||
} |
|
||||||
|
|
||||||
} |
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue