package me.hammerle.supersnuvi.entity; import java.util.LinkedList; import javafx.scene.image.Image; import javafx.scene.paint.Color; import me.hammerle.supersnuvi.gamelogic.Level; import me.hammerle.supersnuvi.input.IKeyHandler; import me.hammerle.supersnuvi.rendering.GameRenderer; import me.hammerle.supersnuvi.tiles.Location; import me.hammerle.supersnuvi.util.CollisionBox; import me.hammerle.supersnuvi.util.Face; import me.hammerle.supersnuvi.util.Utils; public class Entity { // 1 Tile = 1 m // Pixel m s Pixel // ----- * --- * ------ = ------- // m s Tick Tick // added correction factor, real life gravity seems to strong public static double GRAVITY = GameRenderer.TILE_SIZE * 9.81 / 60 * 0.1; private double prevPosX; private double prevPrevY; private double posX; private double posY; private double prevMotionX; private double prevMotionY; private double motionX; private double motionY; private boolean onGround; private boolean inWater; private final CollisionBox collisionBox; private final double width; private final double height; private Image image; private final Level level; public Entity(Level level, double x, double y, double width, double height) { this.level = level; this.posX = x; this.posY = y; this.prevPosX = x; this.prevPrevY = y; this.motionX = 0; this.motionY = 0; this.onGround = false; this.inWater = false; this.collisionBox = new CollisionBox(0, 0, width, height); this.width = width; this.height = height; image = Utils.getColoredImage(Color.BLACK, (int) width, (int) height); } public Level getLevel() { return level; } //-------------------------------------------------------------------------- // ticking //-------------------------------------------------------------------------- public void controlTick(IKeyHandler keyHandler) { } public final void tick() { if(isAffectedByGravity()) { motionY -= GRAVITY; } prevMotionX = motionX; prevMotionY = motionY; prevPosX = posX; prevPrevY = posY; setAllowedMotion(); posX += motionX; posY += motionY; motionX = Utils.round(motionX); motionY = Utils.round(motionY); if(motionY != 0) { onGround = false; } posX = Utils.round(posX); posY = Utils.round(posY); if(!onGround) { motionX *= 0.7; } LinkedList list = level.getMovementBoxesAt(this, getBox()); if(!list.isEmpty()) { System.out.println(getBox()); list.forEach(s -> System.out.println(s)); System.out.println("WELL"); posX = prevPosX; posY = prevPrevY; } doCollision(); } //-------------------------------------------------------------------------- // gravity, friction //-------------------------------------------------------------------------- public final boolean jump() { if(onGround) { onGround = false; motionY += getJumpPower(); return true; } return false; } public boolean isAffectedByGravity() { return true; } public double getJumpPower() { return 0; } public boolean isOnGround() { return onGround; } public final double getMaxSpeedModifier() { double max = 1; if(inWater) { max *= 0.65; } return max; } public final void setInWater(boolean b) { inWater = b; } public final boolean isInWater() { return inWater; } public final void resetMovementData() { inWater = false; } //-------------------------------------------------------------------------- // collision stuff //-------------------------------------------------------------------------- public final CollisionBox getBox() { return collisionBox.reset().offset(posX, posY); } private void setAllowedMotion() { if(motionX == 0 && motionY == 0) { return; } double dirX = motionX; double dirY = motionY; double startX = posX; double startY = posY; double endX = startX + dirX; double endY = startY + dirY; CollisionBox eBox = getBox(); double halfWidth = eBox.getWidth() / 2; double halfHeight = eBox.getHeight() / 2; eBox = eBox.copy(); startX += halfWidth; startY += halfHeight; endX += halfWidth; endY += halfHeight; // expanding area by the size of the entity CollisionBox expandedBox = eBox.copy().expand(motionX, motionY); LinkedList allBoxes = level.getMovementBoxesAt(this, expandedBox); if(allBoxes.isEmpty()) { return; } double x; double y; char lastWall = 0; if(dirX >= 0) { if(dirY <= 0) // Right - Down { for(CollisionBox box : allBoxes) { box.grow(halfWidth, halfHeight); if(endX <= box.getMinX() || endY >= box.getMaxY() || startX >= box.getMaxX() || startY <= box.getMinY()) { continue; } x = Utils.interpolateX(startX, startY, endX, endY, box.getMaxY()); y = Utils.interpolateY(startX, startY, endX, endY, box.getMinX()); if(y > box.getMinY() && y < box.getMaxY()) { endX = box.getMinX(); endY = y; lastWall = 'x'; } else if(x > box.getMinX() && x < box.getMaxX()) { endY = box.getMaxY(); endX = x; lastWall = 'y'; } else if(x >= box.getMinX() && x <= box.getMaxX()) { lastWall = 'y'; } } if(lastWall == 'x') { double lineEndY = startY + dirY; for(CollisionBox box : allBoxes) { if(endX <= box.getMinX() || lineEndY >= box.getMaxY() || endX >= box.getMaxX() || endY <= box.getMinY()) { continue; } lineEndY = box.getMaxY(); } endY = lineEndY; } else if(lastWall == 'y') { double lineEndX = startX + dirX; for(CollisionBox box : allBoxes) { if(lineEndX <= box.getMinX() || endY >= box.getMaxY() || endX >= box.getMaxX() || endY <= box.getMinY()) { continue; } lineEndX = box.getMinX(); } endX = lineEndX; } } else // Right - Up { for(CollisionBox box : allBoxes) { box.grow(halfWidth, halfHeight); if(endX <= box.getMinX() || endY <= box.getMinY() || startX >= box.getMaxX() || startY >= box.getMaxY()) { continue; } x = Utils.interpolateX(startX, startY, endX, endY, box.getMinY()); y = Utils.interpolateY(startX, startY, endX, endY, box.getMinX()); if(y > box.getMinY() && y < box.getMaxY()) { endX = box.getMinX(); endY = y; lastWall = 'x'; } else if(x > box.getMinX() && x < box.getMaxX()) { endY = box.getMinY(); endX = x; lastWall = 'y'; } else if(x >= box.getMinX() && x <= box.getMaxX()) { lastWall = 'y'; } } if(lastWall == 'x') { double lineEndY = startY + dirY; for(CollisionBox box : allBoxes) { if(endX <= box.getMinX() || lineEndY <= box.getMinY() || endX >= box.getMaxX() || endY >= box.getMaxY()) { continue; } lineEndY = box.getMinY(); } endY = lineEndY; } else if(lastWall == 'y') { double lineEndX = startX + dirX; for(CollisionBox box : allBoxes) { if(lineEndX <= box.getMinX() || endY >= box.getMaxY() || endX >= box.getMaxX() || endY <= box.getMinY()) { continue; } lineEndX = box.getMinX(); } endX = lineEndX; } } } else { if(dirY <= 0) // Left - Down { for(CollisionBox box : allBoxes) { box.grow(halfWidth, halfHeight); if(endX >= box.getMaxX() || endY >= box.getMaxY() || startX <= box.getMinX() || startY <= box.getMinY()) { continue; } x = Utils.interpolateX(startX, startY, endX, endY, box.getMaxY()); y = Utils.interpolateY(startX, startY, endX, endY, box.getMaxX()); if(y > box.getMinY() && y < box.getMaxY()) { endX = box.getMaxX(); endY = y; lastWall = 'x'; } else if(x > box.getMinX() && x < box.getMaxX()) { endY = box.getMaxY(); endX = x; lastWall = 'y'; } else if(x >= box.getMinX() && x <= box.getMaxX()) { lastWall = 'y'; } } if(lastWall == 'x') { double lineEndY = startY + dirY; for(CollisionBox box : allBoxes) { if(endX <= box.getMinX() || lineEndY >= box.getMaxY() || endX >= box.getMaxX() || endY <= box.getMinY()) { continue; } lineEndY = box.getMaxY(); } endY = lineEndY; } else if(lastWall == 'y') { double lineEndX = startX + dirX; for(CollisionBox box : allBoxes) { if(lineEndX >= box.getMaxX() || endY >= box.getMaxY() || endX <= box.getMinX() || endY <= box.getMinY()) { continue; } lineEndX = box.getMaxX(); } endX = lineEndX; } } else // Left - Up { for(CollisionBox box : allBoxes) { box.grow(halfWidth, halfHeight); if(endX >= box.getMaxX() || endY <= box.getMinY() || startX <= box.getMinX() || startY >= box.getMaxY()) { continue; } x = Utils.interpolateX(startX, startY, endX, endY, box.getMinY()); y = Utils.interpolateY(startX, startY, endX, endY, box.getMaxX()); if(y > box.getMinY() && y < box.getMaxY()) { endX = box.getMaxX(); endY = y; lastWall = 'x'; } else if(x > box.getMinX() && x < box.getMaxX()) { endY = box.getMinY(); endX = x; lastWall = 'y'; } else if(x >= box.getMinX() && x <= box.getMaxX()) { lastWall = 'y'; } } if(lastWall == 'x') { double lineEndY = startY + dirY; for(CollisionBox box : allBoxes) { if(endX <= box.getMinX() || lineEndY <= box.getMinY() || endX >= box.getMaxX() || endY >= box.getMaxY()) { continue; } lineEndY = box.getMinY(); } endY = lineEndY; } else if(lastWall == 'y') { double lineEndX = startX + dirX; for(CollisionBox box : allBoxes) { if(lineEndX >= box.getMaxX() || endY >= box.getMaxY() || endX <= box.getMinX() || endY <= box.getMinY()) { continue; } lineEndX = box.getMaxX(); } endX = lineEndX; } } } motionX = endX - startX; motionY = endY - startY; if(dirY < 0 && motionY == 0) { onGround = true; } if(Math.abs(motionY) >= 1000 || Math.abs(motionX) >= 1000) { System.err.println("illegal movement " + motionX + " " + motionY); System.exit(0); } /*if(!onGround && Math.abs(dirY) - Math.abs(motionY) > 1) { System.out.println("\nVORHER " + dirX + " " + dirY); System.out.println("NACHHER " + motionX + " " + motionY); System.out.println("\nPOS " + posX + " " + posY); System.exit(0); }*/ //System.exit(0); } private void doCollision() { CollisionBox box = getBox().copy(); for(Face f : Face.values()) { box.reset(); box.expand(f.getCollisionOffsetX(), f.getCollisionOffsetY()); level.getEntitiesCollidingWith(this, box).forEach(ent -> this.onCollideWithEntity(ent, f)); level.getTilesCollidingWith(box).forEach(loc -> { this.onCollideWithTile(loc, f); loc.getTile().onEntityCollide(this, loc.getX(), loc.getY(), f); }); } } public void onCollideWithTile(Location loc, Face face) { } public void onCollideWithEntity(Entity ent, Face face) { } //-------------------------------------------------------------------------- // basic coord and motion stuff //-------------------------------------------------------------------------- public final double getX() { return posX; } public final double getY() { return posY; } public final double getPreviousX() { return prevPosX; } public final double getPreviousY() { return prevPrevY; } public boolean isMoving() { return motionX != 0.0 || motionY != 0.0; } public final double getMotionX() { return motionX; } public final void setMotionX(double motionX) { prevMotionX = this.motionX; this.motionX = motionX; } public final double getMotionY() { return motionY; } public final void setMotionY(double motionY) { prevMotionY = this.motionY; this.motionY = motionY; } public final double getPreviousMotionX() { return prevMotionX; } public final double getPreviousMotionY() { return prevMotionY; } //-------------------------------------------------------------------------- // rendering stuff //-------------------------------------------------------------------------- public final double getWidth() { return width; } public final double getHeight() { return height; } public final double getRenderX() { return posX; } public final double getRenderY() { return posY + height; } public void setImage(Image image) { this.image = image; } public boolean drawImageFlipped() { return false; } public Image getImage() { return image; } public double getRenderOffsetX() { return 0; } public double getRenderOffsetY() { return 0; } }