Split Classic and Alt. Invincibility

- It's what the people want, why bother to complain?
- Alt is now its own separate item titled the S-Monitor
- Several variables, macros, functions, and comments have been renamed to fit this change
This commit is contained in:
yamamama 2026-02-16 02:59:17 -05:00
parent a719adb4e6
commit d5ca5516de
29 changed files with 462 additions and 399 deletions

View file

@ -8,24 +8,8 @@ Alternative items are secondary versions of existing SRB2Kart items that **reint
To toggle on/off these alternate versions, you can do so with the following commands:
- ``altitem_invincibility`` (for Invincibility)
- ``altitem_shrink`` (for Shrink)
### Alt. Invincibility (Power: Occupies Slot)
Invincibility, in its normal form, is a power item that appears in the lower ranks, letting players cut through offroad and spin out opponents with prejudice. **Alt. Invincibility** works significantly different, trading its aggression for speed and optimized recovery.
To begin, Alt. Invincibility's item odds work as an inversion of the Self-Propelled Bomb's: Like how the S.P.B. only appears when first is extremely far ahead of the pack, Alt. Invincibility only reveals itself to players who are **extremely far behind the pack**. In some extreme cases, Alt. Invincibilty will override the race-start cooldown to power-up players already left in the dust.
Unlike the standard Invincibility, **you can't damage players with Alt. Invincibility**, instead bumping them like you would normally.
Its time limit (and power) is directly tied to your distance from the "cluster player", the player closest to the largest collection on the map of (losing) players. If you notice yourself falling super far behind on the minimap, you're likely to roll Alt. Invincibility. **The further the distance, the stronger your invincibility**, and the faster you'll go. Think of it like a non-autopilot version of Mario Kart's Bullet Bill, or Sonic Racing's Drill Wisp. _Make huge comebacks, and don't lose hope!_
Do note, though: Alt. Invincibility is designed as a "gap closer", and not traditional catchup. Once you pass the cluster player, the timer **decreases at an exponentially fast rate**; don't count on it to let you steal wins from the leader!
As you run out of power (your Invincibility drops below 5 seconds), **offroad will gradually begin to affect you again**. A warning signal usually sounds as your invincibility is about to run out, as well. Be careful when you see the rainbow color begin to fade!
If you want something else, or your invincibility is running low, hold ITEM to cancel your Invincibility; the same as you would Grow.
### Alt. Shrink (Power)
Shrink, in its normal form, functions much like Mario Kart's Lightning item. Most servers keep it off due to how frequent and disruptive it is to races.
@ -60,5 +44,15 @@ If you overcharge the Flame Shield, the Flamometer **catches fire**, and you'll
If you run into players while charging your Flame Shield, **you'll flip them over**, making them lose a significant amount of speed. You however, rush right through them.
### Land Mine (Drop Behind)
This is an item exclusive to first. Drop a discreet mine onto the track; anyone who runs into it **flips over and loses speed**.
### S-Monitor (Power: Occupies Slot)
The **S-Monitor** is a special power item that works significantly different from most others, trading aggression for speed and optimized recovery.
To begin, the S-Monitor's item odds work as an inversion of the Self-Propelled Bomb's: Like how the S.P.B. only appears when first is extremely far ahead of the pack, the S-Monitor only reveals itself to players who are **extremely far behind the pack**. In some extreme cases, the S-Monitor will override the race-start cooldown to power-up players already left in the dust.
Its time limit (and power) is directly tied to your distance from the "cluster player", the player closest to the largest collection on the map of (losing) players. If you notice yourself falling super far behind on the minimap, you're likely to roll an S-Monitor. **The further the distance, the stronger your invincibility**, and the faster you'll go. Your invincibility under the effects of the S-Monitor won't run out until you pass the cluster player. Think of it like a non-autopilot version of Mario Kart's Bullet Bill, or Sonic Racing's Drill Wisp. _Make huge comebacks, and don't lose hope!_
Do note, though: the S-Monitor is designed as a "gap closer", and not traditional catchup. Once you pass the cluster player, your power **will decay at a very fast rate**; don't count on it to let you steal wins from the leader!
As you run out of power, **offroad will gradually begin to affect you again**. A warning signal usually sounds as your invincibility is about to run out, as well. Be careful when you see the rainbow color begin to fade!
If you want something else, hold ITEM to cancel the S-Monitor; the same as you would Grow.

View file

@ -492,17 +492,19 @@ consvar_t cv_kartstacking_panel_maxgrade = CVAR_INIT ("vanillaboost_panel_maxgra
//
// Invincibility
//
// Classic boosts
consvar_t cv_kartstacking_invincibility_classicspeedboost = CVAR_INIT ("vanillaboost_invincibility_classicspeedboost", "0.375", CV_NETVAR|CV_CHEAT|CV_FLOAT|CV_GUARD, CV_Unsigned, NULL);
consvar_t cv_kartstacking_invincibility_classicaccelboost = CVAR_INIT ("vanillaboost_invincibility_classicaccelboost", "3.0", CV_NETVAR|CV_CHEAT|CV_FLOAT|CV_GUARD, CV_Unsigned, NULL);
consvar_t cv_kartstacking_invincibility_classichandleboost = CVAR_INIT ("vanillaboost_invincibility_classichandleboost", "0", CV_NETVAR|CV_CHEAT|CV_FLOAT|CV_GUARD, CV_Unsigned, NULL);
// Alternate boosts
consvar_t cv_kartstacking_invincibility_alternatespeedboost = CVAR_INIT ("vanillaboost_invincibility_alternatespeedboost", "0.75", CV_NETVAR|CV_CHEAT|CV_FLOAT|CV_GUARD, CV_Unsigned, NULL);
consvar_t cv_kartstacking_invincibility_alternateaccelboost = CVAR_INIT ("vanillaboost_invincibility_alternateaccelboost", "3.0", CV_NETVAR|CV_CHEAT|CV_FLOAT|CV_GUARD, CV_Unsigned, NULL);
consvar_t cv_kartstacking_invincibility_alternatehandleboost = CVAR_INIT ("vanillaboost_invincibility_alternatehandleboost", "0.48", CV_NETVAR|CV_CHEAT|CV_FLOAT|CV_GUARD, CV_Unsigned, NULL);
consvar_t cv_kartstacking_invincibility_speedboost = CVAR_INIT ("vanillaboost_invincibility_speedboost", "0.375", CV_NETVAR|CV_CHEAT|CV_FLOAT|CV_GUARD, CV_Unsigned, NULL);
consvar_t cv_kartstacking_invincibility_accelboost = CVAR_INIT ("vanillaboost_invincibility_accelboost", "3.0", CV_NETVAR|CV_CHEAT|CV_FLOAT|CV_GUARD, CV_Unsigned, NULL);
consvar_t cv_kartstacking_invincibility_handleboost = CVAR_INIT ("vanillaboost_invincibility_handleboost", "0", CV_NETVAR|CV_CHEAT|CV_FLOAT|CV_GUARD, CV_Unsigned, NULL);
consvar_t cv_kartstacking_invincibility_stackable = CVAR_INIT ("vanillaboost_invincibility_stackable", "On", CV_NETVAR|CV_GUARD, CV_OnOff, NULL);
//
// S-Monitor
//
consvar_t cv_kartstacking_smonitor_speedboost = CVAR_INIT ("vanillaboost_smonitor_speedboost", "0.75", CV_NETVAR|CV_CHEAT|CV_FLOAT|CV_GUARD, CV_Unsigned, NULL);
consvar_t cv_kartstacking_smonitor_accelboost = CVAR_INIT ("vanillaboost_smonitor_accelboost", "3.0", CV_NETVAR|CV_CHEAT|CV_FLOAT|CV_GUARD, CV_Unsigned, NULL);
consvar_t cv_kartstacking_smonitor_handleboost = CVAR_INIT ("vanillaboost_smonitor_handleboost", "0.48", CV_NETVAR|CV_CHEAT|CV_FLOAT|CV_GUARD, CV_Unsigned, NULL);
consvar_t cv_kartstacking_smonitor_stackable = CVAR_INIT ("vanillaboost_smonitor_stackable", "On", CV_NETVAR|CV_GUARD, CV_OnOff, NULL);
consvar_t cv_kartstacking_grow_speedboost = CVAR_INIT ("vanillaboost_grow_speedboost", "0.2", CV_NETVAR|CV_CHEAT|CV_FLOAT|CV_GUARD, CV_Unsigned, NULL);
consvar_t cv_kartstacking_grow_accelboost = CVAR_INIT ("vanillaboost_grow_accelboost", "0", CV_NETVAR|CV_CHEAT|CV_FLOAT|CV_GUARD, CV_Unsigned, NULL);
consvar_t cv_kartstacking_grow_handleboost = CVAR_INIT ("vanillaboost_grow_handleboost", "0", CV_NETVAR|CV_CHEAT|CV_FLOAT|CV_GUARD, CV_Unsigned, NULL);
@ -662,14 +664,9 @@ consvar_t cv_kartaltshrinktime = CVAR_INIT ("kartaltshrinktime", "14", CV_NETVAR
static CV_PossibleValue_t kartinvintheme_cons_t[] = {{0, "Standard"}, {1, "Full"}, {0, NULL}};
consvar_t cv_kartinvintheme = CVAR_INIT ("kartinvintheme", "Standard", CV_SAVE, kartinvintheme_cons_t, NULL);
// How far the player must be from the cluster to begin frequently rolling Invincibility.
static CV_PossibleValue_t invindist_cons_t[] = {{1, "MIN"}, {32000, "MAX"}, {0, NULL}};
consvar_t cv_kartinvindist = CVAR_INIT ("kartinvindist", "17000", CV_NETVAR|CV_CHEAT|CV_GUARD, invindist_cons_t, NULL);
consvar_t cv_kartinvindistmul = CVAR_INIT ("kartinvindistmul", "0.54", CV_NETVAR|CV_CHEAT|CV_FLOAT|CV_GUARD, CV_Unsigned, NULL);
consvar_t cv_kartinvin_maxtime = CVAR_INIT ("kartinvin_maxtime", "35.0", CV_NETVAR|CV_CHEAT|CV_FLOAT|CV_GUARD, CV_Unsigned, NULL);
consvar_t cv_kartinvin_midtime = CVAR_INIT ("kartinvin_midtime", "23.333", CV_NETVAR|CV_CHEAT|CV_FLOAT|CV_GUARD, CV_Unsigned, NULL);
// How far the player must be from the cluster to roll an S-Monitor.
static CV_PossibleValue_t smonitordist_cons_t[] = {{1, "MIN"}, {32000, "MAX"}, {0, NULL}};
consvar_t cv_kartsmonitordist = CVAR_INIT ("smonitordist", "17000", CV_NETVAR|CV_CHEAT|CV_GUARD, smonitordist_cons_t, NULL);
// opinionated stuff for testing balance tweaks on the shields
consvar_t cv_kartbubble_defense_canidle = CVAR_INIT ("kartbubble_defense_canidle", "On", CV_NETVAR, CV_OnOff, NULL);

View file

@ -116,14 +116,16 @@ extern consvar_t cv_kartstacking_panel_separate;
extern consvar_t cv_kartstacking_panel_maxgrade;
extern consvar_t cv_kartstacking_invincibility_classicspeedboost;
extern consvar_t cv_kartstacking_invincibility_classicaccelboost;
extern consvar_t cv_kartstacking_invincibility_classichandleboost;
extern consvar_t cv_kartstacking_invincibility_alternatespeedboost;
extern consvar_t cv_kartstacking_invincibility_alternateaccelboost;
extern consvar_t cv_kartstacking_invincibility_alternatehandleboost;
extern consvar_t cv_kartstacking_invincibility_speedboost;
extern consvar_t cv_kartstacking_invincibility_accelboost;
extern consvar_t cv_kartstacking_invincibility_handleboost;
extern consvar_t cv_kartstacking_invincibility_stackable;
extern consvar_t cv_kartstacking_smonitor_speedboost;
extern consvar_t cv_kartstacking_smonitor_accelboost;
extern consvar_t cv_kartstacking_smonitor_handleboost;
extern consvar_t cv_kartstacking_smonitor_stackable;
extern consvar_t cv_kartstacking_flame_speedval;
extern consvar_t cv_kartstacking_flame_accelboost;
extern consvar_t cv_kartstacking_flame_handleboost;
@ -210,10 +212,7 @@ extern consvar_t cv_kartexplosion_limitlifetime_cap;
extern consvar_t cv_kartslipdash;
extern consvar_t cv_kartslopeboost;
extern consvar_t cv_kartinvintheme;
extern consvar_t cv_kartinvindist;
extern consvar_t cv_kartinvindistmul;
extern consvar_t cv_kartinvin_maxtime;
extern consvar_t cv_kartinvin_midtime;
extern consvar_t cv_kartsmonitordist;
// opinionated stuff for testing
extern consvar_t cv_kartbubble_defense_canidle;

View file

@ -735,10 +735,12 @@ struct player_t
UINT16 rocketsneakertimer; // Rocket Sneaker duration timer
UINT16 invincibilitytimer; // Invincibility timer
UINT16 maxinvincibilitytime; // (Alternate) Initial time for Invincibility, used for the item bar.
UINT16 invincibilitybottleneck; // (Alternate) Prevents breakaways by gradienting towards a heavier decrement.
INT16 invincibilitycancel; // (Alternate) Duration of Invincibility canceling.
UINT8 invincibilitywarning; // (Alternate) "Timer warning" boolean to signal Alt. Invin. is running out.
UINT16 smonitortimer; // S-Monitor timer
UINT16 maxsmonitortime; // Initial time for the S-Monitor, used for the item bar.
UINT16 smonitorexpiring; // Once you pass the cluster player, this starts the time limit for your S-Monitor invincibility.
INT16 smonitorcancel; // Duration of S-Monitor canceling.
UINT8 smonitorwarning; // "Timer warning" boolean to signal the S-Monitor is running out.
UINT8 eggmanexplode; // Fake item recieved, explode in a few seconds
SINT8 eggmanblame; // (-1 to 15) - Fake item recieved, who set this fake

View file

@ -875,7 +875,7 @@ struct menu_drawer_s const MENU_DRAWERS[] = {
};
struct odds_func_s const USEODDS_FUNCS[] = {
{ "ALTINVINODDS", &KO_AltInvinOdds },
{ "SMONITORODDS", &KO_SMonitorOdds },
{ "SPBRACEODDS", &KO_SPBRaceOdds },
{ NULL, NULL }
};
@ -1760,8 +1760,8 @@ struct int_const_s const INT_CONST[] = {
// invin constants
{"KART_NUMINVSPARKLESANIM", KART_NUMINVSPARKLESANIM},
{"BASEINVINTIME", BASEINVINTIME},
{"MININVINTIME", MININVINTIME},
{"INVINTIME", INVINTIME},
{"SMONITORTIME", SMONITORTIME},
// grow/shrink scale
{"GROW_SCALE", GROW_SCALE},

View file

@ -748,6 +748,10 @@ void G_ControllerRumbleTick(void)
{
high = RUMBLE_MODERATE;
}
else if (player->smonitortimer)
{
high = RUMBLE_MODERATE;
}
else
{
low = high = RUMBLE_MODERATE;

View file

@ -257,8 +257,14 @@ void K_DisplayItemTimers(void)
stplyr->invincibilitytimer,
{qche("K_TIINV1"), qche("K_TIINV2"), qche("K_TIINV3"), qche("K_TIINV4"), qche("K_TIINV5"), qche("K_TIINV6")},
3,
},
{ // s-monitor
"s-monitor",
stplyr->smonitortimer,
{qche("K_TISMR1")/*, qche("K_TIINV2"), qche("K_TIINV3"), qche("K_TIINV4"), qche("K_TIINV5"), qche("K_TIINV6")*/},
3,
// Hide the timer until it's necessary
static_cast<uint32_t>(((stplyr->invincibilitytimer >= MININVINTIME) && K_IsKartItemAlternate(KITEM_INVINCIBILITY)) ? TIMER_NONUMBER : 0),
static_cast<uint32_t>(((stplyr->smonitortimer >= SMONITORTIME)) ? TIMER_NONUMBER : 0),
},
{ // grow
"grow",

View file

@ -4837,7 +4837,7 @@ static void HWR_ProjectSprite(mobj_t *thing)
interptarg = thing;
if (R_IsOverlayingInvinciblePlayer(thing))
if (R_IsOverlayingSMonitorPlayer(thing))
{
// Kill overlay misalignment
interptarg = thing->target;
@ -5282,7 +5282,7 @@ static void HWR_ProjectSprite(mobj_t *thing)
if (thing->colorized)
{
vis->colormap = R_GetTranslationColormap(
R_IsOverlayingInvinciblePlayer(thing) ? TC_BLINK : TC_RAINBOW,
R_IsOverlayingSMonitorPlayer(thing) ? TC_BLINK : TC_RAINBOW,
thing->color,
GTC_CACHE);
}

View file

@ -20,3 +20,4 @@ _(LANDMINE)
_(BUBBLESHIELD)
_(FLAMESHIELD)
_(EGGBRICK)
_(S_MONITOR)

View file

@ -1263,6 +1263,7 @@ void K_BotItemUsage(botdata_t *bd, const player_t *player)
case KITEM_GROW:
case KITEM_SHRINK:
case KITEM_SUPERRING:
case KITEM_S_MONITOR:
K_BotItemGenericTap(bd);
break;
case KITEM_ROCKETSNEAKER:

View file

@ -721,7 +721,7 @@ static boolean K_BubbleReflectingTrapItem(const mobj_t *t)
static boolean K_StrongPlayerBump(const player_t *player)
{
return (((!K_IsKartItemAlternate(KITEM_INVINCIBILITY)) && (player->invincibilitytimer))
return ((player->invincibilitytimer)
|| (player->growshrinktimer > 0));
}
@ -968,12 +968,10 @@ boolean K_PvPTouchDamage(mobj_t *t1, mobj_t *t2)
boolean t1Condition = false;
boolean t2Condition = false;
// Rim suggestion: Flipover damage is negligible at best, just cull it from Invincibility as a whole.
if (!K_IsKartItemAlternate(KITEM_INVINCIBILITY))
{
t1Condition = (t1->player->invincibilitytimer > 0);
t2Condition = (t2->player->invincibilitytimer > 0);
}
t1Condition = (t1->player->invincibilitytimer > 0);
t2Condition = (t2->player->invincibilitytimer > 0);
UINT8 invindamage = DMG_WIPEOUT; // TODO: make this a cvar value
if ((t1Condition == true || flameT1 == true) && (t2Condition == true || flameT2 == true))
{
@ -981,16 +979,16 @@ boolean K_PvPTouchDamage(mobj_t *t1, mobj_t *t2)
K_DoInstashield(t2->player);
return false;
}
else if (!K_IsKartItemAlternate(KITEM_INVINCIBILITY))
else
{
if (t1Condition == true && t2Condition == false)
{
P_DamageMobj(t2, t1, t1, 1, DMG_WIPEOUT);
P_DamageMobj(t2, t1, t1, 1, invindamage);
return true;
}
else if (t1Condition == false && t2Condition == true)
{
P_DamageMobj(t1, t2, t2, 1, DMG_WIPEOUT);
P_DamageMobj(t1, t2, t2, 1, invindamage);
return true;
}
}

View file

@ -17,7 +17,7 @@
#include "r_things.h"
#include "v_video.h"
UINT16 altinvinccolors[16] = {
UINT16 smonitorcolors[16] = {
SKINCOLOR_BLOODCELL, // 0
SKINCOLOR_FUCHSIA, // 1
SKINCOLOR_MOON, // 2
@ -69,14 +69,14 @@ UINT16 K_RainbowColor(tic_t time)
}
/*--------------------------------------------------
UINT16 K_AltInvincibilityColor(tic_t time)
UINT16 K_SMonitorColor(tic_t time)
See header file for description.
--------------------------------------------------*/
UINT16 K_AltInvincibilityColor(tic_t time)
UINT16 K_SMonitorColor(tic_t time)
{
return (UINT16)(altinvinccolors[(time) & 15]);
return (UINT16)(smonitorcolors[(time) & 15]);
}
/*--------------------------------------------------

View file

@ -55,9 +55,9 @@ UINT8 K_ColorRelativeLuminance(UINT8 r, UINT8 g, UINT8 b);
UINT16 K_RainbowColor(tic_t time);
/*--------------------------------------------------
UINT16 K_AltInvincibilityColor(tic_t time)
UINT16 K_SMonitorColor(tic_t time)
Gives you a color from the "alt Invincibility" color table, for less
Gives you a color from the S-Monitor color table, for less
aggressive rainbow coloring.
Input Arguments:-
@ -67,7 +67,7 @@ UINT16 K_RainbowColor(tic_t time);
Skincolor value.
--------------------------------------------------*/
UINT16 K_AltInvincibilityColor(tic_t time);
UINT16 K_SMonitorColor(tic_t time);
/*--------------------------------------------------
void K_RainbowColormap(UINT8 *dest_colormap, skincolornum_t skincolor);

View file

@ -161,8 +161,8 @@ static patch_t *kp_flamefire[18];
// Frames of animation for the fire
#define MAXFLAMOFIRETICS 8
// Rotating Alt. Invin. sparkles
static patch_t *kp_altinvinsparkle;
// Rotating S-Monitor sparkles
static patch_t *kp_smonitorsparkle;
static patch_t *kp_rankbumper;
static patch_t *kp_tinybumper[2];
@ -421,12 +421,12 @@ void K_LoadKartHUDGraphics(void)
HU_UpdatePatch(&kp_driftgaugeparts[3], "K_WDGM4");
HU_UpdatePatch(&kp_driftgaugeparts[4], "K_DGAU3M");
// Alt. Invin. Sparkles
HU_UpdatePatch(&kp_altinvinsparkle, "ALTISPRK");
// S-Monitor. Sparkles
HU_UpdatePatch(&kp_smonitorsparkle, "ALTISPRK");
kp_altinvinsparkle->pivot.x = kp_altinvinsparkle->width / 2;
kp_altinvinsparkle->pivot.y = kp_altinvinsparkle->height / 2;
kp_altinvinsparkle->alignflags |= PATCHALIGN_USEPIVOTS;
kp_smonitorsparkle->pivot.x = kp_smonitorsparkle->width / 2;
kp_smonitorsparkle->pivot.y = kp_smonitorsparkle->height / 2;
kp_smonitorsparkle->alignflags |= PATCHALIGN_USEPIVOTS;
// Flamometer UI Elements
HU_UpdatePatch(&kp_flamometer[0], "THERMOBACK");
@ -1387,17 +1387,17 @@ static void K_drawKartItem(void)
else
localpatch = kp_nodraw;
}
else if ((stplyr->invincibilitytimer) && (K_IsKartItemAlternate(KITEM_INVINCIBILITY)))
else if (stplyr->smonitortimer)
{
if (stplyr->invincibilitytimer < MININVINTIME)
itembar = FixedDiv(stplyr->invincibilitytimer, max(1, stplyr->maxinvincibilitytime));
if (stplyr->smonitortimer < SMONITORTIME)
itembar = FixedDiv(stplyr->smonitortimer, max(1, stplyr->maxsmonitortime));
if (stplyr->invincibilitycancel > 0)
flamebar = FixedDiv(stplyr->invincibilitycancel, 26);
if (stplyr->smonitorcancel > 0)
flamebar = FixedDiv(stplyr->smonitorcancel, 26);
if (leveltime & 1)
{
localpatch = K_GetCachedItemPatch(KITEM_INVINCIBILITY, tiny, 0);
localpatch = K_GetCachedItemPatch(KITEM_S_MONITOR, tiny, 0);
isalt = true;
}
else
@ -1517,7 +1517,7 @@ static void K_drawKartItem(void)
// RadioRacers
if (shouldDrawOnPlayer)
{
boolean rocketinvinbar = ((stplyr->rocketsneakertimer > 1) || ((stplyr->invincibilitytimer) && (K_IsKartItemAlternate(KITEM_INVINCIBILITY))));
boolean rocketsmonitorbar = ((stplyr->rocketsneakertimer > 1) || (stplyr->smonitortimer));
/**
* RadioRacers
@ -1530,10 +1530,10 @@ static void K_drawKartItem(void)
* For rocket sneakers, it's the duration bar.
*/
// For Blan, Alt. Invincibility also needs duration visibility, so we dim the icon there, too.
// For Blan, the S-Monitor also needs duration visibility, so we dim the icon there, too.
// Nice-to-have: some way for custom items to have this feature? Not sure yet.
if (stplyr->itemtype == KITEM_FLAMESHIELD || stplyr->eggmanexplode > 1 || (rocketinvinbar)) {
if (stplyr->itemtype == KITEM_FLAMESHIELD || stplyr->eggmanexplode > 1 || (rocketsmonitorbar)) {
roulettetrans = (INT32)((float)(roulettetrans) * 0.57143f);
}
@ -1653,7 +1653,7 @@ static void K_drawKartItem(void)
V_DrawFixedPatch(fx<<FRACBITS, fy<<FRACBITS, info.hudScale, localpatchflags, K_getItemAltPatch(tiny, false), colmap);
}
// Extensible meter, currently used by Invincibilty, Grow, Rocket Sneakers and Flame Shield
// Extensible meter, currently used by Grow, Rocket Sneakers, Flame Shield, and the S-Monitor
// ...aren't you forgetting something?
if (itembar != -1)
K_DrawItemBar(fx, fy, info.hudScale, fflags|V_HUDTRANS, itembar, (UINT8 []){0, 8, 12});
@ -2020,11 +2020,11 @@ static void K_DrawKartPositionNum(INT32 num)
}
}
static UINT32 K_InvincibilityHUDVisibility(UINT16 t)
static UINT32 K_SMonitorHUDVisibility(UINT16 t)
{
UINT32 alphalevel = st_translucency;
alphalevel = min(10, FixedMul(alphalevel, K_InvincibilityGradient(t)));
alphalevel = min(10, FixedMul(alphalevel, K_SMonitorGradient(t)));
return min(9, 10 - alphalevel)<<V_ALPHASHIFT;
}
@ -2050,7 +2050,7 @@ static boolean K_drawKartPositionFaces(void)
INT32 rankplayer[MAXPLAYERS];
INT32 bumperx, numplayersingame = 0;
UINT8 *colormap;
UINT32 invinchudtrans;
UINT32 smntrhudtrans;
vector2_t offsets;
#ifdef ROTSPRITE
@ -2172,12 +2172,12 @@ static boolean K_drawKartPositionFaces(void)
V_DrawMappedPatch(FACE_X + offsets.x, Y + offsets.y, V_HUDTRANS|V_SNAPTOLEFT, facerank, colormap);
if ((players[rankplayer[i]].invincibilitytimer) && (K_IsKartItemAlternate(KITEM_INVINCIBILITY)))
if (players[rankplayer[i]].smonitortimer)
{
colormap = R_GetTranslationColormap(TC_BLINK, K_AltInvincibilityColor(leveltime / 2), GTC_CACHE);
invinchudtrans = K_InvincibilityHUDVisibility(players[rankplayer[i]].invincibilitytimer);
colormap = R_GetTranslationColormap(TC_BLINK, K_SMonitorColor(leveltime / 2), GTC_CACHE);
smntrhudtrans = K_SMonitorHUDVisibility(players[rankplayer[i]].smonitortimer);
V_DrawMappedPatch(FACE_X + offsets.x, Y + offsets.y, invinchudtrans|V_SNAPTOLEFT|V_ADD, facerank, colormap);
V_DrawMappedPatch(FACE_X + offsets.x, Y + offsets.y, smntrhudtrans|V_SNAPTOLEFT|V_ADD, facerank, colormap);
}
if (LUA_HudEnabled(hud_battlebumpers))
@ -2568,7 +2568,6 @@ static void K_DrawServerMods(INT32 x, INT32 y)
{"Bump Spark", 0, NULL, K_GetBumpSpark() > BUMPSPARK_NOCHARGE, true},
{"Bump Spring", 0, &cv_kartbumpspring, -1, true}
//TODO: separate drawer that enumerates item changes?
// {"Alt. Invin.", 0, NULL, K_IsKartItemAlternate(KITEM_INVINCIBILITY), true}
};
for (j = 0; j < 2; j++)
@ -3499,7 +3498,7 @@ static void K_drawKartPlayerCheck(void)
continue;
}
if ((checkplayer->invincibilitytimer <= 0) && (leveltime & 2))
if ((checkplayer->invincibilitytimer <= 0) && (checkplayer->smonitortimer <= 0) && (leveltime & 2))
{
pnum++; // white frames
}
@ -3520,6 +3519,11 @@ static void K_drawKartPlayerCheck(void)
{
pnum += 2;
}
else if (checkplayer->itemtype == KITEM_S_MONITOR || checkplayer->smonitortimer)
{
// FIXME: Separate "CHECK" icon(?)
pnum += 2;
}
K_ObjectTracking(&result, &v, true);
@ -4216,7 +4220,7 @@ static void K_drawKartMinimap(void)
spbdraw_t spb;
UINT16 usecolor;
boolean colorizeplayer;
fixed_t invingradient = 0;
fixed_t smntrgradient = 0;
#ifdef ROTSPRITE
angle_t rollangle = 0;
@ -4225,7 +4229,7 @@ static void K_drawKartMinimap(void)
patch_t *rotsparkle;
boolean halftrans = false;
fixed_t transmul = 0;
UINT32 invintrans = 0;
UINT32 smonitortrans = 0;
#endif
vector2_t iconoffsets;
@ -4439,11 +4443,16 @@ static void K_drawKartMinimap(void)
colorizeplayer = mobj->colorized;
invingradient = K_InvincibilityGradient(players[i].invincibilitytimer);
smntrgradient = K_SMonitorGradient(players[i].smonitortimer);
if ((players[i].invincibilitytimer) && ((invingradient > (FRACUNIT/2)) || (!K_IsKartItemAlternate(KITEM_INVINCIBILITY))))
if (players[i].invincibilitytimer)
{
usecolor = ((K_IsKartItemAlternate(KITEM_INVINCIBILITY)) ? K_AltInvincibilityColor(leveltime / 2) : K_RainbowColor(leveltime / 2));
usecolor = (K_RainbowColor(leveltime / 2));
colorizeplayer = true;
}
else if ((players[i].smonitortimer) && (smntrgradient > (FRACUNIT/2)))
{
usecolor = (K_SMonitorColor(leveltime / 2));
colorizeplayer = true;
}
else
@ -4476,14 +4485,13 @@ static void K_drawKartMinimap(void)
K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, workingPic, colormap, &iconoffsets);
#ifdef ROTSPRITE
if ((K_IsKartItemAlternate(KITEM_INVINCIBILITY)) &&
(players[i].invincibilitytimer) && (invingradient))
if ((players[i].smonitortimer) && (smntrgradient))
{
// Draw Alt. Invin. sparkles
// Draw S-Monitor sparkles
halftrans =
((splitflags & V_HUDTRANSHALF) == V_HUDTRANSHALF);
transmul = 0;
invintrans = 0;
smonitortrans = 0;
sparkleflags =
splitflags & (~(V_HUDTRANS | V_HUDTRANSHALF));
@ -4499,52 +4507,52 @@ static void K_drawKartMinimap(void)
FRACUNIT - (V_GetHudTrans() * FRACUNIT / 10);
}
invintrans =
smonitortrans =
max(0,
min(9,
10 - FixedMul(FixedMul(10, invingradient),
10 - FixedMul(FixedMul(10, smntrgradient),
transmul)))
<< V_ALPHASHIFT;
sparkleflags |= invintrans;
sparkleflags |= smonitortrans;
if (kp_altinvinsparkle)
if (kp_smonitorsparkle)
{
rot = R_GetRollAngle(-TICTOANGLE(leveltime));
if (rot)
{
rotsparkle = Patch_GetRotated(
kp_altinvinsparkle, rot, false);
kp_smonitorsparkle, rot, false);
}
else
{
rotsparkle = kp_altinvinsparkle;
rotsparkle = kp_smonitorsparkle;
}
if (rotsparkle)
{
widthhalf = ((kp_altinvinsparkle->width) / 2);
heighthalf = ((kp_altinvinsparkle->height) / 2);
widthhalf = ((kp_smonitorsparkle->width) / 2);
heighthalf = ((kp_smonitorsparkle->height) / 2);
if (cv_minihead.value)
{
adjustx = FixedMul(
4,
(widthhalf -
kp_altinvinsparkle->leftoffset) *
kp_smonitorsparkle->leftoffset) *
FRACUNIT / widthhalf);
adjusty = FixedMul(
4,
(heighthalf -
kp_altinvinsparkle->topoffset) *
kp_smonitorsparkle->topoffset) *
FRACUNIT / heighthalf);
}
iconoffsets.x = widthhalf -
kp_altinvinsparkle->leftoffset -
kp_smonitorsparkle->leftoffset -
adjustx;
iconoffsets.y = heighthalf -
kp_altinvinsparkle->topoffset -
kp_smonitorsparkle->topoffset -
adjusty;
K_drawKartMinimapIcon(interpx,
@ -4763,17 +4771,21 @@ static void K_drawKartMinimap(void)
#endif
colorizeplayer = mobj->colorized;
invingradient =
K_InvincibilityGradient(
players[localplayers[i]].invincibilitytimer);
smntrgradient =
K_SMonitorGradient(
players[localplayers[i]].smonitortimer);
if ((players[localplayers[i]].invincibilitytimer) &&
((invingradient > (FRACUNIT / 2)) ||
(!K_IsKartItemAlternate(KITEM_INVINCIBILITY))))
if (players[localplayers[i]].invincibilitytimer)
{
usecolor = (K_RainbowColor(leveltime / 2));
colorizeplayer = true;
}
else if ((players[localplayers[i]].smonitortimer)
&& ((smntrgradient > (FRACUNIT / 2))))
{
usecolor = (K_SMonitorColor(leveltime / 2));
colorizeplayer = true;
}
else
{
usecolor = mobj->color;
@ -4839,13 +4851,12 @@ static void K_drawKartMinimap(void)
K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, workingPic, colormap, &iconoffsets);
#ifdef ROTSPRITE
if ((!nocontest) && (K_IsKartItemAlternate(KITEM_INVINCIBILITY)) &&
(players[localplayers[i]].invincibilitytimer) && (invingradient))
if ((!nocontest) && (players[localplayers[i]].smonitortimer) && (smntrgradient))
{
// Draw Alt. Invin. sparkles
// Draw S-Monitor sparkles
halftrans = ((splitflags & V_HUDTRANSHALF) == V_HUDTRANSHALF);
transmul = 0;
invintrans = 0;
smonitortrans = 0;
sparkleflags = splitflags & (~(V_HUDTRANS | V_HUDTRANSHALF));
@ -4860,48 +4871,48 @@ static void K_drawKartMinimap(void)
transmul *= 2;
invintrans =
smonitortrans =
max(0,
min(9,
10 - FixedMul(FixedMul(10, invingradient), transmul)))
10 - FixedMul(FixedMul(10, smntrgradient), transmul)))
<< V_ALPHASHIFT;
sparkleflags |= invintrans;
sparkleflags |= smonitortrans;
if (kp_altinvinsparkle)
if (kp_smonitorsparkle)
{
rot = R_GetRollAngle(-TICTOANGLE(leveltime));
if (rot)
{
rotsparkle = Patch_GetRotated(
kp_altinvinsparkle, rot, false);
kp_smonitorsparkle, rot, false);
}
else
{
rotsparkle = kp_altinvinsparkle;
rotsparkle = kp_smonitorsparkle;
}
if (rotsparkle)
{
widthhalf = ((kp_altinvinsparkle->width) / 2);
heighthalf = ((kp_altinvinsparkle->height) / 2);
widthhalf = ((kp_smonitorsparkle->width) / 2);
heighthalf = ((kp_smonitorsparkle->height) / 2);
if (cv_minihead.value)
{
adjustx = FixedMul(
4,
(widthhalf - kp_altinvinsparkle->leftoffset) *
(widthhalf - kp_smonitorsparkle->leftoffset) *
FRACUNIT / widthhalf);
adjusty = FixedMul(
4,
(heighthalf - kp_altinvinsparkle->topoffset) *
(heighthalf - kp_smonitorsparkle->topoffset) *
FRACUNIT / heighthalf);
}
iconoffsets.x =
widthhalf - kp_altinvinsparkle->leftoffset - adjustx;
widthhalf - kp_smonitorsparkle->leftoffset - adjustx;
iconoffsets.y =
heighthalf - kp_altinvinsparkle->topoffset - adjusty;
heighthalf - kp_smonitorsparkle->topoffset - adjusty;
K_drawKartMinimapIcon(interpx,
interpy,

View file

@ -358,7 +358,7 @@ static boolean K_DontDoubleMyItems(kartitemtype_e type, UINT8 amount)
|| type == KITEM_INVINCIBILITY || type == KITEM_GROW
|| type == KITEM_BUBBLESHIELD || type == KITEM_FLAMESHIELD
|| type == KITEM_ROCKETSNEAKER || type == KITEM_SHRINK
|| type == KITEM_HYUDORO
|| type == KITEM_HYUDORO || type == KITEM_S_MONITOR
|| (type == KITEM_BANANA && amount >= 4)
|| (type == KITEM_ORBINAUT && amount >= 3)
|| (type == KITEM_SNEAKER && amount >= 3)
@ -498,24 +498,24 @@ UINT32 K_ScaleItemDistance(UINT32 distance, UINT8 numPlayers, boolean spbrush)
#define INVODDS 30
// Prevent integer overflows; don't let this go past 16383
#define INVINDESPERATION 4
#define MAXINVODDS ((MAXPROBABILITY * 2) * INVINDESPERATION)
#define SMONITOR_DESPERATION 4
#define MAXSMONITORODDS ((MAXPROBABILITY * 2) * SMONITOR_DESPERATION)
// Odds value for Alt. Invin. to force itself on trailing players.
#define INVFORCEODDS (MAXINVODDS / 2)
// Odds value for the S-Monitor to force itself on trailing players.
#define SMONITOR_FORCEODDS (MAXSMONITORODDS / 2)
static INT32 K_KartGetInvincibilityOdds(UINT32 dist)
static INT32 K_KartGetSMonitorOdds(UINT32 dist)
{
// I'm tired; use floating-point distances for this fuckshit.
INT32 invdist = INVINDIST;
INT32 monitordist = SMONITORDIST;
float fac_f = (float)(dist) / ((float)(invdist));
float fac_f = (float)(dist) / ((float)(monitordist));
if (fac_f < 1.0f)
return 0;
// If you're far enough for this to be in your item slot, you're far enough to SERIOUSLY need this.
return MAXINVODDS;
return MAXSMONITORODDS;
}
// updates all result cooldown timers, and sets cooldowns for "unique" items
@ -1556,8 +1556,8 @@ void K_SetPlayerItemCooldown(player_t *player, tic_t timer, boolean force)
// Unique odds functions, for REAL this time
// Alt. Invin. odds
INT32 KO_AltInvinOdds(INT32 odds, const kartroulette_t *roulette, const kartresult_t *result, UINT8 *forceme)
// S-Monitor odds
INT32 KO_SMonitorOdds(INT32 odds, const kartroulette_t *roulette, const kartresult_t *result, UINT8 *forceme)
{
(void)result;
@ -1569,10 +1569,10 @@ INT32 KO_AltInvinOdds(INT32 odds, const kartroulette_t *roulette, const kartresu
cdist = (9 * cdist / 5);
}
odds = K_KartGetInvincibilityOdds(cdist);
odds = K_KartGetSMonitorOdds(cdist);
// Special case: if you're SERIOUSLY far behind before the cooldown finishes, ignore it and start forcing,
if (odds >= INVFORCEODDS)
if (odds >= SMONITOR_FORCEODDS)
{
*forceme = 3; // Take priority over SPBs
}
@ -1665,6 +1665,10 @@ static void K_DoGrowShrink(player_t *player, boolean shrinking)
{
; // invincibility has priority in P_RestoreMusic, no point in starting here
}
else if (player->smonitortimer > 0)
{
; // Ditto for S-Monitor
}
else if (P_IsLocalPlayer(player) == true)
{
S_ChangeMusicSpecial((shrinking) ? "kshrnk" : "kgrow");
@ -2054,9 +2058,18 @@ void K_PlayerItemThink(player_t *player, boolean onground)
}
break;
case KITEM_INVINCIBILITY:
if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) // Doesn't hold your item slot hostage in Legacy, so you're free to waste it if you have multiple
if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) // Doesn't hold your item slot hostage, so you're free to waste it if you have multiple
{
K_DoInvincibility(player, K_GetInvincibilityTime(player));
K_DoInvincibility(player, INVINTIME);
K_PlayPowerGloatSound(player->mo);
player->itemamount--;
K_BotResetItemConfirm(player, false);
}
break;
case KITEM_S_MONITOR:
if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO)
{
K_DoSMonitor(player, SMONITORTIME);
K_PlayPowerGloatSound(player->mo);
player->itemamount--;
K_BotResetItemConfirm(player, false);

View file

@ -214,7 +214,7 @@ void K_StartRoulette(player_t *player, kartroulettetype_e roulettetype);
void K_SetPlayerItemCooldown(player_t *player, tic_t timer, boolean force);
useoddsfunc_f KO_AltInvinOdds;
useoddsfunc_f KO_SMonitorOdds;
useoddsfunc_f KO_SPBRaceOdds;
void K_DoThunderShield(player_t *player);

View file

@ -310,14 +310,16 @@ void K_RegisterKartStuff(void)
CV_RegisterVar(&cv_kartstacking_panel_separate);
CV_RegisterVar(&cv_kartstacking_panel_maxgrade);
CV_RegisterVar(&cv_kartstacking_invincibility_classicspeedboost);
CV_RegisterVar(&cv_kartstacking_invincibility_classicaccelboost);
CV_RegisterVar(&cv_kartstacking_invincibility_classichandleboost);
CV_RegisterVar(&cv_kartstacking_invincibility_alternatespeedboost);
CV_RegisterVar(&cv_kartstacking_invincibility_alternateaccelboost);
CV_RegisterVar(&cv_kartstacking_invincibility_alternatehandleboost);
CV_RegisterVar(&cv_kartstacking_invincibility_speedboost);
CV_RegisterVar(&cv_kartstacking_invincibility_accelboost);
CV_RegisterVar(&cv_kartstacking_invincibility_handleboost);
CV_RegisterVar(&cv_kartstacking_invincibility_stackable);
CV_RegisterVar(&cv_kartstacking_smonitor_speedboost);
CV_RegisterVar(&cv_kartstacking_smonitor_accelboost);
CV_RegisterVar(&cv_kartstacking_smonitor_handleboost);
CV_RegisterVar(&cv_kartstacking_smonitor_stackable);
CV_RegisterVar(&cv_kartstacking_grow_speedboost);
CV_RegisterVar(&cv_kartstacking_grow_accelboost);
CV_RegisterVar(&cv_kartstacking_grow_handleboost);
@ -432,11 +434,7 @@ void K_RegisterKartStuff(void)
CV_RegisterVar(&cv_kartinvintheme);
CV_RegisterVar(&cv_kartinvindist);
CV_RegisterVar(&cv_kartinvindistmul);
CV_RegisterVar(&cv_kartinvin_maxtime);
CV_RegisterVar(&cv_kartinvin_midtime);
CV_RegisterVar(&cv_kartsmonitordist);
// experimental stuff
CV_RegisterVar(&cv_kartbubble_defense_canidle);
@ -527,7 +525,6 @@ static fixed_t K_PlayerWeight(mobj_t* mobj, mobj_t* against)
{
fixed_t weight = 5 * FRACUNIT;
fixed_t bubbleMultiplier = FRACUNIT;
const boolean invinisalt = K_IsKartItemAlternate(KITEM_INVINCIBILITY);
if (!mobj->player)
return weight;
@ -543,11 +540,12 @@ static fixed_t K_PlayerWeight(mobj_t* mobj, mobj_t* against)
{
return 0; // This player does not cause any bump action
}
else if (invinisalt && against->player->invincibilitytimer)
else if (against->player->smonitortimer)
{
// Scale Alt. Invin. weight to their power. At full power, you get shoved
// off! Might cause less shitty-feeling bumpcheck moments.
return max(0, FRACUNIT - against->player->kartweight * K_InvincibilityGradient(against->player->invincibilitytimer));
// Scale S-Monitor weight to their power. At full power, you get shoved off!
// Might cause less shitty-feeling bumpcheck moments.
return max(0, FRACUNIT - against->player->kartweight *
K_SMonitorGradient(against->player->smonitortimer));
}
}
@ -557,12 +555,12 @@ static fixed_t K_PlayerWeight(mobj_t* mobj, mobj_t* against)
weight = (mobj->player->kartweight) * FRACUNIT;
if (invinisalt && mobj->player->invincibilitytimer)
if (mobj->player->smonitortimer)
{
// Cap the gradient at 1.0 to prevent exaggerated nonsense.
fixed_t mygradient = min(FRACUNIT, K_InvincibilityGradient(mobj->player->invincibilitytimer));
fixed_t mygradient = min(FRACUNIT, K_SMonitorGradient(mobj->player->smonitortimer));
// Scale Alt. Invin. weight to your power. At full power, they get shoved off!
// Scale S-Monitor weight weight to your power. At full power, they get shoved off!
// Might cause less shitty-feeling bumpcheck moments.
weight = FixedMul(weight, mygradient);
@ -617,7 +615,7 @@ fixed_t K_GetMobjWeight(mobj_t *mobj, mobj_t *against)
case MT_FALLINGROCK:
if (against->player)
{
if (against->player->invincibilitytimer || against->player->growshrinktimer > 0 || (K_GetShieldFromPlayer(against->player) == KSHIELD_BUBBLE))
if (against->player->invincibilitytimer || against->player->smonitortimer || against->player->growshrinktimer > 0 || (K_GetShieldFromPlayer(against->player) == KSHIELD_BUBBLE))
weight = 0;
else
weight = K_PlayerWeight(against, NULL);
@ -1010,11 +1008,17 @@ static fixed_t K_CheckOffroadCollide(mobj_t *mo)
return 0; // couldn't find any offroad
}
static fixed_t K_OffroadGradient(player_t *player, fixed_t offroad)
static fixed_t K_OffroadGradient(player_t* player, fixed_t offroad)
{
// At 50% or lower Invincibility, offroad creeps up on you.
fixed_t invinoffroad = (!K_IsKartItemAlternate(KITEM_INVINCIBILITY)) ? ((player->invincibilitytimer) ? FRACUNIT : 0) : min(FRACUNIT, K_InvincibilityGradient(player->invincibilitytimer));
fixed_t fac = CLAMP(FRACUNIT - invinoffroad, 0, FRACUNIT);
// Invincibility: Just ignore everything.
fixed_t invinoffroad = (player->invincibilitytimer) ? FRACUNIT : 0;
// S-Monitor: At 50% or lower power, offroad creeps up on you.
fixed_t smonitoroffroad = min(FRACUNIT, K_SMonitorGradient(player->smonitortimer));
fixed_t subtrahend = FixedMul(smonitoroffroad, invinoffroad);
fixed_t fac = CLAMP(FRACUNIT - subtrahend, 0, FRACUNIT);
return FixedMul(offroad, fac);
}
@ -1452,6 +1456,11 @@ void K_SpawnNormalSpeedLines(player_t *player)
}
fast->colorized = true;
}
else if (player->smonitortimer)
{
fast->color = K_SMonitorColor(player->smonitortimer);
fast->colorized = true;
}
else if (player->tripwireLeniency)
{
// Make it pink+blue+big when you can go through tripwire
@ -2272,6 +2281,7 @@ tripwirepass_t K_TripwirePassConditions(const player_t *player)
{
if (
player->invincibilitytimer ||
player->smonitortimer ||
player->bubbleboost ||
K_IsAltShrunk(player) ||
(player->sneakertimer && player->realsneakertimer)
@ -2300,7 +2310,8 @@ boolean K_TripwirePass(const player_t *player)
boolean K_PlayerCanPunt(const player_t *player)
{
return player->invincibilitytimer > 0 || player->growshrinktimer > 0
return player->invincibilitytimer > 0 || player->smonitortimer > 0
|| player->growshrinktimer > 0
|| (player->flamestore > 0 && K_GetShieldFromPlayer(player) == KSHIELD_FLAME)
|| (player->bubbleblowup > 0 && K_GetShieldFromPlayer(player) == KSHIELD_BUBBLE)
|| K_AltShrinkArrowBulletCondition(player);
@ -2562,41 +2573,27 @@ static inline fixed_t K_GetSneakerBoostSpeed(void)
}
// Used to determine the speed and power of Invincibility.
fixed_t K_InvincibilityGradient(UINT16 time)
fixed_t K_SMonitorGradient(UINT16 time)
{
return (min(936, (fixed_t)time) * FRACUNIT / (6 * TICRATE));
}
UINT16 K_GetInvincibilityTime(player_t *player)
fixed_t K_GetSMonitorSpeed(UINT16 time)
{
return (!K_IsKartItemAlternate(KITEM_INVINCIBILITY)) ? BASEINVINTIME : MININVINTIME;
fixed_t t = min(FRACUNIT, K_SMonitorGradient(time));
return Easing_OutCubic(t, 0, SMNTRSPEEDBOOST);
}
fixed_t K_GetInvincibilitySpeed(UINT16 time)
fixed_t K_GetSMonitorAccel(UINT16 time)
{
if (!K_IsKartItemAlternate(KITEM_INVINCIBILITY))
return INVINSPEEDBOOSTCLS;
fixed_t t = min(FRACUNIT, K_InvincibilityGradient(time));
return Easing_OutCubic(t, 0, INVINSPEEDBOOSTALT);
fixed_t t = min(FRACUNIT, K_SMonitorGradient(time));
return Easing_OutCubic(t, 0, SMNTRACCELBOOST);
}
fixed_t K_GetInvincibilityAccel(UINT16 time)
fixed_t K_GetSMonitorHandling(UINT16 time)
{
if (!K_IsKartItemAlternate(KITEM_INVINCIBILITY))
return INVINACCELBOOSTCLS;
fixed_t t = min(FRACUNIT, K_InvincibilityGradient(time));
return Easing_OutCubic(t, 0, INVINACCELBOOSTALT);
}
fixed_t K_GetInvincibilityHandling(UINT16 time)
{
if (!K_IsKartItemAlternate(KITEM_INVINCIBILITY))
return INVINHANDLEBOOSTCLS;
fixed_t t = min(FRACUNIT, K_InvincibilityGradient(time));
return Easing_OutCubic(t, 0, INVINHANDLEBOOSTALT);
fixed_t t = min(FRACUNIT, K_SMonitorGradient(time));
return Easing_OutCubic(t, 0, SMNTRHANDLEBOOST);
}
static fixed_t diminish(fixed_t speedboost)
@ -2790,13 +2787,18 @@ static void K_GetKartBoostPower(player_t *player)
if (player->invincibilitytimer) // Invincibility
{
fixed_t invspeedboost = K_GetInvincibilitySpeed(player->invincibilitytimer);
fixed_t invaccelboost = K_GetInvincibilityAccel(player->invincibilitytimer);
fixed_t invhandleboost = K_GetInvincibilityHandling(player->invincibilitytimer);
// + 37.5% top speed, + 300% acceleration
K_DoBoost(player, INVINSPEEDBOOST, INVINACCELBOOST, INVINHANDLEBOOST, INVINSTACKABLE, INVINSTACKABLE);
}
// Legacy: + 37.5% top speed, + 300% acceleration
// Alternative: + ???% top speed, + ???% acceleration
K_DoBoost(player, invspeedboost, invaccelboost, invhandleboost, INVINSTACKABLE, INVINSTACKABLE);
if (player->smonitortimer) // S-Monitor
{
fixed_t smntrspeedboost = K_GetSMonitorSpeed(player->smonitortimer);
fixed_t smntraccelboost = K_GetSMonitorAccel(player->smonitortimer);
fixed_t smntrhandleboost = K_GetSMonitorHandling(player->smonitortimer);
// + ???% top speed, + ???% acceleration
K_DoBoost(player, smntrspeedboost, smntraccelboost, smntrhandleboost, SMNTRSTACKABLE, SMNTRSTACKABLE);
}
if (player->growshrinktimer > 0) // Grow
@ -5029,6 +5031,8 @@ void K_DoPogoSpring(mobj_t *mo, fixed_t vertispeed, UINT8 sound)
thrust = FixedMul(thrust, 5*FRACUNIT/4);
else if (mo->player->invincibilitytimer)
thrust = FixedMul(thrust, 9*FRACUNIT/8);
else if (mo->player->smonitortimer)
thrust = FixedMul(thrust, 9*FRACUNIT/8);
else if (mo->player->flamestore)
thrust = FixedMul(thrust, 9*FRACUNIT/8);
}
@ -5072,42 +5076,49 @@ boolean K_PlayFullInvinTheme(void)
void K_DoInvincibility(player_t *player, tic_t time)
{
const boolean isalt = K_IsKartItemAlternate(KITEM_INVINCIBILITY);
if (!player->invincibilitytimer)
{
mobj_t *overlay = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_INVULNFLASH);
P_SetTarget(&overlay->target, player->mo);
overlay->destscale = player->mo->scale;
P_SetScale(overlay, player->mo->scale);
if (isalt)
{
mobj_t *aura = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_OVERLAY);
P_SetTarget(&aura->target, player->mo);
aura->destscale = player->mo->scale;
P_SetScale(aura, player->mo->scale);
aura->extravalue2 = 1;
}
}
if (isalt)
player->invincibilitytimer = time;
if (P_IsLocalPlayer(player) == true)
{
S_ChangeMusicSpecial(K_PlayFullInvinTheme() ? "kinvnf" : "kinvnc");
}
else //used to be "if (P_IsDisplayPlayer(player) == false)"
{
S_StartSound(player->mo, (cv_kartinvinsfx.value ? sfx_alarmi : sfx_kinvnc));
}
P_RestoreMusic(player);
}
void K_DoSMonitor(player_t *player, tic_t time)
{
if (!player->smonitortimer)
{
mobj_t *aura = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_OVERLAY);
P_SetTarget(&aura->target, player->mo);
aura->destscale = player->mo->scale;
P_SetScale(aura, player->mo->scale);
aura->extravalue2 = 1;
}
// Rim suggestion: Don't allow already invincible players to chain.
if (K_SMonitorGradient(player->smonitortimer) < (FRACUNIT/2))
{
// Rim suggestion: Don't allow already invincible players to chain.
if (K_InvincibilityGradient(player->invincibilitytimer) < (FRACUNIT/2))
{
// Be nice to players at half-power or less.
player->invincibilitytimer = max(player->invincibilitytimer, time);
}
}
else
{
player->invincibilitytimer = time;
// Be nice to players at half-power or less.
player->smonitortimer = max(player->smonitortimer, time);
}
player->maxinvincibilitytime = player->invincibilitytimer;
player->invincibilitybottleneck = 0;
player->invincibilitywarning = 0;
player->maxsmonitortime = player->smonitortimer;
player->smonitorexpiring = 0;
player->smonitorwarning = 0;
if (P_IsLocalPlayer(player) == true)
{
@ -6391,9 +6402,13 @@ static void K_UpdateInvincibilitySounds(player_t *player)
if (player->mo->health > 0)
{
const boolean invinsound =
((player->invincibilitytimer) ||
((player->smonitortimer > 0) && (!player->smonitorwarning)));
if (player->growshrinktimer > 0 && (!localplayer || cv_growmusic.value == 2)) // Prioritize Grow
sfxnum = cv_kartinvinsfx.value ? sfx_alarmg : sfx_kgrow;
else if ((player->invincibilitytimer > 0) && (!player->invincibilitywarning) && (!localplayer || cv_supermusic.value == 2))
else if (invinsound && (!localplayer || cv_supermusic.value == 2))
sfxnum = cv_kartinvinsfx.value ? sfx_alarmi : sfx_kinvnc;
// FIXME: Does Alt. Shrink need an alarm?
}
@ -7433,10 +7448,11 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
player->dashRainbowPogo--;
if (player->invincibilitytimer)
player->invincibilitytimer--;
if (player->smonitortimer)
{
INT16 invinfac = 1;
if ((K_IsKartItemAlternate(KITEM_INVINCIBILITY)) &&
(player->invincibilitytimer > 2))
if (player->smonitortimer > 2)
{
INT16 pingame = 0;
INT32 i;
@ -7452,46 +7468,44 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
continue;
}
// Value to subtract from the Invincibility timer.
INT16 invin_subtrahend = (pingame > 1) ? 2 : 1;
// Value to subtract from the S-Monitor timer.
INT16 smonitor_subtrahend = (pingame > 1) ? 2 : 1;
if ((player->distancefromcluster > 0) && (!player->invincibilitybottleneck) && (pingame > 1))
if ((player->distancefromcluster > 0) && (!player->smonitorexpiring) && (pingame > 1))
{
// Don't subtract shit until we get past the cluster player.
invin_subtrahend = 0;
smonitor_subtrahend = 0;
}
player->invincibilitytimer = (UINT16)(max(
2, (INT32)(player->invincibilitytimer) - invin_subtrahend));
player->smonitortimer = (UINT16)(max(
2, (INT32)(player->smonitortimer) - smonitor_subtrahend));
const boolean warning_cond_standard =
(K_InvincibilityGradient(player->invincibilitytimer) < INVINWARNINGTIME);
const boolean warning_cond = warning_cond_standard;
const boolean warning_cond =
(K_SMonitorGradient(player->smonitortimer) < SMONITORWARNINGTIME);
if (warning_cond)
{
if (!player->invincibilitywarning)
if (!player->smonitorwarning)
{
S_StartSound(player->mo, sfx_cdfm71);
player->invincibilitywarning = 1;
player->invincibilitybottleneck = 1;
player->smonitorwarning = 1;
player->smonitorexpiring = 1;
}
}
}
else
{
player->invincibilitytimer--;
player->invincibilitybottleneck = 0;
player->invincibilitywarning = 0;
player->smonitortimer--;
player->smonitorexpiring = 0;
player->smonitorwarning = 0;
}
}
else
{
player->invincibilitybottleneck = 0; // No need for bottlenecking.
player->maxinvincibilitytime = 0;
player->invincibilitywarning = 0;
player->invincibilitycancel = -1;
player->smonitorexpiring = 0; // No need for bottlenecking.
player->maxsmonitortime = 0;
player->smonitorwarning = 0;
player->smonitorcancel = -1;
}
if (player->checkskip)
@ -7852,11 +7866,8 @@ static void K_KartResetPlayerFullbright(player_t *player)
if (player->invincibilitytimer || player->powers[pw_invulnerability]) // You're gonna kiiiiill
{
if (!K_IsKartItemAlternate(KITEM_INVINCIBILITY))
{
fullbright = true;
goto finalise;
}
fullbright = true;
goto finalise;
}
if (player->growshrinktimer) // Ditto, for grow/shrink
@ -7937,24 +7948,14 @@ void K_KartResetPlayerColor(player_t *player)
if (player->invincibilitytimer || player->powers[pw_invulnerability]) // You're gonna kiiiiill
{
boolean skip = false;
player->mo->color = K_RainbowColor(leveltime / 2);
if (!K_IsKartItemAlternate(KITEM_INVINCIBILITY))
if (player->invincibilitytimer)
{
player->mo->color = K_RainbowColor(leveltime / 2);
if (player->invincibilitytimer)
{
player->mo->colorized = true;
}
skip = true;
}
if (skip)
{
return;
player->mo->colorized = true;
}
return;
}
if (player->growshrinktimer) // Ditto, for grow/shrink
@ -9184,7 +9185,7 @@ INT16 K_GetKartTurnValue(const player_t *player, INT16 turnvalue)
if (K_SlipdashActive() && K_Sliptiding(player)) // slight handling boost based on weight
turnvalue = FixedMul(turnvalue, FRACUNIT + (10 - player->kartweight)*FRACUNIT/48);
if ((player->invincibilitytimer && (!K_IsKartItemAlternate(KITEM_INVINCIBILITY)))
if (player->invincibilitytimer
|| player->sneakertimer || player->bubbleboost ||
player->growshrinktimer > 0 || K_IsAltShrunk(player))
{
@ -9268,17 +9269,11 @@ static void K_SpawnDriftEFX(player_t *player, SINT8 level)
}
}
// Sliptide conditions for Alternative Invincibility.
static boolean K_AltInvinSliptideCondition(player_t *player)
// Sliptide conditions for the S-Monitor.
static boolean K_SMonitorSliptideCondition(player_t *player)
{
if (!K_IsKartItemAlternate(KITEM_INVINCIBILITY))
{
// Not in Alternative, hit the bricks!
return false;
}
// Allow sliptides if you're above half power.
return (K_InvincibilityGradient(player->invincibilitytimer) > (FRACUNIT/2));
return (K_SMonitorGradient(player->smonitortimer) > (FRACUNIT/2));
}
fixed_t K_GetSpeedPercentage(const player_t *player)
@ -9326,7 +9321,7 @@ boolean K_InterceptArrowBullet(player_t *player)
if (!player)
return false;
return ((player->invincibilitytimer && !K_IsKartItemAlternate(KITEM_INVINCIBILITY)) || (player->growshrinktimer > 0) || (player->flamestore >= FLAMESTOREMAX-1));
return ((player->invincibilitytimer) || (player->growshrinktimer > 0) || (player->flamestore >= FLAMESTOREMAX-1));
}
// 0.25 fracunits
@ -9340,7 +9335,7 @@ static boolean K_OtherSliptideCondition(player_t* player)
return false;
return (
K_AltInvinSliptideCondition(player) || K_AltShrinkSliptideCondition(player) ||
K_SMonitorSliptideCondition(player) || K_AltShrinkSliptideCondition(player) ||
(cv_handleboostslip.value && (player->handleboost >= HANDLEBOOSTTHRESHOLD)));
}
@ -10610,8 +10605,10 @@ void K_StripOther(player_t *player)
player->roulettetype = KROULETTETYPE_NORMAL;
player->invincibilitytimer = 0;
player->invincibilitywarning = 0;
player->invincibilitycancel = -1;
player->smonitortimer = 0;
player->smonitorwarning = 0;
player->smonitorcancel = -1;
if (player->growshrinktimer)
{
@ -10841,12 +10838,9 @@ INT32 K_GetShieldFromItem(INT32 item)
}
}
static boolean K_InvincibilitySlotHogging(player_t *player)
static boolean K_SMonitorSlotHogging(player_t *player)
{
if (!K_IsKartItemAlternate(KITEM_INVINCIBILITY))
return false;
return ((player->invincibilitytimer) != 0);
return ((player->smonitortimer) != 0);
}
//
@ -10885,7 +10879,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|| player->itemroulette
|| player->rocketsneakertimer
|| player->eggmanexplode
|| ((K_IsKartItemAlternate(KITEM_INVINCIBILITY)) && player->invincibilitytimer)
|| player->smonitortimer
|| (player->growshrinktimer > 0)
|| player->flametimer
|| (leveltime < starttime)
@ -11090,30 +11084,30 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
player->growcancel = 0;
}
}
// Invincibility
else if (K_InvincibilitySlotHogging(player))
// S-Monitor
else if (K_SMonitorSlotHogging(player))
{
// (Alternate) Cancel Invincibility
if (player->invincibilitycancel >= 0)
// Cancel the S-Monitor
if (player->smonitorcancel >= 0)
{
if (buttons & BT_ATTACK)
{
player->invincibilitycancel++;
if (player->invincibilitycancel > 25)
player->smonitorcancel++;
if (player->smonitorcancel > 25)
{
// Don't fully cancel due to how the music handling works.
player->invincibilitytimer = 1;
player->smonitortimer = 1;
}
}
else
player->invincibilitycancel = 0;
player->smonitorcancel = 0;
}
else
{
if ((buttons & BT_ATTACK) || (player->oldcmd.buttons & BT_ATTACK))
player->invincibilitycancel = -1;
player->smonitorcancel = -1;
else
player->invincibilitycancel = 0;
player->smonitorcancel = 0;
}
}
else if (player->itemamount == 0)

View file

@ -28,8 +28,8 @@ If states are ever added or removed
Make sure this matches the actual number of states
*/
#define KART_NUMINVSPARKLESANIM 12
#define BASEINVINTIME (10 * TICRATE)
#define MININVINTIME (7 * TICRATE)
#define INVINTIME (10 * TICRATE)
#define SMONITORTIME (7 * TICRATE)
#define GROW_SCALE ((3*FRACUNIT)/2)
#define SHRINK_SCALE ((6*FRACUNIT)/8)
@ -85,14 +85,8 @@ extern vector3_t clusterpoint, clusterdtf;
// Bump weight for a Bubble Shield
#define BUBBLEMINWEIGHT (5 * FRACUNIT)
// Invincibility-related constants
#define INVINDIST CV_Get(&cv_kartinvindist)
#define INVINDISTMUL CV_Get(&cv_kartinvindistmul)
#define INVINMIDTIME CV_Get(&cv_kartinvin_midtime)
#define INVINMAXTIME CV_Get(&cv_kartinvin_maxtime)
// 1.22 * FRACUNIT
#define LEGACYALTINVINMUL (122 * FRACUNIT / 100)
// S-Monitor distance value
#define SMONITORDIST CV_Get(&cv_kartsmonitordist)
// Precalculated constants for stacked boost diminishing
// *Somewhat* matches old calc but doesn't use arrays, which makes it faster and more memory efficent
@ -114,14 +108,16 @@ extern vector3_t clusterpoint, clusterdtf;
#define SEPARATEPANELS CV_Get(&cv_kartstacking_panel_separate)
#define MAXPANELSTACK CV_Get(&cv_kartstacking_panel_maxgrade)
#define INVINSPEEDBOOSTCLS CV_Get(&cv_kartstacking_invincibility_classicspeedboost)
#define INVINACCELBOOSTCLS CV_Get(&cv_kartstacking_invincibility_classicaccelboost)
#define INVINHANDLEBOOSTCLS CV_Get(&cv_kartstacking_invincibility_classichandleboost)
#define INVINSPEEDBOOSTALT CV_Get(&cv_kartstacking_invincibility_alternatespeedboost)
#define INVINACCELBOOSTALT CV_Get(&cv_kartstacking_invincibility_alternateaccelboost)
#define INVINHANDLEBOOSTALT CV_Get(&cv_kartstacking_invincibility_alternatehandleboost)
#define INVINSPEEDBOOST CV_Get(&cv_kartstacking_invincibility_speedboost)
#define INVINACCELBOOST CV_Get(&cv_kartstacking_invincibility_accelboost)
#define INVINHANDLEBOOST CV_Get(&cv_kartstacking_invincibility_handleboost)
#define INVINSTACKABLE CV_Get(&cv_kartstacking_invincibility_stackable)
#define SMNTRSPEEDBOOST CV_Get(&cv_kartstacking_smonitor_speedboost)
#define SMNTRACCELBOOST CV_Get(&cv_kartstacking_smonitor_accelboost)
#define SMNTRHANDLEBOOST CV_Get(&cv_kartstacking_smonitor_handleboost)
#define SMNTRSTACKABLE CV_Get(&cv_kartstacking_smonitor_stackable)
#define GROWSPEEDBOOST CV_Get(&cv_kartstacking_grow_speedboost)
#define GROWACCELBOOST CV_Get(&cv_kartstacking_grow_accelboost)
#define GROWHANDLEBOOST CV_Get(&cv_kartstacking_grow_handleboost)
@ -276,12 +272,12 @@ void K_ResetPogoSpring(player_t *player);
extern boolean forcefullinvintheme;
boolean K_PlayFullInvinTheme(void);
void K_DoInvincibility(player_t *player, tic_t time);
#define INVINWARNINGTIME (fixed_t)(0.833333333f * (float)(FRACUNIT))
fixed_t K_InvincibilityGradient(UINT16 time);
UINT16 K_GetInvincibilityTime(player_t *player);
fixed_t K_GetInvincibilitySpeed(UINT16 time);
fixed_t K_GetInvincibilityAccel(UINT16 time);
fixed_t K_GetInvincibilityHandling(UINT16 time);
void K_DoSMonitor(player_t *player, tic_t time);
#define SMONITORWARNINGTIME (fixed_t)(0.833333333f * (float)(FRACUNIT))
fixed_t K_SMonitorGradient(UINT16 time);
fixed_t K_GetSMonitorSpeed(UINT16 time);
fixed_t K_GetSMonitorAccel(UINT16 time);
fixed_t K_GetSMonitorHandling(UINT16 time);
void K_KillBananaChain(mobj_t *banana, mobj_t *inflictor, mobj_t *source);
void K_UpdateHnextList(player_t *player, boolean clean);

View file

@ -4518,30 +4518,39 @@ static int lib_kGetNewSpeed(lua_State *L)
return 1;
}
static int lib_kInvincibilityGradient(lua_State *L)
static int lib_kSMonitorGradient(lua_State *L)
{
UINT16 time = (UINT16)luaL_checkinteger(L, 1);
// HUDSAFE
lua_pushinteger(L, K_InvincibilityGradient(time));
lua_pushinteger(L, K_SMonitorGradient(time));
return 1;
}
static int lib_kGetInvincibilitySpeed(lua_State *L)
static int lib_kGetSMonitorSpeed(lua_State *L)
{
UINT16 time = (UINT16)luaL_checkinteger(L, 1);
// HUDSAFE
lua_pushinteger(L, K_GetInvincibilitySpeed(time));
lua_pushinteger(L, K_GetSMonitorSpeed(time));
return 1;
}
static int lib_kGetInvincibilityAccel(lua_State *L)
static int lib_kGetSMonitorAccel(lua_State *L)
{
UINT16 time = (UINT16)luaL_checkinteger(L, 1);
// HUDSAFE
lua_pushinteger(L, K_GetInvincibilityAccel(time));
lua_pushinteger(L, K_GetSMonitorAccel(time));
return 1;
}
static int lib_kGetSMonitorHandling(lua_State *L)
{
UINT16 time = (UINT16)luaL_checkinteger(L, 1);
// HUDSAFE
lua_pushinteger(L, K_GetSMonitorHandling(time));
return 1;
}
@ -5774,9 +5783,10 @@ static luaL_Reg lib[] = {
{"K_BoostChain",lib_kBoostChain},
{"K_ChainOrDeincrementTime",lib_kChainOrDeincrementTime},
{"K_GetNewSpeed", lib_kGetNewSpeed},
{"K_InvincibilityGradient", lib_kInvincibilityGradient},
{"K_GetInvincibilitySpeed", lib_kGetInvincibilitySpeed},
{"K_GetInvincibilityAccel", lib_kGetInvincibilityAccel},
{"K_SMonitorGradient", lib_kSMonitorGradient},
{"K_GetSMonitorSpeed", lib_kGetSMonitorSpeed},
{"K_GetSMonitorAccel", lib_kGetSMonitorAccel},
{"K_GetSMonitorHandling", lib_kGetSMonitorHandling},
{"K_3dKartMovement", lib_k3dKartMovement},
{"K_MomentumAngle", lib_kMomentumAngle},
{"K_GetKartSpeedFromStat", lib_kGetKartSpeedFromStat},

View file

@ -415,10 +415,11 @@ static int lib_lenLocalplayers(lua_State *L)
X(arrowbullet) \
X(rocketsneakertimer) \
X(invincibilitytimer) \
X(maxinvincibilitytime) \
X(invincibilitybottleneck) \
X(invincibilitycancel) \
X(invincibilitywarning) \
X(smonitortimer) \
X(maxsmonitortime) \
X(smonitorexpiring) \
X(smonitorcancel) \
X(smonitorwarning) \
X(eggmanexplode) \
X(eggmanblame) \
X(bananadrag) \
@ -906,18 +907,21 @@ static int player_get(lua_State *L)
case player_invincibilitytimer:
lua_pushinteger(L, plr->invincibilitytimer);
break;
case player_maxinvincibilitytime:
lua_pushinteger(L, plr->maxinvincibilitytime);
case player_smonitortimer:
lua_pushinteger(L, plr->smonitortimer);
break;
case player_invincibilitybottleneck:
case player_maxsmonitortime:
lua_pushinteger(L, plr->maxsmonitortime);
break;
case player_smonitorexpiring:
// Push as an INT16 due to the negative value signal systems.
lua_pushinteger(L, (INT16)plr->invincibilitybottleneck);
lua_pushinteger(L, (INT16)plr->smonitorexpiring);
break;
case player_invincibilitycancel:
lua_pushinteger(L, plr->invincibilitycancel);
case player_smonitorcancel:
lua_pushinteger(L, plr->smonitorcancel);
break;
case player_invincibilitywarning:
lua_pushboolean(L, (boolean)plr->invincibilitywarning);
case player_smonitorwarning:
lua_pushboolean(L, (boolean)plr->smonitorwarning);
break;
case player_eggmanexplode:
lua_pushinteger(L, plr->eggmanexplode);
@ -1699,20 +1703,23 @@ static int player_set(lua_State *L)
case player_invincibilitytimer:
plr->invincibilitytimer = luaL_checkinteger(L, 3);
break;
case player_maxinvincibilitytime:
case player_smonitortimer:
plr->smonitortimer = luaL_checkinteger(L, 3);
break;
case player_maxsmonitortime:
{
UINT16 maxinv = max(1, (UINT16)luaL_checkinteger(L, 3)); // Prevent zero-divides
plr->maxinvincibilitytime = maxinv;
plr->maxsmonitortime = maxinv;
break;
}
case player_invincibilitybottleneck:
plr->invincibilitybottleneck = (UINT16)luaL_checkinteger(L, 3);
case player_smonitorexpiring:
plr->smonitorexpiring = (UINT16)luaL_checkinteger(L, 3);
break;
case player_invincibilitycancel:
plr->invincibilitycancel = (INT16)luaL_checkinteger(L, 3);
case player_smonitorcancel:
plr->smonitorcancel = (INT16)luaL_checkinteger(L, 3);
break;
case player_invincibilitywarning:
plr->invincibilitywarning = (UINT8)luaL_checkboolean(L, 3);
case player_smonitorwarning:
plr->smonitorwarning = (UINT8)luaL_checkboolean(L, 3);
break;
case player_eggmanexplode:
plr->eggmanexplode = luaL_checkinteger(L, 3);

View file

@ -11632,6 +11632,7 @@ void A_RandomShadowFrame(void *thing)
{
if (actor->target && !actor->target->player->flashing
&& !actor->target->player->invincibilitytimer
&& !actor->target->player->smonitortimer
&& !actor->target->player->growshrinktimer
&& !K_IsPlayerDamaged(actor->target->player)
&& P_IsObjectOnGround(actor->target)
@ -11678,6 +11679,7 @@ void A_RoamingShadowThinker(void *thing)
{
if (actor->target && !actor->target->player->flashing
&& !actor->target->player->invincibilitytimer
&& !actor->target->player->smonitortimer
&& !actor->target->player->growshrinktimer
&& !K_IsPlayerDamaged(actor->target->player))
{
@ -11936,6 +11938,7 @@ void A_ReaperThinker(void *thing)
{
if (!(actor->target == targetplayermo && actor->target && !actor->target->player->flashing
&& !actor->target->player->invincibilitytimer
&& !actor->target->player->smonitortimer
&& !actor->target->player->growshrinktimer
&& !K_IsPlayerDamaged(actor->target->player)))
P_SetTarget(&actor->target, actor->hnext);

View file

@ -83,6 +83,7 @@ boolean P_CanPickupItem(player_t *player, UINT8 weapon)
|| K_IsPlayerDamaged(player)
|| player->squishedtimer > 0
|| player->invincibilitytimer > 0
|| player->smonitortimer > 0
|| player->growshrinktimer > 0
|| player->hyudorotimer > 0
|| player->flametimer > 0)
@ -99,7 +100,7 @@ boolean P_CanPickupItem(player_t *player, UINT8 weapon)
if (player->stealingtimer || player->stolentimer
|| player->rocketsneakertimer
|| player->eggmanexplode
|| ((K_IsKartItemAlternate(KITEM_INVINCIBILITY)) && (player->invincibilitytimer))
|| player->smonitortimer
|| (player->growshrinktimer > 0)
|| player->flametimer)
return false;
@ -491,7 +492,10 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
return;
}
if (player->invincibilitytimer > 0 || player->growshrinktimer > 0 || player->hyudorotimer > 0)
if (player->invincibilitytimer > 0 ||
player->smonitortimer > 0 ||
player->growshrinktimer > 0 ||
player->hyudorotimer > 0)
{
//player->flashing = 0;
K_DropHnextList(player);
@ -2199,7 +2203,10 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
}
}
if (player->invincibilitytimer > 0 || player->growshrinktimer > 0 || player->hyudorotimer > 0)
if (player->invincibilitytimer > 0 ||
player->smonitortimer > 0 ||
player->growshrinktimer > 0 ||
player->hyudorotimer > 0)
{
// Full invulnerability
K_DoInstashield(player);

View file

@ -6863,7 +6863,7 @@ static void P_RemoveOverlay(mobj_t *thing)
}
}
static UINT32 P_GetTranslucencyForInvincibility(mobj_t *mo)
static UINT32 P_GetTranslucencyForSMonitor(mobj_t *mo)
{
if (!mo->player)
{
@ -6871,16 +6871,16 @@ static UINT32 P_GetTranslucencyForInvincibility(mobj_t *mo)
return 0;
}
return max(0, min(9, 10 - FixedMul(10, K_InvincibilityGradient(mo->player->invincibilitytimer))))<<RF_TRANSSHIFT;
return max(0, min(9, 10 - FixedMul(10, K_SMonitorGradient(mo->player->smonitortimer))))<<RF_TRANSSHIFT;
}
// Called only when MT_OVERLAY thinks.
static void P_PlayerInvincibilityOverlay(mobj_t *thing)
static void P_PlayerSMonitorOverlay(mobj_t *thing)
{
I_Assert(thing->target != NULL);
I_Assert(thing->target->player != NULL);
if (!thing->target->player->invincibilitytimer)
if (!thing->target->player->smonitortimer)
{
P_SetTarget(&thing->target, NULL);
@ -6896,7 +6896,7 @@ static void P_PlayerInvincibilityOverlay(mobj_t *thing)
thing->z += thing->target->height - thing->height;
}
thing->color = K_AltInvincibilityColor(leveltime / 2);
thing->color = K_SMonitorColor(leveltime / 2);
thing->colorized = true;
thing->dispoffset = min(2, thing->target->dispoffset + 1);
@ -6908,7 +6908,7 @@ static void P_PlayerInvincibilityOverlay(mobj_t *thing)
thing->sprite2 = thing->target->sprite2;
thing->frame = thing->target->frame | FF_FULLBRIGHT | FF_ADD;
thing->tics = -1;
thing->renderflags = (thing->target->renderflags & ~RF_TRANSMASK)|P_GetTranslucencyForInvincibility(thing->target);
thing->renderflags = (thing->target->renderflags & ~RF_TRANSMASK)|P_GetTranslucencyForSMonitor(thing->target);
thing->skin = thing->target->skin;
thing->standingslope = thing->target->standingslope;
@ -7275,8 +7275,8 @@ static void P_MobjSceneryThink(mobj_t *mobj)
P_AddOverlay(mobj);
if ((mobj->extravalue2) && (mobj->target->player))
{
// Could be overlaying an invincible player.
P_PlayerInvincibilityOverlay(mobj);
// Could be overlaying a player using an S-Monitor.
P_PlayerSMonitorOverlay(mobj);
}
}
break;
@ -8501,7 +8501,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
K_UpdateMobjItemOverlay(part, mobj->threshold, mobj->movecount);
if (mobj->threshold == KITEM_INVINCIBILITY)
if ((mobj->threshold == KITEM_INVINCIBILITY) || (mobj->threshold == KITEM_S_MONITOR))
mobj->color = K_RainbowColor(leveltime);
}
break;
@ -9043,7 +9043,11 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
mobj->colorized = mobj->target->colorized;
break;
case MT_INVULNFLASH:
if (!mobj->target || !mobj->target->health || (mobj->target->player && !mobj->target->player->invincibilitytimer))
if (!mobj->target ||
!mobj->target->health ||
(mobj->target->player &&
!(mobj->target->player->invincibilitytimer ||
mobj->target->player->smonitortimer)))
{
P_RemoveMobj(mobj);
return false;

View file

@ -755,10 +755,11 @@ static void P_NetSyncPlayers(savebuffer_t *save)
SYNC(players[i].rocketsneakertimer);
SYNC(players[i].invincibilitytimer);
SYNC(players[i].maxinvincibilitytime);
SYNC(players[i].invincibilitybottleneck);
SYNC(players[i].invincibilitycancel);
SYNC(players[i].invincibilitywarning);
SYNC(players[i].smonitortimer);
SYNC(players[i].maxsmonitortime);
SYNC(players[i].smonitorexpiring);
SYNC(players[i].smonitorcancel);
SYNC(players[i].smonitorwarning);
SYNC(players[i].eggmanexplode);
SYNC(players[i].eggmanblame);

View file

@ -9338,7 +9338,8 @@ boolean P_AllowFriction(mobj_t *mobj)
|| (cv_kartbubble_boost_offroadignore.value && mobj->player->bubbleboost)
|| mobj->player->growshrinktimer > 0
|| mobj->player->flamestore
|| K_IsAltShrunk(mobj->player))
|| K_IsAltShrunk(mobj->player)
|| mobj->player->smonitortimer)
return false;
return true;

View file

@ -833,8 +833,10 @@ void P_RestoreMusic(player_t *player)
{ \
if (players[p].growshrinktimer > bestlocaltimer) \
{ wantedmus = 1; bestlocaltimer = players[p].growshrinktimer; } \
else if ((players[p].invincibilitytimer > bestlocaltimer) && (!players[p].invincibilitywarning))\
else if (players[p].invincibilitytimer > bestlocaltimer) \
{ wantedmus = 2; bestlocaltimer = players[p].invincibilitytimer; } \
else if ((players[p].smonitortimer > bestlocaltimer) && (!players[p].smonitorwarning))\
{ wantedmus = 2; bestlocaltimer = players[p].smonitortimer; } \
else if ((K_IsKartItemAlternate(KITEM_SHRINK)) && \
(K_GetShrinkTime(&players[p]) > bestlocaltimer)) \
{ wantedmus = 3; bestlocaltimer = K_GetShrinkTime(&players[p]); } \
@ -853,7 +855,9 @@ void P_RestoreMusic(player_t *player)
{
if (player->growshrinktimer > 1)
wantedmus = 1;
else if ((player->invincibilitytimer > 1) && (!player->invincibilitywarning))
else if (player->invincibilitytimer)
wantedmus = 2;
else if (((player->smonitortimer > 1) && (!player->smonitorwarning)))
wantedmus = 2;
else if ((K_IsKartItemAlternate(KITEM_SHRINK)) &&
(K_GetShrinkTime(player) > 1))
@ -1802,12 +1806,16 @@ static void P_CheckQuicksand(player_t *player)
//
static void P_CheckInvincibilityTimer(player_t *player)
{
if (!player->powers[pw_invulnerability] && !player->invincibilitytimer)
if (!player->powers[pw_invulnerability] && !player->invincibilitytimer && !player->smonitortimer)
return;
// Resume normal music stuff.
if (player->invincibilitytimer == 1 || player->powers[pw_invulnerability] == 1)
if (player->invincibilitytimer == 1
|| player->powers[pw_invulnerability] == 1
|| (player->smonitorwarning == 1))
{
player->smonitorwarning = 2; // Don't attempt to restore music again.
//K_KartResetPlayerColor(player); -- this gets called every tic anyways
G_GhostAddColor((INT32) (player - players), GHC_NORMAL);
@ -2321,10 +2329,10 @@ void P_MovePlayer(player_t *player)
&& onground && (leveltime & 1))
K_SpawnBoostTrail(player);
if ((player->invincibilitytimer > 0) &&
(((leveltime %
max(1, 10 - FixedMul(9, K_InvincibilityGradient(player->invincibilitytimer)))) == 0) ||
(!K_IsKartItemAlternate(KITEM_INVINCIBILITY))))
if (((player->invincibilitytimer) ||
((player->smonitortimer > 0) &&
(((leveltime %
max(1, 10 - FixedMul(9, K_SMonitorGradient(player->smonitortimer)))) == 0)))))
{
K_SpawnSparkleTrail(player->mo);
}
@ -4392,7 +4400,13 @@ void P_PlayerThink(player_t *player)
P_CheckInvincibilityTimer(player); // Spawn Invincibility Sparkles
// "Blur" a bit when you have speed shoes and are going fast enough
if ((player->powers[pw_super] || player->powers[pw_sneakers] || player->driftboost || player->sneakertimer || player->startboost || player->ringboost) && !player->invincibilitytimer // SRB2kart
if ((player->powers[pw_super]
|| player->powers[pw_sneakers]
|| player->driftboost
|| player->sneakertimer
|| player->startboost
|| player->ringboost)
&& !(player->invincibilitytimer || player->smonitortimer) // SRB2kart
&& (player->speed + abs(player->mo->momz)) > FixedMul(20*FRACUNIT,player->mo->scale))
{
UINT8 i;
@ -4652,7 +4666,7 @@ void P_ForceLocalAngle(player_t *player, angle_t angle, boolean interp)
boolean P_PlayerFullbright(player_t *player)
{
return (player->invincibilitytimer > 0);
return (player->invincibilitytimer > 0) || (player->smonitortimer > 0);
}
void P_ResetPlayerCheats(void)

View file

@ -46,7 +46,7 @@ patch_t *Patch_GetRotatedSprite(
void *info, INT32 rotationangle);
INT32 R_GetRollAngle(angle_t rollangle);
boolean R_IsOverlayingInvinciblePlayer(mobj_t* mobj);
boolean R_IsOverlayingSMonitorPlayer(mobj_t* mobj);
angle_t R_GetPitchRollAngle(mobj_t *mobj, player_t *viewPlayer, interpmobjstate_t *interp);
angle_t R_ModelRotationAngle(mobj_t *mobj, player_t *viewPlayer);
angle_t R_SpriteRotationAngle(mobj_t *mobj, player_t *viewPlayer, interpmobjstate_t *interp);

View file

@ -89,12 +89,12 @@ static angle_t R_PlayerSpriteRotation(player_t *player, player_t *viewPlayer)
return rollAngle;
}
// Hacky boolean to check if we're the rainbow Invincibility overlay
boolean R_IsOverlayingInvinciblePlayer(mobj_t* mobj)
// Hacky boolean to check if we're the rainbow S-Monitor overlay
boolean R_IsOverlayingSMonitorPlayer(mobj_t* mobj)
{
return ((K_IsKartItemAlternate(KITEM_INVINCIBILITY)) && (mobj->type == MT_OVERLAY) &&
return ((mobj->type == MT_OVERLAY) &&
(mobj->target) && (mobj->extravalue2) && (mobj->target->player) &&
(mobj->target->player->invincibilitytimer));
(mobj->target->player->smonitortimer));
}
angle_t R_ModelRotationAngle(mobj_t *mobj, player_t *viewPlayer)
@ -105,7 +105,7 @@ angle_t R_ModelRotationAngle(mobj_t *mobj, player_t *viewPlayer)
{
rollAngle += R_PlayerSpriteRotation(mobj->player, viewPlayer);
}
else if (R_IsOverlayingInvinciblePlayer(mobj))
else if (R_IsOverlayingSMonitorPlayer(mobj))
{
rollAngle += R_PlayerSpriteRotation(mobj->target->player, viewPlayer);
}
@ -117,7 +117,7 @@ angle_t R_SpriteRotationAngle(mobj_t *mobj, player_t *viewPlayer, interpmobjstat
{
angle_t rollOrPitch;
if (R_IsOverlayingInvinciblePlayer(mobj))
if (R_IsOverlayingSMonitorPlayer(mobj))
{
rollOrPitch = R_GetPitchRollAngle(mobj->target, viewPlayer, interp);
}

View file

@ -922,7 +922,7 @@ UINT8 *R_GetSpriteTranslation(vissprite_t *vis)
if (!(vis->cut & SC_PRECIP) && vis->mobj->colorized)
{
return R_GetTranslationColormap(R_IsOverlayingInvinciblePlayer(vis->mobj) ? TC_BLINK : TC_RAINBOW,
return R_GetTranslationColormap(R_IsOverlayingSMonitorPlayer(vis->mobj) ? TC_BLINK : TC_RAINBOW,
static_cast<skincolornum_t>(vis->mobj->color),
GTC_CACHE);
}
@ -1838,7 +1838,7 @@ static void R_ProjectSprite(mobj_t *thing)
interpmobjstate_t interp = {0};
mobj_t *interptarg = thing;
if (R_IsOverlayingInvinciblePlayer(thing))
if (R_IsOverlayingSMonitorPlayer(thing))
{
// Kill overlay misalignment
interptarg = thing->target;