// Function: A_BrakChase
//
// Description: Chase after your target, but speed and attack are tied to health.
//
// Every time this is called, generate a random number from a 1/4 to 3/4 of mobj's spawn health.
// If health is above that value, use missilestate to attack.
// If health is at or below that value, use meleestate to attack (default to missile state if not available).
//
// Likewise, state will linearly speed up as health goes down.
// Upper bound will be the frame's normal length.
// Lower bound defaults to 1 tic (technically 0, but we round up), unless a lower bound is specified in var1.
//
// var1 = lower-bound of frame length, in tics
// var2 = optional sound to play
//
void A_BrakChase(mobj_t *actor)
{
INT32 delta;
INT32 lowerbound;
INT32 newtics;
INT32 locvar1 = var1;
INT32 locvar2 = var2;
#ifdef HAVE_BLUA
if (LUA_CallAction("A_BrakChase", actor))
return;
#endif
// Set new tics NOW, in case the state changes while we're doing this and we try applying this to the painstate or something silly
if (actor->tics > 1 && locvar1 < actor->tics) // Not much point, otherwise
{
if (locvar1 < 0)
lowerbound = 0;
else
lowerbound = locvar1;
newtics = (((actor->tics - lowerbound) * actor->health) / actor->info->spawnhealth) + lowerbound;
if (newtics < 1)
newtics = 1;
actor->tics = newtics;
}
if (actor->reactiontime)
{
actor->reactiontime--;
if (actor->reactiontime == 0 && actor->type == MT_CYBRAKDEMON)
S_StartSound(0, sfx_bewar1 + P_RandomKey(4));
}
// modify target threshold
if (actor->threshold)
{
if (!actor->target || actor->target->health <= 0)
actor->threshold = 0;
else
actor->threshold--;
}
// turn towards movement direction if not there yet
if (actor->movedir < NUMDIRS)
{
actor->angle &= (7<<29);
delta = actor->angle - (actor->movedir << 29);
if (delta > 0)
actor->angle -= ANGLE_45;
else if (delta < 0)
actor->angle += ANGLE_45;
}
if (!actor->target || !(actor->target->flags & MF_SHOOTABLE))
{
// look for a new target
if (P_LookForPlayers(actor, true, false, 0))
return; // got a new target
P_SetMobjStateNF(actor, actor->info->spawnstate);
return;
}
// do not attack twice in a row
if (actor->flags2 & MF2_JUSTATTACKED)
{
actor->flags2 &= ~MF2_JUSTATTACKED;
P_NewChaseDir(actor);
return;
}
// Check if we can attack
if (P_CheckMissileRange(actor) && !actor->movecount)
{
// Check if we should use "melee" attack first. (Yes, this still runs outside of melee range. Quiet, you.)
if (actor->info->meleestate
&& actor->health <= P_RandomRange(actor->info->spawnhealth/4, (actor->info->spawnhealth * 3)/4)) // Guaranteed true if <= 1/4 health, guaranteed false if > 3/4 health
{
if (actor->info->attacksound)
S_StartAttackSound(actor, actor->info->attacksound);
P_SetMobjState(actor, actor->info->meleestate);
actor->flags2 |= MF2_JUSTATTACKED;
return;
}
// Else, check for missile attack.
else if (actor->info->missilestate)
{
P_SetMobjState(actor, actor->info->missilestate);
actor->flags2 |= MF2_JUSTATTACKED;
return;
}
}
// possibly choose another target
if (multiplayer && !actor->threshold && (actor->target->health <= 0 || !P_CheckSight(actor, actor->target))
&& P_LookForPlayers(actor, true, false, 0))
return; // got a new target
// chase towards player
if (--actor->movecount < 0 || !P_Move(actor, actor->info->speed))
P_NewChaseDir(actor);
// Optionally play a sound effect
if (locvar2 > 0 && locvar2 < NUMSFX)
S_StartSound(actor, (sfxenum_t)locvar2);
// make active sound
if (actor->type != MT_CYBRAKDEMON && actor->info->activesound && P_RandomChance(3*FRACUNIT/256))
{
S_StartSound(actor, actor->info->activesound);
}
}