whoward69
DLL Minion
Basic Triggers
Consider a mod that adds a pre-Ancient era. Instead of all civs starting out with TECH_AGRICULTURE they now start with TECH_FIRE and have to research their way to TECH_AGRICULTURE.
The mod includes the following SQL to remove the free TECH_AGRICULTURE and replace it with TECH_FIRE for all civs.
This works fine for the base civs but as soon as someone decides to use a custom civ we have a problem. If the custom civ mod loads BEFORE our pre-Ancient era mod, there is no issue - the new custom civ will be modified along with all the others. However, if the custom civ happens to load AFTER our era mod, that civ will NOT start with TECH_FIRE but with TECH_AGRICULTURE.
It's unreasonable for all custom civ modders to add a reference to our era mod, and it's also "unfriendly" to either expect players of our mod to not use custom civs and/or expect them to be able to perform "magic" to get the mods to load in the correct sequence.
What we need is some way to update the Civilization_FreeTechs table if any mod adds to it AFTER our mod's SQL has executed - step forward triggers.
In our mod we can add an SQL trigger to catch any new entries into the Civilization_FreeTechs table and convert them as if they had been there when our mod ran
This basically says "After an insert has occurred on the Civilization_FreeTechs table, execute the SQL statements between BEGIN and END"
Note that there may be more than one statement between BEGIN and END.
It's also worth noting that a trigger will NOT trigger itself, consider the following to grant Mining to all civs
It would appear that the INSERT on the Civilization_FreeTechs table between BEGIN/END should itself cause the trigger to execute, but it doesn't.
Note also the use of NEW.CivilizationType to get a column value from the row that caused the trigger to execute.
More info on triggers as understood by SQLite
Conditional Triggers
We can add a condition (WHEN clause) to our trigger to catch specific changes only. Consider a mod that adds leaders for the City States. (We're going to reuse the Leaders and Civilization_Leaders tables for the sample code, but in reality we'd probably create new tables so as not to interfere with the operation of the game core.)
For each City State we need to create an entry for the leader in the Leaders table and an association in the Civilization_Leaders table, eg, for Warsaw
The problem is that the Thera City State is being added by a 3rd party mod, eg with
which the player may or may not be playing with, and if not, we really don't want the entry in the Civilization_Leaders table as the reference to MINOR_CIV_THERA will then be "dangling".
We can change our insert to trap this by using a WHERE clause
but this now requires the Thera mod to load before our mod. We could use a References in ModBuddy but that requires us to know the Thera mod's guid.
Alternatively we could create a trigger that just adds an entry into the Civilization_Leaders table every time a mod adds a new City State, but then we run the risk of getting entries that have no corresponding LeaderheadType entry.
What we really want to do is just activate the trigger when the Thera city state is added, and we can do this by adding a WHEN clause to our trigger
or more generically to hook up any of our added City State leaders as any 3rd party mod adds the corresponding city state
Note: The above assumes that there is a fixed naming convention where the leader type is the minor civ type prefixed by "LEADER_"
Note 2: || is the SQL string concatenation operator
We can test the last trigger by adding a Minoa leader and then adding the City State itself, eg
Consider a mod that adds a pre-Ancient era. Instead of all civs starting out with TECH_AGRICULTURE they now start with TECH_FIRE and have to research their way to TECH_AGRICULTURE.
The mod includes the following SQL to remove the free TECH_AGRICULTURE and replace it with TECH_FIRE for all civs.
Code:
UPDATE Civilization_FreeTechs
SET TechType='TECH_FIRE'
WHERE TechType='TECH_AGRICULTURE';
This works fine for the base civs but as soon as someone decides to use a custom civ we have a problem. If the custom civ mod loads BEFORE our pre-Ancient era mod, there is no issue - the new custom civ will be modified along with all the others. However, if the custom civ happens to load AFTER our era mod, that civ will NOT start with TECH_FIRE but with TECH_AGRICULTURE.
It's unreasonable for all custom civ modders to add a reference to our era mod, and it's also "unfriendly" to either expect players of our mod to not use custom civs and/or expect them to be able to perform "magic" to get the mods to load in the correct sequence.
What we need is some way to update the Civilization_FreeTechs table if any mod adds to it AFTER our mod's SQL has executed - step forward triggers.
In our mod we can add an SQL trigger to catch any new entries into the Civilization_FreeTechs table and convert them as if they had been there when our mod ran
Code:
CREATE TRIGGER CivFireStartingTech
AFTER INSERT ON Civilization_FreeTechs
BEGIN
UPDATE Civilization_FreeTechs
SET TechType='TECH_FIRE'
WHERE TechType='TECH_AGRICULTURE';
END;
This basically says "After an insert has occurred on the Civilization_FreeTechs table, execute the SQL statements between BEGIN and END"
Note that there may be more than one statement between BEGIN and END.
It's also worth noting that a trigger will NOT trigger itself, consider the following to grant Mining to all civs
Code:
CREATE TRIGGER CivMiningStartingTech
AFTER INSERT ON Civilization_FreeTechs
BEGIN
INSERT INTO Civilization_FreeTechs(CivilizationType, TechType)
VALUES(NEW.CivilizationType, 'TECH_MINING');
END;
It would appear that the INSERT on the Civilization_FreeTechs table between BEGIN/END should itself cause the trigger to execute, but it doesn't.
Note also the use of NEW.CivilizationType to get a column value from the row that caused the trigger to execute.
More info on triggers as understood by SQLite
Conditional Triggers
We can add a condition (WHEN clause) to our trigger to catch specific changes only. Consider a mod that adds leaders for the City States. (We're going to reuse the Leaders and Civilization_Leaders tables for the sample code, but in reality we'd probably create new tables so as not to interfere with the operation of the game core.)
For each City State we need to create an entry for the leader in the Leaders table and an association in the Civilization_Leaders table, eg, for Warsaw
Code:
INSERT INTO Leaders(Type, Description, IconAtlas, PortraitIndex)
VALUES('LEADER_MINOR_CIV_WARSAW', 'TXT_KEY_LEADER_MINOR_CIV_WARSAW', 'MINOR_LEADER_ATLAS', 0);
INSERT INTO Civilization_Leaders(CivilizationType, LeaderheadType)
VALUES('MINOR_CIV_WARSAW', 'LEADER_MINOR_CIV_WARSAW');
INSERT INTO Leaders(Type, Description, IconAtlas, PortraitIndex)
VALUES('LEADER_MINOR_CIV_THERA', 'TXT_KEY_LEADER_MINOR_CIV_THERA', 'MINOR_LEADER_ATLAS', 1);
INSERT INTO Civilization_Leaders(CivilizationType, LeaderheadType)
VALUES('MINOR_CIV_THERA', 'LEADER_MINOR_CIV_THERA');
The problem is that the Thera City State is being added by a 3rd party mod, eg with
Code:
INSERT INTO MinorCivilizations(Type, Description, MinorCivTrait)
VALUES('MINOR_CIV_THERA', 'TXT_KEY_CITYSTATE_THERA', 'MINOR_TRAIT_CULTURED');
which the player may or may not be playing with, and if not, we really don't want the entry in the Civilization_Leaders table as the reference to MINOR_CIV_THERA will then be "dangling".
We can change our insert to trap this by using a WHERE clause
Code:
INSERT INTO Civilization_Leaders(CivilizationType, LeaderheadType)
SELECT Type, 'LEADER_MINOR_CIV_THERA' FROM MinorCivilizations
WHERE Type='MINOR_CIV_THERA';
but this now requires the Thera mod to load before our mod. We could use a References in ModBuddy but that requires us to know the Thera mod's guid.
Alternatively we could create a trigger that just adds an entry into the Civilization_Leaders table every time a mod adds a new City State, but then we run the risk of getting entries that have no corresponding LeaderheadType entry.
What we really want to do is just activate the trigger when the Thera city state is added, and we can do this by adding a WHEN clause to our trigger
Code:
CREATE TRIGGER MinorCivLeaderThera
AFTER INSERT ON MinorCivilizations
WHEN 'MINOR_CIV_THERA'=NEW.Type
BEGIN
INSERT INTO Civilization_Leaders(CivilizationType, LeaderheadType)
VALUES('MINOR_CIV_THERA', 'LEADER_MINOR_CIV_THERA');
END;
or more generically to hook up any of our added City State leaders as any 3rd party mod adds the corresponding city state
Code:
CREATE TRIGGER MinorCivLeaders
AFTER INSERT ON MinorCivilizations
WHEN EXISTS (SELECT Type FROM Leaders WHERE Type='LEADER_'||NEW.Type)
BEGIN
INSERT INTO Civilization_Leaders(CivilizationType, LeaderheadType)
VALUES(NEW.Type, 'LEADER_'||NEW.Type);
END;
Note: The above assumes that there is a fixed naming convention where the leader type is the minor civ type prefixed by "LEADER_"
Note 2: || is the SQL string concatenation operator
We can test the last trigger by adding a Minoa leader and then adding the City State itself, eg
Code:
INSERT INTO Leaders(Type, Description, IconAtlas, PortraitIndex)
VALUES('LEADER_MINOR_CIV_MINOA', 'TXT_KEY_LEADER_MINOR_CIV_MINOA', 'MINOR_LEADER_ATLAS', 1);
INSERT INTO MinorCivilizations(Type, Description, MinorCivTrait)
VALUES('MINOR_CIV_MINOA', 'TXT_KEY_CITYSTATE_MINOA', 'MINOR_TRAIT_MARITIME');