Fractal Softworks Forum

Please login or register.

Login with username, password and session length
Advanced search  


Starsector 0.96a is out! (05/05/23)

Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.

Topics - float

Pages: [1] 2
Modding / [0.96a] Unthemed Weapons Collection 0.5.2 (5/31/23)
« on: May 29, 2023, 07:58:26 PM »
Unthemed Weapons Collection

No hard dependencies. Soft dependency on Particle Engine. Will run without it, but some visual effects will be missing.

Weapon pack. No factions, no ships*, no faction-themed stuff. Adds 42 weapons to the game. Also adds fortified caches and a few secrets.

Some of the weapons

Railburst Rifle: Two-shot burst railgun. Regenerating ammo.

Demolisher Cannon: High damage per shot, for a small weapon. Regenerating ammo.

Mortar Screen: Low range, low accuracy, very flux efficient.

Spreadfire Cannon: Shotgun that gets more accurate the longer you fire.

Pummeler: Heavy fragmentation damage. Sustained fire increases fire rate.

Helios Artillery: Long range, low DPS, poor efficiency. AOE damage on hit.

? ? ?

Boltgun: Terrible damage output, but cheap and long range (relatively speaking).

Mining Laser Mk.2: What even is this thing? Limited ammo HE beam weapon.

Enhanced Tactical Laser: Bigger tactical laser that also has a secondary flux-based effect on the target.

Bolt Scrambler: Long range (relatively speaking) shotgun. Difficult to use on turrets, less difficult to use on hardpoints.

Graviton Array: Instant +15% shield damage. Doesn't stack with itself or with graviton beams.

Chaos Pulser: Long range, randomized damage. The stronger a shot is, the less accurate it becomes.

? ? ?

Cluster Mine Launcher: Launches a cluster of mines, as expected.

Spiker SRM: Light sustained shield pressure. Does additional soft flux damage on shield hits.

Paralyzer Rocket Launcher: High tech annihilator.

Bloom Autolauncher: Slow, guided torpedo. Low damage, but causes secondary explosions.

Stinger Swarm Battery: Energy swarmer. Unspectacular damage output, but spammable.

Ion Storm GLMRS: For when you really need to flame something out. Missiles surround the target.

? ? ?

Fortified Caches
Procedurally generated caches with defenders. Defenders aren't automated (no lore reason, gameplay reason is that the player already fights enough derelict drones and remnants). These contain normal loot, though with a rewritten loot system, so quantities and types of items may be different from what you're used to seeing. Can change cache frequency in the settings; toggle off by setting frequency to 0.

Also has an option to regenerate caches. This is OFF by default, but if enabled, fortified caches that are looted will be replaced eventually. Essentially allows the player to have a theoretically endless amount of stuff to loot, allowing them to complete their blueprint collection, etc. without having to raid core worlds.

Version Log
  • 0.5.2:
    Fix: an issue where custom missile AI didn't work unless a campaign game was loaded first.
    Void Ray: fixed tooltip showing wrong info
    Bloom-class torpedo: base price 1000 -> 500
    Electron-class torpedo rack: base price 600 -> 1000
    Sprawl MRM: range 2000 -> 1800
    Cluster Mine Launcher / Heavy Cluster Launcher: explosions no longer inherit mine velocity when Particle Engine is disabled
    Bolt gun: DPS 83 -> 70
    Pummeler: now reaches max fire rate after 10s, down from 15s
  • 0.5.1: Add mod thread id to version files (5/29/23)
  • 0.5: Initial release (5/29/23)

e.g. projectileWeaponSpec.setChargeTime(3f) inside WeaponEffectPluginWithInit.init, expected behavior would be to change the charge time of the weapon to 3 seconds, but nothing happens.
Or projectileWeaponSpec.setEnergyPerShot(1000f) inside WeaponEffectPluginWithInit.init, expected behavior would be to change the base flux per shot to 1000, but this doesn't happen. Interestingly though, weapon.getFluxCostToFire does correctly get modified.

Outside of a market, if the core UI is closed, CoreUITabId becomes null, as expected.
However, inside a market, if the core UI is closed, CoreUITabId remains what it was before it was closed. For example, if you open the refit screen from a market's menu, then close it via the escape key or the top-right button, the refit screen itself closes but CoreUITabId is still REFIT.

Bug Reports & Support / Planetkiller issues
« on: May 08, 2023, 09:50:45 AM »
- Trying to turn the planetkiller in to the Diktat via the option "I have something that will be of great interest to the Lion of Sindria" gives a rule-not-found error.

- Turning the planetkiller in to the LP base that started "The Scythe of Orion" quest rewards a very high LP contact but doesn't actually do anything about the LP hostile activity. The receiver also doesn't make any mention about betraying you, etc., so I would assume that he'd uphold his end of the deal -- but he doesn't.

(Unrelated issue, but if multiple LP fleets are pursuing you and you pay the "larger tithe" to one of them, the others don't stop pursuing and you can end up paying the "larger tithe" multiple times).

Suggestions / Miscellaneous small 0.96a suggestions
« on: May 08, 2023, 08:29:28 AM »
I’ll start:

- Inverting the behavior of shift-strafe button should set the default strafe lock mode to enabled if used in tandem with the strafe lock toggle. Or, the strafe lock setting should be remembered between battles.

- The hidden cache in Sentinel is currently a valid random exploration mission target — not sure if that’s intended or not.

- FMR recharges way too fast on the Pegasus IMO (and also has too many charges when considering the Pegasus specifically), it’s actually kind of hilarious with hurricanes + system specialization. I’d consider changing the system entirely and maybe even adding an innate missile fire rate nerf to the ship.

Edit: current state of Pegasus also severely limits the design space of modded missiles. Basically any large missile that’s guided with a long cooldown becomes OP.

After scanning the center of a binary star system and receiving the topography data, any subsequent sensor burst triggers the "center of star system already scanned" message, regardless of where the scan took place.

I assume this is happening when the hostile event tracker reaches 75, upon which some event is supposed to be revealed.

Stack trace:
java.lang.RuntimeException: Texture [hostile_activity_MINOR_EVENT] from category [events] not found
   at com.fs.starfarer.settings.StarfarerSettings.Ò00000(Unknown Source)
   at com.fs.starfarer.settings.StarfarerSettings$1.getSpriteName(Unknown Source)
   at ageIconImpl(
   at a(
   at ionWithImage(
   at iption(
   at Source)
   at com.fs.starfarer.campaign.comms.v2.EventsPanel.actionPerformed(Unknown Source)
   at Source)
   at com.fs.starfarer.campaign.comms.v2.EventsPanel.advanceImpl(Unknown Source)
   at com.fs.starfarer.ui.thissuper.advance(Unknown Source)
   at com.fs.starfarer.ui.o000.advanceImpl(Unknown Source)
   at com.fs.starfarer.campaign.comms.OOoO.advanceImpl(Unknown Source)
   at com.fs.starfarer.ui.thissuper.advance(Unknown Source)
   at com.fs.starfarer.ui.o000.advanceImpl(Unknown Source)
   at com.fs.starfarer.ui.newui.o0OO.advanceImpl(Unknown Source)
   at com.fs.starfarer.ui.thissuper.advance(Unknown Source)
   at com.fs.starfarer.ui.o000.advanceImpl(Unknown Source)
   at com.fs.starfarer.ui.O.advanceImpl(Unknown Source)
   at com.fs.starfarer.ui.newui.OO0O.advanceImpl(Unknown Source)
   at com.fs.starfarer.ui.thissuper.advance(Unknown Source)
   at com.fs.starfarer.ui.o000.advanceImpl(Unknown Source)
   at com.fs.starfarer.ui.thissuper.advance(Unknown Source)
   at com.fs.starfarer.campaign.CampaignState.advance(Unknown Source)
   at com.fs.starfarer.BaseGameState.traverse(Unknown Source)
   at com.fs.state.AppDriver.begin(Unknown Source)
   at com.fs.starfarer.combat.CombatMain.main(Unknown Source)
   at com.fs.starfarer.StarfarerLauncher.o00000(Unknown Source)
   at com.fs.starfarer.StarfarerLauncher$ Source)

If both "invert behavior of shift key" and the strafe lock are enabled, the strafe lock doesn't do anything -- shift key needs to be held down to use keyboard turning.

Modding / [0.96a] Particle Engine (5/5/23)
« on: April 10, 2023, 06:49:27 PM »
Particle Engine

A library that implements a stateless, instanced particle system.

What is this?
This is a utility mod and a tool. Downloading it and adding it to your game as-is does nothing. It is intended to provide a fast, relatively simple, and relatively up-to-date system for generating, allocating, and rendering particles.

Why does this exist?
Vanilla particles are CPU-bound (individual particles are simulated on the CPU), and essentially the entire game only utilizes a single CPU core. Furthermore, each particle has its own rendering loop. To keep performance reasonable, vanilla Starsector has a fairly low particle limit, after which old particles start getting replaced.

This mod uses slightly more modern (but not actually all that modern) techniques to minimize CPU strain. Most of the work, including simulation, is done on the GPU. The CPU doesn't keep track of individual particles. The particle engine is a singleton object (accessed via static methods) that performs a single draw call per particle type (every particle with the same sprite and blend mode has the same type) per frame. The result is a much more performant particle system.

The part of the vanilla particle system that is exposed and usable by modders is highly limited. As a modder, you're essentially only easily able to add a few types of particles, all via i.e. Global.getCombatEngine().addSmoothParticle. It's also possible to use a CombatEntityPluginWithParticles, though that has its own limitations -- you're forced to use a 4x4 sprite sheet, the plugin has its own state that must be initialized and updated, and you have to set the properties of each particle using a prev pointer. It also seems to be partially broken, i.e. endSizeMult doesn't actually seem to work, and particles either grow or shrink at a fixed rate.

How do I use this?
The starting point of all particle generation is the Particles class. There are essentially just three steps:
  • Create an emitter using Particles.initialize. This returns a default Emitter object.
  • Set the emitter's properties. Here you can set and randomize particles' positions, velocities, accelerations, sizes, colors, shift their colors over time, add radial motion, revolution, and so on.
  • Generate the particles using Particles.burst or, passing in an emitter whose properties you want to use.
There is also a javadoc bundled with the zip download that explains the function of each class and method in more detail.

What are the limitations?
Because particles are simulated on the GPU, they are also stateless (technically it's possible to store state in, e.g. an SSBO, but that's not relevant for now). This means that they don't interact with the game world -- once they're generated, their trajectories are fixed. As a result, particles won't be able to interact with ships or projectiles in either direction -- they can't affect the game state and they can't be affected by the game state. Still, because emitters are managed by the CPU, it's possible to modify emitters according to the game state, so a large variety of effects are nevertheless possible.

Performance is GPU dependent, so PCs with poor graphics cards or low VRAM might experience stuttering or drop outs with obscene numbers of particles. GPU code is also finicky, so with this early version you may run into system-specific issues or bugs.

Additional features
It's possible to read and write emitter properties from and to JSON files. To read an emitter from a JSON file, simply use Emitter.fromJSON in conjunction with Global.getSettings().loadJSON. For an example emitter JSON, see data/particleengine/sample_emitter.json.

Writing emitters to file is a bit tricker due to file IO limitations put in place by Starsector's custom class loader. The particleengine.json file in the mod's root directory contains the directory that saved emitters should be written to. By default, this is ../saved_emitters, i.e. the saved_emitters directory in the Starsector root. If this directory doesn't exist, you will have to create it manually before any write operations will work.

The JSON functionality can be a valuable tool when designing particle effects as changes to the JSON file can be effected without having to restart Starsector. In released code, it's not recommended to use writeJSON, and, if intending to use readJSON, the files should be batch-read at some initialization stage and cached for later reuse.

Custom emitters
While the default Emitter can generate a variety of effects already, it is possible to make your own emitters by implementing IEmitter, or extending the BaseIEmitter class. This is helpful in creating emitters with inter-particle dependencies, i.e. particles whose properties depend on other properties in a non-random way.

You will have to implement the initParticle function, which takes in the index of the particle in the burst or stream and outputs a ParticleData that is passed into the vertex shader. The below sample emitter replicates the vanilla muzzle flash visual, whereby particles' velocities are directed outwards and dependent on their initial distance from the emitter origin:

Muzzle flash emitter code

public class MuzzleFlashEmitter extends BaseIEmitter {

    private final Vector2f location;
    private float angle, arc, range, minLife, maxLife, minSize, maxSize, velocityScale;
    private final float[] color = new float[] {1f, 1f, 1f, 1f};
    private CombatEntityAPI anchor;

    public MuzzleFlashEmitter() {
        location = new Vector2f();
        angle = arc = range = 0f;
        minLife = maxLife = 0.5f;
        minSize = 20f;
        maxSize = 30f;
        velocityScale = 1f;

    public SpriteAPI getSprite() {
        return particleengine.Utils.getLoadedSprite("graphics/fx/particlealpha64sq.png");

    public MuzzleFlashEmitter anchor(CombatEntityAPI anchor) {
        this.anchor = anchor;
        return this;

    public MuzzleFlashEmitter location(Vector2f location) {
        return this;

    public MuzzleFlashEmitter angle(float angle) {
        this.angle = angle;
        return this;

    public MuzzleFlashEmitter arc(float arc) {
        this.arc = arc;
        return this;

    public MuzzleFlashEmitter range(float range) {
        this.range = range;
        return this;

    public MuzzleFlashEmitter life(float minLife, float maxLife) {
        this.minLife = minLife;
        this.maxLife = maxLife;
        return this;

    public MuzzleFlashEmitter size(float minSize, float maxSize) {
        this.minSize = minSize;
        this.maxSize = maxSize;
        return this;

    public MuzzleFlashEmitter color(float r, float g, float b, float a) {
        color[0] = r;
        color[1] = g;
        color[2] = b;
        color[3] = a;
        return this;

    public MuzzleFlashEmitter velocityScale(float velocityScale) {
        this.velocityScale = velocityScale;
        return this;

    public Vector2f getLocation() {
        return location;

    protected ParticleData initParticle(int i) {
        ParticleData data = new ParticleData();

        // Life uniformly random between minLife and maxLife
        float life = randBetween(minLife, maxLife);, life);

        float theta = angle + randBetween(-arc / 2f, arc / 2f);
        float r = range * (float) Math.sqrt(Misc.random.nextFloat());
        Vector2f pt = new Vector2f(r*(float)Math.cos(theta*Misc.RAD_PER_DEG), r*(float)Math.sin(theta*Misc.RAD_PER_DEG));
        // Velocity is proportional to distance from center
        Vector2f vel = Misc.getUnitVectorAtDegreeAngle(theta);
        // Add the anchor's velocity, if it exists
        if (anchor != null) {
            Vector2f.add(anchor.getVelocity(), vel, vel);

        // Size uniformly random between minSize and maxSize
        float size = randBetween(minSize, maxSize);
        data.size(size, size);

        // Color

        return data;

    public float randBetween(float a, float b) {
        return Misc.random.nextFloat() * (b - a) + a;



Sample emitters with code
Ion explosion

        Emitter emitter = Particles.initialize(pt, "graphics/fx/explosion1.png");
        emitter.circleOffset(30f, 50f);, 1.25f);
        emitter.fadeTime(0.1f, 0.1f, 0.3f, 0.5f);
        emitter.facing(0f, 360f);
        emitter.turnRate(-50f, 50f);
        emitter.turnAcceleration(-50f, 50f);
        emitter.circleVelocity(100f, 100f);
        emitter.radialAcceleration(-50f, -100f);
        emitter.revolutionRate(-20f, 20f);
        emitter.randomHSVA(20f, 0.1f, 0f, 0f);
        emitter.colorShiftHSVA(0f, -0.3f, -0.3f, -0.05f);
        emitter.size(150f, 200f);
        emitter.growthRate(100f, 150f);
        emitter.growthAcceleration(-50f, -75f);
        Particles.burst(emitter, 50);

        Emitter ringEmitter = Particles.initialize(pt, "graphics/fx/custom_ring.png"); // Just a simple white ring
        ringEmitter.setAxis(proj.getFacing());, 1f);
        ringEmitter.fadeTime(0.1f, 0.1f, 0.5f, 0.7f);
        ringEmitter.size(300f, 400f, 40f, 60f);
        ringEmitter.growthRate(400f, 500f);
        ringEmitter.growthAcceleration(-50f, -60f);
        ringEmitter.hueShift(-50f, 50f);
        ringEmitter.saturationShift(-0.2f, -0.2f);
        ringEmitter.facing(-55f, -35f);
        Particles.burst(ringEmitter, 10);
        ringEmitter.facing(35f, 55f);
        Particles.burst(ringEmitter, 10);

        Emitter arcEmitter = Particles.initialize(pt, "graphics/fx/custom_emp_arcs.png"); // Just emp_arcs.png with a darkening filter applied to border, 0.3f);
        arcEmitter.fadeTime(0f, 0f, 0.15f, 0.25f);
        arcEmitter.size(350f, 350f, 300f, 300f);
        arcEmitter.growthRate(-40f, -80f);
        arcEmitter.turnRate(-10f, 10f);
        arcEmitter.facing(0f, 360f);
        arcEmitter.color(0.7f, 1f, 1f, 0.7f);
        arcEmitter.facing(0f, 360f);
        arcEmitter.alphaShift(-0.5f, -0.5f);, 1, 50, 0.75f);


Simple trail


        Emitter emitter = Particles.initialize(proj.getLocation(), "graphics/fx/particlealpha_textured.png");, 1.2f);
        emitter.fadeTime(0f, 0f, 0.5f, 0.5f);
        emitter.offset(-50f, -5f, 0f, 0f);
        emitter.velocity(-40f, -20f, 0f, 0f);
        emitter.color(1f, 0f, 1f, 0.8f);
        emitter.randomHSVA(40f, 0.2f, 0f, 0f);
        emitter.saturationShift(-1f, -1.5f);
        emitter.alphaShift(-0.6f, -0.6f);
        emitter.size(80f, 80f, 20f, 20f);
        emitter.growthRate(-50f, -50f, -8f, -8f);, 1, 100f, 5f, new Particles.StreamAction() {
            public boolean apply(Emitter emitter) {
                // Set the emission location and axis to match the projectile's right before each burst of particles is generated.
                return !proj.isExpired();


Stress test with 1 million particles



        Emitter emitter = Particles.initialize(pt);
        emitter.setBlendMode(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA, GL14.GL_FUNC_ADD);, 10f);
        emitter.fadeTime(0f, 0f, 0.5f, 0.5f);
        emitter.circleOffset(0f, 30f);
        emitter.colorHSVA(new float[] {0f, 0.5f, 1f, 1f});
        emitter.size(10f, 25f, 10f, 25f);
        emitter.growthRate(-10f, 10f, -10f, 10f);
        emitter.randomHSVA(360f, 1f, 0f, 0f);
        emitter.revolutionRate(-30f, 30f);
        emitter.colorShiftHSVA(-200f, 200f, -0.1f, 0.1f, -0.1f, 0.1f, -0.1f, -0.1f);
        final float[] amp = {200f};
        emitter.sinusoidalMotionX(-amp[0], amp[0], 0.25f, 0.25f, 0f, 0f);
        emitter.sinusoidalMotionY(-amp[0], amp[0], 0.25f, 0.25f, 0f, 0f);, 1000, 100000, 10f, new Particles.StreamAction() {
            public boolean apply(Emitter emitter) {
                amp[0] += 2f;
                emitter.sinusoidalMotionX(-amp[0], amp[0], 0.25f, 0.25f, 0f, 0f);
                emitter.sinusoidalMotionY(-amp[0], amp[0], 0.25f, 0.25f, 0f, 0f);
                return true;



Version log
  • 0.5 (5/29/23):
    New classes, IEmitter and BaseIEmitter, that may be extended to generate custom particle emitters. Particles can now depend on each other's properties (prior to generation).
    Generify Particles.StreamAction. Note: this will break implementations using StreamAction, to fix, replace all StreamAction with StreamAction<Emitter>.
    Change license, relevant for v0.5+
  • 0.4 (5/5/23):
    Update Starsector version to 0.96a-RC6
    Change default out-of-bounds range from 100 to 500
    Change default particle layer to ABOVE_PARTICLES_LOWER
    Add early exit on Particles.burst if number of particles is <= 0
  • 0.3 (5/1/23):
    Emitters now support CombatEngineLayers, layer can be set via Emitter.setLayer
    Added Emitter.setInactiveBorder. Emitters outside of the screen by more than this amount of world units will ignore particle generation calls.
  • 0.2 (4/11/23):
    Add mod thread id to version file
    Remove axis from json output (it's an emitter property, not a particle property)
    Allow changing of emitter sprite and blend mode via setters
    Add setSyncSize to Emitter, which allows random particle sizes that are the same in the x and y directions (uses the x direction values)
  • 0.1: Initial release (4/10/23)

Mods / [0.96a] Officer Extension (0.5)
« on: January 26, 2022, 10:26:20 PM »
Officer Extension

Overhauls the officer selection dialog, adding additional features:


Manage more officers
  • Manage as many officers as you'd like. Hire new officers regardless of how many are currently under your command.
  • The 8 officer limit (10 with officer management) still applies -- however, it only applies to officers in command of a ship. Officers not in command of a ship do not count toward your officer limit.

Suspend officers
  • Suspend officers in your fleet for an upfront fee equal to twelve times their monthly salary.
  • Suspended officers cannot be assigned to any ship and must be paid 10% of their standard salary.
  • Suspended officers can be reinstated at any time for no cost.
  • Does not apply to mercenaries or AI cores.

Demote officers
  • Select skill icons directly from an officer’s skills panel to prime them for removal. Then remove those skills by demoting that officer. This causes the officer to lose levels equal to the number of skills forgotten.
  • Demoting an officer requires a story point and grants 100% bonus experience.
  • Officers cannot be demoted below level 1.
  • Does not apply to mercenaries or AI cores.


Additional menu options
  • Undo your most recent assignments. This may fail if you suspend, dismiss, or fully integrate officers or AI cores beforehand.
  • Filter officers by skills or by custom user-defined tags. Officers that don't have the selected skills or tags will be hidden from the selection panel.
  • Sort officers, which will sort officers in your fleet in the following order: unassigned mercenaries, assigned mercenaries, unassigned non-mercenary officers, assigned non-mercenary officers, suspended officers. Officers in the same category are sorted by level, followed by name.

Miscellaneous QoL
Sorts officer payroll in the income tab by pay, followed by officer name for officers with equal pay. This applies to suspended officers as well.


Upon interacting with another fleet, displays that fleet's commander's combat skills, including fleet-wide skills.


Level 7 cryopod officers will maintain their maximum level and number of elite skills even if you demote them or remove their elite skills via the retrain option. Like normal officers, they can no longer be retrained if they have fewer than their maximum allowed number of elite skills.

Additional notes:


  • Safe to add to or remove from an existing save file.
  • Inspiration to modify the core UI directly (rather than making yet another rule-based interaction dialog or possibly a new intel tab) derived from andylizi's Planet Search and Dialog Minimap mods.

Version history:


- Just update Starsector version to 0.96a-RC6 and make sure nothing broke as a result
- Reset game pause state on load
- Fix NoSuchMethodException issue on Linux machines
- Add a field for persistent tags for filtering officers in the mod settings. Persistent tags will always be shown before any other custom-defined tags and will be shown even if you don't have any officers with that tag.
- Change all getMemory to getMemoryWithoutUpdate. This should fix crash bugs for officers with empty rank, post, name, etc.
- Overhaul the "edit tags" feature. Now allows you to add tags individually as well as select from a list of all custom-defined tags.
- Add filters for personality and the "exceptional sleeper pod" status
- Allow filtering for elite versions of skills
- Sorting now takes number of elite skills into account
- Change officer count so that only assigned officers count against your limit
- Add undo, filter, sort features in the selection menu
- Allow hiring of officers regardless of current number in fleet
- Allow special cryopod officers to regain lost levels and elite skills
- Suspending an officer now costs 12x their monthly salary (up from 10x). Suspended officers receive 10% pay (down from 25%).
- More detailed revisions in README
- Added two new configurable settings: SP cost to demote officers, and whether the fleet commander skills panel should be split between personal and admiral skills.
- The fleet commander skills display will now display info for the enemy fleet in an ongoing battle, regardless of which fleet you clicked on.
- Renamed the "forget" buttons to "demote"
- Fixed an issue with the ReflectionEnabledClassLoader where it wasn't checking for findLoadedClass before calling findClass
- Various typo fixes and clarifications
0.3.0: initial release

This is a bug that's likely never to be encountered in any standard vanilla (or modded) playthrough, but I thought it'd be worth mentioning nonetheless. The following conditions have to be met:

- You have a ship with modules
- The hint popup that says "you've made changes to the ship's loadout, but have not adjusted the weapon groups" is enabled

To reproduce the bug:

- Open the refit screen for a ship with modules
- Select and add weapons to any module
- Select a different module. The hint popup asking to adjust weapon groups should appear.
- Select "adjust weapon groups"
- Make any nontrivial adjustment (i.e. toggle autofire, change the weapon group of a single weapon)
- After confirming, click on any module
- The previously selected module should disappear and be replaced with a copy of the original module
- This can be done repeatedly, leading to some interesting results (see attached)

Even though ships with modules are not normally obtainable in vanilla, I was able to reproduce the bug in vanilla by temporarily using console commands to spawn a station, saving the game, and then disabling console commands.

After the shield shunt nerf, I got to thinking -- how bad or not bad is it now?

Since the armor damage reduction calculation is rather complex, it's unclear how well a +15% increase to armor actually translates to combat performance. So I devised a couple of metrics to quantify how strong armor is in practical terms, as well as to gauge how strong skills and hull mods that affect armor actually are.

A note: I am making some simplifications for this analysis. First, I'm only going to consider a single block of armor, and I'll assume that that single block contributes all of its armor value, rather than taking an average over many surrounding blocks. Next, I'm going to assume that weapons deal continuous damage with two parameters: a hit strength H, and a base DPS B. Projectiles weapons can be approximated as beam weapons with B equal to their stated DPS and H equal to the projectile damage. The continuous assumption is what makes an analytical solution possible; otherwise, we'd be looking at some ugly recurrence relations.

For those who are unfamiliar with the armor mechanics, I'll give a brief recap. Suppose that A is the total armor of your ship. This is the number that appears in the refit screen, and is modified by hull mods like shield shunt, heavy armor, armored weapon mounts. However, it doesn't account for skills like polarized armor (which gives invisible armor for damage reduction purposes only) and impact mitigation (which reduces damage taken by armor, and used to give a flat invisible armor bonus). The total effective armor, which is the number actually used for damage reduction purposes, can be written as e(A) = uA + v, where u accounts for bonuses of the form "grants x% armor for damage reduction calculation purposes only," and v accounts for bonuses of the form "grants y (flat) armor for damage reduction calculation purposes only."

Suppose your ship currently has X armor. When your ship's armor gets hit by a beam with stated DPS B and hit strength H (usually H = B/2 for beams), the DPS sustained by the armor is (1 - R) * B, where R is the damage reduction fraction. Normally, R = e(X) / (e(X) + H) . However, there are two exceptions: if e(X) / (e(X) + H) > 0.85, then R is set to 0.85 (or 0.9 if you have polarized armor), so that your ship will always take a minimum fraction of damage regardless of how much armor it has. The second exception is if e(X) < e(0.05*A), in which case the armor value used is instead e(0.05*A), so that you will always have a minimum base armor value for damage reduction calculation, even if your armor is fully stripped.

A natural way to judge practical effectiveness of armor is to consider how long it takes for a particular weapon to fully strip it. I'll call this the strip time T. It turns out that, in the absence of the two exceptions above, T is quadratic in A -- that is, T is proportional to A^2. So, the more armor you have, the more effective the additional armor is. This makes flat armor bonuses -- heavy armor in particular -- more useful than they might initially seem.

What I'm really interested in is how much longer it takes to fully strip armor once we add certain skills and hull mods. For this, I'll consider the strip time ratio -- the amount of time it takes to fully strip armor with said skill or hull mod, divided by the amount of time it takes to fully strip armor without it. Skills and hull mods with higher strip time ratios are, in a very practical way, better. However, it comes as no surprise that the strip time ratio varies depending on the ship's base armor value. Here are graphs of strip time ratios of various skills, hull mods, and combinations thereof, for the following weapons: tactical laser (B = 75, H = 37.5), heavy mortar(B = 220, H = 220), and plasma cannon (B = 750, H = 500).

Tactical Laser


Heavy Mortar


Plasma Cannon


Some observations from these graphs:

- For most armor modifiers, strip time ratio increases with increased base armor until it hits a peak, after which it starts to decrease. The peak is the point at which the 85% damage reduction cap starts to kick in, since at this point the strip time goes from quadratic to linear. The obvious exceptions to this are polarized armor, which gains effectiveness past the damage reduction cap due to it increasing said cap to 90%; and heavy armor, due to being a flat increase.

- The general individual order of effectiveness of skills, along with shield shunt, is IM > SS (pre-nerf) > SS (post-nerf) > PA (assuming constant 25% bonus). PA dominates when the weapon's hit strength is low or base armor is high, due to gaining effectiveness once the damage cap is reached. With a 50% constant bonus (pre-nerf), PA actually competes with SS (post-nerf) against a plasma cannon, being weaker up until ~850 base armor and stronger after that. Note that since IM reduces damage and hit strength to armor by 25% (reference:, it is effectively increasing armor by 33%, which is better than even pre-nerf shield shunt.

- Individually, the skills aren't particularly strong, improving strip time by a modest factor. The strongest, impact mitigation, caps out at about a 1.5 strip time ratio. However, they are comparatively much stronger when combined. With a decent base armor, the strip time ratio can be improved by a factor of 2-2.5 when stacking all three of SS, IM, and PA together (assuming 25% bonus for PA). When combined with HA and AWM, you can triple to quadruple your ship's armor's strip time, even with upwards of 1000 base armor. The reason why the skills and hull mods work so well when combined is due to how they stack. Hull mods affect the visible armor value and stack additively with each other, but everything else stacks multiplicatively. Polarized armor affects effective armor and impact mitigation affects hit strength and damage taken, each of which are modified separately from the ship's base armor.

I've attached a pdf showing how I derived the strip time formula, as well as a Mathematica notebook that I used to create the graphs, for anyone who is interested in playing around with the values themselves.

If you just deploy one ship and instantly retreat it, after disengaging, the station bounty will be marked as complete and you will receive the bounty for it.

Bonus: I just completed a station bounty for Kanta’s Den from watching another fleet engaging and failing to destroy it.

Occasionally, when trying to visit the bar at a colony, my game would hang. After some testing, it seems that the culprit is a specific bar event that is causing an infinite loop if it can't find a blueprint to pick. Specifically, in BlueprintIntel.pickItem, the picker is empty after every iteration of the loop.

Curiously, I went ahead and checked if I really had every ship, weapon, and fighter blueprint -- I don't. For example, I'm missing Astral, Champion, and some XIV blueprints. Is this specific bar event meant to always have a quality of 0 (meaning that the only blueprints with positive weight are those with tier 0, or ships with at most 5 FP)? I don't think I've ever seen a version of this bar event that offered anything with a higher tier -- that would explain why this event always seems to have such a small selection. It's always revenant (5 FP), phantom (3 FP), buffalo, perdition wing, and with mods, LR pulse laser.

After Kanta sends you to Epiphany for Loke, the portwatcher there (Favour Seekwisdom in my game, not sure if it varies) has three dialog options. Choosing one of the first two causes him to deny your request. However, if you immediately talk to him again without exiting the dialog, clicking on any of the remaining options will cause an error stating that no matching rule was found:

Fortunately, exiting and reopening the dialog fixes the issue.

Pages: [1] 2