diff --git a/src/d_main.cpp b/src/d_main.cpp index 4ae999d3e..8c7dfee1f 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -93,7 +93,7 @@ #define ASSET_HASH_TEXTURES_KART 0xb4211b2f32b6a291 #define ASSET_HASH_CHARS_KART 0x1e68a3e01aa5c68b #define ASSET_HASH_MAPS_KART 0x38558ed00da41ce9 -#define ASSET_HASH_MAIN_PK3 0x49b468523c8d7784 +#define ASSET_HASH_MAIN_PK3 0x1f23235e31fd1f84 #define ASSET_HASH_MAPPATCH_PK3 0xe6733c2c09ad2c02 #define ASSET_HASH_BONUSCHARS_KART 0x60e6f13d822a7461 #ifdef USE_PATCH_FILE diff --git a/src/doomdef.h b/src/doomdef.h index b617215fe..c54224e8e 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -103,7 +103,7 @@ extern "C" { // Special Hashing. //#define NOFILEHASH -#define NOVERIFYIWADS +//#define NOVERIFYIWADS // Uncheck this to compile debugging code //#define RANGECHECK diff --git a/src/doomstat.h b/src/doomstat.h index 484e773a7..e869975c6 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -677,7 +677,7 @@ extern SINT8 mostwanted; extern SINT8 battlewanted[4]; extern tic_t wantedcalcdelay; extern tic_t indirectitemcooldown; -extern tic_t hyubgone; +//extern tic_t hyubgone; extern tic_t mapreset; extern boolean thwompsactive; extern UINT8 lastLowestLap; diff --git a/src/f_finale.c b/src/f_finale.c index 69ced6bf7..bea8e14e1 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -439,8 +439,14 @@ void F_IntroTicker(void) } if (finalecount == 80) { - char chars[5][10] = {"tails", "chao", "aiai", "sakura"}; - SINT8 random = M_RandomRange(0, 3); + // I'm assuming these are all representative of a "dev character". + // Tails: NepDisk + // Chao: Alug + // Aiai: GHG + // Sakura: Anon + // Doomguy: Minenice + char chars[6][10] = {"tails", "chao", "aiai", "sakura", "doomguy"}; + SINT8 random = M_RandomRange(0, 4); sfxenum_t rsound = skins[R_SkinAvailable(chars[random])].soundsid[SKSKWIN]; S_StartSound(NULL, sfx_flgcap); S_StartSound(NULL, rsound); diff --git a/src/g_game.c b/src/g_game.c index 51849d6af..c9224d710 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -60,6 +60,7 @@ #include "k_grandprix.h" #include "k_boss.h" #include "k_bot.h" +#include "k_odds.h" #include "doomstat.h" #include "acs/interface.h" #include "k_director.h" @@ -299,7 +300,7 @@ SINT8 battlewanted[4]; // WANTED players in battle, worth x2 points SINT8 mostwanted; // The "most wanted" (first in line) player. tic_t wantedcalcdelay; // Time before it recalculates WANTED tic_t indirectitemcooldown; // Cooldown before any more Shrink, SPB, or any other item that works indirectly is awarded -tic_t hyubgone; // Cooldown before hyudoro is allowed to be rerolled +// hyubgone was taken out back. See k_odds.c tic_t mapreset; // Map reset delay when enough players have joined an empty game boolean thwompsactive; // Thwomps activate on lap 2 UINT8 lastLowestLap; // Last lowest lap, for activating race lap executors diff --git a/src/k_bot.cpp b/src/k_bot.cpp index 71a481359..f42f10b4b 100644 --- a/src/k_bot.cpp +++ b/src/k_bot.cpp @@ -46,6 +46,7 @@ consvar_t cv_forcebots = CVAR_INIT ("kartforcebots", "Off", CV_NETVAR|CV_CHEAT, CV_OnOff, NULL); consvar_t cv_botcontrol = CVAR_INIT ("kartbotcontrol", "On", CV_NETVAR|CV_CHEAT, CV_OnOff, NULL); +consvar_t cv_forcerival = CVAR_INIT ("kartbot_forcerival", "Off", CV_NETVAR|CV_CHEAT, CV_OnOff, NULL); botdata_t botdata[MAXPLAYERS]; @@ -159,6 +160,12 @@ void K_SetBot(UINT8 newplayernum, UINT16 skinnum, UINT8 difficulty, botStyle_e s players[newplayernum].botvars.style = style; players[newplayernum].lives = 9; + // Does the server want to force rivals? enforce this here. + if (cv_forcerival.value) + { + players[newplayernum].botvars.rival = true; + } + // The bot may immediately become a spectator AT THE START of a GP. // For each subsequent round of GP, K_UpdateGrandPrixBots will handle this. players[newplayernum].spectator = grandprixinfo.gp && grandprixinfo.initalize; @@ -279,6 +286,16 @@ void K_UpdateMatchRaceBots(void) // Enforce normal style for Match Race players[i].botvars.style = BOT_STYLE_NORMAL; + + // Does the server want to force rivals? enforce this here. + if (cv_forcerival.value) + { + players[i].botvars.rival = true; + } + else + { + players[i].botvars.rival = false; + } } else { diff --git a/src/k_bot.h b/src/k_bot.h index 6e9b66ce2..0facb20af 100644 --- a/src/k_bot.h +++ b/src/k_bot.h @@ -24,6 +24,7 @@ extern "C" { extern consvar_t cv_forcebots; extern consvar_t cv_botcontrol; +extern consvar_t cv_forcerival; // Maximum value of botvars.difficulty #define MAXBOTDIFFICULTY (13) diff --git a/src/k_kart.c b/src/k_kart.c index 809dba4b4..01bdf6060 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -271,6 +271,7 @@ void K_RegisterKartStuff(void) CV_RegisterVar(&cv_kartbot_basetrackcomplexity); CV_RegisterVar(&cv_forcebots); CV_RegisterVar(&cv_botcontrol); + CV_RegisterVar(&cv_forcerival); CV_RegisterVar(&cv_karteliminatelast); CV_RegisterVar(&cv_kartusepwrlv); CV_RegisterVar(&cv_votetime); diff --git a/src/k_odds.c b/src/k_odds.c index c557cbc9b..0c72f18af 100644 --- a/src/k_odds.c +++ b/src/k_odds.c @@ -152,6 +152,80 @@ static UINT8 K_KartItemOddsBattle[NUMKARTRESULTS][2] = { 5, 1 } // Jawz x2 }; +// Cooldown time table; contains both base (index 0) and current (index 1) +// times. Base times are in seconds, current times are in tics. +tic_t ItemBGone[NUMKARTRESULTS][2] = +{ + //BASE CURR + { 0, 0 }, // Null/Sad Face + { 0, 0 }, // Sneaker + { 0, 0 }, // Rocket Sneaker + { 0, 0 }, // Invincibility + { 0, 0 }, // Banana + { 10, 0 }, // Eggman Monitor + { 0, 0 }, // Orbinaut + { 5, 0 }, // Jawz + { 0, 0 }, // Mine + { 10, 0 }, // Ballhog + { 20, 0 }, // Self-Propelled Bomb + { 3, 0 }, // Grow + { 20, 0 }, // Shrink + { 0, 0 }, // Thunder Shield + { 20, 0 }, // Hyudoro + { 0, 0 }, // Pogo Spring + { 0, 0 }, // Kitchen Sink + { 0, 0 }, // Super Ring + { 0, 0 }, // Land Mine + { 5, 0 }, // Bubble Shield + { 8, 0 }, // Flame Shield + { 0, 0 }, // Sneaker x2 + { 0, 0 }, // Sneaker x3 + { 0, 0 }, // Banana x3 + { 30, 0 }, // Banana x10 + { 10, 0 }, // Orbinaut x3 + { 20, 0 }, // Orbinaut x4 + { 10, 0 } // Jawz x2 +}; + +// TODO: Vectorize all item tables and shove them into gamemode-uniqe pools (k_oddstable.cpp?). +// Basically necessary for any hopes of easy SOC support in the near future. + +/** \brief Sets the cooldown timer for an item. When active, the item is removed + * from all players' item pools for some time. + + \param item (SINT8) item to assign cooldown for + \param time (tic_t) cooldown time + + \return void +*/ +void K_SetBGone(SINT8 item, tic_t time) +{ + ItemBGone[item][GONER_CURRCOOLDOWN] = time; +} + +/** \brief Sets the cooldown timer for an item to its "base" (intended) time. + + \param item (SINT8) item to assign base cooldown for + + \return void +*/ +void K_SetBGoneToBase(SINT8 item) +{ + ItemBGone[item][GONER_CURRCOOLDOWN] = (ItemBGone[item][GONER_BASECOOLDOWN] * TICRATE); +} + +/** \brief Gets the cooldown timer for an item. + + \param item (SINT8) item to retrieve cooldown for + \param base (bool) if true, returns the "base" (intended) cooldown instead + + \return (tic_t) cooldown time +*/ +tic_t K_GetBGone(SINT8 item, boolean base) +{ + return ItemBGone[item][base ? GONER_BASECOOLDOWN : GONER_CURRCOOLDOWN] * (base ? TICRATE : 1); +} + // Magic number distance for use with item roulette tiers #define ACTIVEDISTVAR (K_UsingLegacyCheckpoints() ? DISTVAR_LEGACY : DISTVAR) @@ -288,8 +362,10 @@ static void K_KartGetItemResult(player_t *player, SINT8 getitem) if (getitem == KITEM_SPB || getitem == KITEM_SHRINK) // Indirect items indirectitemcooldown = 20*TICRATE; - if (getitem == KITEM_HYUDORO) // Hyudoro cooldown - hyubgone = 20*TICRATE; + if (K_GetBGone(getitem, true) > 0) // Item cooldowns + { + K_SetBGoneToBase(getitem); + } K_BotResetItemConfirm(player, true); @@ -378,7 +454,7 @@ UINT32 K_ScaleItemDistance(UINT32 distance, UINT8 numPlayers, boolean spbrush) #define MAXINVODDS ((MAXPROBABILITY * 2) * INVINDESPERATION) // Odds value for Alt. Invin. to force itself on trailing players. -#define INVFORCEODDS (3 * MAXINVODDS / 4) +#define INVFORCEODDS (MAXINVODDS / 2) static INT32 K_KartGetInvincibilityOdds(UINT32 dist) { @@ -411,6 +487,47 @@ static INT32 K_KartGetInvincibilityOdds(UINT32 dist) return min(MAXINVODDS, finodds); } +// Assigns general cooldowns to shield items. +void K_KartHandleShieldCooldown(SINT8 item) +{ + INT32 i; + + UINT8 pingame = 0, pexiting = 0; + + INT32 shieldtype = KSHIELD_NONE; + shieldtype = K_GetShieldFromItem(item); + + if (shieldtype == KSHIELD_NONE) + { + // Not a shield! + return; + } + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator) + continue; + + if (!(gametyperules & GTR_BUMPERS) || players[i].bumper) + pingame++; + + if (players[i].exiting) + pexiting++; + + if (((shieldtype == K_GetShieldFromItem(players[i].itemtype)) + || (shieldtype == K_GetShieldFromPlayer(&players[i])))) + { + // If this shield has a cooldown, force-apply the cooldown preeemptively for + // the entire time the shield is being held by a player. + if (K_GetBGone(item, true) > 0) + { + K_SetBGoneToBase(item); + break; + } + } + } +} + /** \brief Item Roulette for Kart \param player player object passed from P_KartPlayerThink @@ -674,9 +791,6 @@ INT32 K_KartGetItemOdds( break; case KITEM_HYUDORO: cooldownOnStart = true; - - if (hyubgone > 0) - newodds = 0; break; default: break; @@ -688,7 +802,12 @@ INT32 K_KartGetItemOdds( return newodds; } - if ((indirectItem == true) && (indirectitemcooldown > 0)) + if (K_GetBGone(item, false) > 0) + { + // (Replaces hyubgone) This item is on cooldown; don't let it get rolled. + newodds = 0; + } + else if ((indirectItem == true) && (indirectitemcooldown > 0)) { // Too many items that act indirectly in a match can feel kind of bad. newodds = 0; diff --git a/src/k_odds.h b/src/k_odds.h index cf57d2c50..c5ad73f59 100644 --- a/src/k_odds.h +++ b/src/k_odds.h @@ -36,6 +36,22 @@ extern "C" { extern consvar_t *KartItemCVars[NUMKARTRESULTS-1]; +// Goner; tracks how long an item should be "gone" for. +// AKA a time-based cooldown so Hyudoro and whatever else piss off to prevent +// spamming. + +typedef enum goner_sect_e { + GONER_BASECOOLDOWN = 0, + GONER_CURRCOOLDOWN = 1, +} goner_sect_t; + +extern tic_t ItemBGone[NUMKARTRESULTS][2]; + +void K_SetBGone(SINT8 item, tic_t time); +void K_SetBGoneToBase(SINT8 item); +tic_t K_GetBGone(SINT8 item, boolean base); +void K_KartHandleShieldCooldown(SINT8 item); + UINT32 K_CalculateInitalPDIS(const player_t *player, UINT8 pingame); UINT32 K_CalculatePDIS(const player_t *player, UINT8 numPlayers, boolean *spbrush); UINT8 K_FindUseodds(const player_t *player, fixed_t mashed, UINT32 pdis, UINT8 bestbumper, boolean spbrush); diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 9a3735d35..9762c280e 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -33,6 +33,7 @@ #include "k_color.h" #include "k_hud.h" #include "k_waypoint.h" +#include "k_odds.h" #include "d_netcmd.h" // IsPlayerAdmin #include "m_menu.h" // Player Setup menu color stuff #include "p_spec.h" // P_StartQuake @@ -4110,10 +4111,48 @@ static int lib_kSetHyuCountdown(lua_State *L) { tic_t c = (tic_t)luaL_checkinteger(L, 1); NOHUD - hyubgone = c; + K_SetBGone(KITEM_HYUDORO, c); return 0; } +static int lib_kSetBGone(lua_State *L) +{ + SINT8 item = (SINT8)luaL_checkinteger(L, 1); + tic_t c = (tic_t)luaL_checkinteger(L, 2); + NOHUD + if (item < KITEM_SNEAKER || item > (NUMKARTRESULTS - 1)) + { + luaL_error(L, "given item ID is outside bgone range"); + } + K_SetBGone(item, c); + return 0; +} + +static int lib_kSetBGoneToBase(lua_State *L) +{ + SINT8 item = (SINT8)luaL_checkinteger(L, 1); + NOHUD + if (item < KITEM_SNEAKER || item > (NUMKARTRESULTS - 1)) + { + luaL_error(L, "given item ID is outside bgone range"); + } + K_SetBGoneToBase(item); + return 0; +} + +static int lib_kGetBGone(lua_State *L) +{ + SINT8 item = (SINT8)luaL_checkinteger(L, 1); + boolean base = lua_isnoneornil(L, 2) ? false : luaL_checkboolean(L, 2); + NOHUD + if (item < KITEM_SNEAKER || item > (NUMKARTRESULTS - 1)) + { + luaL_error(L, "given item ID is outside bgone range"); + } + lua_pushinteger(L, K_GetBGone(item, base)); + return 1; +} + // Checks if the floor closet floor under an object would be safe to respawn/land on. static int lib_kSafeRespawnPosition(lua_State *L) { @@ -5302,6 +5341,9 @@ static luaL_Reg lib[] = { {"K_SetExitCountdown",lib_kSetExitCountdown}, {"K_SetIndirectItemCooldown",lib_kSetIndirectItemCountdown}, {"K_SetHyudoroCooldown",lib_kSetHyuCountdown}, + {"K_SetBGone", lib_kSetBGone}, + {"K_SetBGoneToBase", lib_kSetBGoneToBase}, + {"K_GetBGone", lib_kGetBGone}, {"K_SafeRespawnPosition",lib_kSafeRespawnPosition}, {"K_GetCollideAngle",lib_kGetCollideAngle}, diff --git a/src/lua_script.c b/src/lua_script.c index 36b911f37..b3ffc781f 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -27,6 +27,7 @@ #include "p_slopes.h" // for P_SlopeById and slopelist #include "p_polyobj.h" // polyobj_t, PolyObjects #include "k_battle.h" +#include "k_odds.h" #ifdef LUA_ALLOW_BYTECODE #include "d_netfil.h" // for LUA_DumpFile #endif @@ -416,7 +417,7 @@ int LUA_PushGlobals(lua_State *L, const char *word) lua_pushinteger(L, invintype); return 1; } else if (fastcmp(word,"hyubgone")) { - lua_pushinteger(L, hyubgone); + lua_pushinteger(L, K_GetBGone(KITEM_HYUDORO, false)); return 1; } else if (fastcmp(word,"encoremode")) { lua_pushboolean(L, encoremode); @@ -559,7 +560,9 @@ int LUA_WriteGlobals(lua_State *L, const char *word) else if (fastcmp(word,"indirectitemcooldown")) indirectitemcooldown = (tic_t)luaL_checkinteger(L, 2); else if (fastcmp(word,"hyubgone")) - hyubgone = (tic_t)luaL_checkinteger(L, 2); + { + K_SetBGone(KITEM_HYUDORO, (tic_t)luaL_checkinteger(L, 2)); + } else if (fastcmp(word,"starttime")) starttime = (tic_t)luaL_checkinteger(L, 2); else if (fastcmp(word,"introtime")) diff --git a/src/p_saveg.c b/src/p_saveg.c index b1d5820e3..8270810cb 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -41,6 +41,7 @@ #include "k_battle.h" #include "k_pwrlv.h" #include "k_terrain.h" +#include "k_odds.h" #include "acs/interface.h" #include "g_party.h" @@ -4260,7 +4261,14 @@ static boolean P_NetSyncMisc(savebuffer_t *save, boolean resending) SYNC(wantedcalcdelay); SYNC(indirectitemcooldown); - SYNC(hyubgone); + + for (i = 0; i < NUMKARTRESULTS; i++) + { + // hyubgone + //SYNC(ItemBGone[i][GONER_BASECOOLDOWN]); + SYNC(ItemBGone[i][GONER_CURRCOOLDOWN]); + } + SYNC(mapreset); SYNC(spectateGriefed); diff --git a/src/p_setup.c b/src/p_setup.c index cdf461ebc..63ba164f8 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -103,6 +103,7 @@ #include "k_terrain.h" // TRF_TRIPWIRE #include "k_brightmap.h" #include "k_director.h" // K_InitDirector +#include "k_odds.h" // ItemBGone #include "acs/interface.h" #include "doomstat.h" // MAXMUSNAMES #include "k_mapuser.h" @@ -8467,7 +8468,13 @@ static void P_InitGametype(void) wantedcalcdelay = wantedfrequency*2; indirectitemcooldown = 0; - hyubgone = 0; + + for (i = 0; i < NUMKARTRESULTS; i++) + { + // hyubgone + ItemBGone[i][GONER_CURRCOOLDOWN] = 0; + } + mapreset = 0; thwompsactive = false; diff --git a/src/p_tick.c b/src/p_tick.c index 6d3407e1e..e48ac83e2 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -41,6 +41,7 @@ #include "k_director.h" #include "acs/interface.h" #include "k_bot.h" // K_BotTicker +#include "k_odds.h" // ItemBGone #ifdef PARANOIA #include "deh_tables.h" // MOBJTYPE_LIST @@ -925,8 +926,22 @@ void P_Ticker(boolean run) if (indirectitemcooldown > 0) indirectitemcooldown--; - if (hyubgone > 0) - hyubgone--; + for (i = 0; i < NUMKARTRESULTS; i++) + { + // hyubgone + if (K_GetBGone(i, false) > 0) + { + ItemBGone[i][GONER_CURRCOOLDOWN]--; + } + } + + if (gametyperules & GTR_RACEODDS) + { + // Shield cooldowns + K_KartHandleShieldCooldown(KITEM_THUNDERSHIELD); + K_KartHandleShieldCooldown(KITEM_BUBBLESHIELD); + K_KartHandleShieldCooldown(KITEM_FLAMESHIELD); + } K_BossInfoTicker();