diff --git a/src/d_player.h b/src/d_player.h index 16339b32a..a5219c816 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -740,7 +740,7 @@ struct player_t 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 + mobj_t *flybot; // One Flybot767 circling the player while stunned 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. diff --git a/src/k_kart.c b/src/k_kart.c index 5ac1ba0ad..4a05d3f82 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -9738,23 +9738,12 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) && 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)); + player->stunned = max(0, player->stunned - (player->spindash ? 3 : 1)); - // when timer reaches 0, reset the flag and stun combo counter - if ((player->stunned & 0x7FFF) == 0) + // if the flybots aren't spawned, spawn them now! + if (player->stunned != 0 && P_MobjWasRemoved(player->flybot)) { - 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); } } @@ -16313,4 +16302,70 @@ fixed_t K_TeamComebackMultiplier(player_t *player) return multiplier; } +void K_ApplyStun(player_t *player, mobj_t *inflictor, mobj_t *source, ATTRUNUSED INT32 damage, ATTRUNUSED UINT8 damagetype) +{ + #define BASE_STUN_TICS_MIN (4 * TICRATE) + #define BASE_STUN_TICS_MAX (10 * TICRATE) + #define MAX_STUN_REDUCTION (FRACUNIT/2) + #define STUN_REDUCTION_DISTANCE (20000) + INT32 stunTics = 0; + UINT8 numPlayers = 0; + UINT8 i; + + // calculate the number of players playing + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && !players[i].spectator) + { + numPlayers++; + } + } + + // calculate base stun tics + stunTics = Easing_Linear((player->kartweight - 1) * FRACUNIT / 8, BASE_STUN_TICS_MAX, BASE_STUN_TICS_MIN); + + // reduce stun in games with more than 8 players + if (numPlayers > 8) + { + stunTics -= 6 * (numPlayers - 8); + } + + // 1/3 stun values in battle + if (gametyperules & GTR_SPHERES) + { + stunTics /= 3; + } + + if (source && source->player) + { + // hits scored by players apply full stun + ; + } + else if (inflictor && (P_IsKartItem(inflictor->type) || P_IsKartFieldItem(inflictor->type))) + { + // items not thrown by a player apply half stun + stunTics /= 2; + } + else + { + // all other hazards apply 1/4 stun + stunTics /= 4; + } + + UINT32 dist = K_GetItemRouletteDistance(player, D_NumPlayersInRace()); + if (dist > STUN_REDUCTION_DISTANCE) + dist = STUN_REDUCTION_DISTANCE; + + fixed_t distfactor = FixedDiv(dist, STUN_REDUCTION_DISTANCE); // 0-1 as you approach STUN_REDUCTION_DISTANCE + fixed_t stunfactor = Easing_Linear(distfactor, FRACUNIT, MAX_STUN_REDUCTION); + stunTics = FixedMul(stunTics*FRACUNIT, stunfactor)/FRACUNIT; + + player->stunned = max(stunTics, 0); + + #undef BASE_STUN_TICS_MIN + #undef BASE_STUN_TICS_MAX + #undef MAX_STUN_REDUCTION + #undef STUN_REDUCTION_DISTANCE +} + //} diff --git a/src/k_kart.h b/src/k_kart.h index 9d9f3fdf8..a31e886d3 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -340,6 +340,8 @@ boolean K_TryPickMeUp(mobj_t *m1, mobj_t *m2, boolean allowHostile); fixed_t K_TeamComebackMultiplier(player_t *player); +void K_ApplyStun(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype); + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/k_objects.h b/src/k_objects.h index f5c36da12..060e43144 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -440,6 +440,7 @@ void Obj_DestroyedKartParticleLanding(mobj_t *part); void Obj_SpawnFlybotsForPlayer(player_t *player); void Obj_FlybotThink(mobj_t *flybot); void Obj_FlybotDeath(mobj_t *flybot); +void Obj_FlybotRemoved(mobj_t *flybot); /* Pulley */ void Obj_PulleyThink(mobj_t *root); diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 1a7d199c0..6dcbc527f 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -262,8 +262,8 @@ static int player_get(lua_State *L) 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,"flybot")) + LUA_PushUserdata(L, plr->flybot, META_MOBJ); else if (fastcmp(field,"justdi")) lua_pushinteger(L, plr->justDI); else if (fastcmp(field,"flipdi")) @@ -896,8 +896,13 @@ static int player_set(lua_State *L) 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,"flybot")) + { + mobj_t *mo = NULL; + if (!lua_isnil(L, 3)) + mo = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ)); + P_SetTarget(&plr->flybot, mo); + } else if (fastcmp(field,"justdi")) plr->justDI = luaL_checkinteger(L, 3); else if (fastcmp(field,"flipdi")) diff --git a/src/objects/flybot767.c b/src/objects/flybot767.c index 790276387..32293aeec 100644 --- a/src/objects/flybot767.c +++ b/src/objects/flybot767.c @@ -43,6 +43,7 @@ void Obj_SpawnFlybotsForPlayer(player_t *player) { UINT8 i; mobj_t *mo = player->mo; + mobj_t *hprev = mo; fixed_t radius = mo->radius; for (i = 0; i < FLYBOT_QUANTITY; i++) @@ -61,6 +62,17 @@ void Obj_SpawnFlybotsForPlayer(player_t *player) flybot->movedir = flybot->old_angle = flybot->angle = angle + ANGLE_90; flybot->old_z = SetFlybotZ(flybot); flybot->renderflags |= (i * RF_DONTDRAW); + + if (hprev->player) + { + P_SetTarget(&player->flybot, flybot); + } + else + { + P_SetTarget(&hprev->hnext, flybot); + P_SetTarget(&flybot->hprev, hprev); + } + hprev = flybot; } } @@ -80,11 +92,18 @@ void Obj_FlybotThink(mobj_t *flybot) if (mo->player) { - if (((stunned = mo->player->stunned & 0x7FFF) == 0) || (mo->player->playerstate == PST_DEAD)) + if (((stunned = mo->player->stunned) == 0) || (mo->player->playerstate == PST_DEAD)) { P_KillMobj(flybot, NULL, NULL, 0); return; } + + // If player is spindashing, spin faster to hint that stun is going down faster + else if (mo->player->spindash) + { + speed *= 2; + flybot->movedir += FLYBOT_BOB_FREQUENCY*2; + } } flybot->frame = flybot->frame & ~FF_TRANSMASK; @@ -120,6 +139,11 @@ void Obj_FlybotDeath(mobj_t *flybot) if (!P_MobjWasRemoved(mo)) { + if (mo->player && (flybot == mo->player->flybot)) + { + P_SetTarget(&mo->player->flybot, NULL); + } + mom.x = mo->momx; mom.y = mo->momy; mom.z = mo->momz; @@ -140,3 +164,12 @@ void Obj_FlybotDeath(mobj_t *flybot) angle += ANGLE_90; } } + +void Obj_FlybotRemoved(mobj_t *flybot) +{ + mobj_t *mo = flybot->target; + if (!P_MobjWasRemoved(mo) && mo->player && (flybot == mo->player->flybot)) + { + P_SetTarget(&mo->player->flybot, NULL); + } +} diff --git a/src/p_inter.c b/src/p_inter.c index 04a05ce4c..ca9183cc8 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -3041,7 +3041,6 @@ 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. @@ -3488,26 +3487,11 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da } // Apply stun! - // Feel free to move these calculations higher up if different damage sources should apply variable stun in future - #define MIN_STUNTICS (4 * TICRATE) - #define MAX_STUNTICS (10 * 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) + if (type != DMG_STING) { - stunTics /= 3; + K_ApplyStun(player, inflictor, source, damage, damagetype); } - 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); } } diff --git a/src/p_mobj.c b/src/p_mobj.c index da3e08bc6..fe7b5f0fc 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -11855,6 +11855,11 @@ void P_RemoveMobj(mobj_t *mobj) Obj_UnlinkRocks(mobj); break; } + case MT_FLYBOT767: + { + Obj_FlybotRemoved(mobj); + break; + } default: { break; diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 98e544cd4..9908c3bba 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -72,8 +72,7 @@ static savebuffer_t *current_savebuffer; #define ARCHIVEBLOCK_WAYPOINTS 0x7F46498F #define ARCHIVEBLOCK_RNG 0x7FAAB5BD -// Note: This cannot be bigger -// than an UINT16 (for now) +// Note: This cannot have more than 32 entries typedef enum { AWAYVIEW = 0x0001, @@ -93,6 +92,7 @@ typedef enum BARRIER = 0x4000, BALLHOGRETICULE = 0x8000, STONESHOE = 0x10000, + FLYBOT = 0x20000, } player_saveflags; static inline void P_ArchivePlayer(savebuffer_t *save) @@ -368,6 +368,9 @@ static void P_NetArchivePlayers(savebuffer_t *save) if (players[i].stoneShoe) flags |= STONESHOE; + if (players[i].flybot) + flags |= FLYBOT; + WRITEUINT32(save->p, flags); if (flags & SKYBOXVIEW) @@ -418,6 +421,9 @@ static void P_NetArchivePlayers(savebuffer_t *save) if (flags & STONESHOE) WRITEUINT32(save->p, players[i].stoneShoe->mobjnum); + if (flags & FLYBOT) + WRITEUINT32(save->p, players[i].flybot->mobjnum); + WRITEUINT32(save->p, (UINT32)players[i].followitem); WRITEUINT32(save->p, players[i].charflags); @@ -464,7 +470,6 @@ static void P_NetArchivePlayers(savebuffer_t *save) 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); @@ -1073,6 +1078,9 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) if (flags & STONESHOE) players[i].stoneShoe = (mobj_t *)(size_t)READUINT32(save->p); + if (flags & FLYBOT) + players[i].flybot = (mobj_t *)(size_t)READUINT32(save->p); + players[i].followitem = (mobjtype_t)READUINT32(save->p); //SetPlayerSkinByNum(i, players[i].skin); @@ -1120,7 +1128,6 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) 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); @@ -6232,6 +6239,11 @@ static void P_RelinkPointers(void) if (!RelinkMobj(&players[i].stoneShoe)) CONS_Debug(DBG_GAMELOGIC, "stoneShoe not found on player %d\n", i); } + if (players[i].flybot) + { + if (!RelinkMobj(&players[i].flybot)) + CONS_Debug(DBG_GAMELOGIC, "flybot not found on player %d\n", i); + } } }