package com.arretadogames.pilot.world; import android.graphics.RectF; import android.opengl.GLES11; import com.arretadogames.pilot.MainActivity; import com.arretadogames.pilot.R; import com.arretadogames.pilot.entities.CollisionFlag; import com.arretadogames.pilot.entities.Player; import com.arretadogames.pilot.entities.PlayerNumber; import com.arretadogames.pilot.entities.Steppable; import com.arretadogames.pilot.render.Renderable; import com.arretadogames.pilot.render.opengl.GLCanvas; import com.arretadogames.pilot.screens.GameWorldUI; import com.arretadogames.pilot.ui.GameHUDButton; import org.jbox2d.common.Vec2; import org.jbox2d.dynamics.Fixture; import java.util.Random; import javax.microedition.khronos.opengles.GL10; public class RaceStartManager implements Renderable, Steppable, GameHUDButton { private static final float COUNTDOWN_SIZE = 200; private static final float COUNTDOWN_START_TIME = 5.0f; private float halfScreenWidth; private float halfScreenHeight; private final RectF countdownRect; // Impulse it gives to the player that wins the drag private static final Vec2 START_RACE_IMPULSE = new Vec2(4, 0); // Force that pulls the players before the race begins private static final float FORCE = 40.0f; // The initial x offset private static final float INITIAL_POS_X_OFFSET = -22.0f; // This countdown is not the same as the images (3 here does not mean 3 in the image - check render) private float countDownTimer; private boolean raceStarted; private boolean arePlayersCollliding; private boolean activateInput; private Vec2 impulseP1; private Vec2 impulseBackP1; private Vec2 impulseP2; private Vec2 impulseBackP2; private Player p1; private Player p2; private GameHUDButton nextUIListener; private GameWorldUI ui; public RaceStartManager(GameWorld world, GameWorldUI ui) { this.p1 = world.getPlayers().get(PlayerNumber.ONE); this.p2 = world.getPlayers().get(PlayerNumber.TWO); this.ui = ui; ui.setAllButtonListeners(this); setNextUIListener(world); resetCountdown(); impulseP1 = new Vec2(1,0); impulseP1.normalize(); impulseP1.mul(FORCE * p1.body.getMass()); impulseP2 = new Vec2(1,0); impulseP2.normalize(); impulseP2.mul(FORCE * p2.body.getMass()); impulseBackP1 = new Vec2(-1,0); impulseBackP1.normalize(); impulseBackP1.mul(p1.body.getMass()); impulseBackP2 = new Vec2(-1,0); impulseBackP2.normalize(); impulseBackP2.mul(p2.body.getMass()); halfScreenWidth = MainActivity.getActivity().getResources().getDimension(R.dimen.screen_width) / 2; halfScreenHeight = MainActivity.getActivity().getResources().getDimension(R.dimen.screen_height) / 2; countdownRect = new RectF( halfScreenWidth - COUNTDOWN_SIZE / 2, halfScreenHeight - COUNTDOWN_SIZE / 2, halfScreenWidth + COUNTDOWN_SIZE / 2, halfScreenHeight + COUNTDOWN_SIZE / 2); } private void setNextUIListener(GameHUDButton listener) { nextUIListener = listener; } public void resetCountdown() { countDownTimer = COUNTDOWN_START_TIME; p1.setEnabled(false); p2.setEnabled(false); raceStarted = false; setPlayersCollision(false); Vec2 offset = new Vec2(INITIAL_POS_X_OFFSET, 0); p1.body.setTransform(p1.body.getPosition().add(offset), 0); p2.body.setTransform(p1.body.getPosition(), 0); activateInput = false; // Give P1 an bigger impulse p1.body.applyLinearImpulse(new Vec2(5.0f, 0).mul(p1.body.getMass()), p1.body.getPosition(), true); p2.body.applyLinearImpulse(new Vec2(4.5f, 0).mul(p2.body.getMass()), p2.body.getPosition(), true); } private void setPlayersCollision(boolean shouldCollide) { if (shouldCollide) { Fixture f = p1.body.getFixtureList(); while (f != null) { f.getFilterData().maskBits = f.getFilterData().maskBits | CollisionFlag.GROUP_PLAYERS.getValue(); f = f.getNext(); } f = p2.body.getFixtureList(); while (f != null) { f.getFilterData().maskBits = f.getFilterData().maskBits | CollisionFlag.GROUP_PLAYERS.getValue(); f = f.getNext(); } } else { Fixture f = p1.body.getFixtureList(); while (f != null) { f.getFilterData().maskBits = f.getFilterData().maskBits & ~CollisionFlag.GROUP_PLAYERS.getValue(); f = f.getNext(); } f = p2.body.getFixtureList(); while (f != null) { f.getFilterData().maskBits = f.getFilterData().maskBits & ~CollisionFlag.GROUP_PLAYERS.getValue(); f = f.getNext(); } } arePlayersCollliding = shouldCollide; } private void impulsePlayer(PlayerNumber impulseWinPlayer) { raceStarted = true; if (impulseWinPlayer == null) { // Random.. int randomizedNumber = new Random().nextInt(10); if (randomizedNumber % 2 == 0) { impulseWinPlayer = PlayerNumber.ONE; } else { impulseWinPlayer = PlayerNumber.TWO; } } if (impulseWinPlayer.equals(PlayerNumber.ONE)) { p1.body.applyLinearImpulse(START_RACE_IMPULSE.mul(p1.body.getMass()), p1.body.getPosition(), true); } else if (impulseWinPlayer.equals(PlayerNumber.TWO)) { p2.body.applyLinearImpulse(START_RACE_IMPULSE.mul(p2.body.getMass()), p2.body.getPosition(), true); } } private void enablePlayers() { p1.setEnabled(true); p2.setEnabled(true); setPlayersCollision(true); ui.setAllButtonListeners(nextUIListener); } private float currentTimePercentage; @Override public void render(GLCanvas canvas, float timeElapsed) { // TODO: Remove these two GL-Specific methods, insert it inside Canvas to have same functionality GLES11.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA); if (countDownTimer > 0) { // Draw numbers if we have a countdown if (countDownTimer > 4) { canvas.drawBitmap(R.drawable.countdown_3, countdownRect); } else if (countDownTimer > 3) { canvas.drawBitmap(R.drawable.countdown_2, countdownRect); } else if (countDownTimer > 2) { canvas.drawBitmap(R.drawable.countdown_1, countdownRect); } else if (countDownTimer > 1) { currentTimePercentage = 1 - interpolate(2, 1f, countDownTimer); canvas.saveState(); canvas.scale(1+currentTimePercentage * 1.5f, 1+currentTimePercentage * 1.5f, halfScreenWidth, halfScreenHeight); canvas.drawBitmap(R.drawable.countdown_go, countdownRect, 255 * (1 - currentTimePercentage)); canvas.restoreState(); } } GLES11.glBlendFunc(GL10.GL_ONE, GL10.GL_ONE_MINUS_SRC_ALPHA); } @Override public void step(float timeElapsed) { if (countDownTimer >= 2) { if (p1.getPosX() - 0.1f > p2.getPosX()) { p2.body.applyForceToCenter(impulseP2); p1.body.applyForceToCenter(impulseBackP1); } else if (p2.getPosX() - 0.1f > p1.getPosX()) { p1.body.applyForceToCenter(impulseP1); p2.body.applyForceToCenter(impulseBackP2); } } else if (countDownTimer < 2 && !raceStarted && !activateInput) { // At 2s mark, activate input activateInput = true; } else if (countDownTimer < 1.5 && !raceStarted) { // If players havent pressed, impulse someone... impulsePlayer(null); } else if (countDownTimer < 1.1 && !arePlayersCollliding) { // If the race has not started and it is 1.1s mark, then enable players anyway enablePlayers(); } if (countDownTimer > 0) { // Decrements countdown only until it reaches a negative... (we dont need it anymore) countDownTimer -= timeElapsed; } } private float interpolate(float max, float min, float current) { return (current - min) / (max - min); } public boolean isActive() { return countDownTimer <= 0; } @Override public void onClick(int buttonId, boolean pressed) { if (!activateInput) { return; } if (buttonId == GameWorldUI.BT_PLAYER_1_JUMP || buttonId == GameWorldUI.BT_PLAYER_1_ACT) { impulsePlayer(PlayerNumber.ONE); activateInput = false; } else if (buttonId == GameWorldUI.BT_PLAYER_2_JUMP || buttonId == GameWorldUI.BT_PLAYER_2_ACT) { impulsePlayer(PlayerNumber.TWO); activateInput = false; } } }