PatrolComponent.java revision 3c1e67e433728684b5f228c5d4f3e5b1457bb271
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.GameObject.ActionType;
20import 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 */
28public 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