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.CollisionParameters.HitType;
     20 import com.replica.replicaisland.GameObject.ActionType;
     21 
     22 public class NPCComponent extends GameComponent {
     23     private float mPauseTime;
     24     private float mTargetXVelocity;
     25     private int mLastHitTileX;
     26     private int mLastHitTileY;
     27 
     28     private int mDialogEvent;
     29     private int mDialogIndex;
     30 
     31     private HitReactionComponent mHitReactComponent;
     32 
     33     private int[] mQueuedCommands;
     34     private int mQueueTop;
     35     private int mQueueBottom;
     36     private boolean mExecutingQueue;
     37 
     38     private Vector2 mPreviousPosition;
     39 
     40     private float mUpImpulse;
     41     private float mDownImpulse;
     42     private float mHorizontalImpulse;
     43     private float mSlowHorizontalImpulse;
     44     private float mAcceleration;
     45 
     46     private int mGameEvent;
     47     private int mGameEventIndex;
     48     private boolean mSpawnGameEventOnDeath;
     49 
     50     private boolean mReactToHits;
     51     private boolean mFlying;
     52     private boolean mPauseOnAttack;
     53 
     54     private float mDeathTime;
     55 	private float mDeathFadeDelay;
     56 
     57     private static final float UP_IMPULSE = 400.0f;
     58     private static final float DOWN_IMPULSE = -10.0f;
     59     private static final float HORIZONTAL_IMPULSE = 200.0f;
     60     private static final float SLOW_HORIZONTAL_IMPULSE = 50.0f;
     61     private static final float ACCELERATION = 300.0f;
     62     private static final float HIT_IMPULSE = 300.0f;
     63     private static final float HIT_ACCELERATION = 700.0f;
     64 
     65     private static final float DEATH_FADE_DELAY = 4.0f;
     66 
     67     private static final float PAUSE_TIME_SHORT = 1.0f;
     68     private static final float PAUSE_TIME_MEDIUM = 4.0f;
     69     private static final float PAUSE_TIME_LONG = 8.0f;
     70     private static final float PAUSE_TIME_ATTACK = 1.0f;
     71     private static final float PAUSE_TIME_HIT_REACT = 1.0f;
     72 
     73     private static final int COMMAND_QUEUE_SIZE = 16;
     74 
     75     public NPCComponent() {
     76         super();
     77         setPhase(ComponentPhases.THINK.ordinal());
     78         mQueuedCommands = new int[COMMAND_QUEUE_SIZE];
     79         mPreviousPosition = new Vector2();
     80         reset();
     81     }
     82 
     83     @Override
     84     public void reset() {
     85         mPauseTime = 0.0f;
     86         mTargetXVelocity = 0.0f;
     87         mLastHitTileX = 0;
     88         mLastHitTileY = 0;
     89         mDialogEvent = GameFlowEvent.EVENT_SHOW_DIALOG_CHARACTER1;
     90         mDialogIndex = 0;
     91         mHitReactComponent = null;
     92         mQueueTop = 0;
     93         mQueueBottom = 0;
     94         mPreviousPosition.zero();
     95         mExecutingQueue = false;
     96         mUpImpulse = UP_IMPULSE;
     97         mDownImpulse = DOWN_IMPULSE;
     98         mHorizontalImpulse = HORIZONTAL_IMPULSE;
     99         mSlowHorizontalImpulse = SLOW_HORIZONTAL_IMPULSE;
    100         mAcceleration = ACCELERATION;
    101         mGameEvent = -1;
    102         mGameEventIndex = -1;
    103         mSpawnGameEventOnDeath = false;
    104         mReactToHits = false;
    105         mFlying = false;
    106         mDeathTime = 0.0f;
    107         mDeathFadeDelay = DEATH_FADE_DELAY;
    108         mPauseOnAttack = true;
    109     }
    110 
    111     @Override
    112     public void update(float timeDelta, BaseObject parent) {
    113 
    114         GameObject parentObject = (GameObject)parent;
    115 
    116         if (mReactToHits &&
    117         		mPauseTime <= 0.0f &&
    118         		parentObject.getCurrentAction() == ActionType.HIT_REACT) {
    119         	mPauseTime = PAUSE_TIME_HIT_REACT;
    120             pauseMovement(parentObject);
    121         	parentObject.getVelocity().x = -parentObject.facingDirection.x * HIT_IMPULSE;
    122         	parentObject.getAcceleration().x = HIT_ACCELERATION;
    123 
    124         } else if (parentObject.getCurrentAction() == ActionType.DEATH) {
    125         	if (mSpawnGameEventOnDeath && mGameEvent != -1) {
    126         		if (Utils.close(parentObject.getVelocity().x, 0.0f)
    127         				&& parentObject.touchingGround()) {
    128 
    129         			if (mDeathTime < mDeathFadeDelay && mDeathTime + timeDelta >= mDeathFadeDelay) {
    130         				HudSystem hud = sSystemRegistry.hudSystem;
    131 
    132         	        	if (hud != null) {
    133         	        		hud.startFade(false, 1.5f);
    134         	        		hud.sendGameEventOnFadeComplete(mGameEvent, mGameEventIndex);
    135         	        		mGameEvent = -1;
    136         	        	}
    137         			}
    138         			mDeathTime += timeDelta;
    139 
    140         		}
    141         	}
    142         	// nothing else to do.
    143         	return;
    144         } else if (parentObject.life <= 0) {
    145         	parentObject.setCurrentAction(ActionType.DEATH);
    146         	parentObject.getTargetVelocity().x = 0;
    147         	return;
    148         } else if (parentObject.getCurrentAction() == ActionType.INVALID ||
    149         		(!mReactToHits && parentObject.getCurrentAction() == ActionType.HIT_REACT)) {
    150         	parentObject.setCurrentAction(ActionType.MOVE);
    151         }
    152 
    153         if (mPauseTime <= 0.0f) {
    154 
    155             HotSpotSystem hotSpotSystem = sSystemRegistry.hotSpotSystem;
    156 
    157             if (hotSpotSystem != null) {
    158             	final float centerX = parentObject.getCenteredPositionX();
    159                 final int hitTileX = hotSpotSystem.getHitTileX(centerX);
    160                 final int hitTileY = hotSpotSystem.getHitTileY(parentObject.getPosition().y + 10.0f);
    161                 boolean accepted = true;
    162 
    163                 if (hitTileX != mLastHitTileX || hitTileY != mLastHitTileY) {
    164 
    165             	    final int hotSpot = hotSpotSystem.getHotSpotByTile(hitTileX, hitTileY);
    166 
    167                     if (hotSpot >= HotSpotSystem.HotSpotType.NPC_GO_RIGHT && hotSpot <= HotSpotSystem.HotSpotType.NPC_SLOW) {
    168                     	// movement-related commands are immediate
    169                         parentObject.setCurrentAction(ActionType.MOVE);
    170                     	accepted = executeCommand(hotSpot, parentObject, timeDelta);
    171                     } else if (hotSpot == HotSpotSystem.HotSpotType.ATTACK && !mPauseOnAttack) {
    172                     	// when mPauseOnAttack is false, attacks are also immediate.
    173                     	accepted = executeCommand(hotSpot, parentObject, timeDelta);
    174                     } else if (hotSpot == HotSpotSystem.HotSpotType.NPC_RUN_QUEUED_COMMANDS) {
    175                     	if (!mExecutingQueue && mQueueTop != mQueueBottom) {
    176                     		mExecutingQueue = true;
    177                         }
    178                     } else if (hotSpot > HotSpotSystem.HotSpotType.NONE) {
    179                     	queueCommand(hotSpot);
    180                     }
    181                 }
    182 
    183                 if (mExecutingQueue) {
    184                 	if (mQueueTop != mQueueBottom) {
    185                 		accepted = executeCommand(nextCommand(), parentObject, timeDelta);
    186                 		if (accepted) {
    187                 			advanceQueue();
    188                 		}
    189                 	} else {
    190                 		mExecutingQueue = false;
    191                 	}
    192                 }
    193 
    194                 if (accepted) {
    195                 	mLastHitTileX = hitTileX;
    196                 	mLastHitTileY = hitTileY;
    197                 }
    198 
    199             }
    200         } else {
    201             mPauseTime -= timeDelta;
    202             if (mPauseTime < 0.0f) {
    203                 resumeMovement(parentObject);
    204                 mPauseTime = 0.0f;
    205                 parentObject.setCurrentAction(ActionType.MOVE);
    206             }
    207         }
    208 
    209         mPreviousPosition.set(parentObject.getPosition());
    210     }
    211 
    212     private boolean executeCommand(int hotSpot, GameObject parentObject, float timeDelta) {
    213     	boolean hitAccepted = true;
    214     	final CameraSystem camera = sSystemRegistry.cameraSystem;
    215 
    216     	switch(hotSpot) {
    217         case HotSpotSystem.HotSpotType.WAIT_SHORT:
    218             if (mPauseTime == 0.0f) {
    219                 mPauseTime = PAUSE_TIME_SHORT;
    220                 pauseMovement(parentObject);
    221             }
    222             break;
    223         case HotSpotSystem.HotSpotType.WAIT_MEDIUM:
    224             if (mPauseTime == 0.0f) {
    225                 mPauseTime = PAUSE_TIME_MEDIUM;
    226                 pauseMovement(parentObject);
    227             }
    228             break;
    229         case HotSpotSystem.HotSpotType.WAIT_LONG:
    230             if (mPauseTime == 0.0f) {
    231                 mPauseTime = PAUSE_TIME_LONG;
    232                 pauseMovement(parentObject);
    233             }
    234             break;
    235         case HotSpotSystem.HotSpotType.ATTACK:
    236         	if (mPauseOnAttack) {
    237 	            if (mPauseTime == 0.0f) {
    238 	                mPauseTime = PAUSE_TIME_ATTACK;
    239 	                pauseMovement(parentObject);
    240 
    241 	            }
    242         	}
    243             parentObject.setCurrentAction(ActionType.ATTACK);
    244 
    245             break;
    246 
    247         case HotSpotSystem.HotSpotType.TALK:
    248         	if (mHitReactComponent != null) {
    249             	if (parentObject.lastReceivedHitType != HitType.COLLECT) {
    250             		mHitReactComponent.setSpawnGameEventOnHit(
    251             				HitType.COLLECT, mDialogEvent, mDialogIndex);
    252             		if (parentObject.getVelocity().x != 0.0f) {
    253             			pauseMovement(parentObject);
    254             		}
    255             		hitAccepted = false;
    256             	} else {
    257                     parentObject.setCurrentAction(ActionType.MOVE);
    258 
    259             		resumeMovement(parentObject);
    260             		mHitReactComponent.setSpawnGameEventOnHit(HitType.INVALID, 0, 0);
    261             		parentObject.lastReceivedHitType = HitType.INVALID;
    262             	}
    263         	}
    264         	break;
    265 
    266         case HotSpotSystem.HotSpotType.WALK_AND_TALK:
    267         	if (mDialogEvent != GameFlowEvent.EVENT_INVALID) {
    268         		LevelSystem level = sSystemRegistry.levelSystem;
    269         		level.sendGameEvent(mDialogEvent, mDialogIndex, true);
    270         		mDialogEvent = GameFlowEvent.EVENT_INVALID;
    271         	}
    272         	break;
    273 
    274         case HotSpotSystem.HotSpotType.TAKE_CAMERA_FOCUS:
    275         	if (camera != null) {
    276         		camera.setTarget(parentObject);
    277         	}
    278         	break;
    279 
    280         case HotSpotSystem.HotSpotType.RELEASE_CAMERA_FOCUS:
    281 
    282         	if (camera != null) {
    283         		GameObjectManager gameObjectManager = sSystemRegistry.gameObjectManager;
    284         		camera.setTarget(gameObjectManager.getPlayer());
    285         	}
    286         	break;
    287 
    288         case HotSpotSystem.HotSpotType.END_LEVEL:
    289         	HudSystem hud = sSystemRegistry.hudSystem;
    290 
    291         	if (hud != null) {
    292         		hud.startFade(false, 1.5f);
    293         		hud.sendGameEventOnFadeComplete(GameFlowEvent.EVENT_GO_TO_NEXT_LEVEL, 0);
    294         	}
    295         	break;
    296         case HotSpotSystem.HotSpotType.GAME_EVENT:
    297         	if (mGameEvent != -1) {
    298     			LevelSystem level = sSystemRegistry.levelSystem;
    299     			if (level != null) {
    300     				level.sendGameEvent(mGameEvent, mGameEventIndex, true);
    301     				mGameEvent = -1;
    302     			}
    303     		}
    304         	break;
    305 
    306         case HotSpotSystem.HotSpotType.NPC_GO_UP_FROM_GROUND:
    307             if (!parentObject.touchingGround()) {
    308                 hitAccepted = false;
    309                 break;
    310             }
    311             // fall through
    312         case HotSpotSystem.HotSpotType.NPC_GO_UP:
    313         	parentObject.getVelocity().y = mUpImpulse;
    314         	parentObject.getTargetVelocity().y = 0.0f;
    315             mTargetXVelocity = 0.0f;
    316 
    317             break;
    318         case HotSpotSystem.HotSpotType.NPC_GO_DOWN_FROM_CEILING:
    319             if (!parentObject.touchingCeiling()) {
    320                 hitAccepted = false;
    321                 break;
    322             }
    323             // fall through
    324         case HotSpotSystem.HotSpotType.NPC_GO_DOWN:
    325         	parentObject.getVelocity().y = mDownImpulse;
    326         	parentObject.getTargetVelocity().y = 0.0f;
    327         	if (mFlying) {
    328         		mTargetXVelocity = 0.0f;
    329         	}
    330             break;
    331         case HotSpotSystem.HotSpotType.NPC_GO_LEFT:
    332         	parentObject.getTargetVelocity().x = -mHorizontalImpulse;
    333         	parentObject.getAcceleration().x = mAcceleration;
    334         	if (mFlying) {
    335         		parentObject.getVelocity().y = 0.0f;
    336         		parentObject.getTargetVelocity().y = 0.0f;
    337         	}
    338             break;
    339         case HotSpotSystem.HotSpotType.NPC_GO_RIGHT:
    340         	parentObject.getTargetVelocity().x = mHorizontalImpulse;
    341         	parentObject.getAcceleration().x = mAcceleration;
    342         	if (mFlying) {
    343         		parentObject.getVelocity().y = 0.0f;
    344         		parentObject.getTargetVelocity().y = 0.0f;
    345         	}
    346 
    347             break;
    348         case HotSpotSystem.HotSpotType.NPC_GO_UP_RIGHT:
    349         	parentObject.getVelocity().y = mUpImpulse;
    350         	parentObject.getTargetVelocity().x = mHorizontalImpulse;
    351         	parentObject.getAcceleration().x = mAcceleration;
    352 
    353 
    354             break;
    355         case HotSpotSystem.HotSpotType.NPC_GO_UP_LEFT:
    356         	parentObject.getVelocity().y = mUpImpulse;
    357         	parentObject.getTargetVelocity().x = -mHorizontalImpulse;
    358         	parentObject.getAcceleration().x = mAcceleration;
    359 
    360 
    361             break;
    362         case HotSpotSystem.HotSpotType.NPC_GO_DOWN_RIGHT:
    363         	parentObject.getVelocity().y = mDownImpulse;
    364         	parentObject.getTargetVelocity().x = mHorizontalImpulse;
    365         	parentObject.getAcceleration().x = mAcceleration;
    366 
    367 
    368             break;
    369         case HotSpotSystem.HotSpotType.NPC_GO_DOWN_LEFT:
    370         	parentObject.getVelocity().y = mDownImpulse;
    371         	parentObject.getTargetVelocity().x = -mHorizontalImpulse;
    372         	parentObject.getAcceleration().x = mAcceleration;
    373 
    374 
    375             break;
    376         case HotSpotSystem.HotSpotType.NPC_GO_TOWARDS_PLAYER:
    377             int direction = 1;
    378             GameObjectManager manager = sSystemRegistry.gameObjectManager;
    379             if (manager != null) {
    380                 GameObject player = manager.getPlayer();
    381                 if (player != null) {
    382                     direction = Utils.sign(
    383                             player.getCenteredPositionX() -
    384                             parentObject.getCenteredPositionX());
    385                 }
    386             }
    387             parentObject.getTargetVelocity().x = mHorizontalImpulse * direction;
    388             if (mFlying) {
    389             	parentObject.getVelocity().y = 0.0f;
    390         		parentObject.getTargetVelocity().y = 0.0f;
    391         	}
    392             break;
    393         case HotSpotSystem.HotSpotType.NPC_GO_RANDOM:
    394         	parentObject.getTargetVelocity().x = mHorizontalImpulse * (Math.random() > 0.5f ? -1.0f : 1.0f);
    395         	if (mFlying) {
    396         		parentObject.getVelocity().y = 0.0f;
    397         		parentObject.getTargetVelocity().y = 0.0f;
    398         	}
    399             break;
    400 
    401         case HotSpotSystem.HotSpotType.NPC_STOP:
    402         	parentObject.getTargetVelocity().x = 0.0f;
    403         	parentObject.getVelocity().x = 0.0f;
    404             break;
    405 
    406         case HotSpotSystem.HotSpotType.NPC_SLOW:
    407         	parentObject.getTargetVelocity().x = mSlowHorizontalImpulse * Utils.sign(parentObject.getTargetVelocity().x);
    408             break;
    409 
    410         case HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_1_1:
    411         case HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_1_2:
    412         case HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_1_3:
    413         case HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_1_4:
    414         case HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_1_5:
    415         case HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_2_1:
    416         case HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_2_2:
    417         case HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_2_3:
    418         case HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_2_4:
    419         case HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_2_5:
    420         	selectDialog(hotSpot);
    421         	break;
    422         case HotSpotSystem.HotSpotType.NONE:
    423             if (parentObject.touchingGround() && parentObject.getVelocity().y <= 0.0f) {
    424                 //resumeMovement(parentObject);
    425             }
    426             break;
    427     	}
    428 
    429     	return hitAccepted;
    430     }
    431 
    432     private void pauseMovement(GameObject parentObject) {
    433     	mTargetXVelocity = parentObject.getTargetVelocity().x;
    434     	parentObject.getTargetVelocity().x = 0.0f;
    435     	parentObject.getVelocity().x = 0.0f;
    436     }
    437 
    438     private void resumeMovement(GameObject parentObject) {
    439     	parentObject.getTargetVelocity().x = mTargetXVelocity;
    440     	parentObject.getAcceleration().x = mAcceleration;
    441     }
    442 
    443     private void selectDialog(int hitSpot) {
    444 		mDialogEvent = GameFlowEvent.EVENT_SHOW_DIALOG_CHARACTER1;
    445     	mDialogIndex = hitSpot - HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_1_1;
    446 
    447     	if (hitSpot >= HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_2_1) {
    448     		mDialogEvent = GameFlowEvent.EVENT_SHOW_DIALOG_CHARACTER2;
    449     		mDialogIndex = hitSpot - HotSpotSystem.HotSpotType.NPC_SELECT_DIALOG_2_1;
    450     	}
    451     }
    452 
    453     private int nextCommand() {
    454     	int result = HotSpotSystem.HotSpotType.NONE;
    455     	if (mQueueTop != mQueueBottom) {
    456     		result = mQueuedCommands[mQueueTop];
    457     	}
    458     	return result;
    459     }
    460 
    461     private int advanceQueue() {
    462     	int result = HotSpotSystem.HotSpotType.NONE;
    463     	if (mQueueTop != mQueueBottom) {
    464     		result = mQueuedCommands[mQueueTop];
    465     		mQueueTop = (mQueueTop + 1) % COMMAND_QUEUE_SIZE;
    466     	}
    467     	return result;
    468     }
    469 
    470     private void queueCommand(int hotspot) {
    471     	int nextSlot = (mQueueBottom + 1) % COMMAND_QUEUE_SIZE;
    472     	if (nextSlot != mQueueTop) { // only comply if there is space left in the buffer
    473     		mQueuedCommands[mQueueBottom] = hotspot;
    474     		mQueueBottom = nextSlot;
    475     	}
    476     }
    477 
    478     public void setHitReactionComponent(HitReactionComponent hitReact) {
    479     	mHitReactComponent = hitReact;
    480     }
    481 
    482     public void setSpeeds(float horizontalImpulse, float slowHorizontalImpulse, float upImpulse, float downImpulse, float acceleration) {
    483     	mHorizontalImpulse = horizontalImpulse;
    484     	mSlowHorizontalImpulse = slowHorizontalImpulse;
    485     	mUpImpulse = upImpulse;
    486     	mDownImpulse = downImpulse;
    487     	mAcceleration = acceleration;
    488     }
    489 
    490     public void setGameEvent(int event, int index, boolean spawnOnDeath) {
    491     	mGameEvent = event;
    492     	mGameEventIndex = index;
    493     	mSpawnGameEventOnDeath = spawnOnDeath;
    494     }
    495 
    496     public void setReactToHits(boolean react) {
    497     	mReactToHits = react;
    498     }
    499 
    500     public void setFlying(boolean flying) {
    501     	mFlying = flying;
    502     }
    503 
    504     public void setPauseOnAttack(boolean pauseOnAttack) {
    505     	mPauseOnAttack = pauseOnAttack;
    506     }
    507 }
    508