[ZScript] [Enemy AI] Calculating shooting pitch for grenades

Archive of the old editing forum
Forum rules
Before asking on how to use a ZDoom feature, read the ZDoom wiki first. This forum is archived - please use this set of forums to ask new questions.
Locked
User avatar
phantombeta
Posts: 2172
Joined: Thu May 02, 2013 1:27 am
Operating System Version (Optional): Windows 10
Graphics Processor: nVidia with Vulkan support
Location: Brazil

[ZScript] [Enemy AI] Calculating shooting pitch for grenades

Post by phantombeta »

I'm trying to make a boss that calculates the correct firing angle and pitch for his (gravity-affected) grenades.
The code for calculating the angle for target interception works, but the code for calculating the pitch needed to hit the target doesn't. When the monster fires, it shoots nearly straight up and misses me.
I know the math is right, as I tested it outside GZDoom.

Code: Select all

// Pitch calculation function
class S7_Math : actor {
    /* Summary:
    **  Calculates the correct pitch a shooter needs to fire a gravity-affected projectile at to have it hit the target.
    **  Written by Chronos "phantombeta" Ouroboros. The equation were taken from Wikipedia.
    **
    ** Arguments:
    **  shooterPos: The shooter's position.
    **  targetPos: The target's position.
    **  v: The projectile's speed.
    **  grav: The projectile's gravity multiplier.
    **
    ** Returns:
    **  The pitch that the projectile will need to be fired at. Actually returns two doubles.
    */
    static double, double InterceptShotPitch (Vector3 shooterPos, Vector3 targetPos, double v, double grav) {
        double g = BASEGRAVITY * grav,
            x = (shooterPos.xy - targetPos.xy).Length (),
            y = targetPos.z - shooterPos.z;
        
        double sq = (v*v*v*v) - g*(g*(x*x) + 2 * y * (v*v));

        if (sq >= 0 && sq != double.NaN) {
            double x1 = atan2 ((v*v) + sqrt (sq), g * x);
            double x2 = atan2 ((v*v) - sqrt (sq), g * x);

            Console.Printf ("shooter (x: %f, y: %f, z: %f)\ntarget (x: %f, y: %f, z: %f)\nx1: %f\nx2: %f\ng: %f, x: %f, y: %f",
                shooterPos.x, shooterPos.y, shooterPos.z, targetPos.x, targetPos.y, targetPos.z, x1, x2, g, x, y);

            return x1, x2;
        } else
            return double.NaN, double.NaN;
    }
}
// Monster code: Target interception and pitch calculation
virtual Vector3 LeadTarget (Actor targ, double projSpeed) {
        if (!target)
            return (double.NaN, double.NaN, double.NaN);

        Vector3 targPos = targ.pos;
        targPos.z += targ.height / 2;
        if (targ.vel.x != 0 || targ.vel.y != 0 || targ.vel.z != 0) { // If the target is moving, lead it.
            Vector3 tempTargPos = S7_Math.CalculateInterceptShotPosition (pos, targPos, targ.vel, projSpeed);

            if (targPos.x != double.NaN && targPos.y != double.NaN && targPos.z != double.NaN)
                targPos = tempTargPos;
        }

        let marker = Spawn ("S7_PositionMarker", targPos);
        A_Face (marker, 0, 0);
        marker.Destroy ();
        return targPos;
    }

    virtual double PitchCorrection (Vector3 targPos, double xyOffs, double zOffs, double projSpeed, double grav) {
        double x1, x2;
        [x1, x2] = S7_Math.InterceptShotPitch ((pos.x + cos (angle)*xyOffs, pos.y + sin (angle)*xyOffs, pos.z + zOffs), targPos, projSpeed, grav);

        if (x1 == double.NaN && x2 == double.NaN) // If these are null, well fuck. Probably means we can't hit.
            return double.NaN;
        else if (x2 == double.NaN)
            return x1;
        
        return x2;
    }
// Monster code: Shooting
Missile.Grenade:
        TERM J 1 {
            /*if (!CheckIfCloser (target, 875.0))
                return ResolveState ("Missile.NoGrenade");*/

            A_PlaySound ("Terminator/LauncherStart", CHAN_Weapon); // Play the launcher attack begin sound
            A_FaceTarget (90, 45); // Spin to face target   

            return ResolveState (null);
        }
        TERM JJJ 1 A_FaceTarget (90, 45); // Spin to face target

        TERM K  1 bright {
            Vector3 targPos = LeadTarget (target, 75.0);
            double newPitch = PitchCorrection (targPos, -18.0, 35.0, 75.0, 0.2);

            Console.Printf ("%f", newPitch);
            /*if (newPitch == double.NaN)
                Console.Printf ("AAAAAAAAAAAAAA\nAAAAAAAAAAAAAA\nAAAAAAAAAAAAAA\nAAAAAAAAAAAAAA\nAAAAAAAAAAAAAA\nAAAAAAAAAAAAAA\nAAAAAAAAAAAAAA");//return ResolveState ("Missile.GrenadeStronger");*/

            A_SetPitch (-newPitch);
            A_PlaySound ("Terminator/GrenadeFire", CHAN_Weapon); // Play the firing sound
            A_SpawnProjectile ("S7_TerminatorGrenade", 35.0, -18, 0.0, CMF_AimDirection | CMF_AbsolutePitch, pitch);

            return ResolveState (null);
        }
        TERM KK 1 bright;
        goto Missile.Grenade.End;
Last edited by phantombeta on Mon Aug 28, 2017 11:05 am, edited 1 time in total.
User avatar
Rachael
Posts: 13934
Joined: Tue Jan 13, 2004 1:31 pm
Preferred Pronouns: She/Her
Contact:

Re: [ZScript] [Enemy AI] Calculating shooting pitch for gren

Post by Rachael »

First and foremost, as Graf explained in this thread, the gravity constant is not 81.92. We are not operating in fixed point, here - and all fixed point is, is an integer that gets divided by 65536, where the remainder becomes the fractional portion of the decimal. Have you tried a lower value - such as 0.00125?

Remember, between ZDoom 2.8.1 and the end of its development cycle where GZDoom completely took over, ZDoom underwent a huge refactoring and much of the system went through a process where the internals were converted from fixed point to actual floating point.
User avatar
phantombeta
Posts: 2172
Joined: Thu May 02, 2013 1:27 am
Operating System Version (Optional): Windows 10
Graphics Processor: nVidia with Vulkan support
Location: Brazil

Re: [ZScript] [Enemy AI] Calculating shooting pitch for gren

Post by phantombeta »

@Rachael
Yeah, I remembered that and fixed in the code, but forgot it when putting it in this post.
But that doesn't seem to be it, it still doesn't work, for some reason.

EDIT: Also, how the hell do I check for NaNs? I couldn't find any kind of "IsNaN" function, and ==/!= don't seem to work. (Expected, since IIRC IEEE 754 says comparisons with NaNs always return false?)
EDIT 2: Okay, for some reason using 9.807 made it sorta work. (wtf?) Sometimes it hits the floor or goes a tiny bit over the player, though.
Locked

Return to “Editing (Archive)”