Removed bad libraries, added LWJGL and Slick-Util, added

mightypork.utils, some work on the framework.
v5stable
Ondřej Hruška 10 years ago
parent 3578c0ab17
commit 6b8891666a
  1. 41
      .classpath
  2. 21
      README.md
  3. BIN
      lib/OpenAL32.dll
  4. BIN
      lib/OpenAL64.dll
  5. BIN
      lib/gdx-backend-lwjgl-natives.jar
  6. BIN
      lib/gdx-backend-lwjgl-sources.jar
  7. BIN
      lib/gdx-backend-lwjgl.jar
  8. BIN
      lib/gdx-freetype-natives.jar
  9. BIN
      lib/gdx-freetype-sources.jar
  10. BIN
      lib/gdx-freetype.jar
  11. BIN
      lib/gdx-natives.jar
  12. BIN
      lib/gdx-sources.zip
  13. BIN
      lib/gdx.jar
  14. BIN
      lib/jinput-dx8.dll
  15. BIN
      lib/jinput-dx8_64.dll
  16. BIN
      lib/jinput-raw.dll
  17. BIN
      lib/jinput-raw_64.dll
  18. BIN
      lib/jinput.jar
  19. BIN
      lib/jogg-0.0.7.jar
  20. BIN
      lib/jorbis-0.0.15.jar
  21. BIN
      lib/libjinput-linux.so
  22. BIN
      lib/libjinput-linux64.so
  23. BIN
      lib/libjinput-osx.jnilib
  24. BIN
      lib/liblwjgl.jnilib
  25. BIN
      lib/liblwjgl.so
  26. BIN
      lib/liblwjgl64.so
  27. BIN
      lib/libopenal.so
  28. BIN
      lib/libopenal64.so
  29. BIN
      lib/lwjgl-source-2.8.4.zip
  30. BIN
      lib/lwjgl.dll
  31. BIN
      lib/lwjgl.jar
  32. BIN
      lib/lwjgl64.dll
  33. BIN
      lib/lwjgl_util.jar
  34. BIN
      lib/mini2Dx-core-source.zip
  35. BIN
      lib/mini2Dx-core.jar
  36. BIN
      lib/mini2Dx-dependency-injection-desktop-source.zip
  37. BIN
      lib/mini2Dx-dependency-injection-desktop.jar
  38. BIN
      lib/mini2Dx-dependency-injection-source.zip
  39. BIN
      lib/mini2Dx-dependency-injection.jar
  40. BIN
      lib/mini2Dx-ecs-source.zip
  41. BIN
      lib/mini2Dx-ecs.jar
  42. BIN
      lib/mini2Dx-tiled-source.zip
  43. BIN
      lib/mini2Dx-tiled.jar
  44. BIN
      lib/slick-util-src.zip
  45. BIN
      lib/slick-util.jar
  46. 417
      src/mightypork/rogue/App.java
  47. 81
      src/mightypork/rogue/Config.java
  48. 29
      src/mightypork/rogue/Const.java
  49. 15
      src/mightypork/rogue/CrashHandler.java
  50. 22
      src/mightypork/rogue/Paths.java
  51. 200
      src/mightypork/rogue/Screen.java
  52. 30
      src/mightypork/rogue/animations/EmptyAnimator.java
  53. 13
      src/mightypork/rogue/animations/GUIRenderer.java
  54. 21
      src/mightypork/rogue/fonts/Align.java
  55. 317
      src/mightypork/rogue/fonts/FontManager.java
  56. 61
      src/mightypork/rogue/fonts/Fonts.java
  57. 665
      src/mightypork/rogue/fonts/LoadedFont.java
  58. 52
      src/mightypork/rogue/input/InputHandler.java
  59. 122
      src/mightypork/rogue/input/Keys.java
  60. 82
      src/mightypork/rogue/screens/ScreenSplash.java
  61. 77
      src/mightypork/rogue/sounds/AudioPlayer.java
  62. 230
      src/mightypork/rogue/sounds/AudioX.java
  63. 36
      src/mightypork/rogue/sounds/EffectPlayer.java
  64. 51
      src/mightypork/rogue/sounds/JointVolume.java
  65. 132
      src/mightypork/rogue/sounds/LoopPlayer.java
  66. 179
      src/mightypork/rogue/sounds/SoundManager.java
  67. 76
      src/mightypork/rogue/textures/TextureManager.java
  68. 141
      src/mightypork/rogue/textures/Textures.java
  69. 29
      src/mightypork/rogue/textures/Tx.java
  70. 72
      src/mightypork/rogue/textures/TxQuad.java
  71. 91
      src/mightypork/rogue/threads/ThreadSaveScreenshot.java
  72. 32
      src/mightypork/rogue/threads/ThreadScreenshotTrigger.java
  73. 919
      src/mightypork/rogue/util/RenderUtils.java
  74. 10
      src/mightypork/rogue/util/Utils.java
  75. 163
      src/mightypork/utils/files/FileTreeDiff.java
  76. 527
      src/mightypork/utils/files/FileUtils.java
  77. 149
      src/mightypork/utils/files/OsUtils.java
  78. 963
      src/mightypork/utils/files/PropertyManager.java
  79. 204
      src/mightypork/utils/files/SimpleConfig.java
  80. 123
      src/mightypork/utils/files/ZipBuilder.java
  81. 227
      src/mightypork/utils/files/ZipUtils.java
  82. 79
      src/mightypork/utils/files/ion/AbstractIonList.java
  83. 101
      src/mightypork/utils/files/ion/AbstractIonMap.java
  84. 298
      src/mightypork/utils/files/ion/Ion.java
  85. 25
      src/mightypork/utils/files/ion/IonException.java
  86. 157
      src/mightypork/utils/files/ion/IonList.java
  87. 156
      src/mightypork/utils/files/ion/IonMap.java
  88. 57
      src/mightypork/utils/files/ion/IonMarks.java
  89. 43
      src/mightypork/utils/files/ion/Ionizable.java
  90. 17
      src/mightypork/utils/files/ion/IonizableOptional.java
  91. 265
      src/mightypork/utils/files/ion/StreamUtils.java
  92. 159
      src/mightypork/utils/logging/Log.java
  93. 385
      src/mightypork/utils/logging/LogInstance.java
  94. 13
      src/mightypork/utils/logging/LogMonitor.java
  95. 31
      src/mightypork/utils/logging/LogToSysoutMonitor.java
  96. 931
      src/mightypork/utils/math/Calc.java
  97. 107
      src/mightypork/utils/math/Polar.java
  98. 111
      src/mightypork/utils/math/PolarDeg.java
  99. 197
      src/mightypork/utils/math/Range.java
  100. 177
      src/mightypork/utils/math/color/HSV.java
  101. Some files were not shown because too many files have changed in this diff Show More

@ -2,16 +2,35 @@
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="lib" path="lib/gdx-backend-lwjgl-natives.jar"/>
<classpathentry kind="lib" path="lib/gdx-freetype-natives.jar"/>
<classpathentry kind="lib" path="lib/gdx-freetype.jar" sourcepath="lib/gdx-freetype-sources.jar"/>
<classpathentry kind="lib" path="lib/gdx-natives.jar"/>
<classpathentry kind="lib" path="lib/gdx.jar" sourcepath="lib/gdx-sources.jar"/>
<classpathentry kind="lib" path="lib/mini2Dx-dependency-injection-desktop.jar" sourcepath="lib/mini2Dx-dependency-injection-desktop-source.zip"/>
<classpathentry kind="lib" path="lib/mini2Dx-core.jar" sourcepath="lib/mini2Dx-core-source.zip"/>
<classpathentry kind="lib" path="lib/mini2Dx-dependency-injection.jar" sourcepath="lib/mini2Dx-dependency-injection-source.zip"/>
<classpathentry kind="lib" path="lib/mini2Dx-ecs-source.zip" sourcepath="lib/mini2Dx-ecs-source.zip"/>
<classpathentry kind="lib" path="lib/mini2Dx-tiled.jar" sourcepath="lib/mini2Dx-tiled-source.zip"/>
<classpathentry kind="lib" path="lib/gdx-backend-lwjgl.jar" sourcepath="lib/gdx-backend-lwjgl-sources.jar"/>
<classpathentry kind="lib" path="lib/jinput.jar" sourcepath="lib/lwjgl-source-2.8.4.zip">
<attributes>
<attribute name="org.eclipse.jdt.launching.CLASSPATH_ATTR_LIBRARY_PATH_ENTRY" value="PR2-game/lib"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="lib/jogg-0.0.7.jar">
<attributes>
<attribute name="org.eclipse.jdt.launching.CLASSPATH_ATTR_LIBRARY_PATH_ENTRY" value="PR2-game/lib"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="lib/jorbis-0.0.15.jar" sourcepath="lib/lwjgl-source-2.8.4.zip">
<attributes>
<attribute name="org.eclipse.jdt.launching.CLASSPATH_ATTR_LIBRARY_PATH_ENTRY" value="PR2-game/lib"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="lib/lwjgl_util.jar" sourcepath="lib/lwjgl-source-2.8.4.zip">
<attributes>
<attribute name="org.eclipse.jdt.launching.CLASSPATH_ATTR_LIBRARY_PATH_ENTRY" value="PR2-game/lib"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="lib/lwjgl.jar" sourcepath="lib/lwjgl-source-2.8.4.zip">
<attributes>
<attribute name="org.eclipse.jdt.launching.CLASSPATH_ATTR_LIBRARY_PATH_ENTRY" value="PR2-game/lib"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="lib/slick-util.jar" sourcepath="lib/slick-util-src.zip">
<attributes>
<attribute name="org.eclipse.jdt.launching.CLASSPATH_ATTR_LIBRARY_PATH_ENTRY" value="PR2-game/lib"/>
</attributes>
</classpathentry>
<classpathentry kind="output" path="bin"/>
</classpath>

@ -11,21 +11,18 @@ Goals
Features
--------
**SUBJECT TO CHANGE!**
- Randomly generated floors
- Hybrid turn-based gameplay (lower and upper time limit)
- Automatic turn-based combat
- Health, hunger, experience meters; levels
- Entities with basic AI (Monsters)
- Items, weapons, armor
- Treasure chests
- Potions
- Inventory system with 12 slots (+ armor)
- Hybrid turn-based gameplay
- Stats and leveling
- Monsters
- Collectable items
- Potions, food
- Simple inventory system
Used libraries
--------------
- libGDX
- mini2Dx
- Slick2D
- NiftyGUI
- LWJGL

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -0,0 +1,417 @@
package mightypork.rogue;
import static org.lwjgl.opengl.GL11.*;
import java.io.File;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileLock;
import javax.swing.JOptionPane;
import mightypork.rogue.input.Keys;
import mightypork.rogue.screens.ScreenSplash;
import mightypork.rogue.sounds.SoundManager;
import mightypork.rogue.threads.ThreadSaveScreenshot;
import mightypork.rogue.threads.ThreadScreenshotTrigger;
import mightypork.utils.logging.Log;
import mightypork.utils.logging.LogInstance;
import mightypork.utils.math.coord.Coord;
import mightypork.utils.time.TimerDelta;
import mightypork.utils.time.TimerInterpolating;
import org.lwjgl.BufferUtils;
import org.lwjgl.LWJGLException;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.lwjgl.openal.AL;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
public class App {
/** instance */
public static App inst;
/** Current delta time (secs since last render) */
public static double currentDelta = 0;
private static DisplayMode windowDisplayMode = null;
/** current screen */
public static Screen screen = null;
/** Flag that screenshot is scheduled to be taken next loop */
public static boolean scheduledScreenshot = false;
private static boolean lockInstance()
{
if (Config.SINGLE_INSTANCE == false) return true; // bypass lock
final File lockFile = new File(Paths.WORKDIR, ".lock");
try {
final RandomAccessFile randomAccessFile = new RandomAccessFile(lockFile, "rw");
final FileLock fileLock = randomAccessFile.getChannel().tryLock();
if (fileLock != null) {
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run()
{
try {
fileLock.release();
randomAccessFile.close();
lockFile.delete();
} catch (Exception e) {
System.out.println("Unable to remove lock file.");
e.printStackTrace();
}
}
});
return true;
}
} catch (Exception e) {
System.out.println("Unable to create and/or lock file.");
e.printStackTrace();
}
return false;
}
/**
* Is if FS
*
* @return is in fs
*/
public static boolean isFullscreen()
{
return Display.isFullscreen();
}
/**
* @param args
*/
public static void main(String[] args)
{
Thread.setDefaultUncaughtExceptionHandler(new CrashHandler());
inst = new App();
try {
inst.start();
} catch (Throwable t) {
onCrash(t);
}
}
/**
* Show crash report dialog with error stack trace.
*
* @param error
*/
public static void onCrash(Throwable error)
{
Log.e("The game has crashed.");
Log.e(error);
try {
inst.deinit();
} catch (Throwable t) {
// ignore
}
}
/**
* Quit to OS
*/
public void exit()
{
deinit();
System.exit(0);
}
/**
* Get current screen
*
* @return screen
*/
public Screen getScreen()
{
return screen;
}
/**
* Get screen size
*
* @return size
*/
public Coord getSize()
{
return new Coord(Display.getWidth(), Display.getHeight());
}
private void init() throws LWJGLException
{
// initialize main logger
LogInstance li = Log.create("runtime", Paths.LOGS, 10);
li.enable(Config.LOGGING_ENABLED);
li.enableSysout(Config.LOG_TO_STDOUT);
// initialize display
Display.setDisplayMode(windowDisplayMode = new DisplayMode(Const.WINDOW_SIZE_X, Const.WINDOW_SIZE_Y));
Display.setResizable(true);
Display.setVSyncEnabled(true);
Display.setTitle(Const.TITLEBAR);
Display.create();
if (Config.START_IN_FS) {
switchFullscreen();
Display.update();
}
// initialize inputs
Mouse.create();
Keyboard.create();
Keyboard.enableRepeatEvents(false);
// initialize sound system
SoundManager.get().setListener(Const.LISTENER_POS);
SoundManager.get().setMasterVolume(1F);
// start async screenshot trigger listener
(new ThreadScreenshotTrigger()).start();
}
private void start() throws LWJGLException
{
if (!lockInstance()) {
System.out.println("Working directory is locked.\nOnly one instance can run at a time.");
//@formatter:off
JOptionPane.showMessageDialog(
null,
"The game is already running.",
"Instance error",
JOptionPane.ERROR_MESSAGE
);
//@formatter:on
exit();
return;
}
init();
mainLoop();
deinit();
}
private void deinit()
{
Display.destroy();
Mouse.destroy();
Keyboard.destroy();
SoundManager.get().destroy();
AL.destroy();
}
/** timer */
private TimerDelta timerRender;
private TimerInterpolating timerGui;
private int timerAfterResize = 0;
private void mainLoop()
{
screen = new ScreenSplash();
screen.init();
timerRender = new TimerDelta();
timerGui = new TimerInterpolating(Const.FPS_GUI_UPDATE);
while (!Display.isCloseRequested()) {
glLoadIdentity();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
timerGui.sync();
int ticks = timerGui.getSkipped();
if (ticks >= 1) {
screen.updateGui();
timerGui.startNewFrame();
}
currentDelta = timerRender.getDelta();
// RENDER
screen.render(currentDelta);
SoundManager.get().update(currentDelta);
Display.update();
if (scheduledScreenshot) {
takeScreenshot();
scheduledScreenshot = false;
}
if (Keys.justPressed(Keyboard.KEY_F11)) {
Log.f2("F11, toggle fullscreen.");
switchFullscreen();
screen.onFullscreenChange();
Keys.destroyChangeState(Keyboard.KEY_F11);
}
if (Keyboard.isKeyDown(Keyboard.KEY_LCONTROL)) {
if (Keyboard.isKeyDown(Keyboard.KEY_Q)) {
Log.f2("Ctrl+Q, force quit.");
Keys.destroyChangeState(Keyboard.KEY_Q);
exit();
return;
}
// if (Keyboard.isKeyDown(Keyboard.KEY_M)) {
// Log.f2("Ctrl+M, go to main menu.");
// Keys.destroyChangeState(Keyboard.KEY_M);
// replaceScreen(new ScreenMenuMain());
// }
if (Keyboard.isKeyDown(Keyboard.KEY_F)) {
Log.f2("Ctrl+F, switch fullscreen.");
Keys.destroyChangeState(Keyboard.KEY_F);
switchFullscreen();
screen.onFullscreenChange();
}
}
if (Display.wasResized()) {
screen.onWindowResize();
timerAfterResize = 0;
} else { // ensure screen has even size
timerAfterResize++;
if (timerAfterResize > Const.FPS_GUI_UPDATE * 0.3) {
timerAfterResize = 0;
int x = Display.getX();
int y = Display.getY();
int w = Display.getWidth();
int h = Display.getHeight();
if (w % 2 != 0 || h % 2 != 0) {
try {
Display.setDisplayMode(windowDisplayMode = new DisplayMode(w - w % 2, h - h % 2));
screen.onWindowResize();
Display.setLocation(x, y);
} catch (LWJGLException e) {
e.printStackTrace();
}
}
}
}
try {
Display.sync(Const.FPS_RENDER);
} catch (Throwable t) {
Log.e("Your graphics card driver does not support fullscreen properly.", t);
try {
Display.setDisplayMode(windowDisplayMode);
} catch (LWJGLException e) {
Log.e("Error going back from corrupted fullscreen.");
onCrash(e);
}
}
}
}
// UPDATE LOOP END
/**
* Do take a screenshot
*/
public void takeScreenshot()
{
//Effects.play("gui.screenshot");
glReadBuffer(GL_FRONT);
int width = Display.getDisplayMode().getWidth();
int height = Display.getDisplayMode().getHeight();
int bpp = 4; // Assuming a 32-bit display with a byte each for red, green, blue, and alpha.
ByteBuffer buffer = BufferUtils.createByteBuffer(width * height * bpp);
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, buffer);
(new ThreadSaveScreenshot(buffer, width, height, bpp)).start();
}
/**
* Replace screen
*
* @param newScreen new screen
*/
public void replaceScreen(Screen newScreen)
{
screen = newScreen;
screen.init();
}
/**
* Replace screen, don't init it
*
* @param newScreen new screen
*/
public void replaceScreenNoInit(Screen newScreen)
{
screen = newScreen;
}
/**
* Toggle FS if possible
*/
public void switchFullscreen()
{
try {
if (!Display.isFullscreen()) {
Log.f3("Entering fullscreen.");
// save window resize
windowDisplayMode = new DisplayMode(Display.getWidth(), Display.getHeight());
Display.setDisplayMode(Display.getDesktopDisplayMode());
Display.setFullscreen(true);
Display.update();
//
//
// DisplayMode mode = Display.getDesktopDisplayMode(); //findDisplayMode(WIDTH, HEIGHT);
// Display.setDisplayModeAndFullscreen(mode);
} else {
Log.f3("Leaving fullscreen.");
Display.setDisplayMode(windowDisplayMode);
Display.update();
}
} catch (Throwable t) {
Log.e("Failed to toggle fullscreen mode.", t);
try {
Display.setDisplayMode(windowDisplayMode);
Display.update();
} catch (Throwable t1) {
onCrash(t1);
}
}
}
}

@ -0,0 +1,81 @@
package mightypork.rogue;
import mightypork.utils.files.PropertyManager;
import mightypork.utils.logging.Log;
/**
* Main Config class
*
* @author MightyPork
*/
public class Config {
private static PropertyManager mgr;
// opts
public static final int def_LAST_RUN_VERSION = 0;
public static int LAST_RUN_VERSION;
public static final boolean def_START_IN_FS = false;
public static boolean START_IN_FS;
// property keys
private static final String PK_LAST_RUN_VERSION = "status.last_run_version";
private static final String PK_START_IN_FS = "cfg.start_in_fullscreen";
/**
* Prepare config manager and load user settings
*/
public static void init()
{
Log.f2("Initializing configuration manager.");
String comment = Const.APP_NAME + " config file";
mgr = new PropertyManager(Paths.CONFIG, comment);
mgr.cfgNewlineBeforeComments(true);
mgr.cfgSeparateSections(true);
mgr.putInteger(PK_LAST_RUN_VERSION, def_LAST_RUN_VERSION);
mgr.putBoolean(PK_START_IN_FS, def_START_IN_FS);
load(); // load what has been "put"
}
/**
* Save changed fields to config file
*/
public static void save()
{
mgr.setValue(PK_LAST_RUN_VERSION, Const.VERSION);
mgr.setValue(PK_START_IN_FS, START_IN_FS);
mgr.apply();
}
/**
* Load config file and assign values to fields
*/
public static void load()
{
mgr.apply();
LAST_RUN_VERSION = mgr.getInteger(PK_LAST_RUN_VERSION);
START_IN_FS = mgr.getBoolean(PK_START_IN_FS);
}
// options that can't be configured via config file
public static boolean LOGGING_ENABLED = true;
public static boolean LOG_TO_STDOUT = true;
public static boolean SINGLE_INSTANCE = true;
public static boolean LOG_FONTS = false;
}

@ -0,0 +1,29 @@
package mightypork.rogue;
import mightypork.utils.math.coord.Coord;
/**
* Application constants
*
* @author MightyPork
*/
public class Const {
// STRINGS
public static final int VERSION = 1;
public static final String APP_NAME = "Rogue";
public static final String TITLEBAR = APP_NAME + " v." + VERSION;
// AUDIO
public static final Coord LISTENER_POS = Coord.ZERO;
public static final int FPS_RENDER = 200; // max
public static final long FPS_GUI_UPDATE = 60;
// INITIAL WINDOW SIZE
public static final int WINDOW_SIZE_X = 1024;
public static final int WINDOW_SIZE_Y = 768;
}

@ -0,0 +1,15 @@
package mightypork.rogue;
import java.lang.Thread.UncaughtExceptionHandler;
public class CrashHandler implements UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread t, Throwable e)
{
App.onCrash(e);
}
}

@ -0,0 +1,22 @@
package mightypork.rogue;
import java.io.File;
import mightypork.utils.files.OsUtils;
public class Paths {
private static final String APPDIR_NAME = "rogue";
public static final File WORKDIR = OsUtils.getWorkDir(APPDIR_NAME);
public static final File LOGS = OsUtils.getWorkDir(APPDIR_NAME, "logs");
public static final File SCREENSHOTS = OsUtils.getWorkDir(APPDIR_NAME, "screenshots");
public static final File CONFIG = new File(WORKDIR, "config.ini");
public static final String DIR_EFFECTS = "res/sounds/effects/";
public static final String DIR_MUSIC = "res/sounds/music/";
public static final String DIR_LOOPS = "res/sounds/loops/";
}

@ -0,0 +1,200 @@
package mightypork.rogue;
import static org.lwjgl.opengl.GL11.*;
import java.util.Random;
import mightypork.rogue.animations.GUIRenderer;
import mightypork.rogue.input.InputHandler;
import mightypork.rogue.input.Keys;
import mightypork.utils.math.coord.Coord;
import mightypork.utils.math.coord.Vec;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
/**
* Screen class.<br>
* Screen animates 3D world, while contained panels render 2D overlays, process
* inputs and run the game logic.
*
* @author MightyPork
*/
public abstract class Screen implements GUIRenderer, InputHandler {
/** RNG */
protected static Random rand = new Random();
/**
* handle fullscreen change
*/
@Override
public final void onFullscreenChange()
{
onWindowResize();
onViewportChanged();
}
protected abstract void onViewportChanged();
/**
* handle window resize.
*/
public final void onWindowResize()
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
Coord s = App.inst.getSize();
glViewport(0, 0, s.xi(), s.yi());
glOrtho(0, s.x, 0, s.y, -1000, 1000);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glEnable(GL_BLEND);
//glDisable(GL_DEPTH_TEST);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDisable(GL_TEXTURE_2D);
}
/**
* Initialize screen
*/
public void init()
{
onWindowResize();
initScreen();
// SETUP LIGHTS
glDisable(GL_LIGHTING);
// OTHER SETTINGS
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glClearDepth(1f);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glEnable(GL_NORMALIZE);
glShadeModel(GL_SMOOTH);
glDisable(GL_TEXTURE_2D);
}
/**
* Here you can initialize the screen.
*/
public abstract void initScreen();
/**
* Update tick
*/
@Override
public final void updateGui()
{
Mouse.poll();
Keyboard.poll();
checkInputEvents();
onGuiUpdate();
}
protected abstract void onGuiUpdate();
/**
* Render screen
*
* @param delta delta time (position between two update ticks, to allow
* super-smooth animations)
*/
@Override
public final void render(double delta)
{
glPushAttrib(GL_ENABLE_BIT);
// draw the directly rendered 3D stuff
render3D();
glPopAttrib();
}
protected abstract void render3D();
/**
* Check input events and process them.
*/
private final void checkInputEvents()
{
while (Keyboard.next()) {
int key = Keyboard.getEventKey();
boolean down = Keyboard.getEventKeyState();
char c = Keyboard.getEventCharacter();
Keys.onKey(key, down);
onKey(key, c, down);
}
while (Mouse.next()) {
int button = Mouse.getEventButton();
boolean down = Mouse.getEventButtonState();
Coord delta = new Coord(Mouse.getEventDX(), Mouse.getEventDY());
Coord pos = new Coord(Mouse.getEventX(), Mouse.getEventY());
int wheeld = Mouse.getEventDWheel();
onMouseButton(button, down, wheeld, pos, delta);
}
int xc = Mouse.getX();
int yc = Mouse.getY();
int xd = Mouse.getDX();
int yd = Mouse.getDY();
int wd = Mouse.getDWheel();
if (Math.abs(xd) > 0 || Math.abs(yd) > 0 || Math.abs(wd) > 0) {
onMouseMove(new Coord(xc, yc), new Vec(xd, yd), wd);
}
handleKeyStates();
}
@Override
public abstract void onKey(int key, char c, boolean down);
@Override
public abstract void onMouseButton(int button, boolean down, int wheeld, Coord pos, Coord delta);
@Override
public abstract void handleKeyStates();
@Override
public abstract void onMouseMove(Coord coord, Vec vec, int wd);
/**
* Render background 2D (all is ready for rendering)
*
* @param delta delta time
*/
protected abstract void render2D(double delta);
}

@ -0,0 +1,30 @@
package mightypork.rogue.animations;
/**
* Empty animation (no effect)
*
* @author MightyPork
*/
public class EmptyAnimator implements GUIRenderer {
/**
* New empty animation
*/
public EmptyAnimator() {}
@Override
public void updateGui()
{}
@Override
public void render(double delta)
{}
@Override
public void onFullscreenChange()
{}
}

@ -0,0 +1,13 @@
package mightypork.rogue.animations;
public interface GUIRenderer {
public void updateGui();
public void render(double delta);
public void onFullscreenChange();
}

@ -0,0 +1,21 @@
package mightypork.rogue.fonts;
/**
* Alignment
*
* @author MightyPork
*/
@SuppressWarnings("javadoc")
public class Align {
public static final int LEFT = -1;
public static final int RIGHT = 1;
public static final int TOP = 1;
public static final int BOTTOM = -1;
public static final int UP = 1;
public static final int DOWN = -1;
public static final int CENTER = 0;
public static final int MIDDLE = 0;
}

@ -0,0 +1,317 @@
package mightypork.rogue.fonts;
import java.awt.Font;
import java.io.InputStream;
import java.util.HashMap;
import mightypork.rogue.Config;
import mightypork.utils.logging.Log;
import mightypork.utils.math.coord.Coord;
import org.newdawn.slick.util.ResourceLoader;
/**
* Remade universal font manager for Sector.
*
* @author MightyPork
*/
public class FontManager {
private static final boolean DEBUG = Config.LOG_FONTS;
/**
* Glyph tables.
*
* @author MightyPork
*/
public static class Glyphs {
//@formatter:off
/** all glyphs */
public static final String all =
" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]" +
"^_`abcdefghijklmnopqrstuvwxyz{|}~€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ¡¢£¤" +
"¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäå" +
"æçèéêëìíîïðñòóôõö÷øùúûüýþÿ";
/** letters and numbers, sufficient for basic messages etc. NO SPACE */
public static final String alnum_nospace =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
/** letters and numbers, sufficient for basic messages etc. */
public static final String alnum =
" "+alnum_nospace;
/** letters and numbers with the most basic punctuation signs */
public static final String basic_text =
" .-,.?!:;_"+alnum_nospace;
/** letters */
public static final String alpha =
" ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
/** numbers */
public static final String numbers =
" 0123456789.-,:";
/** non-standard variants of alnum */
public static final String alnum_extra =
" ŒÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜŸÝßàáâãäåæçèéêëìíîïðñòóôõöøùúû" +
"üýþÿĚŠČŘŽŤŇĎŮěščřžťňďůŁłđ";
/** signs and punctuation */
public static final String signs =
" !\"#$%&§'()*+,-./:;<=>?@[\\]^_{|}~";
/** extra signs and punctuation */
public static final String signs_extra =
" ¥€£¢`ƒ„…†‡ˆ‰‹‘’“”•›¡¤¦¨ª«¬­¯°±²³´µ¶·¸¹º»¼½¾¿÷™©­®→↓←↑";
/** basic character set. */
public static final String basic = alnum + signs;
//@formatter:on
}
/**
* Font style
*
* @author MightyPork
*/
public static enum Style
{
/** Normal */
NORMAL,
/** Italic */
ITALIC,
/** Stronger italic to left. */
LEFT,
/** Stronger italic to right */
RIGHT,
/** Monospace type */
MONO,
/** Bold */
BOLD,
/** Bold & Italic */
BOLD_I,
/** Bold & Left */
BOLD_L,
/** Bold & Right */
BOLD_R,
/** Heavy style, stronger than bold. */
HEAVY,
/** Light (lighter than normal) */
LIGHT,
/** narrow style, similar to Light */
NARROW,
/** Wide style, like Bold but with thinner lines */
WIDE,
/** Outline variant of normal */
OUTLINE;
}
/**
* Preloaded font identifier [name, size, style]
*
* @author MightyPork
*/
public static class FontId {
/** font size (pt) */
public float size = 24;
/** font name, registered with registerFile */
public String name = "";
/** font style. The given style must be in a file. */
public Style style;
/** Set of glyphs in this ID */
public String glyphs = "";
/** Index for faster comparision of glyph ids. */
public int glyphset_id = 0;
/**
* Preloaded font identifier
*
* @param name font name (registerFile)
* @param size font size (pt)
* @param style font style
* @param glyphs glyphs to load
*/
public FontId(String name, double size, Style style, String glyphs) {
this.name = name;
this.size = (float) size;
this.style = style;
if (glyphs.equals(Glyphs.basic)) {
glyphset_id = 1;
} else if (glyphs.equals(Glyphs.alnum)) {
glyphset_id = 2;
} else if (glyphs.equals(Glyphs.basic_text)) {
glyphset_id = 3;
} else if (glyphs.equals(Glyphs.numbers)) {
glyphset_id = 4;
} else if (glyphs.equals(Glyphs.alpha)) {
glyphset_id = 5;
} else if (glyphs.equals(Glyphs.all)) {
glyphset_id = 6;
} else if (glyphs.equals(Glyphs.alnum_extra)) {
glyphset_id = 7;
} else if (glyphs.equals(Glyphs.signs)) {
glyphset_id = 8;
} else if (glyphs.equals(Glyphs.signs_extra)) {
glyphset_id = 9;
} else {
this.glyphs = glyphs;
}
}
@Override
public boolean equals(Object obj)
{
if (obj == null) return false;
if (!(obj.getClass().isAssignableFrom(getClass()))) return false;
if (obj instanceof FontId) {
if (obj == this) return true;
FontId id2 = ((FontId) obj);
boolean flag = true;
flag &= id2.size == size;
flag &= id2.name.equals(name);
flag &= id2.style == style;
flag &= ((id2.glyphset_id != -1 && id2.glyphset_id == glyphset_id) || id2.glyphs.equals(glyphs));
return flag;
}
return false;
}
@Override
public int hashCode()
{
return (new Float(size).hashCode()) ^ name.hashCode() ^ style.hashCode() ^ glyphset_id;
}
@Override
public String toString()
{
return "[" + name + ", " + size + ", " + style + (glyphset_id > 0 ? ", g=" + glyphset_id : ", g=custom") + "]";
}
}
/**
* Group of styles of one font.
*
* @author MightyPork
*/
public static class FontFamily extends HashMap<Style, String> {
}
/**
* Table of font files. name {style:file,style:file,style:file...}
*/
private static HashMap<String, FontFamily> fontFiles = new HashMap<String, FontFamily>();
/**
* Register font file.
*
* @param path resource path (res/fonts/...)
* @param name font name (for binding)
* @param style font style in this file
*/
public static void registerFile(String path, String name, Style style)
{
if (fontFiles.containsKey(name)) {
if (fontFiles.get(name) != null) {
fontFiles.get(name).put(style, path);
return;
}
}
// insert new table of styles to font name.
FontFamily family = new FontFamily();
family.put(style, path);
fontFiles.put(name, family);
}
/** Counter of loaded fonts */
public static int loadedFontCounter = 0;
/**
* Preload font if needed, get preloaded font.<br>
* If needed file is not available, throws runtime exception.
*
* @param name font name (registerFile)
* @param size font size (pt)
* @param style font style
* @param glyphs glyphs needed
* @return the loaded font.
*/
public static LoadedFont loadFont(String name, double size, Style style, String glyphs)
{
return loadFont(name, size, style, glyphs, 9, 8, Coord.ONE, 0, 0);
}
/**
* Preload font if needed, get preloaded font.<br>
* If needed file is not available, throws runtime exception.
*
* @param name font name (registerFile)
* @param size font size (pt)
* @param style font style
* @param glyphs glyphs needed
* @param correctLeft left horizontal correction
* @param correctRight right horizontal correction
* @param scale font scale (changing aspect ratio)
* @param clipTop top clip (0-1) - top part of the font to be cut off
* @param clipBottom bottom clip (0-1) - bottom part of the font to be cut
* off
* @return the loaded font.
*/
public static LoadedFont loadFont(String name, double size, Style style, String glyphs, int correctLeft, int correctRight, Coord scale, double clipTop, double clipBottom)
{
String resourcePath;
try {
resourcePath = fontFiles.get(name).get(style);
if (resourcePath == null) {
Log.w("Font [" + name + "] does not have variant " + style + ".\nUsing NORMAL instead.");
resourcePath = fontFiles.get(name).get(Style.NORMAL);
if (resourcePath == null) {
throw new NullPointerException();
}
}
} catch (NullPointerException npe) {
throw new RuntimeException("Font loading failed: no font file registered for name \"" + name + "\".");
}
InputStream in = ResourceLoader.getResourceAsStream(resourcePath);
Font awtFont;
try {
awtFont = Font.createFont(Font.TRUETYPE_FONT, in);
} catch (Exception e) {
Log.e("Loading of font " + resourcePath + " failed.", e);
throw new RuntimeException(e);
}
awtFont = awtFont.deriveFont((float) size); // set font size
LoadedFont font = new LoadedFont(awtFont, true, glyphs);
font.setCorrection(correctLeft, correctRight);
font.setClip(clipTop, clipBottom);
font.setScale(scale.x, scale.y);
loadedFontCounter++;
if (DEBUG) Log.f3("Font from file \"" + resourcePath + "\" preloaded.");
return font;
}
}

@ -0,0 +1,61 @@
package mightypork.rogue.fonts;
import static mightypork.rogue.fonts.FontManager.Style.*;
import mightypork.rogue.fonts.FontManager.Glyphs;
import mightypork.utils.logging.Log;
/**
* Global font preloader
*
* @author Rapus
*/
@SuppressWarnings("javadoc")
public class Fonts {
public static LoadedFont splash_info;
public static LoadedFont tooltip;
public static LoadedFont gui;
public static LoadedFont gui_title;
public static LoadedFont program_number;
public static LoadedFont menu_button;
public static LoadedFont menu_title;
public static LoadedFont tiny;
private static void registerFileNames()
{
FontManager.registerFile("res/fonts/4feb.ttf", "4feb", NORMAL);
}
/**
* Load fonts needed for splash.
*/
public static void loadForSplash()
{
registerFileNames();
gui = FontManager.loadFont("4feb", 24, NORMAL, Glyphs.basic).setCorrection(8, 7);
splash_info = FontManager.loadFont("4feb", 42, NORMAL, "Loading.");
}
/**
* Preload all fonts we will use in the game
*/
public static void load()
{
tooltip = FontManager.loadFont("4feb", 24, NORMAL, Glyphs.basic_text).setCorrection(8, 7);
gui_title = FontManager.loadFont("4feb", 30, NORMAL, Glyphs.basic_text);
menu_button = FontManager.loadFont("4feb", 36, NORMAL, Glyphs.basic_text);
menu_title = FontManager.loadFont("4feb", 34, NORMAL, Glyphs.basic_text);
program_number = FontManager.loadFont("4feb", 28, NORMAL, Glyphs.numbers);
tiny = FontManager.loadFont("4feb", 20, NORMAL, Glyphs.basic_text);
Log.i("Fonts loaded = " + FontManager.loadedFontCounter);
}
}

@ -0,0 +1,665 @@
package mightypork.rogue.fonts;
import static mightypork.rogue.fonts.Align.*;
import static org.lwjgl.opengl.GL11.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferByte;
import java.awt.image.DataBufferInt;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import mightypork.rogue.Config;
import mightypork.utils.logging.Log;
import mightypork.utils.math.color.RGB;
import mightypork.utils.math.coord.Coord;
import mightypork.utils.math.coord.CoordI;
import org.lwjgl.BufferUtils;
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.glu.GLU;
/**
* A TrueType font implementation originally for Slick, edited for Bobjob's
* Engine
*
* @original author James Chambers (Jimmy)
* @original author Jeremy Adams (elias4444)
* @original author Kevin Glass (kevglass)
* @original author Peter Korzuszek (genail)
* @new version edited by David Aaron Muhar (bobjob)
* @new version edited by MightyPork
*/
public class LoadedFont {
private static final boolean DEBUG = Config.LOG_FONTS;
/** Map of user defined font characters (Character <-> IntObject) */
private Map<Character, CharStorageEntry> chars = new HashMap<Character, CharStorageEntry>(100);
/** Boolean flag on whether AntiAliasing is enabled or not */
private boolean antiAlias;
/** Font's size */
private int fontSize = 0;
/** Font's height */
private int fontHeight = 0;
/** Texture used to cache the font 0-255 characters */
private int fontTextureID;
/** Default font texture width */
private int textureWidth = 2048;
/** Default font texture height */
private int textureHeight = 2048;
/** A reference to Java's AWT Font that we create our font texture from */
private Font font;
/** The font metrics for our Java AWT font */
private FontMetrics fontMetrics;
private int correctL = 9, correctR = 8;
private double defScaleX = 1, defScaleY = 1;
private double clipVerticalT = 0;
private double clipVerticalB = 0;
private class CharStorageEntry {
/** Character's width */
public int width;
/** Character's height */
public int height;
/** Character's stored x position */
public int texPosX;
/** Character's stored y position */
public int texPosY;
}
public LoadedFont(Font font, boolean antiAlias, String charsNeeded) {
this.font = font;
this.fontSize = font.getSize() + 3;
this.antiAlias = antiAlias;
createSet(charsNeeded.toCharArray());
fontHeight -= 1;
if (fontHeight <= 0) fontHeight = 1;
}
public void setCorrection(boolean on)
{
if (on) {
correctL = 9;//2
correctR = 8;//1
} else {
correctL = 0;
correctR = 0;
}
}
private BufferedImage getFontImage(char ch)
{
// Create a temporary image to extract the character's size
BufferedImage tempfontImage = new BufferedImage(1, 1, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = (Graphics2D) tempfontImage.getGraphics();
if (antiAlias == true) {
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
}
g.setFont(font);
fontMetrics = g.getFontMetrics();
int charwidth = fontMetrics.charWidth(ch) + 8;
if (charwidth <= 0) {
charwidth = 7;
}
int charheight = fontMetrics.getHeight() + 3;
if (charheight <= 0) {
charheight = fontSize;
}
// Create another image holding the character we are creating
BufferedImage fontImage;
fontImage = new BufferedImage(charwidth, charheight, BufferedImage.TYPE_INT_ARGB);
Graphics2D gt = (Graphics2D) fontImage.getGraphics();
if (antiAlias == true) {
gt.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
}
gt.setFont(font);
gt.setColor(Color.WHITE);
int charx = 3;
int chary = 1;
gt.drawString(String.valueOf(ch), (charx), (chary) + fontMetrics.getAscent());
return fontImage;
}
private void createSet(char[] charsToLoad)
{
try {
class LoadedGlyph {
public char c;
public BufferedImage image;
public int width;
public int height;
public LoadedGlyph(char c, BufferedImage image) {
this.image = image;
this.c = c;
this.width = image.getWidth();
this.height = image.getHeight();
}
}
List<LoadedGlyph> glyphs = new ArrayList<LoadedGlyph>();
List<Character> loaded = new ArrayList<Character>();
for (char ch : charsToLoad) {
if (!loaded.contains(ch)) {
glyphs.add(new LoadedGlyph(ch, getFontImage(ch)));
loaded.add(ch);
}
}
Coord canvas = new Coord(128, 128);
double lineHeight = 0;
Coord begin = new Coord(0, 0);
boolean needsLarger = false;
while (true) {
needsLarger = false;
for (LoadedGlyph glyph : glyphs) {
if (begin.x + glyph.width > canvas.x) {
begin.y += lineHeight;
lineHeight = 0;
begin.x = 0;
}
if (lineHeight < glyph.height) {
lineHeight = glyph.height;
}
if (begin.y + lineHeight > canvas.y) {
needsLarger = true;
break;
}
// draw.
begin.x += glyph.width;
}
if (needsLarger) {
canvas.x *= 2;
canvas.y *= 2;
begin.setTo(0, 0);
lineHeight = 0;
} else {
if (DEBUG) Log.f3("Preparing texture " + canvas.x + "x" + canvas.y);
break;
}
}
textureWidth = (int) canvas.x;
textureHeight = (int) canvas.y;
BufferedImage imgTemp = new BufferedImage(textureWidth, textureHeight, BufferedImage.TYPE_INT_ARGB);
Graphics2D g = (Graphics2D) imgTemp.getGraphics();
g.setColor(new Color(0, 0, 0, 1));
g.fillRect(0, 0, textureWidth, textureHeight);
int rowHeight = 0;
int positionX = 0;
int positionY = 0;
for (LoadedGlyph glyph : glyphs) {
CharStorageEntry storedChar = new CharStorageEntry();
storedChar.width = glyph.width;
storedChar.height = glyph.height;
if (positionX + storedChar.width >= textureWidth) {
positionX = 0;
positionY += rowHeight;
rowHeight = 0;
}
storedChar.texPosX = positionX;
storedChar.texPosY = positionY;
if (storedChar.height > fontHeight) {
fontHeight = storedChar.height;
}
if (storedChar.height > rowHeight) {
rowHeight = storedChar.height;
}
// Draw it here
g.drawImage(glyph.image, positionX, positionY, null);
positionX += storedChar.width;
chars.put(glyph.c, storedChar);
}
fontTextureID = loadImage(imgTemp);
imgTemp = null;
} catch (Exception e) {
System.err.println("Failed to create font.");
e.printStackTrace();
}
}
private void drawQuad(double drawX, double drawY, double drawX2, double drawY2, CharStorageEntry charObj)
{
double srcX = charObj.texPosX + charObj.width;
double srcY = charObj.texPosY + charObj.height;
double srcX2 = charObj.texPosX;
double srcY2 = charObj.texPosY;
double DrawWidth = drawX2 - drawX;
double DrawHeight = drawY2 - drawY;
double TextureSrcX = srcX / textureWidth;
double TextureSrcY = srcY / textureHeight;
double SrcWidth = srcX2 - srcX;
double SrcHeight = srcY2 - srcY;
double RenderWidth = (SrcWidth / textureWidth);
double RenderHeight = (SrcHeight / textureHeight);
drawY -= DrawHeight * clipVerticalB;
GL11.glTexCoord2d(TextureSrcX, TextureSrcY);
GL11.glVertex2d(drawX, drawY);
GL11.glTexCoord2d(TextureSrcX, TextureSrcY + RenderHeight);
GL11.glVertex2d(drawX, drawY + DrawHeight);
GL11.glTexCoord2d(TextureSrcX + RenderWidth, TextureSrcY + RenderHeight);
GL11.glVertex2d(drawX + DrawWidth, drawY + DrawHeight);
GL11.glTexCoord2d(TextureSrcX + RenderWidth, TextureSrcY);
GL11.glVertex2d(drawX + DrawWidth, drawY);
}
public int getWidth(String whatchars)
{
if (whatchars == null) whatchars = "";
int totalwidth = 0;
CharStorageEntry charStorage = null;
char currentChar = 0;
for (int i = 0; i < whatchars.length(); i++) {
currentChar = whatchars.charAt(i);
charStorage = chars.get(currentChar);
if (charStorage != null) {
totalwidth += charStorage.width - correctL;
}
}
return (int) (totalwidth * defScaleX);
}
public int getHeight()
{
return (int) (fontHeight * defScaleY * (1 - clipVerticalT - clipVerticalB));
}
public int getLineHeight()
{
return getHeight();
}
public void drawString(double x, double y, String text, double scaleX, double scaleY, RGB color)
{
drawString(x, y, text, 0, text.length() - 1, scaleX, scaleY, color, LEFT);
}
public void drawString(double x, double y, String text, double scaleX, double scaleY, RGB color, int align)
{
drawString(x, y, text, 0, text.length() - 1, scaleX, scaleY, color, align);
}
private void drawString(double x, double y, String text, int startIndex, int endIndex, double scaleX, double scaleY, RGB color, int align)
{
x = Math.round(x);
y = Math.round(y);
scaleX *= defScaleX;
scaleY *= defScaleY;
CharStorageEntry charStorage = null;
int charCurrent;
int totalwidth = 0;
int i = startIndex, d = 1, c = correctL;
float startY = 0;
switch (align) {
case RIGHT: {
d = -1;
c = correctR;
while (i < endIndex) {
if (text.charAt(i) == '\n') startY -= getHeight();
i++;
}
break;
}
case CENTER: {
for (int l = startIndex; l <= endIndex; l++) {
charCurrent = text.charAt(l);
if (charCurrent == '\n') break;
charStorage = chars.get((char) charCurrent);
if (charStorage != null) {
totalwidth += charStorage.width - correctL;
}
}
totalwidth /= -2;
break;
}
case LEFT:
default: {
d = 1;
c = correctL;
break;
}
}
GL11.glPushAttrib(GL_ENABLE_BIT);
GL11.glEnable(GL11.GL_TEXTURE_2D);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, fontTextureID);
GL11.glColor4d(color.r, color.g, color.b, color.a);
GL11.glBegin(GL11.GL_QUADS);
while (i >= startIndex && i <= endIndex) {
charCurrent = text.charAt(i);
charStorage = chars.get(new Character((char) charCurrent));
if (charStorage != null) {
if (d < 0) totalwidth += (charStorage.width - c) * d;
if (charCurrent == '\n') {
startY -= getHeight() * d;
totalwidth = 0;
if (align == CENTER) {
for (int l = i + 1; l <= endIndex; l++) {
charCurrent = text.charAt(l);
if (charCurrent == '\n') break;
charStorage = chars.get((char) charCurrent);
totalwidth += charStorage.width - correctL;
}
totalwidth /= -2;
}
//if center get next lines total width/2;
} else {
//@formatter:off
drawQuad(
(totalwidth + charStorage.width) * scaleX + x,
startY * scaleY + y, totalwidth * scaleX + x,
(startY + charStorage.height) * scaleY + y,
charStorage
);
//@formatter:on
if (d > 0) totalwidth += (charStorage.width - c) * d;
}
}
i += d;
}
GL11.glEnd();
GL11.glPopAttrib();
}
public static int loadImage(BufferedImage bufferedImage)
{
try {
short width = (short) bufferedImage.getWidth();
short height = (short) bufferedImage.getHeight();
//textureLoader.bpp = bufferedImage.getColorModel().hasAlpha() ? (byte)32 : (byte)24;
int bpp = (byte) bufferedImage.getColorModel().getPixelSize();
ByteBuffer byteBuffer;
DataBuffer db = bufferedImage.getData().getDataBuffer();
if (db instanceof DataBufferInt) {
int intI[] = ((DataBufferInt) (bufferedImage.getData().getDataBuffer())).getData();
byte newI[] = new byte[intI.length * 4];
for (int i = 0; i < intI.length; i++) {
byte b[] = intToByteArray(intI[i]);
int newIndex = i * 4;
newI[newIndex] = b[1];
newI[newIndex + 1] = b[2];
newI[newIndex + 2] = b[3];
newI[newIndex + 3] = b[0];
}
byteBuffer = ByteBuffer.allocateDirect(width * height * (bpp / 8)).order(ByteOrder.nativeOrder()).put(newI);
} else {
byteBuffer = ByteBuffer.allocateDirect(width * height * (bpp / 8)).order(ByteOrder.nativeOrder()).put(((DataBufferByte) (bufferedImage.getData().getDataBuffer())).getData());
}
byteBuffer.flip();
int internalFormat = GL11.GL_RGBA8, format = GL11.GL_RGBA;
IntBuffer textureId = BufferUtils.createIntBuffer(1);
GL11.glGenTextures(textureId);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureId.get(0));
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, GL11.GL_CLAMP);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_LINEAR);
GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_LINEAR);
GL11.glTexEnvf(GL11.GL_TEXTURE_ENV, GL11.GL_TEXTURE_ENV_MODE, GL11.GL_MODULATE);
GLU.gluBuild2DMipmaps(GL11.GL_TEXTURE_2D, internalFormat, width, height, format, GL11.GL_UNSIGNED_BYTE, byteBuffer);
return textureId.get(0);
} catch (Exception e) {
e.printStackTrace();
System.exit(-1);
}
return -1;
}
public static boolean isSupported(String fontname)
{
Font font[] = getFonts();
for (int i = font.length - 1; i >= 0; i--) {
if (font[i].getName().equalsIgnoreCase(fontname)) return true;
}
return false;
}
public static Font[] getFonts()
{
return GraphicsEnvironment.getLocalGraphicsEnvironment().getAllFonts();
}
public static Font getFont(String fontname, int style, float size)
{
Font result = null;
GraphicsEnvironment graphicsenvironment = GraphicsEnvironment.getLocalGraphicsEnvironment();
for (Font font : graphicsenvironment.getAllFonts()) {
if (font.getName().equalsIgnoreCase(fontname)) {
result = font.deriveFont(style, size);
break;
}
}
return result;
}
public static byte[] intToByteArray(int value)
{
return new byte[] { (byte) (value >>> 24), (byte) (value >>> 16), (byte) (value >>> 8), (byte) value };
}
public void destroy()
{
IntBuffer scratch = BufferUtils.createIntBuffer(1);
scratch.put(0, fontTextureID);
GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0);
GL11.glDeleteTextures(scratch);
}
public LoadedFont setScale(double x, double y)
{
defScaleX = x;
defScaleY = y;
return this;
}
public LoadedFont setClip(double clipRatioTop, double clipRatioBottom)
{
clipVerticalT = clipRatioTop;
clipVerticalB = clipRatioBottom;
return this;
}
public LoadedFont setCorrection(int correctionLeft, int correctionRight)
{
correctL = correctionLeft;
correctR = correctionRight;
return this;
}
/**
* Draw string with font.
*
* @param x x coord
* @param y y coord
* @param text text to draw
* @param color render color
* @param align (-1,0,1)
*/
public void draw(double x, double y, String text, RGB color, int align)
{
drawString(x, y, text, 1, 1, color, align);
}
/**
* Draw string with font.
*
* @param pos coord
* @param text text to draw
* @param color render color
* @param align (-1,0,1)
*/
public void draw(Coord pos, String text, RGB color, int align)
{
drawString(pos.x, pos.y, text, 1, 1, color, align);
}
/**
* Draw string with font.
*
* @param pos coord
* @param text text to draw
* @param color render color
* @param align (-1,0,1)
*/
public void draw(CoordI pos, String text, RGB color, int align)
{
drawString(pos.x, pos.y, text, 1, 1, color, align);
}
public void drawFuzzy(Coord pos, String text, int align, RGB textColor, RGB blurColor, int blurSize)
{
drawFuzzy(pos, text, align, textColor, blurColor, blurSize, true);
}
public void drawFuzzy(CoordI pos, String text, int align, RGB textColor, RGB blurColor, int blurSize)
{
drawFuzzy(pos.toCoord(), text, align, textColor, blurColor, blurSize, true);
}
public void drawFuzzy(CoordI pos, String text, int align, RGB textColor, RGB blurColor, int blurSize, boolean smooth)
{
drawFuzzy(pos.toCoord(), text, align, textColor, blurColor, blurSize, smooth);
}
public void drawFuzzy(Coord pos, String text, int align, RGB textColor, RGB blurColor, int blurSize, boolean smooth)
{
glPushMatrix();
glTranslated(pos.x, pos.y, pos.z);
//shadow
int sh = blurSize;
int l = glGenLists(1);
glNewList(l, GL_COMPILE);
draw(0, 0, text, blurColor, align);
glEndList();
for (int xx = -sh; xx <= sh; xx += (smooth ? 1 : sh)) {
for (int yy = -sh; yy <= sh; yy += (smooth ? 1 : sh)) {
if (xx == 0 && yy == 0) continue;
glPushMatrix();
glTranslated(xx, yy, 0);
glCallList(l);
glPopMatrix();
}
}
glDeleteLists(l, 1);
draw(0, 0, text, textColor, align);
glPopMatrix();
}
}

@ -0,0 +1,52 @@
package mightypork.rogue.input;
import mightypork.utils.math.coord.Coord;
import mightypork.utils.math.coord.Vec;
/**
* Input event handler
*
* @author MightyPork
*/
public interface InputHandler {
/**
* Called each update tick, if the mouse position was changed.
*
* @param pos mouse position
* @param move mouse motion
* @param wheelDelta mouse wheel delta
*/
public void onMouseMove(Coord pos, Vec move, int wheelDelta);
/**
* Mouse event handler.
*
* @param button button which caused this event
* @param down true = down, false = up
* @param wheelDelta number of steps the wheel turned since last event
* @param pos mouse position
* @param deltaPos delta mouse position
*/
public void onMouseButton(int button, boolean down, int wheelDelta, Coord pos, Coord deltaPos);
/**
* Key event handler.
*
* @param key key index, constant Keyboard.KEY_???
* @param c character typed, if any
* @param down true = down, false = up
*/
public void onKey(int key, char c, boolean down);
/**
* In this method screen can handle static inputs, that is:
* Keyboard.isKeyDown, Mouse.isButtonDown etc.
*/
public void handleKeyStates();
}

@ -0,0 +1,122 @@
package mightypork.rogue.input;
import org.lwjgl.input.Keyboard;
/**
* Key state handler
*/
public class Keys {
private static boolean[] prevKeys;
private static boolean[] keys;
/**
* initialize key state handler
*/
private static void init()
{
if (keys == null) {
keys = new boolean[Keyboard.KEYBOARD_SIZE];
}
if (prevKeys == null) {
prevKeys = new boolean[Keyboard.KEYBOARD_SIZE];
}
}
/**
* method called when key event was detected in Screen class.
*
* @param key
* @param down
*/
public static void onKey(int key, boolean down)
{
init();
prevKeys[key] = keys[key];
keys[key] = down;
}
/**
* Check if key is down
*
* @param key key index
* @return is down
*/
public static boolean isDown(int key)
{
init();
return keys[key];
}
/**
* Check if key is up
*
* @param key key index
* @return is up
*/
public static boolean isUp(int key)
{
init();
return !keys[key];
}
/**
* Check if key was just pressed (changed state since last event on this
* key)
*
* @param key key index
* @return true if changed state to DOWN
*/
public static boolean justPressed(int key)
{
init();
return !prevKeys[key] && keys[key];
}
/**
* Check if key was just released (changed state since last event on this
* key)
*
* @param key key index
* @return true if changed state to UP
*/
public static boolean justReleased(int key)
{
init();
return prevKeys[key] && !keys[key];
}
/**
* Destroy "just" flag for a key.
*
* @param key key index
*/
public static void destroyChangeState(int key)
{
init();
prevKeys[key] = keys[key];
}
}

@ -0,0 +1,82 @@
package mightypork.rogue.screens;
import mightypork.rogue.Screen;
import mightypork.utils.math.coord.Coord;
import mightypork.utils.math.coord.Vec;
public class ScreenSplash extends Screen {
@Override
protected void onViewportChanged()
{
// TODO Auto-generated method stub
}
@Override
public void initScreen()
{
// TODO Auto-generated method stub
}
@Override
protected void onGuiUpdate()
{
// TODO Auto-generated method stub
}
@Override
protected void render3D()
{
// TODO Auto-generated method stub
}
@Override
public void onKey(int key, char c, boolean down)
{
// TODO Auto-generated method stub
}
@Override
public void onMouseButton(int button, boolean down, int wheeld, Coord pos, Coord delta)
{
// TODO Auto-generated method stub
}
@Override
public void handleKeyStates()
{
// TODO Auto-generated method stub
}
@Override
public void onMouseMove(Coord coord, Vec vec, int wd)
{
// TODO Auto-generated method stub
}
@Override
protected void render2D(double delta)
{
// TODO Auto-generated method stub
}
}

@ -0,0 +1,77 @@
package mightypork.rogue.sounds;
import mightypork.utils.objects.Mutable;
public abstract class AudioPlayer {
/** base gain for sfx */
private double baseGain = 1;
/** the track */
private AudioX track;
/** base pitch for sfx */
private double basePitch = 1;
/** dedicated volume control */
private Mutable<Float> gainMultiplier = null;
public AudioPlayer(AudioX track, double baseGain, Mutable<Float> gainMultiplier) {
this(track, 1, baseGain, gainMultiplier);
}
public AudioPlayer(AudioX track, double basePitch, double baseGain, Mutable<Float> gainMultiplier) {
this.track = track;
this.baseGain = baseGain;
this.basePitch = basePitch;
this.gainMultiplier = gainMultiplier;
}
public void destroy()
{
track.release();
track = null;
}
protected AudioX getAudio()
{
return track;
}
protected float getGain(double multiplier)
{
return (float) (baseGain * gainMultiplier.get() * multiplier);
}
protected float getPitch(double multiplier)
{
return (float) (basePitch * multiplier);
}
/**
* Get if audio is valid
*
* @return is valid
*/
protected boolean canPlay()
{
return (track != null);
}
public void load()
{
if (canPlay()) track.load();
}
}

@ -0,0 +1,230 @@
package mightypork.rogue.sounds;
import mightypork.utils.files.FileUtils;
import mightypork.utils.logging.Log;
import mightypork.utils.math.coord.Coord;
import org.newdawn.slick.openal.Audio;
import org.newdawn.slick.openal.SoundStore;
/**
* Wrapper class for slick audio
*
* @author MightyPork
*/
public class AudioX implements Audio {
private enum PlayMode
{
EFFECT, MUSIC;
};
private Audio audio = null;
private float pauseLoopPosition = 0;
private boolean looping = false;
private boolean paused = false;
private PlayMode mode = PlayMode.EFFECT;
private float pitch = 1;
private float gain = 1;
private String resourcePath;
/**
* Pause loop (remember position and stop playing) - if was looping
*/
public void pauseLoop()
{
if (!ensureLoaded()) return;
if (isPlaying() && looping) {
pauseLoopPosition = audio.getPosition();
stop();
paused = true;
}
}
/**
* Resume loop (if was paused)
*
* @return source ID
*/
public int resumeLoop()
{
if (!ensureLoaded()) return -1;
int source = -1;
if (looping && paused) {
if (mode == PlayMode.MUSIC) {
source = audio.playAsMusic(pitch, gain, true);
} else {
source = audio.playAsSoundEffect(pitch, gain, true);
}
audio.setPosition(pauseLoopPosition);
paused = false;
}
return source;
}
/**
* Create deferred primitive audio player
*
* @param resourceName resource to load when needed
*/
public AudioX(String resourceName) {
this.audio = null;
this.resourcePath = resourceName;
}
/**
* Check if can play, if not, try to load sound.
*
* @return can now play
*/
private boolean ensureLoaded()
{
load();
return audio != null;
}
public void load()
{
if (audio != null) return; // already loaded
if (resourcePath == null) return; // not loaded, but can't load anyway
try {
String ext = FileUtils.getExtension(resourcePath);
// java 6 can't use String switch :(
if (ext.equalsIgnoreCase("ogg")) {
audio = SoundStore.get().getOgg(resourcePath);
} else if (ext.equalsIgnoreCase("wav")) {
audio = SoundStore.get().getWAV(resourcePath);
} else if (ext.equalsIgnoreCase("aif")) {
audio = SoundStore.get().getAIF(resourcePath);
} else if (ext.equalsIgnoreCase("mod")) {
audio = SoundStore.get().getMOD(resourcePath);
} else {
resourcePath = null; // don't try next time
Log.e("Invalid audio file extension: " + resourcePath);
}
} catch (Exception e) {
Log.e("Could not load " + resourcePath, e);
resourcePath = null; // don't try next time
}
}
@Override
public void stop()
{
if (!ensureLoaded()) return;
audio.stop();
paused = false;
}
@Override
public int getBufferID()
{
if (!ensureLoaded()) return -1;
return audio.getBufferID();
}
@Override
public boolean isPlaying()
{
if (!ensureLoaded()) return false;
return audio.isPlaying();
}
@Override
public boolean isPaused()
{
if (!ensureLoaded()) return false;
return audio.isPaused();
}
@Override
public int playAsSoundEffect(float pitch, float gain, boolean loop)
{
return playAsSoundEffect(pitch, gain, loop, SoundManager.get().listener);
}
@Override
public int playAsSoundEffect(float pitch, float gain, boolean loop, float x, float y, float z)
{
if (!ensureLoaded()) return -1;
this.pitch = pitch;
this.gain = gain;
looping = loop;
mode = PlayMode.EFFECT;
return audio.playAsSoundEffect(pitch, gain, loop, x, y, z);
}
/**
* Play this sound as a sound effect
*
* @param pitch The pitch of the play back
* @param gain The gain of the play back
* @param loop True if we should loop
* @param pos The position of the sound
* @return The ID of the source playing the sound
*/
public int playAsSoundEffect(float pitch, float gain, boolean loop, Coord pos)
{
return playAsSoundEffect(pitch, gain, loop, (float) pos.x, (float) pos.y, (float) pos.z);
}
@Override
public int playAsMusic(float pitch, float gain, boolean loop)
{
this.pitch = pitch;
this.gain = gain;
looping = loop;
mode = PlayMode.MUSIC;
return audio.playAsMusic(pitch, gain, loop);
}
@Override
public boolean setPosition(float position)
{
return audio.setPosition(position);
}
@Override
public float getPosition()
{
return audio.getPosition();
}
@Override
public void release()
{
audio.release();
audio = null;
}
}

@ -0,0 +1,36 @@
package mightypork.rogue.sounds;
import mightypork.utils.math.coord.Coord;
import mightypork.utils.objects.Mutable;
public class EffectPlayer extends AudioPlayer {
public EffectPlayer(AudioX track, double basePitch, double baseGain, Mutable<Float> gainMultiplier) {
super(track, (float) basePitch, (float) baseGain, gainMultiplier);
}
public int play(double pitch, double gain)
{
if (!canPlay()) return -1;
return getAudio().playAsSoundEffect(getPitch(pitch), getGain(gain), false);
}
public int play(double gain)
{
return play(1, gain);
}
public int play(double pitch, double gain, Coord pos)
{
if (!canPlay()) return -1;
return getAudio().playAsSoundEffect(getPitch(pitch), getGain(gain), false, pos);
}
}

@ -0,0 +1,51 @@
package mightypork.rogue.sounds;
import mightypork.utils.math.Calc;
import mightypork.utils.objects.Mutable;
/**
* Volume multiplex
*
* @author MightyPork
*/
public class JointVolume extends Mutable<Float> {
private Mutable<Float>[] volumes;
/**
* CReate joint volume with master gain of 1
*
* @param volumes individual volumes to join
*/
public JointVolume(Mutable<Float>... volumes) {
super(1F);
this.volumes = volumes;
}
/**
* Get combined gain (multiplied)
*/
@Override
public Float get()
{
float f = super.get();
for (Mutable<Float> v : volumes)
f *= v.get();
return Calc.clampf(f, 0, 1);
}
/**
* Set master gain
*/
@Override
public void set(Float o)
{
super.set(o);
}
}

@ -0,0 +1,132 @@
package mightypork.rogue.sounds;
import mightypork.utils.objects.Mutable;
import mightypork.utils.time.AnimDouble;
import mightypork.utils.time.Pauseable;
import mightypork.utils.time.Updateable;
import org.lwjgl.openal.AL10;
public class LoopPlayer extends AudioPlayer implements Updateable, Pauseable {
private int sourceID = -1;
/** animator for fade in and fade out */
private AnimDouble fadeAnim = new AnimDouble(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;
public LoopPlayer(AudioX track, double pitch, double baseGain, Mutable<Float> gainMultiplier) {
super(track, (float) pitch, (float) baseGain, gainMultiplier);
paused = true;
}
public void setFadeTimes(double in, double out)
{
inTime = in;
outTime = out;
}
private void initLoop()
{
if (!canPlay() && sourceID == -1) {
sourceID = getAudio().playAsSoundEffect(getPitch(1), getGain(1), true);
getAudio().pauseLoop();
}
}
@Override
public void pause()
{
if (!canPlay() || paused) return;
initLoop();
getAudio().pauseLoop();
paused = true;
}
@Override
public boolean isPaused()
{
return paused;
}
@Override
public void resume()
{
if (!canPlay() || paused) return;
initLoop();
sourceID = getAudio().resumeLoop();
paused = false;
}
@Override
public void update(double delta)
{
if (!canPlay() || paused) return;
initLoop();
fadeAnim.update(delta);
double gain = getGain(fadeAnim.getCurrentValue());
if (!paused && gain != lastUpdateGain) {
AL10.alSourcef(sourceID, AL10.AL_GAIN, (float) gain);
lastUpdateGain = gain;
}
if (gain == 0 && !paused) pause(); // pause on zero volume
}
public void fadeIn(double secs)
{
if (!canPlay()) return;
resume();
fadeAnim.fadeIn(secs);
}
public void fadeOut(double secs)
{
if (!canPlay()) return;
fadeAnim.fadeOut(secs);
}
public void fadeIn()
{
fadeIn(inTime);
}
public void fadeOut()
{
fadeOut(outTime);
}
}

@ -0,0 +1,179 @@
package mightypork.rogue.sounds;
import java.nio.FloatBuffer;
import java.util.HashMap;
import java.util.Map;
import mightypork.utils.math.Calc.Buffers;
import mightypork.utils.math.coord.Coord;
import mightypork.utils.objects.Mutable;
import mightypork.utils.time.Updateable;
import org.lwjgl.openal.AL10;
import org.newdawn.slick.openal.SoundStore;
/**
* Preloaded sounds.
*
* @author MightyPork
*/
public class SoundManager implements Updateable {
private static SoundManager inst = new SoundManager();
public Mutable<Float> masterVolume = new Mutable<Float>(1F);
@SuppressWarnings("unchecked")
public Mutable<Float> effectsVolume = new JointVolume(masterVolume);
@SuppressWarnings("unchecked")
public Mutable<Float> loopsVolume = new JointVolume(masterVolume);
public Coord listener = new Coord(Coord.ZERO);
private Map<String, EffectPlayer> effects = new HashMap<String, EffectPlayer>();
private Map<String, LoopPlayer> loops = new HashMap<String, LoopPlayer>();
/**
* Singleton constructor
*/
private SoundManager() {
SoundStore.get().setMaxSources(256);
SoundStore.get().init();
}
public static SoundManager get()
{
return inst;
}
public void addEffect(String key, String resource, float pitch, float gain)
{
EffectPlayer p = new EffectPlayer(new AudioX(resource), pitch, gain, effectsVolume);
effects.put(key, p);
}
public void addLoop(String key, String resource, float pitch, float gain, double fadeIn, double fadeOut)
{
LoopPlayer p = new LoopPlayer(new AudioX(resource), pitch, gain, loopsVolume);
p.setFadeTimes(fadeIn, fadeOut);
loops.put(key, p);
}
public LoopPlayer getLoop(String key)
{
LoopPlayer p = loops.get(key);
if (p == null) {
throw new IllegalArgumentException("Requesting unknown sound loop \"" + key + "\".");
}
return p;
}
public EffectPlayer getEffect(String key)
{
EffectPlayer p = effects.get(key);
if (p == null) {
throw new IllegalArgumentException("Requesting unknown sound effect \"" + key + "\".");
}
return p;
}
public void fadeOutAllLoops()
{
for (LoopPlayer p : loops.values()) {
p.fadeOut();
}
}
public void fadeInLoop(String key)
{
getLoop(key).fadeIn();
}
public void fadeInLoop(String key, double seconds)
{
getLoop(key).fadeIn(seconds);
}
public void pauseLoop(String key)
{
getLoop(key).pause();
}
public void pauseAllLoops()
{
for (LoopPlayer p : loops.values()) {
p.pause();
}
}
public void resumeLoop(String key)
{
getLoop(key).resume();
}
/**
* Set listener pos
*
* @param pos
*/
public void setListener(Coord pos)
{
listener.setTo(pos);
FloatBuffer buf3 = Buffers.alloc(3);
FloatBuffer buf6 = Buffers.alloc(6);
buf3.clear();
Buffers.fill(buf3, (float) pos.x, (float) pos.y, (float) pos.z);
AL10.alListener(AL10.AL_POSITION, buf3);
buf3.clear();
Buffers.fill(buf3, 0, 0, 0);
AL10.alListener(AL10.AL_VELOCITY, buf3);
buf6.clear();
Buffers.fill(buf6, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f, 0.0f);
AL10.alListener(AL10.AL_ORIENTATION, buf6);
buf3 = buf6 = null;
}
@Override
public void update(double delta)
{
for (LoopPlayer lp : loops.values()) {
lp.update(delta);
}
}
public void setMasterVolume(float f)
{
masterVolume.set(f);
}
public void setEffectsVolume(float f)
{
effectsVolume.set(f);
}
public void setMusicVolume(float f)
{
loopsVolume.set(f);
}
public void destroy()
{
SoundStore.get().clear();
}
}

@ -0,0 +1,76 @@
package mightypork.rogue.textures;
import static org.lwjgl.opengl.GL11.*;
import java.io.IOException;
import mightypork.utils.logging.Log;
import org.newdawn.slick.opengl.Texture;
import org.newdawn.slick.opengl.TextureLoader;
import org.newdawn.slick.util.ResourceLoader;
/**
* Texture manager
*
* @author MightyPork
*/
public class TextureManager {
private static Texture lastBinded = null;
/**
* Load texture
*
* @param resourcePath
* @return the loaded texture
*/
public static Texture load(String resourcePath)
{
try {
String ext = resourcePath.substring(resourcePath.length() - 4);
Texture texture = TextureLoader.getTexture(ext.toUpperCase(), ResourceLoader.getResourceAsStream(resourcePath));
if (texture != null) {
return texture;
}
Log.w("Texture " + resourcePath + " could not be loaded.");
return null;
} catch (IOException e) {
Log.e("Loading of texture " + resourcePath + " failed.", e);
throw new RuntimeException(e);
}
}
/**
* Bind texture
*
* @param texture the texture
* @throws RuntimeException if not loaded yet
*/
public static void bind(Texture texture) throws RuntimeException
{
if (texture != lastBinded) {
glBindTexture(GL_TEXTURE_2D, 0);
glBindTexture(GL_TEXTURE_2D, texture.getTextureID());
lastBinded = texture;
}
}
/**
* Unbind all
*/
public static void unbind()
{
glBindTexture(GL_TEXTURE_2D, 0);
lastBinded = null;
}
}

@ -0,0 +1,141 @@
package mightypork.rogue.textures;
import static org.lwjgl.opengl.GL11.*;
import org.newdawn.slick.opengl.Texture;
/**
* Texture loading class
*
* @author MightyPork
*/
@SuppressWarnings("javadoc")
// unrelevant
public class Textures {
protected static Texture buttons_menu;
protected static Texture buttons_small;
// patterns need raw access (are repeated)
public static Texture steel_small_dk;
public static Texture steel_small_lt;
public static Texture steel_big_dk;
public static Texture steel_big_lt;
public static Texture steel_big_scratched;
public static Texture steel_brushed;
public static Texture steel_rivet_belts;
public static Texture water;
// particles need direct access
public static Texture snow;
public static Texture leaves;
public static Texture rain;
public static Texture circle;
protected static Texture logo;
protected static Texture widgets;
public static Texture program;
protected static Texture icons;
public static Texture map_tiles;
public static Texture map_shading;
// spotted
public static Texture turtle_blue;
public static Texture turtle_green;
public static Texture turtle_purple;
public static Texture turtle_yellow;
// misc
public static Texture turtle_red;
public static Texture turtle_brown_dk;
public static Texture turtle_brown_lt;
public static Texture turtle_brown_orn;
// a shadow sheet
public static Texture turtle_shadows;
public static Texture food;
public static Texture food_shadows;
private static final String GUI = "res/images/gui/";
private static final String PATTERNS = "res/images/patterns/";
private static final String PROGRAM = "res/images/program/";
private static final String TILES = "res/images/tiles/";
private static final String SPRITES = "res/images/sprites/";
private static final String PARTICLES = "res/images/particles/";
/**
* Load what's needed for splash
*/
public static void loadForSplash()
{
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
logo = TextureManager.load(GUI + "logo.png");
steel_small_dk = TextureManager.load(PATTERNS + "steel.png");
Tx.initForSplash();
}
/**
* Load textures
*/
public static void load()
{
// gui
buttons_menu = TextureManager.load(GUI + "buttons_menu.png");
buttons_small = TextureManager.load(GUI + "buttons_small.png");
widgets = TextureManager.load(GUI + "widgets.png");
icons = TextureManager.load(GUI + "icons.png");
// patterns
steel_small_lt = TextureManager.load(PATTERNS + "steel_light.png"); // XXX Unused texture
steel_big_dk = TextureManager.load(PATTERNS + "steel_big.png");
steel_big_lt = TextureManager.load(PATTERNS + "steel_big_light.png");
steel_big_scratched = TextureManager.load(PATTERNS + "steel_big_scratched.png");
steel_brushed = TextureManager.load(PATTERNS + "steel_brushed.png");
steel_rivet_belts = TextureManager.load(PATTERNS + "rivet_belts.png");
water = TextureManager.load(PATTERNS + "water.png");
// particles
snow = TextureManager.load(PARTICLES + "snow.png");
leaves = TextureManager.load(PARTICLES + "leaves.png");
rain = TextureManager.load(PARTICLES + "rain.png");
circle = TextureManager.load(PARTICLES + "circle.png");
// program tiles
program = TextureManager.load(PROGRAM + "program.png");
// map tiles
map_tiles = TextureManager.load(TILES + "tiles.png");
map_shading = TextureManager.load(TILES + "shading.png");
// turtle
turtle_blue = TextureManager.load(SPRITES + "HD-blue.png");
turtle_yellow = TextureManager.load(SPRITES + "HD-yellow.png");
turtle_green = TextureManager.load(SPRITES + "HD-green.png");
turtle_purple = TextureManager.load(SPRITES + "HD-purple.png");
turtle_red = TextureManager.load(SPRITES + "HD-red.png");
turtle_brown_dk = TextureManager.load(SPRITES + "HD-brown-dk.png");
turtle_brown_lt = TextureManager.load(SPRITES + "HD-brown-lt.png");
turtle_brown_orn = TextureManager.load(SPRITES + "HD-brown-ornamental.png");
turtle_shadows = TextureManager.load(SPRITES + "HD-shadow.png");
food = TextureManager.load(SPRITES + "food.png");
food_shadows = TextureManager.load(SPRITES + "food-shadow.png");
glDisable(GL_TEXTURE_2D);
Tx.init();
}
}

@ -0,0 +1,29 @@
package mightypork.rogue.textures;
/**
* List of texture quads for GUIs
*
* @author MightyPork
*/
@SuppressWarnings("javadoc")
public class Tx {
// logo
public static TxQuad LOGO;
public static void initForSplash()
{
// splash logo
LOGO = TxQuad.fromSize(Textures.logo, 15, 9, 226, 132);
}
public static void init()
{
// title image (word art)
}
}

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

@ -0,0 +1,91 @@
package mightypork.rogue.threads;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.imageio.ImageIO;
import mightypork.rogue.Paths;
import mightypork.utils.logging.Log;
/**
* Thread for saving screenshot
*
* @author MightyPork
*/
public class ThreadSaveScreenshot extends Thread {
private ByteBuffer buffer;
private int width;
private int height;
private int bpp;
/**
* Save screenshot thread
*
* @param buffer byte buffer with image data
* @param width screen width
* @param height screen height
* @param bpp bits per pixel
*/
public ThreadSaveScreenshot(ByteBuffer buffer, int width, int height, int bpp) {
this.buffer = buffer;
this.width = width;
this.height = height;
this.bpp = bpp;
}
private String getUniqueScreenshotName()
{
DateFormat df = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss");
return df.format(new Date());
}
@Override
public void run()
{
String fname = getUniqueScreenshotName();
// generate unique filename
File file;
int index = 0;
while (true) {
file = new File(Paths.SCREENSHOTS, fname + (index > 0 ? "-" + index : "") + ".png");
if (!file.exists()) break;
index++;
}
Log.f3("Saving screenshot to file: " + file);
String format = "PNG";
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// convert to a buffered image
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
int i = (x + (width * y)) * bpp;
int r = buffer.get(i) & 0xFF;
int g = buffer.get(i + 1) & 0xFF;
int b = buffer.get(i + 2) & 0xFF;
image.setRGB(x, height - (y + 1), (0xFF << 24) | (r << 16) | (g << 8) | b);
}
}
// save to disk
try {
ImageIO.write(image, format, file);
} catch (IOException e) {
Log.e("Failed to save screenshot.", e);
}
}
}

@ -0,0 +1,32 @@
package mightypork.rogue.threads;
import mightypork.rogue.App;
import mightypork.rogue.input.Keys;
import mightypork.utils.logging.Log;
import org.lwjgl.input.Keyboard;
/**
* @author MightyPork
*/
public class ThreadScreenshotTrigger extends Thread {
@Override
public void run()
{
while (true) {
if (Keys.justPressed(Keyboard.KEY_F2)) {
Log.f2("F2, taking screenshot.");
App.scheduledScreenshot = true;
Keys.destroyChangeState(Keyboard.KEY_F2);
}
try {
sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

@ -0,0 +1,919 @@
package mightypork.rogue.util;
import static org.lwjgl.opengl.GL11.*;
import mightypork.rogue.textures.TextureManager;
import mightypork.rogue.textures.TxQuad;
import mightypork.utils.math.color.HSV;
import mightypork.utils.math.color.RGB;
import mightypork.utils.math.coord.Coord;
import mightypork.utils.math.coord.Rect;
import org.newdawn.slick.opengl.Texture;
/**
* Render utilities
*
* @author MightyPork
*/
public class RenderUtils {
/**
* Render quad 2D
*
* @param min min coord
* @param max max coord
*/
public static void quadCoord(Coord min, Coord max)
{
quadCoord(min.x, min.y, max.x, max.y);
}
/**
* Render quad with absolute coords (from screen bottom)
*
* @param left left x
* @param bottom bottom y
* @param right right x
* @param top top y
*/
public static void quadCoord(double left, double bottom, double right, double top)
{
glBegin(GL_QUADS);
glVertex2d(left, top);
glVertex2d(right, top);
glVertex2d(right, bottom);
glVertex2d(left, bottom);
glEnd();
}
/**
* Render quad with coloured border
*
* @param min min
* @param max max
* @param border border width
* @param borderColor border color
* @param insideColor filling color
*/
public static void quadCoordBorder(Coord min, Coord max, double border, RGB borderColor, RGB insideColor)
{
quadCoordBorder(min.x, min.y, max.x, max.y, border, borderColor, insideColor);
}
/**
* Render quad with coloured border
*
* @param minX min x
* @param minY min y
* @param maxX max x
* @param maxY max y
* @param border border width
* @param borderColor border color
* @param insideColor filling color
*/
public static void quadCoordBorder(double minX, double minY, double maxX, double maxY, double border, RGB borderColor, RGB insideColor)
{
double bdr = border;
RenderUtils.setColor(borderColor);
//@formatter:off
RenderUtils.quadCoord(minX, minY, minX + bdr, maxY);
RenderUtils.quadCoord(maxX - bdr, minY, maxX, maxY);
RenderUtils.quadCoord(minX + bdr, maxY - bdr, maxX - bdr, maxY);
RenderUtils.quadCoord(minX + bdr, minY, maxX - bdr, minY + bdr);
//@formatter:on
if (insideColor != null) {
RenderUtils.setColor(insideColor);
RenderUtils.quadCoord(minX + bdr, minY + bdr, maxX - bdr, maxY - bdr);
}
}
/**
* Render quad 2D with gradient horizontal
*
* @param min min coord
* @param max max coord
* @param colorLeft color left
* @param colorRight color right
*/
public static void quadCoordGradH(Coord min, Coord max, RGB colorLeft, RGB colorRight)
{
quadCoordGradH(min.x, min.y, max.x, max.y, colorLeft, colorRight);
}
/**
* Render quad 2D with gradient horizontal
*
* @param minX units from left
* @param minY units from bottom
* @param maxX quad width
* @param maxY quad height
* @param colorLeft color left
* @param colorRight color right
*/
public static void quadCoordGradH(double minX, double minY, double maxX, double maxY, RGB colorLeft, RGB colorRight)
{
glBegin(GL_QUADS);
setColor(colorLeft);
glVertex2d(minX, maxY);
setColor(colorRight);
glVertex2d(maxX, maxY);
setColor(colorRight);
glVertex2d(maxX, minY);
setColor(colorLeft);
glVertex2d(minX, minY);
glEnd();
}
/**
* Render quad 2D with gradient horizontal
*
* @param min min coord
* @param max max coord
* @param colorOuter color outer
* @param colorMiddle color inner
*/
public static void quadCoordGradHBilinear(Coord min, Coord max, RGB colorOuter, RGB colorMiddle)
{
quadCoordGradHBilinear(min.x, min.y, max.x, max.y, colorOuter, colorMiddle);
}
/**
* Render quad 2D with gradient horizontal
*
* @param minX min x
* @param minY min y
* @param maxX max x
* @param maxY max y
* @param colorOuter color outer
* @param colorMiddle color inner
*/
public static void quadCoordGradHBilinear(double minX, double minY, double maxX, double maxY, RGB colorOuter, RGB colorMiddle)
{
glBegin(GL_QUADS);
setColor(colorOuter);
glVertex2d(minX, maxY);
setColor(colorMiddle);
glVertex2d((minX + maxX) / 2, maxY);
setColor(colorMiddle);
glVertex2d((minX + maxX) / 2, minY);
setColor(colorOuter);
glVertex2d(minX, minY);
setColor(colorMiddle);
glVertex2d((minX + maxX) / 2, maxY);
setColor(colorOuter);
glVertex2d(maxX, maxY);
setColor(colorOuter);
glVertex2d(maxX, minY);
setColor(colorMiddle);
glVertex2d((minX + maxX) / 2, minY);
glEnd();
}
/**
* Render quad 2D with gradient vertical
*
* @param min min coord
* @param max max coord
* @param colorTop top color
* @param colorBottom bottom color
*/
public static void quadCoordGradV(Coord min, Coord max, RGB colorTop, RGB colorBottom)
{
quadCoordGradV(min.x, min.y, max.x, max.y, colorTop, colorBottom);
}
/**
* Render quad 2D with gradient vertical
*
* @param minX min X
* @param minY min Y
* @param maxX max X
* @param maxY max Y
* @param colorTop top color
* @param colorBottom bottom color
*/
public static void quadCoordGradV(double minX, double minY, double maxX, double maxY, RGB colorTop, RGB colorBottom)
{
glBegin(GL_QUADS);
setColor(colorTop);
glVertex2d(minX, maxY);
glVertex2d(maxX, maxY);
setColor(colorBottom);
glVertex2d(maxX, minY);
glVertex2d(minX, minY);
glEnd();
}
/**
* Render quad 2D with gradient vertical
*
* @param min min coord
* @param max max coord
* @param colorOuter outer color
* @param colorMiddle middle color
*/
public static void quadCoordGradVBilinear(Coord min, Coord max, RGB colorOuter, RGB colorMiddle)
{
quadCoordGradVBilinear(min.x, min.y, max.x, max.y, colorOuter, colorMiddle);
}
/**
* Render quad 2D with gradient vertical
*
* @param minX min X
* @param minY min Y
* @param maxX max X
* @param maxY max Y
* @param colorOuter outer color
* @param colorMiddle middle color
*/
public static void quadCoordGradVBilinear(double minX, double minY, double maxX, double maxY, RGB colorOuter, RGB colorMiddle)
{
glBegin(GL_QUADS);
setColor(colorOuter);
glVertex2d(minX, maxY);
glVertex2d(maxX, maxY);
setColor(colorMiddle);
glVertex2d(maxX, (maxY + minY) / 2);
glVertex2d(minX, (maxY + minY) / 2);
setColor(colorMiddle);
glVertex2d(minX, (maxY + minY) / 2);
glVertex2d(maxX, (maxY + minY) / 2);
setColor(colorOuter);
glVertex2d(maxX, minY);
glVertex2d(minX, minY);
glEnd();
}
/**
* Render quad with coloured border with outset effect
*
* @param min min coord
* @param max max coord
* @param border border width
* @param fill fill color
* @param inset true for inset, false for outset
*/
public static void quadCoordOutset(Coord min, Coord max, double border, RGB fill, boolean inset)
{
quadCoordOutset(min.x, min.y, max.x, max.y, border, fill, inset);
}
/**
* Render quad with coloured border with outset effect
*
* @param minX left X
* @param minY bottom Y
* @param maxX right X
* @param maxY top Y
* @param border border width
* @param fill fill color
* @param inset true for inset, false for outset
*/
public static void quadCoordOutset(double minX, double minY, double maxX, double maxY, double border, RGB fill, boolean inset)
{
HSV hsv = fill.toHSV();
HSV h;
h = hsv.copy();
h.s *= 0.6;
RGB a = h.toRGB();
h = fill.toHSV();
h.v *= 0.6;
RGB b = h.toRGB();
RGB leftTopC, rightBottomC;
if (!inset) {
leftTopC = a;
rightBottomC = b;
} else {
leftTopC = b;
rightBottomC = a;
}
double bdr = border;
RenderUtils.setColor(leftTopC);
// left + top
glBegin(GL_QUADS);
glVertex2d(minX, minY);
glVertex2d(minX + bdr, minY + bdr);
glVertex2d(minX + bdr, maxY - bdr);
glVertex2d(minX, maxY);
glVertex2d(minX, maxY);
glVertex2d(minX + bdr, maxY - bdr);
glVertex2d(maxX - bdr, maxY - bdr);
glVertex2d(maxX, maxY);
glEnd();
RenderUtils.setColor(rightBottomC);
// right + bottom
glBegin(GL_QUADS);
glVertex2d(maxX, minY);
glVertex2d(maxX, maxY);
glVertex2d(maxX - bdr, maxY - bdr);
glVertex2d(maxX - bdr, minY + bdr);
glVertex2d(minX, minY);
glVertex2d(maxX, minY);
glVertex2d(maxX - bdr, minY + bdr);
glVertex2d(minX + bdr, minY + bdr);
glEnd();
RenderUtils.setColor(fill);
RenderUtils.quadCoord(minX + bdr, minY + bdr, maxX - bdr, maxY - bdr);
}
/**
* Render quad 2D
*
* @param rect rectangle
*/
public static void quadRect(Rect rect)
{
quadCoord(rect.getMin(), rect.getMax());
}
/**
* Render quad 2D
*
* @param rect rectangle
* @param color draw color
*/
public static void quadRect(Rect rect, RGB color)
{
setColor(color);
quadCoord(rect.getMin(), rect.getMax());
}
/**
* Render quad with coloured border
*
* @param rect rect
* @param border border width
* @param borderColor border color
* @param insideColor filling color
*/
public static void quadBorder(Rect rect, double border, RGB borderColor, RGB insideColor)
{
quadCoordBorder(rect.getMin(), rect.getMax(), border, borderColor, insideColor);
}
/**
* Render quad 2D with gradient horizontal
*
* @param rect rect
* @param colorLeft color left
* @param colorRight color right
*/
public static void quadGradH(Rect rect, RGB colorLeft, RGB colorRight)
{
quadCoordGradH(rect.getMin(), rect.getMax(), colorLeft, colorRight);
}
/**
* Render quad 2D with gradient horizontal
*
* @param rect rect
* @param colorOuter color outer
* @param colorMiddle color inner
*/
public static void quadGradHBilinear(Rect rect, RGB colorOuter, RGB colorMiddle)
{
quadCoordGradHBilinear(rect.getMin(), rect.getMax(), colorOuter, colorMiddle);
}
/**
* Render quad 2D with gradient vertical
*
* @param rect rect
* @param colorTop top color
* @param colorBottom bottom color
*/
public static void quadGradV(Rect rect, RGB colorTop, RGB colorBottom)
{
quadCoordGradV(rect.getMin(), rect.getMax(), colorTop, colorBottom);
}
/**
* Render quad 2D with gradient vertical
*
* @param rect rect
* @param colorOuter outer color
* @param colorMiddle middle color
*/
public static void quadGradVBilinear(Rect rect, RGB colorOuter, RGB colorMiddle)
{
quadCoordGradVBilinear(rect.getMin(), rect.getMax(), colorOuter, colorMiddle);
}
/**
* Render quad with coloured border with outset effect
*
* @param rect rectangle
* @param border border width
* @param fill fill color
* @param inset true for inset, false for outset
*/
public static void quadRectOutset(Rect rect, double border, RGB fill, boolean inset)
{
quadCoordOutset(rect.getMin(), rect.getMax(), border, fill, inset);
}
/**
* Render textured rect (texture must be binded already)
*
* @param quad rectangle (px)
* @param textureCoords texture coords (0-1)
*/
public static void quadTexturedAbs(Rect quad, Rect textureCoords)
{
double left = quad.x1();
double bottom = quad.y2();
double right = quad.x2();
double top = quad.y1();
double tleft = textureCoords.x1();
double tbottom = textureCoords.y1();
double tright = textureCoords.x2();
double ttop = textureCoords.y2();
glBegin(GL_QUADS);
glTexCoord2d(tleft, ttop);
glVertex2d(left, top);
glTexCoord2d(tright, ttop);
glVertex2d(right, top);
glTexCoord2d(tright, tbottom);
glVertex2d(right, bottom);
glTexCoord2d(tleft, tbottom);
glVertex2d(left, bottom);
glEnd();
}
/**
* Render textured rect
*
* @param quad rectangle (px)
* @param txCoords texture coords rectangle (px)
* @param texture texture instance
*/
public static void quadTextured(Rect quad, Rect txCoords, Texture texture)
{
quadTextured(quad, txCoords, texture, RGB.WHITE);
}
/**
* Render textured rect
*
* @param quad rectangle (px)
* @param txCoords texture coords rectangle (px)
* @param texture texture instance
* @param tint color tint
*/
public static void quadTextured(Rect quad, Rect txCoords, Texture texture, RGB tint)
{
glEnable(GL_TEXTURE_2D);
setColor(tint);
TextureManager.bind(texture);
quadTexturedAbs(quad, txCoords.div(texture.getImageHeight()));
TextureManager.unbind();
glDisable(GL_TEXTURE_2D);
}
/**
* Render textured rect
*
* @param quad rectangle (px)
* @param txquad texture quad
*/
public static void quadTextured(Rect quad, TxQuad txquad)
{
quadTextured(quad, txquad, RGB.WHITE);
}
/**
* Render textured rect
*
* @param quad rectangle (px)
* @param txquad texture instance
* @param tint color tint
*/
public static void quadTextured(Rect quad, TxQuad txquad, RGB tint)
{
quadTextured(quad, txquad.uvs, txquad.tx, tint);
}
/**
* Render textured frame with borders
*
* @param quadRect drawn rectangle (px)
* @param textureRect rectangle in texture with the basic frame (px)
* @param yOffsetTimes offset count (move frame down n times)
* @param texture the texture
*/
public static void quadTexturedFrame(Rect quadRect, Rect textureRect, int yOffsetTimes, Texture texture)
{
quadTexturedFrame(quadRect, textureRect, yOffsetTimes, texture, RGB.WHITE);
}
/**
* Render textured frame with borders
*
* @param quadRect drawn rectangle (px)
* @param textureRect rectangle in texture with the basic frame (px)
* @param yOffsetTimes offset count (move frame down n times)
* @param texture the texture
* @param tint color tint
*/
public static void quadTexturedFrame(Rect quadRect, Rect textureRect, int yOffsetTimes, Texture texture, RGB tint)
{
Texture tx = texture;
int frameHeight = (int) textureRect.getSize().y;
int yOffset = yOffsetTimes * frameHeight;
double x1 = quadRect.x1();
double y1 = quadRect.y1();
double x2 = quadRect.x2();
double y2 = quadRect.y2();
double w = x2 - x1;
double h = y2 - y1;
double tx1 = textureRect.x1();
double ty1 = textureRect.y1();
double tx2 = textureRect.x2();
double ty2 = textureRect.y2();
double halfY = h / 2D;
double halfX = w / 2D;
// lt, rt
// lb, rb
Rect verts, uvs;
// lt
verts = new Rect(x1, y2, x1 + halfX, y2 - halfY);
uvs = new Rect(tx1, ty1, tx1 + halfX, ty1 + halfY);
uvs.add_ip(0, yOffset);
quadTextured(verts, uvs, tx, tint);
// lb
verts = new Rect(x1, y2 - halfY, x1 + halfX, y1);
uvs = new Rect(tx1, ty2 - halfY, tx1 + halfX, ty2);
uvs.add_ip(0, yOffset);
quadTextured(verts, uvs, tx, tint);
// rt
verts = new Rect(x1 + halfX, y2, x2, y2 - halfY);
uvs = new Rect(tx2 - halfX, ty1, tx2, ty1 + halfY);
uvs.add_ip(0, yOffset);
quadTextured(verts, uvs, tx, tint);
// rb
verts = new Rect(x1 + halfX, y2 - halfY, x2, y1);
uvs = new Rect(tx2 - halfX, ty2 - halfY, tx2, ty2);
uvs.add_ip(0, yOffset);
quadTextured(verts, uvs, tx, tint);
}
/**
* Render textured frame with borders
*
* @param quadRect drawn rectangle (px)
* @param textureRect rectangle in texture with the basic frame (px)
* @param texture the texture
*/
public static void quadTexturedFrame(Rect quadRect, Rect textureRect, Texture texture)
{
quadTexturedFrame(quadRect, textureRect, 0, texture, RGB.WHITE);
}
/**
* Render textured frame with borders
*
* @param quadRect drawn rectangle (px)
* @param txquad texture quad
*/
public static void quadTexturedFrame(Rect quadRect, TxQuad txquad)
{
quadTexturedFrame(quadRect, txquad, 0, RGB.WHITE);
}
/**
* Render textured frame with borders
*
* @param quadRect drawn rectangle (px)
* @param txquad texture quad
* @param yOffsetTimes offset count (move frame down n times)
*/
public static void quadTexturedFrame(Rect quadRect, TxQuad txquad, int yOffsetTimes)
{
quadTexturedFrame(quadRect, txquad, yOffsetTimes, RGB.WHITE);
}
/**
* Render textured frame with borders
*
* @param quadRect drawn rectangle (px)
* @param txquad texture quad
* @param yOffsetTimes offset count (move frame down n times)
* @param tint color tint
*/
public static void quadTexturedFrame(Rect quadRect, TxQuad txquad, int yOffsetTimes, RGB tint)
{
quadTexturedFrame(quadRect, txquad.uvs, yOffsetTimes, txquad.tx, tint);
}
/**
* Render textured frame stretching horizontally (rect height = texture rect
* height)
*
* @param quadRect drawn rectangle (px)
* @param textureRect rectangle in texture with the basic frame (px)
* @param borderSize size of the unstretched horizontal border
* @param texture the texture
*/
public static void quadTexturedStretchH(Rect quadRect, Rect textureRect, int borderSize, Texture texture)
{
quadTexturedStretchH(quadRect, textureRect, borderSize, texture, RGB.WHITE);
}
/**
* Render textured frame stretching horizontally (rect height = texture rect
* height)
*
* @param quadRect drawn rectangle (px)
* @param textureRect rectangle in texture with the basic frame (px)
* @param borderSize size of the unstretched horizontal border
* @param texture the texture
* @param tint color tint
*/
public static void quadTexturedStretchH(Rect quadRect, Rect textureRect, int borderSize, Texture texture, RGB tint)
{
Texture tx = texture;
double x1 = quadRect.x1();
double y1 = quadRect.y1();
double x2 = quadRect.x2();
double y2 = quadRect.y2();
double tx1 = textureRect.x1();
double ty1 = textureRect.y1();
double tx2 = textureRect.x2();
double ty2 = textureRect.y2();
// lt, mi, rt
Rect verts, uvs;
// lt
verts = new Rect(x1, y2, x1 + borderSize, y1);
uvs = new Rect(tx1, ty1, tx1 + borderSize, ty2);
quadTextured(verts, uvs, tx, tint);
// // mi
verts = new Rect(x1 + borderSize, y2, x2 - borderSize, y1);
uvs = new Rect(tx1 + borderSize, ty1, tx2 - borderSize, ty2);
quadTextured(verts, uvs, tx, tint);
// rt
verts = new Rect(x2 - borderSize, y2, x2, y1);
uvs = new Rect(tx2 - borderSize, ty1, tx2, ty2);
quadTextured(verts, uvs, tx, tint);
}
/**
* Render textured frame stretching horizontally (rect height = texture rect
* height)
*
* @param quadRect drawn rectangle (px)
* @param txquad texture quad
* @param borderSize size of the unstretched horizontal border
*/
public static void quadTexturedStretchH(Rect quadRect, TxQuad txquad, int borderSize)
{
quadTexturedStretchH(quadRect, txquad, borderSize, RGB.WHITE);
}
/**
* Render textured frame stretching horizontally (rect height = texture rect
* height)
*
* @param quadRect drawn rectangle (px)
* @param txquad texture quad
* @param borderSize size of the unstretched horizontal border
* @param tint color tint
*/
public static void quadTexturedStretchH(Rect quadRect, TxQuad txquad, int borderSize, RGB tint)
{
quadTexturedStretchH(quadRect, txquad.uvs, borderSize, txquad.tx, tint);
}
/**
* Render textured frame stretching vertically (rect width = texture rect
* width)
*
* @param quadRect drawn rectangle (px)
* @param textureRect rectangle in texture with the basic frame (px)
* @param borderSize size of the unstretched horizontal border
* @param texture the texture
*/
public static void quadTexturedStretchV(Rect quadRect, Rect textureRect, int borderSize, Texture texture)
{
quadTexturedStretchV(quadRect, textureRect, borderSize, texture, RGB.WHITE);
}
/**
* Render textured frame stretching vertically (rect width = texture rect
* width)
*
* @param quadRect drawn rectangle (px)
* @param textureRect rectangle in texture with the basic frame (px)
* @param borderSize size of the unstretched horizontal border
* @param texture the texture
* @param tint color tint
*/
public static void quadTexturedStretchV(Rect quadRect, Rect textureRect, int borderSize, Texture texture, RGB tint)
{
Texture tx = texture;
double x1 = quadRect.x1();
double y1 = quadRect.y1();
double x2 = quadRect.x2();
double y2 = quadRect.y2();
double tx1 = textureRect.x1();
double ty1 = textureRect.y1();
double tx2 = textureRect.x2();
double ty2 = textureRect.y2();
// tp
// mi
// bn
Rect verts, uvs;
// tp
verts = new Rect(x1, y2, x2, y2 - borderSize);
uvs = new Rect(tx1, ty1, tx2, ty1 + borderSize);
quadTextured(verts, uvs, tx, tint);
// mi
verts = new Rect(x1, y2 - borderSize, x2, y1 + borderSize);
uvs = new Rect(tx1, ty1 + borderSize, tx2, ty2 - borderSize);
quadTextured(verts, uvs, tx, tint);
// rt
verts = new Rect(x1, y1 + borderSize, x2, y1);
uvs = new Rect(tx1, ty2 - borderSize, tx2, ty2);
quadTextured(verts, uvs, tx, tint);
}
/**
* Render textured frame stretching vertically (rect width = texture rect
* width)
*
* @param quadRect drawn rectangle (px)
* @param txquad texture quad
* @param borderSize size of the unstretched horizontal border
*/
public static void quadTexturedStretchV(Rect quadRect, TxQuad txquad, int borderSize)
{
quadTexturedStretchV(quadRect, txquad, borderSize, RGB.WHITE);
}
/**
* Render textured frame stretching vertically (rect width = texture rect
* width)
*
* @param quadRect drawn rectangle (px)
* @param txquad texture quad
* @param borderSize size of the unstretched horizontal border
* @param tint color tint
*/
public static void quadTexturedStretchV(Rect quadRect, TxQuad txquad, int borderSize, RGB tint)
{
quadTexturedStretchV(quadRect, txquad.uvs, borderSize, txquad.tx, tint);
}
/**
* Render quad 2D
*
* @param left units from left
* @param bottom units from bottom
* @param width quad width
* @param height quad height
*/
public static void quadSize(double left, double bottom, double width, double height)
{
glBegin(GL_QUADS);
glVertex2d(left, bottom + height);
glVertex2d(left + width, bottom + height);
glVertex2d(left + width, bottom);
glVertex2d(left, bottom);
glEnd();
}
/**
* Bind GL color
*
* @param color RGB color
*/
public static void setColor(RGB color)
{
if (color != null) glColor4d(color.r, color.g, color.b, color.a);
}
/**
* Bind GL color
*
* @param color RGB color
* @param alpha alpha multiplier
*/
public static void setColor(RGB color, double alpha)
{
if (color != null) glColor4d(color.r, color.g, color.b, color.a * alpha);
}
/**
* Translate with coord
*
* @param coord coord
*/
public static void translate(Coord coord)
{
glTranslated(coord.x, coord.y, coord.z);
}
}

@ -0,0 +1,10 @@
package mightypork.rogue.util;
/**
* Utils class
*
* @author MightyPork
*/
public class Utils {
}

@ -0,0 +1,163 @@
package mightypork.utils.files;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.zip.Adler32;
import java.util.zip.CheckedInputStream;
import java.util.zip.Checksum;
import mightypork.utils.logging.Log;
public class FileTreeDiff {
private static final byte[] BUFFER = new byte[2048];
private Checksum ck1 = new Adler32();
private Checksum ck2 = new Adler32();
private boolean logging = true;
private List<Tuple<File>> compared = new ArrayList<Tuple<File>>();
private Comparator<File> fileFirstSorter = new Comparator<File>() {
@Override
public int compare(File o1, File o2)
{
if (!o1.isDirectory() && o2.isDirectory()) return -1;
if (o1.isDirectory() && !o2.isDirectory()) return 1;
return o1.getName().compareTo(o2.getName());
}
};
public void enableLogging(boolean state)
{
logging = state;
}
public boolean areEqual(File dir1, File dir2)
{
if (logging) Log.f3("Comparing directory trees:\n 1. " + dir1 + "\n 2. " + dir2);
try {
compared.clear();
buildList(dir1, dir2);
calcChecksum();
if (logging) Log.f3("No difference found.");
return true;
} catch (NotEqualException e) {
if (logging) Log.f3("Difference found:\n" + e.getMessage());
return false;
}
}
private void calcChecksum() throws NotEqualException
{
FileInputStream in1, in2;
CheckedInputStream cin1 = null, cin2 = null;
for (Tuple<File> pair : compared) {
try {
ck1.reset();
ck2.reset();
in1 = new FileInputStream(pair.a);
in2 = new FileInputStream(pair.b);
cin1 = new CheckedInputStream(in1, ck1);
cin2 = new CheckedInputStream(in2, ck2);
while (true) {
int read1 = cin1.read(BUFFER);
int read2 = cin2.read(BUFFER);
if (read1 != read2 || ck1.getValue() != ck2.getValue()) {
throw new NotEqualException("Bytes differ:\n" + pair.a + "\n" + pair.b);
}
if (read1 == -1) break;
}
} catch (IOException e) {} finally {
try {
if (cin1 != null) cin1.close();
} catch (IOException e) {
// ignore
}
try {
if (cin1 != null) cin1.close();
} catch (IOException e) {
// ignore
}
}
}
}
private void buildList(File f1, File f2) throws NotEqualException
{
if (f1.isDirectory() != f2.isDirectory()) throw new NotEqualException("isDirectory differs:\n" + f1 + "\n" + f2);
if (f1.isFile() && f2.isFile()) {
if (f1.length() != f2.length()) throw new NotEqualException("Sizes differ:\n" + f1 + "\n" + f2);
}
if (f1.isDirectory()) {
File[] children1 = f1.listFiles();
File[] children2 = f2.listFiles();
Arrays.sort(children1, fileFirstSorter);
Arrays.sort(children2, fileFirstSorter);
if (children1.length != children2.length) throw new NotEqualException("Child counts differ:\n" + f1 + "\n" + f2);
for (int i = 0; i < children1.length; i++) {
File ch1 = children1[i];
File ch2 = children2[i];
if (!ch1.getName().equals(ch2.getName())) throw new NotEqualException("Filenames differ:\n" + ch1 + "\n" + ch2);
buildList(ch1, ch2);
}
} else {
compared.add(new Tuple<File>(f1, f2));
}
}
private class NotEqualException extends Exception {
public NotEqualException(String msg) {
super(msg);
}
}
private class Tuple<T> {
public T a;
public T b;
public Tuple(T a, T b) {
this.a = a;
this.b = b;
}
}
}

@ -0,0 +1,527 @@
package mightypork.utils.files;
import java.io.*;
import java.util.ArrayList;
import java.util.List;
import mightypork.utils.logging.Log;
import mightypork.utils.string.StringUtils;
import mightypork.utils.string.validation.StringFilter;
public class FileUtils {
/**
* Copy directory recursively.
*
* @param source source file
* @param target target file
* @throws IOException on error
*/
public static void copyDirectory(File source, File target) throws IOException
{
copyDirectory(source, target, null, null);
}
/**
* Copy directory recursively - advanced variant.
*
* @param source source file
* @param target target file
* @param filter filter accepting only files and dirs to be copied
* @param filesCopied list into which all the target files will be added
* @throws IOException on error
*/
public static void copyDirectory(File source, File target, FileFilter filter, List<File> filesCopied) throws IOException
{
if (!source.exists()) return;
if (source.isDirectory()) {
if (!target.exists()) {
target.mkdir();
}
String[] children = source.list();
for (int i = 0; i < children.length; i++) {
copyDirectory(new File(source, children[i]), new File(target, children[i]), filter, filesCopied);
}
} else {
if (filter != null && !filter.accept(source)) {
return;
}
if (filesCopied != null) filesCopied.add(target);
copyFile(source, target);
}
}
/**
* List directory recursively
*
* @param source source file
* @param filter filter accepting only files and dirs to be copied (or null)
* @param files list of the found files
* @throws IOException on error
*/
public static void listDirectoryRecursive(File source, StringFilter filter, List<File> files) throws IOException
{
if (source.isDirectory()) {
String[] children = source.list();
for (int i = 0; i < children.length; i++) {
listDirectoryRecursive(new File(source, children[i]), filter, files);
}
} else {
if (filter != null && !filter.accept(source.getAbsolutePath())) {
return;
}
files.add(source);
}
}
/**
* Copy file using streams. Make sure target directory exists!
*
* @param source source file
* @param target target file
* @throws IOException on error
*/
public static void copyFile(File source, File target) throws IOException
{
InputStream in = null;
OutputStream out = null;
try {
in = new FileInputStream(source);
out = new FileOutputStream(target);
copyStream(in, out);
} finally {
try {
if (in != null) in.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (out != null) out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* Copy bytes from input to output stream, leaving out stream open
*
* @param in input stream
* @param out output stream
* @throws IOException on error
*/
public static void copyStream(InputStream in, OutputStream out) throws IOException
{
if (in == null) {
throw new NullPointerException("Input stream is null");
}
if (out == null) {
throw new NullPointerException("Output stream is null");
}
byte[] buf = new byte[2048];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
}
/**
* Improved delete
*
* @param path deleted path
* @param recursive recursive delete
* @return success
*/
public static boolean delete(File path, boolean recursive)
{
if (!path.exists()) {
return true;
}
if (!recursive || !path.isDirectory()) return path.delete();
String[] list = path.list();
for (int i = 0; i < list.length; i++) {
if (!delete(new File(path, list[i]), true)) return false;
}
return path.delete();
}
/**
* Read entire file to a string.
*
* @param file file
* @return file contents
* @throws IOException
*/
public static String fileToString(File file) throws IOException
{
FileInputStream fin = new FileInputStream(file);
return streamToString(fin);
}
/**
* Get files in a folder (create folder if needed)
*
* @param dir folder
* @return list of files
*/
public static List<File> listDirectory(File dir)
{
return FileUtils.listDirectory(dir, null);
}
/**
* Get files in a folder (create folder if needed)
*
* @param dir folder
* @param filter file filter
* @return list of files
*/
public static List<File> listDirectory(File dir, FileFilter filter)
{
try {
dir.mkdir();
} catch (RuntimeException e) {
Log.e("Error creating folder " + dir, e);
}
List<File> list = new ArrayList<File>();
try {
for (File f : dir.listFiles(filter)) {
list.add(f);
}
} catch (Exception e) {
Log.e("Error listing folder " + dir, e);
}
return list;
}
/**
* Remove extension.
*
* @param file file
* @return filename without extension
*/
public static String[] getFilenameParts(File file)
{
return getFilenameParts(file.getName());
}
public static String getExtension(File file)
{
return getExtension(file.getName());
}
public static String getExtension(String file)
{
return StringUtils.fromLastChar(file, '.');
}
/**
* Remove extension.
*
* @param filename
* @return filename and extension
*/
public static String[] getFilenameParts(String filename)
{
String ext, name;
try {
ext = StringUtils.fromLastDot(filename);
} catch (StringIndexOutOfBoundsException e) {
ext = "";
}
try {
name = StringUtils.toLastDot(filename);
} catch (StringIndexOutOfBoundsException e) {
name = "";
Log.w("Error extracting extension from file " + filename);
}
return new String[] { name, ext };
}
/**
* Read entire input stream to a string, and close it.
*
* @param in input stream
* @return file contents
*/
public static String streamToString(InputStream in)
{
return streamToString(in, -1);
}
/**
* Read input stream to a string, and close it.
*
* @param in input stream
* @param lines max number of lines (-1 to disable limit)
* @return file contents
*/
public static String streamToString(InputStream in, int lines)
{
if (in == null) {
Log.e(new NullPointerException("Null stream to be converted to String."));
return ""; // to avoid NPE's
}
BufferedReader br = null;
StringBuilder sb = new StringBuilder();
String line;
try {
int cnt = 0;
br = new BufferedReader(new InputStreamReader(in, "UTF-8"));
while ((line = br.readLine()) != null && (cnt < lines || lines <= 0)) {
sb.append(line + "\n");
cnt++;
}
if (cnt == lines && lines > 0) {
sb.append("--- end of preview ---\n");
}
} catch (IOException e) {
Log.e(e);
} finally {
try {
if (br != null) br.close();
} catch (IOException e) {
// ignore
}
}
return sb.toString();
}
public static InputStream stringToStream(String text)
{
if (text == null) return null;
try {
return new ByteArrayInputStream(text.getBytes("UTF-8"));
} catch (UnsupportedEncodingException e) {
Log.e(e);
return null;
}
}
public static InputStream getResource(String path)
{
return FileUtils.class.getResourceAsStream(path);
}
public static String getResourceAsString(String path)
{
return streamToString(FileUtils.class.getResourceAsStream(path));
}
/**
* Save string to file
*
* @param file file
* @param text string
* @throws IOException on error
*/
public static void stringToFile(File file, String text) throws IOException
{
PrintStream out = null;
try {
out = new PrintStream(new FileOutputStream(file), false, "UTF-8");
out.print(text);
out.flush();
} finally {
if (out != null) out.close();
}
}
public static void deleteEmptyDirs(File base)
{
for (File f : listDirectory(base)) {
if (!f.isDirectory()) continue;
deleteEmptyDirs(f);
List<File> children = listDirectory(f);
if (children.size() == 0) {
f.delete();
continue;
}
}
}
/**
* Replace special characters with place holders in a filename.
*
* @param filestring filename string
* @return escaped
*/
public static String escapeFileString(String filestring)
{
StringBuilder sb = new StringBuilder();
for (char c : filestring.toCharArray()) {
switch (c) {
case '%':
sb.append("%%");
break;
case '.':
sb.append("%d");
break;
default:
sb.append(c);
}
}
return sb.toString();
}
/**
* Unescape filename string obtained by escapeFileString().
*
* @param filestring escaped string
* @return clean string
*/
public static String unescapeFileString(String filestring)
{
filestring = filestring.replace("%d", ".");
filestring = filestring.replace("%%", "%");
return filestring;
}
/**
* Escape filename, keeping the same extension
*
* @param filename filename
* @return escaped
*/
public static String escapeFilename(String filename)
{
String[] parts = getFilenameParts(filename);
return escapeFileString(parts[0]) + "." + parts[1];
}
/**
* Unescape filename, keeping the same extension
*
* @param filename escaped filename
* @return unesaped
*/
public static String unescapeFilename(String filename)
{
String[] parts = getFilenameParts(filename);
return unescapeFileString(parts[0]) + "." + parts[1];
}
public static String getBasename(String name)
{
return StringUtils.toLastChar(StringUtils.fromLastChar(name, '/'), '.');
}
public static String getFilename(String name)
{
return StringUtils.fromLastChar(name, '/');
}
/**
* Copy resource to file
*
* @param resname resource name
* @param file out file
* @throws IOException
*/
public static void resourceToFile(String resname, File file) throws IOException
{
InputStream in = null;
OutputStream out = null;
try {
in = FileUtils.getResource(resname);
out = new FileOutputStream(file);
FileUtils.copyStream(in, out);
} finally {
try {
if (in != null) in.close();
} catch (IOException e) {
// ignore
}
try {
if (out != null) out.close();
} catch (IOException e) {
// ignore
}
}
}
/**
* Get resource as string, safely closing streams.
*
* @param resname resource name
* @return resource as string, empty string on failure
*/
public static String resourceToString(String resname)
{
InputStream in = FileUtils.getResource(resname);
return streamToString(in);
}
}

@ -0,0 +1,149 @@
package mightypork.utils.files;
import java.io.File;
public class OsUtils {
public static enum EnumOS
{
linux, macos, solaris, unknown, windows;
public boolean isLinux()
{
return this == linux || this == solaris;
}
public boolean isMac()
{
return this == macos;
}
public boolean isWindows()
{
return this == windows;
}
}
private static EnumOS cachedOs;
/**
* Get App dir, ensure it exists
*
* @param dirname
* @return app dir
*/
public static File getWorkDir(String dirname)
{
return getWorkDir(dirname, true);
}
/**
* Get App sub-folder
*
* @param dirname
* @param subfolderName
* @param create
* @return the folder
*/
public static File getWorkDir(String dirname, String subfolderName, boolean create)
{
File f = new File(getWorkDir(dirname), subfolderName);
if (!f.exists() && create) {
if (!f.mkdirs()) {
throw new RuntimeException("Could not create.");
}
}
return f;
}
/**
* Get App sub-folder, create
*
* @param dirname
* @param subfolderName
* @return the folder
*/
public static File getWorkDir(String dirname, String subfolderName)
{
return getWorkDir(dirname, subfolderName, true);
}
public static EnumOS getOs()
{
if (cachedOs != null) return cachedOs;
String s = System.getProperty("os.name").toLowerCase();
if (s.contains("win")) {
cachedOs = EnumOS.windows;
} else if (s.contains("mac")) {
cachedOs = EnumOS.macos;
} else if (s.contains("linux") || s.contains("unix")) {
cachedOs = EnumOS.linux;
} else if (s.contains("solaris") || s.contains("sunos")) {
cachedOs = EnumOS.solaris;
} else {
cachedOs = EnumOS.unknown;
}
return cachedOs;
}
private static File getWorkDir(String dirname, boolean create)
{
String userhome = System.getProperty("user.home", ".");
File file;
switch (getOs()) {
case linux:
case solaris:
file = new File(userhome, "." + dirname + '/');
break;
case windows:
String appdata = System.getenv("APPDATA");
if (appdata != null) {
file = new File(appdata, "." + dirname + '/');
} else {
file = new File(userhome, "." + dirname + '/');
}
break;
case macos:
file = new File(userhome, "Library/Application Support/" + dirname);
break;
default:
file = new File(userhome, dirname + "/");
break;
}
if (!file.exists() || !file.isDirectory()) {
if (create) {
if (!file.mkdirs()) {
throw new RuntimeException("Could not create working directory.");
}
}
}
return file;
}
}

@ -0,0 +1,963 @@
package mightypork.utils.files;
import java.io.*;
import java.util.*;
import java.util.Map.Entry;
import mightypork.utils.math.Calc;
/**
* Property manager with advanced formatting and value checking.<br>
* Methods starting with put are for filling. Most of the others are shortcuts
* to getters.
*
* @author MightyPork
*/
public class PropertyManager {
/**
* Properties stored in file, alphabetically sorted.<br>
* Property file is much cleaner than the normal java.util.Properties,
* newlines can be inserted to separate categories, and individual keys can
* have their own inline comments.
*
* @author MightyPork
*/
private static class SortedProperties extends Properties {
/** A table of hex digits */
private static final char[] hexChars = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
/**
* this is here because the original method is private.
*
* @param nibble
* @return hex char.
*/
private static char toHex(int nibble)
{
return hexChars[(nibble & 0xF)];
}
private static void writeComments(BufferedWriter bw, String comm) throws IOException
{
String comments = comm.replace("\n\n", "\n \n");
int len = comments.length();
int current = 0;
int last = 0;
char[] uu = new char[6];
uu[0] = '\\';
uu[1] = 'u';
while (current < len) {
char c = comments.charAt(current);
if (c > '\u00ff' || c == '\n' || c == '\r') {
if (last != current) {
bw.write("# " + comments.substring(last, current));
}
if (c > '\u00ff') {
uu[2] = toHex((c >> 12) & 0xf);
uu[3] = toHex((c >> 8) & 0xf);
uu[4] = toHex((c >> 4) & 0xf);
uu[5] = toHex(c & 0xf);
bw.write(new String(uu));
} else {
bw.newLine();
if (c == '\r' && current != len - 1 && comments.charAt(current + 1) == '\n') {
current++;
}
}
last = current + 1;
}
current++;
}
if (last != current) {
bw.write("# " + comments.substring(last, current));
}
bw.newLine();
bw.newLine();
bw.newLine();
}
/** Option: put empty line before each comment. */
public boolean cfgEmptyLineBeforeComment = true;
/**
* Option: Separate sections by newline<br>
* Section = string before first dot in key.
*/
public boolean cfgSeparateSectionsByEmptyLine = true;
private boolean firstEntry = true;
/** Comments for individual keys */
private Hashtable<String, String> keyComments = new Hashtable<String, String>();
private String lastSectionBeginning = "";
@SuppressWarnings({ "unchecked", "rawtypes" })
@Override
public synchronized Enumeration keys()
{
Enumeration keysEnum = super.keys();
Vector keyList = new Vector();
while (keysEnum.hasMoreElements()) {
keyList.add(keysEnum.nextElement());
}
Collections.sort(keyList);
return keyList.elements();
}
private String saveConvert(String theString, boolean escapeSpace, boolean escapeUnicode)
{
int len = theString.length();
int bufLen = len * 2;
if (bufLen < 0) {
bufLen = Integer.MAX_VALUE;
}
StringBuffer outBuffer = new StringBuffer(bufLen);
for (int x = 0; x < len; x++) {
char aChar = theString.charAt(x);
// Handle common case first, selecting largest block that
// avoids the specials below
if ((aChar > 61) && (aChar < 127)) {
if (aChar == '\\') {
outBuffer.append('\\');
outBuffer.append('\\');
continue;
}
outBuffer.append(aChar);
continue;
}
switch (aChar) {
case ' ':
if (x == 0 || escapeSpace) {
outBuffer.append('\\');
}
outBuffer.append(' ');
break;
case '\t':
outBuffer.append('\\');
outBuffer.append('t');
break;
case '\n':
outBuffer.append('\\');
outBuffer.append('n');
break;
case '\r':
outBuffer.append('\\');
outBuffer.append('r');
break;
case '\f':
outBuffer.append('\\');
outBuffer.append('f');
break;
case '=': // Fall through
case ':': // Fall through
case '#': // Fall through
case '!':
outBuffer.append('\\');
outBuffer.append(aChar);
break;
default:
if (((aChar < 0x0020) || (aChar > 0x007e)) & escapeUnicode) {
outBuffer.append('\\');
outBuffer.append('u');
outBuffer.append(toHex((aChar >> 12) & 0xF));
outBuffer.append(toHex((aChar >> 8) & 0xF));
outBuffer.append(toHex((aChar >> 4) & 0xF));
outBuffer.append(toHex(aChar & 0xF));
} else {
outBuffer.append(aChar);
}
}
}
return outBuffer.toString();
}
/**
* Set additional comment to a key
*
* @param key key for comment
* @param comment the comment
*/
public void setKeyComment(String key, String comment)
{
keyComments.put(key, comment);
}
@SuppressWarnings("rawtypes")
@Override
public void store(OutputStream out, String comments) throws IOException
{
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(out, "UTF-8"));
boolean escUnicode = false;
if (comments != null) {
writeComments(bw, comments);
}
synchronized (this) {
for (Enumeration e = keys(); e.hasMoreElements();) {
boolean wasNewLine = false;
String key = (String) e.nextElement();
String val = (String) get(key);
key = saveConvert(key, true, escUnicode);
val = saveConvert(val, false, escUnicode);
if (cfgSeparateSectionsByEmptyLine && !lastSectionBeginning.equals(key.split("[.]")[0])) {
if (!firstEntry) {
bw.newLine();
bw.newLine();
}
wasNewLine = true;
lastSectionBeginning = key.split("[.]")[0];
}
if (keyComments.containsKey(key)) {
String cm = keyComments.get(key);
cm = cm.replace("\r", "\n");
cm = cm.replace("\r\n", "\n");
cm = cm.replace("\n\n", "\n \n");
String[] cmlines = cm.split("\n");
if (!wasNewLine && !firstEntry && cfgEmptyLineBeforeComment) {
bw.newLine();
}
for (String cmline : cmlines) {
bw.write("# " + cmline);
bw.newLine();
}
}
bw.write(key + " = " + val);
bw.newLine();
firstEntry = false;
}
}
bw.flush();
}
}
/**
* Helper class which loads Properties from UTF-8 file (Properties use
* "ISO-8859-1" by default)
*
* @author Itay Maman
*/
private static class PropertiesLoader {
private static String escapifyStr(String str)
{
StringBuilder result = new StringBuilder();
int len = str.length();
for (int x = 0; x < len; x++) {
char ch = str.charAt(x);
if (ch <= 0x007e) {
result.append(ch);
continue;
}
result.append('\\');
result.append('u');
result.append(hexDigit(ch, 12));
result.append(hexDigit(ch, 8));
result.append(hexDigit(ch, 4));
result.append(hexDigit(ch, 0));
}
return result.toString();
}
private static char hexDigit(char ch, int offset)
{
int val = (ch >> offset) & 0xF;
if (val <= 9) {
return (char) ('0' + val);
}
return (char) ('A' + val - 10);
}
public static SortedProperties loadProperties(SortedProperties props, InputStream is) throws IOException
{
return loadProperties(props, is, "utf-8");
}
public static SortedProperties loadProperties(SortedProperties props, InputStream is, String encoding) throws IOException
{
StringBuilder sb = new StringBuilder();
InputStreamReader isr = new InputStreamReader(is, encoding);
while (true) {
int temp = isr.read();
if (temp < 0) {
break;
}
char c = (char) temp;
sb.append(c);
}
String read = sb.toString();
String inputString = escapifyStr(read);
byte[] bs = inputString.getBytes("ISO-8859-1");
ByteArrayInputStream bais = new ByteArrayInputStream(bs);
SortedProperties ps = props;
ps.load(bais);
return ps;
}
}
/**
* Property entry in Property manager.
*
* @author MightyPork
*/
private class Property {
public String entryComment;
public String name;
public boolean bool = false;
public boolean defbool = false;
public double num = -1;
public double defnum = -1;
public String defstr = "";
public String str = "";
public PropertyType type;
/**
* Property
*
* @param key key
* @param default_value default value
* @param entry_type type
* @param entry_comment entry comment
*/
public Property(String key, boolean default_value, PropertyType entry_type, String entry_comment) {
name = key;
defbool = default_value;
type = entry_type;
entryComment = entry_comment;
}
/**
* Property entry
*
* @param key property key
* @param default_value default value
* @param entry_type property type from enum
* @param entry_comment property comment or null
*/
public Property(String key, double default_value, PropertyType entry_type, String entry_comment) {
name = key;
defnum = default_value;
type = entry_type;
entryComment = entry_comment;
}
/**
* Property
*
* @param key key
* @param default_value default value
* @param entry_type type
* @param entry_comment entry comment
*/
public Property(String key, String default_value, PropertyType entry_type, String entry_comment) {
name = key;
defstr = default_value;
type = entry_type;
entryComment = entry_comment;
}
/**
* Get boolean
*
* @return the boolean
*/
public boolean getBoolean()
{
return bool;
}
/**
* Get number
*
* @return the number
*/
public int getInteger()
{
return (int) Math.round(num);
}
/**
* Get number as double
*
* @return the number
*/
public double getDouble()
{
return num;
}
/**
* Get string
*
* @return the string
*/
public String getString()
{
return str;
}
/**
* Is this entry valid?
*
* @return is valid
*/
public boolean isValid()
{
switch (type) {
case STRING:
return str != null;
case BOOLEAN:
case INT:
case DOUBLE:
return true;
}
return false;
}
/**
* Load property value from a file
*
* @param string the string loaded
* @return was OK
*/
public boolean parse(String string)
{
switch (type) {
case INT:
if (string == null) {
num = defnum;
return false;
}
try {
num = Integer.parseInt(string.trim());
} catch (NumberFormatException e) {
num = defnum;
}
break;
case DOUBLE:
if (string == null) {
num = defnum;
return false;
}
try {
num = Double.parseDouble(string.trim());
} catch (NumberFormatException e) {
num = defnum;
}
break;
case STRING:
if (string == null) {
str = defstr;
return false;
}
str = string;
break;
case BOOLEAN:
if (string == null) {
bool = defbool;
return false;
}
String string2 = string.toLowerCase();
bool = string2.equals("yes") || string2.equals("true") || string2.equals("on") || string2.equals("enabled") || string2.equals("enable");
}
return true;
}
/**
* prepare the contents for insertion into Properties
*
* @return the string prepared, or null if type is invalid
*/
@Override
public String toString()
{
if (!isValid()) {
if (type == PropertyType.INT || type == PropertyType.DOUBLE) {
num = defnum;
}
}
switch (type) {
case INT:
return Integer.toString((int) num);
case DOUBLE:
return Calc.floatToString((float) num);
case STRING:
return str;
case BOOLEAN:
return bool ? "True" : "False";
}
return null;
}
/**
* If this entry is not valid, change it to the dafault value.
*/
public void validate()
{
if (!isValid()) {
if (type == PropertyType.STRING) {
str = defstr;
}
}
}
}
/**
* Property types
*/
private enum PropertyType
{
BOOLEAN, INT, STRING, DOUBLE;
}
/** put newline before entry comments */
private boolean cfgNewlineBeforeComments = true;
/** Disable entry validation */
private boolean cfgNoValidate = true;
/** Put newline between sections. */
private boolean cfgSeparateSections = true;
/** Force save, even if nothing changed (used to save changed comments) */
private boolean cfgForceSave;
private File file;
private String fileComment = "";
private TreeMap<String, Property> entries;
private TreeMap<String, String> keyRename;
private TreeMap<String, String> setValues;
private SortedProperties pr = new SortedProperties();
/**
* Create property manager from file path and an initial comment.
*
* @param file file with the props
* @param comment the initial comment. Use \n in it if you want.
*/
public PropertyManager(File file, String comment) {
this.file = file;
this.entries = new TreeMap<String, Property>();
this.setValues = new TreeMap<String, String>();
this.keyRename = new TreeMap<String, String>();
this.fileComment = comment;
}
/**
* Load, fix and write to file.
*/
public void apply()
{
boolean needsSave = false;
FileInputStream fis = null;
try {
new File(file.getParent()).mkdirs();
fis = new FileInputStream(file);
pr = PropertiesLoader.loadProperties(pr, fis);
} catch (IOException e) {
needsSave = true;
pr = new SortedProperties();
} finally {
try {
if (fis != null) fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
pr.cfgSeparateSectionsByEmptyLine = cfgSeparateSections;
pr.cfgEmptyLineBeforeComment = cfgNewlineBeforeComments;
ArrayList<String> keyList = new ArrayList<String>();
// rename keys
for (Entry<String, String> entry : keyRename.entrySet()) {
if (pr.getProperty(entry.getKey()) == null) {
continue;
}
pr.setProperty(entry.getValue(), pr.getProperty(entry.getKey()));
pr.remove(entry.getKey());
needsSave = true;
}
// set the override values into the freshly loaded properties file
for (Entry<String, String> entry : setValues.entrySet()) {
pr.setProperty(entry.getKey(), entry.getValue());
needsSave = true;
}
// validate entries one by one, replace with default when needed
for (Property entry : entries.values()) {
keyList.add(entry.name);
String propOrig = pr.getProperty(entry.name);
if (!entry.parse(propOrig)) needsSave = true;
if (!cfgNoValidate) {
entry.validate();
}
if (entry.entryComment != null) {
pr.setKeyComment(entry.name, entry.entryComment);
}
if (propOrig == null || !entry.toString().equals(propOrig)) {
pr.setProperty(entry.name, entry.toString());
needsSave = true;
}
}
// removed unused props
for (String propname : pr.keySet().toArray(new String[pr.size()])) {
if (!keyList.contains(propname)) {
pr.remove(propname);
needsSave = true;
}
}
// save if needed
if (needsSave || cfgForceSave) {
try {
pr.store(new FileOutputStream(file), fileComment);
} catch (IOException ioe) {
ioe.printStackTrace();
}
}
setValues.clear();
keyRename.clear();
}
/**
* @param newlineBeforeComments put newline before comments
*/
public void cfgNewlineBeforeComments(boolean newlineBeforeComments)
{
this.cfgNewlineBeforeComments = newlineBeforeComments;
}
/**
* @param separateSections do separate sections by newline
*/
public void cfgSeparateSections(boolean separateSections)
{
this.cfgSeparateSections = separateSections;
}
/**
* @param forceSave save even if unchanged.
*/
public void cfgForceSave(boolean forceSave)
{
this.cfgForceSave = forceSave;
}
/**
* @param validate enable validation
*/
public void enableValidation(boolean validate)
{
this.cfgNoValidate = !validate;
}
/**
* Get a property entry (rarely used)
*
* @param n key
* @return the entry
*/
private Property get(String n)
{
try {
return entries.get(n);
} catch (Throwable t) {
return null;
}
}
/**
* Get boolean property
*
* @param n key
* @return the boolean found, or false
*/
public Boolean getBoolean(String n)
{
try {
return entries.get(n).getBoolean();
} catch (Throwable t) {
return false;
}
}
/**
* Get numeric property
*
* @param n key
* @return the int found, or null
*/
public Integer getInteger(String n)
{
try {
return get(n).getInteger();
} catch (Throwable t) {
return -1;
}
}
/**
* Get numeric property as double
*
* @param n key
* @return the double found, or null
*/
public Double getDouble(String n)
{
try {
return get(n).getDouble();
} catch (Throwable t) {
return -1D;
}
}
/**
* Get string property
*
* @param n key
* @return the string found, or null
*/
public String getString(String n)
{
try {
return get(n).getString();
} catch (Throwable t) {
return null;
}
}
/**
* Add a boolean property
*
* @param n key
* @param d default value
*/
public void putBoolean(String n, boolean d)
{
entries.put(n, new Property(n, d, PropertyType.BOOLEAN, null));
return;
}
/**
* Add a boolean property
*
* @param n key
* @param d default value
* @param comment the in-file comment
*/
public void putBoolean(String n, boolean d, String comment)
{
entries.put(n, new Property(n, d, PropertyType.BOOLEAN, comment));
return;
}
/**
* Add a numeric property (double)
*
* @param n key
* @param d default value
*/
public void putDouble(String n, int d)
{
entries.put(n, new Property(n, d, PropertyType.DOUBLE, null));
return;
}
/**
* Add a numeric property (double)
*
* @param n key
* @param d default value
* @param comment the in-file comment
*/
public void putDouble(String n, int d, String comment)
{
entries.put(n, new Property(n, d, PropertyType.DOUBLE, comment));
return;
}
/**
* Add a numeric property
*
* @param n key
* @param d default value
*/
public void putInteger(String n, int d)
{
entries.put(n, new Property(n, d, PropertyType.INT, null));
return;
}
/**
* Add a numeric property
*
* @param n key
* @param d default value
* @param comment the in-file comment
*/
public void putInteger(String n, int d, String comment)
{
entries.put(n, new Property(n, d, PropertyType.INT, comment));
return;
}
/**
* Add a string property
*
* @param n key
* @param d default value
*/
public void putString(String n, String d)
{
entries.put(n, new Property(n, d, PropertyType.STRING, null));
return;
}
/**
* Add a string property
*
* @param n key
* @param d default value
* @param comment the in-file comment
*/
public void putString(String n, String d, String comment)
{
entries.put(n, new Property(n, d, PropertyType.STRING, comment));
return;
}
/**
* Rename key before doing "apply"; value is preserved
*
* @param oldKey old key
* @param newKey new key
*/
public void renameKey(String oldKey, String newKey)
{
keyRename.put(oldKey, newKey);
return;
}
/**
* Set value saved to certain key; use to save runtime-changed configuration
* values.
*
* @param key key
* @param value the saved value
*/
public void setValue(String key, Object value)
{
setValues.put(key, value.toString());
return;
}
}

@ -0,0 +1,204 @@
package mightypork.utils.files;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import mightypork.utils.logging.Log;
/**
* Utility for parsing simple config files<br>
* # and // mark a comment<br>
* empty lines and lines without "=" are ignored<br>
* lines with "=" must have "key = value" format, or a warning is logged.<br>
* use "NULL" to create empty value.
*
* @author MightyPork
*/
public class SimpleConfig {
/**
* Load list from file
*
* @param file file
* @return map of keys and values
* @throws IOException
*/
public static List<String> listFromFile(File file) throws IOException
{
String fileText = FileUtils.fileToString(file);
return listFromString(fileText);
}
/**
* Load map from file
*
* @param file file
* @return map of keys and values
* @throws IOException
*/
public static Map<String, String> mapFromFile(File file) throws IOException
{
String fileText = FileUtils.fileToString(file);
return mapFromString(fileText);
}
/**
* Load list from string
*
* @param text text of the file
* @return map of keys and values
*/
public static List<String> listFromString(String text)
{
List<String> list = new ArrayList<String>();
String[] groupsLines = text.split("\n");
for (String s : groupsLines) {
// ignore invalid lines
if (s.length() == 0) continue;
if (s.startsWith("#") || s.startsWith("//")) continue;
// NULL value
if (s.equalsIgnoreCase("NULL")) s = null;
if (s != null) s = s.replace("\\n", "\n");
// save extracted key-value pair
list.add(s);
}
return list;
}
/**
* Load map from string
*
* @param text text of the file
* @return map of keys and values
*/
public static Map<String, String> mapFromString(String text)
{
LinkedHashMap<String, String> pairs = new LinkedHashMap<String, String>();
String[] groupsLines = text.split("\n");
for (String s : groupsLines) {
// ignore invalid lines
if (s.length() == 0) continue;
if (s.startsWith("#") || s.startsWith("//")) continue;
if (!s.contains("=")) continue;
// split and trim
String[] parts = s.split("=");
for (int i = 0; i < parts.length; i++) {
parts[i] = parts[i].trim();
}
// check if both parts are valid
if (parts.length == 0) {
Log.w("Bad line in config file: " + s);
continue;
}
if (parts.length == 1) {
parts = new String[] { parts[0], "" };
}
if (parts.length != 2) {
Log.w("Bad line in config file: " + s);
continue;
}
// NULL value
if (parts[0].equalsIgnoreCase("NULL")) parts[0] = null;
if (parts[1].equalsIgnoreCase("NULL")) parts[1] = null;
if (parts[0] != null) parts[0] = parts[0].replace("\\n", "\n");
if (parts[1] != null) parts[1] = parts[1].replace("\\n", "\n");
// save extracted key-value pair
pairs.put(parts[0], parts[1]);
}
return pairs;
}
/**
* Save map to file
*
* @param target
* @param data
* @param allowNulls allow nulls.
* @throws IOException
*/
public static void mapToFile(File target, Map<String, String> data, boolean allowNulls) throws IOException
{
List<String> lines = new ArrayList<String>();
for (Entry<String, String> e : data.entrySet()) {
String key = e.getKey();
String value = e.getValue();
if (!allowNulls && (key == null || value == null || key.length() == 0 || value.length() == 0)) continue;
if (key == null) key = "NULL";
if (value == null) value = "NULL";
key = key.replace("\n", "\\n");
value = value.replace("\n", "\\n");
lines.add(key + " = " + value);
}
String text = ""; //# File written by SimpleConfig
for (String s : lines) {
if (text.length() > 0) text += "\n";
text += s;
}
FileUtils.stringToFile(target, text);
}
/**
* Save list to file
*
* @param target
* @param data
* @throws IOException
*/
public static void listToFile(File target, List<String> data) throws IOException
{
String text = ""; //# File written by SimpleConfig
for (String s : data) {
if (text.length() > 0) text += "\n";
if (s == null) s = "NULL";
s = s.replace("\n", "\\n");
text += s;
}
FileUtils.stringToFile(target, text);
}
}

@ -0,0 +1,123 @@
package mightypork.utils.files;
import java.io.*;
import java.util.HashSet;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import mightypork.utils.logging.Log;
/**
* Class for building a zip file
*
* @author MightyPork
*/
public class ZipBuilder {
private ZipOutputStream out;
private HashSet<String> included = new HashSet<String>();
/**
* @param target target zip file
* @throws FileNotFoundException if the file is directory or cannot be
* created
*/
public ZipBuilder(File target) throws FileNotFoundException {
target.getParentFile().mkdirs();
FileOutputStream dest = new FileOutputStream(target);
out = new ZipOutputStream(new BufferedOutputStream(dest));
}
/**
* Add stream to a path
*
* @param path path
* @param in stream
* @throws IOException
*/
public void addStream(String path, InputStream in) throws IOException
{
path = preparePath(path);
if (included.contains(path)) {
Log.f3("Zip already contains file " + path + ", skipping.");
return; // ignore
}
included.add(path);
out.putNextEntry(new ZipEntry(path));
FileUtils.copyStream(in, out);
}
/**
* Add string as a file
*
* @param path path
* @param text text to write
* @throws IOException
*/
public void addString(String path, String text) throws IOException
{
path = preparePath(path);
if (included.contains(path)) return; // ignore
included.add(path);
out.putNextEntry(new ZipEntry(path));
InputStream in = FileUtils.stringToStream(text);
FileUtils.copyStream(in, out);
}
/**
* Add resource obtained via FileUtils.getResource()
*
* @param path path
* @param resPath resource path
* @throws IOException
*/
public void addResource(String path, String resPath) throws IOException
{
path = preparePath(path);
if (included.contains(path)) return; // ignore
included.add(path);
out.putNextEntry(new ZipEntry(path));
InputStream in = FileUtils.getResource(resPath);
FileUtils.copyStream(in, out);
}
/**
* Normalize path
*
* @param path original path
* @return normalized path
*/
private String preparePath(String path)
{
path = path.replace("\\", "/");
if (path.charAt(0) == '/') path = path.substring(1);
return path;
}
/**
* Close the zip stream
*
* @throws IOException
*/
public void close() throws IOException
{
out.close();
}
}

@ -0,0 +1,227 @@
package mightypork.utils.files;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import mightypork.utils.string.validation.StringFilter;
/**
* Utilities for manipulating zip files
*
* @author MightyPork
*/
public class ZipUtils {
private static final int BUFFER_SIZE = 2048;
/**
* Extract zip file to target directory
*
* @param file zip file
* @param outputDir target directory
* @param filter string filter (will be used to test entry names (paths))
* @return list of entries extracted (paths)
* @throws IOException
*/
public static List<String> extractZip(File file, File outputDir, StringFilter filter) throws IOException
{
ZipFile zip = null;
try {
zip = new ZipFile(file);
return extractZip(zip, outputDir, filter);
} finally {
try {
if (zip != null) zip.close();
} catch (IOException e) {
//ignore
}
}
}
/**
* Extract zip file to target directory
*
* @param zip open zip file
* @param outputDir target directory
* @param filter string filter (will be used to test entry names (paths))
* @return list of entries extracted (paths)
* @throws IOException
*/
public static List<String> extractZip(ZipFile zip, File outputDir, StringFilter filter) throws IOException
{
ArrayList<String> files = new ArrayList<String>();
outputDir.mkdirs();
Enumeration<? extends ZipEntry> zipFileEntries = zip.entries();
// process each entry
while (zipFileEntries.hasMoreElements()) {
ZipEntry entry = zipFileEntries.nextElement();
// parse filename and path
String entryPath = entry.getName();
File destFile = new File(outputDir, entryPath);
File destinationParent = destFile.getParentFile();
if (entry.isDirectory() || (filter != null && !filter.accept(entryPath))) continue;
// make sure directories exist
destinationParent.mkdirs();
if (!entry.isDirectory()) {
extractZipEntry(zip, entry, destFile);
files.add(entryPath);
}
}
return files;
}
/**
* Read zip entries and add their paths to a list
*
* @param zipFile open zip file
* @return list of entry names
* @throws IOException on error
*/
public static List<String> listZip(File zipFile) throws IOException
{
ZipFile zip = null;
try {
zip = new ZipFile(zipFile);
return listZip(zip);
} finally {
try {
if (zip != null) zip.close();
} catch (IOException e) {
//ignore
}
}
}
/**
* Read zip entries and add their paths to a list
*
* @param zip open zip file
* @return list of entry names
* @throws IOException on error
*/
public static List<String> listZip(ZipFile zip) throws IOException
{
ArrayList<String> files = new ArrayList<String>();
Enumeration<? extends ZipEntry> zipFileEntries = zip.entries();
// process each entry
while (zipFileEntries.hasMoreElements()) {
ZipEntry entry = zipFileEntries.nextElement();
if (!entry.isDirectory()) {
files.add(entry.getName());
}
}
return files;
}
/**
* Extract one zip entry to target file
*
* @param zip open zip file
* @param entry entry from the zip file
* @param destFile destination file ((NOT directory!)
* @throws IOException on error
*/
public static void extractZipEntry(ZipFile zip, ZipEntry entry, File destFile) throws IOException
{
destFile.getParentFile().mkdirs();
BufferedInputStream is = null;
FileOutputStream fos = null;
BufferedOutputStream dest = null;
try {
is = new BufferedInputStream(zip.getInputStream(entry));
fos = new FileOutputStream(destFile);
dest = new BufferedOutputStream(fos, BUFFER_SIZE);
FileUtils.copyStream(is, dest);
} finally {
try {
if (is != null) is.close();
} catch (IOException e) {
//ignore
}
try {
if (dest != null) dest.close();
} catch (IOException e) {
//ignore
}
}
}
/**
* Load zip entry to String
*
* @param zip open zip file
* @param entry entry from the zip file
* @return loaded string
* @throws IOException on error
*/
public static String zipEntryToString(ZipFile zip, ZipEntry entry) throws IOException
{
BufferedInputStream is = null;
try {
is = new BufferedInputStream(zip.getInputStream(entry));
String s = FileUtils.streamToString(is);
return s;
} finally {
try {
if (is != null) is.close();
} catch (IOException e) {
//ignore
}
}
}
public static boolean entryExists(File selectedFile, String string)
{
ZipFile zf = null;
try {
zf = new ZipFile(selectedFile);
return zf.getEntry(string) != null;
} catch (Exception e) {
return false;
} finally {
try {
if (zf != null) zf.close();
} catch (IOException e) {
//ignore
}
}
}
}

@ -0,0 +1,79 @@
package mightypork.utils.files.ion;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
/**
* Ionizable Arraylist
*
* @author MightyPork
* @param <T>
*/
public abstract class AbstractIonList<T> extends ArrayList<T> implements Ionizable {
@Override
public void ionRead(InputStream in) throws IonException
{
try {
while (true) {
byte b = StreamUtils.readByte(in);
if (b == IonMarks.ENTRY) {
T value = (T) Ion.readObject(in);
add(value);
} else if (b == IonMarks.END) {
break;
} else {
throw new IonException("Unexpected mark in IonList: " + Integer.toHexString(b));
}
}
ionReadCustomData(in);
} catch (IOException e) {
throw new IonException("Error reading ion list", e);
}
}
@Override
public void ionWrite(OutputStream out) throws IonException
{
try {
for (T entry : this) {
if (entry instanceof IonizableOptional && !((IonizableOptional) entry).ionShouldSave()) continue;
StreamUtils.writeByte(out, IonMarks.ENTRY);
Ion.writeObject(out, entry);
}
StreamUtils.writeByte(out, IonMarks.END);
ionWriteCustomData(out);
} catch (IOException e) {
throw new IonException("Error reading ion map", e);
}
}
/**
* Read custom data of this AbstractIonList implementation
*
* @param in input stream
*/
public void ionReadCustomData(InputStream in)
{}
/**
* Write custom data of this AbstractIonList implementation
*
* @param out output stream
*/
public void ionWriteCustomData(OutputStream out)
{}
@Override
public abstract byte ionMark();
}

@ -0,0 +1,101 @@
package mightypork.utils.files.ion;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.LinkedHashMap;
/**
* Ionizable HashMap
*
* @author MightyPork
* @param <V>
*/
public abstract class AbstractIonMap<V> extends LinkedHashMap<String, V> implements Ionizable {
@Override
public V get(Object key)
{
return super.get(key);
}
@Override
public V put(String key, V value)
{
return super.put(key, value);
}
@Override
public void ionRead(InputStream in) throws IonException
{
try {
while (true) {
byte b = StreamUtils.readByte(in);
if (b == IonMarks.ENTRY) {
String key = StreamUtils.readStringBytes(in);
V value;
try {
value = (V) Ion.readObject(in);
put(key, value);
} catch (IonException e) {
e.printStackTrace();
}
} else if (b == IonMarks.END) {
break;
} else {
throw new RuntimeException("Unexpected mark in IonMap: " + Integer.toHexString(b));
}
}
ionReadCustomData(in);
} catch (IOException e) {
throw new IonException("Error reading ion map", e);
}
}
@Override
public void ionWrite(OutputStream out) throws IonException
{
try {
for (java.util.Map.Entry<String, V> entry : entrySet()) {
StreamUtils.writeByte(out, IonMarks.ENTRY);
StreamUtils.writeStringBytes(out, entry.getKey());
Ion.writeObject(out, entry.getValue());
}
StreamUtils.writeByte(out, IonMarks.END);
ionWriteCustomData(out);
} catch (IOException e) {
throw new IonException("Error reading ion map", e);
}
}
/**
* Read custom data of this AbstractIonMap implementation
*
* @param in input stream
*/
public void ionReadCustomData(InputStream in)
{}
/**
* Write custom data of this AbstractIonMap implementation
*
* @param out output stream
*/
public void ionWriteCustomData(OutputStream out)
{}
@Override
public byte ionMark()
{
return IonMarks.MAP;
}
}

@ -0,0 +1,298 @@
package mightypork.utils.files.ion;
import java.io.*;
import java.util.HashMap;
import java.util.Map;
import mightypork.utils.math.Calc;
/**
* Universal data storage system
*
* @author MightyPork
*/
public class Ion {
/** Ionizables<Mark, Class> */
private static Map<Byte, Class<?>> customIonizables = new HashMap<Byte, Class<?>>();
// register default ionizables
static {
try {
registerIonizable(IonMarks.MAP, IonMap.class);
registerIonizable(IonMarks.LIST, IonList.class);
} catch (IonException e) {
e.printStackTrace();
}
}
/**
* Register new Ionizable for direct reconstructing.
*
* @param mark byte mark to be used, see {@link IonMarks} for reference.
* @param objClass class of the registered Ionizable
* @throws IonException
*/
public static void registerIonizable(byte mark, Class<?> objClass) throws IonException
{
if (customIonizables.containsKey(mark)) {
throw new IonException("IonMark " + mark + " is already used.");
}
customIonizables.put(mark, objClass);
}
/**
* Load Ion object from file.
*
* @param file file path
* @return the loaded object
* @throws IonException
*/
public static Object fromFile(String file) throws IonException
{
return fromFile(new File(file));
}
/**
* Load Ion object from file.
*
* @param file file
* @return the loaded object
* @throws IonException on failure
*/
public static Object fromFile(File file) throws IonException
{
InputStream in = null;
try {
in = new FileInputStream(file);
Object obj = fromStream(in);
return obj;
} catch (IOException e) {
throw new IonException("Error loading ION file.", e);
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* Load Ion object from stream.
*
* @param in input stream
* @return the loaded object
* @throws IonException
*/
public static Object fromStream(InputStream in) throws IonException
{
return readObject(in);
}
/**
* Store Ion object to file.
*
* @param path file path
* @param obj object to store
* @throws IonException
*/
public static void toFile(String path, Object obj) throws IonException
{
toFile(new File(path), obj);
}
/**
* Store Ion object to file.
*
* @param path file path
* @param obj object to store
* @throws IonException
*/
public static void toFile(File path, Object obj) throws IonException
{
OutputStream out = null;
try {
String f = path.toString();
File dir = new File(f.substring(0, f.lastIndexOf(File.separator)));
dir.mkdirs();
out = new FileOutputStream(path);
toStream(out, obj);
out.flush();
out.close();
} catch (Exception e) {
throw new IonException("Error writing to ION file.", e);
} finally {
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* Store Ion object to output stream.
*
* @param out output stream *
* @param obj object to store
* @throws IonException
*/
public static void toStream(OutputStream out, Object obj) throws IonException
{
writeObject(out, obj);
}
/**
* Read single ionizable or primitive object from input stream
*
* @param in input stream
* @return the loaded object
* @throws IonException
*/
public static Object readObject(InputStream in) throws IonException
{
try {
int bi = in.read();
if (bi == -1) throw new IonException("Unexpected end of stream.");
byte b = (byte) bi;
if (customIonizables.containsKey(b)) {
Ionizable ion;
try {
ion = ((Ionizable) customIonizables.get(b).newInstance());
} catch (InstantiationException e) {
throw new IonException("Cound not instantiate " + customIonizables.get(b).getSimpleName(), e);
} catch (IllegalAccessException e) {
throw new IonException("Cound not instantiate " + customIonizables.get(b).getSimpleName(), e);
}
ion.ionRead(in);
return ion;
}
switch (b) {
case IonMarks.BOOLEAN:
return StreamUtils.readBoolean(in);
case IonMarks.BYTE:
return StreamUtils.readByte(in);
case IonMarks.CHAR:
return StreamUtils.readChar(in);
case IonMarks.SHORT:
return StreamUtils.readShort(in);
case IonMarks.INT:
return StreamUtils.readInt(in);
case IonMarks.LONG:
return StreamUtils.readLong(in);
case IonMarks.FLOAT:
return StreamUtils.readFloat(in);
case IonMarks.DOUBLE:
return StreamUtils.readDouble(in);
case IonMarks.STRING:
String s = StreamUtils.readString(in);
return s;
default:
throw new IonException("Invalid Ion mark " + Integer.toHexString(bi));
}
} catch (IOException e) {
throw new IonException("Error loading ION file: ", e);
}
}
/**
* Write single ionizable or primitive object to output stream
*
* @param out output stream
* @param obj stored object
* @throws IonException
*/
public static void writeObject(OutputStream out, Object obj) throws IonException
{
try {
if (obj instanceof Ionizable) {
out.write(((Ionizable) obj).ionMark());
((Ionizable) obj).ionWrite(out);
return;
}
if (obj instanceof Boolean) {
out.write(IonMarks.BOOLEAN);
StreamUtils.writeBoolean(out, (Boolean) obj);
return;
}
if (obj instanceof Byte) {
out.write(IonMarks.BYTE);
StreamUtils.writeByte(out, (Byte) obj);
return;
}
if (obj instanceof Character) {
out.write(IonMarks.CHAR);
StreamUtils.writeChar(out, (Character) obj);
return;
}
if (obj instanceof Short) {
out.write(IonMarks.SHORT);
StreamUtils.writeShort(out, (Short) obj);
return;
}
if (obj instanceof Integer) {
out.write(IonMarks.INT);
StreamUtils.writeInt(out, (Integer) obj);
return;
}
if (obj instanceof Long) {
out.write(IonMarks.LONG);
StreamUtils.writeLong(out, (Long) obj);
return;
}
if (obj instanceof Float) {
out.write(IonMarks.FLOAT);
StreamUtils.writeFloat(out, (Float) obj);
return;
}
if (obj instanceof Double) {
out.write(IonMarks.DOUBLE);
StreamUtils.writeDouble(out, (Double) obj);
return;
}
if (obj instanceof String) {
out.write(IonMarks.STRING);
StreamUtils.writeString(out, (String) obj);
return;
}
throw new IonException(Calc.cname(obj) + " can't be stored in Ion storage.");
} catch (IOException e) {
throw new IonException("Could not store " + obj, e);
}
}
}

@ -0,0 +1,25 @@
package mightypork.utils.files.ion;
public class IonException extends Exception {
public IonException() {
super();
}
public IonException(String message, Throwable cause) {
super(message, cause);
}
public IonException(String message) {
super(message);
}
public IonException(Throwable cause) {
super(cause);
}
}

@ -0,0 +1,157 @@
package mightypork.utils.files.ion;
/**
* Ionizable Arraylist
*
* @author MightyPork
*/
public class IonList extends AbstractIonList<Object> implements Ionizable {
public Ionizable getIonizable(int index) throws IonException
{
return (Ionizable) getCheckType(index, Ionizable.class);
}
public boolean getBoolean(int index) throws IonException
{
return (Boolean) getCheckType(index, Boolean.class);
}
public byte getByte(int index) throws IonException
{
return (Byte) getCheckType(index, Byte.class);
}
public char getChar(int index) throws IonException
{
return (Character) getCheckType(index, Character.class);
}
public short getShort(int index) throws IonException
{
return (Short) getCheckType(index, Short.class);
}
public int getInt(int index) throws IonException
{
return (Integer) getCheckType(index, Integer.class);
}
public long getLong(int index) throws IonException
{
return (Long) getCheckType(index, Long.class);
}
public float getFloat(int index) throws IonException
{
return (Float) getCheckType(index, Float.class);
}
public double getDouble(int index) throws IonException
{
return (Double) getCheckType(index, Double.class);
}
public String getString(int index) throws IonException
{
return (String) getCheckType(index, String.class);
}
public void addIonizable(Ionizable o) throws IonException
{
addCheckNull(o);
}
public void addBoolean(boolean o) throws IonException
{
addCheckNull(o);
}
public void addByte(byte o) throws IonException
{
addCheckNull(o);
}
public void addChar(char o) throws IonException
{
addCheckNull(o);
}
public void addShort(short o) throws IonException
{
addCheckNull(o);
}
public void addInt(int o) throws IonException
{
addCheckNull(o);
}
public void addLong(long o) throws IonException
{
addCheckNull(o);
}
public void addFloat(float o) throws IonException
{
addCheckNull(o);
}
public void addDouble(double o) throws IonException
{
addCheckNull(o);
}
public void addString(String o) throws IonException
{
addCheckNull(o);
}
public Object getCheckType(int index, Class<?> type) throws IonException
{
try {
Object o = super.get(index);
if (o == null || !o.getClass().isAssignableFrom(type)) {
throw new IonException("Incorrect object type");
}
return o;
} catch (IndexOutOfBoundsException e) {
throw new IonException("Out of bounds");
}
}
private void addCheckNull(Object o) throws IonException
{
if (o == null) throw new IonException("Cannot store null");
}
@Override
public byte ionMark()
{
return IonMarks.LIST;
}
}

@ -0,0 +1,156 @@
package mightypork.utils.files.ion;
/**
* Ionizable HashMap
*
* @author MightyPork
*/
public class IonMap extends AbstractIonMap<Object> implements Ionizable {
public boolean getBoolean(String key)
{
return (Boolean) get(key);
}
public boolean getBool(String key)
{
return (Boolean) get(key);
}
public byte getByte(String key)
{
return (Byte) get(key);
}
public char getChar(String key)
{
return (Character) get(key);
}
public short getShort(String key)
{
return (Short) get(key);
}
public int getInt(String key)
{
return (Integer) get(key);
}
public long getLong(String key)
{
return (Long) get(key);
}
public float getFloat(String key)
{
return (Float) get(key);
}
public double getDouble(String key)
{
return (Double) get(key);
}
public String getString(String key)
{
return (String) get(key);
}
@Override
public Object get(Object arg0)
{
return super.get(arg0);
}
public void putBoolean(String key, boolean num)
{
put(key, num);
}
public void putBool(String key, boolean num)
{
put(key, num);
}
public void putByte(String key, int num)
{
put(key, (byte) num);
}
public void putChar(String key, char num)
{
put(key, num);
}
public void putCharacter(String key, char num)
{
put(key, num);
}
public void putShort(String key, int num)
{
put(key, num);
}
public void putInt(String key, int num)
{
put(key, num);
}
public void putInteger(String key, int num)
{
put(key, num);
}
public void putLong(String key, long num)
{
put(key, num);
}
public void putFloat(String key, double num)
{
put(key, (float) num);
}
public void putDouble(String key, double num)
{
put(key, num);
}
public void putString(String key, String num)
{
put(key, num);
}
@Override
public byte ionMark()
{
return IonMarks.MAP;
}
}

@ -0,0 +1,57 @@
package mightypork.utils.files.ion;
/**
* Byte marks used to structure data in Ion files.
*
* @author MightyPork
*/
public class IonMarks {
/** Null value */
public static final byte NULL = 0;
/** Boolean value */
public static final byte BOOLEAN = 1;
/** Byte value */
public static final byte BYTE = 2;
/** Character value */
public static final byte CHAR = 3;
/** Short value */
public static final byte SHORT = 4;
/** Integer value */
public static final byte INT = 5;
/** Long value */
public static final byte LONG = 6;
/** Float value */
public static final byte FLOAT = 7;
/** Double value */
public static final byte DOUBLE = 8;
/** String value */
public static final byte STRING = 9;
/** List value (begin) - contains entries, ends with END */
public static final byte LIST = 10;
/** Map value (begin) - contains entries, ends with END */
public static final byte MAP = 11;
/**
* List / Map entry<br>
* In list directly followed by entry value. In map followed by (string) key
* and the entry value.
*/
public static final byte ENTRY = 12;
/** End of List / Map */
public static final byte END = 13;
}

@ -0,0 +1,43 @@
package mightypork.utils.files.ion;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Object that can be saved to and loaded from Ion file.<br>
* All classes implementing Ionizable must be registered to {@link Ion} using
* Ion.registerIonizable(obj.class).
*
* @author MightyPork
*/
public interface Ionizable {
/**
* Load data from the input stream. Mark has already been read, begin
* reading right after it.
*
* @param in input stream
* @throws IonException
*/
public void ionRead(InputStream in) throws IonException;
/**
* Store data to output stream. mark has already been written, begin right
* after it.
*
* @param out Output stream
* @throws IonException
*/
public void ionWrite(OutputStream out) throws IonException;
/**
* Get Ion mark byte.
*
* @return Ion mark byte.
*/
public byte ionMark();
}

@ -0,0 +1,17 @@
package mightypork.utils.files.ion;
/**
* Optional ionizable
*
* @author MightyPork
*/
public interface IonizableOptional extends Ionizable {
/**
* Get if this ionizable should be saved to a list
*
* @return should save
*/
public boolean ionShouldSave();
}

@ -0,0 +1,265 @@
package mightypork.utils.files.ion;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
/**
* Utilities to store and load objects to streams.
*
* @author MightyPork
*/
public class StreamUtils {
private static ByteBuffer bi = ByteBuffer.allocate(Integer.SIZE / 8);
private static ByteBuffer bd = ByteBuffer.allocate(Double.SIZE / 8);
private static ByteBuffer bf = ByteBuffer.allocate(Float.SIZE / 8);
private static ByteBuffer bc = ByteBuffer.allocate(Character.SIZE / 8);
private static ByteBuffer bl = ByteBuffer.allocate(Long.SIZE / 8);
private static ByteBuffer bs = ByteBuffer.allocate(Short.SIZE / 8);
private static byte[] ai = new byte[Integer.SIZE / 8];
private static byte[] ad = new byte[Double.SIZE / 8];
private static byte[] af = new byte[Float.SIZE / 8];
private static byte[] ac = new byte[Character.SIZE / 8];
private static byte[] al = new byte[Long.SIZE / 8];
private static byte[] as = new byte[Short.SIZE / 8];
// CONVERSIONS
private static byte[] convBool(boolean bool)
{
return new byte[] { (byte) (bool ? 1 : 0) };
}
private static byte[] convByte(byte num)
{
return new byte[] { num };
}
private static byte[] convChar(char num)
{
bc.clear();
bc.putChar(num);
return bc.array();
}
private static byte[] convShort(short num)
{
bs.clear();
bs.putShort(num);
return bs.array();
}
private static byte[] convInt(int num)
{
bi.clear();
bi.putInt(num);
return bi.array();
}
private static byte[] convLong(long num)
{
bl.clear();
bl.putLong(num);
return bl.array();
}
private static byte[] convFloat(float num)
{
bf.clear();
bf.putFloat(num);
return bf.array();
}
private static byte[] convDouble(double num)
{
bd.clear();
bd.putDouble(num);
return bd.array();
}
private static byte[] convString(String str)
{
char[] chars = str.toCharArray();
ByteBuffer bstr = ByteBuffer.allocate((Character.SIZE / 8) * chars.length + (Character.SIZE / 8));
for (char c : chars) {
bstr.putChar(c);
}
bstr.putChar((char) 0);
return bstr.array();
}
private static byte[] convString_b(String str)
{
char[] chars = str.toCharArray();
ByteBuffer bstr = ByteBuffer.allocate((Byte.SIZE / 8) * chars.length + 1);
for (char c : chars) {
bstr.put((byte) c);
}
bstr.put((byte) 0);
return bstr.array();
}
public static void writeBoolean(OutputStream out, boolean num) throws IOException
{
out.write(convBool(num));
}
public static void writeByte(OutputStream out, byte num) throws IOException
{
out.write(convByte(num));
}
public static void writeChar(OutputStream out, char num) throws IOException
{
out.write(convChar(num));
}
public static void writeShort(OutputStream out, short num) throws IOException
{
out.write(convShort(num));
}
public static void writeInt(OutputStream out, int num) throws IOException
{
out.write(convInt(num));
}
public static void writeLong(OutputStream out, long num) throws IOException
{
out.write(convLong(num));
}
public static void writeFloat(OutputStream out, float num) throws IOException
{
out.write(convFloat(num));
}
public static void writeDouble(OutputStream out, double num) throws IOException
{
out.write(convDouble(num));
}
public static void writeString(OutputStream out, String str) throws IOException
{
out.write(convString(str));
}
public static void writeStringBytes(OutputStream out, String str) throws IOException
{
out.write(convString_b(str));
}
// READING
public static boolean readBoolean(InputStream in) throws IOException
{
return in.read() > 0;
}
public static byte readByte(InputStream in) throws IOException
{
return (byte) in.read();
}
public static char readChar(InputStream in) throws IOException
{
in.read(ac, 0, ac.length);
ByteBuffer buf = ByteBuffer.wrap(ac);
return buf.getChar();
}
public static short readShort(InputStream in) throws IOException
{
in.read(as, 0, as.length);
ByteBuffer buf = ByteBuffer.wrap(as);
return buf.getShort();
}
public static long readLong(InputStream in) throws IOException
{
in.read(al, 0, al.length);
ByteBuffer buf = ByteBuffer.wrap(al);
return buf.getLong();
}
public static int readInt(InputStream in) throws IOException
{
in.read(ai, 0, ai.length);
ByteBuffer buf = ByteBuffer.wrap(ai);
return buf.getInt();
}
public static float readFloat(InputStream in) throws IOException
{
in.read(af, 0, af.length);
ByteBuffer buf = ByteBuffer.wrap(af);
return buf.getFloat();
}
public static double readDouble(InputStream in) throws IOException
{
in.read(ad, 0, ad.length);
ByteBuffer buf = ByteBuffer.wrap(ad);
return buf.getDouble();
}
public static String readString(InputStream in) throws IOException
{
String s = "";
char c;
while ((c = readChar(in)) > 0) {
s += c;
}
return s;
}
public static String readStringBytes(InputStream in) throws IOException
{
String s = "";
byte b;
while ((b = readByte(in)) > 0) {
s += (char) b;
}
return s;
}
}

@ -0,0 +1,159 @@
package mightypork.utils.logging;
import java.io.File;
import java.util.HashMap;
public class Log {
/** enable static logging */
private static boolean esl;
/**
* Log FINE message in main logger
*
* @param msg message
*/
public static void f1(String msg)
{
if (esl && main != null) main.f1(msg);
}
/**
* Log FINER message in main logger
*
* @param msg message
*/
public static void f2(String msg)
{
if (esl && main != null) main.f2(msg);
}
/**
* Log FINEST message in main logger
*
* @param msg message
*/
public static void f3(String msg)
{
if (esl && main != null) main.f3(msg);
}
/**
* Log INFO message in main logger
*
* @param msg message
*/
public static void i(String msg)
{
if (esl && main != null) main.i(msg);
}
/**
* Log WARNING message in main logger
*
* @param msg message
*/
public static void w(String msg)
{
if (esl && main != null) main.w(msg);
}
/**
* Log ERROR message in main logger
*
* @param msg message
*/
public static void e(String msg)
{
if (esl && main != null) main.e(msg);
}
/**
* Log EXCEPTION and ERROR message in main logger
*
* @param msg message
* @param thrown thrown exception
*/
public static void e(String msg, Throwable thrown)
{
if (esl && main != null) main.e(msg, thrown);
}
/**
* Log EXCEPTION in main logger
*
* @param thrown thrown exception
*/
public static void e(Throwable thrown)
{
if (esl && main != null) main.e(thrown);
}
public static void enable(boolean flag)
{
if (esl && main != null) main.enable(flag);
}
/**
* Enable / disable static log delegate methods
*
* @param flag enable
*/
public static void enableStaticLogging(boolean flag)
{
esl = flag;
}
private static HashMap<String, LogInstance> logs = new HashMap<String, LogInstance>();
private static LogInstance main = null;
/**
* Create a logger. If this is the first logger made, then it'll be made
* available via the static methods.
*
* @param logName log name (used for filename, must be application-unique)
* @param logsDir directory to store logs in
* @param oldLogsCount number of old logs to keep, -1 for infinite, 0 for
* none.
* @return the created Log instance
*/
public static synchronized LogInstance create(String logName, File logsDir, int oldLogsCount)
{
if (logs.containsKey(logName)) return logs.get(logName);
LogInstance log = new LogInstance(logName, logsDir, oldLogsCount);
if (main == null) main = log;
logs.put(logName, log);
return log;
}
public int addMonitor(LogMonitor mon)
{
if (main == null) throw new IllegalStateException("Main logger not initialized.");
return main.addMonitor(mon);
}
public void removeMonitor(int id)
{
if (main == null) throw new IllegalStateException("Main logger not initialized.");
main.removeMonitor(id);
}
}

@ -0,0 +1,385 @@
package mightypork.utils.logging;
import java.io.File;
import java.io.FileFilter;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.logging.FileHandler;
import java.util.logging.Formatter;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import mightypork.utils.files.FileUtils;
/**
* Static logger class.
*
* @author MightyPork
* @copy (c) 2014
*/
public class LogInstance {
/** log file */
private File file;
/** Log name */
private String name;
/** Number of old logs to keep */
private int logs_to_keep;
/** Logs dir */
private File dir;
/** Logger instance. */
private Logger logger;
/** Logging enabled */
private boolean enabled = true;
private boolean sysout = true;
private int monitorId = 0;
private HashMap<Integer, LogMonitor> monitors = new HashMap<Integer, LogMonitor>();
private LogToSysoutMonitor sysoutMonitor;
public LogInstance(String name, File dir, int oldLogCount) {
this.name = name;
this.file = new File(dir, name + getSuffix());
this.dir = dir;
this.logs_to_keep = oldLogCount;
init();
}
/**
* Prepare logs for logging
*/
private void init()
{
logger = Logger.getLogger(name);
cleanup();
FileHandler handler = null;
try {
handler = new FileHandler(file.getPath());
} catch (Exception e) {
throw new RuntimeException("Failed to init log", e);
}
handler.setFormatter(new LogFormatter());
logger.addHandler(handler);
enabled = true;
sysoutMonitor = new LogToSysoutMonitor();
addMonitor(sysoutMonitor);
logger.setUseParentHandlers(false);
logger.setLevel(Level.ALL);
logger.info("Main logger initialized.");
logger.info((new SimpleDateFormat("yyyy/MM/dd HH:mm:ss")).format(new Date()));
}
private void cleanup()
{
for (File f : FileUtils.listDirectory(file.getParentFile())) {
if (!f.isFile()) continue;
if (f.equals(file)) {
Date d = new Date(f.lastModified());
String fname = name + '_' + (new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss")).format(d) + getSuffix();
File f2 = new File(dir, fname);
f.renameTo(f2);
}
}
List<File> oldLogs = FileUtils.listDirectory(dir, new FileFilter() {
@Override
public boolean accept(File f)
{
if (f.isDirectory()) return false;
if (!f.getName().endsWith(getSuffix())) return false;
if (!f.getName().startsWith(name)) return false;
return true;
}
});
Collections.sort(oldLogs, new Comparator<File>() {
@Override
public int compare(File o1, File o2)
{
return o1.getName().compareTo(o2.getName());
}
});
for (int i = 0; i < oldLogs.size() - logs_to_keep; i++) {
oldLogs.get(i).delete();
}
}
/**
* Add log monitor
*
* @param mon monitor
* @return assigned ID
*/
public synchronized int addMonitor(LogMonitor mon)
{
int id = monitorId;
monitorId++;
monitors.put(id, mon);
return id;
}
/**
* Remove a monitor by ID
*
* @param id monitor ID
*/
public synchronized void removeMonitor(int id)
{
monitors.remove(id);
}
/**
* Enable logging.
*
* @param flag do enable logging
*/
public void enable(boolean flag)
{
enabled = flag;
}
/**
* Enable printing logs to sysout
*
* @param flag do enable logging
*/
public void enableSysout(boolean flag)
{
sysout = flag;
sysoutMonitor.enable(sysout);
}
/**
* Log FINE message
*
* @param msg message
*/
public void f1(String msg)
{
if (enabled) logger.log(Level.FINE, msg);
}
/**
* Log FINER message
*
* @param msg message
*/
public void f2(String msg)
{
if (enabled) logger.log(Level.FINER, msg);
}
/**
* Log FINEST message
*
* @param msg message
*/
public void f3(String msg)
{
if (enabled) logger.log(Level.FINEST, msg);
}
/**
* Log INFO message
*
* @param msg message
*/
public void i(String msg)
{
if (enabled) logger.log(Level.INFO, msg);
}
/**
* Log WARNING message (less severe than ERROR)
*
* @param msg message
*/
public void w(String msg)
{
if (enabled) logger.log(Level.WARNING, msg);
}
/**
* Log ERROR message
*
* @param msg message
*/
public void e(String msg)
{
if (enabled) logger.log(Level.SEVERE, msg);
}
/**
* Log THROWING message
*
* @param msg message
* @param thrown thrown exception
*/
public void e(String msg, Throwable thrown)
{
if (enabled) logger.log(Level.SEVERE, msg + "\n" + getStackTrace(thrown));
}
/**
* Log exception thrown
*
* @param thrown thrown exception
*/
public void e(Throwable thrown)
{
if (enabled) logger.log(Level.SEVERE, getStackTrace(thrown));
}
/**
* Get stack trace from throwable
*
* @param t
* @return trace
*/
private String getStackTrace(Throwable t)
{
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw, true);
t.printStackTrace(pw);
pw.flush();
sw.flush();
return sw.toString();
}
/**
* PowerCraft Log file formatter.
*
* @author MightyPork
* @copy (c) 2012
*/
private class LogFormatter extends Formatter {
/** Newline string constant */
private final String nl = System.getProperty("line.separator");
@Override
public String format(LogRecord record)
{
StringBuffer buf = new StringBuffer(180);
if (record.getMessage().equals("\n")) {
return nl;
}
if (record.getMessage().charAt(0) == '\n') {
buf.append(nl);
record.setMessage(record.getMessage().substring(1));
}
Level level = record.getLevel();
String trail = "[ ? ]";
if (level == Level.FINE) {
trail = "[ # ] ";
}
if (level == Level.FINER) {
trail = "[ - ] ";
}
if (level == Level.FINEST) {
trail = "[ ] ";
}
if (level == Level.INFO) {
trail = "[ i ] ";
}
if (level == Level.SEVERE) {
trail = "[!E!] ";
}
if (level == Level.WARNING) {
trail = "[!W!] ";
}
record.setMessage(record.getMessage().replaceAll("\n", nl + trail));
buf.append(trail);
buf.append(formatMessage(record));
buf.append(nl);
Throwable throwable = record.getThrown();
if (throwable != null) {
buf.append("at ");
buf.append(record.getSourceClassName());
buf.append('.');
buf.append(record.getSourceMethodName());
buf.append(nl);
StringWriter sink = new StringWriter();
throwable.printStackTrace(new PrintWriter(sink, true));
buf.append(sink.toString());
buf.append(nl);
}
String str = buf.toString();
for (LogMonitor mon : monitors.values()) {
mon.log(level, str);
}
return str;
}
}
/**
* @return log filename suffix (incl. dot)
*/
protected String getSuffix()
{
return ".log";
}
}

@ -0,0 +1,13 @@
package mightypork.utils.logging;
import java.util.logging.Level;
public interface LogMonitor {
public void log(Level level, String message);
public void enable(boolean enable);
}

@ -0,0 +1,31 @@
package mightypork.utils.logging;
import java.util.logging.Level;
public class LogToSysoutMonitor implements LogMonitor {
private boolean enabled = true;
@Override
public void log(Level level, String message)
{
if (!enabled) return;
if (level == Level.FINE || level == Level.FINER || level == Level.FINEST || level == Level.INFO) {
System.out.print(message);
} else if (level == Level.SEVERE || level == Level.WARNING) {
System.err.print(message);
}
}
@Override
public void enable(boolean enable)
{
this.enabled = enable;
}
}

@ -0,0 +1,931 @@
package mightypork.utils.math;
import java.nio.FloatBuffer;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import mightypork.utils.math.coord.Coord;
import mightypork.utils.math.coord.Vec;
import org.lwjgl.BufferUtils;
/**
* Math helper
*
* @author MightyPork
*/
public class Calc {
/** Square root of two */
public static final double SQ2 = 1.41421356237;
/**
* Get distance from 2D line to 2D point [X,Y]
*
* @param lineDirVec line directional vector
* @param linePoint point of line
* @param point point coordinate
* @return distance
*/
public static double linePointDist(Vec lineDirVec, Coord linePoint, Coord point)
{
// line point L[lx,ly]
double lx = linePoint.x;
double ly = linePoint.y;
// line equation ax+by+c=0
double a = -lineDirVec.y;
double b = lineDirVec.x;
double c = -a * lx - b * ly;
// checked point P[x,y]
double x = point.x;
double y = point.y;
// distance
return Math.abs(a * x + b * y + c) / Math.sqrt(a * a + b * b);
}
/**
* Get distance from 2D line to 2D point [X,Z]
*
* @param lineDirVec line directional vector
* @param linePoint point of line
* @param point point coordinate
* @return distance
*/
public static double linePointDistXZ(Vec lineDirVec, Coord linePoint, Coord point)
{
return linePointDist(new Vec(lineDirVec.x, lineDirVec.z), new Coord(linePoint.x, linePoint.z), new Coord(point.x, point.z));
}
/**
* Get longest side of a right-angled triangle
*
* @param a side a (opposite)
* @param b side b (adjacent)
* @return longest side (hypotenuse)
*/
public static double pythC(double a, double b)
{
return Math.sqrt(square(a) + square(b));
}
/**
* Get adjacent side of a right-angled triangle
*
* @param a side a (opposite)
* @param c side c (hypotenuse)
* @return side b (adjacent)
*/
public static double pythB(double a, double c)
{
return Math.sqrt(square(c) - square(a));
}
/**
* Get opposite side of a right-angled triangle
*
* @param b side b (adjacent)
* @param c side c (hypotenuse)
* @return side a (opposite)
*/
public static double pythA(double b, double c)
{
return Math.sqrt(square(c) - square(b));
}
private static class Angles {
public static double delta(double alpha, double beta, double a360)
{
while (Math.abs(alpha - beta) > a360 / 2D) {
alpha = norm(alpha + a360 / 2D, a360);
beta = norm(beta + a360 / 2D, a360);
}
return beta - alpha;
}
public static double norm(double angle, double a360)
{
while (angle < 0)
angle += a360;
while (angle > a360)
angle -= a360;
return angle;
}
}
/**
* Calc subclass with buffer utils.
*
* @author MightyPork
*/
public static class Buffers {
/**
* Create java.nio.FloatBuffer of given floats, and flip it.
*
* @param obj floats or float array
* @return float buffer
*/
public static FloatBuffer mkFillBuff(float... obj)
{
return (FloatBuffer) BufferUtils.createFloatBuffer(obj.length).put(obj).flip();
}
/**
* Fill java.nio.FloatBuffer with floats or float array
*
* @param buff
* @param obj
*/
public static void fill(FloatBuffer buff, float... obj)
{
buff.put(obj);
buff.flip();
}
/**
* Create new java.nio.FloatBuffer of given length
*
* @param count elements
* @return the new java.nio.FloatBuffer
*/
public static FloatBuffer alloc(int count)
{
return BufferUtils.createFloatBuffer(count);
}
}
/**
* Angle calculations for degrees.
*
* @author MightyPork
*/
public static class Deg {
/** 180° in degrees */
public static final double a180 = 180;
/** 270° in degrees */
public static final double a270 = 270;
/** 360° in degrees */
public static final double a360 = 360;
/** 45° in degrees */
public static final double a45 = 45;
/** 90° in degrees */
public static final double a90 = 90;
/**
* Subtract two angles alpha - beta
*
* @param alpha first angle
* @param beta second angle
* @return (alpha - beta) in degrees
*/
public static double delta(double alpha, double beta)
{
return Angles.delta(alpha, beta, a360);
}
/**
* Difference of two angles (absolute value of delta)
*
* @param alpha first angle
* @param beta second angle
* @return difference in radians
*/
public static double diff(double alpha, double beta)
{
return Math.abs(Angles.delta(alpha, beta, a360));
}
/**
* Cosinus in degrees
*
* @param deg angle in degrees
* @return cosinus
*/
public static double cos(double deg)
{
return Math.cos(toRad(deg));
}
/**
* Sinus in degrees
*
* @param deg angle in degrees
* @return sinus
*/
public static double sin(double deg)
{
return Math.sin(toRad(deg));
}
/**
* Tangents in degrees
*
* @param deg angle in degrees
* @return tangents
*/
public static double tan(double deg)
{
return Math.tan(toRad(deg));
}
/**
* Angle normalized to 0-360 range
*
* @param angle angle to normalize
* @return normalized angle
*/
public static double norm(double angle)
{
return Angles.norm(angle, a360);
}
/**
* Convert to radians
*
* @param deg degrees
* @return radians
*/
public static double toRad(double deg)
{
return Math.toRadians(deg);
}
/**
* Round angle to 0,45,90,135...
*
* @param deg angle in deg. to round
* @param x rounding increment (45 - round to 0,45,90...)
* @return rounded
*/
public static int roundX(double deg, double x)
{
double half = x / 2d;
deg += half;
deg = norm(deg);
int times = (int) Math.floor(deg / x);
double a = times * x;
if (a == 360) a = 0;
return (int) Math.round(a);
}
/**
* Round angle to 0,45,90,135...
*
* @param deg angle in deg. to round
* @return rounded
*/
public static int round45(double deg)
{
return roundX(deg, 45);
}
/**
* Round angle to 0,90,180,270
*
* @param deg angle in deg. to round
* @return rounded
*/
public static int round90(double deg)
{
return roundX(deg, 90);
}
/**
* Round angle to 0,15,30,45,60,75,90...
*
* @param deg angle in deg to round
* @return rounded
*/
public static int round15(double deg)
{
return roundX(deg, 15);
}
}
/**
* Angle calculations for radians.
*
* @author MightyPork
*/
public static class Rad {
/** 180° in radians */
public static final double a180 = Math.PI;
/** 270° in radians */
public static final double a270 = Math.PI * 1.5D;
/** 360° in radians */
public static final double a360 = Math.PI * 2D;
/** 45° in radians */
public static final double a45 = Math.PI / 4D;
/** 90° in radians */
public static final double a90 = Math.PI / 2D;
/**
* Subtract two angles alpha - beta
*
* @param alpha first angle
* @param beta second angle
* @return (alpha - beta) in radians
*/
public static double delta(double alpha, double beta)
{
return Angles.delta(alpha, beta, a360);
}
/**
* Difference of two angles (absolute value of delta)
*
* @param alpha first angle
* @param beta second angle
* @return difference in radians
*/
public static double diff(double alpha, double beta)
{
return Math.abs(Angles.delta(alpha, beta, a360));
}
/**
* Cos
*
* @param rad angle in rads
* @return cos
*/
public static double cos(double rad)
{
return Math.cos(rad);
}
/**
* Sin
*
* @param rad angle in rads
* @return sin
*/
public static double sin(double rad)
{
return Math.sin(rad);
}
/**
* Tan
*
* @param rad angle in rads
* @return tan
*/
public static double tan(double rad)
{
return Math.tan(rad);
}
/**
* Angle normalized to 0-2*PI range
*
* @param angle angle to normalize
* @return normalized angle
*/
public static double norm(double angle)
{
return Angles.norm(angle, a360);
}
/**
* Convert to degrees
*
* @param rad radians
* @return degrees
*/
public static double toDeg(double rad)
{
return Math.toDegrees(rad);
}
}
private static Random rand = new Random();
/**
* Get volume of a sphere
*
* @param radius sphere radius
* @return volume in cubic units
*/
public static double sphereGetVolume(double radius)
{
return (4D / 3D) * Math.PI * cube(radius);
}
/**
* Get radius of a sphere
*
* @param volume sphere volume
* @return radius in units
*/
public static double sphereGetRadius(double volume)
{
return Math.cbrt((3D * volume) / (4 * Math.PI));
}
/**
* Get surface of a circle
*
* @param radius circle radius
* @return volume in square units
*/
public static double circleGetSurface(double radius)
{
return Math.PI * square(radius);
}
/**
* Get radius of a circle
*
* @param surface circle volume
* @return radius in units
*/
public static double circleGetRadius(double surface)
{
return Math.sqrt(surface / Math.PI);
}
/**
* Check if objects are equal (for equals function)
*
* @param a
* @param b
* @return are equal
*/
public static boolean areObjectsEqual(Object a, Object b)
{
return a == null ? b == null : a.equals(b);
}
/**
* Private clamping helper.
*
* @param number number to be clamped
* @param min min value
* @param max max value
* @return clamped double
*/
private static double clamp_double(Number number, Number min, Number max)
{
double n = number.doubleValue();
double mind = min.doubleValue();
double maxd = max.doubleValue();
if (n > maxd) n = maxd;
if (n < mind) n = mind;
if (Double.isNaN(n)) return mind;
return n;
}
/**
* Private clamping helper.
*
* @param number number to be clamped
* @param min min value
* @return clamped double
*/
private static double clamp_double(Number number, Number min)
{
double n = number.doubleValue();
double mind = min.doubleValue();
if (n < mind) n = mind;
return n;
}
/**
* Clamp number to min and max bounds, inclusive.<br>
* DOUBLE version
*
* @param number clamped number
* @param min minimal allowed value
* @param max maximal allowed value
* @return result
*/
public static double clampd(Number number, Number min, Number max)
{
return clamp_double(number, min, max);
}
/**
* Clamp number to min and max bounds, inclusive.<br>
* FLOAT version
*
* @param number clamped number
* @param min minimal allowed value
* @param max maximal allowed value
* @return result
*/
public static float clampf(Number number, Number min, Number max)
{
return (float) clamp_double(number, min, max);
}
/**
* Clamp number to min and max bounds, inclusive.<br>
* INTEGER version
*
* @param number clamped number
* @param min minimal allowed value
* @param max maximal allowed value
* @return result
*/
public static int clampi(Number number, Number min, Number max)
{
return (int) Math.round(clamp_double(number, min, max));
}
/**
* Clamp number to min and max bounds, inclusive.<br>
* INTEGER version
*
* @param number clamped number
* @param range range
* @return result
*/
public static int clampi(Number number, Range range)
{
return (int) Math.round(clamp_double(number, range.getMin(), range.getMax()));
}
/**
* Clamp number to min and max bounds, inclusive.<br>
* DOUBLE version
*
* @param number clamped number
* @param range range
* @return result
*/
public static double clampd(Number number, Range range)
{
return clamp_double(number, range.getMin(), range.getMax());
}
/**
* Clamp number to min and max bounds, inclusive.<br>
* FLOAT version
*
* @param number clamped number
* @param range range
* @return result
*/
public static float clampf(Number number, Range range)
{
return (float) clamp_double(number, range.getMin(), range.getMax());
}
/**
* Clamp number to min and infinite bounds, inclusive.<br>
* DOUBLE version
*
* @param number clamped number
* @param min minimal allowed value
* @return result
*/
public static double clampd(Number number, Number min)
{
return clamp_double(number, min);
}
/**
* Clamp number to min and infinite bounds, inclusive.<br>
* FLOAT version
*
* @param number clamped number
* @param min minimal allowed value
* @return result
*/
public static float clampf(Number number, Number min)
{
return (float) clamp_double(number, min);
}
/**
* Clamp number to min and infinite bounds, inclusive.<br>
* INTEGER version
*
* @param number clamped number
* @param min minimal allowed value
* @return result
*/
public static int clampi(Number number, Number min)
{
return (int) Math.round(clamp_double(number, min));
}
/**
* Get class simple name
*
* @param obj object
* @return simple name
*/
public static String cname(Object obj)
{
if (obj == null) return "NULL";
return obj.getClass().getSimpleName();
}
/**
* Cube a double
*
* @param a squared double
* @return square
*/
public static double cube(double a)
{
return a * a * a;
}
/**
* Convert double to string, remove the mess at the end.
*
* @param d double
* @return string
*/
public static String doubleToString(double d)
{
String s = Double.toString(d);
s = s.replaceAll("([0-9]+\\.[0-9]+)00+[0-9]+", "$1");
s = s.replaceAll("0+$", "");
s = s.replaceAll("\\.$", "");
return s;
}
/**
* Convert float to string, remove the mess at the end.
*
* @param f float
* @return string
*/
public static String floatToString(float f)
{
String s = Float.toString(f);
s = s.replaceAll("([0-9]+\\.[0-9]+)00+[0-9]+", "$1");
s = s.replaceAll("0+$", "");
s = s.replaceAll("\\.$", "");
return s;
}
/**
* Check if number is in range
*
* @param number checked
* @param left lower end
* @param right upper end
* @return is in range
*/
public static boolean inRange(double number, double left, double right)
{
return number >= left && number <= right;
}
/**
* Get number from A to B at delta time (tween A to B)
*
* @param last last number
* @param now new number
* @param dtime delta time
* @return current number to render
*/
public static double interpolate(double last, double now, double dtime)
{
return last + (now - last) * dtime;
}
/**
* Get angle [degrees] from A to B at delta time (tween A to B)
*
* @param last last angle
* @param now new angle
* @param delta delta time
* @return current angle to render
*/
public static double interpolateDeg(double last, double now, double delta)
{
return Deg.norm(last + Deg.delta(now, last) * delta);
}
/**
* Get highest number of a list
*
* @param numbers numbers
* @return lowest
*/
public static double max(double... numbers)
{
double highest = numbers[0];
for (double num : numbers) {
if (num > highest) highest = num;
}
return highest;
}
/**
* Get highest number of a list
*
* @param numbers numbers
* @return lowest
*/
public static float max(float... numbers)
{
float highest = numbers[0];
for (float num : numbers) {
if (num > highest) highest = num;
}
return highest;
}
/**
* Get highest number of a list
*
* @param numbers numbers
* @return lowest
*/
public static int max(int... numbers)
{
int highest = numbers[0];
for (int num : numbers) {
if (num > highest) highest = num;
}
return highest;
}
/**
* Get lowest number of a list
*
* @param numbers numbers
* @return lowest
*/
public static double min(double... numbers)
{
double lowest = numbers[0];
for (double num : numbers) {
if (num < lowest) lowest = num;
}
return lowest;
}
/**
* Get lowest number of a list
*
* @param numbers numbers
* @return lowest
*/
public static float min(float... numbers)
{
float lowest = numbers[0];
for (float num : numbers) {
if (num < lowest) lowest = num;
}
return lowest;
}
/**
* Get lowest number of a list
*
* @param numbers numbers
* @return lowest
*/
public static int min(int... numbers)
{
int lowest = numbers[0];
for (int num : numbers) {
if (num < lowest) lowest = num;
}
return lowest;
}
/**
* Split comma separated list of integers.
*
* @param list String containing the list.
* @return array of integers or null.
*/
public static List<Integer> parseIntList(String list)
{
if (list == null) {
return null;
}
String[] parts = list.split(",");
ArrayList<Integer> intList = new ArrayList<Integer>();
for (String part : parts) {
try {
intList.add(Integer.parseInt(part));
} catch (NumberFormatException e) {}
}
return intList;
}
/**
* Pick random element from a given list.
*
* @param list list of choices
* @return picked element
*/
public static Object pick(List<?> list)
{
if (list.size() == 0) return null;
return list.get(rand.nextInt(list.size()));
}
/**
* Square a double
*
* @param a squared double
* @return square
*/
public static double square(double a)
{
return a * a;
}
/**
* Signum.
*
* @param number
* @return sign, -1,0,1
*/
public static int sgn(double number)
{
return number > 0 ? 1 : number < 0 ? -1 : 0;
}
public static double frag(double d)
{
return d - Math.floor(d);
}
}

@ -0,0 +1,107 @@
package mightypork.utils.math;
import mightypork.utils.math.coord.Coord;
/**
* Polar coordinate
*
* @author MightyPork
*/
public class Polar {
/** angle in radians */
public double angle = 0;
/** distance in units */
public double distance = 0;
/**
* @param angle angle in radians
* @param distance distance from origin
*/
public Polar(double angle, double distance) {
this.angle = angle;
this.distance = distance;
}
/**
* Make polar from coord
*
* @param coord coord
* @return polar
*/
public static Polar fromCoord(Coord coord)
{
return new Polar(Math.atan2(coord.y, coord.x), Math.sqrt(Calc.square(coord.x) + Calc.square(coord.y)));
}
/**
* Make polar from coords
*
* @param x x coord
* @param y y coord
* @return polar
*/
public static Polar fromCoord(double x, double y)
{
return Polar.fromCoord(new Coord(x, y));
}
/**
* Make polar from coords
*
* @param x x coord
* @param z z coord
* @return polar
*/
public static Polar fromCoordXZ(double x, double z)
{
return Polar.fromCoordXZ(new Coord(x, 0, z));
}
/**
* Get coord from polar
*
* @return coord
*/
public Coord toCoord()
{
return new Coord(distance * Math.cos(angle), distance * Math.sin(angle));
}
/**
* Get X,0,Y coord from polar
*
* @return coord
*/
public Coord toCoordXZ()
{
return new Coord(distance * Math.cos(angle), 0, distance * Math.sin(angle));
}
@Override
public String toString()
{
return "Polar(theta=" + angle + ", r=" + distance + ")";
}
/**
* Build polar from X,Z instead of X,Y
*
* @param coord cpprd with X,Z
* @return polar
*/
public static Polar fromCoordXZ(Coord coord)
{
return fromCoord(coord.x, coord.z);
}
}

@ -0,0 +1,111 @@
package mightypork.utils.math;
import mightypork.utils.math.Calc.Deg;
import mightypork.utils.math.Calc.Rad;
import mightypork.utils.math.coord.Coord;
/**
* Polar coordinate in degrees
*
* @author MightyPork
*/
public class PolarDeg {
/** angle in degrees */
public double angle = 0;
/** distance in units */
public double distance = 0;
/**
* Polar coordinate in degrees
*
* @param angle angle in degrees
* @param distance distance from origin
*/
public PolarDeg(double angle, double distance) {
this.angle = angle;
this.distance = distance;
}
/**
* Make polar from coord
*
* @param coord coord
* @return polar
*/
public static PolarDeg fromCoord(Coord coord)
{
return new PolarDeg(Rad.toDeg(Math.atan2(coord.y, coord.x)), Math.sqrt(Calc.square(coord.x) + Calc.square(coord.y)));
}
/**
* Make polar from coords
*
* @param x x coord
* @param y y coord
* @return polar
*/
public static PolarDeg fromCoord(double x, double y)
{
return PolarDeg.fromCoord(new Coord(x, y));
}
/**
* Make polar from coords
*
* @param x x coord
* @param z y coord
* @return polar
*/
public static PolarDeg fromCoordXZ(double x, double z)
{
return PolarDeg.fromCoordXZ(new Coord(x, 0, z));
}
/**
* Get coord from polar
*
* @return coord
*/
public Coord toCoord()
{
return new Coord(distance * Math.cos(Deg.toRad(angle)), distance * Math.sin(Deg.toRad(angle)));
}
/**
* Get X,0,Y coord from polar
*
* @return coord
*/
public Coord toCoordXZ()
{
return new Coord(distance * Math.cos(Deg.toRad(angle)), 0, distance * Math.sin(Deg.toRad(angle)));
}
@Override
public String toString()
{
return "Polar(theta=" + angle + ", r=" + distance + ")";
}
/**
* Build polar from X,Z instead of X,Y
*
* @param coord cpprd with X,Z
* @return polar
*/
public static PolarDeg fromCoordXZ(Coord coord)
{
return fromCoord(coord.x, coord.z);
}
}

@ -0,0 +1,197 @@
package mightypork.utils.math;
import java.util.Random;
/**
* Numeric range, able to generate random numbers and give min/max values.
*
* @author MightyPork
*/
public class Range {
private double min = 0;
private double max = 1;
private static Random rand = new Random();
/**
* Implicit range constructor 0-1
*/
public Range() {}
/**
* Create new range
*
* @param min min number
* @param max max number
*/
public Range(double min, double max) {
if (min > max) {
double t = min;
min = max;
max = t;
}
this.min = min;
this.max = max;
}
/**
* Create new range
*
* @param minmax min = max number
*/
public Range(double minmax) {
this.min = minmax;
this.max = minmax;
}
/**
* Get random integer from range
*
* @return random int
*/
public int randInt()
{
return (int) (Math.round(min) + rand.nextInt((int) (Math.round(max) - Math.round(min)) + 1));
}
/**
* Get random double from this range
*
* @return random double
*/
public double randDouble()
{
return min + rand.nextDouble() * (max - min);
}
/**
* Get min
*
* @return min number
*/
public double getMin()
{
return min;
}
/**
* Get max
*
* @return max number
*/
public double getMax()
{
return max;
}
/**
* Get min
*
* @return min number
*/
public int getMinI()
{
return (int) min;
}
/**
* Get max
*
* @return max number
*/
public int getMaxI()
{
return (int) max;
}
/**
* Set min
*
* @param min min value
*/
public void setMin(double min)
{
this.min = min;
}
/**
* Set max
*
* @param max max value
*/
public void setMax(double max)
{
this.max = max;
}
@Override
public String toString()
{
return "Range(" + min + ";" + max + ")";
}
/**
* Get identical copy
*
* @return copy
*/
public Range copy()
{
return new Range(min, max);
}
/**
* Set to value of other range
*
* @param other copied range
*/
public void setTo(Range other)
{
if (other == null) return;
min = other.min;
max = other.max;
if (min > max) {
double t = min;
min = max;
max = t;
}
}
/**
* Set to min-max values
*
* @param min min value
* @param max max value
*/
public void setTo(double min, double max)
{
if (min > max) {
double t = min;
min = max;
max = t;
}
this.min = min;
this.max = max;
}
}

@ -0,0 +1,177 @@
package mightypork.utils.math.color;
import java.awt.Color;
import mightypork.utils.math.Calc;
/**
* HSV color
*
* @author MightyPork
*/
public class HSV {
/** H */
public double h;
/** S */
public double s;
/** V */
public double v;
/**
* Create black color 0,0,0
*/
public HSV() {}
/**
* Color from HSV 0-1
*
* @param h
* @param s
* @param v
*/
public HSV(Number h, Number s, Number v) {
this.h = h.doubleValue();
this.s = s.doubleValue();
this.v = v.doubleValue();
norm();
}
/**
* @return hue 0-1
*/
public double h()
{
return h;
}
/**
* @return saturation 0-1
*/
public double s()
{
return s;
}
/**
* @return value/brightness 0-1
*/
public double v()
{
return v;
}
/**
* Set color to other color
*
* @param copied copied color
* @return this
*/
public HSV setTo(HSV copied)
{
h = copied.h;
s = copied.s;
v = copied.v;
norm();
return this;
}
/**
* Set to H,S,V 0-1
*
* @param h hue
* @param s saturation
* @param v value
* @return this
*/
public HSV setTo(Number h, Number s, Number v)
{
this.h = h.doubleValue();
this.s = s.doubleValue();
this.v = v.doubleValue();
norm();
return this;
}
/**
* Fix numbers out of range 0-1
*/
public void norm()
{
h = Calc.clampd(h, 0, 1);
s = Calc.clampd(s, 0, 1);
v = Calc.clampd(v, 0, 1);
}
/**
* Convert to RGB
*
* @return RGB representation
*/
public RGB toRGB()
{
norm();
int rgb = Color.HSBtoRGB((float) h, (float) s, (float) v);
return RGB.fromHex(rgb);
}
/**
* Make from RGB
*
* @param color RGB
* @return HSV
*/
public static HSV fromRGB(RGB color)
{
return color.toHSV();
}
@Override
public String toString()
{
return "HSV[" + h + ";" + s + ";" + v + "]";
}
@Override
public boolean equals(Object obj)
{
if (obj == null) return false;
if (!(obj instanceof HSV)) return false;
return ((HSV) obj).h == h && ((HSV) obj).s == s && ((HSV) obj).v == v;
}
@Override
public int hashCode()
{
return Double.valueOf(h).hashCode() ^ Double.valueOf(s).hashCode() ^ Double.valueOf(v).hashCode();
}
/**
* Get a copy
*
* @return copy
*/
public HSV copy()
{
return new HSV().setTo(this);
}
}

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

Loading…
Cancel
Save