diff --git a/src/Sourcefile b/src/Sourcefile index ea5f2d0d5..f2aa652b1 100644 --- a/src/Sourcefile +++ b/src/Sourcefile @@ -124,3 +124,4 @@ k_terrain.c k_director.c k_follower.c k_profiles.c +k_specialstage.c diff --git a/src/d_main.c b/src/d_main.c index 5d83c783b..e6bdd6160 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -75,6 +75,7 @@ #include "k_boss.h" #include "doomstat.h" #include "m_random.h" // P_ClearRandom +#include "k_specialstage.h" #ifdef CMAKECONFIG #include "config.h" @@ -982,6 +983,9 @@ void D_StartTitle(void) // Reset boss info K_ResetBossInfo(); + // Reset Special Stage + K_ResetSpecialStage(); + // empty maptol so mario/etc sounds don't play in sound test when they shouldn't maptol = 0; diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 435cc5b6f..1435eba76 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -62,6 +62,7 @@ #include "doomstat.h" #include "deh_tables.h" #include "m_perfstats.h" +#include "k_specialstage.h" #ifdef HAVE_DISCORDRPC #include "discord.h" @@ -2943,6 +2944,7 @@ static void Command_Map_f(void) if (newgametype == GT_BATTLE) { grandprixinfo.gp = false; + specialStage.active = false; K_ResetBossInfo(); if (mapheaderinfo[newmapnum-1] && @@ -2952,66 +2954,79 @@ static void Command_Map_f(void) bossinfo.encore = newencoremode; } } - else // default GP + else { - grandprixinfo.gamespeed = (cv_kartspeed.value == KARTSPEED_AUTO ? KARTSPEED_NORMAL : cv_kartspeed.value); - grandprixinfo.masterbots = false; - - if (option_skill) + if (mapheaderinfo[newmapnum-1] && + mapheaderinfo[newmapnum-1]->typeoflevel & TOL_SPECIAL) // Special Stage { - const char *masterstr = "Master"; - const char *skillname = COM_Argv(option_skill + 1); - INT32 newskill = -1; - INT32 j; + grandprixinfo.gp = false; + bossinfo.boss = false; - if (!strcasecmp(masterstr, skillname)) - { - newskill = KARTGP_MASTER; - } - else - { - for (j = 0; kartspeed_cons_t[j].strvalue; j++) - { - if (!strcasecmp(kartspeed_cons_t[j].strvalue, skillname)) - { - newskill = (INT16)kartspeed_cons_t[j].value; - break; - } - } + specialStage.active = true; + specialStage.encore = newencoremode; + } + else // default GP + { + grandprixinfo.gamespeed = (cv_kartspeed.value == KARTSPEED_AUTO ? KARTSPEED_NORMAL : cv_kartspeed.value); + grandprixinfo.masterbots = false; - if (!kartspeed_cons_t[j].strvalue) // reached end of the list with no match - { - INT32 num = atoi(COM_Argv(option_skill + 1)); // assume they gave us a skill number, which is okay too - if (num >= KARTSPEED_EASY && num <= KARTGP_MASTER) - newskill = (INT16)num; - } - } - - if (newskill != -1) + if (option_skill) { - if (newskill == KARTGP_MASTER) + const char *masterstr = "Master"; + const char *skillname = COM_Argv(option_skill + 1); + INT32 newskill = -1; + INT32 j; + + if (!strcasecmp(masterstr, skillname)) { - grandprixinfo.gamespeed = KARTSPEED_HARD; - grandprixinfo.masterbots = true; + newskill = KARTGP_MASTER; } else { - grandprixinfo.gamespeed = newskill; - grandprixinfo.masterbots = false; + for (j = 0; kartspeed_cons_t[j].strvalue; j++) + { + if (!strcasecmp(kartspeed_cons_t[j].strvalue, skillname)) + { + newskill = (INT16)kartspeed_cons_t[j].value; + break; + } + } + + if (!kartspeed_cons_t[j].strvalue) // reached end of the list with no match + { + INT32 num = atoi(COM_Argv(option_skill + 1)); // assume they gave us a skill number, which is okay too + if (num >= KARTSPEED_EASY && num <= KARTGP_MASTER) + newskill = (INT16)num; + } + } + + if (newskill != -1) + { + if (newskill == KARTGP_MASTER) + { + grandprixinfo.gamespeed = KARTSPEED_HARD; + grandprixinfo.masterbots = true; + } + else + { + grandprixinfo.gamespeed = newskill; + grandprixinfo.masterbots = false; + } } } + + grandprixinfo.encore = newencoremode; + + grandprixinfo.gp = true; + grandprixinfo.roundnum = 0; + grandprixinfo.cup = NULL; + grandprixinfo.wonround = false; + + bossinfo.boss = false; + specialStage.active = false; + + grandprixinfo.initalize = true; } - - grandprixinfo.encore = newencoremode; - - grandprixinfo.gp = true; - grandprixinfo.roundnum = 0; - grandprixinfo.cup = NULL; - grandprixinfo.wonround = false; - - bossinfo.boss = false; - - grandprixinfo.initalize = true; } } diff --git a/src/doomstat.h b/src/doomstat.h index dc6370ece..60b438fc3 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -518,6 +518,7 @@ enum TypeOfLevel TOL_RACE = 0x0001, ///< Race TOL_BATTLE = 0x0002, ///< Battle TOL_BOSS = 0x0004, ///< Boss (variant of battle, but forbidden) + TOL_SPECIAL = 0x0008, ///< Special Stage (variant of race, but forbidden) // Modifiers TOL_TV = 0x0100 ///< Midnight Channel specific: draw TV like overlay on HUD diff --git a/src/g_game.c b/src/g_game.c index 3dfac9246..b9c576324 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -3143,6 +3143,7 @@ tolinfo_t TYPEOFLEVEL[NUMTOLNAMES] = { {"RACE",TOL_RACE}, {"BATTLE",TOL_BATTLE}, {"BOSS",TOL_BOSS}, + {"SPECIAL",TOL_SPECIAL}, {"TV",TOL_TV}, {NULL, 0} }; diff --git a/src/k_kart.c b/src/k_kart.c index c7559d558..de8eb5a9b 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -40,6 +40,7 @@ #include "k_collide.h" #include "k_follower.h" #include "k_objects.h" +#include "k_specialstage.h" // SOME IMPORTANT VARIABLES DEFINED IN DOOMDEF.H: // gamespeed is cc (0 for easy, 1 for normal, 2 for hard) @@ -60,7 +61,11 @@ void K_TimerInit(void) UINT8 i; UINT8 numPlayers = 0;//, numspec = 0; - if (!bossinfo.boss) + if (specialStage.active == true) + { + K_InitSpecialStage(); + } + else if (bossinfo.boss == false) { for (i = 0; i < MAXPLAYERS; i++) { @@ -418,6 +423,40 @@ static UINT8 K_KartItemOddsBattle[NUMKARTRESULTS][2] = { 5, 1 } // Jawz x2 }; +static UINT8 K_KartItemOddsSpecial[NUMKARTRESULTS-1][4] = +{ + //M N O P + { 0, 0, 2, 3, 4, 0, 0, 0 }, // Sneaker + { 0, 0, 0, 0, 0, 0, 0, 0 }, // Rocket Sneaker + { 0, 0, 0, 0, 2, 5, 5, 7 }, // Invincibility + { 2, 3, 1, 0, 0, 0, 0, 0 }, // Banana + { 1, 2, 0, 0, 0, 0, 0, 0 }, // Eggman Monitor + { 5, 5, 2, 2, 0, 0, 0, 0 }, // Orbinaut + { 0, 4, 2, 1, 0, 0, 0, 0 }, // Jawz + { 0, 3, 3, 1, 0, 0, 0, 0 }, // Mine + { 3, 0, 0, 0, 0, 0, 0, 0 }, // Land Mine + { 0, 0, 2, 2, 0, 0, 0, 0 }, // Ballhog + { 0, 0, 0, 0, 0, 2, 4, 0 }, // Self-Propelled Bomb + { 0, 0, 0, 0, 2, 5, 0, 0 }, // Grow + { 0, 0, 0, 0, 0, 2, 4, 2 }, // Shrink + { 1, 0, 0, 0, 0, 0, 0, 0 }, // Lightning Shield + { 0, 1, 2, 1, 0, 0, 0, 0 }, // Bubble Shield + { 0, 0, 0, 0, 0, 1, 3, 5 }, // Flame Shield + { 3, 0, 0, 0, 0, 0, 0, 0 }, // Hyudoro + { 0, 0, 0, 0, 0, 0, 0, 0 }, // Pogo Spring + { 2, 1, 1, 0, 0, 0, 0, 0 }, // Super Ring + { 0, 0, 0, 0, 0, 0, 0, 0 }, // Kitchen Sink + { 3, 0, 0, 0, 0, 0, 0, 0 }, // Drop Target + { 0, 0, 0, 0, 0, 0, 0, 0 }, // Garden Top + { 0, 0, 0, 0, 0, 0, 0, 0 }, // Sneaker x2 + { 0, 0, 0, 0, 4, 4, 4, 0 }, // Sneaker x3 + { 0, 1, 1, 0, 0, 0, 0, 0 }, // Banana x3 + { 0, 0, 0, 1, 0, 0, 0, 0 }, // Banana x10 + { 0, 0, 1, 0, 0, 0, 0, 0 }, // Orbinaut x3 + { 0, 0, 0, 2, 0, 0, 0, 0 }, // Orbinaut x4 + { 0, 0, 1, 2, 1, 0, 0, 0 } // Jawz x2 +}; + #define DISTVAR (2048) // Magic number distance for use with item roulette tiers #define SPBSTARTDIST (6*DISTVAR) // Distance when SPB can start appearing #define SPBFORCEDIST (12*DISTVAR) // Distance when SPB is forced onto the next person who rolls an item diff --git a/src/k_specialstage.c b/src/k_specialstage.c new file mode 100644 index 000000000..3a2d751ac --- /dev/null +++ b/src/k_specialstage.c @@ -0,0 +1,139 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2022 by Sally "TehRealSalt" Cochenour +// Copyright (C) 2022 by Kart Krew +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file k_specialstage.c +/// \brief Special Stage game logic + +#include "k_specialstage.h" +#include "doomdef.h" +#include "d_player.h" +#include "g_game.h" +#include "p_local.h" +#include "k_kart.h" +#include "s_sound.h" +#include "st_stuff.h" +#include "z_zone.h" +#include "k_waypoint.h" + +struct specialStage specialStage; + +/*-------------------------------------------------- + void K_ResetSpecialStage(void) + + See header file for description. +--------------------------------------------------*/ +void K_ResetSpecialStage(void) +{ + memset(&specialStage, 0, sizeof(struct specialStage)); +} + +/*-------------------------------------------------- + void K_InitSpecialStage(void) + + See header file for description. +--------------------------------------------------*/ +void K_InitSpecialStage(void) +{ + INT32 i; + + specialStage.beamDist = UINT32_MAX; // TODO: make proper value + + for (i = 0; i < MAXPLAYERS; i++) + { + player_t *player = NULL; + + if (playeringame[i] == false) + { + continue; + } + + player = &players[i]; + if (player->spectator == true) + { + continue; + } + + if (player->mo == NULL || P_MobjWasRemoved(player->mo) == true) + { + continue; + } + + // Rolling start? lol + P_InstaThrust(player->mo, player->mo->angle, K_GetKartSpeed(player, false, false)); + } +} + +/*-------------------------------------------------- + static void K_MoveExitBeam(void) + + Updates the exit beam. +--------------------------------------------------*/ +static void K_MoveExitBeam(void) +{ + UINT32 moveDist = 0; + INT32 i; + + if (leveltime <= 2) + { + return; + } + + moveDist = (8 * mapobjectscale) / FRACUNIT; + + if (specialStage.beamDist <= moveDist) + { + specialStage.beamDist = 0; + + // TODO: Fail Special Stage + } + else + { + specialStage.beamDist -= moveDist; + } + + // Find players who are now outside of the level. + for (i = 0; i < MAXPLAYERS; i++) + { + player_t *player = NULL; + + if (playeringame[i] == false) + { + continue; + } + + player = &players[i]; + + if (player->spectator == true + || player->exiting > 0 + || (player->pflags & PF_NOCONTEST)) + { + continue; + } + + if (player->distancetofinish > specialStage.beamDist) + { + P_DoTimeOver(player); + } + } +} + +/*-------------------------------------------------- + void K_TickSpecialStage(void) + + See header file for description. +--------------------------------------------------*/ +void K_TickSpecialStage(void) +{ + if (specialStage.active == false) + { + return; + } + + K_MoveExitBeam(); +} diff --git a/src/k_specialstage.h b/src/k_specialstage.h new file mode 100644 index 000000000..8e11d761d --- /dev/null +++ b/src/k_specialstage.h @@ -0,0 +1,55 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2022 by Sally "TehRealSalt" Cochenour +// Copyright (C) 2022 by Kart Krew +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file k_specialstage.h +/// \brief Special Stage game logic + +#ifndef __K_SPECIALSTAGE__ +#define __K_SPECIALSTAGE__ + +#include "doomdef.h" +#include "doomstat.h" + +extern struct specialStage +{ + boolean active; ///< If true, then we are in a special stage + boolean encore; ///< Copy of encore, just to make sure you can't cheat it with cvars + + UINT32 beamDist; ///< Where the exit beam is. + mobj_t *capsule; ///< The Chaos Emerald capsule. +} specialStage; + +/*-------------------------------------------------- + void K_ResetSpecialStage(void); + + Resets Special Stage information to a clean slate. +--------------------------------------------------*/ + +void K_ResetSpecialStage(void); + + +/*-------------------------------------------------- + void K_InitSpecialStage(void); + + Initializes Special Stage data on map load. +--------------------------------------------------*/ + +void K_InitSpecialStage(void); + + +/*-------------------------------------------------- + void K_TickSpecialStage(void); + + Updates Special Stage data each frame. +--------------------------------------------------*/ + +void K_TickSpecialStage(void); + + +#endif diff --git a/src/p_setup.c b/src/p_setup.c index a4f21ee76..71a115896 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -96,8 +96,8 @@ #include "k_boss.h" #include "k_terrain.h" // TRF_TRIPWIRE #include "k_brightmap.h" -#include "k_terrain.h" // TRF_TRIPWIRE #include "k_director.h" // K_InitDirector +#include "k_specialstage.h" // Replay names have time #if !defined (UNDER_CE) diff --git a/src/p_tick.c b/src/p_tick.c index ad83abca6..f0ba05546 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -36,6 +36,7 @@ #include "k_boss.h" #include "k_waypoint.h" #include "k_director.h" +#include "k_specialstage.h" tic_t leveltime; @@ -695,6 +696,8 @@ void P_Ticker(boolean run) K_BossInfoTicker(); + K_TickSpecialStage(); + if ((gametyperules & GTR_BUMPERS)) { if (wantedcalcdelay && --wantedcalcdelay <= 0)