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 java.util.Comparator;
20
21/**
22 * A node in the game graph that manages the activation status of its children.  The
23 * GameObjectManager moves the objects it manages in and out of the active list (that is,
24 * in and out of the game tree, causing them to be updated or ignored, respectively) each frame
25 * based on the distance of that object to the camera.  Objects may specify an "activation radius"
26 * to define an area around themselves so that the position of the camera can be used to determine
27 * which objects should receive processing time and which should be ignored.  Objects that do not
28 * move should have an activation radius that defines a sphere similar to the size of the screen;
29 * they only need processing when they are visible.  Objects that move around will probably need
30 * larger regions so that they can leave the visible area of the game world and not be immediately
31 * deactivated.
32 */
33public class GameObjectManager extends ObjectManager {
34    private static final int MAX_GAME_OBJECTS = 384;
35    private float mMaxActivationRadius;
36    private final static HorizontalPositionComparator sGameObjectComparator
37        = new HorizontalPositionComparator();
38    private FixedSizeArray<BaseObject> mInactiveObjects;
39    private FixedSizeArray<GameObject> mMarkedForDeathObjects;
40    private GameObject mPlayer;
41    private boolean mVisitingGraph;
42    private Vector2 mCameraFocus;
43
44
45    public GameObjectManager(float maxActivationRadius) {
46        super(MAX_GAME_OBJECTS);
47        mMaxActivationRadius = maxActivationRadius;
48
49        mInactiveObjects = new FixedSizeArray<BaseObject>(MAX_GAME_OBJECTS);
50        mInactiveObjects.setComparator(sGameObjectComparator);
51
52        mMarkedForDeathObjects = new FixedSizeArray<GameObject>(MAX_GAME_OBJECTS);
53        mVisitingGraph = false;
54
55        mCameraFocus = new Vector2();
56
57    }
58
59    @Override
60    public void commitUpdates() {
61        super.commitUpdates();
62
63        GameObjectFactory factory = sSystemRegistry.gameObjectFactory;
64        final int objectsToKillCount = mMarkedForDeathObjects.getCount();
65        if (factory != null && objectsToKillCount > 0) {
66            final Object[] deathArray = mMarkedForDeathObjects.getArray();
67            for (int x = 0; x < objectsToKillCount; x++) {
68                factory.destroy((GameObject)deathArray[x]);
69            }
70            mMarkedForDeathObjects.clear();
71        }
72    }
73
74    @Override
75    public void update(float timeDelta, BaseObject parent) {
76        commitUpdates();
77
78        CameraSystem camera = sSystemRegistry.cameraSystem;
79
80        mCameraFocus.set(camera.getFocusPositionX(), camera.getFocusPositionY());
81        mVisitingGraph = true;
82        FixedSizeArray<BaseObject> objects = getObjects();
83        final int count = objects.getCount();
84
85        if (count > 0) {
86            final Object[] objectArray = objects.getArray();
87            for (int i = count - 1; i >= 0; i--) {
88                GameObject gameObject = (GameObject)objectArray[i];
89                final float distance2 = mCameraFocus.distance2(gameObject.getPosition());
90                if (distance2 < (gameObject.activationRadius * gameObject.activationRadius)
91                        || gameObject.activationRadius == -1) {
92                    gameObject.update(timeDelta, this);
93                } else {
94                	// Remove the object from the list.
95                	// It's safe to just swap the current object with the last
96                	// object because this list is being iterated backwards, so
97                	// the last object in the list has already been processed.
98                	objects.swapWithLast(i);
99                    objects.removeLast();
100                    if (gameObject.destroyOnDeactivation) {
101                        mMarkedForDeathObjects.add(gameObject);
102                    } else {
103                        mInactiveObjects.add((BaseObject)gameObject);
104                    }
105                }
106            }
107        }
108
109        mInactiveObjects.sort(false);
110        final int inactiveCount = mInactiveObjects.getCount();
111        if (inactiveCount > 0) {
112            final Object[] inactiveArray = mInactiveObjects.getArray();
113            for (int i = inactiveCount - 1; i >= 0; i--) {
114                GameObject gameObject = (GameObject)inactiveArray[i];
115
116                final Vector2 position = gameObject.getPosition();
117                final float distance2 = mCameraFocus.distance2(position);
118                final float xDistance = position.x - mCameraFocus.x;
119                if (distance2 < (gameObject.activationRadius * gameObject.activationRadius)
120                        || gameObject.activationRadius == -1) {
121                    gameObject.update(timeDelta, this);
122                    mInactiveObjects.swapWithLast(i);
123                    mInactiveObjects.removeLast();
124                    objects.add(gameObject);
125                } else if (xDistance < -mMaxActivationRadius) {
126                    // We've passed the focus, we can stop processing now
127                    break;
128                }
129            }
130        }
131        mVisitingGraph = false;
132    }
133
134
135    @Override
136    public void add(BaseObject object) {
137        if (object instanceof GameObject) {
138            super.add(object);
139        }
140    }
141
142    @Override
143    public void remove(BaseObject object) {
144        super.remove(object);
145        if (object == mPlayer) {
146            mPlayer = null;
147        }
148    }
149
150    public void destroy(GameObject object) {
151        mMarkedForDeathObjects.add(object);
152        remove(object);
153    }
154
155    public void destroyAll() {
156        assert mVisitingGraph == false;
157        commitUpdates();
158
159        FixedSizeArray<BaseObject> objects = getObjects();
160        final int count = objects.getCount();
161        for (int i = count - 1; i >= 0; i--) {
162            mMarkedForDeathObjects.add((GameObject)objects.get(i));
163            objects.remove(i);
164        }
165
166        final int inactiveObjectCount = mInactiveObjects.getCount();
167        for (int j = inactiveObjectCount - 1; j >= 0; j--) {
168            mMarkedForDeathObjects.add((GameObject)mInactiveObjects.get(j));
169            mInactiveObjects.remove(j);
170        }
171
172        mPlayer = null;
173    }
174
175    public void setPlayer(GameObject player) {
176        mPlayer = player;
177    }
178
179    public GameObject getPlayer() {
180        return mPlayer;
181    }
182
183    /** Comparator for game objects objects. */
184    private final static class HorizontalPositionComparator implements Comparator<BaseObject> {
185        public int compare(BaseObject object1, BaseObject object2) {
186            int result = 0;
187            if (object1 == null && object2 != null) {
188                result = 1;
189            } else if (object1 != null && object2 == null) {
190                result = -1;
191            } else if (object1 != null && object2 != null) {
192                float delta = ((GameObject) object1).getPosition().x
193                    - ((GameObject) object2).getPosition().x;
194                if (delta < 0) {
195                    result = -1;
196                } else if (delta > 0) {
197                    result = 1;
198                }
199            }
200            return result;
201        }
202    }
203}
204