• 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.

Stellaris Dev Diary #31 - Modding (Scripting Anomalies)

Hello, earthlings. My name might be unfamiliar to some dev diary readers; I’ve been working on Stellaris for some time now and am in charge of scripting/content design duties for Stellaris while the eminent Goosecreature is away rearing his brood. This week’s dev diary is the first in a two-part series on modding, which you will read, enjoy, and be enlightened by. We’re here to get our hands dirty, so don’t expect any pretty screenshots! Only functional screenshots.

Stellaris is built on the Clausewitz engine, and has had a lot of functionality ported over from our other games. Since we use a random galaxy generated at game start (unless you mod in a static one) we have to approach things a bit differently than you would in a game with a pre-set map of Europe - if you’ve modded a Paradox Development Studio title before you may come across a few surprises, but you should feel right at home soon enough.

General Modding
Like our other games, Stellaris is very mod-able. Take a look in the \Stellaris\common\ folder and you’ll see. It contains mostly everything that determines how the game functions. Edicts, ethics, game rules, army attachments, name lists, country types, planet classes, modifiers, buildings and so on can all be found here. In \Stellaris\common\defines\ you will find a LUA script file that regulates a lot of basic game behaviors and settings, like the base soft cap on the number of core planets ( CORE_SECTOR_PLANET_CAP = 5 ) or the camera field-of-view ( NCamera = { FOV = 35 } ) available for easy tweaking. Most of these values come with fairly informative comments!

Scripted Triggers & Effects, Chained Event Targets
We recently inherited scripted triggers and effects from Hearts of Iron IV! They’re very good. One addition to the scripting language that originated in Stellaris and may or may not find its way to other games in the future is event target chaining. It allows us to chain targets/scopes/saved event targets together in a way not unlike how we do it in localization. For example, what was once written
Code:
from = {
    owner = {
        establish_communications = root
    }
}
can now be rendered as the much more digestible
Code:
from.owner = { establish_communications = root }
Amazing!

Anomalies
Swiftly moving on to Anomalies, the lifeblood of early game exploration in Stellaris. Incidentally, anomalies are also rather unlike the events found in our other games You can and should make your own! I will show you how.

An anomaly is made up of a minimum of four components; the anomaly category, the anomaly, the event and localization.
  1. \Stellaris\common\anomalies\00_anomaly_categories.txt
  2. \Stellaris\common\anomalies\00_anomalies.txt
  3. \Stellaris\events\00_anomaly_events.txt
  4. \Stellaris\localisation\events_l_english.yml

Anomaly categories define what planets are viable for a certain category of anomalies to spawn on. Anomalies refine this selection further and link to specific events. The event that is eventually fired… does stuff. The localization file provides the text for the event and the category window.

I feel that a picture is worth more than a thousand words, so I’m going to show you pictures of words to maximize efficiency. See if you can follow along!

Step 1 - 00_anomaly_categories
1anomalycategory.png


Step 2 - 00_anomalies
2anomaly.png


Step 3 - 00_anomaly_events
3event.png


Step 4 - events_l_english
4localization.png



And the end result!
Planet Surveyed
5categorywindow.png


Anomaly Researched
6eventwindow.png




That’s all for now, planet-dwellers. Next week, art director Aerie will explain how to get your own art into the game. Exporters! Graphics! Spaceships!
 
Last edited by a moderator:
  • 159
  • 48
Reactions:
There's definitely something to be said for the clarity of the old ways. Chained event targets truly shine as right-hand-side arguments though; where we would often have to do silly things like
Code:
scope = {
    scope = {
        scope = {
            effect = prevprev
        }
    }
}
we can now do
Code:
effect = correct.scope
It's kind of hard to explain without very specific examples that do not make a lot of sense out of context, but... It's good.

You mean like the government types in CK2 that had certain scopes to negate certain other holders and scopes? That had a lot of logical nightmares in it, even though I could mod it give government types while excluding others. It was painful to debug.

That would specifically be things like investiture, theocracies, republics, feudal systems of inheritance or inheritance laws based on m/f sex.
 
  • 1
Reactions:
Sublime Text is free.
I use a free version and I have no problem, you can try to download and use the software on the official website without paid and with no time limit: https://www.sublimetext.com
Sublime Text may be downloaded and evaluated for free, however a license must be purchased for continued use.

Licenses are per-user, rather than per-machine, so you can enjoy Sublime Text on as many computers and operating systems as you wish with your license. More...
Which is why Notepad++ is the program usually used for modding PDS games.
 
  • 1
Reactions:
So I take it there will be no targeted decisions, or character decisions... I guess I might need to avoid doing any comprehensive modding for Stellaris untill we have an RPG expansion like WOL...
 
And the ability to use characters as a deliberate foci point for events. While with scripted triggers it may be possible to semi achieve some functionality, the ability of a player to do so is long gone till we have a greater expansion to do so via character interaction.
 
I dont fully udnerstand what you are talking about. Can you elaborate, please?

Targeted decisions in CK2 was right clicking a character and activating an event chain, which provides some reactivity/rewards and provides results based on other activated event chains connected to each other like a tree's roots and branches.

It had specific gameplay results such as finding blackmail on vassals via the spy option.

As for editing the text script, I use Notepad ++, which highlights the closed loop of brackets, important for when the nestling goes beyond 5.
 
  • 1
Reactions:
Are all the keywords available somewhere? There are some language files for Notepad++, which I assume will be updated for Stellaris soon, but as I use Atom I'd be very happy for either an Atom-package, or just some documentation on syntax and keywords, and I could make a (possibly horrible) attempt to make one myself.
 
There is a console command to dump all available triggers and effects to a text file. Unfortunately these triggers and effects are sometimes improperly documented (or their usage has changed since the code comment was written), so you may find some misleading information in the raw text dump. I expect the wiki will rectify this shortly after release, and I hope to put in some work towards that goal myself.

We will be posting NP++ and Sublime syntax files as-is (no official support) in the coming days, it should be fairly trivial to reverse-engineer those for use with any text editor of your choice.
 
  • 3
  • 2
Reactions:
We will be posting NP++ and Sublime syntax files as-is (no official support) in the coming days, it should be fairly trivial to reverse-engineer those for use with any text editor of your choice.

Is there an ETA on those syntax files for NP++ by chance? :p
 
Can the "On_Spawn" be used in Tile-Blocker mod?

IE On_Spawn Volcano -> Do X (Add deposit maybe?)
Depends on what you want to do, in this case on_spawn is unique to Anomalies. I'm not familiar with this tile blocker mod, but on_spawn gives you a scope on the surveyed planet, so you can directly do stuff to the planet based on what tile blockers are on it, for example.

So,
Code:
on_spawn = {
    if = {
        limit = {
            any_tile = { has_blocker = tb_volcano }
        }
        random_tile = {
            limit = { has_blocker = tb_volcano }
            clear_deposits = yes
            add_deposit = d_vast_mineral_deposit
        }
    }
}
would add a mineral deposit to a volcano tile, if 1) the anomaly category spawns on the planet and 2) there is a volcano tile blocker on the planet. It won't, however, always add a mineral deposit to every planet with a volcano tile blocker, you'd have to use regular events for that.

Is there an ETA on those syntax files for NP++ by chance? :p
Slight delay due to technical issues. Thanks for your continued patience!
 
  • 2
Reactions:
Depends on what you want to do, in this case on_spawn is unique to Anomalies. I'm not familiar with this tile blocker mod, but on_spawn gives you a scope on the surveyed planet, so you can directly do stuff to the planet based on what tile blockers are on it, for example.

So,
Code:
on_spawn = {
    if = {
        limit = {
            any_tile = { has_blocker = tb_volcano }
        }
        random_tile = {
            limit = { has_blocker = tb_volcano }
            clear_deposits = yes
            add_deposit = d_vast_mineral_deposit
        }
    }
}
would add a mineral deposit to a volcano tile, if 1) the anomaly category spawns on the planet and 2) there is a volcano tile blocker on the planet. It won't, however, always add a mineral deposit to every planet with a volcano tile blocker, you'd have to use regular events for that.


Slight delay due to technical issues. Thanks for your continued patience!


Thank for that awesome answer :)

Is it something that will be added eventually? I mean an OnSpawn or an AdditonalEffect on everything so that we can change beahviour more deeply instead of mostly balancing and adding "fluff" content?

What I was looking to achieve was to add a permanent bonus on every tile that had a blocker (removed or not) to one of the 5 ressources according to the blocker's difficulty
 
Slight delay due to technical issues. Thanks for your continued patience!

Good to hear that it's just a delay, was worried you forgot about us :p
But seriously thanks for actually choosing to make that tool available to us, you have no idea how immensely easier something as simple as syntax files makes things for modders with disabilities (myself included)
 
Hey,
I am a total beginnen in moding so i tried to understand the creation of an anomaly , but
i had a problem to understand the first step. My problem is the following:

Step 1 - 00_anomaly_categories
meaning, i don't know what it does.

modifier = {
add = 2
from {

has_ship_flag = dev_diary_flag
}
}

I know that this is a trigger for the anomaly but i have no clue what has_ship_flag and dev_diary_flag stands for.
Thanks in advance forum for our help .
 
Hey,
I am a total beginnen in moding so i tried to understand the creation of an anomaly , but
i had a problem to understand the first step. My problem is the following:

Step 1 - 00_anomaly_categories
meaning, i don't know what it does.

modifier = {
add = 2
from {

has_ship_flag = dev_diary_flag
}
}

I know that this is a trigger for the anomaly but i have no clue what has_ship_flag and dev_diary_flag stands for.
Thanks in advance forum for our help .

Re: 00_anomaly_categories, think of an anomaly category it like a box full of events. It uses the spawn_chance modifier to see what planets it can be discovered on, and then 00_anomalies decides what event from the box should be picked, and then anomaly_events is, well, the event itself.

In the example in the original post, the anomaly category's spawn chance starts at 0, is increased to 3 if the planet is an asteroid (is_asteroid = yes | add = 3), and then the spawn chance is doubled (factor = 2) if the ship that surveys the planet (this is stored in the FROM scope) has had the "dev_diary_flag" (an arbitrary name) set by some previous event. Flags are just bits of text you assign to objects in the game to keep track of them; there are country flags, ship flags, planet flags, pop flags and so on.

That event might have looked something like this:
Code:
country_event = {
    id = dev_diary.500
    title = "Dev Diary Flag Found!"
    desc = "One of our Science Ships has found something that will make it easier to discover certain Anomalies."
    picture = GFX_evt_psionics

    trigger = {
        NOT = {
            has_country_flag = found_dev_flag
        }
        any_owned_ship = {
            is_ship_class = shipclass_science_ship
        }
    }

    mean_time_to_happen = {
        months = 24
    }

    immediate = {
        set_country_flag = found_dev_flag
    }

    option = {
        name = "Great!"
        random_owned_ship = {
            limit = {
                is_ship_class = shipclass_science_ship
            }
            set_ship_flag = dev_diary_flag
        }
    }
}
I don't know if that answers your question or is particularly helpful - modding takes some getting used to when you're just starting out. The EU4 wiki is a pretty good place to read up on how the scripting language works, but feel free to post here if you have any other questions.
 
  • 2
Reactions: