mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
Merge remote-tracking branch 'origin/master' into ta-rebalance-gaiden
This commit is contained in:
commit
fad24db244
23 changed files with 1381 additions and 260 deletions
|
|
@ -123,6 +123,7 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32
|
|||
lua_hudlib.c
|
||||
lua_hudlib_drawlist.c
|
||||
lua_followerlib.c
|
||||
lua_itemroulettelib.c
|
||||
lua_profile.cpp
|
||||
k_kart.c
|
||||
k_respawn.c
|
||||
|
|
|
|||
|
|
@ -426,6 +426,7 @@ struct botvars_t
|
|||
UINT8 difficulty; // Bot's difficulty setting
|
||||
INT16 diffincrease; // In GP: bot difficulty will increase this much next round
|
||||
boolean rival; // If true, they're the GP rival
|
||||
boolean foe; // If true, in contention for top X
|
||||
|
||||
// All entries above persist between rounds and must be recorded in demos
|
||||
|
||||
|
|
@ -516,22 +517,24 @@ struct skybox_t {
|
|||
|
||||
// player_t struct for item roulette variables
|
||||
|
||||
// Doing this the right way is causing problems.
|
||||
// so FINE, it's a static length now.
|
||||
#define ITEM_LIST_SIZE (NUMKARTRESULTS << 3)
|
||||
// In case of dynamic alloc failure, break glass:
|
||||
// #define ITEM_LIST_SIZE (NUMKARTRESULTS << 3)
|
||||
|
||||
typedef struct itemlist_t
|
||||
{
|
||||
size_t len;
|
||||
#ifdef ITEM_LIST_SIZE
|
||||
SINT8 items[ITEM_LIST_SIZE];
|
||||
#else
|
||||
SINT8 *items;
|
||||
size_t cap;
|
||||
#endif
|
||||
} itemlist_t;
|
||||
|
||||
struct itemroulette_t
|
||||
{
|
||||
boolean active;
|
||||
|
||||
#ifdef ITEM_LIST_SIZE
|
||||
size_t itemListLen;
|
||||
SINT8 itemList[ITEM_LIST_SIZE];
|
||||
#else
|
||||
size_t itemListCap;
|
||||
size_t itemListLen;
|
||||
SINT8 *itemList;
|
||||
#endif
|
||||
itemlist_t itemList;
|
||||
|
||||
UINT8 playing, exiting;
|
||||
UINT32 preexpdist, dist, baseDist;
|
||||
|
|
|
|||
|
|
@ -5248,6 +5248,21 @@ struct int_const_s const INT_CONST[] = {
|
|||
{"ENDOFPOWERUPS",ENDOFPOWERUPS},
|
||||
{"LASTPOWERUP",LASTPOWERUP},
|
||||
{"NUMPOWERUPS",NUMPOWERUPS},
|
||||
|
||||
// kartslotmachine_t
|
||||
{"KSM_BAR", KSM_BAR},
|
||||
{"KSM_DOUBLEBAR", KSM_DOUBLEBAR},
|
||||
{"KSM_TRIPLEBAR", KSM_TRIPLEBAR},
|
||||
{"KSM_RING", KSM_RING},
|
||||
{"KSM_SEVEN", KSM_SEVEN},
|
||||
{"KSM_JACKPOT", KSM_JACKPOT},
|
||||
{"KSM__MAX", KSM__MAX},
|
||||
|
||||
// itemflags_t
|
||||
{"IF_USERINGS", IF_USERINGS},
|
||||
{"IF_ITEMOUT", IF_ITEMOUT},
|
||||
{"IF_EGGMANOUT", IF_EGGMANOUT},
|
||||
{"IF_HOLDREADY", IF_HOLDREADY},
|
||||
|
||||
// kartshields_t
|
||||
{"KSHIELD_NONE",KSHIELD_NONE},
|
||||
|
|
|
|||
|
|
@ -321,6 +321,7 @@ void G_ReadDemoExtraData(void)
|
|||
players[p].botvars.difficulty = READUINT8(demobuf.p);
|
||||
players[p].botvars.diffincrease = READINT16(demobuf.p); // needed to avoid having to duplicate logic
|
||||
players[p].botvars.rival = (boolean)READUINT8(demobuf.p);
|
||||
players[p].botvars.foe = (boolean)READUINT8(demobuf.p);
|
||||
}
|
||||
}
|
||||
if (extradata & DXD_PLAYSTATE)
|
||||
|
|
@ -497,6 +498,7 @@ void G_WriteDemoExtraData(void)
|
|||
WRITEUINT8(demobuf.p, players[i].botvars.difficulty);
|
||||
WRITEINT16(demobuf.p, players[i].botvars.diffincrease); // needed to avoid having to duplicate logic
|
||||
WRITEUINT8(demobuf.p, (UINT8)players[i].botvars.rival);
|
||||
WRITEUINT8(demobuf.p, (UINT8)players[i].botvars.foe);
|
||||
}
|
||||
}
|
||||
if (demo_extradata[i] & DXD_PLAYSTATE)
|
||||
|
|
@ -2132,6 +2134,7 @@ void G_BeginRecording(void)
|
|||
WRITEUINT8(demobuf.p, player->botvars.difficulty);
|
||||
WRITEINT16(demobuf.p, player->botvars.diffincrease); // needed to avoid having to duplicate logic
|
||||
WRITEUINT8(demobuf.p, (UINT8)player->botvars.rival);
|
||||
WRITEUINT8(demobuf.p, (UINT8)player->botvars.foe);
|
||||
}
|
||||
|
||||
// Name
|
||||
|
|
@ -3315,6 +3318,7 @@ void G_DoPlayDemoEx(const char *defdemoname, lumpnum_t deflumpnum)
|
|||
players[p].botvars.difficulty = READUINT8(demobuf.p);
|
||||
players[p].botvars.diffincrease = READINT16(demobuf.p); // needed to avoid having to duplicate logic
|
||||
players[p].botvars.rival = (boolean)READUINT8(demobuf.p);
|
||||
players[p].botvars.foe = (boolean)READUINT8(demobuf.p);
|
||||
}
|
||||
|
||||
K_UpdateShrinkCheat(&players[p]);
|
||||
|
|
|
|||
|
|
@ -2271,6 +2271,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
|
||||
INT16 botdiffincrease;
|
||||
boolean botrival;
|
||||
boolean botfoe;
|
||||
|
||||
boolean cangrabitems;
|
||||
|
||||
|
|
@ -2381,6 +2382,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
|
||||
botdiffincrease = players[player].botvars.diffincrease;
|
||||
botrival = players[player].botvars.rival;
|
||||
botfoe = players[player].botvars.foe;
|
||||
|
||||
totalring = players[player].totalring;
|
||||
xtralife = players[player].xtralife;
|
||||
|
|
@ -2641,6 +2643,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
p->spheres = spheres;
|
||||
p->botvars.diffincrease = botdiffincrease;
|
||||
p->botvars.rival = botrival;
|
||||
p->botvars.foe = botfoe;
|
||||
p->xtralife = xtralife;
|
||||
|
||||
// SRB2kart
|
||||
|
|
|
|||
|
|
@ -408,6 +408,8 @@ void K_UpdateMatchRaceBots(void)
|
|||
clear_bots(wantedbots);
|
||||
}
|
||||
|
||||
K_AssignFoes();
|
||||
|
||||
// We should have enough bots now :)
|
||||
|
||||
#ifdef HAVE_DISCORDRPC
|
||||
|
|
@ -615,7 +617,7 @@ fixed_t K_BotMapModifier(void)
|
|||
--------------------------------------------------*/
|
||||
static UINT32 K_BotRubberbandDistance(const player_t *player)
|
||||
{
|
||||
const UINT32 spacing = FixedDiv(640 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)) / FRACUNIT;
|
||||
UINT32 spacing = FixedDiv(640 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)) / FRACUNIT;
|
||||
const UINT8 portpriority = player - players;
|
||||
UINT8 pos = 1;
|
||||
UINT8 i;
|
||||
|
|
@ -626,6 +628,11 @@ static UINT32 K_BotRubberbandDistance(const player_t *player)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
if (player->botvars.foe)
|
||||
spacing /= 2;
|
||||
*/
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (i == portpriority)
|
||||
|
|
@ -649,6 +656,11 @@ static UINT32 K_BotRubberbandDistance(const player_t *player)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (player->botvars.foe && !players[i].botvars.foe)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// First check difficulty levels, then score, then settle it with port priority!
|
||||
if (player->botvars.difficulty < players[i].botvars.difficulty)
|
||||
{
|
||||
|
|
@ -694,6 +706,8 @@ fixed_t K_BotRubberband(const player_t *player)
|
|||
{
|
||||
UINT8 levelreduce = std::min<UINT8>(3, player->botvars.difficulty/4); // How much to drop the "effective level" of bots that are consistently behind
|
||||
expreduce = Easing_Linear((K_EffectiveGradingFactor(player) - MINGRADINGFACTOR) * 2, levelreduce*FRACUNIT, 0);
|
||||
if (player->botvars.foe)
|
||||
expreduce /= 2;
|
||||
}
|
||||
|
||||
fixed_t difficultyEase = (((player->botvars.difficulty - 1) * FRACUNIT) - expreduce) / (MAXBOTDIFFICULTY - 1);
|
||||
|
|
@ -805,7 +819,13 @@ fixed_t K_BotRubberband(const player_t *player)
|
|||
scaled_dist = FixedDiv(scaled_dist, mapobjectscale);
|
||||
}
|
||||
|
||||
constexpr UINT32 END_DIST = 2048 * 14;
|
||||
UINT32 END_DIST = 2048 * 14;
|
||||
|
||||
if (K_EffectiveGradingFactor(player) <= FRACUNIT)
|
||||
{
|
||||
END_DIST = Easing_Linear((K_EffectiveGradingFactor(player) - MINGRADINGFACTOR) * 2, END_DIST * 2, END_DIST);
|
||||
}
|
||||
|
||||
if (scaled_dist < END_DIST)
|
||||
{
|
||||
// At the end of tracks, start slowing down.
|
||||
|
|
@ -823,7 +843,7 @@ fixed_t K_BotRubberband(const player_t *player)
|
|||
fixed_t K_UpdateRubberband(player_t *player)
|
||||
{
|
||||
fixed_t dest = K_BotRubberband(player);
|
||||
|
||||
|
||||
fixed_t deflect = player->botvars.recentDeflection;
|
||||
if (deflect > BOTMAXDEFLECTION)
|
||||
deflect = BOTMAXDEFLECTION;
|
||||
|
|
@ -2146,7 +2166,7 @@ void K_UpdateBotGameplayVars(player_t *player)
|
|||
UINT32 smo = BOTANGLESAMPLES - 1;
|
||||
|
||||
player->botvars.recentDeflection = (smo * player->botvars.recentDeflection / BOTANGLESAMPLES) + (dangle / BOTANGLESAMPLES);
|
||||
|
||||
|
||||
player->botvars.lastAngle = mangle;
|
||||
|
||||
const botcontroller_t *botController = K_GetBotController(player->mo);
|
||||
|
|
|
|||
|
|
@ -299,7 +299,7 @@ static boolean K_RivalBotAggression(const player_t *bot, const player_t *target)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (bot->botvars.rival == false && !cv_levelskull.value)
|
||||
if (!(bot->botvars.rival || bot->botvars.foe) && !cv_levelskull.value)
|
||||
{
|
||||
// Not the rival, we aren't self-aware.
|
||||
return false;
|
||||
|
|
@ -1863,7 +1863,7 @@ static void K_UpdateBotGameplayVarsItemUsageMash(player_t *player)
|
|||
else
|
||||
{
|
||||
botItemPriority_e currentPriority = K_GetBotItemPriority(
|
||||
static_cast<kartitems_t>( player->itemRoulette.itemList[ player->itemRoulette.index ] )
|
||||
static_cast<kartitems_t>( player->itemRoulette.itemList.items[ player->itemRoulette.index ] )
|
||||
);
|
||||
|
||||
if (player->botvars.roulettePriority == currentPriority)
|
||||
|
|
@ -1877,7 +1877,7 @@ static void K_UpdateBotGameplayVarsItemUsageMash(player_t *player)
|
|||
// reduce priority until we get to a valid one.
|
||||
player->botvars.rouletteTimeout++;
|
||||
|
||||
if (player->botvars.rouletteTimeout > player->itemRoulette.itemListLen * player->itemRoulette.speed)
|
||||
if (player->botvars.rouletteTimeout > player->itemRoulette.itemList.len * player->itemRoulette.speed)
|
||||
{
|
||||
player->botvars.roulettePriority--;
|
||||
player->botvars.rouletteTimeout = 0;
|
||||
|
|
@ -1995,9 +1995,9 @@ void K_BotPickItemPriority(player_t *player)
|
|||
player->botvars.rouletteTimeout = 0;
|
||||
|
||||
// Check for items that are extremely high priority.
|
||||
for (i = 0; i < player->itemRoulette.itemListLen; i++)
|
||||
for (i = 0; i < player->itemRoulette.itemList.len; i++)
|
||||
{
|
||||
botItemPriority_e priority = K_GetBotItemPriority( static_cast<kartitems_t>( player->itemRoulette.itemList[i] ) );
|
||||
botItemPriority_e priority = K_GetBotItemPriority( static_cast<kartitems_t>( player->itemRoulette.itemList.items[i] ) );
|
||||
|
||||
if (priority < BOT_ITEM_PR__OVERRIDES)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -98,6 +98,113 @@ static UINT8 K_GetOffsetStartingDifficulty(const UINT8 startingdifficulty, UINT8
|
|||
return startingdifficulty - offset;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
static INT16 K_RivalScore(player_t *bot)
|
||||
|
||||
Creates a "rival score" for a bot, used to determine which bot is the
|
||||
most deserving of the rival status.
|
||||
|
||||
Input Arguments:-
|
||||
bot - Player to check.
|
||||
|
||||
Return:-
|
||||
"Rival score" value.
|
||||
--------------------------------------------------*/
|
||||
static INT16 K_RivalScore(player_t *bot)
|
||||
{
|
||||
const UINT16 difficulty = bot->botvars.difficulty;
|
||||
const UINT16 score = bot->score;
|
||||
SINT8 roundnum = 1, roundsleft = 1;
|
||||
UINT16 lowestscore = UINT16_MAX;
|
||||
UINT8 lowestdifficulty = MAXBOTDIFFICULTY;
|
||||
UINT8 i;
|
||||
|
||||
if (grandprixinfo.cup != NULL && roundqueue.size > 0)
|
||||
{
|
||||
roundnum = roundqueue.roundnum;
|
||||
roundsleft = grandprixinfo.cup->numlevels - roundnum;
|
||||
}
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (!playeringame[i] || players[i].spectator)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (players[i].score < lowestscore)
|
||||
{
|
||||
lowestscore = players[i].score;
|
||||
}
|
||||
|
||||
if (players[i].bot == true && players[i].botvars.difficulty < lowestdifficulty)
|
||||
{
|
||||
lowestdifficulty = players[i].botvars.difficulty;
|
||||
}
|
||||
}
|
||||
|
||||
// In the early game, difficulty is more important.
|
||||
// This will try to influence the higher difficulty bots to get rival more often & get even more points.
|
||||
// However, when we're running low on matches left, we need to focus more on raw score!
|
||||
|
||||
return ((difficulty - lowestdifficulty) * roundsleft) + ((score - lowestscore) * roundnum);
|
||||
}
|
||||
|
||||
static boolean CompareRivals(player_t *a, player_t *b)
|
||||
{
|
||||
if (a == NULL)
|
||||
return false;
|
||||
if (b == NULL)
|
||||
return true;
|
||||
|
||||
if (K_RivalScore(a) != K_RivalScore(b))
|
||||
{
|
||||
// Rival Score is HIGH when bots are strong. Sort them first!
|
||||
return (K_RivalScore(a) > K_RivalScore(b));
|
||||
}
|
||||
|
||||
// Fuck it
|
||||
return a > b;
|
||||
}
|
||||
|
||||
void K_AssignFoes(void)
|
||||
{
|
||||
std::vector<player_t *> bots;
|
||||
boolean addedplayer = false;
|
||||
|
||||
for (UINT8 i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (playeringame[i] == false)
|
||||
continue;
|
||||
|
||||
player_t *player = &players[i];
|
||||
|
||||
if (!player->spectator && player->bot)
|
||||
{
|
||||
addedplayer = true;
|
||||
bots.push_back(player);
|
||||
player->botvars.foe = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Why the fuck is this blowing up sometimes
|
||||
if (!addedplayer)
|
||||
return;
|
||||
|
||||
std::stable_sort(bots.begin(), bots.end(), CompareRivals);
|
||||
|
||||
UINT8 i = 0;
|
||||
for (auto &bot : bots)
|
||||
{
|
||||
if (bot != NULL)
|
||||
bot->botvars.foe = true;
|
||||
|
||||
i++;
|
||||
if (i > 2)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
void K_InitGrandPrixBots(void)
|
||||
|
||||
|
|
@ -254,6 +361,8 @@ void K_InitGrandPrixBots(void)
|
|||
{
|
||||
break;
|
||||
}
|
||||
if (i <= 2)
|
||||
players[newplayernum-1].botvars.foe = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -289,64 +398,13 @@ void K_LoadGrandPrixSaveGame(void)
|
|||
K_SetBot(i, savedata.bots[i].skin, savedata.bots[i].difficulty, BOT_STYLE_NORMAL);
|
||||
|
||||
players[i].botvars.rival = savedata.bots[i].rival;
|
||||
players[i].botvars.foe = savedata.bots[i].foe;
|
||||
players[i].score = savedata.bots[i].score;
|
||||
|
||||
players[i].spectator = K_BotDefaultSpectator();
|
||||
}
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
static INT16 K_RivalScore(player_t *bot)
|
||||
|
||||
Creates a "rival score" for a bot, used to determine which bot is the
|
||||
most deserving of the rival status.
|
||||
|
||||
Input Arguments:-
|
||||
bot - Player to check.
|
||||
|
||||
Return:-
|
||||
"Rival score" value.
|
||||
--------------------------------------------------*/
|
||||
static INT16 K_RivalScore(player_t *bot)
|
||||
{
|
||||
const UINT16 difficulty = bot->botvars.difficulty;
|
||||
const UINT16 score = bot->score;
|
||||
SINT8 roundnum = 1, roundsleft = 1;
|
||||
UINT16 lowestscore = UINT16_MAX;
|
||||
UINT8 lowestdifficulty = MAXBOTDIFFICULTY;
|
||||
UINT8 i;
|
||||
|
||||
if (grandprixinfo.cup != NULL && roundqueue.size > 0)
|
||||
{
|
||||
roundnum = roundqueue.roundnum;
|
||||
roundsleft = grandprixinfo.cup->numlevels - roundnum;
|
||||
}
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (!playeringame[i] || players[i].spectator)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (players[i].score < lowestscore)
|
||||
{
|
||||
lowestscore = players[i].score;
|
||||
}
|
||||
|
||||
if (players[i].bot == true && players[i].botvars.difficulty < lowestdifficulty)
|
||||
{
|
||||
lowestdifficulty = players[i].botvars.difficulty;
|
||||
}
|
||||
}
|
||||
|
||||
// In the early game, difficulty is more important.
|
||||
// This will try to influence the higher difficulty bots to get rival more often & get even more points.
|
||||
// However, when we're running low on matches left, we need to focus more on raw score!
|
||||
|
||||
return ((difficulty - lowestdifficulty) * roundsleft) + ((score - lowestscore) * roundnum);
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
void K_UpdateGrandPrixBots(void)
|
||||
|
||||
|
|
@ -379,6 +437,8 @@ void K_UpdateGrandPrixBots(void)
|
|||
return;
|
||||
}
|
||||
|
||||
K_AssignFoes();
|
||||
|
||||
// Find the rival.
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
|
|
@ -639,7 +699,7 @@ void K_IncreaseBotDifficulty(player_t *bot)
|
|||
// RELAXED MODE:
|
||||
// Continues don't drop bot difficulty, because we always advance.
|
||||
// Bots will still level up from standard advancement; we need a
|
||||
// much steeper rank nudge to keep difficulty at the right level.
|
||||
// much steeper rank nudge to keep difficulty at the right level.
|
||||
if (grandprixinfo.gamespeed == KARTSPEED_EASY)
|
||||
{
|
||||
switch(averageRank)
|
||||
|
|
@ -689,8 +749,8 @@ static boolean CompareJoiners(player_t *a, player_t *b)
|
|||
return (a->spectatewait < b->spectatewait);
|
||||
}
|
||||
|
||||
// They are equals, so just randomize
|
||||
return (P_Random(PR_BOTS) & 1);
|
||||
// Fuck it
|
||||
return a > b;
|
||||
}
|
||||
|
||||
static boolean CompareReplacements(player_t *a, player_t *b)
|
||||
|
|
@ -707,8 +767,8 @@ static boolean CompareReplacements(player_t *a, player_t *b)
|
|||
return (a->position < b->position);
|
||||
}
|
||||
|
||||
// They are equals, so just randomize
|
||||
return (P_Random(PR_BOTS) & 1);
|
||||
// Fuck it
|
||||
return a > b;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -200,6 +200,8 @@ boolean K_CanChangeRules(boolean allowdemos);
|
|||
|
||||
boolean K_BotDefaultSpectator(void);
|
||||
|
||||
void K_AssignFoes(void);
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
|
|
|
|||
|
|
@ -1630,15 +1630,15 @@ static void K_drawKartItem(void)
|
|||
boolean flashOnOne = false;
|
||||
boolean flashOnTwo = false;
|
||||
|
||||
if (stplyr->itemRoulette.itemListLen > 0)
|
||||
if (stplyr->itemRoulette.itemList.len > 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 size_t index = (stplyr->itemRoulette.itemList.len + (stplyr->itemRoulette.index + indexOfs)) % stplyr->itemRoulette.itemList.len;
|
||||
|
||||
const SINT8 result = stplyr->itemRoulette.itemList[index];
|
||||
const SINT8 result = stplyr->itemRoulette.itemList.items[index];
|
||||
const SINT8 item = K_ItemResultToType(result);
|
||||
const boolean usingDebugItemAmount = cv_kartdebugitem.value != KITEM_NONE && cv_kartdebugitem.value == item && cv_kartdebugamount.value > 1;
|
||||
const UINT8 amt = usingDebugItemAmount ? cv_kartdebugamount.value : K_ItemResultToAmount(result, &stplyr->itemRoulette);
|
||||
|
|
@ -1825,7 +1825,7 @@ static void K_drawKartItem(void)
|
|||
}
|
||||
|
||||
UINT8 *boxmap = NULL;
|
||||
if (stplyr->itemRoulette.active && (stplyr->itemRoulette.speed - stplyr->itemRoulette.tics < 3) && stplyr->itemRoulette.index == 0 && stplyr->itemRoulette.itemListLen > 1)
|
||||
if (stplyr->itemRoulette.active && (stplyr->itemRoulette.speed - stplyr->itemRoulette.tics < 3) && stplyr->itemRoulette.index == 0 && stplyr->itemRoulette.itemList.len > 1)
|
||||
{
|
||||
boxmap = R_GetTranslationColormap(TC_ALLWHITE, SKINCOLOR_WHITE, GTC_CACHE);
|
||||
}
|
||||
|
|
@ -2144,15 +2144,15 @@ static void K_drawKartSlotMachine(void)
|
|||
vector2_t rouletteCrop = {10, 10};
|
||||
INT32 i;
|
||||
|
||||
if (stplyr->itemRoulette.itemListLen > 0)
|
||||
if (stplyr->itemRoulette.itemList.len > 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 size_t index = (stplyr->itemRoulette.itemList.len + (stplyr->itemRoulette.index + indexOfs)) % stplyr->itemRoulette.itemList.len;
|
||||
|
||||
const SINT8 result = stplyr->itemRoulette.itemList[index];
|
||||
const SINT8 result = stplyr->itemRoulette.itemList.items[index];
|
||||
|
||||
localpatch[i] = K_GetCachedSlotMachinePatch(result, offset);
|
||||
}
|
||||
|
|
@ -5346,8 +5346,10 @@ static void K_DrawCPUTagForPlayer(fixed_t x, fixed_t y, player_t *p, UINT32 flag
|
|||
K_DrawNameTagItemSpy(barx, bary, p, flags);
|
||||
}
|
||||
|
||||
UINT8 *foecol = R_GetTranslationColormap(TC_RAINBOW, static_cast<skincolornum_t>(SKINCOLOR_RED), GTC_CACHE);
|
||||
|
||||
UINT8 blink = ((leveltime / 7) & 1);
|
||||
V_DrawFixedPatch(x, y, FRACUNIT, flags, kp_cpu[blink], NULL);
|
||||
V_DrawFixedPatch(x, y, FRACUNIT, flags, kp_cpu[blink], (p->botvars.foe) ? foecol : NULL);
|
||||
}
|
||||
|
||||
static void K_DrawNameTagForPlayer(fixed_t x, fixed_t y, player_t *p, UINT32 flags)
|
||||
|
|
@ -7202,7 +7204,7 @@ static void K_drawDistributionDebugger(void)
|
|||
V_DrawRightAlignedThinString(320-(x >> FRACBITS), 100+58, V_SNAPTOTOP|V_SNAPTORIGHT, va("secondToFirst = %u", rouletteData.secondToFirst));
|
||||
|
||||
#ifndef ITEM_LIST_SIZE
|
||||
Z_Free(rouletteData.itemList);
|
||||
Z_Free(rouletteData.itemList.items);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
@ -7314,7 +7316,7 @@ static void K_DrawBotDebugger(void)
|
|||
|
||||
V_DrawSmallString(8, 14, 0, va("Difficulty: %d / %d", bot->botvars.difficulty, MAXBOTDIFFICULTY));
|
||||
V_DrawSmallString(8, 18, 0, va("Difficulty increase: %d", bot->botvars.diffincrease));
|
||||
V_DrawSmallString(8, 22, 0, va("Rival: %d", (UINT8)(bot->botvars.rival == true)));
|
||||
V_DrawSmallString(8, 22, 0, va("Rival / Foe: %d / %d", (UINT8)(bot->botvars.rival == true), (UINT8)(bot->botvars.foe == true)));
|
||||
V_DrawSmallString(8, 26, 0, va("Rubberbanding: %.02f", FIXED_TO_FLOAT(bot->botvars.rubberband) * 100.0f));
|
||||
|
||||
V_DrawSmallString(8, 32, 0, va("Item delay: %d", bot->botvars.itemdelay));
|
||||
|
|
|
|||
14
src/k_kart.c
14
src/k_kart.c
|
|
@ -145,6 +145,9 @@ boolean K_InRaceDuel(void)
|
|||
|
||||
fixed_t K_EffectiveGradingFactor(const player_t *player)
|
||||
{
|
||||
if (player == NULL)
|
||||
return FRACUNIT; // K_FillItemRouletteData can OSTENSIBLY call this with null player for "generic" use.
|
||||
|
||||
fixed_t min = (franticitems) ? MINFRANTICFACTOR : MINGRADINGFACTOR;
|
||||
if (grandprixinfo.gp && grandprixinfo.masterbots && !K_PlayerUsesBotMovement(player))
|
||||
return min;
|
||||
|
|
@ -1622,6 +1625,8 @@ static boolean K_TryDraft(player_t *player, mobj_t *dest, fixed_t minDist, fixed
|
|||
// Double speed for the rival!
|
||||
if (player->botvars.rival || cv_levelskull.value)
|
||||
player->draftpower += add;
|
||||
else if (player->botvars.foe)
|
||||
player->draftpower += add/2;
|
||||
else if (dest->player->bot) // Reduce bot gluts.
|
||||
player->draftpower -= 3*add/4;
|
||||
}
|
||||
|
|
@ -3558,6 +3563,10 @@ static fixed_t K_RingDurationBoost(const player_t *player)
|
|||
// x2.0 for Rival
|
||||
ret *= 2;
|
||||
}
|
||||
else if (player->botvars.foe)
|
||||
{
|
||||
ret = 3 * ret / 2;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
|
@ -3980,6 +3989,11 @@ fixed_t K_GetKartSpeed(const player_t *player, boolean doboostpower, boolean dor
|
|||
// +10% top speed for the rival
|
||||
finalspeed = FixedMul(finalspeed, 11*FRACUNIT/10);
|
||||
}
|
||||
else if (player->bot && player->botvars.foe)
|
||||
{
|
||||
// +5% for foes
|
||||
finalspeed = FixedMul(finalspeed, 21*FRACUNIT/20);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
250
src/k_roulette.c
250
src/k_roulette.c
|
|
@ -404,17 +404,9 @@ botItemPriority_e K_GetBotItemPriority(kartitems_t result)
|
|||
/*--------------------------------------------------
|
||||
static fixed_t K_ItemOddsScale(UINT8 playerCount)
|
||||
|
||||
A multiplier for odds and distances to scale
|
||||
them with the player count.
|
||||
|
||||
Input Arguments:-
|
||||
playerCount - Number of players in the game.
|
||||
|
||||
Return:-
|
||||
Fixed point number, to multiply odds or
|
||||
distances by.
|
||||
See header file for description.
|
||||
--------------------------------------------------*/
|
||||
static fixed_t K_ItemOddsScale(UINT8 playerCount)
|
||||
fixed_t K_ItemOddsScale(UINT8 playerCount)
|
||||
{
|
||||
const UINT8 basePlayer = 8; // The player count we design most of the game around.
|
||||
fixed_t playerScaling = 0;
|
||||
|
|
@ -467,7 +459,7 @@ static UINT32 K_UndoMapScaling(UINT32 distance)
|
|||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
static UINT32 K_ScaleItemDistance(UINT32 distance, UINT8 numPlayers)
|
||||
UINT32 K_ScaleItemDistance(UINT32 distance, UINT8 numPlayers)
|
||||
|
||||
Adjust item distance for lobby-size scaling
|
||||
as well as Frantic Items.
|
||||
|
|
@ -480,10 +472,8 @@ static UINT32 K_UndoMapScaling(UINT32 distance)
|
|||
Return:-
|
||||
New distance after scaling.
|
||||
--------------------------------------------------*/
|
||||
static UINT32 K_ScaleItemDistance(const player_t *player, UINT32 distance, UINT8 numPlayers)
|
||||
UINT32 K_ScaleItemDistance(INT32 distance, UINT8 numPlayers)
|
||||
{
|
||||
(void)player;
|
||||
|
||||
#if 0
|
||||
if (franticitems == true)
|
||||
{
|
||||
|
|
@ -498,24 +488,13 @@ static UINT32 K_ScaleItemDistance(const player_t *player, UINT32 distance, UINT8
|
|||
FRACUNIT + (K_ItemOddsScale(numPlayers) / 2)
|
||||
);
|
||||
|
||||
// Distance is reduced based on the player's gradingfactor
|
||||
// distance = FixedMul(distance, player->gradingfactor);
|
||||
|
||||
return distance;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
static UINT32 K_GetItemRouletteDistance(const player_t *player, UINT8 numPlayers)
|
||||
|
||||
Gets a player's distance used for the item
|
||||
roulette, including all scaling factors.
|
||||
|
||||
Input Arguments:-
|
||||
player - The player to get the distance of.
|
||||
numPlayers - Number of players in the game.
|
||||
|
||||
Return:-
|
||||
The player's finalized item distance.
|
||||
See header file for description.
|
||||
--------------------------------------------------*/
|
||||
UINT32 K_GetItemRouletteDistance(const player_t *player, UINT8 numPlayers)
|
||||
{
|
||||
|
|
@ -567,7 +546,7 @@ UINT32 K_GetItemRouletteDistance(const player_t *player, UINT8 numPlayers)
|
|||
}
|
||||
|
||||
pdis = K_UndoMapScaling(pdis);
|
||||
pdis = K_ScaleItemDistance(player, pdis, numPlayers);
|
||||
pdis = K_ScaleItemDistance(pdis, numPlayers);
|
||||
|
||||
if (player->bot && (player->botvars.rival || cv_levelskull.value))
|
||||
{
|
||||
|
|
@ -579,19 +558,11 @@ UINT32 K_GetItemRouletteDistance(const player_t *player, UINT8 numPlayers)
|
|||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
static boolean K_DenyShieldOdds(kartitems_t item)
|
||||
boolean K_DenyShieldOdds(kartitems_t item)
|
||||
|
||||
Checks if this type of shield already exists in
|
||||
another player's inventory.
|
||||
|
||||
Input Arguments:-
|
||||
item - The item type of the shield.
|
||||
|
||||
Return:-
|
||||
Whether this item is a shield and may not be awarded
|
||||
at this time.
|
||||
See header file for description.
|
||||
--------------------------------------------------*/
|
||||
static boolean K_DenyShieldOdds(kartitems_t item)
|
||||
boolean K_DenyShieldOdds(kartitems_t item)
|
||||
{
|
||||
const INT32 shieldType = K_GetShieldFromItem(item);
|
||||
size_t i;
|
||||
|
|
@ -704,18 +675,9 @@ INT32 K_KartGetBattleOdds(const player_t *player, UINT8 pos, kartitems_t item)
|
|||
/*--------------------------------------------------
|
||||
static boolean K_ForcedSPB(const player_t *player, itemroulette_t *const roulette)
|
||||
|
||||
Determines special conditions where we want
|
||||
to forcefully give the player an SPB.
|
||||
|
||||
Input Arguments:-
|
||||
player - The player the roulette is for.
|
||||
roulette - The item roulette data.
|
||||
|
||||
Return:-
|
||||
true if we want to give the player a forced SPB,
|
||||
otherwise false.
|
||||
See header file for description.
|
||||
--------------------------------------------------*/
|
||||
static boolean K_ForcedSPB(const player_t *player, itemroulette_t *const roulette)
|
||||
boolean K_ForcedSPB(const player_t *player, itemroulette_t *const roulette)
|
||||
{
|
||||
if (K_ItemEnabled(KITEM_SPB) == false)
|
||||
{
|
||||
|
|
@ -778,23 +740,23 @@ static void K_InitRoulette(itemroulette_t *const roulette)
|
|||
size_t i;
|
||||
|
||||
#ifndef ITEM_LIST_SIZE
|
||||
if (roulette->itemList == NULL)
|
||||
if (roulette->itemList.items == NULL)
|
||||
{
|
||||
roulette->itemListCap = 8;
|
||||
roulette->itemList = Z_Calloc(
|
||||
sizeof(SINT8) * roulette->itemListCap,
|
||||
roulette->itemList.cap = 32;
|
||||
roulette->itemList.items = Z_Calloc(
|
||||
sizeof(SINT8) * roulette->itemList.cap,
|
||||
PU_STATIC,
|
||||
&roulette->itemList
|
||||
NULL
|
||||
);
|
||||
|
||||
if (roulette->itemList == NULL)
|
||||
if (roulette->itemList.items == NULL)
|
||||
{
|
||||
I_Error("Not enough memory for item roulette list\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
roulette->itemListLen = 0;
|
||||
roulette->itemList.len = 0;
|
||||
roulette->index = 0;
|
||||
|
||||
roulette->baseDist = roulette->dist = 0;
|
||||
|
|
@ -860,74 +822,65 @@ static void K_InitRoulette(itemroulette_t *const roulette)
|
|||
&& roulette->secondDist > roulette->firstDist)
|
||||
{
|
||||
roulette->secondToFirst = roulette->secondDist - roulette->firstDist;
|
||||
roulette->secondToFirst = K_ScaleItemDistance(&players[i], roulette->secondToFirst, 16 - roulette->playing); // Reversed scaling
|
||||
roulette->secondToFirst = K_ScaleItemDistance(roulette->secondToFirst, 16 - roulette->playing); // Reversed scaling
|
||||
}
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
static void K_PushToRouletteItemList(itemroulette_t *const roulette, INT32 item)
|
||||
void K_PushToRouletteItemList(itemroulette_t *const roulette, INT32 item)
|
||||
|
||||
Pushes a new item to the end of the item
|
||||
roulette's item list. Also accepts slot machine
|
||||
values instead of items.
|
||||
|
||||
Input Arguments:-
|
||||
roulette - The item roulette data to modify.
|
||||
item - The item / slot machine index to push to the list.
|
||||
|
||||
Return:-
|
||||
N/A
|
||||
See header file for description.
|
||||
--------------------------------------------------*/
|
||||
static void K_PushToRouletteItemList(itemroulette_t *const roulette, INT32 item)
|
||||
void K_PushToRouletteItemList(itemroulette_t *const roulette, INT32 item)
|
||||
{
|
||||
#ifdef ITEM_LIST_SIZE
|
||||
if (roulette->itemListLen >= ITEM_LIST_SIZE)
|
||||
if (roulette->itemList.len >= ITEM_LIST_SIZE)
|
||||
{
|
||||
I_Error("Out of space for item reel! Go and make ITEM_LIST_SIZE bigger I guess?\n");
|
||||
return;
|
||||
}
|
||||
#else
|
||||
I_Assert(roulette->itemList != NULL);
|
||||
I_Assert(roulette->itemList.items != NULL);
|
||||
|
||||
if (roulette->itemListLen >= roulette->itemListCap)
|
||||
if (!roulette->ringbox && item >= NUMKARTRESULTS)
|
||||
{
|
||||
roulette->itemListCap *= 2;
|
||||
roulette->itemList = Z_Realloc(
|
||||
roulette->itemList,
|
||||
sizeof(SINT8) * roulette->itemListCap,
|
||||
CONS_Alert(CONS_WARNING, M_GetText("Item Roulette rejected an out-of-range item.\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (roulette->ringbox && item >= KSM__MAX)
|
||||
{
|
||||
CONS_Alert(CONS_WARNING, M_GetText("Casino Roulette rejected an out-of-range item.\n"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (roulette->itemList.len >= roulette->itemList.cap)
|
||||
{
|
||||
roulette->itemList.cap *= 2;
|
||||
roulette->itemList.items = Z_Realloc(
|
||||
roulette->itemList.items,
|
||||
sizeof(SINT8) * roulette->itemList.cap,
|
||||
PU_STATIC,
|
||||
&roulette->itemList
|
||||
NULL
|
||||
);
|
||||
|
||||
if (roulette->itemList == NULL)
|
||||
if (roulette->itemList.items == NULL)
|
||||
{
|
||||
I_Error("Not enough memory for item roulette list\n");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
roulette->itemList[ roulette->itemListLen ] = item;
|
||||
roulette->itemListLen++;
|
||||
roulette->itemList.items[ roulette->itemList.len ] = item;
|
||||
roulette->itemList.len++;
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
static void K_AddItemToReel(const player_t *player, itemroulette_t *const roulette, kartitems_t item)
|
||||
void K_AddItemToReel(const player_t *player, itemroulette_t *const roulette, kartitems_t item)
|
||||
|
||||
Adds an item to a player's item reel. Unlike
|
||||
pushing directly with K_PushToRouletteItemList,
|
||||
this function handles special behaviors (like
|
||||
padding with extra Super Rings).
|
||||
|
||||
Input Arguments:-
|
||||
player - The player to add to the item roulette.
|
||||
This is valid to be NULL.
|
||||
roulette - The player's item roulette data.
|
||||
item - The item to push to the list.
|
||||
|
||||
Return:-
|
||||
N/A
|
||||
See header file for description.
|
||||
--------------------------------------------------*/
|
||||
static void K_AddItemToReel(const player_t *player, itemroulette_t *const roulette, kartitems_t item)
|
||||
void K_AddItemToReel(const player_t *player, itemroulette_t *const roulette, kartitems_t item)
|
||||
{
|
||||
if (player && K_PlayerUsesBotMovement(player) && !K_BotUnderstandsItem(item))
|
||||
return;
|
||||
|
|
@ -951,19 +904,11 @@ static void K_AddItemToReel(const player_t *player, itemroulette_t *const roulet
|
|||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
static void K_CalculateRouletteSpeed(itemroulette_t *const roulette)
|
||||
void K_CalculateRouletteSpeed(itemroulette_t *const roulette)
|
||||
|
||||
Determines the speed for the item roulette,
|
||||
adjusted for progress in the race and front
|
||||
running.
|
||||
|
||||
Input Arguments:-
|
||||
roulette - The item roulette data to modify.
|
||||
|
||||
Return:-
|
||||
N/A
|
||||
See header file for description.
|
||||
--------------------------------------------------*/
|
||||
static void K_CalculateRouletteSpeed(itemroulette_t *const roulette)
|
||||
void K_CalculateRouletteSpeed(itemroulette_t *const roulette)
|
||||
{
|
||||
fixed_t frontRun = 0;
|
||||
fixed_t progress = 0;
|
||||
|
|
@ -1244,7 +1189,7 @@ static boolean K_TimingPermitsItem(kartitems_t item, const itemroulette_t *roule
|
|||
|
||||
static void K_FixEmptyRoulette(const player_t *player, itemroulette_t *const roulette)
|
||||
{
|
||||
if (roulette->itemListLen > 0)
|
||||
if (roulette->itemList.len > 0)
|
||||
return;
|
||||
|
||||
if (K_PlayerUsesBotMovement(player)) // Bots can't use certain items. Give them _something_.
|
||||
|
|
@ -1254,21 +1199,12 @@ static void K_FixEmptyRoulette(const player_t *player, itemroulette_t *const rou
|
|||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulette, boolean ringbox, boolean dryrun)
|
||||
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, boolean ringbox, boolean dryrun)
|
||||
void K_FillItemRoulette(player_t *const player, itemroulette_t *const roulette, boolean ringbox)
|
||||
{
|
||||
UINT32 spawnChance[NUMKARTRESULTS] = {0};
|
||||
UINT32 totalSpawnChance = 0;
|
||||
size_t rngRoll = 0;
|
||||
|
||||
UINT8 numItems = 0;
|
||||
kartitems_t singleItem = KITEM_SAD;
|
||||
|
||||
size_t i, j;
|
||||
|
||||
K_InitRoulette(roulette);
|
||||
|
||||
if (player != NULL)
|
||||
|
|
@ -1281,6 +1217,49 @@ void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulet
|
|||
K_CalculateRouletteSpeed(roulette);
|
||||
}
|
||||
|
||||
// Lua may want to intercept reelbuilder entirely.
|
||||
LUA_HookPreFillItemRoulette(player, roulette, ringbox);
|
||||
|
||||
// If prehook did something, no need to continue.
|
||||
if (roulette->itemList.len != 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
K_FillItemRouletteData(player, roulette, ringbox, false);
|
||||
|
||||
// Lua can modify the final result.
|
||||
LUA_HookFillItemRoulette(player, roulette, ringbox);
|
||||
|
||||
// If somehow there's no items, add sad.
|
||||
if (roulette->itemList.len == 0) {
|
||||
if (roulette->ringbox)
|
||||
K_PushToRouletteItemList(roulette, KSM_BAR);
|
||||
else
|
||||
K_AddItemToReel(player, roulette, KITEM_SAD);
|
||||
}
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulette, boolean ringbox, boolean dryrun)
|
||||
|
||||
See header file for description.
|
||||
--------------------------------------------------*/
|
||||
void K_FillItemRouletteData(player_t *player, itemroulette_t *const roulette, boolean ringbox, boolean dryrun)
|
||||
{
|
||||
UINT32 spawnChance[NUMKARTRESULTS] = {0};
|
||||
UINT32 totalSpawnChance = 0;
|
||||
size_t rngRoll = 0;
|
||||
|
||||
UINT8 numItems = 0;
|
||||
kartitems_t singleItem = KITEM_SAD;
|
||||
|
||||
size_t i, j;
|
||||
|
||||
if (roulette->itemList.items == NULL)
|
||||
{
|
||||
K_InitRoulette(roulette);
|
||||
}
|
||||
|
||||
if (ringbox == true)
|
||||
{
|
||||
// If this is being invoked by a Ring Box, it should literally never produce items.
|
||||
|
|
@ -1313,6 +1292,7 @@ void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulet
|
|||
{
|
||||
K_PushToRouletteItemList(roulette, K_KartItemReelSpecialEnd[i]);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
@ -1792,10 +1772,10 @@ void K_StartItemRoulette(player_t *const player, boolean ringbox)
|
|||
itemroulette_t *const roulette = &player->itemRoulette;
|
||||
size_t i;
|
||||
|
||||
K_FillItemRouletteData(player, roulette, ringbox, false);
|
||||
K_FillItemRoulette(player, roulette, ringbox);
|
||||
|
||||
if (roulette->autoroulette)
|
||||
roulette->index = P_RandomRange(PR_AUTOROULETTE, 0, roulette->itemListLen - 1);
|
||||
roulette->index = P_RandomRange(PR_AUTOROULETTE, 0, roulette->itemList.len - 1);
|
||||
|
||||
if (K_PlayerUsesBotMovement(player) == true)
|
||||
{
|
||||
|
|
@ -1804,9 +1784,9 @@ void K_StartItemRoulette(player_t *const player, boolean ringbox)
|
|||
|
||||
// Prevent further duplicates of items that
|
||||
// are intended to only have one out at a time.
|
||||
for (i = 0; i < roulette->itemListLen; i++)
|
||||
for (i = 0; i < roulette->itemList.len; i++)
|
||||
{
|
||||
kartitems_t item = roulette->itemList[i];
|
||||
kartitems_t item = roulette->itemList.items[i];
|
||||
if (K_ItemSingularity(item) == true)
|
||||
{
|
||||
K_SetItemCooldown(item, TICRATE<<4);
|
||||
|
|
@ -1880,19 +1860,11 @@ fixed_t K_GetSlotOffset(itemroulette_t *const roulette, fixed_t renderDelta, UIN
|
|||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
static void K_KartGetItemResult(player_t *const player, kartitems_t getitem)
|
||||
void K_KartGetItemResult(player_t *const player, kartitems_t getitem)
|
||||
|
||||
Initializes a player's item to what was
|
||||
received from the roulette.
|
||||
|
||||
Input Arguments:-
|
||||
player - The player receiving the item.
|
||||
getitem - The item to give to the player.
|
||||
|
||||
Return:-
|
||||
N/A
|
||||
See header file for description.
|
||||
--------------------------------------------------*/
|
||||
static void K_KartGetItemResult(player_t *const player, kartitems_t getitem)
|
||||
void K_KartGetItemResult(player_t *const player, kartitems_t getitem)
|
||||
{
|
||||
if (K_ItemSingularity(getitem) == true)
|
||||
{
|
||||
|
|
@ -1935,9 +1907,9 @@ void K_KartItemRoulette(player_t *const player, ticcmd_t *const cmd)
|
|||
return;
|
||||
}
|
||||
|
||||
if (roulette->itemListLen == 0
|
||||
if (roulette->itemList.len == 0
|
||||
#ifndef ITEM_LIST_SIZE
|
||||
|| roulette->itemList == NULL
|
||||
|| roulette->itemList.items == NULL
|
||||
#endif
|
||||
)
|
||||
{
|
||||
|
|
@ -2000,7 +1972,7 @@ void K_KartItemRoulette(player_t *const player, ticcmd_t *const cmd)
|
|||
if (fudgedDelay > gap) // Did the roulette tick over in-flight?
|
||||
{
|
||||
fudgedDelay = fudgedDelay - gap; // We're compensating for this gap's worth of delay, so cut it down.
|
||||
roulette->index = roulette->index == 0 ? roulette->itemListLen - 1 : roulette->index - 1; // Roll the roulette index back...
|
||||
roulette->index = roulette->index == 0 ? roulette->itemList.len - 1 : roulette->index - 1; // Roll the roulette index back...
|
||||
roulette->tics = 0; // And just in case our delay is SO high that a fast roulette needs to roll back again...
|
||||
}
|
||||
else
|
||||
|
|
@ -2012,7 +1984,7 @@ 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;
|
||||
|
||||
INT32 finalItem = roulette->itemList[ roulette->index ];
|
||||
INT32 finalItem = roulette->itemList.items[ roulette->index ];
|
||||
|
||||
if (roulette->ringbox == true)
|
||||
{
|
||||
|
|
@ -2057,7 +2029,7 @@ void K_KartItemRoulette(player_t *const player, ticcmd_t *const cmd)
|
|||
|
||||
if (roulette->tics == 0)
|
||||
{
|
||||
roulette->index = (roulette->index + 1) % roulette->itemListLen;
|
||||
roulette->index = (roulette->index + 1) % roulette->itemList.len;
|
||||
roulette->tics = roulette->speed;
|
||||
|
||||
// This makes the roulette produce the random noises.
|
||||
|
|
@ -2070,7 +2042,7 @@ void K_KartItemRoulette(player_t *const player, ticcmd_t *const cmd)
|
|||
else
|
||||
S_StartSound(NULL, sfx_itrol1 + roulette->sound);
|
||||
|
||||
if (roulette->index == 0 && roulette->itemListLen > 1)
|
||||
if (roulette->index == 0 && roulette->itemList.len > 1)
|
||||
{
|
||||
S_StartSound(NULL, sfx_kc50);
|
||||
S_StartSound(NULL, sfx_kc50);
|
||||
|
|
|
|||
155
src/k_roulette.h
155
src/k_roulette.h
|
|
@ -76,6 +76,90 @@ boolean K_ItemSingularity(kartitems_t item);
|
|||
|
||||
botItemPriority_e K_GetBotItemPriority(kartitems_t result);
|
||||
|
||||
/*--------------------------------------------------
|
||||
fixed_t K_ItemOddsScale(UINT8 playerCount)
|
||||
|
||||
A multiplier for odds and distances to scale
|
||||
them with the player count.
|
||||
|
||||
Input Arguments:-
|
||||
playerCount - Number of players in the game.
|
||||
|
||||
Return:-
|
||||
Fixed point number, to multiply odds or
|
||||
distances by.
|
||||
--------------------------------------------------*/
|
||||
|
||||
fixed_t K_ItemOddsScale(UINT8 playerCount);
|
||||
|
||||
/*--------------------------------------------------
|
||||
UINT32 K_ScaleItemDistance(UINT32 distance, UINT8 numPlayers)
|
||||
|
||||
Adjust item distance for lobby-size scaling
|
||||
as well as Frantic Items.
|
||||
|
||||
Input Arguments:-
|
||||
distance - Original distance.
|
||||
numPlayers - Number of players in the game.
|
||||
|
||||
Return:-
|
||||
New distance after scaling.
|
||||
--------------------------------------------------*/
|
||||
|
||||
UINT32 K_ScaleItemDistance(INT32 distance, UINT8 numPlayers);
|
||||
|
||||
/*--------------------------------------------------
|
||||
void K_PushToRouletteItemList(itemroulette_t *const roulette, INT32 item)
|
||||
|
||||
Pushes a new item to the end of the item
|
||||
roulette's item list. Also accepts slot machine
|
||||
values instead of items.
|
||||
|
||||
Input Arguments:-
|
||||
roulette - The item roulette data to modify.
|
||||
item - The item / slot machine index to push to the list.
|
||||
|
||||
Return:-
|
||||
N/A
|
||||
--------------------------------------------------*/
|
||||
|
||||
void K_PushToRouletteItemList(itemroulette_t *const roulette, INT32 item);
|
||||
|
||||
/*--------------------------------------------------
|
||||
void K_AddItemToReel(const player_t *player, itemroulette_t *const roulette, kartitems_t item)
|
||||
|
||||
Adds an item to a player's item reel. Unlike
|
||||
pushing directly with K_PushToRouletteItemList,
|
||||
this function handles special behaviors (like
|
||||
padding with extra Super Rings).
|
||||
|
||||
Input Arguments:-
|
||||
player - The player to add to the item roulette.
|
||||
This is valid to be NULL.
|
||||
roulette - The player's item roulette data.
|
||||
item - The item to push to the list.
|
||||
|
||||
Return:-
|
||||
N/A
|
||||
--------------------------------------------------*/
|
||||
|
||||
void K_AddItemToReel(const player_t *player, itemroulette_t *const roulette, kartitems_t item);
|
||||
|
||||
/*--------------------------------------------------
|
||||
void K_CalculateRouletteSpeed(itemroulette_t *const roulette)
|
||||
|
||||
Determines the speed for the item roulette,
|
||||
adjusted for progress in the race and front
|
||||
running.
|
||||
|
||||
Input Arguments:-
|
||||
roulette - The item roulette data to modify.
|
||||
|
||||
Return:-
|
||||
N/A
|
||||
--------------------------------------------------*/
|
||||
|
||||
void K_CalculateRouletteSpeed(itemroulette_t *const roulette);
|
||||
|
||||
/*--------------------------------------------------
|
||||
INT32 K_KartGetBattleOdds(const player_t *player, itemroulette_t *const roulette, UINT8 pos, kartitems_t item);
|
||||
|
|
@ -96,9 +180,27 @@ botItemPriority_e K_GetBotItemPriority(kartitems_t result);
|
|||
|
||||
INT32 K_KartGetBattleOdds(const player_t *player, UINT8 pos, kartitems_t item);
|
||||
|
||||
/*--------------------------------------------------
|
||||
void K_FillItemRoulette(player_t *player, itemroulette_t *const roulette, boolean ringbox);
|
||||
|
||||
Entry point for roulette builder.
|
||||
Includes Lua hooks.
|
||||
|
||||
Input Arguments:-
|
||||
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_FillItemRoulette(player_t *player, itemroulette_t *const roulette, boolean ringbox);
|
||||
|
||||
|
||||
/*--------------------------------------------------
|
||||
void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulette, boolean ringbox, boolean dryrun);
|
||||
void K_FillItemRouletteData(player_t *player, itemroulette_t *const roulette, boolean ringbox, boolean dryrun);
|
||||
|
||||
Fills out the item roulette struct when it is
|
||||
initially created. This function needs to be
|
||||
|
|
@ -116,7 +218,7 @@ INT32 K_KartGetBattleOdds(const player_t *player, UINT8 pos, kartitems_t item);
|
|||
N/A
|
||||
--------------------------------------------------*/
|
||||
|
||||
void K_FillItemRouletteData(const player_t *player, itemroulette_t *const roulette, boolean ringbox, boolean dryrun);
|
||||
void K_FillItemRouletteData(player_t *player, itemroulette_t *const roulette, boolean ringbox, boolean dryrun);
|
||||
|
||||
|
||||
/*--------------------------------------------------
|
||||
|
|
@ -224,8 +326,57 @@ fixed_t K_GetSlotOffset(itemroulette_t *const roulette, fixed_t renderDelta, UIN
|
|||
|
||||
void K_KartItemRoulette(player_t *const player, ticcmd_t *cmd);
|
||||
|
||||
void K_KartGetItemResult(player_t *const player, kartitems_t getitem);
|
||||
|
||||
/*--------------------------------------------------
|
||||
static UINT32 K_GetItemRouletteDistance(const player_t *player, UINT8 numPlayers)
|
||||
|
||||
Gets a player's distance used for the item
|
||||
roulette, including all scaling factors.
|
||||
|
||||
Input Arguments:-
|
||||
player - The player to get the distance of.
|
||||
numPlayers - Number of players in the game.
|
||||
|
||||
Return:-
|
||||
The player's finalized item distance.
|
||||
--------------------------------------------------*/
|
||||
|
||||
UINT32 K_GetItemRouletteDistance(const player_t *player, UINT8 numPlayers);
|
||||
|
||||
/*--------------------------------------------------
|
||||
boolean K_DenyShieldOdds(kartitems_t item)
|
||||
|
||||
Checks if this type of shield already exists in
|
||||
another player's inventory.
|
||||
|
||||
Input Arguments:-
|
||||
item - The item type of the shield.
|
||||
|
||||
Return:-
|
||||
Whether this item is a shield and may not be awarded
|
||||
at this time.
|
||||
--------------------------------------------------*/
|
||||
|
||||
boolean K_DenyShieldOdds(kartitems_t item);
|
||||
|
||||
/*--------------------------------------------------
|
||||
boolean K_ForcedSPB(const player_t *player, itemroulette_t *const roulette)
|
||||
|
||||
Determines special conditions where we want
|
||||
to forcefully give the player an SPB.
|
||||
|
||||
Input Arguments:-
|
||||
player - The player the roulette is for.
|
||||
roulette - The item roulette data.
|
||||
|
||||
Return:-
|
||||
true if we want to give the player a forced SPB,
|
||||
otherwise false.
|
||||
--------------------------------------------------*/
|
||||
|
||||
boolean K_ForcedSPB(const player_t *player, itemroulette_t *const roulette);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
#include "k_collide.h"
|
||||
#include "k_color.h"
|
||||
#include "k_hud.h"
|
||||
#include "k_grandprix.h"
|
||||
#include "d_netcmd.h" // IsPlayerAdmin
|
||||
#include "k_menu.h" // Player Setup menu color stuff
|
||||
#include "p_spec.h" // P_StartQuake
|
||||
|
|
@ -46,6 +47,7 @@
|
|||
#include "lua_hud.h" // hud_running errors
|
||||
#include "taglist.h" // P_FindSpecialLineFromTag
|
||||
#include "lua_hook.h" // hook_cmd_running errors
|
||||
#include "k_roulette.h"
|
||||
|
||||
#define NOHUD if (hud_running)\
|
||||
return luaL_error(L, "HUD rendering code should not call this function!");\
|
||||
|
|
@ -232,6 +234,8 @@ static const struct {
|
|||
{META_ACTIVATOR, "activator_t"},
|
||||
|
||||
{META_FOLLOWER, "follower_t"},
|
||||
{META_ITEMROULETTE, "itemroulette_t"},
|
||||
{META_ITEMROULETTE_ITEMLIST, "itemroulette_t.itemlist_t"},
|
||||
{NULL, NULL}
|
||||
};
|
||||
|
||||
|
|
@ -3920,6 +3924,68 @@ static int lib_kGetItemPatch(lua_State *L)
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int lib_kGetShieldFromItem(lua_State *L)
|
||||
{
|
||||
kartitems_t item = luaL_checkinteger(L, 1);
|
||||
//HUDSAFE
|
||||
lua_pushinteger(L, K_GetShieldFromItem(item));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int lib_kItemResultToType(lua_State *L)
|
||||
{
|
||||
kartitems_t item = luaL_checkinteger(L, 1);
|
||||
//HUDSAFE
|
||||
lua_pushinteger(L, K_ItemResultToType(item));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int lib_kItemResultToAmount(lua_State *L)
|
||||
{
|
||||
kartitems_t item = luaL_checkinteger(L, 1);
|
||||
//HUDSAFE
|
||||
lua_pushinteger(L, K_ItemResultToAmount(item, NULL));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int lib_kGetItemCooldown(lua_State *L)
|
||||
{
|
||||
kartitems_t item = luaL_checkinteger(L, 1);
|
||||
|
||||
NOHUD
|
||||
INLEVEL
|
||||
|
||||
lua_pushinteger(L, K_GetItemCooldown(item));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int lib_kSetItemCooldown(lua_State *L)
|
||||
{
|
||||
kartitems_t item = luaL_checkinteger(L, 1);
|
||||
tic_t time = luaL_checkinteger(L, 2);
|
||||
|
||||
NOHUD
|
||||
INLEVEL
|
||||
|
||||
K_SetItemCooldown(item, time);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lib_kTimeAttackRules(lua_State *L)
|
||||
{
|
||||
//HUDSAFE
|
||||
lua_pushboolean(L, K_TimeAttackRules());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int lib_kCapsuleTimeAttackRules(lua_State *L)
|
||||
{
|
||||
//HUDSAFE
|
||||
lua_pushboolean(L, K_CapsuleTimeAttackRules());
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
static int lib_kGetCollideAngle(lua_State *L)
|
||||
{
|
||||
mobj_t *t1 = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
|
||||
|
|
@ -4014,6 +4080,12 @@ static int lib_kDeclareWeakspot(lua_State *L)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int lib_kCheckBossIntro(lua_State *L)
|
||||
{
|
||||
lua_pushboolean(L, K_CheckBossIntro());
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int lib_vsGetArena(lua_State *L)
|
||||
{
|
||||
INT32 bossindex = luaL_checkinteger(L, 1);
|
||||
|
|
@ -4057,6 +4129,419 @@ static int lib_vsRandomPointOnArena(lua_State *L)
|
|||
return 2;
|
||||
}
|
||||
|
||||
static int lib_kItemEnabled(lua_State *L)
|
||||
{
|
||||
kartitems_t item = luaL_checkinteger(L, 1);
|
||||
lua_pushboolean(L, K_ItemEnabled(item));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int lib_kItemSingularity(lua_State *L)
|
||||
{
|
||||
kartitems_t item = luaL_checkinteger(L, 1);
|
||||
lua_pushboolean(L, K_ItemSingularity(item));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int lib_kGetBotItemPriority(lua_State *L)
|
||||
{
|
||||
kartitems_t item = luaL_checkinteger(L, 1);
|
||||
lua_pushinteger(L, K_GetBotItemPriority(item));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int lib_kAddItemToReel(lua_State *L)
|
||||
{
|
||||
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
|
||||
itemroulette_t *itemRoulette = NULL;
|
||||
|
||||
NOHUD
|
||||
INLEVEL
|
||||
if (!player)
|
||||
return LUA_ErrInvalid(L, "player_t");
|
||||
itemRoulette = &player->itemRoulette;
|
||||
|
||||
if (lua_isnumber(L, 2))
|
||||
{
|
||||
kartitems_t item = luaL_checkinteger(L, 2);
|
||||
K_AddItemToReel(player, itemRoulette, item);
|
||||
}
|
||||
else if (lua_istable(L, 2))
|
||||
{
|
||||
luaL_checktype(L, 2, LUA_TTABLE);
|
||||
size_t size = luaL_getn(L, 2);
|
||||
|
||||
for (size_t i = 1; i <= size; i++)
|
||||
{
|
||||
lua_rawgeti(L, 2, i);
|
||||
if (lua_isnumber(L, -1))
|
||||
{
|
||||
kartitems_t item = luaL_checkinteger(L, -1);
|
||||
K_AddItemToReel(player, itemRoulette, item);
|
||||
}
|
||||
else // Quit early, let the scripter know they messed up.
|
||||
{
|
||||
lua_pop(L, 1);
|
||||
return luaL_error(L, "Non-integer value in table in index %d.", i);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
else return LUA_ErrInvalid(L, "integer/table");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lib_kPushToRouletteItemList(lua_State *L)
|
||||
{
|
||||
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
|
||||
itemroulette_t *itemRoulette = NULL;
|
||||
NOHUD
|
||||
INLEVEL
|
||||
if (!player)
|
||||
return LUA_ErrInvalid(L, "player_t");
|
||||
itemRoulette = &player->itemRoulette;
|
||||
|
||||
if (lua_isnumber(L, 2))
|
||||
{
|
||||
kartitems_t item = luaL_checkinteger(L, 2);
|
||||
K_PushToRouletteItemList(itemRoulette, item);
|
||||
}
|
||||
else if (lua_istable(L, 2))
|
||||
{
|
||||
luaL_checktype(L, 2, LUA_TTABLE);
|
||||
size_t size = luaL_getn(L, 2);
|
||||
|
||||
for (size_t i = 1; i <= size; i++)
|
||||
{
|
||||
lua_rawgeti(L, 2, i);
|
||||
if (lua_isnumber(L, -1))
|
||||
{
|
||||
kartitems_t item = luaL_checkinteger(L, -1);
|
||||
K_PushToRouletteItemList(itemRoulette, item);
|
||||
}
|
||||
else // Quit early, let the scripter know they messed up.
|
||||
{
|
||||
lua_pop(L, 1);
|
||||
return luaL_error(L, "Non-integer value in table in index %d.", i);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
else return LUA_ErrInvalid(L, "integer/table");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lib_kStartItemRoulette(lua_State *L)
|
||||
{
|
||||
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
|
||||
boolean ringbox = lua_optboolean(L, 2);
|
||||
|
||||
NOHUD
|
||||
INLEVEL
|
||||
if (!player)
|
||||
return LUA_ErrInvalid(L, "player_t");
|
||||
|
||||
K_StartItemRoulette(player, ringbox);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lib_kStartEggmanRoulette(lua_State *L)
|
||||
{
|
||||
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
|
||||
|
||||
NOHUD
|
||||
INLEVEL
|
||||
if (!player)
|
||||
return LUA_ErrInvalid(L, "player_t");
|
||||
|
||||
K_StartEggmanRoulette(player);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lib_kStopRoulette(lua_State *L)
|
||||
{
|
||||
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
|
||||
|
||||
NOHUD
|
||||
INLEVEL
|
||||
if (!player)
|
||||
return LUA_ErrInvalid(L, "player_t");
|
||||
|
||||
K_StopRoulette(&player->itemRoulette);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lib_kKartGetItemResult(lua_State *L)
|
||||
{
|
||||
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
|
||||
kartitems_t item = luaL_checkinteger(L, 2);
|
||||
|
||||
NOHUD
|
||||
INLEVEL
|
||||
if (!player)
|
||||
return LUA_ErrInvalid(L, "player_t");
|
||||
|
||||
K_KartGetItemResult(player, item);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lib_kGetItemRouletteDistance(lua_State *L)
|
||||
{
|
||||
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
|
||||
UINT8 numPlayers = luaL_checkinteger(L, 2);
|
||||
INLEVEL
|
||||
|
||||
if (!player)
|
||||
return LUA_ErrInvalid(L, "player_t");
|
||||
|
||||
lua_pushinteger(L, K_GetItemRouletteDistance(player, numPlayers));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int lib_kFillItemRouletteData(lua_State *L)
|
||||
{
|
||||
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
|
||||
itemroulette_t *itemRoulette = NULL;
|
||||
|
||||
boolean ringbox = lua_optboolean(L, 2);
|
||||
|
||||
NOHUD
|
||||
INLEVEL
|
||||
if (!player)
|
||||
return LUA_ErrInvalid(L, "player_t");
|
||||
itemRoulette = &player->itemRoulette;
|
||||
|
||||
K_FillItemRouletteData(player, itemRoulette, ringbox, false);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lib_kForcedSPB(lua_State *L)
|
||||
{
|
||||
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
|
||||
INLEVEL
|
||||
|
||||
if (!player)
|
||||
return LUA_ErrInvalid(L, "player_t");
|
||||
|
||||
lua_pushboolean(L, K_ForcedSPB(player, &player->itemRoulette));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int lib_kDenyShieldOdds(lua_State *L)
|
||||
{
|
||||
kartitems_t item = luaL_checkinteger(L, 1);
|
||||
INLEVEL
|
||||
lua_pushboolean(L, K_DenyShieldOdds(item));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int lib_kGetRouletteOffset(lua_State *L)
|
||||
{
|
||||
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
|
||||
fixed_t renderDelta = luaL_optnumber(L, 2, FRACUNIT);
|
||||
UINT8 fudge = luaL_optnumber(L, 3, player ? player->cmd.latency : 0);
|
||||
INLEVEL
|
||||
|
||||
if (!player)
|
||||
return LUA_ErrInvalid(L, "player_t");
|
||||
|
||||
lua_pushfixed(L, K_GetRouletteOffset(&player->itemRoulette, renderDelta, fudge));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int lib_kGetSlotOffset(lua_State *L)
|
||||
{
|
||||
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
|
||||
fixed_t renderDelta = luaL_optnumber(L, 2, FRACUNIT);
|
||||
UINT8 fudge = luaL_optnumber(L, 3, player ? player->cmd.latency : 0);
|
||||
INLEVEL
|
||||
|
||||
if (!player)
|
||||
return LUA_ErrInvalid(L, "player_t");
|
||||
|
||||
lua_pushfixed(L, K_GetSlotOffset(&player->itemRoulette, renderDelta, fudge));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int lib_kCalculateRouletteSpeed(lua_State *L)
|
||||
{
|
||||
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
|
||||
|
||||
NOHUD
|
||||
INLEVEL
|
||||
if (!player)
|
||||
return LUA_ErrInvalid(L, "player_t");
|
||||
|
||||
K_CalculateRouletteSpeed(&player->itemRoulette);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lib_kScaleItemDistance(lua_State *L)
|
||||
{
|
||||
UINT32 distance = luaL_checkinteger(L, 1);
|
||||
UINT8 numPlayers = luaL_checkinteger(L, 2);
|
||||
lua_pushfixed(L, K_ScaleItemDistance(distance, numPlayers));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int lib_kItemOddsScale(lua_State *L)
|
||||
{
|
||||
UINT8 playerCount = luaL_checkinteger(L, 1);
|
||||
lua_pushfixed(L, K_ItemOddsScale(playerCount));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int lib_kWipeItemsInReel(lua_State *L)
|
||||
{
|
||||
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
|
||||
itemroulette_t *itemRoulette = NULL;
|
||||
|
||||
NOHUD
|
||||
INLEVEL
|
||||
if (!player)
|
||||
return LUA_ErrInvalid(L, "player_t");
|
||||
itemRoulette = &player->itemRoulette;
|
||||
|
||||
itemRoulette->itemList.len = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lib_kSetItemInReelByIndex(lua_State *L)
|
||||
{
|
||||
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
|
||||
itemroulette_t *itemRoulette = NULL;
|
||||
|
||||
size_t index = luaL_checkinteger(L, 2) - 1;
|
||||
kartitems_t item = luaL_checkinteger(L, 3);
|
||||
|
||||
NOHUD
|
||||
INLEVEL
|
||||
if (!player)
|
||||
return LUA_ErrInvalid(L, "player_t");
|
||||
itemRoulette = &player->itemRoulette;
|
||||
|
||||
if (itemRoulette->itemList.len == 0)
|
||||
return luaL_error(L, "There are no items in the roulette to set.");
|
||||
|
||||
if (index > itemRoulette->itemList.len - 1)
|
||||
return luaL_error(L, "Roulette index %d out of bounds (should be between %d and %d).", index + 1, 1, itemRoulette->itemList.len);
|
||||
|
||||
itemRoulette->itemList.items[index] = item;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void AddOrPushToItemReel(player_t *player, itemroulette_t *roulette, kartitems_t item, boolean addRings)
|
||||
{
|
||||
if (addRings)
|
||||
K_AddItemToReel(player, roulette, item);
|
||||
else
|
||||
K_PushToRouletteItemList(roulette, item);
|
||||
}
|
||||
|
||||
static int lib_kAddItemToReelByIndex(lua_State *L)
|
||||
{
|
||||
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
|
||||
itemroulette_t *itemRoulette = NULL;
|
||||
|
||||
size_t index = luaL_checkinteger(L, 2) - 1;
|
||||
kartitems_t item = luaL_checkinteger(L, 3);
|
||||
boolean addRings = lua_optboolean(L, 4);
|
||||
|
||||
NOHUD
|
||||
INLEVEL
|
||||
if (!player)
|
||||
return LUA_ErrInvalid(L, "player_t");
|
||||
itemRoulette = &player->itemRoulette;
|
||||
|
||||
// If the list is empty, just add the item silently and leave.
|
||||
if (itemRoulette->itemList.len == 0) {
|
||||
AddOrPushToItemReel(player, itemRoulette, item, addRings);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (index > itemRoulette->itemList.len - 1)
|
||||
return luaL_error(L, "Roulette index %d out of bounds (should be between %d and %d).", index + 1, 1, itemRoulette->itemList.len);
|
||||
|
||||
size_t latterItemList = itemRoulette->itemList.len - index;
|
||||
kartitems_t *latterItems = Z_Calloc(
|
||||
sizeof(kartitems_t) * latterItemList,
|
||||
PU_STATIC,
|
||||
NULL
|
||||
);
|
||||
|
||||
if (!latterItems)
|
||||
I_Error("Out of memory during calloc for lib_kAddItemToReelByIndex.");
|
||||
|
||||
for (size_t i = 0; i < latterItemList; i++)
|
||||
{
|
||||
latterItems[i] = itemRoulette->itemList.items[index + i];
|
||||
}
|
||||
|
||||
itemRoulette->itemList.len = index;
|
||||
AddOrPushToItemReel(player, itemRoulette, item, addRings);
|
||||
|
||||
for (size_t i = 0; i < latterItemList; i++)
|
||||
{
|
||||
AddOrPushToItemReel(player, itemRoulette, latterItems[i], addRings);
|
||||
}
|
||||
|
||||
Z_Free(latterItems);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lib_kRemoveItemFromReelByIndex(lua_State *L)
|
||||
{
|
||||
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
|
||||
itemroulette_t *itemRoulette = NULL;
|
||||
|
||||
size_t index = luaL_checkinteger(L, 2) - 1;
|
||||
|
||||
NOHUD
|
||||
INLEVEL
|
||||
if (!player)
|
||||
return LUA_ErrInvalid(L, "player_t");
|
||||
itemRoulette = &player->itemRoulette;
|
||||
|
||||
if (itemRoulette->itemList.len == 0)
|
||||
return luaL_error(L, "There are no items in the roulette to delete.");
|
||||
|
||||
if (index > itemRoulette->itemList.len - 1)
|
||||
return luaL_error(L, "Roulette index %d out of bounds (should be between %d and %d).", index + 1, 1, itemRoulette->itemList.len);
|
||||
|
||||
size_t latterItemList = itemRoulette->itemList.len - index - 1;
|
||||
kartitems_t *latterItems = Z_Calloc(
|
||||
sizeof(kartitems_t) * latterItemList,
|
||||
PU_STATIC,
|
||||
NULL
|
||||
);
|
||||
|
||||
if (!latterItems)
|
||||
I_Error("Out of memory during calloc for lib_kRemoveItemFromReelByIndex.");
|
||||
|
||||
for (size_t i = 0; i < latterItemList; i++)
|
||||
{
|
||||
latterItems[i] = itemRoulette->itemList.items[index + 1 + i];
|
||||
}
|
||||
|
||||
itemRoulette->itemList.len = index;
|
||||
|
||||
for (size_t i = 0; i < latterItemList; i++)
|
||||
K_PushToRouletteItemList(itemRoulette, latterItems[i]);
|
||||
|
||||
Z_Free(latterItems);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lib_kCanChangeRules(lua_State *L)
|
||||
{
|
||||
lua_pushboolean(L, K_CanChangeRules(true));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int lib_getTimeMicros(lua_State *L)
|
||||
{
|
||||
lua_pushinteger(L, I_GetPreciseTime() / (I_GetPrecisePrecision() / 1000000));
|
||||
|
|
@ -4326,9 +4811,15 @@ static luaL_Reg lib[] = {
|
|||
{"K_GetKartAccel",lib_kGetKartAccel},
|
||||
{"K_GetKartFlashing",lib_kGetKartFlashing},
|
||||
{"K_GetItemPatch",lib_kGetItemPatch},
|
||||
|
||||
{"K_GetCollideAngle",lib_kGetCollideAngle},
|
||||
{"K_AddHitLag",lib_kAddHitLag},
|
||||
{"K_GetShieldFromItem",lib_kGetShieldFromItem},
|
||||
{"K_ItemResultToType",lib_kItemResultToType},
|
||||
{"K_ItemResultToAmount",lib_kItemResultToAmount},
|
||||
{"K_GetItemCooldown",lib_kGetItemCooldown},
|
||||
{"K_SetItemCooldown",lib_kSetItemCooldown},
|
||||
{"K_TimeAttackRules",lib_kTimeAttackRules},
|
||||
{"K_CapsuleTimeAttackRules",lib_kCapsuleTimeAttackRules},
|
||||
|
||||
// k_powerup
|
||||
{"K_PowerUpRemaining",lib_kPowerUpRemaining},
|
||||
|
|
@ -4339,9 +4830,39 @@ static luaL_Reg lib[] = {
|
|||
{"K_InitBossHealthBar", lib_kInitBossHealthBar},
|
||||
{"K_UpdateBossHealthBar", lib_kUpdateBossHealthBar},
|
||||
{"K_DeclareWeakspot", lib_kDeclareWeakspot},
|
||||
{"K_CheckBossIntro", lib_kCheckBossIntro},
|
||||
{"VS_GetArena", lib_vsGetArena},
|
||||
{"VS_PredictAroundArena", lib_vsPredictAroundArena},
|
||||
{"VS_RandomPointOnArena", lib_vsRandomPointOnArena},
|
||||
|
||||
// k_roulette
|
||||
{"K_ItemEnabled", lib_kItemEnabled},
|
||||
{"K_ItemSingularity", lib_kItemSingularity},
|
||||
{"K_GetBotItemPriority", lib_kGetBotItemPriority},
|
||||
{"K_AddItemToReel", lib_kAddItemToReel},
|
||||
{"K_PushToRouletteItemList", lib_kPushToRouletteItemList},
|
||||
{"K_StartItemRoulette", lib_kStartItemRoulette},
|
||||
{"K_StartEggmanRoulette", lib_kStartEggmanRoulette},
|
||||
{"K_StopRoulette", lib_kStopRoulette},
|
||||
{"K_KartGetItemResult", lib_kKartGetItemResult},
|
||||
{"K_GetItemRouletteDistance", lib_kGetItemRouletteDistance},
|
||||
{"K_FillItemRouletteData", lib_kFillItemRouletteData},
|
||||
{"K_ForcedSPB", lib_kForcedSPB},
|
||||
{"K_DenyShieldOdds", lib_kDenyShieldOdds},
|
||||
{"K_GetRouletteOffset", lib_kGetRouletteOffset},
|
||||
{"K_GetSlotOffset", lib_kGetSlotOffset},
|
||||
{"K_CalculateRouletteSpeed", lib_kCalculateRouletteSpeed},
|
||||
{"K_ScaleItemDistance", lib_kScaleItemDistance},
|
||||
{"K_ItemOddsScale", lib_kItemOddsScale},
|
||||
// These are not real functions in k_roulette, but they allow
|
||||
// encapsulation on how the scripter interacts with the item reel.
|
||||
{"K_WipeItemsInReel", lib_kWipeItemsInReel},
|
||||
{"K_SetItemInReelByIndex", lib_kSetItemInReelByIndex},
|
||||
{"K_AddItemToReelByIndex", lib_kAddItemToReelByIndex},
|
||||
{"K_RemoveItemFromReelByIndex", lib_kRemoveItemFromReelByIndex},
|
||||
|
||||
// k_grandprix
|
||||
{"K_CanChangeRules", lib_kCanChangeRules},
|
||||
|
||||
// hu_stuff technically?
|
||||
{"HU_DoTitlecardCEcho", lib_startTitlecardCecho},
|
||||
|
|
|
|||
|
|
@ -79,6 +79,8 @@ automatically.
|
|||
X (GameQuit),\
|
||||
X (PlayerCmd),/* building the player's ticcmd struct */\
|
||||
X (VoteThinker),/* Y_VoteTicker */\
|
||||
X (PreFillItemRoulette),/* K_FillItemRouletteData, before attempted reel build */\
|
||||
X (FillItemRoulette),/* K_FillItemRouletteData, after built reel is in place */\
|
||||
|
||||
#define STRING_HOOK_LIST(X) \
|
||||
X (SpecialExecute),\
|
||||
|
|
@ -146,6 +148,8 @@ void LUA_HookPlayerQuit(player_t *, kickreason_t);
|
|||
//int LUA_HookTeamSwitch(player_t *, int newteam, boolean fromspectators, boolean tryingautobalance, boolean tryingscramble);
|
||||
int LUA_HookViewpointSwitch(player_t *player, player_t *newdisplayplayer, boolean forced);
|
||||
int LUA_HookSeenPlayer(player_t *player, player_t *seenfriend);
|
||||
int LUA_HookPreFillItemRoulette(player_t *player, itemroulette_t *const roulette, boolean ringbox);
|
||||
int LUA_HookFillItemRoulette(player_t *player, itemroulette_t *const roulette, boolean ringbox);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
#include "lua_hook.h"
|
||||
#include "lua_hud.h" // hud_running errors
|
||||
#include "lua_profile.h"
|
||||
#include "lua_playerlib.h" // constplayer
|
||||
|
||||
#include "command.h"
|
||||
#include "m_perfstats.h"
|
||||
|
|
@ -1009,4 +1010,39 @@ int LUA_HookSeenPlayer(player_t *player, player_t *seenfriend)
|
|||
return hook.status;
|
||||
}
|
||||
|
||||
static int roulette_hook(
|
||||
player_t *player,
|
||||
itemroulette_t *const roulette,
|
||||
boolean ringbox,
|
||||
int hook_type,
|
||||
Hook_Callback results_handler)
|
||||
{
|
||||
Hook_State hook;
|
||||
if (prepare_hook(&hook, false, hook_type))
|
||||
{
|
||||
if (player == NULL) {
|
||||
lua_pushnil(gL);
|
||||
} else {
|
||||
LUA_PushUserdata(gL, player, META_PLAYER);
|
||||
}
|
||||
LUA_PushUserdata(gL, roulette, META_ITEMROULETTE);
|
||||
lua_pushboolean(gL, ringbox);
|
||||
constplayer = true; // Do not allow players to be modified.
|
||||
call_hooks(&hook, 1, results_handler);
|
||||
constplayer = false; // You're good.
|
||||
}
|
||||
return hook.status;
|
||||
}
|
||||
|
||||
int LUA_HookPreFillItemRoulette(player_t *player, itemroulette_t *const roulette, boolean ringbox)
|
||||
{
|
||||
return roulette_hook(player, roulette, ringbox, HOOK(PreFillItemRoulette), res_true);
|
||||
}
|
||||
|
||||
|
||||
int LUA_HookFillItemRoulette(player_t *player, itemroulette_t *const roulette, boolean ringbox)
|
||||
{
|
||||
return roulette_hook(player, roulette, ringbox, HOOK(FillItemRoulette), res_true);
|
||||
}
|
||||
|
||||
boolean hook_cmd_running = false;
|
||||
|
|
|
|||
276
src/lua_itemroulettelib.c
Normal file
276
src/lua_itemroulettelib.c
Normal file
|
|
@ -0,0 +1,276 @@
|
|||
// DR. ROBOTNIK'S RING RACERS
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2024 by Kart Krew.
|
||||
// Copyright (C) 2020 by Sonic Team Junior.
|
||||
// Copyright (C) 2016 by John "JTE" Muniz.
|
||||
//
|
||||
// This program is free software distributed under the
|
||||
// terms of the GNU General Public License, version 2.
|
||||
// See the 'LICENSE' file for more details.
|
||||
//-----------------------------------------------------------------------------
|
||||
/// \file lua_itemroulettelib.c
|
||||
/// \brief player item roulette structure library for Lua scripting
|
||||
|
||||
#include "doomdef.h"
|
||||
#include "fastcmp.h"
|
||||
#include "d_player.h"
|
||||
#include "z_zone.h"
|
||||
#include "k_roulette.h"
|
||||
|
||||
#include "lua_script.h"
|
||||
#include "lua_libs.h"
|
||||
#include "lua_hud.h" // hud_running errors
|
||||
#include "lua_hook.h" // hook_cmd_running errors
|
||||
|
||||
enum itemroulette {
|
||||
itemroulette_valid = 0,
|
||||
itemroulette_active,
|
||||
itemroulette_itemlist,
|
||||
itemroulette_playing,
|
||||
itemroulette_exiting,
|
||||
itemroulette_dist,
|
||||
itemroulette_basedist,
|
||||
itemroulette_firstdist,
|
||||
itemroulette_seconddist,
|
||||
itemroulette_secondtofirst,
|
||||
itemroulette_index,
|
||||
itemroulette_sound,
|
||||
itemroulette_speed,
|
||||
itemroulette_tics,
|
||||
itemroulette_elapsed,
|
||||
itemroulette_eggman,
|
||||
itemroulette_ringbox,
|
||||
itemroulette_autoroulette,
|
||||
itemroulette_reserved
|
||||
};
|
||||
static const char *const itemroulette_opt[] = {
|
||||
"valid",
|
||||
"active",
|
||||
"itemlist",
|
||||
"playing",
|
||||
"exiting",
|
||||
"dist",
|
||||
"basedist",
|
||||
"firstdist",
|
||||
"seconddist",
|
||||
"secondtofirst",
|
||||
"index",
|
||||
"sound",
|
||||
"speed",
|
||||
"tics",
|
||||
"elapsed",
|
||||
"eggman",
|
||||
"ringbox",
|
||||
"autoroulette",
|
||||
"reserved",
|
||||
NULL
|
||||
};
|
||||
|
||||
static int itemroulette_get(lua_State *L)
|
||||
{
|
||||
itemroulette_t *itemroulette = *((itemroulette_t **)luaL_checkudata(L, 1, META_ITEMROULETTE));
|
||||
enum itemroulette field = luaL_checkoption(L, 2, itemroulette_opt[0], itemroulette_opt);
|
||||
|
||||
// if this is null, welcome to parking garage rally circuit
|
||||
I_Assert(itemroulette != NULL);
|
||||
|
||||
switch (field)
|
||||
{
|
||||
case itemroulette_valid:
|
||||
lua_pushboolean(L, itemroulette != NULL);
|
||||
break;
|
||||
case itemroulette_active:
|
||||
lua_pushboolean(L, itemroulette->active);
|
||||
break;
|
||||
case itemroulette_itemlist:
|
||||
LUA_PushUserdata(L, &itemroulette->itemList, META_ITEMROULETTE_ITEMLIST);
|
||||
break;
|
||||
case itemroulette_playing:
|
||||
lua_pushinteger(L, itemroulette->playing);
|
||||
break;
|
||||
case itemroulette_exiting:
|
||||
lua_pushinteger(L, itemroulette->exiting);
|
||||
break;
|
||||
case itemroulette_dist:
|
||||
lua_pushinteger(L, itemroulette->dist);
|
||||
break;
|
||||
case itemroulette_basedist:
|
||||
lua_pushinteger(L, itemroulette->baseDist);
|
||||
break;
|
||||
case itemroulette_firstdist:
|
||||
lua_pushinteger(L, itemroulette->firstDist);
|
||||
break;
|
||||
case itemroulette_seconddist:
|
||||
lua_pushinteger(L, itemroulette->secondDist);
|
||||
break;
|
||||
case itemroulette_secondtofirst:
|
||||
lua_pushinteger(L, itemroulette->secondToFirst);
|
||||
break;
|
||||
case itemroulette_index:
|
||||
lua_pushinteger(L, itemroulette->index);
|
||||
break;
|
||||
case itemroulette_sound:
|
||||
lua_pushinteger(L, itemroulette->sound);
|
||||
break;
|
||||
case itemroulette_speed:
|
||||
lua_pushinteger(L, itemroulette->speed);
|
||||
break;
|
||||
case itemroulette_tics:
|
||||
lua_pushinteger(L, itemroulette->tics);
|
||||
break;
|
||||
case itemroulette_elapsed:
|
||||
lua_pushinteger(L, itemroulette->elapsed);
|
||||
break;
|
||||
case itemroulette_eggman:
|
||||
lua_pushboolean(L, itemroulette->eggman);
|
||||
break;
|
||||
case itemroulette_ringbox:
|
||||
lua_pushboolean(L, itemroulette->ringbox);
|
||||
break;
|
||||
case itemroulette_autoroulette:
|
||||
lua_pushboolean(L, itemroulette->autoroulette);
|
||||
break;
|
||||
case itemroulette_reserved:
|
||||
lua_pushinteger(L, itemroulette->reserved);
|
||||
break;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#define NOSET luaL_error(L, LUA_QL("itemroulette_t") " field " LUA_QS " should not be set directly.", itemroulette_opt[field])
|
||||
#define UNIMPLEMENTED luaL_error(L, LUA_QL("itemroulette_t") " field " LUA_QS " is not implemented for Lua and cannot be accessed.", itemroulette_opt[field])
|
||||
#define NOSETITEMLIST luaL_error(L, LUA_QL("itemroulette_t") " field " LUA_QS " should not be set directly. Use " LUA_QL("K_AddItemToReel") ", " LUA_QL("K_PushToRouletteItemList") ", or " LUA_QL("K_StartItemRoulette") ", or " LUA_QL("K_StopRoulette") " instead.", itemroulette_opt[field])
|
||||
|
||||
static int itemroulette_set(lua_State *L)
|
||||
{
|
||||
itemroulette_t *itemroulette = *((itemroulette_t **)luaL_checkudata(L, 1, META_ITEMROULETTE));
|
||||
enum itemroulette field = luaL_checkoption(L, 2, itemroulette_opt[0], itemroulette_opt);
|
||||
|
||||
// if this is null, welcome to parking garage rally circuit
|
||||
I_Assert(itemroulette != NULL);
|
||||
|
||||
INLEVEL
|
||||
|
||||
if (hud_running)
|
||||
return luaL_error(L, "Do not alter itemroulette_t in HUD rendering code!");
|
||||
if (hook_cmd_running)
|
||||
return luaL_error(L, "Do not alter itemroulette_t in CMD building code!");
|
||||
|
||||
switch(field)
|
||||
{
|
||||
case itemroulette_valid:
|
||||
return NOSET;
|
||||
case itemroulette_active:
|
||||
itemroulette->active = luaL_checkboolean(L, 3);
|
||||
break;
|
||||
case itemroulette_itemlist:
|
||||
return NOSETITEMLIST;
|
||||
case itemroulette_playing:
|
||||
itemroulette->playing = luaL_checkinteger(L, 3);
|
||||
break;
|
||||
case itemroulette_exiting:
|
||||
itemroulette->exiting = luaL_checkinteger(L, 3);
|
||||
break;
|
||||
case itemroulette_dist:
|
||||
itemroulette->dist = luaL_checkinteger(L, 3);
|
||||
break;
|
||||
case itemroulette_basedist:
|
||||
itemroulette->baseDist = luaL_checkinteger(L, 3);
|
||||
break;
|
||||
case itemroulette_firstdist:
|
||||
itemroulette->firstDist = luaL_checkinteger(L, 3);
|
||||
break;
|
||||
case itemroulette_seconddist:
|
||||
itemroulette->secondDist = luaL_checkinteger(L, 3);
|
||||
break;
|
||||
case itemroulette_secondtofirst:
|
||||
itemroulette->secondToFirst = luaL_checkinteger(L, 3);
|
||||
break;
|
||||
case itemroulette_index:
|
||||
itemroulette->index = luaL_checkinteger(L, 3);
|
||||
break;
|
||||
case itemroulette_sound:
|
||||
itemroulette->sound = luaL_checkinteger(L, 3);
|
||||
break;
|
||||
case itemroulette_speed:
|
||||
itemroulette->speed = luaL_checkinteger(L, 3);
|
||||
break;
|
||||
case itemroulette_tics:
|
||||
itemroulette->tics = luaL_checkinteger(L, 3);
|
||||
break;
|
||||
case itemroulette_elapsed:
|
||||
itemroulette->elapsed = luaL_checkinteger(L, 3);
|
||||
break;
|
||||
case itemroulette_eggman:
|
||||
itemroulette->eggman = luaL_checkboolean(L, 3);
|
||||
break;
|
||||
case itemroulette_ringbox:
|
||||
itemroulette->ringbox = luaL_checkboolean(L, 3);
|
||||
break;
|
||||
case itemroulette_autoroulette:
|
||||
itemroulette->autoroulette = luaL_checkboolean(L, 3);
|
||||
break;
|
||||
case itemroulette_reserved:
|
||||
itemroulette->reserved = luaL_checkinteger(L, 3);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#undef NOSET
|
||||
#undef NOSETITEMLIST
|
||||
#undef UNIMPLEMENTED
|
||||
|
||||
// itemlist, i -> itemlist[i]
|
||||
static int itemrouletteitemlist_get(lua_State *L)
|
||||
{
|
||||
itemlist_t *itemlist = *((itemlist_t **)luaL_checkudata(L, 1, META_ITEMROULETTE_ITEMLIST));
|
||||
size_t index = luaL_checkint(L, 2);
|
||||
|
||||
if (!itemlist)
|
||||
return LUA_ErrInvalid(L, "itemroulette_t.itemlist_t");
|
||||
|
||||
if (index == 0 || index > itemlist->len) {
|
||||
return luaL_error(L, LUA_QL("itemroulette_t.itemlist_t") " index cannot be %d", index);
|
||||
}
|
||||
lua_pushinteger(L, itemlist->items[index-1]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int itemrouletteitemlist_set(lua_State *L)
|
||||
{
|
||||
return luaL_error(L, LUA_QL("itemroulette_t.itemlist_t") " struct cannot be edited by Lua.");
|
||||
}
|
||||
|
||||
// #itemlist -> itemList.len
|
||||
static int itemrouletteitemlist_len(lua_State* L)
|
||||
{
|
||||
itemlist_t *itemlist = *((itemlist_t **)luaL_checkudata(L, 1, META_ITEMROULETTE_ITEMLIST));
|
||||
lua_pushinteger(L, itemlist->len);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int LUA_ItemRouletteLib(lua_State *L)
|
||||
{
|
||||
luaL_newmetatable(L, META_ITEMROULETTE);
|
||||
lua_pushcfunction(L, itemroulette_get);
|
||||
lua_setfield(L, -2, "__index");
|
||||
|
||||
lua_pushcfunction(L, itemroulette_set);
|
||||
lua_setfield(L, -2, "__newindex");
|
||||
lua_pop(L,1);
|
||||
|
||||
luaL_newmetatable(L, META_ITEMROULETTE_ITEMLIST);
|
||||
lua_pushcfunction(L, itemrouletteitemlist_get);
|
||||
lua_setfield(L, -2, "__index");
|
||||
|
||||
lua_pushcfunction(L, itemrouletteitemlist_set);
|
||||
lua_setfield(L, -2, "__newindex");
|
||||
|
||||
lua_pushcfunction(L, itemrouletteitemlist_len);
|
||||
lua_setfield(L, -2, "__len");
|
||||
lua_pop(L,1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -103,6 +103,8 @@ extern lua_State *gL;
|
|||
#define META_ACTIVATOR "ACTIVATOR_T*"
|
||||
|
||||
#define META_FOLLOWER "FOLLOWER_T*"
|
||||
#define META_ITEMROULETTE "ITEMROULETTE_T"
|
||||
#define META_ITEMROULETTE_ITEMLIST "ITEMROULETTE_T.ITEMLIST"
|
||||
|
||||
boolean luaL_checkboolean(lua_State *L, int narg);
|
||||
|
||||
|
|
@ -123,6 +125,7 @@ int LUA_PolyObjLib(lua_State *L);
|
|||
int LUA_BlockmapLib(lua_State *L);
|
||||
int LUA_HudLib(lua_State *L);
|
||||
int LUA_FollowerLib(lua_State *L);
|
||||
int LUA_ItemRouletteLib(lua_State *L);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@
|
|||
#include "lua_hook.h" // hook_cmd_running errors
|
||||
#include "k_profiles.h" // GetPrettyRRID
|
||||
|
||||
boolean constplayer = false;
|
||||
|
||||
static int lib_iteratePlayers(lua_State *L)
|
||||
{
|
||||
INT32 i = -1;
|
||||
|
|
@ -438,10 +440,8 @@ static int player_get(lua_State *L)
|
|||
lua_pushinteger(L, plr->tripwireUnstuck);
|
||||
else if (fastcmp(field,"bumpunstuck"))
|
||||
lua_pushinteger(L, plr->bumpUnstuck);
|
||||
/*
|
||||
else if (fastcmp(field,"itemroulette"))
|
||||
lua_pushinteger(L, plr->itemroulette);
|
||||
*/
|
||||
LUA_PushUserdata(L, &plr->itemRoulette, META_ITEMROULETTE);
|
||||
else if (fastcmp(field,"itemtype"))
|
||||
lua_pushinteger(L, plr->itemtype);
|
||||
else if (fastcmp(field,"itemamount"))
|
||||
|
|
@ -799,6 +799,8 @@ static int player_set(lua_State *L)
|
|||
return luaL_error(L, "Do not alter player_t in HUD rendering code!");
|
||||
if (hook_cmd_running)
|
||||
return luaL_error(L, "Do not alter player_t in CMD building code!");
|
||||
if (constplayer)
|
||||
return luaL_error(L, "Do not alter player_t while modifying the roulette!");
|
||||
|
||||
if (fastcmp(field,"mo")) {
|
||||
mobj_t *newmo = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ));
|
||||
|
|
@ -1083,10 +1085,8 @@ static int player_set(lua_State *L)
|
|||
plr->tripwireUnstuck = luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"bumpunstuck"))
|
||||
plr->bumpUnstuck = luaL_checkinteger(L, 3);
|
||||
/*
|
||||
else if (fastcmp(field,"itemroulette"))
|
||||
plr->itemroulette = luaL_checkinteger(L, 3);
|
||||
*/
|
||||
return NOSET;
|
||||
else if (fastcmp(field,"itemtype"))
|
||||
plr->itemtype = luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"itemamount"))
|
||||
|
|
|
|||
27
src/lua_playerlib.h
Normal file
27
src/lua_playerlib.h
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
// DR. ROBOTNIK'S RING RACERS
|
||||
//-----------------------------------------------------------------------------
|
||||
// Copyright (C) 2024 by Kart Krew.
|
||||
// Copyright (C) 2022 by Sonic Team Junior.
|
||||
// Copyright (C) 2016 by John "JTE" Muniz.
|
||||
//
|
||||
// This program is free software distributed under the
|
||||
// terms of the GNU General Public License, version 2.
|
||||
// See the 'LICENSE' file for more details.
|
||||
//-----------------------------------------------------------------------------
|
||||
/// \file lua_playerlib.h
|
||||
/// \brief LUA Player library header.
|
||||
|
||||
#ifndef __LUA_PLAYER_H__
|
||||
#define __LUA_PLAYER_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern boolean constplayer;
|
||||
|
||||
#ifdef __cplusplus
|
||||
} // extern "C"
|
||||
#endif
|
||||
|
||||
#endif // __LUA_PLAYER_H__
|
||||
|
|
@ -61,6 +61,7 @@ static lua_CFunction liblist[] = {
|
|||
LUA_BlockmapLib, // blockmap stuff
|
||||
LUA_HudLib, // HUD stuff
|
||||
LUA_FollowerLib, // follower_t, followers[]
|
||||
LUA_ItemRouletteLib, // itemroulette_t
|
||||
NULL
|
||||
};
|
||||
|
||||
|
|
@ -894,6 +895,7 @@ void LUA_InvalidatePlayer(player_t *player)
|
|||
LUA_InvalidateUserdata(player);
|
||||
LUA_InvalidateUserdata(player->karthud);
|
||||
LUA_InvalidateUserdata(&player->cmd);
|
||||
LUA_InvalidateUserdata(&player->itemRoulette.itemList);
|
||||
}
|
||||
|
||||
enum
|
||||
|
|
|
|||
|
|
@ -138,6 +138,7 @@ static inline void P_ArchivePlayer(savebuffer_t *save)
|
|||
|
||||
WRITEUINT8(save->p, players[i].botvars.difficulty);
|
||||
WRITEUINT8(save->p, (UINT8)players[i].botvars.rival);
|
||||
WRITEUINT8(save->p, (UINT8)players[i].botvars.foe);
|
||||
|
||||
WRITEUINT32(save->p, players[i].score);
|
||||
}
|
||||
|
|
@ -195,6 +196,7 @@ static boolean P_UnArchivePlayer(savebuffer_t *save)
|
|||
|
||||
savedata.bots[pid].difficulty = READUINT8(save->p);
|
||||
savedata.bots[pid].rival = (boolean)READUINT8(save->p);
|
||||
savedata.bots[pid].foe = (boolean)READUINT8(save->p);
|
||||
savedata.bots[pid].score = READUINT32(save->p);
|
||||
}
|
||||
|
||||
|
|
@ -765,6 +767,7 @@ static void P_NetArchivePlayers(savebuffer_t *save)
|
|||
WRITEUINT8(save->p, players[i].botvars.difficulty);
|
||||
WRITEUINT8(save->p, players[i].botvars.diffincrease);
|
||||
WRITEUINT8(save->p, players[i].botvars.rival);
|
||||
WRITEUINT8(save->p, players[i].botvars.foe);
|
||||
WRITEFIXED(save->p, players[i].botvars.rubberband);
|
||||
WRITEUINT8(save->p, players[i].botvars.bumpslow);
|
||||
WRITEUINT32(save->p, players[i].botvars.itemdelay);
|
||||
|
|
@ -782,33 +785,33 @@ static void P_NetArchivePlayers(savebuffer_t *save)
|
|||
WRITEUINT8(save->p, players[i].itemRoulette.active);
|
||||
|
||||
#ifdef ITEM_LIST_SIZE
|
||||
WRITEUINT32(save->p, players[i].itemRoulette.itemListLen);
|
||||
WRITEUINT32(save->p, players[i].itemRoulette.itemList.len);
|
||||
|
||||
for (q = 0; q < ITEM_LIST_SIZE; q++)
|
||||
{
|
||||
if (q >= players[i].itemRoulette.itemListLen)
|
||||
if (q >= players[i].itemRoulette.itemList.len)
|
||||
{
|
||||
WRITESINT8(save->p, KITEM_NONE);
|
||||
}
|
||||
else
|
||||
{
|
||||
WRITESINT8(save->p, players[i].itemRoulette.itemList[q]);
|
||||
WRITESINT8(save->p, players[i].itemRoulette.itemList.items[q]);
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (players[i].itemRoulette.itemList == NULL)
|
||||
if (players[i].itemRoulette.itemList.items == NULL)
|
||||
{
|
||||
WRITEUINT32(save->p, 0);
|
||||
WRITEUINT32(save->p, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
WRITEUINT32(save->p, players[i].itemRoulette.itemListCap);
|
||||
WRITEUINT32(save->p, players[i].itemRoulette.itemListLen);
|
||||
WRITEUINT32(save->p, players[i].itemRoulette.itemList.cap);
|
||||
WRITEUINT32(save->p, players[i].itemRoulette.itemList.len);
|
||||
|
||||
for (q = 0; q < players[i].itemRoulette.itemListLen; q++)
|
||||
for (q = 0; q < players[i].itemRoulette.itemList.len; q++)
|
||||
{
|
||||
WRITESINT8(save->p, players[i].itemRoulette.itemList[q]);
|
||||
WRITESINT8(save->p, players[i].itemRoulette.itemList.items[q]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -1430,6 +1433,7 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
|
|||
players[i].botvars.difficulty = READUINT8(save->p);
|
||||
players[i].botvars.diffincrease = READUINT8(save->p);
|
||||
players[i].botvars.rival = (boolean)READUINT8(save->p);
|
||||
players[i].botvars.foe = (boolean)READUINT8(save->p);
|
||||
players[i].botvars.rubberband = READFIXED(save->p);
|
||||
players[i].botvars.bumpslow = READUINT8(save->p);
|
||||
players[i].botvars.itemdelay = READUINT32(save->p);
|
||||
|
|
@ -1447,44 +1451,44 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
|
|||
players[i].itemRoulette.active = (boolean)READUINT8(save->p);
|
||||
|
||||
#ifdef ITEM_LIST_SIZE
|
||||
players[i].itemRoulette.itemListLen = (size_t)READUINT32(save->p);
|
||||
players[i].itemRoulette.itemList.len = (size_t)READUINT32(save->p);
|
||||
|
||||
for (q = 0; q < ITEM_LIST_SIZE; q++)
|
||||
{
|
||||
players[i].itemRoulette.itemList[q] = READSINT8(save->p);
|
||||
players[i].itemRoulette.itemList.items[q] = READSINT8(save->p);
|
||||
}
|
||||
#else
|
||||
players[i].itemRoulette.itemListCap = (size_t)READUINT32(save->p);
|
||||
players[i].itemRoulette.itemListLen = (size_t)READUINT32(save->p);
|
||||
players[i].itemRoulette.itemList.cap = (size_t)READUINT32(save->p);
|
||||
players[i].itemRoulette.itemList.len = (size_t)READUINT32(save->p);
|
||||
|
||||
if (players[i].itemRoulette.itemListCap > 0)
|
||||
if (players[i].itemRoulette.itemList.cap > 0)
|
||||
{
|
||||
if (players[i].itemRoulette.itemList == NULL)
|
||||
if (players[i].itemRoulette.itemList.items == NULL)
|
||||
{
|
||||
players[i].itemRoulette.itemList = Z_Calloc(
|
||||
sizeof(SINT8) * players[i].itemRoulette.itemListCap,
|
||||
players[i].itemRoulette.itemList.items = (SINT8*)Z_Calloc(
|
||||
sizeof(SINT8) * players[i].itemRoulette.itemList.cap,
|
||||
PU_STATIC,
|
||||
&players[i].itemRoulette.itemList
|
||||
NULL
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
players[i].itemRoulette.itemList = Z_Realloc(
|
||||
players[i].itemRoulette.itemList,
|
||||
sizeof(SINT8) * players[i].itemRoulette.itemListCap,
|
||||
players[i].itemRoulette.itemList.items = (SINT8*)Z_Realloc(
|
||||
players[i].itemRoulette.itemList.items,
|
||||
sizeof(SINT8) * players[i].itemRoulette.itemList.cap,
|
||||
PU_STATIC,
|
||||
&players[i].itemRoulette.itemList
|
||||
NULL
|
||||
);
|
||||
}
|
||||
|
||||
if (players[i].itemRoulette.itemList == NULL)
|
||||
if (players[i].itemRoulette.itemList.items == NULL)
|
||||
{
|
||||
I_Error("Not enough memory for item roulette list\n");
|
||||
}
|
||||
|
||||
for (q = 0; q < players[i].itemRoulette.itemListLen; q++)
|
||||
for (q = 0; q < players[i].itemRoulette.itemList.len; q++)
|
||||
{
|
||||
players[i].itemRoulette.itemList[q] = READSINT8(save->p);
|
||||
players[i].itemRoulette.itemList.items[q] = READSINT8(save->p);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ struct savedata_bot_s
|
|||
UINT8 skin;
|
||||
UINT8 difficulty;
|
||||
boolean rival;
|
||||
boolean foe;
|
||||
UINT32 score;
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue