Quick Modding Questions Thread

I'm trying to make a Governor promotion give Tourism to a building:
Code:
    <GovernorPromotionModifiers>
        <Row GovernorPromotionType="GOVERNOR_PROMOTION_WATER_WORKS" ModifierId="SM_TOURISM_BONUS_LIGHTHOUSE"/>
    </GovernorPromotionModifiers>
    <Modifiers>
        <Row ModifierId="SM_TOURISM_BONUS_LIGHTHOUSE" ModifierType="MODIFIER_SINGLE_CITY_ADJUST_TOURISM" SubjectRequirementSetId="SM_CITY_HAS_LIGHTHOUSE_REQ"/>
    </Modifiers>
    <ModifierArguments>
        <Row ModifierId="SM_TOURISM_BONUS_LIGHTHOUSE" Name="Amount" Value="2"/>
    </ModifierArguments>
    <RequirementSets>
        <Row RequirementSetId="SM_CITY_HAS_LIGHTHOUSE_REQ" RequirementSetType="REQUIREMENTSET_TEST_ALL"/>
    </RequirementSets>
    <RequirementSetRequirements>
        <Row RequirementSetId="SM_CITY_HAS_LIGHTHOUSE_REQ" RequirementId="SM_CITY_HAS_LIGHTHOUSE_REQID"/>
    </RequirementSetRequirements>
    <Requirements>
        <Row RequirementId="SM_CITY_HAS_LIGHTHOUSE_REQID" RequirementType="REQUIREMENT_CITY_HAS_BUILDING"/>
    </Requirements>
    <RequirementArguments>
       <Row RequirementId="SM_CITY_HAS_LIGHTHOUSE_REQID" Name="BuildingType" Value="BUILDING_LIGHTHOUSE" />
    </RequirementArguments>
No working. At first, I tried "MODIFIER_PLAYER_DISTRICTS_ADJUST_TOURISM_CHANGE", and that didn't work either. I'd rather the Tourism be generated from the district, rather than the City Center. Any ideas?
 
Hi friends!

Here's a LuaReplace situation that's been bugging me, and hopefully someone else has figured out a better solution. The purpose of this mod is to show only the Great People progress for my civilization and hide the progress for everyone else. I've isolated the exact code changes necessary... it's in the AddRecruit function (line 153) in the file ..\Sid Meier's Civilization VI\Base\Assets\UI\Popups\GreatPeoplePopup.lua. Here is the function in question, with the adjustment I want to make. (It's a large function, but I marked my two tiny changes with "-- DB". The rest of the function is exactly what ships with Civ6.)
Code:
function AddRecruit( kData:table, kPerson:table )

    local instance        :table = m_greatPersonPanelIM:GetInstance();
    local classData        :table = GameInfo.GreatPersonClasses[kPerson.ClassID];
    local individualData:table = GameInfo.GreatPersonIndividuals[kPerson.IndividualID];

    if (kPerson.ClassID ~= nil) then
        local portrait:string = "ICON_GENERIC_" .. classData.GreatPersonClassType;
        portrait = portrait:gsub("_CLASS","_INDIVIDUAL");
        instance.Portrait:SetIcon(portrait);
        instance.CircleFlare:SetHide(false);

        local classText:string = Locale.Lookup(classData.Name);
        instance.ClassName:SetText(classText);
        instance.ClassName:SetHide(false);
    else
        instance.CircleFlare:SetHide(true);
        instance.ClassName:SetHide(true);
    end
 
    if kPerson.IndividualID ~= nil then
        local individualName:string = Locale.ToUpper(kPerson.Name);
        instance.IndividualName:SetText( individualName );
    end

    if kPerson.EraID ~= nil then
        local eraName:string = Locale.ToUpper(Locale.Lookup(GameInfo.Eras[kPerson.EraID].Name));
        instance.EraName:SetText( eraName );
    end
 
    if instance["m_EffectsIM"] ~= nil then
        instance["m_EffectsIM"]:ResetInstances();
    else
        instance["m_EffectsIM"] = InstanceManager:new("EffectInstance",    "Top",    instance.EffectStack);
    end

    if kPerson.PassiveNameText ~= nil and kPerson.PassiveNameText ~= "" then
        local effectInst:table    = instance["m_EffectsIM"]:GetInstance();
        local effectText:string = kPerson.PassiveEffectText;
        local fullText:string    = kPerson.PassiveNameText .. "[NEWLINE][NEWLINE]" .. effectText;
        effectInst.Text:SetText( effectText );
        effectInst.EffectTypeIcon:SetToolTipString( fullText );
        effectInst.PassiveAbilityIcon:SetHide(false);
        effectInst.ActiveAbilityIcon:SetHide(true);
    end

    if (kPerson.ActionNameText ~= nil and kPerson.ActionNameText ~= "") then
        local effectInst:table    = instance["m_EffectsIM"]:GetInstance(); 
        local effectText:string    = kPerson.ActionEffectText;
        local fullText:string    = kPerson.ActionNameText; 
        if (kPerson.ActionCharges > 0) then
            fullText = fullText .. " (" .. Locale.Lookup("LOC_GREATPERSON_ACTION_CHARGES", kPerson.ActionCharges) .. ")";
        end
        fullText = fullText .. "[NEWLINE]" .. kPerson.ActionUsageText;
        fullText = fullText .. "[NEWLINE][NEWLINE]" .. effectText;
        effectInst.Text:SetText( effectText );
        effectInst.EffectTypeIcon:SetToolTipString( fullText );

        local actionIcon:string = classData.ActionIcon;
        if actionIcon ~= nil and actionIcon ~= "" then
            local textureOffsetX:number, textureOffsetY:number, textureSheet:string = IconManager:FindIconAtlas(actionIcon, SIZE_ACTION_ICON);
            if(textureSheet == nil or textureSheet == "") then
                UI.DataError("Could not find icon in ViewCurrent: icon=\""..actionIcon.."\", iconSize="..tostring(SIZE_ACTION_ICON) );
            else
                effectInst.ActiveAbilityIcon:SetTexture(textureOffsetX, textureOffsetY, textureSheet);
                effectInst.ActiveAbilityIcon:SetHide(false);
                effectInst.PassiveAbilityIcon:SetHide(true);
            end
        else
            effectInst.ActiveAbilityIcon:SetHide(true);
        end
    end

    if instance["m_RecruitIM"] ~= nil then
        instance["m_RecruitIM"]:ResetInstances();
    else
        instance["m_RecruitIM"] = InstanceManager:new("RecruitInstance", "Top", instance.RecruitStack);
    end

    if instance["m_RecruitExtendedIM"] ~= nil then
        instance["m_RecruitExtendedIM"]:ResetInstances();
    else
        instance["m_RecruitExtendedIM"] = InstanceManager:new("RecruitInstance", "Top", instance.RecruitInfoStack);
    end

    if kPerson.IndividualID ~= nil and kPerson.ClassID ~= nil then

        -- Buy via gold
        if (HasCapability("CAPABILITY_GREAT_PEOPLE_RECRUIT_WITH_GOLD") and (not kPerson.CanRecruit and not kPerson.CanReject and kPerson.PatronizeWithGoldCost ~= nil and kPerson.PatronizeWithGoldCost < 1000000)) then
            instance.GoldButton:SetText(kPerson.PatronizeWithGoldCost .. "[ICON_Gold]");
            instance.GoldButton:SetToolTipString(GetPatronizeWithGoldTT(kPerson));
            instance.GoldButton:SetVoid1(kPerson.IndividualID);
            instance.GoldButton:RegisterCallback(Mouse.eLClick, OnGoldButtonClick);
            instance.GoldButton:SetDisabled((not kPerson.CanPatronizeWithGold) or IsReadOnly());
            instance.GoldButton:SetHide(false);
        else
            instance.GoldButton:SetHide(true);
        end

        -- Buy via Faith
        if (HasCapability("CAPABILITY_GREAT_PEOPLE_RECRUIT_WITH_FAITH") and (not kPerson.CanRecruit and not kPerson.CanReject and kPerson.PatronizeWithFaithCost ~= nil and kPerson.PatronizeWithFaithCost < 1000000)) then
            instance.FaithButton:SetText(kPerson.PatronizeWithFaithCost .. "[ICON_Faith]");
            instance.FaithButton:SetToolTipString(GetPatronizeWithFaithTT(kPerson));
            instance.FaithButton:SetVoid1(kPerson.IndividualID);
            instance.FaithButton:RegisterCallback(Mouse.eLClick, OnFaithButtonClick);
            instance.FaithButton:SetDisabled((not kPerson.CanPatronizeWithFaith) or IsReadOnly());
            instance.FaithButton:SetHide(false);
        else
            instance.FaithButton:SetHide(true);
        end

        -- Recruiting
        if (HasCapability("CAPABILITY_GREAT_PEOPLE_CAN_RECRUIT") and kPerson.CanRecruit and kPerson.RecruitCost ~= nil) then
            instance.RecruitButton:SetToolTipString( Locale.Lookup("LOC_GREAT_PEOPLE_RECRUIT_DETAILS", kPerson.RecruitCost) );
            instance.RecruitButton:SetVoid1(kPerson.IndividualID);
            instance.RecruitButton:RegisterCallback(Mouse.eLClick, OnRecruitButtonClick);
            instance.RecruitButton:SetHide(false);

            -- Auto scroll to first recruitable person.
            if kInstanceToShow==nil then
                kInstanceToShow = instance;
            end
        else
            instance.RecruitButton:SetHide(true);
        end

        -- Rejecting
        if (HasCapability("CAPABILITY_GREAT_PEOPLE_CAN_REJECT") and kPerson.CanReject and kPerson.RejectCost ~= nil) then
            instance.RejectButton:SetToolTipString( Locale.Lookup("LOC_GREAT_PEOPLE_PASS_DETAILS", kPerson.RejectCost ) );
            instance.RejectButton:SetVoid1(kPerson.IndividualID);
            instance.RejectButton:RegisterCallback(Mouse.eLClick, OnRejectButtonClick);
            instance.RejectButton:SetHide(false);
        else
            instance.RejectButton:SetHide(true);
        end

        -- If Recruit or Reject buttons are shown hide the minimized recruit stack
        if not instance.RejectButton:IsHidden() or not instance.RecruitButton:IsHidden() then
            instance.RecruitMinimizedStack:SetHide(true);
        else
            instance.RecruitMinimizedStack:SetHide(false);
        end
 
        -- Recruiting standings
        -- Let's sort the table first by points total, then by the lower player id (to push yours toward the top of the list for readability)
        local recruitTable: table = {};
        for i, kPlayerPoints in ipairs(kData.PointsByClass[kPerson.ClassID]) do
-- DB
            if (kPlayerPoints.PlayerID == Game.GetLocalPlayer()) then
                table.insert(recruitTable,kPlayerPoints);
            end
-- /DB
        end
        table.sort(recruitTable,
            function (a,b)
                if(a.PointsTotal == b.PointsTotal) then
                    return a.PlayerID < b.PlayerID;
                else
                    return a.PointsTotal > b.PointsTotal;
                end
                end);

        for i, kPlayerPoints in ipairs(recruitTable) do
            if (kPlayerPoints.PlayerID == Game.GetLocalPlayer()) then
                FillRecruitInstance(instance.LocalPlayerRecruitInstance, kPlayerPoints, kPerson, classData);
            else
                local recruitInst:table = instance["m_RecruitIM"]:GetInstance();
                FillRecruitInstance(recruitInst, kPlayerPoints, kPerson, classData);
            end

            local recruitExtendedInst:table = instance["m_RecruitExtendedIM"]:GetInstance();
            FillRecruitInstance(recruitExtendedInst, kPlayerPoints, kPerson, classData);
        end

        if (kPerson.EarnConditions ~= nil and kPerson.EarnConditions ~= "") then
            instance.RecruitInfo:SetText("[COLOR_Civ6Red]" .. Locale.Lookup("LOC_GREAT_PEOPLE_CANNOT_EARN_PERSON") .. "[ENDCOLOR]");
            instance.RecruitInfo:SetToolTipString("[COLOR_Civ6Red]" .. kPerson.EarnConditions .. "[ENDCOLOR]");
            instance.RecruitInfo:SetHide(false);
        else
            instance.RecruitInfo:SetHide(true);
        end

        instance.RecruitScroll:CalculateSize();
    end

 
    if kPerson.IndividualID ~= nil then
        -- Set the biography buttons
        instance.BiographyBackButton:SetVoid1( kPerson.IndividualID );
        instance.BiographyBackButton:RegisterCallback( Mouse.eLClick, OnBiographyClick );
        instance.BiographyOpenButton:SetVoid1( kPerson.IndividualID );
        instance.BiographyOpenButton:RegisterCallback( Mouse.eLClick, OnBiographyClick );
 
        -- Setup extended recruit info buttons
        instance.RecruitInfoOpenButton:SetVoid1( kPerson.IndividualID );
        instance.RecruitInfoOpenButton:RegisterCallback( Mouse.eLClick, OnRecruitInfoClick );
-- DB
        instance.RecruitInfoOpenButton:SetHide(true) ;
-- /DB
        instance.RecruitInfoBackButton:SetVoid1( kPerson.IndividualID );
        instance.RecruitInfoBackButton:RegisterCallback( Mouse.eLClick, OnRecruitInfoClick );

        m_kGreatPeople[kPerson.IndividualID] = instance; -- Store instance for later look up
    end

    local noneAvailable        :boolean = (kPerson.IndividualID == nil);
    instance.IndividualName:SetHide( noneAvailable );
    instance.EraName:SetHide( noneAvailable );
    instance.MainInfo:SetHide( noneAvailable );
    instance.BiographyBackButton:SetHide( noneAvailable );
    instance.ClaimedLabel:SetHide( not noneAvailable );
    instance.BiographyArea:SetHide( true );
    instance.RecruitInfoArea:SetHide( true );
    instance.FadedBackground:SetHide( true );
    instance.BiographyOpenButton:SetHide( noneAvailable );
 
    instance.EffectStack:CalculateSize();
    instance.EffectStackScroller:CalculateSize();
end

Great, the function works just how I want it to... so what's the problem?

The problem is... how do I make a mod that carries out those two little changes?

As far as I know, the best way to replace a function is to make my own lua file (DB_GreatPeoplePopup.lua) that first includes the base file and then overwrites the function I want to replace, like so:
Code:
include('GreatPeoplePopup')

function AddRecruit( kData:table, kPerson:table )
    ...
        <my version of the function here>
    ...
end

And this gets wired up in modinfo like so:
Code:
<ReplaceUIScript id="DB_GreatPeoplePopup">
  <Properties>
    <LoadOrder>9999999</LoadOrder>
    <LuaContext>GreatPeoplePopup</LuaContext>
    <LuaReplace>DB_GreatPeoplePopup.lua</LuaReplace>
  </Properties>
</ReplaceUIScript>

But the resulting mod creates a blank Great Person screen, because Firaxis's AddRecruit function uses a bunch of what I call "locally-global" variables... variables that are defined as locals to their own file, but are defined outside of the file's functions so that they can be referenced within the function like global variables. For instance, in the first line of the function I copied above, it invokes the locally-global variable "m_greatPersonPanelIM:GetInstance();". This means that even when my AddRecruit() function is an exact copy-paste of Firaxis's AddRecruit() function, the Great Person Screen is completely blank... because my function doesn't have access to "m_greatPersonPanelIM" and the other locally-global variables.

Is there a way for my mod to access those locally-global variables from the Firaxis file?

Now in some situations you can cache the base function, and then run it in the modified function like so:
Code:
include('GreatPeoplePopup')

PRIOR_AddRecruit = AddRecruit

function AddRecruit( kData:table, kPerson:table )
    PRIOR_AddRecruit(kData, kPerson)
end

And when I do that, the mod will run Firaxis's AddRecruit() and show the normal Great Person Screen. And in some instances the Firaxis function will return the thing it created, and so we can save that return value to our own variable and then modify it like this:
Code:
include('GreatPeoplePopup')

PRIOR_AddRecruit = AddRecruit

function AddRecruit( kData:table, kPerson:table )
    local ReturnedInstance = PRIOR_AddRecruit(kData, kPerson)
    ReturnedInstance.RecruitInfoOpenButton:SetHide(true)
end

But no such luck with this function. AddRecruit() does not return the instance it creates.

The only solution I've been able to come up with is to copy-paste the entire GreatPeoplePopup.lua file into my mod... and then make my modifications. But that's extremely inefficient for only a couple tiny changes. And it's a major maintenance headache every time they release a patch. And it means that my mod completely overwrites all other mods that adjust something in the GreatPeoplePopup.lua file.

Does anyone know of a better solution?

Kind regards,
DB
 
Last edited:
I can't see a solution here, unless Firaxis change the "local-global" to "global", as they've done in some other files when they needed to use them in their own addons IIRC.
 
Thanks Gedemon! I appreciate the quick response.

One other thought here is that I could try to approach this from the xml side. That is, take the xml element I want hidden and set its visibility to false or offset it off the screen.

Is there a way to alter just one element in GreatPeoplePopup.xml? Something like I mentioned above with lua where you include the base xml file and then just overwrite the one element that needs changing.

Or is my only option with UI-based xml to use ImportFile and replace all the elements?

Kind regards,
DB
 
So far as I am aware the xml context file is all or none. They are each their own "animal" and there's no equivalent to the "include" command available in an lua script.
 
I'm trying to make a Governor promotion give Tourism to a building:
Code:
    <GovernorPromotionModifiers>
        <Row GovernorPromotionType="GOVERNOR_PROMOTION_WATER_WORKS" ModifierId="SM_TOURISM_BONUS_LIGHTHOUSE"/>
    </GovernorPromotionModifiers>
    <Modifiers>
        <Row ModifierId="SM_TOURISM_BONUS_LIGHTHOUSE" ModifierType="MODIFIER_SINGLE_CITY_ADJUST_TOURISM" SubjectRequirementSetId="SM_CITY_HAS_LIGHTHOUSE_REQ"/>
    </Modifiers>
    <ModifierArguments>
        <Row ModifierId="SM_TOURISM_BONUS_LIGHTHOUSE" Name="Amount" Value="2"/>
    </ModifierArguments>
    <RequirementSets>
        <Row RequirementSetId="SM_CITY_HAS_LIGHTHOUSE_REQ" RequirementSetType="REQUIREMENTSET_TEST_ALL"/>
    </RequirementSets>
    <RequirementSetRequirements>
        <Row RequirementSetId="SM_CITY_HAS_LIGHTHOUSE_REQ" RequirementId="SM_CITY_HAS_LIGHTHOUSE_REQID"/>
    </RequirementSetRequirements>
    <Requirements>
        <Row RequirementId="SM_CITY_HAS_LIGHTHOUSE_REQID" RequirementType="REQUIREMENT_CITY_HAS_BUILDING"/>
    </Requirements>
    <RequirementArguments>
       <Row RequirementId="SM_CITY_HAS_LIGHTHOUSE_REQID" Name="BuildingType" Value="BUILDING_LIGHTHOUSE" />
    </RequirementArguments>
No working. At first, I tried "MODIFIER_PLAYER_DISTRICTS_ADJUST_TOURISM_CHANGE", and that didn't work either. I'd rather the Tourism be generated from the district, rather than the City Center. Any ideas?
Your problem would appear to be in how you are attempting to use MODIFIER_SINGLE_CITY_ADJUST_TOURISM.
Code:
"DynamicModifiers"
ModifierType				CollectionType		EffectType
MODIFIER_SINGLE_CITY_ADJUST_TOURISM	COLLECTION_OWNER	EFFECT_ADJUST_CITY_TOURISM

"Modifiers"
ModifierId				ModifierType
RELIQUARIES_RELIC_TOURISM_MODIFIER	MODIFIER_SINGLE_CITY_ADJUST_TOURISM
STBASILS_ADDRELIGIOUSTOURISM		MODIFIER_SINGLE_CITY_ADJUST_TOURISM
CURATOR_DOUBLE_SCULPTURE_TOURISM	MODIFIER_SINGLE_CITY_ADJUST_TOURISM
CURATOR_DOUBLE_PORTRAIT_TOURISM		MODIFIER_SINGLE_CITY_ADJUST_TOURISM
CURATOR_DOUBLE_LANDSCAPE_TOURISM	MODIFIER_SINGLE_CITY_ADJUST_TOURISM
CURATOR_DOUBLE_RELIGIOUS_TOURISM	MODIFIER_SINGLE_CITY_ADJUST_TOURISM
CURATOR_DOUBLE_WRITING_TOURISM		MODIFIER_SINGLE_CITY_ADJUST_TOURISM
CURATOR_DOUBLE_MUSIC_TOURISM		MODIFIER_SINGLE_CITY_ADJUST_TOURISM

"ModifierArguments"
ModifierId				Name	Value
RELIQUARIES_RELIC_TOURISM_MODIFIER	GreatWorkObjectType	GREATWORKOBJECT_RELIC
RELIQUARIES_RELIC_TOURISM_MODIFIER	ScalingFactor	300
STBASILS_ADDRELIGIOUSTOURISM		Religious	1
STBASILS_ADDRELIGIOUSTOURISM		ScalingFactor	200
CURATOR_DOUBLE_LANDSCAPE_TOURISM	GreatWorkObjectType	GREATWORKOBJECT_LANDSCAPE
CURATOR_DOUBLE_LANDSCAPE_TOURISM	ScalingFactor	200
CURATOR_DOUBLE_MUSIC_TOURISM		GreatWorkObjectType	GREATWORKOBJECT_MUSIC
CURATOR_DOUBLE_MUSIC_TOURISM		ScalingFactor	200
CURATOR_DOUBLE_PORTRAIT_TOURISM		GreatWorkObjectType	GREATWORKOBJECT_PORTRAIT
CURATOR_DOUBLE_PORTRAIT_TOURISM		ScalingFactor	200
CURATOR_DOUBLE_RELIGIOUS_TOURISM	GreatWorkObjectType	GREATWORKOBJECT_RELIGIOUS
CURATOR_DOUBLE_RELIGIOUS_TOURISM	ScalingFactor	200
CURATOR_DOUBLE_SCULPTURE_TOURISM	GreatWorkObjectType	GREATWORKOBJECT_SCULPTURE
CURATOR_DOUBLE_SCULPTURE_TOURISM	ScalingFactor	200
CURATOR_DOUBLE_WRITING_TOURISM		GreatWorkObjectType	GREATWORKOBJECT_WRITING
CURATOR_DOUBLE_WRITING_TOURISM		ScalingFactor	200
Each instance where "MODIFIER_SINGLE_CITY_ADJUST_TOURISM" is used in table ModifierArguments there is a source and a scaling factor. None use an "amount" argument.

MARAE_TOURISM_FEATURES uses an "Amount" argument but in that case the ModifierType is MODIFIER_SINGLE_CITY_ADJUST_TOURISM_PER_FEATURE in which case the "source" and "scaling factor" are implied in the type of modifier.
 
Your problem would appear to be in how you are attempting to use MODIFIER_SINGLE_CITY_ADJUST_TOURISM.
Code:
"DynamicModifiers"
ModifierType                CollectionType        EffectType
MODIFIER_SINGLE_CITY_ADJUST_TOURISM    COLLECTION_OWNER    EFFECT_ADJUST_CITY_TOURISM

"Modifiers"
ModifierId                ModifierType
RELIQUARIES_RELIC_TOURISM_MODIFIER    MODIFIER_SINGLE_CITY_ADJUST_TOURISM
STBASILS_ADDRELIGIOUSTOURISM        MODIFIER_SINGLE_CITY_ADJUST_TOURISM
CURATOR_DOUBLE_SCULPTURE_TOURISM    MODIFIER_SINGLE_CITY_ADJUST_TOURISM
CURATOR_DOUBLE_PORTRAIT_TOURISM        MODIFIER_SINGLE_CITY_ADJUST_TOURISM
CURATOR_DOUBLE_LANDSCAPE_TOURISM    MODIFIER_SINGLE_CITY_ADJUST_TOURISM
CURATOR_DOUBLE_RELIGIOUS_TOURISM    MODIFIER_SINGLE_CITY_ADJUST_TOURISM
CURATOR_DOUBLE_WRITING_TOURISM        MODIFIER_SINGLE_CITY_ADJUST_TOURISM
CURATOR_DOUBLE_MUSIC_TOURISM        MODIFIER_SINGLE_CITY_ADJUST_TOURISM

"ModifierArguments"
ModifierId                Name    Value
RELIQUARIES_RELIC_TOURISM_MODIFIER    GreatWorkObjectType    GREATWORKOBJECT_RELIC
RELIQUARIES_RELIC_TOURISM_MODIFIER    ScalingFactor    300
STBASILS_ADDRELIGIOUSTOURISM        Religious    1
STBASILS_ADDRELIGIOUSTOURISM        ScalingFactor    200
CURATOR_DOUBLE_LANDSCAPE_TOURISM    GreatWorkObjectType    GREATWORKOBJECT_LANDSCAPE
CURATOR_DOUBLE_LANDSCAPE_TOURISM    ScalingFactor    200
CURATOR_DOUBLE_MUSIC_TOURISM        GreatWorkObjectType    GREATWORKOBJECT_MUSIC
CURATOR_DOUBLE_MUSIC_TOURISM        ScalingFactor    200
CURATOR_DOUBLE_PORTRAIT_TOURISM        GreatWorkObjectType    GREATWORKOBJECT_PORTRAIT
CURATOR_DOUBLE_PORTRAIT_TOURISM        ScalingFactor    200
CURATOR_DOUBLE_RELIGIOUS_TOURISM    GreatWorkObjectType    GREATWORKOBJECT_RELIGIOUS
CURATOR_DOUBLE_RELIGIOUS_TOURISM    ScalingFactor    200
CURATOR_DOUBLE_SCULPTURE_TOURISM    GreatWorkObjectType    GREATWORKOBJECT_SCULPTURE
CURATOR_DOUBLE_SCULPTURE_TOURISM    ScalingFactor    200
CURATOR_DOUBLE_WRITING_TOURISM        GreatWorkObjectType    GREATWORKOBJECT_WRITING
CURATOR_DOUBLE_WRITING_TOURISM        ScalingFactor    200
Each instance where "MODIFIER_SINGLE_CITY_ADJUST_TOURISM" is used in table ModifierArguments there is a source and a scaling factor. None use an "amount" argument.

MARAE_TOURISM_FEATURES uses an "Amount" argument but in that case the ModifierType is MODIFIER_SINGLE_CITY_ADJUST_TOURISM_PER_FEATURE in which case the "source" and "scaling factor" are implied in the type of modifier.
Hmmm... I see. Are you aware of a modifier that would work with what I'm trying to do then? I can't seem to find one. I think I've tried 4 different modifier types by now.
Is there a way to add a modifier to a district so it provides default Tourism, but set a requirement so that the Tourism doesn't kick in until, A) The building is built and B) The governor with the promotion is in the city? I know I can create to requirements to the same requirement_id and I can set it to require the building, but can it be set to require the governor to be IN THE CITY with that particular promotion? I see that there is a "REQUIREMENT_CITY_HAS_SPECIFIC_GOVERNOR_PROMOTION_TYPE" but I'm not sure if that's what I'm looking for.
 
Last edited:
So far as I am aware the xml context file is all or none. They are each their own "animal" and there's no equivalent to the "include" command available in an lua script.

Hi Lee!

Thanks for the quick response! Context xml files can use includes, right? I'm replying from my phone here, but my recollection is that TechTree.xml includes something like TechTreeNode.xml. And because of that, I can change TechTreeNode without touching the TechTree.

I think what you're saying is that there's no include-the-base-file-then-just-replace-one-item-in-it mechanism in Context xml... correct?

And if that's the case, then my little project requires either overwriting the whole lua file or overwriting the whole xml file. Neither is ideal, but I suppose the latter is more maintainable.

Kind regards,
DB
 
Hmmm... I see. Are you aware of a modifier that would work with what I'm trying to do then? I can't seem to find one. I think I've tried 4 different modifier types by now.
Is there a way to add a modifier to a district so it provides default Tourism, but set a requirement so that the Tourism doesn't kick in until, A) The building is built and B) The governor with the promotion is in the city? I know I can create to requirements to the same requirement_id and I can set it to require the building, but can it be set to require the governor to be IN THE CITY with that particular promotion? I see that there is a "REQUIREMENT_CITY_HAS_SPECIFIC_GOVERNOR_PROMOTION_TYPE" but I'm not sure if that's what I'm looking for.
You can use "MODIFIER_PLAYER_DISTRICT_ADJUST_TOURISM_CHANGE" with single ModifierArgument "Amount". Many Buildings, such as The Ferris Wheel and Shopping Mall are using it to grant a fixed Tourism Amount for the District they are built in.
"REQUIREMENT_CITY_HAS_SPECIFIC_GOVERNOR_PROMOTION_TYPE" is exactly what you need for that requirement, since no 2 Governors have the same Promotion.

And if that's the case, then my little project requires either overwriting the whole lua file or overwriting the whole xml file. Neither is ideal, but I suppose the latter is more maintainable.
I think overwriting the Lua file would be the optimal choice, since there are more Mods that change the GP database than the GP Lua file. And I don't think that there is going to be a lot of Firaxis rework of the GP Screen. You could just comment with your initials above the functions you made, so you could easily find them when Firaxis change somthing, and you just have to replace their functions with yours.
But Yeah, it's inconvinient and leads to incompatibilities with Mods altering the same file. That's why I like a Concept such of the "UI Plugins Framework" Mod that makes it easier for Modders to make compatible Mods, unfortunately not many Modders use that (tbh I don't know a single one with a Popular Mod).
 
Holy horsehockey it worked!!

Code:
<BuildingModifiers>
        <Row BuildingType="BUILDING_LIGHTHOUSE" ModifierId="SM_BUILDING_TOURISM_BONUS"/>
        <Row BuildingType="BUILDING_SHIPYARD" ModifierId="SM_BUILDING_TOURISM_BONUS"/>
        <Row BuildingType="BUILDING_SEAPORT" ModifierId="SM_BUILDING_TOURISM_BONUS"/>
        <Row BuildingType="BUILDING_FERRIS_WHEEL" ModifierId="SM_BUILDING_TOURISM_BONUS"/>
        <Row BuildingType="BUILDING_AQUARIUM" ModifierId="SM_BUILDING_TOURISM_BONUS"/>
        <Row BuildingType="BUILDING_AQUATICS_CENTER" ModifierId="SM_BUILDING_TOURISM_BONUS"/>
    </BuildingModifiers>
    <Modifiers>
        <Row ModifierId="SM_BUILDING_TOURISM_BONUS" ModifierType="MODIFIER_PLAYER_DISTRICT_ADJUST_TOURISM_CHANGE" SubjectRequirementSetId="SM_CITY_HAS_GOVERNOR_PROMO_REQ"/>
    </Modifiers>
    <ModifierArguments>
        <Row ModifierId="SM_BUILDING_TOURISM_BONUS" Name="Amount" Value="2"/>
    </ModifierArguments>
    <RequirementSets>
        <Row RequirementSetId="SM_CITY_HAS_GOVERNOR_PROMO_REQ" RequirementSetType="REQUIREMENTSET_TEST_ALL"/>
    </RequirementSets>
    <RequirementSetRequirements>
        <Row RequirementSetId="SM_CITY_HAS_GOVERNOR_PROMO_REQ" RequirementId="SM_CITY_HAS_GOVERNOR_PROMO_REQID"/>
    </RequirementSetRequirements>
    <Requirements>
        <Row RequirementId="SM_CITY_HAS_GOVERNOR_PROMO_REQID" RequirementType="REQUIREMENT_CITY_HAS_SPECIFIC_GOVERNOR_PROMOTION_TYPE"/>
    </Requirements>
    <RequirementArguments>
        <Row RequirementId="SM_CITY_HAS_GOVERNOR_PROMO_REQID" Name="GovernorPromotionType" Value="GOVERNOR_PROMOTION_WATER_WORKS" />
    </RequirementArguments>

I gave the modifiers to the buildings rather than the district (which seems obvious now) with the governor requirements.
 
Yeah, that's the good thing about it, you don't have to assign it to the District (the Game itself assigned it to the Buildings).
 
Code:
<Include File="TechTreeNode"/>
So I guess you can. But then you would need a custom version of one or the other anyway even if you imported your own custom context.xml type of file and then "included" that within the Great People Context xml file. All the context files do is create the "shape" of the panel and where the icons, buttons, and whatnot are displayed rather than the actual functions that are executed within the lua file.
 
Code:
<Include File="TechTreeNode"/>
So I guess you can. But then you would need a custom version of one or the other anyway even if you imported your own custom context.xml type of file and then "included" that within the Great People Context xml file. All the context files do is create the "shape" of the panel and where the icons, buttons, and whatnot are displayed rather than the actual functions that are executed within the lua file.

Hi Lee!

Yes, that's exactly my understanding. If I replace a Firaxis content xml file, my version has to have all the elements of the file I'm replacing... I can't just overwrite one element. However, when Firaxis breaks out just one element (e.g. TechTreeNode) I can overwrite just that one... but I still have to attend to everything in that xml file.

Changing the xml "shape" does include options like hiding elements, resizing them to width/height 0, or placing/offsetting them off the screen (-999999)... one of which might serve my purposes of hiding a certain panel and a button.

Thanks everyone for the quick replies. It ultimately confirmed my understanding, but at least I won't spend more hours combing through Firaxis code looking for a better way to do this.

Kind regards,
DB
 
Hi!
Quick question on how to "debug" a mod...
When I want to try one of my mods, I always have to create a new game. I tried saving a game I created with my mod loaded, exit Civ. Then I change and update the mod, open Civ VI, load my game and the changes are not there...
Is there a way for my mod to update in the saved game when I change/update it?
Thanks a lot in advance!
Have a great modding!
 
Hi!
Quick question on how to "debug" a mod...
When I want to try one of my mods, I always have to create a new game. I tried saving a game I created with my mod loaded, exit Civ. Then I change and update the mod, open Civ VI, load my game and the changes are not there...
Is there a way for my mod to update in the saved game when I change/update it?
Thanks a lot in advance!
Have a great modding!
That depends on the coding. Database updates don't have any effect on saved Games (maybe modifiers and requirements only, but those could break the Game sometimes), Lua Gameplay Scripts only work if you reload the Game and Lua UI Scripts can have either immediate effect (during the Game) or till you save and progress to the next turn.

For Database coding I use DB Browser for SQLite to see if my modifications worked and also to try the codes/comands if they work in the application first (sometimes it doesn't work when referencing other tables than the one you're working on)
For Lua, the good old print statement is unvaluable for debuging.
 
I think overwriting the Lua file would be the optimal choice, since there are more Mods that change the GP database than the GP Lua file.

Hi Zegangani!

Sorry I was unclear. I was actually referring to the xml context file (GreatPeoplePopup.xml) and not the xml database file (GreatPeople.xml). The context file, like its related lua file (GreatPeoplePopup.lua), is specific to the UI of the screen. Of those two files, I believe it's easier to maintain an overhaul of xml context file because:

1) There will be as many lua files as the context file or more, but never fewer. For instance, there is only one "CivicsTree.xml" file and it's included in the base version of the game. However, there are 2 lua files for it... "CivicsTree.lua" (base) and "Civics_Tree_Expansion2.lua". Thus, with each patch, I can either do a diff comparison on one "CivicsTree.xml" or two lua files.

2) The lua files are much bigger and more likely to change. Just about any change Firaxis makes to the UI means a change to the lua file. But many of those changes don't affect the xml context file. Like if they decide sort something differently or change the text of a tooltip... that could all be done in lua, without any changes to the context xml file. On the flip side, it would be rare for them to make a change to the context xml file that did not also have a lua file change. Hence, the lua files will change as much or more than the context xml files.

3) Mods are more likely to adjust something in the lua files. Thus, my mod will be compatible with more mods if I can just rewrite the context xml file. I'm not conflicting with their lua changes.


The downside to the context xml overhaul is that you're more limited in what you can do. But for projects that can get the job done via context xml (like my task of hiding panels and buttons), that should be the way to go.


That's why I like a Concept such of the "UI Plugins Framework" Mod that makes it easier for Modders to make compatible Mods, unfortunately not many Modders use that (tbh I don't know a single one with a Popular Mod).

Thanks for the info on UI Plugins Framework. It looks like a neat project, but I won't use it because it adds a point-of-failure to my mods that I have no control over. The steam workshop page says it hasn't been updated in over 2 years, and the comments suggest that Firaxis patches have left it (and its dependent mods) unusable. That's too bad, because it's a great idea. I guess the only real hope of having it work would be if Firaxis incorporated it into the game itself so that it was always installed, and always updated.

Kind regards,
DB
 
Hello everyone!

I am not sure if this is the correct place to post, but I've been trying to follow the unit art assets tutorial and I came to the last part before testing. He said that if I don't know how to actually implement the unit then that's a problem. Well, I am not entirely sure how to do it..

I copied his sql and when I remove all the standard .xml files from the mod, there is a unit in the game with a weird name (the name thats entered into the Units by the sql: LOC_UNIT_TEST_NAME). So here I am doubting if I did the right thing, because in the xml files that I removed there were some unit_text.xml files which seemed to contain info about these references. So, now I'm lost and I can't seem to find a tutorial here. I feel like most people here came from civ 5 modding, but I never modded or even played civ 5. My modding experience comes only from civ 4 and civ 3 which didn't use modbuddy.

So I guess my question is mostly: can someone point me to a place where I can find out more about what I have to edit to add new units and/or buildings to the game? Thanks!
 
@dbergan I misunderstood you then. You're completelt right, xml Context files are rarely edited by FXS or modifierd by Modders, so they are the more optimal choice to keep compatibility with other Mods that alter just the lua files. And yeah, you're pretty much limited regarding modifying xml context files.

Tbh I'm not really familiar with them, I always get lost when I open a xml context file :lol:. Any suggestion from where I chould start to learn how it works or Threads that explain that?

Yes, the "UI Plugins Framework" is outdated, I just wanted to hint to the Possibilities the Idea opens. It doesn't even Include "GreatPeoplePopup.lua" and "GreatPeoplePopup.xml" just the UIs shown on the Steam Page Picture. If you decide to edit the lua file you can leave similar compatibility options. I would have loved it if Sukritact's Simple UI Adjustment, BTS, EDR and other UI Mods would have that option, so I can easily make something like "GreatPeoplePopup_Zegangani.lua".

There are some Notes in GreatWorksOverview_KublaiKhanVietnam_Monopolies.lua that make compatibility with other UI files possible:
Code:
-- This file is being included into the base GreatWorksOverview file using the wildcard include setup in GreatWorksOverview.lua
-- Refer to the bottom of GreatWorksOverview.lua to see how that's happening
-- DO NOT include any GreatWorksOverview files here or it will cause problems
-- include("GreatWorksOverview");

And in GreatWorksOverview.lua:
Code:
-- This wildcard include will include all loaded files beginning with "GreatWorksOverview_"
-- This method replaces the uses of include("GreatWorksOverview") in files that want to override
-- functions from this file. If you're implementing a new "GreatWorksOverview_" file DO NOT include this file.
include("GreatWorksOverview_", true);

I never tested this, so I can't say if this works also with other UI files or it was just included explicitly for that file (hardcoded stuff?). If Yes, then it's the best thing that we got in the January Pack, even better than the Industry/Monopoly Mode :D (but we wouldn't have got it if it weren't the Mode).
 
you still have the issue of "local globals", the "true" flag is to include multiple files starting with the same prefix.
 
Back
Top Bottom