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 
Game()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      */
bootstrap(Context context, int viewWidth, int viewHeight, int gameWidth, int gameHeight, int difficulty)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 
stopLevel()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 
requestNewLevel()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 
restartLevel()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 
goToLevel(LevelTree.Level level)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. */
start()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 
stop()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 
onTrackballEvent(MotionEvent event)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 
onOrientationEvent(float x, float y, float z)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 
onTouchEvent(MotionEvent event)423     public boolean onTouchEvent(MotionEvent event) {
424         if (mRunning) {
425         	mTouchFilter.updateTouch(event);
426         }
427         return true;
428     }
429 
430 
431 
onKeyDownEvent(int keyCode)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 
onKeyUpEvent(int keyCode)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 
getRenderer()448     public GameRenderer getRenderer() {
449         return mRenderer;
450     }
451 
onPause()452     public void onPause() {
453     	if (mRunning) {
454     		mGameThread.pauseGame();
455     	}
456     }
457 
onResume(Context context, boolean force)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 
onSurfaceReady()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 
setSurfaceView(GLSurfaceView view)483     public void setSurfaceView(GLSurfaceView view) {
484         mSurfaceView = view;
485     }
486 
onSurfaceLost()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 
onSurfaceCreated()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 
setPendingLevel(LevelTree.Level level)512     public void setPendingLevel(LevelTree.Level level) {
513         mPendingLevel = level;
514     }
515 
setSoundEnabled(boolean soundEnabled)516 	public void setSoundEnabled(boolean soundEnabled) {
517 		BaseObject.sSystemRegistry.soundSystem.setSoundEnabled(soundEnabled);
518 	}
519 
setControlOptions(boolean clickAttack, boolean tiltControls, int tiltSensitivity, int movementSensitivity, boolean onScreenControls)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 
setSafeMode(boolean safe)530 	public void setSafeMode(boolean safe) {
531 		mSurfaceView.setSafeMode(safe);
532 	}
533 
getGameTime()534 	public float getGameTime() {
535 		return BaseObject.sSystemRegistry.timeSystem.getGameTime();
536 	}
537 
getLastDeathPosition()538 	public Vector2 getLastDeathPosition() {
539 		return BaseObject.sSystemRegistry.eventRecorder.getLastDeathPosition();
540 	}
541 
setLastEnding(int ending)542 	public void setLastEnding(int ending) {
543 		BaseObject.sSystemRegistry.eventRecorder.setLastEnding(ending);
544 	}
545 
getLastEnding()546 	public int getLastEnding() {
547 		return BaseObject.sSystemRegistry.eventRecorder.getLastEnding();
548 	}
549 
isPaused()550 	public boolean isPaused() {
551 		return (mRunning && mGameThread != null && mGameThread.getPaused());
552 	}
553 
setKeyConfig(int leftKey, int rightKey, int jumpKey, int attackKey)554 	public void setKeyConfig(int leftKey, int rightKey, int jumpKey,
555 			int attackKey) {
556 		BaseObject.sSystemRegistry.inputGameInterface.setKeys(leftKey, rightKey, jumpKey, attackKey);
557 	}
558 
getRobotsDestroyed()559 	public int getRobotsDestroyed() {
560 		return BaseObject.sSystemRegistry.eventRecorder.getRobotsDestroyed();
561 	}
562 
getPearlsCollected()563 	public int getPearlsCollected() {
564 		return BaseObject.sSystemRegistry.eventRecorder.getPearlsCollected();
565 	}
566 
getPearlsTotal()567 	public int getPearlsTotal() {
568 		return BaseObject.sSystemRegistry.eventRecorder.getPearlsTotal();
569 	}
570 }
571