package stray.entity; import stray.Main; import stray.blocks.Block.BlockFaces; import stray.blocks.BlockEmpty; import stray.blocks.BlockPlatform; import stray.util.Coordinate; import stray.util.EntityMover; import stray.util.MathHelper; import stray.util.Sizeable; import stray.world.World; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.graphics.Color; import com.badlogic.gdx.graphics.Texture; public abstract class Entity implements EntityMover, Sizeable { public transient World world; public float x = 0; public float y = 0; public float sizex = 1; public float sizey = 1; /** * collides with blocks */ public boolean hasBlockCollision = true; /** * collides with other entities with this flag as true */ public boolean hasEntityCollision = false; /** * indicates that entities that hit this other entity will shift * velocity*forceTransfer to this entity (and stop) */ public float forceTransfer = 0f; public float dragCoefficient = 1; public float gravityCoefficient = 1; public float bounceCoefficient = 0; public float velox = 0; // speed blocks/sec public float veloy = 0; // speed blocks/sec public float accspeed = 1.5f; // acceleration blocks/sec public float maxspeed = 1.5f; // speed cap blocks/sec private Color tint = getColor(); public Entity(World w, float posx, float posy) { world = w; x = posx; y = posy; prepare(); } /** * called on creation */ public abstract void prepare(); public void render(float delta) { renderSelf((x * World.tilesizex) - world.camera.camerax, Main.convertY((y * World.tilesizey) - world.camera.cameray)); } /** * coordinates are the top-left corner of x, y * * @param x * @param y */ public abstract void renderSelf(float x, float y); protected void renderTextureCentered(Texture tex, float width, float height) { float rx = ((width + (sizex / 2f)) * World.tilesizex) - world.camera.camerax; float ry = ((height + (sizey / 2f)) * World.tilesizey) - world.camera.cameray; rx -= width / 2f; ry -= height / 2f; world.batch.draw(tex, rx, Main.convertY(ry + (sizey * World.tilesizey))); } protected void renderTextureCentered(Texture tex) { renderTextureCentered(tex, tex.getWidth(), tex.getHeight()); } /** * called every render update BEFORE rendering */ public void renderUpdate() { } /** * * @return true to stop death */ public boolean onDeath() { return false; } /** * called every tick, before rendering */ public void tickUpdate() { float drag = world.drag * getLowestDrag() * dragCoefficient; if (velox > 0) { velox -= drag / Main.TICKS; if (velox < 0) velox = 0; } else if (velox < 0) { velox += drag / Main.TICKS; if (velox > 0) velox = 0; } if (getBlockCollidingDown() == null && getEntityCollidingDown() == null) { veloy += (world.gravity / Main.TICKS) * gravityCoefficient; } if (veloy != 0) { int velo = (int) (veloy / Main.TICKS * World.tilesizey); if (velo > 0) { Coordinate c = getBlockCollidingDown(); Entity en = getEntityCollidingDown(); if (c != null) { veloy = -veloy * bounceCoefficient; velo = 0; onCollideDown(); world.getBlock(c.getX(), c.getY()).onCollideUpFace(world, c.getX(), c.getY(), this); } else if (en != null) { onCollideDown(); onCollideEntityDown(en); velo = 0; float delta = Math.abs(this.veloy - en.veloy); en.veloy += delta * en.forceTransfer; this.veloy -= delta * en.forceTransfer; veloy = -veloy * bounceCoefficient; } } else if (velo < 0) { Coordinate c = getBlockCollidingUp(); Entity en = getEntityCollidingUp(); if (c != null) { veloy = -veloy * bounceCoefficient; velo = 0; onCollideUp(); world.getBlock(c.getX(), c.getY()).onCollideDownFace(world, c.getX(), c.getY(), this); } else if (en != null) { onCollideUp(); onCollideEntityUp(en); velo = 0; float delta = Math.abs(this.veloy - en.veloy); en.veloy += delta * en.forceTransfer; this.veloy -= delta * en.forceTransfer; veloy = -veloy * bounceCoefficient; } } for (int i = 0; i < Math.abs(velo); i++) { if (velo > 0) { y += World.tileparty; Coordinate c = getBlockCollidingDown(); Entity en = getEntityCollidingDown(); if (c != null) { veloy = -veloy * bounceCoefficient; onCollideDown(); world.getBlock(c.getX(), c.getY()).onCollideUpFace(world, c.getX(), c.getY(), this); break; } else if (en != null) { onCollideDown(); onCollideEntityDown(en); float delta = Math.abs(this.veloy - en.veloy); en.veloy += delta * en.forceTransfer; this.veloy -= delta * en.forceTransfer; veloy = -veloy * bounceCoefficient; break; } } else if (velo < 0) { y -= World.tileparty; Coordinate c = getBlockCollidingUp(); Entity en = getEntityCollidingUp(); if (c != null) { veloy = -veloy * bounceCoefficient; onCollideUp(); world.getBlock(c.getX(), c.getY()).onCollideDownFace(world, c.getX(), c.getY(), this); break; } else if (en != null) { onCollideUp(); onCollideEntityUp(en); float delta = Math.abs(this.veloy - en.veloy); en.veloy += delta * en.forceTransfer; this.veloy -= delta * en.forceTransfer; veloy = -veloy * bounceCoefficient; break; } } } } if (velox != 0) { int velo = (int) (velox / Main.TICKS * World.tilesizex); if (velo > 0) { Coordinate c = getBlockCollidingRight(); Entity en = getEntityCollidingRight(); if (c != null) { velox = -velox * bounceCoefficient; velo = 0; onCollideRight(); world.getBlock(c.getX(), c.getY()).onCollideLeftFace(world, c.getX(), c.getY(), this); } else if (en != null) { onCollideRight(); onCollideEntityRight(en); velo = 0; float delta = this.velox - en.velox; en.velox += delta * en.forceTransfer; this.velox -= delta * en.forceTransfer; velox = -velox * bounceCoefficient; } } else if (velo < 0) { Coordinate c = getBlockCollidingLeft(); Entity en = getEntityCollidingLeft(); if (c != null) { velox = -velox * bounceCoefficient; velo = 0; onCollideLeft(); world.getBlock(c.getX(), c.getY()).onCollideRightFace(world, c.getX(), c.getY(), this); } else if (en != null) { onCollideLeft(); onCollideEntityLeft(en); velo = 0; float delta = this.velox - en.velox; en.velox += delta * en.forceTransfer; this.velox -= delta * en.forceTransfer; velox = -velox * bounceCoefficient; } } for (int i = 0; i < Math.abs(velo); i++) { if (velo > 0) { x += World.tilepartx; Coordinate c = getBlockCollidingRight(); Entity en = getEntityCollidingRight(); if (c != null) { velox = -velox * bounceCoefficient; onCollideRight(); world.getBlock(c.getX(), c.getY()).onCollideLeftFace(world, c.getX(), c.getY(), this); break; } else if (en != null) { onCollideRight(); onCollideEntityRight(en); float delta = this.velox - en.velox; en.velox += delta * en.forceTransfer; this.velox -= delta * en.forceTransfer; velox = -velox * bounceCoefficient; break; } } else if (velo < 0) { x -= World.tilepartx; Coordinate c = getBlockCollidingLeft(); Entity en = getEntityCollidingLeft(); if (c != null) { velox = -velox * bounceCoefficient; onCollideLeft(); world.getBlock(c.getX(), c.getY()).onCollideRightFace(world, c.getX(), c.getY(), this); break; } else if (en != null) { onCollideLeft(); onCollideEntityLeft(en); float delta = this.velox - en.velox; en.velox += delta * en.forceTransfer; this.velox -= delta * en.forceTransfer; velox = -velox * bounceCoefficient; break; } } } } } public boolean intersectingOther(Entity other) { return (MathHelper.intersects(x, y, sizex, sizey, other.x, other.y, other.sizex, other.sizey)); } @Override public float getCurrentX() { return x; } @Override public float getCurrentY() { return y; } @Override public float getX() { return x; } @Override public float getY() { return y; } @Override public float getWidth() { return sizex; } @Override public float getHeight() { return sizey; } /** * called AFTER velocity has been altered after collision */ public void onCollideLeft() { } /** * called AFTER velocity has been altered after collision */ public void onCollideRight() { } /** * called AFTER velocity has been altered after collision */ public void onCollideUp() { } public void onCollideDown() { } public void onCollideEntityLeft(Entity e) { } public void onCollideEntityRight(Entity e) { } public void onCollideEntityUp(Entity e) { } public void onCollideEntityDown(Entity e) { } public boolean isDead() { return false; } public Coordinate collidingAnyBlock() { Coordinate c = null; c = getBlockCollidingUp(); if (c != null) return c; c = getBlockCollidingDown(); if (c != null) return c; c = getBlockCollidingLeft(); if (c != null) return c; c = getBlockCollidingRight(); if (c != null) return c; return null; } public Entity getEntityCollidingUp() { for (int i = 0; i < world.entities.size; i++) { Entity e = world.entities.get(i); if (e != this && e.hasEntityCollision) { if (((int) (x * World.tilesizex + sizex * World.tilesizex)) > ((int) (e.x * World.tilesizex)) && ((int) (x * World.tilesizex)) < ((int) (e.x * World.tilesizex + e.sizex * World.tilesizex))) { if (((int) (this.y * World.tilesizey)) == ((int) (e.y * World.tilesizey + e.sizey * World.tilesizey))) { return e; } } } } return null; } public Entity getEntityCollidingDown() { for (int i = 0; i < world.entities.size; i++) { Entity e = world.entities.get(i); if (e != this && e.hasEntityCollision) { if (((int) (x * World.tilesizex + sizex * World.tilesizex)) > ((int) (e.x * World.tilesizex)) && ((int) (x * World.tilesizex)) < ((int) (e.x * World.tilesizex + e.sizex * World.tilesizex))) { if (((int) (e.y * World.tilesizey)) == ((int) (this.y * World.tilesizey + this.sizey * World.tilesizey))) { return e; } } } } return null; } public Entity getEntityCollidingLeft() { for (int i = 0; i < world.entities.size; i++) { Entity e = world.entities.get(i); if (e != this && e.hasEntityCollision) { if (((int) (y * World.tilesizey + sizey * World.tilesizey)) > ((int) (e.y * World.tilesizey)) && ((int) (y * World.tilesizey)) < ((int) (e.y * World.tilesizey + e.sizey * World.tilesizey))) { if (((int) (this.x * World.tilesizex)) == ((int) (e.x * World.tilesizex + e.sizex * World.tilesizex))) { return e; } } } } return null; } public Entity getEntityCollidingRight() { for (int i = 0; i < world.entities.size; i++) { Entity e = world.entities.get(i); if (e != this && e.hasEntityCollision) { if (((int) (y * World.tilesizey + sizey * World.tilesizey)) > ((int) (e.y * World.tilesizey)) && ((int) (y * World.tilesizey)) < ((int) (e.y * World.tilesizey + e.sizey * World.tilesizey))) { if (((int) (e.x * World.tilesizex)) == ((int) (this.x * World.tilesizex + this.sizex * World.tilesizex))) { return e; } } } } return null; } public Coordinate getBlockCollidingUp() { int posx = (int) (x * World.tilesizex); int posy = (int) (y * World.tilesizey); int boundx = (int) (sizex * World.tilesizex); int boundy = (int) (sizey * World.tilesizey); if (posy % World.tilesizey != 0) return null; if (y <= 0) return Coordinate.global.setPosition((int) x, (int) y); for (int i = 0; i < boundx; i++) { if ((world.getBlock((posx + i) / World.tilesizex, (posy / World.tilesizey) - 1) .isSolid(world, (posx + i) / World.tilesizex, (posy / World.tilesizey) - 1) & BlockFaces.DOWN) == BlockFaces.DOWN) { return Coordinate.global.setPosition((posx + i) / World.tilesizex, (posy / World.tilesizey) - 1); } } return null; } public Coordinate getBlockCollidingDown() { int posx = (int) (x * World.tilesizex); int posy = (int) (y * World.tilesizey); int boundx = (int) (sizex * World.tilesizex); int boundy = (int) (sizey * World.tilesizey); if ((posy + boundy) % World.tilesizey != 0) return null; if ((y + sizey) >= world.sizey) return Coordinate.global.setPosition((int) x, (int) y); for (int i = 0; i < boundx; i++) { if ((world.getBlock((posx + i) / World.tilesizex, ((posy + boundy) / World.tilesizey)) .isSolid(world, (posx + i) / World.tilesizex, ((posy + boundy) / World.tilesizey)) & BlockFaces.UP) == BlockFaces.UP) { return Coordinate.global.setPosition((posx + i) / World.tilesizex, ((posy + boundy) / World.tilesizey)); } } return null; } public float getLowestDrag() { if (getBlockCollidingDown() == null) return BlockEmpty.DRAG; int posx = (int) (x * World.tilesizex); int posy = (int) (y * World.tilesizey); int boundx = (int) (sizex * World.tilesizex); int boundy = (int) (sizey * World.tilesizey); if ((posy + boundy) % World.tilesizey != 0) return 1; if ((y + sizey) >= world.sizey) return 1; float lowest = 1; for (int i = 0; i < boundx; i++) { if ((world.getBlock((posx + i) / World.tilesizex, ((posy + boundy) / World.tilesizey)) .isSolid(world, (posx + i) / World.tilesizex, ((posy + boundy) / World.tilesizey)) & BlockFaces.UP) == BlockFaces.UP) { if (world.getBlock((posx + i) / World.tilesizex, ((posy + boundy) / World.tilesizey)).getDragCoefficient(world, (posx + i) / World.tilesizex, ((posy + boundy) / World.tilesizey)) < lowest) { lowest = world.getBlock((posx + i) / World.tilesizex, ((posy + boundy) / World.tilesizey)).getDragCoefficient(world, (posx + i) / World.tilesizex, ((posy + boundy) / World.tilesizey)); } } } return lowest; } public float getHighestDrag() { if (getBlockCollidingDown() == null) return BlockEmpty.DRAG; int posx = (int) (x * World.tilesizex); int posy = (int) (y * World.tilesizey); int boundx = (int) (sizex * World.tilesizex); int boundy = (int) (sizey * World.tilesizey); if ((posy + boundy) % World.tilesizey != 0) return 1; if ((y + sizey) >= world.sizey) return 1; float highest = Float.MIN_NORMAL; for (int i = 0; i < boundx; i++) { if ((world.getBlock((posx + i) / World.tilesizex, ((posy + boundy) / World.tilesizey)) .isSolid(world, (posx + i) / World.tilesizex, ((posy + boundy) / World.tilesizey)) & BlockFaces.UP) == BlockFaces.UP) { if (world.getBlock((posx + i) / World.tilesizex, ((posy + boundy) / World.tilesizey)).getDragCoefficient(world, (posx + i) / World.tilesizex, ((posy + boundy) / World.tilesizey)) > highest) { highest = world.getBlock((posx + i) / World.tilesizex, ((posy + boundy) / World.tilesizey)).getDragCoefficient(world, (posx + i) / World.tilesizex, ((posy + boundy) / World.tilesizey)); } } } return highest; } public Coordinate getBlockCollidingLeft() { int posx = (int) (x * World.tilesizex); int posy = (int) (y * World.tilesizey); int boundx = (int) (sizex * World.tilesizex); int boundy = (int) (sizey * World.tilesizey); if (posx % World.tilesizex != 0) return null; if (posx <= 0) return Coordinate.global.setPosition((int) x, (int) y); for (int i = 0; i < boundy; i++) { if ((world.getBlock(((posx) / World.tilesizex) - 1, ((posy + i) / World.tilesizey)) .isSolid(world, ((posx) / World.tilesizex) - 1, ((posy + i) / World.tilesizey)) & BlockFaces.RIGHT) == BlockFaces.RIGHT) { return Coordinate.global.setPosition(((posx) / World.tilesizex) - 1, ((posy + i) / World.tilesizey)); } } return null; } public Coordinate getBlockCollidingRight() { int posx = (int) (x * World.tilesizex); int posy = (int) (y * World.tilesizey); int boundx = (int) (sizex * World.tilesizex); int boundy = (int) (sizey * World.tilesizey); if ((posx + boundx) % World.tilesizex != 0) return null; if ((x + sizex) >= (world.sizex)) return Coordinate.global.setPosition((int) x, (int) y); for (int i = 0; i < boundy; i++) { if ((world .getBlock(((posx + boundx) / World.tilesizex), ((posy + i) / World.tilesizey)) .isSolid(world, ((posx + boundx) / World.tilesizex), ((posy + i) / World.tilesizey)) & BlockFaces.LEFT) == BlockFaces.LEFT) { return Coordinate.global.setPosition(((posx + boundx) / World.tilesizex), ((posy + i) / World.tilesizey)); } } return null; } public Entity entityColliding() { if (!hasEntityCollision) return null; for (Entity e : world.entities) { if (intersectingOther(e)) { if (!e.hasEntityCollision) continue; if (e == this) continue; return e; } } return null; } public Color getColor() { if (tint == null) { tint = new Color(Main.getRandom().nextFloat(), Main.getRandom().nextFloat(), Main .getRandom().nextFloat(), 1); } return tint; } public void accelerate(float x, float y, boolean limitSpeed) { if (x > 0) { velox += (x + (world.drag * Gdx.graphics.getDeltaTime())) * Math.max(World.tilepartx, Math.abs(getHighestDrag())); if (limitSpeed) if (velox > maxspeed) velox = maxspeed; } else if (x < 0) { velox += (x - (world.drag * Gdx.graphics.getDeltaTime())) * Math.max(World.tilepartx, Math.abs(getHighestDrag())); if (limitSpeed) if (velox < -maxspeed) velox = -maxspeed; } if (y > 0) { veloy += y + (world.drag * Gdx.graphics.getDeltaTime()); // if (dragcalc) if (veloy > maxspeed) veloy = maxspeed; } else if (y < 0) { veloy += y - (world.drag * Gdx.graphics.getDeltaTime()); // if (dragcalc) if (veloy < -maxspeed) veloy = -maxspeed; } } public void accelerate(float x, float y) { accelerate(x, y, false); } public void moveUp() { if (getBlockCollidingUp() == null && veloy > -maxspeed) { accelerate(0, -accspeed * Gdx.graphics.getDeltaTime(), true); } } public void moveDown() { if (getBlockCollidingDown() == null && veloy < maxspeed) { accelerate(0, accspeed * Gdx.graphics.getDeltaTime(), true); } } public void moveLeft() { if (getBlockCollidingLeft() == null && velox > -maxspeed) { accelerate(-accspeed * Gdx.graphics.getDeltaTime(), 0, true); } } public void moveRight() { if (getBlockCollidingRight() == null && velox < maxspeed) { accelerate(accspeed * Gdx.graphics.getDeltaTime(), 0, true); } } }