Home | History | Annotate | Download | only in replicaisland
      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 
     65     public HitReactionComponent() {
     66         super();
     67         reset();
     68         setPhase(ComponentPhases.PRE_DRAW.ordinal());
     69     }
     70 
     71     @Override
     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. */
    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. */
    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
    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 
    265     public void setPauseOnAttack(boolean pause) {
    266         mPauseOnAttack = pause;
    267     }
    268 
    269     public void setPauseOnAttackTime(float seconds) {
    270         mPauseOnAttackTime = seconds;
    271     }
    272 
    273     public void setBounceOnHit(boolean bounce) {
    274         mBounceOnHit = bounce;
    275     }
    276 
    277     public void setBounceMagnitude(float magnitude) {
    278         mBounceMagnitude = magnitude;
    279     }
    280 
    281     public void setInvincibleTime(float time) {
    282         mInvincibleAfterHitTime = time;
    283     }
    284 
    285     public void setDieWhenCollected(boolean die) {
    286         mDieOnCollect = true;
    287     }
    288 
    289     public void setDieOnAttack(boolean die) {
    290         mDieOnAttack = die;
    291     }
    292 
    293     public void setInvincible(boolean invincible) {
    294         mInvincible = invincible;
    295     }
    296 
    297     public void setPossessionComponent(ChangeComponentsComponent component) {
    298         mPossessionComponent = component;
    299     }
    300 
    301     public void setInventoryUpdate(InventoryComponent.UpdateRecord update) {
    302         mInventoryUpdate = update;
    303     }
    304 
    305     public void setLauncherComponent(LauncherComponent component, int launchHitType) {
    306         mLauncherComponent = component;
    307         mLauncherHitType = launchHitType;
    308     }
    309 
    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 
    321     public final void setForceInvincible(boolean force) {
    322         mForceInvincibility = force;
    323     }
    324 
    325     public final void setTakeHitSound(int hitType, SoundSystem.Sound sound) {
    326     	mTakeHitSoundHitType = hitType;
    327         mTakeHitSound = sound;
    328     }
    329 
    330     public final void setDealHitSound(int hitType, SoundSystem.Sound sound) {
    331         mDealHitSound = sound;
    332         mDealHitSoundHitType = hitType;
    333     }
    334 
    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