diff --git a/src/d_clisrv.c b/src/d_clisrv.c index bbf115124..867a3e006 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -545,6 +545,7 @@ static inline void resynch_write_player(resynch_pak *rsp, const size_t i) // Score is resynched in the rspfirm resync packet rsp->rings = SHORT(players[i].rings); + rsp->spheres = SHORT(players[i].spheres); rsp->lives = players[i].lives; rsp->lostlife = players[i].lostlife; rsp->continues = players[i].continues; @@ -689,6 +690,7 @@ static void resynch_read_player(resynch_pak *rsp) // Score is resynched in the rspfirm resync packet players[i].rings = SHORT(rsp->rings); + players[i].spheres = SHORT(rsp->spheres); players[i].lives = rsp->lives; players[i].lostlife = rsp->lostlife; players[i].continues = rsp->continues; diff --git a/src/d_clisrv.h b/src/d_clisrv.h index 2f1caa82e..573c98dc8 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -214,6 +214,7 @@ typedef struct // Score is resynched in the confirm resync packet INT16 rings; + INT16 spheres; SINT8 lives; boolean lostlife; SINT8 continues; diff --git a/src/d_player.h b/src/d_player.h index 283196e4d..36829fecc 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -497,6 +497,7 @@ typedef struct player_s // player's ring count INT16 rings; + INT16 spheres; // Power ups. invinc and invis are tic counters. UINT16 powers[NUMPOWERS]; diff --git a/src/doomstat.h b/src/doomstat.h index 7356a8c18..675318c22 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -461,30 +461,30 @@ enum GameTypeRules { // Race rules GTR_CIRCUIT = 1, // Enables the finish line, laps, and the waypoint system. - GTR_RINGS = 1<<1, // Rings will be spawned in this mode. (Don't get too cheeky, ring sting is still enabled :]) GTR_BOTS = 1<<2, // Allows bots in this gametype. Combine with BotTiccmd hooks to make bots support your gametype. // Battle gametype rules - GTR_BUMPERS = 1<<3, // Enables the bumper health system - GTR_WANTED = 1<<4, // Enables the wanted anti-camping system - GTR_KARMA = 1<<5, // Enables the Karma system if you're out of bumpers - GTR_ITEMARROWS = 1<<6, // Show item box arrows above players - GTR_CAPSULES = 1<<7, // Enables the wanted anti-camping system - GTR_BATTLESTARTS = 1<<8, // Use Battle Mode start positions. + GTR_SPHERES = 1<<3, // Replaces rings with blue spheres + GTR_BUMPERS = 1<<4, // Enables the bumper health system + GTR_WANTED = 1<<5, // Enables the wanted anti-camping system + GTR_KARMA = 1<<6, // Enables the Karma system if you're out of bumpers + GTR_ITEMARROWS = 1<<7, // Show item box arrows above players + GTR_CAPSULES = 1<<8, // Enables the wanted anti-camping system + GTR_BATTLESTARTS = 1<<9, // Use Battle Mode start positions. - GTR_POINTLIMIT = 1<<9, // Reaching point limit ends the round - GTR_TIMELIMIT = 1<<10, // Reaching time limit ends the round - GTR_OVERTIME = 1<<11, // Allow overtime behavior + GTR_POINTLIMIT = 1<<10, // Reaching point limit ends the round + GTR_TIMELIMIT = 1<<11, // Reaching time limit ends the round + GTR_OVERTIME = 1<<12, // Allow overtime behavior // Custom gametype rules - GTR_TEAMS = 1<<12, // Teams are forced on - GTR_NOTEAMS = 1<<13, // Teams are forced off - GTR_TEAMSTARTS = 1<<14, // Use team-based start positions + GTR_TEAMS = 1<<13, // Teams are forced on + GTR_NOTEAMS = 1<<14, // Teams are forced off + GTR_TEAMSTARTS = 1<<15, // Use team-based start positions // Grand Prix rules - GTR_CAMPAIGN = 1<<15, // Handles cup-based progression - GTR_LIVES = 1<<16, // Lives system, players are forced to spectate during Game Over. - GTR_SPECIALBOTS = 1<<17, // Bot difficulty gets stronger between rounds, and the rival system is enabled. + GTR_CAMPAIGN = 1<<16, // Handles cup-based progression + GTR_LIVES = 1<<17, // Lives system, players are forced to spectate during Game Over. + GTR_SPECIALBOTS = 1<<18, // Bot difficulty gets stronger between rounds, and the rival system is enabled. // free: to and including 1<<31 }; diff --git a/src/g_game.c b/src/g_game.c index 6757fb2af..7b91c0e85 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2056,6 +2056,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) UINT8 botdifficulty; INT16 rings; + INT16 spheres; angle_t playerangleturn; UINT8 botdiffincrease; @@ -2140,7 +2141,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) itemamount = 0; growshrinktimer = 0; bumper = ((gametyperules & GTR_BUMPERS) ? K_StartingBumperCount() : 0); - rings = ((gametyperules & GTR_RINGS) ? 5 : 0); + rings = ((gametyperules & GTR_SPHERES) ? 0 : 5); + spheres = 0; comebackpoints = 0; wanted = 0; } @@ -2168,6 +2170,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) bumper = players[player].kartstuff[k_bumper]; rings = players[player].rings; + spheres = players[player].spheres; comebackpoints = players[player].kartstuff[k_comebackpoints]; wanted = players[player].kartstuff[k_wanted]; } @@ -2216,6 +2219,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) p->bot = bot; p->botvars.difficulty = botdifficulty; p->rings = rings; + p->spheres = spheres; p->botvars.diffincrease = botdiffincrease; p->botvars.rival = botrival; p->xtralife = xtralife; @@ -2788,9 +2792,9 @@ const char *Gametype_ConstantNames[NUMGAMETYPES] = UINT32 gametypedefaultrules[NUMGAMETYPES] = { // Race - GTR_CIRCUIT|GTR_RINGS|GTR_BOTS, + GTR_CIRCUIT|GTR_BOTS, // Battle - GTR_BUMPERS|GTR_WANTED|GTR_KARMA|GTR_ITEMARROWS|GTR_CAPSULES|GTR_BATTLESTARTS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME + GTR_SPHERES|GTR_BUMPERS|GTR_WANTED|GTR_KARMA|GTR_ITEMARROWS|GTR_CAPSULES|GTR_BATTLESTARTS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_OVERTIME }; // diff --git a/src/info.c b/src/info.c index 6d5bac1ea..d73c13010 100644 --- a/src/info.c +++ b/src/info.c @@ -135,7 +135,7 @@ char sprnames[NUMSPRITES + 1][5] = "TOKE", // Special Stage Token "RFLG", // Red CTF Flag "BFLG", // Blue CTF Flag - //"SPHR", // Sphere + "BSPH", // Sphere "NCHP", // NiGHTS chip "NSTR", // NiGHTS star "EMBM", // Emblem @@ -1800,19 +1800,19 @@ state_t states[NUMSTATES] = {SPR_RING, 22, 1, {NULL}, 0, 0, S_FASTRING1}, // S_FASTRING12 // Blue Sphere for special stages - {SPR_SPHR, FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_BLUESPHERE - {SPR_SPHR, FF_FULLBRIGHT + {SPR_BSPH, FF_SEMIBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_BLUESPHERE + {SPR_BSPH, FF_FULLBRIGHT #ifdef MANIASPHERES |FF_ANIMATE|FF_RANDOMANIM #endif , -1, {NULL}, 1, 4, S_NULL}, // S_BLUESPHEREBONUS - {SPR_SPHR, 0, 20, {NULL}, 0, 0, S_NULL}, // S_BLUESPHERESPARK + {SPR_BSPH, 0, 20, {NULL}, 0, 0, S_NULL}, // S_BLUESPHERESPARK // Bomb Sphere - {SPR_SPHR, FF_FULLBRIGHT|3, 2, {NULL}, 0, 0, S_BOMBSPHERE2}, // S_BOMBSPHERE1 - {SPR_SPHR, FF_FULLBRIGHT|4, 1, {NULL}, 0, 0, S_BOMBSPHERE3}, // S_BOMBSPHERE2 - {SPR_SPHR, FF_FULLBRIGHT|5, 2, {NULL}, 0, 0, S_BOMBSPHERE4}, // S_BOMBSPHERE3 - {SPR_SPHR, FF_FULLBRIGHT|4, 1, {NULL}, 0, 0, S_BOMBSPHERE1}, // S_BOMBSPHERE4 + {SPR_BSPH, FF_FULLBRIGHT|3, 2, {NULL}, 0, 0, S_BOMBSPHERE2}, // S_BOMBSPHERE1 + {SPR_BSPH, FF_FULLBRIGHT|4, 1, {NULL}, 0, 0, S_BOMBSPHERE3}, // S_BOMBSPHERE2 + {SPR_BSPH, FF_FULLBRIGHT|5, 2, {NULL}, 0, 0, S_BOMBSPHERE4}, // S_BOMBSPHERE3 + {SPR_BSPH, FF_FULLBRIGHT|4, 1, {NULL}, 0, 0, S_BOMBSPHERE1}, // S_BOMBSPHERE4 // NiGHTS Chip {SPR_NCHP, FF_FULLBRIGHT|FF_ANIMATE, -1, {NULL}, 15, 2, S_NULL}, // S_NIGHTSCHIP @@ -7923,29 +7923,29 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = }, { // MT_BLUESPHERE - 1706, // doomednum + -1, // doomednum S_BLUESPHERE, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound - MT_FLINGBLUESPHERE, // reactiontime + MT_FLINGBLUESPHERE, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate - S_BLUESPHERESPARK, // deathstate + S_NULL, // deathstate S_NULL, // xdeathstate sfx_s3k65, // deathsound 38*FRACUNIT, // speed - 16*FRACUNIT, // radius - 24*FRACUNIT, // height + 48*FRACUNIT, // radius + 48*FRACUNIT, // height 0, // display offset 100, // mass 0, // damage sfx_None, // activesound - MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT, // flags + MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags S_BLUESPHEREBONUS // raisestate }, @@ -7962,7 +7962,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate - S_BLUESPHERESPARK, // deathstate + S_NULL, // deathstate S_NULL, // xdeathstate sfx_s3k65, // deathsound 38*FRACUNIT, // speed diff --git a/src/info.h b/src/info.h index e7d53cb5b..4e235c1ba 100644 --- a/src/info.h +++ b/src/info.h @@ -406,7 +406,7 @@ typedef enum sprite SPR_TOKE, // Special Stage Token SPR_RFLG, // Red CTF Flag SPR_BFLG, // Blue CTF Flag - //SPR_SPHR, // Sphere + SPR_BSPH, // Sphere SPR_NCHP, // NiGHTS chip SPR_NSTR, // NiGHTS star SPR_EMBM, // Emblem diff --git a/src/k_kart.c b/src/k_kart.c index 4352ce5c5..a5595b24f 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -897,7 +897,7 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) // SPECIAL CASE No. 4: // Being in ring debt occasionally forces Super Ring on you if you mashed - if ((gametyperules & GTR_RINGS) && mashed && player->rings < 0 && cv_superring.value) + if (!(gametyperules & GTR_SPHERES) && mashed && player->rings < 0 && cv_superring.value) { INT32 debtamount = min(20, abs(player->rings)); if (P_RandomChance((debtamount*FRACUNIT)/20)) @@ -2221,6 +2221,12 @@ fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower) finalspeed = K_GetKartSpeedFromStat(kartspeed); + if (player->spheres > 0) + { + fixed_t sphereAdd = (FRACUNIT/80); // 50% at max + finalspeed = FixedMul(finalspeed, FRACUNIT + (sphereAdd * player->spheres)); + } + if (K_PlayerUsesBotMovement(player)) { // Give top speed a buff for bots, since it's a fairly weak stat without drifting @@ -2261,6 +2267,11 @@ fixed_t K_GetKartAccel(player_t *player) //k_accel += 3 * (9 - kartspeed); // 36 - 60 k_accel += 4 * (9 - kartspeed); // 32 - 64 + if (player->spheres > 0) + { + fixed_t sphereAdd = (FRACUNIT/10); // 500% at max + k_accel = FixedMul(k_accel, FRACUNIT + (sphereAdd * player->spheres)); + } if (K_PlayerUsesBotMovement(player)) { @@ -5614,6 +5625,14 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) else if (player->rings < -20) player->rings = -20; + if ((leveltime % TICRATE) == 0) + player->spheres--; + + if (player->spheres > 40) + player->spheres = 40; + else if (player->spheres < 0) + player->spheres = 0; + if (player->kartstuff[k_ringdelay]) player->kartstuff[k_ringdelay]--; diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 5b6251e80..119fab345 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -209,6 +209,8 @@ static int player_get(lua_State *L) lua_pushangle(L, plr->drawangle); else if (fastcmp(field,"rings")) lua_pushinteger(L, plr->rings); + else if (fastcmp(field,"spheres")) + lua_pushinteger(L, plr->spheres); else if (fastcmp(field,"powers")) LUA_PushUserdata(L, plr->powers, META_POWERS); else if (fastcmp(field,"kartstuff")) @@ -476,6 +478,8 @@ static int player_set(lua_State *L) plr->drawangle = luaL_checkangle(L, 3); else if (fastcmp(field,"rings")) plr->rings = (INT32)luaL_checkinteger(L, 3); + else if (fastcmp(field,"spheres")) + plr->spheres = (INT32)luaL_checkinteger(L, 3); else if (fastcmp(field,"powers")) return NOSET; else if (fastcmp(field,"pflags")) diff --git a/src/p_floor.c b/src/p_floor.c index f8f7fef2d..0ad27d580 100644 --- a/src/p_floor.c +++ b/src/p_floor.c @@ -2371,7 +2371,7 @@ void EV_MarioBlock(ffloor_t *rover, sector_t *sector, mobj_t *puncher) P_SetThingPosition(thing); if (thing->flags & MF_SHOOTABLE) P_DamageMobj(thing, puncher, puncher, 1, DMG_NORMAL); - else if (thing->type == MT_RING || thing->type == MT_COIN || thing->type == MT_RANDOMITEM) + else if (thing->type == MT_RING || thing->type == MT_RANDOMITEM) { thing->momz = FixedMul(3*FRACUNIT, thing->scale); P_TouchSpecialThing(thing, puncher, false); diff --git a/src/p_inter.c b/src/p_inter.c index e408c1d27..730e7388d 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -553,6 +553,18 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) return; + case MT_BLUESPHERE: + if (!(P_CanPickupItem(player, 0))) + return; + + // Reached the cap, don't waste 'em! + if (player->spheres >= 40) + return; + + special->momx = special->momy = special->momz = 0; + player->spheres++; + break; + // Secret emblem thingy case MT_EMBLEM: { @@ -2000,7 +2012,7 @@ void P_PlayerRingBurst(player_t *player, INT32 num_rings) fixed_t momxy = 5<shadowscale = FRACUNIT/2; break; case MT_DRIFTCLIP: @@ -9205,8 +9196,6 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) mobj->color = skincolor_blueteam; break; case MT_RING: - case MT_COIN: - case MT_NIGHTSSTAR: if (nummaprings >= 0) nummaprings++; break; @@ -9564,10 +9553,7 @@ void P_RemoveMobj(mobj_t *mobj) // Rings only, please! if (mobj->spawnpoint && (mobj->type == MT_RING - || mobj->type == MT_COIN - || mobj->type == MT_NIGHTSSTAR - || mobj->type == MT_REDTEAMRING - || mobj->type == MT_BLUETEAMRING) + || mobj->type == MT_BLUESPHERE) && !(mobj->flags2 & MF2_DONTRESPAWN)) { itemrespawnque[iquehead] = mobj->spawnpoint; @@ -9979,22 +9965,29 @@ void P_RespawnSpecials(void) pcount++; } - if (pcount == 1) // No respawn when alone - return; - else if (pcount > 1) + if (gametyperules & GTR_SPHERES) { - time = (120 - ((pcount-2) * 10))*TICRATE; - - // If the map is longer or shorter than 3 laps, then adjust ring respawn to account for this. - // 5 lap courses would have more retreaded ground, while 2 lap courses would have less. - if ((mapheaderinfo[gamemap-1]->numlaps != 3) - && !(mapheaderinfo[gamemap-1]->levelflags & LF_SECTIONRACE)) - time = (time * 3) / max(1, mapheaderinfo[gamemap-1]->numlaps); - - if (time < 10*TICRATE) + time = ((MAXPLAYERS+1) - pcount) * (2*TICRATE); + } + else + { + if (pcount == 1) // No respawn when alone + return; + else if (pcount > 1) { - // Ensure it doesn't go into absurdly low values - time = 10*TICRATE; + time = (120 - ((pcount-2) * 10))*TICRATE; + + // If the map is longer or shorter than 3 laps, then adjust ring respawn to account for this. + // 5 lap courses would have more retreaded ground, while 2 lap courses would have less. + if ((mapheaderinfo[gamemap-1]->numlaps != 3) + && !(mapheaderinfo[gamemap-1]->levelflags & LF_SECTIONRACE)) + time = (time * 3) / max(1, mapheaderinfo[gamemap-1]->numlaps); + + if (time < 10*TICRATE) + { + // Ensure it doesn't go into absurdly low values + time = 10*TICRATE; + } } } @@ -10410,6 +10403,7 @@ fixed_t P_GetMapThingSpawnHeight(const mobjtype_t mobjtype, const mapthing_t* mt case MT_SPIKEBALL: case MT_EMBLEM: case MT_RING: + case MT_BLUESPHERE: offset += mthing->options & MTF_AMBUSH ? 24*mapobjectscale : 0; break; @@ -10516,8 +10510,11 @@ static boolean P_AllowMobjSpawn(mapthing_t* mthing, mobjtype_t i) static mobjtype_t P_GetMobjtypeSubstitute(mapthing_t *mthing, mobjtype_t i) { - // Don't need this for Kart YET! (void)mthing; + + if ((gametyperules & GTR_SPHERES) && (i == MT_RING)) + return MT_BLUESPHERE; + return i; } diff --git a/src/p_saveg.c b/src/p_saveg.c index e2988e4d3..375c37120 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -109,6 +109,7 @@ static void P_NetArchivePlayers(void) WRITEANGLE(save_p, players[i].awayviewaiming); WRITEINT32(save_p, players[i].awayviewtics); WRITEINT16(save_p, players[i].rings); + WRITEINT16(save_p, players[i].spheres); for (j = 0; j < NUMPOWERS; j++) WRITEUINT16(save_p, players[i].powers[j]); @@ -302,6 +303,7 @@ static void P_NetUnArchivePlayers(void) players[i].awayviewaiming = READANGLE(save_p); players[i].awayviewtics = READINT32(save_p); players[i].rings = READINT16(save_p); + players[i].spheres = READINT16(save_p); for (j = 0; j < NUMPOWERS; j++) players[i].powers[j] = READUINT16(save_p);