Skip to main content

RequirementSets

But what if we don’t want to grant +5 Food to ALL cities? What if we want to restrict it further to cities with a Granary? Surely there isn’t a CollectionType just for that? That’s where RequirementSets come in! These are sets of Requirements that must be met for a modifier to be active. A modifier can have up to two RequirementSets, one for the Owner, that checks whether the Owner meets the requirements before activating at all, and one for the Subject, which checks each Subject, then only triggers the effect on those that meet the requirements. RequirementSets are assigned to Modifiers (using the RequirementSetID as a unique identifier) via the optional OwnerRequirementSetID and SubjectRequirementSetID columns in the Modifiers table. A modifier can have one, none, or both of those columns filled as desired.
INSERT INTO Modifiers
    (
        ModifierId,
        ModifierType,
        OwnerRequirementSetID,
        SubjectRequirementSetID
    )
VALUES
    (
        'FXS_A_NEW_MODIFIER',
        'FXS_A_NEW_MODIFIER_TYPE',
        NULL,
        'FXS_MY_SUBJECT_REQUIREMENTSET',
    ),
    (
        'FXS_YET_ANOTHER_MODIFIER',
        'FXS_YET_ANOTHER_MODIFIER_TYPE',
        'FXS_MY_OWNER_REQUIREMENTSET',
        'FXS_MY_SUBJECT_REQUIREMENTSET',
    );
Getting back to our example. We’ll need to create a new entry in the RequirementSets table, then go back to where we defined our modifier and edit it to include the new Requirement Set (we’ll assign it as a SubjectRequirementSetID since we want to check whether each city has a Granary).
INSERT INTO RequirementSets
    (
        RequirementSetId,
        RequirementSetType
    )
VALUES 
    (
        'FXS_IRRIGATION_DITCHES_REQUIREMENTS',
        'REQUIREMENTSET_TEST_ALL'
    );
INSERT INTO Modifiers
    (
        ModifierId,
        ModifierType,
        SubjectRequirementSetID
    )
VALUES
    (
        'FXS_IRRIGATION_DITCHES_MODIFIER',
        'FXS_IRRIGATION_DITCHES_MOD_TYPE',
        'FXS_IRRIGATION_DITCHES_REQUIREMENTS'
    );
Note: What’s this REQUIREMENTSET_TEST_ALL thing? Requirement Sets are collections of multiple requirements. There are two ways these requirements can be collectively handled.
  • REQUIREMENTSET_TEST_ALL means ALL the requirements in the set must be met for the modifier to activate.
  • REQUIREMENTSET_TEST_ANY means the modifier will be active if ANY of the requirements in the set are met.

Requirements

Now we need to create a requirement and assign it to the Requirement Set we just created.
INSERT INTO Requirements
    (
        RequirementId,
        RequirementType
    )
VALUES
    (
        'REQUIRES_FXS_IRRIGATION_DITCHES_HAS_GRANARY',
        'REQUIREMENT_CITY_HAS_BUILDING'
    );
    
INSERT INTO RequirementSetRequirements
    (
        RequirementSetId,
        RequirementId
    )
VALUES
    ( 
        'FXS_IRRIGATION_DITCHES_REQUIREMENTS',
        'REQUIRES_FXS_IRRIGATION_DITCHES_HAS_GRANARY'
    );
A requirement consists of a RequirementId which is a unique identifier for the requirement, and a RequirementType that determines what it’s checking for (In this case, we’re checking if a city has a building via REQUIREMENT_CITY_HAS_BUILDING) . Unlike modifiers, you do not create a custom RequirementType, instead reusing an existing one. You’ll also determine the specifics of a modifier via the RequirementArguments table. Similar to ModifierArguments, the valid Names and Values will depend on the RequirementType. You can look for other requirements that share the same RequirementType in the game files or through gameplay-copy.sqlite in the Debug folder.
INSERT INTO RequirementArguments
    (
        RequirementId,
        Name,
        Value
    )
VALUES
    (
        'FXS_IRRIGATION_DITCHES_MODIFIER',
        'BuildingType',
        'BUILDING_GRANARY'
    );

Recap

Phew! That was a lot wasn’t it? Let’s recap:
  1. Modifiers are used to handle many game effects.
  2. Modifiers have an Owner (how it is introduced to the game), and a Subject (what does it affect?)
  3. The Owner is generally determined by which “EntityModifier Table(s)” it’s assigned to.
  4. The effect of a modifier is determined by two things:
    1. The ModifierType, which is a combination of an EffectType (what it does) and a CollectionType (what does it affect/what is the subject).
    2. Its ModifierArguments, which clarifies the specifics of the EffectType.
  5. When a modifier activates can be controlled by RequirementSets. They can check either the Owner (via OwnerRequirementSetID), or the Subjects (via SubjectRequirementSetID).
    • When checking the Owner: the Owner must meet the requirements for the modifier to activate at all.
    • When checking the Subject(s): As there can be multiple subjects, each subject is checked individually. Only subjects that meet the requirements will have the modifier applied to them.
  6. RequirementSets consist of one or more Requirements.
  7. What a Requirement checks for is determined by its RequirementType and its associated RequirementArguments .
Warning: Modifiers interact a little weirdly with saved games. When you save a game, any modifiers that exist get saved as they are. If you change them in the game’s database, modifiers in the saved game do NOT get changed, though modifiers added after loading the save will use the new definition. For this reason, it’s good practice to start a brand new game to test out any modifier changes.

GameEffects XML files

The Modifier System requires managing a LOT of different tables. Which is why Civilization VII has a simpler way of managing it all. When working with XML, instead of trying to modify multiple different tables in the database, you can instead do this:
<?xml version="1.0" encoding="utf-8"?>
<GameEffects xmlns="GameEffects">
    <Modifier id="MOD_PERIPLUS_OF_THE_ERYTHRAEAN_SEA_RESOURCE_CAP" collection="COLLECTION_PLAYER_CITIES" effect="EFFECT_CITY_ADJUST_RESOURCE_CAP">
        <SubjectRequirements type="REQUIREMENTSET_TEST_ANY">
            <Requirement type="REQUIREMENT_PLOT_ADJACENT_TO_COAST"/>
            <Requirement type="REQUIREMENT_PLOT_ADJACENT_TO_RIVER">
                <Argument name="Navigable">true</Argument>
            </Requirement>
        </SubjectRequirements>
        <Argument name="Amount">1</Argument>
        <String context="Description">LOC_MOD_PERIPLUS_OF_THE_ERYTHRAEAN_SEA_RESOURCE_CAP_DESCRIPTION</String>
    </Modifier>
 </GameEffects>
Note how the root element is <GameEffects> instead of <Database>. You can use this on an UpdateDatabase action with the Game scope as per usual. This allows you to create a new modifier much more easily, with the ModifierType, ModifierArguments RequirementSetID, and so forth being generated dynamically based off the provided id. Note the structure. You create a Modifier, define an id, and give it a collection and effect. This allows you to skip defining the ModifierType in a separate step. You define the arguments as child elements of the Modifier. To add requirements, you add either SubjectRequirements or OwnerRequirements child elements, with further Requirement and Argument elements as necessary. There are two caveats to this. The first and most important is you cannot also modify the rest of the database in the same file. The XML file you use to create modifiers cannot also be used to add buildings, leaders, and so on. This is because we’re using <GameEffects> instead of <Database>. You’ll need to manage those in a separate file (you can assign both files to the same ActionGroup in the .modinfo file though)! The second is the various “EntityModifier Tables” are not part of the Modifier declaration. That bit is considered part of the “rest of the database” that was mentioned in the previous point. So the various ConstructibleModifiers, GameModifiers, TraditionModifiers and so on, cannot be added via a gameplay database file with <GameEffects> as the root element.