| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.replica.replicaisland; |
| |
| |
| import com.replica.replicaisland.CollisionParameters.HitType; |
| import com.replica.replicaisland.GameObject.ActionType; |
| import com.replica.replicaisland.SoundSystem.Sound; |
| |
| /** |
| * Player Animation game object component. Responsible for selecting an animation to describe the |
| * player's current state. Requires the object to contain a SpriteComponent to play animations. |
| */ |
| public class AnimationComponent extends GameComponent { |
| |
| public enum PlayerAnimations { |
| IDLE, |
| MOVE, |
| MOVE_FAST, |
| BOOST_UP, |
| BOOST_MOVE, |
| BOOST_MOVE_FAST, |
| STOMP, |
| HIT_REACT, |
| DEATH, |
| FROZEN |
| } |
| |
| private static final float MIN_ROCKET_TIME = 0.0f; |
| private static final float FLICKER_INTERVAL = 0.15f; |
| private static final float FLICKER_DURATION = 3.0f; |
| private static final float LAND_THUMP_DELAY = 0.5f; |
| |
| private SpriteComponent mSprite; |
| private SpriteComponent mJetSprite; |
| private SpriteComponent mSparksSprite; |
| |
| private PlayerComponent mPlayer; |
| private float mLastFlickerTime; |
| private boolean mFlickerOn; |
| private float mFlickerTimeRemaining; |
| |
| private GameObject.ActionType mPreviousAction; |
| |
| private float mLastRocketsOnTime; |
| private boolean mExplodingDeath; |
| |
| private ChangeComponentsComponent mDamageSwap; |
| private Sound mLandThump; |
| private Sound mRocketSound; |
| private Sound mExplosionSound; |
| private float mLandThumpDelay; |
| private int mRocketSoundStream; |
| private boolean mRocketSoundPaused; |
| |
| private int mLastRubyCount; |
| private Sound mRubySound1; |
| private Sound mRubySound2; |
| private Sound mRubySound3; |
| private InventoryComponent mInventory; |
| |
| |
| public AnimationComponent() { |
| super(); |
| reset(); |
| setPhase(ComponentPhases.ANIMATION.ordinal()); |
| } |
| |
| @Override |
| public void reset() { |
| mPreviousAction = ActionType.INVALID; |
| mSprite = null; |
| mJetSprite = null; |
| mSparksSprite = null; |
| mPlayer = null; |
| mLastFlickerTime = 0.0f; |
| mFlickerOn = false; |
| mFlickerTimeRemaining = 0.0f; |
| mLastRocketsOnTime = 0.0f; |
| mExplodingDeath = false; |
| mDamageSwap = null; |
| mLandThump = null; |
| mLandThumpDelay = 0.0f; |
| mRocketSound = null; |
| mRocketSoundStream = -1; |
| mLastRubyCount = 0; |
| mInventory = null; |
| mExplosionSound = null; |
| } |
| |
| @Override |
| public void update(float timeDelta, BaseObject parent) { |
| if (mSprite != null) { |
| |
| GameObject parentObject = (GameObject) parent; |
| |
| final float velocityX = parentObject.getVelocity().x; |
| final float velocityY = parentObject.getVelocity().y; |
| |
| |
| GameObject.ActionType currentAction = parentObject.getCurrentAction(); |
| |
| if (mJetSprite != null) { |
| mJetSprite.setVisible(false); |
| } |
| |
| if (mSparksSprite != null) { |
| mSparksSprite.setVisible(false); |
| } |
| |
| |
| final TimeSystem time = sSystemRegistry.timeSystem; |
| final float gameTime = time.getGameTime(); |
| |
| if (currentAction != ActionType.HIT_REACT && mPreviousAction == ActionType.HIT_REACT) { |
| mFlickerTimeRemaining = FLICKER_DURATION; |
| } |
| |
| |
| final boolean touchingGround = parentObject.touchingGround(); |
| |
| boolean boosting = mPlayer != null ? mPlayer.getRocketsOn() : false; |
| |
| boolean visible = true; |
| |
| SoundSystem sound = sSystemRegistry.soundSystem; |
| |
| // It's usually not necessary to test to see if sound is enabled or not (when it's disabled, |
| // play() is just a nop), but in this case I have a stream that is maintained for the rocket |
| // sounds. So it's simpler to just avoid that code if sound is off. |
| if (sound.getSoundEnabled()) { |
| if (boosting) { |
| mLastRocketsOnTime = gameTime; |
| } else { |
| if (gameTime - mLastRocketsOnTime < MIN_ROCKET_TIME |
| && velocityY >= 0.0f) { |
| boosting = true; |
| } |
| } |
| |
| if (mRocketSound != null) { |
| if (boosting) { |
| if (mRocketSoundStream == -1) { |
| mRocketSoundStream = sound.play(mRocketSound, true, SoundSystem.PRIORITY_HIGH); |
| mRocketSoundPaused = false; |
| } else if (mRocketSoundPaused) { |
| sound.resume(mRocketSoundStream); |
| mRocketSoundPaused = false; |
| } |
| } else { |
| sound.pause(mRocketSoundStream); |
| mRocketSoundPaused = true; |
| } |
| } |
| } |
| |
| // Normally, for collectables like the coin, we could just tell the object to play |
| // a sound when it is collected. The gems are a special case, though, as we |
| // want to pick a different sound depending on how many have been collected. |
| if (mInventory != null && mRubySound1 != null && mRubySound2 != null && mRubySound3 != null) { |
| InventoryComponent.UpdateRecord inventory = mInventory.getRecord(); |
| final int rubyCount = inventory.rubyCount; |
| if (rubyCount != mLastRubyCount) { |
| mLastRubyCount = rubyCount; |
| switch (rubyCount) { |
| case 1: |
| sound.play(mRubySound1, false, SoundSystem.PRIORITY_NORMAL); |
| break; |
| case 2: |
| sound.play(mRubySound2, false, SoundSystem.PRIORITY_NORMAL); |
| break; |
| case 3: |
| sound.play(mRubySound3, false, SoundSystem.PRIORITY_NORMAL); |
| break; |
| } |
| |
| } |
| } |
| |
| // Turn on visual effects (smoke, etc) when the player's life reaches 1. |
| if (mDamageSwap != null) { |
| if (parentObject.life == 1 && !mDamageSwap.getCurrentlySwapped()) { |
| mDamageSwap.activate(parentObject); |
| } else if (parentObject.life != 1 && mDamageSwap.getCurrentlySwapped()) { |
| mDamageSwap.activate(parentObject); |
| } |
| } |
| |
| float opacity = 1.0f; |
| |
| if (currentAction == ActionType.MOVE) { |
| InputGameInterface input = sSystemRegistry.inputGameInterface; |
| final InputXY dpad = input.getDirectionalPad(); |
| if (dpad.getX() < 0.0f) { |
| parentObject.facingDirection.x = -1.0f; |
| } else if (dpad.getX() > 0.0f) { |
| parentObject.facingDirection.x = 1.0f; |
| } |
| |
| // TODO: get rid of these magic numbers! |
| if (touchingGround) { |
| |
| if (Utils.close(velocityX, 0.0f, 30.0f)) { |
| mSprite.playAnimation(PlayerAnimations.IDLE.ordinal()); |
| } else if (Math.abs(velocityX) > 300.0f) { |
| mSprite.playAnimation(PlayerAnimations.MOVE_FAST.ordinal()); |
| } else { |
| mSprite.playAnimation(PlayerAnimations.MOVE.ordinal()); |
| } |
| |
| final InputButton attackButton = input.getAttackButton(); |
| |
| if (attackButton.getPressed()) { |
| // charge |
| final float pressedTime = gameTime - attackButton.getLastPressedTime(); |
| final float wave = (float)Math.cos(pressedTime * (float)Math.PI * 2.0f); |
| opacity = (wave * 0.25f) + 0.75f; |
| } |
| |
| } else { |
| if (boosting) { |
| if (mJetSprite != null) { |
| mJetSprite.setVisible(true); |
| } |
| |
| if (Math.abs(velocityX) < 100.0f && velocityY > 10.0f) { |
| mSprite.playAnimation(PlayerAnimations.BOOST_UP.ordinal()); |
| } else if (Math.abs(velocityX) > 300.0f) { |
| mSprite.playAnimation(PlayerAnimations.BOOST_MOVE_FAST.ordinal()); |
| } else { |
| mSprite.playAnimation(PlayerAnimations.BOOST_MOVE.ordinal()); |
| } |
| } else { |
| |
| if (Utils.close(velocityX, 0.0f, 1.0f)) { |
| mSprite.playAnimation(PlayerAnimations.IDLE.ordinal()); |
| } else if (Math.abs(velocityX) > 300.0f) { |
| mSprite.playAnimation(PlayerAnimations.MOVE_FAST.ordinal()); |
| } else { |
| mSprite.playAnimation(PlayerAnimations.MOVE.ordinal()); |
| } |
| } |
| |
| } |
| } else if (currentAction == ActionType.ATTACK) { |
| mSprite.playAnimation(PlayerAnimations.STOMP.ordinal()); |
| if (touchingGround && gameTime > mLandThumpDelay) { |
| if (mLandThump != null && sound != null) { |
| // modulate the sound slightly to avoid sounding too similar |
| sound.play(mLandThump, false, SoundSystem.PRIORITY_HIGH, 1.0f, |
| (float)(Math.random() * 0.5f) + 0.75f); |
| mLandThumpDelay = gameTime + LAND_THUMP_DELAY; |
| } |
| } |
| } else if (currentAction == ActionType.HIT_REACT) { |
| mSprite.playAnimation(PlayerAnimations.HIT_REACT.ordinal()); |
| |
| if (velocityX > 0.0f) { |
| parentObject.facingDirection.x = -1.0f; |
| } else if (velocityX < 0.0f) { |
| parentObject.facingDirection.x = 1.0f; |
| } |
| |
| if (mSparksSprite != null) { |
| mSparksSprite.setVisible(true); |
| } |
| } else if (currentAction == ActionType.DEATH) { |
| if (mPreviousAction != currentAction) { |
| if (mExplosionSound != null) { |
| sound.play(mExplosionSound, false, SoundSystem.PRIORITY_NORMAL); |
| } |
| // by default, explode when hit with the DEATH hit type. |
| boolean explodingDeath = parentObject.lastReceivedHitType == HitType.DEATH; |
| // or if touching a death tile. |
| HotSpotSystem hotSpot = sSystemRegistry.hotSpotSystem; |
| if (hotSpot != null) { |
| // TODO: HACK! Unify all this code. |
| if (hotSpot.getHotSpot(parentObject.getCenteredPositionX(), |
| parentObject.getPosition().y + 10.0f) == HotSpotSystem.HotSpotType.DIE) { |
| explodingDeath = true; |
| } |
| } |
| if (explodingDeath) { |
| mExplodingDeath = true; |
| GameObjectFactory factory = sSystemRegistry.gameObjectFactory; |
| GameObjectManager manager = sSystemRegistry.gameObjectManager; |
| if (factory != null && manager != null) { |
| GameObject explosion = factory.spawnEffectExplosionGiant(parentObject.getPosition().x, parentObject.getPosition().y); |
| if (explosion != null) { |
| manager.add(explosion); |
| } |
| } |
| } else { |
| mSprite.playAnimation(PlayerAnimations.DEATH.ordinal()); |
| mExplodingDeath = false; |
| } |
| |
| mFlickerTimeRemaining = 0.0f; |
| if (mSparksSprite != null) { |
| if (!mSprite.animationFinished()) { |
| mSparksSprite.setVisible(true); |
| } |
| } |
| } |
| if (mExplodingDeath) { |
| visible = false; |
| } |
| } else if (currentAction == ActionType.FROZEN) { |
| mSprite.playAnimation(PlayerAnimations.FROZEN.ordinal()); |
| } |
| |
| if (mFlickerTimeRemaining > 0.0f) { |
| mFlickerTimeRemaining -= timeDelta; |
| if (gameTime > mLastFlickerTime + FLICKER_INTERVAL) { |
| mLastFlickerTime = gameTime; |
| mFlickerOn = !mFlickerOn; |
| } |
| mSprite.setVisible(mFlickerOn); |
| if (mJetSprite != null && mJetSprite.getVisible()) { |
| mJetSprite.setVisible(mFlickerOn); |
| } |
| } else { |
| mSprite.setVisible(visible); |
| mSprite.setOpacity(opacity); |
| } |
| |
| mPreviousAction = currentAction; |
| } |
| } |
| |
| public void setSprite(SpriteComponent sprite) { |
| mSprite = sprite; |
| } |
| |
| public void setJetSprite(SpriteComponent sprite) { |
| mJetSprite = sprite; |
| } |
| |
| public void setSparksSprite(SpriteComponent sprite) { |
| mSparksSprite = sprite; |
| } |
| |
| public void setPlayer(PlayerComponent player) { |
| mPlayer = player; |
| } |
| |
| public final void setDamageSwap(ChangeComponentsComponent damageSwap) { |
| mDamageSwap = damageSwap; |
| } |
| |
| public void setLandThump(Sound land) { |
| mLandThump = land; |
| } |
| |
| public void setRocketSound(Sound sound) { |
| mRocketSound = sound; |
| } |
| |
| public void setRubySounds(Sound one, Sound two, Sound three) { |
| mRubySound1 = one; |
| mRubySound2 = two; |
| mRubySound3 = three; |
| } |
| |
| public void setInventory(InventoryComponent inventory) { |
| mInventory = inventory; |
| } |
| |
| public void setExplosionSound(Sound sound) { |
| mExplosionSound = sound; |
| } |
| } |