// Function: A_BrakLobShot
//
// Description: Lobs an object at the floor about a third of the way toward your target.
// Implication is it'll bounce the rest of the way.
// (You can also just aim straight at the target, but whatever)
// Formula grabbed from http://en.wikipedia.org/wiki/Trajectory_of_a_projectile#Angle_required_to_hit_coordinate_.28x.2Cy.29
//
// var1 = object # to lob
// var2:
// Lower 16 bits: height offset to shoot from, from the actor's bottom (none that "airtime" malarky)
// Upper 16 bits: if 0, aim 1/3 of the way. Else, aim directly at target.
//
void A_BrakLobShot(mobj_t *actor)
{
fixed_t v; // Velocity to shoot object
fixed_t a1, a2, aToUse; // Velocity squared
fixed_t g; // Gravity
fixed_t x; // Horizontal difference
INT32 x_int; // x! But in integer form!
fixed_t y; // Vertical difference (yes that's normally z in SRB2 shut up)
INT32 y_int; // y! But in integer form!
INT32 intHypotenuse; // x^2 + y^2. Frequently overflows fixed point, hence why we need integers proper.
fixed_t fixedHypotenuse; // However, we can work around that and still get a fixed-point number.
angle_t theta; // Angle of attack
mobjtype_t typeOfShot;
mobj_t *shot; // Object to shoot
fixed_t newTargetX; // If not aiming directly
fixed_t newTargetY; // If not aiming directly
INT32 locvar1 = var1;
INT32 locvar2 = var2 & 0x0000FFFF;
INT32 aimDirect = var2 & 0xFFFF0000;
#ifdef HAVE_BLUA
if (LUA_CallAction("A_BrakLobShot", actor))
return;
#endif
if (!actor->target)
return; // Don't even bother if we've got nothing to aim at.
// Look up actor's current gravity situation
if (actor->subsector->sector->gravity)
g = FixedMul(gravity,(FixedDiv(*actor->subsector->sector->gravity>>FRACBITS, 1000)));
else
g = gravity;
// Look up distance between actor and its target
x = P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y);
if (!aimDirect)
{
// Distance should actually be a third of the way over
x = FixedDiv(x, 3<<FRACBITS);
newTargetX = actor->x + P_ReturnThrustX(actor, actor->angle, x);
newTargetY = actor->y + P_ReturnThrustY(actor, actor->angle, x);
x = P_AproxDistance(newTargetX - actor->x, newTargetY - actor->y);
// Look up height difference between actor and the ground 1/3 of the way to its target
y = P_FloorzAtPos(newTargetX, newTargetY, actor->target->z, actor->target->height) - (actor->z + FixedMul(locvar2*FRACUNIT, actor->scale));
}
else
{
// Look up height difference between actor and its target
y = actor->target->z - (actor->z + FixedMul(locvar2*FRACUNIT, actor->scale));
}
// Get x^2 + y^2. Have to do it in a roundabout manner, because this overflows fixed_t way too easily otherwise.
x_int = x>>FRACBITS;
y_int = y>>FRACBITS;
intHypotenuse = (x_int*x_int) + (y_int*y_int);
fixedHypotenuse = FixedSqrt(intHypotenuse) *256;
// a = g(y+/-sqrt(x^2+y^2)). a1 can be +, a2 can be -.
a1 = FixedMul(g,y+fixedHypotenuse);
a2 = FixedMul(g,y-fixedHypotenuse);
// Determine which one isn't actually an imaginary number (or the smaller of the two, if both are real), and use that for v.
if (a1 < 0 || a2 < 0)
{
if (a1 < 0 && a2 < 0)
{
//Somehow, v^2 is negative in both cases. v is therefore imaginary and something is horribly wrong. Abort!
return;
}
// Just find which one's NOT negative, and use that
aToUse = max(a1,a2);
}
else
{
// Both are positive; use whichever's smaller so it can decay faster
aToUse = min(a1,a2);
}
v = FixedSqrt(aToUse);
// Okay, so we know the velocity. Let's actually find theta.
// We can cut the "+/- sqrt" part out entirely, since v was calculated specifically for it to equal zero. So:
//theta = tantoangle[FixedDiv(aToUse,FixedMul(g,x)) >> DBITS];
theta = tantoangle[SlopeDiv(aToUse,FixedMul(g,x))];
// Okay, complicated math done. Let's fire our object already, sheesh.
A_FaceTarget(actor);
if (locvar1 <= 0 || locvar1 >= NUMMOBJTYPES)
typeOfShot = MT_CANNONBALL;
else typeOfShot = (mobjtype_t)locvar1;
shot = P_SpawnMobj(actor->x, actor->y, actor->z + FixedMul(locvar2*FRACUNIT, actor->scale), typeOfShot);
if (shot->info->seesound)
S_StartSound(shot, shot->info->seesound);
P_SetTarget(&shot->target, actor); // where it came from
shot->angle = actor->angle;
// Horizontal axes first. First parameter is initial horizontal impulse, second is to correct its angle.
shot->momx = FixedMul(FixedMul(v, FINECOSINE(theta >> ANGLETOFINESHIFT)), FINECOSINE(shot->angle >> ANGLETOFINESHIFT));
shot->momy = FixedMul(FixedMul(v, FINECOSINE(theta >> ANGLETOFINESHIFT)), FINESINE(shot->angle >> ANGLETOFINESHIFT));
// Then the vertical axis. No angle-correction needed here.
shot->momz = FixedMul(v, FINESINE(theta >> ANGLETOFINESHIFT));
// I hope that's all that's needed, ugh
}