Fractal Softworks Forum

Please login or register.

Login with username, password and session length
Advanced search  

News:

Starsector 0.9.1a is out! (05/10/19); Blog post: Painting the Stars (02/07/20)

Pages: 1 ... 3 4 [5] 6 7 ... 9

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

dmaiski

  • Captain
  • ****
  • Posts: 422
  • resistance is futile
    • View Profile
Re: The Radioactive Code Dump
« Reply #60 on: December 14, 2013, 01:50:47 AM »

thought i should dump this here for now
this is my curent tool kit for working with vectors:

Spoiler
Code: java
    /**
     *
     * @param vec original vector
     * @param size size of new vector in the direction of motion
     * @return new vector with all variables applied to it
     */
    public static Vector2f V2fReSize(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);
    }

    /**
     *
     * @param vec original vector
     * @param sizeY size of new vector in perpendicular plane of motion
     * @param RiLeRVar determines direction of Y 0 left, 1 right, .5 balanced
     * @return vector with all variables applied to it
     */
    public static Vector2f V2fPerpendicularDisc(Vector2f vec, float sizeY, float RiLeRVar)
    {
        float randomdir = Math.random()>RiLeRVar ? 1:-1;
        float x = vec.getX();
        float y = vec.getY();

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

        float xa = x*sizeY*randomdir/lenght;
        float ya = y*sizeY*randomdir/lenght;

        return new Vector2f(ya, -xa);
    }
   
    /**
     *
     * @param vec original vector
     * @param sizeX size of new vector in the direction of motion
     * @param sizeY size of new vector in perpendicular plane of motion
     * @param RiLeRVar determines direction of Y 0 left, 1 right, .5 balanced
     * @return new vector with all variables applied to it
     */
    public static Vector2f V2fAdjust(Vector2f vec, float sizeX, float sizeY, float RiLeRVar)
    {
        float randomdir = Math.random()>RiLeRVar ? 1:-1;

        float x = vec.getX();
        float y = vec.getY();

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

        float xx = x * sizeX / lenght;
        float yx = y * sizeX / lenght;

        float xy = x*sizeY*randomdir/lenght;
        float yy = y*sizeY*randomdir/lenght;

        return new Vector2f(xx+yy, yx-xy);
    }

    /**
     *
     * @param vec original vector
     * @param mult multiplier to apply
     * @return new vector with all variables applied to it
     */
    public static Vector2f V2fmult(Vector2f vec, float mult)
    {
        float v1x = vec.getX()*mult;
        float v1y = vec.getY()*mult;
        Vector2f v1end = new Vector2f(v1x, v1y);
        return v1end;
    }
[close]
« Last Edit: December 14, 2013, 02:16:34 AM 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: 4854
  • naively breaking things!
    • View Profile
Re: The Radioactive Code Dump
« Reply #61 on: January 07, 2014, 07:23:17 PM »

Want an auto-fire AI that:

1.  Doesn't care about Friendly Fire?
2.  Will ignore Drones?
3.  Doesn't worry about Cloaking?
4.  Is otherwise a complete auto-fire AI?
5.  Is really fast?

Here you go :)

Spoiler
Code: java
package data.scripts.weapons;
import com.fs.starfarer.api.Global;
import com.fs.starfarer.api.combat.AutofireAIPlugin;
import com.fs.starfarer.api.combat.CombatEngineAPI;
import com.fs.starfarer.api.combat.CombatEntityAPI;
import com.fs.starfarer.api.combat.ShipAPI;
import com.fs.starfarer.api.combat.WeaponAPI;
import com.fs.starfarer.api.util.IntervalUtil;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.lazywizard.lazylib.CollectionUtils;
import org.lazywizard.lazylib.MathUtils;
import org.lazywizard.lazylib.VectorUtils;
import org.lazywizard.lazylib.combat.AIUtils;
import org.lazywizard.lazylib.combat.WeaponUtils;
import org.lwjgl.util.vector.Vector2f;

public class StandardAutofireAI implements AutofireAIPlugin {
    
    private final CombatEngineAPI engine = Global.getCombatEngine();
    private final WeaponAPI theWeapon;
    private Vector2f theTargetLoc;
    private ShipAPI manTarget;
    private float energyCost = 0.0f;
    private boolean isPD = false;
    private boolean isBeam = false;
    private boolean shouldFire = false;
    private boolean holdFire = true;
    private CombatEntityAPI theTarget = null;
    private final IntervalUtil checkInterval = new IntervalUtil(0.20f, 0.30f);
    
    //Initial setup.  We just send over the WeaponAPI in this case; we could send over a lot of other parameters here...
    //...if we wanted to build a more general-purpose weapon AI.  This is just a simple example of how to build the state machines and aiming logic.
    public StandardAutofireAI (WeaponAPI weapon)
    {
        this.theWeapon = weapon;
    }  
    
    public static List<ShipAPI> getEnemiesInArcLong(WeaponAPI weapon)
    {
        List<ShipAPI> enemies = new ArrayList<ShipAPI>();
        float range = 10000f;

        for (ShipAPI ship : AIUtils.getEnemiesOnMap(weapon.getShip()))
        {
            if (MathUtils.getDistance(ship, weapon.getLocation()) <= range
                    && weapon.distanceFromArc(ship.getLocation()) == 0f)
            {
                enemies.add(ship);
            }
        }

        return enemies;
    }    
    
    //This is our sorter, that finds the closest ShipAPI that is in the arc that is not a Drone.
    public ShipAPI findBestTarget(WeaponAPI weapon)
    {
        holdFire = false;
        ShipAPI source = weapon.getShip();
        if(source != null){
            manTarget = source.getShipTarget();
            if( manTarget != null
                &&  !manTarget.isHulk()
                &&  !(source.getOwner() == manTarget.getOwner())    // not friendly
                &&  Global.getCombatEngine().isEntityInPlay(manTarget) //Still exists
                &&  MathUtils.getDistance(manTarget,theWeapon.getLocation()) < theWeapon.getRange()//in range
                &&  WeaponUtils.isWithinArc(manTarget,theWeapon) // can be engaged
                )
            {
                holdFire = false;
                return manTarget;
            }
        }    

        //Let's get all of the ShipAPIs in the weapon's arc, sorted by distance
        ArrayList<ShipAPI> possibleTarget = new ArrayList<ShipAPI>();
        //lazylib is convenient, gets everything you want in Searchrange    
        List nearbyEnemies = WeaponUtils.getEnemiesInArc(weapon);
        Collections.sort(nearbyEnemies,new CollectionUtils.SortEntitiesByDistance(weapon.getLocation()));

        //filters through the ship list and removes Drones    
        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
                holdFire = false;
            }
        }
        
        if(possibleTarget.isEmpty())//No valid target found, so hunt for nearest enemy in arc and aim at it
        {
            nearbyEnemies = getEnemiesInArcLong(weapon);
            Collections.sort(nearbyEnemies,new CollectionUtils.SortEntitiesByDistance(weapon.getLocation()));
            possibleTarget.addAll(nearbyEnemies);
            holdFire = true;
        }    
        
        //Returns the closest enemy ship.
        if(!possibleTarget.isEmpty()){
            return possibleTarget.get(0);
        }else{
            return null;
        }
    }    
    
    private Vector2f getFiringSolution(Vector2f source, Vector2f destLocation, Vector2f destVelocity, float projVel)
    {
        float tx = destLocation.getX() - source.getX();
        float ty = destLocation.getY() - source.getY();
        float tvx = destVelocity.getX();
        float tvy = destVelocity.getY();

        // Get quadratic equation components
        float a = tvx*tvx + tvy*tvy - projVel*projVel;
        float b = 2 * (tvx * tx + tvy * ty);
        float c = tx*tx + ty*ty;    

        // Solve quadratic
        Vector2f ts = quad(a, b, c); // See quad(), below

        // Find smallest positive solution
        Vector2f finalSolution = new Vector2f();
        if(ts != null)
        {
            float t0 = ts.getX();
            float t1 = ts.getY();
            float t = Math.min(t0, t1);
            if (t < 0) t = Math.max(t0, t1);    
            if (t > 0)
            {
                finalSolution.setX(destLocation.getX() + destVelocity.getX()*t);
                finalSolution.setY(destLocation.getY() + destVelocity.getY()*t);
            }
        }
        return finalSolution;
    }
    
    private Vector2f quad(float a, float b,float c) {
      Vector2f sol = new Vector2f();
      if (Math.abs(a) < 1e-6) {
            if (Math.abs(b) < 1e-6) {
                if(Math.abs(c) < 1e-6){
                    sol.set(0f,0f);
                } else {
                    sol = null;
                }
            } else {
                sol.setX(-c/b);
                sol.setY(-c/b);
            }
        } else {
            float disc = b*b - 4*a*c;
            if (disc >= 0f) {
                disc = (float) Math.sqrt(disc);
                a = 2*a;
                sol.set((-b-disc)/a, (-b+disc)/a);
            }
      }
      return sol;
    }
    
    // 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;
    }    

    @Override
    public void advance(float amount) {
        
        if (engine.isPaused()) return;
        //If neither of the above are true, go ahead and increment our checkInterval
        checkInterval.advance(amount);
        if(checkInterval.intervalElapsed()){
            findBestTarget(theWeapon);//Every once in awhile, refresh the target (gets manual targets)
        }
        
        isPD = theWeapon.hasAIHint(WeaponAPI.AIHints.PD);
        isBeam = theWeapon.isBeam() || theWeapon.isBurstBeam();
        energyCost = theWeapon.getFluxCostToFire();
        if(energyCost < 1.0f){
            energyCost = 0.0f;
        }
        
        if((theWeapon.getShip().getFluxTracker().getCurrFlux() + energyCost) > theWeapon.getShip().getFluxTracker().getMaxFlux()){
            theTarget = null;
            return;
        }
        
        //UPDATE TARGETS
        //If we don't have a target, find one.  
        //If our target is dead, out of range or removed from the sim or has switched sides, find a new target.        
        ShipAPI targShip = null;
        if(theTarget instanceof ShipAPI) targShip = (ShipAPI) theTarget;
        if (    theTarget == null // unset
                ||  (targShip != null && targShip.isHulk())
                ||  (theWeapon.getShip().getOwner() == theTarget.getOwner()) //Friendly!
                ||  !Global.getCombatEngine().isEntityInPlay(theTarget) //Completely removed from play
                ||  MathUtils.getDistance(theTarget,theWeapon.getLocation()) > theWeapon.getRange() // Out of range
                ||  !WeaponUtils.isWithinArc(theTarget,theWeapon) // Has left the arc
                ||  holdFire  //We have a distant target, but we need to update and not fire
                ) //Is no longer in range
        {
            theTarget = null;
            shouldFire = false;
            theTarget = findBestTarget(theWeapon);
            if(theTarget != null) theTargetLoc = theTarget.getLocation();//Pre-aim at distant targets
            return;
        }
        if(theTarget != null && !holdFire){
            if(isBeam){
                theTargetLoc = theTarget.getLocation();
                //Are we aimed at the target?
                float AngleToEnemy = VectorUtils.getAngle(theWeapon.getLocation(), theTargetLoc);
                float angleToTarget = getAngleDifference(theWeapon.getCurrAngle(), AngleToEnemy);
                float AbsAngD = Math.abs(angleToTarget);
                shouldFire = AbsAngD < 1.5f;//a little loose for beams, to give them pre-fire
            } else{
                theTargetLoc = getFiringSolution(theWeapon.getLocation(),theTarget.getLocation(),theTarget.getVelocity(),theWeapon.getProjectileSpeed());
                //Are we aimed at the target?
                float AngleToEnemy = VectorUtils.getAngle(theWeapon.getLocation(), theTargetLoc);
                float angleToTarget = getAngleDifference(theWeapon.getCurrAngle(), AngleToEnemy);
                float AbsAngD = Math.abs(angleToTarget);
                shouldFire = AbsAngD < 1f;//tighter for non-beams, since they need to lead well
            }  
        } else {
            shouldFire = false;
        }
    }

    @Override
    public boolean shouldFire() {
        return shouldFire;
    }

    @Override
    public void forceOff() {
        //nothing
    }

    @Override
    public Vector2f getTarget() {
        if(theTarget != null){
            return theTargetLoc;
        }
        return null;
    }

    @Override
    public ShipAPI getTargetShip() {
        if(theTarget instanceof ShipAPI) return (ShipAPI) theTarget;            
        return null;
    }

    @Override
    public WeaponAPI getWeapon() {
        return theWeapon;
    }

}

[close]
Please let me know if you encounter any serious bugs with this; if you want a no-FF version, that's pretty straightforward to do (do a narrow arc-sort of friendlies between this weapon and theTarget) but since I don't actually need that, I'm probably going to let others figure that part out :)

Anyhow, thanks to Dmaiski, LazyWizard and Broofa's example of a 2D target-leading operation on Stack Overflow.
« Last Edit: January 08, 2014, 10:24:52 PM by xenoargh »
Logged
Please check out my SS projects :)
Xeno's Mod Pack

xenoargh

  • Admiral
  • *****
  • Posts: 4854
  • naively breaking things!
    • View Profile
Re: The Radioactive Code Dump
« Reply #62 on: January 08, 2014, 06:56:02 AM »

Minor fix for above; adds specific Cloak detection :)
Logged
Please check out my SS projects :)
Xeno's Mod Pack

xenoargh

  • Admiral
  • *****
  • Posts: 4854
  • naively breaking things!
    • View Profile
Re: The Radioactive Code Dump
« Reply #63 on: January 08, 2014, 10:21:19 PM »

Major improvement for the auto-fire AI above; this takes relative velocity and barrel offsets into consideration when providing the 2D vectors to the relevant parts of getFiringSolution() and it fixes a bug with Cloak detection (I'll take that out of the first version, since it's crashy).

WARNING; this version expects a float value for the barrel offsets, which:

A.  You can only read from the JSON; there doesn't appear to be any way to get this from the WeaponAPI atm (API request coming).
B.  If it's not there or is null, boomski.


Otherwise, it's been tested and appears to replicate most of Vanilla's auto-fire AI features at this point, with a bunch of caveats:

  • It still doesn't worry about FF
  • It doesn't care about ammo conservation.
  • It's deliberately flighty about target selection (i.e., it may break off shooting at a Cruiser that's far away to snipe a fighter that's really close, unless said Cruiser was manually targeted, in which case it's going to stay locked on).  The Vanilla AI uses a similar mechanism in its state machine, but this one's tuned a little differently; expect it to change targets more frequently.  You can change that tuning very easily by altering the Interval used to trigger a new target check.
  • It's Flux management is very rudimentary.  It just checks to see that it's going to use Flux and that the Flux of firing would be greater than max Flux.  This probably needs to be tuned so that AI ships don't Overload when using high-Flux alpha-strike weapons.
  • It doesn't respect flag changes or state flags (i.e., this won't do PD right now at all, and it certainly won't do "Strike" AI behaviors; right now it's just killing whatever it can hit).
  • I'm not 100% sure, but I'm guessing that Crew skill is not being taken into account here.  This is basically "perfect" aim (note that that does NOT mean it always hits; if you want weapons that always hit, see the guided missile code laying around here).  Not sure how that's implemented in Vanilla, either- I'll bug Alex about that. I think it's more complicated than a bit of fuzz on the acceptable degree of angle but IDK.

I'm not sure how much of that Vanilla functionality I'll reproduce (PD for sure, but that's very easy); Strike can probably be its own AI type, same for PD, ammo conservation is probably more trouble than it's really worth vs. just doing a target filter, etc.).

I'd like to give a major shout-out for Louis Wasserman on Stack Overflow for his clear explanation about how to do line segment rotations in Java; that's something LazyLib probably needs in it :)

Spoiler
Code: java
package data.scripts.weapons;
import java.awt.geom.Line2D;
import com.fs.starfarer.api.Global;
import com.fs.starfarer.api.combat.AutofireAIPlugin;
import com.fs.starfarer.api.combat.CombatEngineAPI;
import com.fs.starfarer.api.combat.CombatEntityAPI;
import com.fs.starfarer.api.combat.ShipAPI;
import com.fs.starfarer.api.combat.WeaponAPI;
import com.fs.starfarer.api.util.IntervalUtil;
import java.awt.geom.AffineTransform;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import org.lazywizard.lazylib.CollectionUtils;
import org.lazywizard.lazylib.MathUtils;
import org.lazywizard.lazylib.VectorUtils;
import org.lazywizard.lazylib.combat.AIUtils;
import org.lazywizard.lazylib.combat.WeaponUtils;
import org.lwjgl.util.vector.Vector2f;

public class StandardAutofireAI implements AutofireAIPlugin {
    
    private final CombatEngineAPI engine = Global.getCombatEngine();
    private final WeaponAPI theWeapon;
    private final float barrelLength;
    private Vector2f theTargetLoc;
    private ShipAPI manTarget;
    private float energyCost = 0.0f;
    private boolean isPD = false;
    private boolean isBeam = false;
    private boolean shouldFire = false;
    private boolean holdFire = true;
    private CombatEntityAPI theTarget = null;
    private final IntervalUtil checkInterval = new IntervalUtil(0.20f, 0.30f);
    
    //Initial setup.  If waitTime = 0f, the guidance begins one frame after launch.  
    //Otherwise, drifts until waitTime = 0f (useful for anime-style launch systems)
    public StandardAutofireAI (WeaponAPI weapon, float theBarrelLength)
    {
        this.theWeapon = weapon;
        this.barrelLength = theBarrelLength;
    }  
    
    public static List<ShipAPI> getEnemiesInArcLong(WeaponAPI weapon)
    {
        List<ShipAPI> enemies = new ArrayList<ShipAPI>();
        float range = 10000f;

        for (ShipAPI ship : AIUtils.getEnemiesOnMap(weapon.getShip()))
        {
            if (MathUtils.getDistance(ship, weapon.getLocation()) <= range
                    && weapon.distanceFromArc(ship.getLocation()) == 0f)
            {
                enemies.add(ship);
            }
        }

        return enemies;
    }    
    
    //This is our sorter, that finds the closest ShipAPI that is in the arc that is not a Drone.
    public ShipAPI findBestTarget(WeaponAPI weapon)
    {
        holdFire = false;
        ShipAPI source = weapon.getShip();
        if(source != null){
            manTarget = source.getShipTarget();
            if( manTarget != null
                &&  !manTarget.isHulk()
                &&  !(source.getOwner() == manTarget.getOwner())    // not friendly
                &&  Global.getCombatEngine().isEntityInPlay(manTarget) //Still exists
                &&  MathUtils.getDistance(manTarget,theWeapon.getLocation()) < theWeapon.getRange()//in range
                &&  WeaponUtils.isWithinArc(manTarget,theWeapon) // can be engaged
                )
            {
                holdFire = false;
                return manTarget;
            }
        }    

        //Let's get all of the ShipAPIs in the weapon's arc, sorted by distance
        ArrayList<ShipAPI> possibleTarget = new ArrayList<ShipAPI>();
        //lazylib is convenient, gets everything you want in Searchrange    
        List nearbyEnemies = WeaponUtils.getEnemiesInArc(weapon);
        Collections.sort(nearbyEnemies,new CollectionUtils.SortEntitiesByDistance(weapon.getLocation()));

        //filters through the ship list and removes Drones    
        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
                holdFire = false;
            }
        }
        
        if(possibleTarget.isEmpty())//No valid target found, so hunt for nearest enemy in arc and aim at it
        {
            nearbyEnemies = getEnemiesInArcLong(weapon);
            Collections.sort(nearbyEnemies,new CollectionUtils.SortEntitiesByDistance(weapon.getLocation()));
            possibleTarget.addAll(nearbyEnemies);
            holdFire = true;
        }    
        
        //Returns the closest enemy ship.
        if(!possibleTarget.isEmpty()){
            return possibleTarget.get(0);
        }else{
            return null;
        }
    }    
    
    private Vector2f getFiringSolution(Vector2f source, Vector2f destLocation, Vector2f destVelocity, float projVel)
    {
        float tx = destLocation.getX() - source.getX();
        float ty = destLocation.getY() - source.getY();
        float tvx = destVelocity.getX();
        float tvy = destVelocity.getY();

        // Get quadratic equation components
        float a = tvx*tvx + tvy*tvy - projVel*projVel;
        float b = 2 * (tvx * tx + tvy * ty);
        float c = tx*tx + ty*ty;    

        // Solve quadratic
        Vector2f ts = quad(a, b, c); // See quad(), below

        // Find smallest positive solution
        Vector2f finalSolution = new Vector2f();
        if(ts != null)
        {
            float t0 = ts.getX();
            float t1 = ts.getY();
            float t = Math.min(t0, t1);
            if (t < 0) t = Math.max(t0, t1);    
            if (t > 0)
            {
                finalSolution.setX(destLocation.getX() + destVelocity.getX()*t);
                finalSolution.setY(destLocation.getY() + destVelocity.getY()*t);
            }
        }
        return finalSolution;
    }
    
    private Vector2f quad(float a, float b,float c) {
      Vector2f sol = new Vector2f();
      if (Math.abs(a) < 1e-6) {
            if (Math.abs(b) < 1e-6) {
                if(Math.abs(c) < 1e-6){
                    sol.set(0f,0f);
                } else {
                    sol = null;
                }
            } else {
                sol.setX(-c/b);
                sol.setY(-c/b);
            }
        } else {
            float disc = b*b - 4*a*c;
            if (disc >= 0f) {
                disc = (float) Math.sqrt(disc);
                a = 2f*a;
                sol.set((-b-disc)/a, (-b+disc)/a);
            }
      }
      return sol;
    }
    
    // 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;
    }    

    @Override
    public void advance(float amount) {
        
        if (engine.isPaused()) return;
        //If neither of the above are true, go ahead and increment our checkInterval
        checkInterval.advance(amount);
        if(checkInterval.intervalElapsed()){
            findBestTarget(theWeapon);//Every once in awhile, refresh the target (gets manual targets)
        }
        
        isPD = theWeapon.hasAIHint(WeaponAPI.AIHints.PD);
        isBeam = theWeapon.isBeam() || theWeapon.isBurstBeam();
        energyCost = theWeapon.getFluxCostToFire();
        if(energyCost < 1.0f){
            energyCost = 0.0f;
        }
        
        if((theWeapon.getShip().getFluxTracker().getCurrFlux() + energyCost) > theWeapon.getShip().getFluxTracker().getMaxFlux()){
            theTarget = null;
            return;
        }
        
        //UPDATE TARGETS
        //If we don't have a target, find one.  
        //If our target is dead, out of range or removed from the sim or has switched sides, find a new target.        
        ShipAPI targShip = null;
        if(theTarget instanceof ShipAPI) targShip = (ShipAPI) theTarget;
        if (    theTarget == null // unset
                ||  (theWeapon.getShip().getOwner() == theTarget.getOwner()) //Friendly!
                ||  !Global.getCombatEngine().isEntityInPlay(theTarget) //Completely removed from play
                ||  MathUtils.getDistance(theTarget,theWeapon.getLocation()) > theWeapon.getRange() // Out of range
                ||  !WeaponUtils.isWithinArc(theTarget,theWeapon) // Has left the arc
                || barrelLength == -500f  //catches screwed-up barrelLength
                ||  holdFire  //We have a distant target, but we need to update and not fire
                ) //Is no longer in range
        {
            theTarget = null;
            shouldFire = false;
            theTarget = findBestTarget(theWeapon);
            if(theTarget != null) theTargetLoc = theTarget.getLocation();//Pre-aim at distant targets
            return;
        }
        if(targShip != null){
            if(targShip.getPhaseCloak() != null){
                if(targShip.getPhaseCloak().isOn() || targShip.getPhaseCloak().isActive()){
                    if(targShip.getPhaseCloak().getId().contains("phasecloak")){
                        theTarget = null;
                        shouldFire = false;
                        theTarget = findBestTarget(theWeapon);
                        if(theTarget != null) theTargetLoc = theTarget.getLocation();//Pre-aim at distant targets
                        return;
                    }
                }
            }
            if(targShip.isHulk()){
                theTarget = null;
                shouldFire = false;
                theTarget = findBestTarget(theWeapon);
                if(theTarget != null) theTargetLoc = theTarget.getLocation();//Pre-aim at distant targets
                return;                
            }
        }
        if(theTarget != null && !holdFire){
            if(isBeam){
                theTargetLoc = theTarget.getLocation();
                //Are we aimed at the target?
                float AngleToEnemy = VectorUtils.getAngle(theWeapon.getLocation(), theTargetLoc);
                float angleToTarget = getAngleDifference(theWeapon.getCurrAngle(), AngleToEnemy);
                float AbsAngD = Math.abs(angleToTarget);
                shouldFire = AbsAngD < 1.5f;//a little loose for beams, to give them pre-fire
            } else{
                //For projectile guns, getting the correct point to shoot at is considerably more complicated than for laser beams (duh!)
                //To do this, we need to take the barrel's length into account (at least right now) and we also need to factor the relative velocity of
                //the shooter and target.  The first part of this code rotates a line segment so that we have the right offset from the barrel
                //once we have that and combVelocity (our relative velocity) we can then do the quadratic equation needed to get a valid aiming point.
                //Many thanks to Louis Wasserman for this example of simple line segment rotation on Stack Overflow.
                double[] pt = {(double) barrelLength, (double) 0f};
                AffineTransform.getRotateInstance(Math.toRadians(theWeapon.getCurrAngle()), 0, 0).transform(pt, 0, pt, 0, 1); // specifying to use this double[] to hold coords
                float newX = (float) pt[0];
                float newY = (float) pt[1];
                Vector2f offset = new Vector2f(theWeapon.getLocation().getX() + newX, theWeapon.getLocation().getY() + newY);
                
                Vector2f combVelocity = new Vector2f(theTarget.getVelocity().getX() - theWeapon.getShip().getVelocity().getX(),theTarget.getVelocity().getY() -theWeapon.getShip().getVelocity().getY());
                
                theTargetLoc = getFiringSolution(offset,theTarget.getLocation(),combVelocity,theWeapon.getProjectileSpeed());
                //Are we aimed at the target?
                float AngleToEnemy = VectorUtils.getAngle(theWeapon.getLocation(), theTargetLoc);
                float angleToTarget = getAngleDifference(theWeapon.getCurrAngle(), AngleToEnemy);
                float AbsAngD = Math.abs(angleToTarget);
                shouldFire = AbsAngD < 1f;//tighter for non-beams, since they need to lead well
            }  
        } else {
            shouldFire = false;
        }
    }

    @Override
    public boolean shouldFire() {
        return shouldFire;
    }

    @Override
    public void forceOff() {
        //nothing
    }

    @Override
    public Vector2f getTarget() {
        if(theTarget != null){
            return theTargetLoc;
        }
        return null;
    }

    @Override
    public ShipAPI getTargetShip() {
        if(theTarget instanceof ShipAPI) return (ShipAPI) theTarget;            
        return null;
    }

    @Override
    public WeaponAPI getWeapon() {
        return theWeapon;
    }

}

[close]
« Last Edit: January 08, 2014, 11:49:13 PM by xenoargh »
Logged
Please check out my SS projects :)
Xeno's Mod Pack

xenoargh

  • Admiral
  • *****
  • Posts: 4854
  • naively breaking things!
    • View Profile
Re: The Radioactive Code Dump
« Reply #64 on: January 12, 2014, 12:14:58 PM »

Ever wanted Random Battle to be able to use any of the Factions and any of the fleets?

This doesn't quite do that, but it points the way; it shows how iteration through JSON can get the required ships and min / max numbers.  Getting it all neatly-generic so that it'll work with lots of Faction mods all running at the same time is something I'll leave for others; it doesn't appear to be possible to use getAllFactions() in a Mission context (because getSector() returns null) but it probably can be done by reading through the *.faction files and removing the ones that don't match <some condition>.

Spoiler
Code: java
package data.missions.randombattle1;

import com.fs.starfarer.api.Global;

import com.fs.starfarer.api.fleet.FleetGoal;
import com.fs.starfarer.api.fleet.FleetMemberType;
import com.fs.starfarer.api.mission.FleetSide;
import com.fs.starfarer.api.mission.MissionDefinitionAPI;
import com.fs.starfarer.api.mission.MissionDefinitionPlugin;
import java.io.IOException;
import java.util.Iterator;
import org.json.JSONException;
import org.json.JSONObject;
import org.lazywizard.lazylib.MathUtils;

public class MissionDefinition implements MissionDefinitionPlugin {
    //This string is used to follow the steps as we attempt to get data out of JSON
    private String nameString = "blah";

    private void generateFleet(FleetSide side, MissionDefinitionAPI api) {
        //If we only get this far, show this upon crashing
        nameString = "foobar";   
        String[] possibleFactionNames = new String[] {"glaug", "hegemony", "tritachyon", "independent", "punkjunkers", "pirates", "exigency"};   
        String factionName  = possibleFactionNames[(int) MathUtils.getRandomNumberInRange(0, possibleFactionNames.length - 1)];

        if(factionName.contains("glaug")){
           api.addToFleet(side, "glaug_battleship_Standard", FleetMemberType.SHIP, true);
       }else if(factionName.contains("hegemony")){
           api.addToFleet(side, "onslaught_Standard", FleetMemberType.SHIP, true);
       }else if(factionName.contains("tritachyon")){
           api.addToFleet(side, "odyssey_Standard", FleetMemberType.SHIP, true);
       }else if(factionName.contains("pirates")){
           api.addToFleet(side, "acanthus_Standard", FleetMemberType.SHIP, true);
       }else if(factionName.contains("independent")){
           api.addToFleet(side, "conquest_Standard", FleetMemberType.SHIP, true);
       }else if(factionName.contains("exigency")){
           api.addToFleet(side, "exigency_carrier_Standard", FleetMemberType.SHIP, true);
       }else if(factionName.contains("punkjunkers")){
           api.addToFleet(side, "pj_queen_Standard", FleetMemberType.SHIP, true);
       }else{
           api.addToFleet(side, "conquest_Standard", FleetMemberType.SHIP, true);
       }         

        try {
            JSONObject json = Global.getSettings().loadJSON("data/world/factions/" + factionName + ".faction");
            //Success, thus far
            nameString = "Parsed JSON";
            String test = json.getString("shipNamePrefix");
            //We've gotten this far...
            nameString = test;
            JSONObject jsonTwo = json.getJSONObject("fleetCompositions").getJSONObject("StationAssaultFleet").getJSONObject("ships");

            Iterator<String> keysIterator = jsonTwo.keys();
            int min = 0;
            int max = 0;
            while (keysIterator.hasNext())
            {
                //Are we there yet?
                nameString =  "" + min + max;
                test = (String)keysIterator.next();
                min = jsonTwo.getJSONArray(test).getInt(0);
                max = jsonTwo.getJSONArray(test).getInt(1);
                //We're there now :-)
                nameString = "" + min + max;
                if(test.contains("wing")){
                    for(int i = 0; i < MathUtils.getRandomNumberInRange(min,max)+1; i++){
                        api.addToFleet(side, test, FleetMemberType.FIGHTER_WING, false);
                    }
                }else {
                    for(int i = 0; i < MathUtils.getRandomNumberInRange(min,max)+1; i++){
                        api.addToFleet(side, test, FleetMemberType.SHIP, false);
                    }
                }                   
            }
            //We've succeeded; remove the nameString
            nameString = "";
        } catch (IOException ex) {
            //Error; couldn't find the file
            nameString = "no IO";

        } catch (JSONException ex) {
            //Error; bad JSON command
            nameString = "bad JSON";
        }           

    }


    @Override
    public void defineMission(MissionDefinitionAPI api) {
        // Set up the fleets so we can add ships and fighter wings to them.
        // In this scenario, the fleets are attacking each other, but
        // in other scenarios, a fleet may be defending or trying to escape
        api.initFleet(FleetSide.PLAYER, "ISS", FleetGoal.ATTACK, false, 5);
        api.initFleet(FleetSide.ENEMY, "ISS", FleetGoal.ATTACK, true, 5);

        // Set a small blurb for each fleet that shows up on the mission detail and
        // mission results screens to identify each side.
        api.setFleetTagline(FleetSide.PLAYER, "Your forces");
        api.setFleetTagline(FleetSide.ENEMY, "Enemy forces");

        // Set up the fleets
        generateFleet(FleetSide.PLAYER, api);
        generateFleet(FleetSide.ENEMY, api);

        // These show up as items in the bulleted list under
        // "Tactical Objectives" on the mission detail screen
        api.addBriefingItem("Defeat all enemy forces " + nameString);               

        // Set up the map.
        float width = 18000f;
        float height = 14000f;
        api.initMap((float)-width/2f, (float)width/2f, (float)-height/2f, (float)height/2f);

        float minX = -width/2;
        float minY = -height/2;

        for (int i = 0; i < 50; i++) {
                float x = (float) Math.random() * width - width/2;
                float y = (float) Math.random() * height - height/2;
                float radius = 100f + (float) Math.random() * 400f;
                api.addNebula(x, y, radius);
        }
    }
}

[close]

BTW, it'd be nice if GSON / Jackson were built in; parsing JSON is kind of un-fun if you can't turn them into arrays of arrays and parse them easily, like how tables work in Lua.
Logged
Please check out my SS projects :)
Xeno's Mod Pack

Debido

  • Admiral
  • *****
  • Posts: 1183
    • View Profile
Re: The Radioactive Code Dump
« Reply #65 on: February 04, 2014, 07:51:10 PM »

This thread is an awesome repository of even more awesome code. However I think that it needs to be better organised to be more accessible and more easily searchable.

What do you guys think of a different format to provide an easier to access code/mod repository?

There are free web servers out there that run PHP, so it's almost a matter of drag,n,drop what kind of Wiki/Code repository thing we want.

I personally think it would be neat for the mod community to contribute and share their awesome/fun mod code so that others can accelerate their faction development.
Logged

InfinitySquared

  • Commander
  • ***
  • Posts: 118
    • View Profile
    • Email
Re: The Radioactive Code Dump
« Reply #66 on: February 08, 2014, 10:17:06 PM »

Dumb question: how do I make a mod use these scripts again?

Edit: This happens when I try to implement the AOE explosion code:

Code: java
Thread-5] ERROR com.fs.starfarer.combat.O0OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO  - java.lang.RuntimeException: Error compiling [data.scripts.plugins.ExplosiveOnHitEffect]
java.lang.RuntimeException: Error compiling [data.scripts.plugins.ExplosiveOnHitEffect]
at com.fs.starfarer.loading.scripts.ScriptStore$3.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.ClassNotFoundException: Parsing compilation unit "com.fs.starfarer.loading.superThread-5] ERROR com.fs.starfarer.combat.O0OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO  - java.lang.RuntimeException: Error compiling [data.scripts.plugins.ExplosiveOnHitEffect]
java.lang.RuntimeException: Error compiling [data.scripts.plugins.ExplosiveOnHitEffect]
at com.fs.starfarer.loading.scripts.ScriptStore$3.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)
Caused by: java.lang.ClassNotFoundException: Parsing compilation unit "[email protected]"
at org.codehaus.janino.JavaSourceIClassLoader.findIClass(JavaSourceIClassLoader.java:180)
at org.codehaus.janino.IClassLoader.loadIClass(IClassLoader.java:158)
at org.codehaus.janino.JavaSourceClassLoader.generateBytecodes(JavaSourceClassLoader.java:199)
at org.codehaus.janino.JavaSourceClassLoader.findClass(JavaSourceClassLoader.java:164)
at java.lang.ClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
... 2 more
Caused by: org.codehaus.commons.compiler.CompileException: Source file "data/scripts/plugins/ExplosiveOnHitEffect.java" does not declare class "data.scripts.plugins.ExplosiveOnHitEffect"
at org.codehaus.janino.JavaSourceIClassLoader.findIClass(JavaSourceIClassLoader.java:165)
... 7 more
@112bba0"
at org.codehaus.janino.JavaSourceIClassLoader.findIClass(JavaSourceIClassLoader.java:180)
at org.codehaus.janino.IClassLoader.loadIClass(IClassLoader.java:158)
at org.codehaus.janino.JavaSourceClassLoader.generateBytecodes(JavaSourceClassLoader.java:199)
at org.codehaus.janino.JavaSourceClassLoader.findClass(JavaSourceClassLoader.java:164)
at java.lang.ClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
... 2 more
Caused by: org.codehaus.commons.compiler.CompileException: Source file "data/scripts/plugins/ExplosiveOnHitEffect.java" does not declare class "data.scripts.plugins.ExplosiveOnHitEffect"
at org.codehaus.janino.JavaSourceIClassLoader.findIClass(JavaSourceIClassLoader.java:165)
... 7 more

Same thing also happens with the smart PD AI and Lightning Storm scripts.
« Last Edit: February 09, 2014, 02:34:41 AM by InfinitySquared »
Logged

Debido

  • Admiral
  • *****
  • Posts: 1183
    • View Profile
Re: The Radioactive Code Dump
« Reply #67 on: February 09, 2014, 07:10:51 PM »

Source file "data/scripts/plugins/ExplosiveOnHitEffect.java" does not declare class "data.scripts.plugins.ExplosiveOnHitEffect"

That pretty much is the start of your problem

If you note the class itself is in fact called.

public class ExplosiveOnHitEffectWithAOE implements OnHitEffectPlugin {

So, either rename the java file, or refactor the code with the changed class name.
Logged

InfinitySquared

  • Commander
  • ***
  • Posts: 118
    • View Profile
    • Email
Re: The Radioactive Code Dump
« Reply #68 on: February 09, 2014, 07:46:58 PM »

Source file "data/scripts/plugins/ExplosiveOnHitEffect.java" does not declare class "data.scripts.plugins.ExplosiveOnHitEffect"

That pretty much is the start of your problem

If you note the class itself is in fact called.

public class ExplosiveOnHitEffectWithAOE implements OnHitEffectPlugin {

So, either rename the java file, or refactor the code with the changed class name.

Oh! So the filename and the class have to be the same?
Logged

Debido

  • Admiral
  • *****
  • Posts: 1183
    • View Profile
Re: The Radioactive Code Dump
« Reply #69 on: February 09, 2014, 09:08:24 PM »

Yes*
Logged

InfinitySquared

  • Commander
  • ***
  • Posts: 118
    • View Profile
    • Email
Re: The Radioactive Code Dump
« Reply #70 on: February 09, 2014, 09:52:37 PM »

Oh, thanks! It works now.  :D

On another note: this part of the CriticalHit script had been giving me problems.
Code: java
        // Scan all shots on the map for armor piercing projectiles  
        for (Iterator itproj = engine.getProjectiles().iterator(); itproj.hasNext()<img src="http://fractalsoftworks.com/forum/Smileys/default/wink.gif" alt="Wink" border="0">  
        {  

I changed it to this:
Code: Java
 
        // Scan all shots on the map for armor piercing projectiles  
        for (Iterator itproj = engine.getProjectiles().iterator(); itproj.hasNext(); ) //without the space
// <img src="http://fractalsoftworks.com/forum/Smileys/default/wink.gif" alt="Wink" border="0">  
        {  

And the wink turns up again in Line 128:
Code: java
                for (Iterator iter2 = toCheck.iterator(); iter2.hasNext()<img src="http://fractalsoftworks.com/forum/Smileys/default/wink.gif" alt="Wink" border="0">  

It turns out that the forum software parses ; ) -- without the space -- as a wink, and inserted it into the code.

Edit: It also gives me a null error.

Code: java
106266 [Thread-5] ERROR com.fs.starfarer.combat.O0OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO  - java.lang.NullPointerException
java.lang.NullPointerException
at data.scripts.plugins.CriticalHit.critSucessfull(CriticalHit.java:198)
at data.scripts.plugins.CriticalHit.advance(CriticalHit.java:178)
at com.fs.starfarer.title.ooOO.K$Oo.o00000(Unknown Source)
at com.fs.starfarer.combat.oOOO.new.super(Unknown Source)
at com.fs.starfarer.combat.CombatEngine.advance(Unknown Source)
at com.fs.starfarer.combat.G.??00(Unknown Source)
at com.fs.oOOO.A.?0000(Unknown Source)
at com.fs.starfarer.combat.O0OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO.o00000(Unknown Source)
at com.fs.starfarer.StarfarerLauncherjava.run(Unknown Source)
at java.lang.Thread.run(Unknown Source)

« Last Edit: February 09, 2014, 10:14:49 PM by InfinitySquared »
Logged

kazi

  • Admiral
  • *****
  • Posts: 711
    • View Profile
Re: The Radioactive Code Dump
« Reply #71 on: February 10, 2014, 02:39:44 AM »

Dumb question: how do I make a mod use these scripts again?

Edit: This happens when I try to implement the AOE explosion code:

Same thing also happens with the smart PD AI and Lightning Storm scripts.

If you're going for AOE weapons, just use the PROXIMITY_CHARGE weapon behaviour (you can have a MIRV missile fire proximity charges for AOE missiles).
Logged

InfinitySquared

  • Commander
  • ***
  • Posts: 118
    • View Profile
    • Email
Re: The Radioactive Code Dump
« Reply #72 on: February 10, 2014, 03:47:16 AM »

Dumb question: how do I make a mod use these scripts again?

Edit: This happens when I try to implement the AOE explosion code:

Same thing also happens with the smart PD AI and Lightning Storm scripts.

If you're going for AOE weapons, just use the PROXIMITY_CHARGE weapon behaviour (you can have a MIRV missile fire proximity charges for AOE missiles).

This does it on impact which then releases a damage burst to nearby objects. I've managed to implement it now though. It's just the CriticalHit script that's being iffy now.
Logged

etherealblade

  • Commander
  • ***
  • Posts: 134
    • View Profile
Re: The Radioactive Code Dump
« Reply #73 on: March 07, 2014, 09:36:54 AM »

I'd figure I'd ask here instead of making a whole new thread since this is all abouts code. So what would I do to make my beam weapon be able to hit phased ships?

Also, I attempted pulling the kitchen sink ai code out and am trying to see if i can get this to work again...Heres the deal. I know to put the code in it's own file in java file in a missileAI folder in scripts. My ship mod that I have currently has no  mod plugin file. Would All I need to do is post this in it? (I'm sure the answer is no because code is never that simple)
Code
public static final String LRPDai_MISSILE_ID = "SM_lrmPD_m";  //name of the missile that will use that AI  
    public static final String BOMBARDai_MISSILE_ID = "SM_SCbombard_m";//"" 
    @Override 
    public PluginPick pickMissileAI(MissileAPI missile,       //picks the ai 
                                    ShipAPI launchingShip) 
    { 
        if (LRPDai_MISSILE_ID.equals(missile.getProjectileSpecId()))     //missile 
        { 
            return new PluginPick(new LRPDai(missile), CampaignPlugin.PickPriority.MOD_SPECIFIC);     //Ai for that missile 
        } 
        if (BOMBARDai_MISSILE_ID.equals(missile.getProjectileSpecId()))     //another missile 
        { 
            return new PluginPick(new BOMBARDai(missile), CampaignPlugin.PickPriority.MOD_SPECIFIC);    //another ai 
        } 
        return null; 
    } 
Obviously this is an example, because the name of the AI script/file is "kitchensinkai" and the missile wepaon I want it to effect is named "advdronemissile". How would I change what's above to refrence the ai I want to use with my weapon and add it into a blank modplugin file?
Logged
Spoiler

[close]

xenoargh

  • Admiral
  • *****
  • Posts: 4854
  • naively breaking things!
    • View Profile
Re: The Radioactive Code Dump
« Reply #74 on: March 07, 2014, 11:54:09 AM »

You can use LazyLib's getCollides() function to see if the beam's line segment intersects the ship's collision boundary and, if yes, then <do whatever>.

On the missile, see VacuumModPlugin.java in Vacuum for a practical example of how to incorporate a missile AI.
Logged
Please check out my SS projects :)
Xeno's Mod Pack
Pages: 1 ... 3 4 [5] 6 7 ... 9