Grouping objects for easy access

zak15

Chieftain
Joined
Mar 11, 2017
Messages
12
Is it possible to group objects so that you can make mods a bit easier for them? I'll explain using the encampment and its buildings as an example.

Veterancy gives 30% production towards encampment district and its buildings. The XML makes it work by doing the following.

Code:
<Row>
            <ModifierId>VETERANCY_ENCAMPMENT_PRODUCTION</ModifierId>
            <Name>DistrictType</Name>
            <Value>DISTRICT_ENCAMPMENT</Value>
        </Row>
        <Row>
            <ModifierId>VETERANCY_ENCAMPMENT_PRODUCTION</ModifierId>
            <Name>Amount</Name>
            <Value>30</Value>
        </Row>
        <Row>
            <ModifierId>VETERANCY_BARRACKS_PRODUCTION</ModifierId>
            <Name>BuildingType</Name>
            <Value>BUILDING_BARRACKS</Value>
        </Row>
        <Row>
            <ModifierId>VETERANCY_BARRACKS_PRODUCTION</ModifierId>
            <Name>Amount</Name>
            <Value>30</Value>
        </Row>
        <Row>
            <ModifierId>VETERANCY_STABLE_PRODUCTION</ModifierId>
            <Name>BuildingType</Name>
            <Value>BUILDING_STABLE</Value>
        </Row>
        <Row>
            <ModifierId>VETERANCY_STABLE_PRODUCTION</ModifierId>
            <Name>Amount</Name>
            <Value>30</Value>
        </Row>
        <Row>
            <ModifierId>VETERANCY_ARMORY_PRODUCTION</ModifierId>
            <Name>BuildingType</Name>
            <Value>BUILDING_ARMORY</Value>
        </Row>
        <Row>
            <ModifierId>VETERANCY_ARMORY_PRODUCTION</ModifierId>
            <Name>Amount</Name>
            <Value>30</Value>
        </Row>
        <Row>
            <ModifierId>VETERANCY_MILITARY_ACADEMY_PRODUCTION</ModifierId>
            <Name>BuildingType</Name>
            <Value>BUILDING_MILITARY_ACADEMY</Value>
        </Row>
        <Row>
            <ModifierId>VETERANCY_MILITARY_ACADEMY_PRODUCTION</ModifierId>
            <Name>Amount</Name>
            <Value>30</Value>
        </Row>

It goes through each building, then adds 30% production to each one, one by one. It makes it difficult, however, if there are mods which add new buildings, like the Civ6 Improvements Patch does for a number of districts. Is it not possible to make something which gives the 30% production to any building which requires the encampment as a prerequisite district? Rather than make modifierids and so on for each building.
 
You do need modifiers for each different building, because of the modifier arguments, but it IS in fact possible to do what you need via SQL. I'm doing that exact thing for a couple of my leaders' abilities. I'm on mobile right now, so I can't copy/paste, but I'll try to remember when I get home to paste what I have. For example, one ability is to add +2 gold to each building in the Campus district (for that specific leader, anyways).
 
Something like this, in SQL, will generate all the ModiferArguments entries required to implement the Effect relating to Encampment (Defensive) Buildings only (the Effect/Modifier that achieved the same result for Districts is different, so the two distinct Kinds (KIND_BUILDING and KIND_DISTRICT) can't be grouped -- as luei333 explained above):

INSERT INTO ModifierArguments (ModifierId, Name, Type, Value, Extra, SecondExtra) VALUES
SELECT 'VETERANCY_PRODUCTION' || BuildingType, 'BuildingType', 'ARGTYPE_IDENTITY', BuildingType, NULL, NULL
FROM Buildings WHERE Buildings.PrereqDistrict='DISTRICT_ENCAMPMENT'
SELECT 'VETERANCY_PRODUCTION' || BuildingType, 'Amount', 'ARGTYPE_IDENTITY', 30, NULL, NULL
FROM Buildings WHERE Buildings.PrereqDistrict='DISTRICT_ENCAMPMENT';

However, this is only an SQL shortcut to arrive at the same outcome achieved in XML with a lot more typing. The difference is superficial. When GameSchema generates the DebugGameplay database, all the same rows will be generated in the relevant table (ModifierArguments). It all depends whether your prefer the slightly more opaque SQL over the more transparent, but much wordier, XML.

[I am just speculating out loud here] -- If your hope is to group all the items to which the Veterancy Policy applies in order to apply some other effect to them as a set (grouped on the basis of the common criteria that they are subject to the Veterancy Policy), this is quite different. You'd need to create a new table in the GameSchema that incorporates all of them tied to a common identifier (e.g. Veterancy Policy) and the find some common effect that you can apply to them notwithstanding that they are of different Kinds. There is also a column in the NavigationProperties table that marks all the rows in a single column in a designated table as a Property and then designates it as a collection or not. The SQL code in GameSchema comments that if the IsCollection column returns 1/true for a specific Property, that Property is considered a Collection.

Now, whether or not these Collections in NavigationProperties have any relation to KIND_COLLECTION entries in GameEffects.xml, I have no idea. It would seem that there must be some kind of connection, since there is no reason otherwise to use the same word. But there is a lot more IsCollection=true entries in NavigationProperties then there are objects listed under KIND_COLLECTION in GameEffects.xml. It may be simply that not all have been implemented as KIND_COLLECTION by the developers, or it may be that they are unrelated. If the reason for the disparity is that they have not been implemented, I am not sure that we have been given any, let alone a clear method for creating more objects of the KIND_COLLECTION. Are they hardcoded? May be. But then, GameEffects.xml makes a distinction in commentary between Core Collections and Civ6 Collections.

Anyways, I have no clue about the latter two paragraphs. :)
 
Yup, that's pretty much it. I have one of those SQL entries for all the different tables I need, Modifiers, TraitModifiers, ModifierArguments, Requirements, RequirementSets, etc. the advantage of doing it in SQL, rather than XML, is that it will generate the relevant modifiers for any NON-BASE buildings added to the district as well (perhaps subject to the whims of load order). Buildings added by future DLC, other mods, etc.
 
Great replies, thanks a lot guys. SQL is just fine too, and yeah, that's what I need it to do. Just make it run last, if that's possible, then make it do all that.

It's in case I can't think of a nice enough buff for Norway/England so I give one or the other a boost to all harbor district buildings and the harbor itself. Thanks guys.
 
@zak15

To make your mod component run last, adapt the mod I'm uploading here. It has only an SQL component, but you can add XML and other components as you see fit. The effect of making an individual mod component run last is achieved by inserting the <LoadOrder>X</LoadOrder> parameter in <Properties> ... </Properties> in the MODINFO file. This would have to be done for each individual mod component you want to run last. As per a convention that PlotinusRedux has proposed, you should use multiples of 100 for the LoadOrder integer. See the sample code below. The simple mod is attached. It enforces a desired minimum city distance at the last possible moment before the game database is complete and overrides any conflicting changes by any other mod or DLC (or base game) components.

Code:
  <Files>
    <File>EnforceCityDistanceSQL.sql</File>
  </Files>
    <Components>
        <UpdateDatabase id="SQL_Changes">
<!--The value of LoadOrder is intentionally set to a high integer of 10,100, to ensure that the SQL component loads after all other mod and DLC components.
    The Properties element may be safely removed altogether if this functionality is not desired. Conversely, the integer may be increased if the number of other mod
    and DLC components exceeds the value set below and the SQL component of this mod fails to load last of all.-->
            <Properties>
                <LoadOrder>100,100</LoadOrder>
            </Properties>
            <Items>
                <File>EnforceCityDistanceSQL.sql</File>
            </Items>
        </UpdateDatabase>
    </Components>
 

Attachments

  • EnforceCityDistance.zip
    1.6 KB · Views: 33
I have done some of this in my combined tweaks mod.

Basically what you're describing is generating a list of objects you want to apply a change to, then applying it.

Sometimes this is pretty easy to do based on the existing table structures. For example, hitting every single thing that is a Wonder on the Buildings table (added by your mod or someone else's) is simple:

UPDATE Buildings SET ~~whatever~~ WHERE IsWonder=1;


But sometimes it's more complex. For a case like you describe above, depending on the complexity involved, I might use a temporary table. Here's a super simple example:


Code:
CREATE TEMPORARY TABLE tbl_BuildingsNeedingModifiers AS
SELECT BuildingReplaces.CivUniqueBuildingType from BuildingReplaces
WHERE BuildingReplaces.ReplacesBuildingType='BUILDING_TEMPLE';

This creates a temporary table containing the results of the SELECT statement. The table will have one column, CivUniqueBuildingType. I can then feed this table into whatever other SQL statements I want, like so:


Code:
INSERT INTO BuildingModifiers
    (BuildingType, ModifierId)
SELECT tbl_BuildingsNeedingModifiers.CivUniqueBuildingType, 'MODIFIER_WHATEVER' ;

This would associate MODIFIER_WHATEVER with every building listed in tbl_BuildingsNeedingModifiers.


What's extra nice about using a temporary table is to debug, as long as you don't close or load a different mod, you can then go to into SQLite and query the table you created:

Code:
SELECT * FROM tbl_BuildingsNeedingModifiers

The results of a temporary table stick around until the database connection is closed. You can of course explicitly use DROP TABLE to drop tables when they are no longer needed (tho be careful not to drop any table created by Firaxis!)
 
Top Bottom