diff --git a/src/console.c b/src/console.c index c1c5557b9..09a6cab45 100644 --- a/src/console.c +++ b/src/console.c @@ -1608,7 +1608,7 @@ void CON_Drawer(void) if (con_curlines > 0) CON_DrawConsole(); else if (gamestate == GS_LEVEL - || gamestate == GS_INTERMISSION || gamestate == GS_CUTSCENE + || gamestate == GS_INTERMISSION || gamestate == GS_ENDING || gamestate == GS_CUTSCENE || gamestate == GS_CREDITS || gamestate == GS_EVALUATION) CON_DrawHudlines(); } diff --git a/src/d_main.c b/src/d_main.c index c7d709aec..32e874f07 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -310,6 +310,12 @@ static void D_Display(void) wipe = true; break; + case GS_ENDING: + F_EndingDrawer(); + HU_Erase(); + HU_Drawer(); + break; + case GS_CUTSCENE: F_CutsceneDrawer(); HU_Erase(); diff --git a/src/dehacked.c b/src/dehacked.c index 01e2b0bde..0cb7330d0 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -517,7 +517,9 @@ static void readfreeslots(MYFILE *f) continue; // Copy in the spr2 name and increment free_spr2. if (free_spr2 < NUMPLAYERSPRITES) { + CONS_Printf("Sprite SPR2_%s allocated.\n",word); strncpy(spr2names[free_spr2],word,4); + spr2defaults[free_spr2] = 0; spr2names[free_spr2++][4] = 0; } else CONS_Alert(CONS_WARNING, "Ran out of free SPR2 slots!\n"); @@ -1108,6 +1110,7 @@ static void readlevelheader(MYFILE *f, INT32 num) if (fastcmp(word2, "TITLE")) i = 1100; else if (fastcmp(word2, "EVALUATION")) i = 1101; else if (fastcmp(word2, "CREDITS")) i = 1102; + else if (fastcmp(word2, "ENDING")) i = 1103; else // Support using the actual map name, // i.e., Nextlevel = AB, Nextlevel = FZ, etc. @@ -9499,6 +9502,7 @@ static inline int lib_freeslot(lua_State *L) { CONS_Printf("Sprite SPR2_%s allocated.\n",word); strncpy(spr2names[free_spr2],word,4); + spr2defaults[free_spr2] = 0; spr2names[free_spr2++][4] = 0; } else CONS_Alert(CONS_WARNING, "Ran out of free SPR2 slots!\n"); diff --git a/src/doomstat.h b/src/doomstat.h index 87b98ab40..18300967c 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -234,7 +234,7 @@ extern textprompt_t *textprompts[MAX_PROMPTS]; // For the Custom Exit linedef. extern INT16 nextmapoverride; -extern boolean skipstats; +extern UINT8 skipstats; extern UINT32 ssspheres; // Total # of spheres in a level diff --git a/src/f_finale.c b/src/f_finale.c index 27c9ebd68..3d1b2ab83 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -14,6 +14,7 @@ #include "doomdef.h" #include "doomstat.h" #include "d_main.h" +#include "d_netcmd.h" #include "f_finale.h" #include "g_game.h" #include "hu_stuff.h" @@ -36,6 +37,7 @@ #include "p_setup.h" #include "st_stuff.h" // hud hiding #include "fastcmp.h" +#include "console.h" #ifdef HAVE_BLUA #include "lua_hud.h" @@ -53,7 +55,6 @@ static INT32 continuetime; // Short delay when continuing static tic_t animtimer; // Used for some animation timings static INT16 skullAnimCounter; // Prompts: Chevron animation -static INT32 roidtics; // Asteroid spinning static INT32 deplete; static tic_t stoptimer; @@ -95,10 +96,22 @@ static patch_t *ttspop5; static patch_t *ttspop6; static patch_t *ttspop7; +static boolean goodending; +static patch_t *endbrdr[2]; // border - blue, white, pink - where have i seen those colours before? +static patch_t *endbgsp[3]; // nebula, sun, planet +static patch_t *endegrk[2]; // eggrock - replaced midway through good ending +static patch_t *endfwrk[3]; // firework - replaced with skin when good ending +static patch_t *endspkl[3]; // sparkle +static patch_t *endglow[2]; // glow aura - replaced with black rock's midway through good ending +static patch_t *endxpld[4]; // mini explosion +static patch_t *endescp[5]; // escape pod + flame +static INT32 sparkloffs[3][2]; // eggrock explosions/blackrock sparkles +static INT32 sparklloop; + // // PROMPT STATE // -static boolean promptactive = false; +boolean promptactive = false; static mobj_t *promptmo; static INT16 promptpostexectag; static boolean promptblockcontrols; @@ -230,6 +243,7 @@ void F_StartCustomCutscene(INT32 cutscenenum, boolean precutscene, boolean reset void F_StartIntro(void) { S_StopMusic(); + S_StopSounds(); if (introtoplay) { @@ -393,7 +407,6 @@ void F_StartIntro(void) intro_scenenum = 0; finalecount = animtimer = skullAnimCounter = stoptimer = 0; - roidtics = BASEVIDWIDTH - 64; timetonext = introscenetime[intro_scenenum]; } @@ -676,11 +689,42 @@ static void F_IntroDrawScene(void) if (intro_scenenum == 4) // The asteroid SPINS! { - if (roidtics >= 0) + if (intro_curtime > 1) { - V_DrawScaledPatch(roidtics, 24, 0, - (patch = W_CachePatchName(va("ROID00%.2d", intro_curtime%35), PU_CACHE))); - W_UnlockCachedPatch(patch); + INT32 worktics = intro_curtime - 1; + INT32 scale = FRACUNIT; + patch_t *rockpat; + UINT8 *colormap = NULL; + patch_t *glow; + INT32 trans = 0; + + INT32 x = ((BASEVIDWIDTH - 64)<= 5) + trans = (worktics-5)>>1; + if (trans < 10) + V_DrawFixedPatch(x, y, scale, trans< SRB2 Community Contributors". "", "\1Sprite Artists", - "Odi \"Iceman404\" Atunzu", + "\"Iceman404\"", "Victor \"VAdaPEGA\" Ara\x1Fjo", // Araújo -- sorry for our limited font! D: + "Sally \"TehRealSalt\" Cochenour", "Jim \"MotorRoach\" DeMello", "Desmond \"Blade\" DesJardins", "Sherman \"CoatRack\" DesJardins", @@ -1049,7 +1090,10 @@ static const char *credits[] = { "Simon \"sirjuddington\" Judd", // SLADE developer // Acknowledged here are the following: // Minor merge request authors, see guideline above - // Golden - Expanded thin font + // - Golden - Expanded thin font + // Creators of small quantities of sprite/texture assets + // - Arietty - New Green Hill-styled textures + // - Scizor300 - the only other contributor to the 2.0 SRB2 Asset Pack "SRB2 Community Contributors", "", "\1Produced By", @@ -1103,6 +1147,7 @@ void F_StartCredits(void) paused = false; CON_ToggleOff(); S_StopMusic(); + S_StopSounds(); S_ChangeMusicInternal("_creds", false); @@ -1221,7 +1266,7 @@ boolean F_CreditResponder(event_t *event) break; } - if (!(timesBeaten) && !(netgame || multiplayer)) + if (!(timesBeaten) && !(netgame || multiplayer) && !cv_debug) return false; if (event->type != ev_keydown) @@ -1240,10 +1285,8 @@ boolean F_CreditResponder(event_t *event) // ============ // EVALUATION // ============ -#define INTERVAL 50 -#define TRANSLEVEL V_80TRANS -static INT32 eemeralds_start; -static boolean drawemblem = false, drawchaosemblem = false; + +#define SPARKLLOOPTIME 7 // must be odd void F_StartGameEvaluation(void) { @@ -1265,76 +1308,122 @@ void F_StartGameEvaluation(void) if ((!modifiedgame || savemoddata) && !(netgame || multiplayer) && cursaveslot > 0) G_SaveGame((UINT32)cursaveslot); + goodending = (ALL7EMERALDS(emeralds)); + gameaction = ga_nothing; paused = false; CON_ToggleOff(); - finalecount = 0; + finalecount = -1; + sparklloop = 0; } void F_GameEvaluationDrawer(void) { INT32 x, y, i; - const fixed_t radius = 48*FRACUNIT; angle_t fa; INT32 eemeralds_cur; char patchname[7] = "CEMGx0"; + const char* endingtext = (goodending ? "CONGRATULATIONS!" : "TRY AGAIN..."); V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); // Draw all the good crap here. - if (ALL7EMERALDS(emeralds)) - V_DrawString(114, 16, 0, "GOT THEM ALL!"); - else - V_DrawString(124, 16, 0, "TRY AGAIN!"); - eemeralds_start++; - eemeralds_cur = eemeralds_start; - - for (i = 0; i < 7; ++i) + if (finalecount > 0) { - fa = (FixedAngle(eemeralds_cur*FRACUNIT)>>ANGLETOFINESHIFT) & FINEMASK; - x = 160 + FixedInt(FixedMul(FINECOSINE(fa),radius)); - y = 100 + FixedInt(FixedMul(FINESINE(fa),radius)); + INT32 scale = FRACUNIT; + patch_t *rockpat; + UINT8 *colormap[2] = {NULL, NULL}; + patch_t *glow; + INT32 trans = 0; - patchname[4] = 'A'+(char)i; - if (emeralds & (1<= 360) - eemeralds_start -= 360; - - if (finalecount == 5*TICRATE) - { - if ((!modifiedgame || savemoddata) && !(netgame || multiplayer)) + if (finalecount < 5) { - ++timesBeaten; + scale = (finalecount<<(FRACBITS-2)); + x += (30*(FRACUNIT-scale)); + y += (30*(FRACUNIT-scale)); + } - if (ALL7EMERALDS(emeralds)) - ++timesBeatenWithEmeralds; + if (goodending) + { + rockpat = W_CachePatchName(va("ROID00%.2d", 34 - (finalecount % 35)), PU_LEVEL); + glow = W_CachePatchName(va("ENDGLOW%.1d", 2+(finalecount & 1)), PU_LEVEL); + x -= FRACUNIT; + } + else + { + rockpat = W_CachePatchName("ROID0000", PU_LEVEL); + glow = W_CachePatchName(va("ENDGLOW%.1d", (finalecount & 1)), PU_LEVEL); + } - if (ultimatemode) - ++timesBeatenUltimate; + if (finalecount >= 5) + trans = (finalecount-5)>>1; + if (trans < 10) + V_DrawFixedPatch(x, y, scale, trans< (finalecount/SPARKLLOOPTIME)) + j = (finalecount/SPARKLLOOPTIME); + while (j) + { + if (j > 1 || sparklloop >= 2) + { + // if j == 0 - alternate between 0 and 1 + // 1 - 1 and 2 + // 2 - 2 and not rendered + V_DrawFixedPatch(x+sparkloffs[j-1][0], y+sparkloffs[j-1][1], FRACUNIT, 0, W_CachePatchName(va("ENDSPKL%.1d", (j - ((sparklloop & 1) ? 0 : 1))), PU_LEVEL), R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_AQUA, GTC_CACHE)); + } + j--; + } + } + else + { + patch_t *eggrock = W_CachePatchName("ENDEGRK5", PU_LEVEL); + V_DrawFixedPatch(x, y, scale, 0, eggrock, colormap[0]); + if (trans < 10) + V_DrawFixedPatch(x, y, scale, trans<>ANGLETOFINESHIFT) & FINEMASK; + x = (BASEVIDWIDTH<<(FRACBITS-1)) + (60*FINECOSINE(fa)); + y = ((BASEVIDHEIGHT+16)<<(FRACBITS-1)) + (60*FINESINE(fa)); + eemeralds_cur += (360<= 5*TICRATE) { - if (drawemblem) - V_DrawScaledPatch(120, 192, 0, W_CachePatchName("NWNGA0", PU_CACHE)); - - if (drawchaosemblem) - V_DrawScaledPatch(200, 192, 0, W_CachePatchName("NWNGA0", PU_CACHE)); - V_DrawString(8, 16, V_YELLOWMAP, "Unlocked:"); if (!(netgame) && (!modifiedgame || savemoddata)) @@ -1357,16 +1446,603 @@ void F_GameEvaluationDrawer(void) else V_DrawString(8, 96, V_YELLOWMAP, "Prizes not\nawarded in\nmodified games!"); } +#endif } void F_GameEvaluationTicker(void) { - finalecount++; - - if (finalecount > 10*TICRATE) + if (++finalecount > 10*TICRATE) + { F_StartGameEnd(); + return; + } + + if (!goodending) + { + if (sparklloop) + sparklloop--; + + if (finalecount == (5*TICRATE)/2 + || finalecount == (7*TICRATE)/2 + || finalecount == ((7*TICRATE)/2)+5) + { + S_StartSound(NULL, sfx_s3k5c); + sparklloop = 10; + } + } + else if (++sparklloop == SPARKLLOOPTIME) // time to roll the randomisation again + { + angle_t workingangle = FixedAngle((M_RandomKey(360))<>ANGLETOFINESHIFT; + fixed_t workingradius = M_RandomKey(26); + + sparkloffs[2][0] = sparkloffs[1][0]; + sparkloffs[2][1] = sparkloffs[1][1]; + sparkloffs[1][0] = sparkloffs[0][0]; + sparkloffs[1][1] = sparkloffs[0][1]; + + sparkloffs[0][0] = (30< 0) + G_SaveGame((UINT32)cursaveslot); + + gameaction = ga_nothing; + paused = false; + CON_ToggleOff(); + S_StopMusic(); // todo: placeholder + S_StopSounds(); + + finalecount = -10; // what? this totally isn't a hack. why are you asking? + + memset(sparkloffs, 0, sizeof(INT32)*3*2); + sparklloop = 0; + + endbrdr[1] = W_CachePatchName("ENDBRDR1", PU_LEVEL); + + endegrk[0] = W_CachePatchName("ENDEGRK0", PU_LEVEL); + endegrk[1] = W_CachePatchName("ENDEGRK1", PU_LEVEL); + + endglow[0] = W_CachePatchName("ENDGLOW0", PU_LEVEL); + endglow[1] = W_CachePatchName("ENDGLOW1", PU_LEVEL); + + endbgsp[0] = W_CachePatchName("ENDBGSP0", PU_LEVEL); + endbgsp[1] = W_CachePatchName("ENDBGSP1", PU_LEVEL); + endbgsp[2] = W_CachePatchName("ENDBGSP2", PU_LEVEL); + + endspkl[0] = W_CachePatchName("ENDSPKL0", PU_LEVEL); + endspkl[1] = W_CachePatchName("ENDSPKL1", PU_LEVEL); + endspkl[2] = W_CachePatchName("ENDSPKL2", PU_LEVEL); + + endxpld[0] = W_CachePatchName("ENDXPLD0", PU_LEVEL); + endxpld[1] = W_CachePatchName("ENDXPLD1", PU_LEVEL); + endxpld[2] = W_CachePatchName("ENDXPLD2", PU_LEVEL); + endxpld[3] = W_CachePatchName("ENDXPLD3", PU_LEVEL); + + endescp[0] = W_CachePatchName("ENDESCP0", PU_LEVEL); + endescp[1] = W_CachePatchName("ENDESCP1", PU_LEVEL); + endescp[2] = W_CachePatchName("ENDESCP2", PU_LEVEL); + endescp[3] = W_CachePatchName("ENDESCP3", PU_LEVEL); + endescp[4] = W_CachePatchName("ENDESCP4", PU_LEVEL); + + // so we only need to check once + if ((goodending = ALL7EMERALDS(emeralds))) + { + UINT8 skinnum = players[consoleplayer].skin; + spritedef_t *sprdef; + spriteframe_t *sprframe; + if (skins[skinnum].sprites[SPR2_XTRA].numframes >= 5) + { + sprdef = &skins[skinnum].sprites[SPR2_XTRA]; + // character head, skin specific + sprframe = &sprdef->spriteframes[2]; + endfwrk[0] = W_CachePatchNum(sprframe->lumppat[0], PU_LEVEL); + sprframe = &sprdef->spriteframes[3]; + endfwrk[1] = W_CachePatchNum(sprframe->lumppat[0], PU_LEVEL); + sprframe = &sprdef->spriteframes[4]; + endfwrk[2] = W_CachePatchNum(sprframe->lumppat[0], PU_LEVEL); + } + else // eh, yknow what? too lazy to put MISSINGs here. eggman wins if you don't give your character an ending firework display. + { + endfwrk[0] = W_CachePatchName("ENDFWRK0", PU_LEVEL); + endfwrk[1] = W_CachePatchName("ENDFWRK1", PU_LEVEL); + endfwrk[2] = W_CachePatchName("ENDFWRK2", PU_LEVEL); + } + + endbrdr[0] = W_CachePatchName("ENDBRDR2", PU_LEVEL); + } + else + { + // eggman, skin nonspecific + endfwrk[0] = W_CachePatchName("ENDFWRK0", PU_LEVEL); + endfwrk[1] = W_CachePatchName("ENDFWRK1", PU_LEVEL); + endfwrk[2] = W_CachePatchName("ENDFWRK2", PU_LEVEL); + + endbrdr[0] = W_CachePatchName("ENDBRDR0", PU_LEVEL); + } +} + +void F_EndingTicker(void) +{ + if (++finalecount > INFLECTIONPOINT*2) + { + F_StartCredits(); + wipetypepre = INT16_MAX; + return; + } + + if (goodending && finalecount == INFLECTIONPOINT) // time to swap some assets + { + endegrk[0] = W_CachePatchName("ENDEGRK2", PU_LEVEL); + endegrk[1] = W_CachePatchName("ENDEGRK3", PU_LEVEL); + + endglow[0] = W_CachePatchName("ENDGLOW2", PU_LEVEL); + endglow[1] = W_CachePatchName("ENDGLOW3", PU_LEVEL); + + endxpld[0] = W_CachePatchName("ENDEGRK4", PU_LEVEL); + } + + if (++sparklloop == SPARKLLOOPTIME) // time to roll the randomisation again + { + angle_t workingangle = FixedAngle((M_RandomRange(-170, 80))<>ANGLETOFINESHIFT; + fixed_t workingradius = M_RandomKey(26); + + sparkloffs[0][0] = (30< -19) + { + INT32 trans = (-parallaxticker)>>1; + if (trans < 0) + trans = 0; + V_DrawFixedPatch((200< 0) // gunchedrock + { + INT32 scale = FRACUNIT + ((parallaxticker-10)<<7); + INT32 trans = parallaxticker>>2; + UINT8 *colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_JET, GTC_CACHE); + + if (parallaxticker < 10) + { + tweakx = parallaxticker< 0) + { + i -= (3+(tweakx<<1)); + j += tweaky<<2; + } + + if (parallaxticker <= 70) // eggrock/blackrock + { + INT32 trans; + fixed_t scale = FRACUNIT; + UINT8 *colormap[2] = {NULL, NULL}; + + x += i; + y += j; + + if (parallaxticker > 66) + { + scale = ((70 - parallaxticker)<<(FRACBITS-2)); + x += (30*(FRACUNIT-scale)); + y += (30*(FRACUNIT-scale)); + } + else if ((parallaxticker > 60) || (goodending && parallaxticker > 0)) + ; + else + { + doexplosions = true; + if (!sparklloop) + { + x += ((sparkloffs[0][0] < 30< INFLECTIONPOINT) + parallaxticker -= 40; + + if ((-parallaxticker/4) < 5) + { + trans = (-parallaxticker/4) + 5; + if (trans < 0) + trans = 0; + V_DrawFixedPatch(x, y, scale, trans< INFLECTIONPOINT) + { + if (finalecount < INFLECTIONPOINT+10) + V_DrawFadeFill(24, 24, BASEVIDWIDTH-48, BASEVIDHEIGHT-48, 0, 0, INFLECTIONPOINT+10-finalecount); + parallaxticker -= 30; + } + + if ((parallaxticker/2) > -15) + colormap[0] = R_GetTranslationColormap(TC_ALLWHITE, 0, GTC_CACHE); + V_DrawFixedPatch(x, y, scale, 0, rockpat, colormap[0]); + if ((parallaxticker/2) > -25) + { + trans = (parallaxticker/2) + 15; + if (trans < 0) + trans = -trans; + if (trans < 10) + V_DrawFixedPatch(x, y, scale, trans< INFLECTIONPOINT) + { + if (finalecount < INFLECTIONPOINT+10) + V_DrawFixedPatch(x, y, scale, (finalecount-INFLECTIONPOINT)<= 3 && doexplosions) + { + INT32 boomtime = parallaxticker - sparklloop; + + x = ((((BASEVIDWIDTH-82)/2)+11)< INFLECTIONPOINT && finalecount < INFLECTIONPOINT+10) + V_DrawScaledPatch(BASEVIDWIDTH/2, BASEVIDHEIGHT/2, (finalecount-INFLECTIONPOINT)<= TICRATE && finalecount < INFLECTIONPOINT) + { + INT32 workingtime = finalecount - TICRATE; + fixed_t radius = ((vid.width/vid.dupx)*(INFLECTIONPOINT - TICRATE - workingtime))/(INFLECTIONPOINT - TICRATE); + angle_t fa; + INT32 eemeralds_cur[4]; + char patchname[7] = "CEMGx0"; + + radius <<= FRACBITS; + + for (i = 0; i < 4; ++i) + { + if (i == 1) + workingtime -= sparklloop; + else if (i) + workingtime -= SPARKLLOOPTIME; + eemeralds_cur[i] = (workingtime % 360)<>ANGLETOFINESHIFT) & FINEMASK; + x = (BASEVIDWIDTH<<(FRACBITS-1)) + FixedMul(FINECOSINE(fa),radius); + y = (BASEVIDHEIGHT<<(FRACBITS-1)) + FixedMul(FINESINE(fa),radius); + eemeralds_cur[j] += (360<>ANGLETOFINESHIFT) & FINEMASK; + x = (BASEVIDWIDTH<<(FRACBITS-1)) + FixedMul(FINECOSINE(fa),radius); + y = ((BASEVIDHEIGHT+16)<<(FRACBITS-1)) + FixedMul(FINESINE(fa),radius); + eemeralds_cur[0] += (360< 20) + + // look, i make an ending for you last-minute, the least you could do is let me have this + if (cv_soundtest.value == 413) + { + INT32 trans = 0; + boolean donttouch = false; + const char *str; + if (goodending) + str = va("[S] %s: Engage.", skins[players[consoleplayer].skin].realname); + else + str = "[S] Eggman: Abscond."; + + if (finalecount < 10) + trans = (10-finalecount)/2; + else if (finalecount > (2*INFLECTIONPOINT) - 20) + { + trans = 10 + (finalecount/2) - INFLECTIONPOINT; + donttouch = true; + } + + if (trans != 10) + { + //colset(linkmap, 164, 165, 169); -- the ideal purple colour to represent a clicked in-game link, but not worth it just for a soundtest-controlled secret + V_DrawCenteredString(BASEVIDWIDTH/2, 8, V_ALLOWLOWERCASE|(trans<'|(trans<= (2*INFLECTIONPOINT)-TICRATE) ? V_PURPLEMAP : V_BLUEMAP)|(trans<"); + } + + if (finalecount > (2*INFLECTIONPOINT)-(20+(2*TICRATE))) + { + INT32 trans2 = abs((5*FINECOSINE((FixedAngle((finalecount*5)<>ANGLETOFINESHIFT & FINEMASK)))>>FRACBITS)+2; + if (!donttouch) + { + trans = 10 + ((2*INFLECTIONPOINT)-(20+(2*TICRATE))) - finalecount; + if (trans > trans2) + trans2 = trans; + } + else + trans2 += 2*trans; + if (trans2 < 10) + V_DrawCharacter(26, BASEVIDHEIGHT-33, '\x1C'|(trans2<scene[scenenum].musswitchposition, 0, 0); else S_StopMusic(); + S_StopSounds(); } // @@ -2044,7 +2722,7 @@ void F_CutsceneDrawer(void) F_RunWipe(cutscenes[cutnum]->scene[scenenum].fadeoutid, true); } - V_DrawString(textxpos, textypos, 0, cutscene_disptext); + V_DrawString(textxpos, textypos, V_ALLOWLOWERCASE, cutscene_disptext); } void F_CutsceneTicker(void) diff --git a/src/f_finale.h b/src/f_finale.h index 29e9b4d8c..1149e1d5b 100644 --- a/src/f_finale.h +++ b/src/f_finale.h @@ -46,6 +46,9 @@ void F_GameEvaluationDrawer(void); void F_StartGameEvaluation(void); void F_GameEvaluationTicker(void); +void F_EndingTicker(void); +void F_EndingDrawer(void); + void F_CreditTicker(void); void F_CreditDrawer(void); @@ -63,6 +66,7 @@ boolean F_GetPromptHideHud(fixed_t y); void F_StartGameEnd(void); void F_StartIntro(void); void F_StartTitleScreen(void); +void F_StartEnding(void); void F_StartCredits(void); boolean F_ContinueResponder(event_t *event); @@ -125,6 +129,7 @@ enum wipe_evaluation_toblack, wipe_gameend_toblack, wipe_intro_toblack, + wipe_ending_toblack, wipe_cutscene_toblack, // custom intermissions @@ -141,15 +146,16 @@ enum wipe_evaluation_final, wipe_gameend_final, wipe_intro_final, + wipe_ending_final, wipe_cutscene_final, // custom intermissions wipe_specinter_final, wipe_multinter_final, - NUMWIPEDEFS + NUMWIPEDEFS, + WIPEFINALSHIFT = (wipe_level_final-wipe_level_toblack) }; -#define WIPEFINALSHIFT 13 extern UINT8 wipedefs[NUMWIPEDEFS]; #endif diff --git a/src/f_wipe.c b/src/f_wipe.c index 26c65ad91..05229f844 100644 --- a/src/f_wipe.c +++ b/src/f_wipe.c @@ -54,6 +54,7 @@ UINT8 wipedefs[NUMWIPEDEFS] = { 0, // wipe_evaluation_toblack 0, // wipe_gameend_toblack 99, // wipe_intro_toblack (hardcoded) + 0, // wipe_ending_toblack 99, // wipe_cutscene_toblack (hardcoded) 0, // wipe_specinter_toblack @@ -69,6 +70,7 @@ UINT8 wipedefs[NUMWIPEDEFS] = { 0, // wipe_evaluation_final 0, // wipe_gameend_final 99, // wipe_intro_final (hardcoded) + 0, // wipe_ending_final 99, // wipe_cutscene_final (hardcoded) 0, // wipe_specinter_final diff --git a/src/g_game.c b/src/g_game.c index 3cb5da1b5..dad873fe7 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -152,7 +152,7 @@ cutscene_t *cutscenes[128]; textprompt_t *textprompts[MAX_PROMPTS]; INT16 nextmapoverride; -boolean skipstats; +UINT8 skipstats; // Pointers to each CTF flag mobj_t *redflag; @@ -1842,7 +1842,7 @@ boolean G_Responder(event_t *ev) return true; } } - else if (gamestate == GS_CREDITS) + else if (gamestate == GS_CREDITS || gamestate == GS_ENDING) // todo: keep ending here? { if (HU_Responder(ev)) return true; // chat ate the event @@ -2032,6 +2032,12 @@ void G_Ticker(boolean run) F_IntroTicker(); break; + case GS_ENDING: + if (run) + F_EndingTicker(); + HU_Ticker(); + break; + case GS_CUTSCENE: if (run) F_CutsceneTicker(); @@ -2645,7 +2651,7 @@ void G_DoReborn(INT32 playernum) //nextmapoverride = spstage_start; nextmapoverride = gamemap; countdown2 = TICRATE; - skipstats = true; + skipstats = 2; for (i = 0; i < MAXPLAYERS; i++) { @@ -2849,6 +2855,10 @@ void G_ExitLevel(void) // Remove CEcho text on round end. HU_ClearCEcho(); } + else if (gamestate == GS_ENDING) + { + F_StartCredits(); + } else if (gamestate == GS_CREDITS) { F_StartGameEvaluation(); @@ -3116,7 +3126,7 @@ static void G_DoCompleted(void) nextmap = cm; } - if (nextmap < 0 || (nextmap >= NUMMAPS && nextmap < 1100-1) || nextmap > 1102-1) + if (nextmap < 0 || (nextmap >= NUMMAPS && nextmap < 1100-1) || nextmap > 1103-1) I_Error("Followed map %d to invalid map %d\n", prevmap + 1, nextmap + 1); // wrap around in race @@ -3170,7 +3180,7 @@ void G_AfterIntermission(void) { HU_ClearCEcho(); - if (mapheaderinfo[gamemap-1]->cutscenenum && !modeattacking) // Start a custom cutscene. + if (mapheaderinfo[gamemap-1]->cutscenenum && !modeattacking && skipstats <= 1) // Start a custom cutscene. F_StartCustomCutscene(mapheaderinfo[gamemap-1]->cutscenenum-1, false, false); else { @@ -3282,6 +3292,11 @@ void G_EndGame(void) // Only do evaluation and credits in coop games. if (gametype == GT_COOP) { + if (nextmap == 1103-1) // end game with ending + { + F_StartEnding(); + return; + } if (nextmap == 1102-1) // end game with credits { F_StartCredits(); @@ -3700,7 +3715,7 @@ void G_SaveGame(UINT32 slot) backup = va("%s",savename); // save during evaluation or credits? game's over, folks! - if (gamestate == GS_CREDITS || gamestate == GS_EVALUATION) + if (gamestate == GS_ENDING || gamestate == GS_CREDITS || gamestate == GS_EVALUATION) gamecomplete = true; gameaction = ga_nothing; diff --git a/src/g_game.h b/src/g_game.h index 96a192ee8..4b63bc180 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -56,6 +56,8 @@ extern INT16 rw_maximums[NUM_WEAPONS]; extern INT32 pausedelay; extern boolean pausebreakkey; +extern boolean promptactive; + // used in game menu extern consvar_t cv_tutorialprompt; extern consvar_t cv_chatwidth, cv_chatnotifications, cv_chatheight, cv_chattime, cv_consolechat, cv_chatbacktint, cv_chatspamprotection, cv_compactscoreboard; diff --git a/src/g_state.h b/src/g_state.h index 7ee0cfb63..9d34da6a9 100644 --- a/src/g_state.h +++ b/src/g_state.h @@ -27,12 +27,14 @@ typedef enum GS_TITLESCREEN, // title screen GS_TIMEATTACK, // time attack menu + GS_CREDITS, // credit sequence GS_EVALUATION, // Evaluation at the end of a game. - GS_GAMEEND, // game end sequence + GS_GAMEEND, // game end sequence - "did you get all those chaos emeralds?" // Hardcoded fades or other fading methods GS_INTRO, // introduction + GS_ENDING, // currently shared between bad and good endings GS_CUTSCENE, // custom cutscene // Not fadable diff --git a/src/hardware/hw_draw.c b/src/hardware/hw_draw.c index dd9fa8423..1ba357105 100644 --- a/src/hardware/hw_draw.c +++ b/src/hardware/hw_draw.c @@ -283,7 +283,7 @@ void HWR_DrawFixedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale, if (!(option & V_SCALEPATCHMASK)) { - // if it's meant to cover the whole screen, black out the rest + // if it's meant to cover the whole screen, black out the rest (ONLY IF TOP LEFT ISN'T TRANSPARENT) // cx and cy are possibly *slightly* off from float maths // This is done before here compared to software because we directly alter cx and cy to centre if (cx >= -0.1f && cx <= 0.1f && SHORT(gpatch->width) == BASEVIDWIDTH && cy >= -0.1f && cy <= 0.1f && SHORT(gpatch->height) == BASEVIDHEIGHT) @@ -291,8 +291,11 @@ void HWR_DrawFixedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale, // Need to temporarily cache the real patch to get the colour of the top left pixel patch_t *realpatch = W_CacheLumpNumPwad(gpatch->wadnum, gpatch->lumpnum, PU_STATIC); const column_t *column = (const column_t *)((const UINT8 *)(realpatch) + LONG((realpatch)->columnofs[0])); - const UINT8 *source = (const UINT8 *)(column) + 3; - HWR_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, (column->topdelta == 0xff ? 31 : source[0])); + if (!column->topdelta) + { + const UINT8 *source = (const UINT8 *)(column) + 3; + HWR_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, (column->topdelta == 0xff ? 31 : source[0])); + } Z_Free(realpatch); } // centre screen @@ -439,7 +442,7 @@ void HWR_DrawCroppedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscal if (!(option & V_SCALEPATCHMASK)) { - // if it's meant to cover the whole screen, black out the rest + // if it's meant to cover the whole screen, black out the rest (ONLY IF TOP LEFT ISN'T TRANSPARENT) // cx and cy are possibly *slightly* off from float maths // This is done before here compared to software because we directly alter cx and cy to centre if (cx >= -0.1f && cx <= 0.1f && SHORT(gpatch->width) == BASEVIDWIDTH && cy >= -0.1f && cy <= 0.1f && SHORT(gpatch->height) == BASEVIDHEIGHT) @@ -447,8 +450,11 @@ void HWR_DrawCroppedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscal // Need to temporarily cache the real patch to get the colour of the top left pixel patch_t *realpatch = W_CacheLumpNumPwad(gpatch->wadnum, gpatch->lumpnum, PU_STATIC); const column_t *column = (const column_t *)((const UINT8 *)(realpatch) + LONG((realpatch)->columnofs[0])); - const UINT8 *source = (const UINT8 *)(column) + 3; - HWR_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, (column->topdelta == 0xff ? 31 : source[0])); + if (!column->topdelta) + { + const UINT8 *source = (const UINT8 *)(column) + 3; + HWR_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, (column->topdelta == 0xff ? 31 : source[0])); + } Z_Free(realpatch); } // centre screen @@ -683,8 +689,183 @@ void HWR_FadeScreenMenuBack(UINT16 color, UINT8 strength) } else // Do TRANSMAP** fade. { - Surf.FlatColor.rgba = pLocalPalette[color].rgba; - Surf.FlatColor.s.alpha = (UINT8)(strength*25.5f); + Surf.FlatColor.rgba = V_GetColor(color).rgba; + Surf.FlatColor.s.alpha = softwaretranstogl[strength]; + } + HWD.pfnDrawPolygon(&Surf, v, 4, PF_NoTexture|PF_Modulated|PF_Translucent|PF_NoDepthTest); +} + +// -----------------+ +// HWR_DrawFadeFill : draw flat coloured rectangle, with transparency +// -----------------+ +void HWR_DrawFadeFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 color, UINT16 actualcolor, UINT8 strength) +{ + FOutVector v[4]; + FSurfaceInfo Surf; + float fx, fy, fw, fh; + + UINT8 perplayershuffle = 0; + +// 3--2 +// | /| +// |/ | +// 0--1 + + if (splitscreen && (color & V_PERPLAYER)) + { + fixed_t adjusty = ((color & V_NOSCALESTART) ? vid.height : BASEVIDHEIGHT)/2.0f; + h >>= 1; + y >>= 1; +#ifdef QUADS + if (splitscreen > 1) // 3 or 4 players + { + fixed_t adjustx = ((color & V_NOSCALESTART) ? vid.height : BASEVIDHEIGHT)/2.0f; + w >>= 1; + x >>= 1; + if (stplyr == &players[displayplayer]) + { + if (!(color & (V_SNAPTOTOP|V_SNAPTOBOTTOM))) + perplayershuffle |= 1; + if (!(color & (V_SNAPTOLEFT|V_SNAPTORIGHT))) + perplayershuffle |= 4; + color &= ~V_SNAPTOBOTTOM|V_SNAPTORIGHT; + } + else if (stplyr == &players[secondarydisplayplayer]) + { + if (!(color & (V_SNAPTOTOP|V_SNAPTOBOTTOM))) + perplayershuffle |= 1; + if (!(color & (V_SNAPTOLEFT|V_SNAPTORIGHT))) + perplayershuffle |= 8; + x += adjustx; + color &= ~V_SNAPTOBOTTOM|V_SNAPTOLEFT; + } + else if (stplyr == &players[thirddisplayplayer]) + { + if (!(color & (V_SNAPTOTOP|V_SNAPTOBOTTOM))) + perplayershuffle |= 2; + if (!(color & (V_SNAPTOLEFT|V_SNAPTORIGHT))) + perplayershuffle |= 4; + y += adjusty; + color &= ~V_SNAPTOTOP|V_SNAPTORIGHT; + } + else //if (stplyr == &players[fourthdisplayplayer]) + { + if (!(color & (V_SNAPTOTOP|V_SNAPTOBOTTOM))) + perplayershuffle |= 2; + if (!(color & (V_SNAPTOLEFT|V_SNAPTORIGHT))) + perplayershuffle |= 8; + x += adjustx; + y += adjusty; + color &= ~V_SNAPTOTOP|V_SNAPTOLEFT; + } + } + else +#endif + // 2 players + { + if (stplyr == &players[displayplayer]) + { + if (!(color & (V_SNAPTOTOP|V_SNAPTOBOTTOM))) + perplayershuffle |= 1; + color &= ~V_SNAPTOBOTTOM; + } + else //if (stplyr == &players[secondarydisplayplayer]) + { + if (!(color & (V_SNAPTOTOP|V_SNAPTOBOTTOM))) + perplayershuffle |= 2; + y += adjusty; + color &= ~V_SNAPTOTOP; + } + } + } + + fx = (float)x; + fy = (float)y; + fw = (float)w; + fh = (float)h; + + if (!(color & V_NOSCALESTART)) + { + float dupx = (float)vid.dupx, dupy = (float)vid.dupy; + + fx *= dupx; + fy *= dupy; + fw *= dupx; + fh *= dupy; + + if (fabsf((float)vid.width - (float)BASEVIDWIDTH * dupx) > 1.0E-36f) + { + if (color & V_SNAPTORIGHT) + fx += ((float)vid.width - ((float)BASEVIDWIDTH * dupx)); + else if (!(color & V_SNAPTOLEFT)) + fx += ((float)vid.width - ((float)BASEVIDWIDTH * dupx)) / 2; + if (perplayershuffle & 4) + fx -= ((float)vid.width - ((float)BASEVIDWIDTH * dupx)) / 4; + else if (perplayershuffle & 8) + fx += ((float)vid.width - ((float)BASEVIDWIDTH * dupx)) / 4; + } + if (fabsf((float)vid.height - (float)BASEVIDHEIGHT * dupy) > 1.0E-36f) + { + // same thing here + if (color & V_SNAPTOBOTTOM) + fy += ((float)vid.height - ((float)BASEVIDHEIGHT * dupy)); + else if (!(color & V_SNAPTOTOP)) + fy += ((float)vid.height - ((float)BASEVIDHEIGHT * dupy)) / 2; + if (perplayershuffle & 1) + fy -= ((float)vid.height - ((float)BASEVIDHEIGHT * dupy)) / 4; + else if (perplayershuffle & 2) + fy += ((float)vid.height - ((float)BASEVIDHEIGHT * dupy)) / 4; + } + } + + if (fx >= vid.width || fy >= vid.height) + return; + if (fx < 0) + { + fw += fx; + fx = 0; + } + if (fy < 0) + { + fh += fy; + fy = 0; + } + + if (fw <= 0 || fh <= 0) + return; + if (fx + fw > vid.width) + fw = (float)vid.width - fx; + if (fy + fh > vid.height) + fh = (float)vid.height - fy; + + fx = -1 + fx / (vid.width / 2); + fy = 1 - fy / (vid.height / 2); + fw = fw / (vid.width / 2); + fh = fh / (vid.height / 2); + + v[0].x = v[3].x = fx; + v[2].x = v[1].x = fx + fw; + v[0].y = v[1].y = fy; + v[2].y = v[3].y = fy - fh; + + //Hurdler: do we still use this argb color? if not, we should remove it + v[0].argb = v[1].argb = v[2].argb = v[3].argb = 0xff00ff00; //; + v[0].z = v[1].z = v[2].z = v[3].z = 1.0f; + + v[0].sow = v[3].sow = 0.0f; + v[2].sow = v[1].sow = 1.0f; + v[0].tow = v[1].tow = 0.0f; + v[2].tow = v[3].tow = 1.0f; + + if (actualcolor & 0xFF00) // Do COLORMAP fade. + { + Surf.FlatColor.rgba = UINT2RGBA(0x01010160); + Surf.FlatColor.s.alpha = (strength*8); + } + else // Do TRANSMAP** fade. + { + Surf.FlatColor.rgba = V_GetColor(actualcolor).rgba; + Surf.FlatColor.s.alpha = softwaretranstogl[strength]; } HWD.pfnDrawPolygon(&Surf, v, 4, PF_NoTexture|PF_Modulated|PF_Translucent|PF_NoDepthTest); } @@ -905,54 +1086,117 @@ void HWR_DrawConsoleFill(INT32 x, INT32 y, INT32 w, INT32 h, UINT32 color, INT32 FSurfaceInfo Surf; float fx, fy, fw, fh; - if (w < 0 || h < 0) - return; // consistency w/ software + UINT8 perplayershuffle = 0; // 3--2 // | /| // |/ | // 0--1 + if (splitscreen && (color & V_PERPLAYER)) + { + fixed_t adjusty = ((color & V_NOSCALESTART) ? vid.height : BASEVIDHEIGHT)/2.0f; + h >>= 1; + y >>= 1; +#ifdef QUADS + if (splitscreen > 1) // 3 or 4 players + { + fixed_t adjustx = ((color & V_NOSCALESTART) ? vid.height : BASEVIDHEIGHT)/2.0f; + w >>= 1; + x >>= 1; + if (stplyr == &players[displayplayer]) + { + if (!(color & (V_SNAPTOTOP|V_SNAPTOBOTTOM))) + perplayershuffle |= 1; + if (!(color & (V_SNAPTOLEFT|V_SNAPTORIGHT))) + perplayershuffle |= 4; + color &= ~V_SNAPTOBOTTOM|V_SNAPTORIGHT; + } + else if (stplyr == &players[secondarydisplayplayer]) + { + if (!(color & (V_SNAPTOTOP|V_SNAPTOBOTTOM))) + perplayershuffle |= 1; + if (!(color & (V_SNAPTOLEFT|V_SNAPTORIGHT))) + perplayershuffle |= 8; + x += adjustx; + color &= ~V_SNAPTOBOTTOM|V_SNAPTOLEFT; + } + else if (stplyr == &players[thirddisplayplayer]) + { + if (!(color & (V_SNAPTOTOP|V_SNAPTOBOTTOM))) + perplayershuffle |= 2; + if (!(color & (V_SNAPTOLEFT|V_SNAPTORIGHT))) + perplayershuffle |= 4; + y += adjusty; + color &= ~V_SNAPTOTOP|V_SNAPTORIGHT; + } + else //if (stplyr == &players[fourthdisplayplayer]) + { + if (!(color & (V_SNAPTOTOP|V_SNAPTOBOTTOM))) + perplayershuffle |= 2; + if (!(color & (V_SNAPTOLEFT|V_SNAPTORIGHT))) + perplayershuffle |= 8; + x += adjustx; + y += adjusty; + color &= ~V_SNAPTOTOP|V_SNAPTOLEFT; + } + } + else +#endif + // 2 players + { + if (stplyr == &players[displayplayer]) + { + if (!(color & (V_SNAPTOTOP|V_SNAPTOBOTTOM))) + perplayershuffle |= 1; + color &= ~V_SNAPTOBOTTOM; + } + else //if (stplyr == &players[secondarydisplayplayer]) + { + if (!(color & (V_SNAPTOTOP|V_SNAPTOBOTTOM))) + perplayershuffle |= 2; + y += adjusty; + color &= ~V_SNAPTOTOP; + } + } + } + fx = (float)x; fy = (float)y; fw = (float)w; fh = (float)h; - if (!(options & V_NOSCALESTART)) + if (!(color & V_NOSCALESTART)) { float dupx = (float)vid.dupx, dupy = (float)vid.dupy; - if (x == 0 && y == 0 && w == BASEVIDWIDTH && h == BASEVIDHEIGHT) - { - RGBA_t rgbaColour = V_GetColor(color); - FRGBAFloat clearColour; - clearColour.red = (float)rgbaColour.s.red / 255; - clearColour.green = (float)rgbaColour.s.green / 255; - clearColour.blue = (float)rgbaColour.s.blue / 255; - clearColour.alpha = 1; - HWD.pfnClearBuffer(true, false, &clearColour); - return; - } - fx *= dupx; fy *= dupy; fw *= dupx; fh *= dupy; - if (fabsf((float)vid.width - ((float)BASEVIDWIDTH * dupx)) > 1.0E-36f) + if (fabsf((float)vid.width - (float)BASEVIDWIDTH * dupx) > 1.0E-36f) { - if (options & V_SNAPTORIGHT) + if (color & V_SNAPTORIGHT) fx += ((float)vid.width - ((float)BASEVIDWIDTH * dupx)); - else if (!(options & V_SNAPTOLEFT)) + else if (!(color & V_SNAPTOLEFT)) fx += ((float)vid.width - ((float)BASEVIDWIDTH * dupx)) / 2; + if (perplayershuffle & 4) + fx -= ((float)vid.width - ((float)BASEVIDWIDTH * dupx)) / 4; + else if (perplayershuffle & 8) + fx += ((float)vid.width - ((float)BASEVIDWIDTH * dupx)) / 4; } - if (fabsf((float)vid.height - ((float)BASEVIDHEIGHT * dupy)) > 1.0E-36f) + if (fabsf((float)vid.height - (float)BASEVIDHEIGHT * dupy) > 1.0E-36f) { // same thing here - if (options & V_SNAPTOBOTTOM) + if (color & V_SNAPTOBOTTOM) fy += ((float)vid.height - ((float)BASEVIDHEIGHT * dupy)); - else if (!(options & V_SNAPTOTOP)) + else if (!(color & V_SNAPTOTOP)) fy += ((float)vid.height - ((float)BASEVIDHEIGHT * dupy)) / 2; + if (perplayershuffle & 1) + fy -= ((float)vid.height - ((float)BASEVIDHEIGHT * dupy)) / 4; + else if (perplayershuffle & 2) + fy += ((float)vid.height - ((float)BASEVIDHEIGHT * dupy)) / 4; } } @@ -1012,9 +1256,6 @@ void HWR_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 color) UINT8 perplayershuffle = 0; - if (w < 0 || h < 0) - return; // consistency w/ software - // 3--2 // | /| // |/ | diff --git a/src/hardware/hw_main.h b/src/hardware/hw_main.h index fdfc1d257..fab18e08a 100644 --- a/src/hardware/hw_main.h +++ b/src/hardware/hw_main.h @@ -49,6 +49,7 @@ void HWR_CreatePlanePolygons(INT32 bspnum); void HWR_CreateStaticLightmaps(INT32 bspnum); void HWR_PrepLevelCache(size_t pnumtextures); void HWR_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 color); +void HWR_DrawFadeFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 color, UINT16 actualcolor, UINT8 strength); void HWR_DrawConsoleFill(INT32 x, INT32 y, INT32 w, INT32 h, UINT32 color, INT32 options); // Lat: separate flags from color since color needs to be an uint to work right. void HWR_DrawPic(INT32 x,INT32 y,lumpnum_t lumpnum); diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c index e26aa98ff..c2faa8b86 100644 --- a/src/hardware/hw_md2.c +++ b/src/hardware/hw_md2.c @@ -1198,6 +1198,9 @@ static UINT8 P_GetModelSprite2(md2_t *md2, skin_t *skin, UINT8 spr2, player_t *p if (!md2 || !skin) return 0; + if ((spr2 & ~FF_SPR2SUPER) >= free_spr2) + return 0; + while (!(md2->model->spr2frames[spr2*2 + 1]) && spr2 != SPR2_STND && ++i != 32) // recursion limiter @@ -1220,7 +1223,10 @@ static UINT8 P_GetModelSprite2(md2_t *md2, skin_t *skin, UINT8 spr2, player_t *p & SF_NOJUMPSPIN) ? SPR2_SPNG : SPR2_ROLL; break; case SPR2_TIRE: - spr2 = (player && player->charability == CA_SWIM) ? SPR2_SWIM : SPR2_FLY; + spr2 = ((player + ? player->charability + : skin->ability) + == CA_SWIM) ? SPR2_SWIM : SPR2_FLY; break; // Use the handy list, that's what it's there for! @@ -1232,6 +1238,9 @@ static UINT8 P_GetModelSprite2(md2_t *md2, skin_t *skin, UINT8 spr2, player_t *p spr2 |= super; } + if (i >= 32) // probably an infinite loop... + return 0; + return spr2; } diff --git a/src/hu_stuff.c b/src/hu_stuff.c index f86100e27..3bc643c3c 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -2138,7 +2138,7 @@ void HU_Drawer(void) if (!Playing() || gamestate == GS_INTERMISSION || gamestate == GS_CUTSCENE || gamestate == GS_CREDITS || gamestate == GS_EVALUATION - || gamestate == GS_GAMEEND) + || gamestate == GS_ENDING || gamestate == GS_GAMEEND) return; // draw multiplayer rankings diff --git a/src/info.c b/src/info.c index 0ae723546..d35be7b58 100644 --- a/src/info.c +++ b/src/info.c @@ -578,7 +578,8 @@ char spr2names[NUMPLAYERSPRITES][5] = "TALB", "SIGN", - "LIFE" + "LIFE", + "XTRA", }; playersprite_t free_spr2 = SPR2_FIRSTFREESLOT; @@ -595,7 +596,7 @@ playersprite_t spr2defaults[NUMPLAYERSPRITES] = { SPR2_DEAD, // SPR2_DRWN, 0, // SPR2_ROLL, SPR2_SPNG, // SPR2_GASP, - 0, // SPR2_JUMP, (conditional) + 0, // SPR2_JUMP, (conditional, will never be referenced) SPR2_FALL, // SPR2_SPNG, SPR2_WALK, // SPR2_FALL, 0, // SPR2_EDGE, @@ -605,7 +606,7 @@ playersprite_t spr2defaults[NUMPLAYERSPRITES] = { SPR2_SPNG, // SPR2_FLY , SPR2_FLY , // SPR2_SWIM, - 0, // SPR2_TIRE, (conditional) + 0, // SPR2_TIRE, (conditional, will never be referenced) SPR2_FLY , // SPR2_GLID, SPR2_CLMB, // SPR2_CLNG, @@ -632,7 +633,7 @@ playersprite_t spr2defaults[NUMPLAYERSPRITES] = { SPR2_NSTN, // SPR2_NPUL, FF_SPR2SUPER|SPR2_ROLL, // SPR2_NATK, - 0, // SPR2_NGT0, (should never be referenced) + 0, // SPR2_NGT0, (will never be referenced unless skin 0 lacks this) SPR2_NGT0, // SPR2_NGT1, SPR2_NGT1, // SPR2_NGT2, SPR2_NGT2, // SPR2_NGT3, @@ -660,7 +661,7 @@ playersprite_t spr2defaults[NUMPLAYERSPRITES] = { SPR2_NGTB, // SPR2_DRLB, SPR2_NGTC, // SPR2_DRLC, - 0, // SPR2_TAL0, + 0, // SPR2_TAL0, (this will look mighty stupid but oh well) SPR2_TAL0, // SPR2_TAL1, SPR2_TAL1, // SPR2_TAL2, SPR2_TAL2, // SPR2_TAL3, @@ -675,6 +676,7 @@ playersprite_t spr2defaults[NUMPLAYERSPRITES] = { 0, // SPR2_SIGN, 0, // SPR2_LIFE, + 0, // SPR2_XTRA (should never be referenced) }; // Doesn't work with g++, needs actionf_p1 (don't modify this comment) diff --git a/src/info.h b/src/info.h index a9a0321ec..45c1d79b2 100644 --- a/src/info.h +++ b/src/info.h @@ -834,6 +834,7 @@ typedef enum playersprite SPR2_SIGN, // end sign head SPR2_LIFE, // life monitor icon + SPR2_XTRA, // stuff that isn't in-game - keep this last in the list SPR2_FIRSTFREESLOT, SPR2_LASTFREESLOT = 0x7f, diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 7d3ab2140..3c136a436 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -2609,12 +2609,12 @@ static int lib_gSetCustomExitVars(lua_State *L) nextmapoverride = (INT16)luaL_checknumber(L, 1); lua_remove(L, 1); // remove nextmapoverride; skipstats now 1 if available } - skipstats = lua_optboolean(L, 1); + skipstats = luaL_optinteger(L, 2, 0); } else { nextmapoverride = 0; - skipstats = false; + skipstats = 0; } // --- diff --git a/src/lua_infolib.c b/src/lua_infolib.c index 55afa3874..8bd4ce9ff 100644 --- a/src/lua_infolib.c +++ b/src/lua_infolib.c @@ -157,6 +157,18 @@ static int lib_setSpr2default(lua_State *L) playersprite_t i; UINT8 j = 0; + if (hud_running) + return luaL_error(L, "Do not alter spr2defaults[] in HUD rendering code!"); + +// todo: maybe allow setting below first freeslot..? step 1 is toggling this, step 2 is testing to see whether it's net-safe +#ifdef SETALLSPR2DEFAULTS +#define FIRSTMODIFY 0 +#else +#define FIRSTMODIFY SPR2_FIRSTFREESLOT + if (free_spr2 == SPR2_FIRSTFREESLOT) + return luaL_error(L, "You can only modify the spr2defaults[] entries of sprite2 freeslots, and none are currently added."); +#endif + lua_remove(L, 1); // don't care about spr2defaults[] dummy userdata. if (lua_isnumber(L, 1)) @@ -175,8 +187,9 @@ static int lib_setSpr2default(lua_State *L) else return luaL_error(L, "spr2defaults[] invalid index"); - if (i < SPR2_FIRSTFREESLOT || i >= free_spr2) - return luaL_error(L, "spr2defaults[] index %d out of range (%d - %d)", i, SPR2_FIRSTFREESLOT, free_spr2-1); + if (i < FIRSTMODIFY || i >= free_spr2) + return luaL_error(L, "spr2defaults[] index %d out of range (%d - %d)", i, FIRSTMODIFY, free_spr2-1); +#undef FIRSTMODIFY if (lua_isnumber(L, 2)) j = lua_tonumber(L, 2); @@ -189,11 +202,13 @@ static int lib_setSpr2default(lua_State *L) break; } if (j == free_spr2) - return luaL_error(L, "spr2defaults[] invalid index"); + return luaL_error(L, "spr2defaults[] invalid set"); } + else + return luaL_error(L, "spr2defaults[] invalid set"); - if (j >= free_spr2) - j = 0; // return luaL_error(L, "spr2defaults[] set %d out of range (%d - %d)", j, 0, free_spr2-1); + if (j < 0 || j >= free_spr2) + return luaL_error(L, "spr2defaults[] set %d out of range (%d - %d)", j, 0, free_spr2-1); spr2defaults[i] = j; return 0; diff --git a/src/lua_skinlib.c b/src/lua_skinlib.c index a8f785c5a..a28f6a359 100644 --- a/src/lua_skinlib.c +++ b/src/lua_skinlib.c @@ -27,9 +27,6 @@ enum skin { skin_flags, skin_realname, skin_hudname, - skin_charsel, - skin_face, - skin_superface, skin_ability, skin_ability2, skin_thokitem, @@ -66,9 +63,6 @@ static const char *const skin_opt[] = { "flags", "realname", "hudname", - "charsel", - "face", - "superface", "ability", "ability2", "thokitem", @@ -104,7 +98,6 @@ static int skin_get(lua_State *L) { skin_t *skin = *((skin_t **)luaL_checkudata(L, 1, META_SKIN)); enum skin field = luaL_checkoption(L, 2, NULL, skin_opt); - INT32 i; // skins are always valid, only added, never removed I_Assert(skin != NULL); @@ -131,24 +124,6 @@ static int skin_get(lua_State *L) case skin_hudname: lua_pushstring(L, skin->hudname); break; - case skin_charsel: - for (i = 0; i < 8; i++) - if (!skin->charsel[i]) - break; - lua_pushlstring(L, skin->charsel, i); - break; - case skin_face: - for (i = 0; i < 8; i++) - if (!skin->face[i]) - break; - lua_pushlstring(L, skin->face, i); - break; - case skin_superface: - for (i = 0; i < 8; i++) - if (!skin->superface[i]) - break; - lua_pushlstring(L, skin->superface, i); - break; case skin_ability: lua_pushinteger(L, skin->ability); break; diff --git a/src/m_cond.c b/src/m_cond.c index e03542bf3..539c6d1f6 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -240,7 +240,7 @@ UINT8 M_UpdateUnlockablesAndExtraEmblems(void) if (cechoLines) { char slashed[1024] = ""; - for (i = 0; (i < 21) && (i < 24 - cechoLines); ++i) + for (i = 0; (i < 19) && (i < 24 - cechoLines); ++i) slashed[i] = '\\'; slashed[i] = 0; diff --git a/src/m_menu.c b/src/m_menu.c index d9d3a6ba0..38326257b 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -2810,8 +2810,8 @@ boolean M_Responder(event_t *ev) void (*routine)(INT32 choice); // for some casting problem if (dedicated || (demoplayback && titledemo) - || gamestate == GS_INTRO || gamestate == GS_CUTSCENE || gamestate == GS_GAMEEND - || gamestate == GS_CREDITS || gamestate == GS_EVALUATION) + || gamestate == GS_INTRO || gamestate == GS_ENDING || gamestate == GS_CUTSCENE + || gamestate == GS_CREDITS || gamestate == GS_EVALUATION || gamestate == GS_GAMEEND) return false; if (noFurtherInput) @@ -3511,6 +3511,7 @@ void M_InitCharacterTables(void) strcpy(description[i].picname, ""); strcpy(description[i].skinname, ""); description[i].prev = description[i].next = 0; + description[i].pic = NULL; } } @@ -7557,8 +7558,19 @@ static void M_SetupChoosePlayer(INT32 choice) if (i == char_on) allowed = true; - if (description[i].picname[0] == '\0') - strncpy(description[i].picname, skins[skinnum].charsel, 8); + if (!(description[i].picname[0])) + { + if (skins[skinnum].sprites[SPR2_XTRA].numframes >= 2) + { + spritedef_t *sprdef = &skins[skinnum].sprites[SPR2_XTRA]; + spriteframe_t *sprframe = &sprdef->spriteframes[1]; + description[i].pic = W_CachePatchNum(sprframe->lumppat[0], PU_CACHE); + } + else + description[i].pic = W_CachePatchName("MISSING", PU_CACHE); + } + else + description[i].pic = W_CachePatchName(description[i].picname, PU_CACHE); } // else -- Technically, character select icons without corresponding skins get bundled away behind this too. Sucks to be them. Z_Free(name); @@ -7712,7 +7724,7 @@ static void M_DrawSetupChoosePlayerMenu(void) // Draw prev character if it's visible and its number isn't greater than the current one or there's more than two if (o < 32) { - patch = W_CachePatchName(description[prev].picname, PU_CACHE); + patch = description[prev].pic; if (SHORT(patch->width) >= 256) V_DrawCroppedPatch(8<height) + 2*(o-32), SHORT(patch->width), 64 - 2*o); else @@ -7723,7 +7735,7 @@ static void M_DrawSetupChoosePlayerMenu(void) // Draw next character if it's visible and its number isn't less than the current one or there's more than two if (o < 128) // (next != i) was previously a part of this, but it's implicitly true if (prev != i) is true. { - patch = W_CachePatchName(description[next].picname, PU_CACHE); + patch = description[next].pic; if (SHORT(patch->width) >= 256) V_DrawCroppedPatch(8<width), 2*o); else @@ -7732,7 +7744,7 @@ static void M_DrawSetupChoosePlayerMenu(void) } } - patch = W_CachePatchName(description[i].picname, PU_CACHE); + patch = description[i].pic; if (o >= 0 && o <= 32) { if (SHORT(patch->width) >= 256) @@ -8124,9 +8136,16 @@ void M_DrawTimeAttackMenu(void) V_DrawString(currentMenu->x, cursory, V_YELLOWMAP, currentMenu->menuitems[itemOn].text); // Character face! - if (W_CheckNumForName(skins[cv_chooseskin.value-1].charsel) != LUMPERROR) { - PictureOfUrFace = W_CachePatchName(skins[cv_chooseskin.value-1].charsel, PU_CACHE); + if (skins[cv_chooseskin.value-1].sprites[SPR2_XTRA].numframes >= 2) + { + spritedef_t *sprdef = &skins[cv_chooseskin.value-1].sprites[SPR2_XTRA]; + spriteframe_t *sprframe = &sprdef->spriteframes[1]; + PictureOfUrFace = W_CachePatchNum(sprframe->lumppat[0], PU_CACHE); + } + else + PictureOfUrFace = W_CachePatchName("MISSING", PU_CACHE); + if (PictureOfUrFace->width >= 256) V_DrawTinyScaledPatch(224, 120, 0, PictureOfUrFace); else diff --git a/src/m_menu.h b/src/m_menu.h index 04146ebdc..347725e10 100644 --- a/src/m_menu.h +++ b/src/m_menu.h @@ -316,6 +316,7 @@ typedef struct char notes[441]; char picname[8]; char skinname[SKINNAMESIZE*2+2]; // skin&skin\0 + patch_t *pic; UINT8 prev; UINT8 next; } description_t; diff --git a/src/p_mobj.c b/src/p_mobj.c index eac90f5f0..8c74fd76e 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -11110,6 +11110,12 @@ You should think about modifying the deathmatch starts to take full advantage of else skyboxviewpnts[mthing->extrainfo] = mobj; break; + case MT_EGGSTATUE: + if (tutorialmode != (mthing->options & MTF_OBJECTSPECIAL)) + { + mobj->color = SKINCOLOR_GOLD; + mobj->colorized = true; + } case MT_EGGMOBILE3: mobj->cusval = mthing->extrainfo; break; diff --git a/src/p_setup.c b/src/p_setup.c index 15576bbe8..c2872a836 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -3117,7 +3117,7 @@ boolean P_SetupLevel(boolean skipprecip) R_PrecacheLevel(); nextmapoverride = 0; - skipstats = false; + skipstats = 0; if (!(netgame || multiplayer) && (!modifiedgame || savemoddata)) mapvisited[gamemap-1] |= MV_VISITED; @@ -3428,13 +3428,13 @@ boolean P_AddWadFile(const char *wadfilename) ST_UnloadGraphics(); HU_LoadGraphics(); ST_LoadGraphics(); - ST_ReloadSkinFaceGraphics(); // // look for skins // R_AddSkins(wadnum); // faB: wadfile index in wadfiles[] R_PatchSkins(wadnum); // toast: PATCH PATCH + ST_ReloadSkinFaceGraphics(); // // search for maps diff --git a/src/p_spec.c b/src/p_spec.c index e03b661a3..23ab04fc7 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -4662,7 +4662,7 @@ DoneSection2: nextmapoverride = (INT16)(lines[lineindex].frontsector->floorheight>>FRACBITS); if (lines[lineindex].flags & ML_NOCLIMB) - skipstats = true; + skipstats = 1; } } break; diff --git a/src/r_things.c b/src/r_things.c index c791571ac..71e4c0214 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -486,7 +486,11 @@ void R_InitSprites(void) // it can be is do before loading config for skin cvar possible value R_InitSkins(); for (i = 0; i < numwadfiles; i++) + { R_AddSkins((UINT16)i); + R_PatchSkins((UINT16)i); + } + ST_ReloadSkinFaceGraphics(); // // check if all sprites have frames @@ -2507,6 +2511,9 @@ UINT8 P_GetSkinSprite2(skin_t *skin, UINT8 spr2, player_t *player) if (!skin) return 0; + if ((spr2 & ~FF_SPR2SUPER) >= free_spr2) + return 0; + while (!(skin->sprites[spr2].numframes) && spr2 != SPR2_STND && ++i < 32) // recursion limiter @@ -2529,8 +2536,10 @@ UINT8 P_GetSkinSprite2(skin_t *skin, UINT8 spr2, player_t *player) & SF_NOJUMPSPIN) ? SPR2_SPNG : SPR2_ROLL; break; case SPR2_TIRE: - spr2 = (player && player->charability == CA_SWIM) ? SPR2_SWIM : SPR2_FLY; - break; + spr2 = ((player + ? player->charability + : skin->ability) + == CA_SWIM) ? SPR2_SWIM : SPR2_FLY; // Use the handy list, that's what it's there for! default: @@ -2541,6 +2550,9 @@ UINT8 P_GetSkinSprite2(skin_t *skin, UINT8 spr2, player_t *player) spr2 |= super; } + if (i >= 32) // probably an infinite loop... + return 0; + return spr2; } @@ -2560,9 +2572,6 @@ static void Sk_SetDefaultValue(skin_t *skin) strcpy(skin->realname, "Someone"); strcpy(skin->hudname, "???"); - strncpy(skin->charsel, "CHRSONIC", 9); - strncpy(skin->face, "MISSING", 8); - strncpy(skin->superface, "MISSING", 8); skin->starttranscolor = 96; skin->prefcolor = SKINCOLOR_GREEN; @@ -2992,7 +3001,7 @@ void R_AddSkins(UINT16 wadnum) char *value; size_t size; skin_t *skin; - boolean hudname, realname, superface; + boolean hudname, realname; // // search for all skin markers in pwad @@ -3022,7 +3031,7 @@ void R_AddSkins(UINT16 wadnum) skin = &skins[numskins]; Sk_SetDefaultValue(skin); skin->wadnum = wadnum; - hudname = realname = superface = false; + hudname = realname = false; // parse stoken = strtok (buf2, "\r\n= "); while (stoken) @@ -3098,24 +3107,6 @@ void R_AddSkins(UINT16 wadnum) if (!realname) STRBUFCPY(skin->realname, skin->hudname); } - else if (!stricmp(stoken, "charsel")) - { - strupr(value); - strncpy(skin->charsel, value, sizeof skin->charsel); - } - else if (!stricmp(stoken, "face")) - { - strupr(value); - strncpy(skin->face, value, sizeof skin->face); - if (!superface) - strncpy(skin->superface, value, sizeof skin->superface); - } - else if (!stricmp(stoken, "superface")) - { - superface = true; - strupr(value); - strncpy(skin->superface, value, sizeof skin->superface); - } else if (!stricmp(stoken, "availability")) { skin->availability = atoi(value); @@ -3134,6 +3125,7 @@ next_token: // Add sprites R_LoadSkinSprites(wadnum, &lump, &lastlump, skin); + //ST_LoadFaceGraphics(numskins); -- nah let's do this elsewhere R_FlushTranslationColormapCache(); @@ -3144,9 +3136,6 @@ next_token: skin_cons_t[numskins].strvalue = skin->name; #endif - // add face graphics - ST_LoadFaceGraphics(skin->face, skin->superface, numskins); - #ifdef HWRENDER if (rendermode == render_opengl) HWR_AddPlayerMD2(numskins); @@ -3269,6 +3258,9 @@ next_token: // Patch sprites R_LoadSkinSprites(wadnum, &lump, &lastlump, skin); + //ST_LoadFaceGraphics(skinnum); -- nah let's do this elsewhere + + R_FlushTranslationColormapCache(); if (!skin->availability) // Safe to print... CONS_Printf(M_GetText("Patched skin '%s'\n"), skin->name); diff --git a/src/r_things.h b/src/r_things.h index d287df832..9c3d16ab0 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -88,7 +88,6 @@ typedef struct char realname[SKINNAMESIZE+1]; // Display name for level completion. char hudname[SKINNAMESIZE+1]; // HUD name to display (officially exactly 5 characters long) - char charsel[9], face[9], superface[9]; // Arbitrarily named patch lumps UINT8 ability; // ability definition UINT8 ability2; // secondary ability definition diff --git a/src/s_sound.c b/src/s_sound.c index 66b5d4f72..61f93766c 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -519,6 +519,7 @@ void S_StartCaption(sfxenum_t sfx_id, INT32 cnum, UINT16 lifespan) void S_StartSoundAtVolume(const void *origin_p, sfxenum_t sfx_id, INT32 volume) { INT32 sep, pitch, priority, cnum; + const sfxenum_t actual_id = sfx_id; sfxinfo_t *sfx; const mobj_t *origin = (const mobj_t *)origin_p; @@ -657,7 +658,7 @@ void S_StartSoundAtVolume(const void *origin_p, sfxenum_t sfx_id, INT32 volume) #endif // Handle closed caption input. - S_StartCaption(sfx_id, cnum, MAXCAPTIONTICS); + S_StartCaption(actual_id, cnum, MAXCAPTIONTICS); // Assigns the handle to one of the channels in the // mix/output buffer. @@ -710,7 +711,7 @@ dontplay: #endif // Handle closed caption input. - S_StartCaption(sfx_id, cnum, MAXCAPTIONTICS); + S_StartCaption(actual_id, cnum, MAXCAPTIONTICS); // Assigns the handle to one of the channels in the // mix/output buffer. @@ -1682,7 +1683,17 @@ void S_StopMusic(void) if (cv_closedcaptioning.value) { if (closedcaptions[0].s-S_sfx == sfx_None) - closedcaptions[0].t = CAPTIONFADETICS; + { + if (gamestate != wipegamestate) + { + closedcaptions[0].c = NULL; + closedcaptions[0].s = NULL; + closedcaptions[0].t = 0; + closedcaptions[0].b = 0; + } + else + closedcaptions[0].t = CAPTIONFADETICS; + } } } diff --git a/src/screen.c b/src/screen.c index ac7878c4a..fc3f5b8e8 100644 --- a/src/screen.c +++ b/src/screen.c @@ -438,7 +438,9 @@ void SCR_ClosedCaptions(void) if (gamestate == GS_LEVEL) { - if (splitscreen) + if (promptactive) + basey -= 28; + else if (splitscreen) basey -= 8; else if ((modeattacking == ATTACKING_NIGHTS) || (!(maptol & TOL_NIGHTS) diff --git a/src/st_stuff.c b/src/st_stuff.c index 962bdd722..aefb4c53c 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -227,7 +227,7 @@ void ST_doPaletteStuff(void) void ST_UnloadGraphics(void) { - Z_FreeTags(PU_HUDGFX, PU_HUDGFX); + Z_FreeTag(PU_HUDGFX); } void ST_LoadGraphics(void) @@ -343,10 +343,24 @@ void ST_LoadGraphics(void) } // made separate so that skins code can reload custom face graphics -void ST_LoadFaceGraphics(char *facestr, char *superstr, INT32 skinnum) +void ST_LoadFaceGraphics(INT32 skinnum) { - faceprefix[skinnum] = W_CachePatchName(facestr, PU_HUDGFX); - superprefix[skinnum] = W_CachePatchName(superstr, PU_HUDGFX); + if (skins[skinnum].sprites[SPR2_XTRA].numframes) + { + spritedef_t *sprdef = &skins[skinnum].sprites[SPR2_XTRA]; + spriteframe_t *sprframe = &sprdef->spriteframes[0]; + faceprefix[skinnum] = W_CachePatchNum(sprframe->lumppat[0], PU_HUDGFX); + if (skins[skinnum].sprites[(SPR2_XTRA|FF_SPR2SUPER)].numframes) + { + sprdef = &skins[skinnum].sprites[SPR2_XTRA|FF_SPR2SUPER]; + sprframe = &sprdef->spriteframes[0]; + superprefix[skinnum] = W_CachePatchNum(sprframe->lumppat[0], PU_HUDGFX); + } + else + superprefix[skinnum] = faceprefix[skinnum]; // not manually freed, okay to set to same pointer + } + else + faceprefix[skinnum] = superprefix[skinnum] = W_CachePatchName("MISSING", PU_HUDGFX); // ditto facefreed[skinnum] = false; } @@ -355,7 +369,7 @@ void ST_ReloadSkinFaceGraphics(void) INT32 i; for (i = 0; i < numskins; i++) - ST_LoadFaceGraphics(skins[i].face, skins[i].superface, i); + ST_LoadFaceGraphics(i); } static inline void ST_InitData(void) diff --git a/src/st_stuff.h b/src/st_stuff.h index aca4e60d2..40574f46c 100644 --- a/src/st_stuff.h +++ b/src/st_stuff.h @@ -42,7 +42,7 @@ void ST_UnloadGraphics(void); void ST_LoadGraphics(void); // face load graphics, called when skin changes -void ST_LoadFaceGraphics(char *facestr, char *superstr, INT32 playernum); +void ST_LoadFaceGraphics(INT32 playernum); void ST_ReloadSkinFaceGraphics(void); void ST_doPaletteStuff(void); diff --git a/src/v_video.c b/src/v_video.c index df342e74b..85f22eccb 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -309,12 +309,14 @@ static boolean InitCube(void) return true; } +#ifdef BACKWARDSCOMPATCORRECTION /* So it turns out that the way gamma was implemented previously, the default colour profile of the game was messed up. Since this bad decision has been around for a long time, and the intent is to keep the base game looking the same, I'm not gonna be the one to remove this base modification. toast 20/04/17 +... welp yes i am (27/07/19, see the ifdef around it) */ const UINT8 correctiontable[256] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, @@ -333,6 +335,7 @@ const UINT8 correctiontable[256] = 208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223, 224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239, 240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255}; +#endif // keep a copy of the palette so that we can get the RGB value for a color index at any time. static void LoadPalette(const char *lumpname) @@ -351,12 +354,18 @@ static void LoadPalette(const char *lumpname) pal = W_CacheLumpNum(lumpnum, PU_CACHE); for (i = 0; i < palsize; i++) { +#ifdef BACKWARDSCOMPATCORRECTION pMasterPalette[i].s.red = pLocalPalette[i].s.red = correctiontable[*pal++]; pMasterPalette[i].s.green = pLocalPalette[i].s.green = correctiontable[*pal++]; pMasterPalette[i].s.blue = pLocalPalette[i].s.blue = correctiontable[*pal++]; +#else + pMasterPalette[i].s.red = pLocalPalette[i].s.red = *pal++; + pMasterPalette[i].s.green = pLocalPalette[i].s.green = *pal++; + pMasterPalette[i].s.blue = pLocalPalette[i].s.blue = *pal++; +#endif pMasterPalette[i].s.alpha = pLocalPalette[i].s.alpha = 0xFF; - // lerp of colour cubing! + // lerp of colour cubing! if you want, make it smoother yourself if (cube) { float working[4][3]; @@ -732,12 +741,15 @@ void V_DrawFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_t // Center it if necessary if (!(scrn & V_SCALEPATCHMASK)) { - // if it's meant to cover the whole screen, black out the rest + // if it's meant to cover the whole screen, black out the rest (ONLY IF TOP LEFT ISN'T TRANSPARENT) if (x == 0 && SHORT(patch->width) == BASEVIDWIDTH && y == 0 && SHORT(patch->height) == BASEVIDHEIGHT) { column = (const column_t *)((const UINT8 *)(patch) + LONG(patch->columnofs[0])); - source = (const UINT8 *)(column) + 3; - V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, (column->topdelta == 0xff ? 31 : source[0])); + if (!column->topdelta) + { + source = (const UINT8 *)(column) + 3; + V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, source[0]); + } } if (vid.width != BASEVIDWIDTH * dupx) @@ -984,12 +996,7 @@ void V_DrawCroppedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_ if (!(scrn & V_SCALEPATCHMASK)) { // if it's meant to cover the whole screen, black out the rest - if (x == 0 && SHORT(patch->width) == BASEVIDWIDTH && y == 0 && SHORT(patch->height) == BASEVIDHEIGHT) - { - column = (const column_t *)((const UINT8 *)(patch) + LONG(patch->columnofs[0])); - source = (const UINT8 *)(column) + 3; - V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, (column->topdelta == 0xff ? 31 : source[0])); - } + // no the patch is cropped do not do this ever if (vid.width != BASEVIDWIDTH * dupx) { @@ -1349,13 +1356,16 @@ static UINT32 V_GetHWConsBackColor(void) // THANK YOU MPC!!! +// and thanks toaster for cleaning it up. void V_DrawFillConsoleMap(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c) { UINT8 *dest; - INT32 u, v; + const UINT8 *deststop; + INT32 u; UINT8 *fadetable; UINT32 alphalevel = 0; + UINT8 perplayershuffle = 0; if (rendermode == render_none) return; @@ -1369,16 +1379,91 @@ void V_DrawFillConsoleMap(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c) } #endif + if ((alphalevel = ((c & V_ALPHAMASK) >> V_ALPHASHIFT))) + { + if (alphalevel == 13) + alphalevel = hudminusalpha[cv_translucenthud.value]; + else if (alphalevel == 14) + alphalevel = 10 - cv_translucenthud.value; + else if (alphalevel == 15) + alphalevel = hudplusalpha[cv_translucenthud.value]; + + if (alphalevel >= 10) + return; // invis + } + + if (splitscreen && (c & V_PERPLAYER)) + { + fixed_t adjusty = ((c & V_NOSCALESTART) ? vid.height : BASEVIDHEIGHT)>>1; + h >>= 1; + y >>= 1; +#ifdef QUADS + if (splitscreen > 1) // 3 or 4 players + { + fixed_t adjustx = ((c & V_NOSCALESTART) ? vid.height : BASEVIDHEIGHT)>>1; + w >>= 1; + x >>= 1; + if (stplyr == &players[displayplayer]) + { + if (!(c & (V_SNAPTOTOP|V_SNAPTOBOTTOM))) + perplayershuffle |= 1; + if (!(c & (V_SNAPTOLEFT|V_SNAPTORIGHT))) + perplayershuffle |= 4; + c &= ~V_SNAPTOBOTTOM|V_SNAPTORIGHT; + } + else if (stplyr == &players[secondarydisplayplayer]) + { + if (!(c & (V_SNAPTOTOP|V_SNAPTOBOTTOM))) + perplayershuffle |= 1; + if (!(c & (V_SNAPTOLEFT|V_SNAPTORIGHT))) + perplayershuffle |= 8; + x += adjustx; + c &= ~V_SNAPTOBOTTOM|V_SNAPTOLEFT; + } + else if (stplyr == &players[thirddisplayplayer]) + { + if (!(c & (V_SNAPTOTOP|V_SNAPTOBOTTOM))) + perplayershuffle |= 2; + if (!(c & (V_SNAPTOLEFT|V_SNAPTORIGHT))) + perplayershuffle |= 4; + y += adjusty; + c &= ~V_SNAPTOTOP|V_SNAPTORIGHT; + } + else //if (stplyr == &players[fourthdisplayplayer]) + { + if (!(c & (V_SNAPTOTOP|V_SNAPTOBOTTOM))) + perplayershuffle |= 2; + if (!(c & (V_SNAPTOLEFT|V_SNAPTORIGHT))) + perplayershuffle |= 8; + x += adjustx; + y += adjusty; + c &= ~V_SNAPTOTOP|V_SNAPTOLEFT; + } + } + else +#endif + // 2 players + { + if (stplyr == &players[displayplayer]) + { + if (!(c & (V_SNAPTOTOP|V_SNAPTOBOTTOM))) + perplayershuffle |= 1; + c &= ~V_SNAPTOBOTTOM; + } + else //if (stplyr == &players[secondarydisplayplayer]) + { + if (!(c & (V_SNAPTOTOP|V_SNAPTOBOTTOM))) + perplayershuffle |= 2; + y += adjusty; + c &= ~V_SNAPTOTOP; + } + } + } + if (!(c & V_NOSCALESTART)) { INT32 dupx = vid.dupx, dupy = vid.dupy; - if (x == 0 && y == 0 && w == BASEVIDWIDTH && h == BASEVIDHEIGHT) - { // Clear the entire screen, from dest to deststop. Yes, this really works. - memset(screens[0], (UINT8)(c&255), vid.width * vid.height * vid.bpp); - return; - } - x *= dupx; y *= dupy; w *= dupx; @@ -1393,6 +1478,10 @@ void V_DrawFillConsoleMap(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c) x += (vid.width - (BASEVIDWIDTH * dupx)); else if (!(c & V_SNAPTOLEFT)) x += (vid.width - (BASEVIDWIDTH * dupx)) / 2; + if (perplayershuffle & 4) + x -= (vid.width - (BASEVIDWIDTH * dupx)) / 4; + else if (perplayershuffle & 8) + x += (vid.width - (BASEVIDWIDTH * dupx)) / 4; } if (vid.height != BASEVIDHEIGHT * dupy) { @@ -1401,6 +1490,10 @@ void V_DrawFillConsoleMap(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c) y += (vid.height - (BASEVIDHEIGHT * dupy)); else if (!(c & V_SNAPTOTOP)) y += (vid.height - (BASEVIDHEIGHT * dupy)) / 2; + if (perplayershuffle & 1) + y -= (vid.height - (BASEVIDHEIGHT * dupy)) / 4; + else if (perplayershuffle & 2) + y += (vid.height - (BASEVIDHEIGHT * dupy)) / 4; } } @@ -1423,34 +1516,206 @@ void V_DrawFillConsoleMap(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c) h = vid.height-y; dest = screens[0] + y*vid.width + x; - - if ((alphalevel = ((c & V_ALPHAMASK) >> V_ALPHASHIFT))) - { - if (alphalevel == 13) - alphalevel = hudminusalpha[cv_translucenthud.value]; - else if (alphalevel == 14) - alphalevel = 10 - cv_translucenthud.value; - else if (alphalevel == 15) - alphalevel = hudplusalpha[cv_translucenthud.value]; - - if (alphalevel >= 10) - return; // invis - } + deststop = screens[0] + vid.rowbytes * vid.height; c &= 255; // Jimita (12-04-2018) - w = min(w, vid.width); - h = min(h, vid.height); - fadetable = ((UINT8 *)transtables + ((alphalevel-1)<= 0) && dest < deststop; dest += vid.width) { - if (!alphalevel) - dest[u] = consolebgmap[dest[u]]; - else + u = 0; + while (u < w) + { dest[u] = fadetable[consolebgmap[dest[u]]]; + u++; + } } + } + else + { + for (;(--h >= 0) && dest < deststop; dest += vid.width) + { + u = 0; + while (u < w) + { + dest[u] = consolebgmap[dest[u]]; + u++; + } + } + } +} + +// +// If color is 0x00 to 0xFF, draw transtable (strength range 0-9). +// Else, use COLORMAP lump (strength range 0-31). +// c is not color, it is for flags only. transparency flags will be ignored. +// IF YOU ARE NOT CAREFUL, THIS CAN AND WILL CRASH! +// I have kept the safety checks for strength out of this function; +// I don't trust Lua users with it, so it doesn't matter. +// +void V_DrawFadeFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c, UINT16 color, UINT8 strength) +{ + UINT8 *dest; + const UINT8 *deststop; + INT32 u; + UINT8 *fadetable; + UINT8 perplayershuffle = 0; + + if (rendermode == render_none) + return; + +#ifdef HWRENDER + if (rendermode != render_soft && rendermode != render_none) + { + // ughhhhh please can someone else do this? thanks ~toast 25/7/19 in 38 degrees centigrade w/o AC + HWR_DrawFadeFill(x, y, w, h, c, color, strength); // toast two days later - left above comment in 'cause it's funny + return; + } +#endif + + if (splitscreen && (c & V_PERPLAYER)) + { + fixed_t adjusty = ((c & V_NOSCALESTART) ? vid.height : BASEVIDHEIGHT)>>1; + h >>= 1; + y >>= 1; +#ifdef QUADS + if (splitscreen > 1) // 3 or 4 players + { + fixed_t adjustx = ((c & V_NOSCALESTART) ? vid.height : BASEVIDHEIGHT)>>1; + w >>= 1; + x >>= 1; + if (stplyr == &players[displayplayer]) + { + if (!(c & (V_SNAPTOTOP|V_SNAPTOBOTTOM))) + perplayershuffle |= 1; + if (!(c & (V_SNAPTOLEFT|V_SNAPTORIGHT))) + perplayershuffle |= 4; + c &= ~V_SNAPTOBOTTOM|V_SNAPTORIGHT; + } + else if (stplyr == &players[secondarydisplayplayer]) + { + if (!(c & (V_SNAPTOTOP|V_SNAPTOBOTTOM))) + perplayershuffle |= 1; + if (!(c & (V_SNAPTOLEFT|V_SNAPTORIGHT))) + perplayershuffle |= 8; + x += adjustx; + c &= ~V_SNAPTOBOTTOM|V_SNAPTOLEFT; + } + else if (stplyr == &players[thirddisplayplayer]) + { + if (!(c & (V_SNAPTOTOP|V_SNAPTOBOTTOM))) + perplayershuffle |= 2; + if (!(c & (V_SNAPTOLEFT|V_SNAPTORIGHT))) + perplayershuffle |= 4; + y += adjusty; + c &= ~V_SNAPTOTOP|V_SNAPTORIGHT; + } + else //if (stplyr == &players[fourthdisplayplayer]) + { + if (!(c & (V_SNAPTOTOP|V_SNAPTOBOTTOM))) + perplayershuffle |= 2; + if (!(c & (V_SNAPTOLEFT|V_SNAPTORIGHT))) + perplayershuffle |= 8; + x += adjustx; + y += adjusty; + c &= ~V_SNAPTOTOP|V_SNAPTOLEFT; + } + } + else +#endif + // 2 players + { + if (stplyr == &players[displayplayer]) + { + if (!(c & (V_SNAPTOTOP|V_SNAPTOBOTTOM))) + perplayershuffle |= 1; + c &= ~V_SNAPTOBOTTOM; + } + else //if (stplyr == &players[secondarydisplayplayer]) + { + if (!(c & (V_SNAPTOTOP|V_SNAPTOBOTTOM))) + perplayershuffle |= 2; + y += adjusty; + c &= ~V_SNAPTOTOP; + } + } + } + + if (!(c & V_NOSCALESTART)) + { + INT32 dupx = vid.dupx, dupy = vid.dupy; + + x *= dupx; + y *= dupy; + w *= dupx; + h *= dupy; + + // Center it if necessary + if (vid.width != BASEVIDWIDTH * dupx) + { + // dupx adjustments pretend that screen width is BASEVIDWIDTH * dupx, + // so center this imaginary screen + if (c & V_SNAPTORIGHT) + x += (vid.width - (BASEVIDWIDTH * dupx)); + else if (!(c & V_SNAPTOLEFT)) + x += (vid.width - (BASEVIDWIDTH * dupx)) / 2; + if (perplayershuffle & 4) + x -= (vid.width - (BASEVIDWIDTH * dupx)) / 4; + else if (perplayershuffle & 8) + x += (vid.width - (BASEVIDWIDTH * dupx)) / 4; + } + if (vid.height != BASEVIDHEIGHT * dupy) + { + // same thing here + if (c & V_SNAPTOBOTTOM) + y += (vid.height - (BASEVIDHEIGHT * dupy)); + else if (!(c & V_SNAPTOTOP)) + y += (vid.height - (BASEVIDHEIGHT * dupy)) / 2; + if (perplayershuffle & 1) + y -= (vid.height - (BASEVIDHEIGHT * dupy)) / 4; + else if (perplayershuffle & 2) + y += (vid.height - (BASEVIDHEIGHT * dupy)) / 4; + } + } + + if (x >= vid.width || y >= vid.height) + return; // off the screen + if (x < 0) { + w += x; + x = 0; + } + if (y < 0) { + h += y; + y = 0; + } + + if (w <= 0 || h <= 0) + return; // zero width/height wouldn't draw anything + if (x + w > vid.width) + w = vid.width-x; + if (y + h > vid.height) + h = vid.height-y; + + dest = screens[0] + y*vid.width + x; + deststop = screens[0] + vid.rowbytes * vid.height; + + c &= 255; + + fadetable = ((color & 0xFF00) // Color is not palette index? + ? ((UINT8 *)colormaps + strength*256) // Do COLORMAP fade. + : ((UINT8 *)transtables + ((9-strength)<= 0) && dest < deststop; dest += vid.width) + { + u = 0; + while (u < w) + { + dest[u] = fadetable[dest[u]]; + u++; + } + } } // diff --git a/src/v_video.h b/src/v_video.h index 43748692e..7eb990295 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -158,6 +158,8 @@ void V_DrawFlatFill(INT32 x, INT32 y, INT32 w, INT32 h, lumpnum_t flatnum); // fade down the screen buffer before drawing the menu over void V_DrawFadeScreen(UINT16 color, UINT8 strength); +// available to lua over my dead body, which will probably happen in this heat +void V_DrawFadeFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c, UINT16 color, UINT8 strength); void V_DrawFadeConsBack(INT32 plines); void V_DrawPromptBack(INT32 boxheight, INT32 color);