K_drawKartMinimap: Add progression meter variation

- Uses a lot of existing apparatus, just leveraged to draw horizontally instead of a full map.
- Currently only functional in Sealed Star contexts, due to K_GetCircuitLength having no clean alternative for sprint tracks.
    - Tracks Catcher/Emerald.
- K_drawKartProgressionMinimapIcon and K_getKartProgressionMinimapDistance
    - Abstracts the progress of converting distance to finish into a position on the bar.
    - Sibling function of K_drawKartMinimapIcon
        - The author of this commit wanted to make it an alternate outcome of this function, which it shares a lot of similarity with, but doing waypoint traversal in HUD for objects that don't independently track their distance would absolutely tank performance.
        - Tidying up in the main function permitted less minimap state to be used in these.
This commit is contained in:
toaster 2023-04-07 00:59:40 +01:00
parent e82082fd06
commit 7abb2ce162
3 changed files with 214 additions and 54 deletions

View file

@ -13,6 +13,8 @@
#include "k_kart.h"
#include "k_battle.h"
#include "k_grandprix.h"
#include "k_specialstage.h"
#include "k_objects.h"
#include "k_boss.h"
#include "k_color.h"
#include "k_director.h"
@ -78,6 +80,8 @@ patch_t *kp_facehighlight[8];
static patch_t *kp_nocontestminimap;
static patch_t *kp_spbminimap;
static patch_t *kp_wouldyoustillcatchmeifiwereaworm;
static patch_t *kp_catcherminimap;
static patch_t *kp_emeraldminimap;
static patch_t *kp_capsuleminimap[3];
static patch_t *kp_ringsticker[2];
@ -353,6 +357,8 @@ void K_LoadKartHUDGraphics(void)
HU_UpdatePatch(&kp_spbminimap, "SPBMMAP");
HU_UpdatePatch(&kp_wouldyoustillcatchmeifiwereaworm, "MINIPROG");
HU_UpdatePatch(&kp_catcherminimap, "UFOMAP");
HU_UpdatePatch(&kp_emeraldminimap, "EMEMAP");
HU_UpdatePatch(&kp_capsuleminimap[0], "MINICAP1");
HU_UpdatePatch(&kp_capsuleminimap[1], "MINICAP2");
@ -3567,6 +3573,47 @@ static void K_drawKartNameTags(void)
}
}
#define PROGRESSION_BAR_WIDTH 120
static INT32 K_getKartProgressionMinimapDistance(UINT32 distancetofinish)
{
INT32 dist;
if (specialstageinfo.maxDist == 0U)
{
return 0;
}
dist = specialstageinfo.maxDist/PROGRESSION_BAR_WIDTH;
dist = (specialstageinfo.maxDist-distancetofinish)/dist;
if (dist > PROGRESSION_BAR_WIDTH)
{
return PROGRESSION_BAR_WIDTH;
}
if (dist < 0)
{
return 0;
}
return dist;
}
static void K_drawKartProgressionMinimapIcon(UINT32 distancetofinish, INT32 hudx, INT32 hudy, INT32 flags, patch_t *icon, UINT8 *colormap)
{
if (distancetofinish == UINT32_MAX)
return;
hudx += K_getKartProgressionMinimapDistance(distancetofinish);
hudx = ((hudx - (SHORT(icon->width)/2))<<FRACBITS);
hudy = ((hudy - (SHORT(icon->height)/2))<<FRACBITS);
V_DrawFixedPatch(hudx, hudy, FRACUNIT, flags, icon, colormap);
}
static void K_drawKartMinimapIcon(fixed_t objx, fixed_t objy, INT32 hudx, INT32 hudy, INT32 flags, patch_t *icon, UINT8 *colormap)
{
// amnum xpos & ypos are the icon's speed around the HUD.
@ -3585,8 +3632,8 @@ static void K_drawKartMinimapIcon(fixed_t objx, fixed_t objy, INT32 hudx, INT32
if (encoremode)
amnumxpos = -amnumxpos;
amxpos = amnumxpos + ((hudx + (SHORT(minimapinfo.minimap_pic->width)-SHORT(icon->width))/2)<<FRACBITS);
amypos = amnumypos + ((hudy + (SHORT(minimapinfo.minimap_pic->height)-SHORT(icon->height))/2)<<FRACBITS);
amxpos = amnumxpos + ((hudx - (SHORT(icon->width))/2)<<FRACBITS);
amypos = amnumypos + ((hudy - (SHORT(icon->height))/2)<<FRACBITS);
V_DrawFixedPatch(amxpos, amypos, FRACUNIT, flags, icon, colormap);
}
@ -3602,8 +3649,8 @@ static void K_drawKartMinimapDot(fixed_t objx, fixed_t objy, INT32 hudx, INT32 h
if (encoremode)
amnumxpos = -amnumxpos;
amxpos = (amnumxpos / FRACUNIT) + (SHORT(minimapinfo.minimap_pic->width) / 2);
amypos = (amnumypos / FRACUNIT) + (SHORT(minimapinfo.minimap_pic->height) / 2);
amxpos = (amnumxpos / FRACUNIT);
amypos = (amnumypos / FRACUNIT);
if (flags & V_NOSCALESTART)
{
@ -3649,17 +3696,26 @@ static void K_drawKartMinimapWaypoint(waypoint_t *wp, INT32 hudx, INT32 hudy, IN
static void K_drawKartMinimap(void)
{
patch_t *workingPic;
INT32 i = 0;
INT32 x, y;
INT32 minimaptrans = 4;
INT32 splitflags = 0;
UINT8 skin = 0;
UINT8 *colormap = NULL;
SINT8 localplayers[MAXSPLITSCREENPLAYERS];
SINT8 numlocalplayers = 0;
mobj_t *mobj, *next; // for SPB drawing (or any other item(s) we may wanna draw, I dunno!)
fixed_t interpx, interpy;
boolean doprogressionbar = false;
boolean dofade = false, doencore = false;
// Draw the HUD only when playing in a level.
// hu_stuff needs this, unlike st_stuff.
if (gamestate != GS_LEVEL)
@ -3670,16 +3726,58 @@ static void K_drawKartMinimap(void)
if (stplyr != &players[displayplayers[0]])
return;
if (minimapinfo.minimap_pic == NULL)
if (specialstageinfo.valid == true)
{
return; // no pic, just get outta here
// future work: maybe make this a unique gametype rule?
// I would do this now if it were easier to get the
// distancetofinish for an arbitrary object. ~toast 070423
doprogressionbar = true;
}
if (r_splitscreen < 2) // 1/2P right aligned
if (doprogressionbar == false)
{
splitflags = (V_SLIDEIN|V_SNAPTORIGHT);
if (minimapinfo.minimap_pic == NULL)
{
return; // no pic, just get outta here
}
else if (r_splitscreen < 2) // 1/2P right aligned
{
splitflags = (V_SLIDEIN|V_SNAPTORIGHT);
}
else if (r_splitscreen == 3) // 4P splits
{
dofade = true;
}
// 3P lives in the middle of the bottom right
// viewport and shouldn't fade in OR slide
x = MINI_X;
y = MINI_Y;
workingPic = minimapinfo.minimap_pic;
doencore = encoremode;
}
else if (r_splitscreen == 3) // 4P centered
else
{
x = BASEVIDWIDTH/2;
if (r_splitscreen > 0)
{
y = BASEVIDHEIGHT/2;
dofade = true;
}
else
{
y = 180;
splitflags = (V_SLIDEIN|V_SNAPTOBOTTOM);
}
workingPic = kp_wouldyoustillcatchmeifiwereaworm;
}
if (dofade)
{
const tic_t length = TICRATE/2;
@ -3687,34 +3785,49 @@ static void K_drawKartMinimap(void)
return;
if (lt_exitticker < length)
minimaptrans = (((INT32)lt_exitticker)*minimaptrans)/((INT32)length);
if (!minimaptrans)
return;
}
// 3P lives in the middle of the bottom right player and shouldn't fade in OR slide
if (!minimaptrans)
return;
x = MINI_X - (SHORT(minimapinfo.minimap_pic->width)/2);
y = MINI_Y - (SHORT(minimapinfo.minimap_pic->height)/2);
minimaptrans = ((10-minimaptrans)<<FF_TRANSSHIFT);
if (encoremode)
V_DrawScaledPatch(x+SHORT(minimapinfo.minimap_pic->width), y, splitflags|minimaptrans|V_FLIP, minimapinfo.minimap_pic);
if (doencore)
{
V_DrawScaledPatch(
x + (SHORT(workingPic->width)/2),
y - (SHORT(workingPic->height)/2),
splitflags|minimaptrans|V_FLIP,
workingPic
);
}
else
V_DrawScaledPatch(x, y, splitflags|minimaptrans, minimapinfo.minimap_pic);
{
V_DrawScaledPatch(
x - (SHORT(workingPic->width)/2),
y - (SHORT(workingPic->height)/2),
splitflags|minimaptrans,
workingPic
);
}
// most icons will be rendered semi-ghostly.
splitflags |= V_HUDTRANSHALF;
// let offsets transfer to the heads, too!
if (encoremode)
x += SHORT(minimapinfo.minimap_pic->leftoffset);
if (doencore)
x += SHORT(workingPic->leftoffset);
else
x -= SHORT(minimapinfo.minimap_pic->leftoffset);
y -= SHORT(minimapinfo.minimap_pic->topoffset);
x -= SHORT(workingPic->leftoffset);
y -= SHORT(workingPic->topoffset);
if (doprogressionbar == true)
{
x -= PROGRESSION_BAR_WIDTH/2;
}
// Draw the super item in Battle
if ((gametyperules & GTR_OVERTIME) && battleovertime.enabled)
if (doprogressionbar == false && (gametyperules & GTR_OVERTIME) && battleovertime.enabled)
{
if (battleovertime.enabled >= 10*TICRATE || (battleovertime.enabled & 1))
{
@ -3732,7 +3845,7 @@ static void K_drawKartMinimap(void)
localplayers[i] = -1;
// Player's tiny icons on the Automap. (drawn opposite direction so player 1 is drawn last in splitscreen)
if (ghosts)
if (ghosts && doprogressionbar == true) // future work: show ghosts on progression bar
{
demoghost *g = ghosts;
while (g)
@ -3757,19 +3870,15 @@ static void K_drawKartMinimap(void)
K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, faceprefix[skin][FACE_MINIMAP], colormap);
g = g->next;
}
if (!stplyr->mo || stplyr->spectator || stplyr->exiting)
return;
localplayers[numlocalplayers++] = stplyr-players;
}
else
{
for (i = MAXPLAYERS-1; i >= 0; i--)
{
if (!playeringame[i])
continue;
if (!players[i].mo || players[i].spectator || !players[i].mo->skin || players[i].exiting)
if (!players[i].mo || players[i].spectator || !players[i].mo->skin
|| (doprogressionbar == false && players[i].exiting))
continue;
// This player is out of the game!
@ -3818,22 +3927,33 @@ static void K_drawKartMinimap(void)
colormap = NULL;
}
interpx = R_InterpolateFixed(mobj->old_x, mobj->x);
interpy = R_InterpolateFixed(mobj->old_y, mobj->y);
K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, workingPic, colormap);
// Target reticule
if (((gametyperules & GTR_CIRCUIT) && players[i].position == spbplace)
|| ((gametyperules & (GTR_BOSS|GTR_POINTLIMIT)) == GTR_POINTLIMIT && K_IsPlayerWanted(&players[i])))
if (doprogressionbar == false)
{
K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, kp_wantedreticle, NULL);
interpx = R_InterpolateFixed(mobj->old_x, mobj->x);
interpy = R_InterpolateFixed(mobj->old_y, mobj->y);
K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, workingPic, colormap);
// Target reticule
if (((gametyperules & GTR_CIRCUIT) && players[i].position == spbplace)
|| ((gametyperules & (GTR_BOSS|GTR_POINTLIMIT)) == GTR_POINTLIMIT && K_IsPlayerWanted(&players[i])))
{
K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, kp_wantedreticle, NULL);
}
}
else
{
K_drawKartProgressionMinimapIcon(players[i].distancetofinish, x, y, splitflags, workingPic, colormap);
}
}
}
// draw minimap-pertinent objects
for (mobj = trackercap; mobj; mobj = next)
if (doprogressionbar == true)
{
// future work: support these specific objects on this
}
else for (mobj = trackercap; mobj; mobj = next)
{
next = mobj->itnext;
@ -3887,7 +4007,34 @@ static void K_drawKartMinimap(void)
}
// ...but first, any boss targets.
if (bossinfo.valid == true)
if (doprogressionbar == true)
{
if (specialstageinfo.valid == true)
{
UINT32 distancetofinish = K_GetSpecialUFODistance();
if (distancetofinish > 0 && specialstageinfo.ufo != NULL && P_MobjWasRemoved(specialstageinfo.ufo) == false)
{
colormap = NULL;
if (specialstageinfo.ufo->health > 1)
{
workingPic = kp_catcherminimap;
}
else
{
workingPic = kp_emeraldminimap;
if (specialstageinfo.ufo->color)
{
colormap = R_GetTranslationColormap(TC_DEFAULT, specialstageinfo.ufo->color, GTC_CACHE);
}
}
K_drawKartProgressionMinimapIcon(distancetofinish, x, y, splitflags, workingPic, colormap);
}
}
// future work: support boss minimap icons on the progression bar
}
else if (bossinfo.valid == true)
{
for (i = 0; i < NUMWEAKSPOTS; i++)
{
@ -3949,20 +4096,27 @@ static void K_drawKartMinimap(void)
colormap = NULL;
}
interpx = R_InterpolateFixed(mobj->old_x, mobj->x);
interpy = R_InterpolateFixed(mobj->old_y, mobj->y);
K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, workingPic, colormap);
// Target reticule
if (((gametyperules & GTR_CIRCUIT) && players[localplayers[i]].position == spbplace)
|| ((gametyperules & (GTR_BOSS|GTR_POINTLIMIT)) == GTR_POINTLIMIT && K_IsPlayerWanted(&players[localplayers[i]])))
if (doprogressionbar == false)
{
K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, kp_wantedreticle, NULL);
interpx = R_InterpolateFixed(mobj->old_x, mobj->x);
interpy = R_InterpolateFixed(mobj->old_y, mobj->y);
K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, workingPic, colormap);
// Target reticule
if (((gametyperules & GTR_CIRCUIT) && players[localplayers[i]].position == spbplace)
|| ((gametyperules & (GTR_BOSS|GTR_POINTLIMIT)) == GTR_POINTLIMIT && K_IsPlayerWanted(&players[localplayers[i]])))
{
K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, kp_wantedreticle, NULL);
}
}
else
{
K_drawKartProgressionMinimapIcon(players[i].distancetofinish, x, y, splitflags, workingPic, colormap);
}
}
if (cv_kartdebugwaypoints.value != 0)
if (doprogressionbar == false && cv_kartdebugwaypoints.value != 0)
{
size_t idx;
@ -3983,6 +4137,8 @@ static void K_drawKartMinimap(void)
}
}
#undef PROGRESSION_BAR_WIDTH
static void K_drawKartFinish(boolean finish)
{
INT32 timer, minsplitstationary, pnum = 0, splitflags = V_SPLITSCREEN;

View file

@ -24,8 +24,10 @@ extern struct specialstageinfo
{
boolean valid; ///< If true, then data in this struct is valid
UINT32 beamDist; ///< Where the exit beam is.
mobj_t *ufo; ///< The Chaos Emerald capsule.
UINT32 maxDist; ///< The distance from one end of the track to another.
UINT32 beamDist; ///< Where the exit beam is.
} specialstageinfo;
/*--------------------------------------------------

View file

@ -345,6 +345,7 @@ static void UFOMove(mobj_t *ufo)
ufo->momx = 0;
ufo->momy = 0;
ufo->momz = ufo_speed(ufo);
ufo_distancetofinish(ufo) = 0;
return;
}
@ -901,6 +902,7 @@ static mobj_t *InitSpecialUFO(waypoint_t *start)
ufo = P_SpawnMobj(start->mobj->x, start->mobj->y, start->mobj->z, MT_SPECIAL_UFO);
ufo_waypoint(ufo) = (INT32)K_GetWaypointHeapIndex(start);
UFOUpdateDistanceToFinish(ufo);
specialstageinfo.maxDist = ufo_distancetofinish(ufo);
}
ufo_speed(ufo) = FixedMul(UFO_START_SPEED, K_GetKartGameSpeedScalar(gamespeed));