Merge branch 'cobalt-ai' into 'master'

CobaltBW's Tails AI

See merge request STJr/SRB2Internal!358
This commit is contained in:
MascaraSnake 2019-11-18 17:26:28 -05:00
commit 2418e33a00
7 changed files with 312 additions and 42 deletions

View File

@ -24,12 +24,48 @@ static boolean lastForward = false;
static boolean lastBlocked = false; static boolean lastBlocked = false;
static boolean blocked = 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 mobj_t *overlay;
static inline void B_ResetAI(void)
{
jump_last = false;
spin_last = false;
anxiety = 0;
panic = false;
flymode = 0;
spinmode = false;
thinkfly = false;
}
static inline void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd) static inline void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
{ {
boolean forward=false, backward=false, left=false, right=false, jump=false, spin=false; boolean forward=false, backward=false, left=false, right=false, jump=false, spin=false;
angle_t angle;
INT16 rangle; player_t *player = sonic->player, *bot = tails->player;
fixed_t dist; ticcmd_t *pcmd = &player->cmd;
boolean water = tails->eflags & MFE_UNDERWATER;
boolean flip = P_MobjFlip(tails);
boolean _2d = (tails->flags2 & MF2_TWOD) || twodlevel;
fixed_t scale = tails->scale;
fixed_t dist = P_AproxDistance(sonic->x - tails->x, sonic->y - tails->y);
fixed_t zdist = flip * (sonic->z - tails->z);
angle_t ang = R_PointToAngle2(tails->x, tails->y, sonic->x, sonic->y);
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 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
// We can't follow Sonic if he's not around! // We can't follow Sonic if he's not around!
if (!sonic || sonic->health <= 0) if (!sonic || sonic->health <= 0)
@ -58,46 +94,260 @@ static inline void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cm
return; return;
} }
// Gather data about the environment // Adapted from CobaltBW's tails_AI.wad
dist = P_AproxDistance(tails->x-sonic->x, tails->y-sonic->y);
if (tails->player->pflags & PF_STARTDASH) // Check water
angle = sonic->angle; if (water)
{
followmin = 0;
followthres = 16*scale;
followmax >>= 1;
thinkfly = false;
}
// Check anxiety
if (spinmode)
{
anxiety = 0;
panic = false;
}
else if (dist > followmax || zdist > comfortheight || stalled)
{
anxiety = min(anxiety + 2, 70);
if (anxiety >= 70)
panic = true;
}
else else
angle = R_PointToAngle2(tails->x, tails->y, sonic->x, sonic->y); {
anxiety = max(anxiety - 1, 0);
// Decide which direction to turn panic = false;
angle = (tails->angle - angle);
if (angle < ANGLE_180) {
right = true; // We need to turn right
rangle = AngleFixed(angle)>>FRACBITS;
} else {
left = true; // We need to turn left
rangle = 360-(AngleFixed(angle)>>FRACBITS);
} }
// Decide to move forward if you're finished turning // Orientation
if (abs(rangle) < 10) { // We're facing the right way? if ((bot->pflags & (PF_SPINNING|PF_STARTDASH)) || flymode == 2)
left = right = false; // Stop turning {
forward = true; // and walk forward instead. cmd->angleturn = (sonic->angle - tails->angle) >> FRACBITS;
}
else
{
cmd->angleturn = (ang - tails->angle) >> FRACBITS;
} }
if (dist < (sonic->radius+tails->radius)*3) // We're close enough?
forward = false; // Stop walking.
// Decide when to jump // ********
if (!(tails->player->pflags & (PF_JUMPED|PF_JUMPDOWN))) { // We're not jumping yet... // FLY MODE
if (forward && lastForward && blocked && lastBlocked) // We've been stopped by a wall or something // spinmode check
jump = true; // Try to jump up if (spinmode)
} else if ((tails->player->pflags & (PF_JUMPDOWN|PF_JUMPED)) == (PF_JUMPDOWN|PF_JUMPED)) { // When we're already jumping... thinkfly = false;
if (lastForward && blocked) // We're still stuck on something? else
{
// Activate co-op flight
if (thinkfly && player->pflags & PF_JUMPED)
{
if (!jump_last)
{
jump = true;
flymode = 1;
thinkfly = false;
bot->pflags |= PF_CANCARRY;
}
}
// Check positioning
// Thinker for co-op flight
if (!(water || pmom || bmom)
&& (dist < touchdist)
&& !(pcmd->forwardmove || pcmd->sidemove || player->dashspeed)
&& P_IsObjectOnGround(sonic) && P_IsObjectOnGround(tails)
&& !(player->pflags & PF_STASIS)
&& bot->charability == CA_FLY)
thinkfly = true;
else
thinkfly = false;
// Ready for takeoff
if (flymode == 1)
{
thinkfly = false;
if (zdist < -64*scale || (flip * tails->momz) > scale) // Make sure we're not too high up
spin = true;
else if (!jump_last)
jump = true;
// Abort if the player moves away or spins
if (dist > followthres || player->dashspeed)
flymode = 0;
// Set carried state
if (player->powers[pw_carry] == CR_PLAYER && sonic->tracer == tails)
{
flymode = 2;
}
}
// Read player inputs while carrying
else if (flymode == 2)
{
cmd->forwardmove = pcmd->forwardmove;
cmd->sidemove = pcmd->sidemove;
if (pcmd->buttons & BT_USE)
{
spin = true;
jump = false;
}
else if (!jump_last)
jump = true;
// End flymode
if (player->powers[pw_carry] != CR_PLAYER)
{
flymode = 0;
}
}
}
if (flymode && P_IsObjectOnGround(tails) && !(pcmd->buttons & BT_JUMP))
flymode = 0;
// ********
// SPINNING
if (panic || flymode || !(player->pflags & PF_SPINNING) || (player->pflags & PF_JUMPED))
spinmode = false;
else
{
if (!_2d)
{
// Spindash
if (player->dashspeed)
{
if (dist < followthres && dist > touchdist) // Do positioning
{
cmd->angleturn = (ang - tails->angle) >> FRACBITS;
cmd->forwardmove = 50;
spinmode = true;
}
else if (dist < touchdist)
{
if (!bmom && (!(bot->pflags & PF_SPINNING) || (bot->dashspeed && bot->pflags & PF_SPINNING)))
{
cmd->angleturn = (sonic->angle - tails->angle) >> FRACBITS;
spin = true;
}
spinmode = true;
}
else
spinmode = false;
}
// Spin
else if (player->dashspeed == bot->dashspeed && player->pflags & PF_SPINNING)
{
if (bot->pflags & PF_SPINNING || !spin_last)
{
spin = true;
cmd->angleturn = (sonic->angle - tails->angle) >> FRACBITS;
cmd->forwardmove = MAXPLMOVE;
spinmode = true;
}
else
spinmode = false;
}
}
// 2D mode
else
{
if (((player->dashspeed && !bmom) || (player->dashspeed == bot->dashspeed && (player->pflags & PF_SPINNING)))
&& ((bot->pflags & PF_SPINNING) || !spin_last))
{
spin = true;
spinmode = true;
}
}
}
// ********
// FOLLOW
if (!(flymode || spinmode))
{
// Too far
if (panic || dist > followthres)
{
if (!_2d)
cmd->forwardmove = MAXPLMOVE;
else if (sonic->x > tails->x)
cmd->sidemove = MAXPLMOVE;
else
cmd->sidemove = -MAXPLMOVE;
}
// Within threshold
else if (!panic && dist > followmin && abs(zdist) < 192*scale)
{
if (!_2d)
cmd->forwardmove = FixedHypot(pcmd->forwardmove, pcmd->sidemove);
else
cmd->sidemove = pcmd->sidemove;
}
// Below min
else if (dist < followmin)
{
// Copy inputs
cmd->angleturn = (sonic->angle - tails->angle) >> FRACBITS;
bot->drawangle = ang;
cmd->forwardmove = 8 * pcmd->forwardmove / 10;
cmd->sidemove = 8 * pcmd->sidemove / 10;
}
}
// ********
// JUMP
if (!(flymode || spinmode))
{
// Flying catch-up
if (bot->pflags & PF_THOKKED)
{
cmd->forwardmove = min(MAXPLMOVE, (dist/scale)>>3);
if (zdist < -64*scale)
spin = true;
else if (zdist > 0 && !jump_last)
jump = true;
}
// Just landed
if (tails->eflags & MFE_JUSTHITFLOOR)
jump = false;
// 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)
//|| (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))
jump = true; jump = true;
if (sonic->floorz > tails->floorz) // He's still above us? Jump HIGHER, then! // Start flying
else if (bot->pflags & PF_JUMPED && panic && !jump_last && bot->charability == CA_FLY)
jump = true; jump = true;
} }
// Decide when to spin // ********
if (sonic->player->pflags & PF_STARTDASH // HISTORY
&& (tails->player->pflags & PF_STARTDASH || (P_AproxDistance(tails->momx, tails->momy) < 2*FRACUNIT && !forward))) jump_last = jump;
spin = true; spin_last = spin;
// ********
// Thinkfly overlay
if (thinkfly)
{
if (overlay == NULL)
{
overlay = P_SpawnMobjFromMobj(tails, 0, 0, 0, MT_OVERLAY);
P_SetTarget(&overlay->target, tails);
P_SetMobjState(overlay, S_FLIGHTINDICATOR);
}
}
else if (overlay != NULL)
{
P_RemoveMobj(overlay);
overlay = NULL;
}
// Turn the virtual keypresses into ticcmd_t. // Turn the virtual keypresses into ticcmd_t.
B_KeysToTiccmd(tails, cmd, forward, backward, left, right, false, false, jump, spin); B_KeysToTiccmd(tails, cmd, forward, backward, left, right, false, false, jump, spin);
@ -141,7 +391,7 @@ 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) 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)
{ {
// don't try to do stuff if your sonic is in a minecart or something // don't try to do stuff if your sonic is in a minecart or something
if (players[consoleplayer].powers[pw_carry]) if (players[consoleplayer].powers[pw_carry] && players[consoleplayer].powers[pw_carry] != CR_PLAYER)
return; return;
// Turn the virtual keypresses into ticcmd_t. // Turn the virtual keypresses into ticcmd_t.
if (twodlevel || mo->flags2 & MF2_TWOD) { if (twodlevel || mo->flags2 & MF2_TWOD) {
@ -179,6 +429,7 @@ void B_KeysToTiccmd(mobj_t *mo, ticcmd_t *cmd, boolean forward, boolean backward
cmd->sidemove += MAXPLMOVE<<FRACBITS>>16; cmd->sidemove += MAXPLMOVE<<FRACBITS>>16;
} }
} else { } else {
angle_t angle;
if (forward) if (forward)
cmd->forwardmove += MAXPLMOVE<<FRACBITS>>16; cmd->forwardmove += MAXPLMOVE<<FRACBITS>>16;
if (backward) if (backward)
@ -191,6 +442,13 @@ void B_KeysToTiccmd(mobj_t *mo, ticcmd_t *cmd, boolean forward, boolean backward
cmd->sidemove -= MAXPLMOVE<<FRACBITS>>16; cmd->sidemove -= MAXPLMOVE<<FRACBITS>>16;
if (straferight) if (straferight)
cmd->sidemove += MAXPLMOVE<<FRACBITS>>16; cmd->sidemove += MAXPLMOVE<<FRACBITS>>16;
// cap inputs so the bot can't accelerate faster diagonally
angle = R_PointToAngle2(0, 0, cmd->sidemove << FRACBITS, cmd->forwardmove << FRACBITS);
INT32 maxforward = abs(P_ReturnThrustY(NULL, angle, MAXPLMOVE));
INT32 maxside = abs(P_ReturnThrustX(NULL, angle, MAXPLMOVE));
cmd->forwardmove = max(min(cmd->forwardmove, maxforward), -maxforward);
cmd->sidemove = max(min(cmd->sidemove, maxside), -maxside);
} }
if (jump) if (jump)
cmd->buttons |= BT_JUMP; cmd->buttons |= BT_JUMP;
@ -217,7 +475,7 @@ boolean B_CheckRespawn(player_t *player)
// If he's doing any of these things, he probably doesn't want to see us. // If he's doing any of these things, he probably doesn't want to see us.
if (sonic->player->pflags & (PF_GLIDING|PF_SLIDING|PF_BOUNCING) if (sonic->player->pflags & (PF_GLIDING|PF_SLIDING|PF_BOUNCING)
|| (sonic->player->panim != PA_IDLE && sonic->player->panim != PA_WALK) || (sonic->player->panim != PA_IDLE && sonic->player->panim != PA_WALK)
|| (sonic->player->powers[pw_carry])) || (sonic->player->powers[pw_carry] && sonic->player->powers[pw_carry] != CR_PLAYER))
return false; return false;
// Low ceiling, do not want! // Low ceiling, do not want!
@ -252,6 +510,8 @@ void B_RespawnBot(INT32 playernum)
if (!sonic || sonic->health <= 0) if (!sonic || sonic->health <= 0)
return; return;
B_ResetAI();
player->bot = 1; player->bot = 1;
P_SpawnPlayer(playernum); P_SpawnPlayer(playernum);
tails = player->mo; tails = player->mo;

View File

@ -7179,6 +7179,8 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_FOUR2", "S_FOUR2",
"S_FIVE2", "S_FIVE2",
"S_FLIGHTINDICATOR",
"S_LOCKON1", "S_LOCKON1",
"S_LOCKON2", "S_LOCKON2",
"S_LOCKON3", "S_LOCKON3",

View File

@ -506,6 +506,7 @@ light_t *t_lspr[NUMSPRITES] =
// Game Indicators // Game Indicators
&lspr[NOLIGHT], // SPR_SCOR &lspr[NOLIGHT], // SPR_SCOR
&lspr[NOLIGHT], // SPR_DRWN &lspr[NOLIGHT], // SPR_DRWN
&lspr[NOLIGHT], // SPR_FLII
&lspr[NOLIGHT], // SPR_LCKN &lspr[NOLIGHT], // SPR_LCKN
&lspr[NOLIGHT], // SPR_TTAG &lspr[NOLIGHT], // SPR_TTAG
&lspr[NOLIGHT], // SPR_GFLG &lspr[NOLIGHT], // SPR_GFLG

View File

@ -403,6 +403,7 @@ char sprnames[NUMSPRITES + 1][5] =
// Game Indicators // Game Indicators
"SCOR", // Score logo "SCOR", // Score logo
"DRWN", // Drowning Timer "DRWN", // Drowning Timer
"FLII", // Flight indicator
"LCKN", // Target "LCKN", // Target
"TTAG", // Tag Sign "TTAG", // Tag Sign
"GFLG", // Got Flag sign "GFLG", // Got Flag sign
@ -3341,6 +3342,9 @@ state_t states[NUMSTATES] =
{SPR_DRWN, 10, 40, {NULL}, 0, 0, S_NULL}, // S_FOUR2 {SPR_DRWN, 10, 40, {NULL}, 0, 0, S_NULL}, // S_FOUR2
{SPR_DRWN, 11, 40, {NULL}, 0, 0, S_NULL}, // S_FIVE2 {SPR_DRWN, 11, 40, {NULL}, 0, 0, S_NULL}, // S_FIVE2
// Flight indicator
{SPR_FLII, FF_FULLBRIGHT|FF_ANIMATE|0, -1, {NULL}, 4, 4, S_NULL}, // S_FLIGHTINDICATOR
{SPR_LCKN, FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL}, // S_LOCKON1 {SPR_LCKN, FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL}, // S_LOCKON1
{SPR_LCKN, 1|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL}, // S_LOCKON2 {SPR_LCKN, 1|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL}, // S_LOCKON2
{SPR_LCKN, 2|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL}, // S_LOCKON3 {SPR_LCKN, 2|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL}, // S_LOCKON3

View File

@ -668,6 +668,7 @@ typedef enum sprite
// Game Indicators // Game Indicators
SPR_SCOR, // Score logo SPR_SCOR, // Score logo
SPR_DRWN, // Drowning Timer SPR_DRWN, // Drowning Timer
SPR_FLII, // AI flight indicator
SPR_LCKN, // Target SPR_LCKN, // Target
SPR_TTAG, // Tag Sign SPR_TTAG, // Tag Sign
SPR_GFLG, // Got Flag sign SPR_GFLG, // Got Flag sign
@ -3479,6 +3480,8 @@ typedef enum state
S_FOUR2, S_FOUR2,
S_FIVE2, S_FIVE2,
S_FLIGHTINDICATOR,
S_LOCKON1, S_LOCKON1,
S_LOCKON2, S_LOCKON2,
S_LOCKON3, S_LOCKON3,

View File

@ -595,9 +595,6 @@ static void P_DoTailsCarry(player_t *sonic, player_t *tails)
if (sonic->pflags & PF_FINISHED) if (sonic->pflags & PF_FINISHED)
return; return;
if (tails->bot == 1)
return;
if ((sonic->mo->eflags & MFE_VERTICALFLIP) != (tails->mo->eflags & MFE_VERTICALFLIP)) if ((sonic->mo->eflags & MFE_VERTICALFLIP) != (tails->mo->eflags & MFE_VERTICALFLIP))
return; // Both should be in same gravity return; // Both should be in same gravity

View File

@ -5331,7 +5331,10 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
player->powers[pw_tailsfly] = tailsflytics + 1; // Set the fly timer player->powers[pw_tailsfly] = tailsflytics + 1; // Set the fly timer
player->pflags &= ~(PF_JUMPED|PF_NOJUMPDAMAGE|PF_SPINNING|PF_STARTDASH); player->pflags &= ~(PF_JUMPED|PF_NOJUMPDAMAGE|PF_SPINNING|PF_STARTDASH);
player->pflags |= (PF_THOKKED|PF_CANCARRY); if (player->bot == 1)
player->pflags |= PF_THOKKED;
else
player->pflags |= (PF_THOKKED|PF_CANCARRY);
} }
break; break;
case CA_GLIDEANDCLIMB: case CA_GLIDEANDCLIMB:
@ -8400,7 +8403,7 @@ static void P_MovePlayer(player_t *player)
// Tails Put-Put noise // Tails Put-Put noise
if (player->charability == CA_FLY if (player->charability == CA_FLY
&& player->bot != 1 && (player->pflags & PF_CANCARRY)
&& !(player->mo->eflags & MFE_UNDERWATER) && !(player->mo->eflags & MFE_UNDERWATER)
&& leveltime % 10 == 0 && leveltime % 10 == 0
&& !player->spectator) && !player->spectator)
@ -12330,7 +12333,7 @@ void P_PlayerAfterThink(player_t *player)
player->mo->momz = tails->momz; player->mo->momz = tails->momz;
} }
if (gametype == GT_COOP) if (gametype == GT_COOP && (!tails->player || tails->player->bot != 1))
{ {
player->mo->angle = tails->angle; player->mo->angle = tails->angle;