Home | History | Annotate | Download | only in replicaisland
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.replica.replicaisland;
     18 
     19 import android.content.Context;
     20 import android.os.Build;
     21 import android.view.KeyEvent;
     22 import android.view.MotionEvent;
     23 import android.view.WindowManager;
     24 import android.widget.Toast;
     25 
     26 /**
     27  * High-level setup object for the AndouKun game engine.
     28  * This class sets up the core game engine objects and threads.  It also passes events to the
     29  * game thread from the main UI thread.
     30  */
     31 public class Game extends AllocationGuard {
     32     private GameThread mGameThread;
     33     private Thread mGame;
     34     private ObjectManager mGameRoot;
     35 
     36     private GameRenderer mRenderer;
     37     private GLSurfaceView mSurfaceView;
     38     private boolean mRunning;
     39     private boolean mBootstrapComplete;
     40     private LevelTree.Level mPendingLevel;
     41     private LevelTree.Level mCurrentLevel;
     42     private LevelTree.Level mLastLevel;
     43     private boolean mGLDataLoaded;
     44     private ContextParameters mContextParameters;
     45     private TouchFilter mTouchFilter;
     46 
     47     public Game() {
     48         super();
     49         mRunning = false;
     50         mBootstrapComplete = false;
     51         mGLDataLoaded = false;
     52         mContextParameters = new ContextParameters();
     53     }
     54 
     55     /**
     56      * Creates core game objects and constructs the game engine object graph.  Note that the
     57      * game does not actually begin running after this function is called (see start() below).
     58      * Also note that textures are not loaded from the resource pack by this function, as OpenGl
     59      * isn't yet available.
     60      * @param context
     61      */
     62     public void bootstrap(Context context, int viewWidth, int viewHeight, int gameWidth, int gameHeight, int difficulty) {
     63         if (!mBootstrapComplete) {
     64             mRenderer = new GameRenderer(context, this, gameWidth, gameHeight);
     65 
     66             // Create core systems
     67             BaseObject.sSystemRegistry.openGLSystem = new OpenGLSystem(null);
     68 
     69             BaseObject.sSystemRegistry.customToastSystem = new CustomToastSystem(context);
     70 
     71             ContextParameters params = mContextParameters;
     72             params.viewWidth = viewWidth;
     73             params.viewHeight = viewHeight;
     74             params.gameWidth = gameWidth;
     75             params.gameHeight = gameHeight;
     76             params.viewScaleX = (float)viewWidth / gameWidth;
     77             params.viewScaleY = (float)viewHeight / gameHeight;
     78             params.context = context;
     79             params.difficulty = difficulty;
     80             BaseObject.sSystemRegistry.contextParameters = params;
     81 
     82             final int sdkVersion = Integer.parseInt(Build.VERSION.SDK);
     83             if (sdkVersion < Build.VERSION_CODES.ECLAIR) {
     84             	mTouchFilter = new SingleTouchFilter();
     85             } else {
     86             	mTouchFilter = new MultiTouchFilter();
     87             }
     88 
     89             // Short-term textures are cleared between levels.
     90             TextureLibrary shortTermTextureLibrary = new TextureLibrary();
     91             BaseObject.sSystemRegistry.shortTermTextureLibrary = shortTermTextureLibrary;
     92 
     93             // Long-term textures persist between levels.
     94             TextureLibrary longTermTextureLibrary = new TextureLibrary();
     95             BaseObject.sSystemRegistry.longTermTextureLibrary = longTermTextureLibrary;
     96 
     97             // The buffer library manages hardware VBOs.
     98             BaseObject.sSystemRegistry.bufferLibrary = new BufferLibrary();
     99 
    100 
    101 
    102             BaseObject.sSystemRegistry.soundSystem = new SoundSystem();
    103 
    104             // The root of the game graph.
    105             MainLoop gameRoot = new MainLoop();
    106 
    107             InputSystem input = new InputSystem();
    108             BaseObject.sSystemRegistry.inputSystem = input;
    109             BaseObject.sSystemRegistry.registerForReset(input);
    110 
    111             WindowManager windowMgr = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
    112             int rotationIndex = windowMgr.getDefaultDisplay().getOrientation();
    113             input.setScreenRotation(rotationIndex);
    114 
    115             InputGameInterface inputInterface = new InputGameInterface();
    116             gameRoot.add(inputInterface);
    117             BaseObject.sSystemRegistry.inputGameInterface = inputInterface;
    118 
    119             LevelSystem level = new LevelSystem();
    120             BaseObject.sSystemRegistry.levelSystem = level;
    121 
    122             CollisionSystem collision = new CollisionSystem();
    123             BaseObject.sSystemRegistry.collisionSystem = collision;
    124             BaseObject.sSystemRegistry.hitPointPool = new HitPointPool();
    125 
    126             GameObjectManager gameManager = new GameObjectManager(params.viewWidth * 2);
    127             BaseObject.sSystemRegistry.gameObjectManager = gameManager;
    128 
    129             GameObjectFactory objectFactory = new GameObjectFactory();
    130             BaseObject.sSystemRegistry.gameObjectFactory = objectFactory;
    131 
    132             BaseObject.sSystemRegistry.hotSpotSystem = new HotSpotSystem();
    133 
    134             BaseObject.sSystemRegistry.levelBuilder = new LevelBuilder();
    135 
    136             BaseObject.sSystemRegistry.channelSystem = new ChannelSystem();
    137             BaseObject.sSystemRegistry.registerForReset(BaseObject.sSystemRegistry.channelSystem);
    138 
    139             CameraSystem camera = new CameraSystem();
    140 
    141 
    142             BaseObject.sSystemRegistry.cameraSystem = camera;
    143             BaseObject.sSystemRegistry.registerForReset(camera);
    144 
    145             collision.loadCollisionTiles(context.getResources().openRawResource(R.raw.collision));
    146 
    147             gameRoot.add(gameManager);
    148 
    149             // Camera must come after the game manager so that the camera target moves before the camera
    150             // centers.
    151 
    152             gameRoot.add(camera);
    153 
    154 
    155             // More basic systems.
    156 
    157             GameObjectCollisionSystem dynamicCollision = new GameObjectCollisionSystem();
    158             gameRoot.add(dynamicCollision);
    159             BaseObject.sSystemRegistry.gameObjectCollisionSystem = dynamicCollision;
    160 
    161 
    162             RenderSystem renderer = new RenderSystem();
    163             BaseObject.sSystemRegistry.renderSystem = renderer;
    164             BaseObject.sSystemRegistry.vectorPool = new VectorPool();
    165             BaseObject.sSystemRegistry.drawableFactory = new DrawableFactory();
    166 
    167             HudSystem hud = new HudSystem();
    168             hud.setFuelDrawable(
    169                     new DrawableBitmap(longTermTextureLibrary.allocateTexture(
    170                             R.drawable.ui_bar), 0, 0),
    171                     new DrawableBitmap(longTermTextureLibrary.allocateTexture(
    172                             R.drawable.ui_bar_bg), 0, 0));
    173             hud.setFadeTexture(longTermTextureLibrary.allocateTexture(R.drawable.black));
    174             hud.setButtonDrawables(
    175                     new DrawableBitmap(longTermTextureLibrary.allocateTexture(
    176                             R.drawable.ui_button_fly_disabled), 0, 0),
    177                     new DrawableBitmap(longTermTextureLibrary.allocateTexture(
    178                             R.drawable.ui_button_fly_off), 0, 0),
    179                     new DrawableBitmap(longTermTextureLibrary.allocateTexture(
    180                             R.drawable.ui_button_fly_on), 0, 0),
    181                     new DrawableBitmap(longTermTextureLibrary.allocateTexture(
    182                             R.drawable.ui_button_stomp_off), 0, 0),
    183                     new DrawableBitmap(longTermTextureLibrary.allocateTexture(
    184                             R.drawable.ui_button_stomp_on), 0, 0),
    185                     new DrawableBitmap(longTermTextureLibrary.allocateTexture(
    186                             R.drawable.ui_movement_slider_base), 0, 0),
    187                     new DrawableBitmap(longTermTextureLibrary.allocateTexture(
    188                             R.drawable.ui_movement_slider_button_off), 0, 0),
    189                     new DrawableBitmap(longTermTextureLibrary.allocateTexture(
    190                             R.drawable.ui_movement_slider_button_on), 0, 0));
    191             Texture[] digitTextures = {
    192                     longTermTextureLibrary.allocateTexture(R.drawable.ui_0),
    193                     longTermTextureLibrary.allocateTexture(R.drawable.ui_1),
    194                     longTermTextureLibrary.allocateTexture(R.drawable.ui_2),
    195                     longTermTextureLibrary.allocateTexture(R.drawable.ui_3),
    196                     longTermTextureLibrary.allocateTexture(R.drawable.ui_4),
    197                     longTermTextureLibrary.allocateTexture(R.drawable.ui_5),
    198                     longTermTextureLibrary.allocateTexture(R.drawable.ui_6),
    199                     longTermTextureLibrary.allocateTexture(R.drawable.ui_7),
    200                     longTermTextureLibrary.allocateTexture(R.drawable.ui_8),
    201                     longTermTextureLibrary.allocateTexture(R.drawable.ui_9)
    202             };
    203             DrawableBitmap[] digits = {
    204                     new DrawableBitmap(digitTextures[0], 0, 0),
    205                     new DrawableBitmap(digitTextures[1], 0, 0),
    206                     new DrawableBitmap(digitTextures[2], 0, 0),
    207                     new DrawableBitmap(digitTextures[3], 0, 0),
    208                     new DrawableBitmap(digitTextures[4], 0, 0),
    209                     new DrawableBitmap(digitTextures[5], 0, 0),
    210                     new DrawableBitmap(digitTextures[6], 0, 0),
    211                     new DrawableBitmap(digitTextures[7], 0, 0),
    212                     new DrawableBitmap(digitTextures[8], 0, 0),
    213                     new DrawableBitmap(digitTextures[9], 0, 0)
    214             };
    215             DrawableBitmap xDrawable = new DrawableBitmap(
    216                     longTermTextureLibrary.allocateTexture(R.drawable.ui_x), 0, 0);
    217 
    218             hud.setDigitDrawables(digits, xDrawable);
    219             hud.setCollectableDrawables(
    220                     new DrawableBitmap(
    221                             longTermTextureLibrary.allocateTexture(R.drawable.ui_pearl), 0, 0),
    222                     new DrawableBitmap(
    223                             longTermTextureLibrary.allocateTexture(R.drawable.ui_gem), 0, 0));
    224 
    225             BaseObject.sSystemRegistry.hudSystem = hud;
    226             if (AndouKun.VERSION < 0) {
    227             	hud.setShowFPS(true);
    228             }
    229             gameRoot.add(hud);
    230 
    231             BaseObject.sSystemRegistry.vibrationSystem = new VibrationSystem();
    232 
    233             EventRecorder eventRecorder = new EventRecorder();
    234             BaseObject.sSystemRegistry.eventRecorder = eventRecorder;
    235             BaseObject.sSystemRegistry.registerForReset(eventRecorder);
    236 
    237             gameRoot.add(collision);
    238 
    239             // debug systems
    240             //BaseObject.sSystemRegistry.debugSystem = new DebugSystem(longTermTextureLibrary);
    241             //dynamicCollision.setDebugPrefs(false, true);
    242 
    243 
    244             objectFactory.preloadEffects();
    245 
    246             mGameRoot = gameRoot;
    247 
    248             mGameThread = new GameThread(mRenderer);
    249             mGameThread.setGameRoot(mGameRoot);
    250 
    251 
    252             mCurrentLevel = null;
    253 
    254             mBootstrapComplete = true;
    255         }
    256     }
    257 
    258 
    259     protected synchronized void stopLevel() {
    260         stop();
    261         GameObjectManager manager = BaseObject.sSystemRegistry.gameObjectManager;
    262         manager.destroyAll();
    263         manager.commitUpdates();
    264 
    265         //TODO: it's not strictly necessary to clear the static data here, but if I don't do it
    266         // then two things happen: first, the static data will refer to junk Texture objects, and
    267         // second, memory that may not be needed for the next level will hang around.  One solution
    268         // would be to break up the texture library into static and non-static things, and
    269         // then selectively clear static game components based on their usefulness next level,
    270         // but this is way simpler.
    271         GameObjectFactory factory = BaseObject.sSystemRegistry.gameObjectFactory;
    272         factory.clearStaticData();
    273         factory.sanityCheckPools();
    274 
    275         // Reset the level
    276         BaseObject.sSystemRegistry.levelSystem.reset();
    277 
    278         // Ensure sounds have stopped.
    279         BaseObject.sSystemRegistry.soundSystem.stopAll();
    280 
    281         // Reset systems that need it.
    282         BaseObject.sSystemRegistry.reset();
    283 
    284         // Dump the short-term texture objects only.
    285         mSurfaceView.flushTextures(BaseObject.sSystemRegistry.shortTermTextureLibrary);
    286         BaseObject.sSystemRegistry.shortTermTextureLibrary.removeAll();
    287         mSurfaceView.flushBuffers(BaseObject.sSystemRegistry.bufferLibrary);
    288         BaseObject.sSystemRegistry.bufferLibrary.removeAll();
    289     }
    290 
    291     public synchronized void requestNewLevel() {
    292     	// tell the Renderer to call us back when the
    293     	// render thread is ready to manage some texture memory.
    294     	mRenderer.requestCallback();
    295     }
    296 
    297     public synchronized void restartLevel() {
    298         DebugLog.d("AndouKun", "Restarting...");
    299         final LevelTree.Level level = mCurrentLevel;
    300         stop();
    301 
    302         // Destroy all game objects and respawn them.  No need to destroy other systems.
    303         GameObjectManager manager = BaseObject.sSystemRegistry.gameObjectManager;
    304         manager.destroyAll();
    305         manager.commitUpdates();
    306 
    307         // Ensure sounds have stopped.
    308         BaseObject.sSystemRegistry.soundSystem.stopAll();
    309 
    310         // Reset systems that need it.
    311         BaseObject.sSystemRegistry.reset();
    312 
    313         LevelSystem levelSystem = BaseObject.sSystemRegistry.levelSystem;
    314         levelSystem.incrementAttemptsCount();
    315         levelSystem.spawnObjects();
    316 
    317         BaseObject.sSystemRegistry.hudSystem.startFade(true, 0.2f);
    318 
    319         mCurrentLevel = level;
    320         mPendingLevel = null;
    321         start();
    322     }
    323 
    324     protected synchronized void goToLevel(LevelTree.Level level) {
    325 
    326         ContextParameters params = BaseObject.sSystemRegistry.contextParameters;
    327         BaseObject.sSystemRegistry.levelSystem.loadLevel(level,
    328                 params.context.getResources().openRawResource(level.resource), mGameRoot);
    329 
    330         Context context = params.context;
    331         mRenderer.setContext(context);
    332         mSurfaceView.loadTextures(BaseObject.sSystemRegistry.longTermTextureLibrary);
    333         mSurfaceView.loadTextures(BaseObject.sSystemRegistry.shortTermTextureLibrary);
    334         mSurfaceView.loadBuffers(BaseObject.sSystemRegistry.bufferLibrary);
    335 
    336         mGLDataLoaded = true;
    337 
    338         mCurrentLevel = level;
    339         mPendingLevel = null;
    340 
    341         TimeSystem time = BaseObject.sSystemRegistry.timeSystem;
    342         time.reset();
    343 
    344         HudSystem hud = BaseObject.sSystemRegistry.hudSystem;
    345         if (hud != null) {
    346             hud.startFade(true, 1.0f);
    347         }
    348 
    349 
    350         CustomToastSystem toast = BaseObject.sSystemRegistry.customToastSystem;
    351         if (toast != null) {
    352         	if (level.inThePast) {
    353         		toast.toast(context.getString(R.string.memory_playback_start), Toast.LENGTH_LONG);
    354         	} else {
    355         		if (mLastLevel != null && mLastLevel.inThePast) {
    356             		toast.toast(context.getString(R.string.memory_playback_complete), Toast.LENGTH_LONG);
    357         		}
    358         	}
    359         }
    360 
    361         mLastLevel = level;
    362 
    363         start();
    364     }
    365 
    366     /** Starts the game running. */
    367     public void start() {
    368         if (!mRunning) {
    369             assert mGame == null;
    370             // Now's a good time to run the GC.
    371             Runtime r = Runtime.getRuntime();
    372             r.gc();
    373             DebugLog.d("AndouKun", "Start!");
    374             mGame = new Thread(mGameThread);
    375             mGame.setName("Game");
    376             mGame.start();
    377             mRunning = true;
    378             AllocationGuard.sGuardActive = false;
    379         } else {
    380             mGameThread.resumeGame();
    381         }
    382     }
    383 
    384     public void stop() {
    385         if (mRunning) {
    386             DebugLog.d("AndouKun", "Stop!");
    387             if (mGameThread.getPaused()) {
    388                 mGameThread.resumeGame();
    389             }
    390             mGameThread.stopGame();
    391             try {
    392                 mGame.join();
    393             } catch (InterruptedException e) {
    394                 mGame.interrupt();
    395             }
    396             mGame = null;
    397             mRunning = false;
    398             mCurrentLevel = null;
    399             AllocationGuard.sGuardActive = false;
    400         }
    401     }
    402 
    403     public boolean onTrackballEvent(MotionEvent event) {
    404         if (mRunning) {
    405         	if (event.getAction() == MotionEvent.ACTION_MOVE) {
    406         		BaseObject.sSystemRegistry.inputSystem.roll(event.getRawX(), event.getRawY());
    407         	} else if (event.getAction() == MotionEvent.ACTION_DOWN) {
    408         		onKeyDownEvent(KeyEvent.KEYCODE_DPAD_CENTER);
    409         	} else if (event.getAction() == MotionEvent.ACTION_UP) {
    410         		onKeyUpEvent(KeyEvent.KEYCODE_DPAD_CENTER);
    411         	}
    412         }
    413         return true;
    414     }
    415 
    416     public boolean onOrientationEvent(float x, float y, float z) {
    417         if (mRunning) {
    418         	BaseObject.sSystemRegistry.inputSystem.setOrientation(x, y, z);
    419         }
    420         return true;
    421     }
    422 
    423     public boolean onTouchEvent(MotionEvent event) {
    424         if (mRunning) {
    425         	mTouchFilter.updateTouch(event);
    426         }
    427         return true;
    428     }
    429 
    430 
    431 
    432     public boolean onKeyDownEvent(int keyCode) {
    433         boolean result = false;
    434         if (mRunning) {
    435             BaseObject.sSystemRegistry.inputSystem.keyDown(keyCode);
    436         }
    437         return result;
    438     }
    439 
    440     public boolean onKeyUpEvent(int keyCode) {
    441         boolean result = false;
    442         if (mRunning) {
    443         	BaseObject.sSystemRegistry.inputSystem.keyUp(keyCode);
    444         }
    445         return result;
    446     }
    447 
    448     public GameRenderer getRenderer() {
    449         return mRenderer;
    450     }
    451 
    452     public void onPause() {
    453     	if (mRunning) {
    454     		mGameThread.pauseGame();
    455     	}
    456     }
    457 
    458     public void onResume(Context context, boolean force) {
    459     	if (force && mRunning) {
    460     		mGameThread.resumeGame();
    461     	} else {
    462 	        mRenderer.setContext(context);
    463 	        // Don't explicitly resume the game here.  We'll do that in
    464 	        // the SurfaceReady() callback, which will prevent the game
    465 	        // starting before the render thread is ready to go.
    466 	        BaseObject.sSystemRegistry.contextParameters.context = context;
    467     	}
    468     }
    469 
    470     public void onSurfaceReady() {
    471         DebugLog.d("AndouKun", "Surface Ready");
    472 
    473         if (mPendingLevel != null && mPendingLevel != mCurrentLevel) {
    474             if (mRunning) {
    475                 stopLevel();
    476             }
    477             goToLevel(mPendingLevel);
    478         } else if (mGameThread.getPaused() && mRunning) {
    479             mGameThread.resumeGame();
    480         }
    481     }
    482 
    483     public void setSurfaceView(GLSurfaceView view) {
    484         mSurfaceView = view;
    485     }
    486 
    487     public void onSurfaceLost() {
    488         DebugLog.d("AndouKun", "Surface Lost");
    489 
    490         BaseObject.sSystemRegistry.shortTermTextureLibrary.invalidateAll();
    491         BaseObject.sSystemRegistry.longTermTextureLibrary.invalidateAll();
    492         BaseObject.sSystemRegistry.bufferLibrary.invalidateHardwareBuffers();
    493 
    494         mGLDataLoaded = false;
    495     }
    496 
    497     public void onSurfaceCreated() {
    498         DebugLog.d("AndouKun", "Surface Created");
    499 
    500         // TODO: this is dumb.  SurfaceView doesn't need to control everything here.
    501         // GL should just be passed to this function and then set up directly.
    502 
    503         if (!mGLDataLoaded && mGameThread.getPaused() && mRunning && mPendingLevel == null) {
    504 
    505             mSurfaceView.loadTextures(BaseObject.sSystemRegistry.longTermTextureLibrary);
    506             mSurfaceView.loadTextures(BaseObject.sSystemRegistry.shortTermTextureLibrary);
    507             mSurfaceView.loadBuffers(BaseObject.sSystemRegistry.bufferLibrary);
    508             mGLDataLoaded = true;
    509         }
    510     }
    511 
    512     public void setPendingLevel(LevelTree.Level level) {
    513         mPendingLevel = level;
    514     }
    515 
    516 	public void setSoundEnabled(boolean soundEnabled) {
    517 		BaseObject.sSystemRegistry.soundSystem.setSoundEnabled(soundEnabled);
    518 	}
    519 
    520 	public void setControlOptions(boolean clickAttack,
    521 			boolean tiltControls, int tiltSensitivity, int movementSensitivity, boolean onScreenControls) {
    522 		BaseObject.sSystemRegistry.inputGameInterface.setUseClickForAttack(clickAttack);
    523 		BaseObject.sSystemRegistry.inputGameInterface.setUseOrientationForMovement(tiltControls);
    524 		BaseObject.sSystemRegistry.inputGameInterface.setOrientationMovementSensitivity((tiltSensitivity / 100.0f));
    525 		BaseObject.sSystemRegistry.inputGameInterface.setMovementSensitivity((movementSensitivity / 100.0f));
    526 		BaseObject.sSystemRegistry.inputGameInterface.setUseOnScreenControls(onScreenControls);
    527 		BaseObject.sSystemRegistry.hudSystem.setMovementSliderMode(onScreenControls);
    528 	}
    529 
    530 	public void setSafeMode(boolean safe) {
    531 		mSurfaceView.setSafeMode(safe);
    532 	}
    533 
    534 	public float getGameTime() {
    535 		return BaseObject.sSystemRegistry.timeSystem.getGameTime();
    536 	}
    537 
    538 	public Vector2 getLastDeathPosition() {
    539 		return BaseObject.sSystemRegistry.eventRecorder.getLastDeathPosition();
    540 	}
    541 
    542 	public void setLastEnding(int ending) {
    543 		BaseObject.sSystemRegistry.eventRecorder.setLastEnding(ending);
    544 	}
    545 
    546 	public int getLastEnding() {
    547 		return BaseObject.sSystemRegistry.eventRecorder.getLastEnding();
    548 	}
    549 
    550 	public boolean isPaused() {
    551 		return (mRunning && mGameThread != null && mGameThread.getPaused());
    552 	}
    553 
    554 	public void setKeyConfig(int leftKey, int rightKey, int jumpKey,
    555 			int attackKey) {
    556 		BaseObject.sSystemRegistry.inputGameInterface.setKeys(leftKey, rightKey, jumpKey, attackKey);
    557 	}
    558 
    559 	public int getRobotsDestroyed() {
    560 		return BaseObject.sSystemRegistry.eventRecorder.getRobotsDestroyed();
    561 	}
    562 
    563 	public int getPearlsCollected() {
    564 		return BaseObject.sSystemRegistry.eventRecorder.getPearlsCollected();
    565 	}
    566 
    567 	public int getPearlsTotal() {
    568 		return BaseObject.sSystemRegistry.eventRecorder.getPearlsTotal();
    569 	}
    570 }
    571