diff --git a/src/d_player.h b/src/d_player.h index 098b4485ca5e602328a4b03b5e0c6719fe6d154b..95d2f609dab322189fcb3892d7beace6d0fd45dc 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -373,6 +373,16 @@ typedef enum AI_SPINFOLLOW } aistatetype_t; +// NiGHTS text +typedef enum +{ + NTV_NONE = 0, + NTV_GETSPHERES, + NTV_GETMORESPHERES, + NTV_BONUSTIMESTART, + NTV_BONUSTIMEEND, +} nightstextvar_t; + // ======================================================================== // PLAYER STRUCTURE @@ -572,7 +582,8 @@ typedef struct player_s // Statistical purposes. tic_t marebegunat; // Leveltime when mare begun tic_t startedtime; // Time which you started this mare with. - tic_t finishedtime; // Time it took you to finish the mare (used for display) + tic_t finishedtime; // The time it took to destroy the capsule on this mare (used for bonus time display) + tic_t lastmaretime; // The time it took to complete the last mare (used for rank display) tic_t lapbegunat; // Leveltime when lap begun tic_t lapstartedtime; // Time which you started this lap with. INT16 finishedspheres; // The spheres you had left upon finishing the mare @@ -587,7 +598,7 @@ typedef struct player_s UINT8 totalmarebonuslap; // total mare bonus lap INT32 maxlink; // maximum link obtained UINT8 texttimer; // nights_texttime should not be local - UINT8 textvar; // which line of NiGHTS text to show -- let's not use cheap hacks + UINT8 textvar; // which line of NiGHTS text to show -- see nightstextvar_t INT16 lastsidehit, lastlinehit; diff --git a/src/deh_soc.c b/src/deh_soc.c index beaf7cbf0cd4f861ce413fc9c504755a2b4965e8..65db63ebb13fab76dc84dbbfb64f43fa13a8895f 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -1543,6 +1543,12 @@ void readlevelheader(MYFILE *f, INT32 num) P_AddGradesForMare((INT16)(num-1), mare-1, word2); } + // NiGHTS time limits (per mare) + else if (fastncmp(word, "NIGHTSTIME", 10)) + { + P_AddNiGHTSTimes((INT16)(num-1), word2); + } + // Strings that can be truncated else if (fastcmp(word, "SELECTHEADING")) { diff --git a/src/doomstat.h b/src/doomstat.h index c2fc963478b56682a03acf6d592398c134e861bc..bde2bacc72890d47b61a6085a26c5cda33dcfad0 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -335,6 +335,7 @@ typedef struct INT32 sstimer; ///< Timer for special stages. UINT32 ssspheres; ///< Sphere requirement in special stages. fixed_t gravity; ///< Map-wide gravity. + UINT16 nightstimer[8]; ///< Per-mare time limits for NiGHTS stages. // Title card. char ltzzpatch[8+1]; ///< Zig zag patch. diff --git a/src/f_finale.c b/src/f_finale.c index 3478c0c6ba0d14425ef2c334f4ca051b10e2eac7..9ff50147e0bb36ae17d3c23672b82d6528f54871 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -229,6 +229,7 @@ static UINT8 cutscene_boostspeed = 0; char stjrintro[9] = "STJRI000"; static huddrawlist_h luahuddrawlist_title; +static huddrawlist_h luahuddrawlist_continue[2]; // // This alters the text string cutscene_disptext. @@ -3532,9 +3533,14 @@ void F_TitleDemoTicker(void) static skin_t *contskins[2]; static UINT16 cont_spr2[2][6]; static UINT8 *contcolormaps[2]; +static player_t *contPlayers[2]; +static skincolornum_t contColors[2]; // it's possible to change your skincolor in the continue screen, so this is for Lua to identify the skincolor that was used to cache the colormap +static boolean contOverrides[2]; void F_StartContinue(void) { + UINT8 i; + I_Assert(!netgame && !multiplayer); if (continuesInSession && players[consoleplayer].continues <= 0) @@ -3557,9 +3563,12 @@ void F_StartContinue(void) S_ChangeMusicInternal("_conti", false); S_StopSounds(); + contPlayers[0] = &players[consoleplayer]; contskins[0] = skins[players[consoleplayer].skin]; + cont_spr2[0][0] = P_GetSkinSprite2(contskins[0], SPR2_CNT1, NULL); cont_spr2[0][2] = contskins[0]->contangle & 7; + contColors[0] = players[consoleplayer].skincolor; contcolormaps[0] = R_GetTranslationColormap(players[consoleplayer].skin, players[consoleplayer].skincolor, GTC_CACHE); cont_spr2[0][4] = contskins[0]->sprites[cont_spr2[0][0]].numframes; cont_spr2[0][5] = max(1, contskins[0]->contspeed); @@ -3573,9 +3582,12 @@ void F_StartContinue(void) else // HACK secondplaya = 1; + contPlayers[1] = &players[secondplaya]; contskins[1] = skins[players[secondplaya].skin]; + cont_spr2[1][0] = P_GetSkinSprite2(contskins[1], SPR2_CNT4, NULL); cont_spr2[1][2] = (contskins[1]->contangle >> 3) & 7; + contColors[1] = players[secondplaya].skincolor; contcolormaps[1] = R_GetTranslationColormap(players[secondplaya].skin, players[secondplaya].skincolor, GTC_CACHE); cont_spr2[1][4] = contskins[1]->sprites[cont_spr2[1][0]].numframes; if (cont_spr2[1][0] == SPR2_CNT4) @@ -3595,6 +3607,58 @@ void F_StartContinue(void) timetonext = (11*TICRATE)+11; continuetime = 0; + + // allocate and/or clear Lua continue screen draw lists + for (i = 0; i < 2; i++) + { + if (!LUA_HUD_IsDrawListValid(luahuddrawlist_continue[i])) + { + LUA_HUD_DestroyDrawList(luahuddrawlist_continue[i]); + luahuddrawlist_continue[i] = LUA_HUD_CreateDrawList(); + } + LUA_HUD_ClearDrawList(luahuddrawlist_continue[i]); + contOverrides[i] = false; + } +} + +static void F_DestroyContinueDrawLists(void) +{ + UINT8 i; + for (i = 0; i < 2; i++) + { + LUA_HUD_DestroyDrawList(luahuddrawlist_continue[i]); + luahuddrawlist_continue[i] = NULL; + contOverrides[i] = false; + } +} + +static void F_DrawContinueCharacter(INT32 dx, INT32 dy, UINT8 n) +{ + spritedef_t *sprdef; + spriteframe_t *sprframe; + patch_t *patch; + + if (renderisnewtic) + { + LUA_HUD_ClearDrawList(luahuddrawlist_continue[n]); + contOverrides[n] = LUA_HookCharacterHUD + ( + HUD_HOOK(continue), luahuddrawlist_continue[n], contPlayers[n], + dx, dy, contskins[n]->highresscale, + (INT32)(&contskins[n] - skins), cont_spr2[n][0], cont_spr2[n][1], cont_spr2[n][2] + 1, contColors[n], // add 1 to rotation to convert internal angle numbers (0-7) to WAD editor angle numbers (1-8) + imcontinuing ? continuetime : timetonext, imcontinuing + ); + } + + LUA_HUD_DrawList(luahuddrawlist_continue[n]); + + if (contOverrides[n] == true) + return; + + sprdef = &contskins[n]->sprites[cont_spr2[n][0]]; + sprframe = &sprdef->spriteframes[cont_spr2[n][1]]; + patch = W_CachePatchNum(sprframe->lumppat[cont_spr2[n][2]], PU_PATCH_LOWPRIORITY); + V_DrawFixedPatch((dx), (dy), contskins[n]->highresscale, (sprframe->flip & (1<<cont_spr2[n][2])) ? V_FLIP : 0, patch, contcolormaps[n]); } // @@ -3603,8 +3667,6 @@ void F_StartContinue(void) // void F_ContinueDrawer(void) { - spritedef_t *sprdef; - spriteframe_t *sprframe; patch_t *patch; INT32 i, x = (BASEVIDWIDTH>>1), ncontinues = players[consoleplayer].continues; char numbuf[9] = "CONTNUM*"; @@ -3649,7 +3711,7 @@ void F_ContinueDrawer(void) else if (ncontinues > 10) { if (!(continuetime & 1) || continuetime > 17) - V_DrawContinueIcon(x, 68, 0, players[consoleplayer].skin, players[consoleplayer].skincolor); + V_DrawContinueIcon(x, 68, 0, (INT32)(&contskins[0] - skins), contColors[0]); V_DrawScaledPatch(x+12, 66, 0, stlivex); V_DrawRightAlignedString(x+38, 64, 0, va("%d",(imcontinuing ? ncontinues-1 : ncontinues))); @@ -3663,7 +3725,7 @@ void F_ContinueDrawer(void) { if (i == (ncontinues/2) && ((continuetime & 1) || continuetime > 17)) continue; - V_DrawContinueIcon(x - (i*30), 68, 0, players[consoleplayer].skin, players[consoleplayer].skincolor); + V_DrawContinueIcon(x - (i*30), 68, 0, (INT32)(&contskins[0] - skins), contColors[0]); } x = BASEVIDWIDTH>>1; } @@ -3703,21 +3765,12 @@ void F_ContinueDrawer(void) else if (lift[0] > TICRATE+5) lift[0] = TICRATE+5; -#define drawchar(dx, dy, n) {\ - sprdef = &contskins[n]->sprites[cont_spr2[n][0]];\ - sprframe = &sprdef->spriteframes[cont_spr2[n][1]];\ - patch = W_CachePatchNum(sprframe->lumppat[cont_spr2[n][2]], PU_PATCH_LOWPRIORITY);\ - V_DrawFixedPatch((dx), (dy), contskins[n]->highresscale, (sprframe->flip & (1<<cont_spr2[n][2])) ? V_FLIP : 0, patch, contcolormaps[n]);\ - } - if (offsy < 0) - drawchar((BASEVIDWIDTH<<(FRACBITS-1))-offsx, ((140-lift[0])<<FRACBITS)-offsy, 0); + F_DrawContinueCharacter((BASEVIDWIDTH<<(FRACBITS-1))-offsx, ((140-lift[0])<<FRACBITS)-offsy, 0); if (contskins[1]) - drawchar((BASEVIDWIDTH<<(FRACBITS-1))+offsx, ((140-lift[1])<<FRACBITS)+offsy, 1); + F_DrawContinueCharacter((BASEVIDWIDTH<<(FRACBITS-1))+offsx, ((140-lift[1])<<FRACBITS)+offsy, 1); if (offsy >= 0) - drawchar((BASEVIDWIDTH<<(FRACBITS-1))-offsx, ((140-lift[0])<<FRACBITS)-offsy, 0); - -#undef drawchar + F_DrawContinueCharacter((BASEVIDWIDTH<<(FRACBITS-1))-offsx, ((140-lift[0])<<FRACBITS)-offsy, 0); if (timetonext > (11*TICRATE)) V_DrawFadeScreen(31, timetonext-(11*TICRATE)); @@ -3733,6 +3786,7 @@ void F_ContinueTicker(void) { if (!(--timetonext)) { + F_DestroyContinueDrawLists(); Command_ExitGame_f(); return; } @@ -3742,6 +3796,7 @@ void F_ContinueTicker(void) { if (++continuetime == 3*TICRATE) { + F_DestroyContinueDrawLists(); G_Continue(); return; } diff --git a/src/info.c b/src/info.c index 382ecba7fbb5205b146ab90a5e5c46096ee9034a..ab46cdbc728de4d8d71c39e97adf245235301acc 100644 --- a/src/info.c +++ b/src/info.c @@ -7493,12 +7493,12 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = sfx_steam1, // deathsound 0, // speed 32*FRACUNIT, // radius - 1*FRACUNIT, // height + 16*FRACUNIT, // height 0, // display offset 20*FRACUNIT, // mass 0, // damage sfx_None, // activesound - MF_SOLID, // flags + MF_SPECIAL, // flags S_NULL // raisestate }, diff --git a/src/lua_hook.h b/src/lua_hook.h index 026e5c4aaf97c54326a22d5de56f4e44cbddd09f..17e0e99a0787d43fa12f817018b29acaac723495 100644 --- a/src/lua_hook.h +++ b/src/lua_hook.h @@ -86,6 +86,8 @@ automatically. X (title),/* titlescreen */\ X (titlecard),\ X (intermission),\ + X (continue),\ + X (playersetup),\ /* I chose to access the hook enums through a macro as well. This could provide @@ -117,6 +119,13 @@ extern boolean hook_cmd_running; void LUA_HookVoid(int hook); void LUA_HookHUD(int hook, huddrawlist_h drawlist); +int LUA_HookCharacterHUD +( + int hook, huddrawlist_h drawlist, player_t *player, + fixed_t x, fixed_t y, fixed_t scale, + INT32 skinIndex, UINT8 sprite2, UINT8 frame, UINT8 rotation, skincolornum_t color, + INT32 ticker, boolean mode +); int LUA_HookMobj(mobj_t *, int hook); int LUA_Hook2Mobj(mobj_t *, mobj_t *, int hook); diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c index 529c189ff7883bbd377327ad5b44d2ae5af4e196..54381e4aedc47a64d89190f5582c3c5856634ba6 100644 --- a/src/lua_hooklib.c +++ b/src/lua_hooklib.c @@ -375,6 +375,17 @@ static boolean prepare_string_hook return false; } +static boolean prepare_hud_hook +( + Hook_State * hook, + int default_status, + int hook_type +){ + return init_hook_type(hook, default_status, + hook_type, 0, NULL, + hudHookIds[hook_type].numHooks); +} + static void init_hook_call ( Hook_State * hook, @@ -490,6 +501,21 @@ static int call_mobj_type_hooks(Hook_State *hook, mobjtype_t mobj_type) return call_mapped(hook, &mobjHookIds[mobj_type][hook->hook_type]); } +static void call_hud_hooks +( + Hook_State * hook, + int results, + Hook_Callback results_handler +){ + hud_running = true; // local hook + init_hook_call(hook, results, results_handler); + call_mapped(hook, &hudHookIds[hook->hook_type]); + hud_running = false; + + lua_pushnil(gL); + lua_setfield(gL, LUA_REGISTRYINDEX, "HUD_DRAW_LIST"); +} + static int call_hooks ( Hook_State * hook, @@ -648,23 +674,39 @@ int LUA_HookKey(event_t *event, int hook_type) void LUA_HookHUD(int hook_type, huddrawlist_h list) { - const hook_t * map = &hudHookIds[hook_type]; Hook_State hook; - if (map->numHooks > 0) + if (prepare_hud_hook(&hook, 0, hook_type)) { - start_hook_stack(); - begin_hook_values(&hook); - LUA_SetHudHook(hook_type, list); + call_hud_hooks(&hook, 0, res_none); + } +} - hud_running = true; // local hook - init_hook_call(&hook, 0, res_none); - call_mapped(&hook, map); - hud_running = false; - - lua_pushnil(gL); - lua_setfield(gL, LUA_REGISTRYINDEX, "HUD_DRAW_LIST"); +int LUA_HookCharacterHUD +( + int hook_type, huddrawlist_h list, player_t *player, + fixed_t x, fixed_t y, fixed_t scale, + INT32 skinIndex, UINT8 sprite2, UINT8 frame, UINT8 rotation, skincolornum_t color, + INT32 ticker, boolean mode +){ + Hook_State hook; + if (prepare_hud_hook(&hook, false, hook_type)) + { + LUA_SetHudHook(hook_type, list); + LUA_PushUserdata(gL, player, META_PLAYER); + lua_pushfixed(gL, x); + lua_pushfixed(gL, y); + lua_pushfixed(gL, scale); + lua_pushstring(gL, skins[skinIndex]->name); + lua_pushinteger(gL, sprite2); + lua_pushinteger(gL, frame); + lua_pushinteger(gL, rotation); + lua_pushinteger(gL, color); + lua_pushinteger(gL, ticker); + lua_pushboolean(gL, mode); + call_hud_hooks(&hook, 1, res_true); } + return hook.status; } /* ========================================================================= diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index b6ef2579c131d8de29a521615cb6a74acc94f5a0..744a56f7fe528031dd0777c06da35067fb59dde4 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -190,6 +190,7 @@ enum player_e player_marelap, player_marebonuslap, player_marebegunat, + player_lastmaretime, player_startedtime, player_finishedtime, player_lapbegunat, @@ -337,6 +338,7 @@ static const char *const player_opt[] = { "marelap", "marebonuslap", "marebegunat", + "lastmaretime", "startedtime", "finishedtime", "lapbegunat", @@ -725,6 +727,9 @@ static int player_get(lua_State *L) case player_marebegunat: lua_pushinteger(L, plr->marebegunat); break; + case player_lastmaretime: + lua_pushinteger(L, plr->lastmaretime); + break; case player_startedtime: lua_pushinteger(L, plr->startedtime); break; @@ -1219,6 +1224,9 @@ static int player_set(lua_State *L) case player_marebegunat: plr->marebegunat = (tic_t)luaL_checkinteger(L, 3); break; + case player_lastmaretime: + plr->lastmaretime = (tic_t)luaL_checkinteger(L, 3); + break; case player_startedtime: plr->startedtime = (tic_t)luaL_checkinteger(L, 3); break; diff --git a/src/m_menu.c b/src/m_menu.c index 6a85a2ef14f880365b6fab0621d076ea9e6396fe..50011347550c32622238ef02a6d538be62bbb31f 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -184,6 +184,9 @@ static fixed_t char_scroll = 0; static tic_t keydown = 0; +// Lua +static huddrawlist_h luahuddrawlist_playersetup; + // // PROTOTYPES // @@ -11926,6 +11929,19 @@ static UINT8 multi_frame; static UINT16 multi_spr2; static boolean multi_paused; static boolean multi_invcolor; +static boolean multi_override; + +static spritedef_t *multi_followitem_sprdef; +static INT32 multi_followitem_skinnum; +static UINT8 multi_followitem_numframes; +static UINT8 multi_followitem_startframe; +static UINT8 multi_followitem_frame; +static fixed_t multi_followitem_duration; +static fixed_t multi_followitem_tics; +static fixed_t multi_followitem_scale; +static fixed_t multi_followitem_yoffset; + +#define MULTI_DURATION (4*FRACUNIT) // this is set before entering the MultiPlayer setup menu, // for either player 1 or 2 @@ -12070,9 +12086,92 @@ static menucolor_t *M_GridIndexToMenuColor(UINT16 index) } } +static void M_SetPlayerSetupFollowItem(void) +{ + const mobjtype_t followitem = skins[setupm_fakeskin]->followitem; + + switch (followitem) + { + case MT_TAILSOVERLAY: + { + const state_t *state = &states[S_TAILSOVERLAY_MINUS30DEGREES]; + const UINT8 sprite2 = P_GetSkinSprite2(skins[setupm_fakeskin], state->frame & FF_FRAMEMASK, NULL); + + if (state->sprite != SPR_PLAY) + break; + + multi_followitem_sprdef = &skins[setupm_fakeskin]->sprites[sprite2]; + multi_followitem_skinnum = setupm_fakeskin; + multi_followitem_numframes = multi_followitem_sprdef->numframes; + multi_followitem_startframe = 0; + multi_followitem_frame = multi_frame; + multi_followitem_duration = MULTI_DURATION; + multi_followitem_tics = multi_tics; + multi_followitem_scale = FRACUNIT; + multi_followitem_yoffset = 0; + + if ((state->frame & FF_SPR2MIDSTART) && (multi_followitem_numframes > 0) && M_RandomChance(FRACUNIT / 2)) + { + multi_followitem_frame += multi_followitem_numframes / 2; + } + break; + } + case MT_METALJETFUME: + { + const state_t *state = &states[S_JETFUME1]; + + if (!(state->frame & FF_ANIMATE)) + break; + + multi_followitem_sprdef = &sprites[state->sprite]; + multi_followitem_skinnum = TC_DEFAULT; + multi_followitem_numframes = state->var1 + 1; + multi_followitem_startframe = state->frame & FF_FRAMEMASK; + multi_followitem_frame = multi_followitem_startframe; + multi_followitem_duration = state->var2 * FRACUNIT; + multi_followitem_tics = multi_tics % multi_followitem_duration; + multi_followitem_scale = 2 * FRACUNIT / 3; + multi_followitem_yoffset = (skins[setupm_fakeskin]->height - FixedMul(mobjinfo[followitem].height, multi_followitem_scale)) >> 1; + break; + } + default: + multi_followitem_sprdef = NULL; + break; + } +} + +static void M_DrawPlayerSetupFollowItem(INT32 x, INT32 y, fixed_t scale, INT32 flags) +{ + spriteframe_t *sprframe; + patch_t *patch; + UINT8 *colormap; + + if (multi_followitem_sprdef == NULL) + return; + + if (multi_followitem_frame >= multi_followitem_startframe + multi_followitem_numframes) + multi_followitem_frame = multi_followitem_startframe; + + colormap = R_GetTranslationColormap(multi_followitem_skinnum, setupm_fakecolor->color, GTC_CACHE); + + sprframe = &multi_followitem_sprdef->spriteframes[multi_followitem_frame]; + patch = W_CachePatchNum(sprframe->lumppat[0], PU_PATCH); + if (sprframe->flip & 1) // Only for first sprite + flags |= V_FLIP; // This sprite is left/right flipped! + + x <<= FRACBITS; + y <<= FRACBITS; + y -= FixedMul(multi_followitem_yoffset, scale); + + scale = FixedMul(scale, multi_followitem_scale); + + V_DrawFixedPatch(x, y, scale, flags, patch, colormap); +} + static void M_DrawSetupMultiPlayerMenu(void) { INT32 x, y, cursory = 0, flags = 0; + fixed_t scale; spritedef_t *sprdef; spriteframe_t *sprframe; patch_t *patch; @@ -12122,11 +12221,24 @@ static void M_DrawSetupMultiPlayerMenu(void) y += 11; // anim the player in the box - multi_tics -= renderdeltatics; - while (!multi_paused && multi_tics <= 0) + if (!multi_paused) { - multi_frame++; - multi_tics += 4*FRACUNIT; + multi_tics -= renderdeltatics; + while (multi_tics <= 0) + { + multi_frame++; + multi_tics += MULTI_DURATION; + } + + if (multi_followitem_sprdef != NULL) + { + multi_followitem_tics -= renderdeltatics; + while (multi_followitem_tics <= 0) + { + multi_followitem_frame++; + multi_followitem_tics += multi_followitem_duration; + } + } } #define charw 74 @@ -12141,17 +12253,38 @@ static void M_DrawSetupMultiPlayerMenu(void) goto faildraw; // ok, draw player sprite for sure now - colormap = R_GetTranslationColormap(setupm_fakeskin, setupm_fakecolor->color, GTC_CACHE); - if (multi_frame >= sprdef->numframes) multi_frame = 0; + scale = FixedDiv(skins[setupm_fakeskin]->highresscale, skins[setupm_fakeskin]->shieldscale); + +#define chary (y+64) + + if (renderisnewtic) + { + LUA_HUD_ClearDrawList(luahuddrawlist_playersetup); + multi_override = LUA_HookCharacterHUD + ( + HUD_HOOK(playersetup), luahuddrawlist_playersetup, setupm_player, + x << FRACBITS, chary << FRACBITS, scale, + setupm_fakeskin, multi_spr2, multi_frame, 1, setupm_fakecolor->color, + (multi_tics >> FRACBITS) + 1, multi_paused + ); + } + + LUA_HUD_DrawList(luahuddrawlist_playersetup); + + if (multi_override == true) + goto colordraw; + + colormap = R_GetTranslationColormap(setupm_fakeskin, setupm_fakecolor->color, GTC_CACHE); + sprframe = &sprdef->spriteframes[multi_frame]; patch = W_CachePatchNum(sprframe->lumppat[0], PU_PATCH); if (sprframe->flip & 1) // Only for first sprite flags |= V_FLIP; // This sprite is left/right flipped! -#define chary (y+64) + M_DrawPlayerSetupFollowItem(x, chary, scale, flags & ~V_FLIP); V_DrawFixedPatch( x<<FRACBITS, @@ -12344,6 +12477,20 @@ static void M_DrawColorRamp(INT32 x, INT32 y, INT32 w, INT32 h, skincolor_t colo V_DrawFill(x, y+(i*h), w, h, color.ramp[i]); } +static void M_InitPlayerSetupLua(void) +{ + // I'd really like to assume that the drawlist has been destroyed, + // but it appears M_ClearMenus has options not to call exit routines... + // so that doesn't seem safe to me?? + if (!LUA_HUD_IsDrawListValid(luahuddrawlist_playersetup)) + { + LUA_HUD_DestroyDrawList(luahuddrawlist_playersetup); + luahuddrawlist_playersetup = LUA_HUD_CreateDrawList(); + } + LUA_HUD_ClearDrawList(luahuddrawlist_playersetup); + multi_override = false; +} + // Handle 1P/2P MP Setup static void M_HandleSetupMultiPlayer(INT32 choice) { @@ -12389,6 +12536,7 @@ static void M_HandleSetupMultiPlayer(INT32 choice) } while ((prev_setupm_fakeskin != setupm_fakeskin) && !(R_SkinUsable(-1, setupm_fakeskin))); multi_spr2 = P_GetSkinSprite2(skins[setupm_fakeskin], SPR2_WALK, NULL); + M_SetPlayerSetupFollowItem(); } else if (itemOn == 2) // player color { @@ -12429,6 +12577,7 @@ static void M_HandleSetupMultiPlayer(INT32 choice) } while ((prev_setupm_fakeskin != setupm_fakeskin) && !(R_SkinUsable(-1, setupm_fakeskin))); multi_spr2 = P_GetSkinSprite2(skins[setupm_fakeskin], SPR2_WALK, NULL); + M_SetPlayerSetupFollowItem(); } else if (itemOn == 2) // player color { @@ -12549,7 +12698,7 @@ static void M_SetupMultiPlayer(INT32 choice) (void)choice; multi_frame = 0; - multi_tics = 4*FRACUNIT; + multi_tics = MULTI_DURATION; strcpy(setupm_name, cv_playername.string); @@ -12579,6 +12728,10 @@ static void M_SetupMultiPlayer(INT32 choice) MP_PlayerSetupMenu[2].status = (IT_KEYHANDLER|IT_STRING); multi_spr2 = P_GetSkinSprite2(skins[setupm_fakeskin], SPR2_WALK, NULL); + M_SetPlayerSetupFollowItem(); + + // allocate and/or clear Lua player setup draw list + M_InitPlayerSetupLua(); MP_PlayerSetupDef.prevMenu = currentMenu; M_SetupNextMenu(&MP_PlayerSetupDef); @@ -12590,7 +12743,7 @@ static void M_SetupMultiPlayer2(INT32 choice) (void)choice; multi_frame = 0; - multi_tics = 4*FRACUNIT; + multi_tics = MULTI_DURATION; strcpy (setupm_name, cv_playername2.string); @@ -12620,6 +12773,10 @@ static void M_SetupMultiPlayer2(INT32 choice) MP_PlayerSetupMenu[2].status = (IT_KEYHANDLER|IT_STRING); multi_spr2 = P_GetSkinSprite2(skins[setupm_fakeskin], SPR2_WALK, NULL); + M_SetPlayerSetupFollowItem(); + + // allocate and/or clear Lua player setup draw list + M_InitPlayerSetupLua(); MP_PlayerSetupDef.prevMenu = currentMenu; M_SetupNextMenu(&MP_PlayerSetupDef); @@ -12641,6 +12798,12 @@ static boolean M_QuitMultiPlayerMenu(void) // send color if changed if (setupm_fakecolor->color != setupm_cvcolor->value) COM_BufAddText (va("%s %d\n",setupm_cvcolor->name,setupm_fakecolor->color)); + + // de-allocate Lua player setup drawlist + LUA_HUD_DestroyDrawList(luahuddrawlist_playersetup); + luahuddrawlist_playersetup = NULL; + multi_override = false; + return true; } diff --git a/src/p_enemy.c b/src/p_enemy.c index 428c023ff3e6333b7f62cda4e3f528762b1d606e..4990db6fd50a2f96102056984b4d02e3c0205ddb 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -4189,8 +4189,10 @@ static void P_DoBoss5Death(mobj_t *mo) if (!P_MobjWasRemoved(pole)) { P_SetScale(pole, 2*FRACUNIT, true); + pole->angle = mo->tracer->angle; pole->momx = P_ReturnThrustX(pole, pole->angle, speed); pole->momy = P_ReturnThrustY(pole, pole->angle, speed); + P_SetTarget(&pole->tracer, P_SpawnMobj( pole->x, pole->y, pole->z - 256*FRACUNIT, @@ -4199,7 +4201,7 @@ static void P_DoBoss5Death(mobj_t *mo) { pole->tracer->flags |= MF_NOCLIPTHING; P_SetScale(pole->tracer, 2*FRACUNIT, true); - pole->angle = pole->tracer->angle = mo->tracer->angle; + pole->tracer->angle = mo->tracer->angle; pole->tracer->momx = pole->momx; pole->tracer->momy = pole->momy; @@ -5192,7 +5194,7 @@ void A_SetSolidSteam(mobj_t *actor) return; actor->flags &= ~MF_NOCLIP; - actor->flags |= MF_SOLID; + actor->flags |= MF_SPECIAL; if (!(actor->flags2 & MF2_AMBUSH)) { if (P_RandomChance(FRACUNIT/8)) @@ -5222,7 +5224,7 @@ void A_UnsetSolidSteam(mobj_t *actor) if (LUA_CallAction(A_UNSETSOLIDSTEAM, actor)) return; - actor->flags &= ~MF_SOLID; + actor->flags &= ~MF_SPECIAL; actor->flags |= MF_NOCLIP; } diff --git a/src/p_inter.c b/src/p_inter.c index 5534f7865c23afc2d70ceae209e6ea432f6223f8..406f4af2227d294adb2883433009b4699b9881bf 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -392,17 +392,50 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) } } + // Ignore multihits in "ouchie" mode + if (special->flags & (MF_ENEMY | MF_BOSS) && special->flags2 & MF2_FRET) + return; + player = toucher->player; - I_Assert(player != NULL); // Only players can touch stuff! - if (player->spectator) - return; + if (player) + { + if (player->spectator) + return; - // Ignore multihits in "ouchie" mode - if (special->flags & (MF_ENEMY|MF_BOSS) && special->flags2 & MF2_FRET) - return; + // Some hooks may assume that the toucher is a player, so we keep it in here. + if (LUA_HookTouchSpecial(special, toucher) || P_MobjWasRemoved(special)) + return; + } - if (LUA_HookTouchSpecial(special, toucher) || P_MobjWasRemoved(special)) + if (player || (toucher->flags & MF_PUSHABLE)) // Special area for objects that are interactable by both player AND MF_PUSHABLE. + { + if (special->type == MT_STEAM) + { + if (player && player->mo->state == &states[player->mo->info->painstate]) // can't use gas jets when player is in pain! + return; + + fixed_t speed = special->info->mass; // gas jets use this for the vertical thrust + SINT8 flipval = P_MobjFlip(special); // virtually everything here centers around the thruster's gravity, not the object's! + + if (special->state != &states[S_STEAM1]) // Only when it bursts + return; + + toucher->eflags |= MFE_SPRUNG; + toucher->momz = flipval * FixedMul(speed, FixedSqrt(FixedMul(special->scale, toucher->scale))); // scale the speed with both objects' scales, just like with springs! + + if (player) + { + P_ResetPlayer(player); + if (player->panim != PA_FALL) + P_SetMobjState(toucher, S_PLAY_FALL); + } + + return; // Don't collect it! + } + } + + if (!player) // Only players can touch stuff! return; // 0 = none, 1 = elemental pierce, 2 = bubble bounce @@ -1881,6 +1914,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) toucher->tracer->flags2 = (toucher->tracer->flags2 & ~MF2_AMBUSH) | destambush; } return; + default: // SOC or script pickup if (player->bot && player->bot != BOT_MPAI) return; diff --git a/src/p_local.h b/src/p_local.h index 0bcd6da1dd7f42b5e56650d8a2cc70fd775f1e22..249c3cd4b6de5248140c00e2dfc2332081a5d00e 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -307,21 +307,19 @@ void P_PushableThinker(mobj_t *mobj); void P_SceneryThinker(mobj_t *mobj); -fixed_t P_MobjFloorZ(mobj_t *mobj, sector_t *sector, sector_t *boundsec, fixed_t x, fixed_t y, line_t *line, boolean lowest, boolean perfect); -fixed_t P_MobjCeilingZ(mobj_t *mobj, sector_t *sector, sector_t *boundsec, fixed_t x, fixed_t y, line_t *line, boolean lowest, boolean perfect); -#define P_GetFloorZ(mobj, sector, x, y, line) P_MobjFloorZ(mobj, sector, NULL, x, y, line, false, false) -#define P_GetCeilingZ(mobj, sector, x, y, line) P_MobjCeilingZ(mobj, sector, NULL, x, y, line, true, false) -#define P_GetFOFTopZ(mobj, sector, fof, x, y, line) P_MobjCeilingZ(mobj, sectors + fof->secnum, sector, x, y, line, false, false) -#define P_GetFOFBottomZ(mobj, sector, fof, x, y, line) P_MobjFloorZ(mobj, sectors + fof->secnum, sector, x, y, line, true, false) -#define P_GetSpecialBottomZ(mobj, src, bound) P_MobjFloorZ(mobj, src, bound, mobj->x, mobj->y, NULL, src != bound, true) -#define P_GetSpecialTopZ(mobj, src, bound) P_MobjCeilingZ(mobj, src, bound, mobj->x, mobj->y, NULL, src == bound, true) - -fixed_t P_CameraFloorZ(camera_t *mobj, sector_t *sector, sector_t *boundsec, fixed_t x, fixed_t y, line_t *line, boolean lowest, boolean perfect); -fixed_t P_CameraCeilingZ(camera_t *mobj, sector_t *sector, sector_t *boundsec, fixed_t x, fixed_t y, line_t *line, boolean lowest, boolean perfect); -#define P_CameraGetFloorZ(mobj, sector, x, y, line) P_CameraFloorZ(mobj, sector, NULL, x, y, line, false, false) -#define P_CameraGetCeilingZ(mobj, sector, x, y, line) P_CameraCeilingZ(mobj, sector, NULL, x, y, line, true, false) -#define P_CameraGetFOFTopZ(mobj, sector, fof, x, y, line) P_CameraCeilingZ(mobj, sectors + fof->secnum, sector, x, y, line, false, false) -#define P_CameraGetFOFBottomZ(mobj, sector, fof, x, y, line) P_CameraFloorZ(mobj, sectors + fof->secnum, sector, x, y, line, true, false) +fixed_t P_MobjFloorZ(sector_t *sector, sector_t *boundsec, fixed_t x, fixed_t y, fixed_t radius, line_t *line, boolean lowest, boolean perfect); +fixed_t P_MobjCeilingZ(sector_t *sector, sector_t *boundsec, fixed_t x, fixed_t y, fixed_t radius, line_t *line, boolean lowest, boolean perfect); +#define P_GetFloorZ(mobj, sector, x, y, line) P_MobjFloorZ(sector, NULL, x, y, mobj->radius, line, false, false) +#define P_GetCeilingZ(mobj, sector, x, y, line) P_MobjCeilingZ(sector, NULL, x, y, mobj->radius, line, true, false) +#define P_GetFOFTopZ(mobj, sector, fof, x, y, line) P_MobjCeilingZ(sectors + fof->secnum, sector, x, y, mobj->radius, line, false, false) +#define P_GetFOFBottomZ(mobj, sector, fof, x, y, line) P_MobjFloorZ(sectors + fof->secnum, sector, x, y, mobj->radius, line, true, false) +#define P_GetSpecialBottomZ(mobj, src, bound) P_MobjFloorZ(src, bound, mobj->x, mobj->y, mobj->radius, NULL, src != bound, true) +#define P_GetSpecialTopZ(mobj, src, bound) P_MobjCeilingZ(src, bound, mobj->x, mobj->y, mobj->radius, NULL, src == bound, true) + +#define P_CameraGetFloorZ(mobj, sector, x, y, line) P_MobjFloorZ(sector, NULL, x, y, mobj->radius, line, false, false) +#define P_CameraGetCeilingZ(mobj, sector, x, y, line) P_MobjCeilingZ(sector, NULL, x, y, mobj->radius, line, true, false) +#define P_CameraGetFOFTopZ(mobj, sector, fof, x, y, line) P_MobjCeilingZ(sectors + fof->secnum, sector, x, y, mobj->radius, line, false, false) +#define P_CameraGetFOFBottomZ(mobj, sector, fof, x, y, line) P_MobjFloorZ(sectors + fof->secnum, sector, x, y, mobj->radius, line, true, false) boolean P_InsideANonSolidFFloor(mobj_t *mobj, ffloor_t *rover); boolean P_CheckDeathPitCollide(mobj_t *mo); diff --git a/src/p_map.c b/src/p_map.c index 7b64fe3bb782de111f0b70053306ce851a1ea190..f97ddfa3cd8aa3fa9f7e9dbb3e6d76b041aca164 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -502,72 +502,56 @@ springstate: return final; } -static void P_DoFanAndGasJet(mobj_t *spring, mobj_t *object) +static void P_DoFan(mobj_t *fan, mobj_t *object) { player_t *p = object->player; // will be NULL if not a player fixed_t zdist; // distance between bottoms - fixed_t speed = spring->info->mass; // conveniently, both fans and gas jets use this for the vertical thrust - SINT8 flipval = P_MobjFlip(spring); // virtually everything here centers around the thruster's gravity, not the object's! + fixed_t speed = fan->info->mass; // fans use this for the vertical thrust + SINT8 flipval = P_MobjFlip(fan); // virtually everything here centers around the thruster's gravity, not the object's! - if (p && object->state == &states[object->info->painstate]) // can't use fans and gas jets when player is in pain! + if (p && object->state == &states[object->info->painstate]) // can't use fans when player is in pain! return; // is object's top below thruster's position? if not, calculate distance between their bottoms - if (spring->eflags & MFE_VERTICALFLIP) + if (fan->eflags & MFE_VERTICALFLIP) { - if (object->z > spring->z + spring->height) + if (object->z > fan->z + fan->height) return; - zdist = (spring->z + spring->height) - (object->z + object->height); + zdist = (fan->z + fan->height) - (object->z + object->height); } else { - if (object->z + object->height < spring->z) + if (object->z + object->height < fan->z) return; - zdist = object->z - spring->z; + zdist = object->z - fan->z; } object->standingslope = NULL; // No launching off at silly angles for you. - switch (spring->type) + switch (fan->type) { case MT_FAN: // fan - if (zdist > (spring->health << FRACBITS)) // max z distance determined by health (set by map thing args[0]) + if (zdist > (fan->health << FRACBITS)) // max z distance determined by health (set by map thing args[0]) break; - if (flipval*object->momz >= FixedMul(speed, spring->scale)) // if object's already moving faster than your best, don't bother + if (flipval*object->momz >= FixedMul(speed, fan->scale)) // if object's already moving faster than your best, don't bother break; if (p && (p->climbing || p->pflags & PF_GLIDING)) // doesn't affect Knux when he's using his abilities! break; - object->momz += flipval*FixedMul(speed/4, spring->scale); + object->momz += flipval*FixedMul(speed/4, fan->scale); // limit the speed if too high - if (flipval*object->momz > FixedMul(speed, spring->scale)) - object->momz = flipval*FixedMul(speed, spring->scale); + if (flipval*object->momz > FixedMul(speed, fan->scale)) + object->momz = flipval*FixedMul(speed, fan->scale); if (p && !p->powers[pw_tailsfly] && !p->powers[pw_carry]) // doesn't reset anim for Tails' flight { P_ResetPlayer(p); P_SetMobjState(object, S_PLAY_FALL); - P_SetTarget(&object->tracer, spring); + P_SetTarget(&object->tracer, fan); p->powers[pw_carry] = CR_FAN; } break; - case MT_STEAM: // Steam - if (zdist > FixedMul(16*FRACUNIT, spring->scale)) - break; - if (spring->state != &states[S_STEAM1]) // Only when it bursts - break; - - object->eflags |= MFE_SPRUNG; - object->momz = flipval*FixedMul(speed, FixedSqrt(FixedMul(spring->scale, object->scale))); // scale the speed with both objects' scales, just like with springs! - - if (p) - { - P_ResetPlayer(p); - if (p->panim != PA_FALL) - P_SetMobjState(object, S_PLAY_FALL); - } - break; default: break; } @@ -1484,13 +1468,13 @@ static unsigned PIT_DoCheckThing(mobj_t *thing) } // check for special pickup - if (thing->flags & MF_SPECIAL && tmthing->player) + if (thing->flags & MF_SPECIAL) { P_TouchSpecialThing(thing, tmthing, true); // can remove thing return CHECKTHING_COLLIDE; } // check again for special pickup - if (tmthing->flags & MF_SPECIAL && thing->player) + if (tmthing->flags & MF_SPECIAL) { P_TouchSpecialThing(tmthing, thing, true); // can remove thing return CHECKTHING_COLLIDE; @@ -1578,15 +1562,15 @@ static unsigned PIT_DoCheckThing(mobj_t *thing) if (thing->flags & MF_PUSHABLE) { - if (tmthing->type == MT_FAN || tmthing->type == MT_STEAM) - P_DoFanAndGasJet(tmthing, thing); + if (tmthing->type == MT_FAN) + P_DoFan(tmthing, thing); } if (tmthing->flags & MF_PUSHABLE) { - if (thing->type == MT_FAN || thing->type == MT_STEAM) + if (thing->type == MT_FAN) { - P_DoFanAndGasJet(thing, tmthing); + P_DoFan(thing, tmthing); return CHECKTHING_COLLIDE; } else if (thing->flags & MF_SPRING) @@ -1679,8 +1663,8 @@ static unsigned PIT_DoCheckThing(mobj_t *thing) } } - if (tmthing->type == MT_FAN || tmthing->type == MT_STEAM) - P_DoFanAndGasJet(tmthing, thing); + if (tmthing->type == MT_FAN) + P_DoFan(tmthing, thing); } if (tmthing->player) // Is the moving/interacting object the player? @@ -1688,8 +1672,8 @@ static unsigned PIT_DoCheckThing(mobj_t *thing) if (!tmthing->health) return CHECKTHING_IGNORE; - if (thing->type == MT_FAN || thing->type == MT_STEAM) - P_DoFanAndGasJet(thing, tmthing); + if (thing->type == MT_FAN) + P_DoFan(thing, tmthing); else if (thing->flags & MF_SPRING && tmthing->player->powers[pw_carry] != CR_MINECART) { if ( thing->z <= tmthing->z + tmthing->height @@ -1755,8 +1739,8 @@ static unsigned PIT_DoCheckThing(mobj_t *thing) // not solid not blocked unsigned collide = CHECKTHING_NOCOLLIDE; - if ((tmthing->flags & MF_SPRING || tmthing->type == MT_STEAM || tmthing->type == MT_SPIKE || tmthing->type == MT_WALLSPIKE) && (thing->player)) - ; // springs, gas jets and springs should never be able to step up onto a player + if ((tmthing->flags & MF_SPRING || tmthing->type == MT_SPIKE || tmthing->type == MT_WALLSPIKE) && (thing->player)) + ; // springs and spikes should never be able to step up onto a player // z checking at last // Treat noclip things as non-solid! else if ((thing->flags & (MF_SOLID|MF_NOCLIP)) == MF_SOLID diff --git a/src/p_mobj.c b/src/p_mobj.c index 84ea3d065330b58907c1cc982122223bd67718da..9cdd2628db8cfec3ee8af83a6f96b3713879b164 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -1108,9 +1108,8 @@ static fixed_t HighestOnLine(fixed_t radius, fixed_t x, fixed_t y, line_t *line, ); } -fixed_t P_MobjFloorZ(mobj_t *mobj, sector_t *sector, sector_t *boundsec, fixed_t x, fixed_t y, line_t *line, boolean lowest, boolean perfect) +fixed_t P_MobjFloorZ(sector_t *sector, sector_t *boundsec, fixed_t x, fixed_t y, fixed_t radius, line_t *line, boolean lowest, boolean perfect) { - I_Assert(mobj != NULL); I_Assert(sector != NULL); if (sector->f_slope) { @@ -1119,14 +1118,14 @@ fixed_t P_MobjFloorZ(mobj_t *mobj, sector_t *sector, sector_t *boundsec, fixed_t // Get the corner of the object that should be the highest on the slope if (slope->d.x < 0) - testx = mobj->radius; + testx = radius; else - testx = -mobj->radius; + testx = -radius; if (slope->d.y < 0) - testy = mobj->radius; + testy = radius; else - testy = -mobj->radius; + testy = -radius; if ((slope->zdelta > 0) ^ !!(lowest)) { testx = -testx; @@ -1141,7 +1140,7 @@ fixed_t P_MobjFloorZ(mobj_t *mobj, sector_t *sector, sector_t *boundsec, fixed_t return P_GetSlopeZAt(slope, testx, testy); // If boundsec is set, we're looking for specials. In that case, iterate over every line in this sector to find the TRUE highest/lowest point - if (perfect) { + if (perfect && boundsec) { size_t i; line_t *ld; fixed_t bbox[4]; @@ -1152,10 +1151,10 @@ fixed_t P_MobjFloorZ(mobj_t *mobj, sector_t *sector, sector_t *boundsec, fixed_t else finalheight = INT32_MIN; - bbox[BOXLEFT] = x-mobj->radius; - bbox[BOXRIGHT] = x+mobj->radius; - bbox[BOXTOP] = y+mobj->radius; - bbox[BOXBOTTOM] = y-mobj->radius; + bbox[BOXLEFT] = x-radius; + bbox[BOXRIGHT] = x+radius; + bbox[BOXTOP] = y+radius; + bbox[BOXBOTTOM] = y-radius; for (i = 0; i < boundsec->linecount; i++) { ld = boundsec->lines[i]; @@ -1167,9 +1166,9 @@ fixed_t P_MobjFloorZ(mobj_t *mobj, sector_t *sector, sector_t *boundsec, fixed_t continue; if (lowest) - finalheight = min(finalheight, HighestOnLine(mobj->radius, x, y, ld, slope, true)); + finalheight = min(finalheight, HighestOnLine(radius, x, y, ld, slope, true)); else - finalheight = max(finalheight, HighestOnLine(mobj->radius, x, y, ld, slope, false)); + finalheight = max(finalheight, HighestOnLine(radius, x, y, ld, slope, false)); } return finalheight; @@ -1180,14 +1179,13 @@ fixed_t P_MobjFloorZ(mobj_t *mobj, sector_t *sector, sector_t *boundsec, fixed_t if (line == NULL) return P_GetSlopeZAt(slope, x, y); - return HighestOnLine(mobj->radius, x, y, line, slope, lowest); + return HighestOnLine(radius, x, y, line, slope, lowest); } else // Well, that makes it easy. Just get the floor height return sector->floorheight; } -fixed_t P_MobjCeilingZ(mobj_t *mobj, sector_t *sector, sector_t *boundsec, fixed_t x, fixed_t y, line_t *line, boolean lowest, boolean perfect) +fixed_t P_MobjCeilingZ(sector_t *sector, sector_t *boundsec, fixed_t x, fixed_t y, fixed_t radius, line_t *line, boolean lowest, boolean perfect) { - I_Assert(mobj != NULL); I_Assert(sector != NULL); if (sector->c_slope) { @@ -1196,14 +1194,14 @@ fixed_t P_MobjCeilingZ(mobj_t *mobj, sector_t *sector, sector_t *boundsec, fixed // Get the corner of the object that should be the highest on the slope if (slope->d.x < 0) - testx = mobj->radius; + testx = radius; else - testx = -mobj->radius; + testx = -radius; if (slope->d.y < 0) - testy = mobj->radius; + testy = radius; else - testy = -mobj->radius; + testy = -radius; if ((slope->zdelta > 0) ^ !!(lowest)) { testx = -testx; @@ -1218,7 +1216,7 @@ fixed_t P_MobjCeilingZ(mobj_t *mobj, sector_t *sector, sector_t *boundsec, fixed return P_GetSlopeZAt(slope, testx, testy); // If boundsec is set, we're looking for specials. In that case, iterate over every line in this sector to find the TRUE highest/lowest point - if (perfect) { + if (perfect && boundsec) { size_t i; line_t *ld; fixed_t bbox[4]; @@ -1229,10 +1227,10 @@ fixed_t P_MobjCeilingZ(mobj_t *mobj, sector_t *sector, sector_t *boundsec, fixed else finalheight = INT32_MIN; - bbox[BOXLEFT] = x-mobj->radius; - bbox[BOXRIGHT] = x+mobj->radius; - bbox[BOXTOP] = y+mobj->radius; - bbox[BOXBOTTOM] = y-mobj->radius; + bbox[BOXLEFT] = x-radius; + bbox[BOXRIGHT] = x+radius; + bbox[BOXTOP] = y+radius; + bbox[BOXBOTTOM] = y-radius; for (i = 0; i < boundsec->linecount; i++) { ld = boundsec->lines[i]; @@ -1244,9 +1242,9 @@ fixed_t P_MobjCeilingZ(mobj_t *mobj, sector_t *sector, sector_t *boundsec, fixed continue; if (lowest) - finalheight = min(finalheight, HighestOnLine(mobj->radius, x, y, ld, slope, true)); + finalheight = min(finalheight, HighestOnLine(radius, x, y, ld, slope, true)); else - finalheight = max(finalheight, HighestOnLine(mobj->radius, x, y, ld, slope, false)); + finalheight = max(finalheight, HighestOnLine(radius, x, y, ld, slope, false)); } return finalheight; @@ -1257,165 +1255,11 @@ fixed_t P_MobjCeilingZ(mobj_t *mobj, sector_t *sector, sector_t *boundsec, fixed if (line == NULL) return P_GetSlopeZAt(slope, x, y); - return HighestOnLine(mobj->radius, x, y, line, slope, lowest); + return HighestOnLine(radius, x, y, line, slope, lowest); } else // Well, that makes it easy. Just get the ceiling height return sector->ceilingheight; } -// Now do the same as all above, but for cameras because apparently cameras are special? -fixed_t P_CameraFloorZ(camera_t *mobj, sector_t *sector, sector_t *boundsec, fixed_t x, fixed_t y, line_t *line, boolean lowest, boolean perfect) -{ - I_Assert(mobj != NULL); - I_Assert(sector != NULL); - - if (sector->f_slope) { - fixed_t testx, testy; - pslope_t *slope = sector->f_slope; - - // Get the corner of the object that should be the highest on the slope - if (slope->d.x < 0) - testx = mobj->radius; - else - testx = -mobj->radius; - - if (slope->d.y < 0) - testy = mobj->radius; - else - testy = -mobj->radius; - - if ((slope->zdelta > 0) ^ !!(lowest)) { - testx = -testx; - testy = -testy; - } - - testx += x; - testy += y; - - // If the highest point is in the sector, then we have it easy! Just get the Z at that point - if (R_IsPointInSector(boundsec ? boundsec : sector, testx, testy)) - return P_GetSlopeZAt(slope, testx, testy); - - // If boundsec is set, we're looking for specials. In that case, iterate over every line in this sector to find the TRUE highest/lowest point - if (perfect) { - size_t i; - line_t *ld; - fixed_t bbox[4]; - fixed_t finalheight; - - if (lowest) - finalheight = INT32_MAX; - else - finalheight = INT32_MIN; - - bbox[BOXLEFT] = x-mobj->radius; - bbox[BOXRIGHT] = x+mobj->radius; - bbox[BOXTOP] = y+mobj->radius; - bbox[BOXBOTTOM] = y-mobj->radius; - for (i = 0; i < boundsec->linecount; i++) { - ld = boundsec->lines[i]; - - if (bbox[BOXRIGHT] <= ld->bbox[BOXLEFT] || bbox[BOXLEFT] >= ld->bbox[BOXRIGHT] - || bbox[BOXTOP] <= ld->bbox[BOXBOTTOM] || bbox[BOXBOTTOM] >= ld->bbox[BOXTOP]) - continue; - - if (P_BoxOnLineSide(bbox, ld) != -1) - continue; - - if (lowest) - finalheight = min(finalheight, HighestOnLine(mobj->radius, x, y, ld, slope, true)); - else - finalheight = max(finalheight, HighestOnLine(mobj->radius, x, y, ld, slope, false)); - } - - return finalheight; - } - - // If we're just testing for base sector location (no collision line), just go for the center's spot... - // It'll get fixed when we test for collision anyway, and the final result can't be lower than this - if (line == NULL) - return P_GetSlopeZAt(slope, x, y); - - return HighestOnLine(mobj->radius, x, y, line, slope, lowest); - } else // Well, that makes it easy. Just get the floor height - return sector->floorheight; -} - -fixed_t P_CameraCeilingZ(camera_t *mobj, sector_t *sector, sector_t *boundsec, fixed_t x, fixed_t y, line_t *line, boolean lowest, boolean perfect) -{ - I_Assert(mobj != NULL); - I_Assert(sector != NULL); - - if (sector->c_slope) { - fixed_t testx, testy; - pslope_t *slope = sector->c_slope; - - // Get the corner of the object that should be the highest on the slope - if (slope->d.x < 0) - testx = mobj->radius; - else - testx = -mobj->radius; - - if (slope->d.y < 0) - testy = mobj->radius; - else - testy = -mobj->radius; - - if ((slope->zdelta > 0) ^ !!(lowest)) { - testx = -testx; - testy = -testy; - } - - testx += x; - testy += y; - - // If the highest point is in the sector, then we have it easy! Just get the Z at that point - if (R_IsPointInSector(boundsec ? boundsec : sector, testx, testy)) - return P_GetSlopeZAt(slope, testx, testy); - - // If boundsec is set, we're looking for specials. In that case, iterate over every line in this sector to find the TRUE highest/lowest point - if (perfect) { - size_t i; - line_t *ld; - fixed_t bbox[4]; - fixed_t finalheight; - - if (lowest) - finalheight = INT32_MAX; - else - finalheight = INT32_MIN; - - bbox[BOXLEFT] = x-mobj->radius; - bbox[BOXRIGHT] = x+mobj->radius; - bbox[BOXTOP] = y+mobj->radius; - bbox[BOXBOTTOM] = y-mobj->radius; - for (i = 0; i < boundsec->linecount; i++) { - ld = boundsec->lines[i]; - - if (bbox[BOXRIGHT] <= ld->bbox[BOXLEFT] || bbox[BOXLEFT] >= ld->bbox[BOXRIGHT] - || bbox[BOXTOP] <= ld->bbox[BOXBOTTOM] || bbox[BOXBOTTOM] >= ld->bbox[BOXTOP]) - continue; - - if (P_BoxOnLineSide(bbox, ld) != -1) - continue; - - if (lowest) - finalheight = min(finalheight, HighestOnLine(mobj->radius, x, y, ld, slope, true)); - else - finalheight = max(finalheight, HighestOnLine(mobj->radius, x, y, ld, slope, false)); - } - - return finalheight; - } - - // If we're just testing for base sector location (no collision line), just go for the center's spot... - // It'll get fixed when we test for collision anyway, and the final result can't be lower than this - if (line == NULL) - return P_GetSlopeZAt(slope, x, y); - - return HighestOnLine(mobj->radius, x, y, line, slope, lowest); - } else // Well, that makes it easy. Just get the ceiling height - return sector->ceilingheight; -} static void P_PlayerFlip(mobj_t *mo) { if (!mo->player) @@ -2347,8 +2191,8 @@ boolean P_CheckDeathPitCollide(mobj_t *mo) if (mo->player && mo->player->pflags & PF_GODMODE) return false; - fixed_t sectorFloor = P_GetSectorFloorZAt(mo->subsector->sector, mo->x, mo->y); - fixed_t sectorCeiling = P_GetSectorCeilingZAt(mo->subsector->sector, mo->x, mo->y); + fixed_t sectorFloor = P_GetSpecialBottomZ(mo, mo->subsector->sector, mo->subsector->sector); + fixed_t sectorCeiling = P_GetSpecialTopZ(mo, mo->subsector->sector, mo->subsector->sector); if (((mo->z <= sectorFloor && ((mo->subsector->sector->flags & MSF_TRIGGERSPECIAL_HEADBUMP) || !(mo->eflags & MFE_VERTICALFLIP)) && (mo->subsector->sector->flags & MSF_FLIPSPECIAL_FLOOR)) diff --git a/src/p_saveg.c b/src/p_saveg.c index 3187d461119bebf0f911dee33c3e7fc4af0e11ea..5e4d6d0760441e6bc94c6815824b8b7e1ab38c80 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -261,6 +261,7 @@ static void P_NetArchivePlayers(void) WRITEUINT8(save_p, players[i].marelap); WRITEUINT8(save_p, players[i].marebonuslap); WRITEUINT32(save_p, players[i].marebegunat); + WRITEUINT32(save_p, players[i].lastmaretime); WRITEUINT32(save_p, players[i].startedtime); WRITEUINT32(save_p, players[i].finishedtime); WRITEUINT32(save_p, players[i].lapbegunat); @@ -492,6 +493,7 @@ static void P_NetUnArchivePlayers(void) players[i].marelap = READUINT8(save_p); players[i].marebonuslap = READUINT8(save_p); players[i].marebegunat = READUINT32(save_p); + players[i].lastmaretime = READUINT32(save_p); players[i].startedtime = READUINT32(save_p); players[i].finishedtime = READUINT32(save_p); players[i].lapbegunat = READUINT32(save_p); diff --git a/src/p_setup.c b/src/p_setup.c index 93c4937c4d40118230bb128af9d4f345143816a0..beab864d3bcb45ebff12b92728f20971221217f5 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -358,6 +358,8 @@ static void P_ClearSingleMapHeaderInfo(INT16 i) mapheaderinfo[num]->marathonnext = 0; mapheaderinfo[num]->startrings = 0; mapheaderinfo[num]->sstimer = 90; + for (UINT8 n = 0; n < 8; n++) + mapheaderinfo[num]->nightstimer[n] = 0; mapheaderinfo[num]->ssspheres = 1; mapheaderinfo[num]->gravity = FRACUNIT/2; mapheaderinfo[num]->keywords[0] = '\0'; @@ -525,6 +527,29 @@ UINT32 P_GetScoreForGradeOverall(INT16 map, UINT8 grade) return score; } +void P_AddNiGHTSTimes(INT16 i, char *gtext) +{ + char *spos = gtext; + + for (UINT8 n = 0; n < 8; n++) + { + if (spos != NULL) + { + mapheaderinfo[i]->nightstimer[n] = atoi(spos); + CONS_Debug(DBG_SETUP, "%u ", atoi(spos)); + // Grab next comma + spos = strchr(spos, ','); + if (spos) + ++spos; + } + else + { + mapheaderinfo[i]->nightstimer[n] = 0; + } + } + +} + // // levelflats // @@ -2700,13 +2725,18 @@ static void P_WriteTextmap(void) fprintf(f, "rotationfloor = %f;\n", FIXED_TO_FLOAT(AngleFixed(wsectors[i].floorangle))); if (wsectors[i].ceilingangle != 0) fprintf(f, "rotationceiling = %f;\n", FIXED_TO_FLOAT(AngleFixed(wsectors[i].ceilingangle))); - if (wsectors[i].extra_colormap) + if (wsectors[i].extra_colormap) { INT32 lightcolor = P_RGBAToColor(wsectors[i].extra_colormap->rgba); UINT8 lightalpha = R_GetRgbaA(wsectors[i].extra_colormap->rgba); INT32 fadecolor = P_RGBAToColor(wsectors[i].extra_colormap->fadergba); UINT8 fadealpha = R_GetRgbaA(wsectors[i].extra_colormap->fadergba); + // For now, convert alpha from new (0-255) to old 'A-Z' (0-25) range + // TODO: remove this limitation in a backwards-compatible way (UDMF versioning?) + lightalpha /= 10; + fadealpha /= 10; + if (lightcolor != 0) fprintf(f, "lightcolor = %d;\n", lightcolor); if (lightalpha != 25) @@ -2968,7 +2998,8 @@ static void P_LoadTextmap(void) P_InitializeSector(sc); if (textmap_colormap.used) { - // Convert alpha values from old 0-25 (A-Z) range to 0-255 range + // Convert alpha values from old 'A-Z' (0-25) range to new UINT8 (0-255) range + // TODO: remove this limitation in a backwards-compatible way (UDMF versioning?) UINT8 lightalpha = (textmap_colormap.lightalpha * 102) / 10; UINT8 fadealpha = (textmap_colormap.fadealpha * 102) / 10; diff --git a/src/p_setup.h b/src/p_setup.h index 61c445c6523ba28a589c54364bd644e701282110..da38d4c08b152f87fab59517706c3ccbc7544339 100644 --- a/src/p_setup.h +++ b/src/p_setup.h @@ -87,5 +87,6 @@ UINT8 P_GetGrade(UINT32 pscore, INT16 map, UINT8 mare); UINT8 P_HasGrades(INT16 map, UINT8 mare); UINT32 P_GetScoreForGrade(INT16 map, UINT8 mare, UINT8 grade); UINT32 P_GetScoreForGradeOverall(INT16 map, UINT8 grade); +void P_AddNiGHTSTimes(INT16 i, char *gtext); #endif diff --git a/src/p_user.c b/src/p_user.c index eb1fa04bd5ea1facb946c9b183b06854b45dd6f1..7cd128cf080792f64df4a56ca4aeb4e9076e6b94 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -776,7 +776,7 @@ static void P_DeNightserizePlayer(player_t *player) // NiGHTS Time! void P_NightserizePlayer(player_t *player, INT32 nighttime) { - UINT8 oldmare, oldmarelap, oldmarebonuslap; + UINT8 oldmare, oldmarelap, oldmarebonuslap, newmare; // Bots can't be NiGHTSerized, silly!1 :P if (player->bot) @@ -797,6 +797,11 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime) } } + // Use mare-specific time limit if specified + newmare = P_FindLowestMare(); + if (mapheaderinfo[gamemap-1]->nightstimer[newmare] > 0) + nighttime = mapheaderinfo[gamemap-1]->nightstimer[newmare]; + player->pflags &= ~(PF_SPINDOWN|PF_JUMPDOWN|PF_ATTACKDOWN|PF_SHIELDDOWN|PF_STARTDASH|PF_GLIDING|PF_JUMPED|PF_NOJUMPDAMAGE|PF_THOKKED|PF_SHIELDABILITY|PF_SPINNING|PF_DRILLING); player->homing = 0; player->mo->fuse = 0; @@ -867,7 +872,7 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime) continue; players[i].texttimer = (3 * TICRATE) - 10; - players[i].textvar = 4; // Score and grades + players[i].textvar = NTV_BONUSTIMEEND; // Score and grades players[i].lastmare = players[i].mare; players[i].lastmarelap = players[i].marelap; players[i].lastmarebonuslap = players[i].marebonuslap; @@ -885,7 +890,8 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime) } // Add score to leaderboards now - G_AddTempNightsRecords(player, players[i].marescore, leveltime - player->marebegunat, players[i].mare + 1); + player->lastmaretime = leveltime - player->marebegunat; + G_AddTempNightsRecords(player, players[i].marescore, player->lastmaretime, players[i].mare + 1); // transfer scores anyway players[i].totalmarescore += players[i].marescore; @@ -906,12 +912,13 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime) player->lastmarelap = oldmarelap; player->lastmarebonuslap = oldmarebonuslap; player->texttimer = 4*TICRATE; - player->textvar = 4; // Score and grades + player->textvar = NTV_BONUSTIMEEND; // Score and grades player->finishedspheres = (INT16)(player->spheres); player->finishedrings = (INT16)(player->rings); - + // Add score to temp leaderboards - G_AddTempNightsRecords(player, player->marescore, leveltime - player->marebegunat, (UINT8)(oldmare + 1)); + player->lastmaretime = leveltime - player->marebegunat; + G_AddTempNightsRecords(player, player->marescore, player->lastmaretime, (UINT8)(oldmare + 1)); // Starting a new mare, transfer scores player->totalmarescore += player->marescore; @@ -924,7 +931,7 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime) } else { - player->textvar = 5; // Nothing, just tells it to go to the GET n RINGS/SPHERES text in a bit + player->textvar = NTV_NONE; // Nothing, just tells it to go to the GET n RINGS/SPHERES text in a bit player->texttimer = 40; // Don't show before title card @@ -7005,7 +7012,7 @@ static void P_DoNiGHTSCapsule(player_t *player) { players[i].bonustime = true; players[i].texttimer = 4*TICRATE; - players[i].textvar = 1; // Time Bonus + players[i].textvar = NTV_BONUSTIMESTART; // Time Bonus players[i].finishedtime = players[i].nightstime; if (!G_IsSpecialStage(gamemap)) P_AddPlayerScore(&players[i], (players[i].finishedtime/TICRATE) * 100); @@ -7089,12 +7096,12 @@ static void P_DoNiGHTSCapsule(player_t *player) { S_StartScreamSound(player->mo, sfx_lose); player->texttimer = 4*TICRATE; - player->textvar = 3; // Get more rings! + player->textvar = NTV_GETMORESPHERES; // Get more spheres/chips! player->capsule->reactiontime = 0; player->capsule->extravalue1 = player->capsule->cvmem =\ player->capsule->cusval = player->capsule->movecount =\ player->capsule->lastlook = player->capsule->extravalue2 = -1; - P_RunNightsCapsuleTouchExecutors(player->mo, false, false); // run capsule exit executors, and we lacked rings + P_RunNightsCapsuleTouchExecutors(player->mo, false, false); // run capsule exit executors, and we lacked spheres/chips } } } @@ -12448,13 +12455,13 @@ void P_PlayerThink(player_t *player) if (player->texttimer) { --player->texttimer; - if (!player->texttimer && !player->exiting && player->textvar >= 4) + if (!player->texttimer && !player->exiting && (player->textvar == NTV_NONE || player->textvar == NTV_BONUSTIMEEND)) { player->texttimer = 4*TICRATE; - player->textvar = 2; // GET n RINGS! + player->textvar = NTV_GETSPHERES; // GET n SPHERES/CHIPS! - if (!P_MobjWasRemoved(player->capsule) && player->capsule->health != player->capsule->spawnpoint->angle) - player->textvar++; // GET n MORE RINGS! + if (!P_MobjWasRemoved(player->capsule) && player->capsule->health != player->capsule->spawnpoint->args[1]) + player->textvar = NTV_GETMORESPHERES; // GET n MORE SPHERES/CHIPS! } } diff --git a/src/st_stuff.c b/src/st_stuff.c index 3c796b5b3d8903c5425babe0bce8e9e57572e142..7df6f8848dd80c2cb0eb5839fd0389b7c86a3b8b 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -1755,22 +1755,23 @@ static void ST_drawNightsRecords(void) switch (stplyr->textvar) { - case 1: // A "Bonus Time Start" by any other name... + case NTV_BONUSTIMESTART: // A "Bonus Time Start" by any other name... { V_DrawCenteredString(BASEVIDWIDTH/2, 52, V_GREENMAP|aflag, M_GetText("GET TO THE GOAL!")); V_DrawCenteredString(BASEVIDWIDTH/2, 60, aflag, M_GetText("SCORE MULTIPLIER START!")); if (stplyr->finishedtime) { - V_DrawString(BASEVIDWIDTH/2 - 48, 140, aflag, "TIME:"); - V_DrawString(BASEVIDWIDTH/2 - 48, 148, aflag, "BONUS:"); - V_DrawRightAlignedString(BASEVIDWIDTH/2 + 48, 140, V_ORANGEMAP|aflag, va("%d", (stplyr->startedtime - stplyr->finishedtime)/TICRATE)); - V_DrawRightAlignedString(BASEVIDWIDTH/2 + 48, 148, V_ORANGEMAP|aflag, va("%d", (stplyr->finishedtime/TICRATE) * 100)); + tic_t maretime = stplyr->startedtime - stplyr->finishedtime; + V_DrawString(BASEVIDWIDTH/2 - 48, 140, V_YELLOWMAP|aflag, "TIME:"); + V_DrawString(BASEVIDWIDTH/2 - 48, 148, V_YELLOWMAP|aflag, "BONUS:"); + V_DrawRightAlignedString(BASEVIDWIDTH/2 + 48, 140, aflag, va("%i:%02i.%02i", G_TicsToMinutes(maretime,true), G_TicsToSeconds(maretime), G_TicsToCentiseconds(maretime))); + V_DrawRightAlignedString(BASEVIDWIDTH/2 + 48, 148, aflag, va("%d", (stplyr->finishedtime/TICRATE) * 100)); } break; } - case 2: // Get n Spheres - case 3: // Get n more Spheres + case NTV_GETSPHERES: // Get n Spheres + case NTV_GETMORESPHERES: // Get n more Spheres { if (!stplyr->capsule) return; @@ -1778,31 +1779,37 @@ static void ST_drawNightsRecords(void) // Yes, this string is an abomination. V_DrawCenteredString(BASEVIDWIDTH/2, 60, aflag, va(M_GetText("\x80GET\x82 %d\x80 %s%s%s!"), stplyr->capsule->health, - (stplyr->textvar == 3) ? M_GetText("MORE ") : "", + (stplyr->textvar == NTV_GETMORESPHERES) ? M_GetText("MORE ") : "", (G_IsSpecialStage(gamemap)) ? "SPHERE" : "CHIP", (stplyr->capsule->health > 1) ? "S" : "")); break; } - case 4: // End Bonus + case NTV_BONUSTIMEEND: // End Bonus { - V_DrawString(BASEVIDWIDTH/2 - 56, 140, aflag, (G_IsSpecialStage(gamemap)) ? "SPHERES:" : "CHIPS:"); - V_DrawString(BASEVIDWIDTH/2 - 56, 148, aflag, "BONUS:"); - V_DrawRightAlignedString(BASEVIDWIDTH/2 + 56, 140, V_ORANGEMAP|aflag, va("%d", stplyr->finishedspheres)); - V_DrawRightAlignedString(BASEVIDWIDTH/2 + 56, 148, V_ORANGEMAP|aflag, va("%d", stplyr->finishedspheres * 50)); - ST_DrawNightsOverlayNum((BASEVIDWIDTH/2 + 56)<<FRACBITS, 160<<FRACBITS, FRACUNIT, aflag, stplyr->lastmarescore, nightsnum, SKINCOLOR_AZURE); + V_DrawString(BASEVIDWIDTH/2 - 48, 132, V_YELLOWMAP|aflag, "TIME:"); + V_DrawString(BASEVIDWIDTH/2 - 48, 140, V_YELLOWMAP|aflag, (G_IsSpecialStage(gamemap)) ? "SPHERES:" : "CHIPS:"); + V_DrawString(BASEVIDWIDTH/2 - 48, 148, V_YELLOWMAP|aflag, "BONUS:"); + V_DrawRightAlignedString(BASEVIDWIDTH/2 + 48, 132, aflag, va("%i:%02i.%02i", G_TicsToMinutes(stplyr->lastmaretime,true), G_TicsToSeconds(stplyr->lastmaretime), G_TicsToCentiseconds(stplyr->lastmaretime))); + V_DrawRightAlignedString(BASEVIDWIDTH/2 + 48, 140, aflag, va("%d", stplyr->finishedspheres)); + V_DrawRightAlignedString(BASEVIDWIDTH/2 + 48, 148, aflag, va("%d", stplyr->finishedspheres * 50)); + ST_DrawNightsOverlayNum((BASEVIDWIDTH/2 + 48)<<FRACBITS, 160<<FRACBITS, FRACUNIT, aflag, stplyr->lastmarescore, nightsnum, SKINCOLOR_AZURE); + + // If this is a multi-mare map, display the mare number. + if (stplyr->lastmare || P_FindLowestMare() < UINT8_MAX) + V_DrawLevelActNum(BASEVIDWIDTH/2 - 80, 128 + 3, aflag, stplyr->lastmare + 1); // If new record, say so! if (!(netgame || multiplayer) && G_GetBestNightsScore(gamemap, stplyr->lastmare + 1, clientGamedata) <= stplyr->lastmarescore) { if (stplyr->texttimer & 16) - V_DrawCenteredString(BASEVIDWIDTH/2, 184, V_YELLOWMAP|aflag, "* NEW RECORD *"); + V_DrawCenteredString(BASEVIDWIDTH/2, 184, aflag, "\x85* \x82NEW RECORD \x85*\x80"); } if (P_HasGrades(gamemap, stplyr->lastmare + 1)) { UINT8 grade = P_GetGrade(stplyr->lastmarescore, gamemap, stplyr->lastmare); - if (modeattacking || grade >= GRADE_A) - V_DrawTranslucentPatch(BASEVIDWIDTH/2 + 60, 160, aflag, ngradeletters[grade]); + if (modeattacking || !G_IsSpecialStage(gamemap) || grade >= GRADE_A) + V_DrawTranslucentPatch(BASEVIDWIDTH/2 + 60, 128, aflag, ngradeletters[grade]); } break; } @@ -1912,7 +1919,7 @@ static void ST_drawNiGHTSHUD(void) // Link drawing if (!oldspecialstage // Don't display when the score is showing (it popping up for a split second when exiting a map is intentional) - && !(stplyr->texttimer && stplyr->textvar == 4) + && !(stplyr->texttimer && stplyr->textvar == NTV_BONUSTIMEEND) && LUA_HudEnabled(hud_nightslink) && ((cv_debug & DBG_NIGHTSBASIC) || stplyr->linkcount > 1)) // When debugging, show "0 Link". { @@ -1959,7 +1966,7 @@ static void ST_drawNiGHTSHUD(void) INT32 amount; const INT32 length = 88; - origamount = stplyr->capsule->spawnpoint->angle; + origamount = stplyr->capsule->spawnpoint->args[1]; I_Assert(origamount > 0); // should not happen now ST_DrawTopLeftOverlayPatch(72, 8, nbracket);