diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 43bb54538..11d603cae 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -617,7 +617,7 @@ static inline void resynch_write_player(resynch_pak *rsp, const size_t i) rsp->airtime = (tic_t)LONG(players[i].airtime); - rsp->bumpers = SHORT(players[i].bumpers); + rsp->bumpers = players[i].bumpers; rsp->karmadelay = SHORT(players[i].karmadelay); rsp->eliminated = players[i].eliminated; @@ -765,7 +765,7 @@ static void resynch_read_player(resynch_pak *rsp) players[i].airtime = (tic_t)LONG(rsp->airtime); - players[i].bumpers = SHORT(rsp->bumpers); + players[i].bumpers = rsp->bumpers; players[i].karmadelay = SHORT(rsp->karmadelay); players[i].eliminated = rsp->eliminated; diff --git a/src/d_clisrv.h b/src/d_clisrv.h index c5f69d9df..ab08f0bdb 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -281,7 +281,7 @@ typedef struct // SRB2kart INT32 kartstuff[NUMKARTSTUFF]; tic_t airtime; - INT16 bumpers; + UINT8 bumpers; INT16 karmadelay; boolean eliminated; diff --git a/src/d_player.h b/src/d_player.h index a9330ffc8..f9159e8b7 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -524,7 +524,7 @@ typedef struct player_s waypoint_t *nextwaypoint; respawnvars_t respawn; // Respawn info tic_t airtime; // Keep track of how long you've been in the air - INT16 bumpers; + UINT8 bumpers; INT16 karmadelay; boolean eliminated; diff --git a/src/dehacked.c b/src/dehacked.c index 49b34e8bc..ae9ab561a 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -11313,6 +11313,7 @@ struct { {"DMG_EXPLODE",DMG_EXPLODE}, {"DMG_SQUISH",DMG_SQUISH}, {"DMG_STING",DMG_STING}, + {"DMG_KARMA",DMG_KARMA}, //// Death types {"DMG_INSTAKILL",DMG_INSTAKILL}, {"DMG_DEATHPIT",DMG_DEATHPIT}, diff --git a/src/k_kart.c b/src/k_kart.c index ecdb32f5d..cf45256ac 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2461,93 +2461,51 @@ void K_DoInstashield(player_t *player) P_SetTarget(&layerb->target, player->mo); } -void K_BattleHitPlayer(player_t *player, player_t *victim, UINT8 points, boolean reducewanted) +void K_BattleAwardHit(player_t *player, player_t *victim, mobj_t *inflictor, UINT8 bumpersRemoved) { - if (reducewanted == false) - points = 1; // Force to 1 + UINT8 points = 1; + boolean trapItem = false; + + if (player == NULL || victim == NULL) + { + // Invalid player or victim + return; + } + + if (player == victim) + { + // You cannot give yourself points + return; + } + + if ((inflictor && !P_MobjWasRemoved(inflictor)) && (inflictor->type == MT_BANANA && inflictor->health > 1)) + { + trapItem = true; + } + + // Only apply score bonuses to non-bananas + if (trapItem == false) + { + if (K_IsPlayerWanted(victim)) + { + // +3 points for hitting a wanted player + points = 3; + } + else if (gametyperules & GTR_BUMPERS) + { + if ((victim->bumpers > 0) && (victim->bumpers <= bumpersRemoved)) + { + // +2 points for finishing off a player + points = 2; + } + } + } if (gametyperules & GTR_POINTLIMIT) { P_AddPlayerScore(player, points); K_SpawnBattlePoints(player, victim, points); } - - if ((gametyperules & GTR_WANTED) && (reducewanted == true)) - { - // Seems a little backwards, but the WANTED system is meant to prevent camping. - // If you don't want people to go after you, then be proactive! - player->kartstuff[k_wanted] -= wantedreduce; - victim->kartstuff[k_wanted] -= (wantedreduce/2); - } -} - -void K_RemoveBumper(player_t *player, mobj_t *inflictor, mobj_t *source, UINT8 amount, boolean force) -{ - UINT8 score = 1; - boolean trapitem = false; - - if (amount <= 0) - return; - - if (!(gametyperules & GTR_BUMPERS)) - return; - - if (force == false) - { - if (player->powers[pw_flashing] || P_PlayerInPain(player)) - return; - } - - if (inflictor && !P_MobjWasRemoved(inflictor)) - { - if (inflictor->type == MT_BANANA && inflictor->health <= 1) - { - trapitem = true; - } - } - - if (gametyperules & GTR_POINTLIMIT) - { - if (K_IsPlayerWanted(player)) - score = 3; - else if ((gametyperules & GTR_BUMPERS) && (player->bumpers <= amount)) - score = 2; - } - - if (source && source->player && player != source->player) - { - K_BattleHitPlayer(source->player, player, score, trapitem); - } - - if (player->bumpers > 0) - { - if (player->bumpers <= amount) - { - mobj_t *karmahitbox = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_KARMAHITBOX); // Player hitbox is too small!! - P_SetTarget(&karmahitbox->target, player->mo); - karmahitbox->destscale = player->mo->scale; - P_SetScale(karmahitbox, player->mo->scale); - CONS_Printf(M_GetText("%s lost all of their bumpers!\n"), player_names[player-players]); - } - - player->bumpers -= amount; - K_CalculateBattleWanted(); - } - - if (player->bumpers <= 0) - { - player->bumpers = 0; - player->karmadelay = comebacktime; - - if (player->kartstuff[k_comebackmode] == 2) - { - mobj_t *poof = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_EXPLODE); - S_StartSound(poof, mobjinfo[MT_KARMAHITBOX].seesound); - player->kartstuff[k_comebackmode] = 0; - } - } - - K_CheckBumpers(); } void K_SpinPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 type) @@ -2660,30 +2618,110 @@ void K_DebtStingPlayer(player_t *player, mobj_t *source) P_SetPlayerMobjState(player->mo, S_KART_SPINOUT); } -void K_StealBumper(player_t *player, player_t *victim, UINT8 amount) +void K_HandleBumperChanges(player_t *player, UINT8 prevBumpers) { - INT32 intendedamount = player->bumpers + amount; - INT32 newbumper; - angle_t newangle, diff; - fixed_t newx, newy; - mobj_t *newmo; - - if (amount <= 0) + if (!(gametyperules & GTR_BUMPERS)) + { + // Bumpers aren't being used return; + } + + // TODO: replace all console text print-outs with a real visual + + if (player->bumpers > 0 && prevBumpers == 0) + { + if (player->kartstuff[k_comebackmode] == 2) + { + mobj_t *poof = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_EXPLODE); + S_StartSound(poof, mobjinfo[MT_KARMAHITBOX].seesound); + } + + player->kartstuff[k_comebackmode] = 0; + + if (netgame) + { + CONS_Printf(M_GetText("%s is back in the game!\n"), player_names[player-players]); + } + } + else if (player->bumpers == 0 && prevBumpers > 0) + { + mobj_t *karmahitbox = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_KARMAHITBOX); + P_SetTarget(&karmahitbox->target, player->mo); + + karmahitbox->destscale = player->mo->destscale; + P_SetScale(karmahitbox, player->mo->scale); + + if (netgame) + { + CONS_Printf(M_GetText("%s lost all of their bumpers!\n"), player_names[player-players]); + } + } + + player->karmadelay = comebacktime; + K_CalculateBattleWanted(); + K_CheckBumpers(); +} + +void K_DestroyBumpers(player_t *player, UINT8 amount) +{ + UINT8 oldBumpers = player->bumpers; if (!(gametyperules & GTR_BUMPERS)) - return; - - if (netgame && player->bumpers <= 0) - CONS_Printf(M_GetText("%s is back in the game!\n"), player_names[player-players]); - - while (player->bumpers < intendedamount) { - newbumper = player->bumpers; + return; + } + + amount = min(amount, player->bumpers); + + if (amount == 0) + { + return; + } + + player->bumpers -= amount; + + // TODO: Store a bumperlist on the player mobj, + // that way we can do a bumper destruction animation + + K_HandleBumperChanges(player, oldBumpers); +} + +void K_TakeBumpersFromPlayer(player_t *player, player_t *victim, UINT8 amount) +{ + UINT8 oldPlayerBumpers = player->bumpers; + UINT8 oldVictimBumpers = victim->bumpers; + + UINT8 tookBumpers = 0; + + if (!(gametyperules & GTR_BUMPERS)) + { + return; + } + + amount = min(amount, victim->bumpers); + + if (amount == 0) + { + return; + } + + while ((tookBumpers < amount) && (victim->bumpers > 0)) + { + UINT8 newbumper = player->bumpers; + + angle_t newangle, diff; + fixed_t newx, newy; + + mobj_t *newmo; + if (newbumper <= 1) + { diff = 0; + } else + { diff = FixedAngle(360*FRACUNIT/newbumper); + } newangle = player->mo->angle; newx = player->mo->x + P_ReturnThrustX(player->mo, newangle + ANGLE_180, 64*FRACUNIT); @@ -2691,36 +2729,42 @@ void K_StealBumper(player_t *player, player_t *victim, UINT8 amount) newmo = P_SpawnMobj(newx, newy, player->mo->z, MT_BATTLEBUMPER); newmo->threshold = newbumper; + P_SetTarget(&newmo->tracer, victim->mo); P_SetTarget(&newmo->target, player->mo); + newmo->angle = (diff * (newbumper-1)); newmo->color = victim->skincolor; if (newbumper+1 < 2) + { P_SetMobjState(newmo, S_BATTLEBUMPER3); + } else if (newbumper+1 < 3) + { P_SetMobjState(newmo, S_BATTLEBUMPER2); + } else + { P_SetMobjState(newmo, S_BATTLEBUMPER1); + } player->bumpers++; + victim->bumpers--; + tookBumpers++; } + if (tookBumpers == 0) + { + // No change occured. + return; + } + + // Play steal sound S_StartSound(player->mo, sfx_3db06); - player->powers[pw_flashing] = K_GetKartFlashing(player); - player->karmadelay = comebacktime; - - /* - victim->powers[pw_flashing] = K_GetKartFlashing(victim); - victim->karmadelay = comebacktime; - */ - - victim->kartstuff[k_instashield] = 15; - if (cv_kartdebughuddrop.value && !modeattacking) - K_DropItems(victim); - else - K_DropHnextList(victim, false); + K_HandleBumperChanges(player, oldPlayerBumpers); + K_HandleBumperChanges(victim, oldVictimBumpers); } // source is the mobj that originally threw the bomb that exploded etc. diff --git a/src/k_kart.h b/src/k_kart.h index ce3300b52..cadb8b772 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -42,13 +42,14 @@ void K_KartPlayerAfterThink(player_t *player); angle_t K_MomentumAngle(mobj_t *mo); void K_SetHitLagForObjects(mobj_t *mo1, mobj_t *mo2, INT32 tics); void K_DoInstashield(player_t *player); -void K_BattleHitPlayer(player_t *player, player_t *victim, UINT8 points, boolean reducewanted); -void K_RemoveBumper(player_t *player, mobj_t *inflictor, mobj_t *source, UINT8 amount, boolean force); +void K_BattleAwardHit(player_t *player, player_t *victim, mobj_t *inflictor, UINT8 bumpersRemoved); void K_SpinPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 type); void K_SquishPlayer(player_t *player, mobj_t *inflictor, mobj_t *source); void K_ExplodePlayer(player_t *player, mobj_t *inflictor, mobj_t *source); void K_DebtStingPlayer(player_t *player, mobj_t *source); -void K_StealBumper(player_t *player, player_t *victim, UINT8 amount); +void K_HandleBumperChanges(player_t *player, UINT8 prevBumpers); +void K_DestroyBumpers(player_t *player, UINT8 amount); +void K_TakeBumpersFromPlayer(player_t *player, player_t *victim, UINT8 amount); void K_SpawnKartExplosion(fixed_t x, fixed_t y, fixed_t z, fixed_t radius, INT32 number, mobjtype_t type, angle_t rotangle, boolean spawncenter, boolean ghostit, mobj_t *source); void K_SpawnMineExplosion(mobj_t *source, UINT8 color); UINT16 K_DriftSparkColor(player_t *player, INT32 charge); diff --git a/src/lua_baselib.c b/src/lua_baselib.c index a9d09dd58..d689dc848 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -3432,7 +3432,7 @@ static int lib_kExplodePlayer(lua_State *L) return 0; } -static int lib_kStealBumper(lua_State *L) +static int lib_kTakeBumpersFromPlayer(lua_State *L) { player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); player_t *victim = *((player_t **)luaL_checkudata(L, 2, META_PLAYER)); @@ -3442,7 +3442,7 @@ static int lib_kStealBumper(lua_State *L) return LUA_ErrInvalid(L, "player_t"); if (!victim) return LUA_ErrInvalid(L, "player_t"); - K_StealBumper(player, victim, amount); + K_TakeBumpersFromPlayer(player, victim, amount); return 0; } @@ -3919,7 +3919,7 @@ static luaL_Reg lib[] = { {"K_SpinPlayer",lib_kSpinPlayer}, {"K_SquishPlayer",lib_kSquishPlayer}, {"K_ExplodePlayer",lib_kExplodePlayer}, - {"K_StealBumper",lib_kStealBumper}, + {"K_TakeBumpersFromPlayer",lib_kTakeBumpersFromPlayer}, {"K_SpawnKartExplosion",lib_kSpawnKartExplosion}, {"K_SpawnMineExplosion",lib_kSpawnMineExplosion}, {"K_SpawnBoostTrail",lib_kSpawnBoostTrail}, diff --git a/src/p_inter.c b/src/p_inter.c index 9b71eac0d..39dfe7404 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -305,7 +305,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) { mobj_t *boom; - if (P_DamageMobj(toucher, special, special->target, 1, DMG_EXPLODE) == false) + if (P_DamageMobj(toucher, special, special->target, 1, DMG_KARMA) == false) { return; } @@ -323,9 +323,6 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) S_StartSound(boom, special->info->attacksound); - K_StealBumper(special->target->player, player, max(1, player->bumpers-1)); // bumpers-1 to slowly remove bumpers from the economy - K_RemoveBumper(player, special->target, special->target, player->bumpers, true); - special->target->player->karthud[khud_yougotem] = 2*TICRATE; special->target->player->karmadelay = comebacktime; } @@ -1692,7 +1689,7 @@ static boolean P_KillPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, { case DMG_DEATHPIT: // Respawn kill types - K_RemoveBumper(player, NULL, NULL, 1, true); + K_DestroyBumpers(player, 1); K_DoIngameRespawn(player); return false; default: @@ -1733,7 +1730,7 @@ static boolean P_KillPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, P_SetTarget(&boom->target, player->mo); } - K_RemoveBumper(player, NULL, NULL, player->bumpers, true); + K_DestroyBumpers(player, player->bumpers); player->eliminated = true; } @@ -1923,31 +1920,54 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da } } - // We successfully hit 'em! + // We successfully damaged them! Give 'em some bumpers! if (type != DMG_STING) { - UINT8 bumpadd = 1; + UINT8 takeBumpers = 1; if (damagetype & DMG_STEAL) { - bumpadd = 2; + takeBumpers = 2; + + if (type == DMG_KARMA) + { + takeBumpers = player->bumpers; + } + } + else + { + if (type == DMG_KARMA) + { + // Take half of their bumpers for karma comeback damage + takeBumpers = max(1, player->bumpers / 2); + } } if (source && source != player->mo && source->player) { K_PlayHitEmSound(source); - K_StealBumper(source->player, player, bumpadd); + + K_BattleAwardHit(source->player, player, inflictor, takeBumpers); + K_TakeBumpersFromPlayer(source->player, player, takeBumpers); + + if (type == DMG_KARMA) + { + // Destroy any remainder bumpers from the player for karma comeback damage + K_DestroyBumpers(player, player->bumpers); + } if (damagetype & DMG_STEAL) { - // Give them ALL of your emeralds :) + // Give them ALL of your emeralds instantly :) source->player->powers[pw_emeralds] |= player->powers[pw_emeralds]; player->powers[pw_emeralds] = 0; K_CheckEmeralds(source->player); } } - - K_RemoveBumper(player, inflictor, source, bumpadd, false); + else + { + K_DestroyBumpers(player, takeBumpers); + } if (!(damagetype & DMG_STEAL)) { @@ -1968,6 +1988,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da ringburst = 0; break; case DMG_EXPLODE: + case DMG_KARMA: K_ExplodePlayer(player, inflictor, source); break; case DMG_WIPEOUT: @@ -1997,7 +2018,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da K_PlayPainSound(player->mo); - if ((type == DMG_EXPLODE) || (cv_kartdebughuddrop.value && !modeattacking)) + if ((type == DMG_EXPLODE || type == DMG_KARMA) || (cv_kartdebughuddrop.value && !modeattacking)) { K_DropItems(player); } diff --git a/src/p_local.h b/src/p_local.h index bdc28075a..b577f3cef 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -467,6 +467,7 @@ typedef struct BasicFF_s #define DMG_EXPLODE 0x02 #define DMG_SQUISH 0x03 #define DMG_STING 0x04 +#define DMG_KARMA 0x05 // Karma Bomb explosion -- works like DMG_EXPLODE, but steals half of their bumpers & deletes the rest //// Death types - cannot be combined with damage types #define DMG_INSTAKILL 0x80 #define DMG_DEATHPIT 0x81 diff --git a/src/p_mobj.c b/src/p_mobj.c index 42bbecb1e..090977559 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -5278,15 +5278,23 @@ static void P_MobjSceneryThink(mobj_t *mobj) } break; case MT_BATTLEBUMPER: - if (mobj->health > 0 && mobj->target && mobj->target->player - && mobj->target->health > 0 && !mobj->target->player->spectator) + if (mobj->health <= 0) { + // DO EXPLODE ANIMATION HERE + //CONS_Printf("bumper explosion\n"); + P_RemoveMobj(mobj); + return; + } + else if (mobj->target && mobj->target->player && mobj->target->health > 0 && !mobj->target->player->spectator) + { + // Following a player + fixed_t rad = 32*mobj->target->scale; fixed_t offz; angle_t ang, diff; if (!((mobj->target->player-players) & 1)) - ang = (FixedAngle(mobj->info->speed) * -1); + ang = -FixedAngle(mobj->info->speed); else ang = FixedAngle(mobj->info->speed); @@ -5341,21 +5349,20 @@ static void P_MobjSceneryThink(mobj_t *mobj) P_SetThingPosition(mobj); } - // Was this so hard? if (mobj->target->player->bumpers <= mobj->threshold) { + // Sliently remove P_RemoveMobj(mobj); return; } } - else if ((mobj->health > 0 - && (!mobj->target || !mobj->target->player || !mobj->target->player->mo || mobj->target->health <= 0 || mobj->target->player->spectator)) - || (mobj->health <= 0 && P_IsObjectOnGround(mobj)) - || P_CheckDeathPitCollide(mobj)) // When in death state + else { + // Sliently remove P_RemoveMobj(mobj); return; } + break; case MT_PLAYERARROW: if (mobj->target && mobj->target->health @@ -10167,20 +10174,25 @@ void P_SpawnPlayer(INT32 playernum) P_SetScale(mobj, mobj->destscale); P_FlashPal(p, 0, 0); // Resets - if ((gametyperules & GTR_BUMPERS)) // SRB2kart + if (gametyperules & GTR_BUMPERS) { mobj_t *overheadarrow = P_SpawnMobj(mobj->x, mobj->y, mobj->z + mobj->height + 16*FRACUNIT, MT_PLAYERARROW); P_SetTarget(&overheadarrow->target, mobj); overheadarrow->drawflags |= MFD_DONTDRAW; P_SetScale(overheadarrow, mobj->destscale); - if (p->spectator && pcount > 1) // HEY! No being cheap... - p->bumpers = 0; - else if (p->bumpers > 0 || leveltime < 1 - || (p->jointime <= 1 && pcount <= 1)) + if (p->spectator) { - if (leveltime < 1 || (p->jointime <= 1 && pcount <= 1)) // Start of the map? - p->bumpers = K_StartingBumperCount(); // Reset those bumpers! + // HEY! No being cheap... + p->bumpers = 0; + } + else if ((p->bumpers > 0) || (leveltime < starttime) || (pcount <= 1)) + { + if ((leveltime < starttime) || (pcount <= 1)) // Start of the map? + { + // Reset those bumpers! + p->bumpers = K_StartingBumperCount(); + } if (p->bumpers) { diff --git a/src/p_saveg.c b/src/p_saveg.c index 1b02f39a7..afc08ffdb 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -257,7 +257,7 @@ static void P_NetArchivePlayers(void) WRITEUINT32(save_p, K_GetWaypointHeapIndex(players[i].nextwaypoint)); WRITEUINT32(save_p, players[i].airtime); - WRITEINT16(save_p, players[i].bumpers); + WRITEUINT8(save_p, players[i].bumpers); WRITEINT16(save_p, players[i].karmadelay); WRITEUINT8(save_p, players[i].eliminated); @@ -447,7 +447,7 @@ static void P_NetUnArchivePlayers(void) players[i].nextwaypoint = (waypoint_t *)(size_t)READUINT32(save_p); players[i].airtime = READUINT32(save_p); - players[i].bumpers = READINT16(save_p); + players[i].bumpers = READUINT8(save_p); players[i].karmadelay = READINT16(save_p); players[i].eliminated = (boolean)READUINT8(save_p);