• We have updated our Community Code of Conduct. Please read through the new rules for the forum that are an integral part of Paradox Interactive’s User Agreement.

Pancakelord

Lord of Pancakes
43 Badges
Apr 7, 2018
3.369
12.251
  • Cities: Skylines - Green Cities
  • Stellaris: Leviathans Story Pack
  • Cities: Skylines - Natural Disasters
  • Hearts of Iron IV: Together for Victory
  • Stellaris: Ancient Relics
  • Cities: Skylines - Mass Transit
  • Surviving Mars
  • Hearts of Iron IV: Death or Dishonor
  • Imperator: Rome
  • Stellaris: Digital Anniversary Edition
  • Hearts of Iron IV: Expansion Pass
  • Stellaris: Humanoids Species Pack
  • Stellaris: Apocalypse
  • Cities: Skylines - Parklife
  • Stellaris: Distant Stars
  • Shadowrun Returns
  • Cities: Skylines Industries
  • Imperator: Rome Deluxe Edition
  • Cities: Skylines - After Dark
  • Stellaris: Nemesis
  • Europa Universalis IV
  • Stellaris: Necroids
  • Crusader Kings III
  • War of the Roses
  • Cities: Skylines
  • Stellaris: Federations
  • Magicka: Wizard Wars Founder Wizard
  • Cities: Skylines - Snowfall
  • Stellaris: Lithoids
  • Stellaris: Galaxy Edition
  • Stellaris: Galaxy Edition
  • Stellaris: Galaxy Edition
  • Hearts of Iron IV: Cadet
  • Hearts of Iron IV: Colonel
  • Stellaris - Path to Destruction bundle
  • Stellaris: Megacorp
  • Stellaris: Synthetic Dawn
  • Crusader Kings II
  • Stellaris
  • Cities: Skylines Deluxe Edition
  • Sword of the Stars II
  • March of the Eagles
  • Darkest Hour
Hello,
This mod will speed up purges for crisis factions for V2.7+. I've created it here on the steam workshop:
https://steamcommunity.com/sharedfiles/filedetails/?id=2245706058
1601649228927.png

It's also on the paradox modding site but I never use that, so I couldn't actually tell you how to find it there lol.

Background info on the problem this mod solves:
Currently when crises invade worlds they use the generic "Extermination purge" - this is available to regular empires and is balanced so it can take months to kill a single pop. This is not good for a crisis ... it means it could be sitting around for decades or even centuries trying to purge an ecumenopolis (for example) or even years for smaller worlds, this slows down crisis expansion to a crawl.
In past versions of the game (pre 2.0) multiple pops could be killed at the same time/in the same month by a purge.
Post 2.0 this is impossible - only one may die at a time and at most one may die per a month. This led to 2/3 crises (Prethoryn swarm & contingency)struggling to expand, as they need to invade worlds before they can move on (the unbidden just bomb and move on).

What this mod does, exactly;
It adds a check that runs each time a battle ends, when it sees a battle was won by a non-default (i.e. not a playable) country, whilst the galaxy has a crisis ongoing (determined by a global flag), it will fire a second event that executes 1 pop on that world if it's been marked for "purging".
  • It will then fire the event again on (currently) a 7 day delay, to kill another pop.
  • If there is only 1 pop left on the world the event aborts.
  • The last pop is left to be killed by the purge process incase any scripts hook in to that (i dont think they do, but better to be safe than sorry).
  • It should be noted that purging is still running so you have slightly more than 1 pop dying each week when you average it.
Lets take a world with 200 pops as our test case:
Purging adds 35/100 each month and there is no overflow so to purge 1 pop takes 3 months. Purging 200 pops takes 600 months (600/12 = FIFTY YEARS! to wipe out a world with 200 pops)

Now lets take my mod and recompute that. (200-1)/7 = 28.43 months to wipe out all but the last pop on a planet + 3 months for purging to kill the last one = 31 months (31/12 = 2.6 years)
[total #pops - 1 as it wont fire on the last one / 7 days per pop death]

But there's more, as purging is running concurrently, we can take 28/3 to see that ~9 pops will have been "vanilla-purged" alongside this mod,

So the actual time to wipe out 200 pops is closer to 2 years total. (50 years vanilla / 2 years modded = ~25x faster)
TLDR: This means crises will purge worlds approx. 25x faster with this mod enabled.

If you combine this with the time defenders hold out (on high pop or fortified worlds) it will still give you a slim chance to mount a rescue/liberation of the world before its infested... but you'll need to move fast no more waiting around for a century before saving those pops.

How does this mod work, exactly:
I have copied in the 2 files below for those who just want to see how it works, for curiosity or use in their own mods without having to fetch it off steam first.
And by all means if you want to use this for your own mod or whatever, go right ahead.
Here is the on_action file (located in modroot\common\on_actions\fastpurge_on_action.txt)
.
Code:
# Triggers country_event for the attacker upon victory
# (Before controller is switched)
# This = country, leader attacker
# From = country, planet owner
# FromFrom = planet
# IDENTITIES: attacker is the side that "IsHostile" to
# the planet controller; e.g. spawned monster armies
# are attackers, but if they win and the player attempts
# to retake the planet, the player is the attacker
on_planet_attackers_win = {
    events = {
        fastcrisispurge.1
    }
}
.
Here is the event file (located in modroot\events\fastpurge_event.txt)
.
Code:
namespace = fastcrisispurge

# Pop death interval
@deathinterval = 7    # days between re-running fastcrisispurge.2 to kill a pop.

# Gate-keeper event. Triggered by on_planet_attackers_win, runs on the winning attacker country (#root).
country_event = {
    id = fastcrisispurge.1

    hide_window = yes                                        # no popup
    is_triggered_only = yes                                    # only fires on successful invasions

    trigger = {
        has_global_flag = galactic_crisis_happened            # Do nothing if no crisis has kicked off.
        OR = {                                                # Do nothing if the invader isnt one of the crisis country types.
            is_country_type = swarm
            is_country_type = extradimensional
            is_country_type = extradimensional_2
            is_country_type = extradimensional_3
            is_country_type = ai_empire
            is_country_type = gray_goo
            has_country_flag = uses_faster_purges_mod        # If modders want to, they can give this flag to their custom factions for it to work with them.
        }
    }

    immediate = {
        #set_country_flag = faster_pop_purge_engaged_cntry    # debug flag on attacker (not needed)

        FromFrom = {                                        # Target the planet from the battle
            planet_event = { id = fastcrisispurge.2 }        # proceed to kill-pop event
        }
    }
}

# Killing event. scopes to the invaded planet.
planet_event = {
    id = fastcrisispurge.2

    hide_window = yes                                # no popup
    is_triggered_only = yes                            # triggered via above
    location = root

    pre_triggers = {
        has_owner = yes                                # Not a barren or empty world
        is_occupied_flag = yes                        # Does the "occupied" flag even work for crises?
        has_ground_combat = no                        # No pop-killing during fighting - if another empire counter-invades this should abort the next pop-death.
    }

    trigger = {                                     # if there is 1 pop left, abort event. Let it die via purging or some non-event means, to not break other checks.
        num_pops > 1                                # only execute if #pops greater than 1 on planet
    }

    immediate = {
        set_planet_flag = faster_crisis_purge_on    # enable flag on world for debug
     
        random_pop = {                                 # selects random pop marked for purge on the planet
            limit = {
                is_being_purged = yes
            }
            kill_pop = yes                             # kills said pop.
        }
        remove_planet_flag = faster_crisis_purge_on
        planet_event = { id = fastcrisispurge.2 days = @deathinterval }    # N days later re-fire event to kill another pop
    }
}
.
Performance:
What thread isnt complete without its own performance comments?
I'm deleting pops, and only on specific worlds that the crisis has landed on, and only once an ingame week (though multiple worlds can purge on different days, its not locked to date, but rather every 7 days, spreading the load out more evenly in a game-month), so it's pretty insignificant CPU load.

I run the game on an i7-4790k clocked at 4.5ghz with 16gb DDR3 ram.
I have noticed no slowdowns whilst testing this vs vanilla, I tag-switched to the prethoryn and invaded several worlds simultaneously and it didnt lead to stutter for me.
Your mileage may vary.
 
Last edited:
  • 2Love
Reactions:
doesn't running extra checks take up processing power, which is most assuredly not something a lategame empire has to spare? i know you said it didn't cause lag for you but you have an excellent setup and any additional lag to the normal lategame lag would likely go unnoticed as it would probably only be a few percent points difference

i love this idea but it does seem to have a few issues
 
doesn't running extra checks take up processing power, which is most assuredly not something a lategame empire has to spare? i know you said it didn't cause lag for you but you have an excellent setup and any additional lag to the normal lategame lag would likely go unnoticed as it would probably only be a few percent points difference

i love this idea but it does seem to have a few issues
That's a fair question. This entire "killing event" is handled via a pre-check which is only called on-event... In plain English that means the check will only run
  • On the event that ANY successful planet invasion occurs.
  • And then in event #1 the game will immediately abort the pop-event if it doesn't see
    1. a global crisis flag (this is about as fast-a check as you can get with the tools at hand, as, it's a Boolean "WE IN CRISIS? Y/N")
    2. a crisis faction is the attacking force -
      • Whilst its impossible to be certain, I'm confident this is about as fast as a global-flag check, too, as it
        1. scopes to the attacker_country and
        2. runs a boolean test on their flags (presumably country flags are held in an array).
Planetary invasions aren't super common, in the grand scheme of things, you get maybe 1-2 a month (and it's only victories that matter, for this event, conflict time drags that out further and a statistically significant number of invasions fail, too, if we assume AI v AI battles), even on huge galaxies, and the checks I use don't require anything special like NP calculations, they're just flag-fetches from memory and booleans, so the initial checks really wont be a drain on the performance unless you're somehow running the game below min specs on a TI-85.

If it DOES see all those things, only then does it proceed to test and kill pops with #2 event.
  1. If someone else is currently invading the world it'll also abort (as this is a pre-trigger it's compiled in (i assume) C++ and as fast as you can get, frankly)
  2. If it sees only 1 pop left on a world, it'll abort, ending (or prematurely terminating) the pop-kill event (I do this because scripts may be tied on the threshold of 1->0 pops)
    • Counting IS processor intensive, as I think all pop-ops and event threads happen on a single thread each BUT - the game seems to use a "least efforts" count, it'll stop as soon as it hits its target (in this case, 2 pops), so counting 0, 1, 2 on a scoped world really isn't too demanding in the grand scheme of things.
      • as testing this num_pops >15000 and >45000 on a world with 50k pops (yes, really - spawning 50k pops took for-fking-ever, pop-spawns lockup the game like nothing else as they hook into a bunch of things), the 15k pop count resulted in dramatically shorter lockup times.
  3. Then the actual kill process is handled via randomly selecting a pop (I dont know what random means in the context of the code, I assume it just grabs the first pop ID, e.g. 15438, in the list of "purging pops" taken from the "primary list" assigned to that world) then kills it.
    • Killing 1 pop is almost as fast as killing 50000 pops when you explicitly tell the game to kill all_pops.
    • but when you tell it to kill N number of pops it's slow as that requires a count,
      • it is faster when telling it to kill certain strata of pops (from my tests, I tried a "logans run" style script that only killed clerks, for example), so the limits like is_strata/job/whatever create exclusive lists speeding up the process
    • This means the actual act of killing/de-spawning a pop is not processor intensive, rather it's the aggregation of the target pops that is processor intensive. And, as I mentioned above, it's only selecting 1 at a time, eliminating the speed issue from counting.
  4. The last component is a timer, running every 7 days.
    • I thought about doing this per day (the same way as the Greater Than ourselves edict - yes that thing runs very frequently) but it did lead to stutter because you really do start to notice the count-command when a purge is running (think back to 2.2 megacorp), it was also too effective/fast at killing lol.
    • I didnt want to do it per month as that was 1 too slow and 2 risked piling more calculations at certain intervals,
    • By setting this to a rolling 7 day timer I could balance the kill speed (I could make killing slower by setting it to 8 days for example) and I can spread the workload out "randomly" over the course of a month, 5 worlds might be invaded on different days by the contingency, each running their own timers refreshing on separate days, till pops=1 or the planet changes hands again to a non-crisis,
      • so work never builds up on a single day (the crisis will almost certainly never invade multiple worlds AND WIN, as victory date is what is used for the on_action, all on the same day).
All I can say is give it a go, this is about as efficient a method as I could come up with, for crises specifically, without taking too many shortcuts that might break compatibility elsewhere
  • (I could for example remove num_pops >1, letting the script wipe out the final pop on a world, rather than the vanilla purge system wiping the last pop out, but that might break something, hard to know)
I have thought about making a "general purge mod" that disables bypasses vanilla purge speeds entirely and is fully handled by script, but this WOULD have performance issues, as it'd have to run far more frequently on every planet in the galaxy, not just ones invaded by the crisis, as with this mod.
  • But i'm waiting for 2.8 to drop first [likely next Thursday, they release Stellaris DLCs on Thursdays for some reason]
  • as PDX mentioned somewhere they were looking into the "assimilation new year bug" (this is when the entire game locks up because assimilation will try and fetch certain pops for conversion from one species to another) - how they solve that might give me some insight into making my own general pop-killing mod. Specifically I need an efficient way to seek and select random pops, so rip-off their code and swap out "assimilate_pop" for "kill_pop" (i'm oversimplifying here)