mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
Merge remote-tracking branch 'origin/master' into lua-roulette
This commit is contained in:
commit
b6cee4850e
25 changed files with 1007 additions and 180 deletions
|
|
@ -687,6 +687,7 @@ consvar_t cv_items[] = {
|
|||
UnsavedNetVar("gardentop", "On").on_off(),
|
||||
UnsavedNetVar("gachabom", "On").on_off(),
|
||||
UnsavedNetVar("stoneshoe", "On").on_off(),
|
||||
UnsavedNetVar("toxomister", "On").on_off(),
|
||||
UnsavedNetVar("dualsneaker", "On").on_off(),
|
||||
UnsavedNetVar("triplesneaker", "On").on_off(),
|
||||
UnsavedNetVar("triplebanana", "On").on_off(),
|
||||
|
|
|
|||
|
|
@ -166,7 +166,7 @@ UINT8 *PutFileNeeded(UINT16 firstfile)
|
|||
for (; i < numwadfiles; i++) //mainwads+1, otherwise we start on the first mainwad
|
||||
{
|
||||
// If it has only music/sound lumps, don't put it in the list
|
||||
if (i > mainwads && !wadfiles[i]->important)
|
||||
if (!wadfiles[i]->important)
|
||||
continue;
|
||||
|
||||
if (firstfile)
|
||||
|
|
@ -175,6 +175,8 @@ UINT8 *PutFileNeeded(UINT16 firstfile)
|
|||
continue;
|
||||
}
|
||||
|
||||
// CONS_Printf("putting %d (%s) - mw %d\n", i, wadfiles[i]->filename, mainwads);
|
||||
|
||||
nameonly(strcpy(wadfilename, wadfiles[i]->filename));
|
||||
|
||||
// Look below at the WRITE macros to understand what these numbers mean.
|
||||
|
|
@ -564,6 +566,9 @@ INT32 CL_CheckFiles(void)
|
|||
#endif
|
||||
for (i = 0; i < fileneedednum || j < numwadfiles;)
|
||||
{
|
||||
// CONS_Printf("checking %d of %d / %d of %d?\n", i, fileneedednum, j, numwadfiles);
|
||||
// CONS_Printf("i: %s / j: %s \n", fileneeded[i].filename, wadfiles[j]->filename);
|
||||
|
||||
if (j < numwadfiles && !wadfiles[j]->important)
|
||||
{
|
||||
// Unimportant on our side.
|
||||
|
|
@ -574,11 +579,17 @@ INT32 CL_CheckFiles(void)
|
|||
// If this test is true, we've reached the end of one file list
|
||||
// and the other still has a file that's important
|
||||
if (i >= fileneedednum || j >= numwadfiles)
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
||||
// For the sake of speed, only bother with a md5 check
|
||||
if (memcmp(wadfiles[j]->md5sum, fileneeded[i].md5sum, 16))
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
|
||||
// It's accounted for! let's keep going.
|
||||
CONS_Debug(DBG_NETPLAY, "'%s' accounted for\n", fileneeded[i].filename);
|
||||
|
|
|
|||
|
|
@ -199,7 +199,8 @@ Run this macro, then #undef FOREACH afterward
|
|||
FOREACH (DROPTARGET, 21),\
|
||||
FOREACH (GARDENTOP, 22),\
|
||||
FOREACH (GACHABOM, 23),\
|
||||
FOREACH (STONESHOE, 24)
|
||||
FOREACH (STONESHOE, 24),\
|
||||
FOREACH (TOXOMISTER, 25)
|
||||
|
||||
typedef enum
|
||||
{
|
||||
|
|
@ -1068,6 +1069,7 @@ struct player_t
|
|||
mobj_t *hand;
|
||||
mobj_t *flickyAttacker;
|
||||
mobj_t *stoneShoe;
|
||||
mobj_t *toxomisterCloud;
|
||||
|
||||
SINT8 pitblame; // Index of last player that hit you, resets after being in control for a bit. If you deathpit, credit the old attacker!
|
||||
|
||||
|
|
|
|||
|
|
@ -3120,6 +3120,11 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi
|
|||
"S_FLYBOT767",
|
||||
|
||||
"S_STON",
|
||||
|
||||
"S_TOXAA",
|
||||
"S_TOXAA_DEAD",
|
||||
"S_TOXAB",
|
||||
"S_TOXBA",
|
||||
};
|
||||
|
||||
// RegEx to generate this from info.h: ^\tMT_([^,]+), --> \t"MT_\1",
|
||||
|
|
@ -4028,6 +4033,10 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t
|
|||
|
||||
"MT_STONESHOE",
|
||||
"MT_STONESHOE_CHAIN",
|
||||
|
||||
"MT_TOXOMISTER_POLE",
|
||||
"MT_TOXOMISTER_EYE",
|
||||
"MT_TOXOMISTER_CLOUD",
|
||||
};
|
||||
|
||||
const char *const MOBJFLAG_LIST[] = {
|
||||
|
|
|
|||
|
|
@ -45,7 +45,8 @@ void ScreenshotPass::capture(Rhi& rhi)
|
|||
{
|
||||
// Read the aligned data into unaligned linear memory, flipping the rows in the process.
|
||||
uint32_t pixel_data_row = (height_ - row) - 1;
|
||||
std::move(&pixel_data_[pixel_data_row * read_stride], &pixel_data_[pixel_data_row * read_stride + stride], &packed_data_[row * stride]);
|
||||
uint8_t* pixel_data_row_ptr = &pixel_data_[pixel_data_row * read_stride];
|
||||
std::move(pixel_data_row_ptr, pixel_data_row_ptr + stride, &packed_data_[row * stride]);
|
||||
}
|
||||
|
||||
if (g_takemapthumbnail != TMT_NO)
|
||||
|
|
|
|||
87
src/info.c
87
src/info.c
|
|
@ -801,6 +801,8 @@ char sprnames[NUMSPRITES + 1][5] =
|
|||
"STUN",
|
||||
|
||||
"STON",
|
||||
"TOXA",
|
||||
"TOXB",
|
||||
|
||||
// Pulley
|
||||
"HCCH",
|
||||
|
|
@ -3264,7 +3266,7 @@ state_t states[NUMSTATES] =
|
|||
{SPR_WAYP, 1|FF_FLOORSPRITE, 1, {NULL}, 0, 0, S_NULL}, // S_WAYPOINTSPLAT
|
||||
{SPR_EGOO, 0, 1, {NULL}, 0, 0, S_NULL}, // S_EGOORB
|
||||
|
||||
{SPR_AMPA, FF_FULLBRIGHT|FF_ANIMATE, -1, {NULL}, 41, 1, S_NULL}, // S_AMPS
|
||||
{SPR_AMPA, FF_FULLBRIGHT|FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 41, 1, S_NULL}, // S_AMPS
|
||||
{SPR_EXPC, FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_EXP
|
||||
|
||||
// Water Trail
|
||||
|
|
@ -3704,6 +3706,11 @@ state_t states[NUMSTATES] =
|
|||
{SPR_STUN, FF_FULLBRIGHT|FF_ANIMATE, -1, {NULL}, 4, 4, S_NULL}, // S_FLYBOT767
|
||||
|
||||
{SPR_STON, 0, -1, {NULL}, 0, 0, S_STON}, // S_STON
|
||||
//
|
||||
{SPR_TOXA, 0, -1, {NULL}, 0, 0, S_TOXAA}, // S_TOXAA
|
||||
{SPR_TOXA, 0, 175, {NULL}, 0, 0, S_NULL}, // S_TOXAA_DEAD
|
||||
{SPR_TOXA, 1, -1, {NULL}, 0, 0, S_TOXAB}, // S_TOXAB
|
||||
{SPR_TOXB, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 6, 5, S_TOXBA}, // S_TOXBA
|
||||
};
|
||||
|
||||
mobjinfo_t mobjinfo[NUMMOBJTYPES] =
|
||||
|
|
@ -22712,6 +22719,84 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
|
|||
MF_SPECIAL|MF_SCENERY|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_PICKUPFROMBELOW|MF_DONTENCOREMAP, // flags
|
||||
S_NULL // raisestate
|
||||
},
|
||||
{ // MT_TOXOMISTER_POLE
|
||||
-1, // doomednum
|
||||
S_TOXAA, // spawnstate
|
||||
1, // spawnhealth
|
||||
S_NULL, // seestate
|
||||
sfx_tossed, // seesound
|
||||
8, // reactiontime
|
||||
sfx_None, // attacksound
|
||||
S_NULL, // painstate
|
||||
0, // painchance
|
||||
sfx_None, // painsound
|
||||
S_NULL, // meleestate
|
||||
S_NULL, // missilestate
|
||||
S_TOXAA_DEAD, // deathstate
|
||||
S_NULL, // xdeathstate
|
||||
sfx_None, // deathsound
|
||||
0, // speed
|
||||
32*FRACUNIT, // radius
|
||||
64*FRACUNIT, // height
|
||||
0, // display offset
|
||||
0, // mass
|
||||
0, // damage
|
||||
sfx_None, // activesound
|
||||
MF_SHOOTABLE|MF_DONTENCOREMAP, // flags
|
||||
S_NULL // raisestate
|
||||
},
|
||||
{ // MT_TOXOMISTER_EYE
|
||||
-1, // doomednum
|
||||
S_TOXAB, // spawnstate
|
||||
1000, // spawnhealth
|
||||
S_NULL, // seestate
|
||||
sfx_None, // seesound
|
||||
8, // reactiontime
|
||||
sfx_None, // attacksound
|
||||
S_NULL, // painstate
|
||||
0, // painchance
|
||||
sfx_None, // painsound
|
||||
S_NULL, // meleestate
|
||||
S_NULL, // missilestate
|
||||
S_NULL, // deathstate
|
||||
S_NULL, // xdeathstate
|
||||
sfx_None, // deathsound
|
||||
0, // speed
|
||||
32*FRACUNIT, // radius
|
||||
64*FRACUNIT, // height
|
||||
0, // display offset
|
||||
0, // mass
|
||||
0, // damage
|
||||
sfx_None, // activesound
|
||||
MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPTHING|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags
|
||||
S_NULL // raisestate
|
||||
},
|
||||
{ // MT_TOXOMISTER_CLOUD
|
||||
-1, // doomednum
|
||||
S_TOXBA, // spawnstate
|
||||
1000, // spawnhealth
|
||||
S_NULL, // seestate
|
||||
sfx_None, // seesound
|
||||
8, // reactiontime
|
||||
sfx_None, // attacksound
|
||||
S_NULL, // painstate
|
||||
0, // painchance
|
||||
sfx_None, // painsound
|
||||
S_NULL, // meleestate
|
||||
S_NULL, // missilestate
|
||||
S_NULL, // deathstate
|
||||
S_NULL, // xdeathstate
|
||||
sfx_None, // deathsound
|
||||
0, // speed
|
||||
70*FRACUNIT, // radius
|
||||
70*FRACUNIT, // height
|
||||
0, // display offset
|
||||
0, // mass
|
||||
0, // damage
|
||||
sfx_None, // activesound
|
||||
MF_SPECIAL|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags
|
||||
S_NULL // raisestate
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
11
src/info.h
11
src/info.h
|
|
@ -1338,6 +1338,8 @@ typedef enum sprite
|
|||
SPR_STUN,
|
||||
|
||||
SPR_STON,
|
||||
SPR_TOXA,
|
||||
SPR_TOXB,
|
||||
|
||||
// Pulley
|
||||
SPR_HCCH,
|
||||
|
|
@ -4188,6 +4190,11 @@ typedef enum state
|
|||
|
||||
S_STON,
|
||||
|
||||
S_TOXAA,
|
||||
S_TOXAA_DEAD,
|
||||
S_TOXAB,
|
||||
S_TOXBA,
|
||||
|
||||
S_FIRSTFREESLOT,
|
||||
S_LASTFREESLOT = S_FIRSTFREESLOT + NUMSTATEFREESLOTS - 1,
|
||||
NUMSTATES
|
||||
|
|
@ -5119,6 +5126,10 @@ typedef enum mobj_type
|
|||
MT_STONESHOE,
|
||||
MT_STONESHOE_CHAIN,
|
||||
|
||||
MT_TOXOMISTER_POLE,
|
||||
MT_TOXOMISTER_EYE,
|
||||
MT_TOXOMISTER_CLOUD,
|
||||
|
||||
MT_FIRSTFREESLOT,
|
||||
MT_LASTFREESLOT = MT_FIRSTFREESLOT + NUMMOBJFREESLOTS - 1,
|
||||
NUMMOBJTYPES
|
||||
|
|
|
|||
|
|
@ -2160,3 +2160,10 @@ void K_UpdateBotGameplayVars(player_t *player)
|
|||
|
||||
K_UpdateBotGameplayVarsItemUsage(player);
|
||||
}
|
||||
|
||||
boolean K_BotUnderstandsItem(kartitems_t item)
|
||||
{
|
||||
if (item == KITEM_BALLHOG)
|
||||
return false; // Sorry. MRs welcome!
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -401,6 +401,7 @@ void K_BotItemUsage(const player_t *player, ticcmd_t *cmd, INT16 turnamt);
|
|||
|
||||
void K_BotPickItemPriority(player_t *player);
|
||||
|
||||
boolean K_BotUnderstandsItem(kartitems_t item);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
|
|
|
|||
421
src/k_hud.cpp
421
src/k_hud.cpp
|
|
@ -172,6 +172,7 @@ static patch_t *kp_droptarget[3];
|
|||
static patch_t *kp_gardentop[3];
|
||||
static patch_t *kp_gachabom[3];
|
||||
static patch_t *kp_stoneshoe[3];
|
||||
static patch_t *kp_toxomister[3];
|
||||
static patch_t *kp_bar[2];
|
||||
static patch_t *kp_doublebar[2];
|
||||
static patch_t *kp_triplebar[2];
|
||||
|
|
@ -239,8 +240,11 @@ static patch_t *kp_team_you;
|
|||
static patch_t *kp_duel_foe;
|
||||
static patch_t *kp_duel_you;
|
||||
static patch_t *kp_duel_sticker;
|
||||
static patch_t *kp_duel_4sticker;
|
||||
static patch_t *kp_duel_under;
|
||||
static patch_t *kp_duel_4under;
|
||||
static patch_t *kp_duel_over;
|
||||
static patch_t *kp_duel_4over;
|
||||
static patch_t *kp_duel_margin[24];
|
||||
|
||||
patch_t *kp_autoroulette;
|
||||
|
|
@ -649,6 +653,7 @@ void K_LoadKartHUDGraphics(void)
|
|||
HU_UpdatePatch(&kp_gardentop[0], "K_ITGTOP");
|
||||
HU_UpdatePatch(&kp_gachabom[0], "K_ITGBOM");
|
||||
HU_UpdatePatch(&kp_stoneshoe[0], "K_ITSTON");
|
||||
HU_UpdatePatch(&kp_toxomister[0], "K_ITTOX");
|
||||
HU_UpdatePatch(&kp_bar[0], "K_RBBAR");
|
||||
HU_UpdatePatch(&kp_doublebar[0], "K_RBBAR2");
|
||||
HU_UpdatePatch(&kp_triplebar[0], "K_RBBAR3");
|
||||
|
|
@ -710,6 +715,7 @@ void K_LoadKartHUDGraphics(void)
|
|||
HU_UpdatePatch(&kp_gardentop[1], "K_ISGTOP");
|
||||
HU_UpdatePatch(&kp_gachabom[1], "K_ISGBOM");
|
||||
HU_UpdatePatch(&kp_stoneshoe[1], "K_ISSTON");
|
||||
HU_UpdatePatch(&kp_toxomister[1], "K_ISTOX");
|
||||
HU_UpdatePatch(&kp_bar[1], "K_SBBAR");
|
||||
HU_UpdatePatch(&kp_doublebar[1], "K_SBBAR2");
|
||||
HU_UpdatePatch(&kp_triplebar[1], "K_SBBAR3");
|
||||
|
|
@ -769,6 +775,7 @@ void K_LoadKartHUDGraphics(void)
|
|||
HU_UpdatePatch(&kp_gardentop[2], "ISPYGTOP");
|
||||
HU_UpdatePatch(&kp_gachabom[2], "ISPYGBOM");
|
||||
HU_UpdatePatch(&kp_stoneshoe[2], "ISPYSTON");
|
||||
HU_UpdatePatch(&kp_toxomister[2], "ISPYTOX");
|
||||
|
||||
// CHECK indicators
|
||||
sprintf(buffer, "K_CHECKx");
|
||||
|
|
@ -1083,8 +1090,11 @@ void K_LoadKartHUDGraphics(void)
|
|||
|
||||
HU_UpdatePatch(&kp_duel_foe, "DUEL_FOE");
|
||||
HU_UpdatePatch(&kp_duel_sticker, "DUEL_S");
|
||||
HU_UpdatePatch(&kp_duel_4sticker, "DUEL4_S");
|
||||
HU_UpdatePatch(&kp_duel_under, "DUEL_B");
|
||||
HU_UpdatePatch(&kp_duel_4under, "DUEL4_B");
|
||||
HU_UpdatePatch(&kp_duel_over, "DUEL_B2");
|
||||
HU_UpdatePatch(&kp_duel_4over, "DUEL4_B2");
|
||||
HU_UpdatePatch(&kp_duel_you, "DUEL_YOU");
|
||||
|
||||
sprintf(buffer, "DUELMBxx");
|
||||
|
|
@ -1190,6 +1200,7 @@ static patch_t *K_GetCachedItemPatch(INT32 item, UINT8 offset)
|
|||
kp_gardentop,
|
||||
kp_gachabom,
|
||||
kp_stoneshoe,
|
||||
kp_toxomister,
|
||||
};
|
||||
|
||||
if (item == KITEM_SAD || (item > KITEM_NONE && item < NUMKARTITEMS))
|
||||
|
|
@ -3306,20 +3317,50 @@ INT32 K_GetTransFlagFromFixed(fixed_t value)
|
|||
}
|
||||
}
|
||||
|
||||
static tic_t duel_lastleveltime = 0;
|
||||
static INT32 duel_marginanim = 0;
|
||||
static INT32 duel_lastmargin = 0;
|
||||
static INT32 youheight = 0;
|
||||
// We want to draw teams and duel HUD in a player context,
|
||||
// but also precisely control how often it's drawn, even if
|
||||
// some players have no view.
|
||||
static UINT8 K_FirstActiveDisplayPlayer(player_t *player)
|
||||
{
|
||||
UINT8 i;
|
||||
for (i = 0; i <= r_splitscreen; i++)
|
||||
{
|
||||
player_t *pl = &players[displayplayers[i]];
|
||||
if (!pl->spectator && !camera[i].freecam)
|
||||
break;
|
||||
}
|
||||
|
||||
if (player == &players[displayplayers[i]])
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// MAXSPLITSCREENPLAYERS not allowed here, warning for change later
|
||||
static tic_t duel_lastleveltime[4];
|
||||
static INT32 duel_marginanim[4];
|
||||
static INT32 duel_lastmargin[4];
|
||||
static INT32 youheight[4];
|
||||
|
||||
static void K_drawKartDuelScores(void)
|
||||
{
|
||||
if (!K_InRaceDuel())
|
||||
return;
|
||||
|
||||
if (r_splitscreen > 1 && !K_FirstActiveDisplayPlayer(stplyr))
|
||||
return;
|
||||
|
||||
using srb2::Draw;
|
||||
|
||||
player_t *foe = K_DuelOpponent(stplyr);
|
||||
|
||||
if (stplyr == foe)
|
||||
return;
|
||||
|
||||
boolean use4p = (r_splitscreen) ? 1 : 0;
|
||||
|
||||
UINT8 vn = R_GetViewNumber();
|
||||
|
||||
INT32 basex = 0;
|
||||
INT32 basey = 48;
|
||||
INT32 flags = V_SNAPTOLEFT|V_HUDTRANS|V_SLIDEIN;
|
||||
|
|
@ -3342,13 +3383,66 @@ static void K_drawKartDuelScores(void)
|
|||
INT32 youscorex = 16;
|
||||
INT32 youscorey = 69;
|
||||
|
||||
Draw::Font scorefont = Draw::Font::kThinTimer;
|
||||
INT32 margx = 0;
|
||||
INT32 margy = 0;
|
||||
|
||||
boolean redraw = false; // Draw a duplicate?
|
||||
boolean redrawn = false;
|
||||
|
||||
if (use4p)
|
||||
{
|
||||
basex = BASEVIDWIDTH/2 - 40;
|
||||
basey = 0;
|
||||
|
||||
flags = V_SNAPTOTOP|V_HUDTRANS|V_SLIDEIN;
|
||||
|
||||
redraw = true;
|
||||
|
||||
if (r_splitscreen == 1)
|
||||
{
|
||||
redraw = false;
|
||||
flags |= V_SNAPTORIGHT;
|
||||
if (R_GetViewNumber() == 1)
|
||||
{
|
||||
flags |= V_SNAPTOBOTTOM;
|
||||
flags &= ~V_SNAPTOTOP;
|
||||
basey = BASEVIDHEIGHT - 40;
|
||||
}
|
||||
basex = BASEVIDWIDTH - 80;
|
||||
}
|
||||
|
||||
barx = 40;
|
||||
bary = 7;
|
||||
barheight = 35; // MOTHERFUCK FLIPPED IN 4P
|
||||
barwidth = 4; // DITTO
|
||||
|
||||
foex = 6;
|
||||
foey = 12;
|
||||
youx = 63;
|
||||
youy = 12;
|
||||
|
||||
foescorex = foex + 6;
|
||||
foescorey = foey + 12;
|
||||
youscorex = youx + 6;
|
||||
youscorey = youy + 12;
|
||||
|
||||
margx = 15;
|
||||
margy = -40;
|
||||
}
|
||||
|
||||
redraw:
|
||||
|
||||
Draw::Font scorefont = use4p ? Draw::Font::kZVote : Draw::Font::kThinTimer;
|
||||
Draw::Align scorealign = use4p ? Draw::Align::kCenter : Draw::Align::kLeft;
|
||||
|
||||
UINT8 ri = 6;
|
||||
INT32 youfill = skincolors[stplyr->skincolor].ramp[ri];
|
||||
INT32 foefill = skincolors[foe->skincolor].ramp[ri];
|
||||
|
||||
V_DrawScaledPatch(basex, basey, flags, kp_duel_sticker);
|
||||
if (use4p)
|
||||
V_DrawScaledPatch(basex, basey, flags, kp_duel_4sticker);
|
||||
else
|
||||
V_DrawScaledPatch(basex, basey, flags, kp_duel_sticker);
|
||||
|
||||
INT32 scoredelta = stplyr->duelscore - foe->duelscore;
|
||||
INT32 clutchscore = DUELWINNINGSCORE - 1; // we want the bar to be full when NEXT checkpoint wins...
|
||||
|
|
@ -3371,27 +3465,43 @@ static void K_drawKartDuelScores(void)
|
|||
targetyouheight = 2*barheight - savemargin;
|
||||
}
|
||||
|
||||
if (leveltime != duel_lastleveltime)
|
||||
if (leveltime != duel_lastleveltime[vn])
|
||||
{
|
||||
INT32 slide = std::max(1, abs(targetyouheight - youheight)/3);
|
||||
if (targetyouheight > youheight)
|
||||
youheight += slide;
|
||||
else if (targetyouheight < youheight)
|
||||
youheight -= slide;
|
||||
INT32 slide = std::max(1, abs(targetyouheight - youheight[vn])/3);
|
||||
if (targetyouheight > youheight[vn])
|
||||
youheight[vn] += slide;
|
||||
else if (targetyouheight < youheight[vn])
|
||||
youheight[vn] -= slide;
|
||||
}
|
||||
|
||||
INT32 foeheight = 2*barheight-youheight; // barheight is a single tied bar, so total height of the full gauge is 2x barheight
|
||||
INT32 foeheight = 2*barheight-youheight[vn]; // barheight is a single tied bar, so total height of the full gauge is 2x barheight
|
||||
|
||||
V_DrawFill(basex+barx, basey+bary-barheight, barwidth, foeheight, foefill|flags);
|
||||
V_DrawFill(basex+barx, basey+bary-barheight+foeheight, barwidth, youheight, youfill|flags);
|
||||
if (use4p)
|
||||
{
|
||||
V_DrawFill(basex+barx-barheight, basey+bary, foeheight, barwidth, foefill|flags);
|
||||
V_DrawFill(basex+barx-barheight+foeheight, basey+bary, youheight[vn], barwidth, youfill|flags);
|
||||
}
|
||||
else
|
||||
{
|
||||
V_DrawFill(basex+barx, basey+bary-barheight, barwidth, foeheight, foefill|flags);
|
||||
V_DrawFill(basex+barx, basey+bary-barheight+foeheight, barwidth, youheight[vn], youfill|flags);
|
||||
}
|
||||
|
||||
V_DrawScaledPatch(basex, basey, flags, kp_duel_under);
|
||||
V_DrawScaledPatch(basex, basey-barheight+foeheight, flags, kp_duel_over);
|
||||
V_DrawScaledPatch(basex, basey, flags, kp_duel_foe);
|
||||
V_DrawScaledPatch(basex, basey, flags, kp_duel_you);
|
||||
V_DrawScaledPatch(basex, basey, flags, use4p ? kp_duel_4under : kp_duel_under);
|
||||
|
||||
Draw foenum = Draw(basex+foescorex, basey+foescorey).flags(flags).font(scorefont).align(Draw::Align::kLeft);
|
||||
Draw younum = Draw(basex+youscorex, basey+youscorey).flags(flags).font(scorefont).align(Draw::Align::kLeft);
|
||||
if (use4p)
|
||||
V_DrawScaledPatch(basex-barheight+foeheight, basey, flags, kp_duel_4over);
|
||||
else
|
||||
V_DrawScaledPatch(basex, basey-barheight+foeheight, flags, kp_duel_over);
|
||||
|
||||
if (!use4p)
|
||||
{
|
||||
V_DrawScaledPatch(basex, basey, flags, kp_duel_foe);
|
||||
V_DrawScaledPatch(basex, basey, flags, kp_duel_you);
|
||||
}
|
||||
|
||||
Draw foenum = Draw(basex+foescorex, basey+foescorey).flags(flags).font(scorefont).align(scorealign);
|
||||
Draw younum = Draw(basex+youscorex, basey+youscorey).flags(flags).font(scorefont).align(scorealign);
|
||||
|
||||
if (abs(scoredelta) == clutchscore && ((leveltime % 2) || cv_reducevfx.value))
|
||||
{
|
||||
|
|
@ -3414,8 +3524,8 @@ static void K_drawKartDuelScores(void)
|
|||
for (UINT8 draw = 0; draw < 2; draw++)
|
||||
{
|
||||
UINT8 drawme = draw ? (stplyr - players) : (foe - players);
|
||||
UINT8 drawx = basex + (draw ? youx : foex);
|
||||
UINT8 drawy = basey + (draw ? youy : foey);
|
||||
UINT16 drawx = basex + (draw ? youx : foex);
|
||||
UINT16 drawy = basey + (draw ? youy : foey);
|
||||
|
||||
if (!playeringame[drawme] || players[drawme].spectator)
|
||||
continue;
|
||||
|
|
@ -3450,7 +3560,7 @@ static void K_drawKartDuelScores(void)
|
|||
else
|
||||
colormap = R_GetTranslationColormap(workingskin, static_cast<skincolornum_t>(players[drawme].mo->color), GTC_CACHE);
|
||||
|
||||
V_DrawMappedPatch(drawx+xoff, drawy+yoff, flags|flipflag, faceprefix[workingskin][FACE_RANK], colormap);
|
||||
V_DrawMappedPatch(drawx+xoff, drawy+yoff, flags|flipflag, faceprefix[workingskin][use4p ? FACE_MINIMAP : FACE_RANK], colormap);
|
||||
}
|
||||
|
||||
// Dogshit. Should have just figured out how to do log base 5 in C++.
|
||||
|
|
@ -3466,139 +3576,150 @@ static void K_drawKartDuelScores(void)
|
|||
INT32 boostspersymbol = 3; // How many boosts should it take to see a new symbol?
|
||||
// rawmargin = (leveltime/10)%(3*boostspersymbol);
|
||||
|
||||
if (duel_lastleveltime != leveltime) // Trigger the "slide" animation when rawmargin changes.
|
||||
if (duel_lastleveltime[vn] != leveltime) // Trigger the "slide" animation when rawmargin changes.
|
||||
{
|
||||
duel_marginanim = std::min(duel_marginanim + 1, 100); // not magic just arbitrary
|
||||
if (duel_lastmargin != rawmargin)
|
||||
duel_marginanim[vn] = std::min(duel_marginanim[vn] + 1, 100); // not magic just arbitrary
|
||||
if (duel_lastmargin[vn] != rawmargin)
|
||||
{
|
||||
duel_marginanim = 0;
|
||||
duel_lastmargin = rawmargin;
|
||||
duel_marginanim[vn] = 0;
|
||||
duel_lastmargin[vn] = rawmargin;
|
||||
}
|
||||
}
|
||||
|
||||
duel_lastleveltime = leveltime;
|
||||
duel_lastleveltime[vn] = leveltime;
|
||||
|
||||
// CONS_Printf("=== RAWMARGIN %d\n", rawmargin);
|
||||
|
||||
if (rawmargin == 0)
|
||||
return;
|
||||
|
||||
rawmargin--; // Start at 0, idiot
|
||||
|
||||
// We're invoking the RNG to get a slightly chaotic symbol distribution,
|
||||
// but we're a HUD hook, so we need to keep the results of the call consistent.
|
||||
P_SetRandSeed(PR_NUISANCE, 69 + rawmargin);
|
||||
|
||||
INT32 highsymbol = rawmargin/boostspersymbol + 1; // Highest symbol that should appear.
|
||||
INT32 symbolsperupgrade = 5; // What is each symbol worth relative to each other? Like, 5 Stars = 1 Moon, etc.
|
||||
|
||||
// Okay, so we would LOVE to do this in a way that isn't a big clusterfuck, like just
|
||||
// doing rawmargin^3 and then subtracting powers of 5 out of that. Unfortunately, UINT64
|
||||
// is too small for the values that feel intuitively right here, so we have to do some of
|
||||
// the math on a limited set of symbols, then shift up. This is the concept of "symbol
|
||||
// headroom" that's in use here.
|
||||
//
|
||||
// (Note that Puyo~n uses a super inconsistent symbol table, probably to avoid this problem,
|
||||
// but we're assholes and want things to feel logically consistent I guess?
|
||||
// I dunno. I sort of feel like I should have just directly used the Puyo~n garbage table and
|
||||
// avoided most of this, LOL)
|
||||
|
||||
INT32 symbolheadroom = 5; // Maximum # symbols we can "step down".
|
||||
INT32 frac = rawmargin % boostspersymbol; // Used in intermediate calculations.
|
||||
INT32 minsymbol = std::max(1, highsymbol - symbolheadroom); // The lowest symbol that should appear.
|
||||
INT32 symbolheadroominuse = highsymbol - minsymbol; // The # of symbols we are stepping down.
|
||||
INT32 minscore = std::pow(symbolsperupgrade, symbolheadroominuse+1);
|
||||
INT32 maxscore = std::pow(symbolsperupgrade, symbolheadroominuse+2) - 1;
|
||||
|
||||
// CONS_Printf("min %d max %d\n", minscore, maxscore);
|
||||
|
||||
// We show the player successive combos with the same leading symbol, but we
|
||||
// waht them to feel intuitively like they're increasing each time.
|
||||
// Maxscore and minscore have been mapped to the correct power-of-N, so any
|
||||
// point we pick between them will lead with the correct symbol once we adjust
|
||||
// for symbol headroom. Pick a point that's appropriate for how "far" into the
|
||||
// current symbol we are.
|
||||
fixed_t lobound = FRACUNIT * frac / boostspersymbol;
|
||||
fixed_t hibound = FRACUNIT * (frac+1) / boostspersymbol;
|
||||
fixed_t roll = P_RandomRange(PR_NUISANCE, lobound, hibound);
|
||||
|
||||
INT32 margin = Easing_Linear(roll, minscore, maxscore); // The score we're trying to draw a garbage stack for.
|
||||
|
||||
INT32 margindigits[5];
|
||||
memset(margindigits, -1, sizeof(margindigits));
|
||||
|
||||
INT32 nummargindigits = 0;
|
||||
|
||||
// CONS_Printf("margin %d min %d max %d roll %d shiu %d ms %d\n", margin, minscore, maxscore, roll, symbolheadroominuse, minsymbol);
|
||||
|
||||
if (rawmargin/boostspersymbol >= (MARGINLEVELS-1))
|
||||
if (rawmargin != 0)
|
||||
{
|
||||
// Capped out. Show 5 Chaos.
|
||||
nummargindigits = 5;
|
||||
for(UINT8 i = 0; i < nummargindigits; i++)
|
||||
rawmargin--; // Start at 0, idiot
|
||||
|
||||
// We're invoking the RNG to get a slightly chaotic symbol distribution,
|
||||
// but we're a HUD hook, so we need to keep the results of the call consistent.
|
||||
P_SetRandSeed(PR_NUISANCE, 69 + rawmargin);
|
||||
|
||||
INT32 highsymbol = rawmargin/boostspersymbol + 1; // Highest symbol that should appear.
|
||||
INT32 symbolsperupgrade = 5; // What is each symbol worth relative to each other? Like, 5 Stars = 1 Moon, etc.
|
||||
|
||||
// Okay, so we would LOVE to do this in a way that isn't a big clusterfuck, like just
|
||||
// doing rawmargin^3 and then subtracting powers of 5 out of that. Unfortunately, UINT64
|
||||
// is too small for the values that feel intuitively right here, so we have to do some of
|
||||
// the math on a limited set of symbols, then shift up. This is the concept of "symbol
|
||||
// headroom" that's in use here.
|
||||
//
|
||||
// (Note that Puyo~n uses a super inconsistent symbol table, probably to avoid this problem,
|
||||
// but we're assholes and want things to feel logically consistent I guess?
|
||||
// I dunno. I sort of feel like I should have just directly used the Puyo~n garbage table and
|
||||
// avoided most of this, LOL)
|
||||
|
||||
INT32 symbolheadroom = 5; // Maximum # symbols we can "step down".
|
||||
INT32 frac = rawmargin % boostspersymbol; // Used in intermediate calculations.
|
||||
INT32 minsymbol = std::max(1, highsymbol - symbolheadroom); // The lowest symbol that should appear.
|
||||
INT32 symbolheadroominuse = highsymbol - minsymbol; // The # of symbols we are stepping down.
|
||||
INT32 minscore = std::pow(symbolsperupgrade, symbolheadroominuse+1);
|
||||
INT32 maxscore = std::pow(symbolsperupgrade, symbolheadroominuse+2) - 1;
|
||||
|
||||
// CONS_Printf("min %d max %d\n", minscore, maxscore);
|
||||
|
||||
// We show the player successive combos with the same leading symbol, but we
|
||||
// waht them to feel intuitively like they're increasing each time.
|
||||
// Maxscore and minscore have been mapped to the correct power-of-N, so any
|
||||
// point we pick between them will lead with the correct symbol once we adjust
|
||||
// for symbol headroom. Pick a point that's appropriate for how "far" into the
|
||||
// current symbol we are.
|
||||
fixed_t lobound = FRACUNIT * frac / boostspersymbol;
|
||||
fixed_t hibound = FRACUNIT * (frac+1) / boostspersymbol;
|
||||
fixed_t roll = P_RandomRange(PR_NUISANCE, lobound, hibound);
|
||||
|
||||
INT32 margin = Easing_Linear(roll, minscore, maxscore); // The score we're trying to draw a garbage stack for.
|
||||
|
||||
INT32 margindigits[5];
|
||||
memset(margindigits, -1, sizeof(margindigits));
|
||||
|
||||
INT32 nummargindigits = 0;
|
||||
|
||||
// CONS_Printf("margin %d min %d max %d roll %d shiu %d ms %d\n", margin, minscore, maxscore, roll, symbolheadroominuse, minsymbol);
|
||||
|
||||
if (rawmargin/boostspersymbol >= (MARGINLEVELS-1))
|
||||
{
|
||||
margindigits[i] = MARGINLEVELS-1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Subtract powers of N from our chosen score to create a decent-enough-looking
|
||||
// garbage stack, then queue up the right patches to be drawn, shifting all the math
|
||||
// up by "minsymbol"—remember, once maxsymbol goes above symbolheadroom, we are doing
|
||||
// a low-precision version of the math that ignores low enough symbols.
|
||||
while (margin > 0)
|
||||
{
|
||||
INT32 significant_margin = 0;
|
||||
for (UINT8 i = symbolheadroominuse+1; i >= 0; i--)
|
||||
// Capped out. Show 5 Chaos.
|
||||
nummargindigits = 5;
|
||||
for(UINT8 i = 0; i < nummargindigits; i++)
|
||||
{
|
||||
INT32 test = std::pow(symbolsperupgrade, i);
|
||||
// CONS_Printf("testing %d (%d)\n", i, test);
|
||||
if (margin >= test)
|
||||
{
|
||||
significant_margin = i;
|
||||
break;
|
||||
}
|
||||
margindigits[i] = MARGINLEVELS-1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Subtract powers of N from our chosen score to create a decent-enough-looking
|
||||
// garbage stack, then queue up the right patches to be drawn, shifting all the math
|
||||
// up by "minsymbol"—remember, once maxsymbol goes above symbolheadroom, we are doing
|
||||
// a low-precision version of the math that ignores low enough symbols.
|
||||
while (margin > 0)
|
||||
{
|
||||
INT32 significant_margin = 0;
|
||||
for (UINT8 i = symbolheadroominuse+1; i >= 0; i--)
|
||||
{
|
||||
INT32 test = std::pow(symbolsperupgrade, i);
|
||||
// CONS_Printf("testing %d (%d)\n", i, test);
|
||||
if (margin >= test)
|
||||
{
|
||||
significant_margin = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
INT32 index = significant_margin;
|
||||
INT32 index = significant_margin;
|
||||
|
||||
margindigits[nummargindigits] = index + minsymbol - 1;
|
||||
// CONS_Printf("digit %d %d\n", nummargindigits, margindigits[nummargindigits]);
|
||||
margindigits[nummargindigits] = index + minsymbol - 1;
|
||||
// CONS_Printf("digit %d %d\n", nummargindigits, margindigits[nummargindigits]);
|
||||
|
||||
nummargindigits++;
|
||||
nummargindigits++;
|
||||
|
||||
// CONS_Printf("margin was %d ", margin);
|
||||
margin -= std::pow(symbolsperupgrade, index);
|
||||
// CONS_Printf("is %d\n", margin);
|
||||
// CONS_Printf("margin was %d ", margin);
|
||||
margin -= std::pow(symbolsperupgrade, index);
|
||||
// CONS_Printf("is %d\n", margin);
|
||||
|
||||
if (nummargindigits >= 3 + frac)
|
||||
break;
|
||||
if (nummargindigits >= 3 + frac)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
INT32 marginspacing = std::min(6, duel_marginanim[vn]);
|
||||
INT32 marginx = ((nummargindigits-1) * marginspacing)/2;
|
||||
|
||||
for (INT32 i = nummargindigits - 1; i >= 0; i--)
|
||||
{
|
||||
// CONS_Printf("draw %d - %d\n", i, margindigits[i]);
|
||||
V_DrawScaledPatch(basex + margx + marginx, basey + margy, flags, kp_duel_margin[margindigits[i]]);
|
||||
marginx -= marginspacing;
|
||||
}
|
||||
}
|
||||
|
||||
INT32 marginspacing = std::min(6, duel_marginanim);
|
||||
INT32 marginx = ((nummargindigits-1) * marginspacing)/2;
|
||||
|
||||
for (INT32 i = nummargindigits - 1; i >= 0; i--)
|
||||
if (redraw && !redrawn)
|
||||
{
|
||||
// CONS_Printf("draw %d - %d\n", i, margindigits[i]);
|
||||
V_DrawScaledPatch(basex + marginx, basey, flags, kp_duel_margin[margindigits[i]]);
|
||||
marginx -= marginspacing;
|
||||
basey = BASEVIDHEIGHT - 40;
|
||||
flags &= ~V_SNAPTOTOP;
|
||||
flags |= V_SNAPTOBOTTOM;
|
||||
redrawn = true;
|
||||
goto redraw;
|
||||
}
|
||||
}
|
||||
|
||||
static INT32 easedallyscore = 0;
|
||||
static tic_t scorechangecooldown = 0;
|
||||
// MAXSPLITSCREENPLAYERS not allowed here, warning for changes later
|
||||
static INT32 easedallyscore[4];
|
||||
static tic_t scorechangecooldown[4];
|
||||
// Mildly ugly. Don't want to export this to khud when it's so nicely handled here,
|
||||
// but HUD hooks run at variable timing based on your actual framerate.
|
||||
static tic_t teams_lastleveltime = 0;
|
||||
static tic_t teams_lastleveltime[4];
|
||||
|
||||
void K_drawKartTeamScores(boolean fromintermission, INT32 interoffset)
|
||||
{
|
||||
if (G_GametypeHasTeams() == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (r_splitscreen > 1 && !K_FirstActiveDisplayPlayer(stplyr))
|
||||
return;
|
||||
|
||||
if (TEAM__MAX != 3)
|
||||
return; // "maybe someday" - the magic conch
|
||||
|
|
@ -3606,7 +3727,8 @@ void K_drawKartTeamScores(boolean fromintermission, INT32 interoffset)
|
|||
// I get to write HUD code from scratch, so it's going to be horribly
|
||||
// verbose and obnoxious.
|
||||
|
||||
UINT8 use4p = (r_splitscreen > 1) ? 1 : 0;
|
||||
UINT8 use4p = !!(r_splitscreen);
|
||||
UINT8 vn = R_GetViewNumber();
|
||||
|
||||
INT32 basex = BASEVIDWIDTH/2 + 20;
|
||||
INT32 basey = 0;
|
||||
|
|
@ -3660,6 +3782,18 @@ void K_drawKartTeamScores(boolean fromintermission, INT32 interoffset)
|
|||
facex = -2;
|
||||
facey = -5;
|
||||
faceoff = 4;
|
||||
|
||||
if (r_splitscreen == 1 && !fromintermission)
|
||||
{
|
||||
basex += 110;
|
||||
flags |= V_SNAPTORIGHT;
|
||||
if (R_GetViewNumber() == 1)
|
||||
{
|
||||
flags |= V_SNAPTOBOTTOM;
|
||||
flags &= ~V_SNAPTOTOP;
|
||||
basey = 170;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fromintermission)
|
||||
|
|
@ -3723,47 +3857,47 @@ void K_drawKartTeamScores(boolean fromintermission, INT32 interoffset)
|
|||
R_GetTranslationColormap(TC_RAINBOW, g_teaminfo[allies].color, GTC_CACHE) :
|
||||
R_GetTranslationColormap(TC_RAINBOW, g_teaminfo[enemies].color, GTC_CACHE);
|
||||
|
||||
if (scorechangecooldown)
|
||||
scorechangecooldown--;
|
||||
if (scorechangecooldown[vn])
|
||||
scorechangecooldown[vn]--;
|
||||
|
||||
// prevent giga flicker on team scoring
|
||||
if (easedallyscore == allyscore)
|
||||
if (easedallyscore[vn] == allyscore)
|
||||
{
|
||||
// :O
|
||||
}
|
||||
else
|
||||
{
|
||||
if (teams_lastleveltime != leveltime) // Timing consistency
|
||||
if (teams_lastleveltime[vn] != leveltime) // Timing consistency
|
||||
{
|
||||
INT32 delta = abs(easedallyscore - allyscore); // how wrong is display score?
|
||||
INT32 delta = abs(easedallyscore[vn] - allyscore); // how wrong is display score?
|
||||
|
||||
if (scorechangecooldown == 0 && delta)
|
||||
if (scorechangecooldown[vn] == 0 && delta)
|
||||
{
|
||||
if (allyscore > easedallyscore)
|
||||
if (allyscore > easedallyscore[vn])
|
||||
{
|
||||
easedallyscore++;
|
||||
easedallyscore[vn]++;
|
||||
if (!cv_reducevfx.value)
|
||||
allycolor = R_GetTranslationColormap(TC_BLINK, SKINCOLOR_WHITE, GTC_CACHE);
|
||||
}
|
||||
else
|
||||
{
|
||||
easedallyscore--;
|
||||
easedallyscore[vn]--;
|
||||
if (!cv_reducevfx.value)
|
||||
enemycolor = R_GetTranslationColormap(TC_BLINK, SKINCOLOR_WHITE, GTC_CACHE);
|
||||
}
|
||||
scorechangecooldown = TICRATE/delta;
|
||||
scorechangecooldown[vn] = TICRATE/delta;
|
||||
}
|
||||
}
|
||||
|
||||
if (!fromintermission)
|
||||
{
|
||||
// replace scores with eased scores
|
||||
allyscore = easedallyscore;
|
||||
allyscore = easedallyscore[vn];
|
||||
enemyscore = totalscore - allyscore;
|
||||
}
|
||||
}
|
||||
|
||||
teams_lastleveltime = leveltime;
|
||||
teams_lastleveltime[vn] = leveltime;
|
||||
|
||||
fixed_t enemypercent = FixedDiv(enemyscore*FRACUNIT, totalscore*FRACUNIT);
|
||||
// fixed_t allypercent = FixedDiv(allyscore*FRACUNIT, totalscore*FRACUNIT);
|
||||
|
|
@ -3806,7 +3940,7 @@ void K_drawKartTeamScores(boolean fromintermission, INT32 interoffset)
|
|||
|
||||
// Draw at the top and bottom of the screen in 4P.
|
||||
// Draw only at the bottom in intermission.
|
||||
boolean shouldsecondpass = use4p;
|
||||
boolean shouldsecondpass = (r_splitscreen > 1);
|
||||
boolean onsecondpass = fromintermission;
|
||||
|
||||
draw:
|
||||
|
|
@ -7746,11 +7880,8 @@ void K_drawKartHUD(void)
|
|||
K_DrawKartPositionNum(stplyr->position);
|
||||
}
|
||||
|
||||
if (R_GetViewNumber() == 0)
|
||||
{
|
||||
K_drawKartTeamScores(false, 0);
|
||||
K_drawKartDuelScores();
|
||||
}
|
||||
K_drawKartTeamScores(false, 0);
|
||||
K_drawKartDuelScores();
|
||||
}
|
||||
|
||||
// This sucks, but we need to draw rings before EXP because 4P amps
|
||||
|
|
|
|||
|
|
@ -462,7 +462,7 @@ std::optional<TargetTracking::Tooltip> object_tooltip(const mobj_t* mobj)
|
|||
|
||||
case MT_PLAYER:
|
||||
{
|
||||
if (stplyr->fastfall == 0 && K_CanSuperTransfer(stplyr))
|
||||
if (mobj->player == stplyr && stplyr->fastfall == 0 && K_CanSuperTransfer(stplyr))
|
||||
return Tooltip(
|
||||
TextElement(
|
||||
TextElement().parse("<c_animated>").font(splitfont))
|
||||
|
|
|
|||
85
src/k_kart.c
85
src/k_kart.c
|
|
@ -4146,6 +4146,11 @@ fixed_t K_GetNewSpeed(const player_t *player)
|
|||
p_speed = 15 * p_speed / 10;
|
||||
}
|
||||
|
||||
if (!P_MobjWasRemoved(player->toxomisterCloud))
|
||||
{
|
||||
p_speed = FixedMul(p_speed, Obj_GetToxomisterCloudDrag(player->toxomisterCloud));
|
||||
}
|
||||
|
||||
if (K_PlayerUsesBotMovement(player) == true && player->botvars.rubberband > 0)
|
||||
{
|
||||
// Acceleration is tied to top speed...
|
||||
|
|
@ -7079,7 +7084,7 @@ mobj_t *K_ThrowKartItemEx(player_t *player, boolean missile, mobjtype_t mapthing
|
|||
{
|
||||
mobj_t *lasttrail = K_FindLastTrailMobj(player);
|
||||
|
||||
if (mapthing == MT_BUBBLESHIELDTRAP) // Drop directly on top of you.
|
||||
if (mapthing == MT_BUBBLESHIELDTRAP || mapthing == MT_TOXOMISTER_POLE) // Drop directly on top of you.
|
||||
{
|
||||
newangle = player->mo->angle;
|
||||
newx = player->mo->x + player->mo->momx;
|
||||
|
|
@ -9582,6 +9587,11 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
|
|||
player->pflags2 &= ~PF2_SUPERTRANSFERVFX;
|
||||
}
|
||||
|
||||
if (K_PlayerUsesBotMovement(player) && !K_BotUnderstandsItem(player->itemtype) && player->itemamount)
|
||||
{
|
||||
K_DropItems(player);
|
||||
}
|
||||
|
||||
if (player->transfer)
|
||||
{
|
||||
if (player->fastfall)
|
||||
|
|
@ -12785,6 +12795,7 @@ static INT32 K_FlameShieldMax(player_t *player)
|
|||
UINT32 distv = 1024; // Pre no-scams: 2048
|
||||
distv = distv * 16 / FLAMESHIELD_MAX; // Old distv was based on a 16-segment bar
|
||||
UINT32 scamradius = 1500*4; // How close is close enough that we shouldn't be allowed to scam 1st?
|
||||
// UINT8 i;
|
||||
|
||||
disttofinish = K_GetItemRouletteDistance(player, 8);
|
||||
|
||||
|
|
@ -14010,23 +14021,35 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
player->instaWhipCharge = 0;
|
||||
}
|
||||
|
||||
|
||||
if ((player->cmd.buttons & BT_BAIL) && (player->cmd.buttons & BT_RESPAWNMASK) != BT_RESPAWNMASK && ((player->itemtype && player->itemamount) || (player->rings > 0) || player->superring > 0 || player->pickuprings > 0 || player->itemRoulette.active))
|
||||
if (player->cmd.buttons & BT_BAIL && (player->cmd.buttons & BT_RESPAWNMASK) != BT_RESPAWNMASK)
|
||||
{
|
||||
boolean grounded = P_IsObjectOnGround(player->mo);
|
||||
onground && player->tumbleBounces == 0 ? player->bailcharge += 2 : player->bailcharge++; // charge twice as fast on the ground
|
||||
if ((P_PlayerInPain(player) && player->bailcharge == 1) || (grounded && P_PlayerInPain(player) && player->bailcharge == 2)) // this is brittle ..
|
||||
if (leveltime < introtime || (gametyperules & GTR_SPHERES))
|
||||
{
|
||||
mobj_t *bail = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z + player->mo->height/2, MT_BAILCHARGE);
|
||||
S_StartSound(bail, sfx_gshb9); // I tried to use info.c, but you can't play sounds on mobjspawn via A_PlaySound
|
||||
S_StartSound(bail, sfx_kc4e);
|
||||
P_SetTarget(&bail->target, player->mo);
|
||||
bail->renderflags |= RF_FULLBRIGHT; // set fullbright here, were gonna animate frames in the thinker and it saves us from setting FF_FULLBRIGHT every frame
|
||||
// No bailing in GTR_SPHERES because I cannot be fucked to do manual Last Chance right now.
|
||||
// Maybe someday!
|
||||
if (!(player->oldcmd.buttons & BT_BAIL))
|
||||
if (P_IsDisplayPlayer(player))
|
||||
S_StartSound(player->mo, sfx_s3k7b);
|
||||
player->bailcharge = 0;
|
||||
}
|
||||
else if ((player->itemtype && player->itemamount) || player->rings > 0 || player->superring > 0 || player->pickuprings > 0 || player->itemRoulette.active)
|
||||
{
|
||||
// Set up bail charge, provided we have something to bail with (any rings or item resource).
|
||||
boolean grounded = P_IsObjectOnGround(player->mo);
|
||||
onground && player->tumbleBounces == 0 ? player->bailcharge += 2 : player->bailcharge++; // charge twice as fast on the ground
|
||||
if ((P_PlayerInPain(player) && player->bailcharge == 1) || (grounded && P_PlayerInPain(player) && player->bailcharge == 2)) // this is brittle ..
|
||||
{
|
||||
mobj_t *bail = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z + player->mo->height/2, MT_BAILCHARGE);
|
||||
S_StartSound(bail, sfx_gshb9); // I tried to use info.c, but you can't play sounds on mobjspawn via A_PlaySound
|
||||
S_StartSound(bail, sfx_kc4e);
|
||||
P_SetTarget(&bail->target, player->mo);
|
||||
bail->renderflags |= RF_FULLBRIGHT; // set fullbright here, were gonna animate frames in the thinker and it saves us from setting FF_FULLBRIGHT every frame
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
player->bailcharge = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
player->bailcharge = 0;
|
||||
}
|
||||
|
||||
if ((!P_PlayerInPain(player) && player->bailcharge >= 5) || player->bailcharge >= BAIL_MAXCHARGE)
|
||||
|
|
@ -14039,7 +14062,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
UINT32 debtrings = 20;
|
||||
if (player->rings < 0)
|
||||
{
|
||||
debtrings -= player->rings;
|
||||
debtrings += player->rings;
|
||||
player->rings = 0;
|
||||
}
|
||||
|
||||
|
|
@ -14951,8 +14974,15 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO)
|
||||
{
|
||||
S_StartSound(player->mo, sfx_gsha7);
|
||||
P_Thrust(player->mo, K_MomentumAngle(player->mo), 25*player->mo->scale);
|
||||
P_Thrust(player->mo, player->mo->angle, 25*player->mo->scale);
|
||||
if (P_IsObjectOnGround(player->mo)) // facing angle blends w/ momentum angle for game-feel
|
||||
{
|
||||
P_Thrust(player->mo, player->mo->angle, 25*player->mo->scale);
|
||||
P_Thrust(player->mo, K_MomentumAngle(player->mo), 25*player->mo->scale);
|
||||
}
|
||||
else // air version is momentum angle only, reduces cheese, is twice as strong to compensate
|
||||
{
|
||||
P_Thrust(player->mo, K_MomentumAngle(player->mo), 50*player->mo->scale);
|
||||
}
|
||||
|
||||
UINT8 numsparks = 8;
|
||||
for (UINT8 i = 0; i < numsparks; i++)
|
||||
|
|
@ -15059,6 +15089,21 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
player->botvars.itemconfirm = 0;
|
||||
}
|
||||
break;
|
||||
case KITEM_TOXOMISTER:
|
||||
if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO)
|
||||
{
|
||||
K_SetItemOut(player); // need this to set itemscale
|
||||
|
||||
mobj_t *pole = K_ThrowKartItem(player, false, MT_TOXOMISTER_POLE, -1, 0, 0);
|
||||
Obj_InitToxomisterPole(pole);
|
||||
|
||||
K_UnsetItemOut(player);
|
||||
|
||||
player->itemamount--;
|
||||
K_PlayAttackTaunt(player->mo);
|
||||
player->botvars.itemconfirm = 0;
|
||||
}
|
||||
break;
|
||||
case KITEM_SAD:
|
||||
if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO
|
||||
&& !player->sadtimer)
|
||||
|
|
@ -16353,6 +16398,7 @@ boolean K_IsPickMeUpItem(mobjtype_t type)
|
|||
case MT_SSMINE:
|
||||
case MT_SSMINE_SHIELD:
|
||||
case MT_FLOATINGITEM: // Stone Shoe
|
||||
case MT_TOXOMISTER_POLE:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
|
|
@ -16415,6 +16461,9 @@ static boolean K_PickUp(player_t *player, mobj_t *picked)
|
|||
else
|
||||
type = KITEM_SAD;
|
||||
break;
|
||||
case MT_TOXOMISTER_POLE:
|
||||
type = KITEM_TOXOMISTER;
|
||||
break;
|
||||
default:
|
||||
type = KITEM_SAD;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -46,10 +46,10 @@ Make sure this matches the actual number of states
|
|||
|
||||
#define BAIL_MAXCHARGE (84) // tics to bail when in painstate nad in air, on ground is half, if you touch this, also update Obj_BailChargeThink synced animation logic
|
||||
#define BAIL_DROP (FRACUNIT)
|
||||
#define BAIL_BOOST (FRACUNIT)
|
||||
#define BAIL_BOOST (6*FRACUNIT/5)
|
||||
#define BAIL_CREDIT_DEBTRINGS (true)
|
||||
#define BAIL_DROPFREQUENCY (2)
|
||||
#define BAILSTUN (TICRATE*10)
|
||||
#define BAILSTUN (TICRATE*7)
|
||||
|
||||
#define MAXCOMBOTHRUST (mapobjectscale*20)
|
||||
#define MAXCOMBOFLOAT (mapobjectscale*10)
|
||||
|
|
|
|||
|
|
@ -475,6 +475,16 @@ boolean Obj_TickStoneShoeChain(mobj_t *chain);
|
|||
player_t *Obj_StoneShoeOwnerPlayer(mobj_t *shoe);
|
||||
void Obj_CollideStoneShoe(mobj_t *mover, mobj_t *mobj);
|
||||
|
||||
/* Toxomister */
|
||||
void Obj_InitToxomisterPole(mobj_t *pole);
|
||||
boolean Obj_TickToxomisterPole(mobj_t *pole);
|
||||
boolean Obj_TickToxomisterEye(mobj_t *eye);
|
||||
boolean Obj_TickToxomisterCloud(mobj_t *cloud);
|
||||
boolean Obj_ToxomisterPoleCollide(mobj_t *pole, mobj_t *toucher);
|
||||
boolean Obj_ToxomisterCloudCollide(mobj_t *cloud, mobj_t *toucher);
|
||||
fixed_t Obj_GetToxomisterCloudDrag(mobj_t *cloud);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -71,13 +71,14 @@ void M_MPRoomSelectInit(INT32 choice)
|
|||
if (modifiedgame)
|
||||
{
|
||||
M_StartMessage("Server Browser & Add-Ons", M_GetText("You have add-ons loaded.\nYou won't be able to join netgames!\n\nTo play online, restart the game\nand don't load any addons.\n\n\"Dr. Robotnik's Ring Racers\" will\nautomatically add everything\nyou need when you join.\n"), NULL, MM_NOTHING, NULL, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
// The following behaviour is affected by modifiedgame despite the above restriction.
|
||||
// It's a sanity check were that to be removed, wheither by us or by a modified client.
|
||||
// "wheither"? That typo rules, I'm keeping that ~toast 280823
|
||||
|
||||
// thanks toaster - Tyron 2025-07-02
|
||||
|
||||
mpmenu.room = (modifiedgame == true) ? 1 : 0;
|
||||
mpmenu.ticker = 0;
|
||||
mpmenu.servernum = 0;
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ target_sources(SRB2SDL2 PRIVATE
|
|||
stone-shoe.cpp
|
||||
exp.c
|
||||
bail.c
|
||||
toxomister.cpp
|
||||
)
|
||||
|
||||
add_subdirectory(versus)
|
||||
|
|
|
|||
|
|
@ -325,10 +325,10 @@ kill_monitor_part (mobj_t *part)
|
|||
static inline UINT32
|
||||
restore_item_rng (UINT32 seed)
|
||||
{
|
||||
const UINT32 oldseed = P_GetRandSeed(PR_ITEM_ROULETTE);
|
||||
const UINT32 oldseed = P_GetRandSeed(PR_ITEM_SPAWNER);
|
||||
|
||||
P_SetRandSeedNet(PR_ITEM_ROULETTE,
|
||||
P_GetInitSeed(PR_ITEM_ROULETTE), seed);
|
||||
P_SetRandSeedNet(PR_ITEM_SPAWNER,
|
||||
P_GetInitSeed(PR_ITEM_SPAWNER), seed);
|
||||
|
||||
return oldseed;
|
||||
}
|
||||
|
|
@ -478,7 +478,7 @@ Obj_MonitorSpawnParts (mobj_t *monitor)
|
|||
P_SetScale(monitor, (monitor->destscale *= 2));
|
||||
|
||||
monitor_itemcount(monitor) = 0;
|
||||
monitor_rngseed(monitor) = P_GetRandSeed(PR_ITEM_ROULETTE);
|
||||
monitor_rngseed(monitor) = P_GetRandSeed(PR_ITEM_SPAWNER);
|
||||
monitor_spawntic(monitor) = leveltime;
|
||||
monitor_emerald(monitor) = 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -230,7 +230,7 @@ private:
|
|||
|
||||
if (P_IsObjectOnGround(this))
|
||||
{
|
||||
momz = 32 * mapobjectscale;
|
||||
momz = flip(32 * mapobjectscale);
|
||||
bouncing(true);
|
||||
voice(sfx_s3k5f);
|
||||
P_StartQuakeFromMobj(5, 40 * scale(), 512 * scale(), this);
|
||||
|
|
@ -271,6 +271,11 @@ private:
|
|||
follow()->player->stonedrag = dist > minDist();
|
||||
|
||||
sprzoff(30 * scale());
|
||||
|
||||
if (is_flipped() != follow()->is_flipped())
|
||||
{
|
||||
K_FlipFromObject(this, follow());
|
||||
}
|
||||
}
|
||||
|
||||
void move_chain()
|
||||
|
|
@ -292,6 +297,7 @@ private:
|
|||
while (Mobj::valid(node))
|
||||
{
|
||||
node->move_origin({p, pz});
|
||||
K_FlipFromObject(node, this);
|
||||
node->sprzoff(sprzoff());
|
||||
|
||||
// Let chain flicker like shoe does
|
||||
|
|
|
|||
439
src/objects/toxomister.cpp
Normal file
439
src/objects/toxomister.cpp
Normal file
|
|
@ -0,0 +1,439 @@
|
|||
// DR. ROBOTNIK'S RING RACERS
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2025 by James Robert Roman
|
||||
// Copyright (C) 2025 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.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
|
||||
#include "objects.hpp"
|
||||
|
||||
#include "../core/static_vec.hpp"
|
||||
#include "../d_player.h"
|
||||
#include "../doomdef.h"
|
||||
#include "../doomtype.h"
|
||||
#include "../g_game.h"
|
||||
#include "../k_hud.h" // transflag
|
||||
#include "../m_easing.h"
|
||||
#include "../m_fixed.h"
|
||||
#include "../m_random.h"
|
||||
#include "../r_main.h"
|
||||
#include "../tables.h"
|
||||
|
||||
using namespace srb2::objects;
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
Fixed distance3d(const Mobj* a, const Mobj* b)
|
||||
{
|
||||
return FixedHypot(FixedHypot(a->x - b->x, a->y - b->y), a->z - b->z);
|
||||
}
|
||||
|
||||
Vec2<Fixed> angle_vector(angle_t x)
|
||||
{
|
||||
return Vec2<Fixed> {FCOS(x), FSIN(x)};
|
||||
}
|
||||
|
||||
// copied from objects/hyudoro.c
|
||||
static void
|
||||
sine_bob
|
||||
( mobj_t * hyu,
|
||||
INT32 height,
|
||||
angle_t a,
|
||||
fixed_t sineofs)
|
||||
{
|
||||
hyu->sprzoff = FixedMul(height * hyu->scale,
|
||||
sineofs + FINESINE(a >> ANGLETOFINESHIFT)) * P_MobjFlip(hyu);
|
||||
}
|
||||
|
||||
static void
|
||||
bob_in_place
|
||||
( mobj_t * hyu,
|
||||
INT32 height,
|
||||
INT32 bob_speed)
|
||||
{
|
||||
sine_bob(hyu,
|
||||
height,
|
||||
(leveltime & (bob_speed - 1)) *
|
||||
(ANGLE_MAX / bob_speed), -(3*FRACUNIT/4));
|
||||
}
|
||||
|
||||
struct Eye;
|
||||
struct Pole;
|
||||
struct Cloud;
|
||||
|
||||
struct Eye : Mobj
|
||||
{
|
||||
static constexpr INT32 kOrbitRadius = 24;
|
||||
|
||||
bool valid() const { return Mobj::valid(owner()) && owner()->health > 0; }
|
||||
|
||||
bool tick()
|
||||
{
|
||||
if (!valid())
|
||||
{
|
||||
remove();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
struct Pole : Mobj
|
||||
{
|
||||
static constexpr sfxenum_t kSound = sfx_s3kdal;
|
||||
|
||||
void extravalue1() = delete;
|
||||
tic_t last_touch0() const { return mobj_t::extravalue1; }
|
||||
void last_touch0(tic_t n) { mobj_t::extravalue1 = n; }
|
||||
|
||||
void extravalue2() = delete;
|
||||
bool clouds_spawned() const { return mobj_t::extravalue2; }
|
||||
void clouds_spawned(bool n) { mobj_t::extravalue2 = n; }
|
||||
|
||||
void reactiontime() = delete;
|
||||
tic_t sound_started() const { return mobj_t::reactiontime; }
|
||||
void sound_started(tic_t n) { mobj_t::reactiontime = n; }
|
||||
|
||||
void tracer() = delete;
|
||||
Eye* eye() const { return Mobj::tracer<Eye>(); }
|
||||
void eye(Eye* n) { Mobj::tracer(n); }
|
||||
|
||||
bool valid() const
|
||||
{
|
||||
if (!Mobj::valid(eye()))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void init()
|
||||
{
|
||||
Eye* p_eye = spawn_from<Eye>(MT_TOXOMISTER_EYE);
|
||||
|
||||
p_eye->owner(this);
|
||||
p_eye->spriteyoffset(96*FRACUNIT);
|
||||
|
||||
last_touch0(leveltime);
|
||||
clouds_spawned(false);
|
||||
eye(p_eye);
|
||||
|
||||
flags |= MF_SPECIAL;
|
||||
}
|
||||
|
||||
void spawn_clouds_in_orbit();
|
||||
|
||||
bool tick()
|
||||
{
|
||||
if (!valid())
|
||||
{
|
||||
remove();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (P_IsObjectOnGround(this))
|
||||
{
|
||||
if (!clouds_spawned())
|
||||
{
|
||||
spawn_clouds_in_orbit();
|
||||
clouds_spawned(true);
|
||||
voice(sfx_s3k9e);
|
||||
}
|
||||
|
||||
if (!voice_playing(kSound))
|
||||
{
|
||||
voice(kSound);
|
||||
sound_started(leveltime);
|
||||
}
|
||||
|
||||
if ((leveltime - sound_started()) % 256 == 0)
|
||||
voice(kSound);
|
||||
}
|
||||
else
|
||||
{
|
||||
P_SpawnGhostMobj(this);
|
||||
}
|
||||
|
||||
tick_eye();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void tick_eye()
|
||||
{
|
||||
Mobj::PosArg p = {pos2d(), z};
|
||||
|
||||
p.x += momx;
|
||||
p.y += momy;
|
||||
p.z += momz;
|
||||
|
||||
Mobj* targ = find_nearest_eyeball_target();
|
||||
if (targ)
|
||||
{
|
||||
INT32 angle_to_targ = angle_to2d(targ);
|
||||
Vec2<Fixed> v = angle_vector(angle_to_targ) * Fixed {Eye::kOrbitRadius * mapobjectscale};
|
||||
|
||||
p.x += v.x;
|
||||
p.y += v.y;
|
||||
|
||||
eye()->angle = angle_to_targ;
|
||||
}
|
||||
|
||||
eye()->move_origin(p);
|
||||
}
|
||||
|
||||
angle_t angle_to2d(Mobj* mobj) const
|
||||
{
|
||||
return R_PointToAngle2(x, y, mobj->x, mobj->y);
|
||||
}
|
||||
|
||||
Mobj* find_nearest_eyeball_target() const
|
||||
{
|
||||
srb2::StaticVec<Mobj*, MAXPLAYERS> targets;
|
||||
|
||||
for (INT32 i = 0; i < MAXPLAYERS; ++i)
|
||||
{
|
||||
if (!playeringame[i])
|
||||
continue;
|
||||
|
||||
if (!players[i].mo)
|
||||
continue;
|
||||
|
||||
targets.push_back(static_cast<Mobj*>(players[i].mo));
|
||||
}
|
||||
|
||||
if (targets.empty())
|
||||
return nullptr;
|
||||
|
||||
return *std::min_element(
|
||||
targets.begin(),
|
||||
targets.end(),
|
||||
[this](Mobj* a, Mobj* b) { return distance3d(this, a) < distance3d(this, b); }
|
||||
);
|
||||
}
|
||||
|
||||
bool touch(Mobj* toucher)
|
||||
{
|
||||
if (touch_cooldown(toucher, 0))
|
||||
return false;
|
||||
|
||||
if (K_TryPickMeUp(this, toucher, false))
|
||||
return false;
|
||||
|
||||
// Adapted from P_XYMovement, MT_JAWZ
|
||||
voice(info->deathsound);
|
||||
P_KillMobj(this, NULL, NULL, DMG_NORMAL);
|
||||
|
||||
P_SetObjectMomZ(this, 24*FRACUNIT, false);
|
||||
instathrust(R_PointToAngle2(toucher->x, toucher->y, x, y), 32 * mapobjectscale);
|
||||
|
||||
flags &= ~MF_NOGRAVITY;
|
||||
hitlag(toucher, toucher, 8, true);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool touch_cooldown
|
||||
( Mobj* toucher,
|
||||
UINT8 k)
|
||||
{
|
||||
tic_t cooldown = leveltime - last_touch0();
|
||||
|
||||
if (toucher == target() && cooldown < 10)
|
||||
{
|
||||
last_touch0(leveltime);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
struct Cloud : Mobj
|
||||
{
|
||||
static constexpr INT32 kMaxFuse = 5*TICRATE;
|
||||
|
||||
void hnext() = delete;
|
||||
Mobj* follow() const { return Mobj::hnext<Mobj>(); }
|
||||
void follow(Mobj* n) { Mobj::hnext(n); }
|
||||
|
||||
void tracer() = delete;
|
||||
Pole* pole() const { return Mobj::tracer<Pole>(); }
|
||||
void pole(Pole* n) { Mobj::tracer(n); }
|
||||
|
||||
Fixed fuse_frac() const { return FRACUNIT - fuse * FRACUNIT / kMaxFuse; }
|
||||
Fixed drag_var() const { return Easing_Linear(fuse_frac(), FRACUNIT/3, FRACUNIT); }
|
||||
|
||||
bool tick()
|
||||
{
|
||||
if (Mobj::valid(follow()))
|
||||
return tick_follow();
|
||||
|
||||
return tick_patrol();
|
||||
}
|
||||
|
||||
bool tick_follow()
|
||||
{
|
||||
if (!Mobj::valid(follow()))
|
||||
{
|
||||
remove();
|
||||
return false;
|
||||
}
|
||||
|
||||
move_origin(follow()->pos());
|
||||
momx = 0;
|
||||
momy = 0;
|
||||
momz = 0;
|
||||
|
||||
bob_in_place(this, 8, 64);
|
||||
voice_loop(sfx_s3kcfl);
|
||||
|
||||
if (leveltime % (TICRATE/3) == 0 && follow()->player->rings > -20) // toxomister ring drain
|
||||
{
|
||||
follow()->player->rings--;
|
||||
S_StartSound(follow()->player->mo, sfx_antiri);
|
||||
}
|
||||
|
||||
if (fuse < 3*TICRATE && leveltime % (1 + fuse / TICRATE) == 0)
|
||||
{
|
||||
renderflags ^= RF_DONTDRAW;
|
||||
}
|
||||
|
||||
if (fuse < kMaxFuse && (kMaxFuse - fuse) % 20 == 0 && Mobj::valid(target()) && target()->player && follow()->player)
|
||||
{
|
||||
K_SpawnAmps(target()->player, K_PvPAmpReward(3, target()->player, follow()->player), this);
|
||||
}
|
||||
|
||||
follow()->player->stunned = fuse; // stunned as long as cloud is here
|
||||
return true;
|
||||
}
|
||||
|
||||
bool tick_patrol()
|
||||
{
|
||||
if (Mobj::valid(pole()) && pole()->health > 0)
|
||||
{
|
||||
move_origin(pole()->pos());
|
||||
instathrust(angle, 64 * mapobjectscale);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!fuse)
|
||||
{
|
||||
fuse = 3*TICRATE;
|
||||
instathrust(angle, 2 * mapobjectscale);
|
||||
}
|
||||
|
||||
if (leveltime & 1)
|
||||
{
|
||||
renderflags ^= RF_DONTDRAW;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool touch(Mobj* toucher)
|
||||
{
|
||||
if (toucher == target())
|
||||
return false;
|
||||
|
||||
if (toucher->player)
|
||||
{
|
||||
if (this == toucher->player->toxomisterCloud) // already attached
|
||||
return true;
|
||||
|
||||
if (!P_MobjWasRemoved(toucher->player->toxomisterCloud))
|
||||
{
|
||||
toucher->player->pflags |= PF_CASTSHADOW;
|
||||
return true;
|
||||
}
|
||||
|
||||
P_SetTarget(&toucher->player->toxomisterCloud, this);
|
||||
}
|
||||
|
||||
toucher->hitlag(8);
|
||||
scale_to(destscale);
|
||||
follow(toucher);
|
||||
fuse = kMaxFuse;
|
||||
renderflags &= ~RF_DONTDRAW;
|
||||
voice(sfx_s3k8a);
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
void Pole::spawn_clouds_in_orbit()
|
||||
{
|
||||
constexpr INT32 kNumClouds = 6;
|
||||
std::array<UINT32, kNumClouds> weights;
|
||||
std::array<INT32, kNumClouds> order;
|
||||
|
||||
angle_t a = 0;
|
||||
angle_t a_incr = ANGLE_MAX / kNumClouds;
|
||||
|
||||
for (INT32 i = 0; i < kNumClouds; ++i)
|
||||
{
|
||||
weights[i] = P_Random(PR_TRACKHAZARD);
|
||||
order[i] = i;
|
||||
}
|
||||
|
||||
std::stable_sort(order.begin(), order.end(), [&](INT32 a, INT32 b) { return weights[a] < weights[b]; });
|
||||
|
||||
for (INT32 i : order)
|
||||
{
|
||||
Cloud* cloud = spawn_from<Cloud>({}, MT_TOXOMISTER_CLOUD);
|
||||
|
||||
cloud->pole(this);
|
||||
cloud->angle = a;
|
||||
cloud->target(target());
|
||||
cloud->spriteyoffset(24*FRACUNIT);
|
||||
cloud->hitlag(2 + i * 4);
|
||||
cloud->scale_between(1, cloud->scale(), cloud->scale() / 5);
|
||||
|
||||
a += a_incr;
|
||||
}
|
||||
}
|
||||
|
||||
}; // namespace
|
||||
|
||||
void Obj_InitToxomisterPole(mobj_t *pole)
|
||||
{
|
||||
static_cast<Pole*>(pole)->init();
|
||||
}
|
||||
|
||||
boolean Obj_TickToxomisterPole(mobj_t *pole)
|
||||
{
|
||||
return static_cast<Pole*>(pole)->tick();
|
||||
}
|
||||
|
||||
boolean Obj_TickToxomisterEye(mobj_t *eye)
|
||||
{
|
||||
return static_cast<Eye*>(eye)->tick();
|
||||
}
|
||||
|
||||
boolean Obj_TickToxomisterCloud(mobj_t *cloud)
|
||||
{
|
||||
return static_cast<Cloud*>(cloud)->tick();
|
||||
}
|
||||
|
||||
boolean Obj_ToxomisterPoleCollide(mobj_t *pole, mobj_t *toucher)
|
||||
{
|
||||
return static_cast<Pole*>(pole)->touch(static_cast<Mobj*>(toucher));
|
||||
}
|
||||
|
||||
boolean Obj_ToxomisterCloudCollide(mobj_t *cloud, mobj_t *toucher)
|
||||
{
|
||||
return static_cast<Cloud*>(cloud)->touch(static_cast<Mobj*>(toucher));
|
||||
}
|
||||
|
||||
fixed_t Obj_GetToxomisterCloudDrag(mobj_t *cloud)
|
||||
{
|
||||
return static_cast<Cloud*>(cloud)->drag_var();
|
||||
}
|
||||
|
|
@ -1129,6 +1129,14 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
Obj_CollideStoneShoe(toucher, special);
|
||||
return;
|
||||
|
||||
case MT_TOXOMISTER_POLE:
|
||||
Obj_ToxomisterPoleCollide(special, toucher);
|
||||
return;
|
||||
|
||||
case MT_TOXOMISTER_CLOUD:
|
||||
Obj_ToxomisterCloudCollide(special, toucher);
|
||||
return;
|
||||
|
||||
default: // SOC or script pickup
|
||||
P_SetTarget(&special->target, toucher);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -1025,6 +1025,7 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
|
|||
|| g_tm.thing->type == MT_MONITOR
|
||||
|| g_tm.thing->type == MT_BATTLECAPSULE
|
||||
|| g_tm.thing->type == MT_KART_LEFTOVER
|
||||
|| g_tm.thing->type == MT_TOXOMISTER_POLE
|
||||
|| (g_tm.thing->type == MT_PLAYER)))
|
||||
{
|
||||
// see if it went over / under
|
||||
|
|
@ -1043,6 +1044,7 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
|
|||
|| thing->type == MT_MONITOR
|
||||
|| thing->type == MT_BATTLECAPSULE
|
||||
|| thing->type == MT_KART_LEFTOVER
|
||||
|| thing->type == MT_TOXOMISTER_POLE
|
||||
|| (thing->type == MT_PLAYER)))
|
||||
{
|
||||
// see if it went over / under
|
||||
|
|
|
|||
36
src/p_mobj.c
36
src/p_mobj.c
|
|
@ -1095,7 +1095,7 @@ fixed_t P_GetMobjGravity(mobj_t *mo)
|
|||
|
||||
wasflip = (mo->eflags & MFE_VERTICALFLIP) != 0;
|
||||
|
||||
if (mo->type != MT_SPINFIRE)
|
||||
if (mo->type != MT_SPINFIRE && mo->type != MT_STONESHOE)
|
||||
mo->eflags &= ~MFE_VERTICALFLIP;
|
||||
|
||||
if (mo->subsector->sector->ffloors) // Check for 3D floor gravity too.
|
||||
|
|
@ -1248,6 +1248,10 @@ fixed_t P_GetMobjGravity(mobj_t *mo)
|
|||
case MT_GACHABOM:
|
||||
gravityadd = (5*gravityadd)/2;
|
||||
break;
|
||||
case MT_TOXOMISTER_POLE:
|
||||
if (mo->health > 0)
|
||||
gravityadd = (5*gravityadd)/2;
|
||||
break;
|
||||
case MT_BANANA:
|
||||
case MT_BALLHOG:
|
||||
case MT_BALLHOG_RETICULE_TEST:
|
||||
|
|
@ -1303,7 +1307,7 @@ fixed_t P_GetMobjGravity(mobj_t *mo)
|
|||
gravityadd *= 2;
|
||||
break;
|
||||
case MT_STONESHOE:
|
||||
gravityadd *= 4;
|
||||
gravityadd = -4 * abs(gravityadd) * P_MobjFlip(mo);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
@ -2337,6 +2341,7 @@ boolean P_ZMovement(mobj_t *mo)
|
|||
case MT_BIGTUMBLEWEED:
|
||||
case MT_LITTLETUMBLEWEED:
|
||||
case MT_EMERALD:
|
||||
case MT_TOXOMISTER_POLE:
|
||||
if (!(mo->flags & MF_NOCLIPHEIGHT) && P_CheckDeathPitCollide(mo))
|
||||
{
|
||||
P_RemoveMobj(mo);
|
||||
|
|
@ -5320,6 +5325,7 @@ boolean P_IsKartItem(INT32 type)
|
|||
case MT_HYUDORO:
|
||||
case MT_SINK:
|
||||
case MT_GACHABOM:
|
||||
case MT_TOXOMISTER_POLE:
|
||||
return true;
|
||||
|
||||
default:
|
||||
|
|
@ -5346,6 +5352,7 @@ boolean P_IsKartFieldItem(INT32 type)
|
|||
case MT_DROPTARGET:
|
||||
case MT_DUELBOMB:
|
||||
case MT_GACHABOM:
|
||||
case MT_TOXOMISTER_POLE:
|
||||
return true;
|
||||
|
||||
default:
|
||||
|
|
@ -5379,6 +5386,8 @@ boolean P_IsRelinkItem(INT32 type)
|
|||
case MT_HYUDORO_CENTER:
|
||||
case MT_SINK:
|
||||
case MT_GACHABOM:
|
||||
case MT_TOXOMISTER_POLE:
|
||||
case MT_FLOATINGITEM: // Stone Shoe Trap
|
||||
return true;
|
||||
|
||||
default:
|
||||
|
|
@ -6863,6 +6872,12 @@ static boolean P_MobjDeadThink(mobj_t *mobj)
|
|||
P_SetMobjState(mobj, mobj->info->xdeathstate);
|
||||
/* FALLTHRU */
|
||||
case MT_JAWZ_SHIELD:
|
||||
mobj->renderflags ^= RF_DONTDRAW;
|
||||
break;
|
||||
case MT_TOXOMISTER_POLE:
|
||||
if (mobj->momz == 0 && P_IsObjectOnGround(mobj))
|
||||
P_SetMobjState(mobj, mobj->info->xdeathstate);
|
||||
|
||||
mobj->renderflags ^= RF_DONTDRAW;
|
||||
break;
|
||||
case MT_SSMINE:
|
||||
|
|
@ -10295,6 +10310,15 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
|
|||
case MT_STONESHOE:
|
||||
return Obj_TickStoneShoe(mobj);
|
||||
|
||||
case MT_TOXOMISTER_POLE:
|
||||
return Obj_TickToxomisterPole(mobj);
|
||||
|
||||
case MT_TOXOMISTER_EYE:
|
||||
return Obj_TickToxomisterEye(mobj);
|
||||
|
||||
case MT_TOXOMISTER_CLOUD:
|
||||
return Obj_TickToxomisterCloud(mobj);
|
||||
|
||||
default:
|
||||
// check mobj against possible water content, before movement code
|
||||
P_MobjCheckWater(mobj);
|
||||
|
|
@ -11159,6 +11183,9 @@ static void P_DefaultMobjShadowScale(mobj_t *thing)
|
|||
case MT_STONESHOE_CHAIN:
|
||||
thing->shadowscale = FRACUNIT/5;
|
||||
break;
|
||||
case MT_TOXOMISTER_POLE:
|
||||
thing->shadowscale = FRACUNIT;
|
||||
break;
|
||||
default:
|
||||
if (thing->flags & (MF_ENEMY|MF_BOSS))
|
||||
thing->shadowscale = FRACUNIT;
|
||||
|
|
@ -12978,12 +13005,12 @@ static boolean P_AllowMobjSpawn(mapthing_t* mthing, mobjtype_t i)
|
|||
// spawn all the duel mode objects itself, which ends up
|
||||
// calling this function again.
|
||||
// So that's why this check is even here.
|
||||
if (inDuel == false && (grandprixinfo.gp == false || grandprixinfo.eventmode != GPEVENT_BONUS))
|
||||
if (inDuel == false && (grandprixinfo.gp == false || grandprixinfo.eventmode != GPEVENT_BONUS) && gametype != GT_TUTORIAL)
|
||||
{
|
||||
if (K_IsDuelItem(i) == true
|
||||
&& K_DuelItemAlwaysSpawns(mthing) == false)
|
||||
{
|
||||
// Only spawns in Duels or GP bonus rounds.
|
||||
// Only spawns in Duels, GP bonus rounds or Tutorials.
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -15487,6 +15514,7 @@ boolean P_MobjCanChangeFlip(mobj_t *mobj)
|
|||
case MT_SHRINK_CHAIN:
|
||||
case MT_SHRINK_LASER:
|
||||
case MT_SHRINK_PARTICLE:
|
||||
case MT_STONESHOE:
|
||||
return false;
|
||||
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -93,6 +93,7 @@ typedef enum
|
|||
BALLHOGRETICULE = 0x8000,
|
||||
STONESHOE = 0x10000,
|
||||
FLYBOT = 0x20000,
|
||||
TOXOMISTERCLOUD = 0x40000,
|
||||
} player_saveflags;
|
||||
|
||||
static inline void P_ArchivePlayer(savebuffer_t *save)
|
||||
|
|
@ -368,6 +369,9 @@ static void P_NetArchivePlayers(savebuffer_t *save)
|
|||
if (players[i].stoneShoe)
|
||||
flags |= STONESHOE;
|
||||
|
||||
if (players[i].toxomisterCloud)
|
||||
flags |= TOXOMISTERCLOUD;
|
||||
|
||||
if (players[i].flybot)
|
||||
flags |= FLYBOT;
|
||||
|
||||
|
|
@ -421,6 +425,9 @@ static void P_NetArchivePlayers(savebuffer_t *save)
|
|||
if (flags & STONESHOE)
|
||||
WRITEUINT32(save->p, players[i].stoneShoe->mobjnum);
|
||||
|
||||
if (flags & TOXOMISTERCLOUD)
|
||||
WRITEUINT32(save->p, players[i].toxomisterCloud->mobjnum);
|
||||
|
||||
if (flags & FLYBOT)
|
||||
WRITEUINT32(save->p, players[i].flybot->mobjnum);
|
||||
|
||||
|
|
@ -1082,6 +1089,9 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
|
|||
if (flags & STONESHOE)
|
||||
players[i].stoneShoe = (mobj_t *)(size_t)READUINT32(save->p);
|
||||
|
||||
if (flags & TOXOMISTERCLOUD)
|
||||
players[i].toxomisterCloud = (mobj_t *)(size_t)READUINT32(save->p);
|
||||
|
||||
if (flags & FLYBOT)
|
||||
players[i].flybot = (mobj_t *)(size_t)READUINT32(save->p);
|
||||
|
||||
|
|
@ -6247,6 +6257,11 @@ static void P_RelinkPointers(void)
|
|||
if (!RelinkMobj(&players[i].stoneShoe))
|
||||
CONS_Debug(DBG_GAMELOGIC, "stoneShoe not found on player %d\n", i);
|
||||
}
|
||||
if (players[i].toxomisterCloud)
|
||||
{
|
||||
if (!RelinkMobj(&players[i].toxomisterCloud))
|
||||
CONS_Debug(DBG_GAMELOGIC, "toxomisterCloud not found on player %d\n", i);
|
||||
}
|
||||
if (players[i].flybot)
|
||||
{
|
||||
if (!RelinkMobj(&players[i].flybot))
|
||||
|
|
|
|||
|
|
@ -8434,6 +8434,14 @@ void P_FreeLevelState(void)
|
|||
HWR_ClearAllTextures();
|
||||
#endif
|
||||
|
||||
if (rendermode == render_soft)
|
||||
{
|
||||
// Queued draws might reference patches or colormaps about to be freed.
|
||||
// Flush 2D to make sure no read-after-free occurs.
|
||||
srb2::rhi::Rhi* rhi = srb2::sys::get_rhi(srb2::sys::g_current_rhi);
|
||||
srb2::sys::main_hardware_state()->twodee_renderer->flush(*rhi, srb2::g_2d);
|
||||
}
|
||||
|
||||
G_FreeGhosts(); // ghosts are allocated with PU_LEVEL
|
||||
Patch_FreeTag(PU_PATCH_LOWPRIORITY);
|
||||
Patch_FreeTag(PU_PATCH_ROTATED);
|
||||
|
|
|
|||
|
|
@ -4259,6 +4259,7 @@ void P_PlayerThink(player_t *player)
|
|||
PlayerPointerErase(player->ballhogreticule);
|
||||
PlayerPointerErase(player->flickyAttacker);
|
||||
PlayerPointerErase(player->stoneShoe);
|
||||
PlayerPointerErase(player->toxomisterCloud);
|
||||
PlayerPointerErase(player->powerup.flickyController);
|
||||
PlayerPointerErase(player->powerup.barrier);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue