package com.arretadogames.pilot.entities; import com.arretadogames.pilot.render.AnimationManager; import com.arretadogames.pilot.render.AnimationSwitcher; import com.arretadogames.pilot.render.PhysicsRect; import com.arretadogames.pilot.render.Renderable; import com.arretadogames.pilot.render.opengl.GLCanvas; import org.jbox2d.common.Vec2; import org.jbox2d.dynamics.Body; /** * Toucan implementation */ public class Toucan implements Renderable, Steppable { private enum ToucanState { INITIAL_FLIGHT, GRAB, PULL_FLIGHT, DROP, OUT_FLIGHT; } private static final PhysicsRect TOUCAN_SIZE = new PhysicsRect(2, 2); private static final Vec2 GRAB_OFFSET = new Vec2(0, 0.6f); private static final float TARGET_PLAYER_X_OFFSET = -5f; private static final float TARGET_GROUND_Y_OFFSET = 2f; private static final Vec2 TARGET_FLAG_OFFSET = new Vec2(-10, 2); private static final Vec2 FLIGHT_OUT_POSITION_RELATIVE_TO_TARGET = new Vec2(4, 10); /* All duration are in seconds */ private static final float GRAB_FLIGHT_DURATION = 1; private static final float GRAB_DURATION = 0.0001f; private static final float PULL_FLIGHT_DURATION = 1f; private static final float DROP_DURATION = 0.0001f; private static final float OUT_FLIGHT_DURATION = 3; private Vec2 flightOutTargetPosition = new Vec2(); private Vec2 oldPos = new Vec2(); private Vec2 center = new Vec2(); private Vec2 grabbedPlayerPos = new Vec2(); private Player playerToGrab; private Player target; private AnimationSwitcher sprite; private float flagX; private float remainingTime; private ToucanState state; /** * Toucan receives the camera to updates its starting position * * @param camera */ public Toucan() { this.sprite = AnimationManager.getInstance().getSprite("AraraAzul"); } /** * If the Toucan is currently active * * @return True - If the toucan if active<br> * False - Otherwise */ public boolean isActive() { return playerToGrab != null; } /** * Activates the Toucan using the given parameters * * @param xPos The initial X position * @param yPos The initial Y position * @param playerToGrab The player to be grabbed * @param targetPlayer The player that the toucan will be grabbed to */ public void activate(float xPos, float yPos, Player playerToGrab, Player targetPlayer, float flagX) { if (!isActive() && playerToGrab != null && targetPlayer != null) { this.center.set(xPos, yPos); this.oldPos.set(xPos, yPos); this.target = targetPlayer; this.playerToGrab = playerToGrab; this.remainingTime = GRAB_FLIGHT_DURATION; this.state = ToucanState.INITIAL_FLIGHT; this.playerToGrab.setForceStop(true); this.flagX = flagX; } } /* * Performs a cleanup operation after a grab. */ private void cleanUp() { this.target = null; this.playerToGrab = null; this.remainingTime = 0; this.state = null; this.flagX = Float.MAX_VALUE; } @Override public void render(GLCanvas canvas, float timeElapsed) { if (!isActive()) return; canvas.saveState(); canvas.translatePhysics(center.x, center.y); sprite.render(canvas, TOUCAN_SIZE, timeElapsed); canvas.restoreState(); } @Override public void step(float timeElapsed) { if (!isActive()) return; remainingTime -= timeElapsed; switch (state) { case INITIAL_FLIGHT: if (remainingTime <= 0) { state = ToucanState.GRAB; remainingTime = GRAB_DURATION; setOldPosition(); } else { moveTowards(playerToGrab.body.getPosition().x + GRAB_OFFSET.x, playerToGrab.body.getPosition().y + GRAB_OFFSET.y, remainingTime, GRAB_FLIGHT_DURATION); } break; case GRAB: if (remainingTime <= 0) { state = ToucanState.PULL_FLIGHT; remainingTime = PULL_FLIGHT_DURATION; playerToGrab.setGhostMode(true); } break; case PULL_FLIGHT: if (remainingTime <= 0) { playerToGrab.setGhostMode(false); playerToGrab.setForceStop(false); state = ToucanState.DROP; remainingTime = DROP_DURATION; setOldPosition(); } else { Body b = target.body; if (b.getPosition().x < flagX) { moveTowards(b.getPosition().x + TARGET_PLAYER_X_OFFSET, TARGET_GROUND_Y_OFFSET, remainingTime, PULL_FLIGHT_DURATION); } else { moveTowards(flagX + TARGET_FLAG_OFFSET.x, TARGET_FLAG_OFFSET.y, remainingTime, PULL_FLIGHT_DURATION); } grabbedPlayerPos.set(center).addLocal(GRAB_OFFSET.x, -GRAB_OFFSET.y); playerToGrab.body.setTransform(grabbedPlayerPos, 0); } break; case DROP: if (remainingTime <= 0) { state = ToucanState.OUT_FLIGHT; remainingTime = OUT_FLIGHT_DURATION; flightOutTargetPosition.set(target.body.getPosition()) .addLocal(FLIGHT_OUT_POSITION_RELATIVE_TO_TARGET.x, FLIGHT_OUT_POSITION_RELATIVE_TO_TARGET.y); setOldPosition(); } break; case OUT_FLIGHT: if (remainingTime <= 0) { cleanUp(); } else { moveTowards(flightOutTargetPosition.x, flightOutTargetPosition.y, remainingTime, OUT_FLIGHT_DURATION); } break; default: break; } } /* * Sets the variable oldPos to the current center. */ private void setOldPosition() { oldPos.set(center); } /* * Move the toucan towards the given position and in the given time * parameters.<br> The velocity will be based on the total time he had and * the remaining time he now has. */ private void moveTowards(float targetX, float targetY, float time, float totalTime) { center.x = oldPos.x + (targetX - oldPos.x) * (1 - (time / totalTime)); center.y = oldPos.y + (targetY - oldPos.y) * (1 - (time / totalTime)); } }