package com.amaya.game;
import com.amaya.game.entities.Fish;
import com.amaya.game.entities.Level;
import com.amaya.game.entities.environment.Drop;
import com.amaya.game.entities.modifiers.CommandsFactory;
import com.amaya.game.entities.modifiers.Event;
import com.amaya.game.entities.modifiers.Expirable;
import com.amaya.game.entities.modifiers.Mandate;
import com.amaya.game.entities.modifiers.Modifier;
import com.amaya.game.entities.modifiers.MoveTo;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.math.Vector3;
import java.util.ArrayList;
import java.util.List;
import static com.amaya.game.Spacefish.Dimensions;
/** Runtime state of the game. */
public class GameController {
/* [ CONSTANTS ] ========================================================================================================================================= */
/** Logging tag. */
public static final String TAG = Spacefish.LOG_TAG;
/* [ MEMBERS ] =========================================================================================================================================== */
/** Reference on current level. */
private final Level mCurrentLevel;
/** Reference on current game. */
private final Spacefish mGame;
/** Stack of commands for execution. */
private final List<Mandate> mMandates = new ArrayList<Mandate>();
/** Accumulated game time. */
private float mGameTime = 0;
/** Current state of the game. */
private KnownStates mState = KnownStates.PLAY;
/* [ CONSTRUCTORS ] ====================================================================================================================================== */
public GameController(final Spacefish game, final Level level) {
mGame = game;
mCurrentLevel = level;
getGame().getFish().reset();
}
/* [ PUBLIC API ] ======================================================================================================================================== */
public void resize(final int width, final int height) {
// do nothing
}
public boolean touched(final Vector3 point) {
// ignore all calls if we are not in PLAY mode
if (KnownStates.PLAY != mState)
return false;
// drop old "move-to' command if exist one
final MoveTo mtc = Mandate.findFirst(getMandates(), MoveTo.class);
if (null != mtc) {
getMandates().remove(mtc);
}
getMandates().add(CommandsFactory.moveTo(getGame().getFish().Bounds, point));
return true;
}
public void update(final float delta) {
// ignore all calls if we are not in PLAY mode
if (KnownStates.PLAY != mState)
return;
mGameTime = getGameTime() + delta;
processCommands(delta);
processLevel(delta);
processCollisions();
}
/* [ GETTER / SETTER METHODS ] =========================================================================================================================== */
/** Reference on current game. */
public Spacefish getGame() {
return mGame;
}
/** Reference on current level. */
public Level getCurrentLevel() {
return mCurrentLevel;
}
/** Stack of commands for execution. */
public List<Mandate> getMandates() {
return mMandates;
}
/** Accumulated game time. */
public float getGameTime() {
return mGameTime;
}
public KnownStates getState() {
return mState;
}
public void setState(final KnownStates state) {
mState = state;
}
/* [ IMPLEMENTATION & HELPERS ] ========================================================================================================================== */
private void processCommands(final float delta) {
final List<Mandate> toDelete = new ArrayList<Mandate>();
for (Mandate cmd : getMandates()) {
if (cmd instanceof MoveTo) {
if (processMoveTo((MoveTo) cmd, delta)) {
toDelete.add(cmd);
}
} else if (cmd instanceof Expirable) {
if (processExpiring((Expirable) cmd, delta)) {
toDelete.add(cmd);
}
} else if (cmd instanceof Event) {
if (processEvent((Event) cmd, delta)) {
toDelete.add(cmd);
}
} else if (cmd instanceof Modifier) {
if (processOther((Modifier) cmd, delta)) {
toDelete.add(cmd);
}
}
}
// do cleanup after applying the commands
for (Mandate cmd : toDelete) {
getMandates().remove(cmd);
}
}
private boolean processEvent(final Event event, final float delta) {
if (Events.ANNOYING_SOUND.equals(event.Name)) {
GameResources.getInstance().getHitSound().play();
return true;
}
return false;
}
private boolean processExpiring(final Expirable cmd, final float delta) {
if (Fish.Fields.SPEED.equals(cmd.Name)) {
getGame().getFish().addModifier(cmd);
return true;
}
return false;
}
private boolean processMoveTo(final MoveTo mtc, final float delta) {
// game entity
final Fish fish = getGame().getFish();
// modifiers/commands
final List<Mandate> mandates = new ArrayList<Mandate>();
mandates.add(mtc);
// apply movement algorithm
fish.getStrategy().update(fish, mandates, getGameTime(), delta);
// try to detect is our behavior processing finished or not
final Vector2 current = fish.getPosition();
final Vector2 last = mtc.getEnd();
if (Math.abs(current.x - last.x) <= Dimensions.ACCURACY) {
if (Math.abs(current.y - last.y) <= Dimensions.ACCURACY) {
if (Spacefish.Debug.MOVE_COMMANDS)
Gdx.app.log(TAG, "[move-to] processing finished.");
// command processed, remove it
return true;
}
}
// keep command in stack
return false;
}
private boolean processOther(final Modifier cmd, final float delta) {
final Fish fish = getGame().getFish().addModifier(cmd);
// end of the game
if (fish.getHealth() <= 0) {
setState(KnownStates.GAME_OVER);
getGame().navigateToGameOver();
}
return true;
}
private void processLevel(final float delta) {
final Level.KnownStates oldState = getCurrentLevel().getState();
// get new objects for displaying
getCurrentLevel().update(getGameTime(), delta);
final Level.KnownStates newState = getCurrentLevel().getState();
processLevelDrops(getCurrentLevel().Aliens, delta);
processLevelDrops(getCurrentLevel().Asteroids, delta);
// level finished
if (newState != oldState && Level.KnownStates.DONE == newState) {
mState = KnownStates.GAME_OVER;
getGame().navigateToGameOver();
}
}
private void processLevelDrops(final List<? extends Drop> list, final float delta) {
// calculate new positions
for (Drop drop : list) {
drop.getStrategy().update(drop, null, getGameTime(), delta);
}
// do cleanup
for (int i = list.size() - 1; i >= 0; i--) {
final Drop drop = list.get(i);
if (drop.getPosition().y < Dimensions.ACCURACY) {
list.remove(i);
}
}
}
private void processCollisions() {
final Rectangle rcFish = getGame().getFish().Bounds;
processCollisions(rcFish, getCurrentLevel().Aliens);
processCollisions(rcFish, getCurrentLevel().Asteroids);
}
private void processCollisions(final Rectangle rc, final List<? extends Drop> list) {
for (int i = list.size() - 1; i >= 0; i--) {
final Drop drop = list.get(i);
if (drop.Bounds.overlaps(rc)) {
if (Spacefish.Debug.FISH_COLLISIONS) {
Gdx.app.log(TAG, "[collision] with: " + drop.Bounds + ", entity: " + drop.getClass().getSimpleName());
Gdx.app.log(TAG, "[collision] modifier: " + drop.getModifier());
}
getMandates().add(drop.getModifier());
list.remove(i);
}
}
}
/* [ NESTED DECLARATIONS ] =============================================================================================================================== */
public interface Events {
String PREFIX = "Event: ";
String ANNOYING_SOUND = PREFIX + "AnnoyingSound";
}
public static enum KnownStates {
PLAY,
PAUSED,
GAME_OVER;
}
}