diff --git a/src/d_netcmd.c b/src/d_netcmd.c index f391e83c1..3ef453df5 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/d_player.h b/src/d_player.h index 96e990f79..2f6d3ca74 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -192,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), @@ -410,6 +421,7 @@ struct itemroulette_t tic_t elapsed; boolean eggman; + boolean ringbox; }; // enum for bot item priorities @@ -767,6 +779,9 @@ struct player_t boolean markedfordeath; + UINT8 ringboxdelay; // Delay until Ring Box auto-activates + UINT8 ringboxaward; // Where did we stop? + fixed_t outrun; // Milky Way road effect uint8_t public_key[PUBKEYLENGTH]; diff --git a/src/deh_tables.c b/src/deh_tables.c index ddbae5cbf..8f99be9bf 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 7d1c0023c..0a081d08f 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 @@ -3895,7 +3896,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 @@ -3909,7 +3922,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 @@ -22394,7 +22406,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 @@ -22405,7 +22417,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 @@ -22432,7 +22444,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 a203dbc3d..fb725f23f 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 @@ -4302,7 +4301,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, @@ -4317,7 +4329,6 @@ typedef enum state S_SPHEREBOX10, S_SPHEREBOX11, S_SPHEREBOX12, - S_DEADSPHEREBOX, // Random Item Pop S_RANDOMITEMPOP1, diff --git a/src/k_hud.c b/src/k_hud.c index c0098b016..91fc27daa 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++) @@ -875,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 @@ -1217,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); @@ -1533,6 +1572,118 @@ 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]; + + // == 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 hstretch = 3; + INT32 splitbsx, splitbsy = 0; + UINT16 localcolor[3] = { stplyr->skincolor }; + SINT8 colormode[3] = { TC_RAINBOW }; + + fixed_t rouletteOffset = 0; + fixed_t rouletteSpace = SLOT_SPACING; + vector2_t rouletteCrop = {10, 10}; + 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.itemListLen + (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_GetSlotOffset(&stplyr->itemRoulette, rendertimefrac); + } + else + { + if (!stplyr->ringboxdelay) + return; + } + + // 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 + 10; + fy = ITEM_Y + 10; + fflags = V_SNAPTOLEFT|V_SNAPTOTOP|V_SPLITSCREEN; + } + else // else, that means we're P2 or P4. + { + fx = ITEM2_X + 7; + fy = ITEM2_Y + 10; + fflags = V_SNAPTORIGHT|V_SNAPTOTOP|V_SPLITSCREEN; + } + + rouletteSpace = SLOT_SPACING_SPLITSCREEN; + rouletteOffset = FixedMul(rouletteOffset, FixedDiv(SLOT_SPACING_SPLITSCREEN, SLOT_SPACING)); + rouletteCrop.x = 16; + rouletteCrop.y = 13; + splitbsx = -6; + splitbsy = -6; + boxoffy += 2; + hstretch = 0; + } + 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 + boxoffx + splitbsx) << FRACBITS), ((fy + rouletteCrop.y + boxoffy - vstretch + splitbsy) << FRACBITS), + rouletteSpace + (hstretch<itemRoulette.ringbox && stplyr->itemamount == 0 && stplyr->itemtype == 0) + { + K_drawKartSlotMachine(); + } + else + { + K_drawKartItem(); + } + } if (demo.title) ; diff --git a/src/k_kart.c b/src/k_kart.c index 1a6ba0d06..cf36a300e 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -361,6 +361,7 @@ void K_RegisterKartStuff(void) CV_RegisterVar(&cv_kartvoices); CV_RegisterVar(&cv_kartbot); CV_RegisterVar(&cv_karteliminatelast); + CV_RegisterVar(&cv_thunderdome); CV_RegisterVar(&cv_kartusepwrlv); CV_RegisterVar(&cv_votetime); CV_RegisterVar(&cv_botscanvote); @@ -10753,6 +10754,26 @@ void K_MoveKartPlayer(player_t *player, boolean onground) player->pflags &= ~PF_USERINGS; } + if (player->ringboxdelay) + { + player->ringboxdelay--; + if (player->ringboxdelay == 0) + { + // TODO + 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 * (behindMulti + 10) / 10; + + K_AwardPlayerRings(player, award, 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_objects.h b/src/k_objects.h index a20798dac..52e29275a 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -135,6 +135,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 5a6f79dda..c85162c12 100644 --- a/src/k_roulette.c +++ b/src/k_roulette.c @@ -218,6 +218,17 @@ static kartitems_t K_KartItemReelBoss[] = KITEM_NONE }; +static kartslotmachine_t K_KartItemReelRingBox[] = +{ + KSM_BAR, + KSM_DOUBLEBAR, + KSM_TRIPLEBAR, + KSM_RING, + KSM_SEVEN, + KSM_JACKPOT, + KSM__MAX +}; + /*-------------------------------------------------- boolean K_ItemEnabled(kartitems_t item) @@ -438,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; @@ -1052,6 +1063,7 @@ static void K_InitRoulette(itemroulette_t *const roulette) roulette->active = true; roulette->eggman = false; + roulette->ringbox = false; for (i = 0; i < MAXPLAYERS; i++) { @@ -1104,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) @@ -1253,11 +1266,11 @@ 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. --------------------------------------------------*/ -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; @@ -1276,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) @@ -1412,12 +1439,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 +1471,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; } @@ -1461,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) @@ -1572,9 +1612,17 @@ 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) + { + player->ringboxdelay = TICRATE; + player->ringboxaward = finalItem; + } + else + { + K_KartGetItemResult(player, finalItem); + } player->karthud[khud_itemblink] = TICRATE; player->karthud[khud_itemblinkmode] = 0; @@ -1582,7 +1630,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); } } @@ -1603,7 +1654,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..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); @@ -98,7 +101,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 +112,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 +130,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); /*-------------------------------------------------- @@ -169,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); @@ -186,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/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/objects/random-item.c b/src/objects/random-item.c index 4ec2106fe..f392d9752 100644 --- a/src/objects/random-item.c +++ b/src/objects/random-item.c @@ -102,6 +102,29 @@ void Obj_RandomItemVisuals(mobj_t *mobj) ItemBoxBob(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... + // ...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) && !cv_thunderdome.value) + { + mobj->extravalue1++; + if (mobj->extravalue1 == RINGBOX_TIME) + { + // 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) @@ -116,6 +139,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; @@ -148,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); } diff --git a/src/p_enemy.c b/src/p_enemy.c index 9f6f5c490..5e786c677 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); @@ -12984,54 +12983,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) - { - K_StartItemRoulette(actor->target->player); - } - - // 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 c771b3427..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; @@ -186,6 +187,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 +356,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 >= RINGBOX_TIME) + 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 +370,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 @@ -434,7 +498,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 054efcbb1..803559ef9 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -9718,7 +9718,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])) { @@ -9726,22 +9726,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; + 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) @@ -11592,11 +11580,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) diff --git a/src/p_saveg.c b/src/p_saveg.c index f80ffff7b..3f075fc2d 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -527,6 +527,8 @@ 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); WRITEFIXED(save->p, players[i].outrun); // respawnvars_t @@ -600,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); @@ -931,6 +934,8 @@ 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); players[i].outrun = READFIXED(save->p); // respawnvars_t @@ -1015,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);