SQL code-check and lua event advice

LeeS

Imperator
Joined
Jul 23, 2013
Messages
7,241
Location
Illinois, USA
I'm currently working on a unit production modifier system wherein a building will modify unit production based on a unit's class rather than on its UnitCombat type.

Currently we have the ability to set a building as have X modifier for unit domain and unit combat type (tables Building_DomainProductionModifiers and Building_UnitCombatProductionModifiers) as well as table Unit_ProductionModifierBuildings. None of these allow specification of a Unit-Class, however, and Unit_ProductionModifierBuildings is darn near useless as soon as one begins to consider other mods and units they may introduce into the game. Table Building_UnitCombatProductionModifiers is unfortunate especially as regards gunpowder units because so very many different unit-classes are contained within the larger UNITCOMBAT_GUN.

Everything in the code as I have it at the moment is functioning correctly, but I would like to make this system available to any who may want to use it, so:

-----------------------------------------------------------------------------------------------------------

For this SQL code:
Spoiler :
Code:
CREATE TABLE IF NOT EXISTS
        Building_UnitClassProductionModifiers (
        BuildingType		text    REFERENCES Buildings(TYPE)	DEFAULT NULL,
        UnitClassType		text    REFERENCES UnitClasses(TYPE)	DEFAULT NULL,
	Modifier		integer					DEFAULT 0);

--=======================================Following formats borrowed from JFD================================================	
-- BuildingClasses
--==========================================================================================================================		
INSERT OR REPLACE INTO BuildingClasses
	(Type,					DefaultBuilding,		Description,				NoLimit)
VALUES	('BUILDINGCLASS_UNITCLASS_PRODMOD',	'BUILDING_UNITCLASS_PRODMOD', 	'TXT_KEY_BUILDING_UNITCLASS_PRODMOD', 	1);		
--==========================================================================================================================	
-- Buildings
--==========================================================================================================================		
INSERT OR REPLACE INTO Buildings
	(Type, BuildingClass, Cost, FaithCost, GreatWorkCount, PrereqTech, MilitaryProductionModifier, Description, NeverCapture, IconAtlas, PortraitIndex)
VALUES	('BUILDING_UNITCLASS_PRODMOD', 'BUILDINGCLASS_UNITCLASS_PRODMOD', -1, -1, -1, NULL, 1, 'TXT_KEY_BUILDING_UNITCLASS_PRODMOD', 1, 'BW_ATLAS_1', 19);
Have I got everything correct in my "INSERT OR REPLACE INTO" and "CREATE TABLE IF NOT EXISTS" methods so that if three different mods are all running this system, the second and third will not cause file-loading failure, and there will only end within the database with one version of the new table, the building-class, and the building.

This is only the second time I've attempted these more advanced SQL methods, so I have zero confidence everything is correct re the usages when multiple mods are all trying to use the same system.

----------------------------------------------------------------------------------------------------------

Lua event hook advice:
As reference, here is the code I am currently using:
Spoiler :
Code:
local iProductionModifierUnitClassesBuilding = GameInfoTypes.BUILDING_UNITCLASS_PRODMOD
local tBuildingsToCheck = {}
local tUnitsToCheck = {}

for row in GameInfo.Building_UnitClassProductionModifiers() do
	local sUnitClassType = row.UnitClassType
	local iBuildingID = GameInfoTypes[row.BuildingType]
	local iModifier = row.Modifier
	if tBuildingsToCheck[iBuildingID] == nil then
		tBuildingsToCheck[iBuildingID] = "true"
	end
	for Unit in GameInfo.Units("Class = '" .. sUnitClassType .. "'") do
		if tUnitsToCheck[Unit.ID] == nil then
			tUnitsToCheck[Unit.ID] = {}
		end
		tUnitsToCheck[Unit.ID][iBuildingID] = iModifier
	end
end
function GetNumberUnitClassProdModifiersToSet(pCity)
	local iNumberOfBuildingsToSet = 0
	if (pCity:GetProductionUnit() > -1) then
		if (tUnitsToCheck[pCity:GetProductionUnit()]) then
			for iBuildingID,iModifier in pairs(tUnitsToCheck[pCity:GetProductionUnit()]) do
				if pCity:IsHasBuilding(iBuildingID) then
					iNumberOfBuildingsToSet = iNumberOfBuildingsToSet + iModifier
				end
			end
		end
	end
	return iNumberOfBuildingsToSet
end
function Building_UnitClassProductionModifiers(iPlayer)
	local pPlayer = Players[iPlayer]
	if pPlayer:GetCivilizationType() == GameInfoTypes["CIVILIZATION_BARBARIAN"] then return end
	if not pPlayer:IsAlive() then return end
	for pCity in pPlayer:Cities() do
		pCity:SetNumRealBuilding(iProductionModifierUnitClassesBuilding, GetNumberUnitClassProdModifiersToSet(pCity))
	end
end
function CityBuiltOrBoughtBuilding_UnitClassProductionModifiers(ownerId, cityId, buildingType, bGold, bFaithOrCulture)
	if tBuildingsToCheck[buildingType] then
		local pCity = Players[ownerId]:GetCityByID(cityId);
		pCity:SetNumRealBuilding(iProductionModifierUnitClassesBuilding, GetNumberUnitClassProdModifiersToSet(pCity))
	end
end
GameEvents.PlayerDoTurn.Add(Building_UnitClassProductionModifiers)
GameEvents.CityConstructed.Add(CityBuiltOrBoughtBuilding_UnitClassProductionModifiers)
  1. I am not concerned really with code as written so far. It functions properly and seems to be reasonably-well adapted to using data-driven techniques, so shouldn't be a terrible processing-time hog.
  2. What I don't have the moment, and am not sure what the best event hook would be, is to make the modifiers appear in the city as soon as the player selects one of the units that qualify for the modifier as their city's production-item.
    • As-is, the code will only fire on turn processing, or upon completion of one of the buildings that give the modifier (in this case as a result of gold-purchasing would be the only realistic way this could happen)
    • Will the suggested hook event fire also for the AI when they "select" their production item ?
  3. I haven't done much successful city-view or city-production-qeue modding, hence the request for advice on the best hook to use to accomplish the real-time addition of the modifiers.
 
SQL

CREATE TABLE IF NOT EXISTS ...

does exactly what it says on the tin, so in the first mod the table will be created (as the table doesn't exist) and in the second and subsequent mods it won't be created (and no error will occur) as it does exist.

INSERT OR REPLACE INTO ...

For the first mod, the row doesn't exist, so it is inserted. In the second and subsequent mods, the row (decided from the unique Type column) does exist so it will be replaced (overwritten)

You can test these statements by starting up SQLiteSpy or other database viewing program and executing the statements repeatedly.

Note, that there is still an issue ...

If, in a future version of the mod, you change the structure of the UnitClassProductionModifiers table by adding one or more columns, if the player has two mods active (one with the old format and one with the new and the old format mod loads first), the create table with the new format won't execute (as the table already exist) but then any insert statements into the table from the new format mod will fail (as they will be trying to insert values into a column that doesn't exist).

This may or may not be a consideration
 
Good to know on the alterred tables thing. Best to think everything through that you'll need from the very start I guess. For this specific usage I can't think of anything else the table would need, but if making a larger table to add to the game that would definitely be an issue because I always seem to realise later that there's just one more thing that could be added to "X" or something that could just as well be subtracted.

--------------------------------------------------------------------------------

Any thoughts on which hooks to look at using to provide for the 'instant effect' when the player changes or selects city production. I know there are some UI-type events, and there's DirtyCitySomethingOrOther, but I'm not sure if those would work for creating that real-time effect.
 
Any thoughts on which hooks to look at ...

I've more or less given up trying to bend the Firaxis events, I just mod the DLL and add the ones I need ;)
 
found an event hook that works at least for the human player:

Events.SpecificCityInfoDirty

I know that it is being used in the Barbarians Evolved mod so would appear to fire for AI based in how it is being used.
 
This should do what you want
Code:
function OnSpecificCityInfoDirty(iPlayer, iCity, iUpdateType)
	if (iUpdateType == CityUpdateTypes.CITY_UPDATE_TYPE_PRODUCTION) then
		-- City production was changed.
		-- This should fire for both human and AI players
	end
end
Events.SpecificCityInfoDirty.Add(OnSpecificCityInfoDirty)

the event is fired by both the C++ pushOrder() code (which is used by the game core for both AI cities and human puppet cities) and also by the UI CityProduction Lua code
 
This should do what you want
Code:
function OnSpecificCityInfoDirty(iPlayer, iCity, iUpdateType)
	if (iUpdateType == CityUpdateTypes.CITY_UPDATE_TYPE_PRODUCTION) then
		-- City production was changed.
		-- This should fire for both human and AI players
	end
end
Events.SpecificCityInfoDirty.Add(OnSpecificCityInfoDirty)

the event is fired by both the C++ pushOrder() code (which is used by the game core for both AI cities and human puppet cities) and also by the UI CityProduction Lua code
Thanks for the confirm that it fires correctly for the AI.

I had come up with essentially identical code to test run it that it actually performed in the way desired for the human player but was stymied on a method to test if it worked correctly for the AI, other than 900 print statements and waiting for the AI to actually select one of the units I was interested in.
 
and waiting for the AI to actually select one of the units I was interested in.

PlayerCanTrain/Construct()
is this the AI, is this the unit, return false
end

or something like that ;)

FireTuner can be useful here, as you can add event handlers like that (well, obviously the real lua code, not that pseudo-garbage) directly from the Lua console
 
PlayerCanTrain/Construct()
is this the AI, is this the unit, return false
end

or something like that ;)

FireTuner can be useful here, as you can add event handlers like that (well, obviously the real lua code, not that pseudo-garbage) directly from the Lua console
oh, I see. During debugging-phase, just take away the other choices for the AI and you'll be able to 'see' faster whether the 'real' part of your code is firing correctly for the AI, because they'll have no other available choices to make.
 
Back
Top Bottom