diff --git a/src/d_player.h b/src/d_player.h index 9659340ed..c5da56cf5 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -696,6 +696,7 @@ struct player_t UINT8 gateSound; // Sound effect combo SINT8 aizdriftstrat; // (-1 to 1) - Let go of your drift while boosting? Helper for the SICK STRATZ (sliptiding!) you have just unlocked + SINT8 aizdriftextend; // Nonzero when you were sliptiding last tic, sign indicates direction. INT32 aizdrifttilt; INT32 aizdriftturn; @@ -953,6 +954,7 @@ struct player_t UINT16 infinitether; // Generic infinitether time, used for infinitether leniency. UINT8 finalfailsafe; // When you can't Ringshooter, force respawn as a last ditch effort! + UINT8 freeRingShooterCooldown; // Can't use a free Ring Shooter again too soon after respawning. UINT8 lastsafelap; UINT8 lastsafecheatcheck; diff --git a/src/g_demo.cpp b/src/g_demo.cpp index fea181a6a..56a0f83e9 100644 --- a/src/g_demo.cpp +++ b/src/g_demo.cpp @@ -110,7 +110,7 @@ demoghost *ghosts = NULL; // DEMO RECORDING // -#define DEMOVERSION 0x0008 +#define DEMOVERSION 0x0009 #define DEMOHEADER "\xF0" "KartReplay" "\x0F" #define DF_ATTACKMASK (ATTACKING_TIME|ATTACKING_LAP|ATTACKING_SPB) // This demo contains time/lap data @@ -2205,6 +2205,18 @@ void G_BeginRecording(void) WRITEUINT8(demobuf.p, grandprixinfo.eventmode); } + // Save netUnlocked from actual unlocks + // (netUnlocked is used in m_cond.c M_CheckNetUnlockByID) + WRITEUINT32(demobuf.p, MAXUNLOCKABLES); + for (size_t unlockindex = 0; unlockindex < MAXUNLOCKABLES; unlockindex++) + { + UINT8 unlock = gamedata->unlocked[unlockindex]; + WRITEUINT8(demobuf.p, unlock); + } + + // Save "mapmusrng" used for altmusic selection + WRITEUINT8(demobuf.p, mapmusrng); + // Now store some info for each in-game player // Lat' 12/05/19: Do note that for the first game you load, everything that gets saved here is total garbage; @@ -3163,6 +3175,21 @@ void G_DoPlayDemo(const char *defdemoname) grandprixinfo.eventmode = static_cast(READUINT8(demobuf.p)); } + // Load unlocks into netUnlocked + { + UINT32 unlockables = READUINT32(demobuf.p); + UINT32 unlocksread = std::min(unlockables, MAXUNLOCKABLES); + for (size_t i = 0; i < unlocksread; i++) + { + netUnlocked[i] = static_cast(READUINT8(demobuf.p)); + } + // skip remainder + demobuf.p += unlockables - unlocksread; + } + + // Load "mapmusrng" used for altmusic selection + mapmusrng = READUINT8(demobuf.p); + // Sigh ... it's an empty demo. if (*demobuf.p == DEMOMARKER) { @@ -3911,11 +3938,12 @@ boolean G_CheckDemoStatus(void) G_FinishExitLevel(); else { + UINT8 wasmodeattacking = modeattacking; G_StopDemo(); if (timedemo_quit) COM_ImmedExecute("quit"); - else if (modeattacking) + else if (wasmodeattacking) M_EndModeAttackRun(); else if (demo.attract == DEMO_ATTRACT_CREDITS) F_ContinueCredits(); diff --git a/src/k_kart.c b/src/k_kart.c index 341cd407a..26cf6ade9 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -8750,6 +8750,9 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) player->finalfailsafe = 0; } + if (player->freeRingShooterCooldown && !player->mo->hitlag) + player->freeRingShooterCooldown--; + if (player->superring) { player->nextringaward++; @@ -10538,20 +10541,21 @@ static void K_KartDrift(player_t *player, boolean onground) // No longer meet the conditions to sliptide? // We'll spot you the sliptide as long as you keep turning, but no charging wavedashes. - boolean keepsliptide = false; + boolean extendedSliptide = false; + // We don't meet sliptide conditions! if ((player->handleboost < (SLIPTIDEHANDLING/2)) || (!player->steering) || (!player->aizdriftstrat) || (player->steering > 0) != (player->aizdriftstrat > 0)) { - if (!player->drift && player->steering && player->aizdriftstrat && player->wavedash // If we were sliptiding last tic, - && (player->steering > 0) == (player->aizdriftstrat > 0) // we're steering in the right direction, + if (!player->drift && player->steering && player->aizdriftextend // If we were sliptiding last tic, + && (player->steering > 0) == (player->aizdriftextend > 0) // we're steering in the right direction, && player->speed >= K_GetKartSpeed(player, false, true)) // and we're above the threshold to spawn dust... { - keepsliptide = true; // Then keep your current sliptide, but note the behavior change for wavedash handling. + extendedSliptide = true; // Then keep your current sliptide, but note the behavior change for wavedash handling. } - else + else // Otherwise, update sliptide status as usual. { if (!player->drift) player->aizdriftstrat = 0; @@ -10563,16 +10567,22 @@ static void K_KartDrift(player_t *player, boolean onground) if (player->airtime > 2) // Arbitrary number. Small discontinuities due to Super Jank shouldn't thrash your handling properties. { player->aizdriftstrat = 0; - keepsliptide = false; + extendedSliptide = false; } + // If we're sliptiding, whether through an extension or otherwise, allow sliptide extensions next tic. + if (K_Sliptiding(player)) + player->aizdriftextend = player->aizdriftstrat; + else + player->aizdriftextend = 0; + if ((player->aizdriftstrat && !player->drift) - || (keepsliptide)) + || (extendedSliptide)) { K_SpawnAIZDust(player); - if (!keepsliptide) + if (!extendedSliptide) { // Give charge proportional to your angle. Sharp turns are rewarding, slow analog slides are not—remember, this is giving back the speed you gave up. UINT16 addCharge = FixedInt( @@ -10610,9 +10620,9 @@ static void K_KartDrift(player_t *player, boolean onground) player->aizdriftstrat = 0; */ - if (!K_Sliptiding(player) || keepsliptide) + if (!K_Sliptiding(player) || extendedSliptide) { - if (!keepsliptide && K_IsLosingWavedash(player) && player->wavedash > 0) + if (!extendedSliptide && K_IsLosingWavedash(player) && player->wavedash > 0) { if (player->wavedash > HIDEWAVEDASHCHARGE && !S_SoundPlaying(player->mo, sfx_waved2)) S_StartSoundAtVolume(player->mo, sfx_waved2, 255); // Losing combo time, going to boost diff --git a/src/k_respawn.c b/src/k_respawn.c index f72cb61e2..3cd82d1d0 100644 --- a/src/k_respawn.c +++ b/src/k_respawn.c @@ -885,6 +885,9 @@ static void K_HandleDropDash(player_t *player) player->respawn.state = RESPAWNST_NONE; player->mo->flags &= ~(MF_NOCLIPTHING); + + // Don't touch another Ring Shooter (still lets you summon a Ring Shooter yourself) + player->freeRingShooterCooldown = 2*TICRATE; } } diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index fbd93153a..d912e1fca 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -287,6 +287,8 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->gateSound); else if (fastcmp(field,"aizdriftstraft")) lua_pushinteger(L, plr->aizdriftstrat); + else if (fastcmp(field,"aizdriftextend")) + lua_pushinteger(L, plr->aizdriftextend); else if (fastcmp(field,"aizdrifttilt")) lua_pushinteger(L, plr->aizdrifttilt); else if (fastcmp(field,"aizdriftturn")) diff --git a/src/m_cond.c b/src/m_cond.c index a306e4844..345641454 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -3270,7 +3270,7 @@ boolean M_CheckNetUnlockByID(UINT16 unlockid) return true; // default permit } - if (netgame) + if (netgame || demo.playback) { return netUnlocked[unlockid]; } diff --git a/src/objects/ufo.c b/src/objects/ufo.c index be59b425e..2a2148088 100644 --- a/src/objects/ufo.c +++ b/src/objects/ufo.c @@ -951,6 +951,7 @@ boolean Obj_SpecialUFODamage(mobj_t *ufo, mobj_t *inflictor, mobj_t *source, UIN ACS_RunCatcherScript(source); S_StopSound(ufo); + S_StartSound(ufo, sfx_gbrk); S_StartSound(ufo, sfx_clawk2); P_StartQuake(20, 64 * ufo->scale, 0, NULL); @@ -960,6 +961,14 @@ boolean Obj_SpecialUFODamage(mobj_t *ufo, mobj_t *inflictor, mobj_t *source, UIN S_StartSound(ufo, sfx_clawht); S_StopSoundByID(ufo, sfx_clawzm); + + for (int i = 0; i <= maxhum; i++) + { + S_StopSoundByID(ufo, hums[i]); + } + + UFOUpdateSound(ufo); + P_StartQuake(10, 64 * ufo->scale, 0, NULL); return true; diff --git a/src/p_inter.c b/src/p_inter.c index f0f953715..11e32dbff 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -915,7 +915,10 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) return; case MT_RINGSHOOTER: - Obj_PlayerUsedRingShooter(special, player); + if (player->freeRingShooterCooldown) + player->pflags |= PF_CASTSHADOW; // you can't use this right now! + else + Obj_PlayerUsedRingShooter(special, player); return; case MT_SUPER_FLICKY: diff --git a/src/p_mobj.h b/src/p_mobj.h index 1e429aa26..1002bac46 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -447,6 +447,8 @@ struct mobj_t mobj_t *owner; + INT32 po_movecount; // Polyobject carrying (NOT savegame, NOT Lua) + // WARNING: New fields must be added separately to savegame and Lua. }; diff --git a/src/p_polyobj.c b/src/p_polyobj.c index a27b2d847..021b8520e 100644 --- a/src/p_polyobj.c +++ b/src/p_polyobj.c @@ -879,14 +879,10 @@ static void Polyobj_carryThings(polyobj_t *po, fixed_t dx, fixed_t dy) for (; mo; mo = mo->bnext) { - // lastlook is used by the SPB to determine targets, do not let it affect it - if (mo->type == MT_SPB) + if (mo->po_movecount == pomovecount) continue; - if (mo->lastlook == pomovecount) - continue; - - mo->lastlook = pomovecount; + mo->po_movecount = pomovecount; // Don't scroll objects that aren't affected by gravity if (mo->flags & MF_NOGRAVITY) @@ -1115,14 +1111,10 @@ static void Polyobj_rotateThings(polyobj_t *po, vector2_t origin, angle_t delta, for (; mo; mo = mo->bnext) { - // lastlook is used by the SPB to determine targets, do not let it affect it - if (mo->type == MT_SPB) + if (mo->po_movecount == pomovecount) continue; - if (mo->lastlook == pomovecount) - continue; - - mo->lastlook = pomovecount; + mo->po_movecount = pomovecount; // Don't scroll objects that aren't affected by gravity if (mo->flags & MF_NOGRAVITY) diff --git a/src/p_saveg.c b/src/p_saveg.c index 8a423f5ec..9c450c667 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -443,6 +443,7 @@ static void P_NetArchivePlayers(savebuffer_t *save) WRITEUINT8(save->p, players[i].gateSound); WRITESINT8(save->p, players[i].aizdriftstrat); + WRITESINT8(save->p, players[i].aizdriftextend); WRITEINT32(save->p, players[i].aizdrifttilt); WRITEINT32(save->p, players[i].aizdriftturn); @@ -1025,6 +1026,7 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) players[i].gateSound = READUINT8(save->p); players[i].aizdriftstrat = READSINT8(save->p); + players[i].aizdriftextend = READSINT8(save->p); players[i].aizdrifttilt = READINT32(save->p); players[i].aizdriftturn = READINT32(save->p); diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 8305d680b..8b195aa46 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -8177,20 +8177,33 @@ void P_ResetLevelMusic(void) { UINT8 idx = 0; - if (mapheaderinfo[gamemap-1]->musname_size > 1) + mapheader_t* mapheader = mapheaderinfo[gamemap - 1]; + + // To keep RNG in sync, we will always pull from RNG, even if unused + UINT32 random = P_Random(PR_MUSICSELECT); + + if (demo.playback) + { + // mapmusrng has already been set by the demo; just make sure it's valid + if (mapmusrng >= mapheader->musname_size) + { + mapmusrng = 0; + } + return; + } + + if (mapheader->musname_size > 1) { UINT8 tempmapmus[MAXMUSNAMES], tempmapmus_size = 1, i; tempmapmus[0] = 0; - for (i = 1; i < mapheaderinfo[gamemap-1]->musname_size; i++) + for (i = 1; i < mapheader->musname_size; i++) { - if (mapheaderinfo[gamemap-1]->cache_muslock[i-1] < MAXUNLOCKABLES - && !M_CheckNetUnlockByID(mapheaderinfo[gamemap-1]->cache_muslock[i-1])) + if (mapheader->cache_muslock[i-1] < MAXUNLOCKABLES + && !M_CheckNetUnlockByID(mapheader->cache_muslock[i-1])) continue; - //CONS_Printf("TEST - %u\n", i); - tempmapmus[tempmapmus_size++] = i; } @@ -8204,9 +8217,8 @@ void P_ResetLevelMusic(void) } else { - idx = P_RandomKey(PR_MUSICSELECT, tempmapmus_size); + idx = random % tempmapmus_size; } - //CONS_Printf("Rolled position %u, maps to %u\n", mapmusrng, tempmapmus[mapmusrng]); idx = tempmapmus[idx]; } } @@ -8216,7 +8228,13 @@ void P_ResetLevelMusic(void) void P_LoadLevelMusic(void) { - const char *music = mapheaderinfo[gamemap-1]->musname[mapmusrng]; + mapheader_t* mapheader = mapheaderinfo[gamemap-1]; + const char *music = mapheader->musname[0]; + + if (mapmusrng < mapheader->musname_size) + { + music = mapheader->musname[mapmusrng]; + } if (gametyperules & GTR_NOPOSITION || modeattacking != ATTACKING_NONE) {