package net.alcuria.umbracraft.engine.components;
import net.alcuria.umbracraft.Config;
import net.alcuria.umbracraft.Game;
import net.alcuria.umbracraft.engine.components.AnimationGroupComponent.Direction;
import net.alcuria.umbracraft.engine.entities.Entity;
import net.alcuria.umbracraft.engine.map.Map;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
/** A component to handle map collision.
* @author Andrew Keturi */
public class MapCollisionComponent implements Component {
private BitmapFont debug;
private final int height, width;
private boolean isControlled;
private final Map map;
// private boolean onGround, onStairs;
private MapCollisionState state;
public MapCollisionComponent(int width, int height) {
this.width = width;
this.height = height;
map = Game.map();
state = MapCollisionState.FALLING_DOWN;
}
private void checkJump(Direction direction, Entity entity) {
if (!state.isOnGround()) {
return;
}
final float len = Math.abs(entity.velocity.x) + Math.abs(entity.velocity.y);
if (len < 1.5f && isControlled) {
return;
}
// get the tile coordinates of the entity
float nearX = (entity.position.x);
float farX = (entity.position.x);
float nearY = (entity.position.y);
float farY = (entity.position.y);
float z = entity.position.z / Config.tileWidth;
switch (direction) {
case UP:
farY += 14;
break;
case DOWN:
farY -= 14;
break;
case LEFT:
farX -= 14;
break;
case RIGHT:
farX += 14;
break;
default:
break;
}
nearX /= Config.tileWidth;
nearY /= Config.tileWidth;
farX /= Config.tileWidth;
farY /= Config.tileWidth;
// check for jumping from a higher to lower altitude
if (map.getAltitudeAt(nearX, nearY) < (int) z && state == MapCollisionState.ON_GROUND) {
state = MapCollisionState.JUMPING_UP;
entity.velocity.z = 5;
}
// check if the altitude in front of the player is just one tile up
if ((map.getAltitudeAt(farX, farY) - 1 == z) && state == MapCollisionState.ON_GROUND) {
entity.velocity.z = 5;
}
}
@Override
public void create(Entity entity) {
debug = Game.assets().get("fonts/message.fnt", BitmapFont.class);
isControlled = entity.getComponent(ControlledInputComponent.class) != null;
entity.setRenderOffset(height / 2);
Game.log("Set render offset for " + entity.getName() + " to " + entity.getRenderOffset());
}
@Override
public void dispose(Entity entity) {
}
/** @return the component's height */
public int getHeight() {
return height;
}
/** @return the component's width */
public int getWidth() {
return width;
}
/** @return whether or not the entity is colliding with the ground */
public boolean isOnGround() {
return state.isOnGround();
}
/** @return whether or not the entity is on stairs */
public boolean isOnStairs() {
return state == MapCollisionState.ON_STAIRS;
}
@Override
public void render(Entity entity) {
// Game.log(state + "");
int tileX1 = (int) (entity.position.x + width) / Config.tileWidth;
int tileY = (int) (entity.position.y + height + entity.velocity.y) / Config.tileWidth;
if (Game.isDebug()) {
debug.draw(Game.batch(), map.getAltitudeAt(tileX1, tileY) + "", width, height);
Game.batch().draw(Game.assets().get("debug.png", Texture.class), entity.position.x - width / 2, entity.position.y - height / 2, width, height);
}
}
public void setOnPlatform() {
state = MapCollisionState.ON_PLATFORM;
}
public void setOnStairs() {
state = MapCollisionState.ON_STAIRS;
}
@Override
public void update(Entity entity) {
switch (state) {
case FALLING_DOWN:
break;
case JUMPING_UP:
break;
case ON_GROUND:
break;
case ON_PLATFORM:
break;
case ON_STAIRS:
break;
default:
break;
}
// stair updates
int centerX = (int) (entity.position.x) / Config.tileWidth;
int centerY = (int) (entity.position.y) / Config.tileWidth;
int tileYNorth = (int) (2 + entity.position.y + height / 2) / Config.tileWidth;
int tileYSouth = (int) ((entity.position.y - height / 2) - 0) / Config.tileWidth;
final float tileAltitudeFloat = (entity.position.z / Config.tileWidth);
if (map.getTypeAt(centerX, centerY) == map.getDefinition().stairs) {
if (tileAltitudeFloat < map.getAltitudeAt(centerX, tileYNorth) && entity.velocity.y > 0) {
entity.velocity.z = entity.velocity.y;
entity.velocity.y = 0;
} else if (tileAltitudeFloat > map.getAltitudeAt(centerX, tileYSouth) && entity.velocity.y < 0) {
entity.velocity.z = entity.velocity.y;
entity.velocity.y = 0;
}
state = MapCollisionState.ON_STAIRS;
} else if (state == MapCollisionState.ON_STAIRS) {
state = MapCollisionState.ON_GROUND;
}
final int tileAltitude = (int) (entity.position.z / Config.tileWidth);
// update position
// check for collisions
if (entity.velocity.y > 0) {
// NORTH
int tileX1 = (int) (entity.position.x + width / 2) / Config.tileWidth;
int tileX2 = (int) (entity.position.x - width / 2) / Config.tileWidth;
int tileY = (int) (entity.position.y + height / 2 + entity.velocity.y) / Config.tileWidth;
if (map.getAltitudeAt(tileX1, tileY) > tileAltitude || map.getAltitudeAt(tileX2, tileY) > tileAltitude) {
entity.velocity.y = 0;
// nudge out if we're stuck
if (map.getAltitudeAt(tileX1, tileY) <= tileAltitude && entity.velocity.x == 0) {
entity.velocity.x += 2f;
} else if (map.getAltitudeAt(tileX2, tileY) <= tileAltitude && entity.velocity.x == 0) {
entity.velocity.x -= 2f;
}
}
checkJump(Direction.UP, entity);
} else if (entity.velocity.y < 0 && state != MapCollisionState.ON_STAIRS) {
// SOUTH
int tileX1 = (int) (entity.position.x + width / 2) / Config.tileWidth;
int tileX2 = (int) (entity.position.x - width / 2) / Config.tileWidth;
int tileY = (int) (entity.position.y - height / 2 + entity.velocity.y) / Config.tileWidth;
if (map.getAltitudeAt(tileX1, tileY) > tileAltitude || map.getAltitudeAt(tileX2, tileY) > tileAltitude) {
entity.velocity.y = 0;
// nudge out if we're stuck
if (map.getAltitudeAt(tileX1, tileY) <= tileAltitude && entity.velocity.x == 0) {
entity.velocity.x += 2f;
} else if (map.getAltitudeAt(tileX2, tileY) <= tileAltitude && entity.velocity.x == 0) {
entity.velocity.x -= 2f;
}
}
checkJump(Direction.DOWN, entity);
}
if (entity.velocity.x > 0) {
// RIGHT
int tileX = (int) (entity.position.x + width / 2 + entity.velocity.x) / Config.tileWidth;
int tileY1 = (int) (entity.position.y + height / 2 + entity.velocity.y) / Config.tileWidth;
int tileY2 = (int) (entity.position.y - height / 2 + entity.velocity.y) / Config.tileWidth;
if (map.getAltitudeAt(tileX, tileY1) > tileAltitude || map.getAltitudeAt(tileX, tileY2) > tileAltitude) {
entity.velocity.x = 0;
// nudge out if we're stuck
if (map.getAltitudeAt(tileX, tileY1) <= tileAltitude && entity.velocity.y == 0) {
entity.velocity.y += 2f;
} else if (map.getAltitudeAt(tileX, tileY2) <= tileAltitude && entity.velocity.y == 0) {
entity.velocity.y -= 2f;
}
}
checkJump(Direction.RIGHT, entity);
} else if (entity.velocity.x < 0) {
// LEFT
int tileX = (int) (entity.position.x - width / 2 + entity.velocity.x) / Config.tileWidth;
int tileY2 = (int) (entity.position.y - height / 2 + entity.velocity.y) / Config.tileWidth;
int tileY1 = (int) (entity.position.y + height / 2 + entity.velocity.y) / Config.tileWidth;
if (map.getAltitudeAt(tileX, tileY1) > tileAltitude || map.getAltitudeAt(tileX, tileY2) > tileAltitude) {
entity.velocity.x = 0;
// nudge out if we're stuck
if (map.getAltitudeAt(tileX, tileY1) <= tileAltitude && entity.velocity.y == 0) {
entity.velocity.y += 2f;
} else if (map.getAltitudeAt(tileX, tileY2) <= tileAltitude && entity.velocity.y == 0) {
entity.velocity.y -= 2f;
}
}
checkJump(Direction.LEFT, entity);
}
entity.position.add(entity.velocity);
// check for stairs, falling, or placement
if (state == MapCollisionState.ON_STAIRS) {
// reset back to y so the animations work correctly
entity.velocity.y = entity.velocity.z;
entity.velocity.z = 0;
} else if (entity.position.z / Config.tileWidth > map.getAltitudeAt(centerX, centerY)) {
state = MapCollisionState.FALLING_DOWN;
entity.velocity.z -= 0.5f;
} else {
state = MapCollisionState.ON_GROUND;
entity.velocity.z = 0;
entity.position.z = map.getAltitudeAt(centerX, centerY) * Config.tileWidth;
}
// check if we're STILL inside a wall, and if so gently nudge out
if (state == MapCollisionState.ON_GROUND && entity.velocity.z <= 0) {
// north/south
int tileY1 = (int) (entity.position.y + height / 2) / Config.tileWidth;
int tileY2 = (int) (entity.position.y - height / 2) / Config.tileWidth;
if (map.getAltitudeAt(centerX, tileY1) > map.getAltitudeAt(centerX, centerY)) {
entity.position.y -= 0.75f;
} else if (map.getAltitudeAt(centerX, tileY2) > map.getAltitudeAt(centerX, centerY)) {
entity.position.y += 0.75f;
}
// east/west
int tileX1 = (int) (entity.position.x + width / 2) / Config.tileWidth;
int tileX2 = (int) (entity.position.x - width / 2) / Config.tileWidth;
if (map.getAltitudeAt(tileX1, centerY) > map.getAltitudeAt(centerX, centerY)) {
entity.position.x -= 1f;
} else if (map.getAltitudeAt(tileX2, centerY) > map.getAltitudeAt(centerX, centerY)) {
entity.position.x += 1f;
}
}
}
}