master
Ondřej Hruška 10 years ago
parent 84789ccf47
commit 137dbc21da
  1. 12
      src/mightypork/rogue/Const.java
  2. 51
      src/mightypork/rogue/Launcher.java
  3. 78
      src/mightypork/rogue/RogueApp.java
  4. 6
      src/mightypork/rogue/RogueConfig.java
  5. 14
      src/mightypork/rogue/RogueKeys.java
  6. 78
      src/mightypork/rogue/RogueResources.java
  7. 4
      src/mightypork/rogue/RogueRoutes.java
  8. 18
      src/mightypork/rogue/RogueStateManager.java
  9. 17
      src/mightypork/rogue/events/LoadingOverlayRequest.java
  10. 22
      src/mightypork/rogue/events/RogueStateRequest.java
  11. 34
      src/mightypork/rogue/screens/FpsOverlay.java
  12. 53
      src/mightypork/rogue/screens/LoadingOverlay.java
  13. 4
      src/mightypork/rogue/screens/RogueScreen.java
  14. 25
      src/mightypork/rogue/screens/game/HeartBar.java
  15. 42
      src/mightypork/rogue/screens/game/IngameNav.java
  16. 81
      src/mightypork/rogue/screens/game/InvSlot.java
  17. 66
      src/mightypork/rogue/screens/game/LayerAskSave.java
  18. 45
      src/mightypork/rogue/screens/game/LayerDeath.java
  19. 89
      src/mightypork/rogue/screens/game/LayerGameUi.java
  20. 151
      src/mightypork/rogue/screens/game/LayerInv.java
  21. 29
      src/mightypork/rogue/screens/game/LayerMapView.java
  22. 40
      src/mightypork/rogue/screens/game/LayerWin.java
  23. 23
      src/mightypork/rogue/screens/game/NavButton.java
  24. 165
      src/mightypork/rogue/screens/game/ScreenGame.java
  25. 58
      src/mightypork/rogue/screens/menu/ScreenMainMenu.java
  26. 55
      src/mightypork/rogue/screens/select_world/ScreenSelectWorld.java
  27. 91
      src/mightypork/rogue/screens/select_world/WorldSlot.java
  28. 118
      src/mightypork/rogue/screens/story/ScreenStory.java
  29. 92
      src/mightypork/rogue/world/Inventory.java
  30. 84
      src/mightypork/rogue/world/PlayerControl.java
  31. 66
      src/mightypork/rogue/world/PlayerData.java
  32. 227
      src/mightypork/rogue/world/PlayerFacade.java
  33. 137
      src/mightypork/rogue/world/World.java
  34. 141
      src/mightypork/rogue/world/WorldConsole.java
  35. 76
      src/mightypork/rogue/world/WorldProvider.java
  36. 95
      src/mightypork/rogue/world/WorldRenderer.java
  37. 21
      src/mightypork/rogue/world/entity/AiTimer.java
  38. 46
      src/mightypork/rogue/world/entity/Entities.java
  39. 159
      src/mightypork/rogue/world/entity/Entity.java
  40. 33
      src/mightypork/rogue/world/entity/EntityModel.java
  41. 27
      src/mightypork/rogue/world/entity/EntityModule.java
  42. 33
      src/mightypork/rogue/world/entity/EntityPathFinder.java
  43. 6
      src/mightypork/rogue/world/entity/EntityRenderer.java
  44. 2
      src/mightypork/rogue/world/entity/EntityType.java
  45. 31
      src/mightypork/rogue/world/entity/impl/BossRatAi.java
  46. 23
      src/mightypork/rogue/world/entity/impl/BrownRatAi.java
  47. 41
      src/mightypork/rogue/world/entity/impl/EntityBossRat.java
  48. 45
      src/mightypork/rogue/world/entity/impl/EntityBrownRat.java
  49. 47
      src/mightypork/rogue/world/entity/impl/EntityGrayRat.java
  50. 89
      src/mightypork/rogue/world/entity/impl/EntityPlayer.java
  51. 27
      src/mightypork/rogue/world/entity/impl/GrayRatAi.java
  52. 203
      src/mightypork/rogue/world/entity/impl/MonsterAi.java
  53. 77
      src/mightypork/rogue/world/entity/modules/EntityModuleHealth.java
  54. 149
      src/mightypork/rogue/world/entity/modules/EntityModulePosition.java
  55. 12
      src/mightypork/rogue/world/entity/modules/EntityMoveListener.java
  56. 109
      src/mightypork/rogue/world/entity/modules/EntityPos.java
  57. 43
      src/mightypork/rogue/world/entity/render/EntityRendererMobLR.java
  58. 2
      src/mightypork/rogue/world/events/GameWinEvent.java
  59. 4
      src/mightypork/rogue/world/events/GameWinHandler.java
  60. 2
      src/mightypork/rogue/world/events/PlayerDeathHandler.java
  61. 4
      src/mightypork/rogue/world/events/PlayerKilledEvent.java
  62. 15
      src/mightypork/rogue/world/events/PlayerStepEndEvent.java
  63. 2
      src/mightypork/rogue/world/events/PlayerStepEndListener.java
  64. 6
      src/mightypork/rogue/world/events/WorldAscendRequest.java
  65. 2
      src/mightypork/rogue/world/events/WorldAscendRequestListener.java
  66. 4
      src/mightypork/rogue/world/events/WorldDescendRequest.java
  67. 2
      src/mightypork/rogue/world/events/WorldDescendRequestListener.java
  68. 25
      src/mightypork/rogue/world/events/WorldPauseRequest.java
  69. 134
      src/mightypork/rogue/world/gen/LevelBuilder.java
  70. 32
      src/mightypork/rogue/world/gen/MapTheme.java
  71. 4
      src/mightypork/rogue/world/gen/RoomBuilder.java
  72. 27
      src/mightypork/rogue/world/gen/RoomEntry.java
  73. 19
      src/mightypork/rogue/world/gen/Rooms.java
  74. 355
      src/mightypork/rogue/world/gen/ScratchMap.java
  75. 80
      src/mightypork/rogue/world/gen/WorldCreator.java
  76. 30
      src/mightypork/rogue/world/gen/WorldGenError.java
  77. 52
      src/mightypork/rogue/world/gen/rooms/AbstractRectRoom.java
  78. 14
      src/mightypork/rogue/world/gen/rooms/BasicRoom.java
  79. 26
      src/mightypork/rogue/world/gen/rooms/BossRoom.java
  80. 14
      src/mightypork/rogue/world/gen/rooms/DeadEndRoom.java
  81. 20
      src/mightypork/rogue/world/gen/rooms/EntranceRoom.java
  82. 20
      src/mightypork/rogue/world/gen/rooms/ExitRoom.java
  83. 23
      src/mightypork/rogue/world/gen/rooms/ItemShrineRoom.java
  84. 14
      src/mightypork/rogue/world/gen/rooms/SecretRoom.java
  85. 18
      src/mightypork/rogue/world/gen/rooms/StorageRoom.java
  86. 13
      src/mightypork/rogue/world/gen/rooms/TreasureChestRoom.java
  87. 30
      src/mightypork/rogue/world/gen/themes/ThemeBrick.java
  88. 98
      src/mightypork/rogue/world/gui/MapView.java
  89. 56
      src/mightypork/rogue/world/gui/Minimap.java
  90. 53
      src/mightypork/rogue/world/gui/WorldConsoleRenderer.java
  91. 61
      src/mightypork/rogue/world/gui/interaction/MIPKeyboard.java
  92. 74
      src/mightypork/rogue/world/gui/interaction/MIPMouse.java
  93. 25
      src/mightypork/rogue/world/gui/interaction/MapInteractionPlugin.java
  94. 125
      src/mightypork/rogue/world/item/Item.java
  95. 33
      src/mightypork/rogue/world/item/ItemModel.java
  96. 15
      src/mightypork/rogue/world/item/ItemRenderer.java
  97. 54
      src/mightypork/rogue/world/item/Items.java
  98. 25
      src/mightypork/rogue/world/item/impl/ItemBaseFood.java
  99. 23
      src/mightypork/rogue/world/item/impl/ItemBaseWeapon.java
  100. 47
      src/mightypork/rogue/world/item/impl/active/ItemHeartPiece.java
  101. Some files were not shown because too many files have changed in this diff Show More

@ -3,24 +3,24 @@ package mightypork.rogue;
/** /**
* Application constants * Application constants
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public final class Const { public final class Const {
// STRINGS // STRINGS
public static final int VERSION = 6; public static final int VERSION = 6;
public static final String APP_NAME = "Rogue: Savage Rats"; public static final String APP_NAME = "Rogue: Savage Rats";
public static final String TITLEBAR = APP_NAME + ", v" + VERSION; public static final String TITLEBAR = APP_NAME + ", v" + VERSION;
// AUDIO // AUDIO
public static final int FPS_RENDER = 120; // max public static final int FPS_RENDER = 120; // max
// INITIAL WINDOW SIZE // INITIAL WINDOW SIZE
public static final int WINDOW_W = 640; public static final int WINDOW_W = 640;
public static final int WINDOW_H = 480; public static final int WINDOW_H = 480;
/** Render dark in unknown area & skip invisible stuff */ /** Render dark in unknown area & skip invisible stuff */
public static boolean RENDER_UFOG = true; public static boolean RENDER_UFOG = true;
} }

@ -4,101 +4,100 @@ package mightypork.rogue;
import java.io.File; import java.io.File;
import java.util.logging.Level; import java.util.logging.Level;
import junk.BaseApp;
import mightypork.gamecore.core.App; import mightypork.gamecore.core.App;
import mightypork.utils.files.OsUtils; import mightypork.utils.files.OsUtils;
public class Launcher { public class Launcher {
/** /**
* Launcher * Launcher
* *
* @param args * @param args
*/ */
public static void main(String[] args) public static void main(String[] args)
{ {
// System.out.println("argv = " + Arrays.toString(args)+"\n"); // System.out.println("argv = " + Arrays.toString(args)+"\n");
Level llSyso = Level.FINER; Level llSyso = Level.FINER;
final Level llFile = Level.ALL; final Level llFile = Level.ALL;
File workdir = null; File workdir = null;
boolean logBus = false; boolean logBus = false;
try { try {
boolean localWorkdir = false; boolean localWorkdir = false;
String lwdDir = null; String lwdDir = null;
for (int i = 0; i < args.length; i++) { for (int i = 0; i < args.length; i++) {
final String arg = args[i]; final String arg = args[i];
if (arg.equals("--workdir") || arg.equals("-w")) { if (arg.equals("--workdir") || arg.equals("-w")) {
localWorkdir = true; localWorkdir = true;
lwdDir = args[i + 1]; lwdDir = args[i + 1];
i++; i++;
continue; continue;
} else if (arg.equals("--silent") || arg.equals("-s")) { } else if (arg.equals("--silent") || arg.equals("-s")) {
llSyso = Level.OFF; llSyso = Level.OFF;
continue; continue;
} else if (arg.equals("--warn") || arg.equals("-e")) { } else if (arg.equals("--warn") || arg.equals("-e")) {
llSyso = Level.WARNING; llSyso = Level.WARNING;
continue; continue;
} else if (arg.equals("--verbose") || arg.equals("-v")) { } else if (arg.equals("--verbose") || arg.equals("-v")) {
llSyso = Level.ALL; llSyso = Level.ALL;
continue; continue;
} else if (arg.equals("--help") || arg.equals("-h")) { } else if (arg.equals("--help") || arg.equals("-h")) {
printHelp(); printHelp();
System.exit(0); System.exit(0);
} else if (arg.equals("--debug-bus")) { } else if (arg.equals("--debug-bus")) {
logBus = true; logBus = true;
} else { } else {
System.err.println("Unknown argument: " + arg); System.err.println("Unknown argument: " + arg);
printHelp(); printHelp();
System.exit(1); System.exit(1);
} }
} }
if (!localWorkdir) { if (!localWorkdir) {
workdir = OsUtils.getHomeWorkDir(".rogue"); workdir = OsUtils.getHomeWorkDir(".rogue");
} else { } else {
workdir = new File(lwdDir); workdir = new File(lwdDir);
} }
} catch (final Exception e) { } catch (final Exception e) {
System.out.println("Error parsing arguments:"); System.out.println("Error parsing arguments:");
e.printStackTrace(); e.printStackTrace();
printHelp(); printHelp();
System.exit(1); System.exit(1);
} }
final App app = new RogueApp(); final App app = new RogueApp();
app.getInitOptions().setWorkdir(workdir); app.getInitOptions().setWorkdir(workdir);
app.getInitOptions().setSigleInstance(true); app.getInitOptions().setSigleInstance(true);
app.getInitOptions().setLogLevel(llFile, llSyso); app.getInitOptions().setLogLevel(llFile, llSyso);
app.getInitOptions().setBusLogging(logBus); app.getInitOptions().setBusLogging(logBus);
app.start(); app.start();
} }
private static void printHelp() private static void printHelp()
{ {
//@formatter:off //@formatter:off
System.out.println( System.out.println(
"Arguments:\n" + "Arguments:\n" +
"\t--workdir <path>, -w <path> .... specify working directory\n" + "\t--workdir <path>, -w <path> .... specify working directory\n" +
"\t--verbose, --debug, -v ......... print all messages\n" + "\t--verbose, --debug, -v ......... print all messages\n" +
"\t--silent, -s ................... print no messages\n" + "\t--silent, -s ................... print no messages\n" +
"\t--warnings, -e ................. print only warning and error messages\n" + "\t--warnings, -e ................. print only warning and error messages\n" +
"\t--help, -h ..................... show this help\n"); "\t--help, -h ..................... show this help\n");
//@formatter:on //@formatter:on
} }

@ -2,7 +2,6 @@ package mightypork.rogue;
import junk.AppInitOptions; import junk.AppInitOptions;
import junk.BaseApp;
import mightypork.gamecore.backends.lwjgl.LwjglBackend; import mightypork.gamecore.backends.lwjgl.LwjglBackend;
import mightypork.gamecore.backends.lwjgl.LwjglInputModule; import mightypork.gamecore.backends.lwjgl.LwjglInputModule;
import mightypork.gamecore.core.App; import mightypork.gamecore.core.App;
@ -35,37 +34,38 @@ import mightypork.utils.logging.Log;
/** /**
* Main class * Main class
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public final class RogueApp extends App implements ViewportChangeListener { public final class RogueApp extends App implements ViewportChangeListener {
public RogueApp() { public RogueApp()
{
super(new LwjglBackend()); super(new LwjglBackend());
AppInitOptions opt = getInitOptions(); final AppInitOptions opt = getInitOptions();
opt.addRoutes(new RogueRoutes()); opt.addRoutes(new RogueRoutes());
opt.addResources(new RogueResources()); opt.addResources(new RogueResources());
opt.addKeys(new RogueKeys()); opt.addKeys(new RogueKeys());
opt.addConfig(new RogueConfig()); opt.addConfig(new RogueConfig());
opt.setBusLogging(true); opt.setBusLogging(true);
opt.setConfigFile("config.ini", "Rogue config file"); opt.setConfigFile("config.ini", "Rogue config file");
opt.setLogOptions("logs", "runtime", 5, java.util.logging.Level.ALL); opt.setLogOptions("logs", "runtime", 5, java.util.logging.Level.ALL);
} }
@Override @Override
protected void registerIonizables() protected void registerIonizables()
{ {
super.registerIonizables(); super.registerIonizables();
Ion.register(Level.class); Ion.register(Level.class);
Ion.register(Inventory.class); Ion.register(Inventory.class);
} }
@Override @Override
protected void initDisplay(GraphicsModule gfx) protected void initDisplay(GraphicsModule gfx)
{ {
@ -73,52 +73,52 @@ public final class RogueApp extends App implements ViewportChangeListener {
final int w = Config.getValue("display.width"); final int w = Config.getValue("display.width");
final int h = Config.getValue("display.height"); final int h = Config.getValue("display.height");
final boolean fs = Config.getValue("display.fullscreen"); final boolean fs = Config.getValue("display.fullscreen");
gfx.setSize(w, h); gfx.setSize(w, h);
gfx.setResizable(true); gfx.setResizable(true);
gfx.setFullscreen(fs); gfx.setFullscreen(fs);
gfx.setTitle(Const.TITLEBAR); gfx.setTitle(Const.TITLEBAR);
gfx.setTargetFps(Const.FPS_RENDER); gfx.setTargetFps(Const.FPS_RENDER);
gfx.createDisplay(); gfx.createDisplay();
} }
@Override @Override
protected void initScreens(ScreenRegistry screens) protected void initScreens(ScreenRegistry screens)
{ {
super.initScreens(screens); super.initScreens(screens);
/* game screen references world provider instance */ /* game screen references world provider instance */
WorldProvider.init(this); WorldProvider.setBaseDir(this);
getEventBus().subscribe(new RogueStateManager(this)); getEventBus().subscribe(new RogueStateManager(this));
screens.addScreen("main_menu", new ScreenMainMenu(this)); screens.addScreen("main_menu", new ScreenMainMenu(this));
screens.addScreen("select_world", new ScreenSelectWorld(this)); screens.addScreen("select_world", new ScreenSelectWorld(this));
screens.addScreen("game", new ScreenGame(this)); screens.addScreen("game", new ScreenGame(this));
screens.addScreen("story", new ScreenStory(this)); screens.addScreen("story", new ScreenStory(this));
screens.addOverlay(new FpsOverlay(this)); screens.addOverlay(new FpsOverlay(this));
screens.addOverlay(new LoadingOverlay(this)); screens.addOverlay(new LoadingOverlay(this));
} }
@Override @Override
protected void initInputSystem(LwjglInputModule input) protected void initInputSystem(LwjglInputModule input)
{ {
// this will work only with reusable events (such as requests) // this will work only with reusable events (such as requests)
bindEventToKey(new FullscreenToggleRequest(), "global.fullscreen"); bindEventToKey(new FullscreenToggleRequest(), "global.fullscreen");
bindEventToKey(new ScreenshotRequest(), "global.screenshot"); bindEventToKey(new ScreenshotRequest(), "global.screenshot");
bindEventToKey(new ShutdownEvent(), "global.quit"); bindEventToKey(new ShutdownEvent(), "global.quit");
bindEventToKey(new ShutdownEvent(), "global.quit_force"); bindEventToKey(new ShutdownEvent(), "global.quit_force");
} }
private void bindEventToKey(final BusEvent<?> event, String strokeName) private void bindEventToKey(final BusEvent<?> event, String strokeName)
{ {
getInput().bindKey(Config.getKeyStroke(strokeName), Trigger.RISING, new Runnable() { getInput().bindKey(Config.getKeyStroke(strokeName), Trigger.RISING, new Runnable() {
@Override @Override
public void run() public void run()
{ {
@ -126,14 +126,14 @@ public final class RogueApp extends App implements ViewportChangeListener {
} }
}); });
} }
@Override @Override
protected void postInit() protected void postInit()
{ {
getEventBus().send(new MainLoopRequest(new Runnable() { getEventBus().send(new MainLoopRequest(new Runnable() {
@Override @Override
public void run() public void run()
{ {
@ -146,31 +146,31 @@ public final class RogueApp extends App implements ViewportChangeListener {
} }
}, false)); }, false));
} }
@Override @Override
public void onViewportChanged(ViewportChangeEvent event) public void onViewportChanged(ViewportChangeEvent event)
{ {
// save viewport size to config file // save viewport size to config file
final boolean fs = gfx().isFullscreen(); final boolean fs = gfx().isFullscreen();
Config.setValue("display.fullscreen", fs); Config.setValue("display.fullscreen", fs);
if (!fs) { if (!fs) {
Config.setValue("display.width", gfx().getWidth()); Config.setValue("display.width", gfx().getWidth());
Config.setValue("display.height", gfx().getHeight()); Config.setValue("display.height", gfx().getHeight());
} }
} }
@Override @Override
public void onScreenshotRequest() public void onScreenshotRequest()
{ {
// screenshot sound // screenshot sound
Res.getSoundEffect("gui.shutter").play(0.8); Res.getSoundEffect("gui.shutter").play(0.8);
} }
@Override @Override
protected void writeLogHeader() protected void writeLogHeader()
{ {

@ -5,15 +5,15 @@ import mightypork.utils.config.propmgr.PropertyManager;
public class RogueConfig implements ConfigSetup { public class RogueConfig implements ConfigSetup {
@Override @Override
public void addOptions(PropertyManager prop) public void addOptions(PropertyManager prop)
{ {
prop.addBoolean("display.fullscreen", false, "Start in fullscreen (remembers state at exit)"); prop.addBoolean("display.fullscreen", false, "Start in fullscreen (remembers state at exit)");
prop.addInteger("display.width", 1024, "Initial width (remembers from last time)"); prop.addInteger("display.width", 1024, "Initial width (remembers from last time)");
prop.addInteger("display.height", 768, "Initial height (remembers from last time)"); prop.addInteger("display.height", 768, "Initial height (remembers from last time)");
prop.addBoolean("opt.show_story", true, "Show story on start-up."); prop.addBoolean("opt.show_story", true, "Show story on start-up.");
} }
} }

@ -2,23 +2,23 @@ package mightypork.rogue;
public class RogueKeys implements KeySetup { public class RogueKeys implements KeySetup {
@Override @Override
public void addKeys(KeyOpts keys) public void addKeys(KeyOpts keys)
{ {
keys.addKey("global.quit", "CTRL+Q", "Quit the game"); keys.addKey("global.quit", "CTRL+Q", "Quit the game");
keys.addKey("global.quit_force", "CTRL+SHIFT+Q", "Quit the game without asking, low-level"); keys.addKey("global.quit_force", "CTRL+SHIFT+Q", "Quit the game without asking, low-level");
keys.addKey("global.screenshot", "F2", "Take screenshot (save into working directory)"); keys.addKey("global.screenshot", "F2", "Take screenshot (save into working directory)");
keys.addKey("global.fullscreen", "F11", "Toggle fullscreen"); keys.addKey("global.fullscreen", "F11", "Toggle fullscreen");
keys.addKey("global.fps_meter", "F3", "Toggle FPS meter overlay"); keys.addKey("global.fps_meter", "F3", "Toggle FPS meter overlay");
keys.addKey("general.close", "ESC", "Leave a dialog or screen"); keys.addKey("general.close", "ESC", "Leave a dialog or screen");
keys.addKey("general.cancel", "ESC", "\"Cancel\" option in dialogs"); keys.addKey("general.cancel", "ESC", "\"Cancel\" option in dialogs");
keys.addKey("general.confirm", "ENTER", "\"Confirm\" option in dialogs"); keys.addKey("general.confirm", "ENTER", "\"Confirm\" option in dialogs");
keys.addKey("general.yes", "Y", "\"Yes\" option in dialogs"); keys.addKey("general.yes", "Y", "\"Yes\" option in dialogs");
keys.addKey("general.no", "N", "\"No\" option in dialogs"); keys.addKey("general.no", "N", "\"No\" option in dialogs");
keys.addKey("game.quit", "ESC", "Quit to menu"); keys.addKey("game.quit", "ESC", "Quit to menu");
keys.addKey("game.save", "CTRL+S", "Save to file"); keys.addKey("game.save", "CTRL+S", "Save to file");
keys.addKey("game.load", "CTRL+L", "Load from file"); keys.addKey("game.load", "CTRL+L", "Load from file");
@ -28,14 +28,14 @@ public class RogueKeys implements KeySetup {
keys.addKey("game.drop", "D", "Drop last picked item"); keys.addKey("game.drop", "D", "Drop last picked item");
keys.addKey("game.inventory", "I", "Toggle inventory view"); keys.addKey("game.inventory", "I", "Toggle inventory view");
keys.addKey("game.pause", "P", "Pause the game"); keys.addKey("game.pause", "P", "Pause the game");
keys.addKey("game.walk.up", "UP", "Walk north"); keys.addKey("game.walk.up", "UP", "Walk north");
keys.addKey("game.walk.down", "DOWN", "Walk south"); keys.addKey("game.walk.down", "DOWN", "Walk south");
keys.addKey("game.walk.left", "LEFT", "Walk west"); keys.addKey("game.walk.left", "LEFT", "Walk west");
keys.addKey("game.walk.right", "RIGHT", "Walk east"); keys.addKey("game.walk.right", "RIGHT", "Walk east");
keys.addKey("game.cheat.xray", "CTRL+SHIFT+X", "Cheat to see unexplored tiles"); keys.addKey("game.cheat.xray", "CTRL+SHIFT+X", "Cheat to see unexplored tiles");
keys.addKey("game.inv.use", "E", "Use (eat or equip) the selected item"); keys.addKey("game.inv.use", "E", "Use (eat or equip) the selected item");
keys.addKey("game.inv.drop", "D", "Drop the selected item"); keys.addKey("game.inv.drop", "D", "Drop the selected item");
keys.addKey("game.inv.move.left", "LEFT", "Move inventory cursor left"); keys.addKey("game.inv.move.left", "LEFT", "Move inventory cursor left");

@ -2,7 +2,7 @@ package mightypork.rogue;
import mightypork.gamecore.audio.SoundRegistry; import mightypork.gamecore.audio.SoundRegistry;
import mightypork.gamecore.backends.lwjgl.graphics.font.DeferredLwjglFont; import mightypork.gamecore.backends.lwjgl.graphics.font.LwjglFont;
import mightypork.gamecore.graphics.fonts.DeferredFont; import mightypork.gamecore.graphics.fonts.DeferredFont;
import mightypork.gamecore.graphics.fonts.FontRegistry; import mightypork.gamecore.graphics.fonts.FontRegistry;
import mightypork.gamecore.graphics.fonts.Glyphs; import mightypork.gamecore.graphics.fonts.Glyphs;
@ -16,72 +16,72 @@ import mightypork.utils.math.constraints.rect.Rect;
public class RogueResources implements ResourceSetup { public class RogueResources implements ResourceSetup {
@Override @Override
public void addFonts(FontRegistry fonts) public void addFonts(FontRegistry fonts)
{ {
DeferredFont font; DeferredFont font;
//fonts.loadFont("polygon_pixel", new DeferredFont("/res/font/PolygonPixel5x7Standard.ttf", Glyphs.basic, 16)); //fonts.loadFont("polygon_pixel", new DeferredFont("/res/font/PolygonPixel5x7Standard.ttf", Glyphs.basic, 16));
fonts.addFont("press_start", font = new DeferredLwjglFont("/res/font/PressStart2P.ttf", Glyphs.basic, 16)); fonts.addFont("press_start", font = new LwjglFont("/res/font/PressStart2P.ttf", Glyphs.basic, 16));
fonts.addFont("battlenet", font = new DeferredLwjglFont("/res/font/battlenet.ttf", Glyphs.basic, 16)); fonts.addFont("battlenet", font = new LwjglFont("/res/font/battlenet.ttf", Glyphs.basic, 16));
font.setDiscardRatio(3 / 16D, 2 / 16D); font.setDiscardRatio(3 / 16D, 2 / 16D);
fonts.addFont("tinyutf", font = new DeferredLwjglFont("/res/font/TinyUnicode2.ttf", Glyphs.basic, 16)); fonts.addFont("tinyutf", font = new LwjglFont("/res/font/TinyUnicode2.ttf", Glyphs.basic, 16));
font.setDiscardRatio(5 / 16D, 3 / 16D); font.setDiscardRatio(5 / 16D, 3 / 16D);
// aliases // aliases
fonts.addAlias("thick", "press_start"); fonts.addAlias("thick", "press_start");
fonts.addAlias("thin", "battlenet"); fonts.addAlias("thin", "battlenet");
fonts.addAlias("tiny", "tinyutf"); fonts.addAlias("tiny", "tinyutf");
} }
@Override @Override
public void addSounds(SoundRegistry sounds) public void addSounds(SoundRegistry sounds)
{ {
//sounds.addLoop("music.dungeon", "/res/audio/music/Lightless_Dawn.ogg", 1, 1, 3, 1.5); //sounds.addLoop("music.dungeon", "/res/audio/music/Lightless_Dawn.ogg", 1, 1, 3, 1.5);
sounds.addLoop("music.menu", "/res/audio/music/Home_Base_Groove.ogg", 1, 0.7, 3, 1.5); sounds.addLoop("music.menu", "/res/audio/music/Home_Base_Groove.ogg", 1, 0.7, 3, 1.5);
sounds.addLoop("music.dungeon", "/res/audio/music/8bit_Dungeon_Level.ogg", 1, 0.6, 3, 1.5); sounds.addLoop("music.dungeon", "/res/audio/music/8bit_Dungeon_Level.ogg", 1, 0.6, 3, 1.5);
//sounds.addLoop("music.dungeon2.boss", "/res/audio/music/8bit_Dungeon_Boss.ogg", 1, 0.6, 3, 1.5); //sounds.addLoop("music.dungeon2.boss", "/res/audio/music/8bit_Dungeon_Boss.ogg", 1, 0.6, 3, 1.5);
sounds.addEffect("gui.shutter", "/res/audio/effects/shutter.ogg", 1, 1); sounds.addEffect("gui.shutter", "/res/audio/effects/shutter.ogg", 1, 1);
sounds.addEffect("gui.click", "/res/audio/effects/click.ogg", 1, 1); sounds.addEffect("gui.click", "/res/audio/effects/click.ogg", 1, 1);
sounds.addEffect("game.win", "/res/audio/effects/win.ogg", 1, 0.7); sounds.addEffect("game.win", "/res/audio/effects/win.ogg", 1, 0.7);
sounds.addEffect("game.lose", "/res/audio/effects/lose.ogg", 1, 1); sounds.addEffect("game.lose", "/res/audio/effects/lose.ogg", 1, 1);
sounds.addEffect("item.drop", "/res/audio/effects/drop.ogg", 1, 1); sounds.addEffect("item.drop", "/res/audio/effects/drop.ogg", 1, 1);
sounds.addEffect("item.pickup", "/res/audio/effects/pickup.ogg", 1, 1); sounds.addEffect("item.pickup", "/res/audio/effects/pickup.ogg", 1, 1);
sounds.addEffect("item.break", "/res/audio/effects/break.ogg", 1, 1); sounds.addEffect("item.break", "/res/audio/effects/break.ogg", 1, 1);
sounds.addEffect("player.eat", "/res/audio/effects/eat.ogg", 1, 1); sounds.addEffect("player.eat", "/res/audio/effects/eat.ogg", 1, 1);
sounds.addEffect("player.hurt", "/res/audio/effects/hurt.ogg", 1, 1); sounds.addEffect("player.hurt", "/res/audio/effects/hurt.ogg", 1, 1);
sounds.addEffect("crate.open", "/res/audio/effects/crate.ogg", 1, 1); sounds.addEffect("crate.open", "/res/audio/effects/crate.ogg", 1, 1);
sounds.addEffect("door.open", "/res/audio/effects/door.ogg", 1, 1); sounds.addEffect("door.open", "/res/audio/effects/door.ogg", 1, 1);
sounds.addEffect("door.close", "/res/audio/effects/door.ogg", 0.8, 1); sounds.addEffect("door.close", "/res/audio/effects/door.ogg", 0.8, 1);
sounds.addEffect("rat.sqeak1", "/res/audio/effects/mouse1.ogg", 1, 1); sounds.addEffect("rat.sqeak1", "/res/audio/effects/mouse1.ogg", 1, 1);
sounds.addEffect("rat.sqeak2", "/res/audio/effects/mouse2.ogg", 1, 1); sounds.addEffect("rat.sqeak2", "/res/audio/effects/mouse2.ogg", 1, 1);
sounds.addEffect("rat.sqeak3", "/res/audio/effects/mouse3.ogg", 1, 1); sounds.addEffect("rat.sqeak3", "/res/audio/effects/mouse3.ogg", 1, 1);
sounds.addEffect("rat.sqeak4", "/res/audio/effects/mouse4.ogg", 1, 1); sounds.addEffect("rat.sqeak4", "/res/audio/effects/mouse4.ogg", 1, 1);
sounds.addEffect("rat.sqeak5", "/res/audio/effects/mouse5.ogg", 1, 1); sounds.addEffect("rat.sqeak5", "/res/audio/effects/mouse5.ogg", 1, 1);
} }
@Override @Override
public void addTextures(TextureRegistry textures) public void addTextures(TextureRegistry textures)
{ {
ITexture texture; ITexture texture;
QuadGrid grid; QuadGrid grid;
// gui // gui
texture = textures.addTexture("/res/img/gui.png", FilterMode.NEAREST, WrapMode.CLAMP); texture = textures.addTexture("/res/img/gui.png", FilterMode.NEAREST, WrapMode.CLAMP);
// small gui elements // small gui elements
grid = texture.grid(32, 32); grid = texture.grid(32, 32);
textures.add("hud.heart.on", grid.makeQuad(0, 0)); textures.add("hud.heart.on", grid.makeQuad(0, 0));
@ -89,14 +89,14 @@ public class RogueResources implements ResourceSetup {
textures.add("hud.heart.half", grid.makeQuad(2, 0)); textures.add("hud.heart.half", grid.makeQuad(2, 0));
textures.add("hud.xp.on", grid.makeQuad(0, 1)); textures.add("hud.xp.on", grid.makeQuad(0, 1));
textures.add("hud.xp.off", grid.makeQuad(1, 1)); textures.add("hud.xp.off", grid.makeQuad(1, 1));
// nav // nav
grid = texture.grid(8, 8); grid = texture.grid(8, 8);
textures.add("nav.bg", grid.makeQuad(0, 7, 4, 1)); textures.add("nav.bg", grid.makeQuad(0, 7, 4, 1));
textures.add("nav.button.bg.base", grid.makeQuad(4, 7)); textures.add("nav.button.bg.base", grid.makeQuad(4, 7));
textures.add("nav.button.bg.hover", grid.makeQuad(5, 7)); textures.add("nav.button.bg.hover", grid.makeQuad(5, 7));
textures.add("nav.button.bg.down", grid.makeQuad(6, 7)); textures.add("nav.button.bg.down", grid.makeQuad(6, 7));
textures.add("nav.button.fg.eat", grid.makeQuad(0, 6)); textures.add("nav.button.fg.eat", grid.makeQuad(0, 6));
textures.add("nav.button.fg.inventory", grid.makeQuad(1, 6)); textures.add("nav.button.fg.inventory", grid.makeQuad(1, 6));
textures.add("nav.button.fg.attack", grid.makeQuad(2, 6)); textures.add("nav.button.fg.attack", grid.makeQuad(2, 6));
@ -107,10 +107,10 @@ public class RogueResources implements ResourceSetup {
textures.add("nav.button.fg.magnify", grid.makeQuad(7, 6)); textures.add("nav.button.fg.magnify", grid.makeQuad(7, 6));
textures.add("nav.button.fg.save", grid.makeQuad(7, 5)); textures.add("nav.button.fg.save", grid.makeQuad(7, 5));
textures.add("nav.button.fg.load", grid.makeQuad(6, 5)); textures.add("nav.button.fg.load", grid.makeQuad(6, 5));
textures.add("inv.slot.base", grid.makeQuad(0, 5)); textures.add("inv.slot.base", grid.makeQuad(0, 5));
textures.add("inv.slot.selected", grid.makeQuad(1, 5)); textures.add("inv.slot.selected", grid.makeQuad(1, 5));
// sprites // sprites
texture = textures.addTexture("/res/img/sprites.png", FilterMode.NEAREST, WrapMode.CLAMP); texture = textures.addTexture("/res/img/sprites.png", FilterMode.NEAREST, WrapMode.CLAMP);
grid = texture.grid(8, 8); grid = texture.grid(8, 8);
@ -119,7 +119,7 @@ public class RogueResources implements ResourceSetup {
textures.add("sprite.rat.brown", grid.makeSheet(0, 2, 4, 1)); textures.add("sprite.rat.brown", grid.makeSheet(0, 2, 4, 1));
textures.add("sprite.rat.boss", grid.makeSheet(0, 3, 4, 1)); textures.add("sprite.rat.boss", grid.makeSheet(0, 3, 4, 1));
textures.add("sprite.zzz", grid.makeQuad(0, 7)); // sleep thingy textures.add("sprite.zzz", grid.makeQuad(0, 7)); // sleep thingy
// logo // logo
texture = textures.addTexture("/res/img/logo.png", FilterMode.NEAREST, WrapMode.CLAMP); texture = textures.addTexture("/res/img/logo.png", FilterMode.NEAREST, WrapMode.CLAMP);
textures.add("logo", texture.makeQuad(Rect.make(0, 0, 0.543, 0.203))); textures.add("logo", texture.makeQuad(Rect.make(0, 0, 0.543, 0.203)));
@ -127,52 +127,52 @@ public class RogueResources implements ResourceSetup {
textures.add("death", grid.makeQuad(0, 2)); textures.add("death", grid.makeQuad(0, 2));
textures.add("death2", grid.makeQuad(1, 2, 1.5, 1)); textures.add("death2", grid.makeQuad(1, 2, 1.5, 1));
textures.add("win", grid.makeQuad(2.5, 2, 1.5, 1)); textures.add("win", grid.makeQuad(2.5, 2, 1.5, 1));
grid = texture.grid(8, 4); grid = texture.grid(8, 4);
textures.add("story_1", grid.makeQuad(0, 2, 3, 1)); textures.add("story_1", grid.makeQuad(0, 2, 3, 1));
textures.add("story_2", grid.makeQuad(3, 2, 3, 1)); textures.add("story_2", grid.makeQuad(3, 2, 3, 1));
textures.add("story_3", grid.makeQuad(0, 3, 3, 1)); textures.add("story_3", grid.makeQuad(0, 3, 3, 1));
// tiles // tiles
texture = textures.addTexture("tiles", "/res/img/tiles.png", FilterMode.NEAREST, WrapMode.CLAMP); texture = textures.addTexture("tiles", "/res/img/tiles.png", FilterMode.NEAREST, WrapMode.CLAMP);
grid = texture.grid(8, 8); grid = texture.grid(8, 8);
textures.add("tile.brick.floor", grid.makeSheet(0, 1, 5, 1)); textures.add("tile.brick.floor", grid.makeSheet(0, 1, 5, 1));
textures.add("tile.brick.wall", grid.makeSheet(0, 0, 8, 1)); textures.add("tile.brick.wall", grid.makeSheet(0, 0, 8, 1));
textures.add("tile.brick.door.closed", grid.makeQuad(1, 2)); textures.add("tile.brick.door.closed", grid.makeQuad(1, 2));
textures.add("tile.brick.door.open", grid.makeQuad(2, 2)); textures.add("tile.brick.door.open", grid.makeQuad(2, 2));
textures.add("tile.brick.door.secret", grid.makeSheet(0, 3, 2, 1)); textures.add("tile.brick.door.secret", grid.makeSheet(0, 3, 2, 1));
textures.add("tile.brick.passage", grid.makeSheet(3, 2, 4, 1)); textures.add("tile.brick.passage", grid.makeSheet(3, 2, 4, 1));
textures.add("tile.brick.stairs.up", grid.makeQuad(0, 6)); textures.add("tile.brick.stairs.up", grid.makeQuad(0, 6));
textures.add("tile.brick.stairs.down", grid.makeQuad(1, 6)); textures.add("tile.brick.stairs.down", grid.makeQuad(1, 6));
textures.add("tile.extra.chest.closed", grid.makeQuad(0, 4)); textures.add("tile.extra.chest.closed", grid.makeQuad(0, 4));
textures.add("tile.extra.chest.open", grid.makeQuad(1, 4)); textures.add("tile.extra.chest.open", grid.makeQuad(1, 4));
// shadows // shadows
textures.add("tile.shadow.n", grid.makeQuad(0, 7)); textures.add("tile.shadow.n", grid.makeQuad(0, 7));
textures.add("tile.shadow.s", grid.makeQuad(0, 7).flipY()); textures.add("tile.shadow.s", grid.makeQuad(0, 7).flipY());
textures.add("tile.shadow.w", grid.makeQuad(1, 7)); textures.add("tile.shadow.w", grid.makeQuad(1, 7));
textures.add("tile.shadow.e", grid.makeQuad(1, 7).flipX()); textures.add("tile.shadow.e", grid.makeQuad(1, 7).flipX());
textures.add("tile.shadow.nw", grid.makeQuad(2, 7)); textures.add("tile.shadow.nw", grid.makeQuad(2, 7));
textures.add("tile.shadow.ne", grid.makeQuad(2, 7).flipX()); textures.add("tile.shadow.ne", grid.makeQuad(2, 7).flipX());
textures.add("tile.shadow.sw", grid.makeQuad(2, 7).flipY()); textures.add("tile.shadow.sw", grid.makeQuad(2, 7).flipY());
textures.add("tile.shadow.se", grid.makeQuad(2, 7).flipY().flipX()); textures.add("tile.shadow.se", grid.makeQuad(2, 7).flipY().flipX());
// unexplored fog // unexplored fog
textures.add("tile.ufog.n", grid.makeQuad(3, 7)); textures.add("tile.ufog.n", grid.makeQuad(3, 7));
textures.add("tile.ufog.s", grid.makeQuad(3, 7).flipY()); textures.add("tile.ufog.s", grid.makeQuad(3, 7).flipY());
textures.add("tile.ufog.w", grid.makeQuad(4, 7)); textures.add("tile.ufog.w", grid.makeQuad(4, 7));
textures.add("tile.ufog.e", grid.makeQuad(4, 7).flipX()); textures.add("tile.ufog.e", grid.makeQuad(4, 7).flipX());
textures.add("tile.ufog.nw", grid.makeQuad(5, 7)); textures.add("tile.ufog.nw", grid.makeQuad(5, 7));
textures.add("tile.ufog.ne", grid.makeQuad(5, 7).flipX()); textures.add("tile.ufog.ne", grid.makeQuad(5, 7).flipX());
textures.add("tile.ufog.sw", grid.makeQuad(5, 7).flipY()); textures.add("tile.ufog.sw", grid.makeQuad(5, 7).flipY());
textures.add("tile.ufog.se", grid.makeQuad(5, 7).flipY().flipX()); textures.add("tile.ufog.se", grid.makeQuad(5, 7).flipY().flipX());
textures.add("tile.ufog.full", grid.makeQuad(6, 7)); textures.add("tile.ufog.full", grid.makeQuad(6, 7));
texture = textures.addTexture("items", "/res/img/items.png", FilterMode.NEAREST, WrapMode.CLAMP); texture = textures.addTexture("items", "/res/img/items.png", FilterMode.NEAREST, WrapMode.CLAMP);
grid = texture.grid(8, 8); grid = texture.grid(8, 8);
textures.add("item.meat", grid.makeQuad(0, 0)); textures.add("item.meat", grid.makeQuad(0, 0));
@ -187,5 +187,5 @@ public class RogueResources implements ResourceSetup {
textures.add("item.knife", grid.makeQuad(1, 1)); textures.add("item.knife", grid.makeQuad(1, 1));
textures.add("item.twig", grid.makeQuad(2, 1)); textures.add("item.twig", grid.makeQuad(2, 1));
} }
} }

@ -2,7 +2,7 @@ package mightypork.rogue;
public class RogueRoutes implements RouteSetup { public class RogueRoutes implements RouteSetup {
@Override @Override
public void addRoutes(RouteOpts routeOpts) public void addRoutes(RouteOpts routeOpts)
{ {
@ -10,5 +10,5 @@ public class RogueRoutes implements RouteSetup {
routeOpts.addPath("slot2", "saves/slot_2.ion"); routeOpts.addPath("slot2", "saves/slot_2.ion");
routeOpts.addPath("slot3", "saves/slot_3.ion"); routeOpts.addPath("slot3", "saves/slot_3.ion");
} }
} }

@ -8,40 +8,40 @@ import mightypork.utils.logging.Log;
public class RogueStateManager extends BusNode { public class RogueStateManager extends BusNode {
public static enum RogueState public static enum RogueState
{ {
MAIN_MENU, SELECT_WORLD, PLAY_WORLD, EXIT, STORY MAIN_MENU, SELECT_WORLD, PLAY_WORLD, EXIT, STORY
} }
public void triggerAction(RogueState state, boolean fromDark) public void triggerAction(RogueState state, boolean fromDark)
{ {
switch (state) { switch (state) {
case MAIN_MENU: case MAIN_MENU:
App.bus().send(new CrossfadeRequest("main_menu", fromDark)); App.bus().send(new CrossfadeRequest("main_menu", fromDark));
break; break;
case SELECT_WORLD: case SELECT_WORLD:
App.bus().send(new CrossfadeRequest("select_world", fromDark)); App.bus().send(new CrossfadeRequest("select_world", fromDark));
break; break;
case PLAY_WORLD: case PLAY_WORLD:
App.bus().send(new CrossfadeRequest("game", fromDark)); App.bus().send(new CrossfadeRequest("game", fromDark));
break; break;
case STORY: case STORY:
App.bus().send(new CrossfadeRequest("story", fromDark)); App.bus().send(new CrossfadeRequest("story", fromDark));
break; break;
case EXIT: case EXIT:
App.bus().send(new CrossfadeRequest(null)); App.bus().send(new CrossfadeRequest(null));
break; break;
default: default:
Log.w("Unknown action: " + state); Log.w("Unknown action: " + state);
break; break;
} }
} }
} }

@ -8,30 +8,31 @@ import mightypork.utils.eventbus.events.flags.SingleReceiverEvent;
/** /**
* Request to execute a given task in a loading overlay * Request to execute a given task in a loading overlay
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
@SingleReceiverEvent @SingleReceiverEvent
public class LoadingOverlayRequest extends BusEvent<LoadingOverlay> { public class LoadingOverlayRequest extends BusEvent<LoadingOverlay> {
private final String msg; private final String msg;
private final Runnable task; private final Runnable task;
/** /**
* @param msg task description * @param msg task description
* @param task task runnable * @param task task runnable
*/ */
public LoadingOverlayRequest(String msg, Runnable task) { public LoadingOverlayRequest(String msg, Runnable task)
{
this.task = task; this.task = task;
this.msg = msg; this.msg = msg;
} }
@Override @Override
protected void handleBy(LoadingOverlay handler) protected void handleBy(LoadingOverlay handler)
{ {
handler.show(msg, task); handler.show(msg, task);
} }
} }

@ -8,27 +8,29 @@ import mightypork.utils.eventbus.BusEvent;
/** /**
* Request for a game state change * Request for a game state change
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class RogueStateRequest extends BusEvent<RogueStateManager> { public class RogueStateRequest extends BusEvent<RogueStateManager> {
final private RogueState requested; final private RogueState requested;
private final boolean fromDark; private final boolean fromDark;
public RogueStateRequest(RogueState requested) { public RogueStateRequest(RogueState requested)
{
this.requested = requested; this.requested = requested;
this.fromDark = false; this.fromDark = false;
} }
public RogueStateRequest(RogueState requested, boolean fromDark) { public RogueStateRequest(RogueState requested, boolean fromDark)
{
this.requested = requested; this.requested = requested;
this.fromDark = fromDark; this.fromDark = fromDark;
} }
@Override @Override
protected void handleBy(RogueStateManager handler) protected void handleBy(RogueStateManager handler)
{ {

@ -2,7 +2,6 @@ package mightypork.rogue.screens;
import mightypork.gamecore.core.App; import mightypork.gamecore.core.App;
import mightypork.gamecore.core.config.Config;
import mightypork.gamecore.graphics.fonts.IFont; import mightypork.gamecore.graphics.fonts.IFont;
import mightypork.gamecore.gui.Action; import mightypork.gamecore.gui.Action;
import mightypork.gamecore.gui.components.painters.TextPainter; import mightypork.gamecore.gui.components.painters.TextPainter;
@ -19,51 +18,52 @@ import mightypork.utils.string.StringProvider;
/** /**
* FPS indicator overlay * FPS indicator overlay
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class FpsOverlay extends Overlay { public class FpsOverlay extends Overlay {
TextPainter tp; TextPainter tp;
public FpsOverlay() { public FpsOverlay()
{
/* /*
* Toggle key: F3 * Toggle key: F3
*/ */
bindKey(App.cfg().getKeyStroke("global.fps_meter"), Trigger.RISING, new Action() { bindKey(App.cfg().getKeyStroke("global.fps_meter"), Trigger.RISING, new Action() {
@Override @Override
public void execute() public void execute()
{ {
setVisible(!isVisible()); setVisible(!isVisible());
} }
}); });
final IFont font = Res.getFont("thin"); final IFont font = Res.getFont("thin");
final Num h = root.height(); final Num h = root.height();
final RectBound constraint = root.shrink(h.perc(3)).topRight().startRect().growDown(h.perc(5).max(16)); final RectBound constraint = root.shrink(h.perc(3)).topRight().startRect().growDown(h.perc(5).max(16));
tp = new TextPainter(font, AlignX.RIGHT, RGB.YELLOW, new StringProvider() { tp = new TextPainter(font, AlignX.RIGHT, RGB.YELLOW, new StringProvider() {
@Override @Override
public String getString() public String getString()
{ {
return App.gfx().getFps() + " fps"; return App.gfx().getFps() + " fps";
} }
}); });
tp.setRect(constraint); tp.setRect(constraint);
tp.setShadow(RGB.BLACK_60, Vect.make(tp.height().div(8).round())); tp.setShadow(RGB.BLACK_60, Vect.make(tp.height().div(8).round()));
root.add(tp); root.add(tp);
setVisible(false); // initially hide. setVisible(false); // initially hide.
} }
@Override @Override
public int getZIndex() public int getZIndex()
{ {

@ -20,36 +20,36 @@ import mightypork.utils.string.StringProvider;
/** /**
* Overlay with blue background and loading-info text, that accompanies an async * Overlay with blue background and loading-info text, that accompanies an async
* task. * task.
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class LoadingOverlay extends Overlay { public class LoadingOverlay extends Overlay {
private static final double T_IN = 0.5; private static final double T_IN = 0.5;
private static final double T_OUT = 1; private static final double T_OUT = 1;
private final NumAnimated alpha = new NumAnimated(0); private final NumAnimated alpha = new NumAnimated(0);
private final StringProvider msgStrProv = new StringProvider() { private final StringProvider msgStrProv = new StringProvider() {
@Override @Override
public String getString() public String getString()
{ {
return msg == null ? "" : msg; return msg == null ? "" : msg;
} }
}; };
private boolean busy; private boolean busy;
private String msg; private String msg;
private Runnable task; private Runnable task;
private final TimedTask tt = new TimedTask() { private final TimedTask tt = new TimedTask() {
@Override @Override
public void run() public void run()
{ {
Support.runAsThread(new Runnable() { Support.runAsThread(new Runnable() {
@Override @Override
public void run() public void run()
{ {
@ -61,54 +61,55 @@ public class LoadingOverlay extends Overlay {
}); });
} }
}; };
public LoadingOverlay() { public LoadingOverlay()
{
final QuadPainter qp = new QuadPainter(PAL16.SEABLUE); final QuadPainter qp = new QuadPainter(PAL16.SEABLUE);
qp.setRect(root); qp.setRect(root);
root.add(qp); root.add(qp);
updated.add(alpha); updated.add(alpha);
updated.add(tt); updated.add(tt);
Rect textRect = root.shrink(Num.ZERO, root.height().perc(48)); Rect textRect = root.shrink(Num.ZERO, root.height().perc(48));
textRect = textRect.moveY(root.height().perc(-10)); textRect = textRect.moveY(root.height().perc(-10));
final TextPainter tp = new TextPainter(Res.getFont("thick"), AlignX.CENTER, RGB.WHITE, msgStrProv); final TextPainter tp = new TextPainter(Res.getFont("thick"), AlignX.CENTER, RGB.WHITE, msgStrProv);
tp.setRect(textRect); tp.setRect(textRect);
tp.setShadow(RGB.BLACK_60, tp.height().mul(1 / 8D).toVectXY()); tp.setShadow(RGB.BLACK_60, tp.height().mul(1 / 8D).toVectXY());
root.add(tp); root.add(tp);
setAlpha(alpha); setAlpha(alpha);
} }
@Override @Override
public int getZIndex() public int getZIndex()
{ {
return 10001; // not too high, so app can put something on top return 10001; // not too high, so app can put something on top
} }
/** /**
* Show for a task * Show for a task
* *
* @param message task description * @param message task description
* @param task task * @param task task
*/ */
public void show(String message, Runnable task) public void show(String message, Runnable task)
{ {
if (busy) throw new IllegalStateException("Loader is busy with another task."); if (busy) throw new IllegalStateException("Loader is busy with another task.");
this.msg = message; this.msg = message;
this.task = task; this.task = task;
this.busy = true; this.busy = true;
alpha.setEasing(Easing.SINE_IN); alpha.setEasing(Easing.SINE_IN);
alpha.fadeIn(T_IN); alpha.fadeIn(T_IN);
tt.start(T_IN); tt.start(T_IN);
} }
} }

@ -11,8 +11,8 @@ import mightypork.utils.annotations.Stub;
public class RogueScreen extends LayeredScreen implements ShutdownListener { public class RogueScreen extends LayeredScreen implements ShutdownListener {
@Override @Override
@Stub @Stub
public void onShutdown(ShutdownEvent event) public void onShutdown(ShutdownEvent event)

@ -11,28 +11,29 @@ import mightypork.utils.math.constraints.rect.Rect;
public class HeartBar extends BaseComponent { public class HeartBar extends BaseComponent {
private final TxQuad img_on; private final TxQuad img_on;
private final TxQuad img_off; private final TxQuad img_off;
private final TxQuad img_half; private final TxQuad img_half;
private final Num total; private final Num total;
private final Num active; private final Num active;
private final NumVar index = new NumVar(0); private final NumVar index = new NumVar(0);
private final Rect heart; private final Rect heart;
public HeartBar(Num total, Num active, TxQuad img_on, TxQuad img_half, TxQuad img_off, AlignX align) { public HeartBar(Num total, Num active, TxQuad img_on, TxQuad img_half, TxQuad img_off, AlignX align)
{
super(); super();
this.total = total; this.total = total;
this.active = active; this.active = active;
this.img_on = img_on; this.img_on = img_on;
this.img_off = img_off; this.img_off = img_off;
this.img_half = img_half; this.img_half = img_half;
final Num h = height(); final Num h = height();
final Num w = width(); final Num w = width();
switch (align) { switch (align) {
case LEFT: case LEFT:
heart = leftEdge().growRight(h).moveX(index.mul(h)); heart = leftEdge().growRight(h).moveX(index.mul(h));
@ -46,19 +47,19 @@ public class HeartBar extends BaseComponent {
default: default:
heart = null; // impossible heart = null; // impossible
} }
} }
@Override @Override
protected void renderComponent() protected void renderComponent()
{ {
for (int i = 0; i < total.value(); i++) { for (int i = 0; i < total.value(); i++) {
index.setTo(i); index.setTo(i);
final double rem = active.value() - i; final double rem = active.value() - i;
App.gfx().quad(heart, (rem > 0.6 ? img_on : rem > 0.25 ? img_half : img_off)); App.gfx().quad(heart, (rem > 0.6 ? img_on : rem > 0.25 ? img_half : img_off));
} }
} }
} }

@ -12,49 +12,51 @@ import mightypork.utils.math.constraints.rect.RectBound;
public class IngameNav extends LayoutComponent { public class IngameNav extends LayoutComponent {
private final FlowColumnLayout leftFlow; private final FlowColumnLayout leftFlow;
private final FlowColumnLayout rightFlow; private final FlowColumnLayout rightFlow;
private final Rect paintHelper; private final Rect paintHelper;
private final TxQuad bg; private final TxQuad bg;
public IngameNav() { public IngameNav()
{
this(null); this(null);
} }
public IngameNav(RectBound context) { public IngameNav(RectBound context)
{
super(context); super(context);
final Rect shr = this.shrink(height().perc(5)); final Rect shr = this.shrink(height().perc(5));
leftFlow = new FlowColumnLayout(context, shr.height(), AlignX.LEFT); leftFlow = new FlowColumnLayout(context, shr.height(), AlignX.LEFT);
rightFlow = new FlowColumnLayout(context, shr.height(), AlignX.RIGHT); rightFlow = new FlowColumnLayout(context, shr.height(), AlignX.RIGHT);
leftFlow.setRect(shr); leftFlow.setRect(shr);
rightFlow.setRect(shr); rightFlow.setRect(shr);
attach(leftFlow); attach(leftFlow);
attach(rightFlow); attach(rightFlow);
paintHelper = leftEdge().growRight(height().mul(4)); paintHelper = leftEdge().growRight(height().mul(4));
bg = Res.getTxQuad("nav.bg"); bg = Res.getTxQuad("nav.bg");
} }
public void addLeft(NavButton comp) public void addLeft(NavButton comp)
{ {
leftFlow.add(comp); leftFlow.add(comp);
} }
public void addRight(NavButton comp) public void addRight(NavButton comp)
{ {
rightFlow.add(comp); rightFlow.add(comp);
} }
@Override @Override
public void renderComponent() public void renderComponent()
{ {
@ -62,8 +64,8 @@ public class IngameNav extends LayoutComponent {
for (int i = 0; i < Math.ceil(width().value() / paintHelper.width().value()); i++) { for (int i = 0; i < Math.ceil(width().value() / paintHelper.width().value()); i++) {
App.gfx().quad(paintHelper.moveX(paintHelper.width().value() * i), bg); App.gfx().quad(paintHelper.moveX(paintHelper.width().value() * i), bg);
} }
super.renderComponent(); super.renderComponent();
} }
} }

@ -22,79 +22,80 @@ import mightypork.utils.math.constraints.rect.caching.RectCache;
/** /**
* Button in the ingame nav * Button in the ingame nav
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class InvSlot extends ClickableComponent { public class InvSlot extends ClickableComponent {
private final TxQuad txBase, txSelected; private final TxQuad txBase, txSelected;
protected boolean selected = false; protected boolean selected = false;
protected int index; protected int index;
private final RectCache itemRect; private final RectCache itemRect;
private final RectCache uiRect; private final RectCache uiRect;
private final InvSlot[] slots; private final InvSlot[] slots;
private final TextPainter rbTxP; private final TextPainter rbTxP;
private final TextPainter rtTxP; private final TextPainter rtTxP;
private final RectCache rbTxRect; private final RectCache rbTxRect;
private final RectCache rtTxRect; private final RectCache rtTxRect;
private final Rect usesRect; private final Rect usesRect;
private final Num hAlpha = Num.make(0.7); private final Num hAlpha = Num.make(0.7);
public InvSlot(int index, InvSlot[] allSlots) { public InvSlot(int index, InvSlot[] allSlots)
{
super(); super();
this.txBase = Res.getTxQuad("inv.slot.base"); this.txBase = Res.getTxQuad("inv.slot.base");
this.txSelected = Res.getTxQuad("inv.slot.selected"); this.txSelected = Res.getTxQuad("inv.slot.selected");
this.index = index; this.index = index;
this.slots = allSlots; this.slots = allSlots;
this.uiRect = getRect().shrink(height().perc(16)).cached(); this.uiRect = getRect().shrink(height().perc(16)).cached();
this.itemRect = uiRect.shrink(Num.ZERO, height().perc(14), height().perc(14), Num.ZERO).cached(); this.itemRect = uiRect.shrink(Num.ZERO, height().perc(14), height().perc(14), Num.ZERO).cached();
this.usesRect = uiRect.topLeft().startRect().grow(Num.ZERO, uiRect.width().perc(40), Num.ZERO, uiRect.height().perc(8)); this.usesRect = uiRect.topLeft().startRect().grow(Num.ZERO, uiRect.width().perc(40), Num.ZERO, uiRect.height().perc(8));
//@formatter:off //@formatter:off
this.rbTxRect = uiRect.bottomEdge() this.rbTxRect = uiRect.bottomEdge()
.moveY(uiRect.height().perc(1*(30/7D))) .moveY(uiRect.height().perc(1*(30/7D)))
.growUp(uiRect.height().perc(30)).cached(); .growUp(uiRect.height().perc(30)).cached();
//@formatter:on //@formatter:on
rbTxP = new TextPainter(Res.getFont("tiny"), AlignX.RIGHT, RGB.WHITE); rbTxP = new TextPainter(Res.getFont("tiny"), AlignX.RIGHT, RGB.WHITE);
rbTxP.setRect(rbTxRect); rbTxP.setRect(rbTxRect);
rbTxP.setShadow(RGB.BLACK_70, rbTxP.getRect().height().div(7).toVectXY()); rbTxP.setShadow(RGB.BLACK_70, rbTxP.getRect().height().div(7).toVectXY());
//@formatter:off //@formatter:off
this.rtTxRect = uiRect.topEdge() this.rtTxRect = uiRect.topEdge()
.growDown(uiRect.height().perc(30)).cached(); .growDown(uiRect.height().perc(30)).cached();
//@formatter:on //@formatter:on
rtTxP = new TextPainter(Res.getFont("tiny"), AlignX.RIGHT, RGB.GREEN); rtTxP = new TextPainter(Res.getFont("tiny"), AlignX.RIGHT, RGB.GREEN);
rtTxP.setRect(rtTxRect); rtTxP.setRect(rtTxRect);
rtTxP.setShadow(RGB.BLACK_70, rtTxP.getRect().height().div(7).toVectXY()); rtTxP.setShadow(RGB.BLACK_70, rtTxP.getRect().height().div(7).toVectXY());
setAction(new Action() { setAction(new Action() {
@Override @Override
protected void execute() protected void execute()
{ {
for (final InvSlot sl : slots) { for (final InvSlot sl : slots) {
sl.selected = false; sl.selected = false;
} }
selected = true; selected = true;
} }
}); });
} }
@Override @Override
public void updateLayout() public void updateLayout()
{ {
@ -103,23 +104,23 @@ public class InvSlot extends ClickableComponent {
rbTxRect.poll(); rbTxRect.poll();
rtTxRect.poll(); rtTxRect.poll();
} }
@Override @Override
protected void renderComponent() protected void renderComponent()
{ {
TxQuad bg; TxQuad bg;
if (selected) { if (selected) {
bg = txSelected; bg = txSelected;
} else { } else {
bg = txBase; bg = txBase;
} }
App.gfx().quad(this, bg); App.gfx().quad(this, bg);
final PlayerFacade pl = WorldProvider.get().getPlayer(); final PlayerFacade pl = WorldProvider.get().getPlayer();
final Item itm = pl.getInventory().getItem(index); final Item itm = pl.getInventory().getItem(index);
if (itm != null && !itm.isEmpty()) { if (itm != null && !itm.isEmpty()) {
itm.render(itemRect); itm.render(itemRect);
@ -128,39 +129,39 @@ public class InvSlot extends ClickableComponent {
rbTxP.setColor(RGB.WHITE); rbTxP.setColor(RGB.WHITE);
rbTxP.render(); rbTxP.render();
} }
if (pl.getSelectedWeaponIndex() == index) { if (pl.getSelectedWeaponIndex() == index) {
rbTxP.setText("*"); rbTxP.setText("*");
rbTxP.setColor(RGB.YELLOW); rbTxP.setColor(RGB.YELLOW);
rbTxP.render(); rbTxP.render();
} }
if (itm.getType() == ItemType.FOOD) { if (itm.getType() == ItemType.FOOD) {
rtTxP.setText(Support.str(itm.getFoodPoints() / 2D)); rtTxP.setText(Support.str(itm.getFoodPoints() / 2D));
rtTxP.setColor(RGB.GREEN); rtTxP.setColor(RGB.GREEN);
rtTxP.render(); rtTxP.render();
} else if (itm.getType() == ItemType.WEAPON) { } else if (itm.getType() == ItemType.WEAPON) {
final int atk = itm.getAttackPoints(); final int atk = itm.getAttackPoints();
rtTxP.setText((atk >= 0 ? "+" : "") + atk); rtTxP.setText((atk >= 0 ? "+" : "") + atk);
rtTxP.setColor(RGB.CYAN); rtTxP.setColor(RGB.CYAN);
rtTxP.render(); rtTxP.render();
} }
if (itm.isDamageable()) { if (itm.isDamageable()) {
Color.pushAlpha(hAlpha); Color.pushAlpha(hAlpha);
App.gfx().quad(usesRect, RGB.BLACK); App.gfx().quad(usesRect, RGB.BLACK);
final double useRatio = (itm.getRemainingUses() / (double) itm.getMaxUses()); final double useRatio = (itm.getRemainingUses() / (double) itm.getMaxUses());
final Color barColor = (useRatio > 0.6 ? RGB.GREEN : useRatio > 0.2 ? RGB.ORANGE : RGB.RED); final Color barColor = (useRatio > 0.6 ? RGB.GREEN : useRatio > 0.2 ? RGB.ORANGE : RGB.RED);
App.gfx().quad(usesRect.shrinkRight(usesRect.width().value() * (1 - useRatio)), barColor); App.gfx().quad(usesRect.shrinkRight(usesRect.width().value() * (1 - useRatio)), barColor);
Color.popAlpha(); Color.popAlpha();
} }
} }
} }
} }

@ -24,18 +24,18 @@ import mightypork.utils.math.constraints.num.Num;
public class LayerAskSave extends FadingLayer { public class LayerAskSave extends FadingLayer {
public Runnable task; public Runnable task;
private final ScreenGame gscreen; private final ScreenGame gscreen;
public void setTask(Runnable task) public void setTask(Runnable task)
{ {
this.task = task; this.task = task;
} }
@Override @Override
protected void onHideFinished() protected void onHideFinished()
{ {
@ -43,61 +43,61 @@ public class LayerAskSave extends FadingLayer {
gscreen.setState(GScrState.WORLD); // go back.. gscreen.setState(GScrState.WORLD); // go back..
} }
} }
public LayerAskSave(final ScreenGame screen) public LayerAskSave(final ScreenGame screen)
{ {
super(screen); super(screen);
this.gscreen = screen; this.gscreen = screen;
// darker down to cover console. // darker down to cover console.
final QuadPainter qp = new QuadPainter(RGB.BLACK_80); final QuadPainter qp = new QuadPainter(RGB.BLACK_80);
qp.setRect(root); qp.setRect(root);
root.add(qp); root.add(qp);
final IFont thick_font = Res.getFont("thick"); final IFont thick_font = Res.getFont("thick");
final RowLayout rl = new RowLayout(root, 2); final RowLayout rl = new RowLayout(root, 2);
rl.setRect(root.shrink(Num.ZERO, root.height().perc(40)).moveY(root.height().perc(-10))); rl.setRect(root.shrink(Num.ZERO, root.height().perc(40)).moveY(root.height().perc(-10)));
root.add(rl); root.add(rl);
final TextPainter txp = new TextPainter(thick_font, AlignX.CENTER, RGB.WHITE, "Save the game?"); final TextPainter txp = new TextPainter(thick_font, AlignX.CENTER, RGB.WHITE, "Save the game?");
rl.add(txp, 1); rl.add(txp, 1);
txp.setVPaddingPercent(25); txp.setVPaddingPercent(25);
final LinearLayout ll = new LinearLayout(root, AlignX.CENTER); final LinearLayout ll = new LinearLayout(root, AlignX.CENTER);
rl.add(ll); rl.add(ll);
final double vPadPerc = 20; final double vPadPerc = 20;
final TextButton btn1 = new TextButton(thick_font, "Yes", ScreenGame.COLOR_BTN_GOOD); final TextButton btn1 = new TextButton(thick_font, "Yes", ScreenGame.COLOR_BTN_GOOD);
btn1.textPainter.setAlign(AlignX.RIGHT); btn1.textPainter.setAlign(AlignX.RIGHT);
btn1.textPainter.setVPaddingPercent(vPadPerc); btn1.textPainter.setVPaddingPercent(vPadPerc);
ll.add(btn1); ll.add(btn1);
ll.gap(50); ll.gap(50);
final TextButton btn2 = new TextButton(thick_font, "No", ScreenGame.COLOR_BTN_BAD); final TextButton btn2 = new TextButton(thick_font, "No", ScreenGame.COLOR_BTN_BAD);
btn2.textPainter.setAlign(AlignX.CENTER); btn2.textPainter.setAlign(AlignX.CENTER);
btn2.textPainter.setVPaddingPercent(vPadPerc); btn2.textPainter.setVPaddingPercent(vPadPerc);
ll.add(btn2); ll.add(btn2);
ll.gap(50); ll.gap(50);
final TextButton btn3 = new TextButton(thick_font, "Cancel", ScreenGame.COLOR_BTN_CANCEL); final TextButton btn3 = new TextButton(thick_font, "Cancel", ScreenGame.COLOR_BTN_CANCEL);
btn3.textPainter.setAlign(AlignX.LEFT); btn3.textPainter.setAlign(AlignX.LEFT);
btn3.textPainter.setVPaddingPercent(vPadPerc); btn3.textPainter.setVPaddingPercent(vPadPerc);
ll.add(btn3); ll.add(btn3);
final Action cancel = new Action() { final Action cancel = new Action() {
@Override @Override
protected void execute() protected void execute()
{ {
hide(); hide();
} }
}; };
final Action save = new Action() { final Action save = new Action() {
@Override @Override
protected void execute() protected void execute()
{ {
@ -109,39 +109,39 @@ public class LayerAskSave extends FadingLayer {
} }
} }
}; };
final Action discard = new Action() { final Action discard = new Action() {
@Override @Override
protected void execute() protected void execute()
{ {
if (task != null) task.run(); if (task != null) task.run();
} }
}; };
btn1.setAction(save); btn1.setAction(save);
btn2.setAction(discard); btn2.setAction(discard);
btn3.setAction(cancel); btn3.setAction(cancel);
Config cfg = App.cfg(); final Config cfg = App.cfg();
bindKey(cfg.getKeyStroke("general.close"), Trigger.RISING, cancel); bindKey(cfg.getKeyStroke("general.close"), Trigger.RISING, cancel);
bindKey(cfg.getKeyStroke("general.cancel"), Trigger.RISING, cancel); bindKey(cfg.getKeyStroke("general.cancel"), Trigger.RISING, cancel);
bindKey(cfg.getKeyStroke("general.yes"), Trigger.RISING, save); bindKey(cfg.getKeyStroke("general.yes"), Trigger.RISING, save);
bindKey(cfg.getKeyStroke("general.confirm"), Trigger.RISING, save); bindKey(cfg.getKeyStroke("general.confirm"), Trigger.RISING, save);
bindKey(cfg.getKeyStroke("general.no"), Trigger.RISING, discard); bindKey(cfg.getKeyStroke("general.no"), Trigger.RISING, discard);
} }
@Override @Override
public int getZIndex() public int getZIndex()
{ {
return 300; return 300;
} }
@Override @Override
public void update(double delta) public void update(double delta)
{ {

@ -28,44 +28,45 @@ import mightypork.utils.math.constraints.num.Num;
public class LayerDeath extends FadingLayer { public class LayerDeath extends FadingLayer {
public LayerDeath(final ScreenGame screen) { public LayerDeath(final ScreenGame screen)
{
super(screen); super(screen);
// darker down to cover console. // darker down to cover console.
final QuadPainter qp = new QuadPainter(RGB.BLACK_80); final QuadPainter qp = new QuadPainter(RGB.BLACK_80);
qp.setRect(root); qp.setRect(root);
root.add(qp); root.add(qp);
final IFont thick_font = Res.getFont("thick"); final IFont thick_font = Res.getFont("thick");
final RowLayout rl = new RowLayout(root, 5); final RowLayout rl = new RowLayout(root, 5);
rl.setRect(root.shrink(Num.ZERO, root.height().perc(15))); rl.setRect(root.shrink(Num.ZERO, root.height().perc(15)));
root.add(rl); root.add(rl);
final TextPainter txp = new TextPainter(thick_font, AlignX.CENTER, RGB.YELLOW, "Rats won!"); final TextPainter txp = new TextPainter(thick_font, AlignX.CENTER, RGB.YELLOW, "Rats won!");
rl.add(txp, 1); rl.add(txp, 1);
txp.setVPaddingPercent(13); txp.setVPaddingPercent(13);
LinearLayout linl = new LinearLayout(root, AlignX.CENTER); LinearLayout linl = new LinearLayout(root, AlignX.CENTER);
linl.add(new ImagePainter(Res.getTxQuad("death2"))); linl.add(new ImagePainter(Res.getTxQuad("death2")));
rl.add(linl, 3); rl.add(linl, 3);
linl = new LinearLayout(root, AlignX.CENTER); linl = new LinearLayout(root, AlignX.CENTER);
rl.add(linl); rl.add(linl);
final TextButton btn1 = new TextButton(thick_font, "Retry", ScreenGame.COLOR_BTN_GOOD); final TextButton btn1 = new TextButton(thick_font, "Retry", ScreenGame.COLOR_BTN_GOOD);
btn1.textPainter.setVPaddingPercent(25); btn1.textPainter.setVPaddingPercent(25);
linl.add(btn1); linl.add(btn1);
linl.add(new LinearGap(50)); linl.add(new LinearGap(50));
final TextButton btn2 = new TextButton(thick_font, "Leave", ScreenGame.COLOR_BTN_BAD); final TextButton btn2 = new TextButton(thick_font, "Leave", ScreenGame.COLOR_BTN_BAD);
btn2.textPainter.setVPaddingPercent(25); btn2.textPainter.setVPaddingPercent(25);
linl.add(btn2); linl.add(btn2);
final Action load = new Action() { final Action load = new Action() {
@Override @Override
protected void execute() protected void execute()
{ {
@ -77,31 +78,31 @@ public class LayerDeath extends FadingLayer {
} }
} }
}; };
final Action quit = new Action() { final Action quit = new Action() {
@Override @Override
protected void execute() protected void execute()
{ {
App.bus().send(new RogueStateRequest(RogueState.MAIN_MENU)); App.bus().send(new RogueStateRequest(RogueState.MAIN_MENU));
} }
}; };
btn1.setAction(load); btn1.setAction(load);
btn2.setAction(quit); btn2.setAction(quit);
Config cfg = App.cfg(); final Config cfg = App.cfg();
bindKey(cfg.getKeyStroke("game.load"), Trigger.RISING, load); bindKey(cfg.getKeyStroke("game.load"), Trigger.RISING, load);
bindKey(cfg.getKeyStroke("general.confirm"), Trigger.RISING, load); bindKey(cfg.getKeyStroke("general.confirm"), Trigger.RISING, load);
bindKey(cfg.getKeyStroke("general.close"), Trigger.RISING, quit); bindKey(cfg.getKeyStroke("general.close"), Trigger.RISING, quit);
} }
@Override @Override
public int getZIndex() public int getZIndex()
{ {
return 300; return 300;
} }
} }

@ -16,70 +16,71 @@ import mightypork.utils.string.StringProvider;
public class LayerGameUi extends ScreenLayer { public class LayerGameUi extends ScreenLayer {
private final Num playerHealthTotal = new Num() { private final Num playerHealthTotal = new Num() {
@Override @Override
public double value() public double value()
{ {
return WorldProvider.get().getPlayer().getHealthMax() / 2D; return WorldProvider.get().getPlayer().getHealthMax() / 2D;
} }
}; };
private final Num playerHealthActive = new Num() { private final Num playerHealthActive = new Num() {
@Override @Override
public double value() public double value()
{ {
return WorldProvider.get().getPlayer().getHealth() / 2D; return WorldProvider.get().getPlayer().getHealth() / 2D;
} }
}; };
protected Minimap miniMap; protected Minimap miniMap;
private final ScreenGame gameScreen; private final ScreenGame gameScreen;
public LayerGameUi(ScreenGame screen) { public LayerGameUi(ScreenGame screen)
{
super(screen); super(screen);
this.gameScreen = screen; this.gameScreen = screen;
buildNav(); buildNav();
buildDisplays(); buildDisplays();
buildMinimap(); buildMinimap();
buildConsole(); buildConsole();
} }
private void buildConsole() private void buildConsole()
{ {
final Num rh = root.height(); final Num rh = root.height();
final Num rw = root.width(); final Num rw = root.width();
final Rect consoleRect = root.shrink(rw.perc(2), Num.ZERO, rh.perc(6), rh.perc(16)); final Rect consoleRect = root.shrink(rw.perc(2), Num.ZERO, rh.perc(6), rh.perc(16));
final Num perRow = consoleRect.height().div(20).max(12).min(32); final Num perRow = consoleRect.height().div(20).max(12).min(32);
final WorldConsoleRenderer wcr = new WorldConsoleRenderer(perRow); final WorldConsoleRenderer wcr = new WorldConsoleRenderer(perRow);
wcr.setRect(consoleRect); wcr.setRect(consoleRect);
root.add(wcr); root.add(wcr);
} }
private void buildMinimap() private void buildMinimap()
{ {
miniMap = new Minimap(); miniMap = new Minimap();
miniMap.setRect(root.shrink(root.width().perc(5), root.height().perc(15))); miniMap.setRect(root.shrink(root.width().perc(5), root.height().perc(15)));
root.add(miniMap); root.add(miniMap);
} }
private void buildDisplays() private void buildDisplays()
{ {
final Num h = root.height(); final Num h = root.height();
//@formatter:off //@formatter:off
final HeartBar hearts = new HeartBar( final HeartBar hearts = new HeartBar(
playerHealthTotal, playerHealthTotal,
@ -89,13 +90,13 @@ public class LayerGameUi extends ScreenLayer {
Res.getTxQuad("hud.heart.off"), Res.getTxQuad("hud.heart.off"),
AlignX.LEFT); AlignX.LEFT);
//@formatter:on //@formatter:on
final Rect hearts_box = root.shrink(h.perc(3)).topEdge().growDown(h.perc(6)); final Rect hearts_box = root.shrink(h.perc(3)).topEdge().growDown(h.perc(6));
hearts.setRect(hearts_box); hearts.setRect(hearts_box);
root.add(hearts); root.add(hearts);
final TextPainter levelText = new TextPainter(Res.getFont("tiny"), AlignX.RIGHT, RGB.WHITE, new StringProvider() { final TextPainter levelText = new TextPainter(Res.getFont("tiny"), AlignX.RIGHT, RGB.WHITE, new StringProvider() {
@Override @Override
public String getString() public String getString()
{ {
@ -103,62 +104,62 @@ public class LayerGameUi extends ScreenLayer {
return (w.isPaused() ? "[P] " : "") + "Floor " + (1 + w.getPlayer().getLevelNumber()); return (w.isPaused() ? "[P] " : "") + "Floor " + (1 + w.getPlayer().getLevelNumber());
} }
}); });
levelText.setRect(hearts_box.moveY(hearts_box.height().mul(1 / 7D))); levelText.setRect(hearts_box.moveY(hearts_box.height().mul(1 / 7D)));
root.add(levelText); root.add(levelText);
} }
private void buildNav() private void buildNav()
{ {
final IngameNav nav = new IngameNav(); final IngameNav nav = new IngameNav();
nav.setRect(root.bottomEdge().growUp(root.height().perc(12))); nav.setRect(root.bottomEdge().growUp(root.height().perc(12)));
root.add(nav); root.add(nav);
NavButton btn; NavButton btn;
nav.addRight(btn = new NavButton(Res.getTxQuad("nav.button.fg.inventory"))); nav.addRight(btn = new NavButton(Res.getTxQuad("nav.button.fg.inventory")));
btn.setAction(gameScreen.actionToggleInv); btn.setAction(gameScreen.actionToggleInv);
nav.addRight(btn = new NavButton(Res.getTxQuad("nav.button.fg.eat"))); nav.addRight(btn = new NavButton(Res.getTxQuad("nav.button.fg.eat")));
btn.setAction(gameScreen.actionEat); btn.setAction(gameScreen.actionEat);
nav.addRight(btn = new NavButton(Res.getTxQuad("nav.button.fg.pause"))); nav.addRight(btn = new NavButton(Res.getTxQuad("nav.button.fg.pause")));
btn.setAction(gameScreen.actionTogglePause); btn.setAction(gameScreen.actionTogglePause);
// TODO actions // TODO actions
//nav.addLeft(new NavButton(Res.txq("nav.button.fg.options"))); //nav.addLeft(new NavButton(Res.txq("nav.button.fg.options")));
//nav.addLeft(new NavButton(Res.txq("nav.button.fg.help"))); //nav.addLeft(new NavButton(Res.txq("nav.button.fg.help")));
nav.addLeft(btn = new NavButton(Res.getTxQuad("nav.button.fg.menu"))); nav.addLeft(btn = new NavButton(Res.getTxQuad("nav.button.fg.menu")));
btn.setAction(gameScreen.actionMenu); btn.setAction(gameScreen.actionMenu);
nav.addLeft(btn = new NavButton(Res.getTxQuad("nav.button.fg.save"))); nav.addLeft(btn = new NavButton(Res.getTxQuad("nav.button.fg.save")));
btn.setAction(gameScreen.actionSave); btn.setAction(gameScreen.actionSave);
nav.addLeft(btn = new NavButton(Res.getTxQuad("nav.button.fg.load"))); nav.addLeft(btn = new NavButton(Res.getTxQuad("nav.button.fg.load")));
btn.setAction(gameScreen.actionLoad); btn.setAction(gameScreen.actionLoad);
nav.addLeft(btn = new NavButton(Res.getTxQuad("nav.button.fg.map"))); nav.addLeft(btn = new NavButton(Res.getTxQuad("nav.button.fg.map")));
btn.setAction(gameScreen.actionToggleMinimap); btn.setAction(gameScreen.actionToggleMinimap);
nav.addLeft(btn = new NavButton(Res.getTxQuad("nav.button.fg.magnify"))); nav.addLeft(btn = new NavButton(Res.getTxQuad("nav.button.fg.magnify")));
btn.setAction(gameScreen.actionToggleZoom); btn.setAction(gameScreen.actionToggleZoom);
} }
@Override @Override
public int getZIndex() public int getZIndex()
{ {
return 101; return 101;
} }
@Override @Override
public int getEventPriority() public int getEventPriority()
{ {
return 400; return 400;
} }
} }

@ -26,34 +26,34 @@ import mightypork.utils.string.StringProvider;
public class LayerInv extends FadingLayer { public class LayerInv extends FadingLayer {
private static final int SLOT_COUNT = 8; private static final int SLOT_COUNT = 8;
private static final int SLOT_ROW = 4; private static final int SLOT_ROW = 4;
private final KeyStroke keyUse = App.cfg().getKeyStroke("game.inv.use"); private final KeyStroke keyUse = App.cfg().getKeyStroke("game.inv.use");
private final KeyStroke keyDrop = App.cfg().getKeyStroke("game.inv.drop"); private final KeyStroke keyDrop = App.cfg().getKeyStroke("game.inv.drop");
private final KeyStroke keyClose = App.cfg().getKeyStroke("general.close"); private final KeyStroke keyClose = App.cfg().getKeyStroke("general.close");
private final StringProvider contextStrProv = new StringProvider() { private final StringProvider contextStrProv = new StringProvider() {
@Override @Override
public String getString() public String getString()
{ {
String s = keyClose + "-close"; String s = keyClose + "-close";
final int selected = getSelectedSlot(); final int selected = getSelectedSlot();
if (selected != -1) { if (selected != -1) {
final PlayerFacade pl = WorldProvider.get().getPlayer(); final PlayerFacade pl = WorldProvider.get().getPlayer();
final Item itm = pl.getInventory().getItem(selected); final Item itm = pl.getInventory().getItem(selected);
if (itm != null && !itm.isEmpty()) { if (itm != null && !itm.isEmpty()) {
s = keyDrop + "-drop," + s; s = keyDrop + "-drop," + s;
if (itm.getType() == ItemType.FOOD) { if (itm.getType() == ItemType.FOOD) {
s = keyUse + "-eat," + s; s = keyUse + "-eat," + s;
} }
if (itm.getType() == ItemType.WEAPON) { if (itm.getType() == ItemType.WEAPON) {
s = keyUse + "-equip," + s; s = keyUse + "-equip," + s;
} }
@ -61,15 +61,15 @@ public class LayerInv extends FadingLayer {
} else { } else {
s = "Click-select," + s; s = "Click-select," + s;
} }
return s; return s;
} }
}; };
private final InvSlot[] slots = new InvSlot[SLOT_COUNT]; private final InvSlot[] slots = new InvSlot[SLOT_COUNT];
private ScreenGame gscreen; private ScreenGame gscreen;
private int getSelectedSlot() private int getSelectedSlot()
{ {
for (final InvSlot sl : slots) { for (final InvSlot sl : slots) {
@ -80,20 +80,20 @@ public class LayerInv extends FadingLayer {
} }
return -1; return -1;
} }
private void selectSlot(int i) private void selectSlot(int i)
{ {
for (final InvSlot sl : slots) { for (final InvSlot sl : slots) {
sl.selected = false; sl.selected = false;
} }
if (i >= 0 && i < SLOT_COUNT) { if (i >= 0 && i < SLOT_COUNT) {
slots[i].selected = true; slots[i].selected = true;
} }
} }
@Override @Override
protected void onHideFinished() protected void onHideFinished()
{ {
@ -101,44 +101,45 @@ public class LayerInv extends FadingLayer {
gscreen.actionToggleInv.run(); gscreen.actionToggleInv.run();
} }
} }
public LayerInv(final ScreenGame screen) { public LayerInv(final ScreenGame screen)
{
super(screen); super(screen);
this.gscreen = screen; this.gscreen = screen;
final Rect fg = root.shrink(root.height().perc(15)); final Rect fg = root.shrink(root.height().perc(15));
// darker down to cover console. // darker down to cover console.
final QuadPainter qp = new QuadPainter(RGB.BLACK_30, RGB.BLACK_30, RGB.BLACK_90, RGB.BLACK_90); final QuadPainter qp = new QuadPainter(RGB.BLACK_30, RGB.BLACK_30, RGB.BLACK_90, RGB.BLACK_90);
qp.setRect(root); qp.setRect(root);
root.add(qp); root.add(qp);
int pos = 0; int pos = 0;
final GridLayout gl = new GridLayout(fg, 10, 1); final GridLayout gl = new GridLayout(fg, 10, 1);
root.add(gl); root.add(gl);
final TextPainter txp = new TextPainter(Res.getFont("thick"), AlignX.CENTER, RGB.YELLOW, "Inventory"); final TextPainter txp = new TextPainter(Res.getFont("thick"), AlignX.CENTER, RGB.YELLOW, "Inventory");
gl.put(txp, pos, 0, 1, 1); gl.put(txp, pos, 0, 1, 1);
txp.setVPaddingPercent(5); txp.setVPaddingPercent(5);
pos += 1; pos += 1;
final FlowColumnLayout row1 = new FlowColumnLayout(root, null, AlignX.LEFT); final FlowColumnLayout row1 = new FlowColumnLayout(root, null, AlignX.LEFT);
row1.setElementWidth(row1.height()); row1.setElementWidth(row1.height());
final ConstraintLayout cl1 = new ConstraintLayout(root); final ConstraintLayout cl1 = new ConstraintLayout(root);
row1.setRect(cl1.axisV().grow(cl1.height().mul(2), Num.ZERO)); row1.setRect(cl1.axisV().grow(cl1.height().mul(2), Num.ZERO));
cl1.add(row1); cl1.add(row1);
gl.put(cl1, pos, 0, 4, 1); gl.put(cl1, pos, 0, 4, 1);
pos += 4; pos += 4;
row1.add(slots[0] = new InvSlot(0, slots)); row1.add(slots[0] = new InvSlot(0, slots));
row1.add(slots[1] = new InvSlot(1, slots)); row1.add(slots[1] = new InvSlot(1, slots));
row1.add(slots[2] = new InvSlot(2, slots)); row1.add(slots[2] = new InvSlot(2, slots));
row1.add(slots[3] = new InvSlot(3, slots)); row1.add(slots[3] = new InvSlot(3, slots));
final FlowColumnLayout row2 = new FlowColumnLayout(root, null, AlignX.LEFT); final FlowColumnLayout row2 = new FlowColumnLayout(root, null, AlignX.LEFT);
row2.setElementWidth(row2.height()); row2.setElementWidth(row2.height());
final ConstraintLayout cl2 = new ConstraintLayout(root); final ConstraintLayout cl2 = new ConstraintLayout(root);
@ -146,18 +147,18 @@ public class LayerInv extends FadingLayer {
cl2.add(row2); cl2.add(row2);
gl.put(cl2, pos, 0, 4, 1); gl.put(cl2, pos, 0, 4, 1);
pos += 4; pos += 4;
row2.add(slots[4] = new InvSlot(4, slots)); row2.add(slots[4] = new InvSlot(4, slots));
row2.add(slots[5] = new InvSlot(5, slots)); row2.add(slots[5] = new InvSlot(5, slots));
row2.add(slots[6] = new InvSlot(6, slots)); row2.add(slots[6] = new InvSlot(6, slots));
row2.add(slots[7] = new InvSlot(7, slots)); row2.add(slots[7] = new InvSlot(7, slots));
final TextPainter txp2 = new TextPainter(Res.getFont("thick"), AlignX.CENTER, RGB.WHITE, contextStrProv); final TextPainter txp2 = new TextPainter(Res.getFont("thick"), AlignX.CENTER, RGB.WHITE, contextStrProv);
gl.put(txp2, pos, 0, 1, 1); gl.put(txp2, pos, 0, 1, 1);
txp2.setVPaddingPercent(25); txp2.setVPaddingPercent(25);
bindKey(keyClose, Trigger.RISING, new Runnable() { bindKey(keyClose, Trigger.RISING, new Runnable() {
@Override @Override
public void run() public void run()
{ {
@ -165,150 +166,150 @@ public class LayerInv extends FadingLayer {
hide(); hide();
} }
}); });
bindKey(keyUse, Trigger.RISING, new Runnable() { bindKey(keyUse, Trigger.RISING, new Runnable() {
@Override @Override
public void run() public void run()
{ {
if (!isEnabled()) return; if (!isEnabled()) return;
if (WorldProvider.get().getPlayer().isDead()) return; if (WorldProvider.get().getPlayer().isDead()) return;
useSelectedItem(); useSelectedItem();
} }
}); });
bindKey(keyDrop, Trigger.RISING, new Runnable() { bindKey(keyDrop, Trigger.RISING, new Runnable() {
@Override @Override
public void run() public void run()
{ {
if (!isEnabled()) return; if (!isEnabled()) return;
final int selected = getSelectedSlot(); final int selected = getSelectedSlot();
if (selected != -1) { if (selected != -1) {
WorldProvider.get().getPlayer().dropItem(selected); WorldProvider.get().getPlayer().dropItem(selected);
} }
} }
}); });
setupGridWalkKeys(); setupGridWalkKeys();
} }
protected void useSelectedItem() protected void useSelectedItem()
{ {
final int selected = getSelectedSlot(); final int selected = getSelectedSlot();
if (selected != -1) { if (selected != -1) {
final World world = WorldProvider.get().getWorld(); final World world = WorldProvider.get().getWorld();
final PlayerFacade pl = WorldProvider.get().getPlayer(); final PlayerFacade pl = WorldProvider.get().getPlayer();
final Item itm = pl.getInventory().getItem(selected); final Item itm = pl.getInventory().getItem(selected);
if (itm != null && !itm.isEmpty()) { if (itm != null && !itm.isEmpty()) {
final ItemType type = itm.getType(); final ItemType type = itm.getType();
if (type == ItemType.FOOD) { if (type == ItemType.FOOD) {
if (pl.eatFood(itm)) { if (pl.eatFood(itm)) {
pl.getInventory().clean(); pl.getInventory().clean();
} }
} else if (type == ItemType.WEAPON) { } else if (type == ItemType.WEAPON) {
if (pl.getSelectedWeaponIndex() == selected) { if (pl.getSelectedWeaponIndex() == selected) {
pl.selectWeapon(-1); pl.selectWeapon(-1);
} else { } else {
pl.selectWeapon(selected); pl.selectWeapon(selected);
} }
world.getConsole().msgEquipWeapon(pl.getSelectedWeapon()); world.getConsole().msgEquipWeapon(pl.getSelectedWeapon());
} }
} }
} }
} }
private void setupGridWalkKeys() private void setupGridWalkKeys()
{ {
Config cfg = App.cfg(); final Config cfg = App.cfg();
bindKey(cfg.getKeyStroke("game.inv.move.left"), Trigger.RISING, new Runnable() { bindKey(cfg.getKeyStroke("game.inv.move.left"), Trigger.RISING, new Runnable() {
@Override @Override
public void run() public void run()
{ {
if (!isEnabled()) return; if (!isEnabled()) return;
final int sel = getSelectedSlot(); final int sel = getSelectedSlot();
if (sel == -1) { if (sel == -1) {
selectSlot(0); selectSlot(0);
return; return;
} }
selectSlot((SLOT_COUNT + (sel - 1)) % SLOT_COUNT); selectSlot((SLOT_COUNT + (sel - 1)) % SLOT_COUNT);
} }
}); });
bindKey(cfg.getKeyStroke("game.inv.move.right"), Trigger.RISING, new Runnable() { bindKey(cfg.getKeyStroke("game.inv.move.right"), Trigger.RISING, new Runnable() {
@Override @Override
public void run() public void run()
{ {
if (!isEnabled()) return; if (!isEnabled()) return;
final int sel = getSelectedSlot(); final int sel = getSelectedSlot();
if (sel == -1) { if (sel == -1) {
selectSlot(0); selectSlot(0);
return; return;
} }
selectSlot((SLOT_COUNT + (sel + 1)) % SLOT_COUNT); selectSlot((SLOT_COUNT + (sel + 1)) % SLOT_COUNT);
} }
}); });
bindKey(cfg.getKeyStroke("game.inv.move.up"), Trigger.RISING, new Runnable() { bindKey(cfg.getKeyStroke("game.inv.move.up"), Trigger.RISING, new Runnable() {
@Override @Override
public void run() public void run()
{ {
if (!isEnabled()) return; if (!isEnabled()) return;
final int sel = getSelectedSlot(); final int sel = getSelectedSlot();
if (sel == -1) { if (sel == -1) {
selectSlot(0); selectSlot(0);
return; return;
} }
selectSlot((SLOT_COUNT + (sel - SLOT_ROW)) % SLOT_COUNT); selectSlot((SLOT_COUNT + (sel - SLOT_ROW)) % SLOT_COUNT);
} }
}); });
bindKey(cfg.getKeyStroke("game.inv.move.down"), Trigger.RISING, new Runnable() { bindKey(cfg.getKeyStroke("game.inv.move.down"), Trigger.RISING, new Runnable() {
@Override @Override
public void run() public void run()
{ {
if (!isEnabled()) return; if (!isEnabled()) return;
final int sel = getSelectedSlot(); final int sel = getSelectedSlot();
if (sel == -1) { if (sel == -1) {
selectSlot(0); selectSlot(0);
return; return;
} }
selectSlot((sel + SLOT_ROW) % SLOT_COUNT); selectSlot((sel + SLOT_ROW) % SLOT_COUNT);
} }
}); });
} }
@Override @Override
public int getZIndex() public int getZIndex()
{ {
return 300; return 300;
} }
} }

@ -10,40 +10,41 @@ import mightypork.utils.math.constraints.num.Num;
public class LayerMapView extends ScreenLayer { public class LayerMapView extends ScreenLayer {
protected final MapView map; protected final MapView map;
public LayerMapView(Screen screen) { public LayerMapView(Screen screen)
{
super(screen); super(screen);
// render component // render component
map = new MapView(); map = new MapView();
// map input plugins // map input plugins
map.addPlugin(new MIPKeyboard(map)); map.addPlugin(new MIPKeyboard(map));
map.addPlugin(new MIPMouse(map)); map.addPlugin(new MIPMouse(map));
// size of lower navbar // size of lower navbar
final Num lownav = root.height().perc(12); final Num lownav = root.height().perc(12);
map.setRect(root.shrinkBottom(lownav)); map.setRect(root.shrinkBottom(lownav));
root.add(map); root.add(map);
} }
@Override @Override
public int getZIndex() public int getZIndex()
{ {
return 0; // stay down return 0; // stay down
} }
@Override @Override
public int getEventPriority() public int getEventPriority()
{ {
return 100; return 100;
} }
} }

@ -4,7 +4,6 @@ package mightypork.rogue.screens.game;
import java.io.File; import java.io.File;
import mightypork.gamecore.core.App; import mightypork.gamecore.core.App;
import mightypork.gamecore.core.config.Config;
import mightypork.gamecore.graphics.fonts.IFont; import mightypork.gamecore.graphics.fonts.IFont;
import mightypork.gamecore.gui.Action; import mightypork.gamecore.gui.Action;
import mightypork.gamecore.gui.components.input.TextButton; import mightypork.gamecore.gui.components.input.TextButton;
@ -25,68 +24,69 @@ import mightypork.utils.math.constraints.num.Num;
public class LayerWin extends FadingLayer { public class LayerWin extends FadingLayer {
public LayerWin(final ScreenGame screen) { public LayerWin(final ScreenGame screen)
{
super(screen); super(screen);
// darker down to cover console. // darker down to cover console.
final QuadPainter qp = new QuadPainter(RGB.BLACK_80); final QuadPainter qp = new QuadPainter(RGB.BLACK_80);
qp.setRect(root); qp.setRect(root);
root.add(qp); root.add(qp);
final IFont thick_font = Res.getFont("thick"); final IFont thick_font = Res.getFont("thick");
final RowLayout rl = new RowLayout(root, 5); final RowLayout rl = new RowLayout(root, 5);
rl.setRect(root.shrink(Num.ZERO, root.height().perc(15))); rl.setRect(root.shrink(Num.ZERO, root.height().perc(15)));
root.add(rl); root.add(rl);
final TextPainter txp = new TextPainter(thick_font, AlignX.CENTER, RGB.YELLOW, "You won!"); final TextPainter txp = new TextPainter(thick_font, AlignX.CENTER, RGB.YELLOW, "You won!");
rl.add(txp, 1); rl.add(txp, 1);
txp.setVPaddingPercent(13); txp.setVPaddingPercent(13);
LinearLayout linl = new LinearLayout(root, AlignX.CENTER); LinearLayout linl = new LinearLayout(root, AlignX.CENTER);
linl.add(new ImagePainter(Res.getTxQuad("win"))); linl.add(new ImagePainter(Res.getTxQuad("win")));
rl.add(linl, 3); rl.add(linl, 3);
linl = new LinearLayout(root, AlignX.CENTER); linl = new LinearLayout(root, AlignX.CENTER);
rl.add(linl); rl.add(linl);
final TextButton btn1 = new TextButton(thick_font, "Leave", ScreenGame.COLOR_BTN_GOOD); final TextButton btn1 = new TextButton(thick_font, "Leave", ScreenGame.COLOR_BTN_GOOD);
btn1.textPainter.setVPaddingPercent(25); btn1.textPainter.setVPaddingPercent(25);
linl.add(btn1); linl.add(btn1);
final Action quit = new Action() { final Action quit = new Action() {
@Override @Override
protected void execute() protected void execute()
{ {
// delete the save file // delete the save file
final File f = WorldProvider.get().getWorld().getSaveFile(); final File f = WorldProvider.get().getWorld().getSaveFile();
f.delete(); f.delete();
App.bus().send(new RogueStateRequest(RogueState.MAIN_MENU)); App.bus().send(new RogueStateRequest(RogueState.MAIN_MENU));
} }
}; };
btn1.setAction(quit); btn1.setAction(quit);
bindKey(App.cfg().getKeyStroke("general.confirm"), Trigger.RISING, quit); bindKey(App.cfg().getKeyStroke("general.confirm"), Trigger.RISING, quit);
bindKey(App.cfg().getKeyStroke("general.close"), Trigger.RISING, quit); bindKey(App.cfg().getKeyStroke("general.close"), Trigger.RISING, quit);
} }
@Override @Override
public int getZIndex() public int getZIndex()
{ {
return 300; return 300;
} }
@Override @Override
protected void onShowFinished() protected void onShowFinished()
{ {
App.audio().fadeOutAllLoops(); App.audio().fadeOutAllLoops();
Res.getSoundEffect("game.win").play(1); Res.getSoundEffect("game.win").play(1);
} }
} }

@ -9,28 +9,29 @@ import mightypork.gamecore.resources.Res;
/** /**
* Button in the ingame nav * Button in the ingame nav
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class NavButton extends ClickableComponent { public class NavButton extends ClickableComponent {
private final TxQuad base, hover, down, fg; private final TxQuad base, hover, down, fg;
public NavButton(TxQuad fg) { public NavButton(TxQuad fg)
{
super(); super();
this.base = Res.getTxQuad("nav.button.bg.base"); this.base = Res.getTxQuad("nav.button.bg.base");
this.hover = Res.getTxQuad("nav.button.bg.hover"); this.hover = Res.getTxQuad("nav.button.bg.hover");
this.down = Res.getTxQuad("nav.button.bg.down"); this.down = Res.getTxQuad("nav.button.bg.down");
this.fg = fg; this.fg = fg;
} }
@Override @Override
protected void renderComponent() protected void renderComponent()
{ {
TxQuad bg; TxQuad bg;
if (btnDownOver) { if (btnDownOver) {
bg = down; bg = down;
} else if (isMouseOver()) { } else if (isMouseOver()) {
@ -38,11 +39,11 @@ public class NavButton extends ClickableComponent {
} else { } else {
bg = base; bg = base;
} }
if (!isEnabled()) bg = base; // override effects if (!isEnabled()) bg = base; // override effects
App.gfx().quad(this, bg); App.gfx().quad(this, bg);
App.gfx().quad(this, fg); App.gfx().quad(this, fg);
} }
} }

@ -25,34 +25,34 @@ import mightypork.utils.math.color.Color;
public class ScreenGame extends RogueScreen implements PlayerDeathHandler, GameWinHandler { public class ScreenGame extends RogueScreen implements PlayerDeathHandler, GameWinHandler {
public static final Color COLOR_BTN_GOOD = Color.fromHex(0x28CB2D); public static final Color COLOR_BTN_GOOD = Color.fromHex(0x28CB2D);
public static final Color COLOR_BTN_BAD = Color.fromHex(0xCB2828); public static final Color COLOR_BTN_BAD = Color.fromHex(0xCB2828);
public static final Color COLOR_BTN_CANCEL = Color.fromHex(0xFFFB55); public static final Color COLOR_BTN_CANCEL = Color.fromHex(0xFFFB55);
/** /**
* Game gui state. * Game gui state.
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public enum GScrState public enum GScrState
{ {
WORLD, INV, DEATH, WIN, GOTO_MENU, GOTO_QUIT; WORLD, INV, DEATH, WIN, GOTO_MENU, GOTO_QUIT;
} }
private LayerInv invLayer; private LayerInv invLayer;
private LayerGameUi hudLayer; private LayerGameUi hudLayer;
private LayerDeath deathLayer; private LayerDeath deathLayer;
private LayerWin winLayer; private LayerWin winLayer;
private LayerMapView worldLayer; private LayerMapView worldLayer;
private GScrState state = null; private GScrState state = null;
private final ActionGroup worldActions = new ActionGroup(); private final ActionGroup worldActions = new ActionGroup();
public Action actionEat = new Action() { public Action actionEat = new Action() {
@Override @Override
public void execute() public void execute()
{ {
@ -61,50 +61,50 @@ public class ScreenGame extends RogueScreen implements PlayerDeathHandler, GameW
pl.tryToEatSomeFood(); pl.tryToEatSomeFood();
} }
}; };
public Action actionToggleInv = new Action() { public Action actionToggleInv = new Action() {
@Override @Override
public void execute() public void execute()
{ {
setState(getState() == GScrState.INV ? GScrState.WORLD : GScrState.INV); setState(getState() == GScrState.INV ? GScrState.WORLD : GScrState.INV);
} }
}; };
public Action actionToggleMinimap = new Action() { public Action actionToggleMinimap = new Action() {
@Override @Override
public void execute() public void execute()
{ {
hudLayer.miniMap.setVisible(!hudLayer.miniMap.isVisible()); hudLayer.miniMap.setVisible(!hudLayer.miniMap.isVisible());
} }
}; };
public Action actionTogglePause = new Action() { public Action actionTogglePause = new Action() {
@Override @Override
public void execute() public void execute()
{ {
App.bus().send(new WorldPauseRequest(PauseAction.TOGGLE)); App.bus().send(new WorldPauseRequest(PauseAction.TOGGLE));
} }
}; };
public Action actionToggleZoom = new Action() { public Action actionToggleZoom = new Action() {
@Override @Override
public void execute() public void execute()
{ {
worldLayer.map.toggleMag(); worldLayer.map.toggleMag();
} }
}; };
public Action actionSave = new Action() { public Action actionSave = new Action() {
@Override @Override
public void execute() public void execute()
{ {
if (WorldProvider.get().getWorld().isGameOver()) return; if (WorldProvider.get().getWorld().isGameOver()) return;
try { try {
WorldProvider.get().saveWorld(); WorldProvider.get().saveWorld();
WorldProvider.get().getWorld().getConsole().msgWorldSaved(); WorldProvider.get().getWorld().getConsole().msgWorldSaved();
@ -114,46 +114,46 @@ public class ScreenGame extends RogueScreen implements PlayerDeathHandler, GameW
} }
} }
}; };
public Action actionLoad = new Action() { public Action actionLoad = new Action() {
@Override @Override
public void execute() public void execute()
{ {
if (WorldProvider.get().getWorld().isGameOver()) return; if (WorldProvider.get().getWorld().isGameOver()) return;
try { try {
final File f = WorldProvider.get().getWorld().getSaveFile(); final File f = WorldProvider.get().getWorld().getSaveFile();
WorldProvider.get().loadWorld(f); WorldProvider.get().loadWorld(f);
WorldProvider.get().getWorld().getConsole().msgReloaded(); WorldProvider.get().getWorld().getConsole().msgReloaded();
} catch (final Exception e) { } catch (final Exception e) {
Log.e("Could not load the world.", e); Log.e("Could not load the world.", e);
WorldProvider.get().getWorld().getConsole().msgLoadFailed(); WorldProvider.get().getWorld().getConsole().msgLoadFailed();
} }
} }
}; };
public Action actionMenu = new Action() { public Action actionMenu = new Action() {
@Override @Override
public void execute() public void execute()
{ {
setState(GScrState.GOTO_MENU); setState(GScrState.GOTO_MENU);
} }
}; };
public Action actionQuit = new Action() { public Action actionQuit = new Action() {
@Override @Override
public void execute() public void execute()
{ {
setState(GScrState.GOTO_QUIT); setState(GScrState.GOTO_QUIT);
} }
}; };
public Action actionDropLastPickedItem = new Action() { public Action actionDropLastPickedItem = new Action() {
@Override @Override
public void execute() public void execute()
{ {
@ -163,62 +163,62 @@ public class ScreenGame extends RogueScreen implements PlayerDeathHandler, GameW
} }
}; };
private LayerAskSave askSaveLayer; private LayerAskSave askSaveLayer;
/** /**
* Set gui state (overlay) * Set gui state (overlay)
* *
* @param nstate new state * @param nstate new state
*/ */
public void setState(GScrState nstate) public void setState(GScrState nstate)
{ {
if (this.state == nstate) return; if (this.state == nstate) return;
final boolean gameOver = WorldProvider.get().getWorld().isGameOver(); final boolean gameOver = WorldProvider.get().getWorld().isGameOver();
if (nstate != GScrState.WORLD && state == GScrState.WORLD) { // leaving world. if (nstate != GScrState.WORLD && state == GScrState.WORLD) { // leaving world.
App.bus().send(new WorldPauseRequest(PauseAction.PAUSE)); App.bus().send(new WorldPauseRequest(PauseAction.PAUSE));
worldActions.setEnabled(false); // disable world actions worldActions.setEnabled(false); // disable world actions
} }
if (nstate != GScrState.DEATH) deathLayer.hide(); if (nstate != GScrState.DEATH) deathLayer.hide();
if (nstate != GScrState.WIN) winLayer.hide(); if (nstate != GScrState.WIN) winLayer.hide();
if (nstate != GScrState.INV) invLayer.hide(); if (nstate != GScrState.INV) invLayer.hide();
if (nstate != GScrState.GOTO_MENU && nstate != GScrState.GOTO_QUIT) askSaveLayer.hide(); if (nstate != GScrState.GOTO_MENU && nstate != GScrState.GOTO_QUIT) askSaveLayer.hide();
Runnable task; Runnable task;
switch (nstate) { switch (nstate) {
case WORLD: case WORLD:
App.bus().send(new WorldPauseRequest(PauseAction.RESUME)); App.bus().send(new WorldPauseRequest(PauseAction.RESUME));
worldActions.setEnabled(true); worldActions.setEnabled(true);
break; break;
case INV: case INV:
invLayer.show(); invLayer.show();
break; break;
case DEATH: case DEATH:
deathLayer.show(); deathLayer.show();
break; break;
case WIN: case WIN:
winLayer.show(); winLayer.show();
break; break;
case GOTO_MENU: case GOTO_MENU:
case GOTO_QUIT: case GOTO_QUIT:
final RogueState goal = (nstate == GScrState.GOTO_MENU) ? RogueState.MAIN_MENU : RogueState.EXIT; final RogueState goal = (nstate == GScrState.GOTO_MENU) ? RogueState.MAIN_MENU : RogueState.EXIT;
task = new Runnable() { task = new Runnable() {
@Override @Override
public void run() public void run()
{ {
App.bus().send(new RogueStateRequest(goal)); App.bus().send(new RogueStateRequest(goal));
} }
}; };
if (gameOver) { if (gameOver) {
task.run(); task.run();
} else { } else {
@ -226,68 +226,69 @@ public class ScreenGame extends RogueScreen implements PlayerDeathHandler, GameW
askSaveLayer.show(); askSaveLayer.show();
} }
} }
this.state = nstate; this.state = nstate;
} }
public GScrState getState() public GScrState getState()
{ {
return state; return state;
} }
public ScreenGame() { public ScreenGame()
{
addLayer(invLayer = new LayerInv(this)); addLayer(invLayer = new LayerInv(this));
addLayer(deathLayer = new LayerDeath(this)); addLayer(deathLayer = new LayerDeath(this));
addLayer(winLayer = new LayerWin(this)); addLayer(winLayer = new LayerWin(this));
addLayer(hudLayer = new LayerGameUi(this)); addLayer(hudLayer = new LayerGameUi(this));
addLayer(worldLayer = new LayerMapView(this)); addLayer(worldLayer = new LayerMapView(this));
addLayer(askSaveLayer = new LayerAskSave(this)); addLayer(askSaveLayer = new LayerAskSave(this));
Config cfg = App.cfg(); final Config cfg = App.cfg();
bindKey(cfg.getKeyStroke("game.pause"), Trigger.RISING, actionTogglePause); bindKey(cfg.getKeyStroke("game.pause"), Trigger.RISING, actionTogglePause);
bindKey(cfg.getKeyStroke("game.inventory"), Trigger.RISING, actionToggleInv); bindKey(cfg.getKeyStroke("game.inventory"), Trigger.RISING, actionToggleInv);
bindKey(cfg.getKeyStroke("game.drop"), Trigger.RISING, actionDropLastPickedItem); bindKey(cfg.getKeyStroke("game.drop"), Trigger.RISING, actionDropLastPickedItem);
bindKey(cfg.getKeyStroke("game.eat"), Trigger.RISING, actionEat); bindKey(cfg.getKeyStroke("game.eat"), Trigger.RISING, actionEat);
bindKey(cfg.getKeyStroke("game.minimap"), Trigger.RISING, actionToggleMinimap); bindKey(cfg.getKeyStroke("game.minimap"), Trigger.RISING, actionToggleMinimap);
bindKey(cfg.getKeyStroke("game.zoom"), Trigger.RISING, actionToggleZoom); bindKey(cfg.getKeyStroke("game.zoom"), Trigger.RISING, actionToggleZoom);
bindKey(cfg.getKeyStroke("game.load"), Trigger.RISING, actionLoad); bindKey(cfg.getKeyStroke("game.load"), Trigger.RISING, actionLoad);
bindKey(cfg.getKeyStroke("game.save"), Trigger.RISING, actionSave); bindKey(cfg.getKeyStroke("game.save"), Trigger.RISING, actionSave);
bindKey(cfg.getKeyStroke("game.quit"), Trigger.RISING, actionMenu); bindKey(cfg.getKeyStroke("game.quit"), Trigger.RISING, actionMenu);
// bindKey(new KeyStroke(Keys.W), Edge.RISING, new Runnable() { // bindKey(new KeyStroke(Keys.W), Edge.RISING, new Runnable() {
// //
// @Override // @Override
// public void run() // public void run()
// { // {
// setState(GScrState.WIN); // setState(GScrState.WIN);
// } // }
// }); // });
// add as actions - enableables. // add as actions - enableables.
worldActions.add(worldLayer); worldActions.add(worldLayer);
worldActions.add(hudLayer); worldActions.add(hudLayer);
worldActions.add(actionEat); worldActions.add(actionEat);
worldActions.add(actionToggleMinimap); worldActions.add(actionToggleMinimap);
worldActions.add(actionTogglePause); worldActions.add(actionTogglePause);
worldActions.add(actionToggleZoom); worldActions.add(actionToggleZoom);
worldActions.add(actionSave); worldActions.add(actionSave);
worldActions.add(actionLoad); worldActions.add(actionLoad);
worldActions.add(actionMenu); worldActions.add(actionMenu);
worldActions.add(actionDropLastPickedItem); worldActions.add(actionDropLastPickedItem);
worldActions.setEnabled(true); worldActions.setEnabled(true);
// CHEAT - X-ray // CHEAT - X-ray
bindKey(cfg.getKeyStroke("game.cheat.xray"), Trigger.RISING, new Runnable() { bindKey(cfg.getKeyStroke("game.cheat.xray"), Trigger.RISING, new Runnable() {
@Override @Override
public void run() public void run()
{ {
@ -295,32 +296,32 @@ public class ScreenGame extends RogueScreen implements PlayerDeathHandler, GameW
} }
}); });
} }
@Override @Override
protected void onScreenEnter() protected void onScreenEnter()
{ {
super.onScreenEnter(); super.onScreenEnter();
WorldProvider.get().setListening(true); WorldProvider.get().setListening(true);
setState(GScrState.WORLD); setState(GScrState.WORLD);
hideAllPopups(); hideAllPopups();
App.audio().fadeOutAllLoops(); App.audio().fadeOutAllLoops();
Res.getSoundLoop("music.dungeon").fadeIn(); Res.getSoundLoop("music.dungeon").fadeIn();
} }
@Override @Override
protected void onScreenLeave() protected void onScreenLeave()
{ {
super.onScreenLeave(); super.onScreenLeave();
WorldProvider.get().setListening(false); WorldProvider.get().setListening(false);
Res.getSoundLoop("music.dungeon").fadeOut(); Res.getSoundLoop("music.dungeon").fadeOut();
} }
private void hideAllPopups() private void hideAllPopups()
{ {
invLayer.hideImmediate(); invLayer.hideImmediate();
@ -328,29 +329,29 @@ public class ScreenGame extends RogueScreen implements PlayerDeathHandler, GameW
winLayer.hideImmediate(); winLayer.hideImmediate();
askSaveLayer.hideImmediate(); askSaveLayer.hideImmediate();
} }
@Override @Override
public void onPlayerKilled() public void onPlayerKilled()
{ {
setState(GScrState.DEATH); setState(GScrState.DEATH);
} }
@Override @Override
public void onGameWon() public void onGameWon()
{ {
setState(GScrState.WIN); setState(GScrState.WIN);
} }
@Override @Override
public void onShutdown(ShutdownEvent event) public void onShutdown(ShutdownEvent event)
{ {
// if player is dead, don't ask don't ask for save // if player is dead, don't ask don't ask for save
final PlayerFacade pl = WorldProvider.get().getPlayer(); final PlayerFacade pl = WorldProvider.get().getPlayer();
if (pl.isDead()) return; if (pl.isDead()) return;
actionQuit.run(); actionQuit.run();
event.consume(); event.consume();
} }

@ -2,8 +2,6 @@ package mightypork.rogue.screens.menu;
import mightypork.gamecore.core.App; import mightypork.gamecore.core.App;
import mightypork.gamecore.core.config.Config;
import mightypork.gamecore.core.events.ShutdownEvent;
import mightypork.gamecore.graphics.fonts.IFont; import mightypork.gamecore.graphics.fonts.IFont;
import mightypork.gamecore.gui.Action; import mightypork.gamecore.gui.Action;
import mightypork.gamecore.gui.components.input.TextButton; import mightypork.gamecore.gui.components.input.TextButton;
@ -27,51 +25,51 @@ import mightypork.utils.math.constraints.rect.Rect;
/** /**
* Main menu screen * Main menu screen
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class ScreenMainMenu extends RogueScreen { public class ScreenMainMenu extends RogueScreen {
/** /**
* The layer * The layer
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
class MenuLayer extends ScreenLayer { class MenuLayer extends ScreenLayer {
public MenuLayer(Screen screen) public MenuLayer(Screen screen)
{ {
super(screen); super(screen);
init(); init();
} }
private void init() private void init()
{ {
final Rect menuBox = root.shrink(Num.ZERO, root.height().perc(15)).moveY(root.height().perc(-4)); final Rect menuBox = root.shrink(Num.ZERO, root.height().perc(15)).moveY(root.height().perc(-4));
final QuadPainter bg = QuadPainter.gradV(Color.fromHex(0x007eb3), PAL16.SEABLUE); final QuadPainter bg = QuadPainter.gradV(Color.fromHex(0x007eb3), PAL16.SEABLUE);
bg.setRect(root); bg.setRect(root);
root.add(bg); root.add(bg);
final RowLayout rows = new RowLayout(menuBox, 13); final RowLayout rows = new RowLayout(menuBox, 13);
rows.enableCaching(true); rows.enableCaching(true);
root.add(rows); root.add(rows);
final LinearLayout linlayout = new LinearLayout(root, AlignX.CENTER); final LinearLayout linlayout = new LinearLayout(root, AlignX.CENTER);
linlayout.add(new ImagePainter(Res.getTxQuad("logo"))); linlayout.add(new ImagePainter(Res.getTxQuad("logo")));
rows.add(linlayout, 4); rows.add(linlayout, 4);
rows.skip(1); rows.skip(1);
TextButton btn; TextButton btn;
final IFont btnFont = Res.getFont("thick"); final IFont btnFont = Res.getFont("thick");
// world button // world button
btn = new TextButton(btnFont, "Play", PAL16.SLIMEGREEN); btn = new TextButton(btnFont, "Play", PAL16.SLIMEGREEN);
btn.setAction(new Action() { btn.setAction(new Action() {
@Override @Override
protected void execute() protected void execute()
{ {
@ -80,10 +78,10 @@ public class ScreenMainMenu extends RogueScreen {
}); });
rows.add(btn, 2); rows.add(btn, 2);
rows.skip(1); rows.skip(1);
btn = new TextButton(btnFont, "Story", PAL16.CLOUDBLUE); btn = new TextButton(btnFont, "Story", PAL16.CLOUDBLUE);
btn.setAction(new Action() { btn.setAction(new Action() {
@Override @Override
protected void execute() protected void execute()
{ {
@ -92,11 +90,11 @@ public class ScreenMainMenu extends RogueScreen {
}); });
rows.add(btn, 2); rows.add(btn, 2);
rows.skip(1); rows.skip(1);
// quit button // quit button
btn = new TextButton(btnFont, "Exit", PAL16.BLOODRED); btn = new TextButton(btnFont, "Exit", PAL16.BLOODRED);
btn.setAction(new Action() { btn.setAction(new Action() {
@Override @Override
protected void execute() protected void execute()
{ {
@ -104,9 +102,9 @@ public class ScreenMainMenu extends RogueScreen {
} }
}); });
rows.add(btn, 2); rows.add(btn, 2);
bindKey(App.cfg().getKeyStroke("general.close"), Trigger.RISING, new Runnable() { bindKey(App.cfg().getKeyStroke("general.close"), Trigger.RISING, new Runnable() {
@Override @Override
public void run() public void run()
{ {
@ -114,28 +112,28 @@ public class ScreenMainMenu extends RogueScreen {
} }
}); });
} }
@Override @Override
public int getZIndex() public int getZIndex()
{ {
return 2; return 2;
} }
} }
public ScreenMainMenu() public ScreenMainMenu()
{ {
addLayer(new MenuLayer(this)); addLayer(new MenuLayer(this));
} }
@Override @Override
protected void onScreenEnter() protected void onScreenEnter()
{ {
super.onScreenEnter(); super.onScreenEnter();
App.audio().fadeOutAllLoops(); App.audio().fadeOutAllLoops();
Res.getSoundLoop("music.menu").fadeIn(); Res.getSoundLoop("music.menu").fadeIn();
} }

@ -2,8 +2,6 @@ package mightypork.rogue.screens.select_world;
import mightypork.gamecore.core.App; import mightypork.gamecore.core.App;
import mightypork.gamecore.core.WorkDir;
import mightypork.gamecore.core.config.Config;
import mightypork.gamecore.gui.components.layout.RowLayout; import mightypork.gamecore.gui.components.layout.RowLayout;
import mightypork.gamecore.gui.components.painters.QuadPainter; import mightypork.gamecore.gui.components.painters.QuadPainter;
import mightypork.gamecore.gui.components.painters.TextPainter; import mightypork.gamecore.gui.components.painters.TextPainter;
@ -14,6 +12,7 @@ import mightypork.gamecore.resources.Res;
import mightypork.rogue.RogueStateManager.RogueState; import mightypork.rogue.RogueStateManager.RogueState;
import mightypork.rogue.events.RogueStateRequest; import mightypork.rogue.events.RogueStateRequest;
import mightypork.rogue.screens.RogueScreen; import mightypork.rogue.screens.RogueScreen;
import mightypork.utils.files.WorkDir;
import mightypork.utils.logging.Log; import mightypork.utils.logging.Log;
import mightypork.utils.math.AlignX; import mightypork.utils.math.AlignX;
import mightypork.utils.math.color.Color; import mightypork.utils.math.color.Color;
@ -24,59 +23,61 @@ import mightypork.utils.math.constraints.rect.Rect;
/** /**
* Main menu screen * Main menu screen
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class ScreenSelectWorld extends RogueScreen { public class ScreenSelectWorld extends RogueScreen {
public ScreenSelectWorld() { public ScreenSelectWorld()
{
addLayer(new WorldsLayer(this)); addLayer(new WorldsLayer(this));
} }
class WorldsLayer extends ScreenLayer { class WorldsLayer extends ScreenLayer {
private WorldSlot slot1; private WorldSlot slot1;
private WorldSlot slot2; private WorldSlot slot2;
private WorldSlot slot3; private WorldSlot slot3;
public WorldsLayer(Screen screen) { public WorldsLayer(Screen screen)
{
super(screen); super(screen);
init(); init();
} }
private void init() private void init()
{ {
final Rect menuBox = root.shrink(root.width().perc(25), root.height().perc(30)).moveY(root.height().perc(-10)); final Rect menuBox = root.shrink(root.width().perc(25), root.height().perc(30)).moveY(root.height().perc(-10));
final QuadPainter bg = QuadPainter.gradV(Color.fromHex(0x007eb3), PAL16.SEABLUE); final QuadPainter bg = QuadPainter.gradV(Color.fromHex(0x007eb3), PAL16.SEABLUE);
bg.setRect(root); bg.setRect(root);
root.add(bg); root.add(bg);
final RowLayout rows = new RowLayout(menuBox, 4); final RowLayout rows = new RowLayout(menuBox, 4);
rows.enableCaching(true); rows.enableCaching(true);
root.add(rows); root.add(rows);
TextPainter tp; TextPainter tp;
rows.add(tp = new TextPainter(Res.getFont("thick"), AlignX.CENTER, RGB.YELLOW, "Save slot:")); rows.add(tp = new TextPainter(Res.getFont("thick"), AlignX.CENTER, RGB.YELLOW, "Save slot:"));
tp.setVPaddingPercent(20); tp.setVPaddingPercent(20);
tp.setShadow(RGB.BLACK_50, tp.height().mul(0.6 / 8D).toVectXY()); tp.setShadow(RGB.BLACK_50, tp.height().mul(0.6 / 8D).toVectXY());
slot1 = new WorldSlot(WorkDir.getFile("slot1")); slot1 = new WorldSlot(WorkDir.getFile("slot1"));
rows.add(slot1); rows.add(slot1);
slot2 = new WorldSlot(WorkDir.getFile("slot2")); slot2 = new WorldSlot(WorkDir.getFile("slot2"));
rows.add(slot2); rows.add(slot2);
slot3 = new WorldSlot(WorkDir.getFile("slot3")); slot3 = new WorldSlot(WorkDir.getFile("slot3"));
rows.add(slot3); rows.add(slot3);
// escape to quitn from here // escape to quitn from here
bindKey(App.cfg().getKeyStroke("general.close"), Trigger.RISING, new Runnable() { bindKey(App.cfg().getKeyStroke("general.close"), Trigger.RISING, new Runnable() {
@Override @Override
public void run() public void run()
{ {
@ -84,20 +85,20 @@ public class ScreenSelectWorld extends RogueScreen {
} }
}); });
} }
@Override @Override
public int getZIndex() public int getZIndex()
{ {
return 2; return 2;
} }
@Override @Override
protected void onScreenEnter() protected void onScreenEnter()
{ {
super.onScreenEnter(); super.onScreenEnter();
Log.f3("Refreshing save slots"); Log.f3("Refreshing save slots");
slot1.refresh(); slot1.refresh();
slot2.refresh(); slot2.refresh();

@ -26,120 +26,121 @@ import mightypork.utils.string.StringProvider;
public class WorldSlot extends ConstraintLayout { public class WorldSlot extends ConstraintLayout {
private final StringProvider lblStrp = new StringProvider() { private final StringProvider lblStrp = new StringProvider() {
@Override @Override
public String getString() public String getString()
{ {
return label; return label;
} }
}; };
private File file; private File file;
private String label; private String label;
private IonDataBundle worldBundle; private IonDataBundle worldBundle;
private TextButton loadBtn; private TextButton loadBtn;
private TextButton delBtn; private TextButton delBtn;
public WorldSlot(File worldFile) { public WorldSlot(File worldFile)
{
this.file = worldFile; this.file = worldFile;
final Rect innerRect = shrink(height().perc(5)); final Rect innerRect = shrink(height().perc(5));
final QuadPainter qp = new QuadPainter(RGB.BLACK.withAlpha(new Num() { final QuadPainter qp = new QuadPainter(RGB.BLACK.withAlpha(new Num() {
@Override @Override
public double value() public double value()
{ {
return isMouseOver() ? 0.15 : 0.1; return isMouseOver() ? 0.15 : 0.1;
} }
})); }));
qp.setRect(innerRect); qp.setRect(innerRect);
add(qp); add(qp);
final GridLayout gridl = new GridLayout(1, 8); final GridLayout gridl = new GridLayout(1, 8);
final Num shrinkH = width().perc(8); final Num shrinkH = width().perc(8);
final Num shrinkV = height().perc(10); final Num shrinkV = height().perc(10);
gridl.setRect(innerRect.shrink(shrinkH, shrinkH, shrinkV, shrinkV.half())); gridl.setRect(innerRect.shrink(shrinkH, shrinkH, shrinkV, shrinkV.half()));
add(gridl); add(gridl);
final IFont font = Res.getFont("thick"); final IFont font = Res.getFont("thick");
gridl.put(loadBtn = new TextButton(font, "", RGB.WHITE), 0, 0, 1, 7); gridl.put(loadBtn = new TextButton(font, "", RGB.WHITE), 0, 0, 1, 7);
loadBtn.textPainter.setVPaddingPercent(20); loadBtn.textPainter.setVPaddingPercent(20);
loadBtn.textPainter.setAlign(AlignX.LEFT); loadBtn.textPainter.setAlign(AlignX.LEFT);
loadBtn.textPainter.setText(lblStrp); loadBtn.textPainter.setText(lblStrp);
loadBtn.disableHoverEffect(); loadBtn.disableHoverEffect();
loadBtn.setAction(new Action() { loadBtn.setAction(new Action() {
@Override @Override
protected void execute() protected void execute()
{ {
String msg; String msg;
if (worldBundle != null) { if (worldBundle != null) {
msg = "Loading world..."; msg = "Loading world...";
} else { } else {
msg = "Creating world..."; msg = "Creating world...";
} }
App.bus().send(new LoadingOverlayRequest(msg, new Runnable() { App.bus().send(new LoadingOverlayRequest(msg, new Runnable() {
@Override @Override
public void run() public void run()
{ {
World w; World w;
if (worldBundle == null) { if (worldBundle == null) {
try { try {
w = WorldProvider.get().createWorld(Double.doubleToLongBits(Math.random())); w = WorldProvider.get().createWorld(Double.doubleToLongBits(Math.random()));
w.setSaveFile(file); w.setSaveFile(file);
WorldProvider.get().saveWorld(); WorldProvider.get().saveWorld();
App.bus().send(new ScreenRequest("game")); App.bus().send(new ScreenRequest("game"));
} catch (final Exception t) { } catch (final Exception t) {
Log.e("Could not create & save the world.", t); Log.e("Could not create & save the world.", t);
} }
} else { } else {
try { try {
w = new World(); w = new World();
w.setSaveFile(file); w.setSaveFile(file);
w.load((IonDataBundle) worldBundle.get("world")); w.load((IonDataBundle) worldBundle.get("world"));
WorldProvider.get().setWorld(w); WorldProvider.get().setWorld(w);
App.bus().send(new ScreenRequest("game")); App.bus().send(new ScreenRequest("game"));
} catch (final Exception e) { } catch (final Exception e) {
Log.e("Could not load the world.", e); Log.e("Could not load the world.", e);
} }
} }
} }
})); }));
} }
}); });
gridl.put(delBtn = new TextButton(font, "X", RGB.RED), 0, 7, 1, 1); gridl.put(delBtn = new TextButton(font, "X", RGB.RED), 0, 7, 1, 1);
delBtn.textPainter.setVPaddingPercent(20); delBtn.textPainter.setVPaddingPercent(20);
delBtn.textPainter.setAlign(AlignX.RIGHT); delBtn.textPainter.setAlign(AlignX.RIGHT);
delBtn.disableHoverEffect(); delBtn.disableHoverEffect();
delBtn.setAction(new Action() { delBtn.setAction(new Action() {
@Override @Override
protected void execute() protected void execute()
{ {
@ -148,30 +149,30 @@ public class WorldSlot extends ConstraintLayout {
refresh(); refresh();
} }
}); });
refresh(); refresh();
} }
public void refresh() public void refresh()
{ {
delBtn.setVisible(false); delBtn.setVisible(false);
delBtn.setEnabled(false); delBtn.setEnabled(false);
if (!file.exists()) { if (!file.exists()) {
label = "<empty>"; label = "<empty>";
worldBundle = null; worldBundle = null;
} else { } else {
delBtn.setVisible(true); delBtn.setVisible(true);
delBtn.setEnabled(true); delBtn.setEnabled(true);
try { try {
worldBundle = Ion.fromFile(file); worldBundle = Ion.fromFile(file);
final int lvl = worldBundle.get("level", -1); final int lvl = worldBundle.get("level", -1);
if (lvl == -1) throw new RuntimeException("Invalid save format."); // let the catch block handle it if (lvl == -1) throw new RuntimeException("Invalid save format."); // let the catch block handle it
label = "Level " + (lvl + 1); label = "Level " + (lvl + 1);
} catch (final Exception e) { } catch (final Exception e) {
Log.w("Error loading world save.", e); Log.w("Error loading world save.", e);
@ -179,5 +180,5 @@ public class WorldSlot extends ConstraintLayout {
} }
} }
} }
} }

@ -2,7 +2,6 @@ package mightypork.rogue.screens.story;
import mightypork.gamecore.core.App; import mightypork.gamecore.core.App;
import mightypork.gamecore.core.config.Config;
import mightypork.gamecore.gui.Action; import mightypork.gamecore.gui.Action;
import mightypork.gamecore.gui.components.layout.RowLayout; import mightypork.gamecore.gui.components.layout.RowLayout;
import mightypork.gamecore.gui.components.layout.linear.LinearLayout; import mightypork.gamecore.gui.components.layout.linear.LinearLayout;
@ -31,9 +30,9 @@ import mightypork.utils.math.timing.TimedTask;
public class ScreenStory extends RogueScreen implements MouseButtonHandler { public class ScreenStory extends RogueScreen implements MouseButtonHandler {
private class LayerSlide extends ScreenLayer { private class LayerSlide extends ScreenLayer {
private final TextPainter tp1; private final TextPainter tp1;
private final TextPainter tp2; private final TextPainter tp2;
private final ImagePainter img; private final ImagePainter img;
@ -41,30 +40,30 @@ public class ScreenStory extends RogueScreen implements MouseButtonHandler {
private final NumAnimated tx1alpha = new NumAnimated(0, Easing.BOUNCE_OUT, 1); private final NumAnimated tx1alpha = new NumAnimated(0, Easing.BOUNCE_OUT, 1);
private final NumAnimated tx2alpha = new NumAnimated(0, Easing.BOUNCE_OUT, 1); private final NumAnimated tx2alpha = new NumAnimated(0, Easing.BOUNCE_OUT, 1);
private final NumAnimated txProceedAlpha = new NumAnimated(0, Easing.CIRC_OUT, 1); private final NumAnimated txProceedAlpha = new NumAnimated(0, Easing.CIRC_OUT, 1);
private String nextImg, nextT1, nextT2; private String nextImg, nextT1, nextT2;
private final TimedTask ttNextSlide = new TimedTask() { private final TimedTask ttNextSlide = new TimedTask() {
@Override @Override
public void run() public void run()
{ {
img.setTxQuad(Res.getTxQuad(nextImg)); img.setTxQuad(Res.getTxQuad(nextImg));
if (nextT1 != null) tp1.setText(nextT1); if (nextT1 != null) tp1.setText(nextT1);
if (nextT2 != null) tp2.setText(nextT2); if (nextT2 != null) tp2.setText(nextT2);
tx1alpha.setTo(0); tx1alpha.setTo(0);
tx2alpha.setTo(0); tx2alpha.setTo(0);
txProceedAlpha.setTo(0); txProceedAlpha.setTo(0);
layerAlpha.setTo(0); layerAlpha.setTo(0);
layerAlpha.fadeIn(); layerAlpha.fadeIn();
ttText1.start(1.5); ttText1.start(1.5);
} }
}; };
private final TimedTask ttText1 = new TimedTask() { private final TimedTask ttText1 = new TimedTask() {
@Override @Override
public void run() public void run()
{ {
@ -76,9 +75,9 @@ public class ScreenStory extends RogueScreen implements MouseButtonHandler {
} }
} }
}; };
private final TimedTask ttText2 = new TimedTask() { private final TimedTask ttText2 = new TimedTask() {
@Override @Override
public void run() public void run()
{ {
@ -90,106 +89,108 @@ public class ScreenStory extends RogueScreen implements MouseButtonHandler {
} }
} }
}; };
private final TimedTask ttFinish = new TimedTask() { private final TimedTask ttFinish = new TimedTask() {
@Override @Override
public void run() public void run()
{ {
txProceedAlpha.fadeIn(); txProceedAlpha.fadeIn();
} }
}; };
private final Color textColor = Color.fromHex(0x7ad8ff); private final Color textColor = Color.fromHex(0x7ad8ff);
public LayerSlide(Screen screen) { public LayerSlide(Screen screen)
{
super(screen); super(screen);
final TextPainter help = new TextPainter(Res.getFont("tiny"), AlignX.CENTER, RGB.WHITE.withAlpha(txProceedAlpha.mul(0.3)), "Space / click to proceed."); final TextPainter help = new TextPainter(Res.getFont("tiny"), AlignX.CENTER, RGB.WHITE.withAlpha(txProceedAlpha.mul(0.3)),
"Space / click to proceed.");
help.setRect(root.bottomEdge().growUp(root.height().perc(4))); help.setRect(root.bottomEdge().growUp(root.height().perc(4)));
help.setVPaddingPercent(5); help.setVPaddingPercent(5);
root.add(help); root.add(help);
final Rect contentRect = root.shrink(Num.ZERO, Num.ZERO, root.height().perc(2), root.height().perc(6)); final Rect contentRect = root.shrink(Num.ZERO, Num.ZERO, root.height().perc(2), root.height().perc(6));
final RowLayout rl = new RowLayout(root, 9); final RowLayout rl = new RowLayout(root, 9);
rl.setRect(contentRect); rl.setRect(contentRect);
root.add(rl); root.add(rl);
final LinearLayout ll = new LinearLayout(root, AlignX.CENTER); final LinearLayout ll = new LinearLayout(root, AlignX.CENTER);
rl.add(ll, 7); rl.add(ll, 7);
img = new ImagePainter(Res.getTxQuad("story_1")); img = new ImagePainter(Res.getTxQuad("story_1"));
ll.add(img); ll.add(img);
tp1 = new TextPainter(Res.getFont("tiny"), AlignX.CENTER, textColor.withAlpha(tx1alpha), ""); tp1 = new TextPainter(Res.getFont("tiny"), AlignX.CENTER, textColor.withAlpha(tx1alpha), "");
rl.add(tp1); rl.add(tp1);
tp1.setVPaddingPercent(19); tp1.setVPaddingPercent(19);
tp2 = new TextPainter(Res.getFont("tiny"), AlignX.CENTER, textColor.withAlpha(tx2alpha), ""); tp2 = new TextPainter(Res.getFont("tiny"), AlignX.CENTER, textColor.withAlpha(tx2alpha), "");
rl.add(tp2); rl.add(tp2);
tp2.setVPaddingPercent(19); tp2.setVPaddingPercent(19);
updated.add(layerAlpha); updated.add(layerAlpha);
updated.add(txProceedAlpha); updated.add(txProceedAlpha);
updated.add(tx1alpha); updated.add(tx1alpha);
updated.add(tx2alpha); updated.add(tx2alpha);
updated.add(ttText1); updated.add(ttText1);
updated.add(ttText2); updated.add(ttText2);
updated.add(ttFinish); updated.add(ttFinish);
updated.add(ttNextSlide); updated.add(ttNextSlide);
setAlpha(layerAlpha); setAlpha(layerAlpha);
} }
@Override @Override
public int getZIndex() public int getZIndex()
{ {
return 1; return 1;
} }
public void showSlide(String image, String text1, String text2) public void showSlide(String image, String text1, String text2)
{ {
ttFinish.stop(); ttFinish.stop();
ttNextSlide.stop(); ttNextSlide.stop();
ttText1.stop(); ttText1.stop();
ttText2.stop(); ttText2.stop();
this.nextImg = image; this.nextImg = image;
this.nextT1 = text1; this.nextT1 = text1;
this.nextT2 = text2; this.nextT2 = text2;
layerAlpha.fadeOut(); layerAlpha.fadeOut();
ttNextSlide.start(1); ttNextSlide.start(1);
} }
public void reset() public void reset()
{ {
ttFinish.stop(); ttFinish.stop();
ttNextSlide.stop(); ttNextSlide.stop();
ttText1.stop(); ttText1.stop();
ttText2.stop(); ttText2.stop();
layerAlpha.setTo(0); layerAlpha.setTo(0);
} }
} }
private LayerSlide slideLayer; private LayerSlide slideLayer;
private final Action next = new Action() { private final Action next = new Action() {
@Override @Override
protected void execute() protected void execute()
{ {
showSlide(++slide); showSlide(++slide);
} }
}; };
private final Action prev = new Action() { private final Action prev = new Action() {
@Override @Override
protected void execute() protected void execute()
{ {
@ -197,32 +198,33 @@ public class ScreenStory extends RogueScreen implements MouseButtonHandler {
showSlide(slide); showSlide(slide);
} }
}; };
private final Action close = new Action() { private final Action close = new Action() {
@Override @Override
protected void execute() protected void execute()
{ {
App.bus().send(new RogueStateRequest(RogueState.MAIN_MENU)); App.bus().send(new RogueStateRequest(RogueState.MAIN_MENU));
} }
}; };
public ScreenStory() { public ScreenStory()
{
addLayer(new LayerColor(this, Color.fromHex(0x040c1e), 0)); addLayer(new LayerColor(this, Color.fromHex(0x040c1e), 0));
addLayer(slideLayer = new LayerSlide(this)); addLayer(slideLayer = new LayerSlide(this));
bindKey(new KeyStroke(Keys.SPACE), Trigger.RISING, next); bindKey(new KeyStroke(Keys.SPACE), Trigger.RISING, next);
bindKey(new KeyStroke(Keys.RIGHT), Trigger.RISING, next); bindKey(new KeyStroke(Keys.RIGHT), Trigger.RISING, next);
bindKey(new KeyStroke(Keys.BACKSPACE), Trigger.RISING, prev); bindKey(new KeyStroke(Keys.BACKSPACE), Trigger.RISING, prev);
bindKey(new KeyStroke(Keys.LEFT), Trigger.RISING, prev); bindKey(new KeyStroke(Keys.LEFT), Trigger.RISING, prev);
bindKey(App.cfg().getKeyStroke("general.close"), Trigger.RISING, close); bindKey(App.cfg().getKeyStroke("general.close"), Trigger.RISING, close);
} }
private int slide = 0; private int slide = 0;
@Override @Override
protected void onScreenEnter() protected void onScreenEnter()
{ {
@ -230,29 +232,29 @@ public class ScreenStory extends RogueScreen implements MouseButtonHandler {
slideLayer.reset(); slideLayer.reset();
showSlide(slide); showSlide(slide);
} }
private void showSlide(int slide) private void showSlide(int slide)
{ {
switch (slide) { switch (slide) {
case 0: case 0:
slideLayer.showSlide("story_1", "Man, it's so hot today!", "Makes me real thirsty, ya know."); slideLayer.showSlide("story_1", "Man, it's so hot today!", "Makes me real thirsty, ya know.");
break; break;
case 1: case 1:
slideLayer.showSlide("story_2", "'Guess I'll go get some beer", "from the cellar."); slideLayer.showSlide("story_2", "'Guess I'll go get some beer", "from the cellar.");
break; break;
case 2: case 2:
slideLayer.showSlide("story_3", "Here we go.. HEY GIVE IT BACK!", "I'll hunt you down, thieves!"); slideLayer.showSlide("story_3", "Here we go.. HEY GIVE IT BACK!", "I'll hunt you down, thieves!");
break; break;
case 3: case 3:
App.bus().send(new RogueStateRequest(RogueState.MAIN_MENU)); App.bus().send(new RogueStateRequest(RogueState.MAIN_MENU));
} }
} }
@Override @Override
public void receive(MouseButtonEvent event) public void receive(MouseButtonEvent event)
{ {

@ -11,57 +11,59 @@ import mightypork.utils.ion.IonOutput;
public class Inventory implements IonBinary { public class Inventory implements IonBinary {
public static final short ION_MARK = 54; public static final short ION_MARK = 54;
private Item[] items; private Item[] items;
private int lastAddIndex = 0; private int lastAddIndex = 0;
public Inventory(int size) { public Inventory(int size)
{
this.items = new Item[size]; this.items = new Item[size];
} }
public Inventory() { public Inventory()
{
// ION constructor // ION constructor
} }
@Override @Override
public void load(IonInput in) throws IOException public void load(IonInput in) throws IOException
{ {
final int size = in.readIntByte(); final int size = in.readIntByte();
items = new Item[size]; items = new Item[size];
// for all items in sequence // for all items in sequence
while (in.hasNextEntry()) { while (in.hasNextEntry()) {
// load item index // load item index
final int i = in.readIntByte(); final int i = in.readIntByte();
// load item // load item
setItem(i, Items.loadItem(in)); setItem(i, Items.loadItem(in));
} }
} }
@Override @Override
public void save(IonOutput out) throws IOException public void save(IonOutput out) throws IOException
{ {
// write length // write length
out.writeIntByte(getSize()); out.writeIntByte(getSize());
// find items that are writable // find items that are writable
for (int i = 0; i < getSize(); i++) { for (int i = 0; i < getSize(); i++) {
final Item item = getItem(i); final Item item = getItem(i);
if (item != null && !item.isEmpty()) { if (item != null && !item.isEmpty()) {
// start sequence entry // start sequence entry
out.startEntry(); out.startEntry();
// write index // write index
out.writeIntByte(i); out.writeIntByte(i);
// write item at index // write item at index
Items.saveItem(out, item); Items.saveItem(out, item);
} }
@ -69,36 +71,36 @@ public class Inventory implements IonBinary {
// close sequence // close sequence
out.endSequence(); out.endSequence();
} }
/** /**
* Get item in a slot * Get item in a slot
* *
* @param i slot number * @param i slot number
* @return item in the slot; can be null. * @return item in the slot; can be null.
*/ */
public Item getItem(int i) public Item getItem(int i)
{ {
if (i < 0 || i > getSize()) return null; if (i < 0 || i > getSize()) return null;
verifyIndex(i); verifyIndex(i);
final Item itm = items[i]; final Item itm = items[i];
if (itm == null || itm.isEmpty()) return null; if (itm == null || itm.isEmpty()) return null;
return itm; return itm;
} }
private void verifyIndex(int i) private void verifyIndex(int i)
{ {
if (i < 0 || i > getSize()) { if (i < 0 || i > getSize()) {
throw new IndexOutOfBoundsException("Invalid inventory index: " + i + ", size: " + getSize()); throw new IndexOutOfBoundsException("Invalid inventory index: " + i + ", size: " + getSize());
} }
} }
/** /**
* Put item in a slot * Put item in a slot
* *
* @param i slot number * @param i slot number
* @param item item to store * @param item item to store
*/ */
@ -108,8 +110,8 @@ public class Inventory implements IonBinary {
items[i] = item; items[i] = item;
lastAddIndex = i; lastAddIndex = i;
} }
/** /**
* @return inventory size * @return inventory size
*/ */
@ -117,11 +119,11 @@ public class Inventory implements IonBinary {
{ {
return items.length; return items.length;
} }
/** /**
* Add an item, try to merge first. * Add an item, try to merge first.
* *
* @param stored stored item * @param stored stored item
* @return true if the item was entirely added, and is now empty. * @return true if the item was entirely added, and is now empty.
*/ */
@ -137,7 +139,7 @@ public class Inventory implements IonBinary {
} }
} }
} }
// try to place in a free slot // try to place in a free slot
for (int i = 0; i < getSize(); i++) { for (int i = 0; i < getSize(); i++) {
final Item itm = getItem(i); final Item itm = getItem(i);
@ -147,12 +149,12 @@ public class Inventory implements IonBinary {
return true; return true;
} }
} }
// could not insert. // could not insert.
return false; return false;
} }
/** /**
* Clean empty items * Clean empty items
*/ */
@ -164,28 +166,26 @@ public class Inventory implements IonBinary {
if (itm.isEmpty()) setItem(i, null); if (itm.isEmpty()) setItem(i, null);
} }
} }
public int getLastAddIndex() public int getLastAddIndex()
{ {
return lastAddIndex; return lastAddIndex;
} }
@Override @Override
public String toString() public String toString()
{ {
String s = "Inv["; String s = "Inv[";
for (int i = 0; i < getSize(); i++) { for (int i = 0; i < getSize(); i++) {
if (i > 0) s += ", "; if (i > 0) s += ", ";
s += i + ": "; s += i + ": ";
final Item itm = getItem(i); final Item itm = getItem(i);
if (itm == null) if (itm == null) s += "<null>";
s += "<null>"; else s += itm;
else
s += itm;
} }
s += "]"; s += "]";
return s; return s;

@ -15,85 +15,85 @@ import mightypork.utils.math.constraints.vect.Vect;
public abstract class PlayerControl { public abstract class PlayerControl {
protected Set<EntityMoveListener> playerMoveListeners = new HashSet<>(); protected Set<EntityMoveListener> playerMoveListeners = new HashSet<>();
private World lastWorld; private World lastWorld;
/** /**
* Implementing classes should return a world instance from this method. * Implementing classes should return a world instance from this method.
* *
* @return * @return
*/ */
protected abstract World provideWorld(); protected abstract World provideWorld();
public World getWorld() public World getWorld()
{ {
final World newWorld = provideWorld(); final World newWorld = provideWorld();
lastWorld = newWorld; lastWorld = newWorld;
return newWorld; return newWorld;
} }
public PlayerFacade getPlayer() public PlayerFacade getPlayer()
{ {
if (getWorld() == null) return null; if (getWorld() == null) return null;
return getWorld().getPlayer(); return getWorld().getPlayer();
} }
public void goNorth() public void goNorth()
{ {
go(Move.NORTH); go(Move.NORTH);
} }
public void goSouth() public void goSouth()
{ {
go(Move.SOUTH); go(Move.SOUTH);
} }
public void goEast() public void goEast()
{ {
go(Move.EAST); go(Move.EAST);
} }
public void goWest() public void goWest()
{ {
go(Move.WEST); go(Move.WEST);
} }
public void navigateTo(Coord pos) public void navigateTo(Coord pos)
{ {
if (!getLevel().getTile(pos).isExplored()) return; if (!getLevel().getTile(pos).isExplored()) return;
getPlayer().navigateTo(pos); getPlayer().navigateTo(pos);
} }
public Level getLevel() public Level getLevel()
{ {
return getWorld().getPlayer().getLevel(); return getWorld().getPlayer().getLevel();
} }
public boolean canGo(Move side) public boolean canGo(Move side)
{ {
return getPlayer().canGoTo(side); return getPlayer().canGoTo(side);
} }
/** /**
* Click tile on player's side * Click tile on player's side
* *
* @param side * @param side
* @return * @return
*/ */
@ -101,44 +101,44 @@ public abstract class PlayerControl {
{ {
return doClickTile(getPlayer().getCoord().add(side).toVect()); return doClickTile(getPlayer().getCoord().add(side).toVect());
} }
public boolean clickTile(Vect pos) public boolean clickTile(Vect pos)
{ {
if (pos.dist(getPlayer().getVisualPos().add(0.5, 0.5)).value() < 1.5) { if (pos.dist(getPlayer().getVisualPos().add(0.5, 0.5)).value() < 1.5) {
return doClickTile(pos); return doClickTile(pos);
} }
return false; return false;
} }
private boolean doClickTile(Vect pos) private boolean doClickTile(Vect pos)
{ {
//try to click tile //try to click tile
if (getLevel().getTile(Coord.fromVect(pos)).onClick()) return true; if (getLevel().getTile(Coord.fromVect(pos)).onClick()) return true;
final Tile t = getLevel().getTile(Coord.fromVect(pos)); final Tile t = getLevel().getTile(Coord.fromVect(pos));
if (!t.isPotentiallyWalkable()) return false; // no point in attacking entity thru wall, right? if (!t.isPotentiallyWalkable()) return false; // no point in attacking entity thru wall, right?
//try to hit entity //try to hit entity
final Entity prey = getLevel().getClosestEntity(pos, EntityType.MONSTER, 1); final Entity prey = getLevel().getClosestEntity(pos, EntityType.MONSTER, 1);
if (prey != null) { if (prey != null) {
getPlayer().attack(prey); getPlayer().attack(prey);
return true; return true;
} }
return false; return false;
} }
public void go(Move side) public void go(Move side)
{ {
getPlayer().cancelPath(); getPlayer().cancelPath();
getPlayer().addPathStep(side); getPlayer().addPathStep(side);
} }
public boolean tryGo(Move e) public boolean tryGo(Move e)
{ {
if (!canGo(e)) return false; if (!canGo(e)) return false;

@ -8,36 +8,36 @@ import mightypork.utils.ion.IonDataBundle;
/** /**
* Player information stored in world. * Player information stored in world.
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class PlayerData implements IonBundled { public class PlayerData implements IonBundled {
/** Player inventory size */ /** Player inventory size */
private static final int INV_SIZE = 8; private static final int INV_SIZE = 8;
/** Attack str with bare hands */ /** Attack str with bare hands */
public static final int BARE_ATTACK = 1; public static final int BARE_ATTACK = 1;
private int eid = -1; // marks not initialized private int eid = -1; // marks not initialized
private int level; private int level;
private Inventory inventory = new Inventory(INV_SIZE); private Inventory inventory = new Inventory(INV_SIZE);
private int selectedWeapon = -1; private int selectedWeapon = -1;
@Override @Override
public void load(IonDataBundle bundle) public void load(IonDataBundle bundle)
{ {
eid = bundle.get("eid", -1); eid = bundle.get("eid", -1);
level = bundle.get("floor", -1); level = bundle.get("floor", -1);
selectedWeapon = bundle.get("weapon", -1); selectedWeapon = bundle.get("weapon", -1);
inventory = bundle.get("inv", inventory); inventory = bundle.get("inv", inventory);
} }
@Override @Override
public void save(IonDataBundle bundle) public void save(IonDataBundle bundle)
{ {
@ -46,69 +46,69 @@ public class PlayerData implements IonBundled {
bundle.put("weapon", selectedWeapon); bundle.put("weapon", selectedWeapon);
bundle.put("inv", inventory); bundle.put("inv", inventory);
} }
public void setEID(int eid) public void setEID(int eid)
{ {
if (isInitialized()) throw new RuntimeException("Cannot change player EID."); if (isInitialized()) throw new RuntimeException("Cannot change player EID.");
this.eid = eid; this.eid = eid;
} }
public void setLevelNumber(int level) public void setLevelNumber(int level)
{ {
this.level = level; this.level = level;
} }
public int getEID() public int getEID()
{ {
return eid; return eid;
} }
public int getLevelNumber() public int getLevelNumber()
{ {
return level; return level;
} }
public boolean isInitialized() public boolean isInitialized()
{ {
return eid != -1; return eid != -1;
} }
public Inventory getInventory() public Inventory getInventory()
{ {
return inventory; return inventory;
} }
public int getSelectedWeaponIndex() public int getSelectedWeaponIndex()
{ {
return selectedWeapon; return selectedWeapon;
} }
public boolean hasWeaponSelected() public boolean hasWeaponSelected()
{ {
return !(selectedWeapon < 0 || selectedWeapon >= getInventory().getSize()); return !(selectedWeapon < 0 || selectedWeapon >= getInventory().getSize());
} }
public Item getSelectedWeapon() public Item getSelectedWeapon()
{ {
if (!hasWeaponSelected()) return null; if (!hasWeaponSelected()) return null;
return inventory.getItem(selectedWeapon); return inventory.getItem(selectedWeapon);
} }
public void selectWeapon(int selectedWeapon) public void selectWeapon(int selectedWeapon)
{ {
this.selectedWeapon = selectedWeapon; this.selectedWeapon = selectedWeapon;
if (!hasWeaponSelected()) selectedWeapon = -1; // normalize if (!hasWeaponSelected()) selectedWeapon = -1; // normalize
} }
} }

@ -17,15 +17,15 @@ import mightypork.utils.math.constraints.vect.Vect;
/** /**
* Convenient access to player-related methods and data in world. * Convenient access to player-related methods and data in world.
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class PlayerFacade { public class PlayerFacade {
/** a world */ /** a world */
private final World world; private final World world;
/** /**
* @return true if can go up * @return true if can go up
*/ */
@ -33,16 +33,17 @@ public class PlayerFacade {
{ {
return world.playerData.getLevelNumber() > 0; return world.playerData.getLevelNumber() > 0;
} }
/** /**
* @param world * @param world
*/ */
PlayerFacade(World world) { PlayerFacade(World world)
{
this.world = world; this.world = world;
} }
/** /**
* @return true if can go down * @return true if can go down
*/ */
@ -50,48 +51,48 @@ public class PlayerFacade {
{ {
return world.playerData.getLevelNumber() < world.levels.size() - 1; return world.playerData.getLevelNumber() < world.levels.size() - 1;
} }
/** /**
* Go one level down if applicable * Go one level down if applicable
*/ */
public void descend() public void descend()
{ {
if (!canDescend()) return; if (!canDescend()) return;
final int lvl_num = getLevelNumber(); final int lvl_num = getLevelNumber();
getLevel().removeEntity(world.playerEntity); getLevel().removeEntity(world.playerEntity);
world.playerData.setLevelNumber(lvl_num + 1); world.playerData.setLevelNumber(lvl_num + 1);
getLevel().forceFreeTile(getLevel().getEnterPoint()); getLevel().forceFreeTile(getLevel().getEnterPoint());
getLevel().addEntity(world.playerEntity, getLevel().getEnterPoint()); getLevel().addEntity(world.playerEntity, getLevel().getEnterPoint());
getLevel().explore(getCoord()); getLevel().explore(getCoord());
world.console.msgEnterFloor(getLevelNumber()); world.console.msgEnterFloor(getLevelNumber());
} }
/** /**
* Go one level up if applicable * Go one level up if applicable
*/ */
public void ascend() public void ascend()
{ {
if (!canAscend()) return; if (!canAscend()) return;
final int lvl_num = getLevelNumber(); final int lvl_num = getLevelNumber();
getLevel().removeEntity(world.playerEntity); getLevel().removeEntity(world.playerEntity);
world.playerData.setLevelNumber(lvl_num - 1); world.playerData.setLevelNumber(lvl_num - 1);
getLevel().forceFreeTile(getLevel().getExitPoint()); getLevel().forceFreeTile(getLevel().getExitPoint());
getLevel().addEntity(world.playerEntity, getLevel().getExitPoint()); getLevel().addEntity(world.playerEntity, getLevel().getExitPoint());
getLevel().explore(getCoord()); getLevel().explore(getCoord());
world.console.msgEnterFloor(getLevelNumber()); world.console.msgEnterFloor(getLevelNumber());
} }
/** /**
* @return current level number, zero based. * @return current level number, zero based.
*/ */
@ -99,8 +100,8 @@ public class PlayerFacade {
{ {
return world.playerData.getLevelNumber(); return world.playerData.getLevelNumber();
} }
/** /**
* @return current level * @return current level
*/ */
@ -108,8 +109,8 @@ public class PlayerFacade {
{ {
return world.levels.get(world.playerData.getLevelNumber()); return world.levels.get(world.playerData.getLevelNumber());
} }
/** /**
* @return entity ID * @return entity ID
*/ */
@ -117,8 +118,8 @@ public class PlayerFacade {
{ {
return world.playerData.getEID(); return world.playerData.getEID();
} }
/** /**
* @return entity coordinate in level * @return entity coordinate in level
*/ */
@ -126,8 +127,8 @@ public class PlayerFacade {
{ {
return world.playerEntity.getCoord(); return world.playerEntity.getCoord();
} }
/** /**
* @return entity visual pos in level * @return entity visual pos in level
*/ */
@ -135,19 +136,19 @@ public class PlayerFacade {
{ {
return world.playerEntity.pos.getVisualPos(); return world.playerEntity.pos.getVisualPos();
} }
/** /**
* Find path to * Find path to
* *
* @param pos * @param pos
*/ */
public void navigateTo(Coord pos) public void navigateTo(Coord pos)
{ {
world.playerEntity.pos.navigateTo(pos); world.playerEntity.pos.navigateTo(pos);
} }
/** /**
* Discard steps in buffer * Discard steps in buffer
*/ */
@ -155,108 +156,108 @@ public class PlayerFacade {
{ {
world.playerEntity.pos.cancelPath(); world.playerEntity.pos.cancelPath();
} }
public void addPathStep(Move step) public void addPathStep(Move step)
{ {
world.playerEntity.pos.addStep(step); world.playerEntity.pos.addStep(step);
} }
public boolean isMoving() public boolean isMoving()
{ {
return world.playerEntity.pos.isMoving(); return world.playerEntity.pos.isMoving();
} }
public double getMoveProgress() public double getMoveProgress()
{ {
return world.playerEntity.pos.getProgress(); return world.playerEntity.pos.getProgress();
} }
public boolean isDead() public boolean isDead()
{ {
return world.playerEntity.isDead(); return world.playerEntity.isDead();
} }
public int getHealth() public int getHealth()
{ {
return world.playerEntity.health.getHealth(); return world.playerEntity.health.getHealth();
} }
public int getHealthMax() public int getHealthMax()
{ {
return world.playerEntity.health.getHealthMax(); return world.playerEntity.health.getHealthMax();
} }
public Inventory getInventory() public Inventory getInventory()
{ {
return world.playerData.getInventory(); return world.playerData.getInventory();
} }
public Entity getEntity() public Entity getEntity()
{ {
return world.playerEntity; return world.playerEntity;
} }
public int getAttackStrength() public int getAttackStrength()
{ {
final Item weapon = world.playerData.getSelectedWeapon(); final Item weapon = world.playerData.getSelectedWeapon();
if (weapon == null) return PlayerData.BARE_ATTACK; if (weapon == null) return PlayerData.BARE_ATTACK;
return PlayerData.BARE_ATTACK + weapon.getAttackPoints(); return PlayerData.BARE_ATTACK + weapon.getAttackPoints();
} }
/** /**
* Eat food. * Eat food.
* *
* @param itm food item * @param itm food item
* @return if something was eaten * @return if something was eaten
*/ */
public boolean eatFood(Item itm) public boolean eatFood(Item itm)
{ {
if (itm == null || itm.isEmpty() || itm.getType() != ItemType.FOOD) return false; if (itm == null || itm.isEmpty() || itm.getType() != ItemType.FOOD) return false;
if (getHealth() < getHealthMax()) { if (getHealth() < getHealthMax()) {
world.playerEntity.health.addHealth(itm.getFoodPoints()); world.playerEntity.health.addHealth(itm.getFoodPoints());
itm.consume(); itm.consume();
world.console.msgEat(itm); world.console.msgEat(itm);
return true; return true;
} }
return false; return false;
} }
public void selectWeapon(int selected) public void selectWeapon(int selected)
{ {
world.playerData.selectWeapon(selected); world.playerData.selectWeapon(selected);
} }
public Item getSelectedWeapon() public Item getSelectedWeapon()
{ {
return world.playerData.getSelectedWeapon(); return world.playerData.getSelectedWeapon();
} }
public int getSelectedWeaponIndex() public int getSelectedWeaponIndex()
{ {
return world.playerData.getSelectedWeaponIndex(); return world.playerData.getSelectedWeaponIndex();
} }
public void tryToEatSomeFood() public void tryToEatSomeFood()
{ {
final List<Item> foods = new ArrayList<>(); final List<Item> foods = new ArrayList<>();
@ -266,63 +267,63 @@ public class PlayerFacade {
foods.add(itm); foods.add(itm);
} }
} }
// sort from smallest to biggest foods // sort from smallest to biggest foods
Collections.sort(foods, new Comparator<Item>() { Collections.sort(foods, new Comparator<Item>() {
@Override @Override
public int compare(Item o1, Item o2) public int compare(Item o1, Item o2)
{ {
return (o1.getFoodPoints() - o2.getFoodPoints()); return (o1.getFoodPoints() - o2.getFoodPoints());
} }
}); });
for (final Item itm : foods) { for (final Item itm : foods) {
if (eatFood(itm)) { if (eatFood(itm)) {
getInventory().clean(); getInventory().clean();
return; return;
} }
} }
if (getHealth() < getHealthMax()) { if (getHealth() < getHealthMax()) {
world.console.msgNoMoreFood(); world.console.msgNoMoreFood();
} else { } else {
world.console.msgNotHungry(); world.console.msgNotHungry();
} }
} }
public void attack(Entity prey) public void attack(Entity prey)
{ {
final int attackPoints = getAttackStrength(); final int attackPoints = getAttackStrength();
prey.receiveAttack(world.getPlayer().getEntity(), attackPoints); prey.receiveAttack(world.getPlayer().getEntity(), attackPoints);
if (prey.isDead()) { if (prey.isDead()) {
world.console.msgKill(prey); world.console.msgKill(prey);
} }
final Item wpn = getSelectedWeapon(); final Item wpn = getSelectedWeapon();
if (wpn != null) { if (wpn != null) {
wpn.use(); wpn.use();
if (wpn.isEmpty()) { if (wpn.isEmpty()) {
world.console.msgWeaponBreak(wpn); world.console.msgWeaponBreak(wpn);
getInventory().clean(); getInventory().clean();
selectWeapon(-1); selectWeapon(-1);
pickBestWeaponIfNoneSelected(); pickBestWeaponIfNoneSelected();
} }
} }
} }
private void pickBestWeaponIfNoneSelected() private void pickBestWeaponIfNoneSelected()
{ {
if (getSelectedWeapon() != null) return; if (getSelectedWeapon() != null) return;
final List<Item> wpns = new ArrayList<>(); final List<Item> wpns = new ArrayList<>();
for (int i = 0; i < getInventory().getSize(); i++) { for (int i = 0; i < getInventory().getSize(); i++) {
final Item itm = getInventory().getItem(i); final Item itm = getInventory().getItem(i);
@ -330,17 +331,17 @@ public class PlayerFacade {
wpns.add(itm); wpns.add(itm);
} }
} }
// sort from smallest to biggest foods // sort from smallest to biggest foods
Collections.sort(wpns, new Comparator<Item>() { Collections.sort(wpns, new Comparator<Item>() {
@Override @Override
public int compare(Item o1, Item o2) public int compare(Item o1, Item o2)
{ {
return (o2.getAttackPoints() - o1.getAttackPoints()); return (o2.getAttackPoints() - o1.getAttackPoints());
} }
}); });
for (final Item itm : wpns) { for (final Item itm : wpns) {
for (int i = 0; i < getInventory().getSize(); i++) { for (int i = 0; i < getInventory().getSize(); i++) {
final Item itm2 = getInventory().getItem(i); final Item itm2 = getInventory().getItem(i);
@ -351,75 +352,75 @@ public class PlayerFacade {
} }
break; // just one cycle break; // just one cycle
} }
world.console.msgEquipWeapon(getSelectedWeapon()); world.console.msgEquipWeapon(getSelectedWeapon());
} }
public boolean addItem(Item item) public boolean addItem(Item item)
{ {
if (!getInventory().addItem(item)) { if (!getInventory().addItem(item)) {
world.console.msgCannotPick(); world.console.msgCannotPick();
return false; return false;
} }
world.console.msgPick(item); world.console.msgPick(item);
if (item.getType() == ItemType.WEAPON) { if (item.getType() == ItemType.WEAPON) {
if (getSelectedWeapon() != null) { if (getSelectedWeapon() != null) {
if (item.getAttackPoints() > getSelectedWeapon().getAttackPoints()) { if (item.getAttackPoints() > getSelectedWeapon().getAttackPoints()) {
selectWeapon(-1); // unselect to grab the best one selectWeapon(-1); // unselect to grab the best one
} }
} }
pickBestWeaponIfNoneSelected(); pickBestWeaponIfNoneSelected();
} }
return true; return true;
} }
public void setHealth(int health) public void setHealth(int health)
{ {
world.playerEntity.health.setHealth(health); world.playerEntity.health.setHealth(health);
} }
public void setHealthMax(int health) public void setHealthMax(int health)
{ {
world.playerEntity.health.setHealthMax(health); world.playerEntity.health.setHealthMax(health);
} }
public World getWorld() public World getWorld()
{ {
return world; return world;
} }
public boolean canGoTo(Move side) public boolean canGoTo(Move side)
{ {
return getEntity().pos.canGoTo(side); return getEntity().pos.canGoTo(side);
} }
public void dropItem(int itemIndex) public void dropItem(int itemIndex)
{ {
final Item itm = getInventory().getItem(itemIndex); final Item itm = getInventory().getItem(itemIndex);
if (itm != null && !itm.isEmpty()) { if (itm != null && !itm.isEmpty()) {
final Item piece = itm.split(1); final Item piece = itm.split(1);
getInventory().clean(); getInventory().clean();
Coord dropPos; Coord dropPos;
if (world.playerEntity.pos.isMoving()) { if (world.playerEntity.pos.isMoving()) {
dropPos = world.playerEntity.pos.getLastPos(); dropPos = world.playerEntity.pos.getLastPos();
} else { } else {
dropPos = getCoord(); dropPos = getCoord();
} }
if (!getLevel().getTile(dropPos).dropItem(piece)) { if (!getLevel().getTile(dropPos).dropItem(piece)) {
getInventory().addItem(piece); // add back getInventory().addItem(piece); // add back
} else { } else {

@ -11,7 +11,6 @@ import mightypork.rogue.world.entity.Entity;
import mightypork.rogue.world.events.GameWinHandler; import mightypork.rogue.world.events.GameWinHandler;
import mightypork.rogue.world.events.PlayerDeathHandler; import mightypork.rogue.world.events.PlayerDeathHandler;
import mightypork.rogue.world.level.Level; import mightypork.rogue.world.level.Level;
import mightypork.utils.eventbus.EventBus;
import mightypork.utils.eventbus.clients.DelegatingClient; import mightypork.utils.eventbus.clients.DelegatingClient;
import mightypork.utils.interfaces.Pauseable; import mightypork.utils.interfaces.Pauseable;
import mightypork.utils.interfaces.Updateable; import mightypork.utils.interfaces.Updateable;
@ -23,71 +22,71 @@ import mightypork.utils.math.algo.Coord;
/** /**
* World object. * World object.
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class World implements DelegatingClient, IonBundled, Pauseable, Updateable, PlayerDeathHandler, GameWinHandler { public class World implements DelegatingClient, IonBundled, Pauseable, Updateable, PlayerDeathHandler, GameWinHandler {
// not saved stuffs // not saved stuffs
private final PlayerFacade playerFacade = new PlayerFacade(this); private final PlayerFacade playerFacade = new PlayerFacade(this);
final WorldConsole console = new WorldConsole(); final WorldConsole console = new WorldConsole();
Entity playerEntity; Entity playerEntity;
private int pauseDepth = 0; private int pauseDepth = 0;
private boolean gameOver = false; private boolean gameOver = false;
/** List of world's levels */ /** List of world's levels */
final ArrayList<Level> levels = new ArrayList<>(); final ArrayList<Level> levels = new ArrayList<>();
/** Player data saved together with world */ /** Player data saved together with world */
final PlayerData playerData = new PlayerData(); final PlayerData playerData = new PlayerData();
/** World seed */ /** World seed */
private long seed; private long seed;
/** Next entity ID */ /** Next entity ID */
private int eid; private int eid;
private File saveFile; private File saveFile;
@SuppressWarnings({ "unchecked", "rawtypes" }) @SuppressWarnings({ "unchecked", "rawtypes" })
@Override @Override
public Collection getChildClients() public Collection getChildClients()
{ {
return levels; return levels;
} }
@Override @Override
public boolean doesDelegate() public boolean doesDelegate()
{ {
return !isPaused(); return !isPaused();
} }
@Override @Override
public void load(IonDataBundle in) public void load(IonDataBundle in)
{ {
seed = in.get("seed", seed); seed = in.get("seed", seed);
eid = in.get("next_eid", eid); eid = in.get("next_eid", eid);
in.loadSequence("levels", levels); in.loadSequence("levels", levels);
// join levels to world // join levels to world
for (final Level lvl : levels) { for (final Level lvl : levels) {
lvl.setWorld(this); lvl.setWorld(this);
} }
in.loadBundled("player", playerData); in.loadBundled("player", playerData);
final int eid = playerData.getEID(); final int eid = playerData.getEID();
final int lvl = playerData.getLevelNumber(); final int lvl = playerData.getLevelNumber();
playerEntity = levels.get(lvl).getEntity(eid); playerEntity = levels.get(lvl).getEntity(eid);
if (playerEntity == null) { if (playerEntity == null) {
Log.e("Player entity not found in the world: " + eid + " on floor " + lvl); Log.e("Player entity not found in the world: " + eid + " on floor " + lvl);
for (int i = 0; i < levels.size(); i++) { for (int i = 0; i < levels.size(); i++) {
final Entity ent = levels.get(i).getEntity(eid); final Entity ent = levels.get(i).getEntity(eid);
if (ent != null) { if (ent != null) {
@ -95,12 +94,12 @@ public class World implements DelegatingClient, IonBundled, Pauseable, Updateabl
break; break;
} }
} }
throw new RuntimeException("Player not found in world."); throw new RuntimeException("Player not found in world.");
} }
} }
@Override @Override
public void save(IonDataBundle out) public void save(IonDataBundle out)
{ {
@ -108,31 +107,31 @@ public class World implements DelegatingClient, IonBundled, Pauseable, Updateabl
out.put("next_eid", eid); out.put("next_eid", eid);
out.putSequence("levels", levels); out.putSequence("levels", levels);
out.putBundled("player", playerData); out.putBundled("player", playerData);
// used for peking in before actually loading the world. // used for peking in before actually loading the world.
out.put("meta.last_level", playerData.getLevelNumber()); out.put("meta.last_level", playerData.getLevelNumber());
} }
public void addLevel(Level level) public void addLevel(Level level)
{ {
levels.add(level); levels.add(level);
level.setWorld(this); level.setWorld(this);
} }
public void setSeed(long seed) public void setSeed(long seed)
{ {
this.seed = seed; this.seed = seed;
} }
public long getSeed() public long getSeed()
{ {
return seed; return seed;
} }
/** /**
* @return new entity ID * @return new entity ID
*/ */
@ -140,79 +139,79 @@ public class World implements DelegatingClient, IonBundled, Pauseable, Updateabl
{ {
return eid++; return eid++;
} }
public void createPlayer() public void createPlayer()
{ {
if (playerData.isInitialized()) { if (playerData.isInitialized()) {
throw new RuntimeException("Player already created."); throw new RuntimeException("Player already created.");
} }
// make entity // make entity
final int playerEid = getNewEID(); final int playerEid = getNewEID();
final Level floor = levels.get(0); final Level floor = levels.get(0);
playerEntity = Entities.PLAYER.createEntity(playerEid); playerEntity = Entities.PLAYER.createEntity(playerEid);
final Coord spawn = floor.getEnterPoint(); final Coord spawn = floor.getEnterPoint();
floor.forceFreeTile(spawn); floor.forceFreeTile(spawn);
final Random rand = new Random(seed + 71); final Random rand = new Random(seed + 71);
while (!floor.addEntity(playerEntity, spawn)) { while (!floor.addEntity(playerEntity, spawn)) {
spawn.x += -1 + rand.nextInt(3); spawn.x += -1 + rand.nextInt(3);
spawn.y += -1 + rand.nextInt(3); spawn.y += -1 + rand.nextInt(3);
} }
floor.explore(spawn); floor.explore(spawn);
playerData.setLevelNumber(0); playerData.setLevelNumber(0);
playerData.setEID(playerEid); playerData.setEID(playerEid);
console.msgEnterFloor(0); console.msgEnterFloor(0);
} }
@Override @Override
public void pause() public void pause()
{ {
pauseDepth++; pauseDepth++;
} }
@Override @Override
public void resume() public void resume()
{ {
if (pauseDepth > 0) pauseDepth--; if (pauseDepth > 0) pauseDepth--;
} }
@Override @Override
public boolean isPaused() public boolean isPaused()
{ {
return pauseDepth > 0; return pauseDepth > 0;
} }
public PlayerFacade getPlayer() public PlayerFacade getPlayer()
{ {
return playerFacade; return playerFacade;
} }
@Override @Override
public void update(double delta) public void update(double delta)
{ {
if (isPaused()) return; if (isPaused()) return;
// update console timing - not as child client // update console timing - not as child client
console.update(delta); console.update(delta);
if (saveFile == null) Log.w("World has no save file."); if (saveFile == null) Log.w("World has no save file.");
} }
/** /**
* @return world console * @return world console
*/ */
@ -220,19 +219,19 @@ public class World implements DelegatingClient, IonBundled, Pauseable, Updateabl
{ {
return console; return console;
} }
/** /**
* Set file for saving * Set file for saving
* *
* @param file save file * @param file save file
*/ */
public void setSaveFile(File file) public void setSaveFile(File file)
{ {
this.saveFile = file; this.saveFile = file;
} }
/** /**
* @return assigned file fro saving * @return assigned file fro saving
*/ */
@ -240,22 +239,22 @@ public class World implements DelegatingClient, IonBundled, Pauseable, Updateabl
{ {
return saveFile; return saveFile;
} }
@Override @Override
public void onGameWon() public void onGameWon()
{ {
gameOver = true; gameOver = true;
} }
@Override @Override
public void onPlayerKilled() public void onPlayerKilled()
{ {
gameOver = true; gameOver = true;
} }
public boolean isGameOver() public boolean isGameOver()
{ {
return gameOver || getPlayer().isDead(); return gameOver || getPlayer().isDead();

@ -15,222 +15,223 @@ import mightypork.utils.math.animation.NumAnimated;
/** /**
* Message log in world view * Message log in world view
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class WorldConsole implements Updateable { public class WorldConsole implements Updateable {
/** Used only for display */ /** Used only for display */
public Item lastPickupItem; public Item lastPickupItem;
public double timeSinceLastPickup = Integer.MAX_VALUE; public double timeSinceLastPickup = Integer.MAX_VALUE;
private static final double DURATION = 5; private static final double DURATION = 5;
public class Entry implements Updateable { public class Entry implements Updateable {
private final String text; private final String text;
private final NumAnimated fadeout; private final NumAnimated fadeout;
private boolean fading = false; private boolean fading = false;
private double elapsed = 0; private double elapsed = 0;
private Entry(String text) { private Entry(String text)
{
this.text = text; this.text = text;
this.fadeout = new NumAnimated(1, Easing.LINEAR); this.fadeout = new NumAnimated(1, Easing.LINEAR);
this.fadeout.setDefaultDuration(0.5); this.fadeout.setDefaultDuration(0.5);
} }
@Override @Override
public void update(double delta) public void update(double delta)
{ {
elapsed += delta; elapsed += delta;
if (fading) { if (fading) {
fadeout.update(delta); fadeout.update(delta);
return; return;
} }
if (elapsed > DURATION) { if (elapsed > DURATION) {
fading = true; fading = true;
fadeout.fadeOut(); fadeout.fadeOut();
} }
} }
private boolean canRemove() private boolean canRemove()
{ {
return fading && fadeout.isFinished(); return fading && fadeout.isFinished();
} }
public double getAlpha() public double getAlpha()
{ {
return !fading ? 1 : fadeout.value(); return !fading ? 1 : fadeout.value();
} }
public String getMessage() public String getMessage()
{ {
return text; return text;
} }
public double getAge() public double getAge()
{ {
return elapsed; return elapsed;
} }
} }
private final Deque<Entry> entries = new LinkedBlockingDeque<>(); private final Deque<Entry> entries = new LinkedBlockingDeque<>();
@Override @Override
public void update(double delta) public void update(double delta)
{ {
for (final Iterator<Entry> iter = entries.iterator(); iter.hasNext();) { for (final Iterator<Entry> iter = entries.iterator(); iter.hasNext();) {
final Entry e = iter.next(); final Entry e = iter.next();
e.update(delta); e.update(delta);
if (e.canRemove()) { if (e.canRemove()) {
iter.remove(); iter.remove();
} }
} }
timeSinceLastPickup += delta; timeSinceLastPickup += delta;
} }
public void addMessage(String message) public void addMessage(String message)
{ {
entries.addFirst(new Entry(message)); entries.addFirst(new Entry(message));
} }
public Collection<Entry> getEntries() public Collection<Entry> getEntries()
{ {
return entries; return entries;
} }
public void clear() public void clear()
{ {
entries.clear(); entries.clear();
} }
public void msgCannotPick() public void msgCannotPick()
{ {
addMessage("Can't collect items, inventory is full."); addMessage("Can't collect items, inventory is full.");
} }
public void msgDie(Entity attacker) public void msgDie(Entity attacker)
{ {
addMessage("You've been defeated by " + addArticle(attacker.getVisualName()) + "!"); addMessage("You've been defeated by " + addArticle(attacker.getVisualName()) + "!");
} }
public void msgDiscoverSecretDoor() public void msgDiscoverSecretDoor()
{ {
addMessage("You've discovered a secret door."); addMessage("You've discovered a secret door.");
} }
public void msgEat(Item item) public void msgEat(Item item)
{ {
addMessage("You've eaten " + addArticle(item.getVisualName()) + "."); addMessage("You've eaten " + addArticle(item.getVisualName()) + ".");
} }
public void msgEnterFloor(int floor) public void msgEnterFloor(int floor)
{ {
addMessage("~ Floor " + (1 + floor) + " ~"); addMessage("~ Floor " + (1 + floor) + " ~");
} }
public void msgEquipWeapon(Item item) public void msgEquipWeapon(Item item)
{ {
addMessage("You're now wielding " + (item == null ? "NOTHING" : addArticle(item.getVisualName())) + "."); addMessage("You're now wielding " + (item == null ? "NOTHING" : addArticle(item.getVisualName())) + ".");
} }
public void msgHeartPiece() public void msgHeartPiece()
{ {
addMessage("Your health capacity has been increased."); addMessage("Your health capacity has been increased.");
} }
public void msgKill(Entity prey) public void msgKill(Entity prey)
{ {
addMessage("You've killed " + addArticle(prey.getVisualName()) + "."); addMessage("You've killed " + addArticle(prey.getVisualName()) + ".");
} }
public void msgNoMoreFood() public void msgNoMoreFood()
{ {
addMessage("You don't have any food!"); addMessage("You don't have any food!");
} }
public void msgNotHungry() public void msgNotHungry()
{ {
addMessage("You are not hungry."); addMessage("You are not hungry.");
} }
public void msgPick(Item item) public void msgPick(Item item)
{ {
addMessage("You've picked up " + addArticle(item.getVisualName()) + "."); addMessage("You've picked up " + addArticle(item.getVisualName()) + ".");
lastPickupItem = item; lastPickupItem = item;
timeSinceLastPickup = 0; timeSinceLastPickup = 0;
} }
public void msgDroppedItem(Item item) public void msgDroppedItem(Item item)
{ {
addMessage("You've dropped " + addArticle(item.getVisualName()) + "."); addMessage("You've dropped " + addArticle(item.getVisualName()) + ".");
lastPickupItem = null; lastPickupItem = null;
} }
public void msgWeaponBreak(Item item) public void msgWeaponBreak(Item item)
{ {
addMessage("Your " + item.getVisualName() + " has broken!"); addMessage("Your " + item.getVisualName() + " has broken!");
} }
public void msgWorldSaved() public void msgWorldSaved()
{ {
addMessage("Game saved to file."); addMessage("Game saved to file.");
} }
public void msgWorldSaveError() public void msgWorldSaveError()
{ {
addMessage("Error while saving; See the log for details."); addMessage("Error while saving; See the log for details.");
} }
public void msgReloaded() public void msgReloaded()
{ {
addMessage("World loaded from file."); addMessage("World loaded from file.");
} }
public void msgLoadFailed() public void msgLoadFailed()
{ {
addMessage("Error while loading; See the log for details."); addMessage("Error while loading; See the log for details.");
} }
public void msgOpenChest() public void msgOpenChest()
{ {
addMessage("You've opened a treasure chest!"); addMessage("You've opened a treasure chest!");
} }
private String addArticle(String name) private String addArticle(String name)
{ {
switch (Character.toLowerCase(name.charAt(0))) { switch (Character.toLowerCase(name.charAt(0))) {

@ -14,50 +14,50 @@ import mightypork.utils.logging.Log;
/** /**
* Global singleton world holder and storage * Global singleton world holder and storage
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class WorldProvider extends BusNode { public class WorldProvider extends BusNode {
public static synchronized void init() public static synchronized void init()
{ {
if (inst == null) { if (inst == null) {
inst = new WorldProvider(); inst = new WorldProvider();
} }
} }
public WorldProvider() public WorldProvider()
{ {
setListening(false); setListening(false);
} }
private static WorldProvider inst; private static WorldProvider inst;
public static WorldProvider get() public static WorldProvider get()
{ {
if (inst == null) { if (inst == null) {
throw new IllegalStateException("World provider not initialized."); throw new IllegalStateException("World provider not initialized.");
} }
return inst; return inst;
} }
private World world; private World world;
private final PlayerControl playerControl = new PlayerControl() { private final PlayerControl playerControl = new PlayerControl() {
@Override @Override
protected World provideWorld() protected World provideWorld()
{ {
return world; return world;
} }
}; };
/** /**
* Create and register a world based on a seed * Create and register a world based on a seed
* *
* @param seed random seed * @param seed random seed
* @return the world * @return the world
*/ */
@ -68,35 +68,35 @@ public class WorldProvider extends BusNode {
setWorld(w); setWorld(w);
return w; return w;
} }
public World getWorld() public World getWorld()
{ {
return world; return world;
} }
public void setWorld(World newWorld) public void setWorld(World newWorld)
{ {
if (world != null) removeChildClient(world); if (world != null) removeChildClient(world);
world = newWorld; world = newWorld;
if (newWorld == null) return; if (newWorld == null) return;
addChildClient(world); addChildClient(world);
} }
public void loadWorld(File file) throws IOException public void loadWorld(File file) throws IOException
{ {
Log.f2("Loading world from: " + file); Log.f2("Loading world from: " + file);
final IonDataBundle bu = Ion.fromFile(file); final IonDataBundle bu = Ion.fromFile(file);
setWorld(bu.loadBundled("world", new World())); setWorld(bu.loadBundled("world", new World()));
world.setSaveFile(file); world.setSaveFile(file);
} }
public void saveWorld(File file) throws IOException public void saveWorld(File file) throws IOException
{ {
if (world == null) { if (world == null) {
@ -105,44 +105,44 @@ public class WorldProvider extends BusNode {
if (file == null) { if (file == null) {
throw new IllegalStateException("Trying to save world to a NULL file."); throw new IllegalStateException("Trying to save world to a NULL file.");
} }
if (world.isGameOver()) { if (world.isGameOver()) {
throw new IllegalStateException("Cannot save, player is dead."); throw new IllegalStateException("Cannot save, player is dead.");
} }
Log.f2("Saving world to: " + file); Log.f2("Saving world to: " + file);
final IonDataBundle bu = new IonDataBundle(); final IonDataBundle bu = new IonDataBundle();
bu.put("level", world.getPlayer().getLevelNumber()); bu.put("level", world.getPlayer().getLevelNumber());
bu.putBundled("world", world); bu.putBundled("world", world);
Ion.toFile(file, bu); Ion.toFile(file, bu);
} }
/** /**
* Save to world's assigned save file. * Save to world's assigned save file.
* *
* @throws IOException * @throws IOException
*/ */
public void saveWorld() throws IOException public void saveWorld() throws IOException
{ {
saveWorld(world.getSaveFile()); saveWorld(world.getSaveFile());
} }
public Level getCurrentLevel() public Level getCurrentLevel()
{ {
return getWorld().getPlayer().getLevel(); return getWorld().getPlayer().getLevel();
} }
public PlayerFacade getPlayer() public PlayerFacade getPlayer()
{ {
return getWorld().getPlayer(); return getWorld().getPlayer();
} }
/** /**
* @return constant player control (world independent) * @return constant player control (world independent)
*/ */
@ -150,5 +150,5 @@ public class WorldProvider extends BusNode {
{ {
return playerControl; return playerControl;
} }
} }

@ -20,92 +20,93 @@ import mightypork.utils.math.constraints.vect.VectConst;
/** /**
* World rendering untility * World rendering untility
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class WorldRenderer extends RectProxy { public class WorldRenderer extends RectProxy {
private static final boolean USE_BATCH_RENDERING = true; private static final boolean USE_BATCH_RENDERING = true;
private final Num tileSize; private final Num tileSize;
// can be changed // can be changed
private RectConst mapRect; private RectConst mapRect;
private Level activeLevel; private Level activeLevel;
private final Rect rightShadow; private final Rect rightShadow;
private final Rect leftShadow; private final Rect leftShadow;
private final Rect topShadow; private final Rect topShadow;
private final Rect bottomShadow; private final Rect bottomShadow;
private TileRenderContext trc; private TileRenderContext trc;
public WorldRenderer(Rect viewport, Num tileSize) { public WorldRenderer(Rect viewport, Num tileSize)
{
super(viewport); super(viewport);
this.tileSize = tileSize; this.tileSize = tileSize;
final Num grX = width().perc(30); final Num grX = width().perc(30);
leftShadow = leftEdge().growRight(grX); leftShadow = leftEdge().growRight(grX);
rightShadow = rightEdge().growLeft(grX); rightShadow = rightEdge().growLeft(grX);
final Num grY = height().perc(20); final Num grY = height().perc(20);
topShadow = topEdge().growDown(grY); topShadow = topEdge().growDown(grY);
bottomShadow = bottomEdge().growUp(grY); bottomShadow = bottomEdge().growUp(grY);
} }
private void prepareRenderContextIfNeeded() private void prepareRenderContextIfNeeded()
{ {
final Level level = WorldProvider.get().getCurrentLevel(); final Level level = WorldProvider.get().getCurrentLevel();
if (activeLevel == level) return; if (activeLevel == level) return;
activeLevel = level; activeLevel = level;
mapRect = Rect.make(0, 0, level.getWidth(), level.getHeight()); mapRect = Rect.make(0, 0, level.getWidth(), level.getHeight());
trc = new TileRenderContext(activeLevel, mapRect); trc = new TileRenderContext(activeLevel, mapRect);
} }
private VectConst getOffset() private VectConst getOffset()
{ {
return Vect.make(WorldProvider.get().getPlayer().getVisualPos().neg().sub(0.5, 0.5)).freeze(); return Vect.make(WorldProvider.get().getPlayer().getVisualPos().neg().sub(0.5, 0.5)).freeze();
} }
public void render() public void render()
{ {
prepareRenderContextIfNeeded(); prepareRenderContextIfNeeded();
final GraphicsModule gfx = App.gfx(); final GraphicsModule gfx = App.gfx();
gfx.pushGeometry(); gfx.pushGeometry();
gfx.setColor(RGB.WHITE); gfx.setColor(RGB.WHITE);
gfx.translate(center()); gfx.translate(center());
gfx.scaleXY(tileSize.value()); gfx.scaleXY(tileSize.value());
gfx.translate(getOffset()); gfx.translate(getOffset());
// tiles to render // tiles to render
final Coord pos = WorldProvider.get().getPlayer().getCoord(); final Coord pos = WorldProvider.get().getPlayer().getCoord();
final double w = width().value(); final double w = width().value();
final double h = height().value(); final double h = height().value();
final double ts = tileSize.value(); final double ts = tileSize.value();
final int xtilesh = (int) (w / (ts * 2)) + 1; final int xtilesh = (int) (w / (ts * 2)) + 1;
final int ytilesh = (int) (h / (ts * 2)) + 1; final int ytilesh = (int) (h / (ts * 2)) + 1;
final int x1 = pos.x - xtilesh; final int x1 = pos.x - xtilesh;
final int y1 = pos.y - ytilesh; final int y1 = pos.y - ytilesh;
final int x2 = pos.x + xtilesh; final int x2 = pos.x + xtilesh;
final int y2 = pos.y + ytilesh; final int y2 = pos.y + ytilesh;
// === TILES === // === TILES ===
// batch rendering of the tiles // batch rendering of the tiles
// TODO // TODO
// if (USE_BATCH_RENDERING) { // if (USE_BATCH_RENDERING) {
@ -120,32 +121,32 @@ public class WorldRenderer extends RectProxy {
// if (USE_BATCH_RENDERING) { // if (USE_BATCH_RENDERING) {
// gfx.leaveBatchTexturedQuadMode(); // gfx.leaveBatchTexturedQuadMode();
// } // }
// === ITEMS ON TILES === // === ITEMS ON TILES ===
for (trc.pos.x = x1; trc.pos.x <= x2; trc.pos.x++) { for (trc.pos.x = x1; trc.pos.x <= x2; trc.pos.x++) {
for (trc.pos.y = y1; trc.pos.y <= y2; trc.pos.y++) { for (trc.pos.y = y1; trc.pos.y <= y2; trc.pos.y++) {
trc.renderItems(); trc.renderItems();
} }
} }
// === ENTITIES === // === ENTITIES ===
for (final Entity e : activeLevel.getEntities()) { for (final Entity e : activeLevel.getEntities()) {
// avoid entities out of view rect // avoid entities out of view rect
final Vect entPos = e.pos.getVisualPos(); final Vect entPos = e.pos.getVisualPos();
final int x = (int) Math.round(entPos.x()); final int x = (int) Math.round(entPos.x());
final int y = (int) Math.round(entPos.y()); final int y = (int) Math.round(entPos.y());
if (x < x1 - ts || x > x2 + ts) continue; if (x < x1 - ts || x > x2 + ts) continue;
if (y < y1 - ts || y > y2 + ts) continue; if (y < y1 - ts || y > y2 + ts) continue;
e.render(trc); e.render(trc);
} }
// === unexplored fog === // === unexplored fog ===
// batch rendering of the tiles // batch rendering of the tiles
// TODO // TODO
// if (USE_BATCH_RENDERING) { // if (USE_BATCH_RENDERING) {
@ -162,24 +163,24 @@ public class WorldRenderer extends RectProxy {
// if (USE_BATCH_RENDERING) { // if (USE_BATCH_RENDERING) {
// gfx.leaveBatchTexturedQuadMode(); // gfx.leaveBatchTexturedQuadMode();
// } // }
// === OVERLAY SHADOW === // === OVERLAY SHADOW ===
gfx.popGeometry(); gfx.popGeometry();
gfx.quad(leftShadow, new GradH(RGB.BLACK, RGB.NONE)); gfx.quad(leftShadow, new GradH(RGB.BLACK, RGB.NONE));
gfx.quad(rightShadow, new GradH(RGB.NONE, RGB.BLACK)); gfx.quad(rightShadow, new GradH(RGB.NONE, RGB.BLACK));
gfx.quad(topShadow, new GradV(RGB.BLACK, RGB.NONE)); gfx.quad(topShadow, new GradV(RGB.BLACK, RGB.NONE));
gfx.quad(bottomShadow, new GradV(RGB.NONE, RGB.BLACK)); gfx.quad(bottomShadow, new GradV(RGB.NONE, RGB.BLACK));
} }
public Vect getClickedTile(Vect clickPos) public Vect getClickedTile(Vect clickPos)
{ {
final int ts = (int) tileSize.value(); final int ts = (int) tileSize.value();
final Vect v = clickPos.sub(center().add(getOffset().mul(ts))); final Vect v = clickPos.sub(center().add(getOffset().mul(ts)));
return Vect.make(v.x() / ts, v.y() / ts); return Vect.make(v.x() / ts, v.y() / ts);
} }
} }

@ -7,16 +7,17 @@ import mightypork.utils.math.timing.TaskRepeater;
public abstract class AiTimer extends TaskRepeater implements IonBundled { public abstract class AiTimer extends TaskRepeater implements IonBundled {
public AiTimer(double duration) { public AiTimer(double duration)
{
super(duration); super(duration);
} }
@Override @Override
public abstract void run(); public abstract void run();
@Override @Override
public void load(IonDataBundle bundle) public void load(IonDataBundle bundle)
{ {
@ -26,12 +27,12 @@ public abstract class AiTimer extends TaskRepeater implements IonBundled {
} else { } else {
resume(); resume();
} }
setProgress(bundle.get("progress", getProgress())); setProgress(bundle.get("progress", getProgress()));
setDuration(bundle.get("duration", getDuration())); setDuration(bundle.get("duration", getDuration()));
} }
@Override @Override
public void save(IonDataBundle bundle) public void save(IonDataBundle bundle)
{ {
@ -39,5 +40,5 @@ public abstract class AiTimer extends TaskRepeater implements IonBundled {
bundle.put("progress", getProgress()); bundle.put("progress", getProgress());
bundle.put("duration", getDuration()); bundle.put("duration", getDuration());
} }
} }

@ -14,45 +14,45 @@ import mightypork.utils.ion.IonOutput;
/** /**
* Entity registry * Entity registry
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public final class Entities { public final class Entities {
private static final EntityModel[] entities = new EntityModel[256]; private static final EntityModel[] entities = new EntityModel[256];
public static final EntityModel PLAYER = new EntityModel(1, EntityPlayer.class); public static final EntityModel PLAYER = new EntityModel(1, EntityPlayer.class);
public static final EntityModel RAT_GRAY = new EntityModel(2, EntityGrayRat.class); public static final EntityModel RAT_GRAY = new EntityModel(2, EntityGrayRat.class);
public static final EntityModel RAT_BROWN = new EntityModel(3, EntityBrownRat.class); public static final EntityModel RAT_BROWN = new EntityModel(3, EntityBrownRat.class);
public static final EntityModel RAT_BOSS = new EntityModel(4, EntityBossRat.class); public static final EntityModel RAT_BOSS = new EntityModel(4, EntityBossRat.class);
public static void register(int id, EntityModel model) public static void register(int id, EntityModel model)
{ {
if (id < 0 || id >= entities.length) { if (id < 0 || id >= entities.length) {
throw new IllegalArgumentException("Entity model ID " + id + " is out of range."); throw new IllegalArgumentException("Entity model ID " + id + " is out of range.");
} }
if (entities[id] != null) { if (entities[id] != null) {
throw new IllegalArgumentException("Entity model ID " + id + " already in use."); throw new IllegalArgumentException("Entity model ID " + id + " already in use.");
} }
entities[id] = model; entities[id] = model;
} }
public static EntityModel get(int id) public static EntityModel get(int id)
{ {
final EntityModel e = entities[id]; final EntityModel e = entities[id];
if (e == null) { if (e == null) {
throw new IllegalArgumentException("No entity model with ID " + id + "."); throw new IllegalArgumentException("No entity model with ID " + id + ".");
} }
return e; return e;
} }
public static void loadEntities(IonInput in, Collection<Entity> entities) throws IOException public static void loadEntities(IonInput in, Collection<Entity> entities) throws IOException
{ {
entities.clear(); entities.clear();
@ -60,34 +60,34 @@ public final class Entities {
entities.add(loadEntity(in)); entities.add(loadEntity(in));
} }
} }
public static void saveEntities(IonOutput out, Collection<Entity> entities) throws IOException public static void saveEntities(IonOutput out, Collection<Entity> entities) throws IOException
{ {
for (final Entity entity : entities) { for (final Entity entity : entities) {
out.startEntry(); out.startEntry();
saveEntity(out, entity); saveEntity(out, entity);
} }
out.endSequence(); out.endSequence();
} }
public static Entity loadEntity(IonInput in) throws IOException public static Entity loadEntity(IonInput in) throws IOException
{ {
final int id = in.readIntByte(); final int id = in.readIntByte();
final EntityModel model = get(id); final EntityModel model = get(id);
return model.loadEntity(in); return model.loadEntity(in);
} }
public static void saveEntity(IonOutput out, Entity entity) throws IOException public static void saveEntity(IonOutput out, Entity entity) throws IOException
{ {
final EntityModel model = entity.getModel(); final EntityModel model = entity.getModel();
out.writeIntByte(model.id); out.writeIntByte(model.id);
model.saveEntity(out, entity); model.saveEntity(out, entity);
} }
} }

@ -23,83 +23,84 @@ import mightypork.utils.math.algo.pathfinding.PathFinder;
/** /**
* World entity (mob or player). Entities are attached to the event bus. * World entity (mob or player). Entities are attached to the event bus.
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public abstract class Entity implements IonBundled, Updateable, DelegatingClient { public abstract class Entity implements IonBundled, Updateable, DelegatingClient {
private Level level; private Level level;
private final EntityModel model; private final EntityModel model;
/** Entity ID */ /** Entity ID */
private int entityId = -1; private int entityId = -1;
private final Map<String, EntityModule> modules = new HashMap<>(); private final Map<String, EntityModule> modules = new HashMap<>();
// default modules // default modules
public final EntityModulePosition pos = new EntityModulePosition(this); public final EntityModulePosition pos = new EntityModulePosition(this);
public final EntityModuleHealth health = new EntityModuleHealth(this); public final EntityModuleHealth health = new EntityModuleHealth(this);
private double despawnDelay = 1; private double despawnDelay = 1;
protected Entity lastAttacker; protected Entity lastAttacker;
private boolean freed; private boolean freed;
public Entity(EntityModel model, int eid) { public Entity(EntityModel model, int eid)
{
this.entityId = eid; this.entityId = eid;
this.model = model; this.model = model;
// register modules // register modules
addModule("pos", pos); addModule("pos", pos);
addModule("health", health); addModule("health", health);
} }
@Override @Override
public final void save(IonDataBundle bundle) public final void save(IonDataBundle bundle)
{ {
bundle.put("eid", entityId); bundle.put("eid", entityId);
final IonDataBundle modulesBundle = new IonDataBundle(); final IonDataBundle modulesBundle = new IonDataBundle();
for (final Entry<String, EntityModule> entry : modules.entrySet()) { for (final Entry<String, EntityModule> entry : modules.entrySet()) {
modulesBundle.putBundled(entry.getKey(), entry.getValue()); modulesBundle.putBundled(entry.getKey(), entry.getValue());
} }
bundle.put("modules", modulesBundle); bundle.put("modules", modulesBundle);
final IonDataBundle extra = new IonDataBundle(); final IonDataBundle extra = new IonDataBundle();
saveExtra(extra); saveExtra(extra);
bundle.put("extra", extra); bundle.put("extra", extra);
} }
@Stub @Stub
protected void saveExtra(IonDataBundle bundle) protected void saveExtra(IonDataBundle bundle)
{ {
} }
@Override @Override
public final void load(IonDataBundle bundle) public final void load(IonDataBundle bundle)
{ {
entityId = bundle.get("eid", -1); entityId = bundle.get("eid", -1);
if (entityId < 0) throw new IllegalValueException("Bad entity id: " + entityId); if (entityId < 0) throw new IllegalValueException("Bad entity id: " + entityId);
final IonDataBundle modulesBundle = bundle.get("modules", new IonDataBundle()); final IonDataBundle modulesBundle = bundle.get("modules", new IonDataBundle());
for (final Entry<String, EntityModule> entry : modules.entrySet()) { for (final Entry<String, EntityModule> entry : modules.entrySet()) {
modulesBundle.loadBundled(entry.getKey(), entry.getValue()); modulesBundle.loadBundled(entry.getKey(), entry.getValue());
} }
final IonDataBundle extra = bundle.get("extra", new IonDataBundle()); final IonDataBundle extra = bundle.get("extra", new IonDataBundle());
loadExtra(extra); loadExtra(extra);
} }
@Stub @Stub
protected void loadExtra(IonDataBundle bundle) protected void loadExtra(IonDataBundle bundle)
{ {
} }
protected final void addModule(String key, EntityModule module) protected final void addModule(String key, EntityModule module)
{ {
if (modules.containsKey(key)) { if (modules.containsKey(key)) {
@ -107,8 +108,8 @@ public abstract class Entity implements IonBundled, Updateable, DelegatingClient
} }
modules.put(key, module); modules.put(key, module);
} }
/** /**
* @return unique entity id * @return unique entity id
*/ */
@ -116,53 +117,53 @@ public abstract class Entity implements IonBundled, Updateable, DelegatingClient
{ {
return entityId; return entityId;
} }
public void setLevel(Level level) public void setLevel(Level level)
{ {
if (level != null && entityId < 0) { if (level != null && entityId < 0) {
entityId = level.getWorld().getNewEID(); entityId = level.getWorld().getNewEID();
} }
if (level != null) level.freeTile(getCoord()); if (level != null) level.freeTile(getCoord());
this.level = level; this.level = level;
if (level != null) level.occupyTile(getCoord()); if (level != null) level.occupyTile(getCoord());
} }
public final Level getLevel() public final Level getLevel()
{ {
return level; return level;
} }
public final World getWorld() public final World getWorld()
{ {
return level.getWorld(); return level.getWorld();
} }
public final EntityModel getModel() public final EntityModel getModel()
{ {
return model; return model;
} }
public abstract PathFinder getPathFinder(); public abstract PathFinder getPathFinder();
@Stub @Stub
public final void render(MapRenderContext context) public final void render(MapRenderContext context)
{ {
getRenderer().render(context); getRenderer().render(context);
} }
protected abstract EntityRenderer getRenderer(); protected abstract EntityRenderer getRenderer();
@Override @Override
public void update(double delta) public void update(double delta)
{ {
@ -171,14 +172,14 @@ public abstract class Entity implements IonBundled, Updateable, DelegatingClient
freed = true; freed = true;
} }
} }
/** /**
* @return entity type (used for AI targeting) * @return entity type (used for AI targeting)
*/ */
public abstract EntityType getType(); public abstract EntityType getType();
/** /**
* @return entity coord in level * @return entity coord in level
*/ */
@ -186,18 +187,18 @@ public abstract class Entity implements IonBundled, Updateable, DelegatingClient
{ {
return pos.getCoord(); return pos.getCoord();
} }
public void setCoord(Coord coord) public void setCoord(Coord coord)
{ {
if (level != null) level.freeTile(getCoord()); if (level != null) level.freeTile(getCoord());
pos.setCoord(coord); pos.setCoord(coord);
if (level != null) level.occupyTile(getCoord()); if (level != null) level.occupyTile(getCoord());
} }
/** /**
* Called right after the entity's health reaches zero. * Called right after the entity's health reaches zero.
*/ */
@ -205,8 +206,8 @@ public abstract class Entity implements IonBundled, Updateable, DelegatingClient
public void onKilled() public void onKilled()
{ {
} }
/** /**
* @return true if dead * @return true if dead
*/ */
@ -214,8 +215,8 @@ public abstract class Entity implements IonBundled, Updateable, DelegatingClient
{ {
return health.isDead(); return health.isDead();
} }
/** /**
* @return whether this dead entity can be removed from level * @return whether this dead entity can be removed from level
*/ */
@ -224,8 +225,8 @@ public abstract class Entity implements IonBundled, Updateable, DelegatingClient
{ {
return isDead() && health.getTimeSinceLastDamage() > despawnDelay; return isDead() && health.getTimeSinceLastDamage() > despawnDelay;
} }
/** /**
* Called after the corpse has been cleaned from level. * Called after the corpse has been cleaned from level.
*/ */
@ -233,12 +234,12 @@ public abstract class Entity implements IonBundled, Updateable, DelegatingClient
public void onCorpseRemoved() public void onCorpseRemoved()
{ {
} }
/** /**
* Receive damage from an attacker.<br> * Receive damage from an attacker.<br>
* The entity can decide whether to dodge, reduce damage etc. * The entity can decide whether to dodge, reduce damage etc.
* *
* @param attacker the entity attacking. Can be null for environmental * @param attacker the entity attacking. Can be null for environmental
* damage. * damage.
* @param attackStrength attack strength in health points to take * @param attackStrength attack strength in health points to take
@ -248,48 +249,48 @@ public abstract class Entity implements IonBundled, Updateable, DelegatingClient
this.lastAttacker = attacker; this.lastAttacker = attacker;
health.receiveDamage(attackStrength); health.receiveDamage(attackStrength);
} }
/** /**
* Set how long after being killed is the corpse elligible for removal * Set how long after being killed is the corpse elligible for removal
* *
* @param despawnDelay (secs) * @param despawnDelay (secs)
*/ */
public void setDespawnDelay(double despawnDelay) public void setDespawnDelay(double despawnDelay)
{ {
this.despawnDelay = despawnDelay; this.despawnDelay = despawnDelay;
} }
public double getDespawnDelay() public double getDespawnDelay()
{ {
return despawnDelay; return despawnDelay;
} }
public abstract String getVisualName(); public abstract String getVisualName();
@Override @Override
public boolean doesDelegate() public boolean doesDelegate()
{ {
return true; return true;
} }
@Override @Override
public Collection<?> getChildClients() public Collection<?> getChildClients()
{ {
return modules.values(); return modules.values();
} }
public Entity getLastAttacker() public Entity getLastAttacker()
{ {
return lastAttacker; return lastAttacker;
} }
public double getLastAttackTime() public double getLastAttackTime()
{ {
return health.getTimeSinceLastDamage(); return health.getTimeSinceLastDamage();

@ -11,29 +11,30 @@ import mightypork.utils.ion.IonOutput;
/** /**
* Entity model - builder * Entity model - builder
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public final class EntityModel { public final class EntityModel {
/** Model ID */ /** Model ID */
public final int id; public final int id;
public final Class<? extends Entity> tileClass; public final Class<? extends Entity> tileClass;
public EntityModel(int id, Class<? extends Entity> entity) { public EntityModel(int id, Class<? extends Entity> entity)
{
Entities.register(id, this); Entities.register(id, this);
this.id = id; this.id = id;
this.tileClass = entity; this.tileClass = entity;
} }
public Entity createEntity(World world) public Entity createEntity(World world)
{ {
return createEntity(world.getNewEID()); return createEntity(world.getNewEID());
} }
public Entity createEntity(int eid) public Entity createEntity(int eid)
{ {
try { try {
@ -42,20 +43,20 @@ public final class EntityModel {
throw new RuntimeException("Could not instantiate a tile.", e); throw new RuntimeException("Could not instantiate a tile.", e);
} }
} }
/** /**
* Create entitiy without EID. EID will be assigned when the entity is added * Create entitiy without EID. EID will be assigned when the entity is added
* to a level. * to a level.
* *
* @return entity. * @return entity.
*/ */
public Entity createEntity() public Entity createEntity()
{ {
return createEntity(-1); return createEntity(-1);
} }
public Entity loadEntity(IonInput in) throws IOException public Entity loadEntity(IonInput in) throws IOException
{ {
final IonDataBundle bundle = in.readBundle(); final IonDataBundle bundle = in.readBundle();
@ -63,8 +64,8 @@ public final class EntityModel {
ent.load(bundle); ent.load(bundle);
return ent; return ent;
} }
public void saveEntity(IonOutput out, Entity entity) throws IOException public void saveEntity(IonOutput out, Entity entity) throws IOException
{ {
final IonDataBundle bundle = new IonDataBundle(); final IonDataBundle bundle = new IonDataBundle();

@ -10,39 +10,40 @@ import mightypork.utils.ion.IonDataBundle;
/** /**
* Abstract entity module<br> * Abstract entity module<br>
* Modules make up an entity AI and behavior. * Modules make up an entity AI and behavior.
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public abstract class EntityModule implements IonBundled, Updateable { public abstract class EntityModule implements IonBundled, Updateable {
protected final Entity entity; protected final Entity entity;
public EntityModule(Entity entity) { public EntityModule(Entity entity)
{
this.entity = entity; this.entity = entity;
} }
/** /**
* @return whether the module should be saved into a world file * @return whether the module should be saved into a world file
*/ */
public abstract boolean isModuleSaved(); public abstract boolean isModuleSaved();
@Override @Override
@Stub @Stub
public void load(IonDataBundle bundle) public void load(IonDataBundle bundle)
{ {
} }
@Override @Override
@Stub @Stub
public void save(IonDataBundle bundle) public void save(IonDataBundle bundle)
{ {
} }
@Override @Override
@Stub @Stub
public void update(double delta) public void update(double delta)

@ -12,51 +12,52 @@ import mightypork.utils.math.algo.pathfinding.PathFinder;
/** /**
* Basic Pathfinder implementation for entities * Basic Pathfinder implementation for entities
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class EntityPathFinder extends PathFinder { public class EntityPathFinder extends PathFinder {
protected final Entity entity; protected final Entity entity;
public EntityPathFinder(Entity entity) { public EntityPathFinder(Entity entity)
{
this.entity = entity; this.entity = entity;
} }
@Override @Override
public boolean isAccessible(Coord pos) public boolean isAccessible(Coord pos)
{ {
return entity.getLevel().isWalkable(pos); return entity.getLevel().isWalkable(pos);
} }
@Override @Override
public int getCost(Coord from, Coord to) public int getCost(Coord from, Coord to)
{ {
return 10; return 10;
} }
@Override @Override
public int getMinCost() public int getMinCost()
{ {
return 10; return 10;
} }
@Override @Override
public Heuristic getHeuristic() public Heuristic getHeuristic()
{ {
return PathFinder.CORNER_HEURISTIC; return PathFinder.CORNER_HEURISTIC;
} }
@Override @Override
public List<Move> getWalkSides() public List<Move> getWalkSides()
{ {
return Moves.CARDINAL_SIDES; return Moves.CARDINAL_SIDES;
} }
} }

@ -6,11 +6,11 @@ import mightypork.rogue.world.level.render.MapRenderContext;
/** /**
* Entity renderer * Entity renderer
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public abstract class EntityRenderer { public abstract class EntityRenderer {
public abstract void render(MapRenderContext context); public abstract void render(MapRenderContext context);
} }

@ -3,7 +3,7 @@ package mightypork.rogue.world.entity;
/** /**
* Type of an entity * Type of an entity
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public enum EntityType public enum EntityType

@ -7,47 +7,48 @@ import mightypork.utils.math.Calc;
public class BossRatAi extends GrayRatAi { public class BossRatAi extends GrayRatAi {
private final AiTimer healTimer = new AiTimer(0.5) { private final AiTimer healTimer = new AiTimer(0.5) {
@Override @Override
public void run() public void run()
{ {
entity.health.addHealth(1); // heal entity.health.addHealth(1); // heal
} }
}; };
public BossRatAi(Entity entity) { public BossRatAi(Entity entity)
{
super(entity); super(entity);
setAttackTime(0.3); setAttackTime(0.3);
} }
@Override @Override
protected int getAttackStrength() protected int getAttackStrength()
{ {
return Calc.randInt(2, 3); return Calc.randInt(2, 3);
} }
@Override @Override
protected int getPreyAbandonDistance() protected int getPreyAbandonDistance()
{ {
return Calc.randInt(12, 18); return Calc.randInt(12, 18);
} }
@Override @Override
public void update(double delta) public void update(double delta)
{ {
super.update(delta); super.update(delta);
healTimer.update(delta); healTimer.update(delta);
} }
@Override @Override
protected double getStepTime() protected double getStepTime()
{ {

@ -6,36 +6,37 @@ import mightypork.utils.math.Calc;
public class BrownRatAi extends GrayRatAi { public class BrownRatAi extends GrayRatAi {
public BrownRatAi(Entity entity) { public BrownRatAi(Entity entity)
{
super(entity); super(entity);
setAttackTime(1.2); setAttackTime(1.2);
setScanTime(1.3); setScanTime(1.3);
} }
@Override @Override
protected double getScanRadius() protected double getScanRadius()
{ {
return isIdle() ? Calc.randInt(2, 4) : Calc.randInt(5, 8); return isIdle() ? Calc.randInt(2, 4) : Calc.randInt(5, 8);
} }
@Override @Override
protected int getAttackStrength() protected int getAttackStrength()
{ {
return Calc.randInt(2, 4); return Calc.randInt(2, 4);
} }
@Override @Override
protected int getPreyAbandonDistance() protected int getPreyAbandonDistance()
{ {
return Calc.randInt(7, 12); return Calc.randInt(7, 12);
} }
@Override @Override
protected double getStepTime() protected double getStepTime()
{ {

@ -13,62 +13,63 @@ import mightypork.utils.math.algo.pathfinding.PathFinder;
public class EntityBossRat extends Entity { public class EntityBossRat extends Entity {
private EntityRenderer renderer; private EntityRenderer renderer;
/** Navigation PFC */ /** Navigation PFC */
private final PathFinder pathf = new EntityPathFinder(this); private final PathFinder pathf = new EntityPathFinder(this);
private final BossRatAi ai = new BossRatAi(this); private final BossRatAi ai = new BossRatAi(this);
public EntityBossRat(EntityModel model, int eid) { public EntityBossRat(EntityModel model, int eid)
{
super(model, eid); super(model, eid);
addModule("ai", ai); addModule("ai", ai);
pos.addMoveListener(ai); pos.addMoveListener(ai);
setDespawnDelay(1); setDespawnDelay(1);
health.setHealthMax(80); health.setHealthMax(80);
health.setHealth(80); health.setHealth(80);
health.setHitCooldownTime(0.33); health.setHitCooldownTime(0.33);
} }
@Override @Override
protected EntityRenderer getRenderer() protected EntityRenderer getRenderer()
{ {
if (renderer == null) { if (renderer == null) {
renderer = new EntityRendererMobLR(this, "sprite.rat.boss"); renderer = new EntityRendererMobLR(this, "sprite.rat.boss");
} }
return renderer; return renderer;
} }
@Override @Override
public EntityType getType() public EntityType getType()
{ {
return EntityType.MONSTER; return EntityType.MONSTER;
} }
@Override @Override
public PathFinder getPathFinder() public PathFinder getPathFinder()
{ {
return pathf; return pathf;
} }
@Override @Override
public void onKilled() public void onKilled()
{ {
// send kill event to listeners, after the entity has despawned (disappeared) // send kill event to listeners, after the entity has despawned (disappeared)
App.bus().sendDelayed(new GameWinEvent(), getDespawnDelay() * 1.5); // dramatic pause App.bus().sendDelayed(new GameWinEvent(), getDespawnDelay() * 1.5); // dramatic pause
} }
@Override @Override
public String getVisualName() public String getVisualName()
{ {

@ -13,69 +13,70 @@ import mightypork.utils.math.algo.pathfinding.PathFinder;
public class EntityBrownRat extends Entity { public class EntityBrownRat extends Entity {
private EntityRenderer renderer; private EntityRenderer renderer;
private final PathFinder pathf = new EntityPathFinder(this); private final PathFinder pathf = new EntityPathFinder(this);
private final BrownRatAi ai = new BrownRatAi(this); private final BrownRatAi ai = new BrownRatAi(this);
public EntityBrownRat(EntityModel model, int eid) { public EntityBrownRat(EntityModel model, int eid)
{
super(model, eid); super(model, eid);
addModule("ai", ai); addModule("ai", ai);
pos.addMoveListener(ai); pos.addMoveListener(ai);
setDespawnDelay(1); setDespawnDelay(1);
health.setHealthMax(16); health.setHealthMax(16);
health.setHealth(Calc.randInt(10, 16)); // tougher to kill health.setHealth(Calc.randInt(10, 16)); // tougher to kill
} }
@Override @Override
protected EntityRenderer getRenderer() protected EntityRenderer getRenderer()
{ {
if (renderer == null) { if (renderer == null) {
renderer = new EntityRendererMobLR(this, "sprite.rat.brown"); renderer = new EntityRendererMobLR(this, "sprite.rat.brown");
} }
return renderer; return renderer;
} }
@Override @Override
public EntityType getType() public EntityType getType()
{ {
return EntityType.MONSTER; return EntityType.MONSTER;
} }
@Override @Override
public PathFinder getPathFinder() public PathFinder getPathFinder()
{ {
return pathf; return pathf;
} }
@Override @Override
public void onKilled() public void onKilled()
{ {
// drop rat stuff // drop rat stuff
if (Calc.rand.nextInt(2) != 0) { if (Calc.rand.nextInt(2) != 0) {
getLevel().dropNear(getCoord(), Items.MEAT.createItem()); getLevel().dropNear(getCoord(), Items.MEAT.createItem());
return; return;
} }
if (Calc.rand.nextInt(3) == 0) { if (Calc.rand.nextInt(3) == 0) {
getLevel().dropNear(getCoord(), Items.CHEESE.createItem()); getLevel().dropNear(getCoord(), Items.CHEESE.createItem());
return; return;
} }
} }
@Override @Override
public String getVisualName() public String getVisualName()
{ {

@ -13,74 +13,75 @@ import mightypork.utils.math.algo.pathfinding.PathFinder;
public class EntityGrayRat extends Entity { public class EntityGrayRat extends Entity {
private final PathFinder pathf = new EntityPathFinder(this); private final PathFinder pathf = new EntityPathFinder(this);
private final GrayRatAi ai = new GrayRatAi(this); private final GrayRatAi ai = new GrayRatAi(this);
private EntityRenderer renderer; private EntityRenderer renderer;
public EntityGrayRat(EntityModel model, int eid) { public EntityGrayRat(EntityModel model, int eid)
{
super(model, eid); super(model, eid);
addModule("ai", ai); addModule("ai", ai);
pos.addMoveListener(ai); pos.addMoveListener(ai);
setDespawnDelay(1); setDespawnDelay(1);
health.setHealthMax(7); health.setHealthMax(7);
health.setHealth(Calc.randInt(4, 7)); health.setHealth(Calc.randInt(4, 7));
} }
@Override @Override
public PathFinder getPathFinder() public PathFinder getPathFinder()
{ {
return pathf; return pathf;
} }
@Override @Override
public EntityType getType() public EntityType getType()
{ {
return EntityType.MONSTER; return EntityType.MONSTER;
} }
@Override @Override
protected EntityRenderer getRenderer() protected EntityRenderer getRenderer()
{ {
if (renderer == null) { if (renderer == null) {
renderer = new EntityRendererMobLR(this, "sprite.rat.gray"); renderer = new EntityRendererMobLR(this, "sprite.rat.gray");
} }
return renderer; return renderer;
} }
@Override @Override
public void onKilled() public void onKilled()
{ {
// drop rat stuff // drop rat stuff
if (Calc.rand.nextInt(6) == 0) { if (Calc.rand.nextInt(6) == 0) {
getLevel().dropNear(getCoord(), Items.BONE.createItemDamaged(40)); getLevel().dropNear(getCoord(), Items.BONE.createItemDamaged(40));
return; return;
} }
if (Calc.rand.nextInt(3) == 0) { if (Calc.rand.nextInt(3) == 0) {
getLevel().dropNear(getCoord(), Items.CHEESE.createItem()); getLevel().dropNear(getCoord(), Items.CHEESE.createItem());
return; return;
} }
if (Calc.rand.nextInt(3) == 0) { if (Calc.rand.nextInt(3) == 0) {
getLevel().dropNear(getCoord(), Items.MEAT.createItem()); getLevel().dropNear(getCoord(), Items.MEAT.createItem());
return; return;
} }
} }
@Override @Override
public String getVisualName() public String getVisualName()
{ {

@ -2,7 +2,12 @@ package mightypork.rogue.world.entity.impl;
import mightypork.gamecore.core.App; import mightypork.gamecore.core.App;
import mightypork.rogue.world.entity.*; import mightypork.rogue.world.entity.Entity;
import mightypork.rogue.world.entity.EntityModel;
import mightypork.rogue.world.entity.EntityModule;
import mightypork.rogue.world.entity.EntityPathFinder;
import mightypork.rogue.world.entity.EntityRenderer;
import mightypork.rogue.world.entity.EntityType;
import mightypork.rogue.world.entity.modules.EntityMoveListener; import mightypork.rogue.world.entity.modules.EntityMoveListener;
import mightypork.rogue.world.entity.render.EntityRendererMobLR; import mightypork.rogue.world.entity.render.EntityRendererMobLR;
import mightypork.rogue.world.events.PlayerKilledEvent; import mightypork.rogue.world.events.PlayerKilledEvent;
@ -14,31 +19,32 @@ import mightypork.utils.math.algo.pathfinding.PathFinder;
public class EntityPlayer extends Entity { public class EntityPlayer extends Entity {
class PlayerAi extends EntityModule implements EntityMoveListener { class PlayerAi extends EntityModule implements EntityMoveListener {
public PlayerAi(Entity entity) { public PlayerAi(Entity entity)
{
super(entity); super(entity);
setDespawnDelay(2); setDespawnDelay(2);
health.setHealthMax(6); // 3 hearts health.setHealthMax(6); // 3 hearts
health.fill(); // fill health bar to max health.fill(); // fill health bar to max
health.setHitCooldownTime(0.5); health.setHitCooldownTime(0.5);
} }
@Override @Override
public void onStepFinished() public void onStepFinished()
{ {
entity.getLevel().explore(entity.pos.getCoord()); entity.getLevel().explore(entity.pos.getCoord());
fireEvt(); fireEvt();
// try to pickup items // try to pickup items
final Tile t = getLevel().getTile(getCoord()); final Tile t = getLevel().getTile(getCoord());
if (t.hasItem()) { if (t.hasItem()) {
final Item item = t.pickItem(); final Item item = t.pickItem();
if (item.pickUp(getWorld().getPlayer())) { if (item.pickUp(getWorld().getPlayer())) {
// player picked item // player picked item
} else { } else {
@ -46,101 +52,102 @@ public class EntityPlayer extends Entity {
} }
} }
} }
@Override @Override
public void onPathFinished() public void onPathFinished()
{ {
fireEvt(); fireEvt();
} }
@Override @Override
public void onPathInterrupted() public void onPathInterrupted()
{ {
} }
private void fireEvt() private void fireEvt()
{ {
App.bus().send(new PlayerStepEndEvent(EntityPlayer.this)); App.bus().send(new PlayerStepEndEvent(EntityPlayer.this));
} }
@Override @Override
public boolean isModuleSaved() public boolean isModuleSaved()
{ {
return false; return false;
} }
} }
private PathFinder pathf; private PathFinder pathf;
private EntityRenderer renderer; private EntityRenderer renderer;
private final PlayerAi ai = new PlayerAi(this); private final PlayerAi ai = new PlayerAi(this);
public EntityPlayer(EntityModel model, int eid) { public EntityPlayer(EntityModel model, int eid)
{
super(model, eid); super(model, eid);
pos.setStepTime(0.25); pos.setStepTime(0.25);
addModule("ai", ai); addModule("ai", ai);
pos.addMoveListener(ai); pos.addMoveListener(ai);
} }
@Override @Override
public PathFinder getPathFinder() public PathFinder getPathFinder()
{ {
if (pathf == null) { if (pathf == null) {
pathf = new EntityPathFinder(this) { pathf = new EntityPathFinder(this) {
@Override @Override
public int getCost(Coord from, Coord to) public int getCost(Coord from, Coord to)
{ {
if (!getLevel().getTile(pos.getCoord()).isExplored()) { if (!getLevel().getTile(pos.getCoord()).isExplored()) {
return 1000; // avoid unexplored, but allow them if there's no other way return 1000; // avoid unexplored, but allow them if there's no other way
} }
return super.getCost(from, to); return super.getCost(from, to);
} }
}; };
} }
return pathf; return pathf;
} }
@Override @Override
protected EntityRenderer getRenderer() protected EntityRenderer getRenderer()
{ {
if (renderer == null) { if (renderer == null) {
renderer = new EntityRendererMobLR(this, "sprite.player"); renderer = new EntityRendererMobLR(this, "sprite.player");
} }
return renderer; return renderer;
} }
@Override @Override
public EntityType getType() public EntityType getType()
{ {
return EntityType.PLAYER; return EntityType.PLAYER;
} }
@Override @Override
public void onKilled() public void onKilled()
{ {
// send kill event to listeners, after the entity has despawned (disappeared) // send kill event to listeners, after the entity has despawned (disappeared)
App.bus().sendDelayed(new PlayerKilledEvent(), getDespawnDelay()); App.bus().sendDelayed(new PlayerKilledEvent(), getDespawnDelay());
getWorld().getConsole().msgDie(lastAttacker); getWorld().getConsole().msgDie(lastAttacker);
} }
@Override @Override
public String getVisualName() public String getVisualName()
{ {

@ -6,43 +6,44 @@ import mightypork.utils.math.Calc;
public class GrayRatAi extends MonsterAi { public class GrayRatAi extends MonsterAi {
public GrayRatAi(Entity entity) { public GrayRatAi(Entity entity)
{
super(entity); super(entity);
setAttackTime(1.2); setAttackTime(1.2);
setScanTime(1.5); setScanTime(1.5);
} }
@Override @Override
protected double getScanRadius() protected double getScanRadius()
{ {
return isIdle() ? Calc.randInt(2, 3) : Calc.randInt(4, 6); return isIdle() ? Calc.randInt(2, 3) : Calc.randInt(4, 6);
} }
@Override @Override
protected double getAttackDistance() protected double getAttackDistance()
{ {
return 1.1; return 1.1;
} }
@Override @Override
protected int getAttackStrength() protected int getAttackStrength()
{ {
return Calc.randInt(1, 2); return Calc.randInt(1, 2);
} }
@Override @Override
protected int getPreyAbandonDistance() protected int getPreyAbandonDistance()
{ {
return Calc.randInt(7, 11); return Calc.randInt(7, 11);
} }
@Override @Override
protected double getStepTime() protected double getStepTime()
{ {

@ -20,11 +20,11 @@ import mightypork.utils.math.algo.pathfinding.PathFinderProxy;
public class MonsterAi extends EntityModule implements EntityMoveListener { public class MonsterAi extends EntityModule implements EntityMoveListener {
private boolean chasing = false; private boolean chasing = false;
private final AiTimer timerFindPrey = new AiTimer(3) { private final AiTimer timerFindPrey = new AiTimer(3) {
@Override @Override
public void run() public void run()
{ {
@ -32,143 +32,144 @@ public class MonsterAi extends EntityModule implements EntityMoveListener {
lookForTarget(); lookForTarget();
} }
}; };
private final AiTimer timerAttack = new AiTimer(1) { private final AiTimer timerAttack = new AiTimer(1) {
@Override @Override
public void run() public void run()
{ {
if (!isChasing()) return; if (!isChasing()) return;
final Entity prey = getPreyEntity(); final Entity prey = getPreyEntity();
if (prey == null || prey.isDead()) return; if (prey == null || prey.isDead()) return;
attackPrey(prey); attackPrey(prey);
} }
}; };
private final AiTimer timerRandomWalk = new AiTimer(0.2) { private final AiTimer timerRandomWalk = new AiTimer(0.2) {
@Override @Override
public void run() public void run()
{ {
if (!isIdle()) return; if (!isIdle()) return;
// annoyed by attacking. // annoyed by attacking.
if (entity.getLastAttackTime() < 0.5) { if (entity.getLastAttackTime() < 0.5) {
lookForTarget(); lookForTarget();
return; return;
} }
if (entity.pos.isMoving()) return; if (entity.pos.isMoving()) return;
if (Calc.rand.nextInt(10) == 0) { if (Calc.rand.nextInt(10) == 0) {
entity.pos.addStep(Moves.randomCardinal()); entity.pos.addStep(Moves.randomCardinal());
} }
} }
}; };
private PathFinder noDoorPf; private PathFinder noDoorPf;
/** Prey id */ /** Prey id */
private int preyId = -1; private int preyId = -1;
public MonsterAi(final Entity entity) { public MonsterAi(final Entity entity)
{
super(entity); super(entity);
noDoorPf = new PathFinderProxy(entity.getPathFinder()) { noDoorPf = new PathFinderProxy(entity.getPathFinder()) {
@Override @Override
public boolean isAccessible(Coord pos) public boolean isAccessible(Coord pos)
{ {
final Tile t = entity.getLevel().getTile(pos); final Tile t = entity.getLevel().getTile(pos);
if (t.isDoor()) return false; if (t.isDoor()) return false;
return super.isAccessible(pos); return super.isAccessible(pos);
} }
}; };
noDoorPf.setIgnoreEnd(true); noDoorPf.setIgnoreEnd(true);
timerAttack.start(); timerAttack.start();
timerFindPrey.start(); timerFindPrey.start();
} }
@Override @Override
public void onStepFinished() public void onStepFinished()
{ {
if (entity.isDead()) return; if (entity.isDead()) return;
if (isChasing()) { if (isChasing()) {
final Entity prey = getPreyEntity(); final Entity prey = getPreyEntity();
if (!isPreyValid(prey)) { if (!isPreyValid(prey)) {
stopChasing(); stopChasing();
return; return;
} }
if (!isPreyInAttackRange(prey)) { if (!isPreyInAttackRange(prey)) {
stepTowardsPrey(prey); stepTowardsPrey(prey);
} }
} }
} }
@Override @Override
public void onPathFinished() public void onPathFinished()
{ {
} }
@Override @Override
public void onPathInterrupted() public void onPathInterrupted()
{ {
} }
@Override @Override
public void save(IonDataBundle bundle) public void save(IonDataBundle bundle)
{ {
bundle.putBundled("tscan", timerFindPrey); bundle.putBundled("tscan", timerFindPrey);
bundle.putBundled("tattack", timerAttack); bundle.putBundled("tattack", timerAttack);
bundle.put("chasing", chasing); bundle.put("chasing", chasing);
bundle.put("prey", preyId); bundle.put("prey", preyId);
} }
@Override @Override
public void load(IonDataBundle bundle) public void load(IonDataBundle bundle)
{ {
bundle.loadBundled("tscan", timerFindPrey); bundle.loadBundled("tscan", timerFindPrey);
bundle.loadBundled("tattack", timerAttack); bundle.loadBundled("tattack", timerAttack);
chasing = bundle.get("chasing", chasing); chasing = bundle.get("chasing", chasing);
preyId = bundle.get("prey", preyId); preyId = bundle.get("prey", preyId);
} }
@Override @Override
public boolean isModuleSaved() public boolean isModuleSaved()
{ {
return true; return true;
} }
@Override @Override
public void update(double delta) public void update(double delta)
{ {
if (entity.isDead()) return; if (entity.isDead()) return;
timerFindPrey.update(delta); timerFindPrey.update(delta);
timerAttack.update(delta); timerAttack.update(delta);
timerRandomWalk.update(delta); timerRandomWalk.update(delta);
// go after the prey // go after the prey
if (isChasing() && !entity.pos.isMoving()) { if (isChasing() && !entity.pos.isMoving()) {
final Entity prey = getPreyEntity(); final Entity prey = getPreyEntity();
@ -176,170 +177,170 @@ public class MonsterAi extends EntityModule implements EntityMoveListener {
// prey killed and cleaned from level // prey killed and cleaned from level
stopChasing(); stopChasing();
} }
if (!isPreyInAttackRange(prey)) { if (!isPreyInAttackRange(prey)) {
stepTowardsPrey(prey); stepTowardsPrey(prey);
} }
} }
} }
public boolean isIdle() public boolean isIdle()
{ {
return !chasing; return !chasing;
} }
public boolean isChasing() public boolean isChasing()
{ {
return chasing; return chasing;
} }
private void lookForTarget() private void lookForTarget()
{ {
if (entity.isDead()) return; if (entity.isDead()) return;
final Entity prey = entity.getLevel().getClosestEntity(entity.pos.getVisualPos(), EntityType.PLAYER, getScanRadius()); final Entity prey = entity.getLevel().getClosestEntity(entity.pos.getVisualPos(), EntityType.PLAYER, getScanRadius());
if (prey != null) { if (prey != null) {
// check if reachable without leaving room // check if reachable without leaving room
final List<Coord> noDoorPath = noDoorPf.findPath(entity.getCoord(), prey.getCoord()); final List<Coord> noDoorPath = noDoorPf.findPath(entity.getCoord(), prey.getCoord());
if (noDoorPath == null) return; // cant reach, give up if (noDoorPath == null) return; // cant reach, give up
startChasing(prey); startChasing(prey);
} }
} }
private Entity getPreyEntity() private Entity getPreyEntity()
{ {
return entity.getLevel().getEntity(preyId); return entity.getLevel().getEntity(preyId);
} }
private boolean isPreyInAttackRange(Entity prey) private boolean isPreyInAttackRange(Entity prey)
{ {
return prey.getCoord().dist(entity.getCoord()) <= getAttackDistance(); return prey.getCoord().dist(entity.getCoord()) <= getAttackDistance();
} }
private boolean isPreyValid(Entity prey) private boolean isPreyValid(Entity prey)
{ {
return prey != null && !prey.isDead(); return prey != null && !prey.isDead();
} }
private void startChasing(Entity prey) private void startChasing(Entity prey)
{ {
if (entity.isDead()) return; if (entity.isDead()) return;
preyId = prey.getEntityId(); preyId = prey.getEntityId();
chasing = true; chasing = true;
entity.pos.setStepTime(getStepTime()); entity.pos.setStepTime(getStepTime());
// follow this one prey // follow this one prey
timerFindPrey.pause(); timerFindPrey.pause();
onStepFinished(); // go towards prey onStepFinished(); // go towards prey
} }
private void stopChasing() private void stopChasing()
{ {
chasing = false; chasing = false;
entity.pos.setStepTime(getStepTime()); entity.pos.setStepTime(getStepTime());
preyId = -1; preyId = -1;
timerFindPrey.restart(); timerFindPrey.restart();
} }
private List<Move> getPathToPrey(Entity prey) private List<Move> getPathToPrey(Entity prey)
{ {
if (!isPreyValid(prey)) return null; if (!isPreyValid(prey)) return null;
return entity.getPathFinder().findPathRelative(entity.getCoord(), prey.getCoord(), false, true); return entity.getPathFinder().findPathRelative(entity.getCoord(), prey.getCoord(), false, true);
} }
private void stepTowardsPrey(Entity prey) private void stepTowardsPrey(Entity prey)
{ {
if (entity.isDead()) return; if (entity.isDead()) return;
if (!isPreyValid(prey)) return; if (!isPreyValid(prey)) return;
// if close enough // if close enough
if (isPreyInAttackRange(prey)) { if (isPreyInAttackRange(prey)) {
// attack using the timed loop // attack using the timed loop
return; return;
} }
final List<Move> preyPath = getPathToPrey(prey); final List<Move> preyPath = getPathToPrey(prey);
if (preyPath == null || preyPath.size() > getPreyAbandonDistance()) { if (preyPath == null || preyPath.size() > getPreyAbandonDistance()) {
stopChasing(); stopChasing();
return; return;
} }
entity.pos.cancelPath(); entity.pos.cancelPath();
entity.pos.addSteps(preyPath); entity.pos.addSteps(preyPath);
} }
private void attackPrey(Entity prey) private void attackPrey(Entity prey)
{ {
if (entity.isDead()) return; if (entity.isDead()) return;
if (!isPreyInAttackRange(prey)) return; if (!isPreyInAttackRange(prey)) return;
prey.receiveAttack(entity, getAttackStrength()); prey.receiveAttack(entity, getAttackStrength());
} }
protected void setAttackTime(double secs) protected void setAttackTime(double secs)
{ {
timerAttack.setDuration(secs); timerAttack.setDuration(secs);
} }
protected void setScanTime(double secs) protected void setScanTime(double secs)
{ {
timerFindPrey.setDuration(secs); timerFindPrey.setDuration(secs);
} }
@Stub @Stub
protected double getScanRadius() protected double getScanRadius()
{ {
return isIdle() ? Calc.randInt(1, 3) : Calc.randInt(4, 8); // For override return isIdle() ? Calc.randInt(1, 3) : Calc.randInt(4, 8); // For override
} }
@Stub @Stub
protected int getPreyAbandonDistance() protected int getPreyAbandonDistance()
{ {
return Calc.randInt(5, 8); // For override return Calc.randInt(5, 8); // For override
} }
@Stub @Stub
protected double getAttackDistance() protected double getAttackDistance()
{ {
return 1; return 1;
} }
@Stub @Stub
protected int getAttackStrength() protected int getAttackStrength()
{ {
return 1; // For override return 1; // For override
} }
@Stub @Stub
protected double getStepTime() protected double getStepTime()
{ {

@ -9,19 +9,20 @@ import mightypork.utils.math.Calc;
public class EntityModuleHealth extends EntityModule { public class EntityModuleHealth extends EntityModule {
public EntityModuleHealth(Entity entity) { public EntityModuleHealth(Entity entity)
{
super(entity); super(entity);
} }
protected int health = 1; protected int health = 1;
protected int maxHealth = 1; protected int maxHealth = 1;
private double hitCooldownTime = 0.36; private double hitCooldownTime = 0.36;
protected boolean dead = false; protected boolean dead = false;
private double timeSinceLastDamage = Integer.MAX_VALUE; private double timeSinceLastDamage = Integer.MAX_VALUE;
@Override @Override
public void load(IonDataBundle bundle) public void load(IonDataBundle bundle)
{ {
@ -29,8 +30,8 @@ public class EntityModuleHealth extends EntityModule {
maxHealth = bundle.get("max_health", maxHealth); maxHealth = bundle.get("max_health", maxHealth);
dead = bundle.get("dead", dead); dead = bundle.get("dead", dead);
} }
@Override @Override
public void save(IonDataBundle bundle) public void save(IonDataBundle bundle)
{ {
@ -38,86 +39,86 @@ public class EntityModuleHealth extends EntityModule {
bundle.put("max_health", maxHealth); bundle.put("max_health", maxHealth);
bundle.put("dead", dead); bundle.put("dead", dead);
} }
@Override @Override
public boolean isModuleSaved() public boolean isModuleSaved()
{ {
return true; return true;
} }
public int getHealth() public int getHealth()
{ {
return health; return health;
} }
public void setHealth(int health) public void setHealth(int health)
{ {
this.health = Calc.clamp(health, 0, maxHealth); this.health = Calc.clamp(health, 0, maxHealth);
if (health <= 0) { if (health <= 0) {
setDead(true); setDead(true);
entity.onKilled(); entity.onKilled();
} }
} }
public int getHealthMax() public int getHealthMax()
{ {
return maxHealth; return maxHealth;
} }
public void setHealthMax(int maxHealth) public void setHealthMax(int maxHealth)
{ {
if (maxHealth <= 0) throw new IllegalValueException("Max health out of allowed range: " + maxHealth); if (maxHealth <= 0) throw new IllegalValueException("Max health out of allowed range: " + maxHealth);
this.maxHealth = maxHealth; this.maxHealth = maxHealth;
} }
public boolean isDead() public boolean isDead()
{ {
return dead; return dead;
} }
public void setDead(boolean dead) public void setDead(boolean dead)
{ {
this.dead = dead; this.dead = dead;
} }
public void receiveDamage(int attackStrength) public void receiveDamage(int attackStrength)
{ {
if (timeSinceLastDamage < hitCooldownTime) return; if (timeSinceLastDamage < hitCooldownTime) return;
setHealth(health - attackStrength); setHealth(health - attackStrength);
timeSinceLastDamage = 0; timeSinceLastDamage = 0;
} }
public void addHealth(int healthPoints) public void addHealth(int healthPoints)
{ {
setHealth(health + healthPoints); setHealth(health + healthPoints);
} }
public void fill() public void fill()
{ {
setHealth(maxHealth); setHealth(maxHealth);
} }
@Override @Override
public void update(double delta) public void update(double delta)
{ {
if (timeSinceLastDamage < 3600) timeSinceLastDamage += delta; if (timeSinceLastDamage < 3600) timeSinceLastDamage += delta;
} }
/** /**
* @return seconds since last attack received (can be used for rendering) * @return seconds since last attack received (can be used for rendering)
*/ */
@ -125,11 +126,11 @@ public class EntityModuleHealth extends EntityModule {
{ {
return timeSinceLastDamage; return timeSinceLastDamage;
} }
/** /**
* Set how long after hit another hit can be received. * Set how long after hit another hit can be received.
* *
* @param secs * @param secs
*/ */
public void setHitCooldownTime(double secs) public void setHitCooldownTime(double secs)

@ -16,27 +16,28 @@ import mightypork.utils.math.constraints.vect.VectConst;
public class EntityModulePosition extends EntityModule { public class EntityModulePosition extends EntityModule {
/** Last pos, will be freed upon finishing move */ /** Last pos, will be freed upon finishing move */
private Coord lastPos = new Coord(0, 0); private Coord lastPos = new Coord(0, 0);
private boolean walking = false; private boolean walking = false;
private final Queue<Move> path = new LinkedList<>(); private final Queue<Move> path = new LinkedList<>();
private final EntityPos entityPos = new EntityPos(); private final EntityPos entityPos = new EntityPos();
private double stepTime = 0.5; private double stepTime = 0.5;
// marks for simple renderers // marks for simple renderers
public int lastXDir = 1; public int lastXDir = 1;
public int lastYDir = 1; public int lastYDir = 1;
private final Set<EntityMoveListener> moveListeners = new LinkedHashSet<>(); private final Set<EntityMoveListener> moveListeners = new LinkedHashSet<>();
public EntityModulePosition(Entity entity) { public EntityModulePosition(Entity entity)
{
super(entity); super(entity);
} }
@Override @Override
public void save(IonDataBundle bundle) public void save(IonDataBundle bundle)
{ {
@ -45,43 +46,43 @@ public class EntityModulePosition extends EntityModule {
bundle.putBundled("pos", entityPos); bundle.putBundled("pos", entityPos);
bundle.put("step_time", stepTime); bundle.put("step_time", stepTime);
} }
@Override @Override
public void load(IonDataBundle bundle) public void load(IonDataBundle bundle)
{ {
bundle.loadSequence("path", path); bundle.loadSequence("path", path);
lastPos = bundle.get("lpos", lastPos); lastPos = bundle.get("lpos", lastPos);
bundle.loadBundled("pos", entityPos); bundle.loadBundled("pos", entityPos);
stepTime = bundle.get("step_time", stepTime); stepTime = bundle.get("step_time", stepTime);
} }
@Override @Override
public boolean isModuleSaved() public boolean isModuleSaved()
{ {
return true; return true;
} }
/** /**
* Set coord without animation * Set coord without animation
* *
* @param coord coord * @param coord coord
*/ */
public void setCoord(Coord coord) public void setCoord(Coord coord)
{ {
freeTile(); // release old tile freeTile(); // release old tile
entityPos.setTo(coord); entityPos.setTo(coord);
lastPos.setTo(coord); lastPos.setTo(coord);
cancelPath(); // discard remaining steps cancelPath(); // discard remaining steps
occupyTile(); occupyTile();
} }
/** /**
* Occupy current tile in level * Occupy current tile in level
*/ */
@ -91,8 +92,8 @@ public class EntityModulePosition extends EntityModule {
entity.getLevel().occupyTile(getCoord()); entity.getLevel().occupyTile(getCoord());
} }
} }
/** /**
* free current tile in level * free current tile in level
*/ */
@ -102,8 +103,8 @@ public class EntityModulePosition extends EntityModule {
entity.getLevel().freeTile(getCoord()); entity.getLevel().freeTile(getCoord());
} }
} }
/** /**
* @param delta delta time * @param delta delta time
*/ */
@ -111,47 +112,47 @@ public class EntityModulePosition extends EntityModule {
public void update(double delta) public void update(double delta)
{ {
if (entity.isDead()) return; // corpses dont walk if (entity.isDead()) return; // corpses dont walk
if (!entityPos.isFinished()) { if (!entityPos.isFinished()) {
entityPos.update(delta); entityPos.update(delta);
} }
if (walking && entityPos.isFinished()) { if (walking && entityPos.isFinished()) {
walking = false; walking = false;
for (final EntityMoveListener l : moveListeners) { for (final EntityMoveListener l : moveListeners) {
l.onStepFinished(); l.onStepFinished();
} }
if (path.isEmpty()) { if (path.isEmpty()) {
for (final EntityMoveListener l : moveListeners) { for (final EntityMoveListener l : moveListeners) {
l.onPathFinished(); l.onPathFinished();
} }
} }
} }
if (!walking && !path.isEmpty()) { if (!walking && !path.isEmpty()) {
walking = true; walking = true;
final Move step = path.poll(); final Move step = path.poll();
final Coord planned = entityPos.getCoord().add(step.toCoord()); final Coord planned = entityPos.getCoord().add(step.toCoord());
if (!entity.getLevel().isWalkable(planned)) { if (!entity.getLevel().isWalkable(planned)) {
cancelPath(); cancelPath();
for (final EntityMoveListener l : moveListeners) { for (final EntityMoveListener l : moveListeners) {
l.onPathInterrupted(); l.onPathInterrupted();
} }
walking = false; walking = false;
} else { } else {
// tmp for renderer // tmp for renderer
if (step.x() != 0) this.lastXDir = step.x(); if (step.x() != 0) this.lastXDir = step.x();
if (step.y() != 0) this.lastYDir = step.y(); if (step.y() != 0) this.lastYDir = step.y();
freeTile(); freeTile();
lastPos.setTo(entityPos.getCoord()); lastPos.setTo(entityPos.getCoord());
entityPos.walk(step, stepTime); entityPos.walk(step, stepTime);
@ -159,8 +160,8 @@ public class EntityModulePosition extends EntityModule {
} }
} }
} }
/** /**
* @return true if path buffer is empty * @return true if path buffer is empty
*/ */
@ -168,21 +169,21 @@ public class EntityModulePosition extends EntityModule {
{ {
return entityPos.isFinished() && path.isEmpty(); return entityPos.isFinished() && path.isEmpty();
} }
/** /**
* Add a step to path buffer * Add a step to path buffer
* *
* @param step * @param step
*/ */
public void addStep(Move step) public void addStep(Move step)
{ {
if (path.isEmpty() && !canGoTo(step)) return; if (path.isEmpty() && !canGoTo(step)) return;
path.add(step); path.add(step);
} }
/** /**
* Discard steps in buffer * Discard steps in buffer
*/ */
@ -190,11 +191,11 @@ public class EntityModulePosition extends EntityModule {
{ {
path.clear(); path.clear();
} }
/** /**
* Find path to * Find path to
* *
* @param target * @param target
* @return path found * @return path found
*/ */
@ -202,36 +203,36 @@ public class EntityModulePosition extends EntityModule {
{ {
if (target.equals(getCoord())) return true; if (target.equals(getCoord())) return true;
final List<Move> newPath = entity.getPathFinder().findPathRelative(entityPos.getCoord(), target); final List<Move> newPath = entity.getPathFinder().findPathRelative(entityPos.getCoord(), target);
if (newPath == null) return false; if (newPath == null) return false;
cancelPath(); cancelPath();
addSteps(newPath); addSteps(newPath);
return true; return true;
} }
/** /**
* Add a move listener. If already present, do nothing. * Add a move listener. If already present, do nothing.
* *
* @param listener the listener * @param listener the listener
*/ */
public void addMoveListener(EntityMoveListener listener) public void addMoveListener(EntityMoveListener listener)
{ {
moveListeners.add(listener); moveListeners.add(listener);
} }
/** /**
* Add steps to path buffer * Add steps to path buffer
* *
* @param path steps * @param path steps
*/ */
public void addSteps(List<Move> path) public void addSteps(List<Move> path)
{ {
this.path.addAll(path); this.path.addAll(path);
} }
/** /**
* @return coord in level * @return coord in level
*/ */
@ -239,19 +240,19 @@ public class EntityModulePosition extends EntityModule {
{ {
return entityPos.getCoord(); return entityPos.getCoord();
} }
/** /**
* Set step time (seconds) * Set step time (seconds)
* *
* @param stepTime step time * @param stepTime step time
*/ */
public void setStepTime(double stepTime) public void setStepTime(double stepTime)
{ {
this.stepTime = stepTime; this.stepTime = stepTime;
} }
/** /**
* @return step progress 0..1 * @return step progress 0..1
*/ */
@ -259,8 +260,8 @@ public class EntityModulePosition extends EntityModule {
{ {
return entityPos.getProgress(); return entityPos.getProgress();
} }
/** /**
* @return visual pos in level; interpolated from last to new coord * @return visual pos in level; interpolated from last to new coord
*/ */
@ -268,29 +269,29 @@ public class EntityModulePosition extends EntityModule {
{ {
return entityPos.getVisualPos(); return entityPos.getVisualPos();
} }
public boolean isMoving() public boolean isMoving()
{ {
return walking; return walking;
} }
public boolean hasPath() public boolean hasPath()
{ {
return isMoving() || !path.isEmpty(); return isMoving() || !path.isEmpty();
} }
public boolean canGoTo(Move side) public boolean canGoTo(Move side)
{ {
return entity.getPathFinder().isAccessible(getCoord().add(side)); return entity.getPathFinder().isAccessible(getCoord().add(side));
} }
public Coord getLastPos() public Coord getLastPos()
{ {
return lastPos; return lastPos;
} }
} }

@ -2,22 +2,22 @@ package mightypork.rogue.world.entity.modules;
public interface EntityMoveListener { public interface EntityMoveListener {
/** /**
* One step of a path finished * One step of a path finished
*/ */
void onStepFinished(); void onStepFinished();
/** /**
* Scheduled path finished * Scheduled path finished
*/ */
void onPathFinished(); void onPathFinished();
/** /**
* Path was interrupted (bumped into a wall or entity) * Path was interrupted (bumped into a wall or entity)
*/ */
void onPathInterrupted(); void onPathInterrupted();
} }

@ -14,142 +14,145 @@ import mightypork.utils.math.constraints.vect.VectConst;
/** /**
* Entity position * Entity position
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
class EntityPos implements IonBundled, Updateable { class EntityPos implements IonBundled, Updateable {
private Coord coord = new Coord(0, 0); private Coord coord = new Coord(0, 0);
private final VectAnimated walkOffset = new VectAnimated(Vect.ZERO, Easing.LINEAR); private final VectAnimated walkOffset = new VectAnimated(Vect.ZERO, Easing.LINEAR);
public EntityPos(Coord pos) { public EntityPos(Coord pos)
{
this.coord.setTo(pos); this.coord.setTo(pos);
} }
public EntityPos(int x, int y) { public EntityPos(int x, int y)
{
this.coord.setTo(x, y); this.coord.setTo(x, y);
} }
public EntityPos() { public EntityPos()
{
} }
public double getProgress() public double getProgress()
{ {
return walkOffset.getProgress(); return walkOffset.getProgress();
} }
@Override @Override
public void load(IonDataBundle in) public void load(IonDataBundle in)
{ {
coord = in.get("pos", coord); coord = in.get("pos", coord);
walkOffset.reset(); walkOffset.reset();
} }
@Override @Override
public void save(IonDataBundle out) public void save(IonDataBundle out)
{ {
out.put("pos", coord); out.put("pos", coord);
} }
public int x() public int x()
{ {
return coord.x; return coord.x;
} }
public int y() public int y()
{ {
return coord.y; return coord.y;
} }
public double visualX() public double visualX()
{ {
return coord.x + walkOffset.x(); return coord.x + walkOffset.x();
} }
public double visualY() public double visualY()
{ {
return coord.y + walkOffset.y(); return coord.y + walkOffset.y();
} }
public double visualXOffset() public double visualXOffset()
{ {
return walkOffset.x(); return walkOffset.x();
} }
public double visualYOffset() public double visualYOffset()
{ {
return walkOffset.y(); return walkOffset.y();
} }
public void setTo(int x, int y) public void setTo(int x, int y)
{ {
coord.setTo(x, y); coord.setTo(x, y);
walkOffset.reset(); walkOffset.reset();
} }
public void setTo(EntityPos pos) public void setTo(EntityPos pos)
{ {
setTo(pos.getCoord()); setTo(pos.getCoord());
} }
public void setTo(Coord c) public void setTo(Coord c)
{ {
coord.setTo(c); coord.setTo(c);
walkOffset.reset(); walkOffset.reset();
} }
@Override @Override
public String toString() public String toString()
{ {
return "EntityPos{" + coord + "}"; return "EntityPos{" + coord + "}";
} }
public void walk(Move step, double secs) public void walk(Move step, double secs)
{ {
setTo(coord.x + step.x(), coord.y + step.y()); setTo(coord.x + step.x(), coord.y + step.y());
walkOffset.setTo(-step.x(), -step.y()); walkOffset.setTo(-step.x(), -step.y());
walkOffset.animate(0, 0, 0, secs); walkOffset.animate(0, 0, 0, secs);
} }
@Override @Override
public void update(double delta) public void update(double delta)
{ {
walkOffset.update(delta); walkOffset.update(delta);
} }
public boolean isFinished() public boolean isFinished()
{ {
return walkOffset.isFinished(); return walkOffset.isFinished();
} }
public Coord getCoord() public Coord getCoord()
{ {
return coord; return coord;
} }
@Override @Override
public int hashCode() public int hashCode()
{ {
@ -158,8 +161,8 @@ class EntityPos implements IonBundled, Updateable {
result = prime * result + ((coord == null) ? 0 : coord.hashCode()); result = prime * result + ((coord == null) ? 0 : coord.hashCode());
return result; return result;
} }
@Override @Override
public boolean equals(Object obj) public boolean equals(Object obj)
{ {
@ -172,8 +175,8 @@ class EntityPos implements IonBundled, Updateable {
} else if (!coord.equals(other.coord)) return false; } else if (!coord.equals(other.coord)) return false;
return true; return true;
} }
public VectConst getVisualPos() public VectConst getVisualPos()
{ {
return Vect.make(walkOffset.x() + coord.x, walkOffset.y() + coord.y); return Vect.make(walkOffset.x() + coord.x, walkOffset.y() + coord.y);

@ -20,56 +20,57 @@ import mightypork.utils.math.constraints.vect.Vect;
/** /**
* Renderer for a walking mob with only one strip (right sided), which is * Renderer for a walking mob with only one strip (right sided), which is
* flipped for walking left. * flipped for walking left.
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class EntityRendererMobLR extends EntityRenderer { public class EntityRendererMobLR extends EntityRenderer {
private final TxSheet sheet; private final TxSheet sheet;
protected final Entity entity; protected final Entity entity;
private final NumVar animRedVar = Num.makeVar(0); private final NumVar animRedVar = Num.makeVar(0);
private final Color hue = Color.rgb(Num.ONE, animRedVar, animRedVar); private final Color hue = Color.rgb(Num.ONE, animRedVar, animRedVar);
public EntityRendererMobLR(Entity entity, String sheetKey) { public EntityRendererMobLR(Entity entity, String sheetKey)
{
this.entity = entity; this.entity = entity;
this.sheet = Res.getTxSheet(sheetKey); this.sheet = Res.getTxSheet(sheetKey);
} }
@Override @Override
public void render(MapRenderContext context) public void render(MapRenderContext context)
{ {
final double hurtTime = entity.health.getTimeSinceLastDamage(); final double hurtTime = entity.health.getTimeSinceLastDamage();
TxQuad q = sheet.getQuad(Calc.frag(entity.pos.getProgress())); TxQuad q = sheet.getQuad(Calc.frag(entity.pos.getProgress()));
if (entity.pos.lastXDir == -1) q = q.flipX(); if (entity.pos.lastXDir == -1) q = q.flipX();
final Rect tileRect = context.getRectForTile(entity.pos.getCoord()); final Rect tileRect = context.getRectForTile(entity.pos.getCoord());
final double w = tileRect.width().value(); final double w = tileRect.width().value();
final Vect visualPos = entity.pos.getVisualPos(); final Vect visualPos = entity.pos.getVisualPos();
final double hurtOffset = (1 - Calc.clamp(hurtTime / 0.1, 0, 1)) * (entity.isDead() ? 0.3 : 0.05); final double hurtOffset = (1 - Calc.clamp(hurtTime / 0.1, 0, 1)) * (entity.isDead() ? 0.3 : 0.05);
Rect spriteRect = Rect.make(visualPos.x() * w, (visualPos.y() - hurtOffset) * w, w, w); Rect spriteRect = Rect.make(visualPos.x() * w, (visualPos.y() - hurtOffset) * w, w, w);
spriteRect = spriteRect.shrink(w * 0.05); spriteRect = spriteRect.shrink(w * 0.05);
animRedVar.setTo(hurtTime / 0.3); animRedVar.setTo(hurtTime / 0.3);
App.gfx().pushGeometry(); App.gfx().pushGeometry();
App.gfx().translate(spriteRect.center()); App.gfx().translate(spriteRect.center());
if (entity.isDead()) { if (entity.isDead()) {
App.gfx().rotateZ(Calc.clamp(hurtTime / 0.3, 0, 1) * 90); App.gfx().rotateZ(Calc.clamp(hurtTime / 0.3, 0, 1) * 90);
} }
final double hw = spriteRect.width().half().value(); final double hw = spriteRect.width().half().value();
App.gfx().quad(Vect.ZERO.expand(hw, hw, hw, hw), q, hue.withAlpha(entity.isDead() ? 1 - Easing.CIRC_IN.get(hurtTime / entity.getDespawnDelay()) : 1)); App.gfx().quad(Vect.ZERO.expand(hw, hw, hw, hw), q, hue.withAlpha(entity.isDead() ? 1 - Easing.CIRC_IN.get(hurtTime / entity.getDespawnDelay()) : 1));
App.gfx().popGeometry(); App.gfx().popGeometry();
} }

@ -5,7 +5,7 @@ import mightypork.utils.eventbus.BusEvent;
public class GameWinEvent extends BusEvent<GameWinHandler> { public class GameWinEvent extends BusEvent<GameWinHandler> {
@Override @Override
protected void handleBy(GameWinHandler handler) protected void handleBy(GameWinHandler handler)
{ {

@ -2,7 +2,7 @@ package mightypork.rogue.world.events;
public interface GameWinHandler { public interface GameWinHandler {
void onGameWon(); void onGameWon();
} }

@ -2,6 +2,6 @@ package mightypork.rogue.world.events;
public interface PlayerDeathHandler { public interface PlayerDeathHandler {
void onPlayerKilled(); void onPlayerKilled();
} }

@ -6,13 +6,13 @@ import mightypork.utils.eventbus.BusEvent;
public class PlayerKilledEvent extends BusEvent<PlayerDeathHandler> { public class PlayerKilledEvent extends BusEvent<PlayerDeathHandler> {
@Override @Override
protected void handleBy(PlayerDeathHandler handler) protected void handleBy(PlayerDeathHandler handler)
{ {
// not dead, discard event. // not dead, discard event.
if (!WorldProvider.get().getPlayer().isDead()) return; if (!WorldProvider.get().getPlayer().isDead()) return;
handler.onPlayerKilled(); handler.onPlayerKilled();
} }
} }

@ -8,20 +8,21 @@ import mightypork.utils.eventbus.events.flags.NotLoggedEvent;
@NotLoggedEvent @NotLoggedEvent
public class PlayerStepEndEvent extends BusEvent<PlayerStepEndListener> { public class PlayerStepEndEvent extends BusEvent<PlayerStepEndListener> {
private final EntityPlayer player; private final EntityPlayer player;
public PlayerStepEndEvent(EntityPlayer player) { public PlayerStepEndEvent(EntityPlayer player)
{
super(); super();
this.player = player; this.player = player;
} }
@Override @Override
protected void handleBy(PlayerStepEndListener handler) protected void handleBy(PlayerStepEndListener handler)
{ {
handler.onStepFinished(player); handler.onStepFinished(player);
} }
} }

@ -5,6 +5,6 @@ import mightypork.rogue.world.entity.impl.EntityPlayer;
public interface PlayerStepEndListener { public interface PlayerStepEndListener {
void onStepFinished(EntityPlayer player); void onStepFinished(EntityPlayer player);
} }

@ -6,15 +6,15 @@ import mightypork.utils.eventbus.BusEvent;
/** /**
* Player wants to go up * Player wants to go up
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class WorldAscendRequest extends BusEvent<WorldAscendRequestListener> { public class WorldAscendRequest extends BusEvent<WorldAscendRequestListener> {
@Override @Override
protected void handleBy(WorldAscendRequestListener handler) protected void handleBy(WorldAscendRequestListener handler)
{ {
handler.onAscendRequest(); handler.onAscendRequest();
} }
} }

@ -2,7 +2,7 @@ package mightypork.rogue.world.events;
public interface WorldAscendRequestListener { public interface WorldAscendRequestListener {
/** /**
* Player clicked up-stairs * Player clicked up-stairs
*/ */

@ -5,11 +5,11 @@ import mightypork.utils.eventbus.BusEvent;
public class WorldDescendRequest extends BusEvent<WorldDescendRequestListener> { public class WorldDescendRequest extends BusEvent<WorldDescendRequestListener> {
@Override @Override
protected void handleBy(WorldDescendRequestListener handler) protected void handleBy(WorldDescendRequestListener handler)
{ {
handler.onDescendRequest(); handler.onDescendRequest();
} }
} }

@ -2,7 +2,7 @@ package mightypork.rogue.world.events;
public interface WorldDescendRequestListener { public interface WorldDescendRequestListener {
/** /**
* Player clicked down-stairs * Player clicked down-stairs
*/ */

@ -7,25 +7,26 @@ import mightypork.utils.eventbus.BusEvent;
/** /**
* Toggle world pause state * Toggle world pause state
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class WorldPauseRequest extends BusEvent<World> { public class WorldPauseRequest extends BusEvent<World> {
public static enum PauseAction public static enum PauseAction
{ {
PAUSE, RESUME, TOGGLE; PAUSE, RESUME, TOGGLE;
} }
private final PauseAction op; private final PauseAction op;
public WorldPauseRequest(PauseAction op) { public WorldPauseRequest(PauseAction op)
{
super(); super();
this.op = op; this.op = op;
} }
@Override @Override
protected void handleBy(World handler) protected void handleBy(World handler)
{ {
@ -33,14 +34,14 @@ public class WorldPauseRequest extends BusEvent<World> {
handler.pause(); handler.pause();
return; return;
} }
if (op == PauseAction.RESUME) { if (op == PauseAction.RESUME) {
handler.resume(); handler.resume();
return; return;
} }
// else // else
// toggle paused state // toggle paused state
if (!handler.isPaused()) { if (!handler.isPaused()) {
handler.pause(); handler.pause();
@ -48,5 +49,5 @@ public class WorldPauseRequest extends BusEvent<World> {
handler.resume(); handler.resume();
} }
} }
} }

@ -16,78 +16,82 @@ import mightypork.utils.math.algo.Coord;
public class LevelBuilder { public class LevelBuilder {
public static enum BuildOrder public static enum BuildOrder
{ {
FIRST, MIDDLE, LAST FIRST, MIDDLE, LAST
} }
private class RoomEntry { private class RoomEntry {
int count; int count;
RoomBuilder room; RoomBuilder room;
boolean important; boolean important;
public RoomEntry(RoomBuilder room, int count, boolean important) { public RoomEntry(RoomBuilder room, int count, boolean important)
{
this.count = count; this.count = count;
this.room = room; this.room = room;
this.important = important; this.important = important;
} }
} }
private class ItemEntry { private class ItemEntry {
Item item; Item item;
boolean important; boolean important;
public ItemEntry(Item item, boolean important) { public ItemEntry(Item item, boolean important)
{
this.item = item; this.item = item;
this.important = important; this.important = important;
} }
} }
private class EntityEntry { private class EntityEntry {
Entity entity; Entity entity;
boolean important; boolean important;
public EntityEntry(Entity item, boolean important) { public EntityEntry(Entity item, boolean important)
{
this.entity = item; this.entity = item;
this.important = important; this.important = important;
} }
} }
private final ScratchMap map; private final ScratchMap map;
private final Random rand; private final Random rand;
private boolean built; private boolean built;
private final LinkedList<RoomEntry> roomsFirst = new LinkedList<>(); private final LinkedList<RoomEntry> roomsFirst = new LinkedList<>();
private final LinkedList<RoomEntry> roomsMiddle = new LinkedList<>(); private final LinkedList<RoomEntry> roomsMiddle = new LinkedList<>();
private final LinkedList<RoomEntry> roomsLast = new LinkedList<>(); private final LinkedList<RoomEntry> roomsLast = new LinkedList<>();
private final LinkedList<ItemEntry> items = new LinkedList<>(); private final LinkedList<ItemEntry> items = new LinkedList<>();
private final LinkedList<EntityEntry> entities = new LinkedList<>(); private final LinkedList<EntityEntry> entities = new LinkedList<>();
/** /**
* make a new level builder instance. * make a new level builder instance.
* *
* @param max_size max map size (square side - tiles) * @param max_size max map size (square side - tiles)
* @param theme tiles theme * @param theme tiles theme
* @param seed level seed * @param seed level seed
*/ */
public LevelBuilder(int max_size, MapTheme theme, long seed) { public LevelBuilder(int max_size, MapTheme theme, long seed)
{
this.rand = new Random(seed); this.rand = new Random(seed);
this.map = new ScratchMap(max_size, theme, rand); this.map = new ScratchMap(max_size, theme, rand);
} }
/** /**
* Add a single room to the room buffer. * Add a single room to the room buffer.
* *
* @param room room builder * @param room room builder
* @param order build order * @param order build order
* @param important try harder and throw error on fail * @param important try harder and throw error on fail
@ -96,11 +100,11 @@ public class LevelBuilder {
{ {
addRoom(room, Range.make(1, 1), order, important); addRoom(room, Range.make(1, 1), order, important);
} }
/** /**
* Add multiple rooms of the type to the room buffer. * Add multiple rooms of the type to the room buffer.
* *
* @param room room builder * @param room room builder
* @param count number of rooms to build * @param count number of rooms to build
* @param order build order * @param order build order
@ -109,94 +113,94 @@ public class LevelBuilder {
public void addRoom(RoomBuilder room, Range count, BuildOrder order, boolean important) public void addRoom(RoomBuilder room, Range count, BuildOrder order, boolean important)
{ {
final List<RoomEntry> list; final List<RoomEntry> list;
switch (order) { switch (order) {
case FIRST: case FIRST:
list = roomsFirst; list = roomsFirst;
break; break;
default: default:
case MIDDLE: case MIDDLE:
list = roomsMiddle; list = roomsMiddle;
break; break;
case LAST: case LAST:
list = roomsLast; list = roomsLast;
break; break;
} }
list.add(new RoomEntry(room, count.randInt(rand), important)); list.add(new RoomEntry(room, count.randInt(rand), important));
} }
private void buildRooms(LinkedList<RoomEntry> list) private void buildRooms(LinkedList<RoomEntry> list)
{ {
while (!list.isEmpty()) { while (!list.isEmpty()) {
Collections.shuffle(list, rand); Collections.shuffle(list, rand);
for (final Iterator<RoomEntry> iter = list.iterator(); iter.hasNext();) { for (final Iterator<RoomEntry> iter = list.iterator(); iter.hasNext();) {
final RoomEntry rge = iter.next(); final RoomEntry rge = iter.next();
map.addRoom(rge.room, rge.important); map.addRoom(rge.room, rge.important);
if ((--rge.count) <= 0) { if ((--rge.count) <= 0) {
iter.remove(); iter.remove();
} }
} }
} }
} }
private void buildCorridors() throws WorldGenError private void buildCorridors() throws WorldGenError
{ {
map.buildCorridors(); map.buildCorridors();
} }
private void buildEntities() private void buildEntities()
{ {
for (final EntityEntry entry : entities) { for (final EntityEntry entry : entities) {
final int tries = entry.important ? 200 : 50; final int tries = entry.important ? 200 : 50;
final boolean success = map.addEntityInMap(entry.entity, tries); final boolean success = map.addEntityInMap(entry.entity, tries);
if (entry.important && !success) { if (entry.important && !success) {
throw new WorldGenError("Could not place an important entity: " + entry.entity); throw new WorldGenError("Could not place an important entity: " + entry.entity);
} }
} }
} }
private void buildItems() private void buildItems()
{ {
for (final ItemEntry entry : items) { for (final ItemEntry entry : items) {
final int tries = entry.important ? 200 : 50; final int tries = entry.important ? 200 : 50;
final boolean success = map.addItemInMap(entry.item, tries); final boolean success = map.addItemInMap(entry.item, tries);
if (entry.important && !success) { if (entry.important && !success) {
throw new WorldGenError("Could not place an important item: " + entry.item); throw new WorldGenError("Could not place an important item: " + entry.item);
} }
} }
} }
private void writeToMap() private void writeToMap()
{ {
buildRooms(roomsFirst); buildRooms(roomsFirst);
buildRooms(roomsMiddle); buildRooms(roomsMiddle);
buildRooms(roomsLast); buildRooms(roomsLast);
buildCorridors(); buildCorridors();
map.fixGlitches(); map.fixGlitches();
buildItems(); buildItems();
buildEntities(); buildEntities();
} }
/** /**
* Write to a new level instance. * Write to a new level instance.
* *
* @param world level's world * @param world level's world
* @return the level * @return the level
* @throws WorldGenError on error in generation * @throws WorldGenError on error in generation
@ -207,22 +211,22 @@ public class LevelBuilder {
throw new WorldGenError("Level already built."); throw new WorldGenError("Level already built.");
} }
built = true; built = true;
writeToMap(); writeToMap();
final Coord size = map.getNeededSize(); final Coord size = map.getNeededSize();
final Level lvl = new Level(size.x, size.y); final Level lvl = new Level(size.x, size.y);
lvl.setWorld(world); // important for creating entities lvl.setWorld(world); // important for creating entities
map.writeToLevel(lvl); map.writeToLevel(lvl);
return lvl; return lvl;
} }
/** /**
* Add an item to be added to the level when tiles are built. * Add an item to be added to the level when tiles are built.
* *
* @param item item to add * @param item item to add
* @param important try harder and throw error on fail * @param important try harder and throw error on fail
* @throws WorldGenError on fail * @throws WorldGenError on fail
@ -231,12 +235,12 @@ public class LevelBuilder {
{ {
items.add(new ItemEntry(item, important)); items.add(new ItemEntry(item, important));
} }
/** /**
* Add an entity to be added to the level when tiles are built.<br> * Add an entity to be added to the level when tiles are built.<br>
* It's EID will be assigned during writing to level. * It's EID will be assigned during writing to level.
* *
* @param entity entity to add * @param entity entity to add
* @param important try harder and throw error on fail * @param important try harder and throw error on fail
* @throws WorldGenError on fail * @throws WorldGenError on fail
@ -245,5 +249,5 @@ public class LevelBuilder {
{ {
entities.add(new EntityEntry(entity, important)); entities.add(new EntityEntry(entity, important));
} }
} }

@ -6,31 +6,31 @@ import mightypork.rogue.world.tile.TileModel;
/** /**
* Map theme to use for building * Map theme to use for building
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public interface MapTheme { public interface MapTheme {
TileModel wall(); TileModel wall();
TileModel floor(); TileModel floor();
TileModel door(); TileModel door();
TileModel passage(); TileModel passage();
TileModel secretDoor(); TileModel secretDoor();
TileModel entrance(); TileModel entrance();
TileModel exit(); TileModel exit();
TileModel chest(); TileModel chest();
} }

@ -8,10 +8,10 @@ import mightypork.utils.math.algo.Coord;
/** /**
* Room model * Room model
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public interface RoomBuilder { public interface RoomBuilder {
RoomEntry buildRoom(ScratchMap map, MapTheme theme, Random rand, Coord center) throws WorldGenError; RoomEntry buildRoom(ScratchMap map, MapTheme theme, Random rand, Coord center) throws WorldGenError;
} }

@ -6,47 +6,48 @@ import mightypork.utils.math.algo.Coord;
/** /**
* Room description entry for {@link ScratchMap} * Room description entry for {@link ScratchMap}
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class RoomEntry { public class RoomEntry {
final Coord min; final Coord min;
final Coord max; final Coord max;
public RoomEntry(Coord min, Coord max) { public RoomEntry(Coord min, Coord max)
{
super(); super();
this.min = min; this.min = min;
this.max = max; this.max = max;
} }
public boolean intersectsWith(Coord amin, Coord amax) public boolean intersectsWith(Coord amin, Coord amax)
{ {
int tw = max.x - min.x; int tw = max.x - min.x;
int th = max.y - min.y; int th = max.y - min.y;
int rw = amax.x - amin.x; int rw = amax.x - amin.x;
int rh = amax.y - amin.y; int rh = amax.y - amin.y;
if (rw <= 0 || rh <= 0 || tw <= 0 || th <= 0) { if (rw <= 0 || rh <= 0 || tw <= 0 || th <= 0) {
return false; return false;
} }
final int tx = min.x; final int tx = min.x;
final int ty = min.y; final int ty = min.y;
final int rx = amin.x; final int rx = amin.x;
final int ry = amin.y; final int ry = amin.y;
rw += rx; rw += rx;
rh += ry; rh += ry;
tw += tx; tw += tx;
th += ty; th += ty;
return ((rw <= rx || rw >= tx) && (rh <= ry || rh >= ty) && (tw <= tx || tw >= rx) && (th <= ty || th >= ry)); return ((rw <= rx || rw >= tx) && (rh <= ry || rh >= ty) && (tw <= tx || tw >= rx) && (th <= ty || th >= ry));
} }
@Override @Override
public String toString() public String toString()
{ {

@ -1,26 +1,33 @@
package mightypork.rogue.world.gen; package mightypork.rogue.world.gen;
import mightypork.rogue.world.gen.rooms.*; import mightypork.rogue.world.gen.rooms.BasicRoom;
import mightypork.rogue.world.gen.rooms.BossRoom;
import mightypork.rogue.world.gen.rooms.DeadEndRoom;
import mightypork.rogue.world.gen.rooms.EntranceRoom;
import mightypork.rogue.world.gen.rooms.ExitRoom;
import mightypork.rogue.world.gen.rooms.ItemShrineRoom;
import mightypork.rogue.world.gen.rooms.StorageRoom;
import mightypork.rogue.world.gen.rooms.TreasureChestRoom;
import mightypork.rogue.world.item.Item; import mightypork.rogue.world.item.Item;
public class Rooms { public class Rooms {
public static final RoomBuilder BASIC = new BasicRoom(); public static final RoomBuilder BASIC = new BasicRoom();
public static final RoomBuilder STORAGE = new StorageRoom(); public static final RoomBuilder STORAGE = new StorageRoom();
public static final RoomBuilder DEAD_END = new DeadEndRoom(); public static final RoomBuilder DEAD_END = new DeadEndRoom();
public static final RoomBuilder ENTRANCE = new EntranceRoom(); public static final RoomBuilder ENTRANCE = new EntranceRoom();
public static final RoomBuilder EXIT = new ExitRoom(); public static final RoomBuilder EXIT = new ExitRoom();
public static final RoomBuilder BOSS = new BossRoom(); public static final RoomBuilder BOSS = new BossRoom();
public static RoomBuilder treasure(Item item) public static RoomBuilder treasure(Item item)
{ {
return new TreasureChestRoom(item); return new TreasureChestRoom(item);
} }
public static RoomBuilder shrine(Item item) public static RoomBuilder shrine(Item item)
{ {
return new ItemShrineRoom(item); return new ItemShrineRoom(item);

@ -25,25 +25,25 @@ import mightypork.utils.math.algo.pathfinding.PathFinder;
/** /**
* Temporary tile map used for level generation. * Temporary tile map used for level generation.
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class ScratchMap { public class ScratchMap {
private Tile[][] map; private Tile[][] map;
private final int width; private final int width;
private final int height; private final int height;
private final List<RoomEntry> rooms = new ArrayList<>(); private final List<RoomEntry> rooms = new ArrayList<>();
/** Coords to connect with corridors */ /** Coords to connect with corridors */
private final List<Coord> nodes = new ArrayList<>(); private final List<Coord> nodes = new ArrayList<>();
private final List<Coord> occupied = new ArrayList<>(); private final List<Coord> occupied = new ArrayList<>();
private final List<Entity> entities = new ArrayList<>(); private final List<Entity> entities = new ArrayList<>();
private final PathFinder pathf = new PathFinder() { private final PathFinder pathf = new PathFinder() {
@Override @Override
public boolean isAccessible(Coord pos) public boolean isAccessible(Coord pos)
{ {
@ -52,108 +52,109 @@ public class ScratchMap {
if (t.isStairs()) return false; if (t.isStairs()) return false;
return t.isPotentiallyWalkable() || (t.genData.protection != TileProtectLevel.STRONG); return t.isPotentiallyWalkable() || (t.genData.protection != TileProtectLevel.STRONG);
} }
@Override @Override
public int getCost(Coord last, Coord pos) public int getCost(Coord last, Coord pos)
{ {
final Tile t = getTile(pos); final Tile t = getTile(pos);
switch (t.getType()) { switch (t.getType()) {
case NULL: case NULL:
return 60; return 60;
case DOOR: case DOOR:
case PASSAGE: case PASSAGE:
return 10; return 10;
case STAIRS: case STAIRS:
case FLOOR: case FLOOR:
return 20; return 20;
case WALL: case WALL:
if (t.genData.protection != TileProtectLevel.NONE) return 2000; if (t.genData.protection != TileProtectLevel.NONE) return 2000;
return 100; return 100;
default: default:
throw new WorldGenError("Unknown tile type: " + t.getType()); throw new WorldGenError("Unknown tile type: " + t.getType());
} }
} }
@Override @Override
public int getMinCost() public int getMinCost()
{ {
return 10; return 10;
} }
@Override @Override
public Heuristic getHeuristic() public Heuristic getHeuristic()
{ {
return PathFinder.CORNER_HEURISTIC; return PathFinder.CORNER_HEURISTIC;
} }
@Override @Override
public List<Move> getWalkSides() public List<Move> getWalkSides()
{ {
return Moves.CARDINAL_SIDES; return Moves.CARDINAL_SIDES;
} }
}; };
{ {
// needed for when the path starts / ends at stairs. // needed for when the path starts / ends at stairs.
pathf.setIgnoreEnd(true); pathf.setIgnoreEnd(true);
pathf.setIgnoreStart(true); pathf.setIgnoreStart(true);
} }
Coord genMin; Coord genMin;
Coord genMax; Coord genMax;
private final MapTheme theme; private final MapTheme theme;
private final Random rand; private final Random rand;
private final Coord enterPoint = new Coord(); private final Coord enterPoint = new Coord();
private final Coord exitPoint = new Coord(); private final Coord exitPoint = new Coord();
private static final boolean FIX_GLITCHES = true; private static final boolean FIX_GLITCHES = true;
public ScratchMap(int max_size, MapTheme theme, Random rand) { public ScratchMap(int max_size, MapTheme theme, Random rand)
{
map = new Tile[max_size][max_size]; map = new Tile[max_size][max_size];
genMin = Coord.make((max_size / 2) - 1, (max_size / 2) - 1); genMin = Coord.make((max_size / 2) - 1, (max_size / 2) - 1);
genMax = genMin.add(1, 1); genMax = genMin.add(1, 1);
width = max_size; width = max_size;
height = max_size; height = max_size;
this.rand = rand; this.rand = rand;
this.theme = theme; this.theme = theme;
fill(Coord.make(0, 0), Coord.make(width - 1, height - 1), Tiles.NULL); fill(Coord.make(0, 0), Coord.make(width - 1, height - 1), Tiles.NULL);
} }
public void addRoom(RoomBuilder rb, boolean critical) throws WorldGenError public void addRoom(RoomBuilder rb, boolean critical) throws WorldGenError
{ {
try { try {
if (rooms.size() > 0) minimizeBounds(); if (rooms.size() > 0) minimizeBounds();
final Coord roomPos = Coord.make(0, 0); final Coord roomPos = Coord.make(0, 0);
int failed = 0; int failed = 0;
int failed_total = 0; int failed_total = 0;
while (true) { while (true) {
final int sizeX = genMax.x - genMin.x; final int sizeX = genMax.x - genMin.x;
final int sizeY = genMax.y - genMin.y; final int sizeY = genMax.y - genMin.y;
roomPos.x = genMin.x + rand.nextInt(sizeX + 1); roomPos.x = genMin.x + rand.nextInt(sizeX + 1);
roomPos.y = genMin.y + rand.nextInt(sizeY + 1); roomPos.y = genMin.y + rand.nextInt(sizeY + 1);
switch (rand.nextInt(4)) { switch (rand.nextInt(4)) {
case 0: case 0:
roomPos.x += (failed_total / 35); roomPos.x += (failed_total / 35);
@ -167,47 +168,47 @@ public class ScratchMap {
case 3: case 3:
roomPos.y -= (failed_total / 35); roomPos.y -= (failed_total / 35);
} }
final RoomEntry rd = rb.buildRoom(this, theme, rand, roomPos); final RoomEntry rd = rb.buildRoom(this, theme, rand, roomPos);
if (rd != null) { if (rd != null) {
rooms.add(rd); rooms.add(rd);
genMin.x = Math.min(genMin.x, rd.min.x); genMin.x = Math.min(genMin.x, rd.min.x);
genMin.y = Math.min(genMin.y, rd.min.y); genMin.y = Math.min(genMin.y, rd.min.y);
genMax.x = Math.max(genMax.x, rd.max.x); genMax.x = Math.max(genMax.x, rd.max.x);
genMax.y = Math.max(genMax.y, rd.max.y); genMax.y = Math.max(genMax.y, rd.max.y);
clampBounds(); clampBounds();
nodes.add(roomPos); nodes.add(roomPos);
return; return;
} else { } else {
failed++; failed++;
failed_total++; failed_total++;
if (failed_total > 1000) { if (failed_total > 1000) {
throw new WorldGenError("Failed to add a room."); throw new WorldGenError("Failed to add a room.");
} }
if (failed > 300) { if (failed > 300) {
Log.w("Faild to build room."); Log.w("Faild to build room.");
if (critical) { if (critical) {
// expand gen bounds // expand gen bounds
genMin.x -= 5; genMin.x -= 5;
genMin.y -= 5; genMin.y -= 5;
genMax.x += 5; genMax.x += 5;
genMax.y += 5; genMax.y += 5;
clampBounds(); clampBounds();
failed = 0; failed = 0;
Log.f3("Trying again."); Log.f3("Trying again.");
continue; continue;
} else { } else {
throw new WorldGenError("Failed to add a room."); throw new WorldGenError("Failed to add a room.");
} }
@ -224,8 +225,8 @@ public class ScratchMap {
} }
} }
} }
/** /**
* Clamp bounds to available area * Clamp bounds to available area
*/ */
@ -236,8 +237,8 @@ public class ScratchMap {
genMax.x = Calc.clamp(genMax.x, 0, width - 1); genMax.x = Calc.clamp(genMax.x, 0, width - 1);
genMax.y = Calc.clamp(genMax.y, 0, height - 1); genMax.y = Calc.clamp(genMax.y, 0, height - 1);
} }
/** /**
* Minimize gen bounds based on defined room bounds * Minimize gen bounds based on defined room bounds
*/ */
@ -245,124 +246,124 @@ public class ScratchMap {
{ {
final Coord low = Coord.make(width, height); final Coord low = Coord.make(width, height);
final Coord high = Coord.make(0, 0); final Coord high = Coord.make(0, 0);
for (final RoomEntry rd : rooms) { for (final RoomEntry rd : rooms) {
low.x = Math.min(low.x, rd.min.x); low.x = Math.min(low.x, rd.min.x);
low.y = Math.min(low.y, rd.min.y); low.y = Math.min(low.y, rd.min.y);
high.x = Math.max(high.x, rd.max.x); high.x = Math.max(high.x, rd.max.x);
high.y = Math.max(high.y, rd.max.y); high.y = Math.max(high.y, rd.max.y);
} }
genMin.setTo(low); genMin.setTo(low);
genMax.setTo(high); genMax.setTo(high);
} }
public boolean isIn(Coord pos) public boolean isIn(Coord pos)
{ {
return pos.x >= 0 && pos.x < width && pos.y >= 0 && pos.y < height; return pos.x >= 0 && pos.x < width && pos.y >= 0 && pos.y < height;
} }
public Tile getTile(Coord pos) public Tile getTile(Coord pos)
{ {
if (!isIn(pos)) { if (!isIn(pos)) {
throw new WorldGenError("Tile not in map: " + pos); throw new WorldGenError("Tile not in map: " + pos);
} }
return map[pos.y][pos.x]; return map[pos.y][pos.x];
} }
public boolean set(Coord pos, TileModel tm) public boolean set(Coord pos, TileModel tm)
{ {
return set(pos, tm.createTile()); return set(pos, tm.createTile());
} }
public boolean set(Coord pos, Tile tile) public boolean set(Coord pos, Tile tile)
{ {
if (!isIn(pos)) { if (!isIn(pos)) {
throw new WorldGenError("Tile not in map: " + pos); throw new WorldGenError("Tile not in map: " + pos);
} }
map[pos.y][pos.x] = tile; map[pos.y][pos.x] = tile;
return true; return true;
} }
public boolean isClear(Coord min, Coord max) public boolean isClear(Coord min, Coord max)
{ {
if (!isIn(min) || !isIn(max)) return false; if (!isIn(min) || !isIn(max)) return false;
for (final RoomEntry r : rooms) { for (final RoomEntry r : rooms) {
if (r.intersectsWith(min, max)) return false; if (r.intersectsWith(min, max)) return false;
} }
return true; return true;
} }
public void protect(Coord pos, TileProtectLevel prot) public void protect(Coord pos, TileProtectLevel prot)
{ {
protect(pos, pos, prot); protect(pos, pos, prot);
} }
public void protect(Coord min, Coord max, TileProtectLevel prot) public void protect(Coord min, Coord max, TileProtectLevel prot)
{ {
if (!isIn(min) || !isIn(max)) throw new WorldGenError("Tile(s) not in map: " + min + " , " + max); if (!isIn(min) || !isIn(max)) throw new WorldGenError("Tile(s) not in map: " + min + " , " + max);
final Coord c = Coord.make(0, 0); final Coord c = Coord.make(0, 0);
for (c.x = min.x; c.x <= max.x; c.x++) for (c.x = min.x; c.x <= max.x; c.x++)
for (c.y = min.y; c.y <= max.y; c.y++) for (c.y = min.y; c.y <= max.y; c.y++)
getTile(c).genData.protection = prot; getTile(c).genData.protection = prot;
} }
public void fill(Coord min, Coord max, TileModel tm) public void fill(Coord min, Coord max, TileModel tm)
{ {
if (!isIn(min) || !isIn(max)) throw new WorldGenError("Tile(s) not in map: " + min + " , " + max); if (!isIn(min) || !isIn(max)) throw new WorldGenError("Tile(s) not in map: " + min + " , " + max);
final Coord c = Coord.make(0, 0); final Coord c = Coord.make(0, 0);
for (c.x = min.x; c.x <= max.x; c.x++) for (c.x = min.x; c.x <= max.x; c.x++)
for (c.y = min.y; c.y <= max.y; c.y++) for (c.y = min.y; c.y <= max.y; c.y++)
set(c, tm.createTile()); set(c, tm.createTile());
} }
public void border(Coord min, Coord max, TileModel tm) public void border(Coord min, Coord max, TileModel tm)
{ {
if (!isIn(min) || !isIn(max)) throw new WorldGenError("Tile(s) not in map: " + min + " , " + max); if (!isIn(min) || !isIn(max)) throw new WorldGenError("Tile(s) not in map: " + min + " , " + max);
final Coord c = Coord.make(0, 0); final Coord c = Coord.make(0, 0);
// top // top
for (c.x = min.x, c.y = min.y; c.x <= max.x; c.x++) for (c.x = min.x, c.y = min.y; c.x <= max.x; c.x++)
set(c, tm.createTile()); set(c, tm.createTile());
//bottom //bottom
for (c.x = min.x, c.y = max.y; c.x <= max.x; c.x++) for (c.x = min.x, c.y = max.y; c.x <= max.x; c.x++)
set(c, tm.createTile()); set(c, tm.createTile());
//left //left
for (c.x = min.x, c.y = min.y + 1; c.y < max.y; c.y++) for (c.x = min.x, c.y = min.y + 1; c.y < max.y; c.y++)
set(c, tm.createTile()); set(c, tm.createTile());
//right //right
for (c.x = max.x, c.y = min.y + 1; c.y < max.y; c.y++) for (c.x = max.x, c.y = min.y + 1; c.y < max.y; c.y++)
set(c, tm.createTile()); set(c, tm.createTile());
} }
public void buildCorridors() public void buildCorridors()
{ {
// Log.f3("Building corridors."); // Log.f3("Building corridors.");
Coord start = nodes.get(0); Coord start = nodes.get(0);
final Set<Coord> starts = new HashSet<>(); final Set<Coord> starts = new HashSet<>();
for (int i = 0; i < 2 + rooms.size() / 5; i++) { for (int i = 0; i < 2 + rooms.size() / 5; i++) {
if (!starts.contains(start)) { if (!starts.contains(start)) {
for (int j = 0; j < nodes.size(); j++) { for (int j = 0; j < nodes.size(); j++) {
@ -373,24 +374,24 @@ public class ScratchMap {
start = Calc.pick(rand, nodes); start = Calc.pick(rand, nodes);
} }
} }
private void buildCorridor(Coord node1, Coord node2) private void buildCorridor(Coord node1, Coord node2)
{ {
// Log.f3("Building corridor " + node1 + " -> " + node2); // Log.f3("Building corridor " + node1 + " -> " + node2);
final List<Coord> steps = pathf.findPath(node1, node2); final List<Coord> steps = pathf.findPath(node1, node2);
if (steps == null) { if (steps == null) {
Log.w("Could not build corridor " + node1 + "->" + node2); Log.w("Could not build corridor " + node1 + "->" + node2);
return; return;
} }
for (final Coord c : steps) { for (final Coord c : steps) {
buildCorridorPiece(c); buildCorridorPiece(c);
} }
} }
private void buildCorridorPiece(Coord pos) private void buildCorridorPiece(Coord pos)
{ {
final Coord c = Coord.make(0, 0); final Coord c = Coord.make(0, 0);
@ -398,17 +399,17 @@ public class ScratchMap {
for (i = -1, c.x = pos.x - 1; c.x <= pos.x + 1; c.x++, i++) { for (i = -1, c.x = pos.x - 1; c.x <= pos.x + 1; c.x++, i++) {
for (j = -1, c.y = pos.y - 1; c.y <= pos.y + 1; c.y++, j++) { for (j = -1, c.y = pos.y - 1; c.y <= pos.y + 1; c.y++, j++) {
if (!isIn(c)) continue; if (!isIn(c)) continue;
genMin.x = Math.min(genMin.x, c.x); genMin.x = Math.min(genMin.x, c.x);
genMin.y = Math.min(genMin.y, c.y); genMin.y = Math.min(genMin.y, c.y);
genMax.x = Math.max(genMax.x, c.x); genMax.x = Math.max(genMax.x, c.x);
genMax.y = Math.max(genMax.y, c.y); genMax.y = Math.max(genMax.y, c.y);
clampBounds(); clampBounds();
final Tile current = getTile(c); final Tile current = getTile(c);
if (!current.isNull() && (current.isPotentiallyWalkable() || current.isStairs())) continue; // floor already, let it be if (!current.isNull() && (current.isPotentiallyWalkable() || current.isStairs())) continue; // floor already, let it be
if (i == 0 && j == 0) { if (i == 0 && j == 0) {
set(c, theme.floor()); set(c, theme.floor());
} else { } else {
@ -418,8 +419,8 @@ public class ScratchMap {
} }
} }
} }
/** /**
* @return dimensions of the area taken by non-null tiles * @return dimensions of the area taken by non-null tiles
*/ */
@ -427,141 +428,141 @@ public class ScratchMap {
{ {
return Coord.make(genMax.x - genMin.x + 1, genMax.y - genMin.y + 1); return Coord.make(genMax.x - genMin.x + 1, genMax.y - genMin.y + 1);
} }
public byte findWalls(Coord pos) public byte findWalls(Coord pos)
{ {
byte walls = 0; byte walls = 0;
for (int i = 0; i < 8; i++) { for (int i = 0; i < 8; i++) {
final Coord cc = pos.add(Moves.getSide(i)); final Coord cc = pos.add(Moves.getSide(i));
if (!isIn(cc)) continue; if (!isIn(cc)) continue;
if (getTile(cc).isWall()) { if (getTile(cc).isWall()) {
walls |= Moves.getBit(i); walls |= Moves.getBit(i);
} }
} }
return walls; return walls;
} }
public byte findFloors(Coord pos) public byte findFloors(Coord pos)
{ {
byte floors = 0; byte floors = 0;
for (int i = 0; i <= 7; i++) { for (int i = 0; i <= 7; i++) {
final Coord cc = pos.add(Moves.getSide(i)); final Coord cc = pos.add(Moves.getSide(i));
if (!isIn(cc)) continue; if (!isIn(cc)) continue;
if (getTile(cc).isFloor()) { if (getTile(cc).isFloor()) {
floors |= Moves.getBit(i); floors |= Moves.getBit(i);
} }
} }
return floors; return floors;
} }
public byte findDoors(Coord pos) public byte findDoors(Coord pos)
{ {
byte doors = 0; byte doors = 0;
for (int i = 0; i <= 7; i++) { for (int i = 0; i <= 7; i++) {
final Coord cc = pos.add(Moves.getSide(i)); final Coord cc = pos.add(Moves.getSide(i));
if (!isIn(cc)) continue; if (!isIn(cc)) continue;
if (getTile(cc).isDoor()) { if (getTile(cc).isDoor()) {
doors |= Moves.getBit(i); doors |= Moves.getBit(i);
} }
} }
return doors; return doors;
} }
public byte findNils(Coord pos) public byte findNils(Coord pos)
{ {
byte nils = 0; byte nils = 0;
for (int i = 0; i <= 7; i++) { for (int i = 0; i <= 7; i++) {
final Coord cc = pos.add(Moves.getSide(i)); final Coord cc = pos.add(Moves.getSide(i));
if (!isIn(cc) || getTile(cc).isNull()) { if (!isIn(cc) || getTile(cc).isNull()) {
nils |= Moves.getBit(i); nils |= Moves.getBit(i);
} }
} }
return nils; return nils;
} }
/** /**
* Fix generator glitches and reduce size to the actual used size * Fix generator glitches and reduce size to the actual used size
*/ */
public void fixGlitches() public void fixGlitches()
{ {
final Tile[][] out = new Tile[height][width]; final Tile[][] out = new Tile[height][width];
// bounds will be adjusted by the actual tiles in the map // bounds will be adjusted by the actual tiles in the map
genMin.x = width; genMin.x = width;
genMin.y = height; genMin.y = height;
genMax.x = 0; genMax.x = 0;
genMax.y = 0; genMax.y = 0;
final Coord c = Coord.make(0, 0); final Coord c = Coord.make(0, 0);
for (c.x = 0; c.x < width; c.x++) { for (c.x = 0; c.x < width; c.x++) {
for (c.y = 0; c.y < height; c.y++) { for (c.y = 0; c.y < height; c.y++) {
final Tile t = getTile(c); final Tile t = getTile(c);
final boolean isNull = t.isNull(); final boolean isNull = t.isNull();
final boolean isDoor = !isNull && t.isDoor(); final boolean isDoor = !isNull && t.isDoor();
final boolean isFloor = !isNull && t.isFloor(); final boolean isFloor = !isNull && t.isFloor();
final boolean isWall = !isNull && t.isWall(); final boolean isWall = !isNull && t.isWall();
// bitmasks // bitmasks
final byte walls = findWalls(c); final byte walls = findWalls(c);
final byte nils = findNils(c); final byte nils = findNils(c);
final byte floors = findFloors(c); final byte floors = findFloors(c);
boolean toWall = false; boolean toWall = false;
boolean toFloor = false; boolean toFloor = false;
boolean toNull = false; boolean toNull = false;
do { do {
if (isWall && floors == 0) { if (isWall && floors == 0) {
toNull = true; toNull = true;
break; break;
} }
if (isFloor && (nils & Moves.BITS_CARDINAL) != 0) { if (isFloor && (nils & Moves.BITS_CARDINAL) != 0) {
toWall = true; // floor with adjacent cardinal null toWall = true; // floor with adjacent cardinal null
break; break;
} }
if (isNull && (floors & Moves.BITS_DIAGONAL) != 0) { if (isNull && (floors & Moves.BITS_DIAGONAL) != 0) {
toWall = true; // null with adjacent diagonal floor toWall = true; // null with adjacent diagonal floor
break; break;
} }
if (isDoor) { if (isDoor) {
if (Calc.countBits((byte) (floors & Moves.BITS_CARDINAL)) < 2) { if (Calc.countBits((byte) (floors & Moves.BITS_CARDINAL)) < 2) {
toWall = true; toWall = true;
break; break;
} }
if (Calc.countBits((byte) (walls & Moves.BITS_CARDINAL)) > 2) { if (Calc.countBits((byte) (walls & Moves.BITS_CARDINAL)) > 2) {
toWall = true; toWall = true;
break; break;
} }
if (Calc.countBits((byte) (floors & Moves.BITS_CARDINAL)) > 2) { if (Calc.countBits((byte) (floors & Moves.BITS_CARDINAL)) > 2) {
toFloor = true; toFloor = true;
break; break;
} }
if ((floors & Moves.BITS_NW_CORNER) == Moves.BITS_NW_CORNER) toWall = true; if ((floors & Moves.BITS_NW_CORNER) == Moves.BITS_NW_CORNER) toWall = true;
if ((floors & Moves.BITS_NE_CORNER) == Moves.BITS_NE_CORNER) toWall = true; if ((floors & Moves.BITS_NE_CORNER) == Moves.BITS_NE_CORNER) toWall = true;
if ((floors & Moves.BITS_SW_CORNER) == Moves.BITS_SW_CORNER) toWall = true; if ((floors & Moves.BITS_SW_CORNER) == Moves.BITS_SW_CORNER) toWall = true;
if ((floors & Moves.BITS_SE_CORNER) == Moves.BITS_SE_CORNER) toWall = true; if ((floors & Moves.BITS_SE_CORNER) == Moves.BITS_SE_CORNER) toWall = true;
} }
} while (false); } while (false);
if (toNull) { if (toNull) {
out[c.y][c.x] = Tiles.NULL.createTile(); out[c.y][c.x] = Tiles.NULL.createTile();
} else if (toWall) { } else if (toWall) {
@ -571,24 +572,24 @@ public class ScratchMap {
} else { } else {
out[c.y][c.x] = map[c.y][c.x]; out[c.y][c.x] = map[c.y][c.x];
} }
if (!out[c.y][c.x].isNull()) { if (!out[c.y][c.x].isNull()) {
genMin.x = Math.min(genMin.x, c.x); genMin.x = Math.min(genMin.x, c.x);
genMin.y = Math.min(genMin.y, c.y); genMin.y = Math.min(genMin.y, c.y);
genMax.x = Math.max(genMax.x, c.x); genMax.x = Math.max(genMax.x, c.x);
genMax.y = Math.max(genMax.y, c.y); genMax.y = Math.max(genMax.y, c.y);
} }
} }
} }
map = out; map = out;
} }
/** /**
* Write tiles and entities into a level * Write tiles and entities into a level
* *
* @param level the level * @param level the level
*/ */
public void writeToLevel(Level level) public void writeToLevel(Level level)
@ -596,23 +597,23 @@ public class ScratchMap {
if (level.getWorld() == null) { if (level.getWorld() == null) {
throw new WorldGenError("Level has no world assigned."); // need for entities throw new WorldGenError("Level has no world assigned."); // need for entities
} }
// make sure no walkable are at edges. // make sure no walkable are at edges.
final Coord c = Coord.make(0, 0); final Coord c = Coord.make(0, 0);
final Coord c1 = Coord.make(0, 0); final Coord c1 = Coord.make(0, 0);
for (c.x = genMin.x, c1.x = 0; c.x <= genMax.x; c.x++, c1.x++) { for (c.x = genMin.x, c1.x = 0; c.x <= genMax.x; c.x++, c1.x++) {
for (c.y = genMin.y, c1.y = 0; c.y <= genMax.y; c.y++, c1.y++) { for (c.y = genMin.y, c1.y = 0; c.y <= genMax.y; c.y++, c1.y++) {
level.setTile(c1, getTile(c)); level.setTile(c1, getTile(c));
} }
} }
final Coord entrance = new Coord(enterPoint.x - genMin.x, enterPoint.y - genMin.y); final Coord entrance = new Coord(enterPoint.x - genMin.x, enterPoint.y - genMin.y);
level.setEnterPoint(entrance); level.setEnterPoint(entrance);
final Coord exit = new Coord(exitPoint.x - genMin.x, exitPoint.y - genMin.y); final Coord exit = new Coord(exitPoint.x - genMin.x, exitPoint.y - genMin.y);
level.setExitPoint(exit); level.setExitPoint(exit);
for (final Entity e : entities) { for (final Entity e : entities) {
final Coord pos = e.getCoord().add(-genMin.x, -genMin.y); final Coord pos = e.getCoord().add(-genMin.x, -genMin.y);
if (!level.addEntityNear(e, pos)) { if (!level.addEntityNear(e, pos)) {
@ -628,101 +629,101 @@ public class ScratchMap {
} }
} }
} }
public void setEntrance(Coord pos) public void setEntrance(Coord pos)
{ {
enterPoint.setTo(pos); enterPoint.setTo(pos);
} }
public void setExit(Coord pos) public void setExit(Coord pos)
{ {
exitPoint.setTo(pos); exitPoint.setTo(pos);
} }
public boolean addItem(Item item, Coord pos) public boolean addItem(Item item, Coord pos)
{ {
return addItem(item, pos, true); return addItem(item, pos, true);
} }
public boolean addItem(Item item, Coord pos, boolean canStack) public boolean addItem(Item item, Coord pos, boolean canStack)
{ {
if (!isIn(pos)) return false; if (!isIn(pos)) return false;
final Tile t = getTile(pos); final Tile t = getTile(pos);
if (!canStack && t.hasItem()) return false; if (!canStack && t.hasItem()) return false;
if (t.dropItem(item)) return true; if (t.dropItem(item)) return true;
return false; return false;
} }
public boolean addItemInArea(Item item, Coord min, Coord max, int tries) public boolean addItemInArea(Item item, Coord min, Coord max, int tries)
{ {
final Coord pos = Coord.zero(); final Coord pos = Coord.zero();
for (int i = 0; i < tries / 2; i++) { for (int i = 0; i < tries / 2; i++) {
pos.x = Calc.randInt(rand, min.x, max.x); pos.x = Calc.randInt(rand, min.x, max.x);
pos.y = Calc.randInt(rand, min.y, max.y); pos.y = Calc.randInt(rand, min.y, max.y);
if (addItem(item, pos, false)) return true; if (addItem(item, pos, false)) return true;
} }
for (int i = 0; i < tries - (tries / 2); i++) { for (int i = 0; i < tries - (tries / 2); i++) {
pos.x = Calc.randInt(rand, min.x, max.x); pos.x = Calc.randInt(rand, min.x, max.x);
pos.y = Calc.randInt(rand, min.y, max.y); pos.y = Calc.randInt(rand, min.y, max.y);
if (addItem(item, pos, true)) return true; if (addItem(item, pos, true)) return true;
} }
return false; return false;
} }
public boolean addItemInMap(Item item, int tries) public boolean addItemInMap(Item item, int tries)
{ {
return addItemInArea(item, genMin, genMax, tries); return addItemInArea(item, genMin, genMax, tries);
} }
public boolean addEntityInArea(Entity entity, Coord min, Coord max, int tries) public boolean addEntityInArea(Entity entity, Coord min, Coord max, int tries)
{ {
final Coord pos = Coord.zero(); final Coord pos = Coord.zero();
for (int i = 0; i < tries; i++) { for (int i = 0; i < tries; i++) {
pos.x = Calc.randInt(rand, min.x, max.x); pos.x = Calc.randInt(rand, min.x, max.x);
pos.y = Calc.randInt(rand, min.y, max.y); pos.y = Calc.randInt(rand, min.y, max.y);
if (!isIn(pos)) continue; if (!isIn(pos)) continue;
if (addEntity(entity, pos)) return true; if (addEntity(entity, pos)) return true;
} }
return false; return false;
} }
public boolean addEntityInMap(Entity entity, int tries) public boolean addEntityInMap(Entity entity, int tries)
{ {
return addEntityInArea(entity, genMin, genMax, tries); return addEntityInArea(entity, genMin, genMax, tries);
} }
public boolean addEntity(Entity entity, Coord pos) public boolean addEntity(Entity entity, Coord pos)
{ {
if (!isIn(pos)) return false; if (!isIn(pos)) return false;
if (pos.dist(enterPoint) < 4) return false; // protected distance. if (pos.dist(enterPoint) < 4) return false; // protected distance.
final Tile t = getTile(pos); final Tile t = getTile(pos);
if (!t.isWalkable()) return false; if (!t.isWalkable()) return false;
if (occupied.contains(pos)) return false; if (occupied.contains(pos)) return false;
occupied.add(pos.copy()); occupied.add(pos.copy());
entity.setCoord(pos); entity.setCoord(pos);
entities.add(entity); entities.add(entity);
return true; return true;
} }
} }

@ -19,103 +19,103 @@ import mightypork.utils.math.Range;
public class WorldCreator { public class WorldCreator {
public static Random rand = new Random(); public static Random rand = new Random();
public static World createWorld(long seed) public static World createWorld(long seed)
{ {
synchronized (rand) { synchronized (rand) {
Log.f2("Generating a new world..."); Log.f2("Generating a new world...");
rand.setSeed(seed); rand.setSeed(seed);
final MapTheme theme = new ThemeBrick(); final MapTheme theme = new ThemeBrick();
final World w = new World(); final World w = new World();
w.setSeed(seed); w.setSeed(seed);
final LevelBuilder levelBuilders[] = new LevelBuilder[7]; final LevelBuilder levelBuilders[] = new LevelBuilder[7];
// build the level rooms // build the level rooms
for (int floor = 1; floor <= 7; floor++) { for (int floor = 1; floor <= 7; floor++) {
Log.f3("Placing rooms for level: " + floor); Log.f3("Placing rooms for level: " + floor);
final LevelBuilder lb = prepareFloor(rand.nextLong(), floor, theme, floor == 7); final LevelBuilder lb = prepareFloor(rand.nextLong(), floor, theme, floor == 7);
levelBuilders[floor - 1] = lb; levelBuilders[floor - 1] = lb;
} }
Log.f3("Placing items..."); Log.f3("Placing items...");
final List<ItemModel> weaponsBasic = new ArrayList<>(); final List<ItemModel> weaponsBasic = new ArrayList<>();
weaponsBasic.add(Items.ROCK); weaponsBasic.add(Items.ROCK);
weaponsBasic.add(Items.BONE); weaponsBasic.add(Items.BONE);
weaponsBasic.add(Items.TWIG); weaponsBasic.add(Items.TWIG);
final List<ItemModel> weaponsMedium = new ArrayList<>(); final List<ItemModel> weaponsMedium = new ArrayList<>();
weaponsMedium.add(Items.CLUB); weaponsMedium.add(Items.CLUB);
weaponsMedium.add(Items.KNIFE); weaponsMedium.add(Items.KNIFE);
final List<ItemModel> weaponsGood = new ArrayList<>(); final List<ItemModel> weaponsGood = new ArrayList<>();
weaponsGood.add(Items.AXE); weaponsGood.add(Items.AXE);
weaponsGood.add(Items.SWORD); weaponsGood.add(Items.SWORD);
for (int i = 0; i < Calc.randInt(rand, 12, 20); i++) { for (int i = 0; i < Calc.randInt(rand, 12, 20); i++) {
final Item item = Calc.pick(rand, weaponsBasic).createItemDamaged(40); final Item item = Calc.pick(rand, weaponsBasic).createItemDamaged(40);
final LevelBuilder lb = levelBuilders[-1 + Calc.randInt(1, 7)]; final LevelBuilder lb = levelBuilders[-1 + Calc.randInt(1, 7)];
lb.addItem(item, false); lb.addItem(item, false);
} }
for (int i = 0; i < Calc.randInt(rand, 2, 4); i++) { for (int i = 0; i < Calc.randInt(rand, 2, 4); i++) {
final Item item = Calc.pick(rand, weaponsMedium).createItemDamaged(50); final Item item = Calc.pick(rand, weaponsMedium).createItemDamaged(50);
final LevelBuilder lb = levelBuilders[-1 + Calc.randInt(3, 5)]; final LevelBuilder lb = levelBuilders[-1 + Calc.randInt(3, 5)];
lb.addRoom(Rooms.treasure(item), BuildOrder.MIDDLE, true); lb.addRoom(Rooms.treasure(item), BuildOrder.MIDDLE, true);
} }
for (int i = 0; i < Calc.randInt(rand, 2, 4); i++) { for (int i = 0; i < Calc.randInt(rand, 2, 4); i++) {
final Item item = Calc.pick(rand, weaponsGood).createItemDamaged(60); final Item item = Calc.pick(rand, weaponsGood).createItemDamaged(60);
final LevelBuilder lb = levelBuilders[-1 + Calc.randInt(4, 7)]; final LevelBuilder lb = levelBuilders[-1 + Calc.randInt(4, 7)];
lb.addRoom(Rooms.treasure(item), BuildOrder.LAST, true); lb.addRoom(Rooms.treasure(item), BuildOrder.LAST, true);
} }
// place random foods // place random foods
final List<ItemModel> randomFood = new ArrayList<>(); final List<ItemModel> randomFood = new ArrayList<>();
randomFood.add(Items.CHEESE); randomFood.add(Items.CHEESE);
randomFood.add(Items.MEAT); randomFood.add(Items.MEAT);
for (int level = 1; level <= 7; level++) { for (int level = 1; level <= 7; level++) {
final LevelBuilder lb = levelBuilders[level - 1]; final LevelBuilder lb = levelBuilders[level - 1];
final Range amount = Range.make(level / 2D, level * 2); final Range amount = Range.make(level / 2D, level * 2);
for (int i = 0; i < amount.randInt(rand); i++) { for (int i = 0; i < amount.randInt(rand); i++) {
lb.addItem(Calc.pick(rand, randomFood).createItem(), false); lb.addItem(Calc.pick(rand, randomFood).createItem(), false);
} }
} }
// place monsters // place monsters
Log.f3("Placing monsters..."); Log.f3("Placing monsters...");
for (int level = 1; level <= 7; level++) { for (int level = 1; level <= 7; level++) {
final LevelBuilder lb = levelBuilders[level - 1]; final LevelBuilder lb = levelBuilders[level - 1];
final Range amount = Range.make(3 + (level) * 2, 3 + level * 3); final Range amount = Range.make(3 + (level) * 2, 3 + level * 3);
for (int i = 0; i < amount.randInt(rand); i++) { for (int i = 0; i < amount.randInt(rand); i++) {
Entity e; Entity e;
if (level >= 2 && Calc.randInt(rand, 0, (int) (3.5 - (level / 2D))) == 0) { if (level >= 2 && Calc.randInt(rand, 0, (int) (3.5 - (level / 2D))) == 0) {
e = Entities.RAT_BROWN.createEntity(); e = Entities.RAT_BROWN.createEntity();
} else { } else {
e = Entities.RAT_GRAY.createEntity(); e = Entities.RAT_GRAY.createEntity();
} }
lb.addEntity(e, false); lb.addEntity(e, false);
} }
} }
// compile levels // compile levels
Log.f3("Building levels..."); Log.f3("Building levels...");
int i = 1; int i = 1;
@ -124,34 +124,34 @@ public class WorldCreator {
w.addLevel(lb.build(w)); w.addLevel(lb.build(w));
i++; i++;
} }
w.createPlayer(); w.createPlayer();
Log.f2("World generation finished."); Log.f2("World generation finished.");
return w; return w;
} }
} }
public static LevelBuilder prepareFloor(long seed, int floor, MapTheme theme, boolean lastLevel) throws WorldGenError public static LevelBuilder prepareFloor(long seed, int floor, MapTheme theme, boolean lastLevel) throws WorldGenError
{ {
final LevelBuilder lb = new LevelBuilder(128, theme, seed); final LevelBuilder lb = new LevelBuilder(128, theme, seed);
lb.addRoom(Rooms.ENTRANCE, BuildOrder.FIRST, true); lb.addRoom(Rooms.ENTRANCE, BuildOrder.FIRST, true);
lb.addRoom(Rooms.BASIC, Range.make(floor / 2, 2 + floor), BuildOrder.MIDDLE, false); lb.addRoom(Rooms.BASIC, Range.make(floor / 2, 2 + floor), BuildOrder.MIDDLE, false);
lb.addRoom(Rooms.DEAD_END, Range.make(1, floor), BuildOrder.MIDDLE, false); lb.addRoom(Rooms.DEAD_END, Range.make(1, floor), BuildOrder.MIDDLE, false);
lb.addRoom(Rooms.STORAGE, Range.make(1, Math.ceil(floor / 3D)), BuildOrder.MIDDLE, false); lb.addRoom(Rooms.STORAGE, Range.make(1, Math.ceil(floor / 3D)), BuildOrder.MIDDLE, false);
if (lastLevel) lb.addRoom(Rooms.BOSS, BuildOrder.LAST, true); if (lastLevel) lb.addRoom(Rooms.BOSS, BuildOrder.LAST, true);
if (!lastLevel) lb.addRoom(Rooms.EXIT, BuildOrder.LAST, true); if (!lastLevel) lb.addRoom(Rooms.EXIT, BuildOrder.LAST, true);
if (floor % 2 == 0) { if (floor % 2 == 0) {
final RoomBuilder heartRoom = Rooms.shrine(Items.HEART_PIECE.createItem()); final RoomBuilder heartRoom = Rooms.shrine(Items.HEART_PIECE.createItem());
lb.addRoom(heartRoom, BuildOrder.LAST, true); lb.addRoom(heartRoom, BuildOrder.LAST, true);
} }
return lb; return lb;
} }
} }

@ -3,28 +3,32 @@ package mightypork.rogue.world.gen;
/** /**
* Error in world generation * Error in world generation
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class WorldGenError extends RuntimeException { public class WorldGenError extends RuntimeException {
public WorldGenError() { public WorldGenError()
{
super(); super();
} }
public WorldGenError(String message, Throwable cause) { public WorldGenError(String message, Throwable cause)
{
super(message, cause); super(message, cause);
} }
public WorldGenError(String message) { public WorldGenError(String message)
{
super(message); super(message);
} }
public WorldGenError(Throwable cause) { public WorldGenError(Throwable cause)
{
super(cause); super(cause);
} }
} }

@ -15,7 +15,7 @@ import mightypork.utils.math.algo.Moves;
public abstract class AbstractRectRoom implements RoomBuilder { public abstract class AbstractRectRoom implements RoomBuilder {
@Override @Override
public RoomEntry buildRoom(ScratchMap map, MapTheme theme, Random rand, Coord center) public RoomEntry buildRoom(ScratchMap map, MapTheme theme, Random rand, Coord center)
{ {
@ -23,46 +23,46 @@ public abstract class AbstractRectRoom implements RoomBuilder {
final Coord innerSize = getInnerSize(rand); final Coord innerSize = getInnerSize(rand);
final int width = 2 + innerSize.x - 1; final int width = 2 + innerSize.x - 1;
final int height = 2 + innerSize.y - 1; final int height = 2 + innerSize.y - 1;
final int wLow = (int) Math.round(width / 2D); final int wLow = (int) Math.round(width / 2D);
final int wHigh = width - wLow; final int wHigh = width - wLow;
final int hLow = (int) Math.round(height / 2D); final int hLow = (int) Math.round(height / 2D);
final int hHigh = height - hLow; final int hHigh = height - hLow;
final Coord min = new Coord(center.x - wLow, center.y - hLow); final Coord min = new Coord(center.x - wLow, center.y - hLow);
final Coord max = new Coord(center.x + wHigh, center.y + hHigh); final Coord max = new Coord(center.x + wHigh, center.y + hHigh);
if (!map.isClear(min.add(-1, -1), max)) return null; if (!map.isClear(min.add(-1, -1), max)) return null;
map.fill(min, max, getFloor(theme)); map.fill(min, max, getFloor(theme));
map.border(min, max, getWall(theme)); map.border(min, max, getWall(theme));
map.protect(min, max, getWallProtectionLevel()); map.protect(min, max, getWallProtectionLevel());
placeDoors(map, theme, rand, min, max); placeDoors(map, theme, rand, min, max);
buildExtras(map, theme, rand, min, max); buildExtras(map, theme, rand, min, max);
return new RoomEntry(min.add(-1, -1), max); return new RoomEntry(min.add(-1, -1), max);
} }
protected TileModel getWall(MapTheme theme) protected TileModel getWall(MapTheme theme)
{ {
return theme.wall(); return theme.wall();
} }
protected TileModel getFloor(MapTheme theme) protected TileModel getFloor(MapTheme theme)
{ {
return theme.floor(); return theme.floor();
} }
protected void placeDoors(ScratchMap map, MapTheme theme, Random rand, Coord min, Coord max) protected void placeDoors(ScratchMap map, MapTheme theme, Random rand, Coord min, Coord max)
{ {
final int width = max.x - min.x; final int width = max.x - min.x;
final int height = max.y - min.y; final int height = max.y - min.y;
for (int i = 0, j = 0; i < getDoorCount(rand) && j < 100; j++) { // j is to prevent inf loop for (int i = 0, j = 0; i < getDoorCount(rand) && j < 100; j++) { // j is to prevent inf loop
final Coord door = min.copy(); final Coord door = min.copy();
switch (rand.nextInt(4)) { switch (rand.nextInt(4)) {
@ -83,29 +83,29 @@ public abstract class AbstractRectRoom implements RoomBuilder {
door.y += 1 + rand.nextInt(height - 1); door.y += 1 + rand.nextInt(height - 1);
break; break;
} }
if ((map.findDoors(door) & Moves.BITS_CARDINAL) == 0) { if ((map.findDoors(door) & Moves.BITS_CARDINAL) == 0) {
map.set(door, getDoorType(theme, rand)); map.set(door, getDoorType(theme, rand));
i++; // increment pointer i++; // increment pointer
} }
} }
} }
@Stub @Stub
protected void buildExtras(ScratchMap map, MapTheme theme, Random rand, Coord min, Coord max) protected void buildExtras(ScratchMap map, MapTheme theme, Random rand, Coord min, Coord max)
{ {
} }
protected abstract Coord getInnerSize(Random rand); protected abstract Coord getInnerSize(Random rand);
protected abstract TileProtectLevel getWallProtectionLevel(); protected abstract TileProtectLevel getWallProtectionLevel();
protected abstract TileModel getDoorType(MapTheme theme, Random rand); protected abstract TileModel getDoorType(MapTheme theme, Random rand);
protected abstract int getDoorCount(Random rand); protected abstract int getDoorCount(Random rand);
} }

@ -10,28 +10,28 @@ import mightypork.utils.math.algo.Coord;
public class BasicRoom extends AbstractRectRoom { public class BasicRoom extends AbstractRectRoom {
@Override @Override
protected TileModel getDoorType(MapTheme theme, Random rand) protected TileModel getDoorType(MapTheme theme, Random rand)
{ {
return rand.nextInt(4) == 0 ? theme.passage() : theme.door(); return rand.nextInt(4) == 0 ? theme.passage() : theme.door();
} }
@Override @Override
protected int getDoorCount(Random rand) protected int getDoorCount(Random rand)
{ {
return 1 + rand.nextInt(5); return 1 + rand.nextInt(5);
} }
@Override @Override
protected TileProtectLevel getWallProtectionLevel() protected TileProtectLevel getWallProtectionLevel()
{ {
return TileProtectLevel.WEAK; return TileProtectLevel.WEAK;
} }
@Override @Override
protected Coord getInnerSize(Random rand) protected Coord getInnerSize(Random rand)
{ {

@ -12,43 +12,43 @@ import mightypork.utils.math.algo.Coord;
public class BossRoom extends SecretRoom { public class BossRoom extends SecretRoom {
@Override @Override
protected int getDoorCount(Random rand) protected int getDoorCount(Random rand)
{ {
return 1; return 1;
} }
@Override @Override
protected void buildExtras(ScratchMap map, MapTheme theme, Random rand, Coord min, Coord max) protected void buildExtras(ScratchMap map, MapTheme theme, Random rand, Coord min, Coord max)
{ {
final Coord bossPos = min.add(3, 3); final Coord bossPos = min.add(3, 3);
final Entity boss = Entities.RAT_BOSS.createEntity(); final Entity boss = Entities.RAT_BOSS.createEntity();
if (!map.addEntity(boss, bossPos)) { if (!map.addEntity(boss, bossPos)) {
throw new WorldGenError("Could not place boss."); throw new WorldGenError("Could not place boss.");
} }
Entity rat; Entity rat;
// 4 guardian rats // 4 guardian rats
rat = Entities.RAT_BROWN.createEntity(); rat = Entities.RAT_BROWN.createEntity();
map.addEntity(rat, min.add(1, 1)); map.addEntity(rat, min.add(1, 1));
rat = Entities.RAT_BROWN.createEntity(); rat = Entities.RAT_BROWN.createEntity();
map.addEntity(rat, Coord.make(max.x - 1, min.y + 1)); map.addEntity(rat, Coord.make(max.x - 1, min.y + 1));
rat = Entities.RAT_BROWN.createEntity(); rat = Entities.RAT_BROWN.createEntity();
map.addEntity(rat, max.add(-1, -1)); map.addEntity(rat, max.add(-1, -1));
rat = Entities.RAT_BROWN.createEntity(); rat = Entities.RAT_BROWN.createEntity();
map.addEntity(rat, Coord.make(min.x + 1, max.y - 1)); map.addEntity(rat, Coord.make(min.x + 1, max.y - 1));
} }
@Override @Override
protected Coord getInnerSize(Random rand) protected Coord getInnerSize(Random rand)
{ {

@ -10,28 +10,28 @@ import mightypork.utils.math.algo.Coord;
public class DeadEndRoom extends AbstractRectRoom { public class DeadEndRoom extends AbstractRectRoom {
@Override @Override
protected Coord getInnerSize(Random rand) protected Coord getInnerSize(Random rand)
{ {
return Coord.make(1, 1); return Coord.make(1, 1);
} }
@Override @Override
protected TileProtectLevel getWallProtectionLevel() protected TileProtectLevel getWallProtectionLevel()
{ {
return TileProtectLevel.STRONG; return TileProtectLevel.STRONG;
} }
@Override @Override
protected TileModel getDoorType(MapTheme theme, Random rand) protected TileModel getDoorType(MapTheme theme, Random rand)
{ {
return theme.floor(); return theme.floor();
} }
@Override @Override
protected int getDoorCount(Random rand) protected int getDoorCount(Random rand)
{ {

@ -12,21 +12,21 @@ import mightypork.utils.math.algo.Coord;
public class EntranceRoom extends AbstractRectRoom { public class EntranceRoom extends AbstractRectRoom {
@Override @Override
protected Coord getInnerSize(Random rand) protected Coord getInnerSize(Random rand)
{ {
return Coord.make(3 + rand.nextInt(3), 3 + rand.nextInt(3)); return Coord.make(3 + rand.nextInt(3), 3 + rand.nextInt(3));
} }
@Override @Override
protected TileProtectLevel getWallProtectionLevel() protected TileProtectLevel getWallProtectionLevel()
{ {
return TileProtectLevel.WEAK; return TileProtectLevel.WEAK;
} }
@Override @Override
protected TileModel getDoorType(MapTheme theme, Random rand) protected TileModel getDoorType(MapTheme theme, Random rand)
{ {
@ -40,8 +40,8 @@ public class EntranceRoom extends AbstractRectRoom {
return theme.door(); return theme.door();
} }
} }
@Override @Override
protected void buildExtras(ScratchMap map, MapTheme theme, Random rand, Coord min, Coord max) protected void buildExtras(ScratchMap map, MapTheme theme, Random rand, Coord min, Coord max)
{ {
@ -50,12 +50,12 @@ public class EntranceRoom extends AbstractRectRoom {
map.protect(c, c, TileProtectLevel.STRONG); map.protect(c, c, TileProtectLevel.STRONG);
map.setEntrance(c.add(1, 0)); map.setEntrance(c.add(1, 0));
} }
@Override @Override
protected int getDoorCount(Random rand) protected int getDoorCount(Random rand)
{ {
return Calc.randInt(rand, 1, 4); return Calc.randInt(rand, 1, 4);
} }
} }

@ -11,28 +11,28 @@ import mightypork.utils.math.algo.Coord;
public class ExitRoom extends AbstractRectRoom { public class ExitRoom extends AbstractRectRoom {
@Override @Override
protected Coord getInnerSize(Random rand) protected Coord getInnerSize(Random rand)
{ {
return Coord.make(3 + rand.nextInt(2), 3 + rand.nextInt(2)); return Coord.make(3 + rand.nextInt(2), 3 + rand.nextInt(2));
} }
@Override @Override
protected TileProtectLevel getWallProtectionLevel() protected TileProtectLevel getWallProtectionLevel()
{ {
return TileProtectLevel.NONE; return TileProtectLevel.NONE;
} }
@Override @Override
protected TileModel getDoorType(MapTheme theme, Random rand) protected TileModel getDoorType(MapTheme theme, Random rand)
{ {
return null; return null;
} }
@Override @Override
protected void buildExtras(ScratchMap map, MapTheme theme, Random rand, Coord min, Coord max) protected void buildExtras(ScratchMap map, MapTheme theme, Random rand, Coord min, Coord max)
{ {
@ -41,12 +41,12 @@ public class ExitRoom extends AbstractRectRoom {
map.protect(c, c, TileProtectLevel.STRONG); map.protect(c, c, TileProtectLevel.STRONG);
map.setExit(c.add(-1, 0)); map.setExit(c.add(-1, 0));
} }
@Override @Override
protected int getDoorCount(Random rand) protected int getDoorCount(Random rand)
{ {
return 0; return 0;
} }
} }

@ -12,33 +12,34 @@ import mightypork.utils.math.algo.Coord;
public class ItemShrineRoom extends SecretRoom { public class ItemShrineRoom extends SecretRoom {
private final Item item; private final Item item;
public ItemShrineRoom(Item item) { public ItemShrineRoom(Item item)
{
this.item = item; this.item = item;
} }
@Override @Override
protected int getDoorCount(Random rand) protected int getDoorCount(Random rand)
{ {
return Calc.randInt(rand, 1, 4); return Calc.randInt(rand, 1, 4);
} }
@Override @Override
protected void buildExtras(ScratchMap map, MapTheme theme, Random rand, Coord min, Coord max) protected void buildExtras(ScratchMap map, MapTheme theme, Random rand, Coord min, Coord max)
{ {
final Coord center = min.add(2, 2); final Coord center = min.add(2, 2);
if (!map.addItem(item, center)) { if (!map.addItem(item, center)) {
throw new WorldGenError("Could not place item in chest."); throw new WorldGenError("Could not place item in chest.");
} }
} }
@Override @Override
protected Coord getInnerSize(Random rand) protected Coord getInnerSize(Random rand)
{ {

@ -11,28 +11,28 @@ import mightypork.utils.math.algo.Coord;
public abstract class SecretRoom extends AbstractRectRoom { public abstract class SecretRoom extends AbstractRectRoom {
@Override @Override
protected TileModel getDoorType(MapTheme theme, Random rand) protected TileModel getDoorType(MapTheme theme, Random rand)
{ {
return rand.nextInt(5) == 0 ? theme.passage() : theme.secretDoor(); return rand.nextInt(5) == 0 ? theme.passage() : theme.secretDoor();
} }
@Override @Override
protected int getDoorCount(Random rand) protected int getDoorCount(Random rand)
{ {
return Calc.randInt(rand, 1, 3); return Calc.randInt(rand, 1, 3);
} }
@Override @Override
protected TileProtectLevel getWallProtectionLevel() protected TileProtectLevel getWallProtectionLevel()
{ {
return TileProtectLevel.STRONG; return TileProtectLevel.STRONG;
} }
@Override @Override
protected Coord getInnerSize(Random rand) protected Coord getInnerSize(Random rand)
{ {

@ -11,44 +11,44 @@ import mightypork.utils.math.algo.Coord;
public class StorageRoom extends SecretRoom { public class StorageRoom extends SecretRoom {
@Override @Override
protected void buildExtras(ScratchMap map, MapTheme theme, Random rand, Coord min, Coord max) protected void buildExtras(ScratchMap map, MapTheme theme, Random rand, Coord min, Coord max)
{ {
int maxStuff = Calc.randInt(rand, 3, 5); int maxStuff = Calc.randInt(rand, 3, 5);
// at least one meat or cheese. // at least one meat or cheese.
final boolean oneMeat = rand.nextBoolean(); final boolean oneMeat = rand.nextBoolean();
for (int i = 0; i < Calc.randInt(rand, oneMeat ? 1 : 0, 3); i++) { for (int i = 0; i < Calc.randInt(rand, oneMeat ? 1 : 0, 3); i++) {
map.addItemInArea(Items.MEAT.createItem(), min, max, 50); map.addItemInArea(Items.MEAT.createItem(), min, max, 50);
if (--maxStuff == 0) return; if (--maxStuff == 0) return;
} }
for (int i = 0; i < Calc.randInt(rand, oneMeat ? 0 : 1, 2); i++) { for (int i = 0; i < Calc.randInt(rand, oneMeat ? 0 : 1, 2); i++) {
map.addItemInArea(Items.CHEESE.createItem(), min, max, 50); map.addItemInArea(Items.CHEESE.createItem(), min, max, 50);
if (--maxStuff == 0) return; if (--maxStuff == 0) return;
} }
for (int i = 0; i < Calc.randInt(rand, 0, 1); i++) { for (int i = 0; i < Calc.randInt(rand, 0, 1); i++) {
map.addItemInArea(Items.ROCK.createItemDamaged(30), min, max, 50); map.addItemInArea(Items.ROCK.createItemDamaged(30), min, max, 50);
if (--maxStuff == 0) return; if (--maxStuff == 0) return;
} }
for (int i = 0; i < Calc.randInt(rand, 0, 1); i++) { for (int i = 0; i < Calc.randInt(rand, 0, 1); i++) {
map.addItemInArea(Items.SANDWICH.createItem(), min, max, 50); map.addItemInArea(Items.SANDWICH.createItem(), min, max, 50);
if (--maxStuff == 0) return; if (--maxStuff == 0) return;
} }
for (int i = 0; i < Calc.randInt(rand, 0, 2); i++) { for (int i = 0; i < Calc.randInt(rand, 0, 2); i++) {
map.addItemInArea(Items.TWIG.createItemDamaged(40), min, max, 50); map.addItemInArea(Items.TWIG.createItemDamaged(40), min, max, 50);
if (--maxStuff == 0) return; if (--maxStuff == 0) return;
} }
for (int i = 0; i < Calc.randInt(rand, 0, 2); i++) { for (int i = 0; i < Calc.randInt(rand, 0, 2); i++) {
map.addItemInArea(Items.BONE.createItemDamaged(40), min, max, 50); map.addItemInArea(Items.BONE.createItemDamaged(40), min, max, 50);
if (--maxStuff == 0) return; if (--maxStuff == 0) return;
} }
} }
} }

@ -11,21 +11,22 @@ import mightypork.utils.math.algo.Coord;
public class TreasureChestRoom extends ItemShrineRoom { public class TreasureChestRoom extends ItemShrineRoom {
public TreasureChestRoom(Item item) { public TreasureChestRoom(Item item)
{
super(item); super(item);
} }
@Override @Override
protected void buildExtras(ScratchMap map, MapTheme theme, Random rand, Coord min, Coord max) protected void buildExtras(ScratchMap map, MapTheme theme, Random rand, Coord min, Coord max)
{ {
// set tile // set tile
final Coord center = min.add(2, 2); final Coord center = min.add(2, 2);
map.set(center, theme.chest()); map.set(center, theme.chest());
map.protect(center, TileProtectLevel.STRONG); map.protect(center, TileProtectLevel.STRONG);
// drop item // drop item
super.buildExtras(map, theme, rand, min, max); super.buildExtras(map, theme, rand, min, max);
} }

@ -8,56 +8,56 @@ import mightypork.rogue.world.tile.Tiles;
// basic dungeon theme // basic dungeon theme
public class ThemeBrick implements MapTheme { public class ThemeBrick implements MapTheme {
@Override @Override
public TileModel wall() public TileModel wall()
{ {
return Tiles.BRICK_WALL; return Tiles.BRICK_WALL;
} }
@Override @Override
public TileModel floor() public TileModel floor()
{ {
return Tiles.BRICK_FLOOR; return Tiles.BRICK_FLOOR;
} }
@Override @Override
public TileModel door() public TileModel door()
{ {
return Tiles.BRICK_DOOR; return Tiles.BRICK_DOOR;
} }
@Override @Override
public TileModel passage() public TileModel passage()
{ {
return Tiles.BRICK_PASSAGE; return Tiles.BRICK_PASSAGE;
} }
@Override @Override
public TileModel secretDoor() public TileModel secretDoor()
{ {
return Tiles.BRICK_HIDDEN_DOOR; return Tiles.BRICK_HIDDEN_DOOR;
} }
@Override @Override
public TileModel entrance() public TileModel entrance()
{ {
return Tiles.BRICK_ENTRANCE; return Tiles.BRICK_ENTRANCE;
} }
@Override @Override
public TileModel exit() public TileModel exit()
{ {
return Tiles.BRICK_EXIT; return Tiles.BRICK_EXIT;
} }
@Override @Override
public TileModel chest() public TileModel chest()
{ {

@ -29,27 +29,28 @@ import mightypork.utils.math.timing.TimedTask;
/** /**
* Level display component * Level display component
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public class MapView extends InputComponent implements DelegatingClient, MouseButtonHandler, Updateable, WorldAscendRequestListener, WorldDescendRequestListener { public class MapView extends InputComponent implements DelegatingClient, MouseButtonHandler, Updateable, WorldAscendRequestListener,
WorldDescendRequestListener {
private static final double transition_time = 0.8; private static final double transition_time = 0.8;
protected final WorldRenderer worldRenderer; protected final WorldRenderer worldRenderer;
public final PlayerControl plc; public final PlayerControl plc;
private final Set<MapInteractionPlugin> plugins = new LinkedHashSet<>(); private final Set<MapInteractionPlugin> plugins = new LinkedHashSet<>();
private final NumAnimated zoom = new NumAnimated(0, Easing.SINE_BOTH); private final NumAnimated zoom = new NumAnimated(0, Easing.SINE_BOTH);
private boolean zoom_in = true; private boolean zoom_in = true;
private final NumAnimated descFadeAnim = new NumAnimated(0); private final NumAnimated descFadeAnim = new NumAnimated(0);
private final Color blackColor = RGB.BLACK.withAlpha(descFadeAnim); private final Color blackColor = RGB.BLACK.withAlpha(descFadeAnim);
private int descDir = 0; private int descDir = 0;
private final TimedTask timerDesc1 = new TimedTask() { private final TimedTask timerDesc1 = new TimedTask() {
@Override @Override
public void run() public void run()
{ {
@ -62,55 +63,56 @@ public class MapView extends InputComponent implements DelegatingClient, MouseBu
} }
} }
}; };
private final TimedTask timerDesc2 = new TimedTask() { private final TimedTask timerDesc2 = new TimedTask() {
@Override @Override
public void run() public void run()
{ {
WorldProvider.get().getWorld().resume(); WorldProvider.get().getWorld().resume();
} }
}; };
private final Num tileSize; private final Num tileSize;
@SuppressWarnings({ "unchecked", "rawtypes" }) @SuppressWarnings({ "unchecked", "rawtypes" })
@Override @Override
public Collection getChildClients() public Collection getChildClients()
{ {
return plugins; return plugins;
} }
@Override @Override
public boolean doesDelegate() public boolean doesDelegate()
{ {
return true; return true;
} }
public MapView() { public MapView()
{
this.tileSize = height().min(width()).div(9).max(32).mul(Num.make(1).sub(zoom.mul(0.5))); this.tileSize = height().min(width()).div(9).max(32).mul(Num.make(1).sub(zoom.mul(0.5)));
this.worldRenderer = new WorldRenderer(this, tileSize); this.worldRenderer = new WorldRenderer(this, tileSize);
plc = WorldProvider.get().getPlayerControl(); plc = WorldProvider.get().getPlayerControl();
zoom.setDefaultDuration(0.5); zoom.setDefaultDuration(0.5);
} }
@Override @Override
protected void renderComponent() protected void renderComponent()
{ {
worldRenderer.render(); worldRenderer.render();
App.gfx().quad(this, blackColor); App.gfx().quad(this, blackColor);
} }
/** /**
* Get tile coord at a screen position * Get tile coord at a screen position
* *
* @param pos position on screen (px) * @param pos position on screen (px)
* @return position on map (tiles) * @return position on map (tiles)
*/ */
@ -118,13 +120,13 @@ public class MapView extends InputComponent implements DelegatingClient, MouseBu
{ {
return worldRenderer.getClickedTile(pos); return worldRenderer.getClickedTile(pos);
} }
@Override @Override
public void receive(MouseButtonEvent event) public void receive(MouseButtonEvent event)
{ {
if (!event.isOver(this)) return; if (!event.isOver(this)) return;
if (event.isButtonEvent()) { if (event.isButtonEvent()) {
for (final MapInteractionPlugin p : plugins) { for (final MapInteractionPlugin p : plugins) {
if (p.onClick(event.getPos(), event.getButton(), event.isDown())) { if (p.onClick(event.getPos(), event.getButton(), event.isDown())) {
@ -133,7 +135,7 @@ public class MapView extends InputComponent implements DelegatingClient, MouseBu
} }
} }
} }
if (event.isWheelEvent()) { if (event.isWheelEvent()) {
final int delta = event.getWheelDelta(); final int delta = event.getWheelDelta();
if (!zoom.isFinished()) return; if (!zoom.isFinished()) return;
@ -146,8 +148,8 @@ public class MapView extends InputComponent implements DelegatingClient, MouseBu
} }
} }
} }
public void toggleMag() public void toggleMag()
{ {
if (zoom_in) { if (zoom_in) {
@ -158,19 +160,19 @@ public class MapView extends InputComponent implements DelegatingClient, MouseBu
zoom_in = true; zoom_in = true;
} }
} }
/** /**
* Add interaction plugin * Add interaction plugin
* *
* @param plugin * @param plugin
*/ */
public void addPlugin(MapInteractionPlugin plugin) public void addPlugin(MapInteractionPlugin plugin)
{ {
plugins.add(plugin); plugins.add(plugin);
} }
@Override @Override
public void update(double delta) public void update(double delta)
{ {
@ -179,40 +181,40 @@ public class MapView extends InputComponent implements DelegatingClient, MouseBu
timerDesc1.update(delta); timerDesc1.update(delta);
timerDesc2.update(delta); timerDesc2.update(delta);
} }
@Override @Override
public void onAscendRequest() public void onAscendRequest()
{ {
if (descFadeAnim.isInProgress()) return; if (descFadeAnim.isInProgress()) return;
final World w = WorldProvider.get().getWorld(); final World w = WorldProvider.get().getWorld();
if (w.getPlayer().canAscend()) { if (w.getPlayer().canAscend()) {
descDir = -1; descDir = -1;
startDescAnim(); startDescAnim();
} }
} }
private void startDescAnim() private void startDescAnim()
{ {
WorldProvider.get().getWorld().pause(); WorldProvider.get().getWorld().pause();
timerDesc2.stop(); timerDesc2.stop();
timerDesc1.start(transition_time); timerDesc1.start(transition_time);
descFadeAnim.setTo(0); descFadeAnim.setTo(0);
descFadeAnim.fadeIn(transition_time); descFadeAnim.fadeIn(transition_time);
} }
@Override @Override
public void onDescendRequest() public void onDescendRequest()
{ {
if (descFadeAnim.isInProgress()) return; if (descFadeAnim.isInProgress()) return;
final World w = WorldProvider.get().getWorld(); final World w = WorldProvider.get().getWorld();
if (w.getPlayer().canDescend()) { if (w.getPlayer().canDescend()) {
descDir = 1; descDir = 1;
startDescAnim(); startDescAnim();

@ -18,76 +18,74 @@ import mightypork.utils.math.constraints.rect.Rect;
import mightypork.utils.math.constraints.rect.var.RectVar; import mightypork.utils.math.constraints.rect.var.RectVar;
import mightypork.utils.math.constraints.vect.Vect; import mightypork.utils.math.constraints.vect.Vect;
import org.lwjgl.opengl.GL11;
public class Minimap extends InputComponent implements MouseButtonHandler { public class Minimap extends InputComponent implements MouseButtonHandler {
private final RectVar bounds = Rect.makeVar(); private final RectVar bounds = Rect.makeVar();
private int unit = 0; private int unit = 0;
private final Num translucency = Num.make(0.8); private final Num translucency = Num.make(0.8);
private final Color playerColor = RGB.RED; private final Color playerColor = RGB.RED;
@Override @Override
protected void renderComponent() protected void renderComponent()
{ {
Color.pushAlpha(translucency); Color.pushAlpha(translucency);
final Level lvl = WorldProvider.get().getCurrentLevel(); final Level lvl = WorldProvider.get().getCurrentLevel();
unit = (int) Math.min(Math.max(2, Math.ceil((height().value() / 2) / (lvl.getHeight() + 2))), 10); unit = (int) Math.min(Math.max(2, Math.ceil((height().value() / 2) / (lvl.getHeight() + 2))), 10);
final Vect plCoord = WorldProvider.get().getPlayer().getVisualPos(); final Vect plCoord = WorldProvider.get().getPlayer().getVisualPos();
final int lw = lvl.getWidth(); final int lw = lvl.getWidth();
final int lh = lvl.getHeight(); final int lh = lvl.getHeight();
final Vect tl = topRight().sub(unit * lw, 0); final Vect tl = topRight().sub(unit * lw, 0);
bounds.setTo(tl, unit * lw, unit * lh); bounds.setTo(tl, unit * lw, unit * lh);
final Coord point = new Coord(tl.xi(), tl.yi()); final Coord point = new Coord(tl.xi(), tl.yi());
// FIXME do not use LWJGL directly // FIXME do not use LWJGL directly
GL11.glDisable(GL11.GL_TEXTURE_2D); GL11.glDisable(GL11.GL_TEXTURE_2D);
GL11.glBegin(GL11.GL_QUADS); GL11.glBegin(GL11.GL_QUADS);
for (final Coord pos = Coord.zero(); pos.y < lh; pos.y++, point.y += unit) { for (final Coord pos = Coord.zero(); pos.y < lh; pos.y++, point.y += unit) {
for (pos.x = 0, point.x = tl.xi(); pos.x < lw; pos.x++, point.x += unit) { for (pos.x = 0, point.x = tl.xi(); pos.x < lw; pos.x++, point.x += unit) {
final Tile t = lvl.getTile(pos); final Tile t = lvl.getTile(pos);
if (t.isNull() || (!t.isExplored() && Const.RENDER_UFOG)) continue; if (t.isNull() || (!t.isExplored() && Const.RENDER_UFOG)) continue;
final Color clr = t.getMapColor(); final Color clr = t.getMapColor();
App.gfx().setColor(clr); App.gfx().setColor(clr);
GL11.glVertex2i(point.x, point.y); GL11.glVertex2i(point.x, point.y);
GL11.glVertex2i(point.x + unit, point.y); GL11.glVertex2i(point.x + unit, point.y);
GL11.glVertex2i(point.x + unit, point.y + unit); GL11.glVertex2i(point.x + unit, point.y + unit);
GL11.glVertex2i(point.x, point.y + unit); GL11.glVertex2i(point.x, point.y + unit);
} }
} }
// player // player
App.gfx().setColor(playerColor); App.gfx().setColor(playerColor);
final double plx = tl.xi() + plCoord.x() * unit; final double plx = tl.xi() + plCoord.x() * unit;
final double ply = tl.yi() + plCoord.y() * unit; final double ply = tl.yi() + plCoord.y() * unit;
GL11.glVertex2d(plx, ply); GL11.glVertex2d(plx, ply);
GL11.glVertex2d(plx + unit, ply); GL11.glVertex2d(plx + unit, ply);
GL11.glVertex2d(plx + unit, ply + unit); GL11.glVertex2d(plx + unit, ply + unit);
GL11.glVertex2d(plx, ply + unit); GL11.glVertex2d(plx, ply + unit);
GL11.glEnd(); GL11.glEnd();
Color.popAlpha(); Color.popAlpha();
} }
@Override @Override
public void receive(MouseButtonEvent event) public void receive(MouseButtonEvent event)
{ {
@ -95,14 +93,14 @@ public class Minimap extends InputComponent implements MouseButtonHandler {
if (event.isUp()) { if (event.isUp()) {
final Vect relative = event.getPos().sub(bounds.origin()); final Vect relative = event.getPos().sub(bounds.origin());
final Coord actual = Coord.make(relative.xi() / unit, relative.yi() / unit); final Coord actual = Coord.make(relative.xi() / unit, relative.yi() / unit);
final PlayerFacade player = WorldProvider.get().getPlayer(); final PlayerFacade player = WorldProvider.get().getPlayer();
if (player.getLevel().getTile(actual).isExplored()) { if (player.getLevel().getTile(actual).isExplored()) {
player.navigateTo(actual); player.navigateTo(actual);
} }
} }
event.consume(); event.consume();
} }
} }

@ -21,69 +21,70 @@ import mightypork.utils.math.constraints.rect.Rect;
public class WorldConsoleRenderer extends BaseComponent { public class WorldConsoleRenderer extends BaseComponent {
private final Num rowHeight; private final Num rowHeight;
private final FontRenderer fr; private final FontRenderer fr;
private final Rect itemViewRect; private final Rect itemViewRect;
public WorldConsoleRenderer(Num rowHeight) { public WorldConsoleRenderer(Num rowHeight)
{
this.rowHeight = rowHeight; this.rowHeight = rowHeight;
this.fr = new FontRenderer(Res.getFont("tiny")); this.fr = new FontRenderer(Res.getFont("tiny"));
final Num itmsize = height().perc(25).min(256).max(16); final Num itmsize = height().perc(25).min(256).max(16);
this.itemViewRect = bottomRight().sub(itmsize.perc(25), itmsize.perc(25)).startRect().grow(itmsize, Num.ZERO, itmsize, Num.ZERO); this.itemViewRect = bottomRight().sub(itmsize.perc(25), itmsize.perc(25)).startRect().grow(itmsize, Num.ZERO, itmsize, Num.ZERO);
} }
@Override @Override
protected void renderComponent() protected void renderComponent()
{ {
final double rh = rowHeight.value(); final double rh = rowHeight.value();
final Rect lowRow = bottomEdge().growUp(rowHeight); final Rect lowRow = bottomEdge().growUp(rowHeight);
final WorldConsole console = WorldProvider.get().getWorld().getConsole(); final WorldConsole console = WorldProvider.get().getWorld().getConsole();
final Collection<WorldConsole.Entry> entries = console.getEntries(); final Collection<WorldConsole.Entry> entries = console.getEntries();
int cnt = 0; int cnt = 0;
final NumVar alph = Num.makeVar(); final NumVar alph = Num.makeVar();
Color.pushAlpha(alph); Color.pushAlpha(alph);
try { try {
for (final WorldConsole.Entry entry : entries) { for (final WorldConsole.Entry entry : entries) {
alph.setTo(entry.getAlpha()); alph.setTo(entry.getAlpha());
final Rect rrr = lowRow.moveY(-rh * cnt); final Rect rrr = lowRow.moveY(-rh * cnt);
fr.draw(entry.getMessage(), rrr.move(rh / 8D, rh / 8D), AlignX.LEFT, RGB.BLACK_60); fr.draw(entry.getMessage(), rrr.move(rh / 8D, rh / 8D), AlignX.LEFT, RGB.BLACK_60);
fr.draw(entry.getMessage(), rrr, AlignX.LEFT, RGB.WHITE); fr.draw(entry.getMessage(), rrr, AlignX.LEFT, RGB.WHITE);
cnt++; cnt++;
} }
} catch (final ConcurrentModificationException e) { } catch (final ConcurrentModificationException e) {
Log.e(e); // this should not happen Log.e(e); // this should not happen
} }
if (console.lastPickupItem != null) { if (console.lastPickupItem != null) {
double alpha = 1; double alpha = 1;
if (console.timeSinceLastPickup > 2) { if (console.timeSinceLastPickup > 2) {
alpha = 1 - Easing.CIRC_OUT.get(Calc.clamp((console.timeSinceLastPickup - 2) / 1, 0, 1)); alpha = 1 - Easing.CIRC_OUT.get(Calc.clamp((console.timeSinceLastPickup - 2) / 1, 0, 1));
} }
alph.setTo(alpha); alph.setTo(alpha);
console.lastPickupItem.render(itemViewRect); console.lastPickupItem.render(itemViewRect);
} }
Color.popAlpha(); Color.popAlpha();
} }
} }

@ -22,9 +22,9 @@ import mightypork.utils.math.constraints.vect.Vect;
public class MIPKeyboard extends MapInteractionPlugin implements DelegatingClient, PlayerStepEndListener, Updateable { public class MIPKeyboard extends MapInteractionPlugin implements DelegatingClient, PlayerStepEndListener, Updateable {
// FIXME cannot be static. // FIXME cannot be static.
//@formatter:off //@formatter:off
private static final KeyStroke[] keys = { private static final KeyStroke[] keys = {
Config.getKeyStroke("game.walk.left"), Config.getKeyStroke("game.walk.left"),
@ -32,41 +32,42 @@ public class MIPKeyboard extends MapInteractionPlugin implements DelegatingClien
Config.getKeyStroke("game.walk.up"), Config.getKeyStroke("game.walk.up"),
Config.getKeyStroke("game.walk.down") Config.getKeyStroke("game.walk.down")
}; };
private static final Move[] sides = { Moves.W, Moves.E, Moves.N, Moves.S }; private static final Move[] sides = { Moves.W, Moves.E, Moves.N, Moves.S };
//@formatter:on //@formatter:on
private final KeyBindingPool kbp = new KeyBindingPool(); private final KeyBindingPool kbp = new KeyBindingPool();
private final List<Object> clients = new ArrayList<>(); private final List<Object> clients = new ArrayList<>();
{ {
clients.add(kbp); clients.add(kbp);
} }
@Override @Override
public boolean doesDelegate() public boolean doesDelegate()
{ {
return true; return true;
} }
@Override @Override
public Collection<?> getChildClients() public Collection<?> getChildClients()
{ {
return clients; return clients;
} }
public MIPKeyboard(MapView mapView) { public MIPKeyboard(MapView mapView)
{
super(mapView); super(mapView);
// bind keys // bind keys
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
final int j = i; final int j = i;
kbp.bindKey(keys[i], Trigger.RISING, new Runnable() { kbp.bindKey(keys[i], Trigger.RISING, new Runnable() {
@Override @Override
public void run() public void run()
{ {
@ -75,41 +76,41 @@ public class MIPKeyboard extends MapInteractionPlugin implements DelegatingClien
}); });
} }
} }
@Override @Override
public void onStepFinished(EntityPlayer player) public void onStepFinished(EntityPlayer player)
{ {
walkByKey(); walkByKey();
} }
@Override @Override
public boolean onClick(Vect mouse, int button, boolean down) public boolean onClick(Vect mouse, int button, boolean down)
{ {
return false; return false;
} }
private void clickSide(Move side) private void clickSide(Move side)
{ {
if (isImmobile() || getPlayer().isMoving()) return; if (isImmobile() || getPlayer().isMoving()) return;
mapView.plc.clickTile(side); mapView.plc.clickTile(side);
} }
private boolean walkByKey() private boolean walkByKey()
{ {
if (isImmobile()) return false; if (isImmobile()) return false;
if (mapView.plc.getPlayer().getMoveProgress() < 0.8) return false; if (mapView.plc.getPlayer().getMoveProgress() < 0.8) return false;
if (LwjglInputModule.getActiveKeyMod() != Keys.MOD_NONE) return false; if (LwjglInputModule.getActiveKeyMod() != Keys.MOD_NONE) return false;
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
if (keys[i].isDown()) { if (keys[i].isDown()) {
final Move side = sides[i]; final Move side = sides[i];
if (mapView.plc.canGo(side)) { if (mapView.plc.canGo(side)) {
mapView.plc.go(side); mapView.plc.go(side);
@ -121,8 +122,8 @@ public class MIPKeyboard extends MapInteractionPlugin implements DelegatingClien
} }
return false; return false;
} }
@Override @Override
public void update(double delta) public void update(double delta)
{ {

@ -1,7 +1,6 @@
package mightypork.rogue.world.gui.interaction; package mightypork.rogue.world.gui.interaction;
import mightypork.gamecore.backends.lwjgl.LwjglInputModule;
import mightypork.gamecore.core.App; import mightypork.gamecore.core.App;
import mightypork.rogue.world.entity.impl.EntityPlayer; import mightypork.rogue.world.entity.impl.EntityPlayer;
import mightypork.rogue.world.events.PlayerStepEndListener; import mightypork.rogue.world.events.PlayerStepEndListener;
@ -16,115 +15,116 @@ import mightypork.utils.math.constraints.vect.Vect;
public class MIPMouse extends MapInteractionPlugin implements PlayerStepEndListener, Updateable { public class MIPMouse extends MapInteractionPlugin implements PlayerStepEndListener, Updateable {
private static final int LEFT = 0; // left private static final int LEFT = 0; // left
private static final int RIGHT = 1; // left private static final int RIGHT = 1; // left
public MIPMouse(MapView mapView) { public MIPMouse(MapView mapView)
{
super(mapView); super(mapView);
} }
@Override @Override
public void update(double delta) public void update(double delta)
{ {
if (isImmobile()) return; if (isImmobile()) return;
final Vect pos = App.input().getMousePos(); final Vect pos = App.input().getMousePos();
if (!pos.isInside(mapView)) return; if (!pos.isInside(mapView)) return;
if (App.input().isMouseButtonDown(LEFT)) { if (App.input().isMouseButtonDown(LEFT)) {
if (mouseWalk(pos)) return; if (mouseWalk(pos)) return;
if (mapView.plc.getPlayer().isMoving() && troToNav(pos)) return; if (mapView.plc.getPlayer().isMoving() && troToNav(pos)) return;
} }
} }
@Override @Override
public boolean onClick(Vect mouse, int button, boolean down) public boolean onClick(Vect mouse, int button, boolean down)
{ {
if (isImmobile()) { if (isImmobile()) {
return false; return false;
} }
final Vect pos = mapView.toWorldPos(mouse); final Vect pos = mapView.toWorldPos(mouse);
if (button == LEFT && !down) { if (button == LEFT && !down) {
// try to click tile // try to click tile
if (mapView.plc.clickTile(pos)) return true; if (mapView.plc.clickTile(pos)) return true;
} }
final Tile t = mapView.plc.getLevel().getTile(Coord.fromVect(pos)); final Tile t = mapView.plc.getLevel().getTile(Coord.fromVect(pos));
if (button == RIGHT && !down && t.isWalkable()) { if (button == RIGHT && !down && t.isWalkable()) {
if (troToNav(mouse)) return true; if (troToNav(mouse)) return true;
return mouseWalk(mouse); return mouseWalk(mouse);
} }
return false; return false;
} }
private boolean troToNav(Vect mouse) private boolean troToNav(Vect mouse)
{ {
if (isImmobile()) return false; if (isImmobile()) return false;
final Coord plpos = mapView.plc.getPlayer().getCoord(); final Coord plpos = mapView.plc.getPlayer().getCoord();
final Coord clicked = Coord.fromVect(mapView.toWorldPos(mouse)); final Coord clicked = Coord.fromVect(mapView.toWorldPos(mouse));
if (clicked.equals(plpos)) return false; if (clicked.equals(plpos)) return false;
final Tile t = mapView.plc.getLevel().getTile(clicked); final Tile t = mapView.plc.getLevel().getTile(clicked);
if (!t.isWalkable() || !t.isExplored()) return false; if (!t.isWalkable() || !t.isExplored()) return false;
mapView.plc.navigateTo(clicked); mapView.plc.navigateTo(clicked);
return true; return true;
} }
private boolean mouseWalk(Vect pos) private boolean mouseWalk(Vect pos)
{ {
if (isImmobile()) return false; if (isImmobile()) return false;
final Coord plpos = mapView.plc.getPlayer().getCoord(); final Coord plpos = mapView.plc.getPlayer().getCoord();
final Coord clicked = Coord.fromVect(mapView.toWorldPos(pos)); final Coord clicked = Coord.fromVect(mapView.toWorldPos(pos));
if (clicked.equals(plpos)) return false; if (clicked.equals(plpos)) return false;
final Polar p = Polar.fromCoord(clicked.x - plpos.x, clicked.y - plpos.y); final Polar p = Polar.fromCoord(clicked.x - plpos.x, clicked.y - plpos.y);
final int dir = Deg.roundTo90(p.getAngleDeg()) / 90; final int dir = Deg.roundTo90(p.getAngleDeg()) / 90;
switch (dir) { switch (dir) {
case 0: case 0:
return mapView.plc.tryGo(Moves.E); return mapView.plc.tryGo(Moves.E);
case 1: case 1:
return mapView.plc.tryGo(Moves.S); return mapView.plc.tryGo(Moves.S);
case 2: case 2:
return mapView.plc.tryGo(Moves.W); return mapView.plc.tryGo(Moves.W);
case 3: case 3:
return mapView.plc.tryGo(Moves.N); return mapView.plc.tryGo(Moves.N);
} }
return false; return false;
} }
@Override @Override
public void onStepFinished(EntityPlayer player) public void onStepFinished(EntityPlayer player)
{ {
if (isImmobile()) return; if (isImmobile()) return;
final Vect pos = App.input().getMousePos(); final Vect pos = App.input().getMousePos();
if (!pos.isInside(mapView)) return; if (!pos.isInside(mapView)) return;
if (App.input().isMouseButtonDown(LEFT)) { if (App.input().isMouseButtonDown(LEFT)) {
if (mouseWalk(pos)) return; if (mouseWalk(pos)) return;
if (mapView.plc.getPlayer().isMoving() && troToNav(pos)) return; if (mapView.plc.getPlayer().isMoving() && troToNav(pos)) return;
} }
} }
} }

@ -9,33 +9,34 @@ import mightypork.utils.math.constraints.vect.Vect;
public abstract class MapInteractionPlugin { public abstract class MapInteractionPlugin {
protected final MapView mapView; protected final MapView mapView;
public MapInteractionPlugin(MapView mapView) { public MapInteractionPlugin(MapView mapView)
{
super(); super();
this.mapView = mapView; this.mapView = mapView;
} }
protected PlayerFacade getPlayer() protected PlayerFacade getPlayer()
{ {
return mapView.plc.getPlayer(); return mapView.plc.getPlayer();
} }
protected boolean isImmobile() protected boolean isImmobile()
{ {
return getPlayer().isDead() || getWorld().isPaused(); return getPlayer().isDead() || getWorld().isPaused();
} }
protected World getWorld() protected World getWorld()
{ {
return WorldProvider.get().getWorld(); return WorldProvider.get().getWorld();
} }
public abstract boolean onClick(Vect mouse, int button, boolean down); public abstract boolean onClick(Vect mouse, int button, boolean down);
} }

@ -11,31 +11,32 @@ import mightypork.utils.math.constraints.rect.Rect;
public abstract class Item implements IonBundled { public abstract class Item implements IonBundled {
private final ItemModel model; private final ItemModel model;
private ItemRenderer renderer; private ItemRenderer renderer;
private int amount = 1; private int amount = 1;
private int uses = 1; private int uses = 1;
public Item(ItemModel model) { public Item(ItemModel model)
{
this.model = model; this.model = model;
} }
public final void render(Rect rect) public final void render(Rect rect)
{ {
if (renderer == null) { if (renderer == null) {
renderer = makeRenderer(); renderer = makeRenderer();
} }
renderer.render(rect); renderer.render(rect);
} }
protected abstract ItemRenderer makeRenderer(); protected abstract ItemRenderer makeRenderer();
@Override @Override
@Stub @Stub
public void save(IonDataBundle out) public void save(IonDataBundle out)
@ -43,8 +44,8 @@ public abstract class Item implements IonBundled {
out.put("c", amount); out.put("c", amount);
out.put("u", uses); out.put("u", uses);
} }
@Override @Override
@Stub @Stub
public void load(IonDataBundle in) public void load(IonDataBundle in)
@ -52,33 +53,33 @@ public abstract class Item implements IonBundled {
amount = in.get("c", amount); amount = in.get("c", amount);
uses = in.get("u", uses); uses = in.get("u", uses);
} }
public final ItemModel getModel() public final ItemModel getModel()
{ {
return model; return model;
} }
@Stub @Stub
protected int getMaxStackSize() protected int getMaxStackSize()
{ {
return isStackable() ? 65535 : 1; return isStackable() ? 65535 : 1;
} }
public boolean canStackWith(Item other) public boolean canStackWith(Item other)
{ {
return (getModel().id == other.getModel().id); return (getModel().id == other.getModel().id);
} }
protected abstract boolean isStackable(); protected abstract boolean isStackable();
/** /**
* Add another item to this item * Add another item to this item
* *
* @param added added item * @param added added item
* @return if items are compatible and the added item was completely moved * @return if items are compatible and the added item was completely moved
* to this item, returns true and the added item should be * to this item, returns true and the added item should be
@ -88,18 +89,18 @@ public abstract class Item implements IonBundled {
{ {
if (!canStackWith(added)) return false; if (!canStackWith(added)) return false;
if (added.isEmpty()) return true; if (added.isEmpty()) return true;
final int room = getMaxStackSize() - this.amount; final int room = getMaxStackSize() - this.amount;
final int avail = added.amount; final int avail = added.amount;
final int moved = Math.min(room, avail); final int moved = Math.min(room, avail);
this.amount += moved; this.amount += moved;
added.amount -= moved; added.amount -= moved;
return added.isEmpty(); return added.isEmpty();
} }
/** /**
* @return item amount in the stack * @return item amount in the stack
*/ */
@ -107,86 +108,86 @@ public abstract class Item implements IonBundled {
{ {
return Math.max(0, amount); return Math.max(0, amount);
} }
/** /**
* Consume one item. * Consume one item.
* *
* @return true if the item is fully consumed and should be removed. * @return true if the item is fully consumed and should be removed.
*/ */
public void consume() public void consume()
{ {
if (isEmpty()) throw new RuntimeException("Item is empty, cannot consume."); if (isEmpty()) throw new RuntimeException("Item is empty, cannot consume.");
amount--; amount--;
} }
public boolean isEmpty() public boolean isEmpty()
{ {
return getAmount() == 0; return getAmount() == 0;
} }
public Item split(int removed) public Item split(int removed)
{ {
if (isEmpty()) throw new RuntimeException("Item is empty, cannot split."); if (isEmpty()) throw new RuntimeException("Item is empty, cannot split.");
final int realRemoved = Math.min(removed, amount); final int realRemoved = Math.min(removed, amount);
final Item newItm = model.createItem(); final Item newItm = model.createItem();
newItm.uses = uses; newItm.uses = uses;
newItm.amount = realRemoved; newItm.amount = realRemoved;
this.amount -= realRemoved; this.amount -= realRemoved;
return newItm; return newItm;
} }
public abstract int getAttackPoints(); public abstract int getAttackPoints();
public abstract int getFoodPoints(); public abstract int getFoodPoints();
public abstract ItemType getType(); public abstract ItemType getType();
@Override @Override
public String toString() public String toString()
{ {
return Support.str(getClass()) + " x " + getAmount(); return Support.str(getClass()) + " x " + getAmount();
} }
public int getRemainingUses() public int getRemainingUses()
{ {
return uses; return uses;
} }
public abstract int getMaxUses(); public abstract int getMaxUses();
public void setRemainingUses(int uses) public void setRemainingUses(int uses)
{ {
this.uses = Calc.clamp(uses, 0, getMaxUses()); this.uses = Calc.clamp(uses, 0, getMaxUses());
} }
public void use() public void use()
{ {
if (uses > 0) uses--; if (uses > 0) uses--;
if (uses == 0) consume(); if (uses == 0) consume();
} }
public abstract boolean isDamageable(); public abstract boolean isDamageable();
public abstract String getVisualName(); public abstract String getVisualName();
@Stub @Stub
public boolean pickUp(PlayerFacade pl) public boolean pickUp(PlayerFacade pl)
{ {

@ -9,23 +9,24 @@ import mightypork.utils.math.Calc;
/** /**
* Item model (builder) * Item model (builder)
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public final class ItemModel { public final class ItemModel {
/** Model ID */ /** Model ID */
public final int id; public final int id;
public final Class<? extends Item> itemClass; public final Class<? extends Item> itemClass;
public ItemModel(int id, Class<? extends Item> item) { public ItemModel(int id, Class<? extends Item> item)
{
Items.register(id, this); Items.register(id, this);
this.id = id; this.id = id;
this.itemClass = item; this.itemClass = item;
} }
/** /**
* @return new item instance of this type * @return new item instance of this type
*/ */
@ -33,32 +34,32 @@ public final class ItemModel {
{ {
try { try {
final Item itm = itemClass.getConstructor(ItemModel.class).newInstance(this); final Item itm = itemClass.getConstructor(ItemModel.class).newInstance(this);
itm.setRemainingUses(itm.getMaxUses()); itm.setRemainingUses(itm.getMaxUses());
return itm; return itm;
} catch (final Exception e) { } catch (final Exception e) {
throw new RuntimeException("Could not instantiate an item.", e); throw new RuntimeException("Could not instantiate an item.", e);
} }
} }
public Item loadItem(IonDataBundle in) throws IOException public Item loadItem(IonDataBundle in) throws IOException
{ {
final Item t = createItem(); final Item t = createItem();
t.load(in); t.load(in);
return t; return t;
} }
public void saveItem(IonDataBundle out, Item item) throws IOException public void saveItem(IonDataBundle out, Item item) throws IOException
{ {
if (itemClass != item.getClass()) throw new RuntimeException("Item class mismatch."); if (itemClass != item.getClass()) throw new RuntimeException("Item class mismatch.");
item.save(out); item.save(out);
} }
public Item createItemDamaged(int minimalHealthPercent) public Item createItemDamaged(int minimalHealthPercent)
{ {
final Item item = createItem(); final Item item = createItem();

@ -5,15 +5,16 @@ import mightypork.utils.math.constraints.rect.Rect;
public abstract class ItemRenderer { public abstract class ItemRenderer {
protected final Item item; protected final Item item;
public ItemRenderer(Item item) { public ItemRenderer(Item item)
{
this.item = item; this.item = item;
} }
public abstract void render(Rect r); public abstract void render(Rect r);
} }

@ -8,7 +8,13 @@ import mightypork.rogue.world.item.impl.active.ItemHeartPiece;
import mightypork.rogue.world.item.impl.food.ItemCheese; import mightypork.rogue.world.item.impl.food.ItemCheese;
import mightypork.rogue.world.item.impl.food.ItemMeat; import mightypork.rogue.world.item.impl.food.ItemMeat;
import mightypork.rogue.world.item.impl.food.ItemSandwich; import mightypork.rogue.world.item.impl.food.ItemSandwich;
import mightypork.rogue.world.item.impl.weapons.*; import mightypork.rogue.world.item.impl.weapons.ItemAxe;
import mightypork.rogue.world.item.impl.weapons.ItemBone;
import mightypork.rogue.world.item.impl.weapons.ItemClub;
import mightypork.rogue.world.item.impl.weapons.ItemKnife;
import mightypork.rogue.world.item.impl.weapons.ItemRock;
import mightypork.rogue.world.item.impl.weapons.ItemSword;
import mightypork.rogue.world.item.impl.weapons.ItemTwig;
import mightypork.utils.ion.IonDataBundle; import mightypork.utils.ion.IonDataBundle;
import mightypork.utils.ion.IonInput; import mightypork.utils.ion.IonInput;
import mightypork.utils.ion.IonOutput; import mightypork.utils.ion.IonOutput;
@ -16,13 +22,13 @@ import mightypork.utils.ion.IonOutput;
/** /**
* Item registry * Item registry
* *
* @author Ondřej Hruška (MightyPork) * @author Ondřej Hruška (MightyPork)
*/ */
public final class Items { public final class Items {
private static final ItemModel[] items = new ItemModel[256]; private static final ItemModel[] items = new ItemModel[256];
public static final ItemModel MEAT = new ItemModel(1, ItemMeat.class); public static final ItemModel MEAT = new ItemModel(1, ItemMeat.class);
public static final ItemModel CHEESE = new ItemModel(2, ItemCheese.class); public static final ItemModel CHEESE = new ItemModel(2, ItemCheese.class);
public static final ItemModel BONE = new ItemModel(3, ItemBone.class); public static final ItemModel BONE = new ItemModel(3, ItemBone.class);
@ -34,54 +40,54 @@ public final class Items {
public static final ItemModel HEART_PIECE = new ItemModel(9, ItemHeartPiece.class); public static final ItemModel HEART_PIECE = new ItemModel(9, ItemHeartPiece.class);
public static final ItemModel TWIG = new ItemModel(10, ItemTwig.class); public static final ItemModel TWIG = new ItemModel(10, ItemTwig.class);
public static final ItemModel KNIFE = new ItemModel(11, ItemKnife.class); public static final ItemModel KNIFE = new ItemModel(11, ItemKnife.class);
public static void register(int id, ItemModel model) public static void register(int id, ItemModel model)
{ {
if (id < 0 || id >= items.length) { if (id < 0 || id >= items.length) {
throw new IllegalArgumentException("Item ID " + id + " is out of range."); throw new IllegalArgumentException("Item ID " + id + " is out of range.");
} }
if (items[id] != null) { if (items[id] != null) {
throw new IllegalArgumentException("Item ID " + id + " already in use."); throw new IllegalArgumentException("Item ID " + id + " already in use.");
} }
items[id] = model; items[id] = model;
} }
public static ItemModel get(int id) public static ItemModel get(int id)
{ {
final ItemModel m = items[id]; final ItemModel m = items[id];
if (m == null) { if (m == null) {
throw new IllegalArgumentException("No item with ID " + id + "."); throw new IllegalArgumentException("No item with ID " + id + ".");
} }
return m; return m;
} }
public static Item loadItem(IonInput in) throws IOException public static Item loadItem(IonInput in) throws IOException
{ {
final int id = in.readIntByte(); final int id = in.readIntByte();
final ItemModel model = get(id); final ItemModel model = get(id);
return model.loadItem(in.readBundle()); return model.loadItem(in.readBundle());
} }
public static void saveItem(IonOutput out, Item item) throws IOException public static void saveItem(IonOutput out, Item item) throws IOException
{ {
final ItemModel model = item.getModel(); final ItemModel model = item.getModel();
out.writeIntByte(model.id); out.writeIntByte(model.id);
final IonDataBundle ib = new IonDataBundle(); final IonDataBundle ib = new IonDataBundle();
model.saveItem(ib, item); model.saveItem(ib, item);
out.writeBundle(ib); out.writeBundle(ib);
} }
public static void loadItems(IonInput in, Collection<Item> items) throws IOException public static void loadItems(IonInput in, Collection<Item> items) throws IOException
{ {
items.clear(); items.clear();
@ -89,16 +95,16 @@ public final class Items {
items.add(loadItem(in)); items.add(loadItem(in));
} }
} }
public static void saveItems(IonOutput out, Collection<Item> items) throws IOException public static void saveItems(IonOutput out, Collection<Item> items) throws IOException
{ {
for (final Item entity : items) { for (final Item entity : items) {
out.startEntry(); out.startEntry();
saveItem(out, entity); saveItem(out, entity);
} }
out.endSequence(); out.endSequence();
} }
} }

@ -7,40 +7,41 @@ import mightypork.rogue.world.item.ItemType;
public abstract class ItemBaseFood extends Item { public abstract class ItemBaseFood extends Item {
public ItemBaseFood(ItemModel model) { public ItemBaseFood(ItemModel model)
{
super(model); super(model);
} }
@Override @Override
public boolean isStackable() public boolean isStackable()
{ {
return true; return true;
} }
@Override @Override
public final int getAttackPoints() public final int getAttackPoints()
{ {
return 0; return 0;
} }
@Override @Override
public final ItemType getType() public final ItemType getType()
{ {
return ItemType.FOOD; return ItemType.FOOD;
} }
@Override @Override
public boolean isDamageable() public boolean isDamageable()
{ {
return false; return false;
} }
@Override @Override
public int getMaxUses() public int getMaxUses()
{ {

@ -7,37 +7,38 @@ import mightypork.rogue.world.item.ItemType;
public abstract class ItemBaseWeapon extends Item { public abstract class ItemBaseWeapon extends Item {
public ItemBaseWeapon(ItemModel model) { public ItemBaseWeapon(ItemModel model)
{
super(model); super(model);
} }
@Override @Override
public boolean isStackable() public boolean isStackable()
{ {
return false; return false;
} }
@Override @Override
public final int getFoodPoints() public final int getFoodPoints()
{ {
return 0; return 0;
} }
@Override @Override
public final ItemType getType() public final ItemType getType()
{ {
return ItemType.WEAPON; return ItemType.WEAPON;
} }
@Override @Override
public boolean isDamageable() public boolean isDamageable()
{ {
return true; return true;
} }
} }

@ -11,77 +11,78 @@ import mightypork.rogue.world.item.render.QuadItemRenderer;
public class ItemHeartPiece extends Item { public class ItemHeartPiece extends Item {
public ItemHeartPiece(ItemModel model) { public ItemHeartPiece(ItemModel model)
{
super(model); super(model);
} }
@Override @Override
protected ItemRenderer makeRenderer() protected ItemRenderer makeRenderer()
{ {
return new QuadItemRenderer(this, Res.getTxQuad("item.heart")); return new QuadItemRenderer(this, Res.getTxQuad("item.heart"));
} }
@Override @Override
protected boolean isStackable() protected boolean isStackable()
{ {
return false; return false;
} }
@Override @Override
public int getAttackPoints() public int getAttackPoints()
{ {
return 0; return 0;
} }
@Override @Override
public int getFoodPoints() public int getFoodPoints()
{ {
return 0; return 0;
} }
@Override @Override
public ItemType getType() public ItemType getType()
{ {
return ItemType.ACTIVE; return ItemType.ACTIVE;
} }
@Override @Override
public int getMaxUses() public int getMaxUses()
{ {
return 1; return 1;
} }
@Override @Override
public boolean isDamageable() public boolean isDamageable()
{ {
return false; return false;
} }
@Override @Override
public String getVisualName() public String getVisualName()
{ {
return "Heart Piece"; return "Heart Piece";
} }
@Override @Override
public boolean pickUp(PlayerFacade pl) public boolean pickUp(PlayerFacade pl)
{ {
pl.setHealthMax(pl.getHealthMax() + 2); // two points / heart pl.setHealthMax(pl.getHealthMax() + 2); // two points / heart
pl.setHealth(pl.getHealthMax()); pl.setHealth(pl.getHealthMax());
pl.getWorld().getConsole().msgHeartPiece(); pl.getWorld().getConsole().msgHeartPiece();
return true; return true;
} }
} }

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

Loading…
Cancel
Save