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.res.AssetManager;
20
21import java.io.IOException;
22import java.io.InputStream;
23
24/**
25 * Manages information about the current level, including setup, deserialization, and tear-down.
26 */
27public class LevelSystem extends BaseObject {
28
29    public int mWidthInTiles;
30    public int mHeightInTiles;
31    public int mTileWidth;
32    public int mTileHeight;
33    public GameObject mBackgroundObject;
34    public ObjectManager mRoot;
35    private byte[] mWorkspaceBytes;
36    private TiledWorld mSpawnLocations;
37    private GameFlowEvent mGameFlowEvent;
38    private int mAttempts;
39    private LevelTree.Level mCurrentLevel;
40
41    public LevelSystem() {
42        super();
43        mWorkspaceBytes = new byte[4];
44        mGameFlowEvent = new GameFlowEvent();
45        reset();
46    }
47
48    @Override
49    public void reset() {
50        if (mBackgroundObject != null && mRoot != null) {
51            mBackgroundObject.removeAll();
52            mBackgroundObject.commitUpdates();
53            mRoot.remove(mBackgroundObject);
54            mBackgroundObject = null;
55            mRoot = null;
56        }
57        mSpawnLocations = null;
58        mAttempts = 0;
59        mCurrentLevel = null;
60    }
61
62    public float getLevelWidth() {
63        return mWidthInTiles * mTileWidth;
64    }
65
66    public float getLevelHeight() {
67        return mHeightInTiles * mTileHeight;
68    }
69
70    public void sendRestartEvent() {
71        mGameFlowEvent.post(GameFlowEvent.EVENT_RESTART_LEVEL, 0,
72                sSystemRegistry.contextParameters.context);
73    }
74
75    public void sendNextLevelEvent() {
76        mGameFlowEvent.post(GameFlowEvent.EVENT_GO_TO_NEXT_LEVEL, 0,
77                sSystemRegistry.contextParameters.context);
78    }
79
80    public void sendGameEvent(int type, int index, boolean immediate) {
81        if (immediate) {
82        	mGameFlowEvent.postImmediate(type, index,
83                sSystemRegistry.contextParameters.context);
84        } else {
85        	mGameFlowEvent.post(type, index,
86                    sSystemRegistry.contextParameters.context);
87        }
88    }
89
90    /**
91     * Loads a level from a binary file.  The file consists of several layers, including background
92     * tile layers and at most one collision layer.  Each layer is used to bootstrap related systems
93     * and provide them with layer data.
94     * @param stream  The input stream for the level file resource.
95     * @param tiles   A tile library to use when constructing tiled background layers.
96     * @param background  An object to assign background layer rendering components to.
97     * @return
98     */
99    public boolean loadLevel(LevelTree.Level level, InputStream stream, ObjectManager root) {
100        boolean success = false;
101        mCurrentLevel = level;
102        AssetManager.AssetInputStream byteStream = (AssetManager.AssetInputStream) stream;
103        int signature;
104        try {
105            signature = (byte)byteStream.read();
106            if (signature == 96) {
107                final int layerCount = (byte)byteStream.read();
108                final int backgroundIndex = (byte)byteStream.read();
109
110                mRoot = root;
111                mTileWidth = 32;
112                mTileHeight = 32;
113
114                ContextParameters params = sSystemRegistry.contextParameters;
115                int currentPriority = SortConstants.BACKGROUND_START + 1;
116                for (int x = 0; x < layerCount; x++) {
117                    final int type = (byte)byteStream.read();
118                    final int tileIndex = (byte)byteStream.read();
119                    byteStream.read(mWorkspaceBytes, 0, 4);
120                    final float scrollSpeed = Utils.byteArrayToFloat(mWorkspaceBytes);
121
122                    // TODO: use a pool here?  Seems pointless.
123                    TiledWorld world = new TiledWorld(byteStream);
124
125                    if (type == 0) { // it's a background layer
126                        assert mWidthInTiles != 0;
127                        assert mTileWidth != 0;
128
129                        // We require a collision layer to set up the tile sizes before we load.
130                        // TODO: this really sucks.  there's no reason each layer can't have its
131                        // own tile widths and heights.  Refactor this crap.
132                        if (mWidthInTiles > 0 && mTileWidth > 0) {
133
134                            LevelBuilder builder = sSystemRegistry.levelBuilder;
135
136                            if (mBackgroundObject == null) {
137                                mBackgroundObject =
138                                    builder.buildBackground(
139                                    		backgroundIndex,
140                                    		mWidthInTiles * mTileWidth,
141                                    		mHeightInTiles * mTileHeight);
142                                root.add(mBackgroundObject);
143                            }
144
145
146                            builder.addTileMapLayer(mBackgroundObject, currentPriority,
147                                    scrollSpeed, params.gameWidth, params.gameHeight,
148                                    mTileWidth, mTileHeight, world, tileIndex);
149
150
151                            currentPriority++;
152                        }
153
154                    } else if (type == 1) { // collision
155                        // Collision always defines the world boundaries.
156                        mWidthInTiles = world.getWidth();
157                        mHeightInTiles = world.getHeight();
158
159
160                        CollisionSystem collision = sSystemRegistry.collisionSystem;
161                        if (collision != null) {
162                            collision.initialize(world, mTileWidth, mTileHeight);
163                        }
164                    } else if (type == 2) { // objects
165                        mSpawnLocations = world;
166                        spawnObjects();
167                    } else if (type == 3) { // hot spots
168                        HotSpotSystem hotSpots = sSystemRegistry.hotSpotSystem;
169                        if (hotSpots != null) {
170                            hotSpots.setWorld(world);
171                        }
172
173                    }
174                }
175
176                // hack!
177                sSystemRegistry.levelBuilder.promoteForegroundLayer(mBackgroundObject);
178
179            }
180
181        } catch (IOException e) {
182            //TODO: figure out the best way to deal with this.  Assert?
183        }
184
185        return success;
186    }
187
188    public void spawnObjects() {
189        GameObjectFactory factory = sSystemRegistry.gameObjectFactory;
190        if (factory != null && mSpawnLocations != null) {
191            DebugLog.d("LevelSystem", "Spawning Objects!");
192
193            factory.spawnFromWorld(mSpawnLocations, mTileWidth, mTileHeight);
194        }
195    }
196
197    public void incrementAttemptsCount() {
198        mAttempts++;
199    }
200
201    public int getAttemptsCount() {
202        return mAttempts;
203    }
204
205    public LevelTree.Level getCurrentLevel() {
206    	return mCurrentLevel;
207    }
208}
209