Anyone know how I could interface with ShieldAPI and change shield color?
Is there a way I could add particle effects OnHit? Like creating two plumes of particles while a laser beam cuts into the hull of a ship.
A) ShipAPI > getShield()
ShieldAPI > setInnerColor
ShieldAPI > setRingColor
B) Plumes over time require a EveryFrameCombatPlugin in addition to your OnHit. The onHit registers the location of the hit with the EveryFrameCOmbatPlugin and the Every Frame handles the creation, flow, color, and removal of the particle plume.
Mind you laser beams are something different to laser beams, but could be used similarly. See BeamEffectPlugin.
Here is an example of my over penetration script
/*Armour Penetration / Shield Ricochet script by Xangle13, updated by Debido
* This script allows a missile/projectile to simulate certain behaviors
* 1. Successfully hitting projectiles will pass through and exit the opposite side of a ship if the armour is low enough
* 2. The projectile will bounce off the enemies shield if it hits at an oblique enough angle
*/
/*
This is the instant pass through variant, projectile(s) will only 'pass through' if the exit side has armour that is compromised.
Dev version, includes pop up text.
*/
package data.scripts.weapons;
import com.fs.starfarer.api.combat.ArmorGridAPI;
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.awt.Color;
import org.lazywizard.lazylib.CollisionUtils;
import org.lazywizard.lazylib.FastTrig;
import org.lazywizard.lazylib.MathUtils;
import org.lazywizard.lazylib.combat.DefenseUtils;
import org.lwjgl.util.vector.Vector2f;
public class diableavionics_ShapedCharge_b implements OnHitEffectPlugin {
private Vector2f particleVelocity1;
private Vector2f particleVelocity2;
private static final Color particleColor = new Color(245, 48, 58, 225);
private static final float particleSize = 2.5f;
private static final float particleBrightness = 1.28f;
private static final float particleDuration = 1.45f;
private static final float explosionSize = 120f;
private static final float explosionSize2 = 60f;
private static final float explosionDuration = 0.33f;
private static final float explosionDuration2 = 0.9f;
private static final boolean dealsSoftFlux = false;
public void onHit(DamagingProjectileAPI projectile, CombatEntityAPI target, Vector2f point, boolean shieldHit, CombatEngineAPI engine) {
float ENTER_FACTOR = 0.1f;
float EXIT_FACTOR = 0.2f;
//if the target is a ship, that is to say it will not work on asteroids
if (target instanceof ShipAPI) {
ShipAPI sourceShip = projectile.getSource();
//check the source ship is not a null entity
if (sourceShip != null) {
//get the weapon that fired the projectile
WeaponAPI weaponFrom = projectile.getWeapon();
//if the weapon that fired the projectile is not null
if (weaponFrom != null) {
//get the projectile location and velocity. Location currently not used
Vector2f projectileVelocity = projectile.getVelocity();
//get the angle at which the projectile is hitting the ship
float fromAngle = projectile.getFacing();
//get the damage amount
float damageAmount = projectile.getDamageAmount();
//get the emp amount
float empAmount = projectile.getEmpAmount();
//get the damage type
DamageType damageType = projectile.getDamageType();
if (shieldHit) {
engine.applyDamage(target, point, damageAmount * (float) (Math.random() * 0.25 + 0.25), damageType, empAmount, false, false, null);
} else {
// get the targets armor grid
ArmorGridAPI targetArmorGrid = ((ShipAPI) target).getArmorGrid();
//get the armor rating
float targetArmorRating = targetArmorGrid.getArmorRating();
//get the armor level of the cell where the projectile is hitting
float targetArmorLevel = DefenseUtils.getArmorLevel((ShipAPI) target, point) * (targetArmorRating / 15);
//calculate the penetration value against the current armor level at the hit point using 0.1f as a fudge factor.
float penetrateValue = (damageAmount * ENTER_FACTOR) - targetArmorLevel;
if ((penetrateValue > 0) && (targetArmorLevel >= 0) && ((float) Math.random() <= (penetrateValue / targetArmorLevel))) {
Vector2f penetrateVelocity = new Vector2f(projectileVelocity.getX(), projectileVelocity.getY());
penetrateVelocity = penetrateVelocity.normalise(penetrateVelocity);
Vector2f penetrateLocation = new Vector2f(point.getX() + penetrateVelocity.getX() * 50f, point.getY() + penetrateVelocity.getY() * 50f);
//project the pentration vector through the ship to an imaginary point beyond the ship on the opposite side
float projectedLocationX = (float) (1200f * FastTrig.cos(Math.toRadians(projectile.getFacing())) + penetrateLocation.x);
float projectedLocationY = (float) (1200f * FastTrig.sin(Math.toRadians(projectile.getFacing())) + penetrateLocation.y);
//location as vector2f
Vector2f projectedLocation = new Vector2f(projectedLocationX, projectedLocationY);
//derive exit location with Lazylib collision utils. This basically uses the imaginary point and the hit point, then checks every segment of the hit boundary segments for an intersect then return the Vector2f value of the closest point where the the two line and the boundary segment meet
//using this vector direction it will use the farthest point on the ship for the exit
//this method will consistently get the outside boundary
//Vector2f exitLocation = CollisionUtils.getCollisionPoint(projectedLocation, penetrateLocation, target);
//use this vector direction to determine if it should exit at the nearest point
//this method does not consistently get the nearest point due to issues probably with other authors ship boundaries.
Vector2f exitLocation = CollisionUtils.getCollisionPoint(penetrateLocation, projectedLocation, target);
if (null != exitLocation) {
float passThroughShipDistance = MathUtils.getDistance(point, exitLocation);
//So let's check IF the imaginary projectile can get 'through' the full distance of internal hull, let's use a value of 1000px, a very very big ship! ie. Zorg
if (passThroughShipDistance > 1000f) {
//apply bonus RNG damage to entry point.
engine.applyDamage(target, point, damageAmount, DamageType.HIGH_EXPLOSIVE, empAmount, false, dealsSoftFlux, projectile.getSource());
//we're done, no exit
return;
}
//Now let's check how strong the armor is on the exit location, and can we penetrate the otherside
float targetArmorExitLevel = DefenseUtils.getArmorLevel((ShipAPI) target, exitLocation) * (targetArmorRating / 15);
float penetrateExitValue = (damageAmount * EXIT_FACTOR) - targetArmorExitLevel;
if ((penetrateExitValue > 0) && (targetArmorExitLevel >= 0) && ((float) Math.random() <= (penetrateExitValue / targetArmorExitLevel))) {
//let's do a simple test if the distance through the ship, we have two versions
// if (passThroughShipDistance > projectile.getVelocity().length() || (passThroughShipDistance - projectile.getVelocity().length()) < 100f){
// float newHull = ((ShipAPI)target).getHitpoints() - projectile.getDamageAmount() * 0.5f;
// ((ShipAPI)target).setHitpoints(newHull);
// return;
// }
float passThroughShipTime = passThroughShipDistance / projectile.getVelocity().length();
APDebrisPlugin.startFire(
target,
exitLocation,
projectile.getDamageAmount() * 0.1f,
5f,
projectile.getSource(),
fromAngle,
passThroughShipTime,
projectile
);
APDelayedProjectilePlugin.startFire(
target,
exitLocation,
projectile.getDamageAmount(),
projectile.getSource(),
fromAngle, passThroughShipTime,
projectile.getWeapon().getId(),
projectile.getVelocity().length(),
projectile
);
//engine.applyDamage(target, point, damageAmount * (float) (Math.random() * 1 + 1), damageType, empAmount, true, true, null);
engine.addFloatingText(penetrateLocation, "Penetrated", 30, new Color(255, 0, 0, 255), target, 1f, 2f);
damageAmount += (50f * Math.random());
engine.applyDamage(target, point, damageAmount, DamageType.HIGH_EXPLOSIVE, empAmount, false, dealsSoftFlux, projectile.getSource());
} else {
// So if the shaped charge cannot penetrate all the way through, it will start causing damage on the opposite side of the ship.
damageAmount += (50f * Math.random());
engine.applyDamage(target, exitLocation, damageAmount, DamageType.HIGH_EXPLOSIVE, empAmount, true, dealsSoftFlux, projectile.getSource());
}
//There are some strange cases where where the algorithm to find the exit location will not return one, this else statement captures that
} else {
engine.spawnExplosion(penetrateLocation, new Vector2f(penetrateVelocity.getX() * 250f, penetrateVelocity.getY() * 250f), new Color(225, 200, 50, 200), damageAmount / 70f, 0.5f);
//
for (int i = 0; i < (int) (damageAmount / 5); i++) {
engine.addHitParticle(penetrateLocation, new Vector2f(penetrateVelocity.getX() * 300f + (float) (Math.random() * 100 - 30), penetrateVelocity.getY() * 500f + (float) (Math.random() * 100 - 30)), damageAmount / 50, 0.5f, 3f, new Color(225, 200, 50, 170));//Color(225,200,50,200)
}
//spawn exit projectile(s) this will spawn 10 projectiles, there is not need for a loop if you just want to spawn one projectile
for (int i = 0; i < 3; i++) {
CombatEntityAPI projectileSpawned = engine.spawnProjectile(null, null, "diableavionics_thunderboltcharge", penetrateLocation, fromAngle + ((float) Math.random() * 20f - 7f), null);
((DamagingProjectileAPI) projectileSpawned).setSource((ShipAPI) target);
}
//engine.applyDamage(target, point, damageAmount * (float) (Math.random() * 1 + 1), damageType, empAmount, true, true, null);
engine.addFloatingText(penetrateLocation, "Bypass", 30, new Color(255, 0, 0, 255), target, 1f, 2f);
}
}
}
}
}
}
}
}
The important part here is the call here
APDebrisPlugin.startFire(
target,
exitLocation,
projectile.getDamageAmount() * 0.1f,
5f,
projectile.getSource(),
fromAngle,
passThroughShipTime,
projectile
);
This passes through the target, and location where it hit and so on. Then this is handled by the EveryFrame
package data.scripts.weapons;
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 java.awt.Color;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.lazywizard.lazylib.FastTrig;
import org.lazywizard.lazylib.MathUtils;
import org.lazywizard.lazylib.combat.entities.AnchoredEntity;
import org.lwjgl.util.vector.Vector2f;
// TODO: Check for nearby debriss and merge them for better performance
public class APDebrisPlugin implements EveryFrameCombatPlugin {
// How long between damage/particle effect ticks
private static final float TIME_BETWEEN_DAMAGE_TICKS = .2f;
private static final float TIME_BETWEEN_PARTICLE_TICKS = .33f;
// Stores the currently DEBRIS_MAP debriss
// Having the Set backed by a WeakHashMap helps prevent memory leaks
private static final List<DebrisData> DEBRIS_MAP = new ArrayList<>();
private CombatEngineAPI engine;
private float lastDamage, lastParticle;
@Override
public void advance(float amount, List events) {
if (engine != Global.getCombatEngine()) {
this.engine = Global.getCombatEngine();
}
if (engine.isPaused() || DEBRIS_MAP.isEmpty()) {
return;
}
lastDamage += amount;
lastParticle += amount;
float damageMod = lastDamage;
boolean dealDamage = false;
if (lastDamage >= TIME_BETWEEN_DAMAGE_TICKS) {
lastDamage -= TIME_BETWEEN_DAMAGE_TICKS;
dealDamage = true;
}
boolean showParticle = false;
if (lastParticle >= TIME_BETWEEN_PARTICLE_TICKS) {
lastParticle -= TIME_BETWEEN_PARTICLE_TICKS;
showParticle = true;
}
// Deal debris damage for all actively DEBRIS_MAP projectiles
for (Iterator iter = DEBRIS_MAP.iterator(); iter.hasNext();) {
DebrisData debris = (DebrisData) iter.next();
// Check if the debris has gone out
if (engine.getTotalElapsedTime(false) >= debris.expiration
|| !engine.isEntityInPlay(debris.getAnchor())) {
iter.remove();
} else if (engine.getTotalElapsedTime(false) < debris.start) {
continue;
} else {
if (dealDamage) {
engine.applyDamage(debris.getAnchor(), debris.getLocation(),
debris.dps * damageMod, DamageType.FRAGMENTATION,
debris.dps * damageMod, true, true, debris.source);
}
// Draw smoke effect to show where the debris is DEBRIS_MAP
float intensity = debris.getRemainingDuration() / debris.getDuration();
float intensity2 = debris.getRemainingDuration() / (debris.getDuration() * debris.getDuration());
if (showParticle) {
Vector2f particleVelocity = new Vector2f(intensity2 * (debris.getDebrisVector().x),
//* 5f
//* debris.dps
//* debris.getRemainingDuration()
//add the x velocity of the host ship
//+ debris.getAnchor().getVelocity().x
//add some randomness
//+ MathUtils.getRandomNumberInRange(-25f, 25f)),
intensity2 * (debris.getDebrisVector().y)
//* 5f
//* debris.dps
//* debris.getRemainingDuration()
//add the y velocity of the host ship
//+ debris.getAnchor().getVelocity().y
////add some randomness
// + MathUtils.getRandomNumberInRange(-25f, 25f))
);
engine.addSmokeParticle(debris.getLocation(), // Location
particleVelocity, // Velocity
MathUtils.getRandomNumberInRange(20f, 40f), // Size
MathUtils.getRandomNumberInRange(.05f, .15f), // Brightness
2.2f, Color.DARK_GRAY); // Duration, color
// engine.spawnExplosion(debris.getLocation(), //location
// particleVelocity,//velocity
// Color.yellow, //colour
// MathUtils.getRandomNumberInRange(2f, 5f), //size
// MathUtils.getRandomNumberInRange(.01f, .05f)); //duration
}
//spawn debris from exit hole
for (int i = 0; i < 2; i++) {
Vector2f debrisVelocity = new Vector2f(1200f * intensity2 * debris.getDebrisVector().x
//* 5f
//* debris.dps
//* debris.getRemainingDuration()
//add the x velocity of the host ship
+ debris.getAnchor().getVelocity().x,
//add some randomness
//+ MathUtils.getRandomNumberInRange(intensity * -15f, intensity * 15f),
1200f * intensity2 * debris.getDebrisVector().y
//* 5f
//* debris.dps
//* debris.getRemainingDuration()
//add the y velocity of the host ship
+ debris.getAnchor().getVelocity().y
////add some randomness
//+ MathUtils.getRandomNumberInRange(intensity * -15f, intensity * 15f)
);
float tempKelvin = intensity * 4800f;
Color debrisColor = KelvinCalc.getRGB(tempKelvin);
engine.addHitParticle(
debris.getLocation(), //location,
debrisVelocity, //velocity
MathUtils.getRandomNumberInRange(1f, 4f) * debris.getRemainingDuration(), //size
intensity, debris.getRemainingDuration() * 0.11f, //, new Color(225, 200, 50, 200)
debrisColor
);
}
}
}
}
public static void startFire(CombatEntityAPI target,
Vector2f hitLoc,
float totalDamage,
float burnDuration,
CombatEntityAPI source,
float debrisAngle,
float delay,
DamagingProjectileAPI projectile
) {
// TODO: merge with nearby debriss on the same target
DEBRIS_MAP.add(new DebrisData(target, hitLoc, totalDamage, burnDuration, source, debrisAngle, delay, projectile));
}
@Override
public void init(CombatEngineAPI engine) {
}
private static class DebrisData {
private final AnchoredEntity hitLoc;
private final CombatEntityAPI source;
private final float dps, expiration, duration, start;
private final float shipStartFacing, deltaAngle;
private final DamagingProjectileAPI damagingProjectile;
public DebrisData(CombatEntityAPI target,
Vector2f hitLoc,
float totalDamage,
float burnDuration,
CombatEntityAPI source,
float debrisAngle,
float delay,
DamagingProjectileAPI projectile
) {
this.hitLoc = new AnchoredEntity(target, hitLoc);
this.source = source;
this.deltaAngle = debrisAngle;
this.duration = burnDuration;
this.shipStartFacing = source.getFacing();
dps = totalDamage / burnDuration;
expiration = Global.getCombatEngine().getTotalElapsedTime(false)
+ burnDuration;
start = Global.getCombatEngine().getTotalElapsedTime(false)
+ delay;
this.damagingProjectile = projectile;
}
public Vector2f getLocation() {
return hitLoc.getLocation();
}
public CombatEntityAPI getAnchor() {
return hitLoc.getAnchor();
}
private float getDebrisAngle() {
return deltaAngle + (shipStartFacing - source.getFacing());
}
public float getRemainingDuration() {
return expiration - Global.getCombatEngine().getTotalElapsedTime(false);
}
public float getDuration() {
return duration;
}
public Vector2f getDebrisVector() {
Vector2f debVector = new Vector2f();
damagingProjectile.getVelocity().normalise(debVector);
return debVector;
}
}
private static class KelvinCalc {
//Code adapted from http://www.tannerhelland.com/4435/convert-temperature-rgb-algorithm-code/
static Color getRGB(float tmpKelvin) {
double tmpCalc = tmpKelvin; //Temperature must fall between 1000 and 40000 degrees
double r, g, b = 0f;
if (tmpKelvin < 1000) {
tmpKelvin = 1000;
}
if (tmpKelvin > 40000) {
tmpKelvin = 40000;
} //All calculations require tmpKelvin \ 100, so only do the conversion once
tmpKelvin /= 100;
//Calculate each color in turn
//First: red
if (tmpKelvin <= 66) {
r = 255;
} else {//Note: the R-squared value for this approximation is .988
tmpCalc = tmpKelvin - 60;
tmpCalc = 329.698727446d * (Math.pow((double) tmpCalc, -0.1332047592d));
r = tmpCalc;
if (r < 0) {
r = 0;
}
if (r > 255) {
r = 255;
}
}
if (tmpKelvin <= 66) {
tmpCalc = tmpKelvin;
tmpCalc = 99.4708025861d * Math.log(tmpCalc) - 161.1195681661d;
g = tmpCalc;
if (g < 0) {
g = 0;
}
if (g > 255) {
g = 255;
}
} else {//Note: the R-squared value for this approximation is .987
tmpCalc = tmpKelvin - 60;
tmpCalc = 288.1221695283d * (Math.pow(tmpCalc, -0.0755148492d));
g = tmpCalc;
if (g < 0) {
g = 0;
if (g > 255) {
g = 255;
}
}
}
//blue
if (tmpKelvin >= 66) {
b = 255;
} else {
if (tmpKelvin <= 19) {
b = 0;
} else {
tmpCalc = tmpKelvin - 10;
tmpCalc = 138.5177312231d * Math.log(tmpCalc) - 305.0447927307;
b = tmpCalc;
if (b < 0) {
b = 0;
}
if (b > 255) {
b = 255;
}
}
}
return new Color((int) r, (int) g, (int) b, 240);
}
}
}
If you have a look through the radioactive code dump, you can probably find a simpler example than the one provided for an onHit. Have a look for some BeamEffectPlugin scripts as well which may do what you want. The example provided is specific for the purposes of over penetration and has a whole lot of math that you probably don't require for your script.