Tie Invincibility to clustering, implement new visual
* Invincibility's time limit is now directly tied to a player's distance from the cluster. * The visual for Invincibility has been reworked to being an additive layer over the player, fading out as you run out of Invincibility.
This commit is contained in:
parent
6788db93b4
commit
99056a47fa
11 changed files with 432 additions and 20 deletions
|
|
@ -462,7 +462,7 @@ consvar_t cv_kartstacking_sneaker_accelboost = CVAR_INIT ("vanillaboost_sneaker_
|
|||
consvar_t cv_kartstacking_sneaker_maxgrade = CVAR_INIT ("vanillaboost_sneaker_maxgrade", "3", CV_NETVAR|CV_CHEAT, CV_Natural, NULL);
|
||||
consvar_t cv_kartstacking_sneaker_stackable = CVAR_INIT ("vanillaboost_sneaker_stackable", "On", CV_NETVAR, CV_OnOff, NULL);
|
||||
|
||||
consvar_t cv_kartstacking_invincibility_speedboost = CVAR_INIT ("vanillaboost_invincibility_speedboost", "0.375", CV_NETVAR|CV_CHEAT|CV_FLOAT, CV_Unsigned, NULL);
|
||||
consvar_t cv_kartstacking_invincibility_speedboost = CVAR_INIT ("vanillaboost_invincibility_speedboost", "0.68", CV_NETVAR|CV_CHEAT|CV_FLOAT, CV_Unsigned, NULL);
|
||||
consvar_t cv_kartstacking_invincibility_accelboost = CVAR_INIT ("vanillaboost_invincibility_accelboost", "3.0", CV_NETVAR|CV_CHEAT|CV_FLOAT, CV_Unsigned, NULL);
|
||||
consvar_t cv_kartstacking_invincibility_stackable = CVAR_INIT ("vanillaboost_invincibility_stackable", "Off", CV_NETVAR, CV_OnOff, NULL);
|
||||
|
||||
|
|
@ -503,6 +503,12 @@ consvar_t cv_kartbumpspring = CVAR_INIT ("kartbumpspring", "No", CV_NETVAR, CV_Y
|
|||
|
||||
consvar_t cv_kartslipdash = CVAR_INIT ("kartslipdash", "No", CV_NETVAR|CV_CALL|CV_NOINIT, CV_YesNo, KartSlipdash_OnChange);
|
||||
|
||||
// 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", "8400", CV_NETVAR|CV_CHEAT, invindist_cons_t, NULL);
|
||||
|
||||
consvar_t cv_kartinvindistmul = CVAR_INIT ("kartinvindistmul", "0.54", CV_NETVAR|CV_CHEAT|CV_FLOAT, CV_Unsigned, NULL);
|
||||
|
||||
static CV_PossibleValue_t kartdebugitem_cons_t[] =
|
||||
{
|
||||
#define FOREACH( name, n ) { n, #name }
|
||||
|
|
@ -527,6 +533,7 @@ static CV_PossibleValue_t kartdebugwaypoint_cons_t[] = {{0, "Off"}, {1, "Forward
|
|||
consvar_t cv_kartdebugwaypoints = CVAR_INIT ("kartdebugwaypoints", "Off", CV_NETVAR|CV_CHEAT, kartdebugwaypoint_cons_t, NULL);
|
||||
consvar_t cv_kartdebuglap = CVAR_INIT ("kartdebuglap", "Off", CV_NETVAR|CV_CHEAT, kartdebugwaypoint_cons_t, NULL);
|
||||
consvar_t cv_kartdebugbot = CVAR_INIT ("kartdebugbot", "Off", CV_NETVAR|CV_CHEAT, CV_OnOff, NULL);
|
||||
consvar_t cv_kartdebugcluster = CVAR_INIT ("kartdebugcluster", "Off", CV_NETVAR|CV_CHEAT, CV_OnOff, NULL);
|
||||
|
||||
consvar_t cv_kartdebugcheckpoint = CVAR_INIT ("kartdebugcheckpoint", "Off", 0, CV_OnOff, NULL);
|
||||
consvar_t cv_kartdebugnodes = CVAR_INIT ("kartdebugnodes", "Off", 0, CV_OnOff, NULL);
|
||||
|
|
|
|||
|
|
@ -171,6 +171,8 @@ extern consvar_t cv_kartpurpledrift;
|
|||
extern consvar_t cv_kartbumpspark;
|
||||
extern consvar_t cv_kartbumpspring;
|
||||
extern consvar_t cv_kartslipdash;
|
||||
extern consvar_t cv_kartinvindist;
|
||||
extern consvar_t cv_kartinvindistmul;
|
||||
|
||||
extern consvar_t cv_encorevotes;
|
||||
|
||||
|
|
@ -179,7 +181,7 @@ extern consvar_t cv_votetime;
|
|||
extern consvar_t cv_kartdebugitem, cv_kartdebugamount, cv_kartdebugdistribution, cv_kartdebughuddrop;
|
||||
extern consvar_t cv_kartdebugshrink;
|
||||
extern consvar_t cv_kartdebugcheckpoint, cv_kartdebugnodes, cv_kartdebugcolorize, cv_kartdebugdirector;
|
||||
extern consvar_t cv_kartdebugwaypoints, cv_kartdebuglap, cv_kartdebugbot;
|
||||
extern consvar_t cv_kartdebugwaypoints, cv_kartdebuglap, cv_kartdebugbot, cv_kartdebugcluster;
|
||||
|
||||
extern consvar_t cv_itemfinder;
|
||||
|
||||
|
|
|
|||
|
|
@ -607,6 +607,7 @@ struct player_t
|
|||
UINT8 positiondelay; // Used for position number, so it can grow when passing/being passed
|
||||
UINT32 distancetofinish;
|
||||
UINT32 distancetofinishprev;
|
||||
UINT32 distancefromcluster;
|
||||
waypoint_t *currentwaypoint;
|
||||
waypoint_t *nextwaypoint;
|
||||
UINT16 bigwaypointgap;
|
||||
|
|
|
|||
|
|
@ -687,6 +687,11 @@ boolean K_SMKIceBlockCollide(mobj_t *t1, mobj_t *t2)
|
|||
return false;
|
||||
}
|
||||
|
||||
boolean K_CanInvincibilityDamage(UINT16 timer)
|
||||
{
|
||||
return (K_InvincibilityGradient(timer) > (FRACUNIT/2));
|
||||
}
|
||||
|
||||
boolean K_PvPTouchDamage(mobj_t *t1, mobj_t *t2)
|
||||
{
|
||||
const boolean flameT1 = ((t1->player->flamedash > 0) && (t1->player->flametimer > 0));
|
||||
|
|
@ -695,8 +700,8 @@ boolean K_PvPTouchDamage(mobj_t *t1, mobj_t *t2)
|
|||
boolean t1Condition = false;
|
||||
boolean t2Condition = false;
|
||||
|
||||
t1Condition = (t1->player->invincibilitytimer > 0);
|
||||
t2Condition = (t2->player->invincibilitytimer > 0);
|
||||
t1Condition = ((t1->player->invincibilitytimer > 0) && (K_CanInvincibilityDamage(t1->player->invincibilitytimer)));
|
||||
t2Condition = ((t2->player->invincibilitytimer > 0) && (K_CanInvincibilityDamage(t2->player->invincibilitytimer)));
|
||||
|
||||
if ((t1Condition == true || flameT1 == true) && (t2Condition == true || flameT2 == true))
|
||||
{
|
||||
|
|
|
|||
101
src/k_hud.c
101
src/k_hud.c
|
|
@ -1758,6 +1758,15 @@ static void K_DrawKartPositionNum(INT32 num)
|
|||
}
|
||||
}
|
||||
|
||||
static UINT32 K_InvincibilityHUDVisibility(UINT16 t)
|
||||
{
|
||||
UINT32 alphalevel = st_translucency;
|
||||
|
||||
alphalevel = min(10, FixedMul(alphalevel, K_InvincibilityGradient(t)));
|
||||
|
||||
return min(9, 10 - alphalevel)<<V_ALPHASHIFT;
|
||||
}
|
||||
|
||||
static boolean K_drawKartPositionFaces(void)
|
||||
{
|
||||
// FACE_X = 15; // 15
|
||||
|
|
@ -1769,6 +1778,7 @@ static boolean K_drawKartPositionFaces(void)
|
|||
INT32 rankplayer[MAXPLAYERS];
|
||||
INT32 bumperx, numplayersingame = 0;
|
||||
UINT8 *colormap;
|
||||
UINT32 invinchudtrans;
|
||||
|
||||
ranklines = 0;
|
||||
memset(completed, 0, sizeof (completed));
|
||||
|
|
@ -1855,6 +1865,7 @@ static boolean K_drawKartPositionFaces(void)
|
|||
if (players[rankplayer[i]].mo->color)
|
||||
{
|
||||
colormap = R_GetTranslationColormap(players[rankplayer[i]].skin, players[rankplayer[i]].mo->color, GTC_CACHE);
|
||||
|
||||
if (players[rankplayer[i]].mo->colorized)
|
||||
colormap = R_GetTranslationColormap(TC_RAINBOW, players[rankplayer[i]].mo->color, GTC_CACHE);
|
||||
else
|
||||
|
|
@ -1862,6 +1873,14 @@ static boolean K_drawKartPositionFaces(void)
|
|||
|
||||
V_DrawMappedPatch(FACE_X, Y, V_HUDTRANS|V_SNAPTOLEFT, faceprefix[players[rankplayer[i]].skin][FACE_RANK], colormap);
|
||||
|
||||
if (players[rankplayer[i]].invincibilitytimer)
|
||||
{
|
||||
colormap = R_GetTranslationColormap(TC_RAINBOW, K_RainbowColor(leveltime / 2), GTC_CACHE);
|
||||
invinchudtrans = K_InvincibilityHUDVisibility(players[rankplayer[i]].invincibilitytimer);
|
||||
|
||||
V_DrawMappedPatch(FACE_X, Y, invinchudtrans|V_SNAPTOLEFT|V_ADD, faceprefix[players[rankplayer[i]].skin][FACE_RANK], colormap);
|
||||
}
|
||||
|
||||
if (LUA_HudEnabled(hud_battlebumpers))
|
||||
{
|
||||
if ((gametyperules & GTR_BUMPERS) && players[rankplayer[i]].bumper > 0)
|
||||
|
|
@ -3438,6 +3457,20 @@ static void K_drawKartMinimapWaypoint(waypoint_t *wp, INT32 hudx, INT32 hudy, IN
|
|||
K_drawKartMinimapDot(wp->mobj->x, wp->mobj->y, hudx, hudy, flags | V_NOSCALESTART, pal, size);
|
||||
}
|
||||
|
||||
static void K_drawKartMinimapCluster(INT32 hudx, INT32 hudy, INT32 flags)
|
||||
{
|
||||
UINT8 pal = 180; // Strong pink color.
|
||||
UINT8 size = 6;
|
||||
|
||||
if (!(flags & V_NOSCALESTART))
|
||||
{
|
||||
hudx *= vid.dupx;
|
||||
hudy *= vid.dupy;
|
||||
}
|
||||
|
||||
K_drawKartMinimapDot(clusterpoint.x, clusterpoint.y, hudx, hudy, flags | V_NOSCALESTART, pal, size);
|
||||
}
|
||||
|
||||
#define ICON_DOT_RADIUS (cv_minihead.value && !cv_showminimapnames.value) ? 8 : 10
|
||||
|
||||
typedef struct
|
||||
|
|
@ -3487,6 +3520,8 @@ static void K_drawKartMinimap(void)
|
|||
mobj_t *mobj, *next; // for SPB drawing (or any other item(s) we may wanna draw, I dunno!)
|
||||
fixed_t interpx, interpy;
|
||||
spbdraw_t spb;
|
||||
UINT16 usecolor;
|
||||
boolean colorizeplayer;
|
||||
|
||||
// Draw the HUD only when playing in a level.
|
||||
// hu_stuff needs this, unlike st_stuff.
|
||||
|
|
@ -3533,6 +3568,8 @@ static void K_drawKartMinimap(void)
|
|||
if (!minimaptrans)
|
||||
return;
|
||||
|
||||
colorizeplayer = false;
|
||||
|
||||
minimaptrans = ((10-minimaptrans)<<FF_TRANSSHIFT);
|
||||
|
||||
if (encoremode)
|
||||
|
|
@ -3618,12 +3655,24 @@ static void K_drawKartMinimap(void)
|
|||
|
||||
workingPic = faceprefix[skin][FACE_MINIMAP];
|
||||
|
||||
colorizeplayer = players[i].mo->colorized;
|
||||
|
||||
if ((players[i].invincibilitytimer) && (K_InvincibilityGradient(players[i].invincibilitytimer) > (FRACUNIT/2)))
|
||||
{
|
||||
usecolor = (K_RainbowColor(leveltime / 2));
|
||||
colorizeplayer = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
usecolor = players[i].mo->color;
|
||||
}
|
||||
|
||||
if (players[i].mo->color)
|
||||
{
|
||||
if (players[i].mo->colorized)
|
||||
colormap = R_GetTranslationColormap(TC_RAINBOW, players[i].mo->color, GTC_CACHE);
|
||||
if (colorizeplayer)
|
||||
colormap = R_GetTranslationColormap(TC_RAINBOW, usecolor, GTC_CACHE);
|
||||
else
|
||||
colormap = R_GetTranslationColormap(skin, players[i].mo->color, GTC_CACHE);
|
||||
colormap = R_GetTranslationColormap(skin, usecolor, GTC_CACHE);
|
||||
}
|
||||
else
|
||||
colormap = NULL;
|
||||
|
|
@ -3769,12 +3818,24 @@ static void K_drawKartMinimap(void)
|
|||
|
||||
workingPic = faceprefix[skin][FACE_MINIMAP];
|
||||
|
||||
colorizeplayer = players[localplayers[i]].mo->colorized;
|
||||
|
||||
if ((players[localplayers[i]].invincibilitytimer) && (K_InvincibilityGradient(players[localplayers[i]].invincibilitytimer) > (FRACUNIT/2)))
|
||||
{
|
||||
usecolor = (K_RainbowColor(leveltime / 2));
|
||||
colorizeplayer = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
usecolor = players[localplayers[i]].mo->color;
|
||||
}
|
||||
|
||||
if (players[localplayers[i]].mo->color)
|
||||
{
|
||||
if (players[localplayers[i]].mo->colorized)
|
||||
colormap = R_GetTranslationColormap(TC_RAINBOW, players[localplayers[i]].mo->color, GTC_CACHE);
|
||||
if (colorizeplayer)
|
||||
colormap = R_GetTranslationColormap(TC_RAINBOW, usecolor, GTC_CACHE);
|
||||
else
|
||||
colormap = R_GetTranslationColormap(skin, players[localplayers[i]].mo->color, GTC_CACHE);
|
||||
colormap = R_GetTranslationColormap(skin, usecolor, GTC_CACHE);
|
||||
}
|
||||
else
|
||||
colormap = NULL;
|
||||
|
|
@ -3841,6 +3902,11 @@ static void K_drawKartMinimap(void)
|
|||
K_drawKartMinimapWaypoint(stplyr->nextwaypoint, x, y, splitflags);
|
||||
}
|
||||
}
|
||||
|
||||
if (cv_kartdebugcluster.value != 0)
|
||||
{
|
||||
K_drawKartMinimapCluster(x, y, splitflags);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -4853,6 +4919,28 @@ static void K_DrawWaypointDebugger(void)
|
|||
V_DrawString(8, 176, 0, va("Finishline Distance: %d", stplyr->distancetofinish));
|
||||
}
|
||||
|
||||
static void K_DrawClusterDebugger(void)
|
||||
{
|
||||
if (cv_kartdebugcluster.value == 0)
|
||||
return;
|
||||
|
||||
if (stplyr != &players[displayplayers[0]]) // only for p1
|
||||
return;
|
||||
|
||||
INT32 vflags = V_6WIDTHSPACE|V_ALLOWLOWERCASE;
|
||||
|
||||
if (K_UsingLegacyCheckpoints() && !(gametype == GT_BATTLE))
|
||||
{
|
||||
V_DrawThinString(8, 136, vflags, va("Cluster player: %s", player_names[clusterid]));
|
||||
V_DrawThinString(8, 146, vflags, va("X: %f, Y: %f, Z: %f, Dist. from cluster: %d", FIXED_TO_FLOAT(clusterpoint.x), FIXED_TO_FLOAT(clusterpoint.y), FIXED_TO_FLOAT(clusterpoint.z), stplyr->distancefromcluster));
|
||||
}
|
||||
else
|
||||
{
|
||||
V_DrawThinString(8, 136, vflags, va("Cluster DtF: %d, Your DtF: %d", clusterdtf.x, stplyr->distancetofinish));
|
||||
V_DrawThinString(8, 146, vflags, va("Distance from Cluster: %d", stplyr->distancefromcluster));
|
||||
}
|
||||
}
|
||||
|
||||
static void K_DrawBotDebugger(void)
|
||||
{
|
||||
if (!cv_kartdebugbot.value || !stplyr->bot)
|
||||
|
|
@ -5112,5 +5200,6 @@ void K_drawKartHUD(void)
|
|||
|
||||
K_DrawWaypointDebugger();
|
||||
K_DrawBotDebugger();
|
||||
K_DrawClusterDebugger();
|
||||
K_DrawDirectorDebugger();
|
||||
}
|
||||
|
|
|
|||
227
src/k_kart.c
227
src/k_kart.c
|
|
@ -51,6 +51,7 @@
|
|||
#include "lua_hook.h" // For MobjDamage and ShouldDamage
|
||||
#include "m_cheat.h" // objectplacing
|
||||
#include "p_spec.h"
|
||||
#include "m_easing.h" // Invincibility gradienting
|
||||
|
||||
|
||||
#include "k_stats.h"
|
||||
|
|
@ -62,6 +63,7 @@
|
|||
#include "k_collide.h"
|
||||
#include "k_follower.h"
|
||||
#include "k_grandprix.h"
|
||||
#include "k_cluster.hpp"
|
||||
|
||||
#include "blan/b_soc.h"
|
||||
|
||||
|
|
@ -83,6 +85,13 @@ consvar_t cv_saltyhop = CVAR_INIT ("hardcodehop", "Off", CV_SAVE, CV_OnOff, NULL
|
|||
// indirectitemcooldown is timer before anyone's allowed another Shrink/SPB
|
||||
// mapreset is set when enough players fill an empty server
|
||||
|
||||
// Cluster point.
|
||||
// During a legacy race, this is an actual 3D vector.
|
||||
// During a waypointed race, this is simply a storage point for the "cluster distance";
|
||||
// the distance to finish with the most active number of players.
|
||||
boolean clusterplayer[MAXPLAYERS];
|
||||
vector3_t clusterpoint, clusterdtf;
|
||||
|
||||
void K_TimerInit(void)
|
||||
{
|
||||
UINT8 i;
|
||||
|
|
@ -268,6 +277,7 @@ void K_RegisterKartStuff(void)
|
|||
CV_RegisterVar(&cv_kartdebugwaypoints);
|
||||
CV_RegisterVar(&cv_kartdebuglap);
|
||||
CV_RegisterVar(&cv_kartdebugbot);
|
||||
CV_RegisterVar(&cv_kartdebugcluster);
|
||||
|
||||
CV_RegisterVar(&cv_kartdebugcheckpoint);
|
||||
CV_RegisterVar(&cv_kartdebugnodes);
|
||||
|
|
@ -342,6 +352,9 @@ void K_RegisterKartStuff(void)
|
|||
|
||||
CV_RegisterVar(&cv_kartslipdash);
|
||||
|
||||
CV_RegisterVar(&cv_kartinvindist);
|
||||
CV_RegisterVar(&cv_kartinvindistmul);
|
||||
|
||||
CV_RegisterVar(&cv_kartdriftsounds);
|
||||
CV_RegisterVar(&cv_kartdriftefx);
|
||||
CV_RegisterVar(&cv_driftsparkpulse);
|
||||
|
|
@ -3562,6 +3575,65 @@ static inline fixed_t K_GetSneakerBoostSpeed(void)
|
|||
}
|
||||
}
|
||||
|
||||
// Used to determine the speed and power of Invincibility.
|
||||
fixed_t K_InvincibilityGradient(UINT16 time)
|
||||
{
|
||||
return (min(936, (fixed_t)time) * FRACUNIT / BASEINVINTIME);
|
||||
}
|
||||
|
||||
static fixed_t K_InvincibilityEasing(fixed_t x)
|
||||
{
|
||||
fixed_t u = max(FRACUNIT / 4, (min(32000, x) * FRACUNIT) / INVINDIST);
|
||||
|
||||
if (x < INVINDIST)
|
||||
return u;
|
||||
|
||||
u = max(0, (u - FRACUNIT));
|
||||
|
||||
if (u < FRACUNIT)
|
||||
return Easing_InCubic(u, FRACUNIT, 22 * FRACUNIT / 10);
|
||||
|
||||
return Easing_OutCubic(
|
||||
min(FRACUNIT, FixedMul(u - FRACUNIT, FRACUNIT/4)), 22 * FRACUNIT / 10, 4 * FRACUNIT);
|
||||
}
|
||||
|
||||
|
||||
UINT16 K_GetInvincibilityTime(player_t *player)
|
||||
{
|
||||
UINT32 i, pingame;
|
||||
|
||||
pingame = 0;
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (!playeringame[i] || players[i].spectator)
|
||||
continue;
|
||||
|
||||
if (!(gametyperules & GTR_BUMPERS) || players[i].bumper)
|
||||
pingame++;
|
||||
|
||||
if (pingame > 1) // We only want to see if one player is playing.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pingame <= 1)
|
||||
return BASEINVINTIME;
|
||||
|
||||
fixed_t clustermul = K_InvincibilityEasing(player->distancefromcluster);
|
||||
return FixedMul(BASEINVINTIME, clustermul);
|
||||
}
|
||||
|
||||
static fixed_t K_GetInvincibilitySpeed(UINT16 time)
|
||||
{
|
||||
fixed_t t = min(FRACUNIT, K_InvincibilityGradient(time));
|
||||
return Easing_OutCubic(t, 0, INVINSPEEDBOOST);
|
||||
}
|
||||
|
||||
static fixed_t K_GetInvincibilityAccel(UINT16 time)
|
||||
{
|
||||
fixed_t t = min(FRACUNIT, K_InvincibilityGradient(time));
|
||||
return Easing_OutCubic(t, 0, INVINACCELBOOST);
|
||||
}
|
||||
|
||||
static fixed_t diminish(fixed_t speedboost)
|
||||
{
|
||||
return FixedSqrt(speedboost + DIMINISHPARAM) - FixedSqrt(DIMINISHPARAM);
|
||||
|
|
@ -3661,7 +3733,9 @@ static void K_GetKartBoostPower(player_t *player)
|
|||
|
||||
if (player->invincibilitytimer) // Invincibility
|
||||
{
|
||||
K_DoBoost(player, INVINSPEEDBOOST, INVINACCELBOOST, INVINSTACKABLE, INVINSTACKABLE); // + 37.5% top speed, + 300% acceleration
|
||||
fixed_t invspeedboost = K_GetInvincibilitySpeed(player->invincibilitytimer);
|
||||
fixed_t invaccelboost = K_GetInvincibilityAccel(player->invincibilitytimer);
|
||||
K_DoBoost(player, invspeedboost, invaccelboost, INVINSTACKABLE, INVINSTACKABLE); // + 37.5% top speed, + 300% acceleration
|
||||
}
|
||||
|
||||
if (player->growshrinktimer > 0) // Grow
|
||||
|
|
@ -5801,6 +5875,12 @@ void K_DoInvincibility(player_t *player, tic_t time)
|
|||
P_SetTarget(&overlay->target, player->mo);
|
||||
overlay->destscale = player->mo->scale;
|
||||
P_SetScale(overlay, player->mo->scale);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
player->invincibilitytimer = time;
|
||||
|
|
@ -7650,7 +7730,9 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
|
|||
player->startboost = K_ChainOrDeincrementTime(player, player->startboost, 1, false);
|
||||
|
||||
if (player->invincibilitytimer)
|
||||
player->invincibilitytimer--;
|
||||
{
|
||||
player->invincibilitytimer--;
|
||||
}
|
||||
|
||||
if (player->checkskip)
|
||||
player->checkskip--;
|
||||
|
|
@ -7957,10 +8039,10 @@ void K_KartResetPlayerColor(player_t *player)
|
|||
{
|
||||
boolean skip = false;
|
||||
|
||||
fullbright = true;
|
||||
//fullbright = true;
|
||||
|
||||
player->mo->color = K_RainbowColor(leveltime / 2);
|
||||
player->mo->colorized = true;
|
||||
// player->mo->color = K_RainbowColor(leveltime / 2);
|
||||
// player->mo->colorized = true;
|
||||
skip = true;
|
||||
|
||||
if (skip)
|
||||
|
|
@ -9680,6 +9762,49 @@ INT32 K_GetDriftAngleOffset(player_t *player)
|
|||
return a;
|
||||
}
|
||||
|
||||
static fixed_t K_Distance3D(fixed_t x1, fixed_t y1, fixed_t z1, fixed_t x2, fixed_t y2, fixed_t z2)
|
||||
{
|
||||
fixed_t dist_xy = R_PointToDist2(x1,y1,x2,y2);
|
||||
|
||||
return R_PointToDist2(0,z1,dist_xy,z2);
|
||||
}
|
||||
|
||||
static fixed_t K_PlayerDistance3D(player_t *source, player_t *destination)
|
||||
{
|
||||
if ((!source->mo) || (!destination->mo))
|
||||
return INT32_MAX; // Return a garbage value.
|
||||
|
||||
return K_Distance3D(source->mo->x,source->mo->y,source->mo->z,destination->mo->x,destination->mo->y,destination->mo->z);
|
||||
}
|
||||
|
||||
static UINT32 K_UpdateDistanceFromCluster(player_t *player)
|
||||
{
|
||||
player_t *cluster_p;
|
||||
|
||||
if (K_UsingLegacyCheckpoints() && !(gametype == GT_BATTLE))
|
||||
{
|
||||
// Compare yourself against the cluster player to determine the distance.
|
||||
cluster_p = &players[clusterid];
|
||||
|
||||
if (player == cluster_p)
|
||||
return 0; // We ARE the cluster player.
|
||||
|
||||
if (player->position <= cluster_p->position)
|
||||
return 0; // Ahead, or tying.
|
||||
|
||||
// Return the 3D distance from the cluster player.
|
||||
return K_PlayerDistance3D(player, cluster_p) / FRACUNIT;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (player->distancetofinish <= clusterdtf.x)
|
||||
return 0; // Ahead, or tying.
|
||||
|
||||
// Return the difference between us and the cluster distance.
|
||||
return (player->distancetofinish - clusterdtf.x);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// K_KartUpdatePosition
|
||||
//
|
||||
|
|
@ -9857,10 +9982,95 @@ void K_KartLegacyUpdatePosition(player_t *player)
|
|||
player->position = position;
|
||||
}
|
||||
|
||||
// Brute-force finds the area on the course with the highest player density in a given radius.
|
||||
// Based on DBSCAN, so it "chain-scans" neighboring players as well for a more accurate result.
|
||||
UINT32 clusterid = 0;
|
||||
|
||||
static vector3_t *K_FindPlayerCluster(fixed_t eps, INT32 (*func)(player_t *, fixed_t, vector3_t *), vector3_t *out)
|
||||
{
|
||||
INT32 bestdensity = 0; // Cluster counter
|
||||
vector3_t tempclusterpoint;
|
||||
player_t *findme;
|
||||
player_t *player;
|
||||
|
||||
INT32 i;
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
clusterplayer[i] = false;
|
||||
|
||||
if (!playeringame[i])
|
||||
continue;
|
||||
|
||||
player = &players[i];
|
||||
|
||||
if (player->spectator)
|
||||
continue; // spectator
|
||||
|
||||
if (!player->mo)
|
||||
continue;
|
||||
|
||||
// Find neighbors
|
||||
INT32 N = func(player, eps, &tempclusterpoint);
|
||||
|
||||
// Double-remove the clusterplayer flag. Bad hack, I know...
|
||||
clusterplayer[i] = false;
|
||||
|
||||
// Density check
|
||||
if (N > bestdensity)
|
||||
{
|
||||
bestdensity = N;
|
||||
|
||||
findme = closesttocluster;
|
||||
|
||||
out->x = tempclusterpoint.x;
|
||||
out->y = tempclusterpoint.y;
|
||||
out->z = tempclusterpoint.z;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (!playeringame[i])
|
||||
continue;
|
||||
|
||||
player = &players[i];
|
||||
|
||||
if (player == findme)
|
||||
{
|
||||
clusterid = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
#define CLUSTER_EPSILON (160*FRACUNIT)
|
||||
#define K_FindPlayerClusterLegacy() (K_FindPlayerCluster(CLUSTER_EPSILON, K_CountNeighboringPlayers, &clusterpoint))
|
||||
#define K_FindPlayerClusterDTF() (K_FindPlayerCluster((CLUSTER_EPSILON / FRACUNIT), K_CountNeighboringPlayersByDTF, &clusterdtf))
|
||||
|
||||
void K_UpdateClusterPoints(void)
|
||||
{
|
||||
// Get the player cluster.
|
||||
if (K_UsingLegacyCheckpoints() && !(gametype == GT_BATTLE))
|
||||
{
|
||||
K_FindPlayerClusterLegacy();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cv_kartdebugcluster.value)
|
||||
{
|
||||
K_FindPlayerClusterLegacy();
|
||||
}
|
||||
|
||||
K_FindPlayerClusterDTF();
|
||||
}
|
||||
}
|
||||
|
||||
void K_UpdateAllPlayerPositions(void)
|
||||
{
|
||||
INT32 i;
|
||||
|
||||
if (!K_UsingLegacyCheckpoints())
|
||||
{
|
||||
// First loop: Ensure all players' distance to the finish line are all accurate
|
||||
|
|
@ -9887,6 +10097,9 @@ void K_UpdateAllPlayerPositions(void)
|
|||
{
|
||||
if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo))
|
||||
{
|
||||
// Get the cluster distance.
|
||||
players[i].distancefromcluster = K_UpdateDistanceFromCluster(&players[i]);
|
||||
|
||||
if (K_UsingLegacyCheckpoints() && !(gametype == GT_BATTLE))
|
||||
K_KartLegacyUpdatePosition(&players[i]);
|
||||
else
|
||||
|
|
@ -10378,7 +10591,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
case KITEM_INVINCIBILITY:
|
||||
if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) // Doesn't hold your item slot hostage normally, so you're free to waste it if you have multiple
|
||||
{
|
||||
K_DoInvincibility(player, 10 * TICRATE);
|
||||
K_DoInvincibility(player, K_GetInvincibilityTime(player));
|
||||
K_PlayPowerGloatSound(player->mo);
|
||||
player->itemamount--;
|
||||
player->botvars.itemconfirm = 0;
|
||||
|
|
|
|||
13
src/k_kart.h
13
src/k_kart.h
|
|
@ -32,13 +32,15 @@ Make sure this matches the actual number of states
|
|||
#define GROW_SCALE ((3*FRACUNIT)/2)
|
||||
#define SHRINK_SCALE ((6*FRACUNIT)/8)
|
||||
|
||||
#define BASEINVINTIME (10 * TICRATE)
|
||||
|
||||
#define AUTORESPAWN_TIME (10 * TICRATE)
|
||||
#define AUTORESPAWN_THRESHOLD (7 * TICRATE)
|
||||
|
||||
#define FLAMESTOREMAX TICRATE*2
|
||||
|
||||
// Fixed distance of a flipover.
|
||||
#define FLIPOVERDIST (112<<FRACBITS)
|
||||
#define FLIPOVERDIST (336<<FRACBITS)
|
||||
|
||||
// Time (in tics) of a flipover.
|
||||
// (TICRATE * (0.56666 * FRACUNIT))
|
||||
|
|
@ -62,6 +64,10 @@ Make sure this matches the actual number of states
|
|||
((grav >> 1) * (FLIPOVERHALFTICS * FLIPOVERHALFTICS))) / \
|
||||
FLIPOVERHALFTICS)
|
||||
|
||||
extern boolean clusterplayer[MAXPLAYERS];
|
||||
extern UINT32 clusterid; // ID of the "cluster player", the one closest to the cluster point.
|
||||
extern vector3_t clusterpoint, clusterdtf;
|
||||
|
||||
// Precalculated constants for stacked boost diminishing
|
||||
// *Somewhat* matches old calc but doesn't use arrays, which makes it faster and more memory efficent
|
||||
#define DIMINISHPARAM K_RAGuard(cv_kartstacking_diminishparam)
|
||||
|
|
@ -78,6 +84,8 @@ Make sure this matches the actual number of states
|
|||
#define SNEAKERACCELBOOST K_RAGuard(cv_kartstacking_sneaker_accelboost)
|
||||
#define MAXSNEAKERSTACK K_RAGuard(cv_kartstacking_sneaker_maxgrade)
|
||||
#define SNEAKERSTACKABLE K_RAGuard(cv_kartstacking_sneaker_stackable)
|
||||
#define INVINDIST K_RAGuard(cv_kartinvindist)
|
||||
#define INVINDISTMUL K_RAGuard(cv_kartinvindistmul)
|
||||
#define INVINSPEEDBOOST K_RAGuard(cv_kartstacking_invincibility_speedboost)
|
||||
#define INVINACCELBOOST K_RAGuard(cv_kartstacking_invincibility_accelboost)
|
||||
#define INVINSTACKABLE K_RAGuard(cv_kartstacking_invincibility_stackable)
|
||||
|
|
@ -205,6 +213,8 @@ typedef enum
|
|||
void K_DoSneaker(player_t *player, INT32 type);
|
||||
|
||||
void K_DoPogoSpring(mobj_t *mo, fixed_t vertispeed, UINT8 sound);
|
||||
fixed_t K_InvincibilityGradient(UINT16 time);
|
||||
UINT16 K_GetInvincibilityTime(player_t *player);
|
||||
void K_DoInvincibility(player_t *player, tic_t time);
|
||||
void K_KillBananaChain(mobj_t *banana, mobj_t *inflictor, mobj_t *source);
|
||||
void K_UpdateHnextList(player_t *player, boolean clean);
|
||||
|
|
@ -224,6 +234,7 @@ void K_SpawnDriftElectricSparks(player_t *player);
|
|||
INT32 K_GetDriftAngleOffset(player_t *player);
|
||||
void K_KartUpdatePosition(player_t *player);
|
||||
void K_KartLegacyUpdatePosition(player_t *player);
|
||||
void K_UpdateClusterPoints(void);
|
||||
void K_UpdateAllPlayerPositions(void);
|
||||
mobj_t *K_CreatePaperItem(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 flip, UINT8 type, UINT8 amount);
|
||||
void K_DropItems(player_t *player);
|
||||
|
|
|
|||
76
src/p_mobj.c
76
src/p_mobj.c
|
|
@ -6715,6 +6715,75 @@ static void P_RemoveOverlay(mobj_t *thing)
|
|||
}
|
||||
}
|
||||
|
||||
static UINT32 P_GetTranslucencyForInvincibility(mobj_t *mo)
|
||||
{
|
||||
if (!mo->player)
|
||||
{
|
||||
// Get out.
|
||||
return 0;
|
||||
}
|
||||
|
||||
return max(0, min(9, 10 - FixedMul(10, K_InvincibilityGradient(mo->player->invincibilitytimer))))<<RF_TRANSSHIFT;
|
||||
}
|
||||
|
||||
// Called only when MT_OVERLAY thinks.
|
||||
static void P_PlayerInvincibilityOverlay(mobj_t *thing)
|
||||
{
|
||||
I_Assert(thing->target != NULL);
|
||||
I_Assert(thing->target->player != NULL);
|
||||
|
||||
if (!thing->target->player->invincibilitytimer)
|
||||
{
|
||||
P_SetTarget(&thing->target, NULL);
|
||||
|
||||
I_Assert(thing->target == NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
thing->destscale = thing->target->scale;
|
||||
|
||||
if (thing->target->eflags & MFE_VERTICALFLIP)
|
||||
{
|
||||
thing->eflags |= MFE_VERTICALFLIP;
|
||||
thing->z += thing->target->height - thing->height;
|
||||
}
|
||||
|
||||
thing->color = K_RainbowColor(leveltime / 2);
|
||||
thing->colorized = true;
|
||||
thing->dispoffset = min(2, thing->target->dispoffset + 1);
|
||||
|
||||
thing->angle = (thing->target->player ? thing->target->player->drawangle : thing->target->angle);
|
||||
|
||||
thing->roll = thing->target->roll;
|
||||
thing->pitch = thing->target->pitch;
|
||||
thing->sloperoll = thing->target->sloperoll;
|
||||
thing->slopepitch = thing->target->slopepitch;
|
||||
|
||||
thing->sprite = thing->target->sprite;
|
||||
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->skin = thing->target->skin;
|
||||
thing->standingslope = thing->target->standingslope;
|
||||
|
||||
thing->sprxoff = thing->target->sprxoff;
|
||||
thing->spryoff = thing->target->spryoff;
|
||||
thing->sprzoff = thing->target->sprzoff;
|
||||
thing->rollangle = thing->target->rollangle;
|
||||
|
||||
thing->spritexscale = thing->target->spritexscale;
|
||||
thing->spriteyscale = thing->target->spriteyscale;
|
||||
thing->spritexoffset = thing->target->spritexoffset;
|
||||
thing->spriteyoffset = thing->target->spriteyoffset;
|
||||
|
||||
if (thing->target->flags2 & MF2_OBJECTFLIP)
|
||||
thing->flags2 |= MF2_OBJECTFLIP;
|
||||
|
||||
if (!(thing->target->flags & MF_DONTENCOREMAP))
|
||||
thing->flags &= ~MF_DONTENCOREMAP;
|
||||
}
|
||||
|
||||
static void P_MobjScaleThink(mobj_t *mobj)
|
||||
{
|
||||
fixed_t oldheight = mobj->height;
|
||||
|
|
@ -7051,7 +7120,14 @@ static void P_MobjSceneryThink(mobj_t *mobj)
|
|||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
P_AddOverlay(mobj);
|
||||
if ((mobj->extravalue2) && (mobj->target->player))
|
||||
{
|
||||
// Could be overlaying an invincible player.
|
||||
P_PlayerInvincibilityOverlay(mobj);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MT_WATERDROP:
|
||||
P_SceneryCheckWater(mobj);
|
||||
|
|
|
|||
|
|
@ -8293,6 +8293,7 @@ static void P_InitPlayers(void)
|
|||
G_SpawnPlayer(i, false);
|
||||
}
|
||||
}
|
||||
K_UpdateClusterPoints();
|
||||
K_UpdateAllPlayerPositions();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -734,6 +734,7 @@ void P_Ticker(boolean run)
|
|||
|
||||
ps_playerthink_time = I_GetPreciseTime();
|
||||
|
||||
K_UpdateClusterPoints();
|
||||
K_UpdateAllPlayerPositions();
|
||||
|
||||
// OK! Now that we got all of that sorted, players can think!
|
||||
|
|
|
|||
10
src/p_user.c
10
src/p_user.c
|
|
@ -2241,8 +2241,14 @@ void P_MovePlayer(player_t *player)
|
|||
&& onground && (leveltime & 1))
|
||||
K_SpawnBoostTrail(player);
|
||||
|
||||
if (player->invincibilitytimer > 0)
|
||||
K_SpawnSparkleTrail(player->mo);
|
||||
if ((player->invincibilitytimer > 0) &&
|
||||
((leveltime % max(1,
|
||||
10 - FixedMul(9,
|
||||
K_InvincibilityGradient(
|
||||
player->invincibilitytimer)))) == 0))
|
||||
{
|
||||
K_SpawnSparkleTrail(player->mo);
|
||||
}
|
||||
|
||||
if (player->wipeoutslow > 1 && (leveltime & 1))
|
||||
K_SpawnWipeoutTrail(player->mo, false);
|
||||
|
|
|
|||
Loading…
Reference in a new issue