• We created a new subforum for the Civ7 reviews, please check them here!

Stacking Relic Bonus

WeaponizedIncontinence

Chieftain
Supporter
Joined
Jan 12, 2025
Messages
5
I'm adding a Holy Roman Empire civilization. My idea for a civ ability is to add a bonus +2 Faith, +2 Culture, and +2 Gold to all relics for each relic that you own. So total yields would look like this:

Relic CountFaithCultureGold
1622
21688
3301818
5483232
5705050


Things I have already tried
  1. Trying to keep track of how many relics I've got using Events.GreatWorkCreated and Events.GreatWorkMoved in lua. Both of these triggers pass an argument to your function that I'll roughly call greatWorkInstanceID which outputs an automatically incremented value starting at 0. This number obviously corresponds to some sort of internal tracking of relics that are instantiated and in other mods I see people using Game.GetGreatWorkType() or cycling through all available great relic slots to get data about these Great Works using this ID. However, these methods are only available for User Interface mods, not gameplay mods.
  2. Adding a modifier to Relics
SQL:
INSERT INTO WI_HRE_Requirements
        (RequirementId, RequirementType)
VALUES   
        ('REQ_WI_HRE_DEFENSOR_ECCLESIAE', 'REQUIREMENT_PLAYER_HAS_CIVILIZATION_OR_LEADER_TRAIT')
;


INSERT INTO WI_HRE_RequirementArguments
        (RequirementId, Name, Value)
VALUES   
        ('REQ_WI_HRE_DEFENSOR_ECCLESIAE', 'TraitType', 'TRAIT_CIVILIZATION_WI_HRE_DEFENSOR_ECCLESIAE')
;


INSERT INTO WI_HRE_RequirementSets
        (RequirementSetId, RequirementSetType)
VALUES   
        ('REQSET_WI_HRE_DEFENSOR_ECCLESIAE', 'REQUIREMENTSET_TEST_ALL')
;


INSERT INTO WI_HRE_RequirementSetRequirements
        (RequirementSetId, RequirementId)
VALUES   
        ('REQSET_WI_HRE_DEFENSOR_ECCLESIAE', 'REQ_WI_HRE_DEFENSOR_ECCLESIAE')
;


INSERT INTO WI_HRE_Modifiers
        (ModifierId, ModifierType, SubjectRequirementSetId)
VALUES   
        ('MODIFIER_WI_HRE_RELIC_ATTACH_FAITH', 'MODIFIER_PLAYER_CITIES_ATTACH_MODIFIER', 'REQSET_WI_HRE_DEFENSOR_ECCLESIAE')
;


INSERT INTO WI_HRE_Modifiers
        (ModifierId, ModifierType)
VALUES   
        ('MODIFIER_WI_HRE_RELIC_EXTRA_FAITH', 'MODIFIER_PLAYER_CITIES_ADJUST_GREATWORK_YIELD')
;


INSERT INTO WI_HRE_GreatWorkModifiers (GreatWorkType, ModifierID)
    SELECT    GreatWorkType
        ,    'MODIFIER_WI_HRE_RELIC_ATTACH_FAITH' as ModifierID
    FROM    GreatWorks
    WHERE    GreatWorkObjectType = 'GREATWORKOBJECT_RELIC'
;

INSERT INTO WI_HRE_ModifierArguments
        (ModifierId, Name, Value)
VALUES   
        ('MODIFIER_WI_HRE_RELIC_ATTACH_FAITH', 'ModifierId', 'GREATWORKOBJECT_RELIC')
    ,    ('MODIFIER_WI_HRE_RELIC_EXTRA_FAITH', 'GreatWorkObjectType', 'GREATWORKOBJECT_RELIC')
    ,    ('MODIFIER_WI_HRE_RELIC_EXTRA_FAITH', 'YieldChange', '2')
    ,    ('MODIFIER_WI_HRE_RELIC_EXTRA_FAITH', 'YieldType', 'YIELD_FAITH')
;

This second attempt just didn't do anything. The idea was for the relic modifier to attach a modifier to the player, but only if it's my civilization.

Is this even possible? So far I haven't even gotten to the stacking modifier part. If I need to create new modifiers for each stack of the bonus, then that's fine. But I can't even get to the part where I count the number of relics.
 
Some more specifics about stuff I tried in lua. The greatWorkInstanceID value that gets passed to functions that listen to Events.GreatWorkCreated and Events.GreatWorkMoved does not correlate to any field in the rows of GameInfo.GreatWorks() table. I tried cycling through buildings in cities to see what great works are stored in those, but GetGreatWorkTypeFromIndex method only works in UI mods (this seems like a strange decision to me). At minimum, I need to be able to identify and track each relic, but without getting a type from the greatWorkInstanceID, I don't see any way of doing this.
 
I think you could get it working with modifiers. Is there a reason you are doing INSERT INTO WI_HRE_GreatWorkModifiers rather than just the Modifiers table?

I would exhaust getting it done in Modifiers, that would be the ideal case. I actually would think your code would work, stacking and all. Each work would provide a modifier, they would stack together.

You have one small mistake in your Modifiers section:

'MODIFIER_WI_HRE_RELIC_ATTACH_FAITH', 'ModifierId', 'GREATWORKOBJECT_RELIC'
The third argument should be 'MODIFIER_WI_HRE_RELIC_EXTRA_FAITH' as you want to attach it to the cities. As a result, even thogh the relics have the modifier that attaches, it doesnt have the correct modifierId to attach to those cities.

I would note no base game content uses GreatWorkModifiers, as it can be buggy. I got it to work with a test mod, but it would only apply the modifier when the work was moved for the first time, and wouldn't stack (it should).

I would also encourage building up your modifiers, like try putting them on a leader trait first, or a monument building, then if they work, put them on the relic but wihtout a requirement, and if that works, then add the requirementset and you have the original.

But I suspect Modifiers wont work here, so you will need some Lua. Yes I see you found a lot of good methods, but they are on the UI side, and you want Gameplay. There is a way around that. What you do is you make an empty UI menu, that has code just to track these great works, and use this particular method to trigger a function you have setup in a Gameplay file.

Code:
local tParameters = {}
tParameters.OnStart= 'SlthOnConvertUnitType'
tParameters.iUnitID = iUnit
tParameters.iUpgradeUnitIndex = 20
tParameters.iCost = 200
UI.RequestPlayerOperation(iPlayer, PlayerOperations.EXECUTE_SCRIPT, tParameters);

Then in your gameplay file you have this:

Code:
local function ConvertUnitType( iPlayer, tParameters)
    local iUnitID = tParameters.iUnitID
    local iNewUnitIndex = tParameters.iUpgradeUnitIndex
    local iUpgradeCost = tParameters.iCost
    -- then do stuff with the parameters you unpacked
    
GameEvents.SlthOnConvertUnitType.Add(ConvertUnitType)

So when the RequestPlayerOperation is run, the gameplay file is listening for it, so can use the parameters you need from the UI side, presumably to do with relics.
 
I think you could get it working with modifiers. Is there a reason you are doing INSERT INTO WI_HRE_GreatWorkModifiers rather than just the Modifiers table?

Short answer: personal preference.
Long answer: I was play testing many different leader / civ abilities, swapping them on and off, and I found it easier to split them off into difference files. However, this caused issues with foreign keys and inserting items into different tables in the wrong order. So I undertook a long and most pointless refactor (which is basically a sacrament for me) to organize the mod by first creating staging versions of each table I'd be inserting into, then inserting data into those tables, and then transferring that data into "prod" tables that the game actually uses. If I was so inclined I could also add some tests before inserting into prod to make sure that nothing breaks, but my zeal for pointless refactoring is limited.

You have one small mistake in your Modifiers section:

'MODIFIER_WI_HRE_RELIC_ATTACH_FAITH', 'ModifierId', 'GREATWORKOBJECT_RELIC'
The third argument should be 'MODIFIER_WI_HRE_RELIC_EXTRA_FAITH' as you want to attach it to the cities. As a result, even thogh the relics have the modifier that attaches, it doesnt have the correct modifierId to attach to those cities.

giphy.gif


Thanks.

I would note no base game content uses GreatWorkModifiers, as it can be buggy. I got it to work with a test mod, but it would only apply the modifier when the work was moved for the first time, and wouldn't stack (it should).

I would also encourage building up your modifiers, like try putting them on a leader trait first, or a monument building, then if they work, put them on the relic but wihtout a requirement, and if that works, then add the requirementset and you have the original.

Very interesting about the modifiers being buggy on GreatWorks. Yeah, developing with the modifiers is definitely a very different experience rather than directly writing code and I'm not that great at it. (As is evident from the obvious user error your pointed out)


But I suspect Modifiers wont work here, so you will need some Lua. Yes I see you found a lot of good methods, but they are on the UI side, and you want Gameplay. There is a way around that. What you do is you make an empty UI menu, that has code just to track these great works, and use this particular method to trigger a function you have setup in a Gameplay file.

Code:
local tParameters = {}
tParameters.OnStart= 'SlthOnConvertUnitType'
tParameters.iUnitID = iUnit
tParameters.iUpgradeUnitIndex = 20
tParameters.iCost = 200
UI.RequestPlayerOperation(iPlayer, PlayerOperations.EXECUTE_SCRIPT, tParameters);

Then in your gameplay file you have this:

Code:
local function ConvertUnitType( iPlayer, tParameters)
    local iUnitID = tParameters.iUnitID
    local iNewUnitIndex = tParameters.iUpgradeUnitIndex
    local iUpgradeCost = tParameters.iCost
    -- then do stuff with the parameters you unpacked
   
GameEvents.SlthOnConvertUnitType.Add(ConvertUnitType)

So when the RequestPlayerOperation is run, the gameplay file is listening for it, so can use the parameters you need from the UI side, presumably to do with relics.

I've always been scared to try out the UI / Gameplay backdoor context connections. Thanks for giving this toy example. I'll give it a try and see how well it works for my use case.


Thank you so so so much for your help here. I'm going to try to find some time this week to try all this stuff out.
 
Well I tried the suggested correction on this line
Code:
'MODIFIER_WI_HRE_RELIC_ATTACH_FAITH', 'ModifierId', 'GREATWORKOBJECT_RELIC'
and it still didn't work. Oh well, I'm trying to find a different way of adding some relic-based flavor to my mod. Thanks for the help.
 
My new approach is to try to attach these modifiers when a Great General retires (which I'm triggering in lua). At first I tried to just attach the modifier multiple times, and that didn't work. Then I tried to overwrite the original modifier with a bigger version of that modifier, but that isn't working either.

SQL:
INSERT INTO WI_HRE_Modifiers
        (ModifierId, ModifierType)
VALUES   
        ('MODIFIER_WI_HRE_RELIC_EXTRA_FAITH_1', 'MODIFIER_PLAYER_CITIES_ADJUST_GREATWORK_YIELD')
    ,    ('MODIFIER_WI_HRE_RELIC_EXTRA_FAITH_2', 'MODIFIER_PLAYER_CITIES_ADJUST_GREATWORK_YIELD')
    ,    ('MODIFIER_WI_HRE_RELIC_EXTRA_FAITH_3', 'MODIFIER_PLAYER_CITIES_ADJUST_GREATWORK_YIELD')
;


INSERT INTO WI_HRE_ModifierArguments
        (ModifierId, Name, Value)
VALUES
        ('MODIFIER_WI_HRE_RELIC_EXTRA_FAITH_1', 'GreatWorkObjectType', 'GREATWORKOBJECT_RELIC')
    ,   ('MODIFIER_WI_HRE_RELIC_EXTRA_FAITH_1', 'YieldChange', '4')
    ,   ('MODIFIER_WI_HRE_RELIC_EXTRA_FAITH_1', 'YieldType', 'YIELD_FAITH')
    ,    ('MODIFIER_WI_HRE_RELIC_EXTRA_FAITH_2', 'GreatWorkObjectType', 'GREATWORKOBJECT_RELIC')
    ,   ('MODIFIER_WI_HRE_RELIC_EXTRA_FAITH_2', 'YieldChange', '8')
    ,   ('MODIFIER_WI_HRE_RELIC_EXTRA_FAITH_2', 'YieldType', 'YIELD_FAITH')
    ,    ('MODIFIER_WI_HRE_RELIC_EXTRA_FAITH_3', 'GreatWorkObjectType', 'GREATWORKOBJECT_RELIC')
    ,   ('MODIFIER_WI_HRE_RELIC_EXTRA_FAITH_3', 'YieldChange', '12')
    ,   ('MODIFIER_WI_HRE_RELIC_EXTRA_FAITH_3', 'YieldType', 'YIELD_FAITH')
;

I'm applying it like this. My retiredGeneralCount variable is just updating a global and it's working just fine. I end up getting the +4 bonus, but it never updates to the higher values.

Code:
retiredGeneralCount = retiredGeneralCount + 1
local faithdModName = "MODIFIER_WI_HRE_RELIC_EXTRA_FAITH_" .. tostring(retiredGeneralCount)
Players[playerID]:AttachModifierByID(faithdModName)
 
Top Bottom