diff --git a/src/d_player.h b/src/d_player.h index 6a3598def..1bc0d07e6 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -579,6 +579,8 @@ struct player_t UINT8 positiondelay; // Used for position number, so it can grow when passing UINT32 distancetofinish; UINT32 distancetofinishprev; + UINT32 lastpickupdistance; // Anti item set farming + UINT8 lastpickuptype; waypoint_t *currentwaypoint; waypoint_t *nextwaypoint; respawnvars_t respawn; // Respawn info diff --git a/src/k_kart.c b/src/k_kart.c index 7ced55df6..e5e8ac4cd 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -8245,6 +8245,9 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) player->incontrol = min(player->incontrol, 5*TICRATE); player->incontrol = max(player->incontrol, -5*TICRATE); + if (P_PlayerInPain(player) || player->respawn.state != RESPAWNST_NONE) + player->lastpickuptype = -1; // got your ass beat, go grab anything + if (player->tumbleBounces > 0) { K_HandleTumbleSound(player); diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index dc9c7f977..dca270903 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -227,6 +227,10 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->distancetofinish); else if (fastcmp(field,"distancetofinishprev")) lua_pushinteger(L, plr->distancetofinishprev); + else if (fastcmp(field,"lastpickupdistance")) + lua_pushinteger(L, plr->lastpickupdistance); + else if (fastcmp(field,"lastpickuptype")) + lua_pushinteger(L, plr->lastpickuptype); else if (fastcmp(field,"airtime")) lua_pushinteger(L, plr->airtime); else if (fastcmp(field,"flashing")) @@ -639,6 +643,8 @@ static int player_set(lua_State *L) return NOSET; else if (fastcmp(field,"distancetofinishprev")) return NOSET; + else if (fastcmp(field,"lastpickupdistance")) + plr->airtime = luaL_checkinteger(L, 3); else if (fastcmp(field,"airtime")) plr->airtime = luaL_checkinteger(L, 3); else if (fastcmp(field,"flashing")) diff --git a/src/objects/random-item.c b/src/objects/random-item.c index a5db705f6..b2dcdc6b9 100644 --- a/src/objects/random-item.c +++ b/src/objects/random-item.c @@ -47,6 +47,11 @@ static player_t *GetItemBoxPlayer(mobj_t *mobj) // Always use normal item box rules -- could pass in "2" for fakes but they blend in better like this if (P_CanPickupItem(&players[i], 1)) { + // Check for players who can take this pickup, but won't be allowed to (antifarming) + UINT8 mytype = (mobj->flags2 & MF2_AMBUSH) ? 2 : 1; + if (P_IsPickupCheesy(&players[i], mytype)) + continue; + fixed_t dist = P_AproxDistance(P_AproxDistance( players[i].mo->x - mobj->x, players[i].mo->y - mobj->y), diff --git a/src/p_inter.c b/src/p_inter.c index 86004ad50..7d8a00372 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -118,6 +118,10 @@ boolean P_CanPickupItem(player_t *player, UINT8 weapon) if (player->exiting || mapreset || (player->pflags & PF_ELIMINATED)) return false; + // 0: Sphere/Ring + // 1: Random Item / Capsule + // 2: Eggbox + // 3: Paperitem if (weapon) { // Item slot already taken up @@ -155,6 +159,27 @@ boolean P_CanPickupItem(player_t *player, UINT8 weapon) return true; } +// Allow players to pick up only one pickup from each set of pickups. +// Anticheese pickup types are different than-P_CanPickupItem weapon, because that system is +// already slightly scary without introducing special cases for different types of the same pickup. +// 1 = floating item, 2 = perma ring, 3 = capsule +boolean P_IsPickupCheesy(player_t *player, UINT8 type) +{ + if (player->lastpickupdistance && player->lastpickuptype == type) + { + UINT32 distancedelta = min(player->distancetofinish - player->lastpickupdistance, player->lastpickupdistance - player->distancetofinish); + if (distancedelta < 2500) + return true; + } + return false; +} + +void P_UpdateLastPickup(player_t *player, UINT8 type) +{ + player->lastpickuptype = type; + player->lastpickupdistance = player->distancetofinish; +} + boolean P_CanPickupEmblem(player_t *player, INT32 emblemID) { if (emblemID < 0 || emblemID >= MAXEMBLEMS) @@ -364,11 +389,16 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) special->flags &= ~MF_SPECIAL; return; case MT_RANDOMITEM: + UINT8 cheesetype = (special->flags2 & MF2_AMBUSH) ? 2 : 1; + if (!P_CanPickupItem(player, 1)) return; + if (P_IsPickupCheesy(player, cheesetype)) + return; special->momx = special->momy = special->momz = 0; P_SetTarget(&special->target, toucher); + P_UpdateLastPickup(player, cheesetype); // P_KillMobj(special, toucher, toucher, DMG_NORMAL); if (special->extravalue1 >= RINGBOX_TIME) K_StartItemRoulette(player, false); @@ -404,9 +434,15 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) default: if (!P_CanPickupItem(player, 1)) return; + if (P_IsPickupCheesy(player, 3)) + return; break; } + // Ring Capsules shouldn't affect pickup cheese, they're just used as condensed ground-ring placements. + if (special->threshold != KITEM_SUPERRING) + P_UpdateLastPickup(player, 3); + S_StartSound(toucher, special->info->deathsound); P_KillMobj(special, toucher, toucher, DMG_NORMAL); return; diff --git a/src/p_local.h b/src/p_local.h index 4fab4fa74..4c6b44449 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -543,6 +543,8 @@ void P_CheckPointLimit(void); boolean P_CheckRacers(void); boolean P_CanPickupItem(player_t *player, UINT8 weapon); +boolean P_IsPickupCheesy(player_t *player, UINT8 type); +void P_UpdateLastPickup(player_t *player, UINT8 type); boolean P_CanPickupEmblem(player_t *player, INT32 emblemID); boolean P_EmblemWasCollected(INT32 emblemID); diff --git a/src/p_saveg.c b/src/p_saveg.c index c3db3804c..7f2410694 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -389,6 +389,8 @@ static void P_NetArchivePlayers(savebuffer_t *save) WRITEUINT8(save->p, players[i].positiondelay); WRITEUINT32(save->p, players[i].distancetofinish); WRITEUINT32(save->p, players[i].distancetofinishprev); + WRITEUINT32(save->p, players[i].lastpickupdistance); + WRITEUINT8(save->p, players[i].lastpickuptype); WRITEUINT32(save->p, K_GetWaypointHeapIndex(players[i].currentwaypoint)); WRITEUINT32(save->p, K_GetWaypointHeapIndex(players[i].nextwaypoint)); WRITEUINT32(save->p, players[i].airtime); @@ -862,6 +864,8 @@ static void P_NetUnArchivePlayers(savebuffer_t *save) players[i].positiondelay = READUINT8(save->p); players[i].distancetofinish = READUINT32(save->p); players[i].distancetofinishprev = READUINT32(save->p); + players[i].lastpickupdistance = READUINT32(save->p); + players[i].lastpickuptype = READUINT8(save->p); players[i].currentwaypoint = (waypoint_t *)(size_t)READUINT32(save->p); players[i].nextwaypoint = (waypoint_t *)(size_t)READUINT32(save->p); players[i].airtime = READUINT32(save->p);