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 17 package com.replica.replicaisland; 18 19 import 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 */ 26 public 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