A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 681 lines 20 kB view raw
1/* Emacs style mode select -*- C++ -*- 2 *----------------------------------------------------------------------------- 3 * 4 * 5 * PrBoom a Doom port merged with LxDoom and LSDLDoom 6 * based on BOOM, a modified and improved DOOM engine 7 * Copyright (C) 1999 by 8 * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman 9 * Copyright (C) 1999-2000 by 10 * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze 11 * 12 * This program is free software; you can redistribute it and/or 13 * modify it under the terms of the GNU General Public License 14 * as published by the Free Software Foundation; either version 2 15 * of the License, or (at your option) any later version. 16 * 17 * This program is distributed in the hope that it will be useful, 18 * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 * GNU General Public License for more details. 21 * 22 * You should have received a copy of the GNU General Public License 23 * along with this program; if not, write to the Free Software 24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 25 * 02111-1307, USA. 26 * 27 * DESCRIPTION: 28 * Game completion, final screen animation. 29 * 30 *----------------------------------------------------------------------------- 31 */ 32 33#include "doomstat.h" 34#include "d_event.h" 35#include "m_swap.h" 36#include "v_video.h" 37#include "w_wad.h" 38#include "s_sound.h" 39#include "sounds.h" 40#include "d_deh.h" // Ty 03/22/98 - externalizations 41#include "f_finale.h" // CPhipps - hmm... 42#include "rockmacros.h" 43 44// Stage of animation: 45// 0 = text, 1 = art screen, 2 = character cast 46static int finalestage; // cph - 47static int finalecount; // made static 48static const char* finaletext; // cph - 49static const char* finaleflat; // made static const 50 51// defines for the end mission display text // phares 52 53#define TEXTSPEED 3 // original value // phares 54#define TEXTWAIT 250 // original value // phares 55#define NEWTEXTSPEED 0.01f // new value // phares 56#define NEWTEXTWAIT 1000 // new value // phares 57 58// CPhipps - removed the old finale screen text message strings; 59// they were commented out for ages already 60// Ty 03/22/98 - ... the new s_WHATEVER extern variables are used 61// in the code below instead. 62 63void F_StartCast (void); 64void F_CastTicker (void); 65boolean F_CastResponder (event_t *ev); 66void F_CastDrawer (void); 67 68void WI_checkForAccelerate(void); // killough 3/28/98: used to 69extern int acceleratestage; // accelerate intermission screens 70static int midstage; // whether we're in "mid-stage" 71 72// 73// F_StartFinale 74// 75void F_StartFinale (void) 76{ 77 gameaction = ga_nothing; 78 gamestate = GS_FINALE; 79 automapmode &= ~am_active; 80 81 // killough 3/28/98: clear accelerative text flags 82 acceleratestage = midstage = 0; 83 84 // Okay - IWAD dependend stuff. 85 // This has been changed severly, and 86 // some stuff might have changed in the process. 87 switch ( gamemode ) 88 { 89 90 // DOOM 1 - E1, E3 or E4, but each nine missions 91 case shareware: 92 case registered: 93 case retail: 94 { 95 S_ChangeMusic(mus_victor, true); 96 97 switch (gameepisode) 98 { 99 case 1: 100 finaleflat = bgflatE1; // Ty 03/30/98 - new externalized bg flats 101 finaletext = s_E1TEXT; // Ty 03/23/98 - Was e1text variable. 102 break; 103 case 2: 104 finaleflat = bgflatE2; 105 finaletext = s_E2TEXT; // Ty 03/23/98 - Same stuff for each 106 break; 107 case 3: 108 finaleflat = bgflatE3; 109 finaletext = s_E3TEXT; 110 break; 111 case 4: 112 finaleflat = bgflatE4; 113 finaletext = s_E4TEXT; 114 break; 115 default: 116 // Ouch. 117 break; 118 } 119 break; 120 } 121 122 // DOOM II and missions packs with E1, M34 123 case commercial: 124 { 125 S_ChangeMusic(mus_read_m, true); 126 127 // Ty 08/27/98 - added the gamemission logic 128 switch (gamemap) 129 { 130 case 6: 131 finaleflat = bgflat06; 132 finaletext = (gamemission==pack_tnt) ? s_T1TEXT : 133 (gamemission==pack_plut) ? s_P1TEXT : s_C1TEXT; 134 break; 135 case 11: 136 finaleflat = bgflat11; 137 finaletext = (gamemission==pack_tnt) ? s_T2TEXT : 138 (gamemission==pack_plut) ? s_P2TEXT : s_C2TEXT; 139 break; 140 case 20: 141 finaleflat = bgflat20; 142 finaletext = (gamemission==pack_tnt) ? s_T3TEXT : 143 (gamemission==pack_plut) ? s_P3TEXT : s_C3TEXT; 144 break; 145 case 30: 146 finaleflat = bgflat30; 147 finaletext = (gamemission==pack_tnt) ? s_T4TEXT : 148 (gamemission==pack_plut) ? s_P4TEXT : s_C4TEXT; 149 break; 150 case 15: 151 finaleflat = bgflat15; 152 finaletext = (gamemission==pack_tnt) ? s_T5TEXT : 153 (gamemission==pack_plut) ? s_P5TEXT : s_C5TEXT; 154 break; 155 case 31: 156 finaleflat = bgflat31; 157 finaletext = (gamemission==pack_tnt) ? s_T6TEXT : 158 (gamemission==pack_plut) ? s_P6TEXT : s_C6TEXT; 159 break; 160 default: 161 // Ouch. 162 break; 163 } 164 break; 165 // Ty 08/27/98 - end gamemission logic 166 } 167 168 // Indeterminate. 169 default: // Ty 03/30/98 - not externalized 170 S_ChangeMusic(mus_read_m, true); 171 finaleflat = "F_SKY1"; // Not used anywhere else. 172 finaletext = s_C1TEXT; // FIXME - other text, music? 173 break; 174 } 175 176 finalestage = 0; 177 finalecount = 0; 178 179} 180 181 182 183boolean F_Responder (event_t *event) 184{ 185 if (finalestage == 2) 186 return F_CastResponder (event); 187 188 return false; 189} 190 191// Get_TextSpeed() returns the value of the text display speed // phares 192// Rewritten to allow user-directed acceleration -- killough 3/28/98 193 194static float Get_TextSpeed(void) 195{ 196 return midstage ? NEWTEXTSPEED : (midstage=acceleratestage) ? 197 acceleratestage=0, NEWTEXTSPEED : TEXTSPEED; 198} 199 200// 201// F_Ticker 202// 203// killough 3/28/98: almost totally rewritten, to use 204// player-directed acceleration instead of constant delays. 205// Now the player can accelerate the text display by using 206// the fire/use keys while it is being printed. The delay 207// automatically responds to the user, and gives enough 208// time to read. 209// 210// killough 5/10/98: add back v1.9 demo compatibility 211// 212 213void F_Ticker(void) 214{ 215 int i; 216 if (!demo_compatibility) 217 WI_checkForAccelerate(); // killough 3/28/98: check for acceleration 218 else 219 if (gamemode == commercial && finalecount > 50) // check for skipping 220 for (i=0; i<MAXPLAYERS; i++) 221 if (players[i].cmd.buttons) 222 goto next_level; // go on to the next level 223 224 // advance animation 225 finalecount++; 226 227 if (finalestage == 2) 228 F_CastTicker(); 229 230 if (!finalestage) 231 { 232 float speed = demo_compatibility ? TEXTSPEED : Get_TextSpeed(); 233 /* killough 2/28/98: changed to allow acceleration */ 234 if (finalecount > strlen(finaletext)*speed + 235 (midstage ? NEWTEXTWAIT : TEXTWAIT) || 236 (midstage && acceleratestage)) { 237 if (gamemode != commercial) // Doom 1 / Ultimate Doom episode end 238 { // with enough time, it's automatic 239 finalecount = 0; 240 finalestage = 1; 241 wipegamestate = -1; // force a wipe 242 if (gameepisode == 3) 243 S_StartMusic(mus_bunny); 244 } 245 else // you must press a button to continue in Doom 2 246 if (!demo_compatibility && midstage) 247 { 248next_level: 249 if (gamemap == 30) 250 F_StartCast(); // cast of Doom 2 characters 251 else 252 gameaction = ga_worlddone; // next level, e.g. MAP07 253 } 254 } 255 } 256} 257 258 259// 260// F_TextWrite 261// 262// This program displays the background and text at end-mission // phares 263// text time. It draws both repeatedly so that other displays, // | 264// like the main menu, can be drawn over it dynamically and // V 265// erased dynamically. The TEXTSPEED constant is changed into 266// the Get_TextSpeed function so that the speed of writing the // ^ 267// text can be increased, and there's still time to read what's // | 268// written. // phares 269// CPhipps - reformatted 270 271#include "hu_stuff.h" 272extern patchnum_t hu_font[HU_FONTSIZE]; 273 274 275void F_TextWrite (void) 276{ 277 V_DrawBackground(finaleflat, 0); 278 { // draw some of the text onto the screen 279 int cx = 10; 280 int cy = 10; 281 const char* ch = finaletext; // CPhipps - const 282 int count = (int)((float)(finalecount - 10)/Get_TextSpeed()); // phares 283 int w; 284 285 if (count < 0) 286 count = 0; 287 288 for ( ; count ; count-- ) { 289 int c = *ch++; 290 291 if (!c) 292 break; 293 if (c == '\n') { 294 cx = 10; 295 cy += 11; 296 continue; 297 } 298 299 c = toupper(c) - HU_FONTSTART; 300 if (c < 0 || c> HU_FONTSIZE) { 301 cx += 4; 302 continue; 303 } 304 305 w = SHORT (hu_font[c].width); 306 if (cx+w > 320) 307 break; 308 // CPhipps - patch drawing updated 309 V_DrawNumPatch(cx, cy, 0, hu_font[c].lumpnum, CR_DEFAULT, VPT_STRETCH); 310 cx+=w; 311 } 312 } 313} 314 315// 316// Final DOOM 2 animation 317// Casting by id Software. 318// in order of appearance 319// 320typedef struct 321{ 322 const char **name; // CPhipps - const** 323 mobjtype_t type; 324} castinfo_t; 325 326#define MAX_CASTORDER 18 /* Ty - hard coded for now */ 327static const castinfo_t castorder[] = { // CPhipps - static const, initialised here 328 { &s_CC_ZOMBIE, MT_POSSESSED }, 329 { &s_CC_SHOTGUN, MT_SHOTGUY }, 330 { &s_CC_HEAVY, MT_CHAINGUY }, 331 { &s_CC_IMP, MT_TROOP }, 332 { &s_CC_DEMON, MT_SERGEANT }, 333 { &s_CC_LOST, MT_SKULL }, 334 { &s_CC_CACO, MT_HEAD }, 335 { &s_CC_HELL, MT_KNIGHT }, 336 { &s_CC_BARON, MT_BRUISER }, 337 { &s_CC_ARACH, MT_BABY }, 338 { &s_CC_PAIN, MT_PAIN }, 339 { &s_CC_REVEN, MT_UNDEAD }, 340 { &s_CC_MANCU, MT_FATSO }, 341 { &s_CC_ARCH, MT_VILE }, 342 { &s_CC_SPIDER, MT_SPIDER }, 343 { &s_CC_CYBER, MT_CYBORG }, 344 { &s_CC_HERO, MT_PLAYER }, 345 { NULL, 0} 346 }; 347 348int castnum; 349int casttics; 350state_t* caststate; 351boolean castdeath; 352int castframes; 353int castonmelee; 354boolean castattacking; 355 356 357// 358// F_StartCast 359// 360extern gamestate_t wipegamestate; 361 362void F_StartCast (void) 363{ 364 wipegamestate = -1; // force a screen wipe 365 castnum = 0; 366 caststate = &states[mobjinfo[castorder[castnum].type].seestate]; 367 casttics = caststate->tics; 368 castdeath = false; 369 finalestage = 2; 370 castframes = 0; 371 castonmelee = 0; 372 castattacking = false; 373 S_ChangeMusic(mus_evil, true); 374} 375 376 377// 378// F_CastTicker 379// 380void F_CastTicker (void) 381{ 382 int st; 383 int sfx; 384 385 if (--casttics > 0) 386 return; // not time to change state yet 387 388 if (caststate->tics == -1 || caststate->nextstate == S_NULL) 389 { 390 // switch from deathstate to next monster 391 castnum++; 392 castdeath = false; 393 if (castorder[castnum].name == NULL) 394 castnum = 0; 395 if (mobjinfo[castorder[castnum].type].seesound) 396 S_StartSound (NULL, mobjinfo[castorder[castnum].type].seesound); 397 caststate = &states[mobjinfo[castorder[castnum].type].seestate]; 398 castframes = 0; 399 } 400 else 401 { 402 // just advance to next state in animation 403 if (caststate == &states[S_PLAY_ATK1]) 404 goto stopattack; // Oh, gross hack! 405 st = caststate->nextstate; 406 caststate = &states[st]; 407 castframes++; 408 409 // sound hacks.... 410 switch (st) 411 { 412 case S_PLAY_ATK1: sfx = sfx_dshtgn; break; 413 case S_POSS_ATK2: sfx = sfx_pistol; break; 414 case S_SPOS_ATK2: sfx = sfx_shotgn; break; 415 case S_VILE_ATK2: sfx = sfx_vilatk; break; 416 case S_SKEL_FIST2: sfx = sfx_skeswg; break; 417 case S_SKEL_FIST4: sfx = sfx_skepch; break; 418 case S_SKEL_MISS2: sfx = sfx_skeatk; break; 419 case S_FATT_ATK8: 420 case S_FATT_ATK5: 421 case S_FATT_ATK2: sfx = sfx_firsht; break; 422 case S_CPOS_ATK2: 423 case S_CPOS_ATK3: 424 case S_CPOS_ATK4: sfx = sfx_shotgn; break; 425 case S_TROO_ATK3: sfx = sfx_claw; break; 426 case S_SARG_ATK2: sfx = sfx_sgtatk; break; 427 case S_BOSS_ATK2: 428 case S_BOS2_ATK2: 429 case S_HEAD_ATK2: sfx = sfx_firsht; break; 430 case S_SKULL_ATK2: sfx = sfx_sklatk; break; 431 case S_SPID_ATK2: 432 case S_SPID_ATK3: sfx = sfx_shotgn; break; 433 case S_BSPI_ATK2: sfx = sfx_plasma; break; 434 case S_CYBER_ATK2: 435 case S_CYBER_ATK4: 436 case S_CYBER_ATK6: sfx = sfx_rlaunc; break; 437 case S_PAIN_ATK3: sfx = sfx_sklatk; break; 438 default: sfx = 0; break; 439 } 440 441 if (sfx) 442 S_StartSound (NULL, sfx); 443 } 444 445 if (castframes == 12) 446 { 447 // go into attack frame 448 castattacking = true; 449 if (castonmelee) 450 caststate=&states[mobjinfo[castorder[castnum].type].meleestate]; 451 else 452 caststate=&states[mobjinfo[castorder[castnum].type].missilestate]; 453 castonmelee ^= 1; 454 if (caststate == &states[S_NULL]) 455 { 456 if (castonmelee) 457 caststate= 458 &states[mobjinfo[castorder[castnum].type].meleestate]; 459 else 460 caststate= 461 &states[mobjinfo[castorder[castnum].type].missilestate]; 462 } 463 } 464 465 if (castattacking) 466 { 467 if (castframes == 24 468 || caststate == &states[mobjinfo[castorder[castnum].type].seestate] ) 469 { 470stopattack: 471 castattacking = false; 472 castframes = 0; 473 caststate = &states[mobjinfo[castorder[castnum].type].seestate]; 474 } 475 } 476 477 casttics = caststate->tics; 478 if (casttics == -1) 479 casttics = 15; 480} 481 482 483// 484// F_CastResponder 485// 486 487boolean F_CastResponder (event_t* ev) 488{ 489 if (ev->type != ev_keydown) 490 return false; 491 492 if (castdeath) 493 return true; // already in dying frames 494 495 // go into death frame 496 castdeath = true; 497 caststate = &states[mobjinfo[castorder[castnum].type].deathstate]; 498 casttics = caststate->tics; 499 castframes = 0; 500 castattacking = false; 501 if (mobjinfo[castorder[castnum].type].deathsound) 502 S_StartSound (NULL, mobjinfo[castorder[castnum].type].deathsound); 503 504 return true; 505} 506 507 508static void F_CastPrint (const char* text) // CPhipps - static, const char* 509{ 510 const char* ch; // CPhipps - const 511 int c; 512 int cx; 513 int w; 514 int width; 515 516 // find width 517 ch = text; 518 width = 0; 519 520 while (ch) 521 { 522 c = *ch++; 523 if (!c) 524 break; 525 c = toupper(c) - HU_FONTSTART; 526 if (c < 0 || c> HU_FONTSIZE) 527 { 528 width += 4; 529 continue; 530 } 531 532 w = SHORT (hu_font[c].width); 533 width += w; 534 } 535 536 // draw it 537 cx = 160-width/2; 538 ch = text; 539 while (ch) 540 { 541 c = *ch++; 542 if (!c) 543 break; 544 c = toupper(c) - HU_FONTSTART; 545 if (c < 0 || c> HU_FONTSIZE) 546 { 547 cx += 4; 548 continue; 549 } 550 551 w = SHORT (hu_font[c].width); 552 // CPhipps - patch drawing updated 553 V_DrawNumPatch(cx, 180, 0, hu_font[c].lumpnum, CR_DEFAULT, VPT_STRETCH); 554 cx+=w; 555 } 556} 557 558 559// 560// F_CastDrawer 561// 562 563void F_CastDrawer (void) 564{ 565 spritedef_t* sprdef; 566 spriteframe_t* sprframe; 567 int lump; 568 boolean flip; 569 570 // erase the entire screen to a background 571 // CPhipps - patch drawing updated 572 V_DrawNamePatch(0,0,0, bgcastcall, CR_DEFAULT, VPT_STRETCH); // Ty 03/30/98 bg texture extern 573 574 F_CastPrint (*(castorder[castnum].name)); 575 576 // draw the current frame in the middle of the screen 577 sprdef = &sprites[caststate->sprite]; 578 sprframe = &sprdef->spriteframes[ caststate->frame & FF_FRAMEMASK]; 579 lump = sprframe->lump[0]; 580 flip = (boolean)sprframe->flip[0]; 581 582 // CPhipps - patch drawing updated 583 V_DrawNumPatch(160, 170, 0, lump+firstspritelump, CR_DEFAULT, 584 VPT_STRETCH | (flip ? VPT_FLIP : 0)); 585} 586 587 588// 589// F_BunnyScroll 590// 591static const char pfub2[] = { "PFUB2" }; 592static const char pfub1[] = { "PFUB1" }; 593 594static void F_BunnyScroll (void) 595{ 596 char name[10]; 597 int stage; 598 static int laststage; 599 600 V_MarkRect (0, 0, SCREENWIDTH, SCREENHEIGHT); 601 { 602 int scrolled = 320 - (finalecount-230)/2; 603 if (scrolled <= 0) { 604 V_DrawNamePatch(0, 0, 0, pfub2, CR_DEFAULT, VPT_STRETCH); 605 } else if (scrolled >= 320) { 606 V_DrawNamePatch(0, 0, 0, pfub1, CR_DEFAULT, VPT_STRETCH); 607 } else { 608#define SCRN 2 609 610 int realscrolled = (SCREENWIDTH * scrolled) / 320; 611 612 V_AllocScreen(SCRN); 613 V_DrawNamePatch(0, 0, SCRN, pfub2, CR_DEFAULT, VPT_STRETCH); 614 V_CopyRect(realscrolled, 0, SCRN, SCREENWIDTH-realscrolled, SCREENHEIGHT, 0, 0, 0, VPT_NONE); 615 V_DrawNamePatch(0, 0, SCRN, pfub1, CR_DEFAULT, VPT_STRETCH); 616 V_CopyRect(0, 0, SCRN, realscrolled, SCREENHEIGHT, SCREENWIDTH-realscrolled, 0, 0, VPT_NONE); 617 V_FreeScreen(SCRN); 618 } 619 } 620 621 if (finalecount < 1130) 622 return; 623 if (finalecount < 1180) 624 { 625 // CPhipps - patch drawing updated 626 V_DrawNamePatch((320-13*8)/2, (200-8*8)/2,0, "END0", CR_DEFAULT, VPT_STRETCH); 627 laststage = 0; 628 return; 629 } 630 631 stage = (finalecount-1180) / 5; 632 if (stage > 6) 633 stage = 6; 634 if (stage > laststage) 635 { 636 S_StartSound (NULL, sfx_pistol); 637 laststage = stage; 638 } 639 640 snprintf (name,sizeof(name), "END%d",stage); 641 // CPhipps - patch drawing updated 642 V_DrawNamePatch((320-13*8)/2, (200-8*8)/2, 0, name, CR_DEFAULT, VPT_STRETCH); 643} 644 645 646// 647// F_Drawer 648// 649void F_Drawer (void) 650{ 651 if (finalestage == 2) 652 { 653 F_CastDrawer (); 654 return; 655 } 656 657 if (!finalestage) 658 F_TextWrite (); 659 else 660 { 661 switch (gameepisode) 662 { 663 // CPhipps - patch drawing updated 664 case 1: 665 if ( gamemode == retail ) 666 V_DrawNamePatch(0, 0, 0, "CREDIT", CR_DEFAULT, VPT_STRETCH); 667 else 668 V_DrawNamePatch(0, 0, 0, "HELP2", CR_DEFAULT, VPT_STRETCH); 669 break; 670 case 2: 671 V_DrawNamePatch(0, 0, 0, "VICTORY2", CR_DEFAULT, VPT_STRETCH); 672 break; 673 case 3: 674 F_BunnyScroll (); 675 break; 676 case 4: 677 V_DrawNamePatch(0, 0, 0, "ENDPIC", CR_DEFAULT, VPT_STRETCH); 678 break; 679 } 680 } 681}