WIP - Dynamic roulette

This commit is contained in:
Antonio Martinez 2024-08-20 04:22:00 -07:00
parent 0dfdf563f3
commit b51f4d28a9
5 changed files with 218 additions and 158 deletions

View file

@ -495,7 +495,6 @@ struct itemroulette_t
SINT8 *itemList;
#endif
UINT8 preexpuseOdds, useOdds;
UINT8 playing, exiting;
UINT32 preexpdist, dist, baseDist;
UINT32 firstDist, secondDist;

View file

@ -5729,7 +5729,6 @@ static void K_drawDistributionDebugger(void)
y += space;
}
V_DrawString((x >> FRACBITS) + 20, 2, V_SNAPTOTOP, va("useOdds[%u]", rouletteData.useOdds));
V_DrawString((x >> FRACBITS) + 20, 10, V_SNAPTOTOP, va("speed = %u", rouletteData.speed));
V_DrawString((x >> FRACBITS) + 20, 22, V_SNAPTOTOP, va("baseDist = %u", rouletteData.baseDist));

View file

@ -7413,32 +7413,8 @@ void K_DropHnextList(player_t *player)
SINT8 K_GetTotallyRandomResult(UINT8 useodds)
{
INT32 spawnchance[NUMKARTRESULTS];
INT32 totalspawnchance = 0;
INT32 i;
memset(spawnchance, 0, sizeof (spawnchance));
for (i = 1; i < NUMKARTRESULTS; i++)
{
// Avoid calling K_FillItemRouletteData since that
// function resets PR_ITEM_ROULETTE.
spawnchance[i] = (
totalspawnchance += K_KartGetItemOdds(NULL, NULL, useodds, i)
);
}
if (totalspawnchance > 0)
{
totalspawnchance = P_RandomKey(PR_ITEM_ROULETTE, totalspawnchance);
for (i = 0; i < NUMKARTRESULTS && spawnchance[i] <= totalspawnchance; i++);
}
else
{
i = KITEM_SAD;
}
return i;
// TODO
return 0;
}
mobj_t *K_CreatePaperItem(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 flip, UINT8 type, UINT16 amount)

View file

@ -651,6 +651,9 @@ typedef struct {
--------------------------------------------------*/
static fixed_t K_AdjustItemOddsToConditions(fixed_t newOdds, const itemconditions_t *conditions, const itemroulette_t *roulette)
{
// TODO
return newOdds;
// None if this applies outside of Race modes (for now?)
if ((gametyperules & GTR_CIRCUIT) == 0)
{
@ -696,6 +699,7 @@ static fixed_t K_AdjustItemOddsToConditions(fixed_t newOdds, const itemcondition
See header file for description.
--------------------------------------------------*/
INT32 K_KartGetItemOdds(const player_t *player, itemroulette_t *const roulette, UINT8 pos, kartitems_t item)
{
boolean bot = false;
@ -888,116 +892,6 @@ INT32 K_KartGetItemOdds(const player_t *player, itemroulette_t *const roulette,
return newOdds;
}
/*--------------------------------------------------
static UINT8 K_FindUseodds(const player_t *player, itemroulette_t *const roulette)
Gets which item bracket the player is in.
This can be adjusted depending on which
items being turned off.
Input Arguments:-
player - The player the roulette is for.
roulette - The item roulette data.
Return:-
The item bracket the player is in, as an
index to the array.
--------------------------------------------------*/
static UINT8 K_FindUseodds(const player_t *player, itemroulette_t *const roulette)
{
UINT8 i;
UINT8 useOdds = 0;
UINT8 distTable[14];
UINT8 distLen = 0;
UINT8 totalSize = 0;
boolean oddsValid[8];
for (i = 0; i < 8; i++)
{
UINT8 j;
if (gametype == GT_BATTLE && i > 1)
{
oddsValid[i] = false;
continue;
}
else if (specialstageinfo.valid == true && i > 3)
{
oddsValid[i] = false;
continue;
}
for (j = 1; j < NUMKARTRESULTS; j++)
{
if (K_KartGetItemOdds(player, roulette, i, j) > 0)
{
break;
}
}
oddsValid[i] = (j < NUMKARTRESULTS);
}
#define SETUPDISTTABLE(odds, num) \
totalSize += num; \
if (oddsValid[odds]) \
for (i = num; i; --i) \
distTable[distLen++] = odds;
if (gametype == GT_BATTLE) // Battle Mode
{
useOdds = 0;
}
else
{
if (specialstageinfo.valid == true) // Special Stages
{
SETUPDISTTABLE(0,2);
SETUPDISTTABLE(1,2);
SETUPDISTTABLE(2,3);
SETUPDISTTABLE(3,1);
}
else
{
SETUPDISTTABLE(0,1);
SETUPDISTTABLE(1,1);
SETUPDISTTABLE(2,1);
SETUPDISTTABLE(3,2);
SETUPDISTTABLE(4,2);
SETUPDISTTABLE(5,3);
SETUPDISTTABLE(6,3);
SETUPDISTTABLE(7,1);
}
for (i = 0; i < totalSize; i++)
{
fixed_t pos = 0;
fixed_t dist = 0;
UINT8 index = 0;
if (i == totalSize-1)
{
useOdds = distTable[distLen - 1];
break;
}
pos = ((i << FRACBITS) * distLen) / totalSize;
dist = FixedMul(DISTVAR << FRACBITS, pos) >> FRACBITS;
index = FixedInt(FixedRound(pos));
if (roulette->dist <= (unsigned)dist)
{
useOdds = distTable[index];
break;
}
}
}
#undef SETUPDISTTABLE
return useOdds;
}
/*--------------------------------------------------
static boolean K_ForcedSPB(const player_t *player, itemroulette_t *const roulette)
@ -1094,7 +988,6 @@ static void K_InitRoulette(itemroulette_t *const roulette)
roulette->itemListLen = 0;
roulette->index = 0;
roulette->useOdds = UINT8_MAX;
roulette->baseDist = roulette->dist = 0;
roulette->playing = roulette->exiting = 0;
roulette->firstDist = roulette->secondDist = UINT32_MAX;
@ -1328,7 +1221,7 @@ void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulet
UINT8 numItems = 0;
kartitems_t singleItem = KITEM_SAD;
size_t i;
size_t i, j;
K_InitRoulette(roulette);
@ -1417,7 +1310,7 @@ void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulet
// every item in the game!
// Create the same item reel given the same inputs.
P_SetRandSeed(PR_ITEM_ROULETTE, ITEM_REEL_SEED);
// P_SetRandSeed(PR_ITEM_ROULETTE, ITEM_REEL_SEED);
for (i = 1; i < NUMKARTRESULTS; i++)
{
@ -1489,40 +1382,235 @@ void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulet
// actually calculate actual item reels.
roulette->preexpdist = K_GetItemRouletteDistance(player, roulette->playing);
roulette->dist = roulette->preexpdist;
roulette->preexpuseOdds = K_FindUseodds(player, roulette);
roulette->dist = FixedMul(roulette->preexpdist, max(player->exp, FRACUNIT/2));
roulette->useOdds = K_FindUseodds(player, roulette);
if (roulette->preexpuseOdds > roulette->useOdds)
// == EVERYTHING FUCKED BELOW THIS LINE
UINT32 targetpower = roulette->dist; // fill roulette with items around this value!
UINT32 powers[NUMKARTRESULTS]; // how strong is each item?
UINT32 deltas[NUMKARTRESULTS]; // how different is that strength from target?
UINT32 candidates[NUMKARTRESULTS]; // how many of this item should we try to insert?
UINT32 dupetolerance[NUMKARTRESULTS]; // how willing are we to select this item after already selecting it? higher values = lower dupe penalty
UINT32 duplicates[NUMKARTRESULTS]; // how many copies of this item are already waiting to be inserted?
boolean permit[NUMKARTRESULTS]; // is this item allowed?
boolean firstonly[NUMKARTRESULTS]; // is this item only first?
boolean nofirst[NUMKARTRESULTS]; // is this item never for first?
// Items permissible?
for (i = 1; i < NUMKARTRESULTS; i++)
{
K_AddMessageForPlayer(player, va("items NERFED %u -> %u", roulette->preexpuseOdds, roulette->useOdds), true, false);
CONS_Printf("%s items NERFED %u -> %u\n", player_names[player - players], roulette->preexpuseOdds, roulette->useOdds);
permit[i] = true;
boolean notNearEnd = false;
boolean powerItem = false;
boolean cooldownOnStart = false;
switch (i)
{
case KITEM_BANANA:
case KITEM_EGGMAN:
case KITEM_SUPERRING:
{
notNearEnd = true;
break;
}
case KITEM_ROCKETSNEAKER:
case KITEM_JAWZ:
case KITEM_LANDMINE:
case KITEM_DROPTARGET:
case KITEM_BALLHOG:
case KRITEM_TRIPLESNEAKER:
case KRITEM_TRIPLEORBINAUT:
case KRITEM_QUADORBINAUT:
case KRITEM_DUALJAWZ:
{
powerItem = true;
break;
}
case KITEM_HYUDORO:
case KRITEM_TRIPLEBANANA:
{
powerItem = true;
notNearEnd = true;
break;
}
case KITEM_INVINCIBILITY:
case KITEM_MINE:
case KITEM_GROW:
case KITEM_BUBBLESHIELD:
{
cooldownOnStart = true;
powerItem = true;
break;
}
case KITEM_FLAMESHIELD:
case KITEM_GARDENTOP:
{
cooldownOnStart = true;
powerItem = true;
notNearEnd = true;
break;
}
case KITEM_SPB:
{
cooldownOnStart = true;
notNearEnd = true;
// TODO forcing, just disable for now
permit[i] = false;
break;
}
case KITEM_SHRINK:
{
cooldownOnStart = true;
powerItem = true;
notNearEnd = true;
break;
}
case KITEM_LIGHTNINGSHIELD:
{
cooldownOnStart = true;
powerItem = true;
if ((gametyperules & GTR_CIRCUIT) && spbplace != -1)
{
permit[i] = false;
}
break;
}
}
if (cooldownOnStart && (leveltime < (30*TICRATE) + starttime))
permit[i] = false;
if (notNearEnd && (roulette != NULL && roulette->baseDist < ENDDIST))
permit[i] = false;
}
else if (roulette->preexpuseOdds < roulette->useOdds)
// invent some bullshit Ps based on existing useodds, temp
for (i = 1; i < NUMKARTRESULTS; i++)
{
K_AddMessageForPlayer(player, va("items BOOSTED %u -> %u", roulette->preexpuseOdds, roulette->useOdds), true, false);
CONS_Printf("%s items BOOSTED %u -> %u\n", player_names[player - players], roulette->preexpuseOdds, roulette->useOdds);
powers[i] = 0;
dupetolerance[i] = 0;
UINT32 entries = 0;
firstonly[i] = true;
nofirst[i] = true;
for (j = 0; j < 8; j++)
{
if (K_KartItemOddsRace[i-1][j] > 0)
{
powers[i] += (j+1) * DISTVAR * K_KartItemOddsRace[i-1][j];
entries += K_KartItemOddsRace[i-1][j];
if (j > 0)
firstonly[i] = false;
if (j == 0)
nofirst[i] = false;
}
}
dupetolerance[i] += entries;
if (entries)
powers[i] /= entries;
// CONS_Printf("%s: %d - %d - FO %d - NF %d\n", cv_items[i-1].name, powers[i], dupetolerance[i], firstonly[i], nofirst[i]);
}
// temp - null stuff that doesn't have odds, then distance correct
for (i = 1; i < NUMKARTRESULTS; i++)
{
if (powers[i] == 0)
permit[i] = false;
else
powers[i] -= DISTVAR;
}
// stupid hack for test run / no waypoints
if (player->position == 1)
targetpower = 0;
// Starting deltas
for (i = 1; i < NUMKARTRESULTS; i++)
{
candidates[i] = 0;
deltas[i] = min(targetpower - powers[i], powers[i] - targetpower);
// CONS_Printf("starting delta for %s is %d\n", cv_items[i-1].name, deltas[i]);
}
// let's start finding items to list
for (i = 0; i < 15; i++)
{
UINT32 lowestdelta = INT32_MAX;
size_t lowestindex = 0;
// CONS_Printf("LOOP %d\n", i);
// check each kartitem to see which is the best fit,
// based on what's closest to our target power
// (but ignore items that aren't allowed now)
for (j = 1; j < NUMKARTRESULTS; j++)
{
// CONS_Printf("precheck %s, FO %d NF %d CD %d\n", cv_items[j-1].name, firstonly[j], nofirst[j], K_GetItemCooldown(j));
if (!permit[j])
continue;
if (K_GetItemCooldown(j))
continue;
if (!K_ItemEnabled(j))
continue;
if (firstonly[j] && player->position > 1)
continue;
if (nofirst[j] && player->position == 1)
continue;
// CONS_Printf("checking %s, delta %d\n", cv_items[j-1].name, deltas[j]);
if (lowestdelta > deltas[j])
{
lowestindex = j;
lowestdelta = deltas[j];
}
}
// couldn't find an item? goodbye lol
if (lowestindex == 0)
break;
// otherwise, prep it to be added and give it a duplicaton penalty,
// so that a different item is more likely to be inserted next
candidates[lowestindex]++;
duplicates[lowestindex]++;
deltas[lowestindex] += (DISTVAR*6/dupetolerance[lowestindex])^(duplicates[lowestindex]);
// CONS_Printf("added %s with candidates %d\n", cv_items[lowestindex-1].name, candidates[lowestindex]);
}
// set up the list indices used to random-shuffle the ro ulette
for (i = 1; i < NUMKARTRESULTS; i++)
{
spawnChance[i] = (
totalSpawnChance += K_KartGetItemOdds(player, roulette, roulette->useOdds, i)
totalSpawnChance += candidates[i]
);
}
if (totalSpawnChance == 0)
{
// This shouldn't happen, but if it does, early exit.
// Maybe can happen if you enable multiple items for
// another gametype, so we give the singleItem as a fallback.
// why did this fucking happen LOL
// don't crash
K_AddItemToReel(player, roulette, singleItem);
return;
}
// Create the same item reel given the same inputs.
P_SetRandSeed(PR_ITEM_ROULETTE, ITEM_REEL_SEED);
// P_SetRandSeed(PR_ITEM_ROULETTE, ITEM_REEL_SEED);
// and insert all of our candidates into the roulette in a random order
while (totalSpawnChance > 0)
{
rngRoll = P_RandomKey(PR_ITEM_ROULETTE, totalSpawnChance);
@ -1531,6 +1619,8 @@ void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulet
continue;
}
// CONS_Printf("adding %s, tsp %d\n", cv_items[i-1].name, totalSpawnChance);
K_AddItemToReel(player, roulette, i);
for (; i < NUMKARTRESULTS; i++)

View file

@ -744,8 +744,6 @@ static void P_NetArchivePlayers(savebuffer_t *save)
}
#endif
WRITEUINT8(save->p, players[i].itemRoulette.preexpuseOdds);
WRITEUINT8(save->p, players[i].itemRoulette.useOdds);
WRITEUINT32(save->p, players[i].itemRoulette.preexpdist);
WRITEUINT32(save->p, players[i].itemRoulette.dist);
WRITEUINT32(save->p, players[i].itemRoulette.index);
@ -1370,8 +1368,6 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
}
#endif
players[i].itemRoulette.preexpuseOdds = READUINT8(save->p);
players[i].itemRoulette.useOdds = READUINT8(save->p);
players[i].itemRoulette.preexpdist = READUINT32(save->p);
players[i].itemRoulette.dist = READUINT32(save->p);
players[i].itemRoulette.index = (size_t)READUINT32(save->p);