From dfa0522326202440304d29d1726c06719a2b2114 Mon Sep 17 00:00:00 2001 From: TehRealSalt Date: Thu, 6 Dec 2018 13:46:38 -0500 Subject: [PATCH] Battle Mode overtime After the time limit is up, spawn a shrinking kill-field. This is one hefty initial commit! --- src/d_netcmd.c | 4 +- src/d_player.h | 1 + src/dehacked.c | 7 +++ src/doomstat.h | 11 ++++ src/g_game.c | 3 ++ src/info.c | 59 ++++++++++++++++++++- src/info.h | 8 +++ src/k_kart.c | 41 +++++++++++++- src/p_enemy.c | 4 +- src/p_inter.c | 99 ++++++++++++++++------------------ src/p_local.h | 1 + src/p_mobj.c | 141 ++++++++++++++++++++++++++++++++++++++++++++++++- src/p_saveg.c | 18 ++++++- src/p_setup.c | 4 ++ src/p_tick.c | 4 ++ 15 files changed, 343 insertions(+), 62 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index f29798382..956990bd2 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -4326,9 +4326,9 @@ void D_GameTypeChanged(INT32 lastgametype) case GT_TEAMMATCH: if (!cv_timelimit.changed && !cv_pointlimit.changed) // user hasn't changed limits { - // default settings for match: no timelimit, no pointlimit + // default settings for match: 3 mins, no pointlimit CV_SetValue(&cv_pointlimit, 0); - CV_SetValue(&cv_timelimit, 0); + CV_SetValue(&cv_timelimit, 3); } if (!cv_itemrespawntime.changed) CV_Set(&cv_itemrespawntime, cv_itemrespawntime.defaultvalue); // respawn normally diff --git a/src/d_player.h b/src/d_player.h index 1b1d4d0a2..ce5a92ab0 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -346,6 +346,7 @@ typedef enum k_comebackpoints, // Number of times you've bombed or gave an item to someone; once it's 3 it gets set back to 0 and you're given a bumper k_comebackmode, // 0 = bomb, 1 = item k_wanted, // Timer for determining WANTED status, lowers when hitting people, prevents the game turning into Camp Lazlo + k_killfield, // How long have you been in the kill field, stay in too long and lose a bumper k_yougotem, // "You Got Em" gfx when hitting someone as a karma player via a method that gets you back in the game instantly // v1.0.2 vars diff --git a/src/dehacked.c b/src/dehacked.c index 8cb704125..44c8163df 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -7099,6 +7099,9 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_KARMAFIREWORK4", "S_KARMAFIREWORKTRAIL", + "S_OVERTIMEFOG", + "S_OVERTIMEORB", + #ifdef SEENAMES "S_NAMECHECK", #endif @@ -7886,6 +7889,9 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_KARMAFIREWORK", + "MT_OVERTIMEFOG", + "MT_OVERTIMEORB", + #ifdef SEENAMES "MT_NAMECHECK", #endif @@ -8278,6 +8284,7 @@ static const char *const KARTSTUFF_LIST[] = { "COMEBACKPOINTS", "COMEBACKMODE", "WANTED", + "KILLFIELD", "YOUGOTEM", "ITEMBLINK", diff --git a/src/doomstat.h b/src/doomstat.h index 69e2e7cd9..605cef5bb 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -471,6 +471,17 @@ extern INT16 votelevels[5][2]; extern SINT8 votes[MAXPLAYERS]; extern SINT8 pickedvote; +/** Battle overtime information + */ +typedef struct +{ + boolean enabled; ///< Has this been initalized yet? + UINT16 radius; ///< Radius of kill field + fixed_t x, y, z; ///< Position to center on (z is only used for visuals) +} battleovertime_t; + +extern battleovertime_t *battleovertime; + extern tic_t hidetime; extern UINT32 timesBeaten; // # of times the game has been beaten. diff --git a/src/g_game.c b/src/g_game.c index ac8e27a37..75e7440b8 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -260,6 +260,9 @@ INT16 votelevels[5][2]; // Levels that were rolled by the host SINT8 votes[MAXPLAYERS]; // Each player's vote SINT8 pickedvote; // What vote the host rolls +// Battle overtime system +battleovertime_t *battleovertime = {NULL}; + // Server-sided, synched variables SINT8 battlewanted[4]; // WANTED players in battle, worth x2 points tic_t wantedcalcdelay; // Time before it recalculates WANTED diff --git a/src/info.c b/src/info.c index 9851ee23b..863d5f8b5 100644 --- a/src/info.c +++ b/src/info.c @@ -69,7 +69,7 @@ char sprnames[NUMSPRITES + 1][5] = "CNDL","DOCH","DUCK","GTRE","CHES","CHIM","DRGN","LZMN","PGSS","ZTCH", "MKMA","MKMP","RTCH","BOWL","BOWH","BRRL","BRRR","HRSE","TOAH","BFRT", "OFRT","RFRT","PFRT","ASPK","HBST","HBSO","HBSF","WBLZ","WBLN","FWRK", - "XMS4","XMS5","VIEW" + "OTFG","XMS4","XMS5","VIEW" }; // Doesn't work with g++, needs actionf_p1 (don't modify this comment) @@ -3394,6 +3394,9 @@ state_t states[NUMSTATES] = {SPR_FWRK, 3|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_KARMAFIREWORK1}, // S_KARMAFIREWORK4 {SPR_FWRK, 4|FF_FULLBRIGHT, TICRATE, {NULL}, 0, 0, S_NULL}, // S_KARMAFIREWORKTRAIL + {SPR_OTFG, FF_FULLBRIGHT|FF_TRANS50, 8, {NULL}, 0, 0, S_NULL}, // S_OVERTIMEFOG + {SPR_OTFG, 1|FF_FULLBRIGHT, 8, {NULL}, 0, 0, S_NULL}, // S_OVERTIMEORB + #ifdef SEENAMES {SPR_NULL, 0, 1, {NULL}, 0, 0, S_NULL}, // S_NAMECHECK #endif @@ -20052,6 +20055,60 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_OVERTIMEFOG + -1, // doomednum + S_OVERTIMEFOG, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 8, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 0, // speed + 8<mo->color = player->skincolor; } } + else if (player->kartstuff[k_killfield]) // You're gonna REALLY diiiiie + { + const INT32 flashtime = 4<<(4-(player->kartstuff[k_killfield]/TICRATE)); + if (player->kartstuff[k_killfield] == 1 || (player->kartstuff[k_killfield] % (flashtime/2) != 0)) + { + player->mo->colorized = false; + player->mo->color = player->skincolor; + } + else if (player->kartstuff[k_killfield] % flashtime == 0) + { + player->mo->colorized = true; + player->mo->color = SKINCOLOR_BYZANTIUM; + } + else + { + player->mo->colorized = true; + player->mo->color = SKINCOLOR_RUBY; + } + } else { player->mo->colorized = false; @@ -4370,8 +4389,28 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) if (player->kartstuff[k_tauntvoices]) player->kartstuff[k_tauntvoices]--; - if (G_BattleGametype() && player->kartstuff[k_bumper] > 0) + if (G_BattleGametype() && player->kartstuff[k_bumper] > 0 + && !player->kartstuff[k_spinouttimer] && !player->kartstuff[k_squishedtimer] + && !player->kartstuff[k_respawn] && !player->powers[pw_flashing]) + { player->kartstuff[k_wanted]++; + if (battleovertime->enabled) + { + if (P_AproxDistance(player->mo->x - battleovertime->x, player->mo->y - battleovertime->y) > (battleovertime->radius<kartstuff[k_killfield]++; + if (player->kartstuff[k_killfield] > 4*TICRATE) + { + K_SpinPlayer(player, NULL, 0, NULL, false); + //player->kartstuff[k_killfield] = 1; + } + } + else if (player->kartstuff[k_killfield] > 0) + player->kartstuff[k_killfield]--; + } + } + else if (player->kartstuff[k_killfield] > 0) + player->kartstuff[k_killfield]--; if (P_IsObjectOnGround(player->mo)) player->kartstuff[k_waterskip] = 0; diff --git a/src/p_enemy.c b/src/p_enemy.c index cc37c7747..a529c9b60 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -8234,7 +8234,7 @@ void A_ItemPop(mobj_t *actor) remains->flags = actor->flags; // Transfer flags remains->flags2 = actor->flags2; // Transfer flags2 remains->fuse = actor->fuse; // Transfer respawn timer - remains->threshold = (actor->threshold == 69 ? 69 : 68); + remains->threshold = (actor->threshold == 70 ? 70 : (actor->threshold == 69 ? 69 : 68)); remains->skin = NULL; remains->spawnpoint = actor->spawnpoint; @@ -8248,7 +8248,7 @@ void A_ItemPop(mobj_t *actor) remains->flags2 &= ~MF2_AMBUSH; - if (G_BattleGametype() && actor->threshold != 69) + if (G_BattleGametype() && (actor->threshold != 69 && actor->threshold != 70)) numgotboxes++; P_RemoveMobj(actor); diff --git a/src/p_inter.c b/src/p_inter.c index fce8ccd56..4ef2f578f 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1755,7 +1755,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) */ void P_CheckTimeLimit(void) { - INT32 i, k; + INT32 i; if (!cv_timelimit.value) return; @@ -1791,66 +1791,62 @@ void P_CheckTimeLimit(void) } //Optional tie-breaker for Match/CTF - else*/ if (cv_overtime.value) + else*/ +#define TESTOVERTIMEINFREEPLAY + if (cv_overtime.value) { - INT32 playerarray[MAXPLAYERS]; - INT32 tempplayer = 0; - INT32 spectators = 0; - INT32 playercount = 0; - - //Figure out if we have enough participating players to care. +#ifndef TESTOVERTIMEINFREEPLAY + boolean foundone = false; // Overtime is used for closing off down to a specific item. for (i = 0; i < MAXPLAYERS; i++) { - if (players[i].exiting) - return; - if (playeringame[i] && players[i].spectator) - spectators++; - } - - if ((D_NumPlayers() - spectators) > 1) - { - // Play the starpost sfx after the first second of overtime. - if (gamestate == GS_LEVEL && (leveltime == (timelimitintics + TICRATE))) - S_StartSound(NULL, sfx_strpst); - - // Normal Match - if (!G_GametypeHasTeams()) + if (!playeringame[i] || players[i].spectator) + continue; + if (foundone) { - //Store the nodes of participating players in an array. - for (i = 0; i < MAXPLAYERS; i++) +#endif + // Initiate the kill zone + if (!battleovertime->enabled) { - if (playeringame[i] && !players[i].spectator) - { - playerarray[playercount] = i; - playercount++; - } - } + UINT8 b = 0; + thinker_t *th; + mobj_t *item = NULL; - //Sort 'em. - for (i = 1; i < playercount; i++) - { - for (k = i; k < playercount; k++) + // Find us an item box to center on. + for (th = thinkercap.next; th != &thinkercap; th = th->next) { - if (players[playerarray[i-1]].marescore < players[playerarray[k]].marescore) - { - tempplayer = playerarray[i-1]; - playerarray[i-1] = playerarray[k]; - playerarray[k] = tempplayer; - } - } - } + mobj_t *thismo; + if (th->function.acp1 != (actionf_p1)P_MobjThinker) + continue; + thismo = (mobj_t *)th; - //End the round if the top players aren't tied. - if (players[playerarray[0]].marescore == players[playerarray[1]].marescore) - return; + if (thismo->type != MT_RANDOMITEM) + continue; + if (thismo->threshold == 69) // Disappears + continue; + b++; + if (item == NULL || (b < nummapboxes && P_RandomChance(((nummapboxes-b)*FRACUNIT)/nummapboxes))) // This is to throw off the RNG some + item = thismo; + if (b >= nummapboxes) // end early if we've found them all already + break; + } + + if (item == NULL) // no item found?! + return; + + item->threshold = 70; // Set constant respawn + battleovertime->x = item->x; + battleovertime->y = item->y; + battleovertime->z = item->z; + battleovertime->radius = 4096; + battleovertime->enabled = true; + } + return; +#ifndef TESTOVERTIMEINFREEPLAY } else - { - //In team match and CTF, determining a tie is much simpler. =P - if (redscore == bluescore) - return; - } + foundone = true; } +#endif } for (i = 0; i < MAXPLAYERS; i++) @@ -1861,9 +1857,6 @@ void P_CheckTimeLimit(void) return; P_DoPlayerExit(&players[i]); } - - /*if (server) - SendNetXCmd(XD_EXITLEVEL, NULL, 0);*/ } /** Checks if a player's score is over the pointlimit and the round should end. diff --git a/src/p_local.h b/src/p_local.h index ddcfd75e8..b6dcd4d0b 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -233,6 +233,7 @@ boolean P_SetMobjState(mobj_t *mobj, statenum_t state); //void P_RunShields(void); void P_RunOverlays(void); void P_RunShadows(void); +void P_RunBattleOvertime(void); void P_MobjThinker(mobj_t *mobj); boolean P_RailThinker(mobj_t *mobj); void P_PushableThinker(mobj_t *mobj); diff --git a/src/p_mobj.c b/src/p_mobj.c index 40b107dcf..595ff1137 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -6409,6 +6409,140 @@ static void P_RemoveShadow(mobj_t *thing) } } +// SAL'S KART BATTLE MODE OVERTIME HANDLER +#define MAXPLANESPERSECTOR (MAXFFLOORS+1)*2 +static void P_SpawnOvertimeParticles(fixed_t x, fixed_t y, mobjtype_t type, boolean ceiling) +{ + UINT8 i; + fixed_t flatz[MAXPLANESPERSECTOR]; + UINT8 numflats = 0; + mobj_t *mo; + subsector_t *ss = R_IsPointInSubsector(x, y); + sector_t *sec; + + if (!ss) + return; + sec = ss->sector; + + // convoluted stuff JUST to get all of the planes we need to draw orbs on :V + + if (sec->floorpic != skyflatnum) + { +#ifdef ESLOPE + flatz[numflats] = (sec->f_slope ? P_GetZAt(sec->f_slope, x, y) : sec->floorheight); +#else + flatz[numflats] = (sec->floorheight); +#endif + numflats++; + } + if (sec->ceilingpic != skyflatnum && ceiling) + { +#ifdef ESLOPE + flatz[numflats] = (sec->c_slope ? P_GetZAt(sec->c_slope, x, y) : sec->ceilingheight) - mobjinfo[MT_THOK].height; +#else + flatz[numflats] = (sec->ceilingheight) - mobjinfo[MT_THOK].height; +#endif + numflats++; + } + + if (sec->ffloors) + { + ffloor_t *rover; + for (rover = sec->ffloors; rover; rover = rover->next) + { + if (!(rover->flags & FF_EXISTS) || !(rover->flags & FF_BLOCKPLAYER)) + continue; + if (*rover->toppic != skyflatnum) + { +#ifdef ESLOPE + flatz[numflats] = (*rover->t_slope ? P_GetZAt(*rover->t_slope, x, y) : *rover->topheight); +#else + flatz[numflats] = (*rover->topheight); +#endif + numflats++; + } + if (*rover->bottompic != skyflatnum && ceiling) + { +#ifdef ESLOPE + flatz[numflats] = (*rover->b_slope ? P_GetZAt(*rover->b_slope, x, y) : *rover->bottomheight) - mobjinfo[MT_THOK].height; +#else + flatz[numflats] = (*rover->bottomheight) - mobjinfo[MT_THOK].height; +#endif + numflats++; + } + } + } + + if (numflats <= 0) // no flats + return; + + for (i = 0; i < numflats; i++) + { + mo = P_SpawnMobj(x, y, flatz[i], type); + + // Lastly, if this can see the skybox mobj, then... we just wasted our time :V + if (skyboxmo[0] && !P_MobjWasRemoved(skyboxmo[0]) && P_CheckSight(skyboxmo[0], mo)) + { + P_RemoveMobj(mo); + continue; + } + + switch(type) + { + case MT_OVERTIMEFOG: + P_SetScale(mo, 2*mo->scale); + mo->destscale = 8*mo->scale; + mo->momz = P_RandomRange(1,8)*mo->scale; + break; + case MT_OVERTIMEORB: + P_SetScale(mo, 2*mo->scale); + mo->destscale = mo->scale/4; + if ((leveltime/2) & 1) + mo->frame++; + break; + default: + break; + } + } +} +#undef MAXPLANESPERSECTOR + +void P_RunBattleOvertime(void) +{ + UINT8 i, j; + + if (battleovertime->radius > 512) + battleovertime->radius--; + else + battleovertime->radius = 512; + + if (leveltime & 1) + { + for (i = 0; i < 16; i++) // 16 base orbs + { + angle_t ang = FixedAngle(((45*i) * (FRACUNIT>>1)) + ((leveltime % 360)<x + P_ReturnThrustX(NULL, ang, battleovertime->radius<y + P_ReturnThrustY(NULL, ang, battleovertime->radius<x + ((P_RandomRange(-64,64) * 128)<y + ((P_RandomRange(-64,64) * 128)<x, y-battleovertime->y) <= (battleovertime->radius<flags2 & MF2_STRONGBOX) ? strongboxamt : weakboxamt); i; --i) s P_RemoveMobj(mobj); // make sure they disappear return; case MT_RANDOMITEM: - if (G_BattleGametype()) + if (G_BattleGametype() && (mobj->threshold != 70)) { if (mobj->threshold != 69) + { + mobj->fuse = cv_itemrespawntime.value*TICRATE + 2; break; + } } else { @@ -9390,6 +9527,8 @@ for (i = ((mobj->flags2 & MF2_STRONGBOX) ? strongboxamt : weakboxamt); i; --i) s // Transfer flags2 (strongbox, objectflip) newmobj->flags2 = mobj->flags2 & ~MF2_DONTDRAW; + if (mobj->threshold == 70) + newmobj->threshold = 70; } P_RemoveMobj(mobj); // make sure they disappear return; diff --git a/src/p_saveg.c b/src/p_saveg.c index 02f774574..8306e64ad 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -961,8 +961,8 @@ typedef enum MD2_EXTVAL2 = 1<<6, MD2_HNEXT = 1<<7, MD2_HPREV = 1<<8, - MD2_COLORIZED = 1<<9, - MD2_WAYPOINTCAP = 1<<10 + MD2_COLORIZED = 1<<9, + MD2_WAYPOINTCAP = 1<<10 #ifdef ESLOPE , MD2_SLOPE = 1<<11 #endif @@ -3298,6 +3298,13 @@ static void P_NetArchiveMisc(void) for (i = 0; i < 4; i++) WRITESINT8(save_p, battlewanted[i]); + // battleovertime_t + WRITEUINT8(save_p, battleovertime->enabled); + WRITEUINT16(save_p, battleovertime->radius); + WRITEFIXED(save_p, battleovertime->x); + WRITEFIXED(save_p, battleovertime->y); + WRITEFIXED(save_p, battleovertime->z); + WRITEUINT32(save_p, wantedcalcdelay); WRITEUINT32(save_p, indirectitemcooldown); WRITEUINT32(save_p, mapreset); @@ -3405,6 +3412,13 @@ static inline boolean P_NetUnArchiveMisc(void) for (i = 0; i < 4; i++) battlewanted[i] = READSINT8(save_p); + // battleovertime_t + battleovertime->enabled = (boolean)READUINT8(save_p); + battleovertime->radius = READUINT16(save_p); + battleovertime->x = READFIXED(save_p); + battleovertime->y = READFIXED(save_p); + battleovertime->z = READFIXED(save_p); + wantedcalcdelay = READUINT32(save_p); indirectitemcooldown = READUINT32(save_p); mapreset = READUINT32(save_p); diff --git a/src/p_setup.c b/src/p_setup.c index 3bdb4d057..01b231116 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -2373,6 +2373,10 @@ static void P_LevelInitStuff(void) for (i = 0; i < 4; i++) battlewanted[i] = -1; + + if (!battleovertime) + battleovertime = Z_Malloc(sizeof(battleovertime_t), PU_STATIC, NULL); + memset(battleovertime, 0, sizeof(battleovertime_t)); } // diff --git a/src/p_tick.c b/src/p_tick.c index b46b248bb..def074d60 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -621,6 +621,8 @@ void P_Ticker(boolean run) if (run) { P_RunThinkers(); + if (G_BattleGametype() && battleovertime->enabled) + P_RunBattleOvertime(); // Run any "after all the other thinkers" stuff for (i = 0; i < MAXPLAYERS; i++) @@ -760,6 +762,8 @@ void P_PreTicker(INT32 frames) } P_RunThinkers(); + if (G_BattleGametype() && battleovertime->enabled) + P_RunBattleOvertime(); // Run any "after all the other thinkers" stuff for (i = 0; i < MAXPLAYERS; i++)