From 41540ed9e5a6ed17e59a14a1d4c957bf11176f25 Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Wed, 14 Jun 2023 23:32:31 -0700 Subject: [PATCH 01/62] WIP: Ring Box --- src/d_netcmd.c | 8 +++++- src/d_player.h | 9 +++++- src/k_hud.c | 36 +++++++++++++++++++++++- src/k_kart.c | 42 ++++++++++++++++++++++++++++ src/k_roulette.c | 59 +++++++++++++++++++++++++++++++++++---- src/k_roulette.h | 10 ++++--- src/objects/random-item.c | 3 ++ src/p_enemy.c | 7 ++++- src/p_inter.c | 2 +- src/p_mobj.c | 1 + 10 files changed, 163 insertions(+), 14 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 712c01a3f..66878ca66 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -398,7 +398,13 @@ consvar_t cv_items[NUMKARTRESULTS-1] = { CVAR_INIT ("tripleorbinaut", "On", CV_NETVAR, CV_OnOff, NULL), CVAR_INIT ("quadorbinaut", "On", CV_NETVAR, CV_OnOff, NULL), CVAR_INIT ("dualjawz", "On", CV_NETVAR, CV_OnOff, NULL), - CVAR_INIT ("triplegachabom", "On", CV_NETVAR, CV_OnOff, NULL) + CVAR_INIT ("triplegachabom", "On", CV_NETVAR, CV_OnOff, NULL), + CVAR_INIT ("bar", "On", CV_NETVAR, CV_OnOff, NULL), + CVAR_INIT ("doublebar", "On", CV_NETVAR, CV_OnOff, NULL), + CVAR_INIT ("triplebar", "On", CV_NETVAR, CV_OnOff, NULL), + CVAR_INIT ("slotring", "On", CV_NETVAR, CV_OnOff, NULL), + CVAR_INIT ("seven", "On", CV_NETVAR, CV_OnOff, NULL), + CVAR_INIT ("jackpot", "On", CV_NETVAR, CV_OnOff, NULL) }; consvar_t cv_kartspeed = CVAR_INIT ("gamespeed", "Auto", CV_NETVAR|CV_CALL|CV_NOINIT, kartspeed_cons_t, KartSpeed_OnChange); diff --git a/src/d_player.h b/src/d_player.h index 877f65c59..56378db93 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -160,7 +160,13 @@ Run this macro, then #undef FOREACH afterward FOREACH (KITCHENSINK, 20),\ FOREACH (DROPTARGET, 21),\ FOREACH (GARDENTOP, 22),\ - FOREACH (GACHABOM, 23) + FOREACH (GACHABOM, 23),\ + FOREACH (BAR, 24),\ + FOREACH (DOUBLEBAR, 25),\ + FOREACH (TRIPLEBAR, 26),\ + FOREACH (SLOTRING, 27),\ + FOREACH (SEVEN, 28),\ + FOREACH (JACKPOT, 29) typedef enum { @@ -399,6 +405,7 @@ struct itemroulette_t tic_t elapsed; boolean eggman; + boolean ringbox; }; // enum for bot item priorities diff --git a/src/k_hud.c b/src/k_hud.c index f958ff6c9..30574a616 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -114,6 +114,7 @@ static patch_t *kp_wantedsplit; static patch_t *kp_wantedreticle; static patch_t *kp_itembg[4]; +static patch_t *kp_ringbg[4]; static patch_t *kp_itemtimer[2]; static patch_t *kp_itemmulsticker[2]; static patch_t *kp_itemx; @@ -142,6 +143,12 @@ static patch_t *kp_kitchensink[2]; static patch_t *kp_droptarget[2]; static patch_t *kp_gardentop[2]; static patch_t *kp_gachabom[2]; +static patch_t *kp_bar[2]; +static patch_t *kp_doublebar[2]; +static patch_t *kp_triplebar[2]; +static patch_t *kp_slotring[2]; +static patch_t *kp_seven[2]; +static patch_t *kp_jackpot[2]; static patch_t *kp_check[6]; @@ -443,6 +450,9 @@ void K_LoadKartHUDGraphics(void) HU_UpdatePatch(&kp_itemmulsticker[0], "K_ITMUL"); HU_UpdatePatch(&kp_itemx, "K_ITX"); + HU_UpdatePatch(&kp_ringbg[0], "K_RBBG"); + HU_UpdatePatch(&kp_ringbg[1], "K_SBBG"); + HU_UpdatePatch(&kp_sadface[0], "K_ITSAD"); HU_UpdatePatch(&kp_sneaker[0], "K_ITSHOE"); HU_UpdatePatch(&kp_rocketsneaker[0], "K_ITRSHE"); @@ -478,6 +488,12 @@ void K_LoadKartHUDGraphics(void) HU_UpdatePatch(&kp_droptarget[0], "K_ITDTRG"); HU_UpdatePatch(&kp_gardentop[0], "K_ITGTOP"); HU_UpdatePatch(&kp_gachabom[0], "K_ITGBOM"); + HU_UpdatePatch(&kp_bar[0], "K_RBBAR"); + HU_UpdatePatch(&kp_doublebar[0], "K_RBBAR2"); + HU_UpdatePatch(&kp_triplebar[0], "K_RBBAR3"); + HU_UpdatePatch(&kp_slotring[0], "K_RBRING"); + HU_UpdatePatch(&kp_seven[0], "K_RBSEV"); + HU_UpdatePatch(&kp_jackpot[0], "K_RBJACK"); sprintf(buffer, "FSMFGxxx"); for (i = 0; i < 104; i++) @@ -531,6 +547,12 @@ void K_LoadKartHUDGraphics(void) HU_UpdatePatch(&kp_droptarget[1], "K_ISDTRG"); HU_UpdatePatch(&kp_gardentop[1], "K_ISGTOP"); HU_UpdatePatch(&kp_gachabom[1], "K_ISGBOM"); + HU_UpdatePatch(&kp_bar[1], "K_SBBAR"); + HU_UpdatePatch(&kp_doublebar[1], "K_SBBAR2"); + HU_UpdatePatch(&kp_triplebar[1], "K_SBBAR3"); + HU_UpdatePatch(&kp_slotring[1], "K_SBRING"); + HU_UpdatePatch(&kp_seven[1], "K_SBSEV"); + HU_UpdatePatch(&kp_jackpot[1], "K_SBJACK"); sprintf(buffer, "FSMFSxxx"); for (i = 0; i < 104; i++) @@ -844,6 +866,12 @@ static patch_t *K_GetCachedItemPatch(INT32 item, UINT8 offset) kp_droptarget, kp_gardentop, kp_gachabom, + kp_bar, + kp_doublebar, + kp_triplebar, + kp_slotring, + kp_seven, + kp_jackpot, }; if (item == KITEM_SAD || (item > KITEM_NONE && item < NUMKARTITEMS)) @@ -1211,6 +1239,12 @@ static void K_drawKartItem(void) vector2_t rouletteCrop = {7, 7}; INT32 i; + if (stplyr->itemRoulette.ringbox) + { + // Todo: owl + localbg = offset ? kp_ringbg[1] : kp_ringbg[0]; + } + if (stplyr->itemRoulette.itemListLen > 0) { // Init with item roulette stuff. @@ -4943,7 +4977,7 @@ static void K_drawDistributionDebugger(void) return; } - K_FillItemRouletteData(stplyr, &rouletteData); + K_FillItemRouletteData(stplyr, &rouletteData, false); for (i = 0; i < rouletteData.itemListLen; i++) { diff --git a/src/k_kart.c b/src/k_kart.c index 33fcd8819..f1144f364 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -11430,6 +11430,48 @@ void K_MoveKartPlayer(player_t *player, boolean onground) K_UpdateHnextList(player, false); } break; + case KITEM_BAR: + if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) + { + K_AwardPlayerRings(player, 10, true); + player->itemamount--; + } + break; + case KITEM_DOUBLEBAR: + if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) + { + K_AwardPlayerRings(player, 20, true); + player->itemamount--; + } + break; + case KITEM_TRIPLEBAR: + if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) + { + K_AwardPlayerRings(player, 30, true); + player->itemamount--; + } + break; + case KITEM_SLOTRING: + if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) + { + K_AwardPlayerRings(player, 50, true); + player->itemamount--; + } + break; + case KITEM_SEVEN: + if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) + { + K_AwardPlayerRings(player, 77, true); + player->itemamount--; + } + break; + case KITEM_JACKPOT: + if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) + { + K_AwardPlayerRings(player, 150, true); + player->itemamount--; + } + break; case KITEM_SAD: if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO && !player->sadtimer) diff --git a/src/k_roulette.c b/src/k_roulette.c index 5a6f79dda..a6caea8b1 100644 --- a/src/k_roulette.c +++ b/src/k_roulette.c @@ -100,6 +100,12 @@ static UINT8 K_KartItemOddsRace[NUMKARTRESULTS-1][8] = { 3, 0, 0, 0, 0, 0, 0, 0 }, // Drop Target { 0, 0, 0, 1, 2, 2, 0, 0 }, // Garden Top { 0, 0, 0, 0, 0, 0, 0, 0 }, // Gachabom + { 0, 0, 0, 0, 0, 0, 0, 0 }, // Bar + { 0, 0, 0, 0, 0, 0, 0, 0 }, // Double Bar + { 0, 0, 0, 0, 0, 0, 0, 0 }, // Triple Bar + { 0, 0, 0, 0, 0, 0, 0, 0 }, // Slot Ring + { 0, 0, 0, 0, 0, 0, 0, 0 }, // Seven + { 0, 0, 0, 0, 0, 0, 0, 0 }, // Jackpot { 0, 0, 2, 3, 3, 1, 0, 0 }, // Sneaker x2 { 0, 0, 0, 0, 4, 4, 4, 0 }, // Sneaker x3 { 0, 1, 1, 0, 0, 0, 0, 0 }, // Banana x3 @@ -134,6 +140,12 @@ static UINT8 K_KartItemOddsBattle[NUMKARTRESULTS-1][2] = { 2, 0 }, // Drop Target { 4, 0 }, // Garden Top { 0, 0 }, // Gachabom + { 0, 0 }, // Bar + { 0, 0 }, // Double Bar + { 0, 0 }, // Triple Bar + { 0, 0 }, // Slot Ring + { 0, 0 }, // Seven + { 0, 0 }, // Jackpot { 0, 0 }, // Sneaker x2 { 0, 1 }, // Sneaker x3 { 0, 0 }, // Banana x3 @@ -168,6 +180,12 @@ static UINT8 K_KartItemOddsSpecial[NUMKARTRESULTS-1][4] = { 0, 0, 0, 0 }, // Drop Target { 0, 0, 0, 0 }, // Garden Top { 0, 0, 0, 0 }, // Gachabom + { 0, 0, 0, 0 }, // Bar + { 0, 0, 0, 0 }, // Double Bar + { 0, 0, 0, 0 }, // Triple Bar + { 0, 0, 0, 0 }, // Slot Ring + { 0, 0, 0, 0 }, // Seven + { 0, 0, 0, 0 }, // Jackpot { 0, 1, 1, 0 }, // Sneaker x2 { 0, 0, 1, 1 }, // Sneaker x3 { 0, 0, 0, 0 }, // Banana x3 @@ -218,6 +236,17 @@ static kartitems_t K_KartItemReelBoss[] = KITEM_NONE }; +static kartitems_t K_KartItemReelRingBox[] = +{ + KITEM_BAR, + KITEM_DOUBLEBAR, + KITEM_TRIPLEBAR, + KITEM_SLOTRING, + KITEM_SEVEN, + KITEM_JACKPOT, + KITEM_NONE +}; + /*-------------------------------------------------- boolean K_ItemEnabled(kartitems_t item) @@ -1052,6 +1081,7 @@ static void K_InitRoulette(itemroulette_t *const roulette) roulette->active = true; roulette->eggman = false; + roulette->ringbox = false; for (i = 0; i < MAXPLAYERS; i++) { @@ -1257,7 +1287,7 @@ static void K_CalculateRouletteSpeed(itemroulette_t *const roulette) See header file for description. --------------------------------------------------*/ -void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulette) +void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulette, boolean ringbox) { UINT32 spawnChance[NUMKARTRESULTS] = {0}; UINT32 totalSpawnChance = 0; @@ -1284,6 +1314,22 @@ void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulet return; } + // SPECIAL CASE No. 1.5: + // If this is being invoked by a Ring Box, it should literally never produce items. + if (ringbox) + { + kartitems_t *presetlist = K_KartItemReelRingBox; + + roulette->ringbox = true; + + for (i = 0; presetlist[i] != KITEM_NONE; i++) + { + K_PushToRouletteItemList(roulette, presetlist[i]); + } + + return; + } + // SPECIAL CASE No. 2: // Use a special, pre-determined item reel for Time Attack / Free Play / End of Sealed Stars if (specialstageinfo.valid) @@ -1412,12 +1458,12 @@ void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulet See header file for description. --------------------------------------------------*/ -void K_StartItemRoulette(player_t *const player) +void K_StartItemRoulette(player_t *const player, boolean ringbox) { itemroulette_t *const roulette = &player->itemRoulette; size_t i; - K_FillItemRouletteData(player, roulette); + K_FillItemRouletteData(player, roulette, ringbox); if (K_PlayerUsesBotMovement(player) == true) { @@ -1444,7 +1490,7 @@ void K_StartItemRoulette(player_t *const player) void K_StartEggmanRoulette(player_t *const player) { itemroulette_t *const roulette = &player->itemRoulette; - K_StartItemRoulette(player); + K_StartItemRoulette(player, false); roulette->eggman = true; } @@ -1603,7 +1649,10 @@ void K_KartItemRoulette(player_t *const player, ticcmd_t *const cmd) if (P_IsDisplayPlayer(player) && !demo.freecam) { - S_StartSound(NULL, sfx_itrol1 + roulette->sound); + if (roulette->ringbox) + S_StartSound(NULL, sfx_s240); + else + S_StartSound(NULL, sfx_itrol1 + roulette->sound); } } else diff --git a/src/k_roulette.h b/src/k_roulette.h index 61624525d..379a67434 100644 --- a/src/k_roulette.h +++ b/src/k_roulette.h @@ -98,7 +98,7 @@ INT32 K_KartGetItemOdds(const player_t *player, itemroulette_t *const roulette, /*-------------------------------------------------- - void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulette); + void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulette, boolean ringbox); Fills out the item roulette struct when it is initially created. This function needs to be @@ -109,16 +109,17 @@ INT32 K_KartGetItemOdds(const player_t *player, itemroulette_t *const roulette, player - The player this roulette data is for. Can be NULL for generic use. roulette - The roulette data struct to fill out. + ringbox - Is this roulette fill triggered by a just-respawned Ring Box? Return:- N/A --------------------------------------------------*/ -void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulette); +void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulette, boolean ringbox); /*-------------------------------------------------- - void K_StartItemRoulette(player_t *const player); + void K_StartItemRoulette(player_t *const player, boolean ringbox); Starts the item roulette sequence for a player. This stage can only be used by gameplay, thus @@ -126,12 +127,13 @@ void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulet Input Arguments:- player - The player to start the item roulette for. + ringbox - Is this roulette being started from a just-respawned Ring Box? Return:- N/A --------------------------------------------------*/ -void K_StartItemRoulette(player_t *const player); +void K_StartItemRoulette(player_t *const player, boolean ringbox); /*-------------------------------------------------- diff --git a/src/objects/random-item.c b/src/objects/random-item.c index 4ec2106fe..cae20f752 100644 --- a/src/objects/random-item.c +++ b/src/objects/random-item.c @@ -102,6 +102,8 @@ void Obj_RandomItemVisuals(mobj_t *mobj) ItemBoxBob(mobj); ItemBoxScaling(mobj); item_vfxtimer(mobj)++; + if (mobj->fuse == 0) + mobj->extravalue1++; } boolean Obj_RandomItemSpawnIn(mobj_t *mobj) @@ -116,6 +118,7 @@ boolean Obj_RandomItemSpawnIn(mobj_t *mobj) } else { + // Spawn a battle monitor in your place and Fucking Die mobj_t *paperspawner = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_PAPERITEMSPOT); paperspawner->spawnpoint = mobj->spawnpoint; mobj->spawnpoint->mobj = paperspawner; diff --git a/src/p_enemy.c b/src/p_enemy.c index 17aa6ea71..4553e4fe5 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -13006,9 +13006,14 @@ void A_ItemPop(mobj_t *actor) } else if (locvar1 == 0) { - K_StartItemRoulette(actor->target->player); + if (actor->extravalue1 >= TICRATE) + K_StartItemRoulette(actor->target->player, false); + else + K_StartItemRoulette(actor->target->player, true); } + actor->extravalue1 = 0; + // Here at mapload in battle? if (!(gametyperules & GTR_CIRCUIT) && (actor->flags2 & MF2_BOSSNOTRAP)) { diff --git a/src/p_inter.c b/src/p_inter.c index c771b3427..58dd878fc 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -434,7 +434,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (special->fuse || !P_CanPickupItem(player, 1)) return; - K_StartItemRoulette(player); + K_StartItemRoulette(player, false); // Karma fireworks /*for (i = 0; i < 5; i++) diff --git a/src/p_mobj.c b/src/p_mobj.c index f0e529a0d..4be545796 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -9731,6 +9731,7 @@ static boolean P_FuseThink(mobj_t *mobj) // Transfer flags2 (strongbox, objectflip, bossnotrap) newmobj->flags2 = mobj->flags2; + newmobj->extravalue1 = 0; } P_RemoveMobj(mobj); // make sure they disappear From 28a65043f95981a1cb2e95e6521099eee907bdfd Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Wed, 14 Jun 2023 23:45:20 -0700 Subject: [PATCH 02/62] Autofire Ring Box items --- src/k_kart.c | 12 ++++++------ src/k_roulette.c | 5 ++++- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index f1144f364..b35ab439d 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -11431,42 +11431,42 @@ void K_MoveKartPlayer(player_t *player, boolean onground) } break; case KITEM_BAR: - if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) + if (player->karthud[khud_itemblink] < 1) { K_AwardPlayerRings(player, 10, true); player->itemamount--; } break; case KITEM_DOUBLEBAR: - if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) + if (player->karthud[khud_itemblink] < 1) { K_AwardPlayerRings(player, 20, true); player->itemamount--; } break; case KITEM_TRIPLEBAR: - if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) + if (player->karthud[khud_itemblink] < 1) { K_AwardPlayerRings(player, 30, true); player->itemamount--; } break; case KITEM_SLOTRING: - if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) + if (player->karthud[khud_itemblink] < 1) { K_AwardPlayerRings(player, 50, true); player->itemamount--; } break; case KITEM_SEVEN: - if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) + if (player->karthud[khud_itemblink] < 1) { K_AwardPlayerRings(player, 77, true); player->itemamount--; } break; case KITEM_JACKPOT: - if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO) + if (player->karthud[khud_itemblink] < 1) { K_AwardPlayerRings(player, 150, true); player->itemamount--; diff --git a/src/k_roulette.c b/src/k_roulette.c index a6caea8b1..835108d52 100644 --- a/src/k_roulette.c +++ b/src/k_roulette.c @@ -1628,7 +1628,10 @@ void K_KartItemRoulette(player_t *const player, ticcmd_t *const cmd) if (P_IsDisplayPlayer(player) && !demo.freecam) { - S_StartSound(NULL, sfx_itrolf); + if (roulette->ringbox) + S_StartSound(NULL, sfx_s245); + else + S_StartSound(NULL, sfx_itrolf); } } From a18f84255be46be5cdba798075a9e266335c8fed Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Wed, 14 Jun 2023 23:59:45 -0700 Subject: [PATCH 03/62] Ring Box items aren't real items --- src/k_kart.c | 18 +++++++++++++++++- src/k_kart.h | 1 + src/objects/hyudoro.c | 3 +++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index b35ab439d..f98f52b57 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6595,7 +6595,7 @@ void K_DropItems(player_t *player) { K_DropHnextList(player); - if (player->mo && !P_MobjWasRemoved(player->mo) && player->itemamount > 0) + if (player->mo && !P_MobjWasRemoved(player->mo) && player->itemamount > 0 && !K_IsRingBoxItem(player->itemtype)) { mobj_t *drop = K_CreatePaperItem( player->mo->x, player->mo->y, player->mo->z + player->mo->height/2, @@ -12135,4 +12135,20 @@ boolean K_Cooperative(void) return false; } +boolean K_IsRingBoxItem(SINT8 itemtype) +{ + switch (itemtype) + { + case KITEM_BAR: + case KITEM_DOUBLEBAR: + case KITEM_TRIPLEBAR: + case KITEM_SLOTRING: + case KITEM_SEVEN: + case KITEM_JACKPOT: + return true; + default: + return true; + } +} + //} diff --git a/src/k_kart.h b/src/k_kart.h index af7d142d2..1896fc11f 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -220,6 +220,7 @@ tic_t K_TimeLimitForGametype(void); UINT32 K_PointLimitForGametype(void); boolean K_Cooperative(void); +boolean K_IsRingBoxItem(SINT8 itemtype); #ifdef __cplusplus } // extern "C" diff --git a/src/objects/hyudoro.c b/src/objects/hyudoro.c index 24a902a78..4ad73f0c3 100644 --- a/src/objects/hyudoro.c +++ b/src/objects/hyudoro.c @@ -446,6 +446,9 @@ hyudoro_patrol_hit_player if (!player->itemamount) return false; + if (K_IsRingBoxItem(player->itemtype)) + return false; + K_AddHitLag(toucher, TICRATE/2, false); hyudoro_mode(hyu) = HYU_RETURN; From 7842032eeeec076587811abe4915bc7940e7d464 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 16 Jun 2023 05:55:33 -0400 Subject: [PATCH 04/62] Split ringbox stuff from itembox stuff --- src/d_netcmd.c | 8 +-- src/d_player.h | 19 ++++--- src/k_hud.c | 127 +++++++++++++++++++++++++++++++++++++++--- src/k_kart.c | 60 +------------------- src/k_kart.h | 1 - src/k_roulette.c | 88 +++++++++++++---------------- src/objects/hyudoro.c | 3 - 7 files changed, 173 insertions(+), 133 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 66878ca66..712c01a3f 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -398,13 +398,7 @@ consvar_t cv_items[NUMKARTRESULTS-1] = { CVAR_INIT ("tripleorbinaut", "On", CV_NETVAR, CV_OnOff, NULL), CVAR_INIT ("quadorbinaut", "On", CV_NETVAR, CV_OnOff, NULL), CVAR_INIT ("dualjawz", "On", CV_NETVAR, CV_OnOff, NULL), - CVAR_INIT ("triplegachabom", "On", CV_NETVAR, CV_OnOff, NULL), - CVAR_INIT ("bar", "On", CV_NETVAR, CV_OnOff, NULL), - CVAR_INIT ("doublebar", "On", CV_NETVAR, CV_OnOff, NULL), - CVAR_INIT ("triplebar", "On", CV_NETVAR, CV_OnOff, NULL), - CVAR_INIT ("slotring", "On", CV_NETVAR, CV_OnOff, NULL), - CVAR_INIT ("seven", "On", CV_NETVAR, CV_OnOff, NULL), - CVAR_INIT ("jackpot", "On", CV_NETVAR, CV_OnOff, NULL) + CVAR_INIT ("triplegachabom", "On", CV_NETVAR, CV_OnOff, NULL) }; consvar_t cv_kartspeed = CVAR_INIT ("gamespeed", "Auto", CV_NETVAR|CV_CALL|CV_NOINIT, kartspeed_cons_t, KartSpeed_OnChange); diff --git a/src/d_player.h b/src/d_player.h index 56378db93..4ff0f5d15 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -160,13 +160,7 @@ Run this macro, then #undef FOREACH afterward FOREACH (KITCHENSINK, 20),\ FOREACH (DROPTARGET, 21),\ FOREACH (GARDENTOP, 22),\ - FOREACH (GACHABOM, 23),\ - FOREACH (BAR, 24),\ - FOREACH (DOUBLEBAR, 25),\ - FOREACH (TRIPLEBAR, 26),\ - FOREACH (SLOTRING, 27),\ - FOREACH (SEVEN, 28),\ - FOREACH (JACKPOT, 29) + FOREACH (GACHABOM, 23) typedef enum { @@ -198,6 +192,17 @@ typedef enum NUMKARTSHIELDS } kartshields_t; +typedef enum +{ + KSM_BAR, + KSM_DOUBLEBAR, + KSM_TRIPLEBAR, + KSM_RING, + KSM_SEVEN, + KSM_JACKPOT, + KSM__MAX, +} kartslotmachine_t; + typedef enum { KSPIN_THRUST = (1<<0), diff --git a/src/k_hud.c b/src/k_hud.c index 30574a616..3f4d56413 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -866,12 +866,6 @@ static patch_t *K_GetCachedItemPatch(INT32 item, UINT8 offset) kp_droptarget, kp_gardentop, kp_gachabom, - kp_bar, - kp_doublebar, - kp_triplebar, - kp_slotring, - kp_seven, - kp_jackpot, }; if (item == KITEM_SAD || (item > KITEM_NONE && item < NUMKARTITEMS)) @@ -903,6 +897,23 @@ static patch_t *K_GetSmallStaticCachedItemPatch(kartitems_t item) return K_GetCachedItemPatch(item, offset); } +static patch_t *K_GetCachedSlotMachinePatch(INT32 item, UINT8 offset) +{ + patch_t **kp[KSM__MAX] = { + kp_bar, + kp_doublebar, + kp_triplebar, + kp_slotring, + kp_seven, + kp_jackpot, + }; + + if (item >= 0 && item < KSM__MAX) + return kp[item][offset]; + else + return NULL; +} + //} INT32 ITEM_X, ITEM_Y; // Item Window @@ -1567,6 +1578,99 @@ static void K_drawKartItem(void) } } +static void K_drawKartSlotMachine(void) +{ + // ITEM_X = BASEVIDWIDTH-50; // 270 + // ITEM_Y = 24; // 24 + + // Why write V_DrawScaledPatch calls over and over when they're all the same? + // Set to 'no item' just in case. + const UINT8 offset = ((r_splitscreen > 1) ? 1 : 0); + + patch_t *localpatch[3] = { kp_nodraw, kp_nodraw, kp_nodraw }; + patch_t *localbg = offset ? kp_ringbg[1] : kp_ringbg[0]; + + INT32 fx = 0, fy = 0, fflags = 0; // final coords for hud and flags... + UINT16 localcolor[3] = { stplyr->skincolor }; + SINT8 colormode[3] = { TC_RAINBOW }; + + fixed_t rouletteOffset = 0; + fixed_t rouletteSpace = ROULETTE_SPACING; + vector2_t rouletteCrop = {7, 7}; + INT32 i; + + if (stplyr->itemRoulette.itemListLen > 0) + { + // Init with item roulette stuff. + for (i = 0; i < 3; i++) + { + const SINT8 indexOfs = i-1; + const size_t index = (stplyr->itemRoulette.index + indexOfs) % stplyr->itemRoulette.itemListLen; + + const SINT8 result = stplyr->itemRoulette.itemList[index]; + + localpatch[i] = K_GetCachedSlotMachinePatch(result, offset); + } + } + + if (stplyr->itemRoulette.active == true) + { + rouletteOffset = K_GetRouletteOffset(&stplyr->itemRoulette, rendertimefrac); + } + + // pain and suffering defined below + if (offset) + { + if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // If we are P1 or P3... + { + fx = ITEM_X; + fy = ITEM_Y; + fflags = V_SNAPTOLEFT|V_SNAPTOTOP|V_SPLITSCREEN; + } + else // else, that means we're P2 or P4. + { + fx = ITEM2_X; + fy = ITEM2_Y; + fflags = V_SNAPTORIGHT|V_SNAPTOTOP|V_SPLITSCREEN; + } + + rouletteSpace = ROULETTE_SPACING_SPLITSCREEN; + rouletteOffset = FixedMul(rouletteOffset, FixedDiv(ROULETTE_SPACING_SPLITSCREEN, ROULETTE_SPACING)); + rouletteCrop.x = 16; + rouletteCrop.y = 15; + } + else + { + fx = ITEM_X; + fy = ITEM_Y; + fflags = V_SNAPTOTOP|V_SNAPTOLEFT|V_SPLITSCREEN; + } + + V_DrawScaledPatch(fx, fy, V_HUDTRANS|V_SLIDEIN|fflags, localbg); + + V_SetClipRect( + (fx + rouletteCrop.x) << FRACBITS, (fy + rouletteCrop.y) << FRACBITS, + rouletteSpace, rouletteSpace, + V_SLIDEIN|fflags + ); + + // item box has special layering, transparency, different sized patches, other fucked up shit + // ring box is evenly spaced and easy + rouletteOffset += rouletteSpace; + for (i = 0; i < 3; i++) + { + V_DrawFixedPatch( + fx<itemRoulette.ringbox) + { + K_drawKartSlotMachine(); + } + else + { + K_drawKartItem(); + } + } if (demo.title) ; diff --git a/src/k_kart.c b/src/k_kart.c index f98f52b57..33fcd8819 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6595,7 +6595,7 @@ void K_DropItems(player_t *player) { K_DropHnextList(player); - if (player->mo && !P_MobjWasRemoved(player->mo) && player->itemamount > 0 && !K_IsRingBoxItem(player->itemtype)) + if (player->mo && !P_MobjWasRemoved(player->mo) && player->itemamount > 0) { mobj_t *drop = K_CreatePaperItem( player->mo->x, player->mo->y, player->mo->z + player->mo->height/2, @@ -11430,48 +11430,6 @@ void K_MoveKartPlayer(player_t *player, boolean onground) K_UpdateHnextList(player, false); } break; - case KITEM_BAR: - if (player->karthud[khud_itemblink] < 1) - { - K_AwardPlayerRings(player, 10, true); - player->itemamount--; - } - break; - case KITEM_DOUBLEBAR: - if (player->karthud[khud_itemblink] < 1) - { - K_AwardPlayerRings(player, 20, true); - player->itemamount--; - } - break; - case KITEM_TRIPLEBAR: - if (player->karthud[khud_itemblink] < 1) - { - K_AwardPlayerRings(player, 30, true); - player->itemamount--; - } - break; - case KITEM_SLOTRING: - if (player->karthud[khud_itemblink] < 1) - { - K_AwardPlayerRings(player, 50, true); - player->itemamount--; - } - break; - case KITEM_SEVEN: - if (player->karthud[khud_itemblink] < 1) - { - K_AwardPlayerRings(player, 77, true); - player->itemamount--; - } - break; - case KITEM_JACKPOT: - if (player->karthud[khud_itemblink] < 1) - { - K_AwardPlayerRings(player, 150, true); - player->itemamount--; - } - break; case KITEM_SAD: if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO && !player->sadtimer) @@ -12135,20 +12093,4 @@ boolean K_Cooperative(void) return false; } -boolean K_IsRingBoxItem(SINT8 itemtype) -{ - switch (itemtype) - { - case KITEM_BAR: - case KITEM_DOUBLEBAR: - case KITEM_TRIPLEBAR: - case KITEM_SLOTRING: - case KITEM_SEVEN: - case KITEM_JACKPOT: - return true; - default: - return true; - } -} - //} diff --git a/src/k_kart.h b/src/k_kart.h index 1896fc11f..af7d142d2 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -220,7 +220,6 @@ tic_t K_TimeLimitForGametype(void); UINT32 K_PointLimitForGametype(void); boolean K_Cooperative(void); -boolean K_IsRingBoxItem(SINT8 itemtype); #ifdef __cplusplus } // extern "C" diff --git a/src/k_roulette.c b/src/k_roulette.c index 835108d52..dc4be35df 100644 --- a/src/k_roulette.c +++ b/src/k_roulette.c @@ -100,12 +100,6 @@ static UINT8 K_KartItemOddsRace[NUMKARTRESULTS-1][8] = { 3, 0, 0, 0, 0, 0, 0, 0 }, // Drop Target { 0, 0, 0, 1, 2, 2, 0, 0 }, // Garden Top { 0, 0, 0, 0, 0, 0, 0, 0 }, // Gachabom - { 0, 0, 0, 0, 0, 0, 0, 0 }, // Bar - { 0, 0, 0, 0, 0, 0, 0, 0 }, // Double Bar - { 0, 0, 0, 0, 0, 0, 0, 0 }, // Triple Bar - { 0, 0, 0, 0, 0, 0, 0, 0 }, // Slot Ring - { 0, 0, 0, 0, 0, 0, 0, 0 }, // Seven - { 0, 0, 0, 0, 0, 0, 0, 0 }, // Jackpot { 0, 0, 2, 3, 3, 1, 0, 0 }, // Sneaker x2 { 0, 0, 0, 0, 4, 4, 4, 0 }, // Sneaker x3 { 0, 1, 1, 0, 0, 0, 0, 0 }, // Banana x3 @@ -140,12 +134,6 @@ static UINT8 K_KartItemOddsBattle[NUMKARTRESULTS-1][2] = { 2, 0 }, // Drop Target { 4, 0 }, // Garden Top { 0, 0 }, // Gachabom - { 0, 0 }, // Bar - { 0, 0 }, // Double Bar - { 0, 0 }, // Triple Bar - { 0, 0 }, // Slot Ring - { 0, 0 }, // Seven - { 0, 0 }, // Jackpot { 0, 0 }, // Sneaker x2 { 0, 1 }, // Sneaker x3 { 0, 0 }, // Banana x3 @@ -180,12 +168,6 @@ static UINT8 K_KartItemOddsSpecial[NUMKARTRESULTS-1][4] = { 0, 0, 0, 0 }, // Drop Target { 0, 0, 0, 0 }, // Garden Top { 0, 0, 0, 0 }, // Gachabom - { 0, 0, 0, 0 }, // Bar - { 0, 0, 0, 0 }, // Double Bar - { 0, 0, 0, 0 }, // Triple Bar - { 0, 0, 0, 0 }, // Slot Ring - { 0, 0, 0, 0 }, // Seven - { 0, 0, 0, 0 }, // Jackpot { 0, 1, 1, 0 }, // Sneaker x2 { 0, 0, 1, 1 }, // Sneaker x3 { 0, 0, 0, 0 }, // Banana x3 @@ -236,15 +218,15 @@ static kartitems_t K_KartItemReelBoss[] = KITEM_NONE }; -static kartitems_t K_KartItemReelRingBox[] = +static kartslotmachine_t K_KartItemReelRingBox[] = { - KITEM_BAR, - KITEM_DOUBLEBAR, - KITEM_TRIPLEBAR, - KITEM_SLOTRING, - KITEM_SEVEN, - KITEM_JACKPOT, - KITEM_NONE + KSM_BAR, + KSM_DOUBLEBAR, + KSM_TRIPLEBAR, + KSM_RING, + KSM_SEVEN, + KSM_JACKPOT, + KSM__MAX }; /*-------------------------------------------------- @@ -1134,19 +1116,20 @@ static void K_InitRoulette(itemroulette_t *const roulette) } /*-------------------------------------------------- - static void K_PushToRouletteItemList(itemroulette_t *const roulette, kartitems_t item) + static void K_PushToRouletteItemList(itemroulette_t *const roulette, INT32 item) Pushes a new item to the end of the item - roulette's item list. + roulette's item list. Also accepts slot machine + values instead of items. Input Arguments:- roulette - The item roulette data to modify. - item - The item to push to the list. + item - The item / slot machine index to push to the list. Return:- N/A --------------------------------------------------*/ -static void K_PushToRouletteItemList(itemroulette_t *const roulette, kartitems_t item) +static void K_PushToRouletteItemList(itemroulette_t *const roulette, INT32 item) { #ifdef ITEM_LIST_SIZE if (roulette->itemListLen >= ITEM_LIST_SIZE) @@ -1283,7 +1266,7 @@ static void K_CalculateRouletteSpeed(itemroulette_t *const roulette) } /*-------------------------------------------------- - void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulette) + void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulette, boolean ringbox) See header file for description. --------------------------------------------------*/ @@ -1306,6 +1289,20 @@ void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulet K_CalculateRouletteSpeed(roulette); } + if (ringbox == true) + { + // If this is being invoked by a Ring Box, it should literally never produce items. + kartslotmachine_t *presetlist = K_KartItemReelRingBox; + roulette->ringbox = true; + + for (i = 0; presetlist[i] != KSM__MAX; i++) + { + K_PushToRouletteItemList(roulette, presetlist[i]); + } + + return; + } + // SPECIAL CASE No. 1: // Give only the debug item if specified if (cv_kartdebugitem.value != KITEM_NONE) @@ -1314,22 +1311,6 @@ void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulet return; } - // SPECIAL CASE No. 1.5: - // If this is being invoked by a Ring Box, it should literally never produce items. - if (ringbox) - { - kartitems_t *presetlist = K_KartItemReelRingBox; - - roulette->ringbox = true; - - for (i = 0; presetlist[i] != KITEM_NONE; i++) - { - K_PushToRouletteItemList(roulette, presetlist[i]); - } - - return; - } - // SPECIAL CASE No. 2: // Use a special, pre-determined item reel for Time Attack / Free Play / End of Sealed Stars if (specialstageinfo.valid) @@ -1618,9 +1599,18 @@ void K_KartItemRoulette(player_t *const player, ticcmd_t *const cmd) // And one more nudge for the remaining delay. roulette->tics = (roulette->tics + fudgedDelay) % roulette->speed; - kartitems_t finalItem = roulette->itemList[ roulette->index ]; + INT32 finalItem = roulette->itemList[ roulette->index ]; - K_KartGetItemResult(player, finalItem); + if (roulette->ringbox == true) + { + // TODO: add logic to make it give you the rings + //player->slotMachineType = finalItem; + //player->slotMachineDelay = TICRATE; + } + else + { + K_KartGetItemResult(player, finalItem); + } player->karthud[khud_itemblink] = TICRATE; player->karthud[khud_itemblinkmode] = 0; diff --git a/src/objects/hyudoro.c b/src/objects/hyudoro.c index 4ad73f0c3..24a902a78 100644 --- a/src/objects/hyudoro.c +++ b/src/objects/hyudoro.c @@ -446,9 +446,6 @@ hyudoro_patrol_hit_player if (!player->itemamount) return false; - if (K_IsRingBoxItem(player->itemtype)) - return false; - K_AddHitLag(toucher, TICRATE/2, false); hyudoro_mode(hyu) = HYU_RETURN; From e507a8b0ad5fb1271f28cc6b670213efe3139a57 Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 18 Jun 2023 22:07:28 +0100 Subject: [PATCH 05/62] P_EndingMusic: Play "EMRLD" in Emerald contexts - gametype has GTR_POWERSTONES and any one player has all 7 emeralds - gametyperules & GTR_SPECIALSTART and any one player isn't losing - GP after completing a GPEVENT_SPECIAL and any one player isn't losing - Catches custom gametypes in place of Sealed Star at the end of a GP --- src/p_user.c | 46 +++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/src/p_user.c b/src/p_user.c index c4535efb8..938bf96e1 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -714,12 +714,50 @@ void P_PlayVictorySound(mobj_t *source) void P_EndingMusic(void) { const char *jingle = NULL; - boolean nointer = false; UINT8 bestPos = UINT8_MAX; player_t *bestPlayer = NULL; SINT8 i; + // See G_DoCompleted and Y_DetermineIntermissionType + boolean nointer = ((modeattacking && (players[consoleplayer].pflags & PF_NOCONTEST)) + || (grandprixinfo.gp == true && grandprixinfo.eventmode != GPEVENT_NONE)); + + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] + || players[i].spectator) + continue; + + // Battle powerstone win + if ((gametyperules & GTR_POWERSTONES) + && ALLCHAOSEMERALDS(players[i].emeralds)) + break; + + // Special round? + if (((gametyperules & GTR_SPECIALSTART) + || (grandprixinfo.gp == true + && grandprixinfo.eventmode == GPEVENT_SPECIAL) + ) == false) + continue; + + // Any player has completed well? + if (!players[i].exiting + || players[i].bot + || K_IsPlayerLosing(&players[i])) + continue; + + // Special win + break; + } + + // Event - Emerald Finish + if (i != MAXPLAYERS) + { + jingle = "EMRLD"; + goto skippingposition; + } + // Event - Level Finish // Check for if this is valid or not for (i = 0; i <= r_splitscreen; i++) @@ -756,10 +794,6 @@ void P_EndingMusic(void) } } - // See G_DoCompleted and Y_DetermineIntermissionType - nointer = ((modeattacking && (players[consoleplayer].pflags & PF_NOCONTEST)) - || (grandprixinfo.gp == true && grandprixinfo.eventmode != GPEVENT_NONE)); - if (bestPlayer == NULL) { // No jingle for you @@ -798,6 +832,8 @@ void P_EndingMusic(void) } } +skippingposition: + if (nointer == true) { // Do not set "racent" in G_Ticker From ed8158c00c94684567a4b31509ea68d4a8169e8a Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 18 Jun 2023 22:09:49 +0100 Subject: [PATCH 06/62] P_SetupSignExit: Bail early if gametype has GTR_SPECIALSTART --- src/p_spec.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/p_spec.c b/src/p_spec.c index 51a62332c..d43c6e07b 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -4647,11 +4647,12 @@ void P_SetupSignExit(player_t *player, boolean tie) thinker_t *think; INT32 numfound = 0; - angle_t bestAngle = K_MomentumAngle(player->mo) + ANGLE_180; - - if (player->position != 1) + if (player->position != 1 + || (gametyperules & GTR_SPECIALSTART)) return; + angle_t bestAngle = K_MomentumAngle(player->mo) + ANGLE_180; + for (; node; node = node->m_thinglist_next) { thing = node->m_thing; From 08b471c9b224126bfd8d9b806bc38fa827a5dba0 Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 18 Jun 2023 22:10:22 +0100 Subject: [PATCH 07/62] Vaguely related request: Increase radius + height for MT_EMERALD by 8FU --- src/info.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/info.c b/src/info.c index 7d1c0023c..8733c8a4c 100644 --- a/src/info.c +++ b/src/info.c @@ -8117,8 +8117,8 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL, // xdeathstate sfx_s3k9c, // deathsound 0, // speed - 72*FRACUNIT, // radius - 72*FRACUNIT, // height + 80*FRACUNIT, // radius + 80*FRACUNIT, // height 0, // display offset 16, // mass 0, // damage From 2c09f397960ac10802cf558fc2f5030664d0c10c Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 18 Jun 2023 23:02:37 +0100 Subject: [PATCH 08/62] K_drawKartHUD: Draw laps if there's 0 laps, because that's more likely to be a testing environment --- src/k_hud.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_hud.c b/src/k_hud.c index c0098b016..8fa17c725 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -5280,7 +5280,7 @@ void K_drawKartHUD(void) { if (gametyperules & GTR_CIRCUIT) { - if (numlaps > 1) + if (numlaps != 1) { K_drawKartLaps(); gametypeinfoshown = true; From 8689e0735e3c7193fd6efd8fec1a8a7cb4baaa94 Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 18 Jun 2023 23:17:21 +0100 Subject: [PATCH 09/62] readcupheader: Permit emptying out BonusGame and SpecialStage for an existing cup Provide a single / and it'll be empty --- src/deh_soc.c | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/deh_soc.c b/src/deh_soc.c index eb30c33ed..e9e184bae 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -3438,6 +3438,15 @@ static void invalidateacrosscups(UINT16 map) mapheaderinfo[map]->cup = NULL; } +static char *MapNameOrRemoval(char *name) +{ + if (name[0] == '\0' + || (name[0] == '/' && name[1] == '\0')) + return NULL; + + return Z_StrDup(name); +} + void readcupheader(MYFILE *f, cupheader_t *cup) { char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); @@ -3539,8 +3548,12 @@ void readcupheader(MYFILE *f, cupheader_t *cup) break; } - cup->levellist[CUPCACHE_BONUS + cup->numbonus] = Z_StrDup(tmp); + cup->levellist[CUPCACHE_BONUS + cup->numbonus] = MapNameOrRemoval(tmp); cup->cachedlevels[CUPCACHE_BONUS + cup->numbonus] = NEXTMAP_INVALID; + + if (cup->levellist[CUPCACHE_BONUS + cup->numbonus] == NULL) + break; + cup->numbonus++; } while((tmp = strtok(NULL,",")) != NULL); } @@ -3548,7 +3561,7 @@ void readcupheader(MYFILE *f, cupheader_t *cup) { invalidateacrosscups(cup->cachedlevels[CUPCACHE_SPECIAL]); Z_Free(cup->levellist[CUPCACHE_SPECIAL]); - cup->levellist[CUPCACHE_SPECIAL] = Z_StrDup(word2); + cup->levellist[CUPCACHE_SPECIAL] = MapNameOrRemoval(word2); cup->cachedlevels[CUPCACHE_SPECIAL] = NEXTMAP_INVALID; } else if (fastcmp(word, "EMERALDNUM")) From 8d3e828663fdb0b9f57f8df6a725b701a42c670a Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 18 Jun 2023 23:18:51 +0100 Subject: [PATCH 10/62] AltPodium for cups We're unlikely to utilise this, but permits creators of custom user-created cups to flex their own style without fighting to replace the default Podium. --- src/deh_soc.c | 7 +++++++ src/doomstat.h | 3 ++- src/k_podium.c | 19 ++++++++++++++----- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/deh_soc.c b/src/deh_soc.c index e9e184bae..7037c7f36 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -3564,6 +3564,13 @@ void readcupheader(MYFILE *f, cupheader_t *cup) cup->levellist[CUPCACHE_SPECIAL] = MapNameOrRemoval(word2); cup->cachedlevels[CUPCACHE_SPECIAL] = NEXTMAP_INVALID; } + else if (fastcmp(word, "ALTPODIUM")) + { + invalidateacrosscups(cup->cachedlevels[CUPCACHE_PODIUM]); + Z_Free(cup->levellist[CUPCACHE_PODIUM]); + cup->levellist[CUPCACHE_PODIUM] = MapNameOrRemoval(word2); + cup->cachedlevels[CUPCACHE_PODIUM] = NEXTMAP_INVALID; + } else if (fastcmp(word, "EMERALDNUM")) { if (i >= 0 && i <= 14) diff --git a/src/doomstat.h b/src/doomstat.h index e6787116e..d25ca720c 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -383,7 +383,8 @@ struct customoption_t #define CUPCACHE_BONUS MAXLEVELLIST #define MAXBONUSLIST 2 #define CUPCACHE_SPECIAL (CUPCACHE_BONUS+MAXBONUSLIST) -#define CUPCACHE_MAX (CUPCACHE_SPECIAL+1) +#define CUPCACHE_PODIUM (CUPCACHE_SPECIAL+1) +#define CUPCACHE_MAX (CUPCACHE_PODIUM+1) #define MAXCUPNAME 16 // includes \0, for cleaner savedata diff --git a/src/k_podium.c b/src/k_podium.c index 630048e98..c5c70bdb9 100644 --- a/src/k_podium.c +++ b/src/k_podium.c @@ -235,16 +235,25 @@ void K_UpdatePodiumWaypoints(player_t *const player) --------------------------------------------------*/ boolean K_StartCeremony(void) { - INT32 podiumMapNum = nummapheaders; - INT32 i; - if (grandprixinfo.gp == false) { return false; } - if (podiummap - && ((podiumMapNum = G_MapNumber(podiummap)) < nummapheaders) + INT32 i; + INT32 podiumMapNum = NEXTMAP_INVALID; + + if (grandprixinfo.cup != NULL + && grandprixinfo.cup->cachedlevels[CUPCACHE_PODIUM] != NEXTMAP_INVALID) + { + podiumMapNum = grandprixinfo.cup->cachedlevels[CUPCACHE_PODIUM]; + } + else if (podiummap) + { + podiumMapNum = G_MapNumber(podiummap); + } + + if (podiumMapNum < nummapheaders && mapheaderinfo[podiumMapNum] && mapheaderinfo[podiumMapNum]->lumpnum != LUMPERROR) { From 43044ec032bcbdd88413467a3d494e1be6c51f81 Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 18 Jun 2023 23:21:04 +0100 Subject: [PATCH 11/62] PlayCredits for Cups Hilariously broken due to the evaulation gamestate, but the first piece of the puzzle: gets the player into the Credits gamestate after the conclusion of a Podium, if the GP context's cup has this boolean set. --- src/deh_soc.c | 8 ++++++-- src/doomstat.h | 3 +++ src/g_game.c | 12 +++++++++++- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/src/deh_soc.c b/src/deh_soc.c index 7037c7f36..f6509eabd 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -3126,7 +3126,7 @@ void readmaincfg(MYFILE *f, boolean mainfile) } else if (fastcmp(word, "LOOPTITLE")) { - looptitle = (value || word2[0] == 'T' || word2[0] == 'Y'); + looptitle = (value != 0 || word2[0] == 'T' || word2[0] == 'Y'); titlechanged = true; } else if (fastcmp(word, "TITLEMAP")) @@ -3137,7 +3137,7 @@ void readmaincfg(MYFILE *f, boolean mainfile) } else if (fastcmp(word, "HIDETITLEPICS") || fastcmp(word, "TITLEPICSHIDE")) { - hidetitlepics = (boolean)(value || word2[0] == 'T' || word2[0] == 'Y'); + hidetitlepics = (boolean)(value != 0 || word2[0] == 'T' || word2[0] == 'Y'); titlechanged = true; } else if (fastcmp(word, "TITLEPICSMODE")) @@ -3578,6 +3578,10 @@ void readcupheader(MYFILE *f, cupheader_t *cup) else deh_warning("%s Cup: invalid emerald number %d", cup->name, i); } + else if (fastcmp(word, "PLAYCREDITS")) + { + cup->playcredits = (i != 0 || word2[0] == 'T' || word2[0] == 'Y'); + } else deh_warning("%s Cup: unknown word '%s'", cup->name, word); } diff --git a/src/doomstat.h b/src/doomstat.h index d25ca720c..70d583f66 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -402,6 +402,9 @@ struct cupheader_t UINT8 numlevels; ///< Number of levels defined in levellist UINT8 numbonus; ///< Number of bonus stages defined UINT8 emeraldnum; ///< ID of Emerald to use for special stage (1-7 for Chaos Emeralds, 8-14 for Super Emeralds, 0 for no emerald) + + boolean playcredits; ///< Play the credits? + cupwindata_t windata[4]; ///< Data for cup visitation cupheader_t *next; ///< Next cup in linked list }; diff --git a/src/g_game.c b/src/g_game.c index 1b3dfa770..252e09368 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1813,7 +1813,17 @@ boolean G_Responder(event_t *ev) if (K_CeremonyResponder(ev)) { - nextmap = NEXTMAP_TITLE; + if (grandprixinfo.gp == true + && grandprixinfo.cup != NULL + && grandprixinfo.cup->playcredits == true) + { + nextmap = NEXTMAP_CREDITS; + } + else + { + nextmap = NEXTMAP_TITLE; + } + G_EndGame(); return true; } From c89048def71e8f9623507b892e735e043fb5cf6d Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 20 Jun 2023 23:22:17 +0100 Subject: [PATCH 12/62] Game End cleanup - Remove specific gamestate, drawer, etc - Just add an extra fade straight into G_EndGame --- src/d_main.c | 4 ---- src/deh_soc.c | 8 -------- src/deh_tables.c | 1 - src/f_finale.c | 44 ++++++++------------------------------------ src/f_finale.h | 4 ---- src/f_wipe.c | 10 ---------- src/g_game.c | 10 ---------- src/g_state.h | 1 - src/k_menufunc.c | 1 - 9 files changed, 8 insertions(+), 75 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index aa79e6584..7439b612a 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -476,10 +476,6 @@ static void D_Display(void) HU_Drawer(); break; - case GS_GAMEEND: - F_GameEndDrawer(); - break; - case GS_EVALUATION: F_GameEvaluationDrawer(); HU_Erase(); diff --git a/src/deh_soc.c b/src/deh_soc.c index f6509eabd..08995d083 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -3365,14 +3365,6 @@ void readwipes(MYFILE *f) else if (fastcmp(pword, "FINAL")) wipeoffset = wipe_evaluation_final; } - else if (fastncmp(word, "GAMEEND_", 8)) - { - pword = word + 8; - if (fastcmp(pword, "TOBLACK")) - wipeoffset = wipe_gameend_toblack; - else if (fastcmp(pword, "FINAL")) - wipeoffset = wipe_gameend_final; - } else if (fastncmp(word, "CEREMONY_", 9)) { pword = word + 9; diff --git a/src/deh_tables.c b/src/deh_tables.c index ddbae5cbf..1435dc5d4 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -6835,7 +6835,6 @@ struct int_const_s const INT_CONST[] = { {"GS_MENU",GS_MENU}, {"GS_CREDITS",GS_CREDITS}, {"GS_EVALUATION",GS_EVALUATION}, - {"GS_GAMEEND",GS_GAMEEND}, {"GS_INTRO",GS_INTRO}, {"GS_ENDING",GS_ENDING}, {"GS_CUTSCENE",GS_CUTSCENE}, diff --git a/src/f_finale.c b/src/f_finale.c index a6f4fb900..688009b68 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -904,7 +904,7 @@ void F_StartGameEvaluation(void) // Credits option in extras menu if (cursaveslot == -1) { - S_FadeOutStopMusic(2*MUSICRATE); + S_FadeOutStopMusic(MUSICRATE/2); F_StartGameEnd(); return; } @@ -1625,44 +1625,16 @@ void F_EndingDrawer(void) // ========== void F_StartGameEnd(void) { - G_SetGamestate(GS_GAMEEND); + // Early fadeout to let the sound finish playing + F_WipeStartScreen(); + V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); + F_WipeEndScreen(); + F_RunWipe(wipe_level_toblack, wipedefs[wipe_level_toblack], false, "FADEMAP0", false, false); - gameaction = ga_nothing; - paused = false; - CON_ToggleOff(); - S_StopSounds(); - - // In case menus are still up?!! - M_ClearMenus(true); - - timetonext = TICRATE; + nextmap = NEXTMAP_TITLE; + G_EndGame(); } -// -// F_GameEndDrawer -// -void F_GameEndDrawer(void) -{ - // this function does nothing -} - -// -// F_GameEndTicker -// -void F_GameEndTicker(void) -{ - if (timetonext > 0) - { - timetonext--; - } - else - { - nextmap = NEXTMAP_TITLE; - G_EndGame(); - } -} - - // ============== // TITLE SCREEN // ============== diff --git a/src/f_finale.h b/src/f_finale.h index 56691a847..6e79faab3 100644 --- a/src/f_finale.h +++ b/src/f_finale.h @@ -33,7 +33,6 @@ boolean F_CutsceneResponder(event_t *ev); boolean F_CreditResponder(event_t *ev); // Called by main loop. -void F_GameEndTicker(void); void F_IntroTicker(void); void F_TitleScreenTicker(boolean run); void F_CutsceneTicker(void); @@ -41,7 +40,6 @@ void F_TitleDemoTicker(void); void F_TextPromptTicker(void); // Called by main loop. -void F_GameEndDrawer(void); void F_IntroDrawer(void); void F_TitleScreenDrawer(void); void F_SkyScroll(INT32 scrollxspeed, INT32 scrollyspeed, const char *patchname); @@ -173,7 +171,6 @@ enum wipe_menu_toblack, wipe_credits_toblack, wipe_evaluation_toblack, - wipe_gameend_toblack, wipe_ceremony_toblack, wipe_intro_toblack, wipe_ending_toblack, @@ -192,7 +189,6 @@ enum wipe_menu_final, wipe_credits_final, wipe_evaluation_final, - wipe_gameend_final, wipe_ceremony_final, wipe_intro_final, wipe_ending_final, diff --git a/src/f_wipe.c b/src/f_wipe.c index 105358407..50d2c7391 100644 --- a/src/f_wipe.c +++ b/src/f_wipe.c @@ -63,7 +63,6 @@ UINT8 wipedefs[NUMWIPEDEFS] = { 1, // wipe_menu_toblack 99, // wipe_credits_toblack 0, // wipe_evaluation_toblack - 0, // wipe_gameend_toblack 0, // wipe_ceremony_toblack UINT8_MAX, // wipe_intro_toblack (hardcoded) 99, // wipe_ending_toblack (hardcoded) @@ -80,7 +79,6 @@ UINT8 wipedefs[NUMWIPEDEFS] = { 1, // wipe_menu_final 99, // wipe_credits_final 0, // wipe_evaluation_final - 0, // wipe_gameend_final 0, // wipe_ceremony_final 99, // wipe_intro_final (hardcoded) 99, // wipe_ending_final (hardcoded) @@ -98,7 +96,6 @@ static boolean g_wipedef_toblack[NUMWIPEDEFS] = { true, // wipe_menu_toblack true, // wipe_credits_toblack true, // wipe_evaluation_toblack - true, // wipe_gameend_toblack true, // wipe_ceremony_toblack true, // wipe_intro_toblack (hardcoded) true, // wipe_ending_toblack (hardcoded) @@ -115,7 +112,6 @@ static boolean g_wipedef_toblack[NUMWIPEDEFS] = { true, // wipe_menu_final true, // wipe_credits_final true, // wipe_evaluation_final - true, // wipe_gameend_final true, // wipe_ceremony_final true, // wipe_intro_final (hardcoded) true, // wipe_ending_final (hardcoded) @@ -133,7 +129,6 @@ static boolean g_wipedef_toinvert[NUMWIPEDEFS] = { false, // wipe_menu_toblack false, // wipe_credits_toblack false, // wipe_evaluation_toblack - false, // wipe_gameend_toblack false, // wipe_ceremony_toblack false, // wipe_intro_toblack (hardcoded) false, // wipe_ending_toblack (hardcoded) @@ -150,7 +145,6 @@ static boolean g_wipedef_toinvert[NUMWIPEDEFS] = { false, // wipe_menu_final false, // wipe_credits_final false, // wipe_evaluation_final - false, // wipe_gameend_final false, // wipe_ceremony_final false, // wipe_intro_final (hardcoded) false, // wipe_ending_final (hardcoded) @@ -168,7 +162,6 @@ static boolean g_wipedef_towhite[NUMWIPEDEFS] = { false, // wipe_menu_toblack false, // wipe_credits_toblack false, // wipe_evaluation_toblack - false, // wipe_gameend_toblack false, // wipe_ceremony_toblack false, // wipe_intro_toblack (hardcoded) false, // wipe_ending_toblack (hardcoded) @@ -185,7 +178,6 @@ static boolean g_wipedef_towhite[NUMWIPEDEFS] = { false, // wipe_menu_final false, // wipe_credits_final false, // wipe_evaluation_final - false, // wipe_gameend_final false, // wipe_ceremony_final false, // wipe_intro_final (hardcoded) false, // wipe_ending_final (hardcoded) @@ -203,7 +195,6 @@ static boolean g_wipedef_crossfade[NUMWIPEDEFS] = { false, // wipe_menu_toblack false, // wipe_credits_toblack false, // wipe_evaluation_toblack - false, // wipe_gameend_toblack false, // wipe_ceremony_toblack false, // wipe_intro_toblack (hardcoded) false, // wipe_ending_toblack (hardcoded) @@ -220,7 +211,6 @@ static boolean g_wipedef_crossfade[NUMWIPEDEFS] = { true, // wipe_menu_final true, // wipe_credits_final true, // wipe_evaluation_final - true, // wipe_gameend_final true, // wipe_ceremony_final true, // wipe_intro_final (hardcoded) true, // wipe_ending_final (hardcoded) diff --git a/src/g_game.c b/src/g_game.c index 252e09368..a04707627 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1832,11 +1832,6 @@ boolean G_Responder(event_t *ev) { return true; } - // Demo End - else if (gamestate == GS_GAMEEND) - { - return true; - } else if (gamestate == GS_INTERMISSION || gamestate == GS_VOTING || gamestate == GS_EVALUATION) { if (HU_Responder(ev)) @@ -2344,11 +2339,6 @@ void G_Ticker(boolean run) HU_Ticker(); break; - case GS_GAMEEND: - if (run) - F_GameEndTicker(); - break; - case GS_EVALUATION: if (run) F_GameEvaluationTicker(); diff --git a/src/g_state.h b/src/g_state.h index b78105639..97a828861 100644 --- a/src/g_state.h +++ b/src/g_state.h @@ -35,7 +35,6 @@ typedef enum GS_CREDITS, // credit sequence GS_EVALUATION, // Evaluation at the end of a game. - GS_GAMEEND, // game end sequence - "did you get all those chaos emeralds?" GS_CEREMONY, // RR: Podium sequence // Hardcoded fades or other fading methods diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 5acbb0e7d..1b4bddb01 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -209,7 +209,6 @@ static boolean M_GamestateCanOpenMenu(void) { case GS_INTRO: case GS_CUTSCENE: - case GS_GAMEEND: case GS_CREDITS: case GS_EVALUATION: case GS_CEREMONY: From 0ecc7ccd547b86f04df54c059b9b4fb9ed7a593a Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 21 Jun 2023 00:03:09 +0100 Subject: [PATCH 13/62] Make credits accessible from the menu again --- src/f_finale.c | 4 ++-- src/k_menu.h | 1 + src/menus/extras-1.c | 12 ++++++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/f_finale.c b/src/f_finale.c index 688009b68..f6bff28fb 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -902,9 +902,9 @@ boolean F_CreditResponder(event_t *event) void F_StartGameEvaluation(void) { // Credits option in extras menu - if (cursaveslot == -1) + if (grandprixinfo.gp == false) { - S_FadeOutStopMusic(MUSICRATE/2); + S_FadeMusic(0, MUSICRATE/4); F_StartGameEnd(); return; } diff --git a/src/k_menu.h b/src/k_menu.h index 7eefd06ea..832b4d7b2 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -1048,6 +1048,7 @@ typedef enum extras_eggtv, extras_stereo, extras_password, + extras_credits, } extras_e; void M_InitExtras(INT32 choice); // init for the struct diff --git a/src/menus/extras-1.c b/src/menus/extras-1.c index 5e11a6121..f623b2d3b 100644 --- a/src/menus/extras-1.c +++ b/src/menus/extras-1.c @@ -5,6 +5,15 @@ #include "../m_cond.h" #include "../m_cheat.h" #include "../s_sound.h" +#include "../f_finale.h" + +static void M_Credits(INT32 choice) +{ + (void)choice; + restoreMenu = currentMenu; + M_ClearMenus(true); + F_StartCredits(); +} menuitem_t EXTRAS_Main[] = { @@ -32,6 +41,9 @@ menuitem_t EXTRAS_Main[] = {IT_STRING | IT_CVAR | IT_CV_STRING, "Password", "If you don't know any passwords, come back later!", NULL, {.cvar = &cv_dummyextraspassword}, 0, 0}, + + {IT_STRING | IT_CALL, "Credits", "It's important to know who makes the video games you play.", + NULL, {.routine = M_Credits}, 0, 0}, }; // the extras menu essentially reuses the options menu stuff From f15a7a946d7756137c3b8fba8d6ab873f0603299 Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 23 Jun 2023 22:00:50 +0100 Subject: [PATCH 14/62] Remove GS_ENDING A whole bunch of extremely specific SRB2 material --- src/d_main.c | 6 - src/deh_tables.c | 1 - src/f_finale.c | 521 ----------------------------------------------- src/f_finale.h | 5 - src/f_wipe.c | 10 - src/g_game.c | 12 +- src/g_state.h | 1 - 7 files changed, 1 insertion(+), 555 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index 7439b612a..3df9d455f 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -464,12 +464,6 @@ static void D_Display(void) } break; - case GS_ENDING: - F_EndingDrawer(); - HU_Erase(); - HU_Drawer(); - break; - case GS_CUTSCENE: F_CutsceneDrawer(); HU_Erase(); diff --git a/src/deh_tables.c b/src/deh_tables.c index 1435dc5d4..3c11a3aa2 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -6836,7 +6836,6 @@ struct int_const_s const INT_CONST[] = { {"GS_CREDITS",GS_CREDITS}, {"GS_EVALUATION",GS_EVALUATION}, {"GS_INTRO",GS_INTRO}, - {"GS_ENDING",GS_ENDING}, {"GS_CUTSCENE",GS_CUTSCENE}, {"GS_DEDICATEDSERVER",GS_DEDICATEDSERVER}, {"GS_WAITINGPLAYERS",GS_WAITINGPLAYERS}, diff --git a/src/f_finale.c b/src/f_finale.c index f6bff28fb..277f8b5e6 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -130,14 +130,6 @@ static patch_t *ttuser[TTMAX_USER]; static INT32 ttuser_count = 0; static boolean goodending; -static patch_t *endbrdr[2]; // border - blue, white, pink - where have i seen those colours before? -static patch_t *endbgsp[3]; // nebula, sun, planet -static patch_t *endegrk[2]; // eggrock - replaced midway through good ending -static patch_t *endfwrk[3]; // firework - replaced with skin when good ending -static patch_t *endspkl[3]; // sparkle -static patch_t *endglow[2]; // glow aura - replaced with black rock's midway through good ending -static patch_t *endxpld[4]; // mini explosion -static patch_t *endescp[5]; // escape pod + flame static INT32 sparkloffs[3][2]; // eggrock explosions/blackrock sparkles static INT32 sparklloop; @@ -1107,519 +1099,6 @@ void F_GameEvaluationTicker(void) #undef SPARKLLOOPTIME -// ========== -// ENDING -// ========== - -#define INFLECTIONPOINT (6*TICRATE) -#define STOPPINGPOINT (14*TICRATE) -#define SPARKLLOOPTIME 15 // must be odd - -static void F_CacheEnding(void) -{ - endbrdr[1] = W_CachePatchName("ENDBRDR1", PU_PATCH_LOWPRIORITY); - - endegrk[0] = W_CachePatchName("ENDEGRK0", PU_PATCH_LOWPRIORITY); - endegrk[1] = W_CachePatchName("ENDEGRK1", PU_PATCH_LOWPRIORITY); - - endglow[0] = W_CachePatchName("ENDGLOW0", PU_PATCH_LOWPRIORITY); - endglow[1] = W_CachePatchName("ENDGLOW1", PU_PATCH_LOWPRIORITY); - - endbgsp[0] = W_CachePatchName("ENDBGSP0", PU_PATCH_LOWPRIORITY); - endbgsp[1] = W_CachePatchName("ENDBGSP1", PU_PATCH_LOWPRIORITY); - endbgsp[2] = W_CachePatchName("ENDBGSP2", PU_PATCH_LOWPRIORITY); - - endspkl[0] = W_CachePatchName("ENDSPKL0", PU_PATCH_LOWPRIORITY); - endspkl[1] = W_CachePatchName("ENDSPKL1", PU_PATCH_LOWPRIORITY); - endspkl[2] = W_CachePatchName("ENDSPKL2", PU_PATCH_LOWPRIORITY); - - endxpld[0] = W_CachePatchName("ENDXPLD0", PU_PATCH_LOWPRIORITY); - endxpld[1] = W_CachePatchName("ENDXPLD1", PU_PATCH_LOWPRIORITY); - endxpld[2] = W_CachePatchName("ENDXPLD2", PU_PATCH_LOWPRIORITY); - endxpld[3] = W_CachePatchName("ENDXPLD3", PU_PATCH_LOWPRIORITY); - - endescp[0] = W_CachePatchName("ENDESCP0", PU_PATCH_LOWPRIORITY); - endescp[1] = W_CachePatchName("ENDESCP1", PU_PATCH_LOWPRIORITY); - endescp[2] = W_CachePatchName("ENDESCP2", PU_PATCH_LOWPRIORITY); - endescp[3] = W_CachePatchName("ENDESCP3", PU_PATCH_LOWPRIORITY); - endescp[4] = W_CachePatchName("ENDESCP4", PU_PATCH_LOWPRIORITY); - - // so we only need to check once - if ((goodending = ALLCHAOSEMERALDS(emeralds))) - { - endfwrk[0] = W_CachePatchName("ENDFWRK3", PU_PATCH); - endfwrk[1] = W_CachePatchName("ENDFWRK4", PU_PATCH); - endfwrk[2] = W_CachePatchName("ENDFWRK5", PU_PATCH); - - endbrdr[0] = W_CachePatchName("ENDBRDR2", PU_PATCH_LOWPRIORITY); - } - else - { - // eggman, skin nonspecific - endfwrk[0] = W_CachePatchName("ENDFWRK0", PU_PATCH_LOWPRIORITY); - endfwrk[1] = W_CachePatchName("ENDFWRK1", PU_PATCH_LOWPRIORITY); - endfwrk[2] = W_CachePatchName("ENDFWRK2", PU_PATCH_LOWPRIORITY); - - endbrdr[0] = W_CachePatchName("ENDBRDR0", PU_PATCH_LOWPRIORITY); - } -} - -static void F_CacheGoodEnding(void) -{ - endegrk[0] = W_CachePatchName("ENDEGRK2", PU_PATCH_LOWPRIORITY); - endegrk[1] = W_CachePatchName("ENDEGRK3", PU_PATCH_LOWPRIORITY); - - endglow[0] = W_CachePatchName("ENDGLOW2", PU_PATCH_LOWPRIORITY); - endglow[1] = W_CachePatchName("ENDGLOW3", PU_PATCH_LOWPRIORITY); - - endxpld[0] = W_CachePatchName("ENDEGRK4", PU_PATCH_LOWPRIORITY); -} - -void F_StartEnding(void) -{ - G_SetGamestate(GS_ENDING); - wipetypepost = INT16_MAX; - - // Just in case they're open ... somehow - M_ClearMenus(true); - - gameaction = ga_nothing; - paused = false; - CON_ToggleOff(); - S_StopMusic(); // todo: placeholder - S_StopSounds(); - - finalecount = -10; // what? this totally isn't a hack. why are you asking? - - memset(sparkloffs, 0, sizeof(INT32)*3*2); - sparklloop = 0; - - F_CacheEnding(); -} - -void F_EndingTicker(void) -{ - if (++finalecount > STOPPINGPOINT) - { - F_StartCredits(); - wipetypepre = INT16_MAX; - return; - } - - if (finalecount == -8) - S_ChangeMusicInternal((goodending ? "_endg" : "_endb"), false); - - if (goodending && finalecount == INFLECTIONPOINT) // time to swap some assets - F_CacheGoodEnding(); - - if (++sparklloop == SPARKLLOOPTIME) // time to roll the randomisation again - { - angle_t workingangle = FixedAngle((M_RandomRange(-170, 80))<>ANGLETOFINESHIFT; - fixed_t workingradius = M_RandomKey(26); - - sparkloffs[0][0] = (30< -19) - { - INT32 trans = (-parallaxticker)>>1; - if (trans < 0) - trans = 0; - V_DrawFixedPatch((200< 0) // gunchedrock - { - INT32 scale = FRACUNIT + ((parallaxticker-10)<<7); - INT32 trans = parallaxticker>>2; - UINT8 *colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_JET, GTC_CACHE); - - if (parallaxticker < 10) - { - tweakx = parallaxticker< 0) - { - i -= (3+(tweakx<<1)); - j += tweaky<<2; - } - - if (parallaxticker <= 70) // eggrock/blackrock - { - INT32 trans; - fixed_t scale = FRACUNIT; - UINT8 *colormap[2] = {NULL, NULL}; - - x += i; - y += j; - - if (parallaxticker > 66) - { - scale = ((70 - parallaxticker)<<(FRACBITS-2)); - x += (30*(FRACUNIT-scale)); - y += (30*(FRACUNIT-scale)); - } - else if ((parallaxticker > 60) || (goodending && parallaxticker > 0)) - ; - else - { - doexplosions = true; - if (!sparklloop) - { - x += ((sparkloffs[0][0] < 30< INFLECTIONPOINT) - parallaxticker -= 40; - - if ((-parallaxticker/4) < 5) - { - trans = (-parallaxticker/4) + 5; - if (trans < 0) - trans = 0; - V_DrawFixedPatch(x, y, scale, trans< INFLECTIONPOINT) - { - if (finalecount < INFLECTIONPOINT+10) - V_DrawFadeFill(24, 24, BASEVIDWIDTH-48, BASEVIDHEIGHT-48, 0, 0, INFLECTIONPOINT+10-finalecount); - parallaxticker -= 30; - } - - if ((parallaxticker/2) > -15) - colormap[0] = R_GetTranslationColormap(TC_ALLWHITE, 0, GTC_CACHE); - V_DrawFixedPatch(x, y, scale, 0, rockpat, colormap[0]); - if ((parallaxticker/2) > -25) - { - trans = (parallaxticker/2) + 15; - if (trans < 0) - trans = -trans; - if (trans < 10) - V_DrawFixedPatch(x, y, scale, trans< INFLECTIONPOINT) - { - if (finalecount < INFLECTIONPOINT+10) - V_DrawFixedPatch(x, y, scale, (finalecount-INFLECTIONPOINT)<= 3 && doexplosions) - { - INT32 boomtime = parallaxticker - sparklloop; - - x = ((((BASEVIDWIDTH-82)/2)+11)< INFLECTIONPOINT && finalecount < INFLECTIONPOINT+10) - V_DrawScaledPatch(BASEVIDWIDTH/2, BASEVIDHEIGHT/2, (finalecount-INFLECTIONPOINT)<= TICRATE && finalecount < INFLECTIONPOINT) - { - INT32 workingtime = finalecount - TICRATE; - fixed_t radius = ((vid.width/vid.dupx)*(INFLECTIONPOINT - TICRATE - workingtime))/(INFLECTIONPOINT - TICRATE); - angle_t fa; - INT32 eemeralds_cur[4]; - char patchname[7] = "CEMGx0"; - - radius <<= FRACBITS; - - for (i = 0; i < 4; ++i) - { - if (i == 1) - workingtime -= sparklloop; - else if (i) - workingtime -= SPARKLLOOPTIME; - eemeralds_cur[i] = (workingtime % 360)<>ANGLETOFINESHIFT) & FINEMASK; - x = (BASEVIDWIDTH<<(FRACBITS-1)) + FixedMul(FINECOSINE(fa),radius); - y = (BASEVIDHEIGHT<<(FRACBITS-1)) + FixedMul(FINESINE(fa),radius); - eemeralds_cur[j] += (360<>ANGLETOFINESHIFT) & FINEMASK; - x = (BASEVIDWIDTH<<(FRACBITS-1)) + FixedMul(FINECOSINE(fa),radius); - y = ((BASEVIDHEIGHT+16)<<(FRACBITS-1)) + FixedMul(FINESINE(fa),radius); - eemeralds_cur[0] += (360< 20) - - // look, i make an ending for you last-minute, the least you could do is let me have this - if (cv_soundtest.value == 413) - { - INT32 trans = 0; - boolean donttouch = false; - const char *str; - if (goodending) - str = va("[S] %s: Engage.", skins[players[consoleplayer].skin].realname); - else - str = "[S] Eggman: Abscond."; - - if (finalecount < 10) - trans = (10-finalecount)/2; - else if (finalecount > STOPPINGPOINT - 20) - { - trans = 10 + (finalecount - STOPPINGPOINT)/2; - donttouch = true; - } - - if (trans < 10) - { - //colset(linkmap, 164, 165, 169); -- the ideal purple colour to represent a clicked in-game link, but not worth it just for a soundtest-controlled secret - V_DrawCenteredString(BASEVIDWIDTH/2, 8, V_ALLOWLOWERCASE|(trans<'|(trans<timesBeaten || finalecount >= STOPPINGPOINT-TICRATE) ? V_PURPLEMAP : V_BLUEMAP)|(trans<"); - } - - if (finalecount > STOPPINGPOINT-(20+(2*TICRATE))) - { - INT32 trans2 = abs((5*FINECOSINE((FixedAngle((finalecount*5)<>ANGLETOFINESHIFT & FINEMASK)))>>FRACBITS)+2; - if (!donttouch) - { - trans = 10 + (STOPPINGPOINT-(20+(2*TICRATE))) - finalecount; - if (trans > trans2) - trans2 = trans; - } - else - trans2 += 2*trans; - if (trans2 < 10) - V_DrawCharacter(26, BASEVIDHEIGHT-33, '\x1C'|(trans2< Date: Fri, 23 Jun 2023 23:13:25 +0100 Subject: [PATCH 15/62] Ring Racers-specific Evaluation - Four evaluation modes. - Perfect - Currently no visual implementation - All others have a cool set of visuals - Multi-stage animation of a glowing threat and a Star that's Sealed - If they're relevant, show the gems you HAVEN'T grabbed - Three modes here - No gems - For Easy mode, asks you to brave a higher difficulty - Chaos Emeralds - Not all 7 chaos emeralds? Push your rank harder! - Super Emeralds - Not all 7 super emeralds? Further challenge awaits! - `useBlackRock` to make evaluation context less specific for custom material is replaced with `useSeal` option - M_CheckCupEmeralds(difficulty) - Returns the Emeralds you have for that difficulty - Obviously returns 0 for Easy - Makes the method of checking collected Emeralds for cup contexts significantly easier --- src/deh_soc.c | 4 +- src/doomstat.h | 2 +- src/f_finale.c | 333 +++++++++++++++++++++++++++++++++++++------------ src/g_game.c | 2 +- src/m_cond.c | 45 ++++--- src/m_cond.h | 1 + 6 files changed, 281 insertions(+), 106 deletions(-) diff --git a/src/deh_soc.c b/src/deh_soc.c index 08995d083..860d6ad0d 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -3120,9 +3120,9 @@ void readmaincfg(MYFILE *f, boolean mainfile) if (creditscutscene > 128) creditscutscene = 128; } - else if (fastcmp(word, "USEBLACKROCK")) + else if (fastcmp(word, "USESEAL")) { - useBlackRock = (UINT8)(value || word2[0] == 'T' || word2[0] == 'Y'); + useSeal = (UINT8)(value || word2[0] == 'T' || word2[0] == 'Y'); } else if (fastcmp(word, "LOOPTITLE")) { diff --git a/src/doomstat.h b/src/doomstat.h index 70d583f66..00b8c9cf7 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -748,7 +748,7 @@ extern INT32 flameseg; extern UINT8 introtoplay; extern UINT8 creditscutscene; -extern UINT8 useBlackRock; +extern UINT8 useSeal; extern UINT8 use1upSound; extern UINT8 maxXtraLife; // Max extra lives from rings diff --git a/src/f_finale.c b/src/f_finale.c index 277f8b5e6..b4845cad6 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -129,10 +129,6 @@ static UINT8 *waitcolormap; // colormap for the spinning character static patch_t *ttuser[TTMAX_USER]; static INT32 ttuser_count = 0; -static boolean goodending; -static INT32 sparkloffs[3][2]; // eggrock explosions/blackrock sparkles -static INT32 sparklloop; - // // PROMPT STATE // @@ -889,8 +885,26 @@ boolean F_CreditResponder(event_t *event) // EVALUATION // ============ +#if 0 + +static INT32 sparkloffs[3][2]; // eggrock explosions/blackrock sparkles +static INT32 sparklloop; + #define SPARKLLOOPTIME 7 // must be odd +#endif + +typedef enum +{ + EVAL_NOTHING, + EVAL_CHAOS, + EVAL_SUPER, + EVAL_PERFECT +} evaluationtype_t; + +static evaluationtype_t evaluationtype; +UINT16 finaleemeralds = 0; + void F_StartGameEvaluation(void) { // Credits option in extras menu @@ -902,20 +916,39 @@ void F_StartGameEvaluation(void) } S_FadeOutStopMusic(5*MUSICRATE); + S_StopMusicCredit(); G_SetGamestate(GS_EVALUATION); // Just in case they're open ... somehow M_ClearMenus(true); - goodending = (ALLCHAOSEMERALDS(emeralds)); + UINT8 difficulty = KARTSPEED_NORMAL; + if (grandprixinfo.gp == true) + { + if (grandprixinfo.masterbots == true) + difficulty = KARTGP_MASTER; + else + difficulty = grandprixinfo.gamespeed; + } + + finaleemeralds = M_CheckCupEmeralds(difficulty); + + if (difficulty == KARTSPEED_EASY) + evaluationtype = EVAL_NOTHING; + else if (!ALLCHAOSEMERALDS(finaleemeralds)) + evaluationtype = EVAL_CHAOS; + else if (!ALLSUPEREMERALDS(finaleemeralds)) + evaluationtype = EVAL_SUPER; + else + evaluationtype = EVAL_PERFECT; gameaction = ga_nothing; paused = false; CON_ToggleOff(); finalecount = -1; - sparklloop = 0; + //sparklloop = 0; } void F_GameEvaluationDrawer(void) @@ -923,68 +956,155 @@ void F_GameEvaluationDrawer(void) INT32 x, y, i; angle_t fa; INT32 eemeralds_cur; - char patchname[7] = "CEMGx0"; - const char* endingtext; + const char *endingtext = NULL, *rankharder = NULL; if (marathonmode) + { endingtext = "THANKS FOR THE RUN!"; - else if (goodending) - endingtext = "CONGRATULATIONS!"; - else + } + else switch (evaluationtype) + { + case EVAL_PERFECT: + endingtext = "CONGRATULATIONS!"; + break; + case EVAL_SUPER: + rankharder = "Further challenge awaits!"; + break; + default: + rankharder = "...push your rank harder"; + break; + case EVAL_NOTHING: + rankharder = "Brave a higher difficulty"; + break; + } + + if (endingtext == NULL) endingtext = "TRY AGAIN..."; + if (usedCheats) + rankharder = "Cheated games can't unlock extras!"; + V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); + const INT32 gainaxtime = ((3*TICRATE)/2) - finalecount; + const INT32 sealtime = finalecount - (4*TICRATE); + INT32 crossfade = 0; + // Draw all the good crap here. - if (finalecount > 0 && useBlackRock) + x = BASEVIDWIDTH<<(FRACBITS-1); + y = (BASEVIDHEIGHT + 16)<<(FRACBITS-1); + + if (useSeal && evaluationtype != EVAL_PERFECT) { - INT32 scale = FRACUNIT; - patch_t *rockpat; - UINT8 *colormap[2] = {NULL, NULL}; - patch_t *glow; - INT32 trans = 0; + patch_t *sealpat; - x = (((BASEVIDWIDTH-82)/2)+11)< 0) { - scale = (finalecount<<(FRACBITS-2)); - x += (30*(FRACUNIT-scale)); - y += (30*(FRACUNIT-scale)); + // Stage 1 - blank + sealpat = W_CachePatchName( + "K_FINB01", + PU_PATCH_LOWPRIORITY + ); } - - if (goodending) + else if (sealtime < 0) { - rockpat = W_CachePatchName(va("ROID00%.2d", 34 - (finalecount % 35)), PU_PATCH_LOWPRIORITY); - glow = W_CachePatchName(va("ENDGLOW%.1d", 2+(finalecount & 1)), PU_PATCH_LOWPRIORITY); - x -= FRACUNIT; + // Stage 2 - Catcher Glow + sealpat = W_CachePatchName( + va("K_FINB0%u", 2+(finalecount & 1)), + PU_PATCH_LOWPRIORITY + ); + + crossfade = 10 + sealtime/3; } else { - rockpat = W_CachePatchName("ROID0000", PU_PATCH_LOWPRIORITY); - glow = W_CachePatchName(va("ENDGLOW%.1d", (finalecount & 1)), PU_PATCH_LOWPRIORITY); + // Stage 3 - Star Within The Seal + sealpat = W_CachePatchName( + "K_FINB05", + PU_PATCH_LOWPRIORITY + ); + +#define SEAL_PULSELEN (TICRATE) + crossfade = (sealtime % (2*SEAL_PULSELEN)) - SEAL_PULSELEN; + if (crossfade < 0) + crossfade = -crossfade; + crossfade = (crossfade * 10)/SEAL_PULSELEN; +#undef SEAL_PULSELEN } - if (finalecount >= 5) - trans = (finalecount-5)>>1; - if (trans < 10) - V_DrawFixedPatch(x, y, scale, trans< 0) + { + sealpat = W_CachePatchName( + "K_FINB04", + PU_PATCH_LOWPRIORITY + ); + + V_DrawFixedPatch( + x, y, + FRACUNIT, + (10-crossfade)<numframes - 2); + + if (refframes < 0) + ; // Not enough sprites + else if (gainaxtime <= refframes) + { + // Animation in progress! + + INT32 gainaxframe; + if (gainaxtime <= 0) + { + // Flicker + gainaxframe = refframes + (finalecount & 1); + } + else + { + // Shwing in + gainaxframe = (sprdef->numframes - 2) - gainaxtime; + } + + spriteframe_t *sprframe = &sprdef->spriteframes[gainaxframe]; + + if (sprframe->lumppat[0] != LUMPERROR) + { + V_DrawFixedPatch( + x, (y - (20*FRACUNIT)), + FRACUNIT/2, + V_ADD + |(crossfade<flip & 1) ? V_FLIP : 0), + W_CachePatchNum(sprframe->lumppat[0], PU_CACHE), + NULL + ); + } + } + } + +#if 0 + if (evaluationtype == EVAL_PERFECT) { INT32 j = (sparklloop & 1) ? 2 : 3; if (j > (finalecount/SPARKLLOOPTIME)) @@ -1003,33 +1123,94 @@ void F_GameEvaluationDrawer(void) j--; } } +#endif + } + + if ((evaluationtype == EVAL_CHAOS || evaluationtype == EVAL_SUPER) + && finalecount > 0) + { + INT32 gemtrans; + + if (useSeal && sealtime > 0) + { + // Stage 3 - aggressive overexposure + gemtrans = 3 + ((10 - crossfade)/3); + } + else if (useSeal && crossfade > 0) + { + // Stage 2 - some overexposure + gemtrans = (crossfade/3); + } else { - patch_t *eggrock = W_CachePatchName("ENDEGRK5", PU_PATCH_LOWPRIORITY); - V_DrawFixedPatch(x, y, scale, 0, eggrock, colormap[0]); - if (trans < 10) - V_DrawFixedPatch(x, y, scale, trans<>ANGLETOFINESHIFT) & FINEMASK; + + V_DrawFixedPatch( + x + (75*FINECOSINE(fa)), y + (75*FINESINE(fa)), + FRACUNIT, + gemtrans, + empat, + R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_CHAOSEMERALD1+(i-basegem), GTC_CACHE) + ); } } - eemeralds_cur = (finalecount % 360)<>ANGLETOFINESHIFT) & FINEMASK; - x = (BASEVIDWIDTH<<(FRACBITS-1)) + (60*FINECOSINE(fa)); - y = ((BASEVIDHEIGHT+16)<<(FRACBITS-1)) + (60*FINESINE(fa)); - eemeralds_cur += (360<= KARTGP_MAX) + difficulty = KARTGP_MASTER; + + cupheader_t *cup; + UINT16 ret = 0; + + for (cup = kartcupheaders; cup; cup = cup->next) + { + if (cup->emeraldnum == 0) + continue; + + if (cup->windata[difficulty].got_emerald == false) + continue; + + ret |= 1<<(cup->emeraldnum-1); + } + + return ret; +} + // See also M_GetConditionString boolean M_CheckCondition(condition_t *cn, player_t *player) { @@ -791,30 +816,12 @@ boolean M_CheckCondition(condition_t *cn, player_t *player) case UC_ALLSUPER: case UC_ALLEMERALDS: { - cupheader_t *cup; UINT16 ret = 0; - UINT8 i; if (gamestate == GS_LEVEL) return false; // this one could be laggy with many cups available - for (cup = kartcupheaders; cup; cup = cup->next) - { - if (cup->emeraldnum == 0) - continue; - - i = cn->requirement; - for (i = cn->requirement; i < KARTGP_MAX; i++) - { - if (cup->windata[i].got_emerald == true) - break; - } - - if (i == KARTGP_MAX) - continue; - - ret |= 1<<(cup->emeraldnum-1); - } + ret = M_CheckCupEmeralds(cn->requirement); if (cn->type == UC_ALLCHAOS) return ALLCHAOSEMERALDS(ret); diff --git a/src/m_cond.h b/src/m_cond.h index beb12d039..83207aad0 100644 --- a/src/m_cond.h +++ b/src/m_cond.h @@ -340,6 +340,7 @@ void M_ClearSecrets(void); void M_ClearStats(void); boolean M_NotFreePlay(player_t *player); +UINT16 M_CheckCupEmeralds(UINT8 difficulty); // Updating conditions and unlockables boolean M_ConditionInterpret(const char *password); From 45f8f1c89fb3190d186bc7a817cd9218a6e03a58 Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 23 Jun 2023 23:16:09 +0100 Subject: [PATCH 16/62] Remove a bunch of unused emerald stuff inherited from SRB2 All superseded by M_CheckCupEmeralds + GTR_POWERSTONES stuff --- src/d_main.c | 1 - src/d_netcmd.c | 1 - src/doomstat.h | 2 -- src/g_game.c | 3 --- src/lua_script.c | 2 -- src/m_cheat.c | 23 ----------------------- src/menus/play-local-race-time-attack.c | 1 - src/p_mobj.h | 2 -- src/p_saveg.c | 2 -- src/p_setup.c | 4 ---- src/p_user.c | 1 - 11 files changed, 42 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index 3df9d455f..dfacb1606 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -970,7 +970,6 @@ void D_ClearState(void) SplitScreen_OnChange(); cht_debug = 0; - emeralds = 0; memset(&luabanks, 0, sizeof(luabanks)); // In case someone exits out at the same time they start a time attack run, diff --git a/src/d_netcmd.c b/src/d_netcmd.c index f391e83c1..d67f09325 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -6254,7 +6254,6 @@ void Command_ExitGame_f(void) SplitScreen_OnChange(); cht_debug = 0; - emeralds = 0; memset(&luabanks, 0, sizeof(luabanks)); if (dirmenu) diff --git a/src/doomstat.h b/src/doomstat.h index 00b8c9cf7..2fc4a3878 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -691,8 +691,6 @@ typedef enum EMERALD_ALL = EMERALD_ALLCHAOS|EMERALD_ALLSUPER } emeraldflags_t; -extern UINT16 emeralds; - #define ALLCHAOSEMERALDS(v) ((v & EMERALD_ALLCHAOS) == EMERALD_ALLCHAOS) #define ALLSUPEREMERALDS(v) ((v & EMERALD_ALLSUPER) == EMERALD_ALLSUPER) #define ALLEMERALDS(v) ((v & EMERALD_ALL) == EMERALD_ALL) diff --git a/src/g_game.c b/src/g_game.c index 491a14f02..b2081eacd 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -140,8 +140,6 @@ boolean usedCheats = false; // Set when a "cheats on" is ever used. UINT8 paused; UINT8 modeattacking = ATTACKING_NONE; boolean imcontinuing = false; -boolean runemeraldmanager = false; -UINT16 emeraldspawndelay = 60*TICRATE; // menu demo things UINT8 numDemos = 0; @@ -205,7 +203,6 @@ static boolean retryingmodeattack = false; UINT8 stagefailed; // Used for GEMS BONUS? Also to see if you beat the stage. -UINT16 emeralds; INT32 luabanks[NUM_LUABANKS]; // Temporary holding place for nights data for the current map diff --git a/src/lua_script.c b/src/lua_script.c index 4da776e55..ee0a5f5ba 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -394,8 +394,6 @@ int LUA_WriteGlobals(lua_State *L, const char *word) skincolor_redring = (UINT16)luaL_checkinteger(L, 2); else if (fastcmp(word, "skincolor_bluering")) skincolor_bluering = (UINT16)luaL_checkinteger(L, 2); - else if (fastcmp(word, "emeralds")) - emeralds = (UINT16)luaL_checkinteger(L, 2); else if (fastcmp(word, "gravity")) gravity = (fixed_t)luaL_checkinteger(L, 2); else if (fastcmp(word, "stoppedclock")) diff --git a/src/m_cheat.c b/src/m_cheat.c index 1f81c370f..17e85e775 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -469,29 +469,6 @@ void Command_Savecheckpoint_f(void) } } -// Like M_GetAllEmeralds() but for console devmode junkies. -/* -void Command_Getallemeralds_f(void) -{ - REQUIRE_CHEATS; - REQUIRE_SINGLEPLAYER; - - emeralds = EMERALD_ALL; - - CONS_Printf(M_GetText("You now have all 7 emeralds.\n")); -} - -void Command_Resetemeralds_f(void) -{ - REQUIRE_CHEATS; - REQUIRE_SINGLEPLAYER; - - emeralds = 0; - - CONS_Printf(M_GetText("Emeralds reset to zero.\n")); -} -*/ - // // Devmode // diff --git a/src/menus/play-local-race-time-attack.c b/src/menus/play-local-race-time-attack.c index ed5990587..701ec94c4 100644 --- a/src/menus/play-local-race-time-attack.c +++ b/src/menus/play-local-race-time-attack.c @@ -485,7 +485,6 @@ void M_StartTimeAttack(INT32 choice) // Still need to reset devmode cht_debug = 0; - emeralds = 0; if (demo.playback) G_StopDemo(); diff --git a/src/p_mobj.h b/src/p_mobj.h index df453b0c2..a531c456d 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -566,8 +566,6 @@ extern INT32 modulothing; #define MAXHUNTEMERALDS 64 extern mapthing_t *huntemeralds[MAXHUNTEMERALDS]; extern INT32 numhuntemeralds; -extern boolean runemeraldmanager; -extern UINT16 emeraldspawndelay; extern INT32 numstarposts; extern UINT16 bossdisabled; extern boolean stoppedclock; diff --git a/src/p_saveg.c b/src/p_saveg.c index f80ffff7b..1e88332e4 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -5622,7 +5622,6 @@ static void P_NetArchiveMisc(savebuffer_t *save, boolean resending) WRITESINT8(save->p, g_pickedVote); - WRITEUINT16(save->p, emeralds); { UINT8 globools = 0; if (stagefailed) @@ -5796,7 +5795,6 @@ static boolean P_NetUnArchiveMisc(savebuffer_t *save, boolean reloading) g_pickedVote = READSINT8(save->p); - emeralds = READUINT16(save->p); { UINT8 globools = READUINT8(save->p); stagefailed = !!(globools & 1); diff --git a/src/p_setup.c b/src/p_setup.c index ee07ccf14..2abe1fef7 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -7351,10 +7351,6 @@ static void P_InitLevelSettings(void) K_TimerReset(); - // special stage tokens, emeralds, and ring total - runemeraldmanager = false; - emeraldspawndelay = 60*TICRATE; - nummaprings = 0; nummapboxes = numgotboxes = 0; maptargets = numtargets = 0; diff --git a/src/p_user.c b/src/p_user.c index 938bf96e1..ed8c06116 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -342,7 +342,6 @@ void P_GiveEmerald(boolean spawnObj) UINT8 em = P_GetNextEmerald(); S_StartSound(NULL, sfx_cgot); // Got the emerald! - emeralds |= (1 << em); stagefailed = false; if (spawnObj) From 4e81f341f7865d16e67d9e96fa28b20b8f7fa55d Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 24 Jun 2023 14:58:54 +0100 Subject: [PATCH 17/62] Perfect evaluation --- src/f_finale.c | 93 +++++++++++++++++++------------------------------- 1 file changed, 35 insertions(+), 58 deletions(-) diff --git a/src/f_finale.c b/src/f_finale.c index b4845cad6..91a5d8958 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -885,15 +885,6 @@ boolean F_CreditResponder(event_t *event) // EVALUATION // ============ -#if 0 - -static INT32 sparkloffs[3][2]; // eggrock explosions/blackrock sparkles -static INT32 sparklloop; - -#define SPARKLLOOPTIME 7 // must be odd - -#endif - typedef enum { EVAL_NOTHING, @@ -948,7 +939,6 @@ void F_StartGameEvaluation(void) CON_ToggleOff(); finalecount = -1; - //sparklloop = 0; } void F_GameEvaluationDrawer(void) @@ -960,12 +950,13 @@ void F_GameEvaluationDrawer(void) if (marathonmode) { - endingtext = "THANKS FOR THE RUN!"; + endingtext = "COOL RUN!"; } else switch (evaluationtype) { case EVAL_PERFECT: - endingtext = "CONGRATULATIONS!"; + endingtext = "CONGRATULATIONS"; + rankharder = "You're too cool!"; break; case EVAL_SUPER: rankharder = "Further challenge awaits!"; @@ -995,7 +986,38 @@ void F_GameEvaluationDrawer(void) x = BASEVIDWIDTH<<(FRACBITS-1); y = (BASEVIDHEIGHT + 16)<<(FRACBITS-1); - if (useSeal && evaluationtype != EVAL_PERFECT) + if (!useSeal) + ; + else if (evaluationtype == EVAL_PERFECT) + { + // Symmetrical slow fade in and out. + if (finalecount > 5*TICRATE) + crossfade = (10*TICRATE) - finalecount; + else + crossfade = finalecount; + + crossfade = 10 - (crossfade * 10)/TICRATE; + if (crossfade < 0) + crossfade = 0; + + // Imagery of a shattered pink palanquin resting in the flowers + // (abandoned cage for a gemstone far above Earth's station) + // ~toast 240623 + if (crossfade != 10) + { + V_DrawFixedPatch( + x, y, + FRACUNIT, + crossfade< (finalecount/SPARKLLOOPTIME)) - j = (finalecount/SPARKLLOOPTIME); - while (j) - { - if (j > 1 || sparklloop >= 2) - { - // if j == 0 - alternate between 0 and 1 - // 1 - 1 and 2 - // 2 - 2 and not rendered - V_DrawFixedPatch(x+sparkloffs[j-1][0], y+sparkloffs[j-1][1], FRACUNIT, 0, - W_CachePatchName(va("ENDSPKL%.1d", (j - ((sparklloop & 1) ? 0 : 1))), PU_PATCH), - R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_AQUAMARINE, GTC_CACHE)); - } - j--; - } - } -#endif } if ((evaluationtype == EVAL_CHAOS || evaluationtype == EVAL_SUPER) @@ -1229,29 +1229,6 @@ void F_GameEvaluationTicker(void) return; } -#if 0 - if (!useSeal) - ; - else if (evaluationtype != EVAL_PERFECT) - { - ; - } - else if (++sparklloop == SPARKLLOOPTIME) // time to roll the randomisation again - { - angle_t workingangle = FixedAngle((M_RandomKey(360))<>ANGLETOFINESHIFT; - fixed_t workingradius = M_RandomKey(26); - - sparkloffs[2][0] = sparkloffs[1][0]; - sparkloffs[2][1] = sparkloffs[1][1]; - sparkloffs[1][0] = sparkloffs[0][0]; - sparkloffs[1][1] = sparkloffs[0][1]; - - sparkloffs[0][0] = (30< Date: Sat, 24 Jun 2023 15:03:17 +0100 Subject: [PATCH 18/62] In DEVELOP builds, you can force a particular evaluation by setting cv_soundtest to the numerical equivalent of the evaluationtype_t you're looking for Replaces the previously uncommited recompilation-based hacks the author of this commit was using to test the previous ones on this branch - 1 is Nothing - 2 is Chaos Emerald - 3 is Super Emerald - 4 is Perfect - 5, 6, 7, etc currently repeat --- src/f_finale.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/f_finale.c b/src/f_finale.c index 91a5d8958..f5436546e 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -890,7 +890,8 @@ typedef enum EVAL_NOTHING, EVAL_CHAOS, EVAL_SUPER, - EVAL_PERFECT + EVAL_PERFECT, + EVAL_MAX } evaluationtype_t; static evaluationtype_t evaluationtype; @@ -899,7 +900,12 @@ UINT16 finaleemeralds = 0; void F_StartGameEvaluation(void) { // Credits option in extras menu - if (grandprixinfo.gp == false) + if ( + grandprixinfo.gp == false +#ifdef DEVELOP + && cv_soundtest.value == 0 +#endif + ) { S_FadeMusic(0, MUSICRATE/4); F_StartGameEnd(); @@ -925,6 +931,11 @@ void F_StartGameEvaluation(void) finaleemeralds = M_CheckCupEmeralds(difficulty); +#ifdef DEVELOP + if (cv_soundtest.value != 0) + evaluationtype = (cv_soundtest.value-1) % EVAL_MAX; + else +#endif if (difficulty == KARTSPEED_EASY) evaluationtype = EVAL_NOTHING; else if (!ALLCHAOSEMERALDS(finaleemeralds)) From 1b03e7a75cbc8e209c7b6748baf1ee417b4003db Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 25 Jun 2023 15:05:03 +0100 Subject: [PATCH 19/62] Evaluation changes - Add music to Perfect evaluation - Thank you darling Tyron :sob: - Different evaluation durations - 18 seconds for Perfect evaluation - Extended because the music is too good - 14 seconds for all other evaluation types - Extended so it's not almost half the length of the perfect one - Timed so the Seal can glow 5 times (instead of 3) --- src/f_finale.c | 37 ++++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/src/f_finale.c b/src/f_finale.c index f5436546e..9f36b6302 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -894,11 +894,17 @@ typedef enum EVAL_MAX } evaluationtype_t; +#define EVALLEN_PERFECT (18*TICRATE) +#define EVALLEN_NORMAL (14*TICRATE) + static evaluationtype_t evaluationtype; UINT16 finaleemeralds = 0; void F_StartGameEvaluation(void) { + S_FadeMusic(0, MUSICRATE/4); + S_StopMusicCredit(); + // Credits option in extras menu if ( grandprixinfo.gp == false @@ -907,14 +913,10 @@ void F_StartGameEvaluation(void) #endif ) { - S_FadeMusic(0, MUSICRATE/4); F_StartGameEnd(); return; } - S_FadeOutStopMusic(5*MUSICRATE); - S_StopMusicCredit(); - G_SetGamestate(GS_EVALUATION); // Just in case they're open ... somehow @@ -1002,8 +1004,8 @@ void F_GameEvaluationDrawer(void) else if (evaluationtype == EVAL_PERFECT) { // Symmetrical slow fade in and out. - if (finalecount > 5*TICRATE) - crossfade = (10*TICRATE) - finalecount; + if (finalecount > EVALLEN_PERFECT/2) + crossfade = EVALLEN_PERFECT - finalecount; else crossfade = finalecount; @@ -1234,13 +1236,29 @@ void F_GameEvaluationDrawer(void) void F_GameEvaluationTicker(void) { - if (++finalecount > 10*TICRATE) + INT32 evallen = EVALLEN_NORMAL; + + if (evaluationtype == EVAL_PERFECT) + { + // tyron made something perfect and i would sooner + // smite everyone in this room starting with myself + // over the idea of cutting it ~toast 250623 + evallen = EVALLEN_PERFECT; + + if (finalecount == 1) + { + // Now start the music + S_ChangeMusicInternal("_SHORE", false); + } + } + + if (++finalecount > evallen) { F_StartGameEnd(); return; } - if (finalecount == 5*TICRATE) + if (finalecount == evallen/2) { if (!usedCheats) { @@ -1252,7 +1270,8 @@ void F_GameEvaluationTicker(void) } } -#undef SPARKLLOOPTIME +#undef EVALLEN_PERFECT +#undef EVALLEN_NORMAL // ========== // GAME END From f371a212586f2530ed893e0be827de3fc1bcf1f2 Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Sun, 25 Jun 2023 23:18:06 -0700 Subject: [PATCH 20/62] Ringbox proto functionality --- src/d_player.h | 3 +++ src/k_hud.c | 11 +++++------ src/k_kart.c | 11 +++++++++++ src/k_roulette.c | 5 ++--- src/lua_playerlib.c | 8 ++++++++ src/p_saveg.c | 6 ++++++ 6 files changed, 35 insertions(+), 9 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index 4ff0f5d15..418a08d2c 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -764,6 +764,9 @@ struct player_t boolean markedfordeath; + UINT8 ringboxdelay; // Delay until Ring Box auto-activates + UINT8 ringboxaward; // Where did we stop? + uint8_t public_key[PUBKEYLENGTH]; #ifdef HWRENDER diff --git a/src/k_hud.c b/src/k_hud.c index 3f4d56413..2e53fef83 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -1250,12 +1250,6 @@ static void K_drawKartItem(void) vector2_t rouletteCrop = {7, 7}; INT32 i; - if (stplyr->itemRoulette.ringbox) - { - // Todo: owl - localbg = offset ? kp_ringbg[1] : kp_ringbg[0]; - } - if (stplyr->itemRoulette.itemListLen > 0) { // Init with item roulette stuff. @@ -1617,6 +1611,11 @@ static void K_drawKartSlotMachine(void) { rouletteOffset = K_GetRouletteOffset(&stplyr->itemRoulette, rendertimefrac); } + else + { + if (!stplyr->ringboxdelay) + return; + } // pain and suffering defined below if (offset) diff --git a/src/k_kart.c b/src/k_kart.c index 33fcd8819..1a02ad382 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -10732,6 +10732,17 @@ void K_MoveKartPlayer(player_t *player, boolean onground) player->pflags &= ~PF_USERINGS; } + if (player->ringboxdelay) + { + player->ringboxdelay--; + if (player->ringboxdelay == 0) + { + // TODO + K_AwardPlayerRings(player, 10 * player->ringboxaward, true); + player->ringboxaward = 0; + } + } + if (player && player->mo && player->mo->health > 0 && !player->spectator && !P_PlayerInPain(player) && !mapreset && leveltime > introtime) { // First, the really specific, finicky items that function without the item being directly in your item slot. diff --git a/src/k_roulette.c b/src/k_roulette.c index dc4be35df..0f722a80f 100644 --- a/src/k_roulette.c +++ b/src/k_roulette.c @@ -1603,9 +1603,8 @@ void K_KartItemRoulette(player_t *const player, ticcmd_t *const cmd) if (roulette->ringbox == true) { - // TODO: add logic to make it give you the rings - //player->slotMachineType = finalItem; - //player->slotMachineDelay = TICRATE; + player->ringboxdelay = TICRATE; + player->ringboxaward = finalItem; } else { diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 9ea6e7399..282279a71 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -251,6 +251,10 @@ static int player_get(lua_State *L) lua_pushboolean(L, plr->flipDI); else if (fastcmp(field,"markedfordeath")) lua_pushboolean(L, plr->markedfordeath); + else if (fastcmp(field,"ringboxdelay")) + lua_pushinteger(L, plr->ringboxdelay); + else if (fastcmp(field,"ringboxaward")) + lua_pushinteger(L, plr->ringboxaward); else if (fastcmp(field,"drift")) lua_pushinteger(L, plr->drift); else if (fastcmp(field,"driftcharge")) @@ -651,6 +655,10 @@ static int player_set(lua_State *L) plr->flipDI = luaL_checkboolean(L, 3); else if (fastcmp(field,"markedfordeath")) plr->markedfordeath = luaL_checkboolean(L, 3); + else if (fastcmp(field,"ringboxdelay")) + plr->ringboxdelay = luaL_checkinteger(L, 3); + else if (fastcmp(field,"ringboxaward")) + plr->ringboxaward = luaL_checkinteger(L, 3); else if (fastcmp(field,"drift")) plr->drift = luaL_checkinteger(L, 3); else if (fastcmp(field,"driftcharge")) diff --git a/src/p_saveg.c b/src/p_saveg.c index ba096a480..a69e75128 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -433,6 +433,9 @@ static void P_NetArchivePlayers(savebuffer_t *save) WRITEUINT8(save->p, players[i].markedfordeath); + WRITEUINT8(save->p, players[i].ringboxdelay); + WRITEUINT8(save->p, players[i].ringboxaward); + // respawnvars_t WRITEUINT8(save->p, players[i].respawn.state); WRITEUINT32(save->p, K_GetWaypointHeapIndex(players[i].respawn.wp)); @@ -828,6 +831,9 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) players[i].markedfordeath = READUINT8(save->p); + players[i].ringboxdelay = READUINT8(save->p); + players[i].ringboxaward = READUINT8(save->p); + // respawnvars_t players[i].respawn.state = READUINT8(save->p); players[i].respawn.wp = (waypoint_t *)(size_t)READUINT32(save->p); From 8cd3305a34e9374e26e056d4012d97cbeea43b2a Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 26 Jun 2023 12:56:01 +0100 Subject: [PATCH 21/62] Cleaner Super Emerald drawing --- src/f_finale.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/f_finale.c b/src/f_finale.c index 9f36b6302..9899033cd 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -1174,9 +1174,9 @@ void F_GameEvaluationDrawer(void) UINT8 basegem = (evaluationtype == EVAL_SUPER) ? 7 : 0; - for (i = basegem; i < (basegem+7); ++i, eemeralds_cur += (360<>ANGLETOFINESHIFT) & FINEMASK; @@ -1186,7 +1186,7 @@ void F_GameEvaluationDrawer(void) FRACUNIT, gemtrans, empat, - R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_CHAOSEMERALD1+(i-basegem), GTC_CACHE) + R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_CHAOSEMERALD1+i, GTC_CACHE) ); } } From 15d744c4e345f6d6969f588dc32c8809699379d0 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 26 Jun 2023 13:33:28 +0100 Subject: [PATCH 22/62] M_CheckCupEmeralds: Only count the first instance of each Emerald in the cups This prevents custom cups from being counted as completing primary Sealed Star progression unless you wipe existing ones. --- src/m_cond.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/src/m_cond.c b/src/m_cond.c index 7229007dc..a152204eb 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -747,17 +747,29 @@ UINT16 M_CheckCupEmeralds(UINT8 difficulty) difficulty = KARTGP_MASTER; cupheader_t *cup; - UINT16 ret = 0; + UINT16 ret = 0, seen = 0; for (cup = kartcupheaders; cup; cup = cup->next) { - if (cup->emeraldnum == 0) + // Does it not *have* an emerald? + if (cup->emeraldnum == 0 || cup->emeraldnum > 14) continue; + UINT16 emerald = 1<<(cup->emeraldnum-1); + + // Only count the first reference. + if (seen & emerald) + continue; + + // We've seen it, prevent future repetitions. + seen |= emerald; + + // Did you actually get it? if (cup->windata[difficulty].got_emerald == false) continue; - ret |= 1<<(cup->emeraldnum-1); + // Wa hoo ! + ret |= emerald; } return ret; From 32eead29e5587d5cfb4cf69a3ae4d8d9089c8cbe Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 26 Jun 2023 14:23:38 +0100 Subject: [PATCH 23/62] Quicker, almost instantaneous Sealed Star conclusion after death Also gets rid of the "wuu wuu wuu" sound from Competition that plays in this instance --- src/p_user.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/p_user.c b/src/p_user.c index ed8c06116..890f087d8 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1436,7 +1436,7 @@ void P_DoPlayerExit(player_t *player, pflags_t flags) { if (specialout == true) { - exitcountdown = TICRATE; + exitcountdown = 2; } else { @@ -1498,7 +1498,8 @@ void P_DoAllPlayersExit(pflags_t flags, boolean trygivelife) { UINT8 i; boolean givenlife = false; - const boolean dofinishsound = (musiccountdown == 0); + boolean dofinishsound = (musiccountdown == 0); + boolean specialout = specialstageinfo.valid; for (i = 0; i < MAXPLAYERS; i++) { @@ -1513,6 +1514,11 @@ void P_DoAllPlayersExit(pflags_t flags, boolean trygivelife) P_DoPlayerExit(&players[i], flags); + if (specialout && K_IsPlayerLosing(&players[i]) == false) + { + specialout = false; + } + if (trygivelife == false) { continue; @@ -1522,7 +1528,7 @@ void P_DoAllPlayersExit(pflags_t flags, boolean trygivelife) givenlife = true; } - if (!dofinishsound) + if (!dofinishsound || specialout) { // You've already finished, don't play again ; From 45547607bd33427230943185ae862d12707ab539 Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Mon, 26 Jun 2023 06:36:37 -0700 Subject: [PATCH 24/62] Ringbox: item respawn refactor + box visuals --- src/deh_tables.c | 17 ++++++++-- src/info.c | 22 ++++++++++--- src/info.h | 19 ++++++++--- src/k_kart.c | 2 +- src/objects/random-item.c | 20 +++++++++++- src/p_enemy.c | 54 ------------------------------- src/p_inter.c | 67 +++++++++++++++++++++++++++++++++++++-- src/p_mobj.c | 23 +++----------- 8 files changed, 136 insertions(+), 88 deletions(-) diff --git a/src/deh_tables.c b/src/deh_tables.c index 3cce64a49..d80bcf379 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -283,7 +283,6 @@ actionpointer_t actionpointers[] = {{A_ChangeHeight}, "A_CHANGEHEIGHT"}, // SRB2Kart - {{A_ItemPop}, "A_ITEMPOP"}, {{A_JawzExplode}, "A_JAWZEXPLODE"}, {{A_SSMineSearch}, "A_SSMINESEARCH"}, {{A_SSMineExplode}, "A_SSMINEEXPLODE"}, @@ -3223,7 +3222,20 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi "S_RANDOMITEM10", "S_RANDOMITEM11", "S_RANDOMITEM12", - "S_DEADRANDOMITEM", + + // Ring Box + "S_RINGBOX1", + "S_RINGBOX2", + "S_RINGBOX3", + "S_RINGBOX4", + "S_RINGBOX5", + "S_RINGBOX6", + "S_RINGBOX7", + "S_RINGBOX8", + "S_RINGBOX9", + "S_RINGBOX10", + "S_RINGBOX11", + "S_RINGBOX12", // Sphere Box (for Battle) "S_SPHEREBOX1", @@ -3238,7 +3250,6 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi "S_SPHEREBOX10", "S_SPHEREBOX11", "S_SPHEREBOX12", - "S_DEADSPHEREBOX", // Random Item Pop "S_RANDOMITEMPOP1", diff --git a/src/info.c b/src/info.c index 05ce6aa36..0dc1b9c81 100644 --- a/src/info.c +++ b/src/info.c @@ -531,6 +531,7 @@ char sprnames[NUMSPRITES + 1][5] = //SRB2kart Sprites (sort later) "RNDM", // Random Item Box "SBOX", // Sphere Box (for Battle) + "RBOX", // Ring Box "RPOP", // Random Item Box Pop "ITRI", // Item Box Debris "ITPA", // Paper item backdrop @@ -3892,7 +3893,19 @@ state_t states[NUMSTATES] = {SPR_RNDM, 18|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_RANDOMITEM11}, // S_RANDOMITEM10 {SPR_RNDM, 20|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_RANDOMITEM12}, // S_RANDOMITEM11 {SPR_RNDM, 22|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_RANDOMITEM1}, // S_RANDOMITEM12 - {SPR_NULL, 0, 0, {A_ItemPop}, 0, 0, S_RANDOMITEM1}, // S_DEADRANDOMITEM + + {SPR_RBOX, FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_RINGBOX2}, // S_RINGBOX1 + {SPR_RBOX, 2|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_RINGBOX3}, // S_RINGBOX2 + {SPR_RBOX, 4|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_RINGBOX4}, // S_RINGBOX3 + {SPR_RBOX, 6|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_RINGBOX5}, // S_RINGBOX4 + {SPR_RBOX, 8|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_RINGBOX6}, // S_RINGBOX5 + {SPR_RBOX, 10|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_RINGBOX7}, // S_RINGBOX6 + {SPR_RBOX, 12|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_RINGBOX8}, // S_RINGBOX7 + {SPR_RBOX, 14|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_RINGBOX9}, // S_RINGBOX8 + {SPR_RBOX, 16|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_RINGBOX10}, // S_RINGBOX9 + {SPR_RBOX, 18|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_RINGBOX11}, // S_RINGBOX10 + {SPR_RBOX, 20|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_RINGBOX12}, // S_RINGBOX11 + {SPR_RBOX, 22|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_RINGBOX1}, // S_RINGBOX12 {SPR_SBOX, FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX2}, // S_SPHEREBOX1 {SPR_SBOX, 2|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX3}, // S_SPHEREBOX2 @@ -3906,7 +3919,6 @@ state_t states[NUMSTATES] = {SPR_SBOX, 18|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX11}, // S_SPHEREBOX10 {SPR_SBOX, 20|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX12}, // S_SPHEREBOX11 {SPR_SBOX, 22|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX1}, // S_SPHEREBOX12 - {SPR_NULL, 0, 0, {A_ItemPop}, 1, 0, S_NULL}, // S_DEADSPHEREBOX {SPR_RPOP, FF_FULLBRIGHT, 5, {NULL}, 0, 0, S_RANDOMITEMPOP2}, // S_RANDOMITEMPOP1 {SPR_RPOP, FF_FULLBRIGHT|1, 5, {NULL}, 0, 0, S_RANDOMITEMPOP3}, // S_RANDOMITEMPOP2 @@ -22385,7 +22397,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = // SRB2kart MT's { // MT_RANDOMITEM 2000, // doomednum - S_RANDOMITEM1, // spawnstate + S_RINGBOX1, // spawnstate 1000, // spawnhealth S_NULL, // seestate sfx_None, // seesound @@ -22396,7 +22408,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate - S_DEADRANDOMITEM, // deathstate + S_NULL, // deathstate S_NULL, // xdeathstate sfx_kc2e, // deathsound 60*FRACUNIT, // speed @@ -22423,7 +22435,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate - S_DEADSPHEREBOX, // deathstate + S_NULL, // deathstate S_NULL, // xdeathstate sfx_kc2e, // deathsound 60*FRACUNIT, // speed diff --git a/src/info.h b/src/info.h index ea28b56c8..d838eb532 100644 --- a/src/info.h +++ b/src/info.h @@ -277,7 +277,6 @@ enum actionnum A_DRAGONWING, A_DRAGONSEGMENT, A_CHANGEHEIGHT, - A_ITEMPOP, A_JAWZEXPLODE, A_SSMINESEARCH, A_SSMINEEXPLODE, @@ -550,7 +549,6 @@ void A_ChangeHeight(); // // SRB2Kart // -void A_ItemPop(); void A_JawzExplode(); void A_SSMineSearch(); void A_SSMineExplode(); @@ -1084,6 +1082,7 @@ typedef enum sprite // SRB2Kart SPR_RNDM, // Random Item Box SPR_SBOX, // Sphere Box (for Battle) + SPR_RBOX, // Ring Box SPR_RPOP, // Random Item Box Pop SPR_ITRI, // Item Box Debris SPR_ITPA, // Paper item backdrop @@ -4299,7 +4298,20 @@ typedef enum state S_RANDOMITEM10, S_RANDOMITEM11, S_RANDOMITEM12, - S_DEADRANDOMITEM, + + // Ring Box + S_RINGBOX1, + S_RINGBOX2, + S_RINGBOX3, + S_RINGBOX4, + S_RINGBOX5, + S_RINGBOX6, + S_RINGBOX7, + S_RINGBOX8, + S_RINGBOX9, + S_RINGBOX10, + S_RINGBOX11, + S_RINGBOX12, // Sphere Box (for Battle) S_SPHEREBOX1, @@ -4314,7 +4326,6 @@ typedef enum state S_SPHEREBOX10, S_SPHEREBOX11, S_SPHEREBOX12, - S_DEADSPHEREBOX, // Random Item Pop S_RANDOMITEMPOP1, diff --git a/src/k_kart.c b/src/k_kart.c index 1a02ad382..763e1e14a 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -10738,7 +10738,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) if (player->ringboxdelay == 0) { // TODO - K_AwardPlayerRings(player, 10 * player->ringboxaward, true); + K_AwardPlayerRings(player, 10 * (player->ringboxaward + 1), true); player->ringboxaward = 0; } } diff --git a/src/objects/random-item.c b/src/objects/random-item.c index cae20f752..623f2ab0c 100644 --- a/src/objects/random-item.c +++ b/src/objects/random-item.c @@ -102,8 +102,26 @@ void Obj_RandomItemVisuals(mobj_t *mobj) ItemBoxBob(mobj); ItemBoxScaling(mobj); item_vfxtimer(mobj)++; - if (mobj->fuse == 0) + + // Respawn flow, documented by a dumb asshole: + // P_TouchSpecialThing -> P_ItemPop sets fuse, NOCLIPTHING and DONTDRAW. + // P_FuseThink does visual flicker, and when fuse is 0, unsets NOCLIPTHING/DONTDRAW/etc... + // ...unless it's a map-start box from Battle, in which case it does nothing and waits for + // P_RespawnBattleBoxes to trigger the effect instead, since Battle boxes don't respawn until + // the player's cleared out a good portion of the map. + // + // Then extraval1 starts ticking up and triggers the transformation from Ringbox to Random Item. + if (mobj->fuse == 0 && !(mobj->flags & MF_NOCLIPTHING)) + { mobj->extravalue1++; + if (mobj->extravalue1 == TICRATE) + { + // Sync the position in RINGBOX and RANDOMITEM animations. + statenum_t animDelta = mobj->state - states - S_RINGBOX1; + P_SetMobjState(mobj, S_RANDOMITEM1 + (animDelta%12)); + } + } + } boolean Obj_RandomItemSpawnIn(mobj_t *mobj) diff --git a/src/p_enemy.c b/src/p_enemy.c index 4553e4fe5..64a13bf09 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -312,7 +312,6 @@ void A_ChangeHeight(mobj_t *actor); // // SRB2Kart // -void A_ItemPop(mobj_t *actor); void A_JawzExplode(mobj_t *actor); void A_SSMineSearch(mobj_t *actor); void A_SSMineExplode(mobj_t *actor); @@ -12972,59 +12971,6 @@ void A_ChangeHeight(mobj_t *actor) // SRB2Kart // -void A_ItemPop(mobj_t *actor) -{ - INT32 locvar1 = var1; - - if (LUA_CallAction(A_ITEMPOP, actor)) - return; - - if (!(actor->target && actor->target->player)) - { - if (cht_debug && !(actor->target && actor->target->player)) - CONS_Printf("ERROR: Powerup has no target!\n"); - return; - } - - // de-solidify - P_UnsetThingPosition(actor); - actor->flags &= ~MF_SOLID; - actor->flags |= MF_NOCLIP; - P_SetThingPosition(actor); - - // RF_DONTDRAW will flicker as the object's fuse gets - // closer to running out (see P_FuseThink) - actor->renderflags |= RF_DONTDRAW|RF_TRANS50; - actor->color = SKINCOLOR_GREY; - actor->colorized = true; - - Obj_SpawnItemDebrisEffects(actor, actor->target); - - if (locvar1 == 1) - { - P_GivePlayerSpheres(actor->target->player, actor->extravalue1); - } - else if (locvar1 == 0) - { - if (actor->extravalue1 >= TICRATE) - K_StartItemRoulette(actor->target->player, false); - else - K_StartItemRoulette(actor->target->player, true); - } - - actor->extravalue1 = 0; - - // Here at mapload in battle? - if (!(gametyperules & GTR_CIRCUIT) && (actor->flags2 & MF2_BOSSNOTRAP)) - { - numgotboxes++; - - // do not flicker back in just yet, handled by - // P_RespawnBattleBoxes eventually - P_SetMobjState(actor, S_INVISIBLE); - } -} - void A_JawzExplode(mobj_t *actor) { INT32 shrapnel = 2; diff --git a/src/p_inter.c b/src/p_inter.c index 58dd878fc..971b08f1c 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -186,6 +186,61 @@ boolean P_EmblemWasCollected(INT32 emblemID) return gamedata->collected[emblemID]; } +static void P_ItemPop(mobj_t *actor) +{ + /* + INT32 locvar1 = var1; + + if (LUA_CallAction(A_ITEMPOP, actor)) + return; + + if (!(actor->target && actor->target->player)) + { + if (cht_debug && !(actor->target && actor->target->player)) + CONS_Printf("ERROR: Powerup has no target!\n"); + return; + } + */ + + P_SetMobjState(actor, S_RINGBOX1); + actor->extravalue1 = 0; + + // de-solidify + actor->flags |= MF_NOCLIPTHING; + + // RF_DONTDRAW will flicker as the object's fuse gets + // closer to running out (see P_FuseThink) + actor->renderflags |= RF_DONTDRAW|RF_TRANS50; + actor->color = SKINCOLOR_GREY; + actor->colorized = true; + + Obj_SpawnItemDebrisEffects(actor, actor->target); + + /* + if (locvar1 == 1) + { + P_GivePlayerSpheres(actor->target->player, actor->extravalue1); + } + else if (locvar1 == 0) + { + if (actor->extravalue1 >= TICRATE) + K_StartItemRoulette(actor->target->player, false); + else + K_StartItemRoulette(actor->target->player, true); + } + */ + + // Here at mapload in battle? + if (!(gametyperules & GTR_CIRCUIT) && (actor->flags2 & MF2_BOSSNOTRAP)) + { + numgotboxes++; + + // do not flicker back in just yet, handled by + // P_RespawnBattleBoxes eventually + P_SetMobjState(actor, S_INVISIBLE); + } +} + /** Takes action based on a ::MF_SPECIAL thing touched by a player. * Actually, this just checks a few things (heights, toucher->player, no * objectplace, no dead or disappearing things) @@ -300,7 +355,13 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) special->momx = special->momy = special->momz = 0; P_SetTarget(&special->target, toucher); - P_KillMobj(special, toucher, toucher, DMG_NORMAL); + // P_KillMobj(special, toucher, toucher, DMG_NORMAL); + if (special->extravalue1 >= TICRATE) + K_StartItemRoulette(player, false); + else + K_StartItemRoulette(player, true); + P_ItemPop(special); + special->fuse = TICRATE; return; case MT_SPHEREBOX: if (!P_CanPickupItem(player, 0)) @@ -308,7 +369,9 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) special->momx = special->momy = special->momz = 0; P_SetTarget(&special->target, toucher); - P_KillMobj(special, toucher, toucher, DMG_NORMAL); + // P_KillMobj(special, toucher, toucher, DMG_NORMAL); + P_ItemPop(special); + P_GivePlayerSpheres(player, special->extravalue1); return; case MT_ITEMCAPSULE: if (special->scale < special->extravalue1) // don't break it while it's respawning diff --git a/src/p_mobj.c b/src/p_mobj.c index 4be545796..c44411461 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -9710,7 +9710,7 @@ static boolean P_FuseThink(mobj_t *mobj) case MT_RANDOMITEM: if (mobj->flags2 & MF2_DONTRESPAWN) { - ; + P_RemoveMobj(mobj); } else if (!(gametyperules & GTR_CIRCUIT) && (mobj->state == &states[S_INVISIBLE])) { @@ -9718,23 +9718,10 @@ static boolean P_FuseThink(mobj_t *mobj) } else { - mobj_t *newmobj; - - // Respawn from mapthing if you have one! - if (mobj->spawnpoint) - { - P_SpawnMapThing(mobj->spawnpoint); - newmobj = mobj->spawnpoint->mobj; // this is set to the new mobj in P_SpawnMapThing - } - else - newmobj = P_SpawnMobj(mobj->x, mobj->y, mobj->z, mobj->type); - - // Transfer flags2 (strongbox, objectflip, bossnotrap) - newmobj->flags2 = mobj->flags2; - newmobj->extravalue1 = 0; + mobj->flags &= ~MF_NOCLIPTHING; + mobj->renderflags &= ~(RF_DONTDRAW|RF_TRANSMASK); } - P_RemoveMobj(mobj); // make sure they disappear return false; case MT_ITEMCAPSULE: if (mobj->spawnpoint) @@ -11569,11 +11556,11 @@ void P_RespawnBattleBoxes(void) if (box->type != MT_RANDOMITEM || (box->flags2 & MF2_DONTRESPAWN) - || box->health > 0 + || !(box->flags & MF_NOCLIPTHING) || box->fuse) continue; // only popped items - box->fuse = TICRATE; // flicker back in (A_ItemPop preps this effect) + box->fuse = TICRATE; // flicker back in P_SetMobjState(box, box->info->raisestate); if (numgotboxes > 0) From 35df66f2b1bc7af00df4bc7537d2e4b3931265a6 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 26 Jun 2023 15:34:48 +0100 Subject: [PATCH 25/62] Play music for normal Evaluations too Lumpname _DRIFT, implement later --- src/f_finale.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/f_finale.c b/src/f_finale.c index 9899033cd..48b95ad43 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -1247,10 +1247,18 @@ void F_GameEvaluationTicker(void) if (finalecount == 1) { - // Now start the music + // sitting on that distant _shore S_ChangeMusicInternal("_SHORE", false); } } + else + { + if (finalecount == 1) + { + // _drift across open waters + S_ChangeMusicInternal("_DRIFT", false); + } + } if (++finalecount > evallen) { From d17399bccec5848a5e852fecc589ec41a9768d89 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 26 Jun 2023 20:23:29 +0100 Subject: [PATCH 26/62] Revert "Quicker, almost instantaneous Sealed Star conclusion after death" This reverts commit 32eead29e5587d5cfb4cf69a3ae4d8d9089c8cbe. --- src/p_user.c | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/p_user.c b/src/p_user.c index 890f087d8..ed8c06116 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1436,7 +1436,7 @@ void P_DoPlayerExit(player_t *player, pflags_t flags) { if (specialout == true) { - exitcountdown = 2; + exitcountdown = TICRATE; } else { @@ -1498,8 +1498,7 @@ void P_DoAllPlayersExit(pflags_t flags, boolean trygivelife) { UINT8 i; boolean givenlife = false; - boolean dofinishsound = (musiccountdown == 0); - boolean specialout = specialstageinfo.valid; + const boolean dofinishsound = (musiccountdown == 0); for (i = 0; i < MAXPLAYERS; i++) { @@ -1514,11 +1513,6 @@ void P_DoAllPlayersExit(pflags_t flags, boolean trygivelife) P_DoPlayerExit(&players[i], flags); - if (specialout && K_IsPlayerLosing(&players[i]) == false) - { - specialout = false; - } - if (trygivelife == false) { continue; @@ -1528,7 +1522,7 @@ void P_DoAllPlayersExit(pflags_t flags, boolean trygivelife) givenlife = true; } - if (!dofinishsound || specialout) + if (!dofinishsound) { // You've already finished, don't play again ; From defd8850922c0d193e213a1827b8d6828ee81b95 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 26 Jun 2023 20:54:43 +0100 Subject: [PATCH 27/62] GP Backup tidy (connected to ending gamestate) - Handle removing GP Backups when any game end sequence is started, not just the Podium (in case no Podium exists) - Guarantee removal in M_StartCup out-of-entries failure state --- src/g_game.c | 2 ++ src/menus/transient/cup-select.c | 3 ++- src/p_setup.c | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index b2081eacd..b1f036edd 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -4651,6 +4651,8 @@ void G_EndGame(void) // Only do evaluation and credits in singleplayer contexts if (!netgame && grandprixinfo.gp == true) { + G_HandleSaveLevel(true); + if (nextmap == NEXTMAP_CEREMONY) // end game with ceremony { if (K_StartCeremony() == true) diff --git a/src/menus/transient/cup-select.c b/src/menus/transient/cup-select.c index f146cd74f..69e155592 100644 --- a/src/menus/transient/cup-select.c +++ b/src/menus/transient/cup-select.c @@ -155,7 +155,8 @@ static void M_StartCup(UINT8 entry) NULL, MM_NOTHING, NULL, NULL ); - G_HandleSaveLevel(true); + if (FIL_FileExists(gpbackup)) + remove(gpbackup); return; } diff --git a/src/p_setup.c b/src/p_setup.c index 2abe1fef7..e7c4e9fad 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -8358,7 +8358,7 @@ void P_PostLoadLevel(void) P_RunCachedActions(); - G_HandleSaveLevel(gamestate == GS_CEREMONY); + G_HandleSaveLevel(false); if (marathonmode & MA_INGAME) { From c9817b957a604c6b4a0d71dcf4faa9cdaaffbe5f Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 26 Jun 2023 22:32:47 +0100 Subject: [PATCH 28/62] New unlockable type for watching the Credits from start to finish Also makes gamedata save/load a little more forward compatible longterm by making a UINT32 bitfield for various once-event flags, with increased minor version --- src/deh_soc.c | 1 + src/f_finale.c | 5 +++++ src/g_game.c | 49 ++++++++++++++++++++++++++++++++++++++++--------- src/m_cond.c | 5 +++++ src/m_cond.h | 2 ++ 5 files changed, 53 insertions(+), 9 deletions(-) diff --git a/src/deh_soc.c b/src/deh_soc.c index 860d6ad0d..6bda19b5c 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -2583,6 +2583,7 @@ static void readcondition(UINT8 set, UINT32 id, char *word2) } } else if ((offset=0) || fastcmp(params[0], "ADDON") + || (++offset && fastcmp(params[0], "CREDITS")) || (++offset && fastcmp(params[0], "REPLAY")) || (++offset && fastcmp(params[0], "CRASH"))) { diff --git a/src/f_finale.c b/src/f_finale.c index 48b95ad43..98d0c15eb 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -808,6 +808,11 @@ void F_CreditTicker(void) { timetonext = 5*TICRATE+1; finalecount = 5*TICRATE; + + // You watched all the credits? What a trooper! + gamedata->everfinishedcredits = true; + if (M_UpdateUnlockablesAndExtraEmblems(true, true)) + G_SaveGameData(); } if (timetonext) diff --git a/src/g_game.c b/src/g_game.c index b1f036edd..6f6e3bc79 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -4725,7 +4725,15 @@ void G_LoadGameSettings(void) } #define GD_VERSIONCHECK 0xBA5ED123 // Change every major version, as usual -#define GD_VERSIONMINOR 3 // Change every format update +#define GD_VERSIONMINOR 4 // Change every format update + +typedef enum +{ + GDEVER_ADDON = 1, + GDEVER_CREDITS = 1<<1, + GDEVER_REPLAY = 1<<2, + GDEVER_SPECIAL = 1<<3, +} gdeverdone_t; static const char *G_GameDataFolder(void) { @@ -4849,9 +4857,21 @@ void G_LoadGameData(void) gamedata->chaokeys = READUINT16(save.p); - gamedata->everloadedaddon = (boolean)READUINT8(save.p); - gamedata->eversavedreplay = (boolean)READUINT8(save.p); - gamedata->everseenspecial = (boolean)READUINT8(save.p); + if (versionMinor >= 4) + { + UINT32 everflags = READUINT32(save.p); + + gamedata->everloadedaddon = !!(everflags & GDEVER_ADDON); + gamedata->everfinishedcredits = !!(everflags & GDEVER_CREDITS); + gamedata->eversavedreplay = !!(everflags & GDEVER_REPLAY); + gamedata->everseenspecial = !!(everflags & GDEVER_SPECIAL); + } + else + { + gamedata->everloadedaddon = (boolean)READUINT8(save.p); + gamedata->eversavedreplay = (boolean)READUINT8(save.p); + gamedata->everseenspecial = (boolean)READUINT8(save.p); + } } else { @@ -5278,7 +5298,7 @@ void G_SaveGameData(void) 4+4+ (4*GDGT_MAX)+ 4+1+2+2+ - 1+1+1+ + 4+ 4+ (MAXEMBLEMS+(MAXUNLOCKABLES*2)+MAXCONDITIONSETS)+ 4+2); @@ -5415,11 +5435,22 @@ void G_SaveGameData(void) WRITEUINT16(save.p, gamedata->keyspending); // 2 WRITEUINT16(save.p, gamedata->chaokeys); // 2 - WRITEUINT8(save.p, gamedata->everloadedaddon); // 1 - WRITEUINT8(save.p, gamedata->eversavedreplay); // 1 - WRITEUINT8(save.p, gamedata->everseenspecial); // 1 + { + UINT32 everflags = 0; - WRITEUINT32(save.p, quickncasehash(timeattackfolder, 64)); + if (gamedata->everloadedaddon) + everflags |= GDEVER_ADDON; + if (gamedata->everfinishedcredits) + everflags |= GDEVER_CREDITS; + if (gamedata->eversavedreplay) + everflags |= GDEVER_REPLAY; + if (gamedata->everseenspecial) + everflags |= GDEVER_SPECIAL; + + WRITEUINT32(save.p, everflags); // 4 + } + + WRITEUINT32(save.p, quickncasehash(timeattackfolder, 64)); // 4 // To save space, use one bit per collected/achieved/unlocked flag for (i = 0; i < MAXEMBLEMS;) // MAXEMBLEMS * 1; diff --git a/src/m_cond.c b/src/m_cond.c index a152204eb..2e69a211f 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -610,6 +610,7 @@ void M_ClearStats(void) gamedata->timesBeaten = 0; gamedata->everloadedaddon = false; + gamedata->everfinishedcredits = false; gamedata->eversavedreplay = false; gamedata->everseenspecial = false; gamedata->evercrashed = false; @@ -854,6 +855,8 @@ boolean M_CheckCondition(condition_t *cn, player_t *player) case UC_ADDON: return ((gamedata->everloadedaddon == true) && M_SecretUnlocked(SECRET_ADDONS, true)); + case UC_CREDITS: + return (gamedata->everfinishedcredits == true); case UC_REPLAY: return (gamedata->eversavedreplay == true); case UC_CRASH: @@ -1308,6 +1311,8 @@ static const char *M_GetConditionString(condition_t *cn) if (!M_SecretUnlocked(SECRET_ADDONS, true)) return NULL; return "load a custom addon into \"Dr. Robotnik's Ring Racers\""; + case UC_CREDITS: + return "watch the developer credits all the way from start to finish"; case UC_REPLAY: return "save a replay after finishing a round"; case UC_CRASH: diff --git a/src/m_cond.h b/src/m_cond.h index 83207aad0..e3975a957 100644 --- a/src/m_cond.h +++ b/src/m_cond.h @@ -52,6 +52,7 @@ typedef enum UC_CONDITIONSET, // CONDITIONSET [condition set number] UC_ADDON, // Ever loaded a custom file? + UC_CREDITS, // Finish watching the credits UC_REPLAY, // Save a replay UC_CRASH, // Hee ho ! @@ -287,6 +288,7 @@ struct gamedata_t // SPECIFIC SPECIAL EVENTS boolean everloadedaddon; + boolean everfinishedcredits; boolean eversavedreplay; boolean everseenspecial; boolean evercrashed; From 5fedd38965198884c03c6107daaf861397143aed Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 27 Jun 2023 00:16:09 +0100 Subject: [PATCH 29/62] Fix condition for G_GetBackupCupData focus to not include Match Race --- src/menus/transient/level-select.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/menus/transient/level-select.c b/src/menus/transient/level-select.c index a759f7c0b..13eed9aef 100644 --- a/src/menus/transient/level-select.c +++ b/src/menus/transient/level-select.c @@ -266,7 +266,7 @@ boolean M_LevelListFromGametype(INT16 gt) G_GetBackupCupData( cupgrid.grandprix == true - || cv_splitplayers.value <= 1 + && cv_splitplayers.value <= 1 ); templevelsearch.cup = kartcupheaders; From 0a6bac0dcc42ecf6557770e91847b2eef4f4f951 Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Mon, 26 Jun 2023 17:36:41 -0700 Subject: [PATCH 30/62] Ringbox: HUD, transform time define, ring-box-only, scaling rewards --- src/d_netcmd.c | 2 ++ src/d_netcmd.h | 1 + src/k_hud.c | 42 +++++++++++++++++++++++++-------------- src/k_kart.c | 12 ++++++++++- src/k_objects.h | 1 + src/k_roulette.c | 15 +++++++++++++- src/k_roulette.h | 22 ++++++++++++++++++++ src/objects/random-item.c | 4 ++-- src/p_inter.c | 2 +- 9 files changed, 81 insertions(+), 20 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 712c01a3f..15a30f777 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -433,6 +433,8 @@ consvar_t cv_kartbot = CVAR_INIT ("bots", "0", CV_NETVAR, kartbot_cons_t, NULL); consvar_t cv_karteliminatelast = CVAR_INIT ("eliminatelast", "Yes", CV_NETVAR|CV_CALL, CV_YesNo, KartEliminateLast_OnChange); +consvar_t cv_thunderdome = CVAR_INIT ("thunderdome", "Off", CV_NETVAR, CV_OnOff, NULL); + consvar_t cv_kartusepwrlv = CVAR_INIT ("usepwrlv", "Yes", CV_NETVAR, CV_YesNo, NULL); static CV_PossibleValue_t kartdebugitem_cons_t[] = diff --git a/src/d_netcmd.h b/src/d_netcmd.h index c9938b4e5..3bb0fa7e5 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -88,6 +88,7 @@ extern consvar_t cv_kartspeedometer; extern consvar_t cv_kartvoices; extern consvar_t cv_kartbot; extern consvar_t cv_karteliminatelast; +extern consvar_t cv_thunderdome; extern consvar_t cv_kartusepwrlv; extern consvar_t cv_votetime; diff --git a/src/k_hud.c b/src/k_hud.c index 2e53fef83..89a1bfc21 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -1256,7 +1256,7 @@ static void K_drawKartItem(void) for (i = 0; i < 3; i++) { const SINT8 indexOfs = i-1; - const size_t index = (stplyr->itemRoulette.index + indexOfs) % stplyr->itemRoulette.itemListLen; + const size_t index = (stplyr->itemRoulette.itemListLen + (stplyr->itemRoulette.index + indexOfs)) % stplyr->itemRoulette.itemListLen; const SINT8 result = stplyr->itemRoulette.itemList[index]; const SINT8 item = K_ItemResultToType(result); @@ -1584,13 +1584,21 @@ static void K_drawKartSlotMachine(void) patch_t *localpatch[3] = { kp_nodraw, kp_nodraw, kp_nodraw }; patch_t *localbg = offset ? kp_ringbg[1] : kp_ringbg[0]; + // == SHITGARBAGE UNLIMITED 2: RISE OF MY ASS == + // FIVE LAYERS OF BULLSHIT PER-PIXEL SHOVING BECAUSE THE PATCHES HAVE DIFFERENT OFFSETS + // IF YOU ARE HERE TO ADJUST THE RINGBOX HUD TURN OFF YOUR COMPUTER AND GO TO YOUR LOCAL PARK + INT32 fx = 0, fy = 0, fflags = 0; // final coords for hud and flags... + INT32 boxoffx = 0; + INT32 boxoffy = -6; + INT32 vstretch = 0; + INT32 splitbsx, splitbsy = 0; UINT16 localcolor[3] = { stplyr->skincolor }; SINT8 colormode[3] = { TC_RAINBOW }; fixed_t rouletteOffset = 0; - fixed_t rouletteSpace = ROULETTE_SPACING; - vector2_t rouletteCrop = {7, 7}; + fixed_t rouletteSpace = SLOT_SPACING; + vector2_t rouletteCrop = {10, 10}; INT32 i; if (stplyr->itemRoulette.itemListLen > 0) @@ -1599,7 +1607,7 @@ static void K_drawKartSlotMachine(void) for (i = 0; i < 3; i++) { const SINT8 indexOfs = i-1; - const size_t index = (stplyr->itemRoulette.index + indexOfs) % stplyr->itemRoulette.itemListLen; + const size_t index = (stplyr->itemRoulette.itemListLen + (stplyr->itemRoulette.index + indexOfs)) % stplyr->itemRoulette.itemListLen; const SINT8 result = stplyr->itemRoulette.itemList[index]; @@ -1609,7 +1617,7 @@ static void K_drawKartSlotMachine(void) if (stplyr->itemRoulette.active == true) { - rouletteOffset = K_GetRouletteOffset(&stplyr->itemRoulette, rendertimefrac); + rouletteOffset = K_GetSlotOffset(&stplyr->itemRoulette, rendertimefrac); } else { @@ -1620,23 +1628,27 @@ static void K_drawKartSlotMachine(void) // pain and suffering defined below if (offset) { + boxoffx -= 4; if (stplyr == &players[displayplayers[0]] || stplyr == &players[displayplayers[2]]) // If we are P1 or P3... { - fx = ITEM_X; - fy = ITEM_Y; + fx = ITEM_X + 10; + fy = ITEM_Y + 10; fflags = V_SNAPTOLEFT|V_SNAPTOTOP|V_SPLITSCREEN; } else // else, that means we're P2 or P4. { - fx = ITEM2_X; - fy = ITEM2_Y; + fx = ITEM2_X + 7; + fy = ITEM2_Y + 10; fflags = V_SNAPTORIGHT|V_SNAPTOTOP|V_SPLITSCREEN; } - rouletteSpace = ROULETTE_SPACING_SPLITSCREEN; - rouletteOffset = FixedMul(rouletteOffset, FixedDiv(ROULETTE_SPACING_SPLITSCREEN, ROULETTE_SPACING)); + rouletteSpace = SLOT_SPACING_SPLITSCREEN; + rouletteOffset = FixedMul(rouletteOffset, FixedDiv(SLOT_SPACING_SPLITSCREEN, SLOT_SPACING)); rouletteCrop.x = 16; - rouletteCrop.y = 15; + rouletteCrop.y = 13; + splitbsx = -6; + splitbsy = -6; + boxoffy += 2; } else { @@ -1648,8 +1660,8 @@ static void K_drawKartSlotMachine(void) V_DrawScaledPatch(fx, fy, V_HUDTRANS|V_SLIDEIN|fflags, localbg); V_SetClipRect( - (fx + rouletteCrop.x) << FRACBITS, (fy + rouletteCrop.y) << FRACBITS, - rouletteSpace, rouletteSpace, + ((fx + rouletteCrop.x + boxoffx + splitbsx) << FRACBITS), ((fy + rouletteCrop.y + boxoffy - vstretch + splitbsy) << FRACBITS), + rouletteSpace, rouletteSpace + (vstretch<ringboxdelay == 0) { // TODO - K_AwardPlayerRings(player, 10 * (player->ringboxaward + 1), true); + UINT32 behind = K_GetItemRouletteDistance(player, player->itemRoulette.playing); + UINT32 behindMulti = behind / 1000; + behindMulti = min(behindMulti, 20); + + UINT32 award = 5*player->ringboxaward + 10; + if (player->ringboxaward > 2) // not a BAR + award = 3 * award / 2; + award = award * (behind / 1000 + 10) / 10; + + K_AwardPlayerRings(player, award, true); player->ringboxaward = 0; } } diff --git a/src/k_objects.h b/src/k_objects.h index 5deb7749f..8dedbb919 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -132,6 +132,7 @@ void Obj_RandomItemVisuals(mobj_t *mobj); boolean Obj_RandomItemSpawnIn(mobj_t *mobj); fixed_t Obj_RandomItemScale(fixed_t oldScale); void Obj_RandomItemSpawn(mobj_t *mobj); +#define RINGBOX_TIME (70) /* Gachabom Rebound */ void Obj_GachaBomReboundThink(mobj_t *mobj); diff --git a/src/k_roulette.c b/src/k_roulette.c index 0f722a80f..c85162c12 100644 --- a/src/k_roulette.c +++ b/src/k_roulette.c @@ -449,7 +449,7 @@ static UINT32 K_ScaleItemDistance(UINT32 distance, UINT8 numPlayers) Return:- The player's finalized item distance. --------------------------------------------------*/ -static UINT32 K_GetItemRouletteDistance(const player_t *player, UINT8 numPlayers) +UINT32 K_GetItemRouletteDistance(const player_t *player, UINT8 numPlayers) { UINT32 pdis = 0; @@ -1488,6 +1488,19 @@ fixed_t K_GetRouletteOffset(itemroulette_t *const roulette, fixed_t renderDelta) return FixedMul(FixedDiv(midTic - curTic, ((roulette->speed + 1) << FRACBITS)), ROULETTE_SPACING); } +/*-------------------------------------------------- + fixed_t K_GetSlotOffset(itemroulette_t *const roulette, fixed_t renderDelta) + + See header file for description. +--------------------------------------------------*/ +fixed_t K_GetSlotOffset(itemroulette_t *const roulette, fixed_t renderDelta) +{ + const fixed_t curTic = (roulette->tics << FRACBITS) - renderDelta; + const fixed_t midTic = roulette->speed * (FRACUNIT >> 1); + + return FixedMul(FixedDiv(midTic - curTic, ((roulette->speed + 1) << FRACBITS)), SLOT_SPACING); +} + /*-------------------------------------------------- static void K_KartGetItemResult(player_t *const player, kartitems_t getitem) diff --git a/src/k_roulette.h b/src/k_roulette.h index 379a67434..294e8dd04 100644 --- a/src/k_roulette.h +++ b/src/k_roulette.h @@ -23,6 +23,9 @@ extern "C" { #define ROULETTE_SPACING (36 << FRACBITS) #define ROULETTE_SPACING_SPLITSCREEN (16 << FRACBITS) +#define SLOT_SPACING (40 << FRACBITS) +#define SLOT_SPACING_SPLITSCREEN (22 << FRACBITS) + /*-------------------------------------------------- boolean K_ItemEnabled(kartitems_t item); @@ -171,6 +174,23 @@ void K_StartEggmanRoulette(player_t *const player); fixed_t K_GetRouletteOffset(itemroulette_t *const roulette, fixed_t renderDelta); +/*-------------------------------------------------- + fixed_t K_GetSlotOffset(itemroulette_t *const roulette, fixed_t renderDelta); + + Gets the Y offset, for use in the slot HUD. + A separate function since it is used both by the + HUD itself, as well as when confirming an item. + + Input Arguments:- + roulette - The roulette we are drawing for. + renderDelta - Fractional tic delta, when used for HUD. + + Return:- + The Y offset when drawing the item. +--------------------------------------------------*/ + +fixed_t K_GetSlotOffset(itemroulette_t *const roulette, fixed_t renderDelta); + /*-------------------------------------------------- void K_KartItemRoulette(player_t *const player, ticcmd_t *cmd); @@ -188,6 +208,8 @@ fixed_t K_GetRouletteOffset(itemroulette_t *const roulette, fixed_t renderDelta) void K_KartItemRoulette(player_t *const player, ticcmd_t *cmd); +UINT32 K_GetItemRouletteDistance(const player_t *player, UINT8 numPlayers); + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/objects/random-item.c b/src/objects/random-item.c index 623f2ab0c..f26d81807 100644 --- a/src/objects/random-item.c +++ b/src/objects/random-item.c @@ -111,10 +111,10 @@ void Obj_RandomItemVisuals(mobj_t *mobj) // the player's cleared out a good portion of the map. // // Then extraval1 starts ticking up and triggers the transformation from Ringbox to Random Item. - if (mobj->fuse == 0 && !(mobj->flags & MF_NOCLIPTHING)) + if (mobj->fuse == 0 && !(mobj->flags & MF_NOCLIPTHING) && !cv_thunderdome.value) { mobj->extravalue1++; - if (mobj->extravalue1 == TICRATE) + if (mobj->extravalue1 == RINGBOX_TIME) { // Sync the position in RINGBOX and RANDOMITEM animations. statenum_t animDelta = mobj->state - states - S_RINGBOX1; diff --git a/src/p_inter.c b/src/p_inter.c index 971b08f1c..999345841 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -356,7 +356,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) special->momx = special->momy = special->momz = 0; P_SetTarget(&special->target, toucher); // P_KillMobj(special, toucher, toucher, DMG_NORMAL); - if (special->extravalue1 >= TICRATE) + if (special->extravalue1 >= RINGBOX_TIME) K_StartItemRoulette(player, false); else K_StartItemRoulette(player, true); From a43744aa2446fa6e79e8ba767545413818e623da Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Mon, 26 Jun 2023 17:51:42 -0700 Subject: [PATCH 31/62] 1P hud fixes --- src/k_hud.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/k_hud.c b/src/k_hud.c index 89a1bfc21..e5541f8bc 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -1592,6 +1592,7 @@ static void K_drawKartSlotMachine(void) INT32 boxoffx = 0; INT32 boxoffy = -6; INT32 vstretch = 0; + INT32 hstretch = 3; INT32 splitbsx, splitbsy = 0; UINT16 localcolor[3] = { stplyr->skincolor }; SINT8 colormode[3] = { TC_RAINBOW }; @@ -1649,6 +1650,7 @@ static void K_drawKartSlotMachine(void) splitbsx = -6; splitbsy = -6; boxoffy += 2; + hstretch = 0; } else { @@ -1661,7 +1663,7 @@ static void K_drawKartSlotMachine(void) V_SetClipRect( ((fx + rouletteCrop.x + boxoffx + splitbsx) << FRACBITS), ((fy + rouletteCrop.y + boxoffy - vstretch + splitbsy) << FRACBITS), - rouletteSpace, rouletteSpace + (vstretch< Date: Mon, 26 Jun 2023 19:02:39 -0700 Subject: [PATCH 32/62] Ringbox: playtest fixups --- src/k_hud.c | 2 +- src/k_kart.c | 2 +- src/objects/random-item.c | 3 +++ src/p_inter.c | 1 + 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/k_hud.c b/src/k_hud.c index e5541f8bc..0824a2c61 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -5352,7 +5352,7 @@ void K_drawKartHUD(void) // Draw the item window if (LUA_HudEnabled(hud_item) && !freecam) { - if (stplyr->itemRoulette.ringbox) + if (stplyr->itemRoulette.ringbox && stplyr->itemamount == 0 && stplyr->itemtype == 0) { K_drawKartSlotMachine(); } diff --git a/src/k_kart.c b/src/k_kart.c index 81f5ad07d..dc64d067d 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -10746,7 +10746,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) UINT32 award = 5*player->ringboxaward + 10; if (player->ringboxaward > 2) // not a BAR award = 3 * award / 2; - award = award * (behind / 1000 + 10) / 10; + award = award * (behindMulti + 10) / 10; K_AwardPlayerRings(player, award, true); player->ringboxaward = 0; diff --git a/src/objects/random-item.c b/src/objects/random-item.c index f26d81807..7c19a4228 100644 --- a/src/objects/random-item.c +++ b/src/objects/random-item.c @@ -103,6 +103,9 @@ void Obj_RandomItemVisuals(mobj_t *mobj) ItemBoxScaling(mobj); item_vfxtimer(mobj)++; + if (mobj->type != MT_RANDOMITEM) + return; + // Respawn flow, documented by a dumb asshole: // P_TouchSpecialThing -> P_ItemPop sets fuse, NOCLIPTHING and DONTDRAW. // P_FuseThink does visual flicker, and when fuse is 0, unsets NOCLIPTHING/DONTDRAW/etc... diff --git a/src/p_inter.c b/src/p_inter.c index 999345841..7da79bf31 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -140,6 +140,7 @@ boolean P_CanPickupItem(player_t *player, UINT8 weapon) // Item slot already taken up if (player->itemRoulette.active == true + || player->ringboxdelay > 0 || (weapon != 3 && player->itemamount) || (player->pflags & PF_ITEMOUT)) return false; From fd0167a3b00ce94b3884aa871211ad95669e48bd Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 27 Jun 2023 00:27:58 -0700 Subject: [PATCH 33/62] Add Super Flicky states Adds 3DFR sprite (previously freeslotted by followers.pk3) --- src/deh_tables.c | 5 +++++ src/info.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++++ src/info.h | 7 ++++++ 3 files changed, 70 insertions(+) diff --git a/src/deh_tables.c b/src/deh_tables.c index ddbae5cbf..9c46ebc93 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -4571,6 +4571,8 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi "S_GACHABOM_EXPLOSION_4", "S_GACHABOM_WAITING", "S_GACHABOM_RETURNING", + + "S_SUPER_FLICKY", }; // RegEx to generate this from info.h: ^\tMT_([^,]+), --> \t"MT_\1", @@ -5697,6 +5699,9 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t "MT_LOOPENDPOINT", "MT_LOOPCENTERPOINT", + + "MT_SUPER_FLICKY", + "MT_SUPER_FLICKY_CONTROLLER", }; const char *const MOBJFLAG_LIST[] = { diff --git a/src/info.c b/src/info.c index 7d1c0023c..61da58f39 100644 --- a/src/info.c +++ b/src/info.c @@ -815,6 +815,8 @@ char sprnames[NUMSPRITES + 1][5] = "GBOM", "GCHX", + "3DFR", + // First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later "VIEW", }; @@ -5235,6 +5237,8 @@ state_t states[NUMSTATES] = {SPR_GCHX, 6|FF_PAPERSPRITE|FF_ANIMATE|FF_REVERSEANIM, 14, {NULL}, 6, 2, S_GACHABOM_WAITING}, // S_GACHABOM_EXPLOSION_4 {SPR_GBOM, FF_INVERT, 8, {A_SetScale}, FRACUNIT, 0, S_GACHABOM_RETURNING}, // S_GACHABOM_WAITING {SPR_GBOM, FF_INVERT, -1, {A_SetScale}, FRACUNIT/2, 1, S_NULL}, // S_GACHABOM_RETURNING + + {SPR_3DFR, 1|FF_ANIMATE, -1, {NULL}, 2, 5, S_NULL}, // S_SUPER_FLICKY }; mobjinfo_t mobjinfo[NUMMOBJTYPES] = @@ -29794,6 +29798,60 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = MF_NOSECTOR|MF_NOBLOCKMAP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP, // flags S_NULL // raisestate }, + + { // MT_SUPER_FLICKY + -1, // doomednum + S_SUPER_FLICKY, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 0, // 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 + 16*FRACUNIT, // radius + 32*FRACUNIT, // height + 0, // display offset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT, // flags + S_NULL // raisestate + }, + + { // MT_SUPER_FLICKY_CONTROLLER + -1, // doomednum + S_INVISIBLE, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 0, // 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 + 16*FRACUNIT, // radius + 32*FRACUNIT, // height + 0, // display offset + 0, // mass + 0, // damage + sfx_None, // activesound + MF_NOSECTOR|MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_SCENERY, // flags + S_NULL // raisestate + }, }; skincolor_t skincolors[MAXSKINCOLORS] = { diff --git a/src/info.h b/src/info.h index a203dbc3d..ea4df4be3 100644 --- a/src/info.h +++ b/src/info.h @@ -1368,6 +1368,8 @@ typedef enum sprite SPR_GBOM, SPR_GCHX, + SPR_3DFR, + // First person view sprites; this is a sprite so that it can be replaced by a specialized MD2 draw later SPR_VIEW, @@ -5667,6 +5669,8 @@ typedef enum state S_GACHABOM_WAITING, S_GACHABOM_RETURNING, + S_SUPER_FLICKY, + S_FIRSTFREESLOT, S_LASTFREESLOT = S_FIRSTFREESLOT + NUMSTATEFREESLOTS - 1, NUMSTATES @@ -6812,6 +6816,9 @@ typedef enum mobj_type MT_LOOPENDPOINT, MT_LOOPCENTERPOINT, + MT_SUPER_FLICKY, + MT_SUPER_FLICKY_CONTROLLER, + MT_FIRSTFREESLOT, MT_LASTFREESLOT = MT_FIRSTFREESLOT + NUMMOBJFREESLOTS - 1, NUMMOBJTYPES From b35d0ae25ebfdab7f24251b0f5d0a122d2eaa2d9 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 26 Jun 2023 23:25:29 -0700 Subject: [PATCH 34/62] Add Super Flicky sounds fbost1, fbird, fhurt1, fhurt2 were previously freeslotted by chars.pk3. They are used by the Flicky skin. --- src/sounds.c | 7 +++++++ src/sounds.h | 6 ++++++ 2 files changed, 13 insertions(+) diff --git a/src/sounds.c b/src/sounds.c index 9e2702a10..3accd6b79 100644 --- a/src/sounds.c +++ b/src/sounds.c @@ -1189,6 +1189,13 @@ sfxinfo_t S_sfx[NUMSFX] = {"iwhp", false, 255, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // Instawhip attack {"gbrk", false, 255, 8, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // Guard break! + // Super Flicky Power-Up + {"supflk", false, 255, 32, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // Woodpecking - SF_NOINTERRUPT + {"fbost1", false, 255, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // Slowing down + {"fbird", false, 255, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // Speeding up + {"fhurt1", false, 255, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // Whipped + {"fhurt2", false, 255, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, // Hunting + // SRB2Kart - Engine sounds // Engine class A {"krta00", false, 48, 65, -1, NULL, 0, -1, -1, LUMPERROR, ""}, diff --git a/src/sounds.h b/src/sounds.h index a3a72c6a7..033728e37 100644 --- a/src/sounds.h +++ b/src/sounds.h @@ -1258,6 +1258,12 @@ typedef enum sfx_iwhp, sfx_gbrk, + sfx_supflk, + sfx_fbost1, + sfx_fbird, + sfx_fhurt1, + sfx_fhurt2, + // Next up: UNIQUE ENGINE SOUNDS! Hoooooo boy... // Engine class A - Low Speed, Low Weight sfx_krta00, From d7373871324697f8dad994a51b510ca7b560c1a6 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 27 Jun 2023 00:06:13 -0700 Subject: [PATCH 35/62] Add flickyAttacker and powerupvars_t to player_t, add to netsave --- src/d_player.h | 7 +++++++ src/k_kart.c | 6 ++++++ src/p_saveg.c | 34 ++++++++++++++++++++++++++++++++++ src/typedef.h | 1 + 4 files changed, 48 insertions(+) diff --git a/src/d_player.h b/src/d_player.h index 96e990f79..a360f41b7 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -439,6 +439,11 @@ typedef struct { boolean flip; } sonicloopvars_t; +// player_t struct for power-ups +struct powerupvars_t { + mobj_t *flickyController; +}; + // player_t struct for all alternative viewpoint variables struct altview_t { @@ -756,6 +761,7 @@ struct player_t mobj_t *sliptideZipIndicator; mobj_t *whip; mobj_t *hand; + mobj_t *flickyAttacker; UINT8 instaShieldCooldown; UINT8 guardCooldown; @@ -777,6 +783,7 @@ struct player_t sonicloopvars_t loop; roundconditions_t roundconditions; + powerupvars_t powerup; }; // WARNING FOR ANYONE ABOUT TO ADD SOMETHING TO THE PLAYER STRUCT, G_PlayerReborn WANTS YOU TO SUFFER diff --git a/src/k_kart.c b/src/k_kart.c index 1a6ba0d06..64f54942e 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -8287,6 +8287,12 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) if (player->hand && P_MobjWasRemoved(player->hand)) P_SetTarget(&player->hand, NULL); + if (player->flickyAttacker && P_MobjWasRemoved(player->flickyAttacker)) + P_SetTarget(&player->flickyAttacker, NULL); + + if (player->powerup.flickyController && P_MobjWasRemoved(player->powerup.flickyController)) + P_SetTarget(&player->powerup.flickyController, NULL); + if (player->spectator == false) { K_KartEbrakeVisuals(player); diff --git a/src/p_saveg.c b/src/p_saveg.c index f80ffff7b..16efa2798 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -79,6 +79,8 @@ typedef enum RINGSHOOTER = 0x0100, WHIP = 0x0200, HAND = 0x0400, + FLICKYATTACKER = 0x0800, + FLICKYCONTROLLER = 0x1000, } player_saveflags; static inline void P_ArchivePlayer(savebuffer_t *save) @@ -319,6 +321,12 @@ static void P_NetArchivePlayers(savebuffer_t *save) if (players[i].ringShooter) flags |= RINGSHOOTER; + if (players[i].flickyAttacker) + flags |= FLICKYATTACKER; + + if (players[i].powerup.flickyController) + flags |= FLICKYCONTROLLER; + WRITEUINT16(save->p, flags); if (flags & SKYBOXVIEW) @@ -351,6 +359,12 @@ static void P_NetArchivePlayers(savebuffer_t *save) if (flags & RINGSHOOTER) WRITEUINT32(save->p, players[i].ringShooter->mobjnum); + if (flags & FLICKYATTACKER) + WRITEUINT32(save->p, players[i].flickyAttacker->mobjnum); + + if (flags & FLICKYCONTROLLER) + WRITEUINT32(save->p, players[i].powerup.flickyController->mobjnum); + WRITEUINT32(save->p, (UINT32)players[i].followitem); WRITEUINT32(save->p, players[i].charflags); @@ -754,6 +768,12 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) if (flags & RINGSHOOTER) players[i].ringShooter = (mobj_t *)(size_t)READUINT32(save->p); + if (flags & FLICKYATTACKER) + players[i].flickyAttacker = (mobj_t *)(size_t)READUINT32(save->p); + + if (flags & FLICKYCONTROLLER) + players[i].powerup.flickyController = (mobj_t *)(size_t)READUINT32(save->p); + players[i].followitem = (mobjtype_t)READUINT32(save->p); //SetPlayerSkinByNum(i, players[i].skin); @@ -5258,6 +5278,20 @@ static void P_RelinkPointers(void) if (!P_SetTarget(&players[i].ringShooter, P_FindNewPosition(temp))) CONS_Debug(DBG_GAMELOGIC, "ringShooter not found on player %d\n", i); } + if (players[i].flickyAttacker) + { + temp = (UINT32)(size_t)players[i].flickyAttacker; + players[i].flickyAttacker = NULL; + if (!P_SetTarget(&players[i].flickyAttacker, P_FindNewPosition(temp))) + CONS_Debug(DBG_GAMELOGIC, "flickyAttacker not found on player %d\n", i); + } + if (players[i].powerup.flickyController) + { + temp = (UINT32)(size_t)players[i].powerup.flickyController; + players[i].powerup.flickyController = NULL; + if (!P_SetTarget(&players[i].powerup.flickyController, P_FindNewPosition(temp))) + CONS_Debug(DBG_GAMELOGIC, "powerup.flickyController not found on player %d\n", i); + } } } diff --git a/src/typedef.h b/src/typedef.h index 275a30da4..013bf7f18 100644 --- a/src/typedef.h +++ b/src/typedef.h @@ -47,6 +47,7 @@ TYPEDEF (botvars_t); TYPEDEF (roundconditions_t); TYPEDEF (skybox_t); TYPEDEF (itemroulette_t); +TYPEDEF (powerupvars_t); TYPEDEF (altview_t); TYPEDEF (player_t); From ed262f780b6fa864df3df8b4119ebc6cf612a665 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 27 Jun 2023 00:31:30 -0700 Subject: [PATCH 36/62] Add majority of Super Flicky object functionality Thinkers and most collision. - 4 Super Flickys deploy from above the owner player, in a radius. - Radius shrinks as Flickys descend. - Flickys orbit their owner until coming within range of another player. - The entire group of Flickys attack another player at once, with some delay between each. - Flickys accelerate toward their target, constantly building speed. - When a Flicky is both within a short radius of its target and the angle of momentum is narrowed toward the target, the Flicky will sharply accelerate to impale the target. - When a Flicky is both outside of a long radius of its target and the angle of momentum is facing away from the target, the Flicky's momentum will be drastically cut in order to make it easier for the Flicky to turn around. - After one of the Flickys in the group hits its target, all but one of the group is free to hunt a different target. - A new target is chosen from a radius around the current target. - Flickys can only target players who are not respawning and who have not already been attacked by another Flicky. - Super Flickys can be blocked by a Guard. The Super Flicky shall have all its momentum reflected (strong knockback). - Super Flickys can be insta-whipped. This shall have the same effect as a Guard, with the additional effect of knocking the 'Super' out of the Super Flicky. - Non-Super Flickys are knocked back with gravity. After bouncing off the ground once, it regains flight and will continue to chase its target. However, it cannot damage the target. After 5 seconds, the Flicky regains Super state. - The Flicky power-up is on a timer. After the timer expires, Flickys lose Super state and ascend back into the air (reverse of their initial descent). - If the Super Flicky is not orbiting its target when it ascends, it retains all horizontal momentum during the ascent, 'flying off into the distance'. --- src/k_objects.h | 17 + src/objects/CMakeLists.txt | 1 + src/objects/super-flicky.cpp | 769 +++++++++++++++++++++++++++++++++++ src/p_inter.c | 12 + src/p_mobj.c | 22 + 5 files changed, 821 insertions(+) create mode 100644 src/objects/super-flicky.cpp diff --git a/src/k_objects.h b/src/k_objects.h index a20798dac..477c109ac 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -2,6 +2,8 @@ #ifndef k_objects_H #define k_objects_H +#include "doomdef.h" +#include "m_fixed.h" #include "tables.h" // angle_t #include "taglist.h" @@ -143,6 +145,21 @@ void Obj_SpawnGachaBomRebound(mobj_t *source, mobj_t *target); /* Servant Hand */ void Obj_ServantHandHandling(player_t *player); +/* Super Flicky Controller */ +void Obj_SpawnSuperFlickySwarm(player_t *owner, tic_t time); +void Obj_SuperFlickyControllerThink(mobj_t *controller); +void Obj_EndSuperFlickySwarm(mobj_t *controller); +void Obj_ExtendSuperFlickySwarm(mobj_t *controller, tic_t time); +tic_t Obj_SuperFlickySwarmTime(const mobj_t *controller); + +/* Super Flicky */ +void Obj_SuperFlickyThink(mobj_t *flicky); +void Obj_WhipSuperFlicky(mobj_t *flicky); +void Obj_BlockSuperFlicky(mobj_t *flicky); +void Obj_SuperFlickyPlayerCollide(mobj_t *flicky, mobj_t *player); +void Obj_SuperFlickyLanding(mobj_t *flicky); +boolean Obj_IsSuperFlickyWhippable(const mobj_t *flicky); + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/objects/CMakeLists.txt b/src/objects/CMakeLists.txt index 6ec83ac0c..1bbf55729 100644 --- a/src/objects/CMakeLists.txt +++ b/src/objects/CMakeLists.txt @@ -21,4 +21,5 @@ target_sources(SRB2SDL2 PRIVATE block.c gachabom-rebound.cpp servant-hand.c + super-flicky.cpp ) diff --git a/src/objects/super-flicky.cpp b/src/objects/super-flicky.cpp new file mode 100644 index 000000000..c28c3250d --- /dev/null +++ b/src/objects/super-flicky.cpp @@ -0,0 +1,769 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 2023 by James R. +// Copyright (C) 2023 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. +//----------------------------------------------------------------------------- +/// \brief Super Flicky power-up, hunts other players + +#include "../d_player.h" +#include "../doomdef.h" +#include "../g_game.h" +#include "../k_battle.h" +#include "../k_kart.h" +#include "../k_objects.h" +#include "../k_respawn.h" +#include "../m_fixed.h" +#include "../m_random.h" +#include "../p_local.h" +#include "../r_main.h" +#include "../s_sound.h" +#include "../tables.h" + +#define flicky_controller(o) ((o)->target) +#define flicky_chasing(o) ((o)->tracer) +#define flicky_next(o) ((o)->hnext) +#define flicky_next_target(o) ((o)->hprev) +#define flicky_phase(o) ((o)->threshold) +#define flicky_delay(o) ((o)->movecount) +#define flicky_mode(o) ((o)->extravalue1) +#define flicky_fly(o) ((o)->extravalue2) + +#define controller_source(o) ((o)->target) +#define controller_chasing(o) ((o)->tracer) +#define controller_flicky(o) ((o)->hnext) +#define controller_mode(o) ((o)->movecount) +#define controller_zofs(o) ((o)->sprzoff) +#define controller_expiry(o) ((o)->fuse) + +namespace +{ + +constexpr tic_t kOrbitSpeed = 2*TICRATE; +constexpr int kOrbitSpacing = ANGLE_90; + +// Multiples of player radius +constexpr fixed_t kOrbitRadiusInitial = 32*FRACUNIT; +constexpr fixed_t kOrbitRadius = 2*FRACUNIT; + +constexpr int kDescendHeight = 256; +constexpr int kDescendSmoothing = 16; + +constexpr int kSearchRadius = 1920; +constexpr int kFlightRadius = 1280; +constexpr int kPeckingRadius = 256; + +constexpr int kFlightSpeed = 2; +constexpr int kPeckingSpeed = 8; + +constexpr fixed_t kRebound = 8*FRACUNIT/9; + +constexpr tic_t kDelay = 8; +constexpr tic_t kStunTime = 5*TICRATE; +constexpr tic_t kBlockTime = 1*TICRATE; + +constexpr int kRiseTime = 1*TICRATE; +constexpr int kRiseSpeed = 4; + +// TODO: skincolor must be updated to 2.2 palette +constexpr skincolornum_t kSuperStart = SKINCOLOR_SUPERGOLD1; +constexpr skincolornum_t kSuperEnd = SKINCOLOR_SUPERGOLD5; + +// copied from objects/hyudoro.c +void +sine_bob +( mobj_t * hyu, + angle_t a, + fixed_t sineofs) +{ + const fixed_t kBobHeight = 4 * mapobjectscale; + + // slightly modified from objects/hyudoro.c + hyu->sprzoff = FixedMul(kBobHeight, + sineofs + FINESINE(a >> ANGLETOFINESHIFT)); +} + +void +bob_in_place +( mobj_t * hyu, + INT32 phase, + INT32 bob_speed) +{ + sine_bob(hyu, + ((leveltime + phase) & (bob_speed - 1)) * + (ANGLE_MAX / bob_speed), -(3*FRACUNIT/4)); +} + +struct Flicky; + +struct Controller : mobj_t +{ + enum class Mode : int + { + kDescend, + kOrbit, + kEnRoute, + kAttached, + kReturning, + }; + + mobj_t* source() const { return controller_source(this); } + void source(mobj_t* n) { P_SetTarget(&controller_source(this), n); } + + mobj_t* chasing() const { return controller_chasing(this); } + void chasing(mobj_t* n) { P_SetTarget(&controller_chasing(this), n); } + + Flicky* flicky() const; + void flicky(Flicky* n); + + Mode mode() const { return static_cast(controller_mode(this)); } + void mode(Mode n) { controller_mode(this) = static_cast(n); } + + fixed_t zofs() const { return controller_zofs(this); } + void zofs(fixed_t n) { controller_zofs(this) = n; } + + tic_t expiry() const { return controller_expiry(this); } + void expiry(tic_t n) { controller_expiry(this) = n; } + + static Controller* spawn(player_t* player, tic_t time) + { + Controller* x = static_cast(P_SpawnMobjFromMobjUnscaled( + player->mo, + 0, + 0, + kDescendHeight * mapobjectscale, + MT_SUPER_FLICKY_CONTROLLER + )); + + x->source(player->mo); + x->mode(Mode::kDescend); + x->zofs(0); + x->expiry(leveltime + time); + + P_SetTarget(&player->powerup.flickyController, x); + + S_StartSound(x, sfx_s3k46); + + return x; + } + + bool valid() { return !P_MobjWasRemoved(source()); } + tic_t time_remaining() const { return expiry() - leveltime; } + tic_t powerup_remaining() const { return ending() ? 0u : time_remaining() - kRiseTime; } + bool ending() const { return time_remaining() <= kRiseTime; } + + void descend() + { + fixed_t head = P_GetMobjHead(source()); + fixed_t tz = head; + + if (mode() == Mode::kDescend) + { + tz = z - ((z - head) / kDescendSmoothing); + + if ((tz - head) < mapobjectscale) + { + mode(Mode::kOrbit); + tz = head; + } + } + + z = tz + zofs(); + + if (ending()) + { + zofs(zofs() + (kRiseSpeed * mapobjectscale * P_MobjFlip(this))); + } + } + + void expand() + { + fixed_t n = FixedMul(kOrbitRadiusInitial, ((z - P_GetMobjHead(source())) / kDescendHeight)); + + radius = FixedMul(FixedMul(kOrbitRadius, source()->radius), FRACUNIT + n); + } + + void end() + { + // +1 in case flicky already thunk this tic + expiry(leveltime + kRiseTime + 1); + } + + void search(); +}; + +struct Flicky : mobj_t +{ + enum class Mode : int + { + kReserved, + kHunting, + kStunned, + kWeak, + }; + + enum class Fly : int + { + kNormal, + kZoom, + kSlow, + }; + + Controller* controller() const { return static_cast(flicky_controller(this)); } + void controller(Controller* n) { P_SetTarget(&flicky_controller(this), n); } + + mobj_t* chasing() const { return flicky_chasing(this); } + void chasing(mobj_t* n) { P_SetTarget(&flicky_chasing(this), n); } + + Flicky* next() const { return static_cast(flicky_next(this)); } + void next(Flicky* n) { P_SetTarget(&flicky_next(this), n); } + + mobj_t* next_target() const { return flicky_next_target(this); } + void next_target(mobj_t* n) { P_SetTarget(&flicky_next_target(this), n); } + + int phase() const { return flicky_phase(this); } + void phase(int n) { flicky_phase(this) = n; } + + int delay() const { return flicky_delay(this); } + void delay(int n) { flicky_delay(this) = n; } + + Mode mode() const { return static_cast(flicky_mode(this)); } + void mode(Mode n) { flicky_mode(this) = static_cast(n); } + + Fly fly() const { return static_cast(flicky_fly(this)); } + void fly(Fly n) { flicky_fly(this) = static_cast(n); } + + mobj_t* source() const { return controller()->source(); } + + static void spawn(Controller* controller, int phase) + { + Flicky* x = static_cast(P_SpawnMobjFromMobj(controller, 0, 0, 0, MT_SUPER_FLICKY)); + + x->controller(controller); + x->phase(phase); + x->delay(0); + x->mode(Mode::kReserved); + x->light_up(true); + + x->next(controller->flicky()); + controller->flicky(x); + } + + static skincolornum_t super_color() + { + return static_cast(kSuperStart + ((leveltime / 4) % ((kSuperEnd - kSuperStart) + 1))); + } + + bool valid() const { return !P_MobjWasRemoved(controller()) && controller()->valid(); } + + bool stunned() const { return mode() == Mode::kStunned || mode() == Mode::kWeak; } + + void light_up(bool n) + { + if (n) + { + renderflags |= RF_FULLBRIGHT; + color = super_color(); + } + else + { + renderflags &= ~(RF_FULLBRIGHT); + color = source()->player ? source()->player->skincolor : source()->color; + } + } + + void animate() + { + P_InstaScale(this, source()->scale * (chasing() ? 2 : 1)); + + if (color >= kSuperStart && color <= kSuperEnd) + { + color = super_color(); + } + + bob_in_place(this, phase() * 8, 32); + } + + void refocus() + { + if (controller()->ending()) + { + if (controller()->time_remaining() == kRiseTime) + { + light_up(false); + rise(); + } + + return; + } + + if (delay() > 0) + { + delay(delay() - 1); + } + else + { + if (chasing() != next_target()) + { + chasing(next_target()); + mode(Mode::kHunting); + + S_StartSound(this, sfx_fhurt2); + } + + if (stunned()) + { + light_up(true); + flags = info->flags; + mode(Mode::kHunting); + + S_StartSound(this, sfx_s3k9f); + } + } + } + + angle_t orbit_angle() const { return controller()->angle + (phase() * kOrbitSpacing); } + + vector3_t orbit_position() const + { + return { + source()->x + FixedMul(FCOS(orbit_angle()), controller()->radius), + source()->y + FixedMul(FSIN(orbit_angle()), controller()->radius), + controller()->z + }; + } + + void orbit() + { + vector3_t pos = orbit_position(); + + P_MoveOrigin(this, pos.x, pos.y, pos.z); + + momx = 0; + momy = 0; + momz = 0; + + angle = orbit_angle() + ANGLE_90; // face direction of orbit + } + + + // copied from objects/spb.c + void spawn_speed_lines(angle_t direction) + { + if (mode() != Mode::kHunting) + { + return; + } + + mobj_t *fast = P_SpawnMobjFromMobjUnscaled( + this, + P_RandomRange(PR_DECORATION, -24, 24) * mapobjectscale, + P_RandomRange(PR_DECORATION, -24, 24) * mapobjectscale, + (height / 2) + (P_RandomRange(PR_DECORATION, -24, 24) * mapobjectscale), + MT_FASTLINE + ); + + P_SetTarget(&fast->target, this); + fast->angle = direction; + + fast->color = source()->color; + fast->colorized = true; + fast->renderflags |= RF_ADD; + + K_MatchGenericExtraFlags(fast, this); + } + + void chase() + { + if (controller()->ending()) + { + return; + } + + vector3_t pos = chasing() ? vector3_t{chasing()->x, chasing()->y, P_GetMobjFeet(chasing())} : orbit_position(); + angle_t th = R_PointToAngle2(x, y, pos.x, pos.y); + + momz = (pos.z - z) / kDescendSmoothing; + angle = K_MomentumAngleReal(this); + + angle_t d = AngleDelta(th, angle); + fixed_t dist = FixedHypot(x - pos.x, y - pos.y); + + const Fly oldFly = fly(); + + if (d < ANGLE_11hh && dist < kPeckingRadius * mapobjectscale) + { + // Drastically speed up when about to intersect + P_Thrust(this, th, kPeckingSpeed * mapobjectscale); + fly(Fly::kZoom); + } + else + { + P_Thrust(this, th, kFlightSpeed * mapobjectscale); + fly(Fly::kNormal); + } + + if (d > ANGLE_45 && dist > kFlightRadius * mapobjectscale) + { + // Cut momentum when too far outside of intended trajectory + momx = FixedMul(momx, kRebound); + momy = FixedMul(momy, kRebound); + + spawn_speed_lines(th); + + fly(Fly::kSlow); + } + else + { + spawn_speed_lines(angle); + } + + // Returning to owner + if (!chasing()) + { + if (AngleDelta(th, R_PointToAngle2(x + momx, y + momy, pos.x, pos.y)) > ANG1) + { + mode(Mode::kReserved); + } + else + { + P_InstaThrust(this, th, FixedHypot(momx, momy)); + } + } + + if (fly() != oldFly) + { + switch (fly()) + { + case Fly::kNormal: + break; + + case Fly::kZoom: + S_StartSound(this, sfx_fbird); + break; + + case Fly::kSlow: + S_StartSound(this, sfx_fbost1); + break; + } + } + } + + void rise() + { + P_SetObjectMomZ(this, kRiseSpeed * FRACUNIT, false); + } + + void damage(mobj_t* mobj) + { + if (!mobj->player) + { + return; + } + + if (mobj != chasing()) + { + return; + } + + if (mode() != Mode::kHunting) + { + return; + } + + if (P_DamageMobj(mobj, this, source(), 1, DMG_NORMAL)) + { + P_InstaThrust(mobj, K_MomentumAngleReal(this), FixedHypot(momx, momy)); + K_StumblePlayer(mobj->player); + + mobj->player->spinouttimer = 1; // need invulnerability for one tic + + P_SetTarget(&mobj->player->flickyAttacker, this); + + controller()->mode(Controller::Mode::kAttached); + } + + S_StartSound(this, sfx_supflk); + } + + void reflect() + { + momx = -(momx); + momy = -(momy); + } + + void nerf() + { + light_up(false); + + flags &= ~(MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT); + } + + void whip() + { + reflect(); + P_SetObjectMomZ(this, 8*FRACUNIT, false); + + nerf(); + + mode(Mode::kStunned); + delay(kStunTime); + + S_StartSound(this, sfx_fhurt1); + } + + void block() + { + reflect(); + + mode(Mode::kStunned); + delay(kBlockTime); + } + + void land() + { + flags |= MF_NOGRAVITY; + + mode(Mode::kWeak); + } +}; + +Flicky* Controller::flicky() const +{ + return static_cast(controller_flicky(this)); +} + +void Controller::flicky(Flicky* n) +{ + P_SetTarget(&controller_flicky(this), n); +} + +void Controller::search() +{ + if (ending()) + { + return; + } + + fixed_t nearestDistance = INT32_MAX; + mobj_t* nearestMobj = nullptr; + + mobj_t* origin = chasing() ? chasing() : source(); + + for (int i = 0; i < MAXPLAYERS; ++i) + { + player_t* player = &players[i]; + mobj_t* mobj = player->mo; + + if (!playeringame[i] || P_MobjWasRemoved(mobj)) + { + continue; + } + + // Do not retarget existing target or owner. + if (mobj == chasing() || mobj == source()) + { + continue; + } + + // Target is already being hunted. + if (player->flickyAttacker) + { + continue; + } + + if (player->respawn.state != RESPAWNST_NONE) + { + continue; + } + + fixed_t dist = FixedHypot(origin->x - mobj->x, origin->y - mobj->y); + + if (dist < kSearchRadius * mapobjectscale && dist < nearestDistance) + { + nearestDistance = dist; + nearestMobj = mobj; + } + } + + if (nearestMobj) + { + if (chasing() && flicky()) + { + // Detach flicky from swarm. This one keeps its previous target. + flicky(flicky()->next()); + } + + chasing(nearestMobj); + mode(Mode::kEnRoute); + + // Update entire swarm + for (Flicky* x = flicky(); x; x = x->next()) + { + x->next_target(chasing()); + x->delay(x->phase() * kDelay); + } + } +} + +}; // namespace + +void Obj_SpawnSuperFlickySwarm(player_t* owner, tic_t time) +{ + Controller* controller = Controller::spawn(owner, time); + + Flicky::spawn(controller, 0); + Flicky::spawn(controller, 1); + Flicky::spawn(controller, 2); + Flicky::spawn(controller, 3); +} + +void Obj_SuperFlickyThink(mobj_t* mobj) +{ + Flicky* x = static_cast(mobj); + + if (!x->valid()) + { + P_RemoveMobj(x); + return; + } + + x->animate(); + x->refocus(); + + switch (x->mode()) + { + case Flicky::Mode::kReserved: + x->orbit(); + break; + + case Flicky::Mode::kHunting: + x->chase(); + break; + + case Flicky::Mode::kStunned: + break; + + case Flicky::Mode::kWeak: + x->chase(); + break; + } +} + +void Obj_SuperFlickyControllerThink(mobj_t* mobj) +{ + Controller* x = static_cast(mobj); + + if (!x->valid()) + { + P_RemoveMobj(x); + return; + } + + if (x->time_remaining() <= 1) + { + P_RemoveMobj(x); + return; + } + + x->angle += ANGLE_MAX / kOrbitSpeed; + + switch (x->mode()) + { + case Controller::Mode::kDescend: + x->descend(); + x->expand(); + break; + + case Controller::Mode::kOrbit: + x->descend(); + x->expand(); + x->search(); + break; + + case Controller::Mode::kEnRoute: + break; + + case Controller::Mode::kAttached: + x->search(); + break; + + case Controller::Mode::kReturning: + x->descend(); + break; + } +} + +void Obj_WhipSuperFlicky(mobj_t* t1) +{ + Flicky* x = static_cast(t1); + + if (x->valid()) + { + x->whip(); + } +} + +void Obj_BlockSuperFlicky(mobj_t* t1) +{ + Flicky* x = static_cast(t1); + + if (x->valid()) + { + x->block(); + } +} + +void Obj_SuperFlickyPlayerCollide(mobj_t* t1, mobj_t* t2) +{ + Flicky* x = static_cast(t1); + + if (x->valid()) + { + x->damage(t2); + } +} + +void Obj_SuperFlickyLanding(mobj_t* mobj) +{ + Flicky* x = static_cast(mobj); + + if (x->valid()) + { + x->land(); + } +} + +void Obj_EndSuperFlickySwarm(mobj_t* mobj) +{ + Controller* x = static_cast(mobj); + + if (x->valid()) + { + x->end(); + } +} + +void Obj_ExtendSuperFlickySwarm(mobj_t* mobj, tic_t time) +{ + Controller* x = static_cast(mobj); + + x->expiry(x->expiry() + time); +} + +tic_t Obj_SuperFlickySwarmTime(const mobj_t* mobj) +{ + const Controller* x = static_cast(mobj); + + return x ? x->powerup_remaining() : 0u; +} + +boolean Obj_IsSuperFlickyWhippable(const mobj_t* mobj) +{ + const Flicky* x = static_cast(mobj); + + return !x->stunned(); +} diff --git a/src/p_inter.c b/src/p_inter.c index c771b3427..6a6aabde9 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -584,6 +584,10 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) Obj_PlayerUsedRingShooter(special, player); return; + case MT_SUPER_FLICKY: + Obj_SuperFlickyPlayerCollide(special, toucher); + return; + default: // SOC or script pickup P_SetTarget(&special->target, toucher); break; @@ -2258,8 +2262,16 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da if (clash) { player->spheres = max(player->spheres - 10, 0); + if (inflictor) + { K_DoPowerClash(target, inflictor); + + if (inflictor->type == MT_SUPER_FLICKY) + { + Obj_BlockSuperFlicky(inflictor); + } + } else if (source) K_DoPowerClash(target, source); } diff --git a/src/p_mobj.c b/src/p_mobj.c index 054efcbb1..7f596338d 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -2464,6 +2464,12 @@ boolean P_ZMovement(mobj_t *mo) return false; } } + else if (mo->type == MT_SUPER_FLICKY) + { + mom.z = -mom.z; + + Obj_SuperFlickyLanding(mo); + } else if (mo->type == MT_DRIFTCLIP) { mom.z = -mom.z/2; @@ -6684,6 +6690,14 @@ static void P_MobjSceneryThink(mobj_t *mobj) case MT_INSTAWHIP_RECHARGE: Obj_InstaWhipRechargeThink(mobj); + if (P_MobjWasRemoved(mobj)) + { + return; + } + break; + case MT_SUPER_FLICKY_CONTROLLER: + Obj_SuperFlickyControllerThink(mobj); + if (P_MobjWasRemoved(mobj)) { return; @@ -9533,6 +9547,14 @@ static boolean P_MobjRegularThink(mobj_t *mobj) case MT_GACHABOM_REBOUND: Obj_GachaBomReboundThink(mobj); + if (P_MobjWasRemoved(mobj)) + { + return false; + } + break; + case MT_SUPER_FLICKY: + Obj_SuperFlickyThink(mobj); + if (P_MobjWasRemoved(mobj)) { return false; From b9820e3264baceca485a0bbc1448ea95f202cf47 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 27 Jun 2023 00:46:30 -0700 Subject: [PATCH 37/62] Whip Super Flicky --- src/k_collide.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/k_collide.cpp b/src/k_collide.cpp index ff193d26e..41f029bbe 100644 --- a/src/k_collide.cpp +++ b/src/k_collide.cpp @@ -898,6 +898,19 @@ boolean K_InstaWhipCollide(mobj_t *shield, mobj_t *victim) } return false; } + else if (victim->type == MT_SUPER_FLICKY) + { + if (Obj_IsSuperFlickyWhippable(victim)) + { + K_AddHitLag(victim, victimHitlag, true); + K_AddHitLag(attacker, attackerHitlag, false); + shield->hitlag = attacker->hitlag; + + Obj_WhipSuperFlicky(victim); + return true; + } + return false; + } else { if (victim->type == MT_ORBINAUT || victim->type == MT_JAWZ || victim->type == MT_GACHABOM From bef55b818b09ab555f0fd533bf4a966edd5c6714 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 27 Jun 2023 00:38:00 -0700 Subject: [PATCH 38/62] Add power-up constants --- src/d_player.h | 11 ++++++++++- src/deh_tables.c | 6 ++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/src/d_player.h b/src/d_player.h index a360f41b7..a8da43220 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -179,7 +179,16 @@ typedef enum KRITEM_DUALJAWZ, KRITEM_TRIPLEGACHABOM, - NUMKARTRESULTS + NUMKARTRESULTS, + + // Power-ups exist in the same enum as items so it's easy + // for paper items to be reused for them. + FIRSTPOWERUP, + POWERUP_SMONITOR = FIRSTPOWERUP, + POWERUP_BARRIER, + POWERUP_BUMPER, + POWERUP_BADGE, + POWERUP_SUPERFLICKY, } kartitems_t; typedef enum diff --git a/src/deh_tables.c b/src/deh_tables.c index 9c46ebc93..31cf88e30 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -6861,6 +6861,12 @@ struct int_const_s const INT_CONST[] = { {"KRITEM_DUALJAWZ",KRITEM_DUALJAWZ}, {"KRITEM_TRIPLEGACHABOM",KRITEM_TRIPLEGACHABOM}, {"NUMKARTRESULTS",NUMKARTRESULTS}, + {"FIRSTPOWERUP",FIRSTPOWERUP}, + {"POWERUP_SMONITOR",POWERUP_SMONITOR}, + {"POWERUP_BARRIER",POWERUP_BARRIER}, + {"POWERUP_BUMPER",POWERUP_BUMPER}, + {"POWERUP_BADGE",POWERUP_BADGE}, + {"POWERUP_SUPERFLICKY",POWERUP_SUPERFLICKY}, // kartshields_t {"KSHIELD_NONE",KSHIELD_NONE}, From 58cccedd642f5e7fb13e778a1e14d5d8f89d22f0 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 27 Jun 2023 00:23:34 -0700 Subject: [PATCH 39/62] MT_FLOATINGITEM: use K_UpdateMobjItemOverlay --- src/p_mobj.c | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/src/p_mobj.c b/src/p_mobj.c index 7f596338d..5f1012708 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -7211,29 +7211,12 @@ static boolean P_MobjRegularThink(mobj_t *mobj) } } - switch (mobj->threshold) + if (mobj->threshold == KITEM_SPB || mobj->threshold == KITEM_SHRINK) { - case KITEM_ORBINAUT: - mobj->sprite = SPR_ITMO; - mobj->frame = FF_FULLBRIGHT|FF_PAPERSPRITE|K_GetOrbinautItemFrame(mobj->movecount); - break; - case KITEM_INVINCIBILITY: - mobj->sprite = SPR_ITMI; - mobj->frame = FF_FULLBRIGHT|FF_PAPERSPRITE|K_GetInvincibilityItemFrame(); - break; - case KITEM_SAD: - mobj->sprite = SPR_ITEM; - mobj->frame = FF_FULLBRIGHT|FF_PAPERSPRITE; - break; - case KITEM_SPB: - case KITEM_SHRINK: - K_SetItemCooldown(mobj->threshold, 20*TICRATE); - /* FALLTHRU */ - default: - mobj->sprite = SPR_ITEM; - mobj->frame = FF_FULLBRIGHT|FF_PAPERSPRITE|(mobj->threshold); - break; + K_SetItemCooldown(mobj->threshold, 20*TICRATE); } + + K_UpdateMobjItemOverlay(mobj, mobj->threshold, mobj->movecount); break; } case MT_ITEMCAPSULE: From a0b0891009481ccce917083d9e5c15e98bb852d4 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 27 Jun 2023 00:08:47 -0700 Subject: [PATCH 40/62] Add K_DropPaperItem, split from K_DropItems but with custom item type and amount --- src/k_kart.c | 26 ++++++++++++++++++-------- src/k_kart.h | 1 + 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 64f54942e..122b80964 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6599,20 +6599,30 @@ mobj_t *K_CreatePaperItem(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 return drop; } +void K_DropPaperItem(player_t *player, UINT8 itemtype, UINT16 itemamount) +{ + if (!player->mo || P_MobjWasRemoved(player->mo)) + { + return; + } + + mobj_t *drop = K_CreatePaperItem( + player->mo->x, player->mo->y, player->mo->z + player->mo->height/2, + player->mo->angle + ANGLE_90, P_MobjFlip(player->mo), + itemtype, itemamount + ); + + K_FlipFromObject(drop, player->mo); +} + // For getting EXTRA hit! void K_DropItems(player_t *player) { K_DropHnextList(player); - if (player->mo && !P_MobjWasRemoved(player->mo) && player->itemamount > 0) + if (player->itemamount > 0) { - mobj_t *drop = K_CreatePaperItem( - player->mo->x, player->mo->y, player->mo->z + player->mo->height/2, - player->mo->angle + ANGLE_90, P_MobjFlip(player->mo), - player->itemtype, player->itemamount - ); - - K_FlipFromObject(drop, player->mo); + K_DropPaperItem(player, player->itemtype, player->itemamount); } K_StripItems(player); diff --git a/src/k_kart.h b/src/k_kart.h index af7d142d2..3235d539a 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -151,6 +151,7 @@ void K_KartUpdatePosition(player_t *player); void K_UpdateAllPlayerPositions(void); SINT8 K_GetTotallyRandomResult(UINT8 useodds); mobj_t *K_CreatePaperItem(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 flip, UINT8 type, UINT8 amount); +void K_DropPaperItem(player_t *player, UINT8 itemtype, UINT16 itemamount); void K_PopPlayerShield(player_t *player); void K_DropItems(player_t *player); void K_DropRocketSneaker(player_t *player); From e4f7efca51b8cfd9c6a6a0215eb9f5885f191d1b Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Tue, 27 Jun 2023 01:42:24 -0700 Subject: [PATCH 41/62] Add roulette ringbox flag to netsave --- src/p_saveg.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/p_saveg.c b/src/p_saveg.c index 59654f8a1..3f075fc2d 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -602,6 +602,7 @@ static void P_NetArchivePlayers(savebuffer_t *save) WRITEUINT32(save->p, players[i].itemRoulette.tics); WRITEUINT32(save->p, players[i].itemRoulette.elapsed); WRITEUINT8(save->p, players[i].itemRoulette.eggman); + WRITEUINT8(save->p, players[i].itemRoulette.ringbox); // sonicloopsvars_t WRITEFIXED(save->p, players[i].loop.radius); @@ -1019,6 +1020,7 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) players[i].itemRoulette.tics = (tic_t)READUINT32(save->p); players[i].itemRoulette.elapsed = (tic_t)READUINT32(save->p); players[i].itemRoulette.eggman = (boolean)READUINT8(save->p); + players[i].itemRoulette.ringbox = (boolean)READUINT8(save->p); // sonicloopsvars_t players[i].loop.radius = READFIXED(save->p); From 919c6698636b122f8cbf3d6c1e80eeb1d00d0716 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 27 Jun 2023 00:43:55 -0700 Subject: [PATCH 42/62] Drop power-ups when whipped - Dropped power-ups become paper items (overloaded to store power-ups instead of items). - The dropped power-up stores its remaining duration. - The power-up can be picked up during any state. - If you already have the same kind of power-up, the duration is simply extended. --- src/CMakeLists.txt | 1 + src/info.c | 1 + src/info.h | 1 + src/k_battle.h | 1 + src/k_collide.cpp | 3 +++ src/k_kart.c | 37 +++++++++++++++++++++++++--------- src/k_kart.h | 2 +- src/k_powerup.cpp | 50 ++++++++++++++++++++++++++++++++++++++++++++++ src/k_powerup.h | 19 ++++++++++++++++++ src/p_inter.c | 22 +++++++++++++------- 10 files changed, 120 insertions(+), 17 deletions(-) create mode 100644 src/k_powerup.cpp create mode 100644 src/k_powerup.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index aa5697997..24b59ae08 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -146,6 +146,7 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32 k_serverstats.c k_zvote.c k_mapuser.c + k_powerup.cpp ) if(SRB2_CONFIG_ENABLE_WEBM_MOVIES) diff --git a/src/info.c b/src/info.c index 61da58f39..c0e95fa73 100644 --- a/src/info.c +++ b/src/info.c @@ -648,6 +648,7 @@ char sprnames[NUMSPRITES + 1][5] = "ITMO", "ITMI", "ITMN", + "PWRB", "WANT", "PBOM", // player bomb diff --git a/src/info.h b/src/info.h index ea4df4be3..50486d2b3 100644 --- a/src/info.h +++ b/src/info.h @@ -1201,6 +1201,7 @@ typedef enum sprite SPR_ITMO, SPR_ITMI, SPR_ITMN, + SPR_PWRB, SPR_WANT, SPR_PBOM, // player bomb diff --git a/src/k_battle.h b/src/k_battle.h index 4e1dda2c8..bd15f8b55 100644 --- a/src/k_battle.h +++ b/src/k_battle.h @@ -10,6 +10,7 @@ extern "C" { #define BATTLE_SPAWN_INTERVAL (4*TICRATE) #define BATTLE_DESPAWN_TIME (15*TICRATE) +#define BATTLE_POWERUP_TIME (20*TICRATE) extern struct battleovertime { diff --git a/src/k_collide.cpp b/src/k_collide.cpp index 41f029bbe..cc3dd492d 100644 --- a/src/k_collide.cpp +++ b/src/k_collide.cpp @@ -16,6 +16,7 @@ #include "k_objects.h" #include "k_roulette.h" #include "k_podium.h" +#include "k_powerup.h" angle_t K_GetCollideAngle(mobj_t *t1, mobj_t *t2) { @@ -888,6 +889,8 @@ boolean K_InstaWhipCollide(mobj_t *shield, mobj_t *victim) P_PlayerRingBurst(victimPlayer, 5); P_DamageMobj(victim, shield, attacker, 1, DMG_STUMBLE); // There's a special exception in P_DamageMobj for type==MT_INSTAWHIP + K_DropPowerUps(victimPlayer); + angle_t thrangle = ANGLE_180 + R_PointToAngle2(victim->x, victim->y, shield->x, shield->y); P_Thrust(victim, thrangle, FRACUNIT*10); diff --git a/src/k_kart.c b/src/k_kart.c index 122b80964..40527d844 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6532,13 +6532,26 @@ SINT8 K_GetTotallyRandomResult(UINT8 useodds) return i; } -mobj_t *K_CreatePaperItem(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 flip, UINT8 type, UINT8 amount) +mobj_t *K_CreatePaperItem(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 flip, UINT8 type, UINT16 amount) { mobj_t *drop = P_SpawnMobj(x, y, z, MT_FLOATINGITEM); - mobj_t *backdrop = P_SpawnMobjFromMobj(drop, 0, 0, 0, MT_OVERLAY); - P_SetTarget(&backdrop->target, drop); - P_SetMobjState(backdrop, S_ITEMBACKDROP); + // FIXME: due to linkdraw sucking major ass, I was unable + // to make a backdrop render behind dropped power-ups + // (which use a smaller sprite than normal items). So + // dropped power-ups have the backdrop baked into the + // sprite for now. + if (type < FIRSTPOWERUP) + { + mobj_t *backdrop = P_SpawnMobjFromMobj(drop, 0, 0, 0, MT_OVERLAY); + + P_SetTarget(&backdrop->target, drop); + P_SetMobjState(backdrop, S_ITEMBACKDROP); + + backdrop->dispoffset = 1; + P_SetTarget(&backdrop->tracer, drop); + backdrop->flags2 |= MF2_LINKDRAW; + } P_SetScale(drop, drop->scale>>4); drop->destscale = (3*drop->destscale)/2; @@ -6587,9 +6600,6 @@ mobj_t *K_CreatePaperItem(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 } drop->flags |= MF_NOCLIPTHING; - backdrop->dispoffset = 1; - P_SetTarget(&backdrop->tracer, drop); - backdrop->flags2 |= MF2_LINKDRAW; if (gametyperules & GTR_CLOSERPLAYERS) { @@ -12004,8 +12014,17 @@ void K_UpdateMobjItemOverlay(mobj_t *part, SINT8 itemType, UINT8 itemCount) part->frame = FF_FULLBRIGHT|FF_PAPERSPRITE; break; default: - part->sprite = SPR_ITEM; - part->frame = FF_FULLBRIGHT|FF_PAPERSPRITE|(itemType); + if (itemType >= FIRSTPOWERUP) + { + part->sprite = SPR_PWRB; + // Not a papersprite. See K_CreatePaperItem for why. + part->frame = FF_FULLBRIGHT|(itemType - FIRSTPOWERUP); + } + else + { + part->sprite = SPR_ITEM; + part->frame = FF_FULLBRIGHT|FF_PAPERSPRITE|(itemType); + } break; } } diff --git a/src/k_kart.h b/src/k_kart.h index 3235d539a..9c9d876d8 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -150,7 +150,7 @@ void K_SpawnDriftElectricSparks(player_t *player, int color, boolean shockwave); void K_KartUpdatePosition(player_t *player); void K_UpdateAllPlayerPositions(void); SINT8 K_GetTotallyRandomResult(UINT8 useodds); -mobj_t *K_CreatePaperItem(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 flip, UINT8 type, UINT8 amount); +mobj_t *K_CreatePaperItem(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 flip, UINT8 type, UINT16 amount); void K_DropPaperItem(player_t *player, UINT8 itemtype, UINT16 itemamount); void K_PopPlayerShield(player_t *player); void K_DropItems(player_t *player); diff --git a/src/k_powerup.cpp b/src/k_powerup.cpp new file mode 100644 index 000000000..f646ec4f7 --- /dev/null +++ b/src/k_powerup.cpp @@ -0,0 +1,50 @@ +/// \brief Battle mode power-up code + +#include "k_kart.h" +#include "k_objects.h" +#include "k_powerup.h" + +tic_t K_PowerUpRemaining(const player_t* player, kartitems_t powerup) +{ + switch (powerup) + { + case POWERUP_SUPERFLICKY: + return Obj_SuperFlickySwarmTime(player->powerup.flickyController); + + default: + return 0u; + } +} + +void K_GivePowerUp(player_t* player, kartitems_t powerup, tic_t time) +{ + switch (powerup) + { + case POWERUP_SUPERFLICKY: + if (K_PowerUpRemaining(player, POWERUP_SUPERFLICKY)) + { + Obj_ExtendSuperFlickySwarm(player->powerup.flickyController, time); + } + else + { + Obj_SpawnSuperFlickySwarm(player, time); + } + break; + + default: + break; + } +} + +void K_DropPowerUps(player_t* player) +{ + if (K_PowerUpRemaining(player, POWERUP_SUPERFLICKY)) + { + mobj_t* swarm = player->powerup.flickyController; + + // Be sure to measure the remaining time before ending the power-up + K_DropPaperItem(player, POWERUP_SUPERFLICKY, Obj_SuperFlickySwarmTime(swarm)); + + Obj_EndSuperFlickySwarm(swarm); + } +} diff --git a/src/k_powerup.h b/src/k_powerup.h new file mode 100644 index 000000000..736ce68fe --- /dev/null +++ b/src/k_powerup.h @@ -0,0 +1,19 @@ +#ifndef __K_POWERUP__ +#define __K_POWERUP__ + +#include "doomtype.h" +#include "d_player.h" + +#ifdef __cplusplus +extern "C" { +#endif + +tic_t K_PowerUpRemaining(const player_t *player, kartitems_t powerup); +void K_GivePowerUp(player_t *player, kartitems_t powerup, tic_t timer); +void K_DropPowerUps(player_t *player); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // __K_POWERUP__ diff --git a/src/p_inter.c b/src/p_inter.c index 6a6aabde9..0a659f255 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -41,6 +41,7 @@ #include "k_roulette.h" #include "k_boss.h" #include "acs/interface.h" +#include "k_powerup.h" // CTF player names #define CTFTEAMCODE(pl) pl->ctfteam ? (pl->ctfteam == 1 ? "\x85" : "\x84") : "" @@ -276,14 +277,21 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) P_InstaThrust(player->mo, player->mo->angle, 20<itemamount && player->itemtype != special->threshold)) - return; - - player->itemtype = special->threshold; - if ((UINT16)(player->itemamount) + special->movecount > 255) - player->itemamount = 255; + if (special->threshold >= FIRSTPOWERUP) + { + K_GivePowerUp(player, special->threshold, special->movecount); + } else - player->itemamount += special->movecount; + { + if (!P_CanPickupItem(player, 3) || (player->itemamount && player->itemtype != special->threshold)) + return; + + player->itemtype = special->threshold; + if ((UINT16)(player->itemamount) + special->movecount > 255) + player->itemamount = 255; + else + player->itemamount += special->movecount; + } S_StartSound(special, special->info->deathsound); From 996f01f4182b83407b3088585fa9f3f95eb6833f Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 27 Jun 2023 00:47:29 -0700 Subject: [PATCH 43/62] Add K_PowerUpRemaining, K_GivePowerUp, K_DropPowerUps to Lua --- src/lua_baselib.c | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 47d0e5994..ef76bfb89 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -36,6 +36,7 @@ #include "p_spec.h" // P_StartQuake #include "i_system.h" // I_GetPreciseTime, I_GetPrecisePrecision #include "hu_stuff.h" // for the cecho +#include "k_powerup.h" #include "lua_script.h" #include "lua_libs.h" @@ -3883,6 +3884,40 @@ static int lib_kAddHitLag(lua_State *L) } +static int lib_kPowerUpRemaining(lua_State *L) +{ + player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); + kartitems_t powerup = luaL_checkinteger(L, 2); + //HUDSAFE + if (!player) + return LUA_ErrInvalid(L, "player_t"); + lua_pushinteger(L, K_PowerUpRemaining(player, powerup)); + return 1; +} + +static int lib_kGivePowerUp(lua_State *L) +{ + player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); + kartitems_t powerup = luaL_checkinteger(L, 2); + tic_t time = (tic_t)luaL_checkinteger(L, 3); + NOHUD + if (!player) + return LUA_ErrInvalid(L, "player_t"); + K_GivePowerUp(player, powerup, time); + return 0; +} + +static int lib_kDropPowerUps(lua_State *L) +{ + player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); + NOHUD + if (!player) + return LUA_ErrInvalid(L, "player_t"); + K_DropPowerUps(player); + return 0; +} + + static int lib_kInitBossHealthBar(lua_State *L) { const char *enemyname = luaL_checkstring(L, 1); @@ -4211,6 +4246,11 @@ static luaL_Reg lib[] = { {"K_GetCollideAngle",lib_kGetCollideAngle}, {"K_AddHitLag",lib_kAddHitLag}, + // k_powerup + {"K_PowerUpRemaining",lib_kPowerUpRemaining}, + {"K_GivePowerUp",lib_kGivePowerUp}, + {"K_DropPowerUps",lib_kDropPowerUps}, + // k_boss {"K_InitBossHealthBar", lib_kInitBossHealthBar}, {"K_UpdateBossHealthBar", lib_kUpdateBossHealthBar}, From 7130435667ad6ca5e681763fbb8e5a36c5dbe6f6 Mon Sep 17 00:00:00 2001 From: James R Date: Tue, 27 Jun 2023 00:39:10 -0700 Subject: [PATCH 44/62] Let give command give power-ups, amount = power-up duration in tics --- src/d_netcmd.c | 38 +++++++++++++++++++++++++++++++++++++- src/m_cheat.h | 1 + 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index f391e83c1..77d69003c 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -67,6 +67,7 @@ #include "k_vote.h" #include "k_zvote.h" #include "k_bot.h" +#include "k_powerup.h" #ifdef SRB2_CONFIG_ENABLE_WEBM_MOVIES #include "m_avrecorder.h" @@ -440,6 +441,11 @@ static CV_PossibleValue_t kartdebugitem_cons_t[] = #define FOREACH( name, n ) { n, #name } KART_ITEM_ITERATOR, #undef FOREACH + {POWERUP_SMONITOR, "SMonitor"}, + {POWERUP_BARRIER, "Barrier"}, + {POWERUP_BUMPER, "Bumper"}, + {POWERUP_BADGE, "Badge"}, + {POWERUP_SUPERFLICKY, "SuperFlicky"}, {0} }; consvar_t cv_kartdebugitem = CVAR_INIT ("debugitem", "None", CV_NETVAR|CV_CHEAT, kartdebugitem_cons_t, NULL); @@ -2072,6 +2078,11 @@ void D_Cheat(INT32 playernum, INT32 cheat, ...) COPY(WRITEUINT8, unsigned int); break; + case CHEAT_GIVEPOWERUP: + COPY(WRITEUINT8, unsigned int); + COPY(WRITEUINT16, unsigned int); + break; + case CHEAT_SCORE: COPY(WRITEUINT32, UINT32); break; @@ -6112,6 +6123,20 @@ static void Got_Cheat(UINT8 **cp, INT32 playernum) break; } + case CHEAT_GIVEPOWERUP: { + UINT8 powerup = READUINT8(*cp); + UINT16 time = READUINT16(*cp); + + // FIXME: we should have actual KITEM_ name array + const char *powerupname = cv_kartdebugitem.PossibleValue[ + 1 + NUMKARTITEMS + (powerup - FIRSTPOWERUP)].strvalue; + + K_GivePowerUp(player, powerup, time); + + CV_CheaterWarning(playernum, va("give powerup %s %d tics", powerupname, time)); + break; + } + case CHEAT_SCORE: { UINT32 score = READUINT32(*cp); @@ -6419,7 +6444,18 @@ static void Command_KartGiveItem_f(void) } } - if (item < NUMKARTITEMS) + if (item >= FIRSTPOWERUP) + { + INT32 amt; + + if (ac > 2) + amt = atoi(COM_Argv(2)); + else + amt = BATTLE_POWERUP_TIME; + + D_Cheat(consoleplayer, CHEAT_GIVEPOWERUP, item, amt); + } + else if (item < NUMKARTITEMS) { INT32 amt; diff --git a/src/m_cheat.h b/src/m_cheat.h index d8b21d477..bf7fc79be 100644 --- a/src/m_cheat.h +++ b/src/m_cheat.h @@ -39,6 +39,7 @@ typedef enum { CHEAT_SCORE, CHEAT_ANGLE, CHEAT_RESPAWNAT, + CHEAT_GIVEPOWERUP, NUMBER_OF_CHEATS } cheat_t; From e75c052e7b6224af2eaf6f193e99ee25939cc6bc Mon Sep 17 00:00:00 2001 From: AJ Martinez Date: Tue, 27 Jun 2023 02:20:20 -0700 Subject: [PATCH 45/62] Force MT_RANDOMITEM to start S_RANDOMITEM in Prisons --- src/objects/random-item.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/objects/random-item.c b/src/objects/random-item.c index 7c19a4228..f392d9752 100644 --- a/src/objects/random-item.c +++ b/src/objects/random-item.c @@ -172,6 +172,14 @@ void Obj_RandomItemSpawn(mobj_t *mobj) { item_vfxtimer(mobj) = P_RandomRange(PR_DECORATION, 0, SCALE_TIME - 1); + // Respawns are handled by P_RespawnBattleBoxes, + // but we do need to start MT_RANDOMITEMs in the right state... + if (mobj->type == MT_RANDOMITEM && (gametyperules & GTR_BUMPERS)) + { + mobj->extravalue1 = RINGBOX_TIME; + P_SetMobjState(mobj, S_RANDOMITEM1); + } + mobj->destscale = Obj_RandomItemScale(mobj->destscale); P_SetScale(mobj, mobj->destscale); } From 07fa5fff01462eaad915442928d14d47136189a3 Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 27 Jun 2023 13:28:12 +0100 Subject: [PATCH 46/62] M_DrawPause: Make the visual case for user-selectable options more flexible/less specific to the gametype changer it was implemented for. A surprise tool that will help us later --- src/k_menudraw.c | 85 +++++++++++++++++++++++++++--------------------- 1 file changed, 48 insertions(+), 37 deletions(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index d01bd593d..6df9183da 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -4262,11 +4262,6 @@ void M_DrawPause(void) INT16 arrxpos = 150 + 2*offset; // To draw the background arrow. INT16 j = 0; - char word1[MAXSTRINGLENGTH]; - INT16 word1len = 0; - char word2[MAXSTRINGLENGTH]; - INT16 word2len = 0; - boolean sok = false; patch_t *vertbg = W_CachePatchName("M_STRIPV", PU_CACHE); patch_t *arrstart = W_CachePatchName("M_PTIP", PU_CACHE); @@ -4364,48 +4359,64 @@ void M_DrawPause(void) } // Draw the string! - // ...but first get what we need to get. - while (currentMenu->menuitems[itemOn].text[j] && j < MAXSTRINGLENGTH) - { - char c = currentMenu->menuitems[itemOn].text[j]; - - if (c == ' ' && !sok) - { - sok = true; - j++; - continue; // We don't care about this :moyai: - } - - if (sok) - { - word2[word2len] = c; - word2len++; - } - else - { - word1[word1len] = c; - word1len++; - } - - j++; - } - - word1[word1len] = '\0'; - word2[word2len] = '\0'; + const char *selectabletext = NULL; if (itemOn == mpause_changegametype) { - INT32 w = V_LSTitleLowStringWidth(gametypes[menugametype]->name, 0)/2; + selectabletext = gametypes[menugametype]->name; + } - if (word1len) - V_DrawCenteredLSTitleHighString(220 + offset*2, 75, 0, word1); + if (selectabletext != NULL) + { + // We have a selection. Let's show the full menu text on top, and the choice below. - V_DrawLSTitleLowString(220-w + offset*2, 103, V_YELLOWMAP, gametypes[menugametype]->name); + INT32 w = V_LSTitleLowStringWidth(selectabletext, 0)/2; + + if (currentMenu->menuitems[itemOn].text) + V_DrawCenteredLSTitleHighString(220 + offset*2, 75, 0, currentMenu->menuitems[itemOn].text); + + V_DrawLSTitleLowString(220-w + offset*2, 103, V_YELLOWMAP, selectabletext); V_DrawCharacter(220-w + offset*2 - 8 - (skullAnimCounter/5), 103+6, '\x1C' | V_YELLOWMAP, false); // left arrow V_DrawCharacter(220+w + offset*2 + 4 + (skullAnimCounter/5), 103+6, '\x1D' | V_YELLOWMAP, false); // right arrow } else { + // This is a regular menu option. Try to break it onto two lines. + + char word1[MAXSTRINGLENGTH]; + INT16 word1len = 0; + char word2[MAXSTRINGLENGTH]; + INT16 word2len = 0; + boolean sok = false; + + while (currentMenu->menuitems[itemOn].text[j] && j < MAXSTRINGLENGTH) + { + const char c = currentMenu->menuitems[itemOn].text[j]; + + if (c == ' ' && !sok) + { + sok = true; + j++; + continue; // We don't care about this :moyai: + } + + if (sok) + { + word2[word2len] = c; + word2len++; + } + else + { + word1[word1len] = c; + word1len++; + } + + j++; + } + + word1[word1len] = '\0'; + word2[word2len] = '\0'; + // If there's no 2nd word, take this opportunity to center this line of text. if (word1len) V_DrawCenteredLSTitleHighString(220 + offset*2, 75 + (!word2len ? 10 : 0), 0, word1); From ec8a6247c22c8dff4178a252865bc891bce83826 Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 27 Jun 2023 14:16:48 +0100 Subject: [PATCH 47/62] Z-vote is now server-authoriative - Only calls callback if you're the server (and not demo.playback, forward thinking for stored xcmd netreplays) - G_GamestateUsesExitLevel() for homogenising the conditions that permit XD_EXITLEVEL to be dispatched and recieved --- src/d_netcmd.c | 21 ++++++++++++++++++++- src/d_netcmd.h | 1 + src/k_zvote.c | 19 +++++++++++++------ 3 files changed, 34 insertions(+), 7 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index f391e83c1..63781835d 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -5646,6 +5646,22 @@ static void Command_Mapmd5_f(void) CONS_Printf(M_GetText("You must be in a level to use this.\n")); } +boolean G_GamestateUsesExitLevel(void) +{ + if (demo.playback) + return false; + + switch (gamestate) + { + case GS_LEVEL: + case GS_CREDITS: + return true; + + default: + return false; + } +} + static void Command_ExitLevel_f(void) { if (!(server || (IsPlayerAdmin(consoleplayer)))) @@ -5656,7 +5672,7 @@ static void Command_ExitLevel_f(void) { CONS_Printf(M_GetText("This cannot be used without cheats enabled.\n")); } - else if (( gamestate != GS_LEVEL && gamestate != GS_CREDITS ) || demo.playback) + else if (G_GamestateUsesExitLevel() == false) { CONS_Printf(M_GetText("You must be in a level to use this.\n")); } @@ -5682,6 +5698,9 @@ static void Got_ExitLevelcmd(UINT8 **cp, INT32 playernum) return; } + if (G_GamestateUsesExitLevel() == false) + return; + G_ExitLevel(); } diff --git a/src/d_netcmd.h b/src/d_netcmd.h index c9938b4e5..621179d86 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -241,6 +241,7 @@ void WeaponPref_Parse(UINT8 **cp, INT32 playernum); void D_SendPlayerConfig(UINT8 n); void Command_ExitGame_f(void); void Command_Retry_f(void); +boolean G_GamestateUsesExitLevel(void); void D_GameTypeChanged(INT32 lastgametype); // not a real _OnChange function anymore void D_MapChange(UINT16 pmapnum, INT32 pgametype, boolean pencoremode, boolean presetplayers, INT32 pdelay, boolean pskipprecutscene, boolean pforcespecialstage); void D_SetupVote(void); diff --git a/src/k_zvote.c b/src/k_zvote.c index 9958e58b7..9c933d734 100644 --- a/src/k_zvote.c +++ b/src/k_zvote.c @@ -57,10 +57,7 @@ static void K_MidVoteKick(void) return; } - if (server) - { - SendKick(g_midVote.victim - players, KICK_MSG_VOTE_KICK); - } + SendKick(g_midVote.victim - players, KICK_MSG_VOTE_KICK); } /*-------------------------------------------------- @@ -70,7 +67,13 @@ static void K_MidVoteKick(void) --------------------------------------------------*/ static void K_MidVoteRockTheVote(void) { - G_ExitLevel(); + if (G_GamestateUsesExitLevel() == false) + { + return; + } + + SendNetXCmd(XD_EXITLEVEL, NULL, 0); +} } static midVoteTypeDef_t g_midVoteTypeDefs[MVT__MAX] = @@ -630,7 +633,11 @@ void K_MidVoteFinalize(fixed_t delayMul) --------------------------------------------------*/ void K_MidVoteSuccess(void) { - if (g_midVoteTypeDefs[ g_midVote.type ].callback != NULL) + if ( + server == true + && demo.playback == false + && g_midVoteTypeDefs[ g_midVote.type ].callback != NULL + ) { g_midVoteTypeDefs[ g_midVote.type ].callback(); } From ddce2e9a2da7ccb8e9136d7e38b2012c55d27856 Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 27 Jun 2023 14:24:45 +0100 Subject: [PATCH 48/62] zvote_call runitback - "Redo Level?" - Functionally `restartlevel` but votable --- src/k_zvote.c | 23 +++++++++++++++++++++++ src/k_zvote.h | 1 + 2 files changed, 24 insertions(+) diff --git a/src/k_zvote.c b/src/k_zvote.c index 9c933d734..b0d1991b6 100644 --- a/src/k_zvote.c +++ b/src/k_zvote.c @@ -74,6 +74,22 @@ static void K_MidVoteRockTheVote(void) SendNetXCmd(XD_EXITLEVEL, NULL, 0); } + +/*-------------------------------------------------- + static void K_MidVoteRunItBack(void) + + MVT_RUNITBACK's success function. +--------------------------------------------------*/ +static void K_MidVoteRunItBack(void) +{ + boolean newencore = false; + + if (cv_kartencore.value != 0) + { + newencore = (cv_kartencore.value == 1) || encoremode; + } + + D_MapChange(gamemap, gametype, newencore, false, 0, false, false); } static midVoteTypeDef_t g_midVoteTypeDefs[MVT__MAX] = @@ -91,6 +107,13 @@ static midVoteTypeDef_t g_midVoteTypeDefs[MVT__MAX] = CVAR_INIT ("zvote_rtv_allowed", "No", CV_SAVE|CV_NETVAR, CV_YesNo, NULL), K_MidVoteRockTheVote }, + + { // MVT_RUNITBACK + "RUNITBACK", + "Redo Level?", + CVAR_INIT ("zvote_runitback_allowed", "No", CV_SAVE|CV_NETVAR, CV_YesNo, NULL), + K_MidVoteRunItBack + }, }; /*-------------------------------------------------- diff --git a/src/k_zvote.h b/src/k_zvote.h index 7296770aa..f1d57a546 100644 --- a/src/k_zvote.h +++ b/src/k_zvote.h @@ -28,6 +28,7 @@ typedef enum { MVT_KICK, // Kick another player in the server MVT_RTV, // Exit level early + MVT_RUNITBACK, // Restart level fresh MVT__MAX, // Total number of vote types } midVoteType_e; From 34563ebecd4d4e4a0bc7f0d38b2e24e52299950b Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 27 Jun 2023 17:30:43 +0100 Subject: [PATCH 49/62] Got_KickCmd: Only PROMOTE any regular kick to votekick if that player's the victim, don't downgrade a ban --- src/d_clisrv.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 2849589ba..e5dde4921 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -3255,7 +3255,11 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) { // Running the callback here would mean a very dumb infinite loop. // We'll manually handle this here by changing the msg type. - msg = KICK_MSG_VOTE_KICK; + if (msg != KICK_MSG_BANNED && msg != KICK_MSG_CUSTOM_BAN) + { + // of course, don't take the teeth out of a ban + msg = KICK_MSG_VOTE_KICK; + } K_MidVoteFinalize(FRACUNIT); // Vote succeeded, so the delay is normal. } else From 15ff670a48046651c40896b4939fdd5b468fe885 Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 27 Jun 2023 17:32:03 +0100 Subject: [PATCH 50/62] Reduce space width of Level Select high/low fonts - fancy fonts shouldn't be monospace --- src/v_video.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/v_video.cpp b/src/v_video.cpp index d25778061..ae65de142 100644 --- a/src/v_video.cpp +++ b/src/v_video.cpp @@ -2286,7 +2286,7 @@ void V_DrawStringScaled( break; case LSHI_FONT: case LSLOW_FONT: - spacew = 16; + spacew = 10; break; case OPPRF_FONT: spacew = 5; @@ -2579,7 +2579,7 @@ fixed_t V_StringScaledWidth( break; case LSHI_FONT: case LSLOW_FONT: - spacew = 16; + spacew = 10; break; case OPPRF_FONT: spacew = 5; From 8a4cca8518de90a97c170ad9f4198ed7cf6d1b4c Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 27 Jun 2023 17:33:01 +0100 Subject: [PATCH 51/62] Seperate out K_SendCallMidVote from Command_CallVote --- src/k_zvote.c | 31 ++++++++++++++++++++++--------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/src/k_zvote.c b/src/k_zvote.c index b0d1991b6..94fffb720 100644 --- a/src/k_zvote.c +++ b/src/k_zvote.c @@ -151,9 +151,6 @@ static boolean K_MidVoteTypeUsesVictim(midVoteType_e voteType) --------------------------------------------------*/ static void Command_CallVote(void) { - UINT8 buf[MAXTEXTCMD]; - UINT8 *buf_p = buf; - size_t numArgs = 0; const char *voteTypeStr = NULL; @@ -162,8 +159,6 @@ static void Command_CallVote(void) const char *voteVariableStr = NULL; INT32 voteVariable = 0; - player_t *victim = NULL; - INT32 i = INT32_MAX; if (netgame == false) @@ -210,11 +205,26 @@ static void Command_CallVote(void) break; } } + } + } - if (voteVariable >= 0 && voteVariable < MAXPLAYERS) - { - victim = &players[voteVariable]; - } + K_SendCallMidVote(voteType, voteVariable); +} + +/*-------------------------------------------------- + void K_SendCallMidVote(midVoteType_e voteType, INT32 voteVariable) + + See header file for description. +--------------------------------------------------*/ +void K_SendCallMidVote(midVoteType_e voteType, INT32 voteVariable) +{ + player_t *victim = NULL; + + if (K_MidVoteTypeUsesVictim(voteType) == true) + { + if (voteVariable >= 0 && voteVariable < MAXPLAYERS) + { + victim = &players[voteVariable]; } } @@ -224,6 +234,9 @@ static void Command_CallVote(void) return; } + UINT8 buf[MAXTEXTCMD]; + UINT8 *buf_p = buf; + WRITEUINT8(buf_p, voteType); WRITEINT32(buf_p, voteVariable); From 721d5d9da99e0cc544849270d1da97bd3d25cfd8 Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 27 Jun 2023 17:45:27 +0100 Subject: [PATCH 52/62] Improve M_HandlePauseMenuGametype and M_HandleHostMenuGametype Simplify massively by using IT_ARROWS --- src/k_menudraw.c | 2 +- src/menus/play-online-1.c | 2 +- src/menus/play-online-host.c | 38 ++++++++------------------------ src/menus/transient/pause-game.c | 34 ++++++++++++++-------------- 4 files changed, 27 insertions(+), 49 deletions(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 6df9183da..bd2ae614a 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -3065,7 +3065,7 @@ void M_DrawMPHost(void) } break; } - case IT_KEYHANDLER: + case IT_ARROWS: { if (currentMenu->menuitems[i].itemaction.routine != M_HandleHostMenuGametype) break; diff --git a/src/menus/play-online-1.c b/src/menus/play-online-1.c index 8a9b74234..7f9a9c294 100644 --- a/src/menus/play-online-1.c +++ b/src/menus/play-online-1.c @@ -80,8 +80,8 @@ void M_MPOptSelectInit(INT32 choice) memcpy(&mpmenu.modewinextend, &arrcpy, sizeof(mpmenu.modewinextend)); // Guarantee menugametype is good - M_NextMenuGametype(forbidden); M_PrevMenuGametype(forbidden); + M_NextMenuGametype(forbidden); if (choice != -1) { diff --git a/src/menus/play-online-host.c b/src/menus/play-online-host.c index 7ab684c5c..a7061a32a 100644 --- a/src/menus/play-online-host.c +++ b/src/menus/play-online-host.c @@ -18,7 +18,7 @@ menuitem_t PLAY_MP_Host[] = {IT_STRING | IT_CVAR, "Max. Players", "Set how many players can play at once. Others will spectate.", NULL, {.cvar = &cv_maxplayers}, 0, 0}, - {IT_STRING | IT_KEYHANDLER, "Gamemode", "Choose the type of play on your server.", + {IT_STRING | IT_ARROWS, "Gamemode", "Choose the type of play on your server.", NULL, {.routine = M_HandleHostMenuGametype}, 0, 0}, {IT_STRING | IT_CALL, "GO", "Select a map with the currently selected gamemode", @@ -54,41 +54,21 @@ void M_MPHostInit(INT32 choice) void M_HandleHostMenuGametype(INT32 choice) { - const UINT8 pid = 0; const UINT32 forbidden = GTR_FORBIDMP; - (void)choice; - - if (M_MenuBackPressed(pid)) - { - M_GoBack(0); - M_SetMenuDelay(pid); - return; - } - else if (menucmd[pid].dpad_lr > 0 || M_MenuConfirmPressed(pid)) + if (choice > 0) { M_NextMenuGametype(forbidden); - S_StartSound(NULL, sfx_s3k5b); - M_SetMenuDelay(pid); } - else if (menucmd[pid].dpad_lr < 0) + else if (choice == -1) + { + menugametype = GT_RACE; + M_PrevMenuGametype(forbidden); + M_NextMenuGametype(forbidden); + } + else { M_PrevMenuGametype(forbidden); - S_StartSound(NULL, sfx_s3k5b); - M_SetMenuDelay(pid); - } - - if (menucmd[pid].dpad_ud > 0) - { - M_NextOpt(); - S_StartSound(NULL, sfx_s3k5b); - M_SetMenuDelay(pid); - } - else if (menucmd[pid].dpad_ud < 0) - { - M_PrevOpt(); - S_StartSound(NULL, sfx_s3k5b); - M_SetMenuDelay(pid); } } diff --git a/src/menus/transient/pause-game.c b/src/menus/transient/pause-game.c index c3b640ea8..e43f05d5d 100644 --- a/src/menus/transient/pause-game.c +++ b/src/menus/transient/pause-game.c @@ -22,7 +22,7 @@ menuitem_t PAUSE_Main[] = {IT_STRING | IT_CALL, "STEREO MODE", "M_ICOSTM", NULL, {.routine = M_SoundTest}, 0, 0}, - {IT_STRING | IT_KEYHANDLER, "GAMETYPE", "M_ICOGAM", + {IT_STRING | IT_ARROWS, "GAMETYPE", "M_ICOGAM", NULL, {.routine = M_HandlePauseMenuGametype}, 0, 0}, {IT_STRING | IT_CALL, "CHANGE MAP", "M_ICOMAP", @@ -71,7 +71,7 @@ menu_t PAUSE_MainDef = { PAUSE_Main, 0, 0, 0, 0, - 0, + MBF_SOUNDLESS, NULL, 1, 10, // For transition with some menus! M_DrawPause, @@ -146,7 +146,7 @@ void M_OpenPauseMenu(void) if (server || IsPlayerAdmin(consoleplayer)) { - PAUSE_Main[mpause_changegametype].status = IT_STRING | IT_KEYHANDLER; + PAUSE_Main[mpause_changegametype].status = IT_STRING | IT_ARROWS; menugametype = gametype; PAUSE_Main[mpause_switchmap].status = IT_STRING | IT_CALL; @@ -267,12 +267,9 @@ boolean M_PauseInputs(INT32 ch) // Change gametype void M_HandlePauseMenuGametype(INT32 choice) { - const UINT8 pid = 0; const UINT32 forbidden = GTR_FORBIDMP; - (void)choice; - - if (M_MenuConfirmPressed(pid)) + if (choice == 2) { if (menugametype != gametype) { @@ -281,26 +278,27 @@ void M_HandlePauseMenuGametype(INT32 choice) return; } - M_SetMenuDelay(pid); S_StartSound(NULL, sfx_s3k7b); + + return; } - else if (M_MenuExtraPressed(pid)) + + if (choice == -1) { menugametype = gametype; - M_SetMenuDelay(pid); S_StartSound(NULL, sfx_s3k7b); + return; } - else if (menucmd[pid].dpad_lr > 0) - { - M_NextMenuGametype(forbidden); - S_StartSound(NULL, sfx_s3k5b); - M_SetMenuDelay(pid); - } - else if (menucmd[pid].dpad_lr < 0) + + if (choice == 0) { M_PrevMenuGametype(forbidden); S_StartSound(NULL, sfx_s3k5b); - M_SetMenuDelay(pid); + } + else + { + M_NextMenuGametype(forbidden); + S_StartSound(NULL, sfx_s3k5b); } } From d8ea8c0fe1fd461ede5c8f597e7f090c30e4c655 Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 27 Jun 2023 17:53:59 +0100 Subject: [PATCH 53/62] Implement "CALL VOTE" for victimless commands on the pause menu Currently just plays the squishy "nope" sound for "KICK PLAYER?", all others are functional Shows whether vote is active or the cooldown is too recent (via numerical timer) as reasons for why you can't select it --- src/k_menu.h | 4 ++ src/k_menudraw.c | 64 +++++++++++++---- src/k_zvote.c | 118 +++++++++++++++++++++++++++---- src/k_zvote.h | 74 +++++++++++++++++++ src/menus/transient/pause-game.c | 49 +++++++++++++ 5 files changed, 281 insertions(+), 28 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index 7eefd06ea..b4ce7c6bd 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -126,6 +126,9 @@ void M_PrevMenuGametype(UINT32 forbidden); void M_HandleHostMenuGametype(INT32 choice); void M_HandlePauseMenuGametype(INT32 choice); +extern UINT32 menucallvote; // not midVoteType_e to prevent #include k_zvote +void M_HandlePauseMenuCallVote(INT32 choice); + // // MENU TYPEDEFS // @@ -447,6 +450,7 @@ typedef enum #ifdef HAVE_DISCORDRPC mpause_discordrequests, #endif + mpause_callvote, mpause_continue, mpause_spectate, diff --git a/src/k_menudraw.c b/src/k_menudraw.c index bd2ae614a..f89eb0db8 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -52,6 +52,7 @@ #include "doomstat.h" // MAXSPLITSCREENPLAYERS #include "k_grandprix.h" // K_CanChangeRules #include "k_rank.h" // K_GetGradeColor +#include "k_zvote.h" // K_GetMidVoteLabel #include "y_inter.h" // Y_RoundQueueDrawer @@ -4360,26 +4361,61 @@ void M_DrawPause(void) // Draw the string! + const char *maintext = NULL; const char *selectabletext = NULL; + INT32 mainflags = V_YELLOWMAP, selectableflags = 0; + if (itemOn == mpause_changegametype) { selectabletext = gametypes[menugametype]->name; } + else if (itemOn == mpause_callvote) + { + selectabletext = K_GetMidVoteLabel(menucallvote); + + if (K_MinimalCheckNewMidVote(menucallvote) == false) + { + if (g_midVote.active == true) + { + maintext = "ACTIVE..."; + } + else if (g_midVote.delay > 0) + { + if (g_midVote.delay != 1) + maintext = va("%u", ((g_midVote.delay - 1) / TICRATE) + 1); + } + else + { + maintext = "INVALID!?"; + } + + if (maintext != NULL) + selectableflags |= V_MODULATE; + } + } + else + { + maintext = currentMenu->menuitems[itemOn].text; + mainflags = 0; + } if (selectabletext != NULL) { // We have a selection. Let's show the full menu text on top, and the choice below. - INT32 w = V_LSTitleLowStringWidth(selectabletext, 0)/2; - if (currentMenu->menuitems[itemOn].text) - V_DrawCenteredLSTitleHighString(220 + offset*2, 75, 0, currentMenu->menuitems[itemOn].text); + V_DrawCenteredLSTitleHighString(220 + offset*2, 75, selectableflags, currentMenu->menuitems[itemOn].text); - V_DrawLSTitleLowString(220-w + offset*2, 103, V_YELLOWMAP, selectabletext); - V_DrawCharacter(220-w + offset*2 - 8 - (skullAnimCounter/5), 103+6, '\x1C' | V_YELLOWMAP, false); // left arrow - V_DrawCharacter(220+w + offset*2 + 4 + (skullAnimCounter/5), 103+6, '\x1D' | V_YELLOWMAP, false); // right arrow + selectableflags |= V_YELLOWMAP; + + INT32 w = V_LSTitleLowStringWidth(selectabletext, selectableflags)/2; + V_DrawLSTitleLowString(220-w + offset*2, 103, selectableflags, selectabletext); + + V_DrawCharacter(220-w + offset*2 - 8 - (skullAnimCounter/5), 103+6, '\x1C' | selectableflags, false); // left arrow + V_DrawCharacter(220+w + offset*2 + (skullAnimCounter/5), 103+6, '\x1D' | selectableflags, false); // right arrow } - else + + if (maintext != NULL) { // This is a regular menu option. Try to break it onto two lines. @@ -4389,11 +4425,9 @@ void M_DrawPause(void) INT16 word2len = 0; boolean sok = false; - while (currentMenu->menuitems[itemOn].text[j] && j < MAXSTRINGLENGTH) + while (maintext[j] && j < MAXSTRINGLENGTH) { - const char c = currentMenu->menuitems[itemOn].text[j]; - - if (c == ' ' && !sok) + if (maintext[j] == ' ' && !sok) { sok = true; j++; @@ -4402,12 +4436,12 @@ void M_DrawPause(void) if (sok) { - word2[word2len] = c; + word2[word2len] = maintext[j]; word2len++; } else { - word1[word1len] = c; + word1[word1len] = maintext[j]; word1len++; } @@ -4419,10 +4453,10 @@ void M_DrawPause(void) // If there's no 2nd word, take this opportunity to center this line of text. if (word1len) - V_DrawCenteredLSTitleHighString(220 + offset*2, 75 + (!word2len ? 10 : 0), 0, word1); + V_DrawCenteredLSTitleHighString(220 + offset*2, 75 + (!word2len ? 10 : 0), mainflags, word1); if (word2len) - V_DrawCenteredLSTitleLowString(220 + offset*2, 103, 0, word2); + V_DrawCenteredLSTitleLowString(220 + offset*2, 103, mainflags, word2); } if (gamestate != GS_INTERMISSION && roundqueue.size > 0) diff --git a/src/k_zvote.c b/src/k_zvote.c index 94fffb720..8f6ba9e95 100644 --- a/src/k_zvote.c +++ b/src/k_zvote.c @@ -117,19 +117,11 @@ static midVoteTypeDef_t g_midVoteTypeDefs[MVT__MAX] = }; /*-------------------------------------------------- - static boolean K_MidVoteTypeUsesVictim(midVoteType_e voteType) + boolean K_MidVoteTypeUsesVictim(midVoteType_e voteType) - Specifies whenever or not a vote type is intended - to specify a "victim", or a player that would be - negatively affected by the vote. - - Input Arguments:- - voteType - The vote type to check. - - Return:- - true if it uses a victim, otherwise false. + See header file for description. --------------------------------------------------*/ -static boolean K_MidVoteTypeUsesVictim(midVoteType_e voteType) +boolean K_MidVoteTypeUsesVictim(midVoteType_e voteType) { switch (voteType) { @@ -380,6 +372,51 @@ boolean K_AnyMidVotesAllowed(void) return false; } +/*-------------------------------------------------- + midVoteType_e K_GetNextCallableMidVote(INT32 seed, boolean backwards) + + See header file for description. +--------------------------------------------------*/ + +midVoteType_e K_GetNextAllowedMidVote(midVoteType_e seed, boolean backwards) +{ + if (seed >= MVT__MAX) + seed = 0; + + midVoteType_e i = seed; + + if (backwards) + { + do + { + if (i <= 0) + i = MVT__MAX; + i--; + + if (g_midVoteTypeDefs[i].cv_allowed.value != 0) + return i; + + } + while (i != seed); + } + else + { + do + { + i++; + if (i >= MVT__MAX) + i = 0; + + if (g_midVoteTypeDefs[i].cv_allowed.value != 0) + return i; + + } + while (i != seed); + } + + return MVT__MAX; +} + /*-------------------------------------------------- boolean K_PlayerIDAllowedInMidVote(const UINT8 id) @@ -507,6 +544,40 @@ UINT8 K_CountMidVotes(void) return voteCount; } +/*-------------------------------------------------- + boolean K_MinimalCheckNewMidVote(midVoteType_e type) + + See header file for description. +--------------------------------------------------*/ +boolean K_MinimalCheckNewMidVote(midVoteType_e type) +{ + if (g_midVote.active == true) + { + // Don't allow another vote if one is already running. + return false; + } + + if (g_midVote.delay > 0) + { + // Don't allow another vote if one has recently just ran. + return false; + } + + if (type < 0 || type >= MVT__MAX) + { + // Invalid range. + return false; + } + + if (g_midVoteTypeDefs[type].cv_allowed.value == 0) + { + // These types of votes aren't allowed on this server. + return false; + } + + return true; +} + /*-------------------------------------------------- boolean K_AllowNewMidVote(player_t *caller, midVoteType_e type, INT32 variable, player_t *victim) @@ -866,6 +937,25 @@ void K_UpdateMidVotePatches(void) HU_UpdatePatch(&g_zBarEnds[1][1][1], "TLBXB0"); } +/*-------------------------------------------------- + const char *K_GetMidVoteLabel(midVoteType_e i) + + See header file for description. +--------------------------------------------------*/ + +const char *K_GetMidVoteLabel(midVoteType_e i) +{ + if ( + i < 0 + || i >= MVT__MAX + || g_midVoteTypeDefs[i].label == NULL) + { + return "N/A"; + } + + return g_midVoteTypeDefs[i].label; +} + /*-------------------------------------------------- static void K_DrawMidVoteBar(fixed_t x, fixed_t y, INT32 flags, fixed_t fill, skincolornum_t color, boolean flipped) @@ -1049,11 +1139,13 @@ void K_DrawMidVote(void) (id & 1) ); + const char *label = K_GetMidVoteLabel(g_midVote.type); + // Vote main label strWidth = V__OneScaleStringWidth( FRACUNIT, V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_SPLITSCREEN, - KART_FONT, g_midVoteTypeDefs[g_midVote.type].label + KART_FONT, label ); V__DrawOneScaleString( @@ -1061,7 +1153,7 @@ void K_DrawMidVote(void) y - (18 * FRACUNIT), FRACUNIT, V_SNAPTOBOTTOM|V_SNAPTORIGHT|V_SPLITSCREEN, NULL, - KART_FONT, g_midVoteTypeDefs[g_midVote.type].label + KART_FONT, label ); // Vote extra text diff --git a/src/k_zvote.h b/src/k_zvote.h index f1d57a546..61ee8c630 100644 --- a/src/k_zvote.h +++ b/src/k_zvote.h @@ -64,6 +64,39 @@ struct midVote_t extern midVote_t g_midVote; +/*-------------------------------------------------- + boolean K_MidVoteTypeUsesVictim(midVoteType_e voteType) + + Specifies whenever or not a vote type is intended + to specify a "victim", or a player that would be + negatively affected by the vote. + + Input Arguments:- + voteType - The vote type to check. + + Return:- + true if it uses a victim, otherwise false. +--------------------------------------------------*/ + +boolean K_MidVoteTypeUsesVictim(midVoteType_e voteType); + + +/*-------------------------------------------------- + void K_SendCallMidVote(midVoteType_e voteType, INT32 voteVariable) + + Prepares and sends net packet for calling a midvote. + + Input Arguments:- + voteType - The type of vote a local player is trying to call. + variable - Extra arguments for the vote type. + + Return:- + N/A +--------------------------------------------------*/ + +void K_SendCallMidVote(midVoteType_e voteType, INT32 voteVariable); + + /*-------------------------------------------------- void K_RegisterMidVoteCVars(void); @@ -100,6 +133,22 @@ void K_ResetMidVote(void); boolean K_AnyMidVotesAllowed(void); +/*-------------------------------------------------- + midVoteType_e K_GetNextCallableMidVote(midVoteType_e seed, boolean backwards) + + Gets the next enabled Z-vote type in the list. + + Input Arguments:- + seed - position in the list to start with + backwards - if true, traverses list in reverse order + + Return:- + next Z-vote id if any vote types are enabled, otherwise MVT__MAX. +--------------------------------------------------*/ + +midVoteType_e K_GetNextAllowedMidVote(midVoteType_e seed, boolean backwards); + + /*-------------------------------------------------- boolean K_PlayerIDAllowedInMidVote(const UINT8 id); @@ -171,6 +220,18 @@ boolean K_PlayerIDMidVoted(const UINT8 id); UINT8 K_CountMidVotes(void); +/*-------------------------------------------------- + boolean K_MinimalCheckNewMidVote(midVoteType_e type) + + Returns if the variables given are a valid state for + pause menu Z-vote flow. + + Input Arguments:- + type - The type of vote they're trying to call. +--------------------------------------------------*/ + +boolean K_MinimalCheckNewMidVote(midVoteType_e type); + /*-------------------------------------------------- boolean K_AllowNewMidVote(player_t *caller, midVoteType_e type, INT32 variable, player_t *victim); @@ -264,6 +325,19 @@ void K_TickMidVote(void); void K_UpdateMidVotePatches(void); +/*-------------------------------------------------- + const char *K_GetMidVoteLabel(midVoteType_e i) + + Input Arguments:- + i - id in the list to retrieve label for + + Return:- + label associated with that id, or a sensible default (not NULL) +--------------------------------------------------*/ + +const char *K_GetMidVoteLabel(midVoteType_e i); + + /*-------------------------------------------------- void K_DrawMidVote(void); diff --git a/src/menus/transient/pause-game.c b/src/menus/transient/pause-game.c index e43f05d5d..cac1c1280 100644 --- a/src/menus/transient/pause-game.c +++ b/src/menus/transient/pause-game.c @@ -5,6 +5,7 @@ #include "../../k_grandprix.h" // K_CanChangeRules #include "../../m_cond.h" #include "../../s_sound.h" +#include "../../k_zvote.h" #ifdef HAVE_DISCORDRPC #include "../../discord.h" @@ -39,6 +40,9 @@ menuitem_t PAUSE_Main[] = NULL, {.routine = M_DiscordRequests}, 0, 0}, #endif + {IT_STRING | IT_ARROWS, "CALL VOTE", "M_ICOVOT", + NULL, {.routine = M_HandlePauseMenuCallVote}, 0, 0}, + {IT_STRING | IT_CALL, "RESUME GAME", "M_ICOUNP", NULL, {.routine = M_QuitPauseMenu}, 0, 0}, @@ -123,6 +127,7 @@ void M_OpenPauseMenu(void) PAUSE_Main[mpause_switchmap].status = IT_DISABLED; PAUSE_Main[mpause_restartmap].status = IT_DISABLED; PAUSE_Main[mpause_tryagain].status = IT_DISABLED; + PAUSE_Main[mpause_callvote].status = IT_DISABLED; #ifdef HAVE_DISCORDRPC PAUSE_Main[mpause_discordrequests].status = IT_DISABLED; #endif @@ -178,6 +183,18 @@ void M_OpenPauseMenu(void) } } + if (netgame) + { + menucallvote = K_GetNextAllowedMidVote(menucallvote, true); + + if (menucallvote != MVT__MAX) + { + menucallvote = K_GetNextAllowedMidVote(menucallvote, false); + + PAUSE_Main[mpause_callvote].status = IT_STRING | IT_ARROWS; + } + } + if (G_GametypeHasSpectators()) { if (splitscreen) @@ -302,6 +319,38 @@ void M_HandlePauseMenuGametype(INT32 choice) } } +// Call vote +UINT32 menucallvote = MVT__MAX; + +void M_HandlePauseMenuCallVote(INT32 choice) +{ + if (choice == 2) + { + if (K_MinimalCheckNewMidVote(menucallvote) == false) + { + // Invalid. + S_StartSound(NULL, sfx_s3k7b); + } + else if (K_MidVoteTypeUsesVictim(menucallvote) == true) + { + // Not yet implemented. + S_StartSound(NULL, sfx_s3k7b); + } + else + { + // Bog standard and victimless, let's send it on its way! + M_ClearMenus(true); + K_SendCallMidVote(menucallvote, 0); + return; + } + + return; + } + + menucallvote = K_GetNextAllowedMidVote(menucallvote, (choice == 0)); + S_StartSound(NULL, sfx_s3k5b); +} + // Restart map void M_RestartMap(INT32 choice) { From 9372f8ff1a5dcfbe18cf78f35f006d5567525626 Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 27 Jun 2023 23:03:05 +0100 Subject: [PATCH 54/62] Conditionset is UINT16, not UINT8 --- src/deh_soc.c | 2 +- src/deh_soc.h | 2 +- src/dehacked.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/deh_soc.c b/src/deh_soc.c index eb30c33ed..c9c457383 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -2859,7 +2859,7 @@ static void readcondition(UINT8 set, UINT32 id, char *word2) M_AddRawCondition(set, (UINT8)id, ty, re, x1, x2, stringvar); } -void readconditionset(MYFILE *f, UINT8 setnum) +void readconditionset(MYFILE *f, UINT16 setnum) { char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); char *word = s; diff --git a/src/deh_soc.h b/src/deh_soc.h index cd20b7714..e1d5e9a32 100644 --- a/src/deh_soc.h +++ b/src/deh_soc.h @@ -62,7 +62,7 @@ skincolornum_t get_skincolor(const char *word); void readwipes(MYFILE *f); void readmaincfg(MYFILE *f, boolean mainfile); -void readconditionset(MYFILE *f, UINT8 setnum); +void readconditionset(MYFILE *f, UINT16 setnum); void readunlockable(MYFILE *f, INT32 num); void reademblemdata(MYFILE *f, INT32 num); void readsound(MYFILE *f, INT32 num); diff --git a/src/dehacked.c b/src/dehacked.c index b46805a97..f6ac70fe1 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -453,7 +453,7 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile) ignorelines(f); } else if (i > 0 && i <= MAXCONDITIONSETS) - readconditionset(f, (UINT8)(i-1)); + readconditionset(f, (UINT16)(i-1)); else { deh_warning("Condition set number %d out of range (1 - %d)", i, MAXCONDITIONSETS); From b59346f63773fc88c673c1bec741c9da35610a7b Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 27 Jun 2023 23:59:01 +0100 Subject: [PATCH 55/62] Votekick menu Not super polished, literally just implemented this way to make freeze --- src/k_menu.h | 11 +++ src/k_menudraw.c | 98 +++++++++++++++++++++++++ src/menus/transient/CMakeLists.txt | 1 + src/menus/transient/pause-game.c | 4 +- src/menus/transient/pause-kick.c | 112 +++++++++++++++++++++++++++++ 5 files changed, 224 insertions(+), 2 deletions(-) create mode 100644 src/menus/transient/pause-kick.c diff --git a/src/k_menu.h b/src/k_menu.h index b4ce7c6bd..6d21212d0 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -418,6 +418,8 @@ extern menu_t EXTRAS_EggTVDef; extern menuitem_t PAUSE_Main[]; extern menu_t PAUSE_MainDef; +extern menu_t PAUSE_KickHandlerDef; + // EXTRAS extern menuitem_t MISC_Manual[]; extern menu_t MISC_ManualDef; @@ -1082,6 +1084,14 @@ void M_QuitPauseMenu(INT32 choice); boolean M_PauseInputs(INT32 ch); void M_PauseTick(void); +extern struct playerkickmenu_s { + tic_t ticker; + UINT8 player; + UINT8 poke; +} playerkickmenu; + +void M_KickHandler(INT32 choice); + extern consvar_t cv_dummymenuplayer; extern consvar_t cv_dummyspectator; @@ -1154,6 +1164,7 @@ void M_DrawMPServerBrowser(void); // Pause menu: void M_DrawPause(void); +void M_DrawKickHandler(void); // Replay Playback void M_DrawPlaybackMenu(void); diff --git a/src/k_menudraw.c b/src/k_menudraw.c index f89eb0db8..c10ef7762 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -4538,6 +4538,104 @@ void M_DrawPause(void) } } +void M_DrawKickHandler(void) +{ + // fake round queue drawer simply to make release + INT32 x = 29 + 4, y = 70, returny = y; + INT32 pokeamount = (playerkickmenu.poke & 1) ? -playerkickmenu.poke/2 : playerkickmenu.poke/2; + INT32 x2 = x + pokeamount - 9 - 8; + + boolean datarightofcolumn = false; + + patch_t *resbar = W_CachePatchName("R_RESBAR", PU_CACHE); // Results bars for players + + UINT8 i; + + for (i = 0; i < MAXPLAYERS; i++) + { + V_DrawMappedPatch( + x, y, + (playeringame[i] == true) + ? ((players[i].spectator == true) ? V_TRANSLUCENT : 0) + : V_MODULATE, + resbar, NULL + ); + + V_DrawRightAlignedThinString( + x+13, y-2, + ((i == playerkickmenu.player) + ? highlightflags + : 0 + ), + va("%u", i) + ); + + if (playeringame[i] == true) + { + if (players[i].skincolor != SKINCOLOR_NONE) + { + UINT8 *charcolormap; + if ((players[i].pflags & PF_NOCONTEST) && players[i].bot) + { + // RETIRED !! + charcolormap = R_GetTranslationColormap(TC_DEFAULT, players[i].skincolor, GTC_CACHE); + V_DrawMappedPatch(x+14, y-5, 0, W_CachePatchName("MINIDEAD", PU_CACHE), charcolormap); + } + else + { + charcolormap = R_GetTranslationColormap(players[i].skin, players[i].skincolor, GTC_CACHE); + V_DrawMappedPatch(x+14, y-5, 0, faceprefix[players[i].skin][FACE_MINIMAP], charcolormap); + } + } + + V_DrawThinString( + x+27, y-2, + ( + P_IsMachineLocalPlayer(&players[i]) + ? highlightflags + : 0 + )|V_ALLOWLOWERCASE|V_6WIDTHSPACE, + player_names[i] + ); + + V_DrawRightAlignedThinString( + x+118, y-2, + V_ALLOWLOWERCASE|V_6WIDTHSPACE, + (players[i].spectator) ? "SPECTATOR" : "PLAYING" + ); + } + + if (i == playerkickmenu.player) + { + V_DrawScaledPatch( + x2, y-1, + (datarightofcolumn ? V_FLIP : 0), + W_CachePatchName("M_CURSOR", PU_CACHE) + ); + } + + y += 13; + + if (i == (MAXPLAYERS-1)/2) + { + x = 169 - 4; + y = returny; + + datarightofcolumn = true; + x2 = x + 118 + 9 + 8 + 4 - pokeamount; + } + } + + //V_DrawFill(32 + (playerkickmenu.player & 8), 32 + (playerkickmenu.player & 7)*8, 8, 8, playeringame[playerkickmenu.player] ? 0 : 16); + + V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName("MENUHINT", PU_CACHE), NULL); + V_DrawCenteredThinString( + BASEVIDWIDTH/2, 12, + V_ALLOWLOWERCASE|V_6WIDTHSPACE, + K_GetMidVoteLabel(menucallvote) + ); +} + void M_DrawPlaybackMenu(void) { INT16 i; diff --git a/src/menus/transient/CMakeLists.txt b/src/menus/transient/CMakeLists.txt index 1b9309749..aa1397595 100644 --- a/src/menus/transient/CMakeLists.txt +++ b/src/menus/transient/CMakeLists.txt @@ -8,6 +8,7 @@ target_sources(SRB2SDL2 PRIVATE discord-requests.c message-box.c pause-game.c + pause-kick.c pause-replay.c virtual-keyboard.c ) diff --git a/src/menus/transient/pause-game.c b/src/menus/transient/pause-game.c index cac1c1280..29ae32177 100644 --- a/src/menus/transient/pause-game.c +++ b/src/menus/transient/pause-game.c @@ -333,8 +333,8 @@ void M_HandlePauseMenuCallVote(INT32 choice) } else if (K_MidVoteTypeUsesVictim(menucallvote) == true) { - // Not yet implemented. - S_StartSound(NULL, sfx_s3k7b); + S_StartSound(NULL, sfx_s3k5b); + M_KickHandler(-1); } else { diff --git a/src/menus/transient/pause-kick.c b/src/menus/transient/pause-kick.c new file mode 100644 index 000000000..2340406ee --- /dev/null +++ b/src/menus/transient/pause-kick.c @@ -0,0 +1,112 @@ +/// \file menus/transient/pause-kick.c +/// \brief Player Kick menu + +#include "../../k_menu.h" +#include "../../s_sound.h" +#include "../../p_local.h" +#include "../../k_zvote.h" + +struct playerkickmenu_s playerkickmenu; + +static void M_PlayerKickHandler(INT32 choice) +{ + const UINT8 pid = 0; + + (void)choice; + + if (menucmd[pid].dpad_lr != 0) // symmetrical in this case + { + S_StartSound(NULL, sfx_s3k5b); + + playerkickmenu.player = ((playerkickmenu.player + 8) % MAXPLAYERS); + + M_SetMenuDelay(pid); + } + + else if (menucmd[pid].dpad_ud > 0) + { + S_StartSound(NULL, sfx_s3k5b); + + playerkickmenu.player = ((playerkickmenu.player + 1) & 7) + (playerkickmenu.player & 8); + + M_SetMenuDelay(pid); + } + + else if (menucmd[pid].dpad_ud < 0) + { + S_StartSound(NULL, sfx_s3k5b); + + playerkickmenu.player = ((playerkickmenu.player + 7) & 7) + (playerkickmenu.player & 8); + + M_SetMenuDelay(pid); + } + + else if (M_MenuBackPressed(pid)) + { + M_GoBack(0); + M_SetMenuDelay(pid); + } + + else if (M_MenuConfirmPressed(pid)) + { + M_SetMenuDelay(pid); + + if ( + playeringame[playerkickmenu.player] + && P_IsMachineLocalPlayer(&players[playerkickmenu.player]) == false + && playerkickmenu.player != serverplayer + ) + { + if ( + K_MinimalCheckNewMidVote(menucallvote) == true +#ifndef DEVELOP + && IsPlayerAdmin(playerkickmenu.player) == false +#endif + ) + { + M_ClearMenus(true); + K_SendCallMidVote(menucallvote, playerkickmenu.player); + return; + } + } + + playerkickmenu.poke = 8; + S_StartSound(NULL, sfx_s3k7b); + } +} + +static menuitem_t PAUSE_KickHandler[] = +{ + {IT_NOTHING | IT_KEYHANDLER, NULL, NULL, NULL, {.routine = M_PlayerKickHandler}, 0, 0}, +}; + +static void M_KickHandlerTick(void) +{ + playerkickmenu.ticker++; + + if (playerkickmenu.poke) + playerkickmenu.poke--; +} + +menu_t PAUSE_KickHandlerDef = { + sizeof(PAUSE_KickHandler) / sizeof(menuitem_t), + &PAUSE_MainDef, + 0, + PAUSE_KickHandler, + 0, 0, + 0, 0, + 0, + NULL, + 0, 0, + M_DrawKickHandler, + M_KickHandlerTick, + NULL, + NULL, + NULL, +}; + +void M_KickHandler(INT32 choice) +{ + PAUSE_KickHandlerDef.prevMenu = currentMenu; + M_SetupNextMenu(&PAUSE_KickHandlerDef, true); +} From 685e1b019f97eb9e4a0ace2d5a6eaa8199170c46 Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 27 Jun 2023 23:59:46 +0100 Subject: [PATCH 56/62] Actually pay attention to K_PlayerIDAllowedInMidVote for access to calling votes --- src/k_menudraw.c | 4 ++++ src/k_zvote.c | 6 ++++++ 2 files changed, 10 insertions(+) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index c10ef7762..4d3f138e4 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -4384,6 +4384,10 @@ void M_DrawPause(void) if (g_midVote.delay != 1) maintext = va("%u", ((g_midVote.delay - 1) / TICRATE) + 1); } + else if (K_PlayerIDAllowedInMidVote(consoleplayer) == false) + { + maintext = "SPECTATING"; + } else { maintext = "INVALID!?"; diff --git a/src/k_zvote.c b/src/k_zvote.c index 8f6ba9e95..8ce9fde7e 100644 --- a/src/k_zvote.c +++ b/src/k_zvote.c @@ -575,6 +575,12 @@ boolean K_MinimalCheckNewMidVote(midVoteType_e type) return false; } + if (K_PlayerIDAllowedInMidVote(consoleplayer) == false) + { + // Invalid calling player. + return false; + } + return true; } From dee753d979e78d2efd3b47209761886274b322c5 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 28 Jun 2023 00:02:09 +0100 Subject: [PATCH 57/62] Admin Tools menu Just a superpowered version of the Call Votekick menu, also just to make release cleanly --- src/k_menu.h | 2 ++ src/k_menudraw.c | 4 +++- src/menus/transient/pause-game.c | 11 ++++++++++- src/menus/transient/pause-kick.c | 25 ++++++++++++++++++++++++- 4 files changed, 39 insertions(+), 3 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index 6d21212d0..888ecb181 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -453,6 +453,7 @@ typedef enum mpause_discordrequests, #endif mpause_callvote, + mpause_admin, mpause_continue, mpause_spectate, @@ -1088,6 +1089,7 @@ extern struct playerkickmenu_s { tic_t ticker; UINT8 player; UINT8 poke; + boolean adminpowered; } playerkickmenu; void M_KickHandler(INT32 choice); diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 4d3f138e4..40f96c9c7 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -4636,7 +4636,9 @@ void M_DrawKickHandler(void) V_DrawCenteredThinString( BASEVIDWIDTH/2, 12, V_ALLOWLOWERCASE|V_6WIDTHSPACE, - K_GetMidVoteLabel(menucallvote) + (playerkickmenu.adminpowered) + ? "You are using ""\x85""Admin Tools""\x80"", ""\x83""(A)""\x80"" to kick and ""\x84""(C)""\x80"" to ban" + : K_GetMidVoteLabel(menucallvote) ); } diff --git a/src/menus/transient/pause-game.c b/src/menus/transient/pause-game.c index 29ae32177..2e73fe040 100644 --- a/src/menus/transient/pause-game.c +++ b/src/menus/transient/pause-game.c @@ -43,6 +43,9 @@ menuitem_t PAUSE_Main[] = {IT_STRING | IT_ARROWS, "CALL VOTE", "M_ICOVOT", NULL, {.routine = M_HandlePauseMenuCallVote}, 0, 0}, + {IT_STRING | IT_ARROWS, "ADMIN TOOLS", "M_ICOADM", + NULL, {.routine = M_KickHandler}, 0, 0}, + {IT_STRING | IT_CALL, "RESUME GAME", "M_ICOUNP", NULL, {.routine = M_QuitPauseMenu}, 0, 0}, @@ -128,6 +131,7 @@ void M_OpenPauseMenu(void) PAUSE_Main[mpause_restartmap].status = IT_DISABLED; PAUSE_Main[mpause_tryagain].status = IT_DISABLED; PAUSE_Main[mpause_callvote].status = IT_DISABLED; + PAUSE_Main[mpause_admin].status = IT_DISABLED; #ifdef HAVE_DISCORDRPC PAUSE_Main[mpause_discordrequests].status = IT_DISABLED; #endif @@ -161,6 +165,11 @@ void M_OpenPauseMenu(void) { PAUSE_Main[mpause_addons].status = IT_STRING | IT_CALL; } + + if (netgame) + { + PAUSE_Main[mpause_admin].status = IT_STRING | IT_CALL; + } } } else if (!netgame && !demo.playback) @@ -183,7 +192,7 @@ void M_OpenPauseMenu(void) } } - if (netgame) + if (netgame && (PAUSE_Main[mpause_admin].status == IT_DISABLED)) { menucallvote = K_GetNextAllowedMidVote(menucallvote, true); diff --git a/src/menus/transient/pause-kick.c b/src/menus/transient/pause-kick.c index 2340406ee..2f4a83ab5 100644 --- a/src/menus/transient/pause-kick.c +++ b/src/menus/transient/pause-kick.c @@ -12,6 +12,8 @@ static void M_PlayerKickHandler(INT32 choice) { const UINT8 pid = 0; + UINT8 kicktype = UINT8_MAX; + (void)choice; if (menucmd[pid].dpad_lr != 0) // symmetrical in this case @@ -47,7 +49,17 @@ static void M_PlayerKickHandler(INT32 choice) M_SetMenuDelay(pid); } + else if (M_MenuExtraPressed(pid) && playerkickmenu.adminpowered) + { + kicktype = KICK_MSG_BANNED; + } + else if (M_MenuConfirmPressed(pid)) + { + kicktype = KICK_MSG_KICKED; + } + + if (kicktype != UINT8_MAX) { M_SetMenuDelay(pid); @@ -57,7 +69,16 @@ static void M_PlayerKickHandler(INT32 choice) && playerkickmenu.player != serverplayer ) { - if ( + if (playerkickmenu.adminpowered) + { + if (consoleplayer == serverplayer || IsPlayerAdmin(consoleplayer)) + { + playerkickmenu.poke = (kicktype == KICK_MSG_BANNED) ? 16 : 12; + SendKick(playerkickmenu.player, kicktype); + return; + } + } + else if ( K_MinimalCheckNewMidVote(menucallvote) == true #ifndef DEVELOP && IsPlayerAdmin(playerkickmenu.player) == false @@ -107,6 +128,8 @@ menu_t PAUSE_KickHandlerDef = { void M_KickHandler(INT32 choice) { + playerkickmenu.adminpowered = (choice >= 0); + PAUSE_KickHandlerDef.prevMenu = currentMenu; M_SetupNextMenu(&PAUSE_KickHandlerDef, true); } From 97dd516568a1b2cc89a13683b9251b8786f2995e Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 28 Jun 2023 00:07:39 +0100 Subject: [PATCH 58/62] Unrelated Pause Menu bugfix: Only permit GP "TRY AGAIN" option in GS_LEVEL, given it's buggy right now (we can change it back when it's fixed) --- src/menus/transient/pause-game.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/menus/transient/pause-game.c b/src/menus/transient/pause-game.c index 2e73fe040..a26e1df14 100644 --- a/src/menus/transient/pause-game.c +++ b/src/menus/transient/pause-game.c @@ -175,7 +175,11 @@ void M_OpenPauseMenu(void) else if (!netgame && !demo.playback) { boolean retryallowed = (modeattacking != ATTACKING_NONE); - if (G_GametypeUsesLives()) + if ( + retryallowed == false + && gamestate == GS_LEVEL + && G_GametypeUsesLives() + ) { for (i = 0; i <= splitscreen; i++) { From cc73279756b0af3f8a88218ce97281506589d38d Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 28 Jun 2023 00:37:30 +0100 Subject: [PATCH 59/62] Allow both CALL VOTE and ADMIN TOOLS in pause menu Also slight re-ordering --- src/k_menu.h | 2 +- src/menus/transient/pause-game.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index 888ecb181..921e0ecea 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -452,8 +452,8 @@ typedef enum #ifdef HAVE_DISCORDRPC mpause_discordrequests, #endif - mpause_callvote, mpause_admin, + mpause_callvote, mpause_continue, mpause_spectate, diff --git a/src/menus/transient/pause-game.c b/src/menus/transient/pause-game.c index a26e1df14..9a24e77ca 100644 --- a/src/menus/transient/pause-game.c +++ b/src/menus/transient/pause-game.c @@ -40,12 +40,12 @@ menuitem_t PAUSE_Main[] = NULL, {.routine = M_DiscordRequests}, 0, 0}, #endif - {IT_STRING | IT_ARROWS, "CALL VOTE", "M_ICOVOT", - NULL, {.routine = M_HandlePauseMenuCallVote}, 0, 0}, - {IT_STRING | IT_ARROWS, "ADMIN TOOLS", "M_ICOADM", NULL, {.routine = M_KickHandler}, 0, 0}, + {IT_STRING | IT_ARROWS, "CALL VOTE", "M_ICOVOT", + NULL, {.routine = M_HandlePauseMenuCallVote}, 0, 0}, + {IT_STRING | IT_CALL, "RESUME GAME", "M_ICOUNP", NULL, {.routine = M_QuitPauseMenu}, 0, 0}, @@ -196,7 +196,7 @@ void M_OpenPauseMenu(void) } } - if (netgame && (PAUSE_Main[mpause_admin].status == IT_DISABLED)) + if (netgame) // && (PAUSE_Main[mpause_admin].status == IT_DISABLED)) { menucallvote = K_GetNextAllowedMidVote(menucallvote, true); From 2c672449543742026c61dc82c45125cafa20418b Mon Sep 17 00:00:00 2001 From: VelocitOni Date: Tue, 27 Jun 2023 20:37:55 -0400 Subject: [PATCH 60/62] RTV and RIB default to Yes "Skip Map" and "Redo Map" on by default for Z-Voting --- src/k_zvote.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/k_zvote.c b/src/k_zvote.c index 8ce9fde7e..784887512 100644 --- a/src/k_zvote.c +++ b/src/k_zvote.c @@ -104,14 +104,14 @@ static midVoteTypeDef_t g_midVoteTypeDefs[MVT__MAX] = { // MVT_RTV "RTV", "Skip Level?", - CVAR_INIT ("zvote_rtv_allowed", "No", CV_SAVE|CV_NETVAR, CV_YesNo, NULL), + CVAR_INIT ("zvote_rtv_allowed", "Yes", CV_SAVE|CV_NETVAR, CV_YesNo, NULL), K_MidVoteRockTheVote }, { // MVT_RUNITBACK "RUNITBACK", "Redo Level?", - CVAR_INIT ("zvote_runitback_allowed", "No", CV_SAVE|CV_NETVAR, CV_YesNo, NULL), + CVAR_INIT ("zvote_runitback_allowed", "Yes", CV_SAVE|CV_NETVAR, CV_YesNo, NULL), K_MidVoteRunItBack }, }; From e2669dba0c4d6c5e743fc8f3fe9554112155f91e Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 28 Jun 2023 12:16:25 +0100 Subject: [PATCH 61/62] Correct readcondition helper function as well --- src/deh_soc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/deh_soc.c b/src/deh_soc.c index c9c457383..14dd9a48e 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -2393,7 +2393,7 @@ void readunlockable(MYFILE *f, INT32 num) Z_Free(s); } -static void readcondition(UINT8 set, UINT32 id, char *word2) +static void readcondition(UINT16 set, UINT32 id, char *word2) { INT32 i; char *params[5]; // condition, requirement, extra info, extra info, stringvar From a933e7a08498b998284fd270b7860413a63d6d3e Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 28 Jun 2023 12:17:41 +0100 Subject: [PATCH 62/62] Provide helpful warning for too-large id counts for ConditionSet Condition# lines --- src/deh_soc.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/deh_soc.c b/src/deh_soc.c index 14dd9a48e..4846ec4a1 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -2865,7 +2865,7 @@ void readconditionset(MYFILE *f, UINT16 setnum) char *word = s; char *word2; char *tmp; - UINT8 id; + UINT16 id; UINT8 previd = 0; M_ClearConditionSet(setnum); @@ -2902,21 +2902,26 @@ void readconditionset(MYFILE *f, UINT16 setnum) if (fastncmp(word, "CONDITION", 9)) { - id = (UINT8)atoi(word + 9); + id = atoi(word + 9); if (id == 0) { deh_warning("Condition set %d: unknown word '%s'", setnum+1, word); continue; } - else if (previd > id) + if (previd > id) { // out of order conditions can cause problems, so enforce proper order deh_warning("Condition set %d: conditions are out of order, ignoring this line", setnum+1); continue; } - previd = id; + if (id > UINT8_MAX) + { + deh_warning("Condition set %d: too many Condition# types, ignoring this line", setnum+1); + continue; + } + previd = (UINT8)id; - readcondition(setnum, id, word2); + readcondition(setnum, (UINT8)id, word2); } else deh_warning("Condition set %d: unknown word '%s'", setnum, word);