mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
Fixes objective message button prompts in tutorials having EXTREMELY weird delays compared to dialogue boxes
6453 lines
159 KiB
C++
6453 lines
159 KiB
C++
// DR. ROBOTNIK'S RING RACERS
|
|
//-----------------------------------------------------------------------------
|
|
// Copyright (C) 2024 by Kart Krew.
|
|
//
|
|
// This program is free software distributed under the
|
|
// terms of the GNU General Public License, version 2.
|
|
// See the 'LICENSE' file for more details.
|
|
//-----------------------------------------------------------------------------
|
|
/// \file k_hud.c
|
|
/// \brief HUD drawing functions exclusive to Kart
|
|
|
|
#include <algorithm>
|
|
#include <array>
|
|
#include <vector>
|
|
#include <deque>
|
|
|
|
#include "v_draw.hpp"
|
|
|
|
#include "k_hud.h"
|
|
#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"
|
|
#include "screen.h"
|
|
#include "doomtype.h"
|
|
#include "doomdef.h"
|
|
#include "hu_stuff.h"
|
|
#include "d_netcmd.h"
|
|
#include "v_video.h"
|
|
#include "r_draw.h"
|
|
#include "st_stuff.h"
|
|
#include "lua_hud.h"
|
|
#include "doomstat.h"
|
|
#include "d_clisrv.h"
|
|
#include "g_game.h"
|
|
#include "p_local.h"
|
|
#include "z_zone.h"
|
|
#include "m_cond.h"
|
|
#include "r_main.h"
|
|
#include "s_sound.h"
|
|
#include "r_things.h"
|
|
#include "r_fps.h"
|
|
#include "m_random.h"
|
|
#include "k_roulette.h"
|
|
#include "k_bot.h"
|
|
#include "k_rank.h"
|
|
#include "g_party.h"
|
|
#include "k_hitlag.h"
|
|
#include "g_input.h"
|
|
#include "k_dialogue.h"
|
|
#include "f_finale.h"
|
|
#include "m_easing.h"
|
|
|
|
//{ Patch Definitions
|
|
static patch_t *kp_nodraw;
|
|
|
|
static patch_t *kp_timesticker;
|
|
static patch_t *kp_timestickerwide;
|
|
static patch_t *kp_lapsticker;
|
|
static patch_t *kp_lapstickerwide;
|
|
static patch_t *kp_lapstickernarrow;
|
|
static patch_t *kp_splitlapflag;
|
|
static patch_t *kp_bumpersticker;
|
|
static patch_t *kp_bumperstickerwide;
|
|
static patch_t *kp_capsulesticker;
|
|
static patch_t *kp_capsulestickerwide;
|
|
static patch_t *kp_karmasticker;
|
|
static patch_t *kp_spheresticker;
|
|
static patch_t *kp_splitspheresticker;
|
|
static patch_t *kp_splitkarmabomb;
|
|
static patch_t *kp_timeoutsticker;
|
|
|
|
static patch_t *kp_prestartbulb[15];
|
|
static patch_t *kp_prestartletters[7];
|
|
|
|
static patch_t *kp_prestartbulb_split[15];
|
|
static patch_t *kp_prestartletters_split[7];
|
|
|
|
static patch_t *kp_startcountdown[20];
|
|
static patch_t *kp_racefault[6];
|
|
static patch_t *kp_racefinish[6];
|
|
|
|
static patch_t *kp_positionnum[10][2][2]; // number, overlay or underlay, splitscreen
|
|
|
|
patch_t *kp_facenum[MAXPLAYERS+1];
|
|
static patch_t *kp_facehighlight[8];
|
|
|
|
static patch_t *kp_nocontestminimap;
|
|
patch_t *kp_unknownminimap;
|
|
static patch_t *kp_spbminimap;
|
|
static patch_t *kp_wouldyoustillcatchmeifiwereaworm;
|
|
static patch_t *kp_catcherminimap;
|
|
static patch_t *kp_emeraldminimap[2];
|
|
static patch_t *kp_capsuleminimap[3];
|
|
static patch_t *kp_battleufominimap;
|
|
static patch_t *kp_superflickyminimap;
|
|
|
|
static patch_t *kp_ringsticker[2];
|
|
static patch_t *kp_ringstickersplit[4];
|
|
static patch_t *kp_ring[6];
|
|
static patch_t *kp_smallring[6];
|
|
static patch_t *kp_ringdebtminus;
|
|
static patch_t *kp_ringdebtminussmall;
|
|
static patch_t *kp_ringspblock[16];
|
|
static patch_t *kp_ringspblocksmall[16];
|
|
|
|
static patch_t *kp_speedometersticker;
|
|
static patch_t *kp_speedometerlabel[4];
|
|
|
|
static patch_t *kp_rankbumper;
|
|
static patch_t *kp_bigbumper;
|
|
static patch_t *kp_tinybumper[2];
|
|
static patch_t *kp_ranknobumpers;
|
|
static patch_t *kp_rankcapsule;
|
|
static patch_t *kp_rankemerald;
|
|
static patch_t *kp_rankemeraldflash;
|
|
static patch_t *kp_rankemeraldback;
|
|
static patch_t *kp_pts[2];
|
|
|
|
static patch_t *kp_goal[2][2]; // [skull][4p]
|
|
static patch_t *kp_goalrod[2]; // [4p]
|
|
static patch_t *kp_goaltext1p;
|
|
|
|
static patch_t *kp_battlewin;
|
|
static patch_t *kp_battlecool;
|
|
static patch_t *kp_battlelose;
|
|
static patch_t *kp_battlewait;
|
|
static patch_t *kp_battleinfo;
|
|
static patch_t *kp_wanted;
|
|
static patch_t *kp_wantedsplit;
|
|
static patch_t *kp_wantedreticle;
|
|
static patch_t *kp_minimapdot;
|
|
|
|
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[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];
|
|
static patch_t *kp_slotring[2];
|
|
static patch_t *kp_seven[2];
|
|
static patch_t *kp_jackpot[2];
|
|
|
|
static patch_t *kp_check[6];
|
|
|
|
static patch_t *kp_rival[2];
|
|
static patch_t *kp_localtag[4][2];
|
|
|
|
static patch_t *kp_talk;
|
|
static patch_t *kp_typdot;
|
|
|
|
patch_t *kp_eggnum[6];
|
|
|
|
static patch_t *kp_flameshieldmeter[FLAMESHIELD_MAX][2];
|
|
static patch_t *kp_flameshieldmeter_bg[FLAMESHIELD_MAX][2];
|
|
|
|
static patch_t *kp_fpview[3];
|
|
static patch_t *kp_inputwheel[5];
|
|
|
|
static patch_t *kp_challenger[25];
|
|
|
|
static patch_t *kp_lapanim_lap[7];
|
|
static patch_t *kp_lapanim_final[11];
|
|
static patch_t *kp_lapanim_number[10][3];
|
|
static patch_t *kp_lapanim_emblem[2];
|
|
static patch_t *kp_lapanim_hand[3];
|
|
|
|
static patch_t *kp_yougotem;
|
|
static patch_t *kp_itemminimap;
|
|
|
|
static patch_t *kp_alagles[10];
|
|
static patch_t *kp_blagles[6];
|
|
|
|
static patch_t *kp_cpu;
|
|
|
|
static patch_t *kp_nametagstem;
|
|
|
|
static patch_t *kp_bossbar[8];
|
|
static patch_t *kp_bossret[4];
|
|
|
|
static patch_t *kp_trickcool[2];
|
|
|
|
patch_t *kp_autoroulette;
|
|
|
|
patch_t *kp_capsuletarget_arrow[2][2];
|
|
patch_t *kp_capsuletarget_icon[2];
|
|
patch_t *kp_capsuletarget_far[2][2];
|
|
patch_t *kp_capsuletarget_far_text[2];
|
|
patch_t *kp_capsuletarget_near[2][8];
|
|
|
|
patch_t *kp_superflickytarget[2][4];
|
|
|
|
patch_t *kp_spraycantarget_far[2][6];
|
|
patch_t *kp_spraycantarget_near[2][6];
|
|
|
|
patch_t *kp_button_a[2][2];
|
|
patch_t *kp_button_b[2][2];
|
|
patch_t *kp_button_c[2][2];
|
|
patch_t *kp_button_x[2][2];
|
|
patch_t *kp_button_y[2][2];
|
|
patch_t *kp_button_z[2][2];
|
|
patch_t *kp_button_start[2];
|
|
patch_t *kp_button_l[2];
|
|
patch_t *kp_button_r[2];
|
|
patch_t *kp_button_up[2];
|
|
patch_t *kp_button_down[2];
|
|
patch_t *kp_button_right[2];
|
|
patch_t *kp_button_left[2];
|
|
|
|
static void K_LoadButtonGraphics(patch_t *kp[2], int letter)
|
|
{
|
|
HU_UpdatePatch(&kp[0], "TLB_%c", letter);
|
|
HU_UpdatePatch(&kp[1], "TLB_%cB", letter);
|
|
}
|
|
|
|
void K_LoadKartHUDGraphics(void)
|
|
{
|
|
INT32 i, j, k;
|
|
char buffer[9];
|
|
|
|
// Null Stuff
|
|
HU_UpdatePatch(&kp_nodraw, "K_TRNULL");
|
|
|
|
// Stickers
|
|
HU_UpdatePatch(&kp_timesticker, "K_STTIME");
|
|
HU_UpdatePatch(&kp_timestickerwide, "K_STTIMW");
|
|
HU_UpdatePatch(&kp_lapsticker, "K_STLAPS");
|
|
HU_UpdatePatch(&kp_lapstickerwide, "K_STLAPW");
|
|
HU_UpdatePatch(&kp_lapstickernarrow, "K_STLAPN");
|
|
HU_UpdatePatch(&kp_splitlapflag, "K_SPTLAP");
|
|
HU_UpdatePatch(&kp_bumpersticker, "K_STBALN");
|
|
HU_UpdatePatch(&kp_bumperstickerwide, "K_STBALW");
|
|
HU_UpdatePatch(&kp_capsulesticker, "K_STCAPN");
|
|
HU_UpdatePatch(&kp_capsulestickerwide, "K_STCAPW");
|
|
HU_UpdatePatch(&kp_karmasticker, "K_STKARM");
|
|
HU_UpdatePatch(&kp_spheresticker, "K_STBSMT");
|
|
HU_UpdatePatch(&kp_splitspheresticker, "K_SPBSMT");
|
|
HU_UpdatePatch(&kp_splitkarmabomb, "K_SPTKRM");
|
|
HU_UpdatePatch(&kp_timeoutsticker, "K_STTOUT");
|
|
|
|
// Pre-start countdown bulbs
|
|
sprintf(buffer, "K_BULBxx");
|
|
for (i = 0; i < 15; i++)
|
|
{
|
|
buffer[6] = '0'+((i+1)/10);
|
|
buffer[7] = '0'+((i+1)%10);
|
|
HU_UpdatePatch(&kp_prestartbulb[i], "%s", buffer);
|
|
}
|
|
|
|
sprintf(buffer, "K_SBLBxx");
|
|
for (i = 0; i < 15; i++)
|
|
{
|
|
buffer[6] = '0'+((i+1)/10);
|
|
buffer[7] = '0'+((i+1)%10);
|
|
HU_UpdatePatch(&kp_prestartbulb_split[i], "%s", buffer);
|
|
}
|
|
|
|
// Pre-start position letters
|
|
HU_UpdatePatch(&kp_prestartletters[0], "K_PL_P");
|
|
HU_UpdatePatch(&kp_prestartletters[1], "K_PL_O");
|
|
HU_UpdatePatch(&kp_prestartletters[2], "K_PL_S");
|
|
HU_UpdatePatch(&kp_prestartletters[3], "K_PL_I");
|
|
HU_UpdatePatch(&kp_prestartletters[4], "K_PL_T");
|
|
HU_UpdatePatch(&kp_prestartletters[5], "K_PL_N");
|
|
HU_UpdatePatch(&kp_prestartletters[6], "K_PL_EX");
|
|
|
|
HU_UpdatePatch(&kp_prestartletters_split[0], "K_SPL_P");
|
|
HU_UpdatePatch(&kp_prestartletters_split[1], "K_SPL_O");
|
|
HU_UpdatePatch(&kp_prestartletters_split[2], "K_SPL_S");
|
|
HU_UpdatePatch(&kp_prestartletters_split[3], "K_SPL_I");
|
|
HU_UpdatePatch(&kp_prestartletters_split[4], "K_SPL_T");
|
|
HU_UpdatePatch(&kp_prestartletters_split[5], "K_SPL_N");
|
|
HU_UpdatePatch(&kp_prestartletters_split[6], "K_SPL_EX");
|
|
|
|
// Starting countdown
|
|
HU_UpdatePatch(&kp_startcountdown[0], "K_CNT3A");
|
|
HU_UpdatePatch(&kp_startcountdown[1], "K_CNT2A");
|
|
HU_UpdatePatch(&kp_startcountdown[2], "K_CNT1A");
|
|
HU_UpdatePatch(&kp_startcountdown[3], "K_CNTGOA");
|
|
HU_UpdatePatch(&kp_startcountdown[4], "K_DUEL1");
|
|
HU_UpdatePatch(&kp_startcountdown[5], "K_CNT3B");
|
|
HU_UpdatePatch(&kp_startcountdown[6], "K_CNT2B");
|
|
HU_UpdatePatch(&kp_startcountdown[7], "K_CNT1B");
|
|
HU_UpdatePatch(&kp_startcountdown[8], "K_CNTGOB");
|
|
HU_UpdatePatch(&kp_startcountdown[9], "K_DUEL2");
|
|
// Splitscreen
|
|
HU_UpdatePatch(&kp_startcountdown[10], "K_SMC3A");
|
|
HU_UpdatePatch(&kp_startcountdown[11], "K_SMC2A");
|
|
HU_UpdatePatch(&kp_startcountdown[12], "K_SMC1A");
|
|
HU_UpdatePatch(&kp_startcountdown[13], "K_SMCGOA");
|
|
HU_UpdatePatch(&kp_startcountdown[14], "K_SDUEL1");
|
|
HU_UpdatePatch(&kp_startcountdown[15], "K_SMC3B");
|
|
HU_UpdatePatch(&kp_startcountdown[16], "K_SMC2B");
|
|
HU_UpdatePatch(&kp_startcountdown[17], "K_SMC1B");
|
|
HU_UpdatePatch(&kp_startcountdown[18], "K_SMCGOB");
|
|
HU_UpdatePatch(&kp_startcountdown[19], "K_SDUEL2");
|
|
|
|
// Fault
|
|
HU_UpdatePatch(&kp_racefault[0], "K_FAULTA");
|
|
HU_UpdatePatch(&kp_racefault[1], "K_FAULTB");
|
|
// Splitscreen
|
|
HU_UpdatePatch(&kp_racefault[2], "K_SMFLTA");
|
|
HU_UpdatePatch(&kp_racefault[3], "K_SMFLTB");
|
|
// 2P splitscreen
|
|
HU_UpdatePatch(&kp_racefault[4], "K_2PFLTA");
|
|
HU_UpdatePatch(&kp_racefault[5], "K_2PFLTB");
|
|
|
|
// Finish
|
|
HU_UpdatePatch(&kp_racefinish[0], "K_FINA");
|
|
HU_UpdatePatch(&kp_racefinish[1], "K_FINB");
|
|
// Splitscreen
|
|
HU_UpdatePatch(&kp_racefinish[2], "K_SMFINA");
|
|
HU_UpdatePatch(&kp_racefinish[3], "K_SMFINB");
|
|
// 2P splitscreen
|
|
HU_UpdatePatch(&kp_racefinish[4], "K_2PFINA");
|
|
HU_UpdatePatch(&kp_racefinish[5], "K_2PFINB");
|
|
|
|
// Position numbers
|
|
sprintf(buffer, "KRNKxyz");
|
|
for (i = 0; i < 10; i++)
|
|
{
|
|
buffer[6] = '0'+i;
|
|
|
|
for (j = 0; j < 2; j++)
|
|
{
|
|
buffer[5] = 'A'+j;
|
|
|
|
for (k = 0; k < 2; k++)
|
|
{
|
|
if (k > 0)
|
|
{
|
|
buffer[4] = 'S';
|
|
}
|
|
else
|
|
{
|
|
buffer[4] = 'B';
|
|
}
|
|
|
|
HU_UpdatePatch(&kp_positionnum[i][j][k], "%s", buffer);
|
|
}
|
|
}
|
|
}
|
|
|
|
sprintf(buffer, "OPPRNKxx");
|
|
for (i = 0; i <= MAXPLAYERS; i++)
|
|
{
|
|
buffer[6] = '0'+(i/10);
|
|
buffer[7] = '0'+(i%10);
|
|
HU_UpdatePatch(&kp_facenum[i], "%s", buffer);
|
|
}
|
|
|
|
sprintf(buffer, "K_CHILIx");
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
buffer[7] = '0'+(i+1);
|
|
HU_UpdatePatch(&kp_facehighlight[i], "%s", buffer);
|
|
}
|
|
|
|
// Special minimap icons
|
|
HU_UpdatePatch(&kp_nocontestminimap, "MINIDEAD");
|
|
HU_UpdatePatch(&kp_unknownminimap, "HUHMAP");
|
|
HU_UpdatePatch(&kp_spbminimap, "SPBMMAP");
|
|
|
|
HU_UpdatePatch(&kp_wouldyoustillcatchmeifiwereaworm, "MINIPROG");
|
|
HU_UpdatePatch(&kp_catcherminimap, "UFOMAP");
|
|
HU_UpdatePatch(&kp_emeraldminimap[0], "EMEMAP");
|
|
HU_UpdatePatch(&kp_emeraldminimap[1], "SUPMAP");
|
|
|
|
HU_UpdatePatch(&kp_capsuleminimap[0], "MINICAP1");
|
|
HU_UpdatePatch(&kp_capsuleminimap[1], "MINICAP2");
|
|
HU_UpdatePatch(&kp_capsuleminimap[2], "MINICAP3");
|
|
|
|
HU_UpdatePatch(&kp_battleufominimap, "MINIBUFO");
|
|
HU_UpdatePatch(&kp_superflickyminimap, "FLKMAPA");
|
|
|
|
// Rings & Lives
|
|
HU_UpdatePatch(&kp_ringsticker[0], "RNGBACKA");
|
|
HU_UpdatePatch(&kp_ringsticker[1], "RNGBACKB");
|
|
|
|
sprintf(buffer, "K_RINGx");
|
|
for (i = 0; i < 6; i++)
|
|
{
|
|
buffer[6] = '0'+(i+1);
|
|
HU_UpdatePatch(&kp_ring[i], "%s", buffer);
|
|
}
|
|
|
|
HU_UpdatePatch(&kp_ringdebtminus, "RDEBTMIN");
|
|
|
|
sprintf(buffer, "SPBRNGxx");
|
|
for (i = 0; i < 16; i++)
|
|
{
|
|
buffer[6] = '0'+((i+1) / 10);
|
|
buffer[7] = '0'+((i+1) % 10);
|
|
HU_UpdatePatch(&kp_ringspblock[i], "%s", buffer);
|
|
}
|
|
|
|
HU_UpdatePatch(&kp_ringstickersplit[0], "SMRNGBGA");
|
|
HU_UpdatePatch(&kp_ringstickersplit[1], "SMRNGBGB");
|
|
|
|
sprintf(buffer, "K_SRINGx");
|
|
for (i = 0; i < 6; i++)
|
|
{
|
|
buffer[7] = '0'+(i+1);
|
|
HU_UpdatePatch(&kp_smallring[i], "%s", buffer);
|
|
}
|
|
|
|
HU_UpdatePatch(&kp_ringdebtminussmall, "SRDEBTMN");
|
|
|
|
sprintf(buffer, "SPBRGSxx");
|
|
for (i = 0; i < 16; i++)
|
|
{
|
|
buffer[6] = '0'+((i+1) / 10);
|
|
buffer[7] = '0'+((i+1) % 10);
|
|
HU_UpdatePatch(&kp_ringspblocksmall[i], "%s", buffer);
|
|
}
|
|
|
|
// Speedometer
|
|
HU_UpdatePatch(&kp_speedometersticker, "K_SPDMBG");
|
|
|
|
sprintf(buffer, "K_SPDMLx");
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
buffer[7] = '0'+(i+1);
|
|
HU_UpdatePatch(&kp_speedometerlabel[i], "%s", buffer);
|
|
}
|
|
|
|
// Extra ranking icons
|
|
HU_UpdatePatch(&kp_rankbumper, "K_BLNICO");
|
|
HU_UpdatePatch(&kp_bigbumper, "K_BLNREG");
|
|
HU_UpdatePatch(&kp_tinybumper[0], "K_BLNA");
|
|
HU_UpdatePatch(&kp_tinybumper[1], "K_BLNB");
|
|
HU_UpdatePatch(&kp_ranknobumpers, "K_NOBLNS");
|
|
HU_UpdatePatch(&kp_rankcapsule, "K_CAPICO");
|
|
HU_UpdatePatch(&kp_rankemerald, "K_EMERC");
|
|
HU_UpdatePatch(&kp_rankemeraldflash, "K_EMERW");
|
|
HU_UpdatePatch(&kp_rankemeraldback, "K_EMERBK");
|
|
HU_UpdatePatch(&kp_pts[0], "K_POINTS");
|
|
HU_UpdatePatch(&kp_pts[1], "K_POINT4");
|
|
|
|
// Battle goal
|
|
HU_UpdatePatch(&kp_goal[0][0], "K_ST1GLA");
|
|
HU_UpdatePatch(&kp_goal[1][0], "K_ST1GLB");
|
|
HU_UpdatePatch(&kp_goal[0][1], "K_ST4GLA");
|
|
HU_UpdatePatch(&kp_goal[1][1], "K_ST4GLB");
|
|
HU_UpdatePatch(&kp_goalrod[0], "K_ST1GLD");
|
|
HU_UpdatePatch(&kp_goalrod[1], "K_ST4GLD");
|
|
HU_UpdatePatch(&kp_goaltext1p, "K_ST1GLC");
|
|
|
|
// Battle graphics
|
|
HU_UpdatePatch(&kp_battlewin, "K_BWIN");
|
|
HU_UpdatePatch(&kp_battlecool, "K_BCOOL");
|
|
HU_UpdatePatch(&kp_battlelose, "K_BLOSE");
|
|
HU_UpdatePatch(&kp_battlewait, "K_BWAIT");
|
|
HU_UpdatePatch(&kp_battleinfo, "K_BINFO");
|
|
HU_UpdatePatch(&kp_wanted, "K_WANTED");
|
|
HU_UpdatePatch(&kp_wantedsplit, "4PWANTED");
|
|
HU_UpdatePatch(&kp_wantedreticle, "MMAPWANT");
|
|
HU_UpdatePatch(&kp_minimapdot, "MMAPDOT");
|
|
|
|
// Kart Item Windows
|
|
HU_UpdatePatch(&kp_itembg[0], "K_ITBG");
|
|
HU_UpdatePatch(&kp_itembg[1], "K_ITBGD");
|
|
HU_UpdatePatch(&kp_itemtimer[0], "K_ITIMER");
|
|
HU_UpdatePatch(&kp_itemmulsticker[0], "K_ITMUL");
|
|
HU_UpdatePatch(&kp_itemx, "K_ITX");
|
|
|
|
HU_UpdatePatch(&kp_ringbg[0], "K_RBBG");
|
|
HU_UpdatePatch(&kp_ringbg[1], "K_SBBG");
|
|
|
|
HU_UpdatePatch(&kp_sadface[0], "K_ITSAD");
|
|
HU_UpdatePatch(&kp_sneaker[0], "K_ITSHOE");
|
|
HU_UpdatePatch(&kp_rocketsneaker[0], "K_ITRSHE");
|
|
|
|
sprintf(buffer, "K_ITINVx");
|
|
for (i = 0; i < 7; i++)
|
|
{
|
|
buffer[7] = '1'+i;
|
|
HU_UpdatePatch(&kp_invincibility[i], "%s", buffer);
|
|
}
|
|
HU_UpdatePatch(&kp_banana[0], "K_ITBANA");
|
|
HU_UpdatePatch(&kp_eggman[0], "K_ITEGGM");
|
|
sprintf(buffer, "K_ITORBx");
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
buffer[7] = '1'+i;
|
|
HU_UpdatePatch(&kp_orbinaut[i], "%s", buffer);
|
|
}
|
|
HU_UpdatePatch(&kp_jawz[0], "K_ITJAWZ");
|
|
HU_UpdatePatch(&kp_mine[0], "K_ITMINE");
|
|
HU_UpdatePatch(&kp_landmine[0], "K_ITLNDM");
|
|
HU_UpdatePatch(&kp_ballhog[0], "K_ITBHOG");
|
|
HU_UpdatePatch(&kp_selfpropelledbomb[0], "K_ITSPB");
|
|
HU_UpdatePatch(&kp_grow[0], "K_ITGROW");
|
|
HU_UpdatePatch(&kp_shrink[0], "K_ITSHRK");
|
|
HU_UpdatePatch(&kp_lightningshield[0], "K_ITTHNS");
|
|
HU_UpdatePatch(&kp_bubbleshield[0], "K_ITBUBS");
|
|
HU_UpdatePatch(&kp_flameshield[0], "K_ITFLMS");
|
|
HU_UpdatePatch(&kp_hyudoro[0], "K_ITHYUD");
|
|
HU_UpdatePatch(&kp_pogospring[0], "K_ITPOGO");
|
|
HU_UpdatePatch(&kp_superring[0], "K_ITRING");
|
|
HU_UpdatePatch(&kp_kitchensink[0], "K_ITSINK");
|
|
HU_UpdatePatch(&kp_droptarget[0], "K_ITDTRG");
|
|
HU_UpdatePatch(&kp_gardentop[0], "K_ITGTOP");
|
|
HU_UpdatePatch(&kp_gachabom[0], "K_ITGBOM");
|
|
HU_UpdatePatch(&kp_bar[0], "K_RBBAR");
|
|
HU_UpdatePatch(&kp_doublebar[0], "K_RBBAR2");
|
|
HU_UpdatePatch(&kp_triplebar[0], "K_RBBAR3");
|
|
HU_UpdatePatch(&kp_slotring[0], "K_RBRING");
|
|
HU_UpdatePatch(&kp_seven[0], "K_RBSEV");
|
|
HU_UpdatePatch(&kp_jackpot[0], "K_RBJACK");
|
|
|
|
sprintf(buffer, "FSMFGxxx");
|
|
for (i = 0; i < FLAMESHIELD_MAX; i++)
|
|
{
|
|
buffer[5] = '0'+((i+1)/100);
|
|
buffer[6] = '0'+(((i+1)/10)%10);
|
|
buffer[7] = '0'+((i+1)%10);
|
|
HU_UpdatePatch(&kp_flameshieldmeter[i][0], "%s", buffer);
|
|
}
|
|
|
|
sprintf(buffer, "FSMBGxxx");
|
|
for (i = 0; i < FLAMESHIELD_MAX; i++)
|
|
{
|
|
buffer[5] = '0'+((i+1)/100);
|
|
buffer[6] = '0'+(((i+1)/10)%10);
|
|
buffer[7] = '0'+((i+1)%10);
|
|
HU_UpdatePatch(&kp_flameshieldmeter_bg[i][0], "%s", buffer);
|
|
}
|
|
|
|
// Splitscreen
|
|
HU_UpdatePatch(&kp_itembg[2], "K_ISBG");
|
|
HU_UpdatePatch(&kp_itembg[3], "K_ISBGD");
|
|
HU_UpdatePatch(&kp_itemtimer[1], "K_ISIMER");
|
|
HU_UpdatePatch(&kp_itemmulsticker[1], "K_ISMUL");
|
|
|
|
HU_UpdatePatch(&kp_sadface[1], "K_ISSAD");
|
|
HU_UpdatePatch(&kp_sneaker[1], "K_ISSHOE");
|
|
HU_UpdatePatch(&kp_rocketsneaker[1], "K_ISRSHE");
|
|
sprintf(buffer, "K_ISINVx");
|
|
for (i = 0; i < 6; i++)
|
|
{
|
|
buffer[7] = '1'+i;
|
|
HU_UpdatePatch(&kp_invincibility[i+7], "%s", buffer);
|
|
}
|
|
HU_UpdatePatch(&kp_banana[1], "K_ISBANA");
|
|
HU_UpdatePatch(&kp_eggman[1], "K_ISEGGM");
|
|
HU_UpdatePatch(&kp_orbinaut[4], "K_ISORBN");
|
|
HU_UpdatePatch(&kp_jawz[1], "K_ISJAWZ");
|
|
HU_UpdatePatch(&kp_mine[1], "K_ISMINE");
|
|
HU_UpdatePatch(&kp_landmine[1], "K_ISLNDM");
|
|
HU_UpdatePatch(&kp_ballhog[1], "K_ISBHOG");
|
|
HU_UpdatePatch(&kp_selfpropelledbomb[1], "K_ISSPB");
|
|
HU_UpdatePatch(&kp_grow[1], "K_ISGROW");
|
|
HU_UpdatePatch(&kp_shrink[1], "K_ISSHRK");
|
|
HU_UpdatePatch(&kp_lightningshield[1], "K_ISTHNS");
|
|
HU_UpdatePatch(&kp_bubbleshield[1], "K_ISBUBS");
|
|
HU_UpdatePatch(&kp_flameshield[1], "K_ISFLMS");
|
|
HU_UpdatePatch(&kp_hyudoro[1], "K_ISHYUD");
|
|
HU_UpdatePatch(&kp_pogospring[1], "K_ISPOGO");
|
|
HU_UpdatePatch(&kp_superring[1], "K_ISRING");
|
|
HU_UpdatePatch(&kp_kitchensink[1], "K_ISSINK");
|
|
HU_UpdatePatch(&kp_droptarget[1], "K_ISDTRG");
|
|
HU_UpdatePatch(&kp_gardentop[1], "K_ISGTOP");
|
|
HU_UpdatePatch(&kp_gachabom[1], "K_ISGBOM");
|
|
HU_UpdatePatch(&kp_bar[1], "K_SBBAR");
|
|
HU_UpdatePatch(&kp_doublebar[1], "K_SBBAR2");
|
|
HU_UpdatePatch(&kp_triplebar[1], "K_SBBAR3");
|
|
HU_UpdatePatch(&kp_slotring[1], "K_SBRING");
|
|
HU_UpdatePatch(&kp_seven[1], "K_SBSEV");
|
|
HU_UpdatePatch(&kp_jackpot[1], "K_SBJACK");
|
|
|
|
sprintf(buffer, "FSMFSxxx");
|
|
for (i = 0; i < 120; i++)
|
|
{
|
|
buffer[5] = '0'+((i+1)/100);
|
|
buffer[6] = '0'+(((i+1)/10)%10);
|
|
buffer[7] = '0'+((i+1)%10);
|
|
HU_UpdatePatch(&kp_flameshieldmeter[i][1], "%s", buffer);
|
|
}
|
|
|
|
sprintf(buffer, "FSMBS0xx");
|
|
for (i = 0; i < 120; i++)
|
|
{
|
|
buffer[5] = '0'+((i+1)/100);
|
|
buffer[6] = '0'+(((i+1)/10)%10);
|
|
buffer[7] = '0'+((i+1)%10);
|
|
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++)
|
|
{
|
|
buffer[7] = '1'+i;
|
|
HU_UpdatePatch(&kp_check[i], "%s", buffer);
|
|
}
|
|
|
|
// Rival indicators
|
|
sprintf(buffer, "K_RIVALx");
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
buffer[7] = '1'+i;
|
|
HU_UpdatePatch(&kp_rival[i], "%s", buffer);
|
|
}
|
|
|
|
// Rival indicators
|
|
sprintf(buffer, "K_SSPLxx");
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
buffer[6] = 'A'+i;
|
|
for (j = 0; j < 2; j++)
|
|
{
|
|
buffer[7] = '1'+j;
|
|
HU_UpdatePatch(&kp_localtag[i][j], "%s", buffer);
|
|
}
|
|
}
|
|
|
|
// Typing indicator
|
|
HU_UpdatePatch(&kp_talk, "K_TALK");
|
|
HU_UpdatePatch(&kp_typdot, "K_TYPDOT");
|
|
|
|
// Eggman warning numbers
|
|
sprintf(buffer, "K_EGGNx");
|
|
for (i = 0; i < 6; i++)
|
|
{
|
|
buffer[6] = '0'+i;
|
|
HU_UpdatePatch(&kp_eggnum[i], "%s", buffer);
|
|
}
|
|
|
|
// First person mode
|
|
HU_UpdatePatch(&kp_fpview[0], "VIEWA0");
|
|
HU_UpdatePatch(&kp_fpview[1], "VIEWB0D0");
|
|
HU_UpdatePatch(&kp_fpview[2], "VIEWC0E0");
|
|
|
|
// Input UI Wheel
|
|
sprintf(buffer, "K_WHEELx");
|
|
for (i = 0; i < 5; i++)
|
|
{
|
|
buffer[7] = '0'+i;
|
|
HU_UpdatePatch(&kp_inputwheel[i], "%s", buffer);
|
|
}
|
|
|
|
// HERE COMES A NEW CHALLENGER
|
|
sprintf(buffer, "K_CHALxx");
|
|
for (i = 0; i < 25; i++)
|
|
{
|
|
buffer[6] = '0'+((i+1)/10);
|
|
buffer[7] = '0'+((i+1)%10);
|
|
HU_UpdatePatch(&kp_challenger[i], "%s", buffer);
|
|
}
|
|
|
|
// Lap start animation
|
|
sprintf(buffer, "K_LAP0x");
|
|
for (i = 0; i < 7; i++)
|
|
{
|
|
buffer[6] = '0'+(i+1);
|
|
HU_UpdatePatch(&kp_lapanim_lap[i], "%s", buffer);
|
|
}
|
|
|
|
sprintf(buffer, "K_LAPFxx");
|
|
for (i = 0; i < 11; i++)
|
|
{
|
|
buffer[6] = '0'+((i+1)/10);
|
|
buffer[7] = '0'+((i+1)%10);
|
|
HU_UpdatePatch(&kp_lapanim_final[i], "%s", buffer);
|
|
}
|
|
|
|
sprintf(buffer, "K_LAPNxx");
|
|
for (i = 0; i < 10; i++)
|
|
{
|
|
buffer[6] = '0'+i;
|
|
for (j = 0; j < 3; j++)
|
|
{
|
|
buffer[7] = '0'+(j+1);
|
|
HU_UpdatePatch(&kp_lapanim_number[i][j], "%s", buffer);
|
|
}
|
|
}
|
|
|
|
sprintf(buffer, "K_LAPE0x");
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
buffer[7] = '0'+(i+1);
|
|
HU_UpdatePatch(&kp_lapanim_emblem[i], "%s", buffer);
|
|
}
|
|
|
|
sprintf(buffer, "K_LAPH0x");
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
buffer[7] = '0'+(i+1);
|
|
HU_UpdatePatch(&kp_lapanim_hand[i], "%s", buffer);
|
|
}
|
|
|
|
HU_UpdatePatch(&kp_yougotem, "YOUGOTEM");
|
|
HU_UpdatePatch(&kp_itemminimap, "MMAPITEM");
|
|
|
|
sprintf(buffer, "ALAGLESx");
|
|
for (i = 0; i < 10; ++i)
|
|
{
|
|
buffer[7] = '0'+i;
|
|
HU_UpdatePatch(&kp_alagles[i], "%s", buffer);
|
|
}
|
|
|
|
sprintf(buffer, "BLAGLESx");
|
|
for (i = 0; i < 6; ++i)
|
|
{
|
|
buffer[7] = '0'+i;
|
|
HU_UpdatePatch(&kp_blagles[i], "%s", buffer);
|
|
}
|
|
|
|
HU_UpdatePatch(&kp_cpu, "K_CPU");
|
|
|
|
HU_UpdatePatch(&kp_nametagstem, "K_NAMEST");
|
|
|
|
HU_UpdatePatch(&kp_trickcool[0], "K_COOL1");
|
|
HU_UpdatePatch(&kp_trickcool[1], "K_COOL2");
|
|
|
|
HU_UpdatePatch(&kp_autoroulette, "A11YITEM");
|
|
|
|
sprintf(buffer, "K_BOSB0x");
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
buffer[7] = '0'+((i+1)%10);
|
|
HU_UpdatePatch(&kp_bossbar[i], "%s", buffer);
|
|
}
|
|
|
|
sprintf(buffer, "K_BOSR0x");
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
buffer[7] = '0'+((i+1)%10);
|
|
HU_UpdatePatch(&kp_bossret[i], "%s", buffer);
|
|
}
|
|
|
|
sprintf(buffer, "HCAPARxx");
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
buffer[6] = 'A'+i;
|
|
|
|
for (j = 0; j < 2; j++)
|
|
{
|
|
buffer[7] = '0'+j;
|
|
HU_UpdatePatch(&kp_capsuletarget_arrow[i][j], "%s", buffer);
|
|
}
|
|
}
|
|
|
|
sprintf(buffer, "HUDCAPDx");
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
buffer[7] = '0'+i;
|
|
HU_UpdatePatch(&kp_capsuletarget_far_text[i], "%s", buffer);
|
|
}
|
|
|
|
sprintf(buffer, "HUDCAPCx");
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
buffer[7] = '0'+i;
|
|
HU_UpdatePatch(&kp_capsuletarget_icon[i], "%s", buffer);
|
|
}
|
|
|
|
sprintf(buffer, "HUDCAPBx");
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
buffer[7] = '0'+i;
|
|
HU_UpdatePatch(&kp_capsuletarget_far[0][i], "%s", buffer);
|
|
}
|
|
|
|
sprintf(buffer, "HUDC4PBx");
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
buffer[7] = '0'+i;
|
|
HU_UpdatePatch(&kp_capsuletarget_far[1][i], "%s", buffer);
|
|
}
|
|
|
|
sprintf(buffer, "HUDCAPAx");
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
buffer[7] = '0'+i;
|
|
HU_UpdatePatch(&kp_capsuletarget_near[0][i], "%s", buffer);
|
|
}
|
|
|
|
sprintf(buffer, "HUDC4PAx");
|
|
for (i = 0; i < 8; i++)
|
|
{
|
|
buffer[7] = '0'+i;
|
|
HU_UpdatePatch(&kp_capsuletarget_near[1][i], "%s", buffer);
|
|
}
|
|
|
|
sprintf(buffer, "HUDFLKAx");
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
buffer[7] = '0'+i;
|
|
HU_UpdatePatch(&kp_superflickytarget[0][i], "%s", buffer);
|
|
}
|
|
|
|
sprintf(buffer, "H4PFLKAx");
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
buffer[7] = '0'+i;
|
|
HU_UpdatePatch(&kp_superflickytarget[1][i], "%s", buffer);
|
|
}
|
|
|
|
sprintf(buffer, "SPCNBFAx");
|
|
for (i = 0; i < 6; i++)
|
|
{
|
|
buffer[7] = '1'+i;
|
|
HU_UpdatePatch(&kp_spraycantarget_far[0][i], "%s", buffer);
|
|
}
|
|
|
|
sprintf(buffer, "SPCNSFAx");
|
|
for (i = 0; i < 6; i++)
|
|
{
|
|
buffer[7] = '1'+i;
|
|
HU_UpdatePatch(&kp_spraycantarget_far[1][i], "%s", buffer);
|
|
}
|
|
|
|
sprintf(buffer, "SPCNBCLx");
|
|
for (i = 0; i < 6; i++)
|
|
{
|
|
buffer[7] = '1'+i;
|
|
HU_UpdatePatch(&kp_spraycantarget_near[0][i], "%s", buffer);
|
|
}
|
|
|
|
sprintf(buffer, "SPCNSCLx");
|
|
for (i = 0; i < 6; i++)
|
|
{
|
|
buffer[7] = '1'+i;
|
|
HU_UpdatePatch(&kp_spraycantarget_near[1][i], "%s", buffer);
|
|
}
|
|
|
|
K_LoadButtonGraphics(kp_button_a[0], 'A');
|
|
K_LoadButtonGraphics(kp_button_a[1], 'N');
|
|
K_LoadButtonGraphics(kp_button_b[0], 'B');
|
|
K_LoadButtonGraphics(kp_button_b[1], 'O');
|
|
K_LoadButtonGraphics(kp_button_c[0], 'C');
|
|
K_LoadButtonGraphics(kp_button_c[1], 'P');
|
|
K_LoadButtonGraphics(kp_button_x[0], 'D');
|
|
K_LoadButtonGraphics(kp_button_x[1], 'Q');
|
|
K_LoadButtonGraphics(kp_button_y[0], 'E');
|
|
K_LoadButtonGraphics(kp_button_y[1], 'R');
|
|
K_LoadButtonGraphics(kp_button_z[0], 'F');
|
|
K_LoadButtonGraphics(kp_button_z[1], 'S');
|
|
K_LoadButtonGraphics(kp_button_start, 'G');
|
|
K_LoadButtonGraphics(kp_button_l, 'H');
|
|
K_LoadButtonGraphics(kp_button_r, 'I');
|
|
K_LoadButtonGraphics(kp_button_up, 'J');
|
|
K_LoadButtonGraphics(kp_button_down, 'K');
|
|
K_LoadButtonGraphics(kp_button_right, 'L');
|
|
K_LoadButtonGraphics(kp_button_left, 'M');
|
|
}
|
|
|
|
// For the item toggle menu
|
|
const char *K_GetItemPatch(UINT8 item, boolean tiny)
|
|
{
|
|
switch (item)
|
|
{
|
|
case KITEM_SNEAKER:
|
|
case KRITEM_DUALSNEAKER:
|
|
case KRITEM_TRIPLESNEAKER:
|
|
return (tiny ? "K_ISSHOE" : "K_ITSHOE");
|
|
case KITEM_ROCKETSNEAKER:
|
|
return (tiny ? "K_ISRSHE" : "K_ITRSHE");
|
|
case KITEM_INVINCIBILITY:
|
|
return (tiny ? "K_ISINV1" : "K_ITINV1");
|
|
case KITEM_BANANA:
|
|
case KRITEM_TRIPLEBANANA:
|
|
return (tiny ? "K_ISBANA" : "K_ITBANA");
|
|
case KITEM_EGGMAN:
|
|
return (tiny ? "K_ISEGGM" : "K_ITEGGM");
|
|
case KITEM_ORBINAUT:
|
|
return (tiny ? "K_ISORBN" : "K_ITORB1");
|
|
case KITEM_JAWZ:
|
|
case KRITEM_DUALJAWZ:
|
|
return (tiny ? "K_ISJAWZ" : "K_ITJAWZ");
|
|
case KITEM_MINE:
|
|
return (tiny ? "K_ISMINE" : "K_ITMINE");
|
|
case KITEM_LANDMINE:
|
|
return (tiny ? "K_ISLNDM" : "K_ITLNDM");
|
|
case KITEM_BALLHOG:
|
|
return (tiny ? "K_ISBHOG" : "K_ITBHOG");
|
|
case KITEM_SPB:
|
|
return (tiny ? "K_ISSPB" : "K_ITSPB");
|
|
case KITEM_GROW:
|
|
return (tiny ? "K_ISGROW" : "K_ITGROW");
|
|
case KITEM_SHRINK:
|
|
return (tiny ? "K_ISSHRK" : "K_ITSHRK");
|
|
case KITEM_LIGHTNINGSHIELD:
|
|
return (tiny ? "K_ISTHNS" : "K_ITTHNS");
|
|
case KITEM_BUBBLESHIELD:
|
|
return (tiny ? "K_ISBUBS" : "K_ITBUBS");
|
|
case KITEM_FLAMESHIELD:
|
|
return (tiny ? "K_ISFLMS" : "K_ITFLMS");
|
|
case KITEM_HYUDORO:
|
|
return (tiny ? "K_ISHYUD" : "K_ITHYUD");
|
|
case KITEM_POGOSPRING:
|
|
return (tiny ? "K_ISPOGO" : "K_ITPOGO");
|
|
case KITEM_SUPERRING:
|
|
return (tiny ? "K_ISRING" : "K_ITRING");
|
|
case KITEM_KITCHENSINK:
|
|
return (tiny ? "K_ISSINK" : "K_ITSINK");
|
|
case KITEM_DROPTARGET:
|
|
return (tiny ? "K_ISDTRG" : "K_ITDTRG");
|
|
case KITEM_GARDENTOP:
|
|
return (tiny ? "K_ISGTOP" : "K_ITGTOP");
|
|
case KITEM_GACHABOM:
|
|
case KRITEM_TRIPLEGACHABOM:
|
|
return (tiny ? "K_ISGBOM" : "K_ITGBOM");
|
|
case KRITEM_TRIPLEORBINAUT:
|
|
return (tiny ? "K_ISORBN" : "K_ITORB3");
|
|
case KRITEM_QUADORBINAUT:
|
|
return (tiny ? "K_ISORBN" : "K_ITORB4");
|
|
default:
|
|
return (tiny ? "K_ISSAD" : "K_ITSAD");
|
|
}
|
|
}
|
|
|
|
static patch_t *K_GetCachedItemPatch(INT32 item, UINT8 offset)
|
|
{
|
|
patch_t **kp[1 + NUMKARTITEMS] = {
|
|
kp_sadface,
|
|
NULL,
|
|
kp_sneaker,
|
|
kp_rocketsneaker,
|
|
kp_invincibility,
|
|
kp_banana,
|
|
kp_eggman,
|
|
kp_orbinaut,
|
|
kp_jawz,
|
|
kp_mine,
|
|
kp_landmine,
|
|
kp_ballhog,
|
|
kp_selfpropelledbomb,
|
|
kp_grow,
|
|
kp_shrink,
|
|
kp_lightningshield,
|
|
kp_bubbleshield,
|
|
kp_flameshield,
|
|
kp_hyudoro,
|
|
kp_pogospring,
|
|
kp_superring,
|
|
kp_kitchensink,
|
|
kp_droptarget,
|
|
kp_gardentop,
|
|
kp_gachabom,
|
|
};
|
|
|
|
if (item == KITEM_SAD || (item > KITEM_NONE && item < NUMKARTITEMS))
|
|
return kp[item - KITEM_SAD][offset];
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
static patch_t *K_GetSmallStaticCachedItemPatch(kartitems_t item)
|
|
{
|
|
UINT8 offset;
|
|
|
|
item = static_cast<kartitems_t>(K_ItemResultToType(item));
|
|
|
|
switch (item)
|
|
{
|
|
case KITEM_INVINCIBILITY:
|
|
offset = 7;
|
|
break;
|
|
|
|
case KITEM_ORBINAUT:
|
|
offset = 4;
|
|
break;
|
|
|
|
default:
|
|
offset = 1;
|
|
}
|
|
|
|
return K_GetCachedItemPatch(item, offset);
|
|
}
|
|
|
|
static patch_t *K_GetCachedSlotMachinePatch(INT32 item, UINT8 offset)
|
|
{
|
|
patch_t **kp[KSM__MAX] = {
|
|
kp_bar,
|
|
kp_doublebar,
|
|
kp_triplebar,
|
|
kp_slotring,
|
|
kp_seven,
|
|
kp_jackpot,
|
|
};
|
|
|
|
if (item >= 0 && item < KSM__MAX)
|
|
return kp[item][offset];
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
//}
|
|
|
|
INT32 ITEM_X, ITEM_Y; // Item Window
|
|
INT32 TIME_X, TIME_Y; // Time Sticker
|
|
INT32 LAPS_X, LAPS_Y; // Lap Sticker
|
|
INT32 POSI_X, POSI_Y; // Position Number
|
|
INT32 FACE_X, FACE_Y; // Top-four Faces
|
|
INT32 STCD_X, STCD_Y; // Starting countdown
|
|
INT32 CHEK_Y; // CHECK graphic
|
|
INT32 MINI_X, MINI_Y; // Minimap
|
|
INT32 WANT_X, WANT_Y; // Battle WANTED poster
|
|
|
|
// This is for the P2 and P4 side of splitscreen. Then we'll flip P1's and P2's to the bottom with V_SPLITSCREEN.
|
|
INT32 ITEM2_X, ITEM2_Y;
|
|
INT32 LAPS2_X, LAPS2_Y;
|
|
INT32 POSI2_X, POSI2_Y;
|
|
|
|
// trick "cool"
|
|
INT32 TCOOL_X, TCOOL_Y;
|
|
|
|
// This version of the function was prototyped in Lua by Nev3r ... a HUGE thank you goes out to them!
|
|
void K_ObjectTracking(trackingResult_t *result, const vector3_t *point, boolean reverse)
|
|
{
|
|
#define NEWTAN(x) FINETANGENT(((x + ANGLE_90) >> ANGLETOFINESHIFT) & 4095) // tan function used by Lua
|
|
#define NEWCOS(x) FINECOSINE((x >> ANGLETOFINESHIFT) & FINEMASK)
|
|
|
|
angle_t viewpointAngle, viewpointAiming, viewpointRoll;
|
|
|
|
INT32 screenWidth, screenHeight;
|
|
fixed_t screenHalfW, screenHalfH;
|
|
|
|
const fixed_t baseFov = 90*FRACUNIT;
|
|
fixed_t fovDiff, fov, fovTangent, fg;
|
|
|
|
fixed_t h;
|
|
INT32 da, dp;
|
|
|
|
UINT8 cameraNum = R_GetViewNumber();
|
|
|
|
I_Assert(result != NULL);
|
|
I_Assert(point != NULL);
|
|
|
|
// Initialize defaults
|
|
result->x = result->y = 0;
|
|
result->scale = FRACUNIT;
|
|
result->onScreen = false;
|
|
|
|
// Take the view's properties as necessary.
|
|
if (reverse)
|
|
{
|
|
viewpointAngle = (INT32)(viewangle + ANGLE_180);
|
|
viewpointAiming = (INT32)InvAngle(aimingangle);
|
|
viewpointRoll = (INT32)viewroll;
|
|
}
|
|
else
|
|
{
|
|
viewpointAngle = (INT32)viewangle;
|
|
viewpointAiming = (INT32)aimingangle;
|
|
viewpointRoll = (INT32)InvAngle(viewroll);
|
|
}
|
|
|
|
// Calculate screen size adjustments.
|
|
screenWidth = vid.width/vid.dupx;
|
|
screenHeight = vid.height/vid.dupy;
|
|
|
|
if (r_splitscreen >= 2)
|
|
{
|
|
// Half-wide screens
|
|
screenWidth >>= 1;
|
|
}
|
|
|
|
if (r_splitscreen >= 1)
|
|
{
|
|
// Half-tall screens
|
|
screenHeight >>= 1;
|
|
}
|
|
|
|
screenHalfW = (screenWidth >> 1) << FRACBITS;
|
|
screenHalfH = (screenHeight >> 1) << FRACBITS;
|
|
|
|
// Calculate FOV adjustments.
|
|
fovDiff = R_FOV(cameraNum) - baseFov;
|
|
fov = ((baseFov - fovDiff) / 2) - (stplyr->fovadd / 2);
|
|
fovTangent = NEWTAN(FixedAngle(fov));
|
|
|
|
if (r_splitscreen == 1)
|
|
{
|
|
// Splitscreen FOV is adjusted to maintain expected vertical view
|
|
fovTangent = 10*fovTangent/17;
|
|
}
|
|
|
|
fg = (screenWidth >> 1) * fovTangent;
|
|
|
|
// Determine viewpoint factors.
|
|
h = R_PointToDist2(point->x, point->y, viewx, viewy);
|
|
da = AngleDeltaSigned(viewpointAngle, R_PointToAngle2(viewx, viewy, point->x, point->y));
|
|
dp = AngleDeltaSigned(viewpointAiming, R_PointToAngle2(0, 0, h, viewz));
|
|
|
|
if (reverse)
|
|
{
|
|
da = -(da);
|
|
}
|
|
|
|
// Set results relative to top left!
|
|
result->x = FixedMul(NEWTAN(da), fg);
|
|
result->y = FixedMul((NEWTAN(viewpointAiming) - FixedDiv((point->z - viewz), 1 + FixedMul(NEWCOS(da), h))), fg);
|
|
|
|
result->angle = da;
|
|
result->pitch = dp;
|
|
result->fov = fg;
|
|
|
|
// Rotate for screen roll...
|
|
if (viewpointRoll)
|
|
{
|
|
fixed_t tempx = result->x;
|
|
viewpointRoll >>= ANGLETOFINESHIFT;
|
|
result->x = FixedMul(FINECOSINE(viewpointRoll), tempx) - FixedMul(FINESINE(viewpointRoll), result->y);
|
|
result->y = FixedMul(FINESINE(viewpointRoll), tempx) + FixedMul(FINECOSINE(viewpointRoll), result->y);
|
|
}
|
|
|
|
// Flipped screen?
|
|
if (encoremode)
|
|
{
|
|
result->x = -result->x;
|
|
}
|
|
|
|
// Center results.
|
|
result->x += screenHalfW;
|
|
result->y += screenHalfH;
|
|
|
|
result->scale = FixedDiv(screenHalfW, h+1);
|
|
|
|
result->onScreen = !((abs(da) > ANG60) || (abs(AngleDeltaSigned(viewpointAiming, R_PointToAngle2(0, 0, h, (viewz - point->z)))) > ANGLE_45));
|
|
|
|
// Cheap dirty hacks for some split-screen related cases
|
|
if (result->x < 0 || result->x > (screenWidth << FRACBITS))
|
|
{
|
|
result->onScreen = false;
|
|
}
|
|
|
|
if (result->y < 0 || result->y > (screenHeight << FRACBITS))
|
|
{
|
|
result->onScreen = false;
|
|
}
|
|
|
|
// adjust to non-green-resolution screen coordinates
|
|
result->x -= ((vid.width/vid.dupx) - BASEVIDWIDTH)<<(FRACBITS-((r_splitscreen >= 2) ? 2 : 1));
|
|
result->y -= ((vid.height/vid.dupy) - BASEVIDHEIGHT)<<(FRACBITS-((r_splitscreen >= 1) ? 2 : 1));
|
|
|
|
return;
|
|
|
|
#undef NEWTAN
|
|
#undef NEWCOS
|
|
}
|
|
|
|
static void K_initKartHUD(void)
|
|
{
|
|
/*
|
|
BASEVIDWIDTH = 320
|
|
BASEVIDHEIGHT = 200
|
|
|
|
Item window graphic is 41 x 33
|
|
|
|
Time Sticker graphic is 116 x 11
|
|
Time Font is a solid block of (8 x [12) x 14], equal to 96 x 14
|
|
Therefore, timestamp is 116 x 14 altogether
|
|
|
|
Lap Sticker is 80 x 11
|
|
Lap flag is 22 x 20
|
|
Lap Font is a solid block of (3 x [12) x 14], equal to 36 x 14
|
|
Therefore, lapstamp is 80 x 20 altogether
|
|
|
|
Position numbers are 43 x 53
|
|
|
|
Faces are 32 x 32
|
|
Faces draw downscaled at 16 x 16
|
|
Therefore, the allocated space for them is 16 x 67 altogether
|
|
|
|
----
|
|
|
|
ORIGINAL CZ64 SPLITSCREEN:
|
|
|
|
Item window:
|
|
if (!splitscreen) { ICONX = 139; ICONY = 20; }
|
|
else { ICONX = BASEVIDWIDTH-315; ICONY = 60; }
|
|
|
|
Time: 236, STRINGY( 12)
|
|
Lap: BASEVIDWIDTH-304, STRINGY(BASEVIDHEIGHT-189)
|
|
|
|
*/
|
|
|
|
// Single Screen (defaults)
|
|
// Item Window
|
|
ITEM_X = 5; // 5
|
|
ITEM_Y = 5; // 5
|
|
// Level Timer
|
|
TIME_X = BASEVIDWIDTH - 148; // 172
|
|
TIME_Y = 9; // 9
|
|
// Level Laps
|
|
LAPS_X = 9; // 9
|
|
LAPS_Y = BASEVIDHEIGHT - 29; // 171
|
|
// Position Number
|
|
POSI_X = BASEVIDWIDTH - 9; // 268
|
|
POSI_Y = BASEVIDHEIGHT - 9; // 138
|
|
// Top-Four Faces
|
|
FACE_X = 9; // 9
|
|
FACE_Y = 92; // 92
|
|
// Starting countdown
|
|
STCD_X = BASEVIDWIDTH/2; // 9
|
|
STCD_Y = BASEVIDHEIGHT/2; // 92
|
|
// CHECK graphic
|
|
CHEK_Y = BASEVIDHEIGHT; // 200
|
|
// Minimap
|
|
MINI_X = BASEVIDWIDTH - 50; // 270
|
|
MINI_Y = (BASEVIDHEIGHT/2)-16; // 84
|
|
// Battle WANTED poster
|
|
WANT_X = BASEVIDWIDTH - 55; // 270
|
|
WANT_Y = BASEVIDHEIGHT- 71; // 176
|
|
|
|
// trick COOL
|
|
TCOOL_X = (BASEVIDWIDTH)/2;
|
|
TCOOL_Y = (BASEVIDHEIGHT)/2 -10;
|
|
|
|
if (r_splitscreen) // Splitscreen
|
|
{
|
|
ITEM_X = 5;
|
|
ITEM_Y = 3;
|
|
|
|
LAPS_Y = (BASEVIDHEIGHT/2)-24;
|
|
|
|
POSI_Y = (BASEVIDHEIGHT/2)- 2;
|
|
|
|
STCD_Y = BASEVIDHEIGHT/4;
|
|
|
|
MINI_X -= 16;
|
|
MINI_Y = (BASEVIDHEIGHT/2);
|
|
|
|
if (r_splitscreen > 1) // 3P/4P Small Splitscreen
|
|
{
|
|
// 1P (top left)
|
|
ITEM_X = -9;
|
|
ITEM_Y = -8;
|
|
|
|
LAPS_X = 3;
|
|
LAPS_Y = (BASEVIDHEIGHT/2)-12;
|
|
|
|
POSI_X = 24;
|
|
POSI_Y = (BASEVIDHEIGHT/2)-26;
|
|
|
|
// 2P (top right)
|
|
ITEM2_X = (BASEVIDWIDTH/2)-39;
|
|
ITEM2_Y = -8;
|
|
|
|
LAPS2_X = (BASEVIDWIDTH/2)-43;
|
|
LAPS2_Y = (BASEVIDHEIGHT/2)-12;
|
|
|
|
POSI2_X = (BASEVIDWIDTH/2)-4;
|
|
POSI2_Y = (BASEVIDHEIGHT/2)-26;
|
|
|
|
// Reminder that 3P and 4P are just 1P and 2P splitscreen'd to the bottom.
|
|
|
|
STCD_X = BASEVIDWIDTH/4;
|
|
|
|
MINI_X = (3*BASEVIDWIDTH/4);
|
|
MINI_Y = (3*BASEVIDHEIGHT/4);
|
|
|
|
TCOOL_X = (BASEVIDWIDTH)/4;
|
|
|
|
if (r_splitscreen > 2) // 4P-only
|
|
{
|
|
MINI_X = (BASEVIDWIDTH/2);
|
|
MINI_Y = (BASEVIDHEIGHT/2);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void K_DrawMapThumbnail(fixed_t x, fixed_t y, fixed_t width, UINT32 flags, UINT16 map, const UINT8 *colormap)
|
|
{
|
|
patch_t *PictureOfLevel = NULL;
|
|
|
|
if (map >= nummapheaders || !mapheaderinfo[map])
|
|
{
|
|
PictureOfLevel = nolvl;
|
|
}
|
|
else if (!mapheaderinfo[map]->thumbnailPic)
|
|
{
|
|
PictureOfLevel = blanklvl;
|
|
}
|
|
else
|
|
{
|
|
PictureOfLevel = static_cast<patch_t*>(mapheaderinfo[map]->thumbnailPic);
|
|
}
|
|
|
|
K_DrawLikeMapThumbnail(x, y, width, flags, PictureOfLevel, colormap);
|
|
}
|
|
|
|
void K_DrawLikeMapThumbnail(fixed_t x, fixed_t y, fixed_t width, UINT32 flags, patch_t *patch, const UINT8 *colormap)
|
|
{
|
|
if (flags & V_FLIP)
|
|
x += width;
|
|
|
|
V_DrawFixedPatch(
|
|
x, y,
|
|
FixedDiv(width, (320 << FRACBITS)),
|
|
flags,
|
|
patch,
|
|
colormap
|
|
);
|
|
}
|
|
|
|
// see also K_DrawNameTagItemSpy
|
|
static void K_drawKartItem(void)
|
|
{
|
|
// ITEM_X = BASEVIDWIDTH-50; // 270
|
|
// ITEM_Y = 24; // 24
|
|
|
|
// Why write V_DrawScaledPatch calls over and over when they're all the same?
|
|
// Set to 'no item' just in case.
|
|
const UINT8 offset = ((r_splitscreen > 1) ? 1 : 0);
|
|
patch_t *localpatch[3] = { kp_nodraw, kp_nodraw, kp_nodraw };
|
|
UINT8 localamt[3] = {0, 0, 0};
|
|
patch_t *localbg = ((offset) ? kp_itembg[2] : kp_itembg[0]);
|
|
patch_t *localinv = ((offset) ? kp_invincibility[((leveltime % (6*3)) / 3) + 7] : kp_invincibility[(leveltime % (7*3)) / 3]);
|
|
INT32 fx = 0, fy = 0, fflags = 0; // final coords for hud and flags...
|
|
const INT32 numberdisplaymin = ((!offset && stplyr->itemtype == KITEM_ORBINAUT) ? 5 : 2);
|
|
INT32 itembar = 0;
|
|
INT32 maxl = 0; // itembar's normal highest value
|
|
const INT32 barlength = (offset ? 12 : 26);
|
|
skincolornum_t localcolor[3] = { static_cast<skincolornum_t>(stplyr->skincolor) };
|
|
SINT8 colormode[3] = { TC_RAINBOW };
|
|
boolean flipamount = false; // Used for 3P/4P splitscreen to flip item amount stuff
|
|
|
|
fixed_t rouletteOffset = 0;
|
|
fixed_t rouletteSpace = ROULETTE_SPACING;
|
|
vector2_t rouletteCrop = {7, 7};
|
|
INT32 i;
|
|
|
|
if (stplyr->itemRoulette.itemListLen > 0)
|
|
{
|
|
// Init with item roulette stuff.
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
const SINT8 indexOfs = i-1;
|
|
const size_t index = (stplyr->itemRoulette.itemListLen + (stplyr->itemRoulette.index + indexOfs)) % stplyr->itemRoulette.itemListLen;
|
|
|
|
const SINT8 result = stplyr->itemRoulette.itemList[index];
|
|
const SINT8 item = K_ItemResultToType(result);
|
|
const UINT8 amt = K_ItemResultToAmount(result);
|
|
|
|
switch (item)
|
|
{
|
|
case KITEM_INVINCIBILITY:
|
|
localpatch[i] = localinv;
|
|
localamt[i] = amt;
|
|
break;
|
|
|
|
case KITEM_ORBINAUT:
|
|
localpatch[i] = kp_orbinaut[(offset ? 4 : std::min(amt-1, 3))];
|
|
if (amt > 4)
|
|
localamt[i] = amt;
|
|
break;
|
|
|
|
default:
|
|
localpatch[i] = K_GetCachedItemPatch(item, offset);
|
|
if (item != KITEM_BALLHOG || amt != 5)
|
|
localamt[i] = amt;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (stplyr->itemRoulette.active == true)
|
|
{
|
|
rouletteOffset = K_GetRouletteOffset(&stplyr->itemRoulette, rendertimefrac, 0);
|
|
}
|
|
else
|
|
{
|
|
// I'm doing this a little weird and drawing mostly in reverse order
|
|
// The only actual reason is to make sneakers line up this way in the code below
|
|
// This shouldn't have any actual baring over how it functions
|
|
// Hyudoro is first, because we're drawing it on top of the player's current item
|
|
|
|
localcolor[1] = SKINCOLOR_NONE;
|
|
rouletteOffset = stplyr->karthud[khud_rouletteoffset];
|
|
|
|
if (stplyr->stealingtimer < 0)
|
|
{
|
|
if (leveltime & 2)
|
|
localpatch[1] = kp_hyudoro[offset];
|
|
else
|
|
localpatch[1] = kp_nodraw;
|
|
}
|
|
else if ((stplyr->stealingtimer > 0) && (leveltime & 2))
|
|
{
|
|
localpatch[1] = kp_hyudoro[offset];
|
|
}
|
|
else if (stplyr->eggmanexplode > 1)
|
|
{
|
|
if (leveltime & 1)
|
|
localpatch[1] = kp_eggman[offset];
|
|
else
|
|
localpatch[1] = kp_nodraw;
|
|
}
|
|
else if (stplyr->ballhogcharge > 0)
|
|
{
|
|
// itembar = stplyr->ballhogcharge;
|
|
// maxl = (((stplyr->itemamount-1) * BALLHOGINCREMENT) + 1);
|
|
|
|
itembar = stplyr->ballhogcharge % BALLHOGINCREMENT;
|
|
maxl = BALLHOGINCREMENT;
|
|
|
|
if (leveltime & 1)
|
|
localpatch[1] = kp_ballhog[offset];
|
|
else
|
|
localpatch[1] = kp_nodraw;
|
|
}
|
|
else if (stplyr->rocketsneakertimer > 1)
|
|
{
|
|
itembar = stplyr->rocketsneakertimer;
|
|
maxl = (itemtime*3) - barlength;
|
|
|
|
if (leveltime & 1)
|
|
localpatch[1] = kp_rocketsneaker[offset];
|
|
else
|
|
localpatch[1] = kp_nodraw;
|
|
}
|
|
else if (stplyr->sadtimer > 0)
|
|
{
|
|
if (leveltime & 2)
|
|
localpatch[1] = kp_sadface[offset];
|
|
else
|
|
localpatch[1] = kp_nodraw;
|
|
}
|
|
else if (stplyr->itemRoulette.reserved > 0)
|
|
{
|
|
localpatch[1] = kp_nodraw;
|
|
}
|
|
else
|
|
{
|
|
if (stplyr->itemamount <= 0)
|
|
return;
|
|
|
|
switch (stplyr->itemtype)
|
|
{
|
|
case KITEM_INVINCIBILITY:
|
|
localpatch[1] = localinv;
|
|
localbg = kp_itembg[offset+1];
|
|
break;
|
|
|
|
case KITEM_ORBINAUT:
|
|
localpatch[1] = kp_orbinaut[(offset ? 4 : std::min(stplyr->itemamount-1, 3))];
|
|
break;
|
|
|
|
case KITEM_SPB:
|
|
case KITEM_LIGHTNINGSHIELD:
|
|
case KITEM_BUBBLESHIELD:
|
|
case KITEM_FLAMESHIELD:
|
|
localbg = kp_itembg[offset+1];
|
|
/*FALLTHRU*/
|
|
|
|
default:
|
|
localpatch[1] = K_GetCachedItemPatch(stplyr->itemtype, offset);
|
|
|
|
if (localpatch[1] == NULL)
|
|
localpatch[1] = kp_nodraw; // diagnose underflows
|
|
break;
|
|
}
|
|
|
|
if ((stplyr->itemflags & IF_ITEMOUT) && !(leveltime & 1))
|
|
localpatch[1] = kp_nodraw;
|
|
}
|
|
|
|
if (stplyr->karthud[khud_itemblink] && (leveltime & 1))
|
|
{
|
|
colormode[1] = TC_BLINK;
|
|
|
|
switch (stplyr->karthud[khud_itemblinkmode])
|
|
{
|
|
case 2:
|
|
localcolor[1] = static_cast<skincolornum_t>(K_RainbowColor(leveltime));
|
|
break;
|
|
case 1:
|
|
localcolor[1] = SKINCOLOR_RED;
|
|
break;
|
|
default:
|
|
localcolor[1] = SKINCOLOR_WHITE;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Hide the other items.
|
|
// Effectively lets the other roulette items
|
|
// show flicker away after you select.
|
|
localpatch[0] = localpatch[2] = kp_nodraw;
|
|
}
|
|
}
|
|
|
|
// pain and suffering defined below
|
|
if (offset)
|
|
{
|
|
if (!(R_GetViewNumber() & 1)) // If we are P1 or P3...
|
|
{
|
|
fx = ITEM_X;
|
|
fy = ITEM_Y;
|
|
fflags = V_SNAPTOLEFT|V_SNAPTOTOP|V_SPLITSCREEN;
|
|
}
|
|
else // else, that means we're P2 or P4.
|
|
{
|
|
fx = ITEM2_X;
|
|
fy = ITEM2_Y;
|
|
fflags = V_SNAPTORIGHT|V_SNAPTOTOP|V_SPLITSCREEN;
|
|
flipamount = true;
|
|
}
|
|
|
|
rouletteSpace = ROULETTE_SPACING_SPLITSCREEN;
|
|
rouletteOffset = FixedMul(rouletteOffset, FixedDiv(ROULETTE_SPACING_SPLITSCREEN, ROULETTE_SPACING));
|
|
rouletteCrop.x = 16;
|
|
rouletteCrop.y = 15;
|
|
}
|
|
else
|
|
{
|
|
fx = ITEM_X;
|
|
fy = ITEM_Y;
|
|
fflags = V_SNAPTOTOP|V_SNAPTOLEFT|V_SPLITSCREEN;
|
|
}
|
|
|
|
if (r_splitscreen == 1)
|
|
{
|
|
fy -= 5;
|
|
}
|
|
|
|
V_DrawScaledPatch(fx, fy, V_HUDTRANS|V_SLIDEIN|fflags, localbg);
|
|
|
|
// Need to draw these in a particular order, for sorting.
|
|
V_SetClipRect(
|
|
(fx + rouletteCrop.x) << FRACBITS, (fy + rouletteCrop.y) << FRACBITS,
|
|
rouletteSpace, rouletteSpace,
|
|
V_SLIDEIN|fflags
|
|
);
|
|
|
|
auto draw_item = [&](fixed_t y, int i)
|
|
{
|
|
const UINT8 *colormap = (localcolor[i] ? R_GetTranslationColormap(colormode[i], localcolor[i], GTC_CACHE) : NULL);
|
|
V_DrawFixedPatch(
|
|
fx<<FRACBITS, (fy<<FRACBITS) + rouletteOffset + y,
|
|
FRACUNIT, V_HUDTRANS|V_SLIDEIN|fflags,
|
|
localpatch[i], colormap
|
|
);
|
|
if (localamt[i] > 1)
|
|
{
|
|
using srb2::Draw;
|
|
Draw(
|
|
fx + rouletteCrop.x + FixedToFloat(rouletteSpace/2),
|
|
fy + rouletteCrop.y + FixedToFloat(rouletteOffset + y + rouletteSpace) - (r_splitscreen > 1 ? 15 : 33))
|
|
.font(r_splitscreen > 1 ? Draw::Font::kRollingNum4P : Draw::Font::kRollingNum)
|
|
.align(Draw::Align::kCenter)
|
|
.flags(V_HUDTRANS|V_SLIDEIN|fflags)
|
|
.colormap(colormap)
|
|
.text("{}", localamt[i]);
|
|
}
|
|
};
|
|
|
|
draw_item(rouletteSpace, 0);
|
|
draw_item(-rouletteSpace, 2);
|
|
|
|
if (stplyr->itemRoulette.active == true)
|
|
{
|
|
// Draw the item underneath the box.
|
|
draw_item(0, 1);
|
|
V_ClearClipRect();
|
|
}
|
|
else
|
|
{
|
|
// Draw the item above the box.
|
|
V_ClearClipRect();
|
|
|
|
// A little goofy, but helps with ballhog charge conveyance—you're "loading" them.
|
|
UINT8 fakeitemamount = stplyr->itemamount - (stplyr->ballhogcharge / BALLHOGINCREMENT);
|
|
|
|
if (fakeitemamount >= numberdisplaymin && stplyr->itemRoulette.active == false)
|
|
{
|
|
// Then, the numbers:
|
|
V_DrawScaledPatch(
|
|
fx + (flipamount ? 48 : 0), fy,
|
|
V_HUDTRANS|V_SLIDEIN|fflags|(flipamount ? V_FLIP : 0),
|
|
kp_itemmulsticker[offset]
|
|
); // flip this graphic for p2 and p4 in split and shift it.
|
|
|
|
V_DrawFixedPatch(
|
|
fx<<FRACBITS, (fy<<FRACBITS) + rouletteOffset,
|
|
FRACUNIT, V_HUDTRANS|V_SLIDEIN|fflags,
|
|
localpatch[1], (localcolor[1] ? R_GetTranslationColormap(colormode[1], localcolor[1], GTC_CACHE) : NULL)
|
|
);
|
|
|
|
if (offset)
|
|
{
|
|
if (flipamount) // reminder that this is for 3/4p's right end of the screen.
|
|
V_DrawString(fx+2, fy+31, V_HUDTRANS|V_SLIDEIN|fflags, va("x%d", fakeitemamount));
|
|
else
|
|
V_DrawString(fx+24, fy+31, V_HUDTRANS|V_SLIDEIN|fflags, va("x%d", fakeitemamount));
|
|
}
|
|
else
|
|
{
|
|
V_DrawScaledPatch(fy+28, fy+41, V_HUDTRANS|V_SLIDEIN|fflags, kp_itemx);
|
|
V_DrawTimerString(fx+38, fy+36, V_HUDTRANS|V_SLIDEIN|fflags, va("%d", fakeitemamount));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
V_DrawFixedPatch(
|
|
fx<<FRACBITS, (fy<<FRACBITS) + rouletteOffset,
|
|
FRACUNIT, V_HUDTRANS|V_SLIDEIN|fflags,
|
|
localpatch[1], (localcolor[1] ? R_GetTranslationColormap(colormode[1], localcolor[1], GTC_CACHE) : NULL)
|
|
);
|
|
}
|
|
}
|
|
|
|
// Extensible meter, currently only used for rocket sneaker...
|
|
if (itembar)
|
|
{
|
|
const INT32 fill = ((itembar*barlength)/maxl);
|
|
const INT32 length = std::min(barlength, fill);
|
|
const INT32 height = (offset ? 1 : 2);
|
|
const INT32 x = (offset ? 17 : 11), y = (offset ? 27 : 35);
|
|
|
|
V_DrawScaledPatch(fx+x, fy+y, V_HUDTRANS|V_SLIDEIN|fflags, kp_itemtimer[offset]);
|
|
// The left dark "AA" edge
|
|
V_DrawFill(fx+x+1, fy+y+1, (length == 2 ? 2 : 1), height, 12|fflags);
|
|
// The bar itself
|
|
if (length > 2)
|
|
{
|
|
V_DrawFill(fx+x+length, fy+y+1, 1, height, 12|fflags); // the right one
|
|
if (height == 2)
|
|
V_DrawFill(fx+x+2, fy+y+2, length-2, 1, 8|fflags); // the dulled underside
|
|
V_DrawFill(fx+x+2, fy+y+1, length-2, 1, 0|fflags); // the shine
|
|
}
|
|
}
|
|
|
|
// Quick Eggman numbers
|
|
if (stplyr->eggmanexplode > 1)
|
|
V_DrawScaledPatch(fx+17, fy+13-offset, V_HUDTRANS|V_SLIDEIN|fflags, kp_eggnum[std::min(5, G_TicsToSeconds(stplyr->eggmanexplode))]);
|
|
|
|
if (stplyr->itemtype == KITEM_FLAMESHIELD && stplyr->flamelength > 0)
|
|
{
|
|
INT32 numframes = FLAMESHIELD_MAX;
|
|
INT32 absolutemax = numframes;
|
|
INT32 flamemax = stplyr->flamelength;
|
|
INT32 flamemeter = std::min(static_cast<INT32>(stplyr->flamemeter), flamemax);
|
|
|
|
INT32 bf = numframes - stplyr->flamelength;
|
|
INT32 ff = numframes - ((flamemeter * numframes) / absolutemax);
|
|
|
|
INT32 xo = 6, yo = 4;
|
|
INT32 flip = 0;
|
|
|
|
if (offset)
|
|
{
|
|
xo++;
|
|
|
|
if (!(R_GetViewNumber() & 1)) // Flip for P1 and P3 (yes, that's correct)
|
|
{
|
|
xo -= 62;
|
|
flip = V_FLIP;
|
|
}
|
|
}
|
|
|
|
/*
|
|
INT32 fmin = (8 * (bf-1));
|
|
if (ff < fmin)
|
|
ff = fmin;
|
|
*/
|
|
|
|
if (bf >= 0 && bf < numframes)
|
|
V_DrawScaledPatch(fx-xo, fy-yo, V_HUDTRANS|V_SLIDEIN|fflags|flip, kp_flameshieldmeter_bg[bf][offset]);
|
|
|
|
if (ff >= 0 && ff < numframes && stplyr->flamemeter > 0)
|
|
{
|
|
if ((stplyr->flamemeter > flamemax) && (leveltime & 1))
|
|
{
|
|
UINT8 *fsflash = R_GetTranslationColormap(TC_BLINK, SKINCOLOR_WHITE, GTC_CACHE);
|
|
V_DrawMappedPatch(fx-xo, fy-yo, V_HUDTRANS|V_SLIDEIN|fflags|flip, kp_flameshieldmeter[ff][offset], fsflash);
|
|
}
|
|
else
|
|
{
|
|
V_DrawScaledPatch(fx-xo, fy-yo, V_HUDTRANS|V_SLIDEIN|fflags|flip, kp_flameshieldmeter[ff][offset]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void K_drawKartSlotMachine(void)
|
|
{
|
|
// ITEM_X = BASEVIDWIDTH-50; // 270
|
|
// ITEM_Y = 24; // 24
|
|
|
|
// Why write V_DrawScaledPatch calls over and over when they're all the same?
|
|
// Set to 'no item' just in case.
|
|
const UINT8 offset = ((r_splitscreen > 1) ? 1 : 0);
|
|
|
|
patch_t *localpatch[3] = { kp_nodraw, kp_nodraw, kp_nodraw };
|
|
patch_t *localbg = offset ? kp_ringbg[1] : kp_ringbg[0];
|
|
|
|
// == SHITGARBAGE UNLIMITED 2: RISE OF MY ASS ==
|
|
// FIVE LAYERS OF BULLSHIT PER-PIXEL SHOVING BECAUSE THE PATCHES HAVE DIFFERENT OFFSETS
|
|
// IF YOU ARE HERE TO ADJUST THE RINGBOX HUD TURN OFF YOUR COMPUTER AND GO TO YOUR LOCAL PARK
|
|
|
|
INT32 fx = 0, fy = 0, fflags = 0; // final coords for hud and flags...
|
|
INT32 boxoffx = 0;
|
|
INT32 boxoffy = -6;
|
|
INT32 vstretch = 0;
|
|
INT32 hstretch = 3;
|
|
INT32 splitbsx = 0, splitbsy = 0;
|
|
skincolornum_t localcolor[3] = { static_cast<skincolornum_t>(stplyr->skincolor) };
|
|
SINT8 colormode[3] = { TC_RAINBOW };
|
|
|
|
fixed_t rouletteOffset = 0;
|
|
fixed_t rouletteSpace = SLOT_SPACING;
|
|
vector2_t rouletteCrop = {10, 10};
|
|
INT32 i;
|
|
|
|
if (stplyr->itemRoulette.itemListLen > 0)
|
|
{
|
|
// Init with item roulette stuff.
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
const SINT8 indexOfs = i-1;
|
|
const size_t index = (stplyr->itemRoulette.itemListLen + (stplyr->itemRoulette.index + indexOfs)) % stplyr->itemRoulette.itemListLen;
|
|
|
|
const SINT8 result = stplyr->itemRoulette.itemList[index];
|
|
|
|
localpatch[i] = K_GetCachedSlotMachinePatch(result, offset);
|
|
}
|
|
}
|
|
|
|
if (stplyr->itemRoulette.active == true)
|
|
{
|
|
rouletteOffset = K_GetSlotOffset(&stplyr->itemRoulette, rendertimefrac, 0);
|
|
}
|
|
else
|
|
{
|
|
rouletteOffset = stplyr->karthud[khud_rouletteoffset];
|
|
|
|
if (!stplyr->ringboxdelay)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (stplyr->karthud[khud_itemblink] && (leveltime & 1))
|
|
{
|
|
colormode[1] = TC_BLINK;
|
|
localcolor[1] = SKINCOLOR_WHITE;
|
|
|
|
// This looks kinda wild with the white-background patch.
|
|
/*
|
|
switch (stplyr->ringboxaward)
|
|
{
|
|
case 5: // JACKPOT!
|
|
localcolor[1] = K_RainbowColor(leveltime);
|
|
break;
|
|
default:
|
|
localcolor[1] = SKINCOLOR_WHITE;
|
|
break;
|
|
}
|
|
*/
|
|
}
|
|
|
|
// pain and suffering defined below
|
|
if (offset)
|
|
{
|
|
boxoffx -= 4;
|
|
if (!(R_GetViewNumber() & 1)) // If we are P1 or P3...
|
|
{
|
|
fx = ITEM_X + 10;
|
|
fy = ITEM_Y + 10;
|
|
fflags = V_SNAPTOLEFT|V_SNAPTOTOP|V_SPLITSCREEN;
|
|
}
|
|
else // else, that means we're P2 or P4.
|
|
{
|
|
fx = ITEM2_X + 7;
|
|
fy = ITEM2_Y + 10;
|
|
fflags = V_SNAPTORIGHT|V_SNAPTOTOP|V_SPLITSCREEN;
|
|
}
|
|
|
|
rouletteSpace = SLOT_SPACING_SPLITSCREEN;
|
|
rouletteOffset = FixedMul(rouletteOffset, FixedDiv(SLOT_SPACING_SPLITSCREEN, SLOT_SPACING));
|
|
rouletteCrop.x = 16;
|
|
rouletteCrop.y = 13;
|
|
splitbsx = -6;
|
|
splitbsy = -6;
|
|
boxoffy += 2;
|
|
hstretch = 0;
|
|
}
|
|
else
|
|
{
|
|
fx = ITEM_X;
|
|
fy = ITEM_Y;
|
|
fflags = V_SNAPTOTOP|V_SNAPTOLEFT|V_SPLITSCREEN;
|
|
}
|
|
|
|
if (r_splitscreen == 1)
|
|
{
|
|
fy -= 5;
|
|
}
|
|
|
|
V_DrawScaledPatch(fx, fy, V_HUDTRANS|V_SLIDEIN|fflags, localbg);
|
|
|
|
V_SetClipRect(
|
|
((fx + rouletteCrop.x + boxoffx + splitbsx) << FRACBITS), ((fy + rouletteCrop.y + boxoffy - vstretch + splitbsy) << FRACBITS),
|
|
rouletteSpace + (hstretch<<FRACBITS), rouletteSpace + (vstretch<<FRACBITS),
|
|
V_SLIDEIN|fflags
|
|
);
|
|
|
|
// item box has special layering, transparency, different sized patches, other fucked up shit
|
|
// ring box is evenly spaced and easy
|
|
rouletteOffset += rouletteSpace;
|
|
for (i = 0; i < 3; i++)
|
|
{
|
|
V_DrawFixedPatch(
|
|
((fx)<<FRACBITS), ((fy)<<FRACBITS) + rouletteOffset,
|
|
FRACUNIT, V_HUDTRANS|V_SLIDEIN|fflags,
|
|
localpatch[i], (localcolor[i] ? R_GetTranslationColormap(colormode[i], localcolor[i], GTC_CACHE) : NULL)
|
|
);
|
|
|
|
rouletteOffset -= rouletteSpace;
|
|
}
|
|
|
|
V_ClearClipRect();
|
|
}
|
|
|
|
tic_t K_TranslateTimer(tic_t drawtime, UINT8 mode, INT32 *return_jitter)
|
|
{
|
|
INT32 jitter = 0;
|
|
|
|
if (!mode && drawtime != UINT32_MAX)
|
|
{
|
|
if (timelimitintics > 0)
|
|
{
|
|
if (drawtime >= timelimitintics)
|
|
{
|
|
jitter = 2;
|
|
if (drawtime & 2)
|
|
jitter = -jitter;
|
|
drawtime = 0;
|
|
}
|
|
else
|
|
{
|
|
drawtime = timelimitintics - drawtime;
|
|
if (secretextratime)
|
|
;
|
|
else if (extratimeintics)
|
|
{
|
|
jitter = 2;
|
|
if (leveltime & 1)
|
|
jitter = -jitter;
|
|
}
|
|
else if (drawtime <= 5*TICRATE)
|
|
{
|
|
jitter = ((drawtime <= 3*TICRATE) && (((drawtime-1) % TICRATE) >= TICRATE-2))
|
|
? 3 : 1;
|
|
if (drawtime & 2)
|
|
jitter = -jitter;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (return_jitter)
|
|
{
|
|
*return_jitter = jitter;
|
|
}
|
|
|
|
return drawtime;
|
|
}
|
|
|
|
INT32 K_drawKartMicroTime(const char *todrawtext, INT32 workx, INT32 worky, INT32 splitflags)
|
|
{
|
|
using srb2::Draw;
|
|
Draw::TextElement text(todrawtext);
|
|
text.flags(splitflags);
|
|
text.font(Draw::Font::kZVote);
|
|
|
|
INT32 result = text.width();
|
|
Draw(workx - result, worky).text(text);
|
|
|
|
return result;
|
|
}
|
|
|
|
void K_drawKartTimestamp(tic_t drawtime, INT32 TX, INT32 TY, INT32 splitflags, UINT8 mode)
|
|
{
|
|
// TIME_X = BASEVIDWIDTH-124; // 196
|
|
// TIME_Y = 6; // 6
|
|
|
|
INT32 jitter = 0;
|
|
|
|
drawtime = K_TranslateTimer(drawtime, mode, &jitter);
|
|
|
|
V_DrawScaledPatch(TX, TY, splitflags, ((mode == 2) ? kp_lapstickerwide : kp_timestickerwide));
|
|
|
|
TX += 33;
|
|
|
|
if (drawtime == UINT32_MAX)
|
|
;
|
|
else if (mode && !drawtime)
|
|
{
|
|
// apostrophe location _'__ __
|
|
V_DrawTimerString(TX+24, TY+3, splitflags, va("'"));
|
|
|
|
// quotation mark location _ __"__
|
|
V_DrawTimerString(TX+60, TY+3, splitflags, va("\""));
|
|
}
|
|
else
|
|
{
|
|
tic_t worktime = drawtime/(60*TICRATE);
|
|
|
|
if (worktime >= 100)
|
|
{
|
|
jitter = (drawtime & 1 ? 1 : -1);
|
|
worktime = 99;
|
|
drawtime = (100*(60*TICRATE))-1;
|
|
}
|
|
|
|
// minutes time 00 __ __
|
|
V_DrawTimerString(TX, TY+3+jitter, splitflags, va("%d", worktime/10));
|
|
V_DrawTimerString(TX+12, TY+3-jitter, splitflags, va("%d", worktime%10));
|
|
|
|
// apostrophe location _'__ __
|
|
V_DrawTimerString(TX+24, TY+3, splitflags, va("'"));
|
|
|
|
worktime = (drawtime/TICRATE % 60);
|
|
|
|
// seconds time _ 00 __
|
|
V_DrawTimerString(TX+36, TY+3+jitter, splitflags, va("%d", worktime/10));
|
|
V_DrawTimerString(TX+48, TY+3-jitter, splitflags, va("%d", worktime%10));
|
|
|
|
// quotation mark location _ __"__
|
|
V_DrawTimerString(TX+60, TY+3, splitflags, va("\""));
|
|
|
|
worktime = G_TicsToCentiseconds(drawtime);
|
|
|
|
// tics _ __ 00
|
|
V_DrawTimerString(TX+72, TY+3+jitter, splitflags, va("%d", worktime/10));
|
|
V_DrawTimerString(TX+84, TY+3-jitter, splitflags, va("%d", worktime%10));
|
|
}
|
|
|
|
// Medal data!
|
|
if ((modeattacking || (mode == 1))
|
|
&& !demo.playback)
|
|
{
|
|
INT32 workx = TX + 96, worky = TY+18;
|
|
UINT8 i = stickermedalinfo.visiblecount;
|
|
|
|
if (stickermedalinfo.targettext[0] != '\0')
|
|
{
|
|
if (!mode)
|
|
{
|
|
if (stickermedalinfo.jitter)
|
|
{
|
|
jitter = stickermedalinfo.jitter+3;
|
|
if (jitter & 2)
|
|
workx += jitter/4;
|
|
else
|
|
workx -= jitter/4;
|
|
}
|
|
|
|
if (stickermedalinfo.norecord == true)
|
|
{
|
|
splitflags = (splitflags &~ V_HUDTRANS)|V_HUDTRANSHALF;
|
|
}
|
|
}
|
|
|
|
workx -= K_drawKartMicroTime(stickermedalinfo.targettext, workx, worky, splitflags);
|
|
}
|
|
|
|
workx -= (((1 + i - stickermedalinfo.platinumcount)*6) - 1);
|
|
|
|
if (!mode)
|
|
splitflags = (splitflags &~ V_HUDTRANSHALF)|V_HUDTRANS;
|
|
while (i > 0)
|
|
{
|
|
i--;
|
|
|
|
if (gamedata->collected[(stickermedalinfo.emblems[i]-emblemlocations)])
|
|
{
|
|
V_DrawMappedPatch(workx, worky, splitflags,
|
|
static_cast<patch_t*>(W_CachePatchName(M_GetEmblemPatch(stickermedalinfo.emblems[i], false), PU_CACHE)),
|
|
R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(stickermedalinfo.emblems[i]), GTC_CACHE)
|
|
);
|
|
|
|
workx += 6;
|
|
}
|
|
else if (
|
|
stickermedalinfo.emblems[i]->type != ET_TIME
|
|
|| stickermedalinfo.emblems[i]->tag != AUTOMEDAL_PLATINUM
|
|
)
|
|
{
|
|
V_DrawMappedPatch(workx, worky, splitflags,
|
|
static_cast<patch_t*>(W_CachePatchName("NEEDIT", PU_CACHE)),
|
|
NULL
|
|
);
|
|
|
|
workx += 6;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (modeattacking & ATTACKING_SPB && stplyr->SPBdistance > 0)
|
|
{
|
|
UINT8 *colormap = R_GetTranslationColormap(stplyr->skin, static_cast<skincolornum_t>(stplyr->skincolor), GTC_CACHE);
|
|
INT32 ybar = 180;
|
|
INT32 widthbar = 120, xbar = 160 - widthbar/2, currentx;
|
|
INT32 barflags = V_SNAPTOBOTTOM|V_SLIDEIN;
|
|
INT32 transflags = ((6)<<FF_TRANSSHIFT);
|
|
|
|
V_DrawScaledPatch(xbar, ybar - 2, barflags|transflags, kp_wouldyoustillcatchmeifiwereaworm);
|
|
|
|
V_DrawMappedPatch(160 + widthbar/2 - 7, ybar - 7, barflags, faceprefix[stplyr->skin][FACE_MINIMAP], colormap);
|
|
|
|
// vibes-based math
|
|
currentx = (stplyr->SPBdistance/mapobjectscale - mobjinfo[MT_SPB].radius/FRACUNIT - mobjinfo[MT_PLAYER].radius/FRACUNIT) * 6;
|
|
if (currentx > 0)
|
|
{
|
|
currentx = sqrt(currentx);
|
|
if (currentx > widthbar)
|
|
currentx = widthbar;
|
|
}
|
|
else
|
|
{
|
|
currentx = 0;
|
|
}
|
|
V_DrawScaledPatch(160 + widthbar/2 - currentx - 5, ybar - 7, barflags, kp_spbminimap);
|
|
}
|
|
}
|
|
|
|
static fixed_t K_DrawKartPositionNumPatch(UINT8 num, UINT8 splitIndex, UINT8 *color, fixed_t x, fixed_t y, fixed_t scale, INT32 flags)
|
|
{
|
|
fixed_t w = FRACUNIT;
|
|
fixed_t h = FRACUNIT;
|
|
INT32 overlayFlags[2];
|
|
INT32 i;
|
|
|
|
if (num > 9)
|
|
{
|
|
return x; // invalid input
|
|
}
|
|
|
|
if ((mapheaderinfo[gamemap - 1]->levelflags & LF_SUBTRACTNUM) == LF_SUBTRACTNUM)
|
|
{
|
|
overlayFlags[0] = V_SUBTRACT;
|
|
overlayFlags[1] = V_ADD;
|
|
}
|
|
else
|
|
{
|
|
overlayFlags[0] = V_ADD;
|
|
overlayFlags[1] = V_SUBTRACT;
|
|
}
|
|
|
|
w = SHORT(kp_positionnum[num][0][splitIndex]->width) * scale;
|
|
h = SHORT(kp_positionnum[num][0][splitIndex]->height) * scale;
|
|
|
|
x -= w;
|
|
|
|
if (flags & V_SNAPTOBOTTOM)
|
|
{
|
|
y -= h;
|
|
}
|
|
|
|
for (i = 1; i >= 0; i--)
|
|
{
|
|
V_DrawFixedPatch(
|
|
x, y, scale,
|
|
flags | overlayFlags[i],
|
|
kp_positionnum[num][i][splitIndex],
|
|
color
|
|
);
|
|
}
|
|
|
|
return x;
|
|
}
|
|
|
|
void K_DrawKartPositionNumXY(
|
|
UINT8 num,
|
|
UINT8 splitIndex,
|
|
fixed_t fx, fixed_t fy, fixed_t scale, INT32 fflags,
|
|
tic_t counter, boolean subtract,
|
|
boolean exit, boolean lastLap, boolean losing
|
|
)
|
|
{
|
|
counter /= 3; // Alternate colors every three frames
|
|
|
|
UINT8 *color = NULL;
|
|
if (exit && num == 1)
|
|
{
|
|
// 1st place winner? You get rainbows!!
|
|
color = R_GetTranslationColormap(TC_DEFAULT, static_cast<skincolornum_t>(SKINCOLOR_POSNUM_BEST1 + (counter % 6)), GTC_CACHE);
|
|
}
|
|
else if (exit || lastLap)
|
|
{
|
|
// On the final lap, or already won.
|
|
boolean useRedNums = losing;
|
|
|
|
if (subtract)
|
|
{
|
|
// Subtracting RED will look BLUE, and vice versa.
|
|
useRedNums = !useRedNums;
|
|
}
|
|
|
|
if (useRedNums == true)
|
|
{
|
|
color = R_GetTranslationColormap(TC_DEFAULT, static_cast<skincolornum_t>(SKINCOLOR_POSNUM_LOSE1 + (counter % 3)), GTC_CACHE);
|
|
}
|
|
else
|
|
{
|
|
color = R_GetTranslationColormap(TC_DEFAULT, static_cast<skincolornum_t>(SKINCOLOR_POSNUM_WIN1 + (counter % 3)), GTC_CACHE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
color = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_POSNUM, GTC_CACHE);
|
|
}
|
|
|
|
if ((fflags & V_SNAPTORIGHT) == 0)
|
|
{
|
|
UINT8 adjustNum = num;
|
|
do
|
|
{
|
|
fixed_t w = SHORT(kp_positionnum[adjustNum % 10][0][splitIndex]->width) * scale;
|
|
fx += w;
|
|
adjustNum /= 10;
|
|
} while (adjustNum);
|
|
}
|
|
|
|
// Draw the number
|
|
do
|
|
{
|
|
fx = K_DrawKartPositionNumPatch(
|
|
(num % 10), splitIndex, color,
|
|
fx, fy, scale, V_SPLITSCREEN|fflags
|
|
);
|
|
num /= 10;
|
|
} while (num);
|
|
}
|
|
|
|
static void K_DrawKartPositionNum(UINT8 num)
|
|
{
|
|
UINT8 splitIndex = (r_splitscreen > 0) ? 1 : 0;
|
|
fixed_t scale = FRACUNIT;
|
|
fixed_t fx = 0, fy = 0;
|
|
transnum_t trans = static_cast<transnum_t>(0);
|
|
INT32 fflags = 0;
|
|
|
|
if (stplyr->lives <= 0 && stplyr->playerstate == PST_DEAD)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (leveltime < (starttime + NUMTRANSMAPS))
|
|
{
|
|
trans = static_cast<transnum_t>((starttime + NUMTRANSMAPS) - leveltime);
|
|
}
|
|
|
|
if (trans >= NUMTRANSMAPS)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (stplyr->positiondelay > 0 || K_PlayerTallyActive(stplyr) == true)
|
|
{
|
|
const UINT8 delay = (stplyr->exiting) ? POS_DELAY_TIME : stplyr->positiondelay;
|
|
const fixed_t add = (scale * 3) >> ((r_splitscreen == 1) ? 1 : 2);
|
|
scale += std::min((add * (delay * delay)) / (POS_DELAY_TIME * POS_DELAY_TIME), add);
|
|
}
|
|
|
|
// pain and suffering defined below
|
|
if (!r_splitscreen)
|
|
{
|
|
fx = BASEVIDWIDTH << FRACBITS;
|
|
fy = BASEVIDHEIGHT << FRACBITS;
|
|
fflags = V_SNAPTOBOTTOM|V_SNAPTORIGHT;
|
|
}
|
|
else if (r_splitscreen == 1) // for this splitscreen, we'll use case by case because it's a bit different.
|
|
{
|
|
fx = BASEVIDWIDTH << FRACBITS;
|
|
|
|
if (R_GetViewNumber() == 0)
|
|
{
|
|
// for player 1: display this at the top right, above the minimap.
|
|
fy = 0;
|
|
fflags = V_SNAPTOTOP|V_SNAPTORIGHT;
|
|
}
|
|
else
|
|
{
|
|
// if we're not p1, that means we're p2. display this at the bottom right, below the minimap.
|
|
fy = BASEVIDHEIGHT << FRACBITS;
|
|
fflags = V_SNAPTOBOTTOM|V_SNAPTORIGHT;
|
|
}
|
|
|
|
fy >>= 1;
|
|
}
|
|
else
|
|
{
|
|
fy = BASEVIDHEIGHT << FRACBITS;
|
|
|
|
if (!(R_GetViewNumber() & 1)) // If we are P1 or P3...
|
|
{
|
|
// If we are P1 or P3...
|
|
fx = 0;
|
|
fflags = V_SNAPTOLEFT|V_SNAPTOBOTTOM;
|
|
}
|
|
else
|
|
{
|
|
// else, that means we're P2 or P4.
|
|
fx = BASEVIDWIDTH << FRACBITS;
|
|
fflags = V_SNAPTORIGHT|V_SNAPTOBOTTOM;
|
|
}
|
|
|
|
fx >>= 1;
|
|
fy >>= 1;
|
|
|
|
// We're putting it in the same corner as
|
|
// the rest of our HUD, so it needs raised.
|
|
fy -= (21 << FRACBITS);
|
|
}
|
|
|
|
if (trans > 0)
|
|
{
|
|
fflags |= (trans << V_ALPHASHIFT);
|
|
}
|
|
|
|
K_DrawKartPositionNumXY(
|
|
num,
|
|
splitIndex,
|
|
fx, fy, scale, fflags,
|
|
leveltime,
|
|
((mapheaderinfo[gamemap - 1]->levelflags & LF_SUBTRACTNUM) == LF_SUBTRACTNUM),
|
|
stplyr->exiting,
|
|
(stplyr->laps >= numlaps),
|
|
K_IsPlayerLosing(stplyr)
|
|
);
|
|
}
|
|
|
|
struct PositionFacesInfo
|
|
{
|
|
INT32 ranklines = 0;
|
|
INT32 strank = -1;
|
|
INT32 numplayersingame = 0;
|
|
INT32 rankplayer[MAXPLAYERS] = {};
|
|
|
|
PositionFacesInfo();
|
|
void draw_1p();
|
|
void draw_4p_battle(int x, int y, INT32 flags);
|
|
|
|
player_t* top() const { return &players[rankplayer[0]]; }
|
|
UINT32 top_score() const { return top()->roundscore; }
|
|
|
|
bool near_goal() const
|
|
{
|
|
if (g_pointlimit == 0)
|
|
return false;
|
|
constexpr tic_t kThreshold = 5;
|
|
return std::max(kThreshold, g_pointlimit) - kThreshold <= top_score();
|
|
}
|
|
|
|
skincolornum_t vomit_color() const
|
|
{
|
|
if (!near_goal())
|
|
{
|
|
return static_cast<skincolornum_t>(top()->skincolor);
|
|
}
|
|
|
|
constexpr int kCycleSpeed = 4;
|
|
constexpr std::array<skincolornum_t, 6> kColors = {
|
|
SKINCOLOR_RED,
|
|
SKINCOLOR_VOMIT,
|
|
SKINCOLOR_YELLOW,
|
|
SKINCOLOR_GREEN,
|
|
SKINCOLOR_JET,
|
|
SKINCOLOR_MOONSET,
|
|
};
|
|
return kColors[leveltime / kCycleSpeed % kColors.size()];
|
|
}
|
|
};
|
|
|
|
PositionFacesInfo::PositionFacesInfo()
|
|
{
|
|
INT32 i, j;
|
|
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
{
|
|
rankplayer[i] = -1;
|
|
|
|
if (!playeringame[i] || players[i].spectator || !players[i].mo)
|
|
continue;
|
|
|
|
numplayersingame++;
|
|
}
|
|
|
|
if (numplayersingame <= 1)
|
|
return;
|
|
|
|
boolean completed[MAXPLAYERS] = {};
|
|
|
|
for (j = 0; j < numplayersingame; j++)
|
|
{
|
|
UINT8 lowestposition = MAXPLAYERS+1;
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
{
|
|
if (completed[i] || !playeringame[i] || players[i].spectator || !players[i].mo)
|
|
continue;
|
|
|
|
if (players[i].position >= lowestposition)
|
|
continue;
|
|
|
|
rankplayer[ranklines] = i;
|
|
lowestposition = players[i].position;
|
|
}
|
|
|
|
i = rankplayer[ranklines];
|
|
|
|
completed[i] = true;
|
|
|
|
if (players+i == stplyr)
|
|
strank = ranklines;
|
|
|
|
//if (ranklines == 5)
|
|
//break; // Only draw the top 5 players -- we do this a different way now...
|
|
|
|
ranklines++;
|
|
}
|
|
}
|
|
|
|
void PositionFacesInfo::draw_1p()
|
|
{
|
|
// FACE_X = 15; // 15
|
|
// FACE_Y = 72; // 72
|
|
|
|
INT32 Y = FACE_Y-9; // -9 to offset where it's being drawn if there are more than one
|
|
INT32 i, j;
|
|
INT32 bumperx, emeraldx;
|
|
INT32 xoff, yoff, flipflag = 0;
|
|
UINT8 workingskin;
|
|
UINT8 *colormap;
|
|
UINT32 skinflags;
|
|
|
|
if (gametyperules & GTR_POINTLIMIT) // playing battle
|
|
{
|
|
Y += 40;
|
|
if (ranklines < 3)
|
|
Y -= 18;
|
|
}
|
|
else if (ranklines < 5)
|
|
Y += (9*ranklines);
|
|
else
|
|
Y += (9*5);
|
|
|
|
ranklines--;
|
|
i = ranklines;
|
|
|
|
if (gametyperules & GTR_POINTLIMIT) // playing battle
|
|
{
|
|
// 3 lines max in Battle
|
|
if (i > 2)
|
|
i = 2;
|
|
ranklines = 0;
|
|
|
|
// You must appear on the leaderboard, even if you don't rank top 3
|
|
if (strank > i)
|
|
{
|
|
strank = i;
|
|
rankplayer[strank] = stplyr - players;
|
|
}
|
|
|
|
// Draw GOAL
|
|
bool skull = g_pointlimit && (g_pointlimit <= stplyr->roundscore);
|
|
INT32 height = i*18;
|
|
INT32 GOAL_Y = Y-height;
|
|
|
|
colormap = nullptr;
|
|
|
|
if (skincolornum_t vomit = vomit_color())
|
|
{
|
|
colormap = R_GetTranslationColormap(TC_DEFAULT, vomit, GTC_CACHE);
|
|
}
|
|
|
|
V_DrawMappedPatch(FACE_X-5, GOAL_Y-32, V_HUDTRANS|V_SLIDEIN|V_SNAPTOLEFT, kp_goal[skull][0], colormap);
|
|
|
|
// Flashing KO
|
|
if (skull)
|
|
{
|
|
if (leveltime % 16 < 8)
|
|
V_DrawScaledPatch(FACE_X-5, GOAL_Y-32, V_HUDTRANS|V_SLIDEIN|V_SNAPTOLEFT, kp_goaltext1p);
|
|
}
|
|
else if (g_pointlimit)
|
|
{
|
|
using srb2::Draw;
|
|
Draw(FACE_X+8.5, GOAL_Y-15)
|
|
.font(Draw::Font::kZVote)
|
|
.align(Draw::Align::kCenter)
|
|
.flags(V_HUDTRANS|V_SLIDEIN|V_SNAPTOLEFT)
|
|
.text("{:02}", g_pointlimit);
|
|
}
|
|
|
|
// Line cutting behind rank faces
|
|
V_DrawScaledPatch(FACE_X+6, GOAL_Y, V_HUDTRANS|V_SLIDEIN|V_SNAPTOLEFT, kp_goalrod[0]);
|
|
}
|
|
else if (strank <= 2) // too close to the top, or a spectator? would have had (strank == -1) called out, but already caught by (strank <= 2)
|
|
{
|
|
if (i > 4) // could be both...
|
|
i = 4;
|
|
ranklines = 0;
|
|
}
|
|
else if (strank+2 >= ranklines) // too close to the bottom?
|
|
{
|
|
ranklines -= 4;
|
|
if (ranklines < 0)
|
|
ranklines = 0;
|
|
}
|
|
else
|
|
{
|
|
i = strank+2;
|
|
ranklines = strank-2;
|
|
}
|
|
|
|
for (; i >= ranklines; i--)
|
|
{
|
|
if (!playeringame[rankplayer[i]]) continue;
|
|
if (players[rankplayer[i]].spectator) continue;
|
|
if (!players[rankplayer[i]].mo) continue;
|
|
|
|
bumperx = FACE_X+19;
|
|
emeraldx = FACE_X+16;
|
|
|
|
skinflags = (demo.playback)
|
|
? demo.skinlist[demo.currentskinid[rankplayer[i]]].flags
|
|
: skins[players[rankplayer[i]].skin].flags;
|
|
|
|
// Flip SF_IRONMAN portraits, but only if they're transformed
|
|
if (skinflags & SF_IRONMAN
|
|
&& !(players[rankplayer[i]].charflags & SF_IRONMAN) )
|
|
{
|
|
flipflag = V_FLIP|V_VFLIP; // blonic flip
|
|
xoff = yoff = 16;
|
|
} else
|
|
{
|
|
flipflag = 0;
|
|
xoff = yoff = 0;
|
|
}
|
|
|
|
if (players[rankplayer[i]].mo->color)
|
|
{
|
|
if ((skin_t*)players[rankplayer[i]].mo->skin)
|
|
workingskin = (skin_t*)players[rankplayer[i]].mo->skin - skins;
|
|
else
|
|
workingskin = players[rankplayer[i]].skin;
|
|
|
|
colormap = R_GetTranslationColormap(workingskin, static_cast<skincolornum_t>(players[rankplayer[i]].mo->color), GTC_CACHE);
|
|
if (players[rankplayer[i]].mo->colorized)
|
|
colormap = R_GetTranslationColormap(TC_RAINBOW, static_cast<skincolornum_t>(players[rankplayer[i]].mo->color), GTC_CACHE);
|
|
else
|
|
colormap = R_GetTranslationColormap(workingskin, static_cast<skincolornum_t>(players[rankplayer[i]].mo->color), GTC_CACHE);
|
|
|
|
V_DrawMappedPatch(FACE_X + xoff, Y + yoff, V_HUDTRANS|V_SLIDEIN|V_SNAPTOLEFT|flipflag, faceprefix[workingskin][FACE_RANK], colormap);
|
|
|
|
if (LUA_HudEnabled(hud_battlebumpers))
|
|
{
|
|
const UINT8 bumpers = K_Bumpers(&players[rankplayer[i]]);
|
|
|
|
if (bumpers > 0)
|
|
{
|
|
V_DrawMappedPatch(bumperx-2, Y, V_HUDTRANS|V_SLIDEIN|V_SNAPTOLEFT, kp_tinybumper[0], colormap);
|
|
for (j = 1; j < bumpers; j++)
|
|
{
|
|
bumperx += 5;
|
|
V_DrawMappedPatch(bumperx, Y, V_HUDTRANS|V_SLIDEIN|V_SNAPTOLEFT, kp_tinybumper[1], colormap);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
for (j = 0; j < 7; j++)
|
|
{
|
|
UINT32 emeraldFlag = (1 << j);
|
|
skincolornum_t emeraldColor = static_cast<skincolornum_t>(SKINCOLOR_CHAOSEMERALD1 + j);
|
|
|
|
if (players[rankplayer[i]].emeralds & emeraldFlag)
|
|
{
|
|
colormap = R_GetTranslationColormap(TC_DEFAULT, emeraldColor, GTC_CACHE);
|
|
V_DrawMappedPatch(emeraldx, Y+7, V_HUDTRANS|V_SLIDEIN|V_SNAPTOLEFT, kp_rankemerald, colormap);
|
|
emeraldx += 7;
|
|
}
|
|
}
|
|
|
|
if (i == strank)
|
|
V_DrawScaledPatch(FACE_X, Y, V_HUDTRANS|V_SLIDEIN|V_SNAPTOLEFT, kp_facehighlight[(leveltime / 4) % 8]);
|
|
|
|
if ((gametyperules & GTR_BUMPERS) && (players[rankplayer[i]].pflags & PF_ELIMINATED))
|
|
V_DrawScaledPatch(FACE_X-4, Y-3, V_HUDTRANS|V_SLIDEIN|V_SNAPTOLEFT, kp_ranknobumpers);
|
|
else if (K_Cooperative())
|
|
;
|
|
else if (gametyperules & GTR_CIRCUIT)
|
|
{
|
|
INT32 pos = players[rankplayer[i]].position;
|
|
if (pos < 0 || pos > MAXPLAYERS)
|
|
pos = 0;
|
|
// Draws the little number over the face
|
|
V_DrawScaledPatch(FACE_X-5, Y+10, V_HUDTRANS|V_SLIDEIN|V_SNAPTOLEFT, kp_facenum[pos]);
|
|
}
|
|
else if (gametyperules & GTR_POINTLIMIT)
|
|
{
|
|
INT32 flags = V_HUDTRANS | V_SLIDEIN | V_SNAPTOLEFT;
|
|
|
|
colormap = NULL;
|
|
|
|
if (g_pointlimit && g_pointlimit <= players[rankplayer[i]].roundscore)
|
|
{
|
|
if (leveltime % 8 < 4)
|
|
{
|
|
colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_TANGERINE, GTC_CACHE);
|
|
}
|
|
|
|
flags |= V_STRINGDANCE;
|
|
}
|
|
|
|
V_DrawStringScaled(
|
|
(FACE_X - 5) * FRACUNIT,
|
|
(Y + 10) * FRACUNIT,
|
|
FRACUNIT,
|
|
FRACUNIT,
|
|
FRACUNIT,
|
|
flags,
|
|
colormap,
|
|
PINGF_FONT,
|
|
va("%d", players[rankplayer[i]].roundscore)
|
|
);
|
|
}
|
|
|
|
Y -= 18;
|
|
}
|
|
}
|
|
|
|
void PositionFacesInfo::draw_4p_battle(int x, int y, INT32 flags)
|
|
{
|
|
using srb2::Draw;
|
|
Draw row = Draw(x, y).flags(V_HUDTRANS | V_SLIDEIN | flags).font(Draw::Font::kPing);
|
|
|
|
UINT8 skull = []
|
|
{
|
|
if (g_pointlimit == 0)
|
|
return 0;
|
|
|
|
int party = G_PartySize(consoleplayer);
|
|
for (int i = 0; i < party; ++i)
|
|
{
|
|
// Is any party member about to win?
|
|
if (g_pointlimit <= players[G_PartyMember(consoleplayer, i)].roundscore)
|
|
{
|
|
return 1;
|
|
}
|
|
}
|
|
return 0;
|
|
}();
|
|
|
|
skincolornum_t vomit = vomit_color();
|
|
(vomit ? row.colormap(vomit) : row).patch(kp_goal[skull][1]);
|
|
|
|
if (!skull && g_pointlimit)
|
|
{
|
|
row.xy(8.5, 5).align(Draw::Align::kCenter).text("{:02}", g_pointlimit);
|
|
}
|
|
|
|
row.xy(7, 18).patch(kp_goalrod[1]);
|
|
|
|
auto head = [&](Draw col, int i)
|
|
{
|
|
const player_t& p = players[rankplayer[i]];
|
|
col.colormap(p.skin, static_cast<skincolornum_t>(p.skincolor)).patch(faceprefix[p.skin][FACE_MINIMAP]);
|
|
|
|
bool dance = g_pointlimit && (g_pointlimit <= p.roundscore);
|
|
bool flash = dance && leveltime % 8 < 4;
|
|
(
|
|
flash ?
|
|
col.xy(8, 6).colorize(SKINCOLOR_TANGERINE).flags(V_STRINGDANCE) :
|
|
col.xy(8, 6).flags(dance ? V_STRINGDANCE : 0)
|
|
).text("{:02}", p.roundscore);
|
|
};
|
|
|
|
// Draw top 2 players
|
|
head(row.xy(2, 31), 1);
|
|
head(row.xy(2, 18), 0);
|
|
}
|
|
|
|
static boolean K_drawKartPositionFaces(void)
|
|
{
|
|
PositionFacesInfo state{};
|
|
|
|
if (state.numplayersingame <= 1)
|
|
return true;
|
|
|
|
if (!LUA_HudEnabled(hud_minirankings))
|
|
return false; // Don't proceed but still return true for free play above if HUD is disabled.
|
|
|
|
switch (r_splitscreen)
|
|
{
|
|
case 0:
|
|
state.draw_1p();
|
|
break;
|
|
|
|
case 1:
|
|
state.draw_4p_battle(292, 78, V_SNAPTORIGHT);
|
|
break;
|
|
|
|
case 2:
|
|
case 3:
|
|
state.draw_4p_battle(152, 9, V_SNAPTOTOP);
|
|
state.draw_4p_battle(152, 147, V_SNAPTOBOTTOM);
|
|
break;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
static void K_drawBossHealthBar(void)
|
|
{
|
|
UINT8 i = 0, barstatus = 1, randlen = 0, darken = 0;
|
|
const INT32 startx = BASEVIDWIDTH - 23;
|
|
INT32 starty = BASEVIDHEIGHT - 25;
|
|
INT32 rolrand = 0, randtemp = 0;
|
|
boolean randsign = false;
|
|
|
|
if (bossinfo.barlen <= 1)
|
|
return;
|
|
|
|
// Entire bar juddering!
|
|
if (lt_exitticker < (TICRATE/2))
|
|
;
|
|
else if (bossinfo.visualbarimpact)
|
|
{
|
|
INT32 mag = std::min((bossinfo.visualbarimpact/4) + 1, 8u);
|
|
if (bossinfo.visualbarimpact & 1)
|
|
starty -= mag;
|
|
else
|
|
starty += mag;
|
|
}
|
|
|
|
if ((lt_ticker >= lt_endtime) && bossinfo.enemyname)
|
|
{
|
|
if (lt_exitticker == 0)
|
|
{
|
|
rolrand = 5;
|
|
}
|
|
else if (lt_exitticker == 1)
|
|
{
|
|
rolrand = 7;
|
|
}
|
|
else
|
|
{
|
|
rolrand = 10;
|
|
}
|
|
V_DrawRightAlignedThinString(startx, starty-rolrand, V_FORCEUPPERCASE|V_HUDTRANS|V_SLIDEIN|V_SNAPTOBOTTOM|V_SNAPTORIGHT, bossinfo.enemyname);
|
|
rolrand = 0;
|
|
}
|
|
|
|
// Used for colour and randomisation.
|
|
if (bossinfo.healthbar <= (bossinfo.visualdiv/FRACUNIT))
|
|
{
|
|
barstatus = 3;
|
|
}
|
|
else if (bossinfo.healthbar <= bossinfo.healthbarpinch)
|
|
{
|
|
barstatus = 2;
|
|
}
|
|
|
|
randtemp = bossinfo.visualbar-(bossinfo.visualdiv/(2*FRACUNIT));
|
|
if (randtemp > 0)
|
|
randlen = P_RandomKey(PR_INTERPHUDRANDOM, randtemp)+1;
|
|
randsign = P_RandomChance(PR_INTERPHUDRANDOM, FRACUNIT/2);
|
|
|
|
// Right wing.
|
|
V_DrawScaledPatch(startx-1, starty, V_HUDTRANS|V_SLIDEIN|V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_FLIP, kp_bossbar[0]);
|
|
|
|
// Draw the bar itself...
|
|
while (i < bossinfo.barlen)
|
|
{
|
|
V_DrawScaledPatch(startx-i, starty, V_HUDTRANS|V_SLIDEIN|V_SNAPTOBOTTOM|V_SNAPTORIGHT, kp_bossbar[1]);
|
|
if (i < bossinfo.visualbar)
|
|
{
|
|
randlen--;
|
|
if (!randlen)
|
|
{
|
|
randtemp = bossinfo.visualbar-(bossinfo.visualdiv/(2*FRACUNIT));
|
|
if (randtemp > 0)
|
|
randlen = P_RandomKey(PR_INTERPHUDRANDOM, randtemp)+1;
|
|
if (barstatus > 1)
|
|
{
|
|
rolrand = P_RandomKey(PR_INTERPHUDRANDOM, barstatus)+1;
|
|
}
|
|
else
|
|
{
|
|
rolrand = 1;
|
|
}
|
|
if (randsign)
|
|
{
|
|
rolrand = -rolrand;
|
|
}
|
|
randsign = !randsign;
|
|
}
|
|
else
|
|
{
|
|
rolrand = 0;
|
|
}
|
|
if (lt_exitticker < (TICRATE/2))
|
|
;
|
|
else if ((bossinfo.visualbar - i) < (INT32)(bossinfo.visualbarimpact/8))
|
|
{
|
|
if (bossinfo.visualbarimpact & 1)
|
|
rolrand += (bossinfo.visualbar - i);
|
|
else
|
|
rolrand -= (bossinfo.visualbar - i);
|
|
}
|
|
if (bossinfo.visualdiv)
|
|
{
|
|
fixed_t work = 0;
|
|
if ((i+1) == bossinfo.visualbar)
|
|
darken = 1;
|
|
else
|
|
{
|
|
darken = 0;
|
|
// a hybrid fixed-int modulo...
|
|
while ((work/FRACUNIT) < bossinfo.visualbar)
|
|
{
|
|
if (work/FRACUNIT != i)
|
|
{
|
|
work += bossinfo.visualdiv;
|
|
continue;
|
|
}
|
|
darken = 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
V_DrawScaledPatch(startx-i, starty+rolrand, V_HUDTRANS|V_SLIDEIN|V_SNAPTOBOTTOM|V_SNAPTORIGHT, kp_bossbar[(2*barstatus)+darken]);
|
|
}
|
|
i++;
|
|
}
|
|
|
|
// Left wing.
|
|
V_DrawScaledPatch(startx-i, starty, V_HUDTRANS|V_SLIDEIN|V_SNAPTOBOTTOM|V_SNAPTORIGHT, kp_bossbar[0]);
|
|
}
|
|
|
|
static void K_drawKartEmeralds(void)
|
|
{
|
|
static const INT32 emeraldOffsets[7][3] = {
|
|
{34, 0, 15},
|
|
{25, 8, 11},
|
|
{43, 8, 19},
|
|
{16, 0, 7},
|
|
{52, 0, 23},
|
|
{ 7, 8, 3},
|
|
{61, 8, 27}
|
|
};
|
|
|
|
INT32 splitflags = V_SLIDEIN|V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_SPLITSCREEN;
|
|
INT32 startx = BASEVIDWIDTH - 77;
|
|
INT32 starty = BASEVIDHEIGHT - 29;
|
|
INT32 i = 0, xindex = 0;
|
|
|
|
{
|
|
if (r_splitscreen)
|
|
{
|
|
starty = (starty/2) - 8;
|
|
}
|
|
starty -= 8;
|
|
|
|
if (r_splitscreen < 2)
|
|
{
|
|
startx -= 8;
|
|
if (r_splitscreen == 1 && R_GetViewNumber() == 0)
|
|
{
|
|
starty = 1;
|
|
}
|
|
V_DrawScaledPatch(startx, starty, V_HUDTRANS|splitflags, kp_rankemeraldback);
|
|
}
|
|
else
|
|
{
|
|
xindex = 2;
|
|
starty -= 15;
|
|
if (!(R_GetViewNumber() & 1)) // If we are P1 or P3...
|
|
{
|
|
startx = LAPS_X;
|
|
splitflags = V_SNAPTOLEFT|V_SNAPTOBOTTOM|V_SPLITSCREEN;
|
|
}
|
|
else // else, that means we're P2 or P4.
|
|
{
|
|
startx = LAPS2_X + 1;
|
|
splitflags = V_SNAPTORIGHT|V_SNAPTOBOTTOM|V_SPLITSCREEN;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < 7; i++)
|
|
{
|
|
UINT32 emeraldFlag = (1 << i);
|
|
skincolornum_t emeraldColor = static_cast<skincolornum_t>(SKINCOLOR_CHAOSEMERALD1 + i);
|
|
|
|
if (stplyr->emeralds & emeraldFlag)
|
|
{
|
|
boolean whiteFlash = (leveltime & 1);
|
|
UINT8 *colormap;
|
|
|
|
if (i & 1)
|
|
{
|
|
whiteFlash = !whiteFlash;
|
|
}
|
|
|
|
colormap = R_GetTranslationColormap(TC_DEFAULT, emeraldColor, GTC_CACHE);
|
|
V_DrawMappedPatch(
|
|
startx + emeraldOffsets[i][xindex], starty + emeraldOffsets[i][1],
|
|
V_HUDTRANS|splitflags,
|
|
kp_rankemerald, colormap
|
|
);
|
|
|
|
if (whiteFlash == true)
|
|
{
|
|
V_DrawScaledPatch(
|
|
startx + emeraldOffsets[i][xindex], starty + emeraldOffsets[i][1],
|
|
V_HUDTRANSHALF|splitflags,
|
|
kp_rankemeraldflash
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void K_drawKartLaps(void)
|
|
{
|
|
INT32 splitflags = V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_SPLITSCREEN;
|
|
|
|
if (r_splitscreen > 1)
|
|
{
|
|
INT32 fx = 0, fy = 0;
|
|
INT32 flipflag = 0;
|
|
|
|
// pain and suffering defined below
|
|
if (r_splitscreen < 2) // don't change shit for THIS splitscreen.
|
|
{
|
|
fx = LAPS_X;
|
|
fy = LAPS_Y;
|
|
}
|
|
else
|
|
{
|
|
if (!(R_GetViewNumber() & 1)) // If we are P1 or P3...
|
|
{
|
|
fx = LAPS_X;
|
|
fy = LAPS_Y;
|
|
splitflags = V_SNAPTOLEFT|V_SNAPTOBOTTOM|V_SPLITSCREEN;
|
|
}
|
|
else // else, that means we're P2 or P4.
|
|
{
|
|
fx = LAPS2_X;
|
|
fy = LAPS2_Y;
|
|
splitflags = V_SNAPTORIGHT|V_SNAPTOBOTTOM|V_SPLITSCREEN;
|
|
flipflag = V_FLIP; // make the string right aligned and other shit
|
|
}
|
|
}
|
|
|
|
// Laps
|
|
V_DrawScaledPatch(fx-2 + (flipflag ? (SHORT(kp_ringstickersplit[1]->width) - 3) : 0), fy, V_HUDTRANS|V_SLIDEIN|splitflags|flipflag, kp_ringstickersplit[0]);
|
|
|
|
V_DrawScaledPatch(fx, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_splitlapflag);
|
|
V_DrawScaledPatch(fx+22, fy, V_HUDTRANS|V_SLIDEIN|splitflags, frameslash);
|
|
|
|
if (numlaps >= 10)
|
|
{
|
|
UINT8 ln[2];
|
|
ln[0] = ((stplyr->laps / 10) % 10);
|
|
ln[1] = (stplyr->laps % 10);
|
|
|
|
V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[ln[0]]);
|
|
V_DrawScaledPatch(fx+17, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[ln[1]]);
|
|
|
|
ln[0] = ((numlaps / 10) % 10);
|
|
ln[1] = (numlaps % 10);
|
|
|
|
V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[ln[0]]);
|
|
V_DrawScaledPatch(fx+31, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[ln[1]]);
|
|
}
|
|
else
|
|
{
|
|
V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[(stplyr->laps) % 10]);
|
|
V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[(numlaps) % 10]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Laps
|
|
V_DrawScaledPatch(LAPS_X, LAPS_Y, V_HUDTRANS|V_SLIDEIN|splitflags, kp_lapsticker);
|
|
V_DrawTimerString(LAPS_X+33, LAPS_Y+3, V_HUDTRANS|V_SLIDEIN|splitflags, va("%d/%d", std::min(stplyr->laps, numlaps), numlaps));
|
|
}
|
|
}
|
|
|
|
#define RINGANIM_FLIPFRAME (RINGANIM_NUMFRAMES/2)
|
|
|
|
static void K_DrawLivesDigits(INT32 x, INT32 y, INT32 width, INT32 flags, patch_t *font[10])
|
|
{
|
|
const SINT8 tens = stplyr->lives / 10;
|
|
|
|
if (tens)
|
|
{
|
|
V_DrawScaledPatch(x, y, flags, font[tens % 10]);
|
|
x += width;
|
|
}
|
|
|
|
V_DrawScaledPatch(x, y, flags, font[stplyr->lives % 10]);
|
|
}
|
|
|
|
static void K_drawRingCounter(boolean gametypeinfoshown)
|
|
{
|
|
const boolean uselives = G_GametypeUsesLives();
|
|
SINT8 ringanim_realframe = stplyr->karthud[khud_ringframe];
|
|
INT32 splitflags = V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_SPLITSCREEN;
|
|
UINT8 rn[2];
|
|
INT32 ringflip = 0;
|
|
UINT8 *ringmap = NULL;
|
|
boolean colorring = false;
|
|
INT32 ringx = 0, fy = 0;
|
|
|
|
rn[0] = ((abs(stplyr->hudrings) / 10) % 10);
|
|
rn[1] = (abs(stplyr->hudrings) % 10);
|
|
|
|
if (stplyr->hudrings <= 0 && stplyr->ringvisualwarning > 1)
|
|
{
|
|
colorring = true;
|
|
if ((leveltime/2 & 1))
|
|
{
|
|
ringmap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_CRIMSON, GTC_CACHE);
|
|
}
|
|
else
|
|
{
|
|
ringmap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_WHITE, GTC_CACHE);
|
|
}
|
|
}
|
|
else if (stplyr->hudrings <= 0 && (leveltime/5 & 1)) // In debt
|
|
{
|
|
ringmap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_CRIMSON, GTC_CACHE);
|
|
colorring = true;
|
|
}
|
|
else if (stplyr->hudrings >= 20) // Maxed out
|
|
ringmap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_YELLOW, GTC_CACHE);
|
|
|
|
if (stplyr->karthud[khud_ringframe] > RINGANIM_FLIPFRAME)
|
|
{
|
|
ringflip = V_FLIP;
|
|
ringanim_realframe = RINGANIM_NUMFRAMES-stplyr->karthud[khud_ringframe];
|
|
ringx += SHORT((r_splitscreen > 1) ? kp_smallring[ringanim_realframe]->width : kp_ring[ringanim_realframe]->width);
|
|
}
|
|
|
|
if (r_splitscreen > 1)
|
|
{
|
|
INT32 fx = 0, fr = 0;
|
|
INT32 flipflag = 0;
|
|
|
|
// pain and suffering defined below
|
|
if (r_splitscreen < 2) // don't change shit for THIS splitscreen.
|
|
{
|
|
fx = LAPS_X;
|
|
fy = LAPS_Y;
|
|
}
|
|
else
|
|
{
|
|
if (!(R_GetViewNumber() & 1)) // If we are P1 or P3...
|
|
{
|
|
fx = LAPS_X;
|
|
fy = LAPS_Y;
|
|
splitflags = V_SNAPTOLEFT|V_SNAPTOBOTTOM|V_SPLITSCREEN;
|
|
}
|
|
else // else, that means we're P2 or P4.
|
|
{
|
|
fx = LAPS2_X;
|
|
fy = LAPS2_Y;
|
|
splitflags = V_SNAPTORIGHT|V_SNAPTOBOTTOM|V_SPLITSCREEN;
|
|
flipflag = V_FLIP; // make the string right aligned and other shit
|
|
}
|
|
}
|
|
|
|
fr = fx;
|
|
|
|
if (gametypeinfoshown)
|
|
{
|
|
fy -= 10;
|
|
}
|
|
|
|
// Rings
|
|
if (!uselives)
|
|
{
|
|
V_DrawScaledPatch(fx-2 + (flipflag ? (SHORT(kp_ringstickersplit[1]->width) - 3) : 0), fy, V_HUDTRANS|V_SLIDEIN|splitflags|flipflag, kp_ringstickersplit[1]);
|
|
if (flipflag)
|
|
fr += 15;
|
|
}
|
|
else
|
|
V_DrawScaledPatch(fx-2 + (flipflag ? (SHORT(kp_ringstickersplit[0]->width) - 3) : 0), fy, V_HUDTRANS|V_SLIDEIN|splitflags|flipflag, kp_ringstickersplit[0]);
|
|
|
|
V_DrawMappedPatch(fr+ringx, fy-3, V_HUDTRANS|V_SLIDEIN|splitflags|ringflip, kp_smallring[ringanim_realframe], (colorring ? ringmap : NULL));
|
|
|
|
if (stplyr->hudrings < 0) // Draw the minus for ring debt
|
|
{
|
|
V_DrawMappedPatch(fr+11, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_ringdebtminussmall, ringmap);
|
|
V_DrawMappedPatch(fr+15, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[rn[0]], ringmap);
|
|
V_DrawMappedPatch(fr+19, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[rn[1]], ringmap);
|
|
}
|
|
else
|
|
{
|
|
V_DrawMappedPatch(fr+11, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[rn[0]], ringmap);
|
|
V_DrawMappedPatch(fr+15, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[rn[1]], ringmap);
|
|
}
|
|
|
|
// SPB ring lock
|
|
if (stplyr->pflags & PF_RINGLOCK)
|
|
V_DrawScaledPatch(fr-12, fy-13, V_HUDTRANS|V_SLIDEIN|splitflags, kp_ringspblocksmall[stplyr->karthud[khud_ringspblock]]);
|
|
|
|
// Lives
|
|
if (uselives)
|
|
{
|
|
UINT8 *colormap = R_GetTranslationColormap(stplyr->skin, static_cast<skincolornum_t>(stplyr->skincolor), GTC_CACHE);
|
|
V_DrawMappedPatch(fr+21, fy-3, V_HUDTRANS|V_SLIDEIN|splitflags, faceprefix[stplyr->skin][FACE_MINIMAP], colormap);
|
|
if (stplyr->lives >= 0)
|
|
K_DrawLivesDigits(fr+34, fy, 4, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fy = LAPS_Y;
|
|
|
|
if (gametypeinfoshown)
|
|
{
|
|
fy -= 11;
|
|
|
|
if ((gametyperules & (GTR_BUMPERS|GTR_CIRCUIT)) == GTR_BUMPERS)
|
|
fy -= 4;
|
|
}
|
|
else
|
|
{
|
|
fy += 9;
|
|
}
|
|
|
|
// Rings
|
|
using srb2::Draw;
|
|
Draw(LAPS_X+7, fy+1)
|
|
.flags(V_HUDTRANS|V_SLIDEIN|splitflags)
|
|
.align(Draw::Align::kCenter)
|
|
.width(uselives ? (stplyr->lives >= 10 ? 70 : 64) : 33)
|
|
.small_sticker();
|
|
|
|
V_DrawMappedPatch(LAPS_X+ringx+7, fy-5, V_HUDTRANS|V_SLIDEIN|splitflags|ringflip, kp_ring[ringanim_realframe], (colorring ? ringmap : NULL));
|
|
|
|
// "Why fy-4? Why LAPS_X+29+1?"
|
|
// "use magic numbers" - jartha 2024-03-05
|
|
if (stplyr->hudrings < 0) // Draw the minus for ring debt
|
|
{
|
|
V_DrawMappedPatch(LAPS_X+23-1, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_ringdebtminus, ringmap);
|
|
using srb2::Draw;
|
|
Draw row = Draw(LAPS_X+29+0, fy-4).flags(V_HUDTRANS|V_SLIDEIN|splitflags).font(Draw::Font::kThinTimer).colormap(ringmap);
|
|
row.text("{:02}", abs(stplyr->hudrings));
|
|
// V_DrawMappedPatch(LAPS_X+29, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[TALLNUM_FONT].font[rn[0]], ringmap);
|
|
// V_DrawMappedPatch(LAPS_X+35, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[TALLNUM_FONT].font[rn[1]], ringmap);
|
|
}
|
|
else
|
|
{
|
|
using srb2::Draw;
|
|
Draw row = Draw(LAPS_X+23+3, fy-4).flags(V_HUDTRANS|V_SLIDEIN|splitflags).font(Draw::Font::kThinTimer).colormap(ringmap);
|
|
row.text("{:02}", abs(stplyr->hudrings));
|
|
// V_DrawMappedPatch(LAPS_X+23, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[TALLNUM_FONT].font[rn[0]], ringmap);
|
|
// V_DrawMappedPatch(LAPS_X+29, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[TALLNUM_FONT].font[rn[1]], ringmap);
|
|
}
|
|
|
|
// SPB ring lock
|
|
if (stplyr->pflags & PF_RINGLOCK)
|
|
V_DrawScaledPatch(LAPS_X-5, fy-17, V_HUDTRANS|V_SLIDEIN|splitflags, kp_ringspblock[stplyr->karthud[khud_ringspblock]]);
|
|
|
|
// Lives
|
|
if (uselives)
|
|
{
|
|
UINT8 *colormap = R_GetTranslationColormap(stplyr->skin, static_cast<skincolornum_t>(stplyr->skincolor), GTC_CACHE);
|
|
V_DrawMappedPatch(LAPS_X+46, fy-5, V_HUDTRANS|V_SLIDEIN|splitflags, faceprefix[stplyr->skin][FACE_RANK], colormap);
|
|
SINT8 livescount = 0;
|
|
if (stplyr->lives > 0)
|
|
{
|
|
livescount = stplyr->lives;
|
|
if (livescount > 10)
|
|
livescount = 10;
|
|
}
|
|
using srb2::Draw;
|
|
Draw row = Draw(LAPS_X+65, fy-4).flags(V_HUDTRANS|V_SLIDEIN|splitflags).font(Draw::Font::kThinTimer);
|
|
row.text("{}", livescount);
|
|
}
|
|
}
|
|
}
|
|
|
|
#undef RINGANIM_FLIPFRAME
|
|
|
|
static void K_drawKartAccessibilityIcons(boolean gametypeinfoshown, INT32 fx)
|
|
{
|
|
INT32 fy = LAPS_Y-14;
|
|
INT32 splitflags = V_SNAPTOLEFT|V_SNAPTOBOTTOM|V_SPLITSCREEN;
|
|
|
|
boolean mirror = false;
|
|
|
|
fx += LAPS_X;
|
|
|
|
if (r_splitscreen < 2) // adjust to speedometer height
|
|
{
|
|
if (battleprisons)
|
|
{
|
|
fy -= 2;
|
|
}
|
|
|
|
if (gametypeinfoshown)
|
|
{
|
|
fy -= 11;
|
|
|
|
if ((gametyperules & (GTR_BUMPERS|GTR_CIRCUIT)) == GTR_BUMPERS)
|
|
fy -= 4;
|
|
}
|
|
else
|
|
{
|
|
fy += 9;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fx = LAPS_X+44;
|
|
fy = LAPS_Y;
|
|
if (R_GetViewNumber() & 1) // If we are not P1 or P3...
|
|
{
|
|
splitflags ^= (V_SNAPTOLEFT|V_SNAPTORIGHT);
|
|
fx = (BASEVIDWIDTH/2) - fx;
|
|
mirror = true;
|
|
}
|
|
}
|
|
|
|
// Kickstart Accel
|
|
if (stplyr->pflags & PF_KICKSTARTACCEL)
|
|
{
|
|
if (mirror)
|
|
fx -= 10;
|
|
|
|
SINT8 col = 0, wid, fil, ofs;
|
|
UINT8 i = 7;
|
|
ofs = (stplyr->kickstartaccel == ACCEL_KICKSTART) ? 1 : 0;
|
|
fil = i-(stplyr->kickstartaccel*i)/ACCEL_KICKSTART;
|
|
|
|
V_DrawFill(fx+4, fy+ofs-1, 2, 1, 31|V_SLIDEIN|splitflags);
|
|
V_DrawFill(fx, (fy+ofs-1)+8, 10, 1, 31|V_SLIDEIN|splitflags);
|
|
|
|
while (i--)
|
|
{
|
|
wid = (i/2)+1;
|
|
V_DrawFill(fx+4-wid, fy+ofs+i, 2+(wid*2), 1, 31|V_SLIDEIN|splitflags);
|
|
if (fil > 0)
|
|
{
|
|
if (i < fil)
|
|
col = 23;
|
|
else if (i == fil)
|
|
col = 3;
|
|
else
|
|
col = 5 + (i-fil)*2;
|
|
}
|
|
else if ((leveltime % 7) == i)
|
|
col = 0;
|
|
else
|
|
col = 3;
|
|
V_DrawFill(fx+5-wid, fy+ofs+i, (wid*2), 1, col|V_SLIDEIN|splitflags);
|
|
}
|
|
|
|
if (mirror)
|
|
fx--;
|
|
else
|
|
fx += 10 + 1;
|
|
}
|
|
|
|
// Auto Roulette
|
|
if (stplyr->pflags & PF_AUTOROULETTE)
|
|
{
|
|
if (mirror)
|
|
fx -= 12;
|
|
|
|
V_DrawScaledPatch(fx, fy-1, V_SLIDEIN|splitflags, kp_autoroulette);
|
|
|
|
if (mirror)
|
|
fx--;
|
|
else
|
|
fx += 12 + 1;
|
|
}
|
|
}
|
|
|
|
static void K_drawKartSpeedometer(boolean gametypeinfoshown)
|
|
{
|
|
static fixed_t convSpeed;
|
|
UINT8 labeln = 0;
|
|
UINT8 numbers[3];
|
|
INT32 splitflags = V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_SPLITSCREEN;
|
|
INT32 fy = LAPS_Y-14;
|
|
|
|
if (battleprisons)
|
|
{
|
|
fy -= 2;
|
|
}
|
|
|
|
if (!stplyr->exiting) // Keep the same speed value as when you crossed the finish line!
|
|
{
|
|
switch (cv_kartspeedometer.value)
|
|
{
|
|
case 1: // Sonic Drift 2 style percentage
|
|
default:
|
|
convSpeed = (stplyr->speed * 100) / K_GetKartSpeed(stplyr, false, true); // Based on top speed!
|
|
labeln = 0;
|
|
break;
|
|
case 2: // Kilometers
|
|
convSpeed = FixedDiv(FixedMul(stplyr->speed, 142371), mapobjectscale) / FRACUNIT; // 2.172409058
|
|
labeln = 1;
|
|
break;
|
|
case 3: // Miles
|
|
convSpeed = FixedDiv(FixedMul(stplyr->speed, 88465), mapobjectscale) / FRACUNIT; // 1.349868774
|
|
labeln = 2;
|
|
break;
|
|
case 4: // Fracunits
|
|
convSpeed = FixedDiv(stplyr->speed, mapobjectscale) / FRACUNIT; // 1.0. duh.
|
|
labeln = 3;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Don't overflow
|
|
// (negative speed IS really high speed :V)
|
|
if (convSpeed > 999 || convSpeed < 0)
|
|
convSpeed = 999;
|
|
|
|
numbers[0] = ((convSpeed / 100) % 10);
|
|
numbers[1] = ((convSpeed / 10) % 10);
|
|
numbers[2] = (convSpeed % 10);
|
|
|
|
if (gametypeinfoshown)
|
|
{
|
|
fy -= 11;
|
|
|
|
if ((gametyperules & (GTR_BUMPERS|GTR_CIRCUIT)) == GTR_BUMPERS)
|
|
fy -= 4;
|
|
}
|
|
else
|
|
{
|
|
fy += 9;
|
|
}
|
|
|
|
using srb2::Draw;
|
|
Draw(LAPS_X+7, fy+1).flags(V_HUDTRANS|V_SLIDEIN|splitflags).align(Draw::Align::kCenter).width(42).small_sticker();
|
|
V_DrawScaledPatch(LAPS_X+7, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[numbers[0]]);
|
|
V_DrawScaledPatch(LAPS_X+13, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[numbers[1]]);
|
|
V_DrawScaledPatch(LAPS_X+19, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[numbers[2]]);
|
|
V_DrawScaledPatch(LAPS_X+29, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_speedometerlabel[labeln]);
|
|
|
|
K_drawKartAccessibilityIcons(gametypeinfoshown, 56);
|
|
}
|
|
|
|
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<int>(stplyr->spheres), 0, 40);
|
|
|
|
UINT8 numBars = std::min((sphere / 10), +maxBars);
|
|
UINT8 colorIndex = (sphere * sizeof(segColors)) / (40 + 1);
|
|
INT32 fx, fy;
|
|
UINT8 i;
|
|
INT32 splitflags = V_HUDTRANS|V_SLIDEIN|V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_SPLITSCREEN;
|
|
INT32 flipflag = 0;
|
|
INT32 xstep = 15;
|
|
|
|
// pain and suffering defined below
|
|
if (r_splitscreen < 2) // don't change shit for THIS splitscreen.
|
|
{
|
|
fx = LAPS_X;
|
|
fy = LAPS_Y-4;
|
|
|
|
if (battleprisons)
|
|
{
|
|
if (r_splitscreen == 1)
|
|
{
|
|
fy -= 8;
|
|
}
|
|
else
|
|
{
|
|
fy -= 5;
|
|
}
|
|
}
|
|
else if (r_splitscreen == 1)
|
|
{
|
|
fy -= 5;
|
|
}
|
|
|
|
if (gametypeinfoshown)
|
|
{
|
|
fy -= 11 + 4;
|
|
}
|
|
else
|
|
{
|
|
fy += 9;
|
|
}
|
|
|
|
V_DrawScaledPatch(fx, fy, splitflags|flipflag, kp_spheresticker);
|
|
}
|
|
else
|
|
{
|
|
xstep = 8;
|
|
if (!(R_GetViewNumber() & 1)) // If we are P1 or P3...
|
|
{
|
|
fx = LAPS_X-2;
|
|
fy = LAPS_Y;
|
|
}
|
|
else // else, that means we're P2 or P4.
|
|
{
|
|
fx = LAPS2_X+(SHORT(kp_splitspheresticker->width) - 10);
|
|
fy = LAPS2_Y;
|
|
splitflags ^= V_SNAPTOLEFT|V_SNAPTORIGHT;
|
|
flipflag = V_FLIP; // make the string right aligned and other shit
|
|
xstep = -xstep;
|
|
}
|
|
|
|
if (battleprisons)
|
|
{
|
|
fy -= 5;
|
|
}
|
|
|
|
if (gametypeinfoshown)
|
|
{
|
|
fy -= 16;
|
|
}
|
|
|
|
V_DrawScaledPatch(fx, fy, splitflags|flipflag, kp_splitspheresticker);
|
|
}
|
|
|
|
if (r_splitscreen < 2)
|
|
{
|
|
fx += 25;
|
|
}
|
|
else
|
|
{
|
|
fx += (flipflag) ? -18 : 13;
|
|
}
|
|
|
|
for (i = 0; i <= numBars; i++)
|
|
{
|
|
UINT8 segLen = (r_splitscreen < 2) ? 10 : 5;
|
|
|
|
if (i == numBars)
|
|
{
|
|
segLen = (sphere % 10);
|
|
if (r_splitscreen < 2)
|
|
;
|
|
else
|
|
{
|
|
segLen = (segLen+1)/2; // offset so nonzero spheres shows up IMMEDIATELY
|
|
if (!segLen)
|
|
break;
|
|
if (flipflag)
|
|
fx += (5-segLen);
|
|
}
|
|
}
|
|
|
|
if (r_splitscreen < 2)
|
|
{
|
|
V_DrawFill(fx, fy + 6, segLen, 3, segColors[std::max(colorIndex-1, 0)] | splitflags);
|
|
V_DrawFill(fx, fy + 7, segLen, 1, segColors[std::max(colorIndex-2, 0)] | splitflags);
|
|
V_DrawFill(fx, fy + 9, segLen, 3, segColors[colorIndex] | splitflags);
|
|
}
|
|
else
|
|
{
|
|
V_DrawFill(fx, fy + 5, segLen, 1, segColors[std::max(colorIndex-1, 0)] | splitflags);
|
|
V_DrawFill(fx, fy + 6, segLen, 1, segColors[std::max(colorIndex-2, 0)] | splitflags);
|
|
V_DrawFill(fx, fy + 7, segLen, 2, segColors[colorIndex] | splitflags);
|
|
}
|
|
|
|
fx += xstep;
|
|
}
|
|
}
|
|
|
|
static void K_drawKartBumpersOrKarma(void)
|
|
{
|
|
UINT8 *colormap = R_GetTranslationColormap(TC_DEFAULT, static_cast<skincolornum_t>(stplyr->skincolor), GTC_CACHE);
|
|
INT32 splitflags = V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_SPLITSCREEN;
|
|
|
|
if (r_splitscreen > 1)
|
|
{
|
|
INT32 fx = 0, fy = 0;
|
|
INT32 flipflag = 0;
|
|
|
|
// pain and suffering defined below
|
|
if (r_splitscreen < 2) // don't change shit for THIS splitscreen.
|
|
{
|
|
fx = LAPS_X;
|
|
fy = LAPS_Y;
|
|
}
|
|
else
|
|
{
|
|
if (!(R_GetViewNumber() & 1)) // If we are P1 or P3...
|
|
{
|
|
fx = LAPS_X;
|
|
fy = LAPS_Y;
|
|
splitflags = V_SNAPTOLEFT|V_SNAPTOBOTTOM|V_SPLITSCREEN;
|
|
}
|
|
else // else, that means we're P2 or P4.
|
|
{
|
|
fx = LAPS2_X;
|
|
fy = LAPS2_Y;
|
|
splitflags = V_SNAPTORIGHT|V_SNAPTOBOTTOM|V_SPLITSCREEN;
|
|
flipflag = V_FLIP; // make the string right aligned and other shit
|
|
}
|
|
}
|
|
|
|
{
|
|
using srb2::Draw;
|
|
int width = 39;
|
|
if (!battleprisons)
|
|
{
|
|
constexpr int kPad = 16;
|
|
if (flipflag)
|
|
fx -= kPad;
|
|
width += kPad;
|
|
}
|
|
Draw(fx-1 + (flipflag ? width + 3 : 0), fy+1)
|
|
.flags(V_HUDTRANS|V_SLIDEIN|splitflags)
|
|
.align(flipflag ? Draw::Align::kRight : Draw::Align::kLeft)
|
|
.width(width)
|
|
.small_sticker();
|
|
}
|
|
|
|
fx += 2;
|
|
|
|
if (battleprisons)
|
|
{
|
|
V_DrawScaledPatch(fx+22, fy, V_HUDTRANS|V_SLIDEIN|splitflags, frameslash);
|
|
V_DrawMappedPatch(fx-1, fy-2, V_HUDTRANS|V_SLIDEIN|splitflags, kp_rankcapsule, NULL);
|
|
|
|
if (numtargets > 9 || maptargets > 9)
|
|
{
|
|
UINT8 ln[2];
|
|
ln[0] = ((numtargets / 10) % 10);
|
|
ln[1] = (numtargets % 10);
|
|
|
|
V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[ln[0]]);
|
|
V_DrawScaledPatch(fx+17, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[ln[1]]);
|
|
|
|
ln[0] = ((maptargets / 10) % 10);
|
|
ln[1] = (maptargets % 10);
|
|
|
|
V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[ln[0]]);
|
|
V_DrawScaledPatch(fx+31, fy, V_HUDTRANS|V_SLIDEIN|splitflags, fontv[PINGNUM_FONT].font[ln[1]]);
|
|
}
|
|
else
|
|
{
|
|
V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[numtargets % 10]);
|
|
V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_facenum[maptargets % 10]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const UINT8 bumpers = K_Bumpers(stplyr);
|
|
const bool dance = g_pointlimit && (g_pointlimit <= stplyr->roundscore);
|
|
|
|
V_DrawMappedPatch(fx-1, fy-2, V_HUDTRANS|V_SLIDEIN|splitflags, kp_rankbumper, colormap);
|
|
|
|
using srb2::Draw;
|
|
Draw row = Draw(fx+12, fy).flags(V_HUDTRANS|V_SLIDEIN|splitflags).font(Draw::Font::kPing);
|
|
row.text("{:02}", bumpers);
|
|
if (dance && leveltime % 8 < 4)
|
|
{
|
|
row = row.colorize(SKINCOLOR_TANGERINE);
|
|
}
|
|
row.xy(10, -2).patch(kp_pts[1]);
|
|
row
|
|
.x(31)
|
|
.flags(dance ? V_STRINGDANCE : 0)
|
|
.text("{:02}", stplyr->roundscore);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
INT32 fy = r_splitscreen == 1 ? LAPS_Y-3 : LAPS_Y;
|
|
|
|
if (battleprisons)
|
|
{
|
|
if (numtargets > 9 && maptargets > 9)
|
|
V_DrawMappedPatch(LAPS_X, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_capsulestickerwide, NULL);
|
|
else
|
|
V_DrawMappedPatch(LAPS_X, fy, V_HUDTRANS|V_SLIDEIN|splitflags, kp_capsulesticker, NULL);
|
|
V_DrawTimerString(LAPS_X+47, fy+3, V_HUDTRANS|V_SLIDEIN|splitflags, va("%d/%d", numtargets, maptargets));
|
|
}
|
|
else
|
|
{
|
|
const UINT8 bumpers = K_Bumpers(stplyr);
|
|
const bool dance = g_pointlimit && (g_pointlimit <= stplyr->roundscore);
|
|
|
|
if (r_splitscreen == 0)
|
|
{
|
|
fy += 2;
|
|
}
|
|
|
|
K_DrawSticker(LAPS_X+12, fy+5, 75, V_HUDTRANS|V_SLIDEIN|splitflags, false);
|
|
V_DrawMappedPatch(LAPS_X+12, fy-2, V_HUDTRANS|V_SLIDEIN|splitflags, kp_bigbumper, colormap);
|
|
|
|
using srb2::Draw;
|
|
Draw row = Draw(LAPS_X+12+23+1, fy+3).flags(V_HUDTRANS|V_SLIDEIN|splitflags).font(Draw::Font::kThinTimer);
|
|
row.text("{:02}", bumpers);
|
|
if (dance && leveltime % 8 < 4)
|
|
{
|
|
row = row.colorize(SKINCOLOR_TANGERINE);
|
|
}
|
|
row.xy(12, -2).patch(kp_pts[0]);
|
|
row
|
|
.x(12+27)
|
|
.flags(dance ? V_STRINGDANCE : 0)
|
|
.text("{:02}", stplyr->roundscore);
|
|
}
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
static void K_drawKartWanted(void)
|
|
{
|
|
UINT8 i, numwanted = 0;
|
|
UINT8 *colormap = NULL;
|
|
INT32 basex = 0, basey = 0;
|
|
|
|
if (!splitscreen)
|
|
return;
|
|
|
|
if (stplyr != &players[displayplayers[0]])
|
|
return;
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
if (battlewanted[i] == -1)
|
|
break;
|
|
numwanted++;
|
|
}
|
|
|
|
if (numwanted <= 0)
|
|
return;
|
|
|
|
// set X/Y coords depending on splitscreen.
|
|
if (r_splitscreen < 3) // 1P and 2P use the same code.
|
|
{
|
|
basex = WANT_X;
|
|
basey = WANT_Y;
|
|
if (r_splitscreen == 2)
|
|
{
|
|
basey += 16; // slight adjust for 3P
|
|
basex -= 6;
|
|
}
|
|
}
|
|
else if (r_splitscreen == 3) // 4P splitscreen...
|
|
{
|
|
basex = BASEVIDWIDTH/2 - (SHORT(kp_wantedsplit->width)/2); // center on screen
|
|
basey = BASEVIDHEIGHT - 55;
|
|
//basey2 = 4;
|
|
}
|
|
|
|
if (battlewanted[0] != -1)
|
|
colormap = R_GetTranslationColormap(TC_DEFAULT, players[battlewanted[0]].skincolor, GTC_CACHE);
|
|
V_DrawFixedPatch(basex<<FRACBITS, basey<<FRACBITS, FRACUNIT, V_HUDTRANS|V_SLIDEIN|(r_splitscreen < 3 ? V_SNAPTORIGHT : 0)|V_SNAPTOBOTTOM, (r_splitscreen > 1 ? kp_wantedsplit : kp_wanted), colormap);
|
|
/*if (basey2)
|
|
V_DrawFixedPatch(basex<<FRACBITS, basey2<<FRACBITS, FRACUNIT, V_HUDTRANS|V_SLIDEIN|V_SNAPTOTOP, (splitscreen == 3 ? kp_wantedsplit : kp_wanted), colormap); // < used for 4p splits.*/
|
|
|
|
for (i = 0; i < numwanted; i++)
|
|
{
|
|
INT32 x = basex+(r_splitscreen > 1 ? 13 : 8), y = basey+(r_splitscreen > 1 ? 16 : 21);
|
|
fixed_t scale = FRACUNIT/2;
|
|
player_t *p = &players[battlewanted[i]];
|
|
|
|
if (battlewanted[i] == -1)
|
|
break;
|
|
|
|
if (numwanted == 1)
|
|
scale = FRACUNIT;
|
|
else
|
|
{
|
|
if (i & 1)
|
|
x += 16;
|
|
if (i > 1)
|
|
y += 16;
|
|
}
|
|
|
|
if (players[battlewanted[i]].skincolor)
|
|
{
|
|
colormap = R_GetTranslationColormap(TC_RAINBOW, p->skincolor, GTC_CACHE);
|
|
V_DrawFixedPatch(x<<FRACBITS, y<<FRACBITS, FRACUNIT, V_HUDTRANS|V_SLIDEIN|(r_splitscreen < 3 ? V_SNAPTORIGHT : 0)|V_SNAPTOBOTTOM, (scale == FRACUNIT ? faceprefix[p->skin][FACE_WANTED] : faceprefix[p->skin][FACE_RANK]), colormap);
|
|
/*if (basey2) // again with 4p stuff
|
|
V_DrawFixedPatch(x<<FRACBITS, (y - (basey-basey2))<<FRACBITS, FRACUNIT, V_HUDTRANS|V_SLIDEIN|V_SNAPTOTOP, (scale == FRACUNIT ? faceprefix[p->skin][FACE_WANTED] : faceprefix[p->skin][FACE_RANK]), colormap);*/
|
|
}
|
|
}
|
|
}
|
|
#endif //if 0
|
|
|
|
static void K_drawKartPlayerCheck(void)
|
|
{
|
|
const fixed_t maxdistance = FixedMul(1280 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed));
|
|
UINT8 i;
|
|
INT32 splitflags = V_SNAPTOBOTTOM|V_SPLITSCREEN;
|
|
fixed_t y = CHEK_Y * FRACUNIT;
|
|
|
|
if (stplyr == NULL || stplyr->mo == NULL || P_MobjWasRemoved(stplyr->mo))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (stplyr->spectator || stplyr->awayview.tics)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (stplyr->cmd.buttons & BT_LOOKBACK)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
{
|
|
player_t *checkplayer = &players[i];
|
|
fixed_t distance = maxdistance+1;
|
|
UINT8 *colormap = NULL;
|
|
UINT8 pnum = 0;
|
|
vector3_t v;
|
|
vector3_t pPos;
|
|
trackingResult_t result;
|
|
|
|
if (!playeringame[i] || checkplayer->spectator)
|
|
{
|
|
// Not in-game
|
|
continue;
|
|
}
|
|
|
|
if (checkplayer->mo == NULL || P_MobjWasRemoved(checkplayer->mo))
|
|
{
|
|
// No object
|
|
continue;
|
|
}
|
|
|
|
if (checkplayer == stplyr)
|
|
{
|
|
// This is you!
|
|
continue;
|
|
}
|
|
|
|
v.x = R_InterpolateFixed(checkplayer->mo->old_x, checkplayer->mo->x);
|
|
v.y = R_InterpolateFixed(checkplayer->mo->old_y, checkplayer->mo->y);
|
|
v.z = R_InterpolateFixed(checkplayer->mo->old_z, checkplayer->mo->z);
|
|
|
|
pPos.x = R_InterpolateFixed(stplyr->mo->old_x, stplyr->mo->x);
|
|
pPos.y = R_InterpolateFixed(stplyr->mo->old_y, stplyr->mo->y);
|
|
pPos.z = R_InterpolateFixed(stplyr->mo->old_z, stplyr->mo->z);
|
|
|
|
distance = R_PointToDist2(pPos.x, pPos.y, v.x, v.y);
|
|
|
|
if (distance > maxdistance)
|
|
{
|
|
// Too far away
|
|
continue;
|
|
}
|
|
|
|
if ((checkplayer->invincibilitytimer <= 0) && (leveltime & 2))
|
|
{
|
|
pnum++; // white frames
|
|
}
|
|
|
|
if (checkplayer->itemtype == KITEM_GROW || checkplayer->growshrinktimer > 0)
|
|
{
|
|
pnum += 4;
|
|
}
|
|
else if (checkplayer->itemtype == KITEM_INVINCIBILITY || checkplayer->invincibilitytimer)
|
|
{
|
|
pnum += 2;
|
|
}
|
|
|
|
K_ObjectTracking(&result, &v, true);
|
|
|
|
if (result.onScreen == true)
|
|
{
|
|
colormap = R_GetTranslationColormap(TC_DEFAULT, static_cast<skincolornum_t>(checkplayer->mo->color), GTC_CACHE);
|
|
V_DrawFixedPatch(result.x, y, FRACUNIT, V_HUDTRANS|V_SPLITSCREEN|splitflags, kp_check[pnum], colormap);
|
|
}
|
|
}
|
|
}
|
|
|
|
static boolean K_ShowPlayerNametag(player_t *p)
|
|
{
|
|
if (cv_seenames.value == 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (demo.playback == true && camera[R_GetViewNumber()].freecam == true)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (stplyr == p)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (gametyperules & GTR_CIRCUIT)
|
|
{
|
|
if ((p->position == 0)
|
|
|| (stplyr->position == 0)
|
|
|| (p->position < stplyr->position-2)
|
|
|| (p->position > stplyr->position+2))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void K_DrawLocalTagForPlayer(fixed_t x, fixed_t y, player_t *p, UINT8 id)
|
|
{
|
|
UINT8 blink = ((leveltime / 7) & 1);
|
|
UINT8 *colormap = R_GetTranslationColormap(TC_RAINBOW, static_cast<skincolornum_t>(p->skincolor), GTC_CACHE);
|
|
V_DrawFixedPatch(x, y, FRACUNIT, V_HUDTRANS|V_SPLITSCREEN, kp_localtag[id][blink], colormap);
|
|
}
|
|
|
|
static void K_DrawRivalTagForPlayer(fixed_t x, fixed_t y)
|
|
{
|
|
UINT8 blink = ((leveltime / 7) & 1);
|
|
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, INT32 flags)
|
|
{
|
|
if (p->typing_duration > duration)
|
|
{
|
|
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, INT32 flags)
|
|
{
|
|
if (p->cmd.flags & TICCMD_TYPING)
|
|
{
|
|
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, flags);
|
|
K_DrawTypingDot(x + 6*FRACUNIT - FRACUNIT/3, y, 31, p, flags);
|
|
K_DrawTypingDot(x + 9*FRACUNIT - FRACUNIT/3, y, 47, p, flags);
|
|
}
|
|
}
|
|
|
|
// 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);
|
|
|
|
UINT8 *colormap = V_GetStringColormap(clr);
|
|
INT32 barx = 0, bary = 0, barw = 0;
|
|
|
|
UINT8 cnum = R_GetViewNumber();
|
|
|
|
// Since there's no "V_DrawFixedFill", and I don't feel like making it,
|
|
// fuck it, we're gonna just V_NOSCALESTART hack it
|
|
if (r_splitscreen > 1 && cnum & 1)
|
|
{
|
|
x += (BASEVIDWIDTH/2) * FRACUNIT;
|
|
}
|
|
|
|
if ((r_splitscreen == 1 && cnum == 1)
|
|
|| (r_splitscreen > 1 && cnum > 1))
|
|
{
|
|
y += (BASEVIDHEIGHT/2) * FRACUNIT;
|
|
}
|
|
|
|
barw = (namelen * vid.dupx);
|
|
|
|
barx = (x * vid.dupx) / FRACUNIT;
|
|
bary = (y * vid.dupy) / FRACUNIT;
|
|
|
|
barx += (6 * vid.dupx);
|
|
bary -= (16 * vid.dupx);
|
|
|
|
// Center it if necessary
|
|
if (vid.width != BASEVIDWIDTH * vid.dupx)
|
|
{
|
|
barx += (vid.width - (BASEVIDWIDTH * vid.dupx)) / 2;
|
|
}
|
|
|
|
if (vid.height != BASEVIDHEIGHT * vid.dupy)
|
|
{
|
|
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|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, flags, kp_nametagstem, colormap);
|
|
|
|
// Draw the name itself
|
|
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
|
|
{
|
|
UINT8 i;
|
|
INT32 x;
|
|
INT32 y;
|
|
boolean candrawtag;
|
|
} weakspotdraw_t;
|
|
|
|
static void K_DrawWeakSpot(weakspotdraw_t *ws)
|
|
{
|
|
UINT8 *colormap;
|
|
UINT8 j = (bossinfo.weakspots[ws->i].type == SPOT_BUMP) ? 1 : 0;
|
|
tic_t flashtime = ~1; // arbitrary high even number
|
|
|
|
if (bossinfo.weakspots[ws->i].time < TICRATE)
|
|
{
|
|
if (bossinfo.weakspots[ws->i].time & 1)
|
|
return;
|
|
|
|
flashtime = bossinfo.weakspots[ws->i].time;
|
|
}
|
|
else if (bossinfo.weakspots[ws->i].time > (WEAKSPOTANIMTIME - TICRATE))
|
|
flashtime = WEAKSPOTANIMTIME - bossinfo.weakspots[ws->i].time;
|
|
|
|
if (flashtime & 1)
|
|
colormap = R_GetTranslationColormap(TC_ALLWHITE, SKINCOLOR_NONE, GTC_CACHE);
|
|
else
|
|
colormap = R_GetTranslationColormap(TC_RAINBOW, static_cast<skincolornum_t>(bossinfo.weakspots[ws->i].color), GTC_CACHE);
|
|
|
|
V_DrawFixedPatch(ws->x, ws->y, FRACUNIT, 0, kp_bossret[j], colormap);
|
|
|
|
if (!ws->candrawtag || flashtime & 1 || flashtime < TICRATE/2)
|
|
return;
|
|
|
|
V_DrawFixedPatch(ws->x, ws->y, FRACUNIT, 0, kp_bossret[j+1], colormap);
|
|
}
|
|
|
|
static void K_drawKartNameTags(void)
|
|
{
|
|
vector3_t c;
|
|
UINT8 cnum = R_GetViewNumber();
|
|
size_t i, j;
|
|
|
|
if (stplyr == NULL || stplyr->mo == NULL || P_MobjWasRemoved(stplyr->mo))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (stplyr->awayview.tics)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Crop within splitscreen bounds
|
|
switch (r_splitscreen)
|
|
{
|
|
case 1:
|
|
V_SetClipRect(
|
|
0,
|
|
cnum == 1 ? (BASEVIDHEIGHT / 2) * FRACUNIT : 0,
|
|
BASEVIDWIDTH * FRACUNIT,
|
|
(BASEVIDHEIGHT / 2) * FRACUNIT,
|
|
0
|
|
);
|
|
break;
|
|
|
|
case 2:
|
|
case 3:
|
|
V_SetClipRect(
|
|
cnum & 1 ? (BASEVIDWIDTH / 2) * FRACUNIT : 0,
|
|
cnum > 1 ? (BASEVIDHEIGHT / 2) * FRACUNIT : 0,
|
|
(BASEVIDWIDTH / 2) * FRACUNIT,
|
|
(BASEVIDHEIGHT / 2) * FRACUNIT,
|
|
0
|
|
);
|
|
break;
|
|
}
|
|
|
|
c.x = viewx;
|
|
c.y = viewy;
|
|
c.z = viewz;
|
|
|
|
// Maybe shouldn't be handling this here... but the camera info is too good.
|
|
if (bossinfo.valid == true)
|
|
{
|
|
weakspotdraw_t weakspotdraw[NUMWEAKSPOTS];
|
|
UINT8 numdraw = 0;
|
|
boolean onleft = false;
|
|
|
|
for (i = 0; i < NUMWEAKSPOTS; i++)
|
|
{
|
|
trackingResult_t result;
|
|
vector3_t v;
|
|
|
|
if (bossinfo.weakspots[i].spot == NULL || P_MobjWasRemoved(bossinfo.weakspots[i].spot))
|
|
{
|
|
// No object
|
|
continue;
|
|
}
|
|
|
|
if (bossinfo.weakspots[i].time == 0 || bossinfo.weakspots[i].type == SPOT_NONE)
|
|
{
|
|
// not visible
|
|
continue;
|
|
}
|
|
|
|
v.x = R_InterpolateFixed(bossinfo.weakspots[i].spot->old_x, bossinfo.weakspots[i].spot->x);
|
|
v.y = R_InterpolateFixed(bossinfo.weakspots[i].spot->old_y, bossinfo.weakspots[i].spot->y);
|
|
v.z = R_InterpolateFixed(bossinfo.weakspots[i].spot->old_z, bossinfo.weakspots[i].spot->z);
|
|
|
|
v.z += (bossinfo.weakspots[i].spot->height / 2);
|
|
|
|
K_ObjectTracking(&result, &v, false);
|
|
if (result.onScreen == false)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
weakspotdraw[numdraw].i = i;
|
|
weakspotdraw[numdraw].x = result.x;
|
|
weakspotdraw[numdraw].y = result.y;
|
|
weakspotdraw[numdraw].candrawtag = true;
|
|
|
|
for (j = 0; j < numdraw; j++)
|
|
{
|
|
if (abs(weakspotdraw[j].x - weakspotdraw[numdraw].x) > 50*FRACUNIT)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
onleft = (weakspotdraw[j].x < weakspotdraw[numdraw].x);
|
|
|
|
if (abs((onleft ? -5 : 5)
|
|
+ weakspotdraw[j].y - weakspotdraw[numdraw].y) > 18*FRACUNIT)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (weakspotdraw[j].x < weakspotdraw[numdraw].x)
|
|
{
|
|
weakspotdraw[j].candrawtag = false;
|
|
break;
|
|
}
|
|
|
|
weakspotdraw[numdraw].candrawtag = false;
|
|
break;
|
|
}
|
|
|
|
numdraw++;
|
|
}
|
|
|
|
for (i = 0; i < numdraw; i++)
|
|
{
|
|
K_DrawWeakSpot(&weakspotdraw[i]);
|
|
}
|
|
}
|
|
|
|
K_drawTargetHUD(&c, stplyr);
|
|
|
|
V_ClearClipRect();
|
|
}
|
|
|
|
#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.
|
|
// The number being divided by is for how fast it moves.
|
|
// The higher the number, the slower it moves.
|
|
|
|
// am xpos & ypos are the icon's starting position. Withouht
|
|
// it, they wouldn't 'spawn' on the top-right side of the HUD.
|
|
|
|
fixed_t amnumxpos, amnumypos;
|
|
INT32 amxpos, amypos;
|
|
|
|
amnumxpos = (FixedMul(objx, minimapinfo.zoom) - minimapinfo.offs_x);
|
|
amnumypos = -(FixedMul(objy, minimapinfo.zoom) - minimapinfo.offs_y);
|
|
|
|
if (encoremode)
|
|
amnumxpos = -amnumxpos;
|
|
|
|
amxpos = amnumxpos + ((hudx - (SHORT(icon->width))/2)<<FRACBITS);
|
|
amypos = amnumypos + ((hudy - (SHORT(icon->height))/2)<<FRACBITS);
|
|
|
|
V_DrawFixedPatch(amxpos, amypos, FRACUNIT, flags, icon, colormap);
|
|
}
|
|
|
|
static void K_drawKartMinimapDot(fixed_t objx, fixed_t objy, INT32 hudx, INT32 hudy, INT32 flags, UINT8 color, UINT8 size)
|
|
{
|
|
fixed_t amnumxpos, amnumypos;
|
|
INT32 amxpos, amypos;
|
|
|
|
amnumxpos = (FixedMul(objx, minimapinfo.zoom) - minimapinfo.offs_x);
|
|
amnumypos = -(FixedMul(objy, minimapinfo.zoom) - minimapinfo.offs_y);
|
|
|
|
if (encoremode)
|
|
amnumxpos = -amnumxpos;
|
|
|
|
amxpos = (amnumxpos / FRACUNIT);
|
|
amypos = (amnumypos / FRACUNIT);
|
|
|
|
if (flags & V_NOSCALESTART)
|
|
{
|
|
amxpos *= vid.dupx;
|
|
amypos *= vid.dupy;
|
|
}
|
|
|
|
V_DrawFill((amxpos + hudx) - (size / 2), (amypos + hudy) - (size / 2), size, size, flags | color);
|
|
}
|
|
|
|
static UINT8 K_RankMinimapWaypoint(waypoint_t *wp)
|
|
{
|
|
if (wp == stplyr->nextwaypoint)
|
|
{
|
|
return 4;
|
|
}
|
|
else if (wp->numnextwaypoints == 0 || wp->numprevwaypoints == 0)
|
|
{
|
|
return 3;
|
|
}
|
|
else if (!K_GetWaypointIsEnabled(wp)) // disabled
|
|
{
|
|
return 2;
|
|
}
|
|
else if (K_GetWaypointIsShortcut(wp)) // shortcut
|
|
{
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
static void K_drawKartMinimapWaypoint(waypoint_t *wp, UINT8 rank, INT32 hudx, INT32 hudy, INT32 flags)
|
|
{
|
|
static UINT8 colors[] =
|
|
{
|
|
0x95, // blue (0 - default)
|
|
0x20, // pink (1 - shortcut)
|
|
0x10, // gray (2 - disabled)
|
|
0x40, // yellow (3 - error)
|
|
0x70, // green (4 - player)
|
|
};
|
|
|
|
UINT8 pal = colors[rank]; // blue
|
|
UINT8 size = 3;
|
|
|
|
if (rank == 4)
|
|
{
|
|
size = 6;
|
|
}
|
|
|
|
if (!(flags & V_NOSCALESTART))
|
|
{
|
|
hudx *= vid.dupx;
|
|
hudy *= vid.dupy;
|
|
}
|
|
|
|
K_drawKartMinimapDot(wp->mobj->x, wp->mobj->y, hudx, hudy, flags | V_NOSCALESTART, pal, size);
|
|
}
|
|
|
|
#define ICON_DOT_RADIUS (10)
|
|
|
|
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;
|
|
|
|
UINT8 minipal;
|
|
|
|
// Draw the HUD only when playing in a level.
|
|
// hu_stuff needs this, unlike st_stuff.
|
|
if (gamestate != GS_LEVEL)
|
|
return;
|
|
|
|
// Only draw for the first player
|
|
// Maybe move this somewhere else where this won't be a concern?
|
|
if (R_GetViewNumber() != 0)
|
|
return;
|
|
|
|
if (specialstageinfo.valid == true)
|
|
{
|
|
// 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 (doprogressionbar == false)
|
|
{
|
|
if (minimapinfo.minimap_pic == NULL)
|
|
{
|
|
return; // no pic, just get outta here
|
|
}
|
|
|
|
else if (r_splitscreen < 1) // 1P right aligned
|
|
{
|
|
splitflags = (V_SLIDEIN|V_SNAPTORIGHT);
|
|
}
|
|
else // 2/4P splits
|
|
{
|
|
if (r_splitscreen == 1)
|
|
splitflags = V_SNAPTORIGHT; // 2P right aligned
|
|
|
|
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
|
|
{
|
|
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)
|
|
{
|
|
minimaptrans = FixedMul(minimaptrans, (st_translucency * FRACUNIT) / 10);
|
|
|
|
if (!minimaptrans)
|
|
return;
|
|
}
|
|
|
|
minimaptrans = ((10-minimaptrans)<<V_ALPHASHIFT);
|
|
|
|
// Really looking forward to never writing this loop again
|
|
UINT8 bestplayer = MAXPLAYERS;
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
{
|
|
if (!playeringame[i])
|
|
continue;
|
|
if (players[i].spectator)
|
|
continue;
|
|
if (players[i].position == 1)
|
|
bestplayer = i;
|
|
}
|
|
|
|
if (bestplayer == MAXPLAYERS || leveltime < starttime) // POSITION / no players
|
|
minipal = ((leveltime/10)%2) ? SKINCOLOR_WHITE : SKINCOLOR_BLACK;
|
|
else if (players[bestplayer].laps >= numlaps) // Final lap
|
|
minipal = K_RainbowColor(leveltime);
|
|
else // Standard: color to leader
|
|
minipal = players[bestplayer].skincolor;
|
|
|
|
if (doencore)
|
|
{
|
|
V_DrawFixedPatch(
|
|
(x + (SHORT(workingPic->width)/2))*FRACUNIT,
|
|
(y - (SHORT(workingPic->height)/2))*FRACUNIT,
|
|
FRACUNIT,
|
|
splitflags|minimaptrans|V_FLIP,
|
|
workingPic,
|
|
R_GetTranslationColormap(TC_DEFAULT, static_cast<skincolornum_t>(minipal), GTC_CACHE)
|
|
);
|
|
}
|
|
else
|
|
{
|
|
V_DrawFixedPatch(
|
|
(x - (SHORT(workingPic->width)/2))*FRACUNIT,
|
|
(y - (SHORT(workingPic->height)/2))*FRACUNIT,
|
|
FRACUNIT,
|
|
splitflags|minimaptrans,
|
|
workingPic,
|
|
R_GetTranslationColormap(TC_DEFAULT, static_cast<skincolornum_t>(minipal), GTC_CACHE)
|
|
);
|
|
}
|
|
|
|
// most icons will be rendered semi-ghostly.
|
|
splitflags |= V_HUDTRANSHALF;
|
|
|
|
// let offsets transfer to the heads, too!
|
|
if (doencore)
|
|
x += SHORT(workingPic->leftoffset);
|
|
else
|
|
x -= SHORT(workingPic->leftoffset);
|
|
y -= SHORT(workingPic->topoffset);
|
|
|
|
if (doprogressionbar == true)
|
|
{
|
|
x -= PROGRESSION_BAR_WIDTH/2;
|
|
}
|
|
|
|
// Draw the super item in Battle
|
|
if (doprogressionbar == false && (gametyperules & GTR_OVERTIME) && battleovertime.enabled)
|
|
{
|
|
if (battleovertime.enabled >= 10*TICRATE || (battleovertime.enabled & 1))
|
|
{
|
|
const INT32 prevsplitflags = splitflags;
|
|
splitflags &= ~V_HUDTRANSHALF;
|
|
splitflags |= V_HUDTRANS;
|
|
colormap = R_GetTranslationColormap(TC_RAINBOW, static_cast<skincolornum_t>(K_RainbowColor(leveltime)), GTC_CACHE);
|
|
K_drawKartMinimapIcon(battleovertime.x, battleovertime.y, x, y, splitflags, kp_itemminimap, colormap);
|
|
splitflags = prevsplitflags;
|
|
}
|
|
}
|
|
|
|
// initialize
|
|
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
|
|
localplayers[i] = -1;
|
|
|
|
// Player's tiny icons on the Automap. (drawn opposite direction so player 1 is drawn last in splitscreen)
|
|
if (ghosts && doprogressionbar == false) // future work: show ghosts on progression bar
|
|
{
|
|
demoghost *g = ghosts;
|
|
while (g)
|
|
{
|
|
if (g->mo && !P_MobjWasRemoved(g->mo) && g->mo->skin)
|
|
{
|
|
skin = ((skin_t*)g->mo->skin)-skins;
|
|
|
|
workingPic = R_CanShowSkinInDemo(skin) ? faceprefix[skin][FACE_MINIMAP] : kp_unknownminimap;
|
|
|
|
if (g->mo->color)
|
|
{
|
|
if (g->mo->colorized)
|
|
colormap = R_GetTranslationColormap(TC_RAINBOW, static_cast<skincolornum_t>(g->mo->color), GTC_CACHE);
|
|
else
|
|
colormap = R_GetTranslationColormap(skin, static_cast<skincolornum_t>(g->mo->color), GTC_CACHE);
|
|
}
|
|
else
|
|
colormap = NULL;
|
|
|
|
interpx = R_InterpolateFixed(g->mo->old_x, g->mo->x);
|
|
interpy = R_InterpolateFixed(g->mo->old_y, g->mo->y);
|
|
|
|
K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, workingPic, colormap);
|
|
}
|
|
|
|
g = g->next;
|
|
}
|
|
}
|
|
|
|
{
|
|
for (i = MAXPLAYERS-1; i >= 0; i--)
|
|
{
|
|
if (!playeringame[i])
|
|
continue;
|
|
if (!players[i].mo || players[i].spectator || !players[i].mo->skin
|
|
|| (doprogressionbar == false && players[i].exiting))
|
|
continue;
|
|
|
|
// This player is out of the game!
|
|
if ((gametyperules & GTR_BUMPERS) && (players[i].pflags & PF_ELIMINATED))
|
|
continue;
|
|
|
|
// This gets set for a player who has GAME OVER'd
|
|
if (P_MobjIsReappearing(players[i].mo))
|
|
continue;
|
|
|
|
if (i == displayplayers[0] || i == displayplayers[1] || i == displayplayers[2] || i == displayplayers[3])
|
|
{
|
|
// Draw display players on top of everything else
|
|
localplayers[numlocalplayers++] = i;
|
|
continue;
|
|
}
|
|
|
|
if (players[i].hyudorotimer > 0)
|
|
{
|
|
if (!((players[i].hyudorotimer < TICRATE/2
|
|
|| players[i].hyudorotimer > hyudorotime-(TICRATE/2))
|
|
&& !(leveltime & 1)))
|
|
continue;
|
|
}
|
|
|
|
mobj = players[i].mo;
|
|
|
|
if (mobj->health <= 0 && (players[i].pflags & PF_NOCONTEST))
|
|
{
|
|
if (P_MobjWasRemoved(mobj->tracer))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (mobj->tracer->renderflags & RF_DONTDRAW)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
workingPic = kp_nocontestminimap;
|
|
colormap = R_GetTranslationColormap(TC_DEFAULT, static_cast<skincolornum_t>(mobj->color), GTC_CACHE);
|
|
|
|
mobj = mobj->tracer;
|
|
}
|
|
else
|
|
{
|
|
skin = ((skin_t*)mobj->skin)-skins;
|
|
|
|
workingPic = R_CanShowSkinInDemo(skin) ? faceprefix[skin][FACE_MINIMAP] : kp_unknownminimap;
|
|
|
|
if (mobj->color)
|
|
{
|
|
if (mobj->colorized)
|
|
colormap = R_GetTranslationColormap(TC_RAINBOW, static_cast<skincolornum_t>(mobj->color), GTC_CACHE);
|
|
else
|
|
colormap = R_GetTranslationColormap(skin, static_cast<skincolornum_t>(mobj->color), GTC_CACHE);
|
|
}
|
|
else
|
|
colormap = NULL;
|
|
}
|
|
|
|
if (doprogressionbar == false)
|
|
{
|
|
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
|
|
if (doprogressionbar == true)
|
|
{
|
|
// future work: support these specific objects on this
|
|
}
|
|
else for (mobj = trackercap; mobj; mobj = next)
|
|
{
|
|
next = mobj->itnext;
|
|
|
|
workingPic = NULL;
|
|
colormap = NULL;
|
|
|
|
if (mobj->health <= 0)
|
|
continue;
|
|
|
|
switch (mobj->type)
|
|
{
|
|
case MT_SPB:
|
|
workingPic = kp_spbminimap;
|
|
#if 0
|
|
if (mobj->target && !P_MobjWasRemoved(mobj->target) && mobj->target->player && mobj->target->player->skincolor)
|
|
{
|
|
colormap = R_GetTranslationColormap(TC_RAINBOW, mobj->target->player->skincolor, GTC_CACHE);
|
|
}
|
|
else
|
|
#endif
|
|
if (mobj->color)
|
|
{
|
|
colormap = R_GetTranslationColormap(TC_RAINBOW, static_cast<skincolornum_t>(mobj->color), GTC_CACHE);
|
|
}
|
|
|
|
break;
|
|
case MT_BATTLECAPSULE:
|
|
workingPic = kp_capsuleminimap[(mobj->extravalue1 != 0 ? 1 : 0)];
|
|
break;
|
|
case MT_CDUFO:
|
|
if (battleprisons)
|
|
workingPic = kp_capsuleminimap[2];
|
|
break;
|
|
case MT_BATTLEUFO:
|
|
workingPic = kp_battleufominimap;
|
|
break;
|
|
case MT_SUPER_FLICKY:
|
|
workingPic = kp_superflickyminimap;
|
|
if (mobj_t* owner = Obj_SuperFlickyOwner(mobj); owner && owner->color)
|
|
{
|
|
colormap = R_GetTranslationColormap(TC_RAINBOW, static_cast<skincolornum_t>(owner->color), GTC_CACHE);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (!workingPic)
|
|
continue;
|
|
|
|
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);
|
|
}
|
|
|
|
// draw our local players here, opaque.
|
|
{
|
|
splitflags &= ~V_HUDTRANSHALF;
|
|
splitflags |= V_HUDTRANS;
|
|
}
|
|
|
|
// ...but first, any boss targets.
|
|
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
|
|
{
|
|
UINT8 emid = 0;
|
|
if (specialstageinfo.ufo->cvmem > 7)
|
|
emid = 1;
|
|
workingPic = kp_emeraldminimap[emid];
|
|
|
|
if (specialstageinfo.ufo->color)
|
|
{
|
|
colormap = R_GetTranslationColormap(TC_DEFAULT, static_cast<skincolornum_t>(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++)
|
|
{
|
|
// exists at all?
|
|
if (bossinfo.weakspots[i].spot == NULL || P_MobjWasRemoved(bossinfo.weakspots[i].spot))
|
|
continue;
|
|
// shows on the minimap?
|
|
if (bossinfo.weakspots[i].minimap == false)
|
|
continue;
|
|
// in the flashing period?
|
|
if ((bossinfo.weakspots[i].time > (WEAKSPOTANIMTIME-(TICRATE/2))) && (bossinfo.weakspots[i].time & 1))
|
|
continue;
|
|
|
|
colormap = NULL;
|
|
|
|
if (bossinfo.weakspots[i].color)
|
|
colormap = R_GetTranslationColormap(TC_RAINBOW, static_cast<skincolornum_t>(bossinfo.weakspots[i].color), GTC_CACHE);
|
|
|
|
interpx = R_InterpolateFixed(bossinfo.weakspots[i].spot->old_x, bossinfo.weakspots[i].spot->x);
|
|
interpy = R_InterpolateFixed(bossinfo.weakspots[i].spot->old_y, bossinfo.weakspots[i].spot->y);
|
|
|
|
// temporary graphic?
|
|
K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, kp_wantedreticle, colormap);
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < numlocalplayers; i++)
|
|
{
|
|
boolean nocontest = false;
|
|
|
|
if (localplayers[i] == -1)
|
|
continue; // this doesn't interest us
|
|
|
|
if ((players[localplayers[i]].hyudorotimer > 0) && (leveltime & 1))
|
|
continue;
|
|
|
|
mobj = players[localplayers[i]].mo;
|
|
|
|
// This gets set for a player who has GAME OVER'd
|
|
if (P_MobjIsReappearing(mobj))
|
|
continue;
|
|
|
|
if (mobj->health <= 0 && (players[localplayers[i]].pflags & PF_NOCONTEST))
|
|
{
|
|
if (P_MobjWasRemoved(mobj->tracer))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (mobj->tracer->renderflags & RF_DONTDRAW)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
workingPic = kp_nocontestminimap;
|
|
colormap = R_GetTranslationColormap(TC_DEFAULT, static_cast<skincolornum_t>(mobj->color), GTC_CACHE);
|
|
|
|
mobj = mobj->tracer;
|
|
|
|
nocontest = true;
|
|
}
|
|
else
|
|
{
|
|
skin = ((skin_t*)mobj->skin)-skins;
|
|
|
|
workingPic = R_CanShowSkinInDemo(skin) ? faceprefix[skin][FACE_MINIMAP] : kp_unknownminimap;
|
|
|
|
if (mobj->color)
|
|
{
|
|
if (mobj->colorized)
|
|
colormap = R_GetTranslationColormap(TC_RAINBOW, static_cast<skincolornum_t>(mobj->color), GTC_CACHE);
|
|
else
|
|
colormap = R_GetTranslationColormap(skin, static_cast<skincolornum_t>(mobj->color), GTC_CACHE);
|
|
}
|
|
else
|
|
colormap = NULL;
|
|
}
|
|
|
|
if (doprogressionbar == false)
|
|
{
|
|
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);
|
|
}
|
|
|
|
if (!nocontest)
|
|
{
|
|
angle_t ang = R_InterpolateAngle(mobj->old_angle, mobj->angle);
|
|
if (encoremode)
|
|
ang = ANGLE_180 - ang;
|
|
|
|
K_drawKartMinimapIcon(
|
|
interpx,
|
|
interpy,
|
|
x + FixedMul(FCOS(ang), ICON_DOT_RADIUS),
|
|
y - FixedMul(FSIN(ang), ICON_DOT_RADIUS),
|
|
splitflags,
|
|
kp_minimapdot,
|
|
colormap
|
|
);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
K_drawKartProgressionMinimapIcon(players[localplayers[i]].distancetofinish, x, y, splitflags, workingPic, colormap);
|
|
}
|
|
}
|
|
|
|
if (doprogressionbar == false && cv_kartdebugwaypoints.value != 0)
|
|
{
|
|
struct MiniWaypoint
|
|
{
|
|
waypoint_t* waypoint;
|
|
UINT8 rank;
|
|
|
|
MiniWaypoint(waypoint_t* wp) : waypoint(wp), rank(K_RankMinimapWaypoint(wp)) {}
|
|
|
|
bool operator<(const MiniWaypoint& b) const noexcept { return rank < b.rank; }
|
|
};
|
|
|
|
std::vector<MiniWaypoint> waypoints;
|
|
size_t idx;
|
|
|
|
waypoints.reserve(K_GetNumWaypoints());
|
|
|
|
for (idx = 0; idx < K_GetNumWaypoints(); ++idx)
|
|
{
|
|
waypoint_t *wp = K_GetWaypointFromIndex(idx);
|
|
|
|
I_Assert(wp != NULL);
|
|
|
|
waypoints.push_back(wp);
|
|
}
|
|
|
|
std::sort(waypoints.begin(), waypoints.end());
|
|
|
|
for (MiniWaypoint& wp : waypoints)
|
|
{
|
|
K_drawKartMinimapWaypoint(wp.waypoint, wp.rank, x, y, splitflags);
|
|
}
|
|
}
|
|
}
|
|
|
|
#undef PROGRESSION_BAR_WIDTH
|
|
|
|
static void K_drawKartFinish(boolean finish)
|
|
{
|
|
INT32 timer, minsplitstationary, pnum = 0, splitflags = V_SPLITSCREEN;
|
|
patch_t **kptodraw;
|
|
|
|
if (finish)
|
|
{
|
|
if (gametyperules & GTR_SPECIALSTART)
|
|
return;
|
|
|
|
timer = stplyr->karthud[khud_finish];
|
|
kptodraw = kp_racefinish;
|
|
minsplitstationary = 2;
|
|
}
|
|
else
|
|
{
|
|
timer = stplyr->karthud[khud_fault];
|
|
kptodraw = kp_racefault;
|
|
minsplitstationary = 1;
|
|
}
|
|
|
|
if (!timer || timer > 2*TICRATE)
|
|
return;
|
|
|
|
if ((timer % (2*5)) / 5) // blink
|
|
pnum = 1;
|
|
|
|
if (r_splitscreen > 0)
|
|
pnum += (r_splitscreen > 1) ? 2 : 4;
|
|
|
|
if (r_splitscreen >= minsplitstationary) // 3/4p, stationary FIN
|
|
{
|
|
V_DrawScaledPatch(STCD_X - (SHORT(kptodraw[pnum]->width)/2), STCD_Y - (SHORT(kptodraw[pnum]->height)/2), splitflags, kptodraw[pnum]);
|
|
return;
|
|
}
|
|
|
|
//else -- 1/2p, scrolling FINISH
|
|
{
|
|
INT32 x, xval, ox, interpx, pwidth;
|
|
|
|
x = ((vid.width<<FRACBITS)/vid.dupx);
|
|
xval = (SHORT(kptodraw[pnum]->width)<<FRACBITS);
|
|
|
|
pwidth = std::max(xval, x);
|
|
|
|
x = ((TICRATE - timer) * pwidth) / TICRATE;
|
|
ox = ((TICRATE - (timer - 1)) * pwidth) / TICRATE;
|
|
|
|
interpx = R_InterpolateFixed(ox, x);
|
|
|
|
if (r_splitscreen && R_GetViewNumber() == 1)
|
|
interpx = -interpx;
|
|
|
|
V_DrawFixedPatch(interpx + (STCD_X<<FRACBITS) - (pwidth / 2),
|
|
(STCD_Y<<FRACBITS) - (SHORT(kptodraw[pnum]->height)<<(FRACBITS-1)),
|
|
FRACUNIT,
|
|
splitflags, kptodraw[pnum], NULL);
|
|
}
|
|
}
|
|
|
|
static void K_drawKartStartBulbs(void)
|
|
{
|
|
const UINT8 start_animation[14] = {
|
|
1, 2, 3, 4, 5, 6, 7, 8,
|
|
7, 6,
|
|
9, 10, 11, 12
|
|
};
|
|
|
|
const UINT8 loop_animation[4] = {
|
|
12, 13, 12, 14
|
|
};
|
|
|
|
const UINT8 chillloop_animation[2] = {
|
|
11, 12
|
|
};
|
|
|
|
const UINT8 letters_order[10] = {
|
|
0, 1, 2, 3, 4, 3, 1, 5, 6, 6
|
|
};
|
|
|
|
const UINT8 letters_transparency[40] = {
|
|
0, 2, 4, 6, 8,
|
|
10, 10, 10, 10, 10,
|
|
10, 10, 10, 10, 10,
|
|
10, 10, 10, 10, 10,
|
|
10, 10, 10, 10, 10,
|
|
10, 10, 10, 10, 10,
|
|
10, 10, 10, 10, 10,
|
|
10, 8, 6, 4, 2
|
|
};
|
|
|
|
fixed_t spacing = 24*FRACUNIT;
|
|
|
|
fixed_t startx = (BASEVIDWIDTH/2)*FRACUNIT;
|
|
fixed_t starty = 48*FRACUNIT;
|
|
fixed_t x, y;
|
|
|
|
UINT8 numperrow = numbulbs/2;
|
|
UINT8 i;
|
|
|
|
if (r_splitscreen >= 1)
|
|
{
|
|
spacing /= 2;
|
|
starty /= 3;
|
|
|
|
if (r_splitscreen > 1)
|
|
{
|
|
startx /= 2;
|
|
}
|
|
}
|
|
|
|
startx += (spacing/2);
|
|
|
|
if (numbulbs <= 10)
|
|
{
|
|
// No second row
|
|
numperrow = numbulbs;
|
|
}
|
|
else
|
|
{
|
|
if (numbulbs & 1)
|
|
{
|
|
numperrow++;
|
|
}
|
|
|
|
starty -= (spacing/2);
|
|
}
|
|
|
|
startx -= (spacing/2) * numperrow;
|
|
|
|
x = startx;
|
|
y = starty;
|
|
|
|
for (i = 0; i < numbulbs; i++)
|
|
{
|
|
UINT8 patchnum = 0;
|
|
INT32 bulbtic = (leveltime - introtime - TICRATE) - (bulbtime * i);
|
|
|
|
if (i == numperrow)
|
|
{
|
|
y += spacing;
|
|
x = startx + (spacing/2);
|
|
}
|
|
|
|
if (bulbtic > 0)
|
|
{
|
|
if (bulbtic < 14)
|
|
{
|
|
patchnum = start_animation[bulbtic];
|
|
}
|
|
else
|
|
{
|
|
const INT32 length = (bulbtime * 3);
|
|
|
|
bulbtic -= 14;
|
|
|
|
if (bulbtic > length)
|
|
{
|
|
bulbtic -= length;
|
|
patchnum = chillloop_animation[bulbtic % 2];
|
|
}
|
|
else
|
|
{
|
|
patchnum = loop_animation[bulbtic % 4];
|
|
}
|
|
}
|
|
}
|
|
|
|
V_DrawFixedPatch(x, y, FRACUNIT, V_SNAPTOTOP|V_SPLITSCREEN,
|
|
(r_splitscreen ? kp_prestartbulb_split[patchnum] : kp_prestartbulb[patchnum]), NULL);
|
|
x += spacing;
|
|
}
|
|
|
|
x = 70*FRACUNIT;
|
|
y = starty;
|
|
|
|
if (r_splitscreen == 1)
|
|
{
|
|
x = 106*FRACUNIT;
|
|
}
|
|
else if (r_splitscreen > 1)
|
|
{
|
|
x = 28*FRACUNIT;
|
|
}
|
|
|
|
if (timeinmap < 16)
|
|
return; // temporary for current map start behaviour
|
|
|
|
for (i = 0; i < 10; i++)
|
|
{
|
|
UINT8 patchnum = letters_order[i];
|
|
INT32 transflag = letters_transparency[(leveltime - i) % 40];
|
|
patch_t *patch = (r_splitscreen ? kp_prestartletters_split[patchnum] : kp_prestartletters[patchnum]);
|
|
|
|
if (transflag >= 10)
|
|
;
|
|
else
|
|
{
|
|
if (transflag != 0)
|
|
transflag = transflag << FF_TRANSSHIFT;
|
|
|
|
V_DrawFixedPatch(x, y, FRACUNIT, V_SNAPTOTOP|V_SPLITSCREEN|transflag, patch, NULL);
|
|
}
|
|
|
|
if (i < 9)
|
|
{
|
|
x += (SHORT(patch->width)) * FRACUNIT/2;
|
|
|
|
patchnum = letters_order[i+1];
|
|
patch = (r_splitscreen ? kp_prestartletters_split[patchnum] : kp_prestartletters[patchnum]);
|
|
x += (SHORT(patch->width)) * FRACUNIT/2;
|
|
|
|
if (r_splitscreen)
|
|
x -= FRACUNIT;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void K_drawKartStartCountdown(void)
|
|
{
|
|
INT32 pnum = 0;
|
|
|
|
if (leveltime >= introtime && leveltime < starttime-(3*TICRATE))
|
|
{
|
|
if (numbulbs > 1)
|
|
K_drawKartStartBulbs();
|
|
}
|
|
else
|
|
{
|
|
|
|
if (leveltime >= starttime-(2*TICRATE)) // 2
|
|
pnum++;
|
|
if (leveltime >= starttime-TICRATE) // 1
|
|
pnum++;
|
|
|
|
if (leveltime >= starttime) // GO!
|
|
{
|
|
UINT8 i;
|
|
UINT8 numplayers = 0;
|
|
|
|
pnum++;
|
|
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
{
|
|
if (playeringame[i] && !players[i].spectator)
|
|
numplayers++;
|
|
|
|
if (numplayers > 2)
|
|
break;
|
|
}
|
|
|
|
if (inDuel == true)
|
|
{
|
|
pnum++; // DUEL
|
|
}
|
|
}
|
|
|
|
if ((leveltime % (2*5)) / 5) // blink
|
|
pnum += 5;
|
|
if (r_splitscreen) // splitscreen
|
|
pnum += 10;
|
|
|
|
V_DrawScaledPatch(STCD_X - (SHORT(kp_startcountdown[pnum]->width)/2), STCD_Y - (SHORT(kp_startcountdown[pnum]->height)/2), V_SPLITSCREEN, kp_startcountdown[pnum]);
|
|
}
|
|
}
|
|
|
|
static void K_drawKartFirstPerson(void)
|
|
{
|
|
static INT32 pnum[4], turn[4], drift[4];
|
|
const INT16 steerThreshold = KART_FULLTURN / 2;
|
|
INT32 pn = 0, tn = 0, dr = 0;
|
|
INT32 target = 0, splitflags = V_SNAPTOBOTTOM|V_SPLITSCREEN;
|
|
INT32 x = BASEVIDWIDTH/2, y = BASEVIDHEIGHT;
|
|
fixed_t scale;
|
|
UINT8 *colmap = NULL;
|
|
|
|
if (stplyr->spectator || !stplyr->mo || (stplyr->mo->renderflags & RF_DONTDRAW))
|
|
return;
|
|
|
|
{
|
|
UINT8 view = R_GetViewNumber();
|
|
pn = pnum[view];
|
|
tn = turn[view];
|
|
dr = drift[view];
|
|
}
|
|
|
|
if (r_splitscreen)
|
|
{
|
|
y >>= 1;
|
|
if (r_splitscreen > 1)
|
|
x >>= 1;
|
|
}
|
|
|
|
{
|
|
if (stplyr->speed < (20*stplyr->mo->scale) && (leveltime & 1) && !r_splitscreen)
|
|
y++;
|
|
|
|
if (stplyr->mo->renderflags & RF_TRANSMASK)
|
|
splitflags |= ((stplyr->mo->renderflags & RF_TRANSMASK) >> RF_TRANSSHIFT) << FF_TRANSSHIFT;
|
|
else if (stplyr->mo->frame & FF_TRANSMASK)
|
|
splitflags |= (stplyr->mo->frame & FF_TRANSMASK);
|
|
}
|
|
|
|
if (stplyr->steering > steerThreshold) // strong left turn
|
|
target = 2;
|
|
else if (stplyr->steering < -steerThreshold) // strong right turn
|
|
target = -2;
|
|
else if (stplyr->steering > 0) // weak left turn
|
|
target = 1;
|
|
else if (stplyr->steering < 0) // weak right turn
|
|
target = -1;
|
|
else // forward
|
|
target = 0;
|
|
|
|
if (encoremode)
|
|
target = -target;
|
|
|
|
if (pn < target)
|
|
pn++;
|
|
else if (pn > target)
|
|
pn--;
|
|
|
|
if (pn < 0)
|
|
splitflags |= V_FLIP; // right turn
|
|
|
|
target = abs(pn);
|
|
if (target > 2)
|
|
target = 2;
|
|
|
|
x <<= FRACBITS;
|
|
y <<= FRACBITS;
|
|
|
|
if (tn != stplyr->steering/50)
|
|
tn -= (tn - (stplyr->steering/50))/8;
|
|
|
|
if (dr != stplyr->drift*16)
|
|
dr -= (dr - (stplyr->drift*16))/8;
|
|
|
|
if (r_splitscreen == 1)
|
|
{
|
|
scale = (2*FRACUNIT)/3;
|
|
y += FRACUNIT/(vid.dupx < vid.dupy ? vid.dupx : vid.dupy); // correct a one-pixel gap on the screen view (not the basevid view)
|
|
}
|
|
else if (r_splitscreen)
|
|
scale = FRACUNIT/2;
|
|
else
|
|
scale = FRACUNIT;
|
|
|
|
if (stplyr->mo)
|
|
{
|
|
UINT8 driftcolor = K_DriftSparkColor(stplyr, stplyr->driftcharge);
|
|
const angle_t ang = R_PointToAngle2(0, 0, stplyr->rmomx, stplyr->rmomy) - stplyr->drawangle;
|
|
// yes, the following is correct. no, you do not need to swap the x and y.
|
|
fixed_t xoffs = -P_ReturnThrustY(stplyr->mo, ang, (BASEVIDWIDTH<<(FRACBITS-2))/2);
|
|
fixed_t yoffs = -P_ReturnThrustX(stplyr->mo, ang, 4*FRACUNIT);
|
|
|
|
// hitlag vibrating
|
|
if (stplyr->mo->hitlag > 0 && (stplyr->mo->eflags & MFE_DAMAGEHITLAG))
|
|
{
|
|
fixed_t mul = stplyr->mo->hitlag * HITLAGJITTERS;
|
|
if (r_splitscreen && mul > FRACUNIT)
|
|
mul = FRACUNIT;
|
|
|
|
if (leveltime & 1)
|
|
{
|
|
mul = -mul;
|
|
}
|
|
|
|
xoffs = FixedMul(xoffs, mul);
|
|
yoffs = FixedMul(yoffs, mul);
|
|
|
|
}
|
|
|
|
if ((yoffs += 4*FRACUNIT) < 0)
|
|
yoffs = 0;
|
|
|
|
if (r_splitscreen)
|
|
xoffs = FixedMul(xoffs, scale);
|
|
|
|
xoffs -= (tn)*scale;
|
|
xoffs -= (dr)*scale;
|
|
|
|
if (stplyr->drawangle == stplyr->mo->angle)
|
|
{
|
|
const fixed_t mag = FixedDiv(stplyr->speed, 10*stplyr->mo->scale);
|
|
|
|
if (mag < FRACUNIT)
|
|
{
|
|
xoffs = FixedMul(xoffs, mag);
|
|
if (!r_splitscreen)
|
|
yoffs = FixedMul(yoffs, mag);
|
|
}
|
|
}
|
|
|
|
if (stplyr->mo->momz > 0) // TO-DO: Draw more of the kart so we can remove this if!
|
|
yoffs += stplyr->mo->momz/3;
|
|
|
|
if (encoremode)
|
|
x -= xoffs;
|
|
else
|
|
x += xoffs;
|
|
if (!r_splitscreen)
|
|
y += yoffs;
|
|
|
|
|
|
if ((leveltime & 1) && (driftcolor != SKINCOLOR_NONE)) // drift sparks!
|
|
colmap = R_GetTranslationColormap(TC_RAINBOW, static_cast<skincolornum_t>(driftcolor), GTC_CACHE);
|
|
else if (stplyr->mo->colorized && stplyr->mo->color) // invincibility/grow/shrink!
|
|
colmap = R_GetTranslationColormap(TC_RAINBOW, static_cast<skincolornum_t>(stplyr->mo->color), GTC_CACHE);
|
|
}
|
|
|
|
V_DrawFixedPatch(x, y, scale, splitflags, kp_fpview[target], colmap);
|
|
|
|
{
|
|
UINT8 view = R_GetViewNumber();
|
|
pnum[view] = pn;
|
|
turn[view] = tn;
|
|
drift[view] = dr;
|
|
}
|
|
}
|
|
|
|
static void K_drawInput(void)
|
|
{
|
|
UINT8 viewnum = R_GetViewNumber();
|
|
boolean freecam = camera[viewnum].freecam; //disable some hud elements w/ freecam
|
|
|
|
if (!cv_drawinput.value && !modeattacking && gametype != GT_TUTORIAL)
|
|
return;
|
|
|
|
if (stplyr->spectator || freecam || demo.attract)
|
|
return;
|
|
|
|
INT32 def[4][3] = {
|
|
{247, 156, V_SNAPTOBOTTOM | V_SNAPTORIGHT}, // 1p
|
|
{247, 56, V_SNAPTOBOTTOM | V_SNAPTORIGHT}, // 2p
|
|
{6, 52, V_SNAPTOBOTTOM | V_SNAPTOLEFT}, // 4p left
|
|
{282 - BASEVIDWIDTH/2, 52, V_SNAPTOBOTTOM | V_SNAPTORIGHT}, // 4p right
|
|
};
|
|
INT32 k = r_splitscreen <= 1 ? r_splitscreen : 2 + (viewnum & 1);
|
|
INT32 flags = def[k][2] | V_SPLITSCREEN;
|
|
char mode = ((stplyr->pflags & PF_ANALOGSTICK) ? '4' : '2') + (r_splitscreen > 1);
|
|
bool local = !demo.playback && P_IsMachineLocalPlayer(stplyr);
|
|
fixed_t slide = K_GetDialogueSlide(FRACUNIT);
|
|
INT32 tallySlide = []
|
|
{
|
|
if (r_splitscreen <= 1)
|
|
{
|
|
return 0;
|
|
}
|
|
if (!stplyr->tally.active)
|
|
{
|
|
return 0;
|
|
}
|
|
constexpr INT32 kSlideDown = 22;
|
|
if (stplyr->tally.state == TALLY_ST_GOTTHRU_SLIDEIN ||
|
|
stplyr->tally.state == TALLY_ST_GAMEOVER_SLIDEIN)
|
|
{
|
|
return Easing_OutQuad(std::min<fixed_t>(stplyr->tally.transition * 2, FRACUNIT), 0, kSlideDown);
|
|
}
|
|
return kSlideDown;
|
|
}();
|
|
if (slide)
|
|
flags &= ~(V_SNAPTORIGHT); // don't draw underneath the dialogue box in non-green resolutions
|
|
|
|
// Move above the boss health bar.
|
|
// TODO: boss HUD only works in 1P, so this only works in 1P too.
|
|
if (LUA_HudEnabled(hud_position) && bossinfo.valid)
|
|
{
|
|
constexpr tic_t kDelay = 2u;
|
|
// See K_drawBossHealthBar
|
|
tic_t start = lt_endtime - 1u;
|
|
tic_t t = std::clamp(lt_ticker, start, start + kDelay) - start;
|
|
def[0][1] -= 24 + Easing_Linear(t * FRACUNIT / kDelay, 0, 7);
|
|
}
|
|
|
|
K_DrawInputDisplay(
|
|
def[k][0] - FixedToFloat(34 * slide),
|
|
def[k][1] - FixedToFloat(51 * slide) + tallySlide,
|
|
flags,
|
|
mode,
|
|
(local ? G_LocalSplitscreenPartyPosition : G_PartyPosition)(stplyr - players),
|
|
local,
|
|
stplyr->speed > 0
|
|
);
|
|
}
|
|
|
|
static void K_drawChallengerScreen(void)
|
|
{
|
|
// This is an insanely complicated animation.
|
|
static UINT8 anim[52] = {
|
|
0,0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10,11,11,12,12,13,13, // frame 1-14, 2 tics: HERE COMES A NEW slides in
|
|
14,14,14,14,14,14, // frame 15, 6 tics: pause on the W
|
|
15,16,17,18, // frame 16-19, 1 tic: CHALLENGER approaches screen
|
|
19,20,19,20,19,20,19,20,19,20, // frame 20-21, 1 tic, 5 alternating: all text vibrates from impact
|
|
21,22,23,24 // frame 22-25, 1 tic: CHALLENGER turns gold
|
|
};
|
|
const UINT8 offset = std::min(52-1u, (3*TICRATE)-mapreset);
|
|
|
|
V_DrawFadeScreen(0xFF00, 16); // Fade out
|
|
V_DrawScaledPatch(0, 0, 0, kp_challenger[anim[offset]]);
|
|
}
|
|
|
|
static void K_drawLapStartAnim(void)
|
|
{
|
|
if (demo.attract == DEMO_ATTRACT_CREDITS)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// This is an EVEN MORE insanely complicated animation.
|
|
const UINT8 t = stplyr->karthud[khud_lapanimation];
|
|
const UINT8 progress = 80 - t;
|
|
|
|
const UINT8 tOld = t + 1;
|
|
const UINT8 progressOld = 80 - tOld;
|
|
|
|
const tic_t leveltimeOld = leveltime - 1;
|
|
|
|
UINT8 *colormap = R_GetTranslationColormap(TC_DEFAULT, static_cast<skincolornum_t>(stplyr->skincolor), GTC_CACHE);
|
|
|
|
fixed_t interpx, interpy, newval, oldval;
|
|
|
|
newval = (BASEVIDWIDTH/2 + (32 * std::max(0, t - 76))) * FRACUNIT;
|
|
oldval = (BASEVIDWIDTH/2 + (32 * std::max(0, tOld - 76))) * FRACUNIT;
|
|
interpx = R_InterpolateFixed(oldval, newval);
|
|
|
|
newval = (48 - (32 * std::max(0, progress - 76))) * FRACUNIT;
|
|
oldval = (48 - (32 * std::max(0, progressOld - 76))) * FRACUNIT;
|
|
interpy = R_InterpolateFixed(oldval, newval);
|
|
|
|
V_DrawFixedPatch(
|
|
interpx, interpy,
|
|
FRACUNIT, V_SNAPTOTOP|V_HUDTRANS,
|
|
(modeattacking ? kp_lapanim_emblem[1] : kp_lapanim_emblem[0]), colormap);
|
|
|
|
if (stplyr->karthud[khud_laphand] >= 1 && stplyr->karthud[khud_laphand] <= 3)
|
|
{
|
|
newval = (4 - abs((signed)((leveltime % 8) - 4))) * FRACUNIT;
|
|
oldval = (4 - abs((signed)((leveltimeOld % 8) - 4))) * FRACUNIT;
|
|
interpy += R_InterpolateFixed(oldval, newval);
|
|
|
|
V_DrawFixedPatch(
|
|
interpx, interpy,
|
|
FRACUNIT, V_SNAPTOTOP|V_HUDTRANS,
|
|
kp_lapanim_hand[stplyr->karthud[khud_laphand]-1], NULL);
|
|
}
|
|
|
|
if (stplyr->latestlap == (UINT8)(numlaps))
|
|
{
|
|
newval = (62 - (32 * std::max(0, progress - 76))) * FRACUNIT;
|
|
oldval = (62 - (32 * std::max(0, progressOld - 76))) * FRACUNIT;
|
|
interpx = R_InterpolateFixed(oldval, newval);
|
|
|
|
V_DrawFixedPatch(
|
|
interpx, // 27
|
|
30*FRACUNIT, // 24
|
|
FRACUNIT, V_SNAPTOTOP|V_HUDTRANS,
|
|
kp_lapanim_final[std::min(progress/2, 10)], NULL);
|
|
|
|
if (progress/2-12 >= 0)
|
|
{
|
|
newval = (188 + (32 * std::max(0, progress - 76))) * FRACUNIT;
|
|
oldval = (188 + (32 * std::max(0, progressOld - 76))) * FRACUNIT;
|
|
interpx = R_InterpolateFixed(oldval, newval);
|
|
|
|
V_DrawFixedPatch(
|
|
interpx, // 194
|
|
30*FRACUNIT, // 24
|
|
FRACUNIT, V_SNAPTOTOP|V_HUDTRANS,
|
|
kp_lapanim_lap[std::min(progress/2-12, 6)], NULL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
newval = (82 - (32 * std::max(0, progress - 76))) * FRACUNIT;
|
|
oldval = (82 - (32 * std::max(0, progressOld - 76))) * FRACUNIT;
|
|
interpx = R_InterpolateFixed(oldval, newval);
|
|
|
|
V_DrawFixedPatch(
|
|
interpx, // 61
|
|
30*FRACUNIT, // 24
|
|
FRACUNIT, V_SNAPTOTOP|V_HUDTRANS,
|
|
kp_lapanim_lap[std::min(progress/2, 6)], NULL);
|
|
|
|
if (progress/2-8 >= 0)
|
|
{
|
|
newval = (188 + (32 * std::max(0, progress - 76))) * FRACUNIT;
|
|
oldval = (188 + (32 * std::max(0, progressOld - 76))) * FRACUNIT;
|
|
interpx = R_InterpolateFixed(oldval, newval);
|
|
|
|
V_DrawFixedPatch(
|
|
interpx, // 194
|
|
30*FRACUNIT, // 24
|
|
FRACUNIT, V_SNAPTOTOP|V_HUDTRANS,
|
|
kp_lapanim_number[(((UINT32)stplyr->latestlap) / 10)][std::min(progress/2-8, 2)], NULL);
|
|
|
|
if (progress/2-10 >= 0)
|
|
{
|
|
newval = (208 + (32 * std::max(0, progress - 76))) * FRACUNIT;
|
|
oldval = (208 + (32 * std::max(0, progressOld - 76))) * FRACUNIT;
|
|
interpx = R_InterpolateFixed(oldval, newval);
|
|
|
|
V_DrawFixedPatch(
|
|
interpx, // 221
|
|
30*FRACUNIT, // 24
|
|
FRACUNIT, V_SNAPTOTOP|V_HUDTRANS,
|
|
kp_lapanim_number[(((UINT32)stplyr->latestlap) % 10)][std::min(progress/2-10, 2)], NULL);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// stretch for "COOOOOL" popup.
|
|
// I can't be fucked to find out any math behind this so have a table lmao
|
|
static fixed_t stretch[6][2] = {
|
|
{FRACUNIT/4, FRACUNIT*4},
|
|
{FRACUNIT/2, FRACUNIT*2},
|
|
{FRACUNIT, FRACUNIT},
|
|
{FRACUNIT*4, FRACUNIT/2},
|
|
{FRACUNIT*8, FRACUNIT/4},
|
|
{FRACUNIT*4, FRACUNIT/2},
|
|
};
|
|
|
|
static void K_drawTrickCool(void)
|
|
{
|
|
|
|
tic_t timer = TICRATE - stplyr->karthud[khud_trickcool];
|
|
|
|
if (timer <= 6)
|
|
{
|
|
V_DrawStretchyFixedPatch(TCOOL_X<<FRACBITS, TCOOL_Y<<FRACBITS, stretch[timer-1][0], stretch[timer-1][1], V_HUDTRANS|V_SPLITSCREEN, kp_trickcool[splitscreen ? 1 : 0], NULL);
|
|
}
|
|
else if (leveltime & 1)
|
|
{
|
|
V_DrawFixedPatch(TCOOL_X<<FRACBITS, (TCOOL_Y<<FRACBITS) - (timer-10)*FRACUNIT/2, FRACUNIT, V_HUDTRANS|V_SPLITSCREEN, kp_trickcool[splitscreen ? 1 : 0], NULL);
|
|
}
|
|
}
|
|
|
|
void K_drawKartFreePlay(void)
|
|
{
|
|
if (!LUA_HudEnabled(hud_freeplay))
|
|
return;
|
|
|
|
if (stplyr->spectator == true)
|
|
return;
|
|
|
|
if (M_NotFreePlay() == true)
|
|
return;
|
|
|
|
if (lt_exitticker < TICRATE/2)
|
|
return;
|
|
|
|
if (((leveltime-lt_endtime) % TICRATE) < TICRATE/2)
|
|
return;
|
|
|
|
INT32 h_snap = r_splitscreen < 2 ? V_SNAPTORIGHT | V_SLIDEIN : V_HUDTRANS;
|
|
fixed_t x = ((r_splitscreen > 1 ? BASEVIDWIDTH/4 : BASEVIDWIDTH - (LAPS_X+6)) * FRACUNIT);
|
|
fixed_t y = ((r_splitscreen ? BASEVIDHEIGHT/2 : BASEVIDHEIGHT) - 20) * FRACUNIT;
|
|
|
|
x -= V_StringScaledWidth(
|
|
FRACUNIT,
|
|
FRACUNIT,
|
|
FRACUNIT,
|
|
V_SNAPTOBOTTOM|h_snap|V_SPLITSCREEN,
|
|
KART_FONT,
|
|
"FREE PLAY"
|
|
) / (r_splitscreen > 1 ? 2 : 1);
|
|
|
|
V_DrawStringScaled(
|
|
x,
|
|
y,
|
|
FRACUNIT,
|
|
FRACUNIT,
|
|
FRACUNIT,
|
|
V_SNAPTOBOTTOM|h_snap|V_SPLITSCREEN,
|
|
NULL,
|
|
KART_FONT,
|
|
"FREE PLAY"
|
|
);
|
|
}
|
|
|
|
static void
|
|
Draw_party_ping (int ss, INT32 snap)
|
|
{
|
|
HU_drawMiniPing(0, 0, playerpingtable[displayplayers[ss]], V_SPLITSCREEN|V_SNAPTOTOP|snap);
|
|
}
|
|
|
|
static void
|
|
K_drawMiniPing (void)
|
|
{
|
|
UINT32 f = V_SNAPTORIGHT;
|
|
UINT8 i = R_GetViewNumber();
|
|
|
|
if (r_splitscreen > 1 && !(i & 1))
|
|
{
|
|
f = V_SNAPTOLEFT;
|
|
}
|
|
|
|
Draw_party_ping(i, f);
|
|
}
|
|
|
|
void K_drawButton(fixed_t x, fixed_t y, INT32 flags, patch_t *button[2], boolean pressed)
|
|
{
|
|
V_DrawFixedPatch(x, y, FRACUNIT, flags, button[(pressed == true) ? 1 : 0], NULL);
|
|
}
|
|
|
|
void K_drawButtonAnim(INT32 x, INT32 y, INT32 flags, patch_t *button[2], tic_t animtic)
|
|
{
|
|
const UINT8 anim_duration = 16;
|
|
const boolean anim = ((animtic % (anim_duration * 2)) < anim_duration);
|
|
K_drawButton(x << FRACBITS, y << FRACBITS, flags, button, anim);
|
|
}
|
|
|
|
static void K_drawDistributionDebugger(void)
|
|
{
|
|
itemroulette_t rouletteData = {0};
|
|
|
|
const fixed_t scale = (FRACUNIT >> 1);
|
|
const fixed_t space = 24 * scale;
|
|
const fixed_t pad = 9 * scale;
|
|
|
|
fixed_t x = -pad;
|
|
fixed_t y = -pad;
|
|
size_t i;
|
|
|
|
if (R_GetViewNumber() != 0) // only for p1
|
|
{
|
|
return;
|
|
}
|
|
|
|
K_FillItemRouletteData(stplyr, &rouletteData, false);
|
|
|
|
for (i = 0; i < rouletteData.itemListLen; i++)
|
|
{
|
|
const kartitems_t item = static_cast<kartitems_t>(rouletteData.itemList[i]);
|
|
UINT8 amount = 1;
|
|
|
|
if (y > (BASEVIDHEIGHT << FRACBITS) - space - pad)
|
|
{
|
|
x += space;
|
|
y = -pad;
|
|
}
|
|
|
|
V_DrawFixedPatch(x, y, scale, V_SNAPTOTOP,
|
|
K_GetSmallStaticCachedItemPatch(item), NULL);
|
|
|
|
// Display amount for multi-items
|
|
amount = K_ItemResultToAmount(item);
|
|
if (amount > 1)
|
|
{
|
|
V_DrawStringScaled(
|
|
x + (18 * scale),
|
|
y + (23 * scale),
|
|
scale, FRACUNIT, FRACUNIT,
|
|
V_SNAPTOTOP,
|
|
NULL, HU_FONT,
|
|
va("x%d", amount)
|
|
);
|
|
}
|
|
|
|
y += space;
|
|
}
|
|
|
|
V_DrawString((x >> FRACBITS) + 20, 2, V_SNAPTOTOP, va("useOdds[%u]", rouletteData.useOdds));
|
|
V_DrawString((x >> FRACBITS) + 20, 10, V_SNAPTOTOP, va("speed = %u", rouletteData.speed));
|
|
|
|
V_DrawString((x >> FRACBITS) + 20, 22, V_SNAPTOTOP, va("baseDist = %u", rouletteData.baseDist));
|
|
V_DrawString((x >> FRACBITS) + 20, 30, V_SNAPTOTOP, va("dist = %u", rouletteData.dist));
|
|
|
|
V_DrawString((x >> FRACBITS) + 20, 42, V_SNAPTOTOP, va("firstDist = %u", rouletteData.firstDist));
|
|
V_DrawString((x >> FRACBITS) + 20, 50, V_SNAPTOTOP, va("secondDist = %u", rouletteData.secondDist));
|
|
V_DrawString((x >> FRACBITS) + 20, 58, V_SNAPTOTOP, va("secondToFirst = %u", rouletteData.secondToFirst));
|
|
|
|
#ifndef ITEM_LIST_SIZE
|
|
Z_Free(rouletteData.itemList);
|
|
#endif
|
|
}
|
|
|
|
static void K_DrawWaypointDebugger(void)
|
|
{
|
|
if (cv_kartdebugwaypoints.value == 0)
|
|
return;
|
|
|
|
if (R_GetViewNumber() != 0) // only for p1
|
|
return;
|
|
|
|
constexpr int kH = 8;
|
|
using srb2::Draw;
|
|
Draw::TextElement label;
|
|
label.font(Draw::Font::kThin);
|
|
label.flags(V_AQUAMAP);
|
|
Draw line = Draw(8, 110).font(Draw::Font::kMenu);
|
|
auto put = [&](const char* label_str, auto&&... args)
|
|
{
|
|
constexpr int kTabWidth = 48;
|
|
label.string(label_str);
|
|
int x = label.width() + kTabWidth;
|
|
x -= x % kTabWidth;
|
|
line.size(x + 4, 2).y(7).fill(31);
|
|
line.text(label);
|
|
line.x(x).text(args...);
|
|
line = line.y(kH);
|
|
};
|
|
|
|
if (netgame)
|
|
{
|
|
line = line.y(-kH);
|
|
put("Online griefing:", "[{}, {}]", stplyr->griefValue/TICRATE, stplyr->griefStrikes);
|
|
}
|
|
|
|
put("Current Waypoint ID:", "{}", K_GetWaypointID(stplyr->currentwaypoint));
|
|
put("Next Waypoint ID:", "{}{}", K_GetWaypointID(stplyr->nextwaypoint), ((stplyr->pflags & PF_WRONGWAY) ? " (WRONG WAY)" : ""));
|
|
put("Respawn Waypoint ID:", "{}", K_GetWaypointID(stplyr->respawn.wp));
|
|
put("Finishline Distance:", "{}", stplyr->distancetofinish);
|
|
put("Last Safe Lap:", "{}", stplyr->lastsafelap);
|
|
|
|
if (numcheatchecks > 0)
|
|
{
|
|
if (stplyr->cheatchecknum == numcheatchecks)
|
|
put("Cheat Check:", "{} / {} (Can finish)", stplyr->cheatchecknum, numcheatchecks);
|
|
else
|
|
put("Cheat Check:", "{} / {}", stplyr->cheatchecknum, numcheatchecks);
|
|
put("Last Safe Cheat Check:", "{}", stplyr->lastsafecheatcheck);
|
|
}
|
|
|
|
if (stplyr->bigwaypointgap)
|
|
{
|
|
put("Auto Respawn Timer:", "{}", stplyr->bigwaypointgap);
|
|
}
|
|
}
|
|
|
|
static void K_DrawBotDebugger(void)
|
|
{
|
|
player_t *bot = NULL;
|
|
|
|
if (cv_kartdebugbots.value == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (R_GetViewNumber() != 0) // only for p1
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (stplyr->bot == true)
|
|
{
|
|
// we ARE the bot
|
|
bot = stplyr;
|
|
}
|
|
else
|
|
{
|
|
// get winning bot
|
|
size_t i;
|
|
for (i = 0; i < MAXPLAYERS; i++)
|
|
{
|
|
player_t *p = NULL;
|
|
|
|
if (playeringame[i] == false)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
p = &players[i];
|
|
if (p->spectator == true || p->bot == false)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (bot == NULL || p->distancetofinish < bot->distancetofinish)
|
|
{
|
|
bot = p;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bot == NULL)
|
|
{
|
|
// no bot exists?
|
|
return;
|
|
}
|
|
|
|
V_DrawSmallString(16, 8, V_YELLOWMAP, va("Bot: %s", player_names[bot - players]));
|
|
|
|
V_DrawSmallString(8, 14, 0, va("Difficulty: %d / %d", bot->botvars.difficulty, MAXBOTDIFFICULTY));
|
|
V_DrawSmallString(8, 18, 0, va("Difficulty increase: %d", bot->botvars.diffincrease));
|
|
V_DrawSmallString(8, 22, 0, va("Rival: %d", (UINT8)(bot->botvars.rival == true)));
|
|
V_DrawSmallString(8, 26, 0, va("Rubberbanding: %.02f", FIXED_TO_FLOAT(bot->botvars.rubberband) * 100.0f));
|
|
|
|
V_DrawSmallString(8, 32, 0, va("Item delay: %d", bot->botvars.itemdelay));
|
|
V_DrawSmallString(8, 36, 0, va("Item confirm: %d", bot->botvars.itemconfirm));
|
|
|
|
V_DrawSmallString(8, 42, 0, va("Turn: %d / %d / %d", -BOTTURNCONFIRM, bot->botvars.turnconfirm, BOTTURNCONFIRM));
|
|
V_DrawSmallString(8, 46, 0, va("Spindash: %d / %d", bot->botvars.spindashconfirm, BOTSPINDASHCONFIRM));
|
|
V_DrawSmallString(8, 50, 0, va("Respawn: %d / %d", bot->botvars.respawnconfirm, BOTRESPAWNCONFIRM));
|
|
|
|
V_DrawSmallString(8, 56, 0, va("Item priority: %d", bot->botvars.roulettePriority));
|
|
V_DrawSmallString(8, 60, 0, va("Item timeout: %d", bot->botvars.rouletteTimeout));
|
|
|
|
V_DrawSmallString(8, 66, 0, va("Complexity: %d", K_GetTrackComplexity()));
|
|
V_DrawSmallString(8, 70, 0, va("Bot modifier: %.2f", FixedToFloat(K_BotMapModifier())));
|
|
}
|
|
|
|
static void K_DrawGPRankDebugger(void)
|
|
{
|
|
gp_rank_e grade = GRADE_E;
|
|
char gradeChar = '?';
|
|
|
|
if (cv_debugrank.value == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (R_GetViewNumber() != 0) // only for p1
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (grandprixinfo.gp == false)
|
|
{
|
|
return;
|
|
}
|
|
|
|
grade = K_CalculateGPGrade(&grandprixinfo.rank);
|
|
|
|
V_DrawThinString(0, 0, V_SNAPTOTOP|V_SNAPTOLEFT,
|
|
va("POS: %d / %d", grandprixinfo.rank.position, RANK_NEUTRAL_POSITION));
|
|
V_DrawThinString(0, 10, V_SNAPTOTOP|V_SNAPTOLEFT,
|
|
va("PTS: %d / %d", grandprixinfo.rank.winPoints, grandprixinfo.rank.totalPoints));
|
|
V_DrawThinString(0, 20, V_SNAPTOTOP|V_SNAPTOLEFT,
|
|
va("LAPS: %d / %d", grandprixinfo.rank.laps, grandprixinfo.rank.totalLaps));
|
|
V_DrawThinString(0, 30, V_SNAPTOTOP|V_SNAPTOLEFT,
|
|
va("CONTINUES: %d", grandprixinfo.rank.continuesUsed));
|
|
V_DrawThinString(0, 40, V_SNAPTOTOP|V_SNAPTOLEFT,
|
|
va("PRISONS: %d / %d", grandprixinfo.rank.prisons, grandprixinfo.rank.totalPrisons));
|
|
V_DrawThinString(0, 50, V_SNAPTOTOP|V_SNAPTOLEFT,
|
|
va("RINGS: %d / %d", grandprixinfo.rank.rings, grandprixinfo.rank.totalRings));
|
|
V_DrawThinString(0, 60, V_SNAPTOTOP|V_SNAPTOLEFT,
|
|
va("EMERALD: %s", (grandprixinfo.rank.specialWon == true) ? "YES" : "NO"));
|
|
|
|
switch (grade)
|
|
{
|
|
case GRADE_E: { gradeChar = 'E'; break; }
|
|
case GRADE_D: { gradeChar = 'D'; break; }
|
|
case GRADE_C: { gradeChar = 'C'; break; }
|
|
case GRADE_B: { gradeChar = 'B'; break; }
|
|
case GRADE_A: { gradeChar = 'A'; break; }
|
|
case GRADE_S: { gradeChar = 'S'; break; }
|
|
default: { break; }
|
|
}
|
|
|
|
V_DrawThinString(0, 90, V_SNAPTOTOP|V_SNAPTOLEFT|V_YELLOWMAP,
|
|
va(" ** FINAL GRADE: %c", gradeChar));
|
|
}
|
|
|
|
typedef enum
|
|
{
|
|
MM_IN,
|
|
MM_HOLD,
|
|
MM_OUT,
|
|
} messagemode_t;
|
|
|
|
typedef struct
|
|
{
|
|
std::string text;
|
|
sfxenum_t sound;
|
|
} message_t;
|
|
|
|
struct messagestate_t
|
|
{
|
|
std::deque<std::string> messages;
|
|
std::string objective = "";
|
|
tic_t timer = 0;
|
|
boolean persist = false;
|
|
messagemode_t mode = MM_IN;
|
|
const tic_t speedyswitch = 2*TICRATE;
|
|
const tic_t lazyswitch = 4*TICRATE;
|
|
|
|
void add(std::string msg)
|
|
{
|
|
messages.push_back(msg);
|
|
}
|
|
|
|
void clear()
|
|
{
|
|
messages.clear();
|
|
switch_mode(MM_IN);
|
|
}
|
|
|
|
void switch_mode(messagemode_t nextmode)
|
|
{
|
|
mode = nextmode;
|
|
timer = 0;
|
|
}
|
|
|
|
void tick()
|
|
{
|
|
if (messages.size() == 0)
|
|
{
|
|
if (!objective.empty())
|
|
restore();
|
|
else
|
|
return;
|
|
}
|
|
|
|
if (exitcountdown)
|
|
return;
|
|
|
|
if (timer == 0 && mode == MM_IN)
|
|
S_StartSound(NULL, sfx_s3k47);
|
|
|
|
timer++;
|
|
|
|
switch (mode)
|
|
{
|
|
case MM_IN:
|
|
if (timer > messages[0].length())
|
|
switch_mode(MM_HOLD);
|
|
break;
|
|
case MM_HOLD:
|
|
if (messages.size() > 1 && timer > speedyswitch) // Waiting message, switch to it right away!
|
|
next();
|
|
else if (timer > lazyswitch && !persist) // If there's no pending message, we can chill for a bit.
|
|
switch_mode(MM_OUT);
|
|
break;
|
|
case MM_OUT:
|
|
if (timer > messages[0].length())
|
|
next();
|
|
break;
|
|
}
|
|
}
|
|
|
|
void restore()
|
|
{
|
|
switch_mode(MM_IN);
|
|
persist = true;
|
|
messages.clear();
|
|
messages.push_front(objective);
|
|
}
|
|
|
|
void next()
|
|
{
|
|
switch_mode(MM_IN);
|
|
persist = false;
|
|
if (messages.size() > 0)
|
|
messages.pop_front();
|
|
}
|
|
|
|
};
|
|
|
|
static std::vector<messagestate_t> messagestates{MAXSPLITSCREENPLAYERS};
|
|
|
|
void K_AddMessage(const char *msg, boolean interrupt, boolean persist)
|
|
{
|
|
for (auto &state : messagestates)
|
|
{
|
|
if (interrupt)
|
|
state.clear();
|
|
|
|
std::string parsedmsg = srb2::Draw::TextElement().parse(msg).string();
|
|
|
|
if (persist)
|
|
state.objective = parsedmsg;
|
|
else
|
|
state.add(parsedmsg);
|
|
}
|
|
}
|
|
|
|
void K_ClearPersistentMessages()
|
|
{
|
|
for (auto &state : messagestates)
|
|
{
|
|
state.objective = "";
|
|
state.clear();
|
|
}
|
|
}
|
|
|
|
// Return value can be used for "paired" splitscreen messages, true = was displayed
|
|
void K_AddMessageForPlayer(player_t *player, const char *msg, boolean interrupt, boolean persist)
|
|
{
|
|
if (!player)
|
|
return;
|
|
|
|
if (player && !P_IsDisplayPlayer(player))
|
|
return;
|
|
|
|
messagestate_t *state = &messagestates[G_PartyPosition(player - players)];
|
|
|
|
if (interrupt)
|
|
state->clear();
|
|
|
|
std::string parsedmsg = srb2::Draw::TextElement().parse(msg).string();
|
|
|
|
if (persist)
|
|
state->objective = parsedmsg;
|
|
else
|
|
state->add(parsedmsg);
|
|
}
|
|
|
|
void K_ClearPersistentMessageForPlayer(player_t *player)
|
|
{
|
|
if (!player)
|
|
return;
|
|
|
|
if (player && !P_IsDisplayPlayer(player))
|
|
return;
|
|
|
|
messagestate_t *state = &messagestates[G_PartyPosition(player - players)];
|
|
state->objective = "";
|
|
}
|
|
|
|
void K_TickMessages()
|
|
{
|
|
for (auto &state : messagestates)
|
|
{
|
|
state.tick();
|
|
}
|
|
}
|
|
|
|
static void K_DrawMessageFeed(void)
|
|
{
|
|
int i;
|
|
|
|
if (exitcountdown)
|
|
return;
|
|
|
|
for (i = 0; i <= r_splitscreen; i++)
|
|
{
|
|
messagestate_t state = messagestates[i];
|
|
|
|
if (state.messages.size() == 0)
|
|
continue;
|
|
|
|
std::string msg = state.messages[0];
|
|
|
|
UINT8 sublen = state.timer;
|
|
if (state.mode == MM_IN)
|
|
sublen = state.timer;
|
|
else if (state.mode == MM_HOLD)
|
|
sublen = msg.length();
|
|
else if (state.mode == MM_OUT)
|
|
sublen = msg.length() - state.timer;
|
|
|
|
std::string submsg = msg.substr(0, sublen);
|
|
|
|
using srb2::Draw;
|
|
|
|
Draw::TextElement text(submsg);
|
|
|
|
text.font(Draw::Font::kMenu);
|
|
|
|
UINT8 x = 160;
|
|
UINT8 y = 10;
|
|
SINT8 shift = 0;
|
|
if (r_splitscreen >= 2)
|
|
{
|
|
text.font(Draw::Font::kThin);
|
|
shift = -2;
|
|
|
|
x = BASEVIDWIDTH/4;
|
|
y = 5;
|
|
|
|
if (i % 2)
|
|
x += BASEVIDWIDTH/2;
|
|
|
|
if (i >= 2)
|
|
y += BASEVIDHEIGHT / 2;
|
|
}
|
|
else if (r_splitscreen >= 1)
|
|
{
|
|
y = 5;
|
|
|
|
if (i >= 1)
|
|
y += BASEVIDHEIGHT / 2;
|
|
}
|
|
UINT16 sw = text.width();
|
|
|
|
K_DrawSticker(x - sw/2, y, sw, 0, true);
|
|
Draw(x, y+shift).align(Draw::Align::kCenter).text(text);
|
|
}
|
|
}
|
|
|
|
void K_drawKartHUD(void)
|
|
{
|
|
boolean islonesome = false;
|
|
UINT8 viewnum = R_GetViewNumber();
|
|
boolean freecam = camera[viewnum].freecam; //disable some hud elements w/ freecam
|
|
|
|
// Define the X and Y for each drawn object
|
|
// This is handled by console/menu values
|
|
K_initKartHUD();
|
|
|
|
// Draw that fun first person HUD! Drawn ASAP so it looks more "real".
|
|
if (!camera[viewnum].chase && !freecam)
|
|
K_drawKartFirstPerson();
|
|
|
|
if (mapreset)
|
|
{
|
|
// HERE COMES A NEW CHALLENGER
|
|
if (R_GetViewNumber() == 0)
|
|
K_drawChallengerScreen();
|
|
return;
|
|
}
|
|
|
|
// Draw full screen stuff that turns off the rest of the HUD
|
|
if (R_GetViewNumber() == 0)
|
|
{
|
|
if (g_emeraldWin)
|
|
K_drawEmeraldWin(false);
|
|
}
|
|
|
|
if (!demo.attract)
|
|
{
|
|
// Draw the CHECK indicator before the other items, so it's overlapped by everything else
|
|
if (LUA_HudEnabled(hud_check)) // delete lua when?
|
|
if (!splitscreen && !players[displayplayers[0]].exiting && !freecam)
|
|
K_drawKartPlayerCheck();
|
|
|
|
// nametags
|
|
if (LUA_HudEnabled(hud_names) && cv_drawpickups.value)
|
|
K_drawKartNameTags();
|
|
|
|
// Draw WANTED status
|
|
#if 0
|
|
if (gametype == GT_BATTLE)
|
|
{
|
|
if (LUA_HudEnabled(hud_wanted))
|
|
K_drawKartWanted();
|
|
}
|
|
#endif
|
|
|
|
if (LUA_HudEnabled(hud_minimap))
|
|
K_drawKartMinimap();
|
|
}
|
|
|
|
if (demo.attract)
|
|
;
|
|
else if (gametype == GT_TUTORIAL)
|
|
{
|
|
islonesome = true;
|
|
}
|
|
else if (!r_splitscreen)
|
|
{
|
|
// Draw the timestamp
|
|
if (LUA_HudEnabled(hud_time))
|
|
{
|
|
bool ta = modeattacking && !demo.playback;
|
|
INT32 flags = V_HUDTRANS|V_SLIDEIN|V_SNAPTOTOP|V_SNAPTORIGHT;
|
|
|
|
tic_t realtime = stplyr->realtime;
|
|
|
|
if (stplyr->karthud[khud_lapanimation]
|
|
&& !stplyr->exiting
|
|
&& stplyr->laptime[LAP_LAST] != 0
|
|
&& stplyr->laptime[LAP_LAST] != UINT32_MAX)
|
|
{
|
|
if ((stplyr->karthud[khud_lapanimation] / 5) & 1)
|
|
{
|
|
realtime = stplyr->laptime[LAP_LAST];
|
|
}
|
|
else
|
|
{
|
|
realtime = UINT32_MAX;
|
|
}
|
|
}
|
|
|
|
K_drawKartTimestamp(realtime, TIME_X, TIME_Y + (ta ? 2 : 0), flags, 0);
|
|
|
|
if (modeattacking)
|
|
{
|
|
if (ta)
|
|
{
|
|
using srb2::Draw;
|
|
Draw(BASEVIDWIDTH - 19, 2)
|
|
.flags(flags | V_YELLOWMAP)
|
|
.align(Draw::Align::kRight)
|
|
.text("\xBE Restart");
|
|
}
|
|
else
|
|
{
|
|
using srb2::Draw;
|
|
Draw row = Draw(BASEVIDWIDTH - 20, TIME_Y + 18).flags(flags).align(Draw::Align::kRight);
|
|
auto insert = [&](const char *label, UINT32 tics)
|
|
{
|
|
Draw::TextElement text =
|
|
tics != UINT32_MAX ?
|
|
Draw::TextElement(
|
|
"{:02}'{:02}\"{:02}",
|
|
G_TicsToMinutes(tics, true),
|
|
G_TicsToSeconds(tics),
|
|
G_TicsToCentiseconds(tics)
|
|
) :
|
|
Draw::TextElement("--'--\"--");
|
|
text.font(Draw::Font::kZVote);
|
|
row.x(-text.width()).flags(V_ORANGEMAP).text(label);
|
|
row.y(1).text(text);
|
|
row = row.y(10);
|
|
};
|
|
if (modeattacking & ATTACKING_TIME)
|
|
insert("Finish: ", hu_demotime);
|
|
if (modeattacking & ATTACKING_LAP)
|
|
insert("Best Lap: ", hu_demolap);
|
|
}
|
|
}
|
|
}
|
|
|
|
islonesome = K_drawKartPositionFaces();
|
|
}
|
|
else
|
|
{
|
|
islonesome = M_NotFreePlay() == false;
|
|
|
|
if (r_splitscreen == 1)
|
|
{
|
|
if (LUA_HudEnabled(hud_time))
|
|
{
|
|
K_drawKart2PTimestamp();
|
|
}
|
|
|
|
if (viewnum == r_splitscreen && gametyperules & GTR_POINTLIMIT)
|
|
{
|
|
K_drawKartPositionFaces();
|
|
}
|
|
}
|
|
else if (viewnum == r_splitscreen)
|
|
{
|
|
if (LUA_HudEnabled(hud_time))
|
|
{
|
|
K_drawKart4PTimestamp();
|
|
}
|
|
|
|
if (gametyperules & GTR_POINTLIMIT)
|
|
{
|
|
K_drawKartPositionFaces();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!stplyr->spectator && !freecam) // Bottom of the screen elements, don't need in spectate mode
|
|
{
|
|
if (demo.attract)
|
|
{
|
|
if (demo.attract == DEMO_ATTRACT_TITLE) // Draw logo on title screen demos
|
|
{
|
|
INT32 x = BASEVIDWIDTH - 8, y = BASEVIDHEIGHT-8, snapflags = V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_SLIDEIN;
|
|
patch_t *pat = static_cast<patch_t*>(W_CachePatchName((M_UseAlternateTitleScreen() ? "MTSJUMPR1" : "MTSBUMPR1"), PU_CACHE));
|
|
const UINT8 *colormap = nullptr;
|
|
|
|
if (INT32 fade = F_AttractDemoExitFade())
|
|
{
|
|
// TODO: Twodee cannot handle
|
|
// V_DrawCustomFadeScreen.
|
|
// However, since the screen fade just
|
|
// uses a colormap, the same colormap can
|
|
// be applied on a per-patch basis.
|
|
// I'm only bothering to apply this
|
|
// colormap to the attract mode sticker,
|
|
// since it's the lone HUD element.
|
|
if (lighttable_t *clm = V_LoadCustomFadeMap("FADEMAP0"))
|
|
{
|
|
// This must be statically allocated for Twodee
|
|
static UINT8 *colormap_storage;
|
|
const UINT8 *fadetable = V_OffsetIntoFadeMap(clm, fade);
|
|
|
|
if (!colormap_storage)
|
|
Z_MallocAlign(256, PU_STATIC, &colormap_storage, 8);
|
|
|
|
memcpy(colormap_storage, fadetable, 256);
|
|
colormap = colormap_storage;
|
|
|
|
Z_Free(clm);
|
|
}
|
|
}
|
|
|
|
if (r_splitscreen == 3)
|
|
{
|
|
x = BASEVIDWIDTH/2;
|
|
y = BASEVIDHEIGHT/2;
|
|
snapflags = 0;
|
|
}
|
|
|
|
V_DrawMappedPatch(x-(SHORT(pat->width)), y-(SHORT(pat->height)), snapflags, pat, colormap);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
boolean gametypeinfoshown = false;
|
|
|
|
if (K_PlayerTallyActive(stplyr) == true)
|
|
{
|
|
K_DrawPlayerTally();
|
|
}
|
|
|
|
if (LUA_HudEnabled(hud_position))
|
|
{
|
|
if (bossinfo.valid)
|
|
{
|
|
K_drawBossHealthBar();
|
|
}
|
|
else if (freecam)
|
|
;
|
|
else if ((gametyperules & GTR_POWERSTONES) && !K_PlayerTallyActive(stplyr))
|
|
{
|
|
if (!battleprisons)
|
|
K_drawKartEmeralds();
|
|
}
|
|
else if (!islonesome && !K_Cooperative())
|
|
K_DrawKartPositionNum(stplyr->position);
|
|
}
|
|
|
|
if (LUA_HudEnabled(hud_gametypeinfo))
|
|
{
|
|
if (gametyperules & GTR_CIRCUIT)
|
|
{
|
|
if (numlaps != 1)
|
|
{
|
|
K_drawKartLaps();
|
|
gametypeinfoshown = true;
|
|
}
|
|
}
|
|
else if (gametyperules & GTR_BUMPERS)
|
|
{
|
|
K_drawKartBumpersOrKarma();
|
|
gametypeinfoshown = true;
|
|
}
|
|
}
|
|
|
|
// Draw the speedometer and/or accessibility icons
|
|
if (cv_kartspeedometer.value && !r_splitscreen && (LUA_HudEnabled(hud_speedometer)))
|
|
{
|
|
K_drawKartSpeedometer(gametypeinfoshown);
|
|
}
|
|
else
|
|
{
|
|
K_drawKartAccessibilityIcons(gametypeinfoshown, 0);
|
|
}
|
|
|
|
if (gametyperules & GTR_SPHERES)
|
|
{
|
|
K_drawBlueSphereMeter(gametypeinfoshown);
|
|
}
|
|
else
|
|
{
|
|
K_drawRingCounter(gametypeinfoshown);
|
|
}
|
|
|
|
// Draw the item window
|
|
if (LUA_HudEnabled(hud_item) && !freecam)
|
|
{
|
|
if (stplyr->itemRoulette.ringbox && stplyr->itemamount == 0 && stplyr->itemtype == 0)
|
|
{
|
|
K_drawKartSlotMachine();
|
|
}
|
|
else
|
|
{
|
|
K_drawKartItem();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Draw the countdowns after everything else.
|
|
if (stplyr->lives <= 0 && stplyr->playerstate == PST_DEAD)
|
|
{
|
|
;
|
|
}
|
|
else if (stplyr->karthud[khud_fault] != 0 && stplyr->karthud[khud_finish] == 0)
|
|
{
|
|
K_drawKartFinish(false);
|
|
}
|
|
else if (starttime != introtime
|
|
&& leveltime >= introtime
|
|
&& leveltime < starttime+TICRATE)
|
|
{
|
|
K_drawKartStartCountdown();
|
|
}
|
|
else if (racecountdown && (!r_splitscreen || !stplyr->exiting))
|
|
{
|
|
char *countstr = va("%d", racecountdown/TICRATE);
|
|
|
|
if (r_splitscreen > 1)
|
|
V_DrawCenteredString(BASEVIDWIDTH/4, LAPS_Y+1, V_SPLITSCREEN, countstr);
|
|
else
|
|
{
|
|
INT32 karlen = strlen(countstr)*6; // half of 12
|
|
V_DrawTimerString((BASEVIDWIDTH/2)-karlen, LAPS_Y+3, V_SPLITSCREEN, countstr);
|
|
}
|
|
}
|
|
|
|
// Race overlays
|
|
if (!freecam)
|
|
{
|
|
if (stplyr->exiting)
|
|
K_drawKartFinish(true);
|
|
else if (!(gametyperules & GTR_CIRCUIT))
|
|
;
|
|
else if (stplyr->karthud[khud_lapanimation] && !r_splitscreen)
|
|
K_drawLapStartAnim();
|
|
}
|
|
|
|
// trick panel cool trick
|
|
if (stplyr->karthud[khud_trickcool])
|
|
K_drawTrickCool();
|
|
|
|
if ((freecam || stplyr->spectator) && LUA_HudEnabled(hud_textspectator))
|
|
{
|
|
K_drawSpectatorHUD(false);
|
|
}
|
|
|
|
if (R_GetViewNumber() == 0 && g_emeraldWin)
|
|
K_drawEmeraldWin(true);
|
|
|
|
if (modeattacking || freecam) // everything after here is MP and debug only
|
|
{
|
|
K_drawInput();
|
|
goto debug;
|
|
}
|
|
|
|
if ((gametyperules & GTR_KARMA) && !r_splitscreen && (stplyr->karthud[khud_yougotem] % 2)) // * YOU GOT EM *
|
|
V_DrawScaledPatch(BASEVIDWIDTH/2 - (SHORT(kp_yougotem->width)/2), 32, V_HUDTRANS, kp_yougotem);
|
|
|
|
// Draw FREE PLAY.
|
|
K_drawKartFreePlay();
|
|
|
|
if ((netgame || cv_mindelay.value) && r_splitscreen && Playing())
|
|
{
|
|
K_drawMiniPing();
|
|
}
|
|
|
|
K_drawKartPowerUps();
|
|
|
|
if (K_DirectorIsAvailable(viewnum) == true && LUA_HudEnabled(hud_textspectator))
|
|
{
|
|
K_drawSpectatorHUD(true);
|
|
}
|
|
else
|
|
{
|
|
K_drawInput();
|
|
}
|
|
|
|
if (cv_kartdebugdistribution.value)
|
|
K_drawDistributionDebugger();
|
|
|
|
if (cv_kartdebugnodes.value)
|
|
{
|
|
UINT8 p;
|
|
for (p = 0; p < MAXPLAYERS; p++)
|
|
V_DrawString(8, 64+(8*p), V_YELLOWMAP, va("%d - %d (%dl)", p, playernode[p], players[p].cmd.latency));
|
|
}
|
|
|
|
if (cv_kartdebugcolorize.value && stplyr->mo && stplyr->mo->skin)
|
|
{
|
|
INT32 x = 0, y = 0;
|
|
UINT16 c;
|
|
|
|
for (c = 0; c < numskincolors; c++)
|
|
{
|
|
if (skincolors[c].accessible)
|
|
{
|
|
UINT8 *cm = R_GetTranslationColormap(TC_RAINBOW, static_cast<skincolornum_t>(c), GTC_CACHE);
|
|
V_DrawFixedPatch(x<<FRACBITS, y<<FRACBITS, FRACUNIT>>1, 0, faceprefix[stplyr->skin][FACE_WANTED], cm);
|
|
|
|
x += 16;
|
|
if (x > BASEVIDWIDTH-16)
|
|
{
|
|
x = 0;
|
|
y += 16;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
debug:
|
|
K_DrawWaypointDebugger();
|
|
K_DrawBotDebugger();
|
|
K_DrawDirectorDebugger();
|
|
K_DrawGPRankDebugger();
|
|
K_DrawMessageFeed();
|
|
}
|
|
|
|
void K_DrawSticker(INT32 x, INT32 y, INT32 width, INT32 flags, boolean isSmall)
|
|
{
|
|
patch_t *stickerEnd;
|
|
INT32 height;
|
|
|
|
if (isSmall == true)
|
|
{
|
|
stickerEnd = static_cast<patch_t*>(W_CachePatchName("K_STIKE2", PU_CACHE));
|
|
height = 6;
|
|
}
|
|
else
|
|
{
|
|
stickerEnd = static_cast<patch_t*>(W_CachePatchName("K_STIKEN", PU_CACHE));
|
|
height = 11;
|
|
}
|
|
|
|
V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, FRACUNIT, flags, stickerEnd, NULL);
|
|
V_DrawFill(x, y, width, height, 24|flags);
|
|
V_DrawFixedPatch((x + width)*FRACUNIT, y*FRACUNIT, FRACUNIT, flags|V_FLIP, stickerEnd, NULL);
|
|
}
|