Example gameplay mod

I haven't seen precedence for Modifier Update so I just copy them, edit then and give them different id (creating brand new modifier that is edit of the original), then regularily update whatever references its id to the new one.
 
I was checking how to apply SQL files to the database, and fortunately it's real simple. It's also an UpdateDatabase action, just that the Item points to an SQL file.

Code:
				<UpdateDatabase>
					<Item>solver-test.sql</Item>
				</UpdateDatabase>

Tested with a simple update query:

Code:
UPDATE Unit_Stats SET Combat=200 WHERE Combat > 0;

The only thing to keep in mind is that dependencies need to be set up correctly, or else the query will run before the database is ready. If the above is done in a modinfo with no dependencies, it will fail because the query will run before any ages are loaded and so there's nothing to update. A dependency on the base game age modules should be set up, then everything is as expected.
 
What should I do if I want to change things that are more deeply nested and/or are outside of tags, like this from diplomacy-gameeffects.xml?
Code:
<Modifier id="PLAYER_DIPLOMACY_GRANT_POLICY_SLOT_COMPLETE" collection="COLLECTION_OWNER" effect="EFFECT_PLAYER_GRANT_TRADITION_SLOTS">
    <Argument name="Amount">1</Argument>
</Modifier>
I think these xml objects translate to dtos in the backend that then creates multiple entries within the -in-game db.

Ie. if you want to update the
1. 'collection', you actually need to update the DynamicModifiers table
2. 'amount', you need to update the ModifierArguments table

Then for all the requirements, you need to go through requirements, requirement sets, requirementsetrequirements etc
 
What should I do if I want to change things that are more deeply nested and/or are outside of tags, like this from diplomacy-gameeffects.xml?
Code:
<Modifier id="PLAYER_DIPLOMACY_GRANT_POLICY_SLOT_COMPLETE" collection="COLLECTION_OWNER" effect="EFFECT_PLAYER_GRANT_TRADITION_SLOTS">
    <Argument name="Amount">1</Argument>
</Modifier>

I'm sure that can be translated to the right XML, but it's probably easier to handle with SQL. My approach would be to examine the gameplay DB and find what the entry looks like there, then update it.

Looking at the DB, seems like modifiers that are used with arguments that come from other entities, live in the DynamicModifiers table. We can find PLAYER_DIPLOMACY_GRANT_POLICY_SLOT_COMPLETE and look it at there, but the argument with the value 1 lives in ModifierArguments. Let's look this one up:

SELECT * FROM ModifierArguments WHERE ModifierID = 'PLAYER_DIPLOMACY_GRANT_POLICY_SLOT_COMPLETE';

Code:
ModifierId                                                                                    Name    Extra    SecondExtra    Type    Value
PLAYER_DIPLOMACY_GRANT_POLICY_SLOT_COMPLETE                                                   Amount                                    1

So an update could be e.g.

UPDATE ModifierArguments SET Value = 2 WHERE ModifierID = 'PLAYER_DIPLOMACY_GRANT_POLICY_SLOT_COMPLETE' AND Name = 'Amount';
 
The only thing to keep in mind is that dependencies need to be set up correctly, or else the query will run before the database is ready. If the above is done in a modinfo with no dependencies, it will fail because the query will run before any ages are loaded and so there's nothing to update. A dependency on the base game age modules should be set up, then everything is as expected.
Is the base module sufficient for everything?
XML:
<Dependencies>
    <Mod id="base-standard" title="LOC_MODULE_BASE_STANDARD_NAME"/>
</Dependencies>
I'm trying to do as simple as a cost update for all buildings via SQL:
SQL:
UPDATE Constructibles
SET Cost = Cost * 3 WHERE ConstructibleClass = "BUILDING";
but it does not apply, unfortunately.

However, same syntax for Adjacency_YieldChanges works just fine, except (and I have just noticed this, as I'm piecing together the puzzle) for unique buildings. This latter is in age-antiquity, etc., and not base, while the standard adjacencies are in base.

Is it possible that I should add age-antiquity as prereq instead? But would that work for modern age buildings? Can all ages be added as dependencies, or that would break quantum physics with unimaginable consequences? :D
 
No, the base module is definitely not for everything. The base module is more like broad rules, but if there's anything you interact with only in a particular age (units, techs, civics, wonders, civs), it's probably in the age modules.

So ConstructibleClass exists in base, as the Improvement/Building/Wonder split is a broad, basic rule of Civ7. But the only buildings in base-standard are the Palace and City Hall, for which your cost modification technically works (I assume) but is pointless since they're automatically placed. To modify all building costs, you'd probably want to apply the change to the specific ages. My guess is you could define a criteria for any age:

Code:
<Criteria id="any-age" any="true">
    <AgeInUse>AGE_ANTIQUITY</AgeInUse>
    <AgeInUse>AGE_EXPLORATION</AgeInUse>
    <AgeInUse>AGE_MODERN</AgeInUse>
</Criteria>

and point to that Criteria in your ActionGroup. No need for additional dependencies though. That's my guess based on how the Shawnee/Tecumseh content is set up.
 
What should I do if I want to change things that are more deeply nested and/or are outside of tags, like this from diplomacy-gameeffects.xml?
Code:
<Modifier id="PLAYER_DIPLOMACY_GRANT_POLICY_SLOT_COMPLETE" collection="COLLECTION_OWNER" effect="EFFECT_PLAYER_GRANT_TRADITION_SLOTS">
    <Argument name="Amount">1</Argument>
</Modifier>
@Acken gave me XML-example how to modify arguments table. Case is very similar and I have been using it in my mods.


My xml-code for same looks today like this:
XML:
<?xml version="1.0" encoding="utf-8"?>
<Database>
    <ModifierArguments>
        <Update>
            <Where ModifierId="TRAIT_INITIAL_SETTLEMENT_CAP" name="Amount"/>
            <Set Value="4"/>
        </Update>
    </Modifiers>
</Database>
 
I'm sure that can be translated to the right XML, but it's probably easier to handle with SQL. My approach would be to examine the gameplay DB and find what the entry looks like there, then update it.

Looking at the DB, seems like modifiers that are used with arguments that come from other entities, live in the DynamicModifiers table. We can find PLAYER_DIPLOMACY_GRANT_POLICY_SLOT_COMPLETE and look it at there, but the argument with the value 1 lives in ModifierArguments. Let's look this one up:

SELECT * FROM ModifierArguments WHERE ModifierID = 'PLAYER_DIPLOMACY_GRANT_POLICY_SLOT_COMPLETE';

Code:
ModifierId                                                                                    Name    Extra    SecondExtra    Type    Value
PLAYER_DIPLOMACY_GRANT_POLICY_SLOT_COMPLETE                                                   Amount                                    1

So an update could be e.g.

UPDATE ModifierArguments SET Value = 2 WHERE ModifierID = 'PLAYER_DIPLOMACY_GRANT_POLICY_SLOT_COMPLETE' AND Name = 'Amount';

where is the gameplay DB? I haven't been able to find it.
 
how would you go about converting the structure of the tecumseh+shawnee dlc to a mod? i want to use it as a template but i can't get it to register as a seperate mod that i could use as a template
 
where is the gameplay DB? I haven't been able to find it.

Edit your AppOptions.txt to tell it to dump the DB to a file, there's a line early in the file you should change:

CopyDatabasesToDisk 1

That will get the databases dumped the Debug subfolder of your Civ7 user folder (the one with mods and saves, not the install one). Examining the databases there is easier than dealing with XML.
 
The mod is going to update an Antiquity unit, but that's only possible when the game is actually in Antiquity. If you're in Age 2 or 3, Medjay aren't even present in the game data, so it'd be an error to update them. As such, the mod defines two criteria for its actions: antiquity-age-current to check if we're in Antiquity, and always to run always.

In the always case, we update the gameplay database with data/units.xml. If we're in Antiquity, we additionally update the text from text/en_us/UnitText.xml. [I think it would work fine to update the non-existent Medjay in other ages as well, but it's not a clean practice].
Help me out here - why update the unit data always if the unit only exists in Antiquity? Wouldn't it make more sense to update the unit data only in Antiquity, and then make the text changes always in case those localization tags are referenced again?
 
Yes, that was for simplicity. I should probably edit as best practice is clearly to update based on the ages, even when it's safe to have an "always" change.
 
Out of lazyness, I wanted to update various Civics across Ages with additional effect in single file and it did fail on not recognizing the FK when the Civic did not exist yet - had to split it into three files loaded exclusively at their appropriate Ages nevertheless not as best practice but as neccesity.
 
The "one file, load always" approach will work as long as the file only affects one age. If it affects things from different ages, a proper age setup has to be used in the mod.
 
I appreciate this thread and all the explanations, it's very helpful. I was hoping to modify the steal tech/civic espionage actions so the SuccessRate isn't always 100% but it doesn't seem possible. I do see the UI updating with the new percentage I use but those actions are always successful no matter what. Other columns for espionage can definitely be modified and are reflected in game like the duration of an action, reveal chance, etc. I'm thinking SuccessRate must be controlled in the core game logic but maybe someone here has any ideas on how to confirm that?
 
No, the base module is definitely not for everything. The base module is more like broad rules, but if there's anything you interact with only in a particular age (units, techs, civics, wonders, civs), it's probably in the age modules.

So ConstructibleClass exists in base, as the Improvement/Building/Wonder split is a broad, basic rule of Civ7. But the only buildings in base-standard are the Palace and City Hall, for which your cost modification technically works (I assume) but is pointless since they're automatically placed. To modify all building costs, you'd probably want to apply the change to the specific ages. My guess is you could define a criteria for any age:

Code:
<Criteria id="any-age" any="true">
    <AgeInUse>AGE_ANTIQUITY</AgeInUse>
    <AgeInUse>AGE_EXPLORATION</AgeInUse>
    <AgeInUse>AGE_MODERN</AgeInUse>
</Criteria>

and point to that Criteria in your ActionGroup. No need for additional dependencies though. That's my guess based on how the Shawnee/Tecumseh content is set up.

What is the downside to just doing this as a default set-up to make sure your unit mods will work?
 
It's not good modding - there will be errors if you try to update things that don't exist (like updating Exploration units in an Antiquity game) and those errors may cause the rest of the mod logic to be ignored, depending on how the game is set up. Even in the best case, where it works, it will produce multiple errors from your mod in the log, and that will make it much harder to later diagnose a different error you may add. Just not a good idea all around - doing things properly will save a lot of time in the long run.
 
@Solver I'm a bit confused about the sample mod. If the Medjay unit is only in Antiquity, how come you are doing the database update in the criteria="always"?
 
I'll edit that soon because it's not a good practice - it's guaranteed to work because it only updates one unit, but that's not a good idea.
 
I'll edit that soon because it's not a good practice - it's guaranteed to work because it only updates one unit, but that's not a good idea.
Thanks for the feedback Solver. As I get deeper I'm seeing the matrix a little more and I see how it could go wrong. Thanks!
 
Back
Top Bottom