Quick Modding Questions Thread

New modder myself, but I believe this is how it works:

So, the game's database is built from the XML files. The SQL files are often a good shorthand to basically edit the table entries as if you were writing additional entries into the XML file. Example:

Code:
INSERT INTO    Types
          (Type,                                              Kind            )
VALUES    ('TRAIT_LEADER_ZAG_SUMETAL_UA',                  'KIND_TRAIT'    ),
          ('ABILITY_ZAG_SUMETAL_UA_GPM_POINTS',            'KIND_ABILITY'    );

Is effectively the same as
Code:
<Types>
     <row Type="TRAIT_LEADER_ZAG_SUMETAL_UA" Kind ="KIND_TRAIT" />
     <row Type="ABILITY_ZAG_SUMETAL_UA_GPM_POINTS" Kind ="KIND_ABILITY" />
</type>

But frankly I find the SQL version more readable. The SQL file has additional advantages in that you can use OR to specify to either INSERT or REPLACE an entry if that entry already exists. If there are two XML files with the same entry, then I believe the database compiling process has to determine which one is used through some other prioritization process.

There may be some advantages to using xml directly, but it seems most modders have switched to using SQL.
 
My reply up above reminds me. I have a question about some SQL in the template I'm using.

Code:
INSERT OR REPLACE INTO LocalizedText (Language, Tag, Text)
VALUES

The one's I've bolded are the ones that show as blue in ModBuddy. This suggests to me that the formatting is off. Since it's all unique tags for my text, I don't see any reason I shouldn't just use

Code:
INSERT INTO LocalizedText (Language, Tag, Text)
VALUES

Or maybe

Code:
INSERT INTO   BaseGameText
                        (Language, Tag, Text)
VALUES

But in both cases 'Text' is still showing up blue. I'm guessing it's tripping a SQL command? If so, then what I really want is this:

Code:
INSERT INTO   BaseGameText
                        (Language, Tag, "Text")
VALUES

and this would explain why I couldn't figure out where I was getting an error from (20 entries for 21 columns).

OK, I'm mostly thinking out loud here, but I think I have it, unless some sees something I've missed?
 
So if REPLACE is a valid command, why is it not blue in ModBuddy? I've changed it to your recommendations, I just want to know what is going on here. :)
 
INSERT OR REPLACE INTO
  • Checks to see if there is already a matching row for the table's Primary Keys. If so, the existing row in the table is literally replaced with the new data.
  • If there is no matching row already within the table, a new row is added to the table
  • Unique Constraint errors are thereby bypassed
INSERT INTO
  • Does not check anything and simply attempts to add the new row
  • If there is already within the table a row that matches for the table's Primary Keys, a Unique Constraint error results, and the code-chunk as well as anything else following within the same file are rejected by the game.
You do not need to use "Text" : you can safely use Text as in this example
Code:
INSERT INTO LocalizedText (Language, Tag, Text)
VALUES
Modbuddy's SQL error-checking is confused by the fact that a column definition when a table is created can be defined as needing a text-string as opposed to an integer or a Boolean value, such as
Code:
"Description" TEXT NOT NULL,
As @Laurana Kanan mentions, Modbuddy's code-checking lacks quite a bit in terms of reliability. You are actually better off opening the SQL or XML file using Notepad++ and examining what it shows you since Notepad++ auto-recognizes for format correctness when you open any SQL, XML, or lua file. Notepad++ cannot however know whether the tables and columns are valid because it cannot open and read the game's database, nor can Notepad++ understand whether lua commands are valid for Civ6 but it can check for basic syntax errors like missing "end" commands.

Modbuddy does not actually open or access the game's database either.

------------------------

The Primary Keys for table LocalizedText are "Tag" and "Language"
This means every new row added to the table must have a unique combination of data for these two columns.

------------------------

Long story short is there is no true substitute for executing the code via the game.
 
I was trying to enable Multiple Specialty districts (same type) in cities. The codes were simple:
Code:
<Update>
<Where DistrictType="DISTRICT_CAMPUS"/> <Set OnePerCity="false"/>
</Update>
<Update>
<Where DistrictType="DISTRICT_COMMERCIAL_HUB"/> <Set OnePerCity="false"/>
</Update>
<Update>
<Where DistrictType="DISTRICT_INDUSTRIAL_ZONE"/> <Set OnePerCity="false"/>
</Update>
<Update>
<Where DistrictType="DISTRICT_THEATER"/> <Set OnePerCity="false"/>
</Update>
<Update>
<Where DistrictType="DISTRICT_HOLY_SITE"/> <Set OnePerCity="false"/>
</Update>
But I got following problems after I did so:
1. No yields indication when you are choosing which tile to place the district.
2. No citizen slots in other districts with the same type, except the first one, which has buildings in it.

Can anyone help me out? I don't expect citizen slots but I do need the yield indication to make this new feature more friendly.
 
#1) My guess would be the issue is in the game's lua file that executes the Pop-up that occurs for District Placements, or else it is an issue with the way the gamecore software works -- one or the other is not coded to 'understand' additional copies of districts with Adjacency Yields. I have not investigated the code in the lua User Interface file that controls the District Placement Pop-up, so this is merely educated guessing on my part.

#2) A city can ever only have one copy of a building, so additional copies of the same District within the same city aren't going to allow additional copies of Buildings.
 
Is there a way to disable the jersey system ?
 
#1) My guess would be the issue is in the game's lua file that executes the Pop-up that occurs for District Placements, or else it is an issue with the way the gamecore software works -- one or the other is not coded to 'understand' additional copies of districts with Adjacency Yields. I have not investigated the code in the lua User Interface file that controls the District Placement Pop-up, so this is merely educated guessing on my part.

#2) A city can ever only have one copy of a building, so additional copies of the same District within the same city aren't going to allow additional copies of Buildings.

Can you tell me which lua file affect this interface? I don't understand lua code.
 
Not that I've seen. You can only "work around" it.
Thanks, I'm pondering reporting the transparency issue as a bug, if the game was doing a fallback on the default colors instead, I suppose we could remove all jersey colors to restore the previous behavior.
 
Can you tell me which lua file affect this interface? I don't understand lua code.
Tbh I'm not sure where the code is located. This is because all the placement lua files for city production, plot buying, etc., refer to code in other files, which refer to code in other files, which refer to mouse click "callbacks", which refer to other code in other files. It's a rabbit warren of interlocking files.
 
To make sure I'm understanding this correctly:

EFFECT_GRANT_YIELD_PER_GREAT_WORK_IN_CITY wants these three arguments:
Amount
GreatWorkObjectType
YieldType

So if I want to use MODIFIER_PLAYER_CITIES_ADJUST_GREATWORK_YIELDto grant multiple yields, I would need my argument to look something like this:


Code:
INSERT INTO    Modifiers
        (ModifierId,                                        ModifierType,                                                OwnerRequirementSetId,                SubjectRequirementSetId,            RunOnce,    Permanent    )
VALUES
        ('MODIFIER_ZAG_SUMETAL_UA_MUSIC_YIELD',              'MODIFIER_PLAYER_CITIES_ADJUST_GREATWORK_YIELD',                        NULL,                                NULL,                                0,            0            );

-----------------------------------------------
-- ModifierArguments
-----------------------------------------------

INSERT INTO    ModifierArguments
        (ModifierId,                                        Name,                        Value                        )
VALUES
        ('MODIFIER_ZAG_SUMETAL_UA_MUSIC_YIELD',                'Amount',                    2                            ),
        ('MODIFIER_ZAG_SUMETAL_UA_MUSIC_YIELD',                'GreatWorkObjectType',        'GREATWORKOBJECT_MUSIC'        ),
        ('MODIFIER_ZAG_SUMETAL_UA_MUSIC_YIELD',                'YieldType',                'YIELD_PRODUCTION'            ),
        ('MODIFIER_ZAG_SUMETAL_UA_MUSIC_YIELD',                'Amount',                    2                            ),
        ('MODIFIER_ZAG_SUMETAL_UA_MUSIC_YIELD',                'GreatWorkObjectType',        'GREATWORKOBJECT_MUSIC'        ),
        ('MODIFIER_ZAG_SUMETAL_UA_MUSIC_YIELD',                'YieldType',                'YIELD_FAITH'                );

Or, do I need to create a separate ModifierId for each yield type?
 
Last edited:
Table ModifierArguments in many cases will accept multiple 'arguments' within the same 'Value' column for "amount" and "yieldtype" settings. So, first, try as
Code:
INSERT INTO    ModifierArguments
        (ModifierId,                                        Name,                        Value                        )
VALUES
        ('MODIFIER_ZAG_SUMETAL_UA_MUSIC_YIELD',                'Amount',                    '2,2'                            ),
        ('MODIFIER_ZAG_SUMETAL_UA_MUSIC_YIELD',                'GreatWorkObjectType',        'GREATWORKOBJECT_MUSIC'        ),
        ('MODIFIER_ZAG_SUMETAL_UA_MUSIC_YIELD',                'YieldType',                'YIELD_PRODUCTION,YIELD_FAITH'            );
The arguments for the Value column must be presented as I have shown, as one long text-string with commas separating the different Yields and the different Amount values. You may also need to do the same thing for the GreatWorkObjectType, ie
Code:
('MODIFIER_ZAG_SUMETAL_UA_MUSIC_YIELD',   'GreatWorkObjectType',  'GREATWORKOBJECT_MUSIC,GREATWORKOBJECT_MUSIC'        ),

If the game does not implement this method for stating the ModifierArguments, then you will need two different Modifiers since repeating a combination of ModifierId and Name is not allowed.

RequirementArguments as I recall will also accept this multi-value method for certain kinds of arguments but most other game-tables will not: they as a general rule only want one "value" for an argument and don't implement stringing of multiple values into one argument.
 
I tried the compressed way LeeS described, and it didn't work. I also then did it with 3 modifier IDs, and it still didn't work. So I'll work my way through my thoughts/process as I put up sections of code.

So, first I need to create 3 new ModifierIds and attach them to the same trait I already have working. Setting up just like Kongo is set up for their Nkisi trait. (I actually tried to use a second leader trait at one point, as other leaders have multiple leader traits, but it crashed on game creation until I switched to only having 1 leader trait.)

Code:
INSERT INTO    TraitModifiers  
        (TraitType,                                    ModifierId                                        )
VALUES  
        ('TRAIT_LEADER_ZAG_SUMETAL_UA',                'MODIFIER_ZAG_SUMETAL_UA_GPM_POINTS'    ),
        ('TRAIT_LEADER_ZAG_SUMETAL_UA',                'MODIFIER_ZAG_SUMETAL_UA_MUSIC_PROD'    ),
        ('TRAIT_LEADER_ZAG_SUMETAL_UA',                'MODIFIER_ZAG_SUMETAL_UA_MUSIC_FAITH'    ),
        ('TRAIT_LEADER_ZAG_SUMETAL_UA',                'MODIFIER_ZAG_SUMETAL_UA_MUSIC_FOOD'    );

Great, now that I have 3 new ModifierIds, I need to define their Modifiers. RunOnce and Permanent are set to false/0 because they are not called on in the original Nkisi trait.

Code:
INSERT INTO    Modifiers
        (ModifierId,                                        ModifierType,                                                OwnerRequirementSetId,            SubjectRequirementSetId,            RunOnce,    Permanent    )
VALUES  
        ('MODIFIER_ZAG_SUMETAL_UA_GPM_POINTS',                'MODIFIER_PLAYER_UNITS_GRANT_ABILITY',                        NULL,                            NULL,                                0,            1            ),
        ('MODIFIER_ZAG_SUMETAL_UA_GPM_POINTS_ABILITY',        'MODIFIER_PLAYER_UNIT_ADJUST_GREAT_PEOPLE_POINTS_PER_KILL',    NULL,                            NULL,                                0,            1            ),
        ('MODIFIER_ZAG_SUMETAL_UA_MUSIC_PROD',                'MODIFIER_PLAYER_CITIES_ADJUST_GREATWORK_YIELD',            NULL,                            NULL,                                0,            0            ),
        ('MODIFIER_ZAG_SUMETAL_UA_MUSIC_FAITH',                'MODIFIER_PLAYER_CITIES_ADJUST_GREATWORK_YIELD',            NULL,                            NULL,                                0,            0            ),
        ('MODIFIER_ZAG_SUMETAL_UA_MUSIC_FOOD',                'MODIFIER_PLAYER_CITIES_ADJUST_GREATWORK_YIELD',            NULL,                            NULL,                                0,            0            );

And now for the many entry version of the arguments. Once more, I'm copying the way the Kongo's Nkisi trait did it (though modified for sql instead of xml).

Code:
INSERT INTO    ModifierArguments
        (ModifierId,                                        Name,                        Value                                )
VALUES  
        ('MODIFIER_ZAG_SUMETAL_UA_GPM_POINTS',                'AbilityType',                'ABILITY_ZAG_SUMETAL_UA_GPM_POINTS'    ),
        ('MODIFIER_ZAG_SUMETAL_UA_GPM_POINTS_ABILITY',        'GreatPersonClassType',        'GREAT_PERSON_CLASS_MUSICIAN'        ),
        ('MODIFIER_ZAG_SUMETAL_UA_GPM_POINTS_ABILITY',        'Amount',                    10                                    ),
        ('MODIFIER_ZAG_SUMETAL_UA_MUSIC_PROD',                'GreatWorkObjectType',        'GREATWORKOBJECT_MUSIC'                ),
        ('MODIFIER_ZAG_SUMETAL_UA_MUSIC_PROD',                'YieldChange',                'YIELD_PRODUCTION'                    ),
        ('MODIFIER_ZAG_SUMETAL_UA_MUSIC_PROD',                'Amount',                    2                                    ),
        ('MODIFIER_ZAG_SUMETAL_UA_MUSIC_FAITH',                'GreatWorkObjectType',        'GREATWORKOBJECT_MUSIC'                ),
        ('MODIFIER_ZAG_SUMETAL_UA_MUSIC_FAITH',                'YieldChange',                'YIELD_FAITH'                        ),
        ('MODIFIER_ZAG_SUMETAL_UA_MUSIC_FAITH',                'Amount',                    2                                    ),
        ('MODIFIER_ZAG_SUMETAL_UA_MUSIC_FOOD',                'GreatWorkObjectType',        'GREATWORKOBJECT_MUSIC'                ),
        ('MODIFIER_ZAG_SUMETAL_UA_MUSIC_FOOD',                'YieldChange',                'YIELD_FOOD'                        ),
        ('MODIFIER_ZAG_SUMETAL_UA_MUSIC_FOOD',                'Amount',                    2                                    );

And what do I get? well, the game still launches and I can select the civ, and create the game. I still get Great Musician Points on a kill. I get a musician and create a new work of music? no changes. Still +4 culture, +4 tourism.

Now, in both the compressed version LeeS showed above and in the version I just posted, looking at the database shows my three new modifiers showing up in the table entries, along with their arguments. They show up as trait modifiers too. The only difference is that in the compressed version they showed up as

Code:
MODIFIER_ZAG_SUMETAL_UA_GPM_POINTS_ABILITY    GreatPersonClassType    ARGTYPE_IDENTITY    GREAT_PERSON_CLASS_MUSICIAN
MODIFIER_ZAG_SUMETAL_UA_GPM_POINTS_ABILITY    Amount    ARGTYPE_IDENTITY    10
MODIFIER_ZAG_SUMETAL_UA_MUSIC_PROD    GreatWorkObjectType    ARGTYPE_IDENTITY    GREATWORKOBJECT_MUSIC,GREATWORKOBJECT_MUSIC,GREATWORKOBJECT_MUSIC
MODIFIER_ZAG_SUMETAL_UA_MUSIC_PROD    YieldChange    ARGTYPE_IDENTITY    YIELD_PRODUCTION,YIELD_FAITH,YIELD_FOOD
MODIFIER_ZAG_SUMETAL_UA_MUSIC_PROD    Amount    ARGTYPE_IDENTITY    2,2,2

(Note: recreated from memory, I didn't think to copy/paste it originally, but I noted the structure. So there might be minor discrepancies, like the Amount entry might have had single quotes, i.e. '2,2,2', but I can't remember for sure.)

While in this version they have more entries, like so:
Code:
MODIFIER_ZAG_SUMETAL_UA_GPM_POINTS_ABILITY    GreatPersonClassType    ARGTYPE_IDENTITY    GREAT_PERSON_CLASS_MUSICIAN
MODIFIER_ZAG_SUMETAL_UA_GPM_POINTS_ABILITY    Amount    ARGTYPE_IDENTITY    10
MODIFIER_ZAG_SUMETAL_UA_MUSIC_PROD    GreatWorkObjectType    ARGTYPE_IDENTITY    GREATWORKOBJECT_MUSIC
MODIFIER_ZAG_SUMETAL_UA_MUSIC_PROD    YieldChange    ARGTYPE_IDENTITY    YIELD_PRODUCTION
MODIFIER_ZAG_SUMETAL_UA_MUSIC_PROD    Amount    ARGTYPE_IDENTITY    2
MODIFIER_ZAG_SUMETAL_UA_MUSIC_FAITH    GreatWorkObjectType    ARGTYPE_IDENTITY    GREATWORKOBJECT_MUSIC
MODIFIER_ZAG_SUMETAL_UA_MUSIC_FAITH    YieldChange    ARGTYPE_IDENTITY    YIELD_FAITH
MODIFIER_ZAG_SUMETAL_UA_MUSIC_FAITH    Amount    ARGTYPE_IDENTITY    2
MODIFIER_ZAG_SUMETAL_UA_MUSIC_FOOD    GreatWorkObjectType    ARGTYPE_IDENTITY    GREATWORKOBJECT_MUSIC
MODIFIER_ZAG_SUMETAL_UA_MUSIC_FOOD    YieldChange    ARGTYPE_IDENTITY    YIELD_FOOD
MODIFIER_ZAG_SUMETAL_UA_MUSIC_FOOD    Amount    ARGTYPE_IDENTITY    2

But either way, it just simply does nothing, and I don't know why. it's creating all the database entries, it's just not applying them in game, though it is applying the Great Musician Points ability to my units.
 
And a somewhat more random and general of a question: Does civ 6 break if someone creates a promotion tree with more than 7 promotions? I know units can have more than 7 promotions if they switch promotion class somewhere along the line, I've had 14 from holding units back to fill their promotion tree before letting them upgrade.
 
Code:
			<ModifierId>TRAIT_GREAT_WORK_PRODUCTION_SCULPTURE</ModifierId>
			<Name>GreatWorkObjectType</Name>
			<Value>GREATWORKOBJECT_SCULPTURE</Value>
		</Row>
		<Row>
			<ModifierId>TRAIT_GREAT_WORK_PRODUCTION_SCULPTURE</ModifierId>
			<Name>YieldType</Name>
			<Value>YIELD_PRODUCTION</Value>
		</Row>
		<Row>
			<ModifierId>TRAIT_GREAT_WORK_PRODUCTION_SCULPTURE</ModifierId>
			<Name>YieldChange</Name>
			<Value>2</Value>
		</Row>
You need to use YieldChange where you currently are using Amount, and you need to use YieldType where you are currently using YieldChange. When something does not work first thing is to look at Database log for error reports, then Modding log to ensure the file is actually being loaded by the game. Then, if these two logs show no errors, the next step is to look at the example you are copying from the original game code or else to logically think through the argument-types you are specifying.

It makes no logical sense to state "YieldChange" and then to specify a value of "YIELD_PRODUCTION" because everywhere else that "YieldChange" is used it requires an integer yield-amount value.

For whatever reason Firaxis decided to code this particular ModifierType to expect a "YieldChange" argument instead of an "Amount" argument. I certainly did not pick up on this until I looked at the game's original code for how the Nkisi trait works.

-------------------------------------------------------------

So this would be needed to attempt using one unified ModifierId
Code:
INSERT INTO    ModifierArguments
        (ModifierId,                                        Name,                        Value                        )
VALUES
        ('MODIFIER_ZAG_SUMETAL_UA_MUSIC_YIELD',                'YieldChange',                    '2,2'                            ),
        ('MODIFIER_ZAG_SUMETAL_UA_MUSIC_YIELD',                'GreatWorkObjectType',        'GREATWORKOBJECT_MUSIC'        ),
        ('MODIFIER_ZAG_SUMETAL_UA_MUSIC_YIELD',                'YieldType',                'YIELD_PRODUCTION,YIELD_FAITH'            );
 
Oh Bloody... that makes perfect sense. I see it now. I just some how was being blind, even when I went back and looked at the Nkisi ability for reference. Thank you!
 
Where in the LUA scripts is the function that assigns a leader/civ to the human player (in single player) when the player selects "random" as their option for leader?

Asking because I want to modify that function to pass a query to the leaders table that passes over certain leaders that I never want to play (if I select random leaders)
I want to make sure that for the AI players with "random" selected for their leader that they can still get every leader in the leaders table, but only if the human selects random do I want to make that change ...

I'm not entirely comfortable reading/writing LUA, but I'm pretty sure what I'm looking for will be in one of those setup LUA files so that's why I've targeted that. If anyone with better understanding of how the game selects leaders when "random" is the option knows where that's done, then yeah, I'll defer to your knowledge.

Spoiler :
I've especially been looking at PlayerSetupLogic.lua at line 316 (text colors added for reference):

function GetPlayerInfo(domain, leader_type)
if(leader_type ~= "RANDOM") then
local info_query = "SELECT CivilizationIcon, LeaderIcon, LeaderName, CivilizationName, LeaderAbilityName, LeaderAbilityDescription, LeaderAbilityIcon, CivilizationAbilityName, CivilizationAbilityDescription, CivilizationAbilityIcon, Portrait, PortraitBackground, PlayerColor from Players where Domain = ? and LeaderType = ? LIMIT 1";
local item_query = "SELECT Name, Description, Icon from PlayerItems where Domain = ? and LeaderType = ? ORDER BY SortIndex";
local info_results = CachedQuery(info_query, domain, leader_type);
local item_results = CachedQuery(item_query, domain, leader_type);


That info_query seems to me to be what's grabbing the civ/leader ... but I don't know the usage of "?" in the queries ... and further down I don't know how "CachedQuery" works ...
 
Back
Top Bottom