Bläddra i källkod

start of open gl port, menu works, levels outstanding

Kajetan Johannes Hammerle 6 år sedan
förälder
incheckning
09e3b5139a

+ 15 - 15
options.txt

@@ -1,15 +1,15 @@
-key.combat="A"
-key.combat.attack="H"
-key.combat.block="G"
-key.combat.dash="D"
-key.combat.dodge="F"
-key.combat.switchface="S"
-key.down="Down"
-key.enter="Enter"
-key.escape="Esc"
-key.jump="Space"
-key.left="Left"
-key.right="Right"
-key.run="Shift"
-key.up="Up"
-sound=true
+key.combat=65
+key.combat.attack=72
+key.combat.block=71
+key.combat.dash=68
+key.combat.dodge=70
+key.combat.switchface=83
+key.down=264
+key.enter=257
+key.escape=256
+key.jump=32
+key.left=263
+key.right=262
+key.run=340
+key.up=265
+sound=false

+ 685 - 0
src/me/hammerle/supersnuvi/Game.java

@@ -0,0 +1,685 @@
+package me.hammerle.supersnuvi;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.HashMap;
+import me.hammerle.snuviengine.api.ColorRenderer;
+import me.hammerle.snuviengine.api.Engine;
+import me.hammerle.snuviengine.api.FontRenderer;
+import me.hammerle.snuviengine.api.KeyBinding;
+import me.hammerle.snuviengine.api.Shader;
+import me.hammerle.supersnuvi.gamelogic.Level;
+import me.hammerle.supersnuvi.savegame.SimpleConfig;
+import me.hammerle.supersnuvi.tiles.*;
+import me.hammerle.supersnuvi.util.SoundUtils;
+
+public class Game extends Engine
+{
+    private static Game instance;
+    
+    public static Game get()
+    {
+        return instance;
+    }
+    
+    // constants
+    public static final ColoredBaseTile FALLBACK_TILE = new ColoredBaseTile(0, 0, 0, 0);
+    
+    // tiles
+    private final HashMap<Integer, Tile> registeredTiles = new HashMap<>();;
+    
+    // levels
+    private Level currentLevel = null;
+    private final Level[] levels;
+    private int levelIndex = 0;
+    
+    // config and savegames
+    private final SimpleConfig config = new SimpleConfig("options.txt", true);
+    private final SimpleConfig[] saveSlots = new SimpleConfig[] 
+    {
+        new SimpleConfig("slot1.txt", true), 
+        new SimpleConfig("slot2.txt", true), 
+        new SimpleConfig("slot3.txt", true)
+    };
+    
+    private int screen = 0;
+    private int startScreenIndex = 0;
+    private int optionScreenIndex = 0;
+    private int slotScreenIndex = 0;
+    
+    private boolean optionsDirty = false;
+    
+    // sound
+    private boolean sound = config.getBoolean("sound", false);
+
+    public Game()
+    {
+        instance = this;
+        registerTiles();
+        
+        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(files[i]);
+        }
+    }
+    
+    @Override
+    public void init()
+    {
+        setMaxFps(120);
+        setNanosPerTick(50_000_000);
+    }
+    
+    // -------------------------------------------------------------------------
+    // tick, rendering
+    // -------------------------------------------------------------------------
+
+    @Override
+    public void tick()
+    {
+        if(currentLevel != null)
+        {
+            SoundUtils.playSound(SoundUtils.Sound.SONG_1);
+            SoundUtils.stopSound(SoundUtils.Sound.MENU_MUSIC);
+            
+            currentLevel.tick();
+            
+            // doing that here to prevent concurent modification
+            if(currentLevel.shouldFinish())
+            {
+                String base = "level." + currentLevel.getName();
+                SimpleConfig sp = saveSlots[slotScreenIndex];
+                
+                // save success
+                sp.set(base, true);
+                
+                // update time, if a new highscore was scored
+                double time = sp.getDouble(base + ".time", Integer.MAX_VALUE);
+                if(currentLevel.getTime() < time)
+                {
+                    sp.set(base + ".time", currentLevel.getTime());
+                }
+                
+                // update bottles, if a new highscore was scored
+                int bottles = sp.getInt(base + ".bottles", 0);
+                if(currentLevel.getCurrentBottles() > bottles)
+                {
+                    sp.set(base + ".bottles", currentLevel.getCurrentBottles());
+                }
+                
+                // final save
+                sp.save();
+                
+                currentLevel.resetLevel();
+                currentLevel = null;
+                return;
+            }
+            
+            if(currentLevel.shouldReset())
+            {
+                if(currentLevel.resetLevel())
+                {
+                    currentLevel = null;
+                }
+            }
+            
+            if(Keys.ESCAPE.isReleased())
+            {
+                currentLevel = null;
+            }
+        }
+        else
+        {
+            SoundUtils.playSound(SoundUtils.Sound.MENU_MUSIC);
+            SoundUtils.stopSound(SoundUtils.Sound.SONG_1);
+            switch(screen)
+            {
+                case 0: // start screen
+                {
+                    menuMove(() -> 
+                    {
+                        // do nothing on escape in start screen
+                    }, () -> 
+                    {
+                        switch(startScreenIndex)
+                        { 
+                            case 0:
+                                screen = 1;
+                                break;
+                            case 1:
+                                screen = 2;
+                                break;
+                            case 2:
+                                stop();
+                                break;
+                        }
+                    }, () -> startScreenIndex++, () -> startScreenIndex--, () -> 
+                    {
+                        if(startScreenIndex < 0)
+                        {
+                            startScreenIndex = 0;
+                        }
+                        else if(startScreenIndex >= 3)
+                        {
+                            startScreenIndex = 2;
+                        }
+                    });
+                    break;
+                }
+                case 1: // slot screen
+                {
+                    menuMove(() -> 
+                    {
+                        screen = 0;
+                    }, () -> 
+                    {
+                        if(slotScreenIndex == 3)
+                        {
+                            screen = 0;
+                            return;
+                        }
+                        screen = 3;
+                    }, () -> slotScreenIndex++, () -> slotScreenIndex--, () -> 
+                    {
+                        if(slotScreenIndex < 0)
+                        {
+                            slotScreenIndex = 0;
+                        }
+                        else if(slotScreenIndex >= 4)
+                        {
+                            slotScreenIndex = 3;
+                        }
+                    });
+                    break;
+                }
+                case 2: // option screen
+                {
+                    menuMove(() -> 
+                    {
+                        screen = 0;
+                    }, () -> 
+                    {
+                        switch(optionScreenIndex)
+                        {
+                            case 0: // toggle sound
+                                sound = !sound;
+                                if(!sound)
+                                {
+                                    SoundUtils.turnSoundOff();
+                                }
+                                optionsDirty = true;
+                                break;
+                            case 15: // save options
+                                Keys.write(config);
+                                config.set("sound", sound);
+                                config.save();
+                                optionsDirty = false;
+                                break;
+                            case 16: // go back
+                                screen = 0;
+                                break;
+                            default: // rebind keys
+                                Keys.rebind(Keys.get(optionScreenIndex - 1));
+                                optionsDirty = true;
+                                break;
+                        }
+                    }, () -> optionScreenIndex++, () -> optionScreenIndex--, () -> 
+                    {
+                        if(optionScreenIndex < 0)
+                        {
+                            optionScreenIndex = 0;
+                        }
+                        int options = Keys.getAmount() + 3;
+                        if(optionScreenIndex >= options)
+                        {
+                            optionScreenIndex = options - 1;
+                        }
+                    });
+                    break;
+                }
+                case 3: // level choose screen
+                {                   
+                    menuMove(() -> 
+                    {
+                        screen = 1;
+                    }, () -> currentLevel = levels[levelIndex], () -> levelIndex++, () -> levelIndex--, () -> 
+                    {
+                        if(levelIndex < 0)
+                        {
+                            levelIndex = 0;
+                        }
+                        else if(levelIndex >= levels.length)
+                        {
+                            levelIndex = levels.length - 1;
+                        }
+                    });
+                    break;
+                }
+            }
+        }
+    }
+    
+    public void tickTiles()
+    {
+        registeredTiles.values().forEach(tile -> tile.tick());
+    }
+    
+    private final static int COLOR_BROWN = 0xFF13458B;
+    private final static int COLOR_OVERLAY = 0x77000000;
+
+    private String getKeyName(KeyBinding key)
+    {
+        if(key.isRebinding())
+        {
+            return "[...]";
+        }
+        return key.getName();
+    }
+    
+    @Override
+    public void renderTick(float lag)
+    {
+        if(currentLevel != null)
+        {
+            currentLevel.render(lag);
+            return;
+        }
+        switch(screen)
+        {
+            case 0:
+            {
+                ColorRenderer cr = Shader.getColorRenderer();
+                FontRenderer fr = Shader.getFontRenderer();
+                
+                float width = Shader.getViewWidth();
+                float height = Shader.getViewHeight();
+                
+                float line = fr.getHeight();
+                
+                float left = width * 0.25f;
+                float right = width * 0.75f;
+                float top = (height - line * 7.0f) * 0.5f;
+                float bottom = top + line * 7.0f;
+                
+                Shader.setTextureEnabled(false);
+                Shader.setColorEnabled(true);
+                
+                // brown background
+                cr.drawRectangle(0, 0, width, height, COLOR_BROWN); 
+                
+                Shader.setBlendingEnabled(true);
+                cr.drawRectangle(left, top, right, bottom, COLOR_OVERLAY);
+                float base = top + (3 + startScreenIndex) * line;
+                cr.drawRectangle(left, base, right, base + line, COLOR_OVERLAY);
+                Shader.setBlendingEnabled(false);
+                        
+                Shader.setTextureEnabled(true);
+                float y = top + line;
+                y = fr.drawString(left + line, y, "Super Snuvi"); 
+                y += line;
+                y = fr.drawString(left + line, y, "Start Game"); 
+                y = fr.drawString(left + line, y, "Options"); 
+                fr.drawString(left + line, y, "Exit Game");
+                break;
+            }
+            case 1:
+            {
+                ColorRenderer cr = Shader.getColorRenderer();
+                FontRenderer fr = Shader.getFontRenderer();
+                
+                float width = Shader.getViewWidth();
+                float height = Shader.getViewHeight();
+                
+                float line = fr.getHeight();
+                
+                float left = width * 0.25f;
+                float right = width * 0.75f;
+                float top = (height - line * 8.0f) * 0.5f;
+                float bottom = top + line * 8.0f;
+                
+                Shader.setTextureEnabled(false);
+                Shader.setColorEnabled(true);
+                
+                // brown background
+                cr.drawRectangle(0, 0, width, height, COLOR_BROWN); 
+                
+                Shader.setBlendingEnabled(true);
+                cr.drawRectangle(left, top, right, bottom, COLOR_OVERLAY);
+                float base = top + (3 + slotScreenIndex) * line;
+                cr.drawRectangle(left, base, right, base + line, COLOR_OVERLAY);
+                Shader.setBlendingEnabled(false);
+                        
+                Shader.setTextureEnabled(true);
+                float y = top + line;
+                y = fr.drawString(left + line, y, "Choose a Savegame"); 
+                y += line;
+                y = fr.drawString(left + line, y, "Slot 1"); 
+                y = fr.drawString(left + line, y, "Slot 2"); 
+                y = fr.drawString(left + line, y, "Slot 3"); 
+                fr.drawString(left + line, y, "Back"); 
+                break;
+            }
+            case 2:
+            {
+                ColorRenderer cr = Shader.getColorRenderer();
+                FontRenderer fr = Shader.getFontRenderer();
+                
+                float width = Shader.getViewWidth();
+                float height = Shader.getViewHeight();
+                
+                float line = fr.getHeight();
+                
+                float left = width * 0.2f;
+                float right = width * 0.8f;
+                float top = (height - line * 21.0f) * 0.5f;
+                float bottom = top + line * 21.0f;
+                
+                Shader.setTextureEnabled(false);
+                Shader.setColorEnabled(true);
+                
+                // brown background
+                cr.drawRectangle(0, 0, width, height, COLOR_BROWN); 
+                
+                Shader.setBlendingEnabled(true);
+                cr.drawRectangle(left, top, right, bottom, COLOR_OVERLAY);
+                float base = top + (3 + optionScreenIndex) * line;
+                cr.drawRectangle(left, base, right, base + line, COLOR_OVERLAY);
+                Shader.setBlendingEnabled(false);
+                        
+                Shader.setTextureEnabled(true);
+                left += line;
+                float secLeft = right - line * 11;
+                float y = top + line;
+                y = fr.drawString(left, y, "Options"); 
+                y += line;
+                fr.drawString(left, y, "Sound"); 
+                y = fr.drawString(secLeft, y, sound ? "yes" : "no"); 
+                fr.drawString(left, y, "K: Up");
+                y = fr.drawString(secLeft, y, getKeyName(Keys.UP)); 
+                fr.drawString(left, y, "K: Down");
+                y = fr.drawString(secLeft, y, getKeyName(Keys.DOWN)); 
+                fr.drawString(left, y, "K: Left");
+                y = fr.drawString(secLeft, y, getKeyName(Keys.LEFT)); 
+                fr.drawString(left, y, "K: Right");
+                y = fr.drawString(secLeft, y, getKeyName(Keys.RIGHT)); 
+                fr.drawString(left, y, "K: Jump");
+                y = fr.drawString(secLeft, y, getKeyName(Keys.JUMP)); 
+                fr.drawString(left, y, "K: Run");
+                y = fr.drawString(secLeft, y, getKeyName(Keys.RUN)); 
+                fr.drawString(left, y, "K: Back");
+                y = fr.drawString(secLeft, y, getKeyName(Keys.ESCAPE)); 
+                fr.drawString(left, y, "K: Enter");
+                y = fr.drawString(secLeft, y, getKeyName(Keys.ENTER)); 
+                fr.drawString(left, y, "K: Combat");
+                y = fr.drawString(secLeft, y, getKeyName(Keys.COMBAT)); 
+                fr.drawString(left, y, "K: Switch Face");
+                y = fr.drawString(secLeft, y, getKeyName(Keys.COMBAT_SWITCH_FACE)); 
+                fr.drawString(left, y, "K: Dash/Dodge");
+                y = fr.drawString(secLeft, y, getKeyName(Keys.COMBAT_DASH)); 
+                fr.drawString(left, y, "K: Dash/Dodge");
+                y = fr.drawString(secLeft, y, getKeyName(Keys.COMBAT_DODGE)); 
+                fr.drawString(left, y, "K: Block");
+                y = fr.drawString(secLeft, y, getKeyName(Keys.COMBAT_BLOCK)); 
+                fr.drawString(left, y, "K: Attack");
+                y = fr.drawString(secLeft, y, getKeyName(Keys.COMBAT_ATTACK)); 
+                if(optionsDirty)
+                {
+                    y = fr.drawString(left, y, true, "&cSave");
+                }
+                else
+                {
+                    y = fr.drawString(left, y, true, "Save");
+                }
+                fr.drawString(left, y, true, "Back");
+                break;
+            }
+            case 3:
+            {
+                ColorRenderer cr = Shader.getColorRenderer();
+                FontRenderer fr = Shader.getFontRenderer();
+                
+                float width = Shader.getViewWidth();
+                float height = Shader.getViewHeight();
+                
+                float line = fr.getHeight();
+                
+                int maxView = 7; // only for odd numbers
+                int half = maxView / 2;
+                
+                float left = width * 0.2f;
+                float right = width * 0.8f;
+                float top = (height - line * (maxView + 4)) * 0.5f;
+                float bottom = top + line * (maxView + 4);
+                
+                Shader.setTextureEnabled(false);
+                Shader.setColorEnabled(true);
+                
+                // brown background
+                cr.drawRectangle(0, 0, width, height, COLOR_BROWN); 
+                
+                Shader.setBlendingEnabled(true);
+                cr.drawRectangle(left, top, right, bottom, COLOR_OVERLAY);
+                int firstIndex;
+                int lastIndex;
+                int baseIndex;
+                if(levelIndex <= half) // first half
+                {
+                    firstIndex = 0;
+                    lastIndex = Math.min(levels.length, maxView);
+                    baseIndex = levelIndex;
+                }
+                else if(levelIndex >= levels.length - half) // last half
+                {
+                    lastIndex = levels.length;
+                    firstIndex = Math.max(lastIndex - maxView, 0);
+                    if(levels.length <= maxView)
+                    {
+                        baseIndex = levelIndex;
+                    }
+                    else
+                    {
+                        baseIndex = levelIndex - (levels.length - half - 1) + half;
+                    }
+                }
+                else // middle
+                {
+                    firstIndex = levelIndex - half;
+                    lastIndex = firstIndex + maxView;
+                    baseIndex = half;
+                }
+                float base = top + (3 + baseIndex) * line;
+                cr.drawRectangle(left, base, right, base + line, COLOR_OVERLAY);
+                Shader.setBlendingEnabled(false);
+                        
+                Shader.setTextureEnabled(true);
+                left += line;
+                float secLeft = right - line * 12;
+                float thirdLeft = right - line * 6;
+                float y = top + line;
+                y = fr.drawString(left, y, "Choose a Level ..."); 
+                y += line;
+                
+                if(firstIndex >= 1)
+                {
+                    y = fr.drawString(left, y, "..."); 
+                    firstIndex++;
+                }
+                SimpleConfig sc = saveSlots[slotScreenIndex];
+                for(int i = firstIndex; i < lastIndex - 1; i++)
+                {
+                    Level l = levels[i];
+                    fr.drawString(left, y, l.getName()); 
+                    fr.drawString(secLeft, y, l.formatBottles(sc.getInt("level." + l.getFileName() + ".bottles", 0))); 
+                    y = fr.drawString(thirdLeft, y, l.formatTime(sc.getDouble("level." + l.getFileName() + ".time", 0))); 
+                }
+                if(lastIndex == levels.length)
+                {
+                    Level l = levels[lastIndex - 1];
+                    fr.drawString(left, y, l.getName()); 
+                    fr.drawString(secLeft, y, l.formatBottles(sc.getInt("level." + l.getFileName() + ".bottles", 0))); 
+                    fr.drawString(thirdLeft, y, l.formatTime(sc.getDouble("level." + l.getFileName() + ".time", 0))); 
+                }
+                else
+                {
+                    fr.drawString(left, y, "..."); 
+                }
+                break;
+            }
+        }
+    }
+    
+    // -------------------------------------------------------------------------
+    // config
+    // -------------------------------------------------------------------------
+    
+    public boolean isSoundEnabled()
+    {
+        return sound;
+    }   
+    
+    // -------------------------------------------------------------------------
+    // 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/grass9"));
+        registeredTiles.put(22, new BaseBoxTile("grass/grass8"));
+        registeredTiles.put(23, new BaseBoxTile("grass/grass10"));
+        registeredTiles.put(28, new BaseBoxTile("grass/grass7"));
+        registeredTiles.put(29, new BaseBoxTile("grass/grass15"));
+        registeredTiles.put(30, new BaseBoxTile("grass/grass16"));
+        registeredTiles.put(31, new BaseBoxTile("grass/grass14"));
+        
+        // bottled soul
+        registeredTiles.put(32, new BottledSoulTile(1));
+        registeredTiles.put(33, new BottledSoulTile(2));
+        registeredTiles.put(34, new BottledSoulTile(3));
+        
+        // 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"));
+        
+        // fog, starting late to make length changes possible
+        for(int i = 0; i < 16; i++)
+        {
+            registeredTiles.put(16000 + i, new FogTile((i + 1) / 16.0));
+        }
+        
+        // london stuff
+        registeredTiles.put(224, new BaseBoxTile("london_background/london_background"));
+        
+        // london streets
+        for(int i = 0; i < 16; i++)
+        {
+            registeredTiles.put(240 + i, new BaseBoxTile("london_streets/london_streets" + i));
+        }
+        
+        // london street light
+        for(int i = 0; i < 25; i++)
+        {
+            registeredTiles.put(256 + i, new BaseBoxTile("street_light/street_light" + (i % 5) + "_" + (i / 5)));
+        }
+    }
+    
+    public Tile getTile(int id)
+    {
+        return registeredTiles.getOrDefault(id, FALLBACK_TILE);
+    }
+    
+    public void resetTiles()
+    {
+        registeredTiles.values().forEach(v -> v.reset());
+    }
+
+    private void menuMove(Runnable esc, Runnable enter, Runnable down, Runnable up, Runnable end)
+    {
+        if(Keys.ESCAPE.isReleased())
+        {
+            esc.run();
+            return;
+        }
+        if(Keys.ENTER.isReleased())
+        {
+            enter.run();
+            return;
+        }
+
+        if(Keys.DOWN.getTime() > 4)
+        {
+            Keys.DOWN.setTime(0);
+            down.run();
+        }
+        else if(Keys.UP.getTime() > 4)
+        {
+            Keys.UP.setTime(0);
+            up.run();
+        }
+        else if(Keys.DOWN.isReleased())
+        {
+            down.run();
+        }
+        else if(Keys.UP.isReleased())
+        {
+            up.run();
+        }
+        end.run();
+    }
+}

+ 88 - 0
src/me/hammerle/supersnuvi/Keys.java

@@ -0,0 +1,88 @@
+package me.hammerle.supersnuvi;
+
+import me.hammerle.snuviengine.api.KeyBinding;
+import me.hammerle.snuviengine.api.KeyHandler;
+import me.hammerle.supersnuvi.savegame.SimpleConfig;
+import static org.lwjgl.glfw.GLFW.*;
+
+public class Keys
+{
+    public final static KeyBinding UP = KeyHandler.register(GLFW_KEY_UP);
+    public final static KeyBinding DOWN = KeyHandler.register(GLFW_KEY_DOWN);
+    public final static KeyBinding LEFT = KeyHandler.register(GLFW_KEY_LEFT);
+    public final static KeyBinding RIGHT = KeyHandler.register(GLFW_KEY_RIGHT);
+    
+    public final static KeyBinding JUMP = KeyHandler.register(GLFW_KEY_SPACE);
+    public final static KeyBinding RUN = KeyHandler.register(GLFW_KEY_LEFT_SHIFT);
+    public final static KeyBinding ESCAPE = KeyHandler.register(GLFW_KEY_ESCAPE);
+    public final static KeyBinding ENTER = KeyHandler.register(GLFW_KEY_ENTER);
+    
+    public final static KeyBinding COMBAT = KeyHandler.register(GLFW_KEY_A);
+    public final static KeyBinding COMBAT_SWITCH_FACE = KeyHandler.register(GLFW_KEY_S);
+    public final static KeyBinding COMBAT_DASH = KeyHandler.register(GLFW_KEY_D);
+    public final static KeyBinding COMBAT_DODGE = KeyHandler.register(GLFW_KEY_F);
+    public final static KeyBinding COMBAT_BLOCK = KeyHandler.register(GLFW_KEY_G);
+    public final static KeyBinding COMBAT_ATTACK = KeyHandler.register(GLFW_KEY_H);
+    
+    private final static KeyBinding[] KEYS = 
+    {
+        UP, DOWN, LEFT, RIGHT, 
+        JUMP, RUN, ESCAPE, ENTER, 
+        COMBAT, COMBAT_SWITCH_FACE, COMBAT_DASH, COMBAT_DODGE, COMBAT_BLOCK, COMBAT_ATTACK
+    };
+    
+    public static void rebind(KeyBinding binding)
+    {
+        KeyHandler.rebind(binding);
+    }
+    
+    public static int getAmount()
+    {
+        return KEYS.length;
+    }
+    
+    public static KeyBinding get(int index)
+    {
+        return KEYS[index];
+    }
+    
+    public static void read(SimpleConfig config)
+    {
+        KeyHandler.rebind(UP, config.getInt("key.up", GLFW_KEY_UP));
+        KeyHandler.rebind(DOWN, config.getInt("key.down", GLFW_KEY_DOWN));
+        KeyHandler.rebind(LEFT, config.getInt("key.left", GLFW_KEY_LEFT));
+        KeyHandler.rebind(RIGHT, config.getInt("key.right", GLFW_KEY_RIGHT));
+        
+        KeyHandler.rebind(JUMP, config.getInt("key.jump", GLFW_KEY_SPACE));
+        KeyHandler.rebind(RUN, config.getInt("key.run", GLFW_KEY_LEFT_SHIFT));
+        KeyHandler.rebind(ESCAPE, config.getInt("key.escape", GLFW_KEY_ESCAPE));
+        KeyHandler.rebind(ENTER, config.getInt("key.enter", GLFW_KEY_ENTER));
+        
+        KeyHandler.rebind(COMBAT, config.getInt("key.combat", GLFW_KEY_A));
+        KeyHandler.rebind(COMBAT_SWITCH_FACE, config.getInt("key.combat.switchface", GLFW_KEY_S));
+        KeyHandler.rebind(COMBAT_DASH, config.getInt("key.combat.dash", GLFW_KEY_D));
+        KeyHandler.rebind(COMBAT_DODGE, config.getInt("key.combat.dodge", GLFW_KEY_F));
+        KeyHandler.rebind(COMBAT_BLOCK, config.getInt("key.combat.block", GLFW_KEY_G));
+        KeyHandler.rebind(COMBAT_ATTACK, config.getInt("key.combat.attack", GLFW_KEY_H));
+    }
+    
+    public static void write(SimpleConfig config)
+    {
+        config.set("key.up", UP.getKey());
+        config.set("key.down", DOWN.getKey());
+        config.set("key.left", LEFT.getKey());
+        config.set("key.right", RIGHT.getKey());
+        
+        config.set("key.jump", JUMP.getKey());
+        config.set("key.run", RUN.getKey());
+        config.set("key.escape", ESCAPE.getKey());
+        config.set("key.enter", ENTER.getKey());
+
+        config.set("key.combat", COMBAT.getKey());
+        config.set("key.combat.switchface", COMBAT_SWITCH_FACE.getKey());
+        config.set("key.combat.dash", COMBAT_DASH.getKey());
+        config.set("key.combat.dodge", COMBAT_DODGE.getKey());
+        config.set("key.combat.block", COMBAT_BLOCK.getKey());
+        config.set("key.combat.attack", COMBAT_ATTACK.getKey());
+    }
+}

+ 10 - 0
src/me/hammerle/supersnuvi/Main.java

@@ -0,0 +1,10 @@
+package me.hammerle.supersnuvi;
+
+public class Main
+{
+    public static void main(String[] args)
+    {
+        Game game = new Game();
+        game.run();
+    }
+}

+ 2 - 2
src/me/hammerle/supersnuvi/entity/Entity.java

@@ -9,7 +9,7 @@ import me.hammerle.supersnuvi.entity.components.ItemCollector;
 import me.hammerle.supersnuvi.entity.components.Movement;
 import me.hammerle.supersnuvi.entity.components.MovementPenalty;
 import me.hammerle.supersnuvi.gamelogic.Level;
-import me.hammerle.supersnuvi.rendering.Game;
+import me.hammerle.supersnuvi.rendering.OldGame;
 import me.hammerle.supersnuvi.tiles.Location;
 import me.hammerle.supersnuvi.util.CollisionBox;
 import me.hammerle.supersnuvi.util.Face;
@@ -22,7 +22,7 @@ public final class Entity
     // ----- * --- * ------ = -------
     //   m      s     Tick     Tick
     // added correction factor, real life gravity seems to strong
-    public static double GRAVITY = Game.TILE_SIZE * 9.81 / 60 * 0.1;
+    public static double GRAVITY = OldGame.TILE_SIZE * 9.81 / 60 * 0.1;
     
     private double posX;
     private double posY;

+ 6 - 6
src/me/hammerle/supersnuvi/entity/EntityBuilder.java

@@ -19,7 +19,7 @@ import me.hammerle.supersnuvi.entity.components.animator.HeroAnimator;
 import me.hammerle.supersnuvi.entity.components.animator.EntityAnimator;
 import me.hammerle.supersnuvi.entity.components.animator.LondonerAnimator;
 import me.hammerle.supersnuvi.gamelogic.Level;
-import me.hammerle.supersnuvi.rendering.Game;
+import me.hammerle.supersnuvi.rendering.OldGame;
 
 public final class EntityBuilder 
 {
@@ -30,8 +30,8 @@ public final class EntityBuilder
     
     public static Entity buildHero(Level level, double x, double y)
     {
-        double w = Game.TILE_SIZE;
-        double h = Game.TILE_SIZE * 2;
+        double w = OldGame.TILE_SIZE;
+        double h = OldGame.TILE_SIZE * 2;
         
         Entity hero = new Entity(level, x, y, w, h, w * 0.90625, h * 0.921875);
         hero.animator = new HeroAnimator(hero);
@@ -53,8 +53,8 @@ public final class EntityBuilder
     
     public static Entity buildLondoner(Level level, double x, double y, boolean evil)
     {
-        double w = Game.TILE_SIZE;
-        double h = Game.TILE_SIZE * 2;
+        double w = OldGame.TILE_SIZE;
+        double h = OldGame.TILE_SIZE * 2;
 
         Entity hero = new Entity(level, x, y, w, h, w * 0.4375, h * 0.703125);
         hero.animator = new LondonerAnimator(hero);
@@ -70,7 +70,7 @@ public final class EntityBuilder
     
     public static Entity buildCrumblingStone(Level level, double x, double y)
     {
-        Entity stone = new Entity(level, x, y, Game.TILE_SIZE, Game.TILE_SIZE * 10);
+        Entity stone = new Entity(level, x, y, OldGame.TILE_SIZE, OldGame.TILE_SIZE * 10);
         stone.animator = new StoneAnimator(stone);
         stone.move = new StoneMovement(stone);
         stone.health = new DefaultDespawn(stone);

+ 12 - 12
src/me/hammerle/supersnuvi/entity/components/ai/HumanController.java

@@ -1,8 +1,8 @@
 package me.hammerle.supersnuvi.entity.components.ai;
 
+import me.hammerle.supersnuvi.Keys;
 import me.hammerle.supersnuvi.entity.Entity;
-import me.hammerle.supersnuvi.javafx.KeyHandler;
-import me.hammerle.supersnuvi.rendering.Game;
+import me.hammerle.supersnuvi.rendering.OldGame;
 import me.hammerle.supersnuvi.util.SoundUtils;
 
 public class HumanController extends EntityController
@@ -22,7 +22,7 @@ public class HumanController extends EntityController
     @Override
     public boolean isRunning() 
     {
-        return KeyHandler.RUN.isDown();
+        return Keys.RUN.isDown();
     }
 
     @Override
@@ -70,7 +70,7 @@ public class HumanController extends EntityController
             return;
         }
             
-        if(KeyHandler.COMBAT.wasJustReleased())
+        if(Keys.COMBAT.isReleased())
         {
             if(combatMode)
             {
@@ -112,7 +112,7 @@ public class HumanController extends EntityController
                 attackTimer = -1;
                 
                 double sign = ent.getAnimator().drawImageFlipped() ? -1 : 1;
-                double extend = sign * Game.TILE_SIZE;
+                double extend = sign * OldGame.TILE_SIZE;
                 boolean once = true;
                 for(Entity e : ent.getLevel().getEntitiesCollidingWith(ent, ent.getBox().expand(extend, 0)))
                 {
@@ -161,23 +161,23 @@ public class HumanController extends EntityController
          
         if(combatMode && combatTimer == 0)
         {
-            if(KeyHandler.COMBAT_BLOCK.isDown() && blockTimer == -1)
+            if(Keys.COMBAT_BLOCK.isDown() && blockTimer == -1)
             {
                 ent.getAnimator().resetFrames();
                 blockTimer = 0;
             }
-            else if(KeyHandler.COMBAT_ATTACK.wasJustReleased() && attackTimer == -1)
+            else if(Keys.COMBAT_ATTACK.isReleased()&& attackTimer == -1)
             {
                 SoundUtils.playSound(SoundUtils.Sound.SWORD_SLASH);
                 attackTimer = 0;
             }
-            else if(KeyHandler.COMBAT_DASH.wasJustReleased() && dashTimer == -1 && ent.getEnergy().getEnergyPercent() >= 0.3)
+            else if(Keys.COMBAT_DASH.isReleased() && dashTimer == -1 && ent.getEnergy().getEnergyPercent() >= 0.3)
             {
                 SoundUtils.playSound(SoundUtils.Sound.DASH);
                 dashTimer = 0;
                 ent.getEnergy().addEnergyPercent(-0.3);
             }
-            else if(KeyHandler.COMBAT_DODGE.wasJustReleased() && dodgeTimer == -1 && ent.getEnergy().getEnergyPercent() >= 0.1)
+            else if(Keys.COMBAT_DODGE.isReleased() && dodgeTimer == -1 && ent.getEnergy().getEnergyPercent() >= 0.1)
             {
                 SoundUtils.playSound(SoundUtils.Sound.DODGE);
                 dodgeTimer = 0;
@@ -188,7 +188,7 @@ public class HumanController extends EntityController
         
         if(!isBlocking() && dodgeTimer == -1 && dashTimer == -1)
         {
-            if(KeyHandler.JUMP.isDown() && ent.getEnergy().getEnergyPercent() > 0.2 && ent.getMovement().jump())
+            if(Keys.JUMP.isDown() && ent.getEnergy().getEnergyPercent() > 0.2 && ent.getMovement().jump())
             {
                 ent.getEnergy().addEnergyPercent(-0.10);
                 SoundUtils.playSound(SoundUtils.Sound.JUMP);
@@ -217,11 +217,11 @@ public class HumanController extends EntityController
 
             if(dodgeTimer == -1 && dashTimer == -1)
             {
-                if(KeyHandler.LEFT.isDown())
+                if(Keys.LEFT.isDown())
                 {
                     ent.setMotionX(-speed * ent.getMovementPenalty().getFactor());
                 }
-                else if(KeyHandler.RIGHT.isDown())
+                else if(Keys.RIGHT.isDown())
                 {
                     ent.setMotionX(speed * ent.getMovementPenalty().getFactor());
                 }

+ 3 - 3
src/me/hammerle/supersnuvi/entity/components/ai/LondonerController.java

@@ -1,7 +1,7 @@
 package me.hammerle.supersnuvi.entity.components.ai;
 
 import me.hammerle.supersnuvi.entity.Entity;
-import me.hammerle.supersnuvi.rendering.Game;
+import me.hammerle.supersnuvi.rendering.OldGame;
 import me.hammerle.supersnuvi.tiles.Location;
 import me.hammerle.supersnuvi.util.Face;
 import me.hammerle.supersnuvi.util.SoundUtils;
@@ -99,7 +99,7 @@ public class LondonerController extends EntityController
             {
                 attackTimer = -1;
                 double sign = ent.getAnimator().drawImageFlipped() ? -1 : 1;
-                double extend = sign * Game.TILE_SIZE;
+                double extend = sign * OldGame.TILE_SIZE;
                 boolean once = true;
                 for(Entity e : ent.getLevel().getEntitiesCollidingWith(ent, ent.getBox().expand(extend, 0)))
                 {
@@ -121,7 +121,7 @@ public class LondonerController extends EntityController
         {
             Entity hero = ent.getLevel().getHero();
             double distance = hero.signedDistanceX(ent);
-            if(Math.abs(distance) < (Game.TILE_SIZE >> 1))
+            if(Math.abs(distance) < (OldGame.TILE_SIZE >> 1))
             {
                 if(attackCooldown > 0)
                 {

+ 5 - 5
src/me/hammerle/supersnuvi/entity/components/animator/HeroAnimator.java

@@ -1,10 +1,10 @@
 package me.hammerle.supersnuvi.entity.components.animator;
 
+import me.hammerle.supersnuvi.Keys;
 import me.hammerle.supersnuvi.entity.Entity;
 import me.hammerle.supersnuvi.entity.components.Health;
 import me.hammerle.supersnuvi.javafx.Image;
-import me.hammerle.supersnuvi.javafx.KeyHandler;
-import me.hammerle.supersnuvi.rendering.Game;
+import me.hammerle.supersnuvi.rendering.OldGame;
 import me.hammerle.supersnuvi.rendering.IRenderer;
 import me.hammerle.supersnuvi.util.Utils;
 
@@ -266,8 +266,8 @@ public class HeroAnimator extends EntityAnimator
     @Override
     public void render(IRenderer renderer) 
     {
-        double x = ent.getX() - 2 * Game.SCALE;
-        double y = ent.getY() + ent.getHeight() - 2 * Game.SCALE;
+        double x = ent.getX() - 2 * OldGame.SCALE;
+        double y = ent.getY() + ent.getHeight() - 2 * OldGame.SCALE;
         
         if(ent.getHealth().isDead())
         {
@@ -477,7 +477,7 @@ public class HeroAnimator extends EntityAnimator
         
         if(ent.getController().isInCombatMode())
         {
-            if(KeyHandler.COMBAT_SWITCH_FACE.wasJustReleased())
+            if(Keys.COMBAT_SWITCH_FACE.isReleased())
             {
                 flipped = !flipped;
             }

+ 3 - 3
src/me/hammerle/supersnuvi/entity/components/animator/LondonerAnimator.java

@@ -3,7 +3,7 @@ package me.hammerle.supersnuvi.entity.components.animator;
 import me.hammerle.supersnuvi.entity.Entity;
 import me.hammerle.supersnuvi.entity.components.Health;
 import me.hammerle.supersnuvi.javafx.Image;
-import me.hammerle.supersnuvi.rendering.Game;
+import me.hammerle.supersnuvi.rendering.OldGame;
 import me.hammerle.supersnuvi.rendering.IRenderer;
 import me.hammerle.supersnuvi.util.Utils;
 
@@ -112,8 +112,8 @@ public class LondonerAnimator extends EntityAnimator
     public void render(IRenderer renderer) 
     {
         boolean flip = drawImageFlipped();
-        double x = ent.getX() - 9 * Game.SCALE;
-        double y = ent.getY() + ent.getHeight() - 2 * Game.SCALE;
+        double x = ent.getX() - 9 * OldGame.SCALE;
+        double y = ent.getY() + ent.getHeight() - 2 * OldGame.SCALE;
         double w = ent.getWidth() * 2;
         double h = ent.getHeight();
         

+ 87 - 35
src/me/hammerle/supersnuvi/gamelogic/Level.java

@@ -6,26 +6,23 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.TreeSet;
 import java.util.stream.Collectors;
+import me.hammerle.snuviengine.api.Shader;
+import me.hammerle.supersnuvi.Game;
 import me.hammerle.supersnuvi.entity.Entity;
 import me.hammerle.supersnuvi.entity.EntityBuilder;
 import me.hammerle.supersnuvi.javafx.Image;
-import me.hammerle.supersnuvi.rendering.Game;
-import me.hammerle.supersnuvi.tiles.BottledSoulTile;
 import me.hammerle.supersnuvi.tiles.Location;
 import me.hammerle.supersnuvi.tiles.StartTile;
 import me.hammerle.supersnuvi.tiles.Tile;
 import me.hammerle.supersnuvi.util.CollisionBox;
-import me.hammerle.supersnuvi.rendering.IRenderer;
 import me.hammerle.supersnuvi.util.Utils;
 
 public final class Level 
 {
-    private final StateRenderer state;
-    private final IRenderer renderer;
-    
     private final boolean worldLoaded;
     private final LevelData data;
     
+    private final String fileName;
     private final String name;
     
     private final HashMap<Integer, Entity> entities = new HashMap<>();;
@@ -46,10 +43,8 @@ public final class Level
     
     private TreeSet<Point> spawns = new TreeSet<>();
     
-    public Level(StateRenderer state, File f)
+    public Level(File f)
     {
-        this.state = state;
-        this.renderer = state.getRenderer();
         this.data = new LevelData(f);
         this.worldLoaded = data.load();
         
@@ -107,7 +102,7 @@ public final class Level
             maxSouls = 0;
             data.forEachInteractTile((x, y, tile) -> 
             {
-                Tile t = state.getTile(tile);
+                Tile t = Game.get().getTile(tile);
                 if(t != null)
                 {
                     maxSouls += t.getBottleScore();
@@ -128,9 +123,10 @@ public final class Level
         }
         else
         {
-            this.name = "error";
+            this.name = f.getName();
             maxSouls = 0;
         }
+        fileName = f.getName();
         
         // there must be at least one spawn
         if(spawns.isEmpty())
@@ -145,6 +141,11 @@ public final class Level
     // basic stuff
     // -------------------------------------------------------------------------
 
+    public String getFileName() 
+    {
+        return fileName;
+    }
+    
     public String getName() 
     {
         return name;
@@ -183,7 +184,7 @@ public final class Level
                 dead = true;
             }
         }*/
-        state.resetTiles();
+        Game.get().resetTiles();
         data.activateEntities();
         souls = 0;
         time = 0.0;
@@ -251,16 +252,16 @@ public final class Level
     // tick
     // -------------------------------------------------------------------------
     
-    public void update()
+    public void tick()
     {
         if(worldLoaded)
         {
             time += 0.0125;
             
-            state.tickTiles();
+            Game.get().tickTiles();
             
             // entity spawn layer
-            int startX = renderer.getFirstVisibleBlockX();
+            /*int startX = renderer.getFirstVisibleBlockX();
             int startY = renderer.getFirstVisibleBlockY();
             int endX = Math.min(renderer.getLastVisibleBlockX() + 1, data.getWidth());
             int endY = Math.min(renderer.getLastVisibleBlockY() + 1, data.getHeight());
@@ -276,7 +277,7 @@ public final class Level
                         entities.put(entityCounter++, ent);
                     }
                 }
-            }, startX, endX, startY, endY);
+            }, startX, endX, startY, endY);*/
             
             // doing entity logic first
             entities.values().removeIf(entity -> 
@@ -293,7 +294,7 @@ public final class Level
         }
     }   
     
-    public char[] formatBottles(int bottles)
+    public String formatBottles(int bottles)
     {
         char[] c = new char[5];
         if(bottles <= 9)
@@ -327,20 +328,20 @@ public final class Level
             c[3] = (char) ((maxSouls / 10) + '0');
             c[4] = (char) ((maxSouls % 10) + '0');
         }
-        return c;
+        return new String(c);
     }
     
-    public char[] formatTime(double time)
+    public String formatTime(double time)
     {
         if(time == -1.0)
         {
-            return new char[] {'-', '-', '-', '-', '-'};
+            return "-----";
         }
         else if(time >= 999.9)
         {
-            return new char[] {'9', '9', '9', '.', '9'};
+            return "999.9";
         }
-        return String.format("%05.1f", time).toCharArray();
+        return String.format("%05.1f", time);
     }
     
     
@@ -390,25 +391,76 @@ public final class Level
         Utils.getImage("gui/time_icon/time_f12", true)
     };
     
-    public void render()
+    private float getViewX(float x) 
+    {
+        x -= Shader.getViewWidth() >> 1;
+        if(x < 0)
+        {
+            return 0;
+        }
+        float max = data.getWidth() * Tile.SIZE - Shader.getViewWidth();
+        if(x > max)
+        {
+            return max;
+        }
+        return x;
+    }
+    
+    private float getViewY(float y) 
+    {
+        y -= Shader.getViewHeight() >> 1;
+        if(y < 0)
+        {
+            return 0;
+        }
+        float max = data.getHeight() * Tile.SIZE - Shader.getViewHeight();
+        if(y > max)
+        {
+            return max;
+        }
+        return y;
+    }
+    
+    public void render(float lag)
     {
         if(worldLoaded)
         {
             // setting the right view center
-            double rWidth = renderer.getWidth();
-            double rHeight = renderer.getHeight();
+            double rWidth = Shader.getViewWidth();
+            double rHeight = Shader.getViewHeight();
+            
+            float centerX = (float) (hero.getX() - hero.getWidth() / 2);
+            float centerY = (float) (hero.getY() - hero.getHeight() / 2);
             
-            double centerX = Math.min(Math.max(0, hero.getX() - rWidth / 2), data.getWidth() * Game.TILE_SIZE - rWidth);
-            double centerY = Math.min(Math.max(0, hero.getY() - rHeight / 2), data.getHeight() * Game.TILE_SIZE - rHeight);
+            float cameraX = getViewX(centerX);
+            float cameraY = getViewY(centerY);
             
-            renderer.setViewCenter(centerX, centerY);
+            Shader.translateTo(-cameraX, -cameraY);
+            Shader.updateMatrix();
+            
+            Shader.setColorEnabled(true);
+            Shader.setTextureEnabled(false);
+            Shader.getColorRenderer().drawRectangle(centerX - 5, centerY - 5, centerX + 5, centerY + 5, 0xFFFF0000);
+            
+            /*data.forEachInteractTile((x, y, tile) -> 
+            {
+                if(tile <= 0)
+                {
+                    return;
+                }
+                x *= Tile.SIZE;
+                y *= Tile.SIZE;
+                Shader.getColorRenderer().drawRectangle(x, y, x + Tile.SIZE, y  + Tile.SIZE, 0xFFFFFF00);
+            });*/
+            
+            /*renderer.setViewCenter(centerX, centerY);
             
             int startX = renderer.getFirstVisibleBlockX();
             int startY = renderer.getFirstVisibleBlockY();
             int endX = Math.min(renderer.getLastVisibleBlockX() + 1, data.getWidth());
-            int endY = Math.min(renderer.getLastVisibleBlockY() + 1, data.getHeight());
+            int endY = Math.min(renderer.getLastVisibleBlockY() + 1, data.getHeight());*/
             
-            data.forEachBackground((x, y, tile) ->
+            /*data.forEachBackground((x, y, tile) ->
             {
                 if(tile != -1)
                 {
@@ -505,7 +557,7 @@ public final class Level
                 renderer.drawFixedImagePart(TIME_CLOCK[(int) clockTick], 0, 0, 32, 32, x, y, w, h);
                 renderer.drawText(x + renderer.getTextWidth(1), y, formatTime(time));
             }
-            renderer.stopTextDrawing();
+            renderer.stopTextDrawing();*/
         }
     }
     
@@ -518,9 +570,9 @@ public final class Level
         int i = data.getInteractionTile(x, y);
         if(i == -1)
         {
-            return StateRenderer.FALLBACK_TILE;
+            return Game.FALLBACK_TILE;
         }
-        return state.getInteractionTile(i);
+        return Game.get().getTile(i);
     }
     
     public CollisionBox getTileMovementBox(int x, int y)
@@ -530,7 +582,7 @@ public final class Level
         {
             return CollisionBox.NULL_BOX;
         }
-        Tile tile = state.getInteractionTile(i);
+        Tile tile = Game.get().getTile(i);
         return tile.getMovementBox(x, y).reset().offset(Utils.toCoord(x), Utils.toCoord(y));
     }
     
@@ -562,7 +614,7 @@ public final class Level
         {
             return CollisionBox.NULL_BOX;
         }
-        Tile tile = state.getInteractionTile(i);
+        Tile tile = Game.get().getTile(i);
         return tile.getCollisionBox(x, y).reset().offset(Utils.toCoord(x), Utils.toCoord(y));
     }
     

+ 0 - 794
src/me/hammerle/supersnuvi/gamelogic/StateRenderer.java

@@ -1,794 +0,0 @@
-package me.hammerle.supersnuvi.gamelogic;
-
-import java.io.File;
-import java.util.Arrays;
-import java.util.HashMap;
-import me.hammerle.supersnuvi.javafx.IKeyBinding;
-import me.hammerle.supersnuvi.javafx.KeyHandler;
-import me.hammerle.supersnuvi.savegame.SimpleConfig;
-import me.hammerle.supersnuvi.tiles.*;
-import me.hammerle.supersnuvi.util.SoundUtils;
-import me.hammerle.supersnuvi.rendering.IRenderer;
-
-public class StateRenderer
-{
-    // constants
-    public static final ColoredBaseTile FALLBACK_TILE = new ColoredBaseTile(0, 0, 0, 0);
-    
-    // rendering
-    private final IRenderer renderer;   
-    
-    // tiles
-    private final HashMap<Integer, Tile> registeredTiles;
-    
-    // levels
-    private Level currentLevel;
-    private final Level[] levels;
-    private int levelIndex;
-    
-    public static final SimpleConfig CONFIG = new SimpleConfig("options.txt", true);
-    private static final SimpleConfig[] SAVE_SLOTS = new SimpleConfig[] 
-    {
-        new SimpleConfig("slot1.txt", true), new SimpleConfig("slot2.txt", true), new SimpleConfig("slot3.txt", true)
-    };
-    
-    private int screen = 0;
-    private int startScreenIndex = 0;
-    private int optionScreenIndex = 0;
-    private int slotScreenIndex = 0;
-    
-    private boolean optionsDirty = false;
-    
-    // sound
-    private static boolean sound = CONFIG.getBoolean("sound", false);
-    public static boolean isSoundEnabled()
-    {
-        return sound;
-    }   
-
-    public StateRenderer(IRenderer renderer)
-    {
-        this.renderer = renderer;
-        this.registeredTiles = new HashMap<>();
-        
-        registerTiles();
-        
-        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;
-    }    
-
-    // -------------------------------------------------------------------------
-    // 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/grass9"));
-        registeredTiles.put(22, new BaseBoxTile("grass/grass8"));
-        registeredTiles.put(23, new BaseBoxTile("grass/grass10"));
-        registeredTiles.put(28, new BaseBoxTile("grass/grass7"));
-        registeredTiles.put(29, new BaseBoxTile("grass/grass15"));
-        registeredTiles.put(30, new BaseBoxTile("grass/grass16"));
-        registeredTiles.put(31, new BaseBoxTile("grass/grass14"));
-        
-        // bottled soul
-        registeredTiles.put(32, new BottledSoulTile(1));
-        registeredTiles.put(33, new BottledSoulTile(2));
-        registeredTiles.put(34, new BottledSoulTile(3));
-        
-        // 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"));
-        
-        // fog, starting late to make length changes possible
-        for(int i = 0; i < 16; i++)
-        {
-            registeredTiles.put(16000 + i, new FogTile((i + 1) / 16.0));
-        }
-        
-        // london stuff
-        registeredTiles.put(224, new BaseBoxTile("london_background/london_background"));
-        
-        // london streets
-        for(int i = 0; i < 16; i++)
-        {
-            registeredTiles.put(240 + i, new BaseBoxTile("london_streets/london_streets" + i));
-        }
-        
-        // london street light
-        for(int i = 0; i < 25; i++)
-        {
-            registeredTiles.put(256 + i, new BaseBoxTile("street_light/street_light" + (i % 5) + "_" + (i / 5)));
-        }
-    }
-    
-    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 IRenderer getRenderer() 
-    {
-        return renderer;
-    }
-    
-    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[] 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);
-        }
-        return chars;
-    }
-    
-    private static char[] getLevelCharLine(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 - 7] = spacer;
-        c[c.length - 13] = spacer;
-        return c;
-    }
-    
-    private static char[] getMoreLevelCharLine(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[1] = '.';
-        c[2] = '.';
-        c[3] = '.';
-        c[c.length - 1] = end;
-        c[c.length - 7] = spacer;
-        c[c.length - 13] = spacer;
-        return c;
-    }
-    
-    private static final int MENU_WIDTH = 36;
-    private static final int MENU_MAX = 5;
-    
-    private static final char[] TABLE_TOP = getCharLine((char) 131, (char) 136, (char) 133, (char) 136);
-    private static final char[] TABLE_HEADING = getCharLine((char) 134, "Choose a Level ...", (char) 134);
-    private static final char[] TABLE_MID = getLevelCharLine((char) 130, (char) 136, (char) 132, (char) 129);
-    private static final char[] TABLE_BOTTOM = getLevelCharLine((char) 137, (char) 136, (char) 138, (char) 135);
-    private static final char[] TABLE_MORE = getMoreLevelCharLine((char) 134, ' ', (char) 134, (char) 134);
-    
-    private static final char[][] START_UP = new char[][] 
-    {
-        getCharLine((char) 131, (char) 136, (char) 133, (char) 136),
-        getCharLine((char) 134, "Super Snuvi", (char) 134),
-        getCharLine((char) 130, (char) 136, (char) 132, (char) 136),
-        getCharLine((char) 134, "Start Game", (char) 134),
-        getCharLine((char) 134, "Options", (char) 134),
-        getCharLine((char) 134, "Exit Game", (char) 134),
-        getCharLine((char) 137, (char) 136, (char) 138, (char) 136)
-    };
-    
-    private static final char[][] SLOTS = new char[][] 
-    {
-        getCharLine((char) 131, (char) 136, (char) 133, (char) 136),
-        getCharLine((char) 134, "Choose a Savegame ...", (char) 134),
-        getCharLine((char) 130, (char) 136, (char) 132, (char) 136),
-        getCharLine((char) 134, "Slot 1", (char) 134),
-        getCharLine((char) 134, "Slot 2", (char) 134),
-        getCharLine((char) 134, "Slot 3", (char) 134),
-        getCharLine((char) 134, "Back", (char) 134),
-        getCharLine((char) 137, (char) 136, (char) 138, (char) 136)
-    };
-    
-    private static final char[][] OPTIONS = new char[][] 
-    {
-        getCharLine((char) 131, (char) 136, (char) 133, (char) 136),
-        getCharLine((char) 134, "Options", (char) 134),
-        getCharLine((char) 130, (char) 136, (char) 132, (char) 136),
-        getCharLine((char) 134, "Sound", (char) 134),  
-        getCharLine((char) 134, "K: Up", (char) 134),
-        getCharLine((char) 134, "K: Down", (char) 134),
-        getCharLine((char) 134, "K: Left", (char) 134),
-        getCharLine((char) 134, "K: Rright", (char) 134),
-        getCharLine((char) 134, "K: Jump", (char) 134),
-        getCharLine((char) 134, "K: Run", (char) 134),
-        getCharLine((char) 134, "K: Back", (char) 134),
-        getCharLine((char) 134, "K: Enter", (char) 134),
-        getCharLine((char) 134, "K: Combat", (char) 134),
-        getCharLine((char) 134, "K: Switch Face", (char) 134),
-        getCharLine((char) 134, "K: Dash/Dodge", (char) 134),
-        getCharLine((char) 134, "K: Dash/Dodge", (char) 134),
-        getCharLine((char) 134, "K: Block", (char) 134),
-        getCharLine((char) 134, "K: Attack", (char) 134),
-        getCharLine((char) 134, "", (char) 134), // save
-        getCharLine((char) 134, "Back", (char) 134),
-        getCharLine((char) 137, (char) 136, (char) 138, (char) 136)
-    };
-    
-    private static final char[] SAVE_OVERLAY = getCharLine(' ', "Save", ' ');
-    
-    private static final int OPTION_OFFSET = 10;
-            
-    static
-    {
-        int pos = OPTIONS[0].length - OPTION_OFFSET;
-        OPTIONS[2][pos] = (char) 129;
-        for(int i = 0; i < 17; i++)
-        {
-            OPTIONS[3 + i][pos] = (char) 134;
-        }
-        OPTIONS[20][pos] = (char) 135;
-    }
-    
-    private void menuMove(Runnable esc, Runnable enter, Runnable down, Runnable up, Runnable end)
-    {
-        if(KeyHandler.ESCAPE.wasJustReleased())
-        {
-            esc.run();
-            return;
-        }
-        if(KeyHandler.ENTER.wasJustReleased())
-        {
-            enter.run();
-            return;
-        }
-
-        if(KeyHandler.DOWN.getDownTime() > 16)
-        {
-            KeyHandler.DOWN.resetTime();
-            down.run();
-        }
-        else if(KeyHandler.UP.getDownTime() > 16)
-        {
-            KeyHandler.UP.resetTime();
-            up.run();
-        }
-        else if(KeyHandler.DOWN.wasJustReleased())
-        {
-            down.run();
-        }
-        else if(KeyHandler.UP.wasJustReleased())
-        {
-            up.run();
-        }
-        end.run();
-    }
-    
-    public void update()
-    {
-        if(currentLevel != null)
-        {
-            SoundUtils.playSound(SoundUtils.Sound.SONG_1);
-            SoundUtils.stopSound(SoundUtils.Sound.MENU_MUSIC);
-            
-            currentLevel.update();
-            
-            // doing that here to prevent concurent modification
-            if(currentLevel.shouldFinish())
-            {
-                String base = "level." + currentLevel.getName();
-                SimpleConfig sp = SAVE_SLOTS[slotScreenIndex];
-                
-                // save success
-                sp.set(base, true);
-                
-                // update time, if a new highscore was scored
-                double time = sp.getDouble(base + ".time", Integer.MAX_VALUE);
-                if(currentLevel.getTime() < time)
-                {
-                    sp.set(base + ".time", currentLevel.getTime());
-                }
-                
-                // update bottles, if a new highscore was scored
-                int bottles = sp.getInt(base + ".bottles", 0);
-                if(currentLevel.getCurrentBottles() > bottles)
-                {
-                    sp.set(base + ".bottles", currentLevel.getCurrentBottles());
-                }
-                
-                // final save
-                sp.save();
-                
-                currentLevel.resetLevel();
-                currentLevel = null;
-                return;
-            }
-            
-            if(currentLevel.shouldReset())
-            {
-                if(currentLevel.resetLevel())
-                {
-                    currentLevel = null;
-                }
-            }
-            
-            if(KeyHandler.ESCAPE.wasJustReleased())
-            {
-                currentLevel = null;
-            }
-        }
-        else
-        {
-            SoundUtils.playSound(SoundUtils.Sound.MENU_MUSIC);
-            SoundUtils.stopSound(SoundUtils.Sound.SONG_1);
-            switch(screen)
-            {
-                case 0: // start screen
-                {
-                    menuMove(() -> 
-                    {
-                        // do nothing on escape in start screen
-                    }, () -> 
-                    {
-                        switch(startScreenIndex)
-                        { 
-                            case 0:
-                                screen = 1;
-                                break;
-                            case 1:
-                                screen = 2;
-                                break;
-                            case 2:
-                                System.exit(0);
-                                break;
-                        }
-                    }, () -> startScreenIndex++, () -> startScreenIndex--, () -> 
-                    {
-                        if(startScreenIndex < 0)
-                        {
-                            startScreenIndex = 0;
-                        }
-                        else if(startScreenIndex >= 3)
-                        {
-                            startScreenIndex = 2;
-                        }
-                    });
-                    break;
-                }
-                case 1: // slot screen
-                {
-                    menuMove(() -> 
-                    {
-                        screen = 0;
-                    }, () -> 
-                    {
-                        if(slotScreenIndex == 3)
-                        {
-                            screen = 0;
-                            return;
-                        }
-                        screen = 3;
-                    }, () -> slotScreenIndex++, () -> slotScreenIndex--, () -> 
-                    {
-                        if(slotScreenIndex < 0)
-                        {
-                            slotScreenIndex = 0;
-                        }
-                        else if(slotScreenIndex >= 4)
-                        {
-                            slotScreenIndex = 3;
-                        }
-                    });
-                    break;
-                }
-                case 2: // option screen
-                {
-                    menuMove(() -> 
-                    {
-                        screen = 0;
-                    }, () -> 
-                    {
-                        switch(optionScreenIndex)
-                        {
-                            case 0: // toggle sound
-                                sound = !sound;
-                                if(!sound)
-                                {
-                                    SoundUtils.turnSoundOff();
-                                }
-                                optionsDirty = true;
-                                break;
-                            case 15: // save options
-                                for(IKeyBinding binding : KeyHandler.ARRAY)
-                                {
-                                    CONFIG.set(binding.getName(), binding.getKeyName());
-                                }
-                                CONFIG.set("sound", sound);
-                                CONFIG.save();
-                                optionsDirty = false;
-                                break;
-                            case 16: // go back
-                                screen = 0;
-                                break;
-                            default: // rebind keys
-                                KeyHandler.rebindKey(KeyHandler.ARRAY[optionScreenIndex - 1]);
-                                optionsDirty = true;
-                                break;
-                        }
-                    }, () -> optionScreenIndex++, () -> optionScreenIndex--, () -> 
-                    {
-                        if(optionScreenIndex < 0)
-                        {
-                            optionScreenIndex = 0;
-                        }
-                        else if(optionScreenIndex >= OPTIONS.length - 4)
-                        {
-                            optionScreenIndex = OPTIONS.length - 5;
-                        }
-                    });
-                    break;
-                }
-                case 3: // level choose screen
-                {                   
-                    menuMove(() -> 
-                    {
-                        screen = 1;
-                    }, () -> currentLevel = levels[levelIndex], () -> levelIndex++, () -> levelIndex--, () -> 
-                    {
-                        if(levelIndex < 0)
-                        {
-                            levelIndex = 0;
-                        }
-                        else if(levelIndex >= levels.length)
-                        {
-                            levelIndex = levels.length - 1;
-                        }
-                    });
-                    break;
-                }
-            }
-        }
-    }
-    
-    public void render()
-    {
-        if(currentLevel != null)
-        {
-            currentLevel.render();
-            return;
-        }
-        switch(screen)
-        {
-            case 0:
-            {
-                renderer.prepareTextDrawing(255, 255, 255, 1.0, MENU_WIDTH * 5 / 4);
-                double x = (renderer.getWidth() - renderer.getTextWidth(MENU_WIDTH)) * 0.5;
-                double y = (renderer.getHeight() - renderer.getTextHeight(7)) * 0.5;
-                double line = renderer.getTextHeight(1);
-                
-                renderer.save();
-                renderer.setFillColor(128, 128, 128, 1.0);
-                renderer.fillRectangle(
-                        x + renderer.getTextWidth(1),
-                        y + renderer.getTextHeight(startScreenIndex + 3) - 1, 
-                        renderer.getTextWidth(MENU_WIDTH - 2), 
-                        line);
-                renderer.restore();
-                
-                for(char[] c : START_UP)
-                {
-                    renderer.drawText(x, y, c);
-                    y += line;
-                }
-                break;
-            }
-            case 1:
-            {
-                renderer.prepareTextDrawing(255, 255, 255, 1.0, MENU_WIDTH * 5 / 4);
-                double x = (renderer.getWidth() - renderer.getTextWidth(MENU_WIDTH)) * 0.5;
-                double y = (renderer.getHeight() - renderer.getTextHeight(7)) * 0.5;
-                double line = renderer.getTextHeight(1);
-                
-                renderer.save();
-                renderer.setFillColor(128, 128, 128, 1.0);
-                renderer.fillRectangle(
-                        x + renderer.getTextWidth(1),
-                        y + renderer.getTextHeight(slotScreenIndex + 3) - 1, 
-                        renderer.getTextWidth(MENU_WIDTH - 2), 
-                        line);
-                renderer.restore();
-                
-                for(char[] c : SLOTS)
-                {
-                    renderer.drawText(x, y, c);
-                    y += line;
-                }
-                break;
-            }
-            case 2:
-            {
-                renderer.prepareTextDrawing(255, 255, 255, 1.0, MENU_WIDTH * 5 / 4);
-                double x = (renderer.getWidth() - renderer.getTextWidth(MENU_WIDTH)) * 0.5;
-                double y = (renderer.getHeight() - renderer.getTextHeight(OPTIONS.length)) * 0.5;
-                double line = renderer.getTextHeight(1);
-                
-                renderer.save();
-                renderer.setFillColor(128, 128, 128, 1.0);
-                renderer.fillRectangle(
-                        x + renderer.getTextWidth(1),
-                        y + renderer.getTextHeight(optionScreenIndex + 3) - 1, 
-                        renderer.getTextWidth(MENU_WIDTH - (OPTION_OFFSET + 1)), 
-                        line);               
-                renderer.fillRectangle(
-                        x + renderer.getTextWidth(MENU_WIDTH - (OPTION_OFFSET - 1)),
-                        y + renderer.getTextHeight(optionScreenIndex + 3) - 1, 
-                        renderer.getTextWidth(OPTION_OFFSET - 2), 
-                        line);
-                renderer.restore();
-                
-                // option top
-                for(int i = 0; i < 3; i++)
-                {
-                    renderer.drawText(x, y, OPTIONS[i]);
-                    y += line;
-                }
-                
-                // sound option
-                if(sound)
-                {
-                    int pos = OPTIONS[3].length - OPTION_OFFSET + 1;
-                    OPTIONS[3][pos] = 'y';
-                    OPTIONS[3][pos + 1] = 'e';
-                    OPTIONS[3][pos + 2] = 's';
-                }
-                else
-                {
-                    int pos = OPTIONS[3].length - OPTION_OFFSET + 1;
-                    OPTIONS[3][pos] = 'n';
-                    OPTIONS[3][pos + 1] = 'o';
-                    OPTIONS[3][pos + 2] = ' ';
-                }
-                renderer.drawText(x, y, OPTIONS[3]);
-                y += line;
-                
-                // key binding options
-                int end = 4 + KeyHandler.ARRAY.length;
-                for(int i = 4; i < end; i++)
-                {
-                    String name = KeyHandler.ARRAY[i - 4].toString();
-                    System.arraycopy(name.toCharArray(), 0, OPTIONS[i], 
-                            OPTIONS[i].length - OPTION_OFFSET + 1, Math.min(OPTION_OFFSET - 2, name.length()));
-                    renderer.drawText(x, y, OPTIONS[i]);
-                    y += line;
-                }
-                
-                // save
-                renderer.drawText(x, y, OPTIONS[end]);
-                if(optionsDirty)
-                {
-                    renderer.save();
-                    renderer.setFillColor(180, 0, 0, 1);
-                    renderer.setStrokeColor(180, 0, 0, 1);
-                    renderer.drawText(x, y, SAVE_OVERLAY);
-                    renderer.restore();
-                }
-                else
-                {
-                    renderer.drawText(x, y, SAVE_OVERLAY);
-                }
-                y += line;
-                
-                // left over option table
-                for(int i = end + 1; i < OPTIONS.length; i++)
-                {
-                    renderer.drawText(x, y, OPTIONS[i]);
-                    y += line;
-                }
-                
-                break;
-            }
-            case 3:
-            {
-                // level screen rendering
-                renderer.prepareTextDrawing(255, 255, 255, 1.0, MENU_WIDTH * 5 / 4);
-
-                int listLength = Math.min(levels.length, MENU_MAX);
-                double x = (renderer.getWidth() - renderer.getTextWidth(MENU_WIDTH)) * 0.5;
-                double y = (renderer.getHeight() - renderer.getTextHeight(listLength + 4)) * 0.5;
-                double line = renderer.getTextHeight(1);
-
-                renderer.drawText(x, y, TABLE_TOP);
-                y += line;
-                renderer.drawText(x, y, TABLE_HEADING);
-                y += line;
-                renderer.drawText(x, y, TABLE_MID);
-                y += line;
-
-                if(levels.length > MENU_MAX)
-                {
-                    int upperHalf = MENU_MAX / 2;
-                    int downHalf = levels.length - upperHalf;
-                    if(levelIndex < upperHalf)
-                    {
-                        paintStringMarking(x, y, line, levelIndex);
-                        y = paintLevelName(x, y, line, 0, MENU_MAX - 1);
-                        renderer.drawText(x, y, TABLE_MORE);
-                        y += line;
-                    }
-                    else if(levelIndex >= downHalf)
-                    {
-                        paintStringMarking(x, y, line, levelIndex - downHalf + upperHalf + ((MENU_MAX & 1) != 0 ? 1 : 0));
-                        renderer.drawText(x, y, TABLE_MORE);
-                        y += line;
-                        y = paintLevelName(x, y, line, levels.length - MENU_MAX + 1, MENU_MAX - 1);
-                    }
-                    else
-                    {
-                        paintStringMarking(x, y, line, upperHalf);
-                        renderer.drawText(x, y, TABLE_MORE);
-                        y += line;
-                        y = paintLevelName(x, y, line, levelIndex - upperHalf + 1, MENU_MAX - 2);
-                        renderer.drawText(x, y, TABLE_MORE);
-                        y += line;
-                    }
-                }
-                else
-                {
-                    paintStringMarking(x, y, line, levelIndex);
-                    y = paintLevelName(x, y, line, 0, levels.length);
-                }
-
-                renderer.drawText(x, y, TABLE_BOTTOM);
-                renderer.stopTextDrawing();
-                break;
-            }
-        }
-    }
-    
-    private double paintLevelName(double x, double y, double line, int from, int length)
-    {
-        SimpleConfig sp = SAVE_SLOTS[slotScreenIndex];
-        
-        char[] chars = new char[MENU_WIDTH];
-        chars[0] = 134;
-        chars[MENU_WIDTH - 1] = 134;
-
-        length += from;
-        for(int j = from; j < length; j++)
-        {
-            String s = levels[j].getName();
-            int border = Math.min(MENU_WIDTH - 14, 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 - 13] = 134;
-            chars[chars.length - 7] = 134;
-            
-            // bottles
-            char[] tmp = levels[j].formatBottles(sp.getInt("level." + s + ".bottles", 0));
-            int l = Math.min(5, tmp.length);
-            System.arraycopy(tmp, 0, chars, chars.length - 7 - l, l);
-            // time
-            tmp = levels[j].formatTime(sp.getDouble("level." + s + ".time", -1.0));
-            l = Math.min(5, tmp.length);
-            System.arraycopy(tmp, 0, chars, chars.length - 1 - l, l);
-            
-            renderer.drawText(x, y, chars);
-            y += line;
-        }
-        return y;
-    }
-    
-    private void paintStringMarking(double x, double y, double line, int pos)
-    {
-        renderer.save();
-        renderer.setFillColor(128, 128, 128, 1.0);
-        renderer.fillRectangle(
-                x + renderer.getTextWidth(1),
-                y + renderer.getTextHeight(pos) - 1, 
-                renderer.getTextWidth(MENU_WIDTH - 14), 
-                line);
-        
-        renderer.fillRectangle(
-                x + renderer.getTextWidth(MENU_WIDTH - 12),
-                y + renderer.getTextHeight(pos) - 1, 
-                renderer.getTextWidth(5), 
-                line);
-        
-        renderer.fillRectangle(
-                x + renderer.getTextWidth(MENU_WIDTH - 6),
-                y + renderer.getTextHeight(pos) - 1, 
-                renderer.getTextWidth(5), 
-                line);
-        renderer.restore();
-    }
-    
-    public void tickTiles()
-    {
-        registeredTiles.values().forEach(tile -> tile.tick());
-    }
-}

+ 3 - 13
src/me/hammerle/supersnuvi/javafx/JavaRenderer.java

@@ -9,7 +9,7 @@ import javafx.scene.canvas.GraphicsContext;
 import javafx.scene.input.KeyEvent;
 import javafx.scene.paint.Color;
 import javafx.stage.Stage;
-import me.hammerle.supersnuvi.rendering.Game;
+import me.hammerle.supersnuvi.rendering.OldGame;
 import me.hammerle.text.SnuviTextPainter;
 
 public class JavaRenderer extends Application implements IJavaRenderer
@@ -86,13 +86,13 @@ public class JavaRenderer extends Application implements IJavaRenderer
     // -------------------------------------------------------------------------
     
     private AnimationTimer timer = null;
-    private Game game;
+    private OldGame game;
     
     private void startLoop()
     {
         if(timer == null)
         {
-            game = new Game(this);
+            game = new OldGame(this);
             timer = new AnimationTimer() 
             {
                 private long last = System.nanoTime();
@@ -105,7 +105,6 @@ public class JavaRenderer extends Application implements IJavaRenderer
                     last = now;
                     while(lag >= 12_500_000)
                     {
-                        KeyHandler.tick();
                         game.update();
                         lag -= 12_500_000;
                         game.render();
@@ -122,15 +121,6 @@ public class JavaRenderer extends Application implements IJavaRenderer
     
     private void initKeyListener()
     {
-        // basic input handling
-        scene.setOnKeyPressed((KeyEvent e) -> 
-        {
-            KeyHandler.pressKey(e.getCode());
-        });
-        scene.setOnKeyReleased((KeyEvent e) -> 
-        {
-            KeyHandler.releaseKey(e.getCode());
-        });
     }
 
     // -------------------------------------------------------------------------

+ 0 - 117
src/me/hammerle/supersnuvi/javafx/KeyBinding.java

@@ -1,117 +0,0 @@
-package me.hammerle.supersnuvi.javafx;
-
-import javafx.scene.input.KeyCode;
-
-public class KeyBinding implements IKeyBinding
-{
-    private boolean isDown;
-    private boolean justReleased;
-    private int downTime;
-    
-    private KeyCode code;
-    private boolean isEdited;
-    
-    private final String name;
-    
-    public KeyBinding(String name, KeyCode code)
-    {
-        downTime = 0;
-        justReleased = false;
-        this.code = code;
-        this.name = name;
-    }
-    
-    @Override
-    public String getName() 
-    {
-        return name;
-    }
-    
-    @Override
-    public String getKeyName() 
-    {
-        return code.getName();
-    }
-    
-    public void setEditStatus(boolean b)
-    {
-        isEdited = b;
-    }
-    
-    public KeyCode getKeyCode()
-    {
-        return code;
-    }
-    
-    public void setKeyCode(KeyCode code)
-    {
-        this.code = code; 
-    }
-    
-    public void press()
-    {
-        isDown = true;
-    }
-    
-    public void release()
-    {
-        isDown = false;
-    }
-    
-    public void tick()
-    {   
-        if(isDown)
-        {
-            downTime++;
-        }
-        else if(justReleased)
-        {
-            downTime = 0;
-            justReleased = false;
-        }
-        else if(downTime > 0)
-        {
-            justReleased = true;
-        }
-    }
-    
-    @Override
-    public boolean isDown()
-    {
-        return downTime > 0;
-    }
-
-    @Override
-    public int getDownTime() 
-    {
-        return downTime;
-    }
-
-    @Override
-    public boolean wasJustReleased() 
-    {
-        return justReleased;
-    }
-    
-    @Override
-    public void resetTime() 
-    {
-        downTime = 0;
-    }
-
-    @Override
-    public String toString() 
-    {
-        if(isEdited)
-        {
-            return "[...]    ";
-        }
-        StringBuilder sb = new StringBuilder();
-        sb.append(code.getName());
-        if(sb.length() < 9)
-        {
-            sb.append("         ".substring(sb.length()));
-        }
-        return sb.toString();
-    }
-}

+ 0 - 110
src/me/hammerle/supersnuvi/javafx/KeyHandler.java

@@ -1,110 +0,0 @@
-package me.hammerle.supersnuvi.javafx;
-
-import java.util.EnumMap;
-import javafx.scene.input.KeyCode;
-import me.hammerle.supersnuvi.gamelogic.StateRenderer;
-
-public class KeyHandler
-{
-    private KeyHandler()
-    {
-    }
-    
-    private final static EnumMap<KeyCode, KeyBinding> BINDINGS = new EnumMap(KeyCode.class);
-    
-    private static KeyBinding registerBinding(String name, KeyCode error)
-    {
-        String s = StateRenderer.CONFIG.getString(name);
-        KeyCode code;
-        if(s == null)
-        {
-            code = error;
-        }
-        else
-        {
-            code = KeyCode.getKeyCode(s);
-            if(code == null)
-            {
-                System.out.println("cant find key '" + s + "'");
-                code = error;
-            }
-        }
-        KeyBinding binding = new KeyBinding(name, code);
-        BINDINGS.put(code, binding);
-        return binding;
-    }
-    
-    public final static IKeyBinding UP = registerBinding("key.up", KeyCode.UP);
-    public final static IKeyBinding DOWN = registerBinding("key.down", KeyCode.DOWN);
-    public final static IKeyBinding LEFT = registerBinding("key.left", KeyCode.LEFT);
-    public final static IKeyBinding RIGHT = registerBinding("key.right", KeyCode.RIGHT);
-    public final static IKeyBinding JUMP = registerBinding("key.jump", KeyCode.SPACE);
-    public final static IKeyBinding RUN = registerBinding("key.run", KeyCode.SHIFT);
-    public final static IKeyBinding ESCAPE = registerBinding("key.escape", KeyCode.ESCAPE);
-    public final static IKeyBinding ENTER = registerBinding("key.enter", KeyCode.ENTER);
-    
-    public final static IKeyBinding COMBAT = registerBinding("key.combat", KeyCode.A);
-    public final static IKeyBinding COMBAT_SWITCH_FACE = registerBinding("key.combat.switchface", KeyCode.S);
-    public final static IKeyBinding COMBAT_DASH = registerBinding("key.combat.dash", KeyCode.D);
-    public final static IKeyBinding COMBAT_DODGE = registerBinding("key.combat.dodge", KeyCode.F);
-    public final static IKeyBinding COMBAT_BLOCK = registerBinding("key.combat.block", KeyCode.G);
-    public final static IKeyBinding COMBAT_ATTACK = registerBinding("key.combat.attack", KeyCode.H);
-    
-    public final static IKeyBinding[] ARRAY = new IKeyBinding[] 
-    {
-        UP, DOWN, LEFT,  RIGHT, JUMP, RUN, ESCAPE, ENTER, COMBAT, COMBAT_SWITCH_FACE, COMBAT_DASH, COMBAT_DODGE, COMBAT_BLOCK, COMBAT_ATTACK
-    };
-    
-    private static KeyBinding rebind = null;
-    
-    public static void rebindKey(IKeyBinding binding)
-    {
-        rebind = (KeyBinding) binding;
-        rebind.setEditStatus(true);
-    }
-           
-    protected static void pressKey(KeyCode key) 
-    {
-        if(rebind == null)
-        {
-            KeyBinding binding = BINDINGS.get(key);
-            if(binding != null)
-            {
-                binding.press();
-            }
-        }
-        else
-        {
-            for(IKeyBinding b : ARRAY)
-            {
-                if(((KeyBinding) b).getKeyCode() == key)
-                {
-                    rebind.setEditStatus(false);
-                    rebind = null;
-                    return;
-                }
-            }
-                
-            BINDINGS.remove(rebind.getKeyCode());
-            rebind.setKeyCode(key);
-            BINDINGS.put(key, rebind);
-            rebind.setEditStatus(false);
-            rebind = null;
-        }
-    }
-
-    protected static void releaseKey(KeyCode key) 
-    {
-        KeyBinding binding = BINDINGS.get(key);
-        if(binding != null)
-        {
-            binding.release();
-        }
-    }
-    
-    protected static void tick()
-    {
-        BINDINGS.values().forEach(binding -> binding.tick());
-    }
-}
-

+ 2 - 11
src/me/hammerle/supersnuvi/rendering/Game.java → src/me/hammerle/supersnuvi/rendering/OldGame.java

@@ -1,10 +1,9 @@
 package me.hammerle.supersnuvi.rendering;
 
-import me.hammerle.supersnuvi.gamelogic.StateRenderer;
 import me.hammerle.supersnuvi.javafx.IJavaRenderer;
 import me.hammerle.supersnuvi.javafx.Image;
 
-public class Game implements IRenderer
+public class OldGame implements IRenderer
 {
     // constants
     public final static int TILE_SIZE = 64;
@@ -19,10 +18,7 @@ public class Game implements IRenderer
     private double offsetX;
     private double offsetY;
     
-    // world rendering
-    private final StateRenderer state;
-    
-    public Game(IJavaRenderer renderer)
+    public OldGame(IJavaRenderer renderer)
     {
         // basic graphic stuff
         this.renderer = renderer;
@@ -31,13 +27,10 @@ public class Game implements IRenderer
         this.cameraY = 0;
         this.offsetX = 0;
         this.offsetY = 0;
-        
-        this.state = new StateRenderer(this);
     }
     
     public void update() 
     {
-        state.update();
     }
     
     public void render() 
@@ -45,8 +38,6 @@ public class Game implements IRenderer
         offsetX = -cameraX;
         offsetY = renderer.getHeight() + cameraY;
         renderer.prepareRendering();
-        
-        state.render();
     }
     
     // -------------------------------------------------------------------------

+ 2 - 2
src/me/hammerle/supersnuvi/tiles/BottledSoulTile.java

@@ -1,9 +1,9 @@
 package me.hammerle.supersnuvi.tiles;
 
 import java.util.HashSet;
+import me.hammerle.supersnuvi.Game;
 import me.hammerle.supersnuvi.javafx.Image;
 import me.hammerle.supersnuvi.entity.Entity;
-import me.hammerle.supersnuvi.gamelogic.StateRenderer;
 import me.hammerle.supersnuvi.util.CollisionBox;
 import me.hammerle.supersnuvi.util.Face;
 import me.hammerle.supersnuvi.util.SoundUtils;
@@ -66,7 +66,7 @@ public class BottledSoulTile extends Tile
     {
         if(states.contains(getKey(x, y)))
         {
-            return StateRenderer.FALLBACK_TILE.getImage(x, y);
+            return Game.FALLBACK_TILE.getImage(x, y);
         }
         return IMAGE[score - 1];
     }

+ 2 - 2
src/me/hammerle/supersnuvi/tiles/ColoredBaseTile.java

@@ -1,7 +1,7 @@
 package me.hammerle.supersnuvi.tiles;
 
 import me.hammerle.supersnuvi.javafx.Image;
-import me.hammerle.supersnuvi.rendering.Game;
+import me.hammerle.supersnuvi.rendering.OldGame;
 import me.hammerle.supersnuvi.util.Utils;
 
 public class ColoredBaseTile extends Tile
@@ -10,7 +10,7 @@ public class ColoredBaseTile extends Tile
     
     public ColoredBaseTile(int r, int g, int b, int alpha)
     {
-        image = Utils.getColoredImage(r, g, b, alpha, Game.TILE_SIZE, Game.TILE_SIZE);
+        image = Utils.getColoredImage(r, g, b, alpha, OldGame.TILE_SIZE, OldGame.TILE_SIZE);
     }
 
     @Override

+ 2 - 2
src/me/hammerle/supersnuvi/tiles/CrumblingStoneTile.java

@@ -1,11 +1,11 @@
 package me.hammerle.supersnuvi.tiles;
 
 import java.util.HashSet;
+import me.hammerle.supersnuvi.Game;
 import me.hammerle.supersnuvi.javafx.Image;
 import me.hammerle.supersnuvi.entity.Entity;
 import me.hammerle.supersnuvi.entity.EntityBuilder;
 import me.hammerle.supersnuvi.gamelogic.Level;
-import me.hammerle.supersnuvi.gamelogic.StateRenderer;
 import me.hammerle.supersnuvi.util.CollisionBox;
 import me.hammerle.supersnuvi.util.Face;
 import me.hammerle.supersnuvi.util.SoundUtils;
@@ -64,7 +64,7 @@ public class CrumblingStoneTile extends Tile
     {
         if(states.contains(getKey(x, y)))
         {
-            return StateRenderer.FALLBACK_TILE.getImage(x, y);
+            return Game.FALLBACK_TILE.getImage(x, y);
         }
         return image;
     }

+ 2 - 2
src/me/hammerle/supersnuvi/tiles/StartTile.java

@@ -1,7 +1,7 @@
 package me.hammerle.supersnuvi.tiles;
 
+import me.hammerle.supersnuvi.Game;
 import me.hammerle.supersnuvi.javafx.Image;
-import me.hammerle.supersnuvi.gamelogic.StateRenderer;
 
 public class StartTile extends Tile
 {
@@ -10,6 +10,6 @@ public class StartTile extends Tile
     @Override
     public Image getImage(int x, int y) 
     {
-        return StateRenderer.FALLBACK_TILE.getImage(x, y);
+        return Game.FALLBACK_TILE.getImage(x, y);
     }
 }

+ 2 - 0
src/me/hammerle/supersnuvi/tiles/Tile.java

@@ -8,6 +8,8 @@ import me.hammerle.supersnuvi.util.Face;
 
 public abstract class Tile 
 {
+    public final static int SIZE = 32;
+    
     private CollisionBox movementCollision;
     private CollisionBox collisionBox;
     

+ 2 - 2
src/me/hammerle/supersnuvi/util/CollisionBox.java

@@ -1,6 +1,6 @@
 package me.hammerle.supersnuvi.util;
 
-import me.hammerle.supersnuvi.rendering.Game;
+import me.hammerle.supersnuvi.rendering.OldGame;
 
 public class CollisionBox 
 {
@@ -31,7 +31,7 @@ public class CollisionBox
     
     public static CollisionBox createScaledBox(double x1, double y1, double x2, double y2)
     {
-        return new CollisionBox(x1 * Game.TILE_SIZE, y1 * Game.TILE_SIZE, x2 * Game.TILE_SIZE, y2 * Game.TILE_SIZE);
+        return new CollisionBox(x1 * OldGame.TILE_SIZE, y1 * OldGame.TILE_SIZE, x2 * OldGame.TILE_SIZE, y2 * OldGame.TILE_SIZE);
     }
     
     public CollisionBox copy()

+ 2 - 7
src/me/hammerle/supersnuvi/util/SoundUtils.java

@@ -1,15 +1,12 @@
 package me.hammerle.supersnuvi.util;
 
 import java.io.File;
-import javax.sound.sampled.AudioFileFormat;
 import javax.sound.sampled.AudioFormat;
 import javax.sound.sampled.AudioInputStream;
 import javax.sound.sampled.AudioSystem;
 import javax.sound.sampled.Clip;
 import javax.sound.sampled.DataLine;
-import javax.sound.sampled.Line;
-import javax.sound.sampled.Mixer;
-import me.hammerle.supersnuvi.gamelogic.StateRenderer;
+import me.hammerle.supersnuvi.Game;
 
 public class SoundUtils 
 {
@@ -31,8 +28,6 @@ public class SoundUtils
         }
     }
     
-    
-    
     public enum Sound
     {
         DASH("dash", true),
@@ -106,7 +101,7 @@ public class SoundUtils
     
     public static void playSound(Sound sound)
     {
-        if(StateRenderer.isSoundEnabled())
+        if(Game.get().isSoundEnabled())
         {
             /*if(sound != Sound.MENU_MUSIC && sound != Sound.SONG_1 && sound != Sound.WALK)
             {

+ 3 - 3
src/me/hammerle/supersnuvi/util/Utils.java

@@ -3,8 +3,8 @@ package me.hammerle.supersnuvi.util;
 import me.hammerle.supersnuvi.javafx.Image;
 import me.hammerle.supersnuvi.javafx.ResourceImage;
 import me.hammerle.supersnuvi.javafx.WritableImage;
-import me.hammerle.supersnuvi.rendering.Game;
-import static me.hammerle.supersnuvi.rendering.Game.TILE_SIZE;
+import me.hammerle.supersnuvi.rendering.OldGame;
+import static me.hammerle.supersnuvi.rendering.OldGame.TILE_SIZE;
 
 public class Utils 
 {
@@ -52,7 +52,7 @@ public class Utils
     public static Image getTileImage(String path)
     {
         ResourceImage image = new ResourceImage();
-        image.load("resources/" + path + ".png", Game.TILE_SIZE, Game.TILE_SIZE);
+        image.load("resources/" + path + ".png", OldGame.TILE_SIZE, OldGame.TILE_SIZE);
         return image;
     }