1 /*
2 Copyright (C) 2004-2011 Parallel Realities
3 Copyright (C) 2011-2015 Perpendicular Dimensions
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13
14 See the GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
20 */
21
22 #include "droidBoss.h"
23
24 void droidBossAttack();
25
droidBossChangeParticles()26 void droidBossChangeParticles()
27 {
28 (self->x < player.x) ? self->face = 0 : self->face = 1;
29
30 int color = 0;
31 float dx, dy;
32
33 dx = 0;
34
35 switch (self->shieldFrequency)
36 {
37 case 0:
38 color = graphics.red;
39 break;
40 case 1:
41 color = graphics.yellow;
42 break;
43 case 2:
44 color = graphics.green;
45 break;
46 case 3:
47 color = graphics.cyan;
48 break;
49 case 4:
50 color = graphics.white;
51 break;
52 }
53
54 for (int i = 0 ; i < 20 ; i++)
55 {
56 dy = Math::rrand(-150, -50);
57 dy /= 100;
58 map.addParticle(self->x + (Math::prand() % self->width), self->y + (Math::prand() % self->height), dx, dy, Math::rrand(1, 60), color, NULL, PAR_WEIGHTLESS);
59 }
60
61 self->setThinkTime(2);
62 self->setActionFinished(10);
63 self->think = &droidBossChangeParticles;
64 self->custom--;
65
66 if (self->custom == 0)
67 {
68 self->setThinkTime(2);
69 self->setActionFinished(10);
70 self->think = &droidBossAttack;
71 }
72 }
73
droidBossChasePlayer()74 void droidBossChasePlayer()
75 {
76 (self->x < player.x) ? self->face = 0 : self->face = 1;
77
78 float dx, dy;
79
80 Math::calculateSlope(player.x, player.y, self->x, self->y, &dx, &dy);
81
82 self->dx = (dx * 2);
83 self->dy = (dy * 2);
84
85 self->setThinkTime(10);
86 self->setActionFinished(10);
87 self->think = &droidBossAttack;
88 }
89
droidBossChangeFrequency()90 void droidBossChangeFrequency()
91 {
92 (self->x < player.x) ? self->face = 0 : self->face = 1;
93
94 self->shieldFrequency = Math::rrand(WP_PISTOL, WP_SPREAD);
95
96 self->think = &droidBossChangeParticles;
97 self->setThinkTime(2);
98 self->setActionFinished(2);
99 self->custom = 5;
100
101 Math::removeBit(&self->flags, ENT_AIMS);
102 Math::removeBit(&self->flags, ENT_RAPIDFIRE);
103
104 switch (self->shieldFrequency)
105 {
106 case WP_PISTOL:
107 self->currentWeapon = getWeaponByName("Aimed Pistol");
108 Math::addBit(&self->flags, ENT_AIMS);
109 self->setSprites(graphics.getSprite("JetpackBlobRight1", true), graphics.getSprite("JetpackBlobLeft1", true), graphics.getSprite("JetpackBlobLeft1", true));
110 break;
111 case WP_MACHINEGUN:
112 self->currentWeapon = getWeaponByName("Machine Gun");
113 Math::addBit(&self->flags, ENT_RAPIDFIRE);
114 self->setSprites(graphics.getSprite("JetpackBlobRight2", true), graphics.getSprite("JetpackBlobLeft2", true), graphics.getSprite("JetpackBlobLeft2", true));
115 break;
116 case WP_LASER:
117 self->currentWeapon = getWeaponByName("Alien Laser Cannon");
118 self->setSprites(graphics.getSprite("JetpackBlobRight3", true), graphics.getSprite("JetpackBlobLeft3", true), graphics.getSprite("JetpackBlobLeft3", true));
119 break;
120 case WP_GRENADES:
121 self->currentWeapon = getWeaponByName("Aimed Pistol");
122 self->shieldFrequency = WP_PISTOL;
123 Math::addBit(&self->flags, ENT_AIMS);
124 self->setSprites(graphics.getSprite("JetpackBlobRight1", true), graphics.getSprite("JetpackBlobLeft1", true), graphics.getSprite("JetpackBlobLeft1", true));
125 break;
126 case WP_SPREAD:
127 self->currentWeapon = &weapon[WP_ALIENSPREAD];
128 Math::addBit(&self->flags, ENT_AIMS);
129 self->setSprites(graphics.getSprite("JetpackBlobRight4", true), graphics.getSprite("JetpackBlobLeft4", true), graphics.getSprite("JetpackBlobLeft4", true));
130 break;
131 }
132
133 audio.playSound(SND_BOSSCUSTOM1, CH_AMBIANCE, self->x);
134 }
135
droidBossDie()136 void droidBossDie()
137 {
138 self->health -= Math::rrand(1, 2);
139 self->setActionFinished(30);
140 self->setThinkTime(2);
141
142 self->dx = Math::rrand(-3, 3);
143 self->dy = Math::rrand(-3, 3);
144
145 addExplosion(self->x + Math::prand() % self->width, self->y + Math::prand() % self->height, 50, &player);
146 addSmokeAndFire(self, Math::rrand(-5, 5), Math::rrand(-5, 5), 2);
147
148 if (self->health <= -100)
149 {
150 checkObjectives(self->name, false);
151
152 map.mainBossPart = NULL;
153 }
154 }
155
droidGoCrazy()156 void droidGoCrazy()
157 {
158 self->dx = Math::rrand(-8, 8);
159 self->dy = Math::rrand(-8, 8);
160
161 (self->dx > 0) ? self->face = 0 : self->face = 1;
162
163 if (self->y >= 4800)
164 self->dy = -2;
165
166 self->setThinkTime(2);
167 self->setActionFinished(5);
168 self->think = &droidGoCrazy;
169
170 self->custom--;
171
172 if (self->custom == 0)
173 {
174 Math::removeBit(&self->flags, ENT_IMMUNE);
175 self->think = &droidBossChangeFrequency;
176 self->setActionFinished(2);
177 self->setThinkTime(2);
178 }
179 }
180
droidBossReact()181 void droidBossReact()
182 {
183 if ((Math::prand() % 10) == 0)
184 {
185 Math::addBit(&self->flags, ENT_IMMUNE);
186 audio.playSound(SND_BOSSCUSTOM2, CH_SPAWN, self->x);
187 self->custom = 5;
188 self->think = &droidGoCrazy;
189 self->setActionFinished(2);
190 self->setThinkTime(2);
191 }
192 }
193
droidBossFire()194 void droidBossFire()
195 {
196 (self->x < player.x) ? self->face = 0 : self->face = 1;
197
198 if (hasClearShot((Entity*)self))
199 {
200 self->flags & ENT_AIMS ? addBullet((Entity*)self, 0, 0) : addBullet((Entity*)self, self->currentWeapon->getSpeed(self->face), 0);
201
202 if (self->currentWeapon == &weapon[WP_ALIENSPREAD])
203 {
204 addBullet(self, self->currentWeapon->getSpeed(self->face), 2);
205 addBullet(self, self->currentWeapon->getSpeed(self->face), -2);
206 }
207 }
208
209 self->setThinkTime(2);
210 self->setActionFinished(self->currentWeapon->reload);
211
212 self->custom--;
213 if (self->custom == 0)
214 self->think = &droidBossAttack;
215 }
216
orbWait()217 void orbWait()
218 {
219 self->setThinkTime(60);
220 self->setActionFinished(60);
221 }
222
orbExplode()223 void orbExplode()
224 {
225 addExplosion(self->x, self->y, 50, self);
226 self->setThinkTime(2);
227 self->setActionFinished(2);
228 self->active = false;
229 self->face = 1;
230 self->think = &orbWait;
231 self->x = 9999;
232 self->y = 9999;
233 }
234
doOrb()235 void doOrb()
236 {
237 (self->x < player.x) ? self->face = 0 : self->face = 1;
238
239 float dx, dy;
240
241 Math::calculateSlope(player.x, player.y, self->x, self->y, &dx, &dy);
242
243 self->dx = (dx * (1 + game.skill));
244 self->dy = (dy * (1 + game.skill));
245
246 self->setThinkTime(2);
247 self->setActionFinished(Math::rrand(10, 90));
248 self->think = &doOrb;
249
250 if (game.skill >= 2)
251 {
252 int distX = (int)fabs(player.x - self->x);
253 int distY = (int)fabs(player.y - self->y);
254
255 distX *= distX;
256 distY *= distY;
257
258 int distance = (int)sqrt(distX + distY);
259
260 if (distance <= 25)
261 self->health = -1;
262 }
263
264 if ((Math::prand() % 10) == 0)
265 self->health = -1;
266 }
267
fireOrb(int i)268 void fireOrb(int i)
269 {
270 debug(("fireOrb()\n"));
271
272 map.boss[i]->active = true;
273 map.boss[i]->maxHealth = map.boss[i]->health = 1 * game.skill;
274 map.boss[i]->x = self->x;
275 map.boss[i]->y = self->y + 20;
276
277 map.boss[i]->think = &doOrb;
278 map.boss[i]->die = &orbExplode;
279 map.boss[i]->setActionFinished(2);
280 map.boss[i]->setThinkTime(2);
281 }
282
droidBossOrbAttack()283 void droidBossOrbAttack()
284 {
285 debug(("droidBossClawAttack\n"));
286
287 for (int i = 1 ; i < 6 ; i++)
288 {
289 if (!map.boss[i]->active)
290 {
291 fireOrb(i);
292 self->setThinkTime(2);
293 self->setActionFinished(90);
294 self->think = &droidBossAttack;
295 return;
296 }
297 }
298
299 // nothing to do(!)
300 self->think = &droidBossAttack;
301 }
302
droidBossMoveRandomly()303 void droidBossMoveRandomly()
304 {
305 self->dx = Math::rrand(-200, 200);
306 self->dy = Math::rrand(-200, 200);
307
308 self->dx *= 0.01;
309 self->dy *= 0.01;
310
311 self->setThinkTime(2);
312 self->setActionFinished(90);
313 self->think = &droidBossAttack;
314 }
315
droidBossAttack()316 void droidBossAttack()
317 {
318 if (player.health < 1)
319 {
320 self->dx = self->dy = 0;
321 return;
322 }
323
324 (self->x < player.x) ? self->face = 0 : self->face = 1;
325
326 int r = Math::prand() % 110;
327
328 if (r < 30)
329 {
330 self->think = &droidBossChasePlayer;
331 self->setThinkTime(2);
332 self->setActionFinished(2);
333 self->custom = 10;
334 }
335 else if (r < 70)
336 {
337 self->think = &droidBossFire;
338 self->setThinkTime(2);
339 self->setActionFinished(2);
340 self->custom = Math::rrand(1, 3);
341 }
342 else if (r < 95)
343 {
344 self->think = &droidBossMoveRandomly;
345 self->setThinkTime(2);
346 self->setActionFinished(2);
347 self->custom = 10;
348 }
349 else if (r < 108)
350 {
351 self->think = &droidBossOrbAttack;
352 self->setThinkTime(2);
353 self->setActionFinished(2);
354 self->custom = Math::rrand(1, 3);
355 }
356 else
357 {
358 self->think = &droidBossChangeFrequency;
359 self->setThinkTime(2);
360 self->setActionFinished(2);
361 }
362 }
363
droidBossInit()364 void droidBossInit()
365 {
366 debug(("droidBossInit\n"));
367
368 map.boss[0] = new Boss();
369 strlcpy(map.boss[0]->name, "BioMech Jetpack Blob", sizeof map.boss[0]->name);
370 map.boss[0]->health = 30 * game.skill;
371 map.boss[0]->maxHealth = 30 * game.skill;
372 map.boss[0]->setSprites(graphics.getSprite("JetpackBlobRight1", true), graphics.getSprite("JetpackBlobLeft1", true), graphics.getSprite("JetpackBlobLeft1", true));
373 map.boss[0]->currentWeapon = getWeaponByName("Aimed Pistol");
374 map.boss[0]->face = 1;
375 map.boss[0]->active = true;
376 map.boss[0]->x = 3172;
377 map.boss[0]->y = 4646;
378 map.boss[0]->immune = 0;
379 map.boss[0]->setThinkTime(2);
380 map.boss[0]->setActionFinished(2);
381 map.boss[0]->think = &droidBossChangeFrequency;
382 map.boss[0]->react = &droidBossReact;
383 map.boss[0]->die = &droidBossDie;
384 Math::addBit(&map.boss[0]->flags, ENT_FLIES);
385
386 map.setMainBossPart(map.boss[0]);
387
388 for (int i = 1 ; i < 6 ; i++)
389 {
390 map.boss[i] = new Boss();
391 strlcpy(map.boss[i]->name, "Orb Bomb", sizeof map.boss[i]->name);
392 map.boss[i]->setSprites(graphics.getSprite("DroidOrb", true), graphics.getSprite("DroidOrb", true), graphics.getSprite("DroidOrb", true));
393 map.boss[i]->active = false;
394 map.boss[i]->face = 1;
395 map.boss[i]->think = &orbWait;
396 map.boss[i]->x = 9999;
397 map.boss[i]->y = 9999;
398 Math::addBit(&map.boss[i]->flags, ENT_FLIES);
399 }
400
401 audio.loadSound(SND_BOSSCUSTOM1, "sound/droidChangeFreq");
402 audio.loadSound(SND_BOSSCUSTOM2, "sound/droidFreakOut");
403
404 debug(("droidBossInit: Done\n"));
405 }
406