The best kittens, technology, and video games blog in the world.

Tuesday, March 27, 2018

Let's make Lucca Mission Pack mod for Europa Universalis 4

In this post we'll create a very simple EU4 mission pack mod. I'll use Lucca as an example. It will be more fun if you pick another country, follow along these step, while customizing your mod a bit.

This is a beginner level tutorial. It doesn't require any skills beyond use of text editors. Because we'll need nonstandard encoding support to deal with localization files, I recommend Visual Studio Code, but other than this one step pretty much any editor will do.

For some more details on how mission system works, you might want to check my previous post. To just learn the new mission system from a player's perspective, watch DDRJake's videos.

There's more to the mission system than can be covered in one tutorial, so I want to write another one covering more advanced subject later.

If you have any questions about these steps, ask them here, or reach me on any social media.

Create mod descriptor

For more complex mods it's best to keep the files somewhere safe and version-controlled, like on github, but we're starting small now so I'm going to skip this step.

Open C:\Users\(YourName)\Documents\Paradox Interactive\Europa Universalis IV\mod or equivalent.

You need to create a descriptor for your mod, for this simple mod create lucca_mission_pack.mod. For very simple mod we'll only have three lines, like this:

name = "Lucca Mission Pack"
path = "mod/lucca_mission_pack"
supported_version = 1.25

name is what's going to appear in the launcher.

path is name of the folder where we'll create mod files. Create the lucca_mission_pack folder. It's best if it has the same name as mod descriptor (except for .mod extension).

supported_version tells the game which exact version the mod supports. It's used mainly to warn the user in game launcher, that the mod didn't get updated for the next version yet.

If you're following these steps in the future, change supported_version to whatever's appropriate.

At this point you can start game launcher, and see that your mod is on the list. If it's not, something went wrong with the mod descriptor and you should check it. No point running the game yet, as the mod does nothing.

Create empty mod files

Inside C:\Users\(YourName)\Documents\Paradox Interactive\Europa Universalis IV\mod\ create lucca_mission_pack folder, inside it lucca_mission_pack\missions subfolder, and inside that lucca_mission_pack\missions\Lucca_Missions.txt file.

The name of the file doesn't matter as long as it ends in .txt and doesn't conflict with any in the game. For example if you call it Hungary_Missions.txt you'll be overwriting Hungary's missions from base game - but if you call it MudkipIsBestPokemon.txt it's just fine.

Location of the file definitely matters, so if you make mistakes the mod will not load correctly.

Planning the missions

If you take a short break from modding and run the game, you'll see that Lucca's existing missions are:
  • column 1 - empty
  • column 2 - generic military missions
  • column 3 - generic diplomatic missions
  • column 4 - generic administrative missions
  • column 5 - regional missions for European countries and HRE minors
It's going to be similar for most countries, except sometimes column 5 will be empty as well.

For a small mission pack we'll create a single mission group, and put it into column 1.

For a bigger mission pack we might replace column 5 as well, add missions on the bottom, or replace some of the generic missions.

Column choice is not just for the interface, as multiple mission groups going into same columns might replace each other.

Create a mission group

Let's open lucca_mission_pack\missions\Lucca_Missions.txt in text editor and setup a group for the missions:

lucca_mission_group = {
  slot = 1
  generic = no
  ai = yes
  potential = {
    tag = LUC
  }
  has_country_shield = yes

  # missions will go here
}

What we're setting up here:
  • in column 1 (columns are called "slots")
  • not generic. This is used as priority if multiple mission groups try to use same column.
  • also available for the AI. Usually should be yes.
  • available if your tag at campaign start is LUC (Lucca). List of tags is on the wiki, usually it's first three letters of country name.
  • that we want to display country shield in the interface. This is purely graphical, but country-specific missions usually do, so let's be consistent here.
For potential you can choose based on other criteria like religion, culture, capital location etc., but they're only checked on campaign start, or when explicitly told so by an event or decision.

In vanilla game, they only change on some nation forming decisions. If you start as Lucca and form Italy, you'll keep your original missions since Italy decision doesn't change missions. If you form France, you'll get your mission pack replaced.

Creating out first mission

Time to create the first mission. We'll start with something very simple. The goal is to build a temple  in Lucca, and as a reward you'll get 200 admin points.

Open missions files, and in the place we left for missions, add this:

lucca_temple_in_capital = {
  icon = mission_european_church
  required_missions = {  }
  trigger = {
    2980 = {
      has_building = temple
    }
  }
  effect = {
    add_adm_power = 200
  }
}

What it does:
  • it sets mission's ID to lucca_temple_in_capital. This needs to be unique, and will be used for localization.
  • it uses icon mission_european_church. This is purely graphical, and I wouldn't worry too much about the icons until you're happy with how your missions work.
  • it sets lists of required previous missions to empty
  • it sets mission completion condition (trigger) - province 2980 (Lucca) has temple. You can find list of province IDs on the wiki.
  • it sets mission reward (effect) - 200 admin power points
At this point it makes sense to launch the game, and check that everything is indeed working.

Try writing something similar for country of your choice.

Start mission chain

OK, so that worked. Let's create some more missions. We'll put them all in same file, one under the other.

For design of a mission chain to be good, they should somehow connect with each other. Here's an idea for a small chain:
  • first we want to build up our capital. As a reward we get some permanent claims on nearby lands.
  • second we want to conquer claims we got in previous mission, as a reward we get some points of multiple types
  • third, we want Lucca to rival one of the big baddies, proving that we're ready. It will require completing previous mission too, so we don't need to check it again. As reward we get 25 year +10% discipline bonus for more enjoyable fights agains our new rivals.
This covers the most common cases - checking that we conquered something, getting rewarded by permanent claims, various types of points, or temporary bonuses. As well as a few extras.

Plan a similar chain for your own country, for now using similar kinds of effects.

Make Lucca Great Again

make_lucca_great_again = {
  icon = mission_high_income
  required_missions = {  }
  trigger = {
    capital_scope = {
      development = 25
      renaissance = 100
    }
  }
  effect = {
    tuscany_area = {
      limit = {
        NOT = { is_core = ROOT }
        NOT = { owned_by = ROOT }
      }
      add_permanent_claim = ROOT
    }
  }
}

Step by step:
  • it sets mission's ID to make_lucca_great_again. We'll use it as key to localization later.
  • it sets required missions to empty
  • to succeed it requires that our capital has at least 25 development and 100% Renaissance institution.  I could have used 2980 (specifically province of Lucca) instead of capital_scope (whichever province is our capital) here. We don't expect capital to move, so in this case it doesn't matter, but both selectors have their uses.
  • as reward we go through all provinces in tuscany_area and add permanent claim to them, unless they're already owned by us or have our core. Areas are defied in map\area.txt file. You don't need to use areas, you can just list provinces separately (the syntax is however a bit different).
So we can load the game, and see that it works.

Try something similar for your country. Pick some capital growth conditions, pick a relevant area from map\area.txt (most areas correspond to a state), and try it in the game.

Luccan Tuscany

We got our claims, so we ought to follow it with a mission checking that we actually conquered what we wanted.

As reward we'll just get some points, but of course you can chain a lot of conquer this area - get claims on next area missions. Making them branch in a way that looks good in the UI is a bit more advanced topic, but just making this chain as long as you'd like should be straightforward.

luccan_tuscany = {
  icon = mission_cannons_firing
  required_missions = { make_lucca_great_again }
  provinces_to_highlight = {
    area = tuscany_area
    NOT = {
      country_or_non_sovereign_subject_holds = ROOT
    }
  }
  trigger = {
    tuscany_area = {
      type = all
      country_or_non_sovereign_subject_holds = ROOT
    }
  }
  effect = {
    add_mil_power = 200
    add_legitimacy = 10
    add_republican_tradition = 10
  }
}

Let's go through it step by step:
  • it sets mission ID to luccan_tuscany
  • it sets mission icon to mission_canonns_firing
  • it requires completion of make_lucca_great_again first. If you arrange mission chain so that next mission depends on one just above it (or one row down and then sideways), the UI will draw pretty arrows to make it clearer how missions are related. Other dependencies still work, just won't look quite as nice.
  • it sets province highlight when mission is mouseovered to all provinces in  provinces_to_highlight which are not owned by us or a (nontributary) subject
  • it sets success condition of owning all provinces in tuscany_area by us or our (nontributary) subject. In addition you still need to do make_lucca_great_again first. 
  • as a reward, we get 200 mil power points, 10 legitimacy, and 10 republican tradition. Depending on government type of the country, the tooltip will only show relevant rewards, so if Lucca is a republic, the tooltip will show "200 mil points, 10 republican tradition", since legitimacy doesn't matter.
Try something similar for your country, customizing rewards, and using the same area you used in your previous mission.

You can also chain claims in a few missions.

Lucca in the Major League

So now that our Lucca conquered Tuscany, let's give it a more ambitious goal - rivalling one of big three regional baddies. As a reward we'll give it a bonus.

lucca_in_the_major_league = {
  icon = mission_cannons_firing
  required_missions = { luccan_tuscany }
  trigger = {
    OR = {
      is_rival = FRA
      is_rival = HAB
      is_rival = TUR
    }
  }
  effect = {
    add_country_modifier = {
      name = lucca_discipline
      duration = 9125 # 25 years
    }
  }
}

Step by step:
  • it sets mission ID to lucca_in_the_major_league
  • it sets mission icon to mission_cannons_firing, I just used the same one as before but feel free to pick something more creative. In the end it's just a minor graphical embellishment.
  • it requires completing luccan_tuscany first, which in turns requires make_lucca_great_again. Because we put those missions in proper order, they'll be connected by nice arrows in the UI.
  • to succeed we need to rival France (FRA), Austria (HAB), or Ottomans (TUR). You can see that in this case tags are not quite what one might guess, presumably as a leftover from EU2 or maybe even EU1 for all I know. There's no need to check that we also did our conquests, as previous mission in the chain checks that.
  • as a reward we get lucca_discipline modifier, for 9125 days (25 years). We just need to tell the game what that means in another file.

Creating modifiers

This is not strictly mission modding, but modifiers are about the most common mission reward, so it's useful to know how to create them.

In our mod create text file lucca_mission_pack\common\static_modifiers\lucca_missions.txt, and put this inside:

lucca_discipline = {
  discipline = 0.10
}

And that's it. You can check common\static_modifiers\00_static_modifiers.txt in the game for more examples.

You can put as many modifiers in single file as you want, just create your own instead of putting edited 00_static_modifiers.txt in your mod.

How EU4 localization works

EU4 localization files use .yml extension, but it's actually custom format somewhat inspired by YAML format, not the real thing.

All localization files live in localisation folder, with files sorted by language.

Each file has header like l_english, and a list of IDs and their localized strings. All the files need to start with the non-standard UTF-8 BOM. If you create a normal text file without BOM, it will be ignored by the game.

If you want your mod to support some other languages, it should be fairly obvious which file names and header to use instead.

As a bonus annoyance, Paradox can't decide between American and British spelling inside their files. Folder name is called "localisation" in all their games, but then HoI4 uses localization_key to choose entry from it. It's very easy to get confused.

Localization

So if you followed along, you have a working mod with 4 missions for your country, or however many you created. There's just one more issue - the missions all use internal development IDs instead of human readable names and descriptions.

There's 9 things we need to edit: 4 mission names, 4 mission descriptions, and name of our modifier.

Those IDs we gave the missions, they all need to be unique, so it makes sense to prefix it by name of the country or mission pack to avoid accidental conflicts, but we can localize them any way we want.

For every mission ID_title is its name key, and ID_desc is the description key. Static modifiers's name keys are just their IDs.

What we'd like to do is create lucca_mission_pack\localisation\lucca_missions_l_english.yml and put the following code there:

l_english:
 lucca_temple_in_capital_title: "Builde Temple in Lucca"
 lucca_temple_in_capital_desc: "As all good Italians, Luccans need a place to pray."
 make_lucca_great_again_title: "Make Lucca Great Again"
 make_lucca_great_again_desc: "Our capital should be the greatest city in Italy since ancient Rome"
 luccan_tuscany: "Conquest of Tuscany"
 luccan_tuscany: "Some Florentines, I assume, are good people, but they'll still be better off under Luccan rule."
 lucca_in_the_major_league_title: "Joining the Major League"
 lucca_in_the_major_league_desc: "Show the world that Lucca is now one of the great powers by rivaling a nearby Great Power."
 lucca_discipline: "Lucca's Dispciplined Army"

The game uses single space indentation, but it's not too strict about this.

What it is extremely strict about is the BOM, so if you use Visual Studio Code, choose "UTF-8 with BOM" as encoding for your new file on the bottom bar. For other editors find appropriate instructions, but many don't support UTF-8 BOM at all.

If you don't know how to get the BOM working, one think you can try is copying existing localization file, deleting everything except the first line (which might look like one broken character followed by l_english: tag) and just replacing the rest of the file with your content.

Great Success

If everything worked, you should see something like this:


Debugging

If you see image like above, but using internal IDs instead of English text, then mod is probably file , but localization is wrong. Most common issue is UTF-8 BOM.

If you see some total mess in first column, like wrong number of missions with silly names like icon, you probably made a typo in mod files, and game .

Open C:\Users\(YourName)\Documents\Paradox Interactive\Europa Universalis IV\logs or equivalent and search for hints. errors.log might give you line number where game got confused.

If mod was in the launcher, but when you play the game you see empty first column, mod probably didn't load at all. Or it did, but your potential tag is wrong, and game doesn't think these missions are appropriate for currently selected country. Double check errors.log and list of tags.

If you don't even see the mod is mod launcher, then mod descriptor file is probably wrong or in wrong place.

Or if none of that works, just ask, but remote debugging is pretty hard. I'll have easier time answering "I want mission to do X when Y" questions than "it doesn't work, what do?" questions.

Download the mod

It's best if you followed the tutorial and wrote it on your own, but if you want to check the files, all the files are on github.

3 comments:

Angeredkey said...

I am trying to do this mod for some new missions for the Palatinate, and some of the stuff seems to not be working. The mission for building a church in the capital come up fine, along with the 1st mission at the start of the chain, which I customized, but after that, the chain doesn't work and none of the other missions show up. I can show you the code I used.
Here:

palatinate_mission_group = {
slot = 1
generic = no
ai = yes
potential = {
tag = PAL
}
has_country_shield = yes

# missions will go here
# Build temple in capital
palatinate_temple_in_capital = {
icon = mission_european_church
required_missions = { }
trigger = {
1761 = {
has_building = temple
}
}
effect = {
add_adm_power = 100
}
}

develop_oberpfalz = {
icon = mission_high_income
required_missions = { }
trigger = {
1757 = {
development = 20
}
}
effect = {
palatinate_area = {
limit = {
NOT = { is_core = ROOT }
NOT = { owned_by = ROOT }
}
add_permanent_claim = ROOT

franconia_area = {
limit = {
NOT = { is_core = ROOT }
NOT = { owned_by = ROOT }
}
add_permanent_claim = ROOT
}
}
}

unify_the_palatinate = {
icon = mission_cannons_firing
required_missions = { develop_oberpfalz }
provinces_to_highlight = {
area = palatinate_area
area = franconia_area
NOT = {
country_or_non_sovereign_subject_holds = ROOT
}
}
trigger = {
palatinate_area = {
type = all
country_or_non_sovereign_subject_holds = ROOT

franconia_area = {
type = all
country_or_non_sovereign_subject_holds = ROOT
}
}
effect = {
add_mil_power = 200
add_legitimacy = 10
add_republican_tradition = 10
add_prestige = 20
}
}

palatinate_big = {
icon = mission_cannons_firing
required_missions = { unify_the_palatinate }
trigger = {
OR = {
is_rival = BOH
is_rival = HAB
is_rival = DAN
}
}
effect = {
add_country_modifier = {
name = palatinate_discipline
duration = 9125 # 25 years
}
}
}
}

Angeredkey said...

By the way, the brackets are spaced out correctly, it's just not showing here.

taw said...

Angeredkey : For the most obvious thing, you forgot to close palatinate_area = { ... in both cases. You have 34 "{" but only 32 "}".