package me.hammerle.supersnuvi.gamelogic; import java.io.File; import java.util.Arrays; import java.util.HashMap; import javafx.scene.input.KeyCode; import javafx.scene.paint.Color; import me.hammerle.supersnuvi.input.IKeyHandler; import me.hammerle.supersnuvi.rendering.IGameRenderer; import me.hammerle.supersnuvi.savegame.SimpleConfig; import me.hammerle.supersnuvi.tiles.*; import me.hammerle.supersnuvi.util.SoundUtils; public class StateRenderer { // constants public static final ColoredBaseTile FALLBACK_TILE = new ColoredBaseTile(Color.TRANSPARENT); // rendering private final IGameRenderer renderer; // tiles private final HashMap registeredTiles; // levels private Level currentLevel; private final Level[] levels; private int levelIndex; private final SimpleConfig config; public static boolean noSound; public StateRenderer(IGameRenderer renderer) { this.renderer = renderer; this.registeredTiles = new HashMap<>(); File[] files = new File("./levels").listFiles(); Arrays.sort(files, (o1, o2) -> o1.compareTo(o2)); levels = new Level[files.length]; for(int i = 0; i < levels.length; i++) { levels[i] = new Level(this, files[i]); } currentLevel = null; levelIndex = 0; config = new SimpleConfig("savegame.txt"); config.load(); noSound = !config.getBoolean("sound", false); SoundUtils.loadSounds(); registerTiles(); } // ------------------------------------------------------------------------- // tile stuff // ------------------------------------------------------------------------- private void registerTiles() { // dirt for(int i = 0; i < 16; i++) { registeredTiles.put(i, new BaseBoxTile("dirt/dirt" + i)); } // grass registeredTiles.put(20, new BaseBoxTile("grass/grass4")); registeredTiles.put(21, new BaseBoxTile("grass/grass7")); registeredTiles.put(22, new BaseBoxTile("grass/grass8")); registeredTiles.put(23, new BaseBoxTile("grass/grass9")); registeredTiles.put(28, new BaseBoxTile("grass/grass10")); registeredTiles.put(29, new BaseBoxTile("grass/grass14")); registeredTiles.put(30, new BaseBoxTile("grass/grass15")); registeredTiles.put(31, new BaseBoxTile("grass/grass16")); // bottled soul registeredTiles.put(BottledSoulTile.ID, new BottledSoulTile()); // bounce shroom registeredTiles.put(48, new TrampolinTile()); // crumbling stones registeredTiles.put(64, new CrumblingStoneTile()); // spike trap registeredTiles.put(80, new SpikeTile()); // water registeredTiles.put(96, new WaterTile(true)); registeredTiles.put(97, new WaterTile(false)); // snuvi start block registeredTiles.put(StartTile.ID, new StartTile()); // sky registeredTiles.put(128, new SkyTile()); // ice registeredTiles.put(144, new SlipperyTile("ice/ice")); // slippery slime registeredTiles.put(148, new SlipperyTile("slippery_slime/slippery_slime148")); registeredTiles.put(149, new SlipperyTile("slippery_slime/slippery_slime149")); registeredTiles.put(150, new SlipperyTile("slippery_slime/slippery_slime150")); registeredTiles.put(151, new SlipperyTile("slippery_slime/slippery_slime151")); registeredTiles.put(156, new SlipperyTile("slippery_slime/slippery_slime156")); registeredTiles.put(157, new SlipperyTile("slippery_slime/slippery_slime157")); registeredTiles.put(158, new SlipperyTile("slippery_slime/slippery_slime158")); registeredTiles.put(159, new SlipperyTile("slippery_slime/slippery_slime159")); // end level registeredTiles.put(160, new GoalTile("end/end")); registeredTiles.put(161, new GoalTile("end/end2")); // thorns registeredTiles.put(176, new KillTile("thorns/thorns_bottom", 2)); registeredTiles.put(177, new KillTile("thorns/thorns_mid", 2)); registeredTiles.put(178, new KillTile("thorns/thorns_top", 2)); registeredTiles.put(179, new KillTile("thorns/thorns_bottom", -3)); registeredTiles.put(180, new KillTile("thorns/thorns_mid", -3)); registeredTiles.put(181, new KillTile("thorns/thorns_top", -3)); // decoration shrooms registeredTiles.put(208, new DecoShroomTile("shrooms/shroom")); registeredTiles.put(209, new DecoShroomTile("shrooms/shroom2")); registeredTiles.put(210, new DecoShroomTile("shrooms/shroom3")); registeredTiles.put(211, new DecoShroomTile("shrooms/shroom4")); } public Tile getTile(int id) { Tile tile = registeredTiles.get(id); if(tile == null) { return FALLBACK_TILE; } return tile; } public Tile getInteractionTile(int id) { return registeredTiles.getOrDefault(id, FALLBACK_TILE); } public void resetTiles() { registeredTiles.values().forEach(v -> v.reset()); } // ------------------------------------------------------------------------- // tick, rendering // ------------------------------------------------------------------------- public IGameRenderer getRenderer() { return renderer; } private static final int MENU_WIDTH = 26; private static final int MENU_X = 12; private static final int MENU_Y = 5; private static final int MENU_MAX = 11; private static final char[] TABLE_TOP = getCharLine((char) 131, (char) 136, (char) 133, (char) 136); private static final char[] TABLE_HEADING = getCharLine((char) 134, "Wähle ein Level ...", (char) 134, ' '); private static final char[] TABLE_MID = getCharLine((char) 130, (char) 136, (char) 132, (char) 129); private static final char[] TABLE_BOTTOM = getCharLine((char) 137, (char) 136, (char) 138, (char) 135); private static final char[] TABLE_MORE = getCharLine((char) 134, "...", (char) 134, (char) 134); private static char[] getCharLine(char start, char mid, char end, char spacer) { char[] c = new char[MENU_WIDTH]; Arrays.fill(c, 1, c.length - 1, mid); c[0] = start; c[c.length - 1] = end; c[c.length - 3] = spacer; return c; } private static char[] getCharLine(char start, String s, char end, char spacer) { char[] chars = new char[MENU_WIDTH]; chars[0] = start; chars[chars.length - 1] = end; int border = Math.min(MENU_WIDTH - 2, s.length()); for(int i = 0; i < border; i++) { chars[i + 1] = s.charAt(i); } chars[chars.length - 3] = spacer; return chars; } public void tick(IKeyHandler keys) { if(currentLevel != null) { SoundUtils.playSound(SoundUtils.SONG_1, false); SoundUtils.stopSound(SoundUtils.MENU_MUSIC); currentLevel.tick(keys); // doing that here to prevent concurent modification if(currentLevel.shouldFinish()) { currentLevel.resetLevel(); config.set("level." + currentLevel.getName(), true); currentLevel = null; config.save(); return; } if(currentLevel.shouldReset()) { currentLevel.resetLevel(); } if(keys.isKeyJustReleased(KeyCode.ESCAPE) > 0) { currentLevel = null; } } else { SoundUtils.playSound(SoundUtils.MENU_MUSIC, false); SoundUtils.stopSound(SoundUtils.SONG_1); if(keys.isKeyJustReleased(KeyCode.ENTER) > 0) { currentLevel = levels[levelIndex]; return; } if(keys.getKeyDownTime(KeyCode.DOWN) > 12) { keys.resetKeyTime(KeyCode.DOWN); levelIndex++; } else if(keys.getKeyDownTime(KeyCode.UP) > 12) { keys.resetKeyTime(KeyCode.UP); levelIndex--; } else if(keys.isKeyJustReleased(KeyCode.DOWN) > 0) { levelIndex++; } else if(keys.isKeyJustReleased(KeyCode.UP) > 0) { levelIndex--; } if(levelIndex < 0) { levelIndex = 0; } else if(levelIndex >= levels.length) { levelIndex = levels.length - 1; } renderer.prepareStringDrawing(Color.WHITE); double lineHeight = renderer.getLineHeight(); double x = renderer.getStringOffsetX(MENU_X); double y = renderer.getStringOffsetY(MENU_Y); renderer.drawChars(TABLE_TOP, x, y); y += lineHeight; renderer.drawChars(TABLE_HEADING, x, y); y += lineHeight; renderer.drawChars(TABLE_MID, x, y); y += lineHeight; if(levels.length > MENU_MAX) { int upperHalf = MENU_MAX / 2; int downHalf = levels.length - upperHalf; if(levelIndex < upperHalf) { paintStringMarking(lineHeight, x, y, levelIndex); y = paintLevelName(x, y, 0, MENU_MAX - 1); renderer.drawChars(TABLE_MORE, x, y); y += lineHeight; } else if(levelIndex >= downHalf) { paintStringMarking(lineHeight, x, y, levelIndex - downHalf + upperHalf + ((MENU_MAX & 1) != 0 ? 1 : 0)); renderer.drawChars(TABLE_MORE, x, y); y += lineHeight; y = paintLevelName(x, y, levels.length - MENU_MAX + 1, MENU_MAX - 1); } else { paintStringMarking(lineHeight, x, y, upperHalf); renderer.drawChars(TABLE_MORE, x, y); y += lineHeight; y = paintLevelName(x, y, levelIndex - upperHalf + 1, MENU_MAX - 2); renderer.drawChars(TABLE_MORE, x, y); y += lineHeight; } } else { paintStringMarking(lineHeight, x, y, levelIndex); y = paintLevelName(x, y, 0, levels.length); } renderer.drawChars(TABLE_BOTTOM, x, y); renderer.stopStringDrawing(); } } private double paintLevelName(double x, double y, int from, int length) { double lineHeight = renderer.getLineHeight(); char[] chars = new char[MENU_WIDTH]; chars[0] = 134; chars[MENU_WIDTH - 1] = 134; String s; int border; length += from; length = Math.min(length, levels.length); for(int j = from; j < length; j++) { s = levels[j].getName(); border = Math.min(MENU_WIDTH - 4, s.length()); for(int i = 0; i < border; i++) { chars[i + 1] = s.charAt(i); } Arrays.fill(chars, border + 1, MENU_WIDTH - 1, (char) 0); chars[chars.length - 3] = 134; if(config.getBoolean("level." + s, false)) { chars[chars.length - 2] = 'x'; } else { chars[chars.length - 2] = 'o'; } renderer.drawChars(chars, x, y); y += lineHeight; } return y; } private void paintStringMarking(double lineHeight, double x, double y, int pos) { renderer.fillRec( x + renderer.getStringWidth(1), y + pos * lineHeight - 1, renderer.getStringWidth(MENU_WIDTH - 4), lineHeight + 1, Color.GRAY); renderer.fillRec( x + renderer.getStringWidth(MENU_WIDTH - 2), y + pos * lineHeight - 1, renderer.getStringWidth(1), lineHeight + 1, Color.GRAY); } public void tick() { registeredTiles.values().forEach(tile -> tile.tick()); } }