Imperator to CKII Converter - Development Thread (not for troubleshooting)

  • 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.
Btw hows this project going :)
I had planned to start trying to create the converter for the beginning of the year, unfortunately due to hardware issues preventing me from starting to do the project. As soon as I have my new hardware and my computer is operational I will start making the converter.

Nevertheless, I do not promise to succeed in doing it alone so if people want to help me in the reflection and its implementation I am a taker
 
Hi all. I started looking at the converter, thinking about how to translate data from Imperator to CK2.

I have not yet done anything in terms of creating the converter and I do not know when I will start doing it so no need to wonder when it will be available because I do not even know if I will succeed in doing it .

However, I am making this message so that you can give me your opinion on what I have done, namely the cultures and the titles (tier1 / tier2 and tier end) others will come.

### CULTURES
imperator = { crusader kings II } - add culture in CK2
# Aksumite group
puntic = {somali}
blemmyan = {nubian}
aksumite = {ethiopian}

# BACTRIAN
bactrian = {sogdian}
sogdian = {sogdian}
shule = {sogdian}
phryni = {sogdian}
tayuan = {sogdian}
wusun = {sogdian}

#BALTIC
aestian = {} - aestian
-CK2
lettigallish
lithuanian
prussian

#BRITANIC
caledonian = {pictish}
iceni = {breton}
trinovantian = {breton}
cantian = {breton}
durotrigan = {breton}
dumnonian = {breton}
dobunnian = {breton}
demetian = {welsh}
ordovitian = {welsh}
coritani = {breton}
cornovian = {breton}
brigantic = {breton}
votadinian = {pictish}
damnonian = {pictish}
taexalian = {pictish}

#CELTIBERIAN
caristian = {basque}
celtician = {} - celtiberian
lusitanian = {} - celtiberian
vettonian = {} - celtiberian
carpetanian = {} - celtiberian
celtiberian = {} - celtiberian
lobetanian = {} - celtiberian
sedetanian = {} - celtiberian
vaccaeian = {} - celtiberian
asturian = {} - celtiberian
callaecian = {} - celtiberian
vardulian = {basque}

#DACIAN
dacian = {} - dacian
getian = {} - dacian
triballoi = {} - dacian
moesi = {} - dacian
harpii = {} - dacian

#EAST LEVANTINE
aramaic = {} - aramaic
assyrian = {assyrian}
babylonian = {} - babylonian

#GAELLIC
hibernian = {irish}
manavian = {irish}

#GALLIC
belgic = {} - gaulish
aremorican = {} - gaulish
lepontic = {} - lepontique
vindelician = {} - gaulish
helvetian = {} - gaulish
belgae = {} - gaulish
biturigan = {} - gaulish
volcae = {occitan}
noric = {} - Noric
salluvian = {occitan}
haedui = {} - gaulish
carnuti = {} - gaulish
treverian = {} - gaulish
santonian = {} - gaulish
lemovician = {} - gaulish
pictonian = {} - gaulish
aulercian = {} - gaulish
celtic_pannonian = {} - pannonian
arverni = {} - gaulish
carnian = {} - gaulish
letobician = {} - gaulish
boian = {bohemian}

#GERMANIC
saxonian = {old_saxon}
anglian = {saxon}
reudingian = {norse}
teutonian = {german}
cimbrian = {norse}
herulian = {norse}
rugian = {norse}
helveconian = {norse}
bastarnae = {norse}
gutones = {crimean_gothic}
suiones = {norse}
vandal = {} - vandal
raumarician = {norse}
irminonic = {norse}
istvaeonic = {norse}
ingvaeonic = {norse}
suebian = {suebi}
lemovian = {norse}
gothonic = {norse}
frisian = {frisian}
-CK2
german a créer dans IR
lombard a créer dans IR
old_frankish a créer dans IR

#HELLENIQUE
macedonian = {} - macedonian
parthinian = {greek}
taulantian = {dalmatian}
abrian = {greek}
dardanian = {} - dardanian
italiotian = {greek}
thracian = {} - thrasian
cretan = {greek}
greco_illyrian = {dalmatian}
achaean = {greek}
epirote = {greek}
boeotian = {greek}
peloponnesian = {} - peloponnesian
athenian = {} - athenian
euboean = {greek}
aetolian = {greek}
aegean = {greek}
greco_pontic = {greek}
lydian = {greek}
troan = {greek}
ionian = {greek}
bithynian = {greek}
cypriot = {greek}
thessalian = {greek}
bosporan = {greek}
syracusan = {greek}
cyrenaican = {greek}
massalian = {greek}
-CK2
alan a créer dans IR
coptic a créer dans IR

#IBERIAN
oretanian = {} - iberian
contestanian = {} - iberian
bastetanian = {} - iberian
ilercavonian = {} - iberian
edetanian = {} - iberian
lacetanian = {} - iberian
indiketian = {} - iberian
ausetanian = {} - iberian
turdetanian = {} - iberian
couneian = {} - iberian
turdulian = {} - iberian
-CK2
visigothic a créer dans IR

#INDIAN
kannadan = {kannada}
tamil = {tamil}
telugu = {telugu}

# ARYAN
vidharban = {marathi}
saurashtran = {gujurati}
kalingan = {oriya}
lankan = {sinhala}
maharashtran = {marathi}
magadhi = {nepali}
gandhari = {panjabi}
shauraseni = {hindustani}
avanti = {rajput}
kamarupi = {assamese}
bangli = {bengali}
sindhi = {sindhi}

#LATIN
roman = {roman}
osco_umbrian = {italian}
messapic = {italian}
venetic = {italian}
ligurian = {italian}
etruscan = {} - etruscan
rhaetian = {} - rhaetian
umbrian = {italian}
samnite = {italian}
messapian = {italian}
sardonian = {sardinian}
histrian = {italian}
liburnian = {italian}
siculian = {italian}
bruttian = {italian}
lucanian = {italian}
catari = {italian}

#NORTH AFRICA
egyptian = {} - old_egyptian
meroitic = {} - meroitic
garamantic = {} - meroitic

#NUMIDIAN
numidian = {} - numidian
berber = {} - numidian
gaetulian = {} - numidian
socossian = {} - numidian
massaesylian = {} - numidian
massylian = {} - numidian

#PERSIAN
cappadocian = {persian}
carian = {persian}
phrygian = {} - phrygian
lycaonian = {persian}
isaurian = {persian}
paphlagonian = {persian}
lycian = {persian}
pamphylian = {persian}
mysian = {persian}
pontic = {} - pontic
cilician = {persian}
armenian = {armenian}
elamite = {} - elamite
albanian = {persian}
ibero = {georgian}
colchian = {georgian}
utian = {persian}
pasargadi = {persian}
sagartian = {persian}
uxian = {persian}
amardian = {persian}
sarangian = {persian}
pactyan = {persian}
cossian = {persian}
cadusian = {persian}
parthian = {} - parthian
parecanian = {baloch}
carmanian = {persian}
median = {persian} - median
gedrosian = {baloch}
hyrcanian = {persian}
sattagydian = {persian}
-CK2
kurdish a créer dans IR
afghan a créer dans IR

#PROTO EUROPEAN
vasconian = {basque}
aquitani = {basque}
nuragic = {} - nuragic
autrigonian = {basque}
ilergetian = {basque}
corsian = {} - corsian
cantabrian = {basque}

#SCYTHIAN
scythian = {} - scythian
sarmatian = {} - samartian
roxolanian = {tocharian}
kharesmian = {} - scythian
margian = {} - scythian
derbiccan = {} - scythian
saka = {saka}
tocharian = {tocharian}
yuezhi = {tocharian}
sindi = {} - samartian
siraci = {tocharian}
maeotian = {} - samartian

#SOUTH LEVANTINE
hadhrami = {bedouin_arabic}
qatabanian = {bedouin_arabic}
sheban = {bedouin_arabic}
minaean = {bedouin_arabic}
himjar = {bedouin_arabic}
makan = {bedouin_arabic}
taif = {bedouin_arabic}
thamudi = {bedouin_arabic}
qedarite = {bedouin_arabic}

#TIBETAN
zhangzhung = {zhangzhung}
sumpa = {sumpa}
yarlung = {bodpa}
tsang = {bodpa}

#WEST LEVANTINE
carthaginian = {} - carthaginian
phoenician = {} - phoenician
hebrew = {} - hebrew
nabatean = {} - nabatean

### TITLE
imperator - CK2
tag = ACH #achaea - d_achaia
tag = AQI #aquitania - d_aquitaine
tag = ARK #arcadia - Add in CK2
tag = ASX #Aremorica - d_brittany
tag = ATU #Asturia - d_asturias
tag = CNN #Cantabria - d_leon
tag = CPT #Carpetania - d_toledo
tag = CBR #Celtiberia - Add in CK2
tag = CYP #Cyprus - d_cyprus
tag = GLL #Gallaecia - d_galicia
tag = LST #Lusitania - d_porto
tag = VTT #Vettonia - d_badajoz
tag = SBN #Suebia - d_swabia
tag = AGO #Argolis - d_athens
tag = AUA #Aestuia - Add in CK2
tag = ARM #armenia - k_armenia
tag = ASR #Assyria - k_mesopotamia
tag = BBY #Babylon - k_iraq
tag = BGG #Belgia - Add in CK2
tag = BRA #Pretania - k_england
tag = CCC #Caledonian Confederacy - k_scotland
tag = CCI #Cilicia - d_armenia_minor
tag = 1KR #Crete - d_krete
tag = DRA #Dravidia - e_deccan
tag = EGY #Egypt - k_egypt
tag = GLT #Galatia - Add in CK2
tag = HVL #Helvetia - Add in CK2
tag = ILL #Illyria - Add in CK2
tag = MAC #Macedon - Add in CK2
tag = MEE #Media - k_persia
tag = NRM #Noricum - Add in CK2
tag = NUM #Numidia - k_africa
tag = PRY #Phrygia - Add in CK2
tag = PON #Pontus - k_trebizond
tag = SII #Sicily - k_sicily
tag = SYA #Syria - k_syria
tag = YMN #Yamnat - k_yemen
tag = SEL #Seleucids - Add in CK2
tag = TRE #Thrace - k_thrace
tag = XXS #Saxonia - k_saxony
tag = HIB #Hibernia - k_ireland
tag = YZI #Yuezhi - k_khotan
tag = ROM #Rome - e_roman_empire
tag = AXM #Axum - e_abyssinia
tag = AIO #Albion - e_britannia
tag = ALX #Alexander's Empire - Add in CK2
tag = BHA #Bharatavarsha - e_india
tag = DAC #Dacia - e_carpathia
tag = GAU #Gaul - Add in CK2
tag = HLA #Hellenic League - e_byzantium
tag = HBR #Greater Iberia - e_spain
tag = PER #Persia/Achaemenid - e_persia
tag = PPN #Phoenicia - Add in CK2
tag = PAR #Parthia - Add in CK2
tag = MGG #Magna Graecia - Add in CK2
tag = KHN #Kushan - e_rajastan
tag = DLA #Delian League - k_grece

If you have any suggestions I am interested
 
  • 1Like
Reactions:
Wow seeing how many of this cultures were simply reduced to greek is bizzare, although I think it's kind of necesarry in regards of how detailed is I:R map in comparison to that of CK II
 
Wow seeing how many of this cultures were simply reduced to greek is bizzare, although I think it's kind of necesarry in regards of how detailed is I:R map in comparison to that of CK II
I chose this solution because I think it is the one that was chosen by CKII during its creation. I cannot see a Greek or Italian unity even if these areas have been in the Roman Empire for almost 1000 years. I just keep athenes and spartes because they are interesting cultures and they are in EUIV, same for Etruscans.
I don't rule out adding other specific cultures that might be cool for CKII
 
Since we don't have a converter yet, and may not get one for CK2, I'm curious if there is a creative way to use the Holy Fury map generator and Ruler Designer to sort of, somewhat, simulate an approximate conversion? Or maybe we can mod the map generator and ruler designer to take a big shortcut. It wouldn't be a true conversion like the other converters but it could still be a fun way to add in a sense of continuity for someone who wants to play an extended campaign of Imperator and then start in CK2. After all, some players continue from HoI4 to Stellaris and that isn't a true conversion either, but a simulated one.
 
Last edited:
Since we don't have a converter yet, and may not get one for CK2, I'm curious if there is a creative way to use the Holy Fury map generator and Ruler Designer to sort of, somewhat, simulate an approximate conversion? Or maybe we can mod the map generator and ruler designer to take a big shortcut. It wouldn't be a true conversion like the other converters but it could still be a fun way to add in a sense of continuity for someone who wants to play an extended campaign of Imperator and then start in CK2. After all, some players continue from HoI4 to Stellaris and that isn't a true conversion either, but a simulated one.

The converter will arrive. Even if I haven't started creating the code yet, I'm thinking about how to do it for cultures, religions, titles, etc.

If on the other hand you want to lend a hand you are welcome (especially for the provinces, because this is the biggest problem for the moment).
 
The converter will arrive. Even if I haven't started creating the code yet, I'm thinking about how to do it for cultures, religions, titles, etc.

If on the other hand you want to lend a hand you are welcome (especially for the provinces, because this is the biggest problem for the moment).
I tried mapping northern Ireland here. It's fun to try and figure out how the land fits together most accurately, but time consuming. Converting is a challenge if the counties are owned by different tags in I:R, since there are multiple I:R cities for [nearly] every CK2 county. How do other converters handle that?

I guess one way would be to map a main holding city for each county, and then if there are other I:R tags with cities mapped to that same county they could take other holdings (towns or castles) in that county.

Edit: Updated the file to finish Ireland (and added the Isle of Man too. Pretty easy that one). Also mapped Blekinge in Denmark because that's CK2's province ID#6 for some reason.
 

Attachments

  • Imperator to CK2 province IDs.csv
    1,1 KB · Views: 24
Last edited:
Hi all.
Today I worked on the Imperator dynasties for CK II in order to keep moving forward on the converter.

The problem, however, is that the dynasties in imperator are pretty weird and I would like some help on how you would go about determining which dynasty goes to which culture.

I specify that the imperator dynasty present in CK II were kept aside and I noted the numbers so all the dynasties which are present in the file with in culture the cultural group are only present in imperator.

I await your feedback and suggestion.

FYI regarding the converter I have not yet started to work on the converter as such, just on the files which are:
-crops (can be improved but I think I made the right choices)
-religions
-titles (not finished yet)
- the dynasties (not finished yet)

I leave you the files that I made for the moment
 

Attachments

  • IMPERATOR_dynasties.txt
    212,9 KB · Views: 7
  • CK2 - imperator.txt
    2,5 KB · Views: 10
  • nouvelles religions.txt
    8,2 KB · Views: 6
Hi all.

I come back to you to share with you what I started to do to prepare the converter to CKII.

So everything is not yet defined, things remain to be created for CKII, but also some like the lines require more work from me because my English is poor level I must have fun translating the lines of ck2 and those of imperator.

If some are interested in helping do not hesitate, the hardest part (porting the map) remains to be done and will require a bit of time.
 

Attachments

  • Imperator vers CK2.rar
    31,2 KB · Views: 5
For imformation for all. I have start programation of the converter imperator to ck2. It just the begining. If you can help me for the map contact me. No date for delivery i learn the language c++
 
Making our own files

By the time a converter has been fully created, it's thousands of lines of code. As you might imagine, that's a lot to look through. We can better organize it by moving some of the code to extra files.

In Visual Studio, right-click on the Project in the Solution Explorer. It should open a long menu. Somewhere it says Add. Hover over that, then choose New Item in the menu that opens from there.

Choose Header File (.h), then give it the name Outputter.h and click Add.

In this new file, add the code
Code:
#ifndef OUTPUTTER_H
#define OUTPUTTER_H



#endif // OUTPUTTER_H

This ensures only one copy of this gets loaded at any point. Don't worry about the details.

Move your output function definitions from Main.cpp to the blank space in the middle of this file.

Note, however, that your functions use string. So also add this to the top of the file:
Code:
#include <string>

Now at the top of Main.cpp, add the line
Code:
#include "Outputter.h"

This allows Main.cpp to see Outputter.h and the function definitions in it.

Now follow the same process to add a new file, but choose C++ File (.cpp) and name it Outputter.cpp.

At the top add the line
Code:
#include "Outputter.h"
so it can also see the function definitions.

Now move the function definitions from Main.cpp into this file. Main.cpp should now only have includes and the main() function.
Main also no longer needs some of those includes. Move the ones for filesystem and fstream into Outputter.cpp.

Build everything and test it. It should work the same as before, but it's more organized.
 
Last edited:
Build everything and test it. It should work the same as before, but it's more organized.
I'm not sure what to move into each but I did something wrong. My Main.cpp is
Code:
#include "Outputter.h"
#include <filesystem>
#include <fstream>
#include <string>


int main(const int argc, const char* argv[])
{
    std::string outputName{ "CK2Tester" };
    if (argc > 1)
        {
            outputName = argv[1];
        }
    outputModPath(outputName);
    outputModFile(outputName);
}

Outputter.h is
Code:
#ifndef OUTPUTTER_H
#define OUTPUTTER_H

void outputModPath(const std::string& outputName);
void outputModFile(const std::string& outputName);

#endif // OUTPUTTER_H
And Outputter.cpp
Code:
#include "Outputter.h"

void outputModPath(const std::string& outputName)
{
    std::filesystem::path modPath{ "output/" + outputName };
    std::filesystem::create_directories(modPath);
}

void outputModFile(const std::string& outputName)
{
    std::ofstream modFile{ "output/" + outputName + ".mod" };
    modFile << "name = \"Converted - " << outputName << "\" \n";
    modFile << "path = \"mod/" << outputName << "\"";
    modFile.close();
}
:confused:
 
I'm not sure what to move into each but I did something wrong. My Main.cpp is
Code:
#include "Outputter.h"
#include <filesystem>
#include <fstream>
#include <string>


int main(const int argc, const char* argv[])
{
    std::string outputName{ "CK2Tester" };
    if (argc > 1)
        {
            outputName = argv[1];
        }
    outputModPath(outputName);
    outputModFile(outputName);
}

Outputter.h is
Code:
#ifndef OUTPUTTER_H
#define OUTPUTTER_H

void outputModPath(const std::string& outputName);
void outputModFile(const std::string& outputName);

#endif // OUTPUTTER_H
And Outputter.cpp
Code:
#include "Outputter.h"

void outputModPath(const std::string& outputName)
{
    std::filesystem::path modPath{ "output/" + outputName };
    std::filesystem::create_directories(modPath);
}

void outputModFile(const std::string& outputName)
{
    std::ofstream modFile{ "output/" + outputName + ".mod" };
    modFile << "name = \"Converted - " << outputName << "\" \n";
    modFile << "path = \"mod/" << outputName << "\"";
    modFile.close();
}
:confused:

Faulty instructions on my part. I've added the following lines:

Note, however, that your functions use string. So also add this to the top of the file:
Code:
#include <string>


It should give you a .h file like so:

Code:
#ifndef OUTPUTTER_H
#define OUTPUTTER_H

#include <string>

void outputModPath(const std::string& outputName);
void outputModFile(const std::string& outputName);

#endif // OUTPUTTER_H
 
  • 1
Reactions:
One more change: Main also no longer needs some of those includes. Move the ones for filesystem and fstream into Outputter.cpp.
 
  • 1
Reactions:
Always Be Cleaning Up

After the last changes, there's still something that could be better. Why does main() need to know that outputting the mod has two steps? main shouldn't have to worry about those details.

Move the two function declarations from Outputter.h into Outputter.cpp. In Outputter.h, create a function called outputMod that has the same inputs and outputs of the functions you moved. In Outputter.cpp, add a definition for outputMod and make it call the two functions.

Now in main.cpp, replace the calls to the functions with a single call to outputMod().

When programming, you should be constantly checking for chances to make things clearer, or seeing where the details are in the wrong place. Don't be afraid to move things around to make it more clear.
 
  • 1
Reactions:
Objects

If you've been thinking ahead, you may have noticed there's a difficulty we're going to have. There's a lot of things the CK2 mod will need to output. Are we going to pass those all to an output function? It'd have a parameter list dozens of lines long! We'd never be able to keep it straight. It'd be easier if we could hold all the data together in a single place. Fortunately there's a way to do that. It involves what in general are called objects, and in C++ are called classes.

Add a file called Ck2World.h and put the following inside:
Code:
#ifndef CK2_WORLD_H
#define CK2_WORLD_H



class Ck2World
{
};



#endif // CK2_WORLD_H

This defines a class called Ck2World that we'll make hold everything for the CK2 mod.

Go to main and add an include for the new file. Then, just before the outputWorld() function, add the line
Code:
const Ck2World ck2World;

This creates an instance of the Ck2World. We could create several if we wanted, but in this case we only need one.

Now we need to change the outputter to work with these. Start by renaming Outputter.cpp and Outputter.h to OutCk2World.cpp and OutCk2World.h. You'll need to change the includes in OutCk2World.cpp and main.cpp to match. Also change the lines in OutCk2World.cpp at the top and bottom to match the new name.

Now in OutCk2World.h, include Ck2World.h. Then change the output world function to take a constant reference to a Ck2World called world. Change the function in OutCk2World.cpp to match. Finally, change the call to outputWorld() in main.cpp to take ck2World instead of outputName.

Right away, there's a problem. How do we know what to give to the two functions we call? Well, we can work backwards from what we need until everything is good.

Right here, we'll need Ck2World to give us an output name. We'll pretend it already can. Add the line
Code:
const auto& outputName = world.getOutputName();

This says to create a constant variable who's type should be automatically figured out named outputName, and to give it the value provided by world.getModName(). Now, it won't work quite yet because world.getModName() doesn't exist. But now we know what to add next.

Go to Ck2World.h and change it to this:
Code:
#ifndef CK2_WORLD_H
#define CK2_WORLD_H



class Ck2World
{
  public:
    [[nodiscard]] const auto& getOutputName() const { return outputName; }
};



#endif // CK2_WORLD_H

There's a lot there:
  • public means the following items can be accessed by anyone who has a Ck2World.
  • getOutputName() is a function, but because its in the definition of Ck2World, it's a part of Ck2World, which is how OutputWorld() can get to it with world.getOutputName().
  • Because it's part of a class, we can create it in the .h file. This should only be done for simple functions.
  • [[nodiscard]] means we force anyone who calls the function to actually do something with the value it returns. It might seem unnecessary, but you'd be surprised how often people will call a function and ignore the values.
  • const auto& is the what the function returns, but its a little different than what we're used to. The const means the caller can't modify the returned value (but they could copy it and modify the copy). auto means that the language should figure out the type of the item. If this isn't possible you'll get an error when trying to build. & means that instead of returning a copy, we return a reference to an item, same as when a function receives a reference.
  • the const after the function name means that this function doesn't change the class. This is useful for ensuring the correctness of our programs, as we can enforce good behavior.
  • Finally, the function returns a variable called outputName
Now we need something called outputName to return. Add the following lines:
Code:
private:
    std::string outputName;
  • private is the opposite of public. It means the following items can't be accessed by things that have a Ck2World. Only Ck2World can access these items.
  • Then we have a variable like we're used to.
Build the project. But don't run it yet, because we still have a problem. How does Ck2World give outputName the proper value?

Classes can a special function with the same name as the class that is used to set everything up. These functions are called constructors. Right after public, add the line
Code:
Ck2World(): outputName{"CK2Tester"} {}

This function will be called when we create a Ck2World. Because it's a constructor, we can do something special. The colon marks the beginning of a list of all the variables that are part of the class. In this case, we only have one. We tell it to set outputName to "CK2Tester". Then the {} specify the constructor itself. There's nothing to do in it, so we have no lines.

You should be able to build and run this same as before. But again, we're a little better organized.
 
Last edited:
  • 1
Reactions:
Namespaces

As you might imagine, we plan to create ImperatorWorld soon. But it might have pieces that share names with Ck2 pieces. This could make naming confusing, unless everything is Ck2World and ImperatorWorld everywhere, but then that's extra typing. Fortunately C++ gives us a tool called namespaces to simplify this.

In Ck2 world, add these lines after the #include line:
Code:
namespace Ck2World
{
then add a closing brace after the class.

Now rename the class to be World. Note that Visual Studio becomes unhappy - you need to also rename the constructor. Do so
While we're here, rename outputName to modName. Do so everywhere it appears in this file.

Now open OutCk2World.h. Because it outputs the Ck2World, it should be in the same namespace. Add
Code:
namespace Ck2World
{
before the function here, and a closing brace after. The function is unhappy because we renamed Ck2World. So make the function take World instead of CK2World. While we're here, rename the function to outputWorld. So far, because we're just in .h files and both are in the same namespace, nothing special has happened.

Now open OutCk2World.cpp. Rename outputMod to outputWorld and change it to take a World. But that's not enough. We need to indicate that this is the outputWorld in Ck2World, not some other outputWorld. We do this by add Ck2World:: before it's name. That should give you the line
Code:
void Ck2World::outWorld(const World& world)

Also put a
Code:
namespace Ck2World
{
}
around the function declarations at the top of this file. The go to the definitions and add the Ck2World:: to both of them.

Finally, go to Main.cpp. You indicate you're using a class in a namespace the same way we did with the functions. Change the line with Ck2World to
Code:
const Ck2World::World ck2World;

Everything should now build, and work the same. If it's not, try to figure out what went wrong. Try Googling the error messages you get, and see if you can figure it out from there. Or ask on the Discord. But here's my code as a reference (try to do it yourself before looking):

Main.cpp
Code:
#include "Ck2World.h"
#include "OutCk2World.h"



int main()
{
    const Ck2World::World ck2World;
    outWorld(ck2World);

    return 0;
}

Ck2World.h
Code:
#ifndef CK2_WORLD
#define CK2_WORLD



namespace Ck2World
{

class World
{
  public:
    World(): modName{"CK2Tester"} {}

    [[nodiscard]] std::string getModName() const { return modName; }

  private:
    std::string modName;
};

} // namespace Ck2World



#endif // CK2_WORLD

OutCk2World.h
Code:
#ifndef OUT_CK2_WORLD_H
#define OUT_CK2_WORLD_H



#include "../Ck2/Ck2World.h"



namespace Ck2World
{

void outWorld(const World& world);

}



#endif // OUT_CK2_WORLD_H

OutCk2World.cpp
Code:
#include "OutCk2World.h"
#include <filesystem>
#include <fstream>
#include <string>



namespace Ck2World
{

void outputModFile(const std::string& outputName);
void createModFolder(const std::string& outputName);

} // namespace Ck2World


void Ck2World::outWorld(const World& world)
{
    const auto& outputName = world.getModName();
    createModFolder(outputName);
    outputModFile(outputName);
}


void Ck2World::outputModFile(const std::string& outputName)
{
    std::ofstream modFile{"output/" + outputName + ".mod"};
    modFile << "name = \"Converted - " << outputName << "\"\n";
    modFile << "path = \"mod/" << outputName << "\"";
    modFile.close();
}


void Ck2World::createModFolder(const std::string& outputName)
{
    const std::filesystem::path modPath{"output/" + outputName};
    create_directories(modPath);
}
 
  • 1Like
Reactions:
Another World

Previously, we created a world for Ck2 and made the output module use this world. But we should go even further. Ck2World should be able to be created from an Imperator world. This would give the converter the overall structure:

Imperator save -> ImperatorWorld -> Ck2World -> Ck2Mod

As before, let's work backwards. In Ck2World's World class change the constructor to this:

Code:
explicit World(const ImperatorWorld::World& impWorld): modName(impWorld.getSaveName()) {}

Let's look at the changes one at a time:
* ImperatorWorld - we're imagining ImperatorWorld before we create it
* ImperatorWorld::World - ImperatorWorld will have a class called World that we take as a parameter to our constructor.
* ImperatorWorld::World& - we want a reference so that we don't copy the whole thing
* const ImperatorWorld::World& - we want a const reference so its clear we're not changing it
* explicit - C++ allows you to do some weird things sometimes, like pass in something that can automatically be changed into an ImperatorWorld. We don't want that, so this word turns it off. We only accept 100% pure ImperatorWorld's here.
* impWorld.getSaveName() - instead of assuming what the mod should be named, we imagine ImperatorWorld::World has a function called getSaveName() that we use instead.

Okay, now we need to create the things we imagined. Create a file called ImperatorWorld.h. Add the usual include guards:
Code:
#ifndef IMPERATOR_WORLD
#define IMPERATOR_WORLD
#endif // IMPERATOR_WORLD

Inside those, create a namespace for ImperatorWorld:
Code:
namespace ImperatorWorld
{
}

Inside this, create a class called World:
Code:
class World
{
}

Make it have a string variable called saveName:
Code:
private:
    std::string saveName = "CK2tester";

This'll cause an error, as we haven't included string yet. Add the include inside the guards but outside the namespace:
Code:
#include <string>

Finally, add the function we imagined:
Code:
[[nodiscard]] const auto& getSaveName() const { return saveName; }

Now go back to Ck2World.h and include our new header:
Code:
#include "../Imperator/ImperatorWorld.h"

Finally, we need to update main. It needs to create an ImperatorWorld, then pass it to Ck2World. Tyry to do this on your own. Once done, you should be able to build, and then run it. It'll do the same as before (create a mod file and folder).

Here's my code for reference:

Main.cpp
Code:
#include "Ck2World.h"
#include "ImperatorWorld.h"
#include "OutCk2World.h"



int main()
{
    const ImperatorWorld::World impWorld;
    const Ck2World::World ck2World(impWorld);
    outWorld(ck2World);

    return 0;
}

ImperatorWorld.h
Code:
#ifndef IMPERATOR_WORLD
#define IMPERATOR_WORLD



#include <string>



namespace ImperatorWorld
{

class World
{
  public:
    [[nodiscard]] const auto& getSaveName() const { return saveName; }

  private:
    std::string saveName = "CK2tester";
};

} // namespace ImperatorWorld



#endif // IMPERATOR_WORLD

Ck2World.h
Code:
#ifndef CK2_WORLD
#define CK2_WORLD



#include "ImperatorWorld.h"



namespace Ck2World
{

class World
{
  public:
    explicit World(const ImperatorWorld::World& impWorld): modName(impWorld.getSaveName()) {}

    [[nodiscard]] std::string getModName() const { return modName; }

  private:
    std::string modName;
};

} // namespace Ck2World



#endif // CK2_WORLD

OutCk2World.h
Code:
#ifndef OUT_CK2_WORLD_H
#define OUT_CK2_WORLD_H



#include "Ck2World.h"



namespace Ck2World
{

void outWorld(const World& world);

}



#endif // OUT_CK2_WORLD_H

OutCk2World.cpp
Code:
#include "OutCk2World.h"
#include <filesystem>
#include <fstream>
#include <string>



namespace Ck2World
{

void outputModFile(const std::string& outputName);
void createModFolder(const std::string& outputName);

} // namespace Ck2World


void Ck2World::outWorld(const World& world)
{
    const auto& outputName = world.getModName();
    createModFolder(outputName);
    outputModFile(outputName);
}


void Ck2World::outputModFile(const std::string& outputName)
{
    std::ofstream modFile{"output/" + outputName + ".mod"};
    modFile << "name = \"Converted - " << outputName << "\"\n";
    modFile << "path = \"mod/" << outputName << "\"";
    modFile.close();
}


void Ck2World::createModFolder(const std::string& outputName)
{
    const std::filesystem::path modPath{"output/" + outputName};
    create_directories(modPath);
}
 
  • 1Like
Reactions:
Stop, Collaborate, and Listen

It's time to start working together, and working on the actual converter. The converters all use git to manage the code, and store it on github. So as a first step, create an account on github. Once you've done so, go to https://github.com/ParadoxGameConverters/ImperatorToCk2. In the top-right corner, there's a button that says 'Fork'. Click it and select your account. This will create a copy of the converter for you to play with.

Next you need to be able to play with it on your computer. Go to https://git-scm.com/downloads and download and install the version that matches your computer. You can also use a manager such as Sourcetree, but you will need to figure out the commands on your own.

Once it's installed, open a git bash client. If you aren't familiar with them, here are some helpful commands:
  • ls - list the files in the current directory
  • pwd - print the current directory
  • cd <new directory> - change directory to the one you specify
  • cd .. - change one directory up
Use those commands to change to a directory where you want the converters to be at. I personally use C:\MyProjects\paradoxGameConvertersForks\
Once there, run the command git clone <your fork>
You can find what to put in <your fork> on github. On your copy there should be a green button that says 'Code'. Click it and there will be a url for you to copy. Put that in for <your fork>
The converter code will all download.
Now switch to the ImperatorToCK2 directory that just got added.

Run the command git remote add upstream https://github.com/ParadoxGameConverters/ImperatorToCk2.git

The usual flow for working with git is:
Make sure you're on your master branch
  1. switch to the master branch
  2. sync with the master copy
  3. create a new 'branch' to do your work
  4. do your work
  5. check in your work
  6. push it to your github copy of the converter
  7. make a pull request to add it to the main converter
  8. get your work reviewed
  9. make any changes requested
  10. repeat 5-7 until it's approved and merged to the master repository
  11. go back to 1
So let's actually do this by having you add yourself to the credits.
  1. Run the command git checkout master
  2. Run the command git fetch upstream
    1. Then run the command git rebase upstream/master
    2. Run the command git push to make sure your copy on github stays up to date
  3. Run the command git checkout -b addToCredits
    1. For other changes, you should pick other names for the branch. Try to make it match what you're planning to work on.
  4. Find DataFiles/Readme.txt and add yourself to the credits as a programmer.
  5. Run the command git statusto see what files have changed
    1. For each, run the command git diff <filename> to double-check the changes
    2. If the changes are good, run the command git add <filename> to add the file to the commit you're preparing
    3. When you've added all files, run the command git commit -m "<A description of your changes>"
      1. You can leave off the -m and description, but it will open an editor to have you put one in. You'll have to figure that out on your own.
  6. Run the command git push
  7. Got back to github. On your copy, it should mention your changes and have a button that says 'Create Pull Request'. Click it and do what it asks.
  8. I should see the change within a day and give you any feedback. That feedback will appear on github and in the converter Discord.
  9. This is the same as 4.
  10. Do steps 5-7 until I have no more feedback and merge your changes to the master repository
    1. Within an hour, you can download the latest automatic build of the converter at https://github.com/ParadoxGameConve...es/download/WindowsRelease/ImperatorToCk2.zip, which will have your changes.
  11. Find something new to do and go back to 1
 
Last edited:
I've been developing the Imperator:Rome to CK II converter from the ground up for a few months, and the current version is now uploaded to GitHub.

Design:

Independent countries in Imperator:Rome are converted as dynamically generated kingdom tier realms, and regional governorships are converted as duchy titles underneath the main country.
20201012015826_1.jpg
20201012015845_1.jpg



CK II Province ownership, culture, religion, and buildings are determined by the combined amount of pops that make up the territories which fill up that CK II province. If the majority of pops of a group of territories that make up a CK II province are Hellenic Romans ruled by Rome, for example, that province in CK II will be Hellenistic, Roman, and owned by Rome, even though there might have been be a few Etruscans or Druidic Lepontic Gauls living there. If no country owns any of the land in Imperator, an independent one province tribal chiefdom will be generated there for CK II.

CK II's economy is based off of buildings and holdings, so pops are translated as buildings. If a combined CK II province has reached a threshold of Imperator pops, a building will be built. I arbitrarily made these numbers, but because pop totals can range from 0-1,000 in Imperator, the threshold for each holding and empty holding get's higher then the last.

There are two starting dates that are generated: 100 AD and 1066 AD. Unfortunately CK II's engine can't handle characters born before 1 AD, and the earliest I could set it without worrying about having to change most characters' ages was 100 AD. 100 AD is still enough time to simulate the spread of Christianity, the migration era, and the rise of Islam. Countries outside of Imperator's map will be filled with what was historically there, although creating still needs to be done. Because 100-1400 AD is a long time to play the game, I've added a second start date at the game's vanilla start, right at the onset of the crusades. Countries outside of the map are filled with their 1066 counterpart.


Implementation:

1602478296322.png


The converter is programmed in Java. The Main class is the primary call for the program, and will store and process much of the information needed to convert. There is currently no GUI for the converter, but until one is developed all commands are entered through the console when the converter asks for them.

Imperator:Rome saves are massive when compared to most other Paradox games, sometimes going as high as 9 million lines, which would take hours or days to parse through and get the information needed to create a conversion mod. This problem has been bypassed by having the TempFiles class going through the original file and breaking it up into smaller, more manageable pieces stored in the main program folder as temporary text files. These temporary text files are tempCharacters.txt for the characters, tempDynasty.txt for the character dynasties, tempCountries.txt for countries, and tempProvinces.txt for the provinces.

Once the temp files are created, Importer.java can go through and import information from each temp file, and then send that information over as string arrays. Importer.java will also import information from conversion tables, such as cultureConversion.txt and religionConversion.txt

Directories.java deals with creating the directories and .mod files required for a CK II mod in the documents/mod folder. Processing.java deals with most tasks related to processing that aren't handled by main, such as localisation and which Imperator provinces belong to a governorship. Characters.java deals with processing characters. The method which creates characters calls on itself recursively whenever it finds a family member, so if a ruler is converted, they'll have all of their relatives converted as well.

Output.java will output the stored information directly to the CK II mod directories, such as country information and character information. Determined by main, only the countries and characters(and their families) which have land when will be converted to the mod. If the country of Olbia fully annexes Rome, for example, the title k_ROM will not be generated. This is to save performance, since those titles would never be accessible in-game either way.

TestQ.java is just for random testing and for converting HSV colors used in Imperator files (such as culture and religion) to RGB, which makes it more convenient to create the new cultures and religions.

Problems/TODO:

Not all of the provinces, religions, and cultures have been mapped yet.

There's a bug on the 1066 start date which causes a crash to desktop after running for a few days. I suspect it's because of characters at war with armies who have become landless by the converter, but I haven't investigated it.

All governments are converted as feudal or tribal, and republics should have a custom playable government type.

Images and flags don't convert, Imperator:Rome generates flags in-game from several .tga and .dds files, but sadly I haven't been able to figure out how to manipulate images in Java.

Governorship titles beneath the main country show up as independent countries in the start screen. I suspect that they need to be listed under the country in common\landed_titles, but I haven't been able to test it.

There aren't any completed events for the rise of Christianity, the great migrations, rise of Islam, or other occurrences to simulate the era.

The GUI interface hasn't been worked on.

The children of converted characters don't have a religion, and sometimes have a mismatched dynasty.


Compatibility:

The converter is currently compatible with Imperator:Rome versions 1.3-1.5, and the final version of CK II. It should be compatible with future versions of Imperator:Rome unless the save structure changes
 
Last edited:
  • 3Like
  • 1Love
  • 1
Reactions: