/* * Copyright (C) 2016 Google Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.android.apps.santatracker.games.gamebase; import android.app.Activity; import android.app.UiModeManager; import android.content.Context; import android.content.res.Configuration; import com.google.android.apps.santatracker.R; import com.google.android.apps.santatracker.games.gumball.Utils; import com.google.android.apps.santatracker.games.simpleengine.Renderer; import com.google.android.apps.santatracker.games.simpleengine.Scene; import com.google.android.apps.santatracker.games.simpleengine.SceneManager; import com.google.android.apps.santatracker.games.simpleengine.SmoothValue; import com.google.android.apps.santatracker.games.simpleengine.SoundManager; import com.google.android.apps.santatracker.games.simpleengine.game.GameObject; import com.google.android.apps.santatracker.games.simpleengine.game.World; import com.google.android.apps.santatracker.games.simpleengine.ui.Button; import com.google.android.apps.santatracker.games.simpleengine.ui.SimpleUI; import com.google.android.apps.santatracker.games.simpleengine.ui.Widget; import com.google.android.apps.santatracker.util.ImmersiveModeHelper; import java.util.Random; public abstract class BaseScene extends Scene implements Widget.WidgetTriggerListener, GameEndedListener { // digit object factory (to display score, etc) protected DigitObjectFactory mDigitFactory; protected GameObjectFactory mObjectFactory; protected World mWorld; protected Renderer mRenderer; protected Random mRandom = new Random(); // score bar object protected GameObject mScoreBarObj; // score bar label object protected GameObject mScoreBarLabelObj; // score digit objects protected GameObject[] mScoreDigitObj = new GameObject[GameConfig.ScoreDisplay.DIGIT_COUNT]; // timer digit objects protected GameObject[] mTimeDigitObj = new GameObject[GameConfig.TimeDisplay.DIGIT_COUNT]; // countdown objects public GameObject mCountdownDigitObj; // player's current score protected int mScore = 0; protected SmoothValue mDisplayedScore = new SmoothValue(0.0f, GameConfig.ScoreDisplay.UPDATE_SPEED); // game ended? protected boolean mGameEnded = false; protected boolean mIsInGameEndingTransition = false; // our UI (buttons, etc) protected SimpleUI mUI = null; // widget trigger messages private static final int MSG_RETURN_WITH_VALUE = 1001; private static final int MSG_SIGN_IN = 1002; private static final int MSG_PAUSE = 1003; private static final int MSG_RESUME = 1004; private static final int MSG_QUIT = 1005; private static final int MSG_SHARE = 1006; private static final int MSG_REPLAY = 1007; private static final int MSG_MUTE = 1008; private static final int MSG_UNMUTE = 1009; private static final int MSG_START = 1010; protected static final int MSG_GO_TO_END_GAME = 1011; // sfx IDs protected int mGameOverSfx; // paused? protected boolean mPaused = false; // in start countdown? protected boolean mInStartCountdown = false; protected float mStartCountdownTimeRemaining; // back key pressed? private boolean mBackKeyPending = false; // DPAD_CENTER key pressed? private boolean mConfirmKeyPending; private long mConfirmKeyEventTime; private final long CENTER_KEY_DELAY_MS = 500; // isRunning on Tv? boolean mIsTv; // pause button Button mPauseButton; // speaker on and mute buttons Button mSpeakerOnButton, mSpeakerMuteButton; // pause curtain, that is, the full screen object we display as a translucent // screen over the whole display when the game is paused GameObject mPauseCurtain = null; // the big play button Button mBigPlayButton = null; Button mBigStartGameButton = null; // quit button Button mQuitButton = null; GameObject mQuitBarLabel = null; // game objects that compose the Sign In ui GameObject mSignInBarObj = null; Button mSignInButton = null; GameObject mSignInTextObj = null; // to be implemented by subclasses protected abstract String getBgmAssetFile(); protected abstract float getDisplayedTime(); protected abstract BaseScene makeNewScene(); // are we signed in private boolean mSignedIn = false; @Override public void onInstall() { // are we signed in? SceneActivity act = (SceneActivity) SceneManager.getInstance().getActivity(); if (act != null) { mSignedIn = act.isSignedIn(); UiModeManager manger = (UiModeManager)act.getSystemService(Context.UI_MODE_SERVICE); mIsTv = Configuration.UI_MODE_TYPE_TELEVISION == manger.getCurrentModeType(); } mRenderer = SceneManager.getInstance().getRenderer(); mWorld = new World(mRenderer); mDigitFactory = new DigitObjectFactory(mRenderer, mWorld); mDigitFactory.requestWhiteTextures(GameConfig.ScoreDisplay.DIGIT_SIZE); mObjectFactory = new GameObjectFactory(mRenderer, mWorld); mObjectFactory.requestTextures(); mUI = new SimpleUI(mRenderer); if (isTv()) { mDigitFactory.makeDigitObjects(GameConfig.ScoreDisplay.DIGIT_COUNT, GameConfig.TYPE_DECOR, mRenderer.getRelativePos(GameConfig.ScoreDisplay.POS_X_REL, GameConfig.ScoreDisplay.POS_X_DELTA), mRenderer.getRelativePos(GameConfig.ScoreDisplay.POS_Y_REL_TV, GameConfig.ScoreDisplay.POS_Y_DELTA_TV), GameConfig.ScoreDisplay.DIGIT_SIZE, GameConfig.ScoreDisplay.DIGIT_SPACING, mScoreDigitObj); mBigPlayButton = mObjectFactory.makeBigPlayButton(this, MSG_RESUME); mBigPlayButton.hide(); mUI.add(mBigPlayButton); float x = GameConfig.TimeDisplay.POS_X_DELTA + GameConfig.TimeDisplay.ICON_SIZE; mDigitFactory.makeDigitObjects(GameConfig.TimeDisplay.DIGIT_COUNT, GameConfig.TYPE_DECOR, mRenderer.getRelativePos(GameConfig.TimeDisplay.POS_X_REL, x), mRenderer.getRelativePos(GameConfig.TimeDisplay.POS_Y_REL_TV, GameConfig.TimeDisplay.POS_Y_DELTA_TV), GameConfig.TimeDisplay.DIGIT_SIZE, GameConfig.TimeDisplay.DIGIT_SPACING, mTimeDigitObj); mPauseCurtain = mObjectFactory.makePauseCurtain(); mPauseCurtain.hide(); } else { mScoreBarObj = mObjectFactory.makeScoreBar(); mScoreBarLabelObj = mObjectFactory.makeScoreBarLabel(); mDigitFactory.makeDigitObjects(GameConfig.ScoreDisplay.DIGIT_COUNT, GameConfig.TYPE_DECOR, mRenderer.getRelativePos(GameConfig.ScoreDisplay.POS_X_REL, GameConfig.ScoreDisplay.POS_X_DELTA), mRenderer.getRelativePos(GameConfig.ScoreDisplay.POS_Y_REL, GameConfig.ScoreDisplay.POS_Y_DELTA), GameConfig.ScoreDisplay.DIGIT_SIZE, GameConfig.ScoreDisplay.DIGIT_SPACING, mScoreDigitObj); hideObjects(mScoreDigitObj); bringObjectsToFront(getScoreDigitVisibleObj()); float x = GameConfig.TimeDisplay.POS_X_DELTA + GameConfig.TimeDisplay.ICON_SIZE; mDigitFactory.makeDigitObjects(GameConfig.TimeDisplay.DIGIT_COUNT, GameConfig.TYPE_DECOR, mRenderer.getRelativePos(GameConfig.TimeDisplay.POS_X_REL, x), mRenderer.getRelativePos(GameConfig.TimeDisplay.POS_Y_REL, GameConfig.TimeDisplay.POS_Y_DELTA), GameConfig.TimeDisplay.DIGIT_SIZE, GameConfig.TimeDisplay.DIGIT_SPACING, mTimeDigitObj); mCountdownDigitObj = mDigitFactory.makeDigitObject( GameConfig.TYPE_DECOR, 0.0f, 0.0f, GameConfig.Countdown.DIGIT_SIZE); mCountdownDigitObj.hide(); mQuitButton = mObjectFactory.makeQuitButton(this, MSG_QUIT); mQuitBarLabel = mObjectFactory.makeQuitBarLabel(); mQuitButton.hide(); mQuitBarLabel.hide(); mUI.add(mQuitButton); mPauseButton = mObjectFactory.makePauseButton(this, MSG_PAUSE); mUI.add(mPauseButton); mSpeakerMuteButton = mObjectFactory.makeSpeakerMuteButton(this, MSG_UNMUTE); mSpeakerOnButton = mObjectFactory.makeSpeakerOnButton(this, MSG_MUTE); mUI.add(mSpeakerMuteButton); mUI.add(mSpeakerOnButton); mPauseCurtain = mObjectFactory.makePauseCurtain(); mPauseCurtain.hide(); mBigPlayButton = mObjectFactory.makeBigPlayButton(this, MSG_RESUME); mBigPlayButton.hide(); mUI.add(mBigPlayButton); mBigStartGameButton = mObjectFactory.makeBigPlayButton(this, MSG_START); mBigStartGameButton.hide(); mUI.add(mBigStartGameButton); } SoundManager soundManager = SceneManager.getInstance().getSoundManager(); SceneManager.getInstance().loadMute(); if(soundManager != null) { soundManager.requestBackgroundMusic(getBgmAssetFile()); mGameOverSfx = soundManager.requestSfx(R.raw.jetpack_gameover); if(soundManager.getMute()) { muteSpeaker(); } else { unmuteSpeaker(); } } } @Override public void onUninstall() { } @Override public void doStandbyFrame(float deltaT) { } @Override public void doFrame(float deltaT) { if (mPaused) { deltaT = 0.0f; } if (mBackKeyPending) { processBackKey(); } if (mConfirmKeyPending) { // TODO: move a focus based on KeyEvent processCenterKey(); } // If Activity lost focus and we're playing the game, pause if (!SceneManager.getInstance().shouldBePlaying() && !mGameEnded && !mPaused) { pauseGame(); } if (!mGameEnded) { updateScore(deltaT); updateTime(deltaT); } else { updateScore(deltaT); checkSignInWidgetsNeeded(); } mWorld.doFrame(deltaT); } private void processBackKey() { mBackKeyPending = false; if (mGameEnded || (mPaused && mIsTv)) { quitGame(); } else if (mPaused) { unpauseGame(); } else { pauseGame(); } } private void processCenterKey() { final long currTime = System.currentTimeMillis(); if (currTime - mConfirmKeyEventTime < CENTER_KEY_DELAY_MS) { mBigPlayButton.setPressed(true); } else { mConfirmKeyPending = false; mBigPlayButton.setPressed(false); if (mPaused) { unpauseGame(); } else if (mGameEnded) { //re-start new game SceneManager.getInstance().requestNewScene(makeNewScene()); } } } private void updateScore(float deltaT) { if (mGameEnded) { mDisplayedScore.setValue(mScore); } else { mDisplayedScore.setTarget(mScore); mDisplayedScore.update(deltaT); } mDigitFactory.setDigits((int) Math.round(mDisplayedScore.getValue()), mScoreDigitObj); bringObjectsToFront(getScoreDigitVisibleObj()); } protected GameObject[] getScoreDigitVisibleObj() { int numDigits = Math.max(String.valueOf((int)mDisplayedScore.getValue()+1).length(), GameConfig.ScoreBar.MIN_DIGITS_VISIBLE); GameObject[] mScoreDigitVisibleObj = new GameObject[numDigits]; for(int i = mScoreDigitObj.length - numDigits; i < mScoreDigitObj.length; i++) { mScoreDigitVisibleObj[i - (mScoreDigitObj.length - numDigits)] = mScoreDigitObj[i]; } return mScoreDigitVisibleObj; } protected void endGame() { mGameEnded = true; // show the podium object mObjectFactory.makePodium(); // move score to final position float x = mRenderer.getRelativePos(GameConfig.Podium.ScoreDisplay.X_REL, GameConfig.Podium.ScoreDisplay.X_DELTA); float y = mRenderer.getRelativePos(GameConfig.Podium.ScoreDisplay.Y_REL, GameConfig.Podium.ScoreDisplay.Y_DELTA); displaceObjectsTo(mScoreDigitObj, x, y); bringObjectsToFront(getScoreDigitVisibleObj()); // hide time counter hideObjects(mTimeDigitObj); // make the "your score is" label mObjectFactory.makeScoreLabel(); // create the end of game UI and add the "play again" button to it mUI.add(mObjectFactory.makeReturnToMapButton(this, MSG_RETURN_WITH_VALUE)); if (isTv()) { //TODO: tv specific ui layout } else { // hide the score bar mScoreBarObj.hide(); mScoreBarLabelObj.hide(); mPauseButton.hide(); mSpeakerMuteButton.hide(); mSpeakerOnButton.hide(); // TODO: real message // create the sign in bar and sign in button if (!mSignedIn) { mSignInBarObj = mObjectFactory.makeSignInBar(); mSignInTextObj = mObjectFactory.makeSignInText(); mUI.add(mSignInButton = mObjectFactory.makeSignInButton(this, MSG_SIGN_IN)); } } // disable the background music SceneManager.getInstance().getSoundManager().enableBgm(false); // play the game over sfx SceneManager.getInstance().getSoundManager().playSfx(mGameOverSfx); } protected boolean isTv() { return mIsTv; } protected void displaceObjectsTo(GameObject[] objs, float x, float y) { float deltaX = x - objs[0].x; float deltaY = y - objs[0].y; int i; for (i = 0; i < objs.length; i++) { objs[i].displaceBy(deltaX, deltaY); } } protected void bringObjectsToFront(GameObject[] objs) { int i; for (i = 0; i < objs.length; i++) { objs[i].show(); objs[i].bringToFront(); } } protected void hideObjects(GameObject[] objs) { int i; for (i = 0; i < objs.length; i++) { objs[i].hide(); } } private void updateTime(float deltaT) { int seconds = (int) Math.ceil(getDisplayedTime()); seconds = seconds < 0 ? 0 : seconds > 99 ? 99 : seconds; mDigitFactory.setDigits(seconds, mTimeDigitObj); bringObjectsToFront(mTimeDigitObj); } @Override public void onScreenResized(int width, int height) { } @Override public void onPointerDown(int pointerId, float x, float y) { super.onPointerDown(pointerId, x, y); if (mUI != null) { mUI.onPointerDown(pointerId, x, y); } } @Override public void onPointerUp(int pointerId, float x, float y) { super.onPointerUp(pointerId, x, y); if (mUI != null) { mUI.onPointerUp(pointerId, x, y); } } protected void startGameScreen() { mPaused = true; SceneManager.getInstance().getSoundManager().stopSound(); if (isTv()) { mPauseCurtain.show(); mPauseCurtain.bringToFront(); mBigPlayButton.show(); mBigPlayButton.bringToFront(); } else { mPauseButton.hide(); mPauseCurtain.show(); mPauseCurtain.bringToFront(); mQuitButton.hide(); mQuitBarLabel.hide(); mSpeakerOnButton.bringToFront(); mSpeakerMuteButton.bringToFront(); mSpeakerMuteButton.setEnabled(true); mSpeakerOnButton.setEnabled(true); mBigStartGameButton.show(); mBigStartGameButton.bringToFront(); } if (Utils.hasKitKat() && SceneManager.getInstance().getActivity() != null) { SceneManager.getInstance().getActivity().runOnUiThread(new Runnable() { @Override public void run() { ImmersiveModeHelper.setImmersiveStickyWithActionBar( SceneManager.getInstance().getActivity().getWindow()); } }); } } protected void pauseGame() { if (!mPaused && !mGameEnded) { mPaused = true; SceneManager.getInstance().getSoundManager().stopSound(); if (isTv()) { mPauseCurtain.show(); mPauseCurtain.bringToFront(); mBigPlayButton.show(); mBigPlayButton.bringToFront(); } else { mPauseButton.hide(); mPauseCurtain.show(); mPauseCurtain.bringToFront(); mBigPlayButton.show(); mBigPlayButton.bringToFront(); mQuitButton.show(); mQuitBarLabel.show(); mQuitButton.bringToFront(); mQuitBarLabel.bringToFront(); mSpeakerMuteButton.setEnabled(false); mSpeakerOnButton.setEnabled(false); } if (Utils.hasKitKat() && SceneManager.getInstance().getActivity() != null) { SceneManager.getInstance().getActivity().runOnUiThread(new Runnable() { @Override public void run() { Activity activity = SceneManager.getInstance().getActivity(); if (activity != null) { ImmersiveModeHelper.setImmersiveStickyWithActionBar( activity.getWindow()); } } }); } } } protected void muteSpeaker() { if(!mGameEnded) { SceneManager.getInstance().getSoundManager().mute(); mSpeakerOnButton.hide(); mSpeakerMuteButton.show(); mSpeakerMuteButton.bringToFront(); } } protected void unmuteSpeaker() { if(!mGameEnded) { SceneManager.getInstance().getSoundManager().unmute(); mSpeakerMuteButton.hide(); mSpeakerOnButton.show(); mSpeakerOnButton.bringToFront(); } } protected void startCountdown() { mInStartCountdown = true; mStartCountdownTimeRemaining = GameConfig.Countdown.TIME; mBigStartGameButton.hide(); mDigitFactory.setDigit(mCountdownDigitObj, GameConfig.Countdown.TIME); mCountdownDigitObj.show(); mCountdownDigitObj.bringToFront(); } protected void unpauseGame() { if (!mPaused) { return; } mPaused = false; SceneManager.getInstance().getSoundManager().enableBgm(true); SceneManager.getInstance().getSoundManager().resumeSound(); if (isTv()) { mPauseCurtain.hide(); mBigPlayButton.hide(); } else { mPauseButton.show(); mPauseCurtain.hide(); mQuitButton.hide(); mQuitBarLabel.hide(); mBigPlayButton.hide(); mCountdownDigitObj.hide(); mSpeakerMuteButton.setEnabled(true); mSpeakerOnButton.setEnabled(true); } if (Utils.hasKitKat() && SceneManager.getInstance().getActivity() != null) { SceneManager.getInstance().getActivity().runOnUiThread(new Runnable() { @Override public void run() { ImmersiveModeHelper.setImmersiveSticky( SceneManager.getInstance().getActivity().getWindow()); } }); } } private int roundScore(int score) { score = (score / 50) * 50; return score <= 0 ? 50 : score; } @Override public void onPointerMove(int pointerId, float x, float y, float deltaX, float deltaY) { if (mUI != null) { mUI.onPointerMove(pointerId, x, y, deltaX, deltaY); } } @Override public void onWidgetTriggered(int message) { SceneActivity act; switch (message) { case MSG_RETURN_WITH_VALUE: returnWithScore(); break; case MSG_REPLAY: SceneManager.getInstance().requestNewScene(makeNewScene()); break; case MSG_SIGN_IN: act = (SceneActivity) SceneManager.getInstance().getActivity(); if (act != null) { // start sign in flow act.beginUserInitiatedSignIn(); } break; case MSG_PAUSE: pauseGame(); break; case MSG_RESUME: unpauseGame(); break; case MSG_QUIT: quitGame(); break; case MSG_SHARE: share(); break; case MSG_MUTE: muteSpeaker(); break; case MSG_UNMUTE: unmuteSpeaker(); break; case MSG_START: startCountdown(); break; case MSG_GO_TO_END_GAME: goToEndGameWithDelay(GameConfig.EndGame.DELAY); break; } } private void quitGame() { Activity act = SceneManager.getInstance().getActivity(); if (act != null && act instanceof SceneActivity) { ((SceneActivity) act).postQuitGame(); } } private void returnWithScore() { Activity act = SceneManager.getInstance().getActivity(); if (act != null && act instanceof SceneActivity) { ((SceneActivity) act).postReturnWithScore(mScore); } } protected void goToEndGameWithDelay(int delay) { Activity act = SceneManager.getInstance().getActivity(); ((SceneActivity)act).setGameEndedListener(this); if (act != null && act instanceof SceneActivity) { ((SceneActivity) act).postDelayedGoToEndGame(delay); } } protected void goToEndGame() { Activity act = SceneManager.getInstance().getActivity(); if (act != null && act instanceof SceneActivity) { ((SceneActivity)act).setGameEndedListener(this); ((SceneActivity) act).postGoToEndGame(); } } @Override public void onGameEnded() { mGameEnded = true; } @Override public int getScore() { return mScore; } private void share() { Activity act = SceneManager.getInstance().getActivity(); if (act != null && act instanceof SceneActivity) { ((SceneActivity) act).share(); } } private void checkSignInWidgetsNeeded() { if (mSignedIn) { if (mSignInBarObj != null) { mSignInBarObj.hide(); } if (mSignInTextObj != null) { mSignInTextObj.hide(); } if (mSignInButton != null) { mSignInButton.hide(); } } } // Caution: Called from the UI thread! public void setSignedIn(boolean signedIn) { mSignedIn = signedIn; } // Caution: Called from the UI thread! public boolean onBackKeyPressed() { // raise a flag and process later (on the game thread) mBackKeyPending = true; return true; } // Caution: Called from the UI thread! public boolean onConfirmKeyPressed() { // raise a flag and process later (on the game thread) if (mConfirmKeyPending) { return true; } mConfirmKeyPending = true; mConfirmKeyEventTime = System.currentTimeMillis(); return true; } }