Merge branch 'ringbox' into 'master'

Ring Box

See merge request KartKrew/Kart!1292
This commit is contained in:
Oni 2023-06-27 09:47:15 +00:00
commit fe3790b0db
17 changed files with 463 additions and 102 deletions

View file

@ -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[] =

View file

@ -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;

View file

@ -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];

View file

@ -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",

View file

@ -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

View file

@ -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,

View file

@ -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<<FRACBITS), rouletteSpace + (vstretch<<FRACBITS),
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)<<FRACBITS), ((fy)<<FRACBITS) + rouletteOffset,
FRACUNIT, V_HUDTRANS|V_SLIDEIN|fflags,
localpatch[i], (localcolor[i] ? R_GetTranslationColormap(colormode[i], localcolor[i], GTC_CACHE) : NULL)
);
rouletteOffset -= rouletteSpace;
}
V_ClearClipRect();
}
void K_drawKartTimestamp(tic_t drawtime, INT32 TX, INT32 TY, INT32 splitflags, UINT8 mode)
{
// TIME_X = BASEVIDWIDTH-124; // 196
@ -4943,7 +5094,7 @@ static void K_drawDistributionDebugger(void)
return;
}
K_FillItemRouletteData(stplyr, &rouletteData);
K_FillItemRouletteData(stplyr, &rouletteData, false);
for (i = 0; i < rouletteData.itemListLen; i++)
{
@ -5200,7 +5351,16 @@ void K_drawKartHUD(void)
// Draw the item window
if (LUA_HudEnabled(hud_item) && !freecam)
K_drawKartItem();
{
if (stplyr->itemRoulette.ringbox && stplyr->itemamount == 0 && stplyr->itemtype == 0)
{
K_drawKartSlotMachine();
}
else
{
K_drawKartItem();
}
}
if (demo.title)
;

View file

@ -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.

View file

@ -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);

View file

@ -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

View file

@ -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

View file

@ -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"))

View file

@ -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);
}

View file

@ -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;

View file

@ -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++)

View file

@ -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)

View file

@ -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);