Support for main loop and confignsaving on exit.

master
Ondřej Hruška 10 years ago
parent af8535620b
commit dbc32c4d0a
  1. 266
      src/junk/AppInitOptions.java
  2. 369
      src/junk/BaseApp.java
  3. 74
      src/mightypork/gamecore/audio/AudioModule.java
  4. 14
      src/mightypork/gamecore/audio/DeferredAudio.java
  5. 44
      src/mightypork/gamecore/audio/IAudio.java
  6. 16
      src/mightypork/gamecore/audio/JointVolume.java
  7. 42
      src/mightypork/gamecore/audio/SoundRegistry.java
  8. 8
      src/mightypork/gamecore/audio/Volume.java
  9. 46
      src/mightypork/gamecore/audio/players/AudioPlayer.java
  10. 24
      src/mightypork/gamecore/audio/players/EffectPlayer.java
  11. 82
      src/mightypork/gamecore/audio/players/LoopPlayer.java
  12. 39
      src/mightypork/gamecore/core/App.java
  13. 22
      src/mightypork/gamecore/core/AppBackend.java
  14. 6
      src/mightypork/gamecore/core/BackendModule.java
  15. 128
      src/mightypork/gamecore/core/DeltaMainLoop.java
  16. 132
      src/mightypork/gamecore/core/MainLoop.java
  17. 11
      src/mightypork/gamecore/core/config/Config.java
  18. 28
      src/mightypork/gamecore/core/config/InitTaskConfig.java
  19. 18
      src/mightypork/gamecore/core/config/KeyStrokeProperty.java
  20. 16
      src/mightypork/gamecore/core/events/ShutdownEvent.java
  21. 2
      src/mightypork/gamecore/core/events/ShutdownListener.java
  22. 66
      src/mightypork/gamecore/core/init/InitTask.java
  23. 10
      src/mightypork/gamecore/core/init/InitTaskCrashHandler.java
  24. 36
      src/mightypork/gamecore/core/init/InitTaskDisplay.java
  25. 26
      src/mightypork/gamecore/core/init/InitTaskIonizables.java
  26. 34
      src/mightypork/gamecore/core/init/InitTaskLog.java
  27. 16
      src/mightypork/gamecore/core/init/InitTaskLogHeader.java
  28. 40
      src/mightypork/gamecore/core/init/InitTaskMainLoop.java
  29. 14
      src/mightypork/gamecore/core/init/InitTaskResourceLoader.java
  30. 6
      src/mightypork/gamecore/core/init/InitTaskResourceLoaderAsync.java
  31. 4
      src/mightypork/gamecore/core/init/InitTaskResourceLoaderNone.java
  32. 10
      src/mightypork/gamecore/core/init/InitTaskResources.java
  33. 42
      src/mightypork/gamecore/core/init/InitTaskWorkdir.java
  34. 10
      src/mightypork/gamecore/core/plugins/AppPlugin.java
  35. 28
      src/mightypork/gamecore/core/plugins/screenshot/InitTaskPluginScreenshot.java
  36. 4
      src/mightypork/gamecore/core/plugins/screenshot/ScreenshotPlugin.java
  37. 4
      src/mightypork/gamecore/core/plugins/screenshot/ScreenshotRequest.java
  38. 32
      src/mightypork/gamecore/core/plugins/screenshot/TaskTakeScreenshot.java
  39. 2
      src/mightypork/gamecore/graphics/FullscreenToggleRequest.java
  40. 194
      src/mightypork/gamecore/graphics/GraphicsModule.java
  41. 4
      src/mightypork/gamecore/graphics/Renderable.java
  42. 2
      src/mightypork/gamecore/graphics/Screenshot.java
  43. 52
      src/mightypork/gamecore/graphics/fonts/DeferredFont.java
  44. 28
      src/mightypork/gamecore/graphics/fonts/FontRegistry.java
  45. 82
      src/mightypork/gamecore/graphics/fonts/FontRenderer.java
  46. 9
      src/mightypork/gamecore/graphics/fonts/FontStyle.java
  47. 22
      src/mightypork/gamecore/graphics/fonts/Glyphs.java
  48. 30
      src/mightypork/gamecore/graphics/fonts/IFont.java
  49. 22
      src/mightypork/gamecore/graphics/textures/DeferredTexture.java
  50. 26
      src/mightypork/gamecore/graphics/textures/ITexture.java
  51. 28
      src/mightypork/gamecore/graphics/textures/QuadGrid.java
  52. 54
      src/mightypork/gamecore/graphics/textures/TextureRegistry.java
  53. 54
      src/mightypork/gamecore/graphics/textures/TxQuad.java
  54. 44
      src/mightypork/gamecore/graphics/textures/TxSheet.java
  55. 20
      src/mightypork/gamecore/gui/Action.java
  56. 22
      src/mightypork/gamecore/gui/ActionGroup.java
  57. 2
      src/mightypork/gamecore/gui/HasAction.java
  58. 78
      src/mightypork/gamecore/gui/components/BaseComponent.java
  59. 38
      src/mightypork/gamecore/gui/components/Component.java
  60. 2
      src/mightypork/gamecore/gui/components/DynamicWidthComponent.java
  61. 2
      src/mightypork/gamecore/gui/components/InputComponent.java
  62. 56
      src/mightypork/gamecore/gui/components/LayoutComponent.java
  63. 36
      src/mightypork/gamecore/gui/components/LinearComponent.java
  64. 12
      src/mightypork/gamecore/gui/components/PluggableRenderable.java
  65. 22
      src/mightypork/gamecore/gui/components/input/ClickableComponent.java
  66. 30
      src/mightypork/gamecore/gui/components/input/ClickableWrapper.java
  67. 32
      src/mightypork/gamecore/gui/components/input/TextButton.java
  68. 24
      src/mightypork/gamecore/gui/components/layout/ColumnLayout.java
  69. 12
      src/mightypork/gamecore/gui/components/layout/ConstraintLayout.java
  70. 30
      src/mightypork/gamecore/gui/components/layout/FlowColumnLayout.java
  71. 28
      src/mightypork/gamecore/gui/components/layout/FlowRowLayout.java
  72. 28
      src/mightypork/gamecore/gui/components/layout/GridLayout.java
  73. 2
      src/mightypork/gamecore/gui/components/layout/NullComponent.java
  74. 24
      src/mightypork/gamecore/gui/components/layout/RowLayout.java
  75. 28
      src/mightypork/gamecore/gui/components/layout/linear/AbstractLinearWrapper.java
  76. 6
      src/mightypork/gamecore/gui/components/layout/linear/LinearGap.java
  77. 34
      src/mightypork/gamecore/gui/components/layout/linear/LinearLayout.java
  78. 16
      src/mightypork/gamecore/gui/components/layout/linear/LinearRectangle.java
  79. 8
      src/mightypork/gamecore/gui/components/layout/linear/LinearSquare.java
  80. 8
      src/mightypork/gamecore/gui/components/layout/linear/LinearWrapper.java
  81. 20
      src/mightypork/gamecore/gui/components/painters/ImagePainter.java
  82. 20
      src/mightypork/gamecore/gui/components/painters/QuadPainter.java
  83. 90
      src/mightypork/gamecore/gui/components/painters/TextPainter.java
  84. 2
      src/mightypork/gamecore/gui/events/LayoutChangeEvent.java
  85. 2
      src/mightypork/gamecore/gui/events/LayoutChangeListener.java
  86. 12
      src/mightypork/gamecore/gui/events/ScreenRequest.java
  87. 2
      src/mightypork/gamecore/gui/events/ScreenRequestListener.java
  88. 14
      src/mightypork/gamecore/gui/events/ViewportChangeEvent.java
  89. 2
      src/mightypork/gamecore/gui/events/ViewportChangeListener.java
  90. 48
      src/mightypork/gamecore/gui/screens/LayeredScreen.java
  91. 98
      src/mightypork/gamecore/gui/screens/Overlay.java
  92. 76
      src/mightypork/gamecore/gui/screens/Screen.java
  93. 18
      src/mightypork/gamecore/gui/screens/ScreenLayer.java
  94. 40
      src/mightypork/gamecore/gui/screens/ScreenRegistry.java
  95. 32
      src/mightypork/gamecore/gui/screens/impl/CrossfadeOverlay.java
  96. 16
      src/mightypork/gamecore/gui/screens/impl/CrossfadeRequest.java
  97. 58
      src/mightypork/gamecore/gui/screens/impl/FadingLayer.java
  98. 14
      src/mightypork/gamecore/gui/screens/impl/LayerColor.java
  99. 44
      src/mightypork/gamecore/input/InputModule.java
  100. 40
      src/mightypork/gamecore/input/Key.java
  101. Some files were not shown because too many files have changed in this diff Show More

@ -1,133 +1,133 @@
package junk;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import mightypork.gamecore.core.AppBackend;
import mightypork.gamecore.resources.ResourceInitializer;
import mightypork.gamecore.resources.loading.AsyncResourceLoader;
import mightypork.gamecore.resources.loading.ResourceLoader;
/**
* Init options holder class
*/
public class AppInitOptions {
String logDir = "log";
String logFilePrefix = "runtime";
String screenshotDir = "screenshots";
boolean busLogging = false;
String configFile = "settings.cfg";
String configComment = "Main config file";
final List<ResourceInitializer> resourceLists = new ArrayList<>();
final List<KeySetup> keyLists = new ArrayList<>();
final List<ConfigSetup> configLists = new ArrayList<>();
ResourceLoader resourceLoader = new AsyncResourceLoader();
Level logLevel = Level.ALL;
public boolean sigleInstance = true;
Level logSoutLevel = Level.ALL;
public void setConfigFile(String filename, String comment)
{
configFile = filename;
configComment = comment;
}
public void addConfig(ConfigSetup cfg)
{
configLists.add(cfg);
}
public void addKeys(KeySetup keys)
{
keyLists.add(keys);
}
public void addResources(ResourceInitializer res)
{
resourceLists.add(res);
}
public void setBackend(AppBackend backend)
{
this.backend = backend;
}
/**
* Set whether to run in single instance mode, or allow multiple instances.<br>
* Multiple instances running can cause various collisions (eg. when writing
* config file or logging).
*
* @param sigleInstance true to allow only one instance
*/
public void setSigleInstance(boolean sigleInstance)
{
this.sigleInstance = sigleInstance;
}
/**
* Set working directory path. If not exists, it will be created.
*
* @param workdir work dir path
*/
public void setWorkdir(File workdir)
{
this.workdir = workdir;
}
public void setBusLogging(boolean yes)
{
busLogging = yes;
}
public void setLogOptions(String logDir, String filePrefix, int archivedCount, Level logLevel)
{
this.logDir = logDir;
this.logFilePrefix = filePrefix;
this.logArchiveCount = archivedCount;
this.logLevel = logLevel;
}
public void setResourceLoader(ResourceLoader resLoader)
{
resourceLoader = resLoader;
}
public void setScreenshotDir(String path)
{
this.screenshotDir = path;
}
public void setLockFile(String lockFile)
{
this.lockFile = lockFile;
}
public void setLogLevel(Level logLevel, Level soutLevel)
{
this.logLevel = logLevel;
this.logSoutLevel = soutLevel;
}
}
//package junk;
//
//
//import java.io.File;
//import java.util.ArrayList;
//import java.util.List;
//import java.util.logging.Level;
//
//import mightypork.gamecore.core.AppBackend;
//import mightypork.gamecore.resources.ResourceInitializer;
//import mightypork.gamecore.resources.loading.AsyncResourceLoader;
//import mightypork.gamecore.resources.loading.ResourceLoader;
//
//
///**
// * Init options holder class
// */
//public class AppInitOptions {
//
// String logDir = "log";
// String logFilePrefix = "runtime";
//
// String screenshotDir = "screenshots";
//
// boolean busLogging = false;
//
// String configFile = "settings.cfg";
// String configComment = "Main config file";
//
// final List<ResourceInitializer> resourceLists = new ArrayList<>();
// final List<KeySetup> keyLists = new ArrayList<>();
// final List<ConfigSetup> configLists = new ArrayList<>();
//
// ResourceLoader resourceLoader = new AsyncResourceLoader();
// Level logLevel = Level.ALL;
// public boolean sigleInstance = true;
// Level logSoutLevel = Level.ALL;
//
//
// public void setConfigFile(String filename, String comment)
// {
// configFile = filename;
// configComment = comment;
// }
//
//
// public void addConfig(ConfigSetup cfg)
// {
// configLists.add(cfg);
// }
//
//
// public void addKeys(KeySetup keys)
// {
// keyLists.add(keys);
// }
//
//
// public void addResources(ResourceInitializer res)
// {
// resourceLists.add(res);
// }
//
//
// public void setBackend(AppBackend backend)
// {
// this.backend = backend;
// }
//
//
// /**
// * Set whether to run in single instance mode, or allow multiple instances.<br>
// * Multiple instances running can cause various collisions (eg. when writing
// * config file or logging).
// *
// * @param sigleInstance true to allow only one instance
// */
// public void setSigleInstance(boolean sigleInstance)
// {
// this.sigleInstance = sigleInstance;
// }
//
//
// /**
// * Set working directory path. If not exists, it will be created.
// *
// * @param workdir work dir path
// */
// public void setWorkdir(File workdir)
// {
// this.workdir = workdir;
// }
//
//
// public void setBusLogging(boolean yes)
// {
// busLogging = yes;
// }
//
//
// public void setLogOptions(String logDir, String filePrefix, int archivedCount, Level logLevel)
// {
// this.logDir = logDir;
// this.logFilePrefix = filePrefix;
// this.logArchiveCount = archivedCount;
// this.logLevel = logLevel;
// }
//
//
// public void setResourceLoader(ResourceLoader resLoader)
// {
// resourceLoader = resLoader;
// }
//
//
// public void setScreenshotDir(String path)
// {
// this.screenshotDir = path;
// }
//
//
// public void setLockFile(String lockFile)
// {
// this.lockFile = lockFile;
// }
//
//
// public void setLogLevel(Level logLevel, Level soutLevel)
// {
// this.logLevel = logLevel;
// this.logSoutLevel = soutLevel;
// }
//}

@ -1,184 +1,185 @@
package junk;
import java.lang.Thread.UncaughtExceptionHandler;
import mightypork.gamecore.core.App;
import mightypork.gamecore.core.AppBackend;
import mightypork.gamecore.core.MainLoop;
import mightypork.gamecore.core.config.Config;
import mightypork.gamecore.gui.screens.ScreenRegistry;
import mightypork.gamecore.gui.screens.impl.CrossfadeOverlay;
import mightypork.gamecore.resources.Res;
import mightypork.gamecore.resources.ResourceInitializer;
import mightypork.utils.files.WorkDir;
import mightypork.utils.logging.Log;
/**
* Basic screen-based game with subsystems.<br>
* This class takes care of the initialization sequence.
*
* @author Ondřej Hruška (MightyPork)
*/
public abstract class BaseApp extends App implements UncaughtExceptionHandler {
// modules
private MainLoop gameLoop;
private ScreenRegistry screenRegistry;
private boolean started = false;
private boolean lockObtained = false;
// init opt holder
private final AppInitOptions opt = new AppInitOptions();
/**
* Get init options
*
* @return opt holder
*/
public AppInitOptions getInitOptions()
{
if (started) {
throw new IllegalStateException("Cannot alter init options after starting the App.");
}
return opt;
}
public BaseApp(AppBackend backend)
{
super(backend);
}
/**
* Start the application
*/
@Override
public final void start()
{
initialize();
Log.i("Starting main loop...");
// open first screen !!!
started = true;
gameLoop.start();
}
/**
* Init the app
*/
protected void initialize()
{
WorkDir.setBaseDir(opt.workdir);
if (opt.sigleInstance) initLock();
lockObtained = true;
for (final RouteSetup rs : opt.routeLists) {
WorkDir.registerRoutes(rs);
}
WorkDir.addPath("_screenshot_dir", opt.screenshotDir);
// apply configurations
Config.init(WorkDir.getFile(opt.configFile), opt.configComment);
// add keys to config
for (final KeySetup l : opt.keyLists) {
Config.registerKeys(l);
}
// add options to config
for (final ConfigSetup c : opt.configLists) {
Config.registerOptions(c);
}
Config.load();
/*
* Display
*/
Log.f2("Initializing Display System...");
initDisplay(gfx());
/*
* Audio
*/
Log.f2("Initializing Sound System...");
soundSystem = new SoundSystem(this);
initSoundSystem(soundSystem);
/*
* Input
*/
Log.f2("Initializing Input System...");
inputSystem = new LwjglInputModule(this);
initInputSystem(inputSystem);
/*
* Prepare main loop
*/
Log.f1("Creating Screen Registry and Game Loop...");
screenRegistry = new ScreenRegistry(this);
gameLoop = createMainLoop();
gameLoop.setRootRenderable(screenRegistry);
/*
* Load resources
*
* Resources should be registered to registries, and AsyncResourceLoader will load them.
*/
Log.f1("Loading resources...");
if (opt.resourceLoader != null) {
opt.resourceLoader.setBaseDir(this);
}
Res.setBaseDir(this);
for (final ResourceInitializer rl : opt.resourceLists) {
Res.load(rl);
}
/*
* Screen registry
*
* Must be after resources, because screens can request them during instantiation.
*/
Log.f2("Registering screens...");
initScreens(screenRegistry);
}
/**
* Register game screens to the registry.
*
* @param screens
*/
protected void initScreens(ScreenRegistry screens)
{
screens.addOverlay(new CrossfadeOverlay(this));
}
/**
* Create game loop instance
*
* @return the game loop.
*/
protected MainLoop createMainLoop()
{
return new MainLoop(this);
}
protected void beforeShutdown()
{
// ???
if (lockObtained) Config.save();
}
}
//package junk;
//
//
//import java.lang.Thread.UncaughtExceptionHandler;
//
//import mightypork.gamecore.core.App;
//import mightypork.gamecore.core.AppBackend;
//import mightypork.gamecore.core.MainLoop;
//import mightypork.gamecore.core.DeltaMainLoop;
//import mightypork.gamecore.core.config.Config;
//import mightypork.gamecore.gui.screens.ScreenRegistry;
//import mightypork.gamecore.gui.screens.impl.CrossfadeOverlay;
//import mightypork.gamecore.resources.Res;
//import mightypork.gamecore.resources.ResourceInitializer;
//import mightypork.utils.files.WorkDir;
//import mightypork.utils.logging.Log;
//
//
///**
// * Basic screen-based game with subsystems.<br>
// * This class takes care of the initialization sequence.
// *
// * @author Ondřej Hruška (MightyPork)
// */
//public abstract class BaseApp extends App implements UncaughtExceptionHandler {
//
// // modules
// private MainLoop gameLoop;
// private ScreenRegistry screenRegistry;
//
// private boolean started = false;
// private boolean lockObtained = false;
//
// // init opt holder
// private final AppInitOptions opt = new AppInitOptions();
//
//
// /**
// * Get init options
// *
// * @return opt holder
// */
// public AppInitOptions getInitOptions()
// {
// if (started) {
// throw new IllegalStateException("Cannot alter init options after starting the App.");
// }
//
// return opt;
// }
//
//
// public BaseApp(AppBackend backend)
// {
// super(backend);
// }
//
//
// /**
// * Start the application
// */
// @Override
// public final void start()
// {
// initialize();
//
// Log.i("Starting main loop...");
//
// // open first screen !!!
// started = true;
// gameLoop.start();
// }
//
//
// /**
// * Init the app
// */
// protected void initialize()
// {
// WorkDir.setBaseDir(opt.workdir);
//
// if (opt.sigleInstance) initLock();
// lockObtained = true;
//
// for (final RouteSetup rs : opt.routeLists) {
// WorkDir.registerRoutes(rs);
// }
// WorkDir.addPath("_screenshot_dir", opt.screenshotDir);
//
// // apply configurations
// Config.init(WorkDir.getFile(opt.configFile), opt.configComment);
//
// // add keys to config
// for (final KeySetup l : opt.keyLists) {
// Config.registerKeys(l);
// }
//
// // add options to config
// for (final ConfigSetup c : opt.configLists) {
// Config.registerOptions(c);
// }
// Config.load();
//
// /*
// * Display
// */
// Log.f2("Initializing Display System...");
// initDisplay(gfx());
//
// /*
// * Audio
// */
// Log.f2("Initializing Sound System...");
// soundSystem = new SoundSystem(this);
// initSoundSystem(soundSystem);
//
// /*
// * Input
// */
// Log.f2("Initializing Input System...");
// inputSystem = new LwjglInputModule(this);
// initInputSystem(inputSystem);
//
// /*
// * Prepare main loop
// */
// Log.f1("Creating Screen Registry and Game Loop...");
// screenRegistry = new ScreenRegistry(this);
// gameLoop = createMainLoop();
// gameLoop.setRootRenderable(screenRegistry);
//
// /*
// * Load resources
// *
// * Resources should be registered to registries, and AsyncResourceLoader will load them.
// */
// Log.f1("Loading resources...");
// if (opt.resourceLoader != null) {
// opt.resourceLoader.setBaseDir(this);
// }
//
// Res.setBaseDir(this);
//
// for (final ResourceInitializer rl : opt.resourceLists) {
// Res.load(rl);
// }
//
// /*
// * Screen registry
// *
// * Must be after resources, because screens can request them during instantiation.
// */
// Log.f2("Registering screens...");
// initScreens(screenRegistry);
// }
//
//
// /**
// * Register game screens to the registry.
// *
// * @param screens
// */
// protected void initScreens(ScreenRegistry screens)
// {
// screens.addOverlay(new CrossfadeOverlay(this));
// }
//
//
// /**
// * Create game loop instance
// *
// * @return the game loop.
// */
// protected MainLoop createMainLoop()
// {
// return new DeltaMainLoop(this);
// }
//
//
// protected void beforeShutdown()
// {
// // ???
// if (lockObtained) Config.save();
// }
//}

@ -19,50 +19,50 @@ import mightypork.utils.math.constraints.vect.Vect;
* @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();
}
/**
* Deinitialize the soud system, release resources etc.<br>
* Audio resources are already destroyed.
*/
protected abstract void deinitSoundSystem();
@Override
public void update(double delta)
{
@ -70,8 +70,8 @@ public abstract class AudioModule extends BackendModule implements Updateable {
lp.update(delta);
}
}
/**
* Create effect resource
*
@ -82,8 +82,8 @@ public abstract class AudioModule extends BackendModule implements Updateable {
{
return new EffectPlayer(createAudioResource(resource), effectsVolume);
}
/**
* Register loop resource (music / effect loop)
*
@ -96,8 +96,8 @@ public abstract class AudioModule extends BackendModule implements Updateable {
loopPlayers.add(p);
return p;
}
/**
* Create {@link DeferredAudio} for a resource, request deferred load and
* add to the resources list.<br>
@ -115,8 +115,8 @@ public abstract class AudioModule extends BackendModule implements Updateable {
resources.add(a);
return a;
}
/**
* Create a backend-specific deferred audio resource.<br>
* The actual resource instance should be created here. Registering, loading
@ -126,8 +126,8 @@ public abstract class AudioModule extends BackendModule implements Updateable {
* @return Deferred Audio
*/
protected abstract DeferredAudio doCreateResource(String res);
/**
* Fade out all loops (= fade out the currently playing loops)
*/
@ -137,8 +137,8 @@ public abstract class AudioModule extends BackendModule implements Updateable {
p.fadeOut();
}
}
/**
* Pause all loops (leave volume unchanged)
*/
@ -148,8 +148,8 @@ public abstract class AudioModule extends BackendModule implements Updateable {
p.pause();
}
}
/**
* Set level of master volume (volume multiplier)
*
@ -159,8 +159,8 @@ public abstract class AudioModule extends BackendModule implements Updateable {
{
masterVolume.set(volume);
}
/**
* Set level of effects volume (volume multiplier)
*
@ -170,8 +170,8 @@ public abstract class AudioModule extends BackendModule implements Updateable {
{
effectsVolume.set(volume);
}
/**
* Set level of loops volume (volume multiplier)
*
@ -181,8 +181,8 @@ public abstract class AudioModule extends BackendModule implements Updateable {
{
loopsVolume.set(volume);
}
/**
* Get level of master volume (volume multiplier)
*
@ -192,8 +192,8 @@ public abstract class AudioModule extends BackendModule implements Updateable {
{
return masterVolume.get();
}
/**
* Get level of effects volume (volume multiplier)
*
@ -203,8 +203,8 @@ public abstract class AudioModule extends BackendModule implements Updateable {
{
return effectsVolume.get();
}
/**
* Get level of loops volume (volume multiplier)
*

@ -14,7 +14,7 @@ import mightypork.utils.math.constraints.vect.Vect;
*/
@Alias(name = "Audio")
public abstract class DeferredAudio extends BaseDeferredResource implements IAudio {
/**
* Create audio
*
@ -24,22 +24,22 @@ public abstract class DeferredAudio extends BaseDeferredResource implements IAud
{
super(resourceName);
}
@Override
public void play(double gain, double pitch, boolean loop)
{
play(gain, pitch, loop, App.sound().getListenerPos());
}
@Override
public void play(double gain, double pitch, boolean loop, double x, double y)
{
play(gain, pitch, loop, x, y, App.sound().getListenerPos().z());
}
@Override
public void play(double gain, double pitch, boolean loop, Vect pos)
{

@ -11,19 +11,19 @@ import mightypork.utils.math.constraints.vect.Vect;
* @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 looping and paused)
*/
void resumeLoop();
/**
* Adjust gain for the currently playing effect (can be used for fading
* music)
@ -31,27 +31,27 @@ public interface IAudio extends Destroyable {
* @param gain gain to set 0..1
*/
void adjustGain(double gain);
/**
* Stop audio playback, free source. Meaningful for loops, may not work
* properly for effects.
*/
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
*
@ -60,8 +60,8 @@ public interface IAudio extends Destroyable {
* @param loop looping
*/
void play(double gain, double pitch, boolean loop);
/**
* Play as sound effect at given position
*
@ -73,8 +73,8 @@ public interface IAudio extends Destroyable {
* @param z
*/
void play(double gain, double pitch, boolean loop, double x, double y, double z);
/**
* Play as sound effect at given X-Y position
*
@ -85,8 +85,8 @@ public interface IAudio extends Destroyable {
* @param y
*/
void play(double gain, double pitch, boolean loop, double x, double y);
/**
* Play as sound effect at given position
*
@ -96,8 +96,8 @@ public interface IAudio extends Destroyable {
* @param pos coord
*/
void play(double gain, double pitch, boolean loop, Vect pos);
/**
* Check if this audio is currently active (ie. playing or paused, not
* stopped)
@ -105,5 +105,5 @@ public interface IAudio extends Destroyable {
* @return is active
*/
boolean isActive();
}

@ -10,10 +10,10 @@ import mightypork.utils.math.Calc;
* @author Ondřej Hruška (MightyPork)
*/
public class JointVolume extends Volume {
private final Volume[] volumes;
/**
* Create joint volume with master gain of 1
*
@ -25,8 +25,8 @@ public class JointVolume extends Volume {
super(1D);
this.volumes = volumes;
}
/**
* Get combined gain (multiplied)
*/
@ -36,11 +36,11 @@ public class JointVolume extends Volume {
double d = super.get();
for (final Volume v : volumes)
d *= v.get();
return Calc.clamp(d, 0, 1);
}
/**
* Set master gain
*/

@ -16,11 +16,11 @@ import mightypork.utils.exceptions.KeyAlreadyExistsException;
* @author Ondřej Hruška (MightyPork)
*/
public class SoundRegistry {
private final Map<String, EffectPlayer> effects = new HashMap<>();
private final Map<String, LoopPlayer> loops = new HashMap<>();
/**
* Register effect resource
*
@ -33,16 +33,16 @@ public class SoundRegistry {
public EffectPlayer addEffect(String key, String resourcePath, double gain, double pitch)
{
final EffectPlayer effect = App.sound().createEffect(resourcePath);
effect.setPitch(pitch);
effect.setGain(gain);
addEffect(key, effect);
return effect;
}
/**
* Register effect resource
*
@ -52,11 +52,11 @@ public class SoundRegistry {
public void addEffect(String key, EffectPlayer effect)
{
if (effects.containsKey(key)) throw new KeyAlreadyExistsException();
effects.put(key, effect);
}
/**
* Register loop resource (music / effect loop)
*
@ -71,17 +71,17 @@ public class SoundRegistry {
public LoopPlayer addLoop(String key, String resourcePath, double gain, double pitch, double fadeIn, double fadeOut)
{
final LoopPlayer loop = App.sound().createLoop(resourcePath);
loop.setPitch(pitch);
loop.setGain(gain);
loop.setFadeTimes(fadeIn, fadeOut);
addLoop(key, loop);
return loop;
}
/**
* Register loop resource (music / effect loop)
*
@ -91,11 +91,11 @@ public class SoundRegistry {
public void addLoop(String key, LoopPlayer loop)
{
if (loops.containsKey(key)) throw new KeyAlreadyExistsException();
loops.put(key, loop);
}
/**
* Get a loop player for key
*
@ -110,8 +110,8 @@ public class SoundRegistry {
}
return p;
}
/**
* Get a effect player for key
*

@ -11,7 +11,7 @@ import mightypork.utils.struct.Mutable;
* @author Ondřej Hruška (MightyPork)
*/
public class Volume extends Mutable<Double> {
/**
* @param d initial value
*/
@ -19,12 +19,12 @@ public class Volume extends Mutable<Double> {
{
super(d);
}
@Override
public void set(Double d)
{
super.set(Calc.clamp(d, 0, 1));
}
}

@ -12,20 +12,20 @@ import mightypork.utils.interfaces.Destroyable;
* @author Ondřej Hruška (MightyPork)
*/
public abstract class AudioPlayer implements Destroyable {
/** the track */
private final IAudio audio;
/** base gain for sfx */
private double baseGain;
/** base pitch for sfx */
private double basePitch;
/** dedicated volume control */
private final Volume gainMultiplier;
/**
* @param track audio resource
* @param volume colume control
@ -33,20 +33,20 @@ public abstract class AudioPlayer implements Destroyable {
public AudioPlayer(IAudio track, Volume volume)
{
this.audio = track;
if (volume == null) volume = new Volume(1D);
this.gainMultiplier = volume;
}
@Override
public void destroy()
{
audio.destroy();
}
/**
* @return audio resource
*/
@ -54,8 +54,8 @@ public abstract class AudioPlayer implements Destroyable {
{
return audio;
}
/**
* Get play gain, computed based on volume and given multiplier
*
@ -66,8 +66,8 @@ public abstract class AudioPlayer implements Destroyable {
{
return baseGain * gainMultiplier.get() * multiplier;
}
/**
* Get pitch
*
@ -78,8 +78,8 @@ public abstract class AudioPlayer implements Destroyable {
{
return basePitch * multiplier;
}
/**
* Get if audio is valid
*
@ -89,8 +89,8 @@ public abstract class AudioPlayer implements Destroyable {
{
return (audio != null);
}
/**
* Set base gain. 1 is original volume, 0 is silence.
*
@ -100,8 +100,8 @@ public abstract class AudioPlayer implements Destroyable {
{
this.baseGain = gain;
}
/**
* Set base pitch. 1 is original pitch, less is deeper, more is higher.
*
@ -111,5 +111,5 @@ public abstract class AudioPlayer implements Destroyable {
{
this.basePitch = pitch;
}
}

@ -12,7 +12,7 @@ import mightypork.utils.math.constraints.vect.Vect;
* @author Ondřej Hruška (MightyPork)
*/
public class EffectPlayer extends AudioPlayer {
/**
* @param track audio resource
* @param volume volume control
@ -21,8 +21,8 @@ public class EffectPlayer extends AudioPlayer {
{
super(track, volume);
}
/**
* Play at listener
*
@ -32,11 +32,11 @@ public class EffectPlayer extends AudioPlayer {
public void play(double gain, double pitch)
{
if (!hasAudio()) return;
getAudio().play(computeGain(gain), computePitch(pitch), false);
}
/**
* Play at listener
*
@ -46,8 +46,8 @@ public class EffectPlayer extends AudioPlayer {
{
play(gain, 1);
}
/**
* Play at given position
*
@ -58,8 +58,8 @@ public class EffectPlayer extends AudioPlayer {
{
play(gain, 1, pos);
}
/**
* Play at given position
*
@ -70,8 +70,8 @@ public class EffectPlayer extends AudioPlayer {
public void play(double gain, double pitch, Vect pos)
{
if (!hasAudio()) return;
getAudio().play(computeGain(gain), computePitch(pitch), false, pos);
}
}

@ -14,22 +14,22 @@ import mightypork.utils.math.animation.NumAnimated;
* @author Ondřej Hruška (MightyPork)
*/
public class LoopPlayer extends AudioPlayer implements Updateable, Pauseable {
/** animator for fade in and fade out */
private final NumAnimated fadeAnim = new NumAnimated(0);
private double lastUpdateGain = 0;
/** flag that track is paused */
private boolean paused = true;
/** Default fadeIn time */
private double inTime = 1;
/** Default fadeOut time */
private double outTime = 1;
/**
* @param track audio resource
* @param volume volume control
@ -37,11 +37,11 @@ public class LoopPlayer extends AudioPlayer implements Updateable, Pauseable {
public LoopPlayer(DeferredAudio track, Volume volume)
{
super(track, volume);
paused = true;
}
/**
* Set fading duration (seconds)
*
@ -53,8 +53,8 @@ public class LoopPlayer extends AudioPlayer implements Updateable, Pauseable {
inTime = in;
outTime = out;
}
private void initLoop()
{
if (hasAudio() && !getAudio().isActive()) {
@ -62,27 +62,27 @@ public class LoopPlayer extends AudioPlayer implements Updateable, Pauseable {
getAudio().pauseLoop();
}
}
@Override
public void pause()
{
if (!hasAudio() || paused) return;
initLoop();
getAudio().pauseLoop();
paused = true;
}
@Override
public boolean isPaused()
{
return paused;
}
/**
* Alias to resume (more meaningful name)
*/
@ -90,40 +90,40 @@ public class LoopPlayer extends AudioPlayer implements Updateable, Pauseable {
{
resume();
}
@Override
public void resume()
{
if (!hasAudio() || !paused) return;
initLoop();
paused = false;
getAudio().adjustGain(computeGain(fadeAnim.value()));
}
@Override
public void update(double delta)
{
if (!hasAudio() || paused) return;
initLoop();
fadeAnim.update(delta);
final double gain = computeGain(fadeAnim.value());
if (!paused && gain != lastUpdateGain) {
getAudio().adjustGain(gain);
lastUpdateGain = gain;
}
if (gain == 0 && !paused) pause(); // pause on zero volume
}
/**
* Resume if paused, and fade in (pick up from current volume).
*
@ -132,13 +132,13 @@ public class LoopPlayer extends AudioPlayer implements Updateable, Pauseable {
public void fadeIn(double fadeTime)
{
if (!hasAudio()) return;
if (isPaused()) fadeAnim.setTo(0);
resume();
fadeAnim.fadeIn(fadeTime);
}
/**
* Fade out and pause when reached zero volume
*
@ -150,8 +150,8 @@ public class LoopPlayer extends AudioPlayer implements Updateable, Pauseable {
if (isPaused()) return;
fadeAnim.fadeOut(fadeTime);
}
/**
* Fade in with default duration
*/
@ -159,8 +159,8 @@ public class LoopPlayer extends AudioPlayer implements Updateable, Pauseable {
{
fadeIn(inTime);
}
/**
* Fade out with default duration
*/
@ -168,5 +168,5 @@ public class LoopPlayer extends AudioPlayer implements Updateable, Pauseable {
{
fadeOut(outTime);
}
}

@ -31,12 +31,16 @@ public class App extends BusNode {
private final AppBackend backend;
private final EventBus eventBus = new EventBus();
private boolean started = false;
private boolean inited = false;
/** List of installed App plugins */
protected final DelegatingList plugins = new DelegatingList();
/** List of initializers */
protected final List<InitTask> initializers = new ArrayList<>();
/** The used main loop instance */
protected MainLoop mainLoop;
/**
* Create an app with given backend.
@ -100,6 +104,17 @@ public class App extends BusNode {
}
/**
* Set the main loop implementation
*
* @param loop main loop impl
*/
public void setMainLoop(MainLoop loop)
{
this.mainLoop = loop;
}
/**
* Get current backend
*
@ -122,12 +137,32 @@ public class App extends BusNode {
throw new IllegalStateException("Already started.");
}
started = true;
Log.i("Starting init...");
init();
if (mainLoop == null) {
throw new IllegalStateException("The app has no main loop assigned.");
}
Log.i("Starting main loop...");
mainLoop.start();
}
private void init()
{
if (inited) {
throw new IllegalStateException("Already inited.");
}
inited = true;
// pre-init hook, just in case anyone wanted to have one.
Log.f2("Calling pre-init hook...");
preInit();
Log.i("=== Starting initialization sequence ===");
Log.f2("Running init tasks...");
// sort initializers by order.
final List<InitTask> orderedInitializers = InitTask.inOrder(initializers);
@ -149,8 +184,6 @@ public class App extends BusNode {
initTask.after();
}
Log.i("=== Initialization sequence completed ===");
// user can now start the main loop etc.
Log.f2("Calling post-init hook...");
postInit();

@ -16,11 +16,11 @@ import mightypork.utils.eventbus.clients.BusNode;
* @author MightyPork
*/
public abstract class AppBackend extends BusNode {
/** App instance assigned using <code>bind()</code> */
protected App app;
/**
* Assign the initialized app instance to an "app" field.
*
@ -33,30 +33,30 @@ public abstract class AppBackend extends BusNode {
}
this.app = app;
}
/**
* Initialize backend modules, add them to event bus.
*/
public abstract void initialize();
/**
* Get graphics module (screen manager, texture and font loader, renderer)
*
* @return graphics module
*/
public abstract GraphicsModule getGraphics();
/**
* Get audio module (
*
* @return audio module
*/
public abstract AudioModule getAudio();
/**
* Get input module
*

@ -12,7 +12,7 @@ import mightypork.utils.interfaces.Destroyable;
* @author MightyPork
*/
public abstract class BackendModule extends BusNode implements Destroyable {
/**
* Initialize the backend module.<br>
* Any initialization that would normally be done in constructor shall be
@ -20,8 +20,8 @@ public abstract class BackendModule extends BusNode implements Destroyable {
* "call to overridable method from constructor"
*/
public abstract void init();
@Override
@Stub
public void destroy()

@ -0,0 +1,128 @@
package mightypork.gamecore.core;
import java.util.Deque;
import java.util.concurrent.ConcurrentLinkedDeque;
import mightypork.gamecore.graphics.Renderable;
import mightypork.utils.annotations.Stub;
import mightypork.utils.eventbus.clients.BusNode;
import mightypork.utils.eventbus.events.UpdateEvent;
import mightypork.utils.logging.Log;
import mightypork.utils.math.timing.Profiler;
import mightypork.utils.math.timing.TimerDelta;
/**
* Delta-timed game loop with task queue etc.
*
* @author Ondřej Hruška (MightyPork)
*/
public class DeltaMainLoop extends BusNode implements MainLoop {
/**
* Max time spent on main loop tasks per cycle (s)
*/
protected double MAX_TIME_TASKS = 1 / 30D;
/**
* Max delta time (s) per frame.<br>
* If delta is larger than this, it's clamped to it.
*/
protected double MAX_DELTA = 1 / 20D;
private final Deque<Runnable> tasks = new ConcurrentLinkedDeque<>();
private TimerDelta timer;
private Renderable rootRenderable;
private volatile boolean running = true;
@Override
public void setRootRenderable(Renderable rootRenderable)
{
this.rootRenderable = rootRenderable;
}
@Override
public void start()
{
timer = new TimerDelta();
while (running) {
App.gfx().beginFrame();
double delta = timer.getDelta();
if (delta > MAX_DELTA) {
Log.f3("(timing) Clamping delta: was " + delta + " s, MAX_DELTA = " + MAX_DELTA + " s");
delta = MAX_DELTA;
}
// dispatch update event
App.bus().sendDirect(new UpdateEvent(delta));
// run main loop tasks
Runnable r;
final long t = Profiler.begin();
while ((r = tasks.poll()) != null) {
Log.f3(" * Main loop task.");
r.run();
if (Profiler.end(t) > MAX_TIME_TASKS) {
Log.f3("! Time's up, postponing task to next cycle.");
break;
}
}
beforeRender();
if (rootRenderable != null) {
rootRenderable.render();
}
afterRender();
App.gfx().endFrame();
}
}
/**
* Called before render
*/
@Stub
protected void beforeRender()
{
//
}
/**
* Called after render
*/
@Stub
protected void afterRender()
{
//
}
@Override
public void destroy()
{
running = false;
}
@Override
public synchronized void queueTask(Runnable task, boolean skipQueue)
{
if (skipQueue) {
tasks.addFirst(task);
} else {
tasks.addLast(task);
}
}
}

@ -1,143 +1,45 @@
package mightypork.gamecore.core;
import java.util.Deque;
import java.util.concurrent.ConcurrentLinkedDeque;
import mightypork.gamecore.graphics.Renderable;
import mightypork.gamecore.gui.screens.ScreenRegistry;
import mightypork.utils.annotations.Stub;
import mightypork.utils.eventbus.clients.BusNode;
import mightypork.utils.eventbus.events.UpdateEvent;
import mightypork.utils.interfaces.Destroyable;
import mightypork.utils.logging.Log;
import mightypork.utils.math.timing.Profiler;
import mightypork.utils.math.timing.TimerDelta;
/**
* Delta-timed game loop with task queue etc.
* A main loop of the app.
*
* @author Ondřej Hruška (MightyPork)
*/
public class MainLoop extends BusNode implements Destroyable {
/**
* Max time spent on main loop tasks per cycle (s)
*/
protected double MAX_TIME_TASKS = 1 / 30D;
/**
* Max delta time (s) per frame.<br>
* If delta is larger than this, it's clamped to it.
*/
protected double MAX_DELTA = 1 / 20D;
private final Deque<Runnable> tasks = new ConcurrentLinkedDeque<>();
private TimerDelta timer;
private Renderable rootRenderable;
private volatile boolean running = true;
public interface MainLoop extends Destroyable {
/**
* Set primary renderable
*
* @param rootRenderable main {@link Renderable}, typically a
* {@link ScreenRegistry}
*/
public void setRootRenderable(Renderable rootRenderable)
{
this.rootRenderable = rootRenderable;
}
public abstract void setRootRenderable(Renderable rootRenderable);
/**
* Start the loop
* Start the loop. The loop should be terminated when the destroy() method
* is called.
*/
public void start()
{
timer = new TimerDelta();
while (running) {
App.gfx().beginFrame();
double delta = timer.getDelta();
if (delta > MAX_DELTA) {
Log.f3("(timing) Clamping delta: was " + delta + " s, MAX_DELTA = " + MAX_DELTA + " s");
delta = MAX_DELTA;
}
// dispatch update event
App.bus().sendDirect(new UpdateEvent(delta));
// run main loop tasks
Runnable r;
final long t = Profiler.begin();
while ((r = tasks.poll()) != null) {
Log.f3(" * Main loop task.");
r.run();
if (Profiler.end(t) > MAX_TIME_TASKS) {
Log.f3("! Time's up, postponing task to next cycle.");
break;
}
}
beforeRender();
if (rootRenderable != null) {
rootRenderable.render();
}
afterRender();
App.gfx().endFrame();
}
}
/**
* Called before render
*/
@Stub
protected void beforeRender()
{
//
}
/**
* Called after render
*/
@Stub
protected void afterRender()
{
//
}
public abstract void start();
@Override
public void destroy()
{
running = false;
}
public abstract void destroy();
/**
* Add a task to queue to be executed in the main loop (in rendering
* context)
* context). This may be eg. loading textures.
*
* @param task task
* @param skipQueue true to skip the queue
*/
public synchronized void queueTask(Runnable task, boolean skipQueue)
{
if (skipQueue) {
tasks.addFirst(task);
} else {
tasks.addLast(task);
}
}
public abstract void queueTask(Runnable task, boolean skipQueue);
}

@ -4,6 +4,8 @@ package mightypork.gamecore.core.config;
import java.util.HashMap;
import java.util.Map;
import mightypork.gamecore.core.events.ShutdownEvent;
import mightypork.gamecore.core.events.ShutdownListener;
import mightypork.gamecore.input.Key;
import mightypork.gamecore.input.KeyStroke;
import mightypork.utils.config.propmgr.Property;
@ -19,7 +21,7 @@ import mightypork.utils.logging.Log;
*
* @author Ondřej Hruška (MightyPork)
*/
public class Config {
public class Config implements ShutdownListener {
/** Array of configs registered for the app */
protected static Map<String, Config> configs = new HashMap<>();
@ -265,4 +267,11 @@ public class Config {
kp.getValue().setTo(key, mod);
}
@Override
public void onShutdown(ShutdownEvent event)
{
save(); // save changes done to the config
}
}

@ -13,7 +13,7 @@ import mightypork.utils.annotations.Stub;
* @author Ondřej Hruška (MightyPork)
*/
public abstract class InitTaskConfig extends InitTask {
/**
* Add a config with given alias
*
@ -24,16 +24,16 @@ public abstract class InitTaskConfig extends InitTask {
{
Config.register(alias, config);
}
/**
* Initialize the main config.
*
* @return the main config.
*/
protected abstract Config buildConfig();
/**
* Initialize extra configs.<br>
* the addConfig() method can be used to register configs.
@ -42,34 +42,34 @@ public abstract class InitTaskConfig extends InitTask {
protected void buildExtraConfigs()
{
}
// locked to encourage the use of the build* methods.
@Override
public final void init()
{
}
@Override
public final void run()
{
addConfig("main", buildConfig());
buildExtraConfigs();
}
@Override
public String getName()
{
return "config";
}
@Override
public String[] getDependencies()
{
return new String[] { "workdir" };
}
}

@ -16,7 +16,7 @@ import mightypork.utils.config.propmgr.Property;
* @author Ondřej Hruška (MightyPork)
*/
public class KeyStrokeProperty extends Property<KeyStroke> {
/**
* Make a keystroke property
*
@ -28,32 +28,32 @@ public class KeyStrokeProperty extends Property<KeyStroke> {
{
super(key, defaultValue, comment);
}
@Override
public void fromString(String string)
{
if (string != null) {
// keep the same instance
final Key backup_key = value.getKey();
final int backup_mod = value.getMod();
value.loadFromString(string);
if (value.getKey() == Keys.NONE) {
value.setTo(backup_key, backup_mod);
}
}
}
@Override
public String toString()
{
return value.saveToString();
}
@Override
public void setValue(Object value)
{

@ -15,10 +15,10 @@ import mightypork.utils.logging.Log;
* @author Ondřej Hruška (MightyPork)
*/
public class ShutdownEvent extends BusEvent<ShutdownListener> {
private final Runnable shutdownTask;
/**
* Make a shutdown event
*
@ -28,15 +28,15 @@ public class ShutdownEvent extends BusEvent<ShutdownListener> {
{
this.shutdownTask = doShutdown;
}
@Override
protected void handleBy(ShutdownListener handler)
{
handler.onShutdown(this);
}
@Override
public void onDispatchComplete(EventBus bus)
{
@ -47,5 +47,5 @@ public class ShutdownEvent extends BusEvent<ShutdownListener> {
Log.i("Shutdown aborted.");
}
}
}

@ -7,7 +7,7 @@ package mightypork.gamecore.core.events;
* @author Ondřej Hruška (MightyPork)
*/
public interface ShutdownListener {
/**
* Intercept quit request.<br>
* Consume the event to abort shutdown (ie. ask user to save)

@ -21,11 +21,11 @@ import mightypork.utils.logging.Log;
* @author Ondřej Hruška (MightyPork)
*/
public abstract class InitTask {
/** App instance assigned using <code>bind()</code> */
protected App app;
/**
* Assign the initialized app instance to an "app" field.
*
@ -35,8 +35,8 @@ public abstract class InitTask {
{
this.app = app;
}
/**
* An init method that is called before the <code>run()</code> method.<br>
* This method should be left unimplemented in the task, and can be used to
@ -47,8 +47,8 @@ public abstract class InitTask {
{
//
}
/**
* Hook for extra action before the main task action.<br>
* Can be overridden during app configuration to "bake-in" extra actions.
@ -58,14 +58,14 @@ public abstract class InitTask {
{
//
}
/**
* Run the initializer on app.
*/
public abstract void run();
/**
* Hook executed after the "run()" method.<br>
* Can be overridden during app configuration to "bake-in" extra actions.
@ -75,8 +75,8 @@ public abstract class InitTask {
{
//
}
/**
* Get name of this initializer (for dependency resolver).<br>
* The name should be short, snake_case and precise.
@ -84,8 +84,8 @@ public abstract class InitTask {
* @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>
@ -99,8 +99,8 @@ public abstract class InitTask {
{
return new String[] {};
}
/**
* Order init tasks so that all dependencies are loaded before thye are
* needed by the tasks.
@ -111,25 +111,25 @@ public abstract class InitTask {
public static List<InitTask> inOrder(List<InitTask> tasks)
{
final List<InitTask> remaining = new ArrayList<>(tasks);
final List<InitTask> ordered = new ArrayList<>();
final Set<String> loaded = new HashSet<>();
// resolve task order
int addedThisIteration = 0;
do {
for (final Iterator<InitTask> i = remaining.iterator(); i.hasNext();) {
final InitTask task = i.next();
String[] deps = task.getDependencies();
if (deps == null) deps = new String[] {};
int unmetDepsCount = deps.length;
for (final String d : deps) {
if (loaded.contains(d)) unmetDepsCount--;
}
if (unmetDepsCount == 0) {
ordered.add(task);
loaded.add(task.getName());
@ -138,38 +138,38 @@ public abstract class InitTask {
}
}
} while (addedThisIteration > 0);
// check if any tasks are left out
if (remaining.size() > 0) {
// build error message for each bad task
int badInitializers = 0;
for (final InitTask task : remaining) {
if (Reflect.hasAnnotation(task.getClass(), OptionalInitTask.class)) {
continue;
}
badInitializers++;
String notSatisfied = "";
for (final String d : task.getDependencies()) {
if (!loaded.contains(d)) {
if (!notSatisfied.isEmpty()) {
notSatisfied += ", ";
}
notSatisfied += d;
}
}
Log.w("InitTask \"" + task.getName() + "\" - missing dependencies: " + notSatisfied);
}
if (badInitializers > 0) throw new RuntimeException("Some InitTask dependencies could not be satisfied.");
}
return ordered;
}
}

@ -16,14 +16,14 @@ import mightypork.utils.logging.Log;
* @author Ondřej Hruška (MightyPork)
*/
public class InitTaskCrashHandler extends InitTask implements UncaughtExceptionHandler {
@Override
public void run()
{
Thread.setDefaultUncaughtExceptionHandler(this);
}
@Override
@Stub
public void uncaughtException(Thread thread, Throwable throwable)
@ -31,8 +31,8 @@ public class InitTaskCrashHandler extends InitTask implements UncaughtExceptionH
Log.e("The game has crashed.", throwable);
App.shutdown();
}
@Override
public String getName()
{

@ -10,12 +10,12 @@ import mightypork.gamecore.graphics.GraphicsModule;
* @author Ondřej Hruška (MightyPork)
*/
public class InitTaskDisplay extends InitTask {
private int width = 800, height = 600, fps = 60;
private boolean resizable, fullscreen;
private String title = "Game";
/**
* Set initial window size
*
@ -27,8 +27,8 @@ public class InitTaskDisplay extends InitTask {
this.width = width;
this.height = height;
}
/**
* Set whether the window should be resizable
*
@ -38,8 +38,8 @@ public class InitTaskDisplay extends InitTask {
{
this.resizable = resizable;
}
/**
* Set window title
*
@ -49,8 +49,8 @@ public class InitTaskDisplay extends InitTask {
{
this.title = title;
}
/**
* Set desired framerate.
*
@ -60,8 +60,8 @@ public class InitTaskDisplay extends InitTask {
{
this.fps = fps;
}
/**
* Set whether the window should start in fullscreen
*
@ -71,26 +71,26 @@ public class InitTaskDisplay extends InitTask {
{
this.fullscreen = fullscreen;
}
@Override
public void run()
{
final GraphicsModule gfx = app.getBackend().getGraphics();
gfx.setSize(width, height);
gfx.setResizable(resizable);
gfx.setTitle(title);
gfx.setTargetFps(fps);
if (fullscreen) gfx.setFullscreen(true);
}
@Override
public String getName()
{
return "display";
}
}

@ -18,20 +18,20 @@ import mightypork.utils.math.algo.Move;
* @author Ondřej Hruška (MightyPork)
*/
public class InitTaskIonizables extends InitTask {
@Override
public void run()
{
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
{
@ -39,19 +39,19 @@ public class InitTaskIonizables extends InitTask {
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
{
@ -59,15 +59,15 @@ public class InitTaskIonizables extends InitTask {
final int y = in.readInt();
return new Move(x, y);
}
});
}
@Override
public String getName()
{
return "ion";
}
}

@ -17,15 +17,15 @@ import mightypork.utils.string.StringUtil;
* @author Ondřej Hruška (MightyPork)
*/
public class InitTaskLog 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".
@ -36,8 +36,8 @@ public class InitTaskLog extends InitTask {
{
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>
@ -50,11 +50,11 @@ public class InitTaskLog extends InitTask {
if (!StringUtil.isValidFilenameString(logName)) {
throw new IllegalArgumentException("Invalid log name.");
}
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>
@ -66,8 +66,8 @@ public class InitTaskLog extends InitTask {
{
this.archiveCount = archiveCount;
}
/**
* Set logging levels (minimal level of message to be accepted)<br>
* Defaults to ALL, ALL.
@ -80,8 +80,8 @@ public class InitTaskLog extends InitTask {
this.levelWrite = levelWrite;
this.levelPrint = levelPrint;
}
@Override
public void run()
{
@ -90,15 +90,15 @@ public class InitTaskLog extends InitTask {
Log.setLevel(levelWrite);
Log.setSysoutLevel(levelPrint);
}
@Override
public String getName()
{
return "log";
}
@Override
public String[] getDependencies()
{

@ -15,35 +15,35 @@ import mightypork.utils.logging.Log;
*/
@OptionalInitTask
public class InitTaskLogHeader extends InitTask {
@Override
public void run()
{
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.getBaseDir().getCanonicalPath() + "\n";
} catch (final IOException e) {
Log.e(e);
}
Log.i(txt);
}
@Override
public String getName()
{
return "log_header";
}
@Override
public String[] getDependencies()
{

@ -0,0 +1,40 @@
package mightypork.gamecore.core.init;
import mightypork.gamecore.core.MainLoop;
/**
* Task to add a resource loader.<br>
* By default the async resource loader is used
*
* @author Ondřej Hruška (MightyPork)
*/
public abstract class InitTaskMainLoop extends InitTask {
/** The loader. */
protected MainLoop loop;
@Override
public void run()
{
loop = getLoopImpl();
app.setMainLoop(loop);
}
/**
* Create a loader impl
*
* @return loader
*/
protected abstract MainLoop getLoopImpl();
@Override
public String getName()
{
return "resource_loader";
}
}

@ -11,27 +11,27 @@ import mightypork.gamecore.resources.loading.ResourceLoader;
* @author Ondřej Hruška (MightyPork)
*/
public abstract class InitTaskResourceLoader extends InitTask {
/** The loader. */
protected ResourceLoader loader;
@Override
public void run()
{
loader = getLoaderImpl();
if (loader != null) loader.init();
}
/**
* Create a loader impl
*
* @return loader
*/
protected abstract ResourceLoader getLoaderImpl();
@Override
public String getName()
{

@ -12,7 +12,7 @@ import mightypork.gamecore.resources.loading.ResourceLoader;
* @author Ondřej Hruška (MightyPork)
*/
public class InitTaskResourceLoaderAsync extends InitTaskResourceLoader {
/**
* Create a loader impl
*
@ -22,9 +22,9 @@ public class InitTaskResourceLoaderAsync extends InitTaskResourceLoader {
protected ResourceLoader getLoaderImpl()
{
final AsyncResourceLoader loader = new AsyncResourceLoader();
// could now configure the loader
return loader;
}
}

@ -11,8 +11,8 @@ import mightypork.gamecore.resources.loading.ResourceLoader;
* @author Ondřej Hruška (MightyPork)
*/
public class InitTaskResourceLoaderNone extends InitTaskResourceLoader {
@Override
protected ResourceLoader getLoaderImpl()
{

@ -11,21 +11,21 @@ import mightypork.gamecore.resources.ResourceInitializer;
* @author Ondřej Hruška (MightyPork)
*/
public abstract class InitTaskResources extends InitTask implements ResourceInitializer {
@Override
public void run()
{
Res.load(this);
}
@Override
public String getName()
{
return "resources";
}
@Override
public String[] getDependencies()
{

@ -21,13 +21,13 @@ import mightypork.utils.logging.Log;
* @author Ondřej Hruška (MightyPork)
*/
public class InitTaskWorkdir extends InitTask {
private File workdirPath;
private boolean doLock;
private String lockFile = ".lock";
private final Map<String, String> namedPaths = new HashMap<>();
/**
* @param workdir path to the working directory
* @param lock whether to lock the directory (single instance mode)
@ -37,8 +37,8 @@ public class InitTaskWorkdir extends InitTask {
this.workdirPath = workdir;
this.doLock = lock;
}
/**
* Set workdir root path
*
@ -48,8 +48,8 @@ public class InitTaskWorkdir extends InitTask {
{
this.workdirPath = path;
}
/**
* Set whether the workdir should be locked when the app is running, to
* prevent other instances from running simultaneously.
@ -60,8 +60,8 @@ public class InitTaskWorkdir extends InitTask {
{
this.doLock = lock;
}
/**
* Set name of the lock file.
*
@ -71,8 +71,8 @@ public class InitTaskWorkdir extends InitTask {
{
this.lockFile = lockFile;
}
/**
* Add a named path
*
@ -83,13 +83,13 @@ public class InitTaskWorkdir extends InitTask {
{
namedPaths.put(alias, path);
}
@Override
public void run()
{
WorkDir.setBaseDir(workdirPath);
// lock working directory
if (doLock) {
final File lock = WorkDir.getFile(lockFile);
@ -98,13 +98,13 @@ public class InitTaskWorkdir extends InitTask {
return;
}
}
for (final 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>
@ -114,7 +114,7 @@ public class InitTaskWorkdir extends InitTask {
protected void onLockError()
{
Log.e("Could not obtain lock file.\nOnly one instance can run at a time.");
//@formatter:off
JOptionPane.showMessageDialog(
null,
@ -123,11 +123,11 @@ public class InitTaskWorkdir extends InitTask {
JOptionPane.ERROR_MESSAGE
);
//@formatter:on
App.shutdown();
}
@Override
public String getName()
{

@ -14,11 +14,11 @@ import mightypork.utils.eventbus.clients.BusNode;
* @author Ondřej Hruška (MightyPork)
*/
public class AppPlugin extends BusNode {
/** App instance assigned using <code>bind()</code> */
protected App app;
/**
* Assign the initialized app instance to an "app" field.
*
@ -28,8 +28,8 @@ public class AppPlugin extends BusNode {
{
this.app = app;
}
/**
* Initialize the plugin for the given App.<br>
* The plugin is already attached to the event bus.

@ -11,10 +11,10 @@ import mightypork.utils.files.WorkDir;
* @author Ondřej Hruška (MightyPork)
*/
public class InitTaskPluginScreenshot extends InitTask {
private String screenshotDir;
/**
* Initialize to use the "screenshots" directory
*/
@ -22,8 +22,8 @@ public class InitTaskPluginScreenshot extends InitTask {
{
this("screenshots");
}
/**
* Initialize to use the given directory for saving.
*
@ -33,8 +33,8 @@ public class InitTaskPluginScreenshot extends InitTask {
{
this.screenshotDir = dir;
}
/**
* Set screenshot directory
*
@ -44,27 +44,27 @@ public class InitTaskPluginScreenshot extends InitTask {
{
this.screenshotDir = dir;
}
@Override
public void run()
{
WorkDir.addPath("_screenshot_dir", screenshotDir);
app.addPlugin(new ScreenshotPlugin());
}
@Override
public String getName()
{
return "plugin_screenshot";
}
@Override
public String[] getDependencies()
{
return new String[] { "workdir" };
}
}

@ -15,14 +15,14 @@ import mightypork.utils.Support;
* @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()
{

@ -12,11 +12,11 @@ import mightypork.utils.eventbus.events.flags.SingleReceiverEvent;
*/
@SingleReceiverEvent
public class ScreenshotRequest extends BusEvent<ScreenshotPlugin> {
@Override
protected void handleBy(ScreenshotPlugin handler)
{
handler.takeScreenshot();
}
}

@ -19,10 +19,10 @@ import mightypork.utils.logging.Log;
* @author MightyPork
*/
public class TaskTakeScreenshot implements Runnable {
private final Screenshot scr;
/**
* Take screenshot. Must be called in render thread.
*/
@ -30,16 +30,16 @@ public class TaskTakeScreenshot implements Runnable {
{
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);
@ -47,8 +47,8 @@ public class TaskTakeScreenshot implements Runnable {
Log.e("Failed to save screenshot.", e);
}
}
/**
* @return File to save the screenshot to.
*/
@ -57,8 +57,8 @@ public class TaskTakeScreenshot implements Runnable {
final String fname = getBaseFilename();
return findFreeFile(fname);
}
/**
* @return directory for screenshots
*/
@ -66,8 +66,8 @@ public class TaskTakeScreenshot implements Runnable {
{
return WorkDir.getDir("_screenshot_dir");
}
/**
* Get base filename for the screenshot, without extension.
*
@ -77,8 +77,8 @@ public class TaskTakeScreenshot implements Runnable {
{
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.
@ -97,5 +97,5 @@ public class TaskTakeScreenshot implements Runnable {
}
return file;
}
}

@ -13,7 +13,7 @@ import mightypork.utils.eventbus.events.flags.SingleReceiverEvent;
*/
@SingleReceiverEvent
public class FullscreenToggleRequest extends BusEvent<GraphicsModule> {
@Override
protected void handleBy(GraphicsModule handler)
{

@ -22,23 +22,23 @@ import mightypork.utils.math.timing.FpsMeter;
* @author MightyPork
*/
public abstract class GraphicsModule extends BackendModule {
/** X axis vector */
protected static final VectConst AXIS_X = Vect.make(1, 0, 0);
/** Y axis vector */
protected static final VectConst AXIS_Y = Vect.make(0, 1, 0);
/** Z axis vector */
protected static final VectConst AXIS_Z = Vect.make(0, 0, 1);
/**
* Set drawing color
*
* @param color color
*/
public abstract void setColor(Color color);
/**
* Set drawing color, adjust alpha
*
@ -46,8 +46,8 @@ public abstract class GraphicsModule extends BackendModule {
* @param alpha alpha multiplier
*/
public abstract void setColor(Color color, double alpha);
/**
* Translate by x, y
*
@ -55,8 +55,8 @@ public abstract class GraphicsModule extends BackendModule {
* @param y y offset
*/
public abstract void translate(double x, double y);
/**
* Translate by x, y, z
*
@ -65,24 +65,24 @@ public abstract class GraphicsModule extends BackendModule {
* @param z z offset
*/
public abstract void translate(double x, double y, double z);
/**
* Translate by offset vector
*
* @param offset offset coordinate
*/
public abstract void translate(Vect offset);
/**
* Translate by offset vector, ignore Z
*
* @param offset offset coordinate
*/
public abstract void translateXY(Vect offset);
/**
* Set scale for translations and coordinates
*
@ -90,8 +90,8 @@ public abstract class GraphicsModule extends BackendModule {
* @param y y scale
*/
public abstract void scale(double x, double y);
/**
* Set scale for translations and coordinates
*
@ -100,72 +100,72 @@ public abstract class GraphicsModule extends BackendModule {
* @param z z scale
*/
public abstract void scale(double x, double y, double z);
/**
* Set scale for translations and coordinates
*
* @param scale vector
*/
public abstract void scale(Vect scale);
/**
* Set scale for translations and coordinates (same value for X and Y scale)
*
* @param scale scaling factor
*/
public abstract void scaleXY(double scale);
/**
* Set X scale for translations and coordinates
*
* @param scale scaling factor
*/
public abstract void scaleX(double scale);
/**
* Set Y scale for translations and coordinates
*
* @param scale scaling factor
*/
public abstract void scaleY(double scale);
/**
* Set Z scale for translations and coordinates
*
* @param scale scaling factor
*/
public abstract void scaleZ(double scale);
/**
* Rotate coordinate system around X axis
*
* @param angle rotation (in degrees)
*/
public abstract void rotateX(double angle);
/**
* Rotate coordinate system around Y axis
*
* @param angle rotation (in degrees)
*/
public abstract void rotateY(double angle);
/**
* Rotate coordinate system around Z axis
*
* @param angle rotation (in degrees)
*/
public abstract void rotateZ(double angle);
/**
* Rotate coordinate system around given axis
*
@ -173,54 +173,54 @@ public abstract class GraphicsModule extends BackendModule {
* @param axis rotation axis (unit vector)
*/
public abstract void rotate(double angle, Vect axis);
/**
* Store render state on stack<br>
* This includes pushGeometry and pushColor.
*/
public abstract void pushState();
/**
* Restore state from stack (must be pushed first)<br>
* This includes popColor and popGeometry.
*/
public abstract void popState();
/**
* Store current rotation and translation on stack
*/
public abstract void pushGeometry();
/**
* Restore rotation and translation from stack
*/
public abstract void popGeometry();
/**
* Store color on stack (so it can later be restored)
*/
public abstract void pushColor();
/**
* Restore color from stack (must be pushed first)
*/
public abstract void popColor();
/**
* Render 2D quad with currently set color
*
* @param rect drawn rect
*/
public abstract void quad(Rect rect);
/**
* Render 2D quad with given color.<br>
* This may change current drawing color.
@ -229,8 +229,8 @@ public abstract class GraphicsModule extends BackendModule {
* @param color draw color
*/
public abstract void quad(Rect rect, Color color);
/**
* Render 2D quad with gradient.<br>
* This may change current drawing color.
@ -239,8 +239,8 @@ public abstract class GraphicsModule extends BackendModule {
* @param grad gradient
*/
public abstract void quad(Rect rect, Grad grad);
/**
* Render textured quad with current color
*
@ -248,8 +248,8 @@ public abstract class GraphicsModule extends BackendModule {
* @param txquad texture quad
*/
public abstract void quad(Rect rect, TxQuad txquad);
/**
* Render textured quad with given color
*
@ -258,14 +258,14 @@ public abstract class GraphicsModule extends BackendModule {
* @param color color tint
*/
public abstract void quad(Rect rect, TxQuad txquad, Color color);
/**
* Setup projection for 2D graphics, using current screen size
*/
public abstract void setupProjection();
/**
* Get backend-flavoured deferred texture. This should support PNG images.
*
@ -273,8 +273,8 @@ public abstract class GraphicsModule extends BackendModule {
* @return the deferred font
*/
public abstract DeferredTexture createTextureResource(String path);
/**
* Get backend-flavoured deferred font. This should support TTF fonts.
*
@ -282,8 +282,8 @@ public abstract class GraphicsModule extends BackendModule {
* @return the deferred font
*/
public abstract DeferredFont createFontResource(String path);
/**
* Set target fps (for syncing in endFrame() call).<br>
* With vsync enabled, the target fps may not be met.
@ -291,8 +291,8 @@ public abstract class GraphicsModule extends BackendModule {
* @param fps requested fps
*/
public abstract void setTargetFps(int fps);
/**
* Set fullscreen. The fullscreen state will be changed when possible (eg.
* at the end of the current frame) and a {@link ViewportChangeEvent} will
@ -301,14 +301,14 @@ public abstract class GraphicsModule extends BackendModule {
* @param fs true for fullscreen
*/
public abstract void setFullscreen(boolean fs);
/**
* Request fullscreen toggle. See setFullscreen() for more info)
*/
public abstract void switchFullscreen();
/**
* Get fullscreen state (note that methods changing fullscreen may not have
* immediate effect, so this method may report the old state if the
@ -317,8 +317,8 @@ public abstract class GraphicsModule extends BackendModule {
* @return is fullscreen
*/
public abstract boolean isFullscreen();
/**
* Take screenshot (expensive processing should be done in separate thread
* when screenshot is saved).<br>
@ -327,20 +327,20 @@ public abstract class GraphicsModule extends BackendModule {
* @return screenshot object
*/
public abstract Screenshot takeScreenshot();
/**
* Start a render frame - clear buffers, prepare rendering context etc.
*/
public abstract void beginFrame();
/**
* End a render frame: flip buffers, sync to fps...
*/
public abstract void endFrame();
/**
* Set display dimensions
*
@ -348,70 +348,70 @@ public abstract class GraphicsModule extends BackendModule {
* @param height display height (pixels)
*/
public abstract void setSize(int width, int height);
/**
* Set window titlebar text
*
* @param title titlebar text
*/
public abstract void setTitle(String title);
/**
* Enable or disable VSync
*
* @param vsync true for vsync enabled
*/
public abstract void setVSync(boolean vsync);
/**
* Set window resizable / fixed
*
* @param resizable true for resizable
*/
public abstract void setResizable(boolean resizable);
/**
* Get screen rect. Should always return the same Rect instance.
*
* @return the rect
*/
public abstract Rect getRect();
/**
* Get current FPS (eg. measured by a {@link FpsMeter})
*
* @return current FPS
*/
public abstract long getFps();
/**
* Get screen center. Should always return the same {@link Vect} instance.
*
* @return screen center.
*/
public abstract Vect getCenter();
/**
* Get screen size. Should always return the same {@link Vect} instance.
*
* @return size
*/
public abstract Vect getSize();
/**
* @return screen width
*/
public abstract int getWidth();
/**
* @return screen height
*/

@ -7,10 +7,10 @@ package mightypork.gamecore.graphics;
* @author Ondřej Hruška (MightyPork)
*/
public interface Renderable {
/**
* Render on screen.
*/
void render();
}

@ -22,7 +22,7 @@ import java.io.IOException;
* @author MightyPork
*/
public interface Screenshot {
/**
* Process byte buffer and write image to a file.<br>
* Image can be cached for future save.

@ -11,42 +11,42 @@ import mightypork.gamecore.resources.BaseDeferredResource;
* @author Ondřej Hruška (MightyPork)
*/
public abstract class DeferredFont extends BaseDeferredResource implements IFont {
/**
* Requested font size. For bitmap fonts, this should match the actual font
* size (in pixels). The font can be scaled after loaded, but it may be
* cached with this size.
*/
protected double size = 12;
/** Requested font style. If not applicable, fall back to PLAIN */
protected FontStyle style = FontStyle.PLAIN;
/**
* Chars that are required to be loaded in the font. A space glyph must be
* also added when loading.
*/
protected String chars = Glyphs.basic;
/** Requested filtering mode */
protected FilterMode filter = FilterMode.NEAREST;
/** Whether to use anti-aliasing for the font. */
protected boolean antialias = false;
/**
* Ratio of the font to discard at the top (how much of the glyphs height is
* blank from top)
*/
protected double discardTop = 0;
/**
* Ratio of the font to discard at the bottom (how much of the glyphs height
* is blank from bottom)
*/
protected double discardBottom = 0;
/**
* Make a font from resource
*
@ -56,8 +56,8 @@ public abstract class DeferredFont extends BaseDeferredResource implements IFont
{
super(resource);
}
/**
* Set font size. If the font is backed by a texture, this is the size at
* which the font is rendered to the texture. For bitmap fonts, this should
@ -69,8 +69,8 @@ public abstract class DeferredFont extends BaseDeferredResource implements IFont
{
this.size = size;
}
/**
* Set desired font style
*
@ -80,8 +80,8 @@ public abstract class DeferredFont extends BaseDeferredResource implements IFont
{
this.style = style;
}
/**
* Set what chars are to be loaded. The space glyph will be loaded always.
*
@ -91,8 +91,8 @@ public abstract class DeferredFont extends BaseDeferredResource implements IFont
{
this.chars = chars;
}
/**
* Set texture filtering mode. For bitmap fonts, set to NEAREST.
*
@ -102,8 +102,8 @@ public abstract class DeferredFont extends BaseDeferredResource implements IFont
{
this.filter = filter;
}
/**
* Set whether to use antialiasing.
*
@ -113,27 +113,27 @@ public abstract class DeferredFont extends BaseDeferredResource implements IFont
{
this.antialias = antialias;
}
@Override
public final void setDiscardRatio(double top, double bottom)
{
discardTop = top;
discardBottom = bottom;
}
@Override
public final double getTopDiscardRatio()
{
return discardTop;
}
@Override
public final double getBottomDiscardRatio()
{
return discardBottom;
}
}

@ -14,11 +14,11 @@ import mightypork.utils.eventbus.clients.BusNode;
* @author Ondřej Hruška (MightyPork)
*/
public class FontRegistry extends BusNode {
private final HashMap<String, IFont> fonts = new HashMap<>();
private final HashMap<String, String> aliases = new HashMap<>();
/**
* Load a {@link DeferredFont}
*
@ -28,11 +28,11 @@ public class FontRegistry extends BusNode {
public void addFont(String key, DeferredFont font)
{
App.bus().send(new ResourceLoadRequest(font));
fonts.put(key, font);
}
/**
* Add a {@link IFont} to the bank.
*
@ -43,8 +43,8 @@ public class FontRegistry extends BusNode {
{
fonts.put(key, font);
}
/**
* Add a font alias. Useful to specify fonts to use for various parts of the
* app, without having to change the aliases throughout the app whenever the
@ -58,8 +58,8 @@ public class FontRegistry extends BusNode {
{
aliases.put(alias_key, font_key);
}
/**
* Get a loaded {@link IFont}
*
@ -69,14 +69,14 @@ public class FontRegistry extends BusNode {
public IFont getFont(String key)
{
IFont f = fonts.get(key);
if (f == null) f = fonts.get(aliases.get(key));
if (f == null) {
throw new RuntimeException("There's no font called " + key + "!");
}
return f;
}
}

@ -15,12 +15,12 @@ import mightypork.utils.math.constraints.vect.Vect;
* @author Ondřej Hruška (MightyPork)
*/
public class FontRenderer {
private IFont font;
private Color color;
/**
* @param font used font
*/
@ -28,8 +28,8 @@ public class FontRenderer {
{
this(font, RGB.WHITE);
}
/**
* @param font used font
* @param color drawing color
@ -39,8 +39,8 @@ public class FontRenderer {
this.font = font;
this.color = color;
}
/**
* Get region needed to draw text at size
*
@ -52,8 +52,8 @@ public class FontRenderer {
{
return font.getNeededSpace(text).mul(getScale(height));
}
/**
* Get width needed to draw text at size
*
@ -65,14 +65,14 @@ public class FontRenderer {
{
return getNeededSpace(text, height).x();
}
private double getScale(double height)
{
return height / font.getLineHeight();
}
/**
* Change drawing font
*
@ -82,8 +82,8 @@ public class FontRenderer {
{
this.font = font;
}
/**
* Set drawing color
*
@ -93,8 +93,8 @@ public class FontRenderer {
{
this.color = color;
}
/**
* Draw on screen
*
@ -106,18 +106,18 @@ public class FontRenderer {
public void draw(String text, Vect pos, double height, Color color)
{
App.gfx().pushGeometry();
final double sc = getScale(height);
App.gfx().translate(pos.x(), pos.y());
App.gfx().scaleXY(sc);
font.draw(text, color);
App.gfx().popGeometry();
}
/**
* Draw on screen
*
@ -130,8 +130,8 @@ public class FontRenderer {
{
this.draw(text, bounds, align, this.color);
}
/**
* Draw on screen
*
@ -144,26 +144,26 @@ public class FontRenderer {
public void draw(String text, Rect bounds, AlignX align, Color color)
{
Vect start;
switch (align) {
case LEFT:
start = bounds.topLeft();
break;
case CENTER:
start = bounds.topCenter();
break;
case RIGHT:
default:
start = bounds.topRight();
break;
}
draw(text, start, bounds.height().value(), align, color);
}
/**
* Draw on screen
*
@ -176,8 +176,8 @@ public class FontRenderer {
{
draw(text, pos, height, align, this.color);
}
/**
* Draw on screen
*
@ -189,27 +189,27 @@ public class FontRenderer {
*/
public void draw(String text, Vect pos, double height, AlignX align, Color color)
{
final double w = getWidth(text, height);
Vect start;
switch (align) {
case LEFT:
start = pos;
break;
case CENTER:
start = pos.sub(w / 2D, 0);
break;
case RIGHT:
default:
start = pos.sub(w, 0);
break;
}
draw(text, start, height, color);
}
}

@ -1,5 +1,6 @@
package mightypork.gamecore.graphics.fonts;
/**
* Font style enum
*/
@ -13,11 +14,11 @@ public enum FontStyle
ITALIC(2),
/** Bond and italic together */
BOLD_ITALIC(1 + 2);
/** Number associated with the style */
public int numval;
/**
* Font style
*
@ -28,4 +29,4 @@ public enum FontStyle
{
this.numval = style;
}
}
}

@ -8,35 +8,35 @@ package mightypork.gamecore.graphics.fonts;
* @author Ondřej Hruška (MightyPork)
*/
public class Glyphs {
/** A-Z a-z */
public static final String latin = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
/** Extra variants of latin glyphs */
public static final String latin_extra = "ŒÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜŸÝßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿĚŠČŘŽŤŇĎŮěščřžťňďůŁłđ";
/** 0-9 */
public static final String numbers = "0123456789";
/** Commonly used punctuation symbols */
public static final String punctuation = ".-,.?!:;\"'";
/** Less common punctuation symbols */
public static final String punctuation_extra = "()¿¡»«›‹“”‘’„…";
/** Commonly used symbols (that are not included in punctuation) */
public static final String symbols = "[]{}#$%&§*+/<=>@\\^_|~°";
/** Less common symbols */
public static final String symbols_extra = "¥€£¢`ƒ†‡ˆ‰•¤¦¨ªº¹²³¬­¯±´µ¶·¸¼½¾×÷™©­®→↓←↑";
/** Latin, numbers, punctuation and symbols */
public static final String basic = latin + numbers + punctuation + symbols;
/** Extra glyphs to accompany "basic" */
public static final String extra = latin_extra + punctuation_extra + symbols_extra;
/** Basic + Extra */
public static final String all = basic + extra;
}

@ -11,7 +11,7 @@ import mightypork.utils.math.constraints.vect.Vect;
* @author Ondřej Hruška (MightyPork)
*/
public interface IFont {
/**
* Draw without scaling at (0, 0) in given color.
*
@ -19,8 +19,8 @@ public interface IFont {
* @param color draw color
*/
void draw(String text, Color color);
/**
* Get suize needed to render give string
*
@ -28,27 +28,27 @@ public interface IFont {
* @return coord (width, height)
*/
Vect getNeededSpace(String text);
/**
* @return font height
*/
int getLineHeight();
/**
* @param text texted text
* @return space needed
*/
int getWidth(String text);
/**
* @return specified font size
*/
int getFontSize();
/**
* Set what vertical ratio of the font size is blank and should be cut off
* when rendering
@ -57,16 +57,16 @@ public interface IFont {
* @param bottom bottom ratio (0-1)
*/
void setDiscardRatio(double top, double bottom);
/**
* Get top discard ratio (blank unused space)
*
* @return ratio
*/
double getTopDiscardRatio();
/**
* Get bottom discard ratio (blank unused space)
*

@ -15,13 +15,13 @@ import mightypork.utils.math.constraints.rect.Rect;
@Alias(name = "Texture")
@MustLoadInRenderingContext
public abstract class DeferredTexture extends BaseDeferredResource implements ITexture {
/** Used filtering mode */
protected FilterMode filter = FilterMode.NEAREST;
/** Used wrapping mode */
protected WrapMode wrap = WrapMode.CLAMP;
/**
* @param resourcePath resource path
*/
@ -29,29 +29,29 @@ public abstract class DeferredTexture extends BaseDeferredResource implements IT
{
super(resourcePath);
}
@Override
public TxQuad makeQuad(Rect uvs)
{
return new TxQuad(this, uvs);
}
@Override
public void setFilter(FilterMode filterMin)
{
this.filter = filterMin;
}
@Override
public void setWrap(WrapMode wrapping)
{
this.wrap = wrapping;
}
@Override
public QuadGrid grid(int x, int y)
{

@ -11,21 +11,21 @@ import mightypork.utils.math.constraints.rect.Rect;
* @author Ondřej Hruška (MightyPork)
*/
public interface ITexture extends Destroyable {
/**
* Set filter for scaling
*
* @param filter filter
*/
void setFilter(FilterMode filter);
/**
* @param wrapping wrap mode
*/
void setWrap(WrapMode wrapping);
/**
* Get a quad from this texture of given position/size
*
@ -33,8 +33,8 @@ public interface ITexture extends Destroyable {
* @return the quad
*/
TxQuad makeQuad(Rect uvs);
/**
* Get a grid for given number of tiles
*
@ -43,20 +43,20 @@ public interface ITexture extends Destroyable {
* @return grid
*/
QuadGrid grid(int x, int y);
/**
* @return source image width (corresponding to width01)
*/
int getImageWidth();
/**
* @return source image height (corresponding to height01)
*/
int getImageHeight();
/**
* @return true if the image is RGBA
*/

@ -11,14 +11,14 @@ import mightypork.utils.math.constraints.rect.Rect;
* @author Ondřej Hruška (MightyPork)
*/
public class QuadGrid {
private final ITexture tx;
private final int txHeight;
private final int txWidth;
private final double tileW;
private final double tileH;
/**
* @param tx backing texture
* @param tilesX number of tile columns
@ -32,8 +32,8 @@ public class QuadGrid {
this.tileW = 1D / tilesX;
this.tileH = 1D / tilesY;
}
/**
* Make square quad at given coords (one grid cell)
*
@ -46,11 +46,11 @@ public class QuadGrid {
if (x < 0 || x >= txWidth || y < 0 || y >= txHeight) {
throw new IndexOutOfBoundsException("Requested invalid txquad coordinates.");
}
return makeQuad(x, y, 1, 1);
}
/**
* Make square quad at given coords, with arbitrary size. Coordinates are
* multiples of cell size.
@ -66,15 +66,15 @@ public class QuadGrid {
if (x < 0 || x >= txWidth || y < 0 || y >= txHeight) {
throw new IndexOutOfBoundsException("Requested invalid txquad coordinates.");
}
if (x + width > txWidth || y + height > txHeight) {
throw new IndexOutOfBoundsException("Requested invalid txquad size (would go beyond texture size).");
}
return tx.makeQuad(Rect.make(tileW * x, tileH * y, tileW * width, tileH * height));
}
/**
* Make a sheet.
*
@ -89,11 +89,11 @@ public class QuadGrid {
if (x < 0 || x >= txWidth || y < 0 || y >= txHeight) {
throw new IndexOutOfBoundsException("Requested invalid txquad coordinates.");
}
if (x + width > txWidth || y + height > txHeight) {
throw new IndexOutOfBoundsException("Requested invalid txsheet size (would go beyond texture size).");
}
return makeQuad(x, y).makeSheet(width, height);
}
}

@ -17,11 +17,11 @@ import mightypork.utils.math.constraints.rect.Rect;
* @author Ondřej Hruška (MightyPork)
*/
public class TextureRegistry {
private final Map<String, ITexture> textures = new HashMap<>();
private final Map<String, TxSheet> sheets = new HashMap<>();
/**
* Load a texture from resource.
*
@ -35,13 +35,13 @@ public class TextureRegistry {
final DeferredTexture texture = App.gfx().createTextureResource(resourcePath);
texture.setFilter(filter);
texture.setWrap(wrap);
App.bus().send(new ResourceLoadRequest(texture));
App.bus().send(new ResourceLoadRequest(texture));
return texture;
}
/**
* Load a texture from resource; if key is not null, the texture will be
* added to the bank.
@ -55,18 +55,18 @@ public class TextureRegistry {
public ITexture addTexture(String key, String resourcePath, FilterMode filter, WrapMode wrap)
{
if (key != null) if (textures.containsKey(key)) throw new KeyAlreadyExistsException();
final ITexture texture = loadTexture(resourcePath, filter, wrap);
if (key != null) {
textures.put(key, texture);
add(key, texture.makeQuad(Rect.ONE));
}
return texture;
}
/**
* Add already created quad to the quad registry
*
@ -76,11 +76,11 @@ public class TextureRegistry {
public void add(String quadKey, TxQuad quad)
{
if (sheets.containsKey(quadKey)) throw new KeyAlreadyExistsException();
sheets.put(quadKey, quad.makeSheet(1, 1));
}
/**
* Add an already created sheet
*
@ -90,11 +90,11 @@ public class TextureRegistry {
public void add(String sheetKey, TxSheet sheet)
{
if (sheets.containsKey(sheetKey)) throw new KeyAlreadyExistsException();
sheets.put(sheetKey, sheet);
}
/**
* Get a {@link TxQuad} for key; if it was added as sheet, the first quad
* ofthe sheet is returned.
@ -106,8 +106,8 @@ public class TextureRegistry {
{
return getSheet(key).getQuad(0); // get the first
}
/**
* Get a loaded {@link ITexture}
*
@ -117,13 +117,13 @@ public class TextureRegistry {
public ITexture getTexture(String key)
{
final ITexture tx = textures.get(key);
if (tx == null) throw new RuntimeException("There's no texture called \"" + key + "\"!");
return tx;
}
/**
* Get a {@link TxSheet} for key
*
@ -133,12 +133,12 @@ public class TextureRegistry {
public TxSheet getSheet(String key)
{
final TxSheet sh = sheets.get(key);
if (sh == null) {
throw new RuntimeException("There's no sheet called \"" + key + "\"!");
}
return sh;
}
}

@ -11,16 +11,16 @@ import mightypork.utils.math.constraints.rect.RectConst;
* @author Ondřej Hruška (MightyPork)
*/
public class TxQuad {
/** The texture */
public final ITexture tx;
/** Coords in texture (0-1) */
public final RectConst uvs;
private boolean flipX;
private boolean flipY;
/**
* TxQuad from origin and size in pixels
*
@ -35,11 +35,11 @@ public class TxQuad {
{
final double w = tx.getImageWidth();
final double h = tx.getImageHeight();
return fromSize(tx, xPx / w, yPx / h, widthPx / w, heightPx / h);
}
/**
* TxQuad from origin and size 0-1
*
@ -54,8 +54,8 @@ public class TxQuad {
{
return new TxQuad(tx, x1, y1, x1 + width, y1 + height);
}
/**
* Make of coords
*
@ -69,8 +69,8 @@ public class TxQuad {
{
this(tx, Rect.make(x1, y1, x2, y2));
}
/**
* @param tx Texture
* @param uvs Rect of texture UVs (0-1); will be frozen.
@ -80,8 +80,8 @@ public class TxQuad {
this.tx = tx;
this.uvs = uvs.freeze();
}
/**
* Clone another
*
@ -94,8 +94,8 @@ public class TxQuad {
this.flipX = txQuad.flipX;
this.flipY = txQuad.flipY;
}
/**
* Get copy
*
@ -105,8 +105,8 @@ public class TxQuad {
{
return new TxQuad(this);
}
/**
* Make a sheet starting with this quad, spannign to right and down.
*
@ -118,8 +118,8 @@ public class TxQuad {
{
return new TxSheet(this, (int) Math.round(width), (int) Math.round(height));
}
/**
* @return copy flipped X
*/
@ -129,8 +129,8 @@ public class TxQuad {
copy.flipX ^= true;
return copy;
}
/**
* @return copy flipped Y
*/
@ -140,8 +140,8 @@ public class TxQuad {
copy.flipY ^= true;
return copy;
}
/**
* @return true if the quad is to be rendered flipped vertically
*/
@ -149,8 +149,8 @@ public class TxQuad {
{
return flipY;
}
/**
* @return true if the quad is to be rendered flipped horizontally
*/
@ -158,8 +158,8 @@ public class TxQuad {
{
return flipX;
}
/**
* Use the same flit/other attributes as the original txQuad
*

@ -13,16 +13,16 @@ import mightypork.utils.logging.Log;
* @author Ondřej Hruška (MightyPork)
*/
public class TxSheet {
private final TxQuad original;
private final TxQuad[] sprites;
private final int width;
private final Random rand = new Random();
private final Random randForSeed = new Random();
private final int count;
/**
* Make a sprite sheet
*
@ -35,11 +35,11 @@ public class TxSheet {
this.original = tx;
this.width = width;
this.count = width * height;
this.sprites = new TxQuad[count];
}
/**
* @return number of quads
*/
@ -47,8 +47,8 @@ public class TxSheet {
{
return count;
}
/**
* Get a quad based on ratio 0-1 (0: first, 1: last)
*
@ -59,8 +59,8 @@ public class TxSheet {
{
return getQuad((int) Math.round((count - 1) * ratio));
}
/**
* Get quad of index
*
@ -73,25 +73,25 @@ public class TxSheet {
Log.w("Index out of bounds: " + index + ", allowed: 0.." + count);
index = index % count;
}
// lazy - init only when needed
if (sprites[index] == null) {
final int x = index % width;
final int y = index / width;
final double origW = original.uvs.width().value();
final double origH = original.uvs.height().value();
final TxQuad txq = new TxQuad(original.tx, original.uvs.move(x * origW, y * origH));
txq.dupeAttrs(original);
sprites[index] = txq;
}
return sprites[index];
}
/**
* Get entirely random TxQuad from this sheet
*
@ -101,8 +101,8 @@ public class TxSheet {
{
return getQuad(rand.nextInt(count));
}
/**
* Get random TxQuad from this sheet
*
@ -114,8 +114,8 @@ public class TxSheet {
randForSeed.setSeed(seed);
return getQuad(randForSeed.nextInt(count));
}
/**
* Get random TxQuad from this sheet
*

@ -10,10 +10,10 @@ import mightypork.utils.interfaces.Enableable;
* @author Ondřej Hruška (MightyPork)
*/
public abstract class Action implements Runnable, Enableable {
private boolean enabled = true;
/**
* Enable the action
*
@ -24,8 +24,8 @@ public abstract class Action implements Runnable, Enableable {
{
this.enabled = enable;
}
/**
* @return true if this action is enabled.
*/
@ -34,8 +34,8 @@ public abstract class Action implements Runnable, Enableable {
{
return enabled;
}
/**
* Run the action, if it's enabled.
*/
@ -44,11 +44,11 @@ public abstract class Action implements Runnable, Enableable {
{
if (enabled) execute();
}
/**
* Do the work.
*/
protected abstract void execute();
}

@ -14,12 +14,12 @@ import mightypork.utils.interfaces.Enableable;
* @author Ondřej Hruška (MightyPork)
*/
public class ActionGroup implements Enableable {
private boolean enabled = true;
private final Set<Enableable> groupMembers = new HashSet<>();
@Override
public void setEnabled(boolean yes)
{
@ -27,15 +27,15 @@ public class ActionGroup implements Enableable {
for (final Enableable e : groupMembers)
e.setEnabled(yes);
}
@Override
public boolean isEnabled()
{
return enabled;
}
/**
* Add an {@link Enableable} to the group
*
@ -45,8 +45,8 @@ public class ActionGroup implements Enableable {
{
groupMembers.add(member);
}
/**
* Remove a group member
*
@ -56,5 +56,5 @@ public class ActionGroup implements Enableable {
{
groupMembers.remove(member);
}
}

@ -7,7 +7,7 @@ package mightypork.gamecore.gui;
* @author Ondřej Hruška (MightyPork)
*/
public interface HasAction {
/**
* Assign an action
*

@ -23,15 +23,15 @@ import mightypork.utils.math.constraints.rect.proxy.RectProxy;
* @author Ondřej Hruška (MightyPork)
*/
public abstract class BaseComponent extends AbstractRectCache implements Component, LayoutChangeListener {
private Rect source;
private boolean visible = true;
private boolean enabled = true;
private int indirectDisableLevel = 0;
private Num alphaMul = Num.ONE;
/**
* Create a base component.<br>
* By default, disable caching to avoid problems with updating. Caching can
@ -41,47 +41,47 @@ public abstract class BaseComponent extends AbstractRectCache implements Compone
{
enableCaching(false);
}
@Override
public void setRect(RectBound rect)
{
this.source = new RectProxy(rect);
}
@Override
public final boolean isVisible()
{
return visible;
}
@Override
public final void setVisible(boolean visible)
{
this.visible = visible;
}
@Override
public final Rect getCacheSource()
{
return source.round(); // round to avoid visual artifacts in fonts and such
}
@Override
public final void render()
{
if (!isVisible()) return;
Color.pushAlpha(alphaMul);
renderComponent();
Color.popAlpha();
}
@Override
public final void onLayoutChanged()
{
@ -91,63 +91,63 @@ public abstract class BaseComponent extends AbstractRectCache implements Compone
Log.e("Component is missing a bounding rect, at: " + Support.str(getClass()));
}
}
@Override
public final void onConstraintChanged()
{
updateLayout();
}
@Override
public final boolean isMouseOver()
{
return App.input().getMousePos().isInside(this);
}
/**
* Draw the component (it's visible)
*/
protected abstract void renderComponent();
@Override
@Stub
public void updateLayout()
{
}
@Override
public void setEnabled(boolean yes)
{
enabled = yes;
}
@Override
public boolean isEnabled()
{
return enabled && isIndirectlyEnabled();
}
@Override
public final void setAlpha(Num alpha)
{
this.alphaMul = alpha;
}
@Override
public final void setAlpha(double alpha)
{
this.alphaMul = Num.make(alpha);
}
@Override
public void setIndirectlyEnabled(boolean yes)
{
@ -157,15 +157,15 @@ public abstract class BaseComponent extends AbstractRectCache implements Compone
if (indirectDisableLevel > 0) indirectDisableLevel--;
}
}
@Override
public boolean isIndirectlyEnabled()
{
return indirectDisableLevel == 0;
}
@Override
public boolean isDirectlyEnabled()
{

@ -12,43 +12,43 @@ import mightypork.utils.math.constraints.num.Num;
* @author Ondřej Hruška (MightyPork)
*/
public interface Component extends Enableable, Hideable, PluggableRenderable {
/**
* Render the component, if it is visible.
*/
@Override
void render();
/**
* The bounding rect was changed. The component should now update any cached
* constraints derived from it.
*/
void updateLayout();
/**
* @return true if mouse is currently over the component
*/
boolean isMouseOver();
/**
* Set alpha multiplier for this and nested components
*
* @param alpha alpha multiplier (dynamic value)
*/
void setAlpha(Num alpha);
/**
* Set alpha multiplier for this and nested components
*
* @param alpha alpha multiplier (constant value)
*/
void setAlpha(double alpha);
/**
* Indirectly enable / disable, used for nested hierarchies.<br>
* When component is twice indirectly disabled, it needs to be twice
@ -57,8 +57,8 @@ public interface Component extends Enableable, Hideable, PluggableRenderable {
* @param yes
*/
void setIndirectlyEnabled(boolean yes);
/**
* Check if the compionent is not indirectly disabled. May still be directly
* disabled.
@ -66,8 +66,8 @@ public interface Component extends Enableable, Hideable, PluggableRenderable {
* @return indirectly enabled
*/
boolean isIndirectlyEnabled();
/**
* Check if the component is directly enabled (set by setEnabled()). May
* still be indirectly disabled.
@ -75,16 +75,16 @@ public interface Component extends Enableable, Hideable, PluggableRenderable {
* @return directly enabled
*/
boolean isDirectlyEnabled();
/**
* Set directly enabled (must be both directly and indirectly enabled to be
* enabled completely)
*/
@Override
public void setEnabled(boolean yes);
/**
* Check if the component is both directly and indirectly enabled
*

@ -8,7 +8,7 @@ package mightypork.gamecore.gui.components;
* @author Ondřej Hruška (MightyPork)
*/
public interface DynamicWidthComponent extends Component {
/**
* Get current width, if the element has specified height
*

@ -10,7 +10,7 @@ import mightypork.utils.eventbus.clients.ToggleableClient;
* @author Ondřej Hruška (MightyPork)
*/
public abstract class InputComponent extends BaseComponent implements ToggleableClient {
@Override
public boolean isListening()
{

@ -15,11 +15,11 @@ import mightypork.utils.math.constraints.rect.RectBound;
* @author Ondřej Hruška (MightyPork)
*/
public abstract class LayoutComponent extends BaseComponent implements ClientHub {
private final DelegatingList clientList;
final LinkedList<Component> components = new LinkedList<>();
/**
* Layout component with the given context (container)
*
@ -31,8 +31,8 @@ public abstract class LayoutComponent extends BaseComponent implements ClientHub
setRect(context);
enableCaching(true); // layout is typically updated only when screen resizes.
}
/**
* Component without context (can be assigned a context using
* <code>setRect()</code>)
@ -41,56 +41,56 @@ public abstract class LayoutComponent extends BaseComponent implements ClientHub
{
this(null);
}
@Override
public Collection<Object> getChildClients()
{
return clientList;
}
@Override
public boolean doesDelegate()
{
return clientList.doesDelegate();
}
@Override
public boolean isListening()
{
return clientList.isListening();
}
@Override
public void addChildClient(Object client)
{
clientList.add(client);
}
@Override
public void removeChildClient(Object client)
{
clientList.remove(client);
}
@Override
public void setEnabled(boolean yes)
{
if (isDirectlyEnabled() != yes) {
super.setEnabled(yes);
for (final Component c : components) {
c.setIndirectlyEnabled(yes);
}
}
}
/**
* Connect to bus and add to element list
*
@ -102,12 +102,12 @@ public abstract class LayoutComponent extends BaseComponent implements ClientHub
if (component == this) {
throw new IllegalArgumentException("Uruboros. (infinite recursion evaded)");
}
components.add(component);
addChildClient(component);
}
@Override
public void renderComponent()
{
@ -115,8 +115,8 @@ public abstract class LayoutComponent extends BaseComponent implements ClientHub
cmp.render();
}
}
@Override
public void updateLayout()
{
@ -124,13 +124,13 @@ public abstract class LayoutComponent extends BaseComponent implements ClientHub
cmp.updateLayout();
}
}
@Override
public void setIndirectlyEnabled(boolean yes)
{
super.setIndirectlyEnabled(yes);
for (final Component cmp : components) {
cmp.setIndirectlyEnabled(yes);
}

@ -15,36 +15,36 @@ import mightypork.utils.math.constraints.vect.proxy.VectAdapter;
* @author Ondřej Hruška (MightyPork)
*/
public abstract class LinearComponent extends BaseComponent implements DynamicWidthComponent {
private final Rect rect = new Rect() {
@Override
public Vect size()
{
return new Vect() {
@Override
public double x()
{
return computeWidth(y());
}
@Override
public double y()
{
return height.value();
}
};
}
@Override
public Vect origin()
{
return new VectAdapter() {
@Override
protected Vect getSource()
{
@ -53,11 +53,11 @@ public abstract class LinearComponent extends BaseComponent implements DynamicWi
};
}
};
private Vect origin;
private Num height;
/**
* Create a linear component
*/
@ -65,15 +65,15 @@ public abstract class LinearComponent extends BaseComponent implements DynamicWi
{
super.setRect(rect);
}
@Override
public void setRect(RectBound rect)
{
throw new RuntimeException("Cannot assign a rect to a linear component. Set origin and height instead.");
}
/**
* Set component's height
*
@ -83,8 +83,8 @@ public abstract class LinearComponent extends BaseComponent implements DynamicWi
{
this.height = height;
}
/**
* Set component's origin
*

@ -13,16 +13,16 @@ import mightypork.utils.math.constraints.rect.RectBound;
* @author Ondřej Hruška (MightyPork)
*/
public interface PluggableRenderable extends Renderable, PluggableRectBound {
@Override
void render();
@Override
Rect getRect();
@Override
void setRect(RectBound rect);
}

@ -9,40 +9,40 @@ import mightypork.gamecore.input.events.MouseButtonHandler;
public abstract class ClickableComponent extends InputComponent implements HasAction, MouseButtonHandler {
protected boolean btnDownOver;
private Action action;
@Override
public void setAction(Action action)
{
this.action = action;
}
protected void triggerAction()
{
if (action != null && isEnabled()) action.run();
}
@Override
public void receive(MouseButtonEvent event)
{
if (!event.isButtonEvent()) return;
if (event.isDown()) {
btnDownOver = event.isOver(this);
}
if (event.isUp()) {
if (btnDownOver && event.isOver(this)) {
triggerAction();
event.consume();
}
btnDownOver = false;
}
}

@ -9,41 +9,41 @@ import mightypork.utils.eventbus.clients.DelegatingClient;
public class ClickableWrapper extends ClickableComponent implements DelegatingClient {
private final Component wrapped;
private final ClientList list;
public ClickableWrapper(Component wrapped)
{
this.wrapped = wrapped;
wrapped.setRect(this);
list = new ClientList(wrapped);
}
@Override
public Collection<?> getChildClients()
{
return list;
}
@Override
public boolean doesDelegate()
{
return true;
}
@Override
protected void renderComponent()
{
wrapped.render();
}
@Override
public void setEnabled(boolean yes)
{
@ -52,13 +52,13 @@ public class ClickableWrapper extends ClickableComponent implements DelegatingCl
wrapped.setIndirectlyEnabled(yes);
}
}
@Override
public void setIndirectlyEnabled(boolean yes)
{
super.setIndirectlyEnabled(yes);
wrapped.setIndirectlyEnabled(yes);
}
}

@ -18,31 +18,31 @@ import mightypork.utils.math.constraints.vect.var.VectVar;
* @author Ondřej Hruška (MightyPork)
*/
public class TextButton extends ClickableComponent implements DynamicWidthComponent {
public final TextPainter textPainter;
private final VectVar offset = Vect.makeVar();
public Vect offsetPassive = height().div(16).toVectXY();
public Vect offsetOver = height().div(20).toVectXY();
public Vect offsetUnder = height().div(32).toVectXY();
private final Color color;
private boolean hoverMove = true;
public TextButton(IFont font, String text, Color color)
{
this.color = color;
this.textPainter = new TextPainter(font, AlignX.CENTER, this.color, text);
this.textPainter.setRect(this);
this.textPainter.setShadow(RGB.BLACK_30, offset);
textPainter.setVPaddingPercent(5);
}
@Override
protected void renderComponent()
{
@ -55,11 +55,11 @@ public class TextButton extends ClickableComponent implements DynamicWidthCompon
} else {
offset.setTo(offsetPassive);
}
textPainter.render();
}
/**
* Disable offset change on hover
*/
@ -67,12 +67,12 @@ public class TextButton extends ClickableComponent implements DynamicWidthCompon
{
hoverMove = false;
}
@Override
public double computeWidth(double height)
{
return textPainter.computeWidth(height);
}
}

@ -6,37 +6,37 @@ import mightypork.utils.math.constraints.rect.RectBound;
public class ColumnLayout extends GridLayout {
private int col = 0;
public ColumnLayout(int rows)
{
this(null, rows);
}
public ColumnLayout(RectBound context, int cols)
{
super(context, 1, cols);
}
public void add(final Component elem)
{
add(elem, 1);
}
public void add(final Component elem, int colSpan)
{
if (elem == null) return;
put(elem, 0, col, 1, colSpan);
col += colSpan;
}
public void skip(int cols)
{
col += cols;

@ -12,18 +12,18 @@ import mightypork.utils.math.constraints.rect.RectBound;
* @author Ondřej Hruška (MightyPork)
*/
public class ConstraintLayout extends LayoutComponent {
public ConstraintLayout()
{
}
public ConstraintLayout(RectBound context)
{
super(context);
}
/**
* Add a component to the layout.<br>
* The component's rect must be set up manually.
@ -34,5 +34,5 @@ public class ConstraintLayout extends LayoutComponent {
{
attach(component);
}
}

@ -15,12 +15,12 @@ import mightypork.utils.math.constraints.rect.RectBound;
* @author Ondřej Hruška (MightyPork)
*/
public class FlowColumnLayout extends LayoutComponent {
private int col = 0;
private Num elementWidth;
private final AlignX align;
/**
* @param context context
* @param elementWidth width of all elements
@ -31,13 +31,13 @@ public class FlowColumnLayout extends LayoutComponent {
super(context);
this.elementWidth = elementWidth;
this.align = align;
if (align != AlignX.LEFT && align != AlignX.RIGHT) {
throw new IllegalArgumentException("Can align only left or right.");
}
}
/**
* make a new holder.<br>
* Context must be assigned before rendering.
@ -49,8 +49,8 @@ public class FlowColumnLayout extends LayoutComponent {
{
this(null, elementWidth, align);
}
/**
* Add an item
*
@ -59,9 +59,9 @@ public class FlowColumnLayout extends LayoutComponent {
public void add(final Component elem)
{
if (elem == null) return;
final Rect r;
switch (align) {
case LEFT:
r = leftEdge().growRight(elementWidth).moveX(elementWidth.mul(col++));
@ -72,16 +72,16 @@ public class FlowColumnLayout extends LayoutComponent {
default:
throw new IllegalArgumentException("Bad align.");
}
elem.setRect(r);
attach(elem);
}
public void setElementWidth(Num elementWidth)
{
this.elementWidth = elementWidth;
}
}

@ -15,12 +15,12 @@ import mightypork.utils.math.constraints.rect.RectBound;
* @author Ondřej Hruška (MightyPork)
*/
public class FlowRowLayout extends LayoutComponent {
private int row = 0;
private Num elementHeight;
private final AlignY align;
/**
* @param context context
* @param elementHeight height of all elements
@ -31,13 +31,13 @@ public class FlowRowLayout extends LayoutComponent {
super(context);
this.elementHeight = elementHeight;
this.align = align;
if (align != AlignY.TOP && align != AlignY.BOTTOM) {
throw new IllegalArgumentException("Can align only to top or bottom.");
}
}
/**
* make a new holder.<br>
* Context must be assigned before rendering.
@ -49,8 +49,8 @@ public class FlowRowLayout extends LayoutComponent {
{
this(null, elementHeight, align);
}
/**
* Add an item
*
@ -59,9 +59,9 @@ public class FlowRowLayout extends LayoutComponent {
public void add(final Component elem)
{
if (elem == null) return;
final Rect r;
switch (align) {
case TOP:
r = topEdge().growDown(elementHeight).moveY(elementHeight.mul(row++));
@ -72,13 +72,13 @@ public class FlowRowLayout extends LayoutComponent {
default:
throw new IllegalArgumentException("Bad align.");
}
elem.setRect(r);
attach(elem);
}
public void setElementHeight(Num elementHeight)
{
this.elementHeight = elementHeight;

@ -13,10 +13,10 @@ import mightypork.utils.math.constraints.rect.builders.TiledRect;
* @author Ondřej Hruška (MightyPork)
*/
public class GridLayout extends LayoutComponent {
private final TiledRect tiler;
/**
* @param context context
* @param rows number of rows
@ -27,8 +27,8 @@ public class GridLayout extends LayoutComponent {
super(context);
this.tiler = tiles(cols, rows);
}
/**
* make a new holder.<br>
* Context must be assigned before rendering.
@ -40,8 +40,8 @@ public class GridLayout extends LayoutComponent {
{
this(null, rows, cols);
}
/**
* Add a row to the holder.
*
@ -52,13 +52,13 @@ public class GridLayout extends LayoutComponent {
public void put(Component elem, int row, int column)
{
if (elem == null) return;
elem.setRect(tiler.tile(column, row));
attach(elem);
}
/**
* Put with span
*
@ -71,10 +71,10 @@ public class GridLayout extends LayoutComponent {
public void put(Component elem, int row, int column, int rowspan, int colspan)
{
if (elem == null) return;
elem.setRect(tiler.span(column, row, colspan, rowspan));
attach(elem);
}
}

@ -10,7 +10,7 @@ import mightypork.gamecore.gui.components.BaseComponent;
* @author Ondřej Hruška (MightyPork)
*/
public class NullComponent extends BaseComponent {
@Override
protected void renderComponent()
{

@ -6,37 +6,37 @@ import mightypork.utils.math.constraints.rect.RectBound;
public class RowLayout extends GridLayout {
private int row = 0;
public RowLayout(int rows)
{
this(null, rows);
}
public RowLayout(RectBound context, int rows)
{
super(context, rows, 1);
}
public void add(final Component elem)
{
add(elem, 1);
}
public void add(final Component elem, int rowSpan)
{
if (elem == null) return;
put(elem, row, 0, rowSpan, 1);
row += rowSpan;
}
public void skip(int rows)
{
row += rows;

@ -15,11 +15,11 @@ import mightypork.utils.eventbus.clients.DelegatingClient;
* @author Ondřej Hruška (MightyPork)
*/
public abstract class AbstractLinearWrapper extends LinearComponent implements DelegatingClient {
protected final Component wrapped;
private final ClientList list;
/**
* @param wrapped wrapped component. Can be null.
*/
@ -34,32 +34,32 @@ public abstract class AbstractLinearWrapper extends LinearComponent implements D
wrapped.setRect(this);
}
}
list = new ClientList(wrapped);
}
@Override
protected void renderComponent()
{
if (wrapped != null) wrapped.render();
}
@Override
public Collection<?> getChildClients()
{
return list;
}
@Override
public boolean doesDelegate()
{
return true;
}
@Override
public void setEnabled(boolean yes)
{
@ -68,8 +68,8 @@ public abstract class AbstractLinearWrapper extends LinearComponent implements D
wrapped.setIndirectlyEnabled(yes);
}
}
@Override
public void setIndirectlyEnabled(boolean yes)
{

@ -11,13 +11,13 @@ import mightypork.utils.math.constraints.num.Num;
* @author Ondřej Hruška (MightyPork)
*/
public class LinearGap extends LinearRectangle {
public LinearGap(Num width)
{
super(new NullComponent(), width);
}
public LinearGap(double heightPercent)
{
this(Num.ZERO);

@ -20,27 +20,27 @@ import mightypork.utils.math.constraints.vect.proxy.VectAdapter;
* @author Ondřej Hruška (MightyPork)
*/
public class LinearLayout extends LayoutComponent {
public LinearLayout(AlignX align)
{
this.align = align;
}
public LinearLayout(RectBound context, AlignX align)
{
super(context);
this.align = align;
}
private final NumSum totalWidth = new NumSum();
private final Vect leftAlignOrigin = LinearLayout.this.origin();
private final Vect centerAlignOrigin = LinearLayout.this.topCenter().sub(totalWidth.half(), Num.ZERO);
private final Vect rightAlignOrigin = LinearLayout.this.topRight().sub(totalWidth, Num.ZERO);
private final Vect leftMostOrigin = new VectAdapter() {
@Override
protected Vect getSource()
{
@ -55,18 +55,18 @@ public class LinearLayout extends LayoutComponent {
}
}
};
private Vect nextOrigin = leftMostOrigin;
private AlignX align = AlignX.LEFT;
public void add(DynamicWidthComponent dwcomp)
{
add(new LinearWrapper(dwcomp));
}
public void add(LinearComponent lincomp)
{
lincomp.setHeight(height());
@ -75,14 +75,14 @@ public class LinearLayout extends LayoutComponent {
totalWidth.addSummand(lincomp.width());
attach(lincomp);
}
public void setAlign(AlignX align)
{
this.align = align;
}
/**
* Add a gap.
*

@ -6,27 +6,27 @@ import mightypork.utils.math.constraints.num.Num;
public class LinearRectangle extends AbstractLinearWrapper {
private Num width;
public LinearRectangle(Component wrapped, Num width)
{
super(wrapped);
this.width = width;
}
public void setWidth(Num width)
{
this.width = width;
}
@Override
public double computeWidth(double height)
{
return this.width.value();
}
}

@ -5,17 +5,17 @@ import mightypork.gamecore.gui.components.Component;
public class LinearSquare extends AbstractLinearWrapper {
public LinearSquare(Component wrapped)
{
super(wrapped);
}
@Override
public double computeWidth(double height)
{
return height;
}
}

@ -5,17 +5,17 @@ import mightypork.gamecore.gui.components.DynamicWidthComponent;
public class LinearWrapper extends AbstractLinearWrapper {
public LinearWrapper(DynamicWidthComponent wrapped)
{
super(wrapped);
}
@Override
public double computeWidth(double height)
{
return ((DynamicWidthComponent) wrapped).computeWidth(height);
}
}

@ -13,10 +13,10 @@ import mightypork.gamecore.gui.components.DynamicWidthComponent;
* @author Ondřej Hruška (MightyPork)
*/
public class ImagePainter extends BaseComponent implements DynamicWidthComponent {
private TxQuad txQuad;
/**
* @param txQuad drawn image
*/
@ -24,25 +24,25 @@ public class ImagePainter extends BaseComponent implements DynamicWidthComponent
{
this.txQuad = txQuad;
}
@Override
public void renderComponent()
{
App.gfx().quad(this, txQuad);
}
@Override
public double computeWidth(double height)
{
return (height / txQuad.uvs.height().value()) * txQuad.uvs.width().value();
}
/**
* Set drawn {@link TxQuad}
*
*
* @param txQuad the drawn quad
*/
public void setTxQuad(TxQuad txQuad)

@ -14,23 +14,23 @@ import mightypork.utils.math.color.Grad;
* @author Ondřej Hruška (MightyPork)
*/
public class QuadPainter extends BaseComponent {
@FactoryMethod
public static QuadPainter gradH(Color colorLeft, Color colorRight)
{
return new QuadPainter(colorLeft, colorRight, colorRight, colorLeft);
}
@FactoryMethod
public static QuadPainter gradV(Color colorTop, Color colorBottom)
{
return new QuadPainter(colorTop, colorTop, colorBottom, colorBottom);
}
private final Grad grad;
/**
* Painter with solid color
*
@ -40,8 +40,8 @@ public class QuadPainter extends BaseComponent {
{
this.grad = new Grad(color, color, color, color);
}
/**
* Painter with coloured vertices.
*
@ -54,8 +54,8 @@ public class QuadPainter extends BaseComponent {
{
this.grad = new Grad(leftTop, rightTop, rightBottom, leftBottom);
}
@Override
public void renderComponent()
{

@ -22,20 +22,20 @@ import mightypork.utils.string.StringWrapper;
* @author Ondřej Hruška (MightyPork)
*/
public class TextPainter extends BaseComponent implements DynamicWidthComponent {
private static final boolean DEBUG_FONT_RENDER = false;
private final FontRenderer font;
private Color color;
private AlignX align;
private StringProvider text;
private boolean shadow;
private double yPaddingPerc = 0;
private Color shadowColor = RGB.BLACK;
private Vect shadowOffset = Vect.make(2, 2);
/**
* @param font font to use
*/
@ -43,32 +43,32 @@ public class TextPainter extends BaseComponent implements DynamicWidthComponent
{
this(font, AlignX.LEFT, RGB.WHITE);
}
public TextPainter(IFont font, Color color, String text)
{
this(font, AlignX.LEFT, color, new StringWrapper(text));
}
public TextPainter(IFont font, Color color, StringProvider text)
{
this(font, AlignX.LEFT, color, text);
}
public TextPainter(IFont font, Color color)
{
this(font, AlignX.LEFT, color, (StringProvider) null);
}
public TextPainter(IFont font, AlignX align, Color color, String text)
{
this(font, align, color, new StringWrapper(text));
}
public TextPainter(IFont font, AlignX align, Color color, StringProvider text)
{
this.font = new FontRenderer(font);
@ -76,92 +76,92 @@ public class TextPainter extends BaseComponent implements DynamicWidthComponent
this.align = align;
this.text = text;
}
public TextPainter(IFont font, AlignX align, Color color)
{
this(font, align, color, (StringProvider) null);
}
@Override
public void renderComponent()
{
if (text == null) return;
final String str = text.getString();
final Num shrY = height().perc(yPaddingPerc);
final Rect rect = getRect().shrink(Num.ZERO, shrY);
if (shadow) {
font.draw(str, rect.round(), align, shadowColor);
}
final Rect r = (shadow ? rect.move(shadowOffset.neg()) : rect).round();
font.draw(str, r, align, color);
if (DEBUG_FONT_RENDER) App.gfx().quad(r, RGB.PINK.withAlpha(0.4));
}
public void setShadow(Color color, Vect offset)
{
setShadow(true);
setShadowColor(color);
setShadowOffset(offset);
}
public void setShadow(boolean shadow)
{
this.shadow = shadow;
}
public void setShadowColor(Color shadowColor)
{
this.shadowColor = shadowColor;
}
public void setShadowOffset(Vect shadowOffset)
{
this.shadowOffset = shadowOffset;
}
public void setColor(Color color)
{
this.color = color;
}
public void setAlign(AlignX align)
{
this.align = align;
}
public void setText(String text)
{
this.text = new StringWrapper(text);
}
public void setText(StringProvider text)
{
this.text = text;
}
public void setVPaddingPercent(double percY)
{
yPaddingPerc = percY;
}
@Override
public double computeWidth(double height)
{

@ -19,7 +19,7 @@ import mightypork.utils.eventbus.events.flags.NonRejectableEvent;
@NonConsumableEvent
@NonRejectableEvent
public class LayoutChangeEvent extends BusEvent<LayoutChangeListener> {
@Override
public void handleBy(LayoutChangeListener handler)
{

@ -7,7 +7,7 @@ package mightypork.gamecore.gui.events;
* @author Ondřej Hruška (MightyPork)
*/
public interface LayoutChangeListener {
/**
* Triggered when display size changed and GUI should be recalculated.
*/

@ -13,10 +13,10 @@ import mightypork.utils.eventbus.events.flags.SingleReceiverEvent;
*/
@SingleReceiverEvent
public class ScreenRequest extends BusEvent<ScreenRequestListener> {
private final String scrName;
/**
* Create a request to change screen
*
@ -26,12 +26,12 @@ public class ScreenRequest extends BusEvent<ScreenRequestListener> {
{
scrName = screenKey;
}
@Override
public void handleBy(ScreenRequestListener handler)
{
handler.showScreen(scrName);
}
}

@ -7,7 +7,7 @@ package mightypork.gamecore.gui.events;
* @author Ondřej Hruška (MightyPork)
*/
public interface ScreenRequestListener {
/**
* @param key screen to show
*/

@ -15,10 +15,10 @@ import mightypork.utils.math.constraints.vect.Vect;
@NonConsumableEvent
@NotLoggedEvent
public class ViewportChangeEvent extends BusEvent<ViewportChangeListener> {
private final Vect screenSize;
/**
* @param size new screen size
*/
@ -26,8 +26,8 @@ public class ViewportChangeEvent extends BusEvent<ViewportChangeListener> {
{
this.screenSize = size;
}
/**
* @return new screen size
*/
@ -35,8 +35,8 @@ public class ViewportChangeEvent extends BusEvent<ViewportChangeListener> {
{
return screenSize;
}
@Override
public void handleBy(ViewportChangeListener handler)
{

@ -7,7 +7,7 @@ package mightypork.gamecore.gui.events;
* @author Ondřej Hruška (MightyPork)
*/
public interface ViewportChangeListener {
/**
* Handle event
*

@ -17,36 +17,36 @@ import mightypork.utils.eventbus.clients.DelegatingClient;
* @author Ondřej Hruška (MightyPork)
*/
public abstract class LayeredScreen extends Screen {
/**
* Wrapper for delegating client, to use custom client ordering.
*
* @author Ondřej Hruška (MightyPork)
*/
private class LayersClient implements DelegatingClient {
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public Collection getChildClients()
{
return layersByEventPriority;
}
@Override
public boolean doesDelegate()
{
return true;
}
}
private final List<ScreenLayer> layersByZIndex = new ArrayList<>();
private final List<ScreenLayer> layersByEventPriority = new ArrayList<>();
private final LayersClient layersClient = new LayersClient();
/**
* Create a layered screen
*/
@ -54,8 +54,8 @@ public abstract class LayeredScreen extends Screen {
{
addChildClient(layersClient);
}
@Override
protected void renderScreen()
{
@ -63,8 +63,8 @@ public abstract class LayeredScreen extends Screen {
if (layer.isVisible()) layer.render();
}
}
/**
* Add a layer to the screen.
*
@ -74,29 +74,29 @@ public abstract class LayeredScreen extends Screen {
{
this.layersByZIndex.add(layer);
this.layersByEventPriority.add(layer);
Collections.sort(layersByEventPriority, new Comparator<Overlay>() {
@Override
public int compare(Overlay o1, Overlay o2)
{
return o2.getEventPriority() - o1.getEventPriority();
}
});
Collections.sort(layersByZIndex, new Comparator<Overlay>() {
@Override
public int compare(Overlay o1, Overlay o2)
{
return o1.getZIndex() - o2.getZIndex();
}
});
}
@Override
protected void onScreenEnter()
{
@ -104,8 +104,8 @@ public abstract class LayeredScreen extends Screen {
layer.onScreenEnter();
}
}
@Override
protected void onScreenLeave()
{
@ -113,5 +113,5 @@ public abstract class LayeredScreen extends Screen {
layer.onScreenLeave();
}
}
}

@ -29,62 +29,62 @@ import mightypork.utils.math.constraints.vect.Vect;
* @author Ondřej Hruška (MightyPork)
*/
public abstract class Overlay extends BusNode implements Comparable<Overlay>, Updateable, Renderable, KeyBinder, Hideable, Enableable, LayoutChangeListener {
private boolean visible = true;
private boolean enabled = true;
private final KeyBindingPool keybindings = new KeyBindingPool();
/** Root layout, rendered and attached to the event bus. */
protected final ConstraintLayout root;
/** Constraint: Mouse position. */
protected final Vect mouse;
/** Extra rendered items (outside root) */
protected final Collection<Renderable> rendered = new ArrayList<>();
/** Extra updated items (not members of the component tree) */
protected final Collection<Updateable> updated = new ArrayList<>();
private Num alphaMul = Num.ONE;
/**
* Create an overlay over the screen
*/
public Overlay()
{
this.mouse = App.input().getMousePos();
this.root = new ConstraintLayout(App.gfx().getRect());
addChildClient(root);
addChildClient(keybindings);
rendered.add(root);
}
@Override
public final void bindKey(KeyStroke stroke, Trigger edge, Runnable task)
{
keybindings.bindKey(stroke, edge, task);
}
@Override
public final void unbindKey(KeyStroke stroke)
{
keybindings.unbindKey(stroke);
}
@Override
public final boolean isVisible()
{
return visible;
}
@Override
public void setVisible(boolean visible)
{
@ -93,8 +93,8 @@ public abstract class Overlay extends BusNode implements Comparable<Overlay>, Up
root.setVisible(visible);
}
}
@Override
public void setEnabled(boolean yes)
{
@ -103,15 +103,15 @@ public abstract class Overlay extends BusNode implements Comparable<Overlay>, Up
root.setEnabled(yes);
}
}
@Override
public boolean isEnabled()
{
return enabled;
}
/**
* Get rendering layer
*
@ -119,8 +119,8 @@ public abstract class Overlay extends BusNode implements Comparable<Overlay>, Up
*/
@Stub
public abstract int getZIndex();
/**
* Get event bus listening priority - useful to block incoming events.
*
@ -130,8 +130,8 @@ public abstract class Overlay extends BusNode implements Comparable<Overlay>, Up
{
return getZIndex();
}
/**
* Render the overlay. The caller MUST check for visibility himself.
*/
@ -139,34 +139,34 @@ public abstract class Overlay extends BusNode implements Comparable<Overlay>, Up
public void render()
{
if (!isVisible()) return;
Color.pushAlpha(alphaMul);
for (final Renderable r : rendered) {
r.render();
}
Color.popAlpha();
}
@Override
public void update(double delta)
{
if (!isEnabled()) return;
for (final Updateable u : updated) {
u.update(delta);
}
}
@Override
public int compareTo(Overlay o)
{
return o.getEventPriority() - getEventPriority();
}
/**
* <p>
* Screen size changed.
@ -182,8 +182,8 @@ public abstract class Overlay extends BusNode implements Comparable<Overlay>, Up
public void onLayoutChanged()
{
}
/**
* Set overlay's alpha multiplier
*
@ -193,8 +193,8 @@ public abstract class Overlay extends BusNode implements Comparable<Overlay>, Up
{
this.alphaMul = alpha;
}
/**
* Set overlay's alpha multiplier
*
@ -204,8 +204,8 @@ public abstract class Overlay extends BusNode implements Comparable<Overlay>, Up
{
this.alphaMul = Num.make(alpha);
}
/**
* Show and set enabled
*/
@ -214,8 +214,8 @@ public abstract class Overlay extends BusNode implements Comparable<Overlay>, Up
setVisible(true);
setEnabled(true);
}
/**
* Hide and set disabled
*/
@ -224,15 +224,15 @@ public abstract class Overlay extends BusNode implements Comparable<Overlay>, Up
setVisible(false);
setEnabled(false);
}
@Override
public boolean isListening()
{
return (isVisible() || isEnabled());
}
@Override
public boolean doesDelegate()
{

@ -21,13 +21,13 @@ import mightypork.utils.math.constraints.rect.RectBound;
* @author Ondřej Hruška (MightyPork)
*/
public abstract class Screen extends BusNode implements Renderable, RectBound, KeyBinder, LayoutChangeListener {
private final KeyBindingPool keybindings = new KeyBindingPool();
private volatile boolean active;
private volatile boolean needSetupViewport = false;
/**
* Make a screen. The screen will initially not listen to the bus, which is
* changed once the setActive method is set to true.
@ -36,31 +36,31 @@ public abstract class Screen extends BusNode implements Renderable, RectBound, K
{
// disable events initially
setListening(false);
addChildClient(keybindings);
}
private void fireLayoutChangeEvent()
{
App.bus().sendDirectToChildren(this, new LayoutChangeEvent());
}
@Override
public final void bindKey(KeyStroke stroke, Trigger edge, Runnable task)
{
keybindings.bindKey(stroke, edge, task);
}
@Override
public final void unbindKey(KeyStroke stroke)
{
keybindings.unbindKey(stroke);
}
/**
* Prepare for being shown
*
@ -71,24 +71,24 @@ public abstract class Screen extends BusNode implements Renderable, RectBound, K
if (shown) {
active = true;
needSetupViewport = true;
fireLayoutChangeEvent();
onScreenEnter();
// enable events
setListening(true);
} else {
onScreenLeave();
active = false;
// disable events
setListening(false);
}
}
/**
* @return true if screen is the current screen
*/
@ -96,41 +96,41 @@ public abstract class Screen extends BusNode implements Renderable, RectBound, K
{
return active;
}
@Override
public void onLayoutChanged()
{
if (!isActive()) return;
needSetupViewport = true;
}
@Override
public final Rect getRect()
{
return App.gfx().getRect();
}
@Override
public void render()
{
if (!isActive()) return;
if (needSetupViewport) {
App.gfx().setupProjection();
}
App.gfx().pushState();
renderScreen();
App.gfx().popState();
}
/**
* Called when the screen becomes active
*/
@ -139,8 +139,8 @@ public abstract class Screen extends BusNode implements Renderable, RectBound, K
{
//
}
/**
* Called when the screen is no longer active
*/
@ -149,11 +149,11 @@ public abstract class Screen extends BusNode implements Renderable, RectBound, K
{
//
}
/**
* Render screen contents (context is ready for 2D rendering)
*/
protected abstract void renderScreen();
}

@ -10,10 +10,10 @@ import mightypork.utils.annotations.Stub;
* @author Ondřej Hruška (MightyPork)
*/
public abstract class ScreenLayer extends Overlay {
private final Screen screen;
/**
* @param screen parent screen
*/
@ -21,8 +21,8 @@ public abstract class ScreenLayer extends Overlay {
{
this.screen = screen;
}
/**
* @return parent screen instance
*/
@ -30,8 +30,8 @@ public abstract class ScreenLayer extends Overlay {
{
return screen;
}
/**
* Called when the screen becomes active
*/
@ -39,8 +39,8 @@ public abstract class ScreenLayer extends Overlay {
protected void onScreenEnter()
{
}
/**
* Called when the screen is no longer active
*/

@ -22,12 +22,12 @@ import mightypork.utils.logging.Log;
* @author Ondřej Hruška (MightyPork)
*/
public class ScreenRegistry extends BusNode implements ScreenRequestListener, ViewportChangeListener, Renderable {
private final Map<String, Screen> screens = new HashMap<>();
private final Collection<Overlay> overlays = new TreeSet<>();
private volatile Screen active = null;
/**
* Add a screen
*
@ -39,8 +39,8 @@ public class ScreenRegistry extends BusNode implements ScreenRequestListener, Vi
screens.put(name, screen);
addChildClient(screen);
}
/**
* Add an overlay
*
@ -51,56 +51,56 @@ public class ScreenRegistry extends BusNode implements ScreenRequestListener, Vi
overlays.add(overlay);
addChildClient(overlay);
}
@Override
public void showScreen(String key)
{
Log.f3("Request to show screen \"" + key + "\"");
// find screen to show
final Screen toShow = screens.get(key);
if (toShow == null) {
throw new RuntimeException("Screen " + key + " not defined.");
}
// deactivate last screen
if (active != null) {
active.setActive(false);
}
// activate new screen
toShow.setActive(true);
active = toShow;
fireLayoutUpdateEvent();
}
@Override
public void render()
{
if (active != null) {
active.render();
for (final Overlay overlay : overlays) {
if (overlay.isVisible()) overlay.render();
}
}
}
@Override
public void onViewportChanged(ViewportChangeEvent event)
{
if (active != null) fireLayoutUpdateEvent();
}
private void fireLayoutUpdateEvent()
{
App.bus().sendDirectToChildren(this, new LayoutChangeEvent());
}
}

@ -18,15 +18,15 @@ import mightypork.utils.math.timing.TimedTask;
* @author Ondřej Hruška (MightyPork)
*/
public class CrossfadeOverlay extends Overlay {
private static final double T_IN = 0.4;
private static final double T_OUT = 0.6;
NumAnimated alpha = new NumAnimated(0);
String requestedScreenName;
TimedTask revealTask = new TimedTask() {
@Override
public void run()
{
@ -39,8 +39,8 @@ public class CrossfadeOverlay extends Overlay {
alpha.fadeOut(T_OUT);
}
};
/**
* Create new crossfade overlay
*/
@ -49,21 +49,21 @@ public class CrossfadeOverlay extends Overlay {
final QuadPainter qp = new QuadPainter(RGB.BLACK); // TODO allow custom colors
qp.setRect(root);
root.add(qp);
updated.add(alpha);
updated.add(revealTask);
setAlpha(alpha);
}
@Override
public int getZIndex()
{
return 10000; // not too high, so app can put something on top
}
/**
* Go to specified screen
*
@ -73,21 +73,21 @@ public class CrossfadeOverlay extends Overlay {
public void goToScreen(String screen, boolean fromDark)
{
requestedScreenName = screen;
if (screen == null) {
// going for halt
App.sound().fadeOutAllLoops();
}
if (fromDark) {
alpha.setTo(1);
revealTask.run();
} else {
revealTask.start(T_IN);
alpha.setEasing(Easing.SINE_IN);
alpha.fadeIn(T_IN);
}
}
}

@ -10,11 +10,11 @@ import mightypork.utils.eventbus.events.flags.SingleReceiverEvent;
*/
@SingleReceiverEvent
public class CrossfadeRequest extends BusEvent<CrossfadeOverlay> {
private final String screen;
private final boolean fromDark;
/**
* @param screen screen key to show. Null = exit the app.
* @param fromDark true to fade from full black (ie. start of the game)
@ -25,8 +25,8 @@ public class CrossfadeRequest extends BusEvent<CrossfadeOverlay> {
this.screen = screen;
this.fromDark = fromDark;
}
/**
* @param screen screen key to show. Null = exit the app.
*/
@ -36,12 +36,12 @@ public class CrossfadeRequest extends BusEvent<CrossfadeOverlay> {
this.screen = screen;
this.fromDark = false;
}
@Override
public void handleBy(CrossfadeOverlay handler)
{
handler.goToScreen(screen, fromDark);
}
}

@ -15,10 +15,10 @@ import mightypork.utils.math.timing.TimedTask;
* @author Ondřej Hruška (MightyPork)
*/
public abstract class FadingLayer extends ScreenLayer {
private final NumAnimated numa;
private final TimedTask hideTimer = new TimedTask() {
@Override
public void run()
{
@ -27,9 +27,9 @@ public abstract class FadingLayer extends ScreenLayer {
onHideFinished();
}
};
private final TimedTask showTimer = new TimedTask() {
@Override
public void run()
{
@ -37,11 +37,11 @@ public abstract class FadingLayer extends ScreenLayer {
onShowFinished();
}
};
private boolean fadingIn = false;
private boolean fadingOut = false;
/**
* Create with default fading time and effect
*
@ -51,8 +51,8 @@ public abstract class FadingLayer extends ScreenLayer {
{
this(screen, new NumAnimated(1, Easing.QUADRATIC_OUT, 0.3));
}
/**
* Create with custom animator
*
@ -62,17 +62,17 @@ public abstract class FadingLayer extends ScreenLayer {
public FadingLayer(Screen screen, NumAnimated easingAnim)
{
super(screen);
numa = easingAnim;
updated.add(numa);
updated.add(hideTimer);
updated.add(showTimer);
setAlpha(numa);
}
/**
* Called after the fade-out was completed
*/
@ -80,8 +80,8 @@ public abstract class FadingLayer extends ScreenLayer {
protected void onHideFinished()
{
}
/**
* Called after the fade-in was completed
*/
@ -89,8 +89,8 @@ public abstract class FadingLayer extends ScreenLayer {
protected void onShowFinished()
{
}
/**
* Show with fading
*/
@ -98,19 +98,19 @@ public abstract class FadingLayer extends ScreenLayer {
public void show()
{
if (fadingIn) return;
if (!isVisible() || fadingOut) {
super.show();
numa.fadeIn();
hideTimer.stop();
showTimer.start(numa.getDefaultDuration());
fadingOut = false;
fadingIn = true;
}
}
/**
* Hide without fading
*/
@ -121,8 +121,8 @@ public abstract class FadingLayer extends ScreenLayer {
super.hide();
onHideFinished();
}
/**
* Show without fading
*/
@ -133,8 +133,8 @@ public abstract class FadingLayer extends ScreenLayer {
super.show();
onShowFinished();
}
/**
* Hide with fading
*/
@ -142,14 +142,14 @@ public abstract class FadingLayer extends ScreenLayer {
public void hide()
{
if (fadingOut) return;
if (isVisible()) {
numa.fadeOut();
hideTimer.start(numa.getDefaultDuration());
fadingOut = true;
fadingIn = false;
}
}
}

@ -13,10 +13,10 @@ import mightypork.utils.math.color.Color;
* @author Ondřej Hruška (MightyPork)
*/
public class LayerColor extends ScreenLayer {
private final int zIndex;
/**
* Overlay with color
*
@ -27,18 +27,18 @@ public class LayerColor extends ScreenLayer {
public LayerColor(Screen screen, Color color, int zIndex)
{
super(screen);
final QuadPainter qp = new QuadPainter(color);
qp.setRect(root);
root.add(qp);
this.zIndex = zIndex;
}
@Override
public int getZIndex()
{
return this.zIndex;
}
}

@ -15,47 +15,47 @@ import mightypork.utils.math.constraints.vect.Vect;
* @author Ondřej Hruška (MightyPork)
*/
public abstract class InputModule extends BackendModule implements KeyBinder {
protected KeyBindingPool keybindings;
@Override
public final void init()
{
initKeyCodes();
initDevices();
keybindings = new KeyBindingPool();
addChildClient(keybindings);
}
/**
* Initialize key codes for keys in {@link Keys}
*/
protected abstract void initKeyCodes();
/**
* Initialize input devices (set up infrastructure for getting the input)
*/
protected abstract void initDevices();
@Override
public void bindKey(KeyStroke stroke, Trigger edge, Runnable task)
{
keybindings.bindKey(stroke, edge, task);
}
@Override
public void unbindKey(KeyStroke stroke)
{
keybindings.unbindKey(stroke);
}
/**
* Get absolute mouse position. Should always return the same Vect instance
* (use a VectVar or similar).
@ -63,24 +63,24 @@ public abstract class InputModule extends BackendModule implements KeyBinder {
* @return mouse position
*/
public abstract Vect getMousePos();
/**
* Check if mouse is inside window
*
* @return true if mouse is inside window.
*/
public abstract boolean isMouseInside();
/**
* Trap mouse cursor in the window / release it
*
* @param grab true to grab, false to release
*/
public abstract void grabMouse(boolean grab);
/**
* Check if key is down. The key comes from the Keys class, so the code is
* the one assigned in initKeyCodes()
@ -89,8 +89,8 @@ public abstract class InputModule extends BackendModule implements KeyBinder {
* @return is down
*/
public abstract boolean isKeyDown(Key key);
/**
* Check mouse button state
*

@ -15,12 +15,12 @@ import mightypork.gamecore.core.App;
* @author Ondřej Hruška (MightyPork)
*/
public class Key {
private int code = -1;
private final String name;
private final Set<String> aliases = new HashSet<>(1);
/**
* Create a key. Note that both name and aliases are converted to uppercase,
* and all underscores are ignored when the aliases are matched.
@ -30,24 +30,24 @@ public class Key {
*/
public Key(String name, String... aliases)
{
// assign name and aliases, converting both to uppercase
this.name = name;
this.aliases.add(prepareForMatch(name));
for (final String al : aliases) {
this.aliases.add(prepareForMatch(al));
}
}
public boolean isDown()
{
return App.input().isKeyDown(this);
}
/**
* Set a key code. This can be used by the {@link InputModule} to store a
* numeric code in the key.
@ -58,8 +58,8 @@ public class Key {
{
this.code = code;
}
/**
* Check if the provided alias matches this key.<br>
* Both the primary alias and the extra aliases are considered.
@ -72,14 +72,14 @@ public class Key {
if (alias == null) return false;
return aliases.contains(prepareForMatch(alias));
}
private String prepareForMatch(String matched)
{
return matched.toUpperCase().replace("_", "");
}
/**
* Get key name (primary alias).
*
@ -89,8 +89,8 @@ public class Key {
{
return name;
}
/**
* Get the numeric code assigned to this key. If none is assigned, the value
* is -1.
@ -101,8 +101,8 @@ public class Key {
{
return code;
}
/**
* Get if this key is not a NONE or undefined key.
*

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save