Lua Objects

Hello. There are two functions listed: Player:GetTechs():SetTech() and Player:GetCulture():SetCivic() which I cannot find being used in any file in entire Civ6 directory. Where did you find those?

see
Using this
Code:
function l(object) for k, v in pairs(getmetatable(object).__index) do print(k); end; end
or this
Code:
function p(table) for k, v in pairs(table) do print(k); end; end
on whatever I found in the Lua files of the game.

need to do units and cities, events, ...

see also: http://forums.civfanatics.com/threads/lua-environment-and-sandboxing.487598/

you need to select an unit (or player, city...) first, for example using:
u = Players[0]:GetUnits():GetFirstReadyUnit()
c = Players[0]:GetCities():GetCapitalCity()

and print (u) or print(c) to check it's not nil then the function.

note that what we can access depend of the context (UI in game or a script defined in the modinfo), see http://forums.civfanatics.com/threads/current-status-of-lua-modding.603706/

Edit: I'm using this in firetuner ATM:

Code:
local player = Players[0]
local unit = nil
local playerUnits = player:GetUnits()
for i, u in playerUnits:Members() do
    if u then
        unit = u
    end
end
local pConfig = PlayerConfigurations[0]
local city = nil -- will be listed under player (Capital City)
local seenO = {}
local seenT = {}
listDump = {GameConfiguration = GameConfiguration, NotificationManager = NotificationManager, PlayerConfigurations = pConfig, PlayerManager = PlayerManager, Players = player, Cities = Cities, CityManager = CityManager, Units = Units, UnitManager = UnitManager, Calendar = Calendar, AutoplayManager = AutoplayManager, Achievements = Achievements, GameEffects = GameEffects, CombatManager = CombatManager, Game = Game, DealManager = DealManager, GetUnitStats = GetUnitStats, DiplomacyManager = DiplomacyManager, Map = Map, Unit = unit }

listExceptionStr = {
    "IsNone",
    "ToPlot",
    "ToArea",
    "GetDiplomaticState",
    "HasProductionProgress",
    "ToolTip",
    "HasBeenPlaced",
    "GetAvailableProductionType",
    "GetTurnsLeft",
    "GetName",
    "GetScienceYieldToolTip",
    "GetPlayerName",
    "GetLeaderName",
    "Name",
    "Description",
    "Text",
    "Password",
    "GetNextEscapingSpyID",
    "Advice",
    "IsResourceExtractableAt",
    "Tooltip",
    "GetNextPursuedSpyID",
    "GetTurnFromIndex",
    "GetTurn",
    "GetNthOffMapSpy",
    "GetPlayerFromIndex",
    "FromIndex",
    "GetDiplomaticActionCost",
    "Turn",
    "Level",
    "GetDiplomatic"
    }
function canTest(functionName)
    local bCanTest = false
    local get = string.find(functionName, "Get")
    --local is = string.find(functionName, "Is")
    --local has = string.find(functionName, "Has")
    if get == 1 or is == 1 or has == 1 then
        bCanTest = true
    end
    for k, str in pairs(listExceptionStr) do
        if string.find(functionName, str) then
            return false
        end
    end
    return bCanTest
end
function dumpO(object,i)
    seenO[object]=true
    local s={}
    local n=0
    for k, v in pairs(getmetatable(object).__index) do
        print(i,k)
        if type(v)=="table" and not seenT[v] then
            dumpT(v,i.."\t")
        end
        if type(v)=="function" then
            object.f = object[k]
            local value = nil
            if canTest(tostring(k)) then
                value = object:f()
                --print(i.."\t - ".."return value :", value)
            end
            if getmetatable(v) and getmetatable(v).__index and not seenO[v] then
                dumpO(v,i.."\t")
            end
            if value and getmetatable(value) and getmetatable(value).__index and not seenO[value] then
                dumpO(value,i.."\t")
            end
            if value and type(value)=="table" and not seenT[value] then
                dumpT(value,i.."\t")
            end
        end
    end;
end
function dumpT(table,i)
    seenT[table]=true
    local s={}
    local n=0
    for k in pairs(table) do
        n=n+1 s[n]=k
    end
    for k,v in ipairs(s) do
        print(i,v)
        v=table[v]
        if type(v)=="table" and not seenT[v] then
            dumpT(v,i.."\t")
        end
    end
end
function dumpAll()
    print("-----------------------------------------------------------------------------------------------------")
    print("-----------------------------------------------------------------------------------------------------")
    print("------------------------------------- STARTING DUMP -------------------------------------------------")
    print("-----------------------------------------------------------------------------------------------------")
    print("-----------------------------------------------------------------------------------------------------")
    for k,v in pairs(listDump) do
        local i = ""
        if type(v)=="function" then
            local value = v()
            print("return value :" .. value)
            if getmetatable(v) and getmetatable(v).__index then
                dumpO(v,i.."\t")
            end
            if getmetatable(value) and getmetatable(value).__index then
                dumpO(value,i.."\t")
            end
            if type(value)=="table" then
                dumpT(value,i.."\t")
            end
        end
        if getmetatable(v) and getmetatable(v).__index then
            print("----- object:", k)
            dumpO(v,i)
            print("----- End attributes.")
        end
        if type(v)=="table" then
            print("------ table:", k)
            dumpT(v,i)
            print("----- End attributes.")
        end
    end
end
dumpAll()
 
Last edited:
@Gedemon Referring to Excel: Can you tell me pls. how did you obtain the list on the Map tab? If I run dumpAll, I get a little different list of Map functions (e.g.there's no GetCityPlots, which I wanted to use). Is Map listed in UserInterface context only?
2nd question: is dumpAll listing all functions for objects listed in listDump? is there a possibility that some functions might not be "somehow" listed?
 
I don't remember for the context when I've listed map.

And yes some object are not listed, I add them when I identify them.

Most should be in the _G tab
 
Yeah, Map.GetCityPlots isn't valid in gameplay context.

You'll have to write your own iterator to crawl through the plots near a city by using a for loop. Firaxis has one in one of their files where they are using Map.GetPlotXYWithRangeCheck

Taken from MapUtilities.lua
Code:
	-- Check for being too close to another of this goody type.
	local uniqueRange = improvement.GoodyRange;
	local plotX = plot:GetX();
	local plotY = plot:GetY();
	for dx = -uniqueRange, uniqueRange - 1, 1 do
		for dy = -uniqueRange, uniqueRange - 1, 1 do
			local otherPlot = Map.GetPlotXYWithRangeCheck(plotX, plotY, dx, dy, uniqueRange);
			if(otherPlot and otherPlot:GetImprovementType() == improvementID) then
				return false;
			end
		end
	end
You would need to 1st get the city's plot object for use as plot or the city's XY directly from the City methods. Then you plug in "3" for variable uniqueRange and adjust this part of the code as needed:
Code:
			if(otherPlot and otherPlot:GetImprovementType() == improvementID) then
				return false;
			end
 
I haven't had a chance to test this as yet but this should work to give you an lua table holding all the plot objects belonging to a city as 'V' values in the table's 'K,V' pairs.

Code:
See Post #91
I haven't tried the Map.GetPlotXYWithRangeCheck method yet in a gameplay script to see if it is valid for gameplay script, and it is possible I am missunderstanding the format needed for the parameter "range" values in the for loop and in the Map.GetPlotXYWithRangeCheck function, but I have tested the other city and plot functions being used and know them to work in Gameplay Script*

In your code you then get the city object as "pCity" and then do as like this:
Code:
local tCityPlots = GetCityPlots(pCity)
for Item,pPlot in pairs(tCityPlots) do
     if not pPlot:IsWater() then
          print("Thar be Land here!")
     end
end


Spoiler * :
or at least they did before the Aussie Summner DLC patch :lol:


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

It may not actually be a good idea to keep this part of the code, now that I think of it:
Code:
for row in GameInfo.GlobalParameters() do
	if row.Name == "CITY_MAX_BUY_PLOT_RANGE" then
		iCityWorkRange = row.Value
	end
end
In Civ5 you could alter the equivalent define within the XML but the actual value was hard-coded at DLL level and ignored the setting changes made in XML. I suspect the same might be true for Civ6 but I haven't tested for that as yet.

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

[edit]there was a typo with an extra ")" character in the code as I originally posted so I fixed that and removed the bit for searching through the GlobalParameters xml-table.
 
Last edited:
I don't understand why Firaxis made some functions available only in a specific context, quite confusing.

Given how rudimentary mod support is at the moment, I'm willing to bet they're exposing functions as they need them. They promised SDK enhancements with a later update, performing a review of which functions to expose on each context may be part of that work.

As to why there are two contexts? It allows them to separate UI mods from gameplay mods. The former is a matter of personal taste, while allowing players to vary the latter would allow players to cheat.

See LeeS post below.
 
Last edited:
There are two contexts because gameplay information runs on one processing game-engine-thread, and UI is run in a second processing thread. This is apparently why also the issues with UI not always correctly relecting what has occured to actual in-game data. What has been exposed to Lua in the one processing environment is different than that in the other processing environment, and reflects more what they have needed than anything else. This multi-thread processing allows faster game execution but apparently the downsides are some of the things we are seeing so far as what is available in which processing environment, and the de-synchs we sometimes see between the data displayed in a UI panel and what players know to be current in their game.

Has nothing at all whatever to do with concerns about "cheating".

Also I experimented with the function for getting city plots and found this is probably the most effective manner of the code, with some corrections for misunderstandings of how the
Code:
Map.GetPlotXYWithRangeCheck
function actually works.

Spoiler GetCtyPlots :
Code:
local iCityRadius = 3
	--this is the radius from the player's city-plot to look for resources and the like

function GetCityPlots(pCity)
	local tTempTable = {}
	if pCity ~= nil then
		local iCityOwner = pCity:GetOwner()
		local iCityX, iCityY = pCity:GetX(), pCity:GetY()
		for dx = (iCityRadius * -1), iCityRadius do
			for dy = (iCityRadius * -1), iCityRadius do
				local pPlotNearCity = Map.GetPlotXYWithRangeCheck(iCityX, iCityY, dx, dy, iCityRadius);
				if pPlotNearCity and (pPlotNearCity:GetOwner() == iCityOwner) and (pCity == Cities.GetPlotPurchaseCity(pPlotNearCity:GetIndex())) then
					table.insert(tTempTable, pPlotNearCity)
				end
			end
		end
	end
	return tTempTable
end
 
Part of the reason to split the UI from the actual gameplay probably has to do with this being a multiplayer game. The gameplay stuff only takes place on one computer that acts as the server and makes decisions/broadcasts the data, while the UI has to display the results on each individual computer. If you have 7 players, you have the need for 1 gameplay context and 7 UI contexts.
 
First of all let me start out by saying that I am not familiar with lua modding. I have however made small tweaks and such inspired by what I could find in other peoples code.

I'm looking for a code that would allow me to glance at the map without triggering natural wonders and meeting minor/major civilizations. Ultimately what I'd like to do is to make small tweaks to civ starting locations at game startup but I need to see the map in order to do this and be able to revert the visibility status.

In the firetuners Map panel there's Reveal All, Explore All and Unreveal and buttons. The current code from the fire tuner is stated below. The only difference between the three buttons seems to be that the value in bold is either 1, 0 or -1, respectively .

The Explore All button almost does the trick as it lifts the FOW for the entire map (better than nothing I suppose) without triggering NW's/civs but I cant find a way to revert it afterwards Unreveal All only enables FOW again.

I'd be grateful if anyone has some nice function I could use :)

if (Game.GetLocalPlayer() ~= -1) then
local pVis = PlayersVisibility[Game.GetLocalPlayer()];
for iPlotIndex = 0, Map.GetPlotCount()-1, 1 do
pVis:ChangeVisibilityCount(iPlotIndex, 0);
end
end

\Skodkim
 
Yeah, Map.GetCityPlots isn't valid in gameplay context.
It's not available to scripts, I had to get it from a gameplay context in my mod, but it uses Map.GetCityPlots():GetPurchasedPlots(pCity)
 
I get
Code:
function expected instead of nil
which traces back to this line
Code:
if Map.GetCityPlots():GetPurchasedPlots(pCity) ~= nil then
from here
Code:
for i, pCity in pCities:Members() do
	if Map.GetCityPlots():GetPurchasedPlots(pCity) ~= nil then
		local tPlots = Map.GetCityPlots():GetPurchasedPlots(pCity)
		for k,v in pairs(tPlots) do
			print("tPlots (k) is " .. tostring(k) .. " : (v) is " .. tostring(v))
		end
	else
		print("Map.GetCityPlots():GetPurchasedPlots(pCity) is considered nil")
	end
	SetCityLockerStatuses(pCity)
end
The file running from
Code:
  <InGameActions>
    <AddGameplayScripts id="LUA_Gameplay2">
      <File>LUA/EraLockerBuildings.lua</File>
    </AddGameplayScripts>
  </InGameActions>
So I am confuse what magic you used to get it to work.
 
My bad I don't know why but when I've read gameplay (and wrote it!) I was thinking "ingame" context as opposed to "script"... I really need to start using modbuddy and the new convention, sorry.
 
I hope they plan to add more stuff to the Gameplay context in the future. I just realized you can't determine which Policies a player is running from the Gameplay Lua, which is a big blow against the ability to design complex policies.
 
Is there a way of forcing code to run in a specific context?

For instance, I have code running in a UI screen which I want to Create\Destroy a dummy building at certain points in the game.
I understand that the City:GetBuildQueue():CreateIncompleteBuilding() function will not run in the UI context so I have this in a Gameplay script.
I set the city for the building by using ExposedMembers, but don't try to create the building until the start of the player's turn (in the OnLocalPlayerTurnBegin event).

It however always fails on the call to CreateIncompleteBuilding(). I have checked that none of the objects it relies on are nil, but the message
'function expected instead of nil' always gets logged.

I think it's because it's not running in the correct context.
 
I just realized you can't determine which Policies a player is running from the Gameplay Lua
Policies usually use modifiers to enforce their effects, so you might try to use GameEffects to determine which modifiers are attached to a player. It's tedious, I know, but it might work.
 
Is there a way of forcing code to run in a specific context?

For instance, I have code running in a UI screen which I want to Create\Destroy a dummy building at certain points in the game.
I understand that the City:GetBuildQueue():CreateIncompleteBuilding() function will not run in the UI context so I have this in a Gameplay script.
I set the city for the building by using ExposedMembers, but don't try to create the building until the start of the player's turn (in the OnLocalPlayerTurnBegin event).

It however always fails on the call to CreateIncompleteBuilding(). I have checked that none of the objects it relies on are nil, but the message
'function expected instead of nil' always gets logged.

I think it's because it's not running in the correct context.
CreateIncompleteBuilding() works for me just fine in a Gameplay Context but I am pretty sure it is nil in a UI context. If you are running from Gameplay Context perhaps you do not have the format correct ?

Code:
function PlaceBuildingInCityCenter(pCity, iBuilding)
	local iCityPlotIndex = Map.GetPlot(pCity:GetX(), pCity:GetY()):GetIndex()
	if not pCity:GetBuildings():HasBuilding(iBuilding) then
		pCity:GetBuildQueue():CreateIncompleteBuilding(iBuilding, iCityPlotIndex, 100);
	else
		if pCity:GetBuildings():IsPillaged(iBuilding) then
			pCity:GetBuildings():RemoveBuilding(iBuilding);
			pCity:GetBuildQueue():CreateIncompleteBuilding(iBuilding, iCityPlotIndex, 100);
		end
	end
end
function RemoveBuildingFromCityCenter(pCity, iBuilding)
	if pCity:GetBuildings():HasBuilding(iBuilding) then
		pCity:GetBuildings():RemoveBuilding(iBuilding);
	end
end
If you are adding or removing to/from a district other than the city center you would need to add code to figure out that district's plot and then the plot's ID when placing the building.
 
Last edited:
Top Bottom