diff --git a/src/cvars.cpp b/src/cvars.cpp index 82d0b12f1..4f4ab8e16 100644 --- a/src/cvars.cpp +++ b/src/cvars.cpp @@ -876,8 +876,10 @@ consvar_t cv_debugrank = PlayerCheat("debugrank", "Off").description("Show GP ra }); consvar_t cv_debugrender_contrast = PlayerCheat("debugrender_contrast", "0.0").floating_point().min_max(-FRACUNIT, FRACUNIT).description("Change level lighting"); +consvar_t cv_debugrender_freezebsp = PlayerCheat("debugrender_freezebsp", "Off").on_off().description("Freeze level culling so you can observe how much of the level is being rendered"); consvar_t cv_debugrender_portal = PlayerCheat("debugrender_portal", "Off").on_off().description("Highlight visual portals in red"); consvar_t cv_debugrender_spriteclip = PlayerCheat("debugrender_spriteclip", "Off").on_off().description("Let sprites draw through walls"); +consvar_t cv_debugrender_visplanes = PlayerCheat("debugrender_visplanes", "Off").on_off().description("Highlight the number of visplanes"); consvar_t cv_devmode_screen = PlayerCheat("devmode_screen", "1").min_max(1, 4).description("Choose which splitscreen player devmode applies to"); consvar_t cv_drawpickups = PlayerCheat("drawpickups", "Yes").yes_no().description("Hide rings, spheres, item capsules, prison capsules (visual only)"); consvar_t cv_drawinput = PlayerCheat("drawinput", "No").yes_no().description("Draw turn inputs outside of Record Attack (turn solver debugging)"); diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 97df20d4d..1f80b4e19 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -209,6 +209,7 @@ CV_PossibleValue_t kartdebugitem_cons_t[] = {POWERUP_BUMPER, "Bumper"}, {POWERUP_BADGE, "Badge"}, {POWERUP_SUPERFLICKY, "SuperFlicky"}, + {POWERUP_POINTS, "Points"}, {0} }; diff --git a/src/d_player.h b/src/d_player.h index 75002da11..ab6a1d917 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -207,6 +207,7 @@ typedef enum POWERUP_BUMPER, POWERUP_BADGE, POWERUP_SUPERFLICKY, + POWERUP_POINTS, ENDOFPOWERUPS, LASTPOWERUP = ENDOFPOWERUPS - 1, NUMPOWERUPS = ENDOFPOWERUPS - FIRSTPOWERUP, diff --git a/src/deh_tables.c b/src/deh_tables.c index 5faf0fe93..36239071c 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -2259,19 +2259,6 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi "S_POWERCLASH", // Invinc/Grow no damage collide VFX "S_GUARDBREAK", // Guard break - "S_PLAYERARROW", // Above player arrow - "S_PLAYERARROW_BOX", - "S_PLAYERARROW_ITEM", - "S_PLAYERARROW_NUMBER", - "S_PLAYERARROW_X", - "S_PLAYERARROW_WANTED1", - "S_PLAYERARROW_WANTED2", - "S_PLAYERARROW_WANTED3", - "S_PLAYERARROW_WANTED4", - "S_PLAYERARROW_WANTED5", - "S_PLAYERARROW_WANTED6", - "S_PLAYERARROW_WANTED7", - "S_PLAYERBOMB1", // Player bomb overlay "S_PLAYERBOMB2", "S_PLAYERBOMB3", @@ -3615,9 +3602,6 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t "MT_POWERCLASH", // Invinc/Grow no damage clash VFX "MT_GUARDBREAK", // Guard break - "MT_PLAYERARROW", - "MT_PLAYERWANTED", - "MT_KARMAHITBOX", "MT_KARMAWHEEL", @@ -5057,6 +5041,7 @@ struct int_const_s const INT_CONST[] = { {"POWERUP_BUMPER",POWERUP_BUMPER}, {"POWERUP_BADGE",POWERUP_BADGE}, {"POWERUP_SUPERFLICKY",POWERUP_SUPERFLICKY}, + {"POWERUP_POINTS",POWERUP_POINTS}, {"ENDOFPOWERUPS",ENDOFPOWERUPS}, {"LASTPOWERUP",LASTPOWERUP}, {"NUMPOWERUPS",NUMPOWERUPS}, diff --git a/src/info.c b/src/info.c index d48741603..430373e7a 100644 --- a/src/info.c +++ b/src/info.c @@ -472,14 +472,12 @@ char sprnames[NUMSPRITES + 1][5] = "PWCL", // Invinc/grow clash VFX "GBRK", // Guard break - "ARRO", // player arrows "ITEM", "ITMO", "ITMI", "ITMN", "PWRB", "RBOW", // power-up aura - "WANT", "PBOM", // player bomb @@ -2744,20 +2742,6 @@ state_t states[NUMSTATES] = {SPR_PWCL, FF_FULLBRIGHT|FF_ANIMATE|FF_PAPERSPRITE, 10, {NULL}, 9, 1, S_NULL}, // S_POWERCLASH {SPR_GBRK, FF_ADD|FF_FULLBRIGHT|FF_ANIMATE|FF_PAPERSPRITE, 24, {NULL}, 5, 4, S_NULL}, // S_GUARDBREAK - // Above player arrow - {SPR_ARRO, FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_PLAYERARROW - {SPR_ARRO, FF_FULLBRIGHT|1, -1, {NULL}, 0, 0, S_NULL}, // S_PLAYERARROW_BOX - {SPR_NULL, FF_FULLBRIGHT, -1, {NULL}, 0, 11, S_NULL}, // S_PLAYERARROW_ITEM - {SPR_ITMN, FF_FULLBRIGHT, 2, {NULL}, 0, 11, S_NULL}, // S_PLAYERARROW_NUMBER - {SPR_ITMN, FF_FULLBRIGHT|11, 2, {NULL}, 0, 11, S_NULL}, // S_PLAYERARROW_X - {SPR_WANT, FF_FULLBRIGHT, 5, {NULL}, 0, 0, S_PLAYERARROW_WANTED2}, // S_PLAYERARROW_WANTED1 - {SPR_WANT, FF_FULLBRIGHT|1, 1, {NULL}, 0, 0, S_PLAYERARROW_WANTED3}, // S_PLAYERARROW_WANTED2 - {SPR_WANT, FF_FULLBRIGHT|2, 3, {NULL}, 0, 0, S_PLAYERARROW_WANTED4}, // S_PLAYERARROW_WANTED3 - {SPR_WANT, FF_FULLBRIGHT|3, 1, {NULL}, 0, 0, S_PLAYERARROW_WANTED5}, // S_PLAYERARROW_WANTED4 - {SPR_WANT, FF_FULLBRIGHT|4, 3, {NULL}, 0, 0, S_PLAYERARROW_WANTED6}, // S_PLAYERARROW_WANTED5 - {SPR_WANT, FF_FULLBRIGHT|5, 1, {NULL}, 0, 0, S_PLAYERARROW_WANTED7}, // S_PLAYERARROW_WANTED6 - {SPR_WANT, FF_FULLBRIGHT|6, 3, {NULL}, 0, 0, S_PLAYERARROW_WANTED1}, // S_PLAYERARROW_WANTED7 - {SPR_SPBM, 0, 1, {NULL}, 0, 0, S_PLAYERBOMB2}, // S_PLAYERBOMB1 {SPR_SPBM, 1, 1, {NULL}, 0, 0, S_PLAYERBOMB3}, // S_PLAYERBOMB2 {SPR_SPBM, 0, 1, {NULL}, 0, 0, S_PLAYERBOMB4}, // S_PLAYERBOMB3 @@ -16422,60 +16406,6 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, - { // MT_PLAYERARROW - -1, // doomednum - S_PLAYERARROW, // 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 - 8, // speed - 36*FRACUNIT, // radius - 37*FRACUNIT, // height - -2, // display offset - 16, // mass - 0, // damage - sfx_None, // activesound - MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP, // flags - S_NULL // raisestate - }, - - { // MT_PLAYERWANTED - -1, // doomednum - S_PLAYERARROW_WANTED1, // 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 - 8, // speed - 36*FRACUNIT, // radius - 37*FRACUNIT, // height - -2, // display offset - 16, // mass - 0, // damage - sfx_None, // activesound - MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP, // flags - S_NULL // raisestate - }, - { // MT_KARMAHITBOX -1, // doomednum S_PLAYERBOMB1, // spawnstate diff --git a/src/info.h b/src/info.h index 4c518cb8e..da42752e4 100644 --- a/src/info.h +++ b/src/info.h @@ -1011,14 +1011,12 @@ typedef enum sprite SPR_PWCL, // Invinc/grow clash VFX SPR_GBRK, // Guard break - SPR_ARRO, // player arrows SPR_ITEM, SPR_ITMO, SPR_ITMI, SPR_ITMN, SPR_PWRB, SPR_RBOW, // power-up aura - SPR_WANT, SPR_PBOM, // player bomb @@ -3250,19 +3248,6 @@ typedef enum state S_POWERCLASH, // Grow/Invinc clash VFX S_GUARDBREAK, - S_PLAYERARROW, // Above player arrow - S_PLAYERARROW_BOX, - S_PLAYERARROW_ITEM, - S_PLAYERARROW_NUMBER, - S_PLAYERARROW_X, - S_PLAYERARROW_WANTED1, - S_PLAYERARROW_WANTED2, - S_PLAYERARROW_WANTED3, - S_PLAYERARROW_WANTED4, - S_PLAYERARROW_WANTED5, - S_PLAYERARROW_WANTED6, - S_PLAYERARROW_WANTED7, - S_PLAYERBOMB1, // Karma player overlays S_PLAYERBOMB2, S_PLAYERBOMB3, @@ -4633,9 +4618,6 @@ typedef enum mobj_type MT_POWERCLASH, // Grow/Invinc clash VFX MT_GUARDBREAK, - MT_PLAYERARROW, - MT_PLAYERWANTED, - MT_KARMAHITBOX, MT_KARMAWHEEL, diff --git a/src/k_battle.c b/src/k_battle.c index 21f4961bb..d08fcee2c 100644 --- a/src/k_battle.c +++ b/src/k_battle.c @@ -173,14 +173,9 @@ void K_CheckBumpers(void) } else if (eliminated >= numingame - 1) { - if (kingofthehill != -1) - { - // If every other player is eliminated, the - // last player standing wins by default. - players[kingofthehill].roundscore = 100; - } - - P_DoAllPlayersExit(0, false); + // If every other player is eliminated, the + // last player standing wins by default. + K_EndBattleRound(kingofthehill != -1 ? &players[kingofthehill] : NULL); return; } @@ -209,15 +204,11 @@ void K_CheckEmeralds(player_t *player) return; } - if (player->exiting) + if (!K_EndBattleRound(player)) { return; } - player->roundscore = 100; // lmao - - P_DoAllPlayersExit(0, false); - // TODO: this would be better if the timing lived in // Tally code. But I didn't do it that, so this just // shittily approximates syncing up with Tally. @@ -969,3 +960,27 @@ boolean K_BattleOvertimeKiller(mobj_t *mobj) return true; } + +boolean K_EndBattleRound(player_t *victor) +{ + if (victor) + { + if (victor->exiting) + { + // In Battle, players always exit altogether. + // So it can be assumed that if this player is + // exiting, the round has already ended. + return false; + } + + if (gametyperules & GTR_POINTLIMIT) + { + // Lock the winner in before the round ends. + victor->roundscore = 100; + } + } + + P_DoAllPlayersExit(0, false); + + return true; +} diff --git a/src/k_battle.h b/src/k_battle.h index bc894c150..f1310e4d1 100644 --- a/src/k_battle.h +++ b/src/k_battle.h @@ -53,6 +53,7 @@ void K_BattleInit(boolean singleplayercontext); UINT8 K_Bumpers(player_t *player); INT32 K_BumpersToHealth(UINT8 bumpers); boolean K_BattleOvertimeKiller(mobj_t *mobj); +boolean K_EndBattleRound(player_t *victor); #ifdef __cplusplus } // extern "C" diff --git a/src/k_endcam.cpp b/src/k_endcam.cpp index 20cdf2848..6d92a9751 100644 --- a/src/k_endcam.cpp +++ b/src/k_endcam.cpp @@ -60,7 +60,7 @@ struct Camera : camera_t struct EndCam : endcam_t { tic_t Time() const { return leveltime - begin; } - bool Freezing() const { return Time() <= swirlDuration; } + bool Freezing() const { return active && Time() <= swirlDuration; } void GC() { diff --git a/src/k_hud.cpp b/src/k_hud.cpp index b26548122..5622e3abe 100644 --- a/src/k_hud.cpp +++ b/src/k_hud.cpp @@ -130,36 +130,36 @@ static patch_t *kp_wantedsplit; static patch_t *kp_wantedreticle; static patch_t *kp_minimapdot; -static patch_t *kp_itembg[4]; +static patch_t *kp_itembg[6]; static patch_t *kp_ringbg[4]; static patch_t *kp_itemtimer[2]; static patch_t *kp_itemmulsticker[2]; static patch_t *kp_itemx; -static patch_t *kp_sadface[2]; -static patch_t *kp_sneaker[2]; -static patch_t *kp_rocketsneaker[2]; -static patch_t *kp_invincibility[13]; -static patch_t *kp_banana[2]; -static patch_t *kp_eggman[2]; -static patch_t *kp_orbinaut[5]; -static patch_t *kp_jawz[2]; -static patch_t *kp_mine[2]; -static patch_t *kp_landmine[2]; -static patch_t *kp_ballhog[2]; -static patch_t *kp_selfpropelledbomb[2]; -static patch_t *kp_grow[2]; -static patch_t *kp_shrink[2]; -static patch_t *kp_lightningshield[2]; -static patch_t *kp_bubbleshield[2]; -static patch_t *kp_flameshield[2]; -static patch_t *kp_hyudoro[2]; -static patch_t *kp_pogospring[2]; -static patch_t *kp_superring[2]; -static patch_t *kp_kitchensink[2]; -static patch_t *kp_droptarget[2]; -static patch_t *kp_gardentop[2]; -static patch_t *kp_gachabom[2]; +static patch_t *kp_sadface[3]; +static patch_t *kp_sneaker[3]; +static patch_t *kp_rocketsneaker[3]; +static patch_t *kp_invincibility[19]; +static patch_t *kp_banana[3]; +static patch_t *kp_eggman[3]; +static patch_t *kp_orbinaut[6]; +static patch_t *kp_jawz[3]; +static patch_t *kp_mine[3]; +static patch_t *kp_landmine[3]; +static patch_t *kp_ballhog[3]; +static patch_t *kp_selfpropelledbomb[3]; +static patch_t *kp_grow[3]; +static patch_t *kp_shrink[3]; +static patch_t *kp_lightningshield[3]; +static patch_t *kp_bubbleshield[3]; +static patch_t *kp_flameshield[3]; +static patch_t *kp_hyudoro[3]; +static patch_t *kp_pogospring[3]; +static patch_t *kp_superring[3]; +static patch_t *kp_kitchensink[3]; +static patch_t *kp_droptarget[3]; +static patch_t *kp_gardentop[3]; +static patch_t *kp_gachabom[3]; static patch_t *kp_bar[2]; static patch_t *kp_doublebar[2]; static patch_t *kp_triplebar[2]; @@ -613,6 +613,40 @@ void K_LoadKartHUDGraphics(void) HU_UpdatePatch(&kp_flameshieldmeter_bg[i][1], "%s", buffer); } + // 4P item spy + HU_UpdatePatch(&kp_itembg[4], "ISPYBG"); + HU_UpdatePatch(&kp_itembg[5], "ISPYBGD"); + + //HU_UpdatePatch(&kp_sadface[2], "ISPYSAD"); + HU_UpdatePatch(&kp_sneaker[2], "ISPYSHOE"); + HU_UpdatePatch(&kp_rocketsneaker[2], "ISPYRSHE"); + sprintf(buffer, "ISPYINVx"); + for (i = 0; i < 6; i++) + { + buffer[7] = '1'+i; + HU_UpdatePatch(&kp_invincibility[i+13], "%s", buffer); + } + HU_UpdatePatch(&kp_banana[2], "ISPYBANA"); + HU_UpdatePatch(&kp_eggman[2], "ISPYEGGM"); + HU_UpdatePatch(&kp_orbinaut[5], "ISPYORBN"); + HU_UpdatePatch(&kp_jawz[2], "ISPYJAWZ"); + HU_UpdatePatch(&kp_mine[2], "ISPYMINE"); + HU_UpdatePatch(&kp_landmine[2], "ISPYLNDM"); + HU_UpdatePatch(&kp_ballhog[2], "ISPYBHOG"); + HU_UpdatePatch(&kp_selfpropelledbomb[2], "ISPYSPB"); + HU_UpdatePatch(&kp_grow[2], "ISPYGROW"); + HU_UpdatePatch(&kp_shrink[2], "ISPYSHRK"); + HU_UpdatePatch(&kp_lightningshield[2], "ISPYTHNS"); + HU_UpdatePatch(&kp_bubbleshield[2], "ISPYBUBS"); + HU_UpdatePatch(&kp_flameshield[2], "ISPYFLMS"); + HU_UpdatePatch(&kp_hyudoro[2], "ISPYHYUD"); + HU_UpdatePatch(&kp_pogospring[2], "ISPYPOGO"); + HU_UpdatePatch(&kp_superring[2], "ISPYRING"); + HU_UpdatePatch(&kp_kitchensink[2], "ISPYSINK"); + HU_UpdatePatch(&kp_droptarget[2], "ISPYDTRG"); + HU_UpdatePatch(&kp_gardentop[2], "ISPYGTOP"); + HU_UpdatePatch(&kp_gachabom[2], "ISPYGBOM"); + // CHECK indicators sprintf(buffer, "K_CHECKx"); for (i = 0; i < 6; i++) @@ -1325,7 +1359,7 @@ void K_DrawLikeMapThumbnail(fixed_t x, fixed_t y, fixed_t width, UINT32 flags, p ); } -// see also MT_PLAYERARROW mobjthinker in p_mobj.c +// see also K_DrawNameTagItemSpy static void K_drawKartItem(void) { // ITEM_X = BASEVIDWIDTH-50; // 270 @@ -3207,6 +3241,7 @@ static void K_drawKartSpeedometer(boolean gametypeinfoshown) static void K_drawBlueSphereMeter(boolean gametypeinfoshown) { const UINT8 maxBars = 4; + // see also K_DrawNameTagSphereMeter const UINT8 segColors[] = {73, 64, 52, 54, 55, 35, 34, 33, 202, 180, 181, 182, 164, 165, 166, 153, 152}; const UINT8 sphere = std::clamp(static_cast(stplyr->spheres), 0, 40); @@ -3675,28 +3710,101 @@ static void K_DrawRivalTagForPlayer(fixed_t x, fixed_t y) V_DrawFixedPatch(x, y, FRACUNIT, V_HUDTRANS|V_SPLITSCREEN, kp_rival[blink], NULL); } -static void K_DrawTypingDot(fixed_t x, fixed_t y, UINT8 duration, player_t *p) +static void K_DrawTypingDot(fixed_t x, fixed_t y, UINT8 duration, player_t *p, INT32 flags) { if (p->typing_duration > duration) { - V_DrawFixedPatch(x, y, FRACUNIT, V_HUDTRANS|V_SPLITSCREEN, kp_typdot, NULL); + V_DrawFixedPatch(x, y, FRACUNIT, V_HUDTRANS|V_SPLITSCREEN|flags, kp_typdot, NULL); } } -static void K_DrawTypingNotifier(fixed_t x, fixed_t y, player_t *p) +static void K_DrawTypingNotifier(fixed_t x, fixed_t y, player_t *p, INT32 flags) { if (p->cmd.flags & TICCMD_TYPING) { - V_DrawFixedPatch(x, y, FRACUNIT, V_HUDTRANS|V_SPLITSCREEN, kp_talk, NULL); + V_DrawFixedPatch(x, y, FRACUNIT, V_HUDTRANS|V_SPLITSCREEN|flags, kp_talk, NULL); /* spacing closer with the last two looks a better most of the time */ - K_DrawTypingDot(x + 3*FRACUNIT, y, 15, p); - K_DrawTypingDot(x + 6*FRACUNIT - FRACUNIT/3, y, 31, p); - K_DrawTypingDot(x + 9*FRACUNIT - FRACUNIT/3, y, 47, p); + K_DrawTypingDot(x + 3*FRACUNIT, y, 15, p, flags); + K_DrawTypingDot(x + 6*FRACUNIT - FRACUNIT/3, y, 31, p, flags); + K_DrawTypingDot(x + 9*FRACUNIT - FRACUNIT/3, y, 47, p, flags); } } -static void K_DrawNameTagForPlayer(fixed_t x, fixed_t y, player_t *p) +// see also K_drawKartItem +static void K_DrawNameTagItemSpy(INT32 x, INT32 y, player_t *p, INT32 flags) +{ + using srb2::Draw; + bool tiny = r_splitscreen > 1; + Draw bar = Draw(x, y).flags(V_NOSCALESTART|flags); + Draw box = tiny ? bar.xy(-22 * vid.dupx, -17 * vid.dupy) : bar.xy(-40 * vid.dupx, -26 * vid.dupy); + + box.colorize(p->skincolor).patch(kp_itembg[tiny ? 4 : 2]); + + if (!(p->itemflags & IF_ITEMOUT) || (leveltime & 1)) + { + switch (p->itemtype) + { + case KITEM_INVINCIBILITY: + box.patch(kp_invincibility[((leveltime % (6*3)) / 3) + (tiny ? 13 : 7)]); + break; + + case KITEM_ORBINAUT: + box.patch(kp_orbinaut[4 + tiny]); + break; + + default: + if (patch_t *ico = K_GetCachedItemPatch(p->itemtype, 1 + tiny)) + { + box.patch(ico); + } + } + } + + if (p->itemamount > 1) + { + (tiny ? + bar.xy(-3 * vid.dupx, -4 * vid.dupy).font(Draw::Font::kPing) : + bar.xy(-4 * vid.dupx, -2 * vid.dupy).font(Draw::Font::kThinTimer) + ) + .align(Draw::Align::kRight) + .text("{}", p->itemamount); + } +} + +static void K_DrawNameTagSphereMeter(INT32 x, INT32 y, INT32 width, INT32 spheres, INT32 flags) +{ + using srb2::Draw; + Draw bar = Draw(x + vid.dupx, y).flags(V_NOSCALESTART).height(vid.dupy); + + // see also K_drawBlueSphereMeter + const UINT8 segColors[] = {73, 64, 52, 54, 55, 35, 34, 33, 202, 180, 181, 182, 164, 165, 166, 153, 152}; + + spheres = std::clamp(spheres, 0, 40); + int colorIndex = (spheres * sizeof segColors) / (40 + 1); + + int px = r_splitscreen > 1 ? 1 : 2; + int b = 10 * px; + int m = spheres * px; + + while (m > 0) + { + if (b > m) + b = m; + + Draw seg = bar.width(b); + + seg.fill(segColors[std::max(colorIndex - 1, 0)]); + seg.y(vid.dupy).fill(segColors[std::max(colorIndex - 2, 0)]); + seg.y(2 * vid.dupy).height(2 * vid.dupy).fill(segColors[colorIndex]); + seg.y(4 * vid.dupy).fill(31); + + bar = bar.x(b + vid.dupx); + m -= b; + } +} + +static void K_DrawNameTagForPlayer(fixed_t x, fixed_t y, player_t *p, INT32 flags) { const INT32 clr = skincolors[p->skincolor].chatcolor; const INT32 namelen = V_ThinStringWidth(player_names[p - players], 0); @@ -3738,16 +3846,76 @@ static void K_DrawNameTagForPlayer(fixed_t x, fixed_t y, player_t *p) bary += (vid.height - (BASEVIDHEIGHT * vid.dupy)) / 2; } + // see also K_CullTargetList + if ((gametyperules & GTR_ITEMARROWS) && p->itemtype != KITEM_NONE && p->itemamount != 0) + { + K_DrawNameTagItemSpy(barx, bary, p, flags); + } + + if (gametyperules & GTR_SPHERES) + { + K_DrawNameTagSphereMeter(barx, bary + (4 * vid.dupy), barw, p->spheres, flags); + } + // Lat: 10/06/2020: colormap can be NULL on the frame you join a game, just arbitrarily use palette indexes 31 and 0 instead of whatever the colormap would give us instead to avoid crashes. - V_DrawFill(barx, bary, barw, (3 * vid.dupy), (colormap ? colormap[31] : 31)|V_NOSCALESTART); - V_DrawFill(barx, bary + vid.dupy, barw, vid.dupy, (colormap ? colormap[0] : 0)|V_NOSCALESTART); + V_DrawFill(barx, bary, barw, (3 * vid.dupy), (colormap ? colormap[31] : 31)|V_NOSCALESTART|flags); + V_DrawFill(barx, bary + vid.dupy, barw, vid.dupy, (colormap ? colormap[0] : 0)|V_NOSCALESTART|flags); // END DRAWFILL DUMBNESS // Draw the stem - V_DrawFixedPatch(x, y, FRACUNIT, 0, kp_nametagstem, colormap); + V_DrawFixedPatch(x, y, FRACUNIT, flags, kp_nametagstem, colormap); // Draw the name itself - V_DrawThinStringAtFixed(x + (5*FRACUNIT), y - (26*FRACUNIT), clr, player_names[p - players]); + V_DrawThinStringAtFixed(x + (5*FRACUNIT), y - (26*FRACUNIT), clr|flags, player_names[p - players]); +} + +playertagtype_t K_WhichPlayerTag(player_t *p) +{ + UINT8 cnum = R_GetViewNumber(); + + if (!(demo.playback == true && camera[cnum].freecam == true) && P_IsDisplayPlayer(p) && + p != &players[displayplayers[cnum]]) + { + return PLAYERTAG_LOCAL; + } + else if (p->bot) + { + if (p->botvars.rival == true) + { + return PLAYERTAG_RIVAL; + } + } + else if (netgame || demo.playback) + { + if (K_ShowPlayerNametag(p) == true) + { + return PLAYERTAG_NAME; + } + } + + return PLAYERTAG_NONE; +} + +void K_DrawPlayerTag(fixed_t x, fixed_t y, player_t *p, playertagtype_t type, INT32 flags) +{ + switch (type) + { + case PLAYERTAG_LOCAL: + K_DrawLocalTagForPlayer(x, y, p, G_PartyPosition(p - players)); + break; + + case PLAYERTAG_RIVAL: + K_DrawRivalTagForPlayer(x, y); + break; + + case PLAYERTAG_NAME: + K_DrawNameTagForPlayer(x, y, p, flags); + K_DrawTypingNotifier(x, y, p, flags); + break; + + default: + break; + } } typedef struct weakspotdraw_t @@ -3789,12 +3957,8 @@ static void K_DrawWeakSpot(weakspotdraw_t *ws) static void K_drawKartNameTags(void) { - const fixed_t maxdistance = 8192*mapobjectscale; vector3_t c; UINT8 cnum = R_GetViewNumber(); - UINT8 tobesorted[MAXPLAYERS]; - fixed_t sortdist[MAXPLAYERS]; - UINT8 sortlen = 0; size_t i, j; if (stplyr == NULL || stplyr->mo == NULL || P_MobjWasRemoved(stplyr->mo)) @@ -3913,141 +4077,6 @@ static void K_drawKartNameTags(void) K_drawTargetHUD(&c, stplyr); - for (i = 0; i < MAXPLAYERS; i++) - { - player_t *ntplayer = &players[i]; - fixed_t distance = maxdistance+1; - vector3_t v; - - if (!playeringame[i] || ntplayer->spectator) - { - // Not in-game - continue; - } - - if (ntplayer->mo == NULL || P_MobjWasRemoved(ntplayer->mo)) - { - // No object - continue; - } - - if (ntplayer->mo->renderflags & K_GetPlayerDontDrawFlag(stplyr)) - { - // Invisible on this screen - continue; - } - - if (!P_CheckSight(stplyr->mo, ntplayer->mo)) - { - // Can't see - continue; - } - - v.x = R_InterpolateFixed(ntplayer->mo->old_x, ntplayer->mo->x); - v.y = R_InterpolateFixed(ntplayer->mo->old_y, ntplayer->mo->y); - v.z = R_InterpolateFixed(ntplayer->mo->old_z, ntplayer->mo->z); - - if (!(ntplayer->mo->eflags & MFE_VERTICALFLIP)) - { - v.z += ntplayer->mo->height; - } - - distance = R_PointToDist2(c.x, c.y, v.x, v.y); - - if (distance > maxdistance) - { - // Too far away - continue; - } - - tobesorted[sortlen] = ntplayer - players; - sortdist[sortlen] = distance; - sortlen++; - } - - if (sortlen > 0) - { - UINT8 sortedplayers[MAXPLAYERS]; - - for (i = 0; i < sortlen; i++) - { - UINT8 pos = 0; - - for (j = 0; j < sortlen; j++) - { - if (j == i) - { - continue; - } - - if (sortdist[i] < sortdist[j] - || (sortdist[i] == sortdist[j] && i > j)) - { - pos++; - } - } - - sortedplayers[pos] = tobesorted[i]; - } - - for (i = 0; i < sortlen; i++) - { - trackingResult_t result; - player_t *ntplayer = &players[sortedplayers[i]]; - - fixed_t headOffset = 36*ntplayer->mo->scale; - - SINT8 localindicator = -1; - vector3_t v; - - v.x = R_InterpolateFixed(ntplayer->mo->old_x, ntplayer->mo->x); - v.y = R_InterpolateFixed(ntplayer->mo->old_y, ntplayer->mo->y); - v.z = R_InterpolateFixed(ntplayer->mo->old_z, ntplayer->mo->z); - - v.z += (ntplayer->mo->height / 2); - - if (stplyr->mo->eflags & MFE_VERTICALFLIP) - { - v.z -= headOffset; - } - else - { - v.z += headOffset; - } - - K_ObjectTracking(&result, &v, false); - - if (result.onScreen == true) - { - if (!(demo.playback == true && camera[cnum].freecam == true) && P_IsDisplayPlayer(ntplayer) && - ntplayer != &players[displayplayers[cnum]]) - { - localindicator = G_PartyPosition(ntplayer - players); - } - - if (localindicator >= 0) - { - K_DrawLocalTagForPlayer(result.x, result.y, ntplayer, localindicator); - } - else if (ntplayer->bot) - { - if (ntplayer->botvars.rival == true) - { - K_DrawRivalTagForPlayer(result.x, result.y); - } - } - else if (netgame || demo.playback) - { - if (K_ShowPlayerNametag(ntplayer) == true) - { - K_DrawNameTagForPlayer(result.x, result.y, ntplayer); - K_DrawTypingNotifier(result.x, result.y, ntplayer); - } - } - } - } - } - V_ClearClipRect(); } diff --git a/src/k_hud.h b/src/k_hud.h index d91d0c5c0..95fd79931 100644 --- a/src/k_hud.h +++ b/src/k_hud.h @@ -99,6 +99,18 @@ void K_ClearPersistentMessages(void); void K_ClearPersistentMessageForPlayer(player_t *player); void K_TickMessages(void); +typedef enum +{ + PLAYERTAG_NONE, + PLAYERTAG_LOCAL, + PLAYERTAG_RIVAL, + PLAYERTAG_NAME, +} +playertagtype_t; + +playertagtype_t K_WhichPlayerTag(player_t *p); +void K_DrawPlayerTag(fixed_t x, fixed_t y, player_t *p, playertagtype_t type, INT32 flags); + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/k_hud_track.cpp b/src/k_hud_track.cpp index c33d31b89..7b1920ab9 100644 --- a/src/k_hud_track.cpp +++ b/src/k_hud_track.cpp @@ -6,6 +6,7 @@ #include "core/static_vec.hpp" #include "v_draw.hpp" +#include "g_game.h" #include "k_battle.h" #include "k_hud.h" #include "k_kart.h" @@ -67,6 +68,7 @@ struct TargetTracking trackingResult_t result; fixed_t camDist; bool foreground; + playertagtype_t nametag; skincolornum_t color() const { @@ -165,6 +167,42 @@ struct TargetTracking return nullptr; } + bool is_player_nametag_on_screen() const + { + const player_t* player = mobj->player; + + if (nametag == PLAYERTAG_NONE) + { + return false; + } + + if (player->spectator) + { + // Not in-game + return false; + } + + if (mobj->renderflags & K_GetPlayerDontDrawFlag(stplyr)) + { + // Invisible on this screen + return false; + } + + if (camDist > 8192*mapobjectscale) + { + // Too far away + return false; + } + + if (!P_CheckSight(stplyr->mo, const_cast(mobj))) + { + // Can't see + return false; + } + + return true; + } + private: Graphics graphics() const { @@ -320,6 +358,12 @@ Visibility is_object_visible(const mobj_t* mobj) void K_DrawTargetTracking(const TargetTracking& target) { + if (target.nametag != PLAYERTAG_NONE) + { + K_DrawPlayerTag(target.result.x, target.result.y, target.mobj->player, target.nametag, target.foreground ? 0 : V_60TRANS); + return; + } + Visibility visibility = is_object_visible(target.mobj); if (visibility == Visibility::kFlicker && (leveltime & 1)) @@ -524,6 +568,7 @@ void K_DrawTargetTracking(const TargetTracking& target) { using srb2::Draw; Draw(FixedToFloat(result.x), FixedToFloat(result.y)) + .flags(V_SPLITSCREEN) .font(Draw::Font::kThin) .align(Draw::Align::kCenter) .text("BUFO ID: {}", Obj_BattleUFOSpawnerID(target.mobj)); @@ -533,10 +578,11 @@ void K_DrawTargetTracking(const TargetTracking& target) void K_CullTargetList(std::vector& targetList) { - constexpr int kBlockSize = 20; - constexpr int kXBlocks = BASEVIDWIDTH / kBlockSize; - constexpr int kYBlocks = BASEVIDHEIGHT / kBlockSize; - bool map[kXBlocks][kYBlocks] = {}; + constexpr int kBlockWidth = 20; + constexpr int kBlockHeight = 10; + constexpr int kXBlocks = BASEVIDWIDTH / kBlockWidth; + constexpr int kYBlocks = BASEVIDHEIGHT / kBlockHeight; + UINT8 map[kXBlocks][kYBlocks] = {}; constexpr fixed_t kTrackerRadius = 30*FRACUNIT/2; // just an approximation of common HUD tracker @@ -552,10 +598,43 @@ void K_CullTargetList(std::vector& targetList) return; } - fixed_t x1 = std::max(((tr.result.x - kTrackerRadius) / kBlockSize) / FRACUNIT, 0); - fixed_t x2 = std::min(((tr.result.x + kTrackerRadius) / kBlockSize) / FRACUNIT, kXBlocks - 1); - fixed_t y1 = std::max(((tr.result.y - kTrackerRadius) / kBlockSize) / FRACUNIT, 0); - fixed_t y2 = std::min(((tr.result.y + kTrackerRadius) / kBlockSize) / FRACUNIT, kYBlocks - 1); + fixed_t x1, x2, y1, y2; + UINT8 bit = 1; + + // TODO: there should be some generic system + // instead of this special case. + if (tr.nametag == PLAYERTAG_NAME) + { + const player_t* p = tr.mobj->player; + + x1 = tr.result.x; + x2 = tr.result.x + ((6 + V_ThinStringWidth(player_names[p - players], 0)) * FRACUNIT); + y1 = tr.result.y - (30 * FRACUNIT); + y2 = tr.result.y - (4 * FRACUNIT); + bit = 2; // nametags will cull on a separate plane + + // see also K_DrawNameTagForPlayer + if ((gametyperules & GTR_ITEMARROWS) && p->itemtype != KITEM_NONE && p->itemamount != 0) + { + x1 -= 24 * FRACUNIT; + } + } + else if (tr.nametag != PLAYERTAG_NONE) + { + return; + } + else + { + x1 = tr.result.x - kTrackerRadius; + x2 = tr.result.x + kTrackerRadius; + y1 = tr.result.y - kTrackerRadius; + y2 = tr.result.y + kTrackerRadius; + } + + x1 = std::max(x1 / kBlockWidth / FRACUNIT, 0); + x2 = std::min(x2 / kBlockWidth / FRACUNIT, kXBlocks - 1); + y1 = std::max(y1 / kBlockHeight / FRACUNIT, 0); + y2 = std::min(y2 / kBlockHeight / FRACUNIT, kYBlocks - 1); bool allMine = true; @@ -563,17 +642,23 @@ void K_CullTargetList(std::vector& targetList) { for (fixed_t y = y1; y <= y2; ++y) { - if (map[x][y]) + if (map[x][y] & bit) { allMine = false; } else { - map[x][y] = true; + map[x][y] |= bit; if (cv_debughudtracker.value) { - V_DrawFill(x * kBlockSize, y * kBlockSize, kBlockSize, kBlockSize, 39 + debugColorCycle); + V_DrawFill( + x * kBlockWidth, + y * kBlockHeight, + kBlockWidth, + kBlockHeight, + (39 + debugColorCycle) | V_SPLITSCREEN + ); } } } @@ -611,7 +696,10 @@ void K_drawTargetHUD(const vector3_t* origin, player_t* player) continue; } - if (is_object_tracking_target(mobj) == false) + bool tracking = is_object_tracking_target(mobj); + playertagtype_t nametag = mobj->player ? K_WhichPlayerTag(mobj->player) : PLAYERTAG_NONE; + + if (tracking == false && nametag == PLAYERTAG_NONE) { continue; } @@ -627,10 +715,39 @@ void K_drawTargetHUD(const vector3_t* origin, player_t* player) tr.mobj = mobj; tr.camDist = R_PointToDist2(origin->x, origin->y, pos.x, pos.y); tr.foreground = false; + tr.nametag = PLAYERTAG_NONE; - K_ObjectTracking(&tr.result, &pos, false); + if (tracking) + { + K_ObjectTracking(&tr.result, &pos, false); + targetList.push_back(tr); + } - targetList.push_back(tr); + if (!mobj->player) + { + continue; + } + + tr.nametag = nametag; + + if (tr.is_player_nametag_on_screen()) + { + fixed_t headOffset = 36*mobj->scale; + if (stplyr->mo->eflags & MFE_VERTICALFLIP) + { + pos.z -= headOffset; + } + else + { + pos.z += headOffset; + } + K_ObjectTracking(&tr.result, &pos, false); + + if (tr.result.onScreen == true) + { + targetList.push_back(tr); + } + } } // Sort by distance from camera. Further trackers get diff --git a/src/k_kart.c b/src/k_kart.c index a11599dd6..c8835bd5b 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3897,6 +3897,12 @@ void K_BattleAwardHit(player_t *player, player_t *victim, mobj_t *inflictor, UIN return; } + if (player->exiting) + { + // The round has already ended, don't mess with points + return; + } + if ((inflictor && !P_MobjWasRemoved(inflictor)) && (inflictor->type == MT_BANANA && inflictor->health > 1)) { trapItem = true; @@ -3923,8 +3929,7 @@ void K_BattleAwardHit(player_t *player, player_t *victim, mobj_t *inflictor, UIN // Check this before adding to player score if ((gametyperules & GTR_BUMPERS) && finishOff && g_pointlimit <= player->roundscore) { - player->roundscore = 100; // Make sure you win! - P_DoAllPlayersExit(0, false); + K_EndBattleRound(player); mobj_t *source = !P_MobjWasRemoved(inflictor) ? inflictor : player->mo; @@ -3937,8 +3942,7 @@ void K_BattleAwardHit(player_t *player, player_t *victim, mobj_t *inflictor, UIN ); } - P_AddPlayerScore(player, points); - K_SpawnBattlePoints(player, victim, points); + K_GivePointsToPlayer(player, victim, points); } void K_SpinPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 type) @@ -4847,6 +4851,12 @@ void K_TakeBumpersFromPlayer(player_t *player, player_t *victim, UINT8 amount) S_StartSound(player->mo, sfx_3db06); } +void K_GivePointsToPlayer(player_t *player, player_t *victim, UINT8 amount) +{ + P_AddPlayerScore(player, amount); + K_SpawnBattlePoints(player, victim, amount); +} + #define MINEQUAKEDIST 4096 // Does the proximity screen flash and quake for explosions @@ -13422,7 +13432,7 @@ UINT32 K_PointLimitForGametype(void) { if (D_IsPlayerHumanAndGaming(i)) { - ptsCap += 4; + ptsCap += 3; } } diff --git a/src/k_kart.h b/src/k_kart.h index ad1132f61..bb532c432 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -134,6 +134,7 @@ INT32 K_ExplodePlayer(player_t *player, mobj_t *inflictor, mobj_t *source); void K_DebtStingPlayer(player_t *player, mobj_t *source); void K_GiveBumpersToPlayer(player_t *player, player_t *victim, UINT8 amount); void K_TakeBumpersFromPlayer(player_t *player, player_t *victim, UINT8 amount); +void K_GivePointsToPlayer(player_t *player, player_t *victim, UINT8 amount); void K_MineFlashScreen(mobj_t *source); void K_SpawnMineExplosion(mobj_t *source, skincolornum_t color, tic_t delay); void K_SpawnLandMineExplosion(mobj_t *source, skincolornum_t color, tic_t delay); diff --git a/src/k_powerup.cpp b/src/k_powerup.cpp index 96b020b81..70e762897 100644 --- a/src/k_powerup.cpp +++ b/src/k_powerup.cpp @@ -6,6 +6,8 @@ #include "k_objects.h" #include "k_powerup.h" #include "k_hud.h" // K_AddMessage +#include "p_mobj.h" +#include "s_sound.h" tic_t K_PowerUpRemaining(const player_t* player, kartitems_t powerup) { @@ -88,6 +90,16 @@ void K_GivePowerUp(player_t* player, kartitems_t powerup, tic_t time) } break; + case POWERUP_POINTS: + K_AddMessageForPlayer(player, "Got 6 POINTS!", true, false); + K_GivePointsToPlayer(player, nullptr, 6); + + if (!P_MobjWasRemoved(player->mo)) + { + S_StartSound(player->mo, sfx_token); + } + break; + default: break; } diff --git a/src/k_vote.c b/src/k_vote.c index 8213ca081..9993e6dd2 100644 --- a/src/k_vote.c +++ b/src/k_vote.c @@ -76,6 +76,8 @@ #define ARM_FRAMES (4) #define BULB_FRAMES (4) +#define SELECTOR_FRAMES (2) + #define CATCHER_SPEED (8*FRACUNIT) #define CATCHER_Y_OFFSET (48*FRACUNIT) #define CATCHER_OFFSCREEN (-CATCHER_Y_OFFSET * 2) @@ -91,6 +93,10 @@ #define SELECTION_SPACING_H (SELECTION_HEIGHT + SELECTION_SPACE) #define SELECTION_HOP (10*FRACUNIT) +#define SELECTOR_SPACE (8*FRACUNIT) +#define SELECTOR_Y ((SELECTION_HEIGHT / 2) + SELECTOR_SPACE) +#define SELECTOR_HEIGHT ((30*FRACUNIT) + SELECTOR_SPACE) + #define PILE_WIDTH (46*FRACUNIT) #define PILE_HEIGHT ((PILE_WIDTH * BASEVIDHEIGHT) / BASEVIDWIDTH) #define PILE_SPACE (4*FRACUNIT) @@ -146,6 +152,7 @@ typedef struct SINT8 selection; UINT8 delay; boolean sentTimeOutVote; + fixed_t x, destX; } y_vote_player; // Vote "pile" data. Objects for each vote scattered about. @@ -188,6 +195,13 @@ typedef struct fixed_t hop; } y_vote_draw_level; +// Voting selector drawing +typedef struct +{ + fixed_t x; + fixed_t destX; +} y_vote_draw_selector; + // General vote drawing typedef struct { @@ -206,6 +220,10 @@ typedef struct fixed_t selectTransition; y_vote_draw_level levels[VOTE_NUM_LEVELS]; + + patch_t *selector_arrow; + patch_t *selector_letter[MAXSPLITSCREENPLAYERS][2]; + y_vote_draw_selector selectors[MAXSPLITSCREENPLAYERS]; } y_vote_draw; static y_vote_data vote = {0}; @@ -241,6 +259,29 @@ boolean Y_PlayerIDCanVote(const UINT8 playerId) return true; } +static boolean Y_PlayerCanSelect(const UINT8 localId) +{ + const UINT8 p = g_localplayers[localId]; + + if (g_pickedVote != VOTE_NOT_PICKED) + { + return false; + } + + if (g_votes[p] != VOTE_NOT_PICKED) + { + return false; + } + + if (vote.players[localId].catcher.action != CATCHER_NA + || vote.players[localId].catcher.delay > 0) + { + return false; + } + + return Y_PlayerIDCanVote(p); +} + static void Y_SortPile(void) { UINT8 numVotes = 0; @@ -697,10 +738,61 @@ static void Y_DrawVoteBackground(void) bgTimer += renderdeltatics; } +static void Y_DrawVoteSelector(const fixed_t y, const fixed_t time, const UINT8 localPlayer) +{ + const fixed_t destX = SELECTION_X + (vote.players[localPlayer].selection * SELECTION_SPACING_W); + + vote_draw.selectors[localPlayer].x += FixedMul( + (destX - vote_draw.selectors[localPlayer].x) * 3 / 4, + renderdeltatics + ); + + if (Y_PlayerCanSelect(localPlayer) == false) + { + return; + } + + static const UINT8 freq = 7; + UINT8 *colormap = NULL; + + if (splitscreen > 0) + { + const UINT8 blink = ((time / freq / FRACUNIT) & 1); + + colormap = R_GetTranslationColormap(TC_RAINBOW, players[ g_localplayers[localPlayer] ].skincolor, GTC_CACHE); + + V_DrawFixedPatch( + vote_draw.selectors[localPlayer].x, y - SELECTOR_Y - (9*FRACUNIT), + FRACUNIT, 0, + vote_draw.selector_letter[localPlayer][blink], + colormap + ); + } + + fixed_t bob = FixedMul((time / freq * 2) + (FRACUNIT / 2), ANGLE_90); + if (localPlayer & 1) + { + bob = FCOS(bob); + } + else + { + bob = FSIN(bob); + } + + V_DrawFixedPatch( + vote_draw.selectors[localPlayer].x, y - SELECTOR_Y + bob, + FRACUNIT, 0, + vote_draw.selector_arrow, + colormap + ); +} + static void Y_DrawVoteSelection(fixed_t offset) { + static fixed_t selectorTimer = 0; + fixed_t x = SELECTION_X; - fixed_t y = SELECTION_Y + FixedMul(offset, SELECTION_HEIGHT * 2); + fixed_t y = SELECTION_Y + FixedMul(offset, (SELECTION_HEIGHT + SELECTOR_HEIGHT) * 2); INT32 i; // @@ -709,7 +801,6 @@ static void Y_DrawVoteSelection(fixed_t offset) for (i = 0; i < VOTE_NUM_LEVELS; i++) { boolean selected = false; - INT32 flags = 0; fixed_t destHop = 0; INT32 j; @@ -743,7 +834,7 @@ static void Y_DrawVoteSelection(fixed_t offset) Y_DrawVoteThumbnail( x, y - vote_draw.levels[i].hop, - SELECTION_WIDTH, flags, + SELECTION_WIDTH, 0, i, (selected == false), -1 ); @@ -758,6 +849,31 @@ static void Y_DrawVoteSelection(fixed_t offset) { Y_DrawCatcher(&vote.players[i].catcher); } + + if (offset != FRACUNIT) + { + // + // Draw splitscreen selectors + // + selectorTimer += renderdeltatics; + + //if (splitscreen > 0) + { + const UINT8 priority = vote.tic % (splitscreen + 1); + + for (i = 0; i <= splitscreen; i++) + { + if (i == priority) + { + continue; + } + + Y_DrawVoteSelector(y, selectorTimer, i); + } + + Y_DrawVoteSelector(y, selectorTimer, priority); + } + } } static void Y_DrawVotePile(void) @@ -1013,6 +1129,7 @@ static void Y_TickPlayerCatcher(const UINT8 localPlayer) { D_ModifyClientVote(g_localplayers[localPlayer], vote.players[localPlayer].selection); catcher->action = CATCHER_NA; + catcher->delay = 5; S_StopSoundByNum(sfx_kc37); } break; @@ -1191,28 +1308,6 @@ static void Y_TickVoteRoulette(void) } } -static boolean Y_PlayerCanSelect(const UINT8 localId) -{ - const UINT8 p = g_localplayers[localId]; - - if (g_pickedVote != VOTE_NOT_PICKED) - { - return false; - } - - if (g_votes[p] != VOTE_NOT_PICKED) - { - return false; - } - - if (vote.players[localId].catcher.action != CATCHER_NA) - { - return false; - } - - return Y_PlayerIDCanVote(p); -} - static void Y_TryMapAngerVote(void) { SINT8 angryMaps[VOTE_NUM_LEVELS] = { -1 }; @@ -1491,7 +1586,7 @@ void Y_VoteTicker(void) // static void Y_InitVoteDrawing(void) { - INT32 i = 0; + INT32 i = 0, j = 0; vote_draw.ruby_icon = W_CachePatchName("RUBYICON", PU_STATIC); @@ -1555,6 +1650,20 @@ static void Y_InitVoteDrawing(void) vote_draw.levels[i].str[sizeof vote_draw.levels[i].str - 1] = '\0'; } + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + { + y_vote_player *const player = &vote.players[i]; + + vote_draw.selectors[i].x = vote_draw.selectors[i].destX = SELECTION_X + (player->selection * SELECTION_SPACING_W); + + for (j = 0; j < SELECTOR_FRAMES; j++) + { + vote_draw.selector_letter[i][j] = W_CachePatchName(va("VSSPTR%c%d", 'A' + i, j + 1), PU_STATIC); + } + } + + vote_draw.selector_arrow = W_CachePatchName("VSSPTR1", PU_STATIC); + vote_draw.selectTransition = FRACUNIT; } @@ -1581,6 +1690,8 @@ void Y_StartVote(void) y_vote_player *const player = &vote.players[i]; y_vote_catcher *const catcher = &player->catcher; + player->selection = (i % VOTE_NUM_LEVELS); + catcher->action = CATCHER_NA; catcher->small = false; catcher->player = -1; @@ -1641,6 +1752,16 @@ static void Y_UnloadVoteData(void) UNLOAD(vote_draw.catcher_bulb[j][i]); } } + + for (j = 0; j < MAXSPLITSCREENPLAYERS; j++) + { + for (i = 0; i < SELECTOR_FRAMES; i++) + { + UNLOAD(vote_draw.selector_letter[j][i]); + } + } + + UNLOAD(vote_draw.selector_arrow); } // diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 2395614b0..93f79311a 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -1097,7 +1097,7 @@ static int lib_pPlayerZMovement(lua_State *L) static int lib_pAddPlayerScore(lua_State *L) { player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); - UINT32 amount = (UINT32)luaL_checkinteger(L, 2); + INT32 amount = (UINT32)luaL_checkinteger(L, 2); NOHUD INLEVEL if (!player) diff --git a/src/objects/monitor.c b/src/objects/monitor.c index 474a49795..0a0df05da 100644 --- a/src/objects/monitor.c +++ b/src/objects/monitor.c @@ -695,10 +695,12 @@ Obj_MonitorOnDeath K_ItemResultToType(result), K_ItemResultToAmount(result))); + drop->momz /= 2; // This is player-locked, so no need to throw it high + if (!P_MobjWasRemoved(source) && source->player) { P_SetTarget(&drop->tracer, source); - drop->extravalue1 = 6*TICRATE; + drop->extravalue1 = 5*TICRATE; drop->colorized = true; drop->color = source->player->skincolor; } diff --git a/src/objects/random-item.c b/src/objects/random-item.c index 550b98d73..3fcd87778 100644 --- a/src/objects/random-item.c +++ b/src/objects/random-item.c @@ -147,8 +147,10 @@ void Obj_RandomItemVisuals(mobj_t *mobj) boolean Obj_RandomItemSpawnIn(mobj_t *mobj) { + // We don't want item spawnpoints to be visible during + // POSITION in Battle. // battleprisons isn't set in time to do this on spawn. GROAN - if ((mobj->flags2 & MF2_BOSSFLEE) && (gametyperules & GTR_BUMPERS) && !battleprisons) + if ((mobj->flags2 & MF2_BOSSFLEE) && (gametyperules & (GTR_CIRCUIT|GTR_PAPERITEMS)) == GTR_PAPERITEMS && !battleprisons) mobj->renderflags |= RF_DONTDRAW; if ((leveltime == starttime) && !(gametyperules & GTR_CIRCUIT) && (mobj->flags2 & MF2_BOSSFLEE)) // here on map start? diff --git a/src/objects/versus/blendeye.c b/src/objects/versus/blendeye.c index e56f516c4..d64ca2009 100644 --- a/src/objects/versus/blendeye.c +++ b/src/objects/versus/blendeye.c @@ -1017,8 +1017,7 @@ void VS_BlendEye_Damage(mobj_t *mobj, mobj_t *inflictor, mobj_t *source, INT32 d if (source && source->player) { - P_AddPlayerScore(source->player, 1); - K_SpawnBattlePoints(source->player, NULL, 1); + K_GivePointsToPlayer(source->player, NULL, 1); } } diff --git a/src/p_inter.c b/src/p_inter.c index efbfc9ca2..3f9a80e8b 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1761,10 +1761,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget K_CheckBumpers(); - if (target->player->roundscore > 1) - target->player->roundscore -= 2; - else - target->player->roundscore = 0; + P_AddPlayerScore(target->player, -2); } target->player->trickpanel = TRICKSTATE_NONE; diff --git a/src/p_local.h b/src/p_local.h index 52699064b..b1d3416d4 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -146,7 +146,7 @@ extern consvar_t cv_tilting; extern fixed_t t_cam_dist[MAXSPLITSCREENPLAYERS], t_cam_height[MAXSPLITSCREENPLAYERS], t_cam_rotate[MAXSPLITSCREENPLAYERS]; -void P_AddPlayerScore(player_t *player, UINT32 amount); +void P_AddPlayerScore(player_t *player, INT32 amount); void P_ResetCamera(player_t *player, camera_t *thiscam); boolean P_TryCameraMove(fixed_t x, fixed_t y, camera_t *thiscam); void P_SlideCameraMove(camera_t *thiscam); diff --git a/src/p_mobj.c b/src/p_mobj.c index f29dc497b..15fd1522a 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -5862,6 +5862,29 @@ static void P_MobjSceneryThink(mobj_t *mobj) P_SpawnGhostMobj(mobj); P_SpawnGhostMobj(mobj->target); } + + // And because this needs to inherit player-lock renderflags, + // may as well do that logic in here too. Groooooooooss + boolean locked = true; + UINT32 lockflag = RF_TRANS40; + if (!mobj->target->extravalue1) + locked = false; + else if (mobj->target->extravalue1 < TICRATE && (mobj->target->extravalue1 % 2)) + locked = false; + + mobj->target->colorized = false; + mobj->target->renderflags &= ~lockflag; + mobj->renderflags &= ~lockflag; + if (locked) + { + mobj->target->colorized = true; + if ((r_splitscreen == 0) && !P_MobjWasRemoved(mobj->target->tracer) + && mobj->target->tracer->player && !P_IsDisplayPlayer(mobj->target->tracer->player)) + { + mobj->target->renderflags |= lockflag; + mobj->renderflags |= lockflag; + } + } break; default: @@ -6234,259 +6257,6 @@ static void P_MobjSceneryThink(mobj_t *mobj) mobj->renderflags ^= RF_DONTDRAW; } break; - - // see also K_drawKartItem in k_hud.c - case MT_PLAYERARROW: // FIXME: Delete this object, attach to name tags instead. - if (mobj->target && mobj->target->health - && mobj->target->player && !mobj->target->player->spectator - && mobj->target->health && mobj->target->player->playerstate != PST_DEAD - /*&& players[displayplayers[0]].mo && !players[displayplayers[0]].spectator*/) - { - fixed_t scale = 3*mobj->target->scale; - mobj->color = mobj->target->color; - K_MatchGenericExtraFlags(mobj, mobj->target); - - if (!(gametyperules & GTR_BUMPERS) -#if 1 // Set to 0 to test without needing to host - || (P_IsDisplayPlayer(mobj->target->player)) -#endif - ) - mobj->renderflags |= RF_DONTDRAW; - - P_UnsetThingPosition(mobj); - mobj->x = mobj->target->x; - mobj->y = mobj->target->y; - - mobj->angle = R_PointToAngle(mobj->x, mobj->y) + ANGLE_90; // literally only happened because i wanted to ^L^R the SPR_ITEM's - - if (!r_splitscreen && players[displayplayers[0]].mo) - { - scale = mobj->target->scale + FixedMul(FixedDiv(abs(P_AproxDistance(players[displayplayers[0]].mo->x-mobj->target->x, - players[displayplayers[0]].mo->y-mobj->target->y)), RING_DIST), mobj->target->scale); - if (scale > 16*mobj->target->scale) - scale = 16*mobj->target->scale; - } - mobj->destscale = scale; - - if (!(mobj->target->eflags & MFE_VERTICALFLIP)) - { - mobj->z = mobj->target->z + mobj->target->height + (16*mobj->target->scale); - mobj->eflags &= ~MFE_VERTICALFLIP; - } - else - { - mobj->z = mobj->target->z - mobj->target->height - (16*mobj->target->scale); - mobj->eflags |= MFE_VERTICALFLIP; - } - P_SetThingPosition(mobj); - - if (!mobj->tracer) - { - mobj_t *overlay = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_OVERLAY); - P_SetTarget(&mobj->tracer, overlay); - P_SetTarget(&mobj->tracer->target, mobj); - P_SetMobjState(mobj->tracer, S_PLAYERARROW_ITEM); - P_SetScale(mobj->tracer, (mobj->tracer->destscale = mobj->scale)); - } - - // Do this in an easy way - if (mobj->target->player->itemRoulette.active) - { - mobj->tracer->color = mobj->target->player->skincolor; - mobj->tracer->colorized = true; - } - else - { - mobj->tracer->color = SKINCOLOR_NONE; - mobj->tracer->colorized = false; - } - - if (!(mobj->renderflags & RF_DONTDRAW)) - { - //const INT32 numberdisplaymin = ((mobj->target->player->itemtype == KITEM_ORBINAUT) ? 5 : 2); - - // Set it to use the correct states for its condition - if (mobj->target->player->itemRoulette.active) - { - P_SetMobjState(mobj, S_PLAYERARROW_BOX); - mobj->tracer->sprite = SPR_ITEM; - mobj->tracer->frame = 1 | FF_FULLBRIGHT; - mobj->tracer->renderflags &= ~RF_DONTDRAW; - } - else if (mobj->target->player->stealingtimer < 0) - { - P_SetMobjState(mobj, S_PLAYERARROW_BOX); - mobj->tracer->sprite = SPR_ITEM; - mobj->tracer->frame = FF_FULLBRIGHT|KITEM_HYUDORO; - if (leveltime & 2) - mobj->tracer->renderflags &= ~RF_DONTDRAW; - else - mobj->tracer->renderflags |= RF_DONTDRAW; - } - else if ((mobj->target->player->stealingtimer > 0) && (leveltime & 2)) - { - P_SetMobjState(mobj, S_PLAYERARROW_BOX); - mobj->tracer->sprite = SPR_ITEM; - mobj->tracer->frame = FF_FULLBRIGHT|KITEM_HYUDORO; - mobj->tracer->renderflags &= ~RF_DONTDRAW; - } - else if (mobj->target->player->eggmanexplode > 1) - { - P_SetMobjState(mobj, S_PLAYERARROW_BOX); - mobj->tracer->sprite = SPR_ITEM; - mobj->tracer->frame = FF_FULLBRIGHT|KITEM_EGGMAN; - if (leveltime & 1) - mobj->tracer->renderflags &= ~RF_DONTDRAW; - else - mobj->tracer->renderflags |= RF_DONTDRAW; - } - else if (mobj->target->player->rocketsneakertimer > 1) - { - //itembar = mobj->target->player->rocketsneakertimer; -- not today satan - P_SetMobjState(mobj, S_PLAYERARROW_BOX); - mobj->tracer->sprite = SPR_ITEM; - mobj->tracer->frame = FF_FULLBRIGHT|KITEM_ROCKETSNEAKER; - if (leveltime & 1) - mobj->tracer->renderflags &= ~RF_DONTDRAW; - else - mobj->tracer->renderflags |= RF_DONTDRAW; - } - else if (mobj->target->player->itemtype && mobj->target->player->itemamount > 0) - { - P_SetMobjState(mobj, S_PLAYERARROW_BOX); - - switch (mobj->target->player->itemtype) - { - case KITEM_ORBINAUT: - mobj->tracer->sprite = SPR_ITMO; - mobj->tracer->frame = FF_FULLBRIGHT|K_GetOrbinautItemFrame(mobj->target->player->itemamount); - break; - case KITEM_INVINCIBILITY: - mobj->tracer->sprite = SPR_ITMI; - mobj->tracer->frame = FF_FULLBRIGHT|K_GetInvincibilityItemFrame(); - break; - case KITEM_SAD: - mobj->tracer->sprite = SPR_ITEM; - mobj->tracer->frame = FF_FULLBRIGHT; - break; - default: - mobj->tracer->sprite = SPR_ITEM; - mobj->tracer->frame = FF_FULLBRIGHT|(mobj->target->player->itemtype); - break; - } - - if (mobj->target->player->itemflags & IF_ITEMOUT) - { - if (leveltime & 1) - mobj->tracer->renderflags &= ~RF_DONTDRAW; - else - mobj->tracer->renderflags |= RF_DONTDRAW; - } - else - mobj->tracer->renderflags &= ~RF_DONTDRAW; - } - else - { - P_SetMobjState(mobj, S_PLAYERARROW); - P_SetMobjState(mobj->tracer, S_PLAYERARROW_ITEM); - } - - mobj->tracer->destscale = scale; - -#if 0 - if (mobj->target->player->itemamount >= numberdisplaymin - && mobj->target->player->itemamount <= 10) // Meh, too difficult to support greater than this; convert this to a decent HUD object and then maybe :V - { - mobj_t *number = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_OVERLAY); - mobj_t *numx = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_OVERLAY); - - P_SetTarget(&number->target, mobj); - P_SetMobjState(number, S_PLAYERARROW_NUMBER); - P_SetScale(number, mobj->scale); - number->destscale = scale; - number->frame = FF_FULLBRIGHT|(mobj->target->player->itemamount); - - P_SetTarget(&numx->target, mobj); - P_SetMobjState(numx, S_PLAYERARROW_X); - P_SetScale(numx, mobj->scale); - numx->destscale = scale; - } -#endif - -#if 0 - if (K_IsPlayerWanted(mobj->target->player) && mobj->movecount != 1) - { - mobj_t *wanted = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_PLAYERWANTED); - P_SetTarget(&wanted->target, mobj->target); - P_SetTarget(&wanted->tracer, mobj); - P_SetScale(wanted, mobj->scale); - wanted->destscale = scale; - mobj->movecount = 1; - } - else if (!K_IsPlayerWanted(mobj->target->player)) -#endif - mobj->movecount = 0; - } - else - mobj->tracer->renderflags |= RF_DONTDRAW; - } - else if (mobj->health > 0) - { - P_KillMobj(mobj, NULL, NULL, DMG_NORMAL); - return; - } - break; - case MT_PLAYERWANTED: - if (mobj->target && mobj->target->health && mobj->tracer - && mobj->target->player && !mobj->target->player->spectator - && mobj->target->health && mobj->target->player->playerstate != PST_DEAD - && players[g_localplayers[0]].mo && !players[g_localplayers[0]].spectator) - { - fixed_t scale = 3*mobj->target->scale; - - if (!K_IsPlayerWanted(mobj->target->player)) - { - mobj->tracer->movecount = 0; - P_RemoveMobj(mobj); - return; - } - - if (mobj->tracer->renderflags & RF_DONTDRAW) - mobj->renderflags |= RF_DONTDRAW; - else - mobj->renderflags &= ~RF_DONTDRAW; - - P_UnsetThingPosition(mobj); - mobj->x = mobj->target->x; - mobj->y = mobj->target->y; - - if (!r_splitscreen && players[displayplayers[0]].mo) - { - scale = mobj->target->scale + FixedMul(FixedDiv(abs(P_AproxDistance(players[displayplayers[0]].mo->x-mobj->target->x, - players[displayplayers[0]].mo->y-mobj->target->y)), RING_DIST), mobj->target->scale); - if (scale > 16*mobj->target->scale) - scale = 16*mobj->target->scale; - } - mobj->destscale = scale; - - if (!(mobj->target->eflags & MFE_VERTICALFLIP)) - { - mobj->z = mobj->target->z + mobj->target->height + (16*mobj->target->scale) + (64*scale); - mobj->eflags &= ~MFE_VERTICALFLIP; - } - else - { - mobj->z = mobj->target->z - mobj->target->height - (16*mobj->target->scale) - (64*scale); - mobj->eflags |= MFE_VERTICALFLIP; - } - P_SetThingPosition(mobj); - } - else if (mobj->health > 0) - { - P_KillMobj(mobj, NULL, NULL, DMG_NORMAL); - return; - } - break; case MT_ITEMCAPSULE_PART: P_ItemCapsulePartThinker(mobj); @@ -7363,14 +7133,12 @@ static boolean P_MobjRegularThink(mobj_t *mobj) K_UpdateMobjItemOverlay(mobj, mobj->threshold, mobj->movecount); + // ACHTUNG HACK - this is the player-lock timer used when breaking monitors, + // but the visual side-effects of this are in the MT_OVERLAY thinker so that + // the backdrop can also go transparent. EUUUAUUAUUAAAUUUUGGGHGHHHHHHHGSSS if (mobj->extravalue1 > 0) - { mobj->extravalue1--; - if (mobj->extravalue1 < TICRATE) - { - mobj->colorized = mobj->extravalue1 & 1; - } - } + break; } case MT_ITEMCAPSULE: @@ -12316,14 +12084,6 @@ void P_SpawnPlayer(INT32 playernum) K_InitWavedashIndicator(p); K_InitTrickIndicator(p); - if (gametyperules & GTR_ITEMARROWS) - { - mobj_t *overheadarrow = P_SpawnMobj(mobj->x, mobj->y, mobj->z + mobj->height + 16*FRACUNIT, MT_PLAYERARROW); - P_SetTarget(&overheadarrow->target, mobj); - overheadarrow->renderflags |= RF_DONTDRAW; - P_SetScale(overheadarrow, mobj->destscale); - } - if ((gametyperules & GTR_BUMPERS) && !p->spectator) { mobj->health = K_BumpersToHealth(K_StartingBumperCount()); diff --git a/src/p_user.c b/src/p_user.c index a7e9995c5..506d0fd9d 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -487,7 +487,7 @@ void P_GivePlayerLives(player_t *player, INT32 numlives) } // Adds to the player's score -void P_AddPlayerScore(player_t *player, UINT32 amount) +void P_AddPlayerScore(player_t *player, INT32 amount) { if (!((gametyperules & GTR_POINTLIMIT))) return; @@ -495,8 +495,11 @@ void P_AddPlayerScore(player_t *player, UINT32 amount) if (player->exiting) // srb2kart return; + // Don't underflow. // Don't go above MAXSCORE. - if (player->roundscore + amount < MAXSCORE) + if (amount < 0 && (UINT32)-amount > player->roundscore) + player->roundscore = 0; + else if (player->roundscore + amount < MAXSCORE) player->roundscore += amount; else player->roundscore = MAXSCORE; diff --git a/src/r_bsp.cpp b/src/r_bsp.cpp index e6a7c011a..736037dac 100644 --- a/src/r_bsp.cpp +++ b/src/r_bsp.cpp @@ -12,6 +12,7 @@ /// \brief BSP traversal, handling of LineSegs for rendering #include +#include #include @@ -31,7 +32,7 @@ #include "k_terrain.h" -extern "C" consvar_t cv_debugfinishline; +extern "C" consvar_t cv_debugfinishline, cv_debugrender_freezebsp; seg_t *curline; side_t *sidedef; @@ -53,6 +54,9 @@ INT32 doorclosed; // can block off the BSP across that seg. boolean g_walloffscreen; +static std::vector> node_cache; +static std::vector* current_node_cache; + boolean R_NoEncore(sector_t *sector, levelflat_t *flat, boolean ceiling) { const boolean invertEncore = (sector->flags & MSF_INVERTENCORE); @@ -1420,5 +1424,46 @@ void R_RenderBSPNode(INT32 bspnum) portalcullsector = NULL; } - R_Subsector(bspnum == -1 ? 0 : bspnum & ~NF_SUBSECTOR); + bspnum = (bspnum == -1 ? 0 : bspnum & ~NF_SUBSECTOR); + R_Subsector(bspnum); + + if (current_node_cache) + { + current_node_cache->push_back(bspnum); + } +} + +static bool render_cache(size_t cachenum) +{ + if (node_cache.empty() && !cv_debugrender_freezebsp.value) + { + current_node_cache = nullptr; + return false; + } + + if (!cv_debugrender_freezebsp.value) + { + // free cache + node_cache = {}; + current_node_cache = nullptr; + return false; + } + + if (node_cache.size() <= cachenum) + { + node_cache.resize(cachenum + 1); + current_node_cache = &node_cache[cachenum]; + return false; + } + + for (INT32 bspnum : node_cache[cachenum]) + R_Subsector(bspnum); + + return true; +} + +void R_RenderFirstBSPNode(size_t cachenum) +{ + if (!render_cache(cachenum)) + R_RenderBSPNode((INT32)numnodes - 1); } diff --git a/src/r_bsp.h b/src/r_bsp.h index 5002a4d8a..17407f5e6 100644 --- a/src/r_bsp.h +++ b/src/r_bsp.h @@ -42,6 +42,7 @@ void R_ClearClipSegs(void); void R_PortalClearClipSegs(INT32 start, INT32 end); void R_ClearDrawSegs(void); void R_RenderBSPNode(INT32 bspnum); +void R_RenderFirstBSPNode(size_t cachenum); // determines when a given sector shouldn't abide by the encoremap's palette. // no longer a static since this is used for encore in hw_main.c as well now: diff --git a/src/r_draw_column.cpp b/src/r_draw_column.cpp index fefbb9fbf..58aa7cf59 100644 --- a/src/r_draw_column.cpp +++ b/src/r_draw_column.cpp @@ -263,7 +263,19 @@ static void R_DrawColumnTemplate(drawcolumndata_t *dc) // -1 is the lower clamp bound because column posts have a "safe" byte before the real data // and a few bytes after as well - *dest = R_DrawColumnPixel(dc, dest, std::clamp(frac >> FRACBITS, npow2min, npow2max)); + //*dest = R_DrawColumnPixel(dc, dest, std::clamp(frac >> FRACBITS, npow2min, npow2max)); + { + // jartha: faster on my AMD FX-6300 CPU. + // Faster than ternaries, faster than std::min/std::max. Don't ask me why. + // I tested by viewing a non-PO2 texture from a consistent distance so it covered the entire screen. + // The framerate difference was about 50 frames at 640x400. + INT32 n = frac >> FRACBITS; + if (n < npow2min) + n = npow2min; + if (n > npow2max) + n = npow2max; + *dest = R_DrawColumnPixel(dc, dest, n); + } dest += vid.width; diff --git a/src/r_main.cpp b/src/r_main.cpp index 7277c2ace..365bd6674 100644 --- a/src/r_main.cpp +++ b/src/r_main.cpp @@ -55,6 +55,8 @@ INT64 mytotal = 0; #endif //profile stuff --------------------------------------------------------- +extern "C" consvar_t cv_debugrender_visplanes; + // Fineangles in the SCREENWIDTH wide window. #define FIELDOFVIEW 2048 @@ -1454,13 +1456,13 @@ static void Mask_Post (maskcount_t* m) // ================ // viewx, viewy, viewangle, all that good stuff must be set -static void R_RenderViewpoint(maskcount_t* mask) +static void R_RenderViewpoint(maskcount_t* mask, INT32 cachenum) { Mask_Pre(mask); curdrawsegs = ds_p; - R_RenderBSPNode((INT32)numnodes - 1); + R_RenderFirstBSPNode(cachenum); R_AddPrecipitationSprites(); Mask_Post(mask); @@ -1516,7 +1518,7 @@ void R_RenderPlayerView(void) srb2::ThreadPool::Sema tp_sema; srb2::g_main_threadpool->begin_sema(); - R_RenderViewpoint(&masks[nummasks - 1]); + R_RenderViewpoint(&masks[nummasks - 1], nummasks - 1); ps_bsptime = I_GetPreciseTime() - ps_bsptime; #ifdef TIMING @@ -1574,7 +1576,7 @@ void R_RenderPlayerView(void) // Render the BSP from the new viewpoint, and clip // any sprites with the new clipsegs and window. - R_RenderViewpoint(&masks[nummasks - 1]); + R_RenderViewpoint(&masks[nummasks - 1], nummasks - 1); portalskipprecipmobjs = false; @@ -1603,6 +1605,58 @@ void R_RenderPlayerView(void) R_DrawMasked(masks, nummasks); ps_sw_maskedtime = I_GetPreciseTime() - ps_sw_maskedtime; + if (cv_debugrender_visplanes.value) + { + for (INT32 i = 0; i < MAXVISPLANES; i++) + { + for (visplane_t* pl = visplanes[i]; pl; pl = pl->next) + { + if (pl->minx > pl->maxx) + continue; + auto col = [](int x, int top, int bot) + { + if (top > bot) + std::swap(top, bot); + UINT8* p = &screens[0][x + top * vid.width]; + while (top <= bot) + { + *p = 35; + p += vid.width; + top++; + } + }; + auto span = [col](int x, int top, int bot) + { + if (top <= bot) + col(x, top, bot); + }; + INT32 top = pl->top[pl->minx]; + INT32 bottom = pl->bottom[pl->minx]; + span(pl->minx, top, bottom); + span(pl->maxx, pl->top[pl->maxx], pl->bottom[pl->maxx]); + for (INT32 x = pl->minx + 1; x < pl->maxx; ++x) + { + INT32 new_top = pl->top[x]; + INT32 new_bottom = pl->bottom[x]; + if (new_top > new_bottom) + continue; + if (top > bottom) + { + col(x, new_top, new_top); + col(x, new_bottom, new_bottom); + } + else + { + col(x, top, new_top); + col(x, bottom, new_bottom); + } + top = new_top; + bottom = new_bottom; + } + } + } + } + // debugrender_portal: fill portals with red, draw over everything if (portal_base && cv_debugrender_portal.value) { diff --git a/src/v_draw.cpp b/src/v_draw.cpp index 6a72639f3..02eb317cc 100644 --- a/src/v_draw.cpp +++ b/src/v_draw.cpp @@ -251,18 +251,23 @@ void Chain::sticker(patch_t* end_graphic, UINT8 color) const Chain::Clipper::Clipper(const Chain& chain) { - V_SetClipRect( - FloatToFixed(chain.clipx1_), - FloatToFixed(chain.clipy1_), - FloatToFixed(chain.clipx2_ - chain.clipx1_), - FloatToFixed(chain.clipy2_ - chain.clipy1_), - chain.flags_ - ); + V_SaveClipRect(&save_); + + if (chain.clip_) + { + V_SetClipRect( + FloatToFixed(chain.clipx1_), + FloatToFixed(chain.clipy1_), + FloatToFixed(chain.clipx2_ - chain.clipx1_), + FloatToFixed(chain.clipy2_ - chain.clipy1_), + chain.flags_ + ); + } } Chain::Clipper::~Clipper() { - V_ClearClipRect(); + V_RestoreClipRect(&save_); } patch_t* Draw::cache_patch(const char* name) diff --git a/src/v_draw.hpp b/src/v_draw.hpp index 9a9459d2e..b7ec5e1d9 100644 --- a/src/v_draw.hpp +++ b/src/v_draw.hpp @@ -154,10 +154,16 @@ public: Chain& clipx() { return clipx(x_, x_ + width_); } Chain& clipy() { return clipy(y_, y_ + height_); } + // True to use internal clipping state + // False to use global state (default) + // Changing the clipping dimensions implicitly sets + // this to true + Chain& clip(bool yes); + Chain& colormap(const UINT8* colormap); - Chain& colormap(skincolornum_t color); - Chain& colormap(INT32 skin, skincolornum_t color); - Chain& colorize(skincolornum_t color); + Chain& colormap(UINT16 color); + Chain& colormap(INT32 skin, UINT16 color); + Chain& colorize(UINT16 color); void text(const char* str) const { string(str, flags_, font_); } void text(const std::string& str) const { text(str.c_str()); } @@ -195,6 +201,9 @@ public: { explicit Clipper(const Chain& chain); ~Clipper(); + + private: + cliprect_t save_; }; float x_ = 0.f; @@ -207,6 +216,7 @@ public: float clipx2_ = BASEVIDWIDTH; float clipy1_ = 0.f; float clipy2_ = BASEVIDHEIGHT; + bool clip_ = false; INT32 flags_ = 0; @@ -251,6 +261,7 @@ public: METHOD(stretch); METHOD(clipx); METHOD(clipy); + METHOD(clip); METHOD(colormap); METHOD(colorize); diff --git a/src/v_draw_setter.hpp b/src/v_draw_setter.hpp index 11b0b3e96..3a21e691a 100644 --- a/src/v_draw_setter.hpp +++ b/src/v_draw_setter.hpp @@ -87,6 +87,7 @@ inline Draw::Chain& Draw::Chain::clipx(float left, float right) { clipx1_ = left; clipx2_ = right; + clip_ = true; return *this; } @@ -94,6 +95,13 @@ inline Draw::Chain& Draw::Chain::clipy(float top, float bottom) { clipy1_ = top; clipy2_ = bottom; + clip_ = true; + return *this; +} + +inline Draw::Chain& Draw::Chain::clip(bool yes) +{ + clip_ = yes; return *this; } @@ -103,19 +111,19 @@ inline Draw::Chain& Draw::Chain::colormap(const UINT8* colormap) return *this; } -inline Draw::Chain& Draw::Chain::colormap(skincolornum_t color) +inline Draw::Chain& Draw::Chain::colormap(UINT16 color) { - return colormap(R_GetTranslationColormap(TC_DEFAULT, color, GTC_CACHE)); + return colormap(R_GetTranslationColormap(TC_DEFAULT, static_cast(color), GTC_CACHE)); } -inline Draw::Chain& Draw::Chain::colormap(INT32 skin, skincolornum_t color) +inline Draw::Chain& Draw::Chain::colormap(INT32 skin, UINT16 color) { - return colormap(R_GetTranslationColormap(skin, color, GTC_CACHE)); + return colormap(R_GetTranslationColormap(skin, static_cast(color), GTC_CACHE)); } -inline Draw::Chain& Draw::Chain::colorize(skincolornum_t color) +inline Draw::Chain& Draw::Chain::colorize(UINT16 color) { - return colormap(R_GetTranslationColormap(TC_RAINBOW, color, GTC_CACHE)); + return colormap(R_GetTranslationColormap(TC_RAINBOW, static_cast(color), GTC_CACHE)); } }; // namespace srb2 diff --git a/src/v_video.cpp b/src/v_video.cpp index dd2ee68e9..64fa37169 100644 --- a/src/v_video.cpp +++ b/src/v_video.cpp @@ -715,6 +715,16 @@ void V_ClearClipRect(void) cliprect.enabled = false; } +void V_SaveClipRect(cliprect_t *copy) +{ + *copy = cliprect; +} + +void V_RestoreClipRect(const cliprect_t *copy) +{ + cliprect = *copy; +} + static UINT8 hudplusalpha[11] = { 10, 8, 6, 4, 2, 0, 0, 0, 0, 0, 0}; static UINT8 hudminusalpha[11] = { 10, 9, 9, 8, 8, 7, 7, 6, 6, 5, 5}; @@ -1015,6 +1025,10 @@ void V_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c) } #endif + UINT32 alphalevel; + if ((alphalevel = V_GetAlphaLevel(c)) >= 10) + return; + if (!(c & V_NOSCALESTART)) { INT32 dupx = vid.dupx, dupy = vid.dupy; @@ -1081,7 +1095,7 @@ void V_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c) g_2d.begin_quad() .patch(nullptr) - .color(r / 255.f, g / 255.f, b / 255.f, 1.f) + .color(r / 255.f, g / 255.f, b / 255.f, (10 - alphalevel) / 10.f) .rect(x, y, w, h) .done(); } diff --git a/src/v_video.h b/src/v_video.h index e1dc023c9..34f4c40ff 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -192,6 +192,8 @@ struct cliprect_t const cliprect_t *V_GetClipRect(void); void V_SetClipRect(fixed_t x, fixed_t y, fixed_t w, fixed_t h, INT32 flags); void V_ClearClipRect(void); +void V_SaveClipRect(cliprect_t *copy); +void V_RestoreClipRect(const cliprect_t *copy); // defines for old functions #define V_DrawPatch(x,y,s,p) V_DrawFixedPatch((x)<