Reposting the guide from the modmodding thread so it doesn't get lost so easily:
(Note: this guide assumes that you know how to compile a new DLL. If you don't, there is some setup required, for which you can find guides in the modding tutorials section, for example this one.)
(Note: this guide assumes that you know how to compile a new DLL. If you don't, there is some setup required, for which you can find guides in the modding tutorials section, for example this one.)
Okay, I'll try to write a tutorial on how to insert a new civilization.
First off, I want to clarify some terminology. Usually, when talking about the game, the terms "civilization" and "player" are used interchangeably. In the code, there is an important difference between the two. You can think of players as "slots" for civilizations in the scenario. Let's say, the current RFC scenario has 26 players. These players can be any civilization, it's just that they are defined as Egypt, Babylonia, China etc. in the scenario. All players may just as well be Egypt.
Civilizations can be added to the game without affecting it at all. For instance, the Harappan or Zulu civilizations are in DoC but aren't assigned to any player. Adding a civilization to the game doesn't make it appear. A new player has to be created first before you can assign that civilization to it.
There is a third term called "team" which you will encounter during the process. Teams are comprised of multiple players and are used in BtS to implement permanent alliances (players that form a permanent alliance become a team, sharing techs and other stuff). Permanent alliances don't exist in RFC, which is why teams and players are treated interchangeably. There are always as many teams as players, and every team is comprised of only one player. For easy of use, teams are ordered in the same way as players are, so that the same ID refers to related teams and players. More on that later.
Creating your civilization
I'm going to assume you have all the graphics you need for your civilization. I'm also going to assume you know how to edit XML. It's documented in the modiki and there are some guides out there. Generally it is a good idea to always look for an entry that is most similar to what you want to add, copy it and then modify it to suit your needs. You need to edit the following files:
1) The civilization itself: XML\Civilizations\CIV4CivilizationInfos.xml
It's a good idea to insert it in alphabetical order (all current civilizations are ordered this way), but that isn't mandatory. A civilization contains a city list that is used for all cities not covered by the CityNameManager. If you know a name on the list is covered by the CNM you should remove it.
2) Their leader: XML\Civilizations\CIV4LeaderheadInfos.xml
If you don't have your own leader personality, I suggest you try to think of which existing leader comes closest to the desired behavior of the new LH and just copy their entry.
3) Their unique building: XML\Buildings\CIV4BuildingInfos.xml
It's a good idea to copy the building it replaces and make some changes. I suggest you insert the new UB after the building it replaces, this is the current standard.
We will return to the buildings folder later to add the embassy.
4) Their unique unit: XML\Units\CIV4UnitInfos.xml
Basically the same procedure as with buildings.
5) Graphics
If you copy the XML entries, I suggest to keep the ART_DEF_... entry unchanged in the beginning. Everything will look like the building/unit/leader it replaced, but you can be sure that the pointers to the graphics are correct and the mod should load fine without crashes (always start the mod after an XML change to make sure you have no syntax errors). You can create new ART_DEF_... entries in the XML if you have art files they can point to.
Civilization button and flag: XML\Art\CIV4ArtDefines_Civilization.xml
Leaderhead graphics: XML\Art\CIV4ArtDefines_Leaderhead.xml
Building button and graphics: XML\Art\CIV4ArtDefines_Building.xml
Unit button and graphics: XML\Art\CIV4ArtDefines_Unit.xml
6) Text
If you have given your new civilization/leader/unit/building a new TXT_KEY_..., it will show up as that in the game because you haven't yet defined what the text key refers to. You can do that in the XML\Text\ folder. It doesn't really matter where you define a text key there, but to remain consistent I recommend the following files:
Civilization name, short name and adjective: CIV4GameText_RFCCivilizations.xml
Leaderhead name: CIV4GameText_Leaderheads.xml
Unique Building name: CIV4GameText_DoC_Buildings.xml
Unique Unit name: CIV4GameText_DoC_Units.xml
You should also create a new file named DynamicNames_[Your civ's name].xml where you can store its dynamic names. You don't have to put in anything there for the mod to work.
Adding a new player to the scenario
Right now we are at the stage where a new civ with all its stuff is in the civilopedia, but doesn't exist in the scenario. The mod should run fine, but behave unexpectedly in some occasions. That is because several Python and C++ constants still need to be adjusted after you have added new civs, buildings and units to the XML files. All of this will happen in the process.
7) Scenario files:Public Maps\Private Maps\
This is where new players can be added. The following changes are required for all scenarios in this folder.
Adding a team: as I said, every player needs its own team, so you first have to add a new team. You can do this by adding another
to the long list of identical statements right at the beginning of the file. You don't have to set anything else for teams (don't worry about techs, we're going to do that in Python later).Code:BeginTeam EndTeam
Adding a player: Just replicate the pattern from the other players:
The position of this entry is very important because the order of entries determines the order of players in the selection screen of the scenario. This order has to be identical to the order of spawn dates, and must be the same in all scenarios (don't worry if this civ is already alive at the beginning of one of the scenarios). The Team variable has to be ascending for all entries. That means you must increment this value for all players that come after your new one.Code:BeginPlayer LeaderType=[The leader you have added to the XML] CivType=[The civilization you have added to the XML] Team=[The position of this player entry in the list of player entries] PlayableCiv=1 Handicap=HANDICAP_PRINCE EndPlayer
If you have multiple leaders, the leader entry only determines who you will see in the selection screen (and the human players leader as far as savegame name suggestions etc. are concerned). AI leaders will always be assigned by Python.
If your scenario has independents and barbarians present at the beginning of the game (anything except 3000 BC), you must find all their unit (BeginUnit/EndUnit) and city (BeginCity/EndCity) entries and increment their UnitOwner/CityOwner values by one. This is because your new player has invariably been inserted before the independents and barbarians, which means their ID has increased by one, which you now have to account for. The same goes for the PlayerXXCulture=Y entries for cities, where you have to increment XX by one.
Your new player now exists in the scenario. At this point, the mod is guaranteed to crash on start. To fix this, the new player has to be accounted for in the DLL.
DLL adjustments
8) At this point, you might just as well add that civs embassy. Every civilization's embassy is its own building class so you have to go to both:
- XML\Buildings\CIV4BuildingClassInfos.xml
- XML\Buildings\CIV4BuildingInfos.xml
Again, the order of the buildings is important, and must align with the order of civilizations. But now on to the DLL stuff.
9) Increase the maximum number of players: CvDefines.h
Find the line that begins with #define MAX_CIV_PLAYERS and increment its value by one.
10) Defining the new player's constant: CvEnums.h
Find the entry "enum PlayerTypes" and add a new constant for your civilization according to its position in the scenario files. Even if you do not intend to use the constant in C++ code (likely if you don't want to code special effects for them), you have to add it so the constants for all following players are increased by one corresponding to their new position.
11) Adjusting global constants: CvRhyes.h
Depending on your changes, you have to increment a couple of constants here by one:
- BEGIN_WONDERS (unless your civ's unique building has already been in the game)
- NUM_BUILDINGS_PLAGUE (unless your civ's unique building has already been in the game)
- NUM_MAJOR_PLAYERS
- NUM_PL
- NUM_CIVS (unless your player's civilization has already been in the game)
12) Setting modifiers and AI maps: CvRhyes.cpp
A lot of stuff has to be done here, you should be able to figure out most of it from the examples already there, I'll try to explain in some special cases. In general, in all lists, a value for your player MUST be inserted according to its position. Usually the entries are labeled with comments indicating the position of every existing player so you just have to insert after that (I recommend updating the comments too).
startingTurnYear[]: the spawn date.
lastResurrectionYear[]: the last date in which a civ can be released, set 2020 if don't want to limit this.
The following are in order of civilizations, not players:
loadingTime[]: number of minutes you have to wait for the autoplay (simply estimate based on surrounding civs), for all scenarios
startingYear[] and startingEra[]: the spawn date as a string, the era array decides whether it will be BC or AD. Has to be set for all scenarios.
We are in order of the player entries in the scenario files again:
Everything from takenTiles[] to compactEmpireModifier[] influences how close the AI likes to place cities, best look for civs similar to what you'd like to achieve here. The "astronomy" arrays take effect as soon as Astronomy is discovered.
targetCityValueDivisor[] influences how valuable far away cities are as a target for conquest. The lower, the further away cities can be and still be targeted.
startingEraFound[]: the "era" of your cities when founded, i.e. the starting population and associated free buildings, for all scenarios, after the discovery of Astronomy and for respawned civs
unitCostModifier[]: modifier for unit upkeep costs
unitCostModifier2[]: modifier for unit production costs
currentEra[]: the era you start the game with, mostly used to limit "entering era" screens when initial techs are assigned
Most other modifiers should be self-explanatory. Choose values similar to civs with similar strengths, unless you want to create a backwards civ modifiers get better for later spawns. Don't worry too much about specific values, you can balance again later, but a value must be added.
Adding text keys (in order of civilizations again):
uniquePower[]: Just follow the pattern there, you can define the text keys afterwards in XML\Text\CIV4GameText_UPBTS
uniqueGoals[]: Same here, define your UHV texts in XML\Text\CIV4GameText_VictoryBTS (or leave the text keys if you haven't chosen a UP or UHV yet)
rating[]: The stars appearing in the civ selection screen.
Further modifiers:
civSpreadFactor[]: chances of a religion spreading in this civilization
borders[]: how close this player is to other players (1-5, 2 isn't used, see comment). Remember, you have to add this civilization on both axes here.
AI maps:
settlersMaps[]: this map influences both AI settling behavior and the historicity of a tile. A value of 90 or higher makes a historical tile. This is also the value for which the AI will usually settle a tile, the higher it is, the more likely for this tile to be chosen. 20 is chosen for water tiles. 40-90 can be settled by the AI, but are rarely prioritized. Values of 3 mean the tile is forbidden from settling.
warMaps[]: how much cities on a specific tile will be targeted in wars by this civilization (useful to guide its conquests in a historical way). Values between 0-10 are used. 10 is usually for their own core which they ought to recover at all costs, 8 for important historical expansion. This map also increases chances of declaring war against players holding many cities with high values here.
I recommend using a spreadsheet editing tool like OpenOffice Calc (free) to edit these, you can copy and paste in both directions. Every player has two entries here, where the second is for a possible respawn. Since you are likely not thinking about a respawn at the moment, you can simply insert the same map in both entries.
13) Making the autoplay work: CvGame.cpp
update(): Find the switch statement for multiple civilizations and add a "case [player constant]:" according to your player's position in the scenario, where player constant is the constant you defined in CvEnums.h. If the player is active from the start in any scenario, you have to add a "break;" statement in the right condition, similar to the examples that are already there.
initDiplomacy(): you can define here if you want your player to be at war with the Independents at the start of a scenario
14) Further AI stuff: CvCityAI.cpp (always use the constant you defined in CvEnums.h)
AI_bestUnit(): find the switch statement for multiple civilizations and add your civilization here if you want it to prioritize building specific unit types.
AI_bestBuildingThreshold(): find the switch statement for multiple civilizations and add your civilization here if you want it to prioritize or avoid specific wonders.
15) Further AI stuff: CvPlayerAI.cpp
AI_bestTech(): find the switch statement for multiple civilizations and add your civilizations here if you want it to prioritize or avoid specific technologies.
AI_bestReligion(): add your civ to this check if you don't want it to convert to Christianity too easily
16) Make the defensive pact AI work: CvTeamAI.cpp
AI_defensivePactTrade(): find the array civsArray[] and insert another 0 into it.
17) Adjust unit work rate modifier: CvUnit.cpp
workRate(): find the switch statement for multiple civilizations and add your civilization here to modify its work rate
With this, we're done with the DLL changes, you only have to compile your code now. It's important to rebuild the entire project because you changed some header files.
Python adjustments
All of these files are in Assets\Python\.
18) Set constants: Consts.py
This is basically the Python equivalent of CvRhyes.cpp, which means that you have a lot to do.
Increase iNumPlayers by one and add a constant for your player in the list below it (order according to scenario file).
Increase iNumCivilizations by one and add a constant for your civilization in the list below it (order according to the XML file).
After that comes a list of geographical groups where you have to insert your player according to its location, it should always be in one group per type.
lNeighbors: insert into list of all players your new player is a neighbor to, and add an own list of neighbors for this player
lOlderNeighbors: as neighbors, but only includes those that have spawned before
tBirth: insert your spawn year in the correct location in the tuple
tFall: year after which a civilization becomes more unstable
tVictoryYears: to display the turn in the victory goal display
tRebirth: scripted respawn, make sure to add a -1 for your civ even if you don't want it to respawn
tRebirthCiv: the civilization a player respawns as (add -1)
tRebithPlot: the tile a civilization respawns on (add -1)
tRebirthArea: the area flipped on rebirth (add -1)
tResurrectionIntervals: list of intervals in which a civ can randomly respawn, add an empty list even if you don't want it to respawn
tYear: starting year and BC/AD
tGoals: UHV goal text keys again, this time accounting for game speed too (marathon/epic/normal), tGoals2 is for respawns again, just copy tGoals1 if you don't want it to respawn
dDawnOfMan3000BC: add your civ here (order doesn't matter here, but I suggest you keep it), you can define the text key XML\Text\CIV4GameText_Dawn_of_Man.xml. If your player is available from start in later scenarios, you might want to set a different DOM text in the following dictionaries.
The following entries are double for respawns again, just do it twice if you don't want to define a respawn. Order is important again.
tCapitals: capital and starting tile
tReserveCapitals: unused, but add a (), for your new player
tCoreAreasTL: LOWER left corner of the rectangle of your core area
tCoreAreasBR: UPPER right corner of the rectangle of your core area
tExceptions: tile in the rectangle to be excluded from the core
(Normal and Broader areas are defined in a similar way)
tNewCapitals: new capital locations for civs where it changes during the game, add a -1 if it doesn't apply
tRespawnCapital: city that becomes capital if existing after a random respawn, add a -1 if it doesn't apply
tRespawnTL, tRespawnBR: area that is flipped on random respawn, add a -1 if it doesn't apply (normal area will be used instead)
tStartingGold: gold at the beginning of the game
lEnemyCivsOnSpawn: chance to start at war with (multiple entries increase chances)
lTotalWarOnSpawn: if it starts at war with these civs, the war will be fought longer
tAggressionLevel: how often the AI will start wars
tAIStopBirthThreshold: the chance to fight a newly spawned civ
tResurrectionProb: chances for a random resurrection
tPatienceThreshold: how the civ reacts to bribes
tMaxColonists: number of free settlers and ships the AI will receive during the game
Increase iNumUnits by one if you added a UU, and add a constant for it in the correct location according to the XML in the list below.
Increase iNumBuildings by one if you added a UB, and add a constant for it in the correct location according to the XML in the list below.
Add a constant for their embassy in the correct location according to the XML in the list below the iNumBuildingsEmbassy entry.
Increase iNumLeaders by the amount of leaders you have added, and insert constants for them into the list below.
tLatestActiveScenario: insert the constant for the latest scenario in which the player is still available here.
19) Script their spawn: RiseAndFall.py
In the beginning, you should make a copy of the constant from Consts.py and get the PyPlayer and PyTeam objects for your player.
assign600ADGold(): set their starting gold if they appear in the 600 AD scenario
assign1700ADGold(): set their starting gold if they appear in the 1700 AD scenario
init1700ADDiplomacy(): set the relations between players at the beginning of the 1700 AD scenario
createAdditionalUnits(): spawn the units received should they end up in a war on spawn
createStartingUnits(): spawn the starting stack (note: the createSettlers() method spawns as many settlers as specified minus the cities already present in their core, but at least one)
createRespawnUnits(): spawn units received after a scripted respawn
createStartingWorkers(): spawn workers received after the capital is founded
create1700ADStartingUnits(): starting stack in the 1700 AD scenario, if it spawns later than 1700 AD, add it to the list at the end
create600ADStartingUnits(): starting stack in the 600 AD scenario, if it spawns later than 600 AD, add an "isHuman()" check and spawn a settler and warrior like for the other players. This is required to keep the autoplay going.
create4000BCStartingUnits(): another "isHuman()" check while spawning warrior and settler to keep the autoplay going.
assign1700ADTechs(), assign600ADTechs(): if they are available on start in these scenarios, set their techs here.
assignTechs(): set everyone's starting techs. My method used for later additions is more efficient than Rhye's long sequence of method calls.
20) Adjust barbarians: Barbs.py
You might not have to do anything here, but it's worth checking if a minor city exists in your new civilizations area, in which case you might want to disable its spawn. You can also add new barbarian spawns here to challenge your new civ, or remove old ones that are now represented by a proper civilization.
20) Include in city name manager: CityNameManager.py
If your new civilization has the same language as an existing civilization, you have it easy here:
getLanguages(): include a check for your civilization and return the appropriate language(s). In every situation, the languages will be tried in order until one is found that has a name for this tile or city.
If you want to add a new language, increment iNumLanguages by one and include it in the list below (position doesn't really matter). You still have to include your civ in the getLanguages() method as below.
Afterwards, go to dFoundMaps and make a new entry for your language. As with settler and war maps, I suggest using OpenOffice Calc or a similar spreadsheet editor.
Also, go to tRenames and define how your new language translates city names. Order is important here, based on where you have inserted your new language in the list above.
22) Adjust the AI communications script: Communications.py
Add your new player to one of the pools at the beginning of the file. I usually only choose the third one.
23) Unique Power and Unique Historical Victory: Victory.py
You can code your UHV in the Victory.py file. Due to the variety of possible victory conditions I won't go into detail here, I suggest you choose similar goals to those that already exist and locate and replicate their code.
Unique Powers are similar, because they are basically extra rules for potentially every aspect of the game. Usually you will have to locate the part of the code that controls the aspect you want it to affect, and then make an exception there.
24) The mod should run again now. Have fun with your new civilization!