cleanup.
This commit is contained in:
@@ -3,24 +3,24 @@ package mightypork.rogue;
|
||||
|
||||
/**
|
||||
* Application constants
|
||||
*
|
||||
*
|
||||
* @author Ondřej Hruška (MightyPork)
|
||||
*/
|
||||
public final class Const {
|
||||
|
||||
|
||||
// STRINGS
|
||||
public static final int VERSION = 6;
|
||||
|
||||
|
||||
public static final String APP_NAME = "Rogue: Savage Rats";
|
||||
public static final String TITLEBAR = APP_NAME + ", v" + VERSION;
|
||||
|
||||
|
||||
// AUDIO
|
||||
public static final int FPS_RENDER = 120; // max
|
||||
|
||||
|
||||
// INITIAL WINDOW SIZE
|
||||
public static final int WINDOW_W = 640;
|
||||
public static final int WINDOW_H = 480;
|
||||
|
||||
|
||||
/** Render dark in unknown area & skip invisible stuff */
|
||||
public static boolean RENDER_UFOG = true;
|
||||
}
|
||||
|
||||
@@ -4,101 +4,100 @@ package mightypork.rogue;
|
||||
import java.io.File;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import junk.BaseApp;
|
||||
import mightypork.gamecore.core.App;
|
||||
import mightypork.utils.files.OsUtils;
|
||||
|
||||
|
||||
public class Launcher {
|
||||
|
||||
|
||||
/**
|
||||
* Launcher
|
||||
*
|
||||
*
|
||||
* @param args
|
||||
*/
|
||||
public static void main(String[] args)
|
||||
{
|
||||
// System.out.println("argv = " + Arrays.toString(args)+"\n");
|
||||
|
||||
|
||||
Level llSyso = Level.FINER;
|
||||
final Level llFile = Level.ALL;
|
||||
|
||||
|
||||
File workdir = null;
|
||||
boolean logBus = false;
|
||||
|
||||
|
||||
try {
|
||||
boolean localWorkdir = false;
|
||||
String lwdDir = null;
|
||||
|
||||
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
final String arg = args[i];
|
||||
|
||||
|
||||
if (arg.equals("--workdir") || arg.equals("-w")) {
|
||||
localWorkdir = true;
|
||||
lwdDir = args[i + 1];
|
||||
i++;
|
||||
continue;
|
||||
|
||||
|
||||
} else if (arg.equals("--silent") || arg.equals("-s")) {
|
||||
llSyso = Level.OFF;
|
||||
continue;
|
||||
|
||||
|
||||
} else if (arg.equals("--warn") || arg.equals("-e")) {
|
||||
llSyso = Level.WARNING;
|
||||
continue;
|
||||
|
||||
|
||||
} else if (arg.equals("--verbose") || arg.equals("-v")) {
|
||||
llSyso = Level.ALL;
|
||||
continue;
|
||||
|
||||
|
||||
} else if (arg.equals("--help") || arg.equals("-h")) {
|
||||
printHelp();
|
||||
System.exit(0);
|
||||
|
||||
|
||||
} else if (arg.equals("--debug-bus")) {
|
||||
logBus = true;
|
||||
|
||||
|
||||
} else {
|
||||
System.err.println("Unknown argument: " + arg);
|
||||
printHelp();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!localWorkdir) {
|
||||
workdir = OsUtils.getHomeWorkDir(".rogue");
|
||||
} else {
|
||||
workdir = new File(lwdDir);
|
||||
}
|
||||
|
||||
|
||||
} catch (final Exception e) {
|
||||
System.out.println("Error parsing arguments:");
|
||||
e.printStackTrace();
|
||||
printHelp();
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
|
||||
final App app = new RogueApp();
|
||||
|
||||
|
||||
app.getInitOptions().setWorkdir(workdir);
|
||||
app.getInitOptions().setSigleInstance(true);
|
||||
|
||||
|
||||
app.getInitOptions().setLogLevel(llFile, llSyso);
|
||||
app.getInitOptions().setBusLogging(logBus);
|
||||
|
||||
|
||||
app.start();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private static void printHelp()
|
||||
{
|
||||
//@formatter:off
|
||||
System.out.println(
|
||||
"Arguments:\n" +
|
||||
"\t--workdir <path>, -w <path> .... specify working directory\n" +
|
||||
"\t--verbose, --debug, -v ......... print all messages\n" +
|
||||
"\t--silent, -s ................... print no messages\n" +
|
||||
"\t--warnings, -e ................. print only warning and error messages\n" +
|
||||
"\t--workdir <path>, -w <path> .... specify working directory\n" +
|
||||
"\t--verbose, --debug, -v ......... print all messages\n" +
|
||||
"\t--silent, -s ................... print no messages\n" +
|
||||
"\t--warnings, -e ................. print only warning and error messages\n" +
|
||||
"\t--help, -h ..................... show this help\n");
|
||||
//@formatter:on
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package mightypork.rogue;
|
||||
|
||||
|
||||
import junk.AppInitOptions;
|
||||
import junk.BaseApp;
|
||||
import mightypork.gamecore.backends.lwjgl.LwjglBackend;
|
||||
import mightypork.gamecore.backends.lwjgl.LwjglInputModule;
|
||||
import mightypork.gamecore.core.App;
|
||||
@@ -35,37 +34,38 @@ import mightypork.utils.logging.Log;
|
||||
|
||||
/**
|
||||
* Main class
|
||||
*
|
||||
*
|
||||
* @author Ondřej Hruška (MightyPork)
|
||||
*/
|
||||
public final class RogueApp extends App implements ViewportChangeListener {
|
||||
|
||||
public RogueApp() {
|
||||
|
||||
public RogueApp()
|
||||
{
|
||||
super(new LwjglBackend());
|
||||
|
||||
AppInitOptions opt = getInitOptions();
|
||||
|
||||
|
||||
final AppInitOptions opt = getInitOptions();
|
||||
|
||||
opt.addRoutes(new RogueRoutes());
|
||||
opt.addResources(new RogueResources());
|
||||
opt.addKeys(new RogueKeys());
|
||||
opt.addConfig(new RogueConfig());
|
||||
opt.setBusLogging(true);
|
||||
|
||||
|
||||
opt.setConfigFile("config.ini", "Rogue config file");
|
||||
opt.setLogOptions("logs", "runtime", 5, java.util.logging.Level.ALL);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected void registerIonizables()
|
||||
{
|
||||
super.registerIonizables();
|
||||
|
||||
|
||||
Ion.register(Level.class);
|
||||
Ion.register(Inventory.class);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
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 h = Config.getValue("display.height");
|
||||
final boolean fs = Config.getValue("display.fullscreen");
|
||||
|
||||
|
||||
gfx.setSize(w, h);
|
||||
gfx.setResizable(true);
|
||||
gfx.setFullscreen(fs);
|
||||
gfx.setTitle(Const.TITLEBAR);
|
||||
gfx.setTargetFps(Const.FPS_RENDER);
|
||||
|
||||
|
||||
gfx.createDisplay();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected void initScreens(ScreenRegistry screens)
|
||||
{
|
||||
super.initScreens(screens);
|
||||
|
||||
|
||||
/* game screen references world provider instance */
|
||||
WorldProvider.init(this);
|
||||
WorldProvider.setBaseDir(this);
|
||||
getEventBus().subscribe(new RogueStateManager(this));
|
||||
|
||||
|
||||
screens.addScreen("main_menu", new ScreenMainMenu(this));
|
||||
screens.addScreen("select_world", new ScreenSelectWorld(this));
|
||||
screens.addScreen("game", new ScreenGame(this));
|
||||
screens.addScreen("story", new ScreenStory(this));
|
||||
|
||||
|
||||
screens.addOverlay(new FpsOverlay(this));
|
||||
screens.addOverlay(new LoadingOverlay(this));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected void initInputSystem(LwjglInputModule input)
|
||||
{
|
||||
// this will work only with reusable events (such as requests)
|
||||
bindEventToKey(new FullscreenToggleRequest(), "global.fullscreen");
|
||||
bindEventToKey(new ScreenshotRequest(), "global.screenshot");
|
||||
|
||||
|
||||
bindEventToKey(new ShutdownEvent(), "global.quit");
|
||||
bindEventToKey(new ShutdownEvent(), "global.quit_force");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private void bindEventToKey(final BusEvent<?> event, String strokeName)
|
||||
{
|
||||
getInput().bindKey(Config.getKeyStroke(strokeName), Trigger.RISING, new Runnable() {
|
||||
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
@@ -126,14 +126,14 @@ public final class RogueApp extends App implements ViewportChangeListener {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected void postInit()
|
||||
{
|
||||
|
||||
|
||||
getEventBus().send(new MainLoopRequest(new Runnable() {
|
||||
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
@@ -146,31 +146,31 @@ public final class RogueApp extends App implements ViewportChangeListener {
|
||||
}
|
||||
}, false));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void onViewportChanged(ViewportChangeEvent event)
|
||||
{
|
||||
// save viewport size to config file
|
||||
final boolean fs = gfx().isFullscreen();
|
||||
|
||||
|
||||
Config.setValue("display.fullscreen", fs);
|
||||
|
||||
|
||||
if (!fs) {
|
||||
Config.setValue("display.width", gfx().getWidth());
|
||||
Config.setValue("display.height", gfx().getHeight());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void onScreenshotRequest()
|
||||
{
|
||||
// screenshot sound
|
||||
Res.getSoundEffect("gui.shutter").play(0.8);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected void writeLogHeader()
|
||||
{
|
||||
|
||||
@@ -5,15 +5,15 @@ import mightypork.utils.config.propmgr.PropertyManager;
|
||||
|
||||
|
||||
public class RogueConfig implements ConfigSetup {
|
||||
|
||||
|
||||
@Override
|
||||
public void addOptions(PropertyManager prop)
|
||||
{
|
||||
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.height", 768, "Initial height (remembers from last time)");
|
||||
|
||||
|
||||
prop.addBoolean("opt.show_story", true, "Show story on start-up.");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -2,23 +2,23 @@ package mightypork.rogue;
|
||||
|
||||
|
||||
public class RogueKeys implements KeySetup {
|
||||
|
||||
|
||||
@Override
|
||||
public void addKeys(KeyOpts keys)
|
||||
{
|
||||
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.screenshot", "F2", "Take screenshot (save into working directory)");
|
||||
keys.addKey("global.fullscreen", "F11", "Toggle fullscreen");
|
||||
keys.addKey("global.fps_meter", "F3", "Toggle FPS meter overlay");
|
||||
|
||||
|
||||
keys.addKey("general.close", "ESC", "Leave a dialog or screen");
|
||||
keys.addKey("general.cancel", "ESC", "\"Cancel\" option in dialogs");
|
||||
keys.addKey("general.confirm", "ENTER", "\"Confirm\" option in dialogs");
|
||||
keys.addKey("general.yes", "Y", "\"Yes\" option in dialogs");
|
||||
keys.addKey("general.no", "N", "\"No\" option in dialogs");
|
||||
|
||||
|
||||
keys.addKey("game.quit", "ESC", "Quit to menu");
|
||||
keys.addKey("game.save", "CTRL+S", "Save to 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.inventory", "I", "Toggle inventory view");
|
||||
keys.addKey("game.pause", "P", "Pause the game");
|
||||
|
||||
|
||||
keys.addKey("game.walk.up", "UP", "Walk north");
|
||||
keys.addKey("game.walk.down", "DOWN", "Walk south");
|
||||
keys.addKey("game.walk.left", "LEFT", "Walk west");
|
||||
keys.addKey("game.walk.right", "RIGHT", "Walk east");
|
||||
|
||||
|
||||
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.drop", "D", "Drop the selected item");
|
||||
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.backends.lwjgl.graphics.font.DeferredLwjglFont;
|
||||
import mightypork.gamecore.backends.lwjgl.graphics.font.LwjglFont;
|
||||
import mightypork.gamecore.graphics.fonts.DeferredFont;
|
||||
import mightypork.gamecore.graphics.fonts.FontRegistry;
|
||||
import mightypork.gamecore.graphics.fonts.Glyphs;
|
||||
@@ -16,72 +16,72 @@ import mightypork.utils.math.constraints.rect.Rect;
|
||||
|
||||
|
||||
public class RogueResources implements ResourceSetup {
|
||||
|
||||
|
||||
@Override
|
||||
public void addFonts(FontRegistry fonts)
|
||||
{
|
||||
DeferredFont font;
|
||||
|
||||
|
||||
//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("battlenet", font = new DeferredLwjglFont("/res/font/battlenet.ttf", Glyphs.basic, 16));
|
||||
fonts.addFont("press_start", font = new LwjglFont("/res/font/PressStart2P.ttf", Glyphs.basic, 16));
|
||||
|
||||
fonts.addFont("battlenet", font = new LwjglFont("/res/font/battlenet.ttf", Glyphs.basic, 16));
|
||||
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);
|
||||
|
||||
|
||||
// aliases
|
||||
fonts.addAlias("thick", "press_start");
|
||||
fonts.addAlias("thin", "battlenet");
|
||||
fonts.addAlias("tiny", "tinyutf");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void addSounds(SoundRegistry sounds)
|
||||
{
|
||||
|
||||
|
||||
//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.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.addEffect("gui.shutter", "/res/audio/effects/shutter.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.lose", "/res/audio/effects/lose.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.break", "/res/audio/effects/break.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("crate.open", "/res/audio/effects/crate.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("rat.sqeak1", "/res/audio/effects/mouse1.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.sqeak4", "/res/audio/effects/mouse4.ogg", 1, 1);
|
||||
sounds.addEffect("rat.sqeak5", "/res/audio/effects/mouse5.ogg", 1, 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void addTextures(TextureRegistry textures)
|
||||
{
|
||||
ITexture texture;
|
||||
QuadGrid grid;
|
||||
|
||||
|
||||
// gui
|
||||
texture = textures.addTexture("/res/img/gui.png", FilterMode.NEAREST, WrapMode.CLAMP);
|
||||
|
||||
|
||||
// small gui elements
|
||||
grid = texture.grid(32, 32);
|
||||
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.xp.on", grid.makeQuad(0, 1));
|
||||
textures.add("hud.xp.off", grid.makeQuad(1, 1));
|
||||
|
||||
|
||||
// nav
|
||||
grid = texture.grid(8, 8);
|
||||
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.hover", grid.makeQuad(5, 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.inventory", grid.makeQuad(1, 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.save", grid.makeQuad(7, 5));
|
||||
textures.add("nav.button.fg.load", grid.makeQuad(6, 5));
|
||||
|
||||
|
||||
textures.add("inv.slot.base", grid.makeQuad(0, 5));
|
||||
textures.add("inv.slot.selected", grid.makeQuad(1, 5));
|
||||
|
||||
|
||||
// sprites
|
||||
texture = textures.addTexture("/res/img/sprites.png", FilterMode.NEAREST, WrapMode.CLAMP);
|
||||
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.boss", grid.makeSheet(0, 3, 4, 1));
|
||||
textures.add("sprite.zzz", grid.makeQuad(0, 7)); // sleep thingy
|
||||
|
||||
|
||||
// logo
|
||||
texture = textures.addTexture("/res/img/logo.png", FilterMode.NEAREST, WrapMode.CLAMP);
|
||||
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("death2", grid.makeQuad(1, 2, 1.5, 1));
|
||||
textures.add("win", grid.makeQuad(2.5, 2, 1.5, 1));
|
||||
|
||||
|
||||
grid = texture.grid(8, 4);
|
||||
textures.add("story_1", grid.makeQuad(0, 2, 3, 1));
|
||||
textures.add("story_2", grid.makeQuad(3, 2, 3, 1));
|
||||
textures.add("story_3", grid.makeQuad(0, 3, 3, 1));
|
||||
|
||||
|
||||
// tiles
|
||||
texture = textures.addTexture("tiles", "/res/img/tiles.png", FilterMode.NEAREST, WrapMode.CLAMP);
|
||||
grid = texture.grid(8, 8);
|
||||
|
||||
|
||||
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.door.closed", grid.makeQuad(1, 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.passage", grid.makeSheet(3, 2, 4, 1));
|
||||
textures.add("tile.brick.stairs.up", grid.makeQuad(0, 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.open", grid.makeQuad(1, 4));
|
||||
|
||||
|
||||
// shadows
|
||||
textures.add("tile.shadow.n", grid.makeQuad(0, 7));
|
||||
textures.add("tile.shadow.s", grid.makeQuad(0, 7).flipY());
|
||||
textures.add("tile.shadow.w", grid.makeQuad(1, 7));
|
||||
textures.add("tile.shadow.e", grid.makeQuad(1, 7).flipX());
|
||||
|
||||
|
||||
textures.add("tile.shadow.nw", grid.makeQuad(2, 7));
|
||||
textures.add("tile.shadow.ne", grid.makeQuad(2, 7).flipX());
|
||||
textures.add("tile.shadow.sw", grid.makeQuad(2, 7).flipY());
|
||||
textures.add("tile.shadow.se", grid.makeQuad(2, 7).flipY().flipX());
|
||||
|
||||
|
||||
// unexplored fog
|
||||
textures.add("tile.ufog.n", grid.makeQuad(3, 7));
|
||||
textures.add("tile.ufog.s", grid.makeQuad(3, 7).flipY());
|
||||
textures.add("tile.ufog.w", grid.makeQuad(4, 7));
|
||||
textures.add("tile.ufog.e", grid.makeQuad(4, 7).flipX());
|
||||
|
||||
|
||||
textures.add("tile.ufog.nw", grid.makeQuad(5, 7));
|
||||
textures.add("tile.ufog.ne", grid.makeQuad(5, 7).flipX());
|
||||
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.full", grid.makeQuad(6, 7));
|
||||
|
||||
|
||||
texture = textures.addTexture("items", "/res/img/items.png", FilterMode.NEAREST, WrapMode.CLAMP);
|
||||
grid = texture.grid(8, 8);
|
||||
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.twig", grid.makeQuad(2, 1));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package mightypork.rogue;
|
||||
|
||||
|
||||
public class RogueRoutes implements RouteSetup {
|
||||
|
||||
|
||||
@Override
|
||||
public void addRoutes(RouteOpts routeOpts)
|
||||
{
|
||||
@@ -10,5 +10,5 @@ public class RogueRoutes implements RouteSetup {
|
||||
routeOpts.addPath("slot2", "saves/slot_2.ion");
|
||||
routeOpts.addPath("slot3", "saves/slot_3.ion");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -8,40 +8,40 @@ import mightypork.utils.logging.Log;
|
||||
|
||||
|
||||
public class RogueStateManager extends BusNode {
|
||||
|
||||
|
||||
public static enum RogueState
|
||||
{
|
||||
MAIN_MENU, SELECT_WORLD, PLAY_WORLD, EXIT, STORY
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void triggerAction(RogueState state, boolean fromDark)
|
||||
{
|
||||
switch (state) {
|
||||
case MAIN_MENU:
|
||||
App.bus().send(new CrossfadeRequest("main_menu", fromDark));
|
||||
break;
|
||||
|
||||
|
||||
case SELECT_WORLD:
|
||||
App.bus().send(new CrossfadeRequest("select_world", fromDark));
|
||||
break;
|
||||
|
||||
|
||||
case PLAY_WORLD:
|
||||
App.bus().send(new CrossfadeRequest("game", fromDark));
|
||||
break;
|
||||
|
||||
|
||||
case STORY:
|
||||
App.bus().send(new CrossfadeRequest("story", fromDark));
|
||||
break;
|
||||
|
||||
|
||||
case EXIT:
|
||||
App.bus().send(new CrossfadeRequest(null));
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
Log.w("Unknown action: " + state);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -8,30 +8,31 @@ import mightypork.utils.eventbus.events.flags.SingleReceiverEvent;
|
||||
|
||||
/**
|
||||
* Request to execute a given task in a loading overlay
|
||||
*
|
||||
*
|
||||
* @author Ondřej Hruška (MightyPork)
|
||||
*/
|
||||
@SingleReceiverEvent
|
||||
public class LoadingOverlayRequest extends BusEvent<LoadingOverlay> {
|
||||
|
||||
|
||||
private final String msg;
|
||||
private final Runnable task;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param msg task description
|
||||
* @param task task runnable
|
||||
*/
|
||||
public LoadingOverlayRequest(String msg, Runnable task) {
|
||||
public LoadingOverlayRequest(String msg, Runnable task)
|
||||
{
|
||||
this.task = task;
|
||||
this.msg = msg;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected void handleBy(LoadingOverlay handler)
|
||||
{
|
||||
handler.show(msg, task);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -8,27 +8,29 @@ import mightypork.utils.eventbus.BusEvent;
|
||||
|
||||
/**
|
||||
* Request for a game state change
|
||||
*
|
||||
*
|
||||
* @author Ondřej Hruška (MightyPork)
|
||||
*/
|
||||
public class RogueStateRequest extends BusEvent<RogueStateManager> {
|
||||
|
||||
|
||||
final private RogueState requested;
|
||||
private final boolean fromDark;
|
||||
|
||||
|
||||
public RogueStateRequest(RogueState requested) {
|
||||
|
||||
|
||||
public RogueStateRequest(RogueState requested)
|
||||
{
|
||||
this.requested = requested;
|
||||
this.fromDark = false;
|
||||
}
|
||||
|
||||
|
||||
public RogueStateRequest(RogueState requested, boolean fromDark) {
|
||||
|
||||
|
||||
public RogueStateRequest(RogueState requested, boolean fromDark)
|
||||
{
|
||||
this.requested = requested;
|
||||
this.fromDark = fromDark;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected void handleBy(RogueStateManager handler)
|
||||
{
|
||||
|
||||
@@ -2,7 +2,6 @@ package mightypork.rogue.screens;
|
||||
|
||||
|
||||
import mightypork.gamecore.core.App;
|
||||
import mightypork.gamecore.core.config.Config;
|
||||
import mightypork.gamecore.graphics.fonts.IFont;
|
||||
import mightypork.gamecore.gui.Action;
|
||||
import mightypork.gamecore.gui.components.painters.TextPainter;
|
||||
@@ -19,51 +18,52 @@ import mightypork.utils.string.StringProvider;
|
||||
|
||||
/**
|
||||
* FPS indicator overlay
|
||||
*
|
||||
*
|
||||
* @author Ondřej Hruška (MightyPork)
|
||||
*/
|
||||
public class FpsOverlay extends Overlay {
|
||||
|
||||
|
||||
TextPainter tp;
|
||||
|
||||
|
||||
public FpsOverlay() {
|
||||
|
||||
|
||||
|
||||
public FpsOverlay()
|
||||
{
|
||||
|
||||
/*
|
||||
* Toggle key: F3
|
||||
*/
|
||||
bindKey(App.cfg().getKeyStroke("global.fps_meter"), Trigger.RISING, new Action() {
|
||||
|
||||
|
||||
@Override
|
||||
public void execute()
|
||||
{
|
||||
setVisible(!isVisible());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
final IFont font = Res.getFont("thin");
|
||||
|
||||
|
||||
final Num h = root.height();
|
||||
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() {
|
||||
|
||||
|
||||
@Override
|
||||
public String getString()
|
||||
{
|
||||
return App.gfx().getFps() + " fps";
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
tp.setRect(constraint);
|
||||
tp.setShadow(RGB.BLACK_60, Vect.make(tp.height().div(8).round()));
|
||||
|
||||
|
||||
root.add(tp);
|
||||
|
||||
|
||||
setVisible(false); // initially hide.
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public int getZIndex()
|
||||
{
|
||||
|
||||
@@ -20,36 +20,36 @@ import mightypork.utils.string.StringProvider;
|
||||
/**
|
||||
* Overlay with blue background and loading-info text, that accompanies an async
|
||||
* task.
|
||||
*
|
||||
*
|
||||
* @author Ondřej Hruška (MightyPork)
|
||||
*/
|
||||
public class LoadingOverlay extends Overlay {
|
||||
|
||||
|
||||
private static final double T_IN = 0.5;
|
||||
private static final double T_OUT = 1;
|
||||
|
||||
|
||||
private final NumAnimated alpha = new NumAnimated(0);
|
||||
|
||||
|
||||
private final StringProvider msgStrProv = new StringProvider() {
|
||||
|
||||
|
||||
@Override
|
||||
public String getString()
|
||||
{
|
||||
return msg == null ? "" : msg;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private boolean busy;
|
||||
private String msg;
|
||||
private Runnable task;
|
||||
|
||||
|
||||
private final TimedTask tt = new TimedTask() {
|
||||
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
Support.runAsThread(new Runnable() {
|
||||
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
@@ -61,54 +61,55 @@ public class LoadingOverlay extends Overlay {
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public LoadingOverlay() {
|
||||
|
||||
|
||||
|
||||
public LoadingOverlay()
|
||||
{
|
||||
|
||||
final QuadPainter qp = new QuadPainter(PAL16.SEABLUE);
|
||||
qp.setRect(root);
|
||||
root.add(qp);
|
||||
|
||||
|
||||
updated.add(alpha);
|
||||
updated.add(tt);
|
||||
|
||||
|
||||
Rect textRect = root.shrink(Num.ZERO, root.height().perc(48));
|
||||
textRect = textRect.moveY(root.height().perc(-10));
|
||||
|
||||
|
||||
final TextPainter tp = new TextPainter(Res.getFont("thick"), AlignX.CENTER, RGB.WHITE, msgStrProv);
|
||||
|
||||
|
||||
tp.setRect(textRect);
|
||||
tp.setShadow(RGB.BLACK_60, tp.height().mul(1 / 8D).toVectXY());
|
||||
root.add(tp);
|
||||
|
||||
|
||||
setAlpha(alpha);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public int getZIndex()
|
||||
{
|
||||
return 10001; // not too high, so app can put something on top
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Show for a task
|
||||
*
|
||||
*
|
||||
* @param message task description
|
||||
* @param task task
|
||||
*/
|
||||
public void show(String message, Runnable task)
|
||||
{
|
||||
if (busy) throw new IllegalStateException("Loader is busy with another task.");
|
||||
|
||||
|
||||
this.msg = message;
|
||||
this.task = task;
|
||||
this.busy = true;
|
||||
|
||||
|
||||
alpha.setEasing(Easing.SINE_IN);
|
||||
alpha.fadeIn(T_IN);
|
||||
|
||||
|
||||
tt.start(T_IN);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,8 @@ import mightypork.utils.annotations.Stub;
|
||||
|
||||
|
||||
public class RogueScreen extends LayeredScreen implements ShutdownListener {
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
@Stub
|
||||
public void onShutdown(ShutdownEvent event)
|
||||
|
||||
@@ -11,28 +11,29 @@ import mightypork.utils.math.constraints.rect.Rect;
|
||||
|
||||
|
||||
public class HeartBar extends BaseComponent {
|
||||
|
||||
|
||||
private final TxQuad img_on;
|
||||
private final TxQuad img_off;
|
||||
private final TxQuad img_half;
|
||||
private final Num total;
|
||||
private final Num active;
|
||||
|
||||
|
||||
private final NumVar index = new NumVar(0);
|
||||
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();
|
||||
this.total = total;
|
||||
this.active = active;
|
||||
this.img_on = img_on;
|
||||
this.img_off = img_off;
|
||||
this.img_half = img_half;
|
||||
|
||||
|
||||
final Num h = height();
|
||||
final Num w = width();
|
||||
|
||||
|
||||
switch (align) {
|
||||
case LEFT:
|
||||
heart = leftEdge().growRight(h).moveX(index.mul(h));
|
||||
@@ -46,19 +47,19 @@ public class HeartBar extends BaseComponent {
|
||||
default:
|
||||
heart = null; // impossible
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected void renderComponent()
|
||||
{
|
||||
for (int i = 0; i < total.value(); i++) {
|
||||
index.setTo(i);
|
||||
|
||||
|
||||
final double rem = active.value() - i;
|
||||
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 {
|
||||
|
||||
|
||||
private final FlowColumnLayout leftFlow;
|
||||
private final FlowColumnLayout rightFlow;
|
||||
private final Rect paintHelper;
|
||||
|
||||
|
||||
private final TxQuad bg;
|
||||
|
||||
|
||||
public IngameNav() {
|
||||
|
||||
|
||||
public IngameNav()
|
||||
{
|
||||
this(null);
|
||||
}
|
||||
|
||||
|
||||
public IngameNav(RectBound context) {
|
||||
|
||||
|
||||
public IngameNav(RectBound context)
|
||||
{
|
||||
super(context);
|
||||
|
||||
|
||||
final Rect shr = this.shrink(height().perc(5));
|
||||
leftFlow = new FlowColumnLayout(context, shr.height(), AlignX.LEFT);
|
||||
rightFlow = new FlowColumnLayout(context, shr.height(), AlignX.RIGHT);
|
||||
|
||||
|
||||
leftFlow.setRect(shr);
|
||||
rightFlow.setRect(shr);
|
||||
attach(leftFlow);
|
||||
attach(rightFlow);
|
||||
|
||||
|
||||
paintHelper = leftEdge().growRight(height().mul(4));
|
||||
|
||||
|
||||
bg = Res.getTxQuad("nav.bg");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void addLeft(NavButton comp)
|
||||
{
|
||||
leftFlow.add(comp);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void addRight(NavButton comp)
|
||||
{
|
||||
rightFlow.add(comp);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
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++) {
|
||||
App.gfx().quad(paintHelper.moveX(paintHelper.width().value() * i), bg);
|
||||
}
|
||||
|
||||
|
||||
super.renderComponent();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -22,79 +22,80 @@ import mightypork.utils.math.constraints.rect.caching.RectCache;
|
||||
|
||||
/**
|
||||
* Button in the ingame nav
|
||||
*
|
||||
*
|
||||
* @author Ondřej Hruška (MightyPork)
|
||||
*/
|
||||
public class InvSlot extends ClickableComponent {
|
||||
|
||||
|
||||
private final TxQuad txBase, txSelected;
|
||||
|
||||
|
||||
protected boolean selected = false;
|
||||
protected int index;
|
||||
|
||||
|
||||
private final RectCache itemRect;
|
||||
private final RectCache uiRect;
|
||||
|
||||
|
||||
private final InvSlot[] slots;
|
||||
|
||||
|
||||
private final TextPainter rbTxP;
|
||||
private final TextPainter rtTxP;
|
||||
|
||||
|
||||
private final RectCache rbTxRect;
|
||||
private final RectCache rtTxRect;
|
||||
|
||||
|
||||
private final Rect usesRect;
|
||||
private final Num hAlpha = Num.make(0.7);
|
||||
|
||||
|
||||
public InvSlot(int index, InvSlot[] allSlots) {
|
||||
|
||||
|
||||
public InvSlot(int index, InvSlot[] allSlots)
|
||||
{
|
||||
super();
|
||||
this.txBase = Res.getTxQuad("inv.slot.base");
|
||||
this.txSelected = Res.getTxQuad("inv.slot.selected");
|
||||
|
||||
|
||||
this.index = index;
|
||||
this.slots = allSlots;
|
||||
|
||||
|
||||
this.uiRect = getRect().shrink(height().perc(16)).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));
|
||||
|
||||
|
||||
//@formatter:off
|
||||
this.rbTxRect = uiRect.bottomEdge()
|
||||
.moveY(uiRect.height().perc(1*(30/7D)))
|
||||
.growUp(uiRect.height().perc(30)).cached();
|
||||
//@formatter:on
|
||||
|
||||
|
||||
rbTxP = new TextPainter(Res.getFont("tiny"), AlignX.RIGHT, RGB.WHITE);
|
||||
rbTxP.setRect(rbTxRect);
|
||||
rbTxP.setShadow(RGB.BLACK_70, rbTxP.getRect().height().div(7).toVectXY());
|
||||
|
||||
|
||||
//@formatter:off
|
||||
this.rtTxRect = uiRect.topEdge()
|
||||
.growDown(uiRect.height().perc(30)).cached();
|
||||
//@formatter:on
|
||||
|
||||
|
||||
rtTxP = new TextPainter(Res.getFont("tiny"), AlignX.RIGHT, RGB.GREEN);
|
||||
rtTxP.setRect(rtTxRect);
|
||||
rtTxP.setShadow(RGB.BLACK_70, rtTxP.getRect().height().div(7).toVectXY());
|
||||
|
||||
|
||||
setAction(new Action() {
|
||||
|
||||
|
||||
@Override
|
||||
protected void execute()
|
||||
{
|
||||
for (final InvSlot sl : slots) {
|
||||
sl.selected = false;
|
||||
}
|
||||
|
||||
|
||||
selected = true;
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void updateLayout()
|
||||
{
|
||||
@@ -103,23 +104,23 @@ public class InvSlot extends ClickableComponent {
|
||||
rbTxRect.poll();
|
||||
rtTxRect.poll();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected void renderComponent()
|
||||
{
|
||||
TxQuad bg;
|
||||
|
||||
|
||||
if (selected) {
|
||||
bg = txSelected;
|
||||
} else {
|
||||
bg = txBase;
|
||||
}
|
||||
|
||||
|
||||
App.gfx().quad(this, bg);
|
||||
|
||||
|
||||
final PlayerFacade pl = WorldProvider.get().getPlayer();
|
||||
|
||||
|
||||
final Item itm = pl.getInventory().getItem(index);
|
||||
if (itm != null && !itm.isEmpty()) {
|
||||
itm.render(itemRect);
|
||||
@@ -128,39 +129,39 @@ public class InvSlot extends ClickableComponent {
|
||||
rbTxP.setColor(RGB.WHITE);
|
||||
rbTxP.render();
|
||||
}
|
||||
|
||||
|
||||
if (pl.getSelectedWeaponIndex() == index) {
|
||||
rbTxP.setText("*");
|
||||
rbTxP.setColor(RGB.YELLOW);
|
||||
rbTxP.render();
|
||||
}
|
||||
|
||||
|
||||
if (itm.getType() == ItemType.FOOD) {
|
||||
rtTxP.setText(Support.str(itm.getFoodPoints() / 2D));
|
||||
rtTxP.setColor(RGB.GREEN);
|
||||
rtTxP.render();
|
||||
} else if (itm.getType() == ItemType.WEAPON) {
|
||||
|
||||
|
||||
final int atk = itm.getAttackPoints();
|
||||
rtTxP.setText((atk >= 0 ? "+" : "") + atk);
|
||||
rtTxP.setColor(RGB.CYAN);
|
||||
rtTxP.render();
|
||||
}
|
||||
|
||||
|
||||
if (itm.isDamageable()) {
|
||||
Color.pushAlpha(hAlpha);
|
||||
|
||||
|
||||
App.gfx().quad(usesRect, RGB.BLACK);
|
||||
|
||||
|
||||
final double useRatio = (itm.getRemainingUses() / (double) itm.getMaxUses());
|
||||
|
||||
|
||||
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);
|
||||
|
||||
|
||||
Color.popAlpha();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -24,18 +24,18 @@ import mightypork.utils.math.constraints.num.Num;
|
||||
|
||||
|
||||
public class LayerAskSave extends FadingLayer {
|
||||
|
||||
|
||||
public Runnable task;
|
||||
|
||||
|
||||
private final ScreenGame gscreen;
|
||||
|
||||
|
||||
|
||||
|
||||
public void setTask(Runnable task)
|
||||
{
|
||||
this.task = task;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected void onHideFinished()
|
||||
{
|
||||
@@ -43,61 +43,61 @@ public class LayerAskSave extends FadingLayer {
|
||||
gscreen.setState(GScrState.WORLD); // go back..
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public LayerAskSave(final ScreenGame screen)
|
||||
{
|
||||
super(screen);
|
||||
this.gscreen = screen;
|
||||
|
||||
|
||||
// darker down to cover console.
|
||||
final QuadPainter qp = new QuadPainter(RGB.BLACK_80);
|
||||
qp.setRect(root);
|
||||
root.add(qp);
|
||||
|
||||
|
||||
final IFont thick_font = Res.getFont("thick");
|
||||
|
||||
|
||||
final RowLayout rl = new RowLayout(root, 2);
|
||||
rl.setRect(root.shrink(Num.ZERO, root.height().perc(40)).moveY(root.height().perc(-10)));
|
||||
root.add(rl);
|
||||
|
||||
|
||||
final TextPainter txp = new TextPainter(thick_font, AlignX.CENTER, RGB.WHITE, "Save the game?");
|
||||
rl.add(txp, 1);
|
||||
txp.setVPaddingPercent(25);
|
||||
|
||||
|
||||
final LinearLayout ll = new LinearLayout(root, AlignX.CENTER);
|
||||
rl.add(ll);
|
||||
|
||||
|
||||
final double vPadPerc = 20;
|
||||
|
||||
|
||||
final TextButton btn1 = new TextButton(thick_font, "Yes", ScreenGame.COLOR_BTN_GOOD);
|
||||
btn1.textPainter.setAlign(AlignX.RIGHT);
|
||||
btn1.textPainter.setVPaddingPercent(vPadPerc);
|
||||
ll.add(btn1);
|
||||
ll.gap(50);
|
||||
|
||||
|
||||
final TextButton btn2 = new TextButton(thick_font, "No", ScreenGame.COLOR_BTN_BAD);
|
||||
btn2.textPainter.setAlign(AlignX.CENTER);
|
||||
btn2.textPainter.setVPaddingPercent(vPadPerc);
|
||||
ll.add(btn2);
|
||||
ll.gap(50);
|
||||
|
||||
|
||||
final TextButton btn3 = new TextButton(thick_font, "Cancel", ScreenGame.COLOR_BTN_CANCEL);
|
||||
btn3.textPainter.setAlign(AlignX.LEFT);
|
||||
btn3.textPainter.setVPaddingPercent(vPadPerc);
|
||||
ll.add(btn3);
|
||||
|
||||
|
||||
final Action cancel = new Action() {
|
||||
|
||||
|
||||
@Override
|
||||
protected void execute()
|
||||
{
|
||||
hide();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
final Action save = new Action() {
|
||||
|
||||
|
||||
@Override
|
||||
protected void execute()
|
||||
{
|
||||
@@ -109,39 +109,39 @@ public class LayerAskSave extends FadingLayer {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
final Action discard = new Action() {
|
||||
|
||||
|
||||
@Override
|
||||
protected void execute()
|
||||
{
|
||||
if (task != null) task.run();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
btn1.setAction(save);
|
||||
btn2.setAction(discard);
|
||||
btn3.setAction(cancel);
|
||||
|
||||
Config cfg = App.cfg();
|
||||
|
||||
|
||||
final Config cfg = App.cfg();
|
||||
|
||||
bindKey(cfg.getKeyStroke("general.close"), Trigger.RISING, cancel);
|
||||
bindKey(cfg.getKeyStroke("general.cancel"), Trigger.RISING, cancel);
|
||||
|
||||
|
||||
bindKey(cfg.getKeyStroke("general.yes"), Trigger.RISING, save);
|
||||
bindKey(cfg.getKeyStroke("general.confirm"), Trigger.RISING, save);
|
||||
|
||||
|
||||
bindKey(cfg.getKeyStroke("general.no"), Trigger.RISING, discard);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public int getZIndex()
|
||||
{
|
||||
return 300;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void update(double delta)
|
||||
{
|
||||
|
||||
@@ -28,44 +28,45 @@ import mightypork.utils.math.constraints.num.Num;
|
||||
|
||||
|
||||
public class LayerDeath extends FadingLayer {
|
||||
|
||||
public LayerDeath(final ScreenGame screen) {
|
||||
|
||||
public LayerDeath(final ScreenGame screen)
|
||||
{
|
||||
super(screen);
|
||||
|
||||
|
||||
// darker down to cover console.
|
||||
final QuadPainter qp = new QuadPainter(RGB.BLACK_80);
|
||||
qp.setRect(root);
|
||||
root.add(qp);
|
||||
|
||||
|
||||
final IFont thick_font = Res.getFont("thick");
|
||||
|
||||
|
||||
final RowLayout rl = new RowLayout(root, 5);
|
||||
rl.setRect(root.shrink(Num.ZERO, root.height().perc(15)));
|
||||
root.add(rl);
|
||||
|
||||
|
||||
final TextPainter txp = new TextPainter(thick_font, AlignX.CENTER, RGB.YELLOW, "Rats won!");
|
||||
rl.add(txp, 1);
|
||||
txp.setVPaddingPercent(13);
|
||||
|
||||
|
||||
LinearLayout linl = new LinearLayout(root, AlignX.CENTER);
|
||||
linl.add(new ImagePainter(Res.getTxQuad("death2")));
|
||||
rl.add(linl, 3);
|
||||
|
||||
|
||||
linl = new LinearLayout(root, AlignX.CENTER);
|
||||
rl.add(linl);
|
||||
|
||||
|
||||
final TextButton btn1 = new TextButton(thick_font, "Retry", ScreenGame.COLOR_BTN_GOOD);
|
||||
btn1.textPainter.setVPaddingPercent(25);
|
||||
linl.add(btn1);
|
||||
|
||||
|
||||
linl.add(new LinearGap(50));
|
||||
|
||||
|
||||
final TextButton btn2 = new TextButton(thick_font, "Leave", ScreenGame.COLOR_BTN_BAD);
|
||||
btn2.textPainter.setVPaddingPercent(25);
|
||||
linl.add(btn2);
|
||||
|
||||
|
||||
final Action load = new Action() {
|
||||
|
||||
|
||||
@Override
|
||||
protected void execute()
|
||||
{
|
||||
@@ -77,31 +78,31 @@ public class LayerDeath extends FadingLayer {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
final Action quit = new Action() {
|
||||
|
||||
|
||||
@Override
|
||||
protected void execute()
|
||||
{
|
||||
App.bus().send(new RogueStateRequest(RogueState.MAIN_MENU));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
btn1.setAction(load);
|
||||
btn2.setAction(quit);
|
||||
|
||||
Config cfg = App.cfg();
|
||||
|
||||
|
||||
final Config cfg = App.cfg();
|
||||
|
||||
bindKey(cfg.getKeyStroke("game.load"), Trigger.RISING, load);
|
||||
bindKey(cfg.getKeyStroke("general.confirm"), Trigger.RISING, load);
|
||||
bindKey(cfg.getKeyStroke("general.close"), Trigger.RISING, quit);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public int getZIndex()
|
||||
{
|
||||
return 300;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -16,70 +16,71 @@ import mightypork.utils.string.StringProvider;
|
||||
|
||||
|
||||
public class LayerGameUi extends ScreenLayer {
|
||||
|
||||
|
||||
private final Num playerHealthTotal = new Num() {
|
||||
|
||||
|
||||
@Override
|
||||
public double value()
|
||||
{
|
||||
return WorldProvider.get().getPlayer().getHealthMax() / 2D;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private final Num playerHealthActive = new Num() {
|
||||
|
||||
|
||||
@Override
|
||||
public double value()
|
||||
{
|
||||
return WorldProvider.get().getPlayer().getHealth() / 2D;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
protected Minimap miniMap;
|
||||
|
||||
|
||||
private final ScreenGame gameScreen;
|
||||
|
||||
|
||||
public LayerGameUi(ScreenGame screen) {
|
||||
|
||||
|
||||
public LayerGameUi(ScreenGame screen)
|
||||
{
|
||||
super(screen);
|
||||
this.gameScreen = screen;
|
||||
|
||||
|
||||
buildNav();
|
||||
|
||||
|
||||
buildDisplays();
|
||||
|
||||
|
||||
buildMinimap();
|
||||
|
||||
|
||||
buildConsole();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private void buildConsole()
|
||||
{
|
||||
final Num rh = root.height();
|
||||
final Num rw = root.width();
|
||||
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 WorldConsoleRenderer wcr = new WorldConsoleRenderer(perRow);
|
||||
wcr.setRect(consoleRect);
|
||||
root.add(wcr);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private void buildMinimap()
|
||||
{
|
||||
miniMap = new Minimap();
|
||||
miniMap.setRect(root.shrink(root.width().perc(5), root.height().perc(15)));
|
||||
root.add(miniMap);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private void buildDisplays()
|
||||
{
|
||||
final Num h = root.height();
|
||||
|
||||
|
||||
//@formatter:off
|
||||
final HeartBar hearts = new HeartBar(
|
||||
playerHealthTotal,
|
||||
@@ -89,13 +90,13 @@ public class LayerGameUi extends ScreenLayer {
|
||||
Res.getTxQuad("hud.heart.off"),
|
||||
AlignX.LEFT);
|
||||
//@formatter:on
|
||||
|
||||
|
||||
final Rect hearts_box = root.shrink(h.perc(3)).topEdge().growDown(h.perc(6));
|
||||
hearts.setRect(hearts_box);
|
||||
root.add(hearts);
|
||||
|
||||
|
||||
final TextPainter levelText = new TextPainter(Res.getFont("tiny"), AlignX.RIGHT, RGB.WHITE, new StringProvider() {
|
||||
|
||||
|
||||
@Override
|
||||
public String getString()
|
||||
{
|
||||
@@ -103,62 +104,62 @@ public class LayerGameUi extends ScreenLayer {
|
||||
return (w.isPaused() ? "[P] " : "") + "Floor " + (1 + w.getPlayer().getLevelNumber());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
levelText.setRect(hearts_box.moveY(hearts_box.height().mul(1 / 7D)));
|
||||
root.add(levelText);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private void buildNav()
|
||||
{
|
||||
final IngameNav nav = new IngameNav();
|
||||
nav.setRect(root.bottomEdge().growUp(root.height().perc(12)));
|
||||
root.add(nav);
|
||||
|
||||
|
||||
NavButton btn;
|
||||
|
||||
|
||||
nav.addRight(btn = new NavButton(Res.getTxQuad("nav.button.fg.inventory")));
|
||||
btn.setAction(gameScreen.actionToggleInv);
|
||||
|
||||
|
||||
nav.addRight(btn = new NavButton(Res.getTxQuad("nav.button.fg.eat")));
|
||||
btn.setAction(gameScreen.actionEat);
|
||||
|
||||
|
||||
nav.addRight(btn = new NavButton(Res.getTxQuad("nav.button.fg.pause")));
|
||||
btn.setAction(gameScreen.actionTogglePause);
|
||||
|
||||
|
||||
// TODO actions
|
||||
//nav.addLeft(new NavButton(Res.txq("nav.button.fg.options")));
|
||||
//nav.addLeft(new NavButton(Res.txq("nav.button.fg.help")));
|
||||
|
||||
|
||||
nav.addLeft(btn = new NavButton(Res.getTxQuad("nav.button.fg.menu")));
|
||||
btn.setAction(gameScreen.actionMenu);
|
||||
|
||||
|
||||
nav.addLeft(btn = new NavButton(Res.getTxQuad("nav.button.fg.save")));
|
||||
btn.setAction(gameScreen.actionSave);
|
||||
|
||||
|
||||
nav.addLeft(btn = new NavButton(Res.getTxQuad("nav.button.fg.load")));
|
||||
btn.setAction(gameScreen.actionLoad);
|
||||
|
||||
|
||||
nav.addLeft(btn = new NavButton(Res.getTxQuad("nav.button.fg.map")));
|
||||
btn.setAction(gameScreen.actionToggleMinimap);
|
||||
|
||||
|
||||
nav.addLeft(btn = new NavButton(Res.getTxQuad("nav.button.fg.magnify")));
|
||||
btn.setAction(gameScreen.actionToggleZoom);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public int getZIndex()
|
||||
{
|
||||
return 101;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public int getEventPriority()
|
||||
{
|
||||
return 400;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -26,34 +26,34 @@ import mightypork.utils.string.StringProvider;
|
||||
|
||||
|
||||
public class LayerInv extends FadingLayer {
|
||||
|
||||
|
||||
private static final int SLOT_COUNT = 8;
|
||||
private static final int SLOT_ROW = 4;
|
||||
|
||||
|
||||
private final KeyStroke keyUse = App.cfg().getKeyStroke("game.inv.use");
|
||||
private final KeyStroke keyDrop = App.cfg().getKeyStroke("game.inv.drop");
|
||||
private final KeyStroke keyClose = App.cfg().getKeyStroke("general.close");
|
||||
|
||||
|
||||
private final StringProvider contextStrProv = new StringProvider() {
|
||||
|
||||
|
||||
@Override
|
||||
public String getString()
|
||||
{
|
||||
String s = keyClose + "-close";
|
||||
|
||||
|
||||
final int selected = getSelectedSlot();
|
||||
if (selected != -1) {
|
||||
|
||||
|
||||
final PlayerFacade pl = WorldProvider.get().getPlayer();
|
||||
|
||||
|
||||
final Item itm = pl.getInventory().getItem(selected);
|
||||
if (itm != null && !itm.isEmpty()) {
|
||||
s = keyDrop + "-drop," + s;
|
||||
|
||||
|
||||
if (itm.getType() == ItemType.FOOD) {
|
||||
s = keyUse + "-eat," + s;
|
||||
}
|
||||
|
||||
|
||||
if (itm.getType() == ItemType.WEAPON) {
|
||||
s = keyUse + "-equip," + s;
|
||||
}
|
||||
@@ -61,15 +61,15 @@ public class LayerInv extends FadingLayer {
|
||||
} else {
|
||||
s = "Click-select," + s;
|
||||
}
|
||||
|
||||
|
||||
return s;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private final InvSlot[] slots = new InvSlot[SLOT_COUNT];
|
||||
private ScreenGame gscreen;
|
||||
|
||||
|
||||
|
||||
|
||||
private int getSelectedSlot()
|
||||
{
|
||||
for (final InvSlot sl : slots) {
|
||||
@@ -80,20 +80,20 @@ public class LayerInv extends FadingLayer {
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private void selectSlot(int i)
|
||||
{
|
||||
for (final InvSlot sl : slots) {
|
||||
sl.selected = false;
|
||||
}
|
||||
|
||||
|
||||
if (i >= 0 && i < SLOT_COUNT) {
|
||||
slots[i].selected = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected void onHideFinished()
|
||||
{
|
||||
@@ -101,44 +101,45 @@ public class LayerInv extends FadingLayer {
|
||||
gscreen.actionToggleInv.run();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public LayerInv(final ScreenGame screen) {
|
||||
|
||||
|
||||
public LayerInv(final ScreenGame screen)
|
||||
{
|
||||
super(screen);
|
||||
this.gscreen = screen;
|
||||
|
||||
|
||||
final Rect fg = root.shrink(root.height().perc(15));
|
||||
|
||||
|
||||
// darker down to cover console.
|
||||
final QuadPainter qp = new QuadPainter(RGB.BLACK_30, RGB.BLACK_30, RGB.BLACK_90, RGB.BLACK_90);
|
||||
|
||||
|
||||
qp.setRect(root);
|
||||
root.add(qp);
|
||||
|
||||
|
||||
int pos = 0;
|
||||
|
||||
|
||||
final GridLayout gl = new GridLayout(fg, 10, 1);
|
||||
root.add(gl);
|
||||
|
||||
|
||||
final TextPainter txp = new TextPainter(Res.getFont("thick"), AlignX.CENTER, RGB.YELLOW, "Inventory");
|
||||
gl.put(txp, pos, 0, 1, 1);
|
||||
txp.setVPaddingPercent(5);
|
||||
pos += 1;
|
||||
|
||||
|
||||
final FlowColumnLayout row1 = new FlowColumnLayout(root, null, AlignX.LEFT);
|
||||
row1.setElementWidth(row1.height());
|
||||
final ConstraintLayout cl1 = new ConstraintLayout(root);
|
||||
row1.setRect(cl1.axisV().grow(cl1.height().mul(2), Num.ZERO));
|
||||
cl1.add(row1);
|
||||
|
||||
|
||||
gl.put(cl1, pos, 0, 4, 1);
|
||||
pos += 4;
|
||||
|
||||
|
||||
row1.add(slots[0] = new InvSlot(0, slots));
|
||||
row1.add(slots[1] = new InvSlot(1, slots));
|
||||
row1.add(slots[2] = new InvSlot(2, slots));
|
||||
row1.add(slots[3] = new InvSlot(3, slots));
|
||||
|
||||
|
||||
final FlowColumnLayout row2 = new FlowColumnLayout(root, null, AlignX.LEFT);
|
||||
row2.setElementWidth(row2.height());
|
||||
final ConstraintLayout cl2 = new ConstraintLayout(root);
|
||||
@@ -146,18 +147,18 @@ public class LayerInv extends FadingLayer {
|
||||
cl2.add(row2);
|
||||
gl.put(cl2, pos, 0, 4, 1);
|
||||
pos += 4;
|
||||
|
||||
|
||||
row2.add(slots[4] = new InvSlot(4, slots));
|
||||
row2.add(slots[5] = new InvSlot(5, slots));
|
||||
row2.add(slots[6] = new InvSlot(6, slots));
|
||||
row2.add(slots[7] = new InvSlot(7, slots));
|
||||
|
||||
|
||||
final TextPainter txp2 = new TextPainter(Res.getFont("thick"), AlignX.CENTER, RGB.WHITE, contextStrProv);
|
||||
gl.put(txp2, pos, 0, 1, 1);
|
||||
txp2.setVPaddingPercent(25);
|
||||
|
||||
|
||||
bindKey(keyClose, Trigger.RISING, new Runnable() {
|
||||
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
@@ -165,150 +166,150 @@ public class LayerInv extends FadingLayer {
|
||||
hide();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
bindKey(keyUse, Trigger.RISING, new Runnable() {
|
||||
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
if (!isEnabled()) return;
|
||||
|
||||
|
||||
if (WorldProvider.get().getPlayer().isDead()) return;
|
||||
|
||||
|
||||
useSelectedItem();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
bindKey(keyDrop, Trigger.RISING, new Runnable() {
|
||||
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
if (!isEnabled()) return;
|
||||
|
||||
|
||||
final int selected = getSelectedSlot();
|
||||
if (selected != -1) {
|
||||
WorldProvider.get().getPlayer().dropItem(selected);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
setupGridWalkKeys();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
protected void useSelectedItem()
|
||||
{
|
||||
final int selected = getSelectedSlot();
|
||||
if (selected != -1) {
|
||||
|
||||
|
||||
final World world = WorldProvider.get().getWorld();
|
||||
final PlayerFacade pl = WorldProvider.get().getPlayer();
|
||||
final Item itm = pl.getInventory().getItem(selected);
|
||||
|
||||
|
||||
if (itm != null && !itm.isEmpty()) {
|
||||
|
||||
|
||||
final ItemType type = itm.getType();
|
||||
|
||||
|
||||
if (type == ItemType.FOOD) {
|
||||
|
||||
|
||||
if (pl.eatFood(itm)) {
|
||||
pl.getInventory().clean();
|
||||
}
|
||||
|
||||
|
||||
} else if (type == ItemType.WEAPON) {
|
||||
|
||||
|
||||
if (pl.getSelectedWeaponIndex() == selected) {
|
||||
pl.selectWeapon(-1);
|
||||
} else {
|
||||
pl.selectWeapon(selected);
|
||||
}
|
||||
world.getConsole().msgEquipWeapon(pl.getSelectedWeapon());
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private void setupGridWalkKeys()
|
||||
{
|
||||
Config cfg = App.cfg();
|
||||
|
||||
final Config cfg = App.cfg();
|
||||
|
||||
bindKey(cfg.getKeyStroke("game.inv.move.left"), Trigger.RISING, new Runnable() {
|
||||
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
if (!isEnabled()) return;
|
||||
|
||||
|
||||
final int sel = getSelectedSlot();
|
||||
if (sel == -1) {
|
||||
selectSlot(0);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
selectSlot((SLOT_COUNT + (sel - 1)) % SLOT_COUNT);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
bindKey(cfg.getKeyStroke("game.inv.move.right"), Trigger.RISING, new Runnable() {
|
||||
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
if (!isEnabled()) return;
|
||||
|
||||
|
||||
final int sel = getSelectedSlot();
|
||||
if (sel == -1) {
|
||||
selectSlot(0);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
selectSlot((SLOT_COUNT + (sel + 1)) % SLOT_COUNT);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
bindKey(cfg.getKeyStroke("game.inv.move.up"), Trigger.RISING, new Runnable() {
|
||||
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
if (!isEnabled()) return;
|
||||
|
||||
|
||||
final int sel = getSelectedSlot();
|
||||
if (sel == -1) {
|
||||
selectSlot(0);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
selectSlot((SLOT_COUNT + (sel - SLOT_ROW)) % SLOT_COUNT);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
bindKey(cfg.getKeyStroke("game.inv.move.down"), Trigger.RISING, new Runnable() {
|
||||
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
if (!isEnabled()) return;
|
||||
|
||||
|
||||
final int sel = getSelectedSlot();
|
||||
if (sel == -1) {
|
||||
selectSlot(0);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
selectSlot((sel + SLOT_ROW) % SLOT_COUNT);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public int getZIndex()
|
||||
{
|
||||
return 300;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -10,40 +10,41 @@ import mightypork.utils.math.constraints.num.Num;
|
||||
|
||||
|
||||
public class LayerMapView extends ScreenLayer {
|
||||
|
||||
|
||||
protected final MapView map;
|
||||
|
||||
|
||||
public LayerMapView(Screen screen) {
|
||||
|
||||
|
||||
public LayerMapView(Screen screen)
|
||||
{
|
||||
super(screen);
|
||||
|
||||
|
||||
// render component
|
||||
|
||||
|
||||
map = new MapView();
|
||||
|
||||
|
||||
// map input plugins
|
||||
map.addPlugin(new MIPKeyboard(map));
|
||||
map.addPlugin(new MIPMouse(map));
|
||||
|
||||
|
||||
// size of lower navbar
|
||||
final Num lownav = root.height().perc(12);
|
||||
map.setRect(root.shrinkBottom(lownav));
|
||||
|
||||
|
||||
root.add(map);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public int getZIndex()
|
||||
{
|
||||
return 0; // stay down
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public int getEventPriority()
|
||||
{
|
||||
return 100;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ package mightypork.rogue.screens.game;
|
||||
import java.io.File;
|
||||
|
||||
import mightypork.gamecore.core.App;
|
||||
import mightypork.gamecore.core.config.Config;
|
||||
import mightypork.gamecore.graphics.fonts.IFont;
|
||||
import mightypork.gamecore.gui.Action;
|
||||
import mightypork.gamecore.gui.components.input.TextButton;
|
||||
@@ -25,68 +24,69 @@ import mightypork.utils.math.constraints.num.Num;
|
||||
|
||||
|
||||
public class LayerWin extends FadingLayer {
|
||||
|
||||
public LayerWin(final ScreenGame screen) {
|
||||
|
||||
public LayerWin(final ScreenGame screen)
|
||||
{
|
||||
super(screen);
|
||||
|
||||
|
||||
// darker down to cover console.
|
||||
final QuadPainter qp = new QuadPainter(RGB.BLACK_80);
|
||||
qp.setRect(root);
|
||||
root.add(qp);
|
||||
|
||||
|
||||
final IFont thick_font = Res.getFont("thick");
|
||||
|
||||
|
||||
final RowLayout rl = new RowLayout(root, 5);
|
||||
rl.setRect(root.shrink(Num.ZERO, root.height().perc(15)));
|
||||
root.add(rl);
|
||||
|
||||
|
||||
final TextPainter txp = new TextPainter(thick_font, AlignX.CENTER, RGB.YELLOW, "You won!");
|
||||
rl.add(txp, 1);
|
||||
txp.setVPaddingPercent(13);
|
||||
|
||||
|
||||
LinearLayout linl = new LinearLayout(root, AlignX.CENTER);
|
||||
linl.add(new ImagePainter(Res.getTxQuad("win")));
|
||||
rl.add(linl, 3);
|
||||
|
||||
|
||||
linl = new LinearLayout(root, AlignX.CENTER);
|
||||
rl.add(linl);
|
||||
|
||||
|
||||
final TextButton btn1 = new TextButton(thick_font, "Leave", ScreenGame.COLOR_BTN_GOOD);
|
||||
btn1.textPainter.setVPaddingPercent(25);
|
||||
linl.add(btn1);
|
||||
|
||||
|
||||
final Action quit = new Action() {
|
||||
|
||||
|
||||
@Override
|
||||
protected void execute()
|
||||
{
|
||||
// delete the save file
|
||||
final File f = WorldProvider.get().getWorld().getSaveFile();
|
||||
f.delete();
|
||||
|
||||
|
||||
App.bus().send(new RogueStateRequest(RogueState.MAIN_MENU));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
btn1.setAction(quit);
|
||||
|
||||
|
||||
bindKey(App.cfg().getKeyStroke("general.confirm"), Trigger.RISING, quit);
|
||||
bindKey(App.cfg().getKeyStroke("general.close"), Trigger.RISING, quit);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public int getZIndex()
|
||||
{
|
||||
return 300;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected void onShowFinished()
|
||||
{
|
||||
App.audio().fadeOutAllLoops();
|
||||
Res.getSoundEffect("game.win").play(1);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -9,28 +9,29 @@ import mightypork.gamecore.resources.Res;
|
||||
|
||||
/**
|
||||
* Button in the ingame nav
|
||||
*
|
||||
*
|
||||
* @author Ondřej Hruška (MightyPork)
|
||||
*/
|
||||
public class NavButton extends ClickableComponent {
|
||||
|
||||
|
||||
private final TxQuad base, hover, down, fg;
|
||||
|
||||
|
||||
public NavButton(TxQuad fg) {
|
||||
|
||||
|
||||
public NavButton(TxQuad fg)
|
||||
{
|
||||
super();
|
||||
this.base = Res.getTxQuad("nav.button.bg.base");
|
||||
this.hover = Res.getTxQuad("nav.button.bg.hover");
|
||||
this.down = Res.getTxQuad("nav.button.bg.down");
|
||||
this.fg = fg;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected void renderComponent()
|
||||
{
|
||||
TxQuad bg;
|
||||
|
||||
|
||||
if (btnDownOver) {
|
||||
bg = down;
|
||||
} else if (isMouseOver()) {
|
||||
@@ -38,11 +39,11 @@ public class NavButton extends ClickableComponent {
|
||||
} else {
|
||||
bg = base;
|
||||
}
|
||||
|
||||
|
||||
if (!isEnabled()) bg = base; // override effects
|
||||
|
||||
|
||||
App.gfx().quad(this, bg);
|
||||
App.gfx().quad(this, fg);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -25,34 +25,34 @@ import mightypork.utils.math.color.Color;
|
||||
|
||||
|
||||
public class ScreenGame extends RogueScreen implements PlayerDeathHandler, GameWinHandler {
|
||||
|
||||
|
||||
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_CANCEL = Color.fromHex(0xFFFB55);
|
||||
|
||||
|
||||
/**
|
||||
* Game gui state.
|
||||
*
|
||||
*
|
||||
* @author Ondřej Hruška (MightyPork)
|
||||
*/
|
||||
public enum GScrState
|
||||
{
|
||||
WORLD, INV, DEATH, WIN, GOTO_MENU, GOTO_QUIT;
|
||||
}
|
||||
|
||||
|
||||
private LayerInv invLayer;
|
||||
private LayerGameUi hudLayer;
|
||||
private LayerDeath deathLayer;
|
||||
private LayerWin winLayer;
|
||||
|
||||
|
||||
private LayerMapView worldLayer;
|
||||
|
||||
|
||||
private GScrState state = null;
|
||||
|
||||
|
||||
private final ActionGroup worldActions = new ActionGroup();
|
||||
|
||||
|
||||
public Action actionEat = new Action() {
|
||||
|
||||
|
||||
@Override
|
||||
public void execute()
|
||||
{
|
||||
@@ -61,50 +61,50 @@ public class ScreenGame extends RogueScreen implements PlayerDeathHandler, GameW
|
||||
pl.tryToEatSomeFood();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public Action actionToggleInv = new Action() {
|
||||
|
||||
|
||||
@Override
|
||||
public void execute()
|
||||
{
|
||||
setState(getState() == GScrState.INV ? GScrState.WORLD : GScrState.INV);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public Action actionToggleMinimap = new Action() {
|
||||
|
||||
|
||||
@Override
|
||||
public void execute()
|
||||
{
|
||||
hudLayer.miniMap.setVisible(!hudLayer.miniMap.isVisible());
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public Action actionTogglePause = new Action() {
|
||||
|
||||
|
||||
@Override
|
||||
public void execute()
|
||||
{
|
||||
App.bus().send(new WorldPauseRequest(PauseAction.TOGGLE));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public Action actionToggleZoom = new Action() {
|
||||
|
||||
|
||||
@Override
|
||||
public void execute()
|
||||
{
|
||||
worldLayer.map.toggleMag();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public Action actionSave = new Action() {
|
||||
|
||||
|
||||
@Override
|
||||
public void execute()
|
||||
{
|
||||
if (WorldProvider.get().getWorld().isGameOver()) return;
|
||||
|
||||
|
||||
try {
|
||||
WorldProvider.get().saveWorld();
|
||||
WorldProvider.get().getWorld().getConsole().msgWorldSaved();
|
||||
@@ -114,46 +114,46 @@ public class ScreenGame extends RogueScreen implements PlayerDeathHandler, GameW
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public Action actionLoad = new Action() {
|
||||
|
||||
|
||||
@Override
|
||||
public void execute()
|
||||
{
|
||||
if (WorldProvider.get().getWorld().isGameOver()) return;
|
||||
|
||||
|
||||
try {
|
||||
final File f = WorldProvider.get().getWorld().getSaveFile();
|
||||
WorldProvider.get().loadWorld(f);
|
||||
WorldProvider.get().getWorld().getConsole().msgReloaded();
|
||||
|
||||
|
||||
} catch (final Exception e) {
|
||||
Log.e("Could not load the world.", e);
|
||||
WorldProvider.get().getWorld().getConsole().msgLoadFailed();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public Action actionMenu = new Action() {
|
||||
|
||||
|
||||
@Override
|
||||
public void execute()
|
||||
{
|
||||
setState(GScrState.GOTO_MENU);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public Action actionQuit = new Action() {
|
||||
|
||||
|
||||
@Override
|
||||
public void execute()
|
||||
{
|
||||
setState(GScrState.GOTO_QUIT);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public Action actionDropLastPickedItem = new Action() {
|
||||
|
||||
|
||||
@Override
|
||||
public void execute()
|
||||
{
|
||||
@@ -163,62 +163,62 @@ public class ScreenGame extends RogueScreen implements PlayerDeathHandler, GameW
|
||||
}
|
||||
};
|
||||
private LayerAskSave askSaveLayer;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Set gui state (overlay)
|
||||
*
|
||||
*
|
||||
* @param nstate new state
|
||||
*/
|
||||
public void setState(GScrState nstate)
|
||||
{
|
||||
if (this.state == nstate) return;
|
||||
|
||||
|
||||
final boolean gameOver = WorldProvider.get().getWorld().isGameOver();
|
||||
|
||||
|
||||
if (nstate != GScrState.WORLD && state == GScrState.WORLD) { // leaving world.
|
||||
App.bus().send(new WorldPauseRequest(PauseAction.PAUSE));
|
||||
|
||||
|
||||
worldActions.setEnabled(false); // disable world actions
|
||||
}
|
||||
|
||||
|
||||
if (nstate != GScrState.DEATH) deathLayer.hide();
|
||||
if (nstate != GScrState.WIN) winLayer.hide();
|
||||
if (nstate != GScrState.INV) invLayer.hide();
|
||||
if (nstate != GScrState.GOTO_MENU && nstate != GScrState.GOTO_QUIT) askSaveLayer.hide();
|
||||
|
||||
|
||||
Runnable task;
|
||||
switch (nstate) {
|
||||
case WORLD:
|
||||
App.bus().send(new WorldPauseRequest(PauseAction.RESUME));
|
||||
worldActions.setEnabled(true);
|
||||
break;
|
||||
|
||||
|
||||
case INV:
|
||||
invLayer.show();
|
||||
break;
|
||||
|
||||
|
||||
case DEATH:
|
||||
deathLayer.show();
|
||||
break;
|
||||
|
||||
|
||||
case WIN:
|
||||
winLayer.show();
|
||||
break;
|
||||
|
||||
|
||||
case GOTO_MENU:
|
||||
case GOTO_QUIT:
|
||||
final RogueState goal = (nstate == GScrState.GOTO_MENU) ? RogueState.MAIN_MENU : RogueState.EXIT;
|
||||
|
||||
|
||||
task = new Runnable() {
|
||||
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
App.bus().send(new RogueStateRequest(goal));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
if (gameOver) {
|
||||
task.run();
|
||||
} else {
|
||||
@@ -226,68 +226,69 @@ public class ScreenGame extends RogueScreen implements PlayerDeathHandler, GameW
|
||||
askSaveLayer.show();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
this.state = nstate;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public GScrState getState()
|
||||
{
|
||||
return state;
|
||||
}
|
||||
|
||||
|
||||
public ScreenGame() {
|
||||
|
||||
|
||||
|
||||
public ScreenGame()
|
||||
{
|
||||
|
||||
addLayer(invLayer = new LayerInv(this));
|
||||
addLayer(deathLayer = new LayerDeath(this));
|
||||
addLayer(winLayer = new LayerWin(this));
|
||||
addLayer(hudLayer = new LayerGameUi(this));
|
||||
addLayer(worldLayer = new LayerMapView(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.inventory"), Trigger.RISING, actionToggleInv);
|
||||
bindKey(cfg.getKeyStroke("game.drop"), Trigger.RISING, actionDropLastPickedItem);
|
||||
bindKey(cfg.getKeyStroke("game.eat"), Trigger.RISING, actionEat);
|
||||
bindKey(cfg.getKeyStroke("game.minimap"), Trigger.RISING, actionToggleMinimap);
|
||||
bindKey(cfg.getKeyStroke("game.zoom"), Trigger.RISING, actionToggleZoom);
|
||||
|
||||
|
||||
bindKey(cfg.getKeyStroke("game.load"), Trigger.RISING, actionLoad);
|
||||
bindKey(cfg.getKeyStroke("game.save"), Trigger.RISING, actionSave);
|
||||
bindKey(cfg.getKeyStroke("game.quit"), Trigger.RISING, actionMenu);
|
||||
|
||||
|
||||
// bindKey(new KeyStroke(Keys.W), Edge.RISING, new Runnable() {
|
||||
//
|
||||
//
|
||||
// @Override
|
||||
// public void run()
|
||||
// {
|
||||
// setState(GScrState.WIN);
|
||||
// }
|
||||
// });
|
||||
|
||||
|
||||
// add as actions - enableables.
|
||||
worldActions.add(worldLayer);
|
||||
worldActions.add(hudLayer);
|
||||
|
||||
|
||||
worldActions.add(actionEat);
|
||||
worldActions.add(actionToggleMinimap);
|
||||
worldActions.add(actionTogglePause);
|
||||
worldActions.add(actionToggleZoom);
|
||||
|
||||
|
||||
worldActions.add(actionSave);
|
||||
worldActions.add(actionLoad);
|
||||
worldActions.add(actionMenu);
|
||||
worldActions.add(actionDropLastPickedItem);
|
||||
|
||||
|
||||
worldActions.setEnabled(true);
|
||||
|
||||
|
||||
// CHEAT - X-ray
|
||||
bindKey(cfg.getKeyStroke("game.cheat.xray"), Trigger.RISING, new Runnable() {
|
||||
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
@@ -295,32 +296,32 @@ public class ScreenGame extends RogueScreen implements PlayerDeathHandler, GameW
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected void onScreenEnter()
|
||||
{
|
||||
super.onScreenEnter();
|
||||
WorldProvider.get().setListening(true);
|
||||
|
||||
|
||||
setState(GScrState.WORLD);
|
||||
hideAllPopups();
|
||||
|
||||
|
||||
App.audio().fadeOutAllLoops();
|
||||
Res.getSoundLoop("music.dungeon").fadeIn();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected void onScreenLeave()
|
||||
{
|
||||
super.onScreenLeave();
|
||||
WorldProvider.get().setListening(false);
|
||||
|
||||
|
||||
Res.getSoundLoop("music.dungeon").fadeOut();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private void hideAllPopups()
|
||||
{
|
||||
invLayer.hideImmediate();
|
||||
@@ -328,29 +329,29 @@ public class ScreenGame extends RogueScreen implements PlayerDeathHandler, GameW
|
||||
winLayer.hideImmediate();
|
||||
askSaveLayer.hideImmediate();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void onPlayerKilled()
|
||||
{
|
||||
setState(GScrState.DEATH);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void onGameWon()
|
||||
{
|
||||
setState(GScrState.WIN);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
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();
|
||||
if (pl.isDead()) return;
|
||||
|
||||
|
||||
actionQuit.run();
|
||||
event.consume();
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@ package mightypork.rogue.screens.menu;
|
||||
|
||||
|
||||
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.gui.Action;
|
||||
import mightypork.gamecore.gui.components.input.TextButton;
|
||||
@@ -27,51 +25,51 @@ import mightypork.utils.math.constraints.rect.Rect;
|
||||
|
||||
/**
|
||||
* Main menu screen
|
||||
*
|
||||
*
|
||||
* @author Ondřej Hruška (MightyPork)
|
||||
*/
|
||||
public class ScreenMainMenu extends RogueScreen {
|
||||
|
||||
|
||||
/**
|
||||
* The layer
|
||||
*
|
||||
*
|
||||
* @author Ondřej Hruška (MightyPork)
|
||||
*/
|
||||
class MenuLayer extends ScreenLayer {
|
||||
|
||||
|
||||
public MenuLayer(Screen screen)
|
||||
{
|
||||
super(screen);
|
||||
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private void init()
|
||||
{
|
||||
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);
|
||||
bg.setRect(root);
|
||||
root.add(bg);
|
||||
|
||||
|
||||
final RowLayout rows = new RowLayout(menuBox, 13);
|
||||
rows.enableCaching(true);
|
||||
root.add(rows);
|
||||
|
||||
|
||||
final LinearLayout linlayout = new LinearLayout(root, AlignX.CENTER);
|
||||
linlayout.add(new ImagePainter(Res.getTxQuad("logo")));
|
||||
rows.add(linlayout, 4);
|
||||
rows.skip(1);
|
||||
|
||||
|
||||
TextButton btn;
|
||||
|
||||
|
||||
final IFont btnFont = Res.getFont("thick");
|
||||
|
||||
|
||||
// world button
|
||||
btn = new TextButton(btnFont, "Play", PAL16.SLIMEGREEN);
|
||||
btn.setAction(new Action() {
|
||||
|
||||
|
||||
@Override
|
||||
protected void execute()
|
||||
{
|
||||
@@ -80,10 +78,10 @@ public class ScreenMainMenu extends RogueScreen {
|
||||
});
|
||||
rows.add(btn, 2);
|
||||
rows.skip(1);
|
||||
|
||||
|
||||
btn = new TextButton(btnFont, "Story", PAL16.CLOUDBLUE);
|
||||
btn.setAction(new Action() {
|
||||
|
||||
|
||||
@Override
|
||||
protected void execute()
|
||||
{
|
||||
@@ -92,11 +90,11 @@ public class ScreenMainMenu extends RogueScreen {
|
||||
});
|
||||
rows.add(btn, 2);
|
||||
rows.skip(1);
|
||||
|
||||
|
||||
// quit button
|
||||
btn = new TextButton(btnFont, "Exit", PAL16.BLOODRED);
|
||||
btn.setAction(new Action() {
|
||||
|
||||
|
||||
@Override
|
||||
protected void execute()
|
||||
{
|
||||
@@ -104,9 +102,9 @@ public class ScreenMainMenu extends RogueScreen {
|
||||
}
|
||||
});
|
||||
rows.add(btn, 2);
|
||||
|
||||
|
||||
bindKey(App.cfg().getKeyStroke("general.close"), Trigger.RISING, new Runnable() {
|
||||
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
@@ -114,28 +112,28 @@ public class ScreenMainMenu extends RogueScreen {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public int getZIndex()
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public ScreenMainMenu()
|
||||
{
|
||||
addLayer(new MenuLayer(this));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected void onScreenEnter()
|
||||
{
|
||||
super.onScreenEnter();
|
||||
|
||||
|
||||
App.audio().fadeOutAllLoops();
|
||||
Res.getSoundLoop("music.menu").fadeIn();
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@ package mightypork.rogue.screens.select_world;
|
||||
|
||||
|
||||
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.painters.QuadPainter;
|
||||
import mightypork.gamecore.gui.components.painters.TextPainter;
|
||||
@@ -14,6 +12,7 @@ import mightypork.gamecore.resources.Res;
|
||||
import mightypork.rogue.RogueStateManager.RogueState;
|
||||
import mightypork.rogue.events.RogueStateRequest;
|
||||
import mightypork.rogue.screens.RogueScreen;
|
||||
import mightypork.utils.files.WorkDir;
|
||||
import mightypork.utils.logging.Log;
|
||||
import mightypork.utils.math.AlignX;
|
||||
import mightypork.utils.math.color.Color;
|
||||
@@ -24,59 +23,61 @@ import mightypork.utils.math.constraints.rect.Rect;
|
||||
|
||||
/**
|
||||
* Main menu screen
|
||||
*
|
||||
*
|
||||
* @author Ondřej Hruška (MightyPork)
|
||||
*/
|
||||
public class ScreenSelectWorld extends RogueScreen {
|
||||
|
||||
public ScreenSelectWorld() {
|
||||
|
||||
public ScreenSelectWorld()
|
||||
{
|
||||
addLayer(new WorldsLayer(this));
|
||||
}
|
||||
|
||||
|
||||
class WorldsLayer extends ScreenLayer {
|
||||
|
||||
|
||||
private WorldSlot slot1;
|
||||
private WorldSlot slot2;
|
||||
private WorldSlot slot3;
|
||||
|
||||
|
||||
public WorldsLayer(Screen screen) {
|
||||
|
||||
|
||||
public WorldsLayer(Screen screen)
|
||||
{
|
||||
super(screen);
|
||||
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private void init()
|
||||
{
|
||||
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);
|
||||
bg.setRect(root);
|
||||
root.add(bg);
|
||||
|
||||
|
||||
final RowLayout rows = new RowLayout(menuBox, 4);
|
||||
rows.enableCaching(true);
|
||||
root.add(rows);
|
||||
|
||||
|
||||
TextPainter tp;
|
||||
|
||||
|
||||
rows.add(tp = new TextPainter(Res.getFont("thick"), AlignX.CENTER, RGB.YELLOW, "Save slot:"));
|
||||
tp.setVPaddingPercent(20);
|
||||
tp.setShadow(RGB.BLACK_50, tp.height().mul(0.6 / 8D).toVectXY());
|
||||
|
||||
|
||||
slot1 = new WorldSlot(WorkDir.getFile("slot1"));
|
||||
rows.add(slot1);
|
||||
|
||||
|
||||
slot2 = new WorldSlot(WorkDir.getFile("slot2"));
|
||||
rows.add(slot2);
|
||||
|
||||
|
||||
slot3 = new WorldSlot(WorkDir.getFile("slot3"));
|
||||
rows.add(slot3);
|
||||
|
||||
|
||||
// escape to quitn from here
|
||||
bindKey(App.cfg().getKeyStroke("general.close"), Trigger.RISING, new Runnable() {
|
||||
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
@@ -84,20 +85,20 @@ public class ScreenSelectWorld extends RogueScreen {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public int getZIndex()
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected void onScreenEnter()
|
||||
{
|
||||
super.onScreenEnter();
|
||||
|
||||
|
||||
Log.f3("Refreshing save slots");
|
||||
slot1.refresh();
|
||||
slot2.refresh();
|
||||
|
||||
@@ -26,120 +26,121 @@ import mightypork.utils.string.StringProvider;
|
||||
|
||||
|
||||
public class WorldSlot extends ConstraintLayout {
|
||||
|
||||
|
||||
private final StringProvider lblStrp = new StringProvider() {
|
||||
|
||||
|
||||
@Override
|
||||
public String getString()
|
||||
{
|
||||
return label;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private File file;
|
||||
private String label;
|
||||
|
||||
|
||||
private IonDataBundle worldBundle;
|
||||
|
||||
|
||||
private TextButton loadBtn;
|
||||
|
||||
|
||||
private TextButton delBtn;
|
||||
|
||||
|
||||
public WorldSlot(File worldFile) {
|
||||
|
||||
|
||||
|
||||
public WorldSlot(File worldFile)
|
||||
{
|
||||
|
||||
this.file = worldFile;
|
||||
|
||||
|
||||
final Rect innerRect = shrink(height().perc(5));
|
||||
|
||||
|
||||
final QuadPainter qp = new QuadPainter(RGB.BLACK.withAlpha(new Num() {
|
||||
|
||||
|
||||
@Override
|
||||
public double value()
|
||||
{
|
||||
return isMouseOver() ? 0.15 : 0.1;
|
||||
}
|
||||
}));
|
||||
|
||||
|
||||
qp.setRect(innerRect);
|
||||
add(qp);
|
||||
|
||||
|
||||
final GridLayout gridl = new GridLayout(1, 8);
|
||||
final Num shrinkH = width().perc(8);
|
||||
final Num shrinkV = height().perc(10);
|
||||
gridl.setRect(innerRect.shrink(shrinkH, shrinkH, shrinkV, shrinkV.half()));
|
||||
add(gridl);
|
||||
|
||||
|
||||
final IFont font = Res.getFont("thick");
|
||||
|
||||
|
||||
gridl.put(loadBtn = new TextButton(font, "", RGB.WHITE), 0, 0, 1, 7);
|
||||
loadBtn.textPainter.setVPaddingPercent(20);
|
||||
loadBtn.textPainter.setAlign(AlignX.LEFT);
|
||||
loadBtn.textPainter.setText(lblStrp);
|
||||
loadBtn.disableHoverEffect();
|
||||
|
||||
|
||||
loadBtn.setAction(new Action() {
|
||||
|
||||
|
||||
@Override
|
||||
protected void execute()
|
||||
{
|
||||
String msg;
|
||||
|
||||
|
||||
if (worldBundle != null) {
|
||||
msg = "Loading world...";
|
||||
} else {
|
||||
msg = "Creating world...";
|
||||
}
|
||||
|
||||
|
||||
App.bus().send(new LoadingOverlayRequest(msg, new Runnable() {
|
||||
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
World w;
|
||||
|
||||
|
||||
if (worldBundle == null) {
|
||||
|
||||
|
||||
try {
|
||||
w = WorldProvider.get().createWorld(Double.doubleToLongBits(Math.random()));
|
||||
w.setSaveFile(file);
|
||||
|
||||
|
||||
WorldProvider.get().saveWorld();
|
||||
|
||||
|
||||
App.bus().send(new ScreenRequest("game"));
|
||||
|
||||
|
||||
} catch (final Exception t) {
|
||||
Log.e("Could not create & save the world.", t);
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
|
||||
try {
|
||||
w = new World();
|
||||
w.setSaveFile(file);
|
||||
w.load((IonDataBundle) worldBundle.get("world"));
|
||||
WorldProvider.get().setWorld(w);
|
||||
|
||||
|
||||
App.bus().send(new ScreenRequest("game"));
|
||||
|
||||
|
||||
} catch (final Exception e) {
|
||||
Log.e("Could not load the world.", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}));
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
gridl.put(delBtn = new TextButton(font, "X", RGB.RED), 0, 7, 1, 1);
|
||||
delBtn.textPainter.setVPaddingPercent(20);
|
||||
delBtn.textPainter.setAlign(AlignX.RIGHT);
|
||||
delBtn.disableHoverEffect();
|
||||
|
||||
|
||||
delBtn.setAction(new Action() {
|
||||
|
||||
|
||||
@Override
|
||||
protected void execute()
|
||||
{
|
||||
@@ -148,30 +149,30 @@ public class WorldSlot extends ConstraintLayout {
|
||||
refresh();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
refresh();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void refresh()
|
||||
{
|
||||
delBtn.setVisible(false);
|
||||
delBtn.setEnabled(false);
|
||||
|
||||
|
||||
if (!file.exists()) {
|
||||
label = "<empty>";
|
||||
worldBundle = null;
|
||||
} else {
|
||||
|
||||
|
||||
delBtn.setVisible(true);
|
||||
delBtn.setEnabled(true);
|
||||
|
||||
|
||||
try {
|
||||
worldBundle = Ion.fromFile(file);
|
||||
final int lvl = worldBundle.get("level", -1);
|
||||
|
||||
|
||||
if (lvl == -1) throw new RuntimeException("Invalid save format."); // let the catch block handle it
|
||||
|
||||
|
||||
label = "Level " + (lvl + 1);
|
||||
} catch (final Exception 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.config.Config;
|
||||
import mightypork.gamecore.gui.Action;
|
||||
import mightypork.gamecore.gui.components.layout.RowLayout;
|
||||
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 {
|
||||
|
||||
|
||||
private class LayerSlide extends ScreenLayer {
|
||||
|
||||
|
||||
private final TextPainter tp1;
|
||||
private final TextPainter tp2;
|
||||
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 tx2alpha = new NumAnimated(0, Easing.BOUNCE_OUT, 1);
|
||||
private final NumAnimated txProceedAlpha = new NumAnimated(0, Easing.CIRC_OUT, 1);
|
||||
|
||||
|
||||
private String nextImg, nextT1, nextT2;
|
||||
|
||||
|
||||
private final TimedTask ttNextSlide = new TimedTask() {
|
||||
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
img.setTxQuad(Res.getTxQuad(nextImg));
|
||||
if (nextT1 != null) tp1.setText(nextT1);
|
||||
if (nextT2 != null) tp2.setText(nextT2);
|
||||
|
||||
|
||||
tx1alpha.setTo(0);
|
||||
tx2alpha.setTo(0);
|
||||
txProceedAlpha.setTo(0);
|
||||
|
||||
|
||||
layerAlpha.setTo(0);
|
||||
layerAlpha.fadeIn();
|
||||
ttText1.start(1.5);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private final TimedTask ttText1 = new TimedTask() {
|
||||
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
@@ -76,9 +75,9 @@ public class ScreenStory extends RogueScreen implements MouseButtonHandler {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private final TimedTask ttText2 = new TimedTask() {
|
||||
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
@@ -90,106 +89,108 @@ public class ScreenStory extends RogueScreen implements MouseButtonHandler {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private final TimedTask ttFinish = new TimedTask() {
|
||||
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
txProceedAlpha.fadeIn();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private final Color textColor = Color.fromHex(0x7ad8ff);
|
||||
|
||||
|
||||
public LayerSlide(Screen screen) {
|
||||
|
||||
|
||||
public LayerSlide(Screen 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.setVPaddingPercent(5);
|
||||
root.add(help);
|
||||
|
||||
|
||||
final Rect contentRect = root.shrink(Num.ZERO, Num.ZERO, root.height().perc(2), root.height().perc(6));
|
||||
final RowLayout rl = new RowLayout(root, 9);
|
||||
rl.setRect(contentRect);
|
||||
root.add(rl);
|
||||
|
||||
|
||||
final LinearLayout ll = new LinearLayout(root, AlignX.CENTER);
|
||||
rl.add(ll, 7);
|
||||
img = new ImagePainter(Res.getTxQuad("story_1"));
|
||||
ll.add(img);
|
||||
|
||||
|
||||
tp1 = new TextPainter(Res.getFont("tiny"), AlignX.CENTER, textColor.withAlpha(tx1alpha), "");
|
||||
rl.add(tp1);
|
||||
tp1.setVPaddingPercent(19);
|
||||
|
||||
|
||||
tp2 = new TextPainter(Res.getFont("tiny"), AlignX.CENTER, textColor.withAlpha(tx2alpha), "");
|
||||
rl.add(tp2);
|
||||
tp2.setVPaddingPercent(19);
|
||||
|
||||
|
||||
updated.add(layerAlpha);
|
||||
updated.add(txProceedAlpha);
|
||||
updated.add(tx1alpha);
|
||||
updated.add(tx2alpha);
|
||||
|
||||
|
||||
updated.add(ttText1);
|
||||
updated.add(ttText2);
|
||||
updated.add(ttFinish);
|
||||
updated.add(ttNextSlide);
|
||||
|
||||
|
||||
setAlpha(layerAlpha);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public int getZIndex()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void showSlide(String image, String text1, String text2)
|
||||
{
|
||||
ttFinish.stop();
|
||||
ttNextSlide.stop();
|
||||
ttText1.stop();
|
||||
ttText2.stop();
|
||||
|
||||
|
||||
this.nextImg = image;
|
||||
this.nextT1 = text1;
|
||||
this.nextT2 = text2;
|
||||
|
||||
|
||||
layerAlpha.fadeOut();
|
||||
ttNextSlide.start(1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void reset()
|
||||
{
|
||||
ttFinish.stop();
|
||||
ttNextSlide.stop();
|
||||
ttText1.stop();
|
||||
ttText2.stop();
|
||||
|
||||
|
||||
layerAlpha.setTo(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private LayerSlide slideLayer;
|
||||
|
||||
|
||||
private final Action next = new Action() {
|
||||
|
||||
|
||||
@Override
|
||||
protected void execute()
|
||||
{
|
||||
showSlide(++slide);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private final Action prev = new Action() {
|
||||
|
||||
|
||||
@Override
|
||||
protected void execute()
|
||||
{
|
||||
@@ -197,32 +198,33 @@ public class ScreenStory extends RogueScreen implements MouseButtonHandler {
|
||||
showSlide(slide);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private final Action close = new Action() {
|
||||
|
||||
|
||||
@Override
|
||||
protected void execute()
|
||||
{
|
||||
App.bus().send(new RogueStateRequest(RogueState.MAIN_MENU));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public ScreenStory() {
|
||||
|
||||
|
||||
|
||||
public ScreenStory()
|
||||
{
|
||||
|
||||
addLayer(new LayerColor(this, Color.fromHex(0x040c1e), 0));
|
||||
addLayer(slideLayer = new LayerSlide(this));
|
||||
|
||||
|
||||
bindKey(new KeyStroke(Keys.SPACE), Trigger.RISING, next);
|
||||
bindKey(new KeyStroke(Keys.RIGHT), Trigger.RISING, next);
|
||||
bindKey(new KeyStroke(Keys.BACKSPACE), Trigger.RISING, prev);
|
||||
bindKey(new KeyStroke(Keys.LEFT), Trigger.RISING, prev);
|
||||
bindKey(App.cfg().getKeyStroke("general.close"), Trigger.RISING, close);
|
||||
}
|
||||
|
||||
|
||||
private int slide = 0;
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected void onScreenEnter()
|
||||
{
|
||||
@@ -230,29 +232,29 @@ public class ScreenStory extends RogueScreen implements MouseButtonHandler {
|
||||
slideLayer.reset();
|
||||
showSlide(slide);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private void showSlide(int slide)
|
||||
{
|
||||
switch (slide) {
|
||||
case 0:
|
||||
slideLayer.showSlide("story_1", "Man, it's so hot today!", "Makes me real thirsty, ya know.");
|
||||
break;
|
||||
|
||||
|
||||
case 1:
|
||||
slideLayer.showSlide("story_2", "'Guess I'll go get some beer", "from the cellar.");
|
||||
break;
|
||||
|
||||
|
||||
case 2:
|
||||
slideLayer.showSlide("story_3", "Here we go.. HEY GIVE IT BACK!", "I'll hunt you down, thieves!");
|
||||
break;
|
||||
|
||||
|
||||
case 3:
|
||||
App.bus().send(new RogueStateRequest(RogueState.MAIN_MENU));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void receive(MouseButtonEvent event)
|
||||
{
|
||||
|
||||
@@ -11,57 +11,59 @@ import mightypork.utils.ion.IonOutput;
|
||||
|
||||
|
||||
public class Inventory implements IonBinary {
|
||||
|
||||
|
||||
public static final short ION_MARK = 54;
|
||||
private Item[] items;
|
||||
private int lastAddIndex = 0;
|
||||
|
||||
|
||||
public Inventory(int size) {
|
||||
|
||||
|
||||
public Inventory(int size)
|
||||
{
|
||||
this.items = new Item[size];
|
||||
}
|
||||
|
||||
|
||||
public Inventory() {
|
||||
|
||||
|
||||
public Inventory()
|
||||
{
|
||||
// ION constructor
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void load(IonInput in) throws IOException
|
||||
{
|
||||
final int size = in.readIntByte();
|
||||
items = new Item[size];
|
||||
|
||||
|
||||
// for all items in sequence
|
||||
while (in.hasNextEntry()) {
|
||||
|
||||
|
||||
// load item index
|
||||
final int i = in.readIntByte();
|
||||
|
||||
|
||||
// load item
|
||||
setItem(i, Items.loadItem(in));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void save(IonOutput out) throws IOException
|
||||
{
|
||||
// write length
|
||||
out.writeIntByte(getSize());
|
||||
|
||||
|
||||
// find items that are writable
|
||||
for (int i = 0; i < getSize(); i++) {
|
||||
final Item item = getItem(i);
|
||||
if (item != null && !item.isEmpty()) {
|
||||
|
||||
|
||||
// start sequence entry
|
||||
out.startEntry();
|
||||
|
||||
|
||||
// write index
|
||||
out.writeIntByte(i);
|
||||
|
||||
|
||||
// write item at index
|
||||
Items.saveItem(out, item);
|
||||
}
|
||||
@@ -69,36 +71,36 @@ public class Inventory implements IonBinary {
|
||||
// close sequence
|
||||
out.endSequence();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get item in a slot
|
||||
*
|
||||
*
|
||||
* @param i slot number
|
||||
* @return item in the slot; can be null.
|
||||
*/
|
||||
public Item getItem(int i)
|
||||
{
|
||||
if (i < 0 || i > getSize()) return null;
|
||||
|
||||
|
||||
verifyIndex(i);
|
||||
final Item itm = items[i];
|
||||
if (itm == null || itm.isEmpty()) return null;
|
||||
return itm;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private void verifyIndex(int i)
|
||||
{
|
||||
if (i < 0 || i > getSize()) {
|
||||
throw new IndexOutOfBoundsException("Invalid inventory index: " + i + ", size: " + getSize());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Put item in a slot
|
||||
*
|
||||
*
|
||||
* @param i slot number
|
||||
* @param item item to store
|
||||
*/
|
||||
@@ -108,8 +110,8 @@ public class Inventory implements IonBinary {
|
||||
items[i] = item;
|
||||
lastAddIndex = i;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return inventory size
|
||||
*/
|
||||
@@ -117,11 +119,11 @@ public class Inventory implements IonBinary {
|
||||
{
|
||||
return items.length;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Add an item, try to merge first.
|
||||
*
|
||||
*
|
||||
* @param stored stored item
|
||||
* @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
|
||||
for (int i = 0; i < getSize(); i++) {
|
||||
final Item itm = getItem(i);
|
||||
@@ -147,12 +149,12 @@ public class Inventory implements IonBinary {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// could not insert.
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Clean empty items
|
||||
*/
|
||||
@@ -164,28 +166,26 @@ public class Inventory implements IonBinary {
|
||||
if (itm.isEmpty()) setItem(i, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public int getLastAddIndex()
|
||||
{
|
||||
return lastAddIndex;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
String s = "Inv[";
|
||||
|
||||
|
||||
for (int i = 0; i < getSize(); i++) {
|
||||
if (i > 0) s += ", ";
|
||||
s += i + ": ";
|
||||
final Item itm = getItem(i);
|
||||
|
||||
if (itm == null)
|
||||
s += "<null>";
|
||||
else
|
||||
s += itm;
|
||||
|
||||
if (itm == null) s += "<null>";
|
||||
else s += itm;
|
||||
}
|
||||
s += "]";
|
||||
return s;
|
||||
|
||||
@@ -15,85 +15,85 @@ import mightypork.utils.math.constraints.vect.Vect;
|
||||
|
||||
|
||||
public abstract class PlayerControl {
|
||||
|
||||
|
||||
protected Set<EntityMoveListener> playerMoveListeners = new HashSet<>();
|
||||
|
||||
|
||||
private World lastWorld;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Implementing classes should return a world instance from this method.
|
||||
*
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
protected abstract World provideWorld();
|
||||
|
||||
|
||||
|
||||
|
||||
public World getWorld()
|
||||
{
|
||||
final World newWorld = provideWorld();
|
||||
|
||||
|
||||
lastWorld = newWorld;
|
||||
|
||||
|
||||
return newWorld;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public PlayerFacade getPlayer()
|
||||
{
|
||||
if (getWorld() == null) return null;
|
||||
|
||||
|
||||
return getWorld().getPlayer();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void goNorth()
|
||||
{
|
||||
go(Move.NORTH);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void goSouth()
|
||||
{
|
||||
go(Move.SOUTH);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void goEast()
|
||||
{
|
||||
go(Move.EAST);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void goWest()
|
||||
{
|
||||
go(Move.WEST);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void navigateTo(Coord pos)
|
||||
{
|
||||
if (!getLevel().getTile(pos).isExplored()) return;
|
||||
getPlayer().navigateTo(pos);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public Level getLevel()
|
||||
{
|
||||
return getWorld().getPlayer().getLevel();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public boolean canGo(Move side)
|
||||
{
|
||||
return getPlayer().canGoTo(side);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Click tile on player's side
|
||||
*
|
||||
*
|
||||
* @param side
|
||||
* @return
|
||||
*/
|
||||
@@ -101,44 +101,44 @@ public abstract class PlayerControl {
|
||||
{
|
||||
return doClickTile(getPlayer().getCoord().add(side).toVect());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public boolean clickTile(Vect pos)
|
||||
{
|
||||
if (pos.dist(getPlayer().getVisualPos().add(0.5, 0.5)).value() < 1.5) {
|
||||
return doClickTile(pos);
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private boolean doClickTile(Vect pos)
|
||||
{
|
||||
//try to click tile
|
||||
if (getLevel().getTile(Coord.fromVect(pos)).onClick()) return true;
|
||||
|
||||
|
||||
final Tile t = getLevel().getTile(Coord.fromVect(pos));
|
||||
if (!t.isPotentiallyWalkable()) return false; // no point in attacking entity thru wall, right?
|
||||
|
||||
|
||||
//try to hit entity
|
||||
final Entity prey = getLevel().getClosestEntity(pos, EntityType.MONSTER, 1);
|
||||
if (prey != null) {
|
||||
getPlayer().attack(prey);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void go(Move side)
|
||||
{
|
||||
getPlayer().cancelPath();
|
||||
getPlayer().addPathStep(side);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public boolean tryGo(Move e)
|
||||
{
|
||||
if (!canGo(e)) return false;
|
||||
|
||||
@@ -8,36 +8,36 @@ import mightypork.utils.ion.IonDataBundle;
|
||||
|
||||
/**
|
||||
* Player information stored in world.
|
||||
*
|
||||
*
|
||||
* @author Ondřej Hruška (MightyPork)
|
||||
*/
|
||||
public class PlayerData implements IonBundled {
|
||||
|
||||
|
||||
/** Player inventory size */
|
||||
private static final int INV_SIZE = 8;
|
||||
|
||||
|
||||
/** Attack str with bare hands */
|
||||
public static final int BARE_ATTACK = 1;
|
||||
|
||||
|
||||
private int eid = -1; // marks not initialized
|
||||
private int level;
|
||||
|
||||
|
||||
private Inventory inventory = new Inventory(INV_SIZE);
|
||||
|
||||
|
||||
private int selectedWeapon = -1;
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void load(IonDataBundle bundle)
|
||||
{
|
||||
eid = bundle.get("eid", -1);
|
||||
level = bundle.get("floor", -1);
|
||||
selectedWeapon = bundle.get("weapon", -1);
|
||||
|
||||
|
||||
inventory = bundle.get("inv", inventory);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void save(IonDataBundle bundle)
|
||||
{
|
||||
@@ -46,69 +46,69 @@ public class PlayerData implements IonBundled {
|
||||
bundle.put("weapon", selectedWeapon);
|
||||
bundle.put("inv", inventory);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void setEID(int eid)
|
||||
{
|
||||
if (isInitialized()) throw new RuntimeException("Cannot change player EID.");
|
||||
this.eid = eid;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void setLevelNumber(int level)
|
||||
{
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public int getEID()
|
||||
{
|
||||
return eid;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public int getLevelNumber()
|
||||
{
|
||||
return level;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public boolean isInitialized()
|
||||
{
|
||||
return eid != -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public Inventory getInventory()
|
||||
{
|
||||
return inventory;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public int getSelectedWeaponIndex()
|
||||
{
|
||||
return selectedWeapon;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public boolean hasWeaponSelected()
|
||||
{
|
||||
return !(selectedWeapon < 0 || selectedWeapon >= getInventory().getSize());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public Item getSelectedWeapon()
|
||||
{
|
||||
if (!hasWeaponSelected()) return null;
|
||||
return inventory.getItem(selectedWeapon);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void selectWeapon(int selectedWeapon)
|
||||
{
|
||||
this.selectedWeapon = selectedWeapon;
|
||||
|
||||
|
||||
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.
|
||||
*
|
||||
*
|
||||
* @author Ondřej Hruška (MightyPork)
|
||||
*/
|
||||
public class PlayerFacade {
|
||||
|
||||
|
||||
/** a world */
|
||||
private final World world;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return true if can go up
|
||||
*/
|
||||
@@ -33,16 +33,17 @@ public class PlayerFacade {
|
||||
{
|
||||
return world.playerData.getLevelNumber() > 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param world
|
||||
*/
|
||||
PlayerFacade(World world) {
|
||||
PlayerFacade(World world)
|
||||
{
|
||||
this.world = world;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return true if can go down
|
||||
*/
|
||||
@@ -50,48 +51,48 @@ public class PlayerFacade {
|
||||
{
|
||||
return world.playerData.getLevelNumber() < world.levels.size() - 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Go one level down if applicable
|
||||
*/
|
||||
public void descend()
|
||||
{
|
||||
if (!canDescend()) return;
|
||||
|
||||
|
||||
final int lvl_num = getLevelNumber();
|
||||
getLevel().removeEntity(world.playerEntity);
|
||||
|
||||
|
||||
world.playerData.setLevelNumber(lvl_num + 1);
|
||||
|
||||
|
||||
getLevel().forceFreeTile(getLevel().getEnterPoint());
|
||||
getLevel().addEntity(world.playerEntity, getLevel().getEnterPoint());
|
||||
getLevel().explore(getCoord());
|
||||
|
||||
|
||||
world.console.msgEnterFloor(getLevelNumber());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Go one level up if applicable
|
||||
*/
|
||||
public void ascend()
|
||||
{
|
||||
if (!canAscend()) return;
|
||||
|
||||
|
||||
final int lvl_num = getLevelNumber();
|
||||
getLevel().removeEntity(world.playerEntity);
|
||||
|
||||
|
||||
world.playerData.setLevelNumber(lvl_num - 1);
|
||||
|
||||
|
||||
getLevel().forceFreeTile(getLevel().getExitPoint());
|
||||
getLevel().addEntity(world.playerEntity, getLevel().getExitPoint());
|
||||
getLevel().explore(getCoord());
|
||||
|
||||
|
||||
world.console.msgEnterFloor(getLevelNumber());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return current level number, zero based.
|
||||
*/
|
||||
@@ -99,8 +100,8 @@ public class PlayerFacade {
|
||||
{
|
||||
return world.playerData.getLevelNumber();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return current level
|
||||
*/
|
||||
@@ -108,8 +109,8 @@ public class PlayerFacade {
|
||||
{
|
||||
return world.levels.get(world.playerData.getLevelNumber());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return entity ID
|
||||
*/
|
||||
@@ -117,8 +118,8 @@ public class PlayerFacade {
|
||||
{
|
||||
return world.playerData.getEID();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return entity coordinate in level
|
||||
*/
|
||||
@@ -126,8 +127,8 @@ public class PlayerFacade {
|
||||
{
|
||||
return world.playerEntity.getCoord();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return entity visual pos in level
|
||||
*/
|
||||
@@ -135,19 +136,19 @@ public class PlayerFacade {
|
||||
{
|
||||
return world.playerEntity.pos.getVisualPos();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Find path to
|
||||
*
|
||||
*
|
||||
* @param pos
|
||||
*/
|
||||
public void navigateTo(Coord pos)
|
||||
{
|
||||
world.playerEntity.pos.navigateTo(pos);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Discard steps in buffer
|
||||
*/
|
||||
@@ -155,108 +156,108 @@ public class PlayerFacade {
|
||||
{
|
||||
world.playerEntity.pos.cancelPath();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void addPathStep(Move step)
|
||||
{
|
||||
world.playerEntity.pos.addStep(step);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public boolean isMoving()
|
||||
{
|
||||
return world.playerEntity.pos.isMoving();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public double getMoveProgress()
|
||||
{
|
||||
return world.playerEntity.pos.getProgress();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public boolean isDead()
|
||||
{
|
||||
return world.playerEntity.isDead();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public int getHealth()
|
||||
{
|
||||
return world.playerEntity.health.getHealth();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public int getHealthMax()
|
||||
{
|
||||
return world.playerEntity.health.getHealthMax();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public Inventory getInventory()
|
||||
{
|
||||
return world.playerData.getInventory();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public Entity getEntity()
|
||||
{
|
||||
return world.playerEntity;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public int getAttackStrength()
|
||||
{
|
||||
final Item weapon = world.playerData.getSelectedWeapon();
|
||||
|
||||
|
||||
if (weapon == null) return PlayerData.BARE_ATTACK;
|
||||
|
||||
|
||||
return PlayerData.BARE_ATTACK + weapon.getAttackPoints();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Eat food.
|
||||
*
|
||||
*
|
||||
* @param itm food item
|
||||
* @return if something was eaten
|
||||
*/
|
||||
public boolean eatFood(Item itm)
|
||||
{
|
||||
if (itm == null || itm.isEmpty() || itm.getType() != ItemType.FOOD) return false;
|
||||
|
||||
|
||||
if (getHealth() < getHealthMax()) {
|
||||
|
||||
|
||||
world.playerEntity.health.addHealth(itm.getFoodPoints());
|
||||
itm.consume();
|
||||
|
||||
|
||||
world.console.msgEat(itm);
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void selectWeapon(int selected)
|
||||
{
|
||||
world.playerData.selectWeapon(selected);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public Item getSelectedWeapon()
|
||||
{
|
||||
return world.playerData.getSelectedWeapon();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public int getSelectedWeaponIndex()
|
||||
{
|
||||
return world.playerData.getSelectedWeaponIndex();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void tryToEatSomeFood()
|
||||
{
|
||||
final List<Item> foods = new ArrayList<>();
|
||||
@@ -266,63 +267,63 @@ public class PlayerFacade {
|
||||
foods.add(itm);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// sort from smallest to biggest foods
|
||||
Collections.sort(foods, new Comparator<Item>() {
|
||||
|
||||
|
||||
@Override
|
||||
public int compare(Item o1, Item o2)
|
||||
{
|
||||
return (o1.getFoodPoints() - o2.getFoodPoints());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
for (final Item itm : foods) {
|
||||
if (eatFood(itm)) {
|
||||
getInventory().clean();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (getHealth() < getHealthMax()) {
|
||||
world.console.msgNoMoreFood();
|
||||
} else {
|
||||
world.console.msgNotHungry();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void attack(Entity prey)
|
||||
{
|
||||
final int attackPoints = getAttackStrength();
|
||||
|
||||
|
||||
prey.receiveAttack(world.getPlayer().getEntity(), attackPoints);
|
||||
|
||||
|
||||
if (prey.isDead()) {
|
||||
world.console.msgKill(prey);
|
||||
}
|
||||
|
||||
|
||||
final Item wpn = getSelectedWeapon();
|
||||
|
||||
|
||||
if (wpn != null) {
|
||||
wpn.use();
|
||||
if (wpn.isEmpty()) {
|
||||
world.console.msgWeaponBreak(wpn);
|
||||
|
||||
|
||||
getInventory().clean();
|
||||
selectWeapon(-1);
|
||||
|
||||
|
||||
pickBestWeaponIfNoneSelected();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private void pickBestWeaponIfNoneSelected()
|
||||
{
|
||||
if (getSelectedWeapon() != null) return;
|
||||
|
||||
|
||||
final List<Item> wpns = new ArrayList<>();
|
||||
for (int i = 0; i < getInventory().getSize(); i++) {
|
||||
final Item itm = getInventory().getItem(i);
|
||||
@@ -330,17 +331,17 @@ public class PlayerFacade {
|
||||
wpns.add(itm);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// sort from smallest to biggest foods
|
||||
Collections.sort(wpns, new Comparator<Item>() {
|
||||
|
||||
|
||||
@Override
|
||||
public int compare(Item o1, Item o2)
|
||||
{
|
||||
return (o2.getAttackPoints() - o1.getAttackPoints());
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
for (final Item itm : wpns) {
|
||||
for (int i = 0; i < getInventory().getSize(); i++) {
|
||||
final Item itm2 = getInventory().getItem(i);
|
||||
@@ -351,75 +352,75 @@ public class PlayerFacade {
|
||||
}
|
||||
break; // just one cycle
|
||||
}
|
||||
|
||||
|
||||
world.console.msgEquipWeapon(getSelectedWeapon());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public boolean addItem(Item item)
|
||||
{
|
||||
if (!getInventory().addItem(item)) {
|
||||
|
||||
|
||||
world.console.msgCannotPick();
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
world.console.msgPick(item);
|
||||
|
||||
|
||||
if (item.getType() == ItemType.WEAPON) {
|
||||
if (getSelectedWeapon() != null) {
|
||||
if (item.getAttackPoints() > getSelectedWeapon().getAttackPoints()) {
|
||||
selectWeapon(-1); // unselect to grab the best one
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pickBestWeaponIfNoneSelected();
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void setHealth(int health)
|
||||
{
|
||||
world.playerEntity.health.setHealth(health);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void setHealthMax(int health)
|
||||
{
|
||||
world.playerEntity.health.setHealthMax(health);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public World getWorld()
|
||||
{
|
||||
return world;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public boolean canGoTo(Move side)
|
||||
{
|
||||
return getEntity().pos.canGoTo(side);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void dropItem(int itemIndex)
|
||||
{
|
||||
final Item itm = getInventory().getItem(itemIndex);
|
||||
if (itm != null && !itm.isEmpty()) {
|
||||
|
||||
|
||||
final Item piece = itm.split(1);
|
||||
getInventory().clean();
|
||||
|
||||
|
||||
Coord dropPos;
|
||||
if (world.playerEntity.pos.isMoving()) {
|
||||
dropPos = world.playerEntity.pos.getLastPos();
|
||||
} else {
|
||||
dropPos = getCoord();
|
||||
}
|
||||
|
||||
|
||||
if (!getLevel().getTile(dropPos).dropItem(piece)) {
|
||||
getInventory().addItem(piece); // add back
|
||||
} else {
|
||||
|
||||
@@ -11,7 +11,6 @@ import mightypork.rogue.world.entity.Entity;
|
||||
import mightypork.rogue.world.events.GameWinHandler;
|
||||
import mightypork.rogue.world.events.PlayerDeathHandler;
|
||||
import mightypork.rogue.world.level.Level;
|
||||
import mightypork.utils.eventbus.EventBus;
|
||||
import mightypork.utils.eventbus.clients.DelegatingClient;
|
||||
import mightypork.utils.interfaces.Pauseable;
|
||||
import mightypork.utils.interfaces.Updateable;
|
||||
@@ -23,71 +22,71 @@ import mightypork.utils.math.algo.Coord;
|
||||
|
||||
/**
|
||||
* World object.
|
||||
*
|
||||
*
|
||||
* @author Ondřej Hruška (MightyPork)
|
||||
*/
|
||||
public class World implements DelegatingClient, IonBundled, Pauseable, Updateable, PlayerDeathHandler, GameWinHandler {
|
||||
|
||||
|
||||
// not saved stuffs
|
||||
private final PlayerFacade playerFacade = new PlayerFacade(this);
|
||||
|
||||
|
||||
final WorldConsole console = new WorldConsole();
|
||||
Entity playerEntity;
|
||||
private int pauseDepth = 0;
|
||||
private boolean gameOver = false;
|
||||
|
||||
|
||||
/** List of world's levels */
|
||||
final ArrayList<Level> levels = new ArrayList<>();
|
||||
|
||||
|
||||
/** Player data saved together with world */
|
||||
final PlayerData playerData = new PlayerData();
|
||||
|
||||
|
||||
/** World seed */
|
||||
private long seed;
|
||||
|
||||
|
||||
/** Next entity ID */
|
||||
private int eid;
|
||||
|
||||
|
||||
private File saveFile;
|
||||
|
||||
|
||||
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
@Override
|
||||
public Collection getChildClients()
|
||||
{
|
||||
return levels;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean doesDelegate()
|
||||
{
|
||||
return !isPaused();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void load(IonDataBundle in)
|
||||
{
|
||||
seed = in.get("seed", seed);
|
||||
eid = in.get("next_eid", eid);
|
||||
in.loadSequence("levels", levels);
|
||||
|
||||
|
||||
// join levels to world
|
||||
for (final Level lvl : levels) {
|
||||
lvl.setWorld(this);
|
||||
}
|
||||
|
||||
|
||||
in.loadBundled("player", playerData);
|
||||
|
||||
|
||||
final int eid = playerData.getEID();
|
||||
final int lvl = playerData.getLevelNumber();
|
||||
|
||||
|
||||
playerEntity = levels.get(lvl).getEntity(eid);
|
||||
if (playerEntity == null) {
|
||||
|
||||
|
||||
Log.e("Player entity not found in the world: " + eid + " on floor " + lvl);
|
||||
|
||||
|
||||
for (int i = 0; i < levels.size(); i++) {
|
||||
final Entity ent = levels.get(i).getEntity(eid);
|
||||
if (ent != null) {
|
||||
@@ -95,12 +94,12 @@ public class World implements DelegatingClient, IonBundled, Pauseable, Updateabl
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
throw new RuntimeException("Player not found in world.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void save(IonDataBundle out)
|
||||
{
|
||||
@@ -108,31 +107,31 @@ public class World implements DelegatingClient, IonBundled, Pauseable, Updateabl
|
||||
out.put("next_eid", eid);
|
||||
out.putSequence("levels", levels);
|
||||
out.putBundled("player", playerData);
|
||||
|
||||
|
||||
// used for peking in before actually loading the world.
|
||||
out.put("meta.last_level", playerData.getLevelNumber());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void addLevel(Level level)
|
||||
{
|
||||
levels.add(level);
|
||||
level.setWorld(this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void setSeed(long seed)
|
||||
{
|
||||
this.seed = seed;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public long getSeed()
|
||||
{
|
||||
return seed;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return new entity ID
|
||||
*/
|
||||
@@ -140,79 +139,79 @@ public class World implements DelegatingClient, IonBundled, Pauseable, Updateabl
|
||||
{
|
||||
return eid++;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void createPlayer()
|
||||
{
|
||||
if (playerData.isInitialized()) {
|
||||
throw new RuntimeException("Player already created.");
|
||||
}
|
||||
|
||||
|
||||
// make entity
|
||||
final int playerEid = getNewEID();
|
||||
|
||||
|
||||
final Level floor = levels.get(0);
|
||||
|
||||
|
||||
playerEntity = Entities.PLAYER.createEntity(playerEid);
|
||||
|
||||
|
||||
final Coord spawn = floor.getEnterPoint();
|
||||
|
||||
|
||||
floor.forceFreeTile(spawn);
|
||||
final Random rand = new Random(seed + 71);
|
||||
|
||||
|
||||
while (!floor.addEntity(playerEntity, spawn)) {
|
||||
spawn.x += -1 + rand.nextInt(3);
|
||||
spawn.y += -1 + rand.nextInt(3);
|
||||
}
|
||||
|
||||
|
||||
floor.explore(spawn);
|
||||
|
||||
|
||||
playerData.setLevelNumber(0);
|
||||
playerData.setEID(playerEid);
|
||||
|
||||
|
||||
console.msgEnterFloor(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void pause()
|
||||
{
|
||||
pauseDepth++;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void resume()
|
||||
{
|
||||
if (pauseDepth > 0) pauseDepth--;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isPaused()
|
||||
{
|
||||
return pauseDepth > 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public PlayerFacade getPlayer()
|
||||
{
|
||||
return playerFacade;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void update(double delta)
|
||||
{
|
||||
if (isPaused()) return;
|
||||
|
||||
|
||||
// update console timing - not as child client
|
||||
console.update(delta);
|
||||
|
||||
|
||||
if (saveFile == null) Log.w("World has no save file.");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return world console
|
||||
*/
|
||||
@@ -220,19 +219,19 @@ public class World implements DelegatingClient, IonBundled, Pauseable, Updateabl
|
||||
{
|
||||
return console;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Set file for saving
|
||||
*
|
||||
*
|
||||
* @param file save file
|
||||
*/
|
||||
public void setSaveFile(File file)
|
||||
{
|
||||
this.saveFile = file;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return assigned file fro saving
|
||||
*/
|
||||
@@ -240,22 +239,22 @@ public class World implements DelegatingClient, IonBundled, Pauseable, Updateabl
|
||||
{
|
||||
return saveFile;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void onGameWon()
|
||||
{
|
||||
gameOver = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void onPlayerKilled()
|
||||
{
|
||||
gameOver = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public boolean isGameOver()
|
||||
{
|
||||
return gameOver || getPlayer().isDead();
|
||||
|
||||
@@ -15,222 +15,223 @@ import mightypork.utils.math.animation.NumAnimated;
|
||||
|
||||
/**
|
||||
* Message log in world view
|
||||
*
|
||||
*
|
||||
* @author Ondřej Hruška (MightyPork)
|
||||
*/
|
||||
public class WorldConsole implements Updateable {
|
||||
|
||||
|
||||
/** Used only for display */
|
||||
public Item lastPickupItem;
|
||||
public double timeSinceLastPickup = Integer.MAX_VALUE;
|
||||
|
||||
|
||||
private static final double DURATION = 5;
|
||||
|
||||
|
||||
public class Entry implements Updateable {
|
||||
|
||||
|
||||
private final String text;
|
||||
private final NumAnimated fadeout;
|
||||
private boolean fading = false;
|
||||
private double elapsed = 0;
|
||||
|
||||
|
||||
private Entry(String text) {
|
||||
|
||||
|
||||
private Entry(String text)
|
||||
{
|
||||
this.text = text;
|
||||
this.fadeout = new NumAnimated(1, Easing.LINEAR);
|
||||
this.fadeout.setDefaultDuration(0.5);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void update(double delta)
|
||||
{
|
||||
elapsed += delta;
|
||||
|
||||
|
||||
if (fading) {
|
||||
fadeout.update(delta);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (elapsed > DURATION) {
|
||||
fading = true;
|
||||
fadeout.fadeOut();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private boolean canRemove()
|
||||
{
|
||||
return fading && fadeout.isFinished();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public double getAlpha()
|
||||
{
|
||||
return !fading ? 1 : fadeout.value();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public String getMessage()
|
||||
{
|
||||
return text;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public double getAge()
|
||||
{
|
||||
return elapsed;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private final Deque<Entry> entries = new LinkedBlockingDeque<>();
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void update(double delta)
|
||||
{
|
||||
for (final Iterator<Entry> iter = entries.iterator(); iter.hasNext();) {
|
||||
final Entry e = iter.next();
|
||||
|
||||
|
||||
e.update(delta);
|
||||
|
||||
|
||||
if (e.canRemove()) {
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
timeSinceLastPickup += delta;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void addMessage(String message)
|
||||
{
|
||||
entries.addFirst(new Entry(message));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public Collection<Entry> getEntries()
|
||||
{
|
||||
return entries;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void clear()
|
||||
{
|
||||
entries.clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void msgCannotPick()
|
||||
{
|
||||
addMessage("Can't collect items, inventory is full.");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void msgDie(Entity attacker)
|
||||
{
|
||||
addMessage("You've been defeated by " + addArticle(attacker.getVisualName()) + "!");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void msgDiscoverSecretDoor()
|
||||
{
|
||||
addMessage("You've discovered a secret door.");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void msgEat(Item item)
|
||||
{
|
||||
addMessage("You've eaten " + addArticle(item.getVisualName()) + ".");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void msgEnterFloor(int floor)
|
||||
{
|
||||
addMessage("~ Floor " + (1 + floor) + " ~");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void msgEquipWeapon(Item item)
|
||||
{
|
||||
addMessage("You're now wielding " + (item == null ? "NOTHING" : addArticle(item.getVisualName())) + ".");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void msgHeartPiece()
|
||||
{
|
||||
addMessage("Your health capacity has been increased.");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void msgKill(Entity prey)
|
||||
{
|
||||
addMessage("You've killed " + addArticle(prey.getVisualName()) + ".");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void msgNoMoreFood()
|
||||
{
|
||||
addMessage("You don't have any food!");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void msgNotHungry()
|
||||
{
|
||||
addMessage("You are not hungry.");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void msgPick(Item item)
|
||||
{
|
||||
addMessage("You've picked up " + addArticle(item.getVisualName()) + ".");
|
||||
lastPickupItem = item;
|
||||
timeSinceLastPickup = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void msgDroppedItem(Item item)
|
||||
{
|
||||
addMessage("You've dropped " + addArticle(item.getVisualName()) + ".");
|
||||
lastPickupItem = null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void msgWeaponBreak(Item item)
|
||||
{
|
||||
addMessage("Your " + item.getVisualName() + " has broken!");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void msgWorldSaved()
|
||||
{
|
||||
addMessage("Game saved to file.");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void msgWorldSaveError()
|
||||
{
|
||||
addMessage("Error while saving; See the log for details.");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void msgReloaded()
|
||||
{
|
||||
addMessage("World loaded from file.");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void msgLoadFailed()
|
||||
{
|
||||
addMessage("Error while loading; See the log for details.");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void msgOpenChest()
|
||||
{
|
||||
addMessage("You've opened a treasure chest!");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private String addArticle(String name)
|
||||
{
|
||||
switch (Character.toLowerCase(name.charAt(0))) {
|
||||
|
||||
@@ -14,50 +14,50 @@ import mightypork.utils.logging.Log;
|
||||
|
||||
/**
|
||||
* Global singleton world holder and storage
|
||||
*
|
||||
*
|
||||
* @author Ondřej Hruška (MightyPork)
|
||||
*/
|
||||
public class WorldProvider extends BusNode {
|
||||
|
||||
|
||||
public static synchronized void init()
|
||||
{
|
||||
if (inst == null) {
|
||||
inst = new WorldProvider();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public WorldProvider()
|
||||
{
|
||||
setListening(false);
|
||||
}
|
||||
|
||||
|
||||
private static WorldProvider inst;
|
||||
|
||||
|
||||
|
||||
|
||||
public static WorldProvider get()
|
||||
{
|
||||
if (inst == null) {
|
||||
throw new IllegalStateException("World provider not initialized.");
|
||||
}
|
||||
|
||||
|
||||
return inst;
|
||||
}
|
||||
|
||||
|
||||
private World world;
|
||||
private final PlayerControl playerControl = new PlayerControl() {
|
||||
|
||||
|
||||
@Override
|
||||
protected World provideWorld()
|
||||
{
|
||||
return world;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Create and register a world based on a seed
|
||||
*
|
||||
*
|
||||
* @param seed random seed
|
||||
* @return the world
|
||||
*/
|
||||
@@ -68,35 +68,35 @@ public class WorldProvider extends BusNode {
|
||||
setWorld(w);
|
||||
return w;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public World getWorld()
|
||||
{
|
||||
return world;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void setWorld(World newWorld)
|
||||
{
|
||||
if (world != null) removeChildClient(world);
|
||||
world = newWorld;
|
||||
|
||||
|
||||
if (newWorld == null) return;
|
||||
|
||||
|
||||
addChildClient(world);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void loadWorld(File file) throws IOException
|
||||
{
|
||||
Log.f2("Loading world from: " + file);
|
||||
|
||||
|
||||
final IonDataBundle bu = Ion.fromFile(file);
|
||||
setWorld(bu.loadBundled("world", new World()));
|
||||
world.setSaveFile(file);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void saveWorld(File file) throws IOException
|
||||
{
|
||||
if (world == null) {
|
||||
@@ -105,44 +105,44 @@ public class WorldProvider extends BusNode {
|
||||
if (file == null) {
|
||||
throw new IllegalStateException("Trying to save world to a NULL file.");
|
||||
}
|
||||
|
||||
|
||||
if (world.isGameOver()) {
|
||||
throw new IllegalStateException("Cannot save, player is dead.");
|
||||
}
|
||||
|
||||
|
||||
Log.f2("Saving world to: " + file);
|
||||
|
||||
|
||||
final IonDataBundle bu = new IonDataBundle();
|
||||
bu.put("level", world.getPlayer().getLevelNumber());
|
||||
bu.putBundled("world", world);
|
||||
|
||||
|
||||
Ion.toFile(file, bu);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Save to world's assigned save file.
|
||||
*
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
public void saveWorld() throws IOException
|
||||
{
|
||||
saveWorld(world.getSaveFile());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public Level getCurrentLevel()
|
||||
{
|
||||
return getWorld().getPlayer().getLevel();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public PlayerFacade getPlayer()
|
||||
{
|
||||
return getWorld().getPlayer();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return constant player control (world independent)
|
||||
*/
|
||||
@@ -150,5 +150,5 @@ public class WorldProvider extends BusNode {
|
||||
{
|
||||
return playerControl;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -20,92 +20,93 @@ import mightypork.utils.math.constraints.vect.VectConst;
|
||||
|
||||
/**
|
||||
* World rendering untility
|
||||
*
|
||||
*
|
||||
* @author Ondřej Hruška (MightyPork)
|
||||
*/
|
||||
public class WorldRenderer extends RectProxy {
|
||||
|
||||
|
||||
private static final boolean USE_BATCH_RENDERING = true;
|
||||
|
||||
|
||||
private final Num tileSize;
|
||||
|
||||
|
||||
// can be changed
|
||||
private RectConst mapRect;
|
||||
private Level activeLevel;
|
||||
|
||||
|
||||
private final Rect rightShadow;
|
||||
private final Rect leftShadow;
|
||||
private final Rect topShadow;
|
||||
private final Rect bottomShadow;
|
||||
|
||||
|
||||
private TileRenderContext trc;
|
||||
|
||||
|
||||
public WorldRenderer(Rect viewport, Num tileSize) {
|
||||
|
||||
|
||||
public WorldRenderer(Rect viewport, Num tileSize)
|
||||
{
|
||||
super(viewport);
|
||||
|
||||
|
||||
this.tileSize = tileSize;
|
||||
|
||||
|
||||
final Num grX = width().perc(30);
|
||||
leftShadow = leftEdge().growRight(grX);
|
||||
rightShadow = rightEdge().growLeft(grX);
|
||||
|
||||
|
||||
final Num grY = height().perc(20);
|
||||
topShadow = topEdge().growDown(grY);
|
||||
bottomShadow = bottomEdge().growUp(grY);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private void prepareRenderContextIfNeeded()
|
||||
{
|
||||
final Level level = WorldProvider.get().getCurrentLevel();
|
||||
|
||||
|
||||
if (activeLevel == level) return;
|
||||
|
||||
|
||||
activeLevel = level;
|
||||
|
||||
|
||||
mapRect = Rect.make(0, 0, level.getWidth(), level.getHeight());
|
||||
|
||||
|
||||
trc = new TileRenderContext(activeLevel, mapRect);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private VectConst getOffset()
|
||||
{
|
||||
return Vect.make(WorldProvider.get().getPlayer().getVisualPos().neg().sub(0.5, 0.5)).freeze();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void render()
|
||||
{
|
||||
prepareRenderContextIfNeeded();
|
||||
|
||||
|
||||
final GraphicsModule gfx = App.gfx();
|
||||
|
||||
|
||||
gfx.pushGeometry();
|
||||
gfx.setColor(RGB.WHITE);
|
||||
gfx.translate(center());
|
||||
gfx.scaleXY(tileSize.value());
|
||||
gfx.translate(getOffset());
|
||||
|
||||
|
||||
// tiles to render
|
||||
|
||||
|
||||
final Coord pos = WorldProvider.get().getPlayer().getCoord();
|
||||
final double w = width().value();
|
||||
final double h = height().value();
|
||||
|
||||
|
||||
final double ts = tileSize.value();
|
||||
|
||||
|
||||
final int xtilesh = (int) (w / (ts * 2)) + 1;
|
||||
final int ytilesh = (int) (h / (ts * 2)) + 1;
|
||||
|
||||
|
||||
final int x1 = pos.x - xtilesh;
|
||||
final int y1 = pos.y - ytilesh;
|
||||
final int x2 = pos.x + xtilesh;
|
||||
final int y2 = pos.y + ytilesh;
|
||||
|
||||
|
||||
// === TILES ===
|
||||
|
||||
|
||||
// batch rendering of the tiles
|
||||
// TODO
|
||||
// if (USE_BATCH_RENDERING) {
|
||||
@@ -120,32 +121,32 @@ public class WorldRenderer extends RectProxy {
|
||||
// if (USE_BATCH_RENDERING) {
|
||||
// gfx.leaveBatchTexturedQuadMode();
|
||||
// }
|
||||
|
||||
|
||||
// === ITEMS ON TILES ===
|
||||
|
||||
|
||||
for (trc.pos.x = x1; trc.pos.x <= x2; trc.pos.x++) {
|
||||
for (trc.pos.y = y1; trc.pos.y <= y2; trc.pos.y++) {
|
||||
trc.renderItems();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// === ENTITIES ===
|
||||
|
||||
|
||||
for (final Entity e : activeLevel.getEntities()) {
|
||||
|
||||
|
||||
// avoid entities out of view rect
|
||||
final Vect entPos = e.pos.getVisualPos();
|
||||
final int x = (int) Math.round(entPos.x());
|
||||
final int y = (int) Math.round(entPos.y());
|
||||
|
||||
|
||||
if (x < x1 - ts || x > x2 + ts) continue;
|
||||
if (y < y1 - ts || y > y2 + ts) continue;
|
||||
|
||||
|
||||
e.render(trc);
|
||||
}
|
||||
|
||||
|
||||
// === unexplored fog ===
|
||||
|
||||
|
||||
// batch rendering of the tiles
|
||||
// TODO
|
||||
// if (USE_BATCH_RENDERING) {
|
||||
@@ -162,24 +163,24 @@ public class WorldRenderer extends RectProxy {
|
||||
// if (USE_BATCH_RENDERING) {
|
||||
// gfx.leaveBatchTexturedQuadMode();
|
||||
// }
|
||||
|
||||
|
||||
// === OVERLAY SHADOW ===
|
||||
|
||||
|
||||
gfx.popGeometry();
|
||||
|
||||
|
||||
gfx.quad(leftShadow, new GradH(RGB.BLACK, RGB.NONE));
|
||||
gfx.quad(rightShadow, new GradH(RGB.NONE, RGB.BLACK));
|
||||
|
||||
|
||||
gfx.quad(topShadow, new GradV(RGB.BLACK, RGB.NONE));
|
||||
gfx.quad(bottomShadow, new GradV(RGB.NONE, RGB.BLACK));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public Vect getClickedTile(Vect clickPos)
|
||||
{
|
||||
final int ts = (int) tileSize.value();
|
||||
final Vect v = clickPos.sub(center().add(getOffset().mul(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 AiTimer(double duration) {
|
||||
|
||||
public AiTimer(double duration)
|
||||
{
|
||||
super(duration);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public abstract void run();
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void load(IonDataBundle bundle)
|
||||
{
|
||||
@@ -26,12 +27,12 @@ public abstract class AiTimer extends TaskRepeater implements IonBundled {
|
||||
} else {
|
||||
resume();
|
||||
}
|
||||
|
||||
|
||||
setProgress(bundle.get("progress", getProgress()));
|
||||
setDuration(bundle.get("duration", getDuration()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void save(IonDataBundle bundle)
|
||||
{
|
||||
@@ -39,5 +40,5 @@ public abstract class AiTimer extends TaskRepeater implements IonBundled {
|
||||
bundle.put("progress", getProgress());
|
||||
bundle.put("duration", getDuration());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -14,45 +14,45 @@ import mightypork.utils.ion.IonOutput;
|
||||
|
||||
/**
|
||||
* Entity registry
|
||||
*
|
||||
*
|
||||
* @author Ondřej Hruška (MightyPork)
|
||||
*/
|
||||
public final class Entities {
|
||||
|
||||
|
||||
private static final EntityModel[] entities = new EntityModel[256];
|
||||
|
||||
|
||||
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_BROWN = new EntityModel(3, EntityBrownRat.class);
|
||||
public static final EntityModel RAT_BOSS = new EntityModel(4, EntityBossRat.class);
|
||||
|
||||
|
||||
|
||||
|
||||
public static void register(int id, EntityModel model)
|
||||
{
|
||||
if (id < 0 || id >= entities.length) {
|
||||
throw new IllegalArgumentException("Entity model ID " + id + " is out of range.");
|
||||
}
|
||||
|
||||
|
||||
if (entities[id] != null) {
|
||||
throw new IllegalArgumentException("Entity model ID " + id + " already in use.");
|
||||
}
|
||||
|
||||
|
||||
entities[id] = model;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public static EntityModel get(int id)
|
||||
{
|
||||
final EntityModel e = entities[id];
|
||||
|
||||
|
||||
if (e == null) {
|
||||
throw new IllegalArgumentException("No entity model with ID " + id + ".");
|
||||
}
|
||||
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public static void loadEntities(IonInput in, Collection<Entity> entities) throws IOException
|
||||
{
|
||||
entities.clear();
|
||||
@@ -60,34 +60,34 @@ public final class Entities {
|
||||
entities.add(loadEntity(in));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public static void saveEntities(IonOutput out, Collection<Entity> entities) throws IOException
|
||||
{
|
||||
for (final Entity entity : entities) {
|
||||
out.startEntry();
|
||||
saveEntity(out, entity);
|
||||
}
|
||||
|
||||
|
||||
out.endSequence();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public static Entity loadEntity(IonInput in) throws IOException
|
||||
{
|
||||
final int id = in.readIntByte();
|
||||
|
||||
|
||||
final EntityModel model = get(id);
|
||||
return model.loadEntity(in);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public static void saveEntity(IonOutput out, Entity entity) throws IOException
|
||||
{
|
||||
final EntityModel model = entity.getModel();
|
||||
|
||||
|
||||
out.writeIntByte(model.id);
|
||||
|
||||
|
||||
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.
|
||||
*
|
||||
*
|
||||
* @author Ondřej Hruška (MightyPork)
|
||||
*/
|
||||
public abstract class Entity implements IonBundled, Updateable, DelegatingClient {
|
||||
|
||||
|
||||
private Level level;
|
||||
private final EntityModel model;
|
||||
|
||||
|
||||
/** Entity ID */
|
||||
private int entityId = -1;
|
||||
|
||||
|
||||
private final Map<String, EntityModule> modules = new HashMap<>();
|
||||
|
||||
|
||||
// default modules
|
||||
public final EntityModulePosition pos = new EntityModulePosition(this);
|
||||
public final EntityModuleHealth health = new EntityModuleHealth(this);
|
||||
private double despawnDelay = 1;
|
||||
protected Entity lastAttacker;
|
||||
private boolean freed;
|
||||
|
||||
|
||||
public Entity(EntityModel model, int eid) {
|
||||
|
||||
|
||||
public Entity(EntityModel model, int eid)
|
||||
{
|
||||
this.entityId = eid;
|
||||
this.model = model;
|
||||
|
||||
|
||||
// register modules
|
||||
addModule("pos", pos);
|
||||
addModule("health", health);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public final void save(IonDataBundle bundle)
|
||||
{
|
||||
bundle.put("eid", entityId);
|
||||
|
||||
|
||||
final IonDataBundle modulesBundle = new IonDataBundle();
|
||||
for (final Entry<String, EntityModule> entry : modules.entrySet()) {
|
||||
modulesBundle.putBundled(entry.getKey(), entry.getValue());
|
||||
}
|
||||
bundle.put("modules", modulesBundle);
|
||||
|
||||
|
||||
final IonDataBundle extra = new IonDataBundle();
|
||||
saveExtra(extra);
|
||||
bundle.put("extra", extra);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Stub
|
||||
protected void saveExtra(IonDataBundle bundle)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public final void load(IonDataBundle bundle)
|
||||
{
|
||||
entityId = bundle.get("eid", -1);
|
||||
if (entityId < 0) throw new IllegalValueException("Bad entity id: " + entityId);
|
||||
|
||||
|
||||
final IonDataBundle modulesBundle = bundle.get("modules", new IonDataBundle());
|
||||
|
||||
|
||||
for (final Entry<String, EntityModule> entry : modules.entrySet()) {
|
||||
modulesBundle.loadBundled(entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
|
||||
final IonDataBundle extra = bundle.get("extra", new IonDataBundle());
|
||||
loadExtra(extra);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Stub
|
||||
protected void loadExtra(IonDataBundle bundle)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
protected final void addModule(String key, EntityModule module)
|
||||
{
|
||||
if (modules.containsKey(key)) {
|
||||
@@ -107,8 +108,8 @@ public abstract class Entity implements IonBundled, Updateable, DelegatingClient
|
||||
}
|
||||
modules.put(key, module);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return unique entity id
|
||||
*/
|
||||
@@ -116,53 +117,53 @@ public abstract class Entity implements IonBundled, Updateable, DelegatingClient
|
||||
{
|
||||
return entityId;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void setLevel(Level level)
|
||||
{
|
||||
if (level != null && entityId < 0) {
|
||||
entityId = level.getWorld().getNewEID();
|
||||
}
|
||||
|
||||
|
||||
if (level != null) level.freeTile(getCoord());
|
||||
|
||||
|
||||
this.level = level;
|
||||
|
||||
|
||||
if (level != null) level.occupyTile(getCoord());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public final Level getLevel()
|
||||
{
|
||||
return level;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public final World getWorld()
|
||||
{
|
||||
return level.getWorld();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public final EntityModel getModel()
|
||||
{
|
||||
return model;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public abstract PathFinder getPathFinder();
|
||||
|
||||
|
||||
|
||||
|
||||
@Stub
|
||||
public final void render(MapRenderContext context)
|
||||
{
|
||||
getRenderer().render(context);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
protected abstract EntityRenderer getRenderer();
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void update(double delta)
|
||||
{
|
||||
@@ -171,14 +172,14 @@ public abstract class Entity implements IonBundled, Updateable, DelegatingClient
|
||||
freed = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return entity type (used for AI targeting)
|
||||
*/
|
||||
public abstract EntityType getType();
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return entity coord in level
|
||||
*/
|
||||
@@ -186,18 +187,18 @@ public abstract class Entity implements IonBundled, Updateable, DelegatingClient
|
||||
{
|
||||
return pos.getCoord();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void setCoord(Coord coord)
|
||||
{
|
||||
if (level != null) level.freeTile(getCoord());
|
||||
|
||||
|
||||
pos.setCoord(coord);
|
||||
|
||||
|
||||
if (level != null) level.occupyTile(getCoord());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Called right after the entity's health reaches zero.
|
||||
*/
|
||||
@@ -205,8 +206,8 @@ public abstract class Entity implements IonBundled, Updateable, DelegatingClient
|
||||
public void onKilled()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return true if dead
|
||||
*/
|
||||
@@ -214,8 +215,8 @@ public abstract class Entity implements IonBundled, Updateable, DelegatingClient
|
||||
{
|
||||
return health.isDead();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Called after the corpse has been cleaned from level.
|
||||
*/
|
||||
@@ -233,12 +234,12 @@ public abstract class Entity implements IonBundled, Updateable, DelegatingClient
|
||||
public void onCorpseRemoved()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Receive damage from an attacker.<br>
|
||||
* The entity can decide whether to dodge, reduce damage etc.
|
||||
*
|
||||
*
|
||||
* @param attacker the entity attacking. Can be null for environmental
|
||||
* damage.
|
||||
* @param attackStrength attack strength in health points to take
|
||||
@@ -248,48 +249,48 @@ public abstract class Entity implements IonBundled, Updateable, DelegatingClient
|
||||
this.lastAttacker = attacker;
|
||||
health.receiveDamage(attackStrength);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Set how long after being killed is the corpse elligible for removal
|
||||
*
|
||||
*
|
||||
* @param despawnDelay (secs)
|
||||
*/
|
||||
public void setDespawnDelay(double despawnDelay)
|
||||
{
|
||||
this.despawnDelay = despawnDelay;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public double getDespawnDelay()
|
||||
{
|
||||
return despawnDelay;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public abstract String getVisualName();
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean doesDelegate()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public Collection<?> getChildClients()
|
||||
{
|
||||
return modules.values();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public Entity getLastAttacker()
|
||||
{
|
||||
return lastAttacker;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public double getLastAttackTime()
|
||||
{
|
||||
return health.getTimeSinceLastDamage();
|
||||
|
||||
@@ -11,29 +11,30 @@ import mightypork.utils.ion.IonOutput;
|
||||
|
||||
/**
|
||||
* Entity model - builder
|
||||
*
|
||||
*
|
||||
* @author Ondřej Hruška (MightyPork)
|
||||
*/
|
||||
public final class EntityModel {
|
||||
|
||||
|
||||
/** Model ID */
|
||||
public final int id;
|
||||
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);
|
||||
this.id = id;
|
||||
this.tileClass = entity;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public Entity createEntity(World world)
|
||||
{
|
||||
return createEntity(world.getNewEID());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public Entity createEntity(int eid)
|
||||
{
|
||||
try {
|
||||
@@ -42,20 +43,20 @@ public final class EntityModel {
|
||||
throw new RuntimeException("Could not instantiate a tile.", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Create entitiy without EID. EID will be assigned when the entity is added
|
||||
* to a level.
|
||||
*
|
||||
*
|
||||
* @return entity.
|
||||
*/
|
||||
public Entity createEntity()
|
||||
{
|
||||
return createEntity(-1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public Entity loadEntity(IonInput in) throws IOException
|
||||
{
|
||||
final IonDataBundle bundle = in.readBundle();
|
||||
@@ -63,8 +64,8 @@ public final class EntityModel {
|
||||
ent.load(bundle);
|
||||
return ent;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void saveEntity(IonOutput out, Entity entity) throws IOException
|
||||
{
|
||||
final IonDataBundle bundle = new IonDataBundle();
|
||||
|
||||
@@ -10,39 +10,40 @@ import mightypork.utils.ion.IonDataBundle;
|
||||
/**
|
||||
* Abstract entity module<br>
|
||||
* Modules make up an entity AI and behavior.
|
||||
*
|
||||
*
|
||||
* @author Ondřej Hruška (MightyPork)
|
||||
*/
|
||||
public abstract class EntityModule implements IonBundled, Updateable {
|
||||
|
||||
|
||||
protected final Entity entity;
|
||||
|
||||
|
||||
public EntityModule(Entity entity) {
|
||||
|
||||
|
||||
public EntityModule(Entity entity)
|
||||
{
|
||||
this.entity = entity;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return whether the module should be saved into a world file
|
||||
*/
|
||||
public abstract boolean isModuleSaved();
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
@Stub
|
||||
public void load(IonDataBundle bundle)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
@Stub
|
||||
public void save(IonDataBundle bundle)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
@Stub
|
||||
public void update(double delta)
|
||||
|
||||
@@ -12,51 +12,52 @@ import mightypork.utils.math.algo.pathfinding.PathFinder;
|
||||
|
||||
/**
|
||||
* Basic Pathfinder implementation for entities
|
||||
*
|
||||
*
|
||||
* @author Ondřej Hruška (MightyPork)
|
||||
*/
|
||||
public class EntityPathFinder extends PathFinder {
|
||||
|
||||
|
||||
protected final Entity entity;
|
||||
|
||||
|
||||
public EntityPathFinder(Entity entity) {
|
||||
|
||||
|
||||
public EntityPathFinder(Entity entity)
|
||||
{
|
||||
this.entity = entity;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isAccessible(Coord pos)
|
||||
{
|
||||
return entity.getLevel().isWalkable(pos);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public int getCost(Coord from, Coord to)
|
||||
{
|
||||
return 10;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public int getMinCost()
|
||||
{
|
||||
return 10;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public Heuristic getHeuristic()
|
||||
{
|
||||
return PathFinder.CORNER_HEURISTIC;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public List<Move> getWalkSides()
|
||||
{
|
||||
return Moves.CARDINAL_SIDES;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -6,11 +6,11 @@ import mightypork.rogue.world.level.render.MapRenderContext;
|
||||
|
||||
/**
|
||||
* Entity renderer
|
||||
*
|
||||
*
|
||||
* @author Ondřej Hruška (MightyPork)
|
||||
*/
|
||||
public abstract class EntityRenderer {
|
||||
|
||||
|
||||
public abstract void render(MapRenderContext context);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package mightypork.rogue.world.entity;
|
||||
|
||||
/**
|
||||
* Type of an entity
|
||||
*
|
||||
*
|
||||
* @author Ondřej Hruška (MightyPork)
|
||||
*/
|
||||
public enum EntityType
|
||||
|
||||
@@ -7,47 +7,48 @@ import mightypork.utils.math.Calc;
|
||||
|
||||
|
||||
public class BossRatAi extends GrayRatAi {
|
||||
|
||||
|
||||
private final AiTimer healTimer = new AiTimer(0.5) {
|
||||
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
entity.health.addHealth(1); // heal
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public BossRatAi(Entity entity) {
|
||||
|
||||
|
||||
public BossRatAi(Entity entity)
|
||||
{
|
||||
super(entity);
|
||||
|
||||
|
||||
setAttackTime(0.3);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected int getAttackStrength()
|
||||
{
|
||||
return Calc.randInt(2, 3);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected int getPreyAbandonDistance()
|
||||
{
|
||||
return Calc.randInt(12, 18);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void update(double delta)
|
||||
{
|
||||
super.update(delta);
|
||||
|
||||
|
||||
healTimer.update(delta);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected double getStepTime()
|
||||
{
|
||||
|
||||
@@ -6,36 +6,37 @@ import mightypork.utils.math.Calc;
|
||||
|
||||
|
||||
public class BrownRatAi extends GrayRatAi {
|
||||
|
||||
public BrownRatAi(Entity entity) {
|
||||
|
||||
public BrownRatAi(Entity entity)
|
||||
{
|
||||
super(entity);
|
||||
|
||||
|
||||
setAttackTime(1.2);
|
||||
setScanTime(1.3);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected double getScanRadius()
|
||||
{
|
||||
return isIdle() ? Calc.randInt(2, 4) : Calc.randInt(5, 8);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected int getAttackStrength()
|
||||
{
|
||||
return Calc.randInt(2, 4);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected int getPreyAbandonDistance()
|
||||
{
|
||||
return Calc.randInt(7, 12);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected double getStepTime()
|
||||
{
|
||||
|
||||
@@ -13,62 +13,63 @@ import mightypork.utils.math.algo.pathfinding.PathFinder;
|
||||
|
||||
|
||||
public class EntityBossRat extends Entity {
|
||||
|
||||
|
||||
private EntityRenderer renderer;
|
||||
|
||||
|
||||
/** Navigation PFC */
|
||||
private final PathFinder pathf = new EntityPathFinder(this);
|
||||
|
||||
|
||||
private final BossRatAi ai = new BossRatAi(this);
|
||||
|
||||
|
||||
public EntityBossRat(EntityModel model, int eid) {
|
||||
|
||||
|
||||
public EntityBossRat(EntityModel model, int eid)
|
||||
{
|
||||
super(model, eid);
|
||||
|
||||
|
||||
addModule("ai", ai);
|
||||
pos.addMoveListener(ai);
|
||||
|
||||
|
||||
setDespawnDelay(1);
|
||||
|
||||
|
||||
health.setHealthMax(80);
|
||||
health.setHealth(80);
|
||||
health.setHitCooldownTime(0.33);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected EntityRenderer getRenderer()
|
||||
{
|
||||
if (renderer == null) {
|
||||
renderer = new EntityRendererMobLR(this, "sprite.rat.boss");
|
||||
}
|
||||
|
||||
|
||||
return renderer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public EntityType getType()
|
||||
{
|
||||
return EntityType.MONSTER;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public PathFinder getPathFinder()
|
||||
{
|
||||
return pathf;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void onKilled()
|
||||
{
|
||||
// send kill event to listeners, after the entity has despawned (disappeared)
|
||||
App.bus().sendDelayed(new GameWinEvent(), getDespawnDelay() * 1.5); // dramatic pause
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String getVisualName()
|
||||
{
|
||||
|
||||
@@ -13,69 +13,70 @@ import mightypork.utils.math.algo.pathfinding.PathFinder;
|
||||
|
||||
|
||||
public class EntityBrownRat extends Entity {
|
||||
|
||||
|
||||
private EntityRenderer renderer;
|
||||
|
||||
|
||||
private final PathFinder pathf = new EntityPathFinder(this);
|
||||
|
||||
|
||||
private final BrownRatAi ai = new BrownRatAi(this);
|
||||
|
||||
|
||||
public EntityBrownRat(EntityModel model, int eid) {
|
||||
|
||||
|
||||
public EntityBrownRat(EntityModel model, int eid)
|
||||
{
|
||||
super(model, eid);
|
||||
|
||||
|
||||
addModule("ai", ai);
|
||||
pos.addMoveListener(ai);
|
||||
|
||||
|
||||
setDespawnDelay(1);
|
||||
|
||||
|
||||
health.setHealthMax(16);
|
||||
health.setHealth(Calc.randInt(10, 16)); // tougher to kill
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected EntityRenderer getRenderer()
|
||||
{
|
||||
if (renderer == null) {
|
||||
renderer = new EntityRendererMobLR(this, "sprite.rat.brown");
|
||||
}
|
||||
|
||||
|
||||
return renderer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public EntityType getType()
|
||||
{
|
||||
return EntityType.MONSTER;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public PathFinder getPathFinder()
|
||||
{
|
||||
return pathf;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void onKilled()
|
||||
{
|
||||
// drop rat stuff
|
||||
|
||||
|
||||
if (Calc.rand.nextInt(2) != 0) {
|
||||
getLevel().dropNear(getCoord(), Items.MEAT.createItem());
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (Calc.rand.nextInt(3) == 0) {
|
||||
getLevel().dropNear(getCoord(), Items.CHEESE.createItem());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String getVisualName()
|
||||
{
|
||||
|
||||
@@ -13,74 +13,75 @@ import mightypork.utils.math.algo.pathfinding.PathFinder;
|
||||
|
||||
|
||||
public class EntityGrayRat extends Entity {
|
||||
|
||||
|
||||
private final PathFinder pathf = new EntityPathFinder(this);
|
||||
|
||||
|
||||
private final GrayRatAi ai = new GrayRatAi(this);
|
||||
|
||||
|
||||
private EntityRenderer renderer;
|
||||
|
||||
|
||||
public EntityGrayRat(EntityModel model, int eid) {
|
||||
|
||||
|
||||
public EntityGrayRat(EntityModel model, int eid)
|
||||
{
|
||||
super(model, eid);
|
||||
|
||||
|
||||
addModule("ai", ai);
|
||||
pos.addMoveListener(ai);
|
||||
|
||||
|
||||
setDespawnDelay(1);
|
||||
|
||||
|
||||
health.setHealthMax(7);
|
||||
health.setHealth(Calc.randInt(4, 7));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public PathFinder getPathFinder()
|
||||
{
|
||||
return pathf;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public EntityType getType()
|
||||
{
|
||||
return EntityType.MONSTER;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected EntityRenderer getRenderer()
|
||||
{
|
||||
if (renderer == null) {
|
||||
renderer = new EntityRendererMobLR(this, "sprite.rat.gray");
|
||||
}
|
||||
|
||||
|
||||
return renderer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void onKilled()
|
||||
{
|
||||
// drop rat stuff
|
||||
|
||||
|
||||
if (Calc.rand.nextInt(6) == 0) {
|
||||
getLevel().dropNear(getCoord(), Items.BONE.createItemDamaged(40));
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (Calc.rand.nextInt(3) == 0) {
|
||||
getLevel().dropNear(getCoord(), Items.CHEESE.createItem());
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (Calc.rand.nextInt(3) == 0) {
|
||||
getLevel().dropNear(getCoord(), Items.MEAT.createItem());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String getVisualName()
|
||||
{
|
||||
|
||||
@@ -2,7 +2,12 @@ package mightypork.rogue.world.entity.impl;
|
||||
|
||||
|
||||
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.render.EntityRendererMobLR;
|
||||
import mightypork.rogue.world.events.PlayerKilledEvent;
|
||||
@@ -14,31 +19,32 @@ import mightypork.utils.math.algo.pathfinding.PathFinder;
|
||||
|
||||
|
||||
public class EntityPlayer extends Entity {
|
||||
|
||||
|
||||
class PlayerAi extends EntityModule implements EntityMoveListener {
|
||||
|
||||
public PlayerAi(Entity entity) {
|
||||
|
||||
public PlayerAi(Entity entity)
|
||||
{
|
||||
super(entity);
|
||||
setDespawnDelay(2);
|
||||
|
||||
|
||||
health.setHealthMax(6); // 3 hearts
|
||||
health.fill(); // fill health bar to max
|
||||
health.setHitCooldownTime(0.5);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void onStepFinished()
|
||||
{
|
||||
entity.getLevel().explore(entity.pos.getCoord());
|
||||
fireEvt();
|
||||
|
||||
|
||||
// try to pickup items
|
||||
|
||||
|
||||
final Tile t = getLevel().getTile(getCoord());
|
||||
if (t.hasItem()) {
|
||||
final Item item = t.pickItem();
|
||||
|
||||
|
||||
if (item.pickUp(getWorld().getPlayer())) {
|
||||
// player picked item
|
||||
} else {
|
||||
@@ -46,101 +52,102 @@ public class EntityPlayer extends Entity {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void onPathFinished()
|
||||
{
|
||||
fireEvt();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void onPathInterrupted()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private void fireEvt()
|
||||
{
|
||||
App.bus().send(new PlayerStepEndEvent(EntityPlayer.this));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isModuleSaved()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
private PathFinder pathf;
|
||||
private EntityRenderer renderer;
|
||||
|
||||
|
||||
private final PlayerAi ai = new PlayerAi(this);
|
||||
|
||||
|
||||
public EntityPlayer(EntityModel model, int eid) {
|
||||
|
||||
|
||||
public EntityPlayer(EntityModel model, int eid)
|
||||
{
|
||||
super(model, eid);
|
||||
|
||||
|
||||
pos.setStepTime(0.25);
|
||||
|
||||
|
||||
addModule("ai", ai);
|
||||
pos.addMoveListener(ai);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public PathFinder getPathFinder()
|
||||
{
|
||||
if (pathf == null) {
|
||||
pathf = new EntityPathFinder(this) {
|
||||
|
||||
|
||||
@Override
|
||||
public int getCost(Coord from, Coord to)
|
||||
{
|
||||
if (!getLevel().getTile(pos.getCoord()).isExplored()) {
|
||||
return 1000; // avoid unexplored, but allow them if there's no other way
|
||||
}
|
||||
|
||||
|
||||
return super.getCost(from, to);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
return pathf;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected EntityRenderer getRenderer()
|
||||
{
|
||||
if (renderer == null) {
|
||||
renderer = new EntityRendererMobLR(this, "sprite.player");
|
||||
}
|
||||
|
||||
|
||||
return renderer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public EntityType getType()
|
||||
{
|
||||
return EntityType.PLAYER;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void onKilled()
|
||||
{
|
||||
// send kill event to listeners, after the entity has despawned (disappeared)
|
||||
App.bus().sendDelayed(new PlayerKilledEvent(), getDespawnDelay());
|
||||
|
||||
|
||||
getWorld().getConsole().msgDie(lastAttacker);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String getVisualName()
|
||||
{
|
||||
|
||||
@@ -6,43 +6,44 @@ import mightypork.utils.math.Calc;
|
||||
|
||||
|
||||
public class GrayRatAi extends MonsterAi {
|
||||
|
||||
public GrayRatAi(Entity entity) {
|
||||
|
||||
public GrayRatAi(Entity entity)
|
||||
{
|
||||
super(entity);
|
||||
|
||||
|
||||
setAttackTime(1.2);
|
||||
setScanTime(1.5);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected double getScanRadius()
|
||||
{
|
||||
return isIdle() ? Calc.randInt(2, 3) : Calc.randInt(4, 6);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected double getAttackDistance()
|
||||
{
|
||||
return 1.1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected int getAttackStrength()
|
||||
{
|
||||
return Calc.randInt(1, 2);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected int getPreyAbandonDistance()
|
||||
{
|
||||
return Calc.randInt(7, 11);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected double getStepTime()
|
||||
{
|
||||
|
||||
@@ -20,11 +20,11 @@ import mightypork.utils.math.algo.pathfinding.PathFinderProxy;
|
||||
|
||||
|
||||
public class MonsterAi extends EntityModule implements EntityMoveListener {
|
||||
|
||||
|
||||
private boolean chasing = false;
|
||||
|
||||
|
||||
private final AiTimer timerFindPrey = new AiTimer(3) {
|
||||
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
@@ -32,143 +32,144 @@ public class MonsterAi extends EntityModule implements EntityMoveListener {
|
||||
lookForTarget();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private final AiTimer timerAttack = new AiTimer(1) {
|
||||
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
if (!isChasing()) return;
|
||||
|
||||
|
||||
final Entity prey = getPreyEntity();
|
||||
|
||||
|
||||
if (prey == null || prey.isDead()) return;
|
||||
|
||||
|
||||
attackPrey(prey);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private final AiTimer timerRandomWalk = new AiTimer(0.2) {
|
||||
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
if (!isIdle()) return;
|
||||
|
||||
|
||||
// annoyed by attacking.
|
||||
if (entity.getLastAttackTime() < 0.5) {
|
||||
lookForTarget();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (entity.pos.isMoving()) return;
|
||||
|
||||
|
||||
if (Calc.rand.nextInt(10) == 0) {
|
||||
entity.pos.addStep(Moves.randomCardinal());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private PathFinder noDoorPf;
|
||||
|
||||
|
||||
/** Prey id */
|
||||
private int preyId = -1;
|
||||
|
||||
|
||||
public MonsterAi(final Entity entity) {
|
||||
|
||||
|
||||
public MonsterAi(final Entity entity)
|
||||
{
|
||||
super(entity);
|
||||
|
||||
|
||||
noDoorPf = new PathFinderProxy(entity.getPathFinder()) {
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isAccessible(Coord pos)
|
||||
{
|
||||
final Tile t = entity.getLevel().getTile(pos);
|
||||
if (t.isDoor()) return false;
|
||||
|
||||
|
||||
return super.isAccessible(pos);
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
noDoorPf.setIgnoreEnd(true);
|
||||
|
||||
|
||||
timerAttack.start();
|
||||
timerFindPrey.start();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void onStepFinished()
|
||||
{
|
||||
if (entity.isDead()) return;
|
||||
|
||||
|
||||
if (isChasing()) {
|
||||
final Entity prey = getPreyEntity();
|
||||
if (!isPreyValid(prey)) {
|
||||
stopChasing();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (!isPreyInAttackRange(prey)) {
|
||||
stepTowardsPrey(prey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void onPathFinished()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void onPathInterrupted()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void save(IonDataBundle bundle)
|
||||
{
|
||||
bundle.putBundled("tscan", timerFindPrey);
|
||||
bundle.putBundled("tattack", timerAttack);
|
||||
|
||||
|
||||
bundle.put("chasing", chasing);
|
||||
|
||||
|
||||
bundle.put("prey", preyId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void load(IonDataBundle bundle)
|
||||
{
|
||||
bundle.loadBundled("tscan", timerFindPrey);
|
||||
bundle.loadBundled("tattack", timerAttack);
|
||||
|
||||
|
||||
chasing = bundle.get("chasing", chasing);
|
||||
|
||||
|
||||
preyId = bundle.get("prey", preyId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isModuleSaved()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void update(double delta)
|
||||
{
|
||||
if (entity.isDead()) return;
|
||||
|
||||
|
||||
timerFindPrey.update(delta);
|
||||
timerAttack.update(delta);
|
||||
timerRandomWalk.update(delta);
|
||||
|
||||
|
||||
// go after the prey
|
||||
if (isChasing() && !entity.pos.isMoving()) {
|
||||
final Entity prey = getPreyEntity();
|
||||
@@ -176,170 +177,170 @@ public class MonsterAi extends EntityModule implements EntityMoveListener {
|
||||
// prey killed and cleaned from level
|
||||
stopChasing();
|
||||
}
|
||||
|
||||
|
||||
if (!isPreyInAttackRange(prey)) {
|
||||
stepTowardsPrey(prey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public boolean isIdle()
|
||||
{
|
||||
return !chasing;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public boolean isChasing()
|
||||
{
|
||||
return chasing;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private void lookForTarget()
|
||||
{
|
||||
if (entity.isDead()) return;
|
||||
|
||||
|
||||
final Entity prey = entity.getLevel().getClosestEntity(entity.pos.getVisualPos(), EntityType.PLAYER, getScanRadius());
|
||||
if (prey != null) {
|
||||
|
||||
|
||||
// check if reachable without leaving room
|
||||
final List<Coord> noDoorPath = noDoorPf.findPath(entity.getCoord(), prey.getCoord());
|
||||
|
||||
|
||||
if (noDoorPath == null) return; // cant reach, give up
|
||||
|
||||
|
||||
startChasing(prey);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private Entity getPreyEntity()
|
||||
{
|
||||
return entity.getLevel().getEntity(preyId);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private boolean isPreyInAttackRange(Entity prey)
|
||||
{
|
||||
return prey.getCoord().dist(entity.getCoord()) <= getAttackDistance();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private boolean isPreyValid(Entity prey)
|
||||
{
|
||||
return prey != null && !prey.isDead();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private void startChasing(Entity prey)
|
||||
{
|
||||
if (entity.isDead()) return;
|
||||
|
||||
|
||||
preyId = prey.getEntityId();
|
||||
chasing = true;
|
||||
|
||||
|
||||
entity.pos.setStepTime(getStepTime());
|
||||
|
||||
|
||||
// follow this one prey
|
||||
timerFindPrey.pause();
|
||||
|
||||
|
||||
onStepFinished(); // go towards prey
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private void stopChasing()
|
||||
{
|
||||
chasing = false;
|
||||
|
||||
|
||||
entity.pos.setStepTime(getStepTime());
|
||||
|
||||
|
||||
preyId = -1;
|
||||
timerFindPrey.restart();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private List<Move> getPathToPrey(Entity prey)
|
||||
{
|
||||
if (!isPreyValid(prey)) return null;
|
||||
|
||||
|
||||
return entity.getPathFinder().findPathRelative(entity.getCoord(), prey.getCoord(), false, true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private void stepTowardsPrey(Entity prey)
|
||||
{
|
||||
if (entity.isDead()) return;
|
||||
|
||||
|
||||
if (!isPreyValid(prey)) return;
|
||||
|
||||
|
||||
// if close enough
|
||||
if (isPreyInAttackRange(prey)) {
|
||||
// attack using the timed loop
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
final List<Move> preyPath = getPathToPrey(prey);
|
||||
|
||||
|
||||
if (preyPath == null || preyPath.size() > getPreyAbandonDistance()) {
|
||||
stopChasing();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
entity.pos.cancelPath();
|
||||
entity.pos.addSteps(preyPath);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private void attackPrey(Entity prey)
|
||||
{
|
||||
if (entity.isDead()) return;
|
||||
|
||||
|
||||
if (!isPreyInAttackRange(prey)) return;
|
||||
|
||||
|
||||
prey.receiveAttack(entity, getAttackStrength());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
protected void setAttackTime(double secs)
|
||||
{
|
||||
timerAttack.setDuration(secs);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
protected void setScanTime(double secs)
|
||||
{
|
||||
timerFindPrey.setDuration(secs);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Stub
|
||||
protected double getScanRadius()
|
||||
{
|
||||
return isIdle() ? Calc.randInt(1, 3) : Calc.randInt(4, 8); // For override
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Stub
|
||||
protected int getPreyAbandonDistance()
|
||||
{
|
||||
return Calc.randInt(5, 8); // For override
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Stub
|
||||
protected double getAttackDistance()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Stub
|
||||
protected int getAttackStrength()
|
||||
{
|
||||
return 1; // For override
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Stub
|
||||
protected double getStepTime()
|
||||
{
|
||||
|
||||
@@ -9,19 +9,20 @@ import mightypork.utils.math.Calc;
|
||||
|
||||
|
||||
public class EntityModuleHealth extends EntityModule {
|
||||
|
||||
public EntityModuleHealth(Entity entity) {
|
||||
|
||||
public EntityModuleHealth(Entity entity)
|
||||
{
|
||||
super(entity);
|
||||
}
|
||||
|
||||
|
||||
protected int health = 1;
|
||||
protected int maxHealth = 1;
|
||||
private double hitCooldownTime = 0.36;
|
||||
protected boolean dead = false;
|
||||
|
||||
|
||||
private double timeSinceLastDamage = Integer.MAX_VALUE;
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void load(IonDataBundle bundle)
|
||||
{
|
||||
@@ -29,8 +30,8 @@ public class EntityModuleHealth extends EntityModule {
|
||||
maxHealth = bundle.get("max_health", maxHealth);
|
||||
dead = bundle.get("dead", dead);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void save(IonDataBundle bundle)
|
||||
{
|
||||
@@ -38,86 +39,86 @@ public class EntityModuleHealth extends EntityModule {
|
||||
bundle.put("max_health", maxHealth);
|
||||
bundle.put("dead", dead);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isModuleSaved()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public int getHealth()
|
||||
{
|
||||
return health;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void setHealth(int health)
|
||||
{
|
||||
|
||||
|
||||
this.health = Calc.clamp(health, 0, maxHealth);
|
||||
|
||||
|
||||
if (health <= 0) {
|
||||
setDead(true);
|
||||
entity.onKilled();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public int getHealthMax()
|
||||
{
|
||||
return maxHealth;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void setHealthMax(int maxHealth)
|
||||
{
|
||||
if (maxHealth <= 0) throw new IllegalValueException("Max health out of allowed range: " + maxHealth);
|
||||
this.maxHealth = maxHealth;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public boolean isDead()
|
||||
{
|
||||
return dead;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void setDead(boolean dead)
|
||||
{
|
||||
this.dead = dead;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void receiveDamage(int attackStrength)
|
||||
{
|
||||
if (timeSinceLastDamage < hitCooldownTime) return;
|
||||
|
||||
|
||||
setHealth(health - attackStrength);
|
||||
timeSinceLastDamage = 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void addHealth(int healthPoints)
|
||||
{
|
||||
setHealth(health + healthPoints);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void fill()
|
||||
{
|
||||
setHealth(maxHealth);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void update(double delta)
|
||||
{
|
||||
if (timeSinceLastDamage < 3600) timeSinceLastDamage += delta;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return seconds since last attack received (can be used for rendering)
|
||||
*/
|
||||
@@ -125,11 +126,11 @@ public class EntityModuleHealth extends EntityModule {
|
||||
{
|
||||
return timeSinceLastDamage;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Set how long after hit another hit can be received.
|
||||
*
|
||||
*
|
||||
* @param secs
|
||||
*/
|
||||
public void setHitCooldownTime(double secs)
|
||||
|
||||
@@ -16,27 +16,28 @@ import mightypork.utils.math.constraints.vect.VectConst;
|
||||
|
||||
|
||||
public class EntityModulePosition extends EntityModule {
|
||||
|
||||
|
||||
/** Last pos, will be freed upon finishing move */
|
||||
private Coord lastPos = new Coord(0, 0);
|
||||
private boolean walking = false;
|
||||
|
||||
|
||||
private final Queue<Move> path = new LinkedList<>();
|
||||
private final EntityPos entityPos = new EntityPos();
|
||||
private double stepTime = 0.5;
|
||||
|
||||
|
||||
// marks for simple renderers
|
||||
public int lastXDir = 1;
|
||||
public int lastYDir = 1;
|
||||
|
||||
|
||||
private final Set<EntityMoveListener> moveListeners = new LinkedHashSet<>();
|
||||
|
||||
|
||||
public EntityModulePosition(Entity entity) {
|
||||
|
||||
|
||||
public EntityModulePosition(Entity entity)
|
||||
{
|
||||
super(entity);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void save(IonDataBundle bundle)
|
||||
{
|
||||
@@ -45,43 +46,43 @@ public class EntityModulePosition extends EntityModule {
|
||||
bundle.putBundled("pos", entityPos);
|
||||
bundle.put("step_time", stepTime);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void load(IonDataBundle bundle)
|
||||
{
|
||||
bundle.loadSequence("path", path);
|
||||
lastPos = bundle.get("lpos", lastPos);
|
||||
bundle.loadBundled("pos", entityPos);
|
||||
|
||||
|
||||
stepTime = bundle.get("step_time", stepTime);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isModuleSaved()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Set coord without animation
|
||||
*
|
||||
*
|
||||
* @param coord coord
|
||||
*/
|
||||
public void setCoord(Coord coord)
|
||||
{
|
||||
freeTile(); // release old tile
|
||||
|
||||
|
||||
entityPos.setTo(coord);
|
||||
lastPos.setTo(coord);
|
||||
cancelPath(); // discard remaining steps
|
||||
|
||||
|
||||
occupyTile();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Occupy current tile in level
|
||||
*/
|
||||
@@ -91,8 +92,8 @@ public class EntityModulePosition extends EntityModule {
|
||||
entity.getLevel().occupyTile(getCoord());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* free current tile in level
|
||||
*/
|
||||
@@ -102,8 +103,8 @@ public class EntityModulePosition extends EntityModule {
|
||||
entity.getLevel().freeTile(getCoord());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @param delta delta time
|
||||
*/
|
||||
@@ -111,47 +112,47 @@ public class EntityModulePosition extends EntityModule {
|
||||
public void update(double delta)
|
||||
{
|
||||
if (entity.isDead()) return; // corpses dont walk
|
||||
|
||||
|
||||
if (!entityPos.isFinished()) {
|
||||
entityPos.update(delta);
|
||||
}
|
||||
|
||||
|
||||
if (walking && entityPos.isFinished()) {
|
||||
walking = false;
|
||||
|
||||
|
||||
for (final EntityMoveListener l : moveListeners) {
|
||||
l.onStepFinished();
|
||||
}
|
||||
|
||||
|
||||
if (path.isEmpty()) {
|
||||
for (final EntityMoveListener l : moveListeners) {
|
||||
l.onPathFinished();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!walking && !path.isEmpty()) {
|
||||
|
||||
|
||||
walking = true;
|
||||
|
||||
|
||||
final Move step = path.poll();
|
||||
|
||||
|
||||
final Coord planned = entityPos.getCoord().add(step.toCoord());
|
||||
|
||||
|
||||
if (!entity.getLevel().isWalkable(planned)) {
|
||||
cancelPath();
|
||||
|
||||
|
||||
for (final EntityMoveListener l : moveListeners) {
|
||||
l.onPathInterrupted();
|
||||
}
|
||||
|
||||
|
||||
walking = false;
|
||||
} else {
|
||||
|
||||
|
||||
// tmp for renderer
|
||||
if (step.x() != 0) this.lastXDir = step.x();
|
||||
if (step.y() != 0) this.lastYDir = step.y();
|
||||
|
||||
|
||||
freeTile();
|
||||
lastPos.setTo(entityPos.getCoord());
|
||||
entityPos.walk(step, stepTime);
|
||||
@@ -159,8 +160,8 @@ public class EntityModulePosition extends EntityModule {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return true if path buffer is empty
|
||||
*/
|
||||
@@ -168,21 +169,21 @@ public class EntityModulePosition extends EntityModule {
|
||||
{
|
||||
return entityPos.isFinished() && path.isEmpty();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Add a step to path buffer
|
||||
*
|
||||
*
|
||||
* @param step
|
||||
*/
|
||||
public void addStep(Move step)
|
||||
{
|
||||
if (path.isEmpty() && !canGoTo(step)) return;
|
||||
|
||||
|
||||
path.add(step);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Discard steps in buffer
|
||||
*/
|
||||
@@ -190,11 +191,11 @@ public class EntityModulePosition extends EntityModule {
|
||||
{
|
||||
path.clear();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Find path to
|
||||
*
|
||||
*
|
||||
* @param target
|
||||
* @return path found
|
||||
*/
|
||||
@@ -202,36 +203,36 @@ public class EntityModulePosition extends EntityModule {
|
||||
{
|
||||
if (target.equals(getCoord())) return true;
|
||||
final List<Move> newPath = entity.getPathFinder().findPathRelative(entityPos.getCoord(), target);
|
||||
|
||||
|
||||
if (newPath == null) return false;
|
||||
cancelPath();
|
||||
addSteps(newPath);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Add a move listener. If already present, do nothing.
|
||||
*
|
||||
*
|
||||
* @param listener the listener
|
||||
*/
|
||||
public void addMoveListener(EntityMoveListener listener)
|
||||
{
|
||||
moveListeners.add(listener);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Add steps to path buffer
|
||||
*
|
||||
*
|
||||
* @param path steps
|
||||
*/
|
||||
public void addSteps(List<Move> path)
|
||||
{
|
||||
this.path.addAll(path);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return coord in level
|
||||
*/
|
||||
@@ -239,19 +240,19 @@ public class EntityModulePosition extends EntityModule {
|
||||
{
|
||||
return entityPos.getCoord();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Set step time (seconds)
|
||||
*
|
||||
*
|
||||
* @param stepTime step time
|
||||
*/
|
||||
public void setStepTime(double stepTime)
|
||||
{
|
||||
this.stepTime = stepTime;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return step progress 0..1
|
||||
*/
|
||||
@@ -259,8 +260,8 @@ public class EntityModulePosition extends EntityModule {
|
||||
{
|
||||
return entityPos.getProgress();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return visual pos in level; interpolated from last to new coord
|
||||
*/
|
||||
@@ -268,29 +269,29 @@ public class EntityModulePosition extends EntityModule {
|
||||
{
|
||||
return entityPos.getVisualPos();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public boolean isMoving()
|
||||
{
|
||||
return walking;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public boolean hasPath()
|
||||
{
|
||||
return isMoving() || !path.isEmpty();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public boolean canGoTo(Move side)
|
||||
{
|
||||
return entity.getPathFinder().isAccessible(getCoord().add(side));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public Coord getLastPos()
|
||||
{
|
||||
return lastPos;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -2,22 +2,22 @@ package mightypork.rogue.world.entity.modules;
|
||||
|
||||
|
||||
public interface EntityMoveListener {
|
||||
|
||||
|
||||
/**
|
||||
* One step of a path finished
|
||||
*/
|
||||
void onStepFinished();
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Scheduled path finished
|
||||
*/
|
||||
void onPathFinished();
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Path was interrupted (bumped into a wall or entity)
|
||||
*/
|
||||
void onPathInterrupted();
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -14,142 +14,145 @@ import mightypork.utils.math.constraints.vect.VectConst;
|
||||
|
||||
/**
|
||||
* Entity position
|
||||
*
|
||||
*
|
||||
* @author Ondřej Hruška (MightyPork)
|
||||
*/
|
||||
class EntityPos implements IonBundled, Updateable {
|
||||
|
||||
|
||||
private Coord coord = new Coord(0, 0);
|
||||
private final VectAnimated walkOffset = new VectAnimated(Vect.ZERO, Easing.LINEAR);
|
||||
|
||||
|
||||
public EntityPos(Coord pos) {
|
||||
|
||||
|
||||
public EntityPos(Coord pos)
|
||||
{
|
||||
this.coord.setTo(pos);
|
||||
}
|
||||
|
||||
|
||||
public EntityPos(int x, int y) {
|
||||
|
||||
|
||||
public EntityPos(int x, int y)
|
||||
{
|
||||
this.coord.setTo(x, y);
|
||||
}
|
||||
|
||||
|
||||
public EntityPos() {
|
||||
|
||||
|
||||
public EntityPos()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public double getProgress()
|
||||
{
|
||||
return walkOffset.getProgress();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void load(IonDataBundle in)
|
||||
{
|
||||
coord = in.get("pos", coord);
|
||||
walkOffset.reset();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void save(IonDataBundle out)
|
||||
{
|
||||
out.put("pos", coord);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public int x()
|
||||
{
|
||||
return coord.x;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public int y()
|
||||
{
|
||||
return coord.y;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public double visualX()
|
||||
{
|
||||
return coord.x + walkOffset.x();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public double visualY()
|
||||
{
|
||||
return coord.y + walkOffset.y();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public double visualXOffset()
|
||||
{
|
||||
return walkOffset.x();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public double visualYOffset()
|
||||
{
|
||||
return walkOffset.y();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void setTo(int x, int y)
|
||||
{
|
||||
coord.setTo(x, y);
|
||||
walkOffset.reset();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void setTo(EntityPos pos)
|
||||
{
|
||||
|
||||
|
||||
setTo(pos.getCoord());
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void setTo(Coord c)
|
||||
{
|
||||
coord.setTo(c);
|
||||
walkOffset.reset();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "EntityPos{" + coord + "}";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void walk(Move step, double secs)
|
||||
{
|
||||
setTo(coord.x + step.x(), coord.y + step.y());
|
||||
walkOffset.setTo(-step.x(), -step.y());
|
||||
walkOffset.animate(0, 0, 0, secs);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void update(double delta)
|
||||
{
|
||||
walkOffset.update(delta);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public boolean isFinished()
|
||||
{
|
||||
return walkOffset.isFinished();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public Coord getCoord()
|
||||
{
|
||||
return coord;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
@@ -158,8 +161,8 @@ class EntityPos implements IonBundled, Updateable {
|
||||
result = prime * result + ((coord == null) ? 0 : coord.hashCode());
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
{
|
||||
@@ -172,8 +175,8 @@ class EntityPos implements IonBundled, Updateable {
|
||||
} else if (!coord.equals(other.coord)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public VectConst getVisualPos()
|
||||
{
|
||||
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
|
||||
* flipped for walking left.
|
||||
*
|
||||
*
|
||||
* @author Ondřej Hruška (MightyPork)
|
||||
*/
|
||||
public class EntityRendererMobLR extends EntityRenderer {
|
||||
|
||||
|
||||
private final TxSheet sheet;
|
||||
|
||||
|
||||
protected final Entity entity;
|
||||
|
||||
|
||||
private final NumVar animRedVar = Num.makeVar(0);
|
||||
|
||||
|
||||
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.sheet = Res.getTxSheet(sheetKey);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void render(MapRenderContext context)
|
||||
{
|
||||
final double hurtTime = entity.health.getTimeSinceLastDamage();
|
||||
|
||||
|
||||
TxQuad q = sheet.getQuad(Calc.frag(entity.pos.getProgress()));
|
||||
|
||||
|
||||
if (entity.pos.lastXDir == -1) q = q.flipX();
|
||||
|
||||
|
||||
final Rect tileRect = context.getRectForTile(entity.pos.getCoord());
|
||||
final double w = tileRect.width().value();
|
||||
final Vect visualPos = entity.pos.getVisualPos();
|
||||
|
||||
|
||||
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);
|
||||
spriteRect = spriteRect.shrink(w * 0.05);
|
||||
|
||||
|
||||
animRedVar.setTo(hurtTime / 0.3);
|
||||
|
||||
|
||||
App.gfx().pushGeometry();
|
||||
|
||||
|
||||
App.gfx().translate(spriteRect.center());
|
||||
|
||||
|
||||
if (entity.isDead()) {
|
||||
App.gfx().rotateZ(Calc.clamp(hurtTime / 0.3, 0, 1) * 90);
|
||||
}
|
||||
|
||||
|
||||
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().popGeometry();
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import mightypork.utils.eventbus.BusEvent;
|
||||
|
||||
|
||||
public class GameWinEvent extends BusEvent<GameWinHandler> {
|
||||
|
||||
|
||||
@Override
|
||||
protected void handleBy(GameWinHandler handler)
|
||||
{
|
||||
|
||||
@@ -2,7 +2,7 @@ package mightypork.rogue.world.events;
|
||||
|
||||
|
||||
public interface GameWinHandler {
|
||||
|
||||
|
||||
void onGameWon();
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -2,6 +2,6 @@ package mightypork.rogue.world.events;
|
||||
|
||||
|
||||
public interface PlayerDeathHandler {
|
||||
|
||||
|
||||
void onPlayerKilled();
|
||||
}
|
||||
|
||||
@@ -6,13 +6,13 @@ import mightypork.utils.eventbus.BusEvent;
|
||||
|
||||
|
||||
public class PlayerKilledEvent extends BusEvent<PlayerDeathHandler> {
|
||||
|
||||
|
||||
@Override
|
||||
protected void handleBy(PlayerDeathHandler handler)
|
||||
{
|
||||
// not dead, discard event.
|
||||
if (!WorldProvider.get().getPlayer().isDead()) return;
|
||||
|
||||
|
||||
handler.onPlayerKilled();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,20 +8,21 @@ import mightypork.utils.eventbus.events.flags.NotLoggedEvent;
|
||||
|
||||
@NotLoggedEvent
|
||||
public class PlayerStepEndEvent extends BusEvent<PlayerStepEndListener> {
|
||||
|
||||
|
||||
private final EntityPlayer player;
|
||||
|
||||
|
||||
public PlayerStepEndEvent(EntityPlayer player) {
|
||||
|
||||
|
||||
public PlayerStepEndEvent(EntityPlayer player)
|
||||
{
|
||||
super();
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected void handleBy(PlayerStepEndListener handler)
|
||||
{
|
||||
handler.onStepFinished(player);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -5,6 +5,6 @@ import mightypork.rogue.world.entity.impl.EntityPlayer;
|
||||
|
||||
|
||||
public interface PlayerStepEndListener {
|
||||
|
||||
|
||||
void onStepFinished(EntityPlayer player);
|
||||
}
|
||||
|
||||
@@ -6,15 +6,15 @@ import mightypork.utils.eventbus.BusEvent;
|
||||
|
||||
/**
|
||||
* Player wants to go up
|
||||
*
|
||||
*
|
||||
* @author Ondřej Hruška (MightyPork)
|
||||
*/
|
||||
public class WorldAscendRequest extends BusEvent<WorldAscendRequestListener> {
|
||||
|
||||
|
||||
@Override
|
||||
protected void handleBy(WorldAscendRequestListener handler)
|
||||
{
|
||||
handler.onAscendRequest();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package mightypork.rogue.world.events;
|
||||
|
||||
|
||||
public interface WorldAscendRequestListener {
|
||||
|
||||
|
||||
/**
|
||||
* Player clicked up-stairs
|
||||
*/
|
||||
|
||||
@@ -5,11 +5,11 @@ import mightypork.utils.eventbus.BusEvent;
|
||||
|
||||
|
||||
public class WorldDescendRequest extends BusEvent<WorldDescendRequestListener> {
|
||||
|
||||
|
||||
@Override
|
||||
protected void handleBy(WorldDescendRequestListener handler)
|
||||
{
|
||||
handler.onDescendRequest();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package mightypork.rogue.world.events;
|
||||
|
||||
|
||||
public interface WorldDescendRequestListener {
|
||||
|
||||
|
||||
/**
|
||||
* Player clicked down-stairs
|
||||
*/
|
||||
|
||||
@@ -7,25 +7,26 @@ import mightypork.utils.eventbus.BusEvent;
|
||||
|
||||
/**
|
||||
* Toggle world pause state
|
||||
*
|
||||
*
|
||||
* @author Ondřej Hruška (MightyPork)
|
||||
*/
|
||||
public class WorldPauseRequest extends BusEvent<World> {
|
||||
|
||||
|
||||
public static enum PauseAction
|
||||
{
|
||||
PAUSE, RESUME, TOGGLE;
|
||||
}
|
||||
|
||||
|
||||
private final PauseAction op;
|
||||
|
||||
|
||||
public WorldPauseRequest(PauseAction op) {
|
||||
|
||||
|
||||
public WorldPauseRequest(PauseAction op)
|
||||
{
|
||||
super();
|
||||
this.op = op;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected void handleBy(World handler)
|
||||
{
|
||||
@@ -33,14 +34,14 @@ public class WorldPauseRequest extends BusEvent<World> {
|
||||
handler.pause();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (op == PauseAction.RESUME) {
|
||||
handler.resume();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// else
|
||||
|
||||
|
||||
// toggle paused state
|
||||
if (!handler.isPaused()) {
|
||||
handler.pause();
|
||||
@@ -48,5 +49,5 @@ public class WorldPauseRequest extends BusEvent<World> {
|
||||
handler.resume();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -16,78 +16,82 @@ import mightypork.utils.math.algo.Coord;
|
||||
|
||||
|
||||
public class LevelBuilder {
|
||||
|
||||
|
||||
public static enum BuildOrder
|
||||
{
|
||||
FIRST, MIDDLE, LAST
|
||||
}
|
||||
|
||||
|
||||
private class RoomEntry {
|
||||
|
||||
|
||||
int count;
|
||||
RoomBuilder room;
|
||||
boolean important;
|
||||
|
||||
|
||||
public RoomEntry(RoomBuilder room, int count, boolean important) {
|
||||
|
||||
|
||||
public RoomEntry(RoomBuilder room, int count, boolean important)
|
||||
{
|
||||
this.count = count;
|
||||
this.room = room;
|
||||
this.important = important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class ItemEntry {
|
||||
|
||||
|
||||
Item item;
|
||||
boolean important;
|
||||
|
||||
|
||||
public ItemEntry(Item item, boolean important) {
|
||||
|
||||
|
||||
public ItemEntry(Item item, boolean important)
|
||||
{
|
||||
this.item = item;
|
||||
this.important = important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class EntityEntry {
|
||||
|
||||
|
||||
Entity entity;
|
||||
boolean important;
|
||||
|
||||
|
||||
public EntityEntry(Entity item, boolean important) {
|
||||
|
||||
|
||||
public EntityEntry(Entity item, boolean important)
|
||||
{
|
||||
this.entity = item;
|
||||
this.important = important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private final ScratchMap map;
|
||||
private final Random rand;
|
||||
private boolean built;
|
||||
|
||||
|
||||
private final LinkedList<RoomEntry> roomsFirst = new LinkedList<>();
|
||||
private final LinkedList<RoomEntry> roomsMiddle = new LinkedList<>();
|
||||
private final LinkedList<RoomEntry> roomsLast = new LinkedList<>();
|
||||
|
||||
|
||||
private final LinkedList<ItemEntry> items = new LinkedList<>();
|
||||
private final LinkedList<EntityEntry> entities = new LinkedList<>();
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* make a new level builder instance.
|
||||
*
|
||||
*
|
||||
* @param max_size max map size (square side - tiles)
|
||||
* @param theme tiles theme
|
||||
* @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.map = new ScratchMap(max_size, theme, rand);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Add a single room to the room buffer.
|
||||
*
|
||||
*
|
||||
* @param room room builder
|
||||
* @param order build order
|
||||
* @param important try harder and throw error on fail
|
||||
@@ -96,11 +100,11 @@ public class LevelBuilder {
|
||||
{
|
||||
addRoom(room, Range.make(1, 1), order, important);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Add multiple rooms of the type to the room buffer.
|
||||
*
|
||||
*
|
||||
* @param room room builder
|
||||
* @param count number of rooms to build
|
||||
* @param order build order
|
||||
@@ -109,94 +113,94 @@ public class LevelBuilder {
|
||||
public void addRoom(RoomBuilder room, Range count, BuildOrder order, boolean important)
|
||||
{
|
||||
final List<RoomEntry> list;
|
||||
|
||||
|
||||
switch (order) {
|
||||
case FIRST:
|
||||
list = roomsFirst;
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
case MIDDLE:
|
||||
list = roomsMiddle;
|
||||
break;
|
||||
|
||||
|
||||
case LAST:
|
||||
list = roomsLast;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
list.add(new RoomEntry(room, count.randInt(rand), important));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private void buildRooms(LinkedList<RoomEntry> list)
|
||||
{
|
||||
while (!list.isEmpty()) {
|
||||
|
||||
|
||||
Collections.shuffle(list, rand);
|
||||
|
||||
|
||||
for (final Iterator<RoomEntry> iter = list.iterator(); iter.hasNext();) {
|
||||
final RoomEntry rge = iter.next();
|
||||
|
||||
|
||||
map.addRoom(rge.room, rge.important);
|
||||
|
||||
|
||||
if ((--rge.count) <= 0) {
|
||||
iter.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private void buildCorridors() throws WorldGenError
|
||||
{
|
||||
map.buildCorridors();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private void buildEntities()
|
||||
{
|
||||
for (final EntityEntry entry : entities) {
|
||||
final int tries = entry.important ? 200 : 50;
|
||||
final boolean success = map.addEntityInMap(entry.entity, tries);
|
||||
|
||||
|
||||
if (entry.important && !success) {
|
||||
throw new WorldGenError("Could not place an important entity: " + entry.entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private void buildItems()
|
||||
{
|
||||
for (final ItemEntry entry : items) {
|
||||
final int tries = entry.important ? 200 : 50;
|
||||
final boolean success = map.addItemInMap(entry.item, tries);
|
||||
|
||||
|
||||
if (entry.important && !success) {
|
||||
throw new WorldGenError("Could not place an important item: " + entry.item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private void writeToMap()
|
||||
{
|
||||
buildRooms(roomsFirst);
|
||||
buildRooms(roomsMiddle);
|
||||
buildRooms(roomsLast);
|
||||
buildCorridors();
|
||||
|
||||
|
||||
map.fixGlitches();
|
||||
|
||||
|
||||
buildItems();
|
||||
buildEntities();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Write to a new level instance.
|
||||
*
|
||||
*
|
||||
* @param world level's world
|
||||
* @return the level
|
||||
* @throws WorldGenError on error in generation
|
||||
@@ -207,22 +211,22 @@ public class LevelBuilder {
|
||||
throw new WorldGenError("Level already built.");
|
||||
}
|
||||
built = true;
|
||||
|
||||
|
||||
writeToMap();
|
||||
|
||||
|
||||
final Coord size = map.getNeededSize();
|
||||
final Level lvl = new Level(size.x, size.y);
|
||||
lvl.setWorld(world); // important for creating entities
|
||||
|
||||
|
||||
map.writeToLevel(lvl);
|
||||
|
||||
|
||||
return lvl;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Add an item to be added to the level when tiles are built.
|
||||
*
|
||||
*
|
||||
* @param item item to add
|
||||
* @param important try harder and throw error on fail
|
||||
* @throws WorldGenError on fail
|
||||
@@ -231,12 +235,12 @@ public class LevelBuilder {
|
||||
{
|
||||
items.add(new ItemEntry(item, important));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Add an entity to be added to the level when tiles are built.<br>
|
||||
* It's EID will be assigned during writing to level.
|
||||
*
|
||||
*
|
||||
* @param entity entity to add
|
||||
* @param important try harder and throw error on fail
|
||||
* @throws WorldGenError on fail
|
||||
@@ -245,5 +249,5 @@ public class LevelBuilder {
|
||||
{
|
||||
entities.add(new EntityEntry(entity, important));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -6,31 +6,31 @@ import mightypork.rogue.world.tile.TileModel;
|
||||
|
||||
/**
|
||||
* Map theme to use for building
|
||||
*
|
||||
*
|
||||
* @author Ondřej Hruška (MightyPork)
|
||||
*/
|
||||
public interface MapTheme {
|
||||
|
||||
|
||||
TileModel wall();
|
||||
|
||||
|
||||
|
||||
|
||||
TileModel floor();
|
||||
|
||||
|
||||
|
||||
|
||||
TileModel door();
|
||||
|
||||
|
||||
|
||||
|
||||
TileModel passage();
|
||||
|
||||
|
||||
|
||||
|
||||
TileModel secretDoor();
|
||||
|
||||
|
||||
|
||||
|
||||
TileModel entrance();
|
||||
|
||||
|
||||
|
||||
|
||||
TileModel exit();
|
||||
|
||||
|
||||
|
||||
|
||||
TileModel chest();
|
||||
}
|
||||
|
||||
@@ -8,10 +8,10 @@ import mightypork.utils.math.algo.Coord;
|
||||
|
||||
/**
|
||||
* Room model
|
||||
*
|
||||
*
|
||||
* @author Ondřej Hruška (MightyPork)
|
||||
*/
|
||||
public interface RoomBuilder {
|
||||
|
||||
|
||||
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}
|
||||
*
|
||||
*
|
||||
* @author Ondřej Hruška (MightyPork)
|
||||
*/
|
||||
public class RoomEntry {
|
||||
|
||||
|
||||
final Coord min;
|
||||
final Coord max;
|
||||
|
||||
|
||||
public RoomEntry(Coord min, Coord max) {
|
||||
|
||||
|
||||
public RoomEntry(Coord min, Coord max)
|
||||
{
|
||||
super();
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public boolean intersectsWith(Coord amin, Coord amax)
|
||||
{
|
||||
int tw = max.x - min.x;
|
||||
int th = max.y - min.y;
|
||||
int rw = amax.x - amin.x;
|
||||
int rh = amax.y - amin.y;
|
||||
|
||||
|
||||
if (rw <= 0 || rh <= 0 || tw <= 0 || th <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
final int tx = min.x;
|
||||
final int ty = min.y;
|
||||
final int rx = amin.x;
|
||||
final int ry = amin.y;
|
||||
|
||||
|
||||
rw += rx;
|
||||
rh += ry;
|
||||
tw += tx;
|
||||
th += ty;
|
||||
|
||||
|
||||
return ((rw <= rx || rw >= tx) && (rh <= ry || rh >= ty) && (tw <= tx || tw >= rx) && (th <= ty || th >= ry));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
|
||||
@@ -1,26 +1,33 @@
|
||||
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;
|
||||
|
||||
|
||||
public class Rooms {
|
||||
|
||||
|
||||
public static final RoomBuilder BASIC = new BasicRoom();
|
||||
public static final RoomBuilder STORAGE = new StorageRoom();
|
||||
public static final RoomBuilder DEAD_END = new DeadEndRoom();
|
||||
public static final RoomBuilder ENTRANCE = new EntranceRoom();
|
||||
public static final RoomBuilder EXIT = new ExitRoom();
|
||||
public static final RoomBuilder BOSS = new BossRoom();
|
||||
|
||||
|
||||
|
||||
|
||||
public static RoomBuilder treasure(Item item)
|
||||
{
|
||||
return new TreasureChestRoom(item);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public static RoomBuilder shrine(Item item)
|
||||
{
|
||||
return new ItemShrineRoom(item);
|
||||
|
||||
@@ -25,25 +25,25 @@ import mightypork.utils.math.algo.pathfinding.PathFinder;
|
||||
|
||||
/**
|
||||
* Temporary tile map used for level generation.
|
||||
*
|
||||
*
|
||||
* @author Ondřej Hruška (MightyPork)
|
||||
*/
|
||||
public class ScratchMap {
|
||||
|
||||
|
||||
private Tile[][] map;
|
||||
private final int width;
|
||||
private final int height;
|
||||
|
||||
|
||||
private final List<RoomEntry> rooms = new ArrayList<>();
|
||||
|
||||
|
||||
/** Coords to connect with corridors */
|
||||
private final List<Coord> nodes = new ArrayList<>();
|
||||
|
||||
|
||||
private final List<Coord> occupied = new ArrayList<>();
|
||||
private final List<Entity> entities = new ArrayList<>();
|
||||
|
||||
|
||||
private final PathFinder pathf = new PathFinder() {
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isAccessible(Coord pos)
|
||||
{
|
||||
@@ -52,108 +52,109 @@ public class ScratchMap {
|
||||
if (t.isStairs()) return false;
|
||||
return t.isPotentiallyWalkable() || (t.genData.protection != TileProtectLevel.STRONG);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public int getCost(Coord last, Coord pos)
|
||||
{
|
||||
final Tile t = getTile(pos);
|
||||
|
||||
|
||||
switch (t.getType()) {
|
||||
case NULL:
|
||||
return 60;
|
||||
|
||||
|
||||
case DOOR:
|
||||
case PASSAGE:
|
||||
return 10;
|
||||
|
||||
|
||||
case STAIRS:
|
||||
case FLOOR:
|
||||
return 20;
|
||||
|
||||
|
||||
case WALL:
|
||||
if (t.genData.protection != TileProtectLevel.NONE) return 2000;
|
||||
|
||||
|
||||
return 100;
|
||||
|
||||
|
||||
default:
|
||||
throw new WorldGenError("Unknown tile type: " + t.getType());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public int getMinCost()
|
||||
{
|
||||
return 10;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public Heuristic getHeuristic()
|
||||
{
|
||||
return PathFinder.CORNER_HEURISTIC;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public List<Move> getWalkSides()
|
||||
{
|
||||
return Moves.CARDINAL_SIDES;
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
{
|
||||
// needed for when the path starts / ends at stairs.
|
||||
pathf.setIgnoreEnd(true);
|
||||
pathf.setIgnoreStart(true);
|
||||
}
|
||||
|
||||
|
||||
Coord genMin;
|
||||
Coord genMax;
|
||||
|
||||
|
||||
private final MapTheme theme;
|
||||
private final Random rand;
|
||||
private final Coord enterPoint = new Coord();
|
||||
private final Coord exitPoint = new Coord();
|
||||
|
||||
|
||||
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];
|
||||
|
||||
|
||||
genMin = Coord.make((max_size / 2) - 1, (max_size / 2) - 1);
|
||||
genMax = genMin.add(1, 1);
|
||||
|
||||
|
||||
width = max_size;
|
||||
height = max_size;
|
||||
this.rand = rand;
|
||||
this.theme = theme;
|
||||
|
||||
|
||||
fill(Coord.make(0, 0), Coord.make(width - 1, height - 1), Tiles.NULL);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void addRoom(RoomBuilder rb, boolean critical) throws WorldGenError
|
||||
{
|
||||
try {
|
||||
if (rooms.size() > 0) minimizeBounds();
|
||||
|
||||
|
||||
final Coord roomPos = Coord.make(0, 0);
|
||||
|
||||
|
||||
int failed = 0;
|
||||
int failed_total = 0;
|
||||
|
||||
|
||||
while (true) {
|
||||
|
||||
|
||||
final int sizeX = genMax.x - genMin.x;
|
||||
final int sizeY = genMax.y - genMin.y;
|
||||
|
||||
|
||||
roomPos.x = genMin.x + rand.nextInt(sizeX + 1);
|
||||
roomPos.y = genMin.y + rand.nextInt(sizeY + 1);
|
||||
|
||||
|
||||
switch (rand.nextInt(4)) {
|
||||
case 0:
|
||||
roomPos.x += (failed_total / 35);
|
||||
@@ -167,47 +168,47 @@ public class ScratchMap {
|
||||
case 3:
|
||||
roomPos.y -= (failed_total / 35);
|
||||
}
|
||||
|
||||
|
||||
final RoomEntry rd = rb.buildRoom(this, theme, rand, roomPos);
|
||||
if (rd != null) {
|
||||
|
||||
|
||||
rooms.add(rd);
|
||||
|
||||
|
||||
genMin.x = Math.min(genMin.x, rd.min.x);
|
||||
genMin.y = Math.min(genMin.y, rd.min.y);
|
||||
|
||||
|
||||
genMax.x = Math.max(genMax.x, rd.max.x);
|
||||
genMax.y = Math.max(genMax.y, rd.max.y);
|
||||
clampBounds();
|
||||
|
||||
|
||||
nodes.add(roomPos);
|
||||
|
||||
|
||||
return;
|
||||
|
||||
|
||||
} else {
|
||||
failed++;
|
||||
failed_total++;
|
||||
|
||||
|
||||
if (failed_total > 1000) {
|
||||
throw new WorldGenError("Failed to add a room.");
|
||||
}
|
||||
|
||||
|
||||
if (failed > 300) {
|
||||
Log.w("Faild to build room.");
|
||||
if (critical) {
|
||||
|
||||
|
||||
// expand gen bounds
|
||||
genMin.x -= 5;
|
||||
genMin.y -= 5;
|
||||
genMax.x += 5;
|
||||
genMax.y += 5;
|
||||
|
||||
|
||||
clampBounds();
|
||||
|
||||
|
||||
failed = 0;
|
||||
Log.f3("Trying again.");
|
||||
continue;
|
||||
|
||||
|
||||
} else {
|
||||
throw new WorldGenError("Failed to add a room.");
|
||||
}
|
||||
@@ -224,8 +225,8 @@ public class ScratchMap {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Clamp bounds to available area
|
||||
*/
|
||||
@@ -236,8 +237,8 @@ public class ScratchMap {
|
||||
genMax.x = Calc.clamp(genMax.x, 0, width - 1);
|
||||
genMax.y = Calc.clamp(genMax.y, 0, height - 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Minimize gen bounds based on defined room bounds
|
||||
*/
|
||||
@@ -245,124 +246,124 @@ public class ScratchMap {
|
||||
{
|
||||
final Coord low = Coord.make(width, height);
|
||||
final Coord high = Coord.make(0, 0);
|
||||
|
||||
|
||||
for (final RoomEntry rd : rooms) {
|
||||
low.x = Math.min(low.x, rd.min.x);
|
||||
low.y = Math.min(low.y, rd.min.y);
|
||||
|
||||
|
||||
high.x = Math.max(high.x, rd.max.x);
|
||||
high.y = Math.max(high.y, rd.max.y);
|
||||
}
|
||||
|
||||
|
||||
genMin.setTo(low);
|
||||
genMax.setTo(high);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public boolean isIn(Coord pos)
|
||||
{
|
||||
return pos.x >= 0 && pos.x < width && pos.y >= 0 && pos.y < height;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public Tile getTile(Coord pos)
|
||||
{
|
||||
if (!isIn(pos)) {
|
||||
throw new WorldGenError("Tile not in map: " + pos);
|
||||
}
|
||||
|
||||
|
||||
return map[pos.y][pos.x];
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public boolean set(Coord pos, TileModel tm)
|
||||
{
|
||||
return set(pos, tm.createTile());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public boolean set(Coord pos, Tile tile)
|
||||
{
|
||||
if (!isIn(pos)) {
|
||||
throw new WorldGenError("Tile not in map: " + pos);
|
||||
}
|
||||
|
||||
|
||||
map[pos.y][pos.x] = tile;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public boolean isClear(Coord min, Coord max)
|
||||
{
|
||||
if (!isIn(min) || !isIn(max)) return false;
|
||||
for (final RoomEntry r : rooms) {
|
||||
if (r.intersectsWith(min, max)) return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void protect(Coord pos, TileProtectLevel prot)
|
||||
{
|
||||
protect(pos, pos, 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);
|
||||
|
||||
|
||||
final Coord c = Coord.make(0, 0);
|
||||
for (c.x = min.x; c.x <= max.x; c.x++)
|
||||
for (c.y = min.y; c.y <= max.y; c.y++)
|
||||
getTile(c).genData.protection = prot;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void fill(Coord min, Coord max, TileModel tm)
|
||||
{
|
||||
if (!isIn(min) || !isIn(max)) throw new WorldGenError("Tile(s) not in map: " + min + " , " + max);
|
||||
|
||||
|
||||
final Coord c = Coord.make(0, 0);
|
||||
for (c.x = min.x; c.x <= max.x; c.x++)
|
||||
for (c.y = min.y; c.y <= max.y; c.y++)
|
||||
set(c, tm.createTile());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void border(Coord min, Coord max, TileModel tm)
|
||||
{
|
||||
if (!isIn(min) || !isIn(max)) throw new WorldGenError("Tile(s) not in map: " + min + " , " + max);
|
||||
|
||||
|
||||
final Coord c = Coord.make(0, 0);
|
||||
|
||||
|
||||
// top
|
||||
for (c.x = min.x, c.y = min.y; c.x <= max.x; c.x++)
|
||||
set(c, tm.createTile());
|
||||
|
||||
|
||||
//bottom
|
||||
for (c.x = min.x, c.y = max.y; c.x <= max.x; c.x++)
|
||||
set(c, tm.createTile());
|
||||
|
||||
|
||||
//left
|
||||
for (c.x = min.x, c.y = min.y + 1; c.y < max.y; c.y++)
|
||||
set(c, tm.createTile());
|
||||
|
||||
|
||||
//right
|
||||
for (c.x = max.x, c.y = min.y + 1; c.y < max.y; c.y++)
|
||||
set(c, tm.createTile());
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void buildCorridors()
|
||||
{
|
||||
// Log.f3("Building corridors.");
|
||||
|
||||
|
||||
Coord start = nodes.get(0);
|
||||
final Set<Coord> starts = new HashSet<>();
|
||||
|
||||
|
||||
for (int i = 0; i < 2 + rooms.size() / 5; i++) {
|
||||
if (!starts.contains(start)) {
|
||||
for (int j = 0; j < nodes.size(); j++) {
|
||||
@@ -373,24 +374,24 @@ public class ScratchMap {
|
||||
start = Calc.pick(rand, nodes);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private void buildCorridor(Coord node1, Coord node2)
|
||||
{
|
||||
// Log.f3("Building corridor " + node1 + " -> " + node2);
|
||||
final List<Coord> steps = pathf.findPath(node1, node2);
|
||||
|
||||
|
||||
if (steps == null) {
|
||||
Log.w("Could not build corridor " + node1 + "->" + node2);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
for (final Coord c : steps) {
|
||||
buildCorridorPiece(c);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private void buildCorridorPiece(Coord pos)
|
||||
{
|
||||
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 (j = -1, c.y = pos.y - 1; c.y <= pos.y + 1; c.y++, j++) {
|
||||
if (!isIn(c)) continue;
|
||||
|
||||
|
||||
genMin.x = Math.min(genMin.x, c.x);
|
||||
genMin.y = Math.min(genMin.y, c.y);
|
||||
|
||||
|
||||
genMax.x = Math.max(genMax.x, c.x);
|
||||
genMax.y = Math.max(genMax.y, c.y);
|
||||
clampBounds();
|
||||
|
||||
|
||||
final Tile current = getTile(c);
|
||||
if (!current.isNull() && (current.isPotentiallyWalkable() || current.isStairs())) continue; // floor already, let it be
|
||||
|
||||
|
||||
if (i == 0 && j == 0) {
|
||||
set(c, theme.floor());
|
||||
} else {
|
||||
@@ -418,8 +419,8 @@ public class ScratchMap {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public byte findWalls(Coord pos)
|
||||
{
|
||||
byte walls = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
final Coord cc = pos.add(Moves.getSide(i));
|
||||
if (!isIn(cc)) continue;
|
||||
|
||||
|
||||
if (getTile(cc).isWall()) {
|
||||
walls |= Moves.getBit(i);
|
||||
}
|
||||
}
|
||||
return walls;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public byte findFloors(Coord pos)
|
||||
{
|
||||
byte floors = 0;
|
||||
for (int i = 0; i <= 7; i++) {
|
||||
final Coord cc = pos.add(Moves.getSide(i));
|
||||
if (!isIn(cc)) continue;
|
||||
|
||||
|
||||
if (getTile(cc).isFloor()) {
|
||||
floors |= Moves.getBit(i);
|
||||
}
|
||||
}
|
||||
return floors;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public byte findDoors(Coord pos)
|
||||
{
|
||||
byte doors = 0;
|
||||
for (int i = 0; i <= 7; i++) {
|
||||
final Coord cc = pos.add(Moves.getSide(i));
|
||||
if (!isIn(cc)) continue;
|
||||
|
||||
|
||||
if (getTile(cc).isDoor()) {
|
||||
doors |= Moves.getBit(i);
|
||||
}
|
||||
}
|
||||
return doors;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public byte findNils(Coord pos)
|
||||
{
|
||||
byte nils = 0;
|
||||
for (int i = 0; i <= 7; i++) {
|
||||
final Coord cc = pos.add(Moves.getSide(i));
|
||||
|
||||
|
||||
if (!isIn(cc) || getTile(cc).isNull()) {
|
||||
nils |= Moves.getBit(i);
|
||||
}
|
||||
}
|
||||
return nils;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Fix generator glitches and reduce size to the actual used size
|
||||
*/
|
||||
public void fixGlitches()
|
||||
{
|
||||
final Tile[][] out = new Tile[height][width];
|
||||
|
||||
|
||||
// bounds will be adjusted by the actual tiles in the map
|
||||
genMin.x = width;
|
||||
genMin.y = height;
|
||||
genMax.x = 0;
|
||||
genMax.y = 0;
|
||||
|
||||
|
||||
final Coord c = Coord.make(0, 0);
|
||||
for (c.x = 0; c.x < width; c.x++) {
|
||||
for (c.y = 0; c.y < height; c.y++) {
|
||||
|
||||
|
||||
final Tile t = getTile(c);
|
||||
final boolean isNull = t.isNull();
|
||||
|
||||
|
||||
final boolean isDoor = !isNull && t.isDoor();
|
||||
final boolean isFloor = !isNull && t.isFloor();
|
||||
final boolean isWall = !isNull && t.isWall();
|
||||
|
||||
|
||||
// bitmasks
|
||||
final byte walls = findWalls(c);
|
||||
final byte nils = findNils(c);
|
||||
final byte floors = findFloors(c);
|
||||
|
||||
|
||||
boolean toWall = false;
|
||||
boolean toFloor = false;
|
||||
boolean toNull = false;
|
||||
|
||||
|
||||
do {
|
||||
if (isWall && floors == 0) {
|
||||
toNull = true;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (isFloor && (nils & Moves.BITS_CARDINAL) != 0) {
|
||||
toWall = true; // floor with adjacent cardinal null
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (isNull && (floors & Moves.BITS_DIAGONAL) != 0) {
|
||||
toWall = true; // null with adjacent diagonal floor
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (isDoor) {
|
||||
|
||||
|
||||
if (Calc.countBits((byte) (floors & Moves.BITS_CARDINAL)) < 2) {
|
||||
toWall = true;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (Calc.countBits((byte) (walls & Moves.BITS_CARDINAL)) > 2) {
|
||||
toWall = true;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (Calc.countBits((byte) (floors & Moves.BITS_CARDINAL)) > 2) {
|
||||
toFloor = true;
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
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_SW_CORNER) == Moves.BITS_SW_CORNER) toWall = true;
|
||||
if ((floors & Moves.BITS_SE_CORNER) == Moves.BITS_SE_CORNER) toWall = true;
|
||||
|
||||
|
||||
}
|
||||
} while (false);
|
||||
|
||||
|
||||
if (toNull) {
|
||||
out[c.y][c.x] = Tiles.NULL.createTile();
|
||||
} else if (toWall) {
|
||||
@@ -571,24 +572,24 @@ public class ScratchMap {
|
||||
} else {
|
||||
out[c.y][c.x] = map[c.y][c.x];
|
||||
}
|
||||
|
||||
|
||||
if (!out[c.y][c.x].isNull()) {
|
||||
genMin.x = Math.min(genMin.x, c.x);
|
||||
genMin.y = Math.min(genMin.y, c.y);
|
||||
|
||||
|
||||
genMax.x = Math.max(genMax.x, c.x);
|
||||
genMax.y = Math.max(genMax.y, c.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
map = out;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Write tiles and entities into a level
|
||||
*
|
||||
*
|
||||
* @param level the level
|
||||
*/
|
||||
public void writeToLevel(Level level)
|
||||
@@ -596,23 +597,23 @@ public class ScratchMap {
|
||||
if (level.getWorld() == null) {
|
||||
throw new WorldGenError("Level has no world assigned."); // need for entities
|
||||
}
|
||||
|
||||
|
||||
// make sure no walkable are at edges.
|
||||
final Coord c = 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.y = genMin.y, c1.y = 0; c.y <= genMax.y; c.y++, c1.y++) {
|
||||
level.setTile(c1, getTile(c));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
final Coord entrance = new Coord(enterPoint.x - genMin.x, enterPoint.y - genMin.y);
|
||||
level.setEnterPoint(entrance);
|
||||
|
||||
|
||||
final Coord exit = new Coord(exitPoint.x - genMin.x, exitPoint.y - genMin.y);
|
||||
level.setExitPoint(exit);
|
||||
|
||||
|
||||
for (final Entity e : entities) {
|
||||
final Coord pos = e.getCoord().add(-genMin.x, -genMin.y);
|
||||
if (!level.addEntityNear(e, pos)) {
|
||||
@@ -628,101 +629,101 @@ public class ScratchMap {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void setEntrance(Coord pos)
|
||||
{
|
||||
enterPoint.setTo(pos);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void setExit(Coord pos)
|
||||
{
|
||||
exitPoint.setTo(pos);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public boolean addItem(Item item, Coord pos)
|
||||
{
|
||||
return addItem(item, pos, true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public boolean addItem(Item item, Coord pos, boolean canStack)
|
||||
{
|
||||
if (!isIn(pos)) return false;
|
||||
|
||||
|
||||
final Tile t = getTile(pos);
|
||||
if (!canStack && t.hasItem()) return false;
|
||||
if (t.dropItem(item)) return true;
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public boolean addItemInArea(Item item, Coord min, Coord max, int tries)
|
||||
{
|
||||
final Coord pos = Coord.zero();
|
||||
|
||||
|
||||
for (int i = 0; i < tries / 2; i++) {
|
||||
pos.x = Calc.randInt(rand, min.x, max.x);
|
||||
pos.y = Calc.randInt(rand, min.y, max.y);
|
||||
if (addItem(item, pos, false)) return true;
|
||||
}
|
||||
|
||||
|
||||
for (int i = 0; i < tries - (tries / 2); i++) {
|
||||
pos.x = Calc.randInt(rand, min.x, max.x);
|
||||
pos.y = Calc.randInt(rand, min.y, max.y);
|
||||
if (addItem(item, pos, true)) return true;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public boolean addItemInMap(Item item, int tries)
|
||||
{
|
||||
return addItemInArea(item, genMin, genMax, tries);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public boolean addEntityInArea(Entity entity, Coord min, Coord max, int tries)
|
||||
{
|
||||
final Coord pos = Coord.zero();
|
||||
|
||||
|
||||
for (int i = 0; i < tries; i++) {
|
||||
pos.x = Calc.randInt(rand, min.x, max.x);
|
||||
pos.y = Calc.randInt(rand, min.y, max.y);
|
||||
if (!isIn(pos)) continue;
|
||||
|
||||
|
||||
if (addEntity(entity, pos)) return true;
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public boolean addEntityInMap(Entity entity, int tries)
|
||||
{
|
||||
return addEntityInArea(entity, genMin, genMax, tries);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public boolean addEntity(Entity entity, Coord pos)
|
||||
{
|
||||
if (!isIn(pos)) return false;
|
||||
|
||||
|
||||
if (pos.dist(enterPoint) < 4) return false; // protected distance.
|
||||
|
||||
|
||||
final Tile t = getTile(pos);
|
||||
if (!t.isWalkable()) return false;
|
||||
|
||||
|
||||
if (occupied.contains(pos)) return false;
|
||||
|
||||
|
||||
occupied.add(pos.copy());
|
||||
entity.setCoord(pos);
|
||||
entities.add(entity);
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,103 +19,103 @@ import mightypork.utils.math.Range;
|
||||
|
||||
|
||||
public class WorldCreator {
|
||||
|
||||
|
||||
public static Random rand = new Random();
|
||||
|
||||
|
||||
|
||||
|
||||
public static World createWorld(long seed)
|
||||
{
|
||||
synchronized (rand) {
|
||||
|
||||
|
||||
Log.f2("Generating a new world...");
|
||||
|
||||
|
||||
rand.setSeed(seed);
|
||||
final MapTheme theme = new ThemeBrick();
|
||||
|
||||
|
||||
final World w = new World();
|
||||
w.setSeed(seed);
|
||||
|
||||
|
||||
final LevelBuilder levelBuilders[] = new LevelBuilder[7];
|
||||
|
||||
|
||||
// build the level rooms
|
||||
for (int floor = 1; floor <= 7; floor++) {
|
||||
|
||||
|
||||
Log.f3("Placing rooms for level: " + floor);
|
||||
|
||||
|
||||
final LevelBuilder lb = prepareFloor(rand.nextLong(), floor, theme, floor == 7);
|
||||
|
||||
|
||||
levelBuilders[floor - 1] = lb;
|
||||
}
|
||||
|
||||
|
||||
Log.f3("Placing items...");
|
||||
final List<ItemModel> weaponsBasic = new ArrayList<>();
|
||||
weaponsBasic.add(Items.ROCK);
|
||||
weaponsBasic.add(Items.BONE);
|
||||
weaponsBasic.add(Items.TWIG);
|
||||
|
||||
|
||||
final List<ItemModel> weaponsMedium = new ArrayList<>();
|
||||
weaponsMedium.add(Items.CLUB);
|
||||
weaponsMedium.add(Items.KNIFE);
|
||||
|
||||
|
||||
final List<ItemModel> weaponsGood = new ArrayList<>();
|
||||
weaponsGood.add(Items.AXE);
|
||||
weaponsGood.add(Items.SWORD);
|
||||
|
||||
|
||||
for (int i = 0; i < Calc.randInt(rand, 12, 20); i++) {
|
||||
final Item item = Calc.pick(rand, weaponsBasic).createItemDamaged(40);
|
||||
final LevelBuilder lb = levelBuilders[-1 + Calc.randInt(1, 7)];
|
||||
lb.addItem(item, false);
|
||||
}
|
||||
|
||||
|
||||
for (int i = 0; i < Calc.randInt(rand, 2, 4); i++) {
|
||||
final Item item = Calc.pick(rand, weaponsMedium).createItemDamaged(50);
|
||||
final LevelBuilder lb = levelBuilders[-1 + Calc.randInt(3, 5)];
|
||||
lb.addRoom(Rooms.treasure(item), BuildOrder.MIDDLE, true);
|
||||
}
|
||||
|
||||
|
||||
for (int i = 0; i < Calc.randInt(rand, 2, 4); i++) {
|
||||
final Item item = Calc.pick(rand, weaponsGood).createItemDamaged(60);
|
||||
final LevelBuilder lb = levelBuilders[-1 + Calc.randInt(4, 7)];
|
||||
|
||||
|
||||
lb.addRoom(Rooms.treasure(item), BuildOrder.LAST, true);
|
||||
}
|
||||
|
||||
|
||||
// place random foods
|
||||
final List<ItemModel> randomFood = new ArrayList<>();
|
||||
randomFood.add(Items.CHEESE);
|
||||
randomFood.add(Items.MEAT);
|
||||
|
||||
|
||||
for (int level = 1; level <= 7; level++) {
|
||||
final LevelBuilder lb = levelBuilders[level - 1];
|
||||
final Range amount = Range.make(level / 2D, level * 2);
|
||||
|
||||
|
||||
for (int i = 0; i < amount.randInt(rand); i++) {
|
||||
lb.addItem(Calc.pick(rand, randomFood).createItem(), false);
|
||||
}
|
||||
}
|
||||
|
||||
// place monsters
|
||||
|
||||
|
||||
// place monsters
|
||||
|
||||
Log.f3("Placing monsters...");
|
||||
for (int level = 1; level <= 7; level++) {
|
||||
|
||||
|
||||
final LevelBuilder lb = levelBuilders[level - 1];
|
||||
|
||||
|
||||
final Range amount = Range.make(3 + (level) * 2, 3 + level * 3);
|
||||
|
||||
|
||||
for (int i = 0; i < amount.randInt(rand); i++) {
|
||||
Entity e;
|
||||
|
||||
|
||||
if (level >= 2 && Calc.randInt(rand, 0, (int) (3.5 - (level / 2D))) == 0) {
|
||||
e = Entities.RAT_BROWN.createEntity();
|
||||
} else {
|
||||
e = Entities.RAT_GRAY.createEntity();
|
||||
}
|
||||
|
||||
|
||||
lb.addEntity(e, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// compile levels
|
||||
Log.f3("Building levels...");
|
||||
int i = 1;
|
||||
@@ -124,34 +124,34 @@ public class WorldCreator {
|
||||
w.addLevel(lb.build(w));
|
||||
i++;
|
||||
}
|
||||
|
||||
|
||||
w.createPlayer();
|
||||
|
||||
|
||||
Log.f2("World generation finished.");
|
||||
|
||||
|
||||
return w;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public static LevelBuilder prepareFloor(long seed, int floor, MapTheme theme, boolean lastLevel) throws WorldGenError
|
||||
{
|
||||
final LevelBuilder lb = new LevelBuilder(128, theme, seed);
|
||||
|
||||
|
||||
lb.addRoom(Rooms.ENTRANCE, BuildOrder.FIRST, true);
|
||||
|
||||
|
||||
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.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.EXIT, BuildOrder.LAST, true);
|
||||
|
||||
|
||||
if (floor % 2 == 0) {
|
||||
final RoomBuilder heartRoom = Rooms.shrine(Items.HEART_PIECE.createItem());
|
||||
lb.addRoom(heartRoom, BuildOrder.LAST, true);
|
||||
}
|
||||
|
||||
|
||||
return lb;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,28 +3,32 @@ package mightypork.rogue.world.gen;
|
||||
|
||||
/**
|
||||
* Error in world generation
|
||||
*
|
||||
*
|
||||
* @author Ondřej Hruška (MightyPork)
|
||||
*/
|
||||
public class WorldGenError extends RuntimeException {
|
||||
|
||||
public WorldGenError() {
|
||||
|
||||
public WorldGenError()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
|
||||
public WorldGenError(String message, Throwable cause) {
|
||||
|
||||
|
||||
public WorldGenError(String message, Throwable cause)
|
||||
{
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
|
||||
public WorldGenError(String message) {
|
||||
|
||||
|
||||
public WorldGenError(String message)
|
||||
{
|
||||
super(message);
|
||||
}
|
||||
|
||||
|
||||
public WorldGenError(Throwable cause) {
|
||||
|
||||
|
||||
public WorldGenError(Throwable cause)
|
||||
{
|
||||
super(cause);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ import mightypork.utils.math.algo.Moves;
|
||||
|
||||
|
||||
public abstract class AbstractRectRoom implements RoomBuilder {
|
||||
|
||||
|
||||
@Override
|
||||
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 int width = 2 + innerSize.x - 1;
|
||||
final int height = 2 + innerSize.y - 1;
|
||||
|
||||
|
||||
final int wLow = (int) Math.round(width / 2D);
|
||||
final int wHigh = width - wLow;
|
||||
final int hLow = (int) Math.round(height / 2D);
|
||||
final int hHigh = height - hLow;
|
||||
|
||||
|
||||
final Coord min = new Coord(center.x - wLow, center.y - hLow);
|
||||
final Coord max = new Coord(center.x + wHigh, center.y + hHigh);
|
||||
|
||||
|
||||
if (!map.isClear(min.add(-1, -1), max)) return null;
|
||||
|
||||
|
||||
map.fill(min, max, getFloor(theme));
|
||||
map.border(min, max, getWall(theme));
|
||||
map.protect(min, max, getWallProtectionLevel());
|
||||
|
||||
|
||||
placeDoors(map, theme, rand, min, max);
|
||||
|
||||
|
||||
buildExtras(map, theme, rand, min, max);
|
||||
|
||||
|
||||
return new RoomEntry(min.add(-1, -1), max);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
protected TileModel getWall(MapTheme theme)
|
||||
{
|
||||
return theme.wall();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
protected TileModel getFloor(MapTheme theme)
|
||||
{
|
||||
return theme.floor();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
protected void placeDoors(ScratchMap map, MapTheme theme, Random rand, Coord min, Coord max)
|
||||
{
|
||||
final int width = max.x - min.x;
|
||||
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
|
||||
final Coord door = min.copy();
|
||||
switch (rand.nextInt(4)) {
|
||||
@@ -83,29 +83,29 @@ public abstract class AbstractRectRoom implements RoomBuilder {
|
||||
door.y += 1 + rand.nextInt(height - 1);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if ((map.findDoors(door) & Moves.BITS_CARDINAL) == 0) {
|
||||
map.set(door, getDoorType(theme, rand));
|
||||
i++; // increment pointer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Stub
|
||||
protected void buildExtras(ScratchMap map, MapTheme theme, Random rand, Coord min, Coord max)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
protected abstract Coord getInnerSize(Random rand);
|
||||
|
||||
|
||||
|
||||
|
||||
protected abstract TileProtectLevel getWallProtectionLevel();
|
||||
|
||||
|
||||
|
||||
|
||||
protected abstract TileModel getDoorType(MapTheme theme, Random rand);
|
||||
|
||||
|
||||
|
||||
|
||||
protected abstract int getDoorCount(Random rand);
|
||||
}
|
||||
|
||||
@@ -10,28 +10,28 @@ import mightypork.utils.math.algo.Coord;
|
||||
|
||||
|
||||
public class BasicRoom extends AbstractRectRoom {
|
||||
|
||||
|
||||
@Override
|
||||
protected TileModel getDoorType(MapTheme theme, Random rand)
|
||||
{
|
||||
return rand.nextInt(4) == 0 ? theme.passage() : theme.door();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected int getDoorCount(Random rand)
|
||||
{
|
||||
return 1 + rand.nextInt(5);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected TileProtectLevel getWallProtectionLevel()
|
||||
{
|
||||
return TileProtectLevel.WEAK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected Coord getInnerSize(Random rand)
|
||||
{
|
||||
|
||||
@@ -12,43 +12,43 @@ import mightypork.utils.math.algo.Coord;
|
||||
|
||||
|
||||
public class BossRoom extends SecretRoom {
|
||||
|
||||
|
||||
@Override
|
||||
protected int getDoorCount(Random rand)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected void buildExtras(ScratchMap map, MapTheme theme, Random rand, Coord min, Coord max)
|
||||
{
|
||||
final Coord bossPos = min.add(3, 3);
|
||||
|
||||
|
||||
final Entity boss = Entities.RAT_BOSS.createEntity();
|
||||
if (!map.addEntity(boss, bossPos)) {
|
||||
throw new WorldGenError("Could not place boss.");
|
||||
}
|
||||
|
||||
|
||||
Entity rat;
|
||||
|
||||
|
||||
// 4 guardian rats
|
||||
|
||||
|
||||
rat = Entities.RAT_BROWN.createEntity();
|
||||
map.addEntity(rat, min.add(1, 1));
|
||||
|
||||
|
||||
rat = Entities.RAT_BROWN.createEntity();
|
||||
map.addEntity(rat, Coord.make(max.x - 1, min.y + 1));
|
||||
|
||||
|
||||
rat = Entities.RAT_BROWN.createEntity();
|
||||
map.addEntity(rat, max.add(-1, -1));
|
||||
|
||||
|
||||
rat = Entities.RAT_BROWN.createEntity();
|
||||
map.addEntity(rat, Coord.make(min.x + 1, max.y - 1));
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected Coord getInnerSize(Random rand)
|
||||
{
|
||||
|
||||
@@ -10,28 +10,28 @@ import mightypork.utils.math.algo.Coord;
|
||||
|
||||
|
||||
public class DeadEndRoom extends AbstractRectRoom {
|
||||
|
||||
|
||||
@Override
|
||||
protected Coord getInnerSize(Random rand)
|
||||
{
|
||||
return Coord.make(1, 1);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected TileProtectLevel getWallProtectionLevel()
|
||||
{
|
||||
return TileProtectLevel.STRONG;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected TileModel getDoorType(MapTheme theme, Random rand)
|
||||
{
|
||||
return theme.floor();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected int getDoorCount(Random rand)
|
||||
{
|
||||
|
||||
@@ -12,21 +12,21 @@ import mightypork.utils.math.algo.Coord;
|
||||
|
||||
|
||||
public class EntranceRoom extends AbstractRectRoom {
|
||||
|
||||
|
||||
@Override
|
||||
protected Coord getInnerSize(Random rand)
|
||||
{
|
||||
return Coord.make(3 + rand.nextInt(3), 3 + rand.nextInt(3));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected TileProtectLevel getWallProtectionLevel()
|
||||
{
|
||||
return TileProtectLevel.WEAK;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected TileModel getDoorType(MapTheme theme, Random rand)
|
||||
{
|
||||
@@ -40,8 +40,8 @@ public class EntranceRoom extends AbstractRectRoom {
|
||||
return theme.door();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
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.setEntrance(c.add(1, 0));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected int getDoorCount(Random rand)
|
||||
{
|
||||
return Calc.randInt(rand, 1, 4);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -11,28 +11,28 @@ import mightypork.utils.math.algo.Coord;
|
||||
|
||||
|
||||
public class ExitRoom extends AbstractRectRoom {
|
||||
|
||||
|
||||
@Override
|
||||
protected Coord getInnerSize(Random rand)
|
||||
{
|
||||
return Coord.make(3 + rand.nextInt(2), 3 + rand.nextInt(2));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected TileProtectLevel getWallProtectionLevel()
|
||||
{
|
||||
return TileProtectLevel.NONE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected TileModel getDoorType(MapTheme theme, Random rand)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
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.setExit(c.add(-1, 0));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected int getDoorCount(Random rand)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -12,33 +12,34 @@ import mightypork.utils.math.algo.Coord;
|
||||
|
||||
|
||||
public class ItemShrineRoom extends SecretRoom {
|
||||
|
||||
|
||||
private final Item item;
|
||||
|
||||
|
||||
public ItemShrineRoom(Item item) {
|
||||
|
||||
|
||||
public ItemShrineRoom(Item item)
|
||||
{
|
||||
this.item = item;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected int getDoorCount(Random rand)
|
||||
{
|
||||
return Calc.randInt(rand, 1, 4);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected void buildExtras(ScratchMap map, MapTheme theme, Random rand, Coord min, Coord max)
|
||||
{
|
||||
final Coord center = min.add(2, 2);
|
||||
|
||||
|
||||
if (!map.addItem(item, center)) {
|
||||
throw new WorldGenError("Could not place item in chest.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected Coord getInnerSize(Random rand)
|
||||
{
|
||||
|
||||
@@ -11,28 +11,28 @@ import mightypork.utils.math.algo.Coord;
|
||||
|
||||
|
||||
public abstract class SecretRoom extends AbstractRectRoom {
|
||||
|
||||
|
||||
@Override
|
||||
protected TileModel getDoorType(MapTheme theme, Random rand)
|
||||
{
|
||||
return rand.nextInt(5) == 0 ? theme.passage() : theme.secretDoor();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected int getDoorCount(Random rand)
|
||||
{
|
||||
return Calc.randInt(rand, 1, 3);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected TileProtectLevel getWallProtectionLevel()
|
||||
{
|
||||
return TileProtectLevel.STRONG;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected Coord getInnerSize(Random rand)
|
||||
{
|
||||
|
||||
@@ -11,44 +11,44 @@ import mightypork.utils.math.algo.Coord;
|
||||
|
||||
|
||||
public class StorageRoom extends SecretRoom {
|
||||
|
||||
|
||||
@Override
|
||||
protected void buildExtras(ScratchMap map, MapTheme theme, Random rand, Coord min, Coord max)
|
||||
{
|
||||
int maxStuff = Calc.randInt(rand, 3, 5);
|
||||
|
||||
|
||||
// at least one meat or cheese.
|
||||
final boolean oneMeat = rand.nextBoolean();
|
||||
|
||||
|
||||
for (int i = 0; i < Calc.randInt(rand, oneMeat ? 1 : 0, 3); i++) {
|
||||
map.addItemInArea(Items.MEAT.createItem(), min, max, 50);
|
||||
if (--maxStuff == 0) return;
|
||||
}
|
||||
|
||||
|
||||
for (int i = 0; i < Calc.randInt(rand, oneMeat ? 0 : 1, 2); i++) {
|
||||
map.addItemInArea(Items.CHEESE.createItem(), min, max, 50);
|
||||
if (--maxStuff == 0) return;
|
||||
}
|
||||
|
||||
|
||||
for (int i = 0; i < Calc.randInt(rand, 0, 1); i++) {
|
||||
map.addItemInArea(Items.ROCK.createItemDamaged(30), min, max, 50);
|
||||
if (--maxStuff == 0) return;
|
||||
}
|
||||
|
||||
|
||||
for (int i = 0; i < Calc.randInt(rand, 0, 1); i++) {
|
||||
map.addItemInArea(Items.SANDWICH.createItem(), min, max, 50);
|
||||
if (--maxStuff == 0) return;
|
||||
}
|
||||
|
||||
|
||||
for (int i = 0; i < Calc.randInt(rand, 0, 2); i++) {
|
||||
map.addItemInArea(Items.TWIG.createItemDamaged(40), min, max, 50);
|
||||
if (--maxStuff == 0) return;
|
||||
}
|
||||
|
||||
|
||||
for (int i = 0; i < Calc.randInt(rand, 0, 2); i++) {
|
||||
map.addItemInArea(Items.BONE.createItemDamaged(40), min, max, 50);
|
||||
if (--maxStuff == 0) return;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,21 +11,22 @@ import mightypork.utils.math.algo.Coord;
|
||||
|
||||
|
||||
public class TreasureChestRoom extends ItemShrineRoom {
|
||||
|
||||
public TreasureChestRoom(Item item) {
|
||||
|
||||
public TreasureChestRoom(Item item)
|
||||
{
|
||||
super(item);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected void buildExtras(ScratchMap map, MapTheme theme, Random rand, Coord min, Coord max)
|
||||
{
|
||||
// set tile
|
||||
final Coord center = min.add(2, 2);
|
||||
|
||||
|
||||
map.set(center, theme.chest());
|
||||
map.protect(center, TileProtectLevel.STRONG);
|
||||
|
||||
|
||||
// drop item
|
||||
super.buildExtras(map, theme, rand, min, max);
|
||||
}
|
||||
|
||||
@@ -8,56 +8,56 @@ import mightypork.rogue.world.tile.Tiles;
|
||||
|
||||
// basic dungeon theme
|
||||
public class ThemeBrick implements MapTheme {
|
||||
|
||||
|
||||
@Override
|
||||
public TileModel wall()
|
||||
{
|
||||
return Tiles.BRICK_WALL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public TileModel floor()
|
||||
{
|
||||
return Tiles.BRICK_FLOOR;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public TileModel door()
|
||||
{
|
||||
return Tiles.BRICK_DOOR;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public TileModel passage()
|
||||
{
|
||||
return Tiles.BRICK_PASSAGE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public TileModel secretDoor()
|
||||
{
|
||||
return Tiles.BRICK_HIDDEN_DOOR;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public TileModel entrance()
|
||||
{
|
||||
return Tiles.BRICK_ENTRANCE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public TileModel exit()
|
||||
{
|
||||
return Tiles.BRICK_EXIT;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public TileModel chest()
|
||||
{
|
||||
|
||||
@@ -29,27 +29,28 @@ import mightypork.utils.math.timing.TimedTask;
|
||||
|
||||
/**
|
||||
* Level display component
|
||||
*
|
||||
*
|
||||
* @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;
|
||||
|
||||
|
||||
protected final WorldRenderer worldRenderer;
|
||||
public final PlayerControl plc;
|
||||
|
||||
|
||||
private final Set<MapInteractionPlugin> plugins = new LinkedHashSet<>();
|
||||
private final NumAnimated zoom = new NumAnimated(0, Easing.SINE_BOTH);
|
||||
private boolean zoom_in = true;
|
||||
|
||||
|
||||
private final NumAnimated descFadeAnim = new NumAnimated(0);
|
||||
private final Color blackColor = RGB.BLACK.withAlpha(descFadeAnim);
|
||||
|
||||
|
||||
private int descDir = 0;
|
||||
|
||||
|
||||
private final TimedTask timerDesc1 = new TimedTask() {
|
||||
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
@@ -62,55 +63,56 @@ public class MapView extends InputComponent implements DelegatingClient, MouseBu
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private final TimedTask timerDesc2 = new TimedTask() {
|
||||
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
WorldProvider.get().getWorld().resume();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private final Num tileSize;
|
||||
|
||||
|
||||
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
@Override
|
||||
public Collection getChildClients()
|
||||
{
|
||||
return plugins;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean doesDelegate()
|
||||
{
|
||||
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.worldRenderer = new WorldRenderer(this, tileSize);
|
||||
plc = WorldProvider.get().getPlayerControl();
|
||||
|
||||
|
||||
zoom.setDefaultDuration(0.5);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected void renderComponent()
|
||||
{
|
||||
worldRenderer.render();
|
||||
|
||||
|
||||
App.gfx().quad(this, blackColor);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Get tile coord at a screen position
|
||||
*
|
||||
*
|
||||
* @param pos position on screen (px)
|
||||
* @return position on map (tiles)
|
||||
*/
|
||||
@@ -118,13 +120,13 @@ public class MapView extends InputComponent implements DelegatingClient, MouseBu
|
||||
{
|
||||
return worldRenderer.getClickedTile(pos);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void receive(MouseButtonEvent event)
|
||||
{
|
||||
if (!event.isOver(this)) return;
|
||||
|
||||
|
||||
if (event.isButtonEvent()) {
|
||||
for (final MapInteractionPlugin p : plugins) {
|
||||
if (p.onClick(event.getPos(), event.getButton(), event.isDown())) {
|
||||
@@ -133,7 +135,7 @@ public class MapView extends InputComponent implements DelegatingClient, MouseBu
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (event.isWheelEvent()) {
|
||||
final int delta = event.getWheelDelta();
|
||||
if (!zoom.isFinished()) return;
|
||||
@@ -146,8 +148,8 @@ public class MapView extends InputComponent implements DelegatingClient, MouseBu
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void toggleMag()
|
||||
{
|
||||
if (zoom_in) {
|
||||
@@ -158,19 +160,19 @@ public class MapView extends InputComponent implements DelegatingClient, MouseBu
|
||||
zoom_in = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Add interaction plugin
|
||||
*
|
||||
*
|
||||
* @param plugin
|
||||
*/
|
||||
public void addPlugin(MapInteractionPlugin plugin)
|
||||
{
|
||||
plugins.add(plugin);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void update(double delta)
|
||||
{
|
||||
@@ -179,40 +181,40 @@ public class MapView extends InputComponent implements DelegatingClient, MouseBu
|
||||
timerDesc1.update(delta);
|
||||
timerDesc2.update(delta);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void onAscendRequest()
|
||||
{
|
||||
if (descFadeAnim.isInProgress()) return;
|
||||
|
||||
|
||||
final World w = WorldProvider.get().getWorld();
|
||||
|
||||
|
||||
if (w.getPlayer().canAscend()) {
|
||||
descDir = -1;
|
||||
startDescAnim();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private void startDescAnim()
|
||||
{
|
||||
WorldProvider.get().getWorld().pause();
|
||||
|
||||
|
||||
timerDesc2.stop();
|
||||
timerDesc1.start(transition_time);
|
||||
descFadeAnim.setTo(0);
|
||||
descFadeAnim.fadeIn(transition_time);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void onDescendRequest()
|
||||
{
|
||||
if (descFadeAnim.isInProgress()) return;
|
||||
|
||||
|
||||
final World w = WorldProvider.get().getWorld();
|
||||
|
||||
|
||||
if (w.getPlayer().canDescend()) {
|
||||
descDir = 1;
|
||||
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.vect.Vect;
|
||||
|
||||
import org.lwjgl.opengl.GL11;
|
||||
|
||||
|
||||
public class Minimap extends InputComponent implements MouseButtonHandler {
|
||||
|
||||
|
||||
private final RectVar bounds = Rect.makeVar();
|
||||
private int unit = 0;
|
||||
private final Num translucency = Num.make(0.8);
|
||||
private final Color playerColor = RGB.RED;
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected void renderComponent()
|
||||
{
|
||||
Color.pushAlpha(translucency);
|
||||
|
||||
|
||||
final Level lvl = WorldProvider.get().getCurrentLevel();
|
||||
unit = (int) Math.min(Math.max(2, Math.ceil((height().value() / 2) / (lvl.getHeight() + 2))), 10);
|
||||
|
||||
|
||||
final Vect plCoord = WorldProvider.get().getPlayer().getVisualPos();
|
||||
|
||||
|
||||
final int lw = lvl.getWidth();
|
||||
final int lh = lvl.getHeight();
|
||||
|
||||
|
||||
final Vect tl = topRight().sub(unit * lw, 0);
|
||||
|
||||
|
||||
bounds.setTo(tl, unit * lw, unit * lh);
|
||||
|
||||
|
||||
final Coord point = new Coord(tl.xi(), tl.yi());
|
||||
|
||||
|
||||
// FIXME do not use LWJGL directly
|
||||
|
||||
|
||||
GL11.glDisable(GL11.GL_TEXTURE_2D);
|
||||
|
||||
|
||||
GL11.glBegin(GL11.GL_QUADS);
|
||||
|
||||
|
||||
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) {
|
||||
|
||||
|
||||
final Tile t = lvl.getTile(pos);
|
||||
if (t.isNull() || (!t.isExplored() && Const.RENDER_UFOG)) continue;
|
||||
|
||||
|
||||
final Color clr = t.getMapColor();
|
||||
|
||||
|
||||
App.gfx().setColor(clr);
|
||||
|
||||
|
||||
GL11.glVertex2i(point.x, point.y);
|
||||
GL11.glVertex2i(point.x + unit, point.y);
|
||||
GL11.glVertex2i(point.x + unit, point.y + unit);
|
||||
GL11.glVertex2i(point.x, point.y + unit);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// player
|
||||
App.gfx().setColor(playerColor);
|
||||
|
||||
|
||||
final double plx = tl.xi() + plCoord.x() * unit;
|
||||
final double ply = tl.yi() + plCoord.y() * unit;
|
||||
|
||||
|
||||
GL11.glVertex2d(plx, ply);
|
||||
GL11.glVertex2d(plx + unit, ply);
|
||||
GL11.glVertex2d(plx + unit, ply + unit);
|
||||
GL11.glVertex2d(plx, ply + unit);
|
||||
|
||||
|
||||
GL11.glEnd();
|
||||
|
||||
|
||||
Color.popAlpha();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void receive(MouseButtonEvent event)
|
||||
{
|
||||
@@ -95,14 +93,14 @@ public class Minimap extends InputComponent implements MouseButtonHandler {
|
||||
if (event.isUp()) {
|
||||
final Vect relative = event.getPos().sub(bounds.origin());
|
||||
final Coord actual = Coord.make(relative.xi() / unit, relative.yi() / unit);
|
||||
|
||||
|
||||
final PlayerFacade player = WorldProvider.get().getPlayer();
|
||||
|
||||
|
||||
if (player.getLevel().getTile(actual).isExplored()) {
|
||||
player.navigateTo(actual);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
event.consume();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,69 +21,70 @@ import mightypork.utils.math.constraints.rect.Rect;
|
||||
|
||||
|
||||
public class WorldConsoleRenderer extends BaseComponent {
|
||||
|
||||
|
||||
private final Num rowHeight;
|
||||
private final FontRenderer fr;
|
||||
private final Rect itemViewRect;
|
||||
|
||||
|
||||
public WorldConsoleRenderer(Num rowHeight) {
|
||||
|
||||
|
||||
public WorldConsoleRenderer(Num rowHeight)
|
||||
{
|
||||
this.rowHeight = rowHeight;
|
||||
this.fr = new FontRenderer(Res.getFont("tiny"));
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected void renderComponent()
|
||||
{
|
||||
final double rh = rowHeight.value();
|
||||
|
||||
|
||||
final Rect lowRow = bottomEdge().growUp(rowHeight);
|
||||
|
||||
|
||||
final WorldConsole console = WorldProvider.get().getWorld().getConsole();
|
||||
final Collection<WorldConsole.Entry> entries = console.getEntries();
|
||||
|
||||
|
||||
int cnt = 0;
|
||||
|
||||
|
||||
final NumVar alph = Num.makeVar();
|
||||
|
||||
|
||||
Color.pushAlpha(alph);
|
||||
|
||||
|
||||
try {
|
||||
|
||||
|
||||
for (final WorldConsole.Entry entry : entries) {
|
||||
|
||||
|
||||
alph.setTo(entry.getAlpha());
|
||||
|
||||
|
||||
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, AlignX.LEFT, RGB.WHITE);
|
||||
|
||||
|
||||
cnt++;
|
||||
}
|
||||
|
||||
|
||||
} catch (final ConcurrentModificationException e) {
|
||||
Log.e(e); // this should not happen
|
||||
}
|
||||
|
||||
|
||||
if (console.lastPickupItem != null) {
|
||||
|
||||
|
||||
double alpha = 1;
|
||||
if (console.timeSinceLastPickup > 2) {
|
||||
alpha = 1 - Easing.CIRC_OUT.get(Calc.clamp((console.timeSinceLastPickup - 2) / 1, 0, 1));
|
||||
}
|
||||
|
||||
|
||||
alph.setTo(alpha);
|
||||
console.lastPickupItem.render(itemViewRect);
|
||||
}
|
||||
|
||||
|
||||
Color.popAlpha();
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -22,9 +22,9 @@ import mightypork.utils.math.constraints.vect.Vect;
|
||||
|
||||
|
||||
public class MIPKeyboard extends MapInteractionPlugin implements DelegatingClient, PlayerStepEndListener, Updateable {
|
||||
|
||||
|
||||
// FIXME cannot be static.
|
||||
|
||||
|
||||
//@formatter:off
|
||||
private static final KeyStroke[] keys = {
|
||||
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.down")
|
||||
};
|
||||
|
||||
|
||||
private static final Move[] sides = { Moves.W, Moves.E, Moves.N, Moves.S };
|
||||
//@formatter:on
|
||||
|
||||
|
||||
private final KeyBindingPool kbp = new KeyBindingPool();
|
||||
private final List<Object> clients = new ArrayList<>();
|
||||
|
||||
|
||||
{
|
||||
clients.add(kbp);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean doesDelegate()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public Collection<?> getChildClients()
|
||||
{
|
||||
return clients;
|
||||
}
|
||||
|
||||
|
||||
public MIPKeyboard(MapView mapView) {
|
||||
|
||||
|
||||
public MIPKeyboard(MapView mapView)
|
||||
{
|
||||
super(mapView);
|
||||
|
||||
|
||||
// bind keys
|
||||
for (int i = 0; i < 4; i++) {
|
||||
|
||||
|
||||
final int j = i;
|
||||
kbp.bindKey(keys[i], Trigger.RISING, new Runnable() {
|
||||
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
@@ -75,41 +76,41 @@ public class MIPKeyboard extends MapInteractionPlugin implements DelegatingClien
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void onStepFinished(EntityPlayer player)
|
||||
{
|
||||
walkByKey();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onClick(Vect mouse, int button, boolean down)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private void clickSide(Move side)
|
||||
{
|
||||
if (isImmobile() || getPlayer().isMoving()) return;
|
||||
|
||||
|
||||
mapView.plc.clickTile(side);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private boolean walkByKey()
|
||||
{
|
||||
if (isImmobile()) return false;
|
||||
|
||||
|
||||
if (mapView.plc.getPlayer().getMoveProgress() < 0.8) return false;
|
||||
|
||||
|
||||
if (LwjglInputModule.getActiveKeyMod() != Keys.MOD_NONE) return false;
|
||||
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (keys[i].isDown()) {
|
||||
|
||||
|
||||
final Move side = sides[i];
|
||||
if (mapView.plc.canGo(side)) {
|
||||
mapView.plc.go(side);
|
||||
@@ -121,8 +122,8 @@ public class MIPKeyboard extends MapInteractionPlugin implements DelegatingClien
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void update(double delta)
|
||||
{
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package mightypork.rogue.world.gui.interaction;
|
||||
|
||||
|
||||
import mightypork.gamecore.backends.lwjgl.LwjglInputModule;
|
||||
import mightypork.gamecore.core.App;
|
||||
import mightypork.rogue.world.entity.impl.EntityPlayer;
|
||||
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 {
|
||||
|
||||
|
||||
private static final int LEFT = 0; // left
|
||||
private static final int RIGHT = 1; // left
|
||||
|
||||
|
||||
public MIPMouse(MapView mapView) {
|
||||
|
||||
|
||||
public MIPMouse(MapView mapView)
|
||||
{
|
||||
super(mapView);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void update(double delta)
|
||||
{
|
||||
if (isImmobile()) return;
|
||||
|
||||
|
||||
final Vect pos = App.input().getMousePos();
|
||||
if (!pos.isInside(mapView)) return;
|
||||
|
||||
|
||||
if (App.input().isMouseButtonDown(LEFT)) {
|
||||
if (mouseWalk(pos)) return;
|
||||
if (mapView.plc.getPlayer().isMoving() && troToNav(pos)) return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean onClick(Vect mouse, int button, boolean down)
|
||||
{
|
||||
if (isImmobile()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
final Vect pos = mapView.toWorldPos(mouse);
|
||||
|
||||
|
||||
if (button == LEFT && !down) {
|
||||
// try to click tile
|
||||
|
||||
|
||||
if (mapView.plc.clickTile(pos)) return true;
|
||||
}
|
||||
|
||||
|
||||
final Tile t = mapView.plc.getLevel().getTile(Coord.fromVect(pos));
|
||||
if (button == RIGHT && !down && t.isWalkable()) {
|
||||
if (troToNav(mouse)) return true;
|
||||
return mouseWalk(mouse);
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private boolean troToNav(Vect mouse)
|
||||
{
|
||||
if (isImmobile()) return false;
|
||||
|
||||
|
||||
final Coord plpos = mapView.plc.getPlayer().getCoord();
|
||||
|
||||
|
||||
final Coord clicked = Coord.fromVect(mapView.toWorldPos(mouse));
|
||||
if (clicked.equals(plpos)) return false;
|
||||
|
||||
|
||||
final Tile t = mapView.plc.getLevel().getTile(clicked);
|
||||
if (!t.isWalkable() || !t.isExplored()) return false;
|
||||
|
||||
|
||||
mapView.plc.navigateTo(clicked);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
private boolean mouseWalk(Vect pos)
|
||||
{
|
||||
if (isImmobile()) return false;
|
||||
|
||||
|
||||
final Coord plpos = mapView.plc.getPlayer().getCoord();
|
||||
final Coord clicked = Coord.fromVect(mapView.toWorldPos(pos));
|
||||
if (clicked.equals(plpos)) return false;
|
||||
|
||||
|
||||
final Polar p = Polar.fromCoord(clicked.x - plpos.x, clicked.y - plpos.y);
|
||||
|
||||
|
||||
final int dir = Deg.roundTo90(p.getAngleDeg()) / 90;
|
||||
|
||||
|
||||
switch (dir) {
|
||||
case 0:
|
||||
return mapView.plc.tryGo(Moves.E);
|
||||
|
||||
|
||||
case 1:
|
||||
return mapView.plc.tryGo(Moves.S);
|
||||
|
||||
|
||||
case 2:
|
||||
return mapView.plc.tryGo(Moves.W);
|
||||
|
||||
|
||||
case 3:
|
||||
return mapView.plc.tryGo(Moves.N);
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public void onStepFinished(EntityPlayer player)
|
||||
{
|
||||
if (isImmobile()) return;
|
||||
|
||||
|
||||
final Vect pos = App.input().getMousePos();
|
||||
if (!pos.isInside(mapView)) return;
|
||||
|
||||
|
||||
if (App.input().isMouseButtonDown(LEFT)) {
|
||||
if (mouseWalk(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 {
|
||||
|
||||
|
||||
protected final MapView mapView;
|
||||
|
||||
|
||||
public MapInteractionPlugin(MapView mapView) {
|
||||
|
||||
|
||||
public MapInteractionPlugin(MapView mapView)
|
||||
{
|
||||
super();
|
||||
this.mapView = mapView;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
protected PlayerFacade getPlayer()
|
||||
{
|
||||
return mapView.plc.getPlayer();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
protected boolean isImmobile()
|
||||
{
|
||||
return getPlayer().isDead() || getWorld().isPaused();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
protected World getWorld()
|
||||
{
|
||||
return WorldProvider.get().getWorld();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
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 {
|
||||
|
||||
|
||||
private final ItemModel model;
|
||||
private ItemRenderer renderer;
|
||||
private int amount = 1;
|
||||
private int uses = 1;
|
||||
|
||||
|
||||
public Item(ItemModel model) {
|
||||
|
||||
|
||||
public Item(ItemModel model)
|
||||
{
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public final void render(Rect rect)
|
||||
{
|
||||
if (renderer == null) {
|
||||
renderer = makeRenderer();
|
||||
}
|
||||
|
||||
|
||||
renderer.render(rect);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
protected abstract ItemRenderer makeRenderer();
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
@Stub
|
||||
public void save(IonDataBundle out)
|
||||
@@ -43,8 +44,8 @@ public abstract class Item implements IonBundled {
|
||||
out.put("c", amount);
|
||||
out.put("u", uses);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
@Stub
|
||||
public void load(IonDataBundle in)
|
||||
@@ -52,33 +53,33 @@ public abstract class Item implements IonBundled {
|
||||
amount = in.get("c", amount);
|
||||
uses = in.get("u", uses);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public final ItemModel getModel()
|
||||
{
|
||||
return model;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Stub
|
||||
protected int getMaxStackSize()
|
||||
{
|
||||
return isStackable() ? 65535 : 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public boolean canStackWith(Item other)
|
||||
{
|
||||
return (getModel().id == other.getModel().id);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
protected abstract boolean isStackable();
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Add another item to this item
|
||||
*
|
||||
*
|
||||
* @param added added item
|
||||
* @return if items are compatible and the added item was completely moved
|
||||
* 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 (added.isEmpty()) return true;
|
||||
|
||||
|
||||
final int room = getMaxStackSize() - this.amount;
|
||||
final int avail = added.amount;
|
||||
|
||||
|
||||
final int moved = Math.min(room, avail);
|
||||
this.amount += moved;
|
||||
added.amount -= moved;
|
||||
|
||||
|
||||
return added.isEmpty();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return item amount in the stack
|
||||
*/
|
||||
@@ -107,86 +108,86 @@ public abstract class Item implements IonBundled {
|
||||
{
|
||||
return Math.max(0, amount);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Consume one item.
|
||||
*
|
||||
*
|
||||
* @return true if the item is fully consumed and should be removed.
|
||||
*/
|
||||
public void consume()
|
||||
{
|
||||
if (isEmpty()) throw new RuntimeException("Item is empty, cannot consume.");
|
||||
|
||||
|
||||
amount--;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public boolean isEmpty()
|
||||
{
|
||||
return getAmount() == 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public Item split(int removed)
|
||||
{
|
||||
if (isEmpty()) throw new RuntimeException("Item is empty, cannot split.");
|
||||
|
||||
|
||||
final int realRemoved = Math.min(removed, amount);
|
||||
|
||||
|
||||
final Item newItm = model.createItem();
|
||||
newItm.uses = uses;
|
||||
newItm.amount = realRemoved;
|
||||
this.amount -= realRemoved;
|
||||
|
||||
|
||||
return newItm;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public abstract int getAttackPoints();
|
||||
|
||||
|
||||
|
||||
|
||||
public abstract int getFoodPoints();
|
||||
|
||||
|
||||
|
||||
|
||||
public abstract ItemType getType();
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return Support.str(getClass()) + " x " + getAmount();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public int getRemainingUses()
|
||||
{
|
||||
return uses;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public abstract int getMaxUses();
|
||||
|
||||
|
||||
|
||||
|
||||
public void setRemainingUses(int uses)
|
||||
{
|
||||
this.uses = Calc.clamp(uses, 0, getMaxUses());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void use()
|
||||
{
|
||||
if (uses > 0) uses--;
|
||||
if (uses == 0) consume();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public abstract boolean isDamageable();
|
||||
|
||||
|
||||
|
||||
|
||||
public abstract String getVisualName();
|
||||
|
||||
|
||||
|
||||
|
||||
@Stub
|
||||
public boolean pickUp(PlayerFacade pl)
|
||||
{
|
||||
|
||||
@@ -9,23 +9,24 @@ import mightypork.utils.math.Calc;
|
||||
|
||||
/**
|
||||
* Item model (builder)
|
||||
*
|
||||
*
|
||||
* @author Ondřej Hruška (MightyPork)
|
||||
*/
|
||||
public final class ItemModel {
|
||||
|
||||
|
||||
/** Model ID */
|
||||
public final int id;
|
||||
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);
|
||||
this.id = id;
|
||||
this.itemClass = item;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @return new item instance of this type
|
||||
*/
|
||||
@@ -33,32 +34,32 @@ public final class ItemModel {
|
||||
{
|
||||
try {
|
||||
final Item itm = itemClass.getConstructor(ItemModel.class).newInstance(this);
|
||||
|
||||
|
||||
itm.setRemainingUses(itm.getMaxUses());
|
||||
|
||||
|
||||
return itm;
|
||||
|
||||
|
||||
} catch (final Exception e) {
|
||||
throw new RuntimeException("Could not instantiate an item.", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public Item loadItem(IonDataBundle in) throws IOException
|
||||
{
|
||||
final Item t = createItem();
|
||||
t.load(in);
|
||||
return t;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void saveItem(IonDataBundle out, Item item) throws IOException
|
||||
{
|
||||
if (itemClass != item.getClass()) throw new RuntimeException("Item class mismatch.");
|
||||
item.save(out);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public Item createItemDamaged(int minimalHealthPercent)
|
||||
{
|
||||
final Item item = createItem();
|
||||
|
||||
@@ -5,15 +5,16 @@ import mightypork.utils.math.constraints.rect.Rect;
|
||||
|
||||
|
||||
public abstract class ItemRenderer {
|
||||
|
||||
|
||||
protected final Item item;
|
||||
|
||||
|
||||
public ItemRenderer(Item item) {
|
||||
|
||||
|
||||
public ItemRenderer(Item item)
|
||||
{
|
||||
this.item = item;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
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.ItemMeat;
|
||||
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.IonInput;
|
||||
import mightypork.utils.ion.IonOutput;
|
||||
@@ -16,13 +22,13 @@ import mightypork.utils.ion.IonOutput;
|
||||
|
||||
/**
|
||||
* Item registry
|
||||
*
|
||||
*
|
||||
* @author Ondřej Hruška (MightyPork)
|
||||
*/
|
||||
public final class Items {
|
||||
|
||||
|
||||
private static final ItemModel[] items = new ItemModel[256];
|
||||
|
||||
|
||||
public static final ItemModel MEAT = new ItemModel(1, ItemMeat.class);
|
||||
public static final ItemModel CHEESE = new ItemModel(2, ItemCheese.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 TWIG = new ItemModel(10, ItemTwig.class);
|
||||
public static final ItemModel KNIFE = new ItemModel(11, ItemKnife.class);
|
||||
|
||||
|
||||
|
||||
|
||||
public static void register(int id, ItemModel model)
|
||||
{
|
||||
if (id < 0 || id >= items.length) {
|
||||
throw new IllegalArgumentException("Item ID " + id + " is out of range.");
|
||||
}
|
||||
|
||||
|
||||
if (items[id] != null) {
|
||||
throw new IllegalArgumentException("Item ID " + id + " already in use.");
|
||||
}
|
||||
|
||||
|
||||
items[id] = model;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public static ItemModel get(int id)
|
||||
{
|
||||
final ItemModel m = items[id];
|
||||
|
||||
|
||||
if (m == null) {
|
||||
throw new IllegalArgumentException("No item with ID " + id + ".");
|
||||
}
|
||||
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public static Item loadItem(IonInput in) throws IOException
|
||||
{
|
||||
final int id = in.readIntByte();
|
||||
final ItemModel model = get(id);
|
||||
return model.loadItem(in.readBundle());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public static void saveItem(IonOutput out, Item item) throws IOException
|
||||
{
|
||||
final ItemModel model = item.getModel();
|
||||
|
||||
|
||||
out.writeIntByte(model.id);
|
||||
|
||||
|
||||
final IonDataBundle ib = new IonDataBundle();
|
||||
model.saveItem(ib, item);
|
||||
out.writeBundle(ib);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public static void loadItems(IonInput in, Collection<Item> items) throws IOException
|
||||
{
|
||||
items.clear();
|
||||
@@ -89,16 +95,16 @@ public final class Items {
|
||||
items.add(loadItem(in));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public static void saveItems(IonOutput out, Collection<Item> items) throws IOException
|
||||
{
|
||||
for (final Item entity : items) {
|
||||
out.startEntry();
|
||||
saveItem(out, entity);
|
||||
}
|
||||
|
||||
|
||||
out.endSequence();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -7,40 +7,41 @@ import mightypork.rogue.world.item.ItemType;
|
||||
|
||||
|
||||
public abstract class ItemBaseFood extends Item {
|
||||
|
||||
public ItemBaseFood(ItemModel model) {
|
||||
|
||||
public ItemBaseFood(ItemModel model)
|
||||
{
|
||||
super(model);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isStackable()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public final int getAttackPoints()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public final ItemType getType()
|
||||
{
|
||||
return ItemType.FOOD;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isDamageable()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public int getMaxUses()
|
||||
{
|
||||
|
||||
@@ -7,37 +7,38 @@ import mightypork.rogue.world.item.ItemType;
|
||||
|
||||
|
||||
public abstract class ItemBaseWeapon extends Item {
|
||||
|
||||
public ItemBaseWeapon(ItemModel model) {
|
||||
|
||||
public ItemBaseWeapon(ItemModel model)
|
||||
{
|
||||
super(model);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isStackable()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public final int getFoodPoints()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public final ItemType getType()
|
||||
{
|
||||
return ItemType.WEAPON;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isDamageable()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -11,77 +11,78 @@ import mightypork.rogue.world.item.render.QuadItemRenderer;
|
||||
|
||||
|
||||
public class ItemHeartPiece extends Item {
|
||||
|
||||
public ItemHeartPiece(ItemModel model) {
|
||||
|
||||
public ItemHeartPiece(ItemModel model)
|
||||
{
|
||||
super(model);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected ItemRenderer makeRenderer()
|
||||
{
|
||||
return new QuadItemRenderer(this, Res.getTxQuad("item.heart"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
protected boolean isStackable()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public int getAttackPoints()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public int getFoodPoints()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public ItemType getType()
|
||||
{
|
||||
return ItemType.ACTIVE;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public int getMaxUses()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isDamageable()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public String getVisualName()
|
||||
{
|
||||
return "Heart Piece";
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public boolean pickUp(PlayerFacade pl)
|
||||
{
|
||||
pl.setHealthMax(pl.getHealthMax() + 2); // two points / heart
|
||||
pl.setHealth(pl.getHealthMax());
|
||||
|
||||
|
||||
pl.getWorld().getConsole().msgHeartPiece();
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user