blob: adbb642babd122d89cd5d676e207529d2b90634a [file] [log] [blame]
/*
* 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.GameObject.ActionType;
import com.replica.replicaisland.HotSpotSystem.HotSpotType;
/**
* This component implements the "patrolling" behavior for AI characters. Patrolling characters
* will walk forward on the map until they hit a direction hot spot or a wall, in which case they
* may change direction. Patrollers can also be configured via this component to attack the player
* if appropriate conditions are met.
*/
public class PatrolComponent extends GameComponent {
private float mMaxSpeed;
private float mAcceleration;
private boolean mAttack;
private float mAttackAtDistance;
private boolean mAttackStopsMovement;
private float mAttackDuration;
private float mAttackDelay;
private boolean mTurnToFacePlayer;
private boolean mFlying;
private float mLastAttackTime;
Vector2 mWorkingVector;
Vector2 mWorkingVector2;
public PatrolComponent() {
super();
mWorkingVector = new Vector2();
mWorkingVector2 = new Vector2();
reset();
setPhase(GameComponent.ComponentPhases.THINK.ordinal());
}
@Override
public void reset() {
mTurnToFacePlayer = false;
mMaxSpeed = 0.0f;
mAcceleration = 0.0f;
mAttack = false;
mAttackAtDistance = 0.0f;
mAttackStopsMovement = false;
mAttackDuration = 0.0f;
mAttackDelay = 0.0f;
mWorkingVector.zero();
mWorkingVector2.zero();
mFlying = false;
}
@Override
public void update(float timeDelta, BaseObject parent) {
GameObject parentObject = (GameObject) parent;
if (parentObject.getCurrentAction() == ActionType.INVALID
|| parentObject.getCurrentAction() == ActionType.HIT_REACT) {
parentObject.setCurrentAction(GameObject.ActionType.MOVE);
}
if ((mFlying || parentObject.touchingGround()) && parentObject.life > 0) {
GameObjectManager manager = sSystemRegistry.gameObjectManager;
GameObject player = null;
if (manager != null) {
player = manager.getPlayer();
}
if (mAttack) {
updateAttack(player, parentObject);
}
if (parentObject.getCurrentAction() == GameObject.ActionType.MOVE
&& mMaxSpeed > 0.0f) {
int hotSpot = HotSpotSystem.HotSpotType.NONE;
HotSpotSystem hotSpotSystem = sSystemRegistry.hotSpotSystem;
if (hotSpotSystem != null) {
// TODO: ack, magic number
hotSpot = hotSpotSystem.getHotSpot(parentObject.getCenteredPositionX(),
parentObject.getPosition().y + 10.0f);
}
final float targetVelocityX = parentObject.getTargetVelocity().x;
final float targetVelocityY = parentObject.getTargetVelocity().y;
boolean goLeft = (parentObject.touchingRightWall()
|| hotSpot == HotSpotType.GO_LEFT) && targetVelocityX >= 0.0f;
boolean goRight = (parentObject.touchingLeftWall()
|| hotSpot == HotSpotType.GO_RIGHT) && targetVelocityX <= 0.0f;
boolean pause = (mMaxSpeed == 0.0f) || hotSpot == HotSpotType.GO_DOWN;
if (mTurnToFacePlayer && player != null && player.life > 0) {
final float horizontalDelta = player.getCenteredPositionX()
- parentObject.getCenteredPositionX();
final int targetFacingDirection = Utils.sign(horizontalDelta);
final float closestDistance = player.width / 2.0f;
if (targetFacingDirection < 0.0f) { // we want to turn to the left
if (goRight) {
goRight = false;
pause = true;
} else if (targetFacingDirection
!= Utils.sign(parentObject.facingDirection.x)) {
goLeft = true;
}
} else if (targetFacingDirection > 0.0f) { // we want to turn to the right
if (goLeft) {
goLeft = false;
pause = true;
} else if (targetFacingDirection
!= Utils.sign(parentObject.facingDirection.x)) {
goRight = true;
}
}
if (Math.abs(horizontalDelta) < closestDistance) {
goRight = false;
goLeft = false;
pause = true;
}
}
if (!mFlying) {
if (!pause && !goLeft && !goRight && targetVelocityX == 0.0f) {
if (parentObject.facingDirection.x < 0.0f) {
goLeft = true;
} else {
goRight = true;
}
}
if (goRight) {
parentObject.getTargetVelocity().x = mMaxSpeed;
parentObject.getAcceleration().x = mAcceleration;
} else if (goLeft) {
parentObject.getTargetVelocity().x = -mMaxSpeed;
parentObject.getAcceleration().x = mAcceleration;
} else if (pause) {
parentObject.getTargetVelocity().x = 0;
parentObject.getAcceleration().x = mAcceleration;
}
} else {
final boolean goUp = (parentObject.touchingGround() && targetVelocityY < 0.0f)
|| hotSpot == HotSpotType.GO_UP;
final boolean goDown = (parentObject.touchingCeiling() && targetVelocityY > 0.0f)
|| hotSpot == HotSpotType.GO_DOWN;
if (goUp) {
parentObject.getTargetVelocity().x = 0.0f;
parentObject.getTargetVelocity().y = mMaxSpeed;
parentObject.getAcceleration().y = mAcceleration;
parentObject.getAcceleration().x = mAcceleration;
} else if (goDown) {
parentObject.getTargetVelocity().x = 0.0f;
parentObject.getTargetVelocity().y = -mMaxSpeed;
parentObject.getAcceleration().y = mAcceleration;
parentObject.getAcceleration().x = mAcceleration;
} else if (goRight) {
parentObject.getTargetVelocity().x = mMaxSpeed;
parentObject.getAcceleration().x = mAcceleration;
parentObject.getAcceleration().y = mAcceleration;
parentObject.getTargetVelocity().y = 0.0f;
} else if (goLeft) {
parentObject.getTargetVelocity().x = -mMaxSpeed;
parentObject.getAcceleration().x = mAcceleration;
parentObject.getAcceleration().y = mAcceleration;
parentObject.getTargetVelocity().y = 0.0f;
}
}
}
} else if (!mFlying && !parentObject.touchingGround() && parentObject.life > 0) {
// A non-flying unit is in the air. In this case, just watch for bounces off walls.
if (Utils.sign(parentObject.getTargetVelocity().x) != Utils.sign(parentObject.getVelocity().x)) {
// Todo: maybe the physics code should adjust target velocity instead in this case?
parentObject.getTargetVelocity().x *= -1.0f;
}
}
}
private void updateAttack(GameObject player, GameObject parentObject) {
TimeSystem time = sSystemRegistry.timeSystem;
final float gameTime = time.getGameTime();
boolean visible = true;
CameraSystem camera = sSystemRegistry.cameraSystem;
ContextParameters context = sSystemRegistry.contextParameters;
final float dx =
Math.abs(parentObject.getCenteredPositionX() - camera.getFocusPositionX());
final float dy =
Math.abs(parentObject.getCenteredPositionY() - camera.getFocusPositionY());
if (dx > context.gameWidth / 2.0f || dy > context.gameHeight / 2.0f) {
visible = false;
}
if (visible && parentObject.getCurrentAction() == GameObject.ActionType.MOVE) {
boolean closeEnough = false;
boolean timeToAttack = (gameTime - mLastAttackTime) > mAttackDelay;
if (mAttackAtDistance > 0 && player != null && player.life > 0
&& timeToAttack) {
// only attack if we are facing the player
if (Utils.sign(player.getPosition().x - parentObject.getPosition().x)
== Utils.sign(parentObject.facingDirection.x)) {
mWorkingVector.set(parentObject.getPosition());
mWorkingVector.x = parentObject.getCenteredPositionX();
mWorkingVector2.set(player.getPosition());
mWorkingVector2.x = player.getCenteredPositionX();
if (mWorkingVector2.distance2(mWorkingVector) <
mAttackAtDistance * mAttackAtDistance) {
closeEnough = true;
}
}
} else {
closeEnough = true; // If no distance has been set, don't worry about
// the player's position.
}
if (timeToAttack && closeEnough) {
// Time to attack.
parentObject.setCurrentAction(GameObject.ActionType.ATTACK);
mLastAttackTime = gameTime;
if (mAttackStopsMovement) {
parentObject.getVelocity().zero();
parentObject.getTargetVelocity().zero();
}
}
} else if (parentObject.getCurrentAction() == GameObject.ActionType.ATTACK) {
if (gameTime - mLastAttackTime > mAttackDuration) {
parentObject.setCurrentAction(GameObject.ActionType.MOVE);
if (mAttackStopsMovement) {
parentObject.getTargetVelocity().x =
mMaxSpeed * Utils.sign(parentObject.facingDirection.x);
parentObject.getAcceleration().x = mAcceleration;
}
}
}
}
public void setMovementSpeed(float speed, float acceleration) {
mMaxSpeed = speed;
mAcceleration = acceleration;
}
public void setupAttack(float distance, float duration, float delay, boolean stopMovement) {
mAttack = true;
mAttackAtDistance = distance;
mAttackStopsMovement = stopMovement;
mAttackDuration = duration;
mAttackDelay = delay;
}
public void setTurnToFacePlayer(boolean turn) {
mTurnToFacePlayer = turn;
}
public void setFlying(boolean flying) {
mFlying = flying;
}
}