Merge branch 'item-scale-tweak' into 'master'

Change the item odds/distance scaling for different player counts

See merge request KartKrew/Kart!429
This commit is contained in:
Sal 2021-06-12 05:16:31 -04:00
commit 1f0d0ea396
3 changed files with 231 additions and 114 deletions

View file

@ -4063,6 +4063,7 @@ static void K_drawDistributionDebugger(void)
kp_superring[1],
kp_kitchensink[1],
kp_sneaker[1],
kp_sneaker[1],
kp_banana[1],
kp_banana[1],
@ -4102,28 +4103,32 @@ static void K_drawDistributionDebugger(void)
}
}
if (franticitems) // Frantic items make the distances between everyone artifically higher, for crazier items
pdis = (15 * pdis) / 14;
if (spbplace != -1 && stplyr->position == spbplace+1) // SPB Rush Mode: It's 2nd place's job to catch-up items and make 1st place's job hell
if (spbplace != -1 && stplyr->position == spbplace+1)
{
// SPB Rush Mode: It's 2nd place's job to catch-up items and make 1st place's job hell
pdis = (3 * pdis) / 2;
spbrush = true;
}
pdis = K_ScaleItemDistance(pdis, pingame, spbrush);
if (stplyr->bot && stplyr->botvars.rival)
{
// Rival has better odds :)
pdis = (15 * pdis) / 14;
}
pdis = ((28 + (8-pingame)) * pdis) / 28; // scale with player count
useodds = K_FindUseodds(stplyr, 0, pdis, bestbumper, spbrush);
for (i = 1; i < NUMKARTRESULTS; i++)
{
const INT32 itemodds = K_KartGetItemOdds(useodds, i, 0, spbrush, stplyr->bot, (stplyr->bot && stplyr->botvars.rival));
INT32 itemodds = K_KartGetItemOdds(
useodds, i,
stplyr->distancetofinish,
0,
spbrush, stplyr->bot, (stplyr->bot && stplyr->botvars.rival)
);
if (itemodds <= 0)
continue;
@ -4145,6 +4150,9 @@ static void K_drawDistributionDebugger(void)
case KRITEM_DUALJAWZ:
amount = 2;
break;
case KRITEM_DUALSNEAKER:
amount = 2;
break;
default:
amount = 3;
break;

View file

@ -348,7 +348,7 @@ static INT32 K_KartItemOddsRace[NUMKARTRESULTS-1][8] =
/*Mine*/ { 0, 2, 3, 1, 0, 0, 0, 0 }, // Mine
/*Land Mine*/ { 4, 0, 0, 0, 0, 0, 0, 0 }, // Land Mine
/*Ballhog*/ { 0, 0, 2, 1, 0, 0, 0, 0 }, // Ballhog
/*Self-Propelled Bomb*/ { 0, 1, 2, 3, 4, 2, 2, 0 }, // Self-Propelled Bomb
/*Self-Propelled Bomb*/ { 0, 0, 0, 0, 0, 2, 4, 0 }, // Self-Propelled Bomb
/*Grow*/ { 0, 0, 0, 1, 2, 3, 0, 0 }, // Grow
/*Shrink*/ { 0, 0, 0, 0, 0, 0, 2, 0 }, // Shrink
/*Thunder Shield*/ { 1, 2, 0, 0, 0, 0, 0, 0 }, // Thunder Shield
@ -400,6 +400,9 @@ static INT32 K_KartItemOddsBattle[NUMKARTRESULTS][2] =
};
#define DISTVAR (2048) // Magic number distance for use with item roulette tiers
#define SPBSTARTDIST (5*DISTVAR) // Distance when SPB is forced onto 2nd place
#define SPBFORCEDIST (15*DISTVAR) // Distance when SPB is forced onto 2nd place
#define ENDDIST (12*DISTVAR) // Distance when the game stops giving you bananas
// Array of states to pick the starting point of the animation, based on the actual time left for invincibility.
static INT32 K_SparkleTrailStartStates[KART_NUMINVSPARKLESANIM][2] = {
@ -491,6 +494,57 @@ static void K_KartGetItemResult(player_t *player, SINT8 getitem)
}
}
fixed_t K_ItemOddsScale(UINT8 numPlayers, boolean spbrush)
{
const UINT8 basePlayer = 8; // The player count we design most of the game around.
UINT8 playerCount = (spbrush ? 2 : numPlayers);
fixed_t playerScaling = 0;
// Then, it multiplies it further if the player count isn't equal to basePlayer.
// This is done to make low player count races more interesting and high player count rates more fair.
// (If you're in SPB mode and in 2nd place, it acts like it's a 1v1, so the catch-up game is not weakened.)
if (playerCount < basePlayer)
{
// Less than basePlayer: increase odds significantly.
// 2P: x2.5
playerScaling = (basePlayer - playerCount) * (FRACUNIT / 4);
}
else if (playerCount > basePlayer)
{
// More than basePlayer: reduce odds slightly.
// 16P: x0.75
playerScaling = (basePlayer - playerCount) * (FRACUNIT / 32);
}
return playerScaling;
}
UINT32 K_ScaleItemDistance(UINT32 distance, UINT8 numPlayers, boolean spbrush)
{
if (mapobjectscale != FRACUNIT)
{
// Bring back to normal scale.
distance = FixedDiv(distance * FRACUNIT, mapobjectscale) / FRACUNIT;
}
if (franticitems == true)
{
// Frantic items pretends everyone's farther apart, for crazier items.
distance = (15 * distance) / 14;
}
if (numPlayers > 0)
{
// Items get crazier with the fewer players that you have.
distance = FixedMul(
distance * FRACUNIT,
FRACUNIT + (K_ItemOddsScale(numPlayers, spbrush) / 2)
) / FRACUNIT;
}
return distance;
}
/** \brief Item Roulette for Kart
\param player player object passed from P_KartPlayerThink
@ -498,13 +552,26 @@ static void K_KartGetItemResult(player_t *player, SINT8 getitem)
\return void
*/
INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean spbrush, boolean bot, boolean rival)
INT32 K_KartGetItemOdds(
UINT8 pos, SINT8 item,
UINT32 ourDist,
fixed_t mashed,
boolean spbrush, boolean bot, boolean rival)
{
INT32 newodds;
INT32 i;
UINT8 pingame = 0, pexiting = 0;
SINT8 first = -1, second = -1;
INT32 secondist = 0;
UINT32 firstDist = UINT32_MAX;
UINT32 secondToFirst = UINT32_MAX;
boolean powerItem = false;
boolean cooldownOnStart = false;
boolean indirectItem = false;
boolean notNearEnd = false;
INT32 shieldtype = KSHIELD_NONE;
I_Assert(item > KITEM_NONE); // too many off by one scenarioes.
@ -513,6 +580,24 @@ INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean spbrush,
if (!KartItemCVars[item-1]->value && !modeattacking)
return 0;
/*
if (bot)
{
// TODO: Item use on bots should all be passed-in functions.
// Instead of manually inserting these, it should return 0
// for any items without an item use function supplied
switch (item)
{
case KITEM_SNEAKER:
break;
default:
return 0;
}
}
*/
(void)bot;
if (gametype == GT_BATTLE)
{
I_Assert(pos < 6); // DO NOT allow positions past the bounds of the table
@ -557,140 +642,149 @@ INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean spbrush,
if (first != -1 && second != -1) // calculate 2nd's distance from 1st, for SPB
{
secondist = players[second].distancetofinish - players[first].distancetofinish;
if (franticitems)
secondist = (15 * secondist) / 14;
secondist = ((28 + (8-pingame)) * secondist) / 28;
}
firstDist = players[first].distancetofinish;
// POWERITEMODDS handles all of the "frantic item" related functionality, for all of our powerful items.
// First, it multiplies it by 2 if franticitems is true; easy-peasy.
// Next, it multiplies it again if it's in SPB mode and 2nd needs to apply pressure to 1st.
// Then, it multiplies it further if the player count isn't equal to 8.
// This is done to make low player count races more interesting and high player count rates more fair.
// (2P normal would be about halfway between 8P normal and 8P frantic.)
// (This scaling is not done for SPB Rush, so that catchup strength is not weakened.)
// Lastly, it *divides* it by your mashed value, which was determined in K_KartItemRoulette, for lesser items needed in a pinch.
#define PLAYERSCALING (8 - (spbrush ? 2 : pingame))
#define POWERITEMODDS(odds) {\
if (franticitems) \
odds *= 2; \
if (rival) \
odds *= 2; \
odds = FixedMul(odds * FRACUNIT, FRACUNIT + ((PLAYERSCALING * FRACUNIT) / 25)) / FRACUNIT; \
if (mashed > 0) \
odds = FixedDiv(odds * FRACUNIT, FRACUNIT + mashed) / FRACUNIT; \
}
#define COOLDOWNONSTART (leveltime < (30*TICRATE)+starttime)
/*
if (bot)
{
// TODO: Item use on bots should all be passed-in functions.
// Instead of manually inserting these, it should return 0
// for any items without an item use function supplied
switch (item)
if (mapobjectscale != FRACUNIT)
{
case KITEM_SNEAKER:
case KITEM_ROCKETSNEAKER:
case KITEM_INVINCIBILITY:
case KITEM_BANANA:
case KITEM_EGGMAN:
case KITEM_ORBINAUT:
case KITEM_JAWZ:
case KITEM_MINE:
case KITEM_LANDMINE:
case KITEM_BALLHOG:
case KITEM_SPB:
case KITEM_GROW:
case KITEM_SHRINK:
case KITEM_HYUDORO:
case KITEM_SUPERRING:
case KITEM_THUNDERSHIELD:
case KITEM_BUBBLESHIELD:
case KITEM_FLAMESHIELD:
case KRITEM_DUALSNEAKER:
case KRITEM_TRIPLESNEAKER:
case KRITEM_TRIPLEBANANA:
case KRITEM_TENFOLDBANANA:
case KRITEM_TRIPLEORBINAUT:
case KRITEM_QUADORBINAUT:
case KRITEM_DUALJAWZ:
break;
default:
return 0;
firstDist = FixedDiv(firstDist * FRACUNIT, mapobjectscale) / FRACUNIT;
}
secondToFirst = K_ScaleItemDistance(
players[second].distancetofinish - players[first].distancetofinish,
pingame, spbrush
);
}
*/
(void)bot;
switch (item)
{
case KITEM_BANANA:
case KITEM_EGGMAN:
case KITEM_SUPERRING:
notNearEnd = true;
break;
case KITEM_ROCKETSNEAKER:
case KITEM_JAWZ:
case KITEM_LANDMINE:
case KITEM_BALLHOG:
case KRITEM_TRIPLESNEAKER:
case KRITEM_TRIPLEBANANA:
case KRITEM_TENFOLDBANANA:
case KRITEM_TRIPLEORBINAUT:
case KRITEM_QUADORBINAUT:
case KRITEM_DUALJAWZ:
POWERITEMODDS(newodds);
powerItem = true;
break;
case KRITEM_TRIPLEBANANA:
case KRITEM_TENFOLDBANANA:
powerItem = true;
notNearEnd = true;
break;
case KITEM_INVINCIBILITY:
case KITEM_MINE:
case KITEM_GROW:
case KITEM_BUBBLESHIELD:
case KITEM_FLAMESHIELD:
if (COOLDOWNONSTART)
newodds = 0;
else
POWERITEMODDS(newodds);
cooldownOnStart = true;
powerItem = true;
break;
case KITEM_SPB:
if ((indirectitemcooldown > 0) || COOLDOWNONSTART
|| (first != -1 && players[first].distancetofinish < 8*DISTVAR)) // No SPB near the end of the race
cooldownOnStart = true;
indirectItem = true;
notNearEnd = true;
if (firstDist < ENDDIST) // No SPB near the end of the race
{
newodds = 0;
}
else
{
INT32 multiplier = (secondist - (5*DISTVAR)) / DISTVAR;
const INT32 distFromStart = max(0, secondToFirst - SPBSTARTDIST);
const INT32 distRange = SPBFORCEDIST - SPBSTARTDIST;
const INT32 mulMax = 3;
INT32 multiplier = (distFromStart * mulMax) / distRange;
if (multiplier < 0)
multiplier = 0;
if (multiplier > 3)
multiplier = 3;
if (multiplier > mulMax)
multiplier = mulMax;
newodds *= multiplier;
}
break;
case KITEM_SHRINK:
if ((indirectitemcooldown > 0) || COOLDOWNONSTART || (pingame-1 <= pexiting))
cooldownOnStart = true;
powerItem = true;
indirectItem = true;
notNearEnd = true;
if (pingame-1 <= pexiting)
newodds = 0;
else
POWERITEMODDS(newodds);
break;
case KITEM_THUNDERSHIELD:
if (spbplace != -1 || COOLDOWNONSTART)
cooldownOnStart = true;
powerItem = true;
if (spbplace != -1)
newodds = 0;
else
POWERITEMODDS(newodds);
break;
case KITEM_HYUDORO:
if ((hyubgone > 0) || COOLDOWNONSTART)
cooldownOnStart = true;
notNearEnd = true;
if (hyubgone > 0)
newodds = 0;
break;
default:
break;
}
#undef POWERITEMODDS
if (newodds == 0)
{
// Nothing else we want to do with odds matters at this point :p
return newodds;
}
if ((indirectItem == true) && (indirectitemcooldown > 0))
{
// Too many items that act indirectly in a match can feel kind of bad.
newodds = 0;
}
else if ((cooldownOnStart == true) && (leveltime < (30*TICRATE)+starttime))
{
// This item should not appear at the beginning of a race. (Usually really powerful crowd-breaking items)
newodds = 0;
}
else if ((notNearEnd == true) && (ourDist < ENDDIST))
{
// This item should not appear at the end of a race. (Usually trap items that lose their effectiveness)
newodds = 0;
}
else if (powerItem == true)
{
// This item is a "power item". This activates "frantic item" toggle related functionality.
fixed_t fracOdds = newodds * FRACUNIT;
if (franticitems == true)
{
// First, power items multiply their odds by 2 if frantic items are on; easy-peasy.
fracOdds *= 2;
}
if (rival == true)
{
// The Rival bot gets frantic-like items, also :p
fracOdds *= 2;
}
fracOdds = FixedMul(fracOdds, FRACUNIT + K_ItemOddsScale(pingame, spbrush));
if (mashed > 0)
{
// Lastly, it *divides* it based on your mashed value, so that power items are less likely when you mash.
fracOdds = FixedDiv(fracOdds, FRACUNIT + mashed);
}
newodds = fracOdds / FRACUNIT;
}
return newodds;
}
@ -721,7 +815,12 @@ UINT8 K_FindUseodds(player_t *player, fixed_t mashed, UINT32 pdis, UINT8 bestbum
for (j = 1; j < NUMKARTRESULTS; j++)
{
if (K_KartGetItemOdds(i, j, mashed, spbrush, player->bot, (player->bot && player->botvars.rival)) > 0)
if (K_KartGetItemOdds(
i, j,
player->distancetofinish,
mashed,
spbrush, player->bot, (player->bot && player->botvars.rival)
) > 0)
{
available = true;
break;
@ -872,28 +971,21 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd)
}
}
if (mapobjectscale != FRACUNIT)
pdis = FixedDiv(pdis * FRACUNIT, mapobjectscale) / FRACUNIT;
if (franticitems) // Frantic items make the distances between everyone artifically higher, for crazier items
{
pdis = (15 * pdis) / 14;
}
if (spbplace != -1 && player->position == spbplace+1) // SPB Rush Mode: It's 2nd place's job to catch-up items and make 1st place's job hell
if (spbplace != -1 && player->position == spbplace+1)
{
// SPB Rush Mode: It's 2nd place's job to catch-up items and make 1st place's job hell
pdis = (3 * pdis) / 2;
spbrush = true;
}
pdis = K_ScaleItemDistance(pdis, pingame, spbrush);
if (player->bot && player->botvars.rival)
{
// Rival has better odds :)
pdis = (15 * pdis) / 14;
}
pdis = ((28 + (8-pingame)) * pdis) / 28; // scale with player count
// SPECIAL CASE No. 1:
// Fake Eggman items
if (player->roulettetype == 2)
@ -994,17 +1086,17 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd)
// SPECIAL CASE No. 5:
// Force SPB onto 2nd if they get too far behind
if ((gametyperules & GTR_CIRCUIT) && player->position == 2 && pdis > (DISTVAR*8)
if ((gametyperules & GTR_CIRCUIT) && player->position == 2 && pdis > SPBFORCEDIST
&& spbplace == -1 && !indirectitemcooldown && !dontforcespb
&& cv_selfpropelledbomb.value)
{
K_KartGetItemResult(player, KITEM_SPB);
player->karthud[khud_itemblink] = TICRATE;
player->karthud[khud_itemblinkmode] = (mashed ? 1 : 0);
player->karthud[khud_itemblinkmode] = 2;
player->itemroulette = 0;
player->roulettetype = 0;
if (P_IsDisplayPlayer(player))
S_StartSound(NULL, (mashed ? sfx_itrolm : sfx_itrolf));
S_StartSound(NULL, sfx_itrolk);
return;
}
@ -1017,7 +1109,14 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd)
useodds = K_FindUseodds(player, mashed, pdis, bestbumper, spbrush);
for (i = 1; i < NUMKARTRESULTS; i++)
spawnchance[i] = (totalspawnchance += K_KartGetItemOdds(useodds, i, mashed, spbrush, player->bot, (player->bot && player->botvars.rival)));
{
spawnchance[i] = (totalspawnchance += K_KartGetItemOdds(
useodds, i,
player->distancetofinish,
mashed,
spbrush, player->bot, (player->bot && player->botvars.rival))
);
}
// Award the player whatever power is rolled
if (totalspawnchance > 0)
@ -5299,7 +5398,15 @@ mobj_t *K_CreatePaperItem(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8
useodds = amount;
for (i = 1; i < NUMKARTRESULTS; i++)
spawnchance[i] = (totalspawnchance += K_KartGetItemOdds(useodds, i, 0, false, false, false));
{
spawnchance[i] = (totalspawnchance += K_KartGetItemOdds(
useodds, i,
UINT32_MAX,
0,
false, false, false
)
);
}
if (totalspawnchance > 0)
{

View file

@ -34,7 +34,9 @@ fixed_t K_GetKartGameSpeedScalar(SINT8 value);
extern consvar_t *KartItemCVars[NUMKARTRESULTS-1];
UINT8 K_FindUseodds(player_t *player, fixed_t mashed, UINT32 pdis, UINT8 bestbumper, boolean spbrush);
INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean spbrush, boolean bot, boolean rival);
fixed_t K_ItemOddsScale(UINT8 numPlayers, boolean spbrush);
UINT32 K_ScaleItemDistance(UINT32 distance, UINT8 numPlayers, boolean spbrush);
INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, UINT32 ourDist, fixed_t mashed, boolean spbrush, boolean bot, boolean rival);
INT32 K_GetShieldFromItem(INT32 item);
fixed_t K_GetMobjWeight(mobj_t *mobj, mobj_t *against);
boolean K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2);