package fi.hbp.angr.models.levels;
import aurelienribon.bodyeditor.BodyEditorLoader;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef;
import com.badlogic.gdx.physics.box2d.BodyDef.BodyType;
import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.badlogic.gdx.scenes.scene2d.Actor;
import fi.hbp.angr.BodyFactory;
import fi.hbp.angr.G;
import fi.hbp.angr.Preloadable;
import fi.hbp.angr.logic.GameState;
import fi.hbp.angr.models.SlingshotActor;
import fi.hbp.angr.models.actors.Hans;
/**
* Abstract Level class.
*
* All game levels should inherit this class.
*/
public abstract class Level extends Actor implements Preloadable {
/** Level name. */
private final String levelName;
/** Terrain sprite for of the level. */
private Sprite[] sprite;
/** Game state object. */
protected GameState gs;
/** Body factor for creating level actors.*/
protected BodyFactory bf;
/** Actor of player model Hans. */
protected Hans hans;
/* TODO Use timer instead of this implementation? */
/** Grenade spawn delay. */
private final float grenadeSpawnDelay = 5.0f;
/** Counter used to count grenade spawn delay. */
private float grenadeSpawnCounter = 0.0f;
/** Number of map slices. */
private final int slices;
/**
* Constructor for Level class.
* @param levelName Texture filename without extension.
*/
public Level(String levelName, int slices) {
this.levelName = levelName;
this.slices = slices;
sprite = new Sprite[slices];
}
/**
* Automatically called preload method.
*
* Prefer use of AssetManager.load().
*/
protected abstract void doOnPreload();
@Override
public final void preload() {
for (int i = 0; i < slices; i++) {
G.getAssetManager().load("data/" + levelName + "_" + i + ".png", Texture.class);
}
doOnPreload();
}
/**
* Automatically called unload method.
*
* Prefer use of AssetManager.unload().
*/
protected abstract void doOnUnload();
@Override
public final void unload() {
for (int i = 0; i < slices; i++) {
G.getAssetManager().unload("data/" + levelName + "_" + i + ".png");
}
doOnUnload();
}
/**
* This method is called when initiating this level.
*/
protected abstract void doOnCreate();
/**
* This method is called when the game screen is initialized.
* @param bf a body factory.
* @param gs a game state object.
*/
public final void levelCreate(BodyFactory bf, GameState gs) {
this.bf = bf;
this.gs = gs;
doOnCreate();
}
/**
* Internal show method that should be called by public show method
* @param mapFd fixture definition for terrain body.
* @param hansX x coordinate for Hans.
* @param hansY y coordinate for Hans.
*/
protected void showMap(FixtureDef mapFd, float hansX, float hansY) {
if (bf == null || gs == null) {
Gdx.app.error("Level", "Body factory or game state not set.");
Gdx.app.exit();
while (true);
}
/* Create map terrain */
createTerrain(mapFd);
/* Spawn player */
hans = (Hans)bf.spawnHans(hansX, hansY, 0);
spawnGrenade();
}
/**
* Initialize terrain.
* @param mapFd map fixture definition.
*/
private void createTerrain(FixtureDef mapFd) {
BodyEditorLoader bel = new BodyEditorLoader(
Gdx.files.internal(levelName + ".json"));
BodyDef bd = new BodyDef();
bd.type = BodyType.StaticBody;
for (int i = 0; i < slices; i++) {
Texture texture = G.getAssetManager().get(
"data/" + levelName + "_" + i + ".png",
Texture.class);
bd.position.set(i * 1000 * G.WORLD_TO_BOX, 0);
sprite[i] = new Sprite(texture);
sprite[i].setPosition(i * 1000, 0);
Body body = bf.getWorld().createBody(bd);
bel.attachFixture(body,
String.valueOf(i),
mapFd,
sprite[i].getWidth() / G.BOX_TO_WORLD);
}
}
/**
* Check if grenade has been used and spawn a new one if needed.
*/
private void updateHans() {
if (hans.isPalmJointActive() == false) {
GameState.Grenades grenades = gs.getGrenades();
if (grenades.getCount() > 0) {
grenadeSpawnCounter += Gdx.graphics.getDeltaTime();
if (grenadeSpawnCounter >= grenadeSpawnDelay) {
grenades.decrement();
if (grenades.getCount() > 0)
spawnGrenade();
grenadeSpawnCounter = 0.0f;
}
}
}
}
/**
* Spawn a new grenade.
*/
private void spawnGrenade() {
hans.resetHandPosition();
hans.setPalmJoint(((SlingshotActor)bf.spawnGrenade(
hans.getX() + 50.0f,
hans.getY() + 30.0f, 0)).getBody());
}
@Override
public void draw(SpriteBatch batch, float parentAlpha) {
for (int i = 0; i < slices; i++) {
sprite[i].draw(batch);
}
updateHans();
}
/**
* Get level width for x axis clamping.
* @return width of level in x axis.
*/
public abstract float getLevelWidth();
/**
* Called when game screen is no longer the current screen for a Game.
*/
public void hide() { }
/**
* Called when game screen becomes the current screen for a Game.
*/
public void show() { }
@Override
public String toString() {
return "Level: " + levelName;
}
}