123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651 |
- package me.hammerle.supersnuvi.entity;
- 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.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.015625f;
- // this one is a little bit bigger to prevent wrong calculation
- // while going upwards
- public static final float UP_STEP = STEP + 0.00390625f;
-
- // the last position is used for interpolation during rendering
- private final Vector lastPos = new Vector();
- // the current position of the entity
- 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 final motion of the last tick
- private final Vector lastMotion = 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 baseFriction = new Vector(0.0f, 1.0f);
- 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
- 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;
- private Face direction = Face.NULL;
-
- protected Entity(float width, float height, Controller c, Health h, Energy e, Movement m, ItemCollector ic, IDespawn d, String type)
- {
- 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;
- }
-
- public String getType()
- {
- return type;
- }
-
- //--------------------------------------------------------------------------
- // components
- //--------------------------------------------------------------------------
-
- public boolean isAnimated()
- {
- return controller.isAnimated();
- }
-
- public Controller getController()
- {
- return controller;
- }
-
- public Health getHealth()
- {
- return health;
- }
-
- public Energy getEnergy()
- {
- return energy;
- }
-
- public Movement getMovement()
- {
- return move;
- }
-
- public ItemCollector getItemCollector()
- {
- return itemCollector;
- }
-
- public void onDespawn()
- {
- onDespawn.onDespawn(this);
- }
-
- //--------------------------------------------------------------------------
- // basic stuff
- //--------------------------------------------------------------------------
-
- public float getSquaredDistance(Entity e)
- {
- return Utils.getSquaredDistance(getCenterX(), getCenterY(), e.getCenterX(), e.getCenterY());
- }
-
- public float getX()
- {
- return pos.getX();
- }
-
- public float getY()
- {
- return pos.getY();
- }
-
- public void setPosition(float x, float y)
- {
- lastPos.set(x, y);
- pos.set(x, y);
- }
-
- public float getLastX()
- {
- return lastPos.getX();
- }
-
- public float getLastY()
- {
- return lastPos.getY();
- }
-
- public float getCenterX()
- {
- return pos.getX() + width * 0.5f;
- }
-
- public float getCenterY()
- {
- return pos.getY() + height * 0.5f;
- }
-
- public float getWidth()
- {
- return width;
- }
-
- public float getHeight()
- {
- return height;
- }
-
- public float getMotionX()
- {
- return motion.getX();
- }
-
- public float getMotionY()
- {
- return motion.getY();
- }
-
- public float getOwnForceX()
- {
- return ownForce.getX();
- }
-
- public float getOwnForceY()
- {
- return ownForce.getY();
- }
- public void applyOwnForce(float x, float y)
- {
- ownForce.add(x, y);
- }
-
- public void applyForce(float x, float y)
- {
- motion.add(x, y);
- }
-
- public void setBaseFriction(float fx, float fy)
- {
- baseFriction.set(fx, fy);
- }
-
- public Vector getFriction()
- {
- return friction;
- }
-
- public boolean isAt(float x, float y)
- {
- return Math.abs(x - pos.getX()) < STEP && Math.abs(y - pos.getY()) < STEP;
- }
-
- public boolean isColliding(float minX, float minY, float maxX, float maxY)
- {
- return maxX > getX() && getX() + width > minX && maxY > getY() && getY() + height > minY;
- }
-
- public boolean isColliding(Entity ent)
- {
- return isColliding(ent.getX(), ent.getY(), ent.getX() + ent.width, ent.getY() + ent.height);
- }
-
- private Face getCollidingFace(Entity o)
- {
- return Utils.getCollidingFace(
- o.getX(), o.getY(), o.getX() + o.getWidth(), o.getY() + o.getHeight(),
- getX(), getY(), getX() + getWidth(), getY() + getHeight());
- }
-
- public boolean jump()
- {
- if(onGround)
- {
- ownForce.addY(-move.getJumpPower());
- return true;
- }
- return false;
- }
-
- public Face getDirection()
- {
- return direction;
- }
-
- //--------------------------------------------------------------------------
- // ticking
- //--------------------------------------------------------------------------
-
- public void tick(Level level)
- {
- // last motion is current motion, last motion will be set in move
- motion.set(lastMotion);
- lastMotion.set(0.0f, 0.0f);
-
- // reset own force
- ownForce.set(0.0f, 0.0f);
-
- // update onGround before controller tick
- onGround = isOnTileGround(level);
- if(onGround)
- {
- if(!move.canMoveOnTiles())
- {
- motion.setY(0.0f);
- }
- }
- else
- {
- friction.set(0.2f, 1.0f);
- }
-
- // tick components
- controller.tick(this, level);
- energy.tick(this);
- health.tick();
-
- // apply gravity if needed
- if(move.hasGravity(this))
- {
- motion.addY(GRAVITY * move.getGravityFactor());
- }
-
- // apply friction
- motion.mul(friction);
- if(move.isInWater())
- {
- motion.mul(friction);
- move.setInWater(false);
- }
- friction.set(baseFriction);
-
- // modify current motion by own force
- motion.add(ownForce);
-
- if(ownForce.getX() < 0.0f)
- {
- direction = Face.LEFT;
- }
- else if(ownForce.getX() > 0.0f)
- {
- direction = Face.RIGHT;
- }
- }
-
- public void tickCollision(Level level)
- {
- // (entity <-> tile) collision
- doTileCollision(level);
- // (entity <-> entity) collision
- level.forEachCollidingEntity(this, (ent) ->
- {
- controller.onCollideWithEntity(this, ent, getCollidingFace(ent));
- });
-
- slideOnEdge(level);
-
- // remeber last position for rendering
- lastPos.set(pos);
- }
-
- public int move(Level level)
- {
- if(motion.getX() == 0.0f && motion.getY() == 0.0f)
- {
- return 0;
- }
- Vector maxMotion = new Vector();
- maxMotion.set(motion);
-
- if(!move.canMoveOnEntities() || !move.canMoveOnTiles())
- {
- reduceMotion(level);
- }
- pos.add(motion);
-
- int r = (motion.getX() == 0.0f && motion.getY() == 0.0f) ? 0 : 1;
-
- lastMotion.add(motion);
- maxMotion.sub(motion);
- motion.set(maxMotion);
-
- return r;
- }
-
- 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;
- }
- }
- }
-
- for(Entity ent : level.getEntities())
- {
- if(ent == this)
- {
- continue;
- }
- if(ent.getMovement().isSolid() && ent.isColliding(minX, minY, maxX, maxY))
- {
- 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 boolean isCollidingWithTiles(int startX, int startY, int endX, int endY, Level level)
- {
- if(move.canMoveOnTiles())
- {
- return false;
- }
- 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;
- }
-
- private boolean isCollidingWithEntities(Level level)
- {
- if(move.canMoveOnEntities())
- {
- return false;
- }
- float minX = pos.getX();
- float minY = pos.getY();
- float maxX = pos.getX() + width;
- float maxY = pos.getY() + height;
- for(Entity ent : level.getEntities())
- {
- if(ent == this)
- {
- continue;
- }
- if(ent.isColliding(minX, minY, maxX, maxY) && (move.isSolid() || ent.move.isSolid()))
- {
- return true;
- }
- }
- for(Entity ent : level.getEntitiesInQueue())
- {
- if(ent == this)
- {
- continue;
- }
- if(ent.isColliding(minX, minY, maxX, maxY) && (move.isSolid() || ent.move.isSolid()))
- {
- return true;
- }
- }
- return false;
- }
- public void reduceMotion(Level level)
- {
- if(motion.getX() == 0.0f && motion.getY() == 0.0f)
- {
- return;
- }
-
- float mx = motion.getX();
- float my = motion.getY();
-
- int startX;
- int endX;
- if(mx < 0.0f)
- {
- startX = Utils.toBlock(pos.getX() + mx);
- endX = Utils.toBlock(pos.getX() + width);
- }
- else
- {
- 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 oldX = pos.getX();
- if(mx < 0.0)
- {
- if(mx > -STEP)
- {
- pos.addX(mx);
- mx = 0.0f;
- }
- else
- {
- pos.addX(-STEP);
- mx += STEP;
- }
- }
- else if(mx > 0.0)
- {
- if(mx < STEP)
- {
- pos.addX(mx);
- mx = 0.0f;
- }
- else
- {
- pos.addX(STEP);
- mx -= STEP;
- }
- }
- if(isCollidingWithTiles(startX, startY, endX, endY, level) || isCollidingWithEntities(level))
- {
- pos.addY(-UP_STEP);
- if(isCollidingWithTiles(startX, startY, endX, endY, level) || isCollidingWithEntities(level))
- {
- pos.addY(UP_STEP);
- pos.setX(oldX);
- mx = 0.0f;
- }
- }
- }
- if(my != 0.0f)
- {
- float oldY = pos.getY();
- if(my < 0.0)
- {
- 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;
- }
- }
- if(isCollidingWithTiles(startX, startY, endX, endY, level) || isCollidingWithEntities(level))
- {
- my = 0.0f;
- pos.setY(oldY);
- }
- }
- }
- motion.set(pos.getX() - oldPosX, pos.getY() - oldPosY);
- pos.set(oldPosX, oldPosY);
- }
-
- private void slideOnEdge(Level level)
- {
- float minY = pos.getY() + height;
- float maxY = pos.getY() + height + STEP * 2;
-
- float minX = pos.getX();
- float midX = pos.getX() + width * 0.5f;
- float maxX = pos.getX() + width;
-
- int startX = Utils.toBlock(minX);
- int startY = Utils.toBlock(minY);
- int endX = Utils.toBlock(maxX);
- int endY = Utils.toBlock(maxY);
-
- int leftEdges = 0;
- int rightEdges = 0;
- 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, midX, maxY, x, y, level))
- {
- rightEdges++;
- }
- if(t.isMoveColliding(midX, minY, maxX, maxY, x, y, level))
- {
- leftEdges++;
- }
- }
- }
-
- if(leftEdges == 0)
- {
- applyForce(STEP * 25, 0.0f);
- }
- if(rightEdges == 0)
- {
- applyForce(-STEP * 25, 0.0f);
- }
- }
-
- public void renderTick(float lag)
- {
- controller.renderTick(this, lag);
- }
-
- //--------------------------------------------------------------------------
- // gravity, friction
- //--------------------------------------------------------------------------
-
- public boolean isOnGround()
- {
- return onGround;
- }
- @Override
- public String toString()
- {
- return String.format("(%f, %f, %f, %f)", pos.getX(), pos.getY(), pos.getX() + width, pos.getY() + height);
- }
- }
|