blob: e660e1e967bee8be118b06726afbfc7e19c242cb [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.CollisionParameters.HitType;
import com.replica.replicaisland.GameObject.ActionType;
public class NPCComponent extends GameComponent {
private float mPauseTime;
private float mTargetXVelocity;
private int mLastHitTileX;
private int mLastHitTileY;
private int mDialogEvent;
private int mDialogIndex;
private HitReactionComponent mHitReactComponent;
private int[] mQueuedCommands;
private int mQueueTop;
private int mQueueBottom;
private boolean mExecutingQueue;
private Vector2 mPreviousPosition;
private float mUpImpulse;
private float mDownImpulse;
private float mHorizontalImpulse;
private float mSlowHorizontalImpulse;
private float mAcceleration;
private int mGameEvent;
private int mGameEventIndex;
private boolean mSpawnGameEventOnDeath;
private boolean mReactToHits;
private boolean mFlying;
private boolean mPauseOnAttack;
private float mDeathTime;
private float mDeathFadeDelay;
private static final float UP_IMPULSE = 400.0f;
private static final float DOWN_IMPULSE = -10.0f;
private static final float HORIZONTAL_IMPULSE = 200.0f;
private static final float SLOW_HORIZONTAL_IMPULSE = 50.0f;
private static final float ACCELERATION = 300.0f;
private static final float HIT_IMPULSE = 300.0f;
private static final float HIT_ACCELERATION = 700.0f;
private static final float DEATH_FADE_DELAY = 4.0f;
private static final float PAUSE_TIME_SHORT = 1.0f;
private static final float PAUSE_TIME_MEDIUM = 4.0f;
private static final float PAUSE_TIME_LONG = 8.0f;
private static final float PAUSE_TIME_ATTACK = 1.0f;
private static final float PAUSE_TIME_HIT_REACT = 1.0f;
private static final int COMMAND_QUEUE_SIZE = 16;
public NPCComponent() {
super();
setPhase(ComponentPhases.THINK.ordinal());
mQueuedCommands = new int[COMMAND_QUEUE_SIZE];
mPreviousPosition = new Vector2();
reset();
}
@Override
public void reset() {
mPauseTime = 0.0f;
mTargetXVelocity = 0.0f;
mLastHitTileX = 0;
mLastHitTileY = 0;
mDialogEvent = GameFlowEvent.EVENT_SHOW_DIALOG_CHARACTER1;
mDialogIndex = 0;
mHitReactComponent = null;
mQueueTop = 0;
mQueueBottom = 0;
mPreviousPosition.zero();
mExecutingQueue = false;
mUpImpulse = UP_IMPULSE;
mDownImpulse = DOWN_IMPULSE;
mHorizontalImpulse = HORIZONTAL_IMPULSE;
mSlowHorizontalImpulse = SLOW_HORIZONTAL_IMPULSE;
mAcceleration = ACCELERATION;
mGameEvent = -1;
mGameEventIndex = -1;
mSpawnGameEventOnDeath = false;
mReactToHits = false;
mFlying = false;
mDeathTime = 0.0f;
mDeathFadeDelay = DEATH_FADE_DELAY;
mPauseOnAttack = true;
}
@Override
public void update(float timeDelta, BaseObject parent) {
GameObject parentObject = (GameObject)parent;
if (mReactToHits &&
mPauseTime <= 0.0f &&
parentObject.getCurrentAction() == ActionType.HIT_REACT) {
mPauseTime = PAUSE_TIME_HIT_REACT;
pauseMovement(parentObject);
parentObject.getVelocity().x = -parentObject.facingDirection.x * HIT_IMPULSE;
parentObject.getAcceleration().x = HIT_ACCELERATION;
} else if (parentObject.getCurrentAction() == ActionType.DEATH) {
if (mSpawnGameEventOnDeath && mGameEvent != -1) {
if (Utils.close(parentObject.getVelocity().x, 0.0f)
&& parentObject.touchingGround()) {
if (mDeathTime < mDeathFadeDelay && mDeathTime + timeDelta >= mDeathFadeDelay) {
HudSystem hud = sSystemRegistry.hudSystem;
if (hud != null) {
hud.startFade(false, 1.5f);
hud.sendGameEventOnFadeComplete(mGameEvent, mGameEventIndex);
mGameEvent = -1;
}
}
mDeathTime += timeDelta;
}
}
// nothing else to do.
return;
} else if (parentObject.life <= 0) {
parentObject.setCurrentAction(ActionType.DEATH);
parentObject.getTargetVelocity().x = 0;
return;
} else if (parentObject.getCurrentAction() == ActionType.INVALID ||
(!mReactToHits && parentObject.getCurrentAction() == ActionType.HIT_REACT)) {
parentObject.setCurrentAction(ActionType.MOVE);
}
if (mPauseTime <= 0.0f) {
HotSpotSystem hotSpotSystem = sSystemRegistry.hotSpotSystem;
if (hotSpotSystem != null) {
final float centerX = parentObject.getCenteredPositionX();
final int hitTileX = hotSpotSystem.getHitTileX(centerX);
final int hitTileY = hotSpotSystem.getHitTileY(parentObject.getPosition().y + 10.0f);
boolean accepted = true;
if (hitTileX != mLastHitTileX || hitTileY != mLastHitTileY) {
final int hotSpot = hotSpotSystem.getHotSpotByTile(hitTileX, hitTileY);
if (hotSpot >= HotSpotSystem.HotSpotType.NPC_GO_RIGHT && hotSpot <= HotSpotSystem.HotSpotType.NPC_SLOW) {
// movement-related commands are immediate
parentObject.setCurrentAction(ActionType.MOVE);
accepted = executeCommand(hotSpot, parentObject, timeDelta);
} else if (hotSpot == HotSpotSystem.HotSpotType.ATTACK && !mPauseOnAttack) {
// when mPauseOnAttack is false, attacks are also immediate.
accepted = executeCommand(hotSpot, parentObject, timeDelta);
} else if (hotSpot == HotSpotSystem.HotSpotType.NPC_RUN_QUEUED_COMMANDS) {
if (!mExecutingQueue && mQueueTop != mQueueBottom) {
mExecutingQueue = true;
}
} else if (hotSpot > HotSpotSystem.HotSpotType.NONE) {
queueCommand(hotSpot);
}
}
if (mExecutingQueue) {
if (mQueueTop != mQueueBottom) {
accepted = executeCommand(nextCommand(), parentObject, timeDelta);
if (accepted) {
advanceQueue();
}
} else {
mExecutingQueue = false;
}
}
if (accepted) {
mLastHitTileX = hitTileX;
mLastHitTileY = hitTileY;
}
}
} else {
mPauseTime -= timeDelta;
if (mPauseTime < 0.0f) {
resumeMovement(parentObject);
mPauseTime = 0.0f;
parentObject.setCurrentAction(ActionType.MOVE);
}
}
mPreviousPosition.set(parentObject.getPosition());
}
private boolean executeCommand(int hotSpot, GameObject parentObject, float timeDelta) {
boolean hitAccepted = true;
final CameraSystem camera = sSystemRegistry.cameraSystem;
switch(hotSpot) {
case HotSpotSystem.HotSpotType.WAIT_SHORT:
if (mPauseTime == 0.0f) {
mPauseTime = PAUSE_TIME_SHORT;
pauseMovement(parentObject);
}
break;
case HotSpotSystem.HotSpotType.WAIT_MEDIUM:
if (mPauseTime == 0.0f) {
mPauseTime = PAUSE_TIME_MEDIUM;
pauseMovement(parentObject);
}
break;
case HotSpotSystem.HotSpotType.WAIT_LONG:
if (mPauseTime == 0.0f) {
mPauseTime = PAUSE_TIME_LONG;
pauseMovement(parentObject);
}
break;
case HotSpotSystem.HotSpotType.ATTACK:
if (mPauseOnAttack) {
if (mPauseTime == 0.0f) {
mPauseTime = PAUSE_TIME_ATTACK;
pauseMovement(parentObject);
}
}
parentObject.setCurrentAction(ActionType.ATTACK);
break;
case HotSpotSystem.HotSpotType.TALK:
if (mHitReactComponent != null) {
if (parentObject.lastReceivedHitType != HitType.COLLECT) {
mHitReactComponent.setSpawnGameEventOnHit(
HitType.COLLECT, mDialogEvent, mDialogIndex);
if (parentObject.getVelocity().x != 0.0f) {
pauseMovement(parentObject);
}
hitAccepted = false;
} else {
parentObject.setCurrentAction(ActionType.MOVE);
resumeMovement(parentObject);
mHitReactComponent.setSpawnGameEventOnHit(HitType.INVALID, 0, 0);
parentObject.lastReceivedHitType = HitType.INVALID;
}
}
break;
case HotSpotSystem.HotSpotType.WALK_AND_TALK:
if (mDialogEvent != GameFlowEvent.EVENT_INVALID) {
LevelSystem level = sSystemRegistry.levelSystem;
level.sendGameEvent(mDialogEvent, mDialogIndex, true);
mDialogEvent = GameFlowEvent.EVENT_INVALID;
}
break;
case HotSpotSystem.HotSpotType.TAKE_CAMERA_FOCUS:
if (camera != null) {
camera.setTarget(parentObject);
}
break;
case HotSpotSystem.HotSpotType.RELEASE_CAMERA_FOCUS:
if (camera != null) {
GameObjectManager gameObjectManager = sSystemRegistry.gameObjectManager;
camera.setTarget(gameObjectManager.getPlayer());
}
break;
case HotSpotSystem.HotSpotType.END_LEVEL:
HudSystem hud = sSystemRegistry.hudSystem;
if (hud != null) {
hud.startFade(false, 1.5f);
hud.sendGameEventOnFadeComplete(GameFlowEvent.EVENT_GO_TO_NEXT_LEVEL, 0);
}
break;
case HotSpotSystem.HotSpotType.GAME_EVENT:
if (mGameEvent != -1) {
LevelSystem level = sSystemRegistry.levelSystem;
if (level != null) {
level.sendGameEvent(mGameEvent, mGameEventIndex, true);
mGameEvent = -1;
}
}
break;
case HotSpotSystem.HotSpotType.NPC_GO_UP_FROM_GROUND:
if (!parentObject.touchingGround()) {
hitAccepted = false;
break;
}
// fall through
case HotSpotSystem.HotSpotType.NPC_GO_UP:
parentObject.getVelocity().y = mUpImpulse;
parentObject.getTargetVelocity().y = 0.0f;
mTargetXVelocity = 0.0f;
break;
case HotSpotSystem.HotSpotType.NPC_GO_DOWN_FROM_CEILING:
if (!parentObject.touchingCeiling()) {
hitAccepted = false;
break;
}
// fall through
case HotSpotSystem.HotSpotType.NPC_GO_DOWN:
parentObject.getVelocity().y = mDownImpulse;
parentObject.getTargetVelocity().y = 0.0f;
if (mFlying) {
mTargetXVelocity = 0.0f;
}
break;
case HotSpotSystem.HotSpotType.NPC_GO_LEFT:
parentObject.getTargetVelocity().x = -mHorizontalImpulse;
parentObject.getAcceleration().x = mAcceleration;
if (mFlying) {
parentObject.getVelocity().y = 0.0f;
parentObject.getTargetVelocity().y = 0.0f;
}
break;
case HotSpotSystem.HotSpotType.NPC_GO_RIGHT:
parentObject.getTargetVelocity().x = mHorizontalImpulse;
parentObject.getAcceleration().x = mAcceleration;
if (mFlying) {
parentObject.getVelocity().y = 0.0f;
parentObject.getTargetVelocity().y = 0.0f;
}
break;
case HotSpotSystem.HotSpotType.NPC_GO_UP_RIGHT:
parentObject.getVelocity().y = mUpImpulse;
parentObject.getTargetVelocity().x = mHorizontalImpulse;
parentObject.getAcceleration().x = mAcceleration;
break;
case HotSpotSystem.HotSpotType.NPC_GO_UP_LEFT:
parentObject.getVelocity().y = mUpImpulse;
parentObject.getTargetVelocity().x = -mHorizontalImpulse;
parentObject.getAcceleration().x = mAcceleration;
break;
case HotSpotSystem.HotSpotType.NPC_GO_DOWN_RIGHT:
parentObject.getVelocity().y = mDownImpulse;
parentObject.getTargetVelocity().x = mHorizontalImpulse;
parentObject.getAcceleration().x = mAcceleration;
break;
case HotSpotSystem.HotSpotType.NPC_GO_DOWN_LEFT:
parentObject.getVelocity().y = mDownImpulse;
parentObject.getTargetVelocity().x = -mHorizontalImpulse;
parentObject.getAcceleration().x = mAcceleration;
break;
case HotSpotSystem.HotSpotType.NPC_GO_TOWARDS_PLAYER:
int direction = 1;
GameObjectManager manager = sSystemRegistry.gameObjectManager;
if (manager != null) {
GameObject player = manager.getPlayer();
if (player != null) {
direction = Utils.sign(
player.getCenteredPositionX() -
parentObject.getCenteredPositionX());
}
}
parentObject.getTargetVelocity().x = mHorizontalImpulse * direction;
if (mFlying) {
parentObject.getVelocity().y = 0.0f;
parentObject.getTargetVelocity().y = 0.0f;
}
break;
case HotSpotSystem.HotSpotType.NPC_GO_RANDOM:
parentObject.getTargetVelocity().x = mHorizontalImpulse * (Math.random() > 0.5f ? -1.0f : 1.0f);
if (mFlying) {
parentObject.getVelocity().y = 0.0f;
parentObject.getTargetVelocity().y = 0.0f;
}
break;
case HotSpotSystem.HotSpotType.NPC_STOP:
parentObject.getTargetVelocity().x = 0.0f;
parentObject.getVelocity().x = 0.0f;
break;
case HotSpotSystem.HotSpotType.NPC_SLOW:
parentObject.getTargetVelocity().x = mSlowHorizontalImpulse * Utils.sign(parentObject.getTargetVelocity().x);
break;
case HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_1_1:
case HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_1_2:
case HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_1_3:
case HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_1_4:
case HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_1_5:
case HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_2_1:
case HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_2_2:
case HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_2_3:
case HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_2_4:
case HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_2_5:
selectDialog(hotSpot);
break;
case HotSpotSystem.HotSpotType.NONE:
if (parentObject.touchingGround() && parentObject.getVelocity().y <= 0.0f) {
//resumeMovement(parentObject);
}
break;
}
return hitAccepted;
}
private void pauseMovement(GameObject parentObject) {
mTargetXVelocity = parentObject.getTargetVelocity().x;
parentObject.getTargetVelocity().x = 0.0f;
parentObject.getVelocity().x = 0.0f;
}
private void resumeMovement(GameObject parentObject) {
parentObject.getTargetVelocity().x = mTargetXVelocity;
parentObject.getAcceleration().x = mAcceleration;
}
private void selectDialog(int hitSpot) {
mDialogEvent = GameFlowEvent.EVENT_SHOW_DIALOG_CHARACTER1;
mDialogIndex = hitSpot - HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_1_1;
if (hitSpot >= HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_2_1) {
mDialogEvent = GameFlowEvent.EVENT_SHOW_DIALOG_CHARACTER2;
mDialogIndex = hitSpot - HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_2_1;
}
}
private int nextCommand() {
int result = HotSpotSystem.HotSpotType.NONE;
if (mQueueTop != mQueueBottom) {
result = mQueuedCommands[mQueueTop];
}
return result;
}
private int advanceQueue() {
int result = HotSpotSystem.HotSpotType.NONE;
if (mQueueTop != mQueueBottom) {
result = mQueuedCommands[mQueueTop];
mQueueTop = (mQueueTop + 1) % COMMAND_QUEUE_SIZE;
}
return result;
}
private void queueCommand(int hotspot) {
int nextSlot = (mQueueBottom + 1) % COMMAND_QUEUE_SIZE;
if (nextSlot != mQueueTop) { // only comply if there is space left in the buffer
mQueuedCommands[mQueueBottom] = hotspot;
mQueueBottom = nextSlot;
}
}
public void setHitReactionComponent(HitReactionComponent hitReact) {
mHitReactComponent = hitReact;
}
public void setSpeeds(float horizontalImpulse, float slowHorizontalImpulse, float upImpulse, float downImpulse, float acceleration) {
mHorizontalImpulse = horizontalImpulse;
mSlowHorizontalImpulse = slowHorizontalImpulse;
mUpImpulse = upImpulse;
mDownImpulse = downImpulse;
mAcceleration = acceleration;
}
public void setGameEvent(int event, int index, boolean spawnOnDeath) {
mGameEvent = event;
mGameEventIndex = index;
mSpawnGameEventOnDeath = spawnOnDeath;
}
public void setReactToHits(boolean react) {
mReactToHits = react;
}
public void setFlying(boolean flying) {
mFlying = flying;
}
public void setPauseOnAttack(boolean pauseOnAttack) {
mPauseOnAttack = pauseOnAttack;
}
}