mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
Merge remote-tracking branch 'origin/master' into nerf-bumper-car-bots
This commit is contained in:
commit
457927a36a
46 changed files with 972 additions and 95 deletions
|
|
@ -240,7 +240,16 @@ public:
|
|||
|
||||
HashMap(HashMap&& r) noexcept
|
||||
{
|
||||
*this = std::move(r);
|
||||
buckets_ = r.buckets_;
|
||||
r.buckets_ = 0;
|
||||
size_ = r.size_;
|
||||
r.size_ = 0;
|
||||
heads_ = r.heads_;
|
||||
r.heads_ = nullptr;
|
||||
hasher_ = r.hasher_;
|
||||
r.hasher_ = {};
|
||||
key_equal_ = r.key_equal_;
|
||||
r.key_equal_ = {};
|
||||
};
|
||||
|
||||
~HashMap()
|
||||
|
|
|
|||
|
|
@ -974,6 +974,7 @@ consvar_t cv_dummymenuplayer = MenuDummy("dummymenuplayer", "P1").onchange(Dummy
|
|||
consvar_t cv_dummyprofileautoroulette = MenuDummy("dummyprofileautoroulette", "Off").on_off();
|
||||
consvar_t cv_dummyprofilefov = MenuDummy("dummyprofilefov", "100").min_max(70, 110);
|
||||
consvar_t cv_dummyprofilelitesteer = MenuDummy("dummyprofilelitesteer", "Off").on_off();
|
||||
consvar_t cv_dummyprofilestrictfastfall = MenuDummy("dummprofilestrictfastfall", "Off").on_off();
|
||||
consvar_t cv_dummyprofiledescriptiveinput = Player("dummyprofiledescriptiveinput", "Modern").values(descriptiveinput_cons_t);
|
||||
consvar_t cv_dummyprofileautoring = MenuDummy("dummyprofileautoring", "Off").on_off();
|
||||
consvar_t cv_dummyprofilekickstart = MenuDummy("dummyprofilekickstart", "Off").on_off();
|
||||
|
|
@ -1085,6 +1086,13 @@ consvar_t cv_litesteer[MAXSPLITSCREENPLAYERS] = {
|
|||
Player("litesteer4", "Off").on_off().onchange(weaponPrefChange4),
|
||||
};
|
||||
|
||||
consvar_t cv_strictfastfall[MAXSPLITSCREENPLAYERS] = {
|
||||
Player("strictfastfall", "Off").on_off().onchange(weaponPrefChange),
|
||||
Player("strictfastfall2", "Off").on_off().onchange(weaponPrefChange2),
|
||||
Player("strictfastfall3", "Off").on_off().onchange(weaponPrefChange3),
|
||||
Player("strictfastfall4", "Off").on_off().onchange(weaponPrefChange4),
|
||||
};
|
||||
|
||||
consvar_t cv_autoring[MAXSPLITSCREENPLAYERS] = {
|
||||
Player("autoring", "Off").on_off().onchange(weaponPrefChange),
|
||||
Player("autoring2", "Off").on_off().onchange(weaponPrefChange2),
|
||||
|
|
|
|||
|
|
@ -123,6 +123,11 @@ static tic_t freezetimeout[MAXNETNODES]; // Until when can this node freeze the
|
|||
// If higher than cv_joindelay * 2 (3 joins in a short timespan), joins are temporarily disabled.
|
||||
static tic_t joindelay = 0;
|
||||
|
||||
// Set when the server requests a signature with an IP mismatch.
|
||||
// This is a common issue on Hamachi, Radmin, and other VPN software that does noncompliant IP remap horeseshit.
|
||||
// When ths is set, provide only blank signatures. If this is set while joining, the server will degrade us to a GUEST.
|
||||
static boolean forceGuest = false;
|
||||
|
||||
UINT16 pingmeasurecount = 1;
|
||||
UINT32 realpingtable[MAXPLAYERS]; //the base table of ping where an average will be sent to everyone.
|
||||
UINT32 playerpingtable[MAXPLAYERS]; //table of player latency values.
|
||||
|
|
@ -276,9 +281,26 @@ shouldsign_t ShouldSignChallenge(uint8_t *message)
|
|||
if ((max(now, then) - min(now, then)) > 60*15)
|
||||
return SIGN_BADTIME;
|
||||
|
||||
// ____ _____ ___ ____ _
|
||||
// / ___|_ _/ _ \| _ \| |
|
||||
// \___ \ | || | | | |_) | |
|
||||
// ___) || || |_| | __/|_|
|
||||
// |____/ |_| \___/|_| (_)
|
||||
// =========================
|
||||
// SIGN_BADIP MUST BE CHECKED LAST, AND RETURN ONLY IF ALL OTHER CHECKS HAVE ALREADY SUCCEEDED.
|
||||
// We allow IP failures through for compatibility with shitty VPNs and fucked-beyond-belief home networks.
|
||||
// If this is checked before other sign-safety conditons, bad actors can INTENTIONALLY SKIP CHECKS.
|
||||
if (realIP != claimedIP && I_IsExternalAddress(&realIP))
|
||||
return SIGN_BADIP;
|
||||
|
||||
#ifdef DEVELOP
|
||||
if (cv_badip.value)
|
||||
{
|
||||
CV_AddValue(&cv_badip, -1);
|
||||
CONS_Alert(CONS_WARNING, "cv_badip enabled, intentionally failing checks\n");
|
||||
return SIGN_BADIP;
|
||||
}
|
||||
#endif
|
||||
// Do NOT return SIGN_BADIP before doing all other available checks.
|
||||
return SIGN_OK;
|
||||
}
|
||||
|
||||
|
|
@ -951,17 +973,19 @@ static boolean CL_SendJoin(void)
|
|||
// Don't leak old signatures from prior sessions.
|
||||
memset(&netbuffer->u.clientcfg.challengeResponse, 0, sizeof(((clientconfig_pak *)0)->challengeResponse));
|
||||
|
||||
forceGuest = false;
|
||||
|
||||
if (client && netgame)
|
||||
{
|
||||
shouldsign_t safe = ShouldSignChallenge(awaitingChallenge);
|
||||
|
||||
if (safe != SIGN_OK)
|
||||
if (safe == SIGN_BADIP)
|
||||
{
|
||||
if (safe == SIGN_BADIP)
|
||||
{
|
||||
I_Error("External server IP didn't match the message it sent.");
|
||||
}
|
||||
else if (safe == SIGN_BADTIME)
|
||||
forceGuest = true;
|
||||
}
|
||||
else if (safe != SIGN_OK)
|
||||
{
|
||||
if (safe == SIGN_BADTIME)
|
||||
{
|
||||
I_Error("External server sent a message with an unusual timestamp.\nMake sure your system time is set correctly.");
|
||||
}
|
||||
|
|
@ -978,7 +1002,7 @@ static boolean CL_SendJoin(void)
|
|||
uint8_t signature[SIGNATURELENGTH];
|
||||
profile_t *localProfile = PR_GetLocalPlayerProfile(i);
|
||||
|
||||
if (PR_IsLocalPlayerGuest(i)) // GUESTS don't have keys
|
||||
if (PR_IsLocalPlayerGuest(i) || forceGuest) // GUESTS don't have keys
|
||||
{
|
||||
memset(signature, 0, sizeof(signature));
|
||||
}
|
||||
|
|
@ -1450,6 +1474,9 @@ static void CL_LoadReceivedSavegame(boolean reloading)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (forceGuest)
|
||||
HU_AddChatText("\x85* This server uses a strange network configuration (VPN?). You have joined as a GUEST.", true);
|
||||
}
|
||||
|
||||
static void CL_ReloadReceivedSavegame(void)
|
||||
|
|
@ -4436,8 +4463,19 @@ static void HandleConnect(SINT8 node)
|
|||
|
||||
if (netgame && sigcheck != 0)
|
||||
{
|
||||
SV_SendRefuse(node, M_GetText("Signature verification failed."));
|
||||
return;
|
||||
uint8_t allZero[SIGNATURELENGTH];
|
||||
memset(allZero, 0, SIGNATURELENGTH);
|
||||
if (memcmp(netbuffer->u.clientcfg.challengeResponse[i], allZero, SIGNATURELENGTH) == 0)
|
||||
{
|
||||
// The connecting client didn't even try to sign this, probably due to an IP mismatch.
|
||||
// Let them in as a guest.
|
||||
memset(lastReceivedKey[node][i], 0, PUBKEYLENGTH);
|
||||
}
|
||||
else
|
||||
{
|
||||
SV_SendRefuse(node, M_GetText("Signature verification failed."));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -5864,11 +5902,11 @@ static void HandlePacketFromPlayer(SINT8 node)
|
|||
memcpy(lastChallengeAll, netbuffer->u.challengeall.secret, sizeof(lastChallengeAll));
|
||||
|
||||
shouldsign_t safe = ShouldSignChallenge(lastChallengeAll);
|
||||
if (safe != SIGN_OK)
|
||||
if (safe == SIGN_BADIP)
|
||||
forceGuest = true;
|
||||
else if (safe != SIGN_OK)
|
||||
{
|
||||
if (safe == SIGN_BADIP)
|
||||
HandleSigfail("External server sent the wrong IP");
|
||||
else if (safe == SIGN_BADTIME)
|
||||
if (safe == SIGN_BADTIME)
|
||||
HandleSigfail("Bad timestamp - is your time set correctly?");
|
||||
else
|
||||
HandleSigfail("Unknown auth error - contact a developer");
|
||||
|
|
@ -5893,7 +5931,7 @@ static void HandlePacketFromPlayer(SINT8 node)
|
|||
{
|
||||
uint8_t signature[SIGNATURELENGTH];
|
||||
profile_t *localProfile = PR_GetLocalPlayerProfile(challengeplayers);
|
||||
if (!PR_IsLocalPlayerGuest(challengeplayers)) // GUESTS don't have keys
|
||||
if (!PR_IsLocalPlayerGuest(challengeplayers) && !forceGuest) // GUESTS don't have keys
|
||||
{
|
||||
crypto_eddsa_sign(signature, localProfile->secret_key, lastChallengeAll, sizeof(lastChallengeAll));
|
||||
|
||||
|
|
|
|||
|
|
@ -1176,7 +1176,14 @@ enum {
|
|||
WP_ANALOGSTICK = 1<<3,
|
||||
WP_AUTORING = 1<<4,
|
||||
WP_SELFMUTE = 1<<5,
|
||||
WP_SELFDEAFEN = 1<<6
|
||||
WP_SELFDEAFEN = 1<<6,
|
||||
WP_STRICTFASTFALL = 1<<7,
|
||||
// WARNING: STUPID LEGACY TIMEWASTER AHEAD
|
||||
// IF YOU ARE ADDING OR MODIFYING WEAPONPREFS, YOU MUST
|
||||
// PRESERVE THEM IN G_PlayerReborn -- OTHERWISE THEY
|
||||
// WILL MYSTERIOUSLY VANISH AFTER ONE RACE
|
||||
//
|
||||
// HOURS LOST TO G_PlayerReborn: UNCOUNTABLE
|
||||
};
|
||||
|
||||
void WeaponPref_Send(UINT8 ssplayer)
|
||||
|
|
@ -1207,6 +1214,9 @@ void WeaponPref_Send(UINT8 ssplayer)
|
|||
prefs |= WP_SELFDEAFEN;
|
||||
}
|
||||
|
||||
if (cv_strictfastfall[ssplayer].value)
|
||||
prefs |= WP_STRICTFASTFALL;
|
||||
|
||||
UINT8 buf[2];
|
||||
buf[0] = prefs;
|
||||
buf[1] = cv_mindelay.value;
|
||||
|
|
@ -1235,6 +1245,9 @@ void WeaponPref_Save(UINT8 **cp, INT32 playernum)
|
|||
if (player->pflags & PF_AUTORING)
|
||||
prefs |= WP_AUTORING;
|
||||
|
||||
if (player->pflags & PF2_STRICTFASTFALL)
|
||||
prefs |= WP_STRICTFASTFALL;
|
||||
|
||||
WRITEUINT8(*cp, prefs);
|
||||
}
|
||||
|
||||
|
|
@ -1246,7 +1259,7 @@ size_t WeaponPref_Parse(const UINT8 *bufstart, INT32 playernum)
|
|||
UINT8 prefs = READUINT8(p);
|
||||
|
||||
player->pflags &= ~(PF_KICKSTARTACCEL|PF_SHRINKME|PF_AUTOROULETTE|PF_AUTORING);
|
||||
player->pflags2 &= ~(PF2_SELFMUTE | PF2_SELFDEAFEN);
|
||||
player->pflags2 &= ~(PF2_SELFMUTE | PF2_SELFDEAFEN | PF2_STRICTFASTFALL);
|
||||
|
||||
if (prefs & WP_KICKSTARTACCEL)
|
||||
player->pflags |= PF_KICKSTARTACCEL;
|
||||
|
|
@ -1271,6 +1284,9 @@ size_t WeaponPref_Parse(const UINT8 *bufstart, INT32 playernum)
|
|||
if (prefs & WP_SELFDEAFEN)
|
||||
player->pflags2 |= PF2_SELFDEAFEN;
|
||||
|
||||
if (prefs & WP_STRICTFASTFALL)
|
||||
player->pflags2 |= PF2_STRICTFASTFALL;
|
||||
|
||||
if (leveltime < 2)
|
||||
{
|
||||
// BAD HACK: No other place I tried to slot this in
|
||||
|
|
|
|||
|
|
@ -138,6 +138,7 @@ typedef enum
|
|||
PF2_SELFDEAFEN = 1<<2,
|
||||
PF2_SERVERMUTE = 1<<3,
|
||||
PF2_SERVERDEAFEN = 1<<4,
|
||||
PF2_STRICTFASTFALL = 1<<5,
|
||||
} pflags2_t;
|
||||
|
||||
typedef enum
|
||||
|
|
@ -725,9 +726,13 @@ struct player_t
|
|||
UINT8 noEbrakeMagnet; // Briefly disable 2.2 responsive ebrake if you're bumped by another player.
|
||||
UINT8 tumbleBounces;
|
||||
UINT16 tumbleHeight; // In *mobjscaled* fracunits, or mfu, not raw fu
|
||||
UINT16 stunned; // Number of tics during which rings cannot be picked up
|
||||
UINT8 stunnedCombo; // Number of hits sustained while stunned, reduces consecutive stun penalties
|
||||
UINT8 justDI; // Turn-lockout timer to briefly prevent unintended turning after DI, resets when actionable or no input
|
||||
boolean flipDI; // Bananas flip the DI direction. Was a bug, but it made bananas much more interesting.
|
||||
|
||||
UINT8 cangrabitems;
|
||||
|
||||
SINT8 drift; // (-5 to 5) - Drifting Left or Right, plus a bigger counter = sharper turn
|
||||
fixed_t driftcharge; // Charge your drift so you can release a burst of speed
|
||||
UINT16 driftboost; // (0 to 125 baseline) - Boost you get from drifting
|
||||
|
|
@ -778,6 +783,8 @@ struct player_t
|
|||
// Item held stuff
|
||||
SINT8 itemtype; // KITEM_ constant for item number
|
||||
UINT8 itemamount; // Amount of said item
|
||||
SINT8 backupitemtype;
|
||||
UINT8 backupitemamount;
|
||||
SINT8 throwdir; // Held dir of controls; 1 = forward, 0 = none, -1 = backward (was "player->heldDir")
|
||||
UINT8 itemscale; // Item scale value, from when an item was taken out. (0 for normal, 1 for grow, 2 for shrink.)
|
||||
|
||||
|
|
|
|||
|
|
@ -34,11 +34,12 @@ typedef enum
|
|||
BT_LOOKBACK = 1<<5, // Look Backward
|
||||
BT_RESPAWN = 1<<6, // Respawn
|
||||
BT_VOTE = 1<<7, // Vote
|
||||
BT_SPINDASH = 1<<8, // Spindash
|
||||
|
||||
BT_EBRAKEMASK = (BT_ACCELERATE|BT_BRAKE),
|
||||
BT_SPINDASHMASK = (BT_ACCELERATE|BT_BRAKE|BT_DRIFT),
|
||||
BT_EBRAKEMASK = (BT_ACCELERATE|BT_BRAKE),
|
||||
BT_SPINDASHMASK = (BT_ACCELERATE|BT_BRAKE|BT_DRIFT),
|
||||
|
||||
// free: 1<<8 to 1<<12
|
||||
// free: 1<<9 to 1<<12
|
||||
|
||||
// Lua garbage, replace with freeslottable buttons some day
|
||||
BT_LUAA = 1<<13,
|
||||
|
|
|
|||
|
|
@ -3081,6 +3081,9 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi
|
|||
"S_BADNIK_EXPLOSION_SHOCKWAVE2",
|
||||
"S_BADNIK_EXPLOSION1",
|
||||
"S_BADNIK_EXPLOSION2",
|
||||
|
||||
// Flybot767 (stun)
|
||||
"S_FLYBOT767",
|
||||
};
|
||||
|
||||
// RegEx to generate this from info.h: ^\tMT_([^,]+), --> \t"MT_\1",
|
||||
|
|
@ -3973,6 +3976,8 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t
|
|||
"MT_PULLUPHOOK",
|
||||
|
||||
"MT_AMPS",
|
||||
|
||||
"MT_FLYBOT767",
|
||||
};
|
||||
|
||||
const char *const MOBJFLAG_LIST[] = {
|
||||
|
|
@ -5005,6 +5010,7 @@ struct int_const_s const INT_CONST[] = {
|
|||
{"BT_LOOKBACK",BT_LOOKBACK},
|
||||
{"BT_RESPAWN",BT_RESPAWN},
|
||||
{"BT_VOTE",BT_VOTE},
|
||||
{"BT_SPINDASH",BT_SPINDASH}, // Real button now, but triggers the macro same as always.
|
||||
{"BT_EBRAKEMASK",BT_EBRAKEMASK}, // Macro button
|
||||
{"BT_SPINDASHMASK",BT_SPINDASHMASK}, // Macro button
|
||||
{"BT_LUAA",BT_LUAA}, // Lua customizable
|
||||
|
|
@ -5246,6 +5252,11 @@ struct int_const_s const INT_CONST[] = {
|
|||
{"TN_CHANGEPITCH",TN_CHANGEPITCH},
|
||||
{"TN_LOOPING",TN_LOOPING},
|
||||
|
||||
{"PICKUP_RINGORSPHERE", PICKUP_RINGORSPHERE},
|
||||
{"PICKUP_ITEMBOX", PICKUP_ITEMBOX},
|
||||
{"PICKUP_EGGBOX", PICKUP_EGGBOX},
|
||||
{"PICKUP_PAPERITEM", PICKUP_PAPERITEM},
|
||||
|
||||
{NULL,0}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -931,6 +931,12 @@ void F_IntroTicker(void)
|
|||
return;
|
||||
}
|
||||
|
||||
if (skiptype == 6) // Dev Exec
|
||||
{
|
||||
ResetSkipSequences();
|
||||
COM_ImmedExecute("exec devexec.cfg");
|
||||
}
|
||||
|
||||
if (doskip && disclaimerskippable)
|
||||
{
|
||||
if (dc_state == DISCLAIMER_FINAL) {
|
||||
|
|
@ -1021,19 +1027,21 @@ static void AdvanceSkipSequences(UINT8 input)
|
|||
UINT8 s2cheat[] = {1, 1, 1};
|
||||
UINT8 s3cheat[] = {2, 2, 2};
|
||||
UINT8 s3kcheat[] = {3, 3, 3};
|
||||
UINT8 spincheat[] = {4, 4, 4};
|
||||
UINT8 spincheat[] = {1, 3, 1};
|
||||
UINT8 devcheat[] = {4, 4, 4};
|
||||
#else
|
||||
UINT8 s2cheat[] = {1, 1, 1, 3, 3, 3, 1};
|
||||
UINT8 s3cheat[] = {1, 1, 3, 3, 1, 1, 1, 1};
|
||||
UINT8 s3kcheat[] = {4, 4, 4, 2, 2, 2, 1, 1, 1};
|
||||
UINT8 spincheat[] = {1, 2, 3, 4, 3, 2, 1};
|
||||
UINT8 devcheat[] = {4, 4, 4};
|
||||
#endif
|
||||
UINT8 nicetry[] = {1, 1, 3, 3, 4, 2, 4, 2};
|
||||
|
||||
#define NUMCHEATSPLUSONE 5
|
||||
#define NUMCHEATSPLUSONE 6
|
||||
|
||||
UINT8 *cheats[NUMCHEATSPLUSONE] = {s2cheat, s3cheat, s3kcheat, nicetry, spincheat};
|
||||
UINT8 cheatlengths[NUMCHEATSPLUSONE] = {sizeof(s2cheat), sizeof(s3cheat), sizeof(s3kcheat), sizeof(nicetry), sizeof(spincheat)};
|
||||
UINT8 *cheats[NUMCHEATSPLUSONE] = {s2cheat, s3cheat, s3kcheat, nicetry, spincheat, devcheat};
|
||||
UINT8 cheatlengths[NUMCHEATSPLUSONE] = {sizeof(s2cheat), sizeof(s3cheat), sizeof(s3kcheat), sizeof(nicetry), sizeof(spincheat), sizeof(devcheat)};
|
||||
|
||||
for (UINT8 i = 0; i < NUMCHEATSPLUSONE; i++) // for each cheat...
|
||||
{
|
||||
|
|
|
|||
|
|
@ -400,7 +400,7 @@ class TiccmdBuilder
|
|||
};
|
||||
|
||||
map(gc_drift, BT_DRIFT); // drift
|
||||
map(gc_spindash, BT_SPINDASHMASK); // C
|
||||
map(gc_spindash, BT_SPINDASH|BT_SPINDASHMASK); // C
|
||||
map(gc_item, BT_ATTACK); // fire
|
||||
|
||||
map(gc_lookback, BT_LOOKBACK); // rear view
|
||||
|
|
|
|||
|
|
@ -207,6 +207,7 @@ boolean G_CompatLevel(UINT16 level)
|
|||
#define DEMO_BOT 0x08
|
||||
#define DEMO_AUTOROULETTE 0x10
|
||||
#define DEMO_AUTORING 0x20
|
||||
#define DEMO_STRICTFASTFALL 0x40
|
||||
|
||||
// For demos
|
||||
#define ZT_FWD 0x0001
|
||||
|
|
@ -2336,6 +2337,8 @@ void G_BeginRecording(void)
|
|||
i |= DEMO_SPECTATOR;
|
||||
if (player->pflags & PF_KICKSTARTACCEL)
|
||||
i |= DEMO_KICKSTART;
|
||||
if (player->pflags & PF2_STRICTFASTFALL)
|
||||
i |= DEMO_STRICTFASTFALL;
|
||||
if (player->pflags & PF_AUTOROULETTE)
|
||||
i |= DEMO_AUTOROULETTE;
|
||||
if (player->pflags & PF_AUTORING)
|
||||
|
|
@ -3453,6 +3456,11 @@ void G_DoPlayDemoEx(const char *defdemoname, lumpnum_t deflumpnum)
|
|||
else
|
||||
players[p].pflags &= ~PF_KICKSTARTACCEL;
|
||||
|
||||
if (flags & DEMO_STRICTFASTFALL)
|
||||
players[p].pflags |= PF2_STRICTFASTFALL;
|
||||
else
|
||||
players[p].pflags &= ~PF2_STRICTFASTFALL;
|
||||
|
||||
if (flags & DEMO_AUTOROULETTE)
|
||||
players[p].pflags |= PF_AUTOROULETTE;
|
||||
else
|
||||
|
|
|
|||
15
src/g_game.c
15
src/g_game.c
|
|
@ -2241,6 +2241,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
UINT8 botdiffincrease;
|
||||
boolean botrival;
|
||||
|
||||
boolean cangrabitems;
|
||||
|
||||
SINT8 xtralife;
|
||||
|
||||
uint8_t public_key[PUBKEYLENGTH];
|
||||
|
|
@ -2342,6 +2344,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
bot = players[player].bot;
|
||||
botdifficulty = players[player].botvars.difficulty;
|
||||
|
||||
cangrabitems = players[player].cangrabitems;
|
||||
|
||||
botdiffincrease = players[player].botvars.diffincrease;
|
||||
botrival = players[player].botvars.rival;
|
||||
|
||||
|
|
@ -2349,7 +2353,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
xtralife = players[player].xtralife;
|
||||
|
||||
pflags = (players[player].pflags & (PF_WANTSTOJOIN|PF_KICKSTARTACCEL|PF_SHRINKME|PF_SHRINKACTIVE|PF_AUTOROULETTE|PF_ANALOGSTICK|PF_AUTORING));
|
||||
pflags2 = (players[player].pflags2 & (PF2_SELFMUTE | PF2_SELFDEAFEN | PF2_SERVERMUTE | PF2_SERVERDEAFEN));
|
||||
pflags2 = (players[player].pflags2 & (PF2_SELFMUTE | PF2_SELFDEAFEN | PF2_SERVERMUTE | PF2_SERVERDEAFEN | PF2_STRICTFASTFALL));
|
||||
|
||||
// SRB2kart
|
||||
memcpy(&itemRoulette, &players[player].itemRoulette, sizeof (itemRoulette));
|
||||
|
|
@ -2425,6 +2429,13 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
bigwaypointgap = 0;
|
||||
|
||||
tallyactive = false;
|
||||
|
||||
cangrabitems = 0;
|
||||
if (gametyperules & GTR_SPHERES
|
||||
|| gametyperules & GTR_CATCHER
|
||||
|| G_TimeAttackStart()
|
||||
|| gametype == GT_TUTORIAL)
|
||||
cangrabitems = EARLY_ITEM_FLICKER;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -2567,6 +2578,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
p->charflags = charflags;
|
||||
p->lastfakeskin = lastfakeskin;
|
||||
|
||||
p->cangrabitems = cangrabitems;
|
||||
|
||||
memcpy(players[player].availabilities, availabilities, sizeof(availabilities));
|
||||
p->followitem = followitem;
|
||||
|
||||
|
|
|
|||
|
|
@ -100,6 +100,7 @@ extern consvar_t cv_pauseifunfocused;
|
|||
extern consvar_t cv_kickstartaccel[MAXSPLITSCREENPLAYERS];
|
||||
extern consvar_t cv_autoroulette[MAXSPLITSCREENPLAYERS];
|
||||
extern consvar_t cv_litesteer[MAXSPLITSCREENPLAYERS];
|
||||
extern consvar_t cv_strictfastfall[MAXSPLITSCREENPLAYERS];
|
||||
extern consvar_t cv_autoring[MAXSPLITSCREENPLAYERS];
|
||||
extern consvar_t cv_shrinkme[MAXSPLITSCREENPLAYERS];
|
||||
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ void K_DrawInputDisplay(float x, float y, INT32 flags, char mode, UINT8 pid, boo
|
|||
box.patch(gfx("PAD{}", analog ? "N" : dpad_suffix(dpad)));
|
||||
box.patch(but('A', gc_a, BT_ACCELERATE));
|
||||
box.patch(but('B', gc_b, BT_LOOKBACK));
|
||||
box.patch(but('C', gc_c, BT_SPINDASHMASK));
|
||||
box.patch(but('C', gc_c, BT_SPINDASH));
|
||||
box.patch(but('X', gc_x, BT_BRAKE));
|
||||
box.patch(but('Y', gc_y, BT_RESPAWN));
|
||||
box.patch(but('Z', gc_z, BT_VOTE));
|
||||
|
|
|
|||
|
|
@ -130,10 +130,10 @@ void TwodeeRenderer::rewrite_patch_quad_vertices(Draw2dList& list, const Draw2dP
|
|||
const float cmd_xrange = cmd.xmax - cmd.xmin;
|
||||
const float cmd_yrange = cmd.ymax - cmd.ymin;
|
||||
|
||||
const float clipped_xmin = cmd.clip ? std::clamp(cmd.xmin, cmd.clip_xmin, cmd.clip_xmax) : cmd.xmin;
|
||||
const float clipped_xmax = cmd.clip ? std::clamp(cmd.xmax, cmd.clip_xmin, cmd.clip_xmax) : cmd.xmax;
|
||||
const float clipped_ymin = cmd.clip ? std::clamp(cmd.ymin, cmd.clip_ymin, cmd.clip_ymax) : cmd.ymin;
|
||||
const float clipped_ymax = cmd.clip ? std::clamp(cmd.ymax, cmd.clip_ymin, cmd.clip_ymax) : cmd.ymax;
|
||||
const float clipped_xmin = cmd.clip ? std::clamp(cmd.xmin, cmd.clip_xmin, std::max(cmd.clip_xmax, cmd.clip_xmin)) : cmd.xmin;
|
||||
const float clipped_xmax = cmd.clip ? std::clamp(cmd.xmax, cmd.clip_xmin, std::max(cmd.clip_xmax, cmd.clip_xmin)) : cmd.xmax;
|
||||
const float clipped_ymin = cmd.clip ? std::clamp(cmd.ymin, cmd.clip_ymin, std::max(cmd.clip_ymax, cmd.clip_ymin)) : cmd.ymin;
|
||||
const float clipped_ymax = cmd.clip ? std::clamp(cmd.ymax, cmd.clip_ymin, std::max(cmd.clip_ymax, cmd.clip_ymin)) : cmd.ymax;
|
||||
|
||||
const float trimmed_left = cmd.flip ? (1.f - trim_umax) : trim_umin;
|
||||
const float trimmed_right = cmd.flip ? trim_umin : (1.f - trim_umax);
|
||||
|
|
|
|||
32
src/info.c
32
src/info.c
|
|
@ -771,6 +771,9 @@ char sprnames[NUMSPRITES + 1][5] =
|
|||
"DIEM", // smoke
|
||||
"DIEN", // explosion
|
||||
|
||||
// Flybot767 (stun)
|
||||
"STUN",
|
||||
|
||||
// Pulley
|
||||
"HCCH",
|
||||
"HCHK",
|
||||
|
|
@ -3632,6 +3635,9 @@ state_t states[NUMSTATES] =
|
|||
{SPR_NULL, 0, 1, {A_PlaySound}, sfx_s3k3d, 1, S_BATTLEBUMPER_EXBLAST1}, // S_BADNIK_EXPLOSION_SHOCKWAVE2
|
||||
{SPR_NULL, 0, 1, {NULL}, 0, 0, S_BADNIK_EXPLOSION2}, // S_BADNIK_EXPLOSION1
|
||||
{SPR_WIPD, FF_FULLBRIGHT|FF_RANDOMANIM|FF_ANIMATE, 30, {NULL}, 9, 3, S_NULL}, // S_BADNIK_EXPLOSION2
|
||||
|
||||
// Flybot767 (stun)
|
||||
{SPR_STUN, FF_FULLBRIGHT|FF_ANIMATE, -1, {NULL}, 4, 4, S_NULL}, // S_FLYBOT767
|
||||
};
|
||||
|
||||
mobjinfo_t mobjinfo[NUMMOBJTYPES] =
|
||||
|
|
@ -22320,6 +22326,32 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
|
|||
MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_NOCLIP|MF_NOCLIPTHING, // flags
|
||||
S_NULL // raisestate
|
||||
},
|
||||
{ // MT_FLYBOT767
|
||||
-1, // doomednum
|
||||
S_FLYBOT767, // spawnstate
|
||||
1000, // spawnhealth
|
||||
S_NULL, // seestate
|
||||
sfx_None, // seesound
|
||||
0, // reactiontime
|
||||
sfx_None, // attacksound
|
||||
S_NULL, // painstate
|
||||
0, // painchance
|
||||
sfx_None, // painsound
|
||||
S_NULL, // meleestate
|
||||
S_NULL, // missilestate
|
||||
S_NULL, // deathstate
|
||||
S_NULL, // xdeathstate
|
||||
sfx_pop, // deathsound
|
||||
4*FRACUNIT, // speed
|
||||
32*FRACUNIT, // radius
|
||||
15*FRACUNIT, // height
|
||||
0, // dispoffset
|
||||
0, // mass
|
||||
0, // damage
|
||||
sfx_None, // activesound
|
||||
MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_NOCLIP|MF_NOCLIPTHING, // flags
|
||||
S_NULL // raisestate
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1306,6 +1306,9 @@ typedef enum sprite
|
|||
SPR_DIEM, // smoke
|
||||
SPR_DIEN, // explosion
|
||||
|
||||
// Flybot767 (stun)
|
||||
SPR_STUN,
|
||||
|
||||
// Pulley
|
||||
SPR_HCCH,
|
||||
SPR_HCHK,
|
||||
|
|
@ -4117,6 +4120,9 @@ typedef enum state
|
|||
S_BADNIK_EXPLOSION1,
|
||||
S_BADNIK_EXPLOSION2,
|
||||
|
||||
// Flybot767 (stun)
|
||||
S_FLYBOT767,
|
||||
|
||||
S_FIRSTFREESLOT,
|
||||
S_LASTFREESLOT = S_FIRSTFREESLOT + NUMSTATEFREESLOTS - 1,
|
||||
NUMSTATES
|
||||
|
|
@ -5032,6 +5038,8 @@ typedef enum mobj_type
|
|||
|
||||
MT_AMPS,
|
||||
|
||||
MT_FLYBOT767,
|
||||
|
||||
MT_FIRSTFREESLOT,
|
||||
MT_LASTFREESLOT = MT_FIRSTFREESLOT + NUMMOBJFREESLOTS - 1,
|
||||
NUMMOBJTYPES
|
||||
|
|
|
|||
|
|
@ -497,7 +497,7 @@ static BlockItReturn_t K_FindObjectsForNudging(mobj_t *thing)
|
|||
break;
|
||||
}
|
||||
|
||||
if (P_CanPickupItem(g_nudgeSearch.botmo->player, 1))
|
||||
if (P_CanPickupItem(g_nudgeSearch.botmo->player, PICKUP_ITEMBOX))
|
||||
{
|
||||
K_AddAttackObject(thing, side, ((thing->extravalue1 < RINGBOX_TIME) ? 10 : 20));
|
||||
}
|
||||
|
|
@ -508,7 +508,7 @@ static BlockItReturn_t K_FindObjectsForNudging(mobj_t *thing)
|
|||
break;
|
||||
}
|
||||
|
||||
if (P_CanPickupItem(g_nudgeSearch.botmo->player, 1)) // Can pick up an actual item
|
||||
if (P_CanPickupItem(g_nudgeSearch.botmo->player, PICKUP_ITEMBOX)) // Can pick up an actual item
|
||||
{
|
||||
const UINT8 stealth = K_EggboxStealth(thing->x, thing->y);
|
||||
const UINT8 requiredstealth = (g_nudgeSearch.botmo->player->botvars.difficulty * g_nudgeSearch.botmo->player->botvars.difficulty);
|
||||
|
|
@ -529,7 +529,7 @@ static BlockItReturn_t K_FindObjectsForNudging(mobj_t *thing)
|
|||
break;
|
||||
}
|
||||
|
||||
if (P_CanPickupItem(g_nudgeSearch.botmo->player, 3))
|
||||
if (P_CanPickupItem(g_nudgeSearch.botmo->player, PICKUP_PAPERITEM))
|
||||
{
|
||||
K_AddAttackObject(thing, side, 20);
|
||||
}
|
||||
|
|
@ -541,8 +541,8 @@ static BlockItReturn_t K_FindObjectsForNudging(mobj_t *thing)
|
|||
break;
|
||||
}
|
||||
|
||||
if ((RINGTOTAL(g_nudgeSearch.botmo->player) < 20 && !(g_nudgeSearch.botmo->player->pflags & PF_RINGLOCK)
|
||||
&& P_CanPickupItem(g_nudgeSearch.botmo->player, 0))
|
||||
if ((RINGTOTAL(g_nudgeSearch.botmo->player) < 20
|
||||
&& P_CanPickupItem(g_nudgeSearch.botmo->player, PICKUP_RINGORSPHERE))
|
||||
&& !thing->extravalue1
|
||||
&& (g_nudgeSearch.botmo->player->itemtype != KITEM_LIGHTNINGSHIELD))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -72,6 +72,9 @@ boolean K_BananaBallhogCollide(mobj_t *t1, mobj_t *t2)
|
|||
if (t1->type == MT_BALLHOG && t2->type == MT_BALLHOG)
|
||||
return true; // Ballhogs don't collide with eachother
|
||||
|
||||
if (K_TryPickMeUp(t1, t2))
|
||||
return true;
|
||||
|
||||
if (t2->player)
|
||||
{
|
||||
if (t2->player->flashing > 0 && t2->hitlag == 0)
|
||||
|
|
@ -172,7 +175,10 @@ boolean K_EggItemCollide(mobj_t *t1, mobj_t *t2)
|
|||
if (t1->health <= 0 || t2->health <= 0)
|
||||
return true;
|
||||
|
||||
if (!P_CanPickupItem(t2->player, 2))
|
||||
if (K_TryPickMeUp(t1, t2))
|
||||
return true;
|
||||
|
||||
if (!P_CanPickupItem(t2->player, PICKUP_EGGBOX))
|
||||
return true;
|
||||
|
||||
K_DropItems(t2->player);
|
||||
|
|
@ -425,6 +431,9 @@ boolean K_LandMineCollide(mobj_t *t1, mobj_t *t2)
|
|||
if (t1->health <= 0 || t2->health <= 0)
|
||||
return true;
|
||||
|
||||
if (K_TryPickMeUp(t1, t2))
|
||||
return true;
|
||||
|
||||
if (t2->player)
|
||||
{
|
||||
const INT32 oldhitlag = t2->hitlag;
|
||||
|
|
@ -532,6 +541,9 @@ boolean K_DropTargetCollide(mobj_t *t1, mobj_t *t2)
|
|||
if (t2->player && (t2->player->hyudorotimer || t2->player->justbumped))
|
||||
return true;
|
||||
|
||||
if (K_TryPickMeUp(t1, t2))
|
||||
return true;
|
||||
|
||||
if (draggeddroptarget && P_MobjWasRemoved(draggeddroptarget))
|
||||
draggeddroptarget = NULL; // Beware order-of-execution on crushers, I guess?!
|
||||
|
||||
|
|
@ -1053,6 +1065,9 @@ boolean K_KitchenSinkCollide(mobj_t *t1, mobj_t *t2)
|
|||
if (((t1->target == t2) || (!(t2->flags & (MF_ENEMY|MF_BOSS)) && (t1->target == t2->target))) && (t1->threshold > 0 || (t2->type != MT_PLAYER && t2->threshold > 0)))
|
||||
return true;
|
||||
|
||||
if (K_TryPickMeUp(t1, t2))
|
||||
return true;
|
||||
|
||||
if (t2->player)
|
||||
{
|
||||
if (t2->player->flashing > 0 && t2->hitlag == 0)
|
||||
|
|
|
|||
126
src/k_hud.cpp
126
src/k_hud.cpp
|
|
@ -209,6 +209,7 @@ static patch_t *kp_alagles[10];
|
|||
static patch_t *kp_blagles[6];
|
||||
|
||||
static patch_t *kp_cpu[2];
|
||||
patch_t *kp_pickmeup[2];
|
||||
|
||||
static patch_t *kp_nametagstem;
|
||||
|
||||
|
|
@ -867,6 +868,9 @@ void K_LoadKartHUDGraphics(void)
|
|||
HU_UpdatePatch(&kp_cpu[0], "K_CPU1");
|
||||
HU_UpdatePatch(&kp_cpu[1], "K_CPU2");
|
||||
|
||||
HU_UpdatePatch(&kp_pickmeup[0], "K_PMU1");
|
||||
HU_UpdatePatch(&kp_pickmeup[1], "K_PMU2");
|
||||
|
||||
HU_UpdatePatch(&kp_nametagstem, "K_NAMEST");
|
||||
|
||||
HU_UpdatePatch(&kp_trickcool[0], "K_COOL1");
|
||||
|
|
@ -1889,6 +1893,125 @@ static void K_drawKartItem(void)
|
|||
}
|
||||
}
|
||||
|
||||
// So, like, we've already established that HUD code is unsavable, right?
|
||||
// == SHITGARBAGE UNLIMITED 3: HACKS GONE WILD ==
|
||||
static void K_drawBackupItem(void)
|
||||
{
|
||||
bool tiny = r_splitscreen > 1;
|
||||
patch_t *localpatch[3] = { kp_nodraw, kp_nodraw, kp_nodraw };
|
||||
patch_t *localbg = (kp_itembg[2]);
|
||||
patch_t *localinv = kp_invincibility[((leveltime % (6*3)) / 3) + 7 + tiny];
|
||||
INT32 fx = 0, fy = 0, fflags = 0, tx = 0, ty = 0; // final coords for hud and flags...
|
||||
const INT32 numberdisplaymin = 2;
|
||||
skincolornum_t localcolor[3] = { static_cast<skincolornum_t>(stplyr->skincolor) };
|
||||
SINT8 colormode[3] = { TC_RAINBOW };
|
||||
boolean flipamount = false; // Used for 3P/4P splitscreen to flip item amount stuff
|
||||
|
||||
if (stplyr->backupitemamount <= 0)
|
||||
return;
|
||||
|
||||
switch (stplyr->backupitemtype)
|
||||
{
|
||||
case KITEM_INVINCIBILITY:
|
||||
localpatch[1] = localinv;
|
||||
localbg = kp_itembg[2];
|
||||
break;
|
||||
|
||||
case KITEM_ORBINAUT:
|
||||
localpatch[1] = kp_orbinaut[tiny+4];
|
||||
break;
|
||||
|
||||
case KITEM_SPB:
|
||||
case KITEM_LIGHTNINGSHIELD:
|
||||
case KITEM_BUBBLESHIELD:
|
||||
case KITEM_FLAMESHIELD:
|
||||
localbg = kp_itembg[2];
|
||||
/*FALLTHRU*/
|
||||
|
||||
default:
|
||||
localpatch[1] = K_GetCachedItemPatch(stplyr->backupitemtype, 1 + tiny);
|
||||
|
||||
if (localpatch[1] == NULL)
|
||||
localpatch[1] = kp_nodraw; // diagnose underflows
|
||||
break;
|
||||
}
|
||||
|
||||
// pain and suffering defined below
|
||||
if (!(R_GetViewNumber() & 1) || (!tiny)) // If we are P1 or P3...
|
||||
{
|
||||
fx = ITEM_X;
|
||||
fy = ITEM_Y;
|
||||
fflags = V_SNAPTOLEFT|V_SNAPTOTOP|V_SPLITSCREEN;
|
||||
}
|
||||
else // else, that means we're P2 or P4.
|
||||
{
|
||||
fx = ITEM2_X;
|
||||
fy = ITEM2_Y;
|
||||
fflags = V_SNAPTORIGHT|V_SNAPTOTOP|V_SPLITSCREEN;
|
||||
flipamount = true;
|
||||
}
|
||||
|
||||
if (r_splitscreen == 1)
|
||||
{
|
||||
fy -= 5;
|
||||
}
|
||||
|
||||
// final fudge - vegeta 2025
|
||||
if (tiny && !(R_GetViewNumber() & 1)) // P1/P3 4P
|
||||
{
|
||||
fx += 26;
|
||||
fy += 5;
|
||||
tx += 10;
|
||||
ty += 18;
|
||||
}
|
||||
else if (tiny && (R_GetViewNumber() & 1)) // P2/P4 4P
|
||||
{
|
||||
fx += -4;
|
||||
fy += 5;
|
||||
tx += 1;
|
||||
ty += 18;
|
||||
}
|
||||
else // 1P/2P
|
||||
{
|
||||
fx += 30;
|
||||
fy += -10;
|
||||
tx += 25;
|
||||
ty += 30;
|
||||
}
|
||||
|
||||
boolean transflag = V_HUDTRANS;
|
||||
|
||||
// I feel like the cardinal sin of all evolving HUDcode is, like, assuming the old offsets do something that makes sense.
|
||||
|
||||
if (stplyr->backupitemamount >= numberdisplaymin && stplyr->itemRoulette.active == false)
|
||||
{
|
||||
/*
|
||||
// Then, the numbers:
|
||||
V_DrawScaledPatch(
|
||||
fx + (flipamount ? 48 : 0), fy,
|
||||
V_HUDTRANS|V_SLIDEIN|fflags|(flipamount ? V_FLIP : 0),
|
||||
kp_itemmulsticker[offset]
|
||||
); // flip this graphic for p2 and p4 in split and shift it.
|
||||
*/
|
||||
|
||||
V_DrawFixedPatch(
|
||||
fx<<FRACBITS, (fy<<FRACBITS),
|
||||
FRACUNIT, transflag|V_SLIDEIN|fflags,
|
||||
localpatch[1], (localcolor[1] ? R_GetTranslationColormap(colormode[1], localcolor[1], GTC_CACHE) : NULL)
|
||||
);
|
||||
|
||||
V_DrawString(fx+tx, fy+ty, V_HUDTRANS|V_SLIDEIN|fflags, va("x%d", stplyr->backupitemamount));
|
||||
}
|
||||
else
|
||||
{
|
||||
V_DrawFixedPatch(
|
||||
fx<<FRACBITS, (fy<<FRACBITS),
|
||||
FRACUNIT, transflag|V_SLIDEIN|fflags,
|
||||
localpatch[1], (localcolor[1] ? R_GetTranslationColormap(colormode[1], localcolor[1], GTC_CACHE) : NULL)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static void K_drawKartSlotMachine(void)
|
||||
{
|
||||
// ITEM_X = BASEVIDWIDTH-50; // 270
|
||||
|
|
@ -6876,6 +6999,9 @@ void K_drawKartHUD(void)
|
|||
{
|
||||
K_drawKartItem();
|
||||
}
|
||||
|
||||
if (stplyr->backupitemtype)
|
||||
K_drawBackupItem();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -130,6 +130,8 @@ extern patch_t *gen_button_keycenter[2];
|
|||
extern patch_t *kp_eggnum[6];
|
||||
extern patch_t *kp_facenum[MAXPLAYERS+1];
|
||||
|
||||
extern patch_t *kp_pickmeup[2];
|
||||
|
||||
extern patch_t *kp_unknownminimap;
|
||||
|
||||
void K_AddMessage(const char *msg, boolean interrupt, boolean persist);
|
||||
|
|
|
|||
|
|
@ -278,6 +278,26 @@ private:
|
|||
}},
|
||||
};
|
||||
|
||||
case MT_JAWZ:
|
||||
case MT_JAWZ_SHIELD:
|
||||
case MT_ORBINAUT:
|
||||
case MT_ORBINAUT_SHIELD:
|
||||
case MT_DROPTARGET:
|
||||
case MT_DROPTARGET_SHIELD:
|
||||
case MT_LANDMINE:
|
||||
case MT_BANANA:
|
||||
case MT_BANANA_SHIELD:
|
||||
case MT_GACHABOM:
|
||||
case MT_EGGMANITEM:
|
||||
case MT_EGGMANITEM_SHIELD:
|
||||
case MT_BUBBLESHIELDTRAP:
|
||||
return {
|
||||
{ // Near
|
||||
{2, TICRATE/2, {kp_pickmeup}, 0}, // 1P
|
||||
{{2, TICRATE/2, {kp_pickmeup}, 0}}, // 4P
|
||||
},
|
||||
};
|
||||
|
||||
default:
|
||||
return {
|
||||
{ // Near
|
||||
|
|
@ -378,6 +398,24 @@ bool is_object_tracking_target(const mobj_t* mobj)
|
|||
return !(mobj->renderflags & (RF_TRANSMASK | RF_DONTDRAW)) && // the spraycan wasn't collected yet
|
||||
P_CheckSight(stplyr->mo, const_cast<mobj_t*>(mobj));
|
||||
|
||||
case MT_JAWZ:
|
||||
case MT_JAWZ_SHIELD:
|
||||
case MT_ORBINAUT:
|
||||
case MT_ORBINAUT_SHIELD:
|
||||
case MT_DROPTARGET:
|
||||
case MT_DROPTARGET_SHIELD:
|
||||
case MT_LANDMINE:
|
||||
case MT_BANANA:
|
||||
case MT_BANANA_SHIELD:
|
||||
case MT_GACHABOM:
|
||||
case MT_BUBBLESHIELDTRAP:
|
||||
case MT_EGGMANITEM:
|
||||
case MT_EGGMANITEM_SHIELD:
|
||||
return (mobj->target && !P_MobjWasRemoved(mobj->target) && (
|
||||
(mobj->target->player && stplyr == mobj->target->player)
|
||||
|| (mobj->target->player && G_SameTeam(stplyr, mobj->target->player))
|
||||
) && P_CheckSight(stplyr->mo, const_cast<mobj_t*>(mobj)));
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
|
@ -863,6 +901,35 @@ void K_drawTargetHUD(const vector3_t* origin, player_t* player)
|
|||
|
||||
if (tracking)
|
||||
{
|
||||
fixed_t itemOffset = 36*mobj->scale;
|
||||
switch (mobj->type)
|
||||
{
|
||||
case MT_JAWZ:
|
||||
case MT_JAWZ_SHIELD:
|
||||
case MT_ORBINAUT:
|
||||
case MT_ORBINAUT_SHIELD:
|
||||
case MT_DROPTARGET:
|
||||
case MT_DROPTARGET_SHIELD:
|
||||
case MT_LANDMINE:
|
||||
case MT_BANANA:
|
||||
case MT_BANANA_SHIELD:
|
||||
case MT_GACHABOM:
|
||||
case MT_BUBBLESHIELDTRAP:
|
||||
case MT_EGGMANITEM:
|
||||
case MT_EGGMANITEM_SHIELD:
|
||||
if (stplyr->mo->eflags & MFE_VERTICALFLIP)
|
||||
{
|
||||
pos.z -= itemOffset;
|
||||
}
|
||||
else
|
||||
{
|
||||
pos.z += itemOffset;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
K_ObjectTracking(&tr.result, &pos, false);
|
||||
targetList.push_back(tr);
|
||||
}
|
||||
|
|
|
|||
206
src/k_kart.c
206
src/k_kart.c
|
|
@ -4235,6 +4235,8 @@ void K_CheckpointCrossAward(player_t *player)
|
|||
return;
|
||||
|
||||
player->exp += K_GetExpAdjustment(player);
|
||||
if (!player->cangrabitems)
|
||||
player->cangrabitems = 1;
|
||||
K_AwardPlayerRings(player, (player->bot ? 20 : 10), true);
|
||||
}
|
||||
|
||||
|
|
@ -8347,6 +8349,31 @@ static void K_MoveHeldObjects(player_t *player)
|
|||
}
|
||||
}
|
||||
|
||||
// If we can move our backup item into main slots, do so.
|
||||
static void K_TryMoveBackupItem(player_t *player)
|
||||
{
|
||||
if (player->itemtype && player->itemtype == player->backupitemtype)
|
||||
{
|
||||
player->itemamount += player->backupitemamount;
|
||||
|
||||
player->backupitemtype = 0;
|
||||
player->backupitemamount = 0;
|
||||
|
||||
S_StartSound(player->mo, sfx_mbs54);
|
||||
}
|
||||
|
||||
if (player->itemtype == KITEM_NONE && player->backupitemtype && P_CanPickupItem(player, PICKUP_PAPERITEM))
|
||||
{
|
||||
player->itemtype = player->backupitemtype;
|
||||
player->itemamount = player->backupitemamount;
|
||||
|
||||
player->backupitemtype = 0;
|
||||
player->backupitemamount = 0;
|
||||
|
||||
S_StartSound(player->mo, sfx_mbs54);
|
||||
}
|
||||
}
|
||||
|
||||
mobj_t *K_FindJawzTarget(mobj_t *actor, player_t *source, angle_t range)
|
||||
{
|
||||
fixed_t best = INT32_MAX;
|
||||
|
|
@ -8817,7 +8844,7 @@ static inline BlockItReturn_t PIT_AttractingRings(mobj_t *thing)
|
|||
return BMIT_CONTINUE; // Too far away
|
||||
}
|
||||
|
||||
if (RINGTOTAL(attractmo->player) >= 20 || (attractmo->player->pflags & PF_RINGLOCK))
|
||||
if (RINGTOTAL(attractmo->player) >= 20 || !P_CanPickupItem(attractmo->player, PICKUP_RINGORSPHERE))
|
||||
{
|
||||
// Already reached max -- just joustle rings around.
|
||||
|
||||
|
|
@ -9156,6 +9183,8 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
|
|||
if (player->itemtype == KITEM_NONE)
|
||||
player->itemflags &= ~IF_HOLDREADY;
|
||||
|
||||
K_TryMoveBackupItem(player);
|
||||
|
||||
if (onground || player->transfer < 10*player->mo->scale)
|
||||
{
|
||||
player->transfer = 0;
|
||||
|
|
@ -9377,6 +9406,33 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
|
|||
if (player->ringdelay)
|
||||
player->ringdelay--;
|
||||
|
||||
if ((player->stunned > 0)
|
||||
&& (player->respawn.state == RESPAWNST_NONE)
|
||||
&& !P_PlayerInPain(player)
|
||||
&& P_IsObjectOnGround(player->mo)
|
||||
)
|
||||
{
|
||||
// MEGA FUCKING HACK BECAUSE P_SAVEG MOBJS ARE FULL
|
||||
// Would updating player_saveflags to 32 bits have any negative consequences?
|
||||
// For now, player->stunned 16th bit is a flag to determine whether the flybots were spawned
|
||||
|
||||
// timer counts down at triple speed while spindashing
|
||||
player->stunned = (player->stunned & 0x8000) | max(0, (player->stunned & 0x7FFF) - (player->spindash ? 3 : 1));
|
||||
|
||||
// when timer reaches 0, reset the flag and stun combo counter
|
||||
if ((player->stunned & 0x7FFF) == 0)
|
||||
{
|
||||
player->stunned = 0;
|
||||
player->stunnedCombo = 0;
|
||||
}
|
||||
// otherwise if the flybots aren't spawned, spawn them now!
|
||||
else if ((player->stunned & 0x8000) == 0)
|
||||
{
|
||||
player->stunned |= 0x8000;
|
||||
Obj_SpawnFlybotsForPlayer(player);
|
||||
}
|
||||
}
|
||||
|
||||
if (player->trickpanel == TRICKSTATE_READY)
|
||||
{
|
||||
if (!player->throwdir && !cmd->turning)
|
||||
|
|
@ -9407,7 +9463,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
|
|||
|
||||
// UINT16 oldringboost = player->ringboost;
|
||||
|
||||
if (player->superring == 0)
|
||||
if (player->superring == 0 || player->stunned)
|
||||
player->ringboost -= max((player->ringboost / roller), 1);
|
||||
else
|
||||
player->ringboost -= min(K_GetFullKartRingPower(player, false) - 1, max(player->ringboost / 2 / roller, 1));
|
||||
|
|
@ -9581,6 +9637,8 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
|
|||
player->invincibilitytimer--;
|
||||
}
|
||||
|
||||
if (player->cangrabitems && player->cangrabitems <= EARLY_ITEM_FLICKER)
|
||||
player->cangrabitems++;
|
||||
|
||||
if (!player->invincibilitytimer)
|
||||
player->invincibilityextensions = 0;
|
||||
|
|
@ -9695,6 +9753,9 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
|
|||
|
||||
UINT8 ringrate = 3 - min(2, (player->superring + existing) / fastringscaler); // Used to consume fat stacks of cash faster.
|
||||
|
||||
if (player->stunned)
|
||||
ringrate = 6;
|
||||
|
||||
if (player->nextringaward >= ringrate)
|
||||
{
|
||||
if (player->instaWhipCharge)
|
||||
|
|
@ -10042,7 +10103,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
|
|||
}
|
||||
|
||||
extern consvar_t cv_fuzz;
|
||||
if (cv_fuzz.value && P_CanPickupItem(player, 1))
|
||||
if (cv_fuzz.value && P_CanPickupItem(player, PICKUP_ITEMBOX))
|
||||
{
|
||||
K_StartItemRoulette(player, P_RandomRange(PR_FUZZ, 0, 1));
|
||||
}
|
||||
|
|
@ -12652,6 +12713,10 @@ static void K_KartSpindash(player_t *player)
|
|||
|
||||
if (player->fastfall == 0)
|
||||
{
|
||||
if (player->pflags2 & PF2_STRICTFASTFALL)
|
||||
if (!(player->cmd.buttons & BT_SPINDASH))
|
||||
return;
|
||||
|
||||
// Factors 3D momentum.
|
||||
player->fastfallBase = FixedHypot(player->speed, player->mo->momz);
|
||||
}
|
||||
|
|
@ -15461,4 +15526,139 @@ void K_BotHitPenalty(player_t *player)
|
|||
}
|
||||
}
|
||||
|
||||
static boolean K_PickUp(player_t *player, mobj_t *picked)
|
||||
{
|
||||
SINT8 type = -1;
|
||||
SINT8 amount = 1;
|
||||
|
||||
switch (picked->type)
|
||||
{
|
||||
case MT_ORBINAUT:
|
||||
case MT_ORBINAUT_SHIELD:
|
||||
type = KITEM_ORBINAUT;
|
||||
break;
|
||||
case MT_JAWZ:
|
||||
case MT_JAWZ_SHIELD:
|
||||
type = KITEM_JAWZ;
|
||||
break;
|
||||
case MT_BALLHOG:
|
||||
type = KITEM_BALLHOG;
|
||||
break;
|
||||
case MT_LANDMINE:
|
||||
type = KITEM_LANDMINE;
|
||||
break;
|
||||
case MT_EGGMANITEM:
|
||||
case MT_EGGMANITEM_SHIELD:
|
||||
type = KITEM_EGGMAN;
|
||||
break;
|
||||
case MT_BANANA:
|
||||
case MT_BANANA_SHIELD:
|
||||
type = KITEM_BANANA;
|
||||
break;
|
||||
case MT_DROPTARGET:
|
||||
case MT_DROPTARGET_SHIELD:
|
||||
type = KITEM_DROPTARGET;
|
||||
break;
|
||||
case MT_GACHABOM:
|
||||
type = KITEM_GACHABOM;
|
||||
break;
|
||||
case MT_BUBBLESHIELDTRAP:
|
||||
type = KITEM_BUBBLESHIELD;
|
||||
break;
|
||||
case MT_SINK:
|
||||
type = KITEM_KITCHENSINK;
|
||||
break;
|
||||
default:
|
||||
type = KITEM_SAD;
|
||||
break;
|
||||
}
|
||||
|
||||
if (type == KITEM_SAD)
|
||||
return false;
|
||||
|
||||
// CONS_Printf("it %d ia %d t %d a %d\n", player->itemtype, player->itemamount, type, amount);
|
||||
|
||||
if (player->itemtype == type && player->itemamount && !(player->itemflags & IF_ITEMOUT))
|
||||
{
|
||||
// We have this item in main slot but not deployed, just add it
|
||||
player->itemamount += amount;
|
||||
}
|
||||
else if (player->backupitemamount && player->backupitemtype)
|
||||
{
|
||||
// We already have a backup item, stack it if it can be stacked or discard it
|
||||
if (player->backupitemtype == type)
|
||||
{
|
||||
player->backupitemamount += amount;
|
||||
}
|
||||
else
|
||||
{
|
||||
K_DropPaperItem(player, player->backupitemtype, player->backupitemamount);
|
||||
player->backupitemtype = type;
|
||||
player->backupitemamount = amount;
|
||||
S_StartSound(player->mo, sfx_kc65);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// We have no backup item, load one up
|
||||
player->backupitemtype = type;
|
||||
player->backupitemamount = amount;
|
||||
}
|
||||
|
||||
S_StartSound(player->mo, sfx_aple);
|
||||
K_TryMoveBackupItem(player);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ACHTUNG this destroys items when returning true, make sure to bail out
|
||||
boolean K_TryPickMeUp(mobj_t *m1, mobj_t *m2)
|
||||
{
|
||||
if (!m1 || P_MobjWasRemoved(m1))
|
||||
return false;
|
||||
|
||||
if (!m2 || P_MobjWasRemoved(m2))
|
||||
return false;
|
||||
|
||||
if (m1->type != MT_PLAYER && m2->type != MT_PLAYER)
|
||||
return false;
|
||||
|
||||
if (m1->type == MT_PLAYER && m2->type == MT_PLAYER)
|
||||
return false;
|
||||
|
||||
// CONS_Printf("player check passed\n");
|
||||
|
||||
mobj_t *victim = m1;
|
||||
mobj_t *inflictor = m2;
|
||||
|
||||
// Convenience for collision functions where arg order is freaky
|
||||
if (m2->type == MT_PLAYER)
|
||||
{
|
||||
victim = m2;
|
||||
inflictor = m1;
|
||||
}
|
||||
|
||||
if (!victim->player)
|
||||
return false;
|
||||
|
||||
boolean allied = (inflictor->target == victim);
|
||||
|
||||
if (!allied && inflictor->target && !P_MobjWasRemoved(inflictor->target))
|
||||
if (inflictor->target->player && G_SameTeam(inflictor->target->player, victim->player))
|
||||
allied = true;
|
||||
|
||||
if (!allied)
|
||||
return false;
|
||||
|
||||
// CONS_Printf("target check passed\n");
|
||||
|
||||
if (!K_PickUp(victim->player, inflictor))
|
||||
return false;
|
||||
|
||||
K_AddHitLag(victim, 3, false);
|
||||
|
||||
P_RemoveMobj(inflictor);
|
||||
return true;
|
||||
}
|
||||
|
||||
//}
|
||||
|
|
|
|||
|
|
@ -60,6 +60,8 @@ Make sure this matches the actual number of states
|
|||
|
||||
#define SCAMDIST (2000)
|
||||
|
||||
#define EARLY_ITEM_FLICKER (NUMTRANSMAPS)
|
||||
|
||||
// 2023-08-26 +ang20 to Sal's OG values to make them friendlier - Tyron
|
||||
#define STUMBLE_STEEP_VAL (ANG60 + ANG20)
|
||||
#define STUMBLE_STEEP_VAL_AIR (ANG30 + ANG10 + ANG20)
|
||||
|
|
@ -314,6 +316,8 @@ UINT32 K_GetNumGradingPoints(void);
|
|||
|
||||
void K_BotHitPenalty(player_t *player);
|
||||
|
||||
boolean K_TryPickMeUp(mobj_t *m1, mobj_t *m2);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -1130,6 +1130,7 @@ extern consvar_t cv_dummyprofileplayername;
|
|||
extern consvar_t cv_dummyprofilekickstart;
|
||||
extern consvar_t cv_dummyprofileautoroulette;
|
||||
extern consvar_t cv_dummyprofilelitesteer;
|
||||
extern consvar_t cv_dummyprofilestrictfastfall;
|
||||
extern consvar_t cv_dummyprofiledescriptiveinput;
|
||||
extern consvar_t cv_dummyprofileautoring;
|
||||
extern consvar_t cv_dummyprofilerumble;
|
||||
|
|
|
|||
|
|
@ -435,6 +435,11 @@ boolean Obj_DestroyKart(mobj_t *kart);
|
|||
void Obj_DestroyedKartParticleThink(mobj_t *part);
|
||||
void Obj_DestroyedKartParticleLanding(mobj_t *part);
|
||||
|
||||
/* Flybot767 (stun) */
|
||||
void Obj_SpawnFlybotsForPlayer(player_t *player);
|
||||
void Obj_FlybotThink(mobj_t *flybot);
|
||||
void Obj_FlybotDeath(mobj_t *flybot);
|
||||
|
||||
/* Pulley */
|
||||
void Obj_PulleyThink(mobj_t *root);
|
||||
void Obj_PulleyHookTouch(mobj_t *special, mobj_t *toucher);
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@ profile_t* PR_MakeProfile(
|
|||
newprofile->kickstartaccel = false;
|
||||
newprofile->autoroulette = false;
|
||||
newprofile->litesteer = false;
|
||||
newprofile->strictfastfall = false;
|
||||
newprofile->descriptiveinput = 1;
|
||||
newprofile->autoring = false;
|
||||
newprofile->rumble = true;
|
||||
|
|
@ -108,6 +109,7 @@ profile_t* PR_MakeProfileFromPlayer(const char *prname, const char *pname, const
|
|||
newprofile->kickstartaccel = cv_kickstartaccel[pnum].value;
|
||||
newprofile->autoroulette = cv_autoroulette[pnum].value;
|
||||
newprofile->litesteer = cv_litesteer[pnum].value;
|
||||
newprofile->strictfastfall = cv_strictfastfall[pnum].value;
|
||||
newprofile->descriptiveinput = cv_descriptiveinput[pnum].value;
|
||||
newprofile->autoring = cv_autoring[pnum].value;
|
||||
newprofile->rumble = cv_rumble[pnum].value;
|
||||
|
|
@ -305,6 +307,7 @@ void PR_SaveProfiles(void)
|
|||
jsonprof.preferences.kickstartaccel = cprof->kickstartaccel;
|
||||
jsonprof.preferences.autoroulette = cprof->autoroulette;
|
||||
jsonprof.preferences.litesteer = cprof->litesteer;
|
||||
jsonprof.preferences.strictfastfall = cprof->strictfastfall;
|
||||
jsonprof.preferences.descriptiveinput = cprof->descriptiveinput;
|
||||
jsonprof.preferences.autoring = cprof->autoring;
|
||||
jsonprof.preferences.rumble = cprof->rumble;
|
||||
|
|
@ -493,6 +496,7 @@ void PR_LoadProfiles(void)
|
|||
newprof->kickstartaccel = jsprof.preferences.kickstartaccel;
|
||||
newprof->autoroulette = jsprof.preferences.autoroulette;
|
||||
newprof->litesteer = jsprof.preferences.litesteer;
|
||||
newprof->strictfastfall = jsprof.preferences.strictfastfall;
|
||||
newprof->descriptiveinput = jsprof.preferences.descriptiveinput;
|
||||
newprof->autoring = jsprof.preferences.autoring;
|
||||
newprof->rumble = jsprof.preferences.rumble;
|
||||
|
|
@ -597,6 +601,7 @@ static void PR_ApplyProfile_Settings(profile_t *p, UINT8 playernum)
|
|||
CV_StealthSetValue(&cv_kickstartaccel[playernum], p->kickstartaccel);
|
||||
CV_StealthSetValue(&cv_autoroulette[playernum], p->autoroulette);
|
||||
CV_StealthSetValue(&cv_litesteer[playernum], p->litesteer);
|
||||
CV_StealthSetValue(&cv_strictfastfall[playernum], p->strictfastfall);
|
||||
CV_StealthSetValue(&cv_descriptiveinput[playernum], p->descriptiveinput);
|
||||
CV_StealthSetValue(&cv_autoring[playernum], p->autoring);
|
||||
CV_StealthSetValue(&cv_rumble[playernum], p->rumble);
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ struct ProfilePreferencesJson
|
|||
bool kickstartaccel;
|
||||
bool autoroulette;
|
||||
bool litesteer;
|
||||
bool strictfastfall;
|
||||
uint8_t descriptiveinput;
|
||||
bool autoring;
|
||||
bool rumble;
|
||||
|
|
@ -56,6 +57,7 @@ struct ProfilePreferencesJson
|
|||
kickstartaccel,
|
||||
autoroulette,
|
||||
litesteer,
|
||||
strictfastfall,
|
||||
descriptiveinput,
|
||||
autoring,
|
||||
rumble,
|
||||
|
|
@ -164,6 +166,7 @@ struct profile_t
|
|||
boolean kickstartaccel; // cv_kickstartaccel
|
||||
boolean autoroulette; // cv_autoroulette
|
||||
boolean litesteer; // cv_litesteer
|
||||
boolean strictfastfall; // cv_strictfastfall
|
||||
UINT8 descriptiveinput; // cv_descriptiveinput
|
||||
boolean autoring; // cv_autoring
|
||||
boolean rumble; // cv_rumble
|
||||
|
|
|
|||
|
|
@ -927,7 +927,7 @@ static void K_AddItemToReel(const player_t *player, itemroulette_t *const roulet
|
|||
// If we're in ring debt, pad out the reel with
|
||||
// a BUNCH of Super Rings.
|
||||
if (K_ItemEnabled(KITEM_SUPERRING) == true
|
||||
&& player->rings <= 0
|
||||
&& player->rings <= -10
|
||||
&& player->position == 1
|
||||
&& (gametyperules & GTR_SPHERES) == 0)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -260,10 +260,16 @@ static int player_get(lua_State *L)
|
|||
lua_pushinteger(L, plr->tumbleBounces);
|
||||
else if (fastcmp(field,"tumbleheight"))
|
||||
lua_pushinteger(L, plr->tumbleHeight);
|
||||
else if (fastcmp(field,"stunned"))
|
||||
lua_pushinteger(L, plr->stunned);
|
||||
else if (fastcmp(field,"stunnedcombo"))
|
||||
lua_pushinteger(L, plr->stunnedCombo);
|
||||
else if (fastcmp(field,"justdi"))
|
||||
lua_pushinteger(L, plr->justDI);
|
||||
else if (fastcmp(field,"flipdi"))
|
||||
lua_pushboolean(L, plr->flipDI);
|
||||
else if (fastcmp(field,"cangrabitems"))
|
||||
lua_pushinteger(L, plr->cangrabitems);
|
||||
else if (fastcmp(field,"analoginput"))
|
||||
lua_pushboolean(L, plr->analoginput);
|
||||
else if (fastcmp(field,"transfer"))
|
||||
|
|
@ -432,6 +438,10 @@ static int player_get(lua_State *L)
|
|||
lua_pushinteger(L, plr->itemtype);
|
||||
else if (fastcmp(field,"itemamount"))
|
||||
lua_pushinteger(L, plr->itemamount);
|
||||
else if (fastcmp(field,"backupitemtype"))
|
||||
lua_pushinteger(L, plr->backupitemtype);
|
||||
else if (fastcmp(field,"backupitemamount"))
|
||||
lua_pushinteger(L, plr->backupitemamount);
|
||||
else if (fastcmp(field,"throwdir"))
|
||||
lua_pushinteger(L, plr->throwdir);
|
||||
else if (fastcmp(field,"sadtimer"))
|
||||
|
|
@ -872,10 +882,16 @@ static int player_set(lua_State *L)
|
|||
plr->tumbleBounces = luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"tumbleheight"))
|
||||
plr->tumbleHeight = luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"stunned"))
|
||||
plr->stunned = luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"stunnedcombo"))
|
||||
plr->stunnedCombo = luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"justdi"))
|
||||
plr->justDI = luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"flipdi"))
|
||||
plr->flipDI = luaL_checkboolean(L, 3);
|
||||
else if (fastcmp(field,"cangrabitems"))
|
||||
plr->cangrabitems = luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"incontrol"))
|
||||
plr->incontrol = luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"progressivethrust"))
|
||||
|
|
|
|||
|
|
@ -102,6 +102,7 @@ void M_StartEditProfile(INT32 c)
|
|||
CV_StealthSetValue(&cv_dummyprofilekickstart, optionsmenu.profile->kickstartaccel);
|
||||
CV_StealthSetValue(&cv_dummyprofileautoroulette, optionsmenu.profile->autoroulette);
|
||||
CV_StealthSetValue(&cv_dummyprofilelitesteer, optionsmenu.profile->litesteer);
|
||||
CV_StealthSetValue(&cv_dummyprofilestrictfastfall, optionsmenu.profile->strictfastfall);
|
||||
CV_StealthSetValue(&cv_dummyprofiledescriptiveinput, optionsmenu.profile->descriptiveinput);
|
||||
CV_StealthSetValue(&cv_dummyprofileautoring, optionsmenu.profile->autoring);
|
||||
CV_StealthSetValue(&cv_dummyprofilerumble, optionsmenu.profile->rumble);
|
||||
|
|
@ -114,6 +115,7 @@ void M_StartEditProfile(INT32 c)
|
|||
CV_StealthSetValue(&cv_dummyprofilekickstart, 0); // off
|
||||
CV_StealthSetValue(&cv_dummyprofileautoroulette, 0); // off
|
||||
CV_StealthSetValue(&cv_dummyprofilelitesteer, 1); // on
|
||||
CV_StealthSetValue(&cv_dummyprofilestrictfastfall, 0); // off
|
||||
CV_StealthSetValue(&cv_dummyprofiledescriptiveinput, 1); // Modern
|
||||
CV_StealthSetValue(&cv_dummyprofileautoring, 0); // on
|
||||
CV_StealthSetValue(&cv_dummyprofilerumble, 1); // on
|
||||
|
|
|
|||
|
|
@ -98,6 +98,7 @@ static void M_ProfileEditApply(void)
|
|||
optionsmenu.profile->kickstartaccel = cv_dummyprofilekickstart.value;
|
||||
optionsmenu.profile->autoroulette = cv_dummyprofileautoroulette.value;
|
||||
optionsmenu.profile->litesteer = cv_dummyprofilelitesteer.value;
|
||||
optionsmenu.profile->strictfastfall = cv_dummyprofilestrictfastfall.value;
|
||||
optionsmenu.profile->descriptiveinput = cv_dummyprofiledescriptiveinput.value;
|
||||
optionsmenu.profile->autoring = cv_dummyprofileautoring.value;
|
||||
optionsmenu.profile->rumble = cv_dummyprofilerumble.value;
|
||||
|
|
|
|||
|
|
@ -114,6 +114,9 @@ menuitem_t OPTIONS_ProfileAccessibility[] = {
|
|||
{IT_STRING | IT_CVAR, "Lite Steer", "Hold DOWN on d-pad/keyboard for shallow turns.",
|
||||
NULL, {.cvar = &cv_dummyprofilelitesteer}, 0, 0},
|
||||
|
||||
{IT_STRING | IT_CVAR, "Strict Fastfall", "Fastfall only with the Spindash button.",
|
||||
NULL, {.cvar = &cv_dummyprofilestrictfastfall}, 0, 0},
|
||||
|
||||
{IT_STRING | IT_CVAR, "Field of View", "Higher FOV lets you see more.",
|
||||
NULL, {.cvar = &cv_dummyprofilefov}, 0, 0},
|
||||
|
||||
|
|
@ -144,7 +147,7 @@ menu_t OPTIONS_ProfileAccessibilityDef = {
|
|||
&OPTIONS_EditProfileDef,
|
||||
0,
|
||||
OPTIONS_ProfileAccessibility,
|
||||
145, 41,
|
||||
145, 31,
|
||||
SKINCOLOR_ULTRAMARINE, 0,
|
||||
MBF_DRAWBGWHILEPLAYING,
|
||||
"FILE",
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@ target_sources(SRB2SDL2 PRIVATE
|
|||
pulley.cpp
|
||||
amps.c
|
||||
ballhog.cpp
|
||||
flybot767.c
|
||||
)
|
||||
|
||||
add_subdirectory(versus)
|
||||
|
|
|
|||
142
src/objects/flybot767.c
Normal file
142
src/objects/flybot767.c
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
// DR. ROBOTNIK'S RING RACERS
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2025 by Lachlan "Lach" Wright
|
||||
// Copyright (C) 2025 by Kart Krew
|
||||
//
|
||||
// This program is free software distributed under the
|
||||
// terms of the GNU General Public License, version 2.
|
||||
// See the 'LICENSE' file for more details.
|
||||
//-----------------------------------------------------------------------------
|
||||
/// \file flybot767.c
|
||||
/// \brief Flybot767 object code.
|
||||
|
||||
#include "../p_local.h"
|
||||
#include "../k_kart.h"
|
||||
#include "../k_objects.h"
|
||||
#include "../s_sound.h"
|
||||
#include "../m_easing.h"
|
||||
|
||||
#define FLYBOT_QUANTITY 2
|
||||
#define FLYBOT_VERTICAL_OFFSET (16 * FRACUNIT)
|
||||
#define FLYBOT_BOB_AMPLITUDE (16 * FRACUNIT)
|
||||
#define FLYBOT_BOB_FREQUENCY (ANG15)
|
||||
#define FLYBOT_FADE_STARTTIME (2 * TICRATE)
|
||||
#define FLYBOT_SCALE (17 * FRACUNIT / 20)
|
||||
static const fixed_t PI = 355 * FRACUNIT / 113;
|
||||
|
||||
static fixed_t SetFlybotZ(mobj_t *flybot)
|
||||
{
|
||||
flybot->z = FixedMul(mapobjectscale, FLYBOT_VERTICAL_OFFSET) + FixedMul(mapobjectscale, P_ReturnThrustX(NULL, flybot->movedir, FLYBOT_BOB_AMPLITUDE));
|
||||
if (flybot->eflags & MFE_VERTICALFLIP)
|
||||
{
|
||||
flybot->z = -flybot->z - flybot->height;
|
||||
}
|
||||
else
|
||||
{
|
||||
flybot->z += flybot->target->height;
|
||||
}
|
||||
flybot->z += flybot->target->z;
|
||||
return flybot->z;
|
||||
}
|
||||
|
||||
void Obj_SpawnFlybotsForPlayer(player_t *player)
|
||||
{
|
||||
UINT8 i;
|
||||
mobj_t *mo = player->mo;
|
||||
fixed_t radius = mo->radius;
|
||||
|
||||
for (i = 0; i < FLYBOT_QUANTITY; i++)
|
||||
{
|
||||
angle_t angle = mo->angle + ANGLE_90 + FixedAngle(i * 360 * FRACUNIT / FLYBOT_QUANTITY);
|
||||
mobj_t *flybot = P_SpawnMobj(
|
||||
mo->x + P_ReturnThrustX(NULL, angle, radius),
|
||||
mo->y + P_ReturnThrustY(NULL, angle, radius),
|
||||
mo->z,
|
||||
MT_FLYBOT767
|
||||
);
|
||||
|
||||
P_InstaScale(flybot, flybot->old_scale = FixedMul(mapobjectscale, FLYBOT_SCALE));
|
||||
P_SetTarget(&flybot->target, mo);
|
||||
flybot->eflags |= mo->eflags & MFE_VERTICALFLIP;
|
||||
flybot->movedir = flybot->old_angle = flybot->angle = angle + ANGLE_90;
|
||||
flybot->old_z = SetFlybotZ(flybot);
|
||||
flybot->renderflags |= (i * RF_DONTDRAW);
|
||||
}
|
||||
}
|
||||
|
||||
void Obj_FlybotThink(mobj_t *flybot)
|
||||
{
|
||||
UINT16 stunned = UINT16_MAX;
|
||||
angle_t deltaAngle, angle;
|
||||
fixed_t radius, circumference;
|
||||
fixed_t speed = FixedMul(mapobjectscale, flybot->info->speed);
|
||||
mobj_t *mo = flybot->target;
|
||||
|
||||
if (P_MobjWasRemoved(mo))
|
||||
{
|
||||
P_KillMobj(flybot, NULL, NULL, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (mo->player)
|
||||
{
|
||||
if (((stunned = mo->player->stunned & 0x7FFF) == 0) || (mo->player->playerstate == PST_DEAD))
|
||||
{
|
||||
P_KillMobj(flybot, NULL, NULL, 0);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
flybot->frame = flybot->frame & ~FF_TRANSMASK;
|
||||
if (stunned < FLYBOT_FADE_STARTTIME)
|
||||
{
|
||||
flybot->frame |= Easing_InCubic(FixedDiv(stunned, FLYBOT_FADE_STARTTIME), 7, 1) << FF_TRANSSHIFT;
|
||||
}
|
||||
|
||||
flybot->eflags = (flybot->eflags & ~MFE_VERTICALFLIP) | (mo->eflags & MFE_VERTICALFLIP);
|
||||
flybot->movedir += FLYBOT_BOB_FREQUENCY;
|
||||
flybot->renderflags ^= RF_DONTDRAW;
|
||||
|
||||
radius = mo->radius;
|
||||
circumference = 2 * FixedMul(PI, radius);
|
||||
deltaAngle = FixedAngle(FixedMul(FixedDiv(speed, circumference), 360 * FRACUNIT));
|
||||
flybot->angle += deltaAngle;
|
||||
angle = flybot->angle - ANGLE_90;
|
||||
|
||||
P_MoveOrigin(flybot,
|
||||
mo->x + P_ReturnThrustX(NULL, angle, radius),
|
||||
mo->y + P_ReturnThrustY(NULL, angle, radius),
|
||||
SetFlybotZ(flybot)
|
||||
);
|
||||
}
|
||||
|
||||
void Obj_FlybotDeath(mobj_t *flybot)
|
||||
{
|
||||
UINT8 i;
|
||||
angle_t angle = 0;
|
||||
fixed_t hThrust = 4*mapobjectscale, vThrust = 4*mapobjectscale;
|
||||
vector3_t mom = {0, 0, 0};
|
||||
mobj_t *mo = flybot->target;
|
||||
|
||||
if (!P_MobjWasRemoved(mo))
|
||||
{
|
||||
mom.x = mo->momx;
|
||||
mom.y = mo->momy;
|
||||
mom.z = mo->momz;
|
||||
//S_StartSound(mo, flybot->info->deathsound);
|
||||
}
|
||||
|
||||
for (i = 0; i < 4; i++)
|
||||
{
|
||||
mo = P_SpawnMobjFromMobj(flybot, 0, 0, 0, MT_PARTICLE);
|
||||
P_SetMobjState(mo, S_SPINDASHDUST);
|
||||
mo->flags |= MF_NOSQUISH;
|
||||
mo->renderflags |= RF_FULLBRIGHT;
|
||||
mo->momx = mom.x;
|
||||
mo->momy = mom.y;
|
||||
mo->momz = mom.z + vThrust;
|
||||
P_Thrust(mo, angle, hThrust);
|
||||
vThrust *= -1;
|
||||
angle += ANGLE_90;
|
||||
}
|
||||
}
|
||||
|
|
@ -629,7 +629,7 @@ award_immediately (mobj_t *hyu)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!P_CanPickupItem(player, 1))
|
||||
if (!P_CanPickupItem(player, PICKUP_ITEMBOX))
|
||||
return false;
|
||||
|
||||
// Prevent receiving any more items or even stacked
|
||||
|
|
|
|||
|
|
@ -190,6 +190,9 @@ boolean Obj_OrbinautJawzCollide(mobj_t *t1, mobj_t *t2)
|
|||
return true;
|
||||
}
|
||||
|
||||
if (K_TryPickMeUp(t1, t2))
|
||||
return true;
|
||||
|
||||
if (t1->type == MT_GARDENTOP)
|
||||
{
|
||||
tumbleitem = true;
|
||||
|
|
|
|||
|
|
@ -47,11 +47,11 @@ static player_t *GetItemBoxPlayer(mobj_t *mobj)
|
|||
continue;
|
||||
}
|
||||
|
||||
// Always use normal item box rules -- could pass in "2" for fakes but they blend in better like this
|
||||
if (P_CanPickupItem(&players[i], 1))
|
||||
// Always use normal item box rules -- could pass in "PICKUP_EGGBOX" for fakes but they blend in better like this
|
||||
if (P_CanPickupItem(&players[i], PICKUP_ITEMBOX))
|
||||
{
|
||||
// Check for players who can take this pickup, but won't be allowed to (antifarming)
|
||||
UINT8 mytype = (mobj->flags2 & MF2_BOSSDEAD) ? 2 : 1;
|
||||
UINT8 mytype = (mobj->flags2 & MF2_BOSSDEAD) ? CHEESE_RINGBOX : CHEESE_ITEMBOX;
|
||||
if (P_IsPickupCheesy(&players[i], mytype))
|
||||
continue;
|
||||
|
||||
|
|
@ -115,6 +115,38 @@ void Obj_RandomItemVisuals(mobj_t *mobj)
|
|||
if (mobj->type != MT_RANDOMITEM)
|
||||
return;
|
||||
|
||||
// Fade items in as we cross the first checkpoint, but don't touch their visibility otherwise!
|
||||
if (!((mobj->flags & MF_NOCLIPTHING) || mobj->fuse))
|
||||
{
|
||||
UINT8 maxgrab = 0;
|
||||
|
||||
for (UINT8 i = 0; i <= r_splitscreen; i++)
|
||||
{
|
||||
maxgrab = max(maxgrab, players[displayplayers[i]].cangrabitems);
|
||||
}
|
||||
|
||||
if (maxgrab == 0)
|
||||
mobj->renderflags |= RF_DONTDRAW;
|
||||
else
|
||||
mobj->renderflags &= ~RF_DONTDRAW;
|
||||
|
||||
if (maxgrab > 0 && maxgrab <= EARLY_ITEM_FLICKER)
|
||||
{
|
||||
UINT8 maxtranslevel = NUMTRANSMAPS;
|
||||
|
||||
UINT8 trans = maxgrab;
|
||||
if (trans > maxtranslevel)
|
||||
trans = maxtranslevel;
|
||||
trans = NUMTRANSMAPS - trans;
|
||||
|
||||
mobj->renderflags &= ~(RF_TRANSMASK);
|
||||
|
||||
if (trans != 0)
|
||||
mobj->renderflags |= (trans << RF_TRANSSHIFT);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Respawn flow, documented by a dumb asshole:
|
||||
// P_TouchSpecialThing -> P_ItemPop sets fuse, NOCLIPTHING and DONTDRAW.
|
||||
// P_FuseThink does visual flicker, and when fuse is 0, unsets NOCLIPTHING/DONTDRAW/etc...
|
||||
|
|
|
|||
|
|
@ -120,18 +120,32 @@ boolean P_CanPickupItem(player_t *player, UINT8 weapon)
|
|||
if (player->exiting || mapreset || (player->pflags & PF_ELIMINATED) || player->itemRoulette.reserved)
|
||||
return false;
|
||||
|
||||
// 0: Sphere/Ring
|
||||
// 1: Random Item / Capsule
|
||||
// 2: Eggbox
|
||||
// 3: Paperitem
|
||||
// See p_local.h for pickup types
|
||||
|
||||
if (weapon != 2 && player->instaWhipCharge)
|
||||
if (weapon != PICKUP_EGGBOX && player->instaWhipCharge)
|
||||
return false;
|
||||
|
||||
if (weapon)
|
||||
if (weapon == PICKUP_ITEMBOX && !player->cangrabitems)
|
||||
return false;
|
||||
|
||||
if (weapon == PICKUP_RINGORSPHERE)
|
||||
{
|
||||
// No picking up rings while SPB is targetting you
|
||||
if (player->pflags & PF_RINGLOCK)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// No picking up rings while stunned
|
||||
if (player->stunned > 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Item slot already taken up
|
||||
if (weapon == 2)
|
||||
if (weapon == PICKUP_EGGBOX)
|
||||
{
|
||||
// Invulnerable
|
||||
if (player->flashing > 0)
|
||||
|
|
@ -153,11 +167,11 @@ boolean P_CanPickupItem(player_t *player, UINT8 weapon)
|
|||
// Item slot already taken up
|
||||
if (player->itemRoulette.active == true
|
||||
|| player->ringboxdelay > 0
|
||||
|| (weapon != 3 && player->itemamount)
|
||||
|| (weapon != PICKUP_PAPERITEM && player->itemamount)
|
||||
|| (player->itemflags & IF_ITEMOUT))
|
||||
return false;
|
||||
|
||||
if (weapon == 3 && K_GetShieldFromItem(player->itemtype) != KSHIELD_NONE)
|
||||
if (weapon == PICKUP_PAPERITEM && K_GetShieldFromItem(player->itemtype) != KSHIELD_NONE)
|
||||
return false; // No stacking shields!
|
||||
}
|
||||
}
|
||||
|
|
@ -168,7 +182,7 @@ boolean P_CanPickupItem(player_t *player, UINT8 weapon)
|
|||
// Allow players to pick up only one pickup from each set of pickups.
|
||||
// Anticheese pickup types are different than-P_CanPickupItem weapon, because that system is
|
||||
// already slightly scary without introducing special cases for different types of the same pickup.
|
||||
// 1 = floating item, 2 = perma ring, 3 = capsule
|
||||
// See p_local.h for cheese types.
|
||||
boolean P_IsPickupCheesy(player_t *player, UINT8 type)
|
||||
{
|
||||
extern consvar_t cv_debugcheese;
|
||||
|
|
@ -411,7 +425,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
if (special->scale < special->destscale/2)
|
||||
return;
|
||||
|
||||
if (!P_CanPickupItem(player, 3) || (player->itemamount && player->itemtype != special->threshold))
|
||||
if (!P_CanPickupItem(player, PICKUP_PAPERITEM) || (player->itemamount && player->itemtype != special->threshold))
|
||||
return;
|
||||
|
||||
player->itemtype = special->threshold;
|
||||
|
|
@ -431,9 +445,9 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
special->flags &= ~MF_SPECIAL;
|
||||
return;
|
||||
case MT_RANDOMITEM: {
|
||||
UINT8 cheesetype = (special->flags2 & MF2_BOSSDEAD) ? 2 : 1; // perma ring box
|
||||
UINT8 cheesetype = (special->flags2 & MF2_BOSSDEAD) ? CHEESE_RINGBOX : CHEESE_ITEMBOX; // perma ring box
|
||||
|
||||
if (!P_CanPickupItem(player, 1))
|
||||
if (!P_CanPickupItem(player, PICKUP_ITEMBOX))
|
||||
return;
|
||||
if (P_IsPickupCheesy(player, cheesetype))
|
||||
return;
|
||||
|
|
@ -473,7 +487,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
return;
|
||||
}
|
||||
case MT_SPHEREBOX:
|
||||
if (!P_CanPickupItem(player, 0))
|
||||
if (!P_CanPickupItem(player, PICKUP_RINGORSPHERE))
|
||||
return;
|
||||
|
||||
special->momx = special->momy = special->momz = 0;
|
||||
|
|
@ -493,15 +507,13 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
return;
|
||||
break;
|
||||
case KITEM_SUPERRING:
|
||||
if (player->pflags & PF_RINGLOCK) // no cheaty rings
|
||||
return;
|
||||
if (player->instaWhipCharge)
|
||||
if (!P_CanPickupItem(player, PICKUP_RINGORSPHERE)) // no cheaty rings
|
||||
return;
|
||||
break;
|
||||
default:
|
||||
if (!P_CanPickupItem(player, 1))
|
||||
if (!P_CanPickupItem(player, PICKUP_ITEMCAPSULE))
|
||||
return;
|
||||
if (P_IsPickupCheesy(player, 3))
|
||||
if (P_IsPickupCheesy(player, CHEESE_ITEMCAPSULE))
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
|
@ -558,7 +570,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
return;
|
||||
}
|
||||
case MT_EMERALD:
|
||||
if (!P_CanPickupItem(player, 0) || P_PlayerInPain(player))
|
||||
if (!P_CanPickupItem(player, PICKUP_RINGORSPHERE) || P_PlayerInPain(player))
|
||||
return;
|
||||
|
||||
if (special->threshold > 0)
|
||||
|
|
@ -617,7 +629,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
return;
|
||||
|
||||
case MT_CDUFO: // SRB2kart
|
||||
if (special->fuse || !P_CanPickupItem(player, 1))
|
||||
if (special->fuse || !P_CanPickupItem(player, PICKUP_ITEMBOX))
|
||||
return;
|
||||
|
||||
K_StartItemRoulette(player, false);
|
||||
|
|
@ -655,6 +667,9 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
if (!player->mo || player->spectator)
|
||||
return;
|
||||
|
||||
if (K_TryPickMeUp(special, toucher))
|
||||
return;
|
||||
|
||||
// attach to player!
|
||||
P_SetTarget(&special->tracer, toucher);
|
||||
toucher->flags |= MF_NOGRAVITY;
|
||||
|
|
@ -681,19 +696,11 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
if (special->extravalue1)
|
||||
return;
|
||||
|
||||
// No picking up rings while SPB is targetting you
|
||||
if (player->pflags & PF_RINGLOCK)
|
||||
return;
|
||||
|
||||
// Prepping instawhip? Don't ruin it by collecting rings
|
||||
if (player->instaWhipCharge)
|
||||
return;
|
||||
|
||||
// Don't immediately pick up spilled rings
|
||||
if (special->threshold > 0 || P_PlayerInPain(player) || player->spindash) // player->spindash: Otherwise, players can pick up rings that are thrown out of them from invinc spindash penalty
|
||||
return;
|
||||
|
||||
if (!(P_CanPickupItem(player, 0)))
|
||||
if (!(P_CanPickupItem(player, PICKUP_RINGORSPHERE)))
|
||||
return;
|
||||
|
||||
// Reached the cap, don't waste 'em!
|
||||
|
|
@ -715,7 +722,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
return;
|
||||
|
||||
case MT_BLUESPHERE:
|
||||
if (!(P_CanPickupItem(player, 0)))
|
||||
if (!(P_CanPickupItem(player, PICKUP_RINGORSPHERE)))
|
||||
return;
|
||||
|
||||
P_GivePlayerSpheres(player, 1);
|
||||
|
|
@ -2301,6 +2308,9 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
|
|||
case MT_EMFAUCET_DRIP:
|
||||
Obj_EMZDripDeath(target);
|
||||
break;
|
||||
case MT_FLYBOT767:
|
||||
Obj_FlybotDeath(target);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
@ -3001,6 +3011,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
|
|||
UINT8 type = (damagetype & DMG_TYPEMASK);
|
||||
const boolean hardhit = (type == DMG_EXPLODE || type == DMG_KARMA || type == DMG_TUMBLE); // This damage type can do evil stuff like ALWAYS combo
|
||||
INT16 ringburst = 5;
|
||||
UINT16 stunTics = 0;
|
||||
|
||||
// Check if the player is allowed to be damaged!
|
||||
// If not, then spawn the instashield effect instead.
|
||||
|
|
@ -3392,6 +3403,27 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
|
|||
player->flipDI = true;
|
||||
}
|
||||
|
||||
// Apply stun!
|
||||
// Feel free to move these calculations higher up if different damage sources should apply variable stun in future
|
||||
#define MIN_STUNTICS (8 * TICRATE)
|
||||
#define MAX_STUNTICS (18 * TICRATE)
|
||||
stunTics = Easing_Linear((player->kartweight - 1) * FRACUNIT / 8, MAX_STUNTICS, MIN_STUNTICS);
|
||||
stunTics >>= player->stunnedCombo; // consecutive hits add half as much stun as the previous hit
|
||||
|
||||
// 1/3 base stun values in battle
|
||||
if (gametyperules & GTR_SPHERES)
|
||||
{
|
||||
stunTics /= 3;
|
||||
}
|
||||
|
||||
if (player->stunnedCombo < UINT8_MAX)
|
||||
{
|
||||
player->stunnedCombo++;
|
||||
}
|
||||
player->stunned = (player->stunned & 0x8000) | min(0x7FFF, (player->stunned & 0x7FFF) + stunTics);
|
||||
#undef MIN_STUNTICS
|
||||
#undef MAX_STUNTICS
|
||||
|
||||
K_DefensiveOverdrive(target->player);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -557,6 +557,17 @@ void P_CheckTimeLimit(void);
|
|||
void P_CheckPointLimit(void);
|
||||
boolean P_CheckRacers(void);
|
||||
|
||||
// Pickup types
|
||||
#define PICKUP_RINGORSPHERE 0
|
||||
#define PICKUP_ITEMBOX 1
|
||||
#define PICKUP_EGGBOX 2
|
||||
#define PICKUP_PAPERITEM 3
|
||||
#define PICKUP_ITEMCAPSULE 4
|
||||
|
||||
#define CHEESE_ITEMBOX 1
|
||||
#define CHEESE_RINGBOX 2
|
||||
#define CHEESE_ITEMCAPSULE 3
|
||||
|
||||
boolean P_CanPickupItem(player_t *player, UINT8 weapon);
|
||||
boolean P_IsPickupCheesy(player_t *player, UINT8 type);
|
||||
void P_UpdateLastPickup(player_t *player, UINT8 type);
|
||||
|
|
|
|||
23
src/p_map.c
23
src/p_map.c
|
|
@ -35,6 +35,7 @@
|
|||
#include "k_terrain.h"
|
||||
#include "k_objects.h"
|
||||
#include "k_boss.h"
|
||||
#include "k_hitlag.h" // K_AddHitlag
|
||||
|
||||
#include "r_splats.h"
|
||||
|
||||
|
|
@ -4122,10 +4123,24 @@ static void P_BouncePlayerMove(mobj_t *mo, TryMoveResult_t *result)
|
|||
// Combo avoidance!
|
||||
if (mo->player && P_PlayerInPain(mo->player) && gametyperules & GTR_BUMPERS && mo->health == 1)
|
||||
{
|
||||
K_StumblePlayer(mo->player);
|
||||
K_BumperInflate(mo->player);
|
||||
mo->player->tumbleBounces = TUMBLEBOUNCES;
|
||||
mo->hitlag = max(mo->hitlag, 6);
|
||||
P_ResetPlayer(mo->player);
|
||||
mo->player->spinouttimer = 0;
|
||||
mo->player->wipeoutslow = 0;
|
||||
mo->player->tumbleBounces = 0;
|
||||
|
||||
K_AddHitLag(mo, 3, false);
|
||||
|
||||
// "I dunno man, just fuckin' do it" - jart
|
||||
S_StartSound(mo, sfx_mbs45);
|
||||
S_StartSound(mo, sfx_mbs45);
|
||||
S_StartSound(mo, sfx_mbs45);
|
||||
S_StartSound(mo, sfx_mbs45);
|
||||
S_StartSound(mo, sfx_mbv84);
|
||||
|
||||
if (mo->eflags & MFE_VERTICALFLIP)
|
||||
mo->momz -= 40*mo->scale;
|
||||
else
|
||||
mo->momz += 40*mo->scale;
|
||||
}
|
||||
|
||||
mo->momx = tmxmove;
|
||||
|
|
|
|||
20
src/p_mobj.c
20
src/p_mobj.c
|
|
@ -5402,6 +5402,19 @@ static boolean P_IsTrackerType(INT32 type)
|
|||
case MT_GARDENTOP: // Frey
|
||||
return true;
|
||||
|
||||
case MT_JAWZ_SHIELD: // Pick-me-up
|
||||
case MT_ORBINAUT:
|
||||
case MT_ORBINAUT_SHIELD:
|
||||
case MT_DROPTARGET:
|
||||
case MT_DROPTARGET_SHIELD:
|
||||
case MT_LANDMINE:
|
||||
case MT_BANANA:
|
||||
case MT_BANANA_SHIELD:
|
||||
case MT_GACHABOM:
|
||||
case MT_EGGMANITEM:
|
||||
case MT_EGGMANITEM_SHIELD:
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
|
@ -10070,6 +10083,12 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
|
|||
break;
|
||||
}
|
||||
|
||||
case MT_FLYBOT767:
|
||||
{
|
||||
Obj_FlybotThink(mobj);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
// check mobj against possible water content, before movement code
|
||||
P_MobjCheckWater(mobj);
|
||||
|
|
@ -12721,6 +12740,7 @@ static boolean P_AllowMobjSpawn(mapthing_t* mthing, mobjtype_t i)
|
|||
switch (i)
|
||||
{
|
||||
case MT_RING:
|
||||
case MT_RANDOMITEM:
|
||||
if (modeattacking & ATTACKING_SPB)
|
||||
return false;
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -455,10 +455,14 @@ static void P_NetArchivePlayers(savebuffer_t *save)
|
|||
WRITEUINT8(save->p, players[i].noEbrakeMagnet);
|
||||
WRITEUINT8(save->p, players[i].tumbleBounces);
|
||||
WRITEUINT16(save->p, players[i].tumbleHeight);
|
||||
WRITEUINT16(save->p, players[i].stunned);
|
||||
WRITEUINT8(save->p, players[i].stunnedCombo);
|
||||
|
||||
WRITEUINT8(save->p, players[i].justDI);
|
||||
WRITEUINT8(save->p, players[i].flipDI);
|
||||
|
||||
WRITEUINT8(save->p, players[i].cangrabitems);
|
||||
|
||||
WRITESINT8(save->p, players[i].drift);
|
||||
WRITEFIXED(save->p, players[i].driftcharge);
|
||||
WRITEUINT16(save->p, players[i].driftboost);
|
||||
|
|
@ -506,6 +510,8 @@ static void P_NetArchivePlayers(savebuffer_t *save)
|
|||
|
||||
WRITESINT8(save->p, players[i].itemtype);
|
||||
WRITEUINT8(save->p, players[i].itemamount);
|
||||
WRITESINT8(save->p, players[i].backupitemtype);
|
||||
WRITEUINT8(save->p, players[i].backupitemamount);
|
||||
WRITESINT8(save->p, players[i].throwdir);
|
||||
|
||||
WRITEUINT8(save->p, players[i].sadtimer);
|
||||
|
|
@ -1094,10 +1100,14 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
|
|||
players[i].noEbrakeMagnet = READUINT8(save->p);
|
||||
players[i].tumbleBounces = READUINT8(save->p);
|
||||
players[i].tumbleHeight = READUINT16(save->p);
|
||||
players[i].stunned = READUINT16(save->p);
|
||||
players[i].stunnedCombo = READUINT8(save->p);
|
||||
|
||||
players[i].justDI = READUINT8(save->p);
|
||||
players[i].flipDI = (boolean)READUINT8(save->p);
|
||||
|
||||
players[i].cangrabitems = READUINT8(save->p);
|
||||
|
||||
players[i].drift = READSINT8(save->p);
|
||||
players[i].driftcharge = READFIXED(save->p);
|
||||
players[i].driftboost = READUINT16(save->p);
|
||||
|
|
@ -1145,6 +1155,8 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
|
|||
|
||||
players[i].itemtype = READSINT8(save->p);
|
||||
players[i].itemamount = READUINT8(save->p);
|
||||
players[i].backupitemtype = READSINT8(save->p);
|
||||
players[i].backupitemamount = READUINT8(save->p);
|
||||
players[i].throwdir = READSINT8(save->p);
|
||||
|
||||
players[i].sadtimer = READUINT8(save->p);
|
||||
|
|
|
|||
|
|
@ -2359,6 +2359,8 @@ static void P_UpdatePlayerAngle(player_t *player)
|
|||
angle_t targetAngle = (player->cmd.angle) << TICCMD_REDUCE;
|
||||
angle_t targetDelta = targetAngle - (player->mo->angle);
|
||||
|
||||
#define SOLVERANGLECHEATS
|
||||
|
||||
#ifdef SOLVERANGLECHEATS
|
||||
// Corrections via fake turn go through easing.
|
||||
// That means undoing them takes the same amount of time as doing them.
|
||||
|
|
|
|||
|
|
@ -44,20 +44,11 @@ public:
|
|||
|
||||
Handle(NullHandleType) noexcept : Handle() {}
|
||||
|
||||
Handle(const Handle&) = default;
|
||||
Handle(Handle&& rhs) noexcept
|
||||
{
|
||||
id_ = std::exchange(rhs.id_, 0);
|
||||
generation_ = std::exchange(rhs.generation_, 0);
|
||||
};
|
||||
Handle(const Handle&) noexcept = default;
|
||||
Handle(Handle&&) noexcept = default;
|
||||
|
||||
Handle& operator=(const Handle&) = default;
|
||||
Handle& operator=(Handle&& rhs) noexcept
|
||||
{
|
||||
id_ = std::exchange(rhs.id_, 0);
|
||||
generation_ = std::exchange(rhs.generation_, 0);
|
||||
return *this;
|
||||
}
|
||||
Handle& operator=(const Handle&) noexcept = default;
|
||||
Handle& operator=(Handle&&) noexcept = default;
|
||||
|
||||
// Conversions from Handles of derived type U to base type T
|
||||
|
||||
|
|
|
|||
|
|
@ -1540,6 +1540,9 @@ sfxinfo_t S_sfx[NUMSFX] =
|
|||
// Walltransfer
|
||||
{"ggfall", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
|
||||
|
||||
// :apple:
|
||||
{"aple", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
|
||||
|
||||
// SRB2kart - Skin sounds
|
||||
{"kwin", false, 64, 96, -1, NULL, 0, SKSKWIN, -1, LUMPERROR, ""},
|
||||
{"klose", false, 64, 96, -1, NULL, 0, SKSKLOSE, -1, LUMPERROR, ""},
|
||||
|
|
|
|||
|
|
@ -1616,6 +1616,9 @@ typedef enum
|
|||
// Walltransfer fuck
|
||||
sfx_ggfall,
|
||||
|
||||
// :apple:
|
||||
sfx_aple,
|
||||
|
||||
// And LASTLY, Kart's skin sounds.
|
||||
sfx_kwin,
|
||||
sfx_klose,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue