Copy editing
[meritous_recharged.git] / src / ending.c
blob259b33c7a10970458a7bd14f4b48114dfe89c737
1 //
2 // ending.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.
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.
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/>.
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: Lancer-X/Asceai",
369 "Sound Effects: Various (public domain) sources",
370 "Music: Various artists",
371 "Beta testing: Quasar",
372 "Beta testing: Terryn",
373 "Beta testing: Wervyn",
374 "\"Ambient Light\" Vogue of Triton",
375 "\"Battle of Ragnarok\" Frostbite",
376 "\"Dragon Cave\" TICAZ",
377 " cavern.xm Unknown",
378 "\"Caverns Boss\" Alexis Janson",
379 "\"Forest Boss\" Alexis Janson",
380 "\"Catacombs Boss\" Alexis Janson",
381 "\"Fear 2\" Mick Rippon",
382 "\"The Final Battle\" Goose/CéDA & iNVASiON",
383 "\"Ice Frontier\" Skaven/FC",
384 "\"KnarkLoader 1.0\" Rapacious",
385 "\"RPG-Battle\" Cyn",
386 "\"Metallic Forest\" Joseph Fox"
389 void DrawCredits()
391 static SDL_Surface *fin = NULL;
392 static SDL_Surface *theend[2] = {NULL};
393 SDL_Rect draw_to;
394 int i;
395 int ypos;
396 int c;
397 int n_credits = sizeof(credits)/sizeof(*credits);
398 int finish_point;
400 finish_point = 400 + (n_credits * 50);
402 draw_to.x = 384;
403 draw_to.y = 352;
405 SDL_FillRect(screen, NULL, 0);
407 if (fin == NULL) {
408 fin = IMG_Load("dat/i/fin.png");
410 theend[0] = IMG_Load("dat/i/theend.png");
411 theend[1] = IMG_Load("dat/i/true_end.png");
414 if (credits_scroll >= (finish_point + 80)) {
415 SDL_BlitSurface(theend[(player_shield == 30)], NULL, screen, NULL);
416 } else {
417 SDL_BlitSurface(fin, NULL, screen, &draw_to);
419 // Show each line of credits
421 for (i = 0; i < n_credits; i++) {
422 ypos = 800 + (i * 100) - credits_scroll * 2;
424 if ((ypos >= 0)&&(ypos < 480)) {
425 c = 255 - abs(ypos - 240);
426 draw_text(120, ypos, credits[i], c);
432 for (i = 0; i < 128; i++) {
433 ending_pal[i].r = 0;
434 ending_pal[i].g = i;
435 ending_pal[i].b = i*2;
437 for (i = 128; i < 256; i++) {
438 ending_pal[i].r = (i - 128)*2+1;
439 ending_pal[i].g = i;
440 ending_pal[i].b = 255;
443 // Dim palette if we're just starting
445 if (credits_scroll < 80) {
446 for (i = 0; i < 256; i++) {
447 ending_pal[i].r = ending_pal[i].r * credits_scroll / 80;
448 ending_pal[i].g = ending_pal[i].g * credits_scroll / 80;
449 ending_pal[i].b = ending_pal[i].b * credits_scroll / 80;
453 // Also palette if we're finishing
455 if ((credits_scroll >= (finish_point))&&(credits_scroll < (finish_point + 80))) {
456 for (i = 0; i < 256; i++) {
457 ending_pal[i].r = ending_pal[i].r * (finish_point+80-credits_scroll) / 80;
458 ending_pal[i].g = ending_pal[i].g * (finish_point+80-credits_scroll) / 80;
459 ending_pal[i].b = ending_pal[i].b * (finish_point+80-credits_scroll) / 80;
463 if ((credits_scroll >= (finish_point + 80))&&(credits_scroll < (finish_point + 160))) {
464 for (i = 0; i < 256; i++) {
465 ending_pal[i].r = ending_pal[i].r * (credits_scroll - (finish_point + 80)) / 80;
466 ending_pal[i].g = ending_pal[i].g * (credits_scroll - (finish_point + 80)) / 80;
467 ending_pal[i].b = ending_pal[i].b * (credits_scroll - (finish_point + 80)) / 80;
471 credits_scroll++;
473 UpdatePalette();
474 VideoUpdate();
477 void RunParticleStorm(int offset)
479 SDL_Rect draw_from, draw_to;
480 int i;
482 for (i = 0; i < 64; i++) {
483 DrawRect(0, i * 15 - offset, 640, 15, 64 - i);
486 for (i = 0; i < 500; i++) {
487 if (pt_t[i] > 0) {
488 pt_t[i]--;
489 } else {
490 pt_vy[i] += 0.1;
491 pt_x[i] += pt_vx[i];
492 pt_y[i] += pt_vy[i];
495 draw_from.x = (rand()%3)*32;
496 draw_from.y = 0;
497 draw_from.w = 32;
498 draw_from.h = 32;
500 draw_to.x = (int)pt_x[i] - 16;
501 draw_to.y = (int)pt_y[i] - 16 - offset;
502 SDL_BlitSurface(glitter, &draw_from, screen, &draw_to);
505 for (i = 0; i < 128; i++) {
506 ending_pal[i].r = i*2;
507 ending_pal[i].g = i*2;
508 ending_pal[i].b = 0;
510 for (i = 128; i < 256; i++) {
511 ending_pal[i].r = 255;
512 ending_pal[i].g = 255;
513 ending_pal[i].b = (i - 128)*2+1;
516 UpdatePalette();
517 VideoUpdate();
520 void DrawStream(int t)
522 int i;
523 int scr_x = 32;
524 int scr_y = 0;
525 int strm_scrl;
526 SDL_Rect draw_from, draw_to;
528 for (i = 0; i < 256; i++) {
529 ending_pal[i].r = i;
530 ending_pal[i].g = (i * 7 / 8) + 16 + sin( (float)t / 8 )*16;
531 ending_pal[i].b = (i * 3 / 4) + 32 + sin( (float)t / 8 )*32;
535 if (t >= 300) {
536 scr_x = 32 + rand()%32 - rand()%32;
537 scr_y = rand()%8;
540 if (t < 10) {
541 scr_y = (20 - t * 2);
544 DrawLevel(scr_x, scr_y, 0, 0);
545 DrawPlayer(344 - scr_x, 228 - scr_y, 0, 0);
547 for (i = 0; i < 7; i++) {
548 strm_scrl = (t * 20) % 128;
549 draw_to.x = 0 - strm_scrl - scr_x + (128*i);
550 draw_to.y = 19 - scr_y;
552 if (i >= 300) {
553 draw_to.y += rand()%4;
554 draw_to.y -= rand()%4;
556 SDL_BlitSurface(streamspr, NULL, screen, &draw_to);
559 // glitter
560 for (i = 0; i < 20; i++) {
561 draw_from.x = (rand()%3)*32;
562 draw_from.y = 0;
563 draw_from.w = 32;
564 draw_from.h = 32;
566 draw_to.x = rand()%(640+32)-32;
567 draw_to.y = (rand()%(124)) + 3;
569 SDL_BlitSurface(glitter, &draw_from, screen, &draw_to);
572 if (t > 250) {
573 if (t < 300) {
574 if (t == 251) {
575 SND_CircuitRelease(1000);
577 DrawCircle(320+32 - scr_x, 240 - scr_y, (t - 254) * 10, 255);
578 DrawCircle(320+32 - scr_x, 240 - scr_y, (t - 252) * 10, 225);
579 DrawCircle(320+32 - scr_x, 240 - scr_y, (t - 250) * 10, 195);
583 UpdatePalette();
584 VideoUpdate();
587 char *PText[10] = { "Activating the seal quickly unblocked the ley lines and allowed",
588 "psi to flow through the Dome again. The remaining shadows were",
589 "quickly flushed out.",
591 "Wervyn Anixil's unconventional use of the psi resulted in him",
592 "being burned out and rendered powerless. Merit will see to it",
593 "that he faces judgement for his crimes.",
595 "Neither of the two psi weapons housed within the Dome had been",
596 "touched. However . . ." };
597 char *PTextV[10] ={ "Activating the seal quickly unblocked the ley lines and allowed",
598 "psi to flow through the Dome again. The remaining shadows were",
599 "quickly flushed out.",
601 "The traitor, who was never identified, perished in the Sealing.",
602 "It soon became clear that the traitor had managed to betray and",
603 "kill the real Wervyn Anixil during his experiments on the psi.",
604 "If the Agate Knife was never found, nobody would have been any",
605 "the wiser, and things could have turned out very differently.",
606 "However, there was one last thing for Merit to do."};
608 void DrawPText(int t)
610 int i;
611 int c;
613 int x, y;
615 for (i = 0; i < 256; i++) {
616 ending_pal[i].r = i;
617 ending_pal[i].g = i;
618 ending_pal[i].b = (i * 3 / 4) + 64;
621 if (t < 300) {
623 for (i = 0; i < 10; i++) {
624 c = (255 + (i * 100) - t*10);
625 if (c < 0) c = 0;
626 if (c > 255) c = 255;
628 if (player_shield != 30) {
629 draw_text(68, 180+i*12, PText[i], c);
630 } else {
631 draw_text(68, 180+i*12, PTextV[i], c);
634 } else {
635 for (i = 0; i < 10; i++) {
636 c = 5 + (t-300) * 5;
638 if (player_shield != 30) {
639 draw_text(68, 180+i*12, PText[i], c);
640 } else {
641 draw_text(68, 180+i*12, PTextV[i], c);
646 for (i = 0; i < (32 * 8); i++) {
647 x = (i % 32)*20;
648 y = (i / 32)*20;
650 c = 237 + (i/32*2) + (rand()% (19 - (i/32) *2));
651 DrawRect(x, y, 20, 20, c);
652 if(!(i>138 && i<148) || (t < 265 || t >= 300)) {
653 c = 237 + (i/32*2) + (rand()% (19 - (i/32) *2));
654 DrawRect(x, 460 - y, 20, 20, c);
658 if (t >= 265 && t < 300) {
659 DrawRect(220, 380, 200, 20, 255);
660 if (t%35>17) {
661 draw_text(320 - (strlen(enterstr)*8)/2, 386, enterstr, 0);
666 UpdatePalette();
667 VideoUpdate();
670 void DrawScrolly(int t)
672 int xp;
673 int yp;
674 int i, j;
675 float a_dir;
676 float v_radius;
677 int all_blue = 0;
678 SDL_Rect draw_from, draw_to;
680 int x, y, r;
682 float bright;
684 if (t < 795) {
685 xp = 8192 - 320 - 3180 + (t * 4);
686 yp = t * 20;
687 } else {
688 xp = 8192 - 320 + ( (t-795) * 10);
689 yp = 795 * 20 - (t-795)*10;
692 // Palette
695 if ((rand() % 10)==9) {
696 all_blue = 1;
698 for (i = 0; i < 256; i++) {
699 bright = sin((float)t / 10.0) * 0.2 + 0.4;
700 ending_pal[i].r = (i * bright + (256*(1.0-bright))) * ((float)(all_blue == 0) * 0.5 + 0.5);
701 ending_pal[i].g = (i * bright + (256*(1.0-bright))) * ((float)(all_blue == 0) * 0.5 + 0.5);
702 ending_pal[i].b = i * bright + (256*(1.0-bright));
704 DrawLevel(xp, yp, 0, 0);
706 v_radius = sin((float)t / 10.0)*20 + 100;
708 for (i = 0; i < 5; i++) {
709 x = rand()%640;
710 y = rand()%480;
711 r = rand()%500+100;
713 DrawCircleEx(x, y, r+2, r-4, 128);
714 DrawCircleEx(x, y, r, r-2, 255);
717 for (i = 0; i < 4; i++) {
718 draw_from.x = (8 + i) * 32;
719 draw_from.y = 0;
720 draw_from.w = 32;
721 draw_from.h = 32;
723 a_dir = ((float)t / 10.0) + (M_PI*(float)i/2);
725 for (j = 10; j >= 0; j--) {
726 DrawCircleEx(320+cos(a_dir)*v_radius, 240+sin(a_dir)*v_radius, 22 + j * 2, 0, abs(j-3) * 15);
728 DrawCircleEx(320+cos(a_dir)*v_radius, 240+sin(a_dir)*v_radius, 20, 0, 0);
730 draw_to.x = 320 + cos(a_dir) * v_radius - 16;
731 draw_to.y = 240 + sin(a_dir) * v_radius - 16;
732 SDL_BlitSurface(artifact_spr, &draw_from, screen, &draw_to);
735 UpdatePalette();
736 VideoUpdate();
739 void DrawCircuitFlash(int t, int method)
741 static SDL_Surface *circ = NULL;
742 static int xpos, ypos;
743 int i, j;
744 SDL_Rect from;
746 if (circ == NULL) {
747 circ = IMG_Load("dat/i/circuits_1.png");
750 if (t == 0) {
751 if (method == 0) {
752 xpos = rand()%641;
753 ypos = rand()%481;
754 } else {
755 xpos = 320;
756 ypos = 240;
760 from.x = xpos;
761 from.y = ypos;
762 from.w = 640;
763 from.h = 480;
765 SDL_BlitSurface(circ, &from, screen, NULL);
767 for (i = 0; i < 256; i++) {
768 if (method == 0) {
769 j = i * t / 4;
770 } else {
771 j = i * t / 8;
772 if (t >= 20) {
773 j += t * 25;
777 if (j > 255) j = 255;
778 ending_pal[i].r = j;
779 ending_pal[i].g = j;
780 ending_pal[i].b = j;
783 UpdatePalette();
784 VideoUpdate();