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 76dccd45d..eaeae4b10 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 31c17f188..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. @@ -5699,7 +5702,8 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_CEZFLOWER", "S_CEZPOLE", - "S_CEZBANNER", + "S_CEZBANNER1", + "S_CEZBANNER2", "S_PINETREE", "S_CEZBUSH1", "S_CEZBUSH2", @@ -5708,7 +5712,8 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_FLAMEHOLDER", "S_FIRETORCH", "S_WAVINGFLAG", - "S_WAVINGFLAGSEG", + "S_WAVINGFLAGSEG1", + "S_WAVINGFLAGSEG2", "S_CRAWLASTATUE", "S_FACESTABBERSTATUE", "S_SUSPICIOUSFACESTABBERSTATUE_WAIT", @@ -7514,8 +7519,10 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_SMALLFIREBAR", // Small Firebar "MT_BIGFIREBAR", // Big Firebar "MT_CEZFLOWER", // Flower - "MT_CEZPOLE", // Pole - "MT_CEZBANNER", // Banner + "MT_CEZPOLE1", // Pole (with red banner) + "MT_CEZPOLE2", // Pole (with blue banner) + "MT_CEZBANNER1", // Banner (red) + "MT_CEZBANNER2", // Banner (blue) "MT_PINETREE", // Pine Tree "MT_CEZBUSH1", // Bush 1 "MT_CEZBUSH2", // Bush 2 @@ -7523,8 +7530,10 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_CANDLEPRICKET", // Candle pricket "MT_FLAMEHOLDER", // Flame holder "MT_FIRETORCH", // Fire torch - "MT_WAVINGFLAG", // Waving flag - "MT_WAVINGFLAGSEG", // Waving flag segment + "MT_WAVINGFLAG1", // Waving flag (red) + "MT_WAVINGFLAG2", // Waving flag (blue) + "MT_WAVINGFLAGSEG1", // Waving flag segment (red) + "MT_WAVINGFLAGSEG2", // Waving flag segment (blue) "MT_CRAWLASTATUE", // Crawla statue "MT_FACESTABBERSTATUE", // Facestabber statue "MT_SUSPICIOUSFACESTABBERSTATUE", // :eggthinking: @@ -9493,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 c0c6360c3..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); @@ -82,7 +86,6 @@ typedef enum // Current menu parameters -extern UINT8 titlemapinaction; extern mobj_t *titlemapcameraref; extern char curbgname[8]; extern SINT8 curfadevalue; @@ -126,6 +129,7 @@ enum wipe_evaluation_toblack, wipe_gameend_toblack, wipe_intro_toblack, + wipe_ending_toblack, wipe_cutscene_toblack, // custom intermissions @@ -142,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 95cc2288d..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; @@ -4366,7 +4381,7 @@ void G_WriteGhostTic(mobj_t *ghost) ghostext.flags = 0; } - if (ghost->player && ghost->player->followmobj) + if (ghost->player && ghost->player->followmobj) // bloats tails runs but what can ya do { INT16 temp; @@ -4592,6 +4607,9 @@ void G_GhostTicker(void) switch(g->color) { default: + case GHC_RETURNSKIN: + g->mo->skin = g->oldmo.skin; + // fallthru case GHC_NORMAL: // Go back to skin color g->mo->color = g->oldmo.color; break; @@ -4602,6 +4620,9 @@ void G_GhostTicker(void) case GHC_FIREFLOWER: // Fireflower g->mo->color = SKINCOLOR_WHITE; break; + case GHC_NIGHTSSKIN: // not actually a colour + g->mo->skin = &skins[DEFAULTNIGHTSSKIN]; + break; } } if (xziptic & EZT_FLIP) diff --git a/src/g_game.h b/src/g_game.h index 3cbde9a3c..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; @@ -140,7 +142,9 @@ typedef enum GHC_NORMAL = 0, GHC_SUPER, GHC_FIREFLOWER, - GHC_INVINCIBLE + GHC_INVINCIBLE, + GHC_NIGHTSSKIN, // not actually a colour + GHC_RETURNSKIN // ditto } ghostcolor_t; // Record/playback tics diff --git a/src/g_state.h b/src/g_state.h index 76c9bd16f..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 @@ -50,6 +52,7 @@ typedef enum } gameaction_t; extern gamestate_t gamestate; +extern UINT8 titlemapinaction; extern UINT8 ultimatemode; // was sk_insane extern gameaction_t gameaction; 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 7606f27a1..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) @@ -1791,7 +1793,11 @@ state_t states[NUMSTATES] = // Blue Sphere for special stages {SPR_SPHR, FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_BLUESPHERE - {SPR_SPHR, FF_FULLBRIGHT|FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 1, 4, S_NULL}, // S_BLUESPHEREBONUS + {SPR_SPHR, FF_FULLBRIGHT +#ifdef MANIASPHERES + |FF_ANIMATE|FF_RANDOMANIM +#endif + , -1, {NULL}, 1, 4, S_NULL}, // S_BLUESPHEREBONUS {SPR_SPHR, 0, 20, {NULL}, 0, 0, S_NULL}, // S_BLUESPHERESPARK // Bomb Sphere @@ -2322,9 +2328,10 @@ state_t states[NUMSTATES] = {SPR_BFBR, FF_FULLBRIGHT|15, 1, {NULL}, 0, 0, S_BIGFIREBAR1}, // S_BIGFIREBAR16 {SPR_FWR4, 0, -1, {NULL}, 0, 0, S_NULL}, // S_CEZFLOWER - {SPR_BANR, 1, -1, {NULL}, 0, 0, S_NULL}, // S_CEZPOLE + {SPR_BANR, 0, -1, {NULL}, 0, 0, S_NULL}, // S_CEZPOLE - {SPR_BANR, FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_CEZBANNER + {SPR_BANR, FF_PAPERSPRITE|1, -1, {NULL}, 0, 0, S_NULL}, // S_CEZBANNER1 + {SPR_BANR, FF_PAPERSPRITE|2, -1, {NULL}, 0, 0, S_NULL}, // S_CEZBANNER2 {SPR_PINE, 0, -1, {NULL}, 0, 0, S_NULL}, // S_PINETREE {SPR_CEZB, 0, -1, {NULL}, 0, 0, S_NULL}, // S_CEZBUSH1 @@ -2338,7 +2345,8 @@ state_t states[NUMSTATES] = {SPR_CTRC, FF_FULLBRIGHT|FF_ANIMATE, 8*3, {A_FlameParticle}, 3, 3, S_FIRETORCH}, // S_FIRETORCH {SPR_CFLG, 0, -1, {NULL}, 0, 0, S_NULL}, // S_WAVINGFLAG - {SPR_CFLG, FF_PAPERSPRITE|1, -1, {NULL}, 0, 0, S_NULL}, // S_WAVINGFLAGSEG + {SPR_CFLG, FF_PAPERSPRITE|1, -1, {NULL}, 0, 0, S_NULL}, // S_WAVINGFLAGSEG1 + {SPR_CFLG, FF_PAPERSPRITE|2, -1, {NULL}, 0, 0, S_NULL}, // S_WAVINGFLAGSEG2 {SPR_CSTA, 0, -1, {NULL}, 0, 0, S_NULL}, // S_CRAWLASTATUE @@ -11018,7 +11026,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, - { // MT_CEZPOLE + { // MT_CEZPOLE1 1117, // doomednum S_CEZPOLE, // spawnstate 1000, // spawnhealth @@ -11045,9 +11053,63 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, - { // MT_CEZBANNER + { // MT_CEZPOLE2 + 1118, // doomednum + S_CEZPOLE, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 40*FRACUNIT, // radius + 224*FRACUNIT, // height + 0, // display offset + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags + S_NULL // raisestate + }, + + { // MT_CEZBANNER1 -1, // doomednum - S_CEZBANNER, // spawnstate + S_CEZBANNER1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 40*FRACUNIT, // radius + 224*FRACUNIT, // height + 0, // display offset + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags + S_NULL // raisestate + }, + + { // MT_CEZBANNER2 + -1, // doomednum + S_CEZBANNER2, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound @@ -11261,8 +11323,8 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, - { // MT_WAVINGFLAG - 1118, // doomednum + { // MT_WAVINGFLAG1 + 1128, // doomednum S_WAVINGFLAG, // spawnstate 1000, // spawnhealth S_NULL, // seestate @@ -11278,8 +11340,8 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed - 4*FRACUNIT, // radius - 104*FRACUNIT, // height + 8*FRACUNIT, // radius + 208*FRACUNIT, // height 0, // display offset 100, // mass 0, // damage @@ -11288,9 +11350,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, - { // MT_WAVINGFLAGSEG - -1, // doomednum - S_WAVINGFLAGSEG, // spawnstate + { // MT_WAVINGFLAG2 + 1129, // doomednum + S_WAVINGFLAG, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound @@ -11305,7 +11367,61 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL, // xdeathstate sfx_None, // deathsound 0, // speed - 4*FRACUNIT, // radius + 8*FRACUNIT, // radius + 208*FRACUNIT, // height + 0, // display offset + 100, // mass + 0, // damage + sfx_None, // activesound + MF_SOLID, // flags + S_NULL // raisestate + }, + + { // MT_WAVINGFLAGSEG1 + -1, // doomednum + S_WAVINGFLAGSEG1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 8*FRACUNIT, // radius + 1, // height -- this is not a typo + 0, // display offset + 100, // mass + 0, // damage + sfx_None, // activesound + MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags + S_NULL // raisestate + }, + + { // MT_WAVINGFLAGSEG2 + -1, // doomednum + S_WAVINGFLAGSEG2, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 8*FRACUNIT, // radius 1, // height -- this is not a typo 0, // display offset 100, // mass diff --git a/src/info.h b/src/info.h index 44f08a4e9..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, @@ -2458,7 +2459,8 @@ typedef enum state S_CEZFLOWER, S_CEZPOLE, - S_CEZBANNER, + S_CEZBANNER1, + S_CEZBANNER2, S_PINETREE, S_CEZBUSH1, S_CEZBUSH2, @@ -2467,7 +2469,8 @@ typedef enum state S_FLAMEHOLDER, S_FIRETORCH, S_WAVINGFLAG, - S_WAVINGFLAGSEG, + S_WAVINGFLAGSEG1, + S_WAVINGFLAGSEG2, S_CRAWLASTATUE, S_FACESTABBERSTATUE, S_SUSPICIOUSFACESTABBERSTATUE_WAIT, @@ -4295,8 +4298,10 @@ typedef enum mobj_type MT_SMALLFIREBAR, // Small Firebar MT_BIGFIREBAR, // Big Firebar MT_CEZFLOWER, // Flower - MT_CEZPOLE, // Pole - MT_CEZBANNER, // Banner + MT_CEZPOLE1, // Pole (with red banner) + MT_CEZPOLE2, // Pole (with blue banner) + MT_CEZBANNER1, // Banner (red) + MT_CEZBANNER2, // Banner (blue) MT_PINETREE, // Pine Tree MT_CEZBUSH1, // Bush 1 MT_CEZBUSH2, // Bush 2 @@ -4304,8 +4309,10 @@ typedef enum mobj_type MT_CANDLEPRICKET, // Candle pricket MT_FLAMEHOLDER, // Flame holder MT_FIRETORCH, // Fire torch - MT_WAVINGFLAG, // Waving flag - MT_WAVINGFLAGSEG, // Waving flag segment + MT_WAVINGFLAG1, // Waving flag (red) + MT_WAVINGFLAG2, // Waving flag (blue) + MT_WAVINGFLAGSEG1, // Waving flag segment (red) + MT_WAVINGFLAGSEG2, // Waving flag segment (blue) MT_CRAWLASTATUE, // Crawla statue MT_FACESTABBERSTATUE, // Facestabber statue MT_SUSPICIOUSFACESTABBERSTATUE, // :eggthinking: diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 1d69b238b..3c136a436 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -33,8 +33,6 @@ #define NOHUD if (hud_running)\ return luaL_error(L, "HUD rendering code should not call this function!"); -#define INLEVEL if (gamestate != GS_LEVEL)\ -return luaL_error(L, "This function can only be used in a level!"); boolean luaL_checkboolean(lua_State *L, int narg) { luaL_checktype(L, narg, LUA_TBOOLEAN); @@ -2047,12 +2045,22 @@ static int lib_pStartQuake(lua_State *L) static int lib_evCrumbleChain(lua_State *L) { - sector_t *sec = *((sector_t **)luaL_checkudata(L, 1, META_SECTOR)); - ffloor_t *rover = *((ffloor_t **)luaL_checkudata(L, 2, META_FFLOOR)); + sector_t *sec = NULL; + ffloor_t *rover = NULL; NOHUD INLEVEL - if (!sec) - return LUA_ErrInvalid(L, "sector_t"); + if (!lua_isnone(L, 2)) + { + if (!lua_isnil(L, 1)) + { + sec = *((sector_t **)luaL_checkudata(L, 1, META_SECTOR)); + if (!sec) + return LUA_ErrInvalid(L, "sector_t"); + } + rover = *((ffloor_t **)luaL_checkudata(L, 2, META_FFLOOR)); + } + else + rover = *((ffloor_t **)luaL_checkudata(L, 1, META_FFLOOR)); if (!rover) return LUA_ErrInvalid(L, "ffloor_t"); EV_CrumbleChain(sec, rover); @@ -2601,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_consolelib.c b/src/lua_consolelib.c index 98d18d8db..c6856b426 100644 --- a/src/lua_consolelib.c +++ b/src/lua_consolelib.c @@ -28,9 +28,6 @@ return luaL_error(L, "HUD rendering code should not call this function!"); // for functions not allowed in hooks or coroutines (supercedes above) #define NOHOOK if (!lua_lumploading)\ return luaL_error(L, "This function cannot be called from within a hook or coroutine!"); -// for functions only allowed within a level -#define INLEVEL if (gamestate != GS_LEVEL)\ -return luaL_error(L, "This function can only be used in a level!"); static const char *cvname = NULL; 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_maplib.c b/src/lua_maplib.c index ded90daf0..82843db4e 100644 --- a/src/lua_maplib.c +++ b/src/lua_maplib.c @@ -333,8 +333,7 @@ static int lib_iterateSectorThinglist(lua_State *L) mobj_t *state = NULL; mobj_t *thing = NULL; - if (gamestate != GS_LEVEL) - return luaL_error(L, "This function can only be used in a level!"); + INLEVEL if (lua_gettop(L) < 2) return luaL_error(L, "Don't call sector.thinglist() directly, use it as 'for rover in sector.thinglist do end'."); @@ -369,8 +368,7 @@ static int lib_iterateSectorFFloors(lua_State *L) ffloor_t *state = NULL; ffloor_t *ffloor = NULL; - if (gamestate != GS_LEVEL) - return luaL_error(L, "This function can only be used in a level!"); + INLEVEL if (lua_gettop(L) < 2) return luaL_error(L, "Don't call sector.ffloors() directly, use it as 'for rover in sector.ffloors do end'."); @@ -1251,8 +1249,7 @@ static int bbox_get(lua_State *L) static int lib_iterateSectors(lua_State *L) { size_t i = 0; - if (gamestate != GS_LEVEL) - return luaL_error(L, "This function can only be used in a level!"); + INLEVEL if (lua_gettop(L) < 2) return luaL_error(L, "Don't call sectors.iterate() directly, use it as 'for sector in sectors.iterate do end'."); lua_settop(L, 2); @@ -1270,8 +1267,7 @@ static int lib_iterateSectors(lua_State *L) static int lib_getSector(lua_State *L) { int field; - if (gamestate != GS_LEVEL) - return luaL_error(L, "You cannot access this outside of a level!"); + INLEVEL lua_settop(L, 2); lua_remove(L, 1); // dummy userdata table is unused. if (lua_isnumber(L, 1)) @@ -1305,8 +1301,7 @@ static int lib_numsectors(lua_State *L) static int lib_iterateSubsectors(lua_State *L) { size_t i = 0; - if (gamestate != GS_LEVEL) - return luaL_error(L, "This function can only be used in a level!"); + INLEVEL if (lua_gettop(L) < 2) return luaL_error(L, "Don't call subsectors.iterate() directly, use it as 'for subsector in subsectors.iterate do end'."); lua_settop(L, 2); @@ -1324,8 +1319,7 @@ static int lib_iterateSubsectors(lua_State *L) static int lib_getSubsector(lua_State *L) { int field; - if (gamestate != GS_LEVEL) - return luaL_error(L, "You cannot access this outside of a level!"); + INLEVEL lua_settop(L, 2); lua_remove(L, 1); // dummy userdata table is unused. if (lua_isnumber(L, 1)) @@ -1359,8 +1353,7 @@ static int lib_numsubsectors(lua_State *L) static int lib_iterateLines(lua_State *L) { size_t i = 0; - if (gamestate != GS_LEVEL) - return luaL_error(L, "This function can only be used in a level!"); + INLEVEL if (lua_gettop(L) < 2) return luaL_error(L, "Don't call lines.iterate() directly, use it as 'for line in lines.iterate do end'."); lua_settop(L, 2); @@ -1378,8 +1371,7 @@ static int lib_iterateLines(lua_State *L) static int lib_getLine(lua_State *L) { int field; - if (gamestate != GS_LEVEL) - return luaL_error(L, "You cannot access this outside of a level!"); + INLEVEL lua_settop(L, 2); lua_remove(L, 1); // dummy userdata table is unused. if (lua_isnumber(L, 1)) @@ -1413,8 +1405,7 @@ static int lib_numlines(lua_State *L) static int lib_iterateSides(lua_State *L) { size_t i = 0; - if (gamestate != GS_LEVEL) - return luaL_error(L, "This function can only be used in a level!"); + INLEVEL if (lua_gettop(L) < 2) return luaL_error(L, "Don't call sides.iterate() directly, use it as 'for side in sides.iterate do end'."); lua_settop(L, 2); @@ -1432,8 +1423,7 @@ static int lib_iterateSides(lua_State *L) static int lib_getSide(lua_State *L) { int field; - if (gamestate != GS_LEVEL) - return luaL_error(L, "You cannot access this outside of a level!"); + INLEVEL lua_settop(L, 2); lua_remove(L, 1); // dummy userdata table is unused. if (lua_isnumber(L, 1)) @@ -1467,8 +1457,7 @@ static int lib_numsides(lua_State *L) static int lib_iterateVertexes(lua_State *L) { size_t i = 0; - if (gamestate != GS_LEVEL) - return luaL_error(L, "This function can only be used in a level!"); + INLEVEL if (lua_gettop(L) < 2) return luaL_error(L, "Don't call vertexes.iterate() directly, use it as 'for vertex in vertexes.iterate do end'."); lua_settop(L, 2); @@ -1486,8 +1475,7 @@ static int lib_iterateVertexes(lua_State *L) static int lib_getVertex(lua_State *L) { int field; - if (gamestate != GS_LEVEL) - return luaL_error(L, "You cannot access this outside of a level!"); + INLEVEL lua_settop(L, 2); lua_remove(L, 1); // dummy userdata table is unused. if (lua_isnumber(L, 1)) @@ -1523,8 +1511,7 @@ static int lib_numvertexes(lua_State *L) static int lib_iterateSegs(lua_State *L) { size_t i = 0; - if (gamestate != GS_LEVEL) - return luaL_error(L, "This function can only be used in a level!"); + INLEVEL if (lua_gettop(L) < 2) return luaL_error(L, "Don't call segs.iterate() directly, use it as 'for seg in segs.iterate do end'."); lua_settop(L, 2); @@ -1542,8 +1529,7 @@ static int lib_iterateSegs(lua_State *L) static int lib_getSeg(lua_State *L) { int field; - if (gamestate != GS_LEVEL) - return luaL_error(L, "You cannot access this outside of a level!"); + INLEVEL lua_settop(L, 2); lua_remove(L, 1); // dummy userdata table is unused. if (lua_isnumber(L, 1)) @@ -1577,8 +1563,7 @@ static int lib_numsegs(lua_State *L) static int lib_iterateNodes(lua_State *L) { size_t i = 0; - if (gamestate != GS_LEVEL) - return luaL_error(L, "This function can only be used in a level!"); + INLEVEL if (lua_gettop(L) < 2) return luaL_error(L, "Don't call nodes.iterate() directly, use it as 'for node in nodes.iterate do end'."); lua_settop(L, 2); @@ -1596,8 +1581,7 @@ static int lib_iterateNodes(lua_State *L) static int lib_getNode(lua_State *L) { int field; - if (gamestate != GS_LEVEL) - return luaL_error(L, "You cannot access this outside of a level!"); + INLEVEL lua_settop(L, 2); lua_remove(L, 1); // dummy userdata table is unused. if (lua_isnumber(L, 1)) diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c index 8bbbebe1d..063158b26 100644 --- a/src/lua_mobjlib.c +++ b/src/lua_mobjlib.c @@ -804,7 +804,12 @@ static int mapthing_set(lua_State *L) else if(fastcmp(field,"z")) mt->z = (INT16)luaL_checkinteger(L, 3); else if(fastcmp(field,"extrainfo")) - mt->extrainfo = (UINT8)luaL_checkinteger(L, 3); + { + INT32 extrainfo = luaL_checkinteger(L, 3); + if (extrainfo & ~15) + return luaL_error(L, "mapthing_t extrainfo set %d out of range (%d - %d)", extrainfo, 0, 15); + mt->extrainfo = (UINT8)extrainfo; + } else if(fastcmp(field,"mobj")) mt->mobj = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ)); else @@ -816,8 +821,7 @@ static int mapthing_set(lua_State *L) static int lib_iterateMapthings(lua_State *L) { size_t i = 0; - if (gamestate != GS_LEVEL) - return luaL_error(L, "This function can only be used in a level!"); + INLEVEL if (lua_gettop(L) < 2) return luaL_error(L, "Don't call mapthings.iterate() directly, use it as 'for mapthing in mapthings.iterate do end'."); lua_settop(L, 2); @@ -835,8 +839,7 @@ static int lib_iterateMapthings(lua_State *L) static int lib_getMapthing(lua_State *L) { int field; - if (gamestate != GS_LEVEL) - return luaL_error(L, "You cannot access this outside of a level!"); + INLEVEL lua_settop(L, 2); lua_remove(L, 1); // dummy userdata table is unused. if (lua_isnumber(L, 1)) diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 700dab211..b7bdaa1be 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -25,8 +25,7 @@ static int lib_iteratePlayers(lua_State *L) { INT32 i = -1; - if (gamestate != GS_LEVEL) - return luaL_error(L, "This function can only be used in a level!"); + INLEVEL if (lua_gettop(L) < 2) { //return luaL_error(L, "Don't call players.iterate() directly, use it as 'for player in players.iterate do end'."); @@ -53,8 +52,7 @@ static int lib_getPlayer(lua_State *L) { const char *field; // i -> players[i] - if (gamestate != GS_LEVEL) - return luaL_error(L, "You cannot access this outside of a level!"); + INLEVEL if (lua_type(L, 2) == LUA_TNUMBER) { lua_Integer i = luaL_checkinteger(L, 2); diff --git a/src/lua_script.h b/src/lua_script.h index b690e4fa7..fcbca2937 100644 --- a/src/lua_script.h +++ b/src/lua_script.h @@ -15,6 +15,7 @@ #include "m_fixed.h" #include "doomtype.h" #include "d_player.h" +#include "g_state.h" #include "blua/lua.h" #include "blua/lualib.h" @@ -97,4 +98,7 @@ void COM_Lua_f(void); // uncomment if you want seg_t/node_t in Lua // #define HAVE_LUA_SEGS +#define INLEVEL if (gamestate != GS_LEVEL && !titlemapinaction)\ +return luaL_error(L, "This can only be used in a level!"); + #endif 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/lua_thinkerlib.c b/src/lua_thinkerlib.c index 63eb15846..877294898 100644 --- a/src/lua_thinkerlib.c +++ b/src/lua_thinkerlib.c @@ -56,8 +56,7 @@ static int lib_iterateThinkers(lua_State *L) thinker_t *th = NULL, *next = NULL; struct iterationState *it; - if (gamestate != GS_LEVEL) - return luaL_error(L, "This function can only be used in a level!"); + INLEVEL it = luaL_checkudata(L, 1, META_ITERATIONSTATE); @@ -112,8 +111,7 @@ static int lib_startIterate(lua_State *L) { struct iterationState *it; - if (gamestate != GS_LEVEL) - return luaL_error(L, "This function can only be used in a level!"); + INLEVEL lua_pushvalue(L, lua_upvalueindex(1)); it = lua_newuserdata(L, sizeof(struct iterationState)); 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 1b77b4a99..560b28be8 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -2830,8 +2830,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) @@ -3531,6 +3531,7 @@ void M_InitCharacterTables(void) strcpy(description[i].picname, ""); strcpy(description[i].skinname, ""); description[i].prev = description[i].next = 0; + description[i].pic = NULL; } } @@ -7577,8 +7578,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); @@ -7732,7 +7744,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 @@ -7743,7 +7755,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 @@ -7752,7 +7764,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) @@ -8144,9 +8156,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_enemy.c b/src/p_enemy.c index 88405437c..18d6f20c0 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -3878,6 +3878,8 @@ bossjustdie: } default: //eggmobiles { + UINT8 extrainfo = (mo->spawnpoint ? mo->spawnpoint->extrainfo : 0); + // Stop exploding and prepare to run. P_SetMobjState(mo, mo->info->xdeathstate); if (P_MobjWasRemoved(mo)) @@ -3897,6 +3899,9 @@ bossjustdie: if (mo2->type != MT_BOSSFLYPOINT) continue; + if (mo2->spawnpoint && mo2->spawnpoint->extrainfo != extrainfo) + continue; + // If this one's further then the last one, don't go for it. if (mo->target && P_AproxDistance(P_AproxDistance(mo->x - mo2->x, mo->y - mo2->y), mo->z - mo2->z) > @@ -12303,6 +12308,7 @@ void A_Boss5FindWaypoint(mobj_t *actor) //INT32 locvar2 = var2; boolean avoidcenter; UINT32 i; + UINT8 extrainfo = (actor->spawnpoint ? actor->spawnpoint->extrainfo : 0); #ifdef HAVE_BLUA if (LUA_CallAction("A_Boss5FindWaypoint", actor)) return; @@ -12312,16 +12318,34 @@ void A_Boss5FindWaypoint(mobj_t *actor) if (locvar1 == 2) // look for the boss waypoint { - for (i = 0; i < nummapthings; i++) + thinker_t *th; + mobj_t *mo2; + P_SetTarget(&actor->tracer, NULL); + // Flee! Flee! Find a point to escape to! If none, just shoot upward! + // scan the thinkers to find the runaway point + for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) { - if (!mapthings[i].mobj) + if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) continue; - if (mapthings[i].mobj->type != MT_BOSSFLYPOINT) + + mo2 = (mobj_t *)th; + + if (mo2->type != MT_BOSSFLYPOINT) continue; - P_SetTarget(&actor->tracer, mapthings[i].mobj); - break; + + if (mo2->spawnpoint && mo2->spawnpoint->extrainfo != extrainfo) + continue; + + // If this one's further then the last one, don't go for it. + if (actor->tracer && + P_AproxDistance(P_AproxDistance(actor->x - mo2->x, actor->y - mo2->y), actor->z - mo2->z) > + P_AproxDistance(P_AproxDistance(actor->x - actor->tracer->x, actor->y - actor->tracer->y), actor->z - actor->tracer->z)) + continue; + + // Otherwise... Do! + P_SetTarget(&actor->tracer, mo2); } - if (i == nummapthings) + if (!actor->tracer) return; // no boss flypoints found } else if (locvar1 == 1) // always go to ambush-marked waypoint @@ -12335,11 +12359,13 @@ void A_Boss5FindWaypoint(mobj_t *actor) continue; if (mapthings[i].mobj->type != MT_FANGWAYPOINT) continue; - if (mapthings[i].options & MTF_AMBUSH) - { - P_SetTarget(&actor->tracer, mapthings[i].mobj); - break; - } + if (mapthings[i].extrainfo != extrainfo) + continue; + if (!(mapthings[i].options & MTF_AMBUSH)) + continue; + + P_SetTarget(&actor->tracer, mapthings[i].mobj); + break; } if (i == nummapthings) @@ -12363,6 +12389,8 @@ void A_Boss5FindWaypoint(mobj_t *actor) continue; if (actor->tracer == mapthings[i].mobj) // this was your tracer last time continue; + if (mapthings[i].extrainfo != extrainfo) + continue; if (mapthings[i].options & MTF_AMBUSH) { if (avoidcenter) @@ -12418,6 +12446,8 @@ void A_Boss5FindWaypoint(mobj_t *actor) continue; if (actor->tracer == mapthings[i].mobj) // this was your tracer last time continue; + if (mapthings[i].extrainfo != extrainfo) + continue; if (mapthings[i].options & MTF_AMBUSH) { if (avoidcenter) diff --git a/src/p_floor.c b/src/p_floor.c index c01e568d0..7887dc530 100644 --- a/src/p_floor.c +++ b/src/p_floor.c @@ -719,6 +719,8 @@ void T_ContinuousFalling(levelspecthink_t *faller) } } + P_CheckSector(faller->sector, false); // you might think this is irrelevant. you would be wrong + faller->sector->floorspeed = faller->speed*faller->direction; faller->sector->ceilspeed = 42; faller->sector->moved = true; @@ -3029,20 +3031,40 @@ INT32 EV_DoElevator(line_t *line, elevator_e elevtype, boolean customspeed) void EV_CrumbleChain(sector_t *sec, ffloor_t *rover) { - size_t i; - size_t leftmostvertex = 0, rightmostvertex = 0; - size_t topmostvertex = 0, bottommostvertex = 0; - fixed_t leftx, rightx; - fixed_t topy, bottomy; - fixed_t topz, bottomz; - fixed_t widthfactor = FRACUNIT, heightfactor = FRACUNIT; - fixed_t a, b, c; - mobjtype_t type = MT_ROCKCRUMBLE1; - fixed_t spacing = (32<master->frontsector + sector_t *controlsec = rover->master->frontsector; + + if (sec == NULL) + { + if (controlsec->numattached) + { + for (i = 0; i < controlsec->numattached; i++) + { + sec = §ors[controlsec->attached[i]]; + if (!sec->ffloors) + continue; + + for (rover = sec->ffloors; rover; rover = rover->next) + { + if (rover->master->frontsector == controlsec) + EV_CrumbleChain(sec, rover); + } + } + } + return; + } + + leftmostvertex = rightmostvertex = topmostvertex = bottommostvertex = 0; + widthfactor = heightfactor = FRACUNIT; + spacing = (32<tag != 0) { diff --git a/src/p_inter.c b/src/p_inter.c index abf33429f..0489bab90 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -670,7 +670,10 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) P_DoMatchSuper(player); } else + { emeralds |= special->info->speed; + stagefailed = false; + } if (special->target && special->target->type == MT_EMERALDSPAWN) { @@ -3652,7 +3655,7 @@ void P_PlayerRingBurst(player_t *player, INT32 num_rings) { INT32 i; mobj_t *mo; - angle_t fa; + angle_t fa, va; fixed_t ns; fixed_t z; boolean nightsreplace = ((maptol & TOL_NIGHTS) && !G_IsSpecialStage(gamemap)); @@ -3674,6 +3677,11 @@ void P_PlayerRingBurst(player_t *player, INT32 num_rings) // Spill weapons first P_PlayerWeaponPanelOrAmmoBurst(player); + if (abs(player->mo->momx) > player->mo->scale || abs(player->mo->momy) > player->mo->scale) + va = R_PointToAngle2(player->mo->momx, player->mo->momy, 0, 0)>>ANGLETOFINESHIFT; + else + va = player->mo->angle>>ANGLETOFINESHIFT; + for (i = 0; i < num_rings; i++) { INT32 objType = mobjinfo[MT_RING].reactiontime; @@ -3695,7 +3703,7 @@ void P_PlayerRingBurst(player_t *player, INT32 num_rings) P_SetScale(mo, player->mo->scale); // Angle offset by player angle, then slightly offset by amount of rings - fa = ((i*FINEANGLES/16) + (player->mo->angle>>ANGLETOFINESHIFT) - ((num_rings-1)*FINEANGLES/32)) & FINEMASK; + fa = ((i*FINEANGLES/16) + va - ((num_rings-1)*FINEANGLES/32)) & FINEMASK; // Make rings spill out around the player in 16 directions like SA, but spill like Sonic 2. // Technically a non-SA way of spilling rings. They just so happen to be a little similar. diff --git a/src/p_local.h b/src/p_local.h index cb8f95533..3c62d6277 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -142,7 +142,7 @@ boolean P_IsObjectOnGround(mobj_t *mo); boolean P_IsObjectOnGroundIn(mobj_t *mo, sector_t *sec); boolean P_InSpaceSector(mobj_t *mo); boolean P_InQuicksand(mobj_t *mo); -boolean P_PlayerHitFloor(player_t *player); +boolean P_PlayerHitFloor(player_t *player, boolean dorollstuff); void P_SetObjectMomZ(mobj_t *mo, fixed_t value, boolean relative); void P_RestoreMusic(player_t *player); diff --git a/src/p_map.c b/src/p_map.c index e78dd1e84..6beca92f1 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -342,7 +342,7 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object) if (horizspeed) { object->player->drawangle = spring->angle; - if (object->player->cmd.forwardmove == 0 && object->player->cmd.sidemove == 0) + if (vertispeed || (object->player->cmd.forwardmove == 0 && object->player->cmd.sidemove == 0)) { object->angle = spring->angle; @@ -1808,7 +1808,7 @@ static boolean PIT_CheckLine(line_t *ld) { tmceilingz = opentop; ceilingline = ld; - tmceilingrover = NULL; + tmceilingrover = openceilingrover; #ifdef ESLOPE tmceilingslope = opentopslope; #endif @@ -1817,7 +1817,7 @@ static boolean PIT_CheckLine(line_t *ld) if (openbottom > tmfloorz) { tmfloorz = openbottom; - tmfloorrover = NULL; + tmfloorrover = openfloorrover; #ifdef ESLOPE tmfloorslope = openbottomslope; #endif @@ -2089,6 +2089,7 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y) #ifdef ESLOPE tmfloorslope = NULL; #endif + tmfloorrover = NULL; } if (polybottom < tmceilingz && abs(delta1) >= abs(delta2)) { @@ -2096,6 +2097,7 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y) #ifdef ESLOPE tmceilingslope = NULL; #endif + tmceilingrover = NULL; } } plink = (polymaplink_t *)(plink->link.next); @@ -2820,7 +2822,7 @@ boolean P_SceneryTryMove(mobj_t *thing, fixed_t x, fixed_t y) static boolean P_ThingHeightClip(mobj_t *thing) { boolean floormoved; - fixed_t oldfloorz = thing->floorz; + fixed_t oldfloorz = thing->floorz, oldz = thing->z; ffloor_t *oldfloorrover = thing->floorrover; ffloor_t *oldceilingrover = thing->ceilingrover; boolean onfloor = P_IsObjectOnGround(thing);//(thing->z <= thing->floorz); @@ -2879,6 +2881,12 @@ static boolean P_ThingHeightClip(mobj_t *thing) thing->z = thing->ceilingz - thing->height; } + if (thing->z != oldz) + { + if (thing->player) + P_PlayerHitFloor(thing->player, false); + } + // debug: be sure it falls to the floor thing->eflags &= ~MFE_ONGROUND; @@ -4079,6 +4087,7 @@ static boolean PIT_ChangeSector(mobj_t *thing, boolean realcrush) boolean P_CheckSector(sector_t *sector, boolean crunch) { msecnode_t *n; + size_t i; nofit = false; crushchange = crunch; @@ -4093,9 +4102,57 @@ boolean P_CheckSector(sector_t *sector, boolean crunch) // First, let's see if anything will keep it from crushing. + + // Sal: This stupid function chain is required to fix polyobjects not being able to crush. + // Monster Iestyn: don't use P_CheckSector actually just look for objects in the blockmap instead + validcount++; + + for (i = 0; i < sector->linecount; i++) + { + if (sector->lines[i]->polyobj) + { + polyobj_t *po = sector->lines[i]->polyobj; + if (po->validcount == validcount) + continue; // skip if already checked + if (!(po->flags & POF_SOLID)) + continue; + if (po->lines[0]->backsector == sector) // Make sure you're currently checking the control sector + { + INT32 x, y; + po->validcount = validcount; + + for (y = po->blockbox[BOXBOTTOM]; y <= po->blockbox[BOXTOP]; ++y) + { + for (x = po->blockbox[BOXLEFT]; x <= po->blockbox[BOXRIGHT]; ++x) + { + mobj_t *mo; + + if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight) + continue; + + mo = blocklinks[y * bmapwidth + x]; + + for (; mo; mo = mo->bnext) + { + // Monster Iestyn: do we need to check if a mobj has already been checked? ...probably not I suspect + + if (!P_MobjTouchingPolyobj(po, mo)) + continue; + + if (!PIT_ChangeSector(mo, false)) + { + nofit = true; + return nofit; + } + } + } + } + } + } + } + if (sector->numattached) { - size_t i; sector_t *sec; for (i = 0; i < sector->numattached; i++) { @@ -4155,9 +4212,53 @@ boolean P_CheckSector(sector_t *sector, boolean crunch) } while (n); // repeat from scratch until all things left are marked valid // Nothing blocked us, so lets crush for real! + + // Sal: This stupid function chain is required to fix polyobjects not being able to crush. + // Monster Iestyn: don't use P_CheckSector actually just look for objects in the blockmap instead + validcount++; + + for (i = 0; i < sector->linecount; i++) + { + if (sector->lines[i]->polyobj) + { + polyobj_t *po = sector->lines[i]->polyobj; + if (po->validcount == validcount) + continue; // skip if already checked + if (!(po->flags & POF_SOLID)) + continue; + if (po->lines[0]->backsector == sector) // Make sure you're currently checking the control sector + { + INT32 x, y; + po->validcount = validcount; + + for (y = po->blockbox[BOXBOTTOM]; y <= po->blockbox[BOXTOP]; ++y) + { + for (x = po->blockbox[BOXLEFT]; x <= po->blockbox[BOXRIGHT]; ++x) + { + mobj_t *mo; + + if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight) + continue; + + mo = blocklinks[y * bmapwidth + x]; + + for (; mo; mo = mo->bnext) + { + // Monster Iestyn: do we need to check if a mobj has already been checked? ...probably not I suspect + + if (!P_MobjTouchingPolyobj(po, mo)) + continue; + + PIT_ChangeSector(mo, true); + return nofit; + } + } + } + } + } + } if (sector->numattached) { - size_t i; sector_t *sec; for (i = 0; i < sector->numattached; i++) { diff --git a/src/p_maputl.c b/src/p_maputl.c index 0ca84096a..740797fb0 100644 --- a/src/p_maputl.c +++ b/src/p_maputl.c @@ -311,6 +311,7 @@ fixed_t opentop, openbottom, openrange, lowfloor, highceiling; #ifdef ESLOPE pslope_t *opentopslope, *openbottomslope; #endif +ffloor_t *openfloorrover, *openceilingrover; // P_CameraLineOpening // P_LineOpening, but for camera @@ -517,6 +518,8 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) I_Assert(front != NULL); I_Assert(back != NULL); + openfloorrover = openceilingrover = NULL; + { // Set open and high/low values here fixed_t frontheight, backheight; @@ -641,6 +644,8 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) pslope_t *ceilingslope = opentopslope; pslope_t *floorslope = openbottomslope; #endif + ffloor_t *floorrover = NULL; + ffloor_t *ceilingrover = NULL; // Check for frontsector's fake floors for (rover = front->ffloors; rover; rover = rover->next) @@ -668,6 +673,7 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) #ifdef ESLOPE ceilingslope = *rover->b_slope; #endif + ceilingrover = rover; } else if (bottomheight < highestceiling) highestceiling = bottomheight; @@ -680,6 +686,7 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) #ifdef ESLOPE floorslope = *rover->t_slope; #endif + floorrover = rover; } else if (topheight > lowestfloor) lowestfloor = topheight; @@ -712,6 +719,7 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) #ifdef ESLOPE ceilingslope = *rover->b_slope; #endif + ceilingrover = rover; } else if (bottomheight < highestceiling) highestceiling = bottomheight; @@ -724,6 +732,7 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) #ifdef ESLOPE floorslope = *rover->t_slope; #endif + floorrover = rover; } else if (topheight > lowestfloor) lowestfloor = topheight; @@ -743,6 +752,7 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) #ifdef ESLOPE ceilingslope = NULL; #endif + ceilingrover = NULL; } else if (polysec->floorheight < highestceiling && delta1 >= delta2) highestceiling = polysec->floorheight; @@ -752,6 +762,7 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) #ifdef ESLOPE floorslope = NULL; #endif + floorrover = NULL; } else if (polysec->ceilingheight > lowestfloor && delta1 < delta2) lowestfloor = polysec->ceilingheight; @@ -765,6 +776,7 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) #ifdef ESLOPE openbottomslope = floorslope; #endif + openfloorrover = floorrover; } if (lowestceiling < opentop) { @@ -772,6 +784,7 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) #ifdef ESLOPE opentopslope = ceilingslope; #endif + openceilingrover = ceilingrover; } if (lowestfloor > lowfloor) diff --git a/src/p_maputl.h b/src/p_maputl.h index 1fcb68d4c..5042817c5 100644 --- a/src/p_maputl.h +++ b/src/p_maputl.h @@ -58,6 +58,7 @@ extern fixed_t opentop, openbottom, openrange, lowfloor, highceiling; #ifdef ESLOPE extern pslope_t *opentopslope, *openbottomslope; #endif +extern ffloor_t *openfloorrover, *openceilingrover; void P_LineOpening(line_t *plinedef, mobj_t *mobj); diff --git a/src/p_mobj.c b/src/p_mobj.c index 9a6e0f2bb..8c74fd76e 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -1784,7 +1784,7 @@ static void P_PushableCheckBustables(mobj_t *mo) continue; } - EV_CrumbleChain(node->m_sector, rover); + EV_CrumbleChain(NULL, rover); // node->m_sector // Run a linedef executor?? if (rover->master->flags & ML_EFFECT5) @@ -3047,7 +3047,7 @@ static void P_PlayerZMovement(mobj_t *mo) } } - clipmomz = P_PlayerHitFloor(mo->player); + clipmomz = P_PlayerHitFloor(mo->player, true); if (!(mo->player->pflags & PF_SPINNING) && mo->player->powers[pw_carry] != CR_NIGHTSMODE) mo->player->pflags &= ~PF_STARTDASH; @@ -3129,7 +3129,7 @@ nightsdone: { // DO THE MARIO! if (rover->flags & FF_SHATTERBOTTOM) // Brick block! - EV_CrumbleChain(node->m_sector, rover); + EV_CrumbleChain(NULL, rover); // node->m_sector else // Question block! EV_MarioBlock(rover, node->m_sector, mo); } @@ -4674,7 +4674,7 @@ static void P_Boss4PinchSpikeballs(mobj_t *mobj, angle_t angle, fixed_t dz) } dz /= 9; - + while ((base = base->tracer)) // there are 10 per spoke, remember that { dx = (originx + P_ReturnThrustX(mobj, angle, (9*132)<x)/9; @@ -5236,6 +5236,7 @@ static void P_Boss7Thinker(mobj_t *mobj) INT32 i; boolean foundgoop = false; INT32 closestNum; + UINT8 extrainfo = (mobj->spawnpoint ? mobj->spawnpoint->extrainfo : 0); // Looks for players in goop. If you find one, try to jump on him. for (i = 0; i < MAXPLAYERS; i++) @@ -5261,17 +5262,23 @@ static void P_Boss7Thinker(mobj_t *mobj) continue; mo2 = (mobj_t *)th; - if (mo2->type == MT_BOSS3WAYPOINT && mo2->spawnpoint) - { - dist = P_AproxDistance(players[i].mo->x - mo2->x, players[i].mo->y - mo2->y); + if (mo2->type != MT_BOSS3WAYPOINT) + continue; + if (!mo2->spawnpoint) + continue; + if (mo2->spawnpoint->extrainfo != extrainfo) + continue; + if (mobj->health <= mobj->info->damage && !(mo2->spawnpoint->options & 7)) + continue; // don't jump to center - if (closestNum == -1 || dist < closestdist) - { - closestNum = (mo2->spawnpoint->options & 7); - closestdist = dist; - foundgoop = true; - } - } + dist = P_AproxDistance(players[i].mo->x - mo2->x, players[i].mo->y - mo2->y); + + if (!(closestNum == -1 || dist < closestdist)) + continue; + + closestNum = (mo2->spawnpoint->options & 7); + closestdist = dist; + foundgoop = true; } waypointNum = closestNum; break; @@ -5280,17 +5287,14 @@ static void P_Boss7Thinker(mobj_t *mobj) if (!foundgoop) { - if (mobj->z > 1056*FRACUNIT) - waypointNum = 0; - else + // Don't jump to the center when health is low. + // Force the player to beat you with missiles. + if (mobj->z <= 1056*FRACUNIT || mobj->health <= mobj->info->damage) waypointNum = 1 + P_RandomKey(4); + else + waypointNum = 0; } - // Don't jump to the center when health is low. - // Force the player to beat you with missiles. - if (mobj->health <= mobj->info->damage && waypointNum == 0) - waypointNum = 1 + P_RandomKey(4); - if (mobj->tracer && mobj->tracer->type == MT_BOSS3WAYPOINT && mobj->tracer->spawnpoint && (mobj->tracer->spawnpoint->options & 7) == waypointNum) { @@ -5299,15 +5303,12 @@ static void P_Boss7Thinker(mobj_t *mobj) else waypointNum--; - waypointNum %= 5; - - if (waypointNum < 0) - waypointNum = 0; + if (mobj->health <= mobj->info->damage) + waypointNum = ((waypointNum + 3) % 4) + 1; // plus four to avoid modulo being negative, minus one to avoid waypoint #0 + else + waypointNum = ((waypointNum + 5) % 5); } - if (waypointNum == 0 && mobj->health <= mobj->info->damage) - waypointNum = 1 + (P_RandomFixed() & 1); - // scan the thinkers to find // the waypoint to use for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) @@ -5316,11 +5317,17 @@ static void P_Boss7Thinker(mobj_t *mobj) continue; mo2 = (mobj_t *)th; - if (mo2->type == MT_BOSS3WAYPOINT && mo2->spawnpoint && (mo2->spawnpoint->options & 7) == waypointNum) - { - hitspot = mo2; - break; - } + if (mo2->type != MT_BOSS3WAYPOINT) + continue; + if (!mo2->spawnpoint) + continue; + if ((mo2->spawnpoint->options & 7) != waypointNum) + continue; + if (mo2->spawnpoint->extrainfo != extrainfo) + continue; + + hitspot = mo2; + break; } if (hitspot == NULL) @@ -7004,6 +7011,9 @@ void P_MobjThinker(mobj_t *mobj) if (mobj->flags & MF_NOTHINK) return; + if ((mobj->flags & MF_BOSS) && mobj->spawnpoint && (bossdisabled & (1<spawnpoint->extrainfo))) + return; + // Remove dead target/tracer. if (mobj->target && P_MobjWasRemoved(mobj->target)) P_SetTarget(&mobj->target, NULL); @@ -8072,7 +8082,8 @@ void P_MobjThinker(mobj_t *mobj) mobj->tracer->z += mobj->height; } break; - case MT_WAVINGFLAG: + case MT_WAVINGFLAG1: + case MT_WAVINGFLAG2: { fixed_t base = (leveltime<<(FRACBITS+1)); mobj_t *seg = mobj->tracer, *prev = mobj; @@ -9675,15 +9686,14 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) case MT_BIGMINE: mobj->extravalue1 = FixedHypot(mobj->x, mobj->y)>>FRACBITS; break; - case MT_WAVINGFLAG: + case MT_WAVINGFLAG1: + case MT_WAVINGFLAG2: { mobj_t *prev = mobj, *cur; UINT8 i; - mobj->destscale <<= 2; - P_SetScale(mobj, mobj->destscale); for (i = 0; i <= 16; i++) // probably should be < but staying authentic to the Lua version { - cur = P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_WAVINGFLAGSEG); + cur = P_SpawnMobjFromMobj(mobj, 0, 0, 0, ((mobj->type == MT_WAVINGFLAG1) ? MT_WAVINGFLAGSEG1 : MT_WAVINGFLAGSEG2));; P_SetTarget(&prev->tracer, cur); cur->extravalue1 = i; prev = cur; @@ -11100,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; @@ -11754,13 +11770,14 @@ ML_EFFECT5 : Don't stop thinking when too far away P_SpawnMobjFromMobj(mobj, -1*FRACUNIT, 0, 0, MT_THZTREEBRANCH)->angle = mobjangle + ANGLE_270; } break; - case MT_CEZPOLE: + case MT_CEZPOLE1: + case MT_CEZPOLE2: { // Spawn the banner angle_t mobjangle = FixedAngle(mthing->angle<angle = mobjangle + ANGLE_90; + 0, ((mobj->type == MT_CEZPOLE1) ? MT_CEZBANNER1 : MT_CEZBANNER2))->angle = mobjangle + ANGLE_90; } break; case MT_HHZTREE_TOP: @@ -11984,6 +12001,15 @@ ML_EFFECT5 : Don't stop thinking when too far away if (i == MT_YELLOWDIAG || i == MT_REDDIAG) mobj->angle += ANGLE_22h; + if (i == MT_YELLOWHORIZ || i == MT_REDHORIZ || i == MT_BLUEHORIZ) + { + if (mthing->options & MTF_OBJECTFLIP) + mobj->z -= 16*FRACUNIT; + else + mobj->z += 16*FRACUNIT; + } + + if (mobj->flags & MF_NIGHTSITEM) { // Spawn already displayed @@ -12011,6 +12037,9 @@ ML_EFFECT5 : Don't stop thinking when too far away if (mthing->options & MTF_OBJECTSPECIAL) { + if (i == MT_YELLOWDIAG || i == MT_REDDIAG) + mobj->flags |= MF_NOGRAVITY; + if ((mobj->flags & MF_MONITOR) && mobj->info->speed != 0) { // flag for strong/weak random boxes @@ -12354,28 +12383,33 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing, boolean bonustime) if (nightsreplace) ringthing = MT_NIGHTSSTAR; + if (mthing->options & MTF_OBJECTFLIP) + { + z = ( +#ifdef ESLOPE + sec->c_slope ? P_GetZAt(sec->c_slope, x, y) : +#endif + sec->ceilingheight) - mobjinfo[ringthing].height; + if (mthing->options >> ZSHIFT) + z -= ((mthing->options >> ZSHIFT) << FRACBITS); + } + else + { + z = ( +#ifdef ESLOPE + sec->f_slope ? P_GetZAt(sec->f_slope, x, y) : +#endif + sec->floorheight); + if (mthing->options >> ZSHIFT) + z += ((mthing->options >> ZSHIFT) << FRACBITS); + } + for (r = 1; r <= 5; r++) { if (mthing->options & MTF_OBJECTFLIP) - { - z = ( -#ifdef ESLOPE - sec->c_slope ? P_GetZAt(sec->c_slope, x, y) : -#endif - sec->ceilingheight) - mobjinfo[ringthing].height - dist*r; - if (mthing->options >> ZSHIFT) - z -= ((mthing->options >> ZSHIFT) << FRACBITS); - } + z -= dist; else - { - z = ( -#ifdef ESLOPE - sec->f_slope ? P_GetZAt(sec->f_slope, x, y) : -#endif - sec->floorheight) + dist*r; - if (mthing->options >> ZSHIFT) - z += ((mthing->options >> ZSHIFT) << FRACBITS); - } + z += dist; mobj = P_SpawnMobj(x, y, z, ringthing); @@ -12409,31 +12443,36 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing, boolean bonustime) closestangle = FixedAngle(mthing->angle*FRACUNIT); fa = (closestangle >> ANGLETOFINESHIFT); + if (mthing->options & MTF_OBJECTFLIP) + { + z = ( +#ifdef ESLOPE + sec->c_slope ? P_GetZAt(sec->c_slope, x, y) : +#endif + sec->ceilingheight) - mobjinfo[ringthing].height; + if (mthing->options >> ZSHIFT) + z -= ((mthing->options >> ZSHIFT) << FRACBITS); + } + else + { + z = ( +#ifdef ESLOPE + sec->f_slope ? P_GetZAt(sec->f_slope, x, y) : +#endif + sec->floorheight); + if (mthing->options >> ZSHIFT) + z += ((mthing->options >> ZSHIFT) << FRACBITS); + } + for (r = 1; r <= iterations; r++) { x += FixedMul(64*FRACUNIT, FINECOSINE(fa)); y += FixedMul(64*FRACUNIT, FINESINE(fa)); if (mthing->options & MTF_OBJECTFLIP) - { - z = ( -#ifdef ESLOPE - sec->c_slope ? P_GetZAt(sec->c_slope, x, y) : -#endif - sec->ceilingheight) - mobjinfo[ringthing].height - 64*FRACUNIT*r; - if (mthing->options >> ZSHIFT) - z -= ((mthing->options >> ZSHIFT) << FRACBITS); - } + z -= 64*FRACUNIT; else - { - z = ( -#ifdef ESLOPE - sec->f_slope ? P_GetZAt(sec->f_slope, x, y) : -#endif - sec->floorheight) + 64*FRACUNIT*r; - if (mthing->options >> ZSHIFT) - z += ((mthing->options >> ZSHIFT) << FRACBITS); - } + z += 64*FRACUNIT; mobj = P_SpawnMobj(x, y, z, ringthing); diff --git a/src/p_mobj.h b/src/p_mobj.h index bfcb09210..77791f928 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -470,4 +470,5 @@ extern INT32 numhuntemeralds; extern boolean runemeraldmanager; extern UINT16 emeraldspawndelay; extern INT32 numstarposts; +extern UINT16 bossdisabled; #endif diff --git a/src/p_polyobj.c b/src/p_polyobj.c index 475fa41b7..040bdca2a 100644 --- a/src/p_polyobj.c +++ b/src/p_polyobj.c @@ -1873,7 +1873,8 @@ void T_PolyObjWaypoint(polywaypoint_t *th) po->lines[0]->backsector->floorheight = target->z - amtz; po->lines[0]->backsector->ceilingheight = target->z + amtz; // Sal: Remember to check your sectors! - P_CheckSector(po->lines[0]->frontsector, (boolean)(po->damage)); + // Monster Iestyn: we only need to bother with the back sector, now that P_CheckSector automatically checks the blockmap + // updating objects in the front one too just added teleporting to ground bugs P_CheckSector(po->lines[0]->backsector, (boolean)(po->damage)); // Apply action to mirroring polyobjects as well start = 0; @@ -1887,7 +1888,8 @@ void T_PolyObjWaypoint(polywaypoint_t *th) po->lines[0]->backsector->floorheight += diffz; // move up/down by same amount as the parent did po->lines[0]->backsector->ceilingheight += diffz; // Sal: Remember to check your sectors! - P_CheckSector(po->lines[0]->frontsector, (boolean)(po->damage)); + // Monster Iestyn: we only need to bother with the back sector, now that P_CheckSector automatically checks the blockmap + // updating objects in the front one too just added teleporting to ground bugs P_CheckSector(po->lines[0]->backsector, (boolean)(po->damage)); } @@ -2050,8 +2052,9 @@ void T_PolyObjWaypoint(polywaypoint_t *th) po->lines[0]->backsector->floorheight += momz; po->lines[0]->backsector->ceilingheight += momz; // Sal: Remember to check your sectors! - P_CheckSector(po->lines[0]->frontsector, (boolean)(po->damage)); // frontsector is NEEDED for crushing - P_CheckSector(po->lines[0]->backsector, (boolean)(po->damage)); // backsector may not be necessary, but just in case + // Monster Iestyn: we only need to bother with the back sector, now that P_CheckSector automatically checks the blockmap + // updating objects in the front one too just added teleporting to ground bugs + P_CheckSector(po->lines[0]->backsector, (boolean)(po->damage)); // Apply action to mirroring polyobjects as well start = 0; @@ -2065,7 +2068,8 @@ void T_PolyObjWaypoint(polywaypoint_t *th) po->lines[0]->backsector->floorheight += momz; po->lines[0]->backsector->ceilingheight += momz; // Sal: Remember to check your sectors! - P_CheckSector(po->lines[0]->frontsector, (boolean)(po->damage)); + // Monster Iestyn: we only need to bother with the back sector, now that P_CheckSector automatically checks the blockmap + // updating objects in the front one too just added teleporting to ground bugs P_CheckSector(po->lines[0]->backsector, (boolean)(po->damage)); } } diff --git a/src/p_saveg.c b/src/p_saveg.c index 2e4ba9228..24b68b971 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -3408,7 +3408,7 @@ static void P_NetUnArchiveThinkers(void) { for (;;) { - thinker_t* th; + thinker_t* th = NULL; tclass = READUINT8(save_p); if (tclass == tc_end) @@ -3990,6 +3990,7 @@ static void P_NetArchiveMisc(void) WRITEUINT32(save_p, leveltime); WRITEUINT32(save_p, ssspheres); WRITEINT16(save_p, lastmap); + WRITEUINT16(save_p, bossdisabled); WRITEUINT16(save_p, emeralds); WRITEUINT8(save_p, stagefailed); @@ -4067,6 +4068,7 @@ static inline boolean P_NetUnArchiveMisc(void) leveltime = READUINT32(save_p); ssspheres = READUINT32(save_p); lastmap = READINT16(save_p); + bossdisabled = READUINT16(save_p); emeralds = READUINT16(save_p); stagefailed = READUINT8(save_p); diff --git a/src/p_setup.c b/src/p_setup.c index c0aa7ffa3..c2872a836 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -102,6 +102,7 @@ line_t *lines; side_t *sides; mapthing_t *mapthings; INT32 numstarposts; +UINT16 bossdisabled; boolean levelloading; UINT8 levelfadecol; @@ -859,12 +860,7 @@ void P_ReloadRings(void) mt->z = (INT16)(R_PointInSubsector(mt->x << FRACBITS, mt->y << FRACBITS) ->sector->floorheight>>FRACBITS); - P_SpawnHoopsAndRings(mt, -#ifdef MANIASPHERES - true); -#else - !G_IsSpecialStage(gamemap)); // prevent flashing spheres in special stages -#endif + P_SpawnHoopsAndRings(mt, true); } } for (i = 0; i < numHoops; i++) @@ -878,11 +874,6 @@ void P_SwitchSpheresBonusMode(boolean bonustime) mobj_t *mo; thinker_t *th; -#ifndef MANIASPHERES - if (G_IsSpecialStage(gamemap)) // prevent flashing spheres in special stages - return; -#endif - // scan the thinkers to find spheres to switch for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) { @@ -2215,7 +2206,7 @@ static void P_LevelInitStuff(void) ssspheres = timeinmap = 0; // special stage - stagefailed = false; + stagefailed = true; // assume failed unless proven otherwise - P_GiveEmerald or emerald touchspecial // Reset temporary record data memset(&ntemprecords, 0, sizeof(nightsdata_t)); @@ -3126,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; @@ -3437,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 3cd0461e2..23ab04fc7 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -3554,6 +3554,29 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) } break; + case 449: // Enable bosses with parameter + { + INT32 bossid = sides[line->sidenum[0]].textureoffset>>FRACBITS; + if (bossid & ~15) // if any bits other than first 16 are set + { + CONS_Alert(CONS_WARNING, + M_GetText("Boss enable linedef (tag %d) has an invalid texture x offset.\nConsider changing it or removing it entirely.\n"), + line->tag); + break; + } + if (line->flags & ML_NOCLIMB) + { + bossdisabled |= (1<tag, mo, NULL); break; @@ -3918,6 +3941,18 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) } break; + case 460: // Award rings + { + INT16 rings = (sides[line->sidenum[0]].textureoffset>>FRACBITS); + INT32 delay = (sides[line->sidenum[0]].rowoffset>>FRACBITS); + if (mo && mo->player) + { + if (delay <= 0 || !(leveltime % delay)) + P_GivePlayerRings(mo->player, rings); + } + } + break; + #ifdef POLYOBJECTS case 480: // Polyobj_DoorSlide case 481: // Polyobj_DoorSwing @@ -4601,7 +4636,10 @@ DoneSection2: if (player->bot) break; if (!(maptol & TOL_NIGHTS) && G_IsSpecialStage(gamemap) && player->nightstime > 6) + { player->nightstime = 6; // Just let P_Ticker take care of the rest. + return; + } // Exit (for FOF exits; others are handled in P_PlayerThink in p_user.c) { @@ -4624,7 +4662,7 @@ DoneSection2: nextmapoverride = (INT16)(lines[lineindex].frontsector->floorheight>>FRACBITS); if (lines[lineindex].flags & ML_NOCLIMB) - skipstats = true; + skipstats = 1; } } break; @@ -6401,6 +6439,9 @@ void P_SpawnSpecials(INT32 fromnetsave) // but currently isn't. (void)fromnetsave; + // yep, we do this here - "bossdisabled" is considered an apparatus of specials. + bossdisabled = 0; + // Init special SECTORs. sector = sectors; for (i = 0; i < numsectors; i++, sector++) @@ -7289,6 +7330,24 @@ void P_SpawnSpecials(INT32 fromnetsave) case 431: break; + case 449: // Enable bosses with parameter + { + INT32 bossid = sides[*lines[i].sidenum].textureoffset>>FRACBITS; + if (bossid & ~15) // if any bits other than first 16 are set + { + CONS_Alert(CONS_WARNING, + M_GetText("Boss enable linedef (tag %d) has an invalid texture x offset.\nConsider changing it or removing it entirely.\n"), + lines[i].tag); + break; + } + if (!(lines[i].flags & ML_NOCLIMB)) + { + bossdisabled |= (1<height, MT_GOTEMERALD); - P_SetTarget(&emmo->target, players[consoleplayer].mo); - P_SetMobjState(emmo, mobjinfo[MT_GOTEMERALD].meleestate + em); - P_SetTarget(&players[consoleplayer].mo->tracer, emmo); + // Only visibly give it to ONE person! + UINT8 i, pnum = ((playeringame[consoleplayer]) && (!players[consoleplayer].spectator) && (players[consoleplayer].mo)) ? consoleplayer : 255; + for (i = 0; i < MAXPLAYERS; i++) + { + mobj_t *emmo; + if (!playeringame[i]) + continue; + if (players[i].spectator) + continue; + if (!players[i].mo) + continue; + + emmo = P_SpawnMobjFromMobj(players[i].mo, 0, 0, players[i].mo->height, MT_GOTEMERALD); + P_SetTarget(&emmo->target, players[i].mo); + P_SetMobjState(emmo, mobjinfo[MT_GOTEMERALD].meleestate + em); + P_SetTarget(&players[i].mo->tracer, emmo); + + if (pnum == 255) + { + i = pnum; + continue; + } + + if (i == pnum) + continue; + + emmo->flags2 |= MF2_DONTDRAW; + } } } @@ -597,7 +621,7 @@ static void P_DeNightserizePlayer(player_t *player) player->mo->skin = &skins[player->skin]; player->followitem = skins[player->skin].followitem; player->mo->color = player->skincolor; - G_GhostAddColor(GHC_NORMAL); + G_GhostAddColor(GHC_RETURNSKIN); // Restore aiming angle if (player == &players[consoleplayer]) @@ -615,7 +639,6 @@ static void P_DeNightserizePlayer(player_t *player) if (playeringame[i] && players[i].powers[pw_carry] == CR_NIGHTSMODE) players[i].nightstime = 1; // force everyone else to fall too. player->exiting = 3*TICRATE; - stagefailed = true; // NIGHT OVER // If you screwed up, kiss your score and ring bonus goodbye. // But only do this in special stage (and instakill!) In regular stages, wait til we hit the ground. @@ -716,6 +739,7 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime) if (!(cv_debug || devparm) && !(netgame || multiplayer || demoplayback)) player->mo->color = skins[DEFAULTNIGHTSSKIN].prefcolor; player->followitem = skins[DEFAULTNIGHTSSKIN].followitem; + G_GhostAddColor(GHC_NIGHTSSKIN); } player->nightstime = player->startedtime = player->lapstartedtime = nighttime*TICRATE; @@ -2007,7 +2031,7 @@ boolean P_InSpaceSector(mobj_t *mo) // Returns true if you are in space // // Handles player hitting floor surface. // Returns whether to clip momz. -boolean P_PlayerHitFloor(player_t *player) +boolean P_PlayerHitFloor(player_t *player, boolean dorollstuff) { boolean clipmomz; @@ -2015,22 +2039,31 @@ boolean P_PlayerHitFloor(player_t *player) if ((clipmomz = !(P_CheckDeathPitCollide(player->mo))) && player->mo->health && !player->spectator) { - if ((player->charability2 == CA2_SPINDASH) && !(player->pflags & PF_THOKKED) && (player->cmd.buttons & BT_USE) && (FixedHypot(player->mo->momx, player->mo->momy) > (5*player->mo->scale))) + if (dorollstuff) { - player->pflags |= PF_SPINNING; - P_SetPlayerMobjState(player->mo, S_PLAY_ROLL); - S_StartSound(player->mo, sfx_spin); + if ((player->charability2 == CA2_SPINDASH) && !(player->pflags & PF_THOKKED) && (player->cmd.buttons & BT_USE) && (FixedHypot(player->mo->momx, player->mo->momy) > (5*player->mo->scale))) + { + player->pflags |= PF_SPINNING; + P_SetPlayerMobjState(player->mo, S_PLAY_ROLL); + S_StartSound(player->mo, sfx_spin); + } + else + player->pflags &= ~PF_SPINNING; } - else - { - player->pflags &= ~PF_SPINNING; - if (player->pflags & PF_GLIDING) // ground gliding + if (player->pflags & PF_GLIDING) // ground gliding + { + if (dorollstuff) { player->skidtime = TICRATE; player->mo->tics = -1; } - else if (player->charability2 == CA2_MELEE && (player->panim == PA_ABILITY2 && player->mo->state-states != S_PLAY_MELEE_LANDING)) + else + player->pflags &= ~PF_GLIDING; + } + else if (player->charability2 == CA2_MELEE && player->panim == PA_ABILITY2) + { + if (player->mo->state-states != S_PLAY_MELEE_LANDING) { mobjtype_t type = player->revitem; P_SetPlayerMobjState(player->mo, S_PLAY_MELEE_LANDING); @@ -2049,7 +2082,7 @@ boolean P_PlayerHitFloor(player_t *player) fixed_t mu = FixedMul(player->maxdash, player->mo->scale); fixed_t mu2 = FixedHypot(player->mo->momx, player->mo->momy); fixed_t ev; - mobj_t *missile; + mobj_t *missile = NULL; if (mu2 < mu) mu2 = mu; ev = (50*FRACUNIT - (mu/25))/50; @@ -2062,45 +2095,48 @@ boolean P_PlayerHitFloor(player_t *player) P_ReturnThrustY(missile, throwang, mu)); // side to side component P_Thrust(missile, player->drawangle, mu2); // forward component P_SetObjectMomZ(missile, (4 + ((i&1)<<1))*FRACUNIT, true); + missile->momz += player->mo->pmomz; missile->fuse = TICRATE/2; missile->extravalue2 = ev; i++; throwang += ANG30; } - if (mobjinfo[type].seesound) + if (mobjinfo[type].seesound && missile) S_StartSound(missile, missile->info->seesound); } } - else if (player->pflags & PF_JUMPED || !(player->pflags & PF_SPINNING) + } + else if (player->charability2 == CA2_GUNSLINGER && player->panim == PA_ABILITY2) + ; + else if (player->pflags & PF_JUMPED || !(player->pflags & PF_SPINNING) || player->powers[pw_tailsfly] || player->mo->state-states == S_PLAY_FLY_TIRED) + { + if (player->cmomx || player->cmomy) { - if (player->cmomx || player->cmomy) - { - if (player->charflags & SF_DASHMODE && player->dashmode >= 3*TICRATE && player->panim != PA_DASH) - P_SetPlayerMobjState(player->mo, S_PLAY_DASH); - else if (player->speed >= FixedMul(player->runspeed, player->mo->scale) - && (player->panim != PA_RUN || player->mo->state-states == S_PLAY_FLOAT_RUN)) - P_SetPlayerMobjState(player->mo, S_PLAY_RUN); - else if ((player->rmomx || player->rmomy) - && (player->panim != PA_WALK || player->mo->state-states == S_PLAY_FLOAT)) - P_SetPlayerMobjState(player->mo, S_PLAY_WALK); - else if (!player->rmomx && !player->rmomy && player->panim != PA_IDLE) - P_SetPlayerMobjState(player->mo, S_PLAY_STND); - } - else - { - if (player->charflags & SF_DASHMODE && player->dashmode >= 3*TICRATE && player->panim != PA_DASH) - P_SetPlayerMobjState(player->mo, S_PLAY_DASH); - else if (player->speed >= FixedMul(player->runspeed, player->mo->scale) - && (player->panim != PA_RUN || player->mo->state-states == S_PLAY_FLOAT_RUN)) - P_SetPlayerMobjState(player->mo, S_PLAY_RUN); - else if ((player->mo->momx || player->mo->momy) - && (player->panim != PA_WALK || player->mo->state-states == S_PLAY_FLOAT)) - P_SetPlayerMobjState(player->mo, S_PLAY_WALK); - else if (!player->mo->momx && !player->mo->momy && player->panim != PA_IDLE) - P_SetPlayerMobjState(player->mo, S_PLAY_STND); - } + if (player->charflags & SF_DASHMODE && player->dashmode >= 3*TICRATE && player->panim != PA_DASH) + P_SetPlayerMobjState(player->mo, S_PLAY_DASH); + else if (player->speed >= FixedMul(player->runspeed, player->mo->scale) + && (player->panim != PA_RUN || player->mo->state-states == S_PLAY_FLOAT_RUN)) + P_SetPlayerMobjState(player->mo, S_PLAY_RUN); + else if ((player->rmomx || player->rmomy) + && (player->panim != PA_WALK || player->mo->state-states == S_PLAY_FLOAT)) + P_SetPlayerMobjState(player->mo, S_PLAY_WALK); + else if (!player->rmomx && !player->rmomy && player->panim != PA_IDLE) + P_SetPlayerMobjState(player->mo, S_PLAY_STND); + } + else + { + if (player->charflags & SF_DASHMODE && player->dashmode >= 3*TICRATE && player->panim != PA_DASH) + P_SetPlayerMobjState(player->mo, S_PLAY_DASH); + else if (player->speed >= FixedMul(player->runspeed, player->mo->scale) + && (player->panim != PA_RUN || player->mo->state-states == S_PLAY_FLOAT_RUN)) + P_SetPlayerMobjState(player->mo, S_PLAY_RUN); + else if ((player->mo->momx || player->mo->momy) + && (player->panim != PA_WALK || player->mo->state-states == S_PLAY_FLOAT)) + P_SetPlayerMobjState(player->mo, S_PLAY_WALK); + else if (!player->mo->momx && !player->mo->momy && player->panim != PA_IDLE) + P_SetPlayerMobjState(player->mo, S_PLAY_STND); } } @@ -2320,7 +2356,7 @@ static void P_CheckBustableBlocks(player_t *player) //if (metalrecording) // G_RecordBustup(rover); - EV_CrumbleChain(node->m_sector, rover); + EV_CrumbleChain(NULL, rover); // node->m_sector // Run a linedef executor?? if (rover->master->flags & ML_EFFECT5) @@ -2537,7 +2573,7 @@ static void P_CheckQuicksand(player_t *player) player->mo->z = ceilingheight - player->mo->height; if (player->mo->momz <= 0) - P_PlayerHitFloor(player); + P_PlayerHitFloor(player, false); } else { @@ -2549,7 +2585,7 @@ static void P_CheckQuicksand(player_t *player) player->mo->z = floorheight; if (player->mo->momz >= 0) - P_PlayerHitFloor(player); + P_PlayerHitFloor(player, false); } friction = abs(rover->master->v1->y - rover->master->v2->y)>>6; @@ -4410,6 +4446,10 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd) { player->mo->z += P_MobjFlip(player->mo); P_SetObjectMomZ(player->mo, player->mindash, false); + if (P_MobjFlip(player->mo)*player->mo->pmomz > 0) + player->mo->momz += player->mo->pmomz; // Add the platform's momentum to your jump. + else + player->mo->pmomz = 0; if (player->mo->eflags & MFE_UNDERWATER) player->mo->momz >>= 1; #if 0 @@ -11641,7 +11681,6 @@ void P_PlayerAfterThink(player_t *player) player->followmobj->threshold = player->mo->z; player->followmobj->movecount = player->panim; player->followmobj->angle = horizangle; - player->followmobj->scale = player->mo->scale; P_SetScale(player->followmobj, player->mo->scale); player->followmobj->destscale = player->mo->destscale; player->followmobj->radius = player->mo->radius; diff --git a/src/r_things.c b/src/r_things.c index 4b1586455..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 @@ -2446,9 +2450,11 @@ static void R_DrawMaskedList (drawnode_t* head) void R_DrawMasked(maskcount_t* masks, UINT8 nummasks) { - drawnode_t heads[nummasks]; /**< Drawnode lists; as many as number of views/portals. */ + drawnode_t *heads; /**< Drawnode lists; as many as number of views/portals. */ SINT8 i; + heads = calloc(nummasks, sizeof(drawnode_t)); + for (i = 0; i < nummasks; i++) { heads[i].next = heads[i].prev = &heads[i]; @@ -2474,6 +2480,8 @@ void R_DrawMasked(maskcount_t* masks, UINT8 nummasks) R_DrawMaskedList(&heads[nummasks - 1]); R_ClearDrawNodes(&heads[nummasks - 1]); } + + free(heads); } // ========================================================================== @@ -2503,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 @@ -2525,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: @@ -2537,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; } @@ -2556,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; @@ -2988,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 @@ -3018,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) @@ -3094,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); @@ -3130,6 +3125,7 @@ next_token: // Add sprites R_LoadSkinSprites(wadnum, &lump, &lastlump, skin); + //ST_LoadFaceGraphics(numskins); -- nah let's do this elsewhere R_FlushTranslationColormapCache(); @@ -3140,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); @@ -3265,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 52131548f..b7dca10ba 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -524,6 +524,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; @@ -662,7 +663,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. @@ -715,7 +716,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. @@ -1675,7 +1676,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; + } } } @@ -1982,8 +1993,10 @@ void GameMIDIMusic_OnChange(void) } } +#ifdef HAVE_OPENMPT void ModFilter_OnChange(void) { if (openmpt_mhandle) openmpt_module_set_render_param(openmpt_mhandle, OPENMPT_MODULE_RENDER_INTERPOLATIONFILTER_LENGTH, cv_modfilter.value); -} \ No newline at end of file +} +#endif \ No newline at end of file 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/sdl/Srb2SDL-vc10.vcxproj b/src/sdl/Srb2SDL-vc10.vcxproj index d05a0d324..72c38b3dc 100644 --- a/src/sdl/Srb2SDL-vc10.vcxproj +++ b/src/sdl/Srb2SDL-vc10.vcxproj @@ -277,6 +277,7 @@ + @@ -430,6 +431,7 @@ + diff --git a/src/sdl/Srb2SDL-vc10.vcxproj.filters b/src/sdl/Srb2SDL-vc10.vcxproj.filters index ca6bd38d2..9e442000f 100644 --- a/src/sdl/Srb2SDL-vc10.vcxproj.filters +++ b/src/sdl/Srb2SDL-vc10.vcxproj.filters @@ -453,6 +453,9 @@ Hw_Hardware + + R_Rend + @@ -894,6 +897,10 @@ Hw_Hardware + + + R_Rend + diff --git a/src/sdl/mixer_sound.c b/src/sdl/mixer_sound.c index 0dd06d5f5..dd9aaaef4 100644 --- a/src/sdl/mixer_sound.c +++ b/src/sdl/mixer_sound.c @@ -196,7 +196,7 @@ consvar_t cv_miditimiditypath = {"midisoundbank", "./timidity", CV_SAVE, NULL, N static void var_cleanup(void) { - loop_point = song_length = 0.0f; + song_length = loop_point = 0.0f; music_bytes = fading_source = fading_target =\ fading_timer = fading_duration = 0; diff --git a/src/st_stuff.c b/src/st_stuff.c index 4122793ad..aefb4c53c 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -110,6 +110,7 @@ static patch_t *orngstat; static patch_t *redstat; static patch_t *yelstat; static patch_t *nbracket; +static patch_t *nring; static patch_t *nhud[12]; static patch_t *nsshud; static patch_t *nbon[12]; @@ -226,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) @@ -311,6 +312,7 @@ void ST_LoadGraphics(void) redstat = W_CachePatchName("REDSTAT", PU_HUDGFX); yelstat = W_CachePatchName("YELSTAT", PU_HUDGFX); nbracket = W_CachePatchName("NBRACKET", PU_HUDGFX); + nring = W_CachePatchName("NRNG1", PU_HUDGFX); for (i = 0; i < 12; ++i) { nhud[i] = W_CachePatchName(va("NHUD%d", i+1), PU_HUDGFX); @@ -341,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; } @@ -353,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) @@ -1545,7 +1561,7 @@ static void ST_drawNiGHTSLink(void) static void ST_drawNiGHTSHUD(void) { INT32 origamount; - INT32 total_spherecount; + INT32 total_spherecount, total_ringcount; const boolean oldspecialstage = (G_IsSpecialStage(gamemap) && !(maptol & TOL_NIGHTS)); // Drill meter @@ -1617,33 +1633,28 @@ static void ST_drawNiGHTSHUD(void) #endif ST_DrawTopLeftOverlayPatch(16, 8, nbracket); if (G_IsSpecialStage(gamemap)) -#ifdef MANIASPHERES ST_DrawTopLeftOverlayPatch(24, 16, ( - (stplyr->bonustime && (leveltime & 4)) ? nssbon : nsshud)); -#else - ST_DrawTopLeftOverlayPatch(24, 16, (nsshud)); -#endif + (stplyr->bonustime && (leveltime & 4) && (states[S_BLUESPHEREBONUS].frame & FF_ANIMATE)) ? nssbon : nsshud)); else ST_DrawTopLeftOverlayPatch(24, 16, *(((stplyr->bonustime) ? nbon : nhud)+((leveltime/2)%12))); if (G_IsSpecialStage(gamemap)) { INT32 i; - total_spherecount = 0; + total_spherecount = total_ringcount = 0; for (i = 0; i < MAXPLAYERS; i++) - if (playeringame[i] /*&& players[i].powers[pw_carry] == CR_NIGHTSMODE*/ && players[i].spheres) - total_spherecount += players[i].spheres; + { + if (!playeringame[i]) + continue; + total_spherecount += players[i].spheres; + total_ringcount += players[i].rings; + } } else - total_spherecount = stplyr->spheres; - - /*if (oldspecialstage) { - if (total_spherecount < ssspheres) - total_spherecount = ssspheres - total_spherecount; - else - total_spherecount = 0; - }*/ + total_spherecount = stplyr->spheres; + total_ringcount = stplyr->spheres; + } if (stplyr->capsule) { @@ -1731,6 +1742,27 @@ static void ST_drawNiGHTSHUD(void) else ST_DrawTopLeftOverlayPatch(40, 8 + 5, narrow[8]); + if (oldspecialstage) + { + // invert for s3k style junk + total_spherecount = ssspheres - total_spherecount; + if (total_spherecount < 0) + total_spherecount = 0; + + if (nummaprings > 0) // don't count down if there ISN'T a valid maximum number of rings, like sonic 3 + { + total_ringcount = nummaprings - total_ringcount; + if (total_ringcount < 0) + total_ringcount = 0; + } + + // now rings! you know, for that perfect bonus. + V_DrawScaledPatch(272, 8, V_PERPLAYER|V_SNAPTOTOP|V_SNAPTORIGHT|V_HUDTRANS, nbracket); + V_DrawScaledPatch(280, 16+1, V_PERPLAYER|V_SNAPTOTOP|V_SNAPTORIGHT|V_HUDTRANS, nring); + V_DrawScaledPatch(280, 8+5, V_FLIP|V_PERPLAYER|V_SNAPTOTOP|V_SNAPTORIGHT|V_HUDTRANS, narrow[8]); + V_DrawTallNum(272, 8 + 11, V_PERPLAYER|V_SNAPTOTOP|V_SNAPTORIGHT|V_HUDTRANS, total_ringcount); + } + if (total_spherecount >= 100) V_DrawTallNum((total_spherecount >= 1000) ? 76 : 72, 8 + 11, V_PERPLAYER|V_SNAPTOTOP|V_SNAPTOLEFT|V_HUDTRANS, total_spherecount); else 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); diff --git a/src/win32/Srb2win-vc10.vcxproj b/src/win32/Srb2win-vc10.vcxproj index acab2507a..c0fe8eda9 100644 --- a/src/win32/Srb2win-vc10.vcxproj +++ b/src/win32/Srb2win-vc10.vcxproj @@ -293,6 +293,7 @@ + @@ -443,6 +444,7 @@ + diff --git a/src/win32/Srb2win-vc10.vcxproj.filters b/src/win32/Srb2win-vc10.vcxproj.filters index c21cedb8a..93806e395 100644 --- a/src/win32/Srb2win-vc10.vcxproj.filters +++ b/src/win32/Srb2win-vc10.vcxproj.filters @@ -456,6 +456,10 @@ Hw_Hardware + + + R_Rend + @@ -857,6 +861,10 @@ Hw_Hardware + + + R_Rend + diff --git a/src/y_inter.c b/src/y_inter.c index 992193964..975902ab0 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -86,8 +86,8 @@ typedef union INT32 passedx3; INT32 passedx4; - y_bonus_t bonus; - patch_t *bonuspatch; + y_bonus_t bonuses[2]; + patch_t *bonuspatches[2]; patch_t *pscore; // SCORE UINT32 score; // fake score @@ -326,34 +326,36 @@ void Y_IntermissionDrawer(void) // draw the header if (intertic <= 2*TICRATE) animatetic = 0; - else if (!animatetic && data.spec.bonus.points == 0 && data.spec.passed3[0] != '\0') + else if (!animatetic && data.spec.bonuses[0].points == 0 && data.spec.bonuses[1].points == 0 && data.spec.passed3[0] != '\0') animatetic = intertic + TICRATE; if (animatetic && (tic_t)intertic >= animatetic) { const INT32 scradjust = (vid.width/vid.dupx)>>3; // 40 for BASEVIDWIDTH INT32 animatetimer = (intertic - animatetic); - if (animatetimer <= 14) + if (animatetimer <= 16) { - xoffset1 = -(animatetimer * scradjust); - xoffset2 = -((animatetimer-2) * scradjust); - xoffset3 = -((animatetimer-4) * scradjust); - xoffset4 = -((animatetimer-6) * scradjust); - xoffset5 = -((animatetimer-8) * scradjust); + xoffset1 = -(animatetimer * scradjust); + xoffset2 = -((animatetimer- 2) * scradjust); + xoffset3 = -((animatetimer- 4) * scradjust); + xoffset4 = -((animatetimer- 6) * scradjust); + xoffset5 = -((animatetimer- 8) * scradjust); + xoffset6 = -((animatetimer-10) * scradjust); if (xoffset2 > 0) xoffset2 = 0; if (xoffset3 > 0) xoffset3 = 0; if (xoffset4 > 0) xoffset4 = 0; if (xoffset5 > 0) xoffset5 = 0; + if (xoffset6 > 0) xoffset6 = 0; } - else if (animatetimer < 32) + else if (animatetimer < 34) { drawsection = 1; - xoffset1 = (22-animatetimer) * scradjust; - xoffset2 = (24-animatetimer) * scradjust; - xoffset3 = (26-animatetimer) * scradjust; - xoffset4 = (28-animatetimer) * scradjust; - xoffset5 = (30-animatetimer) * scradjust; - xoffset6 = (32-animatetimer) * scradjust; + xoffset1 = (24-animatetimer) * scradjust; + xoffset2 = (26-animatetimer) * scradjust; + xoffset3 = (28-animatetimer) * scradjust; + xoffset4 = (30-animatetimer) * scradjust; + xoffset5 = (32-animatetimer) * scradjust; + xoffset6 = (34-animatetimer) * scradjust; if (xoffset1 < 0) xoffset1 = 0; if (xoffset2 < 0) xoffset2 = 0; if (xoffset3 < 0) xoffset3 = 0; @@ -370,9 +372,9 @@ void Y_IntermissionDrawer(void) if (drawsection == 1) { - const char *ringtext = "\x86" "50 RINGS, NO SHIELD"; - const char *tut1text = "\x86" "PRESS " "\x82" "SPIN"; - const char *tut2text = "\x86" "MID-" "\x82" "JUMP"; + const char *ringtext = "\x82" "50 RINGS, NO SHIELD"; + const char *tut1text = "\x82" "PRESS " "\x80" "SPIN"; + const char *tut2text = "\x82" "MID-" "\x80" "JUMP"; ttheight = 16; V_DrawLevelTitle(data.spec.passedx1 + xoffset1, ttheight, 0, data.spec.passed1); ttheight += V_LevelNameHeight(data.spec.passed3) + 2; @@ -389,6 +391,7 @@ void Y_IntermissionDrawer(void) } else { + INT32 yoffset = 0; if (data.spec.passed1[0] != '\0') { ttheight = 24; @@ -402,22 +405,31 @@ void Y_IntermissionDrawer(void) V_DrawLevelTitle(data.spec.passedx2 + xoffset1, ttheight, 0, data.spec.passed2); } - V_DrawScaledPatch(152 + xoffset3, 108, 0, data.spec.bonuspatch); - V_DrawTallNum(BASEVIDWIDTH + xoffset3 - 68, 109, 0, data.spec.bonus.points); - V_DrawScaledPatch(152 + xoffset4, 124, 0, data.spec.pscore); - V_DrawTallNum(BASEVIDWIDTH + xoffset4 - 68, 125, 0, data.spec.score); + V_DrawScaledPatch(152 + xoffset3, 108, 0, data.spec.bonuspatches[0]); + V_DrawTallNum(BASEVIDWIDTH + xoffset3 - 68, 109, 0, data.spec.bonuses[0].points); + if (data.spec.bonuses[1].display) + { + V_DrawScaledPatch(152 + xoffset4, 124, 0, data.spec.bonuspatches[1]); + V_DrawTallNum(BASEVIDWIDTH + xoffset4 - 68, 125, 0, data.spec.bonuses[1].points); + yoffset = 16; + // hack; pass the buck along... + xoffset4 = xoffset5; + xoffset5 = xoffset6; + } + V_DrawScaledPatch(152 + xoffset4, 124+yoffset, 0, data.spec.pscore); + V_DrawTallNum(BASEVIDWIDTH + xoffset4 - 68, 125+yoffset, 0, data.spec.score); // Draw continues! if (!multiplayer /* && (data.spec.continues & 0x80) */) // Always draw outside of netplay { UINT8 continues = data.spec.continues & 0x7F; - V_DrawScaledPatch(152 + xoffset5, 150, 0, data.spec.pcontinues); + V_DrawScaledPatch(152 + xoffset5, 150+yoffset, 0, data.spec.pcontinues); for (i = 0; i < continues; ++i) { if ((data.spec.continues & 0x80) && i == continues-1 && (endtic < 0 || intertic%20 < 10)) break; - V_DrawContinueIcon(246 + xoffset5 - (i*12), 162, 0, *data.spec.playerchar, *data.spec.playercolor); + V_DrawContinueIcon(246 + xoffset5 - (i*12), 162+yoffset, 0, *data.spec.playerchar, *data.spec.playercolor); } } } @@ -904,7 +916,7 @@ void Y_Ticker(void) { INT32 i; UINT32 oldscore = data.spec.score; - boolean skip = false, super = false; + boolean skip = false, super = false, anybonuses = false; if (!intertic) // first time only { @@ -946,16 +958,26 @@ void Y_Ticker(void) return; } - // ring bonus counts down by 222 each tic - data.spec.bonus.points -= 222; - data.spec.score += 222; - if (data.spec.bonus.points < 0 || skip == true) // went too far + // bonuses count down by 222 each tic + for (i = 0; i < 2; ++i) { - data.spec.score += data.spec.bonus.points; - data.spec.bonus.points = 0; + if (!data.spec.bonuses[i].points) + continue; + + data.spec.bonuses[i].points -= 222; + data.spec.score += 222; + if (data.spec.bonuses[i].points < 0 || skip == true) // too far? + { + data.spec.score += data.spec.bonuses[i].points; + data.spec.bonuses[i].points = 0; + } + if (data.spec.score > MAXSCORE) + data.spec.score = MAXSCORE; + if (data.spec.bonuses[i].points > 0) + anybonuses = true; } - if (!data.spec.bonus.points) + if (!anybonuses) { tallydonetic = intertic; if (!((data.spec.continues & 0x80) || (super && ALL7EMERALDS(emeralds)))) // don't set endtic yet! @@ -1301,7 +1323,9 @@ void Y_StartIntermission(void) // give out ring bonuses Y_AwardSpecialStageBonus(); - data.spec.bonuspatch = W_CachePatchName(data.spec.bonus.patch, PU_STATIC); + for (i = 0; i < 2; ++i) + data.spec.bonuspatches[i] = W_CachePatchName(data.spec.bonuses[i].patch, PU_STATIC); + data.spec.pscore = W_CachePatchName("YB_SCORE", PU_STATIC); data.spec.pcontinues = W_CachePatchName("YB_CONTI", PU_STATIC); @@ -1831,7 +1855,7 @@ static void Y_SetPerfectBonus(player_t *player, y_bonus_t *bstruct) memset(bstruct, 0, sizeof(y_bonus_t)); strncpy(bstruct->patch, "YB_PERFE", sizeof(bstruct->patch)); - if (data.coop.gotperfbonus == -1) + if (intertype != int_coop || data.coop.gotperfbonus == -1) { INT32 sharedringtotal = 0; for (i = 0; i < MAXPLAYERS; i++) @@ -1840,15 +1864,33 @@ static void Y_SetPerfectBonus(player_t *player, y_bonus_t *bstruct) sharedringtotal += players[i].rings; } if (!sharedringtotal || nummaprings == -1 || sharedringtotal < nummaprings) - data.coop.gotperfbonus = 0; + bstruct->display = false; else - data.coop.gotperfbonus = 1; + { + bstruct->display = true; + bstruct->points = 50000; + } } - if (!data.coop.gotperfbonus) + if (intertype != int_coop) return; + data.coop.gotperfbonus = (bstruct->display ? 1 : 0); +} + +static void Y_SetSpecialRingBonus(player_t *player, y_bonus_t *bstruct) +{ + INT32 i, sharedringtotal = 0; + + (void)player; + strncpy(bstruct->patch, "YB_RING", sizeof(bstruct->patch)); bstruct->display = true; - bstruct->points = 50000; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i]) continue; + sharedringtotal += players[i].rings; + } + bstruct->points = max(0, (sharedringtotal) * 100); } // This list can be extended in the future with SOC/Lua, perhaps. @@ -1953,23 +1995,33 @@ static void Y_AwardCoopBonuses(void) static void Y_AwardSpecialStageBonus(void) { INT32 i, oldscore, ptlives; - y_bonus_t localbonus; + y_bonus_t localbonuses[2]; data.spec.score = players[consoleplayer].score; - memset(&data.spec.bonus, 0, sizeof(data.spec.bonus)); - data.spec.bonuspatch = NULL; + memset(data.spec.bonuses, 0, sizeof(data.spec.bonuses)); + memset(data.spec.bonuspatches, 0, sizeof(data.coop.bonuspatches)); for (i = 0; i < MAXPLAYERS; i++) { oldscore = players[i].score; if (!playeringame[i] || players[i].lives < 1) // not active or game over - Y_SetNullBonus(&players[i], &localbonus); - else if (maptol & TOL_NIGHTS) // Mare score instead of Rings - Y_SetNightsBonus(&players[i], &localbonus); + { + Y_SetNullBonus(&players[i], &localbonuses[0]); + Y_SetNullBonus(&players[i], &localbonuses[1]); + } + else if (maptol & TOL_NIGHTS) // NiGHTS bonus score instead of Rings + { + Y_SetNightsBonus(&players[i], &localbonuses[0]); + Y_SetNullBonus(&players[i], &localbonuses[1]); + } else - Y_SetRingBonus(&players[i], &localbonus); - players[i].score += localbonus.points; + { + Y_SetSpecialRingBonus(&players[i], &localbonuses[0]); + Y_SetPerfectBonus(&players[i], &localbonuses[1]); + } + players[i].score += localbonuses[0].points; + players[i].score += localbonuses[1].points; if (players[i].score > MAXSCORE) players[i].score = MAXSCORE; @@ -1982,7 +2034,7 @@ static void Y_AwardSpecialStageBonus(void) if (i == consoleplayer) { data.spec.gotlife = (((netgame || multiplayer) && gametype == GT_COOP && cv_cooplives.value == 0) ? 0 : ptlives); - M_Memcpy(&data.spec.bonus, &localbonus, sizeof(data.spec.bonus)); + M_Memcpy(&data.spec.bonuses, &localbonuses, sizeof(data.spec.bonuses)); // Continues related data.spec.continues = min(players[i].continues, 8); @@ -2057,7 +2109,8 @@ static void Y_UnloadData(void) // unload the special stage patches //UNLOAD(data.spec.cemerald); //UNLOAD(data.spec.nowsuper); - UNLOAD(data.spec.bonuspatch); + UNLOAD(data.spec.bonuspatches[1]); + UNLOAD(data.spec.bonuspatches[0]); UNLOAD(data.spec.pscore); UNLOAD(data.spec.pcontinues); break;