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 
     20 import com.replica.replicaisland.CollisionParameters.HitType;
     21 import com.replica.replicaisland.GameObject.ActionType;
     22 import com.replica.replicaisland.SoundSystem.Sound;
     23 
     24 /**
     25  * Player Animation game object component.  Responsible for selecting an animation to describe the
     26  * player's current state.  Requires the object to contain a SpriteComponent to play animations.
     27  */
     28 public class AnimationComponent extends GameComponent {
     29 
     30     public enum PlayerAnimations {
     31         IDLE,
     32         MOVE,
     33         MOVE_FAST,
     34         BOOST_UP,
     35         BOOST_MOVE,
     36         BOOST_MOVE_FAST,
     37         STOMP,
     38         HIT_REACT,
     39         DEATH,
     40         FROZEN
     41     }
     42 
     43     private static final float MIN_ROCKET_TIME = 0.0f;
     44     private static final float FLICKER_INTERVAL = 0.15f;
     45     private static final float FLICKER_DURATION = 3.0f;
     46     private static final float LAND_THUMP_DELAY = 0.5f;
     47 
     48     private SpriteComponent mSprite;
     49     private SpriteComponent mJetSprite;
     50     private SpriteComponent mSparksSprite;
     51 
     52     private PlayerComponent mPlayer;
     53     private float mLastFlickerTime;
     54     private boolean mFlickerOn;
     55     private float mFlickerTimeRemaining;
     56 
     57     private GameObject.ActionType mPreviousAction;
     58 
     59     private float mLastRocketsOnTime;
     60     private boolean mExplodingDeath;
     61 
     62     private ChangeComponentsComponent mDamageSwap;
     63     private Sound mLandThump;
     64     private Sound mRocketSound;
     65     private Sound mExplosionSound;
     66     private float mLandThumpDelay;
     67     private int mRocketSoundStream;
     68     private boolean mRocketSoundPaused;
     69 
     70     private int mLastRubyCount;
     71     private Sound mRubySound1;
     72     private Sound mRubySound2;
     73     private Sound mRubySound3;
     74     private InventoryComponent mInventory;
     75 
     76 
     77     public AnimationComponent() {
     78         super();
     79         reset();
     80         setPhase(ComponentPhases.ANIMATION.ordinal());
     81     }
     82 
     83     @Override
     84     public void reset() {
     85         mPreviousAction = ActionType.INVALID;
     86         mSprite = null;
     87         mJetSprite = null;
     88         mSparksSprite = null;
     89         mPlayer = null;
     90         mLastFlickerTime = 0.0f;
     91         mFlickerOn = false;
     92         mFlickerTimeRemaining = 0.0f;
     93         mLastRocketsOnTime = 0.0f;
     94         mExplodingDeath = false;
     95         mDamageSwap = null;
     96         mLandThump = null;
     97         mLandThumpDelay = 0.0f;
     98         mRocketSound = null;
     99         mRocketSoundStream = -1;
    100         mLastRubyCount = 0;
    101         mInventory = null;
    102         mExplosionSound = null;
    103     }
    104 
    105     @Override
    106     public void update(float timeDelta, BaseObject parent) {
    107         if (mSprite != null) {
    108 
    109             GameObject parentObject = (GameObject) parent;
    110 
    111             final float velocityX = parentObject.getVelocity().x;
    112             final float velocityY = parentObject.getVelocity().y;
    113 
    114 
    115             GameObject.ActionType currentAction = parentObject.getCurrentAction();
    116 
    117             if (mJetSprite != null) {
    118                 mJetSprite.setVisible(false);
    119             }
    120 
    121             if (mSparksSprite != null) {
    122                 mSparksSprite.setVisible(false);
    123             }
    124 
    125 
    126             final TimeSystem time = sSystemRegistry.timeSystem;
    127             final float gameTime = time.getGameTime();
    128 
    129             if (currentAction != ActionType.HIT_REACT && mPreviousAction == ActionType.HIT_REACT) {
    130                 mFlickerTimeRemaining = FLICKER_DURATION;
    131             }
    132 
    133 
    134             final boolean touchingGround = parentObject.touchingGround();
    135 
    136             boolean boosting = mPlayer != null ? mPlayer.getRocketsOn() : false;
    137 
    138             boolean visible = true;
    139 
    140             SoundSystem sound = sSystemRegistry.soundSystem;
    141 
    142             // It's usually not necessary to test to see if sound is enabled or not (when it's disabled,
    143             // play() is just a nop), but in this case I have a stream that is maintained for the rocket
    144             // sounds.  So it's simpler to just avoid that code if sound is off.
    145             if (sound.getSoundEnabled()) {
    146 	            if (boosting) {
    147 	                mLastRocketsOnTime = gameTime;
    148 	            } else {
    149 	                if (gameTime - mLastRocketsOnTime < MIN_ROCKET_TIME
    150 	                        && velocityY >= 0.0f) {
    151 	                    boosting = true;
    152 	                }
    153 	            }
    154 
    155 	            if (mRocketSound != null) {
    156 		            if (boosting) {
    157 		            	if (mRocketSoundStream == -1) {
    158 		            		mRocketSoundStream = sound.play(mRocketSound, true, SoundSystem.PRIORITY_HIGH);
    159 		            		mRocketSoundPaused = false;
    160 		            	} else if (mRocketSoundPaused) {
    161 		            		sound.resume(mRocketSoundStream);
    162 		            		mRocketSoundPaused = false;
    163 		            	}
    164 		            } else {
    165 		            	sound.pause(mRocketSoundStream);
    166 		            	mRocketSoundPaused = true;
    167 		            }
    168 	            }
    169             }
    170 
    171             // Normally, for collectables like the coin, we could just tell the object to play
    172             // a sound when it is collected.  The gems are a special case, though, as we
    173             // want to pick a different sound depending on how many have been collected.
    174             if (mInventory != null && mRubySound1 != null && mRubySound2 != null && mRubySound3 != null) {
    175             	InventoryComponent.UpdateRecord inventory = mInventory.getRecord();
    176             	final int rubyCount = inventory.rubyCount;
    177             	if (rubyCount != mLastRubyCount) {
    178             		mLastRubyCount = rubyCount;
    179             		switch (rubyCount) {
    180             		case 1:
    181             			sound.play(mRubySound1, false, SoundSystem.PRIORITY_NORMAL);
    182             			break;
    183             		case 2:
    184             			sound.play(mRubySound2, false, SoundSystem.PRIORITY_NORMAL);
    185             			break;
    186             		case 3:
    187             			sound.play(mRubySound3, false, SoundSystem.PRIORITY_NORMAL);
    188             			break;
    189             		}
    190 
    191             	}
    192             }
    193 
    194             // Turn on visual effects (smoke, etc) when the player's life reaches 1.
    195             if (mDamageSwap != null) {
    196                 if (parentObject.life == 1 && !mDamageSwap.getCurrentlySwapped()) {
    197                     mDamageSwap.activate(parentObject);
    198                 } else if (parentObject.life != 1 && mDamageSwap.getCurrentlySwapped()) {
    199                     mDamageSwap.activate(parentObject);
    200                 }
    201             }
    202 
    203             float opacity = 1.0f;
    204 
    205             if (currentAction == ActionType.MOVE) {
    206                 InputGameInterface input = sSystemRegistry.inputGameInterface;
    207                 final InputXY dpad = input.getDirectionalPad();
    208                 if (dpad.getX() < 0.0f) {
    209                     parentObject.facingDirection.x = -1.0f;
    210                 } else if (dpad.getX() > 0.0f) {
    211                     parentObject.facingDirection.x = 1.0f;
    212                 }
    213 
    214                 // TODO: get rid of these magic numbers!
    215                 if (touchingGround) {
    216 
    217                     if (Utils.close(velocityX, 0.0f, 30.0f)) {
    218                         mSprite.playAnimation(PlayerAnimations.IDLE.ordinal());
    219                     } else if (Math.abs(velocityX) > 300.0f) {
    220                         mSprite.playAnimation(PlayerAnimations.MOVE_FAST.ordinal());
    221                     } else {
    222                         mSprite.playAnimation(PlayerAnimations.MOVE.ordinal());
    223                     }
    224 
    225                     final InputButton attackButton = input.getAttackButton();
    226 
    227                     if (attackButton.getPressed()) {
    228                         // charge
    229                         final float pressedTime = gameTime - attackButton.getLastPressedTime();
    230                         final float wave = (float)Math.cos(pressedTime * (float)Math.PI * 2.0f);
    231                         opacity = (wave * 0.25f) + 0.75f;
    232                     }
    233 
    234                 } else {
    235                     if (boosting) {
    236                         if (mJetSprite != null) {
    237                             mJetSprite.setVisible(true);
    238                         }
    239 
    240                         if (Math.abs(velocityX) < 100.0f && velocityY > 10.0f) {
    241                             mSprite.playAnimation(PlayerAnimations.BOOST_UP.ordinal());
    242                         } else if (Math.abs(velocityX) > 300.0f) {
    243                             mSprite.playAnimation(PlayerAnimations.BOOST_MOVE_FAST.ordinal());
    244                         } else {
    245                             mSprite.playAnimation(PlayerAnimations.BOOST_MOVE.ordinal());
    246                         }
    247                     } else {
    248 
    249                         if (Utils.close(velocityX, 0.0f, 1.0f)) {
    250                             mSprite.playAnimation(PlayerAnimations.IDLE.ordinal());
    251                         } else if (Math.abs(velocityX) > 300.0f) {
    252                             mSprite.playAnimation(PlayerAnimations.MOVE_FAST.ordinal());
    253                         } else {
    254                             mSprite.playAnimation(PlayerAnimations.MOVE.ordinal());
    255                         }
    256                     }
    257 
    258                 }
    259             } else if (currentAction == ActionType.ATTACK) {
    260                 mSprite.playAnimation(PlayerAnimations.STOMP.ordinal());
    261                 if (touchingGround && gameTime > mLandThumpDelay) {
    262                     if (mLandThump != null && sound != null) {
    263                         // modulate the sound slightly to avoid sounding too similar
    264                         sound.play(mLandThump, false, SoundSystem.PRIORITY_HIGH, 1.0f,
    265                                 (float)(Math.random() * 0.5f) + 0.75f);
    266                         mLandThumpDelay = gameTime + LAND_THUMP_DELAY;
    267                     }
    268                 }
    269             } else if (currentAction == ActionType.HIT_REACT) {
    270                 mSprite.playAnimation(PlayerAnimations.HIT_REACT.ordinal());
    271 
    272                 if (velocityX > 0.0f) {
    273                     parentObject.facingDirection.x = -1.0f;
    274                 } else if (velocityX < 0.0f) {
    275                     parentObject.facingDirection.x = 1.0f;
    276                 }
    277 
    278                 if (mSparksSprite != null) {
    279                     mSparksSprite.setVisible(true);
    280                 }
    281             } else if (currentAction == ActionType.DEATH) {
    282                 if (mPreviousAction != currentAction) {
    283                 	if (mExplosionSound != null) {
    284                 		sound.play(mExplosionSound, false, SoundSystem.PRIORITY_NORMAL);
    285                 	}
    286                 	// by default, explode when hit with the DEATH hit type.
    287                     boolean explodingDeath = parentObject.lastReceivedHitType == HitType.DEATH;
    288                     // or if touching a death tile.
    289                     HotSpotSystem hotSpot = sSystemRegistry.hotSpotSystem;
    290                     if (hotSpot != null) {
    291                         // TODO: HACK!  Unify all this code.
    292                         if (hotSpot.getHotSpot(parentObject.getCenteredPositionX(),
    293                                 parentObject.getPosition().y + 10.0f) == HotSpotSystem.HotSpotType.DIE) {
    294                             explodingDeath = true;
    295                         }
    296                     }
    297                     if (explodingDeath) {
    298                         mExplodingDeath = true;
    299                         GameObjectFactory factory = sSystemRegistry.gameObjectFactory;
    300                         GameObjectManager manager = sSystemRegistry.gameObjectManager;
    301                         if (factory != null && manager != null) {
    302                             GameObject explosion = factory.spawnEffectExplosionGiant(parentObject.getPosition().x, parentObject.getPosition().y);
    303                             if (explosion != null) {
    304                                 manager.add(explosion);
    305                             }
    306                         }
    307                     } else {
    308                         mSprite.playAnimation(PlayerAnimations.DEATH.ordinal());
    309                         mExplodingDeath = false;
    310                     }
    311 
    312                     mFlickerTimeRemaining = 0.0f;
    313                     if (mSparksSprite != null) {
    314                         if (!mSprite.animationFinished()) {
    315                             mSparksSprite.setVisible(true);
    316                         }
    317                     }
    318                 }
    319                 if (mExplodingDeath) {
    320                     visible = false;
    321                 }
    322             } else if (currentAction == ActionType.FROZEN) {
    323                 mSprite.playAnimation(PlayerAnimations.FROZEN.ordinal());
    324             }
    325 
    326             if (mFlickerTimeRemaining > 0.0f) {
    327                 mFlickerTimeRemaining -= timeDelta;
    328                 if (gameTime > mLastFlickerTime + FLICKER_INTERVAL) {
    329                     mLastFlickerTime = gameTime;
    330                     mFlickerOn = !mFlickerOn;
    331                 }
    332                 mSprite.setVisible(mFlickerOn);
    333                 if (mJetSprite != null && mJetSprite.getVisible()) {
    334                     mJetSprite.setVisible(mFlickerOn);
    335                 }
    336             } else {
    337                 mSprite.setVisible(visible);
    338                 mSprite.setOpacity(opacity);
    339             }
    340 
    341             mPreviousAction = currentAction;
    342         }
    343     }
    344 
    345     public void setSprite(SpriteComponent sprite) {
    346         mSprite = sprite;
    347     }
    348 
    349     public void setJetSprite(SpriteComponent sprite) {
    350         mJetSprite = sprite;
    351     }
    352 
    353     public void setSparksSprite(SpriteComponent sprite) {
    354         mSparksSprite = sprite;
    355     }
    356 
    357     public void setPlayer(PlayerComponent player) {
    358         mPlayer = player;
    359     }
    360 
    361     public final void setDamageSwap(ChangeComponentsComponent damageSwap) {
    362         mDamageSwap = damageSwap;
    363     }
    364 
    365     public void setLandThump(Sound land) {
    366         mLandThump = land;
    367     }
    368 
    369 	public void setRocketSound(Sound sound) {
    370 		mRocketSound = sound;
    371 	}
    372 
    373 	public void setRubySounds(Sound one, Sound two, Sound three) {
    374 		mRubySound1 = one;
    375 		mRubySound2 = two;
    376 		mRubySound3 = three;
    377 	}
    378 
    379 	public void setInventory(InventoryComponent inventory) {
    380 		mInventory = inventory;
    381 	}
    382 
    383 	public void setExplosionSound(Sound sound) {
    384 		mExplosionSound = sound;
    385 	}
    386 }
    387