-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathEnemy.pde
284 lines (238 loc) · 8.43 KB
/
Enemy.pde
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
public class Enemy {
public static final int SIZE = 10;
final int TARGET_RADIUS = 10;
final int SLOW_RADIUS = 40;
final float TARGET_ROT_RADIUS = PI/30;
final float SLOW_ROT_RADIUS = PI/15;
final int MAX_SPEED = 3;
final int WHISKER_LENGTH = 20;
final int MAX_ACCELERATION = 3;
final float MAX_ANGULAR_SPEED = PI/20;
final float MAX_ANGULAR_ACCELERATION = PI/40;
final int AVOID_RADIUS = 50;
final float AVOID_FORCE = 200;
final int CLUSTER_RADIUS = 20;
final float CLUSTER_FORCE = 100;
final int PIT_AVOID_RADIUS = 30;
final float PIT_AVOID_FORCE = 1000;
final float WALL_AVOID_FORCE = MAX_ACCELERATION;
final float MAX_HEALTH = 80;
final float JUMP_POW = .3;
public PVector position;
PVector velocity;
float rotation = 0;
float rotationalVelocity = 0;
float lastFire = 0;
final int DAMAGE = 5; // how much damage this enemy deals
final int VALUE = 100; // how much killing this enemy is worth
ArrayList<PVector> path;
Health health;
Task btree;
Blackboard bb;
public Enemy (PVector position, Task t, Blackboard bb) {
this.position = position;
this.velocity = new PVector(0, 0);
this.path = new ArrayList<PVector>();
this.health = new Health(MAX_HEALTH);
this.btree = t;
this.bb = bb;
}
public Bullet shoot() {
lastFire = millis();
return new Bullet(position.copy(), PVector.fromAngle(rotation), false);
}
public void loseHealth(float damage) {
this.health.loseHealth(damage);
}
boolean isInvulnerable() {
return this.health.isInvulnerable();
}
public boolean isDead() {
return this.health.isDead();
}
public int getDamage() {
return DAMAGE;
}
public int getPoints() {
return VALUE;
}
public void setPath(ArrayList<PVector> path) {
this.path = path;
}
public void draw() {
if (isDead()) {
fill(#000000);
} else {
fill(#ff7b00);
}
stroke(#000000);
float sizeToDraw = max(pow(SIZE, 1.0 + position.z / 4), SIZE / 2.0);
circle(position.x, position.y, sizeToDraw);
line(position.x, position.y, position.x + cos(rotation) * sizeToDraw / 2, position.y + sin(rotation) * sizeToDraw / 2);
}
// Determines next move and returns new position
public void update() {
btree.execute();
}
// Gets projected whisker points at 45 degree angles from current velocity
public ArrayList<PVector> getWhiskerPoints() {
ArrayList<PVector> points = new ArrayList<PVector>();
PVector dir = velocity.copy();
dir.normalize();
dir.mult(WHISKER_LENGTH);
dir.rotate(PI / 4);
points.add(PVector.add(dir, position));
dir.rotate(-PI / 2);
points.add(PVector.add(dir, position));
return points;
}
// Attempt to jump
public void jump() {
if (position.z == 0 && velocity.z <= 0) {
velocity.z = JUMP_POW;
}
}
PVector positionInTime() {
float t = JUMP_POW / GameAIBulletHell.GRAVITYSTRENGTH;
return PVector.add(position, PVector.mult(velocity, t));
}
// converts radian amount to be between -PI and PI
float clampRadians(float inputRadians) {
while (inputRadians < -PI) {
inputRadians += 2 * PI;
}
while (inputRadians > PI) {
inputRadians -= 2 * PI;
}
return inputRadians;
}
// Update position and velocity based on distance from target
PVector move(PVector target, ArrayList<Obstacle> obstacles,
ArrayList<Pit> pits, ArrayList<Enemy> enemies, ArrayList<Integer> whiskerResults) {
PVector dir = PVector.sub(target, position);
float dist = dir.mag();
// In range do not move
if (dist < TARGET_RADIUS) {
return position;
}
float targetSpeed = dist > SLOW_RADIUS ? MAX_SPEED : MAX_SPEED * dist / SLOW_RADIUS;
dir.normalize();
PVector targetVelocity = dir;
targetVelocity = targetVelocity.mult(targetSpeed);
PVector acceleration = targetVelocity.sub(velocity);
// avoid obstacles
for (Obstacle o : obstacles) {
avoidThingAtPosition(o.position, acceleration, AVOID_FORCE, AVOID_RADIUS);
}
// avoid pits
for (Pit p : pits) {
PVector pdir = PVector.sub(this.position, p.position);
float distance = pdir.mag();
if (distance < PIT_AVOID_RADIUS) {
PVector landingSpot = positionInTime();
if (crosses(position, landingSpot, p.position, p.size / 2)) {
PVector landingdir = PVector.sub(landingSpot, p.position);
float landingDistance = landingdir.mag();
if (landingDistance > p.size / 2) {
jump();
return PVector.add(position, velocity);
}
}
float repulsionStrength = min(PIT_AVOID_FORCE / (distance * distance), MAX_ACCELERATION);
pdir.normalize();
acceleration.add(pdir.mult(repulsionStrength));
}
}
// avoid other enemies
for (Enemy e : enemies) {
if (e.equals(this)) {
continue;
}
avoidThingAtPosition(e.position, acceleration, CLUSTER_FORCE, CLUSTER_RADIUS);
}
if (acceleration.mag() > MAX_ACCELERATION) {
acceleration.normalize();
acceleration.mult(MAX_ACCELERATION);
}
for (int wp : whiskerResults) {
switch (wp) {
case Room.LEFT:
acceleration.add(new PVector(-WALL_AVOID_FORCE, 0));
break;
case Room.RIGHT:
acceleration.add(new PVector(WALL_AVOID_FORCE, 0));
break;
case Room.BOT:
acceleration.add(new PVector(0, WALL_AVOID_FORCE));
break;
case Room.TOP:
acceleration.add(new PVector(0, -WALL_AVOID_FORCE));
break;
case Room.TOPLEFT:
acceleration.add(new PVector(-WALL_AVOID_FORCE, -WALL_AVOID_FORCE));
break;
case Room.TOPRIGHT:
acceleration.add(new PVector(WALL_AVOID_FORCE, -WALL_AVOID_FORCE));
break;
case Room.BOTLEFT:
acceleration.add(new PVector(-WALL_AVOID_FORCE, WALL_AVOID_FORCE));
break;
case Room.BOTRIGHT:
acceleration.add(new PVector(WALL_AVOID_FORCE, WALL_AVOID_FORCE));
break;
}
}
if (acceleration.mag() > MAX_ACCELERATION) {
acceleration.normalize();
acceleration.mult(MAX_ACCELERATION);
}
velocity = velocity.add(acceleration);
return PVector.add(position, velocity);
}
// Checks if the line from start to end enters a circle with given center and size
boolean crosses(PVector start, PVector end, PVector center, float size) {
PVector diff = PVector.sub(end, start);
// Compute parametrized closest point t
float t = (- diff.x * (start.x - center.x) - diff.y * (start.y - center.y) - diff.z * (start.z - center.z)) / (diff.x * diff.x + diff.y * diff.y + diff.z * diff.z);
// Use edge of line if t outside of start to end range
if (t > 1 || t < 0) {
float edge1Dist = PVector.add(start, PVector.mult(diff, 1)).dist(center);
float edge2Dist = PVector.add(start, PVector.mult(diff, 0)).dist(center);
return edge1Dist < size || edge2Dist < size;
} else {
return PVector.add(start, PVector.mult(diff, t)).dist(center) < size;
}
}
void avoidThingAtPosition(PVector pos, PVector acceleration, float force, int radius) {
PVector dir = PVector.sub(this.position, pos);
float distance = dir.mag();
if (distance < radius) {
float repulsionStrength = min(force / (distance * distance), MAX_ACCELERATION);
dir.normalize();
acceleration.add(dir.mult(repulsionStrength));
}
}
// Update rotation and rotational velocity to face velocity
void steer(PVector target) {
PVector dir = PVector.sub(target, position);
float dist = dir.mag();
float targetRotation = dist < TARGET_RADIUS ? dir.heading() : velocity.heading();
float rotationDiff = clampRadians(targetRotation - rotation);
float rotationMag = abs(rotationDiff);
// In range do not rotate
if (rotationMag < TARGET_ROT_RADIUS) {
return;
}
float targetRotationalVelocity = rotationMag > SLOW_ROT_RADIUS ?
MAX_ANGULAR_SPEED :
MAX_ANGULAR_SPEED * rotationMag / SLOW_ROT_RADIUS;
targetRotationalVelocity *= rotationDiff / rotationMag;
float rotationalAcceleration = targetRotationalVelocity - rotationalVelocity;
float rotationalAccelMag = abs(rotationalAcceleration);
if (rotationalAccelMag > MAX_ANGULAR_ACCELERATION) {
rotationalAcceleration *= MAX_ANGULAR_ACCELERATION / rotationalAccelMag;
}
rotationalVelocity += rotationalAcceleration;
rotation += rotationalVelocity;
}
}