diff --git a/src/d_main.cpp b/src/d_main.cpp index 03f02ac2e..e0be0e5a0 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -1395,6 +1395,33 @@ D_ConvertVersionNumbers (void) #endif } +const char *D_GetFancyBranchName(void) +{ + if (!strcmp(compbranch, "")) + { + // \x8b = aqua highlight + return "\x8b" "detached HEAD" "\x80"; + } + + return compbranch; +} + +static void Command_assert(void) +{ +#if !defined(NDEBUG) || defined(PARANOIA) + CONS_Printf("Yes, assertions are enabled.\n"); +#else + CONS_Printf("No, ssertions are NOT enabled.\n"); +#endif +} + +#ifdef DEVELOP +static void Command_crash(void) +{ + I_Error("The game crashed on PURPOSE, because of the 'crash' command. (This is only enabled in DEVELOP builds.)"); +} +#endif + // // D_SRB2Main // @@ -1560,6 +1587,11 @@ void D_SRB2Main(void) // Do this up here so that WADs loaded through the command line can use ExecCfg COM_Init(); + COM_AddCommand("assert", Command_assert); +#ifdef DEVELOP + COM_AddCommand("crash", Command_crash); +#endif + // add any files specified on the command line with -file wadfile // to the wad list if (!((M_GetUrlProtocolArg() || M_CheckParm("-connect")) && !M_CheckParm("-server"))) diff --git a/src/d_main.h b/src/d_main.h index 98f06d869..f9f543d85 100644 --- a/src/d_main.h +++ b/src/d_main.h @@ -59,6 +59,8 @@ extern boolean usehome; //Alam: which path? extern const char *pandf; //Alam: how to path? extern char srb2path[256]; //Alam: SRB2's Home +const char *D_GetFancyBranchName(void); + // the infinite loop of D_SRB2Loop() called from win_main for windows version void D_SRB2Loop(void) FUNCNORETURN; diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 562c63839..039864f98 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -622,6 +622,14 @@ consvar_t cv_kartflame_fastfuel = CVAR_INIT ("kartflame_fastfuel", "Off", CV_NET // we want this to be default now apparently consvar_t cv_kartflame_offroadburn = CVAR_INIT ("kartflame_offroadburn", "On", CV_NETVAR, CV_OnOff, NULL); +// "Arrow Bullet": above a certain speed threshold, an aura in the shape of the Shrink arrow +// covers you, signaling protection from (most) items and players. +// Default threshold percentage is currently 166%. +consvar_t cv_kartaltshrink_arrowbullet = CVAR_INIT ("kart_altshrink_arrowbullet", "On", CV_NETVAR, CV_OnOff, NULL); + +static CV_PossibleValue_t altshrink_arrowbullet_threshold_cons_t[] = {{0, "MIN"}, {2 * FRACUNIT, "MAX"}, {0, NULL}}; +consvar_t cv_kartaltshrink_arrowbulletthres = CVAR_INIT ("kart_altshrink_arrowbullet_threshold", "1.66", CV_NETVAR|CV_FLOAT, altshrink_arrowbullet_threshold_cons_t, NULL); + static CV_PossibleValue_t kartairsquish_cons_t[] = {{1, "Squish"}, {2, "Flip-over"}, {0, "None"}, {0, NULL}}; consvar_t cv_kartairsquish = CVAR_INIT ("kartairsquish", "None", CV_NETVAR, kartairsquish_cons_t, NULL); @@ -5478,9 +5486,9 @@ static void Command_ListDoomednums_f(void) static void Command_Version_f(void) { #ifdef DEVELOP - CONS_Printf("SRB2Kart %s-%s (%s %s)\n", compbranch, comprevision, compdate, comptime); + CONS_Printf("SRB2Kart %s-%s (%s %s)\n", D_GetFancyBranchName(), comprevision, compdate, comptime); #else - CONS_Printf("SRB2Kart %s (%s %s %s %s) ", VERSIONSTRING, compdate, comptime, comprevision, compbranch); + CONS_Printf("SRB2Kart %s (%s %s %s %s) ", VERSIONSTRING, compdate, comptime, comprevision, D_GetFancyBranchName()); #endif // Base library diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 531d7b89a..3901b8a54 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -190,6 +190,8 @@ extern consvar_t cv_kartbubble_defense_damagerate; extern consvar_t cv_kartbubble_boost_allow; extern consvar_t cv_kartflame_fastfuel; extern consvar_t cv_kartflame_offroadburn; +extern consvar_t cv_kartaltshrink_arrowbullet; +extern consvar_t cv_kartaltshrink_arrowbulletthres; extern consvar_t cv_kartairsquish; diff --git a/src/g_demo.c b/src/g_demo.c index ae31ec6a3..5a98929d9 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -4436,7 +4436,10 @@ boolean G_CheckDemoStatus(void) if (demo.recording && (modeattacking || demo.savemode != DSM_NOTSAVING)) { - G_SaveDemo(); + if (demobuf.p) + { + G_SaveDemo(); + } return true; } diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 628d7f2ad..b239cd815 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -361,7 +361,7 @@ static FUINT HWR_CalcSlopeLight(FUINT lightnum, pslope_t *slope, const sector_t { INT16 finallight = lightnum; - if (slope != NULL && P_ApplyLightOffsetFine(lightnum, sector)) + if (slope != NULL && sector != NULL && P_ApplyLightOffsetFine(lightnum, sector)) { finallight += (fof ? -slope->hwLightOffset : slope->hwLightOffset); diff --git a/src/k_collide.c b/src/k_collide.c index 24f9a0eaf..d1e4c4e7f 100644 --- a/src/k_collide.c +++ b/src/k_collide.c @@ -794,6 +794,10 @@ boolean K_SMKIceBlockCollide(mobj_t *t1, mobj_t *t2) boolean K_PvPTouchDamage(mobj_t *t1, mobj_t *t2) { + // What the fuck is calling this with stale refs? Whatever, validation's cheap. + if (P_MobjWasRemoved(t1) || P_MobjWasRemoved(t2) || !t1->player || !t2->player) + return false; + const boolean flameT1 = ((t1->player->flamestore > 0) && (t1->player->flametimer > 0)); const boolean flameT2 = ((t2->player->flamestore > 0) && (t2->player->flametimer > 0)); const boolean hyudoroT1 = (t1->player->hyudorotimer > 0); @@ -852,6 +856,8 @@ boolean K_PvPTouchDamage(mobj_t *t1, mobj_t *t2) P_InstaThrust(t2, K_MomentumAngle(t2), K_Momentum2D(t2) / 3); K_PlayPainSound(t2, NULL); + + return true; } } if (P_IsObjectOnGround(t1) && P_IsObjectOnGround(t2)) @@ -895,6 +901,8 @@ boolean K_PvPTouchDamage(mobj_t *t1, mobj_t *t2) P_InstaThrust(t1, K_MomentumAngle(t1), K_Momentum2D(t1) / 3); K_PlayPainSound(t1, NULL); + + return true; } } else if (P_IsObjectOnGround(t1) && P_IsObjectOnGround(t2)) diff --git a/src/k_hud.c b/src/k_hud.c index 93129fada..41eddc1b6 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -2036,7 +2036,7 @@ static void K_drawBossHealthBar(void) UINT8 i = 0, barstatus = 1, randlen = 0, darken = 0; const INT32 startx = BASEVIDWIDTH - 23; INT32 starty = BASEVIDHEIGHT - 25; - INT32 rolrand = 0; + INT32 rolrand = 0, randtemp = 0; boolean randsign = false; if (bossinfo.barlen <= 1) @@ -2082,7 +2082,9 @@ static void K_drawBossHealthBar(void) barstatus = 2; } - randlen = M_RandomKey(bossinfo.visualbar-(bossinfo.visualdiv/(2*FRACUNIT)))+1; + randtemp = bossinfo.visualbar-(bossinfo.visualdiv/(2*FRACUNIT)); + if (randtemp > 0) + randlen = M_RandomKey(randtemp)+1; randsign = M_RandomChance(FRACUNIT/2); // Right wing. @@ -2097,7 +2099,9 @@ static void K_drawBossHealthBar(void) randlen--; if (!randlen) { - randlen = M_RandomKey(bossinfo.visualbar-(bossinfo.visualdiv/(2*FRACUNIT)))+1; + randtemp = bossinfo.visualbar-(bossinfo.visualdiv/(2*FRACUNIT)); + if (randtemp > 0) + randlen = M_RandomKey(randtemp)+1; if (barstatus > 1) { rolrand = M_RandomKey(barstatus)+1; @@ -2661,10 +2665,11 @@ static void K_drawKartStatsnLives(void) { INT32 offsetx = 0; INT32 offsety = 0; + boolean split = r_splitscreen == 1; - if (cv_lives_xoffset.value == 0 || cv_lives_yoffset.value == 0) + if ((cv_lives_xoffset.value == 0 && cv_lives_yoffset.value == 0) || split) { - if ((cv_newspeedometer.value == 0 || cv_newspeedometer.value == 2) && !K_RingsActive()) + if ((cv_newspeedometer.value == 0 || cv_newspeedometer.value == 2) && !K_RingsActive() && !split) { offsetx = 25; offsety = 15; diff --git a/src/k_kart.c b/src/k_kart.c index db34e585d..892c6e137 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -406,6 +406,8 @@ void K_RegisterKartStuff(void) CV_RegisterVar(&cv_kartbubble_boost_allow); CV_RegisterVar(&cv_kartflame_fastfuel); CV_RegisterVar(&cv_kartflame_offroadburn); + CV_RegisterVar(&cv_kartaltshrink_arrowbullet); + CV_RegisterVar(&cv_kartaltshrink_arrowbulletthres); CV_RegisterVar(&cv_kartairsquish); @@ -830,6 +832,22 @@ boolean K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean sol return false; } + const boolean mobj1_intercepting = ((mobj1->player) && (K_InterceptArrowBullet(mobj1->player))); + const boolean mobj2_intercepting = ((mobj2->player) && (K_InterceptArrowBullet(mobj2->player))); + + // The other player is an Arrow Bullet, ignore them. + if (mobj1->player && K_AltShrinkArrowBulletCondition(mobj1->player) && (!mobj2_intercepting)) + { + mobj1->player->justbumped = bumptime; + return false; + } + + if (mobj2->player && K_AltShrinkArrowBulletCondition(mobj2->player) && (!mobj1_intercepting)) + { + mobj2->player->justbumped = bumptime; + return false; + } + mass1 = K_GetMobjWeight(mobj1, mobj2); if (solid == true && mass1 > 0) @@ -2176,7 +2194,8 @@ boolean K_TripwirePass(const player_t *player) boolean K_PlayerCanPunt(const player_t *player) { return player->invincibilitytimer > 0 || player->growshrinktimer > 0 || - (player->flamestore > 0 && K_GetShieldFromPlayer(player) == KSHIELD_FLAME); + (player->flamestore > 0 && K_GetShieldFromPlayer(player) == KSHIELD_FLAME) || + K_AltShrinkArrowBulletCondition(player); } boolean K_ItemMobjAllowedtoWaterRun(mobj_t *item) @@ -7536,7 +7555,15 @@ void K_KartResetPlayerColor(player_t *player) if (player->growshrinktimer) // Ditto, for grow/shrink { - if (player->growshrinktimer % 5 == 0) + if (K_AltShrinkArrowBulletCondition(player)) + { + // Arrow Bullet! + player->mo->colorized = true; + player->mo->color = SKINCOLOR_CREAMSICLE; + fullbright = true; + goto finalise; + } + else if (player->growshrinktimer % 5 == 0) { player->mo->colorized = true; player->mo->color = player->growshrinktimer < 0 ? SKINCOLOR_CREAMSICLE : SKINCOLOR_PERIWINKLE; @@ -8911,6 +8938,14 @@ static boolean K_AltInvinSliptideCondition(player_t *player) return (K_InvincibilityGradient(player->invincibilitytimer) > (FRACUNIT/2)); } +fixed_t K_GetSpeedPercentage(player_t *player) +{ + if (!player) + return 0; // NULL player means there's no speeed to gather. + + return (FixedDiv(player->speed, K_GetKartSpeed(player, false, false))); +} + // Sliptide conditions for Alt. Shrink static boolean K_AltShrinkSliptideCondition(player_t *player) { @@ -8926,6 +8961,28 @@ static boolean K_AltShrinkSliptideCondition(player_t *player) return (speedPercent > (3 * FRACUNIT / 4)); } +// Conditions for putting the player in "arrow bullet" mode. +boolean K_AltShrinkArrowBulletCondition(player_t *player) +{ + if (!cv_kartaltshrink_arrowbullet.value) // Not toggled on; we can't do anything + return false; + + if (!player) + return false; // NULL player means there's no way we can BE an arrow bullet. + + // Only if you're at or above the threshold percentage! + return (K_GetSpeedPercentage(player) >= cv_kartaltshrink_arrowbulletthres.value); +} + +// This player intercepts an Arrow Bullet and causes damage. +boolean K_InterceptArrowBullet(player_t *player) +{ + if (!player) + return false; + + return ((player->invincibilitytimer && !K_IsKartItemAlternate(KITEM_INVINCIBILITY)) || (player->growshrinktimer > 0) || (player->flamestore)); +} + static void K_HandleAirDriftDrag(player_t *player, boolean onground) { if (onground && player->airdriftspeed > 0) diff --git a/src/k_kart.h b/src/k_kart.h index 7cc5931d2..1892b0342 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -264,6 +264,9 @@ fixed_t K_InvincibilityGradient(UINT16 time); UINT16 K_GetInvincibilityTime(player_t *player); fixed_t K_GetInvincibilitySpeed(UINT16 time); fixed_t K_GetInvincibilityAccel(UINT16 time); +fixed_t K_GetSpeedPercentage(player_t *player); +boolean K_AltShrinkArrowBulletCondition(player_t *player); +boolean K_InterceptArrowBullet(player_t *player); void K_ResetPogoSpring(player_t *player); void K_DoInvincibility(player_t *player, tic_t time); void K_KillBananaChain(mobj_t *banana, mobj_t *inflictor, mobj_t *source); diff --git a/src/m_menu.c b/src/m_menu.c index 8d92d884f..3ac367d88 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -2848,6 +2848,7 @@ texty -= 10*vid.dupy;\ #if defined(DEVELOP) addtext(V_ALLOWLOWERCASE|V_GREENMAP|V_TRANSLUCENT, comprevision); addtext(V_ALLOWLOWERCASE|V_YELLOWMAP|V_TRANSLUCENT, compbranch); + addtext(0, D_GetFancyBranchName()); V_DrawThinString(0, 0, V_ALLOWLOWERCASE|V_ORANGEMAP|V_TRANSLUCENT|V_SNAPTOTOP, va("%s", complast)); #else // Regular build addtext(V_ALLOWLOWERCASE|V_TRANSLUCENT, va("%s", VERSIONSTRING)); diff --git a/src/p_inter.c b/src/p_inter.c index 6badd614a..5b25a2d34 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -2125,7 +2125,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da if (damagetype != DMG_SPECTATOR && target->player && target->player->spectator) return false; - if (source && source->player && source->player->spectator) + if (!P_MobjWasRemoved(source) && source->player && source->player->spectator) return false; switch (target->type) @@ -2215,6 +2215,9 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da const boolean explosioncombo = type == DMG_EXPLODE // This damage type can do evil stuff like ALWAYS combo && player->bubbleboost == 0; // ...but popping a Bubble Shield protects you! + // An Arrow Bullet player ran into a player with a stronger power item. Damage them! + const boolean intercept = ((!P_MobjWasRemoved(inflictor)) && inflictor->player && K_InterceptArrowBullet(inflictor->player)); + // Check if the player is allowed to be damaged! // If not, then spawn the instashield effect instead. if (!force) @@ -2253,6 +2256,13 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da return false; } + // Alt. Shrink: Tank non-explosion hits, as long as you're in Arrow Bullet mode. + if ((explosioncombo == false) && (intercept == false) && (K_AltShrinkArrowBulletCondition(player))) + { + K_DoInstashield(player); + return false; + } + // Bubble Shield protects you from spinout, but not knockback if (K_GetShieldFromPlayer(player) == KSHIELD_BUBBLE && explosioncombo == false && source != NULL && type != DMG_VOLTAGE diff --git a/src/p_setup.c b/src/p_setup.c index dd8684ebd..bf2f42f65 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -8893,6 +8893,7 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) HWR_ClearAllTextures(); #endif + G_FreeGhosts(); // ghosts are allocated with PU_LEVEL Patch_FreeTag(PU_PATCH_LOWPRIORITY); Patch_FreeTag(PU_PATCH_ROTATED); Z_FreeTags(PU_LEVEL, PU_PURGELEVEL - 1); diff --git a/src/p_spec.c b/src/p_spec.c index fb761b741..9c55fe6b0 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -4417,7 +4417,7 @@ boolean P_ProcessSpecial(activator_t *activator, INT16 special, INT32 *args, cha if (waypointcap == NULL) { // No point in trying at all if no waypoints exist. - break; + return false; } TAG_ITER_SECTORS(args[0], secnum) diff --git a/src/p_user.c b/src/p_user.c index 379d22f34..ac3fe96ca 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -2315,6 +2315,11 @@ void P_MovePlayer(player_t *player) K_SpawnSparkleTrail(player->mo); } + if (K_AltShrinkArrowBulletCondition(player)) + { + K_SpawnSparkleTrail(player->mo); + } + if (player->wipeoutslow > 1 && (leveltime & 1)) K_SpawnWipeoutTrail(player->mo, false); diff --git a/src/sdl/i_system.cpp b/src/sdl/i_system.cpp index 5df6ddba5..fd33b0f89 100644 --- a/src/sdl/i_system.cpp +++ b/src/sdl/i_system.cpp @@ -2124,8 +2124,8 @@ FUNCIERROR void ATTRNORETURN I_Error(const char *error, ...) W_Shutdown(); -#if defined (PARANOIA) && defined (__CYGWIN__) - *(volatile INT32 *)0 = 4; //Alam: Debug! +#if defined (PARANOIA) || defined (DEVELOP) + *(INT32 *)0 = 4; //Alam: Debug! #endif exit(-1);