1 //
2 //   boss.c
3 //
4 //   Copyright 2007, 2008 Lancer-X/ASCEAI
5 //
6 //   This file is part of Meritous.
7 //
8 //   Meritous is free software: you can redistribute it and/or modify
9 //   it under the terms of the GNU General Public License as published by
10 //   the Free Software Foundation, either version 3 of the License, or
11 //   (at your option) any later version.
12 //
13 //   Meritous is distributed in the hope that it will be useful,
14 //   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 //   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 //   GNU General Public License for more details.
17 //
18 //   You should have received a copy of the GNU General Public License
19 //   along with Meritous.  If not, see <http://www.gnu.org/licenses/>.
20 //
21 
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <math.h>
26 #include <time.h>
27 #include <SDL.h>
28 #include <SDL_image.h>
29 
30 #include "levelblit.h"
31 #include "mapgen.h"
32 #include "demon.h"
33 #include "gamemap.h"
34 #include "audio.h"
35 
36 char *boss_names[] = {	"MERIDIAN",
37 						"ATARAXIA",
38 						"MERODACH",
39 						"WERVYN ANIXIL" };
40 
41 int boss_fight_mode = 0;
42 // 0 - no boss fight
43 // 1 - entering boss room
44 // 2 - fiting teh boss
45 // 3 - boss dying
46 // 4-23 - final boss dying sequence
47 
48 int current_boss_room = 0;
49 int current_boss = 0;
50 int boss_x, boss_y;
51 int boss_hp;
52 int boss_lives;
53 int boss_tail_len = 0;
54 int boss_breakpoint;
55 int boss_flash = 0;
56 int boss_new_life = 0;
57 int boss_ox, boss_oy;
58 int boss_dlg = 0;
59 int resetboss = 0;
60 int final_boss_dlg = 0;
61 float boss_dmgrate = 1;
62 float boss_dir;
63 
64 int proxy_seek = 0;
65 
66 int boss_m_heads;
67 int boss_m_hx[4];
68 int boss_m_hy[4];
69 float boss_m_hd[4];
70 
71 float boss_2h_dir = 0;
72 int boss_2h_dst = 64;
73 
74 int percent_required[4] = {15, 40, 60, 75};
75 
76 char *artifact_names[] = { 	"Holy Sword 'Balmung'",
77 							"Mystic Halberd 'Amenonuhoko'",
78 							"Divine Bow 'Gandiva'",
79 							"Cursed Seal of Yahveh"};
80 
81 int CanGetArtifact();
82 void DrawArtifactOverhead(int p_obj);
83 
84 struct dlg_box {
85 	int pos;
86 	int lines;
87 	int last;
88 	char *txt;
89 };
90 
91 struct dlg_box dtext[5][12] =
92 //      "This string of text is precisely seventy eight characters in length, usefully."
93 {
94   { // Meridian
95 	{0, 5, 0,
96 		"MERIDIAN:\n"
97 		"  \n"
98 		"  Different...\n"
99 		"  \n"
100 		"  Your PSI is different. Your magic is perverse. Poisonous. Treacherous."
101 	},
102 	{1, 5, 0,
103 		"MERIT:\n"
104 		"  \n"
105 		"  Nothing but darkness carried on the wind of a curse. It's amazing that you\n"
106 		"  are able to communicate with me at all. Surely a statement on the perversity\n"
107 		"  of my magic, coming from you, is more than a little bit hypocritical."
108 	},
109 	{0, 6, 1,
110 		"MERIDIAN:\n"
111 		"  \n"
112 		"  So. You've even brought that tainted thing with you.\n"
113 		"  That does not really surprise me, but nonetheless, your weak, impure PSI\n"
114 		"  will do you little good against one manifest of shadows. Pitiful thing from\n"
115 		"  the surface, MERIDIAN shall return you to the dust from whence you came!"
116 	}
117   },
118 
119   { // Ataraxia
120 	{0, 5, 0,
121 		"ATARAXIA:\n"
122 		"  \n"
123 		"  It felt nearly as inconsequential as a stilling in the breeze, but there was\n"
124 		"  more to it, wasn't it? It feels as though I lost a part of myself. Was that\n"
125 		"  your doing?"
126 	},
127 	{1, 6, 0,
128 		"MERIT:\n"
129 		"  \n"
130 		"  Moving Ochra's Keys from the ley lines was only the start, am I correct?\n"
131 		"  Nonetheless, what you've already caused is enough. The Keys were placed to\n"
132 		"  prevent corruption in the PSI. Moving them renders the PSI unusable. There\n"
133 		"  is nothing to be gained from THAT."
134 	},
135 	{0, 8, 0,
136 		"ATARAXIA:\n"
137 		"  \n"
138 		"  Statements like that belie your youthful naivety and ignorance. There is no\n"
139 		"  way to make magic unusable. What you see now is the other side of PSI that\n"
140 		"  pitiful beings such as yourself turn a blind eye to. Like the sun at dawn\n"
141 		"  the sun at dusk, it is nonetheless the same sun, and each beautiful in its\n"
142 		"  own way. PSI isn't just for things like you, but for the entire cosmos.\n"
143 		"  Nothing can be gained by preventing it from taking its natural course."
144 	},
145 	{1, 8, 0,
146 		"MERIT:\n"
147 		"  \n"
148 		"  You delude yourself with the crazed whisperings from the muck that spawned\n"
149 		"  you. You say that this PSI is still PSI, but it is not. PSI is the energy of\n"
150 		"  creation. This is the energy of destruction. PSI is a pure force, gathered\n"
151 		"  from around me and targeted with my mind. This is a foul mixture, existing\n"
152 		"  as half-mad corruptions like you. It can only break apart when confronted\n"
153 		"  with true power."
154 	},
155 	{0, 6, 1,
156 		"ATARAXIA:\n"
157 		"  \n"
158 		"  You realise that not even your fellow PSI users agree with you? But, it is\n"
159 		"  of little consequence. I'll show you the true beauty of that which you shun\n"
160 		"  by smothering you in it and turning your flesh into fuel for the PSI that\n"
161 		"  runs through the veins of this Dome!"
162 	}
163   },
164 
165   { // Merodach
166 	{0, 9, 0,
167 		"MERODACH:\n"
168 		"  \n"
169 		"  e v e r y t h i n g   h a s   t u r n e d   t o   d a r k n e s s\n"
170 		"  \n"
171 		"  n o   s o u n d   n o   l i g h t   n o   s e n s a t i o n\n"
172 		"  \n"
173 		"  w a s   t h i s   w h a t   y o u   w e r e   a f t e r   f r o m\n"
174 		"  \n"
175 		"  t h e   b e g i n n i n g ?"
176 	},
177 	{0, 7, 1,
178 		"MERODACH\n"
179 		"  \n"
180 		"  i ' l l   w r a p   m y   r e m a i n i n g   P S I   a r o u n d   y o u\n"
181 		"  \n"
182 		"  a n d   e x t i n g u i s h   t h a t   l i f e   o f   y o u r s\n"
183 		"  \n"
184 		"  l e t ' s   s e e   h o w   y o u   e n j o y   e t e r n a l   r e s t"
185 	}
186   },
187 
188   { // Wervyn Anixil
189     {1, 6, 0,
190 		"MERIT:\n"
191 		"  \n"
192 		"  ANIXIL? So, that's how it is. I assume you worked out a way of using the\n"
193 		"  PSI in this state. So, not happy with your allotted PSI from the Dome, you\n"
194 		"  decided to render it in this form and take it all for yourself.\n"
195 		"  What was your reasoning for doing something this rash, though?"
196 	},
197 
198 	{0, 9, 0,
199 		"WERVYN ANIXIL:\n"
200 		"  \n"
201 		"  All you seem to do is complain. That's what I've discovered through this,\n"
202 		"  and it's definitely part of the reason I did it. I do creative things like\n"
203 		"  this sort of 'punishment' because it lets me better understand the nature of\n"
204 		"  all the forces involved. It tells me that you, MERIT, really do not have a\n"
205 		"  clue how to relate to other PSI users, and that for the most part you're\n"
206 		"  entirely self-absorbed. It tells me that, while PSI users in general doesn't\n"
207 		"  really 'get' the ethos of the forces involved here, and that the rerendered"
208 	},
209 
210 	{0, 9, 0,
211 		"WERVYN ANIXIL:\n"
212 		"  \n"
213 		"  PSI, as intelligent as it is, pretty much entirely understands it, though it\n"
214 		"  was mistaken that the rendering of this form was intentional.\n"
215 		"  \n"
216 		"  Regarding that, by the way, THAT is the reason I thought you needed a\n"
217 		"  punishment. Making ridiculously inefficient, wasteful use of the limited PSI\n"
218 		"  we have available wouldn't have been a problem if it was just the normal PSI\n"
219 		"  like we'd planned it to be. But you knew full well that PSI users who wanted"
220 	},
221 
222 	{0, 9, 0,
223 	    "WERVYN ANIXIL:\n"
224 		"  \n"
225 		"  nothing to do with it were being greatly inconvenienced by usage clogging up\n"
226 		"  the limited ley-lines we have. That makes you an asshole, a deliberate prick\n"
227 		"  to everyone in the world because you thought you could get away with it, and\n"
228 		"  that it wouldn't matter.\n"
229 		"  \n"
230 		"  I also take issue with this nonsense about your punishment being severe,\n"
231 		"  because it's not. You can still use your PSI as you used it before, to a"
232 	},
233 
234 	{0, 9, 0,
235 		"WERVYN ANIXIL:\n"
236 		"  \n"
237 		"  similar level of effectiveness. I specifically allowed access to THIS form\n"
238 		"  of PSI so that you couldn't complain that you couldn't use magic. Now\n"
239 		"  exiling you to the Dome had multiple meanings. One was to effectively tell\n"
240 		"  you, 'I don't think you have anything of value to do, so I'm putting you in\n"
241 		"  a place where no one does anything of value.' I still think that, by the\n"
242 		"  way. Another was, like I said, to see what you would do. Maybe you would\n"
243 		"  actually cast something worth the PSI, then. Or maybe you would take the"
244 	},
245 
246 	{0, 8, 0,
247 		"WERVYN ANIXIL:\n"
248 		"  \n"
249 		"  hint and just stop it for the week until I decided you'd had enough. But you\n"
250 		"  couldn't do either of those things, because all you do is complain.\n"
251 		"  \n"
252 		"  I want you to take a look at yourself and ask what you want to accomplish\n"
253 		"  here, and what you think is keeping you here. Obviously you have some\n"
254 		"  attachment, even though you keep saying the new PSI is worthless. This isn't"
255 	},
256 
257 	{0, 9, 0,
258 		"WERVYN ANIXIL:\n"
259 		"  \n"
260 		"  true, of course, ATARAXIA really captures a good point here. For the most\n"
261 		"  part, PSI users just find you extremely annoying because you continually set\n"
262 		"  yourself at odds with pretty much every kind of PSI usage but your own, and\n"
263 		"  then complain about it. Do you want things to be different? They certainly\n"
264 		"  can be. 'EVEN IF I DO CHANGE, THEN MY PSI WON'T BE GOOD ENOUGH!' That's\n"
265 		"  bullshit. All other PSI users seem to be on the path to figuring out how to\n"
266 		"  just use it. You can too."
267 	},
268 
269 	{0, 9, 0,
270 		"WERVYN ANIXIL:\n"
271 		"  \n"
272 		"  Your 'punishment' is up tomorrow, I'd only planned to do it for a week. Of\n"
273 		"  course, you can continue complaining, and I may have to think of something\n"
274 		"  else to do to you. But I'm not going to kill you. If you want to leave this\n"
275 		"  mortal coil because you can't take it anymore, then leave. If you don't want\n"
276 		"  to leave, then start working out your issues with the new PSI, I actually\n"
277 		"  would love to help. And if you don't want to do that, then I'm sorry, but\n"
278 		"  you're going to continue being useless at magic, because the way you are"
279 	},
280 
281 	{0, 4, 0,
282 		"WERVYN ANIXIL:\n"
283 		"  \n"
284 		"  now, it's not surprising at all that your PSI ability is as stunted as it\n"
285 		"  is."
286 	},
287 
288 	{1, 4, 0,
289 		"MERIT:\n"
290 		"  \n"
291 		"  The tainted PSI has driven even you mad. Mad with power. This is a blow I\n"
292 		"  I must strike, for everyone. For humanity."
293 	},
294 
295 	{0, 3, 1,
296 		"WERVYN ANIXIL:\n"
297 		"  \n"
298 		"  I'll meet you at the point of diminishing returns."
299 	}
300   },
301 
302   { // Wervyn Anixil???? (with Agate Knife)
303     {1, 8, 0,
304 		"MERIT:\n"
305 		"  \n"
306 		"  ANIXIL? So, that's how it is. I assume you worked out a way of using the\n"
307 		"  PSI in this state. So, not happy with your allotted PSI from the Dome, you\n"
308 		"  decided to render it in this form and take it all for yourself.\n"
309 		"  What was your reasoning for doing something this rash, though?\n"
310 		"  \n"
311 		"  That was what you wanted to hear, right?"
312 	},
313 
314 	{0, 7, 0,
315 		"WERVYN ANIXIL????:\n"
316 		"  \n"
317 		"  All you seem to do is complain. That's what I've discovered through this,\n"
318 		"  and it's definitely part of the ... ... ... ... ... ... ... ... ... ... ...\n"
319 		"  ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...\n"
320 		"  ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...\n"
321 		"  ... ... oh."
322 	},
323 
324 	{1, 6, 0,
325 		"MERIT:\n"
326 		"  \n"
327 		"  If I've learned anything in life, it's that things are often not quite as\n"
328 		"  they seem. You couldn't use the Agate Knife, so you attempted to hide it.\n"
329 		"  \n"
330 		"  Unfortunately for you, I found it and recognised it."
331 	},
332 
333 	{0, 7, 0,
334 	    "WERVYN ANIXIL????:\n"
335 		"  \n"
336 		"  . . . . no. It's not just recognising the Knife at all. You were actually\n"
337 		"  able to use it, too.\n"
338 		"  \n"
339 		"  So. You too knew the secret?"
340 	},
341 
342 	{1, 5, 0,
343 		"MERIT:\n"
344 		"  \n"
345 		"  I was one of the few that he told.\n"
346 		"  \n"
347 		"  And so I know."
348 	},
349 
350 	{0, 3, 0,
351 		"????:\n"
352 		"  \n"
353 		"  Umm..."
354 	},
355 
356 	{1, 7, 0,
357 		"MERIT:\n"
358 		"  \n"
359 		"  Well, it wasn't a bad attempt. If I hadn't found THAT, I would never have\n"
360 		"  known.\n"
361 		"  \n"
362 		"  Now that that's taken care of, I might as well finish what I came here for.\n"
363 		"  Prepare yourself."
364 	},
365 
366 	{0, 3, 1,
367 		"????:\n"
368 		"  \n"
369 		"  I won't hold back! There's not a lot of point, now."
370 	}
371   }
372 };
373 
BossDialog()374 void BossDialog()
375 {
376 	int ypos;
377 	int lines;
378 	int dialog_set;
379 
380 	dialog_set = current_boss;
381 
382 	if (current_boss == 3) {
383 		if (player_shield == 30) {
384 			dialog_set = 4;
385 		}
386 	}
387 
388 	lines = dtext[dialog_set][boss_dlg-1].lines;
389 	if (enter_pressed) {
390 		if (dtext[dialog_set][boss_dlg-1].last) {
391 			boss_dlg = 0;
392 			boss_fight_mode = 2;
393 		} else {
394 			boss_dlg++;
395 		}
396 		enter_pressed = 0;
397 	}
398 	if (boss_dlg > 0) {
399 		if (dtext[dialog_set][boss_dlg-1].pos == 0) {
400 			ypos = 29;
401 		} else {
402 			ypos = 466 - lines * 10 - 14;
403 		}
404 		DrawRect(0, ypos, 640, lines * 10 + 14, 0);
405 		DrawRect(2, ypos+2, 636, lines * 10 + 10, 40);
406 		DrawRect(4, ypos+4, 632, lines * 10 + 6, 80);
407 		draw_text(8, ypos+8, dtext[dialog_set][boss_dlg-1].txt, 255);
408 	}
409 }
410 
InitBossVars()411 void InitBossVars()
412 {
413 	current_boss_room = 0;
414 	current_boss = 0;
415 	boss_tail_len = 0;
416 	boss_fight_mode = 0;
417 	boss_flash = 0;
418 	boss_new_life = 0;
419 	final_boss_dlg = 0;
420 }
421 
Curse()422 void Curse()
423 {
424 	int i;
425 	int x, y;
426 	unsigned char tile;
427 	struct RoomConnection *rc;
428 	// Upon taking the cursed seal
429 
430 	// Make the place of power your checkpoint
431 
432 	checkpoint_x = rooms[place_of_power].w * 16 + rooms[place_of_power].x * 32;
433 	checkpoint_y = rooms[place_of_power].h * 16 + rooms[place_of_power].y * 32;
434 
435 	// Turn the start room into a boss room, and clear it
436 
437 	rooms[0].room_type = 2;
438 	Paint(rooms[0].x+1, rooms[0].y+1, rooms[0].w-2, rooms[0].h-2, "/usr/local/share/meritous/d/fbossroom.loc");
439 
440 	// Lock all unvisited rooms off
441 
442 	for (i = 0; i < 3000; i++) {
443 		if (!rooms[i].visited) {
444 			rc = rooms[i].con;
445 
446 			while (rc != NULL) {
447 				x = rc->x2;
448 				y = rc->y2;
449 				tile = Get(x, y);
450 				if ((tile >= 38)&&(tile <= 41)) tile = tile - 38 + 4;
451 				if ((tile >= 21)&&(tile <= 24)) tile = tile - 21 + 4;
452 				if ((tile >= 13)&&(tile <= 16)) tile = tile - 13 + 4;
453 				Put(x, y, tile, GetRoom(x, y));
454 				rc = rc->n;
455 			}
456 			rooms[i].con = NULL;
457 			rooms[i].connections = 0;
458 		}
459 	}
460 
461 	// Every 25th monster becomes a curse. All other monsters are deleted
462 
463 	CurseEnemies();
464 }
465 
DrawPowerObject()466 void DrawPowerObject()
467 {
468 	int p_x, p_y;
469 	int p_obj = rooms[player_room].room_param;
470 	static int tick = 0;
471 	static int collect = 0;
472 	int i;
473 	int required_enemies;
474 	int n_artifacts;
475 	int dx, dy;
476 	int hover_v, off_v;
477 	float ddir;
478 	int dmag;
479 
480 	if (place_of_power == player_room) p_obj = 3;
481 
482 	n_artifacts = current_boss + artifacts[8] + artifacts[9] + artifacts[10];
483 
484 	required_enemies = total_enemies * (percent_required[n_artifacts]) / 100;
485 
486 	if (rooms[player_room].room_type == 4) required_enemies = 0;
487 
488 	SDL_Rect from, to;
489 
490 	hover_v = 16;
491 	off_v = 48;
492 
493 	p_x = (rooms[player_room].w * 32 / 2 - 16) + rooms[player_room].x * 32;
494 
495 	if (!game_paused) {
496 		if ((rooms[player_room].room_type == 5) || (rooms[player_room].room_type == 6)) {
497 			if (CanGetArtifact()) {
498 				if ((Get((player_x+PLAYERW/2)/32, (player_y+PLAYERH/2)/32)==42) ||
499 					(PlayerDist(rooms[player_room].w * 16 + rooms[player_room].x * 32,
500 								rooms[player_room].h * 16 + rooms[player_room].y * 32) < 32)) {
501 					if (rooms[player_room].enemies == 0) {
502 						off_v = 48 - (collect * 48 / 100);
503 						hover_v = 16 - (collect * 16 / 100);
504 
505 						//DrawCircle(p_x + 16 - scroll_x, player_y - 48 - scroll_y, collect * 4 + 1, rand()%192+64);
506 						DrawCircle(player_x + PLAYERW/2 - scroll_x, player_y + PLAYERH/2- scroll_y, (100-collect) * 4 + 1, rand()%192+64);
507 
508 						collect++;
509 						if (collect == 1) {
510 							SND_Pos("/usr/local/share/meritous/a/crystal2.wav", 100, 0);
511 						}
512 						if (collect > 100) {
513 							collect = 0;
514 							rooms[player_room].room_type = 4;
515 							artifacts[8 + p_obj] = 1;
516 							specialmessage = 30 + p_obj;
517 							specialmessagetimer = 120;
518 
519 							if (p_obj == 3) {
520 								Curse();
521 							}
522 						}
523 					}
524 				}
525 			}
526 		}
527 	}
528 
529 	p_y = (rooms[player_room].h * 32 / 2 - 16) + rooms[player_room].y * 32 - off_v + sin((float)tick / 20.0)*hover_v;
530 
531 	from.x = (8 + p_obj) * 32;
532 	from.y = 0;
533 	from.w = 32;
534 	from.h = 32;
535 
536 	to.x = p_x - scroll_x;
537 	to.y = p_y - scroll_y;
538 
539 	if (killed_enemies >= required_enemies) {
540 		DrawCircle(p_x + 16 - scroll_x, p_y + 16 - scroll_y, 34+rand()%5 + collect * 4 + 1, rand()%128+128);
541 	}
542 	SDL_BlitSurface(artifact_spr, &from, screen, &to);
543 
544 	//printf("Required: %d   Killed %d   Value: %d\n", required_enemies, killed_enemies, (int)(sqrt((required_enemies - killed_enemies)/4)+5));
545 	for (i = 0; i < (int)(sqrt((required_enemies - killed_enemies))+5); i++) {
546 		ddir = (float)(rand()%256) * M_PI * 2 / 256;
547 		dmag = rand()%20;
548 		dx = p_x + 16 + cos(ddir)*dmag;
549 		dy = p_y + 16 + sin(ddir)*dmag;
550 		DrawCircle(dx - scroll_x, dy - scroll_y, rand()%5+1, 0);
551 		DrawCircle(dx - scroll_x, dy - scroll_y, rand()%3+1, 64);
552 	}
553 
554 	tick++;
555 }
556 
DrawBossHP(int bar_length)557 void DrawBossHP(int bar_length)
558 {
559 	static SDL_Surface *boss_hp_icon = NULL;
560 	int draw_amt;
561 	Uint8 draw_col;
562 	SDL_Rect to;
563 	to.x = 0;
564 	to.y = 29;
565 
566 	if (boss_hp_icon == NULL) {
567 		boss_hp_icon = IMG_Load("/usr/local/share/meritous/i/boss_icon.png");
568 	}
569 	SDL_BlitSurface(boss_hp_icon, NULL, screen, &to);
570 	DrawRect(16, 28, 624, 17, 0);
571 	DrawRect(17, 29, 622, 15, 32);
572 	DrawRect(18, 30, 620, 13, 64);
573 
574 	draw_col = 128 + (boss_hp * 127 / 1000);
575 	if (bar_length < 100) {
576 		draw_amt = bar_length * 614 / 100;
577 	} else {
578 		draw_amt = boss_hp * 614 / 1000;
579 	}
580 
581 	DrawRect(20, 32, draw_amt+2, 9, draw_col * 2 / 3);
582 	DrawRect(21, 33, draw_amt, 7, draw_col);
583 
584 
585 	if ( (current_boss == 3) && (player_shield == 30) ) {
586 		draw_text(22, 33, "??????????????", 0);
587 	} else {
588 		draw_text(22, 33, boss_names[current_boss], 0);
589 	}
590 }
591 
PDir(int x1,int y1,int x2,int y2)592 int PDir(int x1, int y1, int x2, int y2)
593 {
594 	int dx, dy;
595 
596 	dx = x2 - x1;
597 	dy = y2 - y1;
598 
599 	return atan2(dy, dx);
600 }
601 
CHDir(float original_dir,float new_dir,float dir_delta)602 float CHDir(float original_dir, float new_dir, float dir_delta)
603 {
604 	while (original_dir < 0) original_dir += M_PI * 2;
605 	while (new_dir < 0) new_dir += M_PI * 2;
606 
607 	original_dir = fmodf(original_dir, M_PI * 2) + M_PI * 2;
608 	new_dir = fmodf(new_dir, M_PI * 2) + M_PI * 2;
609 
610 	if (fabs(original_dir - new_dir) <= dir_delta) return new_dir;
611 	if (fabs((original_dir + M_PI * 2) - new_dir) <= dir_delta) return new_dir;
612 	if (fabs(original_dir - (new_dir + M_PI * 2)) <= dir_delta) return new_dir;
613 
614 	if (original_dir > new_dir) {
615 		if ((original_dir - new_dir) <= M_PI) {
616 			return original_dir - dir_delta;
617 		}
618 		if (((new_dir + M_PI*2) - original_dir) <= M_PI) {
619 			return original_dir + dir_delta;
620 		}
621 	} else {
622 		if ((new_dir - original_dir) <= M_PI) {
623 			return original_dir + dir_delta;
624 		}
625 		if (((original_dir + M_PI*2) - new_dir) <= M_PI) {
626 			return original_dir - dir_delta;
627 		}
628 	}
629 
630 	exit(1);
631 
632 	return original_dir;
633 }
634 
TryHurtBoss(int x,int y,int range,int power)635 void TryHurtBoss(int x, int y, int range, int power)
636 {
637 	int atk_power;
638 	if (boss_new_life) return;
639 	if (dist(x, y, boss_x, boss_y) <= range) {
640 		atk_power = 400 * power / circuit_size + power / 75;
641 		if (atk_power > boss_breakpoint) {
642 			boss_hp -= (atk_power - boss_breakpoint) * boss_dmgrate;
643 			SND_Pos("/usr/local/share/meritous/a/enemyhit.wav", 128, dist(x, y, boss_x, boss_y) / 4);
644 			boss_flash = 40;
645 			if (boss_hp <= 0) {
646 				boss_new_life = 1;
647 				boss_hp = 0;
648 			}
649 		}
650 	}
651 }
652 
BossMovement(int move_x,int move_y)653 int BossMovement(int move_x, int move_y)
654 {
655 	if (!IsSolid(Get( (move_x - 12 + (current_boss==3)*6)/32, (move_y - 12 + (current_boss==3)*4)/32))) {
656 		if (!IsSolid(Get( (move_x + 12 - (current_boss==3)*6)/32, (move_y - 12 + (current_boss==3)*4)/32))) {
657 			if (!IsSolid(Get( (move_x - 12 + (current_boss==3)*6)/32, (move_y + 12 - (current_boss==3)*4)/32))) {
658 				if (!IsSolid(Get( (move_x + 12 - (current_boss==3)*6)/32, (move_y + 12 - (current_boss==3)*4)/32))) {
659 					return 1;
660 				}
661 			}
662 		}
663 	}
664 	return 0;
665 }
666 
DrawBoss()667 void DrawBoss()
668 {
669 	int flash_coeff;
670 	static int t = 0;
671 	t++;
672 
673 	flash_coeff = (boss_flash > 0)&&((boss_flash/3) % 2) ? 255 : 0;
674 	if (boss_flash > 0) boss_flash--;
675 	switch (current_boss) {
676 		case 0: {
677 			static SDL_Surface *boss_spr = NULL;
678 			static int tail_x[10], tail_y[10];
679 			int i;
680 
681 			SDL_Rect drawpos;
682 			if (boss_spr == NULL) {
683 				boss_spr = IMG_Load("/usr/local/share/meritous/i/boss1.png");
684 				SDL_SetColorKey(boss_spr, SDL_SRCCOLORKEY | SDL_RLEACCEL, 255);
685 			}
686 
687 			if (boss_tail_len == 0) {
688 				tail_x[boss_tail_len] = boss_x;
689 				tail_y[boss_tail_len] = boss_y;
690 				boss_tail_len++;
691 			} else {
692 				if (dist(tail_x[0], tail_y[0], boss_x, boss_y) >= 24) {
693 					if (boss_tail_len < 10) boss_tail_len++;
694 					for (i = 8; i >= 0; i--) {
695 						tail_x[i+1] = tail_x[i];
696 						tail_y[i+1] = tail_y[i];
697 					}
698 					tail_x[0] = boss_x;
699 					tail_y[0] = boss_y;
700 				} else {
701 					for (i = 0; i < boss_tail_len; i++) {
702 						tail_x[i] += -1 + rand() % 3;
703 						tail_y[i] += -1 + rand() % 3;
704 					}
705 				}
706 			}
707 
708 			for (i = 0; i < boss_tail_len; i++) {
709 				DrawCircleEx(tail_x[i] - scroll_x, tail_y[i] - scroll_y, 48 - i * 3, 0, 96 ^ flash_coeff);
710 			}
711 			for (i = 0; i < boss_tail_len; i++) {
712 				DrawCircleEx(tail_x[i] - scroll_x, tail_y[i] - scroll_y, 44 - i * 3, 0, 0 ^ flash_coeff);
713 			}
714 
715 			drawpos.x = boss_x - 16 - scroll_x;
716 			drawpos.y = boss_y - 16 - scroll_y;
717 
718 			SDL_BlitSurface(boss_spr, NULL, screen, &drawpos);
719 
720 			break;
721 		}
722 		case 1: {
723 			static SDL_Surface *boss_spr = NULL;
724 			float h_dist;
725 			float new_dst, new_dir;
726 			int i, j;
727 			int hx[4], hy[4];
728 			int heads;
729 			int mx, my;
730 			int check_pass;
731 
732 			heads = 5 - boss_lives;
733 			h_dist = M_PI * 2 / heads;
734 
735 			SDL_Rect drawpos;
736 			if (boss_spr == NULL) {
737 				boss_spr = IMG_Load("/usr/local/share/meritous/i/boss2.png");
738 				SDL_SetColorKey(boss_spr, SDL_SRCCOLORKEY | SDL_RLEACCEL, 255);
739 			}
740 
741 			new_dst = 64 + sin((float)t / 80.0) * 24;
742 
743 			new_dir = boss_2h_dir;
744 			new_dir += (float)(rand() % 16) / 128.0;
745 			new_dir -= (float)(rand() % 16) / 128.0;
746 
747 			// Check new dists
748 			check_pass = 1;
749 			for (i = 0; i < heads; i++) {
750 				mx = boss_x + cos(new_dir + h_dist * i) * new_dst;
751 				my = boss_y + sin(new_dir + h_dist * i) * new_dst;
752 
753 				if (!BossMovement(mx, my))
754 					check_pass = 0;
755 			}
756 
757 			if (check_pass) {
758 				boss_2h_dst = new_dst;
759 				boss_2h_dir = new_dir;
760 			}
761 
762 			// Heads
763 
764 			for (i = 0; i < heads; i++) {
765 				hx[i] = boss_x + cos(boss_2h_dir + h_dist * i) * boss_2h_dst;
766 				hy[i] = boss_y + sin(boss_2h_dir + h_dist * i) * boss_2h_dst;
767 			}
768 
769 			// Membranes
770 			for (j = 0; j < heads; j++) {
771 				for (i = 0; i < 5; i++) {
772 					mx = hx[j] + (hx[(j+1)%heads] - hx[j]) * i / 4;
773 					my = hy[j] + (hy[(j+1)%heads] - hy[j]) * i / 4;
774 
775 					DrawCircleEx(mx - scroll_x, my - scroll_y, 24 + abs(i - 2)*12 + rand()%6, 0, 96 ^ flash_coeff);
776 				}
777 			}
778 			for (j = 0; j < heads; j++) {
779 				for (i = 0; i < 5; i++) {
780 					mx = hx[j] + (hx[(j+1)%heads] - hx[j]) * i / 4;
781 					my = hy[j] + (hy[(j+1)%heads] - hy[j]) * i / 4;
782 
783 					DrawCircleEx(mx - scroll_x, my - scroll_y, 20 + abs(i - 2)*10 + rand()%6, 0, 0 ^ flash_coeff);
784 				}
785 			}
786 
787 			// Draw heads
788 
789 			for (i = 0; i < heads; i++) {
790 				drawpos.x = hx[i] - 16 - scroll_x;
791 				drawpos.y = hy[i] - 16 - scroll_y;
792 
793 				SDL_BlitSurface(boss_spr, NULL, screen, &drawpos);
794 			}
795 
796 			break;
797 		}
798 		case 2: {
799 			static SDL_Surface *boss_spr = NULL;
800 			int i;
801 			int mx, my;
802 			float md;
803 			SDL_Rect drawfrom, drawpos;
804 
805 			if (boss_spr == NULL) {
806 				boss_spr = IMG_Load("/usr/local/share/meritous/i/boss3.png");
807 				SDL_SetColorKey(boss_spr, SDL_SRCCOLORKEY | SDL_RLEACCEL, 0);
808 			}
809 
810 			boss_m_heads = boss_lives;
811 
812 			DrawCircleEx(boss_x - scroll_x, boss_y - scroll_y, 48, 0, 0 ^ flash_coeff);
813 			// Draw appendages
814 			for (i = 0; i < boss_m_heads; i++) {
815 				mx = boss_x;
816 				my = boss_y;
817 				md = M_PI * 2.0 / (float)boss_m_heads * i + (M_PI / 4);
818 
819 				while (dist(mx, my, boss_m_hx[i], boss_m_hy[i]) > 12) {
820 					md = CHDir(md, PDir(mx, my, boss_m_hx[i], boss_m_hy[i]), 0.5);
821 					//md = PDir(mx, my, boss_m_hx[i], boss_m_hy[i]);
822 					mx += cos(md) * 12;
823 					my += sin(md) * 12;
824 
825 					DrawCircleEx(mx - scroll_x, my - scroll_y, 16 + rand()%8, 0, 0 ^ flash_coeff);
826 				}
827 			}
828 			for (i = 0; i < boss_m_heads; i++) {
829 				DrawCircleEx(boss_m_hx[i] - scroll_x, boss_m_hy[i] - scroll_y, 28, 0, 0 ^ flash_coeff);
830 			}
831 			for (i = 0; i < boss_m_heads; i++) {
832 				mx = boss_x;
833 				my = boss_y;
834 				md = M_PI * 2.0 / (float)boss_m_heads * i + (M_PI / 4);
835 
836 				while (dist(mx, my, boss_m_hx[i], boss_m_hy[i]) > 12) {
837 					md = CHDir(md, PDir(mx, my, boss_m_hx[i], boss_m_hy[i]), 0.5);
838 					//md = PDir(mx, my, boss_m_hx[i], boss_m_hy[i]);
839 					mx += cos(md) * 12;
840 					my += sin(md) * 12;
841 					drawfrom.x = 32;
842 					drawfrom.y = 32 * (flash_coeff > 0);
843 					drawfrom.w = 32;
844 					drawfrom.h = 32;
845 					drawpos.x = mx - 16 - scroll_x;
846 					drawpos.y = my - 16 - scroll_y;
847 
848 					SDL_BlitSurface(boss_spr, &drawfrom, screen, &drawpos);
849 				}
850 			}
851 			// Draw heads
852 			drawfrom.x = 0;
853 			drawfrom.y = 32 * (flash_coeff > 0);
854 			drawfrom.w = 32;
855 			drawfrom.h = 32;
856 			for (i = 0; i < boss_m_heads; i++) {
857 				drawpos.x = boss_m_hx[i] - 16 - scroll_x;
858 				drawpos.y = boss_m_hy[i] - 16 - scroll_y;
859 				SDL_BlitSurface(boss_spr, &drawfrom, screen, &drawpos);
860 			}
861 
862 			// Draw core
863 			for (i = 0; i < boss_m_heads; i++) {
864 				drawpos.x = boss_x - 16 - scroll_x;
865 				drawpos.y = boss_y - 16 - scroll_y;
866 
867 				SDL_BlitSurface(boss_spr, &drawfrom, screen, &drawpos);
868 			}
869 			break;
870 		}
871 		case 3: {
872 			SDL_Rect drawto;
873 			static SDL_Surface *boss_spr = NULL;
874 
875 			if (boss_spr == NULL) {
876 				boss_spr = IMG_Load("/usr/local/share/meritous/i/boss4.png");
877 				SDL_SetColorKey(boss_spr, SDL_SRCCOLORKEY | SDL_RLEACCEL, 0);
878 			}
879 
880 			// Aura
881 
882 			DrawCircleEx(boss_x - scroll_x, boss_y - scroll_y, 64+rand()%4, 0, (192+rand()%64) ^ flash_coeff);
883 			DrawCircleEx(boss_x - scroll_x, boss_y - scroll_y, 48+rand()%4, 0, (rand()%64) ^ flash_coeff);
884 
885 			drawto.x = boss_x - 16 - scroll_x;
886 			drawto.y = boss_y - 16 - scroll_y;
887 
888 			SDL_BlitSurface(boss_spr, NULL, screen, &drawto);
889 			break;
890 		}
891 	}
892 }
893 
BC_BossIntro()894 void BC_BossIntro()
895 {
896 	static int boss_bar_fill = 0;
897 	static int boss_circle = 0, circle_reduce = 128;
898 
899 	if (boss_dlg != 0) {
900 		DrawBoss();
901 		DrawBossHP(100);
902 		return;
903 	}
904 
905 	if (boss_bar_fill < 100) {
906 		boss_bar_fill += 2;
907 		DrawBossHP(boss_bar_fill);
908 	} else {
909 		if (boss_circle < 128) {
910 			boss_circle += 2;
911 			DrawCircleEx(boss_x - scroll_x, boss_y - scroll_y, boss_circle, 0, 0);
912 			DrawCircle(boss_x - scroll_x, boss_y - scroll_y, boss_circle, 96);
913 		} else {
914 			if (circle_reduce > 4) {
915 				circle_reduce -= 2;
916 				DrawCircleEx(boss_x - scroll_x, boss_y - scroll_y, circle_reduce, 0, 0);
917 				DrawCircle(boss_x - scroll_x, boss_y - scroll_y, circle_reduce, 96);
918 				DrawBoss();
919 			} else {
920 				DrawBoss();
921 				if ((final_boss_dlg == 0) && (current_boss == 3)) {
922 					boss_dlg = 1;
923 					final_boss_dlg = 1;
924 				} else {
925 					boss_fight_mode = 2;
926 				}
927 				boss_circle = 0;
928 				boss_bar_fill = 0;
929 				circle_reduce = 128;
930 			}
931 		}
932 		DrawBossHP(100);
933 	}
934 }
935 
BC_BossCombat()936 void BC_BossCombat()
937 {
938 	static int t = 0;
939 	int move_x, move_y;
940 	float pdir;
941 	float pdir_x, pdir_y;
942 	int i, j;
943 	t++;
944 
945 	pdir = PlayerDir(boss_x, boss_y);
946 	pdir_x = cos(pdir);
947 	pdir_y = sin(pdir);
948 
949 	move_x = boss_x;
950 	move_y = boss_y;
951 	DrawBoss();
952 	switch (current_boss) {
953 		case 0: {
954 			static float boss_dir = 0.0;
955 			static float boss_dir_offset = 1.0;
956 
957 			move_x += cos(boss_dir) * (4 + (3 - boss_lives)/2);
958 			move_y += sin(boss_dir) * (4 + (3 - boss_lives)/2);
959 
960 			move_x += pdir_x * (2 + (3 - boss_lives)/2);
961 			move_y += pdir_y * (2 + (3 - boss_lives)/2);
962 
963 			boss_dir += (float)(rand() % 64)/256 * boss_dir_offset;
964 			if ((rand()%48) == 0) {
965 				boss_dir_offset *= -1.0;
966 			}
967 			while (!BossMovement(move_x, move_y)) {
968 				boss_dir += (float)(rand() % 64)/256 * boss_dir_offset;
969 				if ((rand()%48) == 0) {
970 					boss_dir_offset *= -1.0;
971 				}
972 				move_x = boss_x;
973 				move_y = boss_y;
974 
975 				move_x += cos(boss_dir) * (4 + (3 - boss_lives)/2);
976 				move_y += sin(boss_dir) * (4 + (3 - boss_lives)/2);
977 				move_x += pdir_x * (2 + (3 - boss_lives)/2);
978 				move_y += pdir_y * (2 + (3 - boss_lives)/2);
979 			}
980 			if (BossMovement(move_x, move_y)) {
981 				boss_x = move_x;
982 				boss_y = move_y;
983 			}
984 			if (boss_flash > 15) return;
985 
986 			switch (boss_lives) {
987 				case 3: {
988 					int laser_dmg;
989 					float laser_1_dir, laser_2_dir;
990 
991 					laser_dmg = (rand()%(player_shield/2+1))/2;
992 
993 					for (i = 0; i < player_shield / 5 + 1; i++) {
994 						SpawnBullet(boss_x + (rand() % 48)*pdir_x, boss_y + (rand() % 48)*pdir_y, 3, pdir + M_PI / 4, 2, 0);
995 						SpawnBullet(boss_x + (rand() % 48)*pdir_x, boss_y + (rand() % 48)*pdir_y, 0, pdir, 4.5, 0);
996 					}
997 					laser_1_dir = pdir + 0.3 + sin((float)t / 8)*0.2;
998 					laser_2_dir = pdir - 0.3 + sin((float)t / 8)*0.2;
999 					SpawnLaser(boss_x + pdir_x * 24, boss_y + pdir_y * 8, laser_1_dir, 0, 1, 0, laser_dmg);
1000 					SpawnLaser(boss_x + pdir_x * 24, boss_y + pdir_y * 8, laser_2_dir, 0, 1, 0, laser_dmg);
1001 					break;
1002 				}
1003 				case 2: {
1004 					int laser_dmg;
1005 					float laser_1_dir, laser_2_dir, laser_3_dir;
1006 
1007 					laser_dmg = (rand()%(player_shield/2+1))/2;
1008 					for (i = 0; i < player_shield / 3 + 1; i++) {
1009 						SpawnBullet(boss_x + (rand() % 48)*pdir_x, boss_y + (rand() % 48)*pdir_y, 3, RandomDir(), 3.5, 0);
1010 						SpawnBullet(boss_x + (rand() % 48)*pdir_x, boss_y + (rand() % 48)*pdir_y, 0, RandomDir(), 4.5, 0);
1011 					}
1012 					laser_1_dir = pdir + 0.4 + sin((float)t / 8)*0.2;
1013 					laser_2_dir = pdir - 0.4 + sin((float)t / 8)*0.2;
1014 					laser_3_dir = RandomDir();
1015 					SpawnLaser(boss_x + pdir_x * 24, boss_y + pdir_y * 8, laser_1_dir, 0, 1, 0, laser_dmg);
1016 					SpawnLaser(boss_x + pdir_x * 24, boss_y + pdir_y * 8, laser_2_dir, 0, 1, 0, laser_dmg);
1017 					if ((t % 12) == 0)
1018 						SpawnLaser(boss_x + pdir_x * 24, boss_y + pdir_y * 8, laser_3_dir, 24, 8, 0, laser_dmg * 4);
1019 					break;
1020 				}
1021 				case 1: {
1022 					int laser_dmg;
1023 					float laser_1_dir, laser_2_dir, laser_3_dir;
1024 
1025 					laser_dmg = (rand()%(player_shield/2+1))/2;
1026 					for (i = 0; i < player_shield / 4 + 1; i++) {
1027 						SpawnBullet(boss_x + (rand() % 48)*pdir_x, boss_y + (rand() % 48)*pdir_y, 3, RandomDir(), 3.5, 0);
1028 						SpawnBullet(boss_x + (rand() % 48)*pdir_x, boss_y + (rand() % 48)*pdir_y, 0, pdir, 5.0, 0);
1029 						SpawnBullet(boss_x + (rand() % 48)*pdir_x, boss_y + (rand() % 48)*pdir_y, 0, RandomDir(), 4.5, 0);
1030 					}
1031 					laser_1_dir = pdir + 0.4 + sin((float)t / 8)*0.3;
1032 					laser_2_dir = pdir - 0.4 + sin((float)t / 8)*0.3;
1033 					laser_3_dir = RandomDir();
1034 					SpawnLaser(boss_x + pdir_x * 24, boss_y + pdir_y * 8, laser_1_dir, 0, 1, 0, laser_dmg);
1035 					SpawnLaser(boss_x + pdir_x * 24, boss_y + pdir_y * 8, laser_2_dir, 0, 1, 0, laser_dmg);
1036 					if ((t % 5) == 0)
1037 						SpawnLaser(boss_x + pdir_x * 24, boss_y + pdir_y * 8, RandomDir(), 16, 16, 0, laser_dmg * 4);
1038 					if ((t % 10) == 0)
1039 						SpawnBullet(boss_x + (rand() % 48)*pdir_x, boss_y + (rand() % 48)*pdir_y, 5, RandomDir(), 5, 0);
1040 					break;
1041 				}
1042 			}
1043 			break;
1044 		}
1045 		case 1: {
1046 			float h_dist;
1047 			int i, j;
1048 			int hx[4], hy[4];
1049 			int heads;
1050 			int mx, my;
1051 			int check_pass;
1052 			float hd2;
1053 
1054 			heads = 5 - boss_lives;
1055 			h_dist = M_PI * 2 / heads;
1056 
1057 			// Heads
1058 
1059 			for (i = 0; i < heads; i++) {
1060 				hx[i] = boss_x + cos(boss_2h_dir + h_dist * i) * boss_2h_dst;
1061 				hy[i] = boss_y + sin(boss_2h_dir + h_dist * i) * boss_2h_dst;
1062 			}
1063 
1064 			move_x += -6 + rand() % 13;
1065 			move_y += -6 + rand() % 13;
1066 
1067 			move_x += pdir_x * (2 + (3 - boss_lives)/2);
1068 			move_y += pdir_y * (2 + (3 - boss_lives)/2);
1069 
1070 			// Check movement
1071 			check_pass = 1;
1072 			for (i = 0; i < heads; i++) {
1073 				mx = move_x + cos(boss_2h_dir + h_dist * i) * boss_2h_dst;
1074 				my = move_y + sin(boss_2h_dir + h_dist * i) * boss_2h_dst;
1075 
1076 				if (!BossMovement(mx, my))
1077 					check_pass = 0;
1078 			}
1079 
1080 			if (check_pass) {
1081 				boss_x = move_x;
1082 				boss_y = move_y;
1083 			}
1084 
1085 			if (boss_flash > 30) return;
1086 
1087 			// Main cannons
1088 			if ((t % (10 + boss_lives * 6)) == 1) {
1089 				i = (t / (10 + boss_lives * 6)) % heads;
1090 				for (j = 0; j < player_shield / 2 + 1; j++) {
1091 					SpawnBullet(hx[i] + (rand() % 48)*pdir_x, hy[i] + (rand() % 48)*pdir_y, 0, pdir, 7 + ((float)(rand()%16))/10.0, 0);
1092 				}
1093 				SpawnBullet(hx[i], hy[i], 4, pdir, 10, 0);
1094 			}
1095 
1096 			// Barrage launcher
1097 			for (i = 0; i < player_shield / 4 + heads; i++) {
1098 				SpawnBullet(boss_x, boss_y, 0, RandomDir(), 3 + (float)(rand()%16)/10.0, 0);
1099 			}
1100 
1101 			if (boss_lives == 2) {
1102 				// Splitters
1103 				if ((t % 20) == 1) {
1104 					SpawnBullet(boss_x, boss_y, 5, pdir, 5, 0);
1105 				}
1106 			}
1107 
1108 			// Central laser cannon
1109 
1110 			if ((t % (100 + boss_lives * 50)) == ((100 + boss_lives * 50)-1)) {
1111 				i = player_shield / 3 + 2;
1112 				hd2 = M_PI * 2.0 / (float)i;
1113 				for (j = 0; j < i; j++) {
1114 					SpawnLaser(boss_x + cos(hd2*j) * 20, boss_y + sin(hd2*j) * 20, pdir, 15 + rand()%16, 5, 0, (player_shield / 3 + 1));
1115 				}
1116 			}
1117 
1118 			// Star vomit
1119 
1120 			if (boss_lives == 2) {
1121 				for (i = 0; i < heads; i++) {
1122 					for (j = 0; j < player_shield / 6 + 1; j++) {
1123 						SpawnBullet(hx[i], hy[i], 3, RandomDir(), 6, 0);
1124 					}
1125 				}
1126 			}
1127 
1128 			// Fusion cannon
1129 
1130 			if (boss_lives <= 2) {
1131 				if ((t % (12 + (boss_lives - 1) * 30)) == 9) {
1132 					for (i = 0; i < heads; i++) {
1133 						SpawnLaser(hx[i], hy[i], pdir, 6, 6, 0, (player_shield / 6 + 1));
1134 					}
1135 				}
1136 			}
1137 			break;
1138 		}
1139 		case 2: {
1140 			int mx, my;
1141 			static int t2 = 0;
1142 			float md;
1143 			int trying;
1144 
1145 			boss_m_heads = boss_lives;
1146 
1147 			for (i = 0; i < boss_m_heads; i++) {
1148 				mx = boss_m_hx[i];
1149 				my = boss_m_hy[i];
1150 
1151 				boss_m_hd[i] = CHDir(boss_m_hd[i], PlayerDir(mx, my), 0.2);
1152 				md = M_PI * 2.0 / (float)boss_m_heads * i + (M_PI / 4);
1153 				boss_m_hd[i] = CHDir(boss_m_hd[i], md, 0.1);
1154 
1155 				md = boss_m_hd[i];
1156 
1157 				if ( (t % boss_m_heads) == i) {
1158 					mx += cos(md) * 4 + (boss_m_heads == 1) * 2;
1159 					my += sin(md) * 4 + (boss_m_heads == 1) * 2;
1160 				}
1161 
1162 
1163 				for (j = 0; j < boss_m_heads; j++) {
1164 					if (j != i) {
1165 						if (dist(boss_m_hx[j], boss_m_hy[j], boss_m_hx[i], boss_m_hy[i]) < 200) {
1166 							md = PDir(boss_m_hx[j], boss_m_hy[j], boss_m_hx[i], boss_m_hy[i]);
1167 							mx += cos(md) * 2;
1168 							my += sin(md) * 2;
1169 							boss_m_hd[i] = CHDir(boss_m_hd[i], md, 0.1);
1170 						}
1171 					}
1172 				}
1173 
1174 				trying = 10;
1175 
1176 				while (!BossMovement(mx, my)) {
1177 					trying--;
1178 					if (trying == 0) break;
1179 					md = PlayerDir(boss_m_hx[i], boss_m_hy[i]);
1180 					md = md - 0.3 + (float)(rand()%16) * 0.6;
1181 
1182 					mx = boss_m_hx[i] + cos(md) * 5;
1183 					my = boss_m_hy[i] + sin(md) * 5;
1184 
1185 					if (boss_m_heads == 1) {
1186 						boss_m_hd[i] = CHDir(boss_m_hd[i], PlayerDir(mx, my), 0.2);
1187 						md = boss_m_hd[i];
1188 						mx += cos(md) * 3;
1189 						my += sin(md) * 3;
1190 					}
1191 				}
1192 				if (trying > 0) {
1193 					boss_m_hx[i] = mx;
1194 					boss_m_hy[i] = my;
1195 				}
1196 			}
1197 
1198 			if (boss_flash > 30) return;
1199 
1200 			if (boss_m_heads > 1) {
1201 				if ((t % 2) == 0) {
1202 					i = t % boss_m_heads;
1203 					SpawnBullet(boss_m_hx[i] + cos(boss_m_hd[i])*8, boss_m_hy[i] + sin(boss_m_hd[i])*8, 0, boss_m_hd[i], 10, 0);
1204 				} else {
1205 					i = (t+2) % boss_m_heads;
1206 					md = PlayerDir(boss_m_hx[i], boss_m_hy[i]);
1207 					SpawnBullet(boss_m_hx[i] + cos(boss_m_hd[i])*8, boss_m_hy[i] + sin(boss_m_hd[i])*8, 0, md, 10, 0);
1208 				}
1209 			} else {
1210 				md = CHDir(RandomDir(), PlayerDir(boss_m_hx[0], boss_m_hy[0]), 1.5);
1211 				SpawnBullet(boss_m_hx[0] + cos(boss_m_hd[0])*8, boss_m_hy[0] + sin(boss_m_hd[0])*8, 0, md, 10, 0);
1212 			}
1213 			// Barrage
1214 
1215 			for (i = 0; i < 24; i++) {
1216 				if ((t2 % (6 - (player_shield / 6) + boss_m_heads)) == 0) {
1217 					md = RandomDir();
1218 					if (boss_m_heads > 2) {
1219 						SpawnBullet(boss_x + cos(md)*8, boss_y + sin(md)*8, 0, md, 5, 0);
1220 					} else {
1221 						if (boss_m_heads > 1) {
1222 							SpawnBullet(boss_x + cos(md)*24, boss_y + sin(md)*24, 4, md, 12, 0);
1223 						} else {
1224 							md = CHDir(md, pdir, 0.66);
1225 							SpawnBullet(boss_x + cos(md)*24, boss_y + sin(md)*24, 4, md, 12, 0);
1226 						}
1227 					}
1228 				}
1229 				t2++;
1230 			}
1231 
1232 			// Beams
1233 			if (boss_m_heads < 4) {
1234 				if ((t % 5) == 0) {
1235 					i = (t / 5)%boss_m_heads;
1236 					md = boss_m_hd[i];
1237 					SpawnLaser(boss_m_hx[i], boss_m_hy[i], md, 1, 10, 0.05, (player_shield / 7) + 3 - boss_m_heads);
1238 					md += M_PI / 2;
1239 					SpawnLaser(boss_m_hx[i], boss_m_hy[i], md, 1, 10, 0.05, (player_shield / 7) + 3 - boss_m_heads);
1240 					md += M_PI / 2;
1241 					SpawnLaser(boss_m_hx[i], boss_m_hy[i], md, 1, 10, 0.05, (player_shield / 7) + 3 - boss_m_heads);
1242 					md += M_PI / 2;
1243 					SpawnLaser(boss_m_hx[i], boss_m_hy[i], md, 1, 10, 0.05, (player_shield / 7) + 3 - boss_m_heads);
1244 				}
1245 			}
1246 
1247 			if (boss_m_heads == 1) {
1248 				if ( (t % 30) < player_shield) {
1249 					md = CHDir(RandomDir(), pdir, 1.0);
1250 					SpawnBullet(boss_m_hx[0] + cos(md)*24, boss_m_hy[0] + sin(md)*24, 3, md, 11, 0);
1251 				}
1252 			}
1253 			break;
1254 		}
1255 		case 3:
1256 		{
1257 			int boss_loop;
1258 			int boss_loop_total;
1259 			int mx, my;
1260 			int npattern;
1261 			int i;
1262 			float firedir;
1263 			float flp;
1264 			float cboss_dir;
1265 			int ls_x, ls_y;
1266 
1267 
1268 			mx = boss_x + cos(boss_dir)*(4 + (player_shield==30));
1269 			my = boss_y + sin(boss_dir)*(4 + (player_shield==30));
1270 
1271 			if (BossMovement(mx, my)) {
1272 				boss_x = mx;
1273 				boss_y = my;
1274 				boss_dir = CHDir(boss_dir, pdir, 0.3);
1275 			} else {
1276 				boss_dir = CHDir(boss_dir, RandomDir(), 1);
1277 			}
1278 
1279 			if ((boss_lives == 1) && (player_shield == 30)) {
1280 				{
1281 					int room_w, room_h;
1282 					int room_x, room_y;
1283 					float tmr_t;
1284 					float b_m_dir;
1285 
1286 					float boss_x_bias, boss_y_bias;
1287 
1288 					boss_x_bias = 1.33 - ((float)(boss_hp) / 1200.0);
1289 					boss_y_bias = 0.7 + ((float)(boss_hp) / 1500.0);
1290 
1291 					room_x = rooms[player_room].x * 32 + 64;
1292 					room_y = rooms[player_room].y * 32 + 64;
1293 					room_w = rooms[player_room].w * 32 - 128;
1294 					room_h = rooms[player_room].h * 32 - 128;
1295 
1296 					tmr_t = (float)t / 30.0;
1297 
1298 					mx = (int)((sin(tmr_t * boss_x_bias)*0.5+0.5) * (float)room_w) + room_x;
1299 					my = (int)((cos(tmr_t * boss_y_bias)*0.5+0.5) * (float)room_h) + room_y;
1300 
1301 					if ( dist(mx, my, boss_x, boss_y) < 24) {
1302 						boss_x = mx;
1303 						boss_y = my;
1304 					} else {
1305 						b_m_dir = PDir(boss_x, boss_y, mx, my);
1306 						boss_x += cos(b_m_dir)*24;
1307 						boss_y += sin(b_m_dir)*24;
1308 					}
1309 				}
1310 				if ((t % 50) < 49) {
1311 					proxy_seek = 0;
1312 					for (i = 0; i < 20; i++) {
1313 						firedir = RandomDir();
1314 						SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 7, firedir, 1, 0);
1315 					}
1316 				} else {
1317 					proxy_seek = 1;
1318 				}
1319 				if ( ((t / 50) % 4) == 3) {
1320 					proxy_seek = 1;
1321 				}
1322 
1323 				for (i = 0; i < 24; i++) {
1324 					firedir = M_PI / 15 * (float)i + (float)t / 33.0 * M_PI;
1325 					SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 0, firedir, 10, 0);
1326 				}
1327 
1328 				break;
1329 			}
1330 
1331 			boss_loop_total = 1 + (player_shield == 30)*2;
1332 
1333 			cboss_dir = boss_dir;
1334 
1335 			for (boss_loop = 0; boss_loop < boss_loop_total; boss_loop++) {
1336 				if (player_shield == 30) {
1337 					npattern = (3 - boss_lives) * 3 + 2 - (boss_hp / 334);
1338 				} else {
1339 					npattern = (2 - boss_lives) * 3 + 2 - (boss_hp / 334);
1340 				}
1341 
1342 				switch (npattern) {
1343 					case 0:
1344 						// Spirally pattern
1345 						for (i = 0; i < 4; i++) {
1346 							firedir = M_PI / 2 * (float)i + (float)t / 33.0 * M_PI;
1347 							SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 0, firedir, 5, 0);
1348 						}
1349 						for (i = 0; i < 4; i++) {
1350 							firedir = M_PI / 2 * (float)i + (float)t / 33.0 * M_PI * 2.0;
1351 							SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 0, firedir, 8, 0);
1352 						}
1353 						for (i = 0; i < 4; i++) {
1354 							firedir = RandomDir();
1355 							SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 0, firedir, 12, 0);
1356 						}
1357 					break;
1358 					case 1:
1359 						// Proxies
1360 						if ((t % 100) < 80) {
1361 							for (i = 0; i < 6; i++) {
1362 								firedir = RandomDir();
1363 								SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 7, firedir, 1, 0);
1364 							}
1365 						} else {
1366 							if ((t % 100) == 88) {
1367 								proxy_seek = 1;
1368 							}
1369 							if ((t % 100) == 92) {
1370 								proxy_seek = 0;
1371 							}
1372 						}
1373 						for (i = 0; i < 8; i++) {
1374 							firedir = M_PI / 4 * (float)i + (float)t / 33.0 * M_PI;
1375 							SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 0, firedir, 10, 0);
1376 						}
1377 					break;
1378 					case 2:
1379 						// Laserwalls
1380 						if ((t % 4) == 2) {
1381 							firedir = cboss_dir + (float)t / 33.0 * M_PI;
1382 							SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 8, firedir, 10, 1);
1383 							SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 8, firedir, 5, 1);
1384 						}
1385 
1386 						for (i = 0; i < 4; i++) {
1387 							firedir = cboss_dir + M_PI / 6 * (float)i + (float)t / 33.0 * M_PI;
1388 							SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 0, firedir, 5, 0);
1389 						}
1390 					break;
1391 					case 3:
1392 						if ((t % 30) == 29) {
1393 							firedir = RandomDir();
1394 							SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 8, firedir, 10, 1);
1395 							SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 8, firedir, 9.99, 1);
1396 							SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 8, firedir, 9.98, 1);
1397 							SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 8, firedir, 9.97, 1);
1398 							SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 8, firedir, 9.96, 1);
1399 							SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 8, firedir, 9.95, 1);
1400 							SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 8, firedir, 9.94, 1);
1401 						}
1402 						if ((t % 40) == 39) {
1403 							firedir = RandomDir();
1404 							SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 1, firedir, 6, 1);
1405 							SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 5, firedir+1, 6, 1);
1406 							SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 6, firedir+1, 6, 1);
1407 						}
1408 						if ((t % 50) == 49) {
1409 							proxy_seek = 0;
1410 							firedir = RandomDir();
1411 							for (i = 0; i < 60; i++) {
1412 								SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 7, firedir, 1, 1);
1413 								firedir += M_PI * 2.0 * (float)firedir / 60.0;
1414 							}
1415 						}
1416 						if ((t % 50) == 45) {
1417 							proxy_seek = 1;
1418 						}
1419 
1420 						if ((t % 10) == 2) {
1421 							firedir = cboss_dir + (float)t / 33.0 * M_PI;
1422 							SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 8, firedir, 10, 1);
1423 							SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 8, firedir, 7.5, 1);
1424 							SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 8, firedir, 5, 1);
1425 						}
1426 					break;
1427 					case 4:
1428 						if ((t % 80) == 79) {
1429 							firedir = RandomDir();
1430 							SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 8, firedir, 10, 1);
1431 							SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 8, firedir, 9.98, 1);
1432 							SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 8, firedir, 9.96, 1);
1433 							SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 8, firedir, 9.94, 1);
1434 							SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 8, firedir, 9.92, 1);
1435 						}
1436 						if ((t % 90) == 89) {
1437 							firedir = RandomDir();
1438 							SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 1, firedir, 6, 1);
1439 							SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 5, firedir+1, 6, 1);
1440 							SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 6, firedir+1, 6, 1);
1441 						}
1442 						if ((t % 100) == 99) {
1443 							proxy_seek = 0;
1444 							firedir = RandomDir();
1445 							for (i = 0; i < 60; i++) {
1446 								SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 7, firedir, 1, 1);
1447 								SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 7, firedir, 2, 1);
1448 								SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 7, firedir, 3, 1);
1449 								SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 7, firedir, 4, 1);
1450 								firedir += M_PI * 2.0 * (float)firedir / 60.0;
1451 							}
1452 						}
1453 						if ((t % 100) == 95) {
1454 							proxy_seek = 1;
1455 						}
1456 
1457 						flp = (float)(t % 50) / 49.0;
1458 
1459 						if ((t % 10) == 3) {
1460 							i = (t / 10) % 4;
1461 							switch (i) {
1462 								case 0:
1463 									ls_x = 7904 + flp * 576;
1464 									ls_y = 8000;
1465 									break;
1466 								case 1:
1467 									ls_x = 8480 - flp * 576;
1468 									ls_y = 8416;
1469 									break;
1470 								case 2:
1471 									ls_x = 7904;
1472 									ls_y = 8000 + flp * 416;
1473 									break;
1474 								case 3:
1475 								default:
1476 									ls_x = 8480;
1477 									ls_y = 8416 - flp * 416;
1478 									break;
1479 							}
1480 							firedir = PlayerDir(ls_x, ls_y) - 0.1 + ((float)(rand()%16))/8.0;
1481 							SpawnLaser(ls_x, ls_y, firedir, 8, 2, 0, player_shield/2+1);
1482 						}
1483 
1484 						if ((t % 6) == 2) {
1485 							firedir = cboss_dir + (float)t / 33.0 * M_PI;
1486 							SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 8, firedir, 10, 1);
1487 							SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 8, firedir, 5, 1);
1488 						}
1489 					break;
1490 					case 5:
1491 						flp = (float)(t % 50) / 49.0;
1492 
1493 						if ((t % 8) == 7) {
1494 							i = (t / 8) % 4;
1495 							switch (i) {
1496 								case 0:
1497 									ls_x = 7904 + flp * 576;
1498 									ls_y = 8000;
1499 									break;
1500 								case 1:
1501 									ls_x = 8480 - flp * 576;
1502 									ls_y = 8416;
1503 									break;
1504 								case 2:
1505 									ls_x = 7904;
1506 									ls_y = 8000 + flp * 416;
1507 									break;
1508 								case 3:
1509 								default:
1510 									ls_x = 8480;
1511 									ls_y = 8416 - flp * 416;
1512 									break;
1513 							}
1514 							firedir = PlayerDir(ls_x, ls_y) - 0.1 + ((float)(rand()%16))/8.0;
1515 							SpawnLaser(ls_x, ls_y, firedir - 0.03*6, 5, 5, 0.03, player_shield/3+1);
1516 						}
1517 
1518 						for (i = 0; i < 7; i++) {
1519 							firedir = cboss_dir + M_PI/2 * (float)i + (float)t / 33.0 * M_PI;
1520 							SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 0, firedir, 8, 0);
1521 						}
1522 						for (i = 0; i < 7; i++) {
1523 							firedir = cboss_dir + M_PI/4 + M_PI/2 * (float)i + (float)t / 33.0 * M_PI;
1524 							SpawnBullet(boss_x + cos(firedir)*12, boss_y + sin(firedir)*12, 3, firedir, 8, 0);
1525 						}
1526 					break;
1527 				}
1528 
1529 				cboss_dir += -0.1 + ((float)(rand()%16))/8.0;
1530 			}
1531 		break;
1532 		}
1533 	}
1534 }
1535 
BC_BossDying()1536 void BC_BossDying()
1537 {
1538 	int x, y, rx, ry, rt;
1539 	static int t_timer = 0;
1540 	static int bxp, bdef;
1541 	int i;
1542 	static SDL_Surface *endpics[1] = {NULL};
1543 	static float dr = 0;
1544 
1545 	if (current_boss < 3) {
1546 		specialmessage = 40 + rooms[player_room].room_param;
1547 		specialmessagetimer = 120;
1548 		rooms[player_room].room_type = 4;
1549 		boss_fight_mode = 0;
1550 		current_boss += 1;
1551 		artifacts[8 + rooms[player_room].room_param] = 0;
1552 
1553 		CullEnemies(4);
1554 
1555 		// unlock doors
1556 
1557 		for (y = 0; y < rooms[player_room].h; y++) {
1558 			for (x = 0; x < rooms[player_room].w; x++) {
1559 				rx = x + rooms[player_room].x;
1560 				ry = y + rooms[player_room].y;
1561 				rt = Get(rx, ry);
1562 
1563 				if ((rt >= 21) && (rt <= 24)) {
1564 					Put(rx, ry, rt - 21 + 13, player_room);
1565 
1566 					if (rt == 21) {
1567 						Put(rx, ry+1, 14, GetRoom(rx, ry+1));
1568 					}
1569 					if (rt == 22) {
1570 						Put(rx, ry-1, 13, GetRoom(rx, ry-1));
1571 					}
1572 					if (rt == 23) {
1573 						Put(rx+1, ry, 16, GetRoom(rx+1, ry));
1574 					}
1575 					if (rt == 24) {
1576 						Put(rx-1, ry, 15, GetRoom(rx-1, ry));
1577 					}
1578 				}
1579 			}
1580 		}
1581 
1582 		if (current_boss > 0) {
1583 			SoupUpEnemies();
1584 		}
1585 	} else {
1586 		if (boss_fight_mode == 3) {
1587 			t_timer = 0;
1588 			boss_fight_mode = 4;
1589 			magic_circuit = 0;
1590 			bxp = 4;
1591 			bdef = 256;
1592 			dr = RandomDir();
1593 
1594 			if (training) t_timer = 2000;
1595 		} else {
1596 			DrawArtifactOverhead(3);
1597 			if (bdef > 0) {
1598 				DrawCircleEx(boss_x-scroll_x, boss_y-scroll_y, bxp, bxp-bdef, 0);
1599 				bxp += 4;
1600 				bdef -= 2;
1601 			} else {
1602 				if (t_timer < 220) {
1603 					draw_text(244, 100, "*** Divine Seal ***", 1);
1604 					draw_text(244, 380, "*** Divine Seal ***", 1);
1605 					magic_circuit = circuit_size * 0.75 * t_timer / 220;
1606 					for (i = 0; i < 10; i++) {
1607 						rt = (rand() % 350) + 50;
1608 						DrawCircle(player_x + PLAYERW/2 - scroll_x + cos(dr)*rt, player_y - scroll_y + PLAYERH/2
1609 						+ sin(dr)*rt, rand()%30+5, rand()%128+128);
1610 					}
1611 
1612 					dr -= 0.025;
1613 					for (i = 0; i < 5; i++) {
1614 						Arc(screen, player_x + PLAYERW/2 - scroll_x, player_y + PLAYERH/2 - scroll_y, 450, dr);
1615 						dr += 0.01;
1616 					}
1617 					dr += 0.03;
1618 
1619 					if ((t_timer % 30) == 29) {
1620 						dr = RandomDir();
1621 					}
1622 
1623 					DrawCircle(player_x - scroll_x + PLAYERW/2, player_y - scroll_y + PLAYERH/2, (t_timer % 30) * 15, 255);
1624 					DrawCircle(player_x - scroll_x + PLAYERW/2, player_y - scroll_y + PLAYERH/2, ( (t_timer + 15) % 30) * 15, 255);
1625 				} else {
1626 					magic_circuit = 0;
1627 					if (boss_fight_mode < 23) {
1628 						if ( (t_timer % 4)==3) {
1629 							boss_fight_mode++;
1630 						}
1631 					} else {
1632 						if (endpics[0] == NULL) {
1633 							if (training) {
1634 								endpics[0] = IMG_Load("/usr/local/share/meritous/i/wuss_ending.png");
1635 							}
1636 						}
1637 
1638 						if (training) {
1639 							SDL_BlitSurface(endpics[0], NULL, screen, NULL);
1640 						} else {
1641 							show_ending = 1;
1642 						}
1643 					}
1644 				}
1645 
1646 				if (t_timer == 200) {
1647 					CullEnemies(1);
1648 				}
1649 
1650 				t_timer++;
1651 			}
1652 		}
1653 	}
1654 }
1655 
BC_NewLife()1656 void BC_NewLife()
1657 {
1658 	static int circle_size = 0;
1659 	static int circle_size2 = 128;
1660 
1661 	if (boss_flash > 0) {
1662 		DrawBoss();
1663 		return;
1664 	}
1665 	if (boss_new_life == 1) {
1666 		circle_size = 0;
1667 		circle_size2 = 128;
1668 		boss_ox = rooms[current_boss_room].w * 16 + rooms[current_boss_room].x * 32;
1669 		boss_oy = rooms[current_boss_room].h * 16 + rooms[current_boss_room].y * 32;
1670 		boss_new_life = 2;
1671 	} else {
1672 		if (circle_size < 128) {
1673 			circle_size += 4;
1674 			DrawCircleEx(boss_x - scroll_x, boss_y - scroll_y, circle_size, 0, 0);
1675 			DrawCircle(boss_x - scroll_x, boss_y - scroll_y, circle_size, 96);
1676 			DrawBoss();
1677 
1678 			if (boss_lives > 1) {
1679 				DrawCircleEx(boss_ox - scroll_x, boss_oy - scroll_y, circle_size, 0, 0);
1680 				DrawCircle(boss_ox - scroll_x, boss_oy - scroll_y, circle_size, 96);
1681 			}
1682 		} else {
1683 			if (circle_size2 == 128) {
1684 				boss_ox = boss_x;
1685 				boss_oy = boss_y;
1686 				boss_x = rooms[current_boss_room].w * 16 + rooms[current_boss_room].x * 32;
1687 				boss_y = rooms[current_boss_room].h * 16 + rooms[current_boss_room].y * 32;
1688 				boss_tail_len = 0;
1689 				boss_lives--;
1690 			}
1691 			if (circle_size2 > 4) {
1692 				circle_size2 -= 4;
1693 				if (boss_lives > 0) {
1694 					DrawCircleEx(boss_x - scroll_x, boss_y - scroll_y, circle_size2, 0, 0);
1695 					DrawCircle(boss_x - scroll_x, boss_y - scroll_y, circle_size2, 96);
1696 					DrawBoss();
1697 				}
1698 
1699 				DrawCircleEx(boss_ox - scroll_x, boss_oy - scroll_y, circle_size2, 0, 0);
1700 				DrawCircle(boss_ox - scroll_x, boss_oy - scroll_y, circle_size2, 96);
1701 			} else {
1702 				if (boss_lives > 0) {
1703 					boss_new_life = 0;
1704 					boss_hp = 1000;
1705 					if ( (boss_lives == 1) && (current_boss == 3) && (player_shield == 30) ) {
1706 						player_hp = 6;
1707 						boss_dmgrate = 0.2;
1708 					}
1709 				} else {
1710 					boss_new_life = 0;
1711 					boss_fight_mode = 3;
1712 				}
1713 			}
1714 		}
1715 	}
1716 }
1717 
BossControl()1718 void BossControl()
1719 {
1720 	if ((player_room != current_boss_room)) {
1721 		// Player left, so roll back the boss
1722 		resetboss = 0;
1723 		current_boss_room = 0;
1724 		boss_tail_len = 0;
1725 		boss_fight_mode = 0;
1726 		boss_new_life = 0;
1727 	}
1728 
1729 	if (boss_fight_mode == 1) {
1730 		BC_BossIntro();
1731 		return;
1732 	}
1733 	if (boss_fight_mode == 2) {
1734 		if (boss_new_life > 0) {
1735 			BC_NewLife();
1736 		} else {
1737 			BC_BossCombat();
1738 		}
1739 		return;
1740 	}
1741 	if (boss_fight_mode >= 3) {
1742 		BC_BossDying();
1743 		return;
1744 	}
1745 }
1746 
DrawArtifactOverhead(int p_obj)1747 void DrawArtifactOverhead(int p_obj)
1748 {
1749 	int p_x, p_y;
1750 	static int tick = 0;
1751 
1752 	SDL_Rect from, to;
1753 
1754 	p_x = player_x - 8;
1755 	p_y = player_y - 36 + sin((float)tick / 20.0)*4;
1756 
1757 	from.x = (8 + p_obj) * 32;
1758 	from.y = 0;
1759 	from.w = 32;
1760 	from.h = 32;
1761 
1762 	to.x = p_x - scroll_x;
1763 	to.y = p_y - scroll_y;
1764 	SDL_BlitSurface(artifact_spr, &from, screen, &to);
1765 
1766 
1767 	tick++;
1768 }
1769 
CanGetArtifact()1770 int CanGetArtifact()
1771 {
1772 	int required_enemies;
1773 	int n_artifacts;
1774 	n_artifacts = current_boss + artifacts[8] + artifacts[9] + artifacts[10];
1775 	required_enemies = total_enemies * (percent_required[n_artifacts]) / 100;
1776 
1777 	if (killed_enemies >= required_enemies) return 1;
1778 	return 0;
1779 }
1780 
BossRoom(int room)1781 void BossRoom(int room)
1782 {
1783 	int i;
1784 
1785 	boss_fight_mode = 1;
1786 	current_boss_room = room;
1787 	boss_x = rooms[room].w * 16 + rooms[room].x * 32;
1788 	boss_y = rooms[room].h * 16 + rooms[room].y * 32;
1789 	boss_hp = 1000;
1790 	boss_flash = 0;
1791 	magic_circuit = 0;
1792 	boss_dlg = 0;
1793 	boss_new_life = 0;
1794 
1795 	switch (current_boss) {
1796 		case 0:
1797 			boss_lives = 3;
1798 			boss_breakpoint = 75;
1799 			boss_dmgrate = 1.00;
1800 		break;
1801 		case 1:
1802 			boss_lives = 3;
1803 			boss_breakpoint = 75;
1804 			boss_dmgrate = 0.80;
1805 		break;
1806 		case 2:
1807 			boss_lives = 4;
1808 			boss_breakpoint = 90;
1809 			boss_dmgrate = 1.25;
1810 			boss_m_heads = 4;
1811 			for (i = 0; i < boss_m_heads; i++) {
1812 				boss_m_hd[i] = M_PI / 2 * i + (M_PI / 4);
1813 				boss_m_hx[i] = boss_x + cos(boss_m_hd[i]) * 128;
1814 				boss_m_hy[i] = boss_y + sin(boss_m_hd[i]) * 128;
1815 			}
1816 		break;
1817 		case 3:
1818 			boss_lives = 2 + (player_shield == 30);
1819 			boss_breakpoint = 120;
1820 			boss_dmgrate = 0.40 + 0.10*(player_shield == 30);
1821 			boss_dir = M_PI * 3.0 / 2.0;
1822 		break;
1823 	}
1824 
1825 	if (training) {
1826 		boss_dmgrate *= 1.2;
1827 		boss_breakpoint *= 0.8;
1828 	}
1829 }
1830