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.GameObject.ActionType;
     20 import com.replica.replicaisland.HotSpotSystem.HotSpotType;
     21 
     22 /**
     23  * This component implements the "patrolling" behavior for AI characters.  Patrolling characters
     24  * will walk forward on the map until they hit a direction hot spot or a wall, in which case they
     25  * may change direction.  Patrollers can also be configured via this component to attack the player
     26  * if appropriate conditions are met.
     27  */
     28 public class PatrolComponent extends GameComponent {
     29     private float mMaxSpeed;
     30     private float mAcceleration;
     31     private boolean mAttack;
     32     private float mAttackAtDistance;
     33     private boolean mAttackStopsMovement;
     34     private float mAttackDuration;
     35     private float mAttackDelay;
     36     private boolean mTurnToFacePlayer;
     37     private boolean mFlying;
     38 
     39     private float mLastAttackTime;
     40     Vector2 mWorkingVector;
     41     Vector2 mWorkingVector2;
     42 
     43 
     44     public PatrolComponent() {
     45         super();
     46         mWorkingVector = new Vector2();
     47         mWorkingVector2 = new Vector2();
     48 
     49         reset();
     50         setPhase(GameComponent.ComponentPhases.THINK.ordinal());
     51     }
     52 
     53     @Override
     54     public void reset() {
     55         mTurnToFacePlayer = false;
     56         mMaxSpeed = 0.0f;
     57         mAcceleration = 0.0f;
     58         mAttack = false;
     59         mAttackAtDistance = 0.0f;
     60         mAttackStopsMovement = false;
     61         mAttackDuration = 0.0f;
     62         mAttackDelay = 0.0f;
     63         mWorkingVector.zero();
     64         mWorkingVector2.zero();
     65         mFlying = false;
     66     }
     67 
     68     @Override
     69     public void update(float timeDelta, BaseObject parent) {
     70         GameObject parentObject = (GameObject) parent;
     71 
     72         if (parentObject.getCurrentAction() == ActionType.INVALID
     73         	|| parentObject.getCurrentAction() == ActionType.HIT_REACT) {
     74             parentObject.setCurrentAction(GameObject.ActionType.MOVE);
     75         }
     76 
     77         if ((mFlying || parentObject.touchingGround()) && parentObject.life > 0) {
     78             GameObjectManager manager = sSystemRegistry.gameObjectManager;
     79             GameObject player = null;
     80             if (manager != null) {
     81                 player = manager.getPlayer();
     82             }
     83 
     84             if (mAttack) {
     85                 updateAttack(player, parentObject);
     86             }
     87 
     88 
     89             if (parentObject.getCurrentAction() == GameObject.ActionType.MOVE
     90                     && mMaxSpeed > 0.0f) {
     91                 int hotSpot = HotSpotSystem.HotSpotType.NONE;
     92                 HotSpotSystem hotSpotSystem = sSystemRegistry.hotSpotSystem;
     93                 if (hotSpotSystem != null) {
     94                     // TODO: ack, magic number
     95                     hotSpot = hotSpotSystem.getHotSpot(parentObject.getCenteredPositionX(),
     96                             parentObject.getPosition().y + 10.0f);
     97                 }
     98                 final float targetVelocityX = parentObject.getTargetVelocity().x;
     99                 final float targetVelocityY = parentObject.getTargetVelocity().y;
    100 
    101                 boolean goLeft = (parentObject.touchingRightWall()
    102                         || hotSpot == HotSpotType.GO_LEFT) && targetVelocityX >= 0.0f;
    103 
    104                 boolean goRight = (parentObject.touchingLeftWall()
    105                         || hotSpot == HotSpotType.GO_RIGHT) && targetVelocityX <= 0.0f;
    106 
    107                 boolean pause = (mMaxSpeed == 0.0f) || hotSpot == HotSpotType.GO_DOWN;
    108 
    109                 if (mTurnToFacePlayer && player != null && player.life > 0) {
    110                     final float horizontalDelta = player.getCenteredPositionX()
    111                         - parentObject.getCenteredPositionX();
    112                     final int targetFacingDirection = Utils.sign(horizontalDelta);
    113                     final float closestDistance = player.width / 2.0f;
    114 
    115                     if (targetFacingDirection < 0.0f) { // we want to turn to the left
    116                         if (goRight) {
    117                             goRight = false;
    118                             pause = true;
    119                         } else if (targetFacingDirection
    120                                 != Utils.sign(parentObject.facingDirection.x)) {
    121                             goLeft = true;
    122                         }
    123                     } else if (targetFacingDirection > 0.0f) { // we want to turn to the right
    124                         if (goLeft) {
    125                             goLeft = false;
    126                             pause = true;
    127                         } else if (targetFacingDirection
    128                                 != Utils.sign(parentObject.facingDirection.x)) {
    129                             goRight = true;
    130                         }
    131                     }
    132 
    133                     if (Math.abs(horizontalDelta) < closestDistance) {
    134                         goRight = false;
    135                         goLeft = false;
    136                         pause = true;
    137                     }
    138                 }
    139 
    140                 if (!mFlying) {
    141                     if (!pause && !goLeft && !goRight && targetVelocityX == 0.0f) {
    142                         if (parentObject.facingDirection.x < 0.0f) {
    143                             goLeft = true;
    144                         } else {
    145                             goRight = true;
    146                         }
    147                     }
    148 
    149 
    150                     if (goRight) {
    151                         parentObject.getTargetVelocity().x = mMaxSpeed;
    152                         parentObject.getAcceleration().x = mAcceleration;
    153                     } else if (goLeft) {
    154                         parentObject.getTargetVelocity().x = -mMaxSpeed;
    155                         parentObject.getAcceleration().x = mAcceleration;
    156                     } else if (pause) {
    157                         parentObject.getTargetVelocity().x = 0;
    158                         parentObject.getAcceleration().x = mAcceleration;
    159                     }
    160                 } else {
    161                     final boolean goUp = (parentObject.touchingGround() && targetVelocityY < 0.0f)
    162                     	|| hotSpot == HotSpotType.GO_UP;
    163 
    164                     final boolean goDown = (parentObject.touchingCeiling() && targetVelocityY > 0.0f)
    165                             || hotSpot == HotSpotType.GO_DOWN;
    166 
    167                     if (goUp) {
    168                         parentObject.getTargetVelocity().x = 0.0f;
    169                         parentObject.getTargetVelocity().y = mMaxSpeed;
    170                         parentObject.getAcceleration().y = mAcceleration;
    171                         parentObject.getAcceleration().x = mAcceleration;
    172 
    173                     } else if (goDown) {
    174                         parentObject.getTargetVelocity().x = 0.0f;
    175                         parentObject.getTargetVelocity().y = -mMaxSpeed;
    176                         parentObject.getAcceleration().y = mAcceleration;
    177                         parentObject.getAcceleration().x = mAcceleration;
    178 
    179                     } else if (goRight) {
    180                         parentObject.getTargetVelocity().x = mMaxSpeed;
    181                         parentObject.getAcceleration().x = mAcceleration;
    182                         parentObject.getAcceleration().y = mAcceleration;
    183                         parentObject.getTargetVelocity().y = 0.0f;
    184                     } else if (goLeft) {
    185                         parentObject.getTargetVelocity().x = -mMaxSpeed;
    186                         parentObject.getAcceleration().x = mAcceleration;
    187                         parentObject.getAcceleration().y = mAcceleration;
    188                         parentObject.getTargetVelocity().y = 0.0f;
    189                     }
    190                 }
    191             }
    192         } else if (!mFlying && !parentObject.touchingGround() && parentObject.life > 0) {
    193         	// A non-flying unit is in the air.  In this case, just watch for bounces off walls.
    194         	if (Utils.sign(parentObject.getTargetVelocity().x) != Utils.sign(parentObject.getVelocity().x)) {
    195         		// Todo: maybe the physics code should adjust target velocity instead in this case?
    196         		parentObject.getTargetVelocity().x *= -1.0f;
    197         	}
    198         }
    199     }
    200 
    201     private void updateAttack(GameObject player, GameObject parentObject) {
    202         TimeSystem time = sSystemRegistry.timeSystem;
    203         final float gameTime = time.getGameTime();
    204 
    205         boolean visible = true;
    206         CameraSystem camera = sSystemRegistry.cameraSystem;
    207         ContextParameters context = sSystemRegistry.contextParameters;
    208         final float dx =
    209             Math.abs(parentObject.getCenteredPositionX() - camera.getFocusPositionX());
    210         final float dy =
    211             Math.abs(parentObject.getCenteredPositionY() - camera.getFocusPositionY());
    212         if (dx > context.gameWidth / 2.0f || dy > context.gameHeight / 2.0f) {
    213             visible = false;
    214         }
    215         if (visible && parentObject.getCurrentAction() == GameObject.ActionType.MOVE) {
    216             boolean closeEnough = false;
    217             boolean timeToAttack = (gameTime - mLastAttackTime) > mAttackDelay;
    218             if (mAttackAtDistance > 0 && player != null && player.life > 0
    219                     && timeToAttack) {
    220                 // only attack if we are facing the player
    221                 if (Utils.sign(player.getPosition().x - parentObject.getPosition().x)
    222                         == Utils.sign(parentObject.facingDirection.x)) {
    223                     mWorkingVector.set(parentObject.getPosition());
    224                     mWorkingVector.x = parentObject.getCenteredPositionX();
    225                     mWorkingVector2.set(player.getPosition());
    226                     mWorkingVector2.x = player.getCenteredPositionX();
    227                     if (mWorkingVector2.distance2(mWorkingVector) <
    228                         mAttackAtDistance * mAttackAtDistance) {
    229                         closeEnough = true;
    230                     }
    231                 }
    232             } else {
    233                 closeEnough = true;  // If no distance has been set, don't worry about
    234                                      // the player's position.
    235             }
    236 
    237             if (timeToAttack && closeEnough) {
    238                 // Time to attack.
    239                 parentObject.setCurrentAction(GameObject.ActionType.ATTACK);
    240                 mLastAttackTime = gameTime;
    241                 if (mAttackStopsMovement) {
    242                     parentObject.getVelocity().zero();
    243                     parentObject.getTargetVelocity().zero();
    244                 }
    245             }
    246         } else if (parentObject.getCurrentAction() == GameObject.ActionType.ATTACK) {
    247             if (gameTime - mLastAttackTime > mAttackDuration) {
    248                 parentObject.setCurrentAction(GameObject.ActionType.MOVE);
    249                 if (mAttackStopsMovement) {
    250                     parentObject.getTargetVelocity().x =
    251                         mMaxSpeed * Utils.sign(parentObject.facingDirection.x);
    252                     parentObject.getAcceleration().x = mAcceleration;
    253                 }
    254             }
    255         }
    256     }
    257 
    258     public void setMovementSpeed(float speed, float acceleration) {
    259         mMaxSpeed = speed;
    260         mAcceleration = acceleration;
    261     }
    262 
    263     public void setupAttack(float distance, float duration, float delay, boolean stopMovement) {
    264         mAttack = true;
    265         mAttackAtDistance = distance;
    266         mAttackStopsMovement = stopMovement;
    267         mAttackDuration = duration;
    268         mAttackDelay = delay;
    269     }
    270 
    271     public void setTurnToFacePlayer(boolean turn) {
    272         mTurnToFacePlayer = turn;
    273     }
    274 
    275     public void setFlying(boolean flying) {
    276         mFlying = flying;
    277     }
    278 }
    279