Merge pull request 'Add an alternative Invincibility type' (#69) from Anonimus/blankart:invincerework into blankart-dev
Reviewed-on: https://codeberg.org/NepDisk/blankart/pulls/69
This commit is contained in:
commit
4eeb3a5d2a
28 changed files with 1292 additions and 81 deletions
|
|
@ -122,6 +122,7 @@ k_bheap.c
|
|||
k_bot.cpp
|
||||
k_botitem.cpp
|
||||
k_botsearch.cpp
|
||||
k_cluster.cpp
|
||||
k_grandprix.c
|
||||
k_boss.c
|
||||
k_hud.c
|
||||
|
|
|
|||
|
|
@ -186,6 +186,9 @@ extern CV_PossibleValue_t CV_Natural[];
|
|||
#define KARTGP_MASTER 4 // Not a speed setting, gives hard speed with maxed out bots
|
||||
#define KARTGP_NIGHTMARE 5 // Not a speed setting, gives expert speed with maxed out bots
|
||||
extern CV_PossibleValue_t kartspeed_cons_t[];
|
||||
// Invincibility types.
|
||||
#define KARTINVIN_LEGACY 0
|
||||
#define KARTINVIN_ALTERN 1
|
||||
|
||||
extern consvar_t cv_execversion;
|
||||
|
||||
|
|
|
|||
|
|
@ -86,7 +86,7 @@
|
|||
#define ASSET_HASH_TEXTURES_KART 0xb4211b2f32b6a291
|
||||
#define ASSET_HASH_CHARS_KART 0x1e68a3e01aa5c68b
|
||||
#define ASSET_HASH_MAPS_KART 0x38558ed00da41ce9
|
||||
#define ASSET_HASH_MAIN_PK3 0x9a6188063fcdcc93
|
||||
#define ASSET_HASH_MAIN_PK3 0x7fbb80904dbc97d7
|
||||
#define ASSET_HASH_MAPPATCH_PK3 0x93a9213b2b2ba260
|
||||
#define ASSET_HASH_BONUSCHARS_KART 0x60e6f13d822a7461
|
||||
#ifdef USE_PATCH_FILE
|
||||
|
|
|
|||
|
|
@ -154,6 +154,7 @@ static void KartStacking_OnChange(void);
|
|||
static void KartChaining_OnChange(void);
|
||||
static void KartSlipdash_OnChange(void);
|
||||
static void KartItemBreaker_OnChange(void);
|
||||
static void KartInvinType_OnChange(void);
|
||||
|
||||
static void Schedule_OnChange(void);
|
||||
|
||||
|
|
@ -462,8 +463,10 @@ 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_accelboost = CVAR_INIT ("vanillaboost_invincibility_accelboost", "3.0", CV_NETVAR|CV_CHEAT|CV_FLOAT, CV_Unsigned, NULL);
|
||||
consvar_t cv_kartstacking_invincibility_legacyspeedboost = CVAR_INIT ("vanillaboost_invincibility_legacyspeedboost", "0.375", CV_NETVAR|CV_CHEAT|CV_FLOAT, CV_Unsigned, NULL);
|
||||
consvar_t cv_kartstacking_invincibility_legacyaccelboost = CVAR_INIT ("vanillaboost_invincibility_legacyaccelboost", "3.0", CV_NETVAR|CV_CHEAT|CV_FLOAT, CV_Unsigned, NULL);
|
||||
consvar_t cv_kartstacking_invincibility_alternatespeedboost = CVAR_INIT ("vanillaboost_invincibility_alternatespeedboost", "0.68", CV_NETVAR|CV_CHEAT|CV_FLOAT, CV_Unsigned, NULL);
|
||||
consvar_t cv_kartstacking_invincibility_alternateaccelboost = CVAR_INIT ("vanillaboost_invincibility_alternateaccelboost", "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);
|
||||
|
||||
consvar_t cv_kartstacking_grow_speedboost = CVAR_INIT ("vanillaboost_grow_speedboost", "0.2", CV_NETVAR|CV_CHEAT|CV_FLOAT, CV_Unsigned, NULL);
|
||||
|
|
@ -503,6 +506,19 @@ 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);
|
||||
|
||||
// Invincibility modifiers
|
||||
static CV_PossibleValue_t invintype_cons_t[] = {{0, "Legacy"}, {1, "Alternative"}, {0, NULL}};
|
||||
consvar_t cv_kartinvintype = CVAR_INIT ("kartinvintype", "Legacy", CV_NETVAR|CV_CALL, invintype_cons_t, KartInvinType_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);
|
||||
|
||||
consvar_t cv_kartinvin_maxtime = CVAR_INIT ("kartinvin_maxtime", "35.0", CV_NETVAR|CV_CHEAT|CV_FLOAT, CV_Unsigned, NULL);
|
||||
consvar_t cv_kartinvin_midtime = CVAR_INIT ("kartinvin_midtime", "23.333", CV_NETVAR|CV_CHEAT|CV_FLOAT, CV_Unsigned, NULL);
|
||||
|
||||
static CV_PossibleValue_t kartdebugitem_cons_t[] =
|
||||
{
|
||||
#define FOREACH( name, n ) { n, #name }
|
||||
|
|
@ -527,6 +543,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);
|
||||
|
|
@ -7311,6 +7328,24 @@ static void KartItemBreaker_OnChange(void)
|
|||
}
|
||||
}
|
||||
|
||||
static void KartInvinType_OnChange(void)
|
||||
{
|
||||
if (K_CanChangeRules() == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (leveltime < starttime)
|
||||
{
|
||||
CONS_Printf(M_GetText("Invincibility type has been changed to \"%s\".\n"), cv_kartinvintype.string);
|
||||
invintype = (UINT8)cv_kartinvintype.value;
|
||||
}
|
||||
else
|
||||
{
|
||||
CONS_Printf(M_GetText("Invincibility type will be changed to \"%s\" next round.\n"), cv_kartinvintype.string);
|
||||
}
|
||||
}
|
||||
|
||||
static void Schedule_OnChange(void)
|
||||
{
|
||||
size_t i;
|
||||
|
|
|
|||
|
|
@ -138,8 +138,10 @@ extern consvar_t cv_kartstacking_sneaker_accelboost;
|
|||
extern consvar_t cv_kartstacking_sneaker_maxgrade;
|
||||
extern consvar_t cv_kartstacking_sneaker_stackable;
|
||||
|
||||
extern consvar_t cv_kartstacking_invincibility_speedboost;
|
||||
extern consvar_t cv_kartstacking_invincibility_accelboost;
|
||||
extern consvar_t cv_kartstacking_invincibility_legacyspeedboost;
|
||||
extern consvar_t cv_kartstacking_invincibility_legacyaccelboost;
|
||||
extern consvar_t cv_kartstacking_invincibility_alternatespeedboost;
|
||||
extern consvar_t cv_kartstacking_invincibility_alternateaccelboost;
|
||||
extern consvar_t cv_kartstacking_invincibility_stackable;
|
||||
|
||||
extern consvar_t cv_kartstacking_flame_speedval;
|
||||
|
|
@ -171,6 +173,11 @@ extern consvar_t cv_kartpurpledrift;
|
|||
extern consvar_t cv_kartbumpspark;
|
||||
extern consvar_t cv_kartbumpspring;
|
||||
extern consvar_t cv_kartslipdash;
|
||||
extern consvar_t cv_kartinvintype;
|
||||
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_encorevotes;
|
||||
|
||||
|
|
@ -179,7 +186,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;
|
||||
|
||||
|
|
|
|||
|
|
@ -110,6 +110,8 @@ typedef enum
|
|||
PF_SHRINKME = 1<<25, // "Shrink me" cheat preference
|
||||
PF_SHRINKACTIVE = 1<<26, // "Shrink me" cheat is in effect. (Can't be disabled mid-race)
|
||||
|
||||
PF_JUSTFLIPPED = 1<<27, // Just got flipped over, handle the bump interaction.
|
||||
|
||||
// up to 1<<29 is free
|
||||
PF_ATTACKDOWN = 1<<30, // For lua compat, don't use!
|
||||
PF_SLIDING = 1<<31, // For lua compat, don't use!
|
||||
|
|
@ -605,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;
|
||||
|
|
@ -619,6 +622,10 @@ struct player_t
|
|||
UINT16 flashing;
|
||||
UINT16 spinouttimer; // Spin-out from a banana peel or oil slick (was "pw_bananacam")
|
||||
UINT8 spinouttype; // Determines the mode of spinout/wipeout, see kartspinoutflags_t
|
||||
|
||||
UINT16 flipovertimer; // Flipped over by a player using Invincibility.
|
||||
angle_t flipoverangle; // Movement angle for a flipped-over player.
|
||||
|
||||
UINT8 instashield; // Instashield no-damage animation timer
|
||||
UINT8 wipeoutslow; // Timer before you slowdown when getting wiped out
|
||||
UINT8 justbumped; // Prevent players from endlessly bumping into each other
|
||||
|
|
@ -700,11 +707,16 @@ struct player_t
|
|||
|
||||
UINT8 boostcharge; // Charge during race start
|
||||
|
||||
INT16 growshrinktimer; // > 0 = Big, < 0 = small
|
||||
INT16 growcancel; // Duration of grow canceling
|
||||
INT16 squishedtimer; // Duration of being squished
|
||||
UINT16 rocketsneakertimer; // Rocket Sneaker duration timer
|
||||
UINT16 invincibilitytimer; // Invincibility timer
|
||||
INT16 growshrinktimer; // > 0 = Big, < 0 = small
|
||||
INT16 growcancel; // Duration of grow canceling
|
||||
INT16 squishedtimer; // Duration of being squished
|
||||
|
||||
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 eggmanexplode; // Fake item recieved, explode in a few seconds
|
||||
SINT8 eggmanblame; // (-1 to 15) - Fake item recieved, who set this fake
|
||||
|
|
|
|||
|
|
@ -1039,6 +1039,7 @@ struct int_const_s const INT_CONST[] = {
|
|||
{"DMG_SQUISH",DMG_SQUISH},
|
||||
{"DMG_VOLTAGE",DMG_VOLTAGE},
|
||||
{"DMG_KARMA",DMG_KARMA},
|
||||
{"DMG_FLIPOVER",DMG_FLIPOVER},
|
||||
//// Death types
|
||||
{"DMG_INSTAKILL",DMG_INSTAKILL},
|
||||
{"DMG_DEATHPIT",DMG_DEATHPIT},
|
||||
|
|
|
|||
|
|
@ -664,6 +664,7 @@ extern INT32 cheats;
|
|||
// SRB2kart
|
||||
extern UINT8 numlaps;
|
||||
extern UINT8 gamespeed;
|
||||
extern UINT8 invintype;
|
||||
extern boolean franticitems;
|
||||
extern boolean encoremode, prevencoremode;
|
||||
extern boolean comeback;
|
||||
|
|
|
|||
|
|
@ -914,7 +914,11 @@ static const char *blancredits[] = {
|
|||
"\1Support Programming",
|
||||
"\"hayaunderscore\" aka \"DeltaKaynx\"",
|
||||
"\"WumboSpasm\"",
|
||||
"\"Anonimous\"",
|
||||
"\"Anonimus\"",
|
||||
"",
|
||||
"\1Item Programming",
|
||||
"\"NepDisk\"",
|
||||
"\"Anonimus\"",
|
||||
"",
|
||||
"\1External Programming",
|
||||
"\"Hanicef\"",
|
||||
|
|
@ -929,11 +933,25 @@ static const char *blancredits[] = {
|
|||
"\"JugadorXEI\"",
|
||||
"\"Kimberly\"",
|
||||
"",
|
||||
"\1Item Design",
|
||||
"\"NepDisk\"",
|
||||
"\"Anonimus\"",
|
||||
"\"Denny\" aka \"shephoron\"",
|
||||
"",
|
||||
"\1Design Support",
|
||||
"\"Rim Jobless\"",
|
||||
"\"luna\"",
|
||||
"\"Denny\" aka \"shephoron\"",
|
||||
"",
|
||||
"\1New Graphics Creation",
|
||||
"\"Spee\"",
|
||||
"\"Jin\"",
|
||||
"\"NepDisk\"",
|
||||
"",
|
||||
"\1Debug",
|
||||
"\"Rim Jobless\"",
|
||||
"\"luna\"",
|
||||
"",
|
||||
"\1Special Thanks",
|
||||
"\"Sunflower\" aka \"AnimeSonic\"",
|
||||
"Sunflower's Garden",
|
||||
|
|
|
|||
|
|
@ -282,6 +282,7 @@ INT32 cheats; //for multiplayer cheat commands
|
|||
// Cvars that we don't want changed mid-game
|
||||
UINT8 numlaps; // Removed from Cvar hell
|
||||
UINT8 gamespeed; // Game's current speed (or difficulty, or cc, or etc); 0 for easy, 1 for normal, 2 for hard
|
||||
UINT8 invintype; // How Invincibility functions. 0 for Legacy/Vanilla, 1 for Alternative.
|
||||
boolean encoremode = false; // Encore Mode currently enabled?
|
||||
boolean prevencoremode;
|
||||
boolean franticitems; // Frantic items currently enabled?
|
||||
|
|
|
|||
|
|
@ -786,6 +786,9 @@ _(kc6c)
|
|||
_(kc6d)
|
||||
_(kc6e)
|
||||
|
||||
// MKDS sounds
|
||||
_(mdse8)
|
||||
|
||||
// SRB2kart
|
||||
_(slip)
|
||||
_(screec)
|
||||
|
|
|
|||
263
src/k_cluster.cpp
Normal file
263
src/k_cluster.cpp
Normal file
|
|
@ -0,0 +1,263 @@
|
|||
#include "doomdef.h"
|
||||
#include "doomstat.h"
|
||||
#include "hu_stuff.h"
|
||||
#include "d_player.h"
|
||||
#include "m_fixed.h"
|
||||
#include "k_kart.h"
|
||||
#include "r_main.h"
|
||||
#include "g_game.h"
|
||||
|
||||
#include "k_cluster.hpp"
|
||||
#include <vector>
|
||||
|
||||
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_GetDTFDifference(player_t *source, player_t *destination)
|
||||
{
|
||||
if ((!source->mo) || (!destination->mo))
|
||||
return INT16_MAX; // Return a garbage value.
|
||||
|
||||
if (destination->distancetofinish > source->distancetofinish)
|
||||
return (destination->distancetofinish - source->distancetofinish);
|
||||
|
||||
return (source->distancetofinish - destination->distancetofinish);
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
player_t *closesttocluster;
|
||||
|
||||
INT32 K_CountNeighboringPlayersByDistance(player_t *sourcePlayer, fixed_t eps, vector3_t *out, void *nodevec)
|
||||
{
|
||||
INT64 meanx, meany, meanz;
|
||||
INT32 N;
|
||||
N = 0;
|
||||
vector3_t neighborvector;
|
||||
std::vector<player_t *>clusternodes;
|
||||
std::vector<player_t *> *realvec;
|
||||
|
||||
if (nodevec != nullptr)
|
||||
realvec = static_cast<std::vector<player_t *> *>(nodevec);
|
||||
|
||||
if (!sourcePlayer->mo)
|
||||
return 0;
|
||||
|
||||
meanx = meany = meanz = 0;
|
||||
|
||||
INT32 i;
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
player_t *player;
|
||||
|
||||
if (!playeringame[i])
|
||||
continue;
|
||||
|
||||
player = &players[i];
|
||||
|
||||
if (player == sourcePlayer)
|
||||
continue; // Ignore ourselves.
|
||||
|
||||
if (clusterplayer[i])
|
||||
continue; // Ignore players we've scanned already.
|
||||
|
||||
if (player->spectator)
|
||||
continue; // spectator
|
||||
|
||||
if (!player->mo)
|
||||
continue;
|
||||
|
||||
if (!K_IsPlayerLosing(player))
|
||||
continue; // Ignore winning players to prevent sandbagging.
|
||||
|
||||
// Scan all points in the database
|
||||
if (K_PlayerDistance3D(sourcePlayer, player) <= eps)
|
||||
{
|
||||
clusterplayer[i] = true;
|
||||
clusternodes.push_back(player);
|
||||
|
||||
if (realvec)
|
||||
realvec->push_back(player);
|
||||
|
||||
// Scan our neighbor for any players close to them.
|
||||
K_CountNeighboringPlayersByDistance(player, eps, &neighborvector, &clusternodes);
|
||||
}
|
||||
}
|
||||
|
||||
N = clusternodes.size();
|
||||
|
||||
if (N != 0)
|
||||
{
|
||||
// Return the average center point of this cluster.
|
||||
for (i = 0; i < N; i++)
|
||||
{
|
||||
if (!clusternodes[i])
|
||||
continue;
|
||||
|
||||
if (!clusternodes[i]->mo)
|
||||
continue;
|
||||
|
||||
meanx += clusternodes[i]->mo->x / FRACUNIT;
|
||||
meany += clusternodes[i]->mo->y / FRACUNIT;
|
||||
meanz += clusternodes[i]->mo->z / FRACUNIT;
|
||||
}
|
||||
|
||||
meanx /= N;
|
||||
meany /= N;
|
||||
meanz /= N;
|
||||
|
||||
//CONS_Printf("mean x: %lld, mean y: %lld, mean z: %lld\n", meanx, meany, meanz);
|
||||
|
||||
out->x = (fixed_t)(meanx) << FRACBITS;
|
||||
out->y = (fixed_t)(meany) << FRACBITS;
|
||||
out->z = (fixed_t)(meanz) << FRACBITS;
|
||||
|
||||
// Get the player closest to the cluster.
|
||||
fixed_t disttocluster, bestdist;
|
||||
bestdist = INT32_MAX;
|
||||
for (i = 0; i < N; i++)
|
||||
{
|
||||
disttocluster = K_Distance3D(clusternodes[i]->mo->x,clusternodes[i]->mo->y,clusternodes[i]->mo->z,out->x,out->y,out->z);
|
||||
|
||||
if (disttocluster < bestdist)
|
||||
{
|
||||
closesttocluster = clusternodes[i];
|
||||
bestdist = disttocluster;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
out->z = out->y = out->x = 0;
|
||||
}
|
||||
|
||||
return N;
|
||||
}
|
||||
|
||||
static std::vector<player_t *> dtf_vec;
|
||||
static boolean cleardtf = true;
|
||||
|
||||
static UINT32 K_GetU32Diff(UINT32 a, UINT32 b)
|
||||
{
|
||||
return (b > a) ? (b - a) : (a - b);
|
||||
}
|
||||
|
||||
INT32 K_CountNeighboringPlayersByDTF(player_t *sourcePlayer, fixed_t eps, vector3_t *out)
|
||||
{
|
||||
INT64 mean;
|
||||
INT32 N = 0;
|
||||
vector3_t neighborvector;
|
||||
|
||||
if (!sourcePlayer->mo)
|
||||
return 0;
|
||||
|
||||
if (cleardtf)
|
||||
dtf_vec.clear();
|
||||
|
||||
mean = 0;
|
||||
|
||||
INT32 i;
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
player_t *player;
|
||||
|
||||
if (!playeringame[i])
|
||||
continue;
|
||||
|
||||
player = &players[i];
|
||||
|
||||
if (player == sourcePlayer)
|
||||
continue; // Ignore ourselves.
|
||||
|
||||
if (clusterplayer[i])
|
||||
continue; // Ignore players we've scanned already.
|
||||
|
||||
if (player->spectator)
|
||||
continue; // spectator
|
||||
|
||||
if (!player->mo)
|
||||
continue;
|
||||
|
||||
if (!K_IsPlayerLosing(player))
|
||||
continue; // Ignore winning players to prevent sandbagging.
|
||||
|
||||
// Scan all points in the database
|
||||
if ((fixed_t)K_GetDTFDifference(sourcePlayer, player) <= eps)
|
||||
{
|
||||
//CONS_Printf("%d is less than %d, adding to nodes vector\n", K_GetDTFDifference(sourcePlayer, player), eps);
|
||||
clusterplayer[i] = true;
|
||||
dtf_vec.push_back(player); // Add to result
|
||||
|
||||
cleardtf = false;
|
||||
|
||||
// Scan our neighbor for any players close to them.
|
||||
K_CountNeighboringPlayersByDTF(player, eps, &neighborvector);
|
||||
|
||||
cleardtf = true;
|
||||
}
|
||||
}
|
||||
|
||||
N = dtf_vec.size();
|
||||
|
||||
if (N != 0)
|
||||
{
|
||||
// Return the average center point of this cluster.
|
||||
for (i = 0; i < N; i++)
|
||||
{
|
||||
if (!dtf_vec[i])
|
||||
continue;
|
||||
|
||||
if (!dtf_vec[i]->mo)
|
||||
continue;
|
||||
|
||||
mean += dtf_vec[i]->distancetofinish;
|
||||
}
|
||||
mean /= N;
|
||||
|
||||
out->x = (fixed_t)(mean);
|
||||
out->y = 0;
|
||||
out->z = 0;
|
||||
|
||||
// Get the player closest to the cluster.
|
||||
UINT32 disttocluster, bestdist;
|
||||
bestdist = UINT32_MAX;
|
||||
for (i = 0; i < N; i++)
|
||||
{
|
||||
disttocluster = K_GetU32Diff(dtf_vec[i]->distancetofinish, mean);
|
||||
|
||||
if (disttocluster < bestdist)
|
||||
{
|
||||
closesttocluster = dtf_vec[i];
|
||||
bestdist = disttocluster;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
out->z = out->y = out->x = 0;
|
||||
}
|
||||
|
||||
return N;
|
||||
}
|
||||
|
||||
INT32 K_CountNeighboringPlayers(player_t *sourcePlayer, fixed_t eps, vector3_t *out)
|
||||
{
|
||||
// Dummy vector so the game doesn't SIGSEGV.
|
||||
// There's probably a better solution to this.
|
||||
std::vector<player_t *> dummy = {0};
|
||||
|
||||
return K_CountNeighboringPlayersByDistance(sourcePlayer, eps, out, &dummy);
|
||||
}
|
||||
|
||||
}
|
||||
18
src/k_cluster.hpp
Normal file
18
src/k_cluster.hpp
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#include "doomdef.h"
|
||||
#include "d_player.h"
|
||||
#include "m_fixed.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
extern player_t *closesttocluster;
|
||||
|
||||
INT32 K_CountNeighboringPlayersByDistance(player_t *sourcePlayer, fixed_t eps, vector3_t *out, void *nodevec);
|
||||
INT32 K_CountNeighboringPlayersByDTF(player_t *sourcePlayer, fixed_t eps, vector3_t *out);
|
||||
INT32 K_CountNeighboringPlayers(player_t *sourcePlayer, fixed_t eps, vector3_t *out);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
@ -695,8 +695,12 @@ 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);
|
||||
// Rim suggestion: Flipover damage is negligible at best, just cull it from Invincibility as a whole.
|
||||
if (invintype == KARTINVIN_LEGACY)
|
||||
{
|
||||
t1Condition = (t1->player->invincibilitytimer > 0);
|
||||
t2Condition = (t2->player->invincibilitytimer > 0);
|
||||
}
|
||||
|
||||
if ((t1Condition == true || flameT1 == true) && (t2Condition == true || flameT2 == true))
|
||||
{
|
||||
|
|
@ -704,15 +708,18 @@ boolean K_PvPTouchDamage(mobj_t *t1, mobj_t *t2)
|
|||
K_DoInstashield(t2->player);
|
||||
return false;
|
||||
}
|
||||
else if (t1Condition == true && t2Condition == false)
|
||||
else if (invintype == KARTINVIN_LEGACY)
|
||||
{
|
||||
P_DamageMobj(t2, t1, t1, 1, DMG_WIPEOUT);
|
||||
return true;
|
||||
}
|
||||
else if (t1Condition == false && t2Condition == true)
|
||||
{
|
||||
P_DamageMobj(t1, t2, t2, 1, DMG_WIPEOUT);
|
||||
return true;
|
||||
if (t1Condition == true && t2Condition == false)
|
||||
{
|
||||
P_DamageMobj(t2, t1, t1, 1, DMG_WIPEOUT);
|
||||
return true;
|
||||
}
|
||||
else if (t1Condition == false && t2Condition == true)
|
||||
{
|
||||
P_DamageMobj(t1, t2, t2, 1, DMG_WIPEOUT);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
t1Condition = (t1->scale > t2->scale + (mapobjectscale/8));
|
||||
|
|
|
|||
124
src/k_hud.c
124
src/k_hud.c
|
|
@ -1249,6 +1249,22 @@ static void K_drawKartItem(void)
|
|||
else
|
||||
localpatch = kp_nodraw;
|
||||
}
|
||||
else if ((stplyr->invincibilitytimer) && (invintype == KARTINVIN_ALTERN))
|
||||
{
|
||||
itembar = stplyr->invincibilitytimer;
|
||||
maxl = max(1, stplyr->maxinvincibilitytime);
|
||||
|
||||
if (stplyr->invincibilitycancel > 0)
|
||||
{
|
||||
flamebar = stplyr->invincibilitycancel;
|
||||
flamemaxl = 26;
|
||||
}
|
||||
|
||||
if (leveltime & 1)
|
||||
localpatch = localinv;
|
||||
else
|
||||
localpatch = kp_nodraw;
|
||||
}
|
||||
else if (K_GetShieldFromPlayer(stplyr) == KSHIELD_BUBBLE)
|
||||
{
|
||||
localpatch = kp_bubbleshield[offset];
|
||||
|
|
@ -1392,7 +1408,7 @@ static void K_drawKartItem(void)
|
|||
|
||||
//V_ClearClipRect();
|
||||
|
||||
// Extensible meter, currently used by Grow, Rocket Sneakers and Flame Shield
|
||||
// Extensible meter, currently used by Invincibilty, Grow, Rocket Sneakers and Flame Shield
|
||||
if (itembar)
|
||||
{
|
||||
const INT32 fill = ((itembar*barlength)/maxl);
|
||||
|
|
@ -1758,6 +1774,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 +1794,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 +1881,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 +1889,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) && (invintype == KARTINVIN_ALTERN))
|
||||
{
|
||||
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 +3473,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 +3536,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 +3584,8 @@ static void K_drawKartMinimap(void)
|
|||
if (!minimaptrans)
|
||||
return;
|
||||
|
||||
colorizeplayer = false;
|
||||
|
||||
minimaptrans = ((10-minimaptrans)<<FF_TRANSSHIFT);
|
||||
|
||||
if (encoremode)
|
||||
|
|
@ -3618,12 +3671,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)) || (invintype == KARTINVIN_LEGACY)))
|
||||
{
|
||||
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 +3834,26 @@ 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)) || (invintype == KARTINVIN_LEGACY)))
|
||||
{
|
||||
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 +3920,11 @@ static void K_drawKartMinimap(void)
|
|||
K_drawKartMinimapWaypoint(stplyr->nextwaypoint, x, y, splitflags);
|
||||
}
|
||||
}
|
||||
|
||||
if (cv_kartdebugcluster.value != 0)
|
||||
{
|
||||
K_drawKartMinimapCluster(x, y, splitflags);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -4772,11 +4856,12 @@ static void K_drawDistributionDebugger(void)
|
|||
INT32 amount;
|
||||
|
||||
if (K_UsingLegacyCheckpoints())
|
||||
itemodds = K_KartGetLegacyItemOdds(useodds, item, 0, spbrush);
|
||||
itemodds = K_KartGetLegacyItemOdds(useodds, item, stplyr->distancefromcluster, 0, spbrush);
|
||||
else
|
||||
itemodds = K_KartGetItemOdds(
|
||||
useodds, item,
|
||||
stplyr->distancetofinish,
|
||||
stplyr->distancefromcluster,
|
||||
0,
|
||||
spbrush, stplyr->bot, (stplyr->bot && stplyr->botvars.rival)
|
||||
);
|
||||
|
|
@ -4852,6 +4937,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)
|
||||
|
|
@ -5111,5 +5218,6 @@ void K_drawKartHUD(void)
|
|||
|
||||
K_DrawWaypointDebugger();
|
||||
K_DrawBotDebugger();
|
||||
K_DrawClusterDebugger();
|
||||
K_DrawDirectorDebugger();
|
||||
}
|
||||
|
|
|
|||
616
src/k_kart.c
616
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;
|
||||
|
|
@ -267,6 +276,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);
|
||||
|
|
@ -301,8 +311,10 @@ void K_RegisterKartStuff(void)
|
|||
CV_RegisterVar(&cv_kartstacking_sneaker_maxgrade);
|
||||
CV_RegisterVar(&cv_kartstacking_sneaker_stackable);
|
||||
|
||||
CV_RegisterVar(&cv_kartstacking_invincibility_speedboost);
|
||||
CV_RegisterVar(&cv_kartstacking_invincibility_accelboost);
|
||||
CV_RegisterVar(&cv_kartstacking_invincibility_legacyspeedboost);
|
||||
CV_RegisterVar(&cv_kartstacking_invincibility_legacyaccelboost);
|
||||
CV_RegisterVar(&cv_kartstacking_invincibility_alternatespeedboost);
|
||||
CV_RegisterVar(&cv_kartstacking_invincibility_alternateaccelboost);
|
||||
CV_RegisterVar(&cv_kartstacking_invincibility_stackable);
|
||||
|
||||
CV_RegisterVar(&cv_kartstacking_grow_speedboost);
|
||||
|
|
@ -341,6 +353,13 @@ void K_RegisterKartStuff(void)
|
|||
|
||||
CV_RegisterVar(&cv_kartslipdash);
|
||||
|
||||
CV_RegisterVar(&cv_kartinvintype);
|
||||
CV_RegisterVar(&cv_kartinvindist);
|
||||
CV_RegisterVar(&cv_kartinvindistmul);
|
||||
|
||||
CV_RegisterVar(&cv_kartinvin_maxtime);
|
||||
CV_RegisterVar(&cv_kartinvin_midtime);
|
||||
|
||||
CV_RegisterVar(&cv_kartdriftsounds);
|
||||
CV_RegisterVar(&cv_kartdriftefx);
|
||||
CV_RegisterVar(&cv_driftsparkpulse);
|
||||
|
|
@ -427,10 +446,10 @@ consvar_t *KartItemCVars[NUMKARTRESULTS-1] =
|
|||
&cv_dualjawz
|
||||
};
|
||||
|
||||
#define NUMKARTODDS 80
|
||||
#define NUMKARTODDS (MAXODDS*10)
|
||||
|
||||
// Less ugly 2D arrays
|
||||
static UINT8 K_KartItemOddsRace[NUMKARTRESULTS-1][8] =
|
||||
static UINT8 K_KartItemOddsRace[NUMKARTRESULTS-1][MAXODDS] =
|
||||
{
|
||||
//B C D E F G H I
|
||||
{ 0, 0, 3, 3, 2, 0, 0, 0 }, // Sneaker
|
||||
|
|
@ -687,6 +706,38 @@ UINT32 K_ScaleItemDistance(UINT32 distance, UINT8 numPlayers, boolean spbrush)
|
|||
return distance;
|
||||
}
|
||||
|
||||
#define INVODDS 4
|
||||
#define INVINDESPERATION 4
|
||||
|
||||
static INT32 K_KartGetInvincibilityOdds(UINT32 dist)
|
||||
{
|
||||
if (dist < (INVINDIST/2))
|
||||
return 0;
|
||||
|
||||
INT32 finodds = 0;
|
||||
fixed_t fac = (min(32000, (fixed_t)dist) * FRACUNIT) / (INVINDIST);
|
||||
|
||||
if (fac > FRACUNIT)
|
||||
{
|
||||
// Desperation! Climb exponentially until Invincibility is practically guaranteed.
|
||||
fac = (((min(32000, (fixed_t)dist) * FRACUNIT) / (INVINDIST)) - FRACUNIT) >> 1;
|
||||
finodds = Easing_InCubic(fac, INVODDS, 20 * INVINDESPERATION);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (fac <= FRACHALF)
|
||||
{
|
||||
// Invincibility is practically useless at lower distances.
|
||||
// At below half, remove it from the item pool.
|
||||
return 0;
|
||||
}
|
||||
// Basic linear climb to "reasonable" odds.
|
||||
finodds = FixedMul(INVODDS, fac);
|
||||
}
|
||||
|
||||
return min(20 * INVINDESPERATION, finodds);
|
||||
}
|
||||
|
||||
/** \brief Item Roulette for Kart
|
||||
|
||||
\param player player object passed from P_KartPlayerThink
|
||||
|
|
@ -697,6 +748,7 @@ UINT32 K_ScaleItemDistance(UINT32 distance, UINT8 numPlayers, boolean spbrush)
|
|||
INT32 K_KartGetItemOdds(
|
||||
UINT8 pos, SINT8 item,
|
||||
UINT32 ourDist,
|
||||
UINT32 clusterDist,
|
||||
fixed_t mashed,
|
||||
boolean spbrush, boolean bot, boolean rival)
|
||||
{
|
||||
|
|
@ -747,7 +799,7 @@ INT32 K_KartGetItemOdds(
|
|||
}
|
||||
else if (gametyperules & GTR_RACEODDS)
|
||||
{
|
||||
I_Assert(pos < 8); // Ditto
|
||||
I_Assert(pos < MAXODDS); // Ditto
|
||||
newodds = K_KartItemOddsRace[item-1][pos];
|
||||
}
|
||||
else
|
||||
|
|
@ -833,6 +885,19 @@ INT32 K_KartGetItemOdds(
|
|||
notNearEnd = true;
|
||||
break;
|
||||
case KITEM_INVINCIBILITY:
|
||||
if (invintype == KARTINVIN_ALTERN)
|
||||
{
|
||||
// It's a power item, yes, but we don't want mashing to lessen
|
||||
// its chances, so we lie to the game's face.
|
||||
// Nonetheless, apply the start cooldown.
|
||||
cooldownOnStart = true;
|
||||
|
||||
// Unique odds for Invincibility.
|
||||
newodds = K_KartGetInvincibilityOdds(clusterDist);
|
||||
|
||||
newodds *= 4;
|
||||
break;
|
||||
}
|
||||
case KITEM_MINE:
|
||||
case KITEM_GROW:
|
||||
case KITEM_BUBBLESHIELD:
|
||||
|
|
@ -943,7 +1008,7 @@ INT32 K_KartGetItemOdds(
|
|||
return newodds;
|
||||
}
|
||||
|
||||
INT32 K_KartGetLegacyItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean spbrush)
|
||||
INT32 K_KartGetLegacyItemOdds(UINT8 pos, SINT8 item, fixed_t clusterDist, fixed_t mashed, boolean spbrush)
|
||||
{
|
||||
INT32 newodds;
|
||||
INT32 i;
|
||||
|
|
@ -1023,6 +1088,19 @@ INT32 K_KartGetLegacyItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean spb
|
|||
powerItem = true;
|
||||
break;
|
||||
case KITEM_INVINCIBILITY:
|
||||
if (invintype == KARTINVIN_ALTERN)
|
||||
{
|
||||
// It's a power item, yes, but we don't want mashing to lessen
|
||||
// its chances, so we lie to the game's face.
|
||||
// Nonetheless, apply the start cooldown.
|
||||
cooldownOnStart = true;
|
||||
|
||||
// Unique odds for Invincibility.
|
||||
newodds = K_KartGetInvincibilityOdds(clusterDist);
|
||||
|
||||
newodds *= 4;
|
||||
break;
|
||||
}
|
||||
case KITEM_MINE:
|
||||
case KITEM_GROW:
|
||||
case KITEM_BUBBLESHIELD:
|
||||
|
|
@ -1149,6 +1227,7 @@ UINT8 K_FindUseodds(player_t *player, fixed_t mashed, UINT32 pdis, UINT8 bestbum
|
|||
if (K_KartGetItemOdds(
|
||||
i, j,
|
||||
player->distancetofinish,
|
||||
player->distancefromcluster,
|
||||
mashed,
|
||||
spbrush, player->bot, (player->bot && player->botvars.rival)
|
||||
) > 0)
|
||||
|
|
@ -1249,7 +1328,7 @@ INT32 K_FindLegacyUseodds(player_t *player, fixed_t mashed, INT32 pingame, INT32
|
|||
for (j = 1; j < NUMKARTRESULTS; j++)
|
||||
{
|
||||
|
||||
if (K_KartGetLegacyItemOdds(i, j, mashed, spbrush) > 0)
|
||||
if (K_KartGetLegacyItemOdds(i, j, player->distancefromcluster, mashed, spbrush) > 0)
|
||||
{
|
||||
available = true;
|
||||
break;
|
||||
|
|
@ -1771,7 +1850,7 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd)
|
|||
{
|
||||
if (K_UsingLegacyCheckpoints())
|
||||
{
|
||||
spawnchance[i] = (totalspawnchance += K_KartGetLegacyItemOdds(useodds, i, mashed, spbrush));
|
||||
spawnchance[i] = (totalspawnchance += K_KartGetLegacyItemOdds(useodds, i, player->distancefromcluster, mashed, spbrush));
|
||||
|
||||
}
|
||||
else
|
||||
|
|
@ -1779,6 +1858,7 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd)
|
|||
spawnchance[i] = (totalspawnchance += K_KartGetItemOdds(
|
||||
useodds, i,
|
||||
player->distancetofinish,
|
||||
player->distancefromcluster,
|
||||
mashed,
|
||||
spbrush, player->bot, (player->bot && player->botvars.rival))
|
||||
);
|
||||
|
|
@ -1895,6 +1975,13 @@ fixed_t K_GetMobjWeight(mobj_t *mobj, mobj_t *against)
|
|||
return weight;
|
||||
}
|
||||
|
||||
// Checks if the bump interaction was a flip-over.
|
||||
static boolean K_CheckMobjFlippedOver(mobj_t* mobj1, mobj_t* mobj2)
|
||||
{
|
||||
return ((mobj1->player && (mobj1->player->pflags & PF_JUSTFLIPPED)) ||
|
||||
(mobj2->player && (mobj2->player->pflags & PF_JUSTFLIPPED)));
|
||||
}
|
||||
|
||||
boolean K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean solid)
|
||||
{
|
||||
mobj_t *fx;
|
||||
|
|
@ -1953,6 +2040,19 @@ boolean K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean sol
|
|||
return false;
|
||||
}
|
||||
|
||||
// Don't bump if you're flipping over
|
||||
if (mobj1->player && mobj1->player->flipovertimer && (!(mobj1->player->pflags & PF_JUSTFLIPPED)))
|
||||
{
|
||||
mobj1->player->justbumped = bumptime;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mobj2->player && mobj2->player->flipovertimer && (!(mobj2->player->pflags & PF_JUSTFLIPPED)))
|
||||
{
|
||||
mobj2->player->justbumped = bumptime;
|
||||
return false;
|
||||
}
|
||||
|
||||
mass1 = K_GetMobjWeight(mobj1, mobj2);
|
||||
|
||||
if (solid == true && mass1 > 0)
|
||||
|
|
@ -2039,7 +2139,7 @@ boolean K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean sol
|
|||
// Do the bump fx when we've CONFIRMED we can bump.
|
||||
if ((mobj1->player && mobj1->player->itemtype == KITEM_BUBBLESHIELD) || (mobj2->player && mobj2->player->itemtype == KITEM_BUBBLESHIELD))
|
||||
S_StartSound(mobj1, sfx_s3k44);
|
||||
else
|
||||
else if (!K_CheckMobjFlippedOver(mobj1, mobj2))
|
||||
S_StartSound(mobj1, sfx_s3k49);
|
||||
|
||||
fx = P_SpawnMobj(mobj1->x/2 + mobj2->x/2, mobj1->y/2 + mobj2->y/2, mobj1->z/2 + mobj2->z/2, MT_BUMP);
|
||||
|
|
@ -2064,6 +2164,15 @@ boolean K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean sol
|
|||
P_PlayerRingBurst(mobj1->player, 1);
|
||||
}
|
||||
|
||||
if (mobj1->player->pflags & PF_JUSTFLIPPED)
|
||||
{
|
||||
S_StartSound(mobj2, sfx_mdse8);
|
||||
mobj1->player->flipoverangle = R_PointToAngle2(0,0,mobj1->player->rmomx,mobj1->player->rmomy);
|
||||
P_InstaThrust(mobj1, mobj1->player->flipoverangle, FixedMul(FLIPOVERSPEED, mobj1->scale));
|
||||
mobj1->momz = FixedMul(FlipOverZMomentum(gravity), mobj1->scale);
|
||||
mobj1->player->pflags &= ~PF_JUSTFLIPPED;
|
||||
}
|
||||
|
||||
if (mobj1->player->spinouttimer)
|
||||
{
|
||||
mobj1->player->wipeoutslow = wipeoutslowtime+1;
|
||||
|
|
@ -2090,6 +2199,15 @@ boolean K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean sol
|
|||
mobj2->player->spinouttimer = max(wipeoutslowtime+1, mobj2->player->spinouttimer);
|
||||
//mobj2->player->spinouttype = KSPIN_WIPEOUT; // Enforce type
|
||||
}
|
||||
|
||||
if (mobj2->player->pflags & PF_JUSTFLIPPED)
|
||||
{
|
||||
S_StartSound(mobj1, sfx_mdse8);
|
||||
mobj2->player->flipoverangle = R_PointToAngle2(0,0,mobj2->player->rmomx,mobj2->player->rmomy);
|
||||
P_InstaThrust(mobj2, mobj2->player->flipoverangle, FixedMul(FLIPOVERSPEED, mobj2->scale));
|
||||
mobj2->momz = FixedMul(FlipOverZMomentum(gravity), mobj2->scale);
|
||||
mobj2->player->pflags &= ~PF_JUSTFLIPPED;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -2158,6 +2276,15 @@ 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)
|
||||
{
|
||||
// At 50% or lower Invincibility, offroad creeps up on you.
|
||||
fixed_t invinoffroad = (invintype == KARTINVIN_LEGACY) ? ((player->invincibilitytimer) ? FRACUNIT : 0) : min(FRACUNIT, K_InvincibilityGradient(player->invincibilitytimer) << 1);
|
||||
fixed_t fac = CLAMP(FRACUNIT - invinoffroad, 0, FRACUNIT);
|
||||
|
||||
return FixedMul(offroad, fac);
|
||||
}
|
||||
|
||||
/** \brief Updates the Player's offroad value once per frame
|
||||
|
||||
\param player player object passed from K_KartPlayerThink
|
||||
|
|
@ -2179,6 +2306,9 @@ static void K_UpdateOffroad(player_t *player)
|
|||
offroadstrength = K_CheckOffroadCollide(player->mo);
|
||||
}
|
||||
|
||||
// Gradient our offroad strength.
|
||||
offroadstrength = K_OffroadGradient(player, offroadstrength);
|
||||
|
||||
// If you are in offroad, a timer starts.
|
||||
if (offroadstrength)
|
||||
{
|
||||
|
|
@ -3204,7 +3334,7 @@ boolean K_ApplyOffroad(player_t *player)
|
|||
if (modeattacking != ATTACKING_NONE)
|
||||
sneakertimer = player->sneakertimer > 0;
|
||||
|
||||
if (player->invincibilitytimer || player->hyudorotimer || sneakertimer)
|
||||
if (player->hyudorotimer || sneakertimer)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
|
@ -3523,6 +3653,74 @@ 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, (INVINMIDTIME/10));
|
||||
|
||||
return Easing_OutCubic(
|
||||
min(FRACUNIT, FixedMul(u - FRACUNIT, FRACUNIT/4)), (INVINMIDTIME/10), (INVINMAXTIME/10));
|
||||
}
|
||||
|
||||
|
||||
UINT16 K_GetInvincibilityTime(player_t *player)
|
||||
{
|
||||
UINT32 i, pingame;
|
||||
|
||||
if (invintype == KARTINVIN_LEGACY)
|
||||
return BASEINVINTIME;
|
||||
|
||||
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)
|
||||
{
|
||||
if (invintype == KARTINVIN_LEGACY)
|
||||
return INVINSPEEDBOOSTLGC;
|
||||
|
||||
fixed_t t = min(FRACUNIT, K_InvincibilityGradient(time));
|
||||
return Easing_OutCubic(t, 0, INVINSPEEDBOOSTALT);
|
||||
}
|
||||
|
||||
static fixed_t K_GetInvincibilityAccel(UINT16 time)
|
||||
{
|
||||
if (invintype == KARTINVIN_LEGACY)
|
||||
return INVINACCELBOOSTLGC;
|
||||
|
||||
fixed_t t = min(FRACUNIT, K_InvincibilityGradient(time));
|
||||
return Easing_OutCubic(t, 0, INVINACCELBOOSTALT);
|
||||
}
|
||||
|
||||
static fixed_t diminish(fixed_t speedboost)
|
||||
{
|
||||
return FixedSqrt(speedboost + DIMINISHPARAM) - FixedSqrt(DIMINISHPARAM);
|
||||
|
|
@ -3622,7 +3820,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
|
||||
|
|
@ -3877,7 +4077,10 @@ SINT8 K_GetForwardMove(player_t *player)
|
|||
}
|
||||
}
|
||||
|
||||
if ((player->exiting || mapreset) || player->pflags & PF_STASIS || player->spinouttimer) // pw_introcam?
|
||||
if ((player->exiting || mapreset) ||
|
||||
player->pflags & PF_STASIS ||
|
||||
player->spinouttimer ||
|
||||
player->flipovertimer) // pw_introcam?
|
||||
{
|
||||
forwardmove = 0;
|
||||
if (player->sneakertimer)
|
||||
|
|
@ -4087,6 +4290,19 @@ void K_SpinPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 typ
|
|||
P_SetPlayerMobjState(player->mo, S_KART_SPINOUT);
|
||||
}
|
||||
|
||||
void K_FlipPlayer(player_t *player, mobj_t *inflictor, mobj_t *source)
|
||||
{
|
||||
(void)inflictor;
|
||||
(void)source;
|
||||
|
||||
K_DirectorFollowAttack(player, inflictor, source);
|
||||
|
||||
K_StatPlayerHit(player, source ? source->player : NULL);
|
||||
player->flipovertimer = 1;
|
||||
player->pflags |= PF_JUSTFLIPPED;
|
||||
P_SetPlayerMobjState(player->mo, S_KART_SPINOUT);
|
||||
}
|
||||
|
||||
static void K_RemoveGrowShrink(player_t *player)
|
||||
{
|
||||
if (player->mo && !P_MobjWasRemoved(player->mo))
|
||||
|
|
@ -4193,6 +4409,11 @@ INT32 K_ExplodePlayer(player_t *player, mobj_t *inflictor, mobj_t *source) // A
|
|||
return ringburst;
|
||||
}
|
||||
|
||||
boolean K_IsPlayerDamaged(player_t *player)
|
||||
{
|
||||
return ((player->spinouttimer > 0) || (player->flipovertimer > 0));
|
||||
}
|
||||
|
||||
void K_HandleBumperChanges(player_t *player, UINT8 prevBumpers)
|
||||
{
|
||||
if (!(gametyperules & GTR_BUMPERS))
|
||||
|
|
@ -5741,9 +5962,32 @@ 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);
|
||||
|
||||
if (invintype == KARTINVIN_ALTERN)
|
||||
{
|
||||
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;
|
||||
if (invintype == KARTINVIN_ALTERN)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
|
||||
player->maxinvincibilitytime = time;
|
||||
|
||||
if (P_IsLocalPlayer(player) == true)
|
||||
{
|
||||
|
|
@ -6038,6 +6282,7 @@ mobj_t *K_CreatePaperItem(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8
|
|||
spawnchance[i] = (totalspawnchance += K_KartGetItemOdds(
|
||||
useodds, i,
|
||||
UINT32_MAX,
|
||||
0,
|
||||
0,
|
||||
false, false, false
|
||||
)
|
||||
|
|
@ -7397,7 +7642,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
|
|||
K_UpdateEngineSounds(player); // Thanks, VAda!
|
||||
|
||||
// update boost angle if not spun out
|
||||
if (!player->spinouttimer && !player->wipeoutslow)
|
||||
if (!K_IsPlayerDamaged(player) && !player->wipeoutslow)
|
||||
player->boostangle = player->mo->angle;
|
||||
|
||||
K_GetKartBoostPower(player);
|
||||
|
|
@ -7472,7 +7717,8 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
|
|||
// Make ABSOLUTELY SURE that your flashing tics don't get set WHILE you're still in hit animations.
|
||||
if (player->spinouttimer != 0
|
||||
|| player->wipeoutslow != 0
|
||||
|| player->squishedtimer != 0)
|
||||
|| player->squishedtimer != 0
|
||||
|| player->flipovertimer != 0)
|
||||
{
|
||||
player->flashing = K_GetKartFlashing(player);
|
||||
}
|
||||
|
|
@ -7481,6 +7727,22 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
|
|||
player->flashing--;
|
||||
}
|
||||
|
||||
if (player->flipovertimer)
|
||||
{
|
||||
player->flipovertimer++;
|
||||
|
||||
// Kill off any extra damage values.
|
||||
player->spinouttimer = 0;
|
||||
player->wipeoutslow = 0;
|
||||
|
||||
if (P_IsObjectOnGround(player->mo) &&
|
||||
((INT32)(player->flipovertimer - 1) > 2))
|
||||
{
|
||||
player->flipovertimer = 0;
|
||||
player->mo->rollangle = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (player->spinouttimer)
|
||||
{
|
||||
if ((P_IsObjectOnGround(player->mo)
|
||||
|
|
@ -7573,7 +7835,34 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
|
|||
player->startboost = K_ChainOrDeincrementTime(player, player->startboost, 1, false);
|
||||
|
||||
if (player->invincibilitytimer)
|
||||
player->invincibilitytimer--;
|
||||
{
|
||||
INT16 invinfac = 1;
|
||||
if ((invintype == KARTINVIN_ALTERN) && ((INT16)(player->invincibilitybottleneck) != -1) && (player->invincibilitytimer > 2))
|
||||
{
|
||||
if (player->distancefromcluster < (INVINDIST >> 2))
|
||||
{
|
||||
player->invincibilitybottleneck = min(256, player->invincibilitybottleneck + 4);
|
||||
invinfac = FixedMul(8, max(0, min(FRACUNIT, FRACUNIT - (player->distancefromcluster / (INVINDIST >> 2)))));
|
||||
}
|
||||
else
|
||||
{
|
||||
player->invincibilitybottleneck = max(0, (INT32)(player->invincibilitybottleneck) - 2);
|
||||
}
|
||||
|
||||
player->invincibilitytimer = (UINT16)(max(2, (INT32)(player->invincibilitytimer) - max(1, FixedMul((UINT16)invinfac, player->invincibilitybottleneck << 8))));
|
||||
}
|
||||
else
|
||||
{
|
||||
player->invincibilitytimer--;
|
||||
player->invincibilitybottleneck = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
player->invincibilitybottleneck = 0; // No need for bottlenecking.
|
||||
player->maxinvincibilitytime = 0;
|
||||
player->invincibilitycancel = -1;
|
||||
}
|
||||
|
||||
if (player->checkskip)
|
||||
player->checkskip--;
|
||||
|
|
@ -7880,11 +8169,15 @@ void K_KartResetPlayerColor(player_t *player)
|
|||
{
|
||||
boolean skip = false;
|
||||
|
||||
fullbright = true;
|
||||
if (invintype == KARTINVIN_LEGACY)
|
||||
{
|
||||
fullbright = true;
|
||||
|
||||
player->mo->color = K_RainbowColor(leveltime / 2);
|
||||
player->mo->colorized = true;
|
||||
skip = true;
|
||||
player->mo->color = K_RainbowColor(leveltime / 2);
|
||||
player->mo->colorized = true;
|
||||
|
||||
skip = true;
|
||||
}
|
||||
|
||||
if (skip)
|
||||
{
|
||||
|
|
@ -9603,6 +9896,149 @@ 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);
|
||||
}
|
||||
|
||||
#define MINCLUSTERPLAYERS 6
|
||||
|
||||
static UINT32 K_UpdateDistanceFromCluster(player_t *player)
|
||||
{
|
||||
player_t *cluster_p;
|
||||
UINT32 i, pingame, first, second, tinypoptarget;
|
||||
INT32 divmul;
|
||||
|
||||
pingame = 0;
|
||||
|
||||
first = -1;
|
||||
second = -1;
|
||||
tinypoptarget = -1;
|
||||
|
||||
divmul = 1;
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (!playeringame[i] || players[i].spectator)
|
||||
continue;
|
||||
|
||||
if (!(gametyperules & GTR_BUMPERS) || players[i].bumper)
|
||||
pingame++;
|
||||
|
||||
if (players[i].mo && gametype == GT_RACE)
|
||||
{
|
||||
if (players[i].position == 1 && first == -1)
|
||||
first = i;
|
||||
else if (players[i].position == 2 && second == -1)
|
||||
second = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (pingame <= 1)
|
||||
{
|
||||
// There's only us around.
|
||||
player->invincibilitybottleneck = (UINT16)(-1); // No bottlenecking!
|
||||
return 0;
|
||||
}
|
||||
else if ((pingame > 3) && (pingame < MINCLUSTERPLAYERS))
|
||||
{
|
||||
// "Second place" in this case is actually second to last.
|
||||
// In very small lobbies, it's harder for players to cluster together.
|
||||
second = -1;
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (!playeringame[i] || players[i].spectator)
|
||||
continue;
|
||||
|
||||
if (players[i].mo && gametype == GT_RACE)
|
||||
{
|
||||
if (players[i].position == (pingame - 1) && second == -1)
|
||||
{
|
||||
second = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tinypoptarget = (pingame < 3) ? first : second;
|
||||
|
||||
if (K_UsingLegacyCheckpoints() && !(gametype == GT_BATTLE))
|
||||
{
|
||||
// Compare yourself against the cluster player to determine the distance.
|
||||
|
||||
if ((pingame < MINCLUSTERPLAYERS) && (tinypoptarget != -1))
|
||||
{
|
||||
// In very small lobbies, the cluster player is first place/second to last.
|
||||
// Half the cluster distance so that the losing player
|
||||
// doesn't get overly pitied.
|
||||
cluster_p = &players[tinypoptarget];
|
||||
divmul = 2;
|
||||
}
|
||||
else if (clusterid != -1)
|
||||
{
|
||||
cluster_p = &players[clusterid];
|
||||
}
|
||||
|
||||
if ((clusterid == -1) || (!cluster_p))
|
||||
return 0; // No cluster player; not good!
|
||||
|
||||
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*divmul);
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((pingame < MINCLUSTERPLAYERS) && (tinypoptarget != -1))
|
||||
{
|
||||
// In very small lobbies, compare against first place/second to last's
|
||||
// distance to finish.
|
||||
// Half the cluster distance so that the losing player
|
||||
// doesn't get overly pitied.
|
||||
|
||||
// In a 1v1, this is theoretically impossible; but what do I know?
|
||||
if (player->distancetofinish <= players[tinypoptarget].distancetofinish)
|
||||
return 0; // Ahead, or tying.
|
||||
|
||||
return (player->distancetofinish - players[tinypoptarget].distancetofinish) / 2;
|
||||
}
|
||||
|
||||
UINT32 targetdtf;
|
||||
|
||||
if (clusterid != -1)
|
||||
{
|
||||
// Use the cluster player's DtF.
|
||||
targetdtf = players[clusterid].distancetofinish;
|
||||
}
|
||||
else
|
||||
{
|
||||
targetdtf = (UINT32)(clusterdtf.x);
|
||||
}
|
||||
|
||||
if (player->distancetofinish <= targetdtf)
|
||||
return 0; // Ahead, or tying.
|
||||
|
||||
// Return the difference between us and the cluster distance.
|
||||
return (player->distancetofinish - targetdtf);
|
||||
}
|
||||
}
|
||||
|
||||
#undef MINCLUSTERPLAYERS
|
||||
|
||||
//
|
||||
// K_KartUpdatePosition
|
||||
//
|
||||
|
|
@ -9780,10 +10216,99 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
if (bestdensity)
|
||||
{
|
||||
// Only find the cluster player if a cluster's populated.
|
||||
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
|
||||
|
|
@ -9814,6 +10339,9 @@ void K_UpdateAllPlayerPositions(void)
|
|||
K_KartLegacyUpdatePosition(&players[i]);
|
||||
else
|
||||
K_KartUpdatePosition(&players[i]);
|
||||
|
||||
// Get the cluster distance.
|
||||
players[i].distancefromcluster = K_UpdateDistanceFromCluster(&players[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9853,6 +10381,8 @@ void K_StripOther(player_t *player)
|
|||
player->roulettetype = KROULETTETYPE_NORMAL;
|
||||
|
||||
player->invincibilitytimer = 0;
|
||||
player->invincibilitycancel = -1;
|
||||
|
||||
if (player->growshrinktimer)
|
||||
{
|
||||
K_RemoveGrowShrink(player);
|
||||
|
|
@ -10008,12 +10538,12 @@ static void K_AdjustPlayerFriction(player_t *player)
|
|||
player->mo->movefactor = 32;
|
||||
}
|
||||
|
||||
// Wipeout slowdown
|
||||
if (player->speed > 0 && player->spinouttimer && player->wipeoutslow)
|
||||
// Wipeout slowdown, or getting flipped over
|
||||
if ((player->speed > 0 && player->spinouttimer && player->wipeoutslow) || (player->flipovertimer))
|
||||
{
|
||||
if (player->offroad)
|
||||
player->mo->friction -= 4912;
|
||||
if (player->wipeoutslow == 1)
|
||||
if ((player->wipeoutslow == 1) || (player->flipovertimer))
|
||||
player->mo->friction -= 9824;
|
||||
}
|
||||
}
|
||||
|
|
@ -10037,6 +10567,14 @@ void K_UnsetItemOut(player_t *player)
|
|||
player->bananadrag = 0;
|
||||
}
|
||||
|
||||
static boolean K_InvincibilitySlotHogging(player_t *player)
|
||||
{
|
||||
if (invintype == KARTINVIN_LEGACY)
|
||||
return false;
|
||||
|
||||
return ((player->invincibilitytimer) != 0);
|
||||
}
|
||||
|
||||
//
|
||||
// K_MoveKartPlayer
|
||||
//
|
||||
|
|
@ -10247,6 +10785,32 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
player->growcancel = 0;
|
||||
}
|
||||
}
|
||||
// Invincibility
|
||||
else if (K_InvincibilitySlotHogging(player))
|
||||
{
|
||||
// (Alternate) Cancel Invincibility
|
||||
if (player->invincibilitycancel >= 0)
|
||||
{
|
||||
if (buttons & BT_ATTACK)
|
||||
{
|
||||
player->invincibilitycancel++;
|
||||
if (player->invincibilitycancel > 25)
|
||||
{
|
||||
// Don't fully cancel due to how the music handling works.
|
||||
player->invincibilitytimer = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
player->invincibilitycancel = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((buttons & BT_ATTACK) || (player->oldcmd.buttons & BT_ATTACK))
|
||||
player->invincibilitycancel = -1;
|
||||
else
|
||||
player->invincibilitycancel = 0;
|
||||
}
|
||||
}
|
||||
else if (player->itemamount == 0)
|
||||
{
|
||||
K_UnsetItemOut(player);
|
||||
|
|
@ -10299,9 +10863,9 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
}
|
||||
break;
|
||||
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
|
||||
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
|
||||
{
|
||||
K_DoInvincibility(player, 10 * TICRATE);
|
||||
K_DoInvincibility(player, K_GetInvincibilityTime(player));
|
||||
K_PlayPowerGloatSound(player->mo);
|
||||
player->itemamount--;
|
||||
player->botvars.itemconfirm = 0;
|
||||
|
|
|
|||
55
src/k_kart.h
55
src/k_kart.h
|
|
@ -32,11 +32,51 @@ 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 (336<<FRACBITS)
|
||||
|
||||
// Time (in tics) of a flipover.
|
||||
// (TICRATE * (0.56666 * FRACUNIT))
|
||||
#define FLIPOVERTICS 19
|
||||
#define FLIPOVERHALFTICS (FLIPOVERTICS>>1)
|
||||
|
||||
#define FLIPOVER_90DEGTIME ((FLIPOVERTICS>>2)+1) // 0.1666 * FRACUNIT
|
||||
|
||||
// Angle movement for a flipover
|
||||
// ANGLE_90 / FLIPOVER_90DEGTIME
|
||||
// Precalculating it here for some slight optimization.
|
||||
#define FLIPOVERANG 0x0CCCCCCC
|
||||
|
||||
#define FLIPOVERSPEED (FLIPOVERDIST / FLIPOVERTICS)
|
||||
|
||||
#define FLIPOVERHEIGHT (32 << FRACBITS)
|
||||
|
||||
// By the middle of a jump arc, you'll reach the apex.
|
||||
#define FlipOverZMomentum(grav) \
|
||||
((FLIPOVERHEIGHT + \
|
||||
((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;
|
||||
|
||||
// :)
|
||||
#define MAXODDS 8
|
||||
|
||||
// Invincibility-related constants
|
||||
#define INVINDIST K_RAGuard(cv_kartinvindist)
|
||||
#define INVINDISTMUL K_RAGuard(cv_kartinvindistmul)
|
||||
#define INVINMIDTIME K_RAGuard(cv_kartinvin_midtime)
|
||||
#define INVINMAXTIME K_RAGuard(cv_kartinvin_maxtime)
|
||||
|
||||
// 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)
|
||||
|
|
@ -53,8 +93,10 @@ 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 INVINSPEEDBOOST K_RAGuard(cv_kartstacking_invincibility_speedboost)
|
||||
#define INVINACCELBOOST K_RAGuard(cv_kartstacking_invincibility_accelboost)
|
||||
#define INVINSPEEDBOOSTLGC K_RAGuard(cv_kartstacking_invincibility_legacyspeedboost)
|
||||
#define INVINACCELBOOSTLGC K_RAGuard(cv_kartstacking_invincibility_legacyaccelboost)
|
||||
#define INVINSPEEDBOOSTALT K_RAGuard(cv_kartstacking_invincibility_alternatespeedboost)
|
||||
#define INVINACCELBOOSTALT K_RAGuard(cv_kartstacking_invincibility_alternateaccelboost)
|
||||
#define INVINSTACKABLE K_RAGuard(cv_kartstacking_invincibility_stackable)
|
||||
#define GROWSPEEDBOOST K_RAGuard(cv_kartstacking_grow_speedboost)
|
||||
#define GROWACCELBOOST K_RAGuard(cv_kartstacking_grow_accelboost)
|
||||
|
|
@ -124,8 +166,8 @@ UINT8 K_FindUseodds(player_t *player, fixed_t mashed, UINT32 pdis, UINT8 bestbum
|
|||
INT32 K_FindLegacyUseodds(player_t *player, fixed_t mashed, INT32 pingame, INT32 bestbumper, boolean spbrush, boolean dontforcespb);
|
||||
fixed_t K_ItemOddsScale(UINT8 numPlayers, boolean spbrush);
|
||||
UINT32 K_ScaleItemDistance(UINT32 distance, UINT8 numPlayers, boolean spbrush);
|
||||
INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, UINT32 ourDist, fixed_t mashed, boolean spbrush, boolean bot, boolean rival);
|
||||
INT32 K_KartGetLegacyItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean spbrush);
|
||||
INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, UINT32 ourDist, UINT32 clusterDist, fixed_t mashed, boolean spbrush, boolean bot, boolean rival);
|
||||
INT32 K_KartGetLegacyItemOdds(UINT8 pos, SINT8 item, fixed_t clusterDist, fixed_t mashed, boolean spbrush);
|
||||
INT32 K_GetRollingRouletteItem(player_t *player);
|
||||
INT32 K_GetShieldFromPlayer(player_t *player);
|
||||
INT32 K_GetShieldFromItem(INT32 item);
|
||||
|
|
@ -153,7 +195,9 @@ void K_AwardPlayerRings(player_t *player, UINT16 rings, boolean overload);
|
|||
void K_DoInstashield(player_t *player);
|
||||
void K_BattleAwardHit(player_t *player, player_t *victim, mobj_t *inflictor, UINT8 bumpersRemoved);
|
||||
void K_SpinPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 type);
|
||||
void K_FlipPlayer(player_t *player, mobj_t *inflictor, mobj_t *source);
|
||||
INT32 K_ExplodePlayer(player_t *player, mobj_t *inflictor, mobj_t *source);
|
||||
boolean K_IsPlayerDamaged(player_t *player);
|
||||
void K_HandleBumperChanges(player_t *player, UINT8 prevBumpers);
|
||||
void K_DestroyBumpers(player_t *player, UINT8 amount);
|
||||
void K_TakeBumpersFromPlayer(player_t *player, player_t *victim, UINT8 amount);
|
||||
|
|
@ -178,6 +222,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);
|
||||
|
|
@ -197,6 +243,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);
|
||||
|
|
|
|||
|
|
@ -31,7 +31,9 @@ void K_StatTicker(void)
|
|||
if (p->position == spbplace)
|
||||
kartstats.spbtargettime++;
|
||||
|
||||
if (max(p->spinouttimer, p->wipeoutslow) > 0)
|
||||
if (p->flipovertimer > 0)
|
||||
kartstats.spinouttime++;
|
||||
else if (max(p->spinouttimer, p->wipeoutslow) > 0)
|
||||
kartstats.spinouttime++;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@ extern "C" {
|
|||
\brief units of the fraction
|
||||
*/
|
||||
#define FRACUNIT (1<<FRACBITS)
|
||||
#define FRACHALF (1<<(FRACBITS>>1))
|
||||
#define FRACMASK (FRACUNIT -1)
|
||||
/** \brief Redefinition of INT32 as fixed_t
|
||||
unit used as fixed_t
|
||||
|
|
|
|||
|
|
@ -11324,7 +11324,7 @@ void A_RandomShadowFrame(mobj_t *actor)
|
|||
if (actor->target && !actor->target->player->flashing
|
||||
&& !actor->target->player->invincibilitytimer
|
||||
&& !actor->target->player->growshrinktimer
|
||||
&& !actor->target->player->spinouttimer
|
||||
&& !K_IsPlayerDamaged(actor->target->player)
|
||||
&& P_IsObjectOnGround(actor->target)
|
||||
&& actor->z == actor->target->z)
|
||||
{
|
||||
|
|
@ -11369,7 +11369,7 @@ void A_RoamingShadowThinker(mobj_t *actor)
|
|||
if (actor->target && !actor->target->player->flashing
|
||||
&& !actor->target->player->invincibilitytimer
|
||||
&& !actor->target->player->growshrinktimer
|
||||
&& !actor->target->player->spinouttimer)
|
||||
&& !K_IsPlayerDamaged(actor->target->player))
|
||||
{
|
||||
// send them flying and spawn the WIND!
|
||||
P_InstaThrust(actor->target, 0, 0);
|
||||
|
|
@ -11611,7 +11611,7 @@ void A_ReaperThinker(mobj_t *actor)
|
|||
if (!(actor->target == targetplayermo && actor->target && !actor->target->player->flashing
|
||||
&& !actor->target->player->invincibilitytimer
|
||||
&& !actor->target->player->growshrinktimer
|
||||
&& !actor->target->player->spinouttimer))
|
||||
&& !K_IsPlayerDamaged(actor->target->player)))
|
||||
P_SetTarget(&actor->target, actor->hnext);
|
||||
// if the above isn't correct, then we should go back to targetting waypoints or something.
|
||||
}
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ boolean P_CanPickupItem(player_t *player, UINT8 weapon)
|
|||
{
|
||||
// Invulnerable
|
||||
if (player->flashing > 0
|
||||
|| player->spinouttimer > 0
|
||||
|| K_IsPlayerDamaged(player)
|
||||
|| player->squishedtimer > 0
|
||||
|| player->invincibilitytimer > 0
|
||||
|| player->growshrinktimer > 0
|
||||
|
|
@ -146,6 +146,7 @@ boolean P_CanPickupItem(player_t *player, UINT8 weapon)
|
|||
if (player->stealingtimer || player->stolentimer
|
||||
|| player->rocketsneakertimer
|
||||
|| player->eggmanexplode
|
||||
|| ((invintype == KARTINVIN_ALTERN) && (player->invincibilitytimer))
|
||||
|| (player->growshrinktimer > 0)
|
||||
|| player->flametimer)
|
||||
return false;
|
||||
|
|
@ -2055,7 +2056,7 @@ static UINT8 P_ShouldHookDamage(mobj_t *target, mobj_t *inflictor, mobj_t *sourc
|
|||
|
||||
status = shouldDamage;
|
||||
|
||||
if (shouldSpin > 0 && ((type == DMG_NORMAL) || (type == DMG_WIPEOUT)))
|
||||
if (shouldSpin > 0 && ((type == DMG_NORMAL) || (type == DMG_WIPEOUT) || (type == DMG_FLIPOVER)))
|
||||
status = shouldSpin;
|
||||
else if (shouldExplode > 0 && ((type == DMG_EXPLODE) || (type == DMG_KARMA)))
|
||||
status = shouldExplode;
|
||||
|
|
@ -2266,7 +2267,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
|
|||
K_DestroyBumpers(player, 1);
|
||||
}
|
||||
|
||||
if (!(type == DMG_NORMAL || type == DMG_WIPEOUT || type == DMG_VOLTAGE))
|
||||
if (!(type == DMG_NORMAL || type == DMG_WIPEOUT || type == DMG_VOLTAGE || type == DMG_FLIPOVER))
|
||||
{
|
||||
player->sneakertimer = 0;
|
||||
player->mo->flags2 &= ~MF2_WATERRUN;
|
||||
|
|
@ -2345,6 +2346,12 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
|
|||
K_SquishPlayer(player, inflictor, source);
|
||||
LUA_HookPlayerSquish(target, inflictor, source, damage, damagetype);
|
||||
break;
|
||||
case DMG_FLIPOVER:
|
||||
if (P_IsDisplayPlayer(player))
|
||||
P_StartQuake(32<<FRACBITS, 5);
|
||||
K_FlipPlayer(player, inflictor, source);
|
||||
LUA_HookPlayerSpin(target, inflictor, source, damage, damagetype);
|
||||
K_KartPainEnergyFling(player);
|
||||
case DMG_WIPEOUT:
|
||||
if (P_IsDisplayPlayer(player))
|
||||
P_StartQuake(32<<FRACBITS, 5);
|
||||
|
|
|
|||
|
|
@ -570,12 +570,13 @@ struct BasicFF_t
|
|||
|
||||
/* Damage/death types, for P_DamageMobj and related */
|
||||
//// Damage types
|
||||
#define DMG_NORMAL 0x00
|
||||
#define DMG_WIPEOUT 0x01 // Normal, but with extra flashy effects
|
||||
#define DMG_EXPLODE 0x02
|
||||
#define DMG_SQUISH 0x03
|
||||
#define DMG_VOLTAGE 0x04 // Normal but for killing the spb
|
||||
#define DMG_KARMA 0x05 // Karma Bomb explosion -- works like DMG_EXPLODE, but steals half of their bumpers & deletes the rest
|
||||
#define DMG_NORMAL 0x00
|
||||
#define DMG_WIPEOUT 0x01 // Normal, but with extra flashy effects
|
||||
#define DMG_EXPLODE 0x02
|
||||
#define DMG_SQUISH 0x03
|
||||
#define DMG_VOLTAGE 0x04 // Normal but for killing the spb
|
||||
#define DMG_KARMA 0x05 // Karma Bomb explosion -- works like DMG_EXPLODE, but steals half of their bumpers & deletes the rest
|
||||
#define DMG_FLIPOVER 0x06 // Makes a player flip around in the air at a fixed speed.
|
||||
//// Death types - cannot be combined with damage types
|
||||
#define DMG_INSTAKILL 0x80
|
||||
#define DMG_DEATHPIT 0x81
|
||||
|
|
|
|||
10
src/p_map.c
10
src/p_map.c
|
|
@ -495,6 +495,14 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
|
|||
if ((thing->flags & MF_NOCLIPTHING) || !(thing->flags & (MF_SOLID|MF_SPECIAL|MF_PAIN|MF_SHOOTABLE|MF_SPRING)))
|
||||
return BMIT_CONTINUE;
|
||||
|
||||
// Get rid of the "just flipped" flag.
|
||||
// We only use that to confirm flipover hits.
|
||||
if (thing->player)
|
||||
thing->player->pflags &= ~PF_JUSTFLIPPED;
|
||||
|
||||
if (g_tm.thing->player)
|
||||
g_tm.thing->player->pflags &= ~PF_JUSTFLIPPED;
|
||||
|
||||
blockdist = thing->radius + g_tm.thing->radius;
|
||||
|
||||
if (abs(thing->x - g_tm.x) >= blockdist || abs(thing->y - g_tm.y) >= blockdist)
|
||||
|
|
@ -1267,7 +1275,7 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
|
|||
}
|
||||
|
||||
// no interaction
|
||||
if (g_tm.thing->player->flashing > 0 || g_tm.thing->player->hyudorotimer > 0 || g_tm.thing->player->spinouttimer > 0)
|
||||
if (g_tm.thing->player->flashing > 0 || g_tm.thing->player->hyudorotimer > 0 || K_IsPlayerDamaged(g_tm.thing->player))
|
||||
return BMIT_CONTINUE;
|
||||
|
||||
// collide
|
||||
|
|
|
|||
78
src/p_mobj.c
78
src/p_mobj.c
|
|
@ -2765,7 +2765,7 @@ void P_PlayerZMovement(mobj_t *mo)
|
|||
K_UpdateMobjTerrain(mo, (mo->eflags & MFE_VERTICALFLIP ? g_tm.ceilingpic : g_tm.floorpic));
|
||||
|
||||
// Get up if you fell.
|
||||
if (mo->player->panim == PA_HURT && mo->player->spinouttimer == 0 && mo->player->squishedtimer == 0)
|
||||
if (mo->player->panim == PA_HURT && !K_IsPlayerDamaged(mo->player) && mo->player->squishedtimer == 0)
|
||||
{
|
||||
P_SetPlayerMobjState(mo, S_KART_STILL);
|
||||
}
|
||||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -256,6 +256,8 @@ static void P_NetArchivePlayers(savebuffer_t *save)
|
|||
WRITEUINT16(save->p, players[i].flashing);
|
||||
WRITEUINT16(save->p, players[i].spinouttimer);
|
||||
WRITEUINT8(save->p, players[i].spinouttype);
|
||||
WRITEUINT16(save->p, players[i].flipovertimer);
|
||||
WRITEANGLE(save->p, players[i].flipoverangle);
|
||||
WRITEINT16(save->p, players[i].squishedtimer);
|
||||
WRITEUINT8(save->p, players[i].instashield);
|
||||
WRITEUINT8(save->p, players[i].wipeoutslow);
|
||||
|
|
@ -341,8 +343,13 @@ static void P_NetArchivePlayers(savebuffer_t *save)
|
|||
|
||||
WRITEINT16(save->p, players[i].growshrinktimer);
|
||||
WRITEINT16(save->p, players[i].growcancel);
|
||||
|
||||
WRITEUINT16(save->p, players[i].rocketsneakertimer);
|
||||
|
||||
WRITEUINT16(save->p, players[i].invincibilitytimer);
|
||||
WRITEUINT16(save->p, players[i].maxinvincibilitytime);
|
||||
WRITEUINT16(save->p, players[i].invincibilitybottleneck);
|
||||
WRITEINT16(save->p, players[i].invincibilitycancel);
|
||||
|
||||
WRITEUINT8(save->p, players[i].eggmanexplode);
|
||||
WRITESINT8(save->p, players[i].eggmanblame);
|
||||
|
|
@ -595,6 +602,8 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
|
|||
players[i].flashing = READUINT16(save->p);
|
||||
players[i].spinouttimer = READUINT16(save->p);
|
||||
players[i].spinouttype = READUINT8(save->p);
|
||||
players[i].flipoverangle = READUINT16(save->p);
|
||||
players[i].flipovertimer = READANGLE(save->p);
|
||||
players[i].squishedtimer = READINT16(save->p);
|
||||
players[i].instashield = READUINT8(save->p);
|
||||
players[i].wipeoutslow = READUINT8(save->p);
|
||||
|
|
@ -680,8 +689,13 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
|
|||
|
||||
players[i].growshrinktimer = READINT16(save->p);
|
||||
players[i].growcancel = READINT16(save->p);
|
||||
|
||||
players[i].rocketsneakertimer = READUINT16(save->p);
|
||||
|
||||
players[i].invincibilitytimer = READUINT16(save->p);
|
||||
players[i].maxinvincibilitytime = READUINT16(save->p);
|
||||
players[i].invincibilitybottleneck = READUINT16(save->p);
|
||||
players[i].invincibilitycancel = READINT16(save->p);
|
||||
|
||||
players[i].eggmanexplode = READUINT8(save->p);
|
||||
players[i].eggmanblame = READSINT8(save->p);
|
||||
|
|
|
|||
|
|
@ -7961,6 +7961,8 @@ static void P_InitLevelSettings(boolean reloadinggamestate)
|
|||
if (cv_kartslipdash.value)
|
||||
slipdashactive = true;
|
||||
|
||||
invintype = (UINT8)cv_kartinvintype.value;
|
||||
|
||||
// emerald hunt
|
||||
hunt1 = hunt2 = hunt3 = NULL;
|
||||
|
||||
|
|
@ -8293,6 +8295,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!
|
||||
|
|
|
|||
21
src/p_user.c
21
src/p_user.c
|
|
@ -464,7 +464,7 @@ UINT8 P_FindHighestLap(void)
|
|||
//
|
||||
boolean P_PlayerInPain(player_t *player)
|
||||
{
|
||||
if (player->spinouttimer || player->squishedtimer)
|
||||
if (K_IsPlayerDamaged(player) || player->squishedtimer)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
|
|
@ -1374,7 +1374,7 @@ boolean P_PlayerHitFloor(player_t *player, boolean fromAir)
|
|||
// Cut momentum in half when you hit the ground and
|
||||
// aren't pressing any controls.
|
||||
if (!(player->cmd.forwardmove || player->cmd.sidemove) && !player->cmomx && !player->cmomy
|
||||
&& !(player->spinouttimer))
|
||||
&& !K_IsPlayerDamaged(player))
|
||||
{
|
||||
player->mo->momx = player->mo->momx/2;
|
||||
player->mo->momy = player->mo->momy/2;
|
||||
|
|
@ -1806,7 +1806,7 @@ static void P_3dMovement(player_t *player)
|
|||
{
|
||||
movepushangle = player->mo->angle - (ANGLE_45/5) * player->drift;
|
||||
}
|
||||
else if (player->spinouttimer || player->wipeoutslow) // if spun out, use the boost angle
|
||||
else if (K_IsPlayerDamaged(player) || player->wipeoutslow) // if spun out, use the boost angle
|
||||
{
|
||||
movepushangle = (angle_t)player->boostangle;
|
||||
}
|
||||
|
|
@ -1896,13 +1896,13 @@ static void P_3dMovement(player_t *player)
|
|||
totalthrust.x += P_ReturnThrustX(player->mo, movepushangle, movepushforward);
|
||||
totalthrust.y += P_ReturnThrustY(player->mo, movepushangle, movepushforward);
|
||||
}
|
||||
else if (!(player->spinouttimer))
|
||||
else if (!K_IsPlayerDamaged(player))
|
||||
{
|
||||
K_MomentumToFacing(player);
|
||||
}
|
||||
|
||||
// Sideways movement
|
||||
if (cmd->sidemove != 0 && !((player->exiting || mapreset) || player->spinouttimer))
|
||||
if (cmd->sidemove != 0 && !((player->exiting || mapreset) || K_IsPlayerDamaged(player)))
|
||||
{
|
||||
if (cmd->sidemove > 0)
|
||||
movepushside = (cmd->sidemove * FRACUNIT/128) + FixedDiv(player->speed, K_GetKartSpeed(player, true, true));
|
||||
|
|
@ -2169,6 +2169,10 @@ void P_MovePlayer(player_t *player)
|
|||
player->glanceDir = 0;
|
||||
player->pflags &= ~PF_GAINAX;
|
||||
}
|
||||
else if (player->flipovertimer > 0)
|
||||
{
|
||||
player->mo->rollangle = (angle_t)(player->flipovertimer - 1) * FLIPOVERANG;
|
||||
}
|
||||
else if ((player->spinouttimer > 0))
|
||||
{
|
||||
UINT16 speed = player->spinouttimer/8;
|
||||
|
|
@ -2237,8 +2241,13 @@ void P_MovePlayer(player_t *player)
|
|||
&& onground && (leveltime & 1))
|
||||
K_SpawnBoostTrail(player);
|
||||
|
||||
if (player->invincibilitytimer > 0)
|
||||
if ((player->invincibilitytimer > 0) &&
|
||||
(((leveltime %
|
||||
max(1, 10 - FixedMul(9, K_InvincibilityGradient(player->invincibilitytimer)))) == 0) ||
|
||||
(invintype == KARTINVIN_LEGACY)))
|
||||
{
|
||||
K_SpawnSparkleTrail(player->mo);
|
||||
}
|
||||
|
||||
if (player->wipeoutslow > 1 && (leveltime & 1))
|
||||
K_SpawnWipeoutTrail(player->mo, false);
|
||||
|
|
|
|||
Loading…
Reference in a new issue