mirror of
https://git.do.srb2.org/STJr/SRB2.git
synced 2025-01-19 07:51:43 +00:00
Merge branch 'addplayer' into 'next'
Multiplayer Bot Features (aka AddPlayer) See merge request STJr/SRB2!1383
This commit is contained in:
commit
ccc71ddafc
15 changed files with 506 additions and 219 deletions
238
src/b_bot.c
238
src/b_bot.c
|
@ -18,29 +18,38 @@
|
|||
#include "b_bot.h"
|
||||
#include "lua_hook.h"
|
||||
|
||||
// If you want multiple bots, variables like this will
|
||||
// have to be stuffed in something accessible through player_t.
|
||||
static boolean lastForward = false;
|
||||
static boolean lastBlocked = false;
|
||||
static boolean blocked = false;
|
||||
|
||||
static boolean jump_last = false;
|
||||
static boolean spin_last = false;
|
||||
static UINT8 anxiety = 0;
|
||||
static boolean panic = false;
|
||||
static UINT8 flymode = 0;
|
||||
static boolean spinmode = false;
|
||||
static boolean thinkfly = false;
|
||||
|
||||
static inline void B_ResetAI(void)
|
||||
void B_UpdateBotleader(player_t *player)
|
||||
{
|
||||
jump_last = false;
|
||||
spin_last = false;
|
||||
anxiety = 0;
|
||||
panic = false;
|
||||
flymode = 0;
|
||||
spinmode = false;
|
||||
thinkfly = false;
|
||||
UINT32 i;
|
||||
fixed_t dist;
|
||||
fixed_t neardist = INT32_MAX;
|
||||
player_t *nearplayer = NULL;
|
||||
//Find new botleader
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (players[i].bot || players[i].playerstate != PST_LIVE || players[i].spectator || !players[i].mo)
|
||||
continue;
|
||||
if (!player->mo) //Can't do distance calculations if there's no player object, so we'll just take the first we find
|
||||
{
|
||||
player->botleader = &players[i];
|
||||
return;
|
||||
}
|
||||
//Update best candidate based on nearest distance
|
||||
dist = R_PointToDist2(player->mo->x, player->mo->y, players[i].mo->x, players[i].mo->y);
|
||||
if (neardist > dist)
|
||||
{
|
||||
neardist = dist;
|
||||
nearplayer = &players[i];
|
||||
}
|
||||
}
|
||||
//Set botleader to best candidate (or null if none available)
|
||||
player->botleader = nearplayer;
|
||||
}
|
||||
|
||||
static inline void B_ResetAI(botmem_t *mem)
|
||||
{
|
||||
mem->thinkstate = AI_FOLLOW;
|
||||
mem->catchup_tics = 0;
|
||||
}
|
||||
|
||||
static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
|
||||
|
@ -49,39 +58,47 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
|
|||
|
||||
player_t *player = sonic->player, *bot = tails->player;
|
||||
ticcmd_t *pcmd = &player->cmd;
|
||||
boolean water = tails->eflags & MFE_UNDERWATER;
|
||||
botmem_t *mem = &bot->botmem;
|
||||
boolean water = (tails->eflags & MFE_UNDERWATER);
|
||||
SINT8 flip = P_MobjFlip(tails);
|
||||
boolean _2d = (tails->flags2 & MF2_TWOD) || twodlevel;
|
||||
fixed_t scale = tails->scale;
|
||||
boolean jump_last = (bot->lastbuttons & BT_JUMP);
|
||||
boolean spin_last = (bot->lastbuttons & BT_SPIN);
|
||||
|
||||
fixed_t dist = P_AproxDistance(sonic->x - tails->x, sonic->y - tails->y);
|
||||
fixed_t zdist = flip * (sonic->z - tails->z);
|
||||
angle_t ang = sonic->angle;
|
||||
fixed_t pmom = P_AproxDistance(sonic->momx, sonic->momy);
|
||||
fixed_t bmom = P_AproxDistance(tails->momx, tails->momy);
|
||||
fixed_t followmax = 128 * 8 * scale; // Max follow distance before AI begins to enter "panic" state
|
||||
fixed_t followmax = 128 * 8 * scale; // Max follow distance before AI begins to enter catchup state
|
||||
fixed_t followthres = 92 * scale; // Distance that AI will try to reach
|
||||
fixed_t followmin = 32 * scale;
|
||||
fixed_t comfortheight = 96 * scale;
|
||||
fixed_t touchdist = 24 * scale;
|
||||
boolean stalled = (bmom < scale >> 1) && dist > followthres; // Helps to see if the AI is having trouble catching up
|
||||
boolean samepos = (sonic->x == tails->x && sonic->y == tails->y);
|
||||
|
||||
boolean blocked = bot->blocked;
|
||||
|
||||
if (!samepos)
|
||||
ang = R_PointToAngle2(tails->x, tails->y, sonic->x, sonic->y);
|
||||
|
||||
// We can't follow Sonic if he's not around!
|
||||
if (!sonic || sonic->health <= 0)
|
||||
return;
|
||||
|
||||
// Lua can handle it!
|
||||
if (LUA_HookBotAI(sonic, tails, cmd))
|
||||
return;
|
||||
|
||||
// We can't follow Sonic if he's not around!
|
||||
if (!sonic || sonic->health <= 0)
|
||||
{
|
||||
mem->thinkstate = AI_STANDBY;
|
||||
return;
|
||||
}
|
||||
else if (mem->thinkstate == AI_STANDBY)
|
||||
mem->thinkstate = AI_FOLLOW;
|
||||
|
||||
if (tails->player->powers[pw_carry] == CR_MACESPIN || tails->player->powers[pw_carry] == CR_GENERIC)
|
||||
{
|
||||
boolean isrelevant = (sonic->player->powers[pw_carry] == CR_MACESPIN || sonic->player->powers[pw_carry] == CR_GENERIC);
|
||||
dist = P_AproxDistance(tails->x-sonic->x, tails->y-sonic->y);
|
||||
if (sonic->player->cmd.buttons & BT_JUMP && (sonic->player->pflags & PF_JUMPED) && isrelevant)
|
||||
cmd->buttons |= BT_JUMP;
|
||||
if (isrelevant)
|
||||
|
@ -103,56 +120,57 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
|
|||
followmin = 0;
|
||||
followthres = 16*scale;
|
||||
followmax >>= 1;
|
||||
thinkfly = false;
|
||||
if (mem->thinkstate == AI_THINKFLY)
|
||||
mem->thinkstate = AI_FOLLOW;
|
||||
}
|
||||
|
||||
// Check anxiety
|
||||
if (spinmode)
|
||||
// Update catchup_tics
|
||||
if (mem->thinkstate == AI_SPINFOLLOW)
|
||||
{
|
||||
anxiety = 0;
|
||||
panic = false;
|
||||
mem-> catchup_tics = 0;
|
||||
}
|
||||
else if (dist > followmax || zdist > comfortheight || stalled)
|
||||
{
|
||||
anxiety = min(anxiety + 2, 70);
|
||||
if (anxiety >= 70)
|
||||
panic = true;
|
||||
mem-> catchup_tics = min(mem-> catchup_tics + 2, 70);
|
||||
if (mem-> catchup_tics >= 70)
|
||||
mem->thinkstate = AI_CATCHUP;
|
||||
}
|
||||
else
|
||||
{
|
||||
anxiety = max(anxiety - 1, 0);
|
||||
panic = false;
|
||||
mem-> catchup_tics = max(mem-> catchup_tics - 1, 0);
|
||||
if (mem->thinkstate == AI_CATCHUP)
|
||||
mem->thinkstate = AI_FOLLOW;
|
||||
}
|
||||
|
||||
// Orientation
|
||||
// cmd->angleturn won't be relative to player angle, since we're not going through G_BuildTiccmd.
|
||||
if (bot->pflags & (PF_SPINNING|PF_STARTDASH))
|
||||
{
|
||||
cmd->angleturn = (sonic->angle - tails->angle) >> 16; // NOT FRACBITS DAMNIT
|
||||
cmd->angleturn = (sonic->angle) >> 16; // NOT FRACBITS DAMNIT
|
||||
}
|
||||
else if (flymode == 2)
|
||||
else if (mem->thinkstate == AI_FLYCARRY)
|
||||
{
|
||||
cmd->angleturn = sonic->player->cmd.angleturn - (tails->angle >> 16);
|
||||
cmd->angleturn = sonic->player->cmd.angleturn;
|
||||
}
|
||||
else
|
||||
{
|
||||
cmd->angleturn = (ang - tails->angle) >> 16; // NOT FRACBITS DAMNIT
|
||||
cmd->angleturn = (ang) >> 16; // NOT FRACBITS DAMNIT
|
||||
}
|
||||
|
||||
// ********
|
||||
// FLY MODE
|
||||
// spinmode check
|
||||
if (spinmode || player->exiting)
|
||||
thinkfly = false;
|
||||
// exiting check
|
||||
if (player->exiting && mem->thinkstate == AI_THINKFLY)
|
||||
mem->thinkstate = AI_FOLLOW;
|
||||
else
|
||||
{
|
||||
// Activate co-op flight
|
||||
if (thinkfly && player->pflags & PF_JUMPED)
|
||||
if (mem->thinkstate == AI_THINKFLY && player->pflags & PF_JUMPED)
|
||||
{
|
||||
if (!jump_last)
|
||||
{
|
||||
jump = true;
|
||||
flymode = 1;
|
||||
thinkfly = false;
|
||||
mem->thinkstate = AI_FLYSTANDBY;
|
||||
bot->pflags |= PF_CANCARRY;
|
||||
}
|
||||
}
|
||||
|
@ -165,20 +183,19 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
|
|||
&& P_IsObjectOnGround(sonic) && P_IsObjectOnGround(tails)
|
||||
&& !(player->pflags & PF_STASIS)
|
||||
&& bot->charability == CA_FLY)
|
||||
thinkfly = true;
|
||||
else
|
||||
thinkfly = false;
|
||||
mem->thinkstate = AI_THINKFLY;
|
||||
else if (mem->thinkstate == AI_THINKFLY)
|
||||
mem->thinkstate = AI_FOLLOW;
|
||||
|
||||
// Set carried state
|
||||
if (player->powers[pw_carry] == CR_PLAYER && sonic->tracer == tails)
|
||||
{
|
||||
flymode = 2;
|
||||
mem->thinkstate = AI_FLYCARRY;
|
||||
}
|
||||
|
||||
// Ready for takeoff
|
||||
if (flymode == 1)
|
||||
if (mem->thinkstate == AI_FLYSTANDBY)
|
||||
{
|
||||
thinkfly = false;
|
||||
if (zdist < -64*scale || (flip * tails->momz) > scale) // Make sure we're not too high up
|
||||
spin = true;
|
||||
else if (!jump_last)
|
||||
|
@ -186,10 +203,10 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
|
|||
|
||||
// Abort if the player moves away or spins
|
||||
if (dist > followthres || player->dashspeed)
|
||||
flymode = 0;
|
||||
mem->thinkstate = AI_FOLLOW;
|
||||
}
|
||||
// Read player inputs while carrying
|
||||
else if (flymode == 2)
|
||||
else if (mem->thinkstate == AI_FLYCARRY)
|
||||
{
|
||||
cmd->forwardmove = pcmd->forwardmove;
|
||||
cmd->sidemove = pcmd->sidemove;
|
||||
|
@ -203,19 +220,19 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
|
|||
// End flymode
|
||||
if (player->powers[pw_carry] != CR_PLAYER)
|
||||
{
|
||||
flymode = 0;
|
||||
mem->thinkstate = AI_FOLLOW;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (flymode && P_IsObjectOnGround(tails) && !(pcmd->buttons & BT_JUMP))
|
||||
flymode = 0;
|
||||
if (P_IsObjectOnGround(tails) && !(pcmd->buttons & BT_JUMP) && (mem->thinkstate == AI_FLYSTANDBY || mem->thinkstate == AI_FLYCARRY))
|
||||
mem->thinkstate = AI_FOLLOW;
|
||||
|
||||
// ********
|
||||
// SPINNING
|
||||
if (panic || flymode || !(player->pflags & PF_SPINNING) || (player->pflags & PF_JUMPED))
|
||||
spinmode = false;
|
||||
else
|
||||
if (!(player->pflags & (PF_SPINNING|PF_STARTDASH)) && mem->thinkstate == AI_SPINFOLLOW)
|
||||
mem->thinkstate = AI_FOLLOW;
|
||||
else if (mem->thinkstate == AI_FOLLOW || mem->thinkstate == AI_SPINFOLLOW)
|
||||
{
|
||||
if (!_2d)
|
||||
{
|
||||
|
@ -224,21 +241,21 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
|
|||
{
|
||||
if (dist < followthres && dist > touchdist) // Do positioning
|
||||
{
|
||||
cmd->angleturn = (ang - tails->angle) >> 16; // NOT FRACBITS DAMNIT
|
||||
cmd->angleturn = (ang) >> 16; // NOT FRACBITS DAMNIT
|
||||
cmd->forwardmove = 50;
|
||||
spinmode = true;
|
||||
mem->thinkstate = AI_SPINFOLLOW;
|
||||
}
|
||||
else if (dist < touchdist)
|
||||
{
|
||||
if (!bmom && (!(bot->pflags & PF_SPINNING) || (bot->dashspeed && bot->pflags & PF_SPINNING)))
|
||||
{
|
||||
cmd->angleturn = (sonic->angle - tails->angle) >> 16; // NOT FRACBITS DAMNIT
|
||||
cmd->angleturn = (sonic->angle) >> 16; // NOT FRACBITS DAMNIT
|
||||
spin = true;
|
||||
}
|
||||
spinmode = true;
|
||||
mem->thinkstate = AI_SPINFOLLOW;
|
||||
}
|
||||
else
|
||||
spinmode = false;
|
||||
mem->thinkstate = AI_FOLLOW;
|
||||
}
|
||||
// Spin
|
||||
else if (player->dashspeed == bot->dashspeed && player->pflags & PF_SPINNING)
|
||||
|
@ -246,12 +263,12 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
|
|||
if (bot->pflags & PF_SPINNING || !spin_last)
|
||||
{
|
||||
spin = true;
|
||||
cmd->angleturn = (sonic->angle - tails->angle) >> 16; // NOT FRACBITS DAMNIT
|
||||
cmd->angleturn = (sonic->angle) >> 16; // NOT FRACBITS DAMNIT
|
||||
cmd->forwardmove = MAXPLMOVE;
|
||||
spinmode = true;
|
||||
mem->thinkstate = AI_SPINFOLLOW;
|
||||
}
|
||||
else
|
||||
spinmode = false;
|
||||
mem->thinkstate = AI_FOLLOW;
|
||||
}
|
||||
}
|
||||
// 2D mode
|
||||
|
@ -261,17 +278,19 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
|
|||
&& ((bot->pflags & PF_SPINNING) || !spin_last))
|
||||
{
|
||||
spin = true;
|
||||
spinmode = true;
|
||||
mem->thinkstate = AI_SPINFOLLOW;
|
||||
}
|
||||
else
|
||||
mem->thinkstate = AI_FOLLOW;
|
||||
}
|
||||
}
|
||||
|
||||
// ********
|
||||
// FOLLOW
|
||||
if (!(flymode || spinmode))
|
||||
if (mem->thinkstate == AI_FOLLOW || mem->thinkstate == AI_CATCHUP)
|
||||
{
|
||||
// Too far
|
||||
if (panic || dist > followthres)
|
||||
if (mem->thinkstate == AI_CATCHUP || dist > followthres)
|
||||
{
|
||||
if (!_2d)
|
||||
cmd->forwardmove = MAXPLMOVE;
|
||||
|
@ -281,7 +300,7 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
|
|||
cmd->sidemove = -MAXPLMOVE;
|
||||
}
|
||||
// Within threshold
|
||||
else if (!panic && dist > followmin && abs(zdist) < 192*scale)
|
||||
else if (dist > followmin && abs(zdist) < 192*scale)
|
||||
{
|
||||
if (!_2d)
|
||||
cmd->forwardmove = FixedHypot(pcmd->forwardmove, pcmd->sidemove);
|
||||
|
@ -292,7 +311,7 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
|
|||
else if (dist < followmin)
|
||||
{
|
||||
// Copy inputs
|
||||
cmd->angleturn = (sonic->angle - tails->angle) >> 16; // NOT FRACBITS DAMNIT
|
||||
cmd->angleturn = (sonic->angle) >> 16; // NOT FRACBITS DAMNIT
|
||||
bot->drawangle = ang;
|
||||
cmd->forwardmove = 8 * pcmd->forwardmove / 10;
|
||||
cmd->sidemove = 8 * pcmd->sidemove / 10;
|
||||
|
@ -301,7 +320,7 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
|
|||
|
||||
// ********
|
||||
// JUMP
|
||||
if (!(flymode || spinmode))
|
||||
if (mem->thinkstate == AI_FOLLOW || mem->thinkstate == AI_CATCHUP || (mem->thinkstate == AI_SPINFOLLOW && player->pflags & PF_JUMPED))
|
||||
{
|
||||
// Flying catch-up
|
||||
if (bot->pflags & PF_THOKKED)
|
||||
|
@ -319,31 +338,30 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
|
|||
// Start jump
|
||||
else if (!jump_last && !(bot->pflags & PF_JUMPED) //&& !(player->pflags & PF_SPINNING)
|
||||
&& ((zdist > 32*scale && player->pflags & PF_JUMPED) // Following
|
||||
|| (zdist > 64*scale && panic) // Vertical catch-up
|
||||
|| (stalled && anxiety > 20 && bot->powers[pw_carry] == CR_NONE)
|
||||
|| (zdist > 64*scale && mem->thinkstate == AI_CATCHUP) // Vertical catch-up
|
||||
|| (stalled && mem-> catchup_tics > 20 && bot->powers[pw_carry] == CR_NONE)
|
||||
//|| (bmom < scale>>3 && dist > followthres && !(bot->powers[pw_carry])) // Stopped & not in carry state
|
||||
|| (bot->pflags & PF_SPINNING && !(bot->pflags & PF_JUMPED)))) // Spinning
|
||||
jump = true;
|
||||
// Hold jump
|
||||
else if (bot->pflags & PF_JUMPED && jump_last && tails->momz*flip > 0 && (zdist > 0 || panic))
|
||||
else if (bot->pflags & PF_JUMPED && jump_last && tails->momz*flip > 0 && (zdist > 0 || mem->thinkstate == AI_CATCHUP))
|
||||
jump = true;
|
||||
// Start flying
|
||||
else if (bot->pflags & PF_JUMPED && panic && !jump_last && bot->charability == CA_FLY)
|
||||
else if (bot->pflags & PF_JUMPED && mem->thinkstate == AI_CATCHUP && !jump_last && bot->charability == CA_FLY)
|
||||
jump = true;
|
||||
}
|
||||
|
||||
// ********
|
||||
// HISTORY
|
||||
jump_last = jump;
|
||||
spin_last = spin;
|
||||
//jump_last = jump;
|
||||
//spin_last = spin;
|
||||
|
||||
// Turn the virtual keypresses into ticcmd_t.
|
||||
B_KeysToTiccmd(tails, cmd, forward, backward, left, right, false, false, jump, spin);
|
||||
|
||||
// Update our status
|
||||
lastForward = forward;
|
||||
lastBlocked = blocked;
|
||||
blocked = false;
|
||||
mem->lastForward = forward;
|
||||
mem->lastBlocked = blocked;
|
||||
}
|
||||
|
||||
void B_BuildTiccmd(player_t *player, ticcmd_t *cmd)
|
||||
|
@ -366,22 +384,25 @@ void B_BuildTiccmd(player_t *player, ticcmd_t *cmd)
|
|||
if (LUA_HookTiccmd(player, cmd, HOOK(BotTiccmd)))
|
||||
return;
|
||||
|
||||
// We don't have any main character AI, sorry. D:
|
||||
if (player-players == consoleplayer)
|
||||
// Make sure we have a valid main character to follow
|
||||
B_UpdateBotleader(player);
|
||||
if (!player->botleader)
|
||||
return;
|
||||
|
||||
// Basic Tails AI
|
||||
B_BuildTailsTiccmd(players[consoleplayer].mo, player->mo, cmd);
|
||||
// Single Player Tails AI
|
||||
//B_BuildTailsTiccmd(players[consoleplayer].mo, player->mo, cmd);
|
||||
B_BuildTailsTiccmd(player->botleader->mo, player->mo, cmd);
|
||||
}
|
||||
|
||||
void B_KeysToTiccmd(mobj_t *mo, ticcmd_t *cmd, boolean forward, boolean backward, boolean left, boolean right, boolean strafeleft, boolean straferight, boolean jump, boolean spin)
|
||||
{
|
||||
player_t *player = mo->player;
|
||||
// don't try to do stuff if your sonic is in a minecart or something
|
||||
if (players[consoleplayer].powers[pw_carry] && players[consoleplayer].powers[pw_carry] != CR_PLAYER)
|
||||
if (&player->botleader && player->botleader->powers[pw_carry] && player->botleader->powers[pw_carry] != CR_PLAYER)
|
||||
return;
|
||||
// Turn the virtual keypresses into ticcmd_t.
|
||||
if (twodlevel || mo->flags2 & MF2_TWOD) {
|
||||
if (players[consoleplayer].climbing
|
||||
if (player->botleader->climbing
|
||||
|| mo->player->pflags & PF_GLIDING) {
|
||||
// Don't mess with bot inputs during these unhandled movement conditions.
|
||||
// The normal AI doesn't use abilities, so custom AI should be sending us exactly what it wants anyway.
|
||||
|
@ -420,10 +441,10 @@ void B_KeysToTiccmd(mobj_t *mo, ticcmd_t *cmd, boolean forward, boolean backward
|
|||
cmd->forwardmove += MAXPLMOVE<<FRACBITS>>16;
|
||||
if (backward)
|
||||
cmd->forwardmove -= MAXPLMOVE<<FRACBITS>>16;
|
||||
if (left)
|
||||
if (left)
|
||||
cmd->angleturn += 1280;
|
||||
if (right)
|
||||
cmd->angleturn -= 1280;
|
||||
cmd->angleturn -= 1280;
|
||||
if (strafeleft)
|
||||
cmd->sidemove -= MAXPLMOVE<<FRACBITS>>16;
|
||||
if (straferight)
|
||||
|
@ -447,14 +468,19 @@ void B_KeysToTiccmd(mobj_t *mo, ticcmd_t *cmd, boolean forward, boolean backward
|
|||
void B_MoveBlocked(player_t *player)
|
||||
{
|
||||
(void)player;
|
||||
blocked = true;
|
||||
player->blocked = true;
|
||||
}
|
||||
|
||||
boolean B_CheckRespawn(player_t *player)
|
||||
{
|
||||
mobj_t *sonic = players[consoleplayer].mo;
|
||||
mobj_t *sonic;
|
||||
mobj_t *tails = player->mo;
|
||||
|
||||
//We don't have a main player to spawn to!
|
||||
if (!player->botleader)
|
||||
return false;
|
||||
|
||||
sonic = player->botleader->mo;
|
||||
// We can't follow Sonic if he's not around!
|
||||
if (!sonic || sonic->health <= 0)
|
||||
return false;
|
||||
|
@ -505,15 +531,19 @@ void B_RespawnBot(INT32 playernum)
|
|||
{
|
||||
player_t *player = &players[playernum];
|
||||
fixed_t x,y,z;
|
||||
mobj_t *sonic = players[consoleplayer].mo;
|
||||
mobj_t *sonic;
|
||||
mobj_t *tails;
|
||||
|
||||
if (!player->botleader)
|
||||
return;
|
||||
|
||||
sonic = player->botleader->mo;
|
||||
if (!sonic || sonic->health <= 0)
|
||||
return;
|
||||
|
||||
B_ResetAI();
|
||||
B_ResetAI(&player->botmem);
|
||||
|
||||
player->bot = 1;
|
||||
player->bot = BOT_2PAI;
|
||||
P_SpawnPlayer(playernum);
|
||||
tails = player->mo;
|
||||
|
||||
|
@ -540,10 +570,6 @@ void B_RespawnBot(INT32 playernum)
|
|||
player->powers[pw_spacetime] = sonic->player->powers[pw_spacetime];
|
||||
player->powers[pw_gravityboots] = sonic->player->powers[pw_gravityboots];
|
||||
player->powers[pw_nocontrol] = sonic->player->powers[pw_nocontrol];
|
||||
player->acceleration = sonic->player->acceleration;
|
||||
player->accelstart = sonic->player->accelstart;
|
||||
player->thrustfactor = sonic->player->thrustfactor;
|
||||
player->normalspeed = sonic->player->normalspeed;
|
||||
player->pflags |= PF_AUTOBRAKE|(sonic->player->pflags & PF_DIRECTIONCHAR);
|
||||
|
||||
P_TeleportMove(tails, x, y, z);
|
||||
|
@ -561,11 +587,11 @@ void B_RespawnBot(INT32 playernum)
|
|||
void B_HandleFlightIndicator(player_t *player)
|
||||
{
|
||||
mobj_t *tails = player->mo;
|
||||
|
||||
botmem_t *mem = &player->botmem;
|
||||
if (!tails)
|
||||
return;
|
||||
|
||||
if (thinkfly && player->bot == 1 && tails->health)
|
||||
if (mem->thinkstate == AI_THINKFLY && player->bot == BOT_2PAI && tails->health)
|
||||
{
|
||||
if (!tails->hnext)
|
||||
{
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
/// \file b_bot.h
|
||||
/// \brief Basic bot handling
|
||||
|
||||
void B_UpdateBotleader(player_t *player);
|
||||
void B_BuildTiccmd(player_t *player, ticcmd_t *cmd);
|
||||
void B_KeysToTiccmd(mobj_t *mo, ticcmd_t *cmd, boolean forward, boolean backward, boolean left, boolean right, boolean strafeleft, boolean straferight, boolean jump, boolean spin);
|
||||
boolean B_CheckRespawn(player_t *player);
|
||||
|
|
|
@ -2537,7 +2537,7 @@ void CL_ClearPlayer(INT32 playernum)
|
|||
//
|
||||
// Removes a player from the current game
|
||||
//
|
||||
static void CL_RemovePlayer(INT32 playernum, kickreason_t reason)
|
||||
void CL_RemovePlayer(INT32 playernum, kickreason_t reason)
|
||||
{
|
||||
// Sanity check: exceptional cases (i.e. c-fails) can cause multiple
|
||||
// kick commands to be issued for the same player.
|
||||
|
|
|
@ -405,6 +405,7 @@ void CL_Reset(void);
|
|||
void CL_ClearPlayer(INT32 playernum);
|
||||
void CL_QueryServerList(msg_server_t *list);
|
||||
void CL_UpdateServerList(boolean internetsearch, INT32 room);
|
||||
void CL_RemovePlayer(INT32 playernum, kickreason_t reason);
|
||||
// Is there a game running
|
||||
boolean Playing(void);
|
||||
|
||||
|
|
|
@ -313,9 +313,43 @@ typedef enum
|
|||
RW_RAIL = 32
|
||||
} ringweapons_t;
|
||||
|
||||
//Bot types
|
||||
typedef enum
|
||||
{
|
||||
BOT_NONE = 0,
|
||||
BOT_2PAI,
|
||||
BOT_2PHUMAN,
|
||||
BOT_MPAI
|
||||
} bottype_t;
|
||||
|
||||
//AI states
|
||||
typedef enum
|
||||
{
|
||||
AI_STANDBY = 0,
|
||||
AI_FOLLOW,
|
||||
AI_CATCHUP,
|
||||
AI_THINKFLY,
|
||||
AI_FLYSTANDBY,
|
||||
AI_FLYCARRY,
|
||||
AI_SPINFOLLOW
|
||||
} aistatetype_t;
|
||||
|
||||
|
||||
// ========================================================================
|
||||
// PLAYER STRUCTURE
|
||||
// ========================================================================
|
||||
|
||||
//Bot memory struct
|
||||
typedef struct botmem_s
|
||||
{
|
||||
boolean lastForward;
|
||||
boolean lastBlocked;
|
||||
boolean blocked;
|
||||
UINT8 catchup_tics;
|
||||
UINT8 thinkstate;
|
||||
} botmem_t;
|
||||
|
||||
//Main struct
|
||||
typedef struct player_s
|
||||
{
|
||||
mobj_t *mo;
|
||||
|
@ -525,8 +559,13 @@ typedef struct player_s
|
|||
|
||||
boolean spectator;
|
||||
boolean outofcoop;
|
||||
boolean removing;
|
||||
UINT8 bot;
|
||||
|
||||
struct player_s *botleader;
|
||||
UINT16 lastbuttons;
|
||||
botmem_t botmem;
|
||||
boolean blocked;
|
||||
|
||||
tic_t jointime; // Timer when player joins game to change skin/color
|
||||
tic_t quittime; // Time elapsed since user disconnected, zero if connected
|
||||
#ifdef HWRENDER
|
||||
|
|
|
@ -5170,6 +5170,12 @@ struct int_const_s const INT_CONST[] = {
|
|||
{"GF_REDFLAG",GF_REDFLAG},
|
||||
{"GF_BLUEFLAG",GF_BLUEFLAG},
|
||||
|
||||
// Bot types
|
||||
{"BOT_NONE",BOT_NONE},
|
||||
{"BOT_2PAI",BOT_2PAI},
|
||||
{"BOT_2PHUMAN",BOT_2PHUMAN},
|
||||
{"BOT_MPAI",BOT_MPAI},
|
||||
|
||||
// Customisable sounds for Skins, from sounds.h
|
||||
{"SKSSPIN",SKSSPIN},
|
||||
{"SKSPUTPUT",SKSPUTPUT},
|
||||
|
|
141
src/g_game.c
141
src/g_game.c
|
@ -1071,7 +1071,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
|
|||
boolean turnleft, turnright, strafelkey, straferkey, movefkey, movebkey, mouseaiming, analogjoystickmove, gamepadjoystickmove, thisjoyaiming;
|
||||
boolean strafeisturn; // Simple controls only
|
||||
player_t *player = &players[ssplayer == 2 ? secondarydisplayplayer : consoleplayer];
|
||||
camera_t *thiscam = ((ssplayer == 1 || player->bot == 2) ? &camera : &camera2);
|
||||
camera_t *thiscam = ((ssplayer == 1 || player->bot == BOT_2PHUMAN) ? &camera : &camera2);
|
||||
angle_t *myangle = (ssplayer == 1 ? &localangle : &localangle2);
|
||||
INT32 *myaiming = (ssplayer == 1 ? &localaiming : &localaiming2);
|
||||
|
||||
|
@ -1545,23 +1545,14 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
|
|||
cmd->forwardmove = (SINT8)(cmd->forwardmove + forward);
|
||||
cmd->sidemove = (SINT8)(cmd->sidemove + side);
|
||||
|
||||
if (player->bot == 1) { // Tailsbot for P2
|
||||
if (!player->powers[pw_tailsfly] && (cmd->forwardmove || cmd->sidemove || cmd->buttons))
|
||||
{
|
||||
player->bot = 2; // A player-controlled bot. Returns to AI when it respawns.
|
||||
CV_SetValue(&cv_analog[1], true);
|
||||
}
|
||||
else
|
||||
{
|
||||
G_CopyTiccmd(cmd, I_BaseTiccmd2(), 1); // empty, or external driver
|
||||
B_BuildTiccmd(player, cmd);
|
||||
}
|
||||
B_HandleFlightIndicator(player);
|
||||
}
|
||||
else if (player->bot == 2)
|
||||
// Note: Majority of botstuffs are handled in G_Ticker now.
|
||||
if (player->bot == BOT_2PHUMAN) //Player-controlled bot
|
||||
{
|
||||
G_CopyTiccmd(cmd, I_BaseTiccmd2(), 1); // empty, or external driver
|
||||
// Fix offset angle for P2-controlled Tailsbot when P2's controls are set to non-Legacy
|
||||
cmd->angleturn = (INT16)((localangle - *myangle) >> 16);
|
||||
|
||||
}
|
||||
|
||||
*myangle += (cmd->angleturn<<16);
|
||||
|
||||
if (controlstyle == CS_LMAOGALOG) {
|
||||
|
@ -2207,6 +2198,23 @@ void G_Ticker(boolean run)
|
|||
UINT32 i;
|
||||
INT32 buf;
|
||||
|
||||
// Bot players queued for removal
|
||||
for (i = MAXPLAYERS-1; i != UINT32_MAX; i--)
|
||||
{
|
||||
if (playeringame[i] && players[i].removing)
|
||||
{
|
||||
CL_RemovePlayer(i, i);
|
||||
if (netgame)
|
||||
{
|
||||
char kickmsg[256];
|
||||
|
||||
strcpy(kickmsg, M_GetText("\x82*Bot %s has been removed"));
|
||||
strcpy(kickmsg, va(kickmsg, player_names[i], i));
|
||||
HU_AddChatText(kickmsg, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// see also SCR_DisplayMarathonInfo
|
||||
if ((marathonmode & (MA_INIT|MA_INGAME)) == MA_INGAME && gamestate == GS_LEVEL)
|
||||
marathontime++;
|
||||
|
@ -2292,23 +2300,58 @@ void G_Ticker(boolean run)
|
|||
if (playeringame[i])
|
||||
{
|
||||
INT16 received;
|
||||
// Save last frame's button readings
|
||||
players[i].lastbuttons = players[i].cmd.buttons;
|
||||
|
||||
G_CopyTiccmd(&players[i].cmd, &netcmds[buf][i], 1);
|
||||
|
||||
received = (players[i].cmd.angleturn & TICCMD_RECEIVED);
|
||||
|
||||
players[i].angleturn += players[i].cmd.angleturn - players[i].oldrelangleturn;
|
||||
players[i].oldrelangleturn = players[i].cmd.angleturn;
|
||||
if (P_ControlStyle(&players[i]) == CS_LMAOGALOG)
|
||||
P_ForceLocalAngle(&players[i], players[i].angleturn << 16);
|
||||
else
|
||||
players[i].cmd.angleturn = players[i].angleturn;
|
||||
|
||||
players[i].cmd.angleturn &= ~TICCMD_RECEIVED;
|
||||
// Bot ticcmd handling
|
||||
// Yes, ordinarily this would be handled in G_BuildTiccmd...
|
||||
// ...however, bot players won't have a corresponding consoleplayer or splitscreen player 2 to send that information.
|
||||
// Therefore, this has to be done after ticcmd sends are received.
|
||||
if (players[i].bot == BOT_2PAI) { // Tailsbot for P2
|
||||
if (!players[i].powers[pw_tailsfly] && (players[i].cmd.forwardmove || players[i].cmd.sidemove || players[i].cmd.buttons))
|
||||
{
|
||||
players[i].bot = BOT_2PHUMAN; // A player-controlled bot. Returns to AI when it respawns.
|
||||
CV_SetValue(&cv_analog[1], true);
|
||||
}
|
||||
else
|
||||
{
|
||||
B_BuildTiccmd(&players[i], &players[i].cmd);
|
||||
}
|
||||
B_HandleFlightIndicator(&players[i]);
|
||||
}
|
||||
else if (players[i].bot == BOT_MPAI) {
|
||||
B_BuildTiccmd(&players[i], &players[i].cmd);
|
||||
}
|
||||
|
||||
// Do angle adjustments.
|
||||
if (players[i].bot == BOT_NONE || players[i].bot == BOT_2PHUMAN)
|
||||
{
|
||||
received = (players[i].cmd.angleturn & TICCMD_RECEIVED);
|
||||
players[i].angleturn += players[i].cmd.angleturn - players[i].oldrelangleturn;
|
||||
players[i].oldrelangleturn = players[i].cmd.angleturn;
|
||||
if (P_ControlStyle(&players[i]) == CS_LMAOGALOG)
|
||||
P_ForceLocalAngle(&players[i], players[i].angleturn << 16);
|
||||
else
|
||||
players[i].cmd.angleturn = players[i].angleturn;
|
||||
if (P_ControlStyle(&players[i]) == CS_LMAOGALOG)
|
||||
P_ForceLocalAngle(&players[i], players[i].angleturn << 16);
|
||||
else
|
||||
players[i].cmd.angleturn = players[i].angleturn;
|
||||
|
||||
players[i].cmd.angleturn &= ~TICCMD_RECEIVED;
|
||||
// Use the leveltime sent in the player's ticcmd to determine control lag
|
||||
players[i].cmd.latency = min(((leveltime & 0xFF) - players[i].cmd.latency) & 0xFF, MAXPREDICTTICS-1);
|
||||
}
|
||||
else // Less work is required if we're building a bot ticcmd.
|
||||
{
|
||||
// Since bot TicCmd is pre-determined for both the client and server, the latency and packet checks are simplified.
|
||||
received = 1;
|
||||
players[i].cmd.latency = 0;
|
||||
players[i].angleturn = players[i].cmd.angleturn;
|
||||
players[i].oldrelangleturn = players[i].cmd.angleturn;
|
||||
}
|
||||
players[i].cmd.angleturn |= received;
|
||||
|
||||
// Use the leveltime sent in the player's ticcmd to determine control lag
|
||||
players[i].cmd.latency = min(((leveltime & 0xFF) - players[i].cmd.latency) & 0xFF, MAXPREDICTTICS-1);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2494,6 +2537,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
tic_t quittime;
|
||||
boolean spectator;
|
||||
boolean outofcoop;
|
||||
boolean removing;
|
||||
INT16 bot;
|
||||
SINT8 pity;
|
||||
INT16 rings;
|
||||
|
@ -2510,6 +2554,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
quittime = players[player].quittime;
|
||||
spectator = players[player].spectator;
|
||||
outofcoop = players[player].outofcoop;
|
||||
removing = players[player].removing;
|
||||
pflags = (players[player].pflags & (PF_FLIPCAM|PF_ANALOGMODE|PF_DIRECTIONCHAR|PF_AUTOBRAKE|PF_TAGIT|PF_GAMETYPEOVER));
|
||||
playerangleturn = players[player].angleturn;
|
||||
oldrelangleturn = players[player].oldrelangleturn;
|
||||
|
@ -2586,6 +2631,7 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
p->quittime = quittime;
|
||||
p->spectator = spectator;
|
||||
p->outofcoop = outofcoop;
|
||||
p->removing = removing;
|
||||
p->angleturn = playerangleturn;
|
||||
p->oldrelangleturn = oldrelangleturn;
|
||||
|
||||
|
@ -2630,8 +2676,10 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
|
|||
p->totalring = totalring;
|
||||
|
||||
p->mare = mare;
|
||||
if (bot)
|
||||
p->bot = 1; // reset to AI-controlled
|
||||
if (bot == BOT_2PHUMAN)
|
||||
p->bot = BOT_2PAI; // reset to AI-controlled
|
||||
else
|
||||
p->bot = bot;
|
||||
p->pity = pity;
|
||||
p->rings = rings;
|
||||
p->spheres = spheres;
|
||||
|
@ -2977,7 +3025,8 @@ void G_DoReborn(INT32 playernum)
|
|||
// Make sure objectplace is OFF when you first start the level!
|
||||
OP_ResetObjectplace();
|
||||
|
||||
if (player->bot && playernum != consoleplayer)
|
||||
// Tailsbot
|
||||
if (player->bot == BOT_2PAI || player->bot == BOT_2PHUMAN)
|
||||
{ // Bots respawn next to their master.
|
||||
mobj_t *oldmo = NULL;
|
||||
|
||||
|
@ -2995,6 +3044,28 @@ void G_DoReborn(INT32 playernum)
|
|||
|
||||
return;
|
||||
}
|
||||
|
||||
// Additional players (e.g. independent bots) in Single Player
|
||||
if (playernum != consoleplayer && !(netgame || multiplayer))
|
||||
{
|
||||
mobj_t *oldmo = NULL;
|
||||
// Do nothing if out of lives
|
||||
if (player->lives <= 0)
|
||||
return;
|
||||
|
||||
// Otherwise do respawn, starting by removing the player object
|
||||
if (player->mo)
|
||||
{
|
||||
oldmo = player->mo;
|
||||
P_RemoveMobj(player->mo);
|
||||
}
|
||||
// Do spawning
|
||||
G_SpawnPlayer(playernum);
|
||||
if (oldmo)
|
||||
G_ChangePlayerReferences(oldmo, players[playernum].mo);
|
||||
|
||||
return; //Exit function to avoid proccing other SP related mechanics
|
||||
}
|
||||
|
||||
if (countdowntimeup || (!(netgame || multiplayer) && (gametyperules & GTR_CAMPAIGN)))
|
||||
resetlevel = true;
|
||||
|
@ -3176,7 +3247,7 @@ void G_AddPlayer(INT32 playernum)
|
|||
if (!playeringame[i])
|
||||
continue;
|
||||
|
||||
if (players[i].bot) // ignore dumb, stupid tails
|
||||
if (players[i].bot == BOT_2PAI || players[i].bot == BOT_2PHUMAN) // ignore dumb, stupid tails
|
||||
continue;
|
||||
|
||||
countplayers++;
|
||||
|
@ -3217,7 +3288,7 @@ boolean G_EnoughPlayersFinished(void)
|
|||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (!playeringame[i] || players[i].spectator || players[i].bot)
|
||||
if (!playeringame[i] || players[i].spectator || players[i].bot == BOT_2PAI || players[i].bot == BOT_2PHUMAN)
|
||||
continue;
|
||||
if (players[i].quittime > 30 * TICRATE)
|
||||
continue;
|
||||
|
|
|
@ -29,6 +29,8 @@
|
|||
#include "d_netcmd.h" // IsPlayerAdmin
|
||||
#include "m_menu.h" // Player Setup menu color stuff
|
||||
#include "m_misc.h" // M_MapNumber
|
||||
#include "b_bot.h" // B_UpdateBotleader
|
||||
#include "d_clisrv.h" // CL_RemovePlayer
|
||||
|
||||
#include "lua_script.h"
|
||||
#include "lua_libs.h"
|
||||
|
@ -3397,6 +3399,111 @@ static int lib_gAddGametype(lua_State *L)
|
|||
return 0;
|
||||
}
|
||||
|
||||
// Bot adding function!
|
||||
// Partly lifted from Got_AddPlayer
|
||||
static int lib_gAddPlayer(lua_State *L)
|
||||
{
|
||||
INT16 i, newplayernum, botcount = 1;
|
||||
player_t *newplayer;
|
||||
INT8 skinnum = 0, bot;
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (!playeringame[i])
|
||||
break;
|
||||
|
||||
if (players[i].bot)
|
||||
botcount++; // How many of us are there already?
|
||||
}
|
||||
if (i >= MAXPLAYERS)
|
||||
{
|
||||
lua_pushnil(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
newplayernum = i;
|
||||
|
||||
CL_ClearPlayer(newplayernum);
|
||||
|
||||
playeringame[newplayernum] = true;
|
||||
G_AddPlayer(newplayernum);
|
||||
newplayer = &players[newplayernum];
|
||||
|
||||
newplayer->jointime = 0;
|
||||
newplayer->quittime = 0;
|
||||
|
||||
// Set the bot name (defaults to Bot #)
|
||||
strcpy(player_names[newplayernum], va("Bot %d", botcount));
|
||||
|
||||
// Read the skin argument (defaults to Sonic)
|
||||
if (!lua_isnoneornil(L, 1))
|
||||
{
|
||||
skinnum = R_SkinAvailable(luaL_checkstring(L, 1));
|
||||
skinnum = skinnum < 0 ? 0 : skinnum;
|
||||
}
|
||||
|
||||
// Read the color (defaults to skin prefcolor)
|
||||
if (!lua_isnoneornil(L, 2))
|
||||
newplayer->skincolor = R_GetColorByName(luaL_checkstring(L, 2));
|
||||
else
|
||||
newplayer->skincolor = skins[newplayer->skin].prefcolor;
|
||||
|
||||
// Read the bot name, if given
|
||||
if (!lua_isnoneornil(L, 3))
|
||||
strcpy(player_names[newplayernum], luaL_checkstring(L, 3));
|
||||
|
||||
bot = luaL_optinteger(L, 4, 3);
|
||||
newplayer->bot = (bot >= BOT_NONE && bot <= BOT_MPAI) ? bot : BOT_MPAI;
|
||||
|
||||
// If our bot is a 2P type, we'll need to set its leader so it can spawn
|
||||
if (newplayer->bot == BOT_2PAI || newplayer->bot == BOT_2PHUMAN)
|
||||
B_UpdateBotleader(newplayer);
|
||||
|
||||
// Set the skin (can't do this until AFTER bot type is set!)
|
||||
SetPlayerSkinByNum(newplayernum, skinnum);
|
||||
|
||||
|
||||
if (netgame)
|
||||
{
|
||||
char joinmsg[256];
|
||||
|
||||
strcpy(joinmsg, M_GetText("\x82*Bot %s has joined the game (player %d)"));
|
||||
strcpy(joinmsg, va(joinmsg, player_names[newplayernum], newplayernum));
|
||||
HU_AddChatText(joinmsg, false);
|
||||
}
|
||||
|
||||
LUA_PushUserdata(L, newplayer, META_PLAYER);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// Bot removing function
|
||||
static int lib_gRemovePlayer(lua_State *L)
|
||||
{
|
||||
UINT8 pnum = -1;
|
||||
if (!lua_isnoneornil(L, 1))
|
||||
pnum = luaL_checkinteger(L, 1);
|
||||
else // No argument
|
||||
return luaL_error(L, "argument #1 not given (expected number)");
|
||||
if (pnum >= MAXPLAYERS) // Out of range
|
||||
return luaL_error(L, "playernum %d out of range (0 - %d)", pnum, MAXPLAYERS-1);
|
||||
if (playeringame[pnum]) // Found player
|
||||
{
|
||||
if (players[pnum].bot == BOT_NONE) // Can't remove clients.
|
||||
return luaL_error(L, "G_RemovePlayer can only be used on players with a bot value other than BOT_NONE.");
|
||||
else
|
||||
{
|
||||
players[pnum].removing = true;
|
||||
lua_pushboolean(L, true);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
// Fell through. Invalid player
|
||||
return LUA_ErrInvalid(L, "player_t");
|
||||
}
|
||||
|
||||
|
||||
static int Lcheckmapnumber (lua_State *L, int idx, const char *fun)
|
||||
{
|
||||
if (ISINLEVEL)
|
||||
|
@ -3983,6 +4090,8 @@ static luaL_Reg lib[] = {
|
|||
|
||||
// g_game
|
||||
{"G_AddGametype", lib_gAddGametype},
|
||||
{"G_AddPlayer", lib_gAddPlayer},
|
||||
{"G_RemovePlayer", lib_gRemovePlayer},
|
||||
{"G_BuildMapName",lib_gBuildMapName},
|
||||
{"G_BuildMapTitle",lib_gBuildMapTitle},
|
||||
{"G_FindMap",lib_gFindMap},
|
||||
|
|
|
@ -370,6 +370,12 @@ static int player_get(lua_State *L)
|
|||
lua_pushboolean(L, plr->outofcoop);
|
||||
else if (fastcmp(field,"bot"))
|
||||
lua_pushinteger(L, plr->bot);
|
||||
else if (fastcmp(field,"botleader"))
|
||||
LUA_PushUserdata(L, plr->botleader, META_PLAYER);
|
||||
else if (fastcmp(field,"lastbuttons"))
|
||||
lua_pushinteger(L, plr->lastbuttons);
|
||||
else if (fastcmp(field,"blocked"))
|
||||
lua_pushboolean(L, plr->blocked);
|
||||
else if (fastcmp(field,"jointime"))
|
||||
lua_pushinteger(L, plr->jointime);
|
||||
else if (fastcmp(field,"quittime"))
|
||||
|
@ -719,6 +725,17 @@ static int player_set(lua_State *L)
|
|||
plr->outofcoop = lua_toboolean(L, 3);
|
||||
else if (fastcmp(field,"bot"))
|
||||
return NOSET;
|
||||
else if (fastcmp(field,"botleader"))
|
||||
{
|
||||
player_t *player = NULL;
|
||||
if (!lua_isnil(L, 3))
|
||||
player = *((player_t **)luaL_checkudata(L, 3, META_PLAYER));
|
||||
plr->botleader = player;
|
||||
}
|
||||
else if (fastcmp(field,"lastbuttons"))
|
||||
plr->lastbuttons = (UINT16)luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"blocked"))
|
||||
plr->blocked = (UINT8)luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"jointime"))
|
||||
plr->jointime = (tic_t)luaL_checkinteger(L, 3);
|
||||
else if (fastcmp(field,"quittime"))
|
||||
|
|
|
@ -744,8 +744,8 @@ boolean P_LookForPlayers(mobj_t *actor, boolean allaround, boolean tracer, fixed
|
|||
if (player->mo->health <= 0)
|
||||
continue; // dead
|
||||
|
||||
if (player->bot)
|
||||
continue; // ignore bots
|
||||
if (player->bot == BOT_2PAI || player->bot == BOT_2PHUMAN)
|
||||
continue; // ignore followbots
|
||||
|
||||
if (player->quittime)
|
||||
continue; // Ignore uncontrolled bodies
|
||||
|
@ -3591,7 +3591,7 @@ void A_1upThinker(mobj_t *actor)
|
|||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (!playeringame[i] || players[i].bot || players[i].spectator)
|
||||
if (!playeringame[i] || players[i].bot == BOT_2PAI || players[i].bot == BOT_2PHUMAN || players[i].spectator)
|
||||
continue;
|
||||
|
||||
if (!players[i].mo)
|
||||
|
|
|
@ -151,7 +151,7 @@ boolean P_CanPickupItem(player_t *player, boolean weapon)
|
|||
if (!player->mo || player->mo->health <= 0)
|
||||
return false;
|
||||
|
||||
if (player->bot)
|
||||
if (player->bot && player->bot != BOT_MPAI)
|
||||
{
|
||||
if (weapon)
|
||||
return false;
|
||||
|
@ -178,7 +178,7 @@ void P_DoNightsScore(player_t *player)
|
|||
return; // Don't do any fancy shit for failures.
|
||||
|
||||
dummymo = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z+player->mo->height/2, MT_NIGHTSCORE);
|
||||
if (player->bot)
|
||||
if (player->bot && player->bot != BOT_MPAI)
|
||||
player = &players[consoleplayer];
|
||||
|
||||
if (G_IsSpecialStage(gamemap)) // Global link count? Maybe not a good idea...
|
||||
|
@ -470,14 +470,14 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
if (!(player->charability2 == CA2_MELEE && player->panim == PA_ABILITY2))
|
||||
{
|
||||
fixed_t setmomz = -toucher->momz; // Store this, momz get changed by P_DoJump within P_DoBubbleBounce
|
||||
|
||||
|
||||
if (elementalpierce == 2) // Reset bubblewrap, part 1
|
||||
P_DoBubbleBounce(player);
|
||||
toucher->momz = setmomz;
|
||||
if (elementalpierce == 2) // Reset bubblewrap, part 2
|
||||
{
|
||||
boolean underwater = toucher->eflags & MFE_UNDERWATER;
|
||||
|
||||
|
||||
if (underwater)
|
||||
toucher->momz /= 2;
|
||||
toucher->momz -= (toucher->momz/(underwater ? 8 : 4)); // Cap the height!
|
||||
|
@ -630,7 +630,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
// ***************************** //
|
||||
// Special Stage Token
|
||||
case MT_TOKEN:
|
||||
if (player->bot)
|
||||
if (player->bot && player->bot != BOT_MPAI)
|
||||
return;
|
||||
|
||||
P_AddPlayerScore(player, 1000);
|
||||
|
@ -670,7 +670,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
|
||||
// Emerald Hunt
|
||||
case MT_EMERHUNT:
|
||||
if (player->bot)
|
||||
if (player->bot && player->bot != BOT_MPAI)
|
||||
return;
|
||||
|
||||
if (hunt1 == special)
|
||||
|
@ -701,7 +701,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
case MT_EMERALD5:
|
||||
case MT_EMERALD6:
|
||||
case MT_EMERALD7:
|
||||
if (player->bot)
|
||||
if (player->bot && player->bot != BOT_MPAI)
|
||||
return;
|
||||
|
||||
if (special->threshold)
|
||||
|
@ -738,7 +738,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
// Secret emblem thingy
|
||||
case MT_EMBLEM:
|
||||
{
|
||||
if (demoplayback || player->bot)
|
||||
if (demoplayback || (player->bot && player->bot != BOT_MPAI))
|
||||
return;
|
||||
emblemlocations[special->health-1].collected = true;
|
||||
|
||||
|
@ -751,7 +751,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
// CTF Flags
|
||||
case MT_REDFLAG:
|
||||
case MT_BLUEFLAG:
|
||||
if (player->bot)
|
||||
if (player->bot && player->bot != BOT_MPAI)
|
||||
return;
|
||||
if (player->powers[pw_flashing] || player->tossdelay)
|
||||
return;
|
||||
|
@ -826,7 +826,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
{
|
||||
boolean spec = G_IsSpecialStage(gamemap);
|
||||
boolean cangiveemmy = false;
|
||||
if (player->bot)
|
||||
if (player->bot && player->bot != BOT_MPAI)
|
||||
return;
|
||||
if (player->exiting)
|
||||
return;
|
||||
|
@ -1072,7 +1072,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
}
|
||||
return;
|
||||
case MT_EGGCAPSULE:
|
||||
if (player->bot)
|
||||
if (player->bot && player->bot != BOT_MPAI)
|
||||
return;
|
||||
|
||||
// make sure everything is as it should be, THEN take rings from players in special stages
|
||||
|
@ -1164,7 +1164,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
}
|
||||
return;
|
||||
case MT_NIGHTSSUPERLOOP:
|
||||
if (player->bot || !(player->powers[pw_carry] == CR_NIGHTSMODE))
|
||||
if ((player->bot && player->bot != BOT_MPAI) || !(player->powers[pw_carry] == CR_NIGHTSMODE))
|
||||
return;
|
||||
if (!G_IsSpecialStage(gamemap))
|
||||
player->powers[pw_nights_superloop] = (UINT16)special->info->speed;
|
||||
|
@ -1186,7 +1186,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
}
|
||||
break;
|
||||
case MT_NIGHTSDRILLREFILL:
|
||||
if (player->bot || !(player->powers[pw_carry] == CR_NIGHTSMODE))
|
||||
if ((player->bot && player->bot != BOT_MPAI) || !(player->powers[pw_carry] == CR_NIGHTSMODE))
|
||||
return;
|
||||
if (!G_IsSpecialStage(gamemap))
|
||||
player->drillmeter = special->info->speed;
|
||||
|
@ -1208,7 +1208,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
}
|
||||
break;
|
||||
case MT_NIGHTSHELPER:
|
||||
if (player->bot || !(player->powers[pw_carry] == CR_NIGHTSMODE))
|
||||
if ((player->bot && player->bot != BOT_MPAI) || !(player->powers[pw_carry] == CR_NIGHTSMODE))
|
||||
return;
|
||||
if (!G_IsSpecialStage(gamemap))
|
||||
{
|
||||
|
@ -1240,7 +1240,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
}
|
||||
break;
|
||||
case MT_NIGHTSEXTRATIME:
|
||||
if (player->bot || !(player->powers[pw_carry] == CR_NIGHTSMODE))
|
||||
if ((player->bot && player->bot != BOT_MPAI) || !(player->powers[pw_carry] == CR_NIGHTSMODE))
|
||||
return;
|
||||
if (!G_IsSpecialStage(gamemap))
|
||||
{
|
||||
|
@ -1272,7 +1272,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
}
|
||||
break;
|
||||
case MT_NIGHTSLINKFREEZE:
|
||||
if (player->bot || !(player->powers[pw_carry] == CR_NIGHTSMODE))
|
||||
if ((player->bot && player->bot != BOT_MPAI) || !(player->powers[pw_carry] == CR_NIGHTSMODE))
|
||||
return;
|
||||
if (!G_IsSpecialStage(gamemap))
|
||||
{
|
||||
|
@ -1332,7 +1332,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
if (playeringame[i] && players[i].powers[pw_carry] == CR_NIGHTSMODE)
|
||||
players[i].drillmeter += TICRATE/2;
|
||||
}
|
||||
else if (player->bot)
|
||||
else if (player->bot && player->bot != BOT_MPAI)
|
||||
players[consoleplayer].drillmeter += TICRATE/2;
|
||||
else
|
||||
player->drillmeter += TICRATE/2;
|
||||
|
@ -1385,9 +1385,9 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
thinker_t *th;
|
||||
mobj_t *mo2;
|
||||
|
||||
if (player->bot)
|
||||
if (player->bot && player->bot != BOT_MPAI)
|
||||
return;
|
||||
|
||||
|
||||
// Initialize my junk
|
||||
junk.tags.tags = NULL;
|
||||
junk.tags.count = 0;
|
||||
|
@ -1423,7 +1423,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
return;
|
||||
}
|
||||
case MT_FIREFLOWER:
|
||||
if (player->bot)
|
||||
if (player->bot && player->bot != BOT_MPAI)
|
||||
return;
|
||||
|
||||
S_StartSound(toucher, sfx_mario3);
|
||||
|
@ -1617,7 +1617,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
|
||||
if (special->tracer && !(special->tracer->flags2 & MF2_STRONGBOX))
|
||||
macespin = true;
|
||||
|
||||
|
||||
if (macespin ? (player->powers[pw_ignorelatch] & (1<<15)) : (player->powers[pw_ignorelatch]))
|
||||
return;
|
||||
|
||||
|
@ -1685,7 +1685,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
return; // Only go in the mouth
|
||||
|
||||
// Eaten by player!
|
||||
if ((!player->bot) && (player->powers[pw_underwater] && player->powers[pw_underwater] <= 12*TICRATE + 1))
|
||||
if ((!player->bot || player->bot == BOT_MPAI) && (player->powers[pw_underwater] && player->powers[pw_underwater] <= 12*TICRATE + 1))
|
||||
{
|
||||
player->powers[pw_underwater] = underwatertics + 1;
|
||||
P_RestoreMusic(player);
|
||||
|
@ -1696,7 +1696,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
|
||||
if (!player->climbing)
|
||||
{
|
||||
if (player->bot && toucher->state-states != S_PLAY_GASP)
|
||||
if (player->bot && player->bot != BOT_MPAI && toucher->state-states != S_PLAY_GASP)
|
||||
S_StartSound(toucher, special->info->deathsound); // Force it to play a sound for bots
|
||||
P_SetPlayerMobjState(toucher, S_PLAY_GASP);
|
||||
P_ResetPlayer(player);
|
||||
|
@ -1704,7 +1704,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
|
||||
toucher->momx = toucher->momy = toucher->momz = 0;
|
||||
|
||||
if (player->bot)
|
||||
if (player->bot && player->bot != BOT_MPAI)
|
||||
return;
|
||||
else
|
||||
break;
|
||||
|
@ -1736,7 +1736,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
return;
|
||||
|
||||
case MT_MINECARTSPAWNER:
|
||||
if (!player->bot && special->fuse <= TICRATE && player->powers[pw_carry] != CR_MINECART && !(player->powers[pw_ignorelatch] & (1<<15)))
|
||||
if (!player->bot && player->bot != BOT_MPAI && special->fuse <= TICRATE && player->powers[pw_carry] != CR_MINECART && !(player->powers[pw_ignorelatch] & (1<<15)))
|
||||
{
|
||||
mobj_t *mcart = P_SpawnMobj(special->x, special->y, special->z, MT_MINECART);
|
||||
P_SetTarget(&mcart->target, toucher);
|
||||
|
@ -1789,7 +1789,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
|
|||
}
|
||||
return;
|
||||
default: // SOC or script pickup
|
||||
if (player->bot)
|
||||
if (player->bot && player->bot != BOT_MPAI)
|
||||
return;
|
||||
P_SetTarget(&special->target, toucher);
|
||||
break;
|
||||
|
@ -1813,7 +1813,7 @@ void P_TouchStarPost(mobj_t *post, player_t *player, boolean snaptopost)
|
|||
mobj_t *toucher = player->mo;
|
||||
mobj_t *checkbase = snaptopost ? post : toucher;
|
||||
|
||||
if (player->bot)
|
||||
if (player->bot && player->bot != BOT_MPAI)
|
||||
return;
|
||||
// In circuit, player must have touched all previous starposts
|
||||
if (circuitmap
|
||||
|
@ -2555,7 +2555,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
|
|||
|
||||
if ((target->player->lives <= 1) && (netgame || multiplayer) && G_GametypeUsesCoopLives() && (cv_cooplives.value == 0))
|
||||
;
|
||||
else if (!target->player->bot && !target->player->spectator && (target->player->lives != INFLIVES)
|
||||
else if ((!target->player->bot || target->player->bot == BOT_MPAI) && !target->player->spectator && (target->player->lives != INFLIVES)
|
||||
&& G_GametypeUsesLives())
|
||||
{
|
||||
if (!(target->player->pflags & PF_FINISHED))
|
||||
|
@ -3475,7 +3475,7 @@ void P_SpecialStageDamage(player_t *player, mobj_t *inflictor, mobj_t *source)
|
|||
if (inflictor && inflictor->type == MT_LHRT)
|
||||
return;
|
||||
|
||||
if (player->powers[pw_shield] || player->bot) //If One-Hit Shield
|
||||
if (player->powers[pw_shield] || (player->bot && player->bot != BOT_MPAI)) //If One-Hit Shield
|
||||
{
|
||||
P_RemoveShield(player);
|
||||
S_StartSound(player->mo, sfx_shldls); // Ba-Dum! Shield loss.
|
||||
|
@ -3566,7 +3566,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
|
|||
return false;
|
||||
|
||||
// Make sure that boxes cannot be popped by enemies, red rings, etc.
|
||||
if (target->flags & MF_MONITOR && ((!source || !source->player || source->player->bot)
|
||||
if (target->flags & MF_MONITOR && ((!source || !source->player || (source->player->bot && source->player->bot != BOT_MPAI))
|
||||
|| (inflictor && (inflictor->type == MT_REDRING || (inflictor->type >= MT_THROWNBOUNCE && inflictor->type <= MT_THROWNGRENADE)))))
|
||||
return false;
|
||||
}
|
||||
|
@ -3701,7 +3701,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
|
|||
}
|
||||
else if (LUA_HookMobjDamage(target, inflictor, source, damage, damagetype))
|
||||
return true;
|
||||
else if (player->powers[pw_shield] || (player->bot && !ultimatemode)) //If One-Hit Shield
|
||||
else if (player->powers[pw_shield] || (player->bot && player->bot != BOT_MPAI && !ultimatemode)) //If One-Hit Shield
|
||||
{
|
||||
P_ShieldDamage(player, inflictor, source, damage, damagetype);
|
||||
damage = 0;
|
||||
|
|
12
src/p_mobj.c
12
src/p_mobj.c
|
@ -1839,10 +1839,8 @@ void P_XYMovement(mobj_t *mo)
|
|||
// blocked move
|
||||
moved = false;
|
||||
|
||||
if (player) {
|
||||
if (player->bot)
|
||||
B_MoveBlocked(player);
|
||||
}
|
||||
if (player)
|
||||
B_MoveBlocked(player);
|
||||
|
||||
if (LUA_HookMobj(mo, MOBJ_HOOK(MobjMoveBlocked)))
|
||||
{
|
||||
|
@ -4140,7 +4138,7 @@ boolean P_BossTargetPlayer(mobj_t *actor, boolean closest)
|
|||
|
||||
player = &players[actor->lastlook];
|
||||
|
||||
if (player->pflags & PF_INVIS || player->bot || player->spectator)
|
||||
if (player->pflags & PF_INVIS || player->bot == BOT_2PAI || player->bot == BOT_2PHUMAN || player->spectator)
|
||||
continue; // ignore notarget
|
||||
|
||||
if (!player->mo || P_MobjWasRemoved(player->mo))
|
||||
|
@ -4181,7 +4179,7 @@ boolean P_SupermanLook4Players(mobj_t *actor)
|
|||
if (players[c].pflags & PF_INVIS)
|
||||
continue; // ignore notarget
|
||||
|
||||
if (!players[c].mo || players[c].bot)
|
||||
if (!players[c].mo || players[c].bot == BOT_2PAI || players[c].bot == BOT_2PHUMAN)
|
||||
continue;
|
||||
|
||||
if (players[c].mo->health <= 0)
|
||||
|
@ -7310,7 +7308,7 @@ static void P_RosySceneryThink(mobj_t *mobj)
|
|||
continue;
|
||||
if (!players[i].mo)
|
||||
continue;
|
||||
if (players[i].bot)
|
||||
if (players[i].bot == BOT_2PAI || players[i].bot == BOT_2PHUMAN)
|
||||
continue;
|
||||
if (!players[i].mo->health)
|
||||
continue;
|
||||
|
|
|
@ -193,6 +193,19 @@ static void P_NetArchivePlayers(void)
|
|||
WRITEUINT32(save_p, players[i].dashmode);
|
||||
WRITEUINT32(save_p, players[i].skidtime);
|
||||
|
||||
//////////
|
||||
// Bots //
|
||||
//////////
|
||||
WRITEUINT8(save_p, players[i].bot);
|
||||
WRITEUINT8(save_p, players[i].botmem.lastForward);
|
||||
WRITEUINT8(save_p, players[i].botmem.lastBlocked);
|
||||
WRITEUINT8(save_p, players[i].botmem.catchup_tics);
|
||||
WRITEUINT8(save_p, players[i].botmem.thinkstate);
|
||||
WRITEUINT8(save_p, players[i].removing);
|
||||
|
||||
WRITEUINT8(save_p, players[i].blocked);
|
||||
WRITEUINT16(save_p, players[i].lastbuttons);
|
||||
|
||||
////////////////////////////
|
||||
// Conveyor Belt Movement //
|
||||
////////////////////////////
|
||||
|
@ -407,6 +420,20 @@ static void P_NetUnArchivePlayers(void)
|
|||
players[i].dashmode = READUINT32(save_p); // counter for dashmode ability
|
||||
players[i].skidtime = READUINT32(save_p); // Skid timer
|
||||
|
||||
//////////
|
||||
// Bots //
|
||||
//////////
|
||||
players[i].bot = READUINT8(save_p);
|
||||
|
||||
players[i].botmem.lastForward = READUINT8(save_p);
|
||||
players[i].botmem.lastBlocked = READUINT8(save_p);
|
||||
players[i].botmem.catchup_tics = READUINT8(save_p);
|
||||
players[i].botmem.thinkstate = READUINT8(save_p);
|
||||
players[i].removing = READUINT8(save_p);
|
||||
|
||||
players[i].blocked = READUINT8(save_p);
|
||||
players[i].lastbuttons = READUINT16(save_p);
|
||||
|
||||
////////////////////////////
|
||||
// Conveyor Belt Movement //
|
||||
////////////////////////////
|
||||
|
|
57
src/p_user.c
57
src/p_user.c
|
@ -777,7 +777,7 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime)
|
|||
UINT8 oldmare, oldmarelap, oldmarebonuslap;
|
||||
|
||||
// Bots can't be NiGHTSerized, silly!1 :P
|
||||
if (player->bot)
|
||||
if (player->bot == BOT_2PAI || player->bot == BOT_2PHUMAN)
|
||||
return;
|
||||
|
||||
if (player->powers[pw_carry] != CR_NIGHTSMODE)
|
||||
|
@ -1188,9 +1188,9 @@ void P_GivePlayerRings(player_t *player, INT32 num_rings)
|
|||
{
|
||||
if (!player)
|
||||
return;
|
||||
|
||||
if (player->bot)
|
||||
player = &players[consoleplayer];
|
||||
|
||||
if ((player->bot == BOT_2PAI || player->bot == BOT_2PHUMAN) && player->botleader)
|
||||
player = player->botleader;
|
||||
|
||||
if (!player->mo)
|
||||
return;
|
||||
|
@ -1234,8 +1234,8 @@ void P_GivePlayerSpheres(player_t *player, INT32 num_spheres)
|
|||
if (!player)
|
||||
return;
|
||||
|
||||
if (player->bot)
|
||||
player = &players[consoleplayer];
|
||||
if ((player->bot == BOT_2PAI || player->bot == BOT_2PHUMAN) && player->botleader)
|
||||
player = player->botleader;
|
||||
|
||||
if (!player->mo)
|
||||
return;
|
||||
|
@ -1261,8 +1261,8 @@ void P_GivePlayerLives(player_t *player, INT32 numlives)
|
|||
if (!player)
|
||||
return;
|
||||
|
||||
if (player->bot)
|
||||
player = &players[consoleplayer];
|
||||
if ((player->bot == BOT_2PAI || player->bot == BOT_2PHUMAN) && player->botleader)
|
||||
player = player->botleader;
|
||||
|
||||
if (gamestate == GS_LEVEL)
|
||||
{
|
||||
|
@ -1367,8 +1367,8 @@ void P_AddPlayerScore(player_t *player, UINT32 amount)
|
|||
{
|
||||
UINT32 oldscore;
|
||||
|
||||
if (player->bot)
|
||||
player = &players[consoleplayer];
|
||||
if ((player->bot == BOT_2PAI || player->bot == BOT_2PHUMAN) && player->botleader)
|
||||
player = player->botleader;
|
||||
|
||||
// NiGHTS does it different!
|
||||
if (gamestate == GS_LEVEL && mapheaderinfo[gamemap-1]->typeoflevel & TOL_NIGHTS)
|
||||
|
@ -5378,7 +5378,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
|
|||
player->powers[pw_tailsfly] = tailsflytics + 1; // Set the fly timer
|
||||
|
||||
player->pflags &= ~(PF_JUMPED|PF_NOJUMPDAMAGE|PF_SPINNING|PF_STARTDASH);
|
||||
if (player->bot == 1)
|
||||
if (player->bot == BOT_2PAI)
|
||||
player->pflags |= PF_THOKKED;
|
||||
else
|
||||
player->pflags |= (PF_THOKKED|PF_CANCARRY);
|
||||
|
@ -5965,22 +5965,6 @@ static void P_3dMovement(player_t *player)
|
|||
acceleration = 96 + (FixedDiv(player->speed, player->mo->scale)>>FRACBITS) * 40;
|
||||
topspeed = normalspd;
|
||||
}
|
||||
else if (player->bot)
|
||||
{ // Bot steals player 1's stats
|
||||
normalspd = FixedMul(players[consoleplayer].normalspeed, player->mo->scale);
|
||||
thrustfactor = players[consoleplayer].thrustfactor;
|
||||
acceleration = players[consoleplayer].accelstart + (FixedDiv(player->speed, player->mo->scale)>>FRACBITS) * players[consoleplayer].acceleration;
|
||||
|
||||
if (player->powers[pw_tailsfly])
|
||||
topspeed = normalspd/2;
|
||||
else if (player->mo->eflags & (MFE_UNDERWATER|MFE_GOOWATER))
|
||||
{
|
||||
topspeed = normalspd/2;
|
||||
acceleration = 2*acceleration/3;
|
||||
}
|
||||
else
|
||||
topspeed = normalspd;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (player->powers[pw_super] || player->powers[pw_sneakers])
|
||||
|
@ -9510,11 +9494,11 @@ static void P_DeathThink(player_t *player)
|
|||
if (player->deadtimer < INT32_MAX)
|
||||
player->deadtimer++;
|
||||
|
||||
if (player->bot) // don't allow bots to do any of the below, B_CheckRespawn does all they need for respawning already
|
||||
if (player->bot == BOT_2PAI || player->bot == BOT_2PHUMAN) // don't allow followbots to do any of the below, B_CheckRespawn does all they need for respawning already
|
||||
goto notrealplayer;
|
||||
|
||||
// continue logic
|
||||
if (!(netgame || multiplayer) && player->lives <= 0)
|
||||
if (!(netgame || multiplayer) && player->lives <= 0 && player == &players[consoleplayer]) //Extra players in SP can't be allowed to continue or end game
|
||||
{
|
||||
if (player->deadtimer > (3*TICRATE) && (cmd->buttons & BT_SPIN || cmd->buttons & BT_JUMP) && (!continuesInSession || player->continues > 0))
|
||||
G_UseContinue();
|
||||
|
@ -11487,6 +11471,9 @@ void P_PlayerThink(player_t *player)
|
|||
I_Error("p_playerthink: players[%s].mo == NULL", sizeu1(playeri));
|
||||
#endif
|
||||
|
||||
// Reset terrain blocked status for this frame
|
||||
player->blocked = false;
|
||||
|
||||
// todo: Figure out what is actually causing these problems in the first place...
|
||||
if (player->mo->health <= 0 && player->playerstate == PST_LIVE) //you should be DEAD!
|
||||
{
|
||||
|
@ -11494,7 +11481,7 @@ void P_PlayerThink(player_t *player)
|
|||
player->playerstate = PST_DEAD;
|
||||
}
|
||||
|
||||
if (player->bot)
|
||||
if (player->bot == BOT_2PAI || player->bot == BOT_2PHUMAN)
|
||||
{
|
||||
if (player->playerstate == PST_LIVE || player->playerstate == PST_DEAD)
|
||||
{
|
||||
|
@ -11638,7 +11625,7 @@ void P_PlayerThink(player_t *player)
|
|||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (!playeringame[i] || players[i].spectator || players[i].bot)
|
||||
if (!playeringame[i] || players[i].spectator || players[i].bot == BOT_2PAI || players[i].bot == BOT_2PHUMAN)
|
||||
continue;
|
||||
if (players[i].lives <= 0)
|
||||
continue;
|
||||
|
@ -11669,8 +11656,8 @@ void P_PlayerThink(player_t *player)
|
|||
INT32 i, total = 0, exiting = 0;
|
||||
|
||||
for (i = 0; i < MAXPLAYERS; i++)
|
||||
{
|
||||
if (!playeringame[i] || players[i].spectator || players[i].bot)
|
||||
{
|
||||
if (!playeringame[i] || players[i].spectator || players[i].bot == BOT_2PAI || players[i].bot == BOT_2PHUMAN)
|
||||
continue;
|
||||
if (players[i].quittime > 30 * TICRATE)
|
||||
continue;
|
||||
|
@ -12610,8 +12597,8 @@ void P_PlayerAfterThink(player_t *player)
|
|||
player->mo->momy = tails->momy;
|
||||
player->mo->momz = tails->momz;
|
||||
}
|
||||
|
||||
if (G_CoopGametype() && tails->player && tails->player->bot != 1)
|
||||
|
||||
if (G_CoopGametype() && tails->player && tails->player->bot != BOT_2PAI)
|
||||
{
|
||||
player->mo->angle = tails->angle;
|
||||
|
||||
|
|
|
@ -242,6 +242,11 @@ boolean R_SkinUsable(INT32 playernum, INT32 skinnum)
|
|||
// Force 3.
|
||||
return true;
|
||||
}
|
||||
if (playernum != -1 && players[playernum].bot)
|
||||
{
|
||||
//Force 4.
|
||||
return true;
|
||||
}
|
||||
|
||||
// We will now check if this skin is supposed to be locked or not.
|
||||
|
||||
|
|
Loading…
Reference in a new issue