Merge remote-tracking branch 'origin/master' into no-trick-input-filter

This commit is contained in:
AJ Martinez 2024-05-19 17:10:14 -07:00
commit 3baf39a1fd
20 changed files with 248 additions and 90 deletions

View file

@ -2,6 +2,10 @@ include:
- '.gitlab/ci/templates/*.yml'
- '.gitlab/ci/jobs/*.yml'
workflow:
auto_cancel:
on_new_commit: interruptible
variables:
GIT_CLONE_PATH: $CI_BUILDS_DIR/$CI_CONCURRENT_ID/$CI_PROJECT_PATH
GIT_DEPTH: 20
@ -10,5 +14,6 @@ stages:
- build
default:
interruptible: true
artifacts:
expire_in: 1 day

View file

@ -46,6 +46,9 @@ Environment::Environment()
// Not that we're adding any modules to it, though. :p
global->active = true;
// Set a branch limit (same as ZDoom's instruction limit)
branchLimit = 2000000;
// Add the data & function pointers.
// Starting with raw ACS0 codes. I'm using this classic-style
@ -405,3 +408,42 @@ ACSVM::Word Environment::callSpecImpl
P_ProcessSpecial(activator, spec, args, stringargs);
return 1;
}
void Environment::printKill(ACSVM::Thread *thread, ACSVM::Word type, ACSVM::Word data)
{
std::string scriptName;
ACSVM::String *scriptNamePtr = (thread->script != nullptr) ? (thread->script->name.s) : nullptr;
if (scriptNamePtr && scriptNamePtr->len)
scriptName = std::string(scriptNamePtr->str);
else
scriptName = std::to_string((int)thread->script->name.i);
ACSVM::KillType killType = static_cast<ACSVM::KillType>(type);
if (killType == ACSVM::KillType::BranchLimit)
{
CONS_Alert(CONS_ERROR, "Terminated runaway script %s\n", scriptName.c_str());
return;
}
else if (killType == ACSVM::KillType::UnknownCode)
{
CONS_Alert(CONS_ERROR, "ACSVM ERROR: Unknown opcode %d in script %s\n", data, scriptName.c_str());
}
else if (killType == ACSVM::KillType::UnknownFunc)
{
CONS_Alert(CONS_ERROR, "ACSVM ERROR: Unknown function %d in script %s\n", data, scriptName.c_str());
}
else if (killType == ACSVM::KillType::OutOfBounds)
{
CONS_Alert(CONS_ERROR, "ACSVM ERROR: Jumped to out of bounds location %lu in script %s\n",
(thread->codePtr - thread->module->codeV.data() - 1), scriptName.c_str());
}
else
{
CONS_Alert(CONS_ERROR, "ACSVM ERROR: Kill %u:%d at %lu in script %s\n",
type, data, (thread->codePtr - thread->module->codeV.data() - 1), scriptName.c_str());
}
CONS_Printf("Script terminated.\n");
}

View file

@ -32,6 +32,8 @@ public:
virtual ACSVM::Thread *allocThread();
virtual void printKill(ACSVM::Thread *thread, ACSVM::Word type, ACSVM::Word data);
protected:
virtual void loadModule(ACSVM::Module *module);

View file

@ -860,7 +860,7 @@ consvar_t cv_ufo_health = OnlineCheat("ufo_health", "-1").min_max(-1, 100).descr
consvar_t cv_botscanvote = ServerCheat("botscanvote", "No").yes_no();
void Gravity_OnChange(void);
consvar_t cv_gravity = ServerCheat("gravity", "0.8").floating_point().onchange(Gravity_OnChange).description("Change the default gravity"); // change DEFAULT_GRAVITY if you change this
consvar_t cv_gravity = ServerCheat("gravity", "0.8").floating_point().min_max(0, 200*FRACUNIT).onchange(Gravity_OnChange).description("Change the default gravity"); // change DEFAULT_GRAVITY if you change this
consvar_t cv_kartdebugcolorize = ServerCheat("debugcolorize", "Off").on_off().description("Show all colorized options on the HUD");
consvar_t cv_kartdebugdirector = ServerCheat("debugdirector", "Off").on_off().description("Show director AI on the HUD");

View file

@ -5762,8 +5762,26 @@ static void CL_SendClientCmd(void)
spike_time = 0;
}
/*
if (server) // Clients have to wait for the gamestate to make it back. Servers don't!
lagDelay *= 2; // Simulate the HELLFUCK NIGHTMARE of a complete round trip.
*/
// [deep breath in]
// Plausible, elegant explanation that is WRONG AND SUPER HARMFUL.
// Clients with stable connections were adding their mindelay to network delay,
// even when their mindelay was as high or higher than network delay—which made
// client delay APPEAR slower than host mindelay, by the exact value that made
// "lmao just double it" make sense at the time.
//
// While this fix made client connections match server mindelay in our most common
// test environment, it also masked an issue that seriously affected online handling
// responsiveness, completely ruining our opportunity to further investigate it!
//
// See UpdatePingTable.
// I am taking this shitty code to my grave as an example of "never trust your brain".
// -Tyron 2024-05-15
}
packetsize = sizeof (clientcmd_pak);
@ -6335,8 +6353,15 @@ static void UpdatePingTable(void)
}
else // We're a client, handle mindelay on the way out.
{
if ((neededtic - gametic) < (tic_t)cv_mindelay.value)
lowest_lag = cv_mindelay.value - (neededtic - gametic);
// Previously (neededtic - gametic) - WRONG VALUE!
// Pretty sure that's measuring jitter, not RTT.
// Stable connections would be punished by adding their mindelay to network delay!
tic_t mydelay = playerpingtable[consoleplayer];
if (mydelay < (tic_t)cv_mindelay.value)
lowest_lag = cv_mindelay.value - mydelay;
else
lowest_lag = 0;
}
}

View file

@ -118,7 +118,7 @@ extern "C" consvar_t cv_lua_profile, cv_menuframeskip;
#define ASSET_HASH_MAPS_PK3 "a8bd1f924531c483f500d96583b7b837"
#define ASSET_HASH_UNLOCKS_PK3 "ebc06ff46c2cc80e93dadf5f7099d7b8"
#define ASSET_HASH_STAFFGHOSTS_PK3 "9cb77f6c0e801c1bc61ca84870b65707"
#define ASSET_HASH_SHADERS_PK3 "dbfb1d5eb9818cd2fb81680c0bab05c0"
#define ASSET_HASH_SHADERS_PK3 "7aefd2aa55129b31210aa094cf782695"
#ifdef USE_PATCH_FILE
#define ASSET_HASH_PATCH_PK3 "00000000000000000000000000000000"
#endif

View file

@ -6461,14 +6461,12 @@ void Command_Retry_f(void)
*/
static void Command_Isgamemodified_f(void)
{
if (majormods)
CONS_Printf("The game has been modified with major addons, so you cannot play Record Attack.\n");
else if (savemoddata)
CONS_Printf("The game has been modified with an addon with its own save data, so you can play Record Attack and earn medals.\n");
if (savemoddata)
CONS_Printf("The game has been modified with an addon using its own save data.\n");
else if (modifiedgame)
CONS_Printf("The game has been modified with only minor addons. You can play Record Attack, earn medals and unlock extras.\n");
CONS_Printf("The game has been modified, but is still using Ring Racers save data.\n");
else
CONS_Printf("The game has not been modified. You can play Record Attack, earn medals and unlock extras.\n");
CONS_Printf("The game has not been modified.\n");
}
#ifdef _DEBUG

View file

@ -170,8 +170,9 @@ demoghost *ghosts = NULL;
// - SPB cup TA replays were recorded at this time
// - Slope physics changed with a scaling fix
// - 0x000C (Ring Racers v2.2)
// - 0x000D (Ring Racers v2.3)
#define DEMOVERSION 0x000C
#define DEMOVERSION 0x000D
boolean G_CompatLevel(UINT16 level)
{
@ -2278,6 +2279,7 @@ void G_BeginRecording(void)
WRITEUINT8(demobuf.p, grandprixinfo.gamespeed);
WRITEUINT8(demobuf.p, grandprixinfo.masterbots == true);
WRITEUINT8(demobuf.p, grandprixinfo.eventmode);
WRITEUINT32(demobuf.p, grandprixinfo.specialDamage);
}
// Save netUnlocked from actual unlocks
@ -2525,8 +2527,9 @@ UINT8 G_CmpDemoTime(char *oldname, char *newname)
{
case DEMOVERSION: // latest always supported
case 0x0009: // older staff ghosts
case 0x000A: // older staff ghosts
case 0x000B: // older staff ghosts
case 0x000A: // 2.0, 2.1
case 0x000B: // 2.2 indev (staff ghosts)
case 0x000C: // 2.2
break;
// too old, cannot support.
default:
@ -2676,8 +2679,9 @@ void G_LoadDemoInfo(menudemo_t *pdemo, boolean allownonmultiplayer)
{
case DEMOVERSION: // latest always supported
case 0x0009: // older staff ghosts
case 0x000A: // older staff ghosts
case 0x000B: // older staff ghosts
case 0x000A: // 2.0, 2.1
case 0x000B: // 2.2 indev (staff ghosts)
case 0x000C: // 2.2
if (P_SaveBufferRemaining(&info) < 64)
{
goto corrupt;
@ -3105,8 +3109,9 @@ void G_DoPlayDemoEx(const char *defdemoname, lumpnum_t deflumpnum)
{
case DEMOVERSION: // latest always supported
case 0x0009: // older staff ghosts
case 0x000A: // older staff ghosts
case 0x000B: // older staff ghosts
case 0x000A: // 2.0, 2.1
case 0x000B: // 2.2 indev (staff ghosts)
case 0x000C: // 2.2
break;
// too old, cannot support.
default:
@ -3275,6 +3280,10 @@ void G_DoPlayDemoEx(const char *defdemoname, lumpnum_t deflumpnum)
grandprixinfo.gamespeed = READUINT8(demobuf.p);
grandprixinfo.masterbots = READUINT8(demobuf.p) != 0;
grandprixinfo.eventmode = static_cast<gpEvent_e>(READUINT8(demobuf.p));
if (demo.version >= 0x000D)
{
grandprixinfo.specialDamage = READUINT32(demobuf.p);
}
}
// Load unlocks into netUnlocked
@ -3565,8 +3574,9 @@ void G_AddGhost(savebuffer_t *buffer, const char *defdemoname)
{
case DEMOVERSION: // latest always supported
case 0x0009: // older staff ghosts
case 0x000A: // older staff ghosts
case 0x000B: // older staff ghosts
case 0x000A: // 2.0, 2.1
case 0x000B: // 2.2 indev (staff ghosts)
case 0x000C: // 2.2
break;
// too old, cannot support.
default:
@ -3653,7 +3663,11 @@ void G_AddGhost(savebuffer_t *buffer, const char *defdemoname)
}
if ((flags & DF_GRANDPRIX))
{
p += 3;
if (ghostversion >= 0x000D)
p++;
}
// Skip unlockables
{
@ -3823,8 +3837,9 @@ staffbrief_t *G_GetStaffGhostBrief(UINT8 *buffer)
{
case DEMOVERSION: // latest always supported
case 0x0009: // older staff ghosts
case 0x000A: // older staff ghosts
case 0x000B: // older staff ghosts
case 0x000A: // 2.0, 2.1
case 0x000B: // 2.2 indev (staff ghosts)
case 0x000C: // 2.2
break;
// too old, cannot support.
@ -3878,7 +3893,11 @@ staffbrief_t *G_GetStaffGhostBrief(UINT8 *buffer)
}
if ((flags & DF_GRANDPRIX))
{
p += 3;
if (ghostversion >= 0x000D)
p++;
}
// Skip unlockables
{

View file

@ -358,7 +358,7 @@ void G_ClearRecords(void)
// TODO: Technically, these should only remove time attack records here.
// But I'm out of juice for dev (+ literally, just finished some OJ).
// The stats need to be cleared in M_ClearStats, and I guess there's
// The stats need to be cleared in M_ClearStats, and I guess there's
// no perfect place to wipe mapvisited because it's not actually part of
// basegame progression... so here's fine for launch. ~toast 100424
unloaded_mapheader_t *unloadedmap, *nextunloadedmap = NULL;
@ -652,7 +652,7 @@ static void G_UpdateRecordReplays(void)
}
}
// for consistency among messages: this modifies the game and removes savemoddata.
// for consistency among messages: this marks the game as modified.
void G_SetGameModified(boolean silent, boolean major)
{
if ((majormods && modifiedgame) || !mainwads || (refreshdirmenu & REFRESHDIR_GAMEDATA)) // new gamedata amnesty?
@ -666,9 +666,6 @@ void G_SetGameModified(boolean silent, boolean major)
//savemoddata = false; -- there is literally no reason to do this anymore.
majormods = true;
if (!silent)
CONS_Alert(CONS_NOTICE, M_GetText("Game must be restarted to play Record Attack.\n"));
// If in record attack recording, cancel it.
if (modeattacking)
M_EndModeAttackRun();

View file

@ -287,15 +287,28 @@ void srb2::save_ng_gamedata()
std::string gamedataname_s {gamedatafilename};
fs::path savepath {fmt::format("{}/{}", srb2home, gamedataname_s)};
int random_number = rand();
fs::path tmpsavepath {fmt::format("{}/{}_{}.tmp", srb2home, gamedataname_s, random_number)};
fs::path baksavepath {fmt::format("{}/{}.bak", srb2home, gamedataname_s)};
json ngdata_json = ng;
if (fs::exists(savepath))
{
try
{
fs::rename(savepath, baksavepath);
}
catch (const fs::filesystem_error& ex)
{
CONS_Alert(CONS_ERROR, "Failed to record backup save. Not attempting to save. %s\n", ex.what());
return;
}
}
try
{
std::string tmpsavepathstring = tmpsavepath.string();
srb2::io::FileStream file {tmpsavepathstring, srb2::io::FileStreamMode::kWrite};
std::string savepathstring = savepath.string();
srb2::io::FileStream file {savepathstring, srb2::io::FileStreamMode::kWrite};
// The header is necessary to validate during loading.
srb2::io::write(static_cast<uint32_t>(GD_VERSION_MAJOR), file); // major
@ -308,21 +321,11 @@ void srb2::save_ng_gamedata()
}
catch (const std::exception& ex)
{
CONS_Alert(CONS_ERROR, "NG Gamedata save failed: %s\n", ex.what());
CONS_Alert(CONS_ERROR, "NG Gamedata save failed. Check directory for a ringdata.dat.bak. %s\n", ex.what());
}
catch (...)
{
CONS_Alert(CONS_ERROR, "NG Gamedata save failed\n");
}
try
{
// Now that the save is written successfully, move it over the old save
fs::rename(tmpsavepath, savepath);
}
catch (const fs::filesystem_error& ex)
{
CONS_Alert(CONS_ERROR, "NG Gamedata save succeeded but did not replace old save successfully: %s\n", ex.what());
CONS_Alert(CONS_ERROR, "NG Gamedata save failed. Check directory for a ringdata.dat.bak.\n");
}
}

View file

@ -31,6 +31,7 @@
#include "m_random.h"
#include "r_things.h" // numskins
#include "k_roulette.h"
#include "m_easing.h"
/*--------------------------------------------------
static inline boolean K_ItemButtonWasDown(const player_t *player)
@ -1193,7 +1194,10 @@ static void K_BotItemLightning(const player_t *player, ticcmd_t *cmd)
{
ZoneScoped;
if (K_BotUseItemNearPlayer(player, cmd, 192*player->mo->scale) == false)
fixed_t radius = 192 * player->mo->scale;
radius = Easing_Linear(FRACUNIT * player->botvars.difficulty / MAXBOTDIFFICULTY, 2*radius, radius);
if (K_BotUseItemNearPlayer(player, cmd, radius) == false)
{
if (player->botvars.itemconfirm > 10*TICRATE)
{
@ -1232,7 +1236,8 @@ static void K_BotItemBubble(const player_t *player, ticcmd_t *cmd)
if (player->bubblecool <= 0)
{
const fixed_t radius = 192 * player->mo->scale;
fixed_t radius = 192 * player->mo->scale;
radius = Easing_Linear(FRACUNIT * player->botvars.difficulty / MAXBOTDIFFICULTY, 2*radius, radius);
for (i = 0; i < MAXPLAYERS; i++)
{

View file

@ -2148,6 +2148,12 @@ void K_DrawKartPositionNumXY(
boolean exit, boolean lastLap, boolean losing
)
{
if (cv_reducevfx.value != 0)
{
// Reduce the flashing rate
counter /= 4;
}
counter /= 3; // Alternate colors every three frames
UINT8 *color = NULL;
@ -5025,14 +5031,31 @@ static void K_drawKartStartBulbs(void)
bulbtic -= 14;
// Reduce VFX disables the bulb animation while still presenting this indicator
if (bulbtic > length)
{
bulbtic -= length;
patchnum = chillloop_animation[bulbtic % 2];
if (cv_reducevfx.value != 0)
{
patchnum = chillloop_animation[0];
}
else
{
patchnum = chillloop_animation[bulbtic % 2];
}
}
else
{
patchnum = loop_animation[bulbtic % 4];
if (cv_reducevfx.value != 0)
{
patchnum = loop_animation[0];
}
else
{
patchnum = loop_animation[bulbtic % 4];
}
}
}
}

View file

@ -12617,7 +12617,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
// if a player picks up an item during the instawhip input safety window—the one that triggers
// after you burn to 0 rings—they can continue to hold the input, then charge a usable whip
// without stopping the roulette and acquiring an item, which cancels it.
//
//
// No ghosts use this technique, but your least favorite tournament player might.
if (player->itemRoulette.active)
{
@ -12857,9 +12857,9 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
}
else if (ATTACK_IS_DOWN && (player->itemflags & IF_ITEMOUT)) // Banana x3 thrown
{
player->itemamount--;
K_ThrowKartItem(player, false, MT_BANANA, -1, 0, 0);
K_PlayAttackTaunt(player->mo);
player->itemamount--;
K_UpdateHnextList(player, false);
player->botvars.itemconfirm = 0;
}
@ -12923,9 +12923,9 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
}
else if (ATTACK_IS_DOWN && (player->itemflags & IF_ITEMOUT)) // Orbinaut x3 thrown
{
player->itemamount--;
K_ThrowKartItem(player, true, MT_ORBINAUT, 1, 0, 0);
K_PlayAttackTaunt(player->mo);
player->itemamount--;
K_UpdateHnextList(player, false);
player->botvars.itemconfirm = 0;
}
@ -12966,9 +12966,9 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
}
else if (ATTACK_IS_DOWN && HOLDING_ITEM && (player->itemflags & IF_ITEMOUT)) // Jawz thrown
{
player->itemamount--;
K_ThrowKartItem(player, true, MT_JAWZ, 1, 0, 0);
K_PlayAttackTaunt(player->mo);
player->itemamount--;
K_UpdateHnextList(player, false);
player->botvars.itemconfirm = 0;
}
@ -12994,9 +12994,9 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
}
else if (ATTACK_IS_DOWN && (player->itemflags & IF_ITEMOUT))
{
player->itemamount--;
K_ThrowKartItem(player, false, MT_SSMINE, 1, 1, 0);
K_PlayAttackTaunt(player->mo);
player->itemamount--;
player->itemflags &= ~IF_ITEMOUT;
K_UpdateHnextList(player, true);
player->botvars.itemconfirm = 0;
@ -13032,9 +13032,9 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
}
else if (ATTACK_IS_DOWN && (player->itemflags & IF_ITEMOUT))
{
player->itemamount--;
K_ThrowKartItem(player, (player->throwdir > 0), MT_DROPTARGET, -1, 0, 0);
K_PlayAttackTaunt(player->mo);
player->itemamount--;
player->itemflags &= ~IF_ITEMOUT;
K_UpdateHnextList(player, true);
player->botvars.itemconfirm = 0;
@ -13276,9 +13276,10 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
if (player->bubbleblowup > bubbletime*2)
{
player->itemamount--;
K_ThrowKartItem(player, (player->throwdir > 0), MT_BUBBLESHIELDTRAP, -1, 0, 0);
if (player->throwdir == -1)
{
{
P_InstaThrust(player->mo, player->mo->angle, player->speed + (80 * mapobjectscale));
player->wavedashboost += TICRATE;
player->wavedashpower = FRACUNIT;
@ -13288,7 +13289,6 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
player->bubbleblowup = 0;
player->bubblecool = 0;
player->itemflags &= ~IF_HOLDREADY;
player->itemamount--;
player->botvars.itemconfirm = 0;
}
}
@ -13362,7 +13362,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
player->mo, player->mo->angle,
FixedMul((50*player->mo->scale), K_GetKartGameSpeedScalar(gamespeed))
);
player->wavedashboost += TICRATE;
player->wavedashpower = FRACUNIT;
player->fakeBoost = TICRATE/3;
@ -13454,9 +13454,9 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
}
else if (ATTACK_IS_DOWN && HOLDING_ITEM && (player->itemflags & IF_ITEMOUT)) // Sink thrown
{
player->itemamount--;
K_ThrowKartItem(player, false, MT_SINK, 1, 2, 0);
K_PlayAttackTaunt(player->mo);
player->itemamount--;
player->itemflags &= ~IF_ITEMOUT;
K_UpdateHnextList(player, true);
player->botvars.itemconfirm = 0;
@ -13465,11 +13465,11 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
case KITEM_GACHABOM:
if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO)
{
player->itemamount--;
K_SetItemOut(player); // need this to set itemscale
K_ThrowKartItem(player, true, MT_GACHABOM, 0, 0, 0);
K_UnsetItemOut(player);
K_PlayAttackTaunt(player->mo);
player->itemamount--;
player->roundconditions.gachabom_miser = (
(player->roundconditions.gachabom_miser == 0)
? 1 : 0xFF
@ -13596,7 +13596,13 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
// We'll never need to go above that.
if (player->tricktime <= TRICKDELAY)
{
// 2.3 - Prevent accidental fastfalls during trickdelay
if (!G_CompatLevel(0x000C))
player->pflags |= PF_NOFASTFALL;
player->tricktime++;
}
// debug shit
//CONS_Printf("%d\n", player->mo->momz / mapobjectscale);
@ -13628,14 +13634,20 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
// INT16 aimingcompare = abs(cmd->throwdir) - abs(cmd->turning);
boolean cantrick = true;
UINT16 buttons = player->cmd.buttons;
// 2.2 - Pre-steering trickpanels
if (!G_CompatLevel(0x000A) && !K_PlayerUsesBotMovement(player))
{
if (!(player->cmd.buttons & BT_ACCELERATE))
if (!(buttons & BT_ACCELERATE))
{
cantrick = false;
}
// 2.3 - also allow tricking with the Spindash button
else if (!G_CompatLevel(0x000C) && ((buttons & BT_SPINDASHMASK) == BT_SPINDASHMASK))
{
player->pflags |= PF_NOFASTFALL;
}
}
// Uses cmd->turning over steering intentionally.
@ -13880,9 +13892,22 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
}
else
{
if ((player->pflags & PF_TRICKDELAY) && !(player->cmd.buttons & BT_ACCELERATE) && (player->tricktime >= TRICKDELAY))
if (G_CompatLevel(0x000C))
{
player->pflags &= ~PF_TRICKDELAY;
if ((player->pflags & PF_TRICKDELAY) && !(player->cmd.buttons & BT_ACCELERATE) && (player->tricktime >= TRICKDELAY))
{
player->pflags &= ~PF_TRICKDELAY;
}
}
else
// 2.3 - Spindash to trick
{
// Ignore pre-existing Accel inputs if not pressing Spindash. Always ignore pre-existing Spindash inputs to prevent accidental tricking.
if ((player->pflags & PF_TRICKDELAY) && (!(player->cmd.buttons & BT_ACCELERATE) || (((player->cmd.buttons & BT_SPINDASHMASK) == BT_SPINDASHMASK) && (player->oldcmd.buttons & BT_SPINDASHMASK) != BT_SPINDASHMASK)) && (player->tricktime >= TRICKDELAY))
{
player->pflags &= ~PF_TRICKDELAY;
player->pflags |= PF_NOFASTFALL;
}
}
}

View file

@ -88,6 +88,9 @@ static void M_AddFloatVar(consvar_t *cv, fixed_t step)
const CV_PossibleValue_t *values = cv->PossibleValue;
if (values == NULL) //cvar is unbounded and will not work! return is here only as a failsafe to prevent crashes
return;
for (i = 0; values[i].strvalue; ++i)
{
if (cv->value == values[i].value)
@ -220,7 +223,7 @@ static void M_ChangeCvar(INT32 choice)
"Turning on Auto Roulette",
"\"Ring Racers\" is not designed with random items in mind. With Auto Roulette, you cannot select the item results you want or select an item early."
"\n"
"You will be at a distinct \x85" "disadvantage. \x80\n"
"You will be at a distinct \x85" "disadvantage. \x80\n"
"\n"
"ARE YOU SURE?",
M_ChangeCvarResponse,

View file

@ -319,12 +319,24 @@ void PR_SaveProfiles(void)
std::vector<uint8_t> ubjson = json::to_ubjson(ng);
std::string realpath = fmt::format("{}/{}", srb2home, PROFILESFILE);
int random_number = rand();
std::string tmppath = fmt::format("{}_{}.tmp", realpath, random_number);
std::string bakpath = fmt::format("{}.bak", realpath);
if (fs::exists(realpath))
{
try
{
fs::rename(realpath, bakpath);
}
catch (const fs::filesystem_error& ex)
{
CONS_Alert(CONS_ERROR, "Failed to record profiles backup. Not attempting to save profiles. %s\n", ex.what());
return;
}
}
try
{
io::FileStream file {tmppath, io::FileStreamMode::kWrite};
io::FileStream file {realpath, io::FileStreamMode::kWrite};
io::write(static_cast<uint32_t>(0x52494E47), file, io::Endian::kBE); // "RING"
io::write(static_cast<uint32_t>(0x5052464C), file, io::Endian::kBE); // "PRFL"
@ -334,16 +346,14 @@ void PR_SaveProfiles(void)
io::write(static_cast<uint8_t>(0), file); // reserved4
io::write_exact(file, tcb::as_bytes(tcb::make_span(ubjson)));
file.close();
fs::rename(tmppath, realpath);
}
catch (const std::exception& ex)
{
I_Error("Couldn't save profiles. Are you out of Disk space / playing in a protected folder?\n\nException: %s", ex.what());
I_Error("Couldn't save profiles. Are you out of Disk space / playing in a protected folder? Check directory for a ringprofiles.prf.bak if the profiles file is corrupt.\n\nException: %s", ex.what());
}
catch (...)
{
I_Error("Couldn't save profiles. Are you out of Disk space / playing in a protected folder?");
I_Error("Couldn't save profiles. Are you out of Disk space / playing in a protected folder? Check directory for a ringprofiles.prf.bak if the profiles file is corrupt.");
}
}

View file

@ -138,7 +138,7 @@ INT16 K_PowerLevelPlacementScore(player_t *player)
INT16 K_CalculatePowerLevelAvg(void)
{
fixed_t avg = 0;
INT32 avg = 0;
UINT8 div = 0;
SINT8 t = PWRLV_DISABLED;
UINT8 i;
@ -166,7 +166,7 @@ INT16 K_CalculatePowerLevelAvg(void)
|| clientpowerlevels[i][t] == 0) // splitscreen player
continue;
avg += (clientpowerlevels[i][t] << FRACBITS);
avg += clientpowerlevels[i][t];
div++;
}
@ -178,7 +178,7 @@ INT16 K_CalculatePowerLevelAvg(void)
avg /= div;
return (INT16)(avg >> FRACBITS);
return (INT16)avg;
}
void K_UpdatePowerLevels(player_t *player, UINT8 lap, boolean forfeit)

View file

@ -157,7 +157,7 @@ void M_AddonsRefresh(void)
else if (majormods && !prevmajormods)
{
S_StartSound(NULL, sfx_s221);
message = va("%c%s\x80\nYou've loaded a gameplay-modifying addon.\n\nRecord Attack has been disabled, but you\ncan still play alone in local Multiplayer.\n\nIf you wish to play Record Attack mode, restart the game to disable loaded addons.\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), refreshdirname);
message = va("%c%s\x80\nYou've loaded a gameplay-modifying addon.\nCheck the console log for more info.\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), refreshdirname);
prevmajormods = majormods;
}

View file

@ -59,11 +59,12 @@ void Obj_InstaWhipThink (mobj_t *whip)
void Obj_SpawnInstaWhipRecharge(player_t *player, angle_t angleOffset)
{
mobj_t *x = P_SpawnMobjFromMobj(player->mo, 0, 0, 0, MT_INSTAWHIP_RECHARGE);
mobj_t *x = P_SpawnMobjFromMobj(player->mo, 0, 0, player->mo->height / 2, MT_INSTAWHIP_RECHARGE);
// This was previously used to delay the visual, back when this was VFX for a cooldown
// instead of VFX for a charge. We want to instantly bail out of that state now.
x->tics = 1;
x->eflags &= ~MFE_VERTICALFLIP; // Fix the visual being misaligned.
x->renderflags |= RF_SLOPESPLAT | RF_NOSPLATBILLBOARD;
P_SetTarget(&recharge_target(x), player->mo);
@ -81,7 +82,8 @@ void Obj_InstaWhipRechargeThink(mobj_t *x)
}
P_MoveOrigin(x, target->x, target->y, target->z + (target->height / 2));
P_InstaScale(x, 2 * target->scale);
if (x->scale != target->scale * 2)
P_InstaScale(x, target->scale * 2);
x->angle = target->angle + recharge_offset(x);
// Flickers every other frame
@ -91,6 +93,10 @@ void Obj_InstaWhipRechargeThink(mobj_t *x)
void Obj_SpawnInstaWhipReject(player_t *player)
{
mobj_t *x = P_SpawnMobjFromMobj(player->mo, 0, 0, 0, MT_INSTAWHIP_REJECT);
x->eflags &= ~MFE_VERTICALFLIP;
// Fixes an issue with gravflip misplacing the object for the first tic.
if (player->mo->eflags & MFE_VERTICALFLIP)
P_SetOrigin(x, player->mo->x, player->mo->y, player->mo->z);
P_SetTarget(&recharge_target(x), player->mo);
}

View file

@ -2220,7 +2220,7 @@ static INT16 P_FindClosestTurningForAngle(player_t *player, INT32 targetAngle, I
// Slightly frumpy binary search for the ideal turning input.
// We do this instead of reversing K_GetKartTurnValue so that future handling changes are automatically accounted for.
while (attempts++ < 20) // Practical calls of this function search maximum 10 times, this is solely for safety.
{
// These need to be treated as signed, or situations where boundaries straddle 0 are a mess.
@ -2341,7 +2341,7 @@ static void P_UpdatePlayerAngle(player_t *player)
// Corrections via fake turn go through easing.
// That means undoing them takes the same amount of time as doing them.
// This can lead to oscillating death spiral states on a multi-tic correction, as we swing past the target angle.
// So before we go into death-spirals, if our predicton is _almost_ right...
// So before we go into death-spirals, if our predicton is _almost_ right...
angle_t leniency_base;
if (G_CompatLevel(0x000A))
{
@ -2450,7 +2450,7 @@ void P_MovePlayer(player_t *player)
//////////////////////
P_UpdatePlayerAngle(player);
ticruned++;
if (!(cmd->flags & TICCMD_RECEIVED))
ticmiss++;
@ -4255,7 +4255,7 @@ void P_PlayerThink(player_t *player)
}
else if (cmd->buttons & BT_ACCELERATE)
{
if (!player->exiting && !(player->oldcmd.buttons & BT_ACCELERATE))
if (!player->exiting && !(player->oldcmd.buttons & BT_ACCELERATE) && ((cmd->buttons & BT_SPINDASHMASK) != BT_SPINDASHMASK) && player->trickpanel != TRICKSTATE_READY)
{
player->kickstartaccel = 0;
}
@ -4550,7 +4550,7 @@ void P_PlayerThink(player_t *player)
{
player->stairjank--;
}
// Random skin / "ironman"
{
UINT32 skinflags = (demo.playback)

View file

@ -428,7 +428,7 @@ static void I_ReportSignal(int num, int coredumped)
}
#ifndef NEWSIGNALHANDLER
FUNCNORETURN static ATTRNORETURN void signal_handler(INT32 num)
static ATTRNORETURN void signal_handler(INT32 num)
{
g_in_exiting_signal_handler = true;
@ -447,10 +447,8 @@ FUNCNORETURN static ATTRNORETURN void signal_handler(INT32 num)
write_backtrace(num);
#endif
I_ReportSignal(num, 0);
I_ShutdownSystem();
signal(num, SIG_DFL); //default signal action
raise(num);
I_Quit();
}
#endif
@ -1738,11 +1736,7 @@ void I_Error(const char *error, ...)
if (errorcount == 7)
SDL_Quit();
if (errorcount == 8)
{
M_SaveConfig(NULL);
G_DirtyGameData(); // done first in case an error is in G_SaveGameData
G_SaveGameData();
}
G_DirtyGameData();
if (errorcount > 20)
{
va_start(argptr, error);
@ -1776,9 +1770,10 @@ void I_Error(const char *error, ...)
I_OutputMsg("\nI_Error(): %s\n", buffer);
// ---
M_SaveConfig(NULL); // save game config, cvars..
G_DirtyGameData(); // done first in case an error is in G_SaveGameData
G_SaveGameData(); // Tails 12-08-2002
// FUCK OFF, stop allocating memory to write entire gamedata & configs
// when the program needs to shut down ASAP and we already save
// these all the time! Just set the dirty bit and GET OUT!
G_DirtyGameData();
// Shutdown. Here might be other errors.