Merge branch 'pick-me-up' into 'master'

Pick up allied items

See merge request kart-krew-dev/ring-racers-internal!2545
This commit is contained in:
Oni VelocitOni 2025-05-18 17:44:48 +00:00
commit 5fd2c09022
14 changed files with 409 additions and 0 deletions

View file

@ -782,6 +782,8 @@ struct player_t
// Item held stuff
SINT8 itemtype; // KITEM_ constant for item number
UINT8 itemamount; // Amount of said item
SINT8 backupitemtype;
UINT8 backupitemamount;
SINT8 throwdir; // Held dir of controls; 1 = forward, 0 = none, -1 = backward (was "player->heldDir")
UINT8 itemscale; // Item scale value, from when an item was taken out. (0 for normal, 1 for grow, 2 for shrink.)

View file

@ -72,6 +72,9 @@ boolean K_BananaBallhogCollide(mobj_t *t1, mobj_t *t2)
if (t1->type == MT_BALLHOG && t2->type == MT_BALLHOG)
return true; // Ballhogs don't collide with eachother
if (K_TryPickMeUp(t1, t2))
return true;
if (t2->player)
{
if (t2->player->flashing > 0 && t2->hitlag == 0)
@ -172,6 +175,9 @@ boolean K_EggItemCollide(mobj_t *t1, mobj_t *t2)
if (t1->health <= 0 || t2->health <= 0)
return true;
if (K_TryPickMeUp(t1, t2))
return true;
if (!P_CanPickupItem(t2->player, PICKUP_EGGBOX))
return true;
@ -425,6 +431,9 @@ boolean K_LandMineCollide(mobj_t *t1, mobj_t *t2)
if (t1->health <= 0 || t2->health <= 0)
return true;
if (K_TryPickMeUp(t1, t2))
return true;
if (t2->player)
{
const INT32 oldhitlag = t2->hitlag;
@ -532,6 +541,9 @@ boolean K_DropTargetCollide(mobj_t *t1, mobj_t *t2)
if (t2->player && (t2->player->hyudorotimer || t2->player->justbumped))
return true;
if (K_TryPickMeUp(t1, t2))
return true;
if (draggeddroptarget && P_MobjWasRemoved(draggeddroptarget))
draggeddroptarget = NULL; // Beware order-of-execution on crushers, I guess?!
@ -1053,6 +1065,9 @@ boolean K_KitchenSinkCollide(mobj_t *t1, mobj_t *t2)
if (((t1->target == t2) || (!(t2->flags & (MF_ENEMY|MF_BOSS)) && (t1->target == t2->target))) && (t1->threshold > 0 || (t2->type != MT_PLAYER && t2->threshold > 0)))
return true;
if (K_TryPickMeUp(t1, t2))
return true;
if (t2->player)
{
if (t2->player->flashing > 0 && t2->hitlag == 0)

View file

@ -209,6 +209,7 @@ static patch_t *kp_alagles[10];
static patch_t *kp_blagles[6];
static patch_t *kp_cpu[2];
patch_t *kp_pickmeup[2];
static patch_t *kp_nametagstem;
@ -867,6 +868,9 @@ void K_LoadKartHUDGraphics(void)
HU_UpdatePatch(&kp_cpu[0], "K_CPU1");
HU_UpdatePatch(&kp_cpu[1], "K_CPU2");
HU_UpdatePatch(&kp_pickmeup[0], "K_PMU1");
HU_UpdatePatch(&kp_pickmeup[1], "K_PMU2");
HU_UpdatePatch(&kp_nametagstem, "K_NAMEST");
HU_UpdatePatch(&kp_trickcool[0], "K_COOL1");
@ -1889,6 +1893,125 @@ static void K_drawKartItem(void)
}
}
// So, like, we've already established that HUD code is unsavable, right?
// == SHITGARBAGE UNLIMITED 3: HACKS GONE WILD ==
static void K_drawBackupItem(void)
{
bool tiny = r_splitscreen > 1;
patch_t *localpatch[3] = { kp_nodraw, kp_nodraw, kp_nodraw };
patch_t *localbg = (kp_itembg[2]);
patch_t *localinv = kp_invincibility[((leveltime % (6*3)) / 3) + 7 + tiny];
INT32 fx = 0, fy = 0, fflags = 0, tx = 0, ty = 0; // final coords for hud and flags...
const INT32 numberdisplaymin = 2;
skincolornum_t localcolor[3] = { static_cast<skincolornum_t>(stplyr->skincolor) };
SINT8 colormode[3] = { TC_RAINBOW };
boolean flipamount = false; // Used for 3P/4P splitscreen to flip item amount stuff
if (stplyr->backupitemamount <= 0)
return;
switch (stplyr->backupitemtype)
{
case KITEM_INVINCIBILITY:
localpatch[1] = localinv;
localbg = kp_itembg[2];
break;
case KITEM_ORBINAUT:
localpatch[1] = kp_orbinaut[tiny+4];
break;
case KITEM_SPB:
case KITEM_LIGHTNINGSHIELD:
case KITEM_BUBBLESHIELD:
case KITEM_FLAMESHIELD:
localbg = kp_itembg[2];
/*FALLTHRU*/
default:
localpatch[1] = K_GetCachedItemPatch(stplyr->backupitemtype, 1 + tiny);
if (localpatch[1] == NULL)
localpatch[1] = kp_nodraw; // diagnose underflows
break;
}
// pain and suffering defined below
if (!(R_GetViewNumber() & 1) || (!tiny)) // 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;
flipamount = true;
}
if (r_splitscreen == 1)
{
fy -= 5;
}
// final fudge - vegeta 2025
if (tiny && !(R_GetViewNumber() & 1)) // P1/P3 4P
{
fx += 26;
fy += 5;
tx += 10;
ty += 18;
}
else if (tiny && (R_GetViewNumber() & 1)) // P2/P4 4P
{
fx += -4;
fy += 5;
tx += 1;
ty += 18;
}
else // 1P/2P
{
fx += 30;
fy += -10;
tx += 25;
ty += 30;
}
boolean transflag = V_HUDTRANS;
// I feel like the cardinal sin of all evolving HUDcode is, like, assuming the old offsets do something that makes sense.
if (stplyr->backupitemamount >= numberdisplaymin && stplyr->itemRoulette.active == false)
{
/*
// Then, the numbers:
V_DrawScaledPatch(
fx + (flipamount ? 48 : 0), fy,
V_HUDTRANS|V_SLIDEIN|fflags|(flipamount ? V_FLIP : 0),
kp_itemmulsticker[offset]
); // flip this graphic for p2 and p4 in split and shift it.
*/
V_DrawFixedPatch(
fx<<FRACBITS, (fy<<FRACBITS),
FRACUNIT, transflag|V_SLIDEIN|fflags,
localpatch[1], (localcolor[1] ? R_GetTranslationColormap(colormode[1], localcolor[1], GTC_CACHE) : NULL)
);
V_DrawString(fx+tx, fy+ty, V_HUDTRANS|V_SLIDEIN|fflags, va("x%d", stplyr->backupitemamount));
}
else
{
V_DrawFixedPatch(
fx<<FRACBITS, (fy<<FRACBITS),
FRACUNIT, transflag|V_SLIDEIN|fflags,
localpatch[1], (localcolor[1] ? R_GetTranslationColormap(colormode[1], localcolor[1], GTC_CACHE) : NULL)
);
}
}
static void K_drawKartSlotMachine(void)
{
// ITEM_X = BASEVIDWIDTH-50; // 270
@ -6876,6 +6999,9 @@ void K_drawKartHUD(void)
{
K_drawKartItem();
}
if (stplyr->backupitemtype)
K_drawBackupItem();
}
}
}

View file

@ -130,6 +130,8 @@ extern patch_t *gen_button_keycenter[2];
extern patch_t *kp_eggnum[6];
extern patch_t *kp_facenum[MAXPLAYERS+1];
extern patch_t *kp_pickmeup[2];
extern patch_t *kp_unknownminimap;
void K_AddMessage(const char *msg, boolean interrupt, boolean persist);

View file

@ -278,6 +278,26 @@ private:
}},
};
case MT_JAWZ:
case MT_JAWZ_SHIELD:
case MT_ORBINAUT:
case MT_ORBINAUT_SHIELD:
case MT_DROPTARGET:
case MT_DROPTARGET_SHIELD:
case MT_LANDMINE:
case MT_BANANA:
case MT_BANANA_SHIELD:
case MT_GACHABOM:
case MT_EGGMANITEM:
case MT_EGGMANITEM_SHIELD:
case MT_BUBBLESHIELDTRAP:
return {
{ // Near
{2, TICRATE/2, {kp_pickmeup}, 0}, // 1P
{{2, TICRATE/2, {kp_pickmeup}, 0}}, // 4P
},
};
default:
return {
{ // Near
@ -378,6 +398,24 @@ bool is_object_tracking_target(const mobj_t* mobj)
return !(mobj->renderflags & (RF_TRANSMASK | RF_DONTDRAW)) && // the spraycan wasn't collected yet
P_CheckSight(stplyr->mo, const_cast<mobj_t*>(mobj));
case MT_JAWZ:
case MT_JAWZ_SHIELD:
case MT_ORBINAUT:
case MT_ORBINAUT_SHIELD:
case MT_DROPTARGET:
case MT_DROPTARGET_SHIELD:
case MT_LANDMINE:
case MT_BANANA:
case MT_BANANA_SHIELD:
case MT_GACHABOM:
case MT_BUBBLESHIELDTRAP:
case MT_EGGMANITEM:
case MT_EGGMANITEM_SHIELD:
return (mobj->target && !P_MobjWasRemoved(mobj->target) && (
(mobj->target->player && stplyr == mobj->target->player)
|| (mobj->target->player && G_SameTeam(stplyr, mobj->target->player))
) && P_CheckSight(stplyr->mo, const_cast<mobj_t*>(mobj)));
default:
return false;
}
@ -863,6 +901,35 @@ void K_drawTargetHUD(const vector3_t* origin, player_t* player)
if (tracking)
{
fixed_t itemOffset = 36*mobj->scale;
switch (mobj->type)
{
case MT_JAWZ:
case MT_JAWZ_SHIELD:
case MT_ORBINAUT:
case MT_ORBINAUT_SHIELD:
case MT_DROPTARGET:
case MT_DROPTARGET_SHIELD:
case MT_LANDMINE:
case MT_BANANA:
case MT_BANANA_SHIELD:
case MT_GACHABOM:
case MT_BUBBLESHIELDTRAP:
case MT_EGGMANITEM:
case MT_EGGMANITEM_SHIELD:
if (stplyr->mo->eflags & MFE_VERTICALFLIP)
{
pos.z -= itemOffset;
}
else
{
pos.z += itemOffset;
}
break;
default:
break;
}
K_ObjectTracking(&tr.result, &pos, false);
targetList.push_back(tr);
}

View file

@ -8349,6 +8349,31 @@ static void K_MoveHeldObjects(player_t *player)
}
}
// If we can move our backup item into main slots, do so.
static void K_TryMoveBackupItem(player_t *player)
{
if (player->itemtype && player->itemtype == player->backupitemtype)
{
player->itemamount += player->backupitemamount;
player->backupitemtype = 0;
player->backupitemamount = 0;
S_StartSound(player->mo, sfx_mbs54);
}
if (player->itemtype == KITEM_NONE && player->backupitemtype && P_CanPickupItem(player, PICKUP_PAPERITEM))
{
player->itemtype = player->backupitemtype;
player->itemamount = player->backupitemamount;
player->backupitemtype = 0;
player->backupitemamount = 0;
S_StartSound(player->mo, sfx_mbs54);
}
}
mobj_t *K_FindJawzTarget(mobj_t *actor, player_t *source, angle_t range)
{
fixed_t best = INT32_MAX;
@ -9158,6 +9183,8 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
if (player->itemtype == KITEM_NONE)
player->itemflags &= ~IF_HOLDREADY;
K_TryMoveBackupItem(player);
if (onground || player->transfer < 10*player->mo->scale)
{
player->transfer = 0;
@ -15484,4 +15511,139 @@ UINT32 K_GetNumGradingPoints(void)
return numlaps * (1 + Obj_GetCheckpointCount());
}
static boolean K_PickUp(player_t *player, mobj_t *picked)
{
SINT8 type = -1;
SINT8 amount = 1;
switch (picked->type)
{
case MT_ORBINAUT:
case MT_ORBINAUT_SHIELD:
type = KITEM_ORBINAUT;
break;
case MT_JAWZ:
case MT_JAWZ_SHIELD:
type = KITEM_JAWZ;
break;
case MT_BALLHOG:
type = KITEM_BALLHOG;
break;
case MT_LANDMINE:
type = KITEM_LANDMINE;
break;
case MT_EGGMANITEM:
case MT_EGGMANITEM_SHIELD:
type = KITEM_EGGMAN;
break;
case MT_BANANA:
case MT_BANANA_SHIELD:
type = KITEM_BANANA;
break;
case MT_DROPTARGET:
case MT_DROPTARGET_SHIELD:
type = KITEM_DROPTARGET;
break;
case MT_GACHABOM:
type = KITEM_GACHABOM;
break;
case MT_BUBBLESHIELDTRAP:
type = KITEM_BUBBLESHIELD;
break;
case MT_SINK:
type = KITEM_KITCHENSINK;
break;
default:
type = KITEM_SAD;
break;
}
if (type == KITEM_SAD)
return false;
// CONS_Printf("it %d ia %d t %d a %d\n", player->itemtype, player->itemamount, type, amount);
if (player->itemtype == type && player->itemamount && !(player->itemflags & IF_ITEMOUT))
{
// We have this item in main slot but not deployed, just add it
player->itemamount += amount;
}
else if (player->backupitemamount && player->backupitemtype)
{
// We already have a backup item, stack it if it can be stacked or discard it
if (player->backupitemtype == type)
{
player->backupitemamount += amount;
}
else
{
K_DropPaperItem(player, player->backupitemtype, player->backupitemamount);
player->backupitemtype = type;
player->backupitemamount = amount;
S_StartSound(player->mo, sfx_kc65);
}
}
else
{
// We have no backup item, load one up
player->backupitemtype = type;
player->backupitemamount = amount;
}
S_StartSound(player->mo, sfx_aple);
K_TryMoveBackupItem(player);
return true;
}
// ACHTUNG this destroys items when returning true, make sure to bail out
boolean K_TryPickMeUp(mobj_t *m1, mobj_t *m2)
{
if (!m1 || P_MobjWasRemoved(m1))
return false;
if (!m2 || P_MobjWasRemoved(m2))
return false;
if (m1->type != MT_PLAYER && m2->type != MT_PLAYER)
return false;
if (m1->type == MT_PLAYER && m2->type == MT_PLAYER)
return false;
// CONS_Printf("player check passed\n");
mobj_t *victim = m1;
mobj_t *inflictor = m2;
// Convenience for collision functions where arg order is freaky
if (m2->type == MT_PLAYER)
{
victim = m2;
inflictor = m1;
}
if (!victim->player)
return false;
boolean allied = (inflictor->target == victim);
if (!allied && inflictor->target && !P_MobjWasRemoved(inflictor->target))
if (inflictor->target->player && G_SameTeam(inflictor->target->player, victim->player))
allied = true;
if (!allied)
return false;
// CONS_Printf("target check passed\n");
if (!K_PickUp(victim->player, inflictor))
return false;
K_AddHitLag(victim, 3, false);
P_RemoveMobj(inflictor);
return true;
}
//}

View file

@ -314,6 +314,8 @@ UINT16 K_GetDisplayEXP(player_t *player);
UINT32 K_GetNumGradingPoints(void);
boolean K_TryPickMeUp(mobj_t *m1, mobj_t *m2);
#ifdef __cplusplus
} // extern "C"
#endif

View file

@ -438,6 +438,10 @@ static int player_get(lua_State *L)
lua_pushinteger(L, plr->itemtype);
else if (fastcmp(field,"itemamount"))
lua_pushinteger(L, plr->itemamount);
else if (fastcmp(field,"backupitemtype"))
lua_pushinteger(L, plr->backupitemtype);
else if (fastcmp(field,"backupitemamount"))
lua_pushinteger(L, plr->backupitemamount);
else if (fastcmp(field,"throwdir"))
lua_pushinteger(L, plr->throwdir);
else if (fastcmp(field,"sadtimer"))

View file

@ -190,6 +190,9 @@ boolean Obj_OrbinautJawzCollide(mobj_t *t1, mobj_t *t2)
return true;
}
if (K_TryPickMeUp(t1, t2))
return true;
if (t1->type == MT_GARDENTOP)
{
tumbleitem = true;

View file

@ -667,6 +667,9 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
if (!player->mo || player->spectator)
return;
if (K_TryPickMeUp(special, toucher))
return;
// attach to player!
P_SetTarget(&special->tracer, toucher);
toucher->flags |= MF_NOGRAVITY;

View file

@ -5402,6 +5402,19 @@ static boolean P_IsTrackerType(INT32 type)
case MT_GARDENTOP: // Frey
return true;
case MT_JAWZ_SHIELD: // Pick-me-up
case MT_ORBINAUT:
case MT_ORBINAUT_SHIELD:
case MT_DROPTARGET:
case MT_DROPTARGET_SHIELD:
case MT_LANDMINE:
case MT_BANANA:
case MT_BANANA_SHIELD:
case MT_GACHABOM:
case MT_EGGMANITEM:
case MT_EGGMANITEM_SHIELD:
return true;
default:
return false;
}

View file

@ -510,6 +510,8 @@ static void P_NetArchivePlayers(savebuffer_t *save)
WRITESINT8(save->p, players[i].itemtype);
WRITEUINT8(save->p, players[i].itemamount);
WRITESINT8(save->p, players[i].backupitemtype);
WRITEUINT8(save->p, players[i].backupitemamount);
WRITESINT8(save->p, players[i].throwdir);
WRITEUINT8(save->p, players[i].sadtimer);
@ -1152,6 +1154,8 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
players[i].itemtype = READSINT8(save->p);
players[i].itemamount = READUINT8(save->p);
players[i].backupitemtype = READSINT8(save->p);
players[i].backupitemamount = READUINT8(save->p);
players[i].throwdir = READSINT8(save->p);
players[i].sadtimer = READUINT8(save->p);

View file

@ -1540,6 +1540,9 @@ sfxinfo_t S_sfx[NUMSFX] =
// Walltransfer
{"ggfall", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
// :apple:
{"aple", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
// SRB2kart - Skin sounds
{"kwin", false, 64, 96, -1, NULL, 0, SKSKWIN, -1, LUMPERROR, ""},
{"klose", false, 64, 96, -1, NULL, 0, SKSKLOSE, -1, LUMPERROR, ""},

View file

@ -1616,6 +1616,9 @@ typedef enum
// Walltransfer fuck
sfx_ggfall,
// :apple:
sfx_aple,
// And LASTLY, Kart's skin sounds.
sfx_kwin,
sfx_klose,