Merge branch 'master' into new-menus

This commit is contained in:
SinnamonLat 2021-12-17 12:32:01 +01:00
commit 7c9f3385d4
36 changed files with 1016 additions and 576 deletions

View file

@ -795,10 +795,14 @@ static boolean CL_SendJoin(void)
sizeof netbuffer->u.clientcfg.application);
for (i = 0; i <= splitscreen; i++)
CleanupPlayerName(g_localplayers[i], cv_playername[i].zstring);
for (i = 0; i < MAXSPLITSCREENPLAYERS; i++)
{
// the MAXPLAYERS addition is necessary to communicate that g_localplayers is not yet safe to reference
CleanupPlayerName(MAXPLAYERS+i, cv_playername[i].zstring);
strncpy(netbuffer->u.clientcfg.names[i], cv_playername[i].zstring, MAXPLAYERNAME);
}
// privacy shield for the local players not joining this session
for (; i < MAXSPLITSCREENPLAYERS; i++)
strncpy(netbuffer->u.clientcfg.names[i], va("Player %c", 'A' + i), MAXPLAYERNAME);
return HSendPacket(servernode, false, 0, sizeof (clientconfig_pak));
}
@ -1334,7 +1338,7 @@ static void CL_ReloadReceivedSavegame(void)
for (i = 0; i < MAXPLAYERS; i++)
{
LUA_InvalidatePlayer(&players[i]);
sprintf(player_names[i], "Player %d", i + 1);
sprintf(player_names[i], "Player %c", 'A' + i);
}
CL_LoadReceivedSavegame(true);
@ -2421,7 +2425,7 @@ void CL_RemovePlayer(INT32 playernum, kickreason_t reason)
doomcom->numslots--;
// Reset the name
sprintf(player_names[playernum], "Player %d", playernum+1);
sprintf(player_names[playernum], "Player %c", 'A' + playernum);
player_name_changes[playernum] = 0;
@ -3233,7 +3237,7 @@ void SV_ResetServer(void)
playeringame[i] = false;
playernode[i] = UINT8_MAX;
memset(playeraddress[i], 0, sizeof(*playeraddress));
sprintf(player_names[i], "Player %d", i + 1);
sprintf(player_names[i], "Player %c", 'A' + i);
adminplayers[i] = -1; // Populate the entire adminplayers array with -1.
K_ClearClientPowerLevels();
splitscreen_invitations[i] = -1;
@ -3432,7 +3436,7 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
P_ForceLocalAngle(newplayer, newplayer->angleturn);
D_SendPlayerConfig();
D_SendPlayerConfig(splitscreenplayer);
addedtogame = true;
if (rejoined)
@ -3578,14 +3582,13 @@ static void Got_AddBot(UINT8 **p, INT32 playernum)
LUAh_PlayerJoin(newplayernum);
}
static boolean SV_AddWaitingPlayers(const char *name, const char *name2, const char *name3, const char *name4)
static boolean SV_AddWaitingPlayers(SINT8 node, const char *name, const char *name2, const char *name3, const char *name4)
{
INT32 node, n, newplayer = false;
INT32 n, newplayernum;
UINT8 buf[4 + MAXPLAYERNAME];
UINT8 *buf_p = buf;
INT32 newplayernum;
boolean newplayer = false;
for (node = 0; node < MAXNETNODES; node++)
{
// splitscreen can allow 2+ players in one node
for (; nodewaiting[node] > 0; nodewaiting[node]--)
@ -3704,6 +3707,7 @@ boolean SV_SpawnServer(void)
I_Error("What do you think you're doing?");
return false;
#else
boolean result = false;
if (demo.playback)
G_StopDemo(); // reset engine parameter
if (metalplayback)
@ -3730,7 +3734,14 @@ boolean SV_SpawnServer(void)
else doomcom->numslots = 1;
}
return SV_AddWaitingPlayers(cv_playername[0].zstring, cv_playername[1].zstring, cv_playername[2].zstring, cv_playername[3].zstring);
// strictly speaking, i'm not convinced the following is necessary
// but I'm not confident enough to remove it entirely in case it breaks something
{
SINT8 node = 0;
for (; node < MAXNETNODES; node++)
result |= SV_AddWaitingPlayers(node, cv_playername[0].zstring, cv_playername[1].zstring, cv_playername[2].zstring, cv_playername[3].zstring);
}
return result;
#endif
}
@ -3903,7 +3914,7 @@ static void HandleConnect(SINT8 node)
SV_SendSaveGame(node, false); // send a complete game state
DEBFILE("send savegame\n");
}
SV_AddWaitingPlayers(names[0], names[1], names[2], names[3]);
SV_AddWaitingPlayers(node, names[0], names[1], names[2], names[3]);
joindelay += cv_joindelay.value * TICRATE;
player_joining = true;
}

View file

@ -1418,6 +1418,9 @@ void D_SRB2Main(void)
// setup loading screen
SCR_Startup();
// Do this in background; lots of number crunching
R_InitTranslucencyTables();
CON_SetLoadingProgress(LOADED_ISTARTUPGRAPHICS);
CONS_Printf("HU_Init()...\n");

View file

@ -222,8 +222,6 @@ static void Command_KartGiveItem_f(void);
// CLIENT VARIABLES
// =========================================================================
void SendWeaponPref(UINT8 n);
static CV_PossibleValue_t usemouse_cons_t[] = {{0, "Off"}, {1, "On"}, {2, "Force"}, {0, NULL}};
#ifdef LJOYSTICK
@ -1110,6 +1108,7 @@ boolean EnsurePlayerNameIsGood(char *name, INT32 playernum)
* is restored to what it was before.
*
* We assume that if playernum is in ::g_localplayers
* (unless clientjoin is true, a necessary evil)
* the console variable ::cv_playername[n] is
* already set to newname. However, the player name table is assumed to
* contain the old name.
@ -1128,6 +1127,10 @@ void CleanupPlayerName(INT32 playernum, const char *newname)
char *tmpname = NULL;
INT32 i;
boolean namefailed = true;
boolean clientjoin = !!(playernum >= MAXPLAYERS);
if (clientjoin)
playernum -= MAXPLAYERS;
buf = Z_StrDup(newname);
@ -1185,17 +1188,20 @@ void CleanupPlayerName(INT32 playernum, const char *newname)
}
// no stealing another player's name
for (i = 0; i < MAXPLAYERS; i++)
if (!clientjoin)
{
if (i != playernum && playeringame[i]
&& strcasecmp(tmpname, player_names[i]) == 0)
for (i = 0; i < MAXPLAYERS; i++)
{
break;
if (i != playernum && playeringame[i]
&& strcasecmp(tmpname, player_names[i]) == 0)
{
break;
}
}
}
if (i < MAXPLAYERS)
break;
if (i < MAXPLAYERS)
break;
}
// name is okay then
namefailed = false;
@ -1206,18 +1212,23 @@ void CleanupPlayerName(INT32 playernum, const char *newname)
// set consvars whether namefailed or not, because even if it succeeded,
// spaces may have been removed
for (i = 0; i <= splitscreen; i++)
if (clientjoin)
CV_StealthSet(&cv_playername[playernum], tmpname);
else
{
if (playernum == g_localplayers[i])
for (i = 0; i <= splitscreen; i++)
{
CV_StealthSet(&cv_playername[i], tmpname);
break;
if (playernum == g_localplayers[i])
{
CV_StealthSet(&cv_playername[i], tmpname);
break;
}
}
}
if (i > splitscreen)
{
I_Assert(((void)"CleanupPlayerName used on non-local player", 0));
if (i > splitscreen)
{
I_Assert(((void)"CleanupPlayerName used on non-local player", 0));
}
}
Z_Free(buf);
@ -1813,33 +1824,28 @@ static void Got_LeaveParty(UINT8 **cp,INT32 playernum)
}
}
void D_SendPlayerConfig(void)
void D_SendPlayerConfig(UINT8 n)
{
UINT8 i;
UINT8 buf[4];
UINT8 *p = buf;
for (i = 0; i <= splitscreen; i++)
SendNameAndColor(n);
SendWeaponPref(n);
if (n == 0)
{
UINT8 buf[4];
UINT8 *p = buf;
SendNameAndColor(i);
SendWeaponPref(i);
if (i == 0)
{
// Send it over
WRITEUINT16(p, vspowerlevel[PWRLV_RACE]);
WRITEUINT16(p, vspowerlevel[PWRLV_BATTLE]);
}
else
{
// Splitscreen players have invalid powerlevel
WRITEUINT16(p, 0);
WRITEUINT16(p, 0);
}
SendNetXCmdForPlayer(i, XD_POWERLEVEL, buf, p-buf);
// Send it over
WRITEUINT16(p, vspowerlevel[PWRLV_RACE]);
WRITEUINT16(p, vspowerlevel[PWRLV_BATTLE]);
}
else
{
// Splitscreen players have invalid powerlevel
WRITEUINT16(p, 0);
WRITEUINT16(p, 0);
}
SendNetXCmdForPlayer(n, XD_POWERLEVEL, buf, p-buf);
}
// Only works for displayplayer, sorry!

View file

@ -207,7 +207,8 @@ void D_RegisterServerCommands(void);
void D_RegisterClientCommands(void);
void CleanupPlayerName(INT32 playernum, const char *newname);
boolean EnsurePlayerNameIsGood(char *name, INT32 playernum);
void D_SendPlayerConfig(void);
void SendWeaponPref(UINT8 n);
void D_SendPlayerConfig(UINT8 n);
void Command_ExitGame_f(void);
void Command_Retry_f(void);
void D_GameTypeChanged(INT32 lastgametype); // not a real _OnChange function anymore

View file

@ -161,10 +161,16 @@ UINT8 *PutFileNeeded(UINT16 firstfile)
char wadfilename[MAX_WADPATH] = "";
UINT8 filestatus;
for (i = mainwads+1; i < numwadfiles; i++) //mainwads+1, otherwise we start on the first mainwad
#ifdef DEVELOP
i = 0;
#else
i = mainwads + 1;
#endif
for (; i < numwadfiles; i++) //mainwads+1, otherwise we start on the first mainwad
{
// If it has only music/sound lumps, don't put it in the list
if (!wadfiles[i]->important)
if (i > mainwads && !wadfiles[i]->important)
continue;
if (firstfile)
@ -276,11 +282,16 @@ boolean CL_CheckDownloadable(void)
}
// Downloading locally disabled
#if 0
if (!dlstatus && M_CheckParm("-nodownload"))
dlstatus = 3;
if (!dlstatus)
return true;
#else
if (!dlstatus)
dlstatus = 3;
#endif
// not downloadable, put reason in console
CONS_Alert(CONS_NOTICE, M_GetText("You need additional files to connect to this server:\n"));
@ -489,7 +500,12 @@ INT32 CL_CheckFiles(void)
CONS_Debug(DBG_NETPLAY, "searching for '%s' ", fileneeded[i].filename);
// Check in already loaded files
for (j = mainwads+1; wadfiles[j]; j++)
#ifdef DEVELOP
j = 0;
#else
j = mainwads + 1;
#endif
for (; wadfiles[j]; j++)
{
nameonly(strcpy(wadfilename, wadfiles[j]->filename));
if (!stricmp(wadfilename, fileneeded[i].filename) &&

View file

@ -3476,6 +3476,9 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi
"S_RANDOMITEM12",
"S_DEADRANDOMITEM",
// Sphere Box (for Battle)
"S_SPHEREBOX",
// Random Item Pop
"S_RANDOMITEMPOP1",
"S_RANDOMITEMPOP2",

View file

@ -2793,6 +2793,7 @@ void G_DoPlayDemo(char *defdemoname)
demoflags = READUINT8(demo_p);
gametype = READUINT8(demo_p);
G_SetGametype(gametype);
if (demo.title) // Titledemos should always play and ought to always be compatible with whatever wadlist is running.
G_SkipDemoExtraFiles(&demo_p);

View file

@ -346,7 +346,6 @@ static void kickstartaccel_OnChange(void);
static void kickstartaccel2_OnChange(void);
static void kickstartaccel3_OnChange(void);
static void kickstartaccel4_OnChange(void);
void SendWeaponPref(UINT8 n);
static CV_PossibleValue_t joyaxis_cons_t[] = {{0, "None"},
{1, "X-Axis"}, {2, "Y-Axis"}, {-1, "X-Axis-"}, {-2, "Y-Axis-"},

View file

@ -113,7 +113,7 @@ typedef struct
UINT8 splitscreen;
boolean flip; // screenflip
boolean shearing; // 14042019
angle_t viewaiming; // 17052019
float viewaiming; // 17052019
boolean roll;
FLOAT rollangle; // done to not override USE_FTRANSFORM_ANGLEZ
FLOAT centerx, centery;

View file

@ -1650,7 +1650,7 @@ boolean HWR_DrawModel(gl_vissprite_t *spr)
}
{
fixed_t anglef = AngleFixed(R_SpriteRotationAngle(spr->mobj, NULL));
fixed_t anglef = AngleFixed(R_ModelRotationAngle(spr->mobj, NULL));
p.rollangle = 0.0f;

View file

@ -526,6 +526,7 @@ char sprnames[NUMSPRITES + 1][5] =
//SRB2kart Sprites (sort later)
"RNDM", // Random Item Box
"SBOX", // Sphere Box (for Battle)
"RPOP", // Random Item Box Pop
"SGNS", // Signpost sparkle
"FAST", // Speed boost trail
@ -4061,6 +4062,20 @@ state_t states[NUMSTATES] =
{SPR_RNDM, 22|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_RANDOMITEM1}, // S_RANDOMITEM12
{SPR_NULL, 0, 0, {A_ItemPop}, 0, 0, S_NULL}, // S_DEADRANDOMITEM
{SPR_SBOX, FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX2}, // S_SPHEREBOX1
{SPR_SBOX, 2|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX3}, // S_SPHEREBOX2
{SPR_SBOX, 4|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX4}, // S_SPHEREBOX3
{SPR_SBOX, 6|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX5}, // S_SPHEREBOX4
{SPR_SBOX, 8|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX6}, // S_SPHEREBOX5
{SPR_SBOX, 10|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX7}, // S_SPHEREBOX6
{SPR_SBOX, 12|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX8}, // S_SPHEREBOX7
{SPR_SBOX, 14|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX9}, // S_SPHEREBOX8
{SPR_SBOX, 16|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX10}, // S_SPHEREBOX9
{SPR_SBOX, 18|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX11}, // S_SPHEREBOX10
{SPR_SBOX, 20|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX12}, // S_SPHEREBOX11
{SPR_SBOX, 22|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX1}, // S_SPHEREBOX12
{SPR_NULL, 0, 0, {A_ItemPop}, 1, 0, S_NULL}, // S_DEADSPHEREBOX
{SPR_RPOP, FF_FULLBRIGHT, 5, {NULL}, 0, 0, S_RANDOMITEMPOP2}, // S_RANDOMITEMPOP1
{SPR_RPOP, FF_FULLBRIGHT|1, 5, {NULL}, 0, 0, S_RANDOMITEMPOP3}, // S_RANDOMITEMPOP2
{SPR_RPOP, FF_FULLBRIGHT|2, 5, {NULL}, 0, 0, S_RANDOMITEMPOP4}, // S_RANDOMITEMPOP3
@ -23017,6 +23032,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL // raisestate
},
{ // MT_SPHEREBOX
-1, // doomednum
S_SPHEREBOX1, // spawnstate
1000, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
0, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
0, // painchance
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
S_DEADSPHEREBOX, // deathstate
S_NULL, // xdeathstate
sfx_kc2e, // deathsound
60*FRACUNIT, // speed
48*FRACUNIT, // radius
48*FRACUNIT, // height
0, // display offset
100, // mass
MT_RANDOMITEMPOP, // damage
sfx_None, // activesound
MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags
S_NULL // raisestate
},
{ // MT_RANDOMITEMPOP
-1, // doomednum
S_INVISIBLE, // spawnstate

View file

@ -1070,6 +1070,7 @@ typedef enum sprite
// SRB2Kart
SPR_RNDM, // Random Item Box
SPR_SBOX, // Sphere Box (for Battle)
SPR_RPOP, // Random Item Box Pop
SPR_SGNS, // Signpost sparkle
SPR_FAST, // Speed boost trail
@ -4453,6 +4454,21 @@ typedef enum state
S_RANDOMITEM12,
S_DEADRANDOMITEM,
// Sphere Box (for Battle)
S_SPHEREBOX1,
S_SPHEREBOX2,
S_SPHEREBOX3,
S_SPHEREBOX4,
S_SPHEREBOX5,
S_SPHEREBOX6,
S_SPHEREBOX7,
S_SPHEREBOX8,
S_SPHEREBOX9,
S_SPHEREBOX10,
S_SPHEREBOX11,
S_SPHEREBOX12,
S_DEADSPHEREBOX,
// Random Item Pop
S_RANDOMITEMPOP1,
S_RANDOMITEMPOP2,
@ -6465,6 +6481,7 @@ typedef enum mobj_type
// SRB2kart
MT_RANDOMITEM,
MT_SPHEREBOX,
MT_RANDOMITEMPOP,
MT_FLOATINGITEM,
MT_ITEMCAPSULE,

View file

@ -229,6 +229,28 @@ mobj_t *K_SpawnChaosEmerald(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT
return emerald;
}
mobj_t *K_SpawnSphereBox(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 flip, UINT8 amount)
{
mobj_t *drop = P_SpawnMobj(x, y, z, MT_SPHEREBOX);
(void)amount;
drop->angle = angle;
P_Thrust(drop,
FixedAngle(P_RandomFixed() * 180) + angle,
P_RandomRange(4, 12) * mapobjectscale);
drop->momz = flip * 12 * mapobjectscale;
if (drop->eflags & MFE_UNDERWATER)
drop->momz = (117 * drop->momz) / 200;
drop->flags &= ~(MF_NOGRAVITY|MF_NOCLIPHEIGHT);
drop->extravalue1 = amount;
return drop;
}
void K_DropEmeraldsFromPlayer(player_t *player, UINT32 emeraldType)
{
UINT8 i;
@ -359,6 +381,12 @@ void K_RunPaperItemSpawners(void)
FixedAngle(P_RandomRange(0, 359) * FRACUNIT), flip,
0, 0
);
K_SpawnSphereBox(
battleovertime.x, battleovertime.y, battleovertime.z + (128 * mapobjectscale * flip),
FixedAngle(P_RandomRange(0, 359) * FRACUNIT), flip,
10
);
}
}
else
@ -441,6 +469,14 @@ void K_RunPaperItemSpawners(void)
firstUnspawnedEmerald
);
}
else if (P_RandomChance(FRACUNIT/3))
{
drop = K_SpawnSphereBox(
spotList[r]->x, spotList[r]->y, spotList[r]->z + (128 * mapobjectscale * flip),
FixedAngle(P_RandomRange(0, 359) * FRACUNIT), flip,
10
);
}
else
{
drop = K_CreatePaperItem(

View file

@ -22,6 +22,7 @@ void K_SpawnBattlePoints(player_t *source, player_t *victim, UINT8 amount);
void K_CheckBumpers(void);
void K_CheckEmeralds(player_t *player);
mobj_t *K_SpawnChaosEmerald(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 flip, UINT32 emeraldType);
mobj_t *K_SpawnSphereBox(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 flip, UINT8 amount);
void K_DropEmeraldsFromPlayer(player_t *player, UINT32 emeraldType);
UINT8 K_NumEmeralds(player_t *player);
void K_RunPaperItemSpawners(void);

View file

@ -273,16 +273,94 @@ boolean K_PlayerUsesBotMovement(player_t *player)
--------------------------------------------------*/
boolean K_BotCanTakeCut(player_t *player)
{
if (!K_ApplyOffroad(player)
if (
#if 1
K_TripwirePass(player) == true
#else
K_ApplyOffroad(player) == false
#endif
|| player->itemtype == KITEM_SNEAKER
|| player->itemtype == KITEM_ROCKETSNEAKER
|| player->itemtype == KITEM_INVINCIBILITY
|| player->itemtype == KITEM_HYUDORO)
)
{
return true;
}
return false;
}
/*--------------------------------------------------
static line_t *K_FindBotController(mobj_t *mo)
Finds if any bot controller linedefs are tagged to the bot's sector.
Input Arguments:-
mo - The bot player's mobj.
Return:-
Linedef of the bot controller. NULL if it doesn't exist.
--------------------------------------------------*/
static line_t *K_FindBotController(mobj_t *mo)
{
msecnode_t *node;
ffloor_t *rover;
INT16 lineNum = -1;
mtag_t tag;
I_Assert(mo != NULL);
I_Assert(!P_MobjWasRemoved(mo));
for (node = mo->touching_sectorlist; node; node = node->m_sectorlist_next)
{
if (!node->m_sector)
{
continue;
}
tag = Tag_FGet(&node->m_sector->tags);
lineNum = P_FindSpecialLineFromTag(2004, tag, -1); // todo: needs to not use P_FindSpecialLineFromTag
if (lineNum != -1)
{
break;
}
for (rover = node->m_sector->ffloors; rover; rover = rover->next)
{
sector_t *rs = NULL;
if (!(rover->flags & FF_EXISTS))
{
continue;
}
if (mo->z > *rover->topheight || mo->z + mo->height < *rover->bottomheight)
{
continue;
}
rs = &sectors[rover->secnum];
tag = Tag_FGet(&rs->tags);
lineNum = P_FindSpecialLineFromTag(2004, tag, -1);
if (lineNum != -1)
{
break;
}
}
}
if (lineNum != -1)
{
return &lines[lineNum];
}
else
{
return NULL;
}
}
/*--------------------------------------------------
static UINT32 K_BotRubberbandDistance(player_t *player)
@ -346,6 +424,7 @@ fixed_t K_BotRubberband(player_t *player)
fixed_t rubberband = FRACUNIT;
fixed_t max, min;
player_t *firstplace = NULL;
line_t *botController = NULL;
UINT8 i;
if (player->exiting)
@ -354,6 +433,17 @@ fixed_t K_BotRubberband(player_t *player)
return FRACUNIT;
}
botController = K_FindBotController(player->mo);
if (botController != NULL)
{
// No Climb Flag: Disable rubberbanding
if (botController->flags & ML_NOCLIMB)
{
return FRACUNIT;
}
}
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || players[i].spectator)
@ -430,8 +520,8 @@ fixed_t K_BotTopSpeedRubberband(player_t *player)
}
else
{
// Max at +10% for level 9 bots
rubberband = FRACUNIT + ((rubberband - FRACUNIT) / 10);
// Max at +20% for level 9 bots
rubberband = FRACUNIT + ((rubberband - FRACUNIT) / 5);
}
// Only allow you to go faster than your regular top speed if you're facing the right direction
@ -488,6 +578,14 @@ fixed_t K_BotFrictionRubberband(player_t *player, fixed_t frict)
return frict;
}
if (player->tiregrease > 0)
{
// This isn't great -- it means rubberbanding will slow down when they hit a spring
// But it's better than the opposite where they accelerate into hyperspace :V
// (would appreciate an actual fix though ... could try being additive instead of multiplicative)
return frict;
}
origFrict = FixedDiv(ORIG_FRICTION, FRACUNIT + (rubberband / 2));
if (frict == ORIG_FRICTION)
@ -567,14 +665,11 @@ static botprediction_t *K_CreateBotPrediction(player_t *player)
const INT16 handling = K_GetKartTurnValue(player, KART_FULLTURN); // Reduce prediction based on how fast you can turn
const INT16 normal = KART_FULLTURN; // "Standard" handling to compare to
const fixed_t distreduce = K_BotReducePrediction(player);
const fixed_t radreduce = min(distreduce + FRACUNIT/4, FRACUNIT);
const tic_t futuresight = (TICRATE * normal) / max(1, handling); // How far ahead into the future to try and predict
const fixed_t speed = max(P_AproxDistance(player->mo->momx, player->mo->momy), K_GetKartSpeed(player, false) / 4);
const fixed_t speed = P_AproxDistance(player->mo->momx, player->mo->momy);
const INT32 startDist = (768 * mapobjectscale) / FRACUNIT;
const INT32 distance = ((FixedMul(speed, distreduce) / FRACUNIT) * futuresight) + startDist;
const INT32 distance = ((speed / FRACUNIT) * futuresight) + startDist;
botprediction_t *predict = Z_Calloc(sizeof(botprediction_t), PU_STATIC, NULL);
waypoint_t *wp = player->nextwaypoint;
@ -583,6 +678,9 @@ static botprediction_t *K_CreateBotPrediction(player_t *player)
fixed_t smallestradius = INT32_MAX;
angle_t angletonext = ANGLE_MAX;
// Halves radius when encountering a wall on your way to your destination.
fixed_t radreduce = FRACUNIT;
size_t nwp;
size_t i;
@ -595,7 +693,7 @@ static botprediction_t *K_CreateBotPrediction(player_t *player)
{
predict->x = wp->mobj->x;
predict->y = wp->mobj->y;
predict->radius = FixedMul(wp->mobj->radius, radreduce);
predict->radius = wp->mobj->radius;
return predict;
}
@ -635,12 +733,12 @@ static botprediction_t *K_CreateBotPrediction(player_t *player)
for (i = 0; i < wp->numnextwaypoints; i++)
{
if (!K_GetWaypointIsEnabled(wp->nextwaypoints[i]))
if (K_GetWaypointIsEnabled(wp->nextwaypoints[i]) == false)
{
continue;
}
if (K_GetWaypointIsShortcut(wp->nextwaypoints[i]) && !K_BotCanTakeCut(player))
if (K_GetWaypointIsShortcut(wp->nextwaypoints[i]) == true && K_BotCanTakeCut(player) == false)
{
continue;
}
@ -673,6 +771,13 @@ static botprediction_t *K_CreateBotPrediction(player_t *player)
disttonext = (INT32)wp->nextwaypointdistances[nwp];
if (P_TraceBotTraversal(player->mo, wp->nextwaypoints[nwp]->mobj) == false)
{
// If we can't get a direct path to this waypoint, we don't want to check much further...
disttonext *= 2;
radreduce = FRACUNIT/2;
}
if (disttonext > distanceleft)
{
break;
@ -761,7 +866,7 @@ static UINT8 K_TrySpindash(player_t *player)
return 0;
}
if (speedDiff < (3 * baseAccel / 4))
if (speedDiff < (baseAccel / 4))
{
if (player->botvars.spindashconfirm < BOTSPINDASHCONFIRM)
{
@ -797,70 +902,6 @@ static UINT8 K_TrySpindash(player_t *player)
return 0;
}
/*--------------------------------------------------
static INT16 K_FindBotController(mobj_t *mo)
Finds if any bot controller linedefs are tagged to the bot's sector.
Input Arguments:-
mo - The bot player's mobj.
Return:-
Line number of the bot controller. -1 if it doesn't exist.
--------------------------------------------------*/
static INT16 K_FindBotController(mobj_t *mo)
{
msecnode_t *node;
ffloor_t *rover;
INT16 lineNum = -1;
mtag_t tag;
I_Assert(mo != NULL);
I_Assert(!P_MobjWasRemoved(mo));
for (node = mo->touching_sectorlist; node; node = node->m_sectorlist_next)
{
if (!node->m_sector)
{
continue;
}
tag = Tag_FGet(&node->m_sector->tags);
lineNum = P_FindSpecialLineFromTag(2004, tag, -1); // todo: needs to not use P_FindSpecialLineFromTag
if (lineNum != -1)
{
return lineNum;
}
for (rover = node->m_sector->ffloors; rover; rover = rover->next)
{
sector_t *rs = NULL;
if (!(rover->flags & FF_EXISTS))
{
continue;
}
if (mo->z > *rover->topheight || mo->z + mo->height < *rover->bottomheight)
{
continue;
}
rs = &sectors[rover->secnum];
tag = Tag_FGet(&rs->tags);
lineNum = P_FindSpecialLineFromTag(2004, tag, -1);
if (lineNum != -1)
{
return lineNum;
}
}
}
return -1;
}
/*--------------------------------------------------
static void K_DrawPredictionDebug(botprediction_t *predict, player_t *player)
@ -925,6 +966,50 @@ static void K_DrawPredictionDebug(botprediction_t *predict, player_t *player)
}
}
/*--------------------------------------------------
static void K_BotTrick(player_t *player, ticcmd_t *cmd, line_t *botController)
Determines inputs for trick panels.
Input Arguments:-
player - Player to generate the ticcmd for.
cmd - The player's ticcmd to modify.
botController - Linedef for the bot controller.
Return:-
None
--------------------------------------------------*/
static void K_BotTrick(player_t *player, ticcmd_t *cmd, line_t *botController)
{
// Trick panel state -- do nothing until a controller line is found, in which case do a trick.
if (botController == NULL)
{
return;
}
if (player->trickpanel == 1)
{
INT32 type = (sides[botController->sidenum[0]].rowoffset / FRACUNIT);
// Y Offset: Trick type
switch (type)
{
case 1:
cmd->turning = KART_FULLTURN;
break;
case 2:
cmd->turning = -KART_FULLTURN;
break;
case 3:
cmd->buttons |= BT_FORWARD;
break;
case 4:
cmd->buttons |= BT_BACKWARD;
break;
}
}
}
/*--------------------------------------------------
void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd)
@ -936,7 +1021,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd)
boolean trySpindash = true;
UINT8 spindash = 0;
INT32 turnamt = 0;
INT16 botController = -1;
line_t *botController = NULL;
// Can't build a ticcmd if we aren't spawned...
if (!player->mo)
@ -959,7 +1044,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd)
}
// Complete override of all ticcmd functionality
if (LUAh_BotTiccmd(player, cmd))
if (LUAh_BotTiccmd(player, cmd) == true)
{
return;
}
@ -968,30 +1053,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd)
if (player->trickpanel != 0)
{
// Trick panel state -- do nothing until a controller line is found, in which case do a trick.
if (player->trickpanel == 1 && botController != -1)
{
line_t *controllerLine = &lines[botController];
INT32 type = (sides[controllerLine->sidenum[0]].rowoffset / FRACUNIT);
// Y Offset: Trick type
switch (type)
{
case 1:
cmd->turning = KART_FULLTURN;
break;
case 2:
cmd->turning = -KART_FULLTURN;
break;
case 3:
cmd->buttons |= BT_FORWARD;
break;
case 4:
cmd->buttons |= BT_BACKWARD;
break;
}
}
K_BotTrick(player, cmd, botController);
// Don't do anything else.
return;
@ -1000,20 +1062,19 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd)
if ((player->nextwaypoint != NULL
&& player->nextwaypoint->mobj != NULL
&& !P_MobjWasRemoved(player->nextwaypoint->mobj))
|| (botController != -1))
|| (botController != NULL))
{
// Handle steering towards waypoints!
SINT8 turnsign = 0;
angle_t destangle, moveangle, angle;
INT16 anglediff;
if (botController != -1)
if (botController != NULL && (botController->flags & ML_EFFECT1))
{
const fixed_t dist = (player->mo->radius * 4);
line_t *controllerLine = &lines[botController];
// X Offset: Movement direction
destangle = FixedAngle(sides[controllerLine->sidenum[0]].textureoffset);
destangle = FixedAngle(sides[botController->sidenum[0]].textureoffset);
// Overwritten prediction
predict = Z_Calloc(sizeof(botprediction_t), PU_STATIC, NULL);
@ -1112,18 +1173,6 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd)
// Don't turn at all
turnamt = 0;
}
else
{
// Make minor adjustments
turnamt /= 4;
}
}
if (anglediff > 60)
{
// Actually, don't go too fast...
cmd->forwardmove /= 2;
cmd->buttons |= BT_BRAKE;
}
}
}

View file

@ -15,6 +15,7 @@
#include "k_waypoint.h"
#include "d_player.h"
#include "r_defs.h"
// Maximum value of botvars.difficulty
#define MAXBOTDIFFICULTY 9
@ -185,19 +186,22 @@ UINT8 K_EggboxStealth(fixed_t x, fixed_t y);
/*--------------------------------------------------
fixed_t K_BotReducePrediction(player_t *player);
boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, fixed_t y)
Finds walls nearby the specified player, to create a multiplier
to pull bot predictions back by.
Tells us if a bot will play more careful around
this sector. Checks FOFs in the sector, as well.
Input Arguments:-
player - Player to compare.
player - Player to check against.
sec - Sector to check against.
x - Linedef cross X position, for slopes
y - Linedef cross Y position, for slopes
Return:-
Multiplier in fixed point scale.
true if avoiding this sector, false otherwise.
--------------------------------------------------*/
fixed_t K_BotReducePrediction(player_t *player);
boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, fixed_t y);
/*--------------------------------------------------
@ -219,8 +223,8 @@ void K_NudgePredictionTowardsObjects(botprediction_t *predict, player_t *player)
/*--------------------------------------------------
void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd);
Gives a multiplier for a bot's rubberbanding. Meant to be used for top speed,
acceleration, and handling.
Creates a bot's ticcmd, looking at its surroundings to
try and figure out what it should do.
Input Arguments:-
player - Player to generate the ticcmd for.

View file

@ -86,7 +86,7 @@ static boolean K_BotUseItemNearPlayer(player_t *player, ticcmd_t *cmd, fixed_t r
}
/*--------------------------------------------------
static boolean K_PlayerNearSpot(player_t *player, fixed_t x, fixed_t y, fixed_t radius)
static player_t *K_PlayerNearSpot(player_t *player, fixed_t x, fixed_t y, fixed_t radius)
Looks for players around a specified x/y coordinate.
@ -97,9 +97,9 @@ static boolean K_BotUseItemNearPlayer(player_t *player, ticcmd_t *cmd, fixed_t r
radius - The radius to look for players in.
Return:-
true if a player was found around the coordinate, otherwise false.
The player we found, NULL if nothing was found.
--------------------------------------------------*/
static boolean K_PlayerNearSpot(player_t *player, fixed_t x, fixed_t y, fixed_t radius)
static player_t *K_PlayerNearSpot(player_t *player, fixed_t x, fixed_t y, fixed_t radius)
{
UINT8 i;
@ -129,15 +129,15 @@ static boolean K_PlayerNearSpot(player_t *player, fixed_t x, fixed_t y, fixed_t
if (dist <= radius)
{
return true;
return target;
}
}
return false;
return NULL;
}
/*--------------------------------------------------
static boolean K_PlayerPredictThrow(player_t *player, UINT8 extra)
static player_t *K_PlayerPredictThrow(player_t *player, UINT8 extra)
Looks for players around the predicted coordinates of their thrown item.
@ -146,9 +146,9 @@ static boolean K_PlayerNearSpot(player_t *player, fixed_t x, fixed_t y, fixed_t
extra - Extra throwing distance, for aim forward on mines.
Return:-
true if a player was found around the coordinate, otherwise false.
The player we're trying to throw at, NULL if none was found.
--------------------------------------------------*/
static boolean K_PlayerPredictThrow(player_t *player, UINT8 extra)
static player_t *K_PlayerPredictThrow(player_t *player, UINT8 extra)
{
const fixed_t dist = (30 + (extra * 10)) * player->mo->scale;
const UINT32 airtime = FixedDiv(dist + player->mo->momz, gravity);
@ -159,7 +159,7 @@ static boolean K_PlayerPredictThrow(player_t *player, UINT8 extra)
}
/*--------------------------------------------------
static boolean K_PlayerInCone(player_t *player, UINT16 cone, boolean flip)
static player_t *K_PlayerInCone(player_t *player, UINT16 cone, boolean flip)
Looks for players in the .
@ -172,7 +172,7 @@ static boolean K_PlayerPredictThrow(player_t *player, UINT8 extra)
Return:-
true if a player was found in the cone, otherwise false.
--------------------------------------------------*/
static boolean K_PlayerInCone(player_t *player, fixed_t radius, UINT16 cone, boolean flip)
static player_t *K_PlayerInCone(player_t *player, fixed_t radius, UINT16 cone, boolean flip)
{
UINT8 i;
@ -222,22 +222,96 @@ static boolean K_PlayerInCone(player_t *player, fixed_t radius, UINT16 cone, boo
{
if (ad >= 180-cone)
{
return true;
return target;
}
}
else
{
if (ad <= cone)
{
return true;
return target;
}
}
}
}
return NULL;
}
/*--------------------------------------------------
static boolean K_RivalBotAggression(player_t *bot, player_t *target)
Returns if a bot is a rival & wants to be aggressive to a player.
Input Arguments:-
bot - Bot to check.
target - Who the bot wants to attack.
Return:-
false if not the rival. false if the target is another bot. Otherwise, true.
--------------------------------------------------*/
static boolean K_RivalBotAggression(player_t *bot, player_t *target)
{
if (bot == NULL || target == NULL)
{
// Invalid.
return false;
}
if (bot->bot == false)
{
// lol
return false;
}
if (bot->botvars.rival == false)
{
// Not the rival, we aren't self-aware.
return false;
}
if (target->bot == false)
{
// This bot knows that the real threat is the player.
return true;
}
// Calling them your friends is misleading, but you'll at least spare them.
return false;
}
/*--------------------------------------------------
static void K_ItemConfirmForTarget(player_t *bot, player_t *target, UINT16 amount)
Handles updating item confirm values for offense items.
Input Arguments:-
bot - Bot to check.
target - Who the bot wants to attack.
amount - Amount to increase item confirm time by.
Return:-
None
--------------------------------------------------*/
static void K_ItemConfirmForTarget(player_t *bot, player_t *target, UINT16 amount)
{
if (bot == NULL || target == NULL)
{
return;
}
if (K_RivalBotAggression(bot, target) == true)
{
// Double the rate when you're aggressive.
bot->botvars.itemconfirm += amount << 1;
}
else
{
// Do as normal.
bot->botvars.itemconfirm += amount;
}
}
/*--------------------------------------------------
static boolean K_BotGenericPressItem(player_t *player, ticcmd_t *cmd, SINT8 dir)
@ -316,21 +390,21 @@ static boolean K_BotRevealsGenericTrap(player_t *player, INT16 turnamt, boolean
}
// Check the predicted throws.
if (K_PlayerPredictThrow(player, 0))
if (K_PlayerPredictThrow(player, 0) != NULL)
{
return true;
}
if (mine)
{
if (K_PlayerPredictThrow(player, 1))
if (K_PlayerPredictThrow(player, 1) != NULL)
{
return true;
}
}
// Check your behind.
if (K_PlayerInCone(player, player->mo->radius * 16, 10, true))
if (K_PlayerInCone(player, player->mo->radius * 16, 10, true) != NULL)
{
return true;
}
@ -447,7 +521,6 @@ static void K_BotItemRocketSneaker(player_t *player, ticcmd_t *cmd)
}
}
/*--------------------------------------------------
static void K_BotItemBanana(player_t *player, ticcmd_t *cmd, INT16 turnamt)
@ -464,9 +537,17 @@ static void K_BotItemRocketSneaker(player_t *player, ticcmd_t *cmd)
static void K_BotItemBanana(player_t *player, ticcmd_t *cmd, INT16 turnamt)
{
SINT8 throwdir = -1;
player_t *target = NULL;
player->botvars.itemconfirm++;
target = K_PlayerInCone(player, player->mo->radius * 16, 10, true);
if (target != NULL)
{
K_ItemConfirmForTarget(player, target, player->botvars.difficulty);
throwdir = -1;
}
if (abs(turnamt) >= KART_FULLTURN/2)
{
player->botvars.itemconfirm += player->botvars.difficulty / 2;
@ -474,19 +555,15 @@ static void K_BotItemBanana(player_t *player, ticcmd_t *cmd, INT16 turnamt)
}
else
{
if (K_PlayerPredictThrow(player, 0))
target = K_PlayerPredictThrow(player, 0);
if (target != NULL)
{
player->botvars.itemconfirm += player->botvars.difficulty * 2;
K_ItemConfirmForTarget(player, target, player->botvars.difficulty * 2);
throwdir = 1;
}
}
if (K_PlayerInCone(player, player->mo->radius * 16, 10, true))
{
player->botvars.itemconfirm += player->botvars.difficulty;
throwdir = -1;
}
if (player->botvars.itemconfirm > 2*TICRATE || player->bananadrag >= TICRATE)
{
K_BotGenericPressItem(player, cmd, throwdir);
@ -509,12 +586,14 @@ static void K_BotItemBanana(player_t *player, ticcmd_t *cmd, INT16 turnamt)
static void K_BotItemMine(player_t *player, ticcmd_t *cmd, INT16 turnamt)
{
SINT8 throwdir = 0;
player_t *target = NULL;
player->botvars.itemconfirm++;
if (K_PlayerInCone(player, player->mo->radius * 16, 10, true))
target = K_PlayerInCone(player, player->mo->radius * 16, 10, true);
if (target != NULL)
{
player->botvars.itemconfirm += player->botvars.difficulty;
K_ItemConfirmForTarget(player, target, player->botvars.difficulty);
throwdir = -1;
}
@ -525,27 +604,63 @@ static void K_BotItemMine(player_t *player, ticcmd_t *cmd, INT16 turnamt)
}
else
{
if (K_PlayerPredictThrow(player, 0))
target = K_PlayerPredictThrow(player, 0);
if (target != NULL)
{
player->botvars.itemconfirm += player->botvars.difficulty * 2;
K_ItemConfirmForTarget(player, target, player->botvars.difficulty * 2);
throwdir = 0;
}
if (K_PlayerPredictThrow(player, 1))
target = K_PlayerPredictThrow(player, 1);
if (target != NULL)
{
player->botvars.itemconfirm += player->botvars.difficulty * 2;
K_ItemConfirmForTarget(player, target, player->botvars.difficulty * 2);
throwdir = 1;
}
}
if (player->botvars.itemconfirm > 2*TICRATE || player->bananadrag >= TICRATE)
{
K_BotGenericPressItem(player, cmd, throwdir);
}
}
/*--------------------------------------------------
static void K_BotItemLandmine(player_t *player, ticcmd_t *cmd, INT16 turnamt)
Item usage for landmine tossing.
Input Arguments:-
player - Bot to do this for.
cmd - Bot's ticcmd to edit.
turnamt - How hard they currently are turning.
Return:-
None
--------------------------------------------------*/
static void K_BotItemLandmine(player_t *player, ticcmd_t *cmd, INT16 turnamt)
{
player_t *target = NULL;
player->botvars.itemconfirm++;
if (abs(turnamt) >= KART_FULLTURN/2)
{
player->botvars.itemconfirm += player->botvars.difficulty / 2;
}
target = K_PlayerInCone(player, player->mo->radius * 16, 10, true);
if (target != NULL)
{
K_ItemConfirmForTarget(player, target, player->botvars.difficulty);
}
if (player->botvars.itemconfirm > 2*TICRATE)
{
K_BotGenericPressItem(player, cmd, -1);
}
}
/*--------------------------------------------------
static void K_BotItemEggman(player_t *player, ticcmd_t *cmd)
@ -562,18 +677,21 @@ static void K_BotItemEggman(player_t *player, ticcmd_t *cmd)
{
const UINT8 stealth = K_EggboxStealth(player->mo->x, player->mo->y);
SINT8 throwdir = -1;
player_t *target = NULL;
player->botvars.itemconfirm++;
if (K_PlayerPredictThrow(player, 0))
target = K_PlayerPredictThrow(player, 0);
if (target != NULL)
{
player->botvars.itemconfirm += player->botvars.difficulty / 2;
K_ItemConfirmForTarget(player, target, player->botvars.difficulty / 2);
throwdir = 1;
}
if (K_PlayerInCone(player, player->mo->radius * 16, 10, true))
target = K_PlayerInCone(player, player->mo->radius * 16, 10, true);
if (target != NULL)
{
player->botvars.itemconfirm += player->botvars.difficulty;
K_ItemConfirmForTarget(player, target, player->botvars.difficulty);
throwdir = -1;
}
@ -603,6 +721,7 @@ static void K_BotItemEggman(player_t *player, ticcmd_t *cmd)
static boolean K_BotRevealsEggbox(player_t *player)
{
const UINT8 stealth = K_EggboxStealth(player->mo->x, player->mo->y);
player_t *target = NULL;
// This is a stealthy spot for an eggbox, lets reveal it!
if (stealth > 1)
@ -611,13 +730,15 @@ static boolean K_BotRevealsEggbox(player_t *player)
}
// Check the predicted throws.
if (K_PlayerPredictThrow(player, 0))
target = K_PlayerPredictThrow(player, 0);
if (target != NULL)
{
return true;
}
// Check your behind.
if (K_PlayerInCone(player, player->mo->radius * 16, 10, true))
target = K_PlayerInCone(player, player->mo->radius * 16, 10, true);
if (target != NULL)
{
return true;
}
@ -644,7 +765,7 @@ static void K_BotItemEggmanShield(player_t *player, ticcmd_t *cmd)
return;
}
if (K_BotRevealsEggbox(player) || (player->botvars.itemconfirm++ > 20*TICRATE))
if (K_BotRevealsEggbox(player) == true || (player->botvars.itemconfirm++ > 20*TICRATE))
{
K_BotGenericPressItem(player, cmd, 0);
}
@ -666,8 +787,9 @@ static void K_BotItemEggmanExplosion(player_t *player, ticcmd_t *cmd)
{
if (player->position == 1)
{
// Hey, we aren't gonna find anyone up here...
// why don't we slow down a bit? :)
cmd->forwardmove /= 2;
cmd->buttons |= BT_BRAKE;
}
K_BotUseItemNearPlayer(player, cmd, 128*player->mo->scale);
@ -690,23 +812,32 @@ static void K_BotItemOrbinaut(player_t *player, ticcmd_t *cmd)
const fixed_t topspeed = K_GetKartSpeed(player, false);
fixed_t radius = (player->mo->radius * 32);
SINT8 throwdir = -1;
UINT8 snipeMul = 2;
player_t *target = NULL;
if (player->speed > topspeed)
{
radius = FixedMul(radius, FixedDiv(player->speed, topspeed));
snipeMul = 3; // Confirm faster when you'll throw it with a bunch of extra speed!!
}
player->botvars.itemconfirm++;
if (K_PlayerInCone(player, radius, 10, false))
target = K_PlayerInCone(player, radius, 10, false);
if (target != NULL)
{
player->botvars.itemconfirm += player->botvars.difficulty * 2;
K_ItemConfirmForTarget(player, target, player->botvars.difficulty * snipeMul);
throwdir = 1;
}
else if (K_PlayerInCone(player, radius, 10, true))
{
player->botvars.itemconfirm += player->botvars.difficulty;
throwdir = -1;
target = K_PlayerInCone(player, radius, 10, true);
if (target != NULL)
{
K_ItemConfirmForTarget(player, target, player->botvars.difficulty);
throwdir = -1;
}
}
if (player->botvars.itemconfirm > 5*TICRATE)
@ -732,24 +863,54 @@ static void K_BotItemJawz(player_t *player, ticcmd_t *cmd)
const fixed_t topspeed = K_GetKartSpeed(player, false);
fixed_t radius = (player->mo->radius * 32);
SINT8 throwdir = 1;
UINT8 snipeMul = 2;
INT32 lastTarg = player->lastjawztarget;
player_t *target = NULL;
if (player->speed > topspeed)
{
radius = FixedMul(radius, FixedDiv(player->speed, topspeed));
snipeMul = 3; // Confirm faster when you'll throw it with a bunch of extra speed!!
}
player->botvars.itemconfirm++;
if (K_PlayerInCone(player, radius, 10, true))
target = K_PlayerInCone(player, radius, 10, true);
if (target != NULL)
{
player->botvars.itemconfirm += player->botvars.difficulty;
K_ItemConfirmForTarget(player, target, player->botvars.difficulty);
throwdir = -1;
}
if (player->lastjawztarget != -1)
if (lastTarg != -1
&& playeringame[lastTarg] == true
&& players[lastTarg].spectator == false
&& players[lastTarg].mo != NULL
&& P_MobjWasRemoved(players[lastTarg].mo) == false)
{
player->botvars.itemconfirm += player->botvars.difficulty * 2;
throwdir = 1;
mobj_t *targMo = players[lastTarg].mo;
mobj_t *mobj = NULL, *next = NULL;
boolean targettedAlready = false;
target = &players[lastTarg];
// Make sure no other Jawz are targetting this player.
for (mobj = kitemcap; mobj; mobj = next)
{
next = mobj->itnext;
if (mobj->type == MT_JAWZ && mobj->target == targMo)
{
targettedAlready = true;
break;
}
}
if (targettedAlready == false)
{
K_ItemConfirmForTarget(player, target, player->botvars.difficulty * snipeMul);
throwdir = 1;
}
}
if (player->botvars.itemconfirm > 5*TICRATE)
@ -772,7 +933,7 @@ static void K_BotItemJawz(player_t *player, ticcmd_t *cmd)
--------------------------------------------------*/
static void K_BotItemThunder(player_t *player, ticcmd_t *cmd)
{
if (!K_BotUseItemNearPlayer(player, cmd, 192*player->mo->scale))
if (K_BotUseItemNearPlayer(player, cmd, 192*player->mo->scale) == false)
{
if (player->botvars.itemconfirm > 10*TICRATE)
{
@ -1036,7 +1197,6 @@ void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt)
K_BotItemSneaker(player, cmd);
break;
case KITEM_BANANA:
case KITEM_LANDMINE:
if (!(player->pflags & PF_ITEMOUT))
{
K_BotItemGenericTrapShield(player, cmd, turnamt, false);
@ -1081,6 +1241,9 @@ void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt)
K_BotItemMine(player, cmd, turnamt);
}
break;
case KITEM_LANDMINE:
K_BotItemLandmine(player, cmd, turnamt);
break;
case KITEM_THUNDERSHIELD:
K_BotItemThunder(player, cmd);
break;

View file

@ -122,7 +122,7 @@ UINT8 K_EggboxStealth(fixed_t x, fixed_t y)
}
}
return (globalsmuggle.randomitems * globalsmuggle.eggboxes);
return (globalsmuggle.randomitems * (globalsmuggle.eggboxes + 1));
}
/*--------------------------------------------------
@ -162,21 +162,11 @@ static boolean K_BotHatesThisSectorsSpecial(player_t *player, sector_t *sec)
}
/*--------------------------------------------------
static boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, fixed_t y)
boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, fixed_t y)
Tells us if a bot will play more careful around
this sector. Checks FOFs in the sector, as well.
Input Arguments:-
player - Player to check against.
sec - Sector to check against.
x - Linedef cross X position, for slopes
y - Linedef cross Y position, for slopes
Return:-
true if avoiding this sector, false otherwise.
See header file for description.
--------------------------------------------------*/
static boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, fixed_t y)
boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, fixed_t y)
{
const boolean flip = (player->mo->eflags & MFE_VERTICALFLIP);
INT32 specialflag = 0;
@ -257,171 +247,6 @@ static boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x,
return K_BotHatesThisSectorsSpecial(player, bestsector);
}
/*--------------------------------------------------
static boolean K_FindBlockingWalls(line_t *line)
Blockmap search function.
Reels the bot prediction back in based on solid walls
or other obstacles surrounding the bot.
Input Arguments:-
line - Linedef passed in from iteration.
Return:-
true continues searching, false ends the search early.
--------------------------------------------------*/
static boolean K_FindBlockingWalls(line_t *line)
{
// Condensed version of PIT_CheckLine
const fixed_t maxstepmove = FixedMul(MAXSTEPMOVE, mapobjectscale);
fixed_t maxstep = maxstepmove;
fixed_t linedist = INT32_MAX;
INT32 lineside = 0;
vertex_t pos;
if (!globalsmuggle.botmo || P_MobjWasRemoved(globalsmuggle.botmo) || !globalsmuggle.botmo->player)
{
return false;
}
if (line->polyobj && !(line->polyobj->flags & POF_SOLID))
{
return true;
}
if (tmbbox[BOXRIGHT] <= line->bbox[BOXLEFT] || tmbbox[BOXLEFT] >= line->bbox[BOXRIGHT]
|| tmbbox[BOXTOP] <= line->bbox[BOXBOTTOM] || tmbbox[BOXBOTTOM] >= line->bbox[BOXTOP])
{
return true;
}
if (P_BoxOnLineSide(tmbbox, line) != -1)
{
return true;
}
lineside = P_PointOnLineSide(globalsmuggle.botmo->x, globalsmuggle.botmo->y, line);
// one sided line
if (!line->backsector)
{
if (lineside)
{
// don't hit the back side
return true;
}
goto blocked;
}
if ((line->flags & ML_IMPASSABLE) || (line->flags & ML_BLOCKPLAYERS))
{
goto blocked;
}
// set openrange, opentop, openbottom
P_LineOpening(line, globalsmuggle.botmo);
if (globalsmuggle.botmo->player->waterskip)
maxstep += maxstepmove;
if (P_MobjTouchingSectorSpecial(globalsmuggle.botmo, 1, 13, false))
maxstep <<= 1;
else if (P_MobjTouchingSectorSpecial(globalsmuggle.botmo, 1, 12, false))
maxstep = 0;
if ((openrange < globalsmuggle.botmo->height) // doesn't fit
|| (opentop - globalsmuggle.botmo->z < globalsmuggle.botmo->height) // mobj is too high
|| (openbottom - globalsmuggle.botmo->z > maxstep)) // too big a step up
{
goto blocked;
}
// Treat damage sectors like walls
P_ClosestPointOnLine(globalsmuggle.botmo->x, globalsmuggle.botmo->y, line, &pos);
if (lineside)
{
if (K_BotHatesThisSector(globalsmuggle.botmo->player, line->frontsector, pos.x, pos.y))
goto blocked;
}
else
{
if (K_BotHatesThisSector(globalsmuggle.botmo->player, line->backsector, pos.x, pos.y))
goto blocked;
}
// We weren't blocked!
return true;
blocked:
linedist = K_DistanceOfLineFromPoint(line->v1->x, line->v1->y, line->v2->x, line->v2->y, globalsmuggle.botmo->x, globalsmuggle.botmo->y);
linedist -= (globalsmuggle.botmo->radius * 8); // Maintain a reasonable distance away from it
if (linedist > globalsmuggle.distancetocheck)
{
return true;
}
if (linedist <= 0)
{
globalsmuggle.closestlinedist = 0;
return false;
}
if (linedist < globalsmuggle.closestlinedist)
{
globalsmuggle.closestlinedist = linedist;
}
return true;
}
/*--------------------------------------------------
fixed_t K_BotReducePrediction(player_t *player)
See header file for description.
--------------------------------------------------*/
fixed_t K_BotReducePrediction(player_t *player)
{
INT32 xl, xh, yl, yh, bx, by;
globalsmuggle.botmo = player->mo;
globalsmuggle.distancetocheck = (player->mo->radius * 32);
globalsmuggle.closestlinedist = INT32_MAX;
tmx = player->mo->x;
tmy = player->mo->y;
xl = (unsigned)(tmx - globalsmuggle.distancetocheck - bmaporgx)>>MAPBLOCKSHIFT;
xh = (unsigned)(tmx + globalsmuggle.distancetocheck - bmaporgx)>>MAPBLOCKSHIFT;
yl = (unsigned)(tmy - globalsmuggle.distancetocheck - bmaporgy)>>MAPBLOCKSHIFT;
yh = (unsigned)(tmy + globalsmuggle.distancetocheck - bmaporgy)>>MAPBLOCKSHIFT;
BMBOUNDFIX(xl, xh, yl, yh);
tmbbox[BOXTOP] = tmy + globalsmuggle.distancetocheck;
tmbbox[BOXBOTTOM] = tmy - globalsmuggle.distancetocheck;
tmbbox[BOXRIGHT] = tmx + globalsmuggle.distancetocheck;
tmbbox[BOXLEFT] = tmx - globalsmuggle.distancetocheck;
// Check for lines that the bot might collide with
for (bx = xl; bx <= xh; bx++)
{
for (by = yl; by <= yh; by++)
{
P_BlockLinesIterator(bx, by, K_FindBlockingWalls);
}
}
if (globalsmuggle.closestlinedist == INT32_MAX)
{
return FRACUNIT;
}
return (FRACUNIT/2) + (FixedDiv(globalsmuggle.closestlinedist, globalsmuggle.distancetocheck) / 2);
}
/*--------------------------------------------------
static void K_AddAttackObject(mobj_t *thing, UINT8 side, UINT8 weight)

View file

@ -2682,7 +2682,7 @@ boolean K_ApplyOffroad(player_t *player)
boolean K_SlopeResistance(player_t *player)
{
if (player->invincibilitytimer || player->sneakertimer || player->tiregrease)
if (player->invincibilitytimer || player->sneakertimer || player->tiregrease || player->flamedash)
return true;
return false;
}
@ -8634,8 +8634,6 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
boolean HOLDING_ITEM = (player->pflags & (PF_ITEMOUT|PF_EGGMANOUT));
boolean NO_HYUDORO = (player->stealingtimer == 0);
player->pflags &= ~PF_HITFINISHLINE;
if (!player->exiting)
{
if (player->oldposition < player->position) // But first, if you lost a place,

View file

@ -13254,6 +13254,8 @@ void A_ChangeHeight(mobj_t *actor)
void A_ItemPop(mobj_t *actor)
{
INT32 locvar1 = var1;
mobj_t *remains;
mobjtype_t explode;
@ -13308,7 +13310,9 @@ void A_ItemPop(mobj_t *actor)
if (actor->info->deathsound)
S_StartSound(remains, actor->info->deathsound);
if (!((gametyperules & GTR_BUMPERS) && actor->target->player->bumpers <= 0))
if (locvar1 == 1)
P_GivePlayerSpheres(actor->target->player, actor->extravalue1);
else if (locvar1 == 0)
actor->target->player->itemroulette = 1;
remains->flags2 &= ~MF2_AMBUSH;
@ -14143,7 +14147,6 @@ void A_LandMineExplode(mobj_t *actor)
INT32 colour = SKINCOLOR_KETCHUP; // we spell words properly here
INT32 i;
mobj_t *smoldering;
mobj_t *dust;
if (LUA_CallAction(A_LANDMINEEXPLODE, actor))
return;
@ -14161,18 +14164,6 @@ void A_LandMineExplode(mobj_t *actor)
P_SetScale(smoldering, actor->scale);
smoldering->tics = TICRATE*3;
// Spawn a ring:
for (i = 0; i < 32; i++)
{
dust = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SMOKE);
P_SetMobjState(dust, S_OPAQUESMOKE1);
dust->angle = (ANGLE_180/16) * i;
P_SetScale(dust, actor->scale);
dust->destscale = actor->scale*4;
dust->scalespeed = actor->scale/4;
P_InstaThrust(dust, dust->angle, FixedMul(20*FRACUNIT, actor->scale));
}
actor->fuse = actor->tics; // disappear when this state ends.
// spawn a few physics explosions

View file

@ -268,12 +268,14 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
if (!P_CanPickupItem(player, 1))
return;
if ((gametyperules & GTR_BUMPERS) && player->bumpers <= 0)
{
return;
}
special->momx = special->momy = special->momz = 0;
P_SetTarget(&special->target, toucher);
P_KillMobj(special, toucher, toucher, DMG_NORMAL);
break;
case MT_SPHEREBOX:
if (player->bumpers <= 0)
return;
P_SetTarget(&special->target, toucher);
P_KillMobj(special, toucher, toucher, DMG_NORMAL);
break;
@ -510,16 +512,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
if (!(P_CanPickupItem(player, 0)))
return;
// Reached the cap, don't waste 'em!
if (player->spheres >= 40)
return;
// Not alive
if ((gametyperules & GTR_BUMPERS) && (player->bumpers <= 0))
return;
special->momx = special->momy = special->momz = 0;
player->spheres++;
P_GivePlayerSpheres(player, 1);
break;
// Secret emblem thingy

View file

@ -175,7 +175,7 @@ void P_RestoreMusic(player_t *player);
boolean P_EndingMusic(player_t *player);
mobj_t *P_SpawnGhostMobj(mobj_t *mobj);
INT32 P_GivePlayerRings(player_t *player, INT32 num_rings);
void P_GivePlayerSpheres(player_t *player, INT32 num_spheres);
INT32 P_GivePlayerSpheres(player_t *player, INT32 num_spheres);
void P_GivePlayerLives(player_t *player, INT32 numlives);
UINT8 P_GetNextEmerald(void);
void P_GiveEmerald(boolean spawnObj);
@ -404,6 +404,8 @@ boolean P_IsLineBlocking(const line_t *ld, const mobj_t *thing);
boolean P_IsLineTripWire(const line_t *ld);
boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y);
boolean P_CheckCameraPosition(fixed_t x, fixed_t y, camera_t *thiscam);
fixed_t P_BaseStepUp(void);
fixed_t P_GetThingStepUp(mobj_t *thing);
boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff);
boolean P_Move(mobj_t *actor, fixed_t speed);
boolean P_SetOrigin(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z);
@ -413,6 +415,7 @@ void P_BouncePlayerMove(mobj_t *mo);
void P_BounceMove(mobj_t *mo);
boolean P_CheckSight(mobj_t *t1, mobj_t *t2);
boolean P_TraceBlockingLines(mobj_t *t1, mobj_t *t2);
boolean P_TraceBotTraversal(mobj_t *t1, mobj_t *t2);
void P_CheckHoopPosition(mobj_t *hoopthing, fixed_t x, fixed_t y, fixed_t z, fixed_t radius);
boolean P_CheckSector(sector_t *sector, boolean crunch);

View file

@ -2431,6 +2431,42 @@ static boolean P_WaterStepUp(mobj_t *thing)
P_WaterRunning(thing);
}
fixed_t P_BaseStepUp(void)
{
return FixedMul(MAXSTEPMOVE, mapobjectscale);
}
fixed_t P_GetThingStepUp(mobj_t *thing)
{
const fixed_t maxstepmove = P_BaseStepUp();
fixed_t maxstep = maxstepmove;
if (thing->type == MT_SKIM)
{
// Skim special (not needed for kart?)
return 0;
}
if (P_WaterStepUp(thing) == true)
{
// Add some extra stepmove when waterskipping
maxstep += maxstepmove;
}
if (P_MobjTouchingSectorSpecial(thing, 1, 13, false))
{
// If using type Section1:13, double the maxstep.
maxstep <<= 1;
}
else if (P_MobjTouchingSectorSpecial(thing, 1, 12, false))
{
// If using type Section1:12, no maxstep. For short walls, like Egg Zeppelin
maxstep = 0;
}
return maxstep;
}
//
// P_TryMove
// Attempt to move to a new position.
@ -2492,21 +2528,7 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
if (!(thing->flags & MF_NOCLIP))
{
//All things are affected by their scale.
const fixed_t maxstepmove = FixedMul(MAXSTEPMOVE, mapobjectscale);
fixed_t maxstep = maxstepmove;
if (thing->player && P_WaterStepUp(thing))
maxstep += maxstepmove; // Add some extra stepmove when waterskipping
// If using type Section1:13, double the maxstep.
if (P_MobjTouchingSectorSpecial(thing, 1, 13, false))
maxstep <<= 1;
// If using type Section1:12, no maxstep. For short walls, like Egg Zeppelin
else if (P_MobjTouchingSectorSpecial(thing, 1, 12, false))
maxstep = 0;
if (thing->type == MT_SKIM)
maxstep = 0;
fixed_t maxstep = P_GetThingStepUp(thing);
if (tmceilingz - tmfloorz < thing->height)
{
@ -2743,7 +2765,7 @@ boolean P_SceneryTryMove(mobj_t *thing, fixed_t x, fixed_t y)
if (!(thing->flags & MF_NOCLIP))
{
const fixed_t maxstep = FixedMul(MAXSTEPMOVE, mapobjectscale);
const fixed_t maxstep = P_BaseStepUp();
if (tmceilingz - tmfloorz < thing->height)
return false; // doesn't fit
@ -3205,7 +3227,7 @@ static boolean PTR_LineIsBlocking(line_t *li)
if (opentop - slidemo->z < slidemo->height)
return true; // mobj is too high
if (openbottom - slidemo->z > FixedMul(MAXSTEPMOVE, mapobjectscale))
if (openbottom - slidemo->z > P_GetThingStepUp(slidemo))
return true; // too big a step up
return false;

View file

@ -3059,14 +3059,76 @@ void P_MobjCheckWater(mobj_t *mobj)
return;
// The rest of this code only executes on a water state change.
if (waterwasnotset || !!(mobj->eflags & MFE_UNDERWATER) == wasinwater)
if (!!(mobj->eflags & MFE_UNDERWATER) == wasinwater)
return;
if (p && !p->waterskip &&
p->curshield != KSHIELD_BUBBLE && wasinwater)
{
S_StartSound(mobj, sfx_s3k38);
}
if ((p) // Players
|| (mobj->flags & MF_PUSHABLE) // Pushables
|| ((mobj->info->flags & MF_PUSHABLE) && mobj->fuse) // Previously pushable, might be moving still
)
{
// Time to spawn the bubbles!
{
INT32 i;
INT32 bubblecount;
UINT8 prandom[4];
mobj_t *bubble;
mobjtype_t bubbletype;
if (mobj->eflags & MFE_GOOWATER || wasingoo)
S_StartSound(mobj, sfx_ghit);
else if (mobj->eflags & MFE_TOUCHLAVA)
S_StartSound(mobj, sfx_splash);
else
S_StartSound(mobj, sfx_splish); // And make a sound!
bubblecount = FixedDiv(abs(mobj->momz), mobj->scale)>>(FRACBITS-1);
// Max bubble count
if (bubblecount > 128)
bubblecount = 128;
// Create tons of bubbles
for (i = 0; i < bubblecount; i++)
{
// P_RandomByte()s are called individually to allow consistency
// across various compilers, since the order of function calls
// in C is not part of the ANSI specification.
prandom[0] = P_RandomByte();
prandom[1] = P_RandomByte();
prandom[2] = P_RandomByte();
prandom[3] = P_RandomByte();
bubbletype = MT_SMALLBUBBLE;
if (!(prandom[0] & 0x3)) // medium bubble chance up to 64 from 32
bubbletype = MT_MEDIUMBUBBLE;
bubble = P_SpawnMobj(
mobj->x + FixedMul((prandom[1]<<(FRACBITS-3)) * (prandom[0]&0x80 ? 1 : -1), mobj->scale),
mobj->y + FixedMul((prandom[2]<<(FRACBITS-3)) * (prandom[0]&0x40 ? 1 : -1), mobj->scale),
mobj->z + FixedMul((prandom[3]<<(FRACBITS-2)), mobj->scale), bubbletype);
if (bubble)
{
if (P_MobjFlip(mobj)*mobj->momz < 0)
bubble->momz = mobj->momz >> 4;
else
bubble->momz = 0;
bubble->destscale = mobj->scale;
P_SetScale(bubble, mobj->scale);
}
}
}
if (waterwasnotset)
return;
// Check to make sure you didn't just cross into a sector to jump out of
// that has shallower water than the block you were originally in.
if ((!(mobj->eflags & MFE_VERTICALFLIP) && mobj->watertop-mobj->floorz <= height>>1)
@ -3151,59 +3213,6 @@ void P_MobjCheckWater(mobj_t *mobj)
P_SetScale(splish, mobj->scale);
}
}
// Time to spawn the bubbles!
{
INT32 i;
INT32 bubblecount;
UINT8 prandom[4];
mobj_t *bubble;
mobjtype_t bubbletype;
if (mobj->eflags & MFE_GOOWATER || wasingoo)
S_StartSound(mobj, sfx_ghit);
else if (mobj->eflags & MFE_TOUCHLAVA)
S_StartSound(mobj, sfx_splash);
else
S_StartSound(mobj, sfx_splish); // And make a sound!
bubblecount = FixedDiv(abs(mobj->momz), mobj->scale)>>(FRACBITS-1);
// Max bubble count
if (bubblecount > 128)
bubblecount = 128;
// Create tons of bubbles
for (i = 0; i < bubblecount; i++)
{
// P_RandomByte()s are called individually to allow consistency
// across various compilers, since the order of function calls
// in C is not part of the ANSI specification.
prandom[0] = P_RandomByte();
prandom[1] = P_RandomByte();
prandom[2] = P_RandomByte();
prandom[3] = P_RandomByte();
bubbletype = MT_SMALLBUBBLE;
if (!(prandom[0] & 0x3)) // medium bubble chance up to 64 from 32
bubbletype = MT_MEDIUMBUBBLE;
bubble = P_SpawnMobj(
mobj->x + FixedMul((prandom[1]<<(FRACBITS-3)) * (prandom[0]&0x80 ? 1 : -1), mobj->scale),
mobj->y + FixedMul((prandom[2]<<(FRACBITS-3)) * (prandom[0]&0x40 ? 1 : -1), mobj->scale),
mobj->z + FixedMul((prandom[3]<<(FRACBITS-2)), mobj->scale), bubbletype);
if (bubble)
{
if (P_MobjFlip(mobj)*mobj->momz < 0)
bubble->momz = mobj->momz >> 4;
else
bubble->momz = 0;
bubble->destscale = mobj->scale;
P_SetScale(bubble, mobj->scale);
}
}
}
}
}
@ -4095,7 +4104,7 @@ boolean P_BossTargetPlayer(mobj_t *actor, boolean closest)
player = &players[actor->lastlook];
if (player->bot || player->spectator)
if (player->spectator)
continue; // ignore notarget
if (!player->mo || P_MobjWasRemoved(player->mo))
@ -8597,6 +8606,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
}
break;
case MT_RANDOMITEM:
case MT_SPHEREBOX:
if (gametype == GT_BATTLE && mobj->threshold == 70)
{
mobj->color = K_RainbowColor(leveltime);
@ -9377,6 +9387,7 @@ static void P_DefaultMobjShadowScale(mobj_t *thing)
thing->shadowscale = 12*FRACUNIT/5;
break;
case MT_RANDOMITEM:
case MT_SPHEREBOX:
thing->shadowscale = FRACUNIT/2;
thing->whiteshadow = false;
break;

View file

@ -18,6 +18,9 @@
#include "r_main.h"
#include "r_state.h"
#include "k_bot.h" // K_BotHatesThisSector
#include "k_kart.h" // K_TripwirePass
//
// P_CheckSight
//
@ -572,7 +575,7 @@ static boolean P_CrossBlockingSubsector(size_t num, register traceblocking_t *tb
if (P_IsLineBlocking(line, tb->compareThing) == true)
{
// This line will block us
// This line will always block us
return false;
}
}
@ -656,3 +659,192 @@ boolean P_TraceBlockingLines(mobj_t *t1, mobj_t *t2)
// the head node is the last node output
return P_CrossBSPNodeBlocking((INT32)numnodes - 1, &tb);
}
//
// ANOTHER version, this time for bot traversal.
// (TODO: since we have so many versions of this function, the differences
// should maybe just be a function var that gets called?)
//
static boolean P_CrossBotTraversalSubsector(size_t num, register traceblocking_t *tb)
{
seg_t *seg;
INT32 count;
#ifdef RANGECHECK
if (num >= numsubsectors)
I_Error("P_CrossBotTraversalSubsector: ss %s with numss = %s\n", sizeu1(num), sizeu2(numsubsectors));
#endif
// haleyjd 02/23/06: this assignment should be after the above check
seg = segs + subsectors[num].firstline;
for (count = subsectors[num].numlines; --count >= 0; seg++) // check lines
{
line_t *line = seg->linedef;
divline_t divl;
const vertex_t *v1,*v2;
fixed_t maxstep = INT32_MAX;
if (seg->glseg)
continue;
// already checked other side?
if (line->validcount == validcount)
continue;
line->validcount = validcount;
// OPTIMIZE: killough 4/20/98: Added quick bounding-box rejection test
if (line->bbox[BOXLEFT ] > tb->bbox[BOXRIGHT ] ||
line->bbox[BOXRIGHT ] < tb->bbox[BOXLEFT ] ||
line->bbox[BOXBOTTOM] > tb->bbox[BOXTOP ] ||
line->bbox[BOXTOP] < tb->bbox[BOXBOTTOM])
continue;
v1 = line->v1;
v2 = line->v2;
// line isn't crossed?
if (P_DivlineSide(v1->x, v1->y, &tb->strace) ==
P_DivlineSide(v2->x, v2->y, &tb->strace))
continue;
// stop because it is not two sided anyway
if (!(line->flags & ML_TWOSIDED))
return false;
divl.dx = v2->x - (divl.x = v1->x);
divl.dy = v2->y - (divl.y = v1->y);
// line isn't crossed?
if (P_DivlineSide(tb->strace.x, tb->strace.y, &divl) ==
P_DivlineSide(tb->t2x, tb->t2y, &divl))
continue;
if (P_IsLineBlocking(line, tb->compareThing) == true)
{
// This line will always block us
return false;
}
// set openrange, opentop, openbottom
P_LineOpening(line, tb->compareThing);
maxstep = P_GetThingStepUp(tb->compareThing);
if ((openrange < tb->compareThing->height) // doesn't fit
|| (opentop - tb->compareThing->z < tb->compareThing->height) // mobj is too high
|| (openbottom - tb->compareThing->z > maxstep)) // too big a step up
{
// This line situationally blocks us
return false;
}
// Treat damage sectors like walls
if (tb->compareThing->player != NULL)
{
boolean alreadyHates = K_BotHatesThisSector(tb->compareThing->player, tb->compareThing->subsector->sector, tb->compareThing->x, tb->compareThing->y);
if (alreadyHates == false)
{
INT32 lineside = 0;
vertex_t pos;
P_ClosestPointOnLine(tb->compareThing->x, tb->compareThing->y, line, &pos);
lineside = P_PointOnLineSide(tb->compareThing->x, tb->compareThing->y, line);
if (K_BotHatesThisSector(tb->compareThing->player, ((lineside == 1) ? line->frontsector : line->backsector), pos.x, pos.y))
{
// This line does not block us, but we don't want to be in it.
return false;
}
}
if (P_IsLineTripWire(line) == true && K_TripwirePass(tb->compareThing->player) == false)
{
// Can't go through trip wire.
return false;
}
}
}
// passed the subsector ok
return true;
}
static boolean P_CrossBSPNodeBotTraversal(INT32 bspnum, register traceblocking_t *tb)
{
while (!(bspnum & NF_SUBSECTOR))
{
register node_t *bsp = nodes + bspnum;
INT32 side = P_DivlineSide(tb->strace.x,tb->strace.y,(divline_t *)bsp)&1;
if (side == P_DivlineSide(tb->t2x, tb->t2y, (divline_t *) bsp))
bspnum = bsp->children[side]; // doesn't touch the other side
else // the partition plane is crossed here
{
if (!P_CrossBSPNodeBotTraversal(bsp->children[side], tb))
return false; // cross the starting side
else
bspnum = bsp->children[side^1]; // cross the ending side
}
}
return P_CrossBotTraversalSubsector((bspnum == -1 ? 0 : bspnum & ~NF_SUBSECTOR), tb);
}
boolean P_TraceBotTraversal(mobj_t *t1, mobj_t *t2)
{
const sector_t *s1, *s2;
size_t pnum;
traceblocking_t tb;
// First check for trivial rejection.
if (!t1 || !t2)
return false;
I_Assert(!P_MobjWasRemoved(t1));
I_Assert(!P_MobjWasRemoved(t2));
if (!t1->subsector || !t2->subsector
|| !t1->subsector->sector || !t2->subsector->sector)
return false;
s1 = t1->subsector->sector;
s2 = t2->subsector->sector;
pnum = (s1-sectors)*numsectors + (s2-sectors);
if (rejectmatrix != NULL)
{
// Check in REJECT table.
if (rejectmatrix[pnum>>3] & (1 << (pnum&7))) // can't possibly be connected
return false;
}
// killough 11/98: shortcut for melee situations
// same subsector? obviously visible
// haleyjd 02/23/06: can't do this if there are polyobjects in the subsec
if (!t1->subsector->polyList &&
t1->subsector == t2->subsector)
return true;
validcount++;
tb.strace.dx = (tb.t2x = t2->x) - (tb.strace.x = t1->x);
tb.strace.dy = (tb.t2y = t2->y) - (tb.strace.y = t1->y);
if (t1->x > t2->x)
tb.bbox[BOXRIGHT] = t1->x, tb.bbox[BOXLEFT] = t2->x;
else
tb.bbox[BOXRIGHT] = t2->x, tb.bbox[BOXLEFT] = t1->x;
if (t1->y > t2->y)
tb.bbox[BOXTOP] = t1->y, tb.bbox[BOXBOTTOM] = t2->y;
else
tb.bbox[BOXTOP] = t2->y, tb.bbox[BOXBOTTOM] = t1->y;
tb.compareThing = t1;
// the head node is the last node output
return P_CrossBSPNodeBotTraversal((INT32)numnodes - 1, &tb);
}

View file

@ -500,6 +500,26 @@ INT32 P_GivePlayerRings(player_t *player, INT32 num_rings)
return num_rings;
}
INT32 P_GivePlayerSpheres(player_t *player, INT32 num_spheres)
{
num_spheres += player->spheres;
// Not alive
if ((gametyperules & GTR_BUMPERS) && (player->bumpers <= 0))
return 0;
if (num_spheres > 40) // Reached the cap, don't waste 'em!
num_spheres = 40;
else if (num_spheres < 0)
num_spheres = 0;
num_spheres -= player->spheres;
player->spheres += num_spheres;
return num_spheres;
}
//
// P_GivePlayerLives
//
@ -2502,6 +2522,8 @@ static void P_ConsiderAllGone(void)
//
static void P_DeathThink(player_t *player)
{
boolean playerGone = false;
player->deltaviewheight = 0;
if (player->deadtimer < INT32_MAX)
@ -2522,7 +2544,19 @@ static void P_DeathThink(player_t *player)
K_KartPlayerHUDUpdate(player);
if (player->lives > 0 && !(player->pflags & PF_NOCONTEST) && player->deadtimer > TICRATE)
if (player->pflags & PF_NOCONTEST)
{
playerGone = true;
}
else if (player->bot == false)
{
if (G_GametypeUsesLives() == true && player->lives == 0)
{
playerGone = true;
}
}
if (playerGone == false && player->deadtimer > TICRATE)
{
player->playerstate = PST_REBORN;
}
@ -4222,9 +4256,6 @@ void P_PlayerThink(player_t *player)
ticcmd_t *cmd;
const size_t playeri = (size_t)(player - players);
player->old_drawangle = player->drawangle;
player->old_viewrollangle = player->viewrollangle;
#ifdef PARANOIA
if (!player->mo)
I_Error("p_playerthink: players[%s].mo == NULL", sizeu1(playeri));
@ -4237,6 +4268,11 @@ void P_PlayerThink(player_t *player)
player->playerstate = PST_DEAD;
}
player->old_drawangle = player->drawangle;
player->old_viewrollangle = player->viewrollangle;
player->pflags &= ~PF_HITFINISHLINE;
if (player->awayviewmobj && P_MobjWasRemoved(player->awayviewmobj))
{
P_SetTarget(&player->awayviewmobj, NULL); // remove awayviewmobj asap if invalid

View file

@ -26,6 +26,7 @@
#include "z_zone.h"
#include "console.h" // Until buffering gets finished
#include "k_color.h" // SRB2kart
#include "i_threads.h"
#ifdef HWRENDER
#include "hardware/hw_main.h"
@ -192,6 +193,29 @@ CV_PossibleValue_t Followercolor_cons_t[MAXSKINCOLORS+3]; // +3 to account for "
#define TRANSTAB_AMTMUL10 (255.0f / 10.0f)
static void R_AllocateBlendTables(void)
{
INT32 i;
for (i = 0; i < NUMBLENDMAPS; i++)
{
if (i == blendtab_modulate)
continue;
blendtables[i] = Z_MallocAlign((NUMTRANSTABLES + 1) * 0x10000, PU_STATIC, NULL, 16);
}
// Modulation blending only requires a single table
blendtables[blendtab_modulate] = Z_MallocAlign(0x10000, PU_STATIC, NULL, 16);
}
#ifdef HAVE_THREADS
static void R_GenerateBlendTables_Thread(void *userdata)
{
(void)userdata;
R_GenerateBlendTables();
}
#endif
/** \brief Initializes the translucency tables used by the Software renderer.
*/
void R_InitTranslucencyTables(void)
@ -212,20 +236,20 @@ void R_InitTranslucencyTables(void)
W_ReadLump(W_GetNumForName("TRANS80"), transtables+0x70000);
W_ReadLump(W_GetNumForName("TRANS90"), transtables+0x80000);
R_AllocateBlendTables();
#ifdef HAVE_THREADS
I_spawn_thread("blend-tables",
R_GenerateBlendTables_Thread, NULL);
#else
R_GenerateBlendTables();
#endif
}
void R_GenerateBlendTables(void)
{
INT32 i;
for (i = 0; i < NUMBLENDMAPS; i++)
{
if (i == blendtab_modulate)
continue;
blendtables[i] = Z_MallocAlign((NUMTRANSTABLES + 1) * 0x10000, PU_STATIC, NULL, 16);
}
for (i = 0; i <= 9; i++)
{
const size_t offs = (0x10000 * i);
@ -236,8 +260,6 @@ void R_GenerateBlendTables(void)
R_GenerateTranslucencyTable(blendtables[blendtab_reversesubtract] + offs, AST_REVERSESUBTRACT, alpha);
}
// Modulation blending only requires a single table
blendtables[blendtab_modulate] = Z_MallocAlign(0x10000, PU_STATIC, NULL, 16);
R_GenerateTranslucencyTable(blendtables[blendtab_modulate], AST_MODULATE, 0);
}

View file

@ -149,10 +149,6 @@ static void ChaseCam_OnChange(void);
static void ChaseCam2_OnChange(void);
static void ChaseCam3_OnChange(void);
static void ChaseCam4_OnChange(void);
void SendWeaponPref(void);
void SendWeaponPref2(void);
void SendWeaponPref3(void);
void SendWeaponPref4(void);
consvar_t cv_tailspickup = CVAR_INIT ("tailspickup", "On", CV_NETVAR|CV_NOSHOWHELP, CV_OnOff, NULL);
consvar_t cv_chasecam[MAXSPLITSCREENPLAYERS] = {
@ -1141,7 +1137,7 @@ void R_Init(void)
R_InitLightTables();
//I_OutputMsg("\nR_InitTranslucencyTables\n");
R_InitTranslucencyTables();
//R_InitTranslucencyTables();
R_InitDrawNodes();

View file

@ -42,6 +42,7 @@ patch_t *Patch_GetRotatedSprite(
INT32 R_GetRollAngle(angle_t rollangle);
angle_t R_GetPitchRollAngle(mobj_t *mobj, player_t *viewPlayer);
angle_t R_ModelRotationAngle(mobj_t *mobj, player_t *viewPlayer);
angle_t R_SpriteRotationAngle(mobj_t *mobj, player_t *viewPlayer);
#endif

View file

@ -64,10 +64,9 @@ static angle_t R_PlayerSpriteRotation(player_t *player, player_t *viewPlayer)
return rollAngle;
}
angle_t R_SpriteRotationAngle(mobj_t *mobj, player_t *viewPlayer)
angle_t R_ModelRotationAngle(mobj_t *mobj, player_t *viewPlayer)
{
angle_t rollOrPitch = R_GetPitchRollAngle(mobj, viewPlayer);
angle_t rollAngle = (rollOrPitch + mobj->rollangle);
angle_t rollAngle = mobj->rollangle;
if (mobj->player)
{
@ -77,6 +76,12 @@ angle_t R_SpriteRotationAngle(mobj_t *mobj, player_t *viewPlayer)
return rollAngle;
}
angle_t R_SpriteRotationAngle(mobj_t *mobj, player_t *viewPlayer)
{
angle_t rollOrPitch = R_GetPitchRollAngle(mobj, viewPlayer);
return (rollOrPitch + R_ModelRotationAngle(mobj, viewPlayer));
}
INT32 R_GetRollAngle(angle_t rollangle)
{
INT32 ra = AngleFixed(rollangle)>>FRACBITS;

View file

@ -105,6 +105,7 @@ typedef struct
} spriteinfo_t;
// Portable Network Graphics
#define PNG_HEADER_SIZE (8)
boolean Picture_IsLumpPNG(const UINT8 *d, size_t s);
#define Picture_ThrowPNGError(lumpname, wadfilename) I_Error("W_Wad: Lump \"%s\" in file \"%s\" is a .png - please convert to either Doom or Flat (raw) image format.", lumpname, wadfilename); // Fears Of LJ Sonic

View file

@ -311,11 +311,6 @@ void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum)
}
player->skincolor = newcolor = skin->prefcolor;
if (player->bot && botingame)
{
botskin = (UINT8)(skinnum + 1);
botcolor = skin->prefcolor;
}
}
#endif

View file

@ -725,6 +725,7 @@ Rloadflats (INT32 i, INT32 w)
UINT16 texstart, texend;
texture_t *texture;
texpatch_t *patch;
UINT8 header[PNG_HEADER_SIZE];
// Yes
if (wadfiles[w]->type == RET_PK3)
@ -743,7 +744,6 @@ Rloadflats (INT32 i, INT32 w)
// Work through each lump between the markers in the WAD.
for (j = 0; j < (texend - texstart); j++)
{
UINT8 *flatlump;
UINT16 wadnum = (UINT16)w;
lumpnum_t lumpnum = texstart + j;
size_t lumplength;
@ -755,7 +755,7 @@ Rloadflats (INT32 i, INT32 w)
continue; // If it is then SKIP IT
}
flatlump = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE);
W_ReadLumpHeaderPwad(wadnum, lumpnum, header, sizeof header, 0);
lumplength = W_LumpLengthPwad(wadnum, lumpnum);
switch (lumplength)
@ -790,12 +790,14 @@ Rloadflats (INT32 i, INT32 w)
M_Memcpy(texture->name, W_CheckNameForNumPwad(wadnum, lumpnum), sizeof(texture->name));
#ifndef NO_PNG_LUMPS
if (Picture_IsLumpPNG((UINT8 *)flatlump, lumplength))
if (Picture_IsLumpPNG(header, lumplength))
{
UINT8 *flatlump = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE);
INT32 width, height;
Picture_PNGDimensions((UINT8 *)flatlump, &width, &height, NULL, NULL, lumplength);
texture->width = (INT16)width;
texture->height = (INT16)height;
Z_Free(flatlump);
}
else
#endif
@ -814,8 +816,6 @@ Rloadflats (INT32 i, INT32 w)
patch->lump = texstart + j;
patch->flip = 0;
Z_Unlock(flatlump);
texturewidth[i] = texture->width;
textureheight[i] = texture->height << FRACBITS;
i++;
@ -835,8 +835,8 @@ Rloadtextures (INT32 i, INT32 w)
UINT16 j;
UINT16 texstart, texend, texturesLumpPos;
texture_t *texture;
softwarepatch_t *patchlump;
texpatch_t *patch;
softwarepatch_t patchlump;
// Get the lump numbers for the markers in the WAD, if they exist.
if (wadfiles[w]->type == RET_PK3)
@ -876,7 +876,7 @@ Rloadtextures (INT32 i, INT32 w)
continue; // If it is then SKIP IT
}
patchlump = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE);
W_ReadLumpHeaderPwad(wadnum, lumpnum, &patchlump, PNG_HEADER_SIZE, 0);
#ifndef NO_PNG_LUMPS
lumplength = W_LumpLengthPwad(wadnum, lumpnum);
#endif
@ -888,18 +888,20 @@ Rloadtextures (INT32 i, INT32 w)
M_Memcpy(texture->name, W_CheckNameForNumPwad(wadnum, lumpnum), sizeof(texture->name));
#ifndef NO_PNG_LUMPS
if (Picture_IsLumpPNG((UINT8 *)patchlump, lumplength))
if (Picture_IsLumpPNG((UINT8 *)&patchlump, lumplength))
{
UINT8 *png = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE);
INT32 width, height;
Picture_PNGDimensions((UINT8 *)patchlump, &width, &height, NULL, NULL, lumplength);
Picture_PNGDimensions(png, &width, &height, NULL, NULL, lumplength);
texture->width = (INT16)width;
texture->height = (INT16)height;
Z_Free(png);
}
else
#endif
{
texture->width = SHORT(patchlump->width);
texture->height = SHORT(patchlump->height);
texture->width = SHORT(patchlump.width);
texture->height = SHORT(patchlump.height);
}
texture->type = TEXTURETYPE_SINGLEPATCH;
@ -915,8 +917,6 @@ Rloadtextures (INT32 i, INT32 w)
patch->lump = texstart + j;
patch->flip = 0;
Z_Unlock(patchlump);
texturewidth[i] = texture->width;
textureheight[i] = texture->height << FRACBITS;
i++;

View file

@ -286,16 +286,18 @@ boolean R_AddSingleSpriteDef(const char *sprname, spritedef_t *spritedef, UINT16
#ifndef NO_PNG_LUMPS
{
softwarepatch_t *png = W_CacheLumpNumPwad(wadnum, l, PU_STATIC);
UINT8 header[PNG_HEADER_SIZE];
size_t len = W_LumpLengthPwad(wadnum, l);
if (Picture_IsLumpPNG((UINT8 *)png, len))
W_ReadLumpHeaderPwad(wadnum, l, header, sizeof header, 0);
if (Picture_IsLumpPNG(header, len))
{
UINT8 *png = W_CacheLumpNumPwad(wadnum, l, PU_STATIC);
Picture_PNGDimensions((UINT8 *)png, &width, &height, &topoffset, &leftoffset, len);
isPNG = true;
Z_Free(png);
}
Z_Free(png);
}
if (!isPNG)

View file

@ -1480,10 +1480,10 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si
int zErr; // Helper var.
z_stream strm;
unsigned long rawSize = l->disksize;
unsigned long decSize = l->size;
unsigned long decSize = size;
rawData = Z_Malloc(rawSize, PU_STATIC, NULL);
decData = Z_Malloc(decSize, PU_STATIC, NULL);
decData = dest;
if (fread(rawData, 1, rawSize, handle) < rawSize)
I_Error("wad %d, lump %d: cannot read compressed data", wad, lump);
@ -1501,12 +1501,8 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si
zErr = inflateInit2(&strm, -15);
if (zErr == Z_OK)
{
zErr = inflate(&strm, Z_FINISH);
if (zErr == Z_STREAM_END)
{
M_Memcpy(dest, decData, size);
}
else
zErr = inflate(&strm, Z_SYNC_FLUSH);
if (zErr != Z_OK && zErr != Z_STREAM_END)
{
size = 0;
zerr(zErr);
@ -1520,7 +1516,6 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si
}
Z_Free(rawData);
Z_Free(decData);
#ifdef NO_PNG_LUMPS
if (Picture_IsLumpPNG((UINT8 *)dest, size))

View file

@ -1322,7 +1322,7 @@ static void Y_VoteStops(SINT8 pick, SINT8 level)
if (gametype != votelevels[level][1])
{
INT16 lastgametype = gametype;
gametype = votelevels[level][1];
G_SetGametype(votelevels[level][1]);
D_GameTypeChanged(lastgametype);
forceresetplayers = true;
}