Also allow to open help file with F1
[meritous_recharged.git] / src / ending.c
blob591cc9ef389e180a0055603bde6dab6649125bf2
1 //
2 // ending.c
3 //
4 // Copyright 2007, 2008 Lancer-X/ASCEAI
5 //
6 // This file is part of Meritous Recharged.
7 //
8 // Meritous Recharged 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.
13 // Meritous Recharged 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.
18 // You should have received a copy of the GNU General Public License
19 // along with Meritous Recharged. If not, see <http://www.gnu.org/licenses/>.
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <math.h>
25 #include <SDL.h>
26 #include <SDL_image.h>
27 #include <assert.h>
29 #include "levelblit.h"
30 #include "audio.h"
31 #include "boss.h"
32 #include "mapgen.h"
34 void DrawScrolly(int t);
35 void DrawPText(int t);
36 void DrawSText(int t);
37 void DrawSTextV(int t);
38 void DrawCircuitFlash(int t, int method);
39 void DrawStream(int t);
41 void InitParticleStorm();
42 void RunParticleStorm(int offset);
44 SDL_Surface *streamspr = NULL, *glitter = NULL;
46 SDL_Color ending_pal[256];
48 void UpdatePalette()
50 SDL_SetPalette(screen, SDL_PHYSPAL, ending_pal, 0, 256);
53 void DrawCredits();
55 int credits_scroll = 0;
57 int EndingEvents()
59 static SDL_Event event;
61 player_room = 0;
62 current_boss = 3;
63 boss_fight_mode = 4;
65 MusicUpdate();
67 while (SDL_PollEvent(&event)) {
68 if (event.type == SDL_KEYDOWN) {
69 switch (event.key.keysym.sym) {
70 case SDLK_ESCAPE:
71 return 1;
72 case SDLK_RETURN:
73 return 2;
74 default:
75 break;
78 if (event.type == SDL_JOYBUTTONDOWN) {
79 switch (event.jbutton.button) {
80 case 6:
81 return 1;
82 case 1:
83 return 2;
84 default:
85 break;
88 if (event.type == SDL_QUIT) {
89 return 1;
93 return 0;
96 void ShowEnding()
98 int i, event_type;
100 if (streamspr == NULL) {
101 streamspr = IMG_Load("dat/i/stream.png");
102 SDL_SetColorKey(streamspr, SDL_SRCCOLORKEY | SDL_RLEACCEL, 0);
103 glitter = IMG_Load("dat/i/glitter.png");
104 SDL_SetColorKey(glitter, SDL_SRCCOLORKEY | SDL_RLEACCEL, 0);
107 for (i = 0; i < 500; i += 1) {
108 if (((i % 60) >= 24)&&((i % 60) < 34)) {
109 DrawCircuitFlash((i % 60) - 24, 0);
110 } else {
111 DrawScrolly(i);
113 EndCycle(0);
114 if (EndingEvents()==1) return;
116 for (i = 0; i < 30; i++) {
117 DrawCircuitFlash(i, 1);
119 EndCycle(0);
120 if (EndingEvents()==1) return;
122 SDL_FillRect(screen, NULL, 255);
124 i=0;
125 while (1) {
126 i++;
127 if(i>=300) i=265;
128 DrawPText(i);
129 EndCycle(0);
130 event_type = EndingEvents();
131 if (event_type == 1) return;
132 if (event_type == 2) break;
134 for (i=300; i<350; i++) {
135 DrawPText(i);
136 EndCycle(0);
137 if (EndingEvents()) return;
140 Paint(0, 0, 22, 27, "dat/d/cstream.loc");
142 if (player_shield < 30) {
143 for (i = 0; i < 400; i++) {
144 DrawStream(i);
145 EndCycle(0);
146 if (EndingEvents()==1) return;
148 InitParticleStorm();
149 for (i = 0; i < 240; i++) {
150 RunParticleStorm(240-i);
151 EndCycle(0);
152 if (EndingEvents()==1) return;
154 for (i = 0; i < 60; i++) {
155 RunParticleStorm(0);
156 EndCycle(0);
157 if (EndingEvents()==1) return;
159 for (i = 0; i < 180; i++) {
160 RunParticleStorm(i*3);
161 EndCycle(0);
162 if (EndingEvents()==1) return;
164 i=0;
165 while (1) {
166 i++;
167 if(i>=400) i=350;
168 DrawSText(i);
169 EndCycle(0);
170 event_type = EndingEvents();
171 if (event_type == 1) return;
172 if (event_type == 2) break;
174 for (i=400; i<500; i++) {
175 DrawSText(i);
176 EndCycle(0);
177 if (EndingEvents()==1) return;
179 } else {
180 for (i = 0; i < 250; i++) {
181 DrawStream(i);
182 EndCycle(0);
183 if (EndingEvents()==1) return;
185 i=0;
186 while (1) {
187 i++;
188 if (i>=400) i=350;
189 DrawSTextV(i);
190 EndCycle(0);
191 event_type = EndingEvents();
192 if (event_type == 1) return;
193 if (event_type == 2) break;
195 for (i=400; i<500; i++) {
196 DrawSTextV(i);
197 EndCycle(0);
198 if (EndingEvents()==1) return;
202 credits_scroll = 0;
203 for (;;) {
204 DrawCredits();
205 EndCycle(0);
206 if (EndingEvents()==1) return;
210 char *SText[15] = { "Merit released the locks on the psi flowing through the Dome,",
211 "releasing the flow of psi into the atmosphere.",
213 "The Orcus Dome was originally built to centralise the limited",
214 "psi available to everyone. However, this made the existing",
215 "reserves more vulnerable to malicious psi users",
217 "While other psi users initially resented Merit for his rash",
218 "behaviour, they eventually adjusted to the decentralisation.",
220 "Eventually, psi users grew so adept at manipulating the",
221 "diluted flows of psi that they were capable of the same things",
222 "as before. Each psi user would keep their own individual",
223 "reserves of psi for when they needed to weild greater power,",
224 "and the balance of power was restored." };
226 char *STextV[15] = {"Merit decided to assume the role of custodian over the Orcus",
227 "Dome, in Wervyn Anixil's place. He resumed the experiments on",
228 "psi and found ways of making the Dome's remaining supply go as",
229 "far as it could.",
231 "Other psi users were suspicious of Merit, just as they were",
232 "wary of Wervyn Anixil before him, but they soon adjusted.",
234 "The balance of power was quickly restored, and stabilised for",
235 "eternity due to the work of Wervyn Anixil and now Merit.",
238 " [[ BEST ENDING ]]",
240 ""};
242 char* enterstr = "Press [Enter]";
244 void DrawSText(int t)
246 int offset;
247 if (t<245) {
248 offset = 540 + (t / 2);
249 } else {
250 offset = 663;
252 int i, c;
253 t = t * 350 / 500;
254 int cl;
255 if (t<245) {
256 cl = 350 - t;
257 } else {
258 cl = 105;
261 for (i = 0; i < 64; i++) {
262 DrawRect(0, i * 15 - offset, 640, 15, (64 - i) * cl / 350);
264 for (i = 64; i < 128; i++) {
265 DrawRect(0, i * 15 - offset, 640, 15, (i - 64) * cl / 350);
268 if (t < 300) {
269 for (i = 0; i < 15; i++) {
270 c = (255 + (i * 100) - t*10);
271 if (c < 0) c = 0;
272 if (c > 255) c = 255;
274 draw_text(68, 150+i*12, SText[i], 255-c);
276 } else {
277 for (i = 0; i < 15; i++) {
278 c = 5 + (t-300) * 5;
280 draw_text(68, 150+i*12, SText[i], 255-c);
285 if (t >= 245 && t < 280) {
286 if (t%35>17) {
287 draw_text(320 - (strlen(enterstr)*8)/2, 386, enterstr, 255);
291 UpdatePalette();
292 VideoUpdate();
294 void DrawSTextV(int t)
296 int offset;
297 if (t<245) {
298 offset = 540 + (t / 2);
299 } else {
300 offset = 663;
302 int i, c;
303 t = t * 350 / 500;
304 int cl;
305 if (t<245) {
306 cl = 350 - t;
307 } else {
308 cl = 105;
311 for (i = 0; i < 64; i++) {
312 DrawRect(0, i * 15 - offset, 640, 15, (64 - i) * cl / 350);
314 for (i = 64; i < 128; i++) {
315 DrawRect(0, i * 15 - offset, 640, 15, (i - 64) * cl / 350);
318 if (t < 300) {
319 for (i = 0; i < 15; i++) {
320 c = (255 + (i * 100) - t*10);
321 if (c < 0) c = 0;
322 if (c > 255) c = 255;
324 draw_text(68, 150+i*12, STextV[i], 255-c);
326 } else {
327 for (i = 0; i < 15; i++) {
328 c = 5 + (t-300) * 5;
330 draw_text(68, 150+i*12, STextV[i], 255-c);
334 if (t >= 245 && t < 280) {
335 if (t%35>17) {
336 draw_text(320 - (strlen(enterstr)*8)/2, 386, enterstr, 255);
340 UpdatePalette();
341 VideoUpdate();
345 float pt_x[500];
346 float pt_y[500];
347 float pt_vx[500];
348 float pt_vy[500];
349 int pt_t[500];
351 void InitParticleStorm()
353 int i;
355 for (i = 0; i < 500; i++) {
356 pt_x[i] = 320;
357 pt_y[i] = 960;
358 pt_vx[i] = (float)(rand()%101) / 33.333 - 1.5;
359 pt_vy[i] = (float)(rand()%101) / 10.0 - 16.1;
360 pt_t[i] = rand()%100;
364 char *credits[] = {
365 "Concept: Lancer-X/Asceai",
366 "Game design: Lancer-X/Asceai",
367 "Graphics: Lancer-X/Asceai",
368 "Programming (until v1.2): Lancer-X/Asceai",
369 "Programming (since v1.3): Wuzzy",
370 "Sound Effects: Various (public domain) sources",
371 "Music: Various artists",
372 "Beta testing: Quasar",
373 "Beta testing: Terryn",
374 "Beta testing: Wervyn",
375 "\"Ambient Light\" Vogue of Triton",
376 "\"Battle of Ragnarok\" Frostbite",
377 "\"Dragon Cave\" TICAZ",
378 " cavern.xm Unknown",
379 "\"Caverns Boss\" Alexis Janson",
380 "\"Forest Boss\" Alexis Janson",
381 "\"Catacombs Boss\" Alexis Janson",
382 "\"Fear 2\" Mick Rippon",
383 "\"The Final Battle\" Goose/CéDA & iNVASiON",
384 "\"Ice Frontier\" Skaven/FC",
385 "\"KnarkLoader 1.0\" Rapacious",
386 "\"RPG-Battle\" Cyn",
387 "\"Metallic Forest\" Joseph Fox"
390 void DrawCredits()
392 static SDL_Surface *fin = NULL;
393 static SDL_Surface *theend[2] = {NULL};
394 SDL_Rect draw_to;
395 int i;
396 int ypos;
397 int c;
398 int n_credits = sizeof(credits)/sizeof(*credits);
399 int finish_point;
401 finish_point = 400 + (n_credits * 50);
403 draw_to.x = 384;
404 draw_to.y = 352;
406 SDL_FillRect(screen, NULL, 0);
408 if (fin == NULL) {
409 fin = IMG_Load("dat/i/fin.png");
411 theend[0] = IMG_Load("dat/i/theend.png");
412 theend[1] = IMG_Load("dat/i/true_end.png");
415 if (credits_scroll >= (finish_point + 80)) {
416 SDL_BlitSurface(theend[(player_shield == 30)], NULL, screen, NULL);
417 } else {
418 SDL_BlitSurface(fin, NULL, screen, &draw_to);
420 // Show each line of credits
422 for (i = 0; i < n_credits; i++) {
423 ypos = 800 + (i * 100) - credits_scroll * 2;
425 if ((ypos >= 0)&&(ypos < 480)) {
426 c = 255 - abs(ypos - 240);
427 draw_text(120, ypos, credits[i], c);
433 for (i = 0; i < 128; i++) {
434 ending_pal[i].r = 0;
435 ending_pal[i].g = i;
436 ending_pal[i].b = i*2;
438 for (i = 128; i < 256; i++) {
439 ending_pal[i].r = (i - 128)*2+1;
440 ending_pal[i].g = i;
441 ending_pal[i].b = 255;
444 // Dim palette if we're just starting
446 if (credits_scroll < 80) {
447 for (i = 0; i < 256; i++) {
448 ending_pal[i].r = ending_pal[i].r * credits_scroll / 80;
449 ending_pal[i].g = ending_pal[i].g * credits_scroll / 80;
450 ending_pal[i].b = ending_pal[i].b * credits_scroll / 80;
454 // Also palette if we're finishing
456 if ((credits_scroll >= (finish_point))&&(credits_scroll < (finish_point + 80))) {
457 for (i = 0; i < 256; i++) {
458 ending_pal[i].r = ending_pal[i].r * (finish_point+80-credits_scroll) / 80;
459 ending_pal[i].g = ending_pal[i].g * (finish_point+80-credits_scroll) / 80;
460 ending_pal[i].b = ending_pal[i].b * (finish_point+80-credits_scroll) / 80;
464 if ((credits_scroll >= (finish_point + 80))&&(credits_scroll < (finish_point + 160))) {
465 for (i = 0; i < 256; i++) {
466 ending_pal[i].r = ending_pal[i].r * (credits_scroll - (finish_point + 80)) / 80;
467 ending_pal[i].g = ending_pal[i].g * (credits_scroll - (finish_point + 80)) / 80;
468 ending_pal[i].b = ending_pal[i].b * (credits_scroll - (finish_point + 80)) / 80;
472 credits_scroll++;
474 UpdatePalette();
475 VideoUpdate();
478 void RunParticleStorm(int offset)
480 SDL_Rect draw_from, draw_to;
481 int i;
483 for (i = 0; i < 64; i++) {
484 DrawRect(0, i * 15 - offset, 640, 15, 64 - i);
487 for (i = 0; i < 500; i++) {
488 if (pt_t[i] > 0) {
489 pt_t[i]--;
490 } else {
491 pt_vy[i] += 0.1;
492 pt_x[i] += pt_vx[i];
493 pt_y[i] += pt_vy[i];
496 draw_from.x = (rand()%3)*32;
497 draw_from.y = 0;
498 draw_from.w = 32;
499 draw_from.h = 32;
501 draw_to.x = (int)pt_x[i] - 16;
502 draw_to.y = (int)pt_y[i] - 16 - offset;
503 SDL_BlitSurface(glitter, &draw_from, screen, &draw_to);
506 for (i = 0; i < 128; i++) {
507 ending_pal[i].r = i*2;
508 ending_pal[i].g = i*2;
509 ending_pal[i].b = 0;
511 for (i = 128; i < 256; i++) {
512 ending_pal[i].r = 255;
513 ending_pal[i].g = 255;
514 ending_pal[i].b = (i - 128)*2+1;
517 UpdatePalette();
518 VideoUpdate();
521 void DrawStream(int t)
523 int i;
524 int scr_x = 32;
525 int scr_y = 0;
526 int strm_scrl;
527 SDL_Rect draw_from, draw_to;
529 for (i = 0; i < 256; i++) {
530 ending_pal[i].r = i;
531 ending_pal[i].g = (i * 7 / 8) + 16 + sin( (float)t / 8 )*16;
532 ending_pal[i].b = (i * 3 / 4) + 32 + sin( (float)t / 8 )*32;
536 if (t >= 300) {
537 scr_x = 32 + rand()%32 - rand()%32;
538 scr_y = rand()%8;
541 if (t < 10) {
542 scr_y = (20 - t * 2);
545 DrawLevel(scr_x, scr_y, 0, 0);
546 DrawPlayer(344 - scr_x, 228 - scr_y, 0, 0);
548 for (i = 0; i < 7; i++) {
549 strm_scrl = (t * 20) % 128;
550 draw_to.x = 0 - strm_scrl - scr_x + (128*i);
551 draw_to.y = 19 - scr_y;
553 if (i >= 300) {
554 draw_to.y += rand()%4;
555 draw_to.y -= rand()%4;
557 SDL_BlitSurface(streamspr, NULL, screen, &draw_to);
560 // glitter
561 for (i = 0; i < 20; i++) {
562 draw_from.x = (rand()%3)*32;
563 draw_from.y = 0;
564 draw_from.w = 32;
565 draw_from.h = 32;
567 draw_to.x = rand()%(640+32)-32;
568 draw_to.y = (rand()%(124)) + 3;
570 SDL_BlitSurface(glitter, &draw_from, screen, &draw_to);
573 if (t > 250) {
574 if (t < 300) {
575 if (t == 251) {
576 SND_CircuitRelease(1000);
578 DrawCircle(320+32 - scr_x, 240 - scr_y, (t - 254) * 10, 255);
579 DrawCircle(320+32 - scr_x, 240 - scr_y, (t - 252) * 10, 225);
580 DrawCircle(320+32 - scr_x, 240 - scr_y, (t - 250) * 10, 195);
584 UpdatePalette();
585 VideoUpdate();
588 char *PText[10] = { "Activating the seal quickly unblocked the ley lines and allowed",
589 "psi to flow through the Dome again. The remaining shadows were",
590 "quickly flushed out.",
592 "Wervyn Anixil's unconventional use of the psi resulted in him",
593 "being burned out and rendered powerless. Merit will see to it",
594 "that he faces judgement for his crimes.",
596 "Neither of the two psi weapons housed within the Dome had been",
597 "touched. However . . ." };
598 char *PTextV[10] ={ "Activating the seal quickly unblocked the ley lines and allowed",
599 "psi to flow through the Dome again. The remaining shadows were",
600 "quickly flushed out.",
602 "The traitor, who was never identified, perished in the Sealing.",
603 "It soon became clear that the traitor had managed to betray and",
604 "kill the real Wervyn Anixil during his experiments on the psi.",
605 "If the Agate Knife was never found, nobody would have been any",
606 "the wiser, and things could have turned out very differently.",
607 "However, there was one last thing for Merit to do."};
609 void DrawPText(int t)
611 int i;
612 int c;
614 int x, y;
616 for (i = 0; i < 256; i++) {
617 ending_pal[i].r = i;
618 ending_pal[i].g = i;
619 ending_pal[i].b = (i * 3 / 4) + 64;
622 if (t < 300) {
624 for (i = 0; i < 10; i++) {
625 c = (255 + (i * 100) - t*10);
626 if (c < 0) c = 0;
627 if (c > 255) c = 255;
629 if (player_shield != 30) {
630 draw_text(68, 180+i*12, PText[i], c);
631 } else {
632 draw_text(68, 180+i*12, PTextV[i], c);
635 } else {
636 for (i = 0; i < 10; i++) {
637 c = 5 + (t-300) * 5;
639 if (player_shield != 30) {
640 draw_text(68, 180+i*12, PText[i], c);
641 } else {
642 draw_text(68, 180+i*12, PTextV[i], c);
647 for (i = 0; i < (32 * 8); i++) {
648 x = (i % 32)*20;
649 y = (i / 32)*20;
651 c = 237 + (i/32*2) + (rand()% (19 - (i/32) *2));
652 DrawRect(x, y, 20, 20, c);
653 if(!(i>138 && i<148) || (t < 265 || t >= 300)) {
654 c = 237 + (i/32*2) + (rand()% (19 - (i/32) *2));
655 DrawRect(x, 460 - y, 20, 20, c);
659 if (t >= 265 && t < 300) {
660 DrawRect(220, 380, 200, 20, 255);
661 if (t%35>17) {
662 draw_text(320 - (strlen(enterstr)*8)/2, 386, enterstr, 0);
667 UpdatePalette();
668 VideoUpdate();
671 void DrawScrolly(int t)
673 int xp;
674 int yp;
675 int i, j;
676 float a_dir;
677 float v_radius;
678 int all_blue = 0;
679 SDL_Rect draw_from, draw_to;
681 int x, y, r;
683 float bright;
685 if (t < 795) {
686 xp = 8192 - 320 - 3180 + (t * 4);
687 yp = t * 20;
688 } else {
689 xp = 8192 - 320 + ( (t-795) * 10);
690 yp = 795 * 20 - (t-795)*10;
693 // Palette
696 if ((rand() % 10)==9) {
697 all_blue = 1;
699 for (i = 0; i < 256; i++) {
700 bright = sin((float)t / 10.0) * 0.2 + 0.4;
701 ending_pal[i].r = (i * bright + (256*(1.0-bright))) * ((float)(all_blue == 0) * 0.5 + 0.5);
702 ending_pal[i].g = (i * bright + (256*(1.0-bright))) * ((float)(all_blue == 0) * 0.5 + 0.5);
703 ending_pal[i].b = i * bright + (256*(1.0-bright));
705 DrawLevel(xp, yp, 0, 0);
707 v_radius = sin((float)t / 10.0)*20 + 100;
709 for (i = 0; i < 5; i++) {
710 x = rand()%640;
711 y = rand()%480;
712 r = rand()%500+100;
714 DrawCircleEx(x, y, r+2, r-4, 128);
715 DrawCircleEx(x, y, r, r-2, 255);
718 for (i = 0; i < 4; i++) {
719 draw_from.x = (8 + i) * 32;
720 draw_from.y = 0;
721 draw_from.w = 32;
722 draw_from.h = 32;
724 a_dir = ((float)t / 10.0) + (M_PI*(float)i/2);
726 for (j = 10; j >= 0; j--) {
727 DrawCircleEx(320+cos(a_dir)*v_radius, 240+sin(a_dir)*v_radius, 22 + j * 2, 0, abs(j-3) * 15);
729 DrawCircleEx(320+cos(a_dir)*v_radius, 240+sin(a_dir)*v_radius, 20, 0, 0);
731 draw_to.x = 320 + cos(a_dir) * v_radius - 16;
732 draw_to.y = 240 + sin(a_dir) * v_radius - 16;
733 SDL_BlitSurface(artifact_spr, &draw_from, screen, &draw_to);
736 UpdatePalette();
737 VideoUpdate();
740 void DrawCircuitFlash(int t, int method)
742 static SDL_Surface *circ = NULL;
743 static int xpos, ypos;
744 int i, j;
745 SDL_Rect from;
747 if (circ == NULL) {
748 circ = IMG_Load("dat/i/circuits_1.png");
751 if (t == 0) {
752 if (method == 0) {
753 xpos = rand()%641;
754 ypos = rand()%481;
755 } else {
756 xpos = 320;
757 ypos = 240;
761 from.x = xpos;
762 from.y = ypos;
763 from.w = 640;
764 from.h = 480;
766 SDL_BlitSurface(circ, &from, screen, NULL);
768 for (i = 0; i < 256; i++) {
769 if (method == 0) {
770 j = i * t / 4;
771 } else {
772 j = i * t / 8;
773 if (t >= 20) {
774 j += t * 25;
778 if (j > 255) j = 255;
779 ending_pal[i].r = j;
780 ending_pal[i].g = j;
781 ending_pal[i].b = j;
784 UpdatePalette();
785 VideoUpdate();