|
This article or section is incomplete. It doesn't have all of the necessary core information on this topic. Please help the SRB2 Wiki by finishing this article.
|
A_AttractChase is an action that is used as part of the thinker for the rings, CTF team rings (red and blue) and coins, both in their regular forms and when spilled by the player. Its main purpose is to make rings/coins look for and chase players with an Attraction Shield. However, it also causes the spilled versions of the rings/coins to flicker before disappearing, and can cause normal rings/coins to be replaced by their spilled versions or vice versa. Note that SRB2's rings and coins themselves do not call this action via their states, but instead call it every tic from their internal Object thinkers. This is so that their chasing behavior can be smooth regardless of their state durations.
Synopsis
The actor will check for the nearest visible player with an Attraction Shield within 512 fracunits around it; if one is found, it will set the player as its tracer and chase it at a speed defined by the Object's Speed
value. When the actor is chasing such a player, MF_NOCLIPHEIGHT
is removed and MF_NOCLIP
added to the actor's flags, allowing the actor to pass through walls but not the floor/ceiling. If the actor's tracer no longer exists, is not a player, has no health, or cannot be seen, the actor removes MF_NOCLIP
from its flags and stops following the tracer if it has one.
If, however, the actor's tracer is a player without an Attraction Shield (when the actor presumably was previously attracted by a shield that has now been lost), the actor will spawn an Object of a type determined by ReactionTime
to use as the "spilled" Object, while the actor itself is removed from the map. For "spilled" Objects (which should have a ReactionTime
value matching their Object type), the actor will turn on and off MF2_DONTDRAW
every other tic to flicker in its last two seconds before disappearing. However, if the "spilled" Object finds a player with an Attraction Shield nearby, the actor will spawn an Object of a type determined by PainChance
to appear as the regular non-spilled version of the Object, and the actor itself is removed from the map.
Example values for ReactionTime
and PainChance
for existing Object types are given below:
Object type
|
Description
|
ReactionTime
|
PainChance
|
MT_RING
|
Ring
|
MT_FLINGRING
|
0
|
MT_FLINGRING
|
Spilled ring
|
MT_FLINGRING
|
MT_RING
|
MT_COIN
|
Coin
|
MT_FLINGCOIN
|
0
|
MT_FLINGCOIN
|
Spilled coin
|
MT_FLINGCOIN
|
MT_COIN
|
If the actor has the flag MF2_NIGHTSPULL
however, when the actor is being attracted to the player in a NiGHTS map, this action will do nothing. This action will also do nothing if the actor does not have any health.
Object type property |
Use
|
ReactionTime |
Object type to spawn when the tracer no longer has an Attraction Shield (unless this is the same as the actor's Object type)
|
PainChance |
Object type to spawn when attracted by an Attraction Shield (if the actor's Object type matches its ReactionTime value)
|
Speed |
Speed the actor chases players with an Attraction Shield
|
Code – A_AttractChase
|
|
// Function: A_AttractChase
//
// Description: Makes a ring chase after a player with a ring shield and also causes spilled rings to flicker.
//
// var1 = unused
// var2 = unused
//
void A_AttractChase(mobj_t *actor)
{
#ifdef HAVE_BLUA
if (LUA_CallAction("A_AttractChase", actor))
return;
#endif
if (actor->flags2 & MF2_NIGHTSPULL || !actor->health)
return;
// spilled rings flicker before disappearing
if (leveltime & 1 && actor->type == (mobjtype_t)actor->info->reactiontime && actor->fuse && actor->fuse < 2*TICRATE)
actor->flags2 |= MF2_DONTDRAW;
else
actor->flags2 &= ~MF2_DONTDRAW;
// Turn flingrings back into regular rings if attracted.
if (actor->tracer && actor->tracer->player
&& !(actor->tracer->player->powers[pw_shield] & SH_PROTECTELECTRIC) && actor->info->reactiontime && actor->type != (mobjtype_t)actor->info->reactiontime)
{
mobj_t *newring;
newring = P_SpawnMobj(actor->x, actor->y, actor->z, actor->info->reactiontime);
newring->momx = actor->momx;
newring->momy = actor->momy;
newring->momz = actor->momz;
P_RemoveMobj(actor);
return;
}
P_LookForShield(actor); // Go find 'em, boy!
if (!actor->tracer
|| !actor->tracer->player
|| !actor->tracer->health
|| !P_CheckSight(actor, actor->tracer)) // You have to be able to SEE it...sorta
{
// Lost attracted rings don't through walls anymore.
actor->flags &= ~MF_NOCLIP;
P_SetTarget(&actor->tracer, NULL);
return;
}
// If a FlingRing gets attracted by a shield, change it into a normal ring.
if (actor->type == (mobjtype_t)actor->info->reactiontime)
{
P_SpawnMobj(actor->x, actor->y, actor->z, actor->info->painchance);
P_RemoveMobj(actor);
return;
}
// Keep stuff from going down inside floors and junk
actor->flags &= ~MF_NOCLIPHEIGHT;
// Let attracted rings move through walls and such.
actor->flags |= MF_NOCLIP;
P_Attract(actor, actor->tracer, false);
}
|
|
Code - P_Attract
|
|
void P_Attract(mobj_t *source, mobj_t *dest, boolean nightsgrab) // Home in on your target
{
fixed_t dist, ndist, speedmul;
angle_t vangle;
fixed_t tx = dest->x;
fixed_t ty = dest->y;
fixed_t tz = dest->z + (dest->height/2); // Aim for center
fixed_t xydist = P_AproxDistance(tx - source->x, ty - source->y);
if (!dest || dest->health <= 0 || !dest->player || !source->tracer)
return;
// change angle
source->angle = R_PointToAngle2(source->x, source->y, tx, ty);
// change slope
dist = P_AproxDistance(xydist, tz - source->z);
if (dist < 1)
dist = 1;
if (nightsgrab && source->movefactor)
{
source->movefactor += FRACUNIT/2;
if (dist < source->movefactor)
{
source->momx = source->momy = source->momz = 0;
P_TeleportMove(source, tx, ty, tz);
}
else
{
vangle = R_PointToAngle2(source->z, 0, tz, xydist);
source->momx = FixedMul(FINESINE(vangle >> ANGLETOFINESHIFT), FixedMul(FINECOSINE(source->angle >> ANGLETOFINESHIFT), source->movefactor));
source->momy = FixedMul(FINESINE(vangle >> ANGLETOFINESHIFT), FixedMul(FINESINE(source->angle >> ANGLETOFINESHIFT), source->movefactor));
source->momz = FixedMul(FINECOSINE(vangle >> ANGLETOFINESHIFT), source->movefactor);
}
}
else
{
if (nightsgrab)
speedmul = P_AproxDistance(dest->momx, dest->momy) + FixedMul(8*FRACUNIT, source->scale);
else
speedmul = P_AproxDistance(dest->momx, dest->momy) + FixedMul(source->info->speed, source->scale);
source->momx = FixedMul(FixedDiv(tx - source->x, dist), speedmul);
source->momy = FixedMul(FixedDiv(ty - source->y, dist), speedmul);
source->momz = FixedMul(FixedDiv(tz - source->z, dist), speedmul);
}
// Instead of just unsetting NOCLIP like an idiot, let's check the distance to our target.
ndist = P_AproxDistance(P_AproxDistance(tx - (source->x+source->momx),
ty - (source->y+source->momy)),
tz - (source->z+source->momz));
if (ndist > dist) // gone past our target
{
// place us on top of them then.
source->momx = source->momy = source->momz = 0;
P_UnsetThingPosition(source);
source->x = tx;
source->y = ty;
source->z = tz;
P_SetThingPosition(source);
}
}
|
|
Code - P_LookForShield
|
|
/** Looks for a player with a ring shield.
* Used by rings.
*
* \param actor Ring looking for a shield to be attracted to.
* \return True if a player with ring shield is found, otherwise false.
* \sa A_AttractChase
*/
static boolean P_LookForShield(mobj_t *actor)
{
INT32 c = 0, stop;
player_t *player;
// BP: first time init, this allow minimum lastlook changes
if (actor->lastlook < 0)
actor->lastlook = P_RandomByte();
actor->lastlook %= MAXPLAYERS;
stop = (actor->lastlook - 1) & PLAYERSMASK;
for (; ; actor->lastlook = ((actor->lastlook + 1) & PLAYERSMASK))
{
// done looking
if (actor->lastlook == stop)
return false;
if (!playeringame[actor->lastlook])
continue;
if (c++ == 2)
return false;
player = &players[actor->lastlook];
if (!player->mo || player->mo->health <= 0)
continue; // dead
//When in CTF, don't pull rings that you cannot pick up.
if ((actor->type == MT_REDTEAMRING && player->ctfteam != 1) ||
(actor->type == MT_BLUETEAMRING && player->ctfteam != 2))
continue;
if ((player->powers[pw_shield] & SH_PROTECTELECTRIC)
&& (P_AproxDistance(P_AproxDistance(actor->x-player->mo->x, actor->y-player->mo->y), actor->z-player->mo->z) < FixedMul(RING_DIST, player->mo->scale)))
{
P_SetTarget(&actor->tracer, player->mo);
if (actor->hnext)
P_SetTarget(&actor->hnext->hprev, actor->hprev);
if (actor->hprev)
P_SetTarget(&actor->hprev->hnext, actor->hnext);
return true;
}
}
//return false;
}
|
|