浏览代码

refactoring, preparation for collision / physic update, collision moved
into entities / tiles

Kajetan Johannes Hammerle 5 年之前
父节点
当前提交
0252c11b6d
共有 39 个文件被更改,包括 788 次插入510 次删除
  1. 12 14
      levels/00-Tech_Demo.snuvi
  2. 14 10
      src/me/hammerle/supersnuvi/Game.java
  3. 3 1
      src/me/hammerle/supersnuvi/Keys.java
  4. 297 219
      src/me/hammerle/supersnuvi/entity/Entity.java
  5. 51 25
      src/me/hammerle/supersnuvi/entity/EntityBuilder.java
  6. 4 12
      src/me/hammerle/supersnuvi/entity/components/DefaultEnergy.java
  7. 3 24
      src/me/hammerle/supersnuvi/entity/components/DefaultHealth.java
  8. 2 14
      src/me/hammerle/supersnuvi/entity/components/DefaultMovement.java
  9. 2 9
      src/me/hammerle/supersnuvi/entity/components/Energy.java
  10. 2 20
      src/me/hammerle/supersnuvi/entity/components/Health.java
  11. 0 10
      src/me/hammerle/supersnuvi/entity/components/IDeath.java
  12. 10 0
      src/me/hammerle/supersnuvi/entity/components/IDespawn.java
  13. 2 26
      src/me/hammerle/supersnuvi/entity/components/Movement.java
  14. 5 4
      src/me/hammerle/supersnuvi/entity/components/StoneMovement.java
  15. 6 13
      src/me/hammerle/supersnuvi/entity/components/ai/Controller.java
  16. 24 16
      src/me/hammerle/supersnuvi/entity/components/ai/HumanController.java
  17. 41 27
      src/me/hammerle/supersnuvi/entity/components/ai/LondonerController.java
  18. 16 12
      src/me/hammerle/supersnuvi/entity/components/ai/PlatformController.java
  19. 7 13
      src/me/hammerle/supersnuvi/entity/components/ai/StartScreenHeroController.java
  20. 2 7
      src/me/hammerle/supersnuvi/entity/components/ai/StoneController.java
  21. 14 8
      src/me/hammerle/supersnuvi/gamelogic/Level.java
  22. 7 0
      src/me/hammerle/supersnuvi/math/IVector.java
  23. 84 0
      src/me/hammerle/supersnuvi/math/Vector.java
  24. 32 0
      src/me/hammerle/supersnuvi/tiles/BaseBoxTile.java
  25. 41 0
      src/me/hammerle/supersnuvi/tiles/BaseCollisionTile.java
  26. 3 2
      src/me/hammerle/supersnuvi/tiles/BottledSoulTile.java
  27. 2 2
      src/me/hammerle/supersnuvi/tiles/CrumblingStoneTile.java
  28. 2 2
      src/me/hammerle/supersnuvi/tiles/GoalTile.java
  29. 3 3
      src/me/hammerle/supersnuvi/tiles/InteractTile.java
  30. 2 2
      src/me/hammerle/supersnuvi/tiles/KillTile.java
  31. 3 3
      src/me/hammerle/supersnuvi/tiles/PressureTile.java
  32. 24 0
      src/me/hammerle/supersnuvi/tiles/RampTile.java
  33. 2 2
      src/me/hammerle/supersnuvi/tiles/SlipperyTile.java
  34. 2 2
      src/me/hammerle/supersnuvi/tiles/SpikeTile.java
  35. 15 0
      src/me/hammerle/supersnuvi/tiles/Tile.java
  36. 1 1
      src/me/hammerle/supersnuvi/tiles/TrampolinTile.java
  37. 4 4
      src/me/hammerle/supersnuvi/tiles/WaterTile.java
  38. 4 3
      src/me/hammerle/supersnuvi/util/Face.java
  39. 40 0
      src/me/hammerle/supersnuvi/util/Utils.java

+ 12 - 14
levels/00-Tech_Demo.snuvi

@@ -4,7 +4,7 @@ event.load("tile_hit");
 event.load("auto_tile_interact");
 //event.load("entity_spawn");
 
-sgoto(0, @loop);
+//sgoto(0, @loop);
 
 @main
 wait();
@@ -31,20 +31,18 @@ elseif(event == "entity_spawn")
 goto(@main);
 
 
-@loop
-
-light.setPositionSmooth(0, entity.getX(entity.getHero()) + 16, entity.getY(entity.getHero()) + 32);
-
-sgoto(0, @loop);
-goto(@main);
+//@loop
+//light.setPositionSmooth(0, entity.getX(entity.getHero()) + 16, entity.getY(entity.getHero()) + 32);
+//sgoto(0, @loop);
+//goto(@main);
 
 
 function onLevelReset()
 {
-    light.setAmbient(0.0, 0.0, 0.0);
-    light.setColor(0, 1.2, 0.9, 0);
-    light.setPosition(0, entity.getX(entity.getHero()), entity.getY(entity.getHero()));
-    light.setStrength(0, 0.005);
+    //light.setAmbient(0.0, 0.0, 0.0);
+    //light.setColor(0, 1.2, 0.9, 0);
+    //light.setPosition(0, entity.getX(entity.getHero()), entity.getY(entity.getHero()));
+    //light.setStrength(0, 0.005);
     
     hero = entity.getHero();
     x = tile.toTileCoord(entity.getX(hero));
@@ -72,9 +70,9 @@ function onLevelReset()
     level.setTile(index, 7, 11, 73);
     level.setTile(index, 8, 10, 74);
     
-    $platform = platform.spawn(tile.toLevelCoord(3), tile.toLevelCoord(3), 3);
-    platform.addmove($platform, tile.toLevelCoord(0), tile.toLevelCoord(9), 1, 2, 50);
-    platform.addmove($platform, tile.toLevelCoord(3), tile.toLevelCoord(3), 1, 2, 50);
+    $platform = platform.spawn(tile.toLevelCoord(5), tile.toLevelCoord(11), 3);
+    platform.addmove($platform, tile.toLevelCoord(5), tile.toLevelCoord(11), 6, 4, 1);
+    platform.addmove($platform, tile.toLevelCoord(5), tile.toLevelCoord(5), 6, 4, 1);
 }
 
 function onInteract(x, y, ent)

+ 14 - 10
src/me/hammerle/supersnuvi/Game.java

@@ -128,7 +128,10 @@ public class Game extends Engine
             SoundUtils.stopSound(SoundUtils.Sound.MENU_MUSIC);
             
             snuviScheduler.setActiveLevel(currentLevel);
-            currentLevel.tick();
+            //if(Keys.TEST.isReleased())
+            {
+                currentLevel.tick();
+            }
             snuviScheduler.tick(currentLevel);
             
             // doing that here to prevent concurent modification
@@ -306,7 +309,8 @@ public class Game extends Engine
         }
     }
     
-    private final static int COLOR_OVERLAY = 0x77000000;
+    private final static int COLOR_OVERLAY = 0x77808080;
+    private final static int COLOR_OVERLAY2 = 0x77202020;
 
     private String getKeyName(KeyBinding key)
     {
@@ -356,7 +360,7 @@ public class Game extends Engine
                 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);
+                cr.drawRectangle(left, base, right, base + line, COLOR_OVERLAY2);
                 Shader.setBlendingEnabled(false);
                         
                 Shader.setTextureEnabled(true);
@@ -392,7 +396,7 @@ public class Game extends Engine
                 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);
+                cr.drawRectangle(left, base, right, base + line, COLOR_OVERLAY2);
                 Shader.setBlendingEnabled(false);
                         
                 Shader.setTextureEnabled(true);
@@ -429,7 +433,7 @@ public class Game extends Engine
                 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);
+                cr.drawRectangle(left, base, right, base + line, COLOR_OVERLAY2);
                 Shader.setBlendingEnabled(false);
                         
                 Shader.setTextureEnabled(true);
@@ -522,7 +526,7 @@ public class Game extends Engine
                     baseIndex = half;
                 }
                 float base = top + (3 + baseIndex) * line;
-                cr.drawRectangle(left, base, right, base + line, COLOR_OVERLAY);
+                cr.drawRectangle(left, base, right, base + line, COLOR_OVERLAY2);
                 Shader.setBlendingEnabled(false);
                         
                 Shader.setTextureEnabled(true);
@@ -777,16 +781,16 @@ public class Game extends Engine
             ent.setPosition(x, y);
             return Void.TYPE;
         });
-        snuviParser.registerFunction("entity.getmotionx", (sc, in) -> (double) ((Entity) in[0].get(sc)).getMotionX());
-        snuviParser.registerFunction("entity.getmotiony", (sc, in) -> (double) ((Entity) in[0].get(sc)).getMotionY());
+        snuviParser.registerFunction("entity.getmotionx", (sc, in) -> (double) ((Entity) in[0].get(sc)).getOwnForceX());
+        snuviParser.registerFunction("entity.getmotiony", (sc, in) -> (double) ((Entity) in[0].get(sc)).getOwnForceY());
         snuviParser.registerFunction("entity.setmotionx", (sc, in) -> 
         {
-            ((Entity) in[0].get(sc)).setMotionX(in[1].getFloat(sc));
+            ((Entity) in[0].get(sc)).applyForce(in[1].getFloat(sc), 0.0f);
             return Void.TYPE;
         });
         snuviParser.registerFunction("entity.setmotiony", (sc, in) -> 
         {
-            ((Entity) in[0].get(sc)).setMotionY(in[1].getFloat(sc));
+            ((Entity) in[0].get(sc)).applyForce(0.0f, in[1].getFloat(sc));
             return Void.TYPE;
         });
         snuviParser.registerFunction("entity.ishero", (sc, in) -> currentLevel.getHero() == in[0].get(sc));

+ 3 - 1
src/me/hammerle/supersnuvi/Keys.java

@@ -17,10 +17,12 @@ public class Keys
     public final static KeyBinding ESCAPE = KeyHandler.register(GLFW_KEY_ESCAPE);
     public final static KeyBinding ENTER = KeyHandler.register(GLFW_KEY_ENTER);
     
+    public final static KeyBinding TEST = KeyHandler.register(GLFW_KEY_T);
+    
     private final static KeyBinding[] KEYS = 
     {
         UP, DOWN, LEFT, RIGHT, 
-        JUMP, RUN, ESCAPE, ENTER
+        JUMP, RUN, ESCAPE, ENTER, TEST
     };
     
     public static void rebind(KeyBinding binding)

+ 297 - 219
src/me/hammerle/supersnuvi/entity/Entity.java

@@ -1,72 +1,61 @@
 package me.hammerle.supersnuvi.entity;
 
-import java.util.List;
 import me.hammerle.supersnuvi.entity.components.ai.Controller;
 import me.hammerle.supersnuvi.entity.components.Energy;
 import me.hammerle.supersnuvi.entity.components.Health;
 import me.hammerle.supersnuvi.entity.components.ItemCollector;
 import me.hammerle.supersnuvi.entity.components.Movement;
 import me.hammerle.supersnuvi.tiles.Tile;
-import me.hammerle.supersnuvi.util.CollisionObject;
 import me.hammerle.supersnuvi.util.Face;
 import me.hammerle.supersnuvi.util.Utils;
 import me.hammerle.supersnuvi.gamelogic.Level;
+import me.hammerle.supersnuvi.math.Vector;
+import me.hammerle.supersnuvi.entity.components.IDespawn;
 
 public final class Entity
 {
     public static final float GRAVITY = 8.0f * Tile.SIZE_SCALE;
     public static final float STEP = 0.0625f;
     // this one is a little bit bigger to prevent wrong calculation
-    // while joing upwars
+    // while going upwards
     public static final float UP_STEP = STEP + 0.00390625f;
     
     // the last position is used for interpolation during rendering
-    private float lastPosX;
-    private float lastPosY;
+    private final Vector lastPos = new Vector();
     // the current position of the entity
-    private float posX;
-    private float posY;
-    
-    // the collision box of the entity
-    private final CollisionObject box;
-    
-    // the motion before the movement collision check
-    private float preMotionX;
-    private float preMotionY;
-    // the motion after the movement collision check
-    private float motionX;
-    private float motionY;
-    
+    private final Vector pos = new Vector();
+    // the width of the entity
+    private final float width;
+    // the height of the entity
+    private final float height;  
+    // own force used by the controller component
+    private final Vector ownForce = new Vector();
+    // the motion, reduced by the movement collision check
+    private final Vector motion = new Vector();
+    // the friction reducing motion each tick
+    private final Vector friction = new Vector(0.0f, 1.0f);
     // a flag indicating that the entity is on the ground
     private boolean onGround = true;
-    
     // entity components
-    protected Controller controller = Controller.NULL;
-    protected Health health = Health.NULL;
-    protected Energy energy = Energy.NULL;
-    protected Movement move = Movement.NULL;
-    protected ItemCollector itemCollector = ItemCollector.NULL;
-    
-    // face
-    private Face face = Face.RIGHT;
-    
+    private final Controller controller;
+    private final Health health;
+    private final Energy energy;
+    private final Movement move;
+    private final ItemCollector itemCollector;
+    private final IDespawn onDespawn;
+    // the type of the entity, used by snuvi script
     private final String type;
     
-    protected Entity(String type, float x, float y, CollisionObject box)
+    protected Entity(float width, float height, Controller c, Health h, Energy e, Movement m, ItemCollector ic, IDespawn d, String type)
     {
-        lastPosX = x;
-        lastPosY = y;
-        posX = x;
-        posY = y;
-
-        // ensure the box cannot be modified from the outside
-        this.box = box.copy().offset(x, y);
-        
-        preMotionX = 0.0f;
-        preMotionY = 0.0f;
-        motionX = 0.0f;
-        motionY = 0.0f;
-        
+        this.width = width;
+        this.height = height;
+        this.controller = c;
+        this.health = h;
+        this.energy = e;
+        this.move = m;
+        this.itemCollector = ic;
+        this.onDespawn = d;
         this.type = type;
     }
     
@@ -109,119 +98,126 @@ public final class Entity
         return itemCollector;
     }
     
+    public void onDespawn()
+    {
+        onDespawn.onDespawn(this);
+    }
+    
     //--------------------------------------------------------------------------
     // basic stuff
     //--------------------------------------------------------------------------
     
     public float getSquaredDistance(Entity e)
     {
-        return Utils.getSquaredDistance(
-                posX + box.getWidth() * 0.5f, posY + box.getHeight() * 0.5f, 
-                e.posX + e.box.getWidth() * 0.5f, e.posY + e.box.getHeight() * 0.5f);
-    }
-    
-    public CollisionObject getBox()
-    {
-        return box;
+        return Utils.getSquaredDistance(getCenterX(), getCenterY(), e.getCenterX(), e.getCenterY());
     }
     
     public float getX()
     {
-        return posX;
+        return pos.getX();
     }
     
     public float getY()
     {
-        return posY;
+        return pos.getY();
     }
     
     public void setPosition(float x, float y)
     {
-        lastPosX = x;
-        lastPosY = y;
-        posX = x;
-        posY = y;
-        box.reset().offset(posX, posY);
+        lastPos.set(x, y);
+        pos.set(x, y);
     }
     
     public float getLastX()
     {
-        return lastPosX;
+        return lastPos.getX();
     }
     
     public float getLastY()
     {
-        return lastPosY;
+        return lastPos.getY();
     }
     
     public float getCenterX()
     {
-        return posX + box.getWidth() * 0.5f;
+        return pos.getX() + width * 0.5f;
     }
     
     public float getCenterY()
     {
-        return posY + box.getHeight() * 0.5f;
+        return pos.getY() + height * 0.5f;
     }
     
     public float getWidth()
     {
-        return box.getWidth();
+        return width;
     }
     
     public float getHeight()
     {
-        return box.getHeight();
+        return height;
+    }
+    
+    public float getMotionX()
+    {
+        return motion.getX();
     }
     
-    public Face getFace()
+    public float getMotionY()
     {
-        if(motionX == 0.0f)
-        {
-            return face;
-        }
-        face = motionX < 0.0f ? Face.LEFT : Face.RIGHT;
-        return face;
+        return motion.getY();
     }
     
-    public void updateFace()
+    public float getOwnForceX()
     {
-        
+        return ownForce.getX();
     }
     
-    public float getMotionX()
+    public float getOwnForceY()
     {
-        return motionX;
+        return ownForce.getY();
+    }
+
+    public void applyOwnForce(float x, float y)
+    {
+        ownForce.add(x, y);
     }
     
-    public void setMotionX(float motionX)
+    public void applyForce(float x, float y)
     {
-        this.motionX = motionX;
+        motion.add(x, y);
     }
     
-    public float getMotionY()
+    public void setFriction(float fx, float fy)
     {
-        return motionY;
+        friction.set(fx, fy);
     }
     
-    public void setMotionY(float motionY)
+    public boolean isAt(float x, float y)
     {
-        this.motionY = motionY;
+        return Math.abs(x - pos.getX()) < STEP && Math.abs(y - pos.getY()) < STEP;
     }
     
-    public float getPreMotionX()
+    public boolean isColliding(float minX, float minY, float maxX, float maxY)
     {
-        return preMotionX;
+        return maxX > getX() && getX() + width > minX && maxY > getY() && getY() + height > minY;
     }
     
-    public float getPreMotionY()
+    private Face getCollidingFace(Entity o)
     {
-        return preMotionY;
+        return Utils.getCollidingFace(
+                o.getX(), o.getY(), o.getX() + o.getWidth(), o.getY() + o.getHeight(),
+                getX(), getY(), getX() + getWidth(), getY() + getHeight());
     }
     
-    public boolean isAt(float x, float y)
+    public boolean jump() 
     {
-        return Math.abs(x - posX) < STEP && Math.abs(y - posY) < STEP;
+        if(onGround)
+        {
+            ownForce.addY(-move.getJumpPower());
+            return true;
+        }
+        return false;
     }
     
     //--------------------------------------------------------------------------
@@ -230,175 +226,257 @@ public final class Entity
     
     public void tick(Level level) 
     {
-        lastPosX = posX;
-        lastPosY = posY;
+        // reset own force
+        ownForce.set(0.0f, 0.0f);
         
-        controller.tick(level);
-        energy.tick();
+        // remeber last position for rendering
+        lastPos.set(pos);
         
-        preMotionX = motionX;
-        preMotionY = motionY;
+        // update onGround before controller tick
+        // ToDo: does not work for ramps
+        onGround = isOnTileGround(level);
         
-        if(move.hasGravity())
+        // tick components
+        controller.tick(this, level);
+        energy.tick(this);
+        health.tick();
+        
+        // apply gravity if needed
+        if(move.hasGravity(this))
         {
-            preMotionY += GRAVITY * move.getGravityFactor();
+            motion.addY(GRAVITY * move.getGravityFactor());
         }
 
+        // apply friction
+        motion.mul(friction);
+        
+        // modify current motion by own force
+        motion.add(ownForce);
+        
+        // (entity <-> tile) collision
+        doTileCollision(level);
+        // (entity <-> entity) collision
+        level.forEachCollidingEntity(this, (ent) -> 
+        {
+            controller.onCollideWithEntity(this, ent, getCollidingFace(ent));
+        });
+    }
+    
+    public void move(Level level)
+    {
+        reduceMotionByTiles(level);   
+        reduceMotionByEntities(level);     
+        pos.add(motion);
+    }
+    
+    private boolean isOnTileGround(Level level)
+    {
+        float minX = pos.getX();
+        float minY = pos.getY() + height;
+        float maxX = pos.getX() + width;
+        float maxY = pos.getY() + height + STEP * 2;
+        int startX = Utils.toBlock(minX);
+        int startY = Utils.toBlock(minY);
+        int endX = Utils.toBlock(maxX);
+        int endY = Utils.toBlock(maxY);
+
+        for(int x = startX; x <= endX; x++)
+        {
+            for(int y = startY; y <= endY; y++)
+            {
+                Tile t = level.getInteractionTile(x, y);
+                if(t.isMoveColliding(minX, minY, maxX, maxY, x, y, level))
+                {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+    
+    private void doTileCollision(Level level)
+    {
+        float minX = pos.getX() - STEP;
+        float minY = pos.getY() - STEP;
+        float maxX = pos.getX() + width + STEP;
+        float maxY = pos.getY() + height + STEP;
+        int startX = Utils.toBlock(minX);
+        int startY = Utils.toBlock(minY);
+        int endX = Utils.toBlock(maxX);
+        int endY = Utils.toBlock(maxY);
+
+        for(int x = startX; x <= endX; x++)
+        {
+            for(int y = startY; y <= endY; y++)
+            {
+                Tile t = level.getInteractionTile(x, y);
+                if(t.isColliding(minX, minY, maxX, maxY, x, y, level))
+                {
+                    Face f = t.getCollidingFace(minX, minY, maxX, maxY, x, y, level);
+                    controller.onCollideWithTile(this, x, y, level, t, f);
+                    t.onEntityCollide(this, x, y, f.getOpposite(), level);
+                }
+            }
+        }
+    }
+    
+    private void reduceMotionByEntities(Level level)
+    {
+    }
+    
+    private boolean isCollidingWithTiles(int startX, int startY, int endX, int endY, Level level)
+    {
+        float minX = getX();
+        float minY = getY();
+        float maxX = minX + width;
+        float maxY = minY + height;
+        for(int x = startX; x <= endX; x++)
+        {
+            for(int y = startY; y <= endY; y++)
+            {
+                if(level.getInteractionTile(x, y).isMoveColliding(minX, minY, maxX, maxY, x, y, level))
+                {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    public void reduceMotionByTiles(Level level)
+    {
         if(move.canMoveEverywhere())
         {
-            motionX = preMotionX;
-            motionY = preMotionY;
+            return;
+        }
+        
+        float mx = motion.getX();
+        float my = motion.getY();
+        
+        if(mx == 0.0f && my == 0.0f)
+        {
+            return;
+        }
+        
+        int startX;
+        int endX;
+        if(mx < 0.0f)
+        {
+            startX = Utils.toBlock(pos.getX() + mx);
+            endX = Utils.toBlock(pos.getX() + width);
         }
         else
         {
-            CollisionObject testBox = box.copy().expand(preMotionX, preMotionY).expand(0.0f, -UP_STEP);
-            List<CollisionObject> boxes = level.getMovementBoxesAt(testBox, this);
-            if(!boxes.isEmpty())
+            startX = Utils.toBlock(pos.getX());
+            endX = Utils.toBlock(pos.getX() + width + mx);
+        }
+        
+        int startY;
+        int endY;
+        if(my < 0.0f)
+        {
+            startY = Utils.toBlock(pos.getY() + my);
+            endY = Utils.toBlock(pos.getY() + height);
+        }
+        else
+        {
+            startY = Utils.toBlock(pos.getY());
+            endY = Utils.toBlock(pos.getY() + height + my);
+        }
+        
+        
+        float oldPosX = pos.getX();
+        float oldPosY = pos.getY();
+        
+        while(mx != 0.0 || my != 0.0)
+        {
+            if(mx != 0.0f)
             {
-                float mx = preMotionX;
-                float my = preMotionY;
-                
-                testBox.reset();
-                
-                float oldX = testBox.getMinX();
-                float oldY = testBox.getMinY();
-                
-                while(mx != 0.0 || my != 0.0)
+                float oldX = pos.getX();
+                if(mx < 0.0)
                 {
-                    testBox.save();
-
-                    if(mx < 0.0)
+                    if(mx > -STEP)
                     {
-                        if(mx > -STEP)
-                        {
-                            testBox.offsetX(mx);
-                            mx = 0.0f;
-                        }
-                        else
-                        {
-                            testBox.offsetX(-STEP);
-                            mx += STEP;
-                        }
+                        pos.addX(mx);
+                        mx = 0.0f;
                     }
-                    else if(mx > 0.0)
+                    else
                     {
-                        if(mx < STEP)
-                        {
-                            testBox.offsetX(mx);
-                            mx = 0.0f;
-                        }
-                        else
-                        {
-                            testBox.offsetX(STEP);
-                            mx -= STEP;
-                        }
+                        pos.addX(-STEP);
+                        mx += STEP;
                     }
-
-                    for(CollisionObject cb : boxes)
-                    {
-                        if(cb.isColliding(testBox))
-                        {
-                            testBox.offsetY(-UP_STEP);
-                            for(CollisionObject cb2 : boxes)
-                            {
-                                if(cb2.isColliding(testBox))
-                                {
-                                    mx = 0.0f;
-                                    testBox.reset();
-                                    break;
-                                }
-                            }
-                            break;
-                        }
-                    }
-                    
-                    testBox.save();
-
-                    if(my < 0.0)
+                }
+                else if(mx > 0.0)
+                {
+                    if(mx < STEP)
                     {
-                        if(my > -STEP)
-                        {
-                            testBox.offsetY(my);
-                            my = 0.0f;
-                        }
-                        else
-                        {
-                            testBox.offsetY(-STEP);
-                            my += STEP;
-                        }
+                        pos.addX(mx);
+                        mx = 0.0f;
                     }
-                    else if(my > 0.0)
+                    else
                     {
-                        if(my < STEP)
-                        {
-                            testBox.offsetY(my);
-                            my = 0.0f;
-                        }
-                        else
-                        {
-                            testBox.offsetY(STEP);
-                            my -= STEP;
-                        }
+                        pos.addX(STEP);
+                        mx -= STEP;
                     }
+                }
 
-                    for(CollisionObject cb : boxes)
+                if(isCollidingWithTiles(startX, startY, endX, endY, level))
+                {
+                    pos.addY(-UP_STEP);
+                    if(isCollidingWithTiles(startX, startY, endX, endY, level))
                     {
-                        if(cb.isColliding(testBox))
-                        {
-                            my = 0.0f;
-                            testBox.reset();
-                            break;
-                        }
+                        pos.addY(UP_STEP);
+                        pos.setX(oldX);
+                        mx = 0.0f;
                     }
                 }
-                
-                motionX = testBox.getMinX() - oldX;
-                motionY = testBox.getMinY() - oldY;
             }
-            else
+            if(my != 0.0f)
             {
-                motionX = preMotionX;
-                motionY = preMotionY;
-            }
-        }
-        
-        posX += motionX;
-        posY += motionY;
-        box.reset().offset(posX, posY);
-        
-        if(!move.canMoveEverywhere())
-        {
-            //onGround = preMotionY > 0.0f && motionY == 0;
-            onGround = !level.getMovementBoxesAt(box.copy().expand(0.0f, STEP * 2), this).isEmpty();
+                float oldY = pos.getY();
 
-            move.setInWater(false);
-            move.setFrictionFactor(0.6f);
-            // apply collision
-            CollisionObject cb = box.copy();
-            for(Face f : Face.values())
-            {
-                cb.reset();
-                cb.expand(f.getCollisionOffsetX(), f.getCollisionOffsetY());
-                level.getEntitiesCollidingWith(this, cb).forEach(ent -> this.controller.onCollideWithEntity(ent, f));
-                level.getCollisionBoxesAt(cb).forEach(loc -> 
+                if(my < 0.0)
                 {
-                    controller.onCollideWithTile(loc, f);
-                    loc.getTile().onEntityCollide(this, loc.getX(), loc.getY(), f.getOpposite(), level);
-                });
-            }
+                    if(my > -STEP)
+                    {
+                        pos.addY(my);
+                        my = 0.0f;
+                    }
+                    else
+                    {
+                        pos.addY(-STEP);
+                        my += STEP;
+                    }
+                }
+                else if(my > 0.0)
+                {
+                    if(my < STEP)
+                    {
+                        pos.addY(my);
+                        my = 0.0f;
+                    }
+                    else
+                    {
+                        pos.addY(STEP);
+                        my -= STEP;
+                    }
+                }
 
-            motionX *= move.getFrictionFactor();
-            if(Math.abs(motionX) < 0.3)
-            {
-                motionX = 0.0f;
+                if(isCollidingWithTiles(startX, startY, endX, endY, level))
+                {
+                    my = 0.0f;
+                    pos.setY(oldY);
+                }
             }
         }
-        health.tick();
+
+        motion.set(pos.getX() - oldPosX, pos.getY() - oldPosY);
+        pos.set(oldPosX, oldPosY);
     }
     
     public void renderTick(float lag)
     {
-        controller.renderTick(lag);
+        controller.renderTick(this, lag);
     }
     
     //--------------------------------------------------------------------------

+ 51 - 25
src/me/hammerle/supersnuvi/entity/EntityBuilder.java

@@ -3,65 +3,91 @@ package me.hammerle.supersnuvi.entity;
 import me.hammerle.supersnuvi.entity.components.DefaultHealth;
 import me.hammerle.supersnuvi.entity.components.DefaultMovement;
 import me.hammerle.supersnuvi.entity.components.DefaultEnergy;
+import me.hammerle.supersnuvi.entity.components.Energy;
+import me.hammerle.supersnuvi.entity.components.Health;
 import me.hammerle.supersnuvi.entity.components.StoneMovement;
 import me.hammerle.supersnuvi.entity.components.ai.HumanController;
 import me.hammerle.supersnuvi.entity.components.ai.LondonerController;
-import me.hammerle.supersnuvi.entity.components.IDeath;
 import me.hammerle.supersnuvi.entity.components.ItemCollector;
 import me.hammerle.supersnuvi.entity.components.ai.PlatformController;
 import me.hammerle.supersnuvi.entity.components.ai.StartScreenHeroController;
 import me.hammerle.supersnuvi.entity.components.ai.StoneController;
-import me.hammerle.supersnuvi.util.CollisionBox;
 import me.hammerle.supersnuvi.util.SoundUtils.Sound;
 import me.hammerle.supersnuvi.gamelogic.Level;
+import me.hammerle.supersnuvi.tiles.Tile;
+import me.hammerle.supersnuvi.entity.components.IDespawn;
 
 public final class EntityBuilder 
 {
     public static Entity buildHero(Level level, float x, float y)
     {
-        Entity hero = new Entity("Hero", x, y, CollisionBox.createScaledTileBox(0.0f, 0.15625f, 0.84375f, 2.0f));
-        hero.controller = new HumanController(hero);
-        hero.health = new DefaultHealth(hero, (ent) -> level.scheduleReset(), 100.0f, null, Sound.MIRROR_CRACK, Sound.MIRROR_BREAK);
-        hero.energy = new DefaultEnergy(hero, 100.0f);
-        hero.move = new DefaultMovement(hero, 12.0f, 0.0f, 50.0f);
-        hero.itemCollector = ItemCollector.HERO;
+        Entity hero = new Entity(Tile.SIZE * 0.84375f, Tile.SIZE * 1.84375f, 
+                new HumanController(), 
+                new DefaultHealth(null, Sound.MIRROR_CRACK, Sound.MIRROR_BREAK), 
+                new DefaultEnergy(),
+                new DefaultMovement(12.0f, 0.0f, 50.0f), 
+                ItemCollector.HERO, 
+                (ent) -> level.scheduleReset(), 
+                "Hero");
+        hero.setPosition(x, y);
         return hero;
     }
     
     public static Entity buildStartScreenHero(Level level, float x, float y)
     {
-        Entity hero = new Entity("Hero", x, y, CollisionBox.createScaledTileBox(0.0f, 0.15625f, 0.84375f, 2.0f));
-        hero.controller = new StartScreenHeroController(hero);
-        hero.health = new DefaultHealth(hero, (ent) -> level.scheduleReset(), 100.0f, null, Sound.MIRROR_CRACK, Sound.MIRROR_BREAK);
-        hero.energy = new DefaultEnergy(hero, 100.0f);
-        hero.move = new DefaultMovement(hero, 12.0f, 0.0f, 50.0f);
-        hero.itemCollector = ItemCollector.HERO;
+        Entity hero = new Entity(Tile.SIZE * 0.84375f, Tile.SIZE * 1.84375f, 
+                new StartScreenHeroController(), 
+                new DefaultHealth(null, Sound.MIRROR_CRACK, Sound.MIRROR_BREAK), 
+                new DefaultEnergy(),
+                new DefaultMovement(12.0f, 0.0f, 50.0f), 
+                ItemCollector.HERO, 
+                (ent) -> level.scheduleReset(), 
+                "Hero");
+        hero.setPosition(x, y);
         return hero;
     }
     
     public static Entity buildLondoner(Level level, float x, float y, boolean evil)
     {
-        Entity londoner = new Entity(evil ? "EvilLondoner" : "Londoner", x, y, CollisionBox.createScaledTileBox(0.21875f, 0.59375f, 0.65625f, 2.0f));
-        londoner.controller = new LondonerController(londoner, evil);
-        londoner.health = new DefaultHealth(londoner, IDeath.NULL, 100.0f, null, null, null);
-        londoner.energy = new DefaultEnergy(londoner, 100.0f);
-        londoner.move = new DefaultMovement(londoner, 3.0f, 0.0f, 50.0f);
+        Entity londoner = new Entity(Tile.SIZE * 0.4375f, Tile.SIZE * 1.40625f, 
+                new LondonerController(evil), 
+                new DefaultHealth(null, null, null),
+                new DefaultEnergy(),
+                new DefaultMovement(3.0f, 0.0f, 50.0f),
+                ItemCollector.NULL,
+                IDespawn.NULL, 
+                evil ? "EvilLondoner" : "Londoner");
+        londoner.setPosition(x, y);
         return londoner;
     }
     
     public static Entity buildCrumblingStone(Level level, float x, float y)
     {
-        Entity stone = new Entity("CrumblingStone", x, y, CollisionBox.DEFAULT_TILE_BOX);
-        stone.controller = new StoneController(stone);
-        stone.move = new StoneMovement(stone);
+        Entity stone = new Entity(Tile.SIZE, Tile.SIZE, 
+                new StoneController(),
+                Health.NULL,
+                Energy.NULL,
+                StoneMovement.STONE,
+                ItemCollector.NULL,
+                IDespawn.NULL, 
+                "CrumblingStone");
+        stone.setPosition(x, y);
         return stone;
     }
     
     public static Entity buildPlatform(Level level, float x, float y, int tx)
     {
-        Entity platform = new Entity("Platform", x, y, CollisionBox.createScaledTileBox(0.03125f, 0.0f, 0.03125f + tx, 0.78125f));
-        platform.controller = new PlatformController(platform);
-        platform.move = new StoneMovement(platform);
+        // render offset: -0.03125, 0
+        Entity platform = new Entity(Tile.SIZE * tx, Tile.SIZE * 0.78125f,
+                new PlatformController(),
+                Health.NULL,
+                Energy.NULL,
+                StoneMovement.STONE,
+                ItemCollector.NULL,
+                IDespawn.NULL,
+                "Platform");
+        platform.setPosition(x, y);
+        platform.setFriction(0.0f, 0.0f);
         return platform;
     }
     

+ 4 - 12
src/me/hammerle/supersnuvi/entity/components/DefaultEnergy.java

@@ -5,23 +5,15 @@ import me.hammerle.supersnuvi.entity.Entity;
 
 public class DefaultEnergy extends Energy
 {
-    private final float maxEnergy;
-    private float energy;
+    private final float maxEnergy = 100.0f;
+    private float energy = maxEnergy;
     
     private static final float BASE = 1.0f / Game.getTicksForMillis(3000);
-    
-    public DefaultEnergy(Entity ent, float maxEnergy) 
-    {
-        super(ent);
-        this.maxEnergy = maxEnergy;
-        this.energy = maxEnergy;
-    }
 
     @Override
-    public void tick() 
+    public void tick(Entity ent) 
     {
-        float mx = ent.getMotionX();
-        
+        float mx = ent.getOwnForceX();
         if(mx == 0.0f)
         {
             addEnergyPercent(BASE);

+ 3 - 24
src/me/hammerle/supersnuvi/entity/components/DefaultHealth.java

@@ -1,32 +1,23 @@
 package me.hammerle.supersnuvi.entity.components;
 
 import me.hammerle.supersnuvi.Game;
-import me.hammerle.supersnuvi.entity.Entity;
-import me.hammerle.supersnuvi.tiles.Tile;
 import me.hammerle.supersnuvi.util.SoundUtils;
 import me.hammerle.supersnuvi.util.SoundUtils.Sound;
 
 public class DefaultHealth extends Health
 {
-    private final float maxHealth;
-    private float health;
+    private final float maxHealth = 100.0f;
+    private float health = maxHealth;
     
     private int hurtTicks = 0;
     private int invincibility = 0;
     
-    private final IDeath death;
-    
     private final Sound soundHeal;
     private final Sound soundHurt;
     private final Sound soundDeath;
     
-    public DefaultHealth(Entity ent, IDeath death, float maxHealth, 
-            Sound soundHeal, Sound soundHurt, Sound soundDeath) 
+    public DefaultHealth(Sound soundHeal, Sound soundHurt, Sound soundDeath) 
     {
-        super(ent);
-        this.death = death;
-        this.maxHealth = maxHealth;
-        this.health = maxHealth;
         this.soundHeal = soundHeal;
         this.soundHurt = soundHurt;
         this.soundDeath = soundDeath;
@@ -56,18 +47,6 @@ public class DefaultHealth extends Health
         return health <= 0.0f;
     }
     
-    @Override
-    public boolean shouldDespawn() 
-    {
-        return (isDead() && !ent.isAnimated());
-    }
-
-    @Override
-    public void onDespawn()
-    {
-        death.onDeath(ent);
-    }
-    
     @Override
     public boolean wasHurt() 
     {

+ 2 - 14
src/me/hammerle/supersnuvi/entity/components/DefaultMovement.java

@@ -11,9 +11,8 @@ public class DefaultMovement extends Movement
     private final float vy;
     private float friction = 1.0f;
     
-    public DefaultMovement(Entity ent, float vx, float vy, float jumpPower) 
+    public DefaultMovement(float vx, float vy, float jumpPower) 
     {
-        super(ent);
         this.jumpPower = jumpPower * Tile.SIZE_SCALE;
         this.vx = vx * Tile.SIZE_SCALE;
         this.vy = vy * Tile.SIZE_SCALE;
@@ -42,17 +41,6 @@ public class DefaultMovement extends Movement
         return vy * getFactor();
     }
 
-    @Override
-    public boolean jump() 
-    {
-        if(ent.isOnGround())
-        {
-            ent.setMotionY((ent.getMotionY() < 0.0f ? ent.getMotionY() : 0.0f) - getJumpPower());
-            return true;
-        }
-        return false;
-    }
-
     @Override
     public float getJumpPower() 
     {
@@ -60,7 +48,7 @@ public class DefaultMovement extends Movement
     }
 
     @Override
-    public boolean hasGravity()
+    public boolean hasGravity(Entity ent)
     {
         return true;
     }

+ 2 - 9
src/me/hammerle/supersnuvi/entity/components/Energy.java

@@ -4,16 +4,9 @@ import me.hammerle.supersnuvi.entity.Entity;
 
 public class Energy
 {
-    public final static Energy NULL = new Energy(null);
+    public final static Energy NULL = new Energy();
     
-    protected final Entity ent;
-    
-    protected Energy(Entity ent)
-    {
-        this.ent = ent;
-    }
-    
-    public void tick()
+    public void tick(Entity ent)
     {
     }
 

+ 2 - 20
src/me/hammerle/supersnuvi/entity/components/Health.java

@@ -1,17 +1,8 @@
 package me.hammerle.supersnuvi.entity.components;
 
-import me.hammerle.supersnuvi.entity.Entity;
-
 public class Health
 {
-    public final static Health NULL = new Health(null);
-    
-    protected final Entity ent;
-    
-    protected Health(Entity ent)
-    {
-        this.ent = ent;
-    }
+    public final static Health NULL = new Health();
     
     public void tick()
     {
@@ -21,16 +12,7 @@ public class Health
     {
         return false;
     }
-    
-    public boolean shouldDespawn()
-    {
-        return false;
-    }
-    
-    public void onDespawn()
-    {
-    }
-    
+
     public boolean wasHurt()
     {
         return false;

+ 0 - 10
src/me/hammerle/supersnuvi/entity/components/IDeath.java

@@ -1,10 +0,0 @@
-package me.hammerle.supersnuvi.entity.components;
-
-import me.hammerle.supersnuvi.entity.Entity;
-
-public interface IDeath
-{
-    public static final IDeath NULL = (Entity ent) -> {};
-    
-    public void onDeath(Entity ent);
-}

+ 10 - 0
src/me/hammerle/supersnuvi/entity/components/IDespawn.java

@@ -0,0 +1,10 @@
+package me.hammerle.supersnuvi.entity.components;
+
+import me.hammerle.supersnuvi.entity.Entity;
+
+public interface IDespawn
+{
+    public static final IDespawn NULL = (Entity ent) -> {};
+    
+    public void onDespawn(Entity ent);
+}

+ 2 - 26
src/me/hammerle/supersnuvi/entity/components/Movement.java

@@ -4,14 +4,7 @@ import me.hammerle.supersnuvi.entity.Entity;
 
 public class Movement
 {
-    public final static Movement NULL = new Movement(null);
-    
-    protected final Entity ent;
-    
-    public Movement(Entity ent) 
-    {
-        this.ent = ent;
-    }
+    public final static Movement NULL = new Movement();
     
     public float getVelocityX()
     {
@@ -28,25 +21,12 @@ public class Movement
         return false;
     }
     
-    //--------------------------------------------------------------------------
-    // jumping
-    //--------------------------------------------------------------------------
-    
-    public boolean jump()
-    {
-        return false;
-    }
-    
     public float getJumpPower()
     {
         return 0.0f;
     }
     
-    //--------------------------------------------------------------------------
-    // gravity
-    //--------------------------------------------------------------------------
-    
-    public boolean hasGravity()
+    public boolean hasGravity(Entity ent)
     {
         return false;
     }
@@ -56,10 +36,6 @@ public class Movement
         return 1.0f;
     }
     
-    //--------------------------------------------------------------------------
-    // friction
-    //--------------------------------------------------------------------------
-    
     public void setInWater(boolean b)
     {
     }

+ 5 - 4
src/me/hammerle/supersnuvi/entity/components/StoneMovement.java

@@ -4,13 +4,14 @@ import me.hammerle.supersnuvi.entity.Entity;
 
 public class StoneMovement extends Movement
 {
-    public StoneMovement(Entity ent) 
+    public final static StoneMovement STONE = new StoneMovement();
+
+    private StoneMovement()
     {
-        super(ent);
     }
-
+    
     @Override
-    public boolean hasGravity() 
+    public boolean hasGravity(Entity ent) 
     {
         return !ent.isAnimated();
     }

+ 6 - 13
src/me/hammerle/supersnuvi/entity/components/ai/Controller.java

@@ -2,25 +2,18 @@ package me.hammerle.supersnuvi.entity.components.ai;
 
 import me.hammerle.supersnuvi.entity.Entity;
 import me.hammerle.supersnuvi.gamelogic.Level;
-import me.hammerle.supersnuvi.tiles.Location;
+import me.hammerle.supersnuvi.tiles.Tile;
 import me.hammerle.supersnuvi.util.Face;
 
 public class Controller
 {
-    public final static Controller NULL = new Controller(null);
+    public final static Controller NULL = new Controller();
     
-    protected final Entity ent;
-    
-    protected Controller(Entity ent)
-    {
-        this.ent = ent;
-    }
-    
-    public void tick(Level level)
+    public void tick(Entity ent, Level level)
     {
     }
     
-    public void renderTick(float lag)
+    public void renderTick(Entity ent, float lag)
     {
     }
     
@@ -29,11 +22,11 @@ public class Controller
         return false;
     }
     
-    public void onCollideWithTile(Location loc, Face face)
+    public void onCollideWithTile(Entity ent, int x, int y, Level l, Tile t, Face face)
     {
     }
     
-    public void onCollideWithEntity(Entity ent, Face face)
+    public void onCollideWithEntity(Entity ent, Entity other, Face face)
     {
     }
 }

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

@@ -16,6 +16,8 @@ public class HumanController extends Controller
     protected final static int SIZE = 1024;
     private final static Texture HERO = new Texture("resources/hero.png");
     
+    private final static float OFFSET_Y = -Tile.SIZE * 0.15625f;
+    
     protected float ox = 0.0f;
     protected float oy = 0.0f;
     protected float w = 0.0f;
@@ -27,11 +29,8 @@ public class HumanController extends Controller
     private int idleCounter = 0;
     protected int idleFrame = 0;
     protected int deathFrame = 0;
-
-    public HumanController(Entity ent) 
-    {
-        super(ent);
-    }
+    
+    protected Face face = Face.RIGHT;
     
     protected void nextWalkFrame()
     {
@@ -73,11 +72,11 @@ public class HumanController extends Controller
     }
     
     @Override
-    public void tick(Level level) 
+    public void tick(Entity ent, Level level) 
     {
         if(ent.getHealth().isDead())
         {
-            ox = ent.getFace() == Face.RIGHT ? 0.0f: -1.15625f * Tile.SIZE;
+            ox = face == Face.RIGHT ? 0.0f: -1.15625f * Tile.SIZE;
             oy = 0.0f;
             h = 2.0f;
             w = 2.0f;
@@ -105,33 +104,33 @@ public class HumanController extends Controller
         
         if(Keys.LEFT.isDown())
         {
-            ent.setMotionX(-speed);
+            ent.applyOwnForce(-speed, 0.0f);
             SoundUtils.playSound(ent.getMovement().isInWater() ? SoundUtils.Sound.WALK_WATER : SoundUtils.Sound.WALK);
         }
         
         if(Keys.RIGHT.isDown())
         {
-            ent.setMotionX(speed);
+            ent.applyOwnForce(speed, 0.0f);
             SoundUtils.playSound(ent.getMovement().isInWater() ? SoundUtils.Sound.WALK_WATER : SoundUtils.Sound.WALK);
         }
         
         if(Keys.JUMP.isDown())
         {
-            if(ent.getEnergy().getEnergyPercent() >= 0.1f && ent.getMovement().jump())
+            if(ent.getEnergy().getEnergyPercent() >= 0.1f && ent.jump())
             {
                 SoundUtils.playSound(Sound.JUMP);
                 ent.getEnergy().addEnergyPercent(-0.1f);
             }
         }
         
-        ox = ent.getFace() == Face.RIGHT ? 0.0f : -0.15625f * Tile.SIZE;
+        ox = face == Face.RIGHT ? 0.0f : -0.15625f * Tile.SIZE;
         oy = 0.0f;
         h = 2.0f;
         w = 1.0f;
         
         if(ent.isOnGround())
         {
-            if(ent.getMotionX() == 0.0f)
+            if(ent.getOwnForceX() == 0.0f)
             {
                 tx = (idleFrame * 32.0f) / SIZE;
                 ty = 128.0f / SIZE;
@@ -150,10 +149,19 @@ public class HumanController extends Controller
             tx = 0.0f;
             ty = 0.0f;
         }
+        
+        if(ent.getOwnForceX() > 0)
+        {
+            face = Face.RIGHT;
+        }
+        else if(ent.getOwnForceX() < 0)
+        {
+            face = Face.LEFT;
+        }
     }
 
     @Override
-    public void renderTick(float lag)
+    public void renderTick(Entity ent, float lag)
     {
         if(ent.getHealth().wasHurt())
         {
@@ -174,7 +182,7 @@ public class HumanController extends Controller
 
         float m1;
         float m2;
-        if(ent.getFace() == Face.LEFT)
+        if(face == Face.LEFT)
         {
             m1 = w * Tile.SIZE;
             m2 = 0.0f;
@@ -185,8 +193,8 @@ public class HumanController extends Controller
             m2 = w * Tile.SIZE;
         }
         Shader.getTextureRenderer().drawRectangle(
-                x + ox + m1, y + oy, 
-                x + ox + m2, y + oy + h * Tile.SIZE, 
+                x + ox + m1, y + oy + OFFSET_Y, 
+                x + ox + m2, y + oy + h * Tile.SIZE + OFFSET_Y, 
                 tx, ty, 
                 tx + (w * 32.0f / SIZE), ty + (h * 32.0f / SIZE));
         

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

@@ -4,7 +4,6 @@ import me.hammerle.snuviengine.api.Shader;
 import me.hammerle.snuviengine.api.Texture;
 import me.hammerle.supersnuvi.entity.Entity;
 import me.hammerle.supersnuvi.gamelogic.Level;
-import me.hammerle.supersnuvi.tiles.Location;
 import me.hammerle.supersnuvi.tiles.RampTile;
 import me.hammerle.supersnuvi.tiles.Tile;
 import me.hammerle.supersnuvi.util.Face;
@@ -15,6 +14,11 @@ public class LondonerController extends Controller
 {
     private final static Texture LONDONER = new Texture("resources/londoner.png");
     
+    // for render offset: 0.21875f, 0.59375f, 0.65625f, 2.0f
+    // render offset: -0.21875, -0.59375
+    private final static float OFFSET_X = -Tile.SIZE * 0.21875f;
+    private final static float OFFSET_Y = -Tile.SIZE * 0.59375f;
+    
     private final boolean evil;
     
     private float ox = 0.0f;
@@ -26,16 +30,16 @@ public class LondonerController extends Controller
     private int deathCounter = 0;
     private int deathFrame = 0;
     
+    private Face oldFace = Face.LEFT;
     private Face direction = Face.LEFT;
     
     private boolean shouldJump = false;
     
     private Entity hurt = null;
     private Entity attacker = null;
-
-    public LondonerController(Entity ent, boolean evil) 
+    
+    public LondonerController(boolean evil) 
     {
-        super(ent);
         this.evil = evil;
     }
     
@@ -51,7 +55,7 @@ public class LondonerController extends Controller
     }
     
     @Override
-    public void tick(Level level) 
+    public void tick(Entity ent, Level level) 
     {
         if(evil)
         {
@@ -59,7 +63,7 @@ public class LondonerController extends Controller
             {
                 if(transformFrame < 3)
                 {
-                    ox = ent.getFace() == Face.RIGHT ? 0.0f: -1.125f * Tile.SIZE;
+                    ox = direction == Face.RIGHT ? 0.0f: -1.125f * Tile.SIZE;
                     tx = transformFrame * 0.125f + 0.125f;
                     ty = 0.375f;
                     transformFrame++;
@@ -74,7 +78,7 @@ public class LondonerController extends Controller
         
         if(ent.getHealth().isDead())
         {
-            ox = ent.getFace() == Face.RIGHT ? -Tile.SIZE: -0.125f * Tile.SIZE;
+            ox = direction == Face.RIGHT ? -Tile.SIZE: -0.125f * Tile.SIZE;
             
             tx = deathFrame * 0.125f;
             ty = hasRedEyes() ? 0.125f : 0.25f;
@@ -94,36 +98,36 @@ public class LondonerController extends Controller
             float hx = level.getHero().getCenterX();
             if(hx < ent.getCenterX())
             {
-                ent.setMotionX(-ent.getMovement().getVelocityX());
+                ent.applyOwnForce(-ent.getMovement().getVelocityX(), 0.0f);
             }
             else
             {
-                ent.setMotionX(ent.getMovement().getVelocityX());
+                ent.applyOwnForce(ent.getMovement().getVelocityX(), 0.0f);
             }
         }
         else
         {
             if(direction == Face.LEFT)
             {
-                ent.setMotionX(-ent.getMovement().getVelocityX());
+                ent.applyOwnForce(-ent.getMovement().getVelocityX(), 0.0f);
             }
             else
             {
-                ent.setMotionX(ent.getMovement().getVelocityX());
+                ent.applyOwnForce(ent.getMovement().getVelocityX(), 0.0f);
             }
         }
         
         if(shouldJump)
         {
             SoundUtils.playSound(SoundUtils.Sound.LONDONER_JUMP);
-            ent.getMovement().jump();
+            ent.jump();
             shouldJump = false;
         }
         
         if(attacker != null)
         {
-            this.ent.getHealth().addHealthPercent(-0.201f);
-            attacker.setMotionY(-attacker.getMovement().getJumpPower() * 0.5f);
+            ent.getHealth().addHealthPercent(-0.201f);
+            attacker.applyForce(0.0f, -attacker.getMovement().getJumpPower() * 0.5f);
             
             attacker = null;
             hurt = null;
@@ -133,25 +137,25 @@ public class LondonerController extends Controller
         {
             hurt.getHealth().addHealthPercent(-0.1f);
 
-            hurt.setMotionY(ent.getMotionY() - 20.0f * Tile.SIZE_SCALE);
+            hurt.applyForce(0.0f, -20.0f * Tile.SIZE_SCALE);
 
             if(ent.getCenterX() < hurt.getCenterX())
             {
-                hurt.setMotionX(hurt.getMotionX() + 10.0f * Tile.SIZE_SCALE);
+                hurt.applyForce(10.0f * Tile.SIZE_SCALE, 0.0f);
             }
             else
             {
-                hurt.setMotionX(hurt.getMotionX() - 10.0f * Tile.SIZE_SCALE);
+                hurt.applyForce(-10.0f * Tile.SIZE_SCALE, 0.0f);
             }
             
             hurt = null;
         }
         
-        ox = ent.getFace() == Face.RIGHT ? 0.0f: -1.125f * Tile.SIZE;
+        ox = direction == Face.RIGHT ? 0.0f: -1.125f * Tile.SIZE;
         
         if(ent.isOnGround())
         {
-            if(ent.getMotionX() == 0.0f)
+            if(ent.getOwnForceX() == 0.0f)
             {
                 tx = 0.875f;
                 ty = hasRedEyes() ? 0.25f : 0.375f;
@@ -171,10 +175,12 @@ public class LondonerController extends Controller
             tx = 0.125f;
             ty = hasRedEyes() ? 0.5f : 0.625f;
         }
+        
+        oldFace = direction;
     }
 
     @Override
-    public void renderTick(float lag)
+    public void renderTick(Entity ent, float lag)
     {
         if(ent.getHealth().wasHurt())
         {
@@ -195,7 +201,7 @@ public class LondonerController extends Controller
         
         float m1;
         float m2;
-        if(ent.getFace() == Face.LEFT)
+        if(oldFace == Face.LEFT)
         {
             m1 = 2.0f * Tile.SIZE;
             m2 = 0.0f;
@@ -206,8 +212,8 @@ public class LondonerController extends Controller
             m2 = 2.0f * Tile.SIZE;
         }
         Shader.getTextureRenderer().drawRectangle(
-                x + ox + m1, y,
-                x + ox + m2, y + 2.0f * Tile.SIZE, 
+                x + ox + m1 + OFFSET_X, y + OFFSET_Y,
+                x + ox + m2 + OFFSET_X, y + 2.0f * Tile.SIZE + OFFSET_Y, 
                 tx, ty, 
                 tx + 0.125f, ty + 0.125f);
         
@@ -219,18 +225,22 @@ public class LondonerController extends Controller
     }
     
     @Override
-    public void onCollideWithTile(Location loc, Face face)
+    public void onCollideWithTile(Entity ent, int x, int y, Level l, Tile t, Face face)
     {
+        if(ent.getHealth().isDead())
+        {
+            return;
+        }
         if(hasRedEyes())
         {
-            if(!(loc.getTile() instanceof RampTile) && face == ent.getFace() && ent.getMotionX() == 0.0f)
+            if(!(t instanceof RampTile) && face == direction && ent.getOwnForceX() == 0.0f)
             {
                 shouldJump = true;
             }
         }
         else
         {
-            if(loc.getTile().shouldAiUseCollisionBox(loc.getX(), loc.getY(), loc.getLevel()))
+            if(t.shouldAiUseCollisionBox(x, y, l))
             {
                 switch(face)
                 {
@@ -246,8 +256,12 @@ public class LondonerController extends Controller
     }
 
     @Override
-    public void onCollideWithEntity(Entity ent, Face face)
+    public void onCollideWithEntity(Entity ent, Entity other, Face face)
     {
+        if(ent.getHealth().isDead())
+        {
+            return;
+        }
         switch(face)
         {
             case LEFT:

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

@@ -34,11 +34,6 @@ public class PlatformController extends Controller
     private final ArrayList<MoveData> dataList = new ArrayList<>();
     private int moveIndex = 0;
     private int waitTicks = 0;
-
-    public PlatformController(Entity ent) 
-    {
-        super(ent);
-    }
   
     @Override
     public boolean isAnimated()
@@ -70,7 +65,7 @@ public class PlatformController extends Controller
     }
     
     @Override
-    public void tick(Level level) 
+    public void tick(Entity ent, Level level) 
     {
         if(dataList.isEmpty())
         {
@@ -80,9 +75,6 @@ public class PlatformController extends Controller
         MoveData data = dataList.get(moveIndex);
         if(ent.isAt(data.x, data.y))
         {
-            ent.setMotionX(0.0f);
-            ent.setMotionY(0.0f);
-            
             waitTicks++;
             if(waitTicks >= data.waitTicks)
             {
@@ -94,13 +86,12 @@ public class PlatformController extends Controller
         {
             float motionX = clamp(data.x - ent.getX(), -data.speedX, data.speedX);
             float motionY = clamp(data.y - ent.getY(), -data.speedY, data.speedY);
-            ent.setMotionX(motionX);
-            ent.setMotionY(motionY);
+            ent.applyOwnForce(motionX, motionY);
         }
     }
 
     @Override
-    public void renderTick(float lag)
+    public void renderTick(Entity ent, float lag)
     {
         TEXTURE.bind();
         float x = Utils.interpolate(ent.getLastX(), ent.getX(), lag);
@@ -118,4 +109,17 @@ public class PlatformController extends Controller
         }
         Shader.getTextureRenderer().drawRectangle(x, y, x + Tile.SIZE, endY, 0.5f, 0.25f, 0.75f, 0.4453125f);
     }
+
+    @Override
+    public void onCollideWithEntity(Entity ent, Entity other, Face face)
+    {
+        if(face == Face.UP)
+        {
+            other.applyForce(ent.getOwnForceX(), 0.0f);
+        }
+        else if(face == Face.LEFT || face == Face.RIGHT)
+        {
+            other.applyForce(ent.getOwnForceX() - other.getOwnForceX(), 0.0f);
+        }
+    }
 }

+ 7 - 13
src/me/hammerle/supersnuvi/entity/components/ai/StartScreenHeroController.java

@@ -2,7 +2,6 @@ package me.hammerle.supersnuvi.entity.components.ai;
 
 import me.hammerle.supersnuvi.entity.Entity;
 import me.hammerle.supersnuvi.gamelogic.Level;
-import me.hammerle.supersnuvi.tiles.Location;
 import me.hammerle.supersnuvi.tiles.RampTile;
 import me.hammerle.supersnuvi.tiles.Tile;
 import me.hammerle.supersnuvi.util.Face;
@@ -11,30 +10,25 @@ public class StartScreenHeroController extends HumanController
 {
     private boolean shouldJump = false;
     
-    public StartScreenHeroController(Entity ent) 
-    {
-        super(ent);
-    }
-    
     @Override
-    public void tick(Level level) 
+    public void tick(Entity ent, Level level) 
     {
-        ent.setMotionX(ent.getMovement().getVelocityX());
+        ent.applyOwnForce(ent.getMovement().getVelocityX(), 0.0f);
         
         if(shouldJump)
         {
             shouldJump = false;
-            ent.getMovement().jump();
+            ent.jump();
         }
         
-        ox = ent.getFace() == Face.RIGHT ? 0.0f : -0.15625f * Tile.SIZE;
+        ox = 0.0f;
         oy = 0.0f;
         h = 2.0f;
         w = 1.0f;
         
         if(ent.isOnGround())
         {
-            if(ent.getMotionX() == 0.0f)
+            if(ent.getOwnForceX() == 0.0f)
             {
                 tx = (idleFrame * 32.0f) / SIZE;
                 ty = 128.0f / SIZE;
@@ -56,9 +50,9 @@ public class StartScreenHeroController extends HumanController
     }
 
     @Override
-    public void onCollideWithTile(Location loc, Face face)
+    public void onCollideWithTile(Entity ent, int x, int y, Level l, Tile t, Face face)
     {
-        if(face == Face.RIGHT && !(loc.getTile() instanceof RampTile))
+        if(face == Face.RIGHT && !(t instanceof RampTile))
         {
             shouldJump = true;
         }

+ 2 - 7
src/me/hammerle/supersnuvi/entity/components/ai/StoneController.java

@@ -12,11 +12,6 @@ public class StoneController extends Controller
     private final static Texture STONE = new Texture("resources/stone.png");
     
     private int frame = 0;
-    
-    public StoneController(Entity ent)
-    {
-        super(ent);
-    }
 
     @Override
     public boolean isAnimated()
@@ -25,7 +20,7 @@ public class StoneController extends Controller
     }
 
     @Override
-    public void renderTick(float lag)
+    public void renderTick(Entity ent, float lag)
     {
         STONE.bind();
         float x = Utils.interpolate(ent.getLastX(), ent.getX(), lag);
@@ -39,7 +34,7 @@ public class StoneController extends Controller
     }
 
     @Override
-    public void tick(Level level)
+    public void tick(Entity ent, Level level)
     {
         if(frame < 40)
         {

+ 14 - 8
src/me/hammerle/supersnuvi/gamelogic/Level.java

@@ -1,10 +1,10 @@
 package me.hammerle.supersnuvi.gamelogic;
 
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.function.Consumer;
-import java.util.stream.Collectors;
 import me.hammerle.snuviscript.code.Script;
 import me.hammerle.supersnuvi.Game;
 import me.hammerle.supersnuvi.entity.Entity;
@@ -32,7 +32,7 @@ public abstract class Level
     private float red = 1.0f;
     private float green = 1.0f;
     private float blue = 1.0f;
-    private Light[] lights = new Light[32];
+    private final Light[] lights = new Light[32];
     
     public Level()
     {
@@ -124,9 +124,9 @@ public abstract class Level
         entities.values().removeIf(ent -> 
         {
             ent.tick(this);
-            if(ent.getHealth().shouldDespawn() || (ent.getY() > getData().getHeight() * Tile.SIZE))
+            if((ent.getHealth().isDead() && !ent.isAnimated()) || (ent.getY() > getData().getHeight() * Tile.SIZE))
             {
-                ent.getHealth().onDespawn();
+                ent.onDespawn();
                 callEvent("entity_despawn", (sc) -> 
                 {
                     sc.setVar("entity", ent);
@@ -135,6 +135,7 @@ public abstract class Level
             }
             return false;
         });
+        entities.values().forEach(ent -> ent.move(this));
 
         if(!spawnQueue.isEmpty())
         {
@@ -150,9 +151,13 @@ public abstract class Level
         }
     }
     
-    public final List<Entity> getEntitiesCollidingWith(Entity not, CollisionObject cb)
+    public final void forEachCollidingEntity(Entity ent, Consumer<Entity> c)
     {
-        return entities.values().stream().filter(ent -> ent != not && ent.getBox().isColliding(cb)).collect(Collectors.toList());
+        float minX = ent.getX() - Entity.STEP;
+        float minY = ent.getY() - Entity.STEP;
+        float maxX = minX + ent.getWidth() + Entity.STEP;
+        float maxY = minY + ent.getHeight() + Entity.STEP;
+        entities.values().stream().filter(other -> ent != other && other.isColliding(minX, minY, maxX, maxY)).forEach(c);
     }
     
     public final void forEachEntity(Consumer<Entity> c)
@@ -221,7 +226,7 @@ public abstract class Level
     // collision
     // -------------------------------------------------------------------------
     
-    private Tile getInteractionTile(int x, int y)
+    public Tile getInteractionTile(int x, int y)
     {
         int i = getData().getInteractionTile(x, y);
         if(i == -1)
@@ -246,7 +251,8 @@ public abstract class Level
         List<CollisionObject> boxes;
         if(not != null)
         {
-            boxes = getEntitiesCollidingWith(not, box).stream().map(ent -> ent.getBox()).collect(Collectors.toList());
+            //boxes = getEntitiesCollidingWith(not, box).stream().map(ent -> ent.getBox()).collect(Collectors.toList());
+            boxes = new LinkedList<>();
         }
         else
         {

+ 7 - 0
src/me/hammerle/supersnuvi/math/IVector.java

@@ -0,0 +1,7 @@
+package me.hammerle.supersnuvi.math;
+
+public interface IVector
+{
+    public float getX();
+    public float getY();
+}

+ 84 - 0
src/me/hammerle/supersnuvi/math/Vector.java

@@ -0,0 +1,84 @@
+package me.hammerle.supersnuvi.math;
+
+public class Vector implements IVector
+{
+    private float x = 0.0f;
+    private float y = 0.0f;
+    
+    public Vector(float x, float y)
+    {
+        this.x = x;
+        this.y = y;
+    }
+    
+    public Vector()
+    {
+        this(0.0f, 0.0f);
+    }
+    
+    public void setX(float x)
+    {
+        this.x = x;
+    }
+    
+    public void setY(float y)
+    {
+        this.y = y;
+    }
+    
+    public void set(float x, float y)
+    {
+        this.x = x;
+        this.y = y;
+    }
+    
+    public void set(Vector v)
+    {
+        set(v.x, v.y);
+    }
+    
+    public void add(float x, float y)
+    {
+        this.x += x;
+        this.y += y;
+    }
+    
+    public void add(Vector v)
+    {
+        add(v.x, v.y);
+    }
+    
+    public void addY(float y)
+    {
+        this.y += y;
+    }
+    
+    public void addX(float x)
+    {
+        this.x += x;
+    }
+    
+    public void mul(Vector v)
+    {
+        x *= v.x;
+        y *= v.y;
+    }
+
+    @Override
+    public float getX()
+    {
+        return x;
+    }
+
+    @Override
+    public float getY()
+    {
+        return y;
+    }
+
+    @Override
+    public String toString()
+    {
+        return String.format("Vector(x = %f, y = %f)", x, y);
+    }
+}

+ 32 - 0
src/me/hammerle/supersnuvi/tiles/BaseBoxTile.java

@@ -1,5 +1,9 @@
 package me.hammerle.supersnuvi.tiles;
 
+import me.hammerle.supersnuvi.gamelogic.Level;
+import me.hammerle.supersnuvi.util.Face;
+import me.hammerle.supersnuvi.util.Utils;
+
 public class BaseBoxTile extends BaseTile
 {
     public BaseBoxTile(float tMinX, float tMinY, float tMaxX, float tMaxY)
@@ -7,4 +11,32 @@ public class BaseBoxTile extends BaseTile
         super(tMinX, tMinY, tMaxX, tMaxY);
         super.setDefaultCollisionBox();
     }
+
+    @Override
+    public boolean isMoveColliding(float minX, float minY, float maxX, float maxY, int x, int y, Level l)
+    {
+        float minTileX = Utils.toCoord(x);
+        float minTileY = Utils.toCoord(y);
+        return maxX > minTileX && minTileX + Tile.SIZE > minX && 
+                maxY > minTileY && minTileY + Tile.SIZE > minY;
+    }
+
+    @Override
+    public boolean isColliding(float minX, float minY, float maxX, float maxY, int x, int y, Level l)
+    {
+        float minTileX = Utils.toCoord(x);
+        float minTileY = Utils.toCoord(y);
+        return maxX > minTileX && minTileX + Tile.SIZE > minX && 
+                maxY > minTileY && minTileY + Tile.SIZE > minY;
+    }
+
+    @Override
+    public Face getCollidingFace(float minX, float minY, float maxX, float maxY, int x, int y, Level l)
+    {
+        float minTileX = Utils.toCoord(x);
+        float minTileY = Utils.toCoord(y);
+        return Utils.getCollidingFace(
+                minTileX, minTileY, minTileX + Tile.SIZE, minTileY + Tile.SIZE,
+                minX, minY, maxX, maxY);
+    }
 }

+ 41 - 0
src/me/hammerle/supersnuvi/tiles/BaseCollisionTile.java

@@ -0,0 +1,41 @@
+package me.hammerle.supersnuvi.tiles;
+
+import me.hammerle.supersnuvi.gamelogic.Level;
+import me.hammerle.supersnuvi.util.Face;
+import me.hammerle.supersnuvi.util.Utils;
+
+public class BaseCollisionTile extends BaseTile
+{
+    private final float x1;
+    private final float y1;
+    private final float x2;
+    private final float y2;
+    
+    public BaseCollisionTile(float tMinX, float tMinY, float tMaxX, float tMaxY, float x1, float y1, float x2, float y2)
+    {
+        super(tMinX, tMinY, tMaxX, tMaxY);
+        this.x1 = x1;
+        this.y1 = y1;
+        this.x2 = x2;
+        this.y2 = y2;
+    }
+    
+    @Override
+    public boolean isColliding(float minX, float minY, float maxX, float maxY, int x, int y, Level l)
+    {
+        float minTileX = Utils.toCoord(x);
+        float minTileY = Utils.toCoord(y);
+        return maxX > minTileX + x1 && minTileX + x2 > minX && 
+                maxY > minTileY + y1 && minTileY + y2 > minY;
+    }
+
+    @Override
+    public Face getCollidingFace(float minX, float minY, float maxX, float maxY, int x, int y, Level l)
+    {
+        float minTileX = Utils.toCoord(x);
+        float minTileY = Utils.toCoord(y);
+        return Utils.getCollidingFace(
+                minTileX + x1, minTileY + y1, minTileX + x2, minTileY + y2,
+                minX, minY, maxX, maxY);
+    }
+}

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

@@ -8,14 +8,15 @@ import me.hammerle.supersnuvi.util.Face;
 import me.hammerle.supersnuvi.util.SoundUtils;
 import me.hammerle.supersnuvi.gamelogic.Level;
 
-public class BottledSoulTile extends BaseTile
+public class BottledSoulTile extends BaseCollisionTile
 {
     private final BlockDataStorage states = new BlockDataStorage();
     private final int score;
     
     public BottledSoulTile(int score)
     {
-        super(0.0625f * (score - 1), 0.0625f, 0.0625f * score, 0.125f);
+        super(0.0625f * (score - 1), 0.0625f, 0.0625f * score, 0.125f,
+                Tile.SIZE * 0.2f, Tile.SIZE * 0.2f, Tile.SIZE * 0.8f, Tile.SIZE * 0.8f);
         super.setCollisionBox(CollisionBox.createScaledTileBox(0.2f, 0.2f, 0.8f, 0.8f));
         this.score = score;
     }

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

@@ -10,13 +10,13 @@ import me.hammerle.supersnuvi.util.SoundUtils;
 import me.hammerle.supersnuvi.util.Utils;
 import me.hammerle.supersnuvi.gamelogic.Level;
 
-public class CrumblingStoneTile extends BaseTile
+public class CrumblingStoneTile extends BaseCollisionTile
 {
     private final BlockDataStorage states = new BlockDataStorage();
     
     public CrumblingStoneTile() 
     {
-        super(0.1875f, 0.125f, 0.25f, 0.1875f);
+        super(0.1875f, 0.125f, 0.25f, 0.1875f, 0.0f, 0.0f, Tile.SIZE, Tile.SIZE * 0.6f);
         super.setCollisionBox(CollisionBox.createScaledTileBox(0.0f, 0.0f, 1.0f, 0.6f));
         super.setMovementBox(CollisionBox.createScaledTileBox(0.0f, 0.0f, 1.0f, 0.6f));
     }

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

@@ -5,11 +5,11 @@ import me.hammerle.supersnuvi.util.CollisionBox;
 import me.hammerle.supersnuvi.util.Face;
 import me.hammerle.supersnuvi.gamelogic.Level;
 
-public class GoalTile extends BaseTile
+public class GoalTile extends BaseCollisionTile
 {
     public GoalTile(float tMinX, float tMinY, float tMaxX, float tMaxY) 
     {
-        super(tMinX, tMinY, tMaxX, tMaxY);
+        super(tMinX, tMinY, tMaxX, tMaxY, Tile.SIZE * 0.1f, Tile.SIZE * 0.1f, Tile.SIZE * 0.9f, Tile.SIZE * 0.9f);
         super.setCollisionBox(CollisionBox.createScaledTileBox(0.1f, 0.1f, 0.9f, 0.9f));
     }
 

+ 3 - 3
src/me/hammerle/supersnuvi/tiles/InteractTile.java

@@ -6,11 +6,11 @@ import me.hammerle.supersnuvi.util.CollisionObject;
 import me.hammerle.supersnuvi.util.Face;
 import me.hammerle.supersnuvi.gamelogic.Level;
 
-public class InteractTile extends BaseTile
+public class InteractTile extends BaseCollisionTile
 {
     public InteractTile(float tMinX, float tMinY, float tMaxX, float tMaxY)
     {
-        super(tMinX, tMinY, tMaxX, tMaxY);
+        super(tMinX, tMinY, tMaxX, tMaxY, 0.0f, 0.0f, Tile.SIZE, Tile.SIZE);
         super.setCollisionBox(CollisionObject.DEFAULT_TILE_BOX);
         super.setMovementBox(CollisionObject.NULL_BOX);
     }
@@ -24,7 +24,7 @@ public class InteractTile extends BaseTile
     @Override
     public void onEntityCollide(Entity ent, int x, int y, Face face, Level l)
     {
-        if(ent.getItemCollector().isHero() && Keys.UP.getTime() == 1 && face == Face.DOWN)
+        if(ent.getItemCollector().isHero() && Keys.UP.getTime() == 1)
         {
             l.callEvent("tile_interact", (sc) -> 
             {

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

@@ -5,11 +5,11 @@ import me.hammerle.supersnuvi.util.CollisionBox;
 import me.hammerle.supersnuvi.util.Face;
 import me.hammerle.supersnuvi.gamelogic.Level;
 
-public class KillTile extends BaseTile
+public class KillTile extends BaseCollisionTile
 {
     public KillTile(float tMinX, float tMinY, float tMaxX, float tMaxY) 
     {
-        super(tMinX, tMinY, tMaxX, tMaxY);
+        super(tMinX, tMinY, tMaxX, tMaxY, Tile.SIZE * 0.1f, Tile.SIZE * 0.1f, Tile.SIZE * 0.9f, Tile.SIZE * 0.9f);
         super.setCollisionBox(CollisionBox.createScaledTileBox(0.1f, 0.1f, 0.9f, 0.9f));
     }
 

+ 3 - 3
src/me/hammerle/supersnuvi/tiles/PressureTile.java

@@ -6,14 +6,14 @@ import me.hammerle.supersnuvi.util.CollisionObject;
 import me.hammerle.supersnuvi.util.Face;
 import me.hammerle.supersnuvi.gamelogic.Level;
 
-public class PressureTile extends BaseTile
+public class PressureTile extends BaseCollisionTile
 {
     public PressureTile(float tMinX, float tMinY, float tMaxX, float tMaxY)
     {
-        super(tMinX, tMinY, tMaxX, tMaxY);
+        super(tMinX, tMinY, tMaxX, tMaxY,
+                Tile.SIZE * 0.09375f, Tile.SIZE * 0.90625f, Tile.SIZE * 0.90625f, Tile.SIZE);
         super.setCollisionBox(CollisionBox.createScaledTileBox(0.09375f, 0.90625f, 0.90625f, 1.0f));
         super.setMovementBox(CollisionObject.NULL_BOX);
-
     }
     
     @Override

+ 24 - 0
src/me/hammerle/supersnuvi/tiles/RampTile.java

@@ -1,13 +1,24 @@
 package me.hammerle.supersnuvi.tiles;
 
+import me.hammerle.supersnuvi.entity.Entity;
 import me.hammerle.supersnuvi.util.CollisionLine;
 import me.hammerle.supersnuvi.gamelogic.Level;
+import me.hammerle.supersnuvi.util.Utils;
 
 public class RampTile extends BaseTile
 {
+    private final float lx1;
+    private final float ly1;
+    private final float lx2;
+    private final float ly2;
+    
     public RampTile(float tMinX, float fMinY, float tMaxX, float tMaxY, float x1, float y1, float x2, float y2)
     {
         super(tMinX, fMinY, tMaxX, tMaxY);
+        lx1 = x1;
+        ly1 = y1;
+        lx2 = x2;
+        ly2 = y2;
         
         CollisionLine line = new CollisionLine(x1, y1, x2, y2);
         super.setCollisionBox(line);
@@ -19,4 +30,17 @@ public class RampTile extends BaseTile
     {
         return false;
     }
+
+    @Override
+    public boolean isMoveColliding(float minX, float minY, float maxX, float maxY, int x, int y, Level l)
+    {
+        float x1 = Utils.toCoord(x) + lx1;
+        float y1 = Utils.toCoord(y) + ly1;
+        float x2 = Utils.toCoord(x) + lx2;
+        float y2 = Utils.toCoord(y) + ly2;
+        return CollisionLine.intersect(x1, y1, x2, y2, minX, minY, maxX, minY) || 
+                CollisionLine.intersect(x1, y1, x2, y2, maxX, minY, maxX, maxY) ||
+                CollisionLine.intersect(x1, y1, x2, y2, maxX, maxY, minX, maxY) ||
+                CollisionLine.intersect(x1, y1, x2, y2, minX, maxY, minX, minY);
+    }
 }

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

@@ -14,13 +14,13 @@ public class SlipperyTile extends BaseBoxTile
     @Override
     public void onEntityCollide(Entity ent, int x, int y, Face face, Level l) 
     {
-        if(face == Face.UP)
+        /*if(face == Face.UP)
         {
             if(ent.getPreMotionY() != Entity.GRAVITY && Math.abs(ent.getMotionX()) <= 0.07)
             {
                 ent.setMotionX(ent.getFace() == Face.RIGHT ? 5.0f : -5.0f);
             }
             ent.getMovement().setFrictionFactor(0.85f);
-        }
+        }*/
     }
 }

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

@@ -7,7 +7,7 @@ import me.hammerle.supersnuvi.util.CollisionBox;
 import me.hammerle.supersnuvi.util.Face;
 import me.hammerle.supersnuvi.gamelogic.Level;
 
-public class SpikeTile extends BaseTile
+public class SpikeTile extends BaseCollisionTile
 {
     private final static Texture.Animation SPIKES = LevelRenderer.TILES.addAnimation(96, 32, 
             "resources/spike_trap/spike_trap_frame1.png", "resources/spike_trap/spike_trap_frame2.png",
@@ -19,7 +19,7 @@ public class SpikeTile extends BaseTile
     
     public SpikeTile() 
     {
-        super(0.1875f, 0.0625f, 0.25f, 0.125f);
+        super(0.1875f, 0.0625f, 0.25f, 0.125f, Tile.SIZE * 0.1f, Tile.SIZE * 0.1f, Tile.SIZE * 0.9f, Tile.SIZE * 0.9f);
         counter = 0;
         frame = 0;
         super.setCollisionBox(CollisionBox.createScaledTileBox(0.1f, 0.1f, 0.9f, 0.9f));

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

@@ -155,4 +155,19 @@ public abstract class Tile
     {
         return true;
     }
+    
+    public boolean isMoveColliding(float minX, float minY, float maxX, float maxY, int x, int y, Level l)
+    {
+        return false;
+    }
+    
+    public boolean isColliding(float minX, float minY, float maxX, float maxY, int x, int y, Level l)
+    {
+        return false;
+    }
+    
+    public Face getCollidingFace(float minX, float minY, float maxX, float maxY, int x, int y, Level l)
+    {
+        return Face.NULL;
+    }
 }

+ 1 - 1
src/me/hammerle/supersnuvi/tiles/TrampolinTile.java

@@ -17,7 +17,7 @@ public class TrampolinTile extends BaseBoxTile
     {
         if(face == Face.UP)
         {
-            ent.setMotionY(-ent.getMovement().getJumpPower());
+            ent.applyForce(0.0f, -ent.getMovement().getJumpPower());
             SoundUtils.playSound(SoundUtils.Sound.JUMP_ON_BOUNCE_SHROOM);
         }
     }

+ 4 - 4
src/me/hammerle/supersnuvi/tiles/WaterTile.java

@@ -32,10 +32,10 @@ public class WaterTile extends BaseTile
     {
         super.onEntityCollide(ent, x, y, face, l);
         ent.getMovement().setInWater(true);
-        if(ent.getMotionY() > 0.0f)
-        {
-            ent.setMotionY(0.0f);
-        }
+        //if(ent.getMotionY() > 0.0f)
+        //{
+        //    ent.setMotionY(0.0f);
+        //}
     }
 
     @Override

+ 4 - 3
src/me/hammerle/supersnuvi/util/Face.java

@@ -4,13 +4,14 @@ import me.hammerle.supersnuvi.entity.Entity;
 
 public enum Face 
 {
+    NULL(0, 0),
     LEFT(-Entity.STEP, 0),
     RIGHT(Entity.STEP, 0),
     UP(0, -Entity.STEP),
     DOWN(0, Entity.STEP);
     
-    private float offsetX;
-    private float offsetY;
+    private final float offsetX;
+    private final float offsetY;
     
     Face(float offsetX, float offsetY)
     {
@@ -27,7 +28,7 @@ public enum Face
             case UP: return DOWN;
             case DOWN: return UP;
         }
-        return UP;
+        return NULL;
     }
     
     public float getCollisionOffsetX()

+ 40 - 0
src/me/hammerle/supersnuvi/util/Utils.java

@@ -23,4 +23,44 @@ public class Utils
     {
         return (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2);
     }
+    
+    public static Face getCollidingFace(
+            float xMin1, float yMin1, float xMax1, float yMax1, 
+            float xMin2, float yMin2, float xMax2, float yMax2)
+    {
+        float diffUp = yMax1 - yMin2;
+        float diffRight = xMax2 - xMin1;
+        float diffDown = yMax2 - yMin1;
+        float diffLeft = xMax1 - xMin2;
+        
+        float min = diffUp;
+        
+        Face f = Face.UP;
+        if(min > diffRight)
+        {
+            min = diffRight;
+            f = Face.RIGHT;
+        }
+        if(min > diffDown)
+        {
+            min = diffDown;
+            f = Face.DOWN;
+        }
+        if(min > diffLeft)
+        {
+            f = Face.LEFT;
+        }
+        
+        int c = 0;
+        c += (min == diffUp) ? 1 : 0;
+        c += (min == diffRight) ? 1 : 0;
+        c += (min == diffDown) ? 1 : 0;
+        c += (min == diffLeft) ? 1 : 0;
+        if(c >= 2)
+        {
+            return Face.NULL;
+        }
+        
+        return f;
+    }
 }