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
17package com.replica.replicaisland;
18
19import android.content.Context;
20import android.os.Build;
21import android.view.KeyEvent;
22import android.view.MotionEvent;
23import android.view.WindowManager;
24import 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 */
31public 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