blob: f60db6764420429d02fee3ab40b72565c0a27e70 [file] [log] [blame]
/*
* 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();
}
}