Is there any way to specify a tile has roads?

Phantagonist

Chieftain
Joined
Aug 3, 2020
Messages
17
Greetings, I am Phantagonist, and I have just uploaded my first Civilization VI mod, Liu Che, an alternate leader for China.

However, I would like to adjust Liu Che combat strength bonus, so that units have higher Combat Strength on roads.

I have tried REQUIREMENT_PLOT_IMPROVEMENT_TYPE_MATCHES, but it doesn't work. Apparently roads are not stored as Improvement Types.

Is there any way to specify a tile has roads through coding?
 
You can check that in Lua (Plot:GetRouteType()) but it won't help you much because there is a very limited number of operations available via Lua for units - only ChangeDamage() and SetDamage().
Is it possible to add custom property to a tile after checking the Route Type? I saw there is a REQUIREMENT_PLOT_PROPERTY_MATCHES, but I don't know how to use that.
 
There is a Lua function Plot:SetProperty(), so maybe it could be combined somehow with modifiers. Many objects in Civ6 have this “property” but I never used them. You need to do some tests and discover the details. Or maybe someone else might know more.
 
From a discord server (lightly edited by me):
Spoiler :

Leugi 05/21/2020
Using 'REQUIREMENT_PLOT_PROPERTY_MATCHES' to dynamically use lua with modifiers:
(discovered by Sukritact)

The new requirement added is going to be pretty useful overall. What it does is checking whether a plot has an assigned property alongside a numerical value. The important thing with this is that you can in fact apply this "Property" to a plot with lua using the following function:

Code:
Map.GetPlot(X, Y):SetProperty("Property", value);

So let's say that under certain circumstances you give a plot the property "LUAMODIFIERSYEAH" with 1 as a value, to check it with the requirement you just need these arguments in table RequirementArguments:

'PropertyName' = 'LUAMODIFIERSYEAH'
'PropertyMinimum' = 1

That will make the modifier work with the lua property you set!

The RequirementType used in table Requirements is
Code:
REQUIREMENT_PLOT_PROPERTY_MATCHES

BTW, the REQUIREMENT_PLOT_PROPERTY_MATCHES seems to only work with COLLECTION_PLAYER_PLOT_YIELDS... So to make more dynamic stuff you might have to create a DynamicModifier similar to this:

Code:
INSERT OR REPLACE INTO DynamicModifiers
            (ModifierType, CollectionType, EffectType)
VALUES ('MODIFIER_LEU_PLOT_ATTACH_MODIFIER', 'COLLECTION_PLAYER_PLOT_YIELDS', 'EFFECT_ATTACH_MODIFIER');

Note that since we might be using this a lot it's better if we use a INSERT OR REPLACE, to prevent mods coliding with each other.

Anyway, you put the requirement to a modifier with this type of dynamic modifier and you should be able to make interesting stuff mixing lua with modifiers.
Here is an example of how to make use of "Property" in lua. The function not only adds a ModifierId to a Player Object via lua, but it also sets a needed "tracking" Property to the player object, and accesses that Property to determine whether or not the Modifier has already been applied to the player.

Code:
function AddModifierToPlayer(iPlayer, sModifierID, bDebugPrint)
	local function Dprint(sMessage)
		if bDebugPrint then print(sMessage) end
	end
	Dprint("function AddModifierToPlayer was executed with argument values of iPlayer = " .. tostring(iPlayer) .. ", sModifierID = " .. tostring(sModifierID) .. ", bDebugPrint = " .. tostring(bDebugPrint))
	local pPlayer = Players[iPlayer]
	if (pPlayer ~= nil) then
		Dprint("AddModifierToPlayer(" .. tostring(iPlayer) .. ", " .. tostring(sModifierID) .. ", " .. tostring(bDebugPrint) .. "): Proceeding with evaluating whether the player already has the modifier.")
		local PlayerProperty = pPlayer:GetProperty(sModifierID)
		Dprint("pPlayer:GetProperty(" .. sModifierID .. ") == " .. tostring(PlayerProperty))
		if (PlayerProperty == nil) then
			Dprint("Modifier " .. sModifierID .. " was seen as NOT being already attached to the player")
			pPlayer:AttachModifierByID(sModifierID)		
			pPlayer:SetProperty(sModifierID, 1)
			Dprint("Modifier " .. sModifierID .. " attached to the player")
		else
			Dprint("Modifier " .. sModifierID .. " was seen as being already attached to the player")
		end
	else
		Dprint("AddModifierToPlayer(" .. tostring(iPlayer) .. ", " .. tostring(sModifierID) .. ", " .. tostring(bDebugPrint) .. ") failed because the iPlayer value is not usable")
	end
end
The third argument in the custom function controls whether or not debugging messages are sent to the lua log and the FireTuner.

To execute the function you would code something like this in lua
Code:
function AddModifierToPlayer(iPlayer, sModifierID, bDebugPrint)
	local function Dprint(sMessage)
		if bDebugPrint then print(sMessage) end
	end
	Dprint("function AddModifierToPlayer was executed with argument values of iPlayer = " .. tostring(iPlayer) .. ", sModifierID = " .. tostring(sModifierID) .. ", bDebugPrint = " .. tostring(bDebugPrint))
	local pPlayer = Players[iPlayer]
	if (pPlayer ~= nil) then
		Dprint("AddModifierToPlayer(" .. tostring(iPlayer) .. ", " .. tostring(sModifierID) .. ", " .. tostring(bDebugPrint) .. "): Proceeding with evaluating whether the player already has the modifier.")
		local PlayerProperty = pPlayer:GetProperty(sModifierID)
		Dprint("pPlayer:GetProperty(" .. sModifierID .. ") == " .. tostring(PlayerProperty))
		if (PlayerProperty == nil) then
			Dprint("Modifier " .. sModifierID .. " was seen as NOT being already attached to the player")
			pPlayer:AttachModifierByID(sModifierID)		
			pPlayer:SetProperty(sModifierID, 1)
			Dprint("Modifier " .. sModifierID .. " attached to the player")
		else
			Dprint("Modifier " .. sModifierID .. " was seen as being already attached to the player")
		end
	else
		Dprint("AddModifierToPlayer(" .. tostring(iPlayer) .. ", " .. tostring(sModifierID) .. ", " .. tostring(bDebugPrint) .. ") failed because the iPlayer value is not usable")
	end
end
.....
.....
.....other lines of lua code might go here
.....
.....
....
AddModifierToPlayer(0, "FARMS_INCREASED_PRODUCTION_TRAIT_X", false)
"FARMS_INCREASED_PRODUCTION_TRAIT_X" in this example is a valid ModifierId already defined within table "Modifiers".

Note that the way the function AddModifierToPlayer is written, it does not matter what value is given as the second argument in the line
Code:
pPlayer:SetProperty(sModifierID, 1)
so long as that value is not "nil" which I do not think the game will accept anyway.

Here is a similar function for City Objects
Code:
function AddModifierToCity(pCity, sModifierID, bDebugPrint)
	local function Dprint(sMessage)
		if bDebugPrint then print(sMessage) end
	end
	if (sModifierID == nil) or (sModifierID == "NONE") then
		Dprint("AddModifierToCity failed because the sModifierID value is either nil or text-string 'NONE'")
		return
	end
	if (pCity ~= nil) then
		Dprint("AddModifierToCity( " .. Locale.Lookup(pCity:GetName()) .. ", " .. tostring(sModifierID) .. ", " .. tostring(bDebugPrint) .. "): Proceeding with evaluating whether the city already has the modifier.")
		local CityProperty = pCity:GetProperty(sModifierID)
		Dprint("pCity:GetProperty(" .. sModifierID .. ") == " .. tostring(CityProperty))
		if (CityProperty == nil) then
			Dprint("Modifier " .. sModifierID .. " was seen as NOT being already attached to the city")
			pCity:AttachModifierByID(sModifierID)		
			pCity:SetProperty(sModifierID, 1)
			Dprint("Modifier " .. sModifierID .. " attached to the city")
		else
			Dprint("Modifier " .. sModifierID .. " was seen as being already attached to the city")
		end
	else
		Dprint("AddModifierToCity(pCity (unknowable city name), " .. tostring(sModifierID) .. ", " .. tostring(bDebugPrint) .. ") failed because the pCity value is not usable")
	end
end
Plot objects can also accept the "AttachModifierByID" method, as well as the "SetProperty" method but I have not as yet written a basic toolkit function for attaching a modifier to a plot.

Note that LuaObject:SetProperty(sPropertyName, Value) only works in GameplayScripts and cannot be attempted in a User Interface script. LuaObject:GetProperty(sPropertyName) works in both GameplayScript and User Interface, and the last value given for a Object:SetProperty(args) is read properly with the same value "return" in both contexts.

The values stored onto a game-object via the "SetProperty()" method persist and survive game saving and reloading, as do the attachment of modifiers via the "AttachModifierByID" method.

These two methods "AttachModifierByID" and "SetProperty" used together can be very powerful in implementing and tracking previous implementation of effects granted via lua, especially where we can as modders see our way clear to creating an effect via lua that is not really doable using Database-only (SQL/XML) methods.


As a last quick note there is also this available in lua:
Code:
local pUnitAbility = pUnit:GetAbility();
local iCurrentCount = pUnitAbility:GetAbilityCount("ABILITY_SOMETHING");
local iChange = ((iCurrentCount ~= 0) and -iCurrentCount or 0)
pUnitAbility:ChangeAbilityCount("ABILITY_SOMETHING", iChange);
The above example code would always set the AbilityCount of UnitAbilityType "ABILITY_SOMETHING" to "0" (ie, turn off the Unit Ability) for the unit.

It appears from experimentation others have done to act as a boolean effect -- "iChange" values greater than "1" don't appear to result in stacking of effects.

"ChangeAbilityCount" is indeed a "Change" method so you need to first retrieve the current value for the "AbilityCount" and then have your code determine what "iChange" value to assign.

Unit Abilities can be turned on and off in this manner. For further info look in the code of the lua files for the Battle to the Death Scenario at
C:\Program Files (x86)\Steam\steamapps\common\Sid Meier's Civilization VI\DLC\CivRoyaleScenario\Scripts\
Files CivRoyaleScenario_UnitCommands.lua and CivRoyaleScenario_StartScript.lua contain examples.
 
Top Bottom