Fractal Softworks Forum

Starsector => Mods => Modding => Topic started by: xenoargh on July 28, 2013, 07:13:02 PM

Title: The Radioactive Code Dump
Post by: xenoargh on July 28, 2013, 07:13:02 PM
Some utility stuff I've been working on and wanted to give to people to do cool stuff with.  

Some of these require LazyLib (http://fractalsoftworks.com/forum/index.php?topic=5444.0), because I'm even lazier than LazyWizard, and see no reason to reinvent that wonderful toolkit.

An auto-explosion generator with automatic AOE.  
Projectile hits stuff and then makes neato explosion and blows up nearby things.
Provides examples of particle-effect manipulation and how to build a simple AOE system on this engine.  Requires LazyLib.

Does not require the AOE-ish Behavior defined for the Projectile (which has advantages and disadvantages - no auto-trigger near other projectiles, but no auto-trigger near spacecraft, either).  Can't be used with Vanilla AOE right now (but this should work in 0.6+), as that apparently isn't allowing the code to run.  Can't be used with Missiles currently (ditto).  I expect that most of this will be resolved in 0.6.
It has some accuracy issues; sometimes objects within the AOE will not register a hit, for whatever reasons.  This mainly effects small stuff.  There's a kludge in place atm to take care of that.
Spoiler
Code
///////////////////////GENERIC EXPLOSION GENERATOR AND AOE SIMULATION
///////////////////////REQUIRES LAZYLIB
package data.scripts;

import java.awt.Color;

import org.lwjgl.util.vector.Vector2f;
import com.fs.starfarer.api.combat.CombatEngineAPI;
import com.fs.starfarer.api.combat.CombatEntityAPI;
import com.fs.starfarer.api.combat.DamageType;
import com.fs.starfarer.api.combat.DamagingProjectileAPI;
import com.fs.starfarer.api.combat.OnHitEffectPlugin;
import com.fs.starfarer.api.combat.ShipAPI;
import com.fs.starfarer.api.combat.WeaponAPI;
import java.util.List;
import java.util.Iterator;

import org.lazywizard.lazylib.MathUtils;
import org.lazywizard.lazylib.CollisionUtils;
import org.lazywizard.lazylib.combat.*;

public class ExplosiveOnHitEffectWithAOE implements OnHitEffectPlugin {

public void onHit(DamagingProjectileAPI projectile, CombatEntityAPI target,
 Vector2f point, boolean shieldHit, CombatEngineAPI engine) {

float emp = projectile.getEmpAmount();
float dam = projectile.getDamageAmount();
WeaponAPI weapon = projectile.getWeapon();
DamageType damType = weapon.getDamageType();
float aoeToCheck = Math.max(dam / 10f, 20f);//Checks an area equal to damage / 10, with a floor of 20 (anything lower than that isn't worth doing, imo, but you may want to put an upper cap on really huge-damage weapons); can be adjusted to whatever you want
float aoeMult = 0.5f;//This is our multiplier against damage for distant targets, for balance purposes.
//AOE CODE
//This code searches first for nearby Ships, then for nearby non-ship entities, and applies damage to them over the AOE.
//When it checks for nearby ships, if it finds any it gets a valid collision (or not) by doing a ray-test...
//Which keeps things easy (don't thank me, thank Alex / LazyWizard)
//We don't need that (fairly expensive) check for nearby missiles, so we don't bother.
//If the checks come up with anything, then it gets damaged, in this case by half of the base damage.
List nearbyEnemies = CombatUtils.getShipsWithinRange(point, aoeToCheck, true);
for(Iterator iterMe = nearbyEnemies.iterator(); iterMe.hasNext();)
{
CombatEntityAPI targEnt = (CombatEntityAPI) iterMe.next();
if(targEnt != target && (targEnt.getOwner() != ((CombatEntityAPI) projectile.getSource()).getOwner()))//No double hits or friendly fire.
{

Vector2f targLoc = targEnt.getLocation();
//Try to get a valid collision point between our explosion's point source and the Entity.
Vector2f colPoint = CollisionUtils.getCollisionPoint(point, targLoc, targEnt);
//If we can't get a good collision point, use the center of the target Entity.  This is potentially a balance issue (hits all going to one armor cell are pretty OP lol), but this case mainly covers little teeny drones and suchlike that should be registering hits from giant explosions nearby, but often don't, for whatever reason.  Bigger things rarely fail, so it usually works out.
if(colPoint == null) colPoint = targLoc;
if(colPoint != null)//Must check this, getCollisionPoint returns null fairly frequently and that's a wrap
{
engine.applyDamage(
targEnt, //enemy Entity
colPoint, //Our 2D vector to the exact world-position of the collision
dam * aoeMult, //DPS modified by the damage multiplier
damType, //Using the damage type here, so that Kinetic / Explosive / Fragmentation AOE works.
emp * aoeMult, //EMP
false, //Does not bypass shields.
false, //Does not do Soft Flux damage (unless you want it to for some strange reason)
projectile.getSource()  //Who owns this projectile?
);
//This is just for testing purposes- shows a blue dot if, when and where hits occur.  Handy for testing balance.
/*
for(int i = 0; i < 6; i++)
{
engine.addHitParticle(colPoint, new Vector2f(MathUtils.getRandomNumberInRange(-5,5), MathUtils.getRandomNumberInRange(-5,5)), MathUtils.getRandomNumberInRange(25,50), 1f, 100f, new Color(0,0,255,255));
}*/

}
}
}
//Simpler task for missiles- we don't need an intersection with Hulls to get a valid armor location.
nearbyEnemies = CombatUtils.getMissilesWithinRange(point, aoeToCheck, true);
for(Iterator iterMe = nearbyEnemies.iterator(); iterMe.hasNext();)
{
CombatEntityAPI targEnt = (CombatEntityAPI) iterMe.next();
if(targEnt != target && (targEnt.getOwner() != ((CombatEntityAPI) projectile.getSource()).getOwner()))//No double hits or friendly fire.
{
Vector2f targLoc = targEnt.getLocation();
engine.applyDamage(
targEnt, //enemy Entity
targLoc, //Our 2D vector to the exact world-position of the collision
dam * aoeMult, //DPS modified by the damage multiplier
damType, //Using the damage type here, so that Kinetic / Explosive / Fragmentation AOE works.
emp * aoeMult, //EMP
false, //Does not bypass shields.
false, //Does not do Soft Flux damage (unless you want it to for some strange reason)
projectile.getSource()  //Who owns this projectile?
);
}
}

//EXPLOSION BITZ
//This code auto-magically builds a reasonably-cool generic explosion, based on weapon damage.
//There isn't much code that's expensive here and the game does auto-cull particles, so this can be made more complex if you want.  Just be careful about how many particles you create per event, since that's a linear cost (i.e., if you stick this on a Vulcan-type weapon, keep the total count low, low or re-write that part).  
int repeat = Math.max(5,(int) ((dam / 60f)));
int repeatTwo = repeat / 3;

//This bit makes backdrop glows show up fairly briefly behind our main explosion.
//Just one particle per repeat, but it costs a lot of operations to built it.
for(int i = 0; i < repeatTwo; i++)
{
Vector2f randPoint = MathUtils.getRandomPointInCircle(point,dam/35f);
float randSize = MathUtils.getRandomNumberInRange(dam/25f,Math.max(dam / 20f,15f));
Vector2f randVec = new Vector2f(MathUtils.getRandomNumberInRange(-dam / 50f ,dam / 50f),MathUtils.getRandomNumberInRange(-dam / 50f ,dam / 50f));
float randDur = 0.1f + MathUtils.getRandomNumberInRange(0.0f,0.3f);
int yelVal = (int) (Math.random() * 200f + 32f);
int randTrans = (int) MathUtils.getRandomNumberInRange(64f,128f);

engine.addSmoothParticle(randPoint, randVec, randSize, 1f, randDur, new Color(255,yelVal,0,randTrans));
}

//Main explosion, fast & slow fires and smoke at the end.
//Six particles per repeat, min 30.
for(int i = 0; i < repeat; i++)
{

int yelVal = (int) (Math.random() * 128f + 64f);
Vector2f randPoint = MathUtils.getRandomPointInCircle(point,dam/100f);

//Random Vectors, in order of speed; generally, we need smoke and central poof to be slower than others
Vector2f randVecFast = new Vector2f(MathUtils.getRandomNumberInRange(-30f - dam / 2f ,30f + dam / 2f),MathUtils.getRandomNumberInRange(-30f - dam / 2f ,30f + dam / 2f));
Vector2f randVecFastTwo = new Vector2f(MathUtils.getRandomNumberInRange(-30f - dam / 2f ,30f + dam / 2f),MathUtils.getRandomNumberInRange(-30f - dam / 2f ,30f + dam / 2f));
Vector2f randVec = new Vector2f(MathUtils.getRandomNumberInRange(-dam / 25f ,dam / 25f),MathUtils.getRandomNumberInRange(-dam / 25f ,dam / 25f));
Vector2f randVecTwo = new Vector2f(MathUtils.getRandomNumberInRange(-dam / 35f ,dam / 35f),MathUtils.getRandomNumberInRange(-dam / 35f,dam / 35f));
Vector2f randVecThree = new Vector2f(MathUtils.getRandomNumberInRange(-dam / 40f ,dam / 40f),MathUtils.getRandomNumberInRange(-dam / 40f,dam / 40f));

float randSize = MathUtils.getRandomNumberInRange(10f,Math.max(dam / 50f,15f));
float randSizeTwo = MathUtils.getRandomNumberInRange(5f,10f);
float randDur = 1f + MathUtils.getRandomNumberInRange(-0.5f,3f);
float randDurTwo = MathUtils.getRandomNumberInRange(0.5f,1f);
int randTrans = (int) MathUtils.getRandomNumberInRange(32f,200f);
int randGray = (int) MathUtils.getRandomNumberInRange(32f,64f);

engine.addHitParticle(point, randVecFast, randSizeTwo, 1f, randDurTwo * 0.5f, new Color(255,yelVal + 64,0,randTrans + 55));
engine.addHitParticle(point, randVecFastTwo, randSizeTwo, 1f, randDurTwo * 0.65f, new Color(255,yelVal + 64,0,randTrans + 55));
engine.addHitParticle(point, randVec, randSize, 1f, randDurTwo, new Color(255,yelVal-64,0,randTrans));
engine.addHitParticle(point, randVecTwo, randSize, 1f, randDurTwo * 0.75f, new Color(255,yelVal-32,0,randTrans));
engine.addHitParticle(point, randVecThree, randSize, 1f, randDurTwo * 0.5f, new Color(255,yelVal,0,randTrans));

engine.addSmokeParticle(point, randVecThree, MathUtils.getRandomNumberInRange(dam / 100f,dam / 50f), MathUtils.getRandomNumberInRange(0.5f,1f), randDur, new Color(randGray,randGray,randGray,randTrans));
}
}
}
[close]

"Heat" special projectile type.  
When it (beam / projectile) hits, it generates multiple sub-hits around the target, potentially bypassing the shields.  Thus it can knock out engines even as the target attempts to retreat and other Fun Things.  
It's insidiously powerful; feel free to combine it with AOE for the ultimate in "crisping" fun.  Space napalm, baby.
Requires LazyLib.
Spoiler
Code
package data.scripts;

import java.awt.Color;

import org.lwjgl.util.vector.Vector2f;
import com.fs.starfarer.api.combat.CombatEngineAPI;
import com.fs.starfarer.api.combat.CombatEntityAPI;
import com.fs.starfarer.api.combat.WeaponAPI;
import com.fs.starfarer.api.combat.DamageType;
import com.fs.starfarer.api.combat.DamagingProjectileAPI;
import com.fs.starfarer.api.combat.OnHitEffectPlugin;
import com.fs.starfarer.api.combat.ShipAPI;

import org.lazywizard.lazylib.CollisionUtils;
import org.lazywizard.lazylib.MathUtils;

public class HellboreOnHitEffect implements OnHitEffectPlugin {

public void onHit(DamagingProjectileAPI projectile, CombatEntityAPI target,
 Vector2f point, boolean shieldHit, CombatEngineAPI engine) {
if (target instanceof ShipAPI) {
float emp = projectile.getEmpAmount() * 0.1f;
float dam = projectile.getDamageAmount() * 0.1f;
WeaponAPI weapon = projectile.getWeapon();
DamageType damType = weapon.getDamageType();
int heatMe = (int) Math.max(6f,dam / 150f);

Vector2f targLoc = target.getLocation();
for(int i = 0; i < heatMe; i++)
{
//Gets a point far, far away and uses it as our ray-test point.  Long distances work better than shorter ones.
Vector2f cloneLoc = MathUtils.getRandomPointOnCircumference(targLoc, 1000000f);

//Try to get a valid collision point between our explosion's point source and the Entity.
Vector2f colPoint = CollisionUtils.getCollisionPoint(cloneLoc, targLoc, target);
//If we can't get a good collision point, use the center of the target Entity.  This is potentially a balance issue (hits all going to one armor cell are pretty OP lol), but this case mainly covers little teeny drones and suchlike that should be registering hits from giant explosions nearby, but often don't, for whatever reason.  Bigger things rarely fail, so it usually works out.
if(colPoint == null) colPoint = targLoc;
if(colPoint != null)//Must check this, getCollisionPoint returns null fairly frequently and that's a wrap
{
engine.applyDamage(
target, //enemy Entity
colPoint, //Our 2D vector to the exact world-position of the collision
dam, //DPS modified by the damage multiplier
damType, //Using the damage type here, so that Kinetic / Explosive / Fragmentation AOE works.
emp, //EMP (if any)
false, //Does not bypass shields.
false, //Does not do Soft Flux damage (unless you want it to for some strange reason)
projectile.getSource()  //Who owns this projectile?
);
}
}  
}
}
}
[close]

Physics fun.  
Car-like driving behaviors with skidding and rolling resistance and stuff.  
Not super-fancy but it gets the job done for sea or land warfare, where we don't want turning to feel like Outer Space, or where we'd like to simulate inertial changes in space a bit more realistically.  

As a bonus, also has code for "inertialess" drive behaviors- change direction without any velocity costs (or adjust as required).
Intended to run within an EveryFrameCombatPlugin.  
Requires some basic vector-math knowledge to do much with, but it has a lot of the basics like adjusting the forward / rightward vectors relative to the Entity's current velocity.

This is as far as my Autoduel-ish mod idea (well, that and a few silly graphics things that might just work in 0.6+) got before I ran out of steam.
Spoiler
Code
		//Gets all ships;
List ships = engine.getAllShips();
Iterator it = ships.iterator();
while (it.hasNext())
{
ShipAPI ship = (ShipAPI) it.next();

if (ship.isFrigate())
{
ship.setCollisionClass(CollisionClass.FIGHTER);
continue;
}

if (ship.isFighter() || ship.isShuttlePod() || ship.isFrigate() || ship.isDrone()) continue;

//Gets the ship's velocity, expressed as a vector.
Vector2f shipVec = ship.getVelocity();
shipVec = (Vector2f) shipVec.scale(0.999f);
float shipSpeed = (float) Math.sqrt(Math.abs(shipVec.getX() * shipVec.getX()) + Math.abs(shipVec.getY() * shipVec.getY()));
MutableShipStatsAPI stats = ship.getMutableStats();
float maxSpeed = (float) stats.getMaxSpeed().getBaseValue();
float speedPenalty = Math.min(50f,(shipSpeed / maxSpeed) * 100.0f);
String id = ship.getFleetMemberId();
stats.getMaxTurnRate().unmodify(id);
stats.getTurnAcceleration().unmodify(id);
stats.getMaxTurnRate().modifyPercent(id,-speedPenalty);
stats.getTurnAcceleration().modifyPercent(id,-speedPenalty);


//public MutableStat getMaxTurnRate();
//public MutableStat getTurnAcceleration();

//Converts the player's heading (in degrees) back to unit vectors.
Vector2f rotVec = new Vector2f ((float) Math.cos(ship.getFacing() * 3.14159f / 180f), (float) Math.sin(ship.getFacing() * 3.14159f / 180f));
//Gets the "right" vector, i.e. perpendicular to the forward axis
Vector2f rightVec = new Vector2f(rotVec.getY() * -1.0f,rotVec.getX());
//Dot products of the two vectors
float dotForward = Vector2f.dot(shipVec, rotVec);
float dotRight = Vector2f.dot(shipVec, rightVec);
//Gives us the forward and rightward vectors of current velocity
Vector2f forwardVelocity = new Vector2f(rotVec.getX() * dotForward, rotVec.getY() * dotForward);
Vector2f rightVelocity = new Vector2f(rightVec.getX() * dotRight, rightVec.getY() * dotRight);
//Final conversion, drops the rightward component's magnitude while preserving the forward component.
//The multiplier 0.02f is our tuning variable; try different amounts to play with the "friction".
Vector2f.add(shipVec,new Vector2f(rightVelocity.getX() * -0.02f, rightVelocity.getY() * -0.02f),shipVec);

//This is an alternative behavior; it keeps forward velocity (towards the forward vector) constant but lowers...
//"right" velocity, resulting in a ship with an "inertialess" drive.  It might be useful for various behaviors.
//Commented out, because the above behavior is closer to classic land / sea movement.
//shipVec = shipVec.add(forwardVelocity,new Vector2f(rightVelocity.getX() * -0.02f, rightVelocity.getY() * -0.02f),shipVec);
}
[close]

Feel free to dump more useful code here; I'd prefer to see things that solve a specific engineering / gamecode problem or provide a nifty visual effect, since those tend to be the hard things.  Documentation is a must; there's no point in giving out neato stuff that's black-box to all but the 1337 :)

If you're wondering what what license it's released as, it's CC-CR, i.e., please credit me if you use this stuff, otherwise do whatever you want, no permission required or any of that stuff.
Title: Re: The Radioactive Code Dump
Post by: xenoargh on July 28, 2013, 07:16:46 PM
Mission example. 
A Mission plugin and Mission that uses it to generate a bunch of dead ships floating around at the start of the Mission.  Used it to generate a "ghost town" during a Mission, but it's a crude example of how to actually make / use the Plugins.  Credit to LazyWizard's half-finished minigame for getting me on the right track.

Mission code.
Spoiler
Code
package data.missions.paragone;

import com.fs.starfarer.api.campaign.CargoAPI.CrewXPLevel;
import com.fs.starfarer.api.mission.FleetSide;
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;

public class MissionDefinition implements MissionDefinitionPlugin {

public void defineMission(MissionDefinitionAPI api) {

// Set up the fleets
api.initFleet(FleetSide.PLAYER, "TTS", FleetGoal.DEFEND, false, 30);
api.initFleet(FleetSide.ENEMY, "LI", FleetGoal.ATTACK, true, 30);

// Set a blurb for each fleet
api.setFleetTagline(FleetSide.PLAYER, "Emergency Force Delta");
api.setFleetTagline(FleetSide.ENEMY, "The Rogue L.I. / TTS Decisive");

// These show up as items in the bulleted list under
// "Tactical Objectives" on the mission detail screen
api.addBriefingItem("The TTS Decisive must be defeated.  The Rook must survive.");

// Set up the player's fleet
api.addToFleet(FleetSide.PLAYER, "aurora_Balanced", FleetMemberType.SHIP, "TTS Rook", true, CrewXPLevel.ELITE);
api.addToFleet(FleetSide.PLAYER, "silent_raven_Standard", FleetMemberType.SHIP, "TTS Dark Wing", true, CrewXPLevel.ELITE);
api.addToFleet(FleetSide.PLAYER, "silent_raven_Standard", FleetMemberType.SHIP, "TTS Ebon Claw", true, CrewXPLevel.ELITE);
//api.addToFleet(FleetSide.PLAYER, "onslaught_Standard", FleetMemberType.SHIP, "TTS Invincible", true, CrewXPLevel.ELITE);

// Mark player flagship as essential.
api.defeatOnShipLoss("TTS Rook");

// Set up the enemy fleet
api.addToFleet(FleetSide.ENEMY, "paragon_Elite", FleetMemberType.SHIP, "TTS Decisive", true, CrewXPLevel.ELITE);

// Set up the map.
float width = 9000f;
float height = 9000f;
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 < 15; i++) {
float x = (float) Math.random() * width - width/2;
float y = (float) Math.random() * height - height/2;
float radius = 100f + (float) Math.random() * 900f;
api.addNebula(x, y, radius);
}

// Add an asteroid field going diagonally across the
// battlefield, 2000 pixels wide, with a maximum of
// 100 asteroids in it.
// 20-70 is the range of asteroid speeds.
api.addAsteroidField(0f, 0f, (float) Math.random() * 360f, width,
20f, 70f, 100);
api.addPlugin(new RandomJunkScript(width, height));
}

}
[close]


The plugin that generates the random junk.
Spoiler
Code
package data.missions.paragone;

import com.fs.starfarer.api.Global;
import com.fs.starfarer.api.combat.CombatEngineAPI;
import com.fs.starfarer.api.combat.CombatEntityAPI;
import com.fs.starfarer.api.combat.EveryFrameCombatPlugin;
import com.fs.starfarer.api.combat.MutableShipStatsAPI;
import com.fs.starfarer.api.combat.ShipAPI;
import com.fs.starfarer.api.fleet.FleetMemberAPI;
import com.fs.starfarer.api.mission.FleetSide;
import com.fs.starfarer.api.util.IntervalUtil;
import java.util.List;
import org.lazywizard.lazylib.MathUtils;
import org.lwjgl.util.vector.Vector2f;
import com.fs.starfarer.api.combat.DamageType;
import java.awt.Color;

public class RandomJunkScript implements EveryFrameCombatPlugin
{
private IntervalUtil goInterval = new IntervalUtil(0.1f, 0.1f);

    // Whether one-time events have been triggered yet
    private boolean hasSpawned = false;

    // The combat engine object used by this battle
    private CombatEngineAPI engine;

private float height;
private float width;

    public RandomJunkScript(float width, float height)
    {
        // We're just passing the battlefield's height and width in here
        this.height = height;
this.width = width;
    }

private void buildShip(String type)
{
float x = (float) Math.random() * width - width/2;
float y = (float) Math.random() * height - height/2;
Vector2f loc = new Vector2f();
loc.x = x;
loc.y = y;
float facing = (float) Math.random() * 360f;
//String variantID = "brawler_Assault"; //Safety code here


//Give the enemy fleet this ship... temporarily...
ShipAPI target = engine.getFleetManager(FleetSide.ENEMY).spawnShipOrWing(type, loc, facing);
//re-use loc here, use the new position of the ship we've built
loc = target.getLocation();
//move the ship waaaaay off-screen
loc.x += 100000f;
loc.y += 100000f;

//Blow up the new ship.
engine.spawnEmpArc(target, loc, (CombatEntityAPI) target, (CombatEntityAPI) target,
   DamageType.ENERGY,
   1000000f,
   0f, // emp
   10000000f, // max range
   "tachyon_lance_emp_impact",
   0.0001f, // thickness
   new Color(0,0,0,0),
   new Color(0,0,0,0)
   );
//Move the ship back to its spawn position.
loc.x -= 100000f;
loc.y -= 100000f;
}

private void buildFriendlyShip(String type)
{
float x = (float) Math.random() * width - width/2;
float y = (float) (Math.random() * (height / 4)) - height / 8 - 2000f;
Vector2f loc = new Vector2f();
loc.x = x;
loc.y = y;
float facing = (float) Math.random() * 360f;

//Give our fleet the ship.
ShipAPI target = engine.getFleetManager(FleetSide.PLAYER).spawnShipOrWing(type, loc, facing);
}


    @Override
    public void advance(float amount, List events)
    {
        // If the game is paused or we've already done the one-time events, do nothing

        if (engine.isPaused())
        {
            return;
        }
        // One time setup at battle start
goInterval.advance(amount);
       if (goInterval.intervalElapsed() && !hasSpawned)
        {

int randomNum = (int) (Math.random() * 6f + 1f);
    for (int i = 0; i < randomNum; i++) {

   buildShip("afflictor_Strike");
}
randomNum = (int) (Math.random() * 6f + 1f);
    for (int i = 0; i < randomNum; i++) {

   buildShip("aurora_Balanced");
}
randomNum = (int) (Math.random() * 6f + 1f);
    for (int i = 0; i < randomNum; i++) {

   buildShip("buffalo_Standard");
}
randomNum = (int) (Math.random() * 6f + 1f);
    for (int i = 0; i < randomNum; i++) {

   buildShip("odyssey_Balanced");
}
randomNum = (int) (Math.random() * 6f + 1f);
    for (int i = 0; i < randomNum; i++) {

   buildFriendlyShip("aurora_Balanced");
}
hasSpawned = true;
        }
    }

    @Override
    public void init(CombatEngineAPI engine)
    {
        this.engine = engine;
    }
}
[close]
Title: Re: The Radioactive Code Dump
Post by: silentstormpt on July 30, 2013, 12:44:49 PM
No major changes on the main code on your Physics Fun, just made it an actual class that can be used by copy/paste into a file named data/scripts/plugins/PhysicsFun.java:

Code: java
package data.scripts.plugins;

import com.fs.starfarer.api.combat.CollisionClass;
import com.fs.starfarer.api.combat.CombatEngineAPI;
import com.fs.starfarer.api.combat.EveryFrameCombatPlugin;
import com.fs.starfarer.api.combat.MutableShipStatsAPI;
import com.fs.starfarer.api.combat.ShipAPI;

import java.util.List;
import java.util.ListIterator;

import org.lwjgl.util.vector.Vector2f;

/*
* Code by: xenoargh
*/

public class PhysicsFun implements EveryFrameCombatPlugin
{
    private CombatEngineAPI engine;

    @Override
    public void init(CombatEngineAPI engine)
    {
        this.engine = engine;
    }//INIT
    
    @Override
    public void advance(float amount, List events)
    {
        if (engine.isPaused()) return;
        
        ListIterator allships = engine.getShips().listIterator();
        
        if(allships.hasNext())
        {
             while(allships.hasNext())
             {
ShipAPI ship = (ShipAPI) allships.next();

if (ship.isFrigate())
{
ship.setCollisionClass(CollisionClass.FIGHTER);
continue;
}//IF

if (ship.isFighter() || ship.isShuttlePod() || ship.isFrigate() || ship.isDrone())
                            continue;//IF

//Gets the ship's velocity, expressed as a vector.
Vector2f shipVec = ship.getVelocity();
shipVec = (Vector2f) shipVec.scale(0.999f);
float shipSpeed = (float) Math.sqrt(Math.abs(shipVec.getX() * shipVec.getX()) + Math.abs(shipVec.getY() * shipVec.getY()));
MutableShipStatsAPI stats = ship.getMutableStats();
float maxSpeed = (float) stats.getMaxSpeed().getBaseValue();
float speedPenalty = Math.min(50f,(shipSpeed / maxSpeed) * 100.0f);
String id = ship.getFleetMemberId();
stats.getMaxTurnRate().unmodify(id);
stats.getTurnAcceleration().unmodify(id);
stats.getMaxTurnRate().modifyPercent(id,-speedPenalty);
stats.getTurnAcceleration().modifyPercent(id,-speedPenalty);

//public MutableStat getMaxTurnRate();
//public MutableStat getTurnAcceleration();

//Converts the player's heading (in degrees) back to unit vectors.
Vector2f rotVec = new Vector2f ((float) Math.cos(ship.getFacing() * 3.14159f / 180f), (float) Math.sin(ship.getFacing() * 3.14159f / 180f));
//Gets the "right" vector, i.e. perpendicular to the forward axis
Vector2f rightVec = new Vector2f(rotVec.getY() * -1.0f,rotVec.getX());
//Dot products of the two vectors
float dotForward = Vector2f.dot(shipVec, rotVec);
float dotRight = Vector2f.dot(shipVec, rightVec);
//Gives us the forward and rightward vectors of current velocity
Vector2f forwardVelocity = new Vector2f(rotVec.getX() * dotForward, rotVec.getY() * dotForward);
Vector2f rightVelocity = new Vector2f(rightVec.getX() * dotRight, rightVec.getY() * dotRight);
//Final conversion, drops the rightward component's magnitude while preserving the forward component.
//The multiplier 0.02f is our tuning variable; try different amounts to play with the "friction".
Vector2f.add(shipVec,new Vector2f(rightVelocity.getX() * -0.02f, rightVelocity.getY() * -0.02f),shipVec);

//This is an alternative behavior; it keeps forward velocity (towards the forward vector) constant but lowers...
//"right" velocity, resulting in a ship with an "inertialess" drive.  It might be useful for various behaviors.
//Commented out, because the above behavior is closer to classic land / sea movement.
//shipVec = shipVec.add(forwardVelocity,new Vector2f(rightVelocity.getX() * -0.02f, rightVelocity.getY() * -0.02f),shipVec);
}//WHILE
        }//IF
    }//ADVANCE
}//CLASS

Just a little more "non programmer friendly"
Title: Re: The Radioactive Code Dump
Post by: silentstormpt on August 02, 2013, 09:13:09 AM
Critical Hit System

Requires LazyLib 1.5

What it does is, every projectile/missile (except beams) has a chance to crit depending on 3 Important factors:


Every time a projectile crits, it will show a message on the poor soul who took that crit in a red text, theres also explosions and a smoke coming out of that spot where the projectile crits
Theres some extra code i left since it wont do any changes to the actual scripts intended purpose and its for a possible update on it.

To add this you just need to create a java file in the data/scripts/plugins/[name_of_the_script].java (without the []), dont forget to change on this part of the code as well
Code
public class [name_of_the_script] implements EveryFrameCombatPlugin
:

Code: java
package data.scripts.plugins;

import com.fs.starfarer.api.Global;
import com.fs.starfarer.api.combat.CollisionClass;
import com.fs.starfarer.api.combat.CombatEngineAPI;
import com.fs.starfarer.api.combat.CombatEntityAPI;
import com.fs.starfarer.api.combat.DamageType;
import com.fs.starfarer.api.combat.DamagingProjectileAPI;
import com.fs.starfarer.api.combat.EveryFrameCombatPlugin;
import com.fs.starfarer.api.combat.MutableStat.StatMod;
import com.fs.starfarer.api.combat.ShipAPI;
import com.fs.starfarer.api.combat.ShipSystemAPI;
import com.fs.starfarer.api.combat.WeaponAPI;
import com.fs.starfarer.api.combat.WeaponAPI.WeaponSize;
import java.awt.Color;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Random;
import org.lazywizard.lazylib.CollisionUtils;
import org.lazywizard.lazylib.MathUtils;
import org.lazywizard.lazylib.combat.CombatUtils;
import org.lwjgl.util.vector.Vector2f;

/*
 * Multiple scripts from the following authors:
 * xangle13 for hes: http://fractalsoftworks.com/forum/index.php?topic=5819 and http://fractalsoftworks.com/forum/index.php?topic=6003.0
 * Psiyon for hes Original Crit System Script;
 * LazyWizard for hes IncendiaryAmmoPlugin, ArmorPiercePlugin and LazyLib 1.5;
 * Alex for helping me with a issue;
 * @author eXist3nZ
 */
public class [name_of_the_script] implements EveryFrameCombatPlugin
{
    // Combat Engine ofc
    private CombatEngineAPI engine;
    // Change this value for hull mods for example, original value is 5% Crit Chance
    public float PercCritChance = 0.05f;
    // CritDamage is 2 times the normal damage.
    public float CritDamage = 2f;
    // Sound to play while piercing a target's shield - Depending on size
    private static final Map SOUND_IDS_PENETRATION_SHIELD = new HashMap();
    static
    {
        SOUND_IDS_PENETRATION_SHIELD.put("Small", "explosion_ship");
        SOUND_IDS_PENETRATION_SHIELD.put("Medium", "explosion_ship");
        SOUND_IDS_PENETRATION_SHIELD.put("Large", "explosion_ship");
    }
    // Sound to play while piercing a target's armor - Depending on size (should be loopable!)
    private static final Map SOUND_IDS_PENETRATION_ARMOR = new HashMap();
    static
    {
        SOUND_IDS_PENETRATION_ARMOR.put("Small", "explosion_ship");
        SOUND_IDS_PENETRATION_ARMOR.put("Medium", "explosion_ship");
        SOUND_IDS_PENETRATION_ARMOR.put("Large", "explosion_ship");
    }
    // Sound on High Explosive explosion
    private static final Map SOUND_IDS_HE = new HashMap();
    static
    {
        SOUND_IDS_HE.put("isFrigate", "explosion_ship");
        SOUND_IDS_HE.put("isDestroyer", "explosion_ship");
        SOUND_IDS_HE.put("isCruiser", "explosion_ship");
        SOUND_IDS_HE.put("isCapital", "explosion_ship");
    }
    // Sound on Energy explosion
    private static final Map SOUND_IDS_ENERGY = new HashMap();
    static
    {
        SOUND_IDS_ENERGY.put("isFrigate", "explosion_ship");
        SOUND_IDS_ENERGY.put("isDestroyer", "explosion_ship");
        SOUND_IDS_ENERGY.put("isCruiser", "explosion_ship");
        SOUND_IDS_ENERGY.put("isCapital", "explosion_ship");
    }
    // Projectile ID (String), pierces shields (boolean)
    private static final List PROJ_IDS = new ArrayList();
    static
    {
        //add all the projectile ids that ignore shield
        PROJ_IDS.add("lw_impaler_shot");
    }
  
    @Override
    public void init(CombatEngineAPI engine) {
        this.engine = engine;
    }
  
    @Override
    public void advance(float amount, List events)
    {
        // Obvious exploit is obvious
        if (engine.isPaused())
        {
            return;
        }

        DamagingProjectileAPI proj;
        CombatEntityAPI entity;
        String spec;

        // Scan all shots on the map for armor piercing projectiles
        for (Iterator itproj = engine.getProjectiles().iterator(); itproj.hasNext();)
        {
            //Grab a projectile in the battlefield
            proj = (DamagingProjectileAPI) itproj.next();
            //Get its ID (the one in the *.proj file)
            spec = proj.getProjectileSpecId();
            
            // We'll do collision checks manually
            //proj.setCollisionClass(CollisionClass.NONE);

            // Find nearby ships, missiles and asteroids
            List toCheck = CombatUtils.getShipsWithinRange(proj.getLocation(),
                    proj.getCollisionRadius() + 5f);
            toCheck.addAll(CombatUtils.getMissilesWithinRange(proj.getLocation(),
                    proj.getCollisionRadius() + 5f));
            toCheck.addAll(CombatUtils.getAsteroidsWithinRange(proj.getLocation(),
                    proj.getCollisionRadius() + 5f));
            // Don't include the ship that fired this projectile!
            toCheck.remove(proj.getSource());
            
            if(proj != null)
            {
                for (Iterator iter2 = toCheck.iterator(); iter2.hasNext();)
                {
                    entity = (CombatEntityAPI) iter2.next();

                    // Check for an active phase cloak
                    if (entity instanceof ShipAPI)
                    {
                        ShipSystemAPI cloak = ((ShipAPI) entity).getPhaseCloak();

                        if (cloak != null && cloak.isActive())
                        {
                            continue;
                        }//IF
                    }//IF

                    // Check for a shield hit
                    if ((entity.getShield() != null
                            && entity.getShield().isOn()
                            && entity.getShield().isWithinArc(proj.getLocation())))
                    {
                        ListIterator projlist = PROJ_IDS.listIterator();

                        while(projlist.hasNext())
                        {                                
                            if(projlist.next().equals(spec))
                            {
                                // Save the CollisionClass
                                CollisionClass projCollision = proj.getCollisionClass();
                                // If we hit a shield, disable collision
                                proj.setCollisionClass(CollisionClass.NONE);
                                // Stop the projectile (ensures a hit for fast projectiles)
                                proj.getVelocity().set(entity.getVelocity());
                                // Then move the projectile inside the ship's shield bounds
                                Vector2f.add((Vector2f) MathUtils.getDirectionalVector(proj,entity).scale(5f), proj.getLocation(), proj.getLocation());
                                // Give back its original Collision
                                proj.setCollisionClass(projCollision);
                                // Play piercing sound (depends on the weapon size)
                                Global.getSoundPlayer().playSound(SOUND_IDS_PENETRATION_SHIELD.get(proj.getWeapon().getSize().getDisplayName()).toString(),
                                        1f,
                                        1f,
                                        proj.getLocation(),
                                        proj.getVelocity());
                            }
                        }
                    }
                    // Check if the projectile is inside the entity's bounds
                    else if (CollisionUtils.isPointWithinBounds(proj.getLocation(), entity))
                    {  
                        //Is this a ship or a asteroid (dont forget asteroids can only be a CombatEntity)
                        if(critSucessfull(proj) && entity instanceof ShipAPI)
                        {
                            shipHitEffect ((ShipAPI) entity,  proj);
                        }//IF
                        else
                        {
                            continue;
                        }
                    }//IF
                }//FOR
            }//IF
        }//FOR
    }//advance
    
    public boolean critSucessfull(DamagingProjectileAPI damagingproj)
    {
        Random r = new Random();
        WeaponAPI weapon = damagingproj.getWeapon();
        float crewMaxXP = 0f;
        
        if(weapon.getDamageType().equals(DamageType.FRAGMENTATION) ||
                weapon.getDamageType().equals(DamageType.KINETIC) ||
                weapon.getDamageType().equals(DamageType.OTHER))
        {
            return false;
        }
        
        ShipAPI ship = damagingproj.getSource();
        float weaponRoF = weapon.getDerivedStats().getRoF();
          
        StatMod crewBonus = ship.getMutableStats().getAutofireAimAccuracy().getFlatStatMod("crew skill bonus");
        
        // Green crew don't grant a bonus
        if (crewBonus != null)
        {
            //Goes from 0f to 1f - thanks Alex and Lazy for the help
            crewMaxXP = crewBonus.getValue();
        }
        
        //slower shooting weapons should not surpass limit of 5%
        if (weaponRoF < 1f)
        {
            weaponRoF = 1f;
        }
        
        float CritChance = r.nextFloat();
        //This means that, if the crew is elite its (0.05f * 1) \ 1 that means 5%,
        //if the crew is actually lower skilled and the weapon's RoF is higher the chances get lower.
        float MAXCritChance = (PercCritChance * crewMaxXP) / weaponRoF;
        
        if(CritChance <= MAXCritChance)
        {
            return true;
        }
        else
        {
            return false;
        }
    }//critSucessful
    
    public void shipHitEffect (ShipAPI ship, DamagingProjectileAPI proj)
    {
        WeaponAPI weapon = proj.getWeapon();
        float explosionSize = 0f;
        float weaponRoF = weapon.getDerivedStats().getRoF();
        
        //Lets make sure we dont get a REALLY HUGE Crit text
        if (weaponRoF < 1f)
        {
            weaponRoF = 1f;
        }
        
        if(weapon.getSize().equals(WeaponSize.SMALL))
        {
            explosionSize = MathUtils.getRandomNumberInRange(20f, 30f) / weaponRoF;
        }
        else if(weapon.getSize().equals(WeaponSize.MEDIUM))
        {
            explosionSize = MathUtils.getRandomNumberInRange(30f, 40f) / weaponRoF;
        }
        else if(weapon.getSize().equals(WeaponSize.LARGE))
        {
            explosionSize = MathUtils.getRandomNumberInRange(40f, 50f) / weaponRoF;
        }
        
        engine.addFloatingText(proj.getLocation(), "CRITICAL HIT", explosionSize, Color.RED, proj.getDamageTarget(), 0f, 0f);
        
        if(weapon.getDamageType().equals(DamageType.HIGH_EXPLOSIVE))
        {
            if(ship.isFrigate()) {  
                explosionEffects(ship,
                        proj,
                        weapon.getDamageType(),
                        SOUND_IDS_HE.get("isFrigate").toString(),
                        explosionSize);
            }
            else if(ship.isDestroyer()) {
                explosionEffects(ship,
                        proj,
                        weapon.getDamageType(),
                        SOUND_IDS_HE.get("isDestroyer").toString(),
                        explosionSize);
            }
            else if(ship.isCruiser()) {
                explosionEffects(ship,
                        proj,
                        weapon.getDamageType(),
                        SOUND_IDS_HE.get("isCruiser").toString(),
                        explosionSize);
            }
            else if(ship.isCapital()) {
                explosionEffects(ship,
                        proj,
                        weapon.getDamageType(),
                        SOUND_IDS_HE.get("isCapital").toString(),
                        explosionSize);
            }
        }
        else if(weapon.getDamageType().equals(DamageType.ENERGY))
        {
            if(ship.isFrigate()) {  
                explosionEffects(ship,
                        proj,
                        weapon.getDamageType(),
                        SOUND_IDS_ENERGY.get("isFrigate").toString(),
                        explosionSize);
            }
            else if(ship.isDestroyer()) {
                explosionEffects(ship,
                        proj,
                        weapon.getDamageType(),
                        SOUND_IDS_ENERGY.get("isDestroyer").toString(),
                        explosionSize);
            }
            else if(ship.isCruiser()) {
                explosionEffects(ship,
                        proj,
                        weapon.getDamageType(),
                        SOUND_IDS_ENERGY.get("isCruiser").toString(),
                        explosionSize);
            }
            else if(ship.isCapital()) {
                explosionEffects(ship,
                        proj,
                        weapon.getDamageType(),
                        SOUND_IDS_ENERGY.get("isCapital").toString(),
                        explosionSize);
            }
        }
    }//shipHitEffect
    
    public void explosionEffects (ShipAPI ship, DamagingProjectileAPI proj, DamageType dmgtype, String ExplosionSoundID, float explosionSize)
    {
        if(dmgtype.equals(DamageType.HIGH_EXPLOSIVE))
        {
            float damage = proj.getDamageAmount() * CritDamage;
            float emp = proj.getEmpAmount() * CritDamage;

            // Apply damage and slow the projectile
            // Note: BALLISTIC_AS_BEAM projectiles won't be slowed!
            engine.applyDamage(ship,
                    proj.getLocation(), damage, proj.getDamageType(),
                    emp, true, true, proj.getSource());

            proj.getVelocity().scale(1.5f - 1.0f);
            
            engine.spawnExplosion(
                    proj.getLocation(),
                    ship.getVelocity(),
                    Color.ORANGE,
                    1f,
                    1.5f);

            // Play Explosion sound
            Global.getSoundPlayer().playSound(ExplosionSoundID,
                    1f,
                    1f,
                    proj.getLocation(),
                    ship.getVelocity());

            if(!ship.isHulk())
            {
                engine.addSmokeParticle(ship.getLocation(), // Location
                        ship.getVelocity(), // Velocity
                        explosionSize, // Size
                        MathUtils.getRandomNumberInRange(.5f, .75f), // Brightness
                        10f, Color.DARK_GRAY); // Duration, color
            }
        }
        else if(dmgtype.equals(DamageType.ENERGY))
        {
            float damage = proj.getDamageAmount() * CritDamage;
            float emp = proj.getEmpAmount() * CritDamage;

            // Apply damage and slow the projectile
            // Note: BALLISTIC_AS_BEAM projectiles won't be slowed!
            engine.applyDamage(ship,
                    proj.getLocation(),
                    damage,
                    proj.getDamageType(),
                    emp,
                    true,
                    true,
                    proj.getSource());

            proj.getVelocity().scale(1.5f - 1.0f);
            
            engine.spawnExplosion(
                    proj.getLocation(),
                    ship.getVelocity(),
                    Color.blue,
                    1f,
                    1.5f);

            // Play Crit Explosion
            Global.getSoundPlayer().playSound(ExplosionSoundID,
                    1f,
                    1f,
                    proj.getLocation(),
                    ship.getVelocity());

            if(!ship.isHulk())
            {
                engine.addSmokeParticle(ship.getLocation(), // Location
                        ship.getVelocity(), // Velocity
                        explosionSize, // Size
                        MathUtils.getRandomNumberInRange(.5f, .75f), // Brightness
                        10f, Color.DARK_GRAY); // Duration, color
            }
        }
    }//explosionEffects
}//MAIN CLASS

Major thanks for alot of ppl, specially Lazy for helping out and release the 1.5 LazyLib.

This script can be "upgraded easily".

EDIT: I've updated the Script, it now takes in account the weapon size, the crit text size depending on how much damage did that Crit do to the ship.
EDIT2: Also fixed a calculation error i hadn't noticed before at the time, some lower RoF weapons had huge crit chance do to being lower then 1
Title: Re: The Radioactive Code Dump
Post by: Thule on August 02, 2013, 12:24:49 PM
There a a couple of those code example threads flying around on the forum, could a mod compile them into a single thread?
Otherwise i fear alot of cool stuff gets lost and burried.
Title: Re: The Radioactive Code Dump
Post by: xenoargh on August 02, 2013, 01:59:35 PM
I love the critical hit system idea, can't wait to read through that one :)
Title: Re: The Radioactive Code Dump
Post by: xenoargh on August 02, 2013, 09:42:16 PM
The Freeze Ray.  Slows down Entities that are hit.  With enough of them, you can immobilize anything. 

Never see a pesky Frigate run away again!  Muahahahahaha... er, I mean, it works, and stuff.  Can't wait to release my mod now, these are just a few of the toys ;)

Spoiler
Code
package data.scripts;

import java.awt.Color;

import org.lwjgl.util.vector.Vector2f;

import com.fs.starfarer.api.combat.BeamAPI;
import com.fs.starfarer.api.combat.BeamEffectPlugin;
import com.fs.starfarer.api.combat.CombatEngineAPI;
import com.fs.starfarer.api.combat.CombatEntityAPI;
import com.fs.starfarer.api.combat.ShipAPI;

public class FreezeRayEffect implements BeamEffectPlugin {
public void advance(float amount, CombatEngineAPI engine, BeamAPI beam) {
CombatEntityAPI target = beam.getDamageTarget();
//Do we have a valid Entity to effect?
if (target != null)
{
//Yes!  Is it in range, and the beam's on?
if (beam.getBrightness() >= 1f)
{
//Get the velocity, and drop it- bigger ships drop a lot less than small ones.
//This might not be balanced for your mod.  The accumulative change in velocity per-frame is really huge!
Vector2f targVel = target.getVelocity();
if(target instanceof ShipAPI)
{
ShipAPI ship = (ShipAPI) target;
if(ship.isDrone())
{
targVel.x *= 0.75f;
targVel.y *= 0.75f;
} else if (ship.isFighter())
{
targVel.x *= 0.95f;
targVel.y *= 0.95f;
} else if (ship.isFrigate())
{
targVel.x *= 0.97f;
targVel.y *= 0.97f;
} else if (ship.isDestroyer())
{
targVel.x *= 0.99f;
targVel.y *= 0.99f;
} else if (ship.isCruiser())
{
targVel.x *= 0.995f;
targVel.y *= 0.995f;
} else if (ship.isCapital())
{
targVel.x *= 0.999f;
targVel.y *= 0.999f;
}
} else {
//If not a ship, slow down a lot.  Instant slowdown for bombs, takes just a few frames for most missiles.
targVel.x *= 0.75f;
targVel.y *= 0.75f;
}
}
}
}
}
[close]
Title: Re: The Radioactive Code Dump
Post by: xenoargh on August 02, 2013, 10:10:38 PM
Fixed version of the Critical Hit system; uses larger text if the damage is heavy, loses a few operations it didn't need, uses the correct formula for Rate of Fire.

Spoiler
Code
//Silentstormpt's Critical Hit system, v. 2.0, xenoargh build
//Credits to Silentstormpt, xangle13, Psyion, LazyWizard, xenoargh, eXist3nz, Alex
package data.scripts.plugins;

import com.fs.starfarer.api.Global;
import com.fs.starfarer.api.combat.CombatEngineAPI;
import com.fs.starfarer.api.combat.CombatEntityAPI;
import com.fs.starfarer.api.combat.DamageType;
import com.fs.starfarer.api.combat.DamagingProjectileAPI;
import com.fs.starfarer.api.combat.EveryFrameCombatPlugin;
import com.fs.starfarer.api.combat.MutableStat.StatMod;
import com.fs.starfarer.api.combat.ShipAPI;
import com.fs.starfarer.api.combat.ShipSystemAPI;
import com.fs.starfarer.api.combat.WeaponAPI;
import java.awt.Color;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.Random;
import org.lazywizard.lazylib.CollisionUtils;
import org.lazywizard.lazylib.MathUtils;
import org.lazywizard.lazylib.combat.CombatUtils;
import org.lwjgl.util.vector.Vector2f;


public class CriticalHits implements EveryFrameCombatPlugin
{
private CombatEngineAPI engine;
// Sound to play while piercing a target's armor (should be loopable!)
private static final String PIERCE_SOUND = "explosion_ship"; // TEMPORARY

public void init(CombatEngineAPI engine) {
this.engine = engine;
}

public void advance(float amount, List events)
{
// Obvious exploit is obvious
if (engine.isPaused())
{
return;
}

DamagingProjectileAPI proj;
CombatEntityAPI entity;
String spec;

// Scan all shots on the map for armor piercing projectiles
for (Iterator itproj = engine.getProjectiles().iterator(); itproj.hasNext();)
{
//Grab a projectile in the battlefield
proj = (DamagingProjectileAPI) itproj.next();

//Did this projectile do damage to an Entity?
if(proj.didDamage() && proj.getDamageTarget() != null)
{
//Get the entity hit
entity = (CombatEntityAPI) proj.getDamageTarget();

// Check to make sure we've hit a ship, and if so, for a shield hit; if we hit a ship's shield or a non-ship, stop!
if(entity instanceof ShipAPI && entity.getShield() == null || entity instanceof ShipAPI && !entity.getShield().isOn() || entity instanceof ShipAPI && !entity.getShield().isWithinArc(proj.getLocation()))
{
spec = proj.getProjectileSpecId();
if(critSucessful(proj))
{
//If Shuttle, stop right now!
if(((ShipAPI) entity).isShuttlePod())
{
continue;
}
//Random damage from crits
amount = MathUtils.getRandomNumberInRange(2f,5f);

float damage = (proj.getDamageAmount() * amount);
float emp = (proj.getEmpAmount() * amount);

Vector2f projVel = proj.getVelocity();
Vector2f projPos = proj.getLocation();

engine.applyDamage(entity,
projPos, damage, proj.getDamageType(),
emp, true, true, proj.getSource());


engine.addFloatingText(projPos, "CRIT!", Math.min(Math.max(20f,damage/200f),50f), Color.RED, entity, 0f, 0f);
//Create particle bursts only for big crits
if(damage > 1500f)
{
for(int i = 0; i < 30; i++)
{
//Invert the vector and randomize a little bit
float randX = MathUtils.getRandomNumberInRange(-60f,60f);
float randY = MathUtils.getRandomNumberInRange(-60f,60f);
Vector2f newVel = new Vector2f (randX - projVel.x / 3f, randY - projVel.y / 3f);
int randYel = (int) MathUtils.getRandomNumberInRange(64f,200f);
engine.addHitParticle(projPos, newVel, MathUtils.getRandomNumberInRange(5f,8f), 1f, MathUtils.getRandomNumberInRange(3f,7f), new Color(255,randYel,0,127));
}
}

}//IF    
}//IF
}//FOR
}//FOR
}//advance

public boolean critSucessful(DamagingProjectileAPI damagingproj)
{
Random r = new Random();
WeaponAPI weapon = damagingproj.getWeapon();
float MAXCritChance = 0.05f; // change this value for hull mods for example, original value is 5%
float crewMaxXP = 0f;
//Safety code, catches null pointer exception
if(weapon == null || weapon.getDamageType() == null) return false;
//If the weapon can't crit, it stops here
if(weapon.getDamageType().equals(DamageType.FRAGMENTATION) ||
weapon.getDamageType().equals(DamageType.ENERGY) ||
weapon.getDamageType().equals(DamageType.OTHER))
{
return false;  //No crit for u!
}

//Where did this shot come from?
ShipAPI ship = damagingproj.getSource();
float weaponRoF = weapon.getDerivedStats().getRoF() * 4f;//Balance here; crits should be really rare for high-RoF weapons

StatMod crewBonus = ship.getMutableStats().getAutofireAimAccuracy().getFlatStatMod("crew skill bonus");
// Green crew don't grant a bonus
if (crewBonus != null)
{
crewMaxXP = crewBonus.getValue(); //goes from 0f to 1f - thanks Alex and Lazy for the help
}

//Get our random number
float CritChance = r.nextFloat();

//Crit chance never goes above the max / min values here.
if(CritChance <= Math.min(Math.max((MAXCritChance * crewMaxXP) / weaponRoF,0.001f),0.05f))
{

return true;  //We got a crit, send it back!
} else {
return false;  //Failed...
}
}//critSucessful
}//MAIN CLASS
[close]
Title: Re: The Radioactive Code Dump
Post by: Thule on August 03, 2013, 01:44:07 AM
The Freeze Ray.  Slows down Entities that are hit.  With enough of them, you can immobilize anything. 

Never see a pesky Frigate run away again!  Muahahahahaha... er, I mean, it works, and stuff.  Can't wait to release my mod now, these are just a few of the toys ;)

Spoiler
Code
package data.scripts;

import java.awt.Color;

import org.lwjgl.util.vector.Vector2f;

import com.fs.starfarer.api.combat.BeamAPI;
import com.fs.starfarer.api.combat.BeamEffectPlugin;
import com.fs.starfarer.api.combat.CombatEngineAPI;
import com.fs.starfarer.api.combat.CombatEntityAPI;
import com.fs.starfarer.api.combat.ShipAPI;

public class FreezeRayEffect implements BeamEffectPlugin {
public void advance(float amount, CombatEngineAPI engine, BeamAPI beam) {
CombatEntityAPI target = beam.getDamageTarget();
//Do we have a valid Entity to effect?
if (target != null)
{
//Yes!  Is it in range, and the beam's on?
if (beam.getBrightness() >= 1f)
{
//Get the velocity, and drop it- bigger ships drop a lot less than small ones.
//This might not be balanced for your mod.  The accumulative change in velocity per-frame is really huge!
Vector2f targVel = target.getVelocity();
if(target instanceof ShipAPI)
{
ShipAPI ship = (ShipAPI) target;
if(ship.isDrone())
{
targVel.x *= 0.75f;
targVel.y *= 0.75f;
} else if (ship.isFighter())
{
targVel.x *= 0.95f;
targVel.y *= 0.95f;
} else if (ship.isFrigate())
{
targVel.x *= 0.97f;
targVel.y *= 0.97f;
} else if (ship.isDestroyer())
{
targVel.x *= 0.99f;
targVel.y *= 0.99f;
} else if (ship.isCruiser())
{
targVel.x *= 0.995f;
targVel.y *= 0.995f;
} else if (ship.isCapital())
{
targVel.x *= 0.999f;
targVel.y *= 0.999f;
}
} else {
//If not a ship, slow down a lot.  Instant slowdown for bombs, takes just a few frames for most missiles.
targVel.x *= 0.75f;
targVel.y *= 0.75f;
}
}
}
}
}
[close]

Xeno, may i use this for tl? modified of course ^^
Title: Re: The Radioactive Code Dump
Post by: xenoargh on August 07, 2013, 02:28:25 PM
Absolutely, that's why it's posted here :)
Title: Re: The Radioactive Code Dump
Post by: xenoargh on August 22, 2013, 08:50:11 PM
Updated the Critical Hit code.  Thanks to Silentstormpt for pointing out some things that could still be improved :)
Title: Re: The Radioactive Code Dump
Post by: Okim on August 22, 2013, 09:58:15 PM
I might use the 'Freese Beam' if it`s author doesn`t mind.
Title: Re: The Radioactive Code Dump
Post by: xenoargh on August 22, 2013, 10:03:11 PM
Haven't had time to clean it up enough for posting here, but I'm pretty sure it's non-buggy and efficient, so feel free :)
Title: Re: The Radioactive Code Dump
Post by: Gotcha! on August 31, 2013, 09:51:29 AM
I've got a stupid question. How would one attach the critical hit system to a weapon? :-[
Or does this script enable critical hits for every weapon in the mod?

Edit: After testing I see that this script gives all weapons this ability. Would it be possible to limit it to a specific weapon? :)
Title: Re: The Radioactive Code Dump
Post by: silentstormpt on August 31, 2013, 10:48:08 AM
I've got a stupid question. How would one attach the critical hit system to a weapon? :-[
Or does this script enable critical hits for every weapon in the mod?

Edit: After testing I see that this script gives all weapons this ability. Would it be possible to limit it to a specific weapon? :)

Yes and no, it does work on every weapon except for beam weapons, it also only affects High Explosive and Energy damage types. As for the limit for a weapon, might as well make a OnHit script, its alot less CPU intensive.
Title: Re: The Radioactive Code Dump
Post by: Sproginator on September 01, 2013, 05:47:32 AM
Very interesting stockpile you all have here. I'll be keeping an eye on this for when .6 comes out :)
Title: Re: The Radioactive Code Dump
Post by: Gotcha! on September 18, 2013, 03:55:04 PM
Would anyone know how to properly implement the Heat Special Projectile?
Spoiler
I've added this line to one of my projectiles:
Code
"onHitEffect":"data.scripts.plugins.HiiHeatCannonEffect",

But regardless of adding that line I get the following error:

Code
29903 [Thread-6] ERROR com.fs.starfarer.combat.D  - java.lang.RuntimeException: Error compiling [data.scripts.plugins.HiiHeatCannonEffect]
java.lang.RuntimeException: Error compiling [data.scripts.plugins.HiiHeatCannonEffect]
at com.fs.starfarer.loading.scripts.ScriptStore$1.run(Unknown Source)
at java.lang.Thread.run(Thread.java:619)
Caused by: java.lang.ClassNotFoundException: Parsing compilation unit "com.fs.starfarer.loading.A$1@1f217ec"
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(ClassLoader.java:307)
at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
... 2 more
Caused by: org.codehaus.commons.compiler.CompileException: Source file "data/scripts/plugins/HiiHeatCannonEffect.java" does not declare class "data.scripts.plugins.HiiHeatCannonEffect"
at org.codehaus.janino.JavaSourceIClassLoader.findIClass(JavaSourceIClassLoader.java:165)
... 7 more

The contents of HiiHeatCannonEffect.java:

Code
package data.scripts;

import java.awt.Color;

import org.lwjgl.util.vector.Vector2f;
import com.fs.starfarer.api.combat.CombatEngineAPI;
import com.fs.starfarer.api.combat.CombatEntityAPI;
import com.fs.starfarer.api.combat.WeaponAPI;
import com.fs.starfarer.api.combat.DamageType;
import com.fs.starfarer.api.combat.DamagingProjectileAPI;
import com.fs.starfarer.api.combat.OnHitEffectPlugin;
import com.fs.starfarer.api.combat.ShipAPI;

import org.lazywizard.lazylib.CollisionUtils;
import org.lazywizard.lazylib.MathUtils;

public class HiiHeatCannonEffect implements OnHitEffectPlugin {

public void onHit(DamagingProjectileAPI projectile, CombatEntityAPI target,
 Vector2f point, boolean shieldHit, CombatEngineAPI engine) {
if (target instanceof ShipAPI) {
float emp = projectile.getEmpAmount() * 0.1f;
float dam = projectile.getDamageAmount() * 0.1f;
WeaponAPI weapon = projectile.getWeapon();
DamageType damType = weapon.getDamageType();
int heatMe = (int) Math.max(6f,dam / 150f);

Vector2f targLoc = target.getLocation();
for(int i = 0; i < heatMe; i++)
{
//Gets a point far, far away and uses it as our ray-test point.  Long distances work better than shorter ones.
Vector2f cloneLoc = MathUtils.getRandomPointOnCircumference(targLoc, 1000000f);

//Try to get a valid collision point between our explosion's point source and the Entity.
Vector2f colPoint = CollisionUtils.getCollisionPoint(cloneLoc, targLoc, target);
//If we can't get a good collision point, use the center of the target Entity.  This is potentially a balance issue (hits all going to one armor cell are pretty OP lol), but this case mainly covers little teeny drones and suchlike that should be registering hits from giant explosions nearby, but often don't, for whatever reason.  Bigger things rarely fail, so it usually works out.
if(colPoint == null) colPoint = targLoc;
if(colPoint != null)//Must check this, getCollisionPoint returns null fairly frequently and that's a wrap
{
engine.applyDamage(
target, //enemy Entity
colPoint, //Our 2D vector to the exact world-position of the collision
dam, //DPS modified by the damage multiplier
damType, //Using the damage type here, so that Kinetic / Explosive / Fragmentation AOE works.
emp, //EMP (if any)
false, //Does not bypass shields.
false, //Does not do Soft Flux damage (unless you want it to for some strange reason)
projectile.getSource()  //Who owns this projectile?
);
}
}  
}
}
}
[close]

(Solved)
Title: Re: The Radioactive Code Dump
Post by: LazyWizard on September 18, 2013, 04:24:29 PM
Would anyone know how to properly implement the Heat Special Projectile?

You need to change
Code
package data.scripts;
to
Code
package data.scripts.plugins;
in your java file.
Title: Re: The Radioactive Code Dump
Post by: Gotcha! on September 18, 2013, 04:28:43 PM
That's.... not the first time I made that mistake. I hope it'll be the last. -_-'

Thanks again! <3

Nearing completion.
Title: Re: The Radioactive Code Dump
Post by: Uomoz on September 30, 2013, 05:52:11 AM
Tried implementing the crit plugin in UsS, null pointer!

24312 [Thread-6] ERROR com.fs.starfarer.combat.D  - java.lang.NullPointerException
java.lang.NullPointerException
   at data.scripts.plugins.Crit.critSucessfull(Crit.java:191)
   at data.scripts.plugins.Crit.advance(Crit.java:172)
   at com.fs.starfarer.title.Object.K$Oo.o00000(Unknown Source)
   at com.fs.starfarer.combat.super.new.o00000(Unknown Source)
   at com.fs.starfarer.combat.CombatEngine.advance(Unknown Source)
   at com.fs.starfarer.title.B.super(Unknown Source)
   at com.fs.starfarer.new.øÒÒ000(Unknown Source)
   at com.fs.oOOO.super.new(Unknown Source)
   at com.fs.starfarer.combat.D.o00000(Unknown Source)
   at com.fs.starfarer.StarfarerLauncher$2.run(Unknown Source)
   at java.lang.Thread.run(Thread.java:619)
Title: Re: The Radioactive Code Dump
Post by: silentstormpt on September 30, 2013, 07:37:04 AM
Dont forget the new version of Lazylib decrypted some methods used on that crit System, ill need to check what needs to be changed.

EDIT: Added a null check on the projectile, its not really a "fix" but more of a bandage, i need to upgrade the script anyway and remove/add other options besides a normal crit.
EDIT2: Might change the script to be used as a OnHit so its not so heavy in resources on huge fights, this means it needs to be added to every weapon tho.
Title: Re: The Radioactive Code Dump
Post by: Gotcha! on September 30, 2013, 09:51:58 AM
Dont forget the new version of Lazylib decrypted some methods used on that crit System, ill need to check what needs to be changed.

Might change the script to be used as a OnHit so its not so heavy in resources on huge fights, this means it needs to be added to every weapon tho.

Please do!
Spoiler
(http://fc07.deviantart.net/fs71/f/2010/086/2/2/Puppy_Eyes_by_AinaShadox.jpg)
[close]
Title: Re: The Radioactive Code Dump
Post by: dmaiski on October 17, 2013, 07:00:50 AM
i never thought id be here dumping nuclear waste...

a hemogeny inventor named alex once made an ion cannon, this cannon through unknown means fell into the vile tentacles of the BISO reaserch department... bad things happened to that poor ion cannon, unspeakable things! it is now fitted onto many BISO warships as standard for getting rid of pesky hemogeny ships

i present the BISO lightningstorm OnHitEffect script
made it more random, previous was only making a diagonal slash
made it confine itself to the area of the shield it hit (looks epic)
some more minor tweaks to make it work beter(i hope), and more balanced vs shields
final version with annotations to explain what is going on
Spoiler
Code: java
package data.scripts.onhiteffects;

import java.awt.Color;
import java.util.Random;

import com.fs.starfarer.api.combat.*;
import org.lwjgl.util.vector.Vector2f;

public class Lightningstorm implements OnHitEffectPlugin {


    public void onHit(DamagingProjectileAPI projectile, CombatEntityAPI target,
                      Vector2f point, boolean shieldHit, CombatEngineAPI engine) {

        if (target instanceof ShipAPI && !shieldHit) {   //only if hits hull
            do {
                double rnd1 = .5 - Math.random();      //see below for details
                float rn1 = (float)rnd1;
                double rnd2 = .5 - Math.random();
                float rn2 = (float)rnd2;    // +(float)rnd*2 to create a ring outside of ship
                float px = target.getLocation().getX();   //get center of cloud
                float py = target.getLocation().getY();   //get center of cloud
                float sr = target.getCollisionRadius();   //replace to change to manual area or effect
                Vector2f pa = new Vector2f(px + sr*rn1, py + sr*rn2);   //replace "sr" to change to manual area or effect
                float emp = projectile.getEmpAmount();
                float dam = projectile.getDamageAmount();
                engine.spawnEmpArc(projectile.getSource(), pa, target, target,
                    DamageType.ENERGY,
                    dam,
                    emp, // emp
                    sr, // max range prevent bolds hitting wrong side of the ship
                    "tachyon_lance_emp_impact",
                    20f, // thickness
                    new Color(255,10,15,255),
                    new Color(255,255,255,255)
                );
            }while ((float) Math.random() > 0.6f && target instanceof ShipAPI); //duration
        }
        else if (target instanceof ShipAPI && shieldHit) {     //only if hits shield
            do {
                double rnd1 = .5 - Math.random(); //generate a random double with -vs component, range -.5 to +.5
                float rn1 = (float)rnd1;          // convert to float
                double rnd2 = .5 - Math.random();
                float rn2 = (float)rnd2;    // +(float)rnd*2 to create a ring (implemented later in a better way)
                float px = target.getLocation().getX();   //get center of cloud
                float py = target.getLocation().getY();
                float hx = point.getX();   //get center of cloud
                float hy = point.getY();   //get center of cloud
                float sr = target.getCollisionRadius();   //replace to change to manual area or effect
                Vector2f pa = new Vector2f(hx + 1/2*(hx-px) + sr*rn1, hy + 1/2*(hy-py) + sr*rn2);   //cause bolt spawn center to be just outside of ship radius,
                //at the point where it was hit, within a radius bounded by max range(5/3*sr) and (1/2*sr) around point
                float emp = projectile.getEmpAmount();
                float dam = projectile.getDamageAmount();
                engine.spawnEmpArc(projectile.getSource(), pa, target, target,
                        DamageType.ENERGY,
                        2/4*dam, //make it less shield destroying
                        emp, // emp
                        5/3*sr, // max range make bolts hit hull closest
                        "tachyon_lance_emp_impact",
                        20f, // thickness
                        new Color(255,10,15,255),
                        new Color(255,255,255,255)
                );
                if ((float) Math.random() > 0.85f) { //generate additional bolts around ship 15% chance per loop
                    double rnd1b = .25 - .5*Math.random();
                    float rn1b = (float)rnd1b+(float)rnd1b*5;
                    double rnd2b = .25 - .5*Math.random();
                    float rn2b = (float)rnd2b+(float)rnd2b*5;    // +(float)rnd*2 to create a ring outside of ship
                    float pxb = target.getLocation().getX();   //get center of cloud
                    float pyb = target.getLocation().getY();   //get center of cloud
                    Vector2f pab = new Vector2f(pxb + sr*rn1b, pyb + sr*rn2b);   //replace "sr" to change to manual area or effect
                    engine.spawnEmpArc(projectile.getSource(), pab, target, target,
                            DamageType.ENERGY,
                            dam,
                            emp, // emp
                            10000f, // max range
                            "tachyon_lance_emp_impact",
                            20f, // thickness
                            new Color(255,10,15,255),
                            new Color(255,255,255,255)
                    );
                }

            }while ((float) Math.random() > 0.6f && target instanceof ShipAPI); //duration
        }

    }
}
//less scarry math
//Vector2f pa = new Vector2f(px + sr*rn1, py + sr*rn2);   //replace "sr" to change to manual area or effect
//creates a sphere of lightning around target ship

// +(float)rnd*2 to create a ring (implemented later in a better way):
//double rndx1 = rnd1/Math.abs(rnd1)*sr; //to generate exclusion zone of size sr


//projectile.getWeapon().getLocation()
// for arc from gun
//projectile.getDamageTarget().getLocation()
//long form for "point" Vector2f
[close]

Spoiler
(http://i.imgur.com/BxKgfuh.png?1)(http://i.imgur.com/LDfOvEl.png?1)
[close]
(http://i.imgur.com/taXxFag.png?1)
it is complete!

old
Spoiler
(http://i.imgur.com/nKdtNPi.png?1)(http://i.imgur.com/Fy4obhd.png?1)
[close]

things to do(ye gads, the workaround fairy came):
make it spawn purely visual bolts back to impact point when hitting shield
Title: Re: The Radioactive Code Dump
Post by: dmaiski on October 18, 2013, 03:23:25 PM
the BISO reaserch head presents, new MIRV code (with more variables, and it can do more stuff)

my curent working code(its beutifull):
Spoiler
Code: java
if (MathUtils.getDistance(
                add(target.getLocation(), target.getVelocity(), null),              //blow up the enemy ship
                add(missile.getLocation(), multV2f(missile.getVelocity(),3), null))
                < 200
                && MathUtils.getDistance(
                missile.getLocation(),                     //don't blow up your own ship
                missile.getSource().getLocation())
                >missile.getSource().getCollisionRadius()+5)
        {
            CombatEngine engine = CombatEngine.getInstance();          //engine

            String MBRC_p = "MBRC2";      //dummy weapon

            {
                    {
                        int counts = 1;
                        do {
                            float angRAND= (float) ((25*(.5-Math.random())));     //angle of spread
                            float velRAND= (float) (1+.5*(.5-Math.random()));      //variance of projectile speeds
                            float splashVEL = 255f*velRAND;    //speed of bullets launched
                            float misFFACE = missile.getFacing()-angRAND;
                            float x = (float) (splashVEL*Math.cos(Math.toRadians(misFFACE)));
                            float y = (float) (splashVEL*Math.sin(Math.toRadians(misFFACE)));
                            Vector2f vecFIRE = new Vector2f(x,y);
                            engine.spawnProjectile(null, null,MBRC_p,
                                    missile.getLocation(),          //Vector2f firing point
                                    misFFACE,            //float   angle of spread
                                    add(missile.getVelocity(), vecFIRE, null)

                                   //multV2f(multV2f(missile.getVelocity(),1),velRAND)           //Vector2f  aditional velocity
                                    //add(multV2f(missile.getVelocity(), 1), multRanV2f(missile.getVelocity(),1,.5f),null)
                            );
                            counts++;
                        }while (counts<30);         //30x pew pew

                        //engine.removeEntity(missile); //make missile go poof
                    }
                //lightning
                   // float emp = missile.getEmpAmount();
                    //float dam = missile.getDamageAmount();

                    //engine.spawnEmpArc(missile.getSource(), missile.getLocation(), target, target,
                           // DamageType.ENERGY,dam/4, emp/4,
                            //10000f, "tachyon_lance_emp_impact",20f, new Color(255,10,15,255),new Color(255,100,100,255));
                }
            //to stop missile shooting again
            //engine.removeEntity(missile);   //make missile go poof
            missile.flameOut();               //make missile flame out
            return;

        }

you also need these:
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;
    }
    //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;
    }
    //more code
    public static Vector2f multRanV2f(Vector2f Vector1, float Multiplier, float Random)
    {
        double rxd = .5 - Math.random(); //generate a random double with -vs component, range -.5 to +.5
        float rx = (float)rxd*2*Random;          // convert to float
        double ryd = .5 - Math.random();
        float ry = (float)ryd*2*Random;
        float v1x = Vector1.getX()*Multiplier*rx;
        float v1y = Vector1.getY()*Multiplier*ry;
        Vector2f v1end = new Vector2f(v1x, v1y);
        return v1end;
    }
}
[close]




old code made during a drunken stupor, more versetile, insanely complex
Spoiler
Spoiler
Code: java
//create variables
        //capitalized can be changed

        //insert targeter code here (anything goes, you may need to change it so T gives tx/ty a float value
        //see at bottom for possible code
        CombatEntityAPI T = target;//end point of " " (enemy api/point in space/ect) for targeter
        MissileAPI P = missile;    //start point of " " for weapon
        float px = P.getLocation().getX();   //get location og p (for center of dakakakaka)
        float py = P.getLocation().getY();
        float mvx = missile.getVelocity().getX();
        float mvy = missile.getVelocity().getY();
        float tx = px+mvx;   //get location of t (for center of pew pew)
        float ty = py+mvy;
        double rxd = .5 - Math.random(); //generate a random double with -vs component, range -.5 to +.5
        float rx = (float)rxd*2;          // convert to float
        double ryd = .5 - Math.random();
        float ry = (float)ryd*2;
        double rpd = Math.random(); //+ve random
        float rp = (float)rpd;
        float Tvar = 50f; //radius of t (sets mirv spread (angular))
        float Pvar = 1f;   //variable for vectors can use random float
        double prany = py/Math.abs(py)*Pvar;
        double pranx = px/Math.abs(px)*Pvar;
        float prx = (float)pranx;
        float pry = (float)prany;
        float Rvar = 5f; //how much the velocities of projectiles can vary
        double rvd = .5 - Math.random(); //generate a random double with -vs component, range -.5 to +.5
        float rv = (float)rxd*Rvar;



        //math

        float Fx = (tx * rx * Tvar);  //create randomized point field to shoot at
        float Fy = (ty * ry * Tvar);
        float Vx = (Fx * px * prx);         //create vectors for pewpews to follow
        float Vy = (Fy * py * pry);
        double Smoothv = (Math.sqrt(((tx-px)*(tx-px))+((ty-py)*(ty-py)))/Math.sqrt((Vx*Vx)+(Vy*Vy)))*rv;   //smoothes out ragged shot
        float Sv = (float) Smoothv;
        Vector2f Pjv = new Vector2f(Vx*Sv, Vy*Sv);   //make the actual vector

        //projectile spawn code here

        engine.removeEntity(missile); //make missile go poof
    }


//for canister of flashcetes behaviour
//CombatEntityAPI T = target;//end point of " " (enemy api/point in space/ect) for targeter
//MissileAPI P = missile;    //start point of " " for weapon
//float px = P.getLocation().getX();   //get location og p (for center of dakakakaka)
//float py = P.getLocation().getY();
//float mvx = missile.getVelocity().getX();
//float mvy = missile.getVelocity().getY();
//float tx = px+mvx;   //get location of t (for center of pew pew)
//float ty = py+mvy;
[close]

makes vectors to apply velocities to projectiles, and can pretymuch make any MIRV/mine/anything behaviour you want
Spoiler
Code
{
        //create variables
        //capitalized can be changed

        //insert targeter code here (anything goes, you may need to change it so T gives tx/ty a float value
        //see at bottom for possible code
        CombatEntityAPI T = target;//end point of " " (enemy api/point in space/ect) for targeter
        MissileAPI P = missile;    //start point of " " for weapon
        float px = P.getLocation().getX();   //get location og p (for center of dakakakaka)
        float py = P.getLocation().getY();
        //float mvx = missile.getVelocity().getX();
        //float mvy = missile.getVelocity().getY();
        float tx = T.getLocation().getX();   //get location of t (for center of pew pew)
        float ty = T.getLocation().getY();
        double rxd = .5 - Math.random(); //generate a random double with -vs component, range -.5 to +.5
        float rx = (float)rxd*2;          // convert to float
        double ryd = .5 - Math.random();
        float ry = (float)ryd*2;
        double rpd = Math.random(); //+ve random
        float rp = (float)rpd;
        float Tvar = 50f; //radius of t (sets mirv spread (angular))
        float Pvar = 1f;   //variable for vectors can use random float
        double prany = py/Math.abs(py)*Pvar;
        double pranx = px/Math.abs(px)*Pvar;
        float prx = (float)pranx;
        float pry = (float)prany;
        float Rvar = 5f; //how much the velocities of projectiles can vary
        double rvd = .5 - Math.random(); //generate random velocity multiplier for smooth function (to arr roughness back in
        float rv = (float)rvd*Rvar;



        //math

        float Fx = (tx * rx * Tvar);  //create randomized point field to shoot at
        float Fy = (ty * ry * Tvar);
        float Vx = (Fx * px * prx);         //create vectors for pewpews to follow
        float Vy = (Fy * py * pry);
        double Smoothv = (Math.sqrt(((tx-px)*(tx-px))+((ty-py)*(ty-py)))/Math.sqrt((Vx*Vx)+(Vy*Vy)))*rv;   //smoothes out ragged shot
        float Sv = (float) Smoothv;
        Vector2f Pjv = new Vector2f(Vx*Sv, Vy*Sv);   //make the actual vector

        engine.spawnProjectile(null, null,
               SBKR1,
                missile.getLocation(), 0,Pjv);

        engine.removeEntity(missile); //make missile go poof
    }
[close]
in theory this crates an skbr shell to fly at the enemy ship
[close]
(http://i.imgur.com/4b0HGmr.png?1)
big picture
Spoiler
Spoiler
(http://i.imgur.com/m87qNLE.png?1)
[close]


(http://i.imgur.com/bGxMNMG.png?1)
sexy fireworks

(http://i.imgur.com/Hf4N9Rz.png?1)
accidentaly made awsome
[close]
Title: Re: The Radioactive Code Dump
Post by: dmaiski on October 22, 2013, 02:18:47 PM
DRONE missiles
Spoiler
(http://i.imgur.com/3w9ODDJ.png?1)
[close]
(http://i.imgur.com/mV9fFGj.png?1)

stays at a set distance and blasts the enemy ships appart
who needs fighters anyways?


Spoiler
Code: java
int timer = 0;  //needs to be outside of advance() to work as a timer
int timer2 = (int) (600*Math.random());  //random start point for strafing
public void advance(float amount)
        {
        timer++;

        if (MathUtils.getDistance(
                add(target.getLocation(), target.getVelocity(), null),              //blow up the enemy ship
                add(missile.getLocation(), multV2f(missile.getVelocity(),3), null))
                < 600
                && MathUtils.getDistance(
                missile.getLocation(),                     //don't blow up your own ship
                missile.getSource().getLocation())
                >missile.getSource().getCollisionRadius()+5
                && timer==1
                )
        {
            timer++;
                CombatEngine engine = CombatEngine.getInstance();          //engine

                String MBRC_p = "MBRC2";      //dummy weapon

                {

                    {
                        int counts = 1;
                        do {
                            float angRAND= (float) ((25*(.5-Math.random())));     //angle of spread
                            float velRAND= (float) (1+.5*(.5-Math.random()));      //variance of projectile speeds
                            float splashVEL = 255f*velRAND;    //speed of bullets launched
                            float misFFACE = missile.getFacing()-angRAND;
                            float x = (float) (splashVEL*Math.cos(Math.toRadians(misFFACE)));
                            float y = (float) (splashVEL*Math.sin(Math.toRadians(misFFACE)));
                            Vector2f vecFIRE = new Vector2f(x,y);
                            engine.spawnProjectile(null, null,MBRC_p,
                                    missile.getLocation(),          //Vector2f firing point
                                    misFFACE,            //float   angle of spread
                                    add(missile.getVelocity(), vecFIRE, null)

                                    //multV2f(multV2f(missile.getVelocity(),1),velRAND)           //Vector2f  aditional velocity
                                    //add(multV2f(missile.getVelocity(), 1), multRanV2f(missile.getVelocity(),1,.5f),null)
                            );
                            counts++;
                        }while (counts<2);         //2x pew pew


                        //engine.removeEntity(missile); //make missile go poof
                    }
                    //lightning
                    // float emp = missile.getEmpAmount();
                    //float dam = missile.getDamageAmount();

                    //engine.spawnEmpArc(missile.getSource(), missile.getLocation(), target, target,
                    // DamageType.ENERGY,dam/4, emp/4,
                    //10000f, "tachyon_lance_emp_impact",20f, new Color(255,10,15,255),new Color(255,100,100,255));
                }
                //to stop missile shooting again
                //engine.removeEntity(missile);   //make missile go poof

                //missile.flameOut();               //make missile flame out
                return;
        }
        if (timer>40)
        {timer=0;}
        
        //missile pathing code
        if (Math.abs(angularDistance) < 100      //get in close
                && MathUtils.getDistance(missile.getLocation(), target.getLocation())
                >320+100/target.getCollisionRadius()*target.getCollisionRadius())
        {
            missile.giveCommand(ShipCommand.ACCELERATE);
        }
        if (Math.abs(angularDistance) < 100     //keep distance
                && MathUtils.getDistance(missile.getLocation(), target.getLocation())
                <280+100/target.getCollisionRadius()*target.getCollisionRadius())
        {
            missile.giveCommand(ShipCommand.ACCELERATE_BACKWARDS);
        }

        if (Math.abs(angularDistance) < 100   //strafe
                && MathUtils.getDistance(missile.getLocation(), target.getLocation())<400+target.getCollisionRadius())
        {
            missile.giveCommand(timer2 > 300 ? ShipCommand.STRAFE_LEFT : ShipCommand.STRAFE_RIGHT);
        }
        timer2++;
        if (timer2>600)
        {
            timer2=0;
        }
        }
[close]
Title: Re: The Radioactive Code Dump
Post by: dmaiski on October 22, 2013, 05:31:06 PM
BISO fleet have been known to leave quite a mess behind, mainly in the shattered hulls of the enemy, but also through the vast numbers of mines they habitually deploy during battle.

Here, the BISO research department presents their findings on the subject of intelligent munitions, and automated mine dispersal methods:

Spoiler
(http://i.imgur.com/2xtMHjg.png?1)
A1 minelayer deploying minefield

(http://i.imgur.com/KgztMbC.png?1)
10 seconds later, minefield is forming, Cluster procesing is engaged to produce an even spread

(http://i.imgur.com/6MkXhWp.png?1)
test subject 129331 enters minefield, aproximate range from deployment zone, 3km

(http://i.imgur.com/0l6BpiR.png?1)
o gads the evilbastards!!! they teleported BEHIND my ship and deployed dozens of mines so I cant escape!!!
trully feindish!

btw, for those poor of vision, those pale yelow dots spread evenly across the map, they aren't stars i can tell you that much...
[close]
images, lots of them


beware, long, lots of code:


new "smart" mines that track enemy ships range to mine to maximize damage
also this code is optimized to use as few cpu cycles as posible, otherwhise verry dense minefields can cause crashes
Spoiler
Code: java
package data.scripts.MissileAI;


import com.fs.starfarer.api.combat.*;

import com.fs.starfarer.combat.CombatEngine;
import org.lazywizard.lazylib.MathUtils;
import org.lazywizard.lazylib.combat.AIUtils;
import org.lwjgl.util.vector.Vector2f;
import static org.lwjgl.util.vector.Vector2f.add;




public class CustomMINEMissileAI implements MissileAIPlugin
{
    // Our missile object
    private final MissileAPI missile;

    public CustomMINEMissileAI(MissileAPI missile)
    {
        this.missile = missile;
    }


    float range1 = 0;
    float range2 = 0;
    int trigger = 0;
    int deathcode = 0;
    double minetimer = 0;
    ShipAPI enemy=null;

    @Override
    public void advance(float amount)
    {
        CombatEngine engine = CombatEngine.getInstance();          //engine
        // Apparently commands still work while fizzling
        if (missile.isFading() || missile.isFizzling())
        {
            return;
        }


        minetimer++;




        //intelligent mine distribution code
        MissileAPI near = AIUtils.getNearestMissile(missile);

        if (near != null                               //check if there are any nearby mines
                && near.getProjectileSpecId().equals(missile.getProjectileSpecId())
                && MathUtils.getDistance(near, missile) < 400)
        {
            //if there are move away from them
            float angularDistance = getAngleDifference(
                    missile.getFacing(), MathUtils.getAngle(missile.getLocation(),
                    near.getLocation()));
            if (Math.abs(angularDistance) < 175)
            {
                missile.giveCommand(angularDistance < 0f
                        ? ShipCommand.TURN_LEFT : ShipCommand.TURN_RIGHT);
            }
            if (Math.abs(angularDistance) > 135)
            {
                missile.giveCommand(ShipCommand.ACCELERATE);
            }
            if (Math.abs(angularDistance) < 45)
            {
                missile.giveCommand(ShipCommand.ACCELERATE_BACKWARDS);
            }
        }
        else
        {
            missile.giveCommand(ShipCommand.DECELERATE);              //mine dose not move
        }

        //enemy tracking code
        if (Math.tanh(minetimer)>.2) //run once every 3 frames to prevent fun stuff
        {
            if (enemy==null && AIUtils.getNearestEnemy(missile)!=null && AIUtils.getNearestEnemy(missile).isAlive()
                    )
            {
                float targetfinder = MathUtils.getDistance(AIUtils.getNearestEnemy(missile).getLocation(), missile.getLocation())
                        -AIUtils.getNearestEnemy(missile).getCollisionRadius();
                if (targetfinder < 300)
                {
                    enemy=AIUtils.getNearestEnemy(missile);                //remember that enemy
                }
                if (targetfinder > 300)
                {
                    return;
                }

            }


            if (enemy != null)  //check if there is an enemy all these need it
            {
                if (enemy!=AIUtils.getNearestEnemy(missile))
                {
                    deathcode++;      //if a second enemy comes close
                    trigger++;
                }

                if (!enemy.isAlive() |  enemy.isHulk())
                {
                    enemy=null;      //if tracking enemy is dead, don't explode
                }

                if (deathcode==0 && range1==0
                        && MathUtils.getDistance(                               //find out if there are enemy in range
                        enemy.getLocation(),
                        missile.getLocation())-enemy.getCollisionRadius()
                        < 200)
                {
                    range1 = MathUtils.getDistance(                               //find get the range to target
                            enemy.getLocation(),
                            missile.getLocation())-enemy.getCollisionRadius();
                }
            }



            if (deathcode==0 && range1!=0)     //for all grouped in here
            {
                if (range1<100) //when enemy is close enough go boom
                {
                    trigger++;       //make it go boom
                    deathcode++;     //stop tracking
                }


                {
                    range2 = MathUtils.getDistance(enemy.getLocation(),                               //find get the range to target now, 1 tick later
                            missile.getLocation())-enemy.getCollisionRadius();
                }

                if (range1>range2)
                {
                    float range3 = MathUtils.getDistance(                               //find get the range to target now, 1 tick later
                            Vector2f.add(enemy.getLocation(), multV2f(enemy.getVelocity(), .5f), null), //analyze its velocity
                            missile.getLocation())-enemy.getCollisionRadius();
                    if (range1<range3)
                    {
                        trigger++;
                        deathcode++;
                    }
                }

                if (range1>range2)
                {
                    range1=range2;               //store new target range, if it got closer
                }



                if (range1<range2)
                {
                    trigger++;       //make it go boom
                    deathcode++;     //stop tracking
                }

                if (missile.getMaxHitpoints()*0.9>missile.getHitpoints())
                {
                    trigger++;          //if damaged below 90%hp while in range, detonate
                    deathcode++;
                }
            }



            //explosive code
            if (trigger != 0
                    &&MathUtils.getDistance(
                    missile.getLocation(),                     //don't blow up your own ships
                    missile.getSource().getLocation())
                    >AIUtils.getNearestAlly(missile).getCollisionRadius()+210)
            {


                String MBRC_p = "MBRC2";      //dummy weapon

                {
                    {
                        int counts = 1;
                        do {
                            float angRAND= (float) ((360*(.5-Math.random())));     //angle of spread
                            float velRAND= (float) (.6+.5*(.5-Math.random()));      //variance of projectile speeds
                            float splashVEL = 155f*velRAND;    //speed of bullets launched
                            float misFFACE = missile.getFacing()-angRAND;
                            float x = (float) (splashVEL*Math.cos(Math.toRadians(misFFACE)));
                            float y = (float) (splashVEL*Math.sin(Math.toRadians(misFFACE)));
                            Vector2f vecFIRE = new Vector2f(x,y);
                            engine.spawnProjectile(null, null,MBRC_p,
                                    missile.getLocation(),          //Vector2f firing point
                                    misFFACE,            //float   angle of spread
                                    add(missile.getVelocity(), vecFIRE, null)

                                    //multV2f(multV2f(missile.getVelocity(),1),velRAND)           //Vector2f  aditional velocity
                                    //add(multV2f(missile.getVelocity(), 1), multRanV2f(missile.getVelocity(),1,.5f),null)
                            );
                            counts++;
                        }while (counts<100);         //30x pew pew

                        //engine.removeEntity(missile); //make missile go poof
                    }
                    //lightning
                    // float emp = missile.getEmpAmount();
                    //float dam = missile.getDamageAmount();

                    //engine.spawnEmpArc(missile.getSource(), missile.getLocation(), target, target,
                    // DamageType.ENERGY,dam/4, emp/4,
                    //10000f, "tachyon_lance_emp_impact",20f, new Color(255,10,15,255),new Color(255,100,100,255));
                }
                //to stop missile shooting again
                engine.removeEntity(missile);   //make missile go poof
                //missile.flameOut();               //make missile flame out
                return;

            }
        }

    }
    //in next lazylib
    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   scale can not be used in static methods...
    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;
    }
}
[close]


old "dumb" mines that do not track enemy ships range to mine to maximize damage (they still have dispersal code)
Spoiler
Code: java
package data.scripts.MissileAI;

import com.fs.starfarer.api.Global;
import com.fs.starfarer.api.combat.*;

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

import com.fs.starfarer.combat.CombatEngine;
import org.lazywizard.lazylib.MathUtils;
import org.lazywizard.lazylib.combat.AIUtils;
import org.lazywizard.lazylib.combat.CombatUtils;
import org.lwjgl.util.vector.Vector2f;

import static org.lazywizard.lazylib.combat.CombatUtils.getShipsWithinRange;
import static org.lwjgl.util.vector.Vector2f.add;




public class CustomMINEMissileAI implements MissileAIPlugin
{
    // Our missile object
    private final MissileAPI missile;

    public CustomMINEMissileAI(MissileAPI missile)
    {
        this.missile = missile;
    }

    //mine
    @Override
    public void advance(float amount)
    {
        CombatEngine engine = CombatEngine.getInstance();          //engine
        // Apparently commands still work while fizzling
        if (missile.isFading() || missile.isFizzling())
        {
            return;
        }

        if (AIUtils.getNearestEnemy(missile) != null                     //check if there is an enemy
                && MathUtils.getDistance(                               //find out if there are enemy in range
                AIUtils.getNearestEnemy(missile).getLocation(),
                missile.getLocation()
                )< 200+AIUtils.getNearestEnemy(missile).getCollisionRadius()
                && MathUtils.getDistance(
                missile.getLocation(),                     //don't blow up your own ship
                missile.getSource().getLocation())
                >missile.getSource().getCollisionRadius()+5)
        {


            String MBRC_p = "MBRC2";      //dummy weapon

            {
                {
                    int counts = 1;
                    do {
                        float angRAND= (float) ((360*(.5-Math.random())));     //angle of spread
                        float velRAND= (float) (.6+.5*(.5-Math.random()));      //variance of projectile speeds
                        float splashVEL = 155f*velRAND;    //speed of bullets launched
                        float misFFACE = missile.getFacing()-angRAND;
                        float x = (float) (splashVEL*Math.cos(Math.toRadians(misFFACE)));
                        float y = (float) (splashVEL*Math.sin(Math.toRadians(misFFACE)));
                        Vector2f vecFIRE = new Vector2f(x,y);
                        engine.spawnProjectile(null, null,MBRC_p,
                                missile.getLocation(),          //Vector2f firing point
                                misFFACE,            //float   angle of spread
                                add(missile.getVelocity(), vecFIRE, null)

                                //multV2f(multV2f(missile.getVelocity(),1),velRAND)           //Vector2f  aditional velocity
                                //add(multV2f(missile.getVelocity(), 1), multRanV2f(missile.getVelocity(),1,.5f),null)
                        );
                        counts++;
                    }while (counts<100);         //30x pew pew

                    //engine.removeEntity(missile); //make missile go poof
                }
                //lightning
                // float emp = missile.getEmpAmount();
                //float dam = missile.getDamageAmount();

                //engine.spawnEmpArc(missile.getSource(), missile.getLocation(), target, target,
                // DamageType.ENERGY,dam/4, emp/4,
                //10000f, "tachyon_lance_emp_impact",20f, new Color(255,10,15,255),new Color(255,100,100,255));
            }
            //to stop missile shooting again
            engine.removeEntity(missile);   //make missile go poof
            //missile.flameOut();               //make missile flame out
            return;

        }
        MissileAPI near = AIUtils.getNearestMissile(missile);

        if (near != null
                && near.getProjectileSpecId().equals(missile.getProjectileSpecId())
                && MathUtils.getDistance(near, missile) < 400)
        {

            float angularDistance = getAngleDifference(
                    missile.getFacing(), MathUtils.getAngle(missile.getLocation(),
                    near.getLocation()));
            if (Math.abs(angularDistance) < 175)
            {
                missile.giveCommand(angularDistance < 0f
                        ? ShipCommand.TURN_LEFT : ShipCommand.TURN_RIGHT);
            }
            if (Math.abs(angularDistance) > 135)
            {
                missile.giveCommand(ShipCommand.ACCELERATE);
            }
            if (Math.abs(angularDistance) < 45)
            {
                missile.giveCommand(ShipCommand.ACCELERATE_BACKWARDS);
            }
        }
        else
        {
            missile.giveCommand(ShipCommand.DECELERATE);              //mine dose not move under its own power
        }

    }
    //in next lazylib
    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]

all code demonstrated in the aptly named BISO v 0.69 (http://fractalsoftworks.com/forum/index.php?topic=7041.0) post off topic replies there please, and discuss the actual code here
Title: Re: The Radioactive Code Dump
Post by: dmaiski on October 23, 2013, 12:13:12 PM
SCATTER missileAI

BISO engineers are curently developing an artificialy inteligent missile that automaticaly spaces itself away from neighbors

(http://i.imgur.com/ddOc0Qm.png?1)
Spoiler
(http://i.imgur.com/HRijEhH.png?1)
(http://i.imgur.com/oq9lgZZ.png?1)
[close]
Spoiler
Code: java
        //spread code
        float timerVariable =                             //timer variable code//how likley is it to run pathAI
                (((MathUtils.getDistance(missile, target.getLocation())                                                      //9
                        /MathUtils.getDistance(missile.getSource(), target))      //d to target/d from target to launch      //10
                        /(MathUtils.getDistance(missile.getSource(), missile)                                                //1
                        +MathUtils.getDistance(missile.getSource(), target)*.1f     //divided by                             //2
                        /MathUtils.getDistance(missile.getSource(), target))))/10;//d to launch ship/d from target to launch //10
        //returns 1 when launched  ~0 when impact                 //((9/10)/((1+1)/10))=4.5 =.45
        spread= 10+(float)(50*Math.sqrt((double)timerVariable));  //adaptive spread
        //when missile closes spread drops to a minimum o 10% of value at 50, max is ~50/1.5





        MissileAPI near = AIUtils.getNearestMissile(missile);

        if (pathAI>10) {pathAI=0;}
        pathAI++;

        if ((near != null                               //check if there are any nearby mines
                && near.getProjectileSpecId().equals(missile.getProjectileSpecId())
                && MathUtils.getDistance(near, missile) > spread))
        {
            pathAI=1;
        }
        if (near==null)
        {
            pathAI=1;
        }
        if (MathUtils.getDistance(missile, target)-target.getCollisionRadius()<500)
        {
            pathAI=1;
        }


        //spread code

        if (near != null)
        {
            //if there are move away from them

            if (pathAI>5.1)
            {
                float angularDistanceS = getAngleDifference(
                        missile.getFacing(), MathUtils.getAngle(missile.getLocation(),
                        (near.getLocation())));
                if (near.getProjectileSpecId().equals(missile.getProjectileSpecId())
                        && MathUtils.getDistance(near, missile) < spread)
                {

                    if (Math.abs(angularDistanceS) < 45)
                    {
                        missile.giveCommand(angularDistanceS < 0f
                                ? ShipCommand.TURN_LEFT : ShipCommand.TURN_RIGHT);
                        missile.giveCommand(angularDistanceS < 0f
                                ? ShipCommand.STRAFE_LEFT : ShipCommand.STRAFE_RIGHT);
                        missile.giveCommand(ShipCommand.DECELERATE);
                    }


                    if (Math.abs(angularDistanceS) > 45 && Math.abs(angularDistanceS) <90)
                    {
                        missile.giveCommand(angularDistanceS < 0f
                                ? ShipCommand.STRAFE_LEFT : ShipCommand.STRAFE_RIGHT);
                        missile.giveCommand(ShipCommand.ACCELERATE);
                    }
                }
            }
        }
        //normal ai

        if (pathAI<5.1)
        {
            float angularDistance = getAngleDifference(
                    missile.getFacing(), MathUtils.getAngle(missile.getLocation(),
                    (add(target.getLocation(),
                            multV2f(target.getVelocity(),
                                    MathUtils.getDistance(target.getLocation(),
                                            missile.getLocation())/missile.getVelocity().lengthSquared()),
                            null))  ));


            if (Math.abs(angularDistance) > 0.2)
            {
                missile.giveCommand(angularDistance > 0
                        ? ShipCommand.TURN_LEFT : ShipCommand.TURN_RIGHT);

            }

            if (Math.abs(angularDistance) < 100
                    && MathUtils.getDistance(missile, target)-target.getCollisionRadius()>290)
            {
                missile.giveCommand(ShipCommand.ACCELERATE);
            }



            if (MathUtils.getDistance(
                    missile.getLocation(),                     //don't blow up your own ship
                    missile.getSource().getLocation())
                    <missile.getSource().getCollisionRadius()*2)
            {
                missile.giveCommand(ShipCommand.ACCELERATE);

            }
            if (MathUtils.getDistance(missile, target)-target.getCollisionRadius()<300
                    && Math.abs(angularDistance) > 1)
            {
                missile.giveCommand(ShipCommand.DECELERATE);
                if (Math.abs(angularDistance) < 10)
                {
                    deathcode=1;
                }
            }


        }
[close]
as usual any unasigned cout++ values go outside of the method
final code, effective enough
Title: Re: The Radioactive Code Dump
Post by: Cosmitz on October 23, 2013, 12:43:48 PM
To be fair, most of those yell 'flachettes' and less 'missile'.
Title: Re: The Radioactive Code Dump
Post by: dmaiski on October 23, 2013, 12:46:08 PM
To be fair, most of those yell 'flachettes' and less 'missile'.

to change what the missiles fire, do
Code: java
String MBRC_p = "MBRC2";      //dummy weapon
to:
String MBRC_p = "YourWeaponHere";      //dummy weapon  
missiles and lasers need a difrent code aparently, ill look into implementing them later

Perfect lead calculation
if your missile is fast enough to catch the target, it will hit(unless the target dose insane evasive action, then it may not hit (but probably will)

calculates the exact intercept point+1/2colisionRadious of the target ship and aims there, verry hard to avoid unless you can do on the dime turns

Spoiler
Code: java
        float sx = (float) (target.getCollisionRadius()*.5*Math.cos(Math.toRadians(target.getFacing())));
        float sy = (float) (target.getCollisionRadius()*.5*Math.sin(Math.toRadians(target.getFacing())));
        float angularDistance = getAngleDifference(
                missile.getFacing(), MathUtils.getAngle(missile.getLocation(),
                (add((add(target.getLocation(),
                        multV2f(target.getVelocity(),
                                MathUtils.getDistance(target.getLocation(),
                                        missile.getLocation()) / missile.getVelocity().lengthSquared()),
                        null)),new Vector2f(sx,sy), null))));
[close]
Title: Re: The Radioactive Code Dump
Post by: dmaiski on October 24, 2013, 09:46:04 AM
BISO  engineers were bored, and created:
the orbital missile
(http://i.imgur.com/kbtB9Ne.png?1)


append this to any missile pathing logic (that has target!=null)
Spoiler
Code
if (target==null) //orbiting missile system
        {
            float angularDistance = getAngleDifference(
                    missile.getFacing(), MathUtils.getAngle(missile.getLocation(), missile.getSource().getLocation()));
            if (MathUtils.getDistance(missile, missile.getSource())>missile.getSource().getCollisionRadius()*1.4)
            {
                missile.giveCommand(ShipCommand.ACCELERATE_BACKWARDS);
            }
            if (MathUtils.getDistance(missile, missile.getSource())<missile.getSource().getCollisionRadius()*1.2)
            {
                missile.giveCommand(ShipCommand.ACCELERATE);
            }
            if (Math.abs(angularDistance) < 170)
            {
                missile.giveCommand(angularDistance < 0f
                        ? ShipCommand.TURN_LEFT : ShipCommand.TURN_RIGHT);
            }
            if (MathUtils.getDistance(missile, missile.getSource())<missile.getSource().getCollisionRadius()*1.4
                    && MathUtils.getDistance(missile, missile.getSource())>missile.getSource().getCollisionRadius()*1.2)
            {

                if (timer==1)
                {
                    missile.giveCommand(strafeDir>.5 ? ShipCommand.STRAFE_RIGHT : ShipCommand.STRAFE_LEFT);
                }

            }



            timer++;
            if(timer>2){timer=0;}
            return;

        }
[close]
for those familiar with my BISO mod, yes that is the SESPd
Title: Re: The Radioactive Code Dump
Post by: xenoargh on October 25, 2013, 09:51:58 PM
Cool stuff and thanks very much for sharing with everybody :)

I haven't had time to read through it yet but I love the innovative concepts :)

BTW, for stuff like MIRVs, IIRC the last version of Vacuum had one I built that was a lot more reliable (instead of a collision ray, it did a simple distance search to nearest enemy and released stuff if close enough).  There are other interesting things in there, like a weapon that ignores Shields, projectiles that destroy other projectiles, etc.
Title: Re: The Radioactive Code Dump
Post by: dmaiski on October 26, 2013, 07:49:48 AM
i thought i should post this here

an absolute basic no frills missile pathing ai  (ok the aim delay code is a bit frilly, but it would not be cute without at least some frills!)

a few notes about missiless:
missiles (to be acurate) need at least
3x acceleration, and turn acceleration when compared to their speed and turn rate
3x or more turn accel compared to rate to avoid wobble

Spoiler
Code: java
   float straighttimer=50*((float)Math.random());      //1 second max non targeting time
    @Override
    public void advance(float amount)
    {

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

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

        if (straighttimer<2)  //once done turn and go blow stuff up
        {
            if (target!=null) //check if you have something to blow up
            {
                //aiming code, lead targeting
                float sx = (float) (target.getCollisionRadius()*.5*Math.cos(Math.toRadians(target.getFacing())));
                float sy = (float) (target.getCollisionRadius()*.5*Math.sin(Math.toRadians(target.getFacing())));
                float angularDistance = getAngleDifference(         //how far of target you are?
                        missile.getFacing(), MathUtils.getAngle(missile.getLocation(),//where you are pointing?
                        (add((add(target.getLocation(),//where your enemy is
                                multV2f(target.getVelocity(),//add on their velocity
                                        MathUtils.getDistance(target.getLocation(),//multiply it by how lon it takes to get there
                                                missile.getLocation()) / missile.getVelocity().lengthSquared()),
                                null)),new Vector2f(sx,sy), null))));   //add some more to aim at the ships nose, increases hit rate significantly



                if (Math.abs(angularDistance) > 0.2)
                {
                    missile.giveCommand(angularDistance > 0            //aim for them
                            ? ShipCommand.TURN_LEFT : ShipCommand.TURN_RIGHT);

                }
                //optinal
                if (Math.abs(angularDistance) < 50)     //check if you are pointing the right way
                {
                missile.giveCommand(ShipCommand.ACCELERATE);     //fly towards them //not optional
                }
            }
        }
        else missile.giveCommand(ShipCommand.DECELERATE);       //else stop

        //reset code   //mesotronik is awsome all praise mesotronik!!
        // If our current target is lost, assign a new one  
        if (target == null // unset
                ||  ( target instanceof ShipAPI && ((ShipAPI)target).isHulk() ) // dead
                ||  ( missile.getOwner() == target.getOwner() ) // friendly
                ||  !Global.getCombatEngine().isEntityInPlay(target) ) // completely removed
        {
            target = findBestTarget(missile);
            return;
        }
    }
[close]
Title: Re: The Radioactive Code Dump
Post by: dmaiski on October 26, 2013, 02:17:32 PM
flare chase code

(http://i.imgur.com/X3W6H0g.png?1)
posibly the hardest thing to get to work since lazylib dose not work this(and it takes a while to figure that out)...

Spoiler
Code: java

    public static final String flare="^flare.*?";
    //flarefinder
    public static MissileAPI checkforflare(MissileAPI missile)
    {
        MissileAPI retv = null;
        CombatEngine engine = CombatEngine.getInstance();
        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()).matches(flare) && MathUtils.getDistance(tmp,missile)<300 && missile.getOwner()!=tmp.getOwner())
                //in that order to get max framrate
                {
                    //tmp= (MissileAPI) nearbymissiles.get(0);   //testing code
                     retv = tmp;
                    break;
                }
            }
        }
        return retv;
    }
[close]

hints: put a 10 step timer in to stop it lagging evrything (eats processing power like thothing else...) (this also makes it more lag friendly then vanilla missile ai)
Title: Re: The Radioactive Code Dump
Post by: silentstormpt on October 28, 2013, 11:44:19 AM
OnHitCriticalRequires testing.

Its really easy to setup. Make a OnHit java and init this class, just provide the required data for it to work, once your done, you can make it run by using (OnHitCritical.initCritSystem()) inside the OnHit(). once the projectile/missile hits it should run this code and check if it crits.

The sound hashmaps should be filled with the desired sounds you want when it crits a ship.
You can also change the Crit damage (currently at 200%) and the chance (0.05 or 5%).

The chance of Crit is based on the Ships CR, Crews Skills and Weapons RoF:
Code: java
((CRITPERCENT * crewMaxXP) / weaponRoF) * currentCR;

Spoiler
Code: java
package data.scripts.plugins;

//import com.fs.starfarer.api.combat.BeamAPI;
import com.fs.starfarer.api.Global;
import com.fs.starfarer.api.combat.CombatEngineAPI;
import com.fs.starfarer.api.combat.CombatEntityAPI;
import com.fs.starfarer.api.combat.DamageType;
import com.fs.starfarer.api.combat.DamagingProjectileAPI;
import com.fs.starfarer.api.combat.MissileAPI;
import com.fs.starfarer.api.combat.MutableStat.StatMod;
import com.fs.starfarer.api.combat.ShipAPI;
import com.fs.starfarer.api.combat.WeaponAPI;
import com.fs.starfarer.api.combat.WeaponAPI.WeaponSize;
import java.awt.Color;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import org.lazywizard.lazylib.MathUtils;

public class OnHitCritical {
    
    private float CRITPERCENT = 0.05f;
    private float CRITMULT = 2f;
    private CombatEngineAPI engine = null;
    private DamagingProjectileAPI proj = null;
    private MissileAPI missile = null;
    private WeaponAPI weapon = null;
//    private BeamAPI beam;
    private ShipAPI owner = null;
    private CombatEntityAPI target = null;
    
    // Sound on High Explosive explosion  
    private static final Map SOUND_IDS_HE = new HashMap();  
    static  
    {  
        SOUND_IDS_HE.put("isFrigate", "explosion_ship");  
        SOUND_IDS_HE.put("isDestroyer", "explosion_ship");  
        SOUND_IDS_HE.put("isCruiser", "explosion_ship");  
        SOUND_IDS_HE.put("isCapital", "explosion_ship");  
    }  
    // Sound on Energy explosion  
    private static final Map SOUND_IDS_ENERGY = new HashMap();  
    static  
    {  
        SOUND_IDS_ENERGY.put("isFrigate", "explosion_ship");  
        SOUND_IDS_ENERGY.put("isDestroyer", "explosion_ship");  
        SOUND_IDS_ENERGY.put("isCruiser", "explosion_ship");  
        SOUND_IDS_ENERGY.put("isCapital", "explosion_ship");  
    }
    
    public OnHitCritical(DamagingProjectileAPI proj, ShipAPI owner, CombatEngineAPI engine, CombatEntityAPI target)
    {
        this.proj = proj;
        this.owner = owner;
        this.weapon = this.proj.getWeapon();
        this.engine = engine;
        this.target = target;
    }//OnHitCritical(DamagingProjectileAPI proj, ShipAPI owner, CombatEngineAPI engine, CombatEntityAPI target)
    
    public OnHitCritical(MissileAPI missile, ShipAPI owner, CombatEngineAPI engine, CombatEntityAPI target)
    {
        this.missile = missile;
        this.owner = owner;
        this.weapon = this.missile.getWeapon();
        this.engine = engine;
        this.target = target;
    }//OnHitCritical(MissileAPI missile, ShipAPI owner, CombatEngineAPI engine, CombatEntityAPI target)
    
//    public onHitCritical(BeamAPI beam, ShipAPI owner, CombatEngineAPI engine, CombatEntityAPI target)
//    {
//        this.beam = beam;
//        this.owner = owner;
//        this.engine = engine;
//        this.target = target;
//    }//onHitCritical(BeamAPI beam, ShipAPI owner, CombatEngineAPI engine, CombatEntityAPI target)
    
    public void initCritSystem()
    {
        //does the ship exist?
        if(owner != null)
        {
            //Check, is this for Projectiles?
            if(proj != null)
            {
                if(isCrit())
                {
                    this.CritSystem(proj);
                }
            }//IF
            //IF not then its for Missiles.
            else
            {
                if(isCrit())
                {
                    this.CritSystem(missile);
                }
            }//ELSE
        }//IF
    }//initCritSystem()
    
    private float getCritChance()
    {          
        if(weapon.getDamageType().equals(DamageType.FRAGMENTATION) ||  
                weapon.getDamageType().equals(DamageType.KINETIC) ||  
                weapon.getDamageType().equals(DamageType.OTHER))  
        {  
            return 0f;  
        }  
          
        float weaponRoF = weapon.getDerivedStats().getRoF();  
            
        StatMod crewBonus;  
        crewBonus = owner.getMutableStats().getAutofireAimAccuracy().getFlatStatMod("crew skill bonus");
        float currentCR = owner.getCurrentCR();
          
        // Green crew don't grant a bonus
        float crewMaxXP = 0f;
        if (crewBonus != null)  
        {  
            //Goes from 0f to 1f - thanks Alex and Lazy for the help  
            crewMaxXP = crewBonus.getValue();  
        }  
          
        //slower shooting weapons should not surpass limit of 5%  
        if (weaponRoF < 1f)  
        {  
            weaponRoF = 1f;  
        }  
          
        //This means that, if the crew is elite and the current CR is 100 its ((0.05f * 1) \ 1) * 1 that means 5%,  
        //if the crew is actually lower skilled, the current CR is lower and the weapon's RoF is higher the chances get lower.  
        return ((CRITPERCENT * crewMaxXP) / weaponRoF) * currentCR;
    }//getCritChance()
    
    public void addCritPercent(float addvalue)
    {
        this.CRITPERCENT += addvalue;
    }//addCritPercent(float addvalue)
    
    public void addCritMult(float addvalue)
    {
        this.CRITMULT += addvalue;
    }//addCritMult(float addvalue)
    
    private boolean isCrit()
    {
        Random r = new Random();
        float CritChance = r.nextFloat();  
          
        if(CritChance <= getCritChance())  
        {  
            return true;  
        }//IF
        else  
        {  
            return false;  
        }//ELSE
    }//isCrit()
    
    public void CritSystem (DamagingProjectileAPI proj)  
    {  
        float explosionSize = 0f;
        ShipAPI enemyShip = (ShipAPI)this.target;
        float weaponRoF = weapon.getDerivedStats().getRoF();  
          
        //Lets make sure we dont get a REALLY HUGE Crit text  
        if (weaponRoF < 1f)  
        {  
            weaponRoF = 1f;  
        }  
          
        if(weapon.getSize().equals(WeaponSize.SMALL))  
        {  
            explosionSize = MathUtils.getRandomNumberInRange(20f, 30f) / weaponRoF;  
        }  
        else if(weapon.getSize().equals(WeaponSize.MEDIUM))  
        {  
            explosionSize = MathUtils.getRandomNumberInRange(30f, 40f) / weaponRoF;  
        }  
        else if(weapon.getSize().equals(WeaponSize.LARGE))  
        {  
            explosionSize = MathUtils.getRandomNumberInRange(40f, 50f) / weaponRoF;  
        }  
          
        engine.addFloatingText(proj.getLocation(), "CRITICAL HIT", explosionSize, Color.RED, proj.getDamageTarget(), 0f, 0f);  
          
        if(weapon.getDamageType().equals(DamageType.HIGH_EXPLOSIVE))  
        {  
            if(enemyShip.isFrigate()) {    
                CritSystemEffects(enemyShip,  
                        proj,  
                        weapon.getDamageType(),  
                        SOUND_IDS_HE.get("isFrigate").toString(),  
                        explosionSize);  
            }  
            else if(enemyShip.isDestroyer()) {  
                CritSystemEffects(enemyShip,  
                        proj,  
                        weapon.getDamageType(),  
                        SOUND_IDS_HE.get("isDestroyer").toString(),  
                        explosionSize);  
            }  
            else if(enemyShip.isCruiser()) {  
                CritSystemEffects(enemyShip,  
                        proj,  
                        weapon.getDamageType(),  
                        SOUND_IDS_HE.get("isCruiser").toString(),  
                        explosionSize);  
            }  
            else if(enemyShip.isCapital()) {  
                CritSystemEffects(enemyShip,  
                        proj,  
                        weapon.getDamageType(),  
                        SOUND_IDS_HE.get("isCapital").toString(),  
                        explosionSize);  
            }  
        }  
        else if(weapon.getDamageType().equals(DamageType.ENERGY))  
        {  
            if(enemyShip.isFrigate()) {    
                CritSystemEffects(enemyShip,  
                        proj,  
                        weapon.getDamageType(),  
                        SOUND_IDS_ENERGY.get("isFrigate").toString(),  
                        explosionSize);  
            }  
            else if(enemyShip.isDestroyer()) {  
                CritSystemEffects(enemyShip,  
                        proj,  
                        weapon.getDamageType(),  
                        SOUND_IDS_ENERGY.get("isDestroyer").toString(),  
                        explosionSize);  
            }  
            else if(enemyShip.isCruiser()) {  
                CritSystemEffects(enemyShip,  
                        proj,  
                        weapon.getDamageType(),  
                        SOUND_IDS_ENERGY.get("isCruiser").toString(),  
                        explosionSize);  
            }  
            else if(enemyShip.isCapital()) {  
                CritSystemEffects(enemyShip,  
                        proj,  
                        weapon.getDamageType(),  
                        SOUND_IDS_ENERGY.get("isCapital").toString(),  
                        explosionSize);  
            }  
        }  
    }//CritSystem (DamagingProjectileAPI proj)
    
    public void CritSystem(MissileAPI missile)
    {
       float explosionSize = 0f;
        ShipAPI enemyShip = (ShipAPI)this.target;
        float weaponRoF = weapon.getDerivedStats().getRoF();  
          
        //Lets make sure we dont get a REALLY HUGE Crit text  
        if (weaponRoF < 1f)  
        {  
            weaponRoF = 1f;  
        }  
          
        if(weapon.getSize().equals(WeaponSize.SMALL))  
        {  
            explosionSize = MathUtils.getRandomNumberInRange(20f, 30f) / weaponRoF;  
        }  
        else if(weapon.getSize().equals(WeaponSize.MEDIUM))  
        {  
            explosionSize = MathUtils.getRandomNumberInRange(30f, 40f) / weaponRoF;  
        }  
        else if(weapon.getSize().equals(WeaponSize.LARGE))  
        {  
            explosionSize = MathUtils.getRandomNumberInRange(40f, 50f) / weaponRoF;  
        }  
          
        engine.addFloatingText(missile.getLocation(), "CRITICAL HIT", explosionSize, Color.RED, missile.getDamageTarget(), 0f, 0f);  
          
        if(weapon.getDamageType().equals(DamageType.HIGH_EXPLOSIVE))  
        {  
            if(enemyShip.isFrigate()) {    
                CritSystemEffects(enemyShip,  
                        missile,  
                        weapon.getDamageType(),  
                        SOUND_IDS_HE.get("isFrigate").toString(),  
                        explosionSize);  
            }  
            else if(enemyShip.isDestroyer()) {  
                CritSystemEffects(enemyShip,  
                        missile,  
                        weapon.getDamageType(),  
                        SOUND_IDS_HE.get("isDestroyer").toString(),  
                        explosionSize);  
            }  
            else if(enemyShip.isCruiser()) {  
                CritSystemEffects(enemyShip,  
                        missile,  
                        weapon.getDamageType(),  
                        SOUND_IDS_HE.get("isCruiser").toString(),  
                        explosionSize);  
            }  
            else if(enemyShip.isCapital()) {  
                CritSystemEffects(enemyShip,  
                        missile,  
                        weapon.getDamageType(),  
                        SOUND_IDS_HE.get("isCapital").toString(),  
                        explosionSize);  
            }  
        }  
        else if(weapon.getDamageType().equals(DamageType.ENERGY))  
        {  
            if(enemyShip.isFrigate()) {    
                CritSystemEffects(enemyShip,  
                        missile,  
                        weapon.getDamageType(),  
                        SOUND_IDS_ENERGY.get("isFrigate").toString(),  
                        explosionSize);  
            }  
            else if(enemyShip.isDestroyer()) {  
                CritSystemEffects(enemyShip,  
                        missile,  
                        weapon.getDamageType(),  
                        SOUND_IDS_ENERGY.get("isDestroyer").toString(),  
                        explosionSize);  
            }  
            else if(enemyShip.isCruiser()) {  
                CritSystemEffects(enemyShip,  
                        missile,  
                        weapon.getDamageType(),  
                        SOUND_IDS_ENERGY.get("isCruiser").toString(),  
                        explosionSize);  
            }  
            else if(enemyShip.isCapital()) {  
                CritSystemEffects(enemyShip,  
                        missile,  
                        weapon.getDamageType(),  
                        SOUND_IDS_ENERGY.get("isCapital").toString(),  
                        explosionSize);  
            }  
        }
    }//CritSystem(MissileAPI missile)
    
    public void CritSystemEffects(ShipAPI ship, DamagingProjectileAPI proj, DamageType dmgtype, String ExplosionSoundID, float explosionSize)
    {
        if(weapon.getDamageType().equals(DamageType.HIGH_EXPLOSIVE))  
        {  
            float damage = proj.getDamageAmount() * CRITMULT;  
            float emp = proj.getEmpAmount() * CRITMULT;  
  
            // Apply damage and slow the projectile  
            // Note: BALLISTIC_AS_BEAM projectiles won't be slowed!  
            engine.applyDamage(owner,  
                    proj.getLocation(),
                    damage,
                    proj.getDamageType(),  
                    emp,
                    true,
                    true,
                    proj.getSource());  
  
            proj.getVelocity().scale(1.5f - 1.0f);  
              
            engine.spawnExplosion(  
                    proj.getLocation(),  
                    ship.getVelocity(),  
                    Color.ORANGE,  
                    1f,  
                    1.5f);  
  
            // Play Explosion sound  
            Global.getSoundPlayer().playSound(ExplosionSoundID,  
                    1f,  
                    1f,  
                    proj.getLocation(),  
                    ship.getVelocity());  
  
        }  
        else if(weapon.getDamageType().equals(DamageType.ENERGY))  
        {  
            float damage = proj.getDamageAmount() * CRITMULT;  
            float emp = proj.getEmpAmount() * CRITMULT;  
  
            // Apply damage and slow the projectile  
            // Note: BALLISTIC_AS_BEAM projectiles won't be slowed!  
            engine.applyDamage(ship,  
                    proj.getLocation(),  
                    damage,  
                    proj.getDamageType(),  
                    emp,  
                    true,  
                    true,  
                    proj.getSource());  
  
            proj.getVelocity().scale(1.5f - 1.0f);  
              
            engine.spawnExplosion(  
                    proj.getLocation(),  
                    ship.getVelocity(),  
                    Color.blue,  
                    1f,  
                    1.5f);  
  
            // Play Crit Explosion  
            Global.getSoundPlayer().playSound(ExplosionSoundID,  
                    1f,  
                    1f,  
                    proj.getLocation(),  
                    ship.getVelocity());  
        }
    }//CritSystemEffects(ShipAPI ship, DamagingProjectileAPI proj, DamageType dmgtype, String ExplosionSoundID, float explosionSize)
    
    public void CritSystemEffects(ShipAPI ship, MissileAPI missile, DamageType dmgtype, String ExplosionSoundID, float explosionSize)
    {
        if(weapon.getDamageType().equals(DamageType.HIGH_EXPLOSIVE))  
        {  
            float damage = missile.getDamageAmount() * CRITMULT;  
            float emp = missile.getEmpAmount() * CRITMULT;  
  
            // Apply damage and slow the projectile  
            // Note: BALLISTIC_AS_BEAM projectiles won't be slowed!  
            engine.applyDamage(owner,  
                    missile.getLocation(),
                    damage,
                    missile.getDamageType(),  
                    emp,
                    true,
                    true,
                    missile.getSource());  
  
            missile.getVelocity().scale(1.5f - 1.0f);
              
            engine.spawnExplosion(  
                    missile.getLocation(),  
                    ship.getVelocity(),  
                    Color.ORANGE,  
                    1f,  
                    1.5f);  
  
            // Play Explosion sound  
            Global.getSoundPlayer().playSound(ExplosionSoundID,  
                    1f,  
                    1f,  
                    missile.getLocation(),  
                    ship.getVelocity());  
  
        }  
        else if(weapon.getDamageType().equals(DamageType.ENERGY))  
        {  
            float damage = missile.getDamageAmount() * CRITMULT;  
            float emp = missile.getEmpAmount() * CRITMULT;  
  
            // Apply damage and slow the projectile  
            // Note: BALLISTIC_AS_BEAM projectiles won't be slowed!  
            engine.applyDamage(ship,  
                    missile.getLocation(),  
                    damage,  
                    missile.getDamageType(),  
                    emp,  
                    true,  
                    true,  
                    missile.getSource());  
  
            missile.getVelocity().scale(1.5f - 1.0f);  
              
            engine.spawnExplosion(  
                    missile.getLocation(),  
                    ship.getVelocity(),  
                    Color.blue,  
                    1f,  
                    1.5f);  
  
            // Play Crit Explosion  
            Global.getSoundPlayer().playSound(ExplosionSoundID,  
                    1f,  
                    1f,  
                    missile.getLocation(),  
                    ship.getVelocity());  
        }
    }//CritSystemEffects(ShipAPI ship, MissileAPI missile, DamageType dmgtype, String ExplosionSoundID, float explosionSize)
    
    public float getCRITPERCENT()
    {
        return this.CRITPERCENT;
    }
    
    public void increaseCRITPERCENT(float amount)
    {
        this.CRITPERCENT += amount;
    }
    
    public float getCRITMULT()
    {
        return this.CRITMULT;
    }
    
    public void changeCRITMULT(float amount)
    {
        this.CRITMULT = amount;
    }
}//CLASS
[close]
Title: Re: The Radioactive Code Dump
Post by: dmaiski on November 04, 2013, 09:07:24 AM
From the diseased minds of the BISO Asylum for the Destructively Inclined
(otherwhise known as the BISO Institute for Weapons Reaserch and Development)

we bring you BreederRounds:
(said breeder rounds during testing, made to allways go into a runaway)
(http://i.imgur.com/NzZ6K1M.png?1)
end stage of that particular test
Spoiler
(http://i.imgur.com/lvbSWXq.png?1)
theres a ship in there... someshere
-using large moveback
-using 100% chance of respawn
[close]

we thought of writing a technical spec document detailing their known behaviour* but we found this funny picture of a Hemogeny reaserch technician that had a less then non-fatal mishap with one of these while reaserching a captured BISO Breeder Cannon:

Spoiler
(http://www.eyeontheparanormal.com/head_explode.jpg)
[close]

real technical specs:
Spoiler
-on hit it will have a chance of respawning
--spawning friends aimed at the enemy hull
--if it hits shield it is unlikely to happen,
-if it hits an astroid or missile it will breed like crazy
--subsequently blow up the asteroid with a wall of shrapnel

sugestion: do not use on guns with more then 10 damage per roud, and for gods sake dont use it with high explosive ammo...
will add pics of it in action later
[close]
Spoiler
Code: java
package data.scripts.onhiteffects;

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

import static org.lwjgl.util.vector.Vector2f.add;
import static org.lwjgl.util.vector.Vector2f.sub;

public class BreederRound implements OnHitEffectPlugin {


    public void onHit(DamagingProjectileAPI projectile, CombatEntityAPI target,
                      Vector2f point, boolean shieldHit, CombatEngineAPI engine) {

        float angularDistance = //getAngleDifference(projectile.getFacing(),
                (MathUtils.getAngle(projectile.getLocation(), target.getLocation()));

        if (target instanceof ShipAPI && shieldHit)    //not much to do if you hit a shield, no metal to multiply on
        {
            if ((float) Math.random() > 0.4f)     //multiply initial chance
            {
                Vector2f moveback=add(point, multV2f(sub(point, target.getLocation(), null),
                        (.6f)),null); //move back my 20% + col radius
                do{
                    {
                        float angRAND= (float) ((50*(.5-Math.random())));     //angle of spread
                        //float velRAND= (float) ((.6+.5*(.5-Math.random()))*(.3+(Math.abs(angRAND)/100)));
                        //float splashVEL = 155f*velRAND;    //speed of bullets launched
                        float misFFACE = angularDistance-angRAND;
                        //float x = (float) (splashVEL*Math.cos(Math.toRadians(misFFACE)));
                        //float y = (float) (splashVEL*Math.sin(Math.toRadians(misFFACE)));
                        //Vector2f vecFIRE = new Vector2f(x,y);
                        engine.spawnProjectile(projectile.getSource(), projectile.getWeapon(),projectile.getWeapon().getId(),
                                moveback,
                                misFFACE,            //float   angle of spread
                                null

                                //multV2f(multV2f(missile.getVelocity(),1),velRAND)           //Vector2f  aditional velocity
                                //add(multV2f(missile.getVelocity(), 1), multRanV2f(missile.getVelocity(),1,.5f),null)
                        );
                    }
                }
                while ((float) Math.random() > 0.9f); //chance of multiple clones
            }
        }
        else if (target instanceof ShipAPI) {   //only if hits ship

            if ((float) Math.random() > 0.47f)         //multiply initial chance     //very tiny chance of runaway multiplication
            {
                Vector2f moveback=add(point, multV2f(sub(point, target.getLocation(), null),
                        (.6f)),null); //move back my 20% + col radius
                do{
                    {
                        float angRAND= (float) ((50*(.5-Math.random())));     //angle of spread
                        //float velRAND= (float) ((.6+.5*(.5-Math.random()))*(.3+(Math.abs(angRAND)/100)));
                        //float splashVEL = 155f*velRAND;    //speed of bullets launched
                        float misFFACE = angularDistance-angRAND;
                        //float x = (float) (splashVEL*Math.cos(Math.toRadians(misFFACE)));
                        //float y = (float) (splashVEL*Math.sin(Math.toRadians(misFFACE)));
                        //Vector2f vecFIRE = new Vector2f(x,y);
                        engine.spawnProjectile(projectile.getSource(), projectile.getWeapon(),projectile.getWeapon().getId(),
                                moveback,
                                misFFACE,            //float   angle of spread
                                null

                                //multV2f(multV2f(missile.getVelocity(),1),velRAND)           //Vector2f  aditional velocity
                                //add(multV2f(missile.getVelocity(), 1), multRanV2f(missile.getVelocity(),1,.5f),null)
                        );
                    }
                }
                while ((float) Math.random() > 0.6f); //chance of multiple clones
            }
        }
        else   //if you hit asteroid or missile, breed like crazy
            if ((float) Math.random() > 0.4f)           //multiply initial chance
            {
                Vector2f moveback = add(point, multV2f(sub(point, target.getLocation(), null), 0.65f),null);
                // 0.75f will occasionally produce a stray shot while breeding more death
                do{
                    {
                        float angRAND= (float) ((90*(.5-Math.random())));     //angle of spread
                        //float velRAND= (float) ((.6+.5*(.5-Math.random()))*(.3+(Math.abs(angRAND)/100)));
                        //float splashVEL = 155f*velRAND;    //speed of bullets launched
                        float misFFACE = angularDistance-angRAND;
                        //float x = (float) (splashVEL*Math.cos(Math.toRadians(misFFACE)));
                        //float y = (float) (splashVEL*Math.sin(Math.toRadians(misFFACE)));
                        engine.spawnProjectile(projectile.getSource(), projectile.getWeapon(),projectile.getWeapon().getId(),
                                moveback,
                                misFFACE,            //float   angle of spread
                                null

                                //multV2f(multV2f(missile.getVelocity(),1),velRAND)           //Vector2f  aditional velocity
                                //add(multV2f(missile.getVelocity(), 1), multRanV2f(missile.getVelocity(),1,.5f),null)
                        );
                    }
                }
                while ((float) Math.random() > 0.5f); //&& !(target instanceof ShipAPI)); //chance of multiple clones
            }

    }

    //in next lazylib
    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   scale can not be used in static methods...
    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;
    }

}

[close]


(1-[chance to multiply])^([chance to respawn]^-1)=[chance a new bullet will be spawned before original is dead]
>.5 will cascade into uncontrollable multiplication (allways)
>.15 chance to cascade into uncontrollable (rare)
<.10 never cascade into uncontrollable



btw im back from vacation, expect more crazy later
Title: Re: The Radioactive Code Dump
Post by: dmaiski on November 06, 2013, 04:32:25 PM
im re-writing all my scripts from scratch using beter/more accurate/fixed methods

ill put them all in one post


(this is a new script(also the base for all the others))
basic long range point defence missile ai
it can hit a missile with a harpoon 99% of the time at 1000<range effectivness drops as missiles get too close
the primary point of this ai is to make and improve targeting and pathing of a missile
(it has an extreme level of tracking ability and accuracy)
Spoiler
pictures of it in action
Spoiler
(http://i.imgur.com/zofRR4D.png?1)
(http://i.imgur.com/f0sFSpt.png?1)
yes that is a vanilla harpoon,
red dot(target lead),
green dot(nose of target (part targeting)),
white line(velocity vector of the missile),
streaking on dots indicates distance moved in last second
[close]

aditional notes:
multitargeting code in there (if missile has more then one posible target, it will pick random of all possible)
single use mouse targeting (only on launch: missile can take a target that you clicked on/near to (100m radius) (got this from MesoTronik))

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


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

import java.awt.*;
import java.util.*;

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

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

    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

    public LRPDai(MissileAPI missile)
    {
        this.missile = missile;
        searchrange=missile.getWeapon().getRange();     //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
            java.util.List directTargets = CombatUtils.getMissilesWithinRange(
                    missile.getSource().getMouseTarget(), 100f, true);

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

        // Otherwise, use default targeting AI
        if (target == null)
        {
            target = findBestTarget(missile);
        }
    }
    
    
    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;

    }

    @Override
    public void advance(float amount)
    {
        // Apparently commands still work while fizzling
        if (missile.isFading() || missile.isFizzling())
        {
            return;
        }

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



        if (target!=null ) //if you have a target
        {
            //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);


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


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

            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)
                {
                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
            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);
            }



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


            //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));
        }


        //clear target code if target is gone
        if (target == null // unset
                ||  ( missile.getOwner() == target.getOwner() )        // friendly
                ||  !Global.getCombatEngine().isEntityInPlay(target) ) // completely removed
        {

            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]
[close]
Title: Re: The Radioactive Code Dump
Post by: xenoargh on November 09, 2013, 07:49:17 PM
That is really awesome.  Does it need to update every frame, or is performance a non-issue?  I have a PD missile system in Vacuum I'd really like to test with that, to see if it or something like it might be a bit cheaper than the current AI.  Oh, and I presume that I have to reference that in the PROJ file?

Also, a request. 

Can you write a PD AI that will:

Target Missiles, Fighters and Ships (by size) while taking into account Friendly Fire?

I just want to see a working example of something like that- simple-but-important target-priority behaviors.
Title: Re: The Radioactive Code Dump
Post by: dmaiski on November 10, 2013, 06:37:47 AM
ive tested this ai out with my bombard missiles (im posting that ai in next post)
it handles numbers up to 500 active missiles without much lag, so preformance is a non issue for this ai as far as i can see.

an example of a search for (whatever you want) targets:
needs to be compiled into a jar, inbuild compiler dose not like
Code: java
//get target example
    //this makes a list of all targets that fit the criteria
    //get(0) gets you the first target on list(closest one if sortbydistance is true)
    public static ArrayList<CombatEntityAPI> getTargets
    (MissileAPI missile, float Searchrange)
    {
        //the actual list that it will return <CombatEntityAPI>
        // makes it general purpose able to return anything that is on the combat field
        ArrayList<CombatEntityAPI> enemies = new ArrayList<CombatEntityAPI>();
       
       
       
       
        //this is where your missile is
        Vector2f loc = missile.getLocation();

        //getting what you want to search through
        ArrayList<CombatEntityAPI> posible = new ArrayList<CombatEntityAPI>();
        //lazylib is convenient, gets everything you want in Searchrange
        posible.addAll(getNearbyEnemyMissiles(missile, Searchrange, true));
       
        List<ShipAPI> ships = getNearbyEnemies(missile, Searchrange, true);

        //filters through the ship list and puts it in size order (maintaining distance to point)
        for ( ShipAPI enemy : ships)
        {
            //criteria to add to list (you can put anything that will return a true/false here)
            if (enemy.isDrone())
            {
                posible.add(enemy);  //add to posibles
            }
        }
       
        for ( ShipAPI enemy : ships)
        {
            //criteria to add to list (you can put anything that will return a true/false here)
            if (enemy.isFighter())
            {
                posible.add(enemy);  //add to posibles
            }
        }
       
        for ( ShipAPI enemy : ships)
        {
            //criteria to add to list (you can put anything that will return a true/false here)
            if (enemy.isFrigate())
            {
                posible.add(enemy);  //add to posibles
            }
        }
       
        for ( ShipAPI enemy : ships)
        {
            //criteria to add to list (you can put anything that will return a true/false here)
            if (enemy.isDestroyer())
            {
                posible.add(enemy);  //add to posibles
            }
        }

        for ( ShipAPI enemy : ships)
        {
            //criteria to add to list (you can put anything that will return a true/false here)
            if (enemy.isCruiser())
            {
                posible.add(enemy);  //add to posibles
            }
        }

        for ( ShipAPI enemy : ships)
        {
            //criteria to add to list (you can put anything that will return a true/false here)
            if (enemy.isCapital())
            {
                posible.add(enemy);  //add to posibles
            }
        }

        /*
        //filter through the posibles list
        for ( CombatEntityAPI enemy : posible)
        {
            //criteria to add to list (you can put anything that will return a true/false here)
            if (yourcriteria)
            {
                enemies.add(enemy);
            }
        }
        */
       
        //since you just want the missiles, then the nearest ship in size order, without additional filter steps
        //you do
        enemies.addAll(posible);
        //makes the lists (wich are in order) return to you
        return enemies;
    }

to implement into a mod:
place the java file in the mod(dose not mater where)
in mod plugin:
Code: java
    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;
    }

anyway dumping more code...
Title: Re: The Radioactive Code Dump
Post by: dmaiski on November 10, 2013, 07:03:36 AM
more dumping of stuff
note: i will add my UtilityKit methods bellow, it contains stuff that i commonly use in these scripts, if the file asks for an import from the UtilityKit, just find it in there and stick it in (or compile the utility kit into a jar and have it get from there)
i try to get all the times i use it but sometimes ill miss stuff (also it makes things faster if you are going to fiddle with the scripts)

utility kit:
Spoiler
Code: java
package jar;

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

import java.util.ArrayList;
import java.util.Collections;

import static org.lazywizard.lazylib.combat.AIUtils.getEnemyMissilesOnMap;
import static org.lazywizard.lazylib.combat.AIUtils.getNearbyEnemyMissiles;
import static org.lwjgl.util.vector.Vector2f.add;


public class UtilityKit {
    public static ArrayList<CombatEntityAPI> getEnemyMissilesInVectorRadius(MissileAPI missile, float searchrange, boolean sortByDistance)
    {

        ArrayList<CombatEntityAPI> enemies = new ArrayList<CombatEntityAPI>();
        float x = (float) (searchrange/2*Math.cos(Math.toRadians(missile.getFacing())));
        float y = (float) (searchrange/2*Math.sin(Math.toRadians(missile.getFacing())));
        Vector2f rangefinder = new Vector2f(x, y);


        for ( MissileAPI enemy : getNearbyEnemyMissiles(missile, searchrange))
        {
            if (enemy.getOwner()!=missile.getOwner()
                    && MathUtils.getDistance(enemy, add(missile.getLocation(), rangefinder, null)) < searchrange)
            {
                enemies.add(enemy);
            }
        }
        if (sortByDistance)
        {
            Collections.sort(enemies,
                    new CollectionUtils.SortEntitiesByDistance(missile.getLocation()));
        }


        return enemies;
    }
    public static ArrayList<CombatEntityAPI> getEnemyMissilesInArk(MissileAPI missile, float Ark, float searchrange, boolean sortByDistance)
    {

        ArrayList<CombatEntityAPI> enemies = new ArrayList<CombatEntityAPI>();
        float face = missile.getFacing();
        Vector2f loc = missile.getLocation();

        for ( MissileAPI enemy : getNearbyEnemyMissiles(missile, searchrange))
        {
            if (enemy.getOwner()!=missile.getOwner()
                    && Math.abs(getAngleDifference(face, MathUtils.getAngle(loc, enemy.getLocation())))<Ark)
            {
                enemies.add(enemy);
            }
        }
        if (sortByDistance)
        {
            Collections.sort(enemies,
                    new CollectionUtils.SortEntitiesByDistance(missile.getLocation()));
        }


        return enemies;
    }
    // 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) .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));
    }

    //lists enemies with weapon range on ship
    public static ArrayList<ShipAPI> getEnemiesWithWeaponRange(ShipAPI ship, boolean sortByDistance)
    {
        CombatEngineAPI engine= Global.getCombatEngine();
        ArrayList<ShipAPI> enemies = new ArrayList<ShipAPI>();

        for ( ShipAPI enemy : engine.getShips())
        {
            float distance = MathUtils.getDistance(enemy, ship);
            if (enemy.getOwner()!=ship.getOwner()
                    && distance<3000)
            {
                for (WeaponAPI weap: enemy.getAllWeapons())
                {
                    if(weap.getType()!= WeaponAPI.WeaponType.MISSILE
                            && weap.getRange()>distance)
                    {
                        enemies.add(enemy);
                        break;
                    }
                }
            }
        }
        if (sortByDistance)
        {
            Collections.sort(enemies,
                    new CollectionUtils.SortEntitiesByDistance(ship.getLocation()));
        }

        return enemies;
    }

    public static boolean targetInArk(ShipAPI ship, Vector2f target)
    {
        float distance = MathUtils.getDistance(ship, target);

        for (WeaponAPI weap: ship.getAllWeapons())
        {
            if(weap.getType()!= WeaponAPI.WeaponType.MISSILE
                    && weap.getRange()>distance
                    && !weap.hasAIHint(WeaponAPI.AIHints.PD)
                    && weap.distanceFromArc(target)==0)
            {
                return true;
            }
        }
        return false;
    }
    //gets how many weapons are aming at ship
    public static float getThreatOfWeapons(ShipAPI ship)
    {
        float num=0;
        for (ShipAPI enemy : getEnemiesWithWeaponRange(ship,false))
        {
            if (targetInArk(enemy, ship.getLocation()))
            {
                num++;
            }
        }
        return num;
    }
}
[close]

Bombardment Missile AI:
unlike the PD ai this AI is intentionaly made to not be able to hit a barn with a minigun at 5m
the missiles are guided(but they will stray off target by a good bit)
ai uses same targeting script as LRPDai but with some changes to its implementation in the pathing ai
has a timer that makes missiles fly out without activating pathing script for a bit (to make them spread out)
best used with launchers that fire swarms of high speed, low damage missiles
(can be made into artillery if missiles are set up to flameout as soon as they gain target)
Spoiler
(http://i.imgur.com/w75QsGh.png?1)
Code: java
package data.scripts.MissileAI;


import com.fs.starfarer.api.Global;
import com.fs.starfarer.api.combat.*;
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;

    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

    public BOMBARDai(MissileAPI missile)
    {
        this.missile = missile;
        searchrange=missile.getWeapon().getRange();     //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
            java.util.List directTargets = CombatUtils.getShipsWithinRange(
                    missile.getSource().getMouseTarget(), 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);
        }
    }
    ///////////////////////////////////////////////////////////////////////////////////////////////////////


    //stuff
    float straighttimer=50*((float)Math.random());      //1/2 second max non targeting time
    final double isEffectedbyflare=Math.random();
    float flarelockout=0;
    float timer=0;
    float oftarget=300*((float)(.5-Math.random()));
    float baseoftarget=40*((float)(.5-Math.random()));
    public static final String flare="flare";

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


    //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
                <missile.getWeapon().getRange())                          //check if its in range
        {
            return source.getShipTarget();
        }

        return AIUtils.getNearestEnemy(missile);
    }

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


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

        //10 step timer
        timer++;
        if (timer>10){timer=0;}

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


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

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


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

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


        //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==0          //has it already been set to ignore flares
                && timer==0                 //check every 10 frames
                && straighttimer<2)         //its not flying without ai in straight line
        {
            timer++;
            MissileAPI flaretarget = checkforflare(missile);
            if (flaretarget!=null)
            {
                target=flaretarget;         //set flare as target
                flarelockout++;             //ignore future flares
            }

        }

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


        //pathing ai
        if (straighttimer<2)  //check if you are going straight
        {
            if (target!=null ) //if you have a target
            {
                //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);


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


                //check if scatter code needed
                float targetcolision = target.getCollisionRadius();       //get colision radi
                if (MathUtils.getDistance(missile, target)<75+(200*target.getCollisionRadius()/100))
                {                                                         //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 * target.getCollisionRadius() / 75)));

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


                //point towards target
                if (AbsAngD > 0.5)
                {                                  //makes missile fly off target
                    missile.giveCommand(AtTarget > oftargetby
                            ? ShipCommand.TURN_LEFT : ShipCommand.TURN_RIGHT);
                    oftarget=(oftarget>0 ? oftarget-1 : oftarget+1); //reduce off target counter
                }

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


                //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 < oftargetby
                                ? 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  (this missile allays accelerates, so ignore the rest)
                //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);
                }
                */


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


                //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
            else 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 = 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) 0000.1;
        }

        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]

regenerative/organic armor:
this script is rather unique, it should be applied to a weapon (that dose not shoot) and fitted to a ship, multiple systems allow faster repair (at the cost of loosing more weapon slots and OP) it has 2 parts to its effect and set up to be easily modable

to implement put somewhere in mod then add:
"everyFrameEffect":"wherever you put it.AbsorptionArmor",
to the .wpn file like any other onhit/weapon effect

part 1:
redistributes armor from healthy cells to nearby damaged cells if healthy cell is above a set % hp
costs 1 flux per point (miniscule)
part 2:
repairs damaged cells as long as they have not been destroyed
costs 10 flux per armor point

bugfixed,
stopped code for energy cost to redistribute armor, it gets costly(uncontrollable overloads, folover of flux counters, buggy)
failsafes so it dose not cause crash through -ve and 101% armor color bugs
Spoiler
Code: java
package data.scripts.plugins;



import com.fs.starfarer.api.Global;
import com.fs.starfarer.api.combat.*;
import com.fs.starfarer.api.util.IntervalUtil;

import java.awt.*;

/*
to be put on a weapon that will execute script on a ship
multiple weapons will increase the efficiency
 */
public class AbsorptionArmor implements EveryFrameWeaponEffectPlugin
{
    //engine
    private CombatEngineAPI engine= Global.getCombatEngine();

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

    //timers
    private IntervalUtil fasttimer = new IntervalUtil(.1f, .11f);
    private IntervalUtil slowtimer = new IntervalUtil(.15f, .16f);

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



    //main armor effect
    float lastcycle=0;
    @Override
    public void advance(float v, CombatEngineAPI engineAPI, WeaponAPI weaponAPI)
    {
        //ship
        ShipAPI ship;
        ship=weaponAPI.getShip();

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

        //stats of system
        float FluPerAPoint = 10;    //how much 1 armor point is worth in terms of flux
        //float ReBaArmRate = .005f;  //how fast armor rebalanced  max 1 for instant
        float MaxFlux = .8f;        //cap for this system being active
        float RegenRate = .002f;    //rate armor regenerates as a decimal
        float activeRate = .005f;    //how fast active armor balancer works
        float MinHealthActive = .4f;//minimum health for active armor sharing
        float weaponsizemult = 1f;
        //weapon size multiplier
        WeaponAPI.WeaponSize weapsize = weaponAPI.getSize();
        if (weapsize.equals(WeaponAPI.WeaponSize.SMALL)) {weaponsizemult=.5f;}
        if (weapsize.equals(WeaponAPI.WeaponSize.MEDIUM)){weaponsizemult=1f;}
        if (weapsize.equals(WeaponAPI.WeaponSize.LARGE)) {weaponsizemult=2f;}

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

        //game is paused dont do anything  //weapon is disabled ""
        if (engine.isPaused() || weaponAPI.isDisabled())
        {
            return;
        }

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

        //if(ship.getSystem().isActive()) optional link to ship system
        {
            //advance timers
            slowtimer.advance(v);
            fasttimer.advance(v);

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

            //main code
            if (fasttimer.intervalElapsed())
            {
                //stuff that is used alot
                ArmorGridAPI armorgrid = ship.getArmorGrid();
                float armorrating = armorgrid.getArmorRating();
                float MaxCell = armorgrid.getMaxArmorInCell();

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

                //armor grid stats
                int maxX = armorgrid.getLeftOf()+armorgrid.getRightOf();
                int maxY = armorgrid.getAbove()+armorgrid.getBelow();

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

                //avarage armor of ship hull
                float armorcells = 0;               //number of cells ship has
                for (int X=0; X<maxX; X++){for (int Y=0; Y<maxY; Y++){armorcells++;}}
                //float ReBalArmor = curarmor/armorcells;

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

                //adjusted stats
                float adjust = weaponsizemult*Math.min(125 / armorcells, 4); //max increase of rate (prevents 100x rate on small ship)
                FluPerAPoint = 10;    //how much 1 armor point is worth in terms of flux
                //float ReBaArmRate = .005f;  //how fast armor rebalanced  max 1 for instant
                MaxFlux = .8f;        //cap for this system being active
                RegenRate = .002f*adjust;    //rate armor regenerates as a decimal
                activeRate = .005f*adjust;    //how fast active armor balancer works
                MinHealthActive = .4f*adjust;//minimum health for active armor sharing

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

                //basic armor state of ship
                float curarmor = getTotalArmor(ship);
                //float ArmLost = armorgrid.getArmorRating()-curarmor;  //how much armor was damaged

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

                //calculate regen rate based on flux (prevents cells from filling up sequentially at low flux)
                float FluxRemaining = (ship.getFluxTracker().getMaxFlux()*MaxFlux) - ship.getFluxTracker().getCurrFlux();
                //float FluxToRepairMax = ArmLost * FluPerAPoint;
                float NormRepPerFrame = (MaxCell * RegenRate)
                        *((MaxFlux-ship.getFluxTracker().getFluxLevel())/MaxFlux);//aditional level of repair decrease
                //float FluxToRepairNorm = NormRepPerFrame * FluPerAPoint * armorcells;
                //float FluxForRep = (FluxToRepairMax < FluxToRepairNorm ? FluxToRepairMax : FluxToRepairNorm);

                //easier, more accurate  (compares the cost to repair in last cycle to amount of flux left)
                if (lastcycle==0) {lastcycle=NormRepPerFrame*armorcells*FluPerAPoint;}
                float FluxForRep = lastcycle;
                float FluxToRepairNorm = lastcycle;
                float RepRate = (FluxForRep<FluxRemaining ? NormRepPerFrame:NormRepPerFrame*(FluxRemaining/FluxToRepairNorm));

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


                //armor manager
                float next=0;
                lastcycle=0; //clears lastcycle
                //active cycle (needs to be separate)
                for (int X=0; X<maxX; X++)       //
                {                                //cycle through all armor cells on ship
                    for (int Y=0; Y<maxY; Y++)   //
                    {
                        float cur = armorgrid.getArmorValue(X, Y); //health of current cell
                        //Active ReBalArmor
                        //mover armor from nearby cells to damaged ones
                        //can be tied to an if statement
                        {
                            //take armor of nearby healthy cells
                            float Forwardsum=0;
                            for (int Xa=(X==0? X:X-1); Xa<maxX && Xa>=0 && Xa<=X+1; Xa++)
                            {
                                for (int Ya=(Y==0? Y:Y-1); Ya<maxY && Ya>=0 && Ya<=Y+1; Ya++)
                                {
                                    float cell = armorgrid.getArmorValue(Xa, Ya);
                                    if (cell>cur && armorgrid.getArmorFraction(Xa, Ya)>MinHealthActive)
                                    {
                                        float diff = (cell - cur)*activeRate;
                                        next = (cell-diff);
                                        armorgrid.setArmorValue(Xa, Ya, next>0? next:0);
                                        Forwardsum+=diff;
                                        //ship.getFluxTracker().increaseFlux(diff*FluPerAPoint*.1f, true);
                                        //uses 1/10th of the normal flux to move armor around (too costly in flux)
                                    }
                                }
                            }
                            next = (cur + Forwardsum);
                            armorgrid.setArmorValue(X, Y, next<MaxCell? next:MaxCell); //add it to cell
                        }
                    }
                }

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


                //passive cycle
                for (int X=0; X<maxX; X++)       //
                {                                //cycle through all armor cells on ship
                    for (int Y=0; Y<maxY; Y++)   //
                    {
                        float cur = armorgrid.getArmorValue(X, Y); //health of current cell

                        //only do repair if cell health is more then 0 prevents immortal ship syndrome
                        if (cur>0)
                        {
                            //regen armor
                            if (cur<MaxCell)
                            {
                                next = cur + RepRate;          //how much armor should be regenerated


                                armorgrid.setArmorValue(X, Y, next<MaxCell? next:MaxCell);
                                float fluxuse = (next - cur) * FluPerAPoint;
                                ship.getFluxTracker().increaseFlux(fluxuse, true);
                                lastcycle+=fluxuse;
                            }
                        }

                    }
                }

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

                //test
                if (slowtimer.intervalElapsed())
                {
                engine.addFloatingText(ship.getLocation(), "armorcells  " + armorcells + " armorrating  " + armorrating + " curentarmor  " + curarmor +" MaxCell  "+ MaxCell +"  waeposizemult  "+weaponsizemult, 20f, Color.RED, ship, 1f, .5f);
                }

            }
        }
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////////

    //gets total armor of a ship
    public static float getTotalArmor(ShipAPI ship)
    {
        ArmorGridAPI armorgrid = ship.getArmorGrid();
        float sum=0;
        int maxX = armorgrid.getLeftOf()+armorgrid.getRightOf();
        int maxY = armorgrid.getAbove()+armorgrid.getBelow();

        for (int X=0; X<maxX; X++)
        {
            for (int Y=0; Y<maxY; Y++)
            {
                sum += armorgrid.getArmorValue(X, Y);
            }
        }
        return sum;
    }

}
[close]

Title: Re: The Radioactive Code Dump
Post by: dmaiski on November 10, 2013, 04:05:07 PM
 >:( hit post length limit  >:(

similar to the armor manager, but more simple
when installed onto a ship, if ship reaches 95% flux this will vent the flux without overloding ship or vent weapon disable effect
meant to be used with weapons that have limited ammo (intended as a single use emergency system)
Spoiler
Code: java
package data.scripts.plugins;



import com.fs.starfarer.api.Global;
import com.fs.starfarer.api.combat.*;
import com.fs.starfarer.api.util.IntervalUtil;

import java.awt.*;

/*
to be put on a weapon that will execute script on a ship
multiple weapons will increase the efficiency
 */
public class AuxilaryFluxVents implements EveryFrameWeaponEffectPlugin
{
    //engine
    private CombatEngineAPI engine= Global.getCombatEngine();

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

    //timers
    private IntervalUtil fasttimer = new IntervalUtil(.05f, .06f);
    private IntervalUtil slowtimer = new IntervalUtil(.15f, .16f);

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



    //main armor effect
    boolean fired=false;
    @Override
    public void advance(float v, CombatEngineAPI engineAPI, WeaponAPI weaponAPI)
    {
        //ship
        ShipAPI ship;
        ship=weaponAPI.getShip();

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


        //weapon size multiplier
        float weaponsizemult = 1;
        WeaponAPI.WeaponSize weapsize = weaponAPI.getSize();
        if (weapsize.equals(WeaponAPI.WeaponSize.SMALL)) {weaponsizemult=.5f;}
        if (weapsize.equals(WeaponAPI.WeaponSize.MEDIUM)){weaponsizemult=1f;}
        if (weapsize.equals(WeaponAPI.WeaponSize.LARGE)) {weaponsizemult=2f;}
        //stats of system
        float rate = .01f*weaponsizemult; //how fast vent happens (20x this number)
        float minflux = .1f; //lowest value of flux it can get to
        float maxflux = .95f;
        FluxTrackerAPI flux = ship.getFluxTracker();

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

        //game is paused dont do anything
        if (engine.isPaused())
        {
            return;
        }

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

        //if(ship.getSystem().isActive()) optional link to ship system
        {
            //advance timers
            //slowtimer.advance(v);
            fasttimer.advance(v);
            if (fasttimer.intervalElapsed())
            {
                //////////////////////////////////////////////////////////////////////////////////////////////

                //controls for flux vent
                if (!fired)  //if weapon has not fired
                {
                    //if flux is to high fire

                    if ((ship.getFluxTracker().getFluxLevel()>maxflux && weaponAPI.getAmmo()>0))
                    {weaponAPI.isFiring();fired=true;
                        weaponAPI.setRemainingCooldownTo(100);
                        weaponAPI.setAmmo(weaponAPI.getAmmo()-1);
                        flux.setCurrFlux(flux.getMaxFlux());}//forces all other systems on ship on
                    //record that you fired
                    if (weaponAPI.isFiring()){fired=true;}
                }

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


                //main vent code
                float FVsec = (flux.getMaxFlux()*.05f)*rate;
                if (fired && ship.getFluxTracker().getFluxLevel()>minflux)
                {
                    //prevents firing
                    weaponAPI.setRemainingCooldownTo(100);
                    {
                        flux.decreaseFlux(FVsec);
                    }
                }

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


                //if venting done
                if (fired && ship.getFluxTracker().getFluxLevel()<minflux)
                {
                    fired=false;
                    weaponAPI.setRemainingCooldownTo(0); //if now at minflux firing is allowed
                }

                //test
                slowtimer.advance(v);
                if (slowtimer.intervalElapsed())
                {
                    engine.addFloatingText(ship.getLocation(), "FVsec  " + FVsec + "rate  " + rate, 20f, Color.RED, ship, 1f, .5f);
            }
            }
        }
    }
}
[/spoiler]
[close]
Title: Re: The Radioactive Code Dump
Post by: silentstormpt on November 10, 2013, 05:15:13 PM
>:( hit post length limit  >:(

similar to the armor manager, but more simple
when installed onto a ship, if ship reaches 95% flux this will vent the flux without overloding ship or vent weapon disable effect
meant to be used with weapons that have limited ammo (intended as a single use emergency system)
Spoiler
Code: java
package data.scripts.plugins;



import com.fs.starfarer.api.Global;
import com.fs.starfarer.api.combat.*;
import com.fs.starfarer.api.util.IntervalUtil;

import java.awt.*;

/*
to be put on a weapon that will execute script on a ship
multiple weapons will increase the efficiency
 */
public class AuxilaryFluxVents implements EveryFrameWeaponEffectPlugin
{
    //engine
    private CombatEngineAPI engine= Global.getCombatEngine();

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

    //timers
    private IntervalUtil fasttimer = new IntervalUtil(.05f, .06f);
    private IntervalUtil slowtimer = new IntervalUtil(.15f, .16f);

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



    //main armor effect
    boolean fired=false;
    @Override
    public void advance(float v, CombatEngineAPI engineAPI, WeaponAPI weaponAPI)
    {
        //ship
        ShipAPI ship;
        ship=weaponAPI.getShip();

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


        //weapon size multiplier
        float weaponsizemult = 1;
        WeaponAPI.WeaponSize weapsize = weaponAPI.getSize();
        if (weapsize.equals(WeaponAPI.WeaponSize.SMALL)) {weaponsizemult=.5f;}
        if (weapsize.equals(WeaponAPI.WeaponSize.MEDIUM)){weaponsizemult=1f;}
        if (weapsize.equals(WeaponAPI.WeaponSize.LARGE)) {weaponsizemult=2f;}
        //stats of system
        float rate = .01f*weaponsizemult; //how fast vent happens (20x this number)
        float minflux = .1f; //lowest value of flux it can get to
        float maxflux = .95f;
        FluxTrackerAPI flux = ship.getFluxTracker();

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

        //game is paused dont do anything
        if (engine.isPaused())
        {
            return;
        }

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

        //if(ship.getSystem().isActive()) optional link to ship system
        {
            //advance timers
            //slowtimer.advance(v);
            fasttimer.advance(v);
            if (fasttimer.intervalElapsed())
            {
                //////////////////////////////////////////////////////////////////////////////////////////////

                //controls for flux vent
                if (!fired)  //if weapon has not fired
                {
                    //if flux is to high fire

                    if ((ship.getFluxTracker().getFluxLevel()>maxflux && weaponAPI.getAmmo()>0))
                    {weaponAPI.isFiring();fired=true;
                        weaponAPI.setRemainingCooldownTo(100);
                        weaponAPI.setAmmo(weaponAPI.getAmmo()-1);
                        flux.setCurrFlux(flux.getMaxFlux());}//forces all other systems on ship on
                    //record that you fired
                    if (weaponAPI.isFiring()){fired=true;}
                }

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


                //main vent code
                float FVsec = (flux.getMaxFlux()*.05f)*rate;
                if (fired && ship.getFluxTracker().getFluxLevel()>minflux)
                {
                    //prevents firing
                    weaponAPI.setRemainingCooldownTo(100);
                    {
                        flux.decreaseFlux(FVsec);
                    }
                }

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


                //if venting done
                if (fired && ship.getFluxTracker().getFluxLevel()<minflux)
                {
                    fired=false;
                    weaponAPI.setRemainingCooldownTo(0); //if now at minflux firing is allowed
                }

                //test
                slowtimer.advance(v);
                if (slowtimer.intervalElapsed())
                {
                    engine.addFloatingText(ship.getLocation(), "FVsec  " + FVsec + "rate  " + rate, 20f, Color.RED, ship, 1f, .5f);
            }
            }
        }
    }
}
[/spoiler]
[close]
Heres a challenge, make a AI and a hull mod or ship system that the higher the flux amount, the more damage it does with its weapons, the AI should try to get as close as it can get to overloading to maximize damage output, this also means it will pull shields and fire to get flux fast then turns into a suicidal berserker that ignores defense to do damage instead.
Title: Re: The Radioactive Code Dump
Post by: dmaiski on November 10, 2013, 05:27:57 PM
not really that easy, needs to override the ships main AI somehow, and thats too much work since you would need to write the whole thing from scratch

look at mutable ship stats, its posible to make shipwide weapon boost that is based on flux,
an ai for that is just a flux tracker and a if(flux>x) ship.useSystem()

example of such
Spoiler
rudimentary AI for a system
implement with:
"aiScript":"data.shipsystems.scripts.ai.ArmorAI",
in .system file
Spoiler
Code: java
package data.shipsystems.scripts.ai;

import com.fs.starfarer.api.combat.*;
import com.fs.starfarer.api.util.IntervalUtil;
import org.lwjgl.util.vector.Vector2f;



public class ExampleAI implements ShipSystemAIScript {

    private ShipAPI ship;
    private CombatEngineAPI engine;
    private ShipwideAIFlags flags;
    private ShipSystemAPI system;

    private IntervalUtil tracker = new IntervalUtil(0.5f, 1f);

    public void init(ShipAPI ship, ShipSystemAPI system, ShipwideAIFlags flags, CombatEngineAPI engine) {
        this.ship = ship;
        this.flags = flags;
        this.engine = engine;
        this.system = system;
    }

    @SuppressWarnings("unchecked")
    public void advance(float amount, Vector2f missileDangerDir, Vector2f collisionDangerDir, ShipAPI target)
    {
        if (ship.getShield().isOn())
        {ship.useSystem();}

    }
}
[close]

example stat mod
Spoiler
Code: java
package data.shipsystems.scripts;


import com.fs.starfarer.api.combat.MutableShipStatsAPI;
import com.fs.starfarer.api.plugins.ShipSystemStatsScript;

public class Example implements ShipSystemStatsScript {

    public void apply(MutableShipStatsAPI stats, String id, State state, float effectLevel)
    {
        stats.getShieldArcBonus().modifyMult(id, 360);
    }

    public void unapply(MutableShipStatsAPI stats, String id)
    {
        stats.getShieldArcBonus().unmodify(id);
    }

    public StatusData getStatusData(int index, State state, float effectLevel) {
        if (index == 0) {
            return new StatusData("dose x now", false);
        }

        return null;
    }
}
[close]
[close]
Title: Re: The Radioactive Code Dump
Post by: etherealblade on November 18, 2013, 12:57:28 AM
DRONE missiles
stays at a set distance and blasts the enemy ships appart
who needs fighters anyways?


Spoiler
Code: java
int timer = 0;  //needs to be outside of advance() to work as a timer
int timer2 = (int) (600*Math.random());  //random start point for strafing
public void advance(float amount)
        {
        timer++;

        if (MathUtils.getDistance(
                add(target.getLocation(), target.getVelocity(), null),              //blow up the enemy ship
                add(missile.getLocation(), multV2f(missile.getVelocity(),3), null))
                < 600
                && MathUtils.getDistance(
                missile.getLocation(),                     //don't blow up your own ship
                missile.getSource().getLocation())
                >missile.getSource().getCollisionRadius()+5
                && timer==1
                )
        {
            timer++;
                CombatEngine engine = CombatEngine.getInstance();          //engine

                String MBRC_p = "MBRC2";      //dummy weapon

                {

                    {
                        int counts = 1;
                        do {
                            float angRAND= (float) ((25*(.5-Math.random())));     //angle of spread
                            float velRAND= (float) (1+.5*(.5-Math.random()));      //variance of projectile speeds
                            float splashVEL = 255f*velRAND;    //speed of bullets launched
                            float misFFACE = missile.getFacing()-angRAND;
                            float x = (float) (splashVEL*Math.cos(Math.toRadians(misFFACE)));
                            float y = (float) (splashVEL*Math.sin(Math.toRadians(misFFACE)));
                            Vector2f vecFIRE = new Vector2f(x,y);
                            engine.spawnProjectile(null, null,MBRC_p,
                                    missile.getLocation(),          //Vector2f firing point
                                    misFFACE,            //float   angle of spread
                                    add(missile.getVelocity(), vecFIRE, null)

                                    //multV2f(multV2f(missile.getVelocity(),1),velRAND)           //Vector2f  aditional velocity
                                    //add(multV2f(missile.getVelocity(), 1), multRanV2f(missile.getVelocity(),1,.5f),null)
                            );
                            counts++;
                        }while (counts<2);         //2x pew pew


                        //engine.removeEntity(missile); //make missile go poof
                    }
                    //lightning
                    // float emp = missile.getEmpAmount();
                    //float dam = missile.getDamageAmount();

                    //engine.spawnEmpArc(missile.getSource(), missile.getLocation(), target, target,
                    // DamageType.ENERGY,dam/4, emp/4,
                    //10000f, "tachyon_lance_emp_impact",20f, new Color(255,10,15,255),new Color(255,100,100,255));
                }
                //to stop missile shooting again
                //engine.removeEntity(missile);   //make missile go poof

                //missile.flameOut();               //make missile flame out
                return;
        }
        if (timer>40)
        {timer=0;}
        
        //missile pathing code
        if (Math.abs(angularDistance) < 100      //get in close
                && MathUtils.getDistance(missile.getLocation(), target.getLocation())
                >320+100/target.getCollisionRadius()*target.getCollisionRadius())
        {
            missile.giveCommand(ShipCommand.ACCELERATE);
        }
        if (Math.abs(angularDistance) < 100     //keep distance
                && MathUtils.getDistance(missile.getLocation(), target.getLocation())
                <280+100/target.getCollisionRadius()*target.getCollisionRadius())
        {
            missile.giveCommand(ShipCommand.ACCELERATE_BACKWARDS);
        }

        if (Math.abs(angularDistance) < 100   //strafe
                && MathUtils.getDistance(missile.getLocation(), target.getLocation())<400+target.getCollisionRadius())
        {
            missile.giveCommand(timer2 > 300 ? ShipCommand.STRAFE_LEFT : ShipCommand.STRAFE_RIGHT);
        }
        timer2++;
        if (timer2>600)
        {
            timer2=0;
        }
        }
[close]

Where do I put this juicy goodness in my weapons files? Add it as a missile? or to the weapon itself?
Title: Re: The Radioactive Code Dump
Post by: dmaiski on November 18, 2013, 08:44:50 AM
Where do I put this juicy goodness in my weapons files? Add it as a missile? or to the weapon itself?

same way as previous, all missile ai use this unless otherwhise stated
Spoiler
to implement into a mod:
place the .java(ie code) file in the mod(does not mater where)
in mod plugin:
Code: (java)
view plaincopy to clipboardprint?

        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; 
        } 
[close]

you may need to tack it onto a target getter and misc wrapper from some of the missile AI that i have posted recently
(i have not gotten around to re-writing that one for it to be just a copy>paste implement job yet)
if you can wait till thursday ill probably have it done up by then, atm too much other stuff I am doing


on that note, MINEai
(makes mines that shoot projectiles or missiles in 360 deg ark)
basic components of other mirv type missile mounted weapon AI
a number of improvments and simplifications over old version
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 MINEai implements MissileAIPlugin
{
    // Our missile object
    private final MissileAPI missile;

    private final float MaxDetRange=250;     //max range of the blast
    private final float MinDetRange=100;     //max range for best damage
    private final float SeperationRange=350; //seperation between mines

    public MINEai(MissileAPI missile)
    {
        this.missile = missile;
    }

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


    //stuff
    float range1 = 0;
    float range2 = 0;
    boolean trigger = false;
    boolean deathcode = false;
    private IntervalUtil minetimer = new IntervalUtil(.1f, .11f);
    ShipAPI enemy=null;

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

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

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

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


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

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

        //housekeeping stuff
        minetimer.advance(amount);                         //timer
        CombatEngineAPI engine = Global.getCombatEngine(); //engine

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

        //missile location (used lots)
        Vector2f MLoc = missile.getLocation();

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


        //intelligent distribution code
        MissileAPI near = AIUtils.getNearestMissile(missile);  //find the nearest missile to you

        if (near != null                                       //check if there are any nearby mines
                && near.getProjectileSpecId().equals(missile.getProjectileSpecId())    //is it same type as you
                && MathUtils.getDistance(near, MLoc) < SeperationRange)             //is it too close
        {
            //if true move away from them
            float angularDistance = getAngleDifference(       //find the missile relative direction
                    missile.getFacing(), MathUtils.getAngle(MLoc,
                    (near.getLocation())));
            if (Math.abs(angularDistance) < 175)             //facing towards it
            {
                missile.giveCommand(angularDistance < 0f
                        ? ShipCommand.TURN_LEFT : ShipCommand.TURN_RIGHT);   //turn away
            }
            if (Math.abs(angularDistance) > 135)                             //if facing away from it
            {
                missile.giveCommand(ShipCommand.ACCELERATE);                 //then move forwards
            }
            if (Math.abs(angularDistance) < 45)                              //facing towards it
            {
                missile.giveCommand(ShipCommand.ACCELERATE_BACKWARDS);       //then move backwards
            }
        }
        else    //if enough separation achieved
        {
            missile.giveCommand(ShipCommand.DECELERATE);              //mine dose not move
        }

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


        //enemy tracking code
        if (minetimer.intervalElapsed()) //run once every 3 frames to prevent fun stuff
        {
            /////////////////////////////////////////////////////////////////////////////////////////////////////////


            //new target aquisition
            //if you have no enemy, look for the nearest enemy
            if (enemy==null && AIUtils.getNearestEnemy(missile)!=null && AIUtils.getNearestEnemy(missile).isAlive())
            {
                //check its in range
                float targetfinder = MathUtils.getDistance(AIUtils.getNearestEnemy(missile), MLoc);
                if (targetfinder < 300)
                {
                    enemy=AIUtils.getNearestEnemy(missile);                //remember that enemy
                }
                if (targetfinder > 300)                                    //else restart
                {
                    return;
                }

            }

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

            //once you have a target
            if (enemy != null)
            {
                //////////////////////////////////////////////////////////////////////////////////////////////////////


                //housekeeping/misc
                //check its the same enemy as previous
                if (enemy!=AIUtils.getNearestEnemy(missile))
                {
                    deathcode=true;      //if a second enemy comes closer then first
                    trigger=true;        //detonate
                }

                //check enemy has not died
                if (!enemy.isAlive() |  enemy.isHulk())
                {
                    enemy=null;      //if tracking enemy is dead, reset
                    range1=0;
                    deathcode=false;
                    trigger=false;
                }

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

                //rangetracker (part1)
                //tracks how close enemy is (predicts where the enemy will be next frame to get its motion)
                if (!deathcode && range1==0 && enemy!=null
                        && MathUtils.getDistance(enemy, MLoc)< MaxDetRange*.9f)  //find out if there are enemy in range
                {
                    range1 = MathUtils.getDistance(enemy, MLoc);     //store range to target
                }
            }

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


            if (!deathcode && range1!=0)    //for all grouped in here
            {
                //when enemy is close enough go boom
                if (range1<MinDetRange)
                {
                    trigger=true;       //make it go boom
                    deathcode=true;     //stop tracking
                }

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


                //rangetracker (cont)
                //find get the range to target now
                range2 = MathUtils.getDistance(enemy,MLoc);


                if (range1>range2)
                {
                    //predict its motion based on velocity
                    float range3 = MathUtils.getDistance(Vector2f.add(
                            enemy.getLocation(), multV2f(enemy.getVelocity(), .5f), null), // its velocity
                            MLoc)-enemy.getCollisionRadius();             //distance to missile

                    //compare new range to curent range
                    if (range1<range3)   //if it mover away explode
                    {
                        trigger=true;
                        deathcode=true;
                        return;
                    }
                    else {range1=range2;}               //store new target range, if it got closer
                }
                else//if its moving away
                {
                    trigger=true;       //make it go boom
                    deathcode=true;     //stop tracking
                }

                //if damaged below 90%hp while in range, detonate
                if (missile.getMaxHitpoints()*0.9>missile.getHitpoints())
                {
                    trigger=true;
                    deathcode=true;
                }
            }

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


            //explosion code
            if (trigger && MathUtils.getDistance(
                    missile,AIUtils.getNearestAlly(missile))>MaxDetRange*.8f)//don't blow up your own ships)
            {
                //for projectiles
                {
                    //dummy weapon  supports missiles and projectiles
                    //suggested projectile stats, range:1,  speed:1
                    String Projectile = "DUMMY1";
                    {
                        {
                            //spawn projectiles
                            int counts=0;
                            do {
                                float angRAND= (float) ((360*(.5-Math.random()))); //angle of spread
                                float speedRAND= (float) ((MaxDetRange-MinDetRange)*Math.random());//variance of speed
                                float PSpeed = MinDetRange+speedRAND;             //speed of bullets launched
                                float PAng = missile.getFacing()-angRAND;         //direction of fire
                                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(missile.getVelocity(), PVec, null); //true Pvec
                                engine.spawnProjectile(
                                        missile.getSource(),
                                        missile.getWeapon(),             //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,
                                        15f,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<100); //how many projectiles?
                        }
                    }
                    //////////////////////////////////////////////////////////////////////////////////

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

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

                    //to stop missile shooting again
                    engine.removeEntity(missile);   //make missile go poof
                    //missile.flameOut();               //make missile flame out
                    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;
    }

    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////
}
[close]
Title: Re: The Radioactive Code Dump
Post by: etherealblade on November 19, 2013, 11:10:27 PM
Patience is a virtue I like to strive to live by. I'd be happy whenever your got around to it. 8D
Title: Re: The Radioactive Code Dump
Post by: dmaiski 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
(http://i.imgur.com/6ohCIlO.png?1)
[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
Title: Re: The Radioactive Code Dump
Post by: ValkyriaL on November 20, 2013, 03:19:32 PM
Nothing Valkyrian PD can't handle. >.>
Title: Re: The Radioactive Code Dump
Post by: MesoTroniK on November 20, 2013, 03:49:16 PM
Dammit man, to think I held the crown for the largest (and balanced) missile spam.
Title: Re: The Radioactive Code Dump
Post by: dmaiski 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
(http://i.imgur.com/c2wsfhg.png?1)
basicaly vanilla MIRV
[close]

we heard you like missiles, so we made missiles, that shoot missiles
(Must See troll image)
Spoiler
(http://i.imgur.com/c4YNyU7.png?1)
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
Title: Re: The Radioactive Code Dump
Post by: dmaiski 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
(http://i.imgur.com/l8OCwb8.png?1)
[close]
less sexy harpoons shooting whatever they have in their vast arsenal
Spoiler
(http://i.imgur.com/W1zo6t4.png?1)
[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
Title: Re: The Radioactive Code Dump
Post by: dmaiski 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
Title: Re: The Radioactive Code Dump
Post by: dmaiski 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\ (https://www.dropbox.com/s/zp97r3guz18evs0/UtilityKit.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)
Title: Re: The Radioactive Code Dump
Post by: xenoargh 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]
Title: Re: The Radioactive Code Dump
Post by: dmaiski 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)
Title: Re: The Radioactive Code Dump
Post by: xenoargh 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]
Title: Re: The Radioactive Code Dump
Post by: xenoargh 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]
Title: Re: The Radioactive Code Dump
Post by: dmaiski 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
Title: Re: The Radioactive Code Dump
Post by: xenoargh 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]
Title: Re: The Radioactive Code Dump
Post by: dmaiski 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]
Title: Re: The Radioactive Code Dump
Post by: xenoargh 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.
Title: Re: The Radioactive Code Dump
Post by: dmaiski 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]
Title: Re: The Radioactive Code Dump
Post by: xenoargh 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.
Title: Re: The Radioactive Code Dump
Post by: xenoargh on January 08, 2014, 06:56:02 AM
Minor fix for above; adds specific Cloak detection :)
Title: Re: The Radioactive Code Dump
Post by: xenoargh 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:


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]
Title: Re: The Radioactive Code Dump
Post by: xenoargh 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.
Title: Re: The Radioactive Code Dump
Post by: Debido 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.
Title: Re: The Radioactive Code Dump
Post by: InfinitySquared 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 "com.fs.starfarer.loading.super$1@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
@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.
Title: Re: The Radioactive Code Dump
Post by: Debido 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.
Title: Re: The Radioactive Code Dump
Post by: InfinitySquared 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?
Title: Re: The Radioactive Code Dump
Post by: Debido on February 09, 2014, 09:08:24 PM
Yes*
Title: Re: The Radioactive Code Dump
Post by: InfinitySquared 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)

Title: Re: The Radioactive Code Dump
Post by: kazi 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).
Title: Re: The Radioactive Code Dump
Post by: InfinitySquared 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.
Title: Re: The Radioactive Code Dump
Post by: etherealblade 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?
Title: Re: The Radioactive Code Dump
Post by: xenoargh 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.
Title: Re: The Radioactive Code Dump
Post by: xenoargh on March 19, 2014, 08:44:28 PM
Are you one of those slightly... compulsive people who wants your asymmetric weapons designs, with their ultra-cool looks, to be properly mirrored on your ship, so that symmetrical ships look symmetrical and orderly, even if the weapon sprites aren't?

This script is for you :)

Code: java
package data.scripts.sfx;
import com.fs.starfarer.api.combat.CombatEngineAPI;
import com.fs.starfarer.api.combat.EveryFrameWeaponEffectPlugin;
import com.fs.starfarer.api.combat.WeaponAPI;
import com.fs.starfarer.api.graphics.SpriteAPI;

public class weaponSide implements EveryFrameWeaponEffectPlugin {
    private boolean runOnce = false;
    
    @Override
    public void advance(float amount, CombatEngineAPI engine, WeaponAPI weapon) {
        
        if (engine.isPaused()) return;
        if(runOnce == false){
            if (weapon.getShip().getOwner() == 0 && weapon.getLocation().getX() < weapon.getShip().getLocation().getX()
                || weapon.getShip().getOwner() == 1 && weapon.getLocation().getX() > weapon.getShip().getLocation().getX()){
                SpriteAPI theSprite = weapon.getSprite();
                theSprite.setWidth(-theSprite.getWidth());
                theSprite.setCenter(-theSprite.getCenterX(),theSprite.getCenterY());
            }
            runOnce = true;
        }
    }
}

Warning: it works only for weapons that have one barrel, centered on the X axis, or two, four, six eight, etc where the barrels are symmetrical XY from the center, or odd-numbered barrels where the right and left barrels are mirrored XY... or, yeah.  Basically, the weapon's barrels and glows need to be symmetrical, even if the sprite isn't.

Will bork with practically anything else, I suspect.  I will probably make an API request to do this engine-side, but for now...
Title: Re: The Radioactive Code Dump
Post by: dmaiski on March 22, 2014, 04:03:43 PM
Highly Radioactive Code

BEWARE (https://www.dropbox.com/s/75rcorkctpdxmke/tHE%20mAD%20OLd%20DaYS.rar)

all my old stuff from way back when(v0.61a) it should all be working (i think) and comented (again, i think)
contains all the stuff i posted here plus alot of experimental stuff i never released/got to work properly/left till later/was just testing/(ect...)

(what i think should be in there)
4 whole mods:
hell crab: contains a bunch of experimental stuff that i was fiddling with (weeapon slot mountable ship systems, armor regeneration)
rusters: testbed for even more experimental stuff i was playing with (all the missile AI i ever made, weird weapons, stuff)
biso test: pre release version of the BISO mod (alot of weapon systems that did intresting stuff)
biso branch: scraps that was branched off of test then abandoned (old outdated, just there for posterity)
Title: Re: The Radioactive Code Dump
Post by: FasterThanSleepyfish on March 22, 2014, 05:05:08 PM
Ambershit, you make no sense. #Bot?
Title: Re: The Radioactive Code Dump
Post by: Lcu on March 23, 2014, 03:45:59 AM
Why do people call him Ambershit?
Title: Re: The Radioactive Code Dump
Post by: dmaiski on March 23, 2014, 03:56:44 AM
various stuff(some of it uses the utility kit i posted here earlier)

armor regenerator
1st cycle: damaged cells take armor from healthy cells to repair themselves
2nd cycle: "alive" cells repair themselves based on the amount of HP they have
Spoiler
Code: java
package data.scripts.plugins;



import com.fs.starfarer.api.Global;
import com.fs.starfarer.api.combat.*;
import com.fs.starfarer.api.util.IntervalUtil;

import java.awt.*;

/*
to be put on a weapon that will execute script on a ship
multiple weapons will increase the efficiency
 */
public class AbsorptionArmor implements EveryFrameWeaponEffectPlugin
{
    //engine
    private CombatEngineAPI engine= Global.getCombatEngine();

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

    //timers
    private IntervalUtil fasttimer = new IntervalUtil(.1f, .11f);
    private IntervalUtil slowtimer = new IntervalUtil(.15f, .16f);

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



    //main armor effect
    float lastcycle=0;
    @Override
    public void advance(float v, CombatEngineAPI engineAPI, WeaponAPI weaponAPI)
    {
        //ship
        ShipAPI ship;
        ship=weaponAPI.getShip();

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

        //stats of system
        float FluPerAPoint = 10;    //how much 1 armor point is worth in terms of flux
        //float ReBaArmRate = .005f;  //how fast armor rebalanced  max 1 for instant
        float MaxFlux = .8f;        //cap for this system being active
        float RegenRate = .002f;    //rate armor regenerates as a decimal
        float activeRate = .005f;    //how fast active armor balancer works
        float MinHealthActive = .4f;//minimum health for active armor sharing
        float weaponsizemult = 1f;
        //weapon size multiplier
        WeaponAPI.WeaponSize weapsize = weaponAPI.getSize();
        if (weapsize.equals(WeaponAPI.WeaponSize.SMALL)) {weaponsizemult=.5f;}
        if (weapsize.equals(WeaponAPI.WeaponSize.MEDIUM)){weaponsizemult=1f;}
        if (weapsize.equals(WeaponAPI.WeaponSize.LARGE)) {weaponsizemult=2f;}

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

        //game is paused dont do anything  //weapon is disabled ""
        if (engine.isPaused() || weaponAPI.isDisabled())
        {
            return;
        }

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

        //if(ship.getSystem().isActive()) optional link to ship system
        {
            //advance timers
            slowtimer.advance(v);
            fasttimer.advance(v);

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

            //main code
            if (fasttimer.intervalElapsed())
            {
                //stuff that is used alot
                ArmorGridAPI armorgrid = ship.getArmorGrid();
                float armorrating = armorgrid.getArmorRating();
                float MaxCell = armorgrid.getMaxArmorInCell();

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

                //armor grid stats
                int maxX = armorgrid.getLeftOf()+armorgrid.getRightOf();
                int maxY = armorgrid.getAbove()+armorgrid.getBelow();

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

                //avarage armor of ship hull
                float armorcells = 0;               //number of cells ship has
                for (int X=0; X<maxX; X++){for (int Y=0; Y<maxY; Y++){armorcells++;}}
                //float ReBalArmor = curarmor/armorcells;

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

                //adjusted stats
                float adjust = weaponsizemult*Math.min(125 / armorcells, 4); //max increase of rate (prevents 100x rate on small ship)
                FluPerAPoint = 10;    //how much 1 armor point is worth in terms of flux
                //float ReBaArmRate = .005f;  //how fast armor rebalanced  max 1 for instant
                MaxFlux = .8f;        //cap for this system being active
                RegenRate = .002f*adjust;    //rate armor regenerates as a decimal
                activeRate = .005f*adjust;    //how fast active armor balancer works
                MinHealthActive = .4f*adjust;//minimum health for active armor sharing

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

                //basic armor state of ship
                float curarmor = getTotalArmor(ship);
                //float ArmLost = armorgrid.getArmorRating()-curarmor;  //how much armor was damaged

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

                //calculate regen rate based on flux (prevents cells from filling up sequentially at low flux)
                float FluxRemaining = (ship.getFluxTracker().getMaxFlux()*MaxFlux) - ship.getFluxTracker().getCurrFlux();
                //float FluxToRepairMax = ArmLost * FluPerAPoint;
                float NormRepPerFrame = (MaxCell * RegenRate)
                        *((MaxFlux-ship.getFluxTracker().getFluxLevel())/MaxFlux);//aditional level of repair decrease
                //float FluxToRepairNorm = NormRepPerFrame * FluPerAPoint * armorcells;
                //float FluxForRep = (FluxToRepairMax < FluxToRepairNorm ? FluxToRepairMax : FluxToRepairNorm);

                //easier, more accurate  (compares the cost to repair in last cycle to amount of flux left)
                if (lastcycle==0) {lastcycle=NormRepPerFrame*armorcells*FluPerAPoint;}
                float FluxForRep = lastcycle;
                float FluxToRepairNorm = lastcycle;
                float RepRate = (FluxForRep<FluxRemaining ? NormRepPerFrame:NormRepPerFrame*(FluxRemaining/FluxToRepairNorm));

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


                //armor manager
                float next=0;
                lastcycle=0; //clears lastcycle
                //active cycle (needs to be separate)
                //lets damaged cells take armor from nearby healthy cells
                for (int X=0; X<maxX; X++)       //
                {                                //cycle through all armor cells on ship
                    for (int Y=0; Y<maxY; Y++)   //
                    {
                        float cur = armorgrid.getArmorValue(X, Y); //health of current cell
                        //Active ReBalArmor
                        //mover armor from nearby cells to damaged ones
                        //can be tied to an if statement
                        {
                            //take armor from nearby healthy cells
                            float Forwardsum=0;
                            for (int Xa=(X==0? X:X-1); Xa<maxX && Xa>=0 && Xa<=X+1; Xa++)
                            {
                                for (int Ya=(Y==0? Y:Y-1); Ya<maxY && Ya>=0 && Ya<=Y+1; Ya++)
                                {
                                    float cell = armorgrid.getArmorValue(Xa, Ya);
                                    if (cell>cur && armorgrid.getArmorFraction(Xa, Ya)>MinHealthActive)
                                    {
                                        float diff = (cell - cur)*activeRate;
                                        next = (cell-diff);
                                        armorgrid.setArmorValue(Xa, Ya, next>0? next:0);
                                        cur+=diff;
                                        //ship.getFluxTracker().increaseFlux(diff*FluPerAPoint*.1f, true);
                                        //uses 1/10th of the normal flux to move armor around (too costly in flux)
                                    }
                                }
                            }
                            next = cur;
                            armorgrid.setArmorValue(X, Y, next<MaxCell? next:MaxCell); //add it to cell
                        }
                    }
                }

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


                //passive cycle
                for (int X=0; X<maxX; X++)       //
                {                                //cycle through all armor cells on ship
                    for (int Y=0; Y<maxY; Y++)   //
                    {
                        float cur = armorgrid.getArmorValue(X, Y); //health of current cell

                        //only do repair if cell health is more then 0 prevents immortal ship syndrome
                        if (cur>0)
                        {
                            //regen armor
                            if (cur<MaxCell)
                            {
                                next = cur + (cur/MaxCell)*RepRate;          //how much armor should be regenerated


                                armorgrid.setArmorValue(X, Y, next<MaxCell? next:MaxCell);
                                float fluxuse = (next - cur) * FluPerAPoint;
                                ship.getFluxTracker().increaseFlux(fluxuse, true);
                                lastcycle+=fluxuse;
                            }
                        }

                    }
                }


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

                //test
                if (slowtimer.intervalElapsed())
                {
                engine.addFloatingText(ship.getLocation(), "armorcells  " + armorcells + " armorrating  " + armorrating + " curentarmor  " + curarmor +" MaxCell  "+ MaxCell +"  waeposizemult  "+weaponsizemult, 20f, Color.RED, ship, 1f, .5f);
                }

            }
        }
    }
    ////////////////////////////////////////////////////////////////////////////////////////////////////

    //gets total armor of a ship
    public static float getTotalArmor(ShipAPI ship)
    {
        ArmorGridAPI armorgrid = ship.getArmorGrid();
        float sum=0;
        int maxX = armorgrid.getLeftOf()+armorgrid.getRightOf();
        int maxY = armorgrid.getAbove()+armorgrid.getBelow();

        for (int X=0; X<maxX; X++)
        {
            for (int Y=0; Y<maxY; Y++)
            {
                sum += armorgrid.getArmorValue(X, Y);
            }
        }
        return sum;
    }

}
[close]

Auxilary flux vents
can be mounted into weapon slots on a ship
use ammo to disperse flux
highly experimental (broken, more or less)
Spoiler
Code: java
package data.scripts.plugins;



import com.fs.starfarer.api.Global;
import com.fs.starfarer.api.combat.*;
import com.fs.starfarer.api.util.IntervalUtil;

import java.awt.*;

/*
to be put on a weapon that will execute script on a ship
multiple weapons will increase the efficiency
 */
public class AuxilaryFluxVents implements EveryFrameWeaponEffectPlugin
{
    //engine
    private CombatEngineAPI engine= Global.getCombatEngine();

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

    //timers
    private IntervalUtil fasttimer = new IntervalUtil(.05f, .06f);
    private IntervalUtil slowtimer = new IntervalUtil(.15f, .16f);

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



    //main armor effect
    boolean fired=false;
    @Override
    public void advance(float v, CombatEngineAPI engineAPI, WeaponAPI weaponAPI)
    {
        //ship
        ShipAPI ship;
        ship=weaponAPI.getShip();

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


        //weapon size multiplier
        float weaponsizemult = 1;
        WeaponAPI.WeaponSize weapsize = weaponAPI.getSize();
        if (weapsize.equals(WeaponAPI.WeaponSize.SMALL)) {weaponsizemult=.5f;}
        if (weapsize.equals(WeaponAPI.WeaponSize.MEDIUM)){weaponsizemult=1f;}
        if (weapsize.equals(WeaponAPI.WeaponSize.LARGE)) {weaponsizemult=2f;}
        //stats of system
        float rate = .01f*weaponsizemult; //how fast vent happens (20x this number)
        float minflux = .1f; //lowest value of flux it can get to
        float maxflux = .95f;
        FluxTrackerAPI flux = ship.getFluxTracker();

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

        //game is paused dont do anything
        if (engine.isPaused())
        {
            return;
        }

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

        //if(ship.getSystem().isActive()) optional link to ship system
        {
            //advance timers
            //slowtimer.advance(v);
            fasttimer.advance(v);
            if (fasttimer.intervalElapsed())
            {
                //////////////////////////////////////////////////////////////////////////////////////////////

                //controls for flux vent
                if (!fired)  //if weapon has not fired
                {
                    //if flux is to high fire

                    if ((ship.getFluxTracker().getFluxLevel()>maxflux && weaponAPI.getAmmo()>0))
                    {weaponAPI.isFiring();fired=true;
                        weaponAPI.setRemainingCooldownTo(100);
                        weaponAPI.setAmmo(weaponAPI.getAmmo()-1);
                        flux.setCurrFlux(flux.getMaxFlux());}//forces all other systems on ship on
                    //record that you fired
                    if (weaponAPI.isFiring()){fired=true;}
                }

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


                //main vent code
                float FVsec = (flux.getMaxFlux()*.05f)*rate;
                if (fired && ship.getFluxTracker().getFluxLevel()>minflux)
                {
                    //prevents firing
                    weaponAPI.setRemainingCooldownTo(100);
                    {
                        flux.decreaseFlux(FVsec);
                    }
                }

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


                //if venting done
                if (fired && ship.getFluxTracker().getFluxLevel()<minflux)
                {
                    fired=false;
                    weaponAPI.setRemainingCooldownTo(0); //if now at minflux firing is allowed
                }

                //test
                slowtimer.advance(v);
                if (slowtimer.intervalElapsed())
                {
                    engine.addFloatingText(ship.getLocation(), "FVsec  " + FVsec + "rate  " + rate, 20f, Color.RED, ship, 1f, .5f);
            }
            }
        }
    }
}
[close]

Grovotron 3000
gravaton modifier beam
send anything that is not bolted to the ground flying in the direction the beam is pointing (at great speeds)
great if your ship has slow weapons like reapers
Spoiler
Code: java
package data.scripts.plugins;

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.lwjgl.util.vector.Vector2f;

import java.awt.*;
import java.util.ArrayList;

import static jar.UtilityKit.V2fPerpendicularDisc;
import static jar.UtilityKit.V2fReSize;
import static org.lwjgl.util.vector.Vector2f.add;
import static org.lwjgl.util.vector.Vector2f.sub;


public class Gravotron implements BeamEffectPlugin {

    private IntervalUtil Interval = new IntervalUtil(0.02f, 0.01f);
    float MaxPoint=400;

    public void advance(float amount, CombatEngineAPI engine, BeamAPI beam)
    {
        if (beam.getBrightness() >= .5f) {
            Interval.advance(amount);

            if (Interval.intervalElapsed()) {
                Vector2f from = beam.getFrom();
                Vector2f to = beam.getTo();
                Vector2f dir = sub(to, from, null);
                float Bang = MathUtils.getFacing(dir);

                ArrayList<CombatEntityAPI> PQueries = new ArrayList<CombatEntityAPI>();
                PQueries.addAll(engine.getAsteroids());
                PQueries.addAll(engine.getProjectiles());

                for (CombatEntityAPI query : PQueries)
                {
                    Vector2f QLoc = query.getLocation();
                    float DtoL = V2fDistanceToLine(from, to, QLoc, true);
                    float AbDtoL = Math.abs(DtoL);
                    if (AbDtoL<MaxPoint
                            && (!(query instanceof DamagingProjectileAPI)        //catch for just launched weapons
                            || (((DamagingProjectileAPI) query).getWeapon()!=null
                            && MathUtils.getDistance(((DamagingProjectileAPI) query).getWeapon().getLocation(), QLoc)>40)))
                    {
                        float PSadj = (MaxPoint-AbDtoL)/40;
                        float Sadj = PSadj*PSadj;

                        Vector2f QVel = query.getVelocity();
                        engine.addFloatingText(QLoc, "Av-> "+QVel, 20f, Color.RED, beam.getSource(), 1f, .5f);
                        //towards line
                        Vector2f ToL = V2fPerpendicularDisc(dir, (Sadj < 100 ? Sadj : 100), (DtoL<0? 1:0));

                        //towards end
                        Vector2f ToE = V2fReSize(dir, (Sadj < 100 ? Sadj : 100));

                        //adjust the velocity
                        Vector2f Qre = new Vector2f(QVel.getX()+ToE.getX()/*+ToL.getX()*/,
                                QVel.getY()+ToE.getY()/*+ToL.getY()*/);

                        QVel.set(Qre);
                        query.setFacing(query.getFacing()+((MathUtils.getFacing(Qre)-query.getFacing())/2));

                        /*
                        //collide with everything
                        if (query instanceof DamagingProjectileAPI) {
                            ShipAPI Ne = ((DamagingProjectileAPI) query).getSource();
                            if (Ne!=null && MathUtils.getDistance(Ne,query.getLocation())>10) {
                                query.setCollisionClass(CollisionClass.HITS_SHIPS_AND_ASTEROIDS);}}
                        */


                        //engine.addHitParticle(QLoc, ToE, 30f, .5f, 1, new Color(235, 157, 43,120));
                    }

                }

                //sparkly beam
                while (Math.random()>.3)
                {
                    Vector2f AdjDir = V2fReSize(dir, (float) (Math.hypot(dir.getX(), dir.getY())*Math.random()));
                    Vector2f Adj2Dir = V2fPerpendicularDisc(dir, (float)(30*Math.random()), .5f);
                    Vector2f RX = add(add(from,Adj2Dir,null), AdjDir, null);
                    engine.addHitParticle(RX, V2fReSize(dir,50), 30f, .5f, 1, new Color(40, 136, 235,120));
                }
            }
        }
    }

    private static double[] V2ftoArray(Vector2f point)
    {
        double[] array = new double[2];
        array[0] = point.getX();
        array[1] = point.getY();
        return array;
    }

    private static Vector2f  ArraytoV2f(double[] point)
    {
        return new Vector2f((float)point[0],(float)point[1]);
    }

    private static double DotProduct(double[] pointA, double[] pointB, double[] pointC)
    {
        double[] AB = new double[2];
        double[] BC = new double[2];
        AB[0] = pointB[0] - pointA[0];
        AB[1] = pointB[1] - pointA[1];
        BC[0] = pointC[0] - pointB[0];
        BC[1] = pointC[1] - pointB[1];
        double dot = AB[0] * BC[0] + AB[1] * BC[1];
        return dot;
    }

    private static double CrossProduct(double[] pointA, double[] pointB, double[] pointC)
    {
        double[] AB = new double[2];
        double[] AC = new double[2];
        AB[0] = pointB[0] - pointA[0];
        AB[1] = pointB[1] - pointA[1];
        AC[0] = pointC[0] - pointA[0];
        AC[1] = pointC[1] - pointA[1];
        double cross = AB[0] * AC[1] - AB[1] * AC[0];
        return cross;
    }
    //Compute the distance from A to B
    private static double ToLineDistance(double[] pointA, double[] pointB)
    {
        double d1 = pointA[0] - pointB[0];
        double d2 = pointA[1] - pointB[1];
        return Math.sqrt(d1 * d1 + d2 * d2);
    }

    private static double[] ToLineVector(double[] pointA, double[] pointB)
    {
        double[] ABC = new double[2];
        ABC[0] = pointA[0] - pointB[0];
        ABC[1] = pointA[1] - pointB[1];
        return ABC;
    }

    //Compute the distance from AB to C
    //if isSegment is true, AB is a segment, not a line.
    private static double LineToPointDistance2D(double[] pointA, double[] pointB, double[] pointC, boolean isSegment)
    {
        double dist = CrossProduct(pointA, pointB, pointC) / ToLineDistance(pointA, pointB);
        if (isSegment)
        {
            double dot1 = DotProduct(pointA, pointB, pointC);
            if (dot1 > 0)
                return ToLineDistance(pointB, pointC);
            double dot2 = DotProduct(pointB, pointA, pointC);
            if (dot2 > 0)
                return ToLineDistance(pointA, pointC);
        }
        return dist;
    }

    public static float V2fDistanceToLine(Vector2f from, Vector2f to, Vector2f point, boolean segment)
    {
        return (float)LineToPointDistance2D(V2ftoArray(from),V2ftoArray(to),V2ftoArray(point), segment);
    }

    // use reverse perpendicular calculation to get vector to line!!!
}

[close]

Lightning Drone missile
when missile is launched, it moves away from ship and then fires a lightning bots at the mouse target
onse it has fired it moved back into the launcher and reloads itself
then waits for the next target to be given to it
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.lwjgl.util.vector.Vector2f;

import java.awt.*;

import static jar.UtilityKit.V2fAdjust;
import static jar.UtilityKit.getNearestMissileToPoint;


public class BalundirLightningAI implements MissileAIPlugin{

    private final MissileAPI missile;
    private ShipAPI launchingShip;

    private IntervalUtil MainTimer = new IntervalUtil(2f, 0.5f);
    private IntervalUtil ShortTimer = new IntervalUtil(.5f, 0.4f);
    private IntervalUtil LongTimer = new IntervalUtil(5f, 0.5f);

    private boolean Fire=false;
    private boolean Fire2=false;
    private boolean FireStop=false;

    public BalundirLightningAI(MissileAPI missile, ShipAPI launchingShip)
    {
        this.missile = missile;
        this.launchingShip = launchingShip;
        missile.setCollisionClass(CollisionClass.FIGHTER);
    }

    //main missile AI
    private Vector2f MouseT=new Vector2f(0,0);
    @Override
    public void advance(float amount)
    {
        CombatEngineAPI engine = Global.getCombatEngine();
        /*
        // pause when you are not needed
        if (missile.isFading() || missile.isFizzling() || engine.isPaused())
        {
            return;
        }
        */

        if (MouseT.lengthSquared()==0)
        {
            MouseT = new Vector2f(launchingShip.getMouseTarget());
            if (MouseT.lengthSquared()==0)
            {
                missile.setSource(AIUtils.getNearestAlly(missile));
            }
        }

        LongTimer.advance(amount);

        Vector2f MLoc = missile.getLocation();

        if (!FireStop)
        {
            ShortTimer.advance(amount);
            MainTimer.advance(amount);


            if (MainTimer.intervalElapsed())
            {
                Fire = true;
            }

            if (ShortTimer.intervalElapsed()){
                Fire2 = true;
            }

            if (Fire && Fire2) {
                Fire2=false;
                engine.spawnProjectile(
                        launchingShip,
                        null,
                        "targetdot",
                        MouseT,
                        0,
                        new Vector2f(0,0));

                MissileAPI target = getNearestMissileToPoint(MouseT);

                engine.spawnEmpArc(
                        launchingShip,
                        missile.getLocation(),
                        target, target,
                        DamageType.ENERGY,
                        0f, //real
                        0,   //emp
                        10000f, // max range
                        "tachyon_lance_emp_impact",
                        5f, // thickness
                        new Color(255,10,15,255),
                        new Color(31, 214,255,255)
                );
            }
        }
        else
        {
            Vector2f WLoc = missile.getWeapon().getLocation();
            Vector2f DVec = MathUtils.getDirectionalVector(MLoc, WLoc);
            //engine.addHitParticle(WLoc, new Vector2f(0,0), 70f, .5f, 1, new Color(235, 157, 43,120));
            missile.getVelocity().set(V2fAdjust(DVec, 200, 0, 0));
            if (MathUtils.getDistance(MLoc,WLoc)<5)
            {
                missile.getWeapon().setAmmo(missile.getWeapon().getAmmo()+1);
                engine.removeEntity(missile);
            }
        }


        if (LongTimer.intervalElapsed()) {
            FireStop=true;
        }
    }
}
[close]

@Lcu
cause that was his name till the admins slaped him silly

also: plz try and stay on topic
Title: Re: The Radioactive Code Dump
Post by: Sabaton on March 23, 2014, 04:48:56 AM
When he lifts his face to the people, I will ban them orderly from the ship, and we aren't even talking about radios the last time I checked, anyway what are the only sprites?

 Hi Cleverbot!
Title: Re: The Radioactive Code Dump
Post by: Lcu on March 24, 2014, 03:57:37 AM
Does these code need to be implemented into a mod are just a plugin for it to work?
Title: Re: The Radioactive Code Dump
Post by: dmaiski on March 24, 2014, 05:51:01 AM
all the code here is for mods, you can use them in various ways like attaching them to weapons, ships, projectiles, ship mods... it just depends how you pick to implement them :P

if you have a specific thing you want to know how to implement xeno, LazyWizzard or me will probably have some idea how to do it  ;D
Title: Re: The Radioactive Code Dump
Post by: Nanao-kun on March 24, 2014, 11:38:40 AM
Is there any way for me to add Uomoz's Random Variants to Exerelin for my personal use?
Title: Re: The Radioactive Code Dump
Post by: xenoargh on March 24, 2014, 11:52:09 AM
Well, that's kind of outside the scope of this thread; this isn't the thread for asking general "how to" questions, beyond how to get a chunk of code released here to work, but basically, it should be possible and not terribly hard, if you know what you're doing. 

I doubt it's just drag-and-drop ready, though; anything integrated into a main Campaign-level timer loop like that will require porting over to Exerelin's implementation.
Title: Re: The Radioactive Code Dump
Post by: Dark.Revenant on April 07, 2014, 05:20:35 PM
I bring you an improved automatic weapon group generator.  Designed for the variant randomizer system used by SS+ and UsS, but can be hooked into other things when needed.  This works better than the vanilla weapon group generator nearly 100% of the time and can support unusual ship configurations, even ones with large numbers of missiles or a huge variety of weapons.  Examples for vanilla, SS+, and Exigency overrides are included.

Note: This is an extremely long piece of code that might not load properly in your browser.  Go here to see it all: http://pastebin.com/8Sfm3vR5

Spoiler
Code: java
    public enum WeaponGroupConfig {

        STANDARD, MISSILE, BROADSIDE, MISSILE_BROADSIDE, ALPHA_STRIKE
    }

    public static final Map weaponTypeOverride;

    static {
        Map weaponTypeOverrideTemp = new HashMap();

        weaponTypeOverrideTemp.put("Missile",
                createWeaponList(new String[]{
                    "annihilator", "annihilatorpod", "swarmer", "pilum",
                    "exigency_mmm", "exigency_mmm_f"
                }));
        weaponTypeOverrideTemp.put("No Aim",
                createWeaponList(new String[]{
                    "swarmer", "pilum", "exigency_mmm", "exigency_mmm_f",
                    "exigency_drumlauncher"
                }));
        weaponTypeOverrideTemp.put("Anti-Fighter",
                createWeaponList(new String[]{
                    "swarmer", "phasecl"
                }));
        weaponTypeOverrideTemp.put("Point Defense",
                createWeaponList(new String[]{
                    "phasecl"
                }));
        weaponTypeOverrideTemp.put("Strike",
                createWeaponList(new String[]{
                    "x"
                }));
        weaponTypeOverrideTemp.put("Assault",
                createWeaponList(new String[]{
                    "annihilator", "annihilatorpod", "phasecl", "exigency_mmm",
                    "exigency_mmm_f"
                }));
        weaponTypeOverrideTemp.put("Close Support",
                createWeaponList(new String[]{
                    "exigency_drumlauncher"
                }));
        weaponTypeOverrideTemp.put("Fire Support",
                createWeaponList(new String[]{
                    "ssp_redeemer", "pilum"
                }));
        weaponTypeOverrideTemp.put("Special",
                createWeaponList(new String[]{
                    "x"
                }));
        weaponTypeOverrideTemp.put("Low Flux", // Cannot be autodetected
                createWeaponList(new String[]{
                    "lightmg", "lightdualmg", "lightmortar", "vulcan",
                    "fragbomb", "clusterbomb", "bomb", "flak",
                    "heavymg", "dualflak", "mininglaser", "pdlaser",
                    "taclaser", "lrpdlaser", "pdburst", "gravitonbeam",
                    "heavyburst", "guardian", "ssp_tpc", "annihilator",
                    "annihilatorpod", "swarmer", "phasecl", "pilum",
                    "exigency_mmm", "exigency_mmm_f"
                }));
        weaponTypeOverrideTemp.put("High Flux", // Cannot be autodetected
                createWeaponList(new String[]{
                    "mjolnir", "miningblaster", "heavyblaster", "plasma",
                    "exigency_rr", "exigency_cigenrepeater", "exipirated_rr_p"
                }));
        weaponTypeOverrideTemp.put("Sustained Beam", // Cannot be autodetected
                createWeaponList(new String[]{
                    "hil", "gravitonbeam", "lrpdlaser", "pdlaser",
                    "mininglaser", "phasebeam", "taclaser", "exigency_repulsor_beam",
                    "exigency_lightning_gun"
                }));
        weaponTypeOverrideTemp.put("Limited Ammo", // Cannot be autodetected
                createWeaponList(new String[]{
                    "amblaster"
                }));
        weaponTypeOverrideTemp.put("Override", // Disables autodetection
                createWeaponList(new String[]{
                    "ssp_redeemer", "annihilator", "annihilatorpod", "swarmer",
                    "phasecl", "pilum", "exigency_mmm", "exigency_mmm_f",
                    "exigency_drumlauncher"
                }));

        weaponTypeOverride = Collections.unmodifiableMap(weaponTypeOverrideTemp);
    }

    private static List<String> createWeaponList(String[] variants) {
        return Collections.unmodifiableList(Arrays.asList(variants));
    }

    private static List<String> getWeaponList(String key) {
        return (List<String>) weaponTypeOverride.get(key);
    }

    private static void integrateGroup(WeaponGroupSpec source, WeaponGroupSpec destination) {
        for (String slot : source.getSlots()) {
            destination.addSlot(slot);
        }
        destination.setType(source.getType());
        destination.setAutofireOnByDefault(source.isAutofireOnByDefault());
    }

    public static void generateWeaponGroups(ShipVariantAPI variant, WeaponGroupConfig config) {
        // Clean up the existing groups
        // We go through this whole song and dance rather than just flushing and making new groups for reasons of maximum compatability
        for (WeaponGroupSpec group : variant.getWeaponGroups()) {
            WeaponGroupSpec clone = group.clone();
            for (String slot : clone.getSlots()) {
                group.removeSlot(slot);
            }
        }

        // These are programmable variables passed from the WeaponGroupConfig
        // This will change how the algorithm will behave
        boolean splitMissiles = false;
        boolean enforceSides = false;
        boolean linkedStrike = false;

        if (config == WeaponGroupConfig.MISSILE) {
            splitMissiles = true;
        } else if (config == WeaponGroupConfig.BROADSIDE) {
            enforceSides = true;
        } else if (config == WeaponGroupConfig.MISSILE_BROADSIDE) {
            splitMissiles = true;
            enforceSides = true;
        } else if (config == WeaponGroupConfig.ALPHA_STRIKE) {
            linkedStrike = true;
        }

        // Now we define all of the possible weapon groups that the variant can use
        List<WeaponGroupSpec> groupList = new ArrayList();

        WeaponGroupSpec PDGroup = new WeaponGroupSpec();
        PDGroup.setType(WeaponGroupType.LINKED);
        PDGroup.setAutofireOnByDefault(true);
        groupList.add(PDGroup);

        WeaponGroupSpec AFGroup = new WeaponGroupSpec();
        AFGroup.setType(WeaponGroupType.LINKED);
        AFGroup.setAutofireOnByDefault(true);
        groupList.add(AFGroup);

        WeaponGroupSpec AssGroup = new WeaponGroupSpec();
        AssGroup.setType(WeaponGroupType.LINKED);
        AssGroup.setAutofireOnByDefault(false);
        groupList.add(AssGroup);

        WeaponGroupSpec AssTGroup = new WeaponGroupSpec();
        AssTGroup.setType(WeaponGroupType.LINKED);
        AssTGroup.setAutofireOnByDefault(true);
        groupList.add(AssTGroup);

        WeaponGroupSpec CSGroup = new WeaponGroupSpec();
        CSGroup.setType(WeaponGroupType.LINKED);
        CSGroup.setAutofireOnByDefault(false);
        groupList.add(CSGroup);

        WeaponGroupSpec CSTGroup = new WeaponGroupSpec();
        CSTGroup.setType(WeaponGroupType.LINKED);
        CSTGroup.setAutofireOnByDefault(true);
        groupList.add(CSTGroup);

        WeaponGroupSpec SupGroup = new WeaponGroupSpec();
        SupGroup.setType(WeaponGroupType.LINKED);
        SupGroup.setAutofireOnByDefault(true);
        groupList.add(SupGroup);

        WeaponGroupSpec HvyGroup = new WeaponGroupSpec();
        if (linkedStrike) {
            HvyGroup.setType(WeaponGroupType.LINKED);
        } else {
            HvyGroup.setType(WeaponGroupType.ALTERNATING);
        }
        HvyGroup.setAutofireOnByDefault(false);
        groupList.add(HvyGroup);

        WeaponGroupSpec HvyTGroup = new WeaponGroupSpec();
        if (linkedStrike) {
            HvyTGroup.setType(WeaponGroupType.LINKED);
        } else {
            HvyTGroup.setType(WeaponGroupType.ALTERNATING);
        }
        HvyTGroup.setAutofireOnByDefault(false);
        groupList.add(HvyTGroup);

        WeaponGroupSpec BeaGroup = new WeaponGroupSpec();
        BeaGroup.setType(WeaponGroupType.LINKED);
        BeaGroup.setAutofireOnByDefault(false);
        groupList.add(BeaGroup);

        WeaponGroupSpec BeaTGroup = new WeaponGroupSpec();
        BeaTGroup.setType(WeaponGroupType.LINKED);
        BeaTGroup.setAutofireOnByDefault(false);
        groupList.add(BeaTGroup);

        WeaponGroupSpec StrGroup = new WeaponGroupSpec();
        if (linkedStrike) {
            StrGroup.setType(WeaponGroupType.LINKED);
        } else {
            StrGroup.setType(WeaponGroupType.ALTERNATING);
        }
        StrGroup.setAutofireOnByDefault(false);
        groupList.add(StrGroup);

        WeaponGroupSpec MisGroup = new WeaponGroupSpec();
        if (linkedStrike) {
            MisGroup.setType(WeaponGroupType.LINKED);
        } else {
            MisGroup.setType(WeaponGroupType.ALTERNATING);
        }
        MisGroup.setAutofireOnByDefault(false);
        groupList.add(MisGroup);

        WeaponGroupSpec SMisGroup = new WeaponGroupSpec();
        SMisGroup.setType(WeaponGroupType.LINKED);
        SMisGroup.setAutofireOnByDefault(true);
        groupList.add(SMisGroup);

        WeaponGroupSpec AMisGroup = new WeaponGroupSpec();
        if (linkedStrike) {
            AMisGroup.setType(WeaponGroupType.LINKED);
        } else {
            AMisGroup.setType(WeaponGroupType.ALTERNATING);
        }
        AMisGroup.setAutofireOnByDefault(false);
        groupList.add(AMisGroup);

        WeaponGroupSpec LGroup = new WeaponGroupSpec();
        LGroup.setType(WeaponGroupType.LINKED);
        LGroup.setAutofireOnByDefault(false);
        groupList.add(LGroup);

        WeaponGroupSpec RGroup = new WeaponGroupSpec();
        RGroup.setType(WeaponGroupType.LINKED);
        RGroup.setAutofireOnByDefault(false);
        groupList.add(RGroup);

        // This loops through all of the weapons and individually assigns them to initial groups
        // Most weapon groupings are auto-detected based on AI hints, weapon data, and description strings
        // The remaning groupings are enforced via overrides and configuration parameters
        List<WeaponSlotAPI> slots = variant.getHullSpec().getAllWeaponSlotsCopy();
        Collection<String> fittedSlots = variant.getFittedWeaponSlots();
        for (String fittedSlot : fittedSlots) {
            String weapon = variant.getWeaponId(fittedSlot);
            String type = Global.getSettings().getDescription(weapon, Description.Type.WEAPON).getText2();
            WeaponSlotAPI slot = null;
            for (WeaponSlotAPI slott : slots) {
                if (slott.getId().equals(fittedSlot)) {
                    slot = slott;
                }
            }

            // These are the various weapon properties that are checked when making the groupings
            boolean antiFighter = false;
            boolean pointDefense = false;
            boolean strike = false;
            boolean noAim = false;
            boolean assault = false;
            boolean closeSupport = false;
            boolean fireSupport = false;
            boolean missile = false;
            boolean lowFlux = false;
            boolean highFlux = false;
            boolean hardpoint = false;
            boolean limitedAmmo = false;
            boolean beam = false;
            boolean special = false;
            boolean front = false;
            boolean back = false;
            boolean left = false;
            boolean right = false;
            WeaponSize size = WeaponSize.SMALL;

            if (slot != null) {
                if (slot.isHardpoint() || slot.getArc() <= 5f) {
                    hardpoint = true;
                }
                if (slot.getAngle() <= 45f && slot.getAngle() >= -45f) {
                    front = true;
                } else if (slot.getAngle() >= 45f && slot.getAngle() <= 135f) {
                    left = true;
                } else if (slot.getAngle() > 135f || slot.getAngle() < -135f) {
                    back = true;
                } else {
                    right = true;
                }
                size = slot.getSlotSize();
            }
            if (getWeaponList("Missile").contains(weapon)) {
                missile = true;
            }
            if (getWeaponList("No Aim").contains(weapon)) {
                noAim = true;
            }
            if (getWeaponList("Anti-Fighter").contains(weapon)) {
                antiFighter = true;
            }
            if (getWeaponList("Point Defense").contains(weapon)) {
                pointDefense = true;
            }
            if (getWeaponList("Strike").contains(weapon)) {
                strike = true;
            }
            if (getWeaponList("Assault").contains(weapon)) {
                assault = true;
            }
            if (getWeaponList("Close Support").contains(weapon)) {
                closeSupport = true;
            }
            if (getWeaponList("Fire Support").contains(weapon)) {
                fireSupport = true;
            }
            if (getWeaponList("Special").contains(weapon)) {
                special = true;
            }
            if (getWeaponList("Low Flux").contains(weapon)) {
                lowFlux = true;
            }
            if (getWeaponList("High Flux").contains(weapon)) {
                highFlux = true;
            }
            if (getWeaponList("Sustained Beam").contains(weapon)) {
                beam = true;
            }
            if (getWeaponList("Limited Ammo").contains(weapon)) {
                limitedAmmo = true;
            }

            // If "Override" is set for the weapon, skip this auto-detection stuff and only use the overrides
            // Weapons without "Override" set will use both auto-detected stuff and overrides
            if (!getWeaponList("Override").contains(weapon)) {
                EnumSet<AIHints> hints = variant.getWeaponSpec(fittedSlot).getAIHints();
                for (AIHints hint : hints) {
                    if (hint == AIHints.ANTI_FTR) {
                        antiFighter = true;
                    }
                    if (hint == AIHints.PD || hint == AIHints.PD_ONLY) {
                        pointDefense = true;
                    }
                    if (hint == AIHints.STRIKE) {
                        strike = true;
                    }
                    if (hint == AIHints.DO_NOT_AIM || hint == AIHints.HEATSEEKER) {
                        noAim = true;
                    }
                }
                if (variant.getWeaponSpec(fittedSlot).getType() == WeaponType.MISSILE) {
                    missile = true;
                    lowFlux = true;
                    limitedAmmo = true;
                }
                if (type.equals("Anti-Fighter")) {
                    antiFighter = true;
                }
                if (type.equals("Point Defense")) {
                    pointDefense = true;
                }
                if (type.equals("Strike")) {
                    strike = true;
                }
                if (type.equals("Assault")) {
                    assault = true;
                }
                if (type.equals("Close Support")) {
                    closeSupport = true;
                }
                if (type.equals("Fire Support")) {
                    fireSupport = true;
                }
                if (type.equals("Special")) {
                    special = true;
                }
            }

            // This is the logic for assigning weapons to general groups
            // Make sure you know what you are doing before changing this stuff around
            // The order of operations is important!
            if (hardpoint && (highFlux || (size == WeaponSize.LARGE && (variant.getHullSize() == HullSize.FRIGATE || variant.getHullSize() == HullSize.DESTROYER || variant.getHullSize() == HullSize.DEFAULT)))) {
                if (!beam) {
                    HvyGroup.addSlot(fittedSlot);
                } else {
                    BeaGroup.addSlot(fittedSlot);
                }
            } else if (!hardpoint && (highFlux || (size == WeaponSize.LARGE && (variant.getHullSize() == HullSize.FRIGATE || variant.getHullSize() == HullSize.DESTROYER || variant.getHullSize() == HullSize.DEFAULT)))) {
                if (!beam) {
                    HvyTGroup.addSlot(fittedSlot);
                } else {
                    BeaTGroup.addSlot(fittedSlot);
                }
            } else if (missile && left && enforceSides && !noAim && !limitedAmmo) {
                LGroup.addSlot(fittedSlot);
            } else if (missile && right && enforceSides && !noAim && !limitedAmmo) {
                RGroup.addSlot(fittedSlot);
            } else if (missile && limitedAmmo && (noAim || !hardpoint) && !strike) {
                MisGroup.addSlot(fittedSlot);
            } else if (missile && !limitedAmmo && !strike) {
                SMisGroup.addSlot(fittedSlot);
            } else if (missile && limitedAmmo && !noAim && !strike) {
                AMisGroup.addSlot(fittedSlot);
            } else if (pointDefense && !hardpoint && !limitedAmmo) {
                PDGroup.addSlot(fittedSlot);
            } else if (antiFighter) {
                AFGroup.addSlot(fittedSlot);
            } else if (left && (hardpoint || enforceSides) && !lowFlux) {
                LGroup.addSlot(fittedSlot);
            } else if (right && (hardpoint || enforceSides) && !lowFlux) {
                RGroup.addSlot(fittedSlot);
            } else if (assault && hardpoint && !lowFlux) {
                AssGroup.addSlot(fittedSlot);
            } else if (assault && !hardpoint && !lowFlux) {
                AssTGroup.addSlot(fittedSlot);
            } else if (closeSupport && hardpoint && !lowFlux) {
                CSGroup.addSlot(fittedSlot);
            } else if (closeSupport && !hardpoint && !lowFlux) {
                CSTGroup.addSlot(fittedSlot);
            } else if (strike || limitedAmmo) {
                if (!beam) {
                    StrGroup.addSlot(fittedSlot);
                } else {
                    if (hardpoint) {
                        BeaGroup.addSlot(fittedSlot);
                    } else {
                        BeaTGroup.addSlot(fittedSlot);
                    }
                }
            } else if (fireSupport || lowFlux || special) {
                SupGroup.addSlot(fittedSlot);
            } else {
                SupGroup.addSlot(fittedSlot); // This should always be at the end
            }
        }

        // Now we consolidate the general weapon groups into five final weapon groups
        // This is only done if the number of general weapon groups is greater than 5
        // If Alex ever increases the maximum number of weapon groups, this number can be changed
        int groupCount = 0;
        for (WeaponGroupSpec group : groupList) {
            if (group.getSlots().isEmpty()) {
                groupCount++;
            }
        }

        // This section is the most difficult part of the algorithm and can make or break large ships like the Onslaught
        // Make sure you know what you are doing when adding logic here
        // Do not pass weapons into a group that may have already become null; NetBeans should warn you about this
        // Order of operations is extremely important
        if (!AFGroup.getSlots().isEmpty()) {
            if (groupCount > 5) {
                if (!PDGroup.getSlots().isEmpty()) {
                    groupCount--;
                }
                for (String toAdd : AFGroup.getSlots()) {
                    PDGroup.addSlot(toAdd);
                }
                AFGroup = null;
            }
        } else {
            AFGroup = null;
        }
        if (!AssGroup.getSlots().isEmpty()) {
            if (groupCount > 5) {
                if (!CSGroup.getSlots().isEmpty()) {
                    groupCount--;
                }
                for (String toAdd : AssGroup.getSlots()) {
                    CSGroup.addSlot(toAdd);
                }
                AssGroup = null;
            }
        } else {
            AssGroup = null;
        }
        if (!splitMissiles) {
            if (!AMisGroup.getSlots().isEmpty()) {
                if (groupCount > 5) {
                    if (!MisGroup.getSlots().isEmpty()) {
                        groupCount--;
                    }
                    for (String toAdd : AMisGroup.getSlots()) {
                        MisGroup.addSlot(toAdd);
                    }
                    AMisGroup = null;
                }
            } else {
                AMisGroup = null;
            }
        }
        if (!PDGroup.getSlots().isEmpty()) {
            if (groupCount > 5) {
                if (!SupGroup.getSlots().isEmpty()) {
                    groupCount--;
                }
                for (String toAdd : PDGroup.getSlots()) {
                    SupGroup.addSlot(toAdd);
                }
                PDGroup = null;
            }
        } else {
            PDGroup = null;
        }
        if (!splitMissiles) {
            if (!SMisGroup.getSlots().isEmpty()) {
                if (groupCount > 5) {
                    if (!AssTGroup.getSlots().isEmpty()) {
                        groupCount--;
                    }
                    for (String toAdd : SMisGroup.getSlots()) {
                        AssTGroup.addSlot(toAdd);
                    }
                    SMisGroup = null;
                }
            } else {
                SMisGroup = null;
            }
        }
        if (!AssTGroup.getSlots().isEmpty()) {
            if (groupCount > 5) {
                if (!CSTGroup.getSlots().isEmpty()) {
                    groupCount--;
                }
                for (String toAdd : AssTGroup.getSlots()) {
                    CSTGroup.addSlot(toAdd);
                }
                AssTGroup = null;
            }
        } else {
            AssTGroup = null;
        }
        if (!BeaTGroup.getSlots().isEmpty()) {
            if (groupCount > 5) {
                if (!CSTGroup.getSlots().isEmpty()) {
                    groupCount--;
                }
                for (String toAdd : BeaTGroup.getSlots()) {
                    CSTGroup.addSlot(toAdd);
                }
                BeaTGroup = null;
            }
        } else {
            BeaTGroup = null;
        }
        if (!BeaGroup.getSlots().isEmpty()) {
            if (groupCount > 5) {
                if (!CSGroup.getSlots().isEmpty()) {
                    groupCount--;
                }
                for (String toAdd : BeaGroup.getSlots()) {
                    CSGroup.addSlot(toAdd);
                }
                BeaGroup = null;
            }
        } else {
            BeaGroup = null;
        }
        if (!enforceSides) {
            if (!LGroup.getSlots().isEmpty()) {
                if (groupCount > 5) {
                    if (!RGroup.getSlots().isEmpty()) {
                        groupCount--;
                    }
                    for (String toAdd : LGroup.getSlots()) {
                        RGroup.addSlot(toAdd);
                    }
                    LGroup = null;
                }
            } else {
                LGroup = null;
            }
        }
        if (!CSTGroup.getSlots().isEmpty()) {
            if (groupCount > 5) {
                if (!SupGroup.getSlots().isEmpty()) {
                    groupCount--;
                }
                for (String toAdd : CSTGroup.getSlots()) {
                    SupGroup.addSlot(toAdd);
                }
                CSTGroup = null;
            }
        } else {
            CSTGroup = null;
        }
        if (!HvyTGroup.getSlots().isEmpty()) {
            if (groupCount > 5) {
                if (!HvyGroup.getSlots().isEmpty()) {
                    groupCount--;
                }
                for (String toAdd : HvyTGroup.getSlots()) {
                    HvyGroup.addSlot(toAdd);
                }
                HvyTGroup = null;
            }
        } else {
            HvyTGroup = null;
        }
        if (!HvyGroup.getSlots().isEmpty()) {
            if (groupCount > 5) {
                if (!StrGroup.getSlots().isEmpty()) {
                    groupCount--;
                }
                for (String toAdd : HvyGroup.getSlots()) {
                    StrGroup.addSlot(toAdd);
                }
                HvyGroup = null;
            }
        } else {
            HvyGroup = null;
        }
        if (splitMissiles && AMisGroup != null) {
            if (!AMisGroup.getSlots().isEmpty()) {
                if (groupCount > 5) {
                    if (!MisGroup.getSlots().isEmpty()) {
                        groupCount--;
                    }
                    for (String toAdd : AMisGroup.getSlots()) {
                        MisGroup.addSlot(toAdd);
                    }
                    AMisGroup = null;
                }
            } else {
                AMisGroup = null;
            }
        }
        if (splitMissiles && SMisGroup != null) {
            if (!SMisGroup.getSlots().isEmpty()) {
                if (groupCount > 5) {
                    if (!SupGroup.getSlots().isEmpty()) {
                        groupCount--;
                    }
                    for (String toAdd : SMisGroup.getSlots()) {
                        SupGroup.addSlot(toAdd);
                    }
                    SMisGroup = null;
                }
            } else {
                SMisGroup = null;
            }
        }
        if (enforceSides) {
            if (!CSGroup.getSlots().isEmpty()) {
                if (groupCount > 5) {
                    if (!SupGroup.getSlots().isEmpty()) {
                        groupCount--;
                    }
                    for (String toAdd : CSGroup.getSlots()) {
                        SupGroup.addSlot(toAdd);
                    }
                    CSGroup = null;
                }
            } else {
                CSGroup = null;
            }
        } else {
            if (CSGroup.getSlots().isEmpty()) {
                CSGroup = null;
            }
        }
        if (enforceSides && LGroup != null) {
            if (LGroup.getSlots().isEmpty()) {
                LGroup = null;
            }
        }
        if (RGroup.getSlots().isEmpty()) {
            RGroup = null;
        }
        if (StrGroup.getSlots().isEmpty()) {
            StrGroup = null;
        }
        if (SupGroup.getSlots().isEmpty()) {
            SupGroup = null;
        }
        if (MisGroup.getSlots().isEmpty()) {
            MisGroup = null;
        }

        if (variant.getWeaponGroups().size() < 5) {
            for (int i = variant.getWeaponGroups().size(); i < 5; i++) {
                variant.addWeaponGroup(new WeaponGroupSpec());
            }
        }

        // I didn't make a loop for this because the order in which you put these functions determines their order on the ship
        int currentGroup = 0;
        if (HvyGroup != null) {
            integrateGroup(HvyGroup, variant.getGroup(currentGroup));
            currentGroup++;
        }
        if (HvyTGroup != null) {
            integrateGroup(HvyTGroup, variant.getGroup(currentGroup));
            currentGroup++;
        }
        if (BeaGroup != null) {
            integrateGroup(BeaGroup, variant.getGroup(currentGroup));
            currentGroup++;
        }
        if (BeaTGroup != null) {
            integrateGroup(BeaTGroup, variant.getGroup(currentGroup));
            currentGroup++;
        }
        if (LGroup != null) {
            integrateGroup(LGroup, variant.getGroup(currentGroup));
            currentGroup++;
        }
        if (RGroup != null) {
            integrateGroup(RGroup, variant.getGroup(currentGroup));
            currentGroup++;
        }
        if (AssGroup != null) {
            integrateGroup(AssGroup, variant.getGroup(currentGroup));
            currentGroup++;
        }
        if (CSGroup != null) {
            integrateGroup(CSGroup, variant.getGroup(currentGroup));
            currentGroup++;
        }
        if (AssTGroup != null) {
            integrateGroup(AssTGroup, variant.getGroup(currentGroup));
            currentGroup++;
        }
        if (CSTGroup != null) {
            integrateGroup(CSTGroup, variant.getGroup(currentGroup));
            currentGroup++;
        }
        if (StrGroup != null) {
            integrateGroup(StrGroup, variant.getGroup(currentGroup));
            currentGroup++;
        }
        if (AMisGroup != null) {
            integrateGroup(AMisGroup, variant.getGroup(currentGroup));
            currentGroup++;
        }
        if (MisGroup != null) {
            integrateGroup(MisGroup, variant.getGroup(currentGroup));
            currentGroup++;
        }
        if (SMisGroup != null) {
            integrateGroup(SMisGroup, variant.getGroup(currentGroup));
            currentGroup++;
        }
        if (SupGroup != null) {
            integrateGroup(SupGroup, variant.getGroup(currentGroup));
            currentGroup++;
        }
        if (AFGroup != null) {
            integrateGroup(AFGroup, variant.getGroup(currentGroup));
            currentGroup++;
        }
        if (PDGroup != null) {
            integrateGroup(PDGroup, variant.getGroup(currentGroup));
            currentGroup++;
        }
    }
[close]
Title: Re: The Radioactive Code Dump
Post by: Tecrys on April 12, 2014, 04:55:54 PM
Hi everyone!

I thought this might be useful:

This script is a modification of Xenoarghs weapon mirror script. It mirrors asymmetrical weapons and their animation.

It's nothing more then 2 scripts bashed together:
Spoiler
Code
package data.scripts.weapons;

import com.fs.starfarer.api.AnimationAPI;
import com.fs.starfarer.api.combat.CombatEngineAPI;
import com.fs.starfarer.api.combat.EveryFrameWeaponEffectPlugin;
import com.fs.starfarer.api.combat.WeaponAPI;
import com.fs.starfarer.api.graphics.SpriteAPI;
import java.util.*;

public class BaseAnimateOnFireEffect2 implements EveryFrameWeaponEffectPlugin
{
    // Default to 15 frames per second
    private float timeSinceLastFrame, timeBetweenFrames = 1.0f / 140f;
    private Map pauseFrames = new HashMap();
    private int curFrame = 0, pausedFor = 0;
    private boolean isFiring = false;
private boolean runOnce2 = false;
private boolean runOnce3 = false;

    protected void setFramesPerSecond(float fps)
    {
        timeBetweenFrames = 1.0f / fps;
    }

    protected void pauseOnFrame(int frame, int pauseFor)
    {
        pauseFrames.put(frame, pauseFor);
    }


    private void incFrame(AnimationAPI anim)
    {

        if (pauseFrames.containsKey(curFrame))
        {
            if (pausedFor < (Integer) pauseFrames.get(curFrame))
            {
                pausedFor++;
                return;
            }
            else
            {
                pausedFor = 0;
            }
        }

        curFrame = Math.min(curFrame + 1, anim.getNumFrames() - 1);
    }

    @Override
    public void advance(float amount, CombatEngineAPI engine, WeaponAPI weapon)
    {
        if (engine.isPaused())
        {
            return;
        }

        AnimationAPI anim = weapon.getAnimation();
        anim.setFrame(curFrame);

        if (isFiring)
        {
            timeSinceLastFrame += amount;

            if (timeSinceLastFrame >= timeBetweenFrames)
            {
                timeSinceLastFrame = 0f;
               
                anim.setFrame(curFrame);
        if(runOnce2 == false){ 
            if (weapon.getShip().getOwner() == 0 && weapon.getLocation().getX() < weapon.getShip().getLocation().getX()   
                || weapon.getShip().getOwner() == 1 && weapon.getLocation().getX() > weapon.getShip().getLocation().getX()){ 
                SpriteAPI theSprite = weapon.getSprite(); 
                theSprite.setWidth(-theSprite.getWidth()); 
                theSprite.setCenter(-theSprite.getCenterX(),theSprite.getCenterY()); 
            } 
           
        } 
             incFrame(anim);

                if (curFrame == anim.getNumFrames() - 1)
                {
                    isFiring = false;
runOnce2 = true; 
                }
            }
        }
        else
        {
            if (weapon.isFiring() && weapon.getChargeLevel() == 1.0f)
            {
                isFiring = true;
                incFrame(anim);
                anim.setFrame(curFrame);
            }
            else
            {
                curFrame = 0;
                anim.setFrame(curFrame);
        if(runOnce3 == false){ 
            if (weapon.getShip().getOwner() == 0 && weapon.getLocation().getX() < weapon.getShip().getLocation().getX()   
                || weapon.getShip().getOwner() == 1 && weapon.getLocation().getX() > weapon.getShip().getLocation().getX()){ 
                SpriteAPI theSprite = weapon.getSprite(); 
                theSprite.setWidth(-theSprite.getWidth()); 
                theSprite.setCenter(-theSprite.getCenterX(),theSprite.getCenterY()); 
            } 
            runOnce3 = true; 
        }
            }
        }
    }
}
[close]

It looks soemtimes ugly in refit, the weapons are only mirrored in combat, after the first animation cycle all frames and the weapon sprite are mirrored.

Thought I should dump this here.
Title: Re: The Radioactive Code Dump
Post by: Dark.Revenant on April 15, 2014, 05:26:11 AM
Less expensive Tow Cable code.  The trick is that it only runs *some* of the frames rather than *all* of the frames, and even runs all at once instead of going through once per Tow Cable.

Be warned: this is very hackish, but seems to work.  I tried a more elegant approach using onFleetSync but it kept producing infinite feedback loops (killing the framerate just as bad as before), and without some kind of event system I had no way of mitigating that.

At least in this version you can safely have hundreds of Oxen without killing the campaign framerate.  Enjoy.

Spoiler
Code: java
package data.scripts.hullmods;

import com.fs.starfarer.api.Global;
import java.util.*;

import com.fs.starfarer.api.campaign.BuffManagerAPI.Buff;
import com.fs.starfarer.api.campaign.CampaignFleetAPI;
import com.fs.starfarer.api.combat.HullModEffect;
import com.fs.starfarer.api.combat.HullModFleetEffect;
import com.fs.starfarer.api.combat.MutableShipStatsAPI;
import com.fs.starfarer.api.combat.MutableStat;
import com.fs.starfarer.api.combat.ShipAPI;
import com.fs.starfarer.api.combat.MutableStat.StatMod;
import com.fs.starfarer.api.combat.ShipAPI.HullSize;
import com.fs.starfarer.api.fleet.FleetMemberAPI;

public class TowCable implements HullModEffect, HullModFleetEffect {

    public static final String HULLMOD_ID = "tow_cable";

    public static class TowCableBuff implements Buff {

        public int buffAmount = 1;
        private final String buffId;

        public TowCableBuff(String buffId) {
            this.buffId = buffId;
        }

        @Override
        public boolean isExpired() {
            return false;
        }

        @Override
        public String getId() {
            return buffId;
        }

        @Override
        public void apply(FleetMemberAPI member) {
            member.getStats().getMaxBurnLevel().modifyFlat(buffId, buffAmount);
        }

        @Override
        public void advance(float days) {
        }
    };

    private final static float INTERVAL = 0.2f; // in days
    private long timestamp = 0;
    private long lastTimestamp = 0;

    // We only run this once in a while, since exact timing is not an issue in the campaign map
    @Override
    public void advanceInCampaign(CampaignFleetAPI fleet) {
        if (fleet == null) {
            return;
        }

        if (timestamp == 0) {
            timestamp = Global.getSector().getClock().getTimestamp();
            lastTimestamp = timestamp;
        }

        if (Global.getSector().getClock().getElapsedDaysSince(timestamp) < INTERVAL && Global.getSector().getClock().getTimestamp() != lastTimestamp) {
            return;
        }

        timestamp = Global.getSector().getClock().getTimestamp();
        lastTimestamp = timestamp;
        List<FleetMemberAPI> all = fleet.getFleetData().getMembersListCopy();

        int numCables = 0;
        float towSpeed = Float.MAX_VALUE; // We make an assumption that all Oxen have the same burn speed
        for (FleetMemberAPI curr : all) {
            if (!curr.canBeDeployedForCombat()) {
                continue;
            }

            if (curr.getVariant().getHullMods().contains(HULLMOD_ID)) {
                numCables++;
                towSpeed = Math.min(towSpeed, getMaxBurnWithoutCables(curr));
            }
        }
        if (numCables <= 0) {
            cleanUpTowCableBuffs(fleet);
            return;
        }

        Map<FleetMemberAPI, Integer> cables = new HashMap();

        for (int cableIndex = 0; cableIndex < numCables; cableIndex++) {
            FleetMemberAPI slowest = getSlowest(all, towSpeed, cables);
            if (slowest == null) {
                break;
            }
            Integer bonus = cables.get(slowest);
            if (bonus == null) {
                bonus = Integer.valueOf(0);
            }
            bonus = bonus + 1;
            cables.put(slowest, bonus);
        }

        for (FleetMemberAPI curr : all) {
            if (!cables.containsKey(curr)) {
                curr.getBuffManager().removeBuff(TOW_CABLE_KEY);
                continue;
            }

            if (cables.get(curr) <= 0) {
                curr.getBuffManager().removeBuff(TOW_CABLE_KEY);
                continue;
            }

            boolean renew = true;
            for (StatMod mod : curr.getStats().getMaxBurnLevel().getFlatMods().values()) {
                if (mod.getSource().equals(TOW_CABLE_KEY)) {
                    if (mod.value == cables.get(curr)) {
                        renew = false;
                    }
                }
            }

            if (renew) {
                TowCableBuff buff = new TowCableBuff(TOW_CABLE_KEY);
                buff.buffAmount = cables.get(curr);
                curr.getBuffManager().addBuff(buff);
            }
        }
    }

    @Override
    public void onFleetSync(CampaignFleetAPI fleet) {
    }

    public TowCable() {

    }

    @Override
    public void advanceInCampaign(FleetMemberAPI member, float amount) {
    }

    private FleetMemberAPI getSlowest(List<FleetMemberAPI> all, float speedCutoff, Map<FleetMemberAPI, Integer> cables) {
        FleetMemberAPI slowest = null;
        float minLevel = Float.MAX_VALUE;
        for (FleetMemberAPI curr : all) {
            if (!isSuitable(curr)) {
                continue;
            }

            float baseBurn = getMaxBurnWithoutCables(curr);
            Integer bonus = cables.get(curr);
            if (bonus == null) {
                bonus = Integer.valueOf(0);
            }

            if (bonus >= getMaxCablesFor(curr)) {
                continue;
            }

            float burnLevel = baseBurn + bonus;

            if (burnLevel >= speedCutoff) {
                continue;
            }

            if (burnLevel < minLevel) {
                minLevel = burnLevel;
                slowest = curr;
            }
        }
        return slowest;
    }

    private int getMaxCablesFor(FleetMemberAPI member) {
        switch (member.getHullSpec().getHullSize()) {
            case CAPITAL_SHIP:
                return 4;
            case CRUISER:
                return 3;
            case DESTROYER:
                return 2;
            case FRIGATE:
                return 1;
        }
        return 1;
    }

    private static float getMaxBurnWithoutCables(FleetMemberAPI member) {
        MutableStat burn = member.getStats().getMaxBurnLevel();
        float val = burn.getModifiedValue();
        float sub = 0;
        for (StatMod mod : burn.getFlatMods().values()) {
            if (mod.getSource().equals(TOW_CABLE_KEY)) {
                sub = mod.getValue();
                break;
            }
        }
        return Math.max(0, val - sub);
    }

    private boolean isSuitable(FleetMemberAPI member) {
        return !member.isFighterWing();
    }

    private void cleanUpTowCableBuffs(CampaignFleetAPI fleet) {
        if (fleet == null) {
            return;
        }
        for (FleetMemberAPI curr : fleet.getFleetData().getMembersListCopy()) {
            curr.getBuffManager().removeBuff(TOW_CABLE_KEY);
        }
    }

    /**
     * One instance of the buff object per ship with a Tow Cable.
     */
    public static final String TOW_CABLE_KEY = "TowCable_PersistentBuffs";

    @Override
    public void advanceInCombat(ShipAPI ship, float amount) {
    }

    @Override
    public void applyEffectsAfterShipCreation(ShipAPI ship, String id) {
    }

    @Override
    public void applyEffectsBeforeShipCreation(HullSize hullSize, MutableShipStatsAPI stats, String id) {
    }

    @Override
    public boolean isApplicableToShip(ShipAPI ship) {
        return true;
    }

    @Override
    public String getDescriptionParam(int index, HullSize hullSize) {
        return null;
    }

}
[close]
Title: Re: The Radioactive Code Dump
Post by: Sundog on April 27, 2014, 01:42:23 PM
A hull mod for stationary structures/buildings/sentry guns/stations with predefined locations.

Spoiler
To use it, create a hullmod that uses the following script:
Code: java
package data.hullmods;

import com.fs.starfarer.api.combat.CombatEntityAPI;
import com.fs.starfarer.api.combat.ShipAPI;
import com.fs.starfarer.api.fleet.FleetMemberAPI;
import java.util.WeakHashMap;
import org.lwjgl.util.vector.Vector2f;

public class GravityAnchor extends BaseHullMod {
    static WeakHashMap locations = new WeakHashMap();
    static WeakHashMap facings = new WeakHashMap();
    
    public static void anchorShip(FleetMemberAPI ship, Vector2f location, float angle) {
        locations.put(ship.getId(), location);
        facings.put(ship.getId(), angle);
    }
    public static void anchorShip(FleetMemberAPI ship, Vector2f location) {
        locations.put(ship.getId(), location);
    }
    public static void anchorShip(ShipAPI ship, Vector2f location, float angle) {
        locations.put(ship.getFleetMemberId(), location);
        facings.put(ship.getFleetMemberId(), angle);
    }
    public static void anchorShip(ShipAPI ship, Vector2f location) {
        locations.put(ship.getFleetMemberId(), location);
    }

    public void setLocation(CombatEntityAPI entity, Vector2f location) {
        Vector2f dif = new Vector2f(location);
        Vector2f.sub(location, entity.getLocation(), dif);
        Vector2f.add(entity.getLocation(), dif, entity.getLocation());
    }

    @Override
    public void advanceInCombat(ShipAPI ship, float amount) {
        super.advanceInCombat(ship, amount);
        
        String id = ship.getFleetMemberId();
        
        if(!locations.containsKey(id)) return;
        else setLocation(ship, (Vector2f)locations.get(id));
        
        if(!facings.containsKey(id)) return;
        else ship.setFacing((Float)facings.get(id));
    }

    @Override
    public boolean isApplicableToShip(ShipAPI ship){
        return false;
    }
}

Add the hullmod to the .ship file of the ship you want to remain stationary.

Use GravityAnchor.anchorShip() to assign a location and optional facing to the ship.
In a mission definition, that would look something like this;
Code: java
FleetMemberAPI member = api.addToFleet(side, variantID, FleetMemberType.SHIP, false);
GravityAnchor.anchorShip(member, new Vector2f(100, -100), 360);

Disclaimer: this isn't tested thoroughly at all, and I'm sure it causes issues with collision resolution.
[close]
Title: Re: The Radioactive Code Dump
Post by: xenoargh on April 29, 2014, 08:51:46 AM
Caveat:  it's considerably better (imo) to do the above through custom EveryFrameCombatScript plugins invoked when the battle is constructed.  Just been down that road while writing up an Admiral AI :)
Title: Re: The Radioactive Code Dump
Post by: Sundog on April 29, 2014, 10:21:21 AM
Originally I decided to use a hullmod to make it easy to specify which ships are stationary and which ones aren't, but since a function call is necessary in the mission definition anyway, I guess it would make more sense to use an EveryFrameCombatPlugin.
Title: Re: The Radioactive Code Dump
Post by: Debido on May 19, 2014, 02:00:44 AM
Hard flux generation for beam weapons

Code: java

package data.scripts.weapons;

import com.fs.starfarer.api.combat.BeamAPI;
import com.fs.starfarer.api.combat.BeamEffectPlugin;
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.util.IntervalUtil;

/**
 *
 * @author Debido
 * fragments derived from Xenoargh's MegaBeamDamageEffect plugin
 *
 * This script is intended to allow the given Beam weapon using this script to apply hard flux damage to the enemy ship if the beam hits their shield
 * This is probably OP, so be careful with game balancing!
 */

public class BeamFullFlux implements BeamEffectPlugin {

    private final IntervalUtil fireInterval = new IntervalUtil(0.1f, 0.1f); //interval between applying flux
    private final float fluxMultiplier = 10.0f; //determines how much hard flux is generated in the enemy ship. 1.0f would be 100% of weapon DPS, 0.1f would be 10%, and 10f would be 1000%

    @Override
    public void advance(float amount, CombatEngineAPI engine, BeamAPI beam) {

        if (engine.isPaused()) {
            return;
        }

        fireInterval.advance(amount);

        CombatEntityAPI target = beam.getDamageTarget();
        //If we have a target, target is a Ship, and shields are being hit.
        if (target != null && target instanceof ShipAPI && target.getShield() != null && target.getShield().isWithinArc(beam.getTo())) {
            if (fireInterval.intervalElapsed()) {
                if (beam.getBrightness() >= 1f) {
                    ShipAPI ship = (ShipAPI) target; //cast as ship
                    ship.getFluxTracker().increaseFlux(fluxMultiplier * beam.getWeapon().getDerivedStats().getDps() / 10f, true); //apply flux
                }

            }

        }
    }
}
Title: Re: The Radioactive Code Dump
Post by: xenoargh on May 19, 2014, 12:53:35 PM
There's actually a much more reliable / accurate method in the newer Vacuum builds:

Spoiler
Code: java
package data.scripts.weapons;

import com.fs.starfarer.api.combat.BeamAPI;
import com.fs.starfarer.api.combat.WeaponAPI;
import com.fs.starfarer.api.combat.WeaponAPI.DerivedWeaponStatsAPI;
import com.fs.starfarer.api.combat.BeamEffectPlugin;
import com.fs.starfarer.api.combat.CombatEngineAPI;
import com.fs.starfarer.api.combat.CombatEntityAPI;
import com.fs.starfarer.api.combat.DamageType;
import com.fs.starfarer.api.combat.ShipAPI;

public class BeamDamageEffect implements BeamEffectPlugin {
    @Override
    public void advance(float amount, CombatEngineAPI engine, BeamAPI beam) {
        if (engine.isPaused()) return;
        if(beam.getBrightness() < 0.99f) return;
        float frameTime = engine.getElapsedInLastFrame();
       
        //Get the beam's target
        CombatEntityAPI target = beam.getDamageTarget();

        //If we have a target, target is a Ship, and shields are being hit.   
        if (target != null && target instanceof ShipAPI && target.getShield() != null && target.getShield().isWithinArc(beam.getTo())) {
            //Now that we have the target, get the weapon ID and get the DPS
            WeaponAPI weapon = beam.getWeapon();
            DamageType damType = weapon.getDamageType();
            DerivedWeaponStatsAPI stats = weapon.getDerivedStats();

            engine.applyDamage(
                target, //enemy Ship
                beam.getTo(), //Our 2D vector to the exact world-position
                dps * frameTime, //We're dividing the DPS by the time that's passed here.
                damType, //Using the damage type here.
                0f, //No EMP, as EMP already has specific rules.  However EMP could go through shields this way if we wanted it to.
                false, //Does not bypass shields.
                false, //Does not do Soft Flux damage (would kind've defeat the whole point, eh?
                beam.getSource()  //Who owns this beam?
             );
        }
    }
}
[close]
Title: Re: The Radioactive Code Dump
Post by: xenoargh on June 05, 2014, 08:55:36 PM
So... are Decorative weapons showing up on your ships after you set them up in Trylobot's otherwise-wonderful editor? 

Do you have other, ah, weird cases, where you definitely want a weapon to not show up in weapon slots, whether players put them there or not, and the engine limitations on Decorative / System / whatever won't quite do it for ya?  Use this snippet :)

Run in an EveryFrameWeaponEffectPlugin script, using a one-time boolean in Advance():

Spoiler
Code: java
        if(runOnce){
            String weaponSlot = weapon.getSlot().getId();
            List<WeaponGroupSpec> weaponGroups = weapon.getShip().getVariant().getWeaponGroups();
            for(WeaponGroupSpec group : weaponGroups){
                List<String> slots = group.getSlots();
                String slotID = new String();
                for(String slot : slots){
                    if(slot.equalsIgnoreCase(weaponSlot)){
                        slotID = slot.toString();
                    }
                }
                if(slotID.equalsIgnoreCase(weaponSlot)) group.removeSlot(slotID);
            }     
            runOnce = false;
        }
[close]



Title: Re: The Radioactive Code Dump
Post by: ValkyriaL on June 06, 2014, 03:50:55 AM
This is very easily fixed with trylobot tho, if you save a variant with decorative weapons, then they are added, if you only save the hull file with the decoratives, then the variant won't have them, but they will still be there, alternatively you can open the variant file and delete the decorative weapons and they are gone from the variant.
Title: Re: The Radioactive Code Dump
Post by: xenoargh on June 06, 2014, 12:34:33 PM
Every time you modify a variant in SFEd you have to do that, and sometimes I do stuff where I have to modify every single ship (like when I added the "vernier thrusters" feature- I think I only had 185 ships then, but meh) and I change variants quite often to rebal.  It all added up to quite a bit of extra work due to one smallish issue with SFEd.  Alex's answer gave me the clue I needed to remove it automatically.

There are probably some other uses for something like this, too :)
Title: Re: The Radioactive Code Dump
Post by: Thaago on June 09, 2014, 01:01:57 PM
Big thanks to everyone who has posted in this thread - I'm making a slew of missile AI's for a TC and this has probably saved me 10 hours of work. :D You all get a beer.
Title: Re: The Radioactive Code Dump
Post by: Debido on August 09, 2014, 04:19:57 AM
This bomber code was considered too powerful for general use

(https://www.dropbox.com/s/6wj2bwtat13ph1p/sps2.gif?raw=1)

Code: java
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package uaf.data.scripts.weapons;

import com.fs.starfarer.api.combat.CombatEngineAPI;
import com.fs.starfarer.api.combat.DamageType;
import com.fs.starfarer.api.combat.EveryFrameWeaponEffectPlugin;
import com.fs.starfarer.api.combat.ShipAPI;
import com.fs.starfarer.api.combat.WeaponAPI;
import java.awt.Color;
import java.util.List;
import org.lazywizard.lazylib.CollisionUtils;
import org.lazywizard.lazylib.MathUtils;
import org.lazywizard.lazylib.combat.CombatUtils;
import org.lwjgl.util.vector.Vector2f;

/**
 *
 * @author Debido
 */
public class SPSBomber implements EveryFrameWeaponEffectPlugin {
    
    private static final Color BOOM_COLOR = new Color(255, 165, 50, 245);
    private static final Vector2f ZERO_VECTOR = new Vector2f(0f,0f);

    @Override
    public void advance(float amount, CombatEngineAPI engine, WeaponAPI weapon) {

        ShipAPI ship = weapon.getShip();

        if (ship == null || !engine.isEntityInPlay(ship) || weapon.getAmmo() == 0) {
            return;
        }
        

        List<ShipAPI> ships = CombatUtils.getShipsWithinRange(ship.getLocation(), 100f);

        for (ShipAPI s : ships){
            if (s.isAlive() && s.getOwner() != ship.getOwner()){
                if (CollisionUtils.isPointWithinBounds(ship.getLocation(), s)){
                    
                    weapon.setAmmo(0);
                    engine.applyDamage(s, ship.getLocation(), weapon.getDerivedStats().getDamagePerShot(), DamageType.HIGH_EXPLOSIVE, 0f, false, false, ship);
                    engine.spawnExplosion(ship.getLocation(), ZERO_VECTOR, BOOM_COLOR, 500f, 1f);
                    engine.addSmokeParticle(ship.getLocation(), ZERO_VECTOR, 600f, 0.9f, 5f, Color.DARK_GRAY);
                    for (int i = 0; i < 10; i++){
                        Vector2f radialVector = MathUtils.getRandomPointOnCircumference(ship.getLocation(), 500f);
                        engine.addHitParticle(ship.getLocation(), radialVector , 10f, 255, 2f, BOOM_COLOR);
                    }
                }
                
            }
        }

    }

}

Title: Re: The Radioactive Code Dump
Post by: ValkyriaL on August 09, 2014, 05:39:10 AM
Now THAT is cool, id love that from my heavy bombers, the bomber itself also seems incredibly valkyrian themed, kitbash maybe? :D
Title: Re: The Radioactive Code Dump
Post by: Debido on August 09, 2014, 06:05:04 AM
It was pretty cool, however consultation from resident senior modder Mesotronik advised me it's somewhat OP - Oh and it bypasses his beloved Exigency repulsor like it was never there.

Anyway I now have some slower bombers in the UAF arsenal that aren't anywhere near as effective, that way it's a bit more like the dagger wing, and less like a cool bunker buster. Oh well. Unless there is a way to balance such an awesome force, I don't think it will make the final cut.
Title: Re: The Radioactive Code Dump
Post by: FasterThanSleepyfish on August 09, 2014, 09:15:20 AM
If there was some way to make it an EMP bomb... that'd be more balanced perhaps.
Title: Re: The Radioactive Code Dump
Post by: Dark.Revenant on August 09, 2014, 12:43:41 PM
The difficulty of balance comes with the difficulty of trying to deal with that bomber.  When Piranhas attack, you can see them coming and swivel your shields, get out of the way, hit them with mass flak, etc.  When Daggers attack, you can intercept them, shoot capital-class weapons at the reapers, get out of the way, soak them on your shield, etc.  Tridents go much the same way.

Bombers are designed, in this game, to be things that can be defeated with perception, appropriate tactics, and planning.  Your bomber, on the other hand, has no adequate countermeasures.  It's too fast to dodge or shoot down, the bomb can't be shot down, etc.  The only option is to soak it on shields, and if the ship doesn't have shields it's just ***.
Title: Re: The Radioactive Code Dump
Post by: Debido on August 09, 2014, 07:15:40 PM
Actually it could still be shot down by energy/laser weapons fairly frequently, and with tweaking the bomber pretty much took 5000su to turn around. It was designed to be fairly ineffective in turning and as such often it would simply fly straight past targets as big as a cruiser if it moved out of the way. Frigates or destroyers could side step fairly easy with enough time. Dunno, might make it a capture only enemy only ship. That is it cannot be captured, or if it is I replace it with the watered down version.

It's fairly trivial to make it an EMP only type damage, that's just modifying line 46 to have EMP damage (engine.applyDamage).
Title: Re: The Radioactive Code Dump
Post by: Tartiflette on August 29, 2014, 04:49:28 AM
My own small contribution to this "dump":
Slashing beam:
Spoiler
(http://i.imgur.com/4iBOZbs.gif)
[close]
Works in two modes depending of the arc available, and two speeds in each.

Code: Java
//by Tartiflette, this script allow for a beam weapon to slash during the main firing sequence
//feel free to use it, credit is appreciated but not mandatory
package data.scripts.weapons;

import com.fs.starfarer.api.combat.CombatEngineAPI;
import com.fs.starfarer.api.combat.EveryFrameWeaponEffectPlugin;
import com.fs.starfarer.api.combat.WeaponAPI;

public class SCY_slasherBeamEffect implements EveryFrameWeaponEffectPlugin {

private boolean isFiring = false;
private float aim = 0.0f;
private float count = 0.0f;
private final float offset = 0.5f;
private boolean hasFired = false;
private boolean RUN_ONCE = false;
private float multiplier = 1;
private boolean turretMode = true;
private float arc = 0.0f;
private float arcFacing = 0.0f;

    
    @Override
    public void advance(float amount, CombatEngineAPI engine, WeaponAPI weapon) {
        // Don't bother with any checks if the game is paused
        if (engine.isPaused()) {
            return;
        }
        
        //run only once to check the available arc, double the slashing width if arc > 90 degrees, hardpoint mode if arc < 20 degrees
        
        if (!RUN_ONCE) {
            if (weapon.getArc() >= 90f) {
                multiplier = 2;
            }
            if (weapon.getArc() <= 10f) {
                multiplier = 0.5f;
            }
            if (weapon.getArc() <= 20f) {
                turretMode = false;
                arc = weapon.getArc();
                arcFacing = weapon.getArcFacing();
                aim = 0.5f;
            }
            RUN_ONCE = true;
        }
        
        
        if (!turretMode) {
            //HARPOINT MODE: goes back and forth in the arc for the duration
            
            if (weapon.isFiring()) {
                
                //initialize the first offset to start on the same direction the weapon is facing
                if (!hasFired) {
                    count = weapon.getCurrAngle() - arcFacing - weapon.getShip().getFacing();
                    hasFired = true;
                }
                
                //if the weapon hit an arc limit, change the rotation direction
                if (count >= ( arc * 0.5f )) {
                    aim = -0.5f*multiplier;                    
                }
                
                if (count <= -( arc * 0.5f )) {
                    aim = 0.5f*multiplier;                    
                }
              
                //slashing in the arc
                weapon.setCurrAngle( weapon.getShip().getFacing() + arcFacing + count );
                count = count + aim;
            }
            //reset the variables when the weapon is cooling down
            if (!weapon.isFiring() && hasFired) {
                hasFired = false;
                aim = 0.5f;
                count = 0f;
            }
            
        } else {
            //TURRET MODE: one large slash
            //charging: the gun turn left in preparation of the slashing
            if (weapon.isFiring() && weapon.getChargeLevel() < 1.0f && !hasFired) {

                //the gun orientation is stored before being altered
                if (weapon.isFiring() && isFiring == false) {
                    aim = weapon.getCurrAngle();
                }

                isFiring = true;
                count++;
                weapon.setCurrAngle(aim + (count * offset * multiplier));
            }

            //firing: the gun slash to the right
            if (weapon.isFiring() && weapon.getChargeLevel() == 1.0f) {
                hasFired = true;
                count = count - 0.5f;
                weapon.setCurrAngle(aim + (count * offset * multiplier));
            }

            //cooldown: the weapon is released and the variables reseted
            if (!weapon.isFiring() && hasFired) {
                isFiring = false;
                hasFired = false;
                aim = 0.0f;
                count = 0f;
            }
        }
    }
}

Vibrating beam:
Spoiler
(http://giant.gfycat.com/GregariousGentleDoe.gif)
[close]
Randomly reorient the weapon every few moments.
Code: Java
//by Tartiflette, this script allow for a beam weapon to vibrate at a predetermined frequency during the main firing sequence
//feel free to use it, credit is appreciated but not mandatory
package data.scripts.weapons;


import com.fs.starfarer.api.combat.CombatEngineAPI;
import com.fs.starfarer.api.combat.EveryFrameWeaponEffectPlugin;
import com.fs.starfarer.api.combat.WeaponAPI;
import com.fs.starfarer.api.util.IntervalUtil;
import org.lazywizard.lazylib.MathUtils;

public class SCY_vibratingBeamEffect implements EveryFrameWeaponEffectPlugin
{
    private boolean isFiring = false;
    private float aim = 0.0f;
    private float offset = 0.0f;
    private final IntervalUtil timer = new IntervalUtil(0.05f, 0.05f);
    
    @Override
    public void advance(float amount, CombatEngineAPI engine, WeaponAPI weapon)
    {
        // Don't bother with any checks if the game is paused
        if (engine.isPaused()) {
            return;
        }
        
        if (weapon.isFiring())
        {
            //don't vibrate during chargeup
            if (weapon.getChargeLevel() == 1.0f)        
            {

                //store the weapon orientation once it reach full power
                if (weapon.isFiring() && !isFiring) {
                    aim = weapon.getCurrAngle();
                    isFiring = true;
                }

                //change the orientation every few moments
                if(timer.intervalElapsed())
                    {
                    offset = (float)MathUtils.getRandomNumberInRange(-2f, 2f);
                    }
                timer.advance(amount);

                //lock the weapon orientation every frame to avoid trying to aim back to the curser
                weapon.setCurrAngle(aim + offset);
            }
        else
            {
                isFiring = false;
                offset = 0.0f;
                aim = 0.0f;
            }
        }
    }
}
Title: Re: The Radioactive Code Dump
Post by: Tartiflette on January 30, 2015, 01:59:51 AM
Well, I was tempted to try a clip reload mechanic since the patch is still not in sight and here it is. After a set timer since the last shot fired the ammo get reloaded by a set amount, nice and easy. To properly function, the weapon need an ammo regeneration equivalent to the clip reloading (for the weapon tooltip) and a cooldown longer than one frame.

Code: Java
//by Tartiflette, this script allow for a weapon to reload it's ammo after some time since it's last shot
//feel free to use it, credit is appreciated but not mandatory
package data.scripts.weapons;

import com.fs.starfarer.api.combat.CombatEngineAPI;
import com.fs.starfarer.api.combat.EveryFrameWeaponEffectPlugin;
import com.fs.starfarer.api.combat.WeaponAPI;

public class SCY_clipEffect implements EveryFrameWeaponEffectPlugin
{
    private boolean runOnce = false;
    private float timer = 0f;
    private int clipSize;
    private int magazine;
    private int clipTimer;
   
    @Override
    public void advance(float amount, CombatEngineAPI engine, WeaponAPI weapon)
    {
        // Don't bother with any checks if the game is paused
        if (engine.isPaused()) {
            return;
        }
       
        // store the max reloading ammo amount, the timer between reloads and initialise the current magazine size
        //WARNING to properly function in autoresolve, the weapon need the equivalent of the clip reloading in ammo regen and a cooldown
        if (!runOnce){
            if (weapon.getId().equals("SCY_laserRepeater_mkiii")){
                clipSize=5;
                clipTimer=5;
            } else {
                //default values
                clipSize=weapon.getMaxAmmo();
                clipTimer=5;
            }
            magazine = weapon.getMaxAmmo();
            runOnce=true;
        }
       
        //prevent normal ammo regeneration
        if (weapon.getAmmo()>magazine){
            weapon.setAmmo(magazine);                   
        }
       
        //only run if the ammo isn't full
        if (weapon.getAmmo()<weapon.getMaxAmmo()){
            //lock the weapon if the ammo reach 0
            //necessary because the weapons can fire upon the ammo regeneration before the script kicks in and remove it
            if(weapon.getAmmo()==0){
                weapon.setRemainingCooldownTo(1);
            }
            //interrupt the reloading if the weapon fire
            if(weapon.isFiring()){
                timer = 0;
                magazine = weapon.getAmmo();
            } else {           
                //timer
                timer+=amount;
                //timer elapsed: reload and reset the timer
                if(timer>=clipTimer){
                    //add a clip to the current ammo
                    weapon.setAmmo(Math.min(weapon.getMaxAmmo(),magazine+clipSize));
                    timer = 0;
                    magazine = weapon.getAmmo();
                    //reset the cooldown
                    weapon.setRemainingCooldownTo(0);
                }
            }
        }
    }
}
Title: Re: The Radioactive Code Dump
Post by: Tartiflette on April 07, 2015, 05:10:50 PM
A big one this time: Custom Muzzle flashes and animation plugin!

It started with this weapon for witch I wanted a muzzle animation independent from the barrel animation, but the glow just wouldn't cut it:
Spoiler
(http://i.imgur.com/W22WBqg.gif)
[close]

So I made a plugin that when it get it's MEMBERS Hashmap fed with WeaponAPIs by weapon scripts, it add muzzle flashes to them.

The plugin:
Code: Java
//By Tartiflette, allows for custom muzzle flashes and other weapons animations without damage decals troubles
package data.scripts.plugins;

import com.fs.starfarer.api.Global;
import com.fs.starfarer.api.combat.BaseEveryFrameCombatPlugin;
import com.fs.starfarer.api.combat.CombatEngineAPI;
import com.fs.starfarer.api.combat.ViewportAPI;
import com.fs.starfarer.api.combat.WeaponAPI;
import com.fs.starfarer.api.graphics.SpriteAPI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.lazywizard.lazylib.MathUtils;
import static org.lwjgl.opengl.GL11.GL_TEXTURE_2D;
import static org.lwjgl.opengl.GL11.glEnable;
import org.lwjgl.util.vector.Vector2f;

public class SCY_muzzleFlashesPlugin extends BaseEveryFrameCombatPlugin {
  
    //MEMBERS is the core of the script, storing both the weapons that have a flash rendered and the index of the current sprite used
    public static Map< WeaponAPI , Float > MEMBERS = new HashMap<>();
    //set the function to access MEMBERS from the weapons scripts    
    public static void addMember(WeaponAPI weapon, Float index) {
        MEMBERS.put(weapon, index);
    }
    public static void removeMember(WeaponAPI weapon) {
        MEMBERS.remove(weapon);
    }
    
    private List<WeaponAPI> toRemove = new ArrayList<>();
    
    //All the info needed, per muzzle type.
    //minigun MKI
    public final String MINIGUN_I_ID = "SCY_minigun_mki";
    //load the muzzle sprites map. Since it's a random muzzle I keep the index 0 free to skip some frame from time to time.
    private final Map<Integer, SpriteAPI> MINIGUN_I = new HashMap<>();    
    {
        MINIGUN_I.put(1, Global.getSettings().getSprite("muzzle", "SCY_minigunMki_muzzle01"));  
        MINIGUN_I.put(2, Global.getSettings().getSprite("muzzle", "SCY_minigunMki_muzzle02"));  
        MINIGUN_I.put(3, Global.getSettings().getSprite("muzzle", "SCY_minigunMki_muzzle03"));  
        MINIGUN_I.put(4, Global.getSettings().getSprite("muzzle", "SCY_minigunMki_muzzle04"));
        MINIGUN_I.put(5, Global.getSettings().getSprite("muzzle", "SCY_minigunMki_muzzle05"));
        MINIGUN_I.put(6, Global.getSettings().getSprite("muzzle", "SCY_minigunMki_muzzle06"));
    }
    //set the muzzle sprite size
    private final float minigun1Width = 28;
    private final float minigun1Height = 53;
    //set the offset from the weapon's center. T for turret, H for Hardpoint. I always keep my muzzle sprites on the weapon axis to only have to worry about the length offset.
    private final float minigun1TOffset=25;
    private final float minigun1HOffset=25;
    
    //KAcc MKII
    public final String KACC2_ID = "SCY_kacc_mkii";
    private final Map<Integer, SpriteAPI> KACC2 = new HashMap<>();
    {
        KACC2.put(0, Global.getSettings().getSprite("muzzle", "SCY_kaccMkii_muzzle00"));  
        KACC2.put(1, Global.getSettings().getSprite("muzzle", "SCY_kaccMkii_muzzle01"));
        KACC2.put(2, Global.getSettings().getSprite("muzzle", "SCY_kaccMkii_muzzle02"));
        KACC2.put(3, Global.getSettings().getSprite("muzzle", "SCY_kaccMkii_muzzle03"));
        KACC2.put(4, Global.getSettings().getSprite("muzzle", "SCY_kaccMkii_muzzle04"));
        KACC2.put(5, Global.getSettings().getSprite("muzzle", "SCY_kaccMkii_muzzle05"));
    }
    private final float kacc2Width = 34;
    private final float kacc2Height = 48;
    private final float kacc2TOffset = 11;
    private final float kacc2HOffset = 15;
    private final float kacc2Delay = 0.05f;
    
    @Override
    public void init(CombatEngineAPI engine) {
        //initialise all the data
        MEMBERS.clear();        
    }
    
    @Override
    public void renderInWorldCoords(ViewportAPI view)
    {
        CombatEngineAPI engine = Global.getCombatEngine();
        if (engine == null){return;}
        
        if (!MEMBERS.isEmpty())
        {
            float amount = (engine.isPaused() ? 0f : engine.getElapsedInLastFrame());
            
            //dig through the MEMBERS
            for (Iterator<Map.Entry< WeaponAPI , Float >> iter = MEMBERS.entrySet().iterator(); iter.hasNext(); ) {                
                Map.Entry< WeaponAPI , Float > entry = iter.next();  
                //Apply the right effect for the right type of weapon
                switch (entry.getKey().getId()) {
                    
                    case MINIGUN_I_ID:
                        //Check if the ship is in play
                        if (!engine.isEntityInPlay(entry.getKey().getShip())){
                            toRemove.add(entry.getKey());
                            break;
                        }
                        //random muzzle flash
                        //turret or hardpoint offset
                        float minigun1Offset;
                        if(entry.getKey().getSlot().isHardpoint()){
                            minigun1Offset=minigun1HOffset;
                        } else {
                            minigun1Offset=minigun1TOffset;
                        }
                        //change the muzzle sprite 8 out of 10 frames if the engine isn't paused
                        if (!engine.isPaused() && Math.random()>0.2f){
                            //place the new muzzle sprite index in the MEMBERS map
                            MEMBERS.put(entry.getKey(), (float)MathUtils.getRandomNumberInRange(0, 6));
                        }
                        //skip if the index is 0, to give accidents in the animation like the real thing
                        if (entry.getValue()!=0){
                            //call the render                            
                            render(entry.getKey(), MINIGUN_I.get((int)Math.round(entry.getValue())), minigun1Width, minigun1Height, minigun1Offset, true);                            
                        }                        
                        break;
                        
                    case KACC2_ID:
                        //animated muzzle flash
                        //turret or hardpoint offset
                        float kacc2Offset;
                        if(entry.getKey().getSlot().isHardpoint()){
                            kacc2Offset=kacc2HOffset;
                        } else {
                            kacc2Offset=kacc2TOffset;
                        }
                        int kacc2Frame;
                        //put the animation timer as the MEMBERS' value
                        //randomly keep the muzzle frame a bit longer
                        if (!engine.isPaused() && Math.random()>0.25){
                            MEMBERS.put(entry.getKey(), amount+entry.getValue());                      
                        }
                        //calculate the current frame from the animation's time
                        kacc2Frame = (int)Math.abs(entry.getValue()/kacc2Delay);
                        //add to the remove list and break if the animation is finished
                        if (kacc2Frame>KACC2.size()-1){
                            toRemove.add(entry.getKey());
                            break;
                        }  
                        //randomly flip the muzzle flash
                        float flip2 = 1;
                        if (Math.sin(100*entry.getValue())>0 ){flip2=-1;}
                        //call the renderer
                        render(entry.getKey(), KACC2.get(kacc2Frame), flip2*kacc2Width, kacc2Height, kacc2Offset, true);                          
                        break;
                }
            }
            //remove the weapons that needs to
            //can't be done from within the iterator or it will fail when members will be missing
            if (!toRemove.isEmpty()){
                for(WeaponAPI w : toRemove ){
                    MEMBERS.remove(w);
                }
                toRemove.clear();
            }
        }
    }
    
    private void render (WeaponAPI weapon, SpriteAPI sprite, float width, float height, float offset, boolean additive){
        //where the magic happen
        sprite.setAlphaMult(1);
        sprite.setSize(width, height);
        if (additive){
            sprite.setAdditiveBlend();
        }
        float aim = weapon.getCurrAngle();
        Vector2f loc =  MathUtils.getPointOnCircumference(weapon.getLocation(),offset,aim); //apply muzzle the offset
        sprite.setAngle(aim-90);
        sprite.renderAtCenter(loc.x, loc.y);

    }
}
I made two example of animation types: the first MINIGUN_ID pull random sprites from a list until the weapon's script removes it, the second ORION_ID play an animation and get removed at the end of it.
The animation call look like this:
Code: Java
//By Tartiflette
package data.scripts.weapons;

import com.fs.starfarer.api.combat.CombatEngineAPI;
import com.fs.starfarer.api.combat.EveryFrameWeaponEffectPlugin;
import com.fs.starfarer.api.combat.WeaponAPI;
import data.scripts.plugins.SCY_muzzleFlashesPlugin;

public class SCY_addToMuzzlePlugin implements EveryFrameWeaponEffectPlugin{
    
    private boolean put=false;
    private boolean runOnce=false;
    private boolean hidden=false;
    
    @Override
    public void advance (float amount, CombatEngineAPI engine, WeaponAPI weapon) {
        
        if(!hidden && engine.isPaused()){return;}
        
        if(!runOnce){
            runOnce=true;
            //check if the mount is hidden
            if (weapon.getSlot().isHidden()){
                hidden=true;
                return;
            }
        }
        
        //assign the weapon for the muzzle flash plugin
        if (weapon.getChargeLevel()==1){
            //add the weapon to the MEMBERS map when it fires
            //"put" is to make sure it's added only once
            if (!put){
                put=true;            
                SCY_muzzleFlashesPlugin.addMember(weapon,0f);
            }
        } else if (weapon.getChargeLevel()== 0){
            //reinitialise "put"
            if (put){
                put=false;
            }
        }
    }
}
Simple, the weapon fires, it get added to MEMBERS and the plugin play the animation.
For the random flashes:
Code: Java
        if (!hidden){
            //assign the weapon for the muzzle flash plugin
            if (weapon.getChargeLevel()==1){
                //add the weapon to the MEMBERS map
                if (!put){
                    put=true;            
                    SCY_muzzleFlashesPlugin.addMember(weapon,0f);
                }
            } else if (weapon.getChargeLevel()== 0){
                //remove the weapon from the MEMBERS map
                if (put){
                    put=false;
                    SCY_muzzleFlashesPlugin.removeMember(weapon);
                }
            }
        }
    }
}
This one removes the weapon from MEMBERS when it stops firing.
Of course the sprites used need to be loaded in the settings:
Code
	"muzzle":{
"SCY_minigunMki_muzzle01":"graphics/SCY/weapons/SCY_minigun_mki/SCY_minigunMki_muzzle_01.png",
"SCY_minigunMki_muzzle02":"graphics/SCY/weapons/SCY_minigun_mki/SCY_minigunMki_muzzle_02.png",
"SCY_minigunMki_muzzle03":"graphics/SCY/weapons/SCY_minigun_mki/SCY_minigunMki_muzzle_03.png",
"SCY_minigunMki_muzzle04":"graphics/SCY/weapons/SCY_minigun_mki/SCY_minigunMki_muzzle_04.png",
"SCY_minigunMki_muzzle05":"graphics/SCY/weapons/SCY_minigun_mki/SCY_minigunMki_muzzle_05.png",
"SCY_minigunMki_muzzle06":"graphics/SCY/weapons/SCY_minigun_mki/SCY_minigunMki_muzzle_06.png",

"SCY_orionMkiii_muzzle01":"graphics/SCY/weapons/SCY_orionMkiii/SCY_orionMkiiiMuzzle_01.png",
"SCY_orionMkiii_muzzle02":"graphics/SCY/weapons/SCY_orionMkiii/SCY_orionMkiiiMuzzle_02.png",
"SCY_orionMkiii_muzzle03":"graphics/SCY/weapons/SCY_orionMkiii/SCY_orionMkiiiMuzzle_03.png",
},
And that's it, add the weapon to the MEMBERS via a everyframeWeaponEffect, set all the sprites in the options and the init and it should works fine as it is.
[EDIT] changed the weapons scripts to avoid being called on weapons in hidden mounts. Depending on the weapon this could or couldn't be necessary.
[EDIT2] corrected a second slight mistake that made the animated flashes not display properly if the game is paused.
[EDIT3] added separate turret offset and hardpoint offset, also changed one of the animation example by one that randomly hold the frame longer or flip it to create more variations.
[EDIT4] variable are now final initialized: having them in the init was useful to play with the offset values in debug mode without relaunching the game, but caused NPE on campaign map. Also converted the Sprites key to integer.
Title: Re: The Radioactive Code Dump
Post by: xenoargh on April 09, 2015, 10:04:52 PM
Cool, there are lots of good uses for that :)  Thanks for sharing the framework!
Title: Re: The Radioactive Code Dump
Post by: Tartiflette on April 10, 2015, 12:05:03 AM
Lots of good uses indeed though as you can guess, these muzzles are drawn above everything else. Even explosions. That's why I used additive blending but still I have some problems with overlapping weapons, or fighters flying above the weapon but under the flash... That's why I avoid using it for small weapons (besides the small minigun 'cause I have no choice). With that little problem in mind, yes it could be customized for other useful stuff. From cool ship-system area effects, to custom ship explosions.
Title: Re: The Radioactive Code Dump
Post by: Tartiflette on April 12, 2015, 03:28:47 AM
Also putting here my simple missile and rocket AIs. I think they are easy to understand and customize. Also since they are "simple" they are quite fast on the CPU, and spamming missiles is much less an issue.

Code: Java
//By Tartiflette, simple and fast Missile AI that will lead it's target and avoid endless circling. Also Engage a new target if the current one get destroyed
package data.scripts.ai;

import com.fs.starfarer.api.Global;
import com.fs.starfarer.api.combat.CombatEngineAPI;
import com.fs.starfarer.api.combat.CombatEntityAPI;
import com.fs.starfarer.api.combat.GuidedMissileAI;
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 org.lazywizard.lazylib.MathUtils;
import org.lazywizard.lazylib.VectorUtils;
import org.lazywizard.lazylib.combat.AIUtils;
import org.lwjgl.util.vector.Vector2f;

public class SCY_simpleMissileAI implements MissileAIPlugin, GuidedMissileAI {
    
    private CombatEngineAPI engine;
    private final MissileAPI missile;
    private CombatEntityAPI target;
    private Vector2f lead = new Vector2f(0f,0f);
    
    // Distance under witch the missile cease to lead the target aim directly for it
    private final float closeRange = 750;
    // Max area the missile seek a target in if the launching ship don't have one
    private final float maxSearchRange = 1000;
    // Angle with the target beyond witch the missile will first turn around before accelerating again
    private final float overshoot = 45;
    //data
    private final float flightSpeed;
    private final static float damping = 0.1f;
    //delay between target actualisation
    private final IntervalUtil actualisation = new IntervalUtil(0.2f, 1f);
    private boolean launch=true;
    private boolean runOnce=false;
    private float eccm=3;

    //////////////////////
    //  DATA COLLECTING //
    //////////////////////
    
    public SCY_simpleMissileAI(MissileAPI missile, ShipAPI launchingShip) {
        this.missile = missile;
        flightSpeed = missile.getMaxSpeed();
    }
    
    //////////////////////
    //   MAIN AI LOOP   //
    //////////////////////
    
    @Override
    public void advance(float amount) {
        
        if (engine != Global.getCombatEngine()) {
            this.engine = Global.getCombatEngine();
        }        
        if(!runOnce){
            runOnce=true;            
            for (String s : missile.getSource().getVariant().getHullMods()) {
                if (s.equals("ECCMPackage")){
                    eccm=1;
                }
            }
        }
        
        //skip the AI if the game is paused, the missile is engineless or fading
        if (Global.getCombatEngine().isPaused() || missile.isFading() || missile.isFizzling()) {return;}
        
        //assigning a target if there is none or it got destroyed
        if (target == null
                || target.getOwner()==missile.getOwner()
                || (target instanceof ShipAPI && ((ShipAPI) target).isHulk()) //comment out this line to remove target reengagement
                ){
            setTarget(assignTarget(missile));
            //forced acceleration by default
            missile.giveCommand(ShipCommand.ACCELERATE);
            return;
        }
      
        actualisation.advance(amount);
        //fiding lead point to aim to
        if(launch || actualisation.intervalElapsed()){
            launch=false;
            if (MathUtils.getDistance(missile, target) <= closeRange) {
                lead = target.getLocation();
            } else {
                lead = AIUtils.getBestInterceptPoint(missile.getLocation(), flightSpeed*eccm, target.getLocation(), target.getVelocity());
                lead = MathUtils.getRandomPointInCircle(lead,Math.max(0,MathUtils.getDistance(missile, target)-closeRange)); //comment out this line to remove the waving during the flight    
            }
        }
        
        if(lead == null) {
            return; //just in case to make sure a correct lead has been calculated
        }
          
        //aimAngle = angle between the missile facing and the lead direction
        float aimAngle = MathUtils.getShortestRotation(missile.getFacing(), VectorUtils.getAngle(missile.getLocation(), lead));

        //if the missile overshoot the target, turn around
        if (Math.abs(aimAngle) > overshoot) {
             if (aimAngle < 0) {
                 missile.giveCommand(ShipCommand.TURN_RIGHT);
             } else {
                 missile.giveCommand(ShipCommand.TURN_LEFT);
             }              
            
        } else {
            //if the lead is forward, turn the missile toward the lead accelerating
            missile.giveCommand(ShipCommand.ACCELERATE);            
                if (aimAngle < 0) {
                    missile.giveCommand(ShipCommand.TURN_RIGHT);
                } else {
                    missile.giveCommand(ShipCommand.TURN_LEFT);
                }  
        }        
        // Damp angular velocity if the missile aim is getting close to the targeted angle
        if (Math.abs(aimAngle) < Math.abs(missile.getAngularVelocity()) * damping)
        {
            missile.setAngularVelocity(aimAngle / damping);
        }
    }
    
    //////////////////////
    //    TARGETTING    //
    //////////////////////
    
    public CombatEntityAPI assignTarget(MissileAPI missile)
    {
        ShipAPI currentTarget = missile.getSource().getShipTarget();
        
        if (currentTarget != null &&
                !currentTarget.isFighter() &&
                !currentTarget.isDrone() &&
                currentTarget.isAlive() &&
                currentTarget.getOwner()!=missile.getOwner()){
            //return the ship's target if it's valid
            return (CombatEntityAPI)currentTarget;            
        } else {
            //else return the closest enemy if in range
            ShipAPI theEnemy = AIUtils.getNearestEnemy(missile);
            if (theEnemy!=null && MathUtils.getDistance(missile,theEnemy)<=maxSearchRange){
                return AIUtils.getNearestEnemy(missile);
            } else {return null;}
        }
    }

    @Override
    public CombatEntityAPI getTarget() {
        return target;
    }

    @Override
    public void setTarget(CombatEntityAPI target) {
        this.target = target;
    }
    
    public void init(CombatEngineAPI engine) {}
}

Code: Java
//By Tartiflette, simple and fast rocket AI that will try to attack a target in a frontal cone, and not reengage any if it misses.
package data.scripts.ai;

import com.fs.starfarer.api.Global;
import com.fs.starfarer.api.combat.CombatEngineAPI;
import com.fs.starfarer.api.combat.CombatEntityAPI;
import com.fs.starfarer.api.combat.GuidedMissileAI;
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 org.lazywizard.lazylib.MathUtils;
import org.lazywizard.lazylib.VectorUtils;
import org.lazywizard.lazylib.combat.AIUtils;
import org.lwjgl.util.vector.Vector2f;

public class SCY_simpleRocketAI implements MissileAIPlugin, GuidedMissileAI {
    
    private CombatEngineAPI engine;
    private final MissileAPI missile;
    private CombatEntityAPI target;
    private Vector2f lead = new Vector2f(0f,0f);
    private boolean overshoot = false;
    private boolean runOnce = false;
    //data
    private final float flightSpeed;
    private final float maxSearchRange = 2000;
    //if the ship has no target, search one in this forward cone
    private final float searchCone = 35;
    //if the ship's target is out of this attack cone, it can't be reached
    private final float cancellingCone = 45;
    private final float damping = 0.1f;
    
    private float offsetX=0, offsetY=0;
    private boolean eccm=false;

    //////////////////////
    //  DATA COLLECTING //
    //////////////////////
    
    public SCY_simpleRocketAI(MissileAPI missile, ShipAPI launchingShip) {
        this.missile = missile;
        flightSpeed = missile.getMaxSpeed();
        for (String s : missile.getSource().getVariant().getHullMods()) {
            if (s.equals("ECCMPackage")){
                eccm=true;
                break;
            }
        }
    }
    
    //////////////////////
    //   MAIN AI LOOP   //
    //////////////////////
    
    @Override
    public void advance(float amount) {
        
        if (engine != Global.getCombatEngine()) {
            this.engine = Global.getCombatEngine();
        }
        
        // assign a target only once
        if (!runOnce){
            setTarget(assignTarget(missile));
            if (target!=null && !eccm){
                offsetX=MathUtils.getRandomNumberInRange(0, target.getCollisionRadius()/2);
                offsetY=MathUtils.getRandomNumberInRange(0, target.getCollisionRadius()/2);
            }
            runOnce=true;            
        }
        
        //always accelerate
        missile.giveCommand(ShipCommand.ACCELERATE);
        
        //skip the AI if the game is paused, the missile is way off course, engineless or without a target
        if (Global.getCombatEngine().isPaused()
                || overshoot
                || missile.isFading()
                || missile.isFizzling()
                || target == null) {
            return;
        }        
        
        //fiding lead point to aim to
        lead = AIUtils.getBestInterceptPoint(missile.getLocation(), flightSpeed, target.getLocation(), target.getVelocity());
        lead = new Vector2f(lead.x+offsetX,lead.y+offsetY);
        
        if(lead == null) {
            return; //just in case to makes sure a correct lead has been calculated
        }
          
        //aimAngle = angle between the missile facing and the lead direction
        float aimAngle = MathUtils.getShortestRotation(missile.getFacing(), VectorUtils.getAngle(missile.getLocation(), lead));
      
        //if the missile overshoot the target, just shut the AI  
        if (Math.abs(aimAngle) > 90) {
            overshoot = true;
            return;                
            
        } else {
            //if the lead is forward, turn the missile toward the lead accelerating
                if (aimAngle < 0) {
                    missile.giveCommand(ShipCommand.TURN_RIGHT);
                } else {
                    missile.giveCommand(ShipCommand.TURN_LEFT);
                }  
        }        
        // Damp angular velocity if the missile aim is getting close to the targeted angle
        if (Math.abs(aimAngle) < Math.abs(missile.getAngularVelocity()) * damping)
        {
            missile.setAngularVelocity(aimAngle / damping);
        }
    }
    
    //////////////////////
    //    TARGETTING    //
    //////////////////////
    
    public CombatEntityAPI assignTarget(MissileAPI missile)
    {
        ShipAPI source = missile.getSource();
        ShipAPI currentTarget = source.getShipTarget();
        
        if (currentTarget != null &&
                !currentTarget.isFighter() &&
                !currentTarget.isDrone() &&
                currentTarget.isAlive() &&
                currentTarget.getOwner()!=missile.getOwner() &&
                //current target is in the attack cone
                Math.abs(MathUtils.getShortestRotation(missile.getFacing(), VectorUtils.getAngle(missile.getLocation(), currentTarget.getLocation()))) < cancellingCone){
            //return the ship's target if it's valid
            return (CombatEntityAPI)currentTarget;            
        } else {
            //search for the closest enemy in the cone of attack
            ShipAPI closest = null;
            float distance, closestDistance = Float.MAX_VALUE;
            //grab all nearby enemies
            for (ShipAPI tmp : AIUtils.getNearbyEnemies(missile, maxSearchRange))
            {
                //rule out ships out of the missile attack cone
                if (Math.abs(MathUtils.getShortestRotation(missile.getFacing(), VectorUtils.getAngle(missile.getLocation(), tmp.getLocation()))) > searchCone)
                {
                    continue;
                }
                //sort closest enemy
                distance = MathUtils.getDistance(tmp, missile.getLocation());  
                if (distance < closestDistance)
                {
                    closest = tmp;
                    closestDistance = distance;
                }
            }
            //return the closest enemy
            return closest;
        }
    }

    @Override
    public CombatEntityAPI getTarget() {
        return target;
    }

    @Override
    public void setTarget(CombatEntityAPI target) {
        this.target = target;
    }
    
    public void init(CombatEngineAPI engine) {}
}
Title: Re: The Radioactive Code Dump
Post by: Tartiflette on June 11, 2015, 07:39:50 AM


Scratch that script! I show a much better version in my next post!


Spoiler
Another one that might interest other people:
With Deathfly we managed to create some pretty convincing fake beams that can be spawned from anywhere.
Spoiler
(http://i.imgur.com/8BAYwp9.gif)

(http://i.imgur.com/zjsNHTs.png)
[close]

They impact instantly, deal soft flux, do not mess so much with PD weapons as the invisible missile trick. The core of the idea is still a dummy missile, but this time it's only for manipulating it's beam-like sprite and it's not moving toward the target. The script is the dummy missile's AI, that way it's instantiated and get removed with the beam fading. I added the few files I used in this example for reference.

Code: Java
//Fake beam AI script: stretches a "beam" shaped missile to the nearest intersection with any ship or asteroid and apply damage on impact.
//By Tartiflette and Deathfly
package data.scripts.ai;

import com.fs.starfarer.api.Global;
import com.fs.starfarer.api.combat.CollisionClass;
import com.fs.starfarer.api.combat.CombatAsteroidAPI;
import com.fs.starfarer.api.combat.CombatEngineAPI;
import com.fs.starfarer.api.combat.CombatEntityAPI;
import com.fs.starfarer.api.combat.DamageType;
import com.fs.starfarer.api.combat.GuidedMissileAI;
import com.fs.starfarer.api.combat.MissileAIPlugin;
import com.fs.starfarer.api.combat.MissileAPI;
import com.fs.starfarer.api.combat.ShieldAPI;
import com.fs.starfarer.api.combat.ShipAPI;
import com.fs.starfarer.api.combat.WeaponAPI;
import static com.fs.starfarer.api.combat.WeaponAPI.WeaponSize.LARGE;
import static com.fs.starfarer.api.combat.WeaponAPI.WeaponSize.MEDIUM;
import java.awt.Color;
import java.awt.geom.Line2D;
import java.util.List;
import org.lazywizard.lazylib.CollisionUtils;
import org.lazywizard.lazylib.MathUtils;
import org.lazywizard.lazylib.combat.CombatUtils;
import org.lwjgl.util.vector.Vector2f;

public class SCY_fakeBeamAI implements MissileAIPlugin, GuidedMissileAI {
    
    private CombatEngineAPI engine;
    private final MissileAPI missile;
    private CombatEntityAPI target=null;
    private Vector2f start = new Vector2f();
    private boolean runOnce = false;
    private float aim=0, timer=0;
    
    //DATA
    //beam MAX range
    private float range = 500;
    //beam DURATION in seconds
    private final float duration = 0.5f;
    //beam WIDTH in su
    private final float width = 15;
    //beam burst DAMAGE
    private final float defaultDamage = 200;
    //beam damage TYPE
    private final DamageType type = DamageType.HIGH_EXPLOSIVE;
    //beam EMP
    private final float emp = 50;
    //impact SIZE
    private final float size = 50;
    //impact COLOR
    private final Color color = new Color (150,200,255,255);

    //////////////////////
    //  DATA COLLECTING //
    //////////////////////
    
    public SCY_fakeBeamAI(MissileAPI missile, ShipAPI launchingShip) {
        this.missile = missile;
        
        //adjust the range depending on the source weapon size
        WeaponAPI.WeaponSize wSize = missile.getWeapon().getSize();
        if (wSize.equals((WeaponAPI.WeaponSize)LARGE)){
            range = 700f;
        } else {
            if (wSize.equals((WeaponAPI.WeaponSize)MEDIUM)){
                range = 600f;
            } else {
                range = 500f;
            }
        }
    }
    
    //////////////////////
    //   MAIN AI LOOP   //
    //////////////////////
    
    @Override
    public void advance(float amount) {
        
        if (engine != Global.getCombatEngine()) {
            this.engine = Global.getCombatEngine();
        }        
        //cancelling IF
        if (Global.getCombatEngine().isPaused()) {return;}
                
        timer+=amount;
        
        //random delay
        if (Math.random()+timer*5 <= 0.85f){return;} //comment out this line to remove the random delay
            
        //only run once, with a random delay first
        if (!runOnce){          
                        
            start = missile.getLocation();
            aim = missile.getFacing();            
            CombatEntityAPI theTarget= null;
            float damage = defaultDamage;
    
            //default end point
            Vector2f end = MathUtils.getPointOnCircumference(start,range,aim);
            
            //list all nearby entities that could be hit
            List <CombatEntityAPI> entity = CombatUtils.getEntitiesWithinRange(start, range+250);
            if (!entity.isEmpty()){
                for (CombatEntityAPI e : entity){
                                        
                    //ignore phased ships
                    if (e.getCollisionClass() == CollisionClass.NONE){continue;}

                    //damage can be reduced against some modded ships
                    float newDamage = defaultDamage;

                    Vector2f col = new Vector2f();                  
                    //ignore everything but ships...
                    if (
                            e instanceof ShipAPI
                            &&
                            CollisionUtils.getCollides(start, end, e.getLocation(), e.getCollisionRadius())
                            ){
                        //check for a shield impact, then hull and take the closest one                  
                        ShipAPI s = (ShipAPI) e;

                        //can the beam intersect a shield
                        Vector2f cShield = getShieldCollisionPoint(start, end, s);
                        if ( cShield != null ){
                            col = cShield;
                        }  

                        //now check for a direct hit
                        Vector2f cHull = CollisionUtils.getCollisionPoint(start, end, s);
                        if (
                                cHull != null
                                &&
                                MathUtils.getDistance(start, col) > MathUtils.getDistance(start, cHull)
                                ){
                            col = cHull;

                            //check for modded ships with damage reduction
                            if (s.getHullSpec().getBaseHullId().startsWith("exigency_")){
                                newDamage = defaultDamage/2;
                            }
                        }                    
                    } else
                        //...and asteroids!
                           if (
                                   e instanceof CombatAsteroidAPI
                                   &&
                                   CollisionUtils.getCollides(start, end, e.getLocation(), e.getCollisionRadius())
                                   ){                              
                        Vector2f cAst = getCollisionPointOnCircumference(start,end,e.getLocation(),e.getCollisionRadius());
                        if ( cAst != null){
                            col = cAst;
                        }
                    }

                    //if there was an impact and it is closer than the curent beam end point, set it as the new end point and store the target to apply damage later damage
                    if (col != new Vector2f() && MathUtils.getDistance(start, col) < MathUtils.getDistance(start, end)) {
                        end = col;
                        theTarget = e;
                        damage = newDamage;
                    }
                }
            }
            
            //if the beam impacted something, apply the damage
            if (theTarget!=null){
                
                //damage
                engine.applyDamage(
                        theTarget,
                        end,
                        damage,
                        type,
                        emp,
                        false,
                        true,
                        missile.getSource()
                );
                //impact flash
                engine.addHitParticle(
                        end,
                        theTarget.getVelocity(),
                        (float)Math.random()*size/2+size,
                        1,
                        (float)Math.random()*duration/2+duration,
                        color
                );
                engine.addHitParticle(
                        end,
                        theTarget.getVelocity(),
                        (float)Math.random()*size/4+size/2,
                        1,
                        0.1f,
                        Color.WHITE
                );
            }          
            //stretch the "missile" sprite to the impact point, plus a little margin
            float length = MathUtils.getDistance(start, end);
            missile.getSpriteAPI().setHeight(length+10);            
            missile.getSpriteAPI().setAdditiveBlend();
            //never run this again
            runOnce = true;
        }
        
        //check for removal
        if (timer>=duration){
            engine.removeEntity(missile);
            return;
        }
        
        //check if the position and orientation has been changed by other scripts and weapons
        if (missile.getLocation() != start){
            setLocation(missile, start);
        }        
        if (missile.getFacing() != aim){
            missile.setFacing(aim);
        }
        
        //fading
        float fadeColor = Math.max(0,(Math.min(1,(float) Math.cos(timer * Math.PI/(2*duration)))));
        //shrinking
        float fadeWidth = width*fadeColor;
        
        missile.getSpriteAPI().setWidth(fadeWidth);
        missile.getSpriteAPI().setCenterY(fadeWidth/2);
        missile.getSpriteAPI().setColor(new Color(fadeColor,fadeColor,fadeColor,fadeColor));        
    }

    @Override
    public CombatEntityAPI getTarget() {
        return target;
    }

    @Override
    public void setTarget(CombatEntityAPI target) {
        this.target = target;
    }
    
    /////////////////////////////////////////
    //                                     //
    //             SHIELD HIT              //
    //                                     //
    /////////////////////////////////////////
    
    public static Vector2f getShieldCollisionPoint(Vector2f lineStart, Vector2f lineEnd, ShipAPI ship){
        // if target not shielded, return null
        ShieldAPI shield = ship.getShield();
        if (shield == null || shield.isOff()) return null;
        Vector2f circleCenter = shield.getLocation();
        float circleRadius = shield.getRadius();
        // calculate the collision point
        Vector2f tmp = new Vector2f(lineEnd);
        boolean shieldHit = false;
        Vector2f tmp1 = getCollisionPointOnCircumference(lineStart,lineEnd, circleCenter, circleRadius);
        if (tmp1 != null){
            if(shield.isWithinArc(tmp1)) {
            tmp = tmp1;
            shieldHit = true;
            }      
            // just in case...
            // if the hit come outside the shield's arc but did not hit the hull and hit the shield's "edge".
            if (!shield.isWithinArc(tmp1)){
                // find the hit point on shield's "edge"
                Vector2f shieldEdge1 = MathUtils.getPointOnCircumference(circleCenter, circleRadius, MathUtils.clampAngle(shield.getFacing() + shield.getActiveArc()/2));
                Vector2f tmp2 = CollisionUtils.getCollisionPoint(lineStart, tmp, circleCenter, shieldEdge1);
                tmp = tmp2 == null ? tmp: tmp2;
                Vector2f shieldEdge2 = MathUtils.getPointOnCircumference(circleCenter, circleRadius, MathUtils.clampAngle(shield.getFacing() - shield.getActiveArc()/2));
                Vector2f tmp3 = CollisionUtils.getCollisionPoint(lineStart, tmp, circleCenter, shieldEdge2);
                tmp = tmp3 == null ? tmp : tmp3;
                // make sure the line did not hit hull.
                if (CollisionUtils.getCollisionPoint(lineStart, tmp, ship) == null){
                    shieldHit = true;
                }
            }
        }
        return shieldHit? tmp : null;        
    }
    
    /////////////////////////////////////////
    //                                     //
    //       CIRCLE COLLISION POINT        //
    //                                     //
    /////////////////////////////////////////
    
    // return the first intersection point of segment lineStart to lineEnd and circumference.
    // if lineStart is outside the circle and segment can not intersection with the circumference, will return null
    // if lineStart is inside the circle, will return lineStart.
    public static Vector2f getCollisionPointOnCircumference(Vector2f lineStart, Vector2f lineEnd, Vector2f circleCenter, float circleRadius){
        
        Vector2f startToEnd = Vector2f.sub(lineEnd, lineStart, null);
        Vector2f startToCenter = Vector2f.sub(circleCenter, lineStart, null);
        double ptSegDistSq = (float) Line2D.ptSegDistSq(lineStart.x, lineStart.y, lineEnd.x, lineEnd.y, circleCenter.x, circleCenter.y);
        float circleRadiusSq = circleRadius * circleRadius;
        
        // if lineStart is outside the circle and segment can not reach the circumference, return null
        if (ptSegDistSq > circleRadiusSq || (startToCenter.lengthSquared() >= circleRadiusSq && startToCenter.lengthSquared()>startToEnd.lengthSquared())) return null;
        // if lineStart is within the circle, return it directly
        if (startToCenter.lengthSquared() < circleRadiusSq) return lineStart;
        
        // calculate the intersection point.
        startToEnd.normalise(startToEnd);
        double dist = Vector2f.dot(startToCenter, startToEnd) -  Math.sqrt(circleRadiusSq - ptSegDistSq);
        startToEnd.scale((float) dist);
        return Vector2f.add(lineStart, startToEnd, null);
    }
    
    /////////////////////////////////////////
    //                                     //
    //        SET TELEPORT LOCATION        //
    //                                     //
    /////////////////////////////////////////
    
    public void setLocation(CombatEntityAPI entity, Vector2f location) {  
        Vector2f dif = new Vector2f(location);  
        Vector2f.sub(location, entity.getLocation(), dif);  
        Vector2f.add(entity.getLocation(), dif, entity.getLocation());  
    }
}

[edit1] slightly modified the code to make the beams impervious to most scripts except the ones that replace a missile AI (like flares).
[edit2] New shield hit calculation from Deathfly that detect "sideway" hits to shields.
[edit3] Further refinement to avoid missfires: beams now ignore projectiles, most notably flares.
[edit4] More misfire avoidance: the lasers now ignore anything that isn't either a ship or an asteroid. Also corrected an issue while hitting shields from the "back".
[edit5] Should be good now, uploaded the latest files, swapped the missile to FLARE type so that it get ignored by most AI, checked for Exigency ships beam damage reduction and cleaned up the target type detection.
[edit6] Those bugs are getting on my nerves...

[close]

[attachment deleted by admin]
Title: Re: The Radioactive Code Dump
Post by: 19_30s on June 11, 2015, 09:09:38 AM
Good Contribution ;D
Title: Re: The Radioactive Code Dump
Post by: Sproginator on June 11, 2015, 10:33:51 AM
Cool mod! I could really do with some help getting Lazy's old Armor Piercing weapon script to work
Title: Re: The Radioactive Code Dump
Post by: Tartiflette on June 16, 2015, 02:01:39 PM
As I was saying, with Deathfly we managed to create some pretty convincing fake beams that can be spawned from anywhere.
Spoiler
(http://i.imgur.com/8BAYwp9.gif)

(http://i.imgur.com/zjsNHTs.png)
[close]

Fake-beam V2.0, look exactly the same but is night 100% mods and AI friendly.
This time it's packed as an utility script you can call from any other script that suits you. It will automatically calculate the impact point, draw the beam and apply damage. And since it's no longer using any dummy missile it's completely transparent for the AI (but it won't see it coming either, beware with the range then) or other fancy scripted factions. The attached zip contains everything you need.

But if you want to take a look first:
Spoiler
The utility script that find the collision point, apply the damage, call the plugin, and create the impact glow:
Code: java
// By Tartiflette and DeathFly

package data.scripts.util;

import com.fs.starfarer.api.combat.CollisionClass;
import com.fs.starfarer.api.combat.CombatAsteroidAPI;
import java.awt.Color;
import org.lwjgl.util.vector.Vector2f;
import com.fs.starfarer.api.combat.CombatEngineAPI;
import com.fs.starfarer.api.combat.CombatEntityAPI;
import com.fs.starfarer.api.combat.DamageType;
import com.fs.starfarer.api.combat.ShieldAPI;
import com.fs.starfarer.api.combat.ShipAPI;
import data.scripts.plugins.FakeBeamPlugin;
import java.awt.geom.Line2D;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.lazywizard.lazylib.CollisionUtils;
import org.lazywizard.lazylib.MathUtils;
import org.lazywizard.lazylib.VectorUtils;
import org.lazywizard.lazylib.combat.CombatUtils;

public class FakeBeam {

    //
    //Fake beam generator.
    //
    //Create a visually convincing beam from arbitrary coordinates.
    //It however has several limitation:
    // - It deal damage instantly and is therefore only meant to be used for burst beams.
    // - It cannot be "cut" by another object passing between the two ends, a very short duration is thus preferable.
    // - Unlike vanilla, it deals full damage to armor, be carefull when using HIGH_EXPLOSIVE damage type.
    //
    // Most of the parameters are self explanatory but just in case:
    //
    //engine : Combat Engine
    //start : source point of the beam
    //range : maximum effective range (the beam will visually fade a few pixels farther)
    //aim : direction of the beam
    //width : width of the beam
    //fading : duration of the beam
    //normalDamage : nominal burst damage of the beam (don't forget to calculate the skill modifiers before that)
    //               will potentially be modified when fighting some modded factions like Exigency.
    //type : damage type of the beam
    //emp : nominal emp damage if any
    //source : ship dealing the damage
    //size : glow size on the impact point
    //duration : duration of the impact glow (should be at least as long as the beam fading)
    //color : color of the impact glow
    //
    //Note that there is no control over the beam's color, you'll have to directly modify the fakeBeamFX.png for that
    //   
   
   
    /////////////////////////////////////////
    //                                     //
    //             FAKE BEAM               //
    //                                     //
    /////////////////////////////////////////   
   
   
    public static void applyFakeBeamEffect (CombatEngineAPI engine, Vector2f start, float range, float aim, float width, float fading, float normalDamage, DamageType type, float emp, ShipAPI source, float size, float duration, Color color)
    {           
        CombatEntityAPI theTarget= null;
        float damage = normalDamage;

        //default end point
        Vector2f end = MathUtils.getPointOnCircumference(start,range,aim);

        //list all nearby entities that could be hit
        List <CombatEntityAPI> entity = CombatUtils.getEntitiesWithinRange(start, range+500);
        if (!entity.isEmpty()){
            for (CombatEntityAPI e : entity){

                //ignore un-hittable stuff like phased ships
                if (e.getCollisionClass() == CollisionClass.NONE){continue;}

                //damage can be reduced against some modded ships
                float newDamage = normalDamage;

                Vector2f col = new Vector2f(1000000,1000000);                 
                //ignore everything but ships...
                if (
                        e instanceof ShipAPI
                        &&
                        CollisionUtils.getCollides(start, end, e.getLocation(), e.getCollisionRadius())
                        ){
                    //check for a shield impact, then hull and take the closest one                 
                    ShipAPI s = (ShipAPI) e;

                    //find the collision point with shields/hull
                    Vector2f hitPoint = getShipCollisionPoint(start, end, s);
                    if ( hitPoint != null ){
                        col = hitPoint;
                    }

                    //check for modded ships with damage reduction
                    if (s.getHullSpec().getBaseHullId().startsWith("exigency_")){
                        newDamage = normalDamage/2;
                    }
                                       
                } else
                    //...and asteroids!
                       if (
                               e instanceof CombatAsteroidAPI
                               &&
                               CollisionUtils.getCollides(start, end, e.getLocation(), e.getCollisionRadius())
                               ){                               
                    Vector2f cAst = getCollisionPointOnCircumference(start,end,e.getLocation(),e.getCollisionRadius());
                    if ( cAst != null){
                        col = cAst;
                    }
                }

                //if there was an impact and it is closer than the curent beam end point, set it as the new end point and store the target to apply damage later damage
                if (col != new Vector2f(1000000,1000000) && MathUtils.getDistance(start, col) < MathUtils.getDistance(start, end)) {
                    end = col;
                    theTarget = e;
                    damage = newDamage;
                }               
            }
                           
            //if the beam impacted something, apply the damage
            if (theTarget!=null){
               
                //damage
                engine.applyDamage(
                        theTarget,
                        end,
                        damage,
                        type,
                        emp,
                        false,
                        true,
                        source
                );
                //impact flash
                engine.addHitParticle(
                        end,
                        theTarget.getVelocity(),
                        (float)Math.random()*size/2+size,
                        1,
                        (float)Math.random()*duration/2+duration,
                        color
                );
                engine.addHitParticle(
                        end,
                        theTarget.getVelocity(),
                        (float)Math.random()*size/4+size/2,
                        1,
                        0.1f,
                        Color.WHITE
                );
            }           
               
            //create the visual effect
            Map <String,Float> VALUES = new HashMap<>();
            VALUES.put("t", fading); //duration
            VALUES.put("w", width); //width
            VALUES.put("h", MathUtils.getDistance(start, end)+10); //length
            VALUES.put("x", start.x); //origin X
            VALUES.put("y", start.y); //origin Y
            VALUES.put("a", aim); //angle
           
            //Add the beam to the plugin
            FakeBeamPlugin.addMember(VALUES);
        }
    }
   
   
    /////////////////////////////////////////
    //                                     //
    //             SHIP HIT                //
    //                                     //
    /////////////////////////////////////////
   
   
    // return the collision point of segment lineStart to lineEnd and a ship (will consider shield).
    // if line can not hit the ship, will return null.
    // if lineStart hit the ship, will return lineStart.
    // if lineStart hit the shield, will return lineStart.
   
    public static Vector2f getShipCollisionPoint(Vector2f lineStart, Vector2f lineEnd, ShipAPI ship){
       
        // if target can not be hit, return null
        if (ship.getCollisionClass() == CollisionClass.NONE) return null;
        ShieldAPI shield = ship.getShield();
       
        // Check hit point when shield is off.
        if(shield == null || shield.isOff()){
            return CollisionUtils.getCollisionPoint(lineStart, lineEnd, ship);
        }
        // If ship's shield is on, thing goes complicated...
        else{
            Vector2f circleCenter = shield.getLocation();
            float circleRadius = shield.getRadius();
            // calculate the shield collision point
            Vector2f tmp1 = getCollisionPointOnCircumference(lineStart, lineEnd, circleCenter, circleRadius);
            if (tmp1 != null){
                // OK! hit the shield in face
                if(shield.isWithinArc(tmp1)){
                    return tmp1;
                } else {           
                    // if the hit come outside the shield's arc but it hit the shield's "edge", find that point.
                    boolean hit = false;
                    Vector2f tmp = new Vector2f(lineEnd);
                   
                    //the beam cannot go farther than it's max range or the hull
                    Vector2f hullHit = CollisionUtils.getCollisionPoint(lineStart, lineEnd, ship);                   
                    if (hullHit != null){
                        tmp = hullHit;
                        hit = true;
                    }
                   
                    //find if the shield is hit from the left or right side
                    if (MathUtils.getShortestRotation(
                            VectorUtils.getAngle(lineStart, lineEnd),
                            VectorUtils.getAngle(lineStart, circleCenter)
                            )
                            <=0){
                        //left side hit
                        Vector2f shieldEdge1 = MathUtils.getPointOnCircumference(circleCenter, circleRadius, MathUtils.clampAngle(shield.getFacing() + shield.getActiveArc()/2));
                        Vector2f tmp2 = CollisionUtils.getCollisionPoint(lineStart, tmp, circleCenter, shieldEdge1);
                        if(tmp2 != null){
                            tmp = tmp2;
                            hit = true;
                        }
                    } else {           
                        //right side hit
                        Vector2f shieldEdge2 = MathUtils.getPointOnCircumference(circleCenter, circleRadius, MathUtils.clampAngle(shield.getFacing() - shield.getActiveArc()/2));
                        Vector2f tmp3 = CollisionUtils.getCollisionPoint(lineStart, tmp, circleCenter, shieldEdge2);
                        if(tmp3 != null){
                            tmp = tmp3;
                            hit = true;
                        }
                    }
                    // return null if do not hit anything.
                    return hit ? tmp : null;
                }
            }
        }
        return null;
    }
   
    /////////////////////////////////////////
    //                                     //
    //       CIRCLE COLLISION POINT        //
    //                                     //
    /////////////////////////////////////////
   
    // return the first intersection point of segment lineStart to lineEnd and circumference.
    // if lineStart is outside the circle and segment can not intersection with the circumference, will return null.
    // if lineStart is inside the circle, will return lineStart.
   
    public static Vector2f getCollisionPointOnCircumference(Vector2f lineStart, Vector2f lineEnd, Vector2f circleCenter, float circleRadius){
       
        Vector2f startToEnd = Vector2f.sub(lineEnd, lineStart, null);
        Vector2f startToCenter = Vector2f.sub(circleCenter, lineStart, null);
        double ptSegDistSq = (float) Line2D.ptSegDistSq(lineStart.x, lineStart.y, lineEnd.x, lineEnd.y, circleCenter.x, circleCenter.y);
        float circleRadiusSq = circleRadius * circleRadius;
       
        // if lineStart is outside the circle and segment can not reach the circumference, return null
        if (ptSegDistSq > circleRadiusSq || (startToCenter.lengthSquared() >= circleRadiusSq && startToCenter.lengthSquared()>startToEnd.lengthSquared())) return null;
        // if lineStart is within the circle, return it directly
        if (startToCenter.lengthSquared() < circleRadiusSq) return lineStart;
       
        // calculate the intersection point.
        startToEnd.normalise(startToEnd);
        double dist = Vector2f.dot(startToCenter, startToEnd) -  Math.sqrt(circleRadiusSq - ptSegDistSq);
        startToEnd.scale((float) dist);
        return Vector2f.add(lineStart, startToEnd, null);
    }
   
    /////////////////////////////////////////
    //                                     //
    //             SHIELD HIT              //
    //                                     //
    /////////////////////////////////////////
   
    // SHOULD ONLY BE USED WHEN YOU ONLY NEED SHIELD COLLISION POINT!
    // if you need the check for a ship hit (considering it's shield), use getShipCollisionPoint instead.
    // return the collision point of segment lineStart to lineEnd and ship's shield.
    // if the line can not hit the shield or if the ship has no shield, return null.
    // if ignoreHull = flase and the line hit the ship's hull first, return null.
    // if lineStart is inside the shield, will return lineStart.
   
    public static Vector2f getShieldCollisionPoint(Vector2f lineStart, Vector2f lineEnd, ShipAPI ship, boolean ignoreHull){
        // if target not shielded, return null
        ShieldAPI shield = ship.getShield();
        if (ship.getCollisionClass()==CollisionClass.NONE || shield == null || shield.isOff()) return null;
        Vector2f circleCenter = shield.getLocation();
        float circleRadius = shield.getRadius();
        // calculate the shield collision point
        Vector2f tmp1 = getCollisionPointOnCircumference(lineStart,lineEnd, circleCenter, circleRadius);
        if (tmp1 != null){
            // OK! hit the shield in face
            if(shield.isWithinArc(tmp1)) return tmp1;               
            else {
                // if the hit come outside the shield's arc but it hit the shield's "edge", find that point.               
               
                Vector2f tmp = new Vector2f(lineEnd);
                boolean hit = false;
               
                //find if the shield is hit from the left or right side
                if (MathUtils.getShortestRotation(
                        VectorUtils.getAngle(lineStart, lineEnd),
                        VectorUtils.getAngle(lineStart, circleCenter)
                        )
                        >=0){
                    Vector2f shieldEdge1 = MathUtils.getPointOnCircumference(circleCenter, circleRadius, MathUtils.clampAngle(shield.getFacing() + shield.getActiveArc()/2));
                    Vector2f tmp2 = CollisionUtils.getCollisionPoint(lineStart, tmp, circleCenter, shieldEdge1);
                    if(tmp2 != null){
                        tmp = tmp2;
                        hit = true;
                    }
                } else {               
                    Vector2f shieldEdge2 = MathUtils.getPointOnCircumference(circleCenter, circleRadius, MathUtils.clampAngle(shield.getFacing() - shield.getActiveArc()/2));
                    Vector2f tmp3 = CollisionUtils.getCollisionPoint(lineStart, tmp, circleCenter, shieldEdge2);
                    if(tmp3 != null){
                        tmp = tmp3;
                        hit = true;
                    }
                }
                // If we don't ignore hull hit, check if there is one...
                if (!ignoreHull || CollisionUtils.getCollisionPoint(lineStart, tmp, ship) != null) return null;
                // return null if do not hit shield.
                return hit ? tmp : null;
            }
        }
        return null;
    }   
}
The everyframe plugin that draw the sprite and make it fade:
Code: java
//By Tartiflette with DeathFly's help
//draw arbitrary beam sprites wherever you need them and fade them out
package data.scripts.plugins;

import com.fs.starfarer.api.Global;
import com.fs.starfarer.api.combat.BaseEveryFrameCombatPlugin;
import com.fs.starfarer.api.combat.CombatEngineAPI;
import com.fs.starfarer.api.combat.ViewportAPI;
import com.fs.starfarer.api.graphics.SpriteAPI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class FakeBeamPlugin extends BaseEveryFrameCombatPlugin {
  
    //FAKEBEAMS is the core of the script, storing both the weapons that have a flash rendered and the index of the current sprite used    
    public static List<Map<String,Float>> FAKEBEAMS = new ArrayList();
    
    //set the function to access FAKEBEAMS from the weapons scripts    
    public static void addMember(Map<String,Float> data) {
        FAKEBEAMS.add(data);
    }
            
    private List<Map<String,Float>> toRemove = new ArrayList<>();
    
    private SpriteAPI beam = Global.getSettings().getSprite("beams", "SCY_fakeBeamFX");
    
    @Override
    public void init(CombatEngineAPI engine) {
        //reinitialize the map
        FAKEBEAMS.clear();
    }
    
    @Override
    public void renderInWorldCoords(ViewportAPI view)
    {
        CombatEngineAPI engine = Global.getCombatEngine();
        if (engine == null){return;}
        
        if (!FAKEBEAMS.isEmpty()){
            float amount = (engine.isPaused() ? 0f : engine.getElapsedInLastFrame());
            
            //dig through the FAKEBEAMS
            for (Map< String,Float > entry : FAKEBEAMS) {
                  
                //Time calculation
                float time = entry.get("t");
                time -= amount;
                
                if (time <= 0){        
                    //faded out, remove the beam and skip                    
                    toRemove.add(entry);
                } else {
                    //draw the beam otherwise                                        
                    float opacity = Math.max(0,(Math.min(1,(float) Math.sin(time * Math.PI))));
                    
                    render(
                            beam, //Sprite to draw
                            entry.get("w") * opacity, //Width entry srinking with the opacity
                            2*entry.get("h"), //Height entry, multiplied by two because centered
                            entry.get("a"), //Angle entry
                            opacity, //opacity duh!
                            entry.get("x"), //X position entry
                            entry.get("y") //Y position entry
                    );
                    
                    //and store the new time value
                    entry.put("t", time);
                }
            }
            //remove the beams that faded out
            //can't be done from within the iterator or it will fail when members will be missing
            if (!toRemove.isEmpty()){
                for(Map< String,Float > w : toRemove ){
                    FAKEBEAMS.remove(w);
                }
                toRemove.clear();
            }            
        }
    }
    
    private void render ( SpriteAPI sprite, float width, float height, float angle, float opacity, float posX, float posY){
        //where the magic happen
        sprite.setAlphaMult(opacity);
        sprite.setSize(width, height);
        sprite.setAdditiveBlend();
        sprite.setAngle(angle-90);
        sprite.renderAtCenter(posX, posY);    
    }
}
and the two lines to add to the settings to make the whole thing work:
Code
{
"plugins":{
"FakeBeamPlugin":"data.scripts.plugins.FakeBeamPlugin",
},
                
"graphics":{
"beams":{
"SCY_fakeBeamFX":"graphics/FAKEBEAM/fakeBeamFX.png",
},
}
}
[close]

To use it you just need to add the plugin and sprites to you settings.json, then a line like that:
Code: java
for (int x=0; x<nbRays; x++) { 
   FakeBeam.applyFakeBeamEffect(  
                            engine,
                            weapon.getLocation(), //start point
                            beamRange, //beam range
                            weapon.getFacing()+MathUtils.getRandomNumberInRange(-10, 10), //spreading
                            width, //beam width
                            duration, //beam duration
                            defaultDamage, //effective damage (will be modified against some specific mods)
                            dmgType, //damage type. Remember that beams are supposed to deal half damage against armor
                            defaultEmp, //effective EMP
                            missile.getSource(), //damage source
                            size, //impact size
                            duration, //impact duration
                            color //impact color
                        );
}
and BAM, you got a laser shotgun. Simple! 8)
[Edit1] (again) faster collision detection using less point calculations.

[attachment deleted by admin]
Title: Re: The Radioactive Code Dump
Post by: Nicke535 on June 17, 2015, 11:57:40 PM
*Awesome quote regarding laser shotguns*

Awesome! Thank you so much, i needed a piece of code just like that! (I will, of course, give full credit)
Title: Re: The Radioactive Code Dump
Post by: Deathfly on July 24, 2015, 12:41:22 AM
well, I think it is time to post the newer FakeBeamTM.

fakeBeam 3.4, for more customizability! (and bug fix.)

As before, this one is packed as a utility again. All needed things are packed in the attached zip and it is ready to use. (expact the settings.json was renamed to avoid accidental override.)

(to use this, you need LazyLib.)

The main change form fakeBeam 2.0 is fix a major bug that will cause some misjudge on shield hit calculate.(That was all my fault, please don't blame Tartiflette >.<) And expand the customizability of the beam render script so you can change the most parameter before or after a fakeBeam was rendered. (like the fakeBeam form and to, fakeBeam sprite and color. fade in, fade out, and duration time etc.)

And this package contains 3 part of scripts.
1st
\data\scripts\plugins\FakeBeamPlugin.java
This one is the fakeBeam render.
Spoiler
If you want to render a FakeBeamTM just call
renderFakeBeam(Vector2f form, Vector2f to, float width, float duration, float fadeInDuration, float fadeOutDuration, SpriteAPI beamSprite, Color beamColor)
and it will render a FakeBeam and return it.

After a FakeBeam was rendered, you can use FakeBeam.setForm(Vector2f form) and FakeBeam.setTo(Vector2f to) to move the beam dynamical, FakeBeam.setSprite(SpriteAPI beamSprite) to mod the sprite, FakeBeam.setColor(Color beamColor) to change beam's color, FakeBeam.setDuration(float duration) to reset the beams live time ect.

Code: java
//By Tartiflette and Deathfly
//draw arbitrary beam sprites wherever you need them and fade them out
package data.scripts.plugins;

import com.fs.starfarer.api.Global;
import com.fs.starfarer.api.combat.BaseEveryFrameCombatPlugin;
import com.fs.starfarer.api.combat.CombatEngineAPI;
import com.fs.starfarer.api.combat.ViewportAPI;
import com.fs.starfarer.api.graphics.SpriteAPI;
import java.awt.Color;
import java.util.ArrayList;
import java.util.List;
import org.lazywizard.lazylib.FastTrig;
import org.lazywizard.lazylib.MathUtils;
import org.lazywizard.lazylib.VectorUtils;
import org.lwjgl.util.vector.Vector2f;

public class FakeBeamPlugin extends BaseEveryFrameCombatPlugin {

    //FAKEBEAMS is the core of the script, storing both the weapons that have a flash rendered and the index of the current sprite used    
    private static final List<FakeBeam> FAKEBEAMS = new ArrayList<>();
    private final List<FakeBeam> toRemove = new ArrayList<>();

    @Override
    public void init(CombatEngineAPI engine) {
        //reinitialize the map
        FAKEBEAMS.clear();
    }

    @Override
    public void renderInWorldCoords(ViewportAPI view) {
        CombatEngineAPI engine = Global.getCombatEngine();
        if (engine == null) {
            return;
        }

        if (!FAKEBEAMS.isEmpty()) {
            float amount = (engine.isPaused() ? 0f : engine.getElapsedInLastFrame());

            //dig through the FAKEBEAMS
            for (FakeBeam fakeBeam : FAKEBEAMS) {

                //Time calculation
                float fadeIn = fakeBeam.getFadeInTimer();
                float liveTime = fakeBeam.getDurationTimer();
                float fadeOut = fakeBeam.getFadeOutTimer();
                float opacity = 0;

                // check if the beam is fading in
                if (fadeIn > 0) {
                    fadeIn -= amount;
                    fakeBeam.setFadeInTimer(fadeIn);
                    opacity = (float) FastTrig.cos((fadeIn / fakeBeam.getFadeInDuration()) * (Math.PI / 2));
                    // check if the beam is on full power
                } else if (liveTime > 0) {
                    liveTime -= amount;
                    fakeBeam.setDurationTimer(liveTime);
                    opacity = 1;
                    // check if the beam is fading out
                } else if (fadeOut > 0) {
                    fadeOut -= amount;
                    fakeBeam.setFadeOutTimer(fadeOut);
                    opacity = (float) FastTrig.sin((fadeOut / fakeBeam.getFadeOutDuration()) * (Math.PI / 2));
                } else {
                    //completely faded out, remove the beam and skip              
                    toRemove.add(fakeBeam);
                    continue;
                }

                //draw the beam
                render(
                        fakeBeam.getSprite(), //Sprite to draw
                        fakeBeam.getColor(), //Sprite color
                        fakeBeam.getWidth() * opacity, //Width entry srinking with the opacity
                        fakeBeam.getLength(), //Height entry, multiplied by two because centered
                        fakeBeam.getAngleToRender(), //Angle entry
                        opacity, //opacity duh!
                        fakeBeam.getCenter()//Center
                );
            }

            //remove the beams that faded out
            //can't be done from within the iterator or it will fail when members will be missing
            if (!toRemove.isEmpty()) {
                for (FakeBeam w : toRemove) {
                    FAKEBEAMS.remove(w);
                }
                toRemove.clear();
            }
        }
    }

    private void render(SpriteAPI sprite, Color color, float width, float height, float angle, float opacity, Vector2f center) {
        //where the magic happen
        if (color != null) {
            sprite.setColor(color);
        }
        sprite.setAlphaMult(opacity);
        sprite.setSize(height, width);
        sprite.setAdditiveBlend();
        sprite.setAngle(angle);
        sprite.renderAtCenter(center.x, center.y);
    }
    
    // a class to record all we need to render a fake beam.
    public static class FakeBeam {

        protected SpriteAPI beamSprite = Global.getSettings().getSprite("graphics/fx/beamcore.png");
        protected Color beamColor = Color.white;
        protected float width = 0f;
        protected float length = 0f;
        protected float angleForRender = 0f;
        protected float duration = 0f;
        protected float durationTimer = 0f;
        protected float fadeInDuration = 0f;
        protected float fadeInTimer = 0f;
        protected float fadeOutDuration = 0f;
        protected float fadeOutTimer = 0f;
        protected Vector2f centerLoc = null;
        protected Vector2f form = null;
        protected Vector2f to = null;

        public void setSprite(SpriteAPI beamSprite) {
            this.beamSprite = beamSprite;
        }

        public SpriteAPI getSprite() {
            return beamSprite;
        }

        public void setColor(Color beamColor) {
            this.beamColor = beamColor;
        }

        public Color getColor() {
            return beamColor;
        }

        public void setWidth(float width) {
            this.width = width;
        }

        public float getWidth() {
            return width;
        }

        public void setLength(float length) {
            this.length = length;
            if (form != null) {
                setTo(MathUtils.getPointOnCircumference(form, length, angleForRender));
            }
        }

        public float getLength() {
            return length;
        }

        public void setCurrAngle(float currAngle) {
            if (form != null) {
                setTo(MathUtils.getPointOnCircumference(form, length, currAngle));
            }
        }

        public float getCurrAngle() {
            return angleForRender;
        }

        public float getAngleToRender() {
            return angleForRender;
        }

        public void setDuration(float duration) {
            this.duration = duration;
            this.durationTimer = duration;
        }

        public float getDuration() {
            return duration;
        }

        public void setDurationTimer(float durationTimer) {
            this.durationTimer = durationTimer;
        }

        public float getDurationTimer() {
            return durationTimer;
        }

        public void setFadeInDuration(float fadeInDuration) {
            this.fadeInDuration = fadeInDuration;
            this.fadeInTimer = fadeInDuration;
        }

        public float getFadeInDuration() {
            return fadeInDuration;
        }

        public void setFadeInTimer(float fadeInTimer) {
            this.fadeInTimer = fadeInTimer;
        }

        public float getFadeInTimer() {
            return fadeInTimer;
        }

        public void setFadeOutDuration(float fadeOutDuration) {
            this.fadeOutDuration = fadeOutDuration;
            this.fadeOutTimer = fadeOutDuration;
        }

        public float getFadeOutDuration() {
            return fadeOutDuration;
        }

        public void setFadeOutTimer(float fadeOutTimer) {
            this.fadeOutTimer = fadeOutTimer;
        }

        public float getFadeOutTimer() {
            return fadeOutTimer;
        }

        public void setForm(Vector2f form) {
            this.form = form;
            if (this.form != null && this.to != null) {
                this.centerLoc = MathUtils.getMidpoint(this.form, this.to);
                this.length = MathUtils.getDistance(form, to);
                this.angleForRender = VectorUtils.getAngle(form, to);
            }
        }

        public Vector2f getForm() {
            return form;
        }

        public void setTo(Vector2f to) {
            this.to = to;
            if (this.to != null && this.form != null) {
                this.centerLoc = MathUtils.getMidpoint(this.form, this.to);
                this.length = MathUtils.getDistance(form, to);
                this.angleForRender = VectorUtils.getAngle(form, to);
            }
        }

        public Vector2f getTo() {
            return to;
        }

        public void setCenter(Vector2f center) {
            this.centerLoc = center;
        }

        public Vector2f getCenter() {
            return centerLoc;
        }
    }

    // 4 methods for render fake beam, will return FakeBeam for some after rendered modify.
    public static FakeBeam renderFakeBeam(Vector2f form, Vector2f to, float width, float duration, float fadeInDuration, float fadeOutDuration, SpriteAPI beamSprite, Color beamColor) {
        FakeBeam fakeBeam = new FakeBeam();
        if (beamSprite != null) {
            fakeBeam.setSprite(beamSprite);
        }
        fakeBeam.setColor(beamColor);
        fakeBeam.setForm(form);
        fakeBeam.setTo(to);
        fakeBeam.setWidth(width);
        fakeBeam.setDuration(duration);
        fakeBeam.setFadeInDuration(fadeInDuration);
        fakeBeam.setFadeOutDuration(fadeOutDuration);
        FAKEBEAMS.add(fakeBeam);
        return fakeBeam;
    }

    public static FakeBeam renderFakeBeam(Vector2f form, Vector2f to, float width, float duration, SpriteAPI beamSprite, Color beamColor) {
        return renderFakeBeam(form, to, width, duration - 0.5f, 0.25f, 0.25f, beamSprite, beamColor);
    }

    public static FakeBeam renderFakeBeam(Vector2f form, float length, float aim, float width, float duration, float fadeInDuration, float fadeOutDuration, SpriteAPI beamSprite, Color beamColor) {
        Vector2f to = MathUtils.getPointOnCircumference(form, length, aim);
        return renderFakeBeam(form, to, width, duration, fadeInDuration, fadeOutDuration, beamSprite, beamColor);
    }

    public static FakeBeam renderFakeBeam(Vector2f form, float length, float aim, float width, float duration, SpriteAPI beamSprite, Color beamColor) {
        Vector2f to = MathUtils.getPointOnCircumference(form, length, aim);
        return renderFakeBeam(form, to, width, duration, beamSprite, beamColor);
    }
}
[close]
2nd
\data\scripts\\utils\CollisionUtilsEX.java
This one expand the CollisionUtils in LazyLib for detection collision between a segment and a ship/shield/circular.
Spoiler
This one is quite self-describing. Just use them if you need.
Code: java
// by Deathfly and Tartiflette
package data.scripts.util;

import com.fs.starfarer.api.combat.CollisionClass;
import com.fs.starfarer.api.combat.ShieldAPI;
import com.fs.starfarer.api.combat.ShipAPI;
import java.awt.geom.Line2D;
import org.lazywizard.lazylib.CollisionUtils;
import org.lazywizard.lazylib.MathUtils;
import org.lwjgl.util.vector.Vector2f;

public class CollisionUtilsEX {

    /////////////////////////////////////////
    //                                     //
    //             SHIP HIT                //
    //                                     //
    /////////////////////////////////////////
    // return the collision point of segment segStart to segEnd and a ship (will consider shield).
    // if segment can not hit the ship, will return null.
    // if segStart hit the ship, will return segStart.
    // if segStart hit the shield, will return segStart.
    public static Vector2f getShipCollisionPoint(Vector2f segStart, Vector2f segEnd, ShipAPI ship) {

        // if target can not be hit, return null
        if (ship.getCollisionClass() == CollisionClass.NONE) {
            return null;
        }
        ShieldAPI shield = ship.getShield();

        // Check hit point when shield is off.
        if (shield == null || shield.isOff()) {
            return CollisionUtils.getCollisionPoint(segStart, segEnd, ship);
        } // If ship's shield is on, thing goes complicated...
        else {
            Vector2f circleCenter = shield.getLocation();
            float circleRadius = shield.getRadius();
            // calculate the shield collision point
            Vector2f tmp1 = getCollisionPointOnCircumference(segStart, segEnd, circleCenter, circleRadius);
            if (tmp1 != null) {
                // OK! hit the shield in face
                if (shield.isWithinArc(tmp1)) {
                    return tmp1;
                } else {
                    // if the hit come outside the shield's arc but it hit the shield's "edge", find that point.
                    boolean hit = false;
                    Vector2f tmp = new Vector2f(segEnd);

                    //the beam cannot go farther than it's max range or the hull
                    Vector2f hullHit = CollisionUtils.getCollisionPoint(segStart, segEnd, ship);
                    if (hullHit != null) {
                        tmp = hullHit;
                        hit = true;
                    }
                    Vector2f shieldEdge1 = MathUtils.getPointOnCircumference(circleCenter, circleRadius, MathUtils.clampAngle(shield.getFacing() + shield.getActiveArc() / 2));
                    Vector2f tmp2 = CollisionUtils.getCollisionPoint(segStart, tmp, circleCenter, shieldEdge1);
                    if (tmp2 != null) {
                        tmp = tmp2;
                        hit = true;
                    }
                    Vector2f shieldEdge2 = MathUtils.getPointOnCircumference(circleCenter, circleRadius, MathUtils.clampAngle(shield.getFacing() - shield.getActiveArc() / 2));
                    Vector2f tmp3 = CollisionUtils.getCollisionPoint(segStart, tmp, circleCenter, shieldEdge2);
                    if (tmp3 != null) {
                        tmp = tmp3;
                        hit = true;
                    }

                    // return null if do not hit anything.
                    return hit ? tmp : null;
                }
            }
        }
        return null;
    }

    /////////////////////////////////////////
    //                                     //
    //       CIRCLE COLLISION POINT        //
    //                                     //
    /////////////////////////////////////////
    // return the first intersection point of segment segStart to segEnd and circumference.
    // if segStart is outside the circle and segment can not intersection with the circumference, will return null.
    // if segStart is inside the circle, will return segStart.
    public static Vector2f getCollisionPointOnCircumference(Vector2f segStart, Vector2f segEnd, Vector2f circleCenter, float circleRadius) {

        Vector2f startToEnd = Vector2f.sub(segEnd, segStart, null);
        Vector2f startToCenter = Vector2f.sub(circleCenter, segStart, null);
        double ptLineDistSq = (float) Line2D.ptLineDistSq(segStart.x, segStart.y, segEnd.x, segEnd.y, circleCenter.x, circleCenter.y);
        float circleRadiusSq = circleRadius * circleRadius;
        
        // if lineStart is within the circle, return it directly
        if (startToCenter.lengthSquared() < circleRadiusSq) {
            return segStart;
        }

        // if lineStart is outside the circle and segment can not reach the circumference, return null
        if (ptLineDistSq > circleRadiusSq || startToCenter.length() - circleRadius > startToEnd.length()) {
            return null;
        }

        // calculate the intersection point.
        startToEnd.normalise(startToEnd);
        double dist = Vector2f.dot(startToCenter, startToEnd) - Math.sqrt(circleRadiusSq - ptLineDistSq);
        startToEnd.scale((float) dist);
        return Vector2f.add(segStart, startToEnd, null);
    }

    /////////////////////////////////////////
    //                                     //
    //             SHIELD HIT              //
    //                                     //
    /////////////////////////////////////////
    // SHOULD ONLY BE USED WHEN YOU ONLY NEED SHIELD COLLISION POINT!
    // if you need the check for a ship hit (considering it's shield), use getShipCollisionPoint instead.
    // return the collision point of segment segStart to segEnd and ship's shield.
    // if the segment can not hit the shield or if the ship has no shield, return null.
    // if ignoreHull = flase and the segment hit the ship's hull first, return null.
    // if segStart is inside the shield, will return segStart.
    public static Vector2f getShieldCollisionPoint(Vector2f segStart, Vector2f segEnd, ShipAPI ship, boolean ignoreHull) {
        // if target not shielded, return null
        ShieldAPI shield = ship.getShield();
        if (ship.getCollisionClass() == CollisionClass.NONE || shield == null || shield.isOff()) {
            return null;
        }
        Vector2f circleCenter = shield.getLocation();
        float circleRadius = shield.getRadius();
        // calculate the shield collision point
        Vector2f tmp1 = getCollisionPointOnCircumference(segStart, segEnd, circleCenter, circleRadius);
        if (tmp1 != null) {
            // OK! hit the shield in face
            if (shield.isWithinArc(tmp1)) {
                return tmp1;
            } else {
                // if the hit come outside the shield's arc but it hit the shield's "edge", find that point.                

                Vector2f tmp = new Vector2f(segEnd);
                boolean hit = false;

                Vector2f shieldEdge1 = MathUtils.getPointOnCircumference(circleCenter, circleRadius, MathUtils.clampAngle(shield.getFacing() + shield.getActiveArc() / 2));
                Vector2f tmp2 = CollisionUtils.getCollisionPoint(segStart, tmp, circleCenter, shieldEdge1);
                if (tmp2 != null) {
                    tmp = tmp2;
                    hit = true;
                }

                Vector2f shieldEdge2 = MathUtils.getPointOnCircumference(circleCenter, circleRadius, MathUtils.clampAngle(shield.getFacing() - shield.getActiveArc() / 2));
                Vector2f tmp3 = CollisionUtils.getCollisionPoint(segStart, tmp, circleCenter, shieldEdge2);
                if (tmp3 != null) {
                    tmp = tmp3;
                    hit = true;
                }

                // If we don't ignore hull hit, check if there is one...
                if (!ignoreHull && CollisionUtils.getCollisionPoint(segStart, tmp, ship) != null) {
                    return null;
                }
                // return null if do not hit shield.
                return hit ? tmp : null;
            }
        }
        return null;
    }
}

[close]
3rd
\data\scripts\utils\FakeBeam.java
A demo for how to use FakeBeamTM
Spoiler
This code was moded form SCY. By use it you can replicate the laser head torpedo's FakeBeam effect. (But the VFX seens a bit discrepancy...never mind.)
It serve as a demo to show how to fake a burst beam. Including how to find out the hit point of beam, and how to render a FakeBeamTM.
Code: java
// By Tartiflette and Deathfly
package data.scripts.util;

import com.fs.starfarer.api.Global;
import com.fs.starfarer.api.combat.CollisionClass;
import com.fs.starfarer.api.combat.CombatAsteroidAPI;
import java.awt.Color;
import org.lwjgl.util.vector.Vector2f;
import com.fs.starfarer.api.combat.CombatEngineAPI;
import com.fs.starfarer.api.combat.CombatEntityAPI;
import com.fs.starfarer.api.combat.DamageType;
import com.fs.starfarer.api.combat.ShipAPI;
import com.fs.starfarer.api.graphics.SpriteAPI;
import data.scripts.plugins.FakeBeamPlugin;
import java.util.List;
import org.lazywizard.lazylib.CollisionUtils;
import org.lazywizard.lazylib.MathUtils;
import org.lazywizard.lazylib.combat.CombatUtils;

public class FakeBeam {

    //
    //Fake beam generator.
    //
    //Create a visually convincing beam from arbitrary coordinates.
    //It however has several limitation:
    // - It deal damage instantly and is therefore only meant to be used for burst beams.
    // - It cannot be "cut" by another object passing between the two ends, a very short duration is thus preferable.
    // - Unlike vanilla, it deals full damage to armor, be carefull when using HIGH_EXPLOSIVE damage type.
    //
    // Most of the parameters are self explanatory but just in case:
    //
    //engine : Combat Engine
    //start : source point of the beam
    //range : maximum effective range (the beam will visually fade a few pixels farther)
    //aim : direction of the beam
    //width : width of the beam
    //fading : duration of the beam
    //normalDamage : nominal burst damage of the beam (don't forget to calculate the skill modifiers before that)
    //               will potentially be modified when fighting some modded factions like Exigency.
    //type : damage type of the beam
    //emp : nominal emp damage if any
    //source : ship dealing the damage
    //size : glow size on the impact point
    //duration : duration of the impact glow (should be at least as long as the beam fading)
    //color : color of the impact glow
    //
    //Note that there is no control over the beam's color, you'll have to directly modify the fakeBeamFX.png for that
    //    
    /////////////////////////////////////////
    //                                     //
    //             FAKE BEAM               //
    //                                     //
    /////////////////////////////////////////    
    public static void applyFakeBeamEffect(CombatEngineAPI engine, Vector2f start, float range, float aim, float width, float fading, float normalDamage, DamageType type, float emp, ShipAPI source, float size, float duration, Color color) {
        CombatEntityAPI theTarget = null;
        float damage = normalDamage;
        // beam sprite to darw.
        SpriteAPI beamSprite = Global.getSettings().getSprite("beams", "SCY_fakeBeamFX");

        //default end point
        Vector2f end = MathUtils.getPointOnCircumference(start, range, aim);
      

        //list all nearby entities that could be hit
        List<CombatEntityAPI> entity = CombatUtils.getEntitiesWithinRange(start, range + 500);
        if (!entity.isEmpty()) {
            for (CombatEntityAPI e : entity) {

                //ignore un-hittable stuff like phased ships
                if (e.getCollisionClass() == CollisionClass.NONE) {
                    continue;
                }
                
                

                //damage can be reduced against some modded ships
                float newDamage = normalDamage;

                Vector2f col = new Vector2f(1000000, 1000000);
                //ignore everything but ships...
                if (e instanceof ShipAPI
                        && CollisionUtils.getCollides(start, end, e.getLocation(), e.getCollisionRadius())) {
                    //check for a shield impact, then hull and take the closest one                  
                    ShipAPI s = (ShipAPI) e;

                    //find the collision point with shields/hull
                    Vector2f hitPoint = CollisionUtilsEX.getShipCollisionPoint(start, end, s);
                    if (hitPoint != null) {
                        col = hitPoint;
                    }

                    //check for modded ships with damage reduction (not in use)
//                    if (s.getHullSpec().getBaseHullId().startsWith("exigency_")) {
//                        newDamage = normalDamage / 2;
//                    }
                    
                    //check for beam damage reduction
                    newDamage = normalDamage * s.getMutableStats().getBeamDamageTakenMult().getModifiedValue();

                } else //...and asteroids!
                if (e instanceof CombatAsteroidAPI
                        && CollisionUtils.getCollides(start, end, e.getLocation(), e.getCollisionRadius())) {
                    Vector2f cAst = CollisionUtilsEX.getCollisionPointOnCircumference(start, end, e.getLocation(), e.getCollisionRadius());
                    if (cAst != null) {
                        col = cAst;
                    }
                }

                //if there was an impact and it is closer than the curent beam end point, set it as the new end point and store the target to apply damage later damage
                if (col != new Vector2f(1000000, 1000000) && MathUtils.getDistance(start, col) < MathUtils.getDistance(start, end)) {
                    end = col;
                    theTarget = e;
                    damage = newDamage;
                }
            }

            //if the beam impacted something, apply the damage
            if (theTarget != null) {

                //damage
                engine.applyDamage(
                        theTarget,
                        end,
                        damage,
                        type,
                        emp,
                        false,
                        true,
                        source
                );
                //impact flash
                engine.addHitParticle(
                        end,
                        theTarget.getVelocity(),
                        (float) Math.random() * size / 2 + size,
                        1,
                        (float) Math.random() * duration / 2 + duration,
                        color
                );
                engine.addHitParticle(
                        end,
                        theTarget.getVelocity(),
                        (float) Math.random() * size / 4 + size / 2,
                        1,
                        0.1f,
                        Color.WHITE
                );
            }
            //Add the beam to the plugin
            FakeBeamPlugin.renderFakeBeam(start, MathUtils.getDistance(start, end) + 10, aim, width, duration, beamSprite, null);
        }
    }
}
[close]


And as before, the two lines needs to be added to the settings.json to make the whole thing work:
Code
{
"plugins":{
"FakeBeamPlugin":"data.scripts.plugins.FakeBeamPlugin",
},
                
"graphics":{
"beams":{
"SCY_fakeBeamFX":"graphics/FAKEBEAM/fakeBeamFX.png",
},
}
}
(Or you can add your own beam sprite instead of SCY_fakeBeamFX.)

And that's all. Hope this one will help.
Have fun!

EDIT: It is my bad to forget this at the frist time. But later better then never.
A special credit to 1930s who give me this idea and the old fakeBeam render source code. And I do hope your Knights will emerge here in sometime, for peace.


[attachment deleted by admin]
Title: Re: The Radioactive Code Dump
Post by: TJJ on July 24, 2015, 03:45:43 AM
If( col != new Vector2f(1000000, 1000000)........

That expression is redundant, it will always be true.
Title: Re: The Radioactive Code Dump
Post by: Tartiflette on July 24, 2015, 04:10:54 AM
If( col != new Vector2f(1000000, 1000000)........

That expression is redundant, it will always be true.
It's there to check if the temporary "col" point has been moved to a collision point since it's creation, witch is the purpose of the few lines above. If it's still at (1000000,1000000) that means the beam didn't collide with anything, but it's not always the case.
Title: Re: The Radioactive Code Dump
Post by: TJJ on July 24, 2015, 05:22:43 AM
If( col != new Vector2f(1000000, 1000000)........

That expression is redundant, it will always be true.
It's there to check if the temporary "col" point has been moved to a collision point since it's creation, witch is the purpose of the few lines above. If it's still at (1000000,1000000) that means the beam didn't collide with anything, but it's not always the case.

You're creating a new object, then checking its reference for inequality against something else.
A newly created reference will never be equal to something (anything!) else.
Thus "col != new Vector2f(1000000, 1000000)" is a tautology, it will always evaluate to "true". (RuntimeExceptions/Errors/concurrency not withstanding.)
Title: Re: The Radioactive Code Dump
Post by: Deathfly on July 24, 2015, 05:45:09 AM
If( col != new Vector2f(1000000, 1000000)........

That expression is redundant, it will always be true.
It's there to check if the temporary "col" point has been moved to a collision point since it's creation, witch is the purpose of the few lines above. If it's still at (1000000,1000000) that means the beam didn't collide with anything, but it's not always the case.

You're creating a new object, then checking its reference for inequality against something else.
A newly created reference will never be equal to something (anything!) else.
Thus "col != new Vector2f(1000000, 1000000)" is a tautology, it will always evaluate to "true". (RuntimeExceptions/Errors/concurrency not withstanding.)

you just remind me a old odd bug we used to have. ^_^
We used to check "col != new Vector2f(0, 0)" until we found if the target's loc is Vector2f(0, 0), the beam will always miss it. >.<
Title: Re: The Radioactive Code Dump
Post by: kazi on August 10, 2015, 02:08:10 AM
Here's a vastly improved version of the FakeBeam code. I pretty much completely rewrote it- posting it here cause I got the original source/idea from here (and it seemed like the nice thing to do). It still performs all of the same function, with none of the drawbacks. Targets/beam hit points are recalculated 8 times a second, and damage is applied continuously. You can essentially use this for any type of beam now, including ones with long firing durations.

We've gone from 3 classes worth of code to 2: a spec class that performs all of the hit and damage calculations, and a rendering class that tracks and manages instances of the spec class fed to it.

The renderer:
Spoiler
Code: Java
package data.scripts.plugins.beamRenderer;

import com.fs.starfarer.api.Global;
import com.fs.starfarer.api.combat.BaseEveryFrameCombatPlugin;
import com.fs.starfarer.api.combat.CombatEngineAPI;
import com.fs.starfarer.api.combat.ViewportAPI;
import com.fs.starfarer.api.graphics.SpriteAPI;

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

import com.fs.starfarer.api.input.InputEventAPI;
import org.lazywizard.lazylib.MathUtils;
import org.lwjgl.util.vector.Vector2f;

/** Draw arbitrary beam sprites wherever you need them and fade them out. Has none of the drawbacks of the old code (static beams, one-time damage).
 *  @author Tartiflette and Deathfly (complete and TOTAL rewrite by kazi)
 */
public class BeamRendererPlugin extends BaseEveryFrameCombatPlugin {

    private CombatEngineAPI engine;

    private static ArrayList<BeamSpec> beamsToRender = new ArrayList<>();
    private ArrayList<BeamSpec> toRemove = new ArrayList<>();

    // add beams to the rendering/damage thread this way (by creating a NEW beamSpec object using the constructor)
    public static void addBeam(BeamSpec newBeam) {
        beamsToRender.add(newBeam);
        newBeam.calcImpactPoint();

        // only draw flashes once
        newBeam.engine.addHitParticle(newBeam.startLoc, new Vector2f(),
                (float) Math.random() * newBeam.size / 2 + newBeam.size,
                1,
                (float) Math.random() * newBeam.duration / 2 + newBeam.duration,
                newBeam.beamColor);
        if (newBeam.target != null) {
            newBeam.engine.addHitParticle(newBeam.hitLoc, newBeam.target.getVelocity(),
                    (float) Math.random() * newBeam.size / 4 + newBeam.size / 2,
                    1, 0.1f, Color.WHITE);
            newBeam.engine.addHitParticle(newBeam.hitLoc, newBeam.target.getVelocity(),
                    (float) Math.random() * newBeam.size / 2 + newBeam.size,
                    1,
                    (float) Math.random() * newBeam.duration / 2 + newBeam.duration,
                    newBeam.beamColor);
        }
    }

    @Override
    public void init(CombatEngineAPI combatEngineAPI) {
        engine = Global.getCombatEngine();
        //reinitialize the map
        beamsToRender.clear();
    }

    @Override
    public void advance(float amount, List<InputEventAPI> events) {
        if (engine == null || engine.isPaused()) {
            return;
        }

        // recalculate render coords and apply damage
        for (BeamSpec beam : beamsToRender) {
            beam.update(amount);
        }
    }

    @Override
    public void renderInWorldCoords(ViewportAPI view) {
        if (engine == null) {
            return;
        }

        if (!beamsToRender.isEmpty()) {
            // iterate through the beams, rendering each in turn
            for (BeamSpec beam : beamsToRender) {
                if (beam.isDone) {
                    toRemove.add(beam);
                    continue;
                }

                //draw the beam
                SpriteAPI sprite = beam.sprite;
                sprite.setAlphaMult(beam.opacity);
                sprite.setSize(MathUtils.getDistance(beam.startLoc, beam.hitLoc) + 25f, beam.width);
                sprite.setAngle(beam.aim);
                Vector2f center = MathUtils.getMidpoint(beam.startLoc, beam.hitLoc);
                sprite.renderAtCenter(center.x, center.y);
            }

            //remove the beams that are done
            if (!toRemove.isEmpty()) {
                beamsToRender.removeAll(toRemove);
                toRemove.clear();
            }
        }
    }
}
[close]

The spec class:
Spoiler
Code: Java
package data.scripts.plugins.beamRenderer;

import com.fs.starfarer.api.Global;
import com.fs.starfarer.api.combat.*;
import com.fs.starfarer.api.graphics.SpriteAPI;
import data.scripts.util.ilk_CollisionUtilsEX;
import org.lazywizard.lazylib.CollisionUtils;
import org.lazywizard.lazylib.FastTrig;
import org.lazywizard.lazylib.MathUtils;
import org.lazywizard.lazylib.combat.CombatUtils;
import org.lwjgl.util.vector.Vector2f;

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

/**
 * Created by Jeff on 2015-08-09.
 */
public class BeamSpec {

    // initialization variables
    CombatEngineAPI engine;
    ShipAPI source;
    float damage;
    DamageType type;
    float empDamage;
    float duration;
    float fadeOut;
    float fadeIn;
    float aim;
    float range;
    float width;
    SpriteAPI sprite;
    float size;
    Color beamColor;

    // dynamic variables calculated by update method
    private float interval;
    private float delta;
    private static final float RECALC_INTERVAL = 0.125f;

    //renderer variables
    Vector2f startLoc;
    Vector2f hitLoc;
    boolean isDone;
    float intensity;
    float opacity;
    CombatEntityAPI target;

    public BeamSpec(CombatEngineAPI combatEngineAPI, ShipAPI setSource, Vector2f startLocSet, float rangeSet, float aimSet, float damageAmt,
                    DamageType damageType, float empDamageAmt, float time, float fadeInSet, float fadeOutSet,
                    String spriteKey, String spriteName, float wide, Color colorSet) {
        engine = combatEngineAPI;
        source = setSource;
        startLoc = startLocSet;
        damage = damageAmt;
        type = damageType;
        empDamage = empDamageAmt;
        duration = time;
        fadeOut = fadeOutSet;
        fadeIn = fadeInSet;
        range = rangeSet;
        aim = aimSet;
        width = wide;
        size = 2 * width;
        sprite = Global.getSettings().getSprite(spriteKey, spriteName);
        sprite.setAdditiveBlend();
        beamColor = colorSet;

        interval = 0f;
        intensity = 0f;
        opacity = 0f;
        hitLoc = new Vector2f();
        isDone = false;
    }

    /** Recalculate damage and hit location based on updated time since last frame
     *
     * @param amount delta time
     */
    public void update(float amount) {

        delta += amount;

        // where are we at in the current beam firing cycle
        if (delta <= fadeIn) {
            intensity = delta / fadeIn;
            opacity = (float) (FastTrig.sin(intensity * Math.PI / 2));
            //second condition for elseif not necessary for next as values lower than fadeIn have already been caught
        } else if (delta <= duration + fadeIn) {
            intensity = 1f;
            opacity = 1f;
        } else if (delta <= fadeIn + duration + fadeOut) {
            intensity = 1f - ((delta - fadeIn - duration) / fadeOut);
            opacity = (float) (FastTrig.sin(intensity * Math.PI / 2));
        } else {
            intensity = 0f;
            opacity = 0f;
            isDone = true;
        }

        // only recalc hitpoint after a certain update interval to avoid wasting cpu for no reason
        interval += amount;
        if (interval > RECALC_INTERVAL) {
            interval = 0f;
            calcImpactPoint();
        }

        // recalc damage if we've hit something
        if (target != null) {
            float currDamage = damage * amount * intensity;
            float currEmp = empDamage * amount * intensity;

            if (target instanceof ShipAPI) {
                ShipAPI ship = (ShipAPI) target;
                //check for modded ships with damage reduction
                //if (ship.getHullSpec().getBaseHullId().startsWith("exigency_") ) currDamage /= 2;

                //check for beam damage reduction
                currDamage *= ship.getMutableStats().getBeamDamageTakenMult().getModifiedValue();

                //check for emp damage reduction
                currEmp *= ship.getMutableStats().getEmpDamageTakenMult().getModifiedValue();
            }
            // DEAL DE DAMAGE!
            engine.applyDamage(target, hitLoc, currDamage, type, currEmp, false, true, source);
        }
    }


    // did we hit something?
    void calcImpactPoint() {
        //default end point
        Vector2f end = MathUtils.getPointOnCircumference(startLoc, range, aim);
        CombatEntityAPI theTarget = null;

        //list all nearby entities that could be hit
        for (CombatEntityAPI entity : CombatUtils.getEntitiesWithinRange(startLoc, range + 500f)) {

            // ignore un-hittable stuff like phased ships
            if (entity.getCollisionClass() == CollisionClass.NONE) {
                continue;
            }

            // ignore friendlies
            if (entity.getOwner() == source.getOwner()) continue;

            // check for collision
            if (CollisionUtils.getCollides(startLoc, end, entity.getLocation(), entity.getCollisionRadius())) {
                Vector2f collide = null;

                // ship collision?
                if (entity instanceof ShipAPI) {
                    //find the collision point with shields/hull
                    Vector2f hitPoint = ilk_CollisionUtilsEX.getShipCollisionPoint(startLoc, end, (ShipAPI) entity);
                    if (hitPoint != null) collide = hitPoint;

                    // asteroid collision?
                } else if (entity instanceof CombatAsteroidAPI) {
                    Vector2f hitPoint = ilk_CollisionUtilsEX.getCollisionPointOnCircumference(startLoc, end, entity.getLocation(), entity.getCollisionRadius());
                    if (hitPoint != null) collide = hitPoint;
                }

                //if impact is closer than the curent beam end point, set it as the new end point and save target
                if ((collide != null) && (MathUtils.getDistance(startLoc, collide) < MathUtils.getDistance(startLoc, end))) {
                    end = collide;
                    theTarget = entity;
                }
            }
        }

        //okay update variables
        target = theTarget;
        hitLoc = end;
    }

    public void setColor(Color color) {
        sprite.setColor(color);
        beamColor = color;
    }

    public void setWidth(float wide) {
        width = wide;
    }

    public void setStartLoc(Vector2f startLoc) {
        this.startLoc = startLoc;
    }
}
[close]

To use, just invoke the renderer's add() method with a new instance of the beam spec, like so:
Code
BeamRendererPlugin.addBeam(new BeamSpec(engine, proj.getSource(), fireLoc, 700f, proj.getFacing(),
                                1000f, DamageType.ENERGY, 0f,
                                0.7f, 0.1f, 0.2f, //duration, in, out
                                "beams", "ilk_fakeBeamFX", 27, new Color(224,184,225,175)));

Note: the only requirement for running this is having both the renderer and the spec class in the same package, as they talk to each other using protected field access (got lazy there). You'll still need the CollisionUtilsEX that Deathfly posted, and the plugin also needs to be registered in settings.json.

EDIT: It just occurred to me that this still doesn't take into account armor values, but it should be super easy to add in (given that you have a hit location for every frame).
Title: Re: The Radioactive Code Dump
Post by: Tartiflette on October 15, 2015, 07:37:42 AM
Yet another missile AI, this time with all the knobs and switches you need to customize it anyway you want. No Java skill needed, but it require to be compiled.
Customizable features:
Tested on a hundred missiles at once with no noticeable performance impact.

Spoiler
Code: Java
//By Tartiflette, highly customizable Missile AI.
package data.scripts.ai;

import com.fs.starfarer.api.Global;
import com.fs.starfarer.api.combat.CombatEngineAPI;
import com.fs.starfarer.api.combat.CombatEntityAPI;
import com.fs.starfarer.api.combat.GuidedMissileAI;
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 java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
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.CombatUtils;
import org.lwjgl.util.vector.Vector2f;

public class CustomMissileAI implements MissileAIPlugin, GuidedMissileAI {
         
   
    //////////////////////
    //     SETTINGS     //
    //////////////////////
   
    //Angle with the target beyond which the missile turn around without accelerating. Avoid endless circling.
    //  Set to a negative value to disable
    private final float OVERSHOT_ANGLE=60;
   
    //Time to complete a wave in seconds.
    private final float WAVE_TIME=2;
   
    //Angle of the waving in degree (divided by 3 with ECCM). Set to a negative value to avoid all waving.
    private final float WAVE_AMPLITUDE=15;
   
    //Damping of the turn speed when closing on the desired aim. The smaller the snappier.
    private final float DAMPING=0.1f;
   
    //Does the missile try to correct it's velocity vector as fast as possible or just point to the desired direction and drift a bit?
    //  Can create strange results with large waving
    //  Require a projectile with a decent turn rate and around twice that in turn acceleration compared to their top speed
    //  Useful for slow torpedoes with low forward acceleration, or ultra precise anti-fighter missiles.     
    private final boolean OVERSTEER=false;  //REQUIRE NO OVERSHOOT ANGLE!
   
    //Does the missile switch its target if it has been destroyed?
    private final boolean TARGET_SWITCH=true;
   
    //Does the missile find a random target or aways tries to hit the ship's one?     
    //   0: No random target seeking,
    //       If the launching ship has a valid target, the missile will pursue that one.
    //       If there is no target selected, it will check for an unselected cursor target.
    //       If there is none, it will pursue its closest valid threat.   
    //   1: Local random target,
    //       If the ship has a target selected, the missile will pick a random valid threat around that one.
    //       If the ship has none, the missile will pursue a random valid threat around the cursor, or itself in AI control.   
    //   2: Full random,
    //       The missile will always seek a random valid threat around itself.
    private final Integer RANDOM_TARGET=0;
   
    //Both targeting behavior can be false for a missile that always get the ship's target or the closest one
    //Prioritize hitting fighters and drones (if false, the missile will still be able to target fighters but not drones)
    private final boolean ANTI_FIGHTER=false;    //INCOMPATIBLE WITH ASSAULT
   
    //Target the biggest threats first
    private final boolean ASSAULT=true;  //INCOMPATIBLE WITH ANTI-FIGHTER
   
    //range in which the missile seek a target in game units.
    private final float MAX_SEARCH_RANGE = 1500;
   
    //range under which the missile start to get progressively more precise in game units.
    private float PRECISION_RANGE=500;
   
    //Is the missile lead the target or tailchase it?
    private final boolean LEADING=true;
   
    //Leading loss without ECCM hullmod. The higher, the less accurate the leading calculation will be.
    //   1: perfect leading with and without ECCM
    //   2: half precision without ECCM
    //   3: a third as precise without ECCM. Default
    //   4, 5, 6 etc : 1/4th, 1/5th, 1/6th etc precision.
    private float ECCM=3;   //A VALUE BELOW 1 WILL PREVENT THE MISSILE FROM EVER HITTING ITS TARGET!
   
   
    //////////////////////
    //    VARIABLES     //
    //////////////////////
   
    //max speed of the missile after modifiers.
    private final float MAX_SPEED;
    //Max range of the missile after modifiers.
    private final float MAX_RANGE;
    //Random starting offset for the waving.
    private final float OFFSET;
    private CombatEngineAPI engine;
    private final MissileAPI MISSILE;
    private CombatEntityAPI target;
    private Vector2f lead = new Vector2f();
    private boolean launch=true;
    private float timer=0, check=0f;

    //////////////////////
    //  DATA COLLECTING //
    //////////////////////
   
    public CustomMissileAI(MissileAPI missile, ShipAPI launchingShip) {
        this.MISSILE = missile;
        MAX_SPEED = missile.getMaxSpeed();
        MAX_RANGE = missile.getWeapon().getRange();
        if (missile.getSource().getVariant().getHullMods().contains("eccm")){
            ECCM=1;
        }       
        //calculate the precision range factor
        PRECISION_RANGE=(float)Math.pow((2*PRECISION_RANGE),2);
        OFFSET=(float)(Math.random()*Math.PI*2);
    }
   
    //////////////////////
    //   MAIN AI LOOP   //
    //////////////////////
   
    @Override
    public void advance(float amount) {
       
        if (engine != Global.getCombatEngine()) {
            this.engine = Global.getCombatEngine();
        }
       
        //skip the AI if the game is paused, the missile is engineless or fading
        if (Global.getCombatEngine().isPaused() || MISSILE.isFading() || MISSILE.isFizzling()) {return;}
       
        //assigning a target if there is none or it got destroyed
        if (target == null
                || target.getOwner()==MISSILE.getOwner()
                || (TARGET_SWITCH && (target instanceof ShipAPI && ((ShipAPI) target).isHulk())
                                  || !engine.isEntityInPlay(target)
                   )
                ){
            setTarget(assignTarget(MISSILE));
            //forced acceleration by default
            MISSILE.giveCommand(ShipCommand.ACCELERATE);
            return;
        }
       
        timer+=amount;
        //finding lead point to aim to       
        if(launch || timer>=check){
            launch=false;
            timer -=check;
            //set the next check time
            check = Math.min(
                    0.25f,
                    Math.max(
                            0.03f,
                            MathUtils.getDistanceSquared(MISSILE, target)/PRECISION_RANGE)
            );
            if(LEADING){
                //best intercepting point
                lead = AIUtils.getBestInterceptPoint(
                        MISSILE.getLocation(),
                        MAX_SPEED*ECCM, //if eccm is intalled the point is accurate, otherwise it's placed closer to the target (almost tailchasing)
                        target.getLocation(),
                        target.getVelocity()
                );               
                //null pointer protection
                if (lead == null) {
                    lead = target.getLocation();
                }
            } else {
                lead = target.getLocation();
            }
        }
       
        //best velocity vector angle for interception
        float correctAngle = VectorUtils.getAngle(
                        MISSILE.getLocation(),
                        lead
                );
       
        if (OVERSTEER){
            //velocity angle correction
            float offCourseAngle = MathUtils.getShortestRotation(
                    VectorUtils.getFacing(MISSILE.getVelocity()),
                    correctAngle
                    );

            float correction = MathUtils.getShortestRotation(               
                    correctAngle,
                    VectorUtils.getFacing(MISSILE.getVelocity())+180
                    )
                    * 0.5f * //oversteer
                    (float)((Math.sin(Math.PI/90*(Math.min(Math.abs(offCourseAngle),45))))); //damping when the correction isn't important

            //modified optimal facing to correct the velocity vector angle as soon as possible
            correctAngle = correctAngle+correction;
        }
       
        if(WAVE_AMPLITUDE>0){           
            //waving
            float multiplier=1;
            if(ECCM<=1){
                multiplier=0.3f;
            }
            correctAngle+=multiplier*WAVE_AMPLITUDE*check*Math.cos(OFFSET+MISSILE.getElapsed()*(2*Math.PI/WAVE_TIME));
        }
       
        //target angle for interception       
        float aimAngle = MathUtils.getShortestRotation( MISSILE.getFacing(), correctAngle);
       
        if(OVERSHOT_ANGLE<=0 || Math.abs(aimAngle)<OVERSHOT_ANGLE){
            MISSILE.giveCommand(ShipCommand.ACCELERATE); 
        }
       
        if (aimAngle < 0) {
            MISSILE.giveCommand(ShipCommand.TURN_RIGHT);
        } else {
            MISSILE.giveCommand(ShipCommand.TURN_LEFT);
        } 
       
        // Damp angular velocity if the missile aim is getting close to the targeted angle
        if (Math.abs(aimAngle) < Math.abs(MISSILE.getAngularVelocity()) * DAMPING) {
            MISSILE.setAngularVelocity(aimAngle / DAMPING);
        }
    }
   
    //////////////////////
    //    TARGETING     //
    //////////////////////
   
    public CombatEntityAPI assignTarget(MissileAPI missile){
       
        ShipAPI theTarget=null;       
        ShipAPI source = missile.getSource();       
        ShipAPI currentTarget;
       
        //check for a target from its source
        if(source != null
                && source.getShipTarget() != null
                && source.getShipTarget() instanceof ShipAPI
                && source.getShipTarget().getOwner() != missile.getOwner()
                ){
            currentTarget=source.getShipTarget();
        } else {
            currentTarget=null;
        }
       
        //random target selection
        if (RANDOM_TARGET>0){ 
            //random mode 1: the missile will look for a target around itsef
            Vector2f location = missile.getLocation();   
            //random mode 2: if its source has a target selected, it will look for random one around that point
            if( RANDOM_TARGET<2){                                     
                if(currentTarget != null
                        && currentTarget.isAlive()
                        && MathUtils.isWithinRange(missile, currentTarget, MAX_RANGE)
                        ){
                    location = currentTarget.getLocation();
                } else if (source != null
                        && source.getMouseTarget()!=null){
                    location=source.getMouseTarget();
                }
            }
            //fetch the right kind of target
            if(ANTI_FIGHTER){
                theTarget = getRandomFighterTarget(location);
            } else if(ASSAULT){
                theTarget = getRandomLargeTarget(location);
            } else {
                theTarget = getAnyTarget(location);
            }   
        //non random targeting   
        } else {
            if(source!=null){
                //ship target first
                if(currentTarget!=null
                        && currentTarget.isAlive()
                        && currentTarget.getOwner()!=missile.getOwner()
                        && !(ANTI_FIGHTER && !(currentTarget.isDrone() && currentTarget.isFighter()))
                        && !(ASSAULT && (currentTarget.isDrone() || currentTarget.isFighter()))
                        ){
                    theTarget=currentTarget;               
                } else {
                    //or cursor target if there isn't one
                    List<ShipAPI> mouseTargets = CombatUtils.getShipsWithinRange(source.getMouseTarget(), 100f);
                    if (!mouseTargets.isEmpty()) {
                        Collections.sort(mouseTargets, new CollectionUtils.SortEntitiesByDistance(source.getMouseTarget()));
                        for (ShipAPI tmp : mouseTargets) {
                            if (tmp.isAlive()
                                    && tmp.getOwner() != missile.getOwner()
                                    && !(ANTI_FIGHTER && !(tmp.isDrone() && tmp.isFighter()))
                                    && !(ASSAULT && (tmp.isDrone() || tmp.isFighter() || tmp.isFrigate()))
                                    ) {
                                theTarget=tmp;
                                break;
                            }
                        }
                    }               
                }
            }
            //still no valid target? lets try the closest one
            //most of the time a ship will have a target so that doesn't need to be perfect.
            if(theTarget==null){
                List<ShipAPI> closeTargets = AIUtils.getNearbyEnemies(missile, MAX_SEARCH_RANGE);
                if (!closeTargets.isEmpty()) {
                    Collections.sort(closeTargets, new CollectionUtils.SortEntitiesByDistance(missile.getLocation()));
                    if (ASSAULT){   //assault missiles will somewhat prioritize toward bigger threats even if there is a closer small one, and avoid fighters and drones.
                        for (ShipAPI tmp : closeTargets) {
                            if (tmp.isAlive()
                                    && tmp.getOwner() != missile.getOwner()
                                    ) {
                                if (tmp.isCapital() || tmp.isCruiser()){
                                    theTarget=tmp;
                                    break;
                                } else if (tmp.isDestroyer() && Math.random()>0.5){
                                    theTarget=tmp;
                                    break;
                                } else if (tmp.isDestroyer() && Math.random()>0.75){
                                    theTarget=tmp;
                                    break;
                                } else if (!tmp.isDrone() && !tmp.isFighter() && Math.random()>0.95){
                                    theTarget=tmp;
                                    break;
                                }
                            }
                        }
                    }else if(ANTI_FIGHTER){    //anti-fighter missile will target the closest drone or fighter
                        for (ShipAPI tmp : closeTargets) {
                            if (tmp.isAlive()
                                    && tmp.getOwner() != missile.getOwner()
                                    && (tmp.isDrone() || tmp.isFighter())
                                    ) {
                                theTarget=tmp;
                                break;
                            }
                        }
                    }else{  //non assault, non anti-fighter missile target the closest non-drone ship
                        for (ShipAPI tmp : closeTargets) {
                            if (tmp.isAlive()
                                    && tmp.getOwner() != missile.getOwner()
                                    && !tmp.isDrone()
                                    ) { 
                                theTarget=tmp;
                                break;
                            }
                        }
                    }
                }
            }
        }       
        return theTarget;
    }
   
    //Random picker for fighters and drones
    public ShipAPI getRandomFighterTarget(Vector2f location){
        ShipAPI select=null;
        Map<Integer, ShipAPI> PRIORITYLIST = new HashMap<>();
        Map<Integer, ShipAPI> OTHERSLIST = new HashMap<>();
        int i=1, u=1;
        List<ShipAPI> potentialTargets = CombatUtils.getShipsWithinRange(location, MAX_RANGE);
        if (!potentialTargets.isEmpty()) {
            for (ShipAPI tmp : potentialTargets) {
                if (tmp.isAlive()
                        && tmp.getOwner() != MISSILE.getOwner()
                        ) {
                    if (tmp.isFighter() || tmp.isDrone()){
                        PRIORITYLIST.put(i, tmp);
                        i++;
                    } else {                           
                        OTHERSLIST.put(u, tmp);
                        u++;
                    }
                }
            }
            if (!PRIORITYLIST.isEmpty()){
                int chooser=Math.round((float)Math.random()*(i-1)+0.5f);
                select=PRIORITYLIST.get(chooser);
            } else if (!OTHERSLIST.isEmpty()){                   
                int chooser=Math.round((float)Math.random()*(u-1)+0.5f);
                select=OTHERSLIST.get(chooser);
            }
        }
        return select;
    }
   
    //Random target selection strongly weighted toward bigger threats in range
    public ShipAPI getRandomLargeTarget(Vector2f location){
        ShipAPI select=null;
        Map<Integer, ShipAPI> PRIORITY1 = new HashMap<>();
        Map<Integer, ShipAPI> PRIORITY2 = new HashMap<>();
        Map<Integer, ShipAPI> PRIORITY3 = new HashMap<>();
        Map<Integer, ShipAPI> PRIORITY4 = new HashMap<>();
        Map<Integer, ShipAPI> OTHERSLIST = new HashMap<>();
        int i=1, u=1, v=1, x=1, y=1;
        List<ShipAPI> potentialTargets = CombatUtils.getShipsWithinRange(location, MAX_RANGE);
        if (!potentialTargets.isEmpty()) {
            for (ShipAPI tmp : potentialTargets) {
                if (tmp.isAlive()
                        && tmp.getOwner() != MISSILE.getOwner()
                        && !tmp.isDrone()
                        ) {
                    if (tmp.isCapital()){
                        PRIORITY1.put(i, tmp);
                        i++;
                        PRIORITY2.put(u, tmp);
                        u++;
                        PRIORITY3.put(x, tmp);
                        x++;
                        PRIORITY4.put(v, tmp);
                        v++;
                        OTHERSLIST.put(y, tmp);
                        y++;
                    } else if (tmp.isCruiser()){
                        PRIORITY2.put(u, tmp);
                        u++;
                        PRIORITY3.put(x, tmp);
                        x++;
                        PRIORITY4.put(v, tmp);
                        v++;
                        OTHERSLIST.put(y, tmp);
                        y++;
                    } else if (tmp.isDestroyer()){
                        PRIORITY3.put(x, tmp);
                        x++;
                        PRIORITY4.put(v, tmp);
                        v++;
                        OTHERSLIST.put(y, tmp);
                        y++;
                    } else if (tmp.isFrigate()){
                        PRIORITY4.put(v, tmp);
                        v++;
                        OTHERSLIST.put(y, tmp);
                        y++;
                    } else {
                        OTHERSLIST.put(y, tmp);
                        y++;
                    }
                }
            }
            if (!PRIORITY1.isEmpty() && Math.random()>0.8f){
                int chooser=Math.round((float)Math.random()*(i-1)+0.5f);
                select=PRIORITY1.get(chooser);
            } else if (!PRIORITY2.isEmpty() && Math.random()>0.8f){
                int chooser=Math.round((float)Math.random()*(u-1)+0.5f);
                select=PRIORITY2.get(chooser);
            } else if (!PRIORITY3.isEmpty() && Math.random()>0.8f){
                int chooser=Math.round((float)Math.random()*(x-1)+0.5f);
                select=PRIORITY3.get(chooser);
            } else if (!PRIORITY4.isEmpty() && Math.random()>0.8f){
                int chooser=Math.round((float)Math.random()*(v-1)+0.5f);
                select=PRIORITY4.get(chooser);
            } else if (!OTHERSLIST.isEmpty()){                   
                int chooser=Math.round((float)Math.random()*(y-1)+0.5f);
                select=OTHERSLIST.get(chooser);
            }
        }
        return select;
    }

    //Pure random target picker
    public ShipAPI getAnyTarget(Vector2f location){
        ShipAPI select=null;
        Map<Integer, ShipAPI> TARGETLIST = new HashMap<>();
        int i=1;
        List<ShipAPI> potentialTargets = CombatUtils.getShipsWithinRange(location, MAX_RANGE);
        if (!potentialTargets.isEmpty()) {
            for (ShipAPI tmp : potentialTargets) {
                if (tmp.isAlive()
                        && tmp.getOwner() != MISSILE.getOwner()
                        && !tmp.isDrone()
                        ){
                    TARGETLIST.put(i, tmp);
                    i++;                       
                }
            }
            if (!TARGETLIST.isEmpty()){
                int chooser=Math.round((float)Math.random()*(i-1)+0.5f);
                select=TARGETLIST.get(chooser);
            }
        }
        return select;
    }
   
    @Override
    public CombatEntityAPI getTarget() {
        return target;
    }

    @Override
    public void setTarget(CombatEntityAPI target) {
        this.target = target;
    }
   
    public void init(CombatEngineAPI engine) {}
}
[close]
Title: Re: The Radioactive Code Dump
Post by: Tartiflette on December 03, 2015, 02:29:36 AM
A few mods add hulmods that are incompatible with vanilla ones the same way augmented engine can't be installed on top of unstable injector. The little problem there is that while you can make your hullmod incompatible with the vanilla one, it's not possible to do the other way around without modifying the vanilla hullmod (which is bad, nobody should do that!) The common solution we used was to instantly remove the modded hullmod if it get installed next to a conflicting hullmod, but without any feedback that can be puzzling from a player perspective.
This should make things clearer:

(http://i.imgur.com/c48KgAU.jpg)

That "fake" hullmod will be installed in place of the conflicting one, then automatically removed when installing anything else (or can be removed manually if you so desire).

hull_mods.csv entry:
Code
HULLMOD CONFLICT,IncompatibleHullmodWarning,,TRUE,0,0,0,0,data.hullmods.IncompatibleHullmodWarning,"%s, the hullmod you tried to install conflicts with this hull or another previously installed hullmod.",graphics/icons/intel/investigation.png

IncompatibleHullmodWarning.java :
Code: Java
package data.hullmods;

import com.fs.starfarer.api.combat.ShipAPI;

public class IncompatibleHullmodWarning extends BaseHullMod {   
   
    @Override
    public String getDescriptionParam(int index, ShipAPI.HullSize hullSize) {
        if (index == 0) return "WARNING";
       
        return null;
    }
}

and the part you have to add to your hull-mod script:
Code: Java
package data.hullmods;

import com.fs.starfarer.api.combat.ShipAPI;
import java.util.HashSet;
import java.util.Set;

public class YourIncompatibleHullMod extends BaseHullMod {   
    private static final Set<String> BLOCKED_HULLMODS = new HashSet<>();
    static
    {
        // These hullmods will automatically be removed
        BLOCKED_HULLMODS.add("augmentedengines");
        BLOCKED_HULLMODS.add("auxiliarythrusters");
        BLOCKED_HULLMODS.add("unstable_injector");
        BLOCKED_HULLMODS.add("drive_shunt");     
    }
    private float check=0;
    private String ERROR="IncompatibleHullmodWarning";

    @Override
    public void applyEffectsAfterShipCreation(ShipAPI ship, String id){
       
        if (check>0) {     
            check-=1;
            if (check<1){
            ship.getVariant().removeMod(ERROR);   
            }
        }
       
        for (String tmp : BLOCKED_HULLMODS) {
            if (ship.getVariant().getHullMods().contains(tmp)) {               
                ship.getVariant().removeMod(tmp);     
                ship.getVariant().addMod(ERROR);
                check=3;
            }
        }
    }
}
Title: Re: The Radioactive Code Dump
Post by: shuul on December 03, 2015, 02:55:51 AM
This thread is a gift, have no idea how long it will take me to understand all of this, but your code looks just damn sexy. Thanks Tartiflette!
Title: Re: The Radioactive Code Dump
Post by: Deathfly on January 14, 2016, 05:11:10 AM
A small update of the CollisionUtilsEX that will make beam collision with the shield "egdes"...well, less accurate to match vanilla beam do.
(And leave a param in there in case if you need those more accurate detection.)

Another change is the getCollisionPointOnCircumference will return the first intersection point of segment regardless whether segStart is inside the Circumference or not. And the original getCollisionPointOnCircumference was replaced by getCollisionPointOnCircle, which will return segStart directly if the segStart is inside the Circle and was used for shield collision check.

Spoiler
Code: java
// by Deathfly and Tartiflette
package data.scripts.util;

import com.fs.starfarer.api.combat.CollisionClass;
import com.fs.starfarer.api.combat.ShieldAPI;
import com.fs.starfarer.api.combat.ShipAPI;
import java.awt.geom.Line2D;
import org.lazywizard.lazylib.CollisionUtils;
import org.lazywizard.lazylib.MathUtils;
import org.lwjgl.util.vector.Vector2f;

public class CollisionUtilsEX {

    /**
     * return the collision point of segment segStart to segEnd and a ship (will
     * consider shield).
     *
     * @return the collision point of segment segStart to segEnd and a ship
     * (will consider shield). if segment can not hit the ship, will return
     * null.
     * @param segStart if segStart hit the ship hull or shield, will return
     * segStart.
     * @param accurateShieldEdgeTest use an additional test to check if the
     * segment hit the shield on edge. Set to false if you want a vanilla like
     * behaviour.
     */
    public static Vector2f getShipCollisionPoint(Vector2f segStart, Vector2f segEnd, ShipAPI ship, boolean accurateShieldEdgeTest) {

        // if target can not be hit, return null
        if (ship.getCollisionClass() == CollisionClass.NONE) {
            return null;
        }
        ShieldAPI shield = ship.getShield();

        // Check hit point when shield is off.
        if (shield == null || shield.isOff()) {
            return CollisionUtils.getCollisionPoint(segStart, segEnd, ship);
        } // If ship's shield is on, thing goes complicated...
        else {
            Vector2f circleCenter = shield.getLocation();
            float circleRadius = shield.getRadius();
            // calculate the shield collision point
            Vector2f tmp1 = getCollisionPointOnCircle(segStart, segEnd, circleCenter, circleRadius);
            if (tmp1 != null) {
                // OK! hit the shield in face
                if (shield.isWithinArc(tmp1)) {
                    return tmp1;
                } else {

                    boolean hit = false;
                    Vector2f tmp = new Vector2f(segEnd);

                    //the beam cannot go farther than it's max range or the hull
                    Vector2f hullHit = CollisionUtils.getCollisionPoint(segStart, segEnd, ship);
                    if (hullHit != null) {
                        tmp = hullHit;
                        hit = true;
                    }

                    // if the hit come outside the shield's arc but it hit the shield's "edge", find that point.
                    if (accurateShieldEdgeTest) {
                        Vector2f shieldEdge1 = MathUtils.getPointOnCircumference(circleCenter, circleRadius, MathUtils.clampAngle(shield.getFacing() + shield.getActiveArc() / 2));
                        Vector2f tmp2 = CollisionUtils.getCollisionPoint(segStart, tmp, circleCenter, shieldEdge1);
                        if (tmp2 != null) {
                            tmp = tmp2;
                            hit = true;
                        }
                        Vector2f shieldEdge2 = MathUtils.getPointOnCircumference(circleCenter, circleRadius, MathUtils.clampAngle(shield.getFacing() - shield.getActiveArc() / 2));
                        Vector2f tmp3 = CollisionUtils.getCollisionPoint(segStart, tmp, circleCenter, shieldEdge2);
                        if (tmp3 != null) {
                            tmp = tmp3;
                            hit = true;
                        }
                    }

                    // return null if segment not hit anything.
                    return hit ? tmp : null;
                }
            }
        }
        return null;
    }

    // Just a compatible method.
    /**
     * return the collision point of segment segStart to segEnd and a ship (will
     * consider shield).
     *
     * @return the collision point of segment segStart to segEnd and a ship
     * (will consider shield). if segment can not hit the ship, will return
     * null.
     * @param segStart if segStart hit the ship hull or shield, will return
     * segStart.
     */
    public static Vector2f getShipCollisionPoint(Vector2f segStart, Vector2f segEnd, ShipAPI ship) {
        return getShipCollisionPoint(segStart, segEnd, ship, false);
    }

    /**
     * return the first intersection point of the segment segStart to segEnd and
     * the circle.
     *
     * @return the first intersection point of segment segStart to segEnd and
     * circumference. if segStart is outside the circle and segment can not
     * intersection with the circumference, will return null.
     * @param segStart if segStart is inside the circle, will return segStart.
     */
    public static Vector2f getCollisionPointOnCircle(Vector2f segStart, Vector2f segEnd, Vector2f circleCenter, float circleRadius) {

        Vector2f startToEnd = Vector2f.sub(segEnd, segStart, null);
        Vector2f startToCenter = Vector2f.sub(circleCenter, segStart, null);
        double ptLineDistSq = (float) Line2D.ptLineDistSq(segStart.x, segStart.y, segEnd.x, segEnd.y, circleCenter.x, circleCenter.y);

        float circleRadiusSq = circleRadius * circleRadius;

        // if lineStart is within the circle, return it directly
        if (startToCenter.lengthSquared() < circleRadiusSq) {
            return segStart;
        }

        // if lineStart is outside the circle and segment can not reach the circumference, return null
        if (ptLineDistSq > circleRadiusSq || startToCenter.length() - circleRadius > startToEnd.length()) {
            return null;
        }

        // calculate the intersection point.
        startToEnd.normalise(startToEnd);
        double dist = Vector2f.dot(startToCenter, startToEnd) - Math.sqrt(circleRadiusSq - ptLineDistSq);
        startToEnd.scale((float) dist);
        return Vector2f.add(segStart, startToEnd, null);
    }

    /**
     * return the first intersection point of the segment segStart to segEnd and
     * the circumference.
     *
     * @return the first intersection point of segment segStart to segEnd and
     * circumference. if segment can not intersection with the circumference,
     * will return null.
     */
    public static Vector2f getCollisionPointOnCircumference(Vector2f segStart, Vector2f segEnd, Vector2f circleCenter, float circleRadius) {

        Vector2f startToEnd = Vector2f.sub(segEnd, segStart, null);
        Vector2f startToCenter = Vector2f.sub(circleCenter, segStart, null);
        double ptLineDistSq = (float) Line2D.ptLineDistSq(segStart.x, segStart.y, segEnd.x, segEnd.y, circleCenter.x, circleCenter.y);
        float circleRadiusSq = circleRadius * circleRadius;
        boolean CoS = false;
        if (startToCenter.lengthSquared() < circleRadiusSq) {
            CoS = true;
        }

        // if lineStart is outside the circle and segment can not reach the circumference, return null
        if (ptLineDistSq > circleRadiusSq || startToCenter.length() - circleRadius > startToEnd.length()) {
            return null;
        }

        // calculate the intersection point.
        startToEnd.normalise(startToEnd);
        double dist;
        if (CoS) {
            dist = Vector2f.dot(startToCenter, startToEnd) + Math.sqrt(circleRadiusSq - ptLineDistSq);
            if (dist < startToEnd.length()) {
                return null;
            }
        } else {
            dist = Vector2f.dot(startToCenter, startToEnd) - Math.sqrt(circleRadiusSq - ptLineDistSq);
        }
        startToEnd.scale((float) dist);
        return Vector2f.add(segStart, startToEnd, null);
    }

    /**
     * SHOULD ONLY BE USED WHEN YOU ONLY NEED CHECK FOR SHIELD COLLISION POINT!
     * if you need the check for a ship hit (considering it's shield), use
     * getShipCollisionPoint instead.
     *
     * @return the collision point of segment segStart to segEnd and ship's
     * shield. if the segment can not hit the shield or the ship has no shield,
     * return null.
     * @param ignoreHull if ignoreHull = flase and the segment hit the ship's
     * hull first, return null.
     * @param segStart if segStart hit the shield, will return segStart.
     * @param accurateShieldEdgeTest use an additional test to check if the
     * segment hit the shield on edge. Set to false if you want a vanilla like
     * behaviour.
     */
    public static Vector2f getShieldCollisionPoint(Vector2f segStart, Vector2f segEnd, ShipAPI ship, boolean ignoreHull, boolean accurateShieldEdgeTest) {
        // if target not shielded, return null
        ShieldAPI shield = ship.getShield();
        if (ship.getCollisionClass() == CollisionClass.NONE || shield == null || shield.isOff()) {
            return null;
        }
        Vector2f circleCenter = shield.getLocation();
        float circleRadius = shield.getRadius();
        // calculate the shield collision point
        Vector2f tmp1 = getCollisionPointOnCircle(segStart, segEnd, circleCenter, circleRadius);
        if (tmp1 != null) {
            // OK! hit the shield in face
            if (shield.isWithinArc(tmp1)) {
                return tmp1;
            } else {
                // if the hit come outside the shield's arc but it hit the shield's "edge", find that point.                

                Vector2f tmp = new Vector2f(segEnd);
                boolean hit = false;
                if (accurateShieldEdgeTest) {
                    Vector2f shieldEdge1 = MathUtils.getPointOnCircumference(circleCenter, circleRadius, MathUtils.clampAngle(shield.getFacing() + shield.getActiveArc() / 2));
                    Vector2f tmp2 = CollisionUtils.getCollisionPoint(segStart, tmp, circleCenter, shieldEdge1);
                    if (tmp2 != null) {
                        tmp = tmp2;
                        hit = true;
                    }

                    Vector2f shieldEdge2 = MathUtils.getPointOnCircumference(circleCenter, circleRadius, MathUtils.clampAngle(shield.getFacing() - shield.getActiveArc() / 2));
                    Vector2f tmp3 = CollisionUtils.getCollisionPoint(segStart, tmp, circleCenter, shieldEdge2);
                    if (tmp3 != null) {
                        tmp = tmp3;
                        hit = true;
                    }
                }
                // If we don't ignore hull hit, check if there is one...
                if (!ignoreHull && CollisionUtils.getCollisionPoint(segStart, tmp, ship) != null) {
                    return null;
                }
                // return null if do not hit shield.
                return hit ? tmp : null;
            }
        }
        return null;
    }

    // Just a compatible method.
    /**
     * SHOULD ONLY BE USED WHEN YOU ONLY NEED CHECK FOR SHIELD COLLISION POINT!
     * if you need the check for a ship hit (considering it's shield), use
     * getShipCollisionPoint instead.
     *
     * @return the collision point of segment segStart to segEnd and ship's
     * shield. if the segment can not hit the shield or the ship has no shield,
     * return null.
     * @param ignoreHull if ignoreHull = flase and the segment hit the ship's
     * hull first, return null.
     * @param segStart if segStart hit the shield, will return segStart.
     */
    public static Vector2f getShieldCollisionPoint(Vector2f segStart, Vector2f segEnd, ShipAPI ship, boolean ignoreHull) {
        return getShieldCollisionPoint(segStart, segEnd, ship, ignoreHull, false);
    }
}
[close]
Title: Re: The Radioactive Code Dump
Post by: Tartiflette on December 19, 2016, 04:56:46 AM
It's been a long time since nothing has been posted here, but I may have something interesting to share:

Ever wanted to create custom effects that aren't particles or emp arcs? Or add some UI elements? But not really willing to fiddle with direct sprite rendering? Or like me tired of having to rewrite rendering code everywhere? Here is the solution with this SpriteRenderManager plugin! Now you can add almost any effect or rasterized UI element with a single line of code!

(http://i.imgur.com/5G7Q3zE.gif)(http://i.imgur.com/flQbMb2.gif)

You can draw sprites at absolute coordinates, of attach them to an entity, or fix them to the UI. There is a single limitation for it: the sprites will only be drawn the next frame.

Spoiler
/*
 * By Tartiflette
 * Plugin managing direct sprite rendering to create new visual effect or add new UI elements with only one line of code.
 * Note that every sprite will be drawn one frame late.

sample use:

SpriteRenderManager.screenspaceRender(
        Global.getSettings().getSprite("misc", "graphics/fx/wormhole_ring_bright3.png"),
        SpriteRenderManager.positioning.FULLSCREEN_MAINTAIN_RATIO,
        new Vector2f(),
        null,
        new Vector2f(50,50),
        null,
        0,
        360,
        Color.blue,
        false,
        1,
        3,
        1
);
 */
package data.scripts.plugins;

import com.fs.starfarer.api.Global;
import com.fs.starfarer.api.combat.BaseEveryFrameCombatPlugin;
import com.fs.starfarer.api.combat.CombatEngineAPI;
import com.fs.starfarer.api.combat.CombatEntityAPI;
import com.fs.starfarer.api.combat.DamagingProjectileAPI;
import com.fs.starfarer.api.combat.ViewportAPI;
import com.fs.starfarer.api.graphics.SpriteAPI;
import java.awt.Color;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.lazywizard.lazylib.VectorUtils;
import org.lwjgl.util.vector.Vector2f;

public class SpriteRenderManager extends BaseEveryFrameCombatPlugin {
   
    private static List<renderData> SINGLEFRAME = new ArrayList<>();
    private static List<battlespaceData> BATTLESPACE = new ArrayList<>();
    private static List<objectspaceData> OBJECTSPACE = new ArrayList<>();
    private static List<screenspaceData> SCREENSPACE = new ArrayList<>();
   
    @Override
    public void init(CombatEngineAPI engine) {
        //reinitialize the lists
        SINGLEFRAME.clear();
        BATTLESPACE.clear();
        OBJECTSPACE.clear();
        SCREENSPACE.clear();
    }
   
    //////////////////////////////
    //                          //
    //     PUBLIC METHODS       //
    //                          //
    //////////////////////////////
   
    /**
     * @param sprite
     * SpriteAPI to render.
     *
     * @param loc
     * Vector2f, center in world coordinates.
     *
     * @param size
     * Vector2f(width, height) in pixels.
     *
     * @param angle
     * float of the sprite's azimuth. 0 is pointing top.
     *
     * @param color
     * Color() override, also used for fading.
     *
     * @param additive
     * boolean for additive blending.
     */
   
    public static void singleFrameRender(SpriteAPI sprite, Vector2f loc, Vector2f size, float angle, Color color, boolean additive) {
        sprite.setSize(size.x, size.y);
        sprite.setAngle(angle);
        sprite.setColor(color);
        if(additive){
            sprite.setAdditiveBlend();
        }
        SINGLEFRAME.add(new renderData(sprite, loc));
    }     
   
    /**
     *
     * @param sprite
     * SpriteAPI to render.
     *
     * @param loc
     * Vector2f, center in world coordinates.
     *
     * @param vel
     * Vector2f() velocity of the sprite.
     *
     * @param size
     * Vector2f(width, height) in pixels.
     *
     * @param growth
     * Vector2f() change of size over time in pixels/sec. Can be negative, a sprite that completely shrunk will be removed.
     *
     * @param angle
     * float of the sprite's azimuth. 0 is pointing top.
     *
     * @param spin
     * float of the sprite's rotation, in degree/sec.
     *
     * @param color
     * Color() override, also used for fading.
     *
     * @param additive
     * boolean for additive blending.
     *
     * @param fadein
     * time in sec for fading in.
     *
     * @param full
     * time in sec at maximum opacity (clamped by color)
     *
     * @param fadeout
     * time in sec for fading out
     */
   
    public static void battlespaceRender(SpriteAPI sprite, Vector2f loc, Vector2f vel, Vector2f size, Vector2f growth, float angle, float spin, Color color, boolean additive, float fadein, float full, float fadeout) {
        sprite.setSize(size.x, size.y);
        sprite.setAngle(angle);
        sprite.setColor(color);
        if(additive){
            sprite.setAdditiveBlend();
        }       
        BATTLESPACE.add(new battlespaceData(sprite, loc, vel, growth, spin, fadein, fadein+full, fadein+full+fadeout, 0));
    }       
   
    /**
     *
     * @param sprite
     * SpriteAPI to render.
     *
     * @param anchor
     * CombatEntityAPI the sprite will follow.
     *
     * @param offset
     * Vector2f, offset from the anchor's center in world coordinates. If parent is true, it will be relative to the anchor's orientation.
     *
     * @param vel
     * Vector2f() velocity of the sprite relative to the anchor. If parent is true, it will be relative to the anchor's orientation.
     *
     * @param size
     * Vector2f(width, height) in pixels.
     *
     * @param growth
     * Vector2f() change of size over time in pixels/sec. Can be negative, a sprite that completely shrunk will be removed.
     *
     * @param angle
     * float of the sprite's azimuth. 0 is pointing front. If parent is true, 0 will match the anchor's orientation.
     *
     * @param spin
     * float of the sprite's rotation, in degree/sec. If parent is true, it will be relative to the anchor's orientation.
     *
     * @param parent
     * boolean, if true the sprite will also follow the anchor's orientation in addition to the position.
     *
     * @param color
     * Color() override, also used for fading.
     *
     * @param additive
     * boolean for additive blending.
     *
     * @param fadein
     * time in sec for fading in.
     *
     * @param full
     * time in sec at maximum opacity (clamped by color). If attached to a projectile that value can be longer than the maximum flight time, for example 99s.
     *
     * @param fadeout
     * time in sec for fading out. If attached to a projectile, the sprite will immediately start to fade if the anchor hit or fade.
     *
     * @param fadeOnDeath
     * if true the sprite will fadeout in case the anchor is removed, if false it will be instantly removed. Mostly useful if you want a long fadeout otherwise.
     */
   
    public static void objectspaceRender(SpriteAPI sprite, CombatEntityAPI anchor, Vector2f offset, Vector2f vel, Vector2f size, Vector2f growth, float angle, float spin, boolean parent, Color color, boolean additive, float fadein, float full, float fadeout, boolean fadeOnDeath) {
        sprite.setSize(size.x, size.y);
        if(parent){           
            sprite.setAngle(anchor.getFacing()+angle+90);
        } else {
            sprite.setAngle(angle+90);
        }
        sprite.setColor(color);
        if(additive){
            sprite.setAdditiveBlend();
        }
        Vector2f loc=new Vector2f(anchor.getLocation());
       
        OBJECTSPACE.add(new objectspaceData(sprite, anchor, loc, offset, vel, growth, angle, spin, parent, fadein, fadein+full, fadein+full+fadeout, fadeOnDeath, 0));
    }   
   
    /**
     *
     * @param sprite
     * SpriteAPI to render.
     *
     * @param pos
     * Positioning mode, set the point of reference, useful for UI elements. STRETCH_TO_FULLSCREEN will override the size, FULLSCREEN_MAINTAIN_RATIO will use the size as a reference and scale the sprite accordingly.
     *
     * @param loc
     * Vector2f, center in world coordinates. Ignore for fullscreen.
     *
     * @param vel
     * Vector2f() velocity of the sprite. Ignore for fullscreen.
     *
     * @param size
     * Vector2f(width, height) in pixels. size reference for FULLSCREEN_MAINTAIN_RATIO, ignore for STRETCH_TO_FULLSCREEN.
     *
     * @param growth
     * Vector2f() change of size over time in pixels/sec. Can be negative, a sprite that completely shrunk will be removed. Ignore for fullscreen.
     *
     * @param angle
     * float of the sprite's azimuth. 0 is pointing top. Ignore for fullscreen.
     *
     * @param spin
     * float of the sprite's rotation, in degree/sec. Ignore for fullscreen.
     *
     * @param color
     * Color() override, also used for fading.
     *
     * @param additive
     * boolean for additive blending.
     *
     * @param fadein
     * time in sec for fading in. Set to -1 for single frame render.
     *
     * @param full
     * time in sec at maximum opacity (clamped by color). Set to -1 for single frame render.
     *
     * @param fadeout
     * time in sec for fading out. Set to -1 for single frame render.
     */
   
    public static void screenspaceRender(SpriteAPI sprite, positioning pos, Vector2f loc, Vector2f vel, Vector2f size, Vector2f growth, float angle, float spin, Color color, boolean additive, float fadein, float full, float fadeout) {
        ViewportAPI screen = Global.getCombatEngine().getViewport();
       
        Vector2f ratio=size;
        Vector2f screenSize= new Vector2f(screen.getVisibleWidth(),screen.getVisibleHeight());
        if(pos == positioning.STRETCH_TO_FULLSCREEN){
            sprite.setSize(screenSize.x, screenSize.y);
        } else if(pos == positioning.FULLSCREEN_MAINTAIN_RATIO) {
            if(size.x/size.y > screenSize.x/screenSize.y){
                ratio = new Vector2f((size.x/size.y)/(screenSize.x/screenSize.y),1);
            } else {
                ratio = new Vector2f(1, (size.y/size.x)/(screenSize.y/screenSize.x));
                sprite.setSize(Global.getCombatEngine().getViewport().getVisibleWidth()*ratio.x,Global.getCombatEngine().getViewport().getVisibleHeight()*ratio.y);
            }
        } else {
            sprite.setSize(size.x*screen.getViewMult(), size.y*screen.getViewMult());
        }
        sprite.setAngle(angle);
        sprite.setColor(color);
        if(additive){
            sprite.setAdditiveBlend();
        }                 
       
        SCREENSPACE.add(new screenspaceData(sprite, pos, loc, vel, ratio, growth, spin, fadein, fadein+full, fadein+full+fadeout, 0));       
    } 
   
    //////////////////////////////
    //                          //
    //         MAIN LOOP        //
    //                          //
    //////////////////////////////   
   
   
    @Override
    public void renderInWorldCoords(ViewportAPI view){       
        CombatEngineAPI engine = Global.getCombatEngine();       
        if (engine == null){return;}       
       
        float amount=0;
        if(!engine.isPaused()){
            amount=engine.getElapsedInLastFrame();
        }
       
        if(!BATTLESPACE.isEmpty()){
            //iterate throught the BATTLESPACE data first:
            for(Iterator<battlespaceData> iter=BATTLESPACE.iterator(); iter.hasNext(); ){
                battlespaceData entry = iter.next();
               
                //add the time spent, that means sprites will never start at 0 exactly, but it simplifies a lot the logic
                entry.TIME+=amount;
                if(entry.TIME>entry.FADEOUT){
                    //remove expended ones
                    iter.remove();
                    continue;
                }
               
                //grow/shrink the sprite to a new size if needed
                if(entry.GROWTH!= null && entry.GROWTH!=new Vector2f()){
                    entry.SPRITE.setSize(entry.SPRITE.getWidth()+(entry.GROWTH.x*amount), entry.SPRITE.getHeight()+(entry.GROWTH.y*amount));
                    //check if the growth made the sprite too small
                    if(entry.SPRITE.getHeight()<=0 || entry.SPRITE.getWidth()<=0){                       
                        //remove sprites that completely shrunk
                        iter.remove();
                        continue;
                    }
                }
               
                //move the sprite to a new center if needed
                if(entry.VEL!= null && entry.VEL!=new Vector2f()){
                    Vector2f move = new Vector2f(entry.VEL);
                    move.scale(amount);
                    Vector2f.add(entry.LOC, move, entry.LOC);
                }

                //spin the sprite if needed
                if(entry.SPIN!=0){
                    entry.SPRITE.setAngle(entry.SPRITE.getAngle()+entry.SPIN*amount);
                }
               
                //fading stuff
                if(entry.TIME<entry.FADEIN){
                    entry.SPRITE.setAlphaMult(entry.TIME/entry.FADEIN);
                } else if(entry.TIME>entry.FULL){                   
                    entry.SPRITE.setAlphaMult(1-((entry.TIME-entry.FULL)/(entry.FADEOUT-entry.FULL)));
                } else {
                    entry.SPRITE.setAlphaMult(1);
                }
               
                //finally render that stuff
                render(new renderData(entry.SPRITE, entry.LOC));
            }
        }
       
        if(!OBJECTSPACE.isEmpty()){
            //then iterate throught the OBJECTSPACE data:
            for(Iterator<objectspaceData> iter=OBJECTSPACE.iterator(); iter.hasNext(); ){
                objectspaceData entry = iter.next();
               
                //check for possible removal on death
                if(!entry.DEATHFADE && engine.isEntityInPlay(entry.ANCHOR)){
                    iter.remove();
                    continue;
                }
               
                //check for projectile attachement fadeout
                if(entry.ANCHOR instanceof DamagingProjectileAPI){
                    //if the proj is fading or removed, offset the fadeout time to the current time
                    if (entry.TIME<entry.FULL && (
                            ((DamagingProjectileAPI)entry.ANCHOR).isFading()
                            || !engine.isEntityInPlay(entry.ANCHOR))
                            ){
                        entry.FADEOUT=(entry.FADEOUT-entry.FULL)+entry.TIME;
                    }
                }
               
                //add the time spent, that means sprites will never start at 0 exactly, but it simplifies a lot the logic
                entry.TIME+=amount;
                if(entry.TIME>entry.FADEOUT){
                    //remove expended ones
                    iter.remove();
                    continue;
                }               
               
                //grow/shrink the sprite to a new size if needed
                if(entry.GROWTH!= null && entry.GROWTH!=new Vector2f()){
                    entry.SPRITE.setSize(entry.SPRITE.getWidth()+(entry.GROWTH.x*amount), entry.SPRITE.getHeight()+(entry.GROWTH.y*amount));
                    //check if the growth made the sprite too small
                    if(entry.SPRITE.getHeight()<=0 || entry.SPRITE.getWidth()<=0){                       
                        //remove sprites that completely shrunk
                        iter.remove();
                        continue;
                    }
                }
               
                //adjust the offset if needed
                if(entry.VEL!= null && entry.VEL!=new Vector2f()){
                    Vector2f move = new Vector2f(entry.VEL);
                    move.scale(amount);
                    Vector2f.add(entry.OFFSET, move, entry.OFFSET);
                }
               
                //addjust the position and orientation
                Vector2f location = new Vector2f(entry.OFFSET); //base offset
               
                //for parenting, check if the anchor is present
                if(entry.PARENT && engine.isEntityInPlay(entry.ANCHOR)){ 
                    //if the sprite is parented, use the ANGLE to store the offset
                    if(entry.SPIN!=0){
                        entry.ANGLE+=entry.SPIN*amount;
                    }
                    entry.SPRITE.setAngle(entry.ANCHOR.getFacing()+90+entry.ANGLE);
                    //orient the offset with the facing
                    VectorUtils.rotate(location, entry.ANCHOR.getFacing(), location);
                } else {
                    //otherwise just orient the sprite
                    if(entry.SPIN!=0){
                        entry.SPRITE.setAngle(entry.SPRITE.getAngle()+entry.SPIN*amount);
                    }
                }
               
                //move the offset on the anchor
                if(engine.isEntityInPlay(entry.ANCHOR)){
                    Vector2f.add(location, entry.ANCHOR.getLocation(), location);
                    entry.LOCATION=entry.ANCHOR.getLocation();
                } else {
                    Vector2f.add(location, entry.LOCATION, location);
                }
               
                //fading stuff
                if(entry.TIME<entry.FADEIN){
                    entry.SPRITE.setAlphaMult(entry.TIME/entry.FADEIN);
                } else if(entry.TIME>entry.FULL){                   
                    entry.SPRITE.setAlphaMult(1-((entry.TIME-entry.FULL)/(entry.FADEOUT-entry.FULL)));
                } else {
                    entry.SPRITE.setAlphaMult(1);
                }
               
                //finally render that stuff
               
                render(new renderData(entry.SPRITE, location));
            }
        }
       
        if(!SCREENSPACE.isEmpty()){
            //iterate throught the BATTLESPACE data first:
           
            Vector2f center;
            ViewportAPI screen = Global.getCombatEngine().getViewport();
           
            for(Iterator<screenspaceData> iter=SCREENSPACE.iterator(); iter.hasNext(); ){
                screenspaceData entry = iter.next();
               
               
                if(entry.FADEOUT<0){
                    // SINGLE FRAME RENDERING
                    if(entry.POS == positioning.FULLSCREEN_MAINTAIN_RATIO){                   
                        center = new Vector2f(screen.getCenter());
                        entry.SPRITE.setSize(entry.SIZE.x*screen.getVisibleWidth(), entry.SIZE.y*screen.getVisibleHeight());
                    } else if(entry.POS == positioning.STRETCH_TO_FULLSCREEN){
                        center = new Vector2f(screen.getCenter());
                        entry.SPRITE.setSize(screen.getVisibleWidth(), screen.getVisibleHeight());
                    } else {
                        Vector2f refPoint=screen.getCenter();
                        switch (entry.POS){

                            case LOW_LEFT:
                                refPoint = new Vector2f(refPoint.x-(screen.getVisibleWidth()/2), refPoint.y-(screen.getVisibleHeight()/2));
                                break;

                            case LOW_RIGHT:
                                refPoint = new Vector2f(refPoint.x-(screen.getVisibleWidth()/2), refPoint.y+(screen.getVisibleHeight()/2));
                                break;

                            case UP_LEFT:
                                refPoint = new Vector2f(refPoint.x+(screen.getVisibleWidth()/2), refPoint.y-(screen.getVisibleHeight()/2));
                                break;

                            case UP_RIGHT:
                                refPoint = new Vector2f(refPoint.x+(screen.getVisibleWidth()/2), refPoint.y+(screen.getVisibleHeight()/2));
                                break;

                            default:
                        }               
                        center = new Vector2f(entry.LOC);
                        center.scale(screen.getViewMult());
                        Vector2f.add(center, refPoint, center);               
                    }

                    //finally render that stuff
                    render(new renderData(entry.SPRITE, center));
                    //and immediatelly remove
                    iter.remove();
                } else {
                    // TIMED RENDERING                   
                    //add the time spent, that means sprites will never start at 0 exactly, but it simplifies a lot the logic
                    entry.TIME+=amount;
                    if(entry.FADEOUT>0 && entry.TIME>entry.FADEOUT){
                        //remove expended ones
                        iter.remove();
                        continue;
                    }               

                    if(entry.POS == positioning.FULLSCREEN_MAINTAIN_RATIO){                   
                        center = new Vector2f(screen.getCenter());
                        entry.SPRITE.setSize(entry.SIZE.x*screen.getVisibleWidth(), entry.SIZE.y*screen.getVisibleHeight());
                    } else if(entry.POS == positioning.STRETCH_TO_FULLSCREEN){
                        center = new Vector2f(screen.getCenter());
                        entry.SPRITE.setSize(screen.getVisibleWidth(), screen.getVisibleHeight());
                    } else {
                        Vector2f refPoint=screen.getCenter();
                        switch (entry.POS){

                            case LOW_LEFT:
                                refPoint = new Vector2f(refPoint.x-(screen.getVisibleWidth()/2), refPoint.y-(screen.getVisibleHeight()/2));
                                break;

                            case LOW_RIGHT:
                                refPoint = new Vector2f(refPoint.x-(screen.getVisibleWidth()/2), refPoint.y+(screen.getVisibleHeight()/2));
                                break;

                            case UP_LEFT:
                                refPoint = new Vector2f(refPoint.x+(screen.getVisibleWidth()/2), refPoint.y-(screen.getVisibleHeight()/2));
                                break;

                            case UP_RIGHT:
                                refPoint = new Vector2f(refPoint.x+(screen.getVisibleWidth()/2), refPoint.y+(screen.getVisibleHeight()/2));
                                break;

                            default:
                        }                   

                        //move the sprite to a new center if needed
                        if(entry.VEL!= null && entry.VEL!=new Vector2f()){
                            Vector2f move = new Vector2f(entry.VEL);
                            move.scale(amount);
                            Vector2f.add(entry.LOC, move, entry.LOC);
                        }
                        center = new Vector2f(entry.LOC);
                        center.scale(screen.getViewMult());
                        Vector2f.add(center, refPoint, center);

                        //grow/shrink the sprite to a new size if needed
                        if(entry.GROWTH!= null && entry.GROWTH!=new Vector2f()){
                            entry.SIZE = new Vector2f(entry.SIZE.x+(entry.GROWTH.x*amount), entry.SIZE.y+(entry.GROWTH.y*amount));
                            //check if the growth made the sprite too small
                            if(entry.SIZE.x<=0 || entry.SIZE.y<=0){                       
                                //remove sprites that completely shrunk
                                iter.remove();
                                continue;
                            }
                        }
                        entry.SPRITE.setSize(entry.SIZE.x*screen.getViewMult(), entry.SIZE.y*screen.getViewMult());

                        //spin the sprite if needed
                        if(entry.SPIN!=0){
                            entry.SPRITE.setAngle(entry.SPRITE.getAngle()+entry.SPIN*amount);
                        }
                    }

                    //fading stuff
                    if(entry.TIME<entry.FADEIN){
                        entry.SPRITE.setAlphaMult(entry.TIME/entry.FADEIN);
                    } else if(entry.TIME>entry.FULL){                   
                        entry.SPRITE.setAlphaMult(1-((entry.TIME-entry.FULL)/(entry.FADEOUT-entry.FULL)));
                    } else {
                        entry.SPRITE.setAlphaMult(1);
                    }
                   
                    //finally render that stuff
                    render(new renderData(entry.SPRITE, center));
                    if(entry.FADEOUT<0){
                        iter.remove();
                    }
                }               
            }
        }
       
        //Single frame sprite rendering
        if(!SINGLEFRAME.isEmpty()){
            for(renderData d : SINGLEFRAME){
                render(d);
            }
            SINGLEFRAME.clear();
        }
    }
   
    //////////////////////////////
    //                          //
    //          RENDER          //
    //                          //
    //////////////////////////////
   
    private void render (renderData data){
        //where the magic happen
        SpriteAPI sprite = data.SPRITE; 
        sprite.renderAtCenter(data.LOC.x, data.LOC.y);
    }
   
    //////////////////////////////
    //                          //
    //      RENDER CLASSES      //
    //                          //
    //////////////////////////////   
   
    private static class renderData {   
        private final SpriteAPI SPRITE;
        private final Vector2f LOC;
       
        public renderData(SpriteAPI sprite, Vector2f loc) {
            this.SPRITE = sprite;
            this.LOC = loc;
        }
    }
   
    private static class battlespaceData {   
        private final SpriteAPI SPRITE;
        private Vector2f LOC;
        private final Vector2f VEL;
        private final Vector2f GROWTH;
        private final float SPIN;
        private final float FADEIN;
        private final float FULL; //fade in + full
        private final float FADEOUT; //full duration
        private float TIME;
       
        public battlespaceData(SpriteAPI sprite, Vector2f loc, Vector2f vel, Vector2f growth, float spin, float fadein, float full, float fadeout, float time) {
            this.SPRITE = sprite;
            this.LOC = loc;
            this.VEL = vel;
            this.GROWTH = growth;
            this.SPIN = spin;
            this.FADEIN = fadein;
            this.FULL = full;
            this.FADEOUT = fadeout;
            this.TIME = time;
        }
    }
   
    private static class objectspaceData {   
        private final SpriteAPI SPRITE;
        private final CombatEntityAPI ANCHOR;
        private Vector2f LOCATION;
        private Vector2f OFFSET;
        private final Vector2f VEL;
        private final Vector2f GROWTH;
        private float ANGLE;
        private final float SPIN;
        private final boolean PARENT;
        private final float FADEIN;
        private float FULL; //fade in + full
        private float FADEOUT; //full duration
        private final boolean DEATHFADE;
        private float TIME;
       
        public objectspaceData(SpriteAPI sprite, CombatEntityAPI anchor, Vector2f loc, Vector2f offset, Vector2f vel, Vector2f growth, float angle, float spin, boolean parent, float fadein, float full, float fadeout, boolean fade, float time) {
            this.SPRITE = sprite;
            this.ANCHOR = anchor;
            this.LOCATION = loc;
            this.OFFSET = offset;
            this.ANGLE = angle;
            this.VEL = vel;
            this.GROWTH = growth;
            this.SPIN = spin;
            this.PARENT = parent;
            this.FADEIN = fadein;
            this.FULL = full;
            this.FADEOUT = fadeout;
            this.DEATHFADE = fade;
            this.TIME = time;
        }
    }
       
    public static enum positioning{
        CENTER,
        LOW_LEFT,
        LOW_RIGHT,
        UP_LEFT,
        UP_RIGHT,
        STRETCH_TO_FULLSCREEN,
        FULLSCREEN_MAINTAIN_RATIO,
    }
   
    private static class screenspaceData {   
        private final SpriteAPI SPRITE;
        private final positioning POS;
        private Vector2f LOC;
        private final Vector2f VEL;
        private Vector2f SIZE;
        private final Vector2f GROWTH;
        private final float SPIN;
        private final float FADEIN;
        private final float FULL; //fade in + full
        private final float FADEOUT; //full duration
        private float TIME;
       
        public screenspaceData(SpriteAPI sprite, positioning position, Vector2f loc, Vector2f vel, Vector2f size, Vector2f growth, float spin, float fadein, float full, float fadeout, float time) {
            this.SPRITE = sprite;
            this.POS = position;
            this.LOC = loc;
            this.VEL = vel;
            this.SIZE = size;
            this.GROWTH = growth;
            this.SPIN = spin;
            this.FADEIN = fadein;
            this.FULL = full;
            this.FADEOUT = fadeout;
            this.TIME = time;
        }
    }   
}
[close]

Title: Re: The Radioactive Code Dump
Post by: Alex on December 19, 2016, 10:04:26 AM
Hey, that's *really* slick.


Which led to me looking at some of the other code, and I noticed what looks like a problem. This kind of stuff:
Code: java
int chooser=Math.round((float)Math.random()*(i-1)+0.5f);  

If I'm reading the code right, it's assuming that "(float)Math.random()" will produce results in the range [0, 1). But it'll actually - very rarely - return a 1, because of the cast. This will happen when Math.random() returns a double value that's closer to 1 than it is to the highest floating point value that's less than 1.

Since in your code there, i seems to be "size of map being selected from plus one", this code should do to generate the map key:
int chooser = (int)(Math.random() * (i - 1)) + 1;

Or just this:
int chooser = new Random().nextInt(i - 1) + 1;

Fortunately, you're getting from a map rather than an array, so it won't result in an out-of-bounds exception, but will just occasionally return null when it had a valid set of targets to choose from.
Title: Re: The Radioactive Code Dump
Post by: Tartiflette on December 19, 2016, 11:11:44 AM
Thanks, I knew there had to be a simpler way to do it but never bothered checking, will change the many instances where I have that piece of code. ::) Btw since you are here, I was about to ask in the modding questions if there is a way to get a script to run last within a frame? That would solve the one frame delay from the sprite render manager. I'm pretty sure there isn't but never hurt to ask.
Title: Re: The Radioactive Code Dump
Post by: Alex on December 19, 2016, 12:22:26 PM
Thanks, I knew there had to be a simpler way to do it but never bothered checking, will change the many instances where I have that piece of code. ::) Btw since you are here, I was about to ask in the modding questions if there is a way to get a script to run last within a frame? That would solve the one frame delay from the sprite render manager. I'm pretty sure there isn't but never hurt to ask.

Yeah, you're right, there isn't.
Title: Re: The Radioactive Code Dump
Post by: Machine on February 17, 2017, 10:07:31 PM
I'm posting an improved version of the Hard flux generation for Beam Weapons BeamEffectPlugin, as it was better than my own creation (for one thing it made me notice that I was doing dps damage per frame), but unlike what I did, it achieved its goal by adding bonus damage to shields.

This script solves that issue by modifying directly the target ships flux stats, removing the fraction of flux generated by the beam meant to be hard flux, and re-adding it as such.

However it is still not flawless, since decreaseFlux, does not discriminate between soft or hard flux, the script might just
replace the hard flux that was generated, and so in the very worst case scenario, the net result could be no overall hard flux generation from the beam weapon.
Although during testing I have not seen that happen yet, but I have to admit that the hard flux to soft flux generation, on a test ship with no dissipation, was not a perfect 50%, as the script would want you to believe, instead it favored a little bit more soft flux generation.

Spoiler
Code
package data.scripts.weapons;

import com.fs.starfarer.api.combat.BeamAPI;
import com.fs.starfarer.api.combat.BeamEffectPlugin;
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;

public class TSC_HardFluxBeamEffect implements BeamEffectPlugin
{
    public void advance(float amount, CombatEngineAPI engine, BeamAPI beam)
    {
        //Time per Frame
        float frameTime = engine.getElapsedInLastFrame();
//Hard Flux to total Flux generation
        float hFluxRatio = 0.5f;
        //Gets the beam's target
        CombatEntityAPI target = beam.getDamageTarget();
       
        //Checks if a target exist, if it is a Ship, and if shields are being hit.     
        if (target != null && target instanceof ShipAPI && target.getShield() != null && target.getShield().isWithinArc(beam.getTo()))
        {
            //Gets the beam's DPS 
            WeaponAPI weapon = beam.getWeapon();
            float dps = weapon.getDerivedStats().getDps();
           
            //Gets the target ship's shield efficiency
            ShipAPI ship = (ShipAPI)target;
            float absorption = ship.getShield().getFluxPerPointOfDamage();
 
//Modifies the target's flux stats removing part of the beam's soft flux replacing it with hard flux
            //(damage/second)*(flux/damage)*(second/frame) = (flux/frame)
            ship.getFluxTracker().decreaseFlux((dps*absorption*frameTime*(hFluxRatio)));
            ship.getFluxTracker().increaseFlux((dps*absorption*frameTime*(hFluxRatio)),true);
        }
    }
}
[close]
Title: Re: The Radioactive Code Dump
Post by: bananana on January 18, 2018, 03:27:54 PM
sup, FS.
wrote a bit of code, hope someone will find it useful.
also maybe someone could point out if i did something obviously wrong/inefficient way?

this bit turns beam weapon into a mass relay thingy.
i.e. as long as weapon fires, any fighter ship near beam will be accelerated towards the beam target.
compilation is required.
Spoiler
package data.scripts;

import com.fs.starfarer.api.combat.*;
import com.fs.starfarer.api.util.IntervalUtil;
import org.lwjgl.util.vector.Vector2f;
import org.lazywizard.lazylib.MathUtils;
import org.lazywizard.lazylib.combat.CombatUtils;
import java.util.List;
import com.fs.starfarer.api.util.Misc;
import java.util.ListIterator;

public class TF_BEAM_ACC implements BeamEffectPlugin {

   float delay = 0.05f;
   
   //@Override
    //@SuppressWarnings("AssignmentToMethodParameter")
   
   
   private IntervalUtil fireInterval = new IntervalUtil(delay, delay*3f);
   private boolean wasZero = true;
   
   public void advance(float amount, CombatEngineAPI engine, BeamAPI beam) {
      //CombatEntityAPI beamtarget = beam.getDamageTarget();
      if (//beamtarget instanceof CombatEntityAPI &&
            beam.getBrightness() >= 1f) {
         float dur = beam.getDamage().getDpsDuration();
         // needed because when the ship is in fast-time, dpsDuration will not be reset every frame as it should be
         if (!wasZero) dur = 0;
         wasZero = beam.getDamage().getDpsDuration() <= 0;
         fireInterval.advance(dur);
         
         if (fireInterval.intervalElapsed()) {   
         
               Vector2f dir = Vector2f.sub(beam.getTo(), beam.getFrom(), new Vector2f());

               if (dir.lengthSquared() > 0) dir.normalise();
               dir.scale(50f);
               Vector2f point = Vector2f.sub(beam.getTo(), dir, new Vector2f());

               float length = dir.lengthSquared();
               float area =600f;
               float empmainwidth = 10;
                        
               List<ShipAPI> targets = CombatUtils.getShipsWithinRange(MathUtils.getMidpoint(beam.getTo(), beam.getFrom()),(dir.lengthSquared()/2f));
               targets.addAll(CombatUtils.getShipsWithinRange(beam.getFrom(),area));

                ListIterator<ShipAPI> iter2 = targets.listIterator(targets.size());
                        while (iter2.hasPrevious()) {
                            CombatEntityAPI target = iter2.previous();
                            if (target instanceof ShipAPI) {
                                ShipAPI ship = (ShipAPI) target;
                                if ((ship.getCollisionClass() == CollisionClass.FIGHTER)&&(ship.getParentStation() == null)&&(area > MathUtils.getDistance(ship.getLocation(),Misc.closestPointOnLineToPoint(beam.getTo(),beam.getFrom(),ship.getLocation())))) {
                                   CombatUtils.applyForce(target, dir, 10000);
                           engine.spawnEmpArc(
                                 beam.getSource(),
                                 Misc.closestPointOnLineToPoint(beam.getTo(),beam.getFrom(),ship.getLocation()),
                                 beam.getSource(),
                                 target,
                                 DamageType.ENERGY,
                                 0, // damage
                                 0, // emp
                                 100000f, // max range
                                 "tachyon_lance_emp_impact",
                                 empmainwidth,
                                 beam.getFringeColor(),
                                 beam.getCoreColor()
                           );
                           iter2.remove();
                           continue;
                                } else {
                           iter2.remove();
                           continue;
                        }
                            }

                        }
               


         }
      }
   }   
}
[close]


feedback is welcome, especially negative feedback.
all free to use, NO credit will be welcome.


Title: Re: The Radioactive Code Dump
Post by: Twogs on May 05, 2018, 04:34:28 AM
Does the AOE script still work?

Tried it and got a crash ...
Title: Re: The Radioactive Code Dump
Post by: xenoargh on May 07, 2018, 07:04:29 PM
Did you set up an IDE and compile it?  Most of my stuff won't play nice with Janino, I'm afraid.  That said... it's 5-year-old code... doubtless it needs some updates :)
Title: Re: The Radioactive Code Dump
Post by: xenoargh on April 08, 2021, 02:11:17 PM
Damage Listener Example

Code for manipulating incoming damage under various circumstances (and a new implementation of the concept behind my Form Shield).  This is mainly an example of how to use DamageListener, but is of interest to anybody interested in the CombatListenerUtil class in general (basically, by extending that, it should be possible to write some very interesting new code for combat).

Please note that this code includes a few references to things in Rebal that don't exist in publicly-released code atm, so you'll need to make minor modifications in places to use this.

Spoiler
Code
//What this does:  scales Beam and Energy damage w/ range, so that closer ranges mean more damage for these weapon types.
package data.scripts.plugins;

import com.fs.starfarer.api.GameState;
import com.fs.starfarer.api.Global;
import com.fs.starfarer.api.combat.BeamAPI;
import com.fs.starfarer.api.combat.WeaponAPI;
import com.fs.starfarer.api.combat.CombatEngineAPI;
import com.fs.starfarer.api.combat.CombatEntityAPI;
import com.fs.starfarer.api.combat.DamageAPI;
import com.fs.starfarer.api.combat.DamageType;
import com.fs.starfarer.api.combat.DamagingProjectileAPI;
import com.fs.starfarer.api.combat.EveryFrameCombatPlugin;
import com.fs.starfarer.api.combat.ShipAPI;
import com.fs.starfarer.api.combat.ViewportAPI;
import com.fs.starfarer.api.combat.listeners.DamageDealtModifier;
import com.fs.starfarer.api.input.InputEventAPI;

import org.lazywizard.lazylib.MathUtils;
import java.util.List;
import org.lwjgl.util.vector.Vector2f;

public class BeamDamageScalar implements EveryFrameCombatPlugin {
CombatEngineAPI engine;

public static float CurrentAmount;

@Override
public void init(CombatEngineAPI cEngine) {
engine = cEngine;
}

public static float beamDamageAmount(BeamAPI beam, WeaponAPI weapon, float dps){
if(weapon == null) return dps;
if(weapon.getShip() == null) return dps;
//Fixes Beams that have multiple barrels doing absurd damage here (looking at you, Guardian).
float damMul = Math.max((float) weapon.getSpec().getTurretFireOffsets().size(),1f);
damMul *= weapon.getShip().getMutableStats().getBeamWeaponDamageMult().getMult();

//The range is now used to give additional damage output, using how close we are to the target.
//Anything less than 1/3 range is at maximum damage, here at 3X damage (but easily adjusted).
float theRangeMult = beamDistMult(beam, weapon);

return (dps * theRangeMult) / damMul;
}

public static float beamDistMult(BeamAPI beam, WeaponAPI weapon){
if(weapon == null) return 1f;
float maxRange = weapon.getRange();
maxRange *= maxRange;
//The range is now used to give additional damage output, using how close we are to the target.
//Anything less than 1/3 range is at maximum damage, here at 4X damage (but easily adjusted).
return Math.min(Math.max((maxRange / MathUtils.getDistanceSquared(beam.getRayEndPrevFrame(),beam.getFrom())),1f),3f);
}

//Gets energy-weapon damage bonus.
public static float energyDamDistMult(WeaponAPI weapon, Vector2f point){
if(weapon == null) return 1f;
float maxRange = weapon.getRange();
maxRange *= maxRange;
//The range is now used to give additional damage output, using how close we are to the target.
//Anything less than 1/3 range is at maximum damage, here at 4X damage (but easily adjusted).
return Math.min(Math.max((maxRange / MathUtils.getDistanceSquared(point,weapon.getLocation())),1f),1.5f);
}

public static float damageAfterFormShield(ShipAPI ship, DamageAPI damage, float finalDamage){
float mulDamage = finalDamage;
switch(damage.getType()){
case KINETIC:
mulDamage *= 2f;
break;
case HIGH_EXPLOSIVE:
mulDamage *= 0.5f;
break;
case FRAGMENTATION:
//Note: this is actually a BUFF, since normal Frag damage vs. shields is 0.25!
//0.5 (50% damage) got everything murdered by Mining Lasers, lol!
mulDamage *= 0.3f;
break;
default:
break;
}

mulDamage *= 0.5f * captainSkill(ship);
return mulDamage;
}

public static float captainSkill(ShipAPI ship){
return ship.getMutableStats().getShieldDamageTakenMult().getMult();
}

@Override
    public void advance(float amount, List events){
if(engine == null || engine.isPaused() || Global.getCurrentState() != GameState.COMBAT) return;
CurrentAmount = amount;

for(ShipAPI ship : engine.getShips()){
if(ship.hasListenerOfClass(HardFluxBeamMod.class) == false){
ship.addListener(new HardFluxBeamMod(ship));
}
}

for(BeamAPI beam : engine.getBeams()){
if(beam.didDamageThisFrame()){
if(beam.getDamageTarget() instanceof ShipAPI){
ShipAPI target = (ShipAPI) beam.getDamageTarget();
if(target.getShield() == null) continue;
if(target.getShield().isOn()){
if(target.getVariant().hasHullMod("shields_formshield")){
float baseDamage = beam.getDamage().computeDamageDealt(amount);
baseDamage = beamDamageAmount(beam,beam.getWeapon(),baseDamage);
baseDamage = damageAfterFormShield(target,beam.getDamage(),baseDamage);
target.getFluxTracker().increaseFlux(baseDamage, true);
FormShieldPlugin.damagedMap.add(target);
}
}
}
}
}
    }

@Override
public void renderInWorldCoords(ViewportAPI vapi) {
}

@Override
public void renderInUICoords(ViewportAPI vapi) {
}

@Override
public void processInputPreCoreControls(float f, List<InputEventAPI> list) {
}

public static class HardFluxBeamMod implements DamageDealtModifier {
protected ShipAPI ship;
public HardFluxBeamMod(ShipAPI ship) {
this.ship = ship;
}

public String modifyDamageDealt(Object param,
CombatEntityAPI target, DamageAPI damage,
Vector2f point, boolean shieldHit) {
if(param == null || target == null || damage == null) return null;
if(target instanceof ShipAPI == false) return null;
ShipAPI sTarg = (ShipAPI) target;
WeaponAPI weapon = null;

boolean hasFormShield = false;
if(sTarg.getShield() != null){
if(sTarg.getShield().isOn()){
if(sTarg.getVariant().hasHullMod("shields_formshield")){
hasFormShield = true;
}
}
}


if(param instanceof BeamAPI){
BeamAPI beam = (BeamAPI) param;
weapon = beam.getWeapon();
if(hasFormShield){
damage.getModifier().modifyPercent("hardfluxbeam", 0f);
return null;
}
if(beam == null || weapon == null) return null;
float damageMul = beamDistMult(beam,weapon);
damage.getModifier().modifyPercent("hardfluxbeam", (damageMul) * 100f);
damage.setForceHardFlux(true);
} else if(param instanceof DamagingProjectileAPI){
DamagingProjectileAPI proj = (DamagingProjectileAPI) param;
weapon = proj.getWeapon();
boolean handleNullDamage = proj == null || weapon == null;
float finalDamage = proj.getDamageAmount();
float energyDamageBuff = 1f;
//Ignores EZ-Damage weapons, because you get into recursive loops, lol.
boolean ez_exception = handleNullDamage || proj.getProjectileSpecId().equals("ez_damage_shot");
if(proj.getDamageType().equals(DamageType.ENERGY) && !ez_exception && !handleNullDamage){
energyDamageBuff = energyDamDistMult(weapon,point);
damage.getModifier().modifyPercent("hardfluxbeam", (energyDamageBuff) * 100f);
}
if(hasFormShield){
finalDamage = damageAfterFormShield(sTarg, damage, finalDamage * energyDamageBuff);
sTarg.getFluxTracker().increaseFlux(finalDamage, true);
FormShieldPlugin.damagedMap.add(sTarg);
damage.setDamage(0f);
}
//Projectile explodes and is removed.
AAA_Weapon_FX_Plugin.explodingProjectiles.add(proj);
} else {
if(hasFormShield){
sTarg.getFluxTracker().increaseFlux(damage.getDamage()*0.5f*captainSkill(sTarg), true);
damage.getModifier().modifyPercent("hardfluxbeam", 0f);
return null;
}
}
return null;
}
}
}
}
[close]
Title: Re: The Radioactive Code Dump
Post by: xenoargh on April 09, 2021, 07:18:40 PM
Revised this work above.  There were a couple of exceptional circumstances it didn't cover, mainly involving damage of various odd types, like direct damage via code, etc., that needed fixing.
Title: Re: The Radioactive Code Dump
Post by: Anexgohan on February 06, 2022, 12:33:56 AM
Yet another missile AI, this time with all the knobs and switches you need to customize it anyway you want. No Java skill needed, but it require to be compiled.
Customizable features:
  • ECCM dependent target leading.
  • Different target selection priorities.
  • Option to reengage a new target when the current one got killed.
  • Controllable vanilla-like waving.
  • Oversteering behavior that tries to orient the velocity toward the leading point instead of just pointing the missile at it.
Tested on a hundred missiles at once with no noticeable performance impact.

Spoiler
Code: Java
//By Tartiflette, highly customizable Missile AI.
package data.scripts.ai;

import com.fs.starfarer.api.Global;
import com.fs.starfarer.api.combat.CombatEngineAPI;
import com.fs.starfarer.api.combat.CombatEntityAPI;
import com.fs.starfarer.api.combat.GuidedMissileAI;
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 java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
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.CombatUtils;
import org.lwjgl.util.vector.Vector2f;

public class CustomMissileAI implements MissileAIPlugin, GuidedMissileAI {
         
   
    //////////////////////
    //     SETTINGS     //
    //////////////////////
   
    //Angle with the target beyond which the missile turn around without accelerating. Avoid endless circling.
    //  Set to a negative value to disable
    private final float OVERSHOT_ANGLE=60;
   
    //Time to complete a wave in seconds.
    private final float WAVE_TIME=2;
   
    //Angle of the waving in degree (divided by 3 with ECCM). Set to a negative value to avoid all waving.
    private final float WAVE_AMPLITUDE=15;
   
    //Damping of the turn speed when closing on the desired aim. The smaller the snappier.
    private final float DAMPING=0.1f;
   
    //Does the missile try to correct it's velocity vector as fast as possible or just point to the desired direction and drift a bit?
    //  Can create strange results with large waving
    //  Require a projectile with a decent turn rate and around twice that in turn acceleration compared to their top speed
    //  Useful for slow torpedoes with low forward acceleration, or ultra precise anti-fighter missiles.     
    private final boolean OVERSTEER=false;  //REQUIRE NO OVERSHOOT ANGLE!
   
    //Does the missile switch its target if it has been destroyed?
    private final boolean TARGET_SWITCH=true;
   
    //Does the missile find a random target or aways tries to hit the ship's one?     
    //   0: No random target seeking,
    //       If the launching ship has a valid target, the missile will pursue that one.
    //       If there is no target selected, it will check for an unselected cursor target.
    //       If there is none, it will pursue its closest valid threat.   
    //   1: Local random target,
    //       If the ship has a target selected, the missile will pick a random valid threat around that one.
    //       If the ship has none, the missile will pursue a random valid threat around the cursor, or itself in AI control.   
    //   2: Full random,
    //       The missile will always seek a random valid threat around itself.
    private final Integer RANDOM_TARGET=0;
   
    //Both targeting behavior can be false for a missile that always get the ship's target or the closest one
    //Prioritize hitting fighters and drones (if false, the missile will still be able to target fighters but not drones)
    private final boolean ANTI_FIGHTER=false;    //INCOMPATIBLE WITH ASSAULT
   
    //Target the biggest threats first
    private final boolean ASSAULT=true;  //INCOMPATIBLE WITH ANTI-FIGHTER
   
    //range in which the missile seek a target in game units.
    private final float MAX_SEARCH_RANGE = 1500;
   
    //range under which the missile start to get progressively more precise in game units.
    private float PRECISION_RANGE=500;
   
    //Is the missile lead the target or tailchase it?
    private final boolean LEADING=true;
   
    //Leading loss without ECCM hullmod. The higher, the less accurate the leading calculation will be.
    //   1: perfect leading with and without ECCM
    //   2: half precision without ECCM
    //   3: a third as precise without ECCM. Default
    //   4, 5, 6 etc : 1/4th, 1/5th, 1/6th etc precision.
    private float ECCM=3;   //A VALUE BELOW 1 WILL PREVENT THE MISSILE FROM EVER HITTING ITS TARGET!
   
   
    //////////////////////
    //    VARIABLES     //
    //////////////////////
   
    //max speed of the missile after modifiers.
    private final float MAX_SPEED;
    //Max range of the missile after modifiers.
    private final float MAX_RANGE;
    //Random starting offset for the waving.
    private final float OFFSET;
    private CombatEngineAPI engine;
    private final MissileAPI MISSILE;
    private CombatEntityAPI target;
    private Vector2f lead = new Vector2f();
    private boolean launch=true;
    private float timer=0, check=0f;

    //////////////////////
    //  DATA COLLECTING //
    //////////////////////
   
    public CustomMissileAI(MissileAPI missile, ShipAPI launchingShip) {
        this.MISSILE = missile;
        MAX_SPEED = missile.getMaxSpeed();
        MAX_RANGE = missile.getWeapon().getRange();
        if (missile.getSource().getVariant().getHullMods().contains("eccm")){
            ECCM=1;
        }       
        //calculate the precision range factor
        PRECISION_RANGE=(float)Math.pow((2*PRECISION_RANGE),2);
        OFFSET=(float)(Math.random()*Math.PI*2);
    }
   
    //////////////////////
    //   MAIN AI LOOP   //
    //////////////////////
   
    @Override
    public void advance(float amount) {
       
        if (engine != Global.getCombatEngine()) {
            this.engine = Global.getCombatEngine();
        }
       
        //skip the AI if the game is paused, the missile is engineless or fading
        if (Global.getCombatEngine().isPaused() || MISSILE.isFading() || MISSILE.isFizzling()) {return;}
       
        //assigning a target if there is none or it got destroyed
        if (target == null
                || target.getOwner()==MISSILE.getOwner()
                || (TARGET_SWITCH && (target instanceof ShipAPI && ((ShipAPI) target).isHulk())
                                  || !engine.isEntityInPlay(target)
                   )
                ){
            setTarget(assignTarget(MISSILE));
            //forced acceleration by default
            MISSILE.giveCommand(ShipCommand.ACCELERATE);
            return;
        }
       
        timer+=amount;
        //finding lead point to aim to       
        if(launch || timer>=check){
            launch=false;
            timer -=check;
            //set the next check time
            check = Math.min(
                    0.25f,
                    Math.max(
                            0.03f,
                            MathUtils.getDistanceSquared(MISSILE, target)/PRECISION_RANGE)
            );
            if(LEADING){
                //best intercepting point
                lead = AIUtils.getBestInterceptPoint(
                        MISSILE.getLocation(),
                        MAX_SPEED*ECCM, //if eccm is intalled the point is accurate, otherwise it's placed closer to the target (almost tailchasing)
                        target.getLocation(),
                        target.getVelocity()
                );               
                //null pointer protection
                if (lead == null) {
                    lead = target.getLocation();
                }
            } else {
                lead = target.getLocation();
            }
        }
       
        //best velocity vector angle for interception
        float correctAngle = VectorUtils.getAngle(
                        MISSILE.getLocation(),
                        lead
                );
       
        if (OVERSTEER){
            //velocity angle correction
            float offCourseAngle = MathUtils.getShortestRotation(
                    VectorUtils.getFacing(MISSILE.getVelocity()),
                    correctAngle
                    );

            float correction = MathUtils.getShortestRotation(               
                    correctAngle,
                    VectorUtils.getFacing(MISSILE.getVelocity())+180
                    )
                    * 0.5f * //oversteer
                    (float)((Math.sin(Math.PI/90*(Math.min(Math.abs(offCourseAngle),45))))); //damping when the correction isn't important

            //modified optimal facing to correct the velocity vector angle as soon as possible
            correctAngle = correctAngle+correction;
        }
       
        if(WAVE_AMPLITUDE>0){           
            //waving
            float multiplier=1;
            if(ECCM<=1){
                multiplier=0.3f;
            }
            correctAngle+=multiplier*WAVE_AMPLITUDE*check*Math.cos(OFFSET+MISSILE.getElapsed()*(2*Math.PI/WAVE_TIME));
        }
       
        //target angle for interception       
        float aimAngle = MathUtils.getShortestRotation( MISSILE.getFacing(), correctAngle);
       
        if(OVERSHOT_ANGLE<=0 || Math.abs(aimAngle)<OVERSHOT_ANGLE){
            MISSILE.giveCommand(ShipCommand.ACCELERATE); 
        }
       
        if (aimAngle < 0) {
            MISSILE.giveCommand(ShipCommand.TURN_RIGHT);
        } else {
            MISSILE.giveCommand(ShipCommand.TURN_LEFT);
        } 
       
        // Damp angular velocity if the missile aim is getting close to the targeted angle
        if (Math.abs(aimAngle) < Math.abs(MISSILE.getAngularVelocity()) * DAMPING) {
            MISSILE.setAngularVelocity(aimAngle / DAMPING);
        }
    }
   
    //////////////////////
    //    TARGETING     //
    //////////////////////
   
    public CombatEntityAPI assignTarget(MissileAPI missile){
       
        ShipAPI theTarget=null;       
        ShipAPI source = missile.getSource();       
        ShipAPI currentTarget;
       
        //check for a target from its source
        if(source != null
                && source.getShipTarget() != null
                && source.getShipTarget() instanceof ShipAPI
                && source.getShipTarget().getOwner() != missile.getOwner()
                ){
            currentTarget=source.getShipTarget();
        } else {
            currentTarget=null;
        }
       
        //random target selection
        if (RANDOM_TARGET>0){ 
            //random mode 1: the missile will look for a target around itsef
            Vector2f location = missile.getLocation();   
            //random mode 2: if its source has a target selected, it will look for random one around that point
            if( RANDOM_TARGET<2){                                     
                if(currentTarget != null
                        && currentTarget.isAlive()
                        && MathUtils.isWithinRange(missile, currentTarget, MAX_RANGE)
                        ){
                    location = currentTarget.getLocation();
                } else if (source != null
                        && source.getMouseTarget()!=null){
                    location=source.getMouseTarget();
                }
            }
            //fetch the right kind of target
            if(ANTI_FIGHTER){
                theTarget = getRandomFighterTarget(location);
            } else if(ASSAULT){
                theTarget = getRandomLargeTarget(location);
            } else {
                theTarget = getAnyTarget(location);
            }   
        //non random targeting   
        } else {
            if(source!=null){
                //ship target first
                if(currentTarget!=null
                        && currentTarget.isAlive()
                        && currentTarget.getOwner()!=missile.getOwner()
                        && !(ANTI_FIGHTER && !(currentTarget.isDrone() && currentTarget.isFighter()))
                        && !(ASSAULT && (currentTarget.isDrone() || currentTarget.isFighter()))
                        ){
                    theTarget=currentTarget;               
                } else {
                    //or cursor target if there isn't one
                    List<ShipAPI> mouseTargets = CombatUtils.getShipsWithinRange(source.getMouseTarget(), 100f);
                    if (!mouseTargets.isEmpty()) {
                        Collections.sort(mouseTargets, new CollectionUtils.SortEntitiesByDistance(source.getMouseTarget()));
                        for (ShipAPI tmp : mouseTargets) {
                            if (tmp.isAlive()
                                    && tmp.getOwner() != missile.getOwner()
                                    && !(ANTI_FIGHTER && !(tmp.isDrone() && tmp.isFighter()))
                                    && !(ASSAULT && (tmp.isDrone() || tmp.isFighter() || tmp.isFrigate()))
                                    ) {
                                theTarget=tmp;
                                break;
                            }
                        }
                    }               
                }
            }
            //still no valid target? lets try the closest one
            //most of the time a ship will have a target so that doesn't need to be perfect.
            if(theTarget==null){
                List<ShipAPI> closeTargets = AIUtils.getNearbyEnemies(missile, MAX_SEARCH_RANGE);
                if (!closeTargets.isEmpty()) {
                    Collections.sort(closeTargets, new CollectionUtils.SortEntitiesByDistance(missile.getLocation()));
                    if (ASSAULT){   //assault missiles will somewhat prioritize toward bigger threats even if there is a closer small one, and avoid fighters and drones.
                        for (ShipAPI tmp : closeTargets) {
                            if (tmp.isAlive()
                                    && tmp.getOwner() != missile.getOwner()
                                    ) {
                                if (tmp.isCapital() || tmp.isCruiser()){
                                    theTarget=tmp;
                                    break;
                                } else if (tmp.isDestroyer() && Math.random()>0.5){
                                    theTarget=tmp;
                                    break;
                                } else if (tmp.isDestroyer() && Math.random()>0.75){
                                    theTarget=tmp;
                                    break;
                                } else if (!tmp.isDrone() && !tmp.isFighter() && Math.random()>0.95){
                                    theTarget=tmp;
                                    break;
                                }
                            }
                        }
                    }else if(ANTI_FIGHTER){    //anti-fighter missile will target the closest drone or fighter
                        for (ShipAPI tmp : closeTargets) {
                            if (tmp.isAlive()
                                    && tmp.getOwner() != missile.getOwner()
                                    && (tmp.isDrone() || tmp.isFighter())
                                    ) {
                                theTarget=tmp;
                                break;
                            }
                        }
                    }else{  //non assault, non anti-fighter missile target the closest non-drone ship
                        for (ShipAPI tmp : closeTargets) {
                            if (tmp.isAlive()
                                    && tmp.getOwner() != missile.getOwner()
                                    && !tmp.isDrone()
                                    ) { 
                                theTarget=tmp;
                                break;
                            }
                        }
                    }
                }
            }
        }       
        return theTarget;
    }
   
    //Random picker for fighters and drones
    public ShipAPI getRandomFighterTarget(Vector2f location){
        ShipAPI select=null;
        Map<Integer, ShipAPI> PRIORITYLIST = new HashMap<>();
        Map<Integer, ShipAPI> OTHERSLIST = new HashMap<>();
        int i=1, u=1;
        List<ShipAPI> potentialTargets = CombatUtils.getShipsWithinRange(location, MAX_RANGE);
        if (!potentialTargets.isEmpty()) {
            for (ShipAPI tmp : potentialTargets) {
                if (tmp.isAlive()
                        && tmp.getOwner() != MISSILE.getOwner()
                        ) {
                    if (tmp.isFighter() || tmp.isDrone()){
                        PRIORITYLIST.put(i, tmp);
                        i++;
                    } else {                           
                        OTHERSLIST.put(u, tmp);
                        u++;
                    }
                }
            }
            if (!PRIORITYLIST.isEmpty()){
                int chooser=Math.round((float)Math.random()*(i-1)+0.5f);
                select=PRIORITYLIST.get(chooser);
            } else if (!OTHERSLIST.isEmpty()){                   
                int chooser=Math.round((float)Math.random()*(u-1)+0.5f);
                select=OTHERSLIST.get(chooser);
            }
        }
        return select;
    }
   
    //Random target selection strongly weighted toward bigger threats in range
    public ShipAPI getRandomLargeTarget(Vector2f location){
        ShipAPI select=null;
        Map<Integer, ShipAPI> PRIORITY1 = new HashMap<>();
        Map<Integer, ShipAPI> PRIORITY2 = new HashMap<>();
        Map<Integer, ShipAPI> PRIORITY3 = new HashMap<>();
        Map<Integer, ShipAPI> PRIORITY4 = new HashMap<>();
        Map<Integer, ShipAPI> OTHERSLIST = new HashMap<>();
        int i=1, u=1, v=1, x=1, y=1;
        List<ShipAPI> potentialTargets = CombatUtils.getShipsWithinRange(location, MAX_RANGE);
        if (!potentialTargets.isEmpty()) {
            for (ShipAPI tmp : potentialTargets) {
                if (tmp.isAlive()
                        && tmp.getOwner() != MISSILE.getOwner()
                        && !tmp.isDrone()
                        ) {
                    if (tmp.isCapital()){
                        PRIORITY1.put(i, tmp);
                        i++;
                        PRIORITY2.put(u, tmp);
                        u++;
                        PRIORITY3.put(x, tmp);
                        x++;
                        PRIORITY4.put(v, tmp);
                        v++;
                        OTHERSLIST.put(y, tmp);
                        y++;
                    } else if (tmp.isCruiser()){
                        PRIORITY2.put(u, tmp);
                        u++;
                        PRIORITY3.put(x, tmp);
                        x++;
                        PRIORITY4.put(v, tmp);
                        v++;
                        OTHERSLIST.put(y, tmp);
                        y++;
                    } else if (tmp.isDestroyer()){
                        PRIORITY3.put(x, tmp);
                        x++;
                        PRIORITY4.put(v, tmp);
                        v++;
                        OTHERSLIST.put(y, tmp);
                        y++;
                    } else if (tmp.isFrigate()){
                        PRIORITY4.put(v, tmp);
                        v++;
                        OTHERSLIST.put(y, tmp);
                        y++;
                    } else {
                        OTHERSLIST.put(y, tmp);
                        y++;
                    }
                }
            }
            if (!PRIORITY1.isEmpty() && Math.random()>0.8f){
                int chooser=Math.round((float)Math.random()*(i-1)+0.5f);
                select=PRIORITY1.get(chooser);
            } else if (!PRIORITY2.isEmpty() && Math.random()>0.8f){
                int chooser=Math.round((float)Math.random()*(u-1)+0.5f);
                select=PRIORITY2.get(chooser);
            } else if (!PRIORITY3.isEmpty() && Math.random()>0.8f){
                int chooser=Math.round((float)Math.random()*(x-1)+0.5f);
                select=PRIORITY3.get(chooser);
            } else if (!PRIORITY4.isEmpty() && Math.random()>0.8f){
                int chooser=Math.round((float)Math.random()*(v-1)+0.5f);
                select=PRIORITY4.get(chooser);
            } else if (!OTHERSLIST.isEmpty()){                   
                int chooser=Math.round((float)Math.random()*(y-1)+0.5f);
                select=OTHERSLIST.get(chooser);
            }
        }
        return select;
    }

    //Pure random target picker
    public ShipAPI getAnyTarget(Vector2f location){
        ShipAPI select=null;
        Map<Integer, ShipAPI> TARGETLIST = new HashMap<>();
        int i=1;
        List<ShipAPI> potentialTargets = CombatUtils.getShipsWithinRange(location, MAX_RANGE);
        if (!potentialTargets.isEmpty()) {
            for (ShipAPI tmp : potentialTargets) {
                if (tmp.isAlive()
                        && tmp.getOwner() != MISSILE.getOwner()
                        && !tmp.isDrone()
                        ){
                    TARGETLIST.put(i, tmp);
                    i++;                       
                }
            }
            if (!TARGETLIST.isEmpty()){
                int chooser=Math.round((float)Math.random()*(i-1)+0.5f);
                select=TARGETLIST.get(chooser);
            }
        }
        return select;
    }
   
    @Override
    public CombatEntityAPI getTarget() {
        return target;
    }

    @Override
    public void setTarget(CombatEntityAPI target) {
        this.target = target;
    }
   
    public void init(CombatEngineAPI engine) {}
}
[close]

Hey, everyone,
I was looking at your forum post here

I'm making some missiles for the game and would love to implement this for my missiles, sadly I have zero coding background could you give me any pointers if it's possible for someone like me to implement this as you said "no java skills needed"
thankyou for reading
Title: Re: The Radioactive Code Dump
Post by: 6chad.noirlee9 on February 06, 2022, 06:36:45 AM
THIS IS SO COOL
i have been hoping for years someone would make a total conversion that involves starting with a group of soldiers or something and upgrade to trucks and tanks and boats and planes and choppers
then perhaps upgrading to actual space ships


the other wish from the list is havin a way to have an actual battle to conquer (in nexerelin) colonies