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