• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.CollisionParameters.HitType;
20 import com.replica.replicaisland.GameObject.ActionType;
21 import com.replica.replicaisland.GameObject.Team;
22 import com.replica.replicaisland.GameObjectFactory.GameObjectType;
23 
24 /**
25  * A general-purpose component that responds to dynamic collision notifications.  This component
26  * may be configured to produce common responses to hit (taking damage, being knocked back, etc), or
27  * it can be derived for entirely different responses.  This component must exist on an object for
28  * that object to respond to dynamic collisions.
29  */
30 public class HitReactionComponent extends GameComponent {
31     private static final float ATTACK_PAUSE_DELAY = (1.0f / 60) * 4;
32     private final static float DEFAULT_BOUNCE_MAGNITUDE = 200.0f;
33     private final static float EVENT_SEND_DELAY = 5.0f;
34 
35     private boolean mPauseOnAttack;
36     private float mPauseOnAttackTime;
37     private boolean mBounceOnHit;
38     private float mBounceMagnitude;
39     private float mInvincibleAfterHitTime;
40     private float mLastHitTime;
41     private boolean mInvincible;
42     private boolean mDieOnCollect;
43     private boolean mDieOnAttack;
44     private ChangeComponentsComponent mPossessionComponent;
45     private InventoryComponent.UpdateRecord mInventoryUpdate;
46     private LauncherComponent mLauncherComponent;
47     private int mLauncherHitType;
48     private float mInvincibleTime;
49     private int mGameEventHitType;
50     private int mGameEventOnHit;
51     private int mGameEventIndexData;
52     private float mLastGameEventTime;
53     private boolean mForceInvincibility;
54     private SoundSystem.Sound mTakeHitSound;
55     private SoundSystem.Sound mDealHitSound;
56     private int mDealHitSoundHitType;
57     private int mTakeHitSoundHitType;
58 
59     private GameObjectFactory.GameObjectType mSpawnOnDealHitObjectType;
60     private int mSpawnOnDealHitHitType;
61     private boolean mAlignDealHitObjectToVictimX;
62     private boolean mAlignDealHitObjectToVictimY;
63 
64 
HitReactionComponent()65     public HitReactionComponent() {
66         super();
67         reset();
68         setPhase(ComponentPhases.PRE_DRAW.ordinal());
69     }
70 
71     @Override
reset()72     public void reset() {
73         mPauseOnAttack = false;
74         mPauseOnAttackTime = ATTACK_PAUSE_DELAY;
75         mBounceOnHit = false;
76         mBounceMagnitude = DEFAULT_BOUNCE_MAGNITUDE;
77         mInvincibleAfterHitTime = 0.0f;
78         mInvincible = false;
79         mDieOnCollect = false;
80         mDieOnAttack = false;
81         mPossessionComponent = null;
82         mInventoryUpdate = null;
83         mLauncherComponent = null;
84         mLauncherHitType = HitType.LAUNCH;
85         mInvincibleTime = 0.0f;
86         mGameEventOnHit = -1;
87         mGameEventIndexData = 0;
88         mLastGameEventTime = -1.0f;
89         mGameEventHitType = CollisionParameters.HitType.INVALID;
90         mForceInvincibility = false;
91         mTakeHitSound = null;
92         mDealHitSound = null;
93         mSpawnOnDealHitObjectType = GameObjectType.INVALID;
94         mSpawnOnDealHitHitType = CollisionParameters.HitType.INVALID;
95         mDealHitSoundHitType = CollisionParameters.HitType.INVALID;
96         mAlignDealHitObjectToVictimX = false;
97         mAlignDealHitObjectToVictimY = false;
98     }
99 
100     /** Called when this object attacks another object. */
hitVictim(GameObject parent, GameObject victim, int hitType, boolean hitAccepted)101     public void hitVictim(GameObject parent, GameObject victim, int hitType,
102             boolean hitAccepted) {
103         if (hitAccepted) {
104             if (mPauseOnAttack && hitType == CollisionParameters.HitType.HIT) {
105                 TimeSystem time = sSystemRegistry.timeSystem;
106                 time.freeze(mPauseOnAttackTime);
107             }
108 
109             if (mDieOnAttack) {
110                 parent.life = 0;
111             }
112 
113             if (hitType == mLauncherHitType && mLauncherComponent != null) {
114                 mLauncherComponent.prepareToLaunch(victim, parent);
115             }
116 
117             if (mDealHitSound != null &&
118             		(hitType == mDealHitSoundHitType ||
119             				mDealHitSoundHitType == CollisionParameters.HitType.INVALID)) {
120                 SoundSystem sound = sSystemRegistry.soundSystem;
121                 if (sound != null) {
122                     sound.play(mDealHitSound, false, SoundSystem.PRIORITY_NORMAL);
123                 }
124             }
125 
126             if (mSpawnOnDealHitObjectType != GameObjectType.INVALID &&
127                     hitType == mSpawnOnDealHitHitType) {
128                 final float x = mAlignDealHitObjectToVictimX ?
129                         victim.getPosition().x : parent.getPosition().x;
130                 final float y = mAlignDealHitObjectToVictimY ?
131                         victim.getPosition().y : parent.getPosition().y;
132 
133                 GameObjectFactory factory = sSystemRegistry.gameObjectFactory;
134                 GameObjectManager manager = sSystemRegistry.gameObjectManager;
135 
136                 if (factory != null) {
137                     GameObject object = factory.spawn(mSpawnOnDealHitObjectType, x,
138                             y, parent.facingDirection.x < 0.0f);
139 
140                     if (object != null && manager != null) {
141                         manager.add(object);
142                     }
143                 }
144             }
145         }
146     }
147 
148     /** Called when this object is hit by another object. */
receivedHit(GameObject parent, GameObject attacker, int hitType)149     public boolean receivedHit(GameObject parent, GameObject attacker, int hitType) {
150         final TimeSystem time = sSystemRegistry.timeSystem;
151         final float gameTime = time.getGameTime();
152 
153         if (mGameEventHitType == hitType &&
154                 mGameEventHitType != CollisionParameters.HitType.INVALID ) {
155         	if (mLastGameEventTime < 0.0f || gameTime > mLastGameEventTime + EVENT_SEND_DELAY) {
156 	            LevelSystem level = sSystemRegistry.levelSystem;
157 	            level.sendGameEvent(mGameEventOnHit, mGameEventIndexData, true);
158 	        } else {
159 	        	// special case.  If we're waiting for a hit type to spawn an event and
160 	        	// another event has just happened, eat this hit so we don't miss
161 	        	// the chance to send the event.
162 	        	hitType = CollisionParameters.HitType.INVALID;
163 	        }
164         	mLastGameEventTime = gameTime;
165         }
166 
167         switch(hitType) {
168             case CollisionParameters.HitType.INVALID:
169                 break;
170 
171             case CollisionParameters.HitType.HIT:
172                 // don't hit our friends, if we have friends.
173                 final boolean sameTeam = (parent.team == attacker.team && parent.team != Team.NONE);
174                 if (!mForceInvincibility && !mInvincible && parent.life > 0 && !sameTeam) {
175                     parent.life -= 1;
176 
177                     if (mBounceOnHit && parent.life > 0) {
178                         VectorPool pool = sSystemRegistry.vectorPool;
179                         Vector2 newVelocity = pool.allocate(parent.getPosition());
180                         newVelocity.subtract(attacker.getPosition());
181                         newVelocity.set(0.5f * Utils.sign(newVelocity.x),
182                                 0.5f * Utils.sign(newVelocity.y));
183                         newVelocity.multiply(mBounceMagnitude);
184                         parent.setVelocity(newVelocity);
185                         parent.getTargetVelocity().zero();
186                         pool.release(newVelocity);
187                     }
188 
189                     if (mInvincibleAfterHitTime > 0.0f) {
190                         mInvincible = true;
191                         mInvincibleTime = mInvincibleAfterHitTime;
192                     }
193 
194                 } else {
195                     // Ignore this hit.
196                     hitType = CollisionParameters.HitType.INVALID;
197                 }
198                 break;
199             case CollisionParameters.HitType.DEATH:
200                 // respect teams?
201                 parent.life = 0;
202                 break;
203             case CollisionParameters.HitType.COLLECT:
204                 if (mInventoryUpdate != null && parent.life > 0) {
205                     InventoryComponent attackerInventory = attacker.findByClass(InventoryComponent.class);
206                     if (attackerInventory != null) {
207                         attackerInventory.applyUpdate(mInventoryUpdate);
208                     }
209                 }
210                 if (mDieOnCollect && parent.life > 0) {
211                     parent.life = 0;
212                 }
213                 break;
214             case CollisionParameters.HitType.POSSESS:
215                 if (mPossessionComponent != null && parent.life > 0 && attacker.life > 0) {
216                     mPossessionComponent.activate(parent);
217                 } else {
218                     hitType = CollisionParameters.HitType.INVALID;
219                 }
220                 break;
221             case CollisionParameters.HitType.LAUNCH:
222                 break;
223 
224             default:
225                 break;
226         }
227 
228 
229         if (hitType != CollisionParameters.HitType.INVALID) {
230             if (mTakeHitSound != null && hitType == mTakeHitSoundHitType) {
231                 SoundSystem sound = sSystemRegistry.soundSystem;
232                 if (sound != null) {
233                     sound.play(mTakeHitSound, false, SoundSystem.PRIORITY_NORMAL);
234                 }
235             }
236             mLastHitTime = gameTime;
237             parent.setCurrentAction(ActionType.HIT_REACT);
238             parent.lastReceivedHitType = hitType;
239 
240         }
241 
242         return hitType != CollisionParameters.HitType.INVALID;
243     }
244 
245     @Override
update(float timeDelta, BaseObject parent)246     public void update(float timeDelta, BaseObject parent) {
247         GameObject parentObject = (GameObject)parent;
248         TimeSystem time = sSystemRegistry.timeSystem;
249 
250         final float gameTime = time.getGameTime();
251 
252         if (mInvincible && mInvincibleTime > 0) {
253             if (time.getGameTime() > mLastHitTime + mInvincibleTime) {
254                 mInvincible = false;
255             }
256         }
257 
258         // This means that the lastReceivedHitType will persist for two frames, giving all systems
259         // a chance to react.
260         if (gameTime - mLastHitTime > timeDelta) {
261             parentObject.lastReceivedHitType = CollisionParameters.HitType.INVALID;
262         }
263     }
264 
setPauseOnAttack(boolean pause)265     public void setPauseOnAttack(boolean pause) {
266         mPauseOnAttack = pause;
267     }
268 
setPauseOnAttackTime(float seconds)269     public void setPauseOnAttackTime(float seconds) {
270         mPauseOnAttackTime = seconds;
271     }
272 
setBounceOnHit(boolean bounce)273     public void setBounceOnHit(boolean bounce) {
274         mBounceOnHit = bounce;
275     }
276 
setBounceMagnitude(float magnitude)277     public void setBounceMagnitude(float magnitude) {
278         mBounceMagnitude = magnitude;
279     }
280 
setInvincibleTime(float time)281     public void setInvincibleTime(float time) {
282         mInvincibleAfterHitTime = time;
283     }
284 
setDieWhenCollected(boolean die)285     public void setDieWhenCollected(boolean die) {
286         mDieOnCollect = true;
287     }
288 
setDieOnAttack(boolean die)289     public void setDieOnAttack(boolean die) {
290         mDieOnAttack = die;
291     }
292 
setInvincible(boolean invincible)293     public void setInvincible(boolean invincible) {
294         mInvincible = invincible;
295     }
296 
setPossessionComponent(ChangeComponentsComponent component)297     public void setPossessionComponent(ChangeComponentsComponent component) {
298         mPossessionComponent = component;
299     }
300 
setInventoryUpdate(InventoryComponent.UpdateRecord update)301     public void setInventoryUpdate(InventoryComponent.UpdateRecord update) {
302         mInventoryUpdate = update;
303     }
304 
setLauncherComponent(LauncherComponent component, int launchHitType)305     public void setLauncherComponent(LauncherComponent component, int launchHitType) {
306         mLauncherComponent = component;
307         mLauncherHitType = launchHitType;
308     }
309 
setSpawnGameEventOnHit(int hitType, int gameFlowEventType, int indexData)310     public void setSpawnGameEventOnHit(int hitType, int gameFlowEventType, int indexData) {
311         mGameEventHitType = hitType;
312         mGameEventOnHit = gameFlowEventType;
313         mGameEventIndexData = indexData;
314         if (hitType == HitType.INVALID) {
315         	// The game event has been cleared, so reset the timer blocking a
316         	// subsequent event.
317         	mLastGameEventTime = -1.0f;
318         }
319     }
320 
setForceInvincible(boolean force)321     public final void setForceInvincible(boolean force) {
322         mForceInvincibility = force;
323     }
324 
setTakeHitSound(int hitType, SoundSystem.Sound sound)325     public final void setTakeHitSound(int hitType, SoundSystem.Sound sound) {
326     	mTakeHitSoundHitType = hitType;
327         mTakeHitSound = sound;
328     }
329 
setDealHitSound(int hitType, SoundSystem.Sound sound)330     public final void setDealHitSound(int hitType, SoundSystem.Sound sound) {
331         mDealHitSound = sound;
332         mDealHitSoundHitType = hitType;
333     }
334 
setSpawnOnDealHit(int hitType, GameObjectType objectType, boolean alignToVictimX, boolean alignToVicitmY)335     public final void setSpawnOnDealHit(int hitType, GameObjectType objectType, boolean alignToVictimX,
336             boolean alignToVicitmY) {
337         mSpawnOnDealHitObjectType = objectType;
338         mSpawnOnDealHitHitType = hitType;
339         mAlignDealHitObjectToVictimX = alignToVictimX;
340         mAlignDealHitObjectToVictimY = alignToVicitmY;
341     }
342 
343 }
344