/* * Copyright (C) 2010 The Android Open Source Project * * 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.replica.replicaisland; import android.content.Context; import android.os.Build; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.WindowManager; import android.widget.Toast; /** * High-level setup object for the AndouKun game engine. * This class sets up the core game engine objects and threads. It also passes events to the * game thread from the main UI thread. */ public class Game extends AllocationGuard { private GameThread mGameThread; private Thread mGame; private ObjectManager mGameRoot; private GameRenderer mRenderer; private GLSurfaceView mSurfaceView; private boolean mRunning; private boolean mBootstrapComplete; private LevelTree.Level mPendingLevel; private LevelTree.Level mCurrentLevel; private LevelTree.Level mLastLevel; private boolean mGLDataLoaded; private ContextParameters mContextParameters; private TouchFilter mTouchFilter; public Game() { super(); mRunning = false; mBootstrapComplete = false; mGLDataLoaded = false; mContextParameters = new ContextParameters(); } /** * Creates core game objects and constructs the game engine object graph. Note that the * game does not actually begin running after this function is called (see start() below). * Also note that textures are not loaded from the resource pack by this function, as OpenGl * isn't yet available. * @param context */ public void bootstrap(Context context, int viewWidth, int viewHeight, int gameWidth, int gameHeight, int difficulty) { if (!mBootstrapComplete) { mRenderer = new GameRenderer(context, this, gameWidth, gameHeight); // Create core systems BaseObject.sSystemRegistry.openGLSystem = new OpenGLSystem(null); BaseObject.sSystemRegistry.customToastSystem = new CustomToastSystem(context); ContextParameters params = mContextParameters; params.viewWidth = viewWidth; params.viewHeight = viewHeight; params.gameWidth = gameWidth; params.gameHeight = gameHeight; params.viewScaleX = (float)viewWidth / gameWidth; params.viewScaleY = (float)viewHeight / gameHeight; params.context = context; params.difficulty = difficulty; BaseObject.sSystemRegistry.contextParameters = params; final int sdkVersion = Integer.parseInt(Build.VERSION.SDK); if (sdkVersion < Build.VERSION_CODES.ECLAIR) { mTouchFilter = new SingleTouchFilter(); } else { mTouchFilter = new MultiTouchFilter(); } // Short-term textures are cleared between levels. TextureLibrary shortTermTextureLibrary = new TextureLibrary(); BaseObject.sSystemRegistry.shortTermTextureLibrary = shortTermTextureLibrary; // Long-term textures persist between levels. TextureLibrary longTermTextureLibrary = new TextureLibrary(); BaseObject.sSystemRegistry.longTermTextureLibrary = longTermTextureLibrary; // The buffer library manages hardware VBOs. BaseObject.sSystemRegistry.bufferLibrary = new BufferLibrary(); BaseObject.sSystemRegistry.soundSystem = new SoundSystem(); // The root of the game graph. MainLoop gameRoot = new MainLoop(); InputSystem input = new InputSystem(); BaseObject.sSystemRegistry.inputSystem = input; BaseObject.sSystemRegistry.registerForReset(input); WindowManager windowMgr = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE); int rotationIndex = windowMgr.getDefaultDisplay().getOrientation(); input.setScreenRotation(rotationIndex); InputGameInterface inputInterface = new InputGameInterface(); gameRoot.add(inputInterface); BaseObject.sSystemRegistry.inputGameInterface = inputInterface; LevelSystem level = new LevelSystem(); BaseObject.sSystemRegistry.levelSystem = level; CollisionSystem collision = new CollisionSystem(); BaseObject.sSystemRegistry.collisionSystem = collision; BaseObject.sSystemRegistry.hitPointPool = new HitPointPool(); GameObjectManager gameManager = new GameObjectManager(params.viewWidth * 2); BaseObject.sSystemRegistry.gameObjectManager = gameManager; GameObjectFactory objectFactory = new GameObjectFactory(); BaseObject.sSystemRegistry.gameObjectFactory = objectFactory; BaseObject.sSystemRegistry.hotSpotSystem = new HotSpotSystem(); BaseObject.sSystemRegistry.levelBuilder = new LevelBuilder(); BaseObject.sSystemRegistry.channelSystem = new ChannelSystem(); BaseObject.sSystemRegistry.registerForReset(BaseObject.sSystemRegistry.channelSystem); CameraSystem camera = new CameraSystem(); BaseObject.sSystemRegistry.cameraSystem = camera; BaseObject.sSystemRegistry.registerForReset(camera); collision.loadCollisionTiles(context.getResources().openRawResource(R.raw.collision)); gameRoot.add(gameManager); // Camera must come after the game manager so that the camera target moves before the camera // centers. gameRoot.add(camera); // More basic systems. GameObjectCollisionSystem dynamicCollision = new GameObjectCollisionSystem(); gameRoot.add(dynamicCollision); BaseObject.sSystemRegistry.gameObjectCollisionSystem = dynamicCollision; RenderSystem renderer = new RenderSystem(); BaseObject.sSystemRegistry.renderSystem = renderer; BaseObject.sSystemRegistry.vectorPool = new VectorPool(); BaseObject.sSystemRegistry.drawableFactory = new DrawableFactory(); HudSystem hud = new HudSystem(); hud.setFuelDrawable( new DrawableBitmap(longTermTextureLibrary.allocateTexture( R.drawable.ui_bar), 0, 0), new DrawableBitmap(longTermTextureLibrary.allocateTexture( R.drawable.ui_bar_bg), 0, 0)); hud.setFadeTexture(longTermTextureLibrary.allocateTexture(R.drawable.black)); hud.setButtonDrawables( new DrawableBitmap(longTermTextureLibrary.allocateTexture( R.drawable.ui_button_fly_disabled), 0, 0), new DrawableBitmap(longTermTextureLibrary.allocateTexture( R.drawable.ui_button_fly_off), 0, 0), new DrawableBitmap(longTermTextureLibrary.allocateTexture( R.drawable.ui_button_fly_on), 0, 0), new DrawableBitmap(longTermTextureLibrary.allocateTexture( R.drawable.ui_button_stomp_off), 0, 0), new DrawableBitmap(longTermTextureLibrary.allocateTexture( R.drawable.ui_button_stomp_on), 0, 0), new DrawableBitmap(longTermTextureLibrary.allocateTexture( R.drawable.ui_movement_slider_base), 0, 0), new DrawableBitmap(longTermTextureLibrary.allocateTexture( R.drawable.ui_movement_slider_button_off), 0, 0), new DrawableBitmap(longTermTextureLibrary.allocateTexture( R.drawable.ui_movement_slider_button_on), 0, 0)); Texture[] digitTextures = { longTermTextureLibrary.allocateTexture(R.drawable.ui_0), longTermTextureLibrary.allocateTexture(R.drawable.ui_1), longTermTextureLibrary.allocateTexture(R.drawable.ui_2), longTermTextureLibrary.allocateTexture(R.drawable.ui_3), longTermTextureLibrary.allocateTexture(R.drawable.ui_4), longTermTextureLibrary.allocateTexture(R.drawable.ui_5), longTermTextureLibrary.allocateTexture(R.drawable.ui_6), longTermTextureLibrary.allocateTexture(R.drawable.ui_7), longTermTextureLibrary.allocateTexture(R.drawable.ui_8), longTermTextureLibrary.allocateTexture(R.drawable.ui_9) }; DrawableBitmap[] digits = { new DrawableBitmap(digitTextures[0], 0, 0), new DrawableBitmap(digitTextures[1], 0, 0), new DrawableBitmap(digitTextures[2], 0, 0), new DrawableBitmap(digitTextures[3], 0, 0), new DrawableBitmap(digitTextures[4], 0, 0), new DrawableBitmap(digitTextures[5], 0, 0), new DrawableBitmap(digitTextures[6], 0, 0), new DrawableBitmap(digitTextures[7], 0, 0), new DrawableBitmap(digitTextures[8], 0, 0), new DrawableBitmap(digitTextures[9], 0, 0) }; DrawableBitmap xDrawable = new DrawableBitmap( longTermTextureLibrary.allocateTexture(R.drawable.ui_x), 0, 0); hud.setDigitDrawables(digits, xDrawable); hud.setCollectableDrawables( new DrawableBitmap( longTermTextureLibrary.allocateTexture(R.drawable.ui_pearl), 0, 0), new DrawableBitmap( longTermTextureLibrary.allocateTexture(R.drawable.ui_gem), 0, 0)); BaseObject.sSystemRegistry.hudSystem = hud; if (AndouKun.VERSION < 0) { hud.setShowFPS(true); } gameRoot.add(hud); BaseObject.sSystemRegistry.vibrationSystem = new VibrationSystem(); EventRecorder eventRecorder = new EventRecorder(); BaseObject.sSystemRegistry.eventRecorder = eventRecorder; BaseObject.sSystemRegistry.registerForReset(eventRecorder); gameRoot.add(collision); // debug systems //BaseObject.sSystemRegistry.debugSystem = new DebugSystem(longTermTextureLibrary); //dynamicCollision.setDebugPrefs(false, true); objectFactory.preloadEffects(); mGameRoot = gameRoot; mGameThread = new GameThread(mRenderer); mGameThread.setGameRoot(mGameRoot); mCurrentLevel = null; mBootstrapComplete = true; } } protected synchronized void stopLevel() { stop(); GameObjectManager manager = BaseObject.sSystemRegistry.gameObjectManager; manager.destroyAll(); manager.commitUpdates(); //TODO: it's not strictly necessary to clear the static data here, but if I don't do it // then two things happen: first, the static data will refer to junk Texture objects, and // second, memory that may not be needed for the next level will hang around. One solution // would be to break up the texture library into static and non-static things, and // then selectively clear static game components based on their usefulness next level, // but this is way simpler. GameObjectFactory factory = BaseObject.sSystemRegistry.gameObjectFactory; factory.clearStaticData(); factory.sanityCheckPools(); // Reset the level BaseObject.sSystemRegistry.levelSystem.reset(); // Ensure sounds have stopped. BaseObject.sSystemRegistry.soundSystem.stopAll(); // Reset systems that need it. BaseObject.sSystemRegistry.reset(); // Dump the short-term texture objects only. mSurfaceView.flushTextures(BaseObject.sSystemRegistry.shortTermTextureLibrary); BaseObject.sSystemRegistry.shortTermTextureLibrary.removeAll(); mSurfaceView.flushBuffers(BaseObject.sSystemRegistry.bufferLibrary); BaseObject.sSystemRegistry.bufferLibrary.removeAll(); } public synchronized void requestNewLevel() { // tell the Renderer to call us back when the // render thread is ready to manage some texture memory. mRenderer.requestCallback(); } public synchronized void restartLevel() { DebugLog.d("AndouKun", "Restarting..."); final LevelTree.Level level = mCurrentLevel; stop(); // Destroy all game objects and respawn them. No need to destroy other systems. GameObjectManager manager = BaseObject.sSystemRegistry.gameObjectManager; manager.destroyAll(); manager.commitUpdates(); // Ensure sounds have stopped. BaseObject.sSystemRegistry.soundSystem.stopAll(); // Reset systems that need it. BaseObject.sSystemRegistry.reset(); LevelSystem levelSystem = BaseObject.sSystemRegistry.levelSystem; levelSystem.incrementAttemptsCount(); levelSystem.spawnObjects(); BaseObject.sSystemRegistry.hudSystem.startFade(true, 0.2f); mCurrentLevel = level; mPendingLevel = null; start(); } protected synchronized void goToLevel(LevelTree.Level level) { ContextParameters params = BaseObject.sSystemRegistry.contextParameters; BaseObject.sSystemRegistry.levelSystem.loadLevel(level, params.context.getResources().openRawResource(level.resource), mGameRoot); Context context = params.context; mRenderer.setContext(context); mSurfaceView.loadTextures(BaseObject.sSystemRegistry.longTermTextureLibrary); mSurfaceView.loadTextures(BaseObject.sSystemRegistry.shortTermTextureLibrary); mSurfaceView.loadBuffers(BaseObject.sSystemRegistry.bufferLibrary); mGLDataLoaded = true; mCurrentLevel = level; mPendingLevel = null; TimeSystem time = BaseObject.sSystemRegistry.timeSystem; time.reset(); HudSystem hud = BaseObject.sSystemRegistry.hudSystem; if (hud != null) { hud.startFade(true, 1.0f); } CustomToastSystem toast = BaseObject.sSystemRegistry.customToastSystem; if (toast != null) { if (level.inThePast) { toast.toast(context.getString(R.string.memory_playback_start), Toast.LENGTH_LONG); } else { if (mLastLevel != null && mLastLevel.inThePast) { toast.toast(context.getString(R.string.memory_playback_complete), Toast.LENGTH_LONG); } } } mLastLevel = level; start(); } /** Starts the game running. */ public void start() { if (!mRunning) { assert mGame == null; // Now's a good time to run the GC. Runtime r = Runtime.getRuntime(); r.gc(); DebugLog.d("AndouKun", "Start!"); mGame = new Thread(mGameThread); mGame.setName("Game"); mGame.start(); mRunning = true; AllocationGuard.sGuardActive = false; } else { mGameThread.resumeGame(); } } public void stop() { if (mRunning) { DebugLog.d("AndouKun", "Stop!"); if (mGameThread.getPaused()) { mGameThread.resumeGame(); } mGameThread.stopGame(); try { mGame.join(); } catch (InterruptedException e) { mGame.interrupt(); } mGame = null; mRunning = false; mCurrentLevel = null; AllocationGuard.sGuardActive = false; } } public boolean onTrackballEvent(MotionEvent event) { if (mRunning) { if (event.getAction() == MotionEvent.ACTION_MOVE) { BaseObject.sSystemRegistry.inputSystem.roll(event.getRawX(), event.getRawY()); } else if (event.getAction() == MotionEvent.ACTION_DOWN) { onKeyDownEvent(KeyEvent.KEYCODE_DPAD_CENTER); } else if (event.getAction() == MotionEvent.ACTION_UP) { onKeyUpEvent(KeyEvent.KEYCODE_DPAD_CENTER); } } return true; } public boolean onOrientationEvent(float x, float y, float z) { if (mRunning) { BaseObject.sSystemRegistry.inputSystem.setOrientation(x, y, z); } return true; } public boolean onTouchEvent(MotionEvent event) { if (mRunning) { mTouchFilter.updateTouch(event); } return true; } public boolean onKeyDownEvent(int keyCode) { boolean result = false; if (mRunning) { BaseObject.sSystemRegistry.inputSystem.keyDown(keyCode); } return result; } public boolean onKeyUpEvent(int keyCode) { boolean result = false; if (mRunning) { BaseObject.sSystemRegistry.inputSystem.keyUp(keyCode); } return result; } public GameRenderer getRenderer() { return mRenderer; } public void onPause() { if (mRunning) { mGameThread.pauseGame(); } } public void onResume(Context context, boolean force) { if (force && mRunning) { mGameThread.resumeGame(); } else { mRenderer.setContext(context); // Don't explicitly resume the game here. We'll do that in // the SurfaceReady() callback, which will prevent the game // starting before the render thread is ready to go. BaseObject.sSystemRegistry.contextParameters.context = context; } } public void onSurfaceReady() { DebugLog.d("AndouKun", "Surface Ready"); if (mPendingLevel != null && mPendingLevel != mCurrentLevel) { if (mRunning) { stopLevel(); } goToLevel(mPendingLevel); } else if (mGameThread.getPaused() && mRunning) { mGameThread.resumeGame(); } } public void setSurfaceView(GLSurfaceView view) { mSurfaceView = view; } public void onSurfaceLost() { DebugLog.d("AndouKun", "Surface Lost"); BaseObject.sSystemRegistry.shortTermTextureLibrary.invalidateAll(); BaseObject.sSystemRegistry.longTermTextureLibrary.invalidateAll(); BaseObject.sSystemRegistry.bufferLibrary.invalidateHardwareBuffers(); mGLDataLoaded = false; } public void onSurfaceCreated() { DebugLog.d("AndouKun", "Surface Created"); // TODO: this is dumb. SurfaceView doesn't need to control everything here. // GL should just be passed to this function and then set up directly. if (!mGLDataLoaded && mGameThread.getPaused() && mRunning && mPendingLevel == null) { mSurfaceView.loadTextures(BaseObject.sSystemRegistry.longTermTextureLibrary); mSurfaceView.loadTextures(BaseObject.sSystemRegistry.shortTermTextureLibrary); mSurfaceView.loadBuffers(BaseObject.sSystemRegistry.bufferLibrary); mGLDataLoaded = true; } } public void setPendingLevel(LevelTree.Level level) { mPendingLevel = level; } public void setSoundEnabled(boolean soundEnabled) { BaseObject.sSystemRegistry.soundSystem.setSoundEnabled(soundEnabled); } public void setControlOptions(boolean clickAttack, boolean tiltControls, int tiltSensitivity, int movementSensitivity, boolean onScreenControls) { BaseObject.sSystemRegistry.inputGameInterface.setUseClickForAttack(clickAttack); BaseObject.sSystemRegistry.inputGameInterface.setUseOrientationForMovement(tiltControls); BaseObject.sSystemRegistry.inputGameInterface.setOrientationMovementSensitivity((tiltSensitivity / 100.0f)); BaseObject.sSystemRegistry.inputGameInterface.setMovementSensitivity((movementSensitivity / 100.0f)); BaseObject.sSystemRegistry.inputGameInterface.setUseOnScreenControls(onScreenControls); BaseObject.sSystemRegistry.hudSystem.setMovementSliderMode(onScreenControls); } public void setSafeMode(boolean safe) { mSurfaceView.setSafeMode(safe); } public float getGameTime() { return BaseObject.sSystemRegistry.timeSystem.getGameTime(); } public Vector2 getLastDeathPosition() { return BaseObject.sSystemRegistry.eventRecorder.getLastDeathPosition(); } public void setLastEnding(int ending) { BaseObject.sSystemRegistry.eventRecorder.setLastEnding(ending); } public int getLastEnding() { return BaseObject.sSystemRegistry.eventRecorder.getLastEnding(); } public boolean isPaused() { return (mRunning && mGameThread != null && mGameThread.getPaused()); } public void setKeyConfig(int leftKey, int rightKey, int jumpKey, int attackKey) { BaseObject.sSystemRegistry.inputGameInterface.setKeys(leftKey, rightKey, jumpKey, attackKey); } public int getRobotsDestroyed() { return BaseObject.sSystemRegistry.eventRecorder.getRobotsDestroyed(); } public int getPearlsCollected() { return BaseObject.sSystemRegistry.eventRecorder.getPearlsCollected(); } public int getPearlsTotal() { return BaseObject.sSystemRegistry.eventRecorder.getPearlsTotal(); } }