package it.marteEngine.entity;
import org.newdawn.slick.GameContainer;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.geom.Vector2f;
import org.newdawn.slick.util.Log;
/**
* this class only works with a fixed frame rate of 60. All calculations are
* based on that assumption. It's okay for this little sample game
*
* @author Thomas
*
*/
public class PhysicsEntity extends Entity {
/** speed vector (x,y) **/
public Vector2f speed = new Vector2f(0, 0);
/** acceleration vector (x,y) **/
public Vector2f acceleration = new Vector2f(0, 0);
public float gravity = 0.2f;
public Vector2f friction = new Vector2f(0.5f, 0.5f);
/** height of a slope a PhysicsEntity can run up */
public int slopeHeight = 1;
public Vector2f maxSpeed = new Vector2f(3, 8);
public PhysicsEntity(float x, float y) {
super(x, y);
// physics entities are solid
addType(SOLID);
}
public void update(GameContainer container, int delta)
throws SlickException {
// update possible animation stuff
super.update(container, delta);
motion(true, true);
gravity(delta);
}
/**
* Moves this entity at it's current speed (speed.x, speed.y) and increases
* speed based on acceleration (acceleration.x, acceleration.y)
*
* @param moveX
* include horizontal movement
* @param moveY
* include vertical movement
*/
public void motion(boolean moveX, boolean moveY) {
// if we should move horizontally
if (moveX) {
// make us move, and stop us on collision
if (!motionx(this, speed.x)) {
speed.x = 0;
}
// increase velocity/speed
speed.x += acceleration.x;
}
// if we should move vertically
if (moveY) {
// make us move, and stop us on collision
if (!motiony(this, speed.y)) {
speed.y = 0;
}
// increase velocity/speed
speed.y += acceleration.y;
}
}
/**
* Increases this entity's vertical speed, based on its gravity (gravity)
*
* @return void
*/
public void gravity(int delta) {
// increase velocity/speed based on gravity
speed.y += gravity;
}
/**
* Slows this entity down, according to its friction (friction.x,
* friction.y)
*
* @param mx
* Include horizontal movement
* @param my
* Include vertical movement
* @return void
*/
public void friction(boolean mx, boolean my) {
// if we should use friction, horizontally
if (mx) {
// speed > 0, then slow down
if (speed.x > 0) {
speed.x -= friction.x;
// if we go below 0, stop.
if (speed.x < 0) {
speed.x = 0;
}
}
// speed < 0, then "speed up" (in a sense)
if (speed.x < 0) {
speed.x += friction.x;
// if we go above 0, stop.
if (speed.x > 0) {
speed.x = 0;
}
}
}
}
/**
* Stops entity from moving to fast, according to maxspeed (mMaxspeed.x,
* mMaxspeed.y)
*
* @param mx
* Include horizontal movement
* @param my
* Include vertical movement
* @return void
*/
public void maxspeed(boolean mx, boolean my) {
if (mx) {
if (Math.abs(speed.x) > maxSpeed.x) {
speed.x = maxSpeed.x * Math.signum(speed.x);
}
}
if (my) {
if (Math.abs(speed.y) > maxSpeed.y) {
speed.y = maxSpeed.y * Math.signum(speed.y);
}
}
}
/**
* Moves the set entity horizontal at a given speed, checking for collisions
* and slopes
*
* @param e
* The entity you want to move
* @param spdx
* The speed at which the entity should move
* @return True (didn't hit a solid) or false (hit a solid)
*/
public boolean motionx(Entity e, float spdx) {
// check each pixel before moving it
for (int i = 0; i < Math.abs(spdx); i++) {
// if we've moved
boolean moved = false;
boolean below = true;
if (e.collide(SOLID, e.x, e.y + 1) == null) {
below = false;
}
// run through how high a slope we can move up
for (int s = 0; s <= slopeHeight; s++) {
// if we don't hit a solid in the direction we're moving,
// move....
if (e.collide(SOLID, e.x + Math.signum(spdx), e.y - s) == null) {
// increase/decrease positions
// if the player is in the way, simply don't move (but don't
// count it as stopping)
if (e.collide(PLAYER, e.x + Math.signum(spdx), e.y - s) == null) {
e.x += Math.signum(spdx);
}
// move up the slope
e.y -= s;
// we've moved
moved = true;
// stop checking for slope (so we don't fly up into the
// air....)
break;
}
}
// if we are now in the air, but just above a platform, move us
// down.
if (below && e.collide(SOLID, e.x, e.y + 1) == null) {
e.y += 1;
}
// if we haven't moved, set our speed to 0
if (!moved) {
return false;
}
}
// hit nothing!
return true;
}
/**
* Moves the set entity vertical at a given speed, checking for collisions
*
* @param e
* The entity you want to move
* @param spdy
* The speed at which the entity should move
* @return True (didn't hit a solid) or false (hit a solid)
*/
public boolean motiony(Entity e, float spdy) {
// for each pixel that we will move...
for (int i = 0; i < Math.abs(spdy); i++) {
// if we DON'T collide with solid
if (e.collide(SOLID, e.x, e.y + Math.signum(spdy)) == null) {
// if we don't run into a player, them move us
if (e.collide(PLAYER, e.x, e.y + Math.signum(spdy)) == null) {
e.y += Math.signum(spdy);
}
// but note that we wont stop our movement if we hit a player.
} else {
// stop movement if we hit a solid
return false;
}
}
// hit nothing!
return true;
}
/**
* Moves an entity of the given type that is on top of this entity (if any).
* Also moves player if it's on top of the entity on top of this one.
* (confusing.. eh?). Mostly used for moving platforms
*
* @param type
* Entity type to check for
* @param speed
* The speep at which to move the thing above you
* @return void
*/
public void moveontop(String type, float speed) {
Entity e = collide(type, x, y - 1);
if (e != null) {
motionx(e, speed);
// if the player is on top of the thing that's being moved, move
// him/her too.
PhysicsEntity p = null;
try {
p = (PhysicsEntity) e;
} catch (ClassCastException cce) {
Log.debug("PhysicsEntity.moveontop(): failed to cast entity '"
+ e.toString() + "' to physics entity!");
}
if (p != null) {
p.moveontop(PLAYER, speed);
}
}
}
}