Fractal Softworks Forum

Please login or register.

Login with username, password and session length
Advanced search  

News:

Starsector 0.97a is out! (02/02/24); New blog post: Simulator Enhancements (03/13/24)

Pages: 1 2 3 [4] 5 6 ... 10

Author Topic: The Radioactive Code Dump  (Read 80451 times)

dmaiski

  • Captain
  • ****
  • Posts: 422
  • resistance is futile
    • View Profile
Re: The Radioactive Code Dump
« Reply #45 on: November 20, 2013, 03:17:32 PM »

Spoiler
as promised im getting around to it

first i needed to finalize the BOMBARDai, it's now verry efficient, easy to use, and quick to implement
(ill be useing it as the base for my MIRV and DRONE ai)
improvments:
now uses interval util for counters
much better flow
booleans where posible
cleaned up ai to have beter logic
more predictable spread pattern
silly bug causing missiles to fly sideways fixed
even more spamable!! (significantly more efficient then vanilla missiles)

on my [expletive redacted] computer:
500 active missile test
32-29 fps BOMBARDai
22-13 fps vanilla baseline

1000+ active missile test
19 fps BOMBARDai (hits 10 at over 1500)
10 fps vanilla baseline (min fps limit)

what it actualy looks like:
Spoiler
[close]
for all your missile storm needs

code:
Spoiler
Code: java
package data.scripts.MissileAI;


import com.fs.starfarer.api.Global;
import com.fs.starfarer.api.combat.*;
import com.fs.starfarer.api.util.IntervalUtil;
import org.lazywizard.lazylib.MathUtils;
import org.lazywizard.lazylib.combat.AIUtils;
import org.lazywizard.lazylib.combat.CombatUtils;
import org.lwjgl.util.vector.Vector2f;

import java.util.Iterator;
import java.util.List;

//import static jar.UtilityKit.LeadVector;
import static org.lwjgl.util.vector.Vector2f.add;

public class BOMBARDai implements MissileAIPlugin
{
    // Our missile object
    private final MissileAPI missile;
    // Our current target (can be null)
    private CombatEntityAPI target;

    //////////////////////////////////////////////////////////////////////////////////////////////////////////


    //stuff that you can change
    float straighttimer=(float)(10*Math.random());                    //non targeting time
    private IntervalUtil STimer = new IntervalUtil(.3f, .0f);         //rate of countdown
    private final double isEffectedbyflare=Math.random();             //flare stuff
    private boolean flarelockout=false;
    private float oftarget=300*((float)(.5-Math.random()));           //max initial off target (scatters missiles at launch)
    private float baseoftarget=40*((float)(.5-Math.random()));        //min off target (makes missiles stay scattered)
    public static final String flare="flare";
    private IntervalUtil Timer = new IntervalUtil(.1f, .0f);          //rate of countdown for off target
    private final boolean StopNTurn=false;                      //when far off target missile will stop and turn to aim

    //////////////////////////////////////////////////////////////////////////////////////////////////////////


    public static float searchrange = 500; //500 range default

    //public static float Ark = 30;          //60deg ark   default

    boolean newlaunch=true;                //only do mouse target once on a newly launched missile
    static float MWRange = 9999f;          //hack to make missile ignore null weapon
    public BOMBARDai(MissileAPI missile)
    {
        this.missile = missile;
        MWRange = missile.getWeapon() != null ? missile.getWeapon().getRange() : 9999f;
        searchrange=MWRange;//based on weapon
        //Ark=5+missile.getWeapon().getArc()/4;           //based on weapon

        // Support for 'fire at target by clicking on them' behavior
        if (newlaunch=true)
        {
            newlaunch=false;
            //get targets near mouse
            Vector2f MouseT = missile.getSource().getMouseTarget();
            if (MathUtils.getDistance(missile,MouseT)<searchrange)
            {
                List directTargets = CombatUtils.getShipsWithinRange(
                        MouseT, 100f, true);

                if (!directTargets.isEmpty())
                {
                    ShipAPI tmp;
                    for (Iterator iter = directTargets.iterator(); iter.hasNext();)
                    {
                        //filter out friendlies
                        tmp = (ShipAPI) iter.next();
                        if (tmp.getOwner() != missile.getSource().getOwner())
                        {
                            target = tmp;
                            break;
                        }
                    }
                }
            }
        }

        // Otherwise, use default targeting AI
        if (target == null)
        {
            target = findBestTarget(missile);
        }
    }
    ///////////////////////////////////////////////////////////////////////////////////////////////////////


    //flare finder

    public static MissileAPI checkforflare(MissileAPI missile)
    {
        MissileAPI retv = null;                                   //if no flares return null
        CombatEngineAPI engine = Global.getCombatEngine();
        List nearbymissiles = engine.getMissiles();               //(target.getCollisionRadius()*10)
        if (!nearbymissiles.isEmpty())
        {
            MissileAPI tmp;
            for (Iterator iter = nearbymissiles.iterator(); iter.hasNext();)
            {
                tmp = (MissileAPI) iter.next();

                if ((tmp.getProjectileSpecId()).startsWith(flare) //is it a flare
                        && MathUtils.getDistance(tmp,missile)<300 //is it near your missile
                        && missile.getOwner()!=tmp.getOwner()     //is it en enemy flare
                        && Math.random()>.5)                      //chance of failure prevents all missiles
                {                                                 //going after one flare at the same time
                    //tmp= (MissileAPI) nearbymissiles.get(0);    //testing code
                    retv = tmp;                                   //return flare
                    break;                                        //stop searching you found your flare
                }
            }
        }
        return retv;
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////

    /* turned off for this AI
    //search for targets in ark in front
    public static MissileAPI findBestTarget(MissileAPI missile)
    {
        //find targets in ark in front of missile
        ArrayList targets = getEnemyMissilesInArk(missile, Ark, searchrange, true);
        if (!targets.isEmpty())
        {   //pick random missile in list     //replace with .get(0) for nearest
            return (MissileAPI) targets.get((int)(targets.size()*Math.random()));
        }
        else return null;

    }
    */
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////


    //get ship target or search for closest enemy
    public static ShipAPI findBestTarget(MissileAPI missile)
    {
        ShipAPI source = missile.getSource();                             //who launched you?
        if (source != null && source.getShipTarget() != null              //check for nulls
                && !source.getShipTarget().isHulk()                       //check if its alive
                && MathUtils.getDistance(source.getShipTarget(), source)  //get range to target
                <MWRange)                          //check if its in range
        {
            return source.getShipTarget();
        }

        return AIUtils.getNearestEnemy(missile);
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////


    //main missile AI
    @Override
    public void advance(float amount)
    {

        //.1 second timer
        Timer.advance(amount);
        STimer.advance(amount);

        ////////////////////////////////////////////////////////////////////////////////////////////////////////////

        //go straight for x after launch
        if (straighttimer>0)
        {
            missile.giveCommand(ShipCommand.ACCELERATE);
            if (STimer.intervalElapsed())
            {straighttimer--;}
            return;
        }
        else
        {
            ///////////////////////////////////////////////////////////////////////////////////////////////////////////


            // Apparently commands still work while fizzling
            if (missile.isFading() || missile.isFizzling())
            {
                return;
            }

            ////////////////////////////////////////////////////////////////////////////////////////////////////////////



            //find a target
            if (target == null)
            {
                target = findBestTarget(missile);
            }

            /////////////////////////////////////////////////////////////////////////////////////////////////////////////


            //if missile is susceptible to flare
            if (isEffectedbyflare>0.4                     //can a flare effect it
                    && !flarelockout                      //has it already been set to ignore flares
                    && Timer.intervalElapsed())           //check every .10 sec

            {
                MissileAPI flaretarget = checkforflare(missile);
                if (flaretarget!=null)
                {
                    target=flaretarget;         //set flare as target
                    flarelockout=true;          //ignore future flares
                }

            }

            //////////////////////////////////////////////////////////////////////////////////////////////////////////////


            //pathing ai
            if (target!=null)               //if you have a target
            {
                //reduction off target/gradual aiming
                if (Timer.intervalElapsed())
                {
                    oftarget=(oftarget>0 ? oftarget-1 : oftarget+1); //reduce off target counter;
                }

                //////////////////////////////////////////////////////////////////////////////////////////////
                //check if scatter code needed
                float targetcolision = target.getCollisionRadius();       //get colision radi
                if (MathUtils.getDistance(missile, target)<75+(targetcolision))
                {                                                         //cutoff for baseoftarget behaviour
                    baseoftarget=0;      //aim variable if you got in close enough is 0 (ie go hit the enemy target)
                }

                ///////////////////////////////////////////////////////////////////////////////////////////////


                //how much off target will the missiles be
                float oftargetby = (0 + (oftarget + (baseoftarget * targetcolision / 75)));

                //////////////////////////////////////////////////////////////////////////////////////////

                //aming
                //1step calculations of various stats
                Vector2f MVel = missile.getVelocity();
                Vector2f MLoc = missile.getLocation();
                Vector2f TLoc = target.getLocation();
                Vector2f TVel = target.getVelocity();
                //float MSpeed = (float)Math.sqrt(MVel.lengthSquared());
                //float TSpeed = (float)Math.sqrt(TVel.lengthSquared());
                float MFace = missile.getFacing();
                double TfaceRad = Math.toRadians(target.getFacing());
                float TCol = target.getCollisionRadius();
                float sx = (float) (TCol*.5*Math.cos(TfaceRad));
                float sy = (float) (TCol*.5*Math.sin(TfaceRad));
                Vector2f TNose = new Vector2f(sx, sy);
                //float RangeToTarget = MathUtils.getDistance(TLoc, MLoc);
                //float TimeToTarget = RangeToTarget/MSpeed;                      //how long till you hit

                //testing InterceptPoint
                Vector2f Lvec = LeadVector(TLoc, TVel, MLoc, MVel);

                //lead target
                Vector2f TLead = add(TLoc,            //target location+
                        Lvec,       //target speed*time to get to target+
                        null);      // if its too long just assume 3 seconds is long enough to course correct



                //aim at nose (can be changed to part targeting with a few tweaks)
                Vector2f TNoseLead = add(TLead, TNose, null);//aim at the nose of the target(optional)

                //main aiming (to find angle you are off target by)
                float AngleToEnemy = MathUtils.getAngle(MLoc, TNoseLead);
                float AtTarget = getAngleDifference(  //how far off target you are
                        MFace, AngleToEnemy);          //where missile pointing, where target is in relation


                float AbsAngD = Math.abs(AtTarget-oftargetby);


                //////////////////////////////////////////////////////////////////////////////////////////////


                //point towards target
                if (AbsAngD > 0.5)
                {                                  //makes missile fly off target
                    missile.giveCommand(AtTarget > oftargetby
                            ? ShipCommand.TURN_LEFT : ShipCommand.TURN_RIGHT);
                }

                ////////////////////////////////////////////////////////////////////////////////////////////////////


                //correct missile velocity vector to be the same as missile facing
                if (AbsAngD < 5)
                {
                    //course correct for missile velocity vector
                    float MFlightAng = MathUtils.getAngle(new Vector2f(0, 0), MVel);
                    float MFlightCC = getAngleDifference(MFace, MFlightAng);
                    if (Math.abs(MFlightCC)>20)
                    {
                        missile.giveCommand(MFlightCC < 0
                                ? ShipCommand.STRAFE_LEFT : ShipCommand.STRAFE_RIGHT);
                    }

                }

                /////////////////////////////////////////////////////////////////////////////////////////////////


                //stop turning once you are on target (way of the hack, ignores missile limitations)
                if (AbsAngD < 0.4)
                {
                    missile.setAngularVelocity(0);
                }

                ///////////////////////////////////////////////////////////////////////////////////////////////


                //acceleration code (stopNturn compatible)
                if (StopNTurn)
                {
                    if (AbsAngD < 40)
                    {
                        missile.giveCommand(ShipCommand.ACCELERATE);
                    }
                    if (MVel.lengthSquared()<TVel.lengthSquared())
                    {
                        missile.giveCommand(ShipCommand.ACCELERATE);
                    }
                    if (AbsAngD > 120)        //if you missed stop and turn around
                    {
                        missile.giveCommand(ShipCommand.DECELERATE);
                    }
                }
                else{missile.giveCommand(ShipCommand.ACCELERATE);}


                /////////////////////////////////////////////////////////////////////////////////////////////////


                //testcode    spawns particle at aim points
                CombatEngineAPI engine = Global.getCombatEngine(); //engine
                //engine.addSmoothParticle(TLead, new Vector2f(0,0), 5, 1, 1, new Color(255,10,15,255));
                //engine.addSmoothParticle(TNoseLead, new Vector2f(0,0), 5, 1, 1, new Color(93, 255, 40,255));
                //engine.addSmoothParticle(MLoc, multV2f(MVel, 4), 5, .5f, 1, new Color(248, 244, 255,255));
                //engine.addSmoothParticle(MLoc, new Vector2f(0,0), 5, .5f, 1, new Color(2, 255, 250,255));

            }

            //////////////////////////////////////////////////////////////////////////////////////////////////////


            //stop if you have no target
            if (target==null && straighttimer<1)
            {missile.giveCommand(ShipCommand.DECELERATE);}

            ///////////////////////////////////////////////////////////////////////////////////////////////////////////


            //clear target if target is gone
            if (target == null // unset
                    ||  ( missile.getOwner() == target.getOwner() )        // friendly
                    ||  !Global.getCombatEngine().isEntityInPlay(target)   // completely removed
                    || ( target instanceof ShipAPI && ((ShipAPI)target).isHulk() ))//dead
            {

                target = findBestTarget(missile);
                return;
            }
        }
    }

    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////


    // Will be in next LazyLib version
    public static float getAngleDifference(float angle1, float angle2)
    {
        float distance = (angle2 - angle1) + 180f;
        distance = (distance / 360.0f);
        distance = ((distance - (float) Math.floor(distance)) * 360f) - 180f;
        return distance;
    }

    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////


    //multiply vectors //own code
    public static Vector2f multV2f(Vector2f Vector1, float Multiplier)
    {
        float v1x = Vector1.getX()*Multiplier;
        float v1y = Vector1.getY()*Multiplier;
        Vector2f v1end = new Vector2f(v1x, v1y);
        return v1end;
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    //find intercept between a missile and a target
    //adapted from code by Jens Seiler
    //http://jaran.de/goodbits/2011/07/17/calculating-an-intercept-course-to-a-target-with-constant-direction-and-velocity-in-a-2-dimensional-plane/
    //have fun if you want to actually understand what going on
    //basicaly gets where T will be by the time M gets to it
    public static Vector2f LeadVector(Vector2f TLoc, Vector2f TVel, Vector2f MLoc, Vector2f MVel)
    {
        //get missiles speed
        float MSpeed = (float)Math.sqrt(MVel.lengthSquared());

        //separate out the vectors
        float Tx = TLoc.getX();
        float Ty = TLoc.getY();
        float Mx = MLoc.getX();
        float My = MLoc.getY();

        float TVx = TVel.getX();
        float TVy = TVel.getY();
        float MVx = MVel.getX();
        float MVy = MVel.getY();

        //subtract position vectors
        float x1 = Tx - Mx;
        float y1 = Ty - My;

        //quadratic fun
        float h1 = TVx*TVx + TVy*TVy - MSpeed*MSpeed;
        if (h1==0)
        {
            h1= (float) .00001;
        }

        float minusMHalf = -(x1*TVx + y1*TVy)/h1;  // h2/h1

        float discriminant = minusMHalf * minusMHalf - (x1*x1 + y1*y1)/h1; // (h2/h1)^2-h3/h1 (ie D)

        //can they intersect?
        if (discriminant < 0)
        {
            return TLoc;
        }


        double root = Math.sqrt(discriminant);

        double t1 = minusMHalf + root;
        double t2 = minusMHalf - root;

        double tMin = Math.min(t1, t2);
        double tMax = Math.max(t1, t2);
        //which of the 2 is smaller
        double time = tMin > 0 ? tMin : tMax;

        //can return -ve time (this is less then useful)
        if (time < 0)
        {
            return TLoc;
        }

        //calculate vector
        return new Vector2f((float)(time * TVx), (float)(time * TVy));
    }
}
[close]

btw, im just pushing the limits of what this can do for stress testing, it can just as easily work with single missile launchers ect...

update: made toggles for stopNturn behaviour when missile too far off target(what vanilla missiles do)
[close]
old see kitchenSINKai series
« Last Edit: December 11, 2013, 02:30:32 PM by dmaiski »
Logged
BISO
(WIP) lots of shiny new weapons ( :-[ i have more weapons then sprites :-[ )

i got a cat pad
its like a mouse pad but better!

ValkyriaL

  • Admiral
  • *****
  • Posts: 2145
  • The Guru of Capital Ships.
    • View Profile
Re: The Radioactive Code Dump
« Reply #46 on: November 20, 2013, 03:19:32 PM »

Nothing Valkyrian PD can't handle. >.>
Logged

MesoTroniK

  • Admiral
  • *****
  • Posts: 1731
  • I am going to destroy your ships
    • View Profile
Re: The Radioactive Code Dump
« Reply #47 on: November 20, 2013, 03:49:16 PM »

Dammit man, to think I held the crown for the largest (and balanced) missile spam.

dmaiski

  • Captain
  • ****
  • Posts: 422
  • resistance is futile
    • View Profile
Re: The Radioactive Code Dump
« Reply #48 on: November 20, 2013, 05:07:58 PM »

MIRV/Flechet MissileAI
Spoiler
based on BOMBARDai
new struff:
MIRV/Flechet behaviour (launch missiles/projectiles at set ranges/angles/ect...)

advantages over Ye Olde MIRV missile:
fully customizable (you cna even make it be identical to vanilla)
cheaper on the CPU cycles
can have 2 dimensional spread(angle, speed of shot)
multiple fire (PVolley stat lets you load multiple shots onto your missile, and have it shoot for as long as you want)

DRONE ai next

normal looking version:
Spoiler

basicaly vanilla MIRV
[close]

we heard you like missiles, so we made missiles, that shoot missiles
(Must See troll image)
Spoiler

Big mama harpoon and het 400 ferocious little babies
[close]

code:
Spoiler
Code: java
package data.scripts.MissileAI;


import com.fs.starfarer.api.Global;
import com.fs.starfarer.api.combat.*;
import com.fs.starfarer.api.util.IntervalUtil;
import org.lazywizard.lazylib.MathUtils;
import org.lazywizard.lazylib.combat.AIUtils;
import org.lazywizard.lazylib.combat.CombatUtils;
import org.lwjgl.util.vector.Vector2f;

import java.awt.*;
import java.util.Iterator;
import java.util.List;

//import static jar.UtilityKit.LeadVector;
import static org.lwjgl.util.vector.Vector2f.add;

public class MIRVai implements MissileAIPlugin
{
    // Our missile object
    private final MissileAPI missile;
    // Our current target (can be null)
    private CombatEntityAPI target;

    //////////////////////////////////////////////////////////////////////////////////////////////////////////


    //stuff that you can change
    float straighttimer=(float)(10*Math.random());              //non targeting time
    private IntervalUtil STimer = new IntervalUtil(.3f, .0f);   //rate of countdown
    private final double isEffectedbyflare=Math.random();       //flare stuff
    private boolean flarelockout=false;
    private float oftarget=300f*((float)(.5-Math.random()));    //max initial off target (scatters missiles at launch)
    private float baseoftarget=40f*((float)(.5-Math.random())); //min off target (makes missiles stay scattered)
    public static final String flare="flare";
    private IntervalUtil Timer = new IntervalUtil(.1f, .0f);    //rate of countdown for off target
    private final boolean StopNTurn=false;                      //when far off target missile will stop and turn to aim

    //MIRV/Flechet script
    //dummy weapon  supports missiles and projectiles
    //suggested projectile stats, range:1,  speed:1
    //replace with projectile cannon like "chaingun" or "vulcan" from vanilla,
    // or your own weapon(no beams)(Ballistic_as_beam are fine)
    private final String Projectile = "harpoon_single";    
    private final float MaxRange=850f;    //max range when it starts to direct aim target
    private final float MinRange=800f;    //min range when it detonates
    private final float PArk=45;          //ark projectiles fly in
    private final int PNum=4;             //shots per volley
    private int PVolleys=4;               //volleys
    private final float Lifetime=10f;      //how long the projectile being used can stay alive (IMPORTANT)
    //this is a value you must enter it is equal to range/ProjSpeed and works as a multiplier to make
    // firing velocity not overshoot the target (due to long life)
    private final float Leadtime=1f;       //fiddle with this if your projectiles are missing the target
    //determines how far ahead of the target your weapon aims
    private IntervalUtil VTimer = new IntervalUtil(.05f, .05f);         //rate of fire
    private final boolean IsFlechet=true;//are the shots fired by missile aimed or follow missile facing?
    //if IsFlechet=false missile will fire shots at the target ignoring its direction of travel

    //////////////////////////////////////////////////////////////////////////////////////////////////////////


    public static float searchrange = 500; //500 range default

    //public static float Ark = 30;          //60deg ark   default

    boolean newlaunch=true;                //only do mouse target once on a newly launched missile
    static float MWRange = 9999f;          //hack to make missile ignore null weapon
    public MIRVai(MissileAPI missile)
    {
        this.missile = missile;
        MWRange = missile.getWeapon() != null ? missile.getWeapon().getRange() : 9999f;
        searchrange=MWRange;//based on weapon
        //Ark=5+missile.getWeapon().getArc()/4;           //based on weapon

        // Support for 'fire at target by clicking on them' behavior
        if (newlaunch=true)
        {
            newlaunch=false;
            //get targets near mouse
            Vector2f MouseT = missile.getSource().getMouseTarget();
            if (MathUtils.getDistance(missile,MouseT)<searchrange)
            {
                List directTargets = CombatUtils.getShipsWithinRange(
                        MouseT, 100f, true);

                if (!directTargets.isEmpty())
                {
                    ShipAPI tmp;
                    for (Iterator iter = directTargets.iterator(); iter.hasNext();)
                    {
                        //filter out friendlies
                        tmp = (ShipAPI) iter.next();
                        if (tmp.getOwner() != missile.getSource().getOwner())
                        {
                            target = tmp;
                            break;
                        }
                    }
                }
            }
        }

        // Otherwise, use default targeting AI
        if (target == null)
        {
            target = findBestTarget(missile);
        }
    }
    ///////////////////////////////////////////////////////////////////////////////////////////////////////


    //flare finder

    public static MissileAPI checkforflare(MissileAPI missile)
    {
        MissileAPI retv = null;                                   //if no flares return null
        CombatEngineAPI engine = Global.getCombatEngine();
        List nearbymissiles = engine.getMissiles();               //(target.getCollisionRadius()*10)
        if (!nearbymissiles.isEmpty())
        {
            MissileAPI tmp;
            for (Iterator iter = nearbymissiles.iterator(); iter.hasNext();)
            {
                tmp = (MissileAPI) iter.next();

                if ((tmp.getProjectileSpecId()).startsWith(flare) //is it a flare
                        && MathUtils.getDistance(tmp,missile)<300 //is it near your missile
                        && missile.getOwner()!=tmp.getOwner()     //is it en enemy flare
                        && Math.random()>.5)                      //chance of failure prevents all missiles
                {                                                 //going after one flare at the same time
                    //tmp= (MissileAPI) nearbymissiles.get(0);    //testing code
                    retv = tmp;                                   //return flare
                    break;                                        //stop searching you found your flare
                }
            }
        }
        return retv;
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////

    /* turned off for this AI
    //search for targets in ark in front
    public static MissileAPI findBestTarget(MissileAPI missile)
    {
        //find targets in ark in front of missile
        ArrayList targets = getEnemyMissilesInArk(missile, Ark, searchrange, true);
        if (!targets.isEmpty())
        {   //pick random missile in list     //replace with .get(0) for nearest
            return (MissileAPI) targets.get((int)(targets.size()*Math.random()));
        }
        else return null;

    }
    */
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////


    //get ship target or search for closest enemy
    public static ShipAPI findBestTarget(MissileAPI missile)
    {
        ShipAPI source = missile.getSource();                             //who launched you?
        if (source != null && source.getShipTarget() != null              //check for nulls
                && !source.getShipTarget().isHulk()                       //check if its alive
                && MathUtils.getDistance(source.getShipTarget(), source)  //get range to target
                <MWRange)                          //check if its in range
        {
            return source.getShipTarget();
        }

        return AIUtils.getNearestEnemy(missile);
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////


    //main missile AI
    @Override
    public void advance(float amount)
    {

        //timer
        Timer.advance(amount);
        STimer.advance(amount);
        VTimer.advance(amount);

        ////////////////////////////////////////////////////////////////////////////////////////////////////////////

        //go straight for x after launch
        if (straighttimer>0)
        {
            missile.giveCommand(ShipCommand.ACCELERATE);
            if (STimer.intervalElapsed())
            {straighttimer--;}
            return;
        }
        else
        {
            ///////////////////////////////////////////////////////////////////////////////////////////////////////////


            // Apparently commands still work while fizzling
            if (missile.isFading() || missile.isFizzling())
            {
                return;
            }

            ////////////////////////////////////////////////////////////////////////////////////////////////////////////



            //find a target
            if (target == null)
            {
                target = findBestTarget(missile);
            }

            /////////////////////////////////////////////////////////////////////////////////////////////////////////////


            //if missile is susceptible to flare
            if (isEffectedbyflare>0.4                     //can a flare effect it
                    && !flarelockout                      //has it already been set to ignore flares
                    && Timer.intervalElapsed())           //check every .10 sec

            {
                MissileAPI flaretarget = checkforflare(missile);
                if (flaretarget!=null)
                {
                    target=flaretarget;         //set flare as target
                    flarelockout=true;          //ignore future flares
                }

            }

            //////////////////////////////////////////////////////////////////////////////////////////////////////////////


            //pathing ai
            if (target!=null)               //if you have a target
            {
                //reduction off target/gradual aiming
                if (Timer.intervalElapsed())
                {
                    oftarget=(oftarget>0 ? oftarget-1 : oftarget+1); //reduce off target counter;
                }

                //////////////////////////////////////////////////////////////////////////////////////////////
                //check if scatter code needed
                float targetcolision = target.getCollisionRadius();       //get colision radi
                if (MathUtils.getDistance(missile, target)<75+(targetcolision))
                {                                                         //cutoff for baseoftarget behaviour
                    baseoftarget=0;      //aim variable if you got in close enough is 0 (ie go hit the enemy target)
                }

                ///////////////////////////////////////////////////////////////////////////////////////////////


                //how much off target will the missiles be
                float oftargetby = (0 + (oftarget + (baseoftarget * targetcolision / 75)));

                //////////////////////////////////////////////////////////////////////////////////////////

                //aming
                //1step calculations of various stats
                Vector2f MVel = missile.getVelocity();
                Vector2f MLoc = missile.getLocation();
                Vector2f TLoc = target.getLocation();
                Vector2f TVel = target.getVelocity();
                //float MSpeed = (float)Math.sqrt(MVel.lengthSquared());
                //float TSpeed = (float)Math.sqrt(TVel.lengthSquared());
                float MFace = missile.getFacing();
                double TfaceRad = Math.toRadians(target.getFacing());
                float TCol = target.getCollisionRadius();
                float sx = (float) (TCol*.5*Math.cos(TfaceRad));
                float sy = (float) (TCol*.5*Math.sin(TfaceRad));
                Vector2f TNose = new Vector2f(sx, sy);
                float RangeToTarget = MathUtils.getDistance(TLoc, MLoc);
                //float TimeToTarget = RangeToTarget/MSpeed;                      //how long till you hit


                //testing InterceptPoint
                Vector2f Lvec = LeadVector(TLoc, TVel, MLoc, MVel);

                //////////////////////////////////////////////////////////////////////////////////////////

                //override when in range
                if (RangeToTarget<MaxRange)
                {
                    Lvec= multV2f(TVel, Leadtime);
                    baseoftarget=0;
                    oftarget=0;
                    oftargetby=0;
                }

                //////////////////////////////////////////////////////////////////////////////////////////


                //lead target
                Vector2f TLead = add(TLoc,            //target location+
                        Lvec,       //target speed*time to get to target+
                        null);      // if its too long just assume 3 seconds is long enough to course correct



                //aim at nose (can be changed to part targeting with a few tweaks)
                Vector2f TNoseLead = add(TLead, TNose, null);//aim at the nose of the target(optional)

                //main aiming (to find angle you are off target by)
                float AngleToEnemy = MathUtils.getAngle(MLoc, TNoseLead);
                float AtTarget = getAngleDifference(  //how far off target you are
                        MFace, AngleToEnemy);          //where missile pointing, where target is in relation


                float AbsAngD = Math.abs(AtTarget-oftargetby);


                //////////////////////////////////////////////////////////////////////////////////////////////


                //point towards target
                if (AbsAngD > 0.5)
                {                                  //makes missile fly off target
                    missile.giveCommand(AtTarget > oftargetby
                            ? ShipCommand.TURN_LEFT : ShipCommand.TURN_RIGHT);
                }

                ////////////////////////////////////////////////////////////////////////////////////////////////////


                //correct missile velocity vector to be the same as missile facing
                if (AbsAngD < 5)
                {
                    //course correct for missile velocity vector
                    float MFlightAng = MathUtils.getAngle(new Vector2f(0, 0), MVel);
                    float MFlightCC = getAngleDifference(MFace, MFlightAng);
                    if (Math.abs(MFlightCC)>20)
                    {
                        missile.giveCommand(MFlightCC < 0
                                ? ShipCommand.STRAFE_LEFT : ShipCommand.STRAFE_RIGHT);
                    }

                }

                /////////////////////////////////////////////////////////////////////////////////////////////////


                //stop turning once you are on target (way of the hack, ignores missile limitations)
                if (AbsAngD < 0.4)
                {
                    missile.setAngularVelocity(0);
                }

                ///////////////////////////////////////////////////////////////////////////////////////////////


                //acceleration code (stopNturn compatible)
                if (StopNTurn)
                {
                    if (AbsAngD < 40)
                    {
                        missile.giveCommand(ShipCommand.ACCELERATE);
                    }
                    if (MVel.lengthSquared()<TVel.lengthSquared())
                    {
                        missile.giveCommand(ShipCommand.ACCELERATE);
                    }
                    if (AbsAngD > 120)        //if you missed stop and turn around
                    {
                        missile.giveCommand(ShipCommand.DECELERATE);
                    }
                }
                else{missile.giveCommand(ShipCommand.ACCELERATE);}

                /////////////////////////////////////////////////////////////////////////////////////////////////


                //testcode    spawns particle at aim points
                CombatEngineAPI engine = Global.getCombatEngine(); //engine
                //engine.addSmoothParticle(TLead, new Vector2f(0,0), 5, 1, 1, new Color(255,10,15,255));
                //engine.addSmoothParticle(TNoseLead, new Vector2f(0,0), 5, 1, 1, new Color(93, 255, 40,255));
                //engine.addSmoothParticle(MLoc, multV2f(MVel, 4), 5, .5f, 1, new Color(248, 244, 255,255));
                //engine.addSmoothParticle(MLoc, new Vector2f(0,0), 5, .5f, 1, new Color(2, 255, 250,255));


                /////////////////////////////////////////////////////////////////////////////////////////////////

                if (VTimer.intervalElapsed() && RangeToTarget<MinRange && PVolleys>0)
                {
                    PVolleys--;
                    {
                        {
                            //spawn projectiles
                            int counts=0;         //how many projectiles?
                            do {
                                float angRAND= (float) ((PArk*(.5-Math.random())));          //angle of spread
                                float speedRAND= (float) (((MaxRange-MinRange)/Lifetime)*Math.random());//variance of speed
                                float PSpeed = (MinRange/Lifetime)+speedRAND;                           //speed of bullets launched
                                float PAng = (IsFlechet ? missile.getFacing()                //direction of fire
                                        :missile.getFacing()+AtTarget)-angRAND;
                                float x = (float) (PSpeed*Math.cos(Math.toRadians(PAng)));
                                float y = (float) (PSpeed*Math.sin(Math.toRadians(PAng)));
                                Vector2f PVec = new Vector2f(x,y);                     //convert all that into a vector
                                Vector2f PMVec = add(MVel, PVec, null);                //true Pvec
                                engine.spawnProjectile(
                                        missile.getSource(),
                                        null,                            //who made you
                                        Projectile,                      //spawn the projectile
                                        MLoc,                            //Vector2f firing origin point
                                        PAng,                            //float angle of fire
                                        PMVec);                          //Vector2f firing velocity

                                /////////////////////////////////////////////////////////////////////////////////////

                                counts++;


                                ////////////////////////////////////////////////////////////////////////////////


                                //optional explosion/particle glows
                                engine.addHitParticle(MLoc, PMVec,
                                        5f,1f,1f,new Color(112, 152, 137,255)); //size, alpha, duration, color
                                engine.spawnExplosion(MLoc, PMVec,
                                        new Color(255, 86, 13,255), 15f, .1f);  //color,size,duration(max)

                                //////////////////////////////////////////////////////////////////////////////////
                            }while (counts<PNum);
                        }
                    }
                    //////////////////////////////////////////////////////////////////////////////////

                    //main explosion flash
                    //engine.addHitParticle(MLoc, missile.getVelocity(),10,1f,1f,new Color(255, 12, 25,255));
                }

                //////////////////////////////////////////////////////////////////////////////////


                if (PVolleys<1)
                {
                    //to stop missile shooting again
                    engine.removeEntity(missile);   //make missile go poof
                    //missile.flameOut();               //make missile flame out
                }
            }

            //////////////////////////////////////////////////////////////////////////////////////////////////////


            //stop if you have no target
            if (target==null && straighttimer<1)
            {missile.giveCommand(ShipCommand.DECELERATE);}

            ///////////////////////////////////////////////////////////////////////////////////////////////////////////


            //clear target if target is gone
            if (target == null // unset
                    ||  ( missile.getOwner() == target.getOwner() )        // friendly
                    ||  !Global.getCombatEngine().isEntityInPlay(target)   // completely removed
                    || ( target instanceof ShipAPI && ((ShipAPI)target).isHulk() ))//dead
            {

                target = findBestTarget(missile);
                return;
            }
        }
    }

    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////


    // Will be in next LazyLib version
    public static float getAngleDifference(float angle1, float angle2)
    {
        float distance = (angle2 - angle1) + 180f;
        distance = (distance / 360.0f);
        distance = ((distance - (float) Math.floor(distance)) * 360f) - 180f;
        return distance;
    }

    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////


    //multiply vectors //own code
    public static Vector2f multV2f(Vector2f Vector1, float Multiplier)
    {
        float v1x = Vector1.getX()*Multiplier;
        float v1y = Vector1.getY()*Multiplier;
        Vector2f v1end = new Vector2f(v1x, v1y);
        return v1end;
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //find intercept between a missile and a target
    //adapted from code by Jens Seiler
    //http://jaran.de/goodbits/2011/07/17/calculating-an-intercept-course-to-a-target-with-constant-direction-and-velocity-in-a-2-dimensional-plane/
    //have fun if you want to actually understand what going on
    //basicaly gets where T will be by the time M gets to it
    public static Vector2f LeadVector(Vector2f TLoc, Vector2f TVel, Vector2f MLoc, Vector2f MVel)
    {
        //get missiles speed
        float MSpeed = (float)Math.sqrt(MVel.lengthSquared());

        //separate out the vectors
        float Tx = TLoc.getX();
        float Ty = TLoc.getY();
        float Mx = MLoc.getX();
        float My = MLoc.getY();

        float TVx = TVel.getX();
        float TVy = TVel.getY();
        float MVx = MVel.getX();
        float MVy = MVel.getY();

        //subtract position vectors
        float x1 = Tx - Mx;
        float y1 = Ty - My;

        //quadratic fun
        float h1 = TVx*TVx + TVy*TVy - MSpeed*MSpeed;
        if (h1==0)
        {
            h1= (float) .00001;
        }

        float minusMHalf = -(x1*TVx + y1*TVy)/h1;  // h2/h1

        float discriminant = minusMHalf * minusMHalf - (x1*x1 + y1*y1)/h1; // (h2/h1)^2-h3/h1 (ie D)

        //can they intersect?
        if (discriminant < 0)
        {
            return TLoc;
        }


        double root = Math.sqrt(discriminant);

        double t1 = minusMHalf + root;
        double t2 = minusMHalf - root;

        double tMin = Math.min(t1, t2);
        double tMax = Math.max(t1, t2);
        //which of the 2 is smaller
        double time = tMin > 0 ? tMin : tMax;

        //can return -ve time (this is less then useful)
        if (time < 0)
        {
            return TLoc;
        }

        //calculate vector
        return new Vector2f((float)(time * TVx), (float)(time * TVy));
    }
}
[close]


soon!!!! soon the KitchenSinkAI will be complete!!!
[close]
old see kitchenSINKai series
« Last Edit: December 11, 2013, 02:29:52 PM by dmaiski »
Logged
BISO
(WIP) lots of shiny new weapons ( :-[ i have more weapons then sprites :-[ )

i got a cat pad
its like a mouse pad but better!

dmaiski

  • Captain
  • ****
  • Posts: 422
  • resistance is futile
    • View Profile
Re: The Radioactive Code Dump
« Reply #49 on: November 20, 2013, 07:58:44 PM »

muahahahahahaha
the Mk.1 kitchenSINKai
so named because it throws evrything includint the kitchen sink at you
Spoiler
sexy looking drones being launched
Spoiler
[close]
less sexy harpoons shooting whatever they have in their vast arsenal
Spoiler
[close]

but in all seriousness, this came about in a flash of inspiration from the crazy idea that you could use a list to pick more then one weapon and stick it all into a drone. thus I armed my drone with machineguns, cannons, missiles, heatseakers, and a lightning gun...

and that all tacked onto a single missile, we here at the BISO reaserch lab follow the way of MOAR DAKAKAKAKA!!! so it is seen as a great achivement.


how to use:
put weapons into list, it will pick one to shoot when its in range, rest is self explanatory(and heavily anotated)

notes:
can orbit a ship like a drone AI
can fire in bursts
stays at set distance from target
fully customiseable
all anotated so you can understand what it does
fairly fast (in terms of procesing speed and lag)

code(600 line behemoth)
Spoiler
Code: java
package data.scripts.MissileAI;


import com.fs.starfarer.api.Global;
import com.fs.starfarer.api.combat.*;
import com.fs.starfarer.api.util.IntervalUtil;
import org.lazywizard.lazylib.MathUtils;
import org.lazywizard.lazylib.combat.AIUtils;
import org.lazywizard.lazylib.combat.CombatUtils;
import org.lwjgl.util.vector.Vector2f;

import java.awt.*;
import java.util.Iterator;
import java.util.List;

//import static jar.UtilityKit.LeadVector;
import static org.lwjgl.util.vector.Vector2f.add;

public class KitchenSinkai implements MissileAIPlugin
{
    // Our missile object
    private final MissileAPI missile;
    // Our current target (can be null)
    private CombatEntityAPI target;

    //////////////////////////////////////////////////////////////////////////////////////////////////////////

    //nasty stuff drone uses as weapons
    //replace with projectile cannon like "chaingun" or "vulcan" from vanilla,
    // or your own weapon(no beams)(Ballistic_as_beam are fine)
    private static String [] Guns = {
            "DUMMY1",
            "DUMMY2",
            "chaingun",
            "vulcan",
    };

    //and missiles
    private static String [] Missiles = {
            "Linghning",                 //special, shoots lightning bolts
            "harpoon_single",
            "heatseeker",
    };

    //////////////////////////////////////////////////////////////////////////////////////////////////////


    //stuff that you can change  (MAIN missile stage AI components)
    private float straighttimer=(float)(10*Math.random());              //non targeting time
    private IntervalUtil STimer = new IntervalUtil(.3f, .0f);   //rate of countdown
    private final double isEffectedbyflare=Math.random();       //flare stuff
    private boolean flarelockout=false;
    private float oftarget=300f*((float)(.5-Math.random()));    //max initial off target (scatters missiles at launch)
    private float baseoftarget=40f*((float)(.5-Math.random())); //min off target (makes missiles stay scattered)
    public static final String flare="flare";
    private IntervalUtil Timer = new IntervalUtil(.1f, .0f);    //rate of countdown for off target
    private final boolean StopNTurn=false;                      //when far off target missile will stop and turn to aim

    //weaponpicker
    private float MislChance=.8f;                        //chance of missiles (1-MislChance)*100=%shots missile
    private int MislVolley=1;                            //number of missiles per
    private int ProjVolley=4;                            //number of Projectiles per
    private boolean Missil=(Math.random() > MislChance);                 //is it firing a missile
    private String Projectile = (Missil? Missiles:Guns)  //picks the weapon
            [(int) ((Missil? Missiles:Guns).length * Math.random())];


    //MIRV/Flechet script (core firing script of a drone missile)
    //dummy weapon  supports missiles and projectiles
    //suggested projectile stats, range:1,  speed:1
    private final float MaxRange=850f;    //max range when it starts to direct aim target
    private final float MinRange=600f;    //min range when it detonates
    private final float PArk=25;          //ark projectiles fly in
    private final int PNum=1;             //shots per volley
    private int PVolleys=Missil ? MislVolley:ProjVolley;    //volleys/burst fire for drone
    private int VCount=0;                 //counter
    private final float Lifetime=1f;      //how long the projectile being used can stay alive (IMPORTANT)
    //this is a value you must enter it is equal to range/ProjSpeed and works as a multiplier to make
    // firing velocity not overshoot the target (due to long life)
    private final float Leadtime=1f;       //fiddle with this if your projectiles are missing the target
    //determines how far ahead of the target your weapon aims
    private IntervalUtil VTimer = new IntervalUtil(.15f, .01f);         //rate of fire
    private final boolean IsFlechet=true;//are the shots fired by missile aimed or follow missile facing?
    //if IsFlechet=false missile will fire shots at the target ignoring its direction of travel

    //DRONE script (core behaviour script)
    private final boolean Orbit=true;     //will the drone strafe around target?
    private double ODir=Math.random(); //direction of strafe
    private IntervalUtil OTimer = new IntervalUtil(3f, .01f); //delay between strafe switch
    private IntervalUtil BTimer = new IntervalUtil(1.8f, .01f); //delay between each burst
    private final float MinOrbit=.9f;       //how close will the drone move in
    private final float MaxOrbit=1.2f;      //how far will the drone move  away
    private boolean Close=true;             //thruster bool
    private boolean Loaded=true;            //fireing bool


    //////////////////////////////////////////////////////////////////////////////////////////////////////////


    public static float searchrange = 500; //500 range default

    //public static float Ark = 30;          //60deg ark   default

    boolean newlaunch=true;                //only do mouse target once on a newly launched missile
    static float MWRange = 9999f;          //hack to make missile ignore null weapon
    public KitchenSinkai(MissileAPI missile)
    {
        this.missile = missile;
        MWRange = missile.getWeapon() != null ? missile.getWeapon().getRange() : 9999f;
        searchrange=MWRange;//based on weapon
        //Ark=5+missile.getWeapon().getArc()/4;           //based on weapon

        // Support for 'fire at target by clicking on them' behavior
        if (newlaunch=true)
        {
            newlaunch=false;
            //get targets near mouse
            Vector2f MouseT = missile.getSource().getMouseTarget();
            if (MathUtils.getDistance(missile,MouseT)<searchrange)
            {
                List directTargets = CombatUtils.getShipsWithinRange(
                        MouseT, 100f, true);

                if (!directTargets.isEmpty())
                {
                    ShipAPI tmp;
                    for (Iterator iter = directTargets.iterator(); iter.hasNext();)
                    {
                        //filter out friendlies
                        tmp = (ShipAPI) iter.next();
                        if (tmp.getOwner() != missile.getSource().getOwner())
                        {
                            target = tmp;
                            break;
                        }
                    }
                }
            }
        }

        // Otherwise, use default targeting AI
        if (target == null)
        {
            target = findBestTarget(missile);
        }
    }
    ///////////////////////////////////////////////////////////////////////////////////////////////////////


    //flare finder

    public static MissileAPI checkforflare(MissileAPI missile)
    {
        MissileAPI retv = null;                                   //if no flares return null
        CombatEngineAPI engine = Global.getCombatEngine();
        List nearbymissiles = engine.getMissiles();               //(target.getCollisionRadius()*10)
        if (!nearbymissiles.isEmpty())
        {
            MissileAPI tmp;
            for (Iterator iter = nearbymissiles.iterator(); iter.hasNext();)
            {
                tmp = (MissileAPI) iter.next();

                if ((tmp.getProjectileSpecId()).startsWith(flare) //is it a flare
                        && MathUtils.getDistance(tmp,missile)<300 //is it near your missile
                        && missile.getOwner()!=tmp.getOwner()     //is it en enemy flare
                        && Math.random()>.5)                      //chance of failure prevents all missiles
                {                                                 //going after one flare at the same time
                    //tmp= (MissileAPI) nearbymissiles.get(0);    //testing code
                    retv = tmp;                                   //return flare
                    break;                                        //stop searching you found your flare
                }
            }
        }
        return retv;
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////

    /* turned off for this AI
    //search for targets in ark in front
    public static MissileAPI findBestTarget(MissileAPI missile)
    {
        //find targets in ark in front of missile
        ArrayList targets = getEnemyMissilesInArk(missile, Ark, searchrange, true);
        if (!targets.isEmpty())
        {   //pick random missile in list     //replace with .get(0) for nearest
            return (MissileAPI) targets.get((int)(targets.size()*Math.random()));
        }
        else return null;

    }
    */
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////


    //get ship target or search for closest enemy
    public static ShipAPI findBestTarget(MissileAPI missile)
    {
        ShipAPI source = missile.getSource();                             //who launched you?
        if (source != null && source.getShipTarget() != null              //check for nulls
                && !source.getShipTarget().isHulk()                       //check if its alive
                && MathUtils.getDistance(source.getShipTarget(), source)  //get range to target
                <MWRange)                          //check if its in range
        {
            return source.getShipTarget();
        }

        return AIUtils.getNearestEnemy(missile);
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////


    //main missile AI
    @Override
    public void advance(float amount)
    {

        //timer
        Timer.advance(amount);
        STimer.advance(amount);
        VTimer.advance(amount);
        OTimer.advance(amount);
        BTimer.advance(amount);

        ////////////////////////////////////////////////////////////////////////////////////////////////////////////

        //go straight for x after launch
        if (straighttimer>0)
        {
            missile.giveCommand(ShipCommand.ACCELERATE);
            if (STimer.intervalElapsed())
            {straighttimer--;}
            return;
        }
        else
        {
            ///////////////////////////////////////////////////////////////////////////////////////////////////////////


            // Apparently commands still work while fizzling
            if (missile.isFading() || missile.isFizzling())
            {
                return;
            }

            ////////////////////////////////////////////////////////////////////////////////////////////////////////////



            //find a target
            if (target == null)
            {
                target = findBestTarget(missile);
            }

            /////////////////////////////////////////////////////////////////////////////////////////////////////////////


            //if missile is susceptible to flare
            if (isEffectedbyflare>0.4                     //can a flare effect it
                    && !flarelockout                      //has it already been set to ignore flares
                    && Timer.intervalElapsed())           //check every .10 sec

            {
                MissileAPI flaretarget = checkforflare(missile);
                if (flaretarget!=null)
                {
                    target=flaretarget;         //set flare as target
                    flarelockout=true;          //ignore future flares
                }

            }

            //////////////////////////////////////////////////////////////////////////////////////////////////////////////


            //pathing ai
            if (target!=null)               //if you have a target
            {
                //reduction off target/gradual aiming
                if (Timer.intervalElapsed())
                {
                    oftarget=(oftarget>0 ? oftarget-1 : oftarget+1); //reduce off target counter;
                }

                //////////////////////////////////////////////////////////////////////////////////////////////
                //check if scatter code needed
                float targetcolision = target.getCollisionRadius();       //get colision radi
                if (MathUtils.getDistance(missile, target)<75+(targetcolision))
                {                                                         //cutoff for baseoftarget behaviour
                    baseoftarget=0;      //aim variable if you got in close enough is 0 (ie go hit the enemy target)
                }

                ///////////////////////////////////////////////////////////////////////////////////////////////


                //how much off target will the missiles be
                float oftargetby = (0 + (oftarget + (baseoftarget * targetcolision / 75)));

                //////////////////////////////////////////////////////////////////////////////////////////

                //aming
                //1step calculations of various stats
                Vector2f MVel = missile.getVelocity();
                Vector2f MLoc = missile.getLocation();
                Vector2f TLoc = target.getLocation();
                Vector2f TVel = target.getVelocity();
                //float MSpeed = (float)Math.sqrt(MVel.lengthSquared());
                //float TSpeed = (float)Math.sqrt(TVel.lengthSquared());
                float MFace = missile.getFacing();
                double TfaceRad = Math.toRadians(target.getFacing());
                float TCol = target.getCollisionRadius();
                float sx = (float) (TCol*.5*Math.cos(TfaceRad));
                float sy = (float) (TCol*.5*Math.sin(TfaceRad));
                Vector2f TNose = new Vector2f(sx, sy);
                float RangeToTarget = MathUtils.getDistance(TLoc, MLoc);
                //float TimeToTarget = RangeToTarget/MSpeed;                      //how long till you hit


                //testing InterceptPoint
                Vector2f Lvec = LeadVector(TLoc, TVel, MLoc, MVel);

                //////////////////////////////////////////////////////////////////////////////////////////

                //override when in range
                if (RangeToTarget<MaxRange)
                {
                    Lvec= multV2f(TVel, Leadtime);
                    baseoftarget=0;
                    oftarget=0;
                    oftargetby=0;
                }

                //////////////////////////////////////////////////////////////////////////////////////////


                //lead target
                Vector2f TLead = add(TLoc,            //target location+
                        Lvec,       //target speed*time to get to target+
                        null);      // if its too long just assume 3 seconds is long enough to course correct



                //aim at nose (can be changed to part targeting with a few tweaks)
                Vector2f TNoseLead = add(TLead, TNose, null);//aim at the nose of the target(optional)

                //main aiming (to find angle you are off target by)
                float AngleToEnemy = MathUtils.getAngle(MLoc, TNoseLead);
                float AtTarget = getAngleDifference(  //how far off target you are
                        MFace, AngleToEnemy);          //where missile pointing, where target is in relation


                float AbsAngD = Math.abs(AtTarget-oftargetby);


                //////////////////////////////////////////////////////////////////////////////////////////////


                //point towards target
                if (AbsAngD > 0.5)
                {                                  //makes missile fly off target
                    missile.giveCommand(AtTarget > oftargetby
                            ? ShipCommand.TURN_LEFT : ShipCommand.TURN_RIGHT);
                }

                ////////////////////////////////////////////////////////////////////////////////////////////////////


                //correct missile velocity vector to be the same as missile facing
                if (AbsAngD < 5)
                {
                    //course correct for missile velocity vector
                    float MFlightAng = MathUtils.getAngle(new Vector2f(0, 0), MVel);
                    float MFlightCC = getAngleDifference(MFace, MFlightAng);
                    if (Math.abs(MFlightCC)>20)
                    {
                        missile.giveCommand(MFlightCC < 0
                                ? ShipCommand.STRAFE_LEFT : ShipCommand.STRAFE_RIGHT);
                    }

                }

                /////////////////////////////////////////////////////////////////////////////////////////////////


                //stop turning once you are on target (way of the hack, ignores missile limitations)
                if (AbsAngD < 0.4)
                {
                    missile.setAngularVelocity(0);
                }

                ///////////////////////////////////////////////////////////////////////////////////////////////

                if(RangeToTarget>MaxRange)
                {
                    //acceleration code (stopNturn compatible)
                    if (StopNTurn)
                    {
                        if (AbsAngD < 40)
                        {
                            missile.giveCommand(ShipCommand.ACCELERATE);
                        }
                        if (MVel.lengthSquared()<TVel.lengthSquared())
                        {
                            missile.giveCommand(ShipCommand.ACCELERATE);
                        }
                        if (AbsAngD > 120)        //if you missed stop and turn around
                        {
                            missile.giveCommand(ShipCommand.DECELERATE);
                        }
                    }
                    else{missile.giveCommand(ShipCommand.ACCELERATE);}
                }
                /////////////////////////////////////////////////////////////////////////////////////////////////

                else                                                   //move back and forth
                {
                    if (RangeToTarget<MinRange*MinOrbit){Close=false;} //boolean to achive back and forth motion
                    if (RangeToTarget>MinRange*MaxOrbit){Close=true;}
                    if (Close) {missile.giveCommand(ShipCommand.ACCELERATE);}
                    else {missile.giveCommand(ShipCommand.ACCELERATE_BACKWARDS);}

                    /////////////////////////////////////////////////////////////////////////////////////////////////

                    //orbit target
                    if (Orbit)
                    {
                        if (OTimer.intervalElapsed())
                        {
                            ODir=Math.random();
                        }
                        missile.giveCommand(ODir < .5
                                ? ShipCommand.STRAFE_LEFT : ShipCommand.STRAFE_RIGHT);
                    }
                }

                /////////////////////////////////////////////////////////////////////////////////////////////////


                //testcode    spawns particle at aim points
                CombatEngineAPI engine = Global.getCombatEngine(); //engine
                //engine.addSmoothParticle(TLead, new Vector2f(0,0), 5, 1, 1, new Color(255,10,15,255));
                //engine.addSmoothParticle(TNoseLead, new Vector2f(0,0), 5, 1, 1, new Color(93, 255, 40,255));
                //engine.addSmoothParticle(MLoc, multV2f(MVel, 4), 5, .5f, 1, new Color(248, 244, 255,255));
                //engine.addSmoothParticle(MLoc, new Vector2f(0,0), 5, .5f, 1, new Color(2, 255, 250,255));


                /////////////////////////////////////////////////////////////////////////////////////////////////

                //reload
                if (!Loaded && BTimer.intervalElapsed())
                {
                    Loaded=true;
                    VCount=0;
                    {
                        //weaponpicker
                        Missil=(Math.random() > MislChance);         //is it firing a missile
                        Projectile = (Missil? Missiles:Guns)         //picks the weapon
                                [(int) ((Missil? Missiles:Guns).length * Math.random())];
                        PVolleys=Missil ? MislVolley:ProjVolley;
                    }
                }



                /////////////////////////////////////////////////////////////////////////////////////////////////


                if (Loaded && VTimer.intervalElapsed() && RangeToTarget<MinRange && VCount<PVolleys)
                {
                    VCount++;
                    {
                        if (Projectile.equals("Linghning"))     //cause lightningbolts!!
                        {
                            engine.spawnEmpArc(missile.getSource(), MLoc, target, target,
                                    DamageType.ENERGY,
                                    50f,
                                    2000,
                                    10000f, // max range
                                    "tachyon_lance_emp_impact",
                                    20f, // thickness
                                    new Color(255,10,15,255),
                                    new Color(255,255,255,255)
                            );
                        }
                        else
                        {
                            //spawn projectiles
                            int counts=0;         //how many projectiles?
                            do {
                                float angRAND= (float) ((PArk*(.5-Math.random())));          //angle of spread
                                float speedRAND= (float) (((MaxRange-MinRange)/Lifetime)*Math.random());//variance of speed
                                float PSpeed = (MinRange/Lifetime)+speedRAND;                           //speed of bullets launched
                                float PAng = (IsFlechet ? missile.getFacing()                //direction of fire
                                        :missile.getFacing()+AtTarget)-angRAND;
                                float x = (float) (PSpeed*Math.cos(Math.toRadians(PAng)));
                                float y = (float) (PSpeed*Math.sin(Math.toRadians(PAng)));
                                Vector2f PVec = new Vector2f(x,y);                     //convert all that into a vector
                                Vector2f PMVec = add(MVel, PVec, null);                //true Pvec
                                engine.spawnProjectile(
                                        missile.getSource(),
                                        null,                            //who made you
                                        Projectile,                      //spawn the projectile
                                        MLoc,                            //Vector2f firing origin point
                                        PAng,                            //float angle of fire
                                        PMVec);                          //Vector2f firing velocity

                                /////////////////////////////////////////////////////////////////////////////////////

                                counts++;


                                ////////////////////////////////////////////////////////////////////////////////


                                //optional explosion/particle glows
                                engine.addHitParticle(MLoc, PMVec,
                                        5f,1f,1f,new Color(112, 152, 137,255)); //size, alpha, duration, color
                                engine.spawnExplosion(MLoc, PMVec,
                                        new Color(255, 86, 13,255), 15f, .1f);  //color,size,duration(max)

                                //////////////////////////////////////////////////////////////////////////////////
                            }while (counts<PNum);
                        }
                    }
                    //////////////////////////////////////////////////////////////////////////////////

                    //main explosion flash
                    //engine.addHitParticle(MLoc, missile.getVelocity(),10,1f,1f,new Color(255, 12, 25,255));
                }

                //////////////////////////////////////////////////////////////////////////////////


                if (VCount>=PVolleys)
                {
                    Loaded=false;
                }
            }


            //////////////////////////////////////////////////////////////////////////////////////////////////////


            //stop if you have no target
            if (target==null && straighttimer<1)
            {missile.giveCommand(ShipCommand.DECELERATE);}

            ///////////////////////////////////////////////////////////////////////////////////////////////////////////


            //clear target if target is gone
            if (target == null // unset
                    ||  ( missile.getOwner() == target.getOwner() )        // friendly
                    ||  !Global.getCombatEngine().isEntityInPlay(target)   // completely removed
                    || ( target instanceof ShipAPI && ((ShipAPI)target).isHulk() ))//dead
            {

                target = findBestTarget(missile);
                return;
            }
        }
    }

    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////


    // Will be in next LazyLib version
    public static float getAngleDifference(float angle1, float angle2)
    {
        float distance = (angle2 - angle1) + 180f;
        distance = (distance / 360.0f);
        distance = ((distance - (float) Math.floor(distance)) * 360f) - 180f;
        return distance;
    }

    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////


    //multiply vectors //own code
    public static Vector2f multV2f(Vector2f Vector1, float Multiplier)
    {
        float v1x = Vector1.getX()*Multiplier;
        float v1y = Vector1.getY()*Multiplier;
        Vector2f v1end = new Vector2f(v1x, v1y);
        return v1end;
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////

    //find intercept between a missile and a target
    //adapted from code by Jens Seiler
    //http://jaran.de/goodbits/2011/07/17/calculating-an-intercept-course-to-a-target-with-constant-direction-and-velocity-in-a-2-dimensional-plane/
    //have fun if you want to actually understand what going on
    //basicaly gets where T will be by the time M gets to it
    public static Vector2f LeadVector(Vector2f TLoc, Vector2f TVel, Vector2f MLoc, Vector2f MVel)
    {
        //get missiles speed
        float MSpeed = (float)Math.sqrt(MVel.lengthSquared());

        //separate out the vectors
        float Tx = TLoc.getX();
        float Ty = TLoc.getY();
        float Mx = MLoc.getX();
        float My = MLoc.getY();

        float TVx = TVel.getX();
        float TVy = TVel.getY();
        float MVx = MVel.getX();
        float MVy = MVel.getY();

        //subtract position vectors
        float x1 = Tx - Mx;
        float y1 = Ty - My;

        //quadratic fun
        float h1 = TVx*TVx + TVy*TVy - MSpeed*MSpeed;
        if (h1==0)
        {
            h1= .0001f;
        }

        float minusMHalf = -(x1*TVx + y1*TVy)/h1;  // h2/h1

        float discriminant = minusMHalf * minusMHalf - (x1*x1 + y1*y1)/h1; // (h2/h1)^2-h3/h1 (ie D)

        //can they intersect?
        if (discriminant < 0)
        {
            return TLoc;
        }


        double root = Math.sqrt(discriminant);

        double t1 = minusMHalf + root;
        double t2 = minusMHalf - root;

        double tMin = Math.min(t1, t2);
        double tMax = Math.max(t1, t2);
        //which of the 2 is smaller
        double time = tMin > 0 ? tMin : tMax;

        //can return -ve time (this is less then useful)
        if (time < 0)
        {
            return TLoc;
        }

        //calculate vector
        return new Vector2f((float)(time * TVx), (float)(time * TVy));
    }
}
[close]

notes, fixing old code up to scratch due to 0000.1 bug in leadvector
[close]
old
« Last Edit: December 11, 2013, 02:29:14 PM by dmaiski »
Logged
BISO
(WIP) lots of shiny new weapons ( :-[ i have more weapons then sprites :-[ )

i got a cat pad
its like a mouse pad but better!

dmaiski

  • Captain
  • ****
  • Posts: 422
  • resistance is futile
    • View Profile
Re: The Radioactive Code Dump
« Reply #50 on: November 22, 2013, 05:05:48 PM »

MK2 kitchenSINKai
Spoiler
now with even more options!
(by that I mean 100lines worth of things you can set up)

Options index:
//MAIN missile stage AI components - dose what it says on the tin, basic stuff to make the missile go, spread out, fly in a line ect
//flarechasing - to make it chase flares
//weaving behaviour - missile will move in a sine wave pattern (overiding)
//scripts for HardTargeting/orbitrange - if its on, once missile gets in range, it will fly straight at the target (less accurate, but better for some uses)
//Heatseeker like behaviour - basicly heatseekers, but better (overiding)
//DRONE script (core behaviour script) - makes the missile orbit that target without getting in close
//weapons - we can arm our missiles with miniguns and rocket launchers
////weaponlist - a list of weapons the weaponpicker can pick from
////weaponpicker - picks a weapon to blast your enemy with
////MIRV/Flechet script (core firing script of a drone missile) - like the vanilla mirv, but better with more options, smarter... evil

at an eye watering 749lines of code
(most of it is comented so you can in theory mess with its guts)
I present to you the MissileAi script with evrything including the kitchen sink in it

sidenotes:
its still faster the the comparable vanilla missile AI, don't ask how... it involved the selling of souls
Spoiler
Code: java
package data.scripts.MissileAI;


import com.fs.starfarer.api.Global;
import com.fs.starfarer.api.combat.*;
import com.fs.starfarer.api.util.IntervalUtil;
import org.lazywizard.lazylib.MathUtils;
import org.lazywizard.lazylib.combat.AIUtils;
import org.lazywizard.lazylib.combat.CombatUtils;
import org.lwjgl.util.vector.Vector2f;

import java.awt.*;
import java.util.Iterator;
import java.util.List;

//import static jar.UtilityKit.LeadVector;
import static org.lwjgl.util.vector.Vector2f.add;

public class kitchenSINKai implements MissileAIPlugin
{
    // Our missile object
    private final MissileAPI missile;
    // Our current target (can be null)
    private CombatEntityAPI target;

    //stuff that you can change  (MAIN missile stage AI components)
    private float straighttimer=(float)(10*Math.random());              //non targeting time
    private IntervalUtil STimer = new IntervalUtil(.3f, .0f);   //rate of countdown
    private final double isEffectedbyflare=Math.random();       //flare stuff
    private boolean flarelockout=false;
    private float oftarget=30f*((float)(.5-Math.random()));    //max initial off target (scatters missiles at launch)
    private float baseoftarget=4f*((float)(.5-Math.random())); //min off target (makes missiles stay scattered)

    //flarechasing
    public static final String flare="flare";
    public final float BaseIgnoreChance=.3f;                    //chance that flares cannot effect it by default
    public final float LockCHance=.3f;                          //chance that future flares it encounters wont effect it
    private IntervalUtil Timer = new IntervalUtil(.1f, .0f);    //rate of countdown for off target
    private final boolean StopNTurn=false;                      //when far off target missile will stop and turn to aim

    //weaving behaviour
    private final boolean Weave=false;                          //go in a zigzag path (improved over extingency script)
    private float Weavesize=40f;                                //how far of course it will go, in degrees
    private float WeaveFreq=40f;                                //how ofter it cycles (arbitary)
    private float weavecount=0;                                 //misc dont change

    //scripts for HardTargeting/orbitrange
    private final float MaxRange=850f;    //max range when it starts to direct aim target
    private final float MinRange=500f;    //min range when it detonates
    private final boolean HardTargeting=true;//will it turn to target once in range

    //Heatseeker like behaviour (uses Min/Max range)
    private final boolean Backtarget=true;   //dose it circle around back of ship
    private final float BackAng=45f;                     //min angle from rear
    private final boolean Straferound=false;             //strafe there or turn there
    private boolean BTcutout=false;                      //shuts down the circling, don't touch

    //DRONE script (core behaviour script)
    private final boolean isDrone=false; //makes missile bob in and out of MinRange and maintain that distance
    private final boolean Orbit=false;     //will the drone strafe around target?
    private double ODir=Math.random(); //direction of strafe
    private IntervalUtil OTimer = new IntervalUtil(3f, .01f); //delay between strafe switch
    //used in Backtarget as well
    private final float MinOrbit=.9f;       //how close will the drone move in
    private final float MaxOrbit=1.2f;      //how far will the drone move  away
    private boolean Close=true;             //thruster bool, don't touch

    //weapons
    //controls firing, everything below needs this on
    private final boolean canFire=false;   //is shooting enabled
    //////////////////////////////////////////////////////////////////////////////////////////////////////////

    //weaponlist
    //nasty stuff drone uses as weapons
    //replace with projectile cannon like "chaingun" or "vulcan" from vanilla,
    // or your own weapon(no beams)(Ballistic_as_beam are fine)
    private static String [] Guns = {
            "DUMMY1",
            "DUMMY2",
            "chaingun",
            "vulcan",
    };

    //and missiles
    private static String [] Missiles = {
            "Linghning",                 //special, shoots lightning bolts
            "harpoon_single",
            "heatseeker",
    };

    //////////////////////////////////////////////////////////////////////////////////////////////////////

    //weaponpicker
    private float MislChance=.8f;                        //chance of missiles (1-MislChance)*100=%shots missile
    private int MislVolley=1;                            //number of missiles per
    private int ProjVolley=4;                            //number of Projectiles per
    private boolean Missil=(Math.random() > MislChance);                 //is it firing a missile
    private String Projectile = (Missil? Missiles:Guns)  //picks the weapon
            [(int) ((Missil? Missiles:Guns).length * Math.random())];


    //MIRV/Flechet script (core firing script of a drone missile)
    //dummy weapon  supports missiles and projectiles
    //suggested projectile stats, range:1,  speed:1
    private final float PArk=25;          //ark projectiles fly in
    private final int PNum=1;             //shots per volley
    private int PVolleys=Missil ? MislVolley:ProjVolley;    //volleys/burst fire for drone
    private int VCount=0;                 //counter
    private final float Lifetime=1f;      //how long the projectile being used can stay alive (IMPORTANT)
    //this is a value you must enter it is equal to range/ProjSpeed and works as a multiplier to make
    // firing velocity not overshoot the target (due to long life)
    private final float Leadtime=1f;       //fiddle with this if your projectiles are missing the target
    //determines how far ahead of the target your weapon aims
    private IntervalUtil VTimer = new IntervalUtil(.15f, .01f);         //rate of fire
    private final boolean IsFlechet=true;//are the shots fired by missile aimed or follow missile facing?
    //if IsFlechet=false missile will fire shots at the target ignoring its direction of travel
    private boolean Loaded=true;            //fireing bool
    private boolean DespawnEmpty=false;      //should it de-spawn the missile once it has run out of ammo
    private boolean SpawnEmpty=false;        //to spawn an empty type subprojectile(casing)
    private boolean canReload=true;          //can reload after a burst
    private IntervalUtil BTimer = new IntervalUtil(1.8f, .01f); //delay between each burst
    public static void emptyspawn(CombatEngineAPI engine, MissileAPI missile)
    {
        engine.spawnProjectile(missile.getSource(),null,//who made you
                "DUMMY2",                        //empty missile,
                missile.getLocation(),           //Vector2f firing origin point
                missile.getFacing(),             //float angle of fire
                missile.getVelocity());          //Vector2f firing velocity)
    }
    //end of stuff you can change

    //////////////////////////////////////////////////////////////////////////////////////////////////////////


    public static float searchrange = 500; //500 range default

    //public static float Ark = 30;          //60deg ark   default

    boolean newlaunch=true;                //only do mouse target once on a newly launched missile
    static float MWRange = 9999f;          //hack to make missile ignore null weapon
    public kitchenSINKai(MissileAPI missile)
    {
        this.missile = missile;
        MWRange = missile.getWeapon() != null ? missile.getWeapon().getRange() : 9999f;
        searchrange=MWRange;//based on weapon
        //Ark=5+missile.getWeapon().getArc()/4;           //based on weapon
        //missile.setCollisionClass(CollisionClass.FIGHTER);//hack reclasify as fighter



        // Support for 'fire at target by clicking on them' behavior
        if (newlaunch=true)
        {
            newlaunch=false;
            //get targets near mouse
            Vector2f MouseT = missile.getSource().getMouseTarget();
            if (MathUtils.getDistance(missile,MouseT)<searchrange)
            {
                List directTargets = CombatUtils.getShipsWithinRange(
                        MouseT, 100f, true);

                if (!directTargets.isEmpty())
                {
                    ShipAPI tmp;
                    for (Iterator iter = directTargets.iterator(); iter.hasNext();)
                    {
                        //filter out friendlies
                        tmp = (ShipAPI) iter.next();
                        if (tmp.getOwner() != missile.getSource().getOwner())
                        {
                            target = tmp;
                            break;
                        }
                    }
                }
            }
        }

        // Otherwise, use default targeting AI
        if (target == null)
        {
            target = findBestTarget(missile);
        }
    }
    ///////////////////////////////////////////////////////////////////////////////////////////////////////


    //flare finder

    public static MissileAPI checkforflare(MissileAPI missile)
    {
        MissileAPI retv = null;                                   //if no flares return null
        CombatEngineAPI engine = Global.getCombatEngine();
        List nearbymissiles = engine.getMissiles();               //(target.getCollisionRadius()*10)
        if (!nearbymissiles.isEmpty())
        {
            MissileAPI tmp;
            for (Iterator iter = nearbymissiles.iterator(); iter.hasNext();)
            {
                tmp = (MissileAPI) iter.next();

                if ((tmp.getProjectileSpecId()).startsWith(flare) //is it a flare
                        && MathUtils.getDistance(tmp,missile)<300 //is it near your missile
                        && missile.getOwner()!=tmp.getOwner()     //is it en enemy flare
                        && Math.random()>.5)                      //chance of failure prevents all missiles
                {                                                 //going after one flare at the same time
                    //tmp= (MissileAPI) nearbymissiles.get(0);    //testing code
                    retv = tmp;                                   //return flare
                    break;                                        //stop searching you found your flare
                }
            }
        }
        return retv;
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////

    /* turned off for this AI
    //search for targets in ark in front
    public static MissileAPI findBestTarget(MissileAPI missile)
    {
        //find targets in ark in front of missile
        ArrayList targets = getEnemyMissilesInArk(missile, Ark, searchrange, true);
        if (!targets.isEmpty())
        {   //pick random missile in list     //replace with .get(0) for nearest
            return (MissileAPI) targets.get((int)(targets.size()*Math.random()));
        }
        else return null;

    }
    */
    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////


    //get ship target or search for closest enemy
    public static ShipAPI findBestTarget(MissileAPI missile)
    {
        ShipAPI source = missile.getSource();                             //who launched you?
        if (source != null && source.getShipTarget() != null              //check for nulls
                && !source.getShipTarget().isHulk()                       //check if its alive
                && MathUtils.getDistance(source.getShipTarget(), source)  //get range to target
                <MWRange)                          //check if its in range
        {
            return source.getShipTarget();
        }

        return AIUtils.getNearestEnemy(missile);
    }

    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////


    //main missile AI
    @Override
    public void advance(float amount)
    {

        //timer
        Timer.advance(amount);
        STimer.advance(amount);
        VTimer.advance(amount);
        OTimer.advance(amount);
        BTimer.advance(amount);
        weavecount++;
        if (weavecount>4000){weavecount=0;} //arbitary reset

        ////////////////////////////////////////////////////////////////////////////////////////////////////////////

        //go straight for x after launch
        if (straighttimer>0)
        {
            missile.giveCommand(ShipCommand.ACCELERATE);
            if (STimer.intervalElapsed())
            {straighttimer--;}
            return;
        }
        else
        {
            ///////////////////////////////////////////////////////////////////////////////////////////////////////////


            // Apparently commands still work while fizzling
            if (missile.isFading() || missile.isFizzling())
            {
                return;
            }

            ////////////////////////////////////////////////////////////////////////////////////////////////////////////



            //find a target
            if (target == null)
            {
                target = findBestTarget(missile);
            }

            /////////////////////////////////////////////////////////////////////////////////////////////////////////////


            //if missile is susceptible to flare
            if (isEffectedbyflare>BaseIgnoreChance        //can a flare effect it
                    && !flarelockout                      //has it already been set to ignore flares
                    && Timer.intervalElapsed())           //check every .10 sec

            {
                MissileAPI flaretarget = checkforflare(missile);
                if (flaretarget!=null)
                {
                    target=flaretarget;         //set flare as target
                    if (LockCHance<(float)Math.random())flarelockout=true;          //ignore future flares
                }

            }

            //////////////////////////////////////////////////////////////////////////////////////////////////////////////


            //pathing ai
            if (target!=null)               //if you have a target
            {
                //reduction off target/gradual aiming
                if (Timer.intervalElapsed())
                {
                    oftarget=(oftarget>0 ? oftarget-1 : oftarget+1); //reduce off target counter;
                }

                //////////////////////////////////////////////////////////////////////////////////////////////
                //check if scatter code needed
                float targetcolision = target.getCollisionRadius();       //get colision radi
                if (MathUtils.getDistance(missile, target)<75+(targetcolision))
                {                                                         //cutoff for baseoftarget behaviour
                    baseoftarget=0;      //aim variable if you got in close enough is 0 (ie go hit the enemy target)
                }

                ///////////////////////////////////////////////////////////////////////////////////////////////


                //how much off target will the missiles be
                float oftargetby = (0 + (oftarget + (baseoftarget * targetcolision / 75)));

                //////////////////////////////////////////////////////////////////////////////////////////

                //aming
                //1step calculations of various stats
                Vector2f MVel = missile.getVelocity();
                Vector2f MLoc = missile.getLocation();
                Vector2f TLoc = target.getLocation();
                Vector2f TVel = target.getVelocity();
                //float MSpeed = (float)Math.sqrt(MVel.lengthSquared());
                //float TSpeed = (float)Math.sqrt(TVel.lengthSquared());
                float MFace = missile.getFacing();
                float TFace = target.getFacing();
                double TfaceRad = Math.toRadians(TFace);
                float TCol = target.getCollisionRadius();
                float sx = (float) (TCol*.5*Math.cos(TfaceRad));
                float sy = (float) (TCol*.5*Math.sin(TfaceRad));
                Vector2f TNose = new Vector2f(sx, sy);
                float RangeToTarget = MathUtils.getDistance(TLoc, MLoc);
                //float TimeToTarget = RangeToTarget/MSpeed;                      //how long till you hit


                //testing InterceptPoint
                Vector2f Lvec = LeadVector(TLoc, TVel, MLoc, MVel);

                //////////////////////////////////////////////////////////////////////////////////////////

                //override when in range
                if (HardTargeting && RangeToTarget<MaxRange)
                {
                    Lvec= multV2f(TVel, Leadtime);
                    baseoftarget=0;
                    oftarget=0;
                    oftargetby=0;
                }

                //////////////////////////////////////////////////////////////////////////////////////////


                //lead target
                Vector2f TLead = add(TLoc,            //target location+
                        Lvec,       //target speed*time to get to target+
                        null);      // if its too long just assume 3 seconds is long enough to course correct



                //aim at nose (can be changed to part targeting with a few tweaks)
                Vector2f TNoseLead = add(TLead, TNose, null);//aim at the nose of the target(optional)

                //main aiming (to find angle you are off target by)
                float AngleToEnemy = MathUtils.getAngle(MLoc, TNoseLead);
                float AtTarget = getAngleDifference(  //how far off target you are
                        MFace, AngleToEnemy);          //where missile pointing, where target is in relation

                //for backtargeting
                if (Backtarget && RangeToTarget<MaxRange)
                {
                    float AngFromTtoM = MathUtils.getAngle(TLoc, MLoc);       //get angle from target
                    float ToMissile = getAngleDifference(TFace, AngFromTtoM); //to missile

                    if (Math.abs(ToMissile)>180-BackAng)   //check if you are behind the target
                    {BTcutout=true;}
                    else if (isDrone)     //override for drones, re enables the AI
                    {BTcutout=false;}

                    if (!Straferound && !BTcutout)
                    {
                    AtTarget=AtTarget-(ToMissile>0? 90:-90);
                    }

                    if (Straferound && !BTcutout)
                    {
                        missile.giveCommand(ToMissile < 0
                                ? ShipCommand.STRAFE_LEFT : ShipCommand.STRAFE_RIGHT);
                    }
                }

                //makes the missile weave
                if (Weave)
                {AtTarget=AtTarget+(float)(Math.sin(weavecount/WeaveFreq)*Weavesize);}

                float AbsAngD = Math.abs(AtTarget-oftargetby);


                //////////////////////////////////////////////////////////////////////////////////////////////


                //point towards target
                if (AbsAngD > 0.5)
                {                                  //makes missile fly off target
                    missile.giveCommand(AtTarget > oftargetby
                            ? ShipCommand.TURN_LEFT : ShipCommand.TURN_RIGHT);
                }

                ////////////////////////////////////////////////////////////////////////////////////////////////////


                //correct missile velocity vector to be the same as missile facing
                if (AbsAngD < 5)
                {
                    //course correct for missile velocity vector
                    float MFlightAng = MathUtils.getAngle(new Vector2f(0, 0), MVel);
                    float MFlightCC = getAngleDifference(MFace, MFlightAng);
                    if (Math.abs(MFlightCC)>20)
                    {
                        missile.giveCommand(MFlightCC < 0
                                ? ShipCommand.STRAFE_LEFT : ShipCommand.STRAFE_RIGHT);
                    }

                }

                /////////////////////////////////////////////////////////////////////////////////////////////////


                //stop turning once you are on target (way of the hack, ignores missile limitations)
                if (AbsAngD < 0.4)
                {
                    missile.setAngularVelocity(0);
                }

                ///////////////////////////////////////////////////////////////////////////////////////////////

                if((!isDrone && !Straferound)                                 //not a drone or strafingBT
                        || (isDrone && RangeToTarget>MaxRange)                //is out or orbital range
                        || (Backtarget && RangeToTarget>MaxRange && !BTcutout)
                        || BTcutout)                                          //is BTcutout
                {
                    //acceleration code (stopNturn compatible)
                    if (StopNTurn)
                    {
                        if (AbsAngD < 40)
                        {
                            missile.giveCommand(ShipCommand.ACCELERATE);
                        }
                        if (MVel.lengthSquared()<TVel.lengthSquared())
                        {
                            missile.giveCommand(ShipCommand.ACCELERATE);
                        }
                        if (AbsAngD > 120)        //if you missed stop and turn around
                        {
                            missile.giveCommand(ShipCommand.DECELERATE);
                        }
                    }
                    else{missile.giveCommand(ShipCommand.ACCELERATE);}
                }
                /////////////////////////////////////////////////////////////////////////////////////////////////

                else  //move back and forth, if its a drone
                {
                    if (RangeToTarget<MinRange*MinOrbit){Close=false;} //boolean to achive back and forth motion
                    if (RangeToTarget>MinRange*MaxOrbit){Close=true;}
                    if (Close) {missile.giveCommand(ShipCommand.ACCELERATE);}
                    else {missile.giveCommand(ShipCommand.ACCELERATE_BACKWARDS);}

                    /////////////////////////////////////////////////////////////////////////////////////////////////

                    //orbit target
                    if (Orbit)
                    {
                        if (OTimer.intervalElapsed())
                        {
                            ODir=Math.random();
                        }
                        missile.giveCommand(ODir < .5
                                ? ShipCommand.STRAFE_LEFT : ShipCommand.STRAFE_RIGHT);
                    }
                }

                /////////////////////////////////////////////////////////////////////////////////////////////////


                //testcode    spawns particle at aim points
                CombatEngineAPI engine = Global.getCombatEngine(); //engine
                //engine.addSmoothParticle(TLead, new Vector2f(0,0), 5, 1, 1, new Color(255,10,15,255));
                //engine.addSmoothParticle(TNoseLead, new Vector2f(0,0), 5, 1, 1, new Color(93, 255, 40,255));
                //engine.addSmoothParticle(MLoc, multV2f(MVel, 4), 5, .5f, 1, new Color(248, 244, 255,255));
                //engine.addSmoothParticle(MLoc, new Vector2f(0,0), 5, .5f, 1, new Color(2, 255, 250,255));


                /////////////////////////////////////////////////////////////////////////////////////////////////

                if (canFire) //overriding controll for firing
                {
                    //reload
                    if (canReload && !Loaded && BTimer.intervalElapsed())
                    {
                        Loaded=true;
                        VCount=0;
                        {
                            //weaponpicker
                            Missil=(Math.random() > MislChance);         //is it firing a missile
                            Projectile = (Missil? Missiles:Guns)         //picks the weapon
                                    [(int) ((Missil? Missiles:Guns).length * Math.random())];
                            PVolleys=Missil ? MislVolley:ProjVolley;
                        }
                    }



                    /////////////////////////////////////////////////////////////////////////////////////////////////


                    if (Loaded && VTimer.intervalElapsed() && RangeToTarget<MinRange && VCount<PVolleys)
                    {
                        VCount++;
                        {
                            if (Projectile.equals("Linghning"))     //cause lightningbolts!!
                            {
                                engine.spawnEmpArc(missile.getSource(), MLoc, target, target,
                                        DamageType.ENERGY,
                                        50f,
                                        2000,
                                        10000f, // max range
                                        "tachyon_lance_emp_impact",
                                        20f, // thickness
                                        new Color(255,10,15,255),
                                        new Color(255,255,255,255)
                                );
                            }
                            else
                            {
                                //spawn projectiles
                                int counts=0;         //how many projectiles?
                                do {
                                    float angRAND= (float) ((PArk*(.5-Math.random())));          //angle of spread
                                    float speedRAND= (float) (((MaxRange-MinRange)/Lifetime)*Math.random());//variance of speed
                                    float PSpeed = (MinRange/Lifetime)+speedRAND;                           //speed of bullets launched
                                    float PAng = (IsFlechet ? missile.getFacing()                //direction of fire
                                            :missile.getFacing()+AtTarget)-angRAND;
                                    float x = (float) (PSpeed*Math.cos(Math.toRadians(PAng)));
                                    float y = (float) (PSpeed*Math.sin(Math.toRadians(PAng)));
                                    Vector2f PVec = new Vector2f(x,y);                     //convert all that into a vector
                                    Vector2f PMVec = add(MVel, PVec, null);                //true Pvec

                                    //for missile weapon offset
                                    float xa = (float) (11*Math.cos(Math.toRadians(missile.getFacing()-95)));
                                    float ya = (float) (11*Math.sin(Math.toRadians(missile.getFacing()-95)));
                                    Vector2f MoffVec = new Vector2f(xa,ya);

                                    //for projectile weapon ofset
                                    float xb = (float) (25*Math.cos(Math.toRadians(missile.getFacing()+45)));
                                    float yb = (float) (25*Math.sin(Math.toRadians(missile.getFacing()+45)));
                                    Vector2f PoffVec = new Vector2f(xb,yb);

                                    Vector2f Lpoint = add(MLoc, Missil ? MoffVec : PoffVec, null);

                                    engine.spawnProjectile(
                                            missile.getSource(),
                                            null,                            //who made you
                                            Projectile,                      //spawn the projectile
                                            Lpoint,                            //Vector2f firing origin point
                                            PAng,                            //float angle of fire
                                            PMVec);                          //Vector2f firing velocity

                                    /////////////////////////////////////////////////////////////////////////////////////

                                    counts++;


                                    ////////////////////////////////////////////////////////////////////////////////


                                    //optional explosion/particle glows
                                    //engine.addHitParticle(Lpoint, PMVec,
                                    //        5f,1f,1f,new Color(112, 152, 137,255)); //size, alpha, duration, color
                                    engine.spawnExplosion(Lpoint, PMVec,
                                            new Color(255, 86, 13,255), 15f, .1f);  //color,size,duration(max)

                                    //////////////////////////////////////////////////////////////////////////////////
                                }while (counts<PNum);
                            }
                        }
                        //////////////////////////////////////////////////////////////////////////////////

                        //main explosion flash
                        //engine.addHitParticle(MLoc, missile.getVelocity(),10,1f,1f,new Color(255, 12, 25,255));
                    }


                    //////////////////////////////////////////////////////////////////////////////////


                    if (VCount>=PVolleys)
                    {
                        Loaded=false;

                        if(DespawnEmpty)
                        {
                            if (SpawnEmpty) {emptyspawn(engine,missile);}
                            engine.removeEntity(missile);
                        }
                    }

                }

            }


            //////////////////////////////////////////////////////////////////////////////////////////////////////


            //stop if you have no target
            if (target==null && straighttimer<1)
            {missile.giveCommand(ShipCommand.DECELERATE);}

            ///////////////////////////////////////////////////////////////////////////////////////////////////////////


            //clear target if target is gone
            if (target == null // unset
                    ||  ( missile.getOwner() == target.getOwner() )        // friendly
                    ||  !Global.getCombatEngine().isEntityInPlay(target)   // completely removed
                    || ( target instanceof ShipAPI && ((ShipAPI)target).isHulk() ))//dead
            {

                target = findBestTarget(missile);
                return;
            }
        }
    }

    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////


    // Will be in next LazyLib version
    public static float getAngleDifference(float angle1, float angle2)
    {
        float distance = (angle2 - angle1) + 180f;
        distance = (distance / 360.0f);
        distance = ((distance - (float) Math.floor(distance)) * 360f) - 180f;
        return distance;
    }

    ///////////////////////////////////////////////////////////////////////////////////////////////////////////////


    //multiply vectors //own code
    public static Vector2f multV2f(Vector2f Vector1, float Multiplier)
    {
        float v1x = Vector1.getX()*Multiplier;
        float v1y = Vector1.getY()*Multiplier;
        Vector2f v1end = new Vector2f(v1x, v1y);
        return v1end;
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    //find intercept between a missile and a target
    //adapted from code by Jens Seiler
    //http://jaran.de/goodbits/2011/07/17/calculating-an-intercept-course-to-a-target-with-constant-direction-and-velocity-in-a-2-dimensional-plane/
    //have fun if you want to actually understand what going on
    //basicaly gets where T will be by the time M gets to it
    public static Vector2f LeadVector(Vector2f TLoc, Vector2f TVel, Vector2f MLoc, Vector2f MVel)
    {
        //get missiles speed
        float MSpeed = (float)Math.sqrt(MVel.lengthSquared());

        //separate out the vectors
        float Tx = TLoc.getX();
        float Ty = TLoc.getY();
        float Mx = MLoc.getX();
        float My = MLoc.getY();

        float TVx = TVel.getX();
        float TVy = TVel.getY();
        float MVx = MVel.getX();
        float MVy = MVel.getY();

        //subtract position vectors
        float x1 = Tx - Mx;
        float y1 = Ty - My;

        //quadratic fun
        float h1 = TVx*TVx + TVy*TVy - MSpeed*MSpeed;
        if (h1==0)
        {
            h1= (float) .0001;
        }

        float minusMHalf = -(x1*TVx + y1*TVy)/h1;  // h2/h1

        float discriminant = minusMHalf * minusMHalf - (x1*x1 + y1*y1)/h1; // (h2/h1)^2-h3/h1 (ie D)

        //can they intersect?
        if (discriminant < 0)
        {
            return TLoc;
        }


        double root = Math.sqrt(discriminant);

        double t1 = minusMHalf + root;
        double t2 = minusMHalf - root;

        double tMin = Math.min(t1, t2);
        double tMax = Math.max(t1, t2);
        //which of the 2 is smaller
        double time = tMin > 0 ? tMin : tMax;

        //can return -ve time (this is less then useful)
        if (time < 0)
        {
            return TLoc;
        }

        //calculate vector
        return new Vector2f((float)(time * TVx), (float)(time * TVy));
    }
}
[close]
[close]
old
« Last Edit: December 11, 2013, 02:28:45 PM by dmaiski »
Logged
BISO
(WIP) lots of shiny new weapons ( :-[ i have more weapons then sprites :-[ )

i got a cat pad
its like a mouse pad but better!

dmaiski

  • Captain
  • ****
  • Posts: 422
  • resistance is futile
    • View Profile
Re: The Radioactive Code Dump
« Reply #51 on: December 11, 2013, 02:20:13 PM »

MK3 kitchenSINKai
now with even more options!

new aditions:
ability to select targeting search script
idle behaviour (including orbit launching ship)
improved targeting logic and flow (no longer a black box)

Options index:
Spoiler
//MAIN missile stage AI components - dose what it says on the tin, basic stuff to make the missile go, spread out, fly in a line ect
//idle activitiy (what to do when you have no targets)
//targeting AI
//flarechasing - to make it chase flares
//weaving behaviour - missile will move in a sine wave pattern (overiding)
//scripts for HardTargeting/orbitrange - if its on, once missile gets in range, it will fly straight at the target (less accurate, but better for some uses)
//Heatseeker like behaviour - basicly heatseekers, but better (overiding)
//DRONE script (core behaviour script) - makes the missile orbit that target without getting in close
//weapons - we can arm our missiles with miniguns and rocket launchers
////weaponlist - a list of weapons the weaponpicker can pick from
////weaponpicker - picks a weapon to blast your enemy with
////MIRV/Flechet script (core firing script of a drone missile) - like the vanilla mirv, but better with more options, smarter... evil
[close]

unfortunate consequence: you will need to place this jar file into a folder in your mod named jar for this to work (cause its using lists)
the jar files that goes into Your_Mod\jar\

ill eventualy make this into a utility similar to lazy lib

the code itself is too large to post here so pasetbin
http://pastebin.com/YV5QXjJU

curent sample is set up like a basic heatseeker (backtargeting=true option)
« Last Edit: December 11, 2013, 06:58:32 PM by dmaiski »
Logged
BISO
(WIP) lots of shiny new weapons ( :-[ i have more weapons then sprites :-[ )

i got a cat pad
its like a mouse pad but better!

xenoargh

  • Admiral
  • *****
  • Posts: 5078
  • naively breaking things!
    • View Profile
Re: The Radioactive Code Dump
« Reply #52 on: December 12, 2013, 04:09:42 PM »

Ever wanted a simple, no-frills missile AI that:

1.  Obeyed a ship's current target (i.e., if you have something selected, it gets shot, period).
2.  Would not waste precious missiles on Drones?
3.  Would not merely aim at the closest target, but would evaluate whether there was anything in its aiming cone?
4.  Could do anime-style delayed engines?
5. Is very light on CPU use per missile per frame, and can be adjusted for load-balance vs. accuracy?

Well, this might just work for you :)  Based on Dmaiski's LRPD source, with a bunch of changes by me :)

Source:
Spoiler
Code: java
package data.scripts.weapons;

import com.fs.starfarer.api.Global;
import com.fs.starfarer.api.combat.MissileAIPlugin;
import com.fs.starfarer.api.combat.MissileAPI;
import com.fs.starfarer.api.combat.ShipAPI;
import com.fs.starfarer.api.combat.ShipCommand;
import com.fs.starfarer.api.util.IntervalUtil;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.lazywizard.lazylib.MathUtils;
import org.lazywizard.lazylib.combat.AIUtils;

import org.lwjgl.util.vector.Vector2f;

//GuidedMissileAI_NoDrones script
//A low-latency guided missile AI with an example of sorting out non-drone targets
//Based on Dmaiski's work on LRPD AI
public class GuidedMissileAI_NoDrones implements MissileAIPlugin
{
    // Our missile object
    private final MissileAPI missile;
    private float waitTime = 0f;

    // Our current target (can be null)
    private ShipAPI target = null;
    
    //Booleans set current control state, updated every tick
    private boolean isAccelerating = false;
    private boolean turnRight = false;
    private boolean turnLeft = false;
    private boolean strafeRight = false;
    private boolean strafeLeft = false;
    
    private final IntervalUtil checkInterval = new IntervalUtil(0.05f, 0.1f);
                
    //Initial setup.  If waitTime = 0f, the guidance begins one frame after launch.  
    //Otherwise, drifts until waitTime = 0f (useful for anime-style launch systems)
    public GuidedMissileAI_NoDrones (MissileAPI missile, float waitTime)
    {
        this.missile = missile;
        this.waitTime = waitTime;
        
        //This line can be enabled to give missile targetting data even while drifting.
        //target = findBestTarget(missile);
    }
    
    //This is our sorter, that finds the closest ShipAPI that is not a Drone.
    public static ShipAPI findBestTarget(MissileAPI missile)
    {
        ShipAPI source = missile.getSource();
        if(source != null){
            ShipAPI manTarget = source.getShipTarget();
            if(manTarget != null && !manTarget.isHulk() &&  !(missile.getOwner() == manTarget.getOwner())        // friendly
                && Global.getCombatEngine().isEntityInPlay(manTarget))
            {
                return manTarget;
            }
        }
        //this is where your missile is
        Vector2f loc = missile.getLocation();

        //getting what you want to search through
        ArrayList<ShipAPI> possibleTarget = new ArrayList<ShipAPI>();
        //lazylib is convenient, gets everything you want in Searchrange    
        List nearbyEnemies = AIUtils.getNearbyEnemies(missile, 5000f, true);

        //filters through the ship list and puts it in size order (maintaining distance to point)      
        for(Iterator iterMe = nearbyEnemies.iterator(); iterMe.hasNext(); )
        {
            ShipAPI enemy = (ShipAPI) iterMe.next();
            //criteria to add to list (you can put anything that will return a true/false here)
            if (!enemy.isDrone())
            {
                possibleTarget.add(enemy);  //add to posibles
            }
        }

        //If we don't have a manual target, then the missile looks for the closest one in the forward arc.
        float searchArc = 45f;
        for(Iterator iterMe = possibleTarget.iterator(); iterMe.hasNext(); ){
            ShipAPI target = (ShipAPI) iterMe.next();
            Vector2f MLoc = missile.getLocation();
            Vector2f TLoc = target.getLocation();
            float MFace = missile.getFacing();        
            
            //main aiming (to find angle you are off target by)
            float AngleToEnemy = MathUtils.getAngle(MLoc, TLoc);
            //Gets the difference in the angle to the enemy vs. the missile's current angle
            float angleToTarget = getAngleDifference(MFace, AngleToEnemy);
            float AbsAngD = Math.abs(angleToTarget);  
            if(AbsAngD < searchArc){
                return target;
            }
        }
        
        //If we haven't found a target within the search arc, this eturns the closest enemy to this missile, or null
        if(!possibleTarget.isEmpty()){
            return possibleTarget.get(0);
        }else{
            return null;
        }
    }

    //Main logic
    @Override
    public void advance(float amount)
    {
        //LOGIC CHECKS
        // If fading or fizzling, don't bother with updates.
        if (missile.isFading() || missile.isFizzling())
        {
            return;
        }
        
        //Do nothing while waitTime > 0
        if(waitTime > 0f){
            waitTime --;
            return;
        }
        
        //If neither of the above are true, go ahead and increment our checkInterval
        checkInterval.advance(amount);
        
        //UPDATE TARGETS
        //If we don't have a target, find one.  
        //If our target is dead, removed from the sim or has switched sides, find a new target.
        if (target == null // unset
                || target.isHulk()
                ||  (missile.getOwner() == target.getOwner())        // friendly
                ||  !Global.getCombatEngine().isEntityInPlay(target) ) // completely removed
        {
            target = null;
            target = findBestTarget(missile);
            return;
        }      
        
        //CONTINUAL CONTROL STATES
        //Checks booleans and gives continual commands here.
        //Done here so that we can use IntervalUtil to save CPU later
        if(isAccelerating){
            missile.giveCommand(ShipCommand.ACCELERATE);
        }else{
            missile.giveCommand(ShipCommand.DECELERATE);
        }
        
        if(turnRight){
            missile.giveCommand(ShipCommand.TURN_RIGHT);
        }
        if (turnLeft){
            missile.giveCommand(ShipCommand.TURN_LEFT);
        }
        if(strafeRight){
            missile.giveCommand(ShipCommand.STRAFE_RIGHT);
        }
        if(strafeLeft){
            missile.giveCommand(ShipCommand.STRAFE_LEFT);
        }

        //MAIN AI LOGIC
        //This code governs the missile's behavior and outputs boolean states that...
        //...are used in the continual control state code.
        //By using an IntervalUtil here, we can do practical load-balancing at the cost of accuracy.
        if (target!=null && checkInterval.intervalElapsed()) //if you have a target
        {
            //Reset all of the continuous control states here.
            isAccelerating = false;
            turnRight = false;
            turnLeft = false;
            strafeRight = false;
            strafeLeft = false;
            //aming
            //1step calculations of various stats
            Vector2f MVel = missile.getVelocity();
            Vector2f MLoc = missile.getLocation();
            Vector2f TVel = target.getVelocity();
            Vector2f TLoc = target.getLocation();
            float MFace = missile.getFacing();

            //Testing InterceptPoint
            float TCol = target.getCollisionRadius();
            Vector2f TVelShort = VectorDirection(TVel, TCol);
            Vector2f TLead = new Vector2f(TLoc.getX() + TVelShort.getX(),TLoc.getY() + TVelShort.getY());

            //Main aiming logic
            float AngleToEnemy = MathUtils.getAngle(MLoc, TLead);
            float angleToTarget = getAngleDifference(  //how far off target you are
                    MFace, AngleToEnemy);         //where missile pointing, where target is in relation

            float AbsAngD = Math.abs(angleToTarget);

            //////////////////////////////////////////////////////////////////////////////////////////////

            if (AbsAngD < 5)
            {
                //course correct for missile velocity vector (some bugs when severly off target)
                float MFlightAng = MathUtils.getAngle(new Vector2f(0, 0), MVel);
                float MFlightCC = getAngleDifference(MFace, MFlightAng);
                if (Math.abs(MFlightCC)>20)
                {
                    if(MFlightCC < 0){
                        strafeLeft = true;
                    } else {
                        strafeRight = true;
                    }
                }
            }
            
            //point towards target
            if (AbsAngD >= 0.5)
            {
                if(angleToTarget > 0){
                    turnLeft = true;
                }else{
                    turnRight = true;
                }
            }
            
            //Damp angular velocity if we're getting close to the target angle
            if(AbsAngD < Math.abs(missile.getAngularVelocity())){
                if(angleToTarget > 0){
                    missile.setAngularVelocity(AbsAngD);
                }else if (angleToTarget < 0){
                    missile.setAngularVelocity(-AbsAngD);
                }                
            }

            //acceleration code
            if (AbsAngD < 20)
            {
                isAccelerating = true;
            }
        }
    }
    
    //Returns a new vector with original direction but new length
    public static Vector2f VectorDirection(Vector2f vec, float size)
    {
        float x = vec.getX();
        float y = vec.getY();

        float lenght =(float) Math.hypot(x, y);
        return new Vector2f(x*size/lenght, y*size/lenght);
    }          

    // Will be in next LazyLib version
    public static float getAngleDifference(float angle1, float angle2)
    {
        float distance = (angle2 - angle1) + 180f;
        distance = (distance / 360.0f);
        distance = ((distance - (float) Math.floor(distance)) * 360f) - 180f;
        return distance;
    }
}
[close]
« Last Edit: December 13, 2013, 08:48:07 PM by xenoargh »
Logged
Please check out my SS projects :)
Xeno's Mod Pack

dmaiski

  • Captain
  • ****
  • Posts: 422
  • resistance is futile
    • View Profile
Re: The Radioactive Code Dump
« Reply #53 on: December 12, 2013, 04:46:37 PM »

Ever wanted a simple, no-frills missile AI that:

Well, this might just work for you :)  Based on Dmaiski's LRPD source, with a bunch of changes by me :)

change the targeting AI to this :
(what you are using is old and (alot) less accurate, this is still verry cheap on the CPU(it just looks large))
Spoiler
Code: java
                //aming
                //1step calculations of various stats
                Vector2f MVel = missile.getVelocity();
                Vector2f MLoc = missile.getLocation();
                Vector2f TLoc = target.getLocation();
                Vector2f TVel = target.getVelocity();
                //float MSpeed = (float)Math.sqrt(MVel.lengthSquared());
                //float TSpeed = (float)Math.sqrt(TVel.lengthSquared());
                float MFace = missile.getFacing();
                float TFace = target.getFacing();
                double TfaceRad = Math.toRadians(TFace);
                float TCol = target.getCollisionRadius();
                float sx = (float) (TCol*.5*Math.cos(TfaceRad));
                float sy = (float) (TCol*.5*Math.sin(TfaceRad));
                Vector2f TNose = new Vector2f(sx, sy);
                float RangeToTarget = MathUtils.getDistance(TLoc, MLoc);
                //float TimeToTarget = RangeToTarget/MSpeed;                      //how long till you hit


                //testing InterceptPoint
                Vector2f Lvec = LeadVector(TLoc, TVel, MLoc, MVel);

                //////////////////////////////////////////////////////////////////////////////////////////
                /*
                //override when in range
                if (HardTargeting && RangeToTarget<MaxRange)
                {
                    Lvec= multV2f(TVel, Leadtime);
                    baseoftarget=0;
                    oftarget=0;
                    oftargetby=0;
                }
                */
                //////////////////////////////////////////////////////////////////////////////////////////


                //lead target
                Vector2f TLead = add(TLoc,            //target location+
                        Lvec,       //target speed*time to get to target+
                        null);      // if its too long just assume 3 seconds is long enough to course correct



                //aim at nose (can be changed to part targeting with a few tweaks)
                Vector2f TNoseLead = add(TLead, TNose, null);//aim at the nose of the target(optional)

                //main aiming (to find angle you are off target by)
                float AngleToEnemy = MathUtils.getAngle(MLoc, TNoseLead);
                float AtTarget = getAngleDifference(  //how far off target you are
                        MFace, AngleToEnemy);

[close]
this is also needed for above code:
Spoiler
Code: java
// Will be in next LazyLib version
    public static float getAngleDifference(float angle1, float angle2)
    {
        float distance = (angle2 - angle1) + 180f;
        distance = (distance / 360.0f);
        distance = ((distance - (float) Math.floor(distance)) * 360f) - 180f;
        return distance;
    }

    //find intercept between a missile and a target
    //adapted from code by Jens Seiler
    //http://jaran.de/goodbits/2011/07/17/calculating-an-intercept-course-to-a-target-with-constant-direction-and-velocity-in-a-2-dimensional-plane/
    //have fun if you want to actually understand what going on
    //basicaly gets where T will be by the time M gets to it
    public static Vector2f LeadVector(Vector2f TLoc, Vector2f TVel, Vector2f MLoc, Vector2f MVel)
    {
        //get missiles speed
        float MSpeed = (float)Math.sqrt(MVel.lengthSquared());

        //separate out the vectors
        float Tx = TLoc.getX();
        float Ty = TLoc.getY();
        float Mx = MLoc.getX();
        float My = MLoc.getY();

        float TVx = TVel.getX();
        float TVy = TVel.getY();
        float MVx = MVel.getX();
        float MVy = MVel.getY();

        //subtract position vectors
        float x1 = Tx - Mx;
        float y1 = Ty - My;

        //quadratic fun
        float h1 = TVx*TVx + TVy*TVy - MSpeed*MSpeed;
        if (h1==0)
        {
            h1= (float) .0001;
        }

        float minusMHalf = -(x1*TVx + y1*TVy)/h1;  // h2/h1

        float discriminant = minusMHalf * minusMHalf - (x1*x1 + y1*y1)/h1; // (h2/h1)^2-h3/h1 (ie D)

        //can they intersect?
        if (discriminant < 0)
        {
            return TLoc;
        }


        double root = Math.sqrt(discriminant);

        double t1 = minusMHalf + root;
        double t2 = minusMHalf - root;

        double tMin = Math.min(t1, t2);
        double tMax = Math.max(t1, t2);
        //which of the 2 is smaller
        double time = tMin > 0 ? tMin : tMax;

        //can return -ve time (this is less then useful)
        if (time < 0)
        {
            return TLoc;
        }

        //calculate vector
        return new Vector2f((float)(time * TVx), (float)(time * TVy));
    }
[close]

btw this is the curent version of my target aming script
(whenever i get to it)
the only improvment the next version will have is an ititerative lead calculation that factors in the missiles speed in the vector to target and maybe another bit to predict target motion based on changes velocity to make the calculation perfectly accurate(read imposible to dodge)
« Last Edit: December 12, 2013, 04:55:31 PM by dmaiski »
Logged
BISO
(WIP) lots of shiny new weapons ( :-[ i have more weapons then sprites :-[ )

i got a cat pad
its like a mouse pad but better!

xenoargh

  • Admiral
  • *****
  • Posts: 5078
  • naively breaking things!
    • View Profile
Re: The Radioactive Code Dump
« Reply #54 on: December 12, 2013, 08:15:20 PM »

You really should read it first, I got rid of a great deal of the load ;)

BTW, you don't need most of that stuff to make a "perfect" missile.  I didn't spend a lot of time reading the code you borrowed, but frankly, I didn't think it made much sense to do all of those steps when you already had most of the data you needed.

Anyhow, a perfect missile is straightforward.
Spoiler
Pseudocode:

Look ahead however far you want (closer steps, more accuracy).

Missile needs to turn +/- X amount / time gap to get lined up on the (target's position vector * velocity / time gap) by the next time gap. 

Change angular velocity to result. 

If it's one-frame, you have a missile that literally cannot miss anything with a collision radius bigger than it, because every frame, it's aimed to where the center of the target will be, unless its velocity is so great that it will pass through the object without detecting a collision (which you can fix easily enough).

In short, if you want perfect aim, you don't need all that engine controller code (in fact, that makes it a lot harder, because you have to factor in acceleration and all that).

But that's not generally what we want; we want to control the missile's performance envelope through the .proj settings and perfection isn't the goal, really; if all of the missiles never missed, it'd really take a lot of fun out of the game.
[close]
Logged
Please check out my SS projects :)
Xeno's Mod Pack

xenoargh

  • Admiral
  • *****
  • Posts: 5078
  • naively breaking things!
    • View Profile
Re: The Radioactive Code Dump
« Reply #55 on: December 12, 2013, 09:32:23 PM »

"Smart" PD Missile AI.

Targets missiles first, then Fighters and Frigates, then anything goes.  Also includes an example of the angular-velocity smoothing referred to above.

Spoiler
Code: java
package data.scripts.weapons;

import com.fs.starfarer.api.Global;
import com.fs.starfarer.api.combat.CombatEntityAPI;
import com.fs.starfarer.api.combat.MissileAIPlugin;
import com.fs.starfarer.api.combat.MissileAPI;
import com.fs.starfarer.api.combat.ShipAPI;
import com.fs.starfarer.api.combat.ShipCommand;
import com.fs.starfarer.api.util.IntervalUtil;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.lazywizard.lazylib.MathUtils;
import org.lazywizard.lazylib.combat.AIUtils;

import org.lwjgl.util.vector.Vector2f;

//GuidedMissileAI_NoDrones script
//A low-latency guided missile AI with an example of a "smart" PD system.
//Targets missiles, fighters, frigates and then other ships, in that order
//Based on Dmaiski's work on LRPD AI
public class GuidedMissileAI_SmartPD implements MissileAIPlugin
{
    // Our missile object
    private final MissileAPI missile;
    private float waitTime = 0f;

    // Our current target (can be null)
    private CombatEntityAPI target;
    
    //Booleans set current control state, updated every tick
    private boolean isAccelerating = false;
    private boolean turnRight = false;
    private boolean turnLeft = false;
    private boolean strafeRight = false;
    private boolean strafeLeft = false;
    
    private final IntervalUtil checkInterval = new IntervalUtil(0.05f, 0.1f);
                
    //Initial setup.  If waitTime = 0f, the guidance begins one frame after launch.  
    //Otherwise, drifts until waitTime = 0f (useful for anime-style launch systems)
    public GuidedMissileAI_SmartPD (MissileAPI missile, float waitTime)
    {
        this.missile = missile;
        this.waitTime = waitTime;
        
        //This line can be enabled to give missile targetting data even while drifting.
        //target = findBestTarget(missile);
    }
    
    //This is our sorter, that finds the closest ShipAPI that is not a Drone.
    public static CombatEntityAPI findBestTarget(MissileAPI missile)
    {
        //Allows manual targets to get top priority.  Disabled here, since it's meant to be PD
        /*
        ShipAPI source = missile.getSource();
        if(source != null){
            CombatEntityAPI manTarget = (CombatEntityAPI) source.getShipTarget();
            if(manTarget != null){
                return manTarget;
            }
        }
        */
        
        //this is where your missile is
        Vector2f loc = missile.getLocation();
        //This is the search arc
        float searchArc = 45f;
        
        List nearbyEnemyMissiles = AIUtils.getNearbyEnemyMissiles(missile, 2000f);
        //Looks for any nearby missiles that are within the missile's arc.
        for(Iterator iterMe = nearbyEnemyMissiles.iterator(); iterMe.hasNext(); ){
            MissileAPI target = (MissileAPI) iterMe.next();
            Vector2f MLoc = missile.getLocation();
            Vector2f TLoc = target.getLocation();
            float MFace = missile.getFacing();        
            
            //Finds angle to enemy missile
            float AngleToEnemy = MathUtils.getAngle(MLoc, TLoc);
            //Gets the difference in the angle to the enemy vs. the missile's current angle
            float angleToTarget = getAngleDifference(MFace, AngleToEnemy);
            float AbsAngD = Math.abs(angleToTarget);  
            if(AbsAngD < searchArc){
                return (CombatEntityAPI) target;  //return target, after casting to CombatEntityAPI
            }
        }  
        //No missiles were found in the arc.  Any nearby missiles at all?
        if(!nearbyEnemyMissiles.isEmpty()){
            return (CombatEntityAPI) nearbyEnemyMissiles.get(0);
        }

        //We didn't find any missiles.  So how's about ships?
        
        //An array that we can prune of members we don't want included later
        ArrayList<CombatEntityAPI> possibleTarget = new ArrayList<CombatEntityAPI>();
        
        //Let's get all of the nearby enemy ships    
        List nearbyEnemies = AIUtils.getNearbyEnemies(missile, 2000f, true);

        //filters through the ship list and eliminates anything that isn't a Fighter or a Frigate.    
        for(Iterator iterMe = nearbyEnemies.iterator(); iterMe.hasNext(); )
        {
            ShipAPI enemy = (ShipAPI) iterMe.next();
            //criteria to add to list (you can put anything that will return a true/false here)
            if (enemy.isFighter() || enemy.isFrigate() && !enemy.isDrone())
            {
                possibleTarget.add((CombatEntityAPI) enemy);  //add to possible targets, after casting to CombatEntityAPI
            }
        }

        //The missile looks for the closest target in the forward arc.
        for(Iterator iterMe = possibleTarget.iterator(); iterMe.hasNext(); ){
            ShipAPI target = (ShipAPI) iterMe.next();
            Vector2f MLoc = missile.getLocation();
            Vector2f TLoc = target.getLocation();
            float MFace = missile.getFacing();        
            
            //Finds angle to the enemy ship
            float AngleToEnemy = MathUtils.getAngle(MLoc, TLoc);
            //Gets the difference in the angle to the enemy vs. the missile's current angle
            float angleToTarget = getAngleDifference(MFace, AngleToEnemy);
            float AbsAngD = Math.abs(angleToTarget);  
            if(AbsAngD < searchArc){
                return (CombatEntityAPI) target;  //return target, after casting to CombatEntityAPI
            }
        }
        
        //If we haven't found a target within the search arc, this returns the closest enemy to this missile, or null
        if(!possibleTarget.isEmpty()){
            //Can't find anything in the arc?  Get the nearest Fighter / Frigate.
            return (CombatEntityAPI) possibleTarget.get(0);
        }else if(!nearbyEnemies.isEmpty()){
            //No nearby Fighters or Frigates?  Get nearest ship
            CombatEntityAPI theEnemy = (CombatEntityAPI) nearbyEnemies.get(0);
            return theEnemy;
        }else{
            //We've utterly failed to find any target at all
            return null;
        }
    }

    //Main logic
    @Override
    public void advance(float amount)
    {
        //LOGIC CHECKS
        // If fading or fizzling, don't bother with updates.
        if (missile.isFading() || missile.isFizzling())
        {
            return;
        }
        
        //Do nothing while waitTime > 0
        if(waitTime > 0f){
            waitTime --;
            return;
        }
        
        //If neither of the above are true, go ahead and increment our checkInterval
        checkInterval.advance(amount);
        
        //UPDATE TARGETS
        //If we don't have a target, find one.  
        //If our target is dead, removed from the sim or has switched sides, find a new target.
        if (target == null // unset
                ||  ( missile.getOwner() == target.getOwner())        // friendly
                ||  !Global.getCombatEngine().isEntityInPlay(target) //Not in play
                )
        {
            target = findBestTarget(missile);
            return;
        }
        
        //Dead ShipAPIs
        if(target instanceof ShipAPI){
            ShipAPI thisTarget = (ShipAPI) target;
            if(thisTarget.isHulk()){
                target = findBestTarget(missile);
                return;
            }
        }
        
        //CONTINUAL CONTROL STATES
        //Checks booleans and gives continual commands here.
        //Done here so that we can use IntervalUtil to save CPU later
        if(isAccelerating){
            missile.giveCommand(ShipCommand.ACCELERATE);
        }else{
            missile.giveCommand(ShipCommand.DECELERATE);
        }
        
        if(turnRight){
            missile.giveCommand(ShipCommand.TURN_RIGHT);
        }
        if (turnLeft){
            missile.giveCommand(ShipCommand.TURN_LEFT);
        }
        if(strafeRight){
            missile.giveCommand(ShipCommand.STRAFE_RIGHT);
        }
        if(strafeLeft){
            missile.giveCommand(ShipCommand.STRAFE_LEFT);
        }

        //MAIN AI LOGIC
        //This code governs the missile's behavior and outputs boolean states that...
        //...are used in the continual control state code.
        //By using an IntervalUtil here, we can do practical load-balancing at the cost of accuracy.
        if (target!=null && checkInterval.intervalElapsed()) //if you have a target
        {
            //Reset all of the continuous control states here.
            isAccelerating = false;
            turnRight = false;
            turnLeft = false;
            strafeRight = false;
            strafeLeft = false;
            //aming
            //1step calculations of various stats
            Vector2f MVel = missile.getVelocity();
            Vector2f MLoc = missile.getLocation();
            Vector2f TVel = target.getVelocity();
            Vector2f TLoc = target.getLocation();
            float MFace = missile.getFacing();

            //testing InterceptPoint
            Vector2f TVelShort = new Vector2f(TVel.getX() * 0.5f, TVel.getY() * 0.5f);
            Vector2f TLead = new Vector2f(TLoc.getX() + TVelShort.getX(),TLoc.getY() + TVelShort.getY());

            //main aiming (to find angle you are off target by)
            float AngleToEnemy = MathUtils.getAngle(MLoc, TLead);
            float angleToTarget = getAngleDifference(  //how far off target you are
                    MFace, AngleToEnemy);         //where missile pointing, where target is in relation

            float AbsAngD = Math.abs(angleToTarget);

            //////////////////////////////////////////////////////////////////////////////////////////////

            if (AbsAngD < 5)
            {
                //course correct for missile velocity vector (some bugs when severly off target)
                float MFlightAng = MathUtils.getAngle(new Vector2f(0, 0), MVel);
                float MFlightCC = getAngleDifference(MFace, MFlightAng);
                if (Math.abs(MFlightCC)>20)
                {
                    if(MFlightCC < 0){
                        strafeLeft = true;
                    } else {
                        strafeRight = true;
                    }
                }
            }
            
            //point towards target
            if (AbsAngD >= 0.5)
            {
                if(angleToTarget > 0){
                    turnLeft = true;
                }else{
                    turnRight = true;
                }
            }
            
            //Damp angular velocity if we're getting close to the target angle
            if(AbsAngD < Math.abs(missile.getAngularVelocity())){
                if(angleToTarget > 0){
                    missile.setAngularVelocity(AbsAngD);
                }else if (angleToTarget < 0){
                    missile.setAngularVelocity(-AbsAngD);
                }                
            }

            //acceleration code
            if (AbsAngD < 40 || MVel.lengthSquared()<TVel.lengthSquared())
            {
                isAccelerating = true;
            }
        }
    }

    // Will be in next LazyLib version
    public static float getAngleDifference(float angle1, float angle2)
    {
        float distance = (angle2 - angle1) + 180f;
        distance = (distance / 360.0f);
        distance = ((distance - (float) Math.floor(distance)) * 360f) - 180f;
        return distance;
    }
}
[close]
« Last Edit: December 13, 2013, 08:48:43 PM by xenoargh »
Logged
Please check out my SS projects :)
Xeno's Mod Pack

dmaiski

  • Captain
  • ****
  • Posts: 422
  • resistance is futile
    • View Profile
Re: The Radioactive Code Dump
« Reply #56 on: December 12, 2013, 10:00:30 PM »

humm so in that case you are just predicting targets location by one step, there are a few problems with that:

a. 1 step is 1/30th? of a second or whatever fram rate game is runing at
b. adding the 1 step to the position vector has no significant effect on your missiles ability to aim(its no difrent then just using location)
c. if 1 step of velocity is larger then targets colision radii, your missile will overaim and totaly miss the target

quick fix, isolate velocity direction and use a self adjusting lead:
this just produces a vector in the direction of ships velocity with your desired lead distance
in this case ships colision radius(so it dose not over aim)
Spoiler
Code: java
    float TCol =target.getCollisionRadius();
    //float VariableLeadDistance =    //some math to produce how far ahead the missile should extrapolate the targets location

    Vector2f TVelShort = VectorDirection(TVel, TCol)        

//TCol+VariableLeadDistance) //optional if you want to use VariableLeadDistance

//...yor pathing code

    //returns a new vector with originals direction but new length
    public static Vector2f VectorDirection(Vector2f vec, float size)
    {
        float x = vec.getX();
        float y = vec.getY();

        float lenght =(float) Math.hypot(x, y);

        return new Vector2f(x*size/lenght, y*size/lenght);
    }
[close]

also general notes:
I dont think janino can handle List.add() (this needs to be externaly compiled)
I really like your bool based missile command system and a few other bits that i never thought of adding in before
« Last Edit: December 12, 2013, 10:10:27 PM by dmaiski »
Logged
BISO
(WIP) lots of shiny new weapons ( :-[ i have more weapons then sprites :-[ )

i got a cat pad
its like a mouse pad but better!

xenoargh

  • Admiral
  • *****
  • Posts: 5078
  • naively breaking things!
    • View Profile
Re: The Radioactive Code Dump
« Reply #57 on: December 13, 2013, 10:57:02 AM »

I like your idea about the offset.  You're right, that would still be cheap but improve accuracy :)

Framerate is 1/60, I think.  I was under the impression that everything runs every frame.  But note that the Interval introduces an unknown.

Yeah, I totally forgot to bother with Janino-friendly conventions there.  Oops.
Spoiler
I agree that I accepted a bit of overshoot there, on the premise that it'd be OK for everything that isn't really small and moving really fast.  After all, if a ship of 30f radius is moving faster than its diameter every update, then it's moving at 3600f. 

So the missiles can sometimes miss targets that are approaching them but are moving laterally.  On tail intercepts, they're fine, and on things that aren't trying to dodge them (i.e., other missiles) they're usually fine, although that's a special case due to relative speeds approaching the limits (a 12-radius missile vs. a 12-radius missile, both moving at 1000f, is getting close to an always-miss, even if the target doesn't ever change vectors).

My main point, though, is that there's little point in trying to compute the lead in a really fancy way, except in the limited case of aiming at Pilums from 2000f out or something like that, where it might be handy to compute the lead a with a time-hack once, reach that point, then break to standard behaviors.  It's too expensive otherwise, at least in Vacuum, where huge fleets duel it out in framerate-crashing glory on a regular basis.
[close]
Logged
Please check out my SS projects :)
Xeno's Mod Pack

dmaiski

  • Captain
  • ****
  • Posts: 422
  • resistance is futile
    • View Profile
Re: The Radioactive Code Dump
« Reply #58 on: December 13, 2013, 01:49:54 PM »

:P yea, but if you were using vanilla missile AI before, even using my kitchenSINK would improve framerate (if you have minimal/moderate behaviour settings enabled)

Spoiler
on that note: why is the vanilla missile AI so expencive
[close]
« Last Edit: December 13, 2013, 01:58:38 PM by dmaiski »
Logged
BISO
(WIP) lots of shiny new weapons ( :-[ i have more weapons then sprites :-[ )

i got a cat pad
its like a mouse pad but better!

xenoargh

  • Admiral
  • *****
  • Posts: 5078
  • naively breaking things!
    • View Profile
Re: The Radioactive Code Dump
« Reply #59 on: December 13, 2013, 08:46:45 PM »

My guess is that the vanilla AI is probably trying to do a few fancy things that are more expensive than fruitful, and it's been made very general-purpose, largely to make modders happy up to this point; for example, PD is probably a case, etc.  

Past that, IDK, it's hard to say without looking at it :)

Anyhow, found a bug in my main script, where it wasn't filtering the manually-targeted ships properly, leading to "dead" missiles sitting around and eating CPU doing nothing useful.  Fixed and updated that script, which also contains your target-leading improvement, which I think works pretty well for most missiles, and other little improvements.  Seems pretty solid and easy to change, and the PD version is pretty effective.
« Last Edit: December 13, 2013, 09:00:40 PM by xenoargh »
Logged
Please check out my SS projects :)
Xeno's Mod Pack
Pages: 1 2 3 [4] 5 6 ... 10