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 com.replica.replicaisland.GameObject.ActionType;
20
21/**
22 * A component that allows an object to spawn other objects and apply velocity to them at
23 * specific intervals.  Can be used to launch projectiles, particle effects, or any other type
24 * of game object.
25 */
26public class LaunchProjectileComponent extends GameComponent {
27    private GameObjectFactory.GameObjectType mObjectTypeToSpawn;
28    private float mOffsetX;
29    private float mOffsetY;
30    private float mVelocityX;
31    private float mVelocityY;
32    private float mThetaError;
33    private GameObject.ActionType mRequiredAction;
34    private float mDelayBetweenShots;
35    private int mProjectilesInSet;
36    private float mDelayBetweenSets;
37    private int mSetsPerActivation;
38    private float mDelayBeforeFirstSet;
39
40    private float mLastProjectileTime;
41    private float mSetStartedTime;
42    private int mLaunchedCount;
43    private int mSetCount;
44
45    private boolean mTrackProjectiles;
46    private int mMaxTrackedProjectiles;
47    private int mTrackedProjectileCount;
48
49    private Vector2 mWorkingVector;
50
51    private SoundSystem.Sound mShootSound;
52
53
54    public LaunchProjectileComponent() {
55        super();
56        setPhase(ComponentPhases.POST_COLLISION.ordinal());
57        mWorkingVector = new Vector2();
58        reset();
59    }
60
61    @Override
62    public void reset() {
63        mRequiredAction = ActionType.INVALID;
64        mObjectTypeToSpawn = GameObjectFactory.GameObjectType.INVALID;
65        mOffsetX = 0.0f;
66        mOffsetY = 0.0f;
67        mVelocityX = 0.0f;
68        mVelocityY = 0.0f;
69        mDelayBetweenShots = 0.0f;
70        mProjectilesInSet = 0;
71        mDelayBetweenSets = 0.0f;
72        mLastProjectileTime = 0.0f;
73        mSetStartedTime = -1.0f;
74        mLaunchedCount = 0;
75        mSetCount = 0;
76        mSetsPerActivation = -1;
77        mProjectilesInSet = 0;
78        mDelayBeforeFirstSet = 0.0f;
79        mTrackProjectiles = false;
80        mMaxTrackedProjectiles = 0;
81        mTrackedProjectileCount = 0;
82        mThetaError = 0.0f;
83        mShootSound = null;
84    }
85
86    @Override
87    public void update(float timeDelta, BaseObject parent) {
88        GameObject parentObject = (GameObject) parent;
89
90        final TimeSystem time = sSystemRegistry.timeSystem;
91        final float gameTime = time.getGameTime();
92
93        if (mTrackedProjectileCount < mMaxTrackedProjectiles || !mTrackProjectiles) {
94            if (parentObject.getCurrentAction() == mRequiredAction
95                    || mRequiredAction == ActionType.INVALID) {
96
97                if (mSetStartedTime == -1.0f) {
98                    mLaunchedCount = 0;
99                    mLastProjectileTime = 0.0f;
100                    mSetStartedTime = gameTime;
101                }
102
103                final float setDelay = mSetCount > 0 ? mDelayBetweenSets : mDelayBeforeFirstSet;
104
105                if (gameTime - mSetStartedTime >= setDelay &&
106                        (mSetCount < mSetsPerActivation || mSetsPerActivation == -1)) {
107                    // We can start shooting.
108                    final float timeSinceLastShot = gameTime - mLastProjectileTime;
109
110                    if (timeSinceLastShot >= mDelayBetweenShots) {
111
112                        launch(parentObject);
113                        mLastProjectileTime = gameTime;
114
115                        if (mLaunchedCount >= mProjectilesInSet && mProjectilesInSet > 0) {
116                            mSetStartedTime = -1.0f;
117                            mSetCount++;
118                        }
119                    }
120                }
121            } else {
122                // Force the timer to start counting when the right action is activated.
123                mSetStartedTime = -1.0f;
124                mSetCount = 0;
125            }
126        }
127    }
128
129    private void launch(GameObject parentObject) {
130        mLaunchedCount++;
131        GameObjectFactory factory = sSystemRegistry.gameObjectFactory;
132        GameObjectManager manager = sSystemRegistry.gameObjectManager;
133        if (factory != null && manager != null) {
134            float offsetX = mOffsetX;
135            float offsetY = mOffsetY;
136            boolean flip = false;
137            if (parentObject.facingDirection.x < 0.0f) {
138                offsetX = parentObject.width - mOffsetX;
139                flip = true;
140            }
141
142            if (parentObject.facingDirection.y < 0.0f) {
143                offsetY = parentObject.height - mOffsetY;
144            }
145
146            final float x = parentObject.getPosition().x + offsetX;
147            final float y = parentObject.getPosition().y + offsetY;
148            GameObject object = factory.spawn(mObjectTypeToSpawn, x, y, flip);
149            if (object != null) {
150	            mWorkingVector.set(1.0f, 1.0f);
151	            if (mThetaError > 0.0f) {
152	                final float angle = (float)(Math.random() * mThetaError * Math.PI * 2.0f);
153	                mWorkingVector.x = (float)Math.sin(angle);
154	                mWorkingVector.y = (float)Math.cos(angle);
155	                if (Utils.close(mWorkingVector.length2(), 0.0f)) {
156	                    mWorkingVector.set(1.0f, 1.0f);
157	                }
158	            }
159	            mWorkingVector.x *= flip ? -mVelocityX : mVelocityX;
160	            mWorkingVector.y *= mVelocityY;
161
162	            object.getVelocity().set(mWorkingVector);
163	            object.getTargetVelocity().set(mWorkingVector);
164	            // Center the projectile on the spawn point.
165	            object.getPosition().x -= object.width / 2.0f;
166	            object.getPosition().y -= object.height / 2.0f;
167
168
169	            if (mTrackProjectiles) {
170	                object.commitUpdates();
171	                LifetimeComponent projectileLife = object.findByClass(LifetimeComponent.class);
172	                if (projectileLife != null) {
173	                    projectileLife.setTrackingSpawner(this);
174	                    mTrackedProjectileCount++;
175	                }
176	            }
177	            manager.add(object);
178
179	            if (mShootSound != null) {
180	            	SoundSystem sound = sSystemRegistry.soundSystem;
181	            	if (sound != null) {
182	            		sound.play(mShootSound, false, SoundSystem.PRIORITY_NORMAL);
183	            	}
184	            }
185            }
186        }
187
188
189    }
190
191    public final void setObjectTypeToSpawn(GameObjectFactory.GameObjectType objectTypeToSpawn) {
192        mObjectTypeToSpawn = objectTypeToSpawn;
193    }
194
195    public final void setOffsetX(float offsetX) {
196        mOffsetX = offsetX;
197    }
198
199    public final void setOffsetY(float offsetY) {
200        mOffsetY = offsetY;
201    }
202
203    public final void setVelocityX(float velocityX) {
204        mVelocityX = velocityX;
205    }
206
207    public final void setVelocityY(float velocityY) {
208        mVelocityY = velocityY;
209    }
210
211    public final void setRequiredAction(GameObject.ActionType requiredAction) {
212        mRequiredAction = requiredAction;
213    }
214
215    public final void setDelayBetweenShots(float launchDelay) {
216        mDelayBetweenShots = launchDelay;
217    }
218
219    public final void setDelayBetweenSets(float delayBetweenSets) {
220        mDelayBetweenSets = delayBetweenSets;
221    }
222
223    public final void setDelayBeforeFirstSet(float delayBeforeFirstSet) {
224        mDelayBeforeFirstSet = delayBeforeFirstSet;
225    }
226
227    public final void setShotsPerSet(int shotCount) {
228        mProjectilesInSet = shotCount;
229    }
230
231    public final void setSetsPerActivation(int setCount) {
232        mSetsPerActivation = setCount;
233    }
234
235    public final void enableProjectileTracking(int max) {
236        mMaxTrackedProjectiles = max;
237        mTrackProjectiles = true;
238    }
239
240    public final void disableProjectileTracking() {
241        mMaxTrackedProjectiles = 0;
242        mTrackProjectiles = false;
243    }
244
245    public final void trackedProjectileDestroyed() {
246        assert mTrackProjectiles;
247        if (mTrackedProjectileCount == mMaxTrackedProjectiles) {
248            // Let's restart the set.
249            mSetStartedTime = -1.0f;
250            mSetCount = 0;
251        }
252        mTrackedProjectileCount--;
253    }
254
255    public final void setThetaError(float error) {
256        mThetaError = error;
257    }
258
259    public final void setShootSound(SoundSystem.Sound shoot) {
260    	mShootSound = shoot;
261    }
262
263}
264