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
17package com.replica.replicaisland;
18
19import com.replica.replicaisland.ChannelSystem.Channel;
20import com.replica.replicaisland.GameObject.ActionType;
21
22public class NPCAnimationComponent extends GameComponent {
23
24    // Animations
25    public static final int IDLE = 0;
26    public static final int WALK = 1;
27    public static final int RUN_START = 2;
28    public static final int RUN = 3;
29    public static final int SHOOT = 4;
30    public static final int JUMP_START = 5;
31    public static final int JUMP_AIR = 6;
32    public static final int TAKE_HIT = 7;
33    public static final int SURPRISED = 8;
34    public static final int DEATH = 9;
35
36
37    protected static final float RUN_SPEED_THRESHOLD = 100.0f;
38    protected static final float JUMP_SPEED_THRESHOLD = 25.0f;
39    protected static final float FALL_SPEED_THRESHOLD = -25.0f;
40    protected static final float FALL_TIME_THRESHOLD = 0.2f;
41
42    private int mCurrentAnimation;
43    private SpriteComponent mSprite;
44    private ChannelSystem.Channel mChannel;
45    private int mChannelTrigger;
46    private boolean mFlying;
47    private boolean mStopAtWalls;	// Controls whether or not the character will go back
48    								// to idle when running into a wall
49
50
51    public NPCAnimationComponent() {
52        super();
53        reset();
54        setPhase(GameComponent.ComponentPhases.ANIMATION.ordinal());
55    }
56
57    @Override
58    public void reset() {
59        mCurrentAnimation = IDLE;
60        mChannel = null;
61        mSprite = null;
62        mFlying = false;
63        mStopAtWalls = true;
64    }
65
66    @Override
67    public void update(float timeDelta, BaseObject parent) {
68        if (mSprite != null) {
69            GameObject parentObject = (GameObject)parent;
70
71            final int oldAnimation = mCurrentAnimation;
72            switch(mCurrentAnimation) {
73                case IDLE:
74                    idle(parentObject);
75                    break;
76                case WALK:
77                    walk(parentObject);
78                    break;
79                case RUN_START:
80                    runStart(parentObject);
81                    break;
82                case RUN:
83                    run(parentObject);
84                    break;
85                case SHOOT:
86                    shoot(parentObject);
87                    break;
88                case JUMP_START:
89                    jumpStart(parentObject);
90                    break;
91                case JUMP_AIR:
92                    jumpAir(parentObject);
93                    break;
94                case TAKE_HIT:
95                	takeHit(parentObject);
96                	break;
97                case SURPRISED:
98                	surprised(parentObject);
99                	break;
100                case DEATH:
101                	death(parentObject);
102                	break;
103               default:
104                    assert(false);
105            }
106
107            if (mChannel != null) {
108            	if (mChannel.value != null
109            			&& ((ChannelSystem.ChannelBooleanValue)mChannel.value).value) {
110            		mCurrentAnimation = mChannelTrigger;
111            	}
112            }
113
114            if (oldAnimation != mCurrentAnimation) {
115                mSprite.playAnimation(mCurrentAnimation);
116            }
117        }
118    }
119
120    protected boolean shouldFall(GameObject parentObject) {
121        boolean result = false;
122        TimeSystem time = sSystemRegistry.timeSystem;
123        final float airTime = time.getGameTime() - parentObject.getLastTouchedFloorTime();
124        if (!mFlying && !parentObject.touchingGround() && airTime > FALL_TIME_THRESHOLD) {
125            final Vector2 velocity = parentObject.getVelocity();
126            if (velocity.y < FALL_SPEED_THRESHOLD) {
127                result = true;
128            }
129        }
130        return result;
131    }
132
133    protected boolean shouldJump(GameObject parentObject) {
134        boolean result = false;
135
136        if (!mFlying) {
137	        final Vector2 velocity = parentObject.getVelocity();
138	        if (velocity.y > JUMP_SPEED_THRESHOLD) {
139	            result = true;
140	        }
141        }
142        return result;
143    }
144
145    protected boolean shouldRun(GameObject parentObject) {
146        boolean result = false;
147        if (!mFlying && parentObject.touchingGround()) {
148            final Vector2 velocity = parentObject.getVelocity();
149            if (Math.abs(velocity.x) >= RUN_SPEED_THRESHOLD) {
150                result = true;
151            }
152        }
153        return result;
154    }
155
156    protected boolean shouldMove(GameObject parentObject) {
157        boolean result = true;
158        final Vector2 velocity = parentObject.getVelocity();
159
160        if (mStopAtWalls) {
161	        if ((velocity.x < 0.0f && parentObject.touchingLeftWall())
162	                || (velocity.x > 0.0f && parentObject.touchingRightWall())) {
163	            result = false;
164	        }
165        }
166        return result;
167    }
168
169    protected boolean shouldTakeHit(GameObject parentObject) {
170    	boolean result = false;
171    	if (parentObject.getCurrentAction() == ActionType.HIT_REACT
172    			&& mSprite.findAnimation(TAKE_HIT) != null) {
173    		result = true;
174    	}
175    	return result;
176    }
177
178    protected void gotoRunStart() {
179        if (mSprite.findAnimation(RUN_START) != null) {
180            mCurrentAnimation = RUN_START;
181        } else {
182            mCurrentAnimation = RUN;
183        }
184    }
185
186    protected void gotoRun() {
187        mCurrentAnimation = RUN;
188    }
189
190    protected void idle(GameObject parentObject) {
191        final GameObject.ActionType currentAction = parentObject.getCurrentAction();
192        if (currentAction == ActionType.MOVE) {
193            final Vector2 velocity = parentObject.getVelocity();
194            if (shouldFall(parentObject)) {
195                mCurrentAnimation = JUMP_AIR;
196            } else if (shouldJump(parentObject)) {
197                mCurrentAnimation = JUMP_START;
198                parentObject.positionLocked = true;
199            } else if (Math.abs(velocity.x) > 0.0f && shouldMove(parentObject)) {
200                if (shouldRun(parentObject)) {
201                	gotoRunStart();
202                	parentObject.positionLocked = true;
203                } else {
204                    mCurrentAnimation = WALK;
205                }
206            }
207        } else if (currentAction == ActionType.ATTACK) {
208            mCurrentAnimation = SHOOT;
209        } else if (shouldTakeHit(parentObject)) {
210        	mCurrentAnimation = TAKE_HIT;
211        } else if (parentObject.getCurrentAction() == ActionType.DEATH) {
212        	mCurrentAnimation = DEATH;
213        }
214     }
215
216    protected void walk(GameObject parentObject) {
217        final GameObject.ActionType currentAction = parentObject.getCurrentAction();
218        if (currentAction == ActionType.MOVE) {
219            final Vector2 velocity = parentObject.getVelocity();
220            if (shouldFall(parentObject)) {
221                mCurrentAnimation = JUMP_AIR;
222            } else if (shouldJump(parentObject)) {
223                mCurrentAnimation = JUMP_START;
224                parentObject.positionLocked = true;
225            } else if (Math.abs(velocity.x) > 0.0f) {
226                if (shouldRun(parentObject)) {
227                    gotoRun();
228                }
229                if (velocity.x > 0.0f) {
230                    parentObject.facingDirection.x = 1;
231                } else {
232                    parentObject.facingDirection.x = -1;
233                }
234            } else {
235                mCurrentAnimation = IDLE;
236            }
237        } else if (currentAction == ActionType.ATTACK) {
238            mCurrentAnimation = SHOOT;
239        } else if (shouldTakeHit(parentObject)) {
240        	mCurrentAnimation = TAKE_HIT;
241        } else if (parentObject.getCurrentAction() == ActionType.DEATH) {
242        	mCurrentAnimation = DEATH;
243        }
244    }
245
246    protected void runStart(GameObject parentObject) {
247    	parentObject.positionLocked = true;
248        if (mSprite.animationFinished()) {
249            mCurrentAnimation = RUN;
250            parentObject.positionLocked = false;
251        }
252    }
253    protected void run(GameObject parentObject) {
254        final GameObject.ActionType currentAction = parentObject.getCurrentAction();
255        if (currentAction == ActionType.MOVE) {
256            final Vector2 velocity = parentObject.getVelocity();
257            if (shouldFall(parentObject)) {
258                mCurrentAnimation = JUMP_AIR;
259            } else if (shouldJump(parentObject)) {
260                parentObject.positionLocked = true;
261                mCurrentAnimation = JUMP_START;
262            } else if (Math.abs(velocity.x) > 0.0f) {
263                if (!shouldRun(parentObject)) {
264                    mCurrentAnimation = WALK;
265                }
266
267                if (velocity.x > 0.0f) {
268                    parentObject.facingDirection.x = 1;
269                } else {
270                    parentObject.facingDirection.x = -1;
271                }
272            } else {
273            	mCurrentAnimation = IDLE;
274            }
275        } else if (currentAction == ActionType.ATTACK) {
276            mCurrentAnimation = SHOOT;
277        } else if (shouldTakeHit(parentObject)) {
278        	mCurrentAnimation = TAKE_HIT;
279        } else if (parentObject.getCurrentAction() == ActionType.DEATH) {
280        	mCurrentAnimation = DEATH;
281        }
282    }
283
284    protected void shoot(GameObject parentObject) {
285        if (mSprite.animationFinished() || parentObject.getCurrentAction() != ActionType.ATTACK) {
286            mCurrentAnimation = IDLE;
287        } else if (shouldTakeHit(parentObject)) {
288        	mCurrentAnimation = TAKE_HIT;
289        } else if (parentObject.getCurrentAction() == ActionType.DEATH) {
290            	mCurrentAnimation = DEATH;
291        } else {
292            final Vector2 velocity = parentObject.getVelocity();
293
294        	if (velocity.x > 0.0f) {
295                parentObject.facingDirection.x = 1;
296            } else if (velocity.x < 0.0f) {
297                parentObject.facingDirection.x = -1;
298            }
299        }
300    }
301
302    protected void jumpStart(GameObject parentObject) {
303        final Vector2 velocity = parentObject.getVelocity();
304
305        if (velocity.x > 0.0f) {
306            parentObject.facingDirection.x = 1;
307        } else if (velocity.x < 0.0f) {
308            parentObject.facingDirection.x = -1;
309        }
310        parentObject.positionLocked = true;
311
312        if (mSprite.animationFinished()) {
313            mCurrentAnimation = JUMP_AIR;
314            parentObject.positionLocked = false;
315        }
316    }
317
318    protected void jumpAir(GameObject parentObject) {
319        final GameObject.ActionType currentAction = parentObject.getCurrentAction();
320        if (currentAction == ActionType.MOVE) {
321            final Vector2 velocity = parentObject.getVelocity();
322
323            if (parentObject.touchingGround()) {
324                if (Math.abs(velocity.x) > 0.0f) {
325                    if (shouldRun(parentObject)) {
326                        mCurrentAnimation = RUN;
327                    } else {
328                        mCurrentAnimation = WALK;
329                    }
330                } else {
331                    mCurrentAnimation = IDLE;
332                }
333            } else {
334
335                if (velocity.x > 0.0f) {
336                    parentObject.facingDirection.x = 1;
337                } else if (velocity.x < 0.0f) {
338                    parentObject.facingDirection.x = -1;
339                }
340
341            }
342        } else {
343            mCurrentAnimation = IDLE;
344        }
345    }
346
347    protected void takeHit(GameObject parentObject) {
348    	if (mSprite.animationFinished()) {
349    		if (parentObject.life > 0 && parentObject.getCurrentAction() != ActionType.DEATH) {
350    			if (parentObject.getCurrentAction() != ActionType.HIT_REACT) {
351    				mCurrentAnimation = IDLE;
352    			}
353			} else {
354    			mCurrentAnimation = DEATH;
355    		}
356    	}
357    }
358
359    protected void surprised(GameObject parentObject) {
360    	if (mSprite.animationFinished()) {
361    		mCurrentAnimation = IDLE;
362    	}
363    }
364
365    protected void death(GameObject parentObject) {
366    }
367
368    public void setSprite(SpriteComponent sprite) {
369        mSprite = sprite;
370    }
371
372	public void setChannel(Channel channel) {
373		mChannel = channel;
374	}
375
376	public void setChannelTrigger(int animation) {
377		mChannelTrigger = animation;
378	}
379
380	public void setFlying(boolean flying) {
381		mFlying = flying;
382	}
383
384	public void setStopAtWalls(boolean stop) {
385		mStopAtWalls = stop;
386	}
387}
388