Lua Objects

in the page you've linked, see
Code:
ExposedMembers.SaveTableToSlot = SaveTableToSlot

at the end of the first code block (the one in UI context)

it then allows the use of
Code:
ExposedMembers.SaveTableToSlot(t, "myTable")

in any context, once all files are loaded.

you can also pass values using LuaEvents as shown here :
https://forums.civfanatics.com/threads/help-with-changegoldbalance.623730/#post-14900833

Ok. My 2¢ re: thread safety:

Writing to tables via SQL transactions is thread safe. So one feature of Lua, that the Table is the principle data type with seamless RW to/from SQL DB tables is an advantage for inter-process communication.

Traditional software languages achieve this by using one model of concurrent computing known as Software Transactional Memory, which basically mimics the RDBMS transaction model for shared memory/data.

As for the second method using Lua events, I have no idea if they are thread safe, as a relatively noob at Lua. Does anyone know?
 
Thanks a lot to Gedemon and Chrisy! That is exactly what I need.
Now I just wonder if "ExposedMembers" is an expression from the game files itself or whether it is just an arbitrary name.It seems like it is not declared anywhere in Gedemons example code. I suspect it gets a global scope if you do not declare it anywhere.
 
Last edited:
Ok, the usage of ExposedMembers has worked for me. However, I have noticed that it fails if you try to use the "==" operator to compare the object in the gameplay context with the object from the UI context. For example if I send a list of original capital cities from UI to gameplay context and if I compare them with a city (called "pCity" in this example) that I got directly from the gameplay context, then the comparison will always fail, i.e.
Code:
pOriginalCapital == pCity
will always be false. It only works if I use something like
Code:
pCity:GetX() == pOriginalCapital:GetX() and pCity:GetY() == pOriginalCapital:GetY()

I am not very familiar with Lua, but I think that the ExposedMembers-method sends different pointers for the same object. In my example I think I get different pointers for the same city, which would explain the comparison fail.

I just wanted to mention this in case someone has the same problems like me.
 
Probably the city's ID # will be the same between contexts. Just remember that every player re-uses the same set of ID #s for their cities. So player 1 will have a city with an ID # of 12345 and player # 2 will also have a city with an ID # of 12345. When a city is captured its ID # is re-configured for the new owner and will not be the same as it was for the previous owner.
 
Code:
pCity:GetX() == pOriginalCapital:GetX() and pCity:GetY() == pOriginalCapital:GetY()

IMO this sort of coding is very "hacky" to me. Not necessarily inelegant code.

Modding Civ has been the closest to hacking I've ever got (despite having studied Cryptography at uni. but that's not hacking like people mistakenly think). Introspecting unpublished API, extrapolating from source code, trial & error, guesswork, and community discussion like this on how to bend the Civ engine to our will is "hacking" in the informal sense that it is used nowadays. Hacking/programming.

So I think I've turned into a hack :lol:

Back to the thread of discussion, my personal preference would be to play safe and avoid mixing the contexts. IDK why the API is separate in UI and gameplay contexts to begin with, so I'd be afraid to do what you guys attempt.

Does anyone know why the API of the Lua objects are different in both contexts? (Is it more than just two stacks running in different threads?)
 
Last edited:
Does anyone know why the API of the Lua objects are different in both contexts? (Is it more than just two stacks running in different threads?)
It's entirely about labor and time-saving. Firaxis as a general rule never adds things they don't need or they cannot concieve a "reasonable" need for from their view of what mod-makers might need. And in Civ6 they moved all the "normal" API they built for lua into the game's modifiers and effects systems under we all believe an assumption that mod-makers would not much be needing a more-fleshed API on the Gameplay side. The UI-side needed all the stuff they implemented into it in order for the base game to display UI info properly.
 
Gedemon, it would be nice, if you also added lua functions from the DLC files in your spreadsheet. E.g
Code:
pCity:GetReligion():AddReligiousPressure(0, stringReligion, iPressure, majorId);
can be a very helfpul mehtod to change the religion pressure in a city.
 
I've not found the time to edit the spreadsheet since a lot of time, it misses the methods added in some patches and the expansion.

I should have done it much earlier, but I'd like to open the file for edition to the modders that want to help here, just send me a PM with your google account name, and I'll add you.
 
Hi I am somewhat a fresh man in modding and I learned a lot from these pages, but I need some help.
I tried a toy Lua Script in order to cause an additional damage to defender unit after combat but it simply has no effect.

Code:
-- Test
-- Author: Entarogan
-- DateCreated: 07/08/2018 00:12:42 AM
--------------------------------------------------------------
print("Damage Test begin!\n");
--------------------------------------------------------------
function Ent_Test_Critical(TableTest)
    print("************");
    local AttackerPlayerID = TableTest[CombatVisType.ATTACKER[ID[player]]];
    local DefenderPlayerID = TableTest[CombatVisType.DEFENDER[ID[player]]];
    print("************");
    print(AttackerPlayerID);
    print(DefenderPlayerID);
    print("************");
    local xPlayerConfig = PlayerConfigurations[AttackerPlayerID];
    local pPlayerConfig = PlayerConfigurations[DefenderPlayerID];
    print("First ter loaded\n");
    if xPlayerConfig:GetLeaderTypeName() == "LEADER_ENT_ERESHKIGAL" then
        print("Second ter loaded\n");
        local AttackerUnitID = TableTest[CombatVisType.ATTACKER[ID[id]]];
        local DefenderUnitID = TableTest[CombatVisType.DEFENDER[ID[id]]];
        local DefenderUnitType = TableTest[CombatVisType.DEFENDER[ID[type]]];
        if  DefenderUnitType == UNIT then
            print("Third ter loaded\n");
            local Player = Players[DefenderPlayerID];
            local pUnits = Player:GetUnits();
            local UnitsMember = pUnits:Members();
            pUnit = UnitsMember[DefenderUnitID];
            local DamageAmount = TableTest[CombatVisType.DEFENDER[DAMAGE_TO]];
            pUnit:ChangeDamage(DamageAmount);
        end
    end
end

GameEvents.Combat.Add(Ent_Test_Critical);
--------------------------------------------------------------

Nothing is printed in Firetuner or Lua.log after a combat in Game. I wonder if I misunderstood the function of GameEvents.Combat. Thank you very much!

Entarogan

**********************
Sorry I've found that it should be "Events" not "GameEvents" but new problems arose at line 9. Do I understand the table "CombatResultParameters" correctly?

It told me that "attempt to index a nil value stack traceback:"
 
Last edited:
The first thing to do would be to ensure that your argument "TableTest" is in fact a table passed from the gamecore to all functions subscribed to Events.Combat, and then to determine what data is contained within the table's key and value pairs, and whether these values within each pair are in fact subtables as your code is assuming. Then you would need to determine what structure the key-value pairs within such a sun-table contain. Are the keys integers or are they text strings, for example.

lua will consider "ID" in the following code as being a variable that needs a valid value
Code:
SomeTableName[ID]
whereas in these examples will interpret "ID" as a text string
Code:
SomeTableName.ID
SomeTableName["ID"]
If lua is interpreting anything as a variable with an unassigned value, you will get an error for attempting to index a nil value.
 
yes, its a table, with another table (CombatResultParameters) for the index/keys of the combat result table (see the Events.Combat sheet of the google doc)

for example to get the attacker playerID, I'd use
Code:
function OnCombat( combatResult )

    local attackerID = combatResult[CombatResultParameters.ATTACKER][CombatResultParameters.ID].player

end
Events.Combat.Add( OnCombat )

note that in the above code, player is a key name, ie you either use anytable.player or anytable["player"] to get its value
 
Yeah, and I completely missed the fact he had an incorrect table-structure here
Code:
local AttackerPlayerID = TableTest[CombatVisType.ATTACKER[ID[player]]];
which would attempt to consider this
Code:
[CombatVisType.ATTACKER[ID[player]]]
all as a key for the main table "TableTest"
 
Thank you both very much I got confused yesterday and then I found that it should be like what things are in C++ or Python.
I have settled the table issues clearly but with a somewhat ungentle method, i.e. print the whole table and find the keys in the form of series of numbers.
 
Thank you both very much I got confused yesterday and then I found that it should be like what things are in C++ or Python.
I have settled the table issues clearly but with a somewhat ungentle method, i.e. print the whole table and find the keys in the form of series of numbers.
And LUA is so flexible that it could use a string as a key of a table (unlike a list in Python), quite interesting.
 
I do not know where to write about this, but I have found out some interesting things about the usage of ExposedMembers:

- If you change functions of ExposedMembers in your code, then it will be applied immediately in the game even without reloading the save file. This can be useful, if you do not want to reload every time you changed your code.
However, I am not sure, whether it costs more performance, if you use ExposedMembers too often.
- In the Lua code a member of ExposedMembers must be defined underneath the definition of the function it is referring to, or else it will reference a nil value.
- The last appearance of a ExposedMembers-definition in the code counts. Previous appearances will be overwritten.
- ExposedMembers-definitions can also be used from other mods. If two functions share the same fieldname in the ExposedMembers-object, then one of them will be overwritten. So you should be careful choosing fieldnames (i.e. the name after the point in "ExposedMembers.").
In this case I believe the load-order of the mods decides, which definition will be overwritten.
 
Last edited:
So recently I've found the following code in the game lua files

Code:
local results = DB.Query("SELECT Quote, QuoteAudio from CivicQuotes where CivicType = ? ORDER BY RANDOM() LIMIT 1", civicType);

This looks interesting but it seems it is not possible to update/insert using Query() function it returns empty table as result of RW SQL operations and Debug DB files was not changed. I have tried to print the members of the DB using Gedemon's code but it shows nothing. I have also searched game scripts and found 3 functions that have been used from DB:

Code:
local results = DB.Query("SELECT Quote, QuoteAudio from CivicQuotes where CivicType = ? ORDER BY RANDOM() LIMIT 1", civicType);
local changes = DB.ConfigurationChanges();
eInterfaceMode = DB.MakeHash(interfaceMode);

Is there another way to find complete functions list DB has?
 
Code:
> for k,v in pairs(DB) do print (k,v) end
Code:
ConfigurationQueryCount    function: 000000004D889580
QueryCount    function: 000000004D889340
GetMemoryUsage    function: 000000004D889880
MakeHash    function: 000000004D889900
Query    function: 000000004D889840
QuerySQL    function: 000000004D889400
CollectMemoryUsage    function: 000000004D888F00
ConfigurationQuery    function: 000000004D8895C0
ConfigurationChanges    function: 000000004D888F80
ConfigurationQuerySQL    function: 000000004D889100

I suspect the debug DB won't be changed after the game has loaded, and that DB.Query uses a (read-only ?) copy loaded in memory

(try to change the CivicQuotes in the debug DB file after starting a game and see if it is reflected by the query in game)

IIRC it was like that with civ5, with the exception of the Localized table that could be modified (write/read) after the game's start using the civ5 DB functions available in Lua.
 
I was able to do this (essentially the same as the Firaxis lua code)
Code:
local civicType = "CIVIC_MILITARY_TRAINING"
local results = DB.Query("SELECT Quote, QuoteAudio from CivicQuotes where CivicType = ? ORDER BY RANDOM() LIMIT 1", civicType);
if(results) then
	for i, row in ipairs(results) do
		print("Calculated Quote for CivicType = " .. civicType .. " was " .. Locale.Lookup(row.Quote))
		break;
	end
end
To make a random quote for the civic print into the lua log, but was never able to execute anything into the DB from a DB.Query(sql) sort of syntax. And since we can't use SQL query language to only read from selected rows in the database as we could in Civ5 I pretty much gave up trying anything
 
Top Bottom