// Function:A_ThrownRing
//
// Description: Thinker for thrown rings/sparkle trail
//
// var1 = unused
// var2 = unused
//
void A_ThrownRing(mobj_t *actor)
{
INT32 c = 0;
INT32 stop;
player_t *player;
fixed_t dist;
#ifdef HAVE_BLUA
if (LUA_CallAction("A_ThrownRing", actor))
return;
#endif
if (leveltime % (TICRATE/7) == 0)
{
mobj_t *ring = NULL;
if (actor->flags2 & MF2_EXPLOSION)
{
if (actor->momx != 0 || actor->momy != 0)
ring = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SMOKE);
// Else spawn nothing because it's totally stationary and constantly smoking would be weird -SH
}
else if (actor->flags2 & MF2_AUTOMATIC)
ring = P_SpawnGhostMobj(actor);
else if (!(actor->flags2 & MF2_RAILRING))
ring = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SPARK);
if (ring)
{
/*
P_SetTarget(&ring->target, actor);
ring->color = actor->color; //copy color
*/
ring->destscale = actor->scale;
P_SetScale(ring, actor->scale);
}
}
// A_GrenadeRing beeping lives once moooooore -SH
if (actor->type == MT_THROWNGRENADE && actor->fuse % TICRATE == 0)
S_StartSound(actor, actor->info->attacksound);
// decrement bounce ring time
if (actor->flags2 & MF2_BOUNCERING)
{
if (actor->fuse)
actor->fuse--;
else {
P_RemoveMobj(actor);
return;
}
}
// spilled rings (and thrown bounce) flicker before disappearing
if (leveltime & 1 && actor->fuse > 0 && actor->fuse < 2*TICRATE
&& actor->type != MT_THROWNGRENADE)
actor->flags2 |= MF2_DONTDRAW;
else
actor->flags2 &= ~MF2_DONTDRAW;
if (actor->tracer && actor->tracer->health <= 0)
P_SetTarget(&actor->tracer, NULL);
// Updated homing ring special capability
// If you have a ring shield, all rings thrown
// at you become homing (except rail)!
if (actor->tracer)
{
// A non-homing ring getting attracted by a
// magnetic player. If he gets too far away, make
// sure to stop the attraction!
if ((!actor->tracer->health) || (actor->tracer->player && (actor->tracer->player->powers[pw_shield] & SH_PROTECTELECTRIC)
&& P_AproxDistance(P_AproxDistance(actor->tracer->x-actor->x,
actor->tracer->y-actor->y), actor->tracer->z-actor->z) > FixedMul(RING_DIST/4, actor->tracer->scale)))
{
P_SetTarget(&actor->tracer, NULL);
}
if (actor->tracer && (actor->tracer->health)
&& (actor->tracer->player->powers[pw_shield] & SH_PROTECTELECTRIC))// Already found someone to follow.
{
const INT32 temp = actor->threshold;
actor->threshold = 32000;
P_HomingAttack(actor, actor->tracer);
actor->threshold = temp;
return;
}
}
// 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;
if (!playeringame[actor->lastlook])
continue;
if (c++ == 2)
return;
player = &players[actor->lastlook];
if (!player->mo)
continue;
if (player->mo->health <= 0)
continue; // dead
if ((netgame || multiplayer) && player->spectator)
continue; // spectator
if (actor->target && actor->target->player)
{
if (player->mo == actor->target)
continue;
// Don't home in on teammates.
if (gametype == GT_CTF
&& actor->target->player->ctfteam == player->ctfteam)
continue;
}
dist = P_AproxDistance(P_AproxDistance(player->mo->x-actor->x,
player->mo->y-actor->y), player->mo->z-actor->z);
// check distance
if (actor->flags2 & MF2_RAILRING)
{
if (dist > FixedMul(RING_DIST/2, player->mo->scale))
continue;
}
else if (dist > FixedMul(RING_DIST, player->mo->scale))
continue;
// do this after distance check because it's more computationally expensive
if (!P_CheckSight(actor, player->mo))
continue; // out of sight
if ((player->powers[pw_shield] & SH_PROTECTELECTRIC)
&& dist < FixedMul(RING_DIST/4, player->mo->scale))
P_SetTarget(&actor->tracer, player->mo);
return;
}
return;
}